Merge "Defend against ICOs with large BMPs embedded DO NOT MERGE" into oc-dev am: 5bf7ed94cf
am: 7d37d0950e

Change-Id: I923e026aec4897244fa9ecbddff429c2ce3aff44
diff --git a/.gitignore b/.gitignore
index f292111..ec250f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,6 @@
 bin/gn.exe
 bin/clang-format
 bin/clang-format.exe
+
+vulkan-out
+gl-out
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index ce55e2c..b4e1f5f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,6 +18,7 @@
         "include/config/",
         "include/core/",
         "include/effects/",
+        "include/encode/",
         "include/gpu/",
         "include/gpu/gl/",
         "include/pathops/",
@@ -34,6 +35,7 @@
         "include/config/",
         "include/core/",
         "include/effects/",
+        "include/encode/",
         "include/gpu/",
         "include/gpu/gl/",
         "include/pathops/",
@@ -46,7 +48,6 @@
         "src/codec/",
         "src/core/",
         "src/effects/",
-        "src/effects/gradients/",
         "src/fonts/",
         "src/gpu/",
         "src/image/",
@@ -57,11 +58,12 @@
         "src/pdf/",
         "src/ports/",
         "src/sfnt/",
+        "src/shaders/",
+        "src/shaders/gradients/",
         "src/sksl/",
         "src/utils/",
         "src/utils/win/",
         "src/xml/",
-        "third_party/etc1/",
         "third_party/gif/",
     ],
 
@@ -72,6 +74,7 @@
         "src/c/sk_paint.cpp",
         "src/c/sk_surface.cpp",
         "src/codec/SkAndroidCodec.cpp",
+        "src/codec/SkBmpBaseCodec.cpp",
         "src/codec/SkBmpCodec.cpp",
         "src/codec/SkBmpMaskCodec.cpp",
         "src/codec/SkBmpRLECodec.cpp",
@@ -108,19 +111,17 @@
         "src/core/SkBitmapCache.cpp",
         "src/core/SkBitmapController.cpp",
         "src/core/SkBitmapDevice.cpp",
-        "src/core/SkBitmapProcShader.cpp",
         "src/core/SkBitmapProcState.cpp",
         "src/core/SkBitmapProcState_matrixProcs.cpp",
         "src/core/SkBitmapProvider.cpp",
         "src/core/SkBitmapScaler.cpp",
+        "src/core/SkBlendMode.cpp",
         "src/core/SkBlitMask_D32.cpp",
         "src/core/SkBlitRow_D16.cpp",
         "src/core/SkBlitRow_D32.cpp",
         "src/core/SkBlitter.cpp",
         "src/core/SkBlitter_A8.cpp",
         "src/core/SkBlitter_ARGB32.cpp",
-        "src/core/SkBlitter_PM4f.cpp",
-        "src/core/SkBlitter_RGB16.cpp",
         "src/core/SkBlitter_Sprite.cpp",
         "src/core/SkBlurImageFilter.cpp",
         "src/core/SkBuffer.cpp",
@@ -130,20 +131,18 @@
         "src/core/SkClipStackDevice.cpp",
         "src/core/SkColor.cpp",
         "src/core/SkColorFilter.cpp",
-        "src/core/SkColorFilterShader.cpp",
         "src/core/SkColorLookUpTable.cpp",
         "src/core/SkColorMatrixFilterRowMajor255.cpp",
-        "src/core/SkColorShader.cpp",
         "src/core/SkColorSpace.cpp",
         "src/core/SkColorSpaceXform.cpp",
         "src/core/SkColorSpaceXformCanvas.cpp",
+        "src/core/SkColorSpaceXformImageGenerator.cpp",
         "src/core/SkColorSpaceXform_A2B.cpp",
         "src/core/SkColorSpaceXformer.cpp",
         "src/core/SkColorSpace_A2B.cpp",
         "src/core/SkColorSpace_ICC.cpp",
         "src/core/SkColorSpace_XYZ.cpp",
         "src/core/SkColorTable.cpp",
-        "src/core/SkComposeShader.cpp",
         "src/core/SkConvertPixels.cpp",
         "src/core/SkConvolver.cpp",
         "src/core/SkCpu.cpp",
@@ -160,6 +159,7 @@
         "src/core/SkDocument.cpp",
         "src/core/SkDraw.cpp",
         "src/core/SkDrawLooper.cpp",
+        "src/core/SkDraw_vertices.cpp",
         "src/core/SkDrawable.cpp",
         "src/core/SkEdge.cpp",
         "src/core/SkEdgeBuilder.cpp",
@@ -181,20 +181,17 @@
         "src/core/SkGraphics.cpp",
         "src/core/SkHalf.cpp",
         "src/core/SkICC.cpp",
-        "src/core/SkImageCacherator.cpp",
         "src/core/SkImageFilter.cpp",
         "src/core/SkImageFilterCache.cpp",
         "src/core/SkImageGenerator.cpp",
         "src/core/SkImageInfo.cpp",
         "src/core/SkLatticeIter.cpp",
-        "src/core/SkLightingShader.cpp",
         "src/core/SkLights.cpp",
         "src/core/SkLineClipper.cpp",
         "src/core/SkLinearBitmapPipeline.cpp",
         "src/core/SkLiteDL.cpp",
         "src/core/SkLiteRecorder.cpp",
         "src/core/SkLocalMatrixImageFilter.cpp",
-        "src/core/SkLocalMatrixShader.cpp",
         "src/core/SkMD5.cpp",
         "src/core/SkMallocPixelRef.cpp",
         "src/core/SkMask.cpp",
@@ -210,7 +207,6 @@
         "src/core/SkMipMap.cpp",
         "src/core/SkModeColorFilter.cpp",
         "src/core/SkMultiPictureDraw.cpp",
-        "src/core/SkNormalBevelSource.cpp",
         "src/core/SkNormalFlatSource.cpp",
         "src/core/SkNormalMapSource.cpp",
         "src/core/SkNormalSource.cpp",
@@ -231,7 +227,6 @@
         "src/core/SkPicturePlayback.cpp",
         "src/core/SkPictureRecord.cpp",
         "src/core/SkPictureRecorder.cpp",
-        "src/core/SkPictureShader.cpp",
         "src/core/SkPixelRef.cpp",
         "src/core/SkPixmap.cpp",
         "src/core/SkPoint.cpp",
@@ -241,7 +236,6 @@
         "src/core/SkRRect.cpp",
         "src/core/SkRTree.cpp",
         "src/core/SkRWBuffer.cpp",
-        "src/core/SkRadialShadowMapShader.cpp",
         "src/core/SkRasterClip.cpp",
         "src/core/SkRasterPipeline.cpp",
         "src/core/SkRasterPipelineBlitter.cpp",
@@ -268,16 +262,11 @@
         "src/core/SkScan_Hairline.cpp",
         "src/core/SkScan_Path.cpp",
         "src/core/SkSemaphore.cpp",
-        "src/core/SkShader.cpp",
-        "src/core/SkShadowShader.cpp",
         "src/core/SkSharedMutex.cpp",
-        "src/core/SkSpanProcs.cpp",
         "src/core/SkSpecialImage.cpp",
         "src/core/SkSpecialSurface.cpp",
         "src/core/SkSpinlock.cpp",
-        "src/core/SkSpriteBlitter4f.cpp",
         "src/core/SkSpriteBlitter_ARGB32.cpp",
-        "src/core/SkSpriteBlitter_RGB16.cpp",
         "src/core/SkStream.cpp",
         "src/core/SkString.cpp",
         "src/core/SkStringUtils.cpp",
@@ -290,13 +279,13 @@
         "src/core/SkTaskGroup.cpp",
         "src/core/SkTextBlob.cpp",
         "src/core/SkThreadID.cpp",
+        "src/core/SkThreadedBMPDevice.cpp",
         "src/core/SkTime.cpp",
         "src/core/SkTypeface.cpp",
         "src/core/SkTypefaceCache.cpp",
         "src/core/SkUnPreMultiply.cpp",
         "src/core/SkUtils.cpp",
         "src/core/SkValidatingReadBuffer.cpp",
-        "src/core/SkVarAlloc.cpp",
         "src/core/SkVertState.cpp",
         "src/core/SkVertices.cpp",
         "src/core/SkWriteBuffer.cpp",
@@ -327,7 +316,6 @@
         "src/effects/SkDropShadowImageFilter.cpp",
         "src/effects/SkEmbossMask.cpp",
         "src/effects/SkEmbossMaskFilter.cpp",
-        "src/effects/SkGaussianEdgeShader.cpp",
         "src/effects/SkHighContrastFilter.cpp",
         "src/effects/SkImageSource.cpp",
         "src/effects/SkLayerDrawLooper.cpp",
@@ -343,29 +331,18 @@
         "src/effects/SkPackBits.cpp",
         "src/effects/SkPaintFlagsDrawFilter.cpp",
         "src/effects/SkPaintImageFilter.cpp",
-        "src/effects/SkPerlinNoiseShader.cpp",
         "src/effects/SkPictureImageFilter.cpp",
         "src/effects/SkRRectsGaussianEdgeMaskFilter.cpp",
         "src/effects/SkTableColorFilter.cpp",
         "src/effects/SkTableMaskFilter.cpp",
         "src/effects/SkTileImageFilter.cpp",
         "src/effects/SkXfermodeImageFilter.cpp",
-        "src/effects/gradients/Sk4fGradientBase.cpp",
-        "src/effects/gradients/Sk4fLinearGradient.cpp",
-        "src/effects/gradients/SkClampRange.cpp",
-        "src/effects/gradients/SkGradientBitmapCache.cpp",
-        "src/effects/gradients/SkGradientShader.cpp",
-        "src/effects/gradients/SkLinearGradient.cpp",
-        "src/effects/gradients/SkRadialGradient.cpp",
-        "src/effects/gradients/SkSweepGradient.cpp",
-        "src/effects/gradients/SkTwoPointConicalGradient.cpp",
-        "src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp",
-        "src/effects/shadows/SkAmbientShadowMaskFilter.cpp",
-        "src/effects/shadows/SkSpotShadowMaskFilter.cpp",
-        "src/fonts/SkGScalerContext.cpp",
         "src/fonts/SkRandomScalerContext.cpp",
         "src/fonts/SkTestScalerContext.cpp",
+        "src/gpu/GrAHardwareBufferImageGenerator.cpp",
         "src/gpu/GrAuditTrail.cpp",
+        "src/gpu/GrBackendSurface.cpp",
+        "src/gpu/GrBackendTextureImageGenerator.cpp",
         "src/gpu/GrBitmapTextureMaker.cpp",
         "src/gpu/GrBlend.cpp",
         "src/gpu/GrBlurUtils.cpp",
@@ -390,6 +367,7 @@
         "src/gpu/GrGpuResourceRef.cpp",
         "src/gpu/GrImageTextureMaker.cpp",
         "src/gpu/GrMemoryPool.cpp",
+        "src/gpu/GrOnFlushResourceProvider.cpp",
         "src/gpu/GrOpFlushState.cpp",
         "src/gpu/GrOpList.cpp",
         "src/gpu/GrPaint.cpp",
@@ -402,10 +380,9 @@
         "src/gpu/GrPathRenderingRenderTargetContext.cpp",
         "src/gpu/GrPathUtils.cpp",
         "src/gpu/GrPipeline.cpp",
-        "src/gpu/GrPipelineAnalysis.cpp",
-        "src/gpu/GrPreFlushResourceProvider.cpp",
         "src/gpu/GrPrimitiveProcessor.cpp",
         "src/gpu/GrProcessor.cpp",
+        "src/gpu/GrProcessorAnalysis.cpp",
         "src/gpu/GrProcessorSet.cpp",
         "src/gpu/GrProcessorUnitTest.cpp",
         "src/gpu/GrProgramDesc.cpp",
@@ -418,6 +395,7 @@
         "src/gpu/GrRenderTargetProxy.cpp",
         "src/gpu/GrResourceCache.cpp",
         "src/gpu/GrResourceProvider.cpp",
+        "src/gpu/GrSKSLPrettyPrint.cpp",
         "src/gpu/GrSWMaskHelper.cpp",
         "src/gpu/GrShaderCaps.cpp",
         "src/gpu/GrShaderVar.cpp",
@@ -439,7 +417,6 @@
         "src/gpu/GrTextureProducer.cpp",
         "src/gpu/GrTextureProxy.cpp",
         "src/gpu/GrTextureRenderTargetProxy.cpp",
-        "src/gpu/GrTextureToYUVPlanes.cpp",
         "src/gpu/GrTraceMarker.cpp",
         "src/gpu/GrXferProcessor.cpp",
         "src/gpu/GrYUVProvider.cpp",
@@ -479,7 +456,6 @@
         "src/gpu/gl/GrGLCreateNullInterface.cpp",
         "src/gpu/gl/GrGLDefaultInterface_native.cpp",
         "src/gpu/gl/GrGLExtensions.cpp",
-        "src/gpu/gl/GrGLExternalTextureData.cpp",
         "src/gpu/gl/GrGLGLSL.cpp",
         "src/gpu/gl/GrGLGpu.cpp",
         "src/gpu/gl/GrGLGpuProgramCache.cpp",
@@ -500,7 +476,6 @@
         "src/gpu/gl/GrGLVertexArray.cpp",
         "src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp",
         "src/gpu/gl/builders/GrGLProgramBuilder.cpp",
-        "src/gpu/gl/builders/GrGLSLPrettyPrint.cpp",
         "src/gpu/gl/builders/GrGLShaderStringBuilder.cpp",
         "src/gpu/glsl/GrGLSL.cpp",
         "src/gpu/glsl/GrGLSLBlend.cpp",
@@ -518,6 +493,7 @@
         "src/gpu/glsl/GrGLSLXferProcessor.cpp",
         "src/gpu/instanced/GLInstancedRendering.cpp",
         "src/gpu/instanced/InstanceProcessor.cpp",
+        "src/gpu/instanced/InstancedOp.cpp",
         "src/gpu/instanced/InstancedRendering.cpp",
         "src/gpu/ops/GrAAConvexPathRenderer.cpp",
         "src/gpu/ops/GrAAConvexTessellator.cpp",
@@ -538,7 +514,6 @@
         "src/gpu/ops/GrMSAAPathRenderer.cpp",
         "src/gpu/ops/GrMeshDrawOp.cpp",
         "src/gpu/ops/GrNonAAFillRectOp.cpp",
-        "src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp",
         "src/gpu/ops/GrNonAAStrokeRectOp.cpp",
         "src/gpu/ops/GrOp.cpp",
         "src/gpu/ops/GrOvalOpFactory.cpp",
@@ -559,6 +534,7 @@
         "src/gpu/text/GrTextUtils.cpp",
         "src/gpu/vk/GrVkBackendContext.cpp",
         "src/gpu/vk/GrVkBuffer.cpp",
+        "src/gpu/vk/GrVkBufferView.cpp",
         "src/gpu/vk/GrVkCaps.cpp",
         "src/gpu/vk/GrVkCommandBuffer.cpp",
         "src/gpu/vk/GrVkCopyManager.cpp",
@@ -586,6 +562,7 @@
         "src/gpu/vk/GrVkSampler.cpp",
         "src/gpu/vk/GrVkSemaphore.cpp",
         "src/gpu/vk/GrVkStencilAttachment.cpp",
+        "src/gpu/vk/GrVkTexelBuffer.cpp",
         "src/gpu/vk/GrVkTexture.cpp",
         "src/gpu/vk/GrVkTextureRenderTarget.cpp",
         "src/gpu/vk/GrVkTransferBuffer.cpp",
@@ -595,20 +572,19 @@
         "src/gpu/vk/GrVkVaryingHandler.cpp",
         "src/gpu/vk/GrVkVertexBuffer.cpp",
         "src/image/SkImage.cpp",
-        "src/image/SkImageShader.cpp",
-        "src/image/SkImage_Generator.cpp",
         "src/image/SkImage_Gpu.cpp",
+        "src/image/SkImage_Lazy.cpp",
         "src/image/SkImage_Raster.cpp",
         "src/image/SkSurface.cpp",
         "src/image/SkSurface_Gpu.cpp",
         "src/image/SkSurface_Raster.cpp",
         "src/images/SkImageEncoder.cpp",
-        "src/images/SkJPEGImageEncoder.cpp",
         "src/images/SkJPEGWriteUtility.cpp",
-        "src/images/SkPNGImageEncoder.cpp",
-        "src/images/SkWEBPImageEncoder.cpp",
+        "src/images/SkJpegEncoder.cpp",
+        "src/images/SkPngEncoder.cpp",
+        "src/images/SkWebpEncoder.cpp",
         "src/jumper/SkJumper.cpp",
-        "src/jumper/SkJumper_generated.cpp",
+        "src/jumper/SkJumper_generated.S",
         "src/jumper/SkJumper_stages.cpp",
         "src/lazy/SkDiscardableMemoryPool.cpp",
         "src/pathops/SkAddIntersections.cpp",
@@ -679,12 +655,33 @@
         "src/ports/SkTLS_pthread.cpp",
         "src/sfnt/SkOTTable_name.cpp",
         "src/sfnt/SkOTUtils.cpp",
+        "src/shaders/SkBitmapProcShader.cpp",
+        "src/shaders/SkColorFilterShader.cpp",
+        "src/shaders/SkColorShader.cpp",
+        "src/shaders/SkComposeShader.cpp",
+        "src/shaders/SkImageShader.cpp",
+        "src/shaders/SkLightingShader.cpp",
+        "src/shaders/SkLocalMatrixShader.cpp",
+        "src/shaders/SkPerlinNoiseShader.cpp",
+        "src/shaders/SkPictureShader.cpp",
+        "src/shaders/SkShader.cpp",
+        "src/shaders/gradients/Sk4fGradientBase.cpp",
+        "src/shaders/gradients/Sk4fLinearGradient.cpp",
+        "src/shaders/gradients/SkClampRange.cpp",
+        "src/shaders/gradients/SkGradientBitmapCache.cpp",
+        "src/shaders/gradients/SkGradientShader.cpp",
+        "src/shaders/gradients/SkLinearGradient.cpp",
+        "src/shaders/gradients/SkRadialGradient.cpp",
+        "src/shaders/gradients/SkSweepGradient.cpp",
+        "src/shaders/gradients/SkTwoPointConicalGradient.cpp",
+        "src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp",
         "src/sksl/SkSLCFGGenerator.cpp",
         "src/sksl/SkSLCompiler.cpp",
         "src/sksl/SkSLGLSLCodeGenerator.cpp",
         "src/sksl/SkSLIRGenerator.cpp",
         "src/sksl/SkSLParser.cpp",
         "src/sksl/SkSLSPIRVCodeGenerator.cpp",
+        "src/sksl/SkSLString.cpp",
         "src/sksl/SkSLUtil.cpp",
         "src/sksl/ir/SkSLSymbolTable.cpp",
         "src/sksl/ir/SkSLType.cpp",
@@ -714,8 +711,6 @@
         "src/utils/SkParseColor.cpp",
         "src/utils/SkParsePath.cpp",
         "src/utils/SkPatchUtils.cpp",
-        "src/utils/SkRGBAToYUV.cpp",
-        "src/utils/SkShadowPaintFilterCanvas.cpp",
         "src/utils/SkShadowTessellator.cpp",
         "src/utils/SkShadowUtils.cpp",
         "src/utils/SkTextBox.cpp",
@@ -736,7 +731,6 @@
         "src/xml/SkXMLWriter.cpp",
         "src/xps/SkXPSDevice.cpp",
         "src/xps/SkXPSDocument.cpp",
-        "third_party/etc1/etc1.cpp",
         "third_party/gif/SkGifImageReader.cpp",
     ],
 
@@ -831,6 +825,7 @@
         "libvulkan",
         "libz",
         "libcutils",
+        "libnativewindow",
     ],
     static_libs: [
         "libarect",
@@ -856,6 +851,7 @@
         "include/config/",
         "include/core/",
         "include/effects/",
+        "include/encode/",
         "include/gpu/",
         "include/gpu/gl/",
         "include/pathops/",
@@ -868,7 +864,6 @@
         "src/codec/",
         "src/core/",
         "src/effects/",
-        "src/effects/gradients/",
         "src/fonts/",
         "src/gpu/",
         "src/image/",
@@ -879,12 +874,13 @@
         "src/pdf/",
         "src/ports/",
         "src/sfnt/",
+        "src/shaders/",
+        "src/shaders/gradients/",
         "src/sksl/",
         "src/utils/",
         "src/utils/win/",
         "src/xml/",
         "tests/",
-        "third_party/etc1/",
         "third_party/gif/",
         "tools/",
         "tools/debugger/",
@@ -937,6 +933,7 @@
         "gm/beziers.cpp",
         "gm/bigblurs.cpp",
         "gm/bigmatrix.cpp",
+        "gm/bigrect.cpp",
         "gm/bigrrectaaeffect.cpp",
         "gm/bigtext.cpp",
         "gm/bigtileimagefilter.cpp",
@@ -952,6 +949,7 @@
         "gm/blend.cpp",
         "gm/blurcircles.cpp",
         "gm/blurcircles2.cpp",
+        "gm/blurignorexform.cpp",
         "gm/blurquickreject.cpp",
         "gm/blurrect.cpp",
         "gm/blurredclippedcircle.cpp",
@@ -961,6 +959,7 @@
         "gm/bug5252.cpp",
         "gm/bug530095.cpp",
         "gm/bug615686.cpp",
+        "gm/bug6643.cpp",
         "gm/cgm.c",
         "gm/cgms.cpp",
         "gm/circles.cpp",
@@ -972,13 +971,12 @@
         "gm/clippedbitmapshaders.cpp",
         "gm/color4f.cpp",
         "gm/coloremoji.cpp",
+        "gm/coloremoji_blendmodes.cpp",
         "gm/colorfilteralpha8.cpp",
         "gm/colorfilterimagefilter.cpp",
         "gm/colorfilters.cpp",
         "gm/colormatrix.cpp",
         "gm/colorspacexform.cpp",
-        "gm/colortype.cpp",
-        "gm/colortypexfermode.cpp",
         "gm/colorwheel.cpp",
         "gm/complexclip.cpp",
         "gm/complexclip2.cpp",
@@ -996,6 +994,7 @@
         "gm/copyTo4444.cpp",
         "gm/crbug_691386.cpp",
         "gm/croppedrects.cpp",
+        "gm/crosscontextimage.cpp",
         "gm/cubicpaths.cpp",
         "gm/dashcircle.cpp",
         "gm/dashcubics.cpp",
@@ -1022,10 +1021,10 @@
         "gm/dstreadshuffle.cpp",
         "gm/emboss.cpp",
         "gm/emptypath.cpp",
+        "gm/encode-alpha-jpeg.cpp",
         "gm/encode-platform.cpp",
         "gm/encode-srgb.cpp",
         "gm/encode.cpp",
-        "gm/etc1.cpp",
         "gm/extractbitmap.cpp",
         "gm/fadefilter.cpp",
         "gm/fatpathfill.cpp",
@@ -1043,7 +1042,6 @@
         "gm/gammaencodedpremul.cpp",
         "gm/gammatext.cpp",
         "gm/gamut.cpp",
-        "gm/gaussianedge.cpp",
         "gm/getpostextpath.cpp",
         "gm/giantbitmap.cpp",
         "gm/glyph_pos.cpp",
@@ -1061,6 +1059,7 @@
         "gm/hardstop_gradients.cpp",
         "gm/highcontrastfilter.cpp",
         "gm/hittestpath.cpp",
+        "gm/hsl.cpp",
         "gm/image.cpp",
         "gm/image_pict.cpp",
         "gm/image_shader.cpp",
@@ -1085,7 +1084,6 @@
         "gm/imagescalealigned.cpp",
         "gm/imagesource.cpp",
         "gm/imagesource2.cpp",
-        "gm/imagetoyuvplanes.cpp",
         "gm/internal_links.cpp",
         "gm/inversepaths.cpp",
         "gm/largeglyphblur.cpp",
@@ -1096,11 +1094,11 @@
         "gm/lighting.cpp",
         "gm/lightingshader.cpp",
         "gm/lightingshader2.cpp",
-        "gm/lightingshaderbevel.cpp",
         "gm/linepaths.cpp",
         "gm/localmatriximagefilter.cpp",
         "gm/localmatriximageshader.cpp",
         "gm/lumafilter.cpp",
+        "gm/makecolorspace.cpp",
         "gm/manypaths.cpp",
         "gm/matrixconvolution.cpp",
         "gm/matriximagefilter.cpp",
@@ -1134,6 +1132,7 @@
         "gm/pictureimagefilter.cpp",
         "gm/pictureimagegenerator.cpp",
         "gm/pictureshader.cpp",
+        "gm/pictureshadercache.cpp",
         "gm/pictureshadertile.cpp",
         "gm/pixelsnap.cpp",
         "gm/plus.cpp",
@@ -1141,6 +1140,7 @@
         "gm/poly2poly.cpp",
         "gm/polygons.cpp",
         "gm/quadpaths.cpp",
+        "gm/radial_gradient_precision.cpp",
         "gm/readpixels.cpp",
         "gm/recordopts.cpp",
         "gm/rectangletexture.cpp",
@@ -1159,11 +1159,11 @@
         "gm/shadertext.cpp",
         "gm/shadertext2.cpp",
         "gm/shadertext3.cpp",
-        "gm/shadowmaps.cpp",
         "gm/shadows.cpp",
         "gm/shadowutils.cpp",
         "gm/shallowgradient.cpp",
         "gm/shapes.cpp",
+        "gm/shapes_as_paths.cpp",
         "gm/showmiplevels.cpp",
         "gm/simple_magnification.cpp",
         "gm/simpleaaclip.cpp",
@@ -1224,8 +1224,8 @@
         "gm/xfermodes.cpp",
         "gm/xfermodes2.cpp",
         "gm/xfermodes3.cpp",
+        "gm/xform_image_gen.cpp",
         "gm/yuvtorgbeffect.cpp",
-        "src/utils/SkMultiPictureDocumentReader.cpp",
         "tests/AAClipTest.cpp",
         "tests/AnnotationTest.cpp",
         "tests/ApplyGammaTest.cpp",
@@ -1238,7 +1238,6 @@
         "tests/BitmapTest.cpp",
         "tests/BlendTest.cpp",
         "tests/BlitMaskClip.cpp",
-        "tests/BlitRowTest.cpp",
         "tests/BlurTest.cpp",
         "tests/CPlusPlusEleven.cpp",
         "tests/CTest.cpp",
@@ -1265,7 +1264,6 @@
         "tests/ColorSpaceXformTest.cpp",
         "tests/ColorTest.cpp",
         "tests/CopySurfaceTest.cpp",
-        "tests/CrossContextImageTest.cpp",
         "tests/DFPathRendererTest.cpp",
         "tests/DashPathEffectTest.cpp",
         "tests/DataRefTest.cpp",
@@ -1282,7 +1280,9 @@
         "tests/DynamicHashTest.cpp",
         "tests/EGLImageTest.cpp",
         "tests/EmptyPathTest.cpp",
+        "tests/EncodeTest.cpp",
         "tests/ExifTest.cpp",
+        "tests/F16StagesTest.cpp",
         "tests/FillPathTest.cpp",
         "tests/FitsInTest.cpp",
         "tests/FlattenDrawableTest.cpp",
@@ -1308,10 +1308,10 @@
         "tests/GrContextAbandonTest.cpp",
         "tests/GrContextFactoryTest.cpp",
         "tests/GrDrawTargetTest.cpp",
-        "tests/GrGLSLPrettyPrintTest.cpp",
-        "tests/GrGetCoeffBlendKnownComponentsTest.cpp",
         "tests/GrMemoryPoolTest.cpp",
+        "tests/GrMeshTest.cpp",
         "tests/GrPorterDuffTest.cpp",
+        "tests/GrSKSLPrettyPrintTest.cpp",
         "tests/GrShapeTest.cpp",
         "tests/GrSurfaceTest.cpp",
         "tests/GrTRecorderTest.cpp",
@@ -1354,12 +1354,12 @@
         "tests/MetaDataTest.cpp",
         "tests/MipMapTest.cpp",
         "tests/OSPathTest.cpp",
+        "tests/OnFlushCallbackTest.cpp",
         "tests/OnceTest.cpp",
         "tests/OverAlignedTest.cpp",
         "tests/PDFDeflateWStreamTest.cpp",
         "tests/PDFDocumentTest.cpp",
         "tests/PDFGlyphsToUnicodeTest.cpp",
-        "tests/PDFInvalidBitmapTest.cpp",
         "tests/PDFJpegEmbedTest.cpp",
         "tests/PDFMetadataAttributeTest.cpp",
         "tests/PDFOpaqueSrcModeToSrcOverTest.cpp",
@@ -1369,6 +1369,7 @@
         "tests/PaintBreakTextTest.cpp",
         "tests/PaintImageFilterTest.cpp",
         "tests/PaintTest.cpp",
+        "tests/ParametricStageTest.cpp",
         "tests/ParsePathTest.cpp",
         "tests/PathCoverageTest.cpp",
         "tests/PathMeasureTest.cpp",
@@ -1434,7 +1435,6 @@
         "tests/PixelRefTest.cpp",
         "tests/Point3Test.cpp",
         "tests/PointTest.cpp",
-        "tests/PreFlushCallbackTest.cpp",
         "tests/PremulAlphaRoundTripTest.cpp",
         "tests/PrimitiveProcessorTest.cpp",
         "tests/ProcessorTest.cpp",
@@ -1489,6 +1489,7 @@
         "tests/SkSLErrorTest.cpp",
         "tests/SkSLGLSLTest.cpp",
         "tests/SkSLMemoryLayoutTest.cpp",
+        "tests/SkSLSPIRVTest.cpp",
         "tests/SkSharedMutexTest.cpp",
         "tests/SortTest.cpp",
         "tests/SpecialImageTest.cpp",
@@ -1520,7 +1521,6 @@
         "tests/TypefaceTest.cpp",
         "tests/UnicodeTest.cpp",
         "tests/UtilsTest.cpp",
-        "tests/VarAllocTest.cpp",
         "tests/VerticesTest.cpp",
         "tests/VkClearTests.cpp",
         "tests/VkHeapTests.cpp",
@@ -1581,6 +1581,7 @@
         "libicuuc",
         "libjpeg",
         "liblog",
+        "libnativewindow",
         "libpiex",
         "libpng",
         "libvulkan",
@@ -1614,6 +1615,7 @@
         "include/config/",
         "include/core/",
         "include/effects/",
+        "include/encode/",
         "include/gpu/",
         "include/gpu/gl/",
         "include/pathops/",
@@ -1626,7 +1628,6 @@
         "src/codec/",
         "src/core/",
         "src/effects/",
-        "src/effects/gradients/",
         "src/fonts/",
         "src/gpu/",
         "src/image/",
@@ -1637,11 +1638,12 @@
         "src/pdf/",
         "src/ports/",
         "src/sfnt/",
+        "src/shaders/",
+        "src/shaders/gradients/",
         "src/sksl/",
         "src/utils/",
         "src/utils/win/",
         "src/xml/",
-        "third_party/etc1/",
         "third_party/gif/",
         "tools/",
         "tools/debugger/",
@@ -1671,6 +1673,7 @@
         "bench/ChartBench.cpp",
         "bench/ChecksumBench.cpp",
         "bench/ChromeBench.cpp",
+        "bench/ClipMaskBench.cpp",
         "bench/CmapBench.cpp",
         "bench/CodecBench.cpp",
         "bench/ColorCanvasDrawBitmapBench.cpp",
@@ -1747,6 +1750,7 @@
         "bench/SKPBench.cpp",
         "bench/ScalarBench.cpp",
         "bench/ShaderMaskBench.cpp",
+        "bench/ShadowBench.cpp",
         "bench/ShapesBench.cpp",
         "bench/Sk4fBench.cpp",
         "bench/SkBlend_optsBench.cpp",
@@ -1811,6 +1815,7 @@
         "gm/beziers.cpp",
         "gm/bigblurs.cpp",
         "gm/bigmatrix.cpp",
+        "gm/bigrect.cpp",
         "gm/bigrrectaaeffect.cpp",
         "gm/bigtext.cpp",
         "gm/bigtileimagefilter.cpp",
@@ -1826,6 +1831,7 @@
         "gm/blend.cpp",
         "gm/blurcircles.cpp",
         "gm/blurcircles2.cpp",
+        "gm/blurignorexform.cpp",
         "gm/blurquickreject.cpp",
         "gm/blurrect.cpp",
         "gm/blurredclippedcircle.cpp",
@@ -1835,6 +1841,7 @@
         "gm/bug5252.cpp",
         "gm/bug530095.cpp",
         "gm/bug615686.cpp",
+        "gm/bug6643.cpp",
         "gm/cgm.c",
         "gm/cgms.cpp",
         "gm/circles.cpp",
@@ -1846,13 +1853,12 @@
         "gm/clippedbitmapshaders.cpp",
         "gm/color4f.cpp",
         "gm/coloremoji.cpp",
+        "gm/coloremoji_blendmodes.cpp",
         "gm/colorfilteralpha8.cpp",
         "gm/colorfilterimagefilter.cpp",
         "gm/colorfilters.cpp",
         "gm/colormatrix.cpp",
         "gm/colorspacexform.cpp",
-        "gm/colortype.cpp",
-        "gm/colortypexfermode.cpp",
         "gm/colorwheel.cpp",
         "gm/complexclip.cpp",
         "gm/complexclip2.cpp",
@@ -1870,6 +1876,7 @@
         "gm/copyTo4444.cpp",
         "gm/crbug_691386.cpp",
         "gm/croppedrects.cpp",
+        "gm/crosscontextimage.cpp",
         "gm/cubicpaths.cpp",
         "gm/dashcircle.cpp",
         "gm/dashcubics.cpp",
@@ -1896,10 +1903,10 @@
         "gm/dstreadshuffle.cpp",
         "gm/emboss.cpp",
         "gm/emptypath.cpp",
+        "gm/encode-alpha-jpeg.cpp",
         "gm/encode-platform.cpp",
         "gm/encode-srgb.cpp",
         "gm/encode.cpp",
-        "gm/etc1.cpp",
         "gm/extractbitmap.cpp",
         "gm/fadefilter.cpp",
         "gm/fatpathfill.cpp",
@@ -1917,7 +1924,6 @@
         "gm/gammaencodedpremul.cpp",
         "gm/gammatext.cpp",
         "gm/gamut.cpp",
-        "gm/gaussianedge.cpp",
         "gm/getpostextpath.cpp",
         "gm/giantbitmap.cpp",
         "gm/glyph_pos.cpp",
@@ -1935,6 +1941,7 @@
         "gm/hardstop_gradients.cpp",
         "gm/highcontrastfilter.cpp",
         "gm/hittestpath.cpp",
+        "gm/hsl.cpp",
         "gm/image.cpp",
         "gm/image_pict.cpp",
         "gm/image_shader.cpp",
@@ -1959,7 +1966,6 @@
         "gm/imagescalealigned.cpp",
         "gm/imagesource.cpp",
         "gm/imagesource2.cpp",
-        "gm/imagetoyuvplanes.cpp",
         "gm/internal_links.cpp",
         "gm/inversepaths.cpp",
         "gm/largeglyphblur.cpp",
@@ -1970,11 +1976,11 @@
         "gm/lighting.cpp",
         "gm/lightingshader.cpp",
         "gm/lightingshader2.cpp",
-        "gm/lightingshaderbevel.cpp",
         "gm/linepaths.cpp",
         "gm/localmatriximagefilter.cpp",
         "gm/localmatriximageshader.cpp",
         "gm/lumafilter.cpp",
+        "gm/makecolorspace.cpp",
         "gm/manypaths.cpp",
         "gm/matrixconvolution.cpp",
         "gm/matriximagefilter.cpp",
@@ -2008,6 +2014,7 @@
         "gm/pictureimagefilter.cpp",
         "gm/pictureimagegenerator.cpp",
         "gm/pictureshader.cpp",
+        "gm/pictureshadercache.cpp",
         "gm/pictureshadertile.cpp",
         "gm/pixelsnap.cpp",
         "gm/plus.cpp",
@@ -2015,6 +2022,7 @@
         "gm/poly2poly.cpp",
         "gm/polygons.cpp",
         "gm/quadpaths.cpp",
+        "gm/radial_gradient_precision.cpp",
         "gm/readpixels.cpp",
         "gm/recordopts.cpp",
         "gm/rectangletexture.cpp",
@@ -2033,11 +2041,11 @@
         "gm/shadertext.cpp",
         "gm/shadertext2.cpp",
         "gm/shadertext3.cpp",
-        "gm/shadowmaps.cpp",
         "gm/shadows.cpp",
         "gm/shadowutils.cpp",
         "gm/shallowgradient.cpp",
         "gm/shapes.cpp",
+        "gm/shapes_as_paths.cpp",
         "gm/showmiplevels.cpp",
         "gm/simple_magnification.cpp",
         "gm/simpleaaclip.cpp",
@@ -2098,8 +2106,8 @@
         "gm/xfermodes.cpp",
         "gm/xfermodes2.cpp",
         "gm/xfermodes3.cpp",
+        "gm/xform_image_gen.cpp",
         "gm/yuvtorgbeffect.cpp",
-        "src/utils/SkMultiPictureDocumentReader.cpp",
         "tools/AndroidSkDebugToStdOut.cpp",
         "tools/CrashHandler.cpp",
         "tools/LsanSuppressions.cpp",
@@ -2147,6 +2155,7 @@
         "libicuuc",
         "libjpeg",
         "liblog",
+        "libnativewindow",
         "libpiex",
         "libpng",
         "libvulkan",
diff --git a/BUILD.gn b/BUILD.gn
index 4999d74..bb355c6 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -6,6 +6,10 @@
 import("gn/android_framework_defines.gni")
 import("gn/shared_sources.gni")
 
+if (is_fuchsia) {
+  import("//build/vulkan/config.gni")
+}
+
 if (!defined(is_skia_standalone)) {
   is_skia_standalone = false
 }
@@ -13,6 +17,7 @@
 
 declare_args() {
   skia_use_angle = false
+  skia_use_egl = false
   skia_use_expat = true
   skia_use_fontconfig = is_linux
   skia_use_freetype = is_android || is_fuchsia || is_linux
@@ -30,7 +35,6 @@
   skia_enable_android_framework_defines = false
   skia_enable_discrete_gpu = true
   skia_enable_effects = true
-  skia_enable_jumper = is_skia_dev_build
   skia_enable_gpu = true
   skia_enable_pdf = true
   skia_enable_spirv_validation = is_skia_dev_build && is_debug
@@ -44,10 +48,26 @@
 
   if (is_android) {
     skia_use_vulkan = defined(ndk_api) && ndk_api >= 24
+  } else if (is_fuchsia) {
+    skia_use_vulkan = fuchsia_use_vulkan
   } else {
     skia_use_vulkan = skia_vulkan_sdk != ""
   }
 }
+declare_args() {
+  if (skia_use_vulkan) {
+    if (is_fuchsia) {
+      skia_vulkan_headers = "$fuchsia_vulkan_sdk/include"
+    } else if (is_linux || is_win) {
+      skia_vulkan_headers = "$skia_vulkan_sdk/include"
+    } else {
+      # When buliding on Android we get the header via the NDK
+      skia_vulkan_headers = ""
+    }
+  } else {
+    skia_vulkan_headers = "third_party/vulkan"
+  }
+}
 
 # Our tools require static linking (they use non-exported symbols).
 skia_enable_tools = skia_enable_tools && !is_component_build
@@ -61,6 +81,7 @@
   "include/config",
   "include/core",
   "include/effects",
+  "include/encode",
   "include/gpu",
   "include/gpu/gl",
   "include/pathops",
@@ -73,6 +94,9 @@
 # Skia public API, generally provided by :skia.
 config("skia_public") {
   include_dirs = skia_public_includes
+  if (skia_vulkan_headers != "") {
+    include_dirs += [ skia_vulkan_headers ]
+  }
   defines = []
   if (is_component_build) {
     defines += [ "SKIA_DLL" ]
@@ -98,7 +122,6 @@
     "src/codec",
     "src/core",
     "src/effects",
-    "src/effects/gradients",
     "src/fonts",
     "src/image",
     "src/images",
@@ -108,18 +131,16 @@
     "src/pdf",
     "src/ports",
     "src/sfnt",
+    "src/shaders",
+    "src/shaders/gradients",
     "src/sksl",
     "src/utils",
     "src/utils/win",
     "src/xml",
-    "third_party/etc1",
     "third_party/gif",
   ]
 
-  defines = [
-    "SK_GAMMA_APPLY_TO_A8",
-    "SK_INTERNAL",
-  ]
+  defines = [ "SK_GAMMA_APPLY_TO_A8" ]
   if (is_android) {
     defines += [
       "SK_GAMMA_EXPONENT=1.4",
@@ -133,23 +154,16 @@
   libs = []
   lib_dirs = []
   if (skia_use_vulkan) {
-    if (skia_vulkan_sdk != "" && !is_android) {
+    if (skia_vulkan_sdk != "" && !is_android && !is_fuchsia) {
       if (is_win) {
-        include_dirs += [ "$skia_vulkan_sdk/Include/" ]
         lib_dirs += [
           "$skia_vulkan_sdk/Bin",
           "$skia_vulkan_sdk/Lib",
         ]
       } else {
-        include_dirs += [ "$skia_vulkan_sdk/include/" ]
         lib_dirs += [ "$skia_vulkan_sdk/lib/" ]
       }
     }
-    if (is_win) {
-      libs += [ "vulkan-1.lib" ]
-    } else {
-      libs += [ "vulkan" ]
-    }
   }
   if (skia_enable_gpu) {
     include_dirs += [ "src/gpu" ]
@@ -411,6 +425,9 @@
   libs = []
   if (is_android) {
     sources += [ "src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp" ]
+  } else if (skia_use_egl) {
+    sources += [ "src/gpu/gl/egl/GrGLCreateNativeInterface_egl.cpp" ]
+    libs += [ "EGL" ]
   } else if (is_linux) {
     sources += [ "src/gpu/gl/glx/GrGLCreateNativeInterface_glx.cpp" ]
     libs += [
@@ -454,8 +471,8 @@
     "src/codec/SkJpegCodec.cpp",
     "src/codec/SkJpegDecoderMgr.cpp",
     "src/codec/SkJpegUtility.cpp",
-    "src/images/SkJPEGImageEncoder.cpp",
     "src/images/SkJPEGWriteUtility.cpp",
+    "src/images/SkJpegEncoder.cpp",
   ]
 }
 
@@ -485,7 +502,7 @@
   sources = [
     "src/codec/SkIcoCodec.cpp",
     "src/codec/SkPngCodec.cpp",
-    "src/images/SkPNGImageEncoder.cpp",
+    "src/images/SkPngEncoder.cpp",
   ]
 }
 
@@ -509,16 +526,6 @@
   ]
 }
 
-optional("jumper") {
-  enabled = skia_enable_jumper
-  public_defines = [ "SK_JUMPER" ]
-  sources = [
-    "src/jumper/SkJumper.cpp",
-    "src/jumper/SkJumper_generated.cpp",
-    "src/jumper/SkJumper_stages.cpp",
-  ]
-}
-
 optional("typeface_freetype") {
   enabled = skia_use_freetype
 
@@ -541,7 +548,7 @@
   sources = [
     "src/codec/SkWebpAdapterCodec.cpp",
     "src/codec/SkWebpCodec.cpp",
-    "src/images/SkWEBPImageEncoder.cpp",
+    "src/images/SkWebpEncoder.cpp",
   ]
 }
 
@@ -578,7 +585,6 @@
     ":gpu",
     ":hsw",
     ":jpeg",
-    ":jumper",
     ":none",
     ":pdf",
     ":png",
@@ -604,6 +610,7 @@
     "src/android/SkBitmapRegionCodec.cpp",
     "src/android/SkBitmapRegionDecoder.cpp",
     "src/codec/SkAndroidCodec.cpp",
+    "src/codec/SkBmpBaseCodec.cpp",
     "src/codec/SkBmpCodec.cpp",
     "src/codec/SkBmpMaskCodec.cpp",
     "src/codec/SkBmpRLECodec.cpp",
@@ -626,7 +633,6 @@
     "src/sfnt/SkOTTable_name.cpp",
     "src/sfnt/SkOTUtils.cpp",
     "src/utils/mac/SkStream_mac.cpp",
-    "third_party/etc1/etc1.cpp",
     "third_party/gif/SkGifImageReader.cpp",
   ]
 
@@ -696,6 +702,8 @@
       "src/ports/SkImageGeneratorCG.cpp",
     ]
     libs += [
+      # AppKit symbols NSFontWeightXXX may be dlsym'ed.
+      "AppKit.framework",
       "ApplicationServices.framework",
       "OpenGL.framework",
     ]
@@ -714,6 +722,9 @@
       "CoreText.framework",
       "ImageIO.framework",
       "MobileCoreServices.framework",
+
+      # UIKit symbols UIFontWeightXXX may be dlsym'ed.
+      "UIKit.framework",
     ]
   }
 
@@ -737,7 +748,8 @@
     skia_h = "$target_gen_dir/skia.h"
     script = "gn/find_headers.py"
     args = [ rebase_path(skia_h, root_build_dir) ] +
-           rebase_path(skia_public_includes)
+           rebase_path(skia_public_includes) +
+           [ rebase_path("third_party/vulkan") ]
     depfile = "$skia_h.deps"
     outputs = [
       skia_h,
@@ -749,15 +761,24 @@
     # TODO: worth fixing?
     executable("fiddle") {
       libs = []
-      if (is_linux) {
-        libs += [ "OSMesa" ]
-      }
-
       sources = [
         "tools/fiddle/draw.cpp",
         "tools/fiddle/fiddle_main.cpp",
       ]
+
+      if (skia_use_egl) {
+        sources += [ "tools/fiddle/egl_context.cpp" ]
+      } else if (skia_use_mesa) {
+        sources += [ "tools/fiddle/mesa_context.cpp" ]
+        if (is_linux) {
+          libs += [ "OSMesa" ]
+        }
+      } else {
+        sources += [ "tools/fiddle/null_context.cpp" ]
+      }
+      testonly = true
       deps = [
+        ":flags",
         ":skia",
         ":skia.h",
       ]
@@ -856,7 +877,7 @@
       ]
       libs = []
 
-      if (is_android) {
+      if (is_android || skia_use_egl) {
         sources += [ "tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp" ]
       } else if (is_ios) {
         sources += [ "tools/gpu/gl/iOS/CreatePlatformGLTestContext_iOS.mm" ]
@@ -885,6 +906,11 @@
       }
       if (skia_use_vulkan) {
         sources += [ "tools/gpu/vk/VkTestContext.cpp" ]
+        if (is_win) {
+          libs += [ "vulkan-1.lib" ]
+        } else {
+          libs += [ "vulkan" ]
+        }
       }
     }
   }
@@ -914,7 +940,6 @@
       "tools/timer",
     ]
     sources = [
-      "src/utils/SkMultiPictureDocumentReader.cpp",  # TODO(halcanary): move to tools?
       "tools/AndroidSkDebugToStdOut.cpp",
       "tools/CrashHandler.cpp",
       "tools/LsanSuppressions.cpp",
@@ -1108,7 +1133,6 @@
     public_include_dirs = [ "samplecode" ]
     include_dirs = [ "experimental" ]
     sources = samples_sources + [
-                "experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp",
                 "experimental/SkSetPoly3To3.cpp",
                 "experimental/SkSetPoly3To3_A.cpp",
                 "experimental/SkSetPoly3To3_D.cpp",
@@ -1164,6 +1188,7 @@
       ":gm",
       ":skia",
       ":tests",
+      ":tool_utils",
     ]
   }
 
@@ -1271,6 +1296,16 @@
     ]
   }
 
+  test_app("create_flutter_test_images") {
+    sources = [
+      "tools/create_flutter_test_images.cpp",
+    ]
+    deps = [
+      ":skia",
+      ":tool_utils",
+    ]
+  }
+
   test_app("get_images_from_skps") {
     sources = [
       "tools/get_images_from_skps.cpp",
@@ -1487,13 +1522,12 @@
 
   if (skia_enable_gpu) {
     test_app("skslc") {
+      defines = [ "SKSL_STANDALONE" ]
       sources = [
         "src/sksl/SkSLMain.cpp",
       ]
-      deps = [
-        ":flags",
-        ":skia",
-      ]
+      sources += skia_sksl_sources
+      include_dirs = [ "src/sksl" ]
     }
   }
 }
diff --git a/DEPS b/DEPS
index 2dc262d..79c723c 100644
--- a/DEPS
+++ b/DEPS
@@ -3,7 +3,7 @@
 deps = {
   "buildtools"                          : "https://chromium.googlesource.com/chromium/buildtools.git@e6b510a9daf822bbe9f922c200c58150803d2fd8",
   "common"                              : "https://skia.googlesource.com/common.git@9737551d7a52c3db3262db5856e6bcd62c462b92",
-  "third_party/externals/angle2"        : "https://chromium.googlesource.com/angle/angle.git@57f17473791703ac82add77c3d77d13d8e2dfbc4",
+  "third_party/externals/angle2"        : "https://chromium.googlesource.com/angle/angle.git@5978e28d3adc30d09c5cbdf51aa1e4b8dbfff6b1",
   "third_party/externals/dng_sdk"       : "https://android.googlesource.com/platform/external/dng_sdk.git@96443b262250c390b0caefbf3eed8463ba35ecae",
   "third_party/externals/expat"         : "https://android.googlesource.com/platform/external/expat.git@android-6.0.1_r55",
   "third_party/externals/freetype"      : "https://skia.googlesource.com/third_party/freetype2.git@447a0b62634802d8acdb56008cff5ff4e50be244",
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 0d58f7f..9695b82 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -444,17 +444,24 @@
   return results
 
 
+def _FooterExists(footers, key, value):
+  for k, v in footers:
+    if k == key and v == value:
+      return True
+  return False
+
+
 def PostUploadHook(cl, change, output_api):
   """git cl upload will call this hook after the issue is created/modified.
 
   This hook does the following:
   * Adds a link to preview docs changes if there are any docs changes in the CL.
-  * Adds 'NOTRY=true' if the CL contains only docs changes.
-  * Adds 'NOTREECHECKS=true' for non master branch changes since they do not
+  * Adds 'No-Try: true' if the CL contains only docs changes.
+  * Adds 'No-Tree-Checks: true' for non master branch changes since they do not
     need to be gated on the master branch's tree.
-  * Adds 'NOTRY=true' for non master branch changes since trybots do not yet
+  * Adds 'No-Try: true' for non master branch changes since trybots do not yet
     work on them.
-  * Adds 'NOPRESUBMIT=true' for non master branch changes since those don't
+  * Adds 'No-Presubmit: true' for non master branch changes since those don't
     run the presubmit checks.
   * Adds extra trybots for the paths defined in PATH_TO_EXTRA_TRYBOTS.
   """
@@ -474,60 +481,51 @@
 
   issue = cl.issue
   if issue:
-    original_description = cl.GetDescription()
-    changeIdLine = None
-    if cl.IsGerrit():
-      # Remove Change-Id from description and add it back at the end.
-      regex = re.compile(r'^(Change-Id: (\w+))(\n*)\Z', re.M | re.I)
-      changeIdLine = re.search(regex, original_description).group(0)
-      original_description = re.sub(regex, '', original_description)
-      original_description = re.sub('\n+\Z', '\n', original_description)
+    original_description_lines, footers = cl.GetDescriptionFooters()
+    new_description_lines = list(original_description_lines)
 
-    new_description = original_description
-
-    # If the change includes only doc changes then add NOTRY=true in the
+    # If the change includes only doc changes then add No-Try: true in the
     # CL's description if it does not exist yet.
-    if all_docs_changes and not re.search(
-        r'^NOTRY=true$', new_description, re.M | re.I):
-      new_description += '\nNOTRY=true'
+    if all_docs_changes and not _FooterExists(footers, 'No-Try', 'true'):
+      new_description_lines.append('No-Try: true')
       results.append(
           output_api.PresubmitNotifyResult(
               'This change has only doc changes. Automatically added '
-              '\'NOTRY=true\' to the CL\'s description'))
+              '\'No-Try: true\' to the CL\'s description'))
 
     # If there is atleast one docs change then add preview link in the CL's
     # description if it does not already exist there.
-    if atleast_one_docs_change and not re.search(
-        r'^DOCS_PREVIEW=.*', new_description, re.M | re.I):
+    docs_preview_link = '%s%s' % (DOCS_PREVIEW_URL, issue)
+    docs_preview_line = 'Docs-Preview: %s' % docs_preview_link
+    if (atleast_one_docs_change and
+        not _FooterExists(footers, 'Docs-Preview', docs_preview_link)):
       # Automatically add a link to where the docs can be previewed.
-      new_description += '\nDOCS_PREVIEW= %s%s' % (DOCS_PREVIEW_URL, issue)
+      new_description_lines.append(docs_preview_line)
       results.append(
           output_api.PresubmitNotifyResult(
               'Automatically added a link to preview the docs changes to the '
               'CL\'s description'))
 
-    # If the target ref is not master then add NOTREECHECKS=true and NOTRY=true
-    # to the CL's description if it does not already exist there.
+    # If the target ref is not master then add 'No-Tree-Checks: true' and
+    # 'No-Try: true' to the CL's description if it does not already exist there.
     target_ref = cl.GetRemoteBranch()[1]
     if target_ref != 'refs/remotes/origin/master':
-      if not re.search(
-          r'^NOTREECHECKS=true$', new_description, re.M | re.I):
-        new_description += "\nNOTREECHECKS=true"
+      if not _FooterExists(footers, 'No-Tree-Checks', 'true'):
+        new_description_lines.append('No-Tree-Checks: true')
         results.append(
             output_api.PresubmitNotifyResult(
                 'Branch changes do not need to rely on the master branch\'s '
-                'tree status. Automatically added \'NOTREECHECKS=true\' to the '
-                'CL\'s description'))
-      if not re.search(
-          r'^NOTRY=true$', new_description, re.M | re.I):
-        new_description += "\nNOTRY=true"
+                'tree status. Automatically added \'No-Tree-Checks: true\' to '
+                'the CL\'s description'))
+      if not _FooterExists(footers, 'No-Try', 'true'):
+        new_description_lines.append('No-Try: true')
         results.append(
             output_api.PresubmitNotifyResult(
                 'Trybots do not yet work for non-master branches. '
-                'Automatically added \'NOTRY=true\' to the CL\'s description'))
-      if not re.search(
-          r'^NOPRESUBMIT=true$', new_description, re.M | re.I):
-        new_description += "\nNOPRESUBMIT=true"
+                'Automatically added \'No-Try: true\' to the CL\'s '
+                'description'))
+      if not _FooterExists(footers, 'No-Presubmit', 'true'):
+        new_description_lines.append('No-Presubmit: true')
         results.append(
             output_api.PresubmitNotifyResult(
                 'Branch changes do not run the presubmit checks.'))
@@ -546,20 +544,18 @@
           _MergeCQExtraTrybotsMaps(
               cq_master_to_trybots, _GetCQExtraTrybotsMap(extra_bots))
     if cq_master_to_trybots:
-      new_description = _AddCQExtraTrybotsToDesc(
-          cq_master_to_trybots, new_description)
+      _AddCQExtraTrybotsToDesc(cq_master_to_trybots, new_description_lines)
 
     # If the description has changed update it.
-    if new_description != original_description:
-      if changeIdLine:
-        # The Change-Id line must have two newlines before it.
-        new_description += '\n\n' + changeIdLine
-      cl.UpdateDescription(new_description)
+    if new_description_lines != original_description_lines:
+      # Add a new line separating the new contents from the old contents.
+      new_description_lines.insert(len(original_description_lines), '')
+      cl.UpdateDescriptionFooters(new_description_lines, footers)
 
     return results
 
 
-def _AddCQExtraTrybotsToDesc(cq_master_to_trybots, description):
+def _AddCQExtraTrybotsToDesc(cq_master_to_trybots, description_lines):
   """Adds the specified master and trybots to the CQ_INCLUDE_TRYBOTS keyword.
 
   If the keyword already exists in the description then it appends to it only
@@ -567,16 +563,21 @@
   If the keyword does not exist then it creates a new section in the
   description.
   """
-  match = re.search(r'^CQ_INCLUDE_TRYBOTS=(.*)$', description, re.M | re.I)
-  if match:
-    original_trybots_map = _GetCQExtraTrybotsMap(match.group(1))
+  found = None
+  foundIdx = -1
+  for idx, line in enumerate(description_lines):
+    if line.startswith('CQ_INCLUDE_TRYBOTS'):
+      found = line
+      foundIdx = idx
+
+  if found:
+    original_trybots_map = _GetCQExtraTrybotsMap(found)
     _MergeCQExtraTrybotsMaps(cq_master_to_trybots, original_trybots_map)
-    new_description = description.replace(
-        match.group(0), _GetCQExtraTrybotsStr(cq_master_to_trybots))
+    new_line = _GetCQExtraTrybotsStr(cq_master_to_trybots)
+    if new_line != found:
+      description_lines[foundIdx] = new_line
   else:
-    new_description = description + "\n%s" % (
-        _GetCQExtraTrybotsStr(cq_master_to_trybots))
-  return new_description
+    description_lines.append(_GetCQExtraTrybotsStr(cq_master_to_trybots))
 
 
 def _MergeCQExtraTrybotsMaps(dest_map, map_to_be_consumed):
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
index 8e881a6..2fd480b 100644
--- a/bench/BitmapBench.cpp
+++ b/bench/BitmapBench.cpp
@@ -44,13 +44,8 @@
             }
         }
     }
-    SkColorTable* ctable = new SkColorTable(storage, 216);
     dst->allocPixels(SkImageInfo::Make(src.width(), src.height(), kIndex_8_SkColorType, aType),
-                     nullptr, ctable);
-    ctable->unref();
-
-    SkAutoLockPixels alps(src);
-    SkAutoLockPixels alpd(*dst);
+                     SkColorTable::Make(storage, 216));
 
     for (int y = 0; y < src.height(); y++) {
         const SkPMColor* srcP = src.getAddr32(0, y);
diff --git a/bench/ClipMaskBench.cpp b/bench/ClipMaskBench.cpp
new file mode 100644
index 0000000..fc3cc16
--- /dev/null
+++ b/bench/ClipMaskBench.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "Benchmark.h"
+#include "sk_tool_utils.h"
+#include "SkCanvas.h"
+#include "SkColorSpace.h"
+#include "SkImage.h"
+#include "SkPictureRecorder.h"
+#include "SkString.h"
+#include "SkSurface.h"
+
+static void DrawMask(SkCanvas* canvas) {
+    sk_tool_utils::draw_checkerboard(canvas, SK_ColorTRANSPARENT, SK_ColorGREEN, 10);
+}
+
+class ClipMaskBench : public Benchmark {
+public:
+    using MaskMakerFunc = sk_sp<SkImage> (*)(int);
+
+    ClipMaskBench(const char name[], const MaskMakerFunc maskMaker)
+        : fName(SkStringPrintf("clipmask_%s", name))
+        , fClip(maskMaker(kSize)) {}
+
+protected:
+    const char* onGetName() override { return fName.c_str(); }
+
+    void onDraw(int loops, SkCanvas* canvas) override {
+        SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, fClip.get(), nullptr, 0);
+
+        for (int i = 0; i < loops; ++i) {
+            canvas->saveLayer(rec);
+            canvas->drawColor(SK_ColorBLUE);
+            canvas->restore();
+        }
+    }
+
+private:
+    static constexpr int kSize = 400;
+
+    SkString       fName;
+    sk_sp<SkImage> fClip;
+};
+
+DEF_BENCH(return new ClipMaskBench("a8", [](int size) -> sk_sp<SkImage> {
+    sk_sp<SkSurface> surface = SkSurface::MakeRaster(SkImageInfo::MakeA8(size, size));
+    DrawMask(surface->getCanvas());
+    return surface->makeImageSnapshot();
+});)
+
+DEF_BENCH(return new ClipMaskBench("8888", [](int size) -> sk_sp<SkImage> {
+    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(size, size);
+    DrawMask(surface->getCanvas());
+    return surface->makeImageSnapshot();
+});)
+
+DEF_BENCH(return new ClipMaskBench("picture", [](int size) -> sk_sp<SkImage> {
+    SkPictureRecorder recorder;
+    DrawMask(recorder.beginRecording(size, size));
+    return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(size, size),
+                                    nullptr, nullptr, SkImage::BitDepth::kU8,
+                                    SkColorSpace::MakeSRGB());
+});)
+
diff --git a/bench/ColorCanvasDrawBitmapBench.cpp b/bench/ColorCanvasDrawBitmapBench.cpp
index de13ba2..c150e79 100644
--- a/bench/ColorCanvasDrawBitmapBench.cpp
+++ b/bench/ColorCanvasDrawBitmapBench.cpp
@@ -6,6 +6,7 @@
 */
 
 #include "Benchmark.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColorSpaceXformCanvas.h"
 #include "SkString.h"
diff --git a/bench/CubicKLMBench.cpp b/bench/CubicKLMBench.cpp
index 3c8f740..1cdb068 100644
--- a/bench/CubicKLMBench.cpp
+++ b/bench/CubicKLMBench.cpp
@@ -22,12 +22,12 @@
         fPoints[3].set(x3, y3);
 
         fName = "cubic_klm_";
-        SkScalar d[3];
+        SkScalar d[4];
         switch (SkClassifyCubic(fPoints, d)) {
-            case kSerpentine_SkCubicType:
+            case SkCubicType::kSerpentine:
                 fName.append("serp");
                 break;
-            case kLoop_SkCubicType:
+            case SkCubicType::kLoop:
                 fName.append("loop");
                 break;
             default:
diff --git a/bench/DisplacementBench.cpp b/bench/DisplacementBench.cpp
index 9edf214..3dd7965 100644
--- a/bench/DisplacementBench.cpp
+++ b/bench/DisplacementBench.cpp
@@ -40,7 +40,7 @@
         paint.setColor(0xFF884422);
         paint.setTextSize(SkIntToScalar(96));
         const char* str = "g";
-        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(55), paint);
+        canvas.drawString(str, SkIntToScalar(15), SkIntToScalar(55), paint);
     }
 
     void makeCheckerboard() {
diff --git a/bench/DrawBitmapAABench.cpp b/bench/DrawBitmapAABench.cpp
index 011c30a..f0ccd89 100644
--- a/bench/DrawBitmapAABench.cpp
+++ b/bench/DrawBitmapAABench.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "Benchmark.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
diff --git a/bench/DrawLatticeBench.cpp b/bench/DrawLatticeBench.cpp
index 806815b..7a8cdf3 100644
--- a/bench/DrawLatticeBench.cpp
+++ b/bench/DrawLatticeBench.cpp
@@ -6,6 +6,7 @@
 */
 
 #include "Benchmark.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkRect.h"
 #include "SkString.h"
diff --git a/bench/FontScalerBench.cpp b/bench/FontScalerBench.cpp
index a5ab7b5..366c54c 100644
--- a/bench/FontScalerBench.cpp
+++ b/bench/FontScalerBench.cpp
@@ -37,7 +37,7 @@
 
             for (int ps = 9; ps <= 24; ps += 2) {
                 paint.setTextSize(SkIntToScalar(ps));
-                canvas->drawText(fText.c_str(), fText.size(),
+                canvas->drawString(fText,
                         0, SkIntToScalar(20), paint);
             }
         }
diff --git a/bench/GLBench.cpp b/bench/GLBench.cpp
index 0fcd56f..f043c95 100644
--- a/bench/GLBench.cpp
+++ b/bench/GLBench.cpp
@@ -67,7 +67,7 @@
 
 GrGLuint GLBench::CompileShader(const GrGLContext* context, const char* sksl, GrGLenum type) {
     const GrGLInterface* gl = context->interface();
-    SkString glsl;
+    SkSL::String glsl;
     SkSL::Program::Settings settings;
     settings.fCaps = context->caps()->shaderCaps();
     std::unique_ptr<SkSL::Program> program = context->compiler()->convertProgram(
diff --git a/bench/GLInstancedArraysBench.cpp b/bench/GLInstancedArraysBench.cpp
index 5268dd6..fc5e8fb 100644
--- a/bench/GLInstancedArraysBench.cpp
+++ b/bench/GLInstancedArraysBench.cpp
@@ -133,7 +133,7 @@
     // setup fragment shader
     GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
     SkString fshaderTxt(version);
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *shaderCaps, &fshaderTxt);
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps, &fshaderTxt);
     oColor.setTypeModifier(GrShaderVar::kIn_TypeModifier);
     oColor.appendDecl(shaderCaps, &fshaderTxt);
     fshaderTxt.append(";\n");
diff --git a/bench/GLVec4ScalarBench.cpp b/bench/GLVec4ScalarBench.cpp
index 26f9220..b5c7e22 100644
--- a/bench/GLVec4ScalarBench.cpp
+++ b/bench/GLVec4ScalarBench.cpp
@@ -131,7 +131,7 @@
     // next stage.
     GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
     SkString fshaderTxt(version);
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *shaderCaps, &fshaderTxt);
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps, &fshaderTxt);
     oPosition.setTypeModifier(GrShaderVar::kIn_TypeModifier);
     oPosition.appendDecl(shaderCaps, &fshaderTxt);
     fshaderTxt.append(";\n");
diff --git a/bench/GLVertexAttributesBench.cpp b/bench/GLVertexAttributesBench.cpp
index 4783d55..7efd984 100644
--- a/bench/GLVertexAttributesBench.cpp
+++ b/bench/GLVertexAttributesBench.cpp
@@ -117,7 +117,7 @@
     // setup fragment shader
     GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
     SkString fshaderTxt(version);
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *shaderCaps, &fshaderTxt);
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps, &fshaderTxt);
 
     const char* fsOutName;
     if (shaderCaps->mustDeclareFragmentShaderOutput()) {
diff --git a/bench/GameBench.cpp b/bench/GameBench.cpp
index 681b823..dfd0aa1 100644
--- a/bench/GameBench.cpp
+++ b/bench/GameBench.cpp
@@ -11,6 +11,7 @@
 #include "SkRandom.h"
 #include "SkShader.h"
 #include "SkString.h"
+#include "SkVertices.h"
 
 // This bench simulates the calls Skia sees from various HTML5 canvas
 // game bench marks
@@ -200,8 +201,9 @@
                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
                     };
-                    canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
-                                         4, verts, uvs, nullptr, indices, 6, p2);
+                    canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                                              4, verts, uvs, nullptr, 6, indices),
+                                         SkBlendMode::kModulate, p2);
                 } else {
                     canvas->drawBitmapRect(fAtlas, src, dst, &p,
                                            SkCanvas::kFast_SrcRectConstraint);
@@ -248,7 +250,6 @@
         static int kCheckSize = 16;
 
         fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
-        SkAutoLockPixels lock(fCheckerboard);
         for (int y = 0; y < kCheckerboardHeight; ++y) {
             int even = (y / kCheckSize) % 2;
 
@@ -281,7 +282,6 @@
         }
 
         fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
-        SkAutoLockPixels lock(fAtlas);
 
         for (int y = 0; y < kTotAtlasHeight; ++y) {
             int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp
index 1685c52..2d6f5d1 100644
--- a/bench/GradientBench.cpp
+++ b/bench/GradientBench.cpp
@@ -35,6 +35,7 @@
 };
 
 static const SkColor gShallowColors[] = { 0xFF555555, 0xFF444444 };
+static const SkScalar gPos[] = {0.25f, 0.75f};
 
 // We have several special-cases depending on the number (and spacing) of colors, so
 // try to exercise those here.
@@ -43,6 +44,7 @@
     { 50, gColors, nullptr, "_hicolor" }, // many color gradient
     { 3, gColors, nullptr, "_3color" },
     { 2, gShallowColors, nullptr, "_shallow" },
+    { 2, gColors, gPos, "_pos" },
 };
 
 /// Ignores scale
@@ -281,6 +283,8 @@
                                     kRect_GeomType, 1, true); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2], SkShader::kClamp_TileMode,
                                     kRect_GeomType, 1, true); )
+DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[4], SkShader::kClamp_TileMode,
+                                    kRect_GeomType, 1, true); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0], SkShader::kRepeat_TileMode,
                                     kRect_GeomType, 1, true); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1], SkShader::kRepeat_TileMode,
@@ -297,6 +301,7 @@
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0]); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1]); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2]); )
+DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[4]); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0], SkShader::kRepeat_TileMode); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1], SkShader::kRepeat_TileMode); )
 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2], SkShader::kRepeat_TileMode); )
diff --git a/bench/ImageFilterDAGBench.cpp b/bench/ImageFilterDAGBench.cpp
index 719e87b..350510b 100644
--- a/bench/ImageFilterDAGBench.cpp
+++ b/bench/ImageFilterDAGBench.cpp
@@ -10,6 +10,7 @@
 #include "SkBlurImageFilter.h"
 #include "SkDisplacementMapEffect.h"
 #include "SkCanvas.h"
+#include "SkImage.h"
 #include "SkMergeImageFilter.h"
 
 
diff --git a/bench/MagnifierBench.cpp b/bench/MagnifierBench.cpp
index 98deade..6403aa6 100644
--- a/bench/MagnifierBench.cpp
+++ b/bench/MagnifierBench.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "Benchmark.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkMagnifierImageFilter.h"
 #include "SkRandom.h"
diff --git a/bench/MergeBench.cpp b/bench/MergeBench.cpp
index 8b6004d..c37d198 100644
--- a/bench/MergeBench.cpp
+++ b/bench/MergeBench.cpp
@@ -24,7 +24,7 @@
     paint.setColor(0xFF884422);
     paint.setTextSize(SkIntToScalar(96));
     const char* str = "g";
-    surface->getCanvas()->drawText(str, strlen(str), 15, 55, paint);
+    surface->getCanvas()->drawString(str, 15, 55, paint);
     return surface->makeImageSnapshot();
 }
 
diff --git a/bench/MipMapBench.cpp b/bench/MipMapBench.cpp
index cc5c8ae..9b6fc27 100644
--- a/bench/MipMapBench.cpp
+++ b/bench/MipMapBench.cpp
@@ -71,3 +71,9 @@
 DEF_BENCH( return new MipMapBench(2047, 2047, SkDestinationSurfaceColorMode::kLegacy); )
 DEF_BENCH( return new MipMapBench(2047, 2047,
                                   SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware); )
+DEF_BENCH( return new MipMapBench(2048, 2047, SkDestinationSurfaceColorMode::kLegacy); )
+DEF_BENCH( return new MipMapBench(2048, 2047,
+                                  SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware); )
+DEF_BENCH( return new MipMapBench(2047, 2048, SkDestinationSurfaceColorMode::kLegacy); )
+DEF_BENCH( return new MipMapBench(2047, 2048,
+                                  SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware); )
diff --git a/bench/PatchBench.cpp b/bench/PatchBench.cpp
index d1d253b..d88dd14 100644
--- a/bench/PatchBench.cpp
+++ b/bench/PatchBench.cpp
@@ -322,3 +322,39 @@
                                         PatchBench::kTexCoords_VertexMode); )
 DEF_BENCH( return new LoopPatchBench(SkVector::Make(3.0f, 3.0f),
                                         PatchBench::kBoth_VertexMode); )
+
+//////////////////////////////////////////////
+#include "SkPatchUtils.h"
+
+class PatchUtilsBench : public Benchmark {
+    SkString    fName;
+    const bool  fLinearInterp;
+public:
+    PatchUtilsBench(bool linearInterp) : fLinearInterp(linearInterp) {
+        fName.printf("patchutils_%s", linearInterp ? "linear" : "legacy");
+    }
+
+    const char* onGetName() override { return fName.c_str(); }
+
+    bool isSuitableFor(Backend backend) override {
+        return backend == kNonRendering_Backend;
+    }
+
+    void onDraw(int loops, SkCanvas*) override {
+        const SkColor colors[] = { 0xFF000000, 0xFF00FF00, 0xFF0000FF, 0xFFFF0000 };
+        const SkPoint pts[] = {
+            { 0, 0 }, { 10, 0 }, { 20, 0 }, { 30, 0 },
+            { 30,10}, { 30,20 }, { 30,30 }, { 20,30 },
+            { 10,30}, { 0, 30 }, { 0, 20 }, { 0, 10 },
+        };
+        const SkPoint tex[] = {
+            { 0, 0 }, { 10, 0 }, { 10, 10 }, { 0, 10 },
+        };
+
+        for (int i = 0; i < 100*loops; ++i) {
+            SkPatchUtils::MakeVertices(pts, colors, tex, 20, 20, fLinearInterp);
+        }
+    }
+};
+DEF_BENCH( return new PatchUtilsBench(false); )
+DEF_BENCH( return new PatchUtilsBench(true); )
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index 3a1410a..6991db5 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -1115,8 +1115,8 @@
 
 
 const SkRect ConservativelyContainsBench::kBounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
-const SkSize ConservativelyContainsBench::kQueryMin = SkSize::Make(SkIntToScalar(1), SkIntToScalar(1));
-const SkSize ConservativelyContainsBench::kQueryMax = SkSize::Make(SkIntToScalar(40), SkIntToScalar(40));
+const SkSize ConservativelyContainsBench::kQueryMin = {SkIntToScalar(1), SkIntToScalar(1)};
+const SkSize ConservativelyContainsBench::kQueryMax = {SkIntToScalar(40), SkIntToScalar(40)};
 const SkRect ConservativelyContainsBench::kBaseRect = SkRect::MakeXYWH(SkIntToScalar(25), SkIntToScalar(25), SkIntToScalar(50), SkIntToScalar(50));
 const SkScalar ConservativelyContainsBench::kRRRadii[2] = {SkIntToScalar(5), SkIntToScalar(10)};
 
diff --git a/bench/PerlinNoiseBench.cpp b/bench/PerlinNoiseBench.cpp
index 6c98374..802c5be 100644
--- a/bench/PerlinNoiseBench.cpp
+++ b/bench/PerlinNoiseBench.cpp
@@ -7,6 +7,7 @@
 #include "Benchmark.h"
 #include "SkCanvas.h"
 #include "SkPerlinNoiseShader.h"
+#include "SkShader.h"
 
 class PerlinNoiseBench : public Benchmark {
     SkISize fSize;
@@ -22,8 +23,7 @@
     }
 
     void onDraw(int loops, SkCanvas* canvas) override {
-        this->test(loops, canvas, 0, 0, SkPerlinNoiseShader::kFractalNoise_Type,
-                   0.1f, 0.1f, 3, 0, false);
+        this->test(loops, canvas, 0, 0, 0.1f, 0.1f, 3, 0, false);
     }
 
 private:
@@ -38,17 +38,13 @@
         canvas->restore();
     }
 
-    void test(int loops, SkCanvas* canvas, int x, int y, SkPerlinNoiseShader::Type type,
+    void test(int loops, SkCanvas* canvas, int x, int y,
               float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed,
               bool stitchTiles) {
-        sk_sp<SkShader> shader = (type == SkPerlinNoiseShader::kFractalNoise_Type) ?
-            SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves,
-                                                  seed, stitchTiles ? &fSize : nullptr) :
-            SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves,
-                                                seed, stitchTiles ? &fSize : nullptr);
         SkPaint paint;
-        paint.setShader(shader);
-
+        paint.setShader(SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY,
+                                                              numOctaves, seed,
+                                                              stitchTiles ? &fSize : nullptr));
         for (int i = 0; i < loops; i++) {
             this->drawClippedRect(canvas, x, y, paint);
         }
diff --git a/bench/ReadPixBench.cpp b/bench/ReadPixBench.cpp
index ad9f747..2a8e9c4 100644
--- a/bench/ReadPixBench.cpp
+++ b/bench/ReadPixBench.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "Benchmark.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 
 
@@ -42,12 +43,13 @@
 
         SkBitmap bitmap;
 
-        bitmap.setInfo(SkImageInfo::MakeN32Premul(kWindowSize, kWindowSize));
+        bitmap.allocPixels(SkImageInfo::MakeN32Premul(kWindowSize, kWindowSize));
 
         for (int i = 0; i < loops; i++) {
             for (int x = 0; x < kNumStepsX; ++x) {
                 for (int y = 0; y < kNumStepsY; ++y) {
-                    canvas->readPixels(&bitmap, x * offX, y * offY);
+                    canvas->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(),
+                                       x * offX, y * offY);
                 }
             }
         }
diff --git a/bench/RepeatTileBench.cpp b/bench/RepeatTileBench.cpp
index 33e9178..5506031 100644
--- a/bench/RepeatTileBench.cpp
+++ b/bench/RepeatTileBench.cpp
@@ -62,14 +62,9 @@
             }
         }
     }
-    SkColorTable* ctable = new SkColorTable(storage, 216);
     dst->allocPixels(SkImageInfo::Make(src.width(), src.height(),
                                        kIndex_8_SkColorType, kOpaque_SkAlphaType),
-                     nullptr, ctable);
-    ctable->unref();
-
-    SkAutoLockPixels alps(src);
-    SkAutoLockPixels alpd(*dst);
+                     SkColorTable::Make(storage, 216));
 
     for (int y = 0; y < src.height(); y++) {
         const SkPMColor* srcP = src.getAddr32(0, y);
diff --git a/bench/ShaderMaskBench.cpp b/bench/ShaderMaskBench.cpp
index 90a5037..1cc2347 100644
--- a/bench/ShaderMaskBench.cpp
+++ b/bench/ShaderMaskBench.cpp
@@ -9,6 +9,7 @@
 #include "SkCanvas.h"
 #include "SkPaint.h"
 #include "SkRandom.h"
+#include "SkShader.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 
@@ -71,14 +72,14 @@
         for (int i = 0; i < loops; i++) {
             SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
             SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
-            canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
+            canvas->drawString(fText, x, y, paint);
         }
 
         paint.setTextSize(SkIntToScalar(48));
         for (int i = 0; i < loops / 4 ; i++) {
             SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
             SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
-            canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
+            canvas->drawString(fText, x, y, paint);
         }
     }
 
diff --git a/bench/ShadowBench.cpp b/bench/ShadowBench.cpp
new file mode 100755
index 0000000..82c4f95
--- /dev/null
+++ b/bench/ShadowBench.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 "Benchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkDrawShadowRec.h"
+#include "SkShadowUtils.h"
+
+class ShadowBench : public Benchmark {
+// Draws a set of shadowed rrects filling the canvas, in various modes:
+// * opaque or transparent
+// * use analytic fast path or geometric tessellation
+public:
+    ShadowBench(bool transparent, bool forceGeometric)
+        : fTransparent(transparent)
+        , fForceGeometric(forceGeometric) {
+        computeName("shadows");
+    }
+
+    bool isVisual() override { return true; }
+
+protected:
+    enum {
+        kWidth = 640,
+        kHeight = 480,
+        kRRSize = 50,
+        kRRRadius = 6,
+        kRRSpace = 8,
+        kRRStep = kRRSize + kRRSpace,
+        kElevation = 16,
+        kNumRRects = ((kWidth - kRRSpace) / kRRStep)*((kHeight - kRRSpace) / kRRStep)
+    };
+
+    void computeName(const char root[]) {
+        static const char kTransChars[2] = {
+            'o', 't'
+        };
+        static const char kGeomChars[2] = {
+            'a', 'g'
+        };
+
+        fBaseName.printf("%s_%c_%c", root, kTransChars[fTransparent], kGeomChars[fForceGeometric]);
+    }
+
+    void genRRects() {
+        int i = 0;
+        for (int x = kRRSpace; x < kWidth - kRRStep; x += kRRStep) {
+            for (int y = kRRSpace; y < kHeight - kRRStep; y += kRRStep) {
+                SkRect rect = SkRect::MakeXYWH(x, y, kRRSize, kRRSize);
+                fRRects[i].addRRect(SkRRect::MakeRectXY(rect, kRRRadius, kRRRadius));
+                ++i;
+            }
+        }
+        SkASSERT(i == kNumRRects);
+    }
+
+    const char* onGetName() override { return fBaseName.c_str(); }
+
+    void onDelayedSetup() override {
+        fRec.fZPlaneParams = SkPoint3::Make(0, 0, kElevation);
+        fRec.fLightPos = SkPoint3::Make(270, 0, 600);
+        fRec.fLightRadius = 800;
+        fRec.fAmbientAlpha = 0.1f;
+        fRec.fSpotAlpha = 0.25f;
+        fRec.fColor = SK_ColorBLACK;
+        fRec.fFlags = 0;
+        if (fTransparent) {
+            fRec.fFlags |= SkShadowFlags::kTransparentOccluder_ShadowFlag;
+        }
+        if (fForceGeometric) {
+            fRec.fFlags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
+        }
+
+        this->genRRects();
+    }
+
+    void onDraw(int loops, SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setColor(SK_ColorWHITE);
+        this->setupPaint(&paint);
+
+        for (int i = 0; i < loops; ++i) {
+            // use the private canvas call so we don't include the time to stuff data in the Rec
+            canvas->private_draw_shadow_rec(fRRects[i % kNumRRects], fRec);
+        }
+    }
+
+private:
+    SkString fBaseName;
+
+    SkPath  fRRects[kNumRRects];
+    SkDrawShadowRec fRec;
+    int    fTransparent;
+    int    fForceGeometric;
+
+    typedef Benchmark INHERITED;
+};
+
+DEF_BENCH(return new ShadowBench(false, false);)
+DEF_BENCH(return new ShadowBench(false, true);)
+DEF_BENCH(return new ShadowBench(true, false);)
+DEF_BENCH(return new ShadowBench(true, true);)
+
diff --git a/bench/SkLinearBitmapPipelineBench.cpp b/bench/SkLinearBitmapPipelineBench.cpp
index bc1b5df..bbacac7 100644
--- a/bench/SkLinearBitmapPipelineBench.cpp
+++ b/bench/SkLinearBitmapPipelineBench.cpp
@@ -15,7 +15,7 @@
 #include "SkImage.h"
 #include "SkLinearBitmapPipeline.h"
 #include "SkPM4f.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 
 struct CommonBitmapFPBenchmark : public Benchmark {
     CommonBitmapFPBenchmark(
@@ -150,8 +150,7 @@
         SkPixmap srcPixmap{fInfo, fBitmap.get(), static_cast<size_t>(4 * width)};
 
 
-        char storage[600];
-        SkArenaAlloc allocator{storage, sizeof(storage), 512};
+        SkSTArenaAlloc<600> allocator(512);
         SkLinearBitmapPipeline pipeline{
             fInvert, filterQuality, fXTile, fYTile, SK_ColorBLACK, srcPixmap, &allocator};
 
@@ -202,10 +201,10 @@
         SkAutoTMalloc<SkPMColor> buffer4b(width*height);
 
         SkArenaAlloc alloc{0};
-        const SkShader::ContextRec rec(fPaint, fM, nullptr,
-                                       SkShader::ContextRec::kPMColor_DstType,
-                                       nullptr);
-        SkShader::Context* ctx = fPaint.getShader()->makeContext(rec, &alloc);
+        const SkShaderBase::ContextRec rec(fPaint, fM, nullptr,
+                                           SkShaderBase::ContextRec::kPMColor_DstType,
+                                           nullptr);
+        SkShaderBase::Context* ctx = as_SB(fPaint.getShader())->makeContext(rec, &alloc);
 
         int count = 100;
 
diff --git a/bench/SkRasterPipelineBench.cpp b/bench/SkRasterPipelineBench.cpp
index 0ae48ee..39e7c91 100644
--- a/bench/SkRasterPipelineBench.cpp
+++ b/bench/SkRasterPipelineBench.cpp
@@ -39,7 +39,7 @@
         void*  src_ctx = src;
         void*  dst_ctx = dst;
 
-        SkRasterPipeline p;
+        SkRasterPipeline_<256> p;
         p.append(SkRasterPipeline::load_8888, &src_ctx);
         p.append_from_srgb(kUnpremul_SkAlphaType);
         p.append(SkRasterPipeline::scale_u8, &mask_ctx);
@@ -59,34 +59,99 @@
         }
 
         while (loops --> 0) {
-            p.run(0,N);
+            p.run(0,0,N);
         }
     }
 };
 DEF_BENCH( return (new SkRasterPipelineBench< true>); )
 DEF_BENCH( return (new SkRasterPipelineBench<false>); )
 
-class SkRasterPipelineLegacyBench : public Benchmark {
+class SkRasterPipelineCompileVsRunBench : public Benchmark {
 public:
+    explicit SkRasterPipelineCompileVsRunBench(bool compile) : fCompile(compile) {}
     bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
     const char* onGetName() override {
-        return "SkRasterPipeline_legacy";
+        return fCompile ? "SkRasterPipeline_compile"
+                        : "SkRasterPipeline_run";
     }
 
     void onDraw(int loops, SkCanvas*) override {
         void*  src_ctx = src;
         void*  dst_ctx = dst;
 
-        SkRasterPipeline p;
+        SkRasterPipeline_<256> p;
         p.append(SkRasterPipeline::load_8888, &dst_ctx);
         p.append(SkRasterPipeline::move_src_dst);
         p.append(SkRasterPipeline::load_8888, &src_ctx);
         p.append(SkRasterPipeline::srcover);
         p.append(SkRasterPipeline::store_8888, &dst_ctx);
 
+        if (fCompile) {
+            auto fn = p.compile();
+            while (loops --> 0) {
+                fn(0,0,N);
+            }
+        } else {
+            while (loops --> 0) {
+                p.run(0,0,N);
+            }
+        }
+    }
+private:
+    bool fCompile;
+};
+DEF_BENCH( return (new SkRasterPipelineCompileVsRunBench(true )); )
+DEF_BENCH( return (new SkRasterPipelineCompileVsRunBench(false)); )
+
+static SkColorSpaceTransferFn gamma(float g) {
+    SkColorSpaceTransferFn fn = {0,0,0,0,0,0,0};
+    fn.fG = g;
+    fn.fA = 1;
+    return fn;
+}
+
+class SkRasterPipeline_2dot2 : public Benchmark {
+public:
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+    const char* onGetName() override {
+        return "SkRasterPipeline_2dot2";
+    }
+
+    void onDraw(int loops, SkCanvas*) override {
+        SkColor4f c = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+        SkColorSpaceTransferFn from_2dot2 = gamma(  2.2f),
+                                 to_2dot2 = gamma(1/2.2f);
+        SkRasterPipeline_<256> p;
+        p.append(SkRasterPipeline::constant_color, &c);
+        p.append(SkRasterPipeline::parametric_r, &from_2dot2);
+        p.append(SkRasterPipeline::parametric_g, &from_2dot2);
+        p.append(SkRasterPipeline::parametric_b, &from_2dot2);
+        p.append(SkRasterPipeline::parametric_r, &  to_2dot2);
+        p.append(SkRasterPipeline::parametric_g, &  to_2dot2);
+        p.append(SkRasterPipeline::parametric_b, &  to_2dot2);
+
         while (loops --> 0) {
-            p.run(0,N);
+            p.run(0,0,N);
         }
     }
 };
-DEF_BENCH( return (new SkRasterPipelineLegacyBench); )
+DEF_BENCH( return (new SkRasterPipeline_2dot2); )
+
+class SkRasterPipelineToSRGB : public Benchmark {
+public:
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+    const char* onGetName() override {
+        return "SkRasterPipeline_to_srgb";
+    }
+
+    void onDraw(int loops, SkCanvas*) override {
+        SkRasterPipeline_<256> p;
+        p.append(SkRasterPipeline::to_srgb);
+
+        while (loops --> 0) {
+            p.run(0,0,N);
+        }
+    }
+};
+DEF_BENCH( return (new SkRasterPipelineToSRGB); )
diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp
index f4a7cdd..6e9df4a 100644
--- a/bench/TextBench.cpp
+++ b/bench/TextBench.cpp
@@ -140,7 +140,7 @@
             } else {
                 SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
                 SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
-                canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
+                canvas->drawString(fText, x, y, paint);
             }
         }
     }
diff --git a/bench/TileBench.cpp b/bench/TileBench.cpp
index 3093016..cd26546 100644
--- a/bench/TileBench.cpp
+++ b/bench/TileBench.cpp
@@ -18,7 +18,6 @@
     float deltaB = 255.0f / height;
     float blue = 255.0f;
 
-    SkAutoLockPixels lock(*bm);
     for (int y = 0; y < height; y++) {
         *bm->getAddr32(0, y) = SkColorSetRGB(0, 0, (U8CPU) blue);
         blue -= deltaB;
diff --git a/bench/VertBench.cpp b/bench/VertBench.cpp
index 0744f94..6e1613b 100644
--- a/bench/VertBench.cpp
+++ b/bench/VertBench.cpp
@@ -11,6 +11,7 @@
 #include "SkRandom.h"
 #include "SkShader.h"
 #include "SkString.h"
+#include "SkVertices.h"
 
 enum VertFlags {
     kColors_VertFlag,
@@ -76,14 +77,15 @@
     }
 
 protected:
-    virtual const char* onGetName() { return fName.c_str(); }
-    virtual void onDraw(int loops, SkCanvas* canvas) {
+    const char* onGetName() override { return fName.c_str(); }
+    void onDraw(int loops, SkCanvas* canvas) override {
         SkPaint paint;
         this->setupPaint(&paint);
 
+        auto verts = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, PTS,
+                                          fPts, nullptr, fColors, IDX, fIdx);
         for (int i = 0; i < loops; i++) {
-            canvas->drawVertices(SkCanvas::kTriangles_VertexMode, PTS,
-                                 fPts, nullptr, fColors, fIdx, IDX, paint);
+            canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
         }
     }
 private:
diff --git a/bench/WritePixelsBench.cpp b/bench/WritePixelsBench.cpp
index 820bb59..4e2dbeb 100644
--- a/bench/WritePixelsBench.cpp
+++ b/bench/WritePixelsBench.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "Benchmark.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkString.h"
 
@@ -52,7 +53,7 @@
 
         SkBitmap bmp;
         bmp.allocN32Pixels(size.width(), size.height());
-        canvas->readPixels(&bmp, 0, 0);
+        canvas->readPixels(bmp, 0, 0);
 
         SkImageInfo info = SkImageInfo::Make(bmp.width(), bmp.height(), fColorType, fAlphaType);
 
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index 2f271db..b6f8343 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -50,6 +50,8 @@
 
 #include <stdlib.h>
 
+extern bool gSkForceRasterPipelineBlitter;
+
 #ifndef SK_BUILD_FOR_WIN32
     #include <unistd.h>
 #endif
@@ -128,6 +130,8 @@
 DEFINE_string(benchType,  "",
         "Apply usual --match rules to bench type: micro, recording, piping, playback, skcodec, etc.");
 
+DEFINE_bool(forceRasterPipeline, false, "sets gSkForceRasterPipelineBlitter");
+
 #if SK_SUPPORT_GPU
 DEFINE_pathrenderer_flag;
 #endif
@@ -154,8 +158,8 @@
     if (!canvas) {
         return false;
     }
-    bmp->setInfo(canvas->imageInfo());
-    if (!canvas->readPixels(bmp, 0, 0)) {
+    bmp->allocPixels(canvas->imageInfo());
+    if (!canvas->readPixels(*bmp, 0, 0)) {
         SkDebugf("Can't read canvas pixels.\n");
         return false;
     }
@@ -1196,6 +1200,9 @@
     if (FLAGS_forceAnalyticAA) {
         gSkForceAnalyticAA = true;
     }
+    if (FLAGS_forceRasterPipeline) {
+        gSkForceRasterPipelineBlitter = true;
+    }
 
     int runs = 0;
     BenchmarkStream benchStream;
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 0b36e4b..0496210 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -329,8 +329,12 @@
                  FLAGS_uninterestingHashesFile[0]);
             return;
         }
+
+        // Copy to a string to make sure SkStrSplit has a terminating \0 to find.
+        SkString contents((const char*)data->data(), data->size());
+
         SkTArray<SkString> hashes;
-        SkStrSplit((const char*)data->data(), kNewline, &hashes);
+        SkStrSplit(contents.c_str(), kNewline, &hashes);
         for (const SkString& hash : hashes) {
             gUninterestingHashes.add(hash);
         }
@@ -599,8 +603,12 @@
     {
         std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
         if (frameInfos.size() > 1) {
-            push_codec_src(path, CodecSrc::kAnimated_Mode, CodecSrc::kGetFromCanvas_DstColorType,
-                           kPremul_SkAlphaType, 1.0f);
+            for (auto dstCT : { CodecSrc::kNonNative8888_Always_DstColorType,
+                    CodecSrc::kGetFromCanvas_DstColorType }) {
+                for (auto at : { kUnpremul_SkAlphaType, kPremul_SkAlphaType }) {
+                    push_codec_src(path, CodecSrc::kAnimated_Mode, dstCT, at, 1.0f);
+                }
+            }
         }
 
     }
@@ -1029,6 +1037,8 @@
     png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8,
                  PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+    png_set_filter(png,  PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE);
+    png_set_compression_level(png, 1);
     png_write_info(png, info);
     for (int j = 0; j < h; j++) {
         png_bytep row = (png_bytep)(rgba + w*j);
@@ -1106,9 +1116,6 @@
             gDefinitelyThreadSafeWork.add([task,name,bitmap,data]{
                 std::unique_ptr<SkStreamAsset> ownedData(data);
 
-                // Why doesn't the copy constructor do this when we have pre-locked pixels?
-                bitmap.lockPixels();
-
                 SkString md5;
                 if (!FLAGS_writePath.isEmpty() || !FLAGS_readPath.isEmpty()) {
                     SkMD5 hash;
@@ -1122,7 +1129,8 @@
                         // We might consider promoting 565 to RGBA too.
                         if (bitmap.colorType() == kBGRA_8888_SkColorType) {
                             SkBitmap swizzle;
-                            SkAssertResult(bitmap.copyTo(&swizzle, kRGBA_8888_SkColorType));
+                            SkAssertResult(sk_tool_utils::copy_to(&swizzle, kRGBA_8888_SkColorType,
+                                                                  bitmap));
                             hash.write(swizzle.getPixels(), swizzle.getSize());
                         } else {
                             hash.write(bitmap.getPixels(), bitmap.getSize());
@@ -1286,9 +1294,11 @@
 
 DEFINE_int32(status_sec, 15, "Print status this often (and if we crash).");
 
+static std::atomic<bool> gStopStatusThread{false};
+
 SkThread* start_status_thread() {
     auto thread = new SkThread([] (void*) {
-        for (;;) {
+        while (!gStopStatusThread.load()) {
             print_status();
         #if defined(SK_BUILD_FOR_WIN)
             Sleep(FLAGS_status_sec * 1000);
@@ -1422,6 +1432,13 @@
     SkPDFImageDumpStats();
 #endif  // SK_PDF_IMAGE_STATS
 
+    // An experiment to work around problems with libmobiledevice driving DM.
+    // Make sure the status thread has stopped completely before we exit.
+#if defined(SK_BUILD_FOR_IOS)
+    gStopStatusThread.store(true);
+    statusThread->join();
+#endif
+
     print_status();
     SkGraphics::PurgeAllCaches();
     info("Finished!\n");
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 41a9151..983a0c1 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -26,6 +26,7 @@
 #include "SkLiteDL.h"
 #include "SkLiteRecorder.h"
 #include "SkMallocPixelRef.h"
+#include "SkMultiPictureDocumentPriv.h"
 #include "SkMultiPictureDraw.h"
 #include "SkNullCanvas.h"
 #include "SkOSFile.h"
@@ -122,6 +123,14 @@
 }
 
 Error BRDSrc::draw(SkCanvas* canvas) const {
+    if (canvas->imageInfo().colorSpace() &&
+            kRGBA_F16_SkColorType != canvas->imageInfo().colorType()) {
+        // SkAndroidCodec uses legacy premultiplication and blending.  Therefore, we only
+        // run these tests on legacy canvases.
+        // We allow an exception for F16, since Android uses F16.
+        return Error::Nonfatal("Skip testing to color correct canvas.");
+    }
+
     SkColorType colorType = canvas->imageInfo().colorType();
     if (kRGB_565_SkColorType == colorType &&
             CodecSrc::kGetFromCanvas_DstColorType != fDstColorType) {
@@ -242,10 +251,10 @@
 SkISize BRDSrc::size() const {
     std::unique_ptr<SkBitmapRegionDecoder> brd(create_brd(fPath));
     if (brd) {
-        return SkISize::Make(SkTMax(1, brd->width() / (int) fSampleSize),
-                SkTMax(1, brd->height() / (int) fSampleSize));
+        return {SkTMax(1, brd->width() / (int)fSampleSize),
+                SkTMax(1, brd->height() / (int)fSampleSize)};
     }
-    return SkISize::Make(0, 0);
+    return {0, 0};
 }
 
 static SkString get_scaled_name(const Path& path, float scale) {
@@ -317,6 +326,16 @@
     }
 
     switch (bitmap.colorType()) {
+        case kRGBA_F16_SkColorType:
+            for (int y = 0; y < bitmap.height(); y++) {
+                void* row = bitmap.getAddr(0, y);
+                SkRasterPipeline_<256> p;
+                p.append(SkRasterPipeline::load_f16, &row);
+                p.append(SkRasterPipeline::premul);
+                p.append(SkRasterPipeline::store_f16, &row);
+                p.run(0,y, bitmap.width());
+            }
+            break;
         case kN32_SkColorType:
             for (int y = 0; y < bitmap.height(); y++) {
                 uint32_t* row = (uint32_t*) bitmap.getAddr(0, y);
@@ -355,7 +374,8 @@
             *decodeInfo = decodeInfo->makeColorType(kGray_8_SkColorType);
             break;
         case CodecSrc::kNonNative8888_Always_DstColorType:
-            if (kRGB_565_SkColorType == canvasColorType) {
+            if (kRGB_565_SkColorType == canvasColorType
+                    || kRGBA_F16_SkColorType == canvasColorType) {
                 return false;
             }
 #ifdef SK_PMCOLOR_IS_RGBA
@@ -371,11 +391,6 @@
             }
 
             if (kRGBA_F16_SkColorType == canvasColorType) {
-                if (kUnpremul_SkAlphaType == dstAlphaType) {
-                    // Testing kPremul is enough for adequate coverage of F16 decoding.
-                    return false;
-                }
-
                 sk_sp<SkColorSpace> linearSpace =
                         as_CSB(decodeInfo->colorSpace())->makeLinearGamma();
                 *decodeInfo = decodeInfo->makeColorSpace(std::move(linearSpace));
@@ -400,6 +415,19 @@
     canvas->drawBitmap(bitmap, left, top);
 }
 
+// For codec srcs, we want the "draw" step to be a memcpy.  Any interesting color space or
+// color format conversions should be performed by the codec.  Sometimes the output of the
+// decode will be in an interesting color space.  On our srgb and f16 backends, we need to
+// "pretend" that the color space is standard sRGB to avoid triggering color conversion
+// at draw time.
+static void set_bitmap_color_space(SkImageInfo* info) {
+    if (kRGBA_F16_SkColorType == info->colorType()) {
+        *info = info->makeColorSpace(SkColorSpace::MakeSRGBLinear());
+    } else {
+        *info = info->makeColorSpace(SkColorSpace::MakeSRGB());
+    }
+}
+
 Error CodecSrc::draw(SkCanvas* canvas) const {
     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
     if (!encoded) {
@@ -438,12 +466,15 @@
     int colorCount = 256;
 
     SkCodec::Options options;
+    options.fPremulBehavior = canvas->imageInfo().colorSpace() ?
+            SkTransferFunctionBehavior::kRespect : SkTransferFunctionBehavior::kIgnore;
     if (kCodecZeroInit_Mode == fMode) {
         memset(pixels.get(), 0, size.height() * rowBytes);
         options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
     }
 
     SkImageInfo bitmapInfo = decodeInfo;
+    set_bitmap_color_space(&bitmapInfo);
     if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
             kBGRA_8888_SkColorType == decodeInfo.colorType()) {
         bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
@@ -464,11 +495,11 @@
 
             // Used to cache a frame that future frames will depend on.
             SkAutoMalloc priorFramePixels;
-            size_t cachedFrame = SkCodec::kNone;
-            for (size_t i = 0; i < frameInfos.size(); i++) {
+            int cachedFrame = SkCodec::kNone;
+            for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
                 options.fFrameIndex = i;
                 // Check for a prior frame
-                const size_t reqFrame = frameInfos[i].fRequiredFrame;
+                const int reqFrame = frameInfos[i].fRequiredFrame;
                 if (reqFrame != SkCodec::kNone && reqFrame == cachedFrame
                         && priorFramePixels.get()) {
                     // Copy into pixels
@@ -488,6 +519,18 @@
                 switch (result) {
                     case SkCodec::kSuccess:
                     case SkCodec::kIncompleteInput: {
+                        // If the next frame depends on this one, store it in priorFrame.
+                        // It is possible that we may discard a frame that future frames depend on,
+                        // but the codec will simply redecode the discarded frame.
+                        // Do this before calling draw_to_canvas, which premultiplies in place. If
+                        // we're decoding to unpremul, we want to pass the unmodified frame to the
+                        // codec for decoding the next frame.
+                        if (static_cast<size_t>(i+1) < frameInfos.size()
+                                && frameInfos[i+1].fRequiredFrame == i) {
+                            memcpy(priorFramePixels.reset(safeSize), pixels.get(), safeSize);
+                            cachedFrame = i;
+                        }
+
                         SkAutoCanvasRestore acr(canvas, true);
                         const int xTranslate = (i % factor) * decodeInfo.width();
                         const int yTranslate = (i / factor) * decodeInfo.height();
@@ -510,13 +553,6 @@
                         return SkStringPrintf("Couldn't getPixels for frame %i in %s.",
                                               i, fPath.c_str());
                 }
-
-                // If a future frame depends on this one, store it in priorFrame.
-                // (Note that if i+1 does *not* depend on i, then no future frame can.)
-                if (i+1 < frameInfos.size() && frameInfos[i+1].fRequiredFrame == i) {
-                    memcpy(priorFramePixels.reset(safeSize), pixels.get(), safeSize);
-                    cachedFrame = i;
-                }
             }
             break;
         }
@@ -556,7 +592,7 @@
             bool useOldScanlineMethod = !useIncremental && !ico;
             if (useIncremental || ico) {
                 if (SkCodec::kSuccess == codec->startIncrementalDecode(decodeInfo, dst,
-                        rowBytes, nullptr, colorPtr, &colorCount)) {
+                        rowBytes, &options, colorPtr, &colorCount)) {
                     int rowsDecoded;
                     if (SkCodec::kIncompleteInput == codec->incrementalDecode(&rowsDecoded)) {
                         codec->fillIncompleteImage(decodeInfo, dst, rowBytes,
@@ -602,7 +638,7 @@
             void* dst = pixels.get();
 
             // Decode odd stripes
-            if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, nullptr, colorPtr,
+            if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options, colorPtr,
                                                                 &colorCount)) {
                 return "Could not start scanline decoder";
             }
@@ -657,13 +693,11 @@
             // align with the jpeg block sizes and it will sometimes not.  This allows us
             // to test interestingly different code paths in the implementation.
             const int tileSize = 36;
-
-            SkCodec::Options opts;
             SkIRect subset;
             for (int x = 0; x < width; x += tileSize) {
                 subset = SkIRect::MakeXYWH(x, 0, SkTMin(tileSize, width - x), height);
-                opts.fSubset = &subset;
-                if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &opts,
+                options.fSubset = &subset;
+                if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options,
                         colorPtr, &colorCount)) {
                     return "Could not start scanline decoder.";
                 }
@@ -691,8 +725,7 @@
             const int w = SkAlign2(W / divisor);
             const int h = SkAlign2(H / divisor);
             SkIRect subset;
-            SkCodec::Options opts;
-            opts.fSubset = &subset;
+            options.fSubset = &subset;
             SkBitmap subsetBm;
             // We will reuse pixel memory from bitmap.
             void* dst = pixels.get();
@@ -716,7 +749,7 @@
                     SkImageInfo subsetBitmapInfo = bitmapInfo.makeWH(scaledW, scaledH);
                     size_t subsetRowBytes = subsetBitmapInfo.minRowBytes();
                     const SkCodec::Result result = codec->getPixels(decodeInfo, dst, subsetRowBytes,
-                            &opts, colorPtr, &colorCount);
+                            &options, colorPtr, &colorCount);
                     switch (result) {
                         case SkCodec::kSuccess:
                         case SkCodec::kIncompleteInput:
@@ -750,7 +783,7 @@
     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
     std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(encoded));
     if (nullptr == codec) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
 
     auto imageSize = codec->getScaledDimensions(fScale);
@@ -797,6 +830,14 @@
 }
 
 Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
+    if (canvas->imageInfo().colorSpace() &&
+            kRGBA_F16_SkColorType != canvas->imageInfo().colorType()) {
+        // SkAndroidCodec uses legacy premultiplication and blending.  Therefore, we only
+        // run these tests on legacy canvases.
+        // We allow an exception for F16, since Android uses F16.
+        return Error::Nonfatal("Skip testing to color correct canvas.");
+    }
+
     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
     if (!encoded) {
         return SkStringPrintf("Couldn't read %s.", fPath.c_str());
@@ -830,6 +871,7 @@
 
     SkBitmap bitmap;
     SkImageInfo bitmapInfo = decodeInfo;
+    set_bitmap_color_space(&bitmapInfo);
     if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
             kBGRA_8888_SkColorType == decodeInfo.colorType()) {
         bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
@@ -856,7 +898,7 @@
     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(encoded));
     if (nullptr == codec) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
     return codec->getSampledDimensions(fSampleSize);
 }
@@ -946,13 +988,14 @@
     // Test various color and alpha types on CPU
     SkImageInfo decodeInfo = gen->getInfo().makeAlphaType(fDstAlphaType);
 
+    SkImageGenerator::Options options;
+    options.fBehavior = canvas->imageInfo().colorSpace() ?
+            SkTransferFunctionBehavior::kRespect : SkTransferFunctionBehavior::kIgnore;
+
     int bpp = SkColorTypeBytesPerPixel(decodeInfo.colorType());
     size_t rowBytes = decodeInfo.width() * bpp;
     SkAutoMalloc pixels(decodeInfo.height() * rowBytes);
-    SkPMColor colorPtr[256];
-    int colorCount = 256;
-
-    if (!gen->getPixels(decodeInfo, pixels.get(), rowBytes, colorPtr, &colorCount)) {
+    if (!gen->getPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
         SkString err =
                 SkStringPrintf("Image generator could not getPixels() for %s\n", fPath.c_str());
 
@@ -966,6 +1009,9 @@
         return err;
     }
 
+    SkPMColor colorPtr[256];
+    int colorCount = 256;
+    set_bitmap_color_space(&decodeInfo);
     draw_to_canvas(canvas, decodeInfo, pixels.get(), rowBytes, colorPtr, colorCount,
                    CodecSrc::kGetFromCanvas_DstColorType);
     return "";
@@ -975,7 +1021,7 @@
     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
     std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(encoded));
     if (nullptr == codec) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
     return codec->getInfo().dimensions();
 }
@@ -1044,6 +1090,7 @@
     }
 
     SkImageInfo bitmapInfo = decodeInfo;
+    set_bitmap_color_space(&bitmapInfo);
     if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
         kBGRA_8888_SkColorType == decodeInfo.colorType())
     {
@@ -1079,9 +1126,9 @@
     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
     std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(encoded));
     if (nullptr == codec) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
-    return SkISize::Make(codec->getInfo().width(), codec->getInfo().height());
+    return {codec->getInfo().width(), codec->getInfo().height()};
 }
 
 Name ColorCodecSrc::name() const {
@@ -1113,15 +1160,15 @@
 SkISize SKPSrc::size() const {
     std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(fPath.c_str());
     if (!stream) {
-        return SkISize::Make(0,0);
+        return {0, 0};
     }
     SkPictInfo info;
     if (!SkPicture::InternalOnly_StreamIsSKP(stream.get(), &info)) {
-        return SkISize::Make(0,0);
+        return {0, 0};
     }
     SkRect viewport = kSKPViewport;
     if (!viewport.intersect(info.fCullRect)) {
-        return SkISize::Make(0,0);
+        return {0, 0};
     }
     return viewport.roundOut().size();
 }
@@ -1131,10 +1178,10 @@
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 #if defined(SK_XML)
 // Used when the image doesn't have an intrinsic size.
-static const SkSize kDefaultSVGSize = SkSize::Make(1000, 1000);
+static const SkSize kDefaultSVGSize = {1000, 1000};
 
 // Used to force-scale tiny fixed-size images.
-static const SkSize kMinimumSVGSize = SkSize::Make(128, 128);
+static const SkSize kMinimumSVGSize = {128, 128};
 
 SVGSrc::SVGSrc(Path path)
     : fName(SkOSPath::Basename(path.c_str()))
@@ -1173,11 +1220,11 @@
 
 SkISize SVGSrc::size() const {
     if (!fDom) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
 
-    return SkSize::Make(fDom->containerSize().width()  * fScale,
-                        fDom->containerSize().height() * fScale).toRound();
+    return SkSize{fDom->containerSize().width() * fScale, fDom->containerSize().height() * fScale}
+            .toRound();
 }
 
 Name SVGSrc::name() const { return fName; }
@@ -1195,30 +1242,39 @@
 
 MSKPSrc::MSKPSrc(Path path) : fPath(path) {
     std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
-    (void)fReader.init(stream.get());
+    int count = SkMultiPictureDocumentReadPageCount(stream.get());
+    if (count > 0) {
+        fPages.reset(count);
+        (void)SkMultiPictureDocumentReadPageSizes(stream.get(), &fPages[0], fPages.count());
+    }
 }
 
-int MSKPSrc::pageCount() const { return fReader.pageCount(); }
+int MSKPSrc::pageCount() const { return fPages.count(); }
 
 SkISize MSKPSrc::size() const { return this->size(0); }
-SkISize MSKPSrc::size(int i) const { return fReader.pageSize(i).toCeil(); }
+SkISize MSKPSrc::size(int i) const {
+    return i >= 0 && i < fPages.count() ? fPages[i].fSize.toCeil() : SkISize{0, 0};
+}
 
 Error MSKPSrc::draw(SkCanvas* c) const { return this->draw(0, c); }
 Error MSKPSrc::draw(int i, SkCanvas* canvas) const {
-    std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
-    if (!stream) {
-        return SkStringPrintf("Unable to open file: %s", fPath.c_str());
-    }
-    if (fReader.pageCount() == 0) {
+    if (this->pageCount() == 0) {
         return SkStringPrintf("Unable to parse MultiPictureDocument file: %s", fPath.c_str());
     }
-    if (i >= fReader.pageCount()) {
+    if (i >= fPages.count() || i < 0) {
         return SkStringPrintf("MultiPictureDocument page number out of range: %d", i);
     }
-    sk_sp<SkPicture> page = fReader.readPage(stream.get(), i);
+    SkPicture* page = fPages[i].fPicture.get();
     if (!page) {
-        return SkStringPrintf("SkMultiPictureDocumentReader failed on page %d: %s",
-                              i, fPath.c_str());
+        std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
+        if (!stream) {
+            return SkStringPrintf("Unable to open file: %s", fPath.c_str());
+        }
+        if (!SkMultiPictureDocumentRead(stream.get(), &fPages[0], fPages.count())) {
+            return SkStringPrintf("SkMultiPictureDocument reader failed on page %d: %s", i,
+                                  fPath.c_str());
+        }
+        page = fPages[i].fPicture.get();
     }
     canvas->drawPicture(page);
     return "";
@@ -1251,16 +1307,10 @@
     , fColorSpace(std::move(colorSpace))
     , fThreaded(threaded) {}
 
-DEFINE_bool(imm, false, "Run gpu configs in immediate mode.");
 DEFINE_bool(drawOpClip, false, "Clip each GrDrawOp to its device bounds for testing.");
-DEFINE_int32(opLookback, -1, "Maximum GrOp lookback for combining, negative means default.");
-DEFINE_int32(opLookahead, -1, "Maximum GrOp lookahead for combining, negative means default.");
 
 Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const {
     GrContextOptions grOptions;
-    grOptions.fImmediateMode = FLAGS_imm;
-    grOptions.fMaxOpCombineLookback = FLAGS_opLookback;
-    grOptions.fMaxOpCombineLookahead = FLAGS_opLookahead;
 
     src.modifyGrContextOptions(&grOptions);
 
@@ -1296,7 +1346,7 @@
         canvas->getGrContext()->dumpGpuStats(log);
     }
     dst->allocPixels(info);
-    canvas->readPixels(dst, 0, 0);
+    canvas->readPixels(*dst, 0, 0);
     if (FLAGS_abandonGpuContext) {
         factory.abandonContexts();
     } else if (FLAGS_releaseAndAbandonGpuContext) {
@@ -1446,11 +1496,9 @@
     SkAlphaType alphaType = kPremul_SkAlphaType;
     (void)SkColorTypeValidateAlphaType(fColorType, alphaType, &alphaType);
 
-    SkMallocPixelRef::ZeroedPRFactory factory;
     dst->allocPixels(SkImageInfo::Make(size.width(), size.height(),
                                        fColorType, alphaType, fColorSpace),
-                     &factory,
-                     nullptr/*colortable*/);
+                     nullptr/*colortable*/, SkBitmap::kZeroPixels_AllocFlag);
     SkCanvas canvas(*dst);
     return src.draw(&canvas);
 }
@@ -1514,7 +1562,7 @@
     SkRect bounds = SkRect::MakeIWH(srcW, srcH);
     matrix->mapRect(&bounds);
     matrix->postTranslate(-bounds.x(), -bounds.y());
-    return SkISize::Make(SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height()));
+    return {SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())};
 }
 
 ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
@@ -1559,7 +1607,6 @@
     canvas.drawBitmap(*bitmap, 0, 0, &paint);
 
     *bitmap = uprighted;
-    bitmap->lockPixels();
     return "";
 }
 
@@ -1861,7 +1908,7 @@
         if (fColorSpin) {
             SkBitmap pixels;
             pixels.allocPixels(canvas->imageInfo());
-            canvas->readPixels(&pixels, 0, 0);
+            canvas->readPixels(pixels, 0, 0);
             for (int y = 0; y < pixels.height(); y++) {
                 for (int x = 0; x < pixels.width(); x++) {
                     uint32_t pixel = *pixels.getAddr32(x, y);
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index b6e1b59..d72d868 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -15,7 +15,7 @@
 #include "SkBitmapRegionDecoder.h"
 #include "SkCanvas.h"
 #include "SkData.h"
-#include "SkMultiPictureDocumentReader.h"
+#include "SkMultiPictureDocument.h"
 #include "SkPicture.h"
 #include "gm.h"
 
@@ -288,7 +288,7 @@
 
 private:
     Path fPath;
-    SkMultiPictureDocumentReader fReader;
+    mutable SkTArray<SkDocumentPage> fPages;
 };
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
diff --git a/example/SkiaSDLExample.cpp b/example/SkiaSDLExample.cpp
index abd198a..f3be850 100644
--- a/example/SkiaSDLExample.cpp
+++ b/example/SkiaSDLExample.cpp
@@ -6,6 +6,7 @@
  *
  */
 
+#include "GrBackendSurface.h"
 #include "GrContext.h"
 #include "SDL.h"
 #include "SkCanvas.h"
@@ -192,16 +193,12 @@
 
     // Wrap the frame buffer object attached to the screen in a Skia render target so Skia can
     // render to it
-    GrBackendRenderTargetDesc desc;
-    desc.fWidth = dm.w;
-    desc.fHeight = dm.h;
-    desc.fConfig = kSkia8888_GrPixelConfig;
-    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    desc.fSampleCnt = kMsaaSampleCount;
-    desc.fStencilBits = kStencilBits;
+    GrGLFrameBufferInfo fbInfo;
     GrGLint buffer;
     GR_GL_GetIntegerv(interface, GR_GL_FRAMEBUFFER_BINDING, &buffer);
-    desc.fRenderTargetHandle = buffer;
+    fbInfo.fFBOID = buffer;
+    GrBackendRenderTarget backendRT(dm.w, dm.h, kMsaaSampleCount, kStencilBits,
+                                    kSkia8888_GrPixelConfig, fbInfo);
 
     // setup SkSurface
     // To use distance field text, use commented out SkSurfaceProps instead
@@ -209,7 +206,10 @@
     //                      SkSurfaceProps::kLegacyFontHost_InitType);
     SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
 
-    sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext, desc, &props));
+    sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext,
+                                                                    backendRT,
+                                                                    kBottomLeft_GrSurfaceOrigin,
+                                                                    &props));
 
     SkCanvas* canvas = surface->getCanvas();
 
diff --git a/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp b/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp
deleted file mode 100644
index da4c42f..0000000
--- a/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.cpp
+++ /dev/null
@@ -1,1403 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkPerlinNoiseShader2.h"
-
-#include "SkArenaAlloc.h"
-#include "SkDither.h"
-#include "SkColorFilter.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkShader.h"
-#include "SkUnPreMultiply.h"
-#include "SkString.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrCoordTransform.h"
-#include "SkGr.h"
-#include "effects/GrConstColorProcessor.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#endif
-
-static const int kBlockSize = 256;
-static const int kBlockMask = kBlockSize - 1;
-static const int kPerlinNoise = 4096;
-static const int kRandMaximum = SK_MaxS32; // 2**31 - 1
-
-static uint8_t improved_noise_permutations[] = {
-    151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225, 140,  36, 103,
-     30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148, 247, 120, 234,  75,   0,  26,
-    197,  62,  94, 252, 219, 203, 117,  35,  11,  32,  57, 177,  33,  88, 237, 149,  56,  87, 174,
-     20, 125, 136, 171, 168,  68, 175,  74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,
-     83, 111, 229, 122,  60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,
-     54,  65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169, 200, 196,
-    135, 130, 116, 188, 159,  86, 164, 100, 109, 198, 173, 186,   3,  64,  52, 217, 226, 250, 124,
-    123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212, 207, 206,  59, 227,  47,  16,  58,  17,
-    182, 189,  28,  42, 223, 183, 170, 213, 119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101,
-    155, 167,  43, 172,   9, 129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185,
-    112, 104, 218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241,  81,
-     51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157, 184,  84, 204, 176,
-    115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93, 222, 114,  67,  29,  24,  72, 243,
-    141, 128, 195,  78,  66, 215,  61, 156, 180,
-    151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225, 140,  36, 103,
-     30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148, 247, 120, 234,  75,   0,  26,
-    197,  62,  94, 252, 219, 203, 117,  35,  11,  32,  57, 177,  33,  88, 237, 149,  56,  87, 174,
-     20, 125, 136, 171, 168,  68, 175,  74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,
-     83, 111, 229, 122,  60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,
-     54,  65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169, 200, 196,
-    135, 130, 116, 188, 159,  86, 164, 100, 109, 198, 173, 186,   3,  64,  52, 217, 226, 250, 124,
-    123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212, 207, 206,  59, 227,  47,  16,  58,  17,
-    182, 189,  28,  42, 223, 183, 170, 213, 119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101,
-    155, 167,  43, 172,   9, 129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185,
-    112, 104, 218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241,  81,
-     51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157, 184,  84, 204, 176,
-    115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93, 222, 114,  67,  29,  24,  72, 243,
-    141, 128, 195,  78,  66, 215,  61, 156, 180
-};
-
-namespace {
-
-// noiseValue is the color component's value (or color)
-// limitValue is the maximum perlin noise array index value allowed
-// newValue is the current noise dimension (either width or height)
-inline int checkNoise(int noiseValue, int limitValue, int newValue) {
-    // If the noise value would bring us out of bounds of the current noise array while we are
-    // stiching noise tiles together, wrap the noise around the current dimension of the noise to
-    // stay within the array bounds in a continuous fashion (so that tiling lines are not visible)
-    if (noiseValue >= limitValue) {
-        noiseValue -= newValue;
-    }
-    return noiseValue;
-}
-
-inline SkScalar smoothCurve(SkScalar t) {
-    return t * t * (3 - 2 * t);
-}
-
-} // end namespace
-
-struct SkPerlinNoiseShader2::StitchData {
-    StitchData()
-      : fWidth(0)
-      , fWrapX(0)
-      , fHeight(0)
-      , fWrapY(0)
-    {}
-
-    bool operator==(const StitchData& other) const {
-        return fWidth == other.fWidth &&
-               fWrapX == other.fWrapX &&
-               fHeight == other.fHeight &&
-               fWrapY == other.fWrapY;
-    }
-
-    int fWidth; // How much to subtract to wrap for stitching.
-    int fWrapX; // Minimum value to wrap.
-    int fHeight;
-    int fWrapY;
-};
-
-struct SkPerlinNoiseShader2::PaintingData {
-    PaintingData(const SkISize& tileSize, SkScalar seed,
-                 SkScalar baseFrequencyX, SkScalar baseFrequencyY,
-                 const SkMatrix& matrix)
-    {
-        SkVector vec[2] = {
-            { SkScalarInvert(baseFrequencyX),   SkScalarInvert(baseFrequencyY)  },
-            { SkIntToScalar(tileSize.fWidth),   SkIntToScalar(tileSize.fHeight) },
-        };
-        matrix.mapVectors(vec, 2);
-
-        fBaseFrequency.set(SkScalarInvert(vec[0].fX), SkScalarInvert(vec[0].fY));
-        fTileSize.set(SkScalarRoundToInt(vec[1].fX), SkScalarRoundToInt(vec[1].fY));
-        this->init(seed);
-        if (!fTileSize.isEmpty()) {
-            this->stitch();
-        }
-
-#if SK_SUPPORT_GPU
-        fPermutationsBitmap.setInfo(SkImageInfo::MakeA8(kBlockSize, 1));
-        fPermutationsBitmap.setPixels(fLatticeSelector);
-
-        fNoiseBitmap.setInfo(SkImageInfo::MakeN32Premul(kBlockSize, 4));
-        fNoiseBitmap.setPixels(fNoise[0][0]);
-
-        fImprovedPermutationsBitmap.setInfo(SkImageInfo::MakeA8(256, 1));
-        fImprovedPermutationsBitmap.setPixels(improved_noise_permutations);
-
-        fGradientBitmap.setInfo(SkImageInfo::MakeN32Premul(16, 1));
-        static uint8_t gradients[] = { 2, 2, 1, 0,
-                                       0, 2, 1, 0,
-                                       2, 0, 1, 0,
-                                       0, 0, 1, 0,
-                                       2, 1, 2, 0,
-                                       0, 1, 2, 0,
-                                       2, 1, 0, 0,
-                                       0, 1, 0, 0,
-                                       1, 2, 2, 0,
-                                       1, 0, 2, 0,
-                                       1, 2, 0, 0,
-                                       1, 0, 0, 0,
-                                       2, 2, 1, 0,
-                                       1, 0, 2, 0,
-                                       0, 2, 1, 0,
-                                       1, 0, 0, 0 };
-        fGradientBitmap.setPixels(gradients);
-#endif
-    }
-
-    int         fSeed;
-    uint8_t     fLatticeSelector[kBlockSize];
-    uint16_t    fNoise[4][kBlockSize][2];
-    SkPoint     fGradient[4][kBlockSize];
-    SkISize     fTileSize;
-    SkVector    fBaseFrequency;
-    StitchData  fStitchDataInit;
-
-private:
-
-#if SK_SUPPORT_GPU
-    SkBitmap   fPermutationsBitmap;
-    SkBitmap   fNoiseBitmap;
-    SkBitmap   fImprovedPermutationsBitmap;
-    SkBitmap   fGradientBitmap;
-#endif
-
-    inline int random()  {
-        static const int gRandAmplitude = 16807; // 7**5; primitive root of m
-        static const int gRandQ = 127773; // m / a
-        static const int gRandR = 2836; // m % a
-
-        int result = gRandAmplitude * (fSeed % gRandQ) - gRandR * (fSeed / gRandQ);
-        if (result <= 0)
-            result += kRandMaximum;
-        fSeed = result;
-        return result;
-    }
-
-    // Only called once. Could be part of the constructor.
-    void init(SkScalar seed)
-    {
-        static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize));
-
-        // According to the SVG spec, we must truncate (not round) the seed value.
-        fSeed = SkScalarTruncToInt(seed);
-        // The seed value clamp to the range [1, kRandMaximum - 1].
-        if (fSeed <= 0) {
-            fSeed = -(fSeed % (kRandMaximum - 1)) + 1;
-        }
-        if (fSeed > kRandMaximum - 1) {
-            fSeed = kRandMaximum - 1;
-        }
-        for (int channel = 0; channel < 4; ++channel) {
-            for (int i = 0; i < kBlockSize; ++i) {
-                fLatticeSelector[i] = i;
-                fNoise[channel][i][0] = (random() % (2 * kBlockSize));
-                fNoise[channel][i][1] = (random() % (2 * kBlockSize));
-            }
-        }
-        for (int i = kBlockSize - 1; i > 0; --i) {
-            int k = fLatticeSelector[i];
-            int j = random() % kBlockSize;
-            SkASSERT(j >= 0);
-            SkASSERT(j < kBlockSize);
-            fLatticeSelector[i] = fLatticeSelector[j];
-            fLatticeSelector[j] = k;
-        }
-
-        // Perform the permutations now
-        {
-            // Copy noise data
-            uint16_t noise[4][kBlockSize][2];
-            for (int i = 0; i < kBlockSize; ++i) {
-                for (int channel = 0; channel < 4; ++channel) {
-                    for (int j = 0; j < 2; ++j) {
-                        noise[channel][i][j] = fNoise[channel][i][j];
-                    }
-                }
-            }
-            // Do permutations on noise data
-            for (int i = 0; i < kBlockSize; ++i) {
-                for (int channel = 0; channel < 4; ++channel) {
-                    for (int j = 0; j < 2; ++j) {
-                        fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j];
-                    }
-                }
-            }
-        }
-
-        // Half of the largest possible value for 16 bit unsigned int
-        static const SkScalar gHalfMax16bits = 32767.5f;
-
-        // Compute gradients from permutated noise data
-        for (int channel = 0; channel < 4; ++channel) {
-            for (int i = 0; i < kBlockSize; ++i) {
-                fGradient[channel][i] = SkPoint::Make(
-                    (fNoise[channel][i][0] - kBlockSize) * gInvBlockSizef,
-                    (fNoise[channel][i][1] - kBlockSize) * gInvBlockSizef);
-                fGradient[channel][i].normalize();
-                // Put the normalized gradient back into the noise data
-                fNoise[channel][i][0] = SkScalarRoundToInt(
-                                               (fGradient[channel][i].fX + 1) * gHalfMax16bits);
-                fNoise[channel][i][1] = SkScalarRoundToInt(
-                                               (fGradient[channel][i].fY + 1) * gHalfMax16bits);
-            }
-        }
-    }
-
-    // Only called once. Could be part of the constructor.
-    void stitch() {
-        SkScalar tileWidth  = SkIntToScalar(fTileSize.width());
-        SkScalar tileHeight = SkIntToScalar(fTileSize.height());
-        SkASSERT(tileWidth > 0 && tileHeight > 0);
-        // When stitching tiled turbulence, the frequencies must be adjusted
-        // so that the tile borders will be continuous.
-        if (fBaseFrequency.fX) {
-            SkScalar lowFrequencx =
-                SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
-            SkScalar highFrequencx =
-                SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
-            // BaseFrequency should be non-negative according to the standard.
-            if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) {
-                fBaseFrequency.fX = lowFrequencx;
-            } else {
-                fBaseFrequency.fX = highFrequencx;
-            }
-        }
-        if (fBaseFrequency.fY) {
-            SkScalar lowFrequency =
-                SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
-            SkScalar highFrequency =
-                SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
-            if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) {
-                fBaseFrequency.fY = lowFrequency;
-            } else {
-                fBaseFrequency.fY = highFrequency;
-            }
-        }
-        // Set up TurbulenceInitial stitch values.
-        fStitchDataInit.fWidth  =
-            SkScalarRoundToInt(tileWidth * fBaseFrequency.fX);
-        fStitchDataInit.fWrapX  = kPerlinNoise + fStitchDataInit.fWidth;
-        fStitchDataInit.fHeight =
-            SkScalarRoundToInt(tileHeight * fBaseFrequency.fY);
-        fStitchDataInit.fWrapY  = kPerlinNoise + fStitchDataInit.fHeight;
-    }
-
-public:
-
-#if SK_SUPPORT_GPU
-    const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; }
-
-    const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; }
-
-    const SkBitmap& getImprovedPermutationsBitmap() const { return fImprovedPermutationsBitmap; }
-
-    const SkBitmap& getGradientBitmap() const { return fGradientBitmap; }
-#endif
-};
-
-sk_sp<SkShader> SkPerlinNoiseShader2::MakeFractalNoise(SkScalar baseFrequencyX,
-                                                       SkScalar baseFrequencyY,
-                                                       int numOctaves, SkScalar seed,
-                                                       const SkISize* tileSize) {
-    return sk_sp<SkShader>(new SkPerlinNoiseShader2(kFractalNoise_Type, baseFrequencyX,
-                                                    baseFrequencyY, numOctaves, seed, tileSize));
-}
-
-sk_sp<SkShader> SkPerlinNoiseShader2::MakeTurbulence(SkScalar baseFrequencyX,
-                                                     SkScalar baseFrequencyY,
-                                                     int numOctaves, SkScalar seed,
-                                                     const SkISize* tileSize) {
-    return sk_sp<SkShader>(new SkPerlinNoiseShader2(kTurbulence_Type, baseFrequencyX,
-                                                    baseFrequencyY, numOctaves, seed, tileSize));
-}
-
-sk_sp<SkShader> SkPerlinNoiseShader2::MakeImprovedNoise(SkScalar baseFrequencyX,
-                                                        SkScalar baseFrequencyY,
-                                                        int numOctaves, SkScalar z) {
-    return sk_sp<SkShader>(new SkPerlinNoiseShader2(kImprovedNoise_Type, baseFrequencyX,
-                                                    baseFrequencyY, numOctaves, z, NULL));
-}
-
-SkPerlinNoiseShader2::SkPerlinNoiseShader2(SkPerlinNoiseShader2::Type type,
-                                         SkScalar baseFrequencyX,
-                                         SkScalar baseFrequencyY,
-                                         int numOctaves,
-                                         SkScalar seed,
-                                         const SkISize* tileSize)
-  : fType(type)
-  , fBaseFrequencyX(baseFrequencyX)
-  , fBaseFrequencyY(baseFrequencyY)
-  , fNumOctaves(numOctaves > 255 ? 255 : numOctaves/*[0,255] octaves allowed*/)
-  , fSeed(seed)
-  , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize)
-  , fStitchTiles(!fTileSize.isEmpty())
-{
-    SkASSERT(numOctaves >= 0 && numOctaves < 256);
-}
-
-SkPerlinNoiseShader2::~SkPerlinNoiseShader2() {
-}
-
-sk_sp<SkFlattenable> SkPerlinNoiseShader2::CreateProc(SkReadBuffer& buffer) {
-    Type type = (Type)buffer.readInt();
-    SkScalar freqX = buffer.readScalar();
-    SkScalar freqY = buffer.readScalar();
-    int octaves = buffer.readInt();
-    SkScalar seed = buffer.readScalar();
-    SkISize tileSize;
-    tileSize.fWidth = buffer.readInt();
-    tileSize.fHeight = buffer.readInt();
-
-    switch (type) {
-        case kFractalNoise_Type:
-            return SkPerlinNoiseShader2::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize);
-        case kTurbulence_Type:
-            return SkPerlinNoiseShader2::MakeTubulence(freqX, freqY, octaves, seed, &tileSize);
-        case kImprovedNoise_Type:
-            return SkPerlinNoiseShader2::MakeImprovedNoise(freqX, freqY, octaves, seed);
-        default:
-            return nullptr;
-    }
-}
-
-void SkPerlinNoiseShader2::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeInt((int) fType);
-    buffer.writeScalar(fBaseFrequencyX);
-    buffer.writeScalar(fBaseFrequencyY);
-    buffer.writeInt(fNumOctaves);
-    buffer.writeScalar(fSeed);
-    buffer.writeInt(fTileSize.fWidth);
-    buffer.writeInt(fTileSize.fHeight);
-}
-
-SkScalar SkPerlinNoiseShader2::PerlinNoiseShaderContext::noise2D(
-        int channel, const StitchData& stitchData, const SkPoint& noiseVector) const {
-    struct Noise {
-        int noisePositionIntegerValue;
-        int nextNoisePositionIntegerValue;
-        SkScalar noisePositionFractionValue;
-        Noise(SkScalar component)
-        {
-            SkScalar position = component + kPerlinNoise;
-            noisePositionIntegerValue = SkScalarFloorToInt(position);
-            noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue);
-            nextNoisePositionIntegerValue = noisePositionIntegerValue + 1;
-        }
-    };
-    Noise noiseX(noiseVector.x());
-    Noise noiseY(noiseVector.y());
-    SkScalar u, v;
-    const SkPerlinNoiseShader2& perlinNoiseShader = static_cast<const SkPerlinNoiseShader2&>(fShader);
-    // If stitching, adjust lattice points accordingly.
-    if (perlinNoiseShader.fStitchTiles) {
-        noiseX.noisePositionIntegerValue =
-            checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
-        noiseY.noisePositionIntegerValue =
-            checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
-        noiseX.nextNoisePositionIntegerValue =
-            checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
-        noiseY.nextNoisePositionIntegerValue =
-            checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
-    }
-    noiseX.noisePositionIntegerValue &= kBlockMask;
-    noiseY.noisePositionIntegerValue &= kBlockMask;
-    noiseX.nextNoisePositionIntegerValue &= kBlockMask;
-    noiseY.nextNoisePositionIntegerValue &= kBlockMask;
-    int i =
-        fPaintingData->fLatticeSelector[noiseX.noisePositionIntegerValue];
-    int j =
-        fPaintingData->fLatticeSelector[noiseX.nextNoisePositionIntegerValue];
-    int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask;
-    int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask;
-    int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
-    int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
-    SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue);
-    SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue);
-    // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
-    SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue,
-                                          noiseY.noisePositionFractionValue); // Offset (0,0)
-    u = fPaintingData->fGradient[channel][b00].dot(fractionValue);
-    fractionValue.fX -= SK_Scalar1; // Offset (-1,0)
-    v = fPaintingData->fGradient[channel][b10].dot(fractionValue);
-    SkScalar a = SkScalarInterp(u, v, sx);
-    fractionValue.fY -= SK_Scalar1; // Offset (-1,-1)
-    v = fPaintingData->fGradient[channel][b11].dot(fractionValue);
-    fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1)
-    u = fPaintingData->fGradient[channel][b01].dot(fractionValue);
-    SkScalar b = SkScalarInterp(u, v, sx);
-    return SkScalarInterp(a, b, sy);
-}
-
-SkScalar SkPerlinNoiseShader2::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint(
-        int channel, StitchData& stitchData, const SkPoint& point) const {
-    const SkPerlinNoiseShader2& perlinNoiseShader = static_cast<const SkPerlinNoiseShader2&>(fShader);
-    if (perlinNoiseShader.fStitchTiles) {
-        // Set up TurbulenceInitial stitch values.
-        stitchData = fPaintingData->fStitchDataInit;
-    }
-    SkScalar turbulenceFunctionResult = 0;
-    SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData->fBaseFrequency.fX,
-                                      point.y() * fPaintingData->fBaseFrequency.fY));
-    SkScalar ratio = SK_Scalar1;
-    for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
-        SkScalar noise = noise2D(channel, stitchData, noiseVector);
-        SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ?
-                            noise : SkScalarAbs(noise);
-        turbulenceFunctionResult += numer / ratio;
-        noiseVector.fX *= 2;
-        noiseVector.fY *= 2;
-        ratio *= 2;
-        if (perlinNoiseShader.fStitchTiles) {
-            // Update stitch values
-            stitchData.fWidth  *= 2;
-            stitchData.fWrapX   = stitchData.fWidth + kPerlinNoise;
-            stitchData.fHeight *= 2;
-            stitchData.fWrapY   = stitchData.fHeight + kPerlinNoise;
-        }
-    }
-
-    // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
-    // by fractalNoise and (turbulenceFunctionResult) by turbulence.
-    if (perlinNoiseShader.fType == kFractalNoise_Type) {
-        turbulenceFunctionResult = SkScalarHalf(turbulenceFunctionResult + 1);
-    }
-
-    if (channel == 3) { // Scale alpha by paint value
-        turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255;
-    }
-
-    // Clamp result
-    return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Improved Perlin Noise based on Java implementation found at http://mrl.nyu.edu/~perlin/noise/
-static SkScalar fade(SkScalar t) {
-    return t * t * t * (t * (t * 6 - 15) + 10);
-}
-
-static SkScalar lerp(SkScalar t, SkScalar a, SkScalar b) {
-    return a + t * (b - a);
-}
-
-static SkScalar grad(int hash, SkScalar x, SkScalar y, SkScalar z) {
-    int h = hash & 15;
-    SkScalar u = h < 8 ? x : y;
-    SkScalar v = h < 4 ? y : h == 12 || h == 14 ? x : z;
-    return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
-}
-
-SkScalar SkPerlinNoiseShader2::PerlinNoiseShaderContext::calculateImprovedNoiseValueForPoint(
-        int channel, const SkPoint& point) const {
-    const SkPerlinNoiseShader2& perlinNoiseShader = static_cast<const SkPerlinNoiseShader2&>(fShader);
-    SkScalar x = point.fX * perlinNoiseShader.fBaseFrequencyX;
-    SkScalar y = point.fY * perlinNoiseShader.fBaseFrequencyY;
-    // z offset between different channels, chosen arbitrarily
-    static const SkScalar CHANNEL_DELTA = 1000.0f;
-    SkScalar z = channel * CHANNEL_DELTA + perlinNoiseShader.fSeed;
-    SkScalar result = 0;
-    SkScalar ratio = SK_Scalar1;
-    for (int i = 0; i < perlinNoiseShader.fNumOctaves; i++) {
-        int X = SkScalarFloorToInt(x) & 255;
-        int Y = SkScalarFloorToInt(y) & 255;
-        int Z = SkScalarFloorToInt(z) & 255;
-        SkScalar px = x - SkScalarFloorToScalar(x);
-        SkScalar py = y - SkScalarFloorToScalar(y);
-        SkScalar pz = z - SkScalarFloorToScalar(z);
-        SkScalar u = fade(px);
-        SkScalar v = fade(py);
-        SkScalar w = fade(pz);
-        uint8_t* permutations = improved_noise_permutations;
-        int A  = permutations[X] + Y;
-        int AA = permutations[A] + Z;
-        int AB = permutations[A + 1] + Z;
-        int B  = permutations[X + 1] + Y;
-        int BA = permutations[B] + Z;
-        int BB = permutations[B + 1] + Z;
-        result += lerp(w, lerp(v, lerp(u, grad(permutations[AA    ], px    , py    , pz    ),
-                                          grad(permutations[BA    ], px - 1, py    , pz    )),
-                                  lerp(u, grad(permutations[AB    ], px    , py - 1, pz    ),
-                                          grad(permutations[BB    ], px - 1, py - 1, pz    ))),
-                          lerp(v, lerp(u, grad(permutations[AA + 1], px    , py    , pz - 1),
-                                          grad(permutations[BA + 1], px - 1, py    , pz - 1)),
-                                  lerp(u, grad(permutations[AB + 1], px    , py - 1, pz - 1),
-                                          grad(permutations[BB + 1], px - 1, py - 1, pz - 1)))) /
-                   ratio;
-        x *= 2;
-        y *= 2;
-        ratio *= 2;
-    }
-    result = SkScalarClampMax((result + 1.0f) / 2.0f, 1.0f);
-    return result;
-}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkPMColor SkPerlinNoiseShader2::PerlinNoiseShaderContext::shade(
-        const SkPoint& point, StitchData& stitchData) const {
-    const SkPerlinNoiseShader2& perlinNoiseShader = static_cast<const SkPerlinNoiseShader2&>(fShader);
-    SkPoint newPoint;
-    fMatrix.mapPoints(&newPoint, &point, 1);
-    newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
-    newPoint.fY = SkScalarRoundToScalar(newPoint.fY);
-
-    U8CPU rgba[4];
-    for (int channel = 3; channel >= 0; --channel) {
-        SkScalar value;
-        if (perlinNoiseShader.fType == kImprovedNoise_Type) {
-            value = calculateImprovedNoiseValueForPoint(channel, newPoint);
-        }
-        else {
-            value = calculateTurbulenceValueForPoint(channel, stitchData, newPoint);
-        }
-        rgba[channel] = SkScalarFloorToInt(255 * value);
-    }
-    return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
-}
-
-SkShader::Context* SkPerlinNoiseShader2::onMakeContext(const ContextRec& rec,
-                                                       SkArenaAlloc* alloc) const {
-    return alloc->make<PerlinNoiseShaderContext>(*this, rec);
-}
-
-SkPerlinNoiseShader2::PerlinNoiseShaderContext::PerlinNoiseShaderContext(
-        const SkPerlinNoiseShader2& shader, const ContextRec& rec)
-    : INHERITED(shader, rec)
-{
-    SkMatrix newMatrix = *rec.fMatrix;
-    newMatrix.preConcat(shader.getLocalMatrix());
-    if (rec.fLocalMatrix) {
-        newMatrix.preConcat(*rec.fLocalMatrix);
-    }
-    // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
-    // (as opposed to 0 based, usually). The same adjustment is in the setData() function.
-    fMatrix.setTranslate(-newMatrix.getTranslateX() + SK_Scalar1, -newMatrix.getTranslateY() + SK_Scalar1);
-    fPaintingData = new PaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX,
-                                     shader.fBaseFrequencyY, newMatrix);
-}
-
-SkPerlinNoiseShader2::PerlinNoiseShaderContext::~PerlinNoiseShaderContext() { delete fPaintingData; }
-
-void SkPerlinNoiseShader2::PerlinNoiseShaderContext::shadeSpan(
-        int x, int y, SkPMColor result[], int count) {
-    SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
-    StitchData stitchData;
-    for (int i = 0; i < count; ++i) {
-        result[i] = shade(point, stitchData);
-        point.fX += SK_Scalar1;
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-class GrGLPerlinNoise2 : public GrGLSLFragmentProcessor {
-public:
-    void emitCode(EmitArgs&) override;
-
-    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-private:
-    GrGLSLProgramDataManager::UniformHandle fStitchDataUni;
-    GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
-
-    typedef GrGLSLFragmentProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrPerlinNoise2Effect : public GrFragmentProcessor {
-public:
-    static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
-                                           SkPerlinNoiseShader2::Type type,
-                                           int numOctaves, bool stitchTiles,
-                                           SkPerlinNoiseShader2::PaintingData* paintingData,
-                                           sk_sp<GrTextureProxy> permutationsProxy,
-                                           sk_sp<GrTextureProxy> noiseProxy,
-                                           const SkMatrix& matrix) {
-        return sk_sp<GrFragmentProcessor>(
-            new GrPerlinNoise2Effect(resourceProvider, type, numOctaves, stitchTiles, paintingData,
-                                     std::move(permutationsProxy), std::move(noiseProxy), matrix));
-    }
-
-    ~GrPerlinNoise2Effect() override { delete fPaintingData; }
-
-    const char* name() const override { return "PerlinNoise"; }
-
-    const SkPerlinNoiseShader2::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; }
-
-    SkPerlinNoiseShader2::Type type() const { return fType; }
-    bool stitchTiles() const { return fStitchTiles; }
-    const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
-    int numOctaves() const { return fNumOctaves; }
-    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
-        return new GrGLPerlinNoise2;
-    }
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override {
-        GrGLPerlinNoise2::GenKey(*this, caps, b);
-    }
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const GrPerlinNoise2Effect& s = sBase.cast<GrPerlinNoise2Effect>();
-        return fType == s.fType &&
-               fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency &&
-               fNumOctaves == s.fNumOctaves &&
-               fStitchTiles == s.fStitchTiles &&
-               fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
-    }
-
-    GrPerlinNoise2Effect(GrResourceProvider* resourceProvider,
-                         SkPerlinNoiseShader2::Type type, int numOctaves, bool stitchTiles,
-                         SkPerlinNoiseShader2::PaintingData* paintingData,
-                         sk_sp<GrTextureProxy> permutationsProxy,
-                         sk_sp<GrTextureProxy> noiseProxy,
-                         const SkMatrix& matrix)
-            : INHERITED(kNone_OptimizationFlags)
-            , fType(type)
-            , fNumOctaves(numOctaves)
-            , fStitchTiles(stitchTiles)
-            , fPermutationsSampler(resourceProvider, std::move(permutationsProxy))
-            , fNoiseSampler(resourceProvider, std::move(noiseProxy))
-            , fPaintingData(paintingData) {
-        this->initClassID<GrPerlinNoise2Effect>();
-        this->addTextureSampler(&fPermutationsSampler);
-        this->addTextureSampler(&fNoiseSampler);
-        fCoordTransform.reset(matrix);
-        this->addCoordTransform(&fCoordTransform);
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    SkPerlinNoiseShader2::Type          fType;
-    GrCoordTransform                    fCoordTransform;
-    int                                 fNumOctaves;
-    bool                                fStitchTiles;
-    TextureSampler                      fPermutationsSampler;
-    TextureSampler                      fNoiseSampler;
-    SkPerlinNoiseShader2::PaintingData* fPaintingData;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoise2Effect);
-
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrPerlinNoise2Effect::TestCreate(GrProcessorTestData* d) {
-    int      numOctaves = d->fRandom->nextRangeU(2, 10);
-    bool     stitchTiles = d->fRandom->nextBool();
-    SkScalar seed = SkIntToScalar(d->fRandom->nextU());
-    SkISize  tileSize = SkISize::Make(d->fRandom->nextRangeU(4, 4096),
-                                      d->fRandom->nextRangeU(4, 4096));
-    SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f,
-                                                          0.99f);
-    SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f,
-                                                          0.99f);
-
-    sk_sp<SkShader> shader(d->fRandom->nextBool() ?
-        SkPerlinNoiseShader2::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed,
-                                               stitchTiles ? &tileSize : nullptr) :
-        SkPerlinNoiseShader2::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed,
-                                             stitchTiles ? &tileSize : nullptr));
-
-    GrTest::TestAsFPArgs asFPArgs(d);
-    return shader->asFragmentProcessor(asFPArgs.args());
-}
-#endif
-
-void GrGLPerlinNoise2::emitCode(EmitArgs& args) {
-    const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>();
-
-    GrGLSLFragmentBuilder* fsBuilder = args.fFragBuilder;
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    SkString vCoords = fsBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-
-    fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                                   "baseFrequency");
-    const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
-
-    const char* stitchDataUni = nullptr;
-    if (pne.stitchTiles()) {
-        fStitchDataUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                    kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                                    "stitchData");
-        stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni);
-    }
-
-    // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8
-    const char* chanCoordR  = "0.125";
-    const char* chanCoordG  = "0.375";
-    const char* chanCoordB  = "0.625";
-    const char* chanCoordA  = "0.875";
-    const char* chanCoord   = "chanCoord";
-    const char* stitchData  = "stitchData";
-    const char* ratio       = "ratio";
-    const char* noiseVec    = "noiseVec";
-    const char* noiseSmooth = "noiseSmooth";
-    const char* floorVal    = "floorVal";
-    const char* fractVal    = "fractVal";
-    const char* uv          = "uv";
-    const char* ab          = "ab";
-    const char* latticeIdx  = "latticeIdx";
-    const char* bcoords     = "bcoords";
-    const char* lattice     = "lattice";
-    const char* inc8bit     = "0.00390625";  // 1.0 / 256.0
-    // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
-    // [-1,1] vector and perform a dot product between that vector and the provided vector.
-    const char* dotLattice  = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);";
-
-    // Add noise function
-    static const GrShaderVar gPerlinNoiseArgs[] =  {
-        GrShaderVar(chanCoord, kFloat_GrSLType),
-        GrShaderVar(noiseVec, kVec2f_GrSLType)
-    };
-
-    static const GrShaderVar gPerlinNoiseStitchArgs[] =  {
-        GrShaderVar(chanCoord, kFloat_GrSLType),
-        GrShaderVar(noiseVec, kVec2f_GrSLType),
-        GrShaderVar(stitchData, kVec2f_GrSLType)
-    };
-
-    SkString noiseCode;
-
-    noiseCode.appendf("\tvec4 %s;\n", floorVal);
-    noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec);
-    noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal);
-    noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec);
-
-    // smooth curve : t * t * (3 - 2 * t)
-    noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);",
-        noiseSmooth, fractVal, fractVal, fractVal);
-
-    // Adjust frequencies if we're stitching tiles
-    if (pne.stitchTiles()) {
-        noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }",
-            floorVal, stitchData, floorVal, stitchData);
-        noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }",
-            floorVal, stitchData, floorVal, stitchData);
-        noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }",
-            floorVal, stitchData, floorVal, stitchData);
-        noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }",
-            floorVal, stitchData, floorVal, stitchData);
-    }
-
-    // Get texture coordinates and normalize
-    noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n",
-        floorVal, floorVal);
-
-    // Get permutation for x
-    {
-        SkString xCoords("");
-        xCoords.appendf("vec2(%s.x, 0.5)", floorVal);
-
-        noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx);
-        fsBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(),
-                                       kVec2f_GrSLType);
-        noiseCode.append(".r;");
-    }
-
-    // Get permutation for x + 1
-    {
-        SkString xCoords("");
-        xCoords.appendf("vec2(%s.z, 0.5)", floorVal);
-
-        noiseCode.appendf("\n\t%s.y = ", latticeIdx);
-        fsBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(),
-                                       kVec2f_GrSLType);
-        noiseCode.append(".r;");
-    }
-
-#if defined(SK_BUILD_FOR_ANDROID)
-    // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3).
-    // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit
-    // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725
-    // (or 0.484368 here). The following rounding operation prevents these precision issues from
-    // affecting the result of the noise by making sure that we only have multiples of 1/255.
-    // (Note that 1/255 is about 0.003921569, which is the value used here).
-    noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);",
-                      latticeIdx, latticeIdx);
-#endif
-
-    // Get (x,y) coordinates with the permutated x
-    noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal);
-
-    noiseCode.appendf("\n\n\tvec2 %s;", uv);
-    // Compute u, at offset (0,0)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord);
-        noiseCode.appendf("\n\tvec4 %s = ", lattice);
-        fsBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-            kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal);
-    // Compute v, at offset (-1,0)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord);
-        noiseCode.append("\n\tlattice = ");
-        fsBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-            kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    // Compute 'a' as a linear interpolation of 'u' and 'v'
-    noiseCode.appendf("\n\tvec2 %s;", ab);
-    noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
-
-    noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal);
-    // Compute v, at offset (-1,-1)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord);
-        noiseCode.append("\n\tlattice = ");
-        fsBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-            kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    noiseCode.appendf("\n\t%s.x += 1.0;", fractVal);
-    // Compute u, at offset (0,-1)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord);
-        noiseCode.append("\n\tlattice = ");
-        fsBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-            kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    // Compute 'b' as a linear interpolation of 'u' and 'v'
-    noiseCode.appendf("\n\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
-    // Compute the noise as a linear interpolation of 'a' and 'b'
-    noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth);
-
-    SkString noiseFuncName;
-    if (pne.stitchTiles()) {
-        fsBuilder->emitFunction(kFloat_GrSLType,
-                                "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs),
-                                gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName);
-    } else {
-        fsBuilder->emitFunction(kFloat_GrSLType,
-                                "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs),
-                                gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName);
-    }
-
-    // There are rounding errors if the floor operation is not performed here
-    fsBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;",
-                           noiseVec, vCoords.c_str(), baseFrequencyUni);
-
-    // Clear the color accumulator
-    fsBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", args.fOutputColor);
-
-    if (pne.stitchTiles()) {
-        // Set up TurbulenceInitial stitch values.
-        fsBuilder->codeAppendf("\n\t\tvec2 %s = %s;", stitchData, stitchDataUni);
-    }
-
-    fsBuilder->codeAppendf("\n\t\tfloat %s = 1.0;", ratio);
-
-    // Loop over all octaves
-    fsBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves());
-
-    fsBuilder->codeAppendf("\n\t\t\t%s += ", args.fOutputColor);
-    if (pne.type() != SkPerlinNoiseShader2::kFractalNoise_Type) {
-        fsBuilder->codeAppend("abs(");
-    }
-    if (pne.stitchTiles()) {
-        fsBuilder->codeAppendf(
-            "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s),"
-                 "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))",
-            noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData,
-            noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData,
-            noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData,
-            noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData);
-    } else {
-        fsBuilder->codeAppendf(
-            "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s),"
-                 "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))",
-            noiseFuncName.c_str(), chanCoordR, noiseVec,
-            noiseFuncName.c_str(), chanCoordG, noiseVec,
-            noiseFuncName.c_str(), chanCoordB, noiseVec,
-            noiseFuncName.c_str(), chanCoordA, noiseVec);
-    }
-    if (pne.type() != SkPerlinNoiseShader2::kFractalNoise_Type) {
-        fsBuilder->codeAppendf(")"); // end of "abs("
-    }
-    fsBuilder->codeAppendf(" * %s;", ratio);
-
-    fsBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec);
-    fsBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio);
-
-    if (pne.stitchTiles()) {
-        fsBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData);
-    }
-    fsBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves
-
-    if (pne.type() == SkPerlinNoiseShader2::kFractalNoise_Type) {
-        // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
-        // by fractalNoise and (turbulenceFunctionResult) by turbulence.
-        fsBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);",
-                               args.fOutputColor,args.fOutputColor);
-    }
-
-    // Clamp values
-    fsBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor);
-
-    // Pre-multiply the result
-    fsBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
-                           args.fOutputColor, args.fOutputColor,
-                           args.fOutputColor, args.fOutputColor);
-}
-
-void GrGLPerlinNoise2::GenKey(const GrProcessor& processor, const GrShaderCaps&,
-                              GrProcessorKeyBuilder* b) {
-    const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>();
-
-    uint32_t key = turbulence.numOctaves();
-
-    key = key << 3; // Make room for next 3 bits
-
-    switch (turbulence.type()) {
-        case SkPerlinNoiseShader2::kFractalNoise_Type:
-            key |= 0x1;
-            break;
-        case SkPerlinNoiseShader2::kTurbulence_Type:
-            key |= 0x2;
-            break;
-        default:
-            // leave key at 0
-            break;
-    }
-
-    if (turbulence.stitchTiles()) {
-        key |= 0x4; // Flip the 3rd bit if tile stitching is on
-    }
-
-    b->add32(key);
-}
-
-void GrGLPerlinNoise2::onSetData(const GrGLSLProgramDataManager& pdman,
-                                const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-
-    const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>();
-
-    const SkVector& baseFrequency = turbulence.baseFrequency();
-    pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
-
-    if (turbulence.stitchTiles()) {
-        const SkPerlinNoiseShader2::StitchData& stitchData = turbulence.stitchData();
-        pdman.set2f(fStitchDataUni, SkIntToScalar(stitchData.fWidth),
-                                   SkIntToScalar(stitchData.fHeight));
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-class GrGLImprovedPerlinNoise : public GrGLSLFragmentProcessor {
-public:
-    void emitCode(EmitArgs&) override;
-
-    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-private:
-    GrGLSLProgramDataManager::UniformHandle fZUni;
-    GrGLSLProgramDataManager::UniformHandle fOctavesUni;
-    GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
-
-    typedef GrGLSLFragmentProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrImprovedPerlinNoiseEffect : public GrFragmentProcessor {
-public:
-    static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
-                                           int octaves, SkScalar z,
-                                           SkPerlinNoiseShader2::PaintingData* paintingData,
-                                           sk_sp<GrTextureProxy> permutationsProxy,
-                                           sk_sp<GrTextureProxy> gradientProxy,
-                                           const SkMatrix& matrix) {
-        return sk_sp<GrFragmentProcessor>(
-            new GrImprovedPerlinNoiseEffect(resourceProvider, octaves, z, paintingData,
-                                            std::move(permutationsProxy),
-                                            std::move(gradientProxy), matrix));
-    }
-
-    ~GrImprovedPerlinNoiseEffect() override { delete fPaintingData; }
-
-    const char* name() const override { return "ImprovedPerlinNoise"; }
-
-    const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
-    SkScalar z() const { return fZ; }
-    int octaves() const { return fOctaves; }
-    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
-        return new GrGLImprovedPerlinNoise;
-    }
-
-    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GrGLImprovedPerlinNoise::GenKey(*this, caps, b);
-    }
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const GrImprovedPerlinNoiseEffect& s = sBase.cast<GrImprovedPerlinNoiseEffect>();
-        return fZ == fZ &&
-               fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency;
-    }
-
-    GrImprovedPerlinNoiseEffect(GrResourceProvider* resourceProvider,
-                                int octaves, SkScalar z,
-                                SkPerlinNoiseShader2::PaintingData* paintingData,
-                                sk_sp<GrTextureProxy> permutationsProxy,
-                                sk_sp<GrTextureProxy> gradientProxy,
-                                const SkMatrix& matrix)
-            : INHERITED(kNone_OptimizationFlags)
-            , fOctaves(octaves)
-            , fZ(z)
-            , fPermutationsSampler(resourceProvider, std::move(permutationsProxy))
-            , fGradientSampler(resourceProvider, std::move(gradientProxy))
-            , fPaintingData(paintingData) {
-        this->initClassID<GrImprovedPerlinNoiseEffect>();
-        this->addTextureSampler(&fPermutationsSampler);
-        this->addTextureSampler(&fGradientSampler);
-        fCoordTransform.reset(matrix);
-        this->addCoordTransform(&fCoordTransform);
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    GrCoordTransform                    fCoordTransform;
-    int                                 fOctaves;
-    SkScalar                            fZ;
-    TextureSampler                      fPermutationsSampler;
-    TextureSampler                      fGradientSampler;
-    SkPerlinNoiseShader2::PaintingData* fPaintingData;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrImprovedPerlinNoiseEffect);
-
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrImprovedPerlinNoiseEffect::TestCreate(GrProcessorTestData* d) {
-    SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f,
-                                                          0.99f);
-    SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f,
-                                                          0.99f);
-    int numOctaves = d->fRandom->nextRangeU(2, 10);
-    SkScalar z = SkIntToScalar(d->fRandom->nextU());
-
-    sk_sp<SkShader> shader(SkPerlinNoiseShader2::MakeImprovedNoise(baseFrequencyX,
-                                                                   baseFrequencyY,
-                                                                   numOctaves,
-                                                                   z));
-
-    GrTest::TestAsFPArgs asFPArgs(d);
-    return shader->asFragmentProcessor(asFPArgs.args());
-}
-#endif
-
-void GrGLImprovedPerlinNoise::emitCode(EmitArgs& args) {
-    GrGLSLFragmentBuilder* fsBuilder = args.fFragBuilder;
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    SkString vCoords = fsBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-
-    fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                                   "baseFrequency");
-    const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
-
-    fOctavesUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                             kFloat_GrSLType, kDefault_GrSLPrecision,
-                                             "octaves");
-    const char* octavesUni = uniformHandler->getUniformCStr(fOctavesUni);
-
-    fZUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                       kFloat_GrSLType, kDefault_GrSLPrecision,
-                                       "z");
-    const char* zUni = uniformHandler->getUniformCStr(fZUni);
-
-    // fade function
-    static const GrShaderVar fadeArgs[] =  {
-        GrShaderVar("t", kVec3f_GrSLType)
-    };
-    SkString fadeFuncName;
-    fsBuilder->emitFunction(kVec3f_GrSLType, "fade", SK_ARRAY_COUNT(fadeArgs),
-                            fadeArgs,
-                            "return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);",
-                            &fadeFuncName);
-
-    // perm function
-    static const GrShaderVar permArgs[] =  {
-        GrShaderVar("x", kFloat_GrSLType)
-    };
-    SkString permFuncName;
-    SkString permCode("return ");
-    // FIXME even though I'm creating these textures with kRepeat_TileMode, they're clamped. Not
-    // sure why. Using fract() (here and the next texture lookup) as a workaround.
-    fsBuilder->appendTextureLookup(&permCode, args.fTexSamplers[0], "vec2(fract(x / 256.0), 0.0)",
-                                   kVec2f_GrSLType);
-    permCode.append(".r * 255.0;");
-    fsBuilder->emitFunction(kFloat_GrSLType, "perm", SK_ARRAY_COUNT(permArgs), permArgs,
-                            permCode.c_str(), &permFuncName);
-
-    // grad function
-    static const GrShaderVar gradArgs[] =  {
-        GrShaderVar("x", kFloat_GrSLType),
-        GrShaderVar("p", kVec3f_GrSLType)
-    };
-    SkString gradFuncName;
-    SkString gradCode("return dot(");
-    fsBuilder->appendTextureLookup(&gradCode, args.fTexSamplers[1], "vec2(fract(x / 16.0), 0.0)",
-                                   kVec2f_GrSLType);
-    gradCode.append(".rgb * 255.0 - vec3(1.0), p);");
-    fsBuilder->emitFunction(kFloat_GrSLType, "grad", SK_ARRAY_COUNT(gradArgs), gradArgs,
-                            gradCode.c_str(), &gradFuncName);
-
-    // lerp function
-    static const GrShaderVar lerpArgs[] =  {
-        GrShaderVar("a", kFloat_GrSLType),
-        GrShaderVar("b", kFloat_GrSLType),
-        GrShaderVar("w", kFloat_GrSLType)
-    };
-    SkString lerpFuncName;
-    fsBuilder->emitFunction(kFloat_GrSLType, "lerp", SK_ARRAY_COUNT(lerpArgs), lerpArgs,
-                            "return a + w * (b - a);", &lerpFuncName);
-
-    // noise function
-    static const GrShaderVar noiseArgs[] =  {
-        GrShaderVar("p", kVec3f_GrSLType),
-    };
-    SkString noiseFuncName;
-    SkString noiseCode;
-    noiseCode.append("vec3 P = mod(floor(p), 256.0);");
-    noiseCode.append("p -= floor(p);");
-    noiseCode.appendf("vec3 f = %s(p);", fadeFuncName.c_str());
-    noiseCode.appendf("float A = %s(P.x) + P.y;", permFuncName.c_str());
-    noiseCode.appendf("float AA = %s(A) + P.z;", permFuncName.c_str());
-    noiseCode.appendf("float AB = %s(A + 1.0) + P.z;", permFuncName.c_str());
-    noiseCode.appendf("float B =  %s(P.x + 1.0) + P.y;", permFuncName.c_str());
-    noiseCode.appendf("float BA = %s(B) + P.z;", permFuncName.c_str());
-    noiseCode.appendf("float BB = %s(B + 1.0) + P.z;", permFuncName.c_str());
-    noiseCode.appendf("float result = %s(", lerpFuncName.c_str());
-    noiseCode.appendf("%s(%s(%s(%s(AA), p),", lerpFuncName.c_str(), lerpFuncName.c_str(),
-                      gradFuncName.c_str(), permFuncName.c_str());
-    noiseCode.appendf("%s(%s(BA), p + vec3(-1.0, 0.0, 0.0)), f.x),", gradFuncName.c_str(),
-                      permFuncName.c_str());
-    noiseCode.appendf("%s(%s(%s(AB), p + vec3(0.0, -1.0, 0.0)),", lerpFuncName.c_str(),
-                      gradFuncName.c_str(), permFuncName.c_str());
-    noiseCode.appendf("%s(%s(BB), p + vec3(-1.0, -1.0, 0.0)), f.x), f.y),",
-                      gradFuncName.c_str(), permFuncName.c_str());
-    noiseCode.appendf("%s(%s(%s(%s(AA + 1.0), p + vec3(0.0, 0.0, -1.0)),",
-                      lerpFuncName.c_str(), lerpFuncName.c_str(), gradFuncName.c_str(),
-                      permFuncName.c_str());
-    noiseCode.appendf("%s(%s(BA + 1.0), p + vec3(-1.0, 0.0, -1.0)), f.x),",
-                      gradFuncName.c_str(), permFuncName.c_str());
-    noiseCode.appendf("%s(%s(%s(AB + 1.0), p + vec3(0.0, -1.0, -1.0)),",
-                      lerpFuncName.c_str(), gradFuncName.c_str(), permFuncName.c_str());
-    noiseCode.appendf("%s(%s(BB + 1.0), p + vec3(-1.0, -1.0, -1.0)), f.x), f.y), f.z);",
-                      gradFuncName.c_str(), permFuncName.c_str());
-    noiseCode.append("return result;");
-    fsBuilder->emitFunction(kFloat_GrSLType, "noise", SK_ARRAY_COUNT(noiseArgs), noiseArgs,
-                            noiseCode.c_str(), &noiseFuncName);
-
-    // noiseOctaves function
-    static const GrShaderVar noiseOctavesArgs[] =  {
-        GrShaderVar("p", kVec3f_GrSLType),
-        GrShaderVar("octaves", kFloat_GrSLType),
-    };
-    SkString noiseOctavesFuncName;
-    SkString noiseOctavesCode;
-    noiseOctavesCode.append("float result = 0.0;");
-    noiseOctavesCode.append("float ratio = 1.0;");
-    noiseOctavesCode.append("for (float i = 0.0; i < octaves; i++) {");
-    noiseOctavesCode.appendf("result += %s(p) / ratio;", noiseFuncName.c_str());
-    noiseOctavesCode.append("p *= 2.0;");
-    noiseOctavesCode.append("ratio *= 2.0;");
-    noiseOctavesCode.append("}");
-    noiseOctavesCode.append("return (result + 1.0) / 2.0;");
-    fsBuilder->emitFunction(kFloat_GrSLType, "noiseOctaves", SK_ARRAY_COUNT(noiseOctavesArgs),
-                            noiseOctavesArgs, noiseOctavesCode.c_str(), &noiseOctavesFuncName);
-
-    fsBuilder->codeAppendf("vec2 coords = %s * %s;", vCoords.c_str(), baseFrequencyUni);
-    fsBuilder->codeAppendf("float r = %s(vec3(coords, %s), %s);", noiseOctavesFuncName.c_str(),
-                           zUni, octavesUni);
-    fsBuilder->codeAppendf("float g = %s(vec3(coords, %s + 0000.0), %s);",
-                           noiseOctavesFuncName.c_str(), zUni, octavesUni);
-    fsBuilder->codeAppendf("float b = %s(vec3(coords, %s + 0000.0), %s);",
-                           noiseOctavesFuncName.c_str(), zUni, octavesUni);
-    fsBuilder->codeAppendf("float a = %s(vec3(coords, %s + 0000.0), %s);",
-                           noiseOctavesFuncName.c_str(), zUni, octavesUni);
-    fsBuilder->codeAppendf("%s = vec4(r, g, b, a);", args.fOutputColor);
-
-    // Clamp values
-    fsBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor);
-
-    // Pre-multiply the result
-    fsBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
-                           args.fOutputColor, args.fOutputColor,
-                           args.fOutputColor, args.fOutputColor);
-}
-
-void GrGLImprovedPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&,
-                                     GrProcessorKeyBuilder* b) {
-}
-
-void GrGLImprovedPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman,
-                                const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-
-    const GrImprovedPerlinNoiseEffect& noise = processor.cast<GrImprovedPerlinNoiseEffect>();
-
-    const SkVector& baseFrequency = noise.baseFrequency();
-    pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
-
-    pdman.set1f(fOctavesUni, SkIntToScalar(noise.octaves()));
-
-    pdman.set1f(fZUni, noise.z());
-}
-
-/////////////////////////////////////////////////////////////////////
-sk_sp<GrFragmentProcessor> SkPerlinNoiseShader2::asFragmentProcessor(const AsFPArgs& args) const {
-    SkASSERT(args.fContext);
-
-    SkMatrix localMatrix = this->getLocalMatrix();
-    if (args.fLocalMatrix) {
-        localMatrix.preConcat(*args.fLocalMatrix);
-    }
-
-    SkMatrix matrix = *args.fViewMatrix;
-    matrix.preConcat(localMatrix);
-
-    // Either we don't stitch tiles, either we have a valid tile size
-    SkASSERT(!fStitchTiles || !fTileSize.isEmpty());
-
-    SkPerlinNoiseShader2::PaintingData* paintingData =
-            new PaintingData(fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix);
-
-    SkMatrix m = *args.fViewMatrix;
-    m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1);
-    m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1);
-
-    if (fType == kImprovedNoise_Type) {
-        GrSamplerParams textureParams(SkShader::TileMode::kRepeat_TileMode,
-                                      GrSamplerParams::FilterMode::kNone_FilterMode);
-        sk_sp<GrTextureProxy> permutationsTexture(
-            GrRefCachedBitmapTextureProxy(args.fContext,
-                                          paintingData->getImprovedPermutationsBitmap(),
-                                          textureParams, nullptr));
-        sk_sp<GrTextureProxy> gradientTexture(
-            GrRefCachedBitmapTextureProxy(args.fContext,
-                                          paintingData->getGradientBitmap(),
-                                          textureParams, nullptr));
-        return GrImprovedPerlinNoiseEffect::Make(args.fContext->resourceProvider(),
-                                                 fNumOctaves, fSeed, paintingData,
-                                                 std::move(permutationsTexture),
-                                                 std::move(gradientTexture), m);
-    }
-
-    if (0 == fNumOctaves) {
-        if (kFractalNoise_Type == fType) {
-            // Extract the incoming alpha and emit rgba = (a/4, a/4, a/4, a/2)
-            // TODO: Either treat the output of this shader as sRGB or allow client to specify a
-            // color space of the noise. Either way, this case (and the GLSL) need to convert to
-            // the destination.
-            sk_sp<GrFragmentProcessor> inner(
-                GrConstColorProcessor::Make(GrColor4f::FromGrColor(0x80404040),
-                                            GrConstColorProcessor::kModulateRGBA_InputMode));
-            return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-        }
-        // Emit zero.
-        return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
-                                           GrConstColorProcessor::kIgnore_InputMode);
-    }
-
-    sk_sp<GrTextureProxy> permutationsProxy = GrMakeCachedBitmapProxy(
-                                                         args.fContext->resourceProvider(),
-                                                         paintingData->getPermutationsBitmap());
-    sk_sp<GrTextureProxy> noiseProxy = GrMakeCachedBitmapProxy(args.fContext->resourceProvider(),
-                                                               paintingData->getNoiseBitmap());
-
-    if (permutationsProxy && noiseProxy) {
-        sk_sp<GrFragmentProcessor> inner(
-            GrPerlinNoise2Effect::Make(args.fContext->resourceProvider(),
-                                       fType,
-                                       fNumOctaves,
-                                       fStitchTiles,
-                                       paintingData,
-                                       std::move(permutationsProxy),
-                                       std::move(noiseProxy),
-                                       m));
-        return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-    }
-    delete paintingData;
-    return nullptr;
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkPerlinNoiseShader2::toString(SkString* str) const {
-    str->append("SkPerlinNoiseShader2: (");
-
-    str->append("type: ");
-    switch (fType) {
-        case kFractalNoise_Type:
-            str->append("\"fractal noise\"");
-            break;
-        case kTurbulence_Type:
-            str->append("\"turbulence\"");
-            break;
-        default:
-            str->append("\"unknown\"");
-            break;
-    }
-    str->append(" base frequency: (");
-    str->appendScalar(fBaseFrequencyX);
-    str->append(", ");
-    str->appendScalar(fBaseFrequencyY);
-    str->append(") number of octaves: ");
-    str->appendS32(fNumOctaves);
-    str->append(" seed: ");
-    str->appendScalar(fSeed);
-    str->append(" stitch tiles: ");
-    str->append(fStitchTiles ? "true " : "false ");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.h b/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.h
deleted file mode 100644
index 0f40ae4..0000000
--- a/experimental/SkPerlinNoiseShader2/SkPerlinNoiseShader2.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkPerlinNoiseShader2_DEFINED
-#define SkPerlinNoiseShader2_DEFINED
-
-#include "SkShader.h"
-
-/** \class SkPerlinNoiseShader2
-
-    SkPerlinNoiseShader2 creates an image using the Perlin turbulence function.
-
-    It can produce tileable noise if asked to stitch tiles and provided a tile size.
-    In order to fill a large area with repeating noise, set the stitchTiles flag to
-    true, and render exactly a single tile of noise. Without this flag, the result
-    will contain visible seams between tiles.
-
-    The algorithm used is described here :
-    http://www.w3.org/TR/SVG/filters.html#feTurbulenceElement
-*/
-class SK_API SkPerlinNoiseShader2 : public SkShader {
-public:
-    struct StitchData;
-    struct PaintingData;
-
-    /**
-     *  About the noise types : the difference between the first 2 is just minor tweaks to the
-     *  algorithm, they're not 2 entirely different noises. The output looks different, but once the
-     *  noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by
-     *  doing :
-     *  kFractalNoise_Type : noise * 0.5 + 0.5
-     *  kTurbulence_Type   : abs(noise)
-     *  Very little differences between the 2 types, although you can tell the difference visually.
-     *  "Improved" is based on the Improved Perlin Noise algorithm described at
-     *  http://mrl.nyu.edu/~perlin/noise/. It is quite distinct from the other two, and the noise is
-     *  a 2D slice of a 3D noise texture. Minor changes to the Z coordinate will result in minor
-     *  changes to the noise, making it suitable for animated noise.
-     */
-    enum Type {
-        kFractalNoise_Type,
-        kTurbulence_Type,
-        kImprovedNoise_Type,
-        kFirstType = kFractalNoise_Type,
-        kLastType = kImprovedNoise_Type
-    };
-    /**
-     *  This will construct Perlin noise of the given type (Fractal Noise or Turbulence).
-     *
-     *  Both base frequencies (X and Y) have a usual range of (0..1).
-     *
-     *  The number of octaves provided should be fairly small, although no limit is enforced.
-     *  Each octave doubles the frequency, so 10 octaves would produce noise from
-     *  baseFrequency * 1, * 2, * 4, ..., * 512, which quickly yields insignificantly small
-     *  periods and resembles regular unstructured noise rather than Perlin noise.
-     *
-     *  If tileSize isn't NULL or an empty size, the tileSize parameter will be used to modify
-     *  the frequencies so that the noise will be tileable for the given tile size. If tileSize
-     *  is NULL or an empty size, the frequencies will be used as is without modification.
-     */
-    static sk_sp<SkShader> MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
-                                            int numOctaves, SkScalar seed,
-                                            const SkISize* tileSize = NULL);
-    static sk_sp<SkShader> MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
-                                          int numOctaves, SkScalar seed,
-                                          const SkISize* tileSize = NULL);
-    /**
-     * Creates an Improved Perlin Noise shader. The z value is roughly equivalent to the seed of the
-     * other two types, but minor variations to z will only slightly change the noise.
-     */
-    static sk_sp<SkShader> MakeImprovedNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
-                                             int numOctaves, SkScalar z);
-    /**
-     * Create alias for CreateTurbulunce until all Skia users changed
-     * its code to use the new naming
-     */
-    static sk_sp<SkShader> MakeTubulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
-                                         int numOctaves, SkScalar seed,
-                                         const SkISize* tileSize = NULL) {
-        return MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize);
-    }
-
-    class PerlinNoiseShaderContext : public SkShader::Context {
-    public:
-        PerlinNoiseShaderContext(const SkPerlinNoiseShader2& shader, const ContextRec&);
-        ~PerlinNoiseShaderContext() override;
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
-    private:
-        SkPMColor shade(const SkPoint& point, StitchData& stitchData) const;
-        SkScalar calculateTurbulenceValueForPoint(
-            int channel,
-            StitchData& stitchData, const SkPoint& point) const;
-        SkScalar calculateImprovedNoiseValueForPoint(int channel, const SkPoint& point) const;
-        SkScalar noise2D(int channel,
-                         const StitchData& stitchData, const SkPoint& noiseVector) const;
-
-        SkMatrix fMatrix;
-        PaintingData* fPaintingData;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPerlinNoiseShader2)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    SkPerlinNoiseShader2(SkPerlinNoiseShader2::Type type, SkScalar baseFrequencyX,
-                        SkScalar baseFrequencyY, int numOctaves, SkScalar seed,
-                        const SkISize* tileSize);
-    ~SkPerlinNoiseShader2() override;
-
-    const SkPerlinNoiseShader2::Type fType;
-    const SkScalar                  fBaseFrequencyX;
-    const SkScalar                  fBaseFrequencyY;
-    const int                       fNumOctaves;
-    const SkScalar                  fSeed;
-    const SkISize                   fTileSize;
-    const bool                      fStitchTiles;
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/experimental/documentation/gerrit.md b/experimental/documentation/gerrit.md
index e3aa775..7767c8c 100644
--- a/experimental/documentation/gerrit.md
+++ b/experimental/documentation/gerrit.md
@@ -122,4 +122,18 @@
 
     git config alias.gerrit-push-message '!f(){ git push origin @:refs/for/master%m=$(echo $*|sed "s/[^A-Za-z0-9]/_/g");};f'
 
+If your branch's upstream branch (set with `git branch --set-upstream-to=...`)
+is set, you can use that to automatically push to that branch:
+
+    gerrit_push_upstream() {
+        local UPSTREAM="$(git rev-parse --abbrev-ref --symbolic-full-name @{u})"
+        local REMOTE="${UPSTREAM%%/*}"
+        local REMOTE_BRANCH="${UPSTREAM#*/}"
+        local MESSAGE="$(echo $*|sed 's/[^A-Za-z0-9]/_/g')"
+        git push "$REMOTE" "@:refs/for/${REMOTE_BRANCH}%m=${MESSAGE}"
+    }
+
+As a Git alias:
+
+    git config alias.gerrit-push-upstream '!f()(U="$(git rev-parse --abbrev-ref --symbolic-full-name @{u})";R="${U%%/*}";B="${U#*/}";M="$(echo $*|sed 's/[^A-Za-z0-9]/_/g')";git push "$R" "@:refs/for/${B}%m=$M");f'
 
diff --git a/experimental/go-demo/main.go b/experimental/go-demo/main.go
new file mode 100644
index 0000000..7df64d6
--- /dev/null
+++ b/experimental/go-demo/main.go
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+package main
+
+/*
+	This is a translation of the example code in
+	"experimental/c-api-example/skia-c-example.c" to test the
+	go-skia package.
+*/
+
+import (
+	"log"
+	"os"
+
+	skia "skia.googlesource.com/skia/experimental/go-skia"
+)
+
+func main() {
+	imgInfo := &skia.ImageInfo{
+		Width:     640,
+		Height:    480,
+		ColorType: skia.GetDefaultColortype(),
+		AlphaType: skia.PREMUL_ALPHATYPE,
+	}
+
+	surface, err := skia.NewRasterSurface(imgInfo)
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	canvas := surface.Canvas()
+
+	fillPaint := skia.NewPaint()
+	fillPaint.SetColor(0xFF0000FF)
+	canvas.DrawPaint(fillPaint)
+	fillPaint.SetColor(0xFF00FFFF)
+
+	rect := skia.NewRect(100, 100, 540, 380)
+	canvas.DrawRect(rect, fillPaint)
+
+	strokePaint := skia.NewPaint()
+	strokePaint.SetColor(0xFFFF0000)
+	strokePaint.SetAntiAlias(true)
+	strokePaint.SetStroke(true)
+	strokePaint.SetStrokeWidth(5.0)
+
+	path := skia.NewPath()
+	path.MoveTo(50, 50)
+	path.LineTo(590, 50)
+	path.CubicTo(-490, 50, 1130, 430, 50, 430)
+	path.LineTo(590, 430)
+	canvas.DrawPath(path, strokePaint)
+
+	fillPaint.SetColor(0x8000FF00)
+	canvas.DrawOval(skia.NewRect(120, 120, 520, 360), fillPaint)
+
+	// // Get a skia image from the surface.
+	skImg := surface.Image()
+
+	// Write new image to file if we have one.
+	if skImg != nil {
+		out, err := os.Create("testimage.png")
+		if err != nil {
+			log.Fatal(err)
+		}
+		defer out.Close()
+
+		if err := skImg.WritePNG(out); err != nil {
+			log.Fatalf("Unable to write png: %s", err)
+		}
+	}
+}
diff --git a/experimental/go-skia/ctypes.go b/experimental/go-skia/ctypes.go
new file mode 100644
index 0000000..64953ed
--- /dev/null
+++ b/experimental/go-skia/ctypes.go
@@ -0,0 +1,58 @@
+//+build ignore
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+package skia
+
+// This file is used to generate 'types.go'
+// from the corresponding type definitions in the C API.
+// Any C struct for which we would like to generate a
+// Go struct with the same memory layout should defined defined here.
+// Any enum that is used in Go should also be listed here, together
+// with the enum values that we want to use.
+
+/*
+#cgo CFLAGS: -I../../include/c
+#include "../../include/c/sk_types.h"
+*/
+import "C"
+
+type Color C.sk_color_t
+
+type ColorType C.sk_colortype_t
+
+const (
+	UNKNOWN_COLORTYPE   ColorType = C.UNKNOWN_SK_COLORTYPE
+	RGBA_8888_COLORTYPE ColorType = C.RGBA_8888_SK_COLORTYPE
+	BGRA_8888_COLORTYPE ColorType = C.BGRA_8888_SK_COLORTYPE
+	ALPHA_8_COLORTYPE   ColorType = C.ALPHA_8_SK_COLORTYPE
+)
+
+type AlphaType C.sk_alphatype_t
+
+const (
+	OPAQUE_ALPHATYPE   AlphaType = C.OPAQUE_SK_ALPHATYPE
+	PREMUL_ALPHATYPE   AlphaType = C.PREMUL_SK_ALPHATYPE
+	UNPREMUL_ALPHATYPE AlphaType = C.UNPREMUL_SK_ALPHATYPE
+)
+
+type PixelGeometry C.sk_pixelgeometry_t
+
+const (
+	UNKNOWN_SK_PIXELGEOMETRY PixelGeometry = C.UNKNOWN_SK_PIXELGEOMETRY
+	RGB_H_SK_PIXELGEOMETRY   PixelGeometry = C.RGB_H_SK_PIXELGEOMETRY
+	BGR_H_SK_PIXELGEOMETRY   PixelGeometry = C.BGR_H_SK_PIXELGEOMETRY
+	RGB_V_SK_PIXELGEOMETRY   PixelGeometry = C.RGB_V_SK_PIXELGEOMETRY
+	BGR_V_SK_PIXELGEOMETRY   PixelGeometry = C.BGR_V_SK_PIXELGEOMETRY
+)
+
+type ImageInfo C.sk_imageinfo_t
+
+type SurfaceProps C.sk_surfaceprops_t
+
+type Rect C.sk_rect_t
diff --git a/experimental/go-skia/skia.go b/experimental/go-skia/skia.go
index 3073057..e477593 100644
--- a/experimental/go-skia/skia.go
+++ b/experimental/go-skia/skia.go
@@ -1,38 +1,213 @@
-package main
-
-// First, build Skia this way:
-//   ./gyp_skia -Dskia_shared_lib=1 && ninja -C out/Debug
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+package skia
 
 /*
-#cgo LDFLAGS: -lGL
-#cgo LDFLAGS: -lGLU
-#cgo LDFLAGS: -lX11
-#cgo LDFLAGS: -ldl
-#cgo LDFLAGS: -lfontconfig
-#cgo LDFLAGS: -lfreetype
-#cgo LDFLAGS: -lgif
-#cgo LDFLAGS: -lm
-#cgo LDFLAGS: -lpng
-#cgo LDFLAGS: -lstdc++
-#cgo LDFLAGS: -lz
-
-#cgo LDFLAGS: -L ../../out/Debug/lib
-#cgo LDFLAGS: -Wl,-rpath=../../out/Debug/lib
+#cgo LDFLAGS: -L${SRCDIR}/../../out/Shared
+#cgo LDFLAGS: -Wl,-rpath=${SRCDIR}/../../out/Shared
 #cgo LDFLAGS: -lskia
-
 #cgo CFLAGS: -I../../include/c
+#include "sk_canvas.h"
+#include "sk_data.h"
+#include "sk_image.h"
+#include "sk_paint.h"
+#include "sk_path.h"
 #include "sk_surface.h"
 */
 import "C"
 
 import (
 	"fmt"
+	"io"
+	"runtime"
+	"unsafe"
 )
 
-func main() {
-	p := C.sk_paint_new()
-	defer C.sk_paint_delete(p)
-	fmt.Println("OK!")
+// TODO(stephana): Add proper documentation to the types defined here.
+
+//////////////////////////////////////////////////////////////////////////
+// Surface
+//////////////////////////////////////////////////////////////////////////
+type Surface struct {
+	ptr *C.sk_surface_t
 }
 
-// TODO: replace this with an idiomatic interface to Skia.
+// func NewRasterSurface(width, height int32, alphaType AlphaType) (*Surface, error) {
+func NewRasterSurface(imgInfo *ImageInfo) (*Surface, error) {
+	ptr := C.sk_surface_new_raster(imgInfo.cPointer(), (*C.sk_surfaceprops_t)(nil))
+	if ptr == nil {
+		return nil, fmt.Errorf("Unable to create raster surface.")
+	}
+
+	ret := &Surface{ptr: ptr}
+	runtime.SetFinalizer(ret, func(s *Surface) {
+		C.sk_surface_unref(s.ptr)
+	})
+	return ret, nil
+}
+
+func (s *Surface) Canvas() *Canvas {
+	return &Canvas{
+		ptr:             C.sk_surface_get_canvas(s.ptr),
+		keepParentAlive: s,
+	}
+}
+
+func (s *Surface) Image() *Image {
+	ret := &Image{
+		ptr:             C.sk_surface_new_image_snapshot(s.ptr),
+		keepParentAlive: s,
+	}
+	runtime.SetFinalizer(ret, func(i *Image) {
+		C.sk_image_unref(i.ptr)
+	})
+	return ret
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Image
+//////////////////////////////////////////////////////////////////////////
+type Image struct {
+	ptr             *C.sk_image_t
+	keepParentAlive *Surface
+}
+
+func (i *Image) WritePNG(w io.Writer) error {
+	data := C.sk_image_encode(i.ptr)
+	defer C.sk_data_unref(data)
+
+	dataPtr := C.sk_data_get_data(data)
+	dataSize := C.sk_data_get_size(data)
+	byteSlice := C.GoBytes(dataPtr, C.int(dataSize))
+	_, err := w.Write(byteSlice)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Canvas
+//////////////////////////////////////////////////////////////////////////
+type Canvas struct {
+	ptr             *C.sk_canvas_t
+	keepParentAlive *Surface
+}
+
+func (c *Canvas) DrawPaint(paint *Paint) {
+	C.sk_canvas_draw_paint(c.ptr, paint.ptr)
+}
+
+func (c *Canvas) DrawOval(rect *Rect, paint *Paint) {
+	// C.sk_canvas_draw_oval(c.ptr, (*C.sk_rect_t)(unsafe.Pointer(rect)), (*C.sk_paint_t)(paint.ptr))
+	C.sk_canvas_draw_oval(c.ptr, rect.cPointer(), paint.ptr)
+}
+
+func (c *Canvas) DrawRect(rect *Rect, paint *Paint) {
+	// C.sk_canvas_draw_rect(c.ptr, (*C.sk_rect_t)(unsafe.Pointer(rect)), (*C.sk_paint_t)(paint.ptr))
+	C.sk_canvas_draw_rect(c.ptr, rect.cPointer(), paint.ptr)
+}
+
+func (c *Canvas) DrawPath(path *Path, paint *Paint) {
+	// C.sk_canvas_draw_path(c.ptr, (*C.sk_path_t)(path.ptr), (*C.sk_paint_t)(paint.ptr))
+	C.sk_canvas_draw_path(c.ptr, path.ptr, paint.ptr)
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Paint
+//////////////////////////////////////////////////////////////////////////
+type Paint struct {
+	ptr *C.sk_paint_t
+}
+
+func NewPaint() *Paint {
+	ret := &Paint{ptr: C.sk_paint_new()}
+	runtime.SetFinalizer(ret, func(p *Paint) {
+		C.sk_paint_delete(p.ptr)
+	})
+	return ret
+}
+
+func (p *Paint) SetColor(color Color) {
+	C.sk_paint_set_color(p.ptr, C.sk_color_t(color))
+}
+
+func (p *Paint) SetAntiAlias(antiAlias bool) {
+	C.sk_paint_set_antialias(p.ptr, C._Bool(antiAlias))
+}
+
+func (p *Paint) SetStroke(val bool) {
+	C.sk_paint_set_stroke(p.ptr, C._Bool(val))
+}
+
+func (p *Paint) SetStrokeWidth(width float32) {
+	C.sk_paint_set_stroke_width(p.ptr, C.float(width))
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Path
+//////////////////////////////////////////////////////////////////////////
+type Path struct {
+	ptr *C.sk_path_t
+}
+
+func NewPath() *Path {
+	ret := &Path{ptr: C.sk_path_new()}
+	runtime.SetFinalizer(ret, func(p *Path) {
+		C.sk_path_delete(p.ptr)
+	})
+	return ret
+}
+
+func (p *Path) MoveTo(x, y float32) {
+	C.sk_path_move_to(p.ptr, C.float(x), C.float(y))
+}
+
+func (p *Path) LineTo(x, y float32) {
+	C.sk_path_line_to(p.ptr, C.float(x), C.float(y))
+}
+
+func (p *Path) QuadTo(x0, y0, x1, y1 float32) {
+	C.sk_path_quad_to(p.ptr, C.float(x0), C.float(y0), C.float(x1), C.float(y1))
+}
+
+func (p *Path) ConicTo(x0, y0, x1, y1, w float32) {
+	C.sk_path_conic_to(p.ptr, C.float(x0), C.float(y0), C.float(x1), C.float(y1), C.float(w))
+}
+
+func (p *Path) CubicTo(x0, y0, x1, y1, x2, y2 float32) {
+	C.sk_path_cubic_to(p.ptr, C.float(x0), C.float(y0), C.float(x1), C.float(y1), C.float(x2), C.float(y2))
+}
+
+func (p *Path) Close() {
+	C.sk_path_close(p.ptr)
+}
+
+// NewRect is a convenience function to define a Rect in a single line.
+func NewRect(left, top, right, bottom float32) *Rect {
+	return &Rect{
+		Left:   left,
+		Top:    top,
+		Right:  right,
+		Bottom: bottom,
+	}
+}
+
+// cPointer casts the pointer to Rect to the corresponding C pointer.
+func (r *Rect) cPointer() *C.sk_rect_t {
+	return (*C.sk_rect_t)(unsafe.Pointer(r))
+}
+
+// cPointer casts the pointer to ImageInfo to the corresponding C pointer.
+func (i *ImageInfo) cPointer() *C.sk_imageinfo_t {
+	return (*C.sk_imageinfo_t)(unsafe.Pointer(i))
+}
+
+// Utility functions.
+func GetDefaultColortype() ColorType {
+	return ColorType(C.sk_colortype_get_default_8888())
+}
diff --git a/experimental/go-skia/types.go b/experimental/go-skia/types.go
new file mode 100644
index 0000000..637ecee
--- /dev/null
+++ b/experimental/go-skia/types.go
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Created by cgo -godefs. Enum fields in structs were fixed by hand.
+// command: go tool cgo -godefs ctypes.go > types.go
+//
+// The purpose of this file is to have Go structs with the same memory
+// layout as their C counterparts. For enums we want the underlying primitive
+// types to match.
+//
+// TODO(stephan): Add tests that allow to detect failure on platforms other
+// than Linux and changes in the underlying C types.
+
+package skia
+
+type Color uint32
+
+type ColorType uint32
+
+const (
+	UNKNOWN_COLORTYPE   ColorType = 0x0
+	RGBA_8888_COLORTYPE ColorType = 0x1
+	BGRA_8888_COLORTYPE ColorType = 0x2
+	ALPHA_8_COLORTYPE   ColorType = 0x3
+)
+
+type AlphaType uint32
+
+const (
+	OPAQUE_ALPHATYPE   AlphaType = 0x0
+	PREMUL_ALPHATYPE   AlphaType = 0x1
+	UNPREMUL_ALPHATYPE AlphaType = 0x2
+)
+
+type PixelGeometry uint32
+
+const (
+	UNKNOWN_SK_PIXELGEOMETRY PixelGeometry = 0x0
+	RGB_H_SK_PIXELGEOMETRY   PixelGeometry = 0x1
+	BGR_H_SK_PIXELGEOMETRY   PixelGeometry = 0x2
+	RGB_V_SK_PIXELGEOMETRY   PixelGeometry = 0x3
+	BGR_V_SK_PIXELGEOMETRY   PixelGeometry = 0x4
+)
+
+type ImageInfo struct {
+	Width     int32
+	Height    int32
+	ColorType ColorType
+	AlphaType AlphaType
+}
+
+type SurfaceProps struct {
+	PixelGeometry PixelGeometry
+}
+
+type Rect struct {
+	Left   float32
+	Top    float32
+	Right  float32
+	Bottom float32
+}
diff --git a/experimental/svg/model/SkSVGLine.cpp b/experimental/svg/model/SkSVGLine.cpp
index 9c8f5aa..2719a9b 100644
--- a/experimental/svg/model/SkSVGLine.cpp
+++ b/experimental/svg/model/SkSVGLine.cpp
@@ -68,7 +68,7 @@
     SkPoint p0, p1;
     std::tie(p0, p1) = this->resolve(lctx);
 
-    canvas->drawLine(p0.x(), p0.y(), p1.x(), p1.y(), paint);
+    canvas->drawLine(p0, p1, paint);
 }
 
 SkPath SkSVGLine::onAsPath(const SkSVGRenderContext& ctx) const {
diff --git a/fuzz/FilterFuzz.cpp b/fuzz/FilterFuzz.cpp
index 312f932..b2d99b7 100644
--- a/fuzz/FilterFuzz.cpp
+++ b/fuzz/FilterFuzz.cpp
@@ -39,6 +39,7 @@
 #include "SkPictureRecorder.h"
 #include "SkPoint3.h"
 #include "SkRandom.h"
+#include "SkRegion.h"
 #include "SkTableColorFilter.h"
 #include "SkTileImageFilter.h"
 #include "SkTypeface.h"
@@ -239,7 +240,7 @@
     paint.setColor(0xFF884422);
     paint.setTextSize(SkIntToScalar(kBitmapSize/2));
     const char* str = "g";
-    canvas.drawText(str, strlen(str), SkIntToScalar(kBitmapSize/8),
+    canvas.drawString(str, SkIntToScalar(kBitmapSize/8),
                     SkIntToScalar(kBitmapSize/4), paint);
 }
 
@@ -325,7 +326,7 @@
     canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint);
     paint.setColor(SK_ColorBLACK);
     paint.setTextSize(SkIntToScalar(kBitmapSize/3));
-    canvas->drawText("Picture", 7, SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
+    canvas->drawString("Picture", SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
 }
 
 static sk_sp<SkColorFilter> make_color_filter() {
diff --git a/fuzz/FuzzCanvas.cpp b/fuzz/FuzzCanvas.cpp
index 3b53339..fb90c06 100644
--- a/fuzz/FuzzCanvas.cpp
+++ b/fuzz/FuzzCanvas.cpp
@@ -38,7 +38,6 @@
 #include "SkDiscretePathEffect.h"
 #include "SkDisplacementMapEffect.h"
 #include "SkDropShadowImageFilter.h"
-#include "SkGaussianEdgeShader.h"
 #include "SkGradientShader.h"
 #include "SkHighContrastFilter.h"
 #include "SkImageSource.h"
@@ -54,6 +53,7 @@
 #include "SkPictureImageFilter.h"
 #include "SkRRectsGaussianEdgeMaskFilter.h"
 #include "SkTableColorFilter.h"
+#include "SkTextBlob.h"
 #include "SkTileImageFilter.h"
 #include "SkXfermodeImageFilter.h"
 
@@ -381,7 +381,8 @@
         }
         // EFFECTS:
         case 9:
-            return SkGaussianEdgeShader::Make();
+            // Deprecated SkGaussianEdgeShader
+            return nullptr;
         case 10: {
             constexpr int kMaxColors = 12;
             SkPoint pts[2];
@@ -982,7 +983,6 @@
     fuzz->nextRange(&w, 1, 1024);
     fuzz->nextRange(&h, 1, 1024);
     bitmap.allocN32Pixels(w, h);
-    SkAutoLockPixels autoLockPixels(bitmap);
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
             SkColor c;
@@ -1717,9 +1717,9 @@
             }
             case 53: {
                 fuzz_paint(fuzz, &paint, depth - 1);
-                SkCanvas::VertexMode vertexMode;
+                SkVertices::VertexMode vertexMode;
                 SkBlendMode blendMode;
-                fuzz_enum_range(fuzz, &vertexMode, 0, SkCanvas::kTriangleFan_VertexMode);
+                fuzz_enum_range(fuzz, &vertexMode, 0, SkVertices::kTriangleFan_VertexMode);
                 fuzz->next(&blendMode);
                 constexpr int kMaxCount = 100;
                 int vertexCount;
@@ -1744,18 +1744,11 @@
                         fuzz->nextRange(&indices[i], 0, vertexCount - 1);
                     }
                 }
-                if (make_fuzz_t<bool>(fuzz)) {
-                    canvas->drawVertices(vertexMode, vertexCount, vertices,
-                                         useTexs ? texs : nullptr, useColors ? colors : nullptr,
-                                         blendMode, indexCount > 0 ? indices : nullptr, indexCount,
-                                         paint);
-                } else {
-                    canvas->drawVertices(SkVertices::MakeCopy(vertexMode, vertexCount, vertices,
-                                                              useTexs ? texs : nullptr,
-                                                              useColors ? colors : nullptr,
-                                                              indexCount, indices),
-                                         blendMode, paint);
-                }
+                canvas->drawVertices(SkVertices::MakeCopy(vertexMode, vertexCount, vertices,
+                                                          useTexs ? texs : nullptr,
+                                                          useColors ? colors : nullptr,
+                                                          indexCount, indices),
+                                     blendMode, paint);
                 break;
             }
             default:
diff --git a/fuzz/fuzz.cpp b/fuzz/fuzz.cpp
index d156680..8e2a733 100644
--- a/fuzz/fuzz.cpp
+++ b/fuzz/fuzz.cpp
@@ -224,11 +224,10 @@
     }
 
     SkBitmap bitmap;
-    SkMallocPixelRef::ZeroedPRFactory zeroFactory;
     SkCodec::Options options;
     options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
 
-    if (!bitmap.tryAllocPixels(decodeInfo, &zeroFactory, colorTable.get())) {
+    if (!bitmap.tryAllocPixels(decodeInfo, colorTable, SkBitmap::kZeroPixels_AllocFlag)) {
         SkDebugf("[terminated] Could not allocate memory.  Image might be too large (%d x %d)",
                  decodeInfo.width(), decodeInfo.height());
         return;
@@ -558,7 +557,7 @@
 #if SK_SUPPORT_GPU
 static void fuzz_sksl2glsl(sk_sp<SkData> bytes) {
     SkSL::Compiler compiler;
-    SkString output;
+    SkSL::String output;
     SkSL::Program::Settings settings;
     sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
     settings.fCaps = caps.get();
diff --git a/gm/SkLinearBitmapPipelineGM.cpp b/gm/SkLinearBitmapPipelineGM.cpp
index cab20d3..87566ad 100644
--- a/gm/SkLinearBitmapPipelineGM.cpp
+++ b/gm/SkLinearBitmapPipelineGM.cpp
@@ -16,7 +16,7 @@
 #include "SkLinearBitmapPipeline.h"
 #include "SkXfermodePriv.h"
 #include "SkPM4fPriv.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 
 static void fill_in_bits(SkBitmap& bm, SkIRect ir, SkColor c, bool premul) {
     bm.allocN32Pixels(ir.width(), ir.height());
@@ -72,11 +72,11 @@
         paint.setFilterQuality(SkFilterQuality::kNone_SkFilterQuality);
     }
     paint.setShader(std::move(shader));
-    const SkShader::ContextRec rec(paint, *mat, nullptr,
-                                   SkBlitter::PreferredShaderDest(pmsrc.info()),
-                                   canvas->imageInfo().colorSpace());
+    const SkShaderBase::ContextRec rec(paint, *mat, nullptr,
+                                       SkBlitter::PreferredShaderDest(pmsrc.info()),
+                                       canvas->imageInfo().colorSpace());
 
-    SkShader::Context* ctx = paint.getShader()->makeContext(rec, &alloc);
+    SkShaderBase::Context* ctx = as_SB(paint.getShader())->makeContext(rec, &alloc);
 
     for (int y = 0; y < ir.height(); y++) {
         ctx->shadeSpan(0, y, pmdst.writable_addr32(0, y), ir.width());
@@ -115,8 +115,7 @@
     uint32_t flags = 0;
     auto procN = SkXfermode::GetD32Proc(SkBlendMode::kSrcOver, flags);
 
-    char storage[512];
-    SkArenaAlloc allocator{storage, sizeof(storage)};
+    SkSTArenaAlloc<512> allocator;
     SkLinearBitmapPipeline pipeline{
             inv, filterQuality,
             SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode,
diff --git a/gm/aaclip.cpp b/gm/aaclip.cpp
index 1431eb6..0c90f53 100644
--- a/gm/aaclip.cpp
+++ b/gm/aaclip.cpp
@@ -65,6 +65,7 @@
     canvas->saveLayer(rec);
     do_draw(canvas, r);
     canvas->restore();
+    canvas->restore();
 
     canvas->restore();  // red-layer
 }
diff --git a/gm/aaxfermodes.cpp b/gm/aaxfermodes.cpp
index 96b9b48..eab68b4 100644
--- a/gm/aaxfermodes.cpp
+++ b/gm/aaxfermodes.cpp
@@ -107,10 +107,10 @@
 
             if (kShape_Pass == drawingPass) {
                 fLabelPaint.setTextAlign(SkPaint::kCenter_Align);
-                canvas->drawText("Src Unknown", sizeof("Src Unknown") - 1,
+                canvas->drawString("Src Unknown",
                         kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2,
                         kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, fLabelPaint);
-                canvas->drawText("Src Opaque", sizeof("Src Opaque") - 1,
+                canvas->drawString("Src Opaque",
                         kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 +
                         kPaintSpacing, kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3,
                         fLabelPaint);
@@ -180,10 +180,10 @@
         titlePaint.setTextSize(9 * titlePaint.getTextSize() / 8);
         titlePaint.setFakeBoldText(true);
         titlePaint.setTextAlign(SkPaint::kCenter_Align);
-        canvas->drawText("Porter Duff", sizeof("Porter Duff") - 1,
+        canvas->drawString("Porter Duff",
                          kLabelSpacing + 4 * kShapeTypeSpacing,
                          kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint);
-        canvas->drawText("Advanced", sizeof("Advanced") - 1,
+        canvas->drawString("Advanced",
                          kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing,
                          kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint);
 
@@ -194,7 +194,7 @@
     void drawModeName(SkCanvas* canvas, SkBlendMode mode) {
         const char* modeName = SkBlendMode_Name(mode);
         fLabelPaint.setTextAlign(SkPaint::kRight_Align);
-        canvas->drawText(modeName, strlen(modeName), kLabelSpacing - kShapeSize / 4,
+        canvas->drawString(modeName, kLabelSpacing - kShapeSize / 4,
                          fLabelPaint.getTextSize() / 4, fLabelPaint);
     }
 
diff --git a/gm/addarc.cpp b/gm/addarc.cpp
index 96ac666..763135f 100644
--- a/gm/addarc.cpp
+++ b/gm/addarc.cpp
@@ -103,7 +103,7 @@
             SkScalar arcLen = rad * R;
             SkPoint pos;
             if (meas.getPosTan(arcLen, &pos, nullptr)) {
-                canvas->drawLine(0, 0, pos.x(), pos.y(), measPaint);
+                canvas->drawLine({0, 0}, pos, measPaint);
             }
         }
     }
diff --git a/gm/all_bitmap_configs.cpp b/gm/all_bitmap_configs.cpp
index ea7b734..4c8ea35 100644
--- a/gm/all_bitmap_configs.cpp
+++ b/gm/all_bitmap_configs.cpp
@@ -14,8 +14,15 @@
 #include "SkColorPriv.h"
 
 static SkBitmap copy_bitmap(const SkBitmap& src, SkColorType colorType) {
+    const SkBitmap* srcPtr = &src;
+    SkBitmap tmp(src);
+    if (kRGB_565_SkColorType == colorType) {
+        tmp.setAlphaType(kOpaque_SkAlphaType);
+        srcPtr = &tmp;
+    }
+
     SkBitmap copy;
-    src.copyTo(&copy, colorType);
+    sk_tool_utils::copy_to(&copy, colorType, *srcPtr);
     copy.setImmutable();
     return copy;
 }
@@ -37,7 +44,6 @@
             SkASSERT(false);
             return bm;
     }
-    SkAutoLockPixels autoLockPixels(bm);
     uint8_t spectrum[256];
     for (int y = 0; y < 256; ++y) {
         spectrum[y] = y;
@@ -124,12 +130,9 @@
         pmColors[i] = premultiply_color(colors[i]);
     }
     SkBitmap bm;
-    sk_sp<SkColorTable> ctable(new SkColorTable(pmColors, SK_ARRAY_COUNT(pmColors)));
     SkImageInfo info = SkImageInfo::Make(SCALE, SCALE, kIndex_8_SkColorType,
                                          kPremul_SkAlphaType);
-    bm.allocPixels(info, nullptr, ctable.get());
-    SkAutoLockPixels autoLockPixels1(n32bitmap);
-    SkAutoLockPixels autoLockPixels2(bm);
+    bm.allocPixels(info, SkColorTable::Make(pmColors, SK_ARRAY_COUNT(pmColors)));
     for (int y = 0; y < SCALE; ++y) {
         for (int x = 0; x < SCALE; ++x) {
             SkPMColor c = *n32bitmap.getAddr32(x, y);
@@ -147,7 +150,7 @@
                  const char text[]) {
     SkASSERT(src.colorType() == colorType);
     canvas->drawBitmap(src, 0.0f, 0.0f);
-    canvas->drawText(text, strlen(text), 0.0f, 12.0f, p);
+    canvas->drawString(text, 0.0f, 12.0f, p);
 }
 
 DEF_SIMPLE_GM(all_bitmap_configs, canvas, SCALE, 6 * SCALE) {
@@ -204,7 +207,7 @@
         const SkColorType ct = kBGRA_8888_SkColorType;
     #endif
     static_assert(ct != kN32_SkColorType, "BRGA!=RGBA");
-    SkAssertResult(n32bitmap.copyTo(&notN32bitmap, ct));
+    SkAssertResult(sk_tool_utils::copy_to(&notN32bitmap, ct, n32bitmap));
     SkASSERT(notN32bitmap.colorType() == ct);
     return SkImage::MakeFromBitmap(notN32bitmap);
 }
diff --git a/gm/animatedGif.cpp b/gm/animatedGif.cpp
index 03bf66c..5f4fe71 100644
--- a/gm/animatedGif.cpp
+++ b/gm/animatedGif.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "gm.h"
+#include "sk_tool_utils.h"
 #include "SkAnimTimer.h"
 #include "SkCanvas.h"
 #include "SkCodec.h"
@@ -26,7 +27,7 @@
         SkPaint paint;
         SkRect bounds;
         paint.measureText(errorText.c_str(), errorText.size(), &bounds);
-        canvas->drawText(errorText.c_str(), errorText.size(), kOffset, bounds.height() + kOffset,
+        canvas->drawString(errorText, kOffset, bounds.height() + kOffset,
                          paint);
     }
 }
@@ -34,9 +35,9 @@
 class AnimatedGifGM : public skiagm::GM {
 private:
     std::unique_ptr<SkCodec>        fCodec;
-    size_t                          fFrame;
+    int                             fFrame;
     double                          fNextUpdate;
-    size_t                          fTotalFrames;
+    int                             fTotalFrames;
     std::vector<SkCodec::FrameInfo> fFrameInfos;
     std::vector<SkBitmap>           fFrames;
 
@@ -53,12 +54,14 @@
             SkCodec::Options opts;
             opts.fFrameIndex = frameIndex;
             opts.fHasPriorFrame = false;
-            const size_t requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
+            const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
             if (requiredFrame != SkCodec::kNone) {
-                SkASSERT(requiredFrame < fFrames.size());
+                SkASSERT(requiredFrame >= 0
+                         && static_cast<size_t>(requiredFrame) < fFrames.size());
                 SkBitmap& requiredBitmap = fFrames[requiredFrame];
                 // For simplicity, do not try to cache old frames
-                if (requiredBitmap.getPixels() && requiredBitmap.copyTo(&bm)) {
+                if (requiredBitmap.getPixels() &&
+                        sk_tool_utils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
                     opts.fHasPriorFrame = true;
                 }
             }
@@ -101,7 +104,7 @@
         canvas->clear(SK_ColorWHITE);
         if (this->initCodec()) {
             SkAutoCanvasRestore acr(canvas, true);
-            for (size_t frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
+            for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
                 this->drawFrame(canvas, frameIndex);
                 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0);
             }
diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp
index c01ece7..54169ce 100644
--- a/gm/arithmode.cpp
+++ b/gm/arithmode.cpp
@@ -60,7 +60,7 @@
         SkString str;
         str.appendScalar(k[i]);
         SkScalar width = paint.measureText(str.c_str(), str.size());
-        canvas->drawText(str.c_str(), str.size(), x, y + paint.getTextSize(), paint);
+        canvas->drawString(str, x, y + paint.getTextSize(), paint);
         x += width + SkIntToScalar(10);
     }
 }
@@ -152,7 +152,7 @@
                 paint.setAntiAlias(true);
                 sk_tool_utils::set_portable_typeface(&paint);
                 SkString str(enforcePMColor ? "enforcePM" : "no enforcePM");
-                canvas->drawText(str.c_str(), str.size(), 0, paint.getTextSize(), paint);
+                canvas->drawString(str, 0, paint.getTextSize(), paint);
             }
             canvas->translate(0, HH + 12);
         }
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index 523ccd3..d3f1965 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -30,9 +30,10 @@
 
     const char* name() const override { return "BezierCubicOrConicTestOp"; }
 
-    static std::unique_ptr<GrMeshDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
-                                              GrColor color, const SkMatrix& klm, SkScalar sign) {
-        return std::unique_ptr<GrMeshDrawOp>(
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(sk_sp<GrGeometryProcessor> gp,
+                                                    const SkRect& rect, GrColor color,
+                                                    const SkMatrix& klm, SkScalar sign) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(
                 new BezierCubicOrConicTestOp(gp, rect, color, klm, sign));
     }
 
@@ -66,7 +67,7 @@
             SkScalar pt3[3] = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
             fKLM.mapHomogeneousPoints(verts[v].fKLM, pt3, 1);
         }
-        helper.recordDraw(target, fGeometryProcessor.get());
+        helper.recordDraw(target, fGeometryProcessor.get(), this->pipeline());
     }
 
     SkMatrix fKLM;
@@ -159,9 +160,9 @@
 
                 SkPaint ctrlPtPaint;
                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
-                canvas->drawCircle(controlPts[0].fX, controlPts[0].fY, 8.f, ctrlPtPaint);
+                canvas->drawCircle(controlPts[0], 8.f, ctrlPtPaint);
                 for (int i = 1; i < 4; ++i) {
-                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
+                    canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
                 }
 
                 SkPaint polyPaint;
@@ -177,7 +178,7 @@
                     SkPoint* pts = chopped + 3 * c;
 
                     for (int i = 0; i < 4; ++i) {
-                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
+                        canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
                     }
 
                     SkRect bounds;
@@ -197,10 +198,10 @@
                         sign = -1.0f;
                     }
 
-                    std::unique_ptr<GrMeshDrawOp> op =
+                    std::unique_ptr<GrLegacyMeshDrawOp> op =
                             BezierCubicOrConicTestOp::Make(gp, bounds, color, klm, sign);
 
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
+                    renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
                             std::move(grPaint), GrAAType::kNone, std::move(op));
                 }
                 ++col;
@@ -298,7 +299,7 @@
                 SkPaint ctrlPtPaint;
                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
                 for (int i = 0; i < 3; ++i) {
-                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
+                    canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
                 }
 
                 SkPaint polyPaint;
@@ -313,7 +314,7 @@
                 for (int c = 0; c < cnt; ++c) {
                     SkPoint* pts = dst[c].fPts;
                     for (int i = 0; i < 3; ++i) {
-                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
+                        canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
                     }
 
                     SkRect bounds;
@@ -330,10 +331,10 @@
                     GrPaint grPaint;
                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
 
-                    std::unique_ptr<GrMeshDrawOp> op =
+                    std::unique_ptr<GrLegacyMeshDrawOp> op =
                             BezierCubicOrConicTestOp::Make(gp, bounds, color, klm, 1.f);
 
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
+                    renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
                             std::move(grPaint), GrAAType::kNone, std::move(op));
                 }
                 ++col;
@@ -396,10 +397,10 @@
     DEFINE_OP_CLASS_ID
     const char* name() const override { return "BezierQuadTestOp"; }
 
-    static std::unique_ptr<GrMeshDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
-                                              GrColor color,
-                                              const GrPathUtils::QuadUVMatrix& devToUV) {
-        return std::unique_ptr<GrMeshDrawOp>(new BezierQuadTestOp(gp, rect, color, devToUV));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(sk_sp<GrGeometryProcessor> gp,
+                                                    const SkRect& rect, GrColor color,
+                                                    const GrPathUtils::QuadUVMatrix& devToUV) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new BezierQuadTestOp(gp, rect, color, devToUV));
     }
 
 private:
@@ -426,7 +427,7 @@
         verts[0].fPosition.setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
                                       sizeof(Vertex));
         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
-        helper.recordDraw(target, fGeometryProcessor.get());
+        helper.recordDraw(target, fGeometryProcessor.get(), this->pipeline());
     }
 
     GrPathUtils::QuadUVMatrix fDevToUV;
@@ -515,7 +516,7 @@
                 SkPaint ctrlPtPaint;
                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
                 for (int i = 0; i < 3; ++i) {
-                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
+                    canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
                 }
 
                 SkPaint polyPaint;
@@ -531,7 +532,7 @@
                     SkPoint* pts = chopped + 2 * c;
 
                     for (int i = 0; i < 3; ++i) {
-                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
+                        canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
                     }
 
                     SkRect bounds;
@@ -548,10 +549,10 @@
 
                     GrPathUtils::QuadUVMatrix DevToUV(pts);
 
-                    std::unique_ptr<GrMeshDrawOp> op =
+                    std::unique_ptr<GrLegacyMeshDrawOp> op =
                             BezierQuadTestOp::Make(gp, bounds, color, DevToUV);
 
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
+                    renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
                             std::move(grPaint), GrAAType::kNone, std::move(op));
                 }
                 ++col;
diff --git a/gm/bigrect.cpp b/gm/bigrect.cpp
new file mode 100644
index 0000000..d2ba7fe
--- /dev/null
+++ b/gm/bigrect.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "gm.h"
+
+// Draws big rects with clip (0, 0, 35, 35). The size of the rects is given by big.
+static void draw_big_rect(SkCanvas* canvas, SkScalar big, const SkPaint& rectPaint) {
+    // Looks like this:
+    // +--+-+----+-+----+
+    // |  | |    | |    |
+    // |--+-+----+-+----+
+    // |--+-+----+-+----+
+    // |  | |    | |    |
+    // |  | |    +-+    |
+    // +--+-+--+     +--+
+    // +--+-+--+     +--+
+    // |  | |    +-+    |
+    // |  | |    | |    |
+    // +--+-+----+-+----+
+
+    canvas->clipRect({0, 0, 35, 35});
+
+    // Align to pixel boundaries.
+    canvas->translate(0.5, 0.5);
+
+    SkRect horiz = SkRect::MakeLTRB(-big, 5, big, 10);
+    canvas->drawRect(horiz, rectPaint);
+
+    SkRect vert = SkRect::MakeLTRB(5, -big, 10, big);
+    canvas->drawRect(vert, rectPaint);
+
+    SkRect fromLeft = SkRect::MakeLTRB(-big, 20, 17, 25);
+    canvas->drawRect(fromLeft, rectPaint);
+
+    SkRect fromTop = SkRect::MakeLTRB(20, -big, 25, 17);
+    canvas->drawRect(fromTop, rectPaint);
+
+    SkRect fromRight = SkRect::MakeLTRB(28, 20, big, 25);
+    canvas->drawRect(fromRight, rectPaint);
+
+    SkRect fromBottom = SkRect::MakeLTRB(20, 28, 25, big);
+    canvas->drawRect(fromBottom, rectPaint);
+
+    SkRect leftBorder = SkRect::MakeLTRB(-2, -1, 0, 35);
+    canvas->drawRect(leftBorder, rectPaint);
+
+    SkRect topBorder = SkRect::MakeLTRB(-1, -2, 35, 0);
+    canvas->drawRect(topBorder, rectPaint);
+
+    SkRect rightBorder = SkRect::MakeLTRB(34, -1, 36, 35);
+    canvas->drawRect(rightBorder, rectPaint);
+
+    SkRect bottomBorder = SkRect::MakeLTRB(-1, 34, 35, 36);
+    canvas->drawRect(bottomBorder, rectPaint);
+
+    SkPaint outOfBoundsPaint;
+    outOfBoundsPaint.setColor(SK_ColorRED);
+    outOfBoundsPaint.setStyle(SkPaint::kStroke_Style);
+    outOfBoundsPaint.setStrokeWidth(0);
+
+    SkRect outOfBounds = SkRect::MakeLTRB(-1, -1, 35, 35);
+    canvas->drawRect(outOfBounds, outOfBoundsPaint);
+}
+
+DEF_SIMPLE_GM(bigrect, canvas, 325, 125) {
+    // Test with sizes:
+    //   - reasonable size (for comparison),
+    //   - outside the range of int32, and
+    //   - outside the range of SkFixed.
+    static const SkScalar sizes[] = {SkIntToScalar(100), 5e10f, 1e6f};
+
+    for (int i = 0; i < 8; i++) {
+        for (int j = 0; j < 3; j++) {
+            canvas->save();
+            canvas->translate(SkIntToScalar(i*40+5), SkIntToScalar(j*40+5));
+
+            SkPaint paint;
+            paint.setColor(SK_ColorBLUE);
+            // These are the three parameters that affect the behavior of SkDraw::drawRect.
+            if (i & 1) {
+                paint.setStyle(SkPaint::kFill_Style);
+            } else {
+                paint.setStyle(SkPaint::kStroke_Style);
+            }
+            if (i & 2) {
+                paint.setStrokeWidth(1);
+            } else {
+                paint.setStrokeWidth(0);
+            }
+            if (i & 4) {
+                paint.setAntiAlias(true);
+            } else {
+                paint.setAntiAlias(false);
+            }
+
+            const SkScalar big = SkFloatToScalar(sizes[j]);
+            draw_big_rect(canvas, big, paint);
+            canvas->restore();
+        }
+    }
+}
diff --git a/gm/bigrrectaaeffect.cpp b/gm/bigrrectaaeffect.cpp
index 0520776..92c3a24 100644
--- a/gm/bigrrectaaeffect.cpp
+++ b/gm/bigrrectaaeffect.cpp
@@ -13,7 +13,7 @@
 #include "SkRRect.h"
 #include "effects/GrRRectEffect.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 namespace skiagm {
 
@@ -81,16 +81,16 @@
                 SkASSERT(fp);
                 if (fp) {
                     GrPaint grPaint;
+                    grPaint.setColor4f(GrColor4f(0, 0, 0, 1.f));
                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
                     grPaint.addCoverageFragmentProcessor(std::move(fp));
 
                     SkRect bounds = testBounds;
                     bounds.offset(SkIntToScalar(x), SkIntToScalar(y));
 
-                    std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                            0xff000000, SkMatrix::I(), bounds, nullptr, nullptr));
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                            std::move(grPaint), GrAAType::kNone, std::move(op));
+                    renderTargetContext->priv().testingOnly_addDrawOp(
+                            GrNonAAFillRectOp::Make(std::move(grPaint), SkMatrix::I(), bounds,
+                                                    nullptr, nullptr, GrAAType::kNone));
                 }
             canvas->restore();
             x = x + fTestOffsetX;
diff --git a/gm/bigtext.cpp b/gm/bigtext.cpp
index 4d5005e..6b3c9b1 100644
--- a/gm/bigtext.cpp
+++ b/gm/bigtext.cpp
@@ -43,7 +43,7 @@
         };
 
         paint.setColor(SK_ColorRED);
-        canvas->drawText("/", 1, pos.fX, pos.fY, paint);
+        canvas->drawString("/", pos.fX, pos.fY, paint);
 
         paint.setColor(SK_ColorBLUE);
         canvas->drawPosText("\\", 1, &pos, paint);
diff --git a/gm/bitmapcopy.cpp b/gm/bitmapcopy.cpp
index 60eecf2..ec9e423 100644
--- a/gm/bitmapcopy.cpp
+++ b/gm/bitmapcopy.cpp
@@ -65,13 +65,13 @@
         SkScalar vertMargin = 10;
 
         SkBitmap src;
-        src.allocN32Pixels(40, 40);
+        src.allocN32Pixels(40, 40, kOpaque_SkAlphaType);
         SkCanvas canvasTmp(src);
 
         draw_checks(&canvasTmp, 40, 40);
 
         for (unsigned i = 0; i < NUM_CONFIGS; ++i) {
-            src.copyTo(&fDst[i], gColorTypes[i]);
+            sk_tool_utils::copy_to(&fDst[i], gColorTypes[i], src);
         }
 
         canvas->clear(sk_tool_utils::color_to_565(0xFFDDDDDD));
@@ -101,7 +101,7 @@
             SkScalar textWidth = paint.measureText(name, strlen(name));
             SkScalar x = (width - textWidth) / SkScalar(2);
             SkScalar y = paint.getFontSpacing() / SkScalar(2);
-            canvas->drawText(name, strlen(name), x, y, paint);
+            canvas->drawString(name, x, y, paint);
 
             // Draw destination bitmap
             canvas->translate(0, vertOffset);
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
index 2cded98..4d31cae 100644
--- a/gm/bitmapfilters.cpp
+++ b/gm/bitmapfilters.cpp
@@ -17,12 +17,9 @@
     for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
         colorsPM[i] = SkPreMultiplyColor(colors[i]);
     }
-    SkColorTable* ctable = new SkColorTable(colorsPM, 4);
-
     bm->allocPixels(SkImageInfo::Make(2, 2, kIndex_8_SkColorType,
-                                      kPremul_SkAlphaType),
-                    nullptr, ctable);
-    ctable->unref();
+                                      kOpaque_SkAlphaType),
+                    SkColorTable::Make(colorsPM, 4));
 
     *bm->getAddr8(0, 0) = 0;
     *bm->getAddr8(1, 0) = 1;
@@ -55,7 +52,7 @@
     paint.setAntiAlias(true);
     sk_tool_utils::set_portable_typeface(&paint);
     const char* name = sk_tool_utils::colortype_name(bm.colorType());
-    canvas->drawText(name, strlen(name), x, SkIntToScalar(bm.height())*scale*5/8,
+    canvas->drawString(name, x, SkIntToScalar(bm.height())*scale*5/8,
                      paint);
     canvas->translate(SkIntToScalar(48), 0);
 
@@ -71,9 +68,9 @@
 class FilterGM : public skiagm::GM {
     void onOnceBeforeDraw() override {
         make_bm(&fBM8);
-        fBM8.copyTo(&fBM4444, kARGB_4444_SkColorType);
-        fBM8.copyTo(&fBM16, kRGB_565_SkColorType);
-        fBM8.copyTo(&fBM32, kN32_SkColorType);
+        sk_tool_utils::copy_to(&fBM4444, kARGB_4444_SkColorType, fBM8);
+        sk_tool_utils::copy_to(&fBM16, kRGB_565_SkColorType, fBM8);
+        sk_tool_utils::copy_to(&fBM32, kN32_SkColorType, fBM8);
     }
 
 public:
diff --git a/gm/bitmapimage.cpp b/gm/bitmapimage.cpp
index 8c37d5d..be17af8 100644
--- a/gm/bitmapimage.cpp
+++ b/gm/bitmapimage.cpp
@@ -8,6 +8,7 @@
 #include "gm.h"
 #include "Resources.h"
 #include "SkCodec.h"
+#include "SkImage.h"
 
 namespace skiagm {
 
diff --git a/gm/blurignorexform.cpp b/gm/blurignorexform.cpp
new file mode 100644
index 0000000..8db56ab
--- /dev/null
+++ b/gm/blurignorexform.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "sk_tool_utils.h"
+
+#include "SkBlurMask.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+// This GM tests out the SkBlurMaskFilter's kIgnoreTransform flag. That flag causes the blur mask
+// filter to not apply the CTM to the blur's radius.
+class BlurIgnoreXformGM : public skiagm::GM {
+public:
+    enum class DrawType {
+        kCircle,
+        kRect,
+        kRRect,
+    };
+
+    BlurIgnoreXformGM(DrawType drawType) : fDrawType(drawType) { }
+
+protected:
+    bool runAsBench() const override { return true; }
+
+    SkString onShortName() override {
+        SkString name;
+        name.printf("blur_ignore_xform_%s",
+                    DrawType::kCircle == fDrawType ? "circle"
+                        : DrawType::kRect == fDrawType ? "rect" : "rrect");
+        return name;
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(375, 475);
+    }
+
+    void onOnceBeforeDraw() override {
+        for (int i = 0; i < kNumBlurs; ++i) {
+            fBlurFilters[i] = SkBlurMaskFilter::Make(
+                                    kNormal_SkBlurStyle,
+                                    SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(20)),
+                                    SkBlurMaskFilter::kHighQuality_BlurFlag | kBlurFlags[i].fFlags);
+        }
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setColor(SK_ColorBLACK);
+        paint.setAntiAlias(true);
+
+        canvas->translate(10, 25);
+        canvas->save();
+        canvas->translate(80, 0);
+        for (size_t i = 0; i < kNumBlurs; ++i) {
+            SkAutoCanvasRestore autoRestore(canvas, true);
+            canvas->translate(SkIntToScalar(i * 150), 0);
+            for (auto scale : kMatrixScales) {
+                canvas->save();
+                canvas->scale(scale.fScale, scale.fScale);
+                static const SkScalar kRadius = 20.0f;
+                SkScalar coord = 50.0f * 1.0f / scale.fScale;
+                SkRect rect = SkRect::MakeXYWH(coord - kRadius , coord - kRadius,
+                                               2 * kRadius, 2 * kRadius);
+                SkRRect rrect = SkRRect::MakeRectXY(rect, kRadius/2.0f, kRadius/2.0f);
+
+                paint.setMaskFilter(fBlurFilters[i]);
+                for (int j = 0; j < 2; ++j) {
+                    canvas->save();
+                    canvas->translate(10 * (1 - j), 10 * (1 - j));
+                    if (DrawType::kCircle == fDrawType) {
+                        canvas->drawCircle(coord, coord, kRadius, paint);
+                    } else if (DrawType::kRect == fDrawType) {
+                        canvas->drawRect(rect, paint);
+                    } else {
+                        canvas->drawRRect(rrect, paint);
+                    }
+                    paint.setMaskFilter(nullptr);
+                    canvas->restore();
+                }
+                canvas->restore();
+                canvas->translate(0, SkIntToScalar(150));
+            }
+        }
+        canvas->restore();
+        if (kBench_Mode != this->getMode()) {
+            this->drawOverlay(canvas);
+        }
+    }
+
+    void drawOverlay(SkCanvas* canvas) {
+        canvas->translate(10, 0);
+        SkPaint textPaint;
+        sk_tool_utils::set_portable_typeface(&textPaint);
+        textPaint.setAntiAlias(true);
+        canvas->save();
+        for (int i = 0; i < kNumBlurs; ++i) {
+            canvas->drawString(kBlurFlags[i].fName, 100, 0, textPaint);
+            canvas->translate(SkIntToScalar(130), 0);
+        }
+        canvas->restore();
+        for (auto scale : kMatrixScales) {
+            canvas->drawString(scale.fName, 0, 50, textPaint);
+            canvas->translate(0, SkIntToScalar(150));
+        }
+    }
+
+private:
+    static constexpr int kNumBlurs = 2;
+
+    static const struct BlurFlags {
+        uint32_t fFlags;
+        const char* fName;
+    } kBlurFlags[kNumBlurs];
+
+    static const struct MatrixScale {
+        float fScale;
+        const char* fName;
+    } kMatrixScales[3];
+
+    DrawType fDrawType;
+    sk_sp<SkMaskFilter> fBlurFilters[kNumBlurs];
+
+    typedef         skiagm::GM INHERITED;
+};
+
+const BlurIgnoreXformGM::BlurFlags BlurIgnoreXformGM::kBlurFlags[] = {
+    {0, "none"},
+    {SkBlurMaskFilter::kIgnoreTransform_BlurFlag, "IgnoreTransform"}
+};
+
+const BlurIgnoreXformGM::MatrixScale BlurIgnoreXformGM::kMatrixScales[] = {
+    {1.0f, "Identity"},
+    {0.5f, "Scale = 0.5"},
+    {2.0f, "Scale = 2.0"}
+};
+
+DEF_GM(return new BlurIgnoreXformGM(BlurIgnoreXformGM::DrawType::kCircle);)
+DEF_GM(return new BlurIgnoreXformGM(BlurIgnoreXformGM::DrawType::kRect);)
+DEF_GM(return new BlurIgnoreXformGM(BlurIgnoreXformGM::DrawType::kRRect);)
+
+
+
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index f1f44e7..a7b1a5f 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -55,14 +55,14 @@
                 SkScalar x = SkIntToScalar(70);
                 SkScalar y = SkIntToScalar(400);
                 paint.setColor(SK_ColorBLACK);
-                canvas->drawText("Hamburgefons Style", 18, x, y, paint);
-                canvas->drawText("Hamburgefons Style", 18,
+                canvas->drawString("Hamburgefons Style", x, y, paint);
+                canvas->drawString("Hamburgefons Style",
                                  x, y + SkIntToScalar(50), paint);
                 paint.setMaskFilter(nullptr);
                 paint.setColor(SK_ColorWHITE);
                 x -= SkIntToScalar(2);
                 y -= SkIntToScalar(2);
-                canvas->drawText("Hamburgefons Style", 18, x, y, paint);
+                canvas->drawString("Hamburgefons Style", x, y, paint);
             }
             canvas->restore();
             flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
diff --git a/gm/bmpfilterqualityrepeat.cpp b/gm/bmpfilterqualityrepeat.cpp
index e7ea97d..313fd93 100644
--- a/gm/bmpfilterqualityrepeat.cpp
+++ b/gm/bmpfilterqualityrepeat.cpp
@@ -76,7 +76,7 @@
             bmpPaint.setShader(SkShader::MakeBitmapShader(fBmp, kTM, kTM, &lm));
             bmpPaint.setFilterQuality(kQualities[q].fQuality);
             canvas->drawRect(rect, bmpPaint);
-            canvas->drawText(kQualities[q].fName, strlen(kQualities[q].fName), 20, 40, textPaint);
+            canvas->drawString(kQualities[q].fName, 20, 40, textPaint);
             canvas->translate(250, 0);
         }
 
diff --git a/gm/bug6643.cpp b/gm/bug6643.cpp
new file mode 100644
index 0000000..abe79fa
--- /dev/null
+++ b/gm/bug6643.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SkGradientShader.h"
+#include "SkPictureRecorder.h"
+#include "gm.h"
+
+DEF_SIMPLE_GM(bug6643, canvas, 200, 200) {
+    SkColor colors[] = { SK_ColorTRANSPARENT, SK_ColorGREEN, SK_ColorTRANSPARENT };
+
+    SkPaint p;
+    p.setAntiAlias(true);
+    p.setShader(SkGradientShader::MakeSweep(100, 100, colors, nullptr, SK_ARRAY_COUNT(colors),
+                                            SkGradientShader::kInterpolateColorsInPremul_Flag,
+                                            nullptr));
+
+    SkPictureRecorder recorder;
+    recorder.beginRecording(200, 200)->drawPaint(p);
+
+    p.setShader(SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
+                                            SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode,
+                                            nullptr, nullptr));
+    canvas->drawColor(SK_ColorWHITE);
+    canvas->drawPaint(p);
+}
diff --git a/gm/circles.cpp b/gm/circles.cpp
index 5c53fe2..9d2eca5 100644
--- a/gm/circles.cpp
+++ b/gm/circles.cpp
@@ -150,7 +150,7 @@
         SkPaint giantPaint;
         giantPaint.setAntiAlias(true);
         giantPaint.setColor(0x80808080);
-        canvas->drawCircle(giantCenter.fX, giantCenter.fY, giantRadius, giantPaint);
+        canvas->drawCircle(giantCenter, giantRadius, giantPaint);
 
         SkRandom rand;
         canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
diff --git a/gm/coloremoji.cpp b/gm/coloremoji.cpp
index f871e53..7626f9a 100644
--- a/gm/coloremoji.cpp
+++ b/gm/coloremoji.cpp
@@ -88,7 +88,7 @@
             paint.setTextSize(textSize);
             paint.getFontMetrics(&metrics);
             y += -metrics.fAscent;
-            canvas->drawText(text, strlen(text), 10, y, paint);
+            canvas->drawString(text, 10, y, paint);
             y += metrics.fDescent + metrics.fLeading;
         }
 
@@ -124,7 +124,7 @@
                             shaderPaint.setTextSize(30);
                             shaderPaint.getFontMetrics(&metrics);
                             y += -metrics.fAscent;
-                            canvas->drawText(text, strlen(text), 380, y, shaderPaint);
+                            canvas->drawString(text, 380, y, shaderPaint);
                             y += metrics.fDescent + metrics.fLeading;
                         }
                     }
@@ -162,10 +162,10 @@
             canvas->save();
             canvas->drawRect(clipRect, clipHairline);
             paint.setAlpha(0x20);
-            canvas->drawText(text, strlen(text), 0, 0, paint);
+            canvas->drawString(text, 0, 0, paint);
             canvas->clipRect(clipRect);
             paint.setAlpha(0xFF);
-            canvas->drawText(text, strlen(text), 0, 0, paint);
+            canvas->drawString(text, 0, 0, paint);
             canvas->restore();
             canvas->translate(0, SkIntToScalar(25));
         }
diff --git a/gm/coloremoji_blendmodes.cpp b/gm/coloremoji_blendmodes.cpp
new file mode 100644
index 0000000..ee3db76
--- /dev/null
+++ b/gm/coloremoji_blendmodes.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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.h"
+#include "sk_tool_utils.h"
+#include "SkBitmap.h"
+#include "SkGradientShader.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+
+namespace skiagm {
+
+static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
+class ColorEmojiBlendModesGM : public GM {
+public:
+    const static int W = 64;
+    const static int H = 64;
+    ColorEmojiBlendModesGM() {}
+
+protected:
+    void onOnceBeforeDraw() override {
+        const SkColor colors[] = {
+            SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE,
+            SK_ColorMAGENTA, SK_ColorCYAN, SK_ColorYELLOW
+        };
+        SkMatrix local;
+        local.setRotate(180);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setShader(SkGradientShader::MakeSweep(0, 0, colors, nullptr, SK_ARRAY_COUNT(colors),
+                                                    0, &local));
+
+        sk_sp<SkTypeface> orig(sk_tool_utils::create_portable_typeface("serif",
+                                   SkFontStyle::FromOldStyle(SkTypeface::kBold)));
+        if (nullptr == orig) {
+            orig = SkTypeface::MakeDefault();
+        }
+        fColorType = sk_tool_utils::emoji_typeface();
+
+        fBG.installPixels(SkImageInfo::Make(2, 2, kARGB_4444_SkColorType,
+                                            kOpaque_SkAlphaType), gData, 4);
+    }
+
+    virtual SkString onShortName() override {
+        SkString name("coloremoji_blendmodes");
+        name.append(sk_tool_utils::platform_os_emoji());
+        return name;
+    }
+
+    virtual SkISize onISize() override {
+        return SkISize::Make(400, 640);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) override {
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+        const SkBlendMode gModes[] = {
+            SkBlendMode::kClear,
+            SkBlendMode::kSrc,
+            SkBlendMode::kDst,
+            SkBlendMode::kSrcOver,
+            SkBlendMode::kDstOver,
+            SkBlendMode::kSrcIn,
+            SkBlendMode::kDstIn,
+            SkBlendMode::kSrcOut,
+            SkBlendMode::kDstOut,
+            SkBlendMode::kSrcATop,
+            SkBlendMode::kDstATop,
+
+            SkBlendMode::kXor,
+            SkBlendMode::kPlus,
+            SkBlendMode::kModulate,
+            SkBlendMode::kScreen,
+            SkBlendMode::kOverlay,
+            SkBlendMode::kDarken,
+            SkBlendMode::kLighten,
+            SkBlendMode::kColorDodge,
+            SkBlendMode::kColorBurn,
+            SkBlendMode::kHardLight,
+            SkBlendMode::kSoftLight,
+            SkBlendMode::kDifference,
+            SkBlendMode::kExclusion,
+            SkBlendMode::kMultiply,
+            SkBlendMode::kHue,
+            SkBlendMode::kSaturation,
+            SkBlendMode::kColor,
+            SkBlendMode::kLuminosity,
+        };
+
+        const SkScalar w = SkIntToScalar(W);
+        const SkScalar h = SkIntToScalar(H);
+        SkMatrix m;
+        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+        auto s = SkShader::MakeBitmapShader(fBG, SkShader::kRepeat_TileMode,
+                                            SkShader::kRepeat_TileMode, &m);
+
+        SkPaint labelP;
+        labelP.setAntiAlias(true);
+        sk_tool_utils::set_portable_typeface(&labelP);
+        labelP.setTextAlign(SkPaint::kCenter_Align);
+
+        SkPaint textP;
+        textP.setAntiAlias(true);
+        textP.setTypeface(fColorType);
+        textP.setTextSize(SkIntToScalar(70));
+
+        const int W = 5;
+
+        SkScalar x0 = 0;
+        SkScalar y0 = 0;
+        SkScalar x = x0, y = y0;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+            SkRect r;
+            r.set(x, y, x+w, y+h);
+
+            SkPaint p;
+            p.setStyle(SkPaint::kFill_Style);
+            p.setShader(s);
+            canvas->drawRect(r, p);
+
+            r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+            p.setStyle(SkPaint::kStroke_Style);
+            p.setShader(nullptr);
+            canvas->drawRect(r, p);
+
+            {
+                SkAutoCanvasRestore arc(canvas, true);
+                canvas->clipRect(r);
+                textP.setBlendMode(gModes[i]);
+                textP.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+                const char* text = sk_tool_utils::emoji_sample_text();
+                SkUnichar unichar = SkUTF8_ToUnichar(text);
+                canvas->drawText(&unichar, 4, x+ w/10.f, y + 7.f*h/8.f, textP);
+            }
+#if 1
+            const char* label = SkBlendMode_Name(gModes[i]);
+            canvas->drawString(label, x + w/2, y - labelP.getTextSize()/2, labelP);
+#endif
+            x += w + SkIntToScalar(10);
+            if ((i % W) == W - 1) {
+                x = x0;
+                y += h + SkIntToScalar(30);
+            }
+        }
+    }
+
+private:
+    SkBitmap            fBG;
+    sk_sp<SkTypeface>   fColorType;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ColorEmojiBlendModesGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/colorfilterimagefilter.cpp b/gm/colorfilterimagefilter.cpp
index a2e20d3..5ef6379 100644
--- a/gm/colorfilterimagefilter.cpp
+++ b/gm/colorfilterimagefilter.cpp
@@ -186,18 +186,24 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+#include "SkGradientShader.h"
 template <typename T> class SkTRefArray : public SkTDArray<T> {
 public:
     ~SkTRefArray() { this->unrefAll(); }
 };
 
-DEF_SIMPLE_GM(colorfiltershader, canvas, 610, 450) {
+DEF_SIMPLE_GM(colorfiltershader, canvas, 610, 610) {
     SkTArray<sk_sp<SkColorFilter>> filters;
     sk_gm_get_colorfilters(&filters);
 
     SkTRefArray<SkShader*> shaders;
     sk_gm_get_shaders(&shaders);
 
+    const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+    *shaders.append() = SkGradientShader::MakeTwoPointConical({0, 0}, 50, {0, 0}, 150,
+                                                              colors, nullptr, 2,
+                                                              SkShader::kClamp_TileMode).release();
+
     SkPaint paint;
     SkRect r = SkRect::MakeWH(120, 120);
 
diff --git a/gm/colorspacexform.cpp b/gm/colorspacexform.cpp
index 4657f6a..eb98b01 100644
--- a/gm/colorspacexform.cpp
+++ b/gm/colorspacexform.cpp
@@ -10,6 +10,7 @@
 #include "SkColorSpace_Base.h"
 #include "SkColorSpaceXform.h"
 #include "SkRect.h"
+#include "SkShader.h"
 
 class ColorSpaceXformGM : public skiagm::GM {
 public:
diff --git a/gm/colortype.cpp b/gm/colortype.cpp
deleted file mode 100644
index aa8aca7..0000000
--- a/gm/colortype.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "gm.h"
-#include "sk_tool_utils.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "../src/fonts/SkGScalerContext.h"
-
-class ColorTypeGM : public skiagm::GM {
-public:
-    ColorTypeGM() {}
-
-protected:
-    void onOnceBeforeDraw() override {
-        const SkColor colors[] = {
-            SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE,
-            SK_ColorMAGENTA, SK_ColorCYAN, SK_ColorYELLOW
-        };
-        SkMatrix local;
-        local.setRotate(180);
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setShader(SkGradientShader::MakeSweep(0, 0, colors, nullptr, SK_ARRAY_COUNT(colors),
-                                                    0, &local));
-
-        sk_sp<SkTypeface> orig(sk_tool_utils::create_portable_typeface(
-                                   "serif", SkFontStyle::FromOldStyle(SkTypeface::kBold)));
-        if (nullptr == orig) {
-            orig = SkTypeface::MakeDefault();
-        }
-        fColorType = sk_make_sp<SkGTypeface>(std::move(orig), paint);
-    }
-
-    SkString onShortName() override {
-        return SkString("colortype");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(640, 480);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setTypeface(fColorType);
-
-        for (SkScalar size = 10; size <= 100; size += 10) {
-            paint.setTextSize(size);
-            canvas->translate(0, paint.getFontMetrics(nullptr));
-            canvas->drawText("Hamburgefons", 12, 10, 10, paint);
-        }
-    }
-
-private:
-    sk_sp<SkTypeface> fColorType;
-
-    typedef skiagm::GM INHERITED;
-};
-
-DEF_GM(return new ColorTypeGM;)
diff --git a/gm/colortypexfermode.cpp b/gm/colortypexfermode.cpp
deleted file mode 100644
index 9a22c8d..0000000
--- a/gm/colortypexfermode.cpp
+++ /dev/null
@@ -1,155 +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.h"
-#include "sk_tool_utils.h"
-#include "SkBitmap.h"
-#include "SkGradientShader.h"
-#include "SkShader.h"
-#include "../src/fonts/SkGScalerContext.h"
-
-namespace skiagm {
-
-static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
-
-class ColorTypeXfermodeGM : public GM {
-public:
-    const static int W = 64;
-    const static int H = 64;
-    ColorTypeXfermodeGM() {}
-
-protected:
-    void onOnceBeforeDraw() override {
-        const SkColor colors[] = {
-            SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE,
-            SK_ColorMAGENTA, SK_ColorCYAN, SK_ColorYELLOW
-        };
-        SkMatrix local;
-        local.setRotate(180);
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setShader(SkGradientShader::MakeSweep(0, 0, colors, nullptr, SK_ARRAY_COUNT(colors),
-                                                    0, &local));
-
-        sk_sp<SkTypeface> orig(sk_tool_utils::create_portable_typeface("serif",
-                                   SkFontStyle::FromOldStyle(SkTypeface::kBold)));
-        if (nullptr == orig) {
-            orig = SkTypeface::MakeDefault();
-        }
-        fColorType = sk_make_sp<SkGTypeface>(orig, paint);
-
-        fBG.installPixels(SkImageInfo::Make(2, 2, kARGB_4444_SkColorType,
-                                            kOpaque_SkAlphaType), gData, 4);
-    }
-
-    virtual SkString onShortName() override {
-        return SkString("colortype_xfermodes");
-    }
-
-    virtual SkISize onISize() override {
-        return SkISize::Make(400, 640);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) override {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
-
-        const SkBlendMode gModes[] = {
-            SkBlendMode::kClear,
-            SkBlendMode::kSrc,
-            SkBlendMode::kDst,
-            SkBlendMode::kSrcOver,
-            SkBlendMode::kDstOver,
-            SkBlendMode::kSrcIn,
-            SkBlendMode::kDstIn,
-            SkBlendMode::kSrcOut,
-            SkBlendMode::kDstOut,
-            SkBlendMode::kSrcATop,
-            SkBlendMode::kDstATop,
-
-            SkBlendMode::kXor,
-            SkBlendMode::kPlus,
-            SkBlendMode::kModulate,
-            SkBlendMode::kScreen,
-            SkBlendMode::kOverlay,
-            SkBlendMode::kDarken,
-            SkBlendMode::kLighten,
-            SkBlendMode::kColorDodge,
-            SkBlendMode::kColorBurn,
-            SkBlendMode::kHardLight,
-            SkBlendMode::kSoftLight,
-            SkBlendMode::kDifference,
-            SkBlendMode::kExclusion,
-            SkBlendMode::kMultiply,
-            SkBlendMode::kHue,
-            SkBlendMode::kSaturation,
-            SkBlendMode::kColor,
-            SkBlendMode::kLuminosity,
-        };
-
-        const SkScalar w = SkIntToScalar(W);
-        const SkScalar h = SkIntToScalar(H);
-        SkMatrix m;
-        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-        auto s = SkShader::MakeBitmapShader(fBG, SkShader::kRepeat_TileMode,
-                                            SkShader::kRepeat_TileMode, &m);
-
-        SkPaint labelP;
-        labelP.setAntiAlias(true);
-        sk_tool_utils::set_portable_typeface(&labelP);
-        labelP.setTextAlign(SkPaint::kCenter_Align);
-
-        SkPaint textP;
-        textP.setAntiAlias(true);
-        textP.setTypeface(fColorType);
-        textP.setTextSize(SkIntToScalar(70));
-
-        const int W = 5;
-
-        SkScalar x0 = 0;
-        SkScalar y0 = 0;
-        SkScalar x = x0, y = y0;
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
-            SkRect r;
-            r.set(x, y, x+w, y+h);
-
-            SkPaint p;
-            p.setStyle(SkPaint::kFill_Style);
-            p.setShader(s);
-            canvas->drawRect(r, p);
-
-            r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
-            p.setStyle(SkPaint::kStroke_Style);
-            p.setShader(nullptr);
-            canvas->drawRect(r, p);
-
-            textP.setBlendMode(gModes[i]);
-            canvas->drawText("H", 1, x+ w/10.f, y + 7.f*h/8.f, textP);
-#if 1
-            const char* label = SkBlendMode_Name(gModes[i]);
-            canvas->drawText(label, strlen(label),
-                             x + w/2, y - labelP.getTextSize()/2, labelP);
-#endif
-            x += w + SkIntToScalar(10);
-            if ((i % W) == W - 1) {
-                x = x0;
-                y += h + SkIntToScalar(30);
-            }
-        }
-    }
-
-private:
-    SkBitmap            fBG;
-    sk_sp<SkTypeface>   fColorType;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new ColorTypeXfermodeGM; }
-static GMRegistry reg(MyFactory);
-
-}
diff --git a/gm/colorwheel.cpp b/gm/colorwheel.cpp
index a1ca021..b938cd5 100644
--- a/gm/colorwheel.cpp
+++ b/gm/colorwheel.cpp
@@ -46,17 +46,17 @@
 
     canvas->clear(sk_tool_utils::color_to_565(SK_ColorLTGRAY));
     paint.setColor(SK_ColorRED);
-    canvas->drawText("R", 1, 8.0f, 20.0f, paint);
+    canvas->drawString("R", 8.0f, 20.0f, paint);
     paint.setColor(SK_ColorGREEN);
-    canvas->drawText("G", 1, 24.0f, 20.0f, paint);
+    canvas->drawString("G", 24.0f, 20.0f, paint);
     paint.setColor(SK_ColorBLUE);
-    canvas->drawText("B", 1, 40.0f, 20.0f, paint);
+    canvas->drawString("B", 40.0f, 20.0f, paint);
     paint.setColor(SK_ColorCYAN);
-    canvas->drawText("C", 1, 56.0f, 20.0f, paint);
+    canvas->drawString("C", 56.0f, 20.0f, paint);
     paint.setColor(SK_ColorMAGENTA);
-    canvas->drawText("M", 1, 72.0f, 20.0f, paint);
+    canvas->drawString("M", 72.0f, 20.0f, paint);
     paint.setColor(SK_ColorYELLOW);
-    canvas->drawText("Y", 1, 88.0f, 20.0f, paint);
+    canvas->drawString("Y", 88.0f, 20.0f, paint);
     paint.setColor(SK_ColorBLACK);
-    canvas->drawText("K", 1, 104.0f, 20.0f, paint);
+    canvas->drawString("K", 104.0f, 20.0f, paint);
 }
diff --git a/gm/complexclip.cpp b/gm/complexclip.cpp
index 2968715..ae9c4db 100644
--- a/gm/complexclip.cpp
+++ b/gm/complexclip.cpp
@@ -151,15 +151,14 @@
                 SkScalar txtX = SkIntToScalar(45);
                 paint.setColor(gClipAColor);
                 const char* aTxt = doInvA ? "InvA " : "A ";
-                canvas->drawText(aTxt, strlen(aTxt), txtX, SkIntToScalar(220), paint);
+                canvas->drawString(aTxt, txtX, SkIntToScalar(220), paint);
                 txtX += paint.measureText(aTxt, strlen(aTxt));
                 paint.setColor(SK_ColorBLACK);
-                canvas->drawText(gOps[op].fName, strlen(gOps[op].fName),
-                                    txtX, SkIntToScalar(220), paint);
+                canvas->drawString(gOps[op].fName, txtX, SkIntToScalar(220), paint);
                 txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName));
                 paint.setColor(gClipBColor);
                 const char* bTxt = doInvB ? "InvB " : "B ";
-                canvas->drawText(bTxt, strlen(bTxt), txtX, SkIntToScalar(220), paint);
+                canvas->drawString(bTxt, txtX, SkIntToScalar(220), paint);
 
                 canvas->translate(SkIntToScalar(250),0);
             }
diff --git a/gm/complexclip3.cpp b/gm/complexclip3.cpp
index 47b80ab..7c5c672 100644
--- a/gm/complexclip3.cpp
+++ b/gm/complexclip3.cpp
@@ -103,8 +103,7 @@
                                                    doAAB ? "A" : "B",
                                                    doInvB ? "I" : "N");
 
-                        canvas->drawText(str.c_str(), strlen(str.c_str()), txtX, SkIntToScalar(130),
-                                         paint);
+                        canvas->drawString(str.c_str(), txtX, SkIntToScalar(130), paint);
                         if (doInvB) {
                             canvas->translate(SkIntToScalar(150),0);
                         } else {
diff --git a/gm/composeshader.cpp b/gm/composeshader.cpp
index ff525f4..a6210c9 100644
--- a/gm/composeshader.cpp
+++ b/gm/composeshader.cpp
@@ -10,6 +10,7 @@
 #include "SkCanvas.h"
 #include "SkGradientShader.h"
 #include "SkGraphics.h"
+#include "SkImage.h"
 #include "SkShader.h"
 #include "SkString.h"
 #include "SkTDArray.h"
@@ -247,13 +248,10 @@
             SkColorType::kAlpha_8_SkColorType, kPremul_SkAlphaType);
     skMask.installPixels(imageInfo, dst8Storage.begin(), width, nullptr, nullptr, nullptr);
     sk_sp<SkImage> skSrc = SkImage::MakeFromBitmap(skBitmap);
-    sk_sp<SkShader> skSrcShader =
-        skSrc->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     sk_sp<SkImage> skMaskImage = SkImage::MakeFromBitmap(skMask);
-    sk_sp<SkShader> skMaskShader = skMaskImage->makeShader(
-        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     paint.setShader(
-        SkShader::MakeComposeShader(skMaskShader, skSrcShader, SkBlendMode::kSrcIn));
+        SkShader::MakeComposeShader(skMaskImage->makeShader(), skSrc->makeShader(),
+                                    SkBlendMode::kSrcIn));
     canvas->drawRect(r, paint);
 }
 
diff --git a/gm/concavepaths.cpp b/gm/concavepaths.cpp
index 890f6c0..fe8a141 100644
--- a/gm/concavepaths.cpp
+++ b/gm/concavepaths.cpp
@@ -159,6 +159,20 @@
     canvas->restore();
 }
 
+// Exercise a case where the intersection is below a bottom edge.
+void test_twist(SkCanvas* canvas, const SkPaint& paint) {
+    SkPath path;
+    canvas->save();
+    path.moveTo(                 0.5,                    6);
+    path.lineTo(5.8070392608642578125, 6.4612660408020019531);
+    path.lineTo(-2.9186885356903076172, 2.811046600341796875);
+    path.lineTo(0.49999994039535522461, -1.4124038219451904297);
+    canvas->translate(420, 220);
+    canvas->scale(10, 10);
+    canvas->drawPath(path, paint);
+    canvas->restore();
+}
+
 // Stairstep with repeated vert (intersection)
 void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
     SkPath path;
@@ -409,6 +423,7 @@
     test_fast_forward(canvas, paint);
     test_hole(canvas, paint);
     test_star(canvas, paint);
+    test_twist(canvas, paint);
     test_inversion_repeat_vertex(canvas, paint);
     test_stairstep(canvas, paint);
     test_stairstep2(canvas, paint);
diff --git a/gm/conicpaths.cpp b/gm/conicpaths.cpp
index d0c6a9f..e0a775a 100644
--- a/gm/conicpaths.cpp
+++ b/gm/conicpaths.cpp
@@ -140,7 +140,7 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     paint.setStyle(SkPaint::kStroke_Style);
-    canvas->drawCircle(c.fX, c.fY, radius, paint);
+    canvas->drawCircle(c, radius, paint);
     SkPath path;
     path.moveTo(288.88884710654133f, -280.26680862609f);
     path.arcTo(0, 0, -39.00216443306411f, 400.6058925796476f, radius);
@@ -156,7 +156,7 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     paint.setStyle(SkPaint::kStroke_Style);
-    canvas->drawCircle(c.fX, c.fY, radius, paint);
+    canvas->drawCircle(c, radius, paint);
 }
 
 DEF_SIMPLE_GM(crbug_640176, canvas, 250, 250) {
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp
index 9f30bed..bbfca00 100644
--- a/gm/constcolorprocessor.cpp
+++ b/gm/constcolorprocessor.cpp
@@ -18,7 +18,7 @@
 #include "SkGradientShader.h"
 #include "effects/GrConstColorProcessor.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 namespace skiagm {
 /**
@@ -109,11 +109,9 @@
                     sk_sp<GrFragmentProcessor> fp(GrConstColorProcessor::Make(color, mode));
 
                     grPaint.addColorFragmentProcessor(std::move(fp));
-
-                    std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                            grPaint.getColor(), viewMatrix, renderRect, nullptr, nullptr));
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                            std::move(grPaint), GrAAType::kNone, std::move(op));
+                    renderTargetContext->priv().testingOnly_addDrawOp(
+                            GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect,
+                                                    nullptr, nullptr, GrAAType::kNone));
 
                     // Draw labels for the input to the processor and the processor to the right of
                     // the test rect. The input label appears above the processor label.
@@ -135,7 +133,7 @@
                     // get the bounds of the text in order to position it
                     labelPaint.measureText(inputLabel.c_str(), inputLabel.size(),
                                            &inputLabelBounds);
-                    canvas->drawText(inputLabel.c_str(), inputLabel.size(),
+                    canvas->drawString(inputLabel,
                                      renderRect.fRight + kPad,
                                      -inputLabelBounds.fTop, labelPaint);
                     // update the bounds to reflect the offset we used to draw it.
@@ -144,7 +142,7 @@
                     SkRect procLabelBounds;
                     labelPaint.measureText(procLabel.c_str(), procLabel.size(),
                                            &procLabelBounds);
-                    canvas->drawText(procLabel.c_str(), procLabel.size(),
+                    canvas->drawString(procLabel,
                                      renderRect.fRight + kPad,
                                      inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop,
                                      labelPaint);
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index 0ce7ba7..8ace4d0 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -46,8 +46,8 @@
 
     const char* name() const override { return "PolyBoundsOp"; }
 
-    static std::unique_ptr<GrMeshDrawOp> Make(const SkRect& rect, GrColor color) {
-        return std::unique_ptr<GrMeshDrawOp>(new PolyBoundsOp(rect, color));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(const SkRect& rect, GrColor color) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new PolyBoundsOp(rect, color));
     }
 
 private:
@@ -71,7 +71,7 @@
 
         fRect.toQuad(verts);
 
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     SkRect fRect;
@@ -183,9 +183,10 @@
                 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
                 grPaint.addCoverageFragmentProcessor(std::move(fp));
 
-                std::unique_ptr<GrMeshDrawOp> op = PolyBoundsOp::Make(p.getBounds(), 0xff000000);
+                std::unique_ptr<GrLegacyMeshDrawOp> op =
+                        PolyBoundsOp::Make(p.getBounds(), 0xff000000);
 
-                renderTargetContext->priv().testingOnly_addMeshDrawOp(
+                renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
                         std::move(grPaint), GrAAType::kNone, std::move(op));
 
                 x += SkScalarCeilToScalar(path->getBounds().width() + kDX);
@@ -223,9 +224,9 @@
                 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
                 grPaint.addCoverageFragmentProcessor(std::move(fp));
 
-                std::unique_ptr<GrMeshDrawOp> op = PolyBoundsOp::Make(rect, 0xff000000);
+                std::unique_ptr<GrLegacyMeshDrawOp> op = PolyBoundsOp::Make(rect, 0xff000000);
 
-                renderTargetContext->priv().testingOnly_addMeshDrawOp(
+                renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
                         std::move(grPaint), GrAAType::kNone, std::move(op));
 
                 x += SkScalarCeilToScalar(rect.width() + kDX);
diff --git a/gm/copyTo4444.cpp b/gm/copyTo4444.cpp
index 64f2c60..97e6f0a 100644
--- a/gm/copyTo4444.cpp
+++ b/gm/copyTo4444.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "gm.h"
+#include "sk_tool_utils.h"
 
 #include "Resources.h"
 #include "SkCanvas.h"
@@ -26,18 +27,20 @@
     }
 
     virtual SkISize onISize() {
-        return SkISize::Make(1024, 512);
+        return SkISize::Make(360, 180);
     }
 
     virtual void onDraw(SkCanvas* canvas) {
         SkBitmap bm, bm4444;
-        if (!GetResourceAsBitmap("mandrill_512.png", &bm)) {
+        if (!GetResourceAsBitmap("dog.jpg", &bm)) {
             SkDebugf("Could not decode the file. Did you forget to set the "
                      "resourcePath?\n");
             return;
         }
         canvas->drawBitmap(bm, 0, 0);
-        SkAssertResult(bm.copyTo(&bm4444, kARGB_4444_SkColorType));
+
+        // This should dither or we will see artifacts in the background of the image.
+        SkAssertResult(sk_tool_utils::copy_to(&bm4444, kARGB_4444_SkColorType, bm));
         canvas->drawBitmap(bm4444, SkIntToScalar(bm.width()), 0);
     }
 
diff --git a/gm/croppedrects.cpp b/gm/croppedrects.cpp
index 0ea265c..006e3d3 100644
--- a/gm/croppedrects.cpp
+++ b/gm/croppedrects.cpp
@@ -47,8 +47,7 @@
         srcCanvas->drawRect(kSrcImageClip.makeInset(kStrokeWidth / 2, kStrokeWidth / 2), stroke);
 
         fSrcImage = srcSurface->makeImageSnapshot();
-        fSrcImageShader = fSrcImage->makeShader(SkShader::kClamp_TileMode,
-                                                SkShader::kClamp_TileMode);
+        fSrcImageShader = fSrcImage->makeShader();
     }
 
     void onDraw(SkCanvas* canvas) override {
diff --git a/gm/crosscontextimage.cpp b/gm/crosscontextimage.cpp
new file mode 100644
index 0000000..cf973db
--- /dev/null
+++ b/gm/crosscontextimage.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "gm.h"
+#include "Resources.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "SkImage.h"
+
+DEF_SIMPLE_GM(cross_context_image, canvas, 512 + 512 + 30, 512 + 128 + 30) {
+    GrContext* context = canvas->getGrContext();
+    if (!context) {
+        skiagm::GM::DrawGpuOnlyMessage(canvas);
+        return;
+    }
+
+    sk_sp<SkData> encodedData = GetResourceAsData("mandrill_512.png");
+
+    sk_sp<SkImage> encodedImage = SkImage::MakeFromEncoded(encodedData);
+    canvas->drawImage(encodedImage, 10, 10);
+
+    sk_sp<SkImage> crossContextImage = SkImage::MakeCrossContextFromEncoded(
+        context, encodedData, false, canvas->imageInfo().colorSpace());
+    canvas->drawImage(crossContextImage, 512 + 30, 10);
+
+    SkIRect subset = SkIRect::MakeXYWH(256 - 64, 256 - 64, 128, 128);
+    sk_sp<SkImage> encodedSubset = encodedImage->makeSubset(subset);
+    sk_sp<SkImage> crossContextSubset = crossContextImage->makeSubset(subset);
+
+    canvas->drawImage(encodedSubset, 10, 512 + 30);
+    canvas->drawImage(crossContextSubset, 512 + 30, 512 + 30);
+}
+
+#endif
diff --git a/gm/cubicpaths.cpp b/gm/cubicpaths.cpp
index fd2156c..6bbcd90 100644
--- a/gm/cubicpaths.cpp
+++ b/gm/cubicpaths.cpp
@@ -202,10 +202,10 @@
         titlePaint.setTextSize(15 * SK_Scalar1);
         const char title[] = "Cubic Drawn Into Rectangle Clips With "
                              "Indicated Style, Fill and Linecaps, with stroke width 10";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
@@ -244,18 +244,15 @@
                     labelPaint.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&labelPaint);
                     labelPaint.setTextSize(10 * SK_Scalar1);
-                    canvas->drawText(gStyles[style].fName,
-                                        strlen(gStyles[style].fName),
-                                        0, rect.height() + 12 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gFills[fill].fName,
-                                        strlen(gFills[fill].fName),
-                                        0, rect.height() + 24 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gCaps[cap].fName,
-                                        strlen(gCaps[cap].fName),
-                                        0, rect.height() + 36 * SK_Scalar1,
-                                        labelPaint);
+                    canvas->drawString(gStyles[style].fName,
+                                       0, rect.height() + 12 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gFills[fill].fName,
+                                       0, rect.height() + 24 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gCaps[cap].fName,
+                                       0, rect.height() + 36 * SK_Scalar1,
+                                       labelPaint);
                 }
                 canvas->restore();
             }
@@ -347,10 +344,10 @@
         titlePaint.setTextSize(15 * SK_Scalar1);
         const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
                              "Indicated Style, Fill and Linecaps, with stroke width 10";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
@@ -389,18 +386,15 @@
                     labelPaint.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&labelPaint);
                     labelPaint.setTextSize(10 * SK_Scalar1);
-                    canvas->drawText(gStyles[style].fName,
-                                        strlen(gStyles[style].fName),
-                                        0, rect.height() + 12 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gFills[fill].fName,
-                                        strlen(gFills[fill].fName),
-                                        0, rect.height() + 24 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gCaps[cap].fName,
-                                        strlen(gCaps[cap].fName),
-                                        0, rect.height() + 36 * SK_Scalar1,
-                                        labelPaint);
+                    canvas->drawString(gStyles[style].fName,
+                                       0, rect.height() + 12 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gFills[fill].fName,
+                                       0, rect.height() + 24 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gCaps[cap].fName,
+                                       0, rect.height() + 36 * SK_Scalar1,
+                                       labelPaint);
                 }
                 canvas->restore();
             }
diff --git a/gm/dashing.cpp b/gm/dashing.cpp
index 13d45cb..b527fbf 100644
--- a/gm/dashing.cpp
+++ b/gm/dashing.cpp
@@ -543,7 +543,7 @@
     sk_tool_utils::set_portable_typeface(&p);
     const SkScalar intervals[] = { 12, 12 };
     p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
-    canvas->drawText("Sausages", 8, 10, 90, p);
+    canvas->drawString("Sausages", 10, 90, p);
     canvas->drawLine(8, 120, 456, 120, p);
 }
 
diff --git a/gm/deferredtextureimage.cpp b/gm/deferredtextureimage.cpp
index c88b878..9c1fe6a 100644
--- a/gm/deferredtextureimage.cpp
+++ b/gm/deferredtextureimage.cpp
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "gm.h"
+#include "SkImage.h"
 #include "SkMipMap.h"
 #include "Resources.h"
 
diff --git a/gm/degeneratesegments.cpp b/gm/degeneratesegments.cpp
index 25da045..9de66ea 100644
--- a/gm/degeneratesegments.cpp
+++ b/gm/degeneratesegments.cpp
@@ -294,10 +294,10 @@
         const char title[] = "Random Paths Drawn Into Rectangle Clips With "
                              "Indicated Style, Fill and Linecaps, "
                              "with Stroke width 6";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1);
@@ -351,38 +351,30 @@
                 labelPaint.setAntiAlias(true);
                 sk_tool_utils::set_portable_typeface(&labelPaint);
                 labelPaint.setTextSize(10 * SK_Scalar1);
-                canvas->drawText(style.fName,
-                                 strlen(style.fName),
-                                 0, rect.height() + 12 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(fill.fName,
-                                 strlen(fill.fName),
-                                 0, rect.height() + 24 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(cap.fName,
-                                 strlen(cap.fName),
-                                 0, rect.height() + 36 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(gSegmentNames[s1],
-                                 strlen(gSegmentNames[s1]),
-                                 0, rect.height() + 48 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(gSegmentNames[s2],
-                                 strlen(gSegmentNames[s2]),
-                                 0, rect.height() + 60 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(gSegmentNames[s3],
-                                 strlen(gSegmentNames[s3]),
-                                 0, rect.height() + 72 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(gSegmentNames[s4],
-                                 strlen(gSegmentNames[s4]),
-                                 0, rect.height() + 84 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(gSegmentNames[s5],
-                                 strlen(gSegmentNames[s5]),
-                                 0, rect.height() + 96 * SK_Scalar1,
-                                 labelPaint);
+                canvas->drawString(style.fName,
+                                   0, rect.height() + 12 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(fill.fName,
+                                   0, rect.height() + 24 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(cap.fName,
+                                   0, rect.height() + 36 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(gSegmentNames[s1],
+                                   0, rect.height() + 48 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(gSegmentNames[s2],
+                                   0, rect.height() + 60 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(gSegmentNames[s3],
+                                   0, rect.height() + 72 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(gSegmentNames[s4],
+                                   0, rect.height() + 84 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(gSegmentNames[s5],
+                                   0, rect.height() + 96 * SK_Scalar1,
+                                   labelPaint);
             }
             canvas->restore();
         }
diff --git a/gm/dftext.cpp b/gm/dftext.cpp
index fb69f9b..6067327 100644
--- a/gm/dftext.cpp
+++ b/gm/dftext.cpp
@@ -187,7 +187,7 @@
         if (fEmojiTypeface) {
             paint.setTypeface(fEmojiTypeface);
             paint.setTextSize(SkIntToScalar(19));
-            canvas->drawText(fEmojiText, strlen(fEmojiText), 670, 90, paint);
+            canvas->drawString(fEmojiText, 670, 90, paint);
         }
 #if SK_SUPPORT_GPU
         // render offscreen buffer
diff --git a/gm/distantclip.cpp b/gm/distantclip.cpp
index 24fbaca..fa75265 100644
--- a/gm/distantclip.cpp
+++ b/gm/distantclip.cpp
@@ -8,6 +8,7 @@
 
 #include "gm.h"
 #include "SkCanvas.h"
+#include "SkPath.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
 
diff --git a/gm/downsamplebitmap.cpp b/gm/downsamplebitmap.cpp
index ba1edc9..fefc94b 100644
--- a/gm/downsamplebitmap.cpp
+++ b/gm/downsamplebitmap.cpp
@@ -127,13 +127,13 @@
           paint.setTextSize(fTextSize);
 
           setTypeface(&paint, "serif", SkFontStyle());
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 1.2f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 1.2f*fTextSize, paint);
           setTypeface(&paint, "serif", SkFontStyle::FromOldStyle(SkTypeface::kBold));
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 2.4f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 2.4f*fTextSize, paint);
           setTypeface(&paint, "serif", SkFontStyle::FromOldStyle(SkTypeface::kItalic));
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 3.6f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 3.6f*fTextSize, paint);
           setTypeface(&paint, "serif", SkFontStyle::FromOldStyle(SkTypeface::kBoldItalic));
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 4.8f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 4.8f*fTextSize, paint);
       }
   private:
       typedef DownsampleBitmapGM INHERITED;
diff --git a/gm/drawatlas.cpp b/gm/drawatlas.cpp
index 2526177..de44b6d 100644
--- a/gm/drawatlas.cpp
+++ b/gm/drawatlas.cpp
@@ -169,4 +169,64 @@
     canvas->drawPath(path, paint);
 }
 
+#include "Resources.h"
+#include "SkColorFilter.h"
+#include "SkVertices.h"
 
+static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
+                                       SkColor color) {
+    SkPoint pos[4];
+    r.toQuad(pos);
+    SkColor colors[4] = { color, color, color, color };
+    return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
+                                pos, pos, colors);
+}
+
+/*
+ *  drawAtlas and drawVertices have several things in common:
+ *  - can create compound "shaders", combining texture and colors
+ *      - these are combined via an explicit blendmode
+ *  - like drawImage, they only respect parts of the paint
+ *      - colorfilter, imagefilter, blendmode, alpha
+ *
+ *  This GM produces a series of pairs of images (atlas | vertices).
+ *  Each pair should look the same, and each set shows a different combination
+ *  of alpha | colorFilter | mode
+ */
+DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
+    const SkRect tex = SkRect::MakeWH(128, 128);
+    const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
+    const SkColor color = 0x884488CC;
+
+    auto image = GetResourceAsImage("mandrill_128.png");
+    auto verts = make_vertices(image, tex, color);
+    const sk_sp<SkColorFilter> filters[] = {
+        nullptr,
+        SkColorFilter::MakeModeFilter(0xFF00FF88, SkBlendMode::kModulate),
+    };
+    const SkBlendMode modes[] = {
+        SkBlendMode::kSrcOver,
+        SkBlendMode::kPlus,
+    };
+
+    canvas->translate(10, 10);
+    SkPaint paint;
+    for (SkBlendMode mode : modes) {
+        for (int alpha : { 0xFF, 0x7F }) {
+            paint.setAlpha(alpha);
+            canvas->save();
+            for (auto cf : filters) {
+                paint.setColorFilter(cf);
+                canvas->drawAtlas(image, &xform, &tex, &color, 1,
+                                  mode, &tex, &paint);
+                canvas->translate(128, 0);
+                paint.setShader(image->makeShader());
+                canvas->drawVertices(verts, mode, paint);
+                paint.setShader(nullptr);
+                canvas->translate(145, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, 145);
+        }
+    }
+}
diff --git a/gm/drawatlascolor.cpp b/gm/drawatlascolor.cpp
index de64cab..548641b 100644
--- a/gm/drawatlascolor.cpp
+++ b/gm/drawatlascolor.cpp
@@ -138,7 +138,7 @@
 
         for (int i = 0; i < numModes; ++i) {
             const char* label = SkBlendMode_Name(gModes[i]);
-            canvas->drawText(label, strlen(label),
+            canvas->drawString(label,
                              i*(target.width()+kPad)+kPad, SkIntToScalar(kTextPad),
                              textP);
         }
diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp
index bad5e2a..2cd1b2f 100644
--- a/gm/drawbitmaprect.cpp
+++ b/gm/drawbitmaprect.cpp
@@ -28,7 +28,6 @@
             p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
         }
     }
-    bm.unlockPixels();
     return bm;
 }
 
@@ -181,7 +180,7 @@
         sk_tool_utils::set_portable_typeface(&blackPaint);
         SkString title;
         title.printf("Bitmap size: %d x %d", gBmpSize, gBmpSize);
-        canvas->drawText(title.c_str(), title.size(), 0,
+        canvas->drawString(title, 0,
                          titleHeight, blackPaint);
 
         canvas->translate(0, SK_Scalar1 * kPadY / 2  + titleHeight);
@@ -200,7 +199,7 @@
                 blackPaint.setTextSize(SK_Scalar1 * 10);
                 SkScalar baseline = dstRect.height() +
                                     blackPaint.getTextSize() + SK_Scalar1 * 3;
-                canvas->drawText(label.c_str(), label.size(),
+                canvas->drawString(label,
                                     0, baseline,
                                     blackPaint);
                 blackPaint.setStyle(SkPaint::kStroke_Style);
diff --git a/gm/drawlooper.cpp b/gm/drawlooper.cpp
index c4052e3..07ec8d8 100644
--- a/gm/drawlooper.cpp
+++ b/gm/drawlooper.cpp
@@ -43,7 +43,7 @@
 
         canvas->drawCircle(50, 50, 30, paint);
         canvas->drawRect({ 150, 50, 200, 100 }, paint);
-        canvas->drawText("Looper", 6, 230, 100, paint);
+        canvas->drawString("Looper", 230, 100, paint);
     }
 
 private:
diff --git a/gm/dropshadowimagefilter.cpp b/gm/dropshadowimagefilter.cpp
index 87cd798..11de727 100644
--- a/gm/dropshadowimagefilter.cpp
+++ b/gm/dropshadowimagefilter.cpp
@@ -45,7 +45,7 @@
     paint.setTextAlign(SkPaint::kCenter_Align);
     canvas->save();
     canvas->clipRect(r);
-    canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint);
+    canvas->drawString("Text", r.centerX(), r.centerY(), paint);
     canvas->restore();
 }
 
diff --git a/gm/dstreadshuffle.cpp b/gm/dstreadshuffle.cpp
index f6086b1..aaef61d 100644
--- a/gm/dstreadshuffle.cpp
+++ b/gm/dstreadshuffle.cpp
@@ -85,7 +85,7 @@
                 paint->setTextSize(100.f);
                 paint->setFakeBoldText(true);
                 sk_tool_utils::set_portable_typeface(paint);
-                canvas->drawText(text, strlen(text), 0.f, 100.f, *paint);
+                canvas->drawString(text, 0.f, 100.f, *paint);
             }
             default:
                 break;
@@ -115,7 +115,7 @@
         rot.postTranslate(3.f, 0);
         for (int i = 0; i < 12; ++i) {
             hairPaint.setColor(GetColor(&colorRandom));
-            canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, hairPaint);
+            canvas->drawLine(pts[0], pts[1], hairPaint);
             rot.mapPoints(pts, 2);
         }
     }
diff --git a/gm/emptypath.cpp b/gm/emptypath.cpp
index 56377e7..67da93f 100644
--- a/gm/emptypath.cpp
+++ b/gm/emptypath.cpp
@@ -68,10 +68,10 @@
         titlePaint.setTextSize(15 * SK_Scalar1);
         const char title[] = "Empty Paths Drawn Into Rectangle Clips With "
                              "Indicated Style and Fill";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
@@ -109,14 +109,12 @@
                 labelPaint.setAntiAlias(true);
                 sk_tool_utils::set_portable_typeface(&labelPaint);
                 labelPaint.setTextSize(12 * SK_Scalar1);
-                canvas->drawText(gStyles[style].fName,
-                                 strlen(gStyles[style].fName),
-                                 0, rect.height() + 15 * SK_Scalar1,
-                                 labelPaint);
-                canvas->drawText(gFills[fill].fName,
-                                 strlen(gFills[fill].fName),
-                                 0, rect.height() + 28 * SK_Scalar1,
-                                 labelPaint);
+                canvas->drawString(gStyles[style].fName,
+                                   0, rect.height() + 15 * SK_Scalar1,
+                                   labelPaint);
+                canvas->drawString(gFills[fill].fName,
+                                   0, rect.height() + 28 * SK_Scalar1,
+                                   labelPaint);
             }
         }
         canvas->restore();
diff --git a/gm/encode-alpha-jpeg.cpp b/gm/encode-alpha-jpeg.cpp
new file mode 100644
index 0000000..6686e78
--- /dev/null
+++ b/gm/encode-alpha-jpeg.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "gm.h"
+#include "SkImage.h"
+#include "SkJpegEncoder.h"
+
+#include "Resources.h"
+
+namespace skiagm {
+
+static inline void read_into_pixmap(SkPixmap* dst, SkImageInfo dstInfo, void* dstPixels,
+        sk_sp<SkImage> src) {
+    dst->reset(dstInfo, dstPixels, dstInfo.minRowBytes());
+    src->readPixels(*dst, 0, 0, SkImage::CachingHint::kDisallow_CachingHint);
+}
+
+static inline sk_sp<SkImage> encode_pixmap_and_make_image(const SkPixmap& src,
+        SkJpegEncoder::AlphaOption alphaOption, SkTransferFunctionBehavior blendBehavior) {
+    SkDynamicMemoryWStream dst;
+    SkJpegEncoder::Options options;
+    options.fAlphaOption = alphaOption;
+    options.fBlendBehavior = blendBehavior;
+    SkJpegEncoder::Encode(&dst, src, options);
+    return SkImage::MakeFromEncoded(dst.detachAsData());
+}
+
+class EncodeJpegAlphaOptsGM : public GM {
+public:
+    EncodeJpegAlphaOptsGM() {}
+
+protected:
+    SkString onShortName() override {
+        return SkString("encode-alpha-jpeg");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(400, 200);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        sk_sp<SkImage> srcImg = GetResourceAsImage("rainbow-gradient.png");
+        fStorage.reset(srcImg->width() * srcImg->height() *
+                SkColorTypeBytesPerPixel(kRGBA_F16_SkColorType));
+
+        SkPixmap src;
+        SkImageInfo info = SkImageInfo::MakeN32Premul(srcImg->width(), srcImg->height(),
+                canvas->imageInfo().colorSpace() ? SkColorSpace::MakeSRGB() : nullptr);
+        read_into_pixmap(&src, info, fStorage.get(), srcImg);
+
+        SkTransferFunctionBehavior behavior = canvas->imageInfo().colorSpace() ?
+                SkTransferFunctionBehavior::kRespect : SkTransferFunctionBehavior::kIgnore;
+
+        // Encode 8888 premul.
+        sk_sp<SkImage> img0 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kIgnore,
+                behavior);
+        sk_sp<SkImage> img1 = encode_pixmap_and_make_image(src,
+                SkJpegEncoder::AlphaOption::kBlendOnBlack, behavior);
+        canvas->drawImage(img0, 0.0f, 0.0f);
+        canvas->drawImage(img1, 0.0f, 100.0f);
+
+        // Encode 8888 unpremul
+        info = info.makeAlphaType(kUnpremul_SkAlphaType);
+        read_into_pixmap(&src, info, fStorage.get(), srcImg);
+        img0 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kIgnore, behavior);
+        img1 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kBlendOnBlack,
+                behavior);
+        canvas->drawImage(img0, 100.0f, 0.0f);
+        canvas->drawImage(img1, 100.0f, 100.0f);
+
+        if (canvas->imageInfo().colorSpace()) {
+            // Encode F16 premul
+            info = SkImageInfo::Make(srcImg->width(), srcImg->height(), kRGBA_F16_SkColorType,
+                    kPremul_SkAlphaType, SkColorSpace::MakeSRGBLinear());
+            read_into_pixmap(&src, info, fStorage.get(), srcImg);
+            img0 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kIgnore, behavior);
+            img1 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kBlendOnBlack,
+                    behavior);
+            canvas->drawImage(img0, 200.0f, 0.0f);
+            canvas->drawImage(img1, 200.0f, 100.0f);
+
+            // Encode F16 unpremul
+            info = info.makeAlphaType(kUnpremul_SkAlphaType);
+            read_into_pixmap(&src, info, fStorage.get(), srcImg);
+            img0 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kIgnore, behavior);
+            img1 = encode_pixmap_and_make_image(src, SkJpegEncoder::AlphaOption::kBlendOnBlack,
+                    behavior);
+            canvas->drawImage(img0, 300.0f, 0.0f);
+            canvas->drawImage(img1, 300.0f, 100.0f);
+        }
+    }
+
+private:
+    SkAutoTMalloc<uint8_t> fStorage;
+
+    typedef GM INHERITED;
+};
+
+DEF_GM( return new EncodeJpegAlphaOptsGM; )
+
+};
diff --git a/gm/encode-platform.cpp b/gm/encode-platform.cpp
index ca3cda1..dd7ca6e 100644
--- a/gm/encode-platform.cpp
+++ b/gm/encode-platform.cpp
@@ -10,8 +10,12 @@
 #include "Resources.h"
 #include "SkCanvas.h"
 #include "SkData.h"
+#include "SkImage.h"
 #include "SkImageEncoderPriv.h"
+#include "SkJpegEncoder.h"
+#include "SkPngEncoder.h"
 #include "SkUnPreMultiply.h"
+#include "SkWebpEncoder.h"
 
 namespace skiagm {
 
@@ -23,7 +27,6 @@
     SkBitmap tmp;
     GetResourceAsBitmap("yellow_rose.png", &tmp);
     tmp.extractSubset(bitmap, SkIRect::MakeWH(256, 256));
-    bitmap->lockPixels();
 }
 
 static void make_unpremul_256(SkBitmap* bitmap) {
@@ -57,7 +60,6 @@
 #endif
 
 static sk_sp<SkData> encode_data(SkEncodedImageFormat type, const SkBitmap& bitmap) {
-    SkAutoLockPixels autoLockPixels(bitmap);
     SkPixmap src;
     if (!bitmap.peekPixels(&src)) {
         return nullptr;
@@ -69,13 +71,22 @@
         return SkEncodeImageWithWIC(&buf, src, type, 100) ? buf.detachAsData() : nullptr;
     #else
         switch (type) {
-            case SkEncodedImageFormat::kPNG:
-                return SkEncodeImageAsPNG(&buf, src, SkEncodeOptions()) ? buf.detachAsData()
-                                                                        : nullptr;
-            case SkEncodedImageFormat::kJPEG:
-                return SkEncodeImageAsJPEG(&buf, src, 100) ? buf.detachAsData() : nullptr;
-            case SkEncodedImageFormat::kWEBP:
-                return SkEncodeImageAsWEBP(&buf, src, 100) ? buf.detachAsData() : nullptr;
+            case SkEncodedImageFormat::kPNG: {
+                SkPngEncoder::Options options;
+                options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+                bool success = SkPngEncoder::Encode(&buf, src, options);
+                return success ? buf.detachAsData() : nullptr;
+            }
+            case SkEncodedImageFormat::kJPEG: {
+                bool success = SkJpegEncoder::Encode(&buf, src, SkJpegEncoder::Options());
+                return success ? buf.detachAsData() : nullptr;
+            }
+            case SkEncodedImageFormat::kWEBP: {
+                SkWebpEncoder::Options options;
+                options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+                bool success = SkWebpEncoder::Encode(&buf, src, options);
+                return success ? buf.detachAsData() : nullptr;
+            }
             default:
                 SkASSERT(false);
                 return nullptr;
diff --git a/gm/encode-srgb.cpp b/gm/encode-srgb.cpp
index f62d48a..3369afe 100644
--- a/gm/encode-srgb.cpp
+++ b/gm/encode-srgb.cpp
@@ -12,9 +12,13 @@
 #include "SkCodec.h"
 #include "SkColorSpace_Base.h"
 #include "SkData.h"
+#include "SkImage.h"
 #include "SkImageEncoderPriv.h"
+#include "SkJpegEncoder.h"
+#include "SkPngEncoder.h"
 #include "SkPM4f.h"
 #include "SkSRGB.h"
+#include "SkWebpEncoder.h"
 
 namespace skiagm {
 
@@ -67,10 +71,9 @@
         pmColors[i] = toPMColor(colors[i]);
     }
 
-    sk_sp<SkColorTable> colorTable(new SkColorTable(pmColors, SK_ARRAY_COUNT(pmColors)));
     SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kIndex_8_SkColorType,
                                          alphaType, colorSpace);
-    bitmap->allocPixels(info, nullptr, colorTable.get());
+    bitmap->allocPixels(info, SkColorTable::Make(pmColors, SK_ARRAY_COUNT(pmColors)));
     for (int y = 0; y < imageHeight; y++) {
         for (int x = 0; x < imageWidth; x++) {
             *bitmap->getAddr8(x, y) = (x / div_round_up(imageWidth, 2)) +
@@ -110,27 +113,28 @@
 }
 
 static sk_sp<SkData> encode_data(const SkBitmap& bitmap, SkEncodedImageFormat format) {
-    SkAutoLockPixels autoLockPixels(bitmap);
     SkPixmap src;
     if (!bitmap.peekPixels(&src)) {
         return nullptr;
     }
     SkDynamicMemoryWStream buf;
 
-    SkEncodeOptions options;
+    SkPngEncoder::Options pngOptions;
+    SkWebpEncoder::Options webpOptions;
     if (bitmap.colorSpace()) {
-        options.fUnpremulBehavior = SkTransferFunctionBehavior::kRespect;
+        pngOptions.fUnpremulBehavior = SkTransferFunctionBehavior::kRespect;
+        webpOptions.fUnpremulBehavior = SkTransferFunctionBehavior::kRespect;
     }
 
     switch (format) {
         case SkEncodedImageFormat::kPNG:
-            SkAssertResult(SkEncodeImageAsPNG(&buf, src, options));
+            SkAssertResult(SkPngEncoder::Encode(&buf, src, pngOptions));
             break;
         case SkEncodedImageFormat::kWEBP:
-            SkAssertResult(SkEncodeImageAsWEBP(&buf, src, options));
+            SkAssertResult(SkWebpEncoder::Encode(&buf, src, webpOptions));
             break;
         case SkEncodedImageFormat::kJPEG:
-            SkAssertResult(SkEncodeImageAsJPEG(&buf, src, options));
+            SkAssertResult(SkJpegEncoder::Encode(&buf, src, SkJpegEncoder::Options()));
             break;
         default:
             break;
diff --git a/gm/encode.cpp b/gm/encode.cpp
index 1dae1bf..bf13d3d 100644
--- a/gm/encode.cpp
+++ b/gm/encode.cpp
@@ -8,6 +8,7 @@
 #include "sk_tool_utils.h"
 #include "SkCanvas.h"
 #include "SkData.h"
+#include "SkImage.h"
 #include "SkImageEncoder.h"
 #include "Resources.h"
 
diff --git a/gm/etc1.cpp b/gm/etc1.cpp
deleted file mode 100644
index 26e6051..0000000
--- a/gm/etc1.cpp
+++ /dev/null
@@ -1,122 +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 "gm.h"
-#include "sk_tool_utils.h"
-#include "SkRandom.h"
-
-#if SK_SUPPORT_GPU
-#include "etc1.h"
-
-#include "GrContext.h"
-#include "GrRenderTargetContext.h"
-#include "GrRenderTargetContextPriv.h"
-#include "GrTextureProxy.h"
-#include "effects/GrSimpleTextureEffect.h"
-#include "ops/GrRectOpFactory.h"
-
-// Basic test of Ganesh's ETC1 support
-class ETC1GM : public skiagm::GM {
-public:
-    ETC1GM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
-    }
-
-protected:
-    SkString onShortName() override {
-        return SkString("etc1");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(kTexWidth + 2*kPad, kTexHeight + 2*kPad);
-    }
-
-    // TODO: we should be creating an ETC1 SkData blob here and going through SkImageCacherator.
-    // That will require an ETC1 Codec though - so for later.
-    void onOnceBeforeDraw() override {
-        SkBitmap bm;
-        SkImageInfo ii = SkImageInfo::Make(kTexWidth, kTexHeight, kRGB_565_SkColorType,
-                                           kOpaque_SkAlphaType);
-        bm.allocPixels(ii);
-
-        bm.erase(SK_ColorBLUE, SkIRect::MakeWH(kTexWidth, kTexHeight));
-
-        for (int y = 0; y < kTexHeight; y += 4) {
-            for (int x = 0; x < kTexWidth; x += 4) {
-                bm.erase((x+y) % 8 ? SK_ColorRED : SK_ColorGREEN, SkIRect::MakeXYWH(x, y, 4, 4));
-            }
-        }
-
-        int size = etc1_get_encoded_data_size(bm.width(), bm.height());
-        fETC1Data.reset(size);
-
-        unsigned char* pixels = (unsigned char*) fETC1Data.get();
-
-        if (etc1_encode_image((unsigned char*) bm.getAddr16(0, 0),
-                              bm.width(), bm.height(), 2, bm.rowBytes(), pixels)) {
-            fETC1Data.reset();
-        }
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-        GrRenderTargetContext* renderTargetContext =
-            canvas->internal_private_accessTopLayerRenderTargetContext();
-        if (!renderTargetContext) {
-            skiagm::GM::DrawGpuOnlyMessage(canvas);
-            return;
-        }
-
-        GrContext* context = canvas->getGrContext();
-        if (!context) {
-            return;
-        }
-
-        GrSurfaceDesc desc;
-        desc.fConfig = kETC1_GrPixelConfig;
-        desc.fWidth = kTexWidth;
-        desc.fHeight = kTexHeight;
-
-        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                   desc, SkBudgeted::kYes,
-                                                                   fETC1Data.get(), 0);
-        if (!proxy) {
-            return;
-        }
-
-        const SkMatrix trans = SkMatrix::MakeTrans(-kPad, -kPad);
-
-        sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(context->resourceProvider(),
-                                                                    std::move(proxy),
-                                                                    nullptr, trans);
-
-        GrPaint grPaint;
-        grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
-        grPaint.addColorFragmentProcessor(std::move(fp));
-
-        SkRect rect = SkRect::MakeXYWH(kPad, kPad, kTexWidth, kTexHeight);
-
-        std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                GrColor_WHITE, SkMatrix::I(), rect, nullptr, nullptr));
-        renderTargetContext->priv().testingOnly_addMeshDrawOp(std::move(grPaint), GrAAType::kNone,
-                                                              std::move(op));
-    }
-
-private:
-    static const int kPad = 8;
-    static const int kTexWidth = 16;
-    static const int kTexHeight = 20;
-
-    SkAutoTMalloc<char> fETC1Data;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new ETC1GM;)
-
-#endif
diff --git a/gm/filterbitmap.cpp b/gm/filterbitmap.cpp
index 0f3371c..92aeddc 100644
--- a/gm/filterbitmap.cpp
+++ b/gm/filterbitmap.cpp
@@ -125,13 +125,13 @@
           paint.setTextSize(fTextSize);
 
           setTypeface(&paint, "serif", SkFontStyle());
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 1.2f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 1.2f*fTextSize, paint);
           setTypeface(&paint, "serif", SkFontStyle::FromOldStyle(SkTypeface::kBold));
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 2.4f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 2.4f*fTextSize, paint);
           setTypeface(&paint, "serif", SkFontStyle::FromOldStyle(SkTypeface::kItalic));
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 3.6f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 3.6f*fTextSize, paint);
           setTypeface(&paint, "serif", SkFontStyle::FromOldStyle(SkTypeface::kBoldItalic));
-          canvas.drawText("Hamburgefons", 12, fTextSize/2, 4.8f*fTextSize, paint);
+          canvas.drawString("Hamburgefons", fTextSize/2, 4.8f*fTextSize, paint);
       }
   private:
       typedef FilterBitmapGM INHERITED;
diff --git a/gm/filterfastbounds.cpp b/gm/filterfastbounds.cpp
index 5102b86..ad042ac 100644
--- a/gm/filterfastbounds.cpp
+++ b/gm/filterfastbounds.cpp
@@ -11,6 +11,7 @@
 #include "SkDropShadowImageFilter.h"
 #include "SkImageSource.h"
 #include "SkOffsetImageFilter.h"
+#include "SkPath.h"
 #include "SkPictureImageFilter.h"
 #include "SkPictureRecorder.h"
 #include "SkRandom.h"
diff --git a/gm/fontcache.cpp b/gm/fontcache.cpp
index 8775b58..f3f8910 100644
--- a/gm/fontcache.cpp
+++ b/gm/fontcache.cpp
@@ -15,7 +15,7 @@
 
 static SkScalar draw_string(SkCanvas* canvas, const SkString& text, SkScalar x,
                            SkScalar y, const SkPaint& paint) {
-    canvas->drawText(text.c_str(), text.size(), x, y, paint);
+    canvas->drawString(text, x, y, paint);
     return x + paint.measureText(text.c_str(), text.size());
 }
 
diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp
index aecb46e..bef26f3 100644
--- a/gm/fontmgr.cpp
+++ b/gm/fontmgr.cpp
@@ -21,7 +21,7 @@
 
 static SkScalar drawString(SkCanvas* canvas, const SkString& text, SkScalar x,
                            SkScalar y, const SkPaint& paint) {
-    canvas->drawText(text.c_str(), text.size(), x, y, paint);
+    canvas->drawString(text, x, y, paint);
     return x + paint.measureText(text.c_str(), text.size());
 }
 
diff --git a/gm/gamma.cpp b/gm/gamma.cpp
index 8642726..8188957 100644
--- a/gm/gamma.cpp
+++ b/gm/gamma.cpp
@@ -63,9 +63,9 @@
 
     auto nextRect = [&](const char* label, const char* label2) {
         canvas->drawRect(r, p);
-        canvas->drawText(label, strlen(label), 0, sz + textPaint.getFontSpacing(), textPaint);
+        canvas->drawString(label, 0, sz + textPaint.getFontSpacing(), textPaint);
         if (label2) {
-            canvas->drawText(label2, strlen(label2), 0, sz + 2 * textPaint.getFontSpacing(),
+            canvas->drawString(label2, 0, sz + 2 * textPaint.getFontSpacing(),
                              textPaint);
         }
         advance();
@@ -73,7 +73,7 @@
 
     auto nextBitmap = [&](const SkBitmap& bmp, const char* label) {
         canvas->drawBitmap(bmp, 0, 0);
-        canvas->drawText(label, strlen(label), 0, sz + textPaint.getFontSpacing(), textPaint);
+        canvas->drawString(label, 0, sz + textPaint.getFontSpacing(), textPaint);
         advance();
     };
 
@@ -86,12 +86,12 @@
 
         SkString srcText = SkStringPrintf("%08X", srcColor);
         SkString dstText = SkStringPrintf("%08X", dstColor);
-        canvas->drawText(srcText.c_str(), srcText.size(), 0, sz + textPaint.getFontSpacing(),
+        canvas->drawString(srcText, 0, sz + textPaint.getFontSpacing(),
                          textPaint);
         const char* modeName = SkBlendMode_Name(mode);
-        canvas->drawText(modeName, strlen(modeName), 0, sz + 2 * textPaint.getFontSpacing(),
+        canvas->drawString(modeName, 0, sz + 2 * textPaint.getFontSpacing(),
                          textPaint);
-        canvas->drawText(dstText.c_str(), dstText.size(), 0, sz + 3 * textPaint.getFontSpacing(),
+        canvas->drawString(dstText, 0, sz + 3 * textPaint.getFontSpacing(),
                          textPaint);
         advance();
     };
diff --git a/gm/gammaencodedpremul.cpp b/gm/gammaencodedpremul.cpp
index 327368f..d55498b 100644
--- a/gm/gammaencodedpremul.cpp
+++ b/gm/gammaencodedpremul.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "gm.h"
+#include "SkColorPriv.h"
 #include "SkColorSpaceXform.h"
 #include "SkColorSpaceXformPriv.h"
 #include "SkOpts.h"
diff --git a/gm/gamut.cpp b/gm/gamut.cpp
index af104c4..9339aa8 100644
--- a/gm/gamut.cpp
+++ b/gm/gamut.cpp
@@ -13,6 +13,7 @@
 #include "SkImagePriv.h"
 #include "SkPM4fPriv.h"
 #include "SkSurface.h"
+#include "SkVertices.h"
 
 static const int gRectSize = 50;
 static const SkScalar gScalarSize = SkIntToScalar(gRectSize);
@@ -105,8 +106,9 @@
             SkPoint::Make(gScalarSize, gScalarSize),
             SkPoint::Make(0, gScalarSize)
         };
-        canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, vertices, nullptr, fColors,
-                             SkBlendMode::kModulate, nullptr, 0, paint);
+        canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, vertices,
+                                                  nullptr, fColors),
+                             SkBlendMode::kModulate, paint);
     }
     const char* label() override {
         return "Vertices";
@@ -177,8 +179,7 @@
         wideGamutCanvas->clear(SK_ColorBLACK);
         renderer->draw(wideGamutCanvas);
 
-        canvas->drawText(renderer->label(), strlen(renderer->label()), x, y + textHeight,
-                         textPaint);
+        canvas->drawString(renderer->label(), x, y + textHeight, textPaint);
 
         // Re-interpret the off-screen images, so we can see the raw data (eg, Wide gamut squares
         // will look desaturated, relative to sRGB).
diff --git a/gm/gaussianedge.cpp b/gm/gaussianedge.cpp
deleted file mode 100644
index 7a45118..0000000
--- a/gm/gaussianedge.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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 "gm.h"
-#include "SkRRect.h"
-#include "SkGaussianEdgeShader.h"
-
-//#define VIZ 1
-
-#ifdef VIZ
-#include "SkStroke.h"
-
-static void draw_stroke(SkCanvas* canvas, const SkRRect& rr, const SkPaint& p, SkColor color) {
-    SkPath output;
-
-    if (SkPaint::kFill_Style == p.getStyle()) {
-        output.addRRect(rr);
-    } else {
-        SkPath input;
-        input.addRRect(rr);
-
-        SkStroke stroke(p);
-        stroke.strokePath(input, &output);
-    }
-
-    SkPaint paint;
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setColor(color);
-
-    canvas->drawPath(output, paint);
-}
-
-static void extract_pts(const SkBitmap& bm, SkTDArray<SkPoint>* pts,
-                        int xOff, int width) {
-    pts->rewind();
-
-    for (int x = 0; x < width; ++x) {
-        SkColor color = bm.getColor(xOff+x, 0);    
-
-        pts->append()->set(SkIntToScalar(x), 255.0f-SkColorGetB(color));
-        if (x > 0 && x < width-1) {
-            pts->append()->set(SkIntToScalar(x), 255.0f-SkColorGetB(color));
-        }
-    }
-}
-
-static void draw_row(SkCanvas* canvas, int row, int width) {
-    SkPaint paint;
-    paint.setAntiAlias(true);
-
-    SkBitmap readback;
-
-    if (!canvas->readPixels(SkIRect::MakeXYWH(0, row, width, 1), &readback)) {
-        return;
-    }
-
-    SkTDArray<SkPoint> pts;
-    pts.setReserve(width/3);
-
-    extract_pts(readback, &pts, 0, width/3);
-    paint.setColor(SK_ColorRED);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, pts.count(), pts.begin(), paint);
-
-    extract_pts(readback, &pts, width/3, width/3);
-    paint.setColor(SK_ColorGREEN);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, pts.count(), pts.begin(), paint);
-
-    extract_pts(readback, &pts, 2*width/3, width/3);
-    paint.setColor(SK_ColorBLUE);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, pts.count(), pts.begin(), paint);
-}
-#endif
-
-namespace skiagm {
-
-// This GM exercises the SkGaussianEdgeShader.
-// It draws three columns showing filled, stroked, and stroke and filled rendering.
-// It draws three rows showing a blur radius smaller than, equal to
-// and, finally, double the RRect's corner radius
-// In VIZ mode an extra column is drawn showing the blur ramps (they should all line up).
-class GaussianEdgeGM : public GM {
-public:
-    GaussianEdgeGM() {
-        this->setBGColor(SK_ColorWHITE);
-    }
-
-protected:
-
-    SkString onShortName() override {
-        return SkString("gaussianedge");
-    }
-
-    SkISize onISize() override {
-        int numCols = kNumBaseCols;
-#ifdef VIZ
-        numCols++;
-#endif
-
-        return SkISize::Make(kPad + numCols*(kCellWidth+kPad),
-                             kPad + kNumRows*(kCellWidth+kPad));
-    }
-
-    static void DrawRow(SkCanvas* canvas, int blurRad, int midLine) {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        SkRRect rrects[kNumBaseCols];
-        SkPaint paints[kNumBaseCols];
-
-        {
-            const SkRect r = SkRect::MakeIWH(kRRSize, kRRSize);
-            const SkRRect baseRR = SkRRect::MakeRectXY(r,
-                                                       SkIntToScalar(kRRRad),
-                                                       SkIntToScalar(kRRRad));
-
-            SkPaint basePaint;
-            basePaint.setAntiAlias(true);
-            basePaint.setShader(SkGaussianEdgeShader::Make());
-            basePaint.setColor(SkColorSetARGB(255, (4 * blurRad) >> 8, (4 * blurRad) & 0xff, 0));
-
-            //----
-            paints[0] = basePaint;
-            rrects[0] = baseRR;
-
-            //----
-            paints[1] = basePaint;
-            paints[1].setStyle(SkPaint::kStroke_Style);
-
-            rrects[1] = baseRR;
-            if (blurRad/2.0f < kRRRad) {
-                rrects[1].inset(blurRad/2.0f, blurRad/2.0f);
-                paints[1].setStrokeWidth(SkIntToScalar(blurRad));
-            } else {
-                SkScalar inset = kRRRad - 0.5f;
-                rrects[1].inset(inset, inset);
-                SkScalar pad = blurRad/2.0f - inset;
-                paints[1].setStrokeWidth(blurRad + 2.0f * pad);                
-                paints[1].setColor(SkColorSetARGB(255, (4 * blurRad) >> 8, (4 * blurRad) & 0xff,
-                                                  (int)(4.0f*pad)));
-            }
-
-            //----
-            paints[2] = basePaint;
-            paints[2].setStyle(SkPaint::kStrokeAndFill_Style);
-
-            rrects[2] = baseRR;
-            if (blurRad/2.0f < kRRRad) {
-                rrects[2].inset(blurRad/2.0f, blurRad/2.0f);
-                paints[2].setStrokeWidth(SkIntToScalar(blurRad));
-            } else {
-                SkScalar inset = kRRRad - 0.5f;
-                rrects[2].inset(inset, inset);
-                SkScalar pad = blurRad/2.0f - inset;
-                paints[2].setStrokeWidth(blurRad + 2.0f * pad);                
-                paints[2].setColor(SkColorSetARGB(255, (4 * blurRad) >> 8, (4 * blurRad) & 0xff,
-                                                  (int)(4.0f*pad)));
-            }
-        }
-
-        //----
-        canvas->save();
-            // draw the shadows
-            for (int i = 0; i < kNumBaseCols; ++i) {
-                canvas->drawRRect(rrects[i], paints[i]);
-                canvas->translate(SkIntToScalar(kCellWidth+kPad), 0.0f);
-            }
-
-#ifdef VIZ
-            // draw the visualization of the shadow ramps
-            draw_row(canvas, midLine, 3*(kRRSize+kPad));
-#endif
-        canvas->restore();
-
-#ifdef VIZ
-        const SkColor colors[kNumBaseCols] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
-
-        // circle back and draw the stroked geometry (they would mess up the viz otherwise)
-        for (int i = 0; i < kNumBaseCols; ++i) {
-            draw_stroke(canvas, rrects[i], paints[i], colors[i]);
-            canvas->translate(SkIntToScalar(kCellWidth+kPad), 0.0f);
-        }
-#endif
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-        GrRenderTargetContext* renderTargetContext =
-            canvas->internal_private_accessTopLayerRenderTargetContext();
-        if (!renderTargetContext) {
-            skiagm::GM::DrawGpuOnlyMessage(canvas);
-            return;
-        }
-
-        const int blurRadii[kNumRows] = { kRRRad/2, kRRRad, 2*kRRRad };
-
-        canvas->translate(SkIntToScalar(kPad), SkIntToScalar(kPad));
-        for (int i = 0; i < kNumRows; ++i) {
-            DrawRow(canvas, blurRadii[i], kPad+(i*kRRSize)+kRRSize/2);
-            canvas->translate(0.0f, SkIntToScalar(kCellWidth+kPad));
-        }
-    }
-
-private:
-    static const int kNumRows = 3;
-    static const int kNumBaseCols = 3;
-    static const int kPad = 5;
-    static const int kRRSize = 256;
-    static const int kRRRad = 64;
-    static const int kCellWidth = kRRSize;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new GaussianEdgeGM;)
-}
diff --git a/gm/gm.cpp b/gm/gm.cpp
index c8f1250..1ba571a 100644
--- a/gm/gm.cpp
+++ b/gm/gm.cpp
@@ -82,7 +82,7 @@
     paint.setColor(SK_ColorRED);
     sk_tool_utils::set_portable_typeface(&paint);
     constexpr char kTxt[] = "GPU Only";
-    bmpCanvas.drawText(kTxt, strlen(kTxt), 20, 40, paint);
+    bmpCanvas.drawString(kTxt, 20, 40, paint);
     SkMatrix localM;
     localM.setRotate(35.f);
     localM.postTranslate(10.f, 0.f);
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
index e7a8a89..27fd0f3 100644
--- a/gm/gradients.cpp
+++ b/gm/gradients.cpp
@@ -937,3 +937,109 @@
 DEF_SIMPLE_GM(gradient_subpixel_4f, canvas, 500, 500) {
     draw_subpixel_gradient(canvas, SkLinearGradient::kForce4fContext_PrivateFlag);
 }
+
+#include "SkPictureRecorder.h"
+
+static void draw_circle_shader(SkCanvas* canvas, SkScalar cx, SkScalar cy, SkScalar r,
+                               sk_sp<SkShader> (*shaderFunc)()) {
+    SkPaint p;
+    p.setAntiAlias(true);
+    p.setShader(shaderFunc());
+    canvas->drawCircle(cx, cy, r, p);
+
+    p.setShader(nullptr);
+    p.setColor(SK_ColorGRAY);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(2);
+    canvas->drawCircle(cx, cy, r, p);
+}
+
+DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
+    draw_circle_shader(canvas, 150, 150, 100, []() -> sk_sp<SkShader> {
+        // Checkerboard using two linear gradients + picture shader.
+        SkScalar kTileSize = 80 / sqrtf(2);
+        SkColor colors1[] = { 0xff000000, 0xff000000,
+                              0xffffffff, 0xffffffff,
+                              0xff000000, 0xff000000 };
+        SkColor colors2[] = { 0xff000000, 0xff000000,
+                              0x00000000, 0x00000000,
+                              0xff000000, 0xff000000 };
+        SkScalar pos[] = { 0, .25f, .25f, .75f, .75f, 1 };
+        static_assert(SK_ARRAY_COUNT(colors1) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
+        static_assert(SK_ARRAY_COUNT(colors2) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
+
+        SkPictureRecorder recorder;
+        recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize));
+
+        SkPaint p;
+
+        SkPoint pts1[] = { { 0, 0 }, { kTileSize, kTileSize }};
+        p.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos, SK_ARRAY_COUNT(colors1),
+                                                 SkShader::kClamp_TileMode, 0, nullptr));
+        recorder.getRecordingCanvas()->drawPaint(p);
+
+        SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
+        p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, SK_ARRAY_COUNT(colors2),
+                                                 SkShader::kClamp_TileMode, 0, nullptr));
+        recorder.getRecordingCanvas()->drawPaint(p);
+
+        SkMatrix m = SkMatrix::I();
+        m.preRotate(45);
+        return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
+                                           SkShader::kRepeat_TileMode,
+                                           SkShader::kRepeat_TileMode, &m, nullptr);
+    });
+
+    draw_circle_shader(canvas, 400, 150, 100, []() -> sk_sp<SkShader> {
+        // Checkerboard using a sweep gradient + picture shader.
+        SkScalar kTileSize = 80;
+        SkColor colors[] = { 0xff000000, 0xff000000,
+                             0xffffffff, 0xffffffff,
+                             0xff000000, 0xff000000,
+                             0xffffffff, 0xffffffff };
+        SkScalar pos[] = { 0, .25f, .25f, .5f, .5f, .75f, .75f, 1 };
+        static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
+
+        SkPaint p;
+        p.setShader(SkGradientShader::MakeSweep(kTileSize / 2, kTileSize / 2,
+                                                colors, pos, SK_ARRAY_COUNT(colors), 0, nullptr));
+        SkPictureRecorder recorder;
+        recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize))->drawPaint(p);
+
+        return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
+                                           SkShader::kRepeat_TileMode,
+                                           SkShader::kRepeat_TileMode, nullptr, nullptr);
+    });
+
+    draw_circle_shader(canvas, 650, 150, 100, []() -> sk_sp<SkShader> {
+        // Dartboard using sweep + radial.
+        const SkColor a = 0xffffffff;
+        const SkColor b = 0xff000000;
+        SkColor colors[] = { a, a, b, b, a, a, b, b, a, a, b, b, a, a, b, b};
+        SkScalar pos[] = { 0, .125f, .125f, .25f, .25f, .375f, .375f, .5f, .5f,
+                           .625f, .625f, .75f, .75f, .875f, .875f, 1};
+        static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
+
+        SkPoint center = { 650, 150 };
+        sk_sp<SkShader> sweep1 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
+                                                             SK_ARRAY_COUNT(colors), 0, nullptr);
+        SkMatrix m = SkMatrix::I();
+        m.preRotate(22.5f, center.x(), center.y());
+        sk_sp<SkShader> sweep2 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
+                                                             SK_ARRAY_COUNT(colors), 0, &m);
+
+        sk_sp<SkShader> sweep(SkShader::MakeComposeShader(sweep1, sweep2, SkBlendMode::kExclusion));
+
+        SkScalar radialPos[] = { 0, .02f, .02f, .04f, .04f, .08f, .08f, .16f, .16f, .31f, .31f,
+                                 .62f, .62f, 1, 1, 1 };
+        static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(radialPos),
+                      "color/pos size mismatch");
+
+        return SkShader::MakeComposeShader(sweep,
+                                           SkGradientShader::MakeRadial(center, 100, colors,
+                                                                        radialPos,
+                                                                        SK_ARRAY_COUNT(radialPos),
+                                                                        SkShader::kClamp_TileMode),
+                                           SkBlendMode::kExclusion);
+    });
+}
diff --git a/gm/gradtext.cpp b/gm/gradtext.cpp
index 7f7f222..cc0c079 100644
--- a/gm/gradtext.cpp
+++ b/gm/gradtext.cpp
@@ -57,7 +57,7 @@
         paint.setShader(make_chrome_solid());
         paint.setTextSize(SkIntToScalar(500));
 
-        canvas->drawText("I", 1, 0, 100, paint);
+        canvas->drawString("I", 0, 100, paint);
     }
 private:
     typedef GM INHERITED;
@@ -77,17 +77,17 @@
         sk_tool_utils::set_portable_typeface(&paint);
 
         paint.setStyle(SkPaint::kFill_Style);
-        canvas->drawText("Normal Fill Text", 16, 0, 50, paint);
+        canvas->drawString("Normal Fill Text", 0, 50, paint);
         paint.setStyle(SkPaint::kStroke_Style);
-        canvas->drawText("Normal Stroke Text", 18, 0, 100, paint);
+        canvas->drawString("Normal Stroke Text", 0, 100, paint);
 
         // Minimal repro doesn't require AA, LCD, or a nondefault typeface
         paint.setShader(make_chrome_solid());
 
         paint.setStyle(SkPaint::kFill_Style);
-        canvas->drawText("Gradient Fill Text", 18, 0, 150, paint);
+        canvas->drawString("Gradient Fill Text", 0, 150, paint);
         paint.setStyle(SkPaint::kStroke_Style);
-        canvas->drawText("Gradient Stroke Text", 20, 0, 200, paint);
+        canvas->drawString("Gradient Stroke Text", 0, 200, paint);
     }
 private:
     typedef GM INHERITED;
diff --git a/gm/highcontrastfilter.cpp b/gm/highcontrastfilter.cpp
index e5330d2..0c7c5e1 100644
--- a/gm/highcontrastfilter.cpp
+++ b/gm/highcontrastfilter.cpp
@@ -52,7 +52,7 @@
 
     paint.setARGB(0xff, 0xbb, 0x77, 0x77);
     paint.setTextSize(0.15f);
-    canvas->drawText("A", 1, 0.15f, 0.35f, paint);
+    canvas->drawString("A", 0.15f, 0.35f, paint);
 
     bounds = SkRect::MakeLTRB(0.1f, 0.8f, 0.9f, 1.0f);
     paint.setARGB(0xff, 0xcc, 0xcc, 0xff);
@@ -60,7 +60,7 @@
 
     paint.setARGB(0xff, 0x88, 0x88, 0xbb);
     paint.setTextSize(0.15f);
-    canvas->drawText("Z", 1, 0.75f, 0.95f, paint);
+    canvas->drawString("Z", 0.75f, 0.95f, paint);
 
     bounds = SkRect::MakeLTRB(0.1f, 0.4f, 0.9f, 0.6f);
     SkPoint     pts[] = { { 0, 0 }, { 1, 0 } };
diff --git a/gm/hsl.cpp b/gm/hsl.cpp
new file mode 100644
index 0000000..39074e2
--- /dev/null
+++ b/gm/hsl.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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 "gm.h"
+#include "sk_tool_utils.h"
+
+// Hue, Saturation, Color, and Luminosity blend modes are oddballs.
+// They nominally convert their inputs to unpremul, then to HSL, then
+// mix-and-match H,S,and L from Src and Dst, then convert back, then premul.
+//
+// In practice that's slow, so instead we pick the color with the correct
+// Hue, and then (approximately) apply the other's Saturation and/or Luminosity.
+// This isn't just an optimization... it's how the modes are specified.
+//
+// Each mode's name describes the Src H,S,L components to keep, taking the
+// others from Dst, where Color == Hue + Saturation.  Color and Luminosity
+// are each other's complements; Hue and Saturation have no complement.
+//
+// All these modes were originally designed to operate on gamma-encoded values,
+// and that's what everyone's used to seeing.  It's unclear wehther they make
+// any sense in a gamma-correct world.  They certainly won't look at all similar.
+//
+// We have had many inconsistent implementations of these modes.
+// This GM tries to demonstrate unambigously how they should work.
+//
+// To go along with our inconsistent implementations, there are two slightly
+// inconsistent specifications of how to perform these blends,
+//   web: https://www.w3.org/TR/compositing-1/#blendingnonseparable
+//   KHR: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt
+// It looks like these are meant to be identical, but they disagree on at least ClipColor().
+//
+// I think the KHR version is just wrong... it produces values >1.  So we use the web version.
+
+static float min(float r, float g, float b) { return SkTMin(r, SkTMin(g, b)); }
+static float max(float r, float g, float b) { return SkTMax(r, SkTMax(g, b)); }
+
+static float sat(float r, float g, float b) { return max(r,g,b) - min(r,g,b); }
+static float lum(float r, float g, float b) { return r*0.30f + g*0.59f + b*0.11f; }
+
+// The two SetSat() routines in the specs look different, but they're logically equivalent.
+// Both map the minimum channel to 0, maximum to s, and scale the middle proportionately.
+// The KHR version has done a better job at simplifying its math, so we use it here.
+static void set_sat(float* r, float* g, float* b, float s) {
+    float mn = min(*r,*g,*b),
+          mx = max(*r,*g,*b);
+    auto channel = [=](float c) {
+        return mx == mn ? 0
+                        : (c - mn) * s / (mx - mn);
+    };
+    *r = channel(*r);
+    *g = channel(*g);
+    *b = channel(*b);
+}
+static void clip_color(float* r, float* g, float* b) {
+    float l  = lum(*r,*g,*b),
+          mn = min(*r,*g,*b),
+          mx = max(*r,*g,*b);
+    auto clip = [=](float c) {
+        if (mn < 0) { c = l + (c - l) * (    l) / (l - mn); }
+        if (mx > 1) { c = l + (c - l) * (1 - l) / (mx - l); }
+        SkASSERT(-0.0001f <  c);   // This may end up very slightly negative...
+        SkASSERT(       c <= 1);
+        return c;
+    };
+    *r = clip(*r);
+    *g = clip(*g);
+    *b = clip(*b);
+}
+static void set_lum(float* r, float* g, float* b, float l) {
+    float diff = l - lum(*r,*g,*b);
+    *r += diff;
+    *g += diff;
+    *b += diff;
+    clip_color(r,g,b);
+}
+
+
+static void hue(float  dr, float  dg, float  db,
+                float* sr, float* sg, float* sb) {
+    // Hue of Src, Saturation and Luminosity of Dst.
+    float R = *sr,
+          G = *sg,
+          B = *sb;
+    set_sat(&R,&G,&B, sat(dr,dg,db));
+    set_lum(&R,&G,&B, lum(dr,dg,db));
+    *sr = R;
+    *sg = G;
+    *sb = B;
+}
+
+static void saturation(float  dr, float  dg, float  db,
+                       float* sr, float* sg, float* sb) {
+    // Saturation of Src, Hue and Luminosity of Dst
+    float R = dr,
+          G = dg,
+          B = db;
+    set_sat(&R,&G,&B, sat(*sr,*sg,*sb));
+    set_lum(&R,&G,&B, lum( dr, dg, db));  // This may seem redundant, but it is not.
+    *sr = R;
+    *sg = G;
+    *sb = B;
+}
+
+static void color(float  dr, float  dg, float  db,
+                  float* sr, float* sg, float* sb) {
+    // Hue and Saturation of Src, Luminosity of Dst.
+    float R = *sr,
+          G = *sg,
+          B = *sb;
+    set_lum(&R,&G,&B, lum(dr,dg,db));
+    *sr = R;
+    *sg = G;
+    *sb = B;
+}
+
+static void luminosity(float  dr, float  dg, float  db,
+                       float* sr, float* sg, float* sb) {
+    // Luminosity of Src, Hue and Saturation of Dst.
+    float R = dr,
+          G = dg,
+          B = db;
+    set_lum(&R,&G,&B, lum(*sr,*sg,*sb));
+    *sr = R;
+    *sg = G;
+    *sb = B;
+}
+
+static SkColor blend(SkColor dst, SkColor src,
+                     void (*mode)(float,float,float, float*,float*,float*),
+                     bool legacy) {
+
+    SkASSERT(SkColorGetA(dst) == 0xff
+          && SkColorGetA(src) == 0xff);   // Not fundamental, just simplifying for this GM.
+
+    auto to_float = [&](SkColor c) {
+        if (legacy) {
+            return SkColor4f{
+                SkColorGetR(c) * (1/255.0f),
+                SkColorGetG(c) * (1/255.0f),
+                SkColorGetB(c) * (1/255.0f),
+                1.0f,
+            };
+        }
+        return SkColor4f::FromColor(c);
+    };
+
+    SkColor4f d = to_float(dst),
+              s = to_float(src);
+
+    mode( d.fR,  d.fG,  d.fB,
+         &s.fR, &s.fG, &s.fB);
+
+    if (legacy) {
+        return SkColorSetRGB(s.fR * 255.0f + 0.5f,
+                             s.fG * 255.0f + 0.5f,
+                             s.fB * 255.0f + 0.5f);
+    }
+    return s.toSkColor();
+}
+
+DEF_SIMPLE_GM(hsl, canvas, 600, 100) {
+    SkPaint label;
+    sk_tool_utils::set_portable_typeface(&label);
+    label.setAntiAlias(true);
+
+    const char* comment = "HSL blend modes are correct when you see no circles in the squares.";
+    canvas->drawText(comment, strlen(comment), 10,10, label);
+
+    // Just to keep things reaaaal simple, we'll only use opaque colors.
+    SkPaint bg, fg;
+    bg.setColor(0xff00ff00);  // Fully-saturated bright green,  H = 120°, S = 100%, L =  50%.
+    fg.setColor(0xff7f3f7f);  // Partly-saturated dim magenta,  H = 300°, S = ~33%, L = ~37%.
+
+    struct {
+        SkBlendMode mode;
+        void (*reference)(float,float,float, float*,float*,float*);
+    } tests[] = {
+        { SkBlendMode::kSrc,        nullptr    },
+        { SkBlendMode::kDst,        nullptr    },
+        { SkBlendMode::kHue,        hue        },
+        { SkBlendMode::kSaturation, saturation },
+        { SkBlendMode::kColor,      color      },
+        { SkBlendMode::kLuminosity, luminosity },
+    };
+    bool legacy = !canvas->imageInfo().colorSpace();
+    for (auto test : tests) {
+        canvas->drawRect({20,20,80,80}, bg);
+
+        fg.setBlendMode(test.mode);
+        canvas->drawRect({20,20,80,80}, fg);
+
+        if (test.reference) {
+            SkPaint ref;
+            ref.setColor(blend(bg.getColor(), fg.getColor(), test.reference, legacy));
+            canvas->drawCircle(50,50, 20, ref);
+        }
+
+        const char* name = SkBlendMode_Name(test.mode);
+        canvas->drawText(name, strlen(name), 20,90, label);
+
+        canvas->translate(100,0);
+    }
+}
diff --git a/gm/image.cpp b/gm/image.cpp
index 8e38ae7..fc7a5ff 100644
--- a/gm/image.cpp
+++ b/gm/image.cpp
@@ -150,17 +150,17 @@
         sk_tool_utils::set_portable_typeface(&textPaint);
         textPaint.setTextSize(8);
 
-        canvas->drawText(kLabel1, strlen(kLabel1), 10,  60, textPaint);
-        canvas->drawText(kLabel2, strlen(kLabel2), 10, 140, textPaint);
-        canvas->drawText(kLabel3, strlen(kLabel3), 10, 220, textPaint);
-        canvas->drawText(kLabel4, strlen(kLabel4), 10, 300, textPaint);
-        canvas->drawText(kLabel5, strlen(kLabel5), 10, 380, textPaint);
-        canvas->drawText(kLabel6, strlen(kLabel6), 10, 460, textPaint);
-        canvas->drawText(kLabel7, strlen(kLabel7), 10, 540, textPaint);
+        canvas->drawString(kLabel1, 10,  60, textPaint);
+        canvas->drawString(kLabel2, 10, 140, textPaint);
+        canvas->drawString(kLabel3, 10, 220, textPaint);
+        canvas->drawString(kLabel4, 10, 300, textPaint);
+        canvas->drawString(kLabel5, 10, 380, textPaint);
+        canvas->drawString(kLabel6, 10, 460, textPaint);
+        canvas->drawString(kLabel7, 10, 540, textPaint);
 
-        canvas->drawText(kLabel8, strlen(kLabel8),  80, 10, textPaint);
-        canvas->drawText(kLabel9, strlen(kLabel9), 160, 10, textPaint);
-        canvas->drawText(kLabel10, strlen(kLabel10), 265, 10, textPaint);
+        canvas->drawString(kLabel8, 80, 10, textPaint);
+        canvas->drawString(kLabel9, 160, 10, textPaint);
+        canvas->drawString(kLabel10, 265, 10, textPaint);
 
         canvas->translate(80, 20);
 
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 792173c..709ee6e 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -8,7 +8,8 @@
 #include "gm.h"
 #include "SkCanvas.h"
 #include "SkImage.h"
-#include "SkImageCacherator.h"
+#include "SkImageGenerator.h"
+#include "SkImage_Base.h"
 #include "SkMakeUnique.h"
 #include "SkPictureRecorder.h"
 #include "SkSurface.h"
@@ -118,35 +119,15 @@
 
 class RasterGenerator : public SkImageGenerator {
 public:
-    RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
-        fBM.lockPixels();
-    }
+    RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
+    {}
+
 protected:
     bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                     SkPMColor* ctable, int* ctableCount) override {
+                     const Options&) override {
         SkASSERT(fBM.width() == info.width());
         SkASSERT(fBM.height() == info.height());
-
-        if (info.colorType() == kIndex_8_SkColorType) {
-            if (SkColorTable* ct = fBM.getColorTable()) {
-                if (ctable) {
-                    memcpy(ctable, ct->readColors(), ct->count() * sizeof(SkPMColor));
-                }
-                if (ctableCount) {
-                    *ctableCount = ct->count();
-                }
-
-                for (int y = 0; y < info.height(); ++y) {
-                    memcpy(pixels, fBM.getAddr8(0, y), fBM.width());
-                    pixels = (char*)pixels + rowBytes;
-                }
-                return true;
-            } else {
-                return false;
-            }
-        } else {
-            return fBM.readPixels(info, pixels, rowBytes, 0, 0);
-        }
+        return fBM.readPixels(info, pixels, rowBytes, 0, 0);
     }
 private:
     SkBitmap fBM;
@@ -161,54 +142,6 @@
     return skstd::make_unique<RasterGenerator>(bm);
 }
 
-// so we can create a color-table
-static int find_closest(SkPMColor c, const SkPMColor table[], int count) {
-    const int cr = SkGetPackedR32(c);
-    const int cg = SkGetPackedG32(c);
-    const int cb = SkGetPackedB32(c);
-
-    int minDist = 999999999;
-    int index = 0;
-    for (int i = 0; i < count; ++i) {
-        int dr = SkAbs32((int)SkGetPackedR32(table[i]) - cr);
-        int dg = SkAbs32((int)SkGetPackedG32(table[i]) - cg);
-        int db = SkAbs32((int)SkGetPackedB32(table[i]) - cb);
-        int dist = dr + dg + db;
-        if (dist < minDist) {
-            minDist = dist;
-            index = i;
-        }
-    }
-    return index;
-}
-
-static std::unique_ptr<SkImageGenerator> make_ctable_generator(GrContext*, sk_sp<SkPicture> pic) {
-    SkBitmap bm;
-    bm.allocN32Pixels(100, 100);
-    SkCanvas canvas(bm);
-    canvas.clear(0);
-    canvas.translate(-100, -100);
-    canvas.drawPicture(pic);
-
-    const SkPMColor colors[] = {
-        SkPreMultiplyColor(SK_ColorRED),
-        SkPreMultiplyColor(0),
-        SkPreMultiplyColor(SK_ColorBLUE),
-    };
-    const int count = SK_ARRAY_COUNT(colors);
-    SkImageInfo info = SkImageInfo::Make(100, 100, kIndex_8_SkColorType, kPremul_SkAlphaType);
-
-    SkBitmap bm2;
-    sk_sp<SkColorTable> ct(new SkColorTable(colors, count));
-    bm2.allocPixels(info, nullptr, ct.get());
-    for (int y = 0; y < info.height(); ++y) {
-        for (int x = 0; x < info.width(); ++x) {
-            *bm2.getAddr8(x, y) = find_closest(*bm.getAddr32(x, y), colors, count);
-        }
-    }
-    return skstd::make_unique<RasterGenerator>(bm2);
-}
-
 class EmptyGenerator : public SkImageGenerator {
 public:
     EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
@@ -246,9 +179,11 @@
         }
 
         // need to copy the subset into a new texture
-        GrSurfaceDesc desc = fProxy->desc();
+        GrSurfaceDesc desc;
+        desc.fConfig = fProxy->config();
         desc.fWidth = info.width();
         desc.fHeight = info.height();
+        desc.fOrigin = fProxy->origin();
 
         sk_sp<GrSurfaceContext> dstContext(fCtx->contextPriv().makeDeferredSurfaceContext(
                                                                             desc,
@@ -287,8 +222,8 @@
     SkString                         fName;
     std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
     sk_sp<SkPicture>                 fPicture;
-    std::unique_ptr<SkImageCacherator> fCache;
-    std::unique_ptr<SkImageCacherator> fCacheSubset;
+    sk_sp<SkImage>                   fImage;
+    sk_sp<SkImage>                   fImageSubset;
 
 public:
     ImageCacheratorGM(const char suffix[],
@@ -316,44 +251,36 @@
 
     void makeCaches(GrContext* ctx) {
         auto gen = fFactory(ctx, fPicture);
-        SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
-        fCache.reset(SkImageCacherator::NewFromGenerator(std::move(gen)));
+        fImage = SkImage::MakeFromGenerator(std::move(gen));
 
         const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
 
         gen = fFactory(ctx, fPicture);
-        SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
-        fCacheSubset.reset(SkImageCacherator::NewFromGenerator(std::move(gen), &subset));
+        fImageSubset = SkImage::MakeFromGenerator(std::move(gen), &subset);
 
-        // whole caches should have the same ID as the generator. Subsets should be diff
-        SkASSERT(fCache->uniqueID() == genID);
-        SkASSERT(fCacheSubset->uniqueID() != genID);
-        SkASSERT(fCacheSubset->uniqueID() != genSubsetID);
-
-        SkASSERT(fCache->info().dimensions() == SkISize::Make(100, 100));
-        SkASSERT(fCacheSubset->info().dimensions() == SkISize::Make(50, 50));
+        SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
+        SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
     }
 
-    static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
+    static void draw_as_bitmap(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
         SkBitmap bitmap;
-        cache->lockAsBitmap(canvas->getGrContext(), &bitmap, nullptr,
-                            canvas->imageInfo().colorSpace());
+        as_IB(image)->getROPixels(&bitmap, canvas->imageInfo().colorSpace());
         canvas->drawBitmap(bitmap, x, y);
     }
 
-    static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
+    static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
 #if SK_SUPPORT_GPU
         sk_sp<SkColorSpace> texColorSpace;
         sk_sp<GrTextureProxy> proxy(
-            cache->lockAsTextureProxy(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
-                                      canvas->imageInfo().colorSpace(), &texColorSpace,
-                                      nullptr, nullptr));
+            as_IB(image)->asTextureProxyRef(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
+                                            canvas->imageInfo().colorSpace(), &texColorSpace,
+                                            nullptr));
         if (!proxy) {
             // show placeholder if we have no texture
             SkPaint paint;
             paint.setStyle(SkPaint::kStroke_Style);
-            SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(cache->info().width()),
-                                        SkIntToScalar(cache->info().width()));
+            SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(image->width()),
+                                        SkIntToScalar(image->width()));
             canvas->drawRect(r, paint);
             canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
             canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
@@ -361,11 +288,10 @@
         }
 
         // No API to draw a GrTexture directly, so we cheat and create a private image subclass
-        sk_sp<SkImage> image(new SkImage_Gpu(canvas->getGrContext(),
-                                             cache->uniqueID(), kPremul_SkAlphaType,
-                                             std::move(proxy), std::move(texColorSpace),
-                                             SkBudgeted::kNo));
-        canvas->drawImage(image.get(), x, y);
+        sk_sp<SkImage> texImage(new SkImage_Gpu(canvas->getGrContext(), image->uniqueID(),
+                                                kPremul_SkAlphaType, std::move(proxy),
+                                                std::move(texColorSpace), SkBudgeted::kNo));
+        canvas->drawImage(texImage.get(), x, y);
 #endif
     }
 
@@ -376,11 +302,11 @@
         // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
         // way we also can force the generateTexture call.
 
-        draw_as_tex(canvas, fCache.get(), 310, 0);
-        draw_as_tex(canvas, fCacheSubset.get(), 310+101, 0);
+        draw_as_tex(canvas, fImage.get(), 310, 0);
+        draw_as_tex(canvas, fImageSubset.get(), 310+101, 0);
 
-        draw_as_bitmap(canvas, fCache.get(), 150, 0);
-        draw_as_bitmap(canvas, fCacheSubset.get(), 150+101, 0);
+        draw_as_bitmap(canvas, fImage.get(), 150, 0);
+        draw_as_bitmap(canvas, fImageSubset.get(), 150+101, 0);
     }
 
     void onDraw(SkCanvas* canvas) override {
@@ -408,7 +334,6 @@
 };
 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
-DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
 #if SK_SUPPORT_GPU
     DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
 #endif
diff --git a/gm/imageblur.cpp b/gm/imageblur.cpp
index d15f11c..47d6374 100644
--- a/gm/imageblur.cpp
+++ b/gm/imageblur.cpp
@@ -28,7 +28,7 @@
             int y = rand.nextULessThan(HEIGHT);
             textPaint.setColor(sk_tool_utils::color_to_565(rand.nextBits(24) | 0xFF000000));
             textPaint.setTextSize(rand.nextRangeScalar(0, 300));
-            canvas->drawText(str, strlen(str), SkIntToScalar(x),
+            canvas->drawString(str, SkIntToScalar(x),
                              SkIntToScalar(y), textPaint);
         }
         canvas->restore();
diff --git a/gm/imageblur2.cpp b/gm/imageblur2.cpp
index 19c92f3..b6f9ff5 100644
--- a/gm/imageblur2.cpp
+++ b/gm/imageblur2.cpp
@@ -70,11 +70,10 @@
                 textPaint.setTextSize(textSize);
 
                 for (int i = 0; i < testStringCount; i++) {
-                    canvas->drawText(kTestStrings[i],
-                                     strlen(kTestStrings[i]),
-                                     SkIntToScalar(x * dx),
-                                     SkIntToScalar(y * dy + textSize * i + textSize),
-                                     textPaint);
+                    canvas->drawString(kTestStrings[i],
+                                       SkIntToScalar(x * dx),
+                                       SkIntToScalar(y * dy + textSize * i + textSize),
+                                       textPaint);
                 }
                 canvas->restore();
             }
diff --git a/gm/imageblurtiled.cpp b/gm/imageblurtiled.cpp
index 7d846be..7f1cfbd 100644
--- a/gm/imageblurtiled.cpp
+++ b/gm/imageblurtiled.cpp
@@ -53,7 +53,7 @@
                 int posY = 0;
                 for (unsigned i = 0; i < SK_ARRAY_COUNT(str); i++) {
                     posY += 100;
-                    canvas->drawText(str[i], strlen(str[i]), SkIntToScalar(0),
+                    canvas->drawString(str[i], SkIntToScalar(0),
                                      SkIntToScalar(posY), textPaint);
                 }
                 canvas->restore();
diff --git a/gm/imagefilters.cpp b/gm/imagefilters.cpp
index 8855afd..dd6287b 100644
--- a/gm/imagefilters.cpp
+++ b/gm/imagefilters.cpp
@@ -123,7 +123,7 @@
         canvas->save();
         SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20);
         canvas->clipRRect(rr, true);
-        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), 0 });
+        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), nullptr, nullptr, 0 });
         canvas->drawColor(0x40FFFFFF);
         canvas->restore();
         canvas->restore();
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
index 8c702b2..6e34713 100644
--- a/gm/imagefiltersbase.cpp
+++ b/gm/imagefiltersbase.cpp
@@ -41,6 +41,9 @@
                                         SkIPoint* offset) const override {
         return nullptr;
     }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return nullptr;
+    }
 
 private:
     typedef SkImageFilter INHERITED;
@@ -83,6 +86,9 @@
         offset->set(0, 0);
         return sk_ref_sp<SkSpecialImage>(source);
     }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return sk_ref_sp(const_cast<IdentityImageFilter*>(this));
+    }
 
 private:
     IdentityImageFilter(sk_sp<SkImageFilter> input) : INHERITED(&input, 1, nullptr) {}
@@ -149,7 +155,7 @@
     sk_tool_utils::set_portable_typeface(&paint);
     paint.setTextSize(r.height()/2);
     paint.setTextAlign(SkPaint::kCenter_Align);
-    canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint);
+    canvas->drawString("Text", r.centerX(), r.centerY(), paint);
 }
 
 static void draw_bitmap(SkCanvas* canvas, const SkRect& r, sk_sp<SkImageFilter> imf) {
@@ -265,7 +271,7 @@
         SkAutoCanvasRestore acr(canvas, true);
         for (size_t i = 0; i < SK_ARRAY_COUNT(flags); ++i) {
             paint.setFlags(flags[i]);
-            canvas->drawText("Hamburgefons", 11, 0, 0, paint);
+            canvas->drawString("Hamburgefon", 0, 0, paint);
             canvas->translate(0, 40);
         }
     }
diff --git a/gm/imagefilterscropped.cpp b/gm/imagefilterscropped.cpp
index b8db8ab..5af6492 100644
--- a/gm/imagefilterscropped.cpp
+++ b/gm/imagefilterscropped.cpp
@@ -46,7 +46,7 @@
     sk_tool_utils::set_portable_typeface(&paint);
     paint.setTextSize(r.height()/2);
     paint.setTextAlign(SkPaint::kCenter_Align);
-    canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint);
+    canvas->drawString("Text", r.centerX(), r.centerY(), paint);
 }
 
 static void draw_bitmap(SkCanvas* canvas, const SkRect& r, sk_sp<SkImageFilter> imf) {
diff --git a/gm/imagemagnifier.cpp b/gm/imagemagnifier.cpp
index a494f4c..2585ea7 100644
--- a/gm/imagemagnifier.cpp
+++ b/gm/imagemagnifier.cpp
@@ -35,7 +35,7 @@
             paint.setColor(sk_tool_utils::color_to_565(rand.nextBits(24) | 0xFF000000));
             paint.setTextSize(rand.nextRangeScalar(0, 300));
             paint.setAntiAlias(true);
-            canvas->drawText(str, strlen(str), SkIntToScalar(x),
+            canvas->drawString(str, SkIntToScalar(x),
                              SkIntToScalar(y), paint);
         }
         canvas->restore();
diff --git a/gm/imagemasksubset.cpp b/gm/imagemasksubset.cpp
index 49ff3c5..9974af3 100644
--- a/gm/imagemasksubset.cpp
+++ b/gm/imagemasksubset.cpp
@@ -28,12 +28,8 @@
 public:
     MaskGenerator(const SkImageInfo& info) : INHERITED(info) {}
 
-    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor*,
-                     int*) override {
-        if (info.colorType() == kIndex_8_SkColorType) {
-            return false;
-        }
-
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&)
+    override {
         SkImageInfo surfaceInfo = info;
         if (kAlpha_8_SkColorType == info.colorType()) {
             surfaceInfo = surfaceInfo.makeColorSpace(nullptr);
@@ -63,7 +59,7 @@
         return make_mask(surface ? surface : SkSurface::MakeRaster(info));
     },
 
-    // SkImage_Generator
+    // SkImage_Lazy
     [](SkCanvas*, const SkImageInfo& info) -> sk_sp<SkImage> {
         return SkImage::MakeFromGenerator(skstd::make_unique<MaskGenerator>(info));
     },
diff --git a/gm/imageresizetiled.cpp b/gm/imageresizetiled.cpp
index 9fa6be0..257e0b2 100644
--- a/gm/imageresizetiled.cpp
+++ b/gm/imageresizetiled.cpp
@@ -43,7 +43,7 @@
                 int posY = 0;
                 for (unsigned i = 0; i < SK_ARRAY_COUNT(str); i++) {
                     posY += 100;
-                    canvas->drawText(str[i], strlen(str[i]), SkIntToScalar(0),
+                    canvas->drawString(str[i], SkIntToScalar(0),
                                      SkIntToScalar(posY), textPaint);
                 }
                 canvas->restore();
diff --git a/gm/imagescalealigned.cpp b/gm/imagescalealigned.cpp
index 3d74f0e..a1f6741 100644
--- a/gm/imagescalealigned.cpp
+++ b/gm/imagescalealigned.cpp
@@ -84,7 +84,7 @@
         surface->getCanvas()->drawRect(border, paint);
 
         paint.setColor(SK_ColorBLACK);
-        surface->getCanvas()->drawLine(start.x(), start.y(), end.x(), end.y(), paint);
+        surface->getCanvas()->drawLine(start, end, paint);
 
         paint.reset();
         paint.setColor(color);
diff --git a/gm/imagetoyuvplanes.cpp b/gm/imagetoyuvplanes.cpp
deleted file mode 100644
index 2ba2696..0000000
--- a/gm/imagetoyuvplanes.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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 <SkSurface.h>
-#include "gm.h"
-#include "SkBitmap.h"
-#include "SkGradientShader.h"
-#include "SkImage.h"
-
-static sk_sp<SkImage> create_image(GrContext* context, int width, int height) {
-    sk_sp<SkSurface> surface;
-    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
-    if (context) {
-        surface = SkSurface::MakeRenderTarget(context,  SkBudgeted::kYes, info);
-    } else {
-        surface = SkSurface::MakeRaster(info);
-    }
-    if (!surface) {
-        return nullptr;
-    }
-    // Create an RGB image from which we will extract planes
-    SkPaint paint;
-    constexpr SkColor kColors[] =
-            { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorWHITE };
-    SkScalar r = (width + height) / 4.f;
-    paint.setShader(SkGradientShader::MakeRadial(SkPoint::Make(0,0), r, kColors,
-                                                 nullptr, SK_ARRAY_COUNT(kColors),
-                                                 SkShader::kMirror_TileMode));
-
-    surface->getCanvas()->drawPaint(paint);
-    return surface->makeImageSnapshot();
-}
-
-DEF_SIMPLE_GM(image_to_yuv_planes, canvas, 120, 525) {
-    constexpr SkScalar kPad = 5.f;
-    constexpr int kImageSize = 32;
-
-    GrContext *context = canvas->getGrContext();
-    sk_sp<SkImage> rgbImage(create_image(context, kImageSize, kImageSize));
-    if (!rgbImage) {
-        return;
-    }
-
-    canvas->drawImage(rgbImage.get(), kPad, kPad);
-    // Test cases where all three planes are the same size, where just u and v are the same size,
-    // and where all differ.
-    constexpr SkISize kSizes[][3] = {
-        {{kImageSize, kImageSize}, {kImageSize  , kImageSize  }, {kImageSize,   kImageSize  }},
-        {{kImageSize, kImageSize}, {kImageSize/2, kImageSize/2}, {kImageSize/2, kImageSize/2}},
-        {{kImageSize, kImageSize}, {kImageSize/2, kImageSize/2}, {kImageSize/3, kImageSize/3}}
-    };
-
-    // A mix of rowbytes triples to go with the above sizes.
-    constexpr size_t kRowBytes[][3] {
-        {0, 0, 0},
-        {kImageSize, kImageSize/2 + 1, kImageSize},
-        {kImageSize + 13, kImageSize, kImageSize/3 + 8}
-    };
-
-
-    SkScalar x = kPad;
-    for (size_t s = 0; s < SK_ARRAY_COUNT(kSizes); ++s) {
-        SkScalar y = rgbImage->height() + 2 * kPad;
-
-        const SkISize *sizes = kSizes[s];
-        size_t realRowBytes[3];
-        for (int i = 0; i < 3; ++i) {
-            realRowBytes[i] = kRowBytes[s][i] ? kRowBytes[s][i] : kSizes[s][i].fWidth;
-        }
-        std::unique_ptr<uint8_t[]> yPlane(new uint8_t[realRowBytes[0] * sizes[0].fHeight]);
-        std::unique_ptr<uint8_t[]> uPlane(new uint8_t[realRowBytes[1] * sizes[1].fHeight]);
-        std::unique_ptr<uint8_t[]> vPlane(new uint8_t[realRowBytes[2] * sizes[2].fHeight]);
-
-        void *planes[3] = {yPlane.get(), uPlane.get(), vPlane.get()};
-
-        // Convert the RGB image to YUV planes using each YUV color space and draw the YUV planes
-        // to the canvas.
-        SkBitmap yuvBmps[3];
-        yuvBmps[0].setInfo(SkImageInfo::MakeA8(sizes[0].fWidth, sizes[0].fHeight), kRowBytes[s][0]);
-        yuvBmps[1].setInfo(SkImageInfo::MakeA8(sizes[1].fWidth, sizes[1].fHeight), kRowBytes[s][1]);
-        yuvBmps[2].setInfo(SkImageInfo::MakeA8(sizes[2].fWidth, sizes[2].fHeight), kRowBytes[s][2]);
-        yuvBmps[0].setPixels(yPlane.get());
-        yuvBmps[1].setPixels(uPlane.get());
-        yuvBmps[2].setPixels(vPlane.get());
-
-        for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
-            // Clear the planes so we don't accidentally see the old values if there is a bug in
-            // readYUV8Planes().
-            memset(yPlane.get(), 0, realRowBytes[0] * sizes[0].fHeight);
-            memset(uPlane.get(), 0, realRowBytes[1] * sizes[1].fHeight);
-            memset(vPlane.get(), 0, realRowBytes[2] * sizes[2].fHeight);
-            if (rgbImage->readYUV8Planes(sizes, planes, kRowBytes[s],
-                                         static_cast<SkYUVColorSpace>(space))) {
-                yuvBmps[0].notifyPixelsChanged();
-                yuvBmps[1].notifyPixelsChanged();
-                yuvBmps[2].notifyPixelsChanged();
-                for (int i = 0; i < 3; ++i) {
-                    canvas->drawBitmap(yuvBmps[i], x, y);
-                    y += kPad + yuvBmps[i].height();
-                }
-            }
-        }
-
-        x += rgbImage->width() + kPad;
-    }
-}
diff --git a/gm/internal_links.cpp b/gm/internal_links.cpp
index c873754..664b411 100644
--- a/gm/internal_links.cpp
+++ b/gm/internal_links.cpp
@@ -64,7 +64,7 @@
         sk_tool_utils::set_portable_typeface(&paint);
         paint.setTextSize(SkIntToScalar(25));
         paint.setColor(SK_ColorBLACK);
-        canvas->drawText(text, strlen(text), x, y, paint);
+        canvas->drawString(text, x, y, paint);
     }
 
     typedef GM INHERITED;
diff --git a/gm/lattice.cpp b/gm/lattice.cpp
index 46c5a94..55759d0 100644
--- a/gm/lattice.cpp
+++ b/gm/lattice.cpp
@@ -107,7 +107,7 @@
                                           padRight, padBottom);
         image_to_bitmap(image.get(), &bitmap);
 
-        const SkTSize<SkScalar> size[] = {
+        const SkSize size[] = {
             {  50,  50, }, // shrink in both axes
             {  50, 200, }, // shrink in X
             { 200,  50, }, // shrink in Y
diff --git a/gm/lcdblendmodes.cpp b/gm/lcdblendmodes.cpp
index 07b55c5..1345d23 100644
--- a/gm/lcdblendmodes.cpp
+++ b/gm/lcdblendmodes.cpp
@@ -133,7 +133,7 @@
                 paint.setShader(make_shader(r));
             }
             SkString string(SkBlendMode_Name(gModes[m]));
-            canvas->drawText(string.c_str(), string.size(), 0, y, paint);
+            canvas->drawString(string, 0, y, paint);
             y+=fTextHeight;
         }
     }
diff --git a/gm/lcdtext.cpp b/gm/lcdtext.cpp
index 5a549eb..1b377ba 100644
--- a/gm/lcdtext.cpp
+++ b/gm/lcdtext.cpp
@@ -58,7 +58,7 @@
         paint.setLCDRenderText(lcdRenderTextEnabled);
         paint.setTextSize(textHeight);
 
-        canvas->drawText(string.c_str(), string.size(), 0, y, paint);
+        canvas->drawString(string, 0, y, paint);
         y += textHeight;
     }
 
@@ -120,7 +120,7 @@
 
             paint.setTextSize(rec[i].fTextSize);
             ScaleAbout(canvas, rec[i].fScale, rec[i].fScale, loc.x(), loc.y());
-            canvas->drawText(rec[i].fText, strlen(rec[i].fText), loc.x(), loc.y(), paint);
+            canvas->drawString(rec[i].fText, loc.x(), loc.y(), paint);
         }
     }
 
@@ -138,7 +138,7 @@
     paint.setLCDRenderText(true);
     paint.setTextSize(20);
 
-    canvas->drawText("Hamburgefons", 12, 30, 30, paint);
+    canvas->drawString("Hamburgefons", 30, 30, paint);
 
     const bool gPreserveLCDText[] = { false, true };
 
@@ -146,18 +146,18 @@
     for (auto preserve : gPreserveLCDText) {
         preserve ? canvas->saveLayerPreserveLCDTextRequests(nullptr, nullptr)
                  : canvas->saveLayer(nullptr, nullptr);
-        if (preserve) {
+        if (preserve && !canvas->imageInfo().colorSpace()) {
             SkPaint noLCD = paint;
             noLCD.setLCDRenderText(false);
-            canvas->drawText("LCD not supported", 17, 30, 60, noLCD);
+            canvas->drawString("LCD not supported", 30, 60, noLCD);
         } else {
-            canvas->drawText("Hamburgefons", 12, 30, 60, paint);
+            canvas->drawString("Hamburgefons", 30, 60, paint);
         }
 
         SkPaint p;
         p.setColor(0xFFCCCCCC);
         canvas->drawRect(SkRect::MakeLTRB(25, 70, 200, 100), p);
-        canvas->drawText("Hamburgefons", 12, 30, 90, paint);
+        canvas->drawString("Hamburgefons", 30, 90, paint);
 
         canvas->restore();
         canvas->translate(0, 80);
diff --git a/gm/lightingshader2.cpp b/gm/lightingshader2.cpp
index c6c246d..edec063 100644
--- a/gm/lightingshader2.cpp
+++ b/gm/lightingshader2.cpp
@@ -171,25 +171,25 @@
                             canvas->translate(0.0f, LABEL_SIZE);
                             SkString label;
                             label.appendf("useNormalSource: %d", useNormalSource);
-                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
+                            canvas->drawString(label, 0.0f, 0.0f, labelPaint);
                         }
                         {
                             canvas->translate(0.0f, LABEL_SIZE);
                             SkString label;
                             label.appendf("useDiffuseShader: %d", useDiffuseShader);
-                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
+                            canvas->drawString(label, 0.0f, 0.0f, labelPaint);
                         }
                         {
                             canvas->translate(0.0f, LABEL_SIZE);
                             SkString label;
                             label.appendf("useTranslucentPaint: %d", useTranslucentPaint);
-                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
+                            canvas->drawString(label, 0.0f, 0.0f, labelPaint);
                         }
                         {
                             canvas->translate(0.0f, LABEL_SIZE);
                             SkString label;
                             label.appendf("useTranslucentShader: %d", useTranslucentShader);
-                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
+                            canvas->drawString(label, 0.0f, 0.0f, labelPaint);
                         }
 
                         canvas->restore();
diff --git a/gm/lightingshaderbevel.cpp b/gm/lightingshaderbevel.cpp
deleted file mode 100644
index 5adf3b8..0000000
--- a/gm/lightingshaderbevel.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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 "gm.h"
-#include "sk_tool_utils.h"
-#include "SkLightingShader.h"
-#include "SkNormalSource.h"
-#include "SkPath.h"
-#include "SkPoint3.h"
-#include "SkShader.h"
-
-
-namespace skiagm {
-
-// This GM exercises lighting shaders when used with bevel SkNormalSource objects.
-class LightingShaderBevelGM : public GM {
-public:
-    LightingShaderBevelGM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
-    }
-
-protected:
-    SkString onShortName() override {
-        return SkString("lightingshaderbevel");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(SkScalarCeilToInt(GRID_NUM_COLUMNS * GRID_CELL_WIDTH),
-                             SkScalarCeilToInt(GRID_NUM_ROWS    * GRID_CELL_WIDTH));
-    }
-
-    void onOnceBeforeDraw() override {
-        SkLights::Builder builder;
-        const SkVector3 kLightFromUpperRight = SkVector3::Make(0.788f, 0.394f, 0.473f);
-
-        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
-                                                     kLightFromUpperRight));
-        builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
-        fLights = builder.finish();
-
-        // fRect is assumed to be square throughout this file
-        fRect = SkRect::MakeIWH(kTexSize, kTexSize);
-        SkMatrix matrix;
-        SkRect bitmapBounds = SkRect::MakeIWH(kTexSize, kTexSize);
-        matrix.setRectToRect(bitmapBounds, fRect, SkMatrix::kFill_ScaleToFit);
-
-        SkBitmap diffuseMap = sk_tool_utils::create_checkerboard_bitmap(
-                kTexSize, kTexSize,
-                sk_tool_utils::color_to_565(0x0),
-                sk_tool_utils::color_to_565(0xFF804020),
-                8);
-        fDiffuse = SkShader::MakeBitmapShader(diffuseMap, SkShader::kClamp_TileMode,
-                                              SkShader::kClamp_TileMode, &matrix);
-
-        fConvexPath.moveTo(fRect.width() / 2.0f, 0.0f);
-        fConvexPath.lineTo(0.0f, fRect.height());
-        fConvexPath.lineTo(fRect.width(), fRect.height());
-        fConvexPath.close();
-
-        // Creating concave path
-        {
-            SkScalar x = 0.0f;
-            SkScalar y = fRect.height() / 2.0f;
-
-            const int NUM_SPIKES = 8;
-
-            const SkScalar x0 = x;
-            const SkScalar dx = fRect.width() / (NUM_SPIKES * 2);
-            const SkScalar dy = SK_Scalar1 * 10;
-
-
-            fConcavePath.moveTo(x, y + dy);
-            for (int i = 0; i < NUM_SPIKES; i++) {
-                x += dx;
-                fConcavePath.lineTo(x, y - dy);
-                x += dx;
-                fConcavePath.lineTo(x, y + dy);
-            }
-            fConcavePath.lineTo(x, y + (2 * dy));
-            fConcavePath.lineTo(x0, y + (2 * dy));
-            fConcavePath.close();
-        }
-    }
-
-    // Scales shape around origin, rotates shape around origin, then translates shape to origin
-    void positionCTM(SkCanvas *canvas, SkScalar scaleX, SkScalar scaleY, SkScalar rotate) const {
-        canvas->translate(kTexSize/2.0f, kTexSize/2.0f);
-        canvas->scale(scaleX, scaleY);
-        canvas->rotate(rotate);
-        canvas->translate(-kTexSize/2.0f, -kTexSize/2.0f);
-    }
-
-    enum Shape {
-        kCircle_Shape,
-        kRect_Shape,
-        kRRect_Shape,
-        kConvexPath_Shape,
-        kConcavePath_Shape,
-
-        kLast_Shape = kConcavePath_Shape
-    };
-    void drawShape(enum Shape shape, SkCanvas* canvas, SkScalar scaleX, SkScalar scaleY,
-                   SkScalar rotate, SkNormalSource::BevelType bevelType, SkScalar bevelHeight) {
-        canvas->save();
-
-        this->positionCTM(canvas, scaleX, scaleY, rotate);
-
-        SkPaint paint;
-
-        SkScalar bevelWidth = 10.0f;
-        sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeBevel(bevelType, bevelWidth,
-                                                                       bevelHeight);
-
-        paint.setShader(SkLightingShader::Make(fDiffuse, std::move(normalSource), fLights));
-        paint.setAntiAlias(true);
-        switch(shape) {
-            case kCircle_Shape:
-                canvas->drawCircle(fRect.centerX(), fRect.centerY(), fRect.width()/2.0f, paint);
-                break;
-            case kRect_Shape:
-                canvas->drawRect(fRect, paint);
-                break;
-            case kRRect_Shape:
-                canvas->drawRoundRect(fRect, 5.0f, 5.0f, paint);
-                break;
-            case kConvexPath_Shape:
-                canvas->drawPath(fConvexPath, paint);
-                break;
-            case kConcavePath_Shape:
-                canvas->drawPath(fConcavePath, paint);
-                break;
-            default:
-                SkDEBUGFAIL("Invalid shape enum for drawShape");
-        }
-
-        canvas->restore();
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-        SkPaint labelPaint;
-        labelPaint.setTypeface(sk_tool_utils::create_portable_typeface("sans-serif",
-                                                                       SkFontStyle()));
-        labelPaint.setAntiAlias(true);
-        labelPaint.setTextSize(LABEL_SIZE);
-
-        int gridNum = 0;
-
-        // Running through all possible parameter combinations
-        for (auto bevelType : {SkNormalSource::BevelType::kLinear,
-                               SkNormalSource::BevelType::kRoundedIn,
-                               SkNormalSource::BevelType::kRoundedOut}) {
-            for (SkScalar bevelHeight: {-7.0f, 7.0f}) {
-                for (int shapeInt = 0; shapeInt < NUM_SHAPES; shapeInt++) {
-                    Shape shape = (Shape)shapeInt;
-
-                    // Determining position
-                    SkScalar xPos = (gridNum / GRID_NUM_ROWS) * GRID_CELL_WIDTH;
-                    SkScalar yPos = (gridNum % GRID_NUM_ROWS) * GRID_CELL_WIDTH;
-
-                    canvas->save();
-
-                    canvas->translate(xPos, yPos);
-                    this->drawShape(shape, canvas, 1.0f, 1.0f, 0.f, bevelType, bevelHeight);
-                    // Drawing labels
-                    canvas->translate(0.0f, SkIntToScalar(kTexSize));
-                    {
-                        canvas->translate(0.0f, LABEL_SIZE);
-                        SkString label;
-                        label.append("bevelType: ");
-                        switch (bevelType) {
-                            case SkNormalSource::BevelType::kLinear:
-                                label.append("linear");
-                                break;
-                            case SkNormalSource::BevelType::kRoundedIn:
-                                label.append("roundedIn");
-                                break;
-                            case SkNormalSource::BevelType::kRoundedOut:
-                                label.append("roundedOut");
-                                break;
-                        }
-                        canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
-                    }
-                    {
-                        canvas->translate(0.0f, LABEL_SIZE);
-                        SkString label;
-                        label.appendf("bevelHeight: %.1f", bevelHeight);
-                        canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
-                    }
-
-                    canvas->restore();
-
-                    gridNum++;
-                }
-            }
-        }
-
-        // Testing rotation
-        for (int shapeInt = 0; shapeInt < NUM_SHAPES; shapeInt++) {
-            Shape shape = (Shape)shapeInt;
-
-            // Determining position
-            SkScalar xPos = (gridNum / GRID_NUM_ROWS) * GRID_CELL_WIDTH;
-            SkScalar yPos = (gridNum % GRID_NUM_ROWS) * GRID_CELL_WIDTH;
-
-            canvas->save();
-
-            canvas->translate(xPos, yPos);
-            this->drawShape(shape, canvas, SK_ScalarRoot2Over2, SK_ScalarRoot2Over2, 45.0f,
-                            SkNormalSource::BevelType::kLinear, 7.0f);
-
-            // Drawing labels
-            canvas->translate(0.0f, SkIntToScalar(kTexSize));
-            {
-                canvas->translate(0.0f, LABEL_SIZE);
-                SkString label;
-                label.appendf("bevelType: linear");
-                canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
-            }
-            {
-                canvas->translate(0.0f, LABEL_SIZE);
-                SkString label;
-                label.appendf("bevelHeight: %.1f", 7.0f);
-                canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
-            }
-            {
-                canvas->translate(0.0f, LABEL_SIZE);
-                SkString label;
-                label.appendf("rotated");
-                canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
-            }
-
-            canvas->restore();
-
-            gridNum++;
-        }
-
-        // Making sure NUM_COMBINATIONS_PER_SHAPE is set correctly
-        SkASSERT(gridNum == (NUM_COMBINATIONS_PER_SHAPE*NUM_SHAPES));
-    }
-
-private:
-    static constexpr int kTexSize = 96;
-    static constexpr int NUM_SHAPES = kLast_Shape + 1;
-    static constexpr int NUM_COMBINATIONS_PER_SHAPE = 7;
-    static constexpr int GRID_NUM_ROWS = NUM_SHAPES;
-    static constexpr int GRID_NUM_COLUMNS = NUM_COMBINATIONS_PER_SHAPE;
-    static constexpr SkScalar LABEL_SIZE = 10.0f;
-    static constexpr int NUM_LABELS_PER_CELL = 3;
-    static constexpr SkScalar GRID_CELL_WIDTH = kTexSize + 10.0f + NUM_LABELS_PER_CELL * LABEL_SIZE;
-
-    sk_sp<SkShader> fDiffuse;
-
-    SkRect fRect;
-    SkPath fConvexPath;
-    SkPath fConcavePath;
-    sk_sp<SkLights> fLights;
-
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new LightingShaderBevelGM;)
-}
diff --git a/gm/linepaths.cpp b/gm/linepaths.cpp
index 2583ff1..94a2979 100644
--- a/gm/linepaths.cpp
+++ b/gm/linepaths.cpp
@@ -83,10 +83,10 @@
         const char titleClose[] = "Line Closed Drawn Into Rectangle Clips With "
             "Indicated Style, Fill and Linecaps, with stroke width 10";
         const char* title = doClose ? titleClose : titleNoClose;
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
@@ -125,18 +125,15 @@
                     labelPaint.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&labelPaint);
                     labelPaint.setTextSize(10 * SK_Scalar1);
-                    canvas->drawText(gStyles[style].fName,
-                                        strlen(gStyles[style].fName),
-                                        0, rect.height() + 12 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gFills[fill].fName,
-                                        strlen(gFills[fill].fName),
-                                        0, rect.height() + 24 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gCaps[cap].fName,
-                                        strlen(gCaps[cap].fName),
-                                        0, rect.height() + 36 * SK_Scalar1,
-                                        labelPaint);
+                    canvas->drawString(gStyles[style].fName,
+                                       0, rect.height() + 12 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gFills[fill].fName,
+                                       0, rect.height() + 24 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gCaps[cap].fName,
+                                       0, rect.height() + 36 * SK_Scalar1,
+                                       labelPaint);
                 }
                 canvas->restore();
             }
diff --git a/gm/localmatriximageshader.cpp b/gm/localmatriximageshader.cpp
index 2c9fcd2..f63497d 100644
--- a/gm/localmatriximageshader.cpp
+++ b/gm/localmatriximageshader.cpp
@@ -28,8 +28,7 @@
     SkMatrix translate = SkMatrix::MakeTrans(100.0f, 0.0f);
     SkMatrix rotate;
     rotate.setRotate(45.0f);
-    sk_sp<SkShader> redImageShader = redImage->makeShader(SkShader::TileMode::kClamp_TileMode,
-            SkShader::TileMode::kClamp_TileMode, &translate);
+    sk_sp<SkShader> redImageShader = redImage->makeShader(&translate);
     sk_sp<SkShader> redLocalMatrixShader = redImageShader->makeWithLocalMatrix(rotate);
 
     // Rotate about the origin will happen first.
@@ -38,8 +37,7 @@
     canvas->drawIRect(SkIRect::MakeWH(250, 250), paint);
 
     sk_sp<SkImage> blueImage = make_image(canvas, SK_ColorBLUE);
-    sk_sp<SkShader> blueImageShader = blueImage->makeShader(SkShader::TileMode::kClamp_TileMode,
-            SkShader::TileMode::kClamp_TileMode, &rotate);
+    sk_sp<SkShader> blueImageShader = blueImage->makeShader(&rotate);
     sk_sp<SkShader> blueLocalMatrixShader = blueImageShader->makeWithLocalMatrix(translate);
 
     // Translate will happen first.
diff --git a/gm/makecolorspace.cpp b/gm/makecolorspace.cpp
new file mode 100644
index 0000000..af0b3d4
--- /dev/null
+++ b/gm/makecolorspace.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 "gm.h"
+#include "Resources.h"
+#include "SkCodec.h"
+#include "SkColorSpace_Base.h"
+#include "SkImage.h"
+#include "SkImagePriv.h"
+
+sk_sp<SkImage> make_raster_image(const char* path, SkTransferFunctionBehavior behavior) {
+    SkString resourcePath = GetResourcePath(path);
+    sk_sp<SkData> resourceData = SkData::MakeFromFileName(resourcePath.c_str());
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(resourceData));
+
+    SkBitmap bitmap;
+    bitmap.allocPixels(codec->getInfo());
+
+    SkCodec::Options opts;
+    opts.fPremulBehavior = behavior;
+    codec->getPixels(codec->getInfo(), bitmap.getPixels(), bitmap.rowBytes(), &opts,
+                     nullptr, nullptr);
+    return SkImage::MakeFromBitmap(bitmap);
+}
+
+sk_sp<SkImage> make_color_space(sk_sp<SkImage> orig, sk_sp<SkColorSpace> colorSpace,
+                                SkTransferFunctionBehavior behavior) {
+    sk_sp<SkImage> xform = orig->makeColorSpace(colorSpace, behavior);
+
+    // Assign an sRGB color space on the xformed image, so we can see the effects of the xform
+    // when we draw.
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+    if (colorSpace->gammaIsLinear()) {
+        srgb = SkColorSpace::MakeSRGBLinear();
+    }
+    return SkImageMakeRasterCopyAndAssignColorSpace(xform.get(), srgb.get());
+}
+
+class MakeCSGM : public skiagm::GM {
+public:
+    MakeCSGM() {}
+
+protected:
+    SkString onShortName() override {
+        return SkString("makecolorspace");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(128*3, 128*4);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkTransferFunctionBehavior behavior = canvas->imageInfo().colorSpace() ?
+                SkTransferFunctionBehavior::kRespect : SkTransferFunctionBehavior::kIgnore;
+
+        sk_sp<SkColorSpace> wideGamut = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                              SkColorSpace::kAdobeRGB_Gamut);
+        sk_sp<SkColorSpace> wideGamutLinear = as_CSB(wideGamut)->makeLinearGamma();
+
+        // Lazy images
+        sk_sp<SkImage> opaqueImage = GetResourceAsImage("mandrill_128.png");
+        sk_sp<SkImage> premulImage = GetResourceAsImage("color_wheel.png");
+        canvas->drawImage(opaqueImage, 0.0f, 0.0f);
+        canvas->drawImage(make_color_space(opaqueImage, wideGamut, behavior), 128.0f, 0.0f);
+        canvas->drawImage(make_color_space(opaqueImage, wideGamutLinear, behavior), 256.0f, 0.0f);
+        canvas->drawImage(premulImage, 0.0f, 128.0f);
+        canvas->drawImage(make_color_space(premulImage, wideGamut, behavior), 128.0f, 128.0f);
+        canvas->drawImage(make_color_space(premulImage, wideGamutLinear, behavior), 256.0f, 128.0f);
+        canvas->translate(0.0f, 256.0f);
+
+        // Raster images
+        opaqueImage = make_raster_image("mandrill_128.png", behavior);
+        premulImage = make_raster_image("color_wheel.png", behavior);
+        canvas->drawImage(opaqueImage, 0.0f, 0.0f);
+        canvas->drawImage(make_color_space(opaqueImage, wideGamut, behavior), 128.0f, 0.0f);
+        canvas->drawImage(make_color_space(opaqueImage, wideGamutLinear, behavior), 256.0f, 0.0f);
+        canvas->drawImage(premulImage, 0.0f, 128.0f);
+        canvas->drawImage(make_color_space(premulImage, wideGamut, behavior), 128.0f, 128.0f);
+        canvas->drawImage(make_color_space(premulImage, wideGamutLinear, behavior), 256.0f, 128.0f);
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+DEF_GM(return new MakeCSGM;)
diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp
index 7c1a215..4af12e1 100644
--- a/gm/matrixconvolution.cpp
+++ b/gm/matrixconvolution.cpp
@@ -46,7 +46,7 @@
         paint.setShader(SkGradientShader::MakeLinear(
             pts, fColors, pos, 2, SkShader::kClamp_TileMode));
         const char* str = "e";
-        canvas.drawText(str, strlen(str), SkIntToScalar(-10), SkIntToScalar(80), paint);
+        canvas.drawString(str, SkIntToScalar(-10), SkIntToScalar(80), paint);
 
         // ... tag the data as sRGB, so color-aware devices do gamut adjustment, etc...
         fBitmap.setInfo(SkImageInfo::MakeS32(80, 80, kPremul_SkAlphaType));
diff --git a/gm/mipmap.cpp b/gm/mipmap.cpp
index 56a78f4..9769cda 100644
--- a/gm/mipmap.cpp
+++ b/gm/mipmap.cpp
@@ -38,7 +38,7 @@
     paint.setTextSize(30);
     SkString str;
     str.printf("scale %g %g", dst.width() / img->width(), dst.height() / img->height());
-//    canvas->drawText(str.c_str(), str.size(), 300, 100, paint);
+//    canvas->drawString(str, 300, 100, paint);
 
     canvas->translate(20, 20);
     for (int i = 0; i < 4; ++i) {
diff --git a/gm/morphology.cpp b/gm/morphology.cpp
index d1513c4..99603c1 100644
--- a/gm/morphology.cpp
+++ b/gm/morphology.cpp
@@ -36,8 +36,8 @@
         const char* str2 = "XYZ";
         paint.setColor(0xFFFFFFFF);
         paint.setTextSize(64);
-        canvas.drawText(str1, strlen(str1), 10, 55, paint);
-        canvas.drawText(str2, strlen(str2), 10, 110, paint);
+        canvas.drawString(str1, 10, 55, paint);
+        canvas.drawString(str2, 10, 110, paint);
     }
 
     SkISize onISize() override {
diff --git a/gm/multipicturedraw.cpp b/gm/multipicturedraw.cpp
index fe2281a..b18fb8a 100644
--- a/gm/multipicturedraw.cpp
+++ b/gm/multipicturedraw.cpp
@@ -10,6 +10,7 @@
 
 #include "SkColorFilter.h"
 #include "SkMultiPictureDraw.h"
+#include "SkPath.h"
 #include "SkPictureRecorder.h"
 #include "SkSurface.h"
 
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
index 4c6e7d2..90a0216 100644
--- a/gm/ninepatchstretch.cpp
+++ b/gm/ninepatchstretch.cpp
@@ -78,7 +78,7 @@
         // amount of bm that should not be stretched (unless we have to)
         const SkScalar fixed = SkIntToScalar(fBitmap.width() - fCenter.width());
 
-        const SkTSize<SkScalar> size[] = {
+        const SkSize size[] = {
             { fixed * 4 / 5, fixed * 4 / 5 },   // shrink in both axes
             { fixed * 4 / 5, fixed * 4 },       // shrink in X
             { fixed * 4,     fixed * 4 / 5 },   // shrink in Y
diff --git a/gm/ovals.cpp b/gm/ovals.cpp
index ec9bd75..d6afdcb 100644
--- a/gm/ovals.cpp
+++ b/gm/ovals.cpp
@@ -268,6 +268,26 @@
 
             canvas->restore();
         }
+
+        // reflected oval
+        for (int i = 0; i < fPaints.count(); ++i) {
+            SkRect oval = SkRect::MakeLTRB(-30, -30, 30, 30);
+            canvas->save();
+            // position the oval, and make it at off-integer coords.
+            canvas->translate(kXStart + SK_Scalar1 * kXStep * 5 + SK_Scalar1 / 4,
+                              kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4 +
+                              SK_ScalarHalf * kYStep);
+            canvas->rotate(90);
+            canvas->scale(1, -1);
+            canvas->scale(1, 0.66f);
+
+            SkColor color = genColor(&rand);
+            fPaints[i].setColor(color);
+
+            canvas->drawRect(oval, rectPaint);
+            canvas->drawOval(oval, fPaints[i]);
+            canvas->restore();
+        }
     }
 
 private:
diff --git a/gm/patch.cpp b/gm/patch.cpp
index c9831c4..1b6dbd1 100644
--- a/gm/patch.cpp
+++ b/gm/patch.cpp
@@ -7,7 +7,9 @@
 
 #include "gm.h"
 #include "SkGradientShader.h"
+#include "SkImage.h"
 #include "SkPatchUtils.h"
+#include "SkPath.h"
 
 static sk_sp<SkShader> make_shader() {
     const SkColor colors[] = {
@@ -24,13 +26,13 @@
     //draw control points
     SkPaint paint;
     SkPoint bottom[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getBottomCubic(cubics, bottom);
+    SkPatchUtils::GetBottomCubic(cubics, bottom);
     SkPoint top[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getTopCubic(cubics, top);
+    SkPatchUtils::GetTopCubic(cubics, top);
     SkPoint left[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getLeftCubic(cubics, left);
+    SkPatchUtils::GetLeftCubic(cubics, left);
     SkPoint right[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getRightCubic(cubics, right);
+    SkPatchUtils::GetRightCubic(cubics, right);
 
     paint.setColor(SK_ColorBLACK);
     paint.setStrokeWidth(0.5f);
@@ -63,65 +65,121 @@
     canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, right + 1, paint);
 }
 
-DEF_SIMPLE_GM(patch_primitive, canvas, 1500, 1100) {
-        SkPaint paint;
+// The order of the colors and points is clockwise starting at upper-left corner.
+const SkPoint gCubics[SkPatchUtils::kNumCtrlPts] = {
+    //top points
+    {100,100},{150,50},{250,150}, {300,100},
+    //right points
+    {250, 150},{350,250},
+    //bottom points
+    {300,300},{250,250},{150,350},{100,300},
+    //left points
+    {50,250},{150,150}
+};
 
-        // The order of the colors and points is clockwise starting at upper-left corner.
-        const SkPoint cubics[SkPatchUtils::kNumCtrlPts] = {
-            //top points
-            {100,100},{150,50},{250,150}, {300,100},
-            //right points
-            {250, 150},{350,250},
-            //bottom points
-            {300,300},{250,250},{150,350},{100,300},
-            //left points
-            {50,250},{150,150}
-        };
+const SkPoint gTexCoords[SkPatchUtils::kNumCorners] = {
+    {0.0f, 0.0f}, {100.0f, 0.0f}, {100.0f,100.0f}, {0.0f, 100.0f}
+};
 
-        const SkColor colors[SkPatchUtils::kNumCorners] = {
-            SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorCYAN
-        };
-        const SkPoint texCoords[SkPatchUtils::kNumCorners] = {
-            {0.0f, 0.0f}, {100.0f, 0.0f}, {100.0f,100.0f}, {0.0f, 100.0f}}
-        ;
 
-        const SkBlendMode modes[] = {
-            SkBlendMode::kSrc,
-            SkBlendMode::kDst,
-            SkBlendMode::kModulate,
-        };
+static void dopatch(SkCanvas* canvas, const SkColor colors[], sk_sp<SkImage> img = nullptr) {
+    SkPaint paint;
 
-        sk_sp<SkShader> shader(make_shader());
+    const SkBlendMode modes[] = {
+        SkBlendMode::kSrc,
+        SkBlendMode::kDst,
+        SkBlendMode::kModulate,
+    };
 
-        canvas->save();
-        for (int y = 0; y < 3; y++) {
-            for (int x = 0; x < 4; x++) {
-                canvas->save();
-                canvas->translate(x * 350.0f, y * 350.0f);
-                switch (x) {
-                    case 0:
-                        canvas->drawPatch(cubics, nullptr, nullptr, modes[y], paint);
-                        break;
-                    case 1:
-                        canvas->drawPatch(cubics, colors, nullptr, modes[y], paint);
-                        break;
-                    case 2:
-                        paint.setShader(shader);
-                        canvas->drawPatch(cubics, nullptr, texCoords, modes[y], paint);
-                        paint.setShader(nullptr);
-                        break;
-                    case 3:
-                        paint.setShader(shader);
-                        canvas->drawPatch(cubics, colors, texCoords, modes[y], paint);
-                        paint.setShader(nullptr);
-                        break;
-                    default:
-                        break;
-                }
+    SkPoint texStorage[4];
+    const SkPoint* tex = gTexCoords;
 
-                draw_control_points(canvas, cubics);
-                canvas->restore();
+    sk_sp<SkShader> shader;
+    if (img) {
+        SkScalar w = img->width();
+        SkScalar h = img->height();
+        shader = img->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+        texStorage[0].set(0, 0);
+        texStorage[1].set(w, 0);
+        texStorage[2].set(w, h);
+        texStorage[3].set(0, h);
+        tex = texStorage;
+    } else {
+        shader = make_shader();
+    }
+
+    canvas->save();
+    for (int y = 0; y < 3; y++) {
+        for (int x = 0; x < 4; x++) {
+            canvas->save();
+            canvas->translate(x * 350.0f, y * 350.0f);
+            switch (x) {
+                case 0:
+                    canvas->drawPatch(gCubics, nullptr, nullptr, modes[y], paint);
+                    break;
+                case 1:
+                    canvas->drawPatch(gCubics, colors, nullptr, modes[y], paint);
+                    break;
+                case 2:
+                    paint.setShader(shader);
+                    canvas->drawPatch(gCubics, nullptr, tex, modes[y], paint);
+                    paint.setShader(nullptr);
+                    break;
+                case 3:
+                    paint.setShader(shader);
+                    canvas->drawPatch(gCubics, colors, tex, modes[y], paint);
+                    paint.setShader(nullptr);
+                    break;
+                default:
+                    break;
             }
+
+            draw_control_points(canvas, gCubics);
+            canvas->restore();
         }
-        canvas->restore();
+    }
+    canvas->restore();
 }
+
+DEF_SIMPLE_GM(patch_primitive, canvas, 1500, 1100) {
+    const SkColor colors[SkPatchUtils::kNumCorners] = {
+        SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorCYAN
+    };
+    dopatch(canvas, colors);
+}
+#include "Resources.h"
+DEF_SIMPLE_GM(patch_image, canvas, 1500, 1100) {
+    const SkColor colors[SkPatchUtils::kNumCorners] = {
+        SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorCYAN
+    };
+    dopatch(canvas, colors, GetResourceAsImage("mandrill_128.png"));
+}
+DEF_SIMPLE_GM(patch_alpha, canvas, 1500, 1100) {
+    const SkColor colors[SkPatchUtils::kNumCorners] = {
+        SK_ColorRED, 0x0000FF00, SK_ColorBLUE, 0x00FF00FF,
+    };
+    dopatch(canvas, colors);
+}
+
+// These two should look the same (one patch, one simple path)
+DEF_SIMPLE_GM(patch_alpha_test, canvas, 550, 250) {
+    canvas->translate(-75, -75);
+
+    const SkColor colors[SkPatchUtils::kNumCorners] = {
+        0x80FF0000, 0x80FF0000, 0x80FF0000, 0x80FF0000,
+    };
+    SkPaint paint;
+    canvas->drawPatch(gCubics, colors, nullptr, SkBlendMode::kModulate, paint);
+
+    canvas->translate(300, 0);
+
+    SkPath path;
+    path.moveTo(gCubics[0]);
+    path.cubicTo(gCubics[ 1], gCubics[ 2], gCubics[ 3]);
+    path.cubicTo(gCubics[ 4], gCubics[ 5], gCubics[ 6]);
+    path.cubicTo(gCubics[ 7], gCubics[ 8], gCubics[ 9]);
+    path.cubicTo(gCubics[10], gCubics[11], gCubics[ 0]);
+    paint.setColor(colors[0]);
+    canvas->drawPath(path, paint);
+}
+
diff --git a/gm/path_stroke_with_zero_length.cpp b/gm/path_stroke_with_zero_length.cpp
index 0f6d200..f21d624 100644
--- a/gm/path_stroke_with_zero_length.cpp
+++ b/gm/path_stroke_with_zero_length.cpp
@@ -151,7 +151,7 @@
         SkScalar pathX = bounds.fLeft - 2;
         SkScalar pathY = bounds.fTop - 2;
         SkMatrix cMatrix = canvas->getTotalMatrix();
-        if (!canvas->readPixels(&offscreen, SkScalarRoundToInt(pathX + cMatrix.getTranslateX()),
+        if (!canvas->readPixels(offscreen, SkScalarRoundToInt(pathX + cMatrix.getTranslateX()),
                 SkScalarRoundToInt(pathY + cMatrix.getTranslateY()))) {
             return;
         }
diff --git a/gm/pdf_never_embed.cpp b/gm/pdf_never_embed.cpp
index 4fba40e..ae63432 100644
--- a/gm/pdf_never_embed.cpp
+++ b/gm/pdf_never_embed.cpp
@@ -53,5 +53,5 @@
 
     canvas->scale(1.0, 0.5);
     p.setColor(0xF0000080);
-    canvas->drawText(text, strlen(text), 30, 700, p);
+    canvas->drawString(text, 30, 700, p);
 }
diff --git a/gm/perlinnoise.cpp b/gm/perlinnoise.cpp
index b3ece9d..6af69ac 100644
--- a/gm/perlinnoise.cpp
+++ b/gm/perlinnoise.cpp
@@ -7,6 +7,16 @@
 
 #include "gm.h"
 #include "SkPerlinNoiseShader.h"
+#include "SkShader.h"
+
+namespace {
+
+enum class Type {
+    kFractalNoise,
+    kTurbulence,
+};
+
+} // anonymous ns
 
 class PerlinNoiseGM : public skiagm::GM {
 public:
@@ -33,11 +43,11 @@
         canvas->restore();
     }
 
-    void test(SkCanvas* canvas, int x, int y, SkPerlinNoiseShader::Type type,
+    void test(SkCanvas* canvas, int x, int y, Type type,
               float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed,
               bool stitchTiles) {
         SkISize tileSize = SkISize::Make(fSize.width() / 2, fSize.height() / 2);
-        sk_sp<SkShader> shader = (type == SkPerlinNoiseShader::kFractalNoise_Type) ?
+        sk_sp<SkShader> shader = (type == Type::kFractalNoise) ?
             SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves,
                                                   seed, stitchTiles ? &tileSize : nullptr) :
             SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves,
@@ -59,31 +69,31 @@
 
     virtual void onDraw(SkCanvas* canvas) {
         canvas->clear(SK_ColorBLACK);
-        test(canvas,   0,   0, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas,   0,   0, Type::kFractalNoise,
              0.1f, 0.1f, 0, 0, false);
-        test(canvas, 100,   0, SkPerlinNoiseShader::kTurbulence_Type,
+        test(canvas, 100,   0, Type::kTurbulence,
              0.1f, 0.1f, 0, 0, false);
 
-        test(canvas,   0, 100, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas,   0, 100, Type::kFractalNoise,
              0.1f, 0.1f, 2, 0, false);
-        test(canvas, 100, 100, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas, 100, 100, Type::kFractalNoise,
              0.05f, 0.1f, 1, 0, true);
 
-        test(canvas,   0, 200, SkPerlinNoiseShader::kTurbulence_Type,
+        test(canvas,   0, 200, Type::kTurbulence,
              0.1f, 0.1f, 1, 0, true);
-        test(canvas, 100, 200, SkPerlinNoiseShader::kTurbulence_Type,
+        test(canvas, 100, 200, Type::kTurbulence,
              0.2f, 0.4f, 5, 0, false);
 
-        test(canvas,   0, 300, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas,   0, 300, Type::kFractalNoise,
              0.1f, 0.1f, 3, 1, false);
-        test(canvas, 100, 300, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas, 100, 300, Type::kFractalNoise,
              0.1f, 0.1f, 3, 4, false);
 
         canvas->scale(0.75f, 1.0f);
 
-        test(canvas,   0, 400, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas,   0, 400, Type::kFractalNoise,
              0.1f, 0.1f, 2, 0, false);
-        test(canvas, 100, 400, SkPerlinNoiseShader::kFractalNoise_Type,
+        test(canvas, 100, 400, Type::kFractalNoise,
              0.1f, 0.05f, 1, 0, true);
     }
 
@@ -107,10 +117,10 @@
         return SkISize::Make(640, 480);
     }
 
-    void install(SkPaint* paint, SkPerlinNoiseShader::Type type,
+    void install(SkPaint* paint, Type type,
               float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed,
               bool stitchTiles) {
-        sk_sp<SkShader> shader = (type == SkPerlinNoiseShader::kFractalNoise_Type) ?
+        sk_sp<SkShader> shader = (type == Type::kFractalNoise) ?
             SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves,
                                                   seed, stitchTiles ? &fSize : nullptr) :
             SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves,
@@ -122,7 +132,7 @@
         canvas->translate(10, 10);
 
         SkPaint paint;
-        install(&paint, SkPerlinNoiseShader::kFractalNoise_Type, 0.1f, 0.1f, 2, 0, false);
+        install(&paint, Type::kFractalNoise, 0.1f, 0.1f, 2, 0, false);
 
         const SkScalar w = SkIntToScalar(fSize.width());
         const SkScalar h = SkIntToScalar(fSize.height());
diff --git a/gm/picture.cpp b/gm/picture.cpp
index 72d4159..5b3cdfb 100644
--- a/gm/picture.cpp
+++ b/gm/picture.cpp
@@ -7,6 +7,7 @@
 
 #include "gm.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "SkPictureRecorder.h"
 
 static sk_sp<SkPicture> make_picture() {
diff --git a/gm/pictureimagefilter.cpp b/gm/pictureimagefilter.cpp
index aaec850..587ed37 100644
--- a/gm/pictureimagefilter.cpp
+++ b/gm/pictureimagefilter.cpp
@@ -33,7 +33,7 @@
     paint.setColor(0xFFFFFFFF);
     paint.setTextSize(SkIntToScalar(96));
     const char* str = "e";
-    canvas->drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
+    canvas->drawString(str, SkIntToScalar(20), SkIntToScalar(70), paint);
     return recorder.finishRecordingAsPicture();
 }
 
@@ -50,7 +50,7 @@
     // this has to be small enough that it doesn't become a path
     paint.setTextSize(SkIntToScalar(36));
     const char* str = "e";
-    canvas->drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
+    canvas->drawString(str, SkIntToScalar(20), SkIntToScalar(70), paint);
     return recorder.finishRecordingAsPicture();
 }
 
diff --git a/gm/pictureimagegenerator.cpp b/gm/pictureimagegenerator.cpp
index 2154e52..4e465d1 100644
--- a/gm/pictureimagegenerator.cpp
+++ b/gm/pictureimagegenerator.cpp
@@ -12,6 +12,7 @@
 #include "SkGradientShader.h"
 #include "SkImageGenerator.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "SkPathOps.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -162,7 +163,8 @@
             SkImageInfo bmInfo = gen->getInfo().makeColorSpace(canvas->imageInfo().refColorSpace());
 
             SkBitmap bm;
-            SkAssertResult(gen->tryGenerateBitmap(&bm, bmInfo, nullptr));
+            bm.allocPixels(bmInfo);
+            SkAssertResult(gen->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()));
 
             const SkScalar x = kDrawSize * (i % kDrawsPerRow);
             const SkScalar y = kDrawSize * (i / kDrawsPerRow);
diff --git a/gm/pictureshadercache.cpp b/gm/pictureshadercache.cpp
new file mode 100644
index 0000000..0784222
--- /dev/null
+++ b/gm/pictureshadercache.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "gm.h"
+
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkShader.h"
+#include "SkSurface.h"
+
+class PictureShaderCacheGM : public skiagm::GM {
+public:
+    PictureShaderCacheGM(SkScalar tileSize)
+        : fTileSize(tileSize) {
+    }
+
+ protected:
+    void drawTile(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setColor(SK_ColorGREEN);
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setAntiAlias(true);
+
+        canvas->drawCircle(fTileSize / 4, fTileSize / 4, fTileSize / 4, paint);
+        canvas->drawRect(SkRect::MakeXYWH(fTileSize / 2, fTileSize / 2,
+                                          fTileSize / 2, fTileSize / 2), paint);
+
+        paint.setColor(SK_ColorRED);
+        canvas->drawLine(fTileSize / 2, fTileSize * 1 / 3,
+                         fTileSize / 2, fTileSize * 2 / 3, paint);
+        canvas->drawLine(fTileSize * 1 / 3, fTileSize / 2,
+                         fTileSize * 2 / 3, fTileSize / 2, paint);
+    }
+
+    void onOnceBeforeDraw() override {
+        SkPictureRecorder recorder;
+        SkCanvas* pictureCanvas = recorder.beginRecording(fTileSize, fTileSize, nullptr, 0);
+        this->drawTile(pictureCanvas);
+        fPicture = recorder.finishRecordingAsPicture();
+    }
+
+    SkString onShortName() override {
+        return SkString("pictureshadercache");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(100, 100);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setShader(SkShader::MakePictureShader(fPicture, SkShader::kRepeat_TileMode,
+                                                    SkShader::kRepeat_TileMode, nullptr,
+                                                    nullptr));
+
+        {
+            // Render in a funny color space that converts green to yellow.
+            SkMatrix44 greenToYellow(SkMatrix44::kIdentity_Constructor);
+            greenToYellow.setFloat(0, 1, 1.0f);
+            sk_sp<SkColorSpace> gty = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                            greenToYellow);
+            SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100, std::move(gty));
+            sk_sp<SkSurface> surface(SkSurface::MakeRaster(info));
+            surface->getCanvas()->drawRect(SkRect::MakeWH(fTileSize, fTileSize), paint);
+        }
+
+        // When we draw to the canvas, we should see green because we should *not* reuse the
+        // cached picture shader.
+        canvas->drawRect(SkRect::MakeWH(fTileSize, fTileSize), paint);
+    }
+
+private:
+    SkScalar         fTileSize;
+    sk_sp<SkPicture> fPicture;
+    SkBitmap         fBitmap;
+
+    typedef GM INHERITED;
+};
+
+DEF_GM(return new PictureShaderCacheGM(100);)
diff --git a/gm/pixelsnap.cpp b/gm/pixelsnap.cpp
index d664d97..f4e4582 100644
--- a/gm/pixelsnap.cpp
+++ b/gm/pixelsnap.cpp
@@ -55,7 +55,7 @@
             canvas->translate(0, SkIntToScalar(kLabelOffsetY));
             for (int i = 0; i <= kSubPixelSteps; ++i) {
                 offset.printf("%d", i);
-                canvas->drawText(offset.c_str(), offset.size(),
+                canvas->drawString(offset,
                                     0, i * kTrans + labelPaint.getTextSize(),
                                     labelPaint);
             }
@@ -66,7 +66,7 @@
             canvas->translate(SkIntToScalar(kLabelOffsetX), 0);
             for (int i = 0; i <= kSubPixelSteps; ++i) {
                 offset.printf("%d", i);
-                canvas->drawText(offset.c_str(), offset.size(),
+                canvas->drawString(offset,
                                     i * SkIntToScalar(kTrans), labelPaint.getTextSize(),
                                     labelPaint);
             }
diff --git a/gm/poly2poly.cpp b/gm/poly2poly.cpp
index 9434bf2..2b9b390 100644
--- a/gm/poly2poly.cpp
+++ b/gm/poly2poly.cpp
@@ -73,8 +73,7 @@
 }
 
 void SkJSCanvas::fillText(const char text[], double x, double y) {
-    fTarget->drawText(text, strlen(text),
-                      SkDoubleToScalar(x), SkDoubleToScalar(y), fFillPaint);
+    fTarget->drawString(text, SkDoubleToScalar(x), SkDoubleToScalar(y), fFillPaint);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/gm/quadpaths.cpp b/gm/quadpaths.cpp
index bac37b9..fb10685 100644
--- a/gm/quadpaths.cpp
+++ b/gm/quadpaths.cpp
@@ -90,10 +90,10 @@
         titlePaint.setTextSize(15 * SK_Scalar1);
         const char title[] = "Quad Drawn Into Rectangle Clips With "
                              "Indicated Style, Fill and Linecaps, with stroke width 10";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
@@ -132,18 +132,15 @@
                     labelPaint.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&labelPaint);
                     labelPaint.setTextSize(10 * SK_Scalar1);
-                    canvas->drawText(gStyles[style].fName,
-                                        strlen(gStyles[style].fName),
-                                        0, rect.height() + 12 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gFills[fill].fName,
-                                        strlen(gFills[fill].fName),
-                                        0, rect.height() + 24 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gCaps[cap].fName,
-                                        strlen(gCaps[cap].fName),
-                                        0, rect.height() + 36 * SK_Scalar1,
-                                        labelPaint);
+                    canvas->drawString(gStyles[style].fName,
+                                       0, rect.height() + 12 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gFills[fill].fName,
+                                       0, rect.height() + 24 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gCaps[cap].fName,
+                                       0, rect.height() + 36 * SK_Scalar1,
+                                       labelPaint);
                 }
                 canvas->restore();
             }
@@ -234,10 +231,10 @@
         titlePaint.setTextSize(15 * SK_Scalar1);
         const char title[] = "Quad Closed Drawn Into Rectangle Clips With "
                              "Indicated Style, Fill and Linecaps, with stroke width 10";
-        canvas->drawText(title, strlen(title),
-                            20 * SK_Scalar1,
-                            20 * SK_Scalar1,
-                            titlePaint);
+        canvas->drawString(title,
+                           20 * SK_Scalar1,
+                           20 * SK_Scalar1,
+                           titlePaint);
 
         SkRandom rand;
         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
@@ -276,18 +273,15 @@
                     labelPaint.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&labelPaint);
                     labelPaint.setTextSize(10 * SK_Scalar1);
-                    canvas->drawText(gStyles[style].fName,
-                                        strlen(gStyles[style].fName),
-                                        0, rect.height() + 12 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gFills[fill].fName,
-                                        strlen(gFills[fill].fName),
-                                        0, rect.height() + 24 * SK_Scalar1,
-                                        labelPaint);
-                    canvas->drawText(gCaps[cap].fName,
-                                        strlen(gCaps[cap].fName),
-                                        0, rect.height() + 36 * SK_Scalar1,
-                                        labelPaint);
+                    canvas->drawString(gStyles[style].fName,
+                                       0, rect.height() + 12 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gFills[fill].fName,
+                                       0, rect.height() + 24 * SK_Scalar1,
+                                       labelPaint);
+                    canvas->drawString(gCaps[cap].fName,
+                                       0, rect.height() + 36 * SK_Scalar1,
+                                       labelPaint);
                 }
                 canvas->restore();
             }
diff --git a/gm/radial_gradient_precision.cpp b/gm/radial_gradient_precision.cpp
new file mode 100644
index 0000000..b4e9a11
--- /dev/null
+++ b/gm/radial_gradient_precision.cpp
@@ -0,0 +1,22 @@
+/*
+ * 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 "gm.h"
+#include "SkGradientShader.h"
+
+// All we're looking for here is that we see a smooth gradient.
+DEF_SIMPLE_GM(radial_gradient_precision, canvas, 200, 200) {
+    SkPoint  center   = {100000, 100000};
+    SkScalar radius   = 40;
+    SkColor  colors[] = {SK_ColorBLACK, SK_ColorWHITE};
+
+    SkPaint p;
+    p.setShader(SkGradientShader::MakeRadial(center, radius,
+                                             colors, nullptr, SK_ARRAY_COUNT(colors),
+                                             SkShader::kRepeat_TileMode));
+    canvas->drawPaint(p);
+}
diff --git a/gm/readpixels.cpp b/gm/readpixels.cpp
index f757a49..69d22bb 100644
--- a/gm/readpixels.cpp
+++ b/gm/readpixels.cpp
@@ -91,10 +91,12 @@
                                     SkColorSpace::MakeSRGB());
 }
 
-static sk_sp<SkColorSpace> make_srgb_transfer_fn(const SkColorSpacePrimaries& primaries) {
+static sk_sp<SkColorSpace> make_parametric_transfer_fn(const SkColorSpacePrimaries& primaries) {
     SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
     SkAssertResult(primaries.toXYZD50(&toXYZD50));
-    return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f;
+    return SkColorSpace::MakeRGB(fn, toXYZD50);
 }
 
 static sk_sp<SkColorSpace> make_wide_gamut() {
@@ -108,7 +110,7 @@
     primaries.fBY = 0.0001f;
     primaries.fWX = 0.34567f;
     primaries.fWY = 0.35850f;
-    return make_srgb_transfer_fn(primaries);
+    return make_parametric_transfer_fn(primaries);
 }
 
 static sk_sp<SkColorSpace> make_small_gamut() {
@@ -121,7 +123,7 @@
     primaries.fBY = 0.16f;
     primaries.fWX = 0.3127f;
     primaries.fWY = 0.3290f;
-    return make_srgb_transfer_fn(primaries);
+    return make_parametric_transfer_fn(primaries);
 }
 
 static void draw_image(SkCanvas* canvas, SkImage* image, SkColorType dstColorType,
diff --git a/gm/rectangletexture.cpp b/gm/rectangletexture.cpp
index 1359d34..c71a51c 100644
--- a/gm/rectangletexture.cpp
+++ b/gm/rectangletexture.cpp
@@ -11,6 +11,7 @@
 
 #if SK_SUPPORT_GPU
 
+#include "GrBackendSurface.h"
 #include "GrContext.h"
 #include "GrGpu.h"
 #include "GrTest.h"
@@ -107,13 +108,11 @@
         GrGLTextureInfo info;
         info.fID = id;
         info.fTarget = TARGET;
-        GrBackendTextureDesc desc;
-        desc.fConfig = kRGBA_8888_GrPixelConfig;
-        desc.fWidth = width;
-        desc.fHeight = height;
-        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-        desc.fTextureHandle = reinterpret_cast<GrBackendObject>(&info);
-        if (sk_sp<SkImage> image = SkImage::MakeFromAdoptedTexture(context, desc)) {
+
+        GrBackendTexture rectangleTex(width, height, kRGBA_8888_GrPixelConfig, info);
+
+        if (sk_sp<SkImage> image = SkImage::MakeFromAdoptedTexture(context, rectangleTex,
+                                                                   kTopLeft_GrSurfaceOrigin)) {
             return image;
         }
         GR_GL_CALL(gl, DeleteTextures(1, &id));
@@ -139,7 +138,7 @@
             SkPaint paint;
             paint.setAntiAlias(true);
             const char* kMsg = "Could not create rectangle texture image.";
-            canvas->drawText(kMsg, strlen(kMsg), 10, 100, paint);
+            canvas->drawString(kMsg, 10, 100, paint);
             return;
         }
 
@@ -164,8 +163,7 @@
 
                 SkPaint clampPaint;
                 clampPaint.setFilterQuality(q);
-                clampPaint.setShader(rectImg->makeShader(SkShader::kClamp_TileMode,
-                                                         SkShader::kClamp_TileMode));
+                clampPaint.setShader(rectImg->makeShader());
                 canvas->drawRect(SkRect::MakeWH(1.5f * kWidth, 1.5f * kHeight), clampPaint);
                 canvas->translate(kWidth * 1.5f + kPad, 0);
 
diff --git a/gm/reveal.cpp b/gm/reveal.cpp
index 3e46555..a793b2d 100644
--- a/gm/reveal.cpp
+++ b/gm/reveal.cpp
@@ -9,7 +9,6 @@
 #include "sk_tool_utils.h"
 #include "SkAnimTimer.h"
 #include "SkBlurMaskFilter.h"
-#include "SkGaussianEdgeShader.h"
 #include "SkRRectsGaussianEdgeMaskFilter.h"
 #include "SkPath.h"
 #include "SkPathOps.h"
@@ -314,7 +313,6 @@
 class RevealGM : public GM {
 public:
     enum Mode {
-        kGaussianEdge_Mode,
         kBlurMask_Mode,
         kRRectsGaussianEdge_Mode,
 
@@ -386,20 +384,7 @@
 
                 // The goal is to replace this clipped draw (which clips the 
                 // shadow) with a draw using the geometric clip
-                if (kGaussianEdge_Mode == fMode) {
-                    canvas->save();
-                        clipObj->clip(canvas);
-
-                        // Draw with GaussianEdgeShader
-                        SkPaint paint;
-                        paint.setAntiAlias(true);
-                        // G channel is an F6.2 radius
-                        int iBlurRad = (int)(4.0f * fBlurRadius);
-                        paint.setColor(SkColorSetARGB(255, iBlurRad >> 8, iBlurRad & 0xFF, 0));
-                        paint.setShader(SkGaussianEdgeShader::Make());
-                        drawObj->draw(canvas, paint);
-                    canvas->restore();
-                } else if (kBlurMask_Mode == fMode) {
+                if (kBlurMask_Mode == fMode) {
                     SkPath clippedPath;
 
                     SkScalar sigma = fBlurRadius / 4.0f;
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index ac66d52..23f576b 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -12,7 +12,7 @@
 #include "GrRenderTargetContextPriv.h"
 #include "effects/GrRRectEffect.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 #endif
 #include "SkRRect.h"
 
@@ -111,14 +111,18 @@
                             GrPaint grPaint;
                             grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
                             grPaint.addCoverageFragmentProcessor(std::move(fp));
+                            grPaint.setColor4f(GrColor4f(0, 0, 0, 1.f));
 
                             SkRect bounds = rrect.getBounds();
                             bounds.outset(2.f, 2.f);
 
-                            std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                                    0xff000000, SkMatrix::I(), bounds, nullptr, nullptr));
-                            renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                                    std::move(grPaint), GrAAType::kNone, std::move(op));
+                            renderTargetContext->priv().testingOnly_addDrawOp(
+                                    GrNonAAFillRectOp::Make(std::move(grPaint),
+                                                            SkMatrix::I(),
+                                                            bounds,
+                                                            nullptr,
+                                                            nullptr,
+                                                            GrAAType::kNone));
                         } else {
                             drew = false;
                         }
diff --git a/gm/samplerstress.cpp b/gm/samplerstress.cpp
index 63873499..f328743 100644
--- a/gm/samplerstress.cpp
+++ b/gm/samplerstress.cpp
@@ -111,7 +111,7 @@
 
         canvas->clipPath(path, true); // AA is on
 
-        canvas->drawText("M", 1,
+        canvas->drawString("M",
                          SkIntToScalar(100), SkIntToScalar(100),
                          paint);
 
@@ -126,7 +126,7 @@
         paint2.setStyle(SkPaint::kStroke_Style);
         paint2.setStrokeWidth(1);
         sk_tool_utils::set_portable_typeface(&paint2);
-        canvas->drawText("M", 1,
+        canvas->drawString("M",
                          SkIntToScalar(100), SkIntToScalar(100),
                          paint2);
 
diff --git a/gm/savelayer.cpp b/gm/savelayer.cpp
index 98a00ca..dd7ff7b 100644
--- a/gm/savelayer.cpp
+++ b/gm/savelayer.cpp
@@ -8,7 +8,6 @@
 #include "gm.h"
 #include "sk_tool_utils.h"
 
-
 static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31;
 
 // This GM tests out the deprecated Android-specific unclipped saveLayer "feature".
@@ -17,7 +16,8 @@
 static void save_layer_unclipped(SkCanvas* canvas,
                                  SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
     SkRect rect = SkRect::MakeLTRB(l, t, r, b);
-    canvas->saveLayer({ &rect, nullptr, nullptr, SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag });
+    canvas->saveLayer({ &rect, nullptr, nullptr, nullptr, nullptr,
+                        SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag });
 }
 
 static void do_draw(SkCanvas* canvas) {
@@ -94,8 +94,8 @@
     for(int i = 1; i < 2; ++i) {
         canvas->translate(100 * i, 0);
         auto flag = i ? SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag : 0;
-        canvas->saveLayer({ &rect1, &paint1, nullptr, flag});
-        canvas->saveLayer({ &rect2, &paint2, nullptr, flag});
+        canvas->saveLayer({ &rect1, &paint1, nullptr, nullptr, nullptr, flag});
+        canvas->saveLayer({ &rect2, &paint2, nullptr, nullptr, nullptr, flag});
         canvas->drawRect(rect3, paint3);
         canvas->restore();
         canvas->restore();
@@ -122,3 +122,118 @@
     canvas->restore();
 };
 
+#include "SkBlurImageFilter.h"
+#include "SkGradientShader.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkSurface.h"
+
+static void draw_mask(SkCanvas* canvas, int size) {
+    const SkScalar cx = size * SK_ScalarHalf,
+                   cy = cx;
+    const SkColor colors[] = { 0x00000000, 0xffff0000, 0x00000000, 0xffff0000, 0x00000000,
+                               0xffff0000, 0x00000000, 0xffff0000, 0x00000000 };
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setShader(SkGradientShader::MakeSweep(cx, cy, colors, nullptr, SK_ARRAY_COUNT(colors)));
+    canvas->drawPaint(paint);
+
+    paint.setShader(SkGradientShader::MakeRadial({cx, cy}, size / 4, colors, nullptr, 2,
+                                                 SkShader::kClamp_TileMode));
+    canvas->drawCircle(cx, cy, size / 4, paint);
+}
+
+DEF_SIMPLE_GM(savelayer_clipmask, canvas, 1200, 1200) {
+    static constexpr int kSize = 100;
+    static constexpr SkRect kLayerBounds = { kSize * 0.25f, kSize * 0.25f,
+                                             kSize * 0.75f, kSize * 0.75f };
+    static constexpr struct {
+        const SkRect* bounds;
+        const SkScalar matrix[9];
+    } kConfigs[] = {
+        { nullptr, { 1     ,  0     ,   0,   0     , 1     ,   0,   0, 0, 1 } },
+        { nullptr, { 2     ,  0     ,   0,   0     , 2     ,   0,   0, 0, 1 } },
+        { nullptr, { 2     ,  0     , -50,   0     , 2     , -50,   0, 0, 1 } },
+        { nullptr, { 0.707f, -0.707f,  50,   0.707f, 0.707f, -20,   0, 0, 1 } },
+        { nullptr, { 0.5f  ,  0     ,  25,   0     , 0.5f  ,  25,   0, 0, 1 } },
+
+        { &kLayerBounds, { 1     ,  0     ,   0,   0     , 1     ,   0,   0, 0, 1 } },
+        { &kLayerBounds, { 2     ,  0     ,   0,   0     , 2     ,   0,   0, 0, 1 } },
+        { &kLayerBounds, { 2     ,  0     , -50,   0     , 2     , -50,   0, 0, 1 } },
+        { &kLayerBounds, { 0.707f, -0.707f,  50,   0.707f, 0.707f, -20,   0, 0, 1 } },
+        { &kLayerBounds, { 0.5f  ,  0     ,  25,   0     , 0.5f  ,  25,   0, 0, 1 } },
+    };
+
+    using MaskMakerFunc = sk_sp<SkImage> (*)(int size);
+    static const MaskMakerFunc kMaskMakers[] = {
+        [](int size) -> sk_sp<SkImage> {
+            auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(size, size));
+            draw_mask(surf->getCanvas(), size);
+            return surf->makeImageSnapshot();
+        },
+
+        [](int size) -> sk_sp<SkImage> {
+            auto surf = SkSurface::MakeRasterN32Premul(size, size);
+            draw_mask(surf->getCanvas(), size);
+            return surf->makeImageSnapshot();
+        },
+
+        [](int size) -> sk_sp<SkImage> {
+            SkPictureRecorder recorder;
+            draw_mask(recorder.beginRecording(size, size), size);
+            return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
+                                            SkISize::Make(size, size),
+                                            nullptr, nullptr,
+                                            SkImage::BitDepth::kU8,
+                                            SkColorSpace::MakeSRGB());
+        }
+    };
+
+    using PaintMakerFunc = SkPaint (*)();
+    static const PaintMakerFunc kPaintMakers[] = {
+        []() -> SkPaint { return SkPaint(); },
+        []() -> SkPaint {
+            SkPaint p; p.setImageFilter(SkBlurImageFilter::Make(2, 2, nullptr)); return p;
+        },
+        []() -> SkPaint { SkPaint p; p.setBlendMode(SkBlendMode::kSrcOut); return p; },
+    };
+
+    canvas->drawColor(0xffcccccc);
+
+    SkMatrix clipMatrix;
+    SkCanvas::SaveLayerRec rec;
+    rec.fClipMatrix = &clipMatrix;
+
+    for (const auto& paintMaker : kPaintMakers) {
+        auto layerPaint = paintMaker();
+        rec.fPaint = &layerPaint;
+
+        for (const auto& maskMaker : kMaskMakers) {
+            sk_sp<SkImage> mask = maskMaker(kSize);
+            rec.fClipMask = mask.get();
+
+            canvas->save();
+            for (const auto cfg : kConfigs) {
+                rec.fBounds = cfg.bounds;
+                clipMatrix.set9(cfg.matrix);
+                canvas->saveLayer(rec);
+
+                SkPaint paint;
+                paint.setColor(0xff0000ff);
+                canvas->drawRect(SkRect::MakeWH(50, 50), paint);
+                paint.setColor(0xffff0000);
+                canvas->drawRect(SkRect::MakeXYWH(50, 0, 50, 50), paint);
+                paint.setColor(0xff00ff00);
+                canvas->drawRect(SkRect::MakeXYWH(50, 50, 50, 50), paint);
+                paint.setColor(0xffffff00);
+                canvas->drawRect(SkRect::MakeXYWH(0, 50, 50, 50), paint);
+
+                canvas->restore();
+                canvas->translate(120, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, 120);
+        }
+    }
+}
diff --git a/gm/shadertext2.cpp b/gm/shadertext2.cpp
index 9b585bf..d23409a 100644
--- a/gm/shadertext2.cpp
+++ b/gm/shadertext2.cpp
@@ -107,14 +107,14 @@
         canvas->translate(0, bmp.height() + labelPaint.getTextSize() + 15.f);
 
         constexpr char kLabelLabel[] = "localM / canvasM";
-        canvas->drawText(kLabelLabel, strlen(kLabelLabel), 0, 0, labelPaint);
+        canvas->drawString(kLabelLabel, 0, 0, labelPaint);
         canvas->translate(0, 15.f);
 
         canvas->save();
         SkScalar maxLabelW = 0;
         canvas->translate(0, kPadY / 2 + kPointSize);
         for (int lm = 0; lm < localMatrices.count(); ++lm) {
-            canvas->drawText(matrices[lm].fLabel, strlen(matrices[lm].fLabel),
+            canvas->drawString(matrices[lm].fLabel,
                              0, labelPaint.getTextSize() - 1, labelPaint);
             SkScalar labelW = labelPaint.measureText(matrices[lm].fLabel,
                                                      strlen(matrices[lm].fLabel));
@@ -132,7 +132,7 @@
             for (int m = 0; m < matrices.count(); ++m) {
                 columnH = 0;
                 canvas->save();
-                canvas->drawText(matrices[m].fLabel, strlen(matrices[m].fLabel),
+                canvas->drawString(matrices[m].fLabel,
                                  0, labelPaint.getTextSize() - 1, labelPaint);
                 canvas->translate(0, kPadY / 2 + kPointSize);
                 columnH += kPadY / 2 + kPointSize;
@@ -178,8 +178,8 @@
                 SkScalar y = columnH + kPadY / 2;
                 SkScalar fillX = -outlinePaint.measureText(kFillLabel, strlen(kFillLabel)) - kPadX;
                 SkScalar strokeX = kPadX;
-                canvas->drawText(kFillLabel, strlen(kFillLabel), fillX, y, labelPaint);
-                canvas->drawText(kStrokeLabel, strlen(kStrokeLabel), strokeX, y, labelPaint);
+                canvas->drawString(kFillLabel, fillX, y, labelPaint);
+                canvas->drawString(kStrokeLabel, strokeX, y, labelPaint);
             }
         }
 }
diff --git a/gm/shadowmaps.cpp b/gm/shadowmaps.cpp
deleted file mode 100644
index e8222f4..0000000
--- a/gm/shadowmaps.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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 "gm.h"
-#include "sk_tool_utils.h"
-#include "SkPathEffect.h"
-#include "SkPictureRecorder.h"
-#include "SkShadowPaintFilterCanvas.h"
-#include "SkShadowShader.h"
-#include "SkSurface.h"
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-
-static sk_sp<SkPicture> make_test_picture(int width, int height) {
-    SkPictureRecorder recorder;
-
-    // LONG RANGE TODO: eventually add SkBBHFactory (bounding box factory)
-    SkCanvas* canvas = recorder.beginRecording(SkRect::MakeIWH(width, height));
-
-    SkASSERT(canvas->getTotalMatrix().isIdentity());
-    SkPaint paint;
-    paint.setColor(SK_ColorGRAY);
-
-    // LONG RANGE TODO: tag occluders
-    // LONG RANGE TODO: track number of IDs we need (hopefully less than 256)
-    //                  and determinate the mapping from z to id
-
-    // universal receiver, "ground"
-    canvas->drawRect(SkRect::MakeIWH(width, height), paint);
-
-    // TODO: Maybe add the ID here along with the depth
-
-    paint.setColor(0xFFEE8888);
-
-    canvas->translateZ(80);
-    canvas->drawRect(SkRect::MakeLTRB(200,150,350,300), paint);
-
-    paint.setColor(0xFF88EE88);
-
-    canvas->translateZ(80);
-    canvas->drawRect(SkRect::MakeLTRB(150,200,300,350), paint);
-
-    paint.setColor(0xFF8888EE);
-
-    canvas->translateZ(80);
-    canvas->drawRect(SkRect::MakeLTRB(100,100,250,250), paint);
-    // TODO: Add an assert that Z order matches painter's order
-    // TODO: think about if the Z-order always matching painting order is too strict
-
-    return recorder.finishRecordingAsPicture();
-}
-
-namespace skiagm {
-
-class ShadowMapsGM : public GM {
-public:
-    ShadowMapsGM() {
-        this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
-    }
-
-    void onOnceBeforeDraw() override {
-        // Create a light set consisting of
-        //   - bluish directional light pointing more right than down
-        //   - reddish directional light pointing more down than right
-        //   - soft white ambient light
-
-        SkLights::Builder builder;
-        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(0.2f, 0.3f, 0.4f),
-                                                     SkVector3::Make(0.2f, 0.1f, 1.0f)));
-        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(0.4f, 0.3f, 0.2f),
-                                                     SkVector3::Make(0.1f, 0.2f, 1.0f)));
-        builder.setAmbientLightColor(SkColor3f::Make(0.4f, 0.4f, 0.4f));
-        fLights = builder.finish();
-
-        fShadowParams.fShadowRadius = 4.0f;
-        fShadowParams.fBiasingConstant = 0.3f;
-        fShadowParams.fMinVariance = 1024;
-        fShadowParams.fType = SkShadowParams::kVariance_ShadowType;
-    }
-
-protected:
-    static constexpr int kWidth = 400;
-    static constexpr int kHeight = 400;
-
-    SkString onShortName() override {
-        return SkString("shadowmaps");
-    }
-
-    SkISize onISize() override {
-        return SkISize::Make(kWidth, kHeight);
-    }
-
-    void onDraw(SkCanvas* canvas) override {
-        // This picture stores the picture of the scene.
-        // It's used to generate the depth maps.
-        sk_sp<SkPicture> pic(make_test_picture(kWidth, kHeight));
-        canvas->setLights(fLights);
-        canvas->drawShadowedPicture(pic, nullptr, nullptr, fShadowParams);
-    }
-
-private:
-    sk_sp<SkLights> fLights;
-    SkShadowParams fShadowParams;
-    typedef GM INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_GM(return new ShadowMapsGM;)
-}
-
-#endif
diff --git a/gm/shadowutils.cpp b/gm/shadowutils.cpp
index 53d9b78..87c151e 100644
--- a/gm/shadowutils.cpp
+++ b/gm/shadowutils.cpp
@@ -12,24 +12,17 @@
 #include "SkShadowUtils.h"
 
 void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color, SkPoint3 lightPos,
-                 SkScalar lightR, bool isAmbient, uint32_t flags, SkResourceCache* cache) {
+                 SkScalar lightR, bool isAmbient, uint32_t flags) {
     SkScalar ambientAlpha = isAmbient ? .5f : 0.f;
     SkScalar spotAlpha = isAmbient ? 0.f : .5f;
     SkShadowUtils::DrawShadow(canvas, path, height, lightPos, lightR, ambientAlpha, spotAlpha,
-                              color, flags, cache);
+                              color, flags);
 }
 
 static constexpr int kW = 800;
 static constexpr int kH = 800;
 
-DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
-    // SkShadowUtils uses a cache of SkVertices meshes. The vertices are created in a local
-    // coordinate system and then translated when reused. The coordinate system depends on
-    // parameters to the generating draw. If other threads are hitting the cache while this GM is
-    // running then we may have different cache behavior leading to slight rendering differences.
-    // To avoid that we use our own isolated cache rather than the global cache.
-    SkResourceCache cache(1 << 20);
-
+void draw_paths(SkCanvas* canvas, bool hideOccluders) {
     SkTArray<SkPath> paths;
     paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
     SkRRect oddRRect;
@@ -41,9 +34,13 @@
     paths.push_back().addOval(SkRect::MakeWH(20, 60));
 
     static constexpr SkScalar kPad = 15.f;
-    static constexpr SkPoint3 kLightPos = {250, 400, 500};
     static constexpr SkScalar kLightR = 100.f;
     static constexpr SkScalar kHeight = 50.f;
+
+    // transform light position relative to canvas to handle tiling
+    SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400);
+    SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 };
+
     canvas->translate(3 * kPad, 3 * kPad);
     canvas->save();
     SkScalar x = 0;
@@ -54,7 +51,7 @@
     m->setRotate(33.f, 25.f, 25.f);
     m->postScale(1.2f, 0.8f, 25.f, 25.f);
     for (auto& m : matrices) {
-        for (auto flags : {kNone_ShadowFlag, kTransparentOccluder_ShadowFlag}) {
+        for (auto flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) {
             for (const auto& path : paths) {
                 SkRect postMBounds = path.getBounds();
                 m.mapRect(&postMBounds);
@@ -70,17 +67,27 @@
 
                 canvas->save();
                 canvas->concat(m);
-                draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags,
-                            &cache);
-                draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags,
-                            &cache);
+                draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR, true, flags);
+                draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR, false, flags);
 
                 // Draw the path outline in green on top of the ambient and spot shadows.
                 SkPaint paint;
-                paint.setColor(SK_ColorGREEN);
                 paint.setAntiAlias(true);
-                paint.setStyle(SkPaint::kStroke_Style);
-                paint.setStrokeWidth(0);
+                if (hideOccluders) {
+                    if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
+                        paint.setColor(SK_ColorCYAN);
+                    } else {
+                        paint.setColor(SK_ColorGREEN);
+                    }
+                    paint.setStyle(SkPaint::kStroke_Style);
+                    paint.setStrokeWidth(0);
+                } else {
+                    paint.setColor(SK_ColorLTGRAY);
+                    if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
+                        paint.setAlpha(128);
+                    }
+                    paint.setStyle(SkPaint::kFill_Style);
+                }
                 canvas->drawPath(path, paint);
                 canvas->restore();
 
@@ -98,7 +105,15 @@
         SkPaint paint;
         paint.setColor(SK_ColorBLACK);
         paint.setAntiAlias(true);
-        canvas->drawCircle(kLightPos.fX, kLightPos.fY, kLightR / 10.f, paint);
+        canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
         canvas->restore();
     }
 }
+
+DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
+    draw_paths(canvas, true);
+}
+
+DEF_SIMPLE_GM(shadow_utils_occl, canvas, kW, kH) {
+    draw_paths(canvas, false);
+}
diff --git a/gm/shapes_as_paths.cpp b/gm/shapes_as_paths.cpp
new file mode 100644
index 0000000..774ceed
--- /dev/null
+++ b/gm/shapes_as_paths.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 "gm.h"
+
+#include "SkAutoPixmapStorage.h"
+#include "SkImage.h"
+#include "SkPath.h"
+#include "SkSurface.h"
+
+namespace skiagm {
+
+static void draw_diff(SkCanvas* canvas, SkImage* imgA, SkImage* imgB) {
+    SkASSERT(imgA->dimensions() == imgB->dimensions());
+
+    int w = imgA->width(), h = imgA->height();
+
+    // First, draw the two images faintly overlaid
+    SkPaint paint;
+    paint.setAlpha(64);
+    paint.setBlendMode(SkBlendMode::kPlus);
+    canvas->drawImage(imgA, 0, 0, &paint);
+    canvas->drawImage(imgB, 0, 0, &paint);
+
+    // Next, read the pixels back, figure out if there are any differences
+    SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
+    SkAutoPixmapStorage pmapA;
+    SkAutoPixmapStorage pmapB;
+    pmapA.alloc(info);
+    pmapB.alloc(info);
+    if (!imgA->readPixels(pmapA, 0, 0) || !imgB->readPixels(pmapB, 0, 0)) {
+        return;
+    }
+
+    int maxDiffX = 0, maxDiffY = 0, maxDiff = 0;
+    SkBitmap highlight;
+    highlight.allocN32Pixels(w, h);
+    highlight.eraseColor(SK_ColorTRANSPARENT);
+
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint32_t pixelA = *pmapA.addr32(x, y);
+            uint32_t pixelB = *pmapB.addr32(x, y);
+            if (pixelA != pixelB) {
+                int diff =
+                    SkTAbs((int)(SkColorGetR(pixelA) - SkColorGetR(pixelB))) +
+                    SkTAbs((int)(SkColorGetG(pixelA) - SkColorGetG(pixelB))) +
+                    SkTAbs((int)(SkColorGetB(pixelA) - SkColorGetB(pixelB))) +
+                    SkTAbs((int)(SkColorGetA(pixelA) - SkColorGetA(pixelB)));
+                if (diff > maxDiff) {
+                    maxDiffX = x;
+                    maxDiffY = y;
+                    maxDiff = diff;
+                }
+                *highlight.getAddr32(x, y) = SkPackARGB32(0xA0, 0xA0, 0x00, 0x00);
+            }
+        }
+    }
+
+    SkPaint outline;
+    outline.setStyle(SkPaint::kStroke_Style);
+    outline.setColor(maxDiff == 0 ? 0xFF007F00 : 0xFF7F0000);
+
+    if (maxDiff > 0) {
+        // Call extra attention to the region we're going to zoom
+        SkPMColor yellow = SkPackARGB32(0xFF, 0xFF, 0xFF, 0x00);
+        *highlight.getAddr32(maxDiffX, maxDiffY) = yellow;
+        *highlight.getAddr32(SkTMax(maxDiffX - 1, 0), maxDiffY) = yellow;
+        *highlight.getAddr32(maxDiffX, SkTMax(maxDiffY - 1, 0)) = yellow;
+        *highlight.getAddr32(SkTMin(maxDiffX + 1, w - 1), maxDiffY) = yellow;
+        *highlight.getAddr32(maxDiffX, SkTMin(maxDiffY + 1, h - 1)) = yellow;
+
+        // Draw the overlay
+        canvas->drawBitmap(highlight, 0, 0);
+
+        // Draw zoom of largest pixel diff
+        SkBitmap bmpA, bmpB;
+        SkAssertResult(bmpA.installPixels(pmapA));
+        SkAssertResult(bmpB.installPixels(pmapB));
+        canvas->drawBitmapRect(bmpA, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10),
+                               SkRect::MakeXYWH(w, 0, w, h), nullptr);
+        canvas->drawBitmapRect(bmpB, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10),
+                               SkRect::MakeXYWH(2 * w, 0, w, h), nullptr);
+
+        // Add lines to separate zoom boxes
+        canvas->drawLine(w, 0, w, h, outline);
+        canvas->drawLine(2 * w, 0, 2 * w, h, outline);
+    }
+
+    // Draw outline of whole test region
+    canvas->drawRect(SkRect::MakeWH(3 * w, h), outline);
+}
+
+namespace {
+typedef std::function<void(SkCanvas*, const SkRect&, const SkPaint&)> ShapeDrawFunc;
+}
+
+/**
+ *  Iterates over a variety of rect shapes, paint parameters, and matrices, calling two different
+ *  user-supplied draw callbacks. Produces a grid clearly showing if the two callbacks produce the
+ *  same visual results in all cases.
+ */
+static void draw_rect_geom_diff_grid(SkCanvas* canvas, ShapeDrawFunc f1, ShapeDrawFunc f2) {
+    // Variables:
+    // - Fill, hairline, wide stroke
+    // - Axis aligned, rotated, scaled, scaled negative, perspective
+    // - Source geometry (normal, collapsed, inverted)
+    //
+    // Things not (yet?) tested:
+    // - AntiAlias on/off
+    // - StrokeAndFill
+    // - Cap/join
+    // - Anything even more elaborate...
+
+    const SkRect kRects[] = {
+        SkRect::MakeXYWH(10, 10, 30, 30),  // Normal
+        SkRect::MakeXYWH(10, 25, 30, 0),   // Collapsed
+        SkRect::MakeXYWH(10, 40, 30, -30), // Inverted
+    };
+
+    const struct { SkPaint::Style fStyle; SkScalar fStrokeWidth; } kStyles[] = {
+        { SkPaint::kFill_Style, 0 },   // Filled
+        { SkPaint::kStroke_Style, 0 }, // Hairline
+        { SkPaint::kStroke_Style, 5 }, // Wide stroke
+    };
+
+    SkMatrix mI = SkMatrix::I();
+    SkMatrix mRot;
+    mRot.setRotate(30, 25, 25);
+    SkMatrix mScale;
+    mScale.setScaleTranslate(0.5f, 1, 12.5f, 0);
+    SkMatrix mFlipX;
+    mFlipX.setScaleTranslate(-1, 1, 50, 0);
+    SkMatrix mFlipY;
+    mFlipY.setScaleTranslate(1, -1, 0, 50);
+    SkMatrix mFlipXY;
+    mFlipXY.setScaleTranslate(-1, -1, 50, 50);
+    SkMatrix mPersp;
+    mPersp.setIdentity();
+    mPersp.setPerspY(0.002f);
+
+    const SkMatrix* kMatrices[] = { &mI, &mRot, &mScale, &mFlipX, &mFlipY, &mFlipXY, &mPersp, };
+
+    canvas->translate(10, 10);
+
+    SkImageInfo info = canvas->imageInfo().makeWH(50, 50);
+    auto surface = canvas->makeSurface(info);
+    if (!surface) {
+        surface = SkSurface::MakeRasterN32Premul(50, 50);
+    }
+
+    for (const SkRect& rect : kRects) {
+        for (const auto& style : kStyles) {
+            canvas->save();
+
+            for (const SkMatrix* mat : kMatrices) {
+                SkPaint paint;
+                paint.setColor(SK_ColorWHITE);
+                paint.setAntiAlias(true);
+                paint.setStyle(style.fStyle);
+                paint.setStrokeWidth(style.fStrokeWidth);
+
+                // Do first draw
+                surface->getCanvas()->clear(SK_ColorBLACK);
+                surface->getCanvas()->save();
+                surface->getCanvas()->concat(*mat);
+                f1(surface->getCanvas(), rect, paint);
+                surface->getCanvas()->restore();
+                auto imgA = surface->makeImageSnapshot();
+
+                // Do second draw
+                surface->getCanvas()->clear(SK_ColorBLACK);
+                surface->getCanvas()->save();
+                surface->getCanvas()->concat(*mat);
+                f2(surface->getCanvas(), rect, paint);
+                surface->getCanvas()->restore();
+                auto imgB = surface->makeImageSnapshot();
+
+                draw_diff(canvas, imgA.get(), imgB.get());
+                canvas->translate(160, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, 60);
+        }
+    }
+}
+
+static const int kNumRows = 9;
+static const int kNumColumns = 7;
+static const int kTotalWidth = kNumColumns * 160 + 10;
+static const int kTotalHeight = kNumRows * 60 + 10;
+
+DEF_SIMPLE_GM_BG(rects_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
+    // Drawing a rect vs. adding it to a path and drawing the path, should produce same results.
+    auto rectDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
+        canvas->drawRect(rect, paint);
+    };
+    auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
+        SkPath path;
+        path.addRect(rect);
+        canvas->drawPath(path, paint);
+    };
+
+    draw_rect_geom_diff_grid(canvas, rectDrawFunc, pathDrawFunc);
+}
+
+DEF_SIMPLE_GM_BG(ovals_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
+    // Drawing an oval vs. adding it to a path and drawing the path, should produce same results.
+    auto ovalDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
+        canvas->drawOval(rect, paint);
+    };
+    auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
+        SkPath path;
+        path.addOval(rect);
+        canvas->drawPath(path, paint);
+    };
+
+    draw_rect_geom_diff_grid(canvas, ovalDrawFunc, pathDrawFunc);
+}
+
+DEF_SIMPLE_GM_BG(arcs_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
+    // Drawing an arc vs. adding it to a path and drawing the path, should produce same results.
+    auto arcDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
+        canvas->drawArc(rect, 10, 200, false, paint);
+    };
+    auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
+        SkPath path;
+        path.addArc(rect, 10, 200);
+        canvas->drawPath(path, paint);
+    };
+
+    draw_rect_geom_diff_grid(canvas, arcDrawFunc, pathDrawFunc);
+}
+
+}
diff --git a/gm/showmiplevels.cpp b/gm/showmiplevels.cpp
index 0dd19d5..3e2091f 100644
--- a/gm/showmiplevels.cpp
+++ b/gm/showmiplevels.cpp
@@ -93,7 +93,6 @@
 
     static void apply_gamma(const SkBitmap& bm) {
         return; // below is our experiment for sRGB correction
-        bm.lockPixels();
         for (int y = 0; y < bm.height(); ++y) {
             for (int x = 0; x < bm.width(); ++x) {
                 SkPMColor c = *bm.getAddr32(x, y);
@@ -121,7 +120,7 @@
 
     static void DrawAndFrame(SkCanvas* canvas, const SkBitmap& orig, SkScalar x, SkScalar y) {
         SkBitmap bm;
-        orig.copyTo(&bm);
+        sk_tool_utils::copy_to(&bm, orig.colorType(), orig);
         apply_gamma(bm);
 
         canvas->drawBitmap(bm, x, y, nullptr);
@@ -136,7 +135,6 @@
         SkScalar y = 4;
 
         SkPixmap prevPM;
-        baseBM.lockPixels();
         baseBM.peekPixels(&prevPM);
 
         SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
@@ -181,7 +179,6 @@
         };
 
         SkPixmap basePM;
-        orig.lockPixels();
         orig.peekPixels(&basePM);
         for (auto method : methods) {
             canvas->translate(orig.width()/2 + 8.0f, 0);
@@ -221,7 +218,14 @@
         return sk_tool_utils::copy_to_g8(dst, src);
     }
 
-    src.copyTo(dst, dstColorType);
+    const SkBitmap* srcPtr = &src;
+    SkBitmap tmp(src);
+    if (kRGB_565_SkColorType == dstColorType) {
+        tmp.setAlphaType(kOpaque_SkAlphaType);
+        srcPtr = &tmp;
+    }
+
+    sk_tool_utils::copy_to(dst, dstColorType, *srcPtr);
 }
 
 /**
diff --git a/gm/simpleaaclip.cpp b/gm/simpleaaclip.cpp
index 8f6c5c9..5e4067f 100644
--- a/gm/simpleaaclip.cpp
+++ b/gm/simpleaaclip.cpp
@@ -27,7 +27,7 @@
     // need to copy for deferred drawing test to work
     SkBitmap bm2;
 
-    bm.deepCopyTo(&bm2);
+    sk_tool_utils::copy_to(&bm2, bm.colorType(), bm);
 
     canvas->drawBitmap(bm2,
                        SK_Scalar1 * mask.fBounds.fLeft,
@@ -162,7 +162,7 @@
         int xOff = 0;
 
         for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
-            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName),
+            canvas->drawString(gOps[op].fName,
                              SkIntToScalar(75), SkIntToScalar(50),
                              textPaint);
 
diff --git a/gm/skbug_257.cpp b/gm/skbug_257.cpp
index fb77532..91c0a16 100644
--- a/gm/skbug_257.cpp
+++ b/gm/skbug_257.cpp
@@ -66,7 +66,7 @@
     sk_tool_utils::set_portable_typeface(&type);
     type.setColor(color);
     const char text[] = "HELLO WORLD";
-    canvas->drawText(text, strlen(text), 32, size / 2 + Y, type);
+    canvas->drawString(text, 32, size / 2 + Y, type);
     SkScalar lineSpacing = type.getFontSpacing();
     exercise_draw_pos_text(canvas, text, 32, size / 2 + Y + lineSpacing, type);
     exercise_draw_pos_text_h(canvas, text, 32,
diff --git a/gm/srcmode.cpp b/gm/srcmode.cpp
index 75721f1..e43933c 100644
--- a/gm/srcmode.cpp
+++ b/gm/srcmode.cpp
@@ -53,7 +53,7 @@
 static void draw_text(SkCanvas* canvas, const SkPaint& paint) {
     SkPaint p(paint);
     p.setTextSize(H/4);
-    canvas->drawText("Hamburge", 8, 0, H*2/3, p);
+    canvas->drawString("Hamburge", 0, H*2/3, p);
 }
 
 class SrcModeGM : public skiagm::GM {
diff --git a/gm/stroketext.cpp b/gm/stroketext.cpp
index f1bb327..ec939d1 100644
--- a/gm/stroketext.cpp
+++ b/gm/stroketext.cpp
@@ -31,7 +31,7 @@
 
     if (strokeWidth > 0) {
         p.setStyle(SkPaint::kFill_Style);
-        canvas->drawText("P", 1, loc.fX, loc.fY - 225, p);
+        canvas->drawString("P", loc.fX, loc.fY - 225, p);
         canvas->drawPosText("P", 1, &loc, p);
     }
 
@@ -39,7 +39,7 @@
     p.setStyle(SkPaint::kStroke_Style);
     p.setStrokeWidth(strokeWidth);
 
-    canvas->drawText("P", 1, loc.fX, loc.fY - 225, p);
+    canvas->drawString("P", loc.fX, loc.fY - 225, p);
     canvas->drawPosText("P", 1, &loc, p);
 }
 
diff --git a/gm/subsetshader.cpp b/gm/subsetshader.cpp
index c4ef5f7..a7029f4 100644
--- a/gm/subsetshader.cpp
+++ b/gm/subsetshader.cpp
@@ -7,6 +7,7 @@
 
 #include "Resources.h"
 #include "SkBitmap.h"
+#include "SkShader.h"
 #include "gm.h"
 
 DEF_SIMPLE_GM(bitmap_subset_shader, canvas, 256, 256) {
diff --git a/gm/surface.cpp b/gm/surface.cpp
index ee4decc..4b400ca 100644
--- a/gm/surface.cpp
+++ b/gm/surface.cpp
@@ -46,7 +46,7 @@
     paint.setTextSize(32);
     paint.setTextAlign(SkPaint::kCenter_Align);
     sk_tool_utils::set_portable_typeface(&paint);
-    canvas->drawText(label, strlen(label), W / 2, H * 3 / 4, paint);
+    canvas->drawString(label, W / 2, H * 3 / 4, paint);
 }
 
 class SurfacePropsGM : public skiagm::GM {
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp
index 5e0f189..b98aea7 100644
--- a/gm/texteffects.cpp
+++ b/gm/texteffects.cpp
@@ -188,7 +188,7 @@
             //  paint.setMaskFilter(nullptr);
             //  paint.setColor(SK_ColorBLACK);
 
-            canvas->drawText(str.c_str(), str.size(), x, y, paint);
+            canvas->drawString(str, x, y, paint);
 
             y += paint.getFontSpacing();
         }
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index 88bcc57..d49e059 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -18,7 +18,7 @@
 #include "SkGradientShader.h"
 #include "effects/GrTextureDomain.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 namespace skiagm {
 /**
@@ -133,11 +133,9 @@
                     }
                     const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y);
                     grPaint.addColorFragmentProcessor(std::move(fp));
-
-                    std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                            GrColor_WHITE, viewMatrix, renderRect, nullptr, nullptr));
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                            std::move(grPaint), GrAAType::kNone, std::move(op));
+                    renderTargetContext->priv().testingOnly_addDrawOp(
+                            GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect,
+                                                    nullptr, nullptr, GrAAType::kNone));
                     x += renderRect.width() + kTestPad;
                 }
                 y += renderRect.height() + kTestPad;
diff --git a/gm/tilemodes.cpp b/gm/tilemodes.cpp
index 009ad64..390ce4a 100644
--- a/gm/tilemodes.cpp
+++ b/gm/tilemodes.cpp
@@ -105,7 +105,7 @@
                 str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
 
                 p.setTextAlign(SkPaint::kCenter_Align);
-                canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+                canvas->drawString(str, x + r.width()/2, y, p);
 
                 x += r.width() * 4 / 3;
             }
@@ -142,7 +142,7 @@
                     p.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&p);
                     str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
-                    canvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
+                    canvas->drawString(str, x, y + r.height() * 2 / 3, p);
                 }
 
                 y += r.height() * 4 / 3;
@@ -224,7 +224,7 @@
 
         for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
             SkString str(gModeNames[kx]);
-            canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+            canvas->drawString(str, x + r.width()/2, y, p);
             x += r.width() * 4 / 3;
         }
 
@@ -235,7 +235,7 @@
             x = SkIntToScalar(16) + w;
 
             SkString str(gModeNames[ky]);
-            canvas->drawText(str.c_str(), str.size(), x, y + h/2, p);
+            canvas->drawString(str, x, y + h/2, p);
 
             x += SkIntToScalar(50);
             for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
diff --git a/gm/tilemodes_scaled.cpp b/gm/tilemodes_scaled.cpp
index 807d63b..3833fa2 100644
--- a/gm/tilemodes_scaled.cpp
+++ b/gm/tilemodes_scaled.cpp
@@ -107,7 +107,7 @@
                 str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
 
                 p.setTextAlign(SkPaint::kCenter_Align);
-                canvas->drawText(str.c_str(), str.size(), scale*(x + r.width()/2), y, p);
+                canvas->drawString(str, scale*(x + r.width()/2), y, p);
 
                 x += r.width() * 4 / 3;
             }
@@ -145,7 +145,7 @@
                     p.setAntiAlias(true);
                     sk_tool_utils::set_portable_typeface(&p);
                     str.printf("%s, %s", gColorTypeNames[i], gFilterNames[j]);
-                    canvas->drawText(str.c_str(), str.size(), scale*x, scale*(y + r.height() * 2 / 3), p);
+                    canvas->drawString(str, scale*x, scale*(y + r.height() * 2 / 3), p);
                 }
 
                 y += r.height() * 4 / 3;
@@ -228,7 +228,7 @@
 
         for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
             SkString str(gModeNames[kx]);
-            canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+            canvas->drawString(str, x + r.width()/2, y, p);
             x += r.width() * 4 / 3;
         }
 
@@ -239,7 +239,7 @@
             x = SkIntToScalar(16) + w;
 
             SkString str(gModeNames[ky]);
-            canvas->drawText(str.c_str(), str.size(), x, y + h/2, p);
+            canvas->drawString(str, x, y + h/2, p);
 
             x += SkIntToScalar(50);
             for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
diff --git a/gm/tinybitmap.cpp b/gm/tinybitmap.cpp
index 0f0f137..f8103cf 100644
--- a/gm/tinybitmap.cpp
+++ b/gm/tinybitmap.cpp
@@ -15,17 +15,13 @@
 
 static SkBitmap make_bitmap() {
     const SkPMColor c[] = { SkPackARGB32(0x80, 0x80, 0, 0) };
-    SkColorTable* ctable = new SkColorTable(c, SK_ARRAY_COUNT(c));
 
     SkBitmap bm;
     bm.allocPixels(SkImageInfo::Make(1, 1, kIndex_8_SkColorType,
                                      kPremul_SkAlphaType),
-                   nullptr, ctable);
-    ctable->unref();
+                   SkColorTable::Make(c, SK_ARRAY_COUNT(c)));
 
-    bm.lockPixels();
     *bm.getAddr8(0, 0) = 0;
-    bm.unlockPixels();
     return bm;
 }
 
diff --git a/gm/variedtext.cpp b/gm/variedtext.cpp
index 563d138..c0f6cbf 100644
--- a/gm/variedtext.cpp
+++ b/gm/variedtext.cpp
@@ -116,7 +116,7 @@
             canvas->save();
                 canvas->clipRect(fClipRects[i]);
                 canvas->translate(fPositions[i].fX, fPositions[i].fY);
-                canvas->drawText(fStrings[i].c_str(), fStrings[i].size(), 0, 0, fPaint);
+                canvas->drawString(fStrings[i], 0, 0, fPaint);
             canvas->restore();
         }
 
diff --git a/gm/vertices.cpp b/gm/vertices.cpp
index 4bf9f87..7c7c011 100644
--- a/gm/vertices.cpp
+++ b/gm/vertices.cpp
@@ -89,12 +89,10 @@
     sk_sp<SkShader>         fShader1;
     sk_sp<SkShader>         fShader2;
     sk_sp<SkColorFilter>    fColorFilter;
-    bool                    fUseObject;
     SkScalar                fShaderScale;
 
 public:
-    VerticesGM(bool useObject, SkScalar shaderScale = 1)
-        : fUseObject(useObject), fShaderScale(shaderScale) {}
+    VerticesGM(SkScalar shaderScale) : fShaderScale(shaderScale) {}
 
 protected:
 
@@ -107,9 +105,6 @@
 
     SkString onShortName() override {
         SkString name("vertices");
-        if (fUseObject) {
-            name.append("_object");
-        }
         if (fShaderScale != 1) {
             name.append("_scaled_shader");
         }
@@ -173,16 +168,10 @@
 
                             const SkColor* colors = attrs.fHasColors ? fColors : nullptr;
                             const SkPoint* texs = attrs.fHasTexs ? fTexs : nullptr;
-                            if (fUseObject) {
-                                auto v = SkVertices::MakeCopy(SkCanvas::kTriangleFan_VertexMode,
-                                                              kMeshVertexCnt, fPts, texs, colors,
-                                                              kMeshIndexCnt, kMeshFan);
-                                canvas->drawVertices(v, mode, paint);
-                            } else {
-                                canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode,
-                                                     kMeshVertexCnt, fPts, texs, colors, mode,
-                                                     kMeshFan, kMeshIndexCnt, paint);
-                            }
+                            auto v = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,
+                                                          kMeshVertexCnt, fPts, texs, colors,
+                                                          kMeshIndexCnt, kMeshFan);
+                            canvas->drawVertices(v, mode, paint);
                             canvas->translate(40, 0);
                             ++x;
                         }
@@ -200,14 +189,13 @@
 
 /////////////////////////////////////////////////////////////////////////////////////
 
-DEF_GM(return new VerticesGM(true);)
-DEF_GM(return new VerticesGM(false);)
-DEF_GM(return new VerticesGM(false, 1 / kShaderSize);)
+DEF_GM(return new VerticesGM(1);)
+DEF_GM(return new VerticesGM(1 / kShaderSize);)
 
-static void draw_batching(SkCanvas* canvas, bool useObject) {
+static void draw_batching(SkCanvas* canvas) {
     // Triangle fans can't batch so we convert to regular triangles,
     static constexpr int kNumTris = kMeshIndexCnt - 2;
-    SkVertices::Builder builder(SkCanvas::kTriangles_VertexMode, kMeshVertexCnt, 3 * kNumTris,
+    SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, kMeshVertexCnt, 3 * kNumTris,
                                 SkVertices::kHasColors_BuilderFlag |
                                 SkVertices::kHasTexCoords_BuilderFlag);
 
@@ -245,14 +233,9 @@
                 paint.setShader(useShader ? shader : nullptr);
 
                 const SkPoint* t = useTex ? texs : nullptr;
-                if (useObject) {
-                    auto v = SkVertices::MakeCopy(SkCanvas::kTriangles_VertexMode, kMeshVertexCnt,
-                                                  pts, t, colors, kNumTris * 3, indices);
-                    canvas->drawVertices(v, SkBlendMode::kModulate, paint);
-                } else {
-                    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, kMeshVertexCnt, pts,
-                                         t, colors, indices, kNumTris * 3, paint);
-                }
+                auto v = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, kMeshVertexCnt,
+                                              pts, t, colors, kNumTris * 3, indices);
+                canvas->drawVertices(v, SkBlendMode::kModulate, paint);
                 canvas->restore();
             }
             canvas->translate(0, 120);
@@ -263,7 +246,7 @@
 
 // This test exists to exercise batching in the gpu backend.
 DEF_SIMPLE_GM(vertices_batching, canvas, 100, 500) {
-    draw_batching(canvas, false);
+    draw_batching(canvas);
     canvas->translate(50, 0);
-    draw_batching(canvas, true);
+    draw_batching(canvas);
 }
diff --git a/gm/verttext2.cpp b/gm/verttext2.cpp
index d7da041..a0ebd01 100644
--- a/gm/verttext2.cpp
+++ b/gm/verttext2.cpp
@@ -76,7 +76,7 @@
         paint.setTypeface(std::move(family));
         paint.setTextSize(textHeight);
 
-        canvas->drawText(string.c_str(), string.size(), y,
+        canvas->drawString(string, y,
                 SkIntToScalar(alignment == SkPaint::kLeft_Align ? 10 : 240),
                 paint);
         y += textHeight;
diff --git a/gm/windowrectangles.cpp b/gm/windowrectangles.cpp
index 8110574..5f569dd 100644
--- a/gm/windowrectangles.cpp
+++ b/gm/windowrectangles.cpp
@@ -188,7 +188,7 @@
     const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), kNumWindows);
 
     GrPaint paint;
-    if (!rtc->isStencilBufferMultisampled()) {
+    if (GrFSAAType::kNone == rtc->fsaaType()) {
         paint.setColor4f(GrColor4f(0, 0.25f, 1, 1));
         this->visualizeAlphaMask(ctx, rtc, reducedClip, std::move(paint));
     } else {
@@ -202,10 +202,10 @@
     const int padRight = (kDeviceRect.right() - kCoverRect.right()) / 2;
     const int padBottom = (kDeviceRect.bottom() - kCoverRect.bottom()) / 2;
     sk_sp<GrRenderTargetContext> maskRTC(
-        ctx->makeRenderTargetContextWithFallback(SkBackingFit::kExact,
-                                                 kCoverRect.width() + padRight,
-                                                 kCoverRect.height() + padBottom,
-                                                 kAlpha_8_GrPixelConfig, nullptr));
+        ctx->makeDeferredRenderTargetContextWithFallback(SkBackingFit::kExact,
+                                                         kCoverRect.width() + padRight,
+                                                         kCoverRect.height() + padBottom,
+                                                         kAlpha_8_GrPixelConfig, nullptr));
     if (!maskRTC ||
         !ctx->resourceProvider()->attachStencilAttachment(maskRTC->accessRenderTarget())) {
         return;
@@ -284,7 +284,7 @@
 
     canvas->clipRect(SkRect::Make(kCoverRect));
     canvas->clear(SK_ColorWHITE);
-    canvas->drawText(errorMsg.c_str(), errorMsg.size(), SkIntToScalar(kCoverRect.centerX()),
+    canvas->drawString(errorMsg, SkIntToScalar(kCoverRect.centerX()),
                      SkIntToScalar(kCoverRect.centerY() - 10), paint);
 }
 
diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp
index bb2378e..8fd1348 100644
--- a/gm/xfermodeimagefilter.cpp
+++ b/gm/xfermodeimagefilter.cpp
@@ -201,17 +201,16 @@
 
 private:
     static void DrawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint,
-                           int x, int y) {
+                                  int x, int y) {
         canvas->save();
         canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
-        canvas->clipRect(SkRect::MakeWH(
-            SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())));
+        canvas->clipRect(SkRect::MakeIWH(bitmap.width(), bitmap.height()));
         canvas->drawBitmap(bitmap, 0, 0, &paint);
         canvas->restore();
     }
 
     static void DrawClippedPaint(SkCanvas* canvas, const SkRect& rect, const SkPaint& paint,
-                          int x, int y) {
+                                 int x, int y) {
         canvas->save();
         canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
         canvas->clipRect(rect);
diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp
index 5d23104..9bf46f6 100644
--- a/gm/xfermodes.cpp
+++ b/gm/xfermodes.cpp
@@ -259,7 +259,7 @@
 
 #if 1
                 const char* label = SkBlendMode_Name(gModes[i].fMode);
-                canvas->drawText(label, strlen(label),
+                canvas->drawString(label,
                                  x + w/2, y - labelP.getTextSize()/2, labelP);
 #endif
                 x += w + SkIntToScalar(10);
diff --git a/gm/xfermodes2.cpp b/gm/xfermodes2.cpp
index 891a864..2db824d 100644
--- a/gm/xfermodes2.cpp
+++ b/gm/xfermodes2.cpp
@@ -72,8 +72,8 @@
             canvas->restore();
 
 #if 1
-            canvas->drawText(SkBlendMode_Name(mode), strlen(SkBlendMode_Name(mode)),
-                             x + w/2, y - labelP.getTextSize()/2, labelP);
+            canvas->drawString(SkBlendMode_Name(mode),
+                               x + w/2, y - labelP.getTextSize()/2, labelP);
 #endif
             x += w + SkIntToScalar(10);
             if ((m % W) == W - 1) {
diff --git a/gm/xfermodes3.cpp b/gm/xfermodes3.cpp
index f977b66..9c78658 100644
--- a/gm/xfermodes3.cpp
+++ b/gm/xfermodes3.cpp
@@ -71,8 +71,7 @@
         for (size_t s = 0; s < SK_ARRAY_COUNT(kStrokes); ++s) {
             for (size_t m = 0; m <= (size_t)SkBlendMode::kLastMode; ++m) {
                 SkBlendMode mode = static_cast<SkBlendMode>(m);
-                canvas->drawText(SkBlendMode_Name(mode),
-                                 strlen(SkBlendMode_Name(mode)),
+                canvas->drawString(SkBlendMode_Name(mode),
                                  SkIntToScalar(x),
                                  SkIntToScalar(y + kSize + 3) + labelP.getTextSize(),
                                  labelP);
@@ -182,7 +181,6 @@
         };
         SkBitmap bg;
         bg.allocN32Pixels(2, 2, true);
-        SkAutoLockPixels bgAlp(bg);
         memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData));
 
         SkMatrix lm;
diff --git a/gm/xform_image_gen.cpp b/gm/xform_image_gen.cpp
new file mode 100644
index 0000000..69a1ce1
--- /dev/null
+++ b/gm/xform_image_gen.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "gm.h"
+#include "SkColorSpaceXformImageGenerator.h"
+
+class ColorXformImageGenGM : public skiagm::GM {
+public:
+    ColorXformImageGenGM() {}
+
+protected:
+
+    SkString onShortName() override {
+        return SkString("color_xform_image_gen");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(100, 100);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkBitmap bitmap;
+        SkImageInfo info =
+                SkImageInfo::MakeN32(100, 100, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
+        bitmap.allocPixels(info);
+        bitmap.eraseColor(SK_ColorRED);
+        bitmap.eraseArea(SkIRect::MakeWH(25, 25), SK_ColorBLUE); // We should not see any blue.
+
+        std::unique_ptr<SkImageGenerator> gen = SkColorSpaceXformImageGenerator::Make(
+                bitmap,
+                SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                      SkColorSpace::kRec2020_Gamut),
+                kNever_SkCopyPixelsMode);
+
+        SkIRect subset = SkIRect::MakeXYWH(25, 25, 50, 50);
+        sk_sp<SkImage> image = SkImage::MakeFromGenerator(std::move(gen), &subset);
+        canvas->drawImage(image, 25, 25);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+DEF_GM( return new ColorXformImageGenGM(); )
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index fda0565..eb2e422 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -19,7 +19,7 @@
 #include "SkGradientShader.h"
 #include "effects/GrYUVEffect.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 #define YSIZE 8
 #define USIZE 4
@@ -132,10 +132,9 @@
                     grPaint.addColorFragmentProcessor(std::move(fp));
                     SkMatrix viewMatrix;
                     viewMatrix.setTranslate(x, y);
-                    std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                            GrColor_WHITE, viewMatrix, renderRect, nullptr, nullptr));
-                    renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                            std::move(grPaint), GrAAType::kNone, std::move(op));
+                    renderTargetContext->priv().testingOnly_addDrawOp(
+                            GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect,
+                                                    nullptr, nullptr, GrAAType::kNone));
                 }
                 x += renderRect.width() + kTestPad;
             }
@@ -257,10 +256,9 @@
                 SkMatrix viewMatrix;
                 viewMatrix.setTranslate(x, y);
                 grPaint.addColorFragmentProcessor(fp);
-                std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                        GrColor_WHITE, viewMatrix, renderRect, nullptr, nullptr));
-                renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                        std::move(grPaint), GrAAType::kNone, std::move(op));
+                renderTargetContext->priv().testingOnly_addDrawOp(
+                        GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect, nullptr,
+                                                nullptr, GrAAType::kNone));
             }
         }
     }
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 3573740..96da677 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -22,6 +22,7 @@
 
   msvc = 2015
 
+  extra_asmflags = []
   extra_cflags = []
   extra_cflags_c = []
   extra_cflags_cc = []
@@ -149,14 +150,11 @@
       "-fPIC",
       "-Werror",
     ]
-    cflags_cc += [
-      "-std=c++11",
-      "-fno-threadsafe-statics",
-    ]
+    cflags_cc += [ "-std=c++11" ]
 
     # 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.
-    if (is_component_build || is_mac) {
+    if (is_component_build || is_ios || is_mac) {
       cflags += [ "-fvisibility=hidden" ]
       cflags_cc += [ "-fvisibility-inlines-hidden" ]
     }
@@ -172,7 +170,6 @@
     cflags += [
       "-no-integrated-as",  # Clang <4.0 doesn't understand 'usw' mnemonic.
       "-march=mips32r2",
-      "-mdspr2",
     ]
   } else if (current_cpu == "x86" && !is_win) {
     asmflags += [ "-m32" ]
@@ -406,6 +403,7 @@
 }
 
 config("extra_flags") {
+  asmflags = extra_asmflags
   cflags = extra_cflags
   cflags_c = extra_cflags_c
   cflags_cc = extra_cflags_cc
@@ -500,7 +498,11 @@
   }
 
   tool("asm") {
-    command = "$env_setup$bin/ml64.exe /nologo /c /Fo {{output}} {{source}}"
+    _ml = "ml"
+    if (target_cpu == "x64") {
+      _ml += "64"
+    }
+    command = "$env_setup$bin/$_ml.exe /nologo /c /Fo {{output}} {{source}}"
     outputs = [
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj",
     ]
@@ -666,7 +668,7 @@
     outputs = [
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
     ]
-    description = "compile {{source}}"
+    description = "assemble {{source}}"
   }
 
   tool("alink") {
diff --git a/gn/android_framework_defines.gni b/gn/android_framework_defines.gni
index f84c275..f9ed28d 100644
--- a/gn/android_framework_defines.gni
+++ b/gn/android_framework_defines.gni
@@ -4,16 +4,15 @@
 # found in the LICENSE file.
 
 android_framework_defines = [
-  "SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT",
-
   # Needed until we fix https://bug.skia.org/2440 .
   "SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG",
   "SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS",
   "SK_SUPPORT_LEGACY_GRADIENT_DITHERING",
   "SK_SUPPORT_LEGACY_DRAWFILTER",
   "SK_IGNORE_GPU_DITHER",
-  "SK_SUPPORT_LEGACY_BITMAP_SETPIXELREF",
   "SK_SUPPORT_LEGACY_SHADER_ISABITMAP",
   "SK_SUPPORT_LEGACY_EMBOSSMASKFILTER",
-  "SK_SUPPORT_LEGACY_CANVAS_HELPERS",
+  "SK_SUPPORT_DEPRECATED_CLIPOPS",
+  "SK_LEGACY_SWEEP_GRADIENT",
+  "SK_SUPPORT_LEGACY_RASTERPIPELINE",
 ]
diff --git a/gn/bench.gni b/gn/bench.gni
index 5aff91b..3276f8c 100644
--- a/gn/bench.gni
+++ b/gn/bench.gni
@@ -27,6 +27,7 @@
   "$_bench/ChartBench.cpp",
   "$_bench/ChecksumBench.cpp",
   "$_bench/ChromeBench.cpp",
+  "$_bench/ClipMaskBench.cpp",
   "$_bench/CmapBench.cpp",
   "$_bench/CodecBench.cpp",
   "$_bench/ColorCanvasDrawBitmapBench.cpp",
@@ -102,6 +103,7 @@
   "$_bench/RTreeBench.cpp",
   "$_bench/ScalarBench.cpp",
   "$_bench/ShaderMaskBench.cpp",
+  "$_bench/ShadowBench.cpp",
   "$_bench/ShapesBench.cpp",
   "$_bench/Sk4fBench.cpp",
   "$_bench/SkBlend_optsBench.cpp",
diff --git a/gn/core.gni b/gn/core.gni
index fa65f25..aae6c8c 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -31,9 +31,9 @@
   "$_src/core/SkBitmapController.cpp",
   "$_src/core/SkBitmapDevice.cpp",
   "$_src/core/SkBitmapDevice.h",
+  "$_src/core/SkThreadedBMPDevice.cpp",
+  "$_src/core/SkThreadedBMPDevice.h",
   "$_src/core/SkBitmapFilter.h",
-  "$_src/core/SkBitmapProcShader.cpp",
-  "$_src/core/SkBitmapProcShader.h",
   "$_src/core/SkBitmapProcState.cpp",
   "$_src/core/SkBitmapProcState.h",
   "$_src/core/SkBitmapProcState_filter.h",
@@ -48,6 +48,7 @@
   "$_src/core/SkBitmapProvider.h",
   "$_src/core/SkBitmapScaler.h",
   "$_src/core/SkBitmapScaler.cpp",
+  "$_src/core/SkBlendMode.cpp",
   "$_src/core/SkBlitBWMaskTemplate.h",
   "$_src/core/SkBlitMask.h",
   "$_src/core/SkBlitMask_D32.cpp",
@@ -58,8 +59,6 @@
   "$_src/core/SkBlitter.cpp",
   "$_src/core/SkBlitter_A8.cpp",
   "$_src/core/SkBlitter_ARGB32.cpp",
-  "$_src/core/SkBlitter_PM4f.cpp",
-  "$_src/core/SkBlitter_RGB16.cpp",
   "$_src/core/SkBlitter_Sprite.cpp",
   "$_src/core/SkBlurImageFilter.cpp",
   "$_src/core/SkBuffer.cpp",
@@ -72,13 +71,10 @@
   "$_src/core/SkClipStackDevice.h",
   "$_src/core/SkColor.cpp",
   "$_src/core/SkColorFilter.cpp",
-  "$_src/core/SkColorFilterShader.cpp",
   "$_src/core/SkColorLookUpTable.cpp",
   "$_src/core/SkColorLookUpTable.h",
   "$_src/core/SkColorMatrixFilterRowMajor255.cpp",
   "$_src/core/SkColorMatrixFilterRowMajor255.h",
-  "$_src/core/SkColorShader.cpp",
-  "$_src/core/SkColorShader.h",
   "$_src/core/SkColorSpace.cpp",
   "$_src/core/SkColorSpace_A2B.cpp",
   "$_src/core/SkColorSpace_A2B.h",
@@ -89,10 +85,11 @@
   "$_src/core/SkColorSpaceXformCanvas.cpp",
   "$_src/core/SkColorSpaceXformer.cpp",
   "$_src/core/SkColorSpaceXformer.h",
+  "$_src/core/SkColorSpaceXformImageGenerator.cpp",
+  "$_src/core/SkColorSpaceXformImageGenerator.h",
   "$_src/core/SkColorSpaceXform_A2B.cpp",
   "$_src/core/SkColorSpaceXform_A2B.h",
   "$_src/core/SkColorTable.cpp",
-  "$_src/core/SkComposeShader.cpp",
   "$_src/core/SkConvertPixels.cpp",
   "$_src/core/SkConvertPixels.h",
   "$_src/core/SkConvolver.cpp",
@@ -119,6 +116,7 @@
   "$_src/core/SkDither.h",
   "$_src/core/SkDocument.cpp",
   "$_src/core/SkDraw.cpp",
+  "$_src/core/SkDraw_vertices.cpp",
   "$_src/core/SkDraw.h",
   "$_src/core/SkDrawable.cpp",
   "$_src/core/SkDrawLooper.cpp",
@@ -127,7 +125,6 @@
   "$_src/core/SkEdgeBuilder.h",
   "$_src/core/SkEdgeClipper.cpp",
   "$_src/core/SkEdgeClipper.h",
-  "$_src/core/SkEmptyShader.h",
   "$_src/core/SkEndian.h",
   "$_src/core/SkExecutor.cpp",
   "$_src/core/SkAnalyticEdge.cpp",
@@ -169,10 +166,7 @@
   "$_src/core/SkImageFilterCache.h",
   "$_src/core/SkImageInfo.cpp",
   "$_src/core/SkImageCacherator.h",
-  "$_src/core/SkImageCacherator.cpp",
   "$_src/core/SkImageGenerator.cpp",
-  "$_src/core/SkLightingShader.h",
-  "$_src/core/SkLightingShader.cpp",
   "$_src/core/SkLights.cpp",
   "$_src/core/SkLinearBitmapPipeline.cpp",
   "$_src/core/SkLinearBitmapPipeline.h",
@@ -185,7 +179,6 @@
   "$_src/core/SkLiteRecorder.cpp",
   "$_src/core/SkLocalMatrixImageFilter.cpp",
   "$_src/core/SkLocalMatrixImageFilter.h",
-  "$_src/core/SkLocalMatrixShader.cpp",
   "$_src/core/SkMD5.cpp",
   "$_src/core/SkMD5.h",
   "$_src/core/SkMallocPixelRef.cpp",
@@ -205,20 +198,18 @@
   "$_src/core/SkMipMap.cpp",
   "$_src/core/SkMipMap.h",
   "$_src/core/SkMiniRecorder.cpp",
+  "$_src/core/SkMiniRecorder.h",
   "$_src/core/SkModeColorFilter.cpp",
   "$_src/core/SkMultiPictureDraw.cpp",
   "$_src/core/SkNextID.h",
   "$_src/core/SkLatticeIter.cpp",
   "$_src/core/SkLatticeIter.h",
-  "$_src/core/SkNormalBevelSource.cpp",
-  "$_src/core/SkNormalBevelSource.h",
   "$_src/core/SkNormalMapSource.cpp",
   "$_src/core/SkNormalMapSource.h",
   "$_src/core/SkNormalFlatSource.cpp",
   "$_src/core/SkNormalFlatSource.h",
   "$_src/core/SkNormalSource.cpp",
   "$_src/core/SkNormalSource.h",
-  "$_src/core/SkNormalSourcePriv.h",
   "$_src/core/SkNx.h",
   "$_src/core/SkOpts.cpp",
   "$_src/core/SkOpts.h",
@@ -251,8 +242,6 @@
   "$_src/core/SkPictureRecord.cpp",
   "$_src/core/SkPictureRecord.h",
   "$_src/core/SkPictureRecorder.cpp",
-  "$_src/core/SkPictureShader.cpp",
-  "$_src/core/SkPictureShader.h",
   "$_src/core/SkPixelRef.cpp",
   "$_src/core/SkPixmap.cpp",
   "$_src/core/SkPoint.cpp",
@@ -260,8 +249,6 @@
   "$_src/core/SkPtrRecorder.cpp",
   "$_src/core/SkQuadClipper.cpp",
   "$_src/core/SkQuadClipper.h",
-  "$_src/core/SkRadialShadowMapShader.cpp",
-  "$_src/core/SkRadialShadowMapShader.h",
   "$_src/core/SkRasterClip.cpp",
   "$_src/core/SkRasterPipeline.cpp",
   "$_src/core/SkRasterPipelineBlitter.cpp",
@@ -271,6 +258,7 @@
   "$_src/core/SkReader32.h",
   "$_src/core/SkRecord.cpp",
   "$_src/core/SkRecords.cpp",
+  "$_src/core/SkRecords.h",
   "$_src/core/SkRecordDraw.cpp",
   "$_src/core/SkRecordOpts.cpp",
   "$_src/core/SkRecordOpts.h",
@@ -299,21 +287,17 @@
   "$_src/core/SkScan_Hairline.cpp",
   "$_src/core/SkScan_Path.cpp",
   "$_src/core/SkSemaphore.cpp",
-  "$_src/core/SkShader.cpp",
   "$_src/core/SkSharedMutex.cpp",
   "$_src/core/SkSharedMutex.h",
   "$_src/core/SkSinglyLinkedList.h",
-  "$_src/core/SkSpanProcs.cpp",
   "$_src/core/SkSpecialImage.cpp",
   "$_src/core/SkSpecialImage.h",
   "$_src/core/SkSpecialSurface.cpp",
   "$_src/core/SkSpecialSurface.h",
   "$_src/core/SkSpinlock.cpp",
   "$_src/core/SkSpriteBlitter_ARGB32.cpp",
-  "$_src/core/SkSpriteBlitter_RGB16.cpp",
   "$_src/core/SkSpriteBlitter.h",
   "$_src/core/SkSpriteBlitterTemplate.h",
-  "$_src/core/SkSpriteBlitter4f.cpp",
   "$_src/core/SkStream.cpp",
   "$_src/core/SkStreamPriv.h",
   "$_src/core/SkString.cpp",
@@ -356,7 +340,6 @@
   "$_src/core/SkValidatingReadBuffer.cpp",
   "$_src/core/SkValidatingReadBuffer.h",
   "$_src/core/SkValidationUtils.h",
-  "$_src/core/SkVarAlloc.cpp",
   "$_src/core/SkVertices.cpp",
   "$_src/core/SkVertState.cpp",
   "$_src/core/SkWriteBuffer.cpp",
@@ -369,16 +352,12 @@
   "$_src/core/SkXfermodeInterpretation.h",
   "$_src/core/SkYUVPlanesCache.cpp",
   "$_src/core/SkYUVPlanesCache.h",
-  "$_src/core/SkShadowShader.cpp",
-  "$_src/core/SkShadowShader.h",
 
   "$_src/image/SkImage.cpp",
-  "$_src/image/SkImage_Generator.cpp",
 
   #        "$_src/image/SkImage_Gpu.cpp",
+  "$_src/image/SkImage_Lazy.cpp",
   "$_src/image/SkImage_Raster.cpp",
-  "$_src/image/SkImageShader.cpp",
-  "$_src/image/SkImageShader.h",
   "$_src/image/SkSurface.cpp",
   "$_src/image/SkSurface_Base.h",
 
@@ -388,13 +367,32 @@
   "$_src/pipe/SkPipeCanvas.cpp",
   "$_src/pipe/SkPipeReader.cpp",
 
+  "$_src/shaders/SkBitmapProcShader.cpp",
+  "$_src/shaders/SkBitmapProcShader.h",
+  "$_src/shaders/SkColorFilterShader.cpp",
+  "$_src/shaders/SkColorFilterShader.h",
+  "$_src/shaders/SkColorShader.cpp",
+  "$_src/shaders/SkColorShader.h",
+  "$_src/shaders/SkComposeShader.cpp",
+  "$_src/shaders/SkComposeShader.h",
+  "$_src/shaders/SkEmptyShader.h",
+  "$_src/shaders/SkImageShader.cpp",
+  "$_src/shaders/SkImageShader.h",
+  "$_src/shaders/SkLightingShader.cpp",
+  "$_src/shaders/SkLightingShader.h",
+  "$_src/shaders/SkLocalMatrixShader.cpp",
+  "$_src/shaders/SkLocalMatrixShader.h",
+  "$_src/shaders/SkPictureShader.cpp",
+  "$_src/shaders/SkPictureShader.h",
+  "$_src/shaders/SkShader.cpp",
+  "$_src/shaders/SkShaderBase.h",
+
   "$_include/core/SkBBHFactory.h",
   "$_include/core/SkBitmap.h",
   "$_include/core/SkCanvas.h",
   "$_include/core/SkColor.h",
   "$_include/core/SkColorFilter.h",
   "$_include/core/SkColorPriv.h",
-  "$_include/core/SkCrossContextImageData.h",
   "$_include/core/SkData.h",
   "$_include/core/SkDeque.h",
   "$_include/core/SkDrawable.h",
@@ -460,13 +458,10 @@
   "$_include/private/SkFloatingPoint.h",
   "$_include/private/SkMalloc.h",
   "$_include/private/SkMessageBus.h",
-  "$_include/private/SkMiniRecorder.h",
   "$_include/private/SkMutex.h",
   "$_include/private/SkOnce.h",
-  "$_include/private/SkRecords.h",
   "$_include/private/SkSemaphore.h",
   "$_include/private/SkShadowFlags.h",
-  "$_include/private/SkShadowParams.h",
   "$_include/private/SkSpinlock.h",
   "$_include/private/SkTemplates.h",
   "$_include/private/SkTArray.h",
@@ -524,7 +519,6 @@
   "$_src/pathops/SkOpEdgeBuilder.h",
   "$_src/pathops/SkOpSegment.h",
   "$_src/pathops/SkOpSpan.h",
-  "$_src/pathops/SkOpTAllocator.h",
   "$_src/pathops/SkPathOpsBounds.h",
   "$_src/pathops/SkPathOpsCommon.h",
   "$_src/pathops/SkPathOpsConic.h",
@@ -540,3 +534,13 @@
   "$_src/pathops/SkPathWriter.h",
   "$_src/pathops/SkReduceOrder.h",
 ]
+
+skia_core_sources += [
+  "$_src/jumper/SkJumper.cpp",
+  "$_src/jumper/SkJumper_stages.cpp",
+]
+if (is_win) {
+  skia_core_sources += [ "$_src/jumper/SkJumper_generated_win.S" ]
+} else {
+  skia_core_sources += [ "$_src/jumper/SkJumper_generated.S" ]
+}
diff --git a/gn/effects.gni b/gn/effects.gni
index 6892ddd..61b1b39 100644
--- a/gn/effects.gni
+++ b/gn/effects.gni
@@ -38,7 +38,6 @@
   "$_src/effects/SkEmbossMask_Table.h",
   "$_src/effects/SkEmbossMaskFilter.cpp",
   "$_src/effects/SkImageSource.cpp",
-  "$_src/effects/SkGaussianEdgeShader.cpp",
   "$_src/effects/SkHighContrastFilter.cpp",
   "$_src/effects/SkLayerDrawLooper.cpp",
   "$_src/effects/SkLayerRasterizer.cpp",
@@ -55,7 +54,6 @@
   "$_src/effects/SkPackBits.h",
   "$_src/effects/SkPaintFlagsDrawFilter.cpp",
   "$_src/effects/SkPaintImageFilter.cpp",
-  "$_src/effects/SkPerlinNoiseShader.cpp",
   "$_src/effects/SkPictureImageFilter.cpp",
   "$_src/effects/SkRRectsGaussianEdgeMaskFilter.cpp",
   "$_src/effects/SkTableColorFilter.cpp",
@@ -63,32 +61,28 @@
   "$_src/effects/SkTileImageFilter.cpp",
   "$_src/effects/SkXfermodeImageFilter.cpp",
 
-  "$_src/effects/gradients/Sk4fGradientBase.cpp",
-  "$_src/effects/gradients/Sk4fGradientBase.h",
-  "$_src/effects/gradients/Sk4fGradientPriv.h",
-  "$_src/effects/gradients/Sk4fLinearGradient.cpp",
-  "$_src/effects/gradients/Sk4fLinearGradient.h",
-  "$_src/effects/gradients/SkClampRange.cpp",
-  "$_src/effects/gradients/SkClampRange.h",
-  "$_src/effects/gradients/SkGradientBitmapCache.cpp",
-  "$_src/effects/gradients/SkGradientBitmapCache.h",
-  "$_src/effects/gradients/SkGradientShader.cpp",
-  "$_src/effects/gradients/SkGradientShaderPriv.h",
-  "$_src/effects/gradients/SkLinearGradient.cpp",
-  "$_src/effects/gradients/SkLinearGradient.h",
-  "$_src/effects/gradients/SkRadialGradient.cpp",
-  "$_src/effects/gradients/SkRadialGradient.h",
-  "$_src/effects/gradients/SkTwoPointConicalGradient.cpp",
-  "$_src/effects/gradients/SkTwoPointConicalGradient.h",
-  "$_src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp",
-  "$_src/effects/gradients/SkTwoPointConicalGradient_gpu.h",
-  "$_src/effects/gradients/SkSweepGradient.cpp",
-  "$_src/effects/gradients/SkSweepGradient.h",
-
-  "$_src/effects/shadows/SkAmbientShadowMaskFilter.cpp",
-  "$_src/effects/shadows/SkAmbientShadowMaskFilter.h",
-  "$_src/effects/shadows/SkSpotShadowMaskFilter.cpp",
-  "$_src/effects/shadows/SkSpotShadowMaskFilter.h",
+  "$_src/shaders/SkPerlinNoiseShader.cpp",
+  "$_src/shaders/gradients/Sk4fGradientBase.cpp",
+  "$_src/shaders/gradients/Sk4fGradientBase.h",
+  "$_src/shaders/gradients/Sk4fGradientPriv.h",
+  "$_src/shaders/gradients/Sk4fLinearGradient.cpp",
+  "$_src/shaders/gradients/Sk4fLinearGradient.h",
+  "$_src/shaders/gradients/SkClampRange.cpp",
+  "$_src/shaders/gradients/SkClampRange.h",
+  "$_src/shaders/gradients/SkGradientBitmapCache.cpp",
+  "$_src/shaders/gradients/SkGradientBitmapCache.h",
+  "$_src/shaders/gradients/SkGradientShader.cpp",
+  "$_src/shaders/gradients/SkGradientShaderPriv.h",
+  "$_src/shaders/gradients/SkLinearGradient.cpp",
+  "$_src/shaders/gradients/SkLinearGradient.h",
+  "$_src/shaders/gradients/SkRadialGradient.cpp",
+  "$_src/shaders/gradients/SkRadialGradient.h",
+  "$_src/shaders/gradients/SkTwoPointConicalGradient.cpp",
+  "$_src/shaders/gradients/SkTwoPointConicalGradient.h",
+  "$_src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp",
+  "$_src/shaders/gradients/SkTwoPointConicalGradient_gpu.h",
+  "$_src/shaders/gradients/SkSweepGradient.cpp",
+  "$_src/shaders/gradients/SkSweepGradient.h",
 
   "$_include/effects/Sk1DPathEffect.h",
   "$_include/effects/Sk2DPathEffect.h",
@@ -105,7 +99,6 @@
   "$_include/effects/SkDiscretePathEffect.h",
   "$_include/effects/SkDisplacementMapEffect.h",
   "$_include/effects/SkDropShadowImageFilter.h",
-  "$_include/effects/SkGaussianEdgeShader.h",
   "$_include/effects/SkGradientShader.h",
   "$_include/effects/SkImageSource.h",
   "$_include/effects/SkLayerDrawLooper.h",
diff --git a/gn/find_headers.py b/gn/find_headers.py
index 4ade28d..8cff31d 100755
--- a/gn/find_headers.py
+++ b/gn/find_headers.py
@@ -19,10 +19,6 @@
 
 blacklist = {
   "GrGLConfig_chrome.h",
-  "GrVkBackendContext.h",
-  "GrVkDefines.h",
-  "GrVkInterface.h",
-  "GrVkTypes.h",
   "SkFontMgr_fontconfig.h",
 }
 
diff --git a/gn/gm.gni b/gn/gm.gni
index 67d2ced..c28504e 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -26,6 +26,7 @@
   "$_gm/beziers.cpp",
   "$_gm/bigblurs.cpp",
   "$_gm/bigmatrix.cpp",
+  "$_gm/bigrect.cpp",
   "$_gm/bigrrectaaeffect.cpp",
   "$_gm/bigtext.cpp",
   "$_gm/bigtileimagefilter.cpp",
@@ -41,6 +42,7 @@
   "$_gm/blend.cpp",
   "$_gm/blurcircles.cpp",
   "$_gm/blurcircles2.cpp",
+  "$_gm/blurignorexform.cpp",
   "$_gm/blurquickreject.cpp",
   "$_gm/blurrect.cpp",
   "$_gm/blurredclippedcircle.cpp",
@@ -48,6 +50,7 @@
   "$_gm/blurs.cpp",
   "$_gm/bmpfilterqualityrepeat.cpp",
   "$_gm/bug5252.cpp",
+  "$_gm/bug6643.cpp",
   "$_gm/bug530095.cpp",
   "$_gm/bug615686.cpp",
   "$_gm/cgm.c",
@@ -61,13 +64,12 @@
   "$_gm/clippedbitmapshaders.cpp",
   "$_gm/color4f.cpp",
   "$_gm/coloremoji.cpp",
+  "$_gm/coloremoji_blendmodes.cpp",
   "$_gm/colorfilteralpha8.cpp",
   "$_gm/colorfilterimagefilter.cpp",
   "$_gm/colorfilters.cpp",
   "$_gm/colormatrix.cpp",
   "$_gm/colorspacexform.cpp",
-  "$_gm/colortype.cpp",
-  "$_gm/colortypexfermode.cpp",
   "$_gm/colorwheel.cpp",
   "$_gm/complexclip.cpp",
   "$_gm/complexclip_blur_tiled.cpp",
@@ -85,6 +87,7 @@
   "$_gm/copyTo4444.cpp",
   "$_gm/crbug_691386.cpp",
   "$_gm/croppedrects.cpp",
+  "$_gm/crosscontextimage.cpp",
   "$_gm/cubicpaths.cpp",
   "$_gm/dashcircle.cpp",
   "$_gm/dashcubics.cpp",
@@ -112,9 +115,9 @@
   "$_gm/emboss.cpp",
   "$_gm/emptypath.cpp",
   "$_gm/encode.cpp",
+  "$_gm/encode-alpha-jpeg.cpp",
   "$_gm/encode-platform.cpp",
   "$_gm/encode-srgb.cpp",
-  "$_gm/etc1.cpp",
   "$_gm/extractbitmap.cpp",
   "$_gm/fadefilter.cpp",
   "$_gm/fatpathfill.cpp",
@@ -132,7 +135,6 @@
   "$_gm/gammaencodedpremul.cpp",
   "$_gm/gammatext.cpp",
   "$_gm/gamut.cpp",
-  "$_gm/gaussianedge.cpp",
   "$_gm/getpostextpath.cpp",
   "$_gm/giantbitmap.cpp",
   "$_gm/glyph_pos.cpp",
@@ -150,6 +152,7 @@
   "$_gm/hardstop_gradients.cpp",
   "$_gm/highcontrastfilter.cpp",
   "$_gm/hittestpath.cpp",
+  "$_gm/hsl.cpp",
   "$_gm/image.cpp",
   "$_gm/image_pict.cpp",
   "$_gm/image_shader.cpp",
@@ -174,7 +177,6 @@
   "$_gm/imagescalealigned.cpp",
   "$_gm/imagesource.cpp",
   "$_gm/imagesource2.cpp",
-  "$_gm/imagetoyuvplanes.cpp",
   "$_gm/internal_links.cpp",
   "$_gm/inversepaths.cpp",
   "$_gm/largeglyphblur.cpp",
@@ -185,11 +187,11 @@
   "$_gm/lighting.cpp",
   "$_gm/lightingshader.cpp",
   "$_gm/lightingshader2.cpp",
-  "$_gm/lightingshaderbevel.cpp",
   "$_gm/linepaths.cpp",
   "$_gm/localmatriximagefilter.cpp",
   "$_gm/localmatriximageshader.cpp",
   "$_gm/lumafilter.cpp",
+  "$_gm/makecolorspace.cpp",
   "$_gm/manypaths.cpp",
   "$_gm/matrixconvolution.cpp",
   "$_gm/matriximagefilter.cpp",
@@ -224,6 +226,7 @@
   "$_gm/pictureimagefilter.cpp",
   "$_gm/pictureimagegenerator.cpp",
   "$_gm/pictureshader.cpp",
+  "$_gm/pictureshadercache.cpp",
   "$_gm/pictureshadertile.cpp",
   "$_gm/pixelsnap.cpp",
   "$_gm/plus.cpp",
@@ -231,6 +234,7 @@
   "$_gm/poly2poly.cpp",
   "$_gm/polygons.cpp",
   "$_gm/quadpaths.cpp",
+  "$_gm/radial_gradient_precision.cpp",
   "$_gm/readpixels.cpp",
   "$_gm/recordopts.cpp",
   "$_gm/rectangletexture.cpp",
@@ -249,11 +253,11 @@
   "$_gm/shadertext.cpp",
   "$_gm/shadertext2.cpp",
   "$_gm/shadertext3.cpp",
-  "$_gm/shadowmaps.cpp",
   "$_gm/shadows.cpp",
   "$_gm/shadowutils.cpp",
   "$_gm/shallowgradient.cpp",
   "$_gm/shapes.cpp",
+  "$_gm/shapes_as_paths.cpp",
   "$_gm/showmiplevels.cpp",
   "$_gm/simpleaaclip.cpp",
   "$_gm/simple_magnification.cpp",
@@ -315,5 +319,6 @@
   "$_gm/xfermodes.cpp",
   "$_gm/xfermodes2.cpp",
   "$_gm/xfermodes3.cpp",
+  "$_gm/xform_image_gen.cpp",
   "$_gm/yuvtorgbeffect.cpp",
 ]
diff --git a/gn/gn_to_bp.py b/gn/gn_to_bp.py
index 487bdff..9e58b80 100644
--- a/gn/gn_to_bp.py
+++ b/gn/gn_to_bp.py
@@ -34,6 +34,7 @@
     'libdng_sdk',
     'libpiex',
     'libcutils',
+    'libnativewindow',
 ]
 
 # The ordering here is important: libsfntly needs to come after libskia.
@@ -132,6 +133,7 @@
         "libvulkan",
         "libz",
         "libcutils",
+        "libnativewindow",
     ],
     static_libs: [
         "libarect",
@@ -192,7 +194,6 @@
 # We'll run GN to get the main source lists and include directories for Skia.
 gn_args = {
   'is_official_build':  'true',
-  'skia_enable_jumper': 'true',
   'skia_enable_tools':  'true',
   'skia_use_vulkan':    'true',
   'target_cpu':         '"none"',
@@ -273,7 +274,6 @@
   'SK_BUILD_FOR_ANDROID_FRAMEWORK',
   'SK_DEFAULT_FONT_CACHE_LIMIT   (768 * 1024)',
   'SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE (512 * 1024)',
-  'SK_IGNORE_ETC1_SUPPORT',
   'SK_USE_FREETYPE_EMBOLDEN',
 ])
 # TODO: move these all to android_framework_defines.gni?
diff --git a/gn/gpu.gni b/gn/gpu.gni
index a481cfd..a0d67bc 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -8,67 +8,57 @@
 _include = get_path_info("../include", "abspath")
 
 skia_gpu_sources = [
+  "$_include/gpu/GrBackendSurface.h",
   "$_include/gpu/GrBlend.h",
-  "$_include/gpu/GrBuffer.h",
   "$_include/gpu/GrCaps.h",
   "$_include/gpu/GrColor.h",
-  "$_include/gpu/GrColorSpaceXform.h",
   "$_include/gpu/GrConfig.h",
   "$_include/gpu/GrContextOptions.h",
   "$_include/gpu/GrContext.h",
-  "$_include/gpu/GrCoordTransform.h",
-  "$_include/gpu/GrExternalTextureData.h",
-  "$_include/gpu/GrFragmentProcessor.h",
   "$_include/gpu/GrGpuResource.h",
-  "$_include/gpu/GrProcessor.h",
-  "$_include/gpu/GrProcessorUnitTest.h",
-  "$_include/gpu/GrProgramElement.h",
   "$_include/gpu/GrGpuResourceRef.h",
   "$_include/gpu/GrRenderTarget.h",
   "$_include/gpu/GrResourceKey.h",
   "$_include/gpu/GrShaderCaps.h",
-  "$_include/gpu/GrShaderVar.h",
   "$_include/gpu/GrSurface.h",
   "$_include/gpu/GrTexture.h",
   "$_include/gpu/GrSamplerParams.h",
-  "$_include/gpu/GrTestUtils.h",
   "$_include/gpu/GrTypes.h",
   "$_include/gpu/GrTypesPriv.h",
 
-  "$_include/gpu/effects/GrBlurredEdgeFragmentProcessor.h",
-  "$_include/gpu/effects/GrConstColorProcessor.h",
-  "$_include/gpu/effects/GrXfermodeFragmentProcessor.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/GrGLSLPrettyPrint.h",
   "$_include/gpu/gl/GrGLTypes.h",
 
   # Private includes
   "$_include/private/GrAuditTrail.h",
   "$_include/private/GrGLSL.h",
-  "$_include/private/GrGLSL_impl.h",
   "$_include/private/GrInstancedPipelineInfo.h",
   "$_include/private/GrSingleOwner.h",
   "$_include/private/GrRenderTargetProxy.h",
   "$_include/private/GrSurfaceProxy.h",
   "$_include/private/GrSwizzle.h",
   "$_include/private/GrTextureProxy.h",
-  "$_include/private/GrTextureRenderTargetProxy.h",
 
   "$_src/gpu/GrAppliedClip.h",
   "$_src/gpu/GrAuditTrail.cpp",
   "$_src/gpu/GrAutoLocaleSetter.h",
   "$_src/gpu/GrAllocator.h",
+  "$_src/gpu/GrBackendSurface.cpp",
+  "$_src/gpu/GrBackendTextureImageGenerator.cpp",
+  "$_src/gpu/GrBackendTextureImageGenerator.h",
+  "$_src/gpu/GrAHardwareBufferImageGenerator.cpp",
+  "$_src/gpu/GrAHardwareBufferImageGenerator.h",
   "$_src/gpu/GrBitmapTextureMaker.cpp",
   "$_src/gpu/GrBitmapTextureMaker.h",
   "$_src/gpu/GrBlend.cpp",
   "$_src/gpu/GrBlurUtils.cpp",
   "$_src/gpu/GrBlurUtils.h",
   "$_src/gpu/GrBuffer.cpp",
+  "$_src/gpu/GrBuffer.h",
   "$_src/gpu/GrBufferAllocPool.cpp",
   "$_src/gpu/GrBufferAllocPool.h",
   "$_src/gpu/GrCaps.cpp",
@@ -76,9 +66,11 @@
   "$_src/gpu/GrClipStackClip.h",
   "$_src/gpu/GrClipStackClip.cpp",
   "$_src/gpu/GrColorSpaceXform.cpp",
+  "$_src/gpu/GrColorSpaceXform.h",
   "$_src/gpu/GrContext.cpp",
   "$_src/gpu/GrContextPriv.h",
   "$_src/gpu/GrCoordTransform.cpp",
+  "$_src/gpu/GrCoordTransform.h",
   "$_src/gpu/GrDefaultGeoProcFactory.cpp",
   "$_src/gpu/GrDefaultGeoProcFactory.h",
   "$_src/gpu/GrDistanceFieldGenFromVector.cpp",
@@ -92,6 +84,7 @@
   "$_src/gpu/GrFixedClip.cpp",
   "$_src/gpu/GrFixedClip.h",
   "$_src/gpu/GrFragmentProcessor.cpp",
+  "$_src/gpu/GrFragmentProcessor.h",
   "$_src/gpu/GrGeometryProcessor.h",
   "$_src/gpu/GrGlyph.h",
   "$_src/gpu/GrGpu.cpp",
@@ -130,12 +123,10 @@
   "$_src/gpu/GrPathUtils.cpp",
   "$_src/gpu/GrPathUtils.h",
   "$_src/gpu/GrPendingProgramElement.h",
-  "$_src/gpu/GrPreFlushResourceProvider.cpp",
-  "$_src/gpu/GrPreFlushResourceProvider.h",
+  "$_src/gpu/GrOnFlushResourceProvider.cpp",
+  "$_src/gpu/GrOnFlushResourceProvider.h",
   "$_src/gpu/GrPipeline.cpp",
   "$_src/gpu/GrPipeline.h",
-  "$_src/gpu/GrPipelineAnalysis.cpp",
-  "$_src/gpu/GrPipelineAnalysis.h",
   "$_src/gpu/GrPipelineBuilder.h",
   "$_src/gpu/GrPrimitiveProcessor.cpp",
   "$_src/gpu/GrPrimitiveProcessor.h",
@@ -143,8 +134,13 @@
   "$_src/gpu/GrProcessorSet.h",
   "$_src/gpu/GrProgramDesc.cpp",
   "$_src/gpu/GrProgramDesc.h",
+  "$_src/gpu/GrProgramElement.h",
   "$_src/gpu/GrProcessor.cpp",
+  "$_src/gpu/GrProcessor.h",
+  "$_src/gpu/GrProcessorAnalysis.cpp",
+  "$_src/gpu/GrProcessorAnalysis.h",
   "$_src/gpu/GrProcessorUnitTest.cpp",
+  "$_src/gpu/GrProcessorUnitTest.h",
   "$_src/gpu/GrGpuResourceRef.cpp",
   "$_src/gpu/GrQuad.h",
   "$_src/gpu/GrRect.h",
@@ -191,9 +187,11 @@
   "$_src/gpu/GrTraceMarker.h",
   "$_src/gpu/GrTracing.h",
   "$_src/gpu/GrTestUtils.cpp",
+  "$_src/gpu/GrTestUtils.h",
   "$_src/gpu/GrShaderVar.cpp",
-  "$_src/gpu/GrSWMaskHelper.cpp",
-  "$_src/gpu/GrSWMaskHelper.h",
+  "$_src/gpu/GrShaderVar.h",
+  "$_src/gpu/GrSKSLPrettyPrint.cpp",
+  "$_src/gpu/GrSKSLPrettyPrint.h",
   "$_src/gpu/GrSoftwarePathRenderer.cpp",
   "$_src/gpu/GrSoftwarePathRenderer.h",
   "$_src/gpu/GrSurfacePriv.h",
@@ -201,6 +199,8 @@
   "$_src/gpu/GrSurfaceContext.cpp",
   "$_src/gpu/GrSurfaceContext.h",
   "$_src/gpu/GrSurfaceProxy.cpp",
+  "$_src/gpu/GrSWMaskHelper.cpp",
+  "$_src/gpu/GrSWMaskHelper.h",
   "$_src/gpu/GrTexture.cpp",
   "$_src/gpu/GrTextureAdjuster.cpp",
   "$_src/gpu/GrTextureAdjuster.h",
@@ -213,9 +213,8 @@
   "$_src/gpu/GrTextureProducer.h",
   "$_src/gpu/GrTextureProxy.cpp",
   "$_src/gpu/GrTextureRenderTargetProxy.cpp",
+  "$_src/gpu/GrTextureRenderTargetProxy.h",
   "$_src/gpu/GrTextureStripAtlas.h",
-  "$_src/gpu/GrTextureToYUVPlanes.cpp",
-  "$_src/gpu/GrTextureToYUVPlanes.h",
   "$_src/gpu/GrTRecorder.h",
   "$_src/gpu/GrUserStencilSettings.h",
   "$_src/gpu/GrWindowRectangles.h",
@@ -266,7 +265,6 @@
   "$_src/gpu/ops/GrMSAAPathRenderer.h",
   "$_src/gpu/ops/GrNonAAFillRectOp.h",
   "$_src/gpu/ops/GrNonAAFillRectOp.cpp",
-  "$_src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp",
   "$_src/gpu/ops/GrNonAAStrokeRectOp.cpp",
   "$_src/gpu/ops/GrNonAAStrokeRectOp.h",
   "$_src/gpu/ops/GrLatticeOp.cpp",
@@ -283,6 +281,7 @@
   "$_src/gpu/ops/GrSemaphoreOp.h",
   "$_src/gpu/ops/GrShadowRRectOp.cpp",
   "$_src/gpu/ops/GrShadowRRectOp.h",
+  "$_src/gpu/ops/GrSimpleMeshDrawOpHelper.h",
   "$_src/gpu/ops/GrSmallPathRenderer.cpp",
   "$_src/gpu/ops/GrSmallPathRenderer.h",
   "$_src/gpu/ops/GrStencilAndCoverPathRenderer.cpp",
@@ -294,9 +293,11 @@
 
   "$_src/gpu/effects/Gr1DKernelEffect.h",
   "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp",
+  "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.h",
   "$_src/gpu/effects/GrConfigConversionEffect.cpp",
   "$_src/gpu/effects/GrConfigConversionEffect.h",
   "$_src/gpu/effects/GrConstColorProcessor.cpp",
+  "$_src/gpu/effects/GrConstColorProcessor.h",
   "$_src/gpu/effects/GrCoverageSetOpXP.cpp",
   "$_src/gpu/effects/GrCoverageSetOpXP.h",
   "$_src/gpu/effects/GrCustomXfermode.cpp",
@@ -340,9 +341,12 @@
   "$_src/gpu/effects/GrTextureDomain.h",
   "$_src/gpu/effects/GrTextureStripAtlas.cpp",
   "$_src/gpu/effects/GrXfermodeFragmentProcessor.cpp",
+  "$_src/gpu/effects/GrXfermodeFragmentProcessor.h",
   "$_src/gpu/effects/GrYUVEffect.cpp",
   "$_src/gpu/effects/GrYUVEffect.h",
 
+  "$_src/gpu/instanced/InstancedOp.cpp",
+  "$_src/gpu/instanced/InstancedOp.h",
   "$_src/gpu/instanced/InstancedRendering.cpp",
   "$_src/gpu/instanced/InstancedRendering.h",
   "$_src/gpu/instanced/InstancedRenderingTypes.h",
@@ -386,7 +390,6 @@
   "$_src/gpu/gl/GrGLGpuCommandBuffer.h",
   "$_src/gpu/gl/GrGLGpuProgramCache.cpp",
   "$_src/gpu/gl/GrGLExtensions.cpp",
-  "$_src/gpu/gl/GrGLExternalTextureData.cpp",
   "$_src/gpu/gl/GrGLInterface.cpp",
   "$_src/gpu/gl/GrGLIRect.h",
   "$_src/gpu/gl/GrGLPath.cpp",
@@ -424,7 +427,6 @@
   "$_src/gpu/gl/builders/GrGLProgramBuilder.h",
   "$_src/gpu/gl/builders/GrGLShaderStringBuilder.cpp",
   "$_src/gpu/gl/builders/GrGLShaderStringBuilder.h",
-  "$_src/gpu/gl/builders/GrGLSLPrettyPrint.cpp",
 
   # GLSL
   "$_src/gpu/glsl/GrGLSL.cpp",
@@ -480,6 +482,8 @@
   "$_src/gpu/vk/GrVkBackendContext.cpp",
   "$_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",
@@ -533,6 +537,8 @@
   "$_src/gpu/vk/GrVkSemaphore.h",
   "$_src/gpu/vk/GrVkStencilAttachment.cpp",
   "$_src/gpu/vk/GrVkStencilAttachment.h",
+  "$_src/gpu/vk/GrVkTexelBuffer.cpp",
+  "$_src/gpu/vk/GrVkTexelBuffer.h",
   "$_src/gpu/vk/GrVkTexture.cpp",
   "$_src/gpu/vk/GrVkTexture.h",
   "$_src/gpu/vk/GrVkTextureRenderTarget.cpp",
diff --git a/gn/samples.gni b/gn/samples.gni
index 13a762c..4a50313 100644
--- a/gn/samples.gni
+++ b/gn/samples.gni
@@ -22,7 +22,6 @@
   "$_samplecode/SampleAnimBlur.cpp",
   "$_samplecode/SampleArc.cpp",
   "$_samplecode/SampleAtlas.cpp",
-  "$_samplecode/SampleBevel.cpp",
   "$_samplecode/SampleBigBlur.cpp",
   "$_samplecode/SampleBigGradient.cpp",
   "$_samplecode/SampleBitmapRect.cpp",
@@ -82,7 +81,8 @@
   "$_samplecode/SampleRepeatTile.cpp",
   "$_samplecode/SampleShaders.cpp",
   "$_samplecode/SampleShaderText.cpp",
-  "$_samplecode/SampleShadowing.cpp",
+  "$_samplecode/SampleShadowReference.cpp",
+  "$_samplecode/SampleShadowUtils.cpp",
   "$_samplecode/SampleShip.cpp",
   "$_samplecode/SampleSlides.cpp",
   "$_samplecode/SampleStringArt.cpp",
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 748466f..a1cab82 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -13,6 +13,7 @@
   "$_src/sksl/SkSLParser.cpp",
   "$_src/sksl/SkSLGLSLCodeGenerator.cpp",
   "$_src/sksl/SkSLSPIRVCodeGenerator.cpp",
+  "$_src/sksl/SkSLString.cpp",
   "$_src/sksl/SkSLUtil.cpp",
   "$_src/sksl/lex.layout.cpp",
   "$_src/sksl/ir/SkSLSymbolTable.cpp",
diff --git a/gn/tests.gni b/gn/tests.gni
index 97b7793..0263dd6 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -19,7 +19,6 @@
   "$_tests/BitSetTest.cpp",
   "$_tests/BlendTest.cpp",
   "$_tests/BlitMaskClip.cpp",
-  "$_tests/BlitRowTest.cpp",
   "$_tests/BlurTest.cpp",
   "$_tests/CachedDataTest.cpp",
   "$_tests/CachedDecodingPixelRefTest.cpp",
@@ -45,7 +44,6 @@
   "$_tests/ColorTest.cpp",
   "$_tests/CopySurfaceTest.cpp",
   "$_tests/CPlusPlusEleven.cpp",
-  "$_tests/CrossContextImageTest.cpp",
   "$_tests/CTest.cpp",
   "$_tests/DashPathEffectTest.cpp",
   "$_tests/DataRefTest.cpp",
@@ -63,7 +61,9 @@
   "$_tests/DynamicHashTest.cpp",
   "$_tests/EGLImageTest.cpp",
   "$_tests/EmptyPathTest.cpp",
+  "$_tests/EncodeTest.cpp",
   "$_tests/ExifTest.cpp",
+  "$_tests/F16StagesTest.cpp",
   "$_tests/FillPathTest.cpp",
   "$_tests/FitsInTest.cpp",
   "$_tests/FlattenableCustomFactory.cpp",
@@ -90,11 +90,11 @@
   "$_tests/GrContextAbandonTest.cpp",
   "$_tests/GrContextFactoryTest.cpp",
   "$_tests/GrDrawTargetTest.cpp",
-  "$_tests/GrGetCoeffBlendKnownComponentsTest.cpp",
-  "$_tests/GrGLSLPrettyPrintTest.cpp",
   "$_tests/GrMemoryPoolTest.cpp",
+  "$_tests/GrMeshTest.cpp",
   "$_tests/GrPorterDuffTest.cpp",
   "$_tests/GrShapeTest.cpp",
+  "$_tests/GrSKSLPrettyPrintTest.cpp",
   "$_tests/GrSurfaceTest.cpp",
   "$_tests/GrTextureMipMapInvalidationTest.cpp",
   "$_tests/GrTRecorderTest.cpp",
@@ -143,6 +143,7 @@
   "$_tests/PaintBreakTextTest.cpp",
   "$_tests/PaintImageFilterTest.cpp",
   "$_tests/PaintTest.cpp",
+  "$_tests/ParametricStageTest.cpp",
   "$_tests/ParsePathTest.cpp",
   "$_tests/PathCoverageTest.cpp",
   "$_tests/PathMeasureTest.cpp",
@@ -150,12 +151,11 @@
   "$_tests/PDFDeflateWStreamTest.cpp",
   "$_tests/PDFDocumentTest.cpp",
   "$_tests/PDFGlyphsToUnicodeTest.cpp",
-  "$_tests/PDFInvalidBitmapTest.cpp",
   "$_tests/PDFJpegEmbedTest.cpp",
   "$_tests/PDFMetadataAttributeTest.cpp",
   "$_tests/PDFOpaqueSrcModeToSrcOverTest.cpp",
   "$_tests/PDFPrimitivesTest.cpp",
-  "$_tests/PreFlushCallbackTest.cpp",
+  "$_tests/OnFlushCallbackTest.cpp",
   "$_tests/PictureBBHTest.cpp",
   "$_tests/PictureShaderTest.cpp",
   "$_tests/PictureTest.cpp",
@@ -216,6 +216,7 @@
   "$_tests/SkSLErrorTest.cpp",
   "$_tests/SkSLGLSLTest.cpp",
   "$_tests/SkSLMemoryLayoutTest.cpp",
+  "$_tests/SkSLSPIRVTest.cpp",
   "$_tests/SortTest.cpp",
   "$_tests/SpecialImageTest.cpp",
   "$_tests/SpecialSurfaceTest.cpp",
@@ -251,7 +252,6 @@
   "$_tests/TypefaceTest.cpp",
   "$_tests/UnicodeTest.cpp",
   "$_tests/UtilsTest.cpp",
-  "$_tests/VarAllocTest.cpp",
   "$_tests/VerticesTest.cpp",
   "$_tests/VkClearTests.cpp",
   "$_tests/VkHeapTests.cpp",
diff --git a/gn/utils.gni b/gn/utils.gni
index bb0bd94..14bec37 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -58,10 +58,6 @@
   "$_src/utils/SkParsePath.cpp",
   "$_src/utils/SkPatchUtils.cpp",
   "$_src/utils/SkPatchUtils.h",
-  "$_src/utils/SkRGBAToYUV.cpp",
-  "$_src/utils/SkRGBAToYUV.h",
-  "$_src/utils/SkShadowPaintFilterCanvas.cpp",
-  "$_src/utils/SkShadowPaintFilterCanvas.h",
   "$_src/utils/SkShadowTessellator.cpp",
   "$_src/utils/SkShadowTessellator.h",
   "$_src/utils/SkShadowUtils.cpp",
@@ -95,8 +91,6 @@
   "$_src/utils/win/SkWGL_win.cpp",
 
   #testing
-  "$_src/fonts/SkGScalerContext.cpp",
-  "$_src/fonts/SkGScalerContext.h",
   "$_src/fonts/SkRandomScalerContext.cpp",
   "$_src/fonts/SkRandomScalerContext.h",
   "$_src/fonts/SkTestScalerContext.cpp",
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index 11d28ea..d498007 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -10,6 +10,7 @@
 
 #include "../private/SkTemplates.h"
 #include "SkColor.h"
+#include "SkColorSpaceXform.h"
 #include "SkEncodedImageFormat.h"
 #include "SkEncodedInfo.h"
 #include "SkImageInfo.h"
@@ -277,7 +278,7 @@
          *
          *  Only meaningful for multi-frame images.
          */
-        size_t                     fFrameIndex;
+        int                        fFrameIndex;
 
         /**
          *  If true, the dst already contains the prior frame.
@@ -415,10 +416,10 @@
      *  @return Enum representing success or reason for failure.
      */
     Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
-            const SkCodec::Options*, SkPMColor* ctable, int* ctableCount);
+            const Options*, SkPMColor* ctable, int* ctableCount);
 
     Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
-            const SkCodec::Options* options) {
+            const Options* options) {
         return this->startIncrementalDecode(dstInfo, dst, rowBytes, options, nullptr, nullptr);
     }
 
@@ -483,7 +484,7 @@
      *      decoding the palette.
      *  @return Enum representing success or reason for failure.
      */
-    Result startScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options* options,
+    Result startScanlineDecode(const SkImageInfo& dstInfo, const Options* options,
             SkPMColor ctable[], int* ctableCount);
 
     /**
@@ -593,9 +594,18 @@
      */
     int outputScanline(int inputScanline) const;
 
+    /**
+     *  Return the number of frames in the image.
+     *
+     *  May require reading through the stream.
+     */
+    int getFrameCount() {
+        return this->onGetFrameCount();
+    }
+
     // The required frame for an independent frame is marked as
     // kNone.
-    static constexpr size_t kNone = static_cast<size_t>(-1);
+    static constexpr int kNone = -1;
 
     /**
      *  Information about individual frames in a multi-framed image.
@@ -605,12 +615,12 @@
          *  The frame that this frame needs to be blended with, or
          *  kNone.
          */
-        size_t fRequiredFrame;
+        int fRequiredFrame;
 
         /**
          *  Number of milliseconds to show this frame.
          */
-        size_t fDuration;
+        int fDuration;
 
         /**
          *  Whether the end marker for this frame is contained in the stream.
@@ -619,10 +629,30 @@
          *  There could be an error in the stream.
          */
         bool fFullyReceived;
+
+        /**
+         *  This is conservative; it will still return non-opaque if e.g. a
+         *  color index-based frame has a color with alpha but does not use it.
+         */
+        SkAlphaType fAlphaType;
     };
 
     /**
-     *  Return info about the frames in the image.
+     *  Return info about a single frame.
+     *
+     *  Only supported by multi-frame images. Does not read through the stream,
+     *  so it should be called after getFrameCount() to parse any frames that
+     *  have not already been parsed.
+     */
+    bool getFrameInfo(int index, FrameInfo* info) const {
+        if (index < 0) {
+            return false;
+        }
+        return this->onGetFrameInfo(index, info);
+    }
+
+    /**
+     *  Return info about all the frames in the image.
      *
      *  May require reading through the stream to determine info about the
      *  frames (including the count).
@@ -631,9 +661,7 @@
      *
      *  For single-frame images, this will return an empty vector.
      */
-    std::vector<FrameInfo> getFrameInfo() {
-        return this->onGetFrameInfo();
-    }
+    std::vector<FrameInfo> getFrameInfo();
 
     static constexpr int kRepetitionCountInfinite = -1;
 
@@ -651,12 +679,15 @@
     }
 
 protected:
+    using XformFormat = SkColorSpaceXform::ColorFormat;
+
     /**
      *  Takes ownership of SkStream*
      */
     SkCodec(int width,
             int height,
             const SkEncodedInfo&,
+            XformFormat srcFormat,
             SkStream*,
             sk_sp<SkColorSpace>,
             Origin = kTopLeft_Origin);
@@ -667,6 +698,7 @@
      */
     SkCodec(const SkEncodedInfo&,
             const SkImageInfo&,
+            XformFormat srcFormat,
             SkStream*,
             Origin = kTopLeft_Origin);
 
@@ -778,7 +810,7 @@
 
     const SkImageInfo& dstInfo() const { return fDstInfo; }
 
-    const SkCodec::Options& options() const { return fOptions; }
+    const Options& options() const { return fOptions; }
 
     /**
      *  Returns the number of scanlines that have been decoded so far.
@@ -792,11 +824,18 @@
 
     bool initializeColorXform(const SkImageInfo& dstInfo,
                               SkTransferFunctionBehavior premulBehavior);
-    SkColorSpaceXform* colorXform() const { return fColorXform.get(); }
+    void applyColorXform(void* dst, const void* src, int count, SkAlphaType) const;
+    void applyColorXform(void* dst, const void* src, int count) const;
 
-    virtual std::vector<FrameInfo> onGetFrameInfo() {
-        // empty vector - this is not animated.
-        return std::vector<FrameInfo>{};
+    SkColorSpaceXform* colorXform() const { return fColorXform.get(); }
+    bool xformOnDecode() const { return fXformOnDecode; }
+
+    virtual int onGetFrameCount() {
+        return 1;
+    }
+
+    virtual bool onGetFrameInfo(int, FrameInfo*) const {
+        return false;
     }
 
     virtual int onGetRepetitionCount() {
@@ -808,13 +847,16 @@
 private:
     const SkEncodedInfo                fEncodedInfo;
     const SkImageInfo                  fSrcInfo;
+    const XformFormat                  fSrcXformFormat;
     std::unique_ptr<SkStream>          fStream;
     bool                               fNeedsRewind;
     const Origin                       fOrigin;
 
     SkImageInfo                        fDstInfo;
-    SkCodec::Options                   fOptions;
+    Options                            fOptions;
+    XformFormat                        fDstXformFormat; // Based on fDstInfo.
     std::unique_ptr<SkColorSpaceXform> fColorXform;
+    bool                               fXformOnDecode;
 
     // Only meaningful during scanline decodes.
     int                                fCurrScanline;
@@ -838,13 +880,13 @@
     }
 
     // Methods for scanline decoding.
-    virtual SkCodec::Result onStartScanlineDecode(const SkImageInfo& /*dstInfo*/,
-            const SkCodec::Options& /*options*/, SkPMColor* /*ctable*/, int* /*ctableCount*/) {
+    virtual Result onStartScanlineDecode(const SkImageInfo& /*dstInfo*/,
+            const Options& /*options*/, SkPMColor* /*ctable*/, int* /*ctableCount*/) {
         return kUnimplemented;
     }
 
     virtual Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t,
-            const SkCodec::Options&, SkPMColor*, int*) {
+            const Options&, SkPMColor*, int*) {
         return kUnimplemented;
     }
 
diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h
index 290a499..bb3e12e 100644
--- a/include/config/SkUserConfig.h
+++ b/include/config/SkUserConfig.h
@@ -15,20 +15,17 @@
   #define SK_HAS_JPEG_LIBRARY
   #define SK_HAS_PNG_LIBRARY
   #define SK_HAS_WEBP_LIBRARY
-  #define SK_IGNORE_ETC1_SUPPORT
   #define SK_IGNORE_GPU_DITHER
   #define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
-  #define SK_INTERNAL
-  #define SK_JUMPER
+  #define SK_LEGACY_SWEEP_GRADIENT
   #define SK_PDF_USE_SFNTLY
-  #define SK_SUPPORT_LEGACY_BITMAP_SETPIXELREF
-  #define SK_SUPPORT_LEGACY_CANVAS_HELPERS
+  #define SK_SUPPORT_DEPRECATED_CLIPOPS
   #define SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
   #define SK_SUPPORT_LEGACY_DRAWFILTER
   #define SK_SUPPORT_LEGACY_EMBOSSMASKFILTER
   #define SK_SUPPORT_LEGACY_GRADIENT_DITHERING
+  #define SK_SUPPORT_LEGACY_RASTERPIPELINE
   #define SK_SUPPORT_LEGACY_SHADER_ISABITMAP
-  #define SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
   #define SK_SUPPORT_PDF
   #define SK_USE_FREETYPE_EMBOLDEN
   #define SK_VULKAN
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index fd25a23..8b8942f 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -20,7 +20,6 @@
 struct SkRect;
 class SkPaint;
 class SkPixelRef;
-class SkPixelRefFactory;
 class SkString;
 
 /** \class SkBitmap
@@ -218,8 +217,8 @@
      *  "unknown" pixels.
      */
     static bool ComputeIsOpaque(const SkBitmap& bm) {
-        SkAutoPixmapUnlock result;
-        return bm.requestLock(&result) && result.pixmap().computeIsOpaque();
+        SkPixmap pmap;
+        return bm.peekPixels(&pmap) && pmap.computeIsOpaque();
     }
 
     /**
@@ -238,16 +237,20 @@
 
     bool setInfo(const SkImageInfo&, size_t rowBytes = 0);
 
+    enum AllocFlags {
+        kZeroPixels_AllocFlag   = 1 << 0,
+    };
     /**
      *  Allocate the bitmap's pixels to match the requested image info. If the Factory
      *  is non-null, call it to allcoate the pixelref. If the ImageInfo requires
-     *  a colortable, then ColorTable must be non-null, and will be ref'd.
+     *  a colortable, then ColorTable must be non-null.
+     *
      *  On failure, the bitmap will be set to empty and return false.
      */
-    bool SK_WARN_UNUSED_RESULT tryAllocPixels(const SkImageInfo&, SkPixelRefFactory*, SkColorTable*);
-
-    void allocPixels(const SkImageInfo& info, SkPixelRefFactory* factory, SkColorTable* ctable) {
-        if (!this->tryAllocPixels(info, factory, ctable)) {
+    bool SK_WARN_UNUSED_RESULT tryAllocPixels(const SkImageInfo& info, sk_sp<SkColorTable> ctable,
+                                              uint32_t flags = 0);
+    void allocPixels(const SkImageInfo& info, sk_sp<SkColorTable> ctable, uint32_t flags = 0) {
+        if (!this->tryAllocPixels(info, std::move(ctable), flags)) {
             sk_throw();
         }
     }
@@ -288,6 +291,11 @@
         this->allocPixels(info);
     }
 
+    // TEMPORARY -- remove after updating Android BitmapTests.cpp:35
+    void allocPixels(const SkImageInfo& info, std::nullptr_t, SkColorTable* ctable) {
+        this->allocPixels(info, sk_ref_sp(ctable));
+    }
+
     /**
      *  Install a pixelref that wraps the specified pixels and rowBytes, and
      *  optional ReleaseProc and context. When the pixels are no longer
@@ -337,27 +345,6 @@
     */
     void setPixels(void* p, SkColorTable* ctable = NULL);
 
-    /** Copies the bitmap's pixels to the location pointed at by dst and returns
-        true if possible, returns false otherwise.
-
-        In the case when the dstRowBytes matches the bitmap's rowBytes, the copy
-        may be made faster by copying over the dst's per-row padding (for all
-        rows but the last). By setting preserveDstPad to true the caller can
-        disable this optimization and ensure that pixels in the padding are not
-        overwritten.
-
-        Always returns false for RLE formats.
-
-        @param dst      Location of destination buffer.
-        @param dstSize  Size of destination buffer. Must be large enough to hold
-                        pixels using indicated stride.
-        @param dstRowBytes  Width of each line in the buffer. If 0, uses
-                            bitmap's internal stride.
-        @param preserveDstPad Must we preserve padding in the dst
-    */
-    bool copyPixelsTo(void* const dst, size_t dstSize, size_t dstRowBytes = 0,
-                      bool preserveDstPad = false) const;
-
     /** Use the standard HeapAllocator to create the pixelref that manages the
         pixel memory. It will be sized based on the current ImageInfo.
         If this is called multiple times, a new pixelref object will be created
@@ -432,63 +419,21 @@
      */
     void setPixelRef(sk_sp<SkPixelRef>, int dx, int dy);
 
-#ifdef SK_SUPPORT_LEGACY_BITMAP_SETPIXELREF
-    /**
-     *  Assign a pixelref and origin to the bitmap. Pixelrefs are reference,
-     *  so the existing one (if any) will be unref'd and the new one will be
-     *  ref'd. (x,y) specify the offset within the pixelref's pixels for the
-     *  top/left corner of the bitmap. For a bitmap that encompases the entire
-     *  pixels of the pixelref, these will be (0,0).
-     */
-    SkPixelRef* setPixelRef(SkPixelRef* pr, int dx, int dy);
-
-    SkPixelRef* setPixelRef(SkPixelRef* pr, const SkIPoint& origin) {
-        return this->setPixelRef(pr, origin.fX, origin.fY);
-    }
-
-    SkPixelRef* setPixelRef(SkPixelRef* pr) {
-        return this->setPixelRef(pr, 0, 0);
-    }
-#endif
-
-    /** Call this to ensure that the bitmap points to the current pixel address
-        in the pixelref. Balance it with a call to unlockPixels(). These calls
-        are harmless if there is no pixelref.
-    */
-    void lockPixels() const;
-    /** When you are finished access the pixel memory, call this to balance a
-        previous call to lockPixels(). This allows pixelrefs that implement
-        cached/deferred image decoding to know when there are active clients of
-        a given image.
-    */
-    void unlockPixels() const;
-
-    /**
-     *  Some bitmaps can return a copy of their pixels for lockPixels(), but
-     *  that copy, if modified, will not be pushed back. These bitmaps should
-     *  not be used as targets for a raster device/canvas (since all pixels
-     *  modifications will be lost when unlockPixels() is called.)
-     */
-    // DEPRECATED
-    bool lockPixelsAreWritable() const;
-
-    bool requestLock(SkAutoPixmapUnlock* result) const;
-
     /** Call this to be sure that the bitmap is valid enough to be drawn (i.e.
         it has non-null pixels, and if required by its colortype, it has a
         non-null colortable. Returns true if all of the above are met.
     */
     bool readyToDraw() const {
         return this->getPixels() != NULL &&
-               (this->colorType() != kIndex_8_SkColorType || fColorTable);
+               (this->colorType() != kIndex_8_SkColorType || this->getColorTable());
     }
 
     /** Return the bitmap's colortable, if it uses one (i.e. colorType is
-        Index_8) and the pixels are locked.
+        Index_8).
         Otherwise returns NULL. Does not affect the colortable's
         reference count.
     */
-    SkColorTable* getColorTable() const { return fColorTable; }
+    SkColorTable* getColorTable() const;
 
     /** Returns a non-zero, unique value corresponding to the pixels in our
         pixelref. Each time the pixels are changed (and notifyPixelsChanged
@@ -545,8 +490,7 @@
      *  lower precision data than is actually in the pixel. Alpha only
      *  colortypes (e.g. kAlpha_8_SkColorType) return black with the appropriate
      *  alpha set.  The value is undefined for kUnknown_SkColorType or if x or y
-     *  are out of bounds, or if the bitmap does not have any pixels (or has not
-     *  be locked with lockPixels())..
+     *  are out of bounds, or if the bitmap does not have any pixels.
      */
     SkColor getColor(int x, int y) const {
         SkPixmap pixmap;
@@ -566,21 +510,21 @@
     void* getAddr(int x, int y) const;
 
     /** Returns the address of the pixel specified by x,y for 32bit pixels.
-     *  In debug build, this asserts that the pixels are allocated and locked,
-     *  and that the colortype is 32-bit, however none of these checks are performed
+     *  In debug build, this asserts that the pixels are allocated and that the
+     *  colortype is 32-bit, however none of these checks are performed
      *  in the release build.
      */
     inline uint32_t* getAddr32(int x, int y) const;
 
     /** Returns the address of the pixel specified by x,y for 16bit pixels.
-     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  In debug build, this asserts that the pixels are allocated
      *  and that the colortype is 16-bit, however none of these checks are performed
      *  in the release build.
      */
     inline uint16_t* getAddr16(int x, int y) const;
 
     /** Returns the address of the pixel specified by x,y for 8bit pixels.
-     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  In debug build, this asserts that the pixels are allocated
      *  and that the colortype is 8-bit, however none of these checks are performed
      *  in the release build.
      */
@@ -588,7 +532,7 @@
 
     /** Returns the color corresponding to the pixel specified by x,y for
      *  colortable based bitmaps.
-     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  In debug build, this asserts that the pixels are allocated,
      *  that the colortype is indexed, and that the colortable is allocated,
      *  however none of these checks are performed in the release build.
      */
@@ -607,24 +551,6 @@
     */
     bool extractSubset(SkBitmap* dst, const SkIRect& subset) const;
 
-    /** Makes a deep copy of this bitmap, respecting the requested colorType,
-     *  and allocating the dst pixels on the cpu.
-     *  Returns false if either there is an error (i.e. the src does not have
-     *  pixels) or the request cannot be satisfied (e.g. the src has per-pixel
-     *  alpha, and the requested colortype does not support alpha).
-     *  @param dst The bitmap to be sized and allocated
-     *  @param ct The desired colorType for dst
-     *  @param allocator Allocator used to allocate the pixelref for the dst
-     *                   bitmap. If this is null, the standard HeapAllocator
-     *                   will be used.
-     *  @return true if the copy was made.
-     */
-    bool copyTo(SkBitmap* dst, SkColorType ct, Allocator* = NULL) const;
-
-    bool copyTo(SkBitmap* dst, Allocator* allocator = NULL) const {
-        return this->copyTo(dst, this->colorType(), allocator);
-    }
-
     /**
      *  Copy the bitmap's pixels into the specified buffer (pixels + rowBytes),
      *  converting them into the requested format (SkImageInfo). The src pixels are read
@@ -645,7 +571,12 @@
      *  - If the src pixels are not available.
      */
     bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                    int srcX, int srcY) const;
+                    int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
+    bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
+                    int srcX, int srcY) const {
+        return this->readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY,
+                SkTransferFunctionBehavior::kRespect);
+    }
     bool readPixels(const SkPixmap& dst, int srcX, int srcY) const;
     bool readPixels(const SkPixmap& dst) const {
         return this->readPixels(dst, 0, 0);
@@ -663,20 +594,7 @@
     bool writePixels(const SkPixmap& src) {
         return this->writePixels(src, 0, 0);
     }
-
-    /**
-     *  Returns true if this bitmap's pixels can be converted into the requested
-     *  colorType, such that copyTo() could succeed.
-     */
-    bool canCopyTo(SkColorType colorType) const;
-
-    /** Makes a deep copy of this bitmap, keeping the copied pixels
-     *  in the same domain as the source: If the src pixels are allocated for
-     *  the cpu, then so will the dst. If the src pixels are allocated on the
-     *  gpu (typically as a texture), the it will do the same for the dst.
-     *  If the request cannot be fulfilled, returns false and dst is unmodified.
-     */
-    bool deepCopyTo(SkBitmap* dst) const;
+    bool writePixels(const SkPixmap& src, int x, int y, SkTransferFunctionBehavior behavior);
 
 #ifdef SK_BUILD_FOR_ANDROID
     bool hasHardwareMipMap() const {
@@ -718,13 +636,12 @@
                       SkIPoint* offset) const;
 
     /**
-     *  If the pixels are available from this bitmap (w/o locking) return true, and fill out the
-     *  specified pixmap (if not null). If the pixels are not available (either because there are
-     *  none, or becuase accessing them would require locking or other machinary) return false and
+     *  If the pixels are available from this bitmap return true, and fill out the
+     *  specified pixmap (if not null). If there are no pixels, return false and
      *  ignore the pixmap parameter.
      *
      *  Note: if this returns true, the results (in the pixmap) are only valid until the bitmap
-     *  is changed in anyway, in which case the results are invalid.
+     *  is changed in any way, in which case the results are invalid.
      */
     bool peekPixels(SkPixmap*) const;
 
@@ -734,10 +651,9 @@
     public:
         /** Allocate the pixel memory for the bitmap, given its dimensions and
             colortype. Return true on success, where success means either setPixels
-            or setPixelRef was called. The pixels need not be locked when this
-            returns. If the colortype requires a colortable, it also must be
-            installed via setColorTable. If false is returned, the bitmap and
-            colortable should be left unchanged.
+            or setPixelRef was called. If the colortype requires a colortable,
+            it also must be installed via setColorTable. If false is returned,
+            the bitmap and colortable should be left unchanged.
         */
         virtual bool allocPixelRef(SkBitmap*, SkColorTable*) = 0;
     private:
@@ -756,14 +672,6 @@
     SK_TO_STRING_NONVIRT()
 
 private:
-    mutable sk_sp<SkPixelRef> fPixelRef;
-    mutable int               fPixelLockCount;
-    // These are just caches from the locked pixelref
-    mutable void*             fPixels;
-    mutable SkColorTable*     fColorTable;    // only meaningful for kIndex8
-
-    SkIPoint                  fPixelRefOrigin;
-
     enum Flags {
         kImageIsVolatile_Flag   = 0x02,
 #ifdef SK_BUILD_FOR_ANDROID
@@ -775,47 +683,25 @@
 #endif
     };
 
-    SkImageInfo               fInfo;
-    uint32_t                  fRowBytes;
-    uint8_t                   fFlags;
-
-    bool writePixels(const SkPixmap& src, int x, int y, SkTransferFunctionBehavior behavior);
+    sk_sp<SkPixelRef>   fPixelRef;
+    void*               fPixels;
+    SkIPoint            fPixelRefOrigin;
+    SkImageInfo         fInfo;
+    uint32_t            fRowBytes;
+    uint8_t             fFlags;
 
     /*  Unreference any pixelrefs or colortables
     */
     void freePixels();
-    void updatePixelsFromRef() const;
+    void updatePixelsFromRef();
 
     static void WriteRawPixels(SkWriteBuffer*, const SkBitmap&);
     static bool ReadRawPixels(SkReadBuffer*, SkBitmap*);
 
-    friend class SkImage_Raster;
     friend class SkReadBuffer;        // unflatten, rawpixels
     friend class SkBinaryWriteBuffer; // rawpixels
-    friend struct SkBitmapProcState;
 };
 
-class SkAutoLockPixels : SkNoncopyable {
-public:
-    SkAutoLockPixels(const SkBitmap& bm, bool doLock = true) : fBitmap(bm) {
-        fDidLock = doLock;
-        if (doLock) {
-            bm.lockPixels();
-        }
-    }
-    ~SkAutoLockPixels() {
-        if (fDidLock) {
-            fBitmap.unlockPixels();
-        }
-    }
-
-private:
-    const SkBitmap& fBitmap;
-    bool            fDidLock;
-};
-//TODO(mtklein): uncomment when 71713004 lands and Chromium's fixed.
-//#define SkAutoLockPixels(...) SK_REQUIRE_LOCAL_VAR(SkAutoLockPixels)
-
 ///////////////////////////////////////////////////////////////////////////////
 
 inline uint32_t* SkBitmap::getAddr32(int x, int y) const {
@@ -843,8 +729,8 @@
     SkASSERT(fPixels);
     SkASSERT(kIndex_8_SkColorType == this->colorType());
     SkASSERT((unsigned)x < (unsigned)this->width() && (unsigned)y < (unsigned)this->height());
-    SkASSERT(fColorTable);
-    return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)];
+    SkASSERT(this->getColorTable());
+    return (*this->getColorTable())[*((const uint8_t*)fPixels + y * fRowBytes + x)];
 }
 
 #endif
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index c1ded2d..8fbca71 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -14,21 +14,20 @@
 #include "SkPaint.h"
 #include "SkRasterHandleAllocator.h"
 #include "SkSurfaceProps.h"
-#include "SkLights.h"
-#include "../private/SkShadowParams.h"
 
 class GrContext;
 class GrRenderTargetContext;
 class SkBaseDevice;
 class SkBitmap;
-class SkCanvasClipVisitor;
 class SkClipStack;
 class SkData;
 class SkDraw;
 class SkDrawable;
 class SkDrawFilter;
+struct SkDrawShadowRec;
 class SkImage;
 class SkImageFilter;
+class SkLights;
 class SkMetaData;
 class SkPath;
 class SkPicture;
@@ -78,7 +77,8 @@
      *  Note: it is valid to request a supported ImageInfo, but with zero
      *  dimensions.
      */
-    static std::unique_ptr<SkCanvas> MakeRasterDirect(const SkImageInfo&, void*, size_t);
+    static std::unique_ptr<SkCanvas> MakeRasterDirect(const SkImageInfo& info, void* pixels,
+                                                      size_t rowBytes);
 
     static std::unique_ptr<SkCanvas> MakeRasterDirectN32(int width, int height, SkPMColor* pixels,
                                                          size_t rowBytes) {
@@ -96,7 +96,7 @@
      *  by any device/pixels. Typically this use used by subclasses who handle
      *  the draw calls in some other way.
      */
-    SkCanvas(int width, int height, const SkSurfaceProps* = NULL);
+    SkCanvas(int width, int height, const SkSurfaceProps* props = NULL);
 
     /** Construct a canvas with the specified device to draw into.
 
@@ -120,7 +120,7 @@
      *  Allows the creation of a legacy SkCanvas even though the |bitmap|
      *  and its pixel ref may have an SkColorSpace.
      */
-    SkCanvas(const SkBitmap& bitmap, ColorBehavior);
+    SkCanvas(const SkBitmap& bitmap, ColorBehavior behavior);
 #endif
 
     /** Construct a canvas with the specified bitmap to draw into.
@@ -145,7 +145,7 @@
      *  for the canvas to the location supplied by the caller, and returns true. Otherwise,
      *  return false and leave the supplied props unchanged.
      */
-    bool getProps(SkSurfaceProps*) const;
+    bool getProps(SkSurfaceProps* props) const;
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -163,13 +163,6 @@
      */
     virtual SkISize getBaseLayerSize() const;
 
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-    /**
-     *  DEPRECATED: call getBaseLayerSize
-     */
-    SkISize getDeviceSize() const { return this->getBaseLayerSize(); }
-#endif
-
     /**
      *  Create a new surface matching the specified info, one that attempts to
      *  be maximally compatible when used with this canvas. If there is no matching Surface type,
@@ -179,13 +172,13 @@
      *  inherits the properties of the surface that owns this canvas. If this canvas has no parent
      *  surface, then the new surface is created with default properties.
      */
-    sk_sp<SkSurface> makeSurface(const SkImageInfo&, const SkSurfaceProps* = nullptr);
+    sk_sp<SkSurface> makeSurface(const SkImageInfo& info, const SkSurfaceProps* props = nullptr);
 
     /**
      * Return the GPU context of the device that is associated with the canvas.
      * For a canvas with non-GPU device, NULL is returned.
      */
-    GrContext* getGrContext();
+    virtual GrContext* getGrContext();
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -215,7 +208,7 @@
      *
      *  On failure, returns false and the pixmap parameter will be ignored.
      */
-    bool peekPixels(SkPixmap*);
+    bool peekPixels(SkPixmap* pixmap);
 
     /**
      *  Copy the pixels from the base-layer into the specified buffer (pixels + rowBytes),
@@ -238,21 +231,8 @@
      */
     bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
                     int srcX, int srcY);
-
-    /**
-     *  Helper for calling readPixels(info, ...). This call will check if bitmap has been allocated.
-     *  If not, it will attempt to call allocPixels(). If this fails, it will return false. If not,
-     *  it calls through to readPixels(info, ...) and returns its result.
-     */
-    bool readPixels(SkBitmap* bitmap, int srcX, int srcY);
-
-    /**
-     *  Helper for allocating pixels and then calling readPixels(info, ...). The bitmap is resized
-     *  to the intersection of srcRect and the base-layer bounds. On success, pixels will be
-     *  allocated in bitmap and true returned. On failure, false is returned and bitmap will be
-     *  set to empty.
-     */
-    bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
+    bool readPixels(const SkPixmap& pixmap, int srcX, int srcY);
+    bool readPixels(const SkBitmap& bitmap, int srcX, int srcY);
 
     /**
      *  This method affects the pixels in the base-layer, and operates in pixel coordinates,
@@ -272,7 +252,7 @@
      *  - If the src colortype/alphatype cannot be converted to the canvas' types
      *  - If this canvas is not backed by pixels (e.g. picture or PDF)
      */
-    bool writePixels(const SkImageInfo&, const void* pixels, size_t rowBytes, int x, int y);
+    bool writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y);
 
     /**
      *  Helper for calling writePixels(info, ...) by passing its pixels and rowbytes. If the bitmap
@@ -344,13 +324,10 @@
     typedef uint32_t SaveLayerFlags;
 
     struct SaveLayerRec {
-        SaveLayerRec()
-            : fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0)
-        {}
+        SaveLayerRec() {}
         SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0)
             : fBounds(bounds)
             , fPaint(paint)
-            , fBackdrop(nullptr)
             , fSaveLayerFlags(saveLayerFlags)
         {}
         SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
@@ -361,13 +338,27 @@
             , fSaveLayerFlags(saveLayerFlags)
         {}
 
-        const SkRect*           fBounds;    // optional
-        const SkPaint*          fPaint;     // optional
-        const SkImageFilter*    fBackdrop;  // optional
-        SaveLayerFlags          fSaveLayerFlags;
+        // EXPERIMENTAL: not ready for general use.
+        SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+                     const SkImage* clipMask, const SkMatrix* clipMatrix,
+                     SaveLayerFlags saveLayerFlags)
+            : fBounds(bounds)
+            , fPaint(paint)
+            , fBackdrop(backdrop)
+            , fClipMask(clipMask)
+            , fClipMatrix(clipMatrix)
+            , fSaveLayerFlags(saveLayerFlags)
+        {}
+
+        const SkRect*           fBounds = nullptr;      // optional
+        const SkPaint*          fPaint = nullptr;       // optional
+        const SkImageFilter*    fBackdrop = nullptr;    // optional
+        const SkImage*          fClipMask = nullptr;    // optional
+        const SkMatrix*         fClipMatrix = nullptr;  // optional -- only used with fClipMask
+        SaveLayerFlags          fSaveLayerFlags = 0;
     };
 
-    int saveLayer(const SaveLayerRec&);
+    int saveLayer(const SaveLayerRec& layerRec);
 
     /** This call balances a previous call to save(), and is used to remove all
         modifications to the matrix/clip/drawFilter state since the last save
@@ -434,31 +425,13 @@
     */
     void resetMatrix();
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    /** Add the specified translation to the current draw depth of the canvas.
-        @param z    The distance to translate in Z.
-                    Negative into screen, positive out of screen.
-                    Without translation, the draw depth defaults to 0.
-    */
-    void translateZ(SkScalar z);
-
-    /** Set the current set of lights in the canvas.
-        @param lights   The lights that we want the canvas to have.
-    */
-    void setLights(sk_sp<SkLights> lights);
-
-    /** Returns the current set of lights the canvas uses
-      */
-    sk_sp<SkLights> getLights() const;
-#endif
-
     /**
      *  Modify the current clip with the specified rectangle.
      *  @param rect The rect to combine with the current clip
      *  @param op The region op to apply to the current clip
      *  @param doAntiAlias true if the clip should be antialiased
      */
-    void clipRect(const SkRect& rect, SkClipOp, bool doAntiAlias);
+    void clipRect(const SkRect& rect, SkClipOp op, bool doAntiAlias);
     void clipRect(const SkRect& rect, SkClipOp op) {
         this->clipRect(rect, op, false);
     }
@@ -571,18 +544,6 @@
         return !bounds->isEmpty();
     }
 
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-    /** Fill the entire canvas' bitmap (restricted to the current clip) with the
-        specified ARGB color, using the specified mode.
-        @param a    the alpha component (0..255) of the color to fill the canvas
-        @param r    the red component (0..255) of the color to fill the canvas
-        @param g    the green component (0..255) of the color to fill the canvas
-        @param b    the blue component (0..255) of the color to fill the canvas
-        @param mode the mode to apply the color in (defaults to SrcOver)
-    */
-    void drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode = SkBlendMode::kSrcOver);
-#endif
-
     /** Fill the entire canvas' bitmap (restricted to the current clip) with the
         specified color and mode.
         @param color    the color to draw with
@@ -654,15 +615,9 @@
     /** Helper method for drawing a single point. See drawPoints() for more details.
      */
     void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint);
-
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-    /** Draws a single pixel in the specified color.
-        @param x        The X coordinate of which pixel to draw
-        @param y        The Y coordiante of which pixel to draw
-        @param color    The color to draw
-    */
-    void drawPoint(SkScalar x, SkScalar y, SkColor color);
-#endif
+    void drawPoint(SkPoint p, const SkPaint& paint) {
+        this->drawPoint(p.x(), p.y(), paint);
+    }
 
     /** Draw a line segment with the specified start and stop x,y coordinates,
         using the specified paint. NOTE: since a line is always "framed", the
@@ -674,6 +629,9 @@
         @param paint The paint used to draw the line
     */
     void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint);
+    void drawLine(SkPoint p0, SkPoint p1, const SkPaint& paint) {
+        this->drawLine(p0.x(), p0.y(), p1.x(), p1.y(), paint);
+    }
 
     /** Draw the specified rectangle using the specified paint. The rectangle
         will be filled or stroked based on the Style in the paint.
@@ -693,19 +651,6 @@
         this->drawRect(r, paint);
     }
 
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-    /** Draw the specified rectangle using the specified paint. The rectangle
-        will be filled or framed based on the Style in the paint.
-        @param left     The left side of the rectangle to be drawn
-        @param top      The top side of the rectangle to be drawn
-        @param right    The right side of the rectangle to be drawn
-        @param bottom   The bottom side of the rectangle to be drawn
-        @param paint    The paint used to draw the rect
-    */
-    void drawRectCoords(SkScalar left, SkScalar top, SkScalar right,
-                        SkScalar bottom, const SkPaint& paint);
-#endif
-
     /** Draw the outline of the specified region using the specified paint.
         @param region   The region to be drawn
         @param paint    The paint used to draw the region
@@ -717,7 +662,7 @@
         @param oval     The rectangle bounds of the oval to be drawn
         @param paint    The paint used to draw the oval
     */
-    void drawOval(const SkRect& oval, const SkPaint&);
+    void drawOval(const SkRect& oval, const SkPaint& paint);
 
     /**
      *  Draw the specified RRect using the specified paint The rrect will be filled or stroked
@@ -732,7 +677,7 @@
      *  Draw the annulus formed by the outer and inner rrects. The results
      *  are undefined if the outer does not contain the inner.
      */
-    void drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint&);
+    void drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint);
 
     /** Draw the specified circle using the specified paint. If radius is <= 0,
         then nothing will be drawn. The circle will be filled
@@ -743,6 +688,9 @@
         @param paint    The paint used to draw the circle
     */
     void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint);
+    void drawCircle(SkPoint center, SkScalar radius, const SkPaint& paint) {
+        this->drawCircle(center.x(), center.y(), radius, paint);
+    }
 
     /** Draw the specified arc, which will be scaled to fit inside the
         specified oval. Sweep angles are not treated as modulo 360 and thus can
@@ -836,10 +784,11 @@
                        SrcRectConstraint constraint = kStrict_SrcRectConstraint);
     // variant that takes src SkIRect
     void drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
-                       const SkPaint* paint, SrcRectConstraint = kStrict_SrcRectConstraint);
+                       const SkPaint* paint,
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint);
     // variant that assumes src == image-bounds
     void drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
-                       SrcRectConstraint = kStrict_SrcRectConstraint);
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint);
 
     void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
                        const SkPaint* paint,
@@ -847,12 +796,13 @@
         this->drawImageRect(image.get(), src, dst, paint, constraint);
     }
     void drawImageRect(const sk_sp<SkImage>& image, const SkIRect& isrc, const SkRect& dst,
-                       const SkPaint* paint, SrcRectConstraint cons = kStrict_SrcRectConstraint) {
-        this->drawImageRect(image.get(), isrc, dst, paint, cons);
+                       const SkPaint* paint, 
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint) {
+        this->drawImageRect(image.get(), isrc, dst, paint, constraint);
     }
     void drawImageRect(const sk_sp<SkImage>& image, const SkRect& dst, const SkPaint* paint,
-                       SrcRectConstraint cons = kStrict_SrcRectConstraint) {
-        this->drawImageRect(image.get(), dst, paint, cons);
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint) {
+        this->drawImageRect(image.get(), dst, paint, constraint);
     }
 
     /**
@@ -869,7 +819,7 @@
      *  - The corners shrink proportionally
      *  - The sides (along the shrink axis) and center are not drawn
      */
-    void drawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
+    void drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
                        const SkPaint* paint = nullptr);
     void drawImageNine(const sk_sp<SkImage>& image, const SkIRect& center, const SkRect& dst,
                        const SkPaint* paint = nullptr) {
@@ -907,12 +857,14 @@
      *  @param constraint Control the tradeoff between speed and exactness w.r.t. the src-rect.
      */
     void drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
-                        const SkPaint* paint, SrcRectConstraint = kStrict_SrcRectConstraint);
+                        const SkPaint* paint,
+                        SrcRectConstraint constraint = kStrict_SrcRectConstraint);
     // variant where src is SkIRect
     void drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
-                        const SkPaint* paint, SrcRectConstraint = kStrict_SrcRectConstraint);
+                        const SkPaint* paint, 
+                        SrcRectConstraint constraint = kStrict_SrcRectConstraint);
     void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
-                        SrcRectConstraint = kStrict_SrcRectConstraint);
+                        SrcRectConstraint constraint = kStrict_SrcRectConstraint);
 
     /**
      *  Draw the bitmap stretched or shrunk differentially to fit into dst.
@@ -1005,6 +957,29 @@
     void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
                   const SkPaint& paint);
 
+    /** Draw null-terminated UTF-8 string, with origin at (x,y), using the specified paint.
+        The origin is interpreted based on the Align setting in the paint.
+        @param string   The null-terminated string to be drawn
+        @param x        The x-coordinate of the origin of the string being drawn
+        @param y        The y-coordinate of the origin of the string being drawn
+        @param paint    The paint used for the string (e.g. color, size, style)
+    */
+    void drawString(const char* string, SkScalar x, SkScalar y, const SkPaint& paint) {
+        if (!string) {
+            return;
+        }
+        this->drawText(string, strlen(string), x, y, paint);
+    }
+
+    /** Draw string, with origin at (x,y), using the specified paint.
+        The origin is interpreted based on the Align setting in the paint.
+        @param string   The string to be drawn
+        @param x        The x-coordinate of the origin of the string being drawn
+        @param y        The y-coordinate of the origin of the string being drawn
+        @param paint    The paint used for the string (e.g. color, size, style)
+    */
+    void drawString(const SkString& string, SkScalar x, SkScalar y, const SkPaint& paint);
+
     /** Draw the text, with each character/glyph origin specified by the pos[]
         array. The origin is interpreted by the Align setting in the paint.
         @param text The text to be drawn
@@ -1060,7 +1035,7 @@
      *  If cullRect is not null, it is a conservative bounds of what will be drawn
      *  taking into account the xforms and the paint, and will be used to accelerate culling.
      */
-    void drawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
+    void drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
                          const SkRect* cullRect, const SkPaint& paint);
 
     /** Draw the text blob, offset by (x,y), using the specified paint.
@@ -1099,99 +1074,11 @@
      *  This is logically equivalent to
      *      saveLayer(paint)/drawPicture/restore
      */
-    void drawPicture(const SkPicture*, const SkMatrix* matrix, const SkPaint* paint);
+    void drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint);
     void drawPicture(const sk_sp<SkPicture>& picture, const SkMatrix* matrix, const SkPaint* paint) {
         this->drawPicture(picture.get(), matrix, paint);
     }
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    /**
-     *  Draw the picture into this canvas, with shadows!
-     *
-     *  We will use the canvas's lights along with the picture information (draw depths of
-     *  objects, etc) to first create a set of shadowmaps for the light-picture pairs, and
-     *  then use that set of shadowmaps to render the scene with shadows.
-     *
-     *  If matrix is non-null, apply that matrix to the CTM when drawing this picture. This is
-     *  logically equivalent to
-     *      save/concat/drawPicture/restore
-     *
-     *  If paint is non-null, draw the picture into a temporary buffer, and then apply the paint's
-     *  alpha/colorfilter/imagefilter/xfermode to that buffer as it is drawn to the canvas.
-     *  This is logically equivalent to
-     *      saveLayer(paint)/drawPicture/restore
-     *
-     *  We also support using variance shadow maps for blurred shadows; the user can specify
-     *  what shadow mapping algorithm to use with params.
-     *    - Variance Shadow Mapping works by storing both the depth and depth^2 in the shadow map.
-     *    - Then, the shadow map can be blurred, and when reading from it, the fragment shader
-     *      can calculate the variance of the depth at a position by doing E(x^2) - E(x)^2.
-     *    - We can then use the depth variance and depth at a fragment to arrive at an upper bound
-     *      of the probability that the current surface is shadowed by using Chebyshev's
-     *      inequality, and then use that to shade the fragment.
-     *
-     *    - There are a few problems with VSM.
-     *      * Light Bleeding | Areas with high variance, such as near the edges of high up rects,
-     *                         will cause their shadow penumbras to overwrite otherwise solid
-     *                         shadows.
-     *      * Shape Distortion | We can combat Light Bleeding by biasing the shadow (setting
-     *                           mostly shaded fragments to completely shaded) and increasing
-     *                           the minimum allowed variance. However, this warps and rounds
-     *                           out the shape of the shadow.
-     */
-    void drawShadowedPicture(const SkPicture*,
-                             const SkMatrix* matrix,
-                             const SkPaint* paint,
-                             const SkShadowParams& params);
-    void drawShadowedPicture(const sk_sp<SkPicture>& picture,
-                             const SkMatrix* matrix,
-                             const SkPaint* paint,
-                             const SkShadowParams& params) {
-        this->drawShadowedPicture(picture.get(), matrix, paint, params);
-    }
-#endif
-
-    enum VertexMode {
-        kTriangles_VertexMode,
-        kTriangleStrip_VertexMode,
-        kTriangleFan_VertexMode
-    };
-
-    /** Draw the array of vertices, interpreted as triangles (based on mode).
-
-        If both textures and vertex-colors are NULL, it strokes hairlines with
-        the paint's color. This behavior is a useful debugging mode to visualize
-        the mesh.
-
-        @param vmode How to interpret the array of vertices
-        @param vertexCount The number of points in the vertices array (and
-                    corresponding texs and colors arrays if non-null)
-        @param vertices Array of vertices for the mesh
-        @param texs May be null. If not null, specifies the coordinate
-                    in _texture_ space (not uv space) for each vertex.
-        @param colors May be null. If not null, specifies a color for each
-                      vertex, to be interpolated across the triangle.
-        @param mode Used if both texs and colors are present and paint has a
-                    shader. In this case the colors are combined with the texture
-                    using mode, before being drawn using the paint.
-        @param indices If not null, array of indices to reference into the
-                    vertex (texs, colors) array.
-        @param indexCount number of entries in the indices array (if not null)
-        @param paint Specifies the shader/texture if present.
-    */
-    void drawVertices(VertexMode vmode, int vertexCount,
-                      const SkPoint vertices[], const SkPoint texs[],
-                      const SkColor colors[], SkBlendMode mode,
-                      const uint16_t indices[], int indexCount,
-                      const SkPaint& paint);
-    void drawVertices(VertexMode vmode, int vertexCount,
-                      const SkPoint vertices[], const SkPoint texs[],
-                      const SkColor colors[], const uint16_t indices[], int indexCount,
-                      const SkPaint& paint) {
-        this->drawVertices(vmode, vertexCount, vertices, texs, colors, SkBlendMode::kModulate,
-                           indices, indexCount, paint);
-    }
-
     /** Draw vertices from an immutable SkVertices object.
 
         @param vertices The mesh to draw.
@@ -1241,7 +1128,7 @@
      *  and blendmode are used to affect each of the quads.
      */
     void drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
-                   const SkColor colors[], int count, SkBlendMode, const SkRect* cullRect,
+                   const SkColor colors[], int count, SkBlendMode mode, const SkRect* cullRect,
                    const SkPaint* paint);
     void drawAtlas(const sk_sp<SkImage>& atlas, const SkRSXform xform[], const SkRect tex[],
                    const SkColor colors[], int count, SkBlendMode mode, const SkRect* cullRect,
@@ -1266,8 +1153,8 @@
      *  If the intent is to force the contents of the drawable into this canvas immediately,
      *  then drawable->draw(canvas) may be called.
      */
-    void drawDrawable(SkDrawable* drawable, const SkMatrix* = NULL);
-    void drawDrawable(SkDrawable*, SkScalar x, SkScalar y);
+    void drawDrawable(SkDrawable* drawable, const SkMatrix* matrix = NULL);
+    void drawDrawable(SkDrawable* drawable, SkScalar x, SkScalar y);
 
     /**
      *  Send an "annotation" to the canvas. The annotation is a key/value pair, where the key is
@@ -1279,24 +1166,18 @@
      *  Note: on may canvas types, this information is ignored, but some canvases (e.g. recording
      *  a picture or drawing to a PDF document) will pass on this information.
      */
-    void drawAnnotation(const SkRect&, const char key[], SkData* value);
+    void drawAnnotation(const SkRect& rect, const char key[], SkData* value);
     void drawAnnotation(const SkRect& rect, const char key[], const sk_sp<SkData>& value) {
         this->drawAnnotation(rect, key, value.get());
     }
 
     //////////////////////////////////////////////////////////////////////////
-#ifdef SK_INTERNAL
-#ifndef SK_SUPPORT_LEGACY_DRAWFILTER
-    #define SK_SUPPORT_LEGACY_DRAWFILTER
-#endif
-#endif
 
 #ifdef SK_SUPPORT_LEGACY_DRAWFILTER
     /** Get the current filter object. The filter's reference count is not
         affected. The filter is saved/restored, just like the matrix and clip.
         @return the canvas' filter (or NULL).
     */
-    SK_ATTR_EXTERNALLY_DEPRECATED("getDrawFilter use is deprecated")
     SkDrawFilter* getDrawFilter() const;
 
     /** Set the new filter (or NULL). Pass NULL to clear any existing filter.
@@ -1307,7 +1188,6 @@
         @param filter the new filter (or NULL)
         @return the new filter
     */
-    SK_ATTR_EXTERNALLY_DEPRECATED("setDrawFilter use is deprecated")
     virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter);
 #endif
     //////////////////////////////////////////////////////////////////////////
@@ -1333,14 +1213,6 @@
     */
     const SkMatrix& getTotalMatrix() const;
 
-    typedef SkCanvasClipVisitor ClipVisitor;
-    /**
-     *  Replays the clip operations, back to front, that have been applied to
-     *  the canvas, calling the appropriate method on the visitor for each
-     *  clip. All clips have already been transformed into device space.
-     */
-    void replayClips(ClipVisitor*) const;
-
     ///////////////////////////////////////////////////////////////////////////
 
     // don't call
@@ -1360,35 +1232,23 @@
                                const SkPaint* paint,
                                SrcRectConstraint constraint = kStrict_SrcRectConstraint);
 
-    // expose minimum amount of information necessary for transitional refactoring
-    /**
-     * Returns CTM and clip bounds, translated from canvas coordinates to top layer coordinates.
-     */
-    void temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds);
-
     /**
      *  Returns the global clip as a region. If the clip contains AA, then only the bounds
      *  of the clip may be returned.
      */
-    void temporary_internal_getRgnClip(SkRegion*);
+    void temporary_internal_getRgnClip(SkRegion* region);
+
+    void private_draw_shadow_rec(const SkPath&, const SkDrawShadowRec&);
 
 protected:
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    /** Returns the current (cumulative) draw depth of the canvas.
-      */
-    SkScalar getZ() const;
-
-    sk_sp<SkLights> fLights;
-#endif
-
     // default impl defers to getDevice()->newSurface(info)
-    virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&);
+    virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props);
 
     // default impl defers to its device
-    virtual bool onPeekPixels(SkPixmap*);
-    virtual bool onAccessTopLayerPixels(SkPixmap*);
+    virtual bool onPeekPixels(SkPixmap* pixmap);
+    virtual bool onAccessTopLayerPixels(SkPixmap* pixmap);
     virtual SkImageInfo onImageInfo() const;
-    virtual bool onGetProps(SkSurfaceProps*) const;
+    virtual bool onGetProps(SkSurfaceProps* props) const;
     virtual void onFlush();
 
     // Subclass save/restore notifiers.
@@ -1401,27 +1261,23 @@
 
     virtual void willSave() {}
     // Overriders should call the corresponding INHERITED method up the inheritance chain.
-    virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) {
+    virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& ) {
         return kFullLayer_SaveLayerStrategy;
     }
     virtual void willRestore() {}
     virtual void didRestore() {}
-    virtual void didConcat(const SkMatrix&) {}
-    virtual void didSetMatrix(const SkMatrix&) {}
+    virtual void didConcat(const SkMatrix& ) {}
+    virtual void didSetMatrix(const SkMatrix& ) {}
     virtual void didTranslate(SkScalar dx, SkScalar dy) {
         this->didConcat(SkMatrix::MakeTrans(dx, dy));
     }
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    virtual void didTranslateZ(SkScalar) {}
-#endif
-
     virtual SkRect onGetLocalClipBounds() const;
     virtual SkIRect onGetDeviceClipBounds() const;
 
 
-    virtual void onDrawAnnotation(const SkRect&, const char key[], SkData* value);
-    virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&);
+    virtual void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value);
+    virtual void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint);
 
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x,
                             SkScalar y, const SkPaint& paint);
@@ -1436,71 +1292,70 @@
     virtual void onDrawTextOnPath(const void* text, size_t byteLength,
                                   const SkPath& path, const SkMatrix* matrix,
                                   const SkPaint& paint);
-    virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
+    virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
                                    const SkRect* cullRect, const SkPaint& paint);
 
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                 const SkPaint& paint);
 
     virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                           const SkPoint texCoords[4], SkBlendMode, const SkPaint& paint);
+                           const SkPoint texCoords[4], SkBlendMode mode, const SkPaint& paint);
 
-    virtual void onDrawDrawable(SkDrawable*, const SkMatrix*);
+    virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix);
 
-    virtual void onDrawPaint(const SkPaint&);
-    virtual void onDrawRect(const SkRect&, const SkPaint&);
+    virtual void onDrawPaint(const SkPaint& paint);
+    virtual void onDrawRect(const SkRect& rect, const SkPaint& paint);
     virtual void onDrawRegion(const SkRegion& region, const SkPaint& paint);
-    virtual void onDrawOval(const SkRect&, const SkPaint&);
-    virtual void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
-                           const SkPaint&);
-    virtual void onDrawRRect(const SkRRect&, const SkPaint&);
-    virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&);
-    virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&);
-    virtual void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
-                             int count, SkBlendMode, const SkRect* cull, const SkPaint*);
-    virtual void onDrawPath(const SkPath&, const SkPaint&);
-    virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*);
-    virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                                 SrcRectConstraint);
-    virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
-                                 const SkPaint*);
-    virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                                    const SkPaint*);
+    virtual void onDrawOval(const SkRect& rect, const SkPaint& paint);
+    virtual void onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle,
+                           bool useCenter, const SkPaint& paint);
+    virtual void onDrawRRect(const SkRRect& rrect, const SkPaint& paint);
+    virtual void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                              const SkPaint& paint);
+    virtual void onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode,
+                                      const SkPaint& paint);
+    virtual void onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect rect[],
+                             const SkColor colors[], int count, SkBlendMode mode,
+                             const SkRect* cull, const SkPaint* paint);
+    virtual void onDrawPath(const SkPath& path, const SkPaint& paint);
+    virtual void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint);
+    virtual void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                 const SkPaint* paint, SrcRectConstraint constraint);
+    virtual void onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
+                                 const SkPaint* paint);
+    virtual void onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
+                                    const SkPaint* paint);
 
-    virtual void onDrawBitmap(const SkBitmap&, SkScalar dx, SkScalar dy, const SkPaint*);
-    virtual void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
-                                  SrcRectConstraint);
-    virtual void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                                  const SkPaint*);
-    virtual void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst,
-                                     const SkPaint*);
+    virtual void onDrawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy,
+                              const SkPaint* paint);
+    virtual void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
+                                  const SkPaint* paint, SrcRectConstraint constraint);
+    virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
+                                  const SkPaint* paint);
+    virtual void onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
+                                     const SkRect& dst, const SkPaint* paint);
+    virtual void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&);
 
     enum ClipEdgeStyle {
         kHard_ClipEdgeStyle,
         kSoft_ClipEdgeStyle
     };
 
-    virtual void onClipRect(const SkRect& rect, SkClipOp, ClipEdgeStyle);
-    virtual void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle);
-    virtual void onClipPath(const SkPath& path, SkClipOp, ClipEdgeStyle);
-    virtual void onClipRegion(const SkRegion& deviceRgn, SkClipOp);
+    virtual void onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle);
+    virtual void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle);
+    virtual void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle);
+    virtual void onClipRegion(const SkRegion& deviceRgn, SkClipOp op);
 
     virtual void onDiscard();
 
-    virtual void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    virtual void onDrawShadowedPicture(const SkPicture*,
-                                       const SkMatrix*,
-                                       const SkPaint*,
-                                       const SkShadowParams& params);
-#endif
+    virtual void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
+                               const SkPaint* paint);
 
     // Clip rectangle bounds. Called internally by saveLayer.
     // returns false if the entire rectangle is entirely clipped out
     // If non-NULL, The imageFilter parameter will be used to expand the clip
     // and offscreen bounds for any margin required by the filter DAG.
-    bool clipRectBounds(const SkRect* bounds, SaveLayerFlags, SkIRect* intersection,
+    bool clipRectBounds(const SkRect* bounds, SaveLayerFlags flags, SkIRect* intersection,
                         const SkImageFilter* imageFilter = NULL);
 
 private:
@@ -1576,7 +1431,7 @@
     enum {
         kMCRecSize      = 128,  // most recent measurement
         kMCRecCount     = 32,   // common depth for save/restores
-        kDeviceCMSize   = 184,  // most recent measurement
+        kDeviceCMSize   = 224,  // most recent measurement
     };
     intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
     intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)];
@@ -1606,13 +1461,10 @@
     friend class AutoDrawLooper;
     friend class SkDebugCanvas;     // needs experimental fAllowSimplifyClip
     friend class SkSurface_Raster;  // needs getDevice()
-    friend class SkRecorder;        // resetForNextPicture
-    friend class SkLiteRecorder;    // resetForNextPicture
     friend class SkNoDrawCanvas;    // InitFlags
     friend class SkPictureImageFilter;  // SkCanvas(SkBaseDevice*, SkSurfaceProps*, InitFlags)
     friend class SkPictureRecord;   // predrawNotify (why does it need it? <reed>)
     friend class SkPicturePlayback; // SaveFlagsToSaveLayerFlags
-    friend class SkDeferredCanvas;  // For use of resetForNextPicture
     friend class SkOverdrawCanvas;
     friend class SkRasterHandleAllocator;
 
@@ -1648,7 +1500,8 @@
                                 SrcRectConstraint);
     void internalDrawPaint(const SkPaint& paint);
     void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy);
-    void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*);
+    void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, SkImage* clipImage,
+                            const SkMatrix& clipMatrix);
 
     // shared by save() and saveLayer()
     void internalSave();
@@ -1739,12 +1592,4 @@
 };
 #define SkAutoCanvasRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoCanvasRestore)
 
-class SkCanvasClipVisitor {
-public:
-    virtual ~SkCanvasClipVisitor();
-    virtual void clipRect(const SkRect&, SkClipOp, bool antialias) = 0;
-    virtual void clipRRect(const SkRRect&, SkClipOp, bool antialias) = 0;
-    virtual void clipPath(const SkPath&, SkClipOp, bool antialias) = 0;
-};
-
 #endif
diff --git a/include/core/SkClipOp.h b/include/core/SkClipOp.h
index f230654..6ae16a8 100644
--- a/include/core/SkClipOp.h
+++ b/include/core/SkClipOp.h
@@ -10,20 +10,24 @@
 
 #include "SkTypes.h"
 
-// SkClipOp enum values always match the corresponding values in SkRegion::Op
-
 enum class SkClipOp {
     kDifference    = 0,
     kIntersect     = 1,
 
-    // Goal: remove these, since they can grow the current clip
-
+#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
     kUnion_deprecated             = 2,
     kXOR_deprecated               = 3,
     kReverseDifference_deprecated = 4,
     kReplace_deprecated           = 5,
+#else
+    kExtraEnumNeedInternallyPleaseIgnoreWillGoAway2   = 2,
+    kExtraEnumNeedInternallyPleaseIgnoreWillGoAway3   = 3,
+    kExtraEnumNeedInternallyPleaseIgnoreWillGoAway4   = 4,
+    kExtraEnumNeedInternallyPleaseIgnoreWillGoAway5   = 5,
+#endif
 
-    kMax_EnumValue = kReplace_deprecated,
+    // Used internally for validation, can only shrink to 1 when the deprecated flag is gone
+    kMax_EnumValue = 5,
 };
 
 #endif
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
index 4d54773..9d757a9 100644
--- a/include/core/SkColor.h
+++ b/include/core/SkColor.h
@@ -67,29 +67,43 @@
 /** return the blue byte from a SkColor value */
 #define SkColorGetB(color)      (((color) >>  0) & 0xFF)
 
-static inline SkColor SkColorSetA(SkColor c, U8CPU a) {
+static constexpr inline SkColor SkColorSetA(SkColor c, U8CPU a) {
     return (c & 0x00FFFFFF) | (a << 24);
 }
 
 // common colors
 
-#define SK_AlphaTRANSPARENT 0x00        //!< transparent SkAlpha value
-#define SK_AlphaOPAQUE      0xFF        //!< opaque SkAlpha value
+/** transparent SkAlpha value */
+#define SK_AlphaTRANSPARENT static_cast<SkAlpha>(0x00)
+/** opaque SkAlpha value */
+#define SK_AlphaOPAQUE      static_cast<SkAlpha>(0xFF)
 
-#define SK_ColorTRANSPARENT 0x00000000  //!< transparent SkColor value
+/** transparent SkColor value */
+#define SK_ColorTRANSPARENT static_cast<SkColor>(0x00000000)
 
-#define SK_ColorBLACK       0xFF000000  //!< black SkColor value
-#define SK_ColorDKGRAY      0xFF444444  //!< dark gray SkColor value
-#define SK_ColorGRAY        0xFF888888  //!< gray SkColor value
-#define SK_ColorLTGRAY      0xFFCCCCCC  //!< light gray SkColor value
-#define SK_ColorWHITE       0xFFFFFFFF  //!< white SkColor value
+/** black SkColor value */
+#define SK_ColorBLACK       static_cast<SkColor>(0xFF000000)
+/** dark gray SkColor value */
+#define SK_ColorDKGRAY      static_cast<SkColor>(0xFF444444)
+/** gray SkColor value */
+#define SK_ColorGRAY        static_cast<SkColor>(0xFF888888)
+/** light gray SkColor value */
+#define SK_ColorLTGRAY      static_cast<SkColor>(0xFFCCCCCC)
+/** white SkColor value */
+#define SK_ColorWHITE       static_cast<SkColor>(0xFFFFFFFF)
 
-#define SK_ColorRED         0xFFFF0000  //!< red SkColor value
-#define SK_ColorGREEN       0xFF00FF00  //!< green SkColor value
-#define SK_ColorBLUE        0xFF0000FF  //!< blue SkColor value
-#define SK_ColorYELLOW      0xFFFFFF00  //!< yellow SkColor value
-#define SK_ColorCYAN        0xFF00FFFF  //!< cyan SkColor value
-#define SK_ColorMAGENTA     0xFFFF00FF  //!< magenta SkColor value
+/** red SkColor value */
+#define SK_ColorRED         static_cast<SkColor>(0xFFFF0000)
+/** green SkColor value */
+#define SK_ColorGREEN       static_cast<SkColor>(0xFF00FF00)
+/** blue SkColor value */
+#define SK_ColorBLUE        static_cast<SkColor>(0xFF0000FF)
+/** yellow SkColor value */
+#define SK_ColorYELLOW      static_cast<SkColor>(0xFFFFFF00)
+/** cyan SkColor value */
+#define SK_ColorCYAN        static_cast<SkColor>(0xFF00FFFF)
+/** magenta SkColor value */
+#define SK_ColorMAGENTA     static_cast<SkColor>(0xFFFF00FF)
 
 ////////////////////////////////////////////////////////////////////////
 
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index a593087..7540162 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -18,6 +18,7 @@
 class SkArenaAlloc;
 class SkBitmap;
 class SkColorSpace;
+class SkColorSpaceXformer;
 class SkRasterPipeline;
 
 /**
@@ -71,10 +72,7 @@
     */
     virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) const = 0;
 
-    virtual void filterSpan4f(const SkPM4f src[], int count, SkPM4f result[]) const;
-
-    bool appendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                      bool shaderIsOpaque) const;
+    void appendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, bool shaderIsOpaque) const;
 
     enum Flags {
         /** If set the filter methods will not change the alpha channel of the colors.
@@ -160,8 +158,12 @@
 protected:
     SkColorFilter() {}
 
-    virtual bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                                bool shaderIsOpaque) const;
+    sk_sp<SkColorFilter> makeColorSpace(SkColorSpaceXformer* xformer) const {
+        return this->onMakeColorSpace(xformer);
+    }
+    virtual sk_sp<SkColorFilter> onMakeColorSpace(SkColorSpaceXformer*) const {
+        return sk_ref_sp(const_cast<SkColorFilter*>(this));
+    }
 
 private:
     /*
@@ -172,6 +174,19 @@
      *  e.g. compose(filter, compose(compose(filter, filter), filter)) --> 4
      */
     virtual int privateComposedFilterCount() const { return 1; }
+
+    /*
+     *  Returns true and sets |outer| and |inner| if this is a compose color filter.
+     *  Returns false otherwise.
+     */
+    virtual bool asACompose(SkColorFilter** /*outer*/, SkColorFilter** /*inner*/) const {
+        return false;
+    }
+
+    virtual void onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                                bool shaderIsOpaque) const = 0;
+
+    friend class SkColorSpaceXformer;
     friend class SkComposeColorFilter;
 
     typedef SkFlattenable INHERITED;
diff --git a/include/core/SkColorSpace.h b/include/core/SkColorSpace.h
index 6a4919b..1d421ca 100644
--- a/include/core/SkColorSpace.h
+++ b/include/core/SkColorSpace.h
@@ -164,8 +164,11 @@
      */
     static bool Equals(const SkColorSpace* src, const SkColorSpace* dst);
 
-protected:
-    SkColorSpace() {}
+private:
+    SkColorSpace() = default;
+    friend class SkColorSpace_Base;
+
+    using INHERITED = SkRefCnt;
 };
 
 enum class SkTransferFunctionBehavior {
diff --git a/include/core/SkColorSpaceXform.h b/include/core/SkColorSpaceXform.h
index ccd36bc..1e761a9 100644
--- a/include/core/SkColorSpaceXform.h
+++ b/include/core/SkColorSpaceXform.h
@@ -60,6 +60,15 @@
 
     virtual ~SkColorSpaceXform() {}
 
+    enum AlphaOp {
+        kPreserve_AlphaOp,      // just transfer src-alpha to dst-alpha
+        kPremul_AlphaOp,        // like kPreserve, but multiplies RGB by it
+        kSrcIsOpaque_AlphaOp,   // src alphas are all 1, this is a perf hint
+    };
+    static bool Apply(SkColorSpace* dstCS, ColorFormat dstFormat, void* dst,
+                      SkColorSpace* srcCS, ColorFormat srcFormat, const void* src,
+                      int count, AlphaOp);
+
 protected:
     SkColorSpaceXform() {}
 };
diff --git a/include/core/SkColorTable.h b/include/core/SkColorTable.h
index 07dfd67..40919d3 100644
--- a/include/core/SkColorTable.h
+++ b/include/core/SkColorTable.h
@@ -24,10 +24,12 @@
 */
 class SK_API SkColorTable : public SkRefCnt {
 public:
+    static sk_sp<SkColorTable> Make(const SkPMColor colors[], int count);
+
     /** Copy up to 256 colors into a new SkColorTable.
      */
     SkColorTable(const SkPMColor colors[], int count);
-    virtual ~SkColorTable();
+    ~SkColorTable() override;
 
     /** Returns the number of colors in the table.
      */
@@ -52,7 +54,7 @@
     void writeToBuffer(SkWriteBuffer&) const;
 
     // may return null
-    static SkColorTable* Create(SkReadBuffer&);
+    static sk_sp<SkColorTable> Create(SkReadBuffer&);
 
 private:
     enum AllocatedWithMalloc {
diff --git a/include/core/SkCrossContextImageData.h b/include/core/SkCrossContextImageData.h
deleted file mode 100644
index bbee6e7..0000000
--- a/include/core/SkCrossContextImageData.h
+++ /dev/null
@@ -1,68 +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 SkCrossContextImageData_DEFINED
-#define SkCrossContextImageData_DEFINED
-
-#include "SkImage.h"
-
-#if SK_SUPPORT_GPU
-#include "GrExternalTextureData.h"
-#endif
-
-class SK_API SkCrossContextImageData : SkNoncopyable {
-public:
-    /**
-     *  Decodes and uploads the encoded data to a texture using the supplied GrContext, then
-     *  returns an instance of SkCrossContextImageData that can be used to transport that texture
-     *  to a different GrContext, across thread boundaries. The GrContext used here, and the one
-     *  used to reconstruct the texture-backed image later must be in the same GL share group,
-     *  or otherwise be able to share resources. After calling this, you *must* construct exactly
-     *  one SkImage from the returned value, using SkImage::MakeFromCrossContextImageData.
-     *
-     *  The texture will be decoded and uploaded to be suitable for use with surfaces that have the
-     *  supplied destination color space. The color space of the texture itself will be determined
-     *  from the encoded data.
-     */
-    static std::unique_ptr<SkCrossContextImageData> MakeFromEncoded(
-        GrContext*, sk_sp<SkData>, SkColorSpace* dstColorSpace);
-
-private:
-    SkCrossContextImageData(sk_sp<SkImage> image) : fImage(std::move(image)) {
-        SkASSERT(!fImage->isTextureBacked());
-    }
-
-#if SK_SUPPORT_GPU
-    SkCrossContextImageData(const GrBackendTextureDesc& desc,
-                            std::unique_ptr<GrExternalTextureData> textureData,
-                            SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace)
-            : fAlphaType(alphaType)
-            , fColorSpace(std::move(colorSpace))
-            , fDesc(desc)
-            , fTextureData(std::move(textureData)) {
-        // Point our texture desc at our copy of the backend information
-        fDesc.fTextureHandle = fTextureData->getBackendObject();
-    }
-#endif
-
-    // For non-GPU backed images
-    sk_sp<SkImage> fImage;
-
-#if SK_SUPPORT_GPU
-    // GPU-backed images store some generic information (needed to reconstruct the SkImage),
-    // and some backend-specific info (to reconstruct the texture).
-    SkAlphaType fAlphaType;
-    sk_sp<SkColorSpace> fColorSpace;
-    GrBackendTextureDesc fDesc;
-    std::unique_ptr<GrExternalTextureData> fTextureData;
-#endif
-
-    friend class SkImage;
-};
-
-#endif
diff --git a/include/core/SkDocument.h b/include/core/SkDocument.h
index 6594511..9204243 100644
--- a/include/core/SkDocument.h
+++ b/include/core/SkDocument.h
@@ -171,8 +171,6 @@
                                      IXpsOMObjectFactory* xpsFactory,
                                      SkScalar dpi = SK_ScalarDefaultRasterDPI);
 #endif
-    // DEPRECATED; TODO(halcanary): remove this function after Chromium switches to new API.
-    static sk_sp<SkDocument> MakeXPS(SkWStream*) { return nullptr; }
 
     /**
      *  Begin a new page for the document, returning the canvas that will draw
diff --git a/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
index 88aeb7e..49c491e 100644
--- a/include/core/SkFlattenable.h
+++ b/include/core/SkFlattenable.h
@@ -78,7 +78,7 @@
         kSkPathEffect_Type,
         kSkPixelRef_Type,
         kSkRasterizer_Type,
-        kSkShader_Type,
+        kSkShaderBase_Type,
         kSkUnused_Type,     // used to be SkUnitMapper
         kSkXfermode_Type,
         kSkNormalSource_Type,
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
index 4b62e55..1c97f0c 100644
--- a/include/core/SkGraphics.h
+++ b/include/core/SkGraphics.h
@@ -72,6 +72,30 @@
      */
     static int SetFontCacheCountLimit(int count);
 
+    /*
+     *  Returns the maximum point size for text that may be cached.
+     *
+     *  Sizes above this will be drawn directly from the font's outline.
+     *  Setting this to a large value may speed up drawing larger text (repeatedly),
+     *  but could cause the cache to purge other sizes more often.
+     *
+     *  This value is a hint to the font engine, and the actual limit may be different due to
+     *  implementation specific details.
+     */
+    static int GetFontCachePointSizeLimit();
+
+    /*
+     *  Set the maximum point size for text that may be cached, returning the previous value.
+     *
+     *  Sizes above this will be drawn directly from the font's outline.
+     *  Setting this to a large value may speed up drawing larger text (repeatedly),
+     *  but could cause the cache to purge other sizes more often.
+     *
+     *  This value is a hint to the font engine, and the actual limit may be different due to
+     *  implementation specific details.
+     */
+    static int SetFontCachePointSizeLimit(int maxPointSize);
+
     /**
      *  For debugging purposes, this will attempt to purge the font cache. It
      *  does not change the limit, but will cause subsequent font measures and
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index f504897..bc0597c 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -18,17 +18,21 @@
 class SkData;
 class SkCanvas;
 class SkColorTable;
-class SkCrossContextImageData;
 class SkImageGenerator;
 class SkPaint;
 class SkPicture;
 class SkPixelSerializer;
 class SkString;
 class SkSurface;
+class GrBackendTexture;
 class GrContext;
 class GrContextThreadSafeProxy;
 class GrTexture;
 
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+struct AHardwareBuffer;
+#endif
+
 /**
  *  SkImage is an abstraction for drawing a rectagle of pixels, though the
  *  particular type of image could be actually storing its data on the GPU, or
@@ -67,7 +71,7 @@
      *  its pixel memory is shareable, it may be shared instead of copied.
      */
     static sk_sp<SkImage> MakeFromBitmap(const SkBitmap&);
-    
+
     /**
      *  Construct a new SkImage based on the given ImageGenerator. Returns NULL on error.
      *  This function will always take ownership of the passed generator.
@@ -90,6 +94,9 @@
      *  managing the lifetime of the underlying platform texture.
      *
      *  Will return NULL if the specified descriptor is unsupported.
+     *
+     *  It is preferred to use the new methods which take a GrBackendTexture instead of a
+     *  GrBackendTextureDesc. This method will eventually be removed.
      */
     static sk_sp<SkImage> MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc) {
         return MakeFromTexture(ctx, desc, kPremul_SkAlphaType, nullptr, nullptr, nullptr);
@@ -108,6 +115,9 @@
      *  no longer is holding a reference to it.
      *
      *  Will return NULL if the specified descriptor is unsupported.
+     *
+     *  It is preferred to use the new methods which take a GrBackendTexture instead of a
+     *  GrBackendTextureDesc. This method will eventually be removed.
      */
     static sk_sp<SkImage> MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
                                           SkAlphaType at, TextureReleaseProc trp,
@@ -121,21 +131,78 @@
     *  no longer is holding a reference to it.
     *
     *  Will return NULL if the specified descriptor is unsupported.
+     *
+     *  It is preferred to use the new methods which take a GrBackendTexture instead of a
+     *  GrBackendTextureDesc. This method will eventually be removed.
     */
     static sk_sp<SkImage> MakeFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType,
                                           sk_sp<SkColorSpace>, TextureReleaseProc, ReleaseContext);
 
     /**
+     *  Create a new image from the specified descriptor. Note - the caller is responsible for
+     *  managing the lifetime of the underlying platform texture.
+     *
+     *  Will return NULL if the specified backend texture is unsupported.
+     */
+    static sk_sp<SkImage> MakeFromTexture(GrContext* ctx,
+                                          const GrBackendTexture& tex, GrSurfaceOrigin origin,
+                                          SkAlphaType at, sk_sp<SkColorSpace> cs) {
+        return MakeFromTexture(ctx, tex, origin, at, cs, nullptr, nullptr);
+    }
+
+    /**
+     *  Create a new image from the GrBackendTexture. The underlying platform texture must stay
+     *  valid and unaltered until the specified release-proc is invoked, indicating that Skia
+     *  no longer is holding a reference to it.
+     *
+     *  Will return NULL if the specified backend texture is unsupported.
+     */
+    static sk_sp<SkImage> MakeFromTexture(GrContext*,
+                                          const GrBackendTexture&, GrSurfaceOrigin origin,
+                                          SkAlphaType, sk_sp<SkColorSpace>,
+                                          TextureReleaseProc, ReleaseContext);
+
+    /**
+     *  Decodes and uploads the encoded data to a GPU backed image using the supplied GrContext.
+     *  That image can be safely used by other GrContexts, across thread boundaries. The GrContext
+     *  used here, and the ones used to draw this image later must be in the same GL share group,
+     *  or otherwise be able to share resources.
+     *
+     *  When the image's ref count reaches zero, the original GrContext will destroy the texture,
+     *  asynchronously.
+     *
+     *  The texture will be decoded and uploaded to be suitable for use with surfaces that have the
+     *  supplied destination color space. The color space of the image itself will be determined
+     *  from the encoded data.
+     */
+    static sk_sp<SkImage> MakeCrossContextFromEncoded(GrContext*, sk_sp<SkData>, bool buildMips,
+                                                      SkColorSpace* dstColorSpace);
+
+    /**
      *  Create a new image from the specified descriptor. Note - Skia will delete or recycle the
      *  texture when the image is released.
      *
      *  Will return NULL if the specified descriptor is unsupported.
+     *
+     *  It is preferred to use the new methods which take a GrBackendTexture instead of a
+     *  GrBackendTextureDesc. This method will eventually be removed.
      */
     static sk_sp<SkImage> MakeFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&,
                                                  SkAlphaType = kPremul_SkAlphaType,
                                                  sk_sp<SkColorSpace> = nullptr);
 
     /**
+     *  Create a new image from the specified descriptor. Note - Skia will delete or recycle the
+     *  texture when the image is released.
+     *
+     *  Will return NULL if the specified backend texture is unsupported.
+     */
+    static sk_sp<SkImage> MakeFromAdoptedTexture(GrContext*,
+                                                 const GrBackendTexture&, GrSurfaceOrigin,
+                                                 SkAlphaType = kPremul_SkAlphaType,
+                                                 sk_sp<SkColorSpace> = nullptr);
+
+    /**
      *  Create a new image by copying the pixels from the specified y, u, v textures. The data
      *  from the textures is immediately ingested into the image and the textures can be modified or
      *  deleted after the function returns. The image will have the dimensions of the y texture.
@@ -169,6 +236,16 @@
                                           const SkMatrix*, const SkPaint*, BitDepth,
                                           sk_sp<SkColorSpace>);
 
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    /**
+     *  Create a new image from the an Android hardware buffer.
+     *  The new image takes a reference on the buffer.
+     */
+    static sk_sp<SkImage> MakeFromAHardwareBuffer(AHardwareBuffer*,
+                                                 SkAlphaType = kPremul_SkAlphaType,
+                                                 sk_sp<SkColorSpace> = nullptr);
+#endif
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
     int width() const { return fWidth; }
@@ -194,15 +271,14 @@
     bool isAlphaOnly() const;
     bool isOpaque() const { return SkAlphaTypeIsOpaque(this->alphaType()); }
 
-    /**
-     * Extracts YUV planes from the SkImage and stores them in client-provided memory. The sizes
-     * planes and rowBytes arrays are ordered [y, u, v].
-     */
-    bool readYUV8Planes(const SkISize[3], void* const planes[3], const size_t rowBytes[3],
-                        SkYUVColorSpace) const;
-
     sk_sp<SkShader> makeShader(SkShader::TileMode, SkShader::TileMode,
                                const SkMatrix* localMatrix = nullptr) const;
+    /**
+     *  Helper version of makeShader() that specifies Clamp tilemode.
+     */
+    sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr) const {
+        return this->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, localMatrix);
+    }
 
     /**
      *  If the image has direct access to its pixels (i.e. they are in local RAM)
@@ -222,6 +298,16 @@
     bool isTextureBacked() const;
 
     /**
+     *  Returns true if the image is able to be drawn to a particular type of device. If context
+     *  is nullptr, tests for drawability to CPU devices. Otherwise, tests for drawability to a GPU
+     *  device backed by context.
+     *
+     *  Texture-backed images may become invalid if their underlying GrContext is abandoned. Some
+     *  generator-backed images may be invalid for CPU and/or GPU.
+     */
+    bool isValid(GrContext* context) const;
+
+    /**
      *  Retrieves the backend API handle of the texture. If flushPendingGrContextIO then the
      *  GrContext will issue to the backend API any deferred IO operations on the texture before
      *  returning.
@@ -333,14 +419,6 @@
     sk_sp<SkImage> makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const;
 
     /**
-     *  Constructs a texture backed image from data that was previously uploaded on another thread
-     *  and GrContext. The GrContext used to upload the data must be in the same GL share group as
-     *  the one passed in here, or otherwise be able to share resources with the passed in context.
-     */
-    static sk_sp<SkImage> MakeFromCrossContextImageData(GrContext*,
-                                                        std::unique_ptr<SkCrossContextImageData>);
-
-    /**
      * If the image is texture-backed this will make a raster copy of it (or nullptr if reading back
      * the pixels fails). Otherwise, it returns the original image.
      */
@@ -437,10 +515,26 @@
      */
     bool isLazyGenerated() const;
 
-protected:
-    SkImage(int width, int height, uint32_t uniqueID);
+    /**
+     *  If |target| is supported, returns an SkImage in the |target| color space.
+     *  Otherwise, returns nullptr.
+     *
+     *  This will leave the image as is if it already in the |target| color space.
+     *  Otherwise, it will convert the pixels from the src color space to the |target|
+     *  color space.  If this->colorSpace() is nullptr, the src color space will be
+     *  treated as sRGB.
+     *
+     *  If |premulBehavior| is kIgnore, any premultiplication or unpremultiplication will
+     *  be performed in the gamma encoded space.  If it is kRespect, premultiplication is
+     *  assumed to be linear.
+     */
+    sk_sp<SkImage> makeColorSpace(sk_sp<SkColorSpace> target,
+                                  SkTransferFunctionBehavior premulBehavior) const;
 
 private:
+    SkImage(int width, int height, uint32_t uniqueID);
+    friend class SkImage_Base;
+
     static sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&,
                                                 const GrMipLevel* texels, int mipLevelCount,
                                                 SkBudgeted, SkDestinationSurfaceColorMode);
diff --git a/include/core/SkImageEncoder.h b/include/core/SkImageEncoder.h
index e4f746a..87d5170 100644
--- a/include/core/SkImageEncoder.h
+++ b/include/core/SkImageEncoder.h
@@ -18,13 +18,16 @@
  * @param  dst     results are written to this stream.
  * @param  src     source pixels.
  * @param  format  image format, not all formats are supported.
- * @param  quality range from 0-100, not all formats respect quality.
+ * @param  quality range from 0-100, this is supported by jpeg and webp.
+ *                 higher values correspond to improved visual quality, but less compression.
  *
  * @return false iff input is bad or format is unsupported.
  *
  * Will always return false if Skia is compiled without image
  * encoders.
  *
+ * Note that webp encodes will use webp lossy compression.
+ *
  * For examples of encoding an image to a file or to a block of memory,
  * see tools/sk_tool_utils.h.
  */
@@ -34,7 +37,6 @@
  * The following helper function wraps SkEncodeImage().
  */
 inline bool SkEncodeImage(SkWStream* dst, const SkBitmap& src, SkEncodedImageFormat f, int q) {
-    SkAutoLockPixels autoLockPixels(src);
     SkPixmap pixmap;
     return src.peekPixels(&pixmap) && SkEncodeImage(dst, pixmap, f, q);
 }
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index ea2afa2..a6ade97 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -20,6 +20,7 @@
 class GrContext;
 class GrFragmentProcessor;
 class SkColorFilter;
+class SkColorSpaceXformer;
 struct SkIPoint;
 class SkSpecialImage;
 class SkImageFilterCache;
@@ -283,6 +284,10 @@
 
     void flatten(SkWriteBuffer&) const override;
 
+    const CropRect* getCropRectIfSet() const {
+        return this->cropRectIsSet() ? &fCropRect : nullptr;
+    }
+
     /**
      *  This is the virtual which should be overridden by the derived class
      *  to perform image filtering.
@@ -395,8 +400,39 @@
     static sk_sp<SkSpecialImage> ImageToColorSpace(SkSpecialImage* src, const OutputProperties&);
 #endif
 
+    /**
+     *  Returns an image filter transformed into a new color space via the |xformer|.
+     */
+    sk_sp<SkImageFilter> makeColorSpace(SkColorSpaceXformer* xformer) const {
+        return this->onMakeColorSpace(xformer);
+    }
+    virtual sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const = 0;
+
 private:
+    // For makeColorSpace().
+    friend class ArithmeticImageFilterImpl;
+    friend class SkAlphaThresholdFilterImpl;
+    friend class SkBlurImageFilterImpl;
+    friend class SkColorFilterImageFilter;
+    friend class SkColorSpaceXformer;
+    friend class SkComposeImageFilter;
+    friend class SkDiffuseLightingImageFilter;
+    friend class SkDisplacementMapEffect;
+    friend class SkDropShadowImageFilter;
+    friend class SkImageSource;
+    friend class SkMagnifierImageFilter;
+    friend class SkMatrixConvolutionImageFilter;
+    friend class SkMatrixImageFilter;
+    friend class SkLocalMatrixImageFilter;
+    friend class SkMergeImageFilter;
+    friend class SkMorphologyImageFilter;
+    friend class SkOffsetImageFilter;
+    friend class SkSpecularLightingImageFilter;
+    friend class SkTileImageFilter;
+    friend class SkXfermodeImageFilter_Base;
+
     friend class SkGraphics;
+
     static void PurgeCache();
 
     void init(sk_sp<SkImageFilter>* inputs, int inputCount, const CropRect* cropRect);
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index 6263dee..4514a2d 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -35,16 +35,14 @@
     uint32_t uniqueID() const { return fUniqueID; }
 
     /**
-     *  Return a ref to the encoded (i.e. compressed) representation,
-     *  of this data. If the GrContext is non-null, then the caller is only interested in
-     *  gpu-specific formats, so the impl may return null even if they have encoded data,
-     *  assuming they know it is not suitable for the gpu.
+     *  Return a ref to the encoded (i.e. compressed) representation
+     *  of this data.
      *
      *  If non-NULL is returned, the caller is responsible for calling
      *  unref() on the data when it is finished.
      */
-    SkData* refEncodedData(GrContext* ctx = nullptr) {
-        return this->onRefEncodedData(ctx);
+    SkData* refEncodedData() {
+        return this->onRefEncodedData();
     }
 
     /**
@@ -53,6 +51,14 @@
     const SkImageInfo& getInfo() const { return fInfo; }
 
     /**
+     *  Can this generator be used to produce images that will be drawable to the specified context
+     *  (or to CPU, if context is nullptr)?
+     */
+    bool isValid(GrContext* context) const {
+        return this->onIsValid(context);
+    }
+
+    /**
      *  Decode into the given pixels, a block of memory of size at
      *  least (info.fHeight - 1) * rowBytes + (info.fWidth *
      *  bytesPerPixel)
@@ -60,7 +66,7 @@
      *  Repeated calls to this function should give the same results,
      *  allowing the PixelRef to be immutable.
      *
-     *  @param info A description of the format (config, size)
+     *  @param info A description of the format
      *         expected by the caller.  This can simply be identical
      *         to the info returned by getInfo().
      *
@@ -70,23 +76,23 @@
      *
      *         A size that does not match getInfo() implies a request
      *         to scale. If the generator cannot perform this scale,
-     *         it will return kInvalidScale.
+     *         it will return false.
      *
-     *  If info is kIndex8_SkColorType, then the caller must provide storage for up to 256
-     *  SkPMColor values in ctable. On success the generator must copy N colors into that storage,
-     *  (where N is the logical number of table entries) and set ctableCount to N.
-     *
-     *  If info is not kIndex8_SkColorType, then the last two parameters may be NULL. If ctableCount
-     *  is not null, it will be set to 0.
+     *         kIndex_8_SkColorType is not supported.
      *
      *  @return true on success.
      */
-    bool getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                   SkPMColor ctable[], int* ctableCount);
+    struct Options {
+        Options()
+            : fBehavior(SkTransferFunctionBehavior::kIgnore)
+        {}
+
+        SkTransferFunctionBehavior fBehavior;
+    };
+    bool getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options* options);
 
     /**
-     *  Simplified version of getPixels() that asserts that info is NOT kIndex8_SkColorType and
-     *  uses the default Options.
+     *  Simplified version of getPixels() that uses the default Options.
      */
     bool getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes);
 
@@ -156,8 +162,6 @@
                                                              SkImage::BitDepth,
                                                              sk_sp<SkColorSpace>);
 
-    bool tryGenerateBitmap(SkBitmap* bm, const SkImageInfo& info, SkBitmap::Allocator* allocator);
-
 protected:
     enum {
         kNeedNewImageUniqueID = 0
@@ -165,27 +169,24 @@
 
     SkImageGenerator(const SkImageInfo& info, uint32_t uniqueId = kNeedNewImageUniqueID);
 
-    virtual SkData* onRefEncodedData(GrContext* ctx);
-
-    virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                             SkPMColor ctable[], int* ctableCount);
-
-    virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const {
-        return false;
-    }
-    virtual bool onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) {
-        return false;
-    }
+    virtual SkData* onRefEncodedData() { return nullptr; }
+    virtual bool onGetPixels(const SkImageInfo&, void*, size_t, const Options&) { return false; }
+    virtual bool onIsValid(GrContext*) const { return true; }
+    virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const { return false; }
+    virtual bool onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) { return false; }
 
 #if SK_SUPPORT_GPU
+    virtual bool onCanGenerateTexture() const { return false; }
     virtual sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
-                                                    const SkIPoint&);
+                                                    const SkIPoint&);   // returns nullptr
 #endif
 
 private:
     const SkImageInfo fInfo;
     const uint32_t fUniqueID;
 
+    friend class SkImage_Lazy;
+
     // This is our default impl, which may be different on different platforms.
     // It is called from NewFromEncoded() after it has checked for any runtime factory.
     // The SkData will never be NULL, as that will have been checked by NewFromEncoded.
diff --git a/include/core/SkLights.h b/include/core/SkLights.h
index d91d919..5c66487 100644
--- a/include/core/SkLights.h
+++ b/include/core/SkLights.h
@@ -113,6 +113,8 @@
         bool operator!= (const Light& b) { return !(this->operator==(b)); }
 
     private:
+        friend class SkLights;
+
         LightType   fType;
         SkColor3f   fColor;           // linear (unpremul) color. Range is 0..1 in each channel.
 
@@ -191,6 +193,10 @@
     SkLights() {
         fAmbientLightColor.set(0.0f, 0.0f, 0.0f);
     }
+
+    friend class SkLightingShaderImpl;
+    sk_sp<SkLights> makeColorSpace(SkColorSpaceXformer* xformer) const;
+
     SkTArray<Light> fLights;
     SkColor3f fAmbientLightColor;
     typedef SkRefCnt INHERITED;
diff --git a/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h
index bb07fa2..e6d9727 100644
--- a/include/core/SkMallocPixelRef.h
+++ b/include/core/SkMallocPixelRef.h
@@ -22,12 +22,10 @@
      *  lifetime of the pixel storage buffer, as this pixelref will not try
      *  to delete it.
      *
-     *  The pixelref will ref() the colortable (if not NULL).
-     *
      *  Returns NULL on failure.
      */
-    static SkMallocPixelRef* NewDirect(const SkImageInfo&, void* addr,
-                                       size_t rowBytes, SkColorTable*);
+    static sk_sp<SkPixelRef> MakeDirect(const SkImageInfo&, void* addr,
+                                       size_t rowBytes, sk_sp<SkColorTable>);
 
     /**
      *  Return a new SkMallocPixelRef, automatically allocating storage for the
@@ -39,22 +37,18 @@
      *
      *  Returns NULL on failure.
      */
-    static SkMallocPixelRef* NewAllocate(const SkImageInfo& info,
-                                         size_t rowBytes, SkColorTable*);
+    static sk_sp<SkPixelRef> MakeAllocate(const SkImageInfo&, size_t rowBytes, sk_sp<SkColorTable>);
 
     /**
-     *  Identical to NewAllocate, except all pixel bytes are zeroed.
+     *  Identical to MakeAllocate, except all pixel bytes are zeroed.
      */
-    static SkMallocPixelRef* NewZeroed(const SkImageInfo& info,
-                                       size_t rowBytes, SkColorTable*);
+    static sk_sp<SkPixelRef> MakeZeroed(const SkImageInfo&, size_t rowBytes, sk_sp<SkColorTable>);
 
     /**
      *  Return a new SkMallocPixelRef with the provided pixel storage,
      *  rowBytes, and optional colortable. On destruction, ReleaseProc
      *  will be called.
      *
-     *  This pixelref will ref() the specified colortable (if not NULL).
-     *
      *  If ReleaseProc is NULL, the pixels will never be released. This
      *  can be useful if the pixels were stack allocated. However, such an
      *  SkMallocPixelRef must not live beyond its pixels (e.g. by copying
@@ -63,10 +57,10 @@
      *  Returns NULL on failure.
      */
     typedef void (*ReleaseProc)(void* addr, void* context);
-    static SkMallocPixelRef* NewWithProc(const SkImageInfo& info,
-                                         size_t rowBytes, SkColorTable*,
-                                         void* addr, ReleaseProc proc,
-                                         void* context);
+    static sk_sp<SkPixelRef> MakeWithProc(const SkImageInfo& info,
+                                          size_t rowBytes, sk_sp<SkColorTable>,
+                                          void* addr, ReleaseProc proc,
+                                          void* context);
 
     /**
      *  Return a new SkMallocPixelRef that will use the provided
@@ -74,51 +68,27 @@
      *  The SkData will be ref()ed and on destruction of the PielRef,
      *  the SkData will be unref()ed.
      *
-     *  This pixelref will ref() the specified colortable (if not NULL).
-     *
      *  Returns NULL on failure.
      */
-    static SkMallocPixelRef* NewWithData(const SkImageInfo& info,
-                                         size_t rowBytes,
-                                         SkColorTable* ctable,
-                                         SkData* data);
-
-    void* getAddr() const { return fStorage; }
-
-    class PRFactory : public SkPixelRefFactory {
-    public:
-        SkPixelRef* create(const SkImageInfo&, size_t rowBytes, SkColorTable*) override;
-    };
-
-    class ZeroedPRFactory : public SkPixelRefFactory {
-    public:
-        SkPixelRef* create(const SkImageInfo&, size_t rowBytes, SkColorTable*) override;
-    };
+    static sk_sp<SkPixelRef> MakeWithData(const SkImageInfo& info,
+                                          size_t rowBytes,
+                                          sk_sp<SkColorTable>,
+                                          sk_sp<SkData> data);
 
 protected:
-    // The ownPixels version of this constructor is deprecated.
-    SkMallocPixelRef(const SkImageInfo&, void* addr, size_t rb, SkColorTable*,
-                     bool ownPixels);
     ~SkMallocPixelRef() override;
 
-    bool onNewLockPixels(LockRec*) override;
-    void onUnlockPixels() override;
-    size_t getAllocatedSizeInBytes() const override;
-
 private:
     // Uses alloc to implement NewAllocate or NewZeroed.
-    static SkMallocPixelRef* NewUsing(void*(*alloc)(size_t),
-                                      const SkImageInfo&,
-                                      size_t rowBytes,
-                                      SkColorTable*);
+    static sk_sp<SkPixelRef> MakeUsing(void*(*alloc)(size_t),
+                                       const SkImageInfo&,
+                                       size_t rowBytes,
+                                       sk_sp<SkColorTable>);
 
-    void*           fStorage;
-    SkColorTable*   fCTable;
-    size_t          fRB;
-    ReleaseProc     fReleaseProc;
-    void*           fReleaseProcContext;
+    ReleaseProc fReleaseProc;
+    void*       fReleaseProcContext;
 
-    SkMallocPixelRef(const SkImageInfo&, void* addr, size_t rb, SkColorTable*,
+    SkMallocPixelRef(const SkImageInfo&, void* addr, size_t rb, sk_sp<SkColorTable>,
                      ReleaseProc proc, void* context);
 
     typedef SkPixelRef INHERITED;
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index d408fb1..1752f26 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -621,6 +621,7 @@
         return 0 == memcmp(fMat, m.fMat, sizeof(fMat));
     }
 
+    // mac chromium dbg requires SK_API to make operator== visible
     friend SK_API bool operator==(const SkMatrix& a, const SkMatrix& b);
     friend SK_API bool operator!=(const SkMatrix& a, const SkMatrix& b) {
         return !(a == b);
diff --git a/include/core/SkMilestone.h b/include/core/SkMilestone.h
index c6f32cb..e1fd729 100644
--- a/include/core/SkMilestone.h
+++ b/include/core/SkMilestone.h
@@ -5,5 +5,5 @@
  * found in the LICENSE file.
  */
 #ifndef SK_MILESTONE
-#define SK_MILESTONE "release/O"
+#define SK_MILESTONE 61
 #endif
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index caa5491..5b2283a 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -49,13 +49,14 @@
     SkPaint(SkPaint&& paint);
     ~SkPaint();
 
-    SkPaint& operator=(const SkPaint&);
-    SkPaint& operator=(SkPaint&&);
+    SkPaint& operator=(const SkPaint& paint);
+    SkPaint& operator=(SkPaint&& paint);
 
     /** operator== may give false negatives: two paints that draw equivalently
         may return false.  It will never give false positives: two paints that
         are not equivalent always return false.
     */
+    // cc_unittests requires SK_API to make operator== visible
     SK_API friend bool operator==(const SkPaint& a, const SkPaint& b);
     friend bool operator!=(const SkPaint& a, const SkPaint& b) {
         return !(a == b);
@@ -66,8 +67,8 @@
      */
     uint32_t getHash() const;
 
-    void flatten(SkWriteBuffer&) const;
-    void unflatten(SkReadBuffer&);
+    void flatten(SkWriteBuffer& buffer) const;
+    void unflatten(SkReadBuffer& buffer);
 
     /** Restores the paint to its initial settings.
     */
@@ -100,7 +101,7 @@
     */
     enum Flags {
         kAntiAlias_Flag       = 0x01,   //!< mask to enable antialiasing
-        kDither_Flag          = 0x04,   //!< mask to enable dithering
+        kDither_Flag          = 0x04,   //!< mask to enable dithering. see setDither()
         kFakeBoldText_Flag    = 0x20,   //!< mask to enable fake-bold text
         kLinearText_Flag      = 0x40,   //!< mask to enable linear-text
         kSubpixelText_Flag    = 0x80,   //!< mask to enable subpixel text positioning
@@ -114,11 +115,6 @@
         // with a bit-width and you'll have to expand it.
 
         kAllFlags = 0xFFFF,
-
-#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTDECORATION
-        kUnderlineText_Flag   = 0x08,
-        kStrikeThruText_Flag  = 0x10,
-#endif
     };
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
@@ -159,9 +155,12 @@
         return SkToBool(this->getFlags() & kDither_Flag);
     }
 
-    /** Helper for setFlags(), setting or clearing the kDither_Flag bit
-        @param dither   true to enable dithering, false to disable it
-        */
+    /**
+     *  Helper for setFlags(), setting or clearing the kDither_Flag bit
+     *  @param dither   true to enable dithering, false to disable it
+     *
+     *  Note: gradients ignore this setting and always dither.
+     */
     void setDither(bool dither);
 
     /** Helper for getFlags(), returning true if kLinearText_Flag bit is set
@@ -236,21 +235,7 @@
      *  X values, and drawText will places its glyphs vertically rather than
      *  horizontally.
      */
-    void setVerticalText(bool);
-
-    /** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set
-        @return true if the underlineText bit is set in the paint's flags.
-    */
-#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTDECORATION
-    bool isUnderlineText() const { return false; }
-#endif
-
-    /** Helper for getFlags(), returns true if kStrikeThruText_Flag bit is set
-        @return true if the strikeThruText bit is set in the paint's flags.
-    */
-#ifdef SK_SUPPORT_LEGACY_PAINT_TEXTDECORATION
-    bool isStrikeThruText() const { return false; }
-#endif
+    void setVerticalText(bool verticalText);
 
     /** Helper for getFlags(), returns true if kFakeBoldText_Flag bit is set
         @return true if the kFakeBoldText_Flag bit is set in the paint's flags.
@@ -501,7 +486,7 @@
      *  If shader is not NULL, its reference count is incremented.
      *  @param shader   May be NULL. The shader to be installed in the paint
      */
-    void setShader(sk_sp<SkShader>);
+    void setShader(sk_sp<SkShader> shader);
 
     /** Get the paint's colorfilter. If there is a colorfilter, its reference
         count is not changed.
@@ -516,7 +501,7 @@
         If filter is not NULL, its reference count is incremented.
         @param filter   May be NULL. The filter to be installed in the paint
     */
-    void setColorFilter(sk_sp<SkColorFilter>);
+    void setColorFilter(sk_sp<SkColorFilter> colorFilter);
 
     SkBlendMode getBlendMode() const { return (SkBlendMode)fBlendMode; }
     bool isSrcOver() const { return (SkBlendMode)fBlendMode == SkBlendMode::kSrcOver; }
@@ -540,7 +525,7 @@
                         paint
         @return         effect
     */
-    void setPathEffect(sk_sp<SkPathEffect>);
+    void setPathEffect(sk_sp<SkPathEffect> pathEffect);
 
     /** Get the paint's maskfilter object.
         <p />
@@ -560,7 +545,7 @@
                             the paint
         @return             maskfilter
     */
-    void setMaskFilter(sk_sp<SkMaskFilter>);
+    void setMaskFilter(sk_sp<SkMaskFilter> maskFilter);
 
     // These attributes are for text/fonts
 
@@ -583,7 +568,7 @@
                         paint
         @return         typeface
     */
-    void setTypeface(sk_sp<SkTypeface>);
+    void setTypeface(sk_sp<SkTypeface> typeface);
 
     /** Get the paint's rasterizer (or NULL).
         <p />
@@ -604,11 +589,11 @@
                           the paint.
         @return           rasterizer
     */
-    void setRasterizer(sk_sp<SkRasterizer>);
+    void setRasterizer(sk_sp<SkRasterizer> rasterizer);
 
     SkImageFilter* getImageFilter() const { return fImageFilter.get(); }
     sk_sp<SkImageFilter> refImageFilter() const;
-    void setImageFilter(sk_sp<SkImageFilter>);
+    void setImageFilter(sk_sp<SkImageFilter> imageFilter);
 
     /**
      *  Return the paint's SkDrawLooper (if any). Does not affect the looper's
@@ -627,9 +612,9 @@
      *  incremented.
      *  @param looper May be NULL. The new looper to be installed in the paint.
      */
-    void setDrawLooper(sk_sp<SkDrawLooper>);
+    void setDrawLooper(sk_sp<SkDrawLooper> drawLooper);
 
-    void setLooper(sk_sp<SkDrawLooper>);
+    void setLooper(sk_sp<SkDrawLooper> drawLooper);
 
     enum Align {
         kLeft_Align,
@@ -1002,6 +987,8 @@
      }
      */
     const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const {
+        // Things like stroking, etc... will do math on the bounds rect, assuming that it's sorted.
+        SkASSERT(orig.isSorted());
         SkPaint::Style style = this->getStyle();
         // ultra fast-case: filling with no effects that affect geometry
         if (kFill_Style == style) {
@@ -1025,29 +1012,15 @@
     // Take the style explicitly, so the caller can force us to be stroked
     // without having to make a copy of the paint just to change that field.
     const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage,
-                                      Style) const;
+                                      Style style) const;
 
-    /**
-     *  Return a matrix that applies the paint's text values: size, scale, skew
-     */
-    static SkMatrix* SetTextMatrix(SkMatrix* matrix, SkScalar size,
-                                   SkScalar scaleX, SkScalar skewX) {
-        matrix->setScale(size * scaleX, size);
-        if (skewX) {
-            matrix->postSkew(skewX, 0);
-        }
-        return matrix;
-    }
 
-    SkMatrix* setTextMatrix(SkMatrix* matrix) const {
-        return SetTextMatrix(matrix, fTextSize, fTextScaleX, fTextSkewX);
-    }
-
-    typedef const SkGlyph& (*GlyphCacheProc)(SkGlyphCache*, const char**);
 
     SK_TO_STRING_NONVIRT()
 
 private:
+    typedef const SkGlyph& (*GlyphCacheProc)(SkGlyphCache*, const char**);
+
     sk_sp<SkTypeface>     fTypeface;
     sk_sp<SkPathEffect>   fPathEffect;
     sk_sp<SkShader>       fShader;
@@ -1137,13 +1110,6 @@
          *  need not match per-se.
          */
         kCanonicalTextSizeForPaths  = 64,
-
-        /*
-         *  Above this size (taking into account CTM and textSize), we never use
-         *  the cache for bits or metrics (we might overflow), so we just ask
-         *  for a caononical size and post-transform that.
-         */
-        kMaxSizeForGlyphCache       = 256,
     };
 
     static bool TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM);
@@ -1153,11 +1119,7 @@
     // have change it to kCanonicalTextSizeForPaths.
     SkScalar setupForAsPaths();
 
-    static SkScalar MaxCacheSize2() {
-        static const SkScalar kMaxSize = SkIntToScalar(kMaxSizeForGlyphCache);
-        static const SkScalar kMag2Max = kMaxSize * kMaxSize;
-        return kMag2Max;
-    }
+    static SkScalar MaxCacheSize2();
 
     friend class SkAutoGlyphCache;
     friend class SkAutoGlyphCacheNoGamma;
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index d5b02c0..60aa44f 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -10,12 +10,8 @@
 
 #include "SkMatrix.h"
 #include "SkPathRef.h"
-#include "SkRefCnt.h"
 
-class SkReader32;
-class SkWriter32;
 class SkAutoPathBoundsUpdate;
-class SkString;
 class SkRRect;
 class SkWStream;
 
@@ -36,11 +32,12 @@
     };
 
     SkPath();
-    SkPath(const SkPath&);
+    SkPath(const SkPath& path);
     ~SkPath();
 
-    SkPath& operator=(const SkPath&);
-    friend  SK_API bool operator==(const SkPath&, const SkPath&);
+    SkPath& operator=(const SkPath& path);
+    // mac chromium dbg requires SK_API to make operator== visible
+    friend SK_API bool operator==(const SkPath& a, const SkPath& b);
     friend bool operator!=(const SkPath& a, const SkPath& b) {
         return !(a == b);
     }
@@ -150,7 +147,7 @@
      *  changed (e.g. lineTo(), addRect(), etc.) then the cached value will be
      *  reset to kUnknown_Convexity.
      */
-    void setConvexity(Convexity);
+    void setConvexity(Convexity convexity);
 
     /**
      *  Returns true if the path is flagged as being convex. This is not a
@@ -521,17 +518,17 @@
         current point on this contour. If there is no previous point, then a
         moveTo(0,0) is inserted automatically.
 
-        @param dx1   The amount to add to the x-coordinate of the last point on
+        @param x1   The amount to add to the x-coordinate of the last point on
                 this contour, to specify the 1st control point of a cubic curve
-        @param dy1   The amount to add to the y-coordinate of the last point on
+        @param y1   The amount to add to the y-coordinate of the last point on
                 this contour, to specify the 1st control point of a cubic curve
-        @param dx2   The amount to add to the x-coordinate of the last point on
+        @param x2   The amount to add to the x-coordinate of the last point on
                 this contour, to specify the 2nd control point of a cubic curve
-        @param dy2   The amount to add to the y-coordinate of the last point on
+        @param y2   The amount to add to the y-coordinate of the last point on
                 this contour, to specify the 2nd control point of a cubic curve
-        @param dx3   The amount to add to the x-coordinate of the last point on
+        @param x3   The amount to add to the x-coordinate of the last point on
                      this contour, to specify the end point of a cubic curve
-        @param dy3   The amount to add to the y-coordinate of the last point on
+        @param y3   The amount to add to the y-coordinate of the last point on
                      this contour, to specify the end point of a cubic curve
     */
     void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
@@ -1084,7 +1081,7 @@
      */
     bool contains(SkScalar x, SkScalar y) const;
 
-    void dump(SkWStream* , bool forceClose, bool dumpAsHex) const;
+    void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const;
     void dump() const;
     void dumpHex() const;
 
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 133d3c9..70f2acb 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -200,10 +200,11 @@
     // V50: SkXfermode -> SkBlendMode
     // V51: more SkXfermode -> SkBlendMode
     // V52: Remove SkTextBlob::fRunCount
+    // V53: SaveLayerRec clip mask
 
     // Only SKPs within the min/current picture version range (inclusive) can be read.
     static const uint32_t     MIN_PICTURE_VERSION = 35;     // Produced by Chrome M39.
-    static const uint32_t CURRENT_PICTURE_VERSION = 52;
+    static const uint32_t CURRENT_PICTURE_VERSION = 53;
 
     static_assert(MIN_PICTURE_VERSION <= 41,
                   "Remove kFontFileName and related code from SkFontDescriptor.cpp.");
diff --git a/include/core/SkPictureRecorder.h b/include/core/SkPictureRecorder.h
index 9e29e0d..09839cb 100644
--- a/include/core/SkPictureRecorder.h
+++ b/include/core/SkPictureRecorder.h
@@ -8,7 +8,6 @@
 #ifndef SkPictureRecorder_DEFINED
 #define SkPictureRecorder_DEFINED
 
-#include "../private/SkMiniRecorder.h"
 #include "SkBBHFactory.h"
 #include "SkPicture.h"
 #include "SkRefCnt.h"
@@ -22,6 +21,7 @@
 class GrContext;
 class SkCanvas;
 class SkDrawable;
+class SkMiniRecorder;
 class SkPictureRecord;
 class SkRecord;
 class SkRecorder;
@@ -38,7 +38,6 @@
     };
 
     enum FinishFlags {
-        kReturnNullForEmpty_FinishFlag  = 1 << 0,   // no draw-ops will return nullptr
     };
 
     /** Returns the canvas that records the drawing commands.
@@ -117,7 +116,7 @@
     sk_sp<SkBBoxHierarchy>      fBBH;
     std::unique_ptr<SkRecorder> fRecorder;
     sk_sp<SkRecord>             fRecord;
-    SkMiniRecorder              fMiniRecorder;
+    std::unique_ptr<SkMiniRecorder> fMiniRecorder;
 
     typedef SkNoncopyable INHERITED;
 };
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index 34ad7c1..4613209 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -27,80 +27,21 @@
 
 /** \class SkPixelRef
 
-    This class is the smart container for pixel memory, and is used with
-    SkBitmap. A pixelref is installed into a bitmap, and then the bitmap can
-    access the actual pixel memory by calling lockPixels/unlockPixels.
-
+    This class is the smart container for pixel memory, and is used with SkBitmap.
     This class can be shared/accessed between multiple threads.
 */
 class SK_API SkPixelRef : public SkRefCnt {
 public:
-    explicit SkPixelRef(const SkImageInfo&);
-    virtual ~SkPixelRef();
 
-    const SkImageInfo& info() const {
-        return fInfo;
-    }
+    SkPixelRef(int width, int height, void* addr, size_t rowBytes, sk_sp<SkColorTable> = nullptr);
 
-    /** Return the pixel memory returned from lockPixels, or null if the
-        lockCount is 0.
-    */
-    void* pixels() const { return fRec.fPixels; }
+    ~SkPixelRef() override;
 
-    /** Return the current colorTable (if any) if pixels are locked, or null.
-    */
-    SkColorTable* colorTable() const { return fRec.fColorTable; }
-
-    size_t rowBytes() const { return fRec.fRowBytes; }
-
-    /**
-     *  To access the actual pixels of a pixelref, it must be "locked".
-     *  Calling lockPixels returns a LockRec struct (on success).
-     */
-    struct LockRec {
-        LockRec() : fPixels(NULL), fColorTable(NULL) {}
-
-        void*           fPixels;
-        SkColorTable*   fColorTable;
-        size_t          fRowBytes;
-
-        void zero() { sk_bzero(this, sizeof(*this)); }
-
-        bool isZero() const {
-            return NULL == fPixels && NULL == fColorTable && 0 == fRowBytes;
-        }
-    };
-
-    SkDEBUGCODE(bool isLocked() const { return fLockCount > 0; })
-    SkDEBUGCODE(int getLockCount() const { return fLockCount; })
-
-    /**
-     *  Call to access the pixel memory. Return true on success. Balance this
-     *  with a call to unlockPixels().
-     */
-    bool lockPixels();
-
-    /**
-     *  Call to access the pixel memory. On success, return true and fill out
-     *  the specified rec. On failure, return false and ignore the rec parameter.
-     *  Balance this with a call to unlockPixels().
-     */
-    bool lockPixels(LockRec* rec);
-
-    /** Call to balanace a previous call to lockPixels(). Returns the pixels
-        (or null) after the unlock. NOTE: lock calls can be nested, but the
-        matching number of unlock calls must be made in order to free the
-        memory (if the subclass implements caching/deferred-decoding.)
-    */
-    void unlockPixels();
-
-    /**
-     *  Some bitmaps can return a copy of their pixels for lockPixels(), but
-     *  that copy, if modified, will not be pushed back. These bitmaps should
-     *  not be used as targets for a raster device/canvas (since all pixels
-     *  modifications will be lost when unlockPixels() is called.)
-     */
-    bool lockPixelsAreWritable() const;
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    void* pixels() const { return fPixels; }
+    SkColorTable* colorTable() const { return fCTable.get(); }
+    size_t rowBytes() const { return fRowBytes; }
 
     /** Returns a non-zero, unique value corresponding to the pixels in this
         pixelref. Each time the pixels are changed (and notifyPixelsChanged is
@@ -127,13 +68,6 @@
      */
     void notifyPixelsChanged();
 
-    /**
-     *  Change the info's AlphaType. Note that this does not automatically
-     *  invalidate the generation ID. If the pixel values themselves have
-     *  changed, then you must explicitly call notifyPixelsChanged() as well.
-     */
-    void changeAlphaType(SkAlphaType at);
-
     /** Returns true if this pixelref is marked as immutable, meaning that the
         contents of its pixels will not change for the lifetime of the pixelref.
     */
@@ -145,53 +79,6 @@
     */
     void setImmutable();
 
-    /** Return the optional URI string associated with this pixelref. May be
-        null.
-    */
-    const char* getURI() const { return fURI.size() ? fURI.c_str() : NULL; }
-
-    /** Copy a URI string to this pixelref, or clear the URI if the uri is null
-     */
-    void setURI(const char uri[]) {
-        fURI.set(uri);
-    }
-
-    /** Copy a URI string to this pixelref
-     */
-    void setURI(const char uri[], size_t len) {
-        fURI.set(uri, len);
-    }
-
-    /** Assign a URI string to this pixelref.
-    */
-    void setURI(const SkString& uri) { fURI = uri; }
-
-    struct LockRequest {
-        SkISize         fSize;
-        SkFilterQuality fQuality;
-    };
-
-    struct LockResult {
-        LockResult() : fPixels(NULL), fCTable(NULL) {}
-
-        void        (*fUnlockProc)(void* ctx);
-        void*       fUnlockContext;
-
-        const void* fPixels;
-        SkColorTable* fCTable;  // should be NULL unless colortype is kIndex8
-        size_t      fRowBytes;
-        SkISize     fSize;
-
-        void unlock() {
-            if (fUnlockProc) {
-                fUnlockProc(fUnlockContext);
-                fUnlockProc = NULL; // can't unlock twice!
-            }
-        }
-    };
-
-    bool requestLock(const LockRequest&, LockResult*);
-
     // Register a listener that may be called the next time our generation ID changes.
     //
     // We'll only call the listener if we're confident that we are the only SkPixelRef with this
@@ -216,72 +103,21 @@
 
     virtual SkDiscardableMemory* diagnostic_only_getDiscardable() const { return NULL; }
 
-    /**
-     *  Returns true if the pixels are generated on-the-fly (when required).
-     */
-    bool isLazyGenerated() const { return this->onIsLazyGenerated(); }
-
 protected:
-    /**
-     *  On success, returns true and fills out the LockRec for the pixels. On
-     *  failure returns false and ignores the LockRec parameter.
-     *
-     *  The caller will have already acquired a mutex for thread safety, so this
-     *  method need not do that.
-     */
-    virtual bool onNewLockPixels(LockRec*) = 0;
-
-    /**
-     *  Balancing the previous successful call to onNewLockPixels. The locked
-     *  pixel address will no longer be referenced, so the subclass is free to
-     *  move or discard that memory.
-     *
-     *  The caller will have already acquired a mutex for thread safety, so this
-     *  method need not do that.
-     */
-    virtual void onUnlockPixels() = 0;
-
-    /** Default impl returns true */
-    virtual bool onLockPixelsAreWritable() const;
-
     // default impl does nothing.
     virtual void onNotifyPixelsChanged();
 
-    /**
-     *  Returns the size (in bytes) of the internally allocated memory.
-     *  This should be implemented in all serializable SkPixelRef derived classes.
-     *  SkBitmap::fPixelRefOffset + SkBitmap::getSafeSize() should never overflow this value,
-     *  otherwise the rendering code may attempt to read memory out of bounds.
-     *
-     *  @return default impl returns 0.
-     */
-    virtual size_t getAllocatedSizeInBytes() const;
-
-    virtual bool onRequestLock(const LockRequest&, LockResult*);
-
-    virtual bool onIsLazyGenerated() const { return false; }
-
-    /** Return the mutex associated with this pixelref. This value is assigned
-        in the constructor, and cannot change during the lifetime of the object.
-    */
-    SkBaseMutex* mutex() const { return &fMutex; }
-
-    // only call from constructor. Flags this to always be locked, removing
-    // the need to grab the mutex and call onLockPixels/onUnlockPixels.
-    // Performance tweak to avoid those calls (esp. in multi-thread use case).
-    void setPreLocked(void*, size_t rowBytes, SkColorTable*);
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    // This is undefined if there are clients in-flight trying to use us
+    void android_only_reset(int width, int height, size_t rowBytes, sk_sp<SkColorTable>);
+#endif
 
 private:
-    mutable SkMutex fMutex;
-
-    // mostly const. fInfo.fAlpahType can be changed at runtime.
-    const SkImageInfo fInfo;
-
-    // LockRec is only valid if we're in a locked state (isLocked())
-    LockRec         fRec;
-    int             fLockCount;
-
-    bool lockPixelsInsideMutex();
+    int                 fWidth;
+    int                 fHeight;
+    sk_sp<SkColorTable> fCTable;
+    void*               fPixels;
+    size_t              fRowBytes;
 
     // Bottom bit indicates the Gen ID is unique.
     bool genIDIsUnique() const { return SkToBool(fTaggedGenID.load() & 1); }
@@ -293,8 +129,6 @@
 
     SkTDArray<GenIDChangeListener*> fGenIDChangeListeners;  // pointers are owned
 
-    SkString    fURI;
-
     // Set true by caches when they cache content that's derived from the current pixels.
     SkAtomic<bool> fAddedToCache;
 
@@ -304,9 +138,6 @@
         kImmutable,             // Once set to this state, it never leaves.
     } fMutability : 8;          // easily fits inside a byte
 
-    // only ever set in constructor, const after that
-    bool fPreLocked;
-
     void needsNewGenID();
     void callGenIDChangeListeners();
 
@@ -314,32 +145,16 @@
     void restoreMutability();
     friend class SkSurface_Raster;   // For the two methods above.
 
-    bool isPreLocked() const { return fPreLocked; }
     friend class SkImage_Raster;
     friend class SkSpecialImage_Raster;
 
-    // When copying a bitmap to another with the same shape and config, we can safely
-    // clone the pixelref generation ID too, which makes them equivalent under caching.
-    friend class SkBitmap;  // only for cloneGenID
-    void cloneGenID(const SkPixelRef&);
-
     void setImmutableWithID(uint32_t genID);
     friend class SkImage_Gpu;
-    friend class SkImageCacherator;
+    friend class SkImage_Lazy;
     friend class SkSpecialImage_Gpu;
+    friend void SkBitmapCache_setImmutableWithID(SkPixelRef*, uint32_t);
 
     typedef SkRefCnt INHERITED;
 };
 
-class SkPixelRefFactory : public SkRefCnt {
-public:
-    /**
-     *  Allocate a new pixelref matching the specified ImageInfo, allocating
-     *  the memory for the pixels. If the ImageInfo requires a ColorTable,
-     *  the pixelref will ref() the colortable.
-     *  On failure return NULL.
-     */
-    virtual SkPixelRef* create(const SkImageInfo&, size_t rowBytes, SkColorTable*) = 0;
-};
-
 #endif
diff --git a/include/core/SkPixmap.h b/include/core/SkPixmap.h
index 6cf948e..1e79bba 100644
--- a/include/core/SkPixmap.h
+++ b/include/core/SkPixmap.h
@@ -183,10 +183,15 @@
     // copy methods
 
     bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                    int srcX, int srcY) const;
+                    int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
     bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes) const {
         return this->readPixels(dstInfo, dstPixels, dstRowBytes, 0, 0);
     }
+    bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int srcX,
+                    int srcY) const {
+        return this->readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY,
+                                SkTransferFunctionBehavior::kRespect);
+    }
     bool readPixels(const SkPixmap& dst, int srcX, int srcY) const {
         return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), srcX, srcY);
     }
@@ -219,54 +224,4 @@
     SkImageInfo     fInfo;
 };
 
-/////////////////////////////////////////////////////////////////////////////////////////////
-
-/////////////////////////////////////////////////////////////////////////////////////////////
-
-class SK_API SkAutoPixmapUnlock : ::SkNoncopyable {
-public:
-    SkAutoPixmapUnlock() : fUnlockProc(NULL), fIsLocked(false) {}
-    SkAutoPixmapUnlock(const SkPixmap& pm, void (*unlock)(void*), void* ctx)
-        : fUnlockProc(unlock), fUnlockContext(ctx), fPixmap(pm), fIsLocked(true)
-    {}
-    ~SkAutoPixmapUnlock() { this->unlock(); }
-
-    /**
-     *  Return the currently locked pixmap. Undefined if it has been unlocked.
-     */
-    const SkPixmap& pixmap() const {
-        SkASSERT(this->isLocked());
-        return fPixmap;
-    }
-
-    bool isLocked() const { return fIsLocked; }
-
-    /**
-     *  Unlocks the pixmap. Can safely be called more than once as it will only call the underlying
-     *  unlock-proc once.
-     */
-    void unlock() {
-        if (fUnlockProc) {
-            SkASSERT(fIsLocked);
-            fUnlockProc(fUnlockContext);
-            fUnlockProc = NULL;
-            fIsLocked = false;
-        }
-    }
-
-    /**
-     *  If there is a currently locked pixmap, unlock it, then copy the specified pixmap
-     *  and (optional) unlock proc/context.
-     */
-    void reset(const SkPixmap& pm, void (*unlock)(void*), void* ctx);
-
-private:
-    void        (*fUnlockProc)(void*);
-    void*       fUnlockContext;
-    SkPixmap    fPixmap;
-    bool        fIsLocked;
-
-    friend class SkBitmap;
-};
-
 #endif
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index c34397c..9628775 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -131,11 +131,20 @@
 #  define SK_DUMP_GOOGLE3_STACK()
 #endif
 
+#ifdef SK_BUILD_FOR_WIN
+// permits visual studio to follow error back to source
+#define SK_DUMP_LINE_FORMAT(message) \
+    SkDebugf("%s(%d): fatal error: \"%s\"\n", __FILE__, __LINE__, message)
+#else
+#define SK_DUMP_LINE_FORMAT(message) \
+    SkDebugf("%s:%d: fatal error: \"%s\"\n", __FILE__, __LINE__, message)
+#endif
+
 #ifndef SK_ABORT
 #  define SK_ABORT(message) \
     do { \
        SkNO_RETURN_HINT(); \
-       SkDebugf("%s:%d: fatal error: \"%s\"\n", __FILE__, __LINE__, message); \
+       SK_DUMP_LINE_FORMAT(message); \
        SK_DUMP_GOOGLE3_STACK(); \
        sk_abort_no_print(); \
     } while (false)
@@ -248,14 +257,6 @@
 #  define SK_ATTR_DEPRECATED(msg) SK_ATTRIBUTE(deprecated)
 #endif
 
-#if !defined(SK_ATTR_EXTERNALLY_DEPRECATED)
-#  if !defined(SK_INTERNAL)
-#    define SK_ATTR_EXTERNALLY_DEPRECATED(msg) SK_ATTR_DEPRECATED(msg)
-#  else
-#    define SK_ATTR_EXTERNALLY_DEPRECATED(msg)
-#  endif
-#endif
-
 /**
  * If your judgment is better than the compiler's (i.e. you've profiled it),
  * you can use SK_ALWAYS_INLINE to force inlining. E.g.
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
index 3b691aa..4b7a33e 100644
--- a/include/core/SkRRect.h
+++ b/include/core/SkRRect.h
@@ -110,7 +110,7 @@
     inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
     inline bool isComplex() const { return kComplex_Type == this->getType(); }
 
-    bool allCornersCircular() const;
+    bool allCornersCircular(SkScalar tolerance = SK_ScalarNearlyZero) const;
 
     SkScalar width() const { return fRect.width(); }
     SkScalar height() const { return fRect.height(); }
diff --git a/include/core/SkRWBuffer.h b/include/core/SkRWBuffer.h
index 451933f..a938db2 100644
--- a/include/core/SkRWBuffer.h
+++ b/include/core/SkRWBuffer.h
@@ -30,6 +30,7 @@
     class SK_API Iter {
     public:
         Iter(const SkROBuffer*);
+        Iter(const sk_sp<SkROBuffer>&);
 
         void reset(const SkROBuffer*);
 
@@ -89,8 +90,11 @@
      */
     void append(const void* buffer, size_t length, size_t reserve = 0);
 
-    SkROBuffer* newRBufferSnapshot() const;
-    SkStreamAsset* newStreamSnapshot() const;
+    sk_sp<SkROBuffer> makeROBufferSnapshot() const {
+        return sk_sp<SkROBuffer>(new SkROBuffer(fHead, fTotalUsed, fTail));
+    }
+
+    std::unique_ptr<SkStreamAsset> makeStreamSnapshot() const;
 
 #ifdef SK_DEBUG
     void validate() const;
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index bcdc4f1..a7b8b11 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -327,8 +327,7 @@
         otherwise return false and do not change this rectangle.
         If either rectangle is empty, do nothing and return false.
     */
-    bool intersect(int32_t left, int32_t top,
-                                         int32_t right, int32_t bottom) {
+    bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
         if (left < right && top < bottom && !this->isEmpty() &&
                 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
             if (fLeft < left) fLeft = left;
@@ -377,7 +376,22 @@
         and may have crossed over each other.
         When this returns, left <= right && top <= bottom
     */
-    void sort();
+    void sort() {
+        if (fLeft > fRight) {
+            SkTSwap<int32_t>(fLeft, fRight);
+        }
+        if (fTop > fBottom) {
+            SkTSwap<int32_t>(fTop, fBottom);
+        }
+    }
+
+    /**
+     *  Return a new Rect that is the sorted version of this rect (left <= right, top <= bottom).
+     */
+    SkIRect makeSorted() const {
+        return MakeLTRB(SkMin32(fLeft, fRight), SkMin32(fTop, fBottom),
+                        SkMax32(fLeft, fRight), SkMax32(fTop, fBottom));
+    }
 
     static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
         static const SkIRect gEmpty = { 0, 0, 0, 0 };
@@ -457,6 +471,11 @@
      */
     bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
 
+    /**
+     *  Return true if the rectangle's width and height are >= 0
+     */
+    bool isSorted() const { return fLeft <= fRight && fTop <= fBottom; }
+
     bool isLargest() const { return SK_ScalarMin == fLeft &&
                                     SK_ScalarMin == fTop &&
                                     SK_ScalarMax == fRight &&
@@ -889,6 +908,14 @@
     }
 
     /**
+     *  Return a new Rect that is the sorted version of this rect (left <= right, top <= bottom).
+     */
+    SkRect makeSorted() const {
+        return MakeLTRB(SkMinScalar(fLeft, fRight), SkMinScalar(fTop, fBottom),
+                        SkMaxScalar(fLeft, fRight), SkMaxScalar(fTop, fBottom));
+    }
+
+    /**
      *  cast-safe way to treat the rect as an array of (4) SkScalars.
      */
     const SkScalar* asScalars() const { return &fLeft; }
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
index 0dd968c..2c409c4 100644
--- a/include/core/SkScalar.h
+++ b/include/core/SkScalar.h
@@ -215,10 +215,4 @@
     return true;
 }
 
-#ifdef SK_SUPPORT_LEGACY_SCALARMUL
-    #define SkScalarMul(a, b)       ((SkScalar)(a) * (b))
-    #define SkScalarMulAdd(a, b, c) ((SkScalar)(a) * (b) + (c))
-    #define SkScalarMulDiv(a, b, c) ((SkScalar)(a) * (b) / (c))
-#endif
-
 #endif
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index e49ad5f..7f78d4b 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -20,6 +20,7 @@
 class SkArenaAlloc;
 class SkColorFilter;
 class SkColorSpace;
+class SkColorSpaceXformer;
 class SkImage;
 class SkPath;
 class SkPicture;
@@ -39,17 +40,6 @@
  */
 class SK_API SkShader : public SkFlattenable {
 public:
-    SkShader(const SkMatrix* localMatrix = NULL);
-    ~SkShader() override;
-
-    /**
-     *  Returns the local matrix.
-     *
-     *  FIXME: This can be incorrect for a Shader with its own local matrix
-     *  that is also wrapped via CreateLocalMatrixShader.
-     */
-    const SkMatrix& getLocalMatrix() const { return fLocalMatrix; }
-
     enum TileMode {
         /** replicate the edge color if the shader draws outside of its
          *  original bounds
@@ -74,23 +64,13 @@
         kTileModeCount = kMirror_TileMode + 1
     };
 
-    // override these in your subclass
-
-    enum Flags {
-        //!< set if all of the colors will be opaque
-        kOpaqueAlpha_Flag = 1 << 0,
-
-        /** set if the spans only vary in X (const in Y).
-            e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient
-            that varies from left-to-right. This flag specifies this for
-            shadeSpan().
-         */
-        kConstInY32_Flag = 1 << 1,
-
-        /** hint for the blitter that 4f is the preferred shading mode.
-         */
-        kPrefers4f_Flag  = 1 << 2,
-    };
+    /**
+     *  Returns the local matrix.
+     *
+     *  FIXME: This can be incorrect for a Shader with its own local matrix
+     *  that is also wrapped via CreateLocalMatrixShader.
+     */
+    const SkMatrix& getLocalMatrix() const;
 
     /**
      *  Returns true if the shader is guaranteed to produce only opaque
@@ -100,150 +80,13 @@
      */
     virtual bool isOpaque() const { return false; }
 
-    /**
-     *  Returns true if the shader is guaranteed to produce only a single color.
-     *  Subclasses can override this to allow loop-hoisting optimization.
-     */
-    virtual bool isConstant() const { return false; }
-
-    /**
-     *  ContextRec acts as a parameter bundle for creating Contexts.
-     */
-    struct ContextRec {
-        enum DstType {
-            kPMColor_DstType, // clients prefer shading into PMColor dest
-            kPM4f_DstType,    // clients prefer shading into PM4f dest
-        };
-
-        ContextRec(const SkPaint& paint, const SkMatrix& matrix, const SkMatrix* localM,
-                   DstType dstType, SkColorSpace* dstColorSpace)
-            : fPaint(&paint)
-            , fMatrix(&matrix)
-            , fLocalMatrix(localM)
-            , fPreferredDstType(dstType)
-            , fDstColorSpace(dstColorSpace) {}
-
-        const SkPaint*  fPaint;            // the current paint associated with the draw
-        const SkMatrix* fMatrix;           // the current matrix in the canvas
-        const SkMatrix* fLocalMatrix;      // optional local matrix
-        const DstType   fPreferredDstType; // the "natural" client dest type
-        SkColorSpace*   fDstColorSpace;    // the color space of the dest surface (if any)
-    };
-
-    class Context : public ::SkNoncopyable {
-    public:
-        Context(const SkShader& shader, const ContextRec&);
-
-        virtual ~Context();
-
-        /**
-         *  Called sometimes before drawing with this shader. Return the type of
-         *  alpha your shader will return. The default implementation returns 0.
-         *  Your subclass should override if it can (even sometimes) report a
-         *  non-zero value, since that will enable various blitters to perform
-         *  faster.
-         */
-        virtual uint32_t getFlags() const { return 0; }
-
-        /**
-         *  Called for each span of the object being drawn. Your subclass should
-         *  set the appropriate colors (with premultiplied alpha) that correspond
-         *  to the specified device coordinates.
-         */
-        virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0;
-
-        virtual void shadeSpan4f(int x, int y, SkPM4f[], int count);
-
-        struct BlitState;
-        typedef void (*BlitBW)(BlitState*,
-                               int x, int y, const SkPixmap&, int count);
-        typedef void (*BlitAA)(BlitState*,
-                               int x, int y, const SkPixmap&, int count, const SkAlpha[]);
-
-        struct BlitState {
-            // inputs
-            Context*    fCtx;
-            SkBlendMode fMode;
-
-            // outputs
-            enum { N = 2 };
-            void*       fStorage[N];
-            BlitBW      fBlitBW;
-            BlitAA      fBlitAA;
-        };
-
-        // Returns true if one or more of the blitprocs are set in the BlitState
-        bool chooseBlitProcs(const SkImageInfo& info, BlitState* state) {
-            state->fBlitBW = nullptr;
-            state->fBlitAA = nullptr;
-            if (this->onChooseBlitProcs(info, state)) {
-                SkASSERT(state->fBlitBW || state->fBlitAA);
-                return true;
-            }
-            return false;
-        }
-
-        /**
-         * The const void* ctx is only const because all the implementations are const.
-         * This can be changed to non-const if a new shade proc needs to change the ctx.
-         */
-        typedef void (*ShadeProc)(const void* ctx, int x, int y, SkPMColor[], int count);
-        virtual ShadeProc asAShadeProc(void** ctx);
-
-        /**
-         *  Similar to shadeSpan, but only returns the alpha-channel for a span.
-         *  The default implementation calls shadeSpan() and then extracts the alpha
-         *  values from the returned colors.
-         */
-        virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
-
-        // Notification from blitter::blitMask in case we need to see the non-alpha channels
-        virtual void set3DMask(const SkMask*) {}
-
-    protected:
-        // Reference to shader, so we don't have to dupe information.
-        const SkShader& fShader;
-
-        enum MatrixClass {
-            kLinear_MatrixClass,            // no perspective
-            kFixedStepInX_MatrixClass,      // fast perspective, need to call fixedStepInX() each
-                                            // scanline
-            kPerspective_MatrixClass        // slow perspective, need to mappoints each pixel
-        };
-        static MatrixClass ComputeMatrixClass(const SkMatrix&);
-
-        uint8_t         getPaintAlpha() const { return fPaintAlpha; }
-        const SkMatrix& getTotalInverse() const { return fTotalInverse; }
-        MatrixClass     getInverseClass() const { return (MatrixClass)fTotalInverseClass; }
-        const SkMatrix& getCTM() const { return fCTM; }
-
-        virtual bool onChooseBlitProcs(const SkImageInfo&, BlitState*) { return false; }
-
-    private:
-        SkMatrix    fCTM;
-        SkMatrix    fTotalInverse;
-        uint8_t     fPaintAlpha;
-        uint8_t     fTotalInverseClass;
-
-        typedef SkNoncopyable INHERITED;
-    };
-
-    /**
-     * Make a context using the memory provided by the arena.
-     *
-     * @return pointer to context or nullptr if can't be created
-     */
-    Context* makeContext(const ContextRec&, SkArenaAlloc*) const;
-
 #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
     /**
      *  Returns true if this shader is just a bitmap, and if not null, returns the bitmap,
      *  localMatrix, and tilemodes. If this is not a bitmap, returns false and ignores the
      *  out-parameters.
      */
-    bool isABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, TileMode xy[2]) const {
-        return this->onIsABitmap(outTexture, outMatrix, xy);
-    }
+    bool isABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, TileMode xy[2]) const;
 
     bool isABitmap() const {
         return this->isABitmap(nullptr, nullptr, nullptr);
@@ -254,9 +97,7 @@
      *  Iff this shader is backed by a single SkImage, return its ptr (the caller must ref this
      *  if they want to keep it longer than the lifetime of the shader). If not, return nullptr.
      */
-    SkImage* isAImage(SkMatrix* localMatrix, TileMode xy[2]) const {
-        return this->onIsAImage(localMatrix, xy);
-    }
+    SkImage* isAImage(SkMatrix* localMatrix, TileMode xy[2]) const;
 
     bool isAImage() const {
         return this->isAImage(nullptr, nullptr) != nullptr;
@@ -323,6 +164,7 @@
      *  These are bare pointers; the ownership and reference count are unchanged.
      */
 
+    // TODO: clean up clients, move to SkShaderBase.
     struct ComposeRec {
         const SkShader*     fShaderA;
         const SkShader*     fShaderB;
@@ -331,53 +173,6 @@
 
     virtual bool asACompose(ComposeRec*) const { return false; }
 
-#if SK_SUPPORT_GPU
-    struct AsFPArgs {
-        AsFPArgs() {}
-        AsFPArgs(GrContext* context,
-                 const SkMatrix* viewMatrix,
-                 const SkMatrix* localMatrix,
-                 SkFilterQuality filterQuality,
-                 SkColorSpace* dstColorSpace)
-            : fContext(context)
-            , fViewMatrix(viewMatrix)
-            , fLocalMatrix(localMatrix)
-            , fFilterQuality(filterQuality)
-            , fDstColorSpace(dstColorSpace) {}
-
-        GrContext*                    fContext;
-        const SkMatrix*               fViewMatrix;
-        const SkMatrix*               fLocalMatrix;
-        SkFilterQuality               fFilterQuality;
-        SkColorSpace*                 fDstColorSpace;
-    };
-
-    /**
-     *  Returns a GrFragmentProcessor that implements the shader for the GPU backend. NULL is
-     *  returned if there is no GPU implementation.
-     *
-     *  The GPU device does not call SkShader::createContext(), instead we pass the view matrix,
-     *  local matrix, and filter quality directly.
-     *
-     *  The GrContext may be used by the to create textures that are required by the returned
-     *  processor.
-     *
-     *  The returned GrFragmentProcessor should expect an unpremultiplied input color and
-     *  produce a premultiplied output.
-     */
-    virtual sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const;
-#endif
-
-    /**
-     *  If the shader can represent its "average" luminance in a single color, return true and
-     *  if color is not NULL, return that color. If it cannot, return false and ignore the color
-     *  parameter.
-     *
-     *  Note: if this returns true, the returned color will always be opaque, as only the RGB
-     *  components are used to compute luminance.
-     */
-    bool asLuminanceColor(SkColor*) const;
-
     //////////////////////////////////////////////////////////////////////////
     //  Methods to create combinations or variants of shaders
 
@@ -457,54 +252,12 @@
      *  If this shader can be represented by another shader + a localMatrix, return that shader and
      *  the localMatrix. If not, return nullptr and ignore the localMatrix parameter.
      */
+    // TODO: clean up clients, move to SkShaderBase.
     virtual sk_sp<SkShader> makeAsALocalMatrixShader(SkMatrix* localMatrix) const;
 
-    SK_TO_STRING_VIRT()
-    SK_DEFINE_FLATTENABLE_TYPE(SkShader)
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-
-    bool appendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                      const SkMatrix& ctm, const SkPaint&) const;
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-
-    bool computeTotalInverse(const ContextRec&, SkMatrix* totalInverse) const;
-
-    /**
-     * Specialize creating a SkShader context using the supplied allocator.
-     * @return pointer to context owned by the arena allocator.
-     */
-    virtual Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const {
-        return nullptr;
-    }
-
-    virtual bool onAsLuminanceColor(SkColor*) const {
-        return false;
-    }
-
-#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
-    virtual bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode[2]) const {
-        return false;
-    }
-#endif
-
-    virtual SkImage* onIsAImage(SkMatrix*, TileMode[2]) const {
-        return nullptr;
-    }
-
-    virtual bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                                const SkMatrix&, const SkPaint&,
-                                const SkMatrix* /*local matrix*/) const;
-
 private:
-    // This is essentially const, but not officially so it can be modified in
-    // constructors.
-    SkMatrix fLocalMatrix;
-
-    // So the SkLocalMatrixShader can whack fLocalMatrix in its SkReadBuffer constructor.
-    friend class SkLocalMatrixShader;
-    friend class SkBitmapProcLegacyShader;    // for computeTotalInverse()
+    SkShader() = default;
+    friend class SkShaderBase;
 
     typedef SkFlattenable INHERITED;
 };
diff --git a/include/core/SkSize.h b/include/core/SkSize.h
index 153335d..061e3c4 100644
--- a/include/core/SkSize.h
+++ b/include/core/SkSize.h
@@ -10,108 +10,83 @@
 
 #include "SkScalar.h"
 
-template <typename T> struct SkTSize {
-    T fWidth;
-    T fHeight;
+struct SkISize {
+    int32_t fWidth;
+    int32_t fHeight;
 
-    static SkTSize Make(T w, T h) {
-        SkTSize s;
-        s.fWidth = w;
-        s.fHeight = h;
-        return s;
-    }
+    static SkISize Make(int32_t w, int32_t h) { return {w, h}; }
 
-    static SkTSize MakeEmpty() {
-        return {0, 0};
-    }
+    static SkISize MakeEmpty() { return {0, 0}; }
 
-    void set(T w, T h) {
-        fWidth = w;
-        fHeight = h;
-    }
+    void set(int32_t w, int32_t h) { *this = SkISize{w, h}; }
 
     /** Returns true iff fWidth == 0 && fHeight == 0
      */
-    bool isZero() const {
-        return 0 == fWidth && 0 == fHeight;
-    }
+    bool isZero() const { return 0 == fWidth && 0 == fHeight; }
 
     /** Returns true if either widht or height are <= 0 */
-    bool isEmpty() const {
-        return fWidth <= 0 || fHeight <= 0;
-    }
+    bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; }
 
     /** Set the width and height to 0 */
-    void setEmpty() {
-        fWidth = fHeight = 0;
-    }
+    void setEmpty() { fWidth = fHeight = 0; }
 
-    T width() const { return fWidth; }
-    T height() const { return fHeight; }
+    int32_t width() const { return fWidth; }
+    int32_t height() const { return fHeight; }
 
-    /** If width or height is < 0, it is set to 0 */
-    void clampNegToZero() {
-        if (fWidth < 0) {
-            fWidth = 0;
-        }
-        if (fHeight < 0) {
-            fHeight = 0;
-        }
-    }
-
-    bool equals(T w, T h) const {
-        return fWidth == w && fHeight == h;
-    }
+    bool equals(int32_t w, int32_t h) const { return fWidth == w && fHeight == h; }
 };
 
-template <typename T>
-static inline bool operator==(const SkTSize<T>& a, const SkTSize<T>& b) {
+static inline bool operator==(const SkISize& a, const SkISize& b) {
     return a.fWidth == b.fWidth && a.fHeight == b.fHeight;
 }
 
-template <typename T>
-static inline bool operator!=(const SkTSize<T>& a, const SkTSize<T>& b) {
-    return !(a == b);
-}
+static inline bool operator!=(const SkISize& a, const SkISize& b) { return !(a == b); }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-typedef SkTSize<int32_t> SkISize;
+struct SkSize {
+    SkScalar fWidth;
+    SkScalar fHeight;
 
-struct SkSize : public SkTSize<SkScalar> {
-    static SkSize Make(SkScalar w, SkScalar h) {
-        SkSize s;
-        s.fWidth = w;
-        s.fHeight = h;
-        return s;
-    }
+    static SkSize Make(SkScalar w, SkScalar h) { return {w, h}; }
 
     static SkSize Make(const SkISize& src) {
-        return Make(SkIntToScalar(src.width()), SkIntToScalar(src.height()));
+        return {SkIntToScalar(src.width()), SkIntToScalar(src.height())};
     }
 
     SkSize& operator=(const SkISize& src) {
-        this->set(SkIntToScalar(src.fWidth), SkIntToScalar(src.fHeight));
-        return *this;
+        return *this = SkSize{SkIntToScalar(src.fWidth), SkIntToScalar(src.fHeight)};
     }
 
-    SkISize toRound() const {
-        SkISize s;
-        s.set(SkScalarRoundToInt(fWidth), SkScalarRoundToInt(fHeight));
-        return s;
-    }
+    static SkSize MakeEmpty() { return {0, 0}; }
 
-    SkISize toCeil() const {
-        SkISize s;
-        s.set(SkScalarCeilToInt(fWidth), SkScalarCeilToInt(fHeight));
-        return s;
-    }
+    void set(SkScalar w, SkScalar h) { *this = SkSize{w, h}; }
 
-    SkISize toFloor() const {
-        SkISize s;
-        s.set(SkScalarFloorToInt(fWidth), SkScalarFloorToInt(fHeight));
-        return s;
-    }
+    /** Returns true iff fWidth == 0 && fHeight == 0
+     */
+    bool isZero() const { return 0 == fWidth && 0 == fHeight; }
+
+    /** Returns true if either widht or height are <= 0 */
+    bool isEmpty() const { return fWidth <= 0 || fHeight <= 0; }
+
+    /** Set the width and height to 0 */
+    void setEmpty() { *this = SkSize{0, 0}; }
+
+    SkScalar width() const { return fWidth; }
+    SkScalar height() const { return fHeight; }
+
+    bool equals(SkScalar w, SkScalar h) const { return fWidth == w && fHeight == h; }
+
+    SkISize toRound() const { return {SkScalarRoundToInt(fWidth), SkScalarRoundToInt(fHeight)}; }
+
+    SkISize toCeil() const { return {SkScalarCeilToInt(fWidth), SkScalarCeilToInt(fHeight)}; }
+
+    SkISize toFloor() const { return {SkScalarFloorToInt(fWidth), SkScalarFloorToInt(fHeight)}; }
 };
 
+static inline bool operator==(const SkSize& a, const SkSize& b) {
+    return a.fWidth == b.fWidth && a.fHeight == b.fHeight;
+}
+
+static inline bool operator!=(const SkSize& a, const SkSize& b) { return !(a == b); }
 #endif
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
index 428f247..01fd82a 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -282,8 +282,6 @@
 
     size_t getLength() const override;
 
-    const void* getMemoryBase() override;
-
 private:
     explicit SkFILEStream(std::shared_ptr<FILE>, size_t size, size_t offset);
     explicit SkFILEStream(std::shared_ptr<FILE>, size_t size, size_t offset, size_t originalOffset);
@@ -386,11 +384,14 @@
 
     /** More efficient version of read(dst, 0, bytesWritten()). */
     void copyTo(void* dst) const;
-    void writeToStream(SkWStream* dst) const;
+    bool writeToStream(SkWStream* dst) const;
 
     /** Equivalent to copyTo() followed by reset(), but may save memory use. */
     void copyToAndReset(void* dst);
 
+    /** Equivalent to writeToStream() followed by reset(), but may save memory use. */
+    bool writeToAndReset(SkWStream* dst);
+
     /** Return the contents as SkData, and then reset the stream. */
     sk_sp<SkData> detachAsData();
 
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 0e2239a..efa2428 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -14,6 +14,7 @@
 
 class SkCanvas;
 class SkPaint;
+class GrBackendRenderTarget;
 class GrContext;
 class GrRenderTarget;
 
@@ -88,6 +89,16 @@
                                                    sk_sp<SkColorSpace>, const SkSurfaceProps*);
 
     /**
+     *  Used to wrap a pre-existing backend 3D API texture as a SkSurface. Skia will not assume
+     *  ownership of the texture and the client must ensure the texture is valid for the lifetime
+     *  of the SkSurface. If sampleCnt > 0, then we will create an intermediate mssa surface which
+     *  we will use for rendering. We then resolve into the passed in texture.
+     */
+    static sk_sp<SkSurface> MakeFromBackendTexture(GrContext*, const GrBackendTexture&,
+                                                   GrSurfaceOrigin origin, int sampleCnt,
+                                                   sk_sp<SkColorSpace>, const SkSurfaceProps*);
+
+    /**
      *  Used to wrap a pre-existing 3D API rendering target as a SkSurface. Skia will not assume
      *  ownership of the render target and the client must ensure the render target is valid for the
      *  lifetime of the SkSurface.
@@ -97,6 +108,12 @@
                                                         sk_sp<SkColorSpace>,
                                                         const SkSurfaceProps*);
 
+    static sk_sp<SkSurface> MakeFromBackendRenderTarget(GrContext*,
+                                                        const GrBackendRenderTarget&,
+                                                        GrSurfaceOrigin origin,
+                                                        sk_sp<SkColorSpace>,
+                                                        const SkSurfaceProps*);
+
     /**
      *  Used to wrap a pre-existing 3D API texture as a SkSurface. Skia will treat the texture as
      *  a rendering target only, but unlike NewFromBackendRenderTarget, Skia will manage and own
@@ -108,6 +125,13 @@
     static sk_sp<SkSurface> MakeFromBackendTextureAsRenderTarget(
         GrContext*, const GrBackendTextureDesc&, sk_sp<SkColorSpace>, const SkSurfaceProps*);
 
+    static sk_sp<SkSurface> MakeFromBackendTextureAsRenderTarget(GrContext*,
+                                                                 const GrBackendTexture&,
+                                                                 GrSurfaceOrigin origin,
+                                                                 int sampleCnt,
+                                                                 sk_sp<SkColorSpace>,
+                                                                 const SkSurfaceProps*);
+
     /**
      * Legacy versions of the above factories, without color space support. These create "legacy"
      * surfaces that operate without gamma correction or color management.
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 07d8bc3..7f2b919 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -20,12 +20,12 @@
 class SkFontData;
 class SkFontDescriptor;
 class SkScalerContext;
-struct SkScalerContextRec;
-struct SkScalerContextEffects;
 class SkStream;
 class SkStreamAsset;
-class SkAdvancedTypefaceMetrics;
 class SkWStream;
+struct SkAdvancedTypefaceMetrics;
+struct SkScalerContextEffects;
+struct SkScalerContextRec;
 
 typedef uint32_t SkFontID;
 /** Machine endian. */
@@ -321,16 +321,12 @@
     void getFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
         this->onGetFontDescriptor(desc, isLocal);
     }
+    // PRIVATE / EXPERIMENTAL -- do not call
+    void* internal_private_getCTFontRef() const {
+        return this->onGetCTFontRef();
+    }
 
 protected:
-    // The type of advance data wanted.
-    enum PerGlyphInfo {
-        kNo_PerGlyphInfo         = 0x0, // Don't populate any per glyph info.
-        kGlyphNames_PerGlyphInfo = 0x1, // Populate glyph names (Type 1 only).
-        kToUnicode_PerGlyphInfo  = 0x2  // Populate ToUnicode table, ignored
-                                        // for Type 1 fonts
-    };
-
     /** uniqueID must be unique and non-zero
     */
     SkTypeface(const SkFontStyle& style, bool isFixedPitch = false);
@@ -347,10 +343,9 @@
     virtual SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
                                                    const SkDescriptor*) const = 0;
     virtual void onFilterRec(SkScalerContextRec*) const = 0;
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                        PerGlyphInfo,
-                        const uint32_t* glyphIDs,
-                        uint32_t glyphIDsCount) const = 0;
+
+    //  Subclasses *must* override this method to work with the PDF backend.
+    virtual std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const;
 
     virtual SkStreamAsset* onOpenStream(int* ttcIndex) const = 0;
     // TODO: make pure virtual.
@@ -384,27 +379,16 @@
 
     virtual bool onComputeBounds(SkRect*) const;
 
+    virtual void* onGetCTFontRef() const { return nullptr; }
+
 private:
-    friend class SkGTypeface;
     friend class SkRandomTypeface;
     friend class SkPDFFont;
     friend class GrPathRendering;
     friend class GrGLPathRendering;
 
-    /** Retrieve detailed typeface metrics.  Used by the PDF backend.
-     @param perGlyphInfo Indicate what glyph specific information (advances,
-     names, etc.) should be populated.
-     @param glyphIDs  For per-glyph info, specify subset of the font by
-     giving glyph ids.  Each integer represents a glyph
-     id.  Passing NULL means all glyphs in the font.
-     @param glyphIDsCount Number of elements in subsetGlyphIds. Ignored if
-     glyphIDs is NULL.
-     @return The returned object has already been referenced.
-     */
-    SkAdvancedTypefaceMetrics* getAdvancedTypefaceMetrics(
-                          PerGlyphInfo,
-                          const uint32_t* glyphIDs = NULL,
-                          uint32_t glyphIDsCount = 0) const;
+    /** Retrieve detailed typeface metrics.  Used by the PDF backend.  */
+    std::unique_ptr<SkAdvancedTypefaceMetrics> getAdvancedMetrics() const;
 
 private:
     SkFontID            fUniqueID;
@@ -418,9 +402,4 @@
 
     typedef SkWeakRefCnt INHERITED;
 };
-
-namespace skstd {
-template <> struct is_bitmask_enum<SkTypeface::PerGlyphInfo> : std::true_type {};
-}
-
 #endif
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
index beb2be5..1dd672b 100644
--- a/include/core/SkTypes.h
+++ b/include/core/SkTypes.h
@@ -442,11 +442,13 @@
 */
 class SK_API SkNoncopyable {
 public:
-    SkNoncopyable() {}
+    SkNoncopyable() = default;
 
-private:
-    SkNoncopyable(const SkNoncopyable&);
-    SkNoncopyable& operator=(const SkNoncopyable&);
+    SkNoncopyable(SkNoncopyable&&) = default;
+    SkNoncopyable& operator =(SkNoncopyable&&) = default;
+
+    SkNoncopyable(const SkNoncopyable&) = delete;
+    SkNoncopyable& operator=(const SkNoncopyable&) = delete;
 };
 
 #endif /* C++ */
diff --git a/include/core/SkVertices.h b/include/core/SkVertices.h
index e4b5bcb..a245c18 100644
--- a/include/core/SkVertices.h
+++ b/include/core/SkVertices.h
@@ -8,7 +8,6 @@
 #ifndef SkVertices_DEFINED
 #define SkVertices_DEFINED
 
-#include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkData.h"
 #include "SkPoint.h"
@@ -20,18 +19,24 @@
  */
 class SkVertices : public SkNVRefCnt<SkVertices> {
 public:
+    enum VertexMode {
+        kTriangles_VertexMode,
+        kTriangleStrip_VertexMode,
+        kTriangleFan_VertexMode,
+    };
+
     /**
      *  Create a vertices by copying the specified arrays. texs and colors may be nullptr,
      *  and indices is ignored if indexCount == 0.
      */
-    static sk_sp<SkVertices> MakeCopy(SkCanvas::VertexMode mode, int vertexCount,
+    static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
                                       const SkPoint positions[],
                                       const SkPoint texs[],
                                       const SkColor colors[],
                                       int indexCount,
                                       const uint16_t indices[]);
 
-    static sk_sp<SkVertices> MakeCopy(SkCanvas::VertexMode mode, int vertexCount,
+    static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
                                       const SkPoint positions[],
                                       const SkPoint texs[],
                                       const SkColor colors[]) {
@@ -46,7 +51,7 @@
     };
     class Builder {
     public:
-        Builder(SkCanvas::VertexMode mode, int vertexCount, int indexCount, uint32_t flags);
+        Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t flags);
 
         bool isValid() const { return fVertices != nullptr; }
 
@@ -62,9 +67,9 @@
         sk_sp<SkVertices> detach();
 
     private:
-        Builder(SkCanvas::VertexMode mode, int vertexCount, int indexCount, const Sizes&);
+        Builder(VertexMode mode, int vertexCount, int indexCount, const Sizes&);
 
-        void init(SkCanvas::VertexMode mode, int vertexCount, int indexCount, const Sizes&);
+        void init(VertexMode mode, int vertexCount, int indexCount, const Sizes&);
 
         // holds a partially complete object. only completed in detach()
         sk_sp<SkVertices> fVertices;
@@ -73,7 +78,7 @@
     };
 
     uint32_t uniqueID() const { return fUniqueID; }
-    SkCanvas::VertexMode mode() const { return fMode; }
+    VertexMode mode() const { return fMode; }
     const SkRect& bounds() const { return fBounds; }
 
     bool hasColors() const { return SkToBool(this->colors()); }
@@ -127,7 +132,7 @@
     int     fVertexCnt;
     int     fIndexCnt;
 
-    SkCanvas::VertexMode fMode;
+    VertexMode fMode;
     // below here is where the actual array data is stored.
 };
 
diff --git a/include/core/SkWriteBuffer.h b/include/core/SkWriteBuffer.h
index 12d5d3b..36c74c1 100644
--- a/include/core/SkWriteBuffer.h
+++ b/include/core/SkWriteBuffer.h
@@ -24,7 +24,7 @@
 class SkFlattenable;
 class SkRefCntSet;
 
-class SkWriteBuffer {
+class SK_API SkWriteBuffer {
 public:
     SkWriteBuffer() {}
     virtual ~SkWriteBuffer() {}
@@ -73,7 +73,7 @@
 /**
  * Concrete implementation that serializes to a flat binary blob.
  */
-class SkBinaryWriteBuffer : public SkWriteBuffer {
+class SK_API SkBinaryWriteBuffer : public SkWriteBuffer {
 public:
     enum Flags {
         kCrossProcess_Flag = 1 << 0,
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index a5ecb3f..478d24b 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -15,6 +15,7 @@
 #include "SkMatrix.h"
 #include "SkPath.h"
 #include "SkPoint.h"
+#include "SkPoint3.h"
 #include "SkRRect.h"
 #include "SkRect.h"
 #include "SkRegion.h"
@@ -118,6 +119,10 @@
         *(SkPoint*)this->reserve(sizeof(pt)) = pt;
     }
 
+    void writePoint3(const SkPoint3& pt) {
+        *(SkPoint3*)this->reserve(sizeof(pt)) = pt;
+    }
+
     void writeRect(const SkRect& rect) {
         *(SkRect*)this->reserve(sizeof(rect)) = rect;
     }
diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h
index 7255ba0..e9af51c 100644
--- a/include/effects/SkColorFilterImageFilter.h
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -25,6 +25,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     bool onIsColorFilterNode(SkColorFilter**) const override;
     bool onCanHandleComplexCTM() const override { return true; }
     bool affectsTransparentBlack() const override;
diff --git a/include/effects/SkComposeImageFilter.h b/include/effects/SkComposeImageFilter.h
index 48757a7..0220011 100644
--- a/include/effects/SkComposeImageFilter.h
+++ b/include/effects/SkComposeImageFilter.h
@@ -26,6 +26,7 @@
     }
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
     bool onCanHandleComplexCTM() const override { return true; }
 
diff --git a/include/effects/SkDashPathEffect.h b/include/effects/SkDashPathEffect.h
index 13cbb4d..f742956 100644
--- a/include/effects/SkDashPathEffect.h
+++ b/include/effects/SkDashPathEffect.h
@@ -10,11 +10,7 @@
 
 #include "SkPathEffect.h"
 
-/** \class SkDashPathEffect
-
-    SkDashPathEffect is a subclass of SkPathEffect that implements dashing
-*/
-class SK_API SkDashPathEffect : public SkPathEffect {
+class SK_API SkDashPathEffect {
 public:
     /** intervals: array containing an even number of entries (>=2), with
          the even indices specifying the length of "on" intervals, and the odd
@@ -37,39 +33,6 @@
         Note: only affects stroked paths.
     */
     static sk_sp<SkPathEffect> Make(const SkScalar intervals[], int count, SkScalar phase);
-
-    virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const override;
-
-    virtual bool asPoints(PointData* results, const SkPath& src,
-                          const SkStrokeRec&, const SkMatrix&,
-                          const SkRect*) const override;
-
-    DashType asADash(DashInfo* info) const override;
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDashPathEffect)
-
-#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const override { return true; }
-#endif
-
-protected:
-    ~SkDashPathEffect() override;
-    SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase);
-    void flatten(SkWriteBuffer&) const override;
-
-private:
-    SkScalar*   fIntervals;
-    int32_t     fCount;
-    SkScalar    fPhase;
-    // computed from phase
-
-    SkScalar    fInitialDashLength;
-    int32_t     fInitialDashIndex;
-    SkScalar    fIntervalLength;
-
-    typedef SkPathEffect INHERITED;
 };
 
 #endif
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
index 72c70a1..c544279 100644
--- a/include/effects/SkDisplacementMapEffect.h
+++ b/include/effects/SkDisplacementMapEffect.h
@@ -35,6 +35,7 @@
 
     virtual SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&,
                                    MapDirection) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
 
     SK_TO_STRING_OVERRIDE()
diff --git a/include/effects/SkDropShadowImageFilter.h b/include/effects/SkDropShadowImageFilter.h
index aaa8b09..b041c0e 100644
--- a/include/effects/SkDropShadowImageFilter.h
+++ b/include/effects/SkDropShadowImageFilter.h
@@ -36,6 +36,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
 
 private:
diff --git a/include/effects/SkGaussianEdgeShader.h b/include/effects/SkGaussianEdgeShader.h
deleted file mode 100644
index ef54ece..0000000
--- a/include/effects/SkGaussianEdgeShader.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkGaussianEdgeShader_DEFINED
-#define SkGaussianEdgeShader_DEFINED
-
-#include "SkShader.h"
-
-class SK_API SkGaussianEdgeShader {
-public:
-    /** Returns a shader that applies a Gaussian blur depending on distance to the edge
-    * Currently this is only useable with Circle and RRect shapes on the GPU backend.
-    * Raster will draw nothing.
-    */
-    static sk_sp<SkShader> Make();
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-
-private:
-    SkGaussianEdgeShader(); // can't be instantiated
-};
-
-#endif
diff --git a/include/effects/SkImageSource.h b/include/effects/SkImageSource.h
index 6953497..04348c6 100644
--- a/include/effects/SkImageSource.h
+++ b/include/effects/SkImageSource.h
@@ -29,6 +29,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 private:
     explicit SkImageSource(sk_sp<SkImage>);
diff --git a/include/effects/SkLumaColorFilter.h b/include/effects/SkLumaColorFilter.h
index 2093944..af9dff1 100644
--- a/include/effects/SkLumaColorFilter.h
+++ b/include/effects/SkLumaColorFilter.h
@@ -42,7 +42,7 @@
 
 private:
     SkLumaColorFilter();
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+    void onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         bool shaderIsOpaque) const override;
 
     typedef SkColorFilter INHERITED;
diff --git a/include/effects/SkMagnifierImageFilter.h b/include/effects/SkMagnifierImageFilter.h
index 84cc9db..fa9a996 100644
--- a/include/effects/SkMagnifierImageFilter.h
+++ b/include/effects/SkMagnifierImageFilter.h
@@ -30,6 +30,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 private:
     SkRect   fSrcRect;
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h
index a18898d..5615469 100644
--- a/include/effects/SkMatrixConvolutionImageFilter.h
+++ b/include/effects/SkMatrixConvolutionImageFilter.h
@@ -81,6 +81,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
     bool affectsTransparentBlack() const override;
 
diff --git a/include/effects/SkMergeImageFilter.h b/include/effects/SkMergeImageFilter.h
index f4cd413..03f0cea 100644
--- a/include/effects/SkMergeImageFilter.h
+++ b/include/effects/SkMergeImageFilter.h
@@ -27,6 +27,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     bool onCanHandleComplexCTM() const override { return true; }
 
 private:
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
index 57413f2..9785091 100644
--- a/include/effects/SkMorphologyImageFilter.h
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -42,6 +42,7 @@
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
                                         const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     void flatten(SkWriteBuffer&) const override;
 
     SkISize radius() const { return fRadius; }
diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h
index cc3ccbf..5d1f703 100644
--- a/include/effects/SkOffsetImageFilter.h
+++ b/include/effects/SkOffsetImageFilter.h
@@ -26,6 +26,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
 
 private:
diff --git a/include/effects/SkPaintImageFilter.h b/include/effects/SkPaintImageFilter.h
index 36a3097..435b677 100644
--- a/include/effects/SkPaintImageFilter.h
+++ b/include/effects/SkPaintImageFilter.h
@@ -33,6 +33,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
 
 private:
     SkPaintImageFilter(const SkPaint& paint, const CropRect* rect);
diff --git a/include/effects/SkPerlinNoiseShader.h b/include/effects/SkPerlinNoiseShader.h
index a86c89b..5bf3847 100644
--- a/include/effects/SkPerlinNoiseShader.h
+++ b/include/effects/SkPerlinNoiseShader.h
@@ -22,25 +22,8 @@
     The algorithm used is described here :
     http://www.w3.org/TR/SVG/filters.html#feTurbulenceElement
 */
-class SK_API SkPerlinNoiseShader : public SkShader {
+class SK_API SkPerlinNoiseShader {
 public:
-    struct StitchData;
-    struct PaintingData;
-
-    /**
-     *  About the noise types : the difference between the 2 is just minor tweaks to the algorithm,
-     *  they're not 2 entirely different noises. The output looks different, but once the noise is
-     *  generated in the [1, -1] range, the output is brought back in the [0, 1] range by doing :
-     *  kFractalNoise_Type : noise * 0.5 + 0.5
-     *  kTurbulence_Type   : abs(noise)
-     *  Very little differences between the 2 types, although you can tell the difference visually.
-     */
-    enum Type {
-        kFractalNoise_Type,
-        kTurbulence_Type,
-        kFirstType = kFractalNoise_Type,
-        kLastType = kTurbulence_Type
-    };
     /**
      *  This will construct Perlin noise of the given type (Fractal Noise or Turbulence).
      *
@@ -61,54 +44,17 @@
     static sk_sp<SkShader> MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
                                           int numOctaves, SkScalar seed,
                                           const SkISize* tileSize = nullptr);
+    /**
+     * Creates an Improved Perlin Noise shader. The z value is roughly equivalent to the seed of the
+     * other two types, but minor variations to z will only slightly change the noise.
+     */
+    static sk_sp<SkShader> MakeImprovedNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
+                                             int numOctaves, SkScalar z);
 
-    class PerlinNoiseShaderContext : public SkShader::Context {
-    public:
-        PerlinNoiseShaderContext(const SkPerlinNoiseShader& shader, const ContextRec&);
-        ~PerlinNoiseShaderContext() override;
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
-    private:
-        SkPMColor shade(const SkPoint& point, StitchData& stitchData) const;
-        SkScalar calculateTurbulenceValueForPoint(
-            int channel,
-            StitchData& stitchData, const SkPoint& point) const;
-        SkScalar noise2D(int channel,
-                         const StitchData& stitchData, const SkPoint& noiseVector) const;
-
-        SkMatrix fMatrix;
-        PaintingData* fPaintingData;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPerlinNoiseShader)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override;
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 
 private:
-    SkPerlinNoiseShader(SkPerlinNoiseShader::Type type, SkScalar baseFrequencyX,
-                        SkScalar baseFrequencyY, int numOctaves, SkScalar seed,
-                        const SkISize* tileSize);
-    ~SkPerlinNoiseShader() override;
-
-    const SkPerlinNoiseShader::Type fType;
-    const SkScalar                  fBaseFrequencyX;
-    const SkScalar                  fBaseFrequencyY;
-    const int                       fNumOctaves;
-    const SkScalar                  fSeed;
-    const SkISize                   fTileSize;
-    const bool                      fStitchTiles;
-
-    typedef SkShader INHERITED;
+    SkPerlinNoiseShader() = delete;
 };
 
 #endif
diff --git a/include/effects/SkPictureImageFilter.h b/include/effects/SkPictureImageFilter.h
index 2782532..13bf66a 100644
--- a/include/effects/SkPictureImageFilter.h
+++ b/include/effects/SkPictureImageFilter.h
@@ -53,11 +53,12 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 private:
     explicit SkPictureImageFilter(sk_sp<SkPicture> picture);
     SkPictureImageFilter(sk_sp<SkPicture> picture, const SkRect& cropRect,
-                         PictureResolution, SkFilterQuality);
+                         PictureResolution, SkFilterQuality, sk_sp<SkColorSpace>);
 
     void drawPictureAtDeviceResolution(SkCanvas* canvas,
                                        const SkIRect& deviceBounds,
@@ -72,6 +73,10 @@
     PictureResolution     fPictureResolution;
     SkFilterQuality       fFilterQuality;
 
+    // Should never be set by a public constructor.  This is only used when onMakeColorSpace()
+    // forces a deferred color space xform.
+    sk_sp<SkColorSpace>   fColorSpace;
+
     typedef SkImageFilter INHERITED;
 };
 
diff --git a/include/effects/SkTileImageFilter.h b/include/effects/SkTileImageFilter.h
index d525fe9..2707741 100644
--- a/include/effects/SkTileImageFilter.h
+++ b/include/effects/SkTileImageFilter.h
@@ -33,6 +33,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 private:
     SkTileImageFilter(const SkRect& srcRect, const SkRect& dstRect, sk_sp<SkImageFilter> input)
diff --git a/include/encode/SkEncoder.h b/include/encode/SkEncoder.h
new file mode 100644
index 0000000..3acfb97
--- /dev/null
+++ b/include/encode/SkEncoder.h
@@ -0,0 +1,41 @@
+/*
+ * 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 SkEncoder_DEFINED
+#define SkEncoder_DEFINED
+
+#include "SkPixmap.h"
+#include "../private/SkTemplates.h"
+
+class SK_API SkEncoder : SkNoncopyable {
+public:
+
+    /**
+     *  Encode |numRows| rows of input.  If the caller requests more rows than are remaining
+     *  in the src, this will encode all of the remaining rows.  |numRows| must be greater
+     *  than zero.
+     */
+    bool encodeRows(int numRows);
+
+    virtual ~SkEncoder() {}
+
+protected:
+
+    virtual bool onEncodeRows(int numRows) = 0;
+
+    SkEncoder(const SkPixmap& src, size_t storageBytes)
+        : fSrc(src)
+        , fCurrRow(0)
+        , fStorage(storageBytes)
+    {}
+
+    const SkPixmap&        fSrc;
+    int                    fCurrRow;
+    SkAutoTMalloc<uint8_t> fStorage;
+};
+
+#endif
diff --git a/include/encode/SkJpegEncoder.h b/include/encode/SkJpegEncoder.h
new file mode 100644
index 0000000..fd7c204
--- /dev/null
+++ b/include/encode/SkJpegEncoder.h
@@ -0,0 +1,98 @@
+/*
+ * 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 SkJpegEncoder_DEFINED
+#define SkJpegEncoder_DEFINED
+
+#include "SkEncoder.h"
+
+class SkJpegEncoderMgr;
+class SkWStream;
+
+class SK_API SkJpegEncoder : public SkEncoder {
+public:
+
+    enum class AlphaOption {
+        kIgnore,
+        kBlendOnBlack,
+    };
+
+    enum class Downsample {
+        /**
+         *  Reduction by a factor of two in both the horizontal and vertical directions.
+         */
+        k420,
+
+        /**
+         *  Reduction by a factor of two in the horizontal direction.
+         */
+        k422,
+
+        /**
+         *  No downsampling.
+         */
+        k444,
+    };
+
+    struct Options {
+        /**
+         *  |fQuality| must be in [0, 100] where 0 corresponds to the lowest quality.
+         */
+        int fQuality = 100;
+
+        /**
+         *  Choose the downsampling factor for the U and V components.  This is only
+         *  meaningful if the |src| is not kGray, since kGray will not be encoded as YUV.
+         *
+         *  Our default value matches the libjpeg-turbo default.
+         */
+        Downsample fDownsample = Downsample::k420;
+
+        /**
+         *  Jpegs must be opaque.  This instructs the encoder on how to handle input
+         *  images with alpha.
+         *
+         *  The default is to ignore the alpha channel and treat the image as opaque.
+         *  Another option is to blend the pixels onto a black background before encoding.
+         *  In the second case, the encoder supports linear or legacy blending.
+         */
+        AlphaOption fAlphaOption = AlphaOption::kIgnore;
+        SkTransferFunctionBehavior fBlendBehavior = SkTransferFunctionBehavior::kRespect;
+    };
+
+    /**
+     *  Encode the |src| pixels to the |dst| stream.
+     *  |options| may be used to control the encoding behavior.
+     *
+     *  Returns true on success.  Returns false on an invalid or unsupported |src|.
+     */
+    static bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options);
+
+    /**
+     *  Create a jpeg encoder that will encode the |src| pixels to the |dst| stream.
+     *  |options| may be used to control the encoding behavior.
+     *
+     *  |dst| is unowned but must remain valid for the lifetime of the object.
+     *
+     *  This returns nullptr on an invalid or unsupported |src|.
+     */
+    static std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src,
+                                           const Options& options);
+
+    ~SkJpegEncoder() override;
+
+protected:
+    bool onEncodeRows(int numRows) override;
+
+private:
+    SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr>, const SkPixmap& src);
+
+    std::unique_ptr<SkJpegEncoderMgr> fEncoderMgr;
+    typedef SkEncoder INHERITED;
+};
+
+#endif
diff --git a/include/encode/SkPngEncoder.h b/include/encode/SkPngEncoder.h
new file mode 100644
index 0000000..6de800f
--- /dev/null
+++ b/include/encode/SkPngEncoder.h
@@ -0,0 +1,98 @@
+/*
+ * 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 SkPngEncoder_DEFINED
+#define SkPngEncoder_DEFINED
+
+#include "SkEncoder.h"
+
+class SkPngEncoderMgr;
+class SkWStream;
+
+class SK_API SkPngEncoder : public SkEncoder {
+public:
+
+    enum class FilterFlag : int {
+        kZero  = 0x00,
+        kNone  = 0x08,
+        kSub   = 0x10,
+        kUp    = 0x20,
+        kAvg   = 0x40,
+        kPaeth = 0x80,
+        kAll   = kNone | kSub | kUp | kAvg | kPaeth,
+    };
+
+    struct Options {
+        /**
+         *  Selects which filtering strategies to use.
+         *
+         *  If a single filter is chosen, libpng will use that filter for every row.
+         *
+         *  If multiple filters are chosen, libpng will use a heuristic to guess which filter
+         *  will encode smallest, then apply that filter.  This happens on a per row basis,
+         *  different rows can use different filters.
+         *
+         *  Using a single filter (or less filters) is typically faster.  Trying all of the
+         *  filters may help minimize the output file size.
+         *
+         *  Our default value matches libpng's default.
+         */
+        FilterFlag fFilterFlags = FilterFlag::kAll;
+
+        /**
+         *  Must be in [0, 9] where 9 corresponds to maximal compression.  This value is passed
+         *  directly to zlib.  0 is a special case to skip zlib entirely, creating dramatically
+         *  larger pngs.
+         *
+         *  Our default value matches libpng's default.
+         */
+        int fZLibLevel = 6;
+
+        /**
+         *  If the input is premultiplied, this controls the unpremultiplication behavior.
+         *  The encoder can convert to linear before unpremultiplying or ignore the transfer
+         *  function and unpremultiply the input as is.
+         */
+        SkTransferFunctionBehavior fUnpremulBehavior = SkTransferFunctionBehavior::kRespect;
+    };
+
+    /**
+     *  Encode the |src| pixels to the |dst| stream.
+     *  |options| may be used to control the encoding behavior.
+     *
+     *  Returns true on success.  Returns false on an invalid or unsupported |src|.
+     */
+    static bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options);
+
+    /**
+     *  Create a png encoder that will encode the |src| pixels to the |dst| stream.
+     *  |options| may be used to control the encoding behavior.
+     *
+     *  |dst| is unowned but must remain valid for the lifetime of the object.
+     *
+     *  This returns nullptr on an invalid or unsupported |src|.
+     */
+    static std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src,
+                                           const Options& options);
+
+    ~SkPngEncoder() override;
+
+protected:
+    bool onEncodeRows(int numRows) override;
+
+    SkPngEncoder(std::unique_ptr<SkPngEncoderMgr>, const SkPixmap& src);
+
+    std::unique_ptr<SkPngEncoderMgr> fEncoderMgr;
+    typedef SkEncoder INHERITED;
+};
+
+static inline SkPngEncoder::FilterFlag operator|(SkPngEncoder::FilterFlag x,
+                                                 SkPngEncoder::FilterFlag y) {
+    return (SkPngEncoder::FilterFlag)((int)x | (int)y);
+}
+
+#endif
diff --git a/include/encode/SkWebpEncoder.h b/include/encode/SkWebpEncoder.h
new file mode 100644
index 0000000..f671be3
--- /dev/null
+++ b/include/encode/SkWebpEncoder.h
@@ -0,0 +1,55 @@
+/*
+ * 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 SkWebpEncoder_DEFINED
+#define SkWebpEncoder_DEFINED
+
+#include "SkEncoder.h"
+
+class SkWStream;
+
+namespace SkWebpEncoder {
+
+    enum class Compression {
+        kLossy,
+        kLossless,
+    };
+
+    struct SK_API Options {
+        /**
+         *  |fCompression| determines whether we will use webp lossy or lossless compression.
+         *
+         *  |fQuality| must be in [0.0f, 100.0f].
+         *  If |fCompression| is kLossy, |fQuality| corresponds to the visual quality of the
+         *  encoding.  Decreasing the quality will result in a smaller encoded image.
+         *  If |fCompression| is kLossless, |fQuality| corresponds to the amount of effort
+         *  put into the encoding.  Lower values will compress faster into larger files,
+         *  while larger values will compress slower into smaller files.
+         *
+         *  This scheme is designed to match the libwebp API.
+         */
+        Compression fCompression = Compression::kLossy;
+        float fQuality = 100.0f;
+
+        /**
+         *  If the input is premultiplied, this controls the unpremultiplication behavior.
+         *  The encoder can convert to linear before unpremultiplying or ignore the transfer
+         *  function and unpremultiply the input as is.
+         */
+        SkTransferFunctionBehavior fUnpremulBehavior = SkTransferFunctionBehavior::kRespect;
+    };
+
+    /**
+     *  Encode the |src| pixels to the |dst| stream.
+     *  |options| may be used to control the encoding behavior.
+     *
+     *  Returns true on success.  Returns false on an invalid or unsupported |src|.
+     */
+    SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options);
+};
+
+#endif
diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h
new file mode 100644
index 0000000..232f220
--- /dev/null
+++ b/include/gpu/GrBackendSurface.h
@@ -0,0 +1,111 @@
+/*
+ * 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 GrBackendSurface_DEFINED
+#define GrBackendSurface_DEFINED
+
+#include "GrTypes.h"
+#include "gl/GrGLTypes.h"
+#include "vk/GrVkTypes.h"
+
+class GrBackendTexture {
+public:
+    GrBackendTexture(int width,
+                     int height,
+                     const GrVkImageInfo& vkInfo);
+
+    GrBackendTexture(int width,
+                     int height,
+                     GrPixelConfig config,
+                     const GrGLTextureInfo& glInfo);
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    GrPixelConfig config() const { return fConfig; }
+    GrBackend backend() const {return fBackend; }
+
+    // If the backend API is Vulkan, this returns a pointer to the GrVkImageInfo struct. Otherwise
+    // it returns nullptr.
+    const GrVkImageInfo* getVkImageInfo() const;
+
+    // If the backend API is GL, this returns a pointer to the GrGLTextureInfo struct. Otherwise
+    // it returns nullptr.
+    const GrGLTextureInfo* getGLTextureInfo() const;
+
+private:
+    // Temporary constructor which can be used to convert from a GrBackendTextureDesc.
+    GrBackendTexture(const GrBackendTextureDesc& desc, GrBackend backend);
+
+    // Friending for access to above constructor taking a GrBackendTextureDesc
+    friend class SkImage;
+    friend class SkSurface;
+
+    int fWidth;         //<! width in pixels
+    int fHeight;        //<! height in pixels
+    GrPixelConfig fConfig;
+    GrBackend fBackend;
+
+    union {
+        GrVkImageInfo   fVkInfo;
+        GrGLTextureInfo fGLInfo;
+    };
+};
+
+class GrBackendRenderTarget {
+public:
+    GrBackendRenderTarget(int width,
+                          int height,
+                          int sampleCnt,
+                          int stencilBits,
+                          const GrVkImageInfo& vkInfo);
+
+    GrBackendRenderTarget(int width,
+                          int height,
+                          int sampleCnt,
+                          int stencilBits,
+                          GrPixelConfig config,
+                          const GrGLFramebufferInfo& glInfo);
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    int sampleCnt() const { return fSampleCnt; }
+    int stencilBits() const { return fStencilBits; }
+    GrPixelConfig config() const { return fConfig; }
+    GrBackend backend() const {return fBackend; }
+
+    // If the backend API is Vulkan, this returns a pointer to the GrVkImageInfo struct. Otherwise
+    // it returns nullptr
+    const GrVkImageInfo* getVkImageInfo() const;
+
+    // If the backend API is GL, this returns a pointer to the GrGLFramebufferInfo struct. Otherwise
+    // it returns nullptr.
+    const GrGLFramebufferInfo* getGLFramebufferInfo() const;
+
+private:
+    // Temporary constructor which can be used to convert from a GrBackendRenderTargetDesc.
+    GrBackendRenderTarget(const GrBackendRenderTargetDesc& desc, GrBackend backend);
+
+    // Friending for access to above constructor taking a GrBackendTextureDesc
+    friend class SkSurface;
+
+    int fWidth;         //<! width in pixels
+    int fHeight;        //<! height in pixels
+
+    int fSampleCnt;
+    int fStencilBits;
+    GrPixelConfig fConfig;
+
+    GrBackend fBackend;
+
+    union {
+        GrVkImageInfo   fVkInfo;
+        GrGLFramebufferInfo fGLInfo;
+    };
+};
+
+#endif
+
diff --git a/include/gpu/GrBlend.h b/include/gpu/GrBlend.h
index 5100bb0..30b0b9b 100644
--- a/include/gpu/GrBlend.h
+++ b/include/gpu/GrBlend.h
@@ -73,121 +73,38 @@
 
 static const int kGrBlendCoeffCnt = kLast_GrBlendCoeff + 1;
 
-/**
- * Given a known blend equation in the form of srcCoeff * srcColor + dstCoeff * dstColor where
- * there may be partial knowledge of the srcColor and dstColor component values, determine what
- * components of the blended output color are known. Coeffs must not refer to the constant or
- * secondary src color.
- */
-void GrGetCoeffBlendKnownComponents(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff,
-                                    GrColor srcColor,
-                                    GrColorComponentFlags srcColorFlags,
-                                    GrColor dstColor,
-                                    GrColorComponentFlags dstColorFlags,
-                                    GrColor* outColor,
-                                    GrColorComponentFlags* outFlags);
-
-template<GrBlendCoeff Coeff>
-struct GrTBlendCoeffRefsSrc : skstd::bool_constant<kSC_GrBlendCoeff == Coeff ||
-                                                   kISC_GrBlendCoeff == Coeff ||
-                                                   kSA_GrBlendCoeff == Coeff ||
-                                                   kISA_GrBlendCoeff == Coeff> {};
-
-#define GR_BLEND_COEFF_REFS_SRC(COEFF) \
-    GrTBlendCoeffRefsSrc<COEFF>::value
-
-inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) {
-    switch (coeff) {
-        case kSC_GrBlendCoeff:
-        case kISC_GrBlendCoeff:
-        case kSA_GrBlendCoeff:
-        case kISA_GrBlendCoeff:
-            return true;
-        default:
-            return false;
-    }
+static constexpr bool GrBlendCoeffRefsSrc(const GrBlendCoeff coeff) {
+    return kSC_GrBlendCoeff == coeff || kISC_GrBlendCoeff == coeff || kSA_GrBlendCoeff == coeff ||
+           kISA_GrBlendCoeff == coeff;
 }
 
-template<GrBlendCoeff Coeff>
-struct GrTBlendCoeffRefsDst : skstd::bool_constant<kDC_GrBlendCoeff == Coeff ||
-                                                   kIDC_GrBlendCoeff == Coeff ||
-                                                   kDA_GrBlendCoeff == Coeff ||
-                                                   kIDA_GrBlendCoeff == Coeff> {};
-
-#define GR_BLEND_COEFF_REFS_DST(COEFF) \
-    GrTBlendCoeffRefsDst<COEFF>::value
-
-inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) {
-    switch (coeff) {
-        case kDC_GrBlendCoeff:
-        case kIDC_GrBlendCoeff:
-        case kDA_GrBlendCoeff:
-        case kIDA_GrBlendCoeff:
-            return true;
-        default:
-            return false;
-    }
+static constexpr bool GrBlendCoeffRefsDst(const GrBlendCoeff coeff) {
+    return kDC_GrBlendCoeff == coeff || kIDC_GrBlendCoeff == coeff || kDA_GrBlendCoeff == coeff ||
+           kIDA_GrBlendCoeff == coeff;
 }
 
-
-template<GrBlendCoeff Coeff>
-struct GrTBlendCoeffRefsSrc2 : skstd::bool_constant<kS2C_GrBlendCoeff == Coeff ||
-                                                    kIS2C_GrBlendCoeff == Coeff ||
-                                                    kS2A_GrBlendCoeff == Coeff ||
-                                                    kIS2A_GrBlendCoeff == Coeff> {};
-
-#define GR_BLEND_COEFF_REFS_SRC2(COEFF) \
-    GrTBlendCoeffRefsSrc2<COEFF>::value
-
-inline bool GrBlendCoeffRefsSrc2(GrBlendCoeff coeff) {
-    switch (coeff) {
-        case kS2C_GrBlendCoeff:
-        case kIS2C_GrBlendCoeff:
-        case kS2A_GrBlendCoeff:
-        case kIS2A_GrBlendCoeff:
-            return true;
-        default:
-            return false;
-    }
+static constexpr bool GrBlendCoeffRefsSrc2(const GrBlendCoeff coeff) {
+    return kS2C_GrBlendCoeff == coeff || kIS2C_GrBlendCoeff == coeff ||
+           kS2A_GrBlendCoeff == coeff || kIS2A_GrBlendCoeff == coeff;
 }
 
+static constexpr bool GrBlendCoeffsUseSrcColor(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+    return kZero_GrBlendCoeff != srcCoeff || GrBlendCoeffRefsSrc(dstCoeff);
+}
 
-template<GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
-struct GrTBlendCoeffsUseSrcColor : skstd::bool_constant<kZero_GrBlendCoeff != SrcCoeff ||
-                                                        GR_BLEND_COEFF_REFS_SRC(DstCoeff)> {};
+static constexpr bool GrBlendCoeffsUseDstColor(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+    return GrBlendCoeffRefsDst(srcCoeff) || kZero_GrBlendCoeff != dstCoeff;
+}
 
-#define GR_BLEND_COEFFS_USE_SRC_COLOR(SRC_COEFF, DST_COEFF) \
-    GrTBlendCoeffsUseSrcColor<SRC_COEFF, DST_COEFF>::value
-
-
-template<GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
-struct GrTBlendCoeffsUseDstColor : skstd::bool_constant<GR_BLEND_COEFF_REFS_DST(SrcCoeff) ||
-                                                        kZero_GrBlendCoeff != DstCoeff> {};
-
-#define GR_BLEND_COEFFS_USE_DST_COLOR(SRC_COEFF, DST_COEFF) \
-    GrTBlendCoeffsUseDstColor<SRC_COEFF, DST_COEFF>::value
-
-
-template<GrBlendEquation Equation>
-struct GrTBlendEquationIsAdvanced : skstd::bool_constant<Equation >= kFirstAdvancedGrBlendEquation> {};
-
-#define GR_BLEND_EQUATION_IS_ADVANCED(EQUATION) \
-    GrTBlendEquationIsAdvanced<EQUATION>::value
-
-inline bool GrBlendEquationIsAdvanced(GrBlendEquation equation) {
+static constexpr bool GrBlendEquationIsAdvanced(GrBlendEquation equation) {
     return equation >= kFirstAdvancedGrBlendEquation;
 }
 
-
-template<GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
-struct GrTBlendModifiesDst : skstd::bool_constant<
-    (kAdd_GrBlendEquation != BlendEquation && kReverseSubtract_GrBlendEquation != BlendEquation) ||
-     kZero_GrBlendCoeff != SrcCoeff ||
-     kOne_GrBlendCoeff != DstCoeff> {};
-
-#define GR_BLEND_MODIFIES_DST(EQUATION, SRC_COEFF, DST_COEFF) \
-    GrTBlendModifiesDst<EQUATION, SRC_COEFF, DST_COEFF>::value
-
+static constexpr bool GrBlendModifiesDst(GrBlendEquation equation, GrBlendCoeff srcCoeff,
+                                         GrBlendCoeff dstCoeff) {
+    return (kAdd_GrBlendEquation != equation && kReverseSubtract_GrBlendEquation != equation) ||
+           kZero_GrBlendCoeff != srcCoeff || kOne_GrBlendCoeff != dstCoeff;
+}
 
 /**
  * Advanced blend equations can always tweak alpha for coverage. (See GrCustomXfermode.cpp)
@@ -212,17 +129,19 @@
  *
  * By inspection we can see this will work as long as dstCoeff has a 1, and any other term in
  * dstCoeff references S.
+ *
+ * Moreover, if the blend doesn't modify the dst at all then it is ok to arbitrarily modify the src
+ * color so folding in coverage is allowed.
  */
-template<GrBlendEquation Equation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
-struct GrTBlendCanTweakAlphaForCoverage : skstd::bool_constant<
-    GR_BLEND_EQUATION_IS_ADVANCED(Equation) ||
-    ((kAdd_GrBlendEquation == Equation || kReverseSubtract_GrBlendEquation == Equation) &&
-      !GR_BLEND_COEFF_REFS_SRC(SrcCoeff) &&
-      (kOne_GrBlendCoeff == DstCoeff ||
-       kISC_GrBlendCoeff == DstCoeff ||
-       kISA_GrBlendCoeff == DstCoeff))> {};
-
-#define GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(EQUATION, SRC_COEFF, DST_COEFF) \
-    GrTBlendCanTweakAlphaForCoverage<EQUATION, SRC_COEFF, DST_COEFF>::value
+static constexpr bool GrBlendAllowsCoverageAsAlpha(GrBlendEquation equation,
+                                                   GrBlendCoeff srcCoeff,
+                                                   GrBlendCoeff dstCoeff) {
+    return GrBlendEquationIsAdvanced(equation) ||
+           !GrBlendModifiesDst(equation, srcCoeff, dstCoeff) ||
+           ((kAdd_GrBlendEquation == equation || kReverseSubtract_GrBlendEquation == equation) &&
+            !GrBlendCoeffRefsSrc(srcCoeff) &&
+            (kOne_GrBlendCoeff == dstCoeff || kISC_GrBlendCoeff == dstCoeff ||
+             kISA_GrBlendCoeff == dstCoeff));
+}
 
 #endif
diff --git a/include/gpu/GrCaps.h b/include/gpu/GrCaps.h
index 239d4a4..30825d5 100644
--- a/include/gpu/GrCaps.h
+++ b/include/gpu/GrCaps.h
@@ -11,13 +11,12 @@
 #include "GrTypes.h"
 #include "GrTypesPriv.h"
 #include "GrBlend.h"
-#include "GrShaderVar.h"
 #include "GrShaderCaps.h"
 #include "SkRefCnt.h"
 #include "SkString.h"
 
 struct GrContextOptions;
-class GrRenderTarget;
+class GrRenderTargetProxy;
 
 /**
  * Represents the capabilities of a GrContext.
@@ -27,7 +26,6 @@
     GrCaps(const GrContextOptions&);
 
     virtual SkString dump() const;
-
     const GrShaderCaps* shaderCaps() const { return fShaderCaps.get(); }
 
     bool npotTextureTileSupport() const { return fNPOTTextureTileSupport; }
@@ -45,15 +43,13 @@
      * Is there support for enabling/disabling sRGB writes for sRGB-capable color buffers?
      */
     bool srgbWriteControl() const { return fSRGBWriteControl; }
-    bool twoSidedStencilSupport() const { return fTwoSidedStencilSupport; }
-    bool stencilWrapOpsSupport() const { return  fStencilWrapOpsSupport; }
     bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; }
     bool gpuTracingSupport() const { return fGpuTracingSupport; }
-    bool compressedTexSubImageSupport() const { return fCompressedTexSubImageSupport; }
     bool oversizedStencilSupport() const { return fOversizedStencilSupport; }
     bool textureBarrierSupport() const { return fTextureBarrierSupport; }
     bool sampleLocationsSupport() const { return fSampleLocationsSupport; }
     bool multisampleDisableSupport() const { return fMultisampleDisableSupport; }
+    bool instanceAttribSupport() const { return fInstanceAttribSupport; }
     bool usesMixedSamples() const { return fUsesMixedSamples; }
     bool preferClientSideDynamicBuffers() const { return fPreferClientSideDynamicBuffers; }
 
@@ -83,6 +79,8 @@
 
     bool avoidInstancedDrawsToFPTargets() const { return fAvoidInstancedDrawsToFPTargets; }
 
+    bool avoidStencilBuffers() const { return fAvoidStencilBuffers; }
+
     /**
      * Indicates the capabilities of the fixed function blend unit.
      */
@@ -161,14 +159,12 @@
 
     int maxWindowRectangles() const { return fMaxWindowRectangles; }
 
-    virtual bool isConfigTexturable(GrPixelConfig config) const = 0;
+    virtual bool isConfigTexturable(GrPixelConfig) const = 0;
     virtual bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const = 0;
     virtual bool canConfigBeImageStorage(GrPixelConfig config) const = 0;
 
     bool suppressPrints() const { return fSuppressPrints; }
 
-    bool immediateFlush() const { return fImmediateFlush; }
-
     size_t bufferMapThreshold() const {
         SkASSERT(fBufferMapThreshold >= 0);
         return fBufferMapThreshold;
@@ -180,6 +176,8 @@
         is not initialized (even if not read by draw calls). */
     bool mustClearUploadedBufferData() const { return fMustClearUploadedBufferData; }
 
+    bool wireframeMode() const { return fWireframeMode; }
+
     bool sampleShadingSupport() const { return fSampleShadingSupport; }
 
     bool fenceSyncSupport() const { return fFenceSyncSupport; }
@@ -187,11 +185,14 @@
 
     /**
      * This is can be called before allocating a texture to be a dst for copySurface. This is only
-     * used for doing dst copies needed in blends, thus the src is always a GrRenderTarget. It will
-     * populate the origin, config, and flags fields of the desc such that copySurface can
-     * efficiently succeed.
+     * used for doing dst copies needed in blends, thus the src is always a GrRenderTargetProxy. It
+     * will populate the origin, config, and flags fields of the desc such that copySurface can
+     * efficiently succeed. rectsMustMatch will be set to true if the copy operation must ensure
+     * that the src and dest rects are identical. disallowSubrect will be set to true if copy rect
+     * must equal src's bounds.
      */
-    virtual bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const = 0;
+    virtual bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                                    bool* rectsMustMatch, bool* disallowSubrect) const = 0;
 
 protected:
     /** Subclasses must call this at the end of their constructors in order to apply caps
@@ -205,17 +206,15 @@
     bool fMipMapSupport                              : 1;
     bool fSRGBSupport                                : 1;
     bool fSRGBWriteControl                           : 1;
-    bool fTwoSidedStencilSupport                     : 1;
-    bool fStencilWrapOpsSupport                      : 1;
     bool fDiscardRenderTargetSupport                 : 1;
     bool fReuseScratchTextures                       : 1;
     bool fReuseScratchBuffers                        : 1;
     bool fGpuTracingSupport                          : 1;
-    bool fCompressedTexSubImageSupport               : 1;
     bool fOversizedStencilSupport                    : 1;
     bool fTextureBarrierSupport                      : 1;
     bool fSampleLocationsSupport                     : 1;
     bool fMultisampleDisableSupport                  : 1;
+    bool fInstanceAttribSupport                      : 1;
     bool fUsesMixedSamples                           : 1;
     bool fPreferClientSideDynamicBuffers             : 1;
     bool fFullClearIsFree                            : 1;
@@ -226,6 +225,7 @@
     bool fUseDrawInsteadOfPartialRenderTargetWrite   : 1;
     bool fUseDrawInsteadOfAllRenderTargetWrites      : 1;
     bool fAvoidInstancedDrawsToFPTargets             : 1;
+    bool fAvoidStencilBuffers                        : 1;
 
     // ANGLE workaround
     bool fPreferVRAMUseOverFlushes                   : 1;
@@ -259,7 +259,7 @@
     virtual void onApplyOptionsOverrides(const GrContextOptions&) {}
 
     bool fSuppressPrints : 1;
-    bool fImmediateFlush: 1;
+    bool fWireframeMode  : 1;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index ea4f422..d107fc6 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -10,7 +10,6 @@
 
 #include "GrCaps.h"
 #include "GrColor.h"
-#include "GrRenderTarget.h"
 #include "SkMatrix.h"
 #include "SkPathEffect.h"
 #include "SkTypes.h"
@@ -34,6 +33,7 @@
 class GrResourceCache;
 class GrResourceProvider;
 class GrSamplerParams;
+class GrSurfaceProxy;
 class GrTextBlobCache;
 class GrTextContext;
 class GrTextureProxy;
@@ -137,6 +137,11 @@
     void getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const;
 
     /**
+     *  Gets the number of bytes in the cache consumed by purgeable (e.g. unlocked) resources.
+     */
+    size_t getResourceCachePurgeableBytes() const;
+
+    /**
      *  Specify the GPU resource cache limits. If the current cache exceeds either
      *  of these, it will be purged (LRU) to keep the cache within these limits.
      *
@@ -166,6 +171,18 @@
      */
     void purgeResourcesNotUsedInMs(std::chrono::milliseconds ms);
 
+    /**
+     * Purge unlocked resources from the cache until the the provided byte count has been reached
+     * or we have purged all unlocked resources. The default policy is to purge in LRU order, but
+     * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other
+     * resource types.
+     *
+     * @param maxBytesToPurge the desired number of bytes to be purged.
+     * @param preferScratchResources If true scratch resources will be purged prior to other
+     *                               resource types.
+     */
+    void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources);
+
     /** Access the context capabilities */
     const GrCaps* caps() const { return fCaps; }
 
@@ -182,25 +199,13 @@
      */
     int getRecommendedSampleCount(GrPixelConfig config, SkScalar dpi) const;
 
-    /**
-     * Create both a GrRenderTarget and a matching GrRenderTargetContext to wrap it.
-     * We guarantee that "asTexture" will succeed for renderTargetContexts created
-     * via this entry point.
+    /*
+     * Create a new render target context backed by a deferred-style
+     * GrRenderTargetProxy. We guarantee that "asTextureProxy" will succeed for
+     * renderTargetContexts created via this entry point.
      */
-    sk_sp<GrRenderTargetContext> makeRenderTargetContext(
-                                                 SkBackingFit fit,
-                                                 int width, int height,
-                                                 GrPixelConfig config,
-                                                 sk_sp<SkColorSpace> colorSpace,
-                                                 int sampleCnt = 0,
-                                                 GrSurfaceOrigin origin = kBottomLeft_GrSurfaceOrigin,
-                                                 const SkSurfaceProps* surfaceProps = nullptr,
-                                                 SkBudgeted = SkBudgeted::kYes);
-
-    // Create a new render target context as above but have it backed by a deferred-style
-    // GrRenderTargetProxy rather than one that is backed by an actual GrRenderTarget
     sk_sp<GrRenderTargetContext> makeDeferredRenderTargetContext(
-                                                 SkBackingFit fit, 
+                                                 SkBackingFit fit,
                                                  int width, int height,
                                                  GrPixelConfig config,
                                                  sk_sp<SkColorSpace> colorSpace,
@@ -214,18 +219,6 @@
      * converted to 8888). It may also swizzle the channels (e.g., BGRA -> RGBA).
      * SRGB-ness will be preserved.
      */
-    sk_sp<GrRenderTargetContext> makeRenderTargetContextWithFallback(
-                                                 SkBackingFit fit,
-                                                 int width, int height,
-                                                 GrPixelConfig config,
-                                                 sk_sp<SkColorSpace> colorSpace,
-                                                 int sampleCnt = 0,
-                                                 GrSurfaceOrigin origin = kBottomLeft_GrSurfaceOrigin,
-                                                 const SkSurfaceProps* surfaceProps = nullptr,
-                                                 SkBudgeted budgeted = SkBudgeted::kYes);
-
-    // Create a new render target context as above but have it backed by a deferred-style
-    // GrRenderTargetProxy rather than one that is backed by an actual GrRenderTarget
     sk_sp<GrRenderTargetContext> makeDeferredRenderTargetContextWithFallback(
                                                  SkBackingFit fit,
                                                  int width, int height,
@@ -245,89 +238,6 @@
      */
     void flush();
 
-   /**
-    * These flags can be used with the read/write pixels functions below.
-    */
-    enum PixelOpsFlags {
-        /** The GrContext will not be flushed before the surface read or write. This means that
-            the read or write may occur before previous draws have executed. */
-        kDontFlush_PixelOpsFlag = 0x1,
-        /** Any surface writes should be flushed to the backend 3D API after the surface operation
-            is complete */
-        kFlushWrites_PixelOp = 0x2,
-        /** The src for write or dst read is unpremultiplied. This is only respected if both the
-            config src and dst configs are an RGBA/BGRA 8888 format. */
-        kUnpremul_PixelOpsFlag  = 0x4,
-    };
-
-    /**
-     * Reads a rectangle of pixels from a surface.
-     * @param surface       the surface to read from.
-     * @param srcColorSpace color space of the surface
-     * @param left          left edge of the rectangle to read (inclusive)
-     * @param top           top edge of the rectangle to read (inclusive)
-     * @param width         width of rectangle to read in pixels.
-     * @param height        height of rectangle to read in pixels.
-     * @param config        the pixel config of the destination buffer
-     * @param dstColorSpace color space of the destination buffer
-     * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero means rows are tightly
-     *                      packed.
-     * @param pixelOpsFlags see PixelOpsFlags enum above.
-     *
-     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
-     *         pixel configs
-     */
-    bool readSurfacePixels(GrSurface* surface, SkColorSpace* srcColorSpace,
-                           int left, int top, int width, int height,
-                           GrPixelConfig config, SkColorSpace* dstColorSpace, void* buffer,
-                           size_t rowBytes = 0,
-                           uint32_t pixelOpsFlags = 0);
-
-    /**
-     * Writes a rectangle of pixels to a surface.
-     * @param surface       the surface to write to.
-     * @param dstColorSpace color space of the surface
-     * @param left          left edge of the rectangle to write (inclusive)
-     * @param top           top edge of the rectangle to write (inclusive)
-     * @param width         width of rectangle to write in pixels.
-     * @param height        height of rectangle to write in pixels.
-     * @param config        the pixel config of the source buffer
-     * @param srcColorSpace color space of the source buffer
-     * @param buffer        memory to read pixels from
-     * @param rowBytes      number of bytes between consecutive rows. Zero
-     *                      means rows are tightly packed.
-     * @param pixelOpsFlags see PixelOpsFlags enum above.
-     * @return true if the write succeeded, false if not. The write can fail because of an
-     *         unsupported combination of surface and src configs.
-     */
-    bool writeSurfacePixels(GrSurface* surface, SkColorSpace* dstColorSpace,
-                            int left, int top, int width, int height,
-                            GrPixelConfig config, SkColorSpace* srcColorSpace, const void* buffer,
-                            size_t rowBytes,
-                            uint32_t pixelOpsFlags = 0);
-
-    /**
-     * After this returns any pending writes to the surface will have been issued to the backend 3D API.
-     */
-    void flushSurfaceWrites(GrSurface* surface);
-
-    /**
-     * After this returns any pending reads or writes to the surface will have been issued to the
-     * backend 3D API.
-     */
-    void flushSurfaceIO(GrSurface* surface);
-
-    /**
-     * Finalizes all pending reads and writes to the surface and also performs an MSAA resolve
-     * if necessary.
-     *
-     * It is not necessary to call this before reading the render target via Skia/GrContext.
-     * GrContext will detect when it must perform a resolve before reading pixels back from the
-     * surface or using it as a texture.
-     */
-    void prepareSurfaceForExternalIO(GrSurface*);
-
     /**
      * An ID associated with this context, guaranteed to be unique.
      */
@@ -395,8 +305,8 @@
 
     bool                                    fDisableGpuYUVConversion;
     bool                                    fDidTestPMConversions;
-    int                                     fPMToUPMConversion;
-    int                                     fUPMToPMConversion;
+    // true if the PM/UPM conversion succeeded; false otherwise
+    bool                                    fPMUPMConversionsRoundTrip;
 
     // In debug builds we guard against improper thread handling
     // This guard is passed to the GrDrawingManager and, from there to all the
@@ -416,6 +326,8 @@
 
     GrAuditTrail                            fAuditTrail;
 
+    GrBackend                               fBackend;
+
     // TODO: have the GrClipStackClip use renderTargetContexts and rm this friending
     friend class GrContextPriv;
 
@@ -426,19 +338,20 @@
     void initCommon(const GrContextOptions&);
 
     /**
-     * These functions create premul <-> unpremul effects if it is possible to generate a pair
-     * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they
-     * return NULL. They also can perform a swizzle as part of the draw.
+     * These functions create premul <-> unpremul effects. If the second argument is 'true', they
+     * use the specialized round-trip effects from GrConfigConversionEffect, otherwise they
+     * create effects that do naive multiply or divide.
      */
-    sk_sp<GrFragmentProcessor> createPMToUPMEffect(GrTexture*, const SkMatrix&);
-    sk_sp<GrFragmentProcessor> createPMToUPMEffect(sk_sp<GrTextureProxy>, const SkMatrix&);
-    sk_sp<GrFragmentProcessor> createUPMToPMEffect(sk_sp<GrTextureProxy>, const SkMatrix&);
-    /** Called before either of the above two functions to determine the appropriate fragment
-        processors for conversions. */
-    void testPMConversionsIfNecessary(uint32_t flags);
-    /** Returns true if we've determined that createPMtoUPMEffect and createUPMToPMEffect will
-        succeed for the passed in config. Otherwise we fall back to SW conversion. */
-    bool validPMUPMConversionExists(GrPixelConfig) const;
+    sk_sp<GrFragmentProcessor> createPMToUPMEffect(sk_sp<GrFragmentProcessor>,
+                                                   bool useConfigConversionEffect);
+    sk_sp<GrFragmentProcessor> createUPMToPMEffect(sk_sp<GrFragmentProcessor>,
+                                                   bool useConfigConversionEffect);
+
+    /**
+     * Returns true if createPMtoUPMEffect and createUPMToPMEffect will succeed for non-sRGB 8888
+     * configs. In other words, did we find a pair of round-trip preserving conversion effects?
+     */
+    bool validPMUPMConversionExists();
 
     /**
      * A callback similar to the above for use by the TextBlobCache
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 8b61c9c..5337138 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -36,15 +36,6 @@
     /** some gpus have problems with partial writes of the rendertarget */
     bool fUseDrawInsteadOfPartialRenderTargetWrite = false;
 
-    /** The GrContext operates in immediate mode. It will issue all draws to the backend API
-        immediately. Intended to ease debugging. */
-    bool fImmediateMode = false;
-
-    /** For debugging, override the default maximum look-back or look-ahead window for GrOp
-        combining. */
-    int fMaxOpCombineLookback = -1;
-    int fMaxOpCombineLookahead = -1;
-
     /** Force us to do all swizzling manually in the shader and don't rely on extensions to do
         swizzling. */
     bool fUseShaderSwizzling = false;
@@ -84,6 +75,11 @@
     bool fSuppressPathRendering = false;
 
     /**
+     * Render everything in wireframe
+     */
+    bool fWireframeMode = false;
+
+    /**
      * Allows the client to include or exclude specific GPU path renderers.
      */
     enum class GpuPathRenderers {
@@ -105,6 +101,17 @@
     };
 
     GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kAll;
+
+    /**
+     * The maximum size of cache textures used for Skia's Glyph cache.
+     */
+    float fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4;
+
+    /**
+     * Bugs on certain drivers cause stencil buffers to leak. This flag causes Skia to avoid
+     * allocating stencil buffers and use alternate rasterization paths, avoiding the leak.
+     */
+    bool fAvoidStencilBuffers = false;
 };
 
 GR_MAKE_BITFIELD_CLASS_OPS(GrContextOptions::GpuPathRenderers)
diff --git a/include/gpu/GrCoordTransform.h b/include/gpu/GrCoordTransform.h
deleted file mode 100644
index 6df6586..0000000
--- a/include/gpu/GrCoordTransform.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCoordTransform_DEFINED
-#define GrCoordTransform_DEFINED
-
-#include "GrProcessor.h"
-#include "SkMatrix.h"
-#include "GrTexture.h"
-#include "GrTypes.h"
-#include "GrShaderVar.h"
-
-class GrTextureProxy;
-
-/**
- * A class representing a linear transformation of local coordinates. GrFragnentProcessors
- * these transformations, and the GrGeometryProcessor implements the transformation.
- */
-class GrCoordTransform : SkNoncopyable {
-public:
-    GrCoordTransform()
-        : fTexture(nullptr)
-        , fNormalize(false)
-        , fReverseY(false)
-        , fPrecision(kDefault_GrSLPrecision) {
-        SkDEBUGCODE(fInProcessor = false);
-    }
-
-    /**
-     * Create a transformation that maps [0, 1] to a texture's boundaries. The precision is inferred
-     * from the texture size and filter. The texture origin also implies whether a y-reversal should
-     * be performed.
-     */
-    GrCoordTransform(const GrTexture* texture, GrSamplerParams::FilterMode filter) {
-        SkASSERT(texture);
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(SkMatrix::I(), texture, filter);
-    }
-
-    GrCoordTransform(GrResourceProvider* resourceProvider, GrTextureProxy* proxy,
-                     GrSamplerParams::FilterMode filter) {
-        SkASSERT(proxy);
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(resourceProvider, SkMatrix::I(), proxy, filter);
-    }
-
-    /**
-     * Create a transformation from a matrix. The precision is inferred from the texture size and
-     * filter. The texture origin also implies whether a y-reversal should be performed.
-     */
-    GrCoordTransform(const SkMatrix& m, const GrTexture* texture,
-                     GrSamplerParams::FilterMode filter) {
-        SkASSERT(texture);
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(m, texture, filter);
-    }
-
-    GrCoordTransform(GrResourceProvider* resourceProvider, const SkMatrix& m,
-                     GrTextureProxy* proxy, GrSamplerParams::FilterMode filter) {
-        SkASSERT(proxy);
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(resourceProvider, m, proxy, filter);
-    }
-
-    /**
-     * Create a transformation that applies the matrix to a coord set.
-     */
-    GrCoordTransform(const SkMatrix& m, GrSLPrecision precision = kDefault_GrSLPrecision) {
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(m, precision);
-    }
-
-    // MDB TODO: rm the GrTexture* flavor of reset
-    void reset(const SkMatrix&, const GrTexture*, GrSamplerParams::FilterMode filter,
-               bool normalize = true);
-
-    void reset(GrResourceProvider*, const SkMatrix&, GrTextureProxy*,
-               GrSamplerParams::FilterMode filter, bool normalize = true);
-
-    void reset(const SkMatrix& m, GrSLPrecision precision = kDefault_GrSLPrecision) {
-        SkASSERT(!fInProcessor);
-        fMatrix = m;
-        fTexture = nullptr;
-        fNormalize = false;
-        fReverseY = false;
-        fPrecision = precision;
-    }
-
-    GrCoordTransform& operator= (const GrCoordTransform& that) {
-        SkASSERT(!fInProcessor);
-        fMatrix = that.fMatrix;
-        fTexture = that.fTexture;
-        fNormalize = that.fNormalize;
-        fReverseY = that.fReverseY;
-        fPrecision = that.fPrecision;
-        return *this;
-    }
-
-    /**
-     * 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;
-    }
-
-    bool hasSameEffectAs(const GrCoordTransform& that) const {
-        if (fNormalize != that.fNormalize ||
-            fReverseY != that.fReverseY ||
-            fPrecision != that.fPrecision ||
-            !fMatrix.cheapEqualTo(that.fMatrix)) {
-            return false;
-        }
-
-        if (fNormalize) {
-            SkASSERT(fTexture && that.fTexture);
-            return fTexture->width() == that.fTexture->width() &&
-                   fTexture->height() == that.fTexture->height();
-        }
-
-        return true;
-    }
-
-    const SkMatrix& getMatrix() const { return fMatrix; }
-    const GrTexture* texture() const { return fTexture; }
-    bool normalize() const { return fNormalize; }
-    bool reverseY() const { return fReverseY; }
-    GrSLPrecision precision() const { return fPrecision; }
-
-private:
-    // 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 GrTexture*        fTexture;
-    bool                    fNormalize;
-    bool                    fReverseY;
-    GrSLPrecision           fPrecision;
-    typedef SkNoncopyable INHERITED;
-
-#ifdef SK_DEBUG
-public:
-    void setInProcessor() const { fInProcessor = true; }
-private:
-    mutable bool fInProcessor;
-#endif
-};
-
-#endif
diff --git a/include/gpu/GrExternalTextureData.h b/include/gpu/GrExternalTextureData.h
deleted file mode 100644
index 5943fd8..0000000
--- a/include/gpu/GrExternalTextureData.h
+++ /dev/null
@@ -1,28 +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 GrExternalTextureData_DEFINED
-#define GrExternalTextureData_DEFINED
-
-#include "GrTypes.h"
-
-class GrContext;
-
-class SK_API GrExternalTextureData : SkNoncopyable {
-public:
-    virtual ~GrExternalTextureData() {}
-    virtual GrBackend getBackend() const = 0;
-protected:
-    virtual GrBackendObject getBackendObject() const = 0;
-    virtual void attachToContext(GrContext*) = 0;
-
-    friend class SkCrossContextImageData;
-    friend class SkImage;
-};
-
-#endif
diff --git a/include/gpu/GrFragmentProcessor.h b/include/gpu/GrFragmentProcessor.h
deleted file mode 100644
index c95c5e5..0000000
--- a/include/gpu/GrFragmentProcessor.h
+++ /dev/null
@@ -1,349 +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 GrFragmentProcessor_DEFINED
-#define GrFragmentProcessor_DEFINED
-
-#include "GrProcessor.h"
-
-class GrCoordTransform;
-class GrGLSLFragmentProcessor;
-class GrInvariantOutput;
-class GrPipeline;
-class GrProcessorKeyBuilder;
-class GrShaderCaps;
-class GrSwizzle;
-
-/** Provides custom fragment shader code. Fragment processors receive an input color (vec4f) and
-    produce an output color. They may reference textures and uniforms. They may use
-    GrCoordTransforms to receive a transformation of the local coordinates that map from local space
-    to the fragment being processed.
- */
-class GrFragmentProcessor : public GrProcessor {
-public:
-    /**
-    *  In many instances (e.g. SkShader::asFragmentProcessor() implementations) it is desirable to
-    *  only consider the input color's alpha. However, there is a competing desire to have reusable
-    *  GrFragmentProcessor subclasses that can be used in other scenarios where the entire input
-    *  color is considered. This function exists to filter the input color and pass it to a FP. It
-    *  does so by returning a parent FP that multiplies the passed in FPs output by the parent's
-    *  input alpha. The passed in FP will not receive an input color.
-    */
-    static sk_sp<GrFragmentProcessor> MulOutputByInputAlpha(sk_sp<GrFragmentProcessor>);
-
-    /**
-     *  This assumes that the input color to the returned processor will be unpremul and that the
-     *  passed processor (which becomes the returned processor's child) produces a premul output.
-     *  The result of the returned processor is a premul of its input color modulated by the child
-     *  processor's premul output.
-     */
-    static sk_sp<GrFragmentProcessor> MakeInputPremulAndMulByOutput(sk_sp<GrFragmentProcessor>);
-
-    /**
-     *  Returns a parent fragment processor that adopts the passed fragment processor as a child.
-     *  The parent will ignore its input color and instead feed the passed in color as input to the
-     *  child.
-     */
-    static sk_sp<GrFragmentProcessor> OverrideInput(sk_sp<GrFragmentProcessor>, GrColor4f);
-
-    /**
-     *  Returns a fragment processor that premuls the input before calling the passed in fragment
-     *  processor.
-     */
-    static sk_sp<GrFragmentProcessor> PremulInput(sk_sp<GrFragmentProcessor>);
-
-    /**
-     *  Returns a fragment processor that calls the passed in fragment processor, and then premuls
-     *  the output.
-     */
-    static sk_sp<GrFragmentProcessor> PremulOutput(sk_sp<GrFragmentProcessor>);
-
-    /**
-     *  Returns a fragment processor that calls the passed in fragment processor, and then unpremuls
-     *  the output.
-     */
-    static sk_sp<GrFragmentProcessor> UnpremulOutput(sk_sp<GrFragmentProcessor>);
-
-    /**
-     *  Returns a fragment processor that calls the passed in fragment processor, and then swizzles
-     *  the output.
-     */
-    static sk_sp<GrFragmentProcessor> SwizzleOutput(sk_sp<GrFragmentProcessor>, const GrSwizzle&);
-
-    /**
-     * Returns a fragment processor that runs the passed in array of fragment processors in a
-     * series. The original input is passed to the first, the first's output is passed to the
-     * second, etc. The output of the returned processor is the output of the last processor of the
-     * series.
-     *
-     * The array elements with be moved.
-     */
-    static sk_sp<GrFragmentProcessor> RunInSeries(sk_sp<GrFragmentProcessor>*, int cnt);
-
-    ~GrFragmentProcessor() override;
-
-    GrGLSLFragmentProcessor* createGLSLInstance() const;
-
-    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
-        this->onGetGLSLProcessorKey(caps, b);
-        for (int i = 0; i < fChildProcessors.count(); ++i) {
-            fChildProcessors[i]->getGLSLProcessorKey(caps, b);
-        }
-    }
-
-    int numCoordTransforms() const { return fCoordTransforms.count(); }
-
-    /** Returns the coordinate transformation at index. index must be valid according to
-        numTransforms(). */
-    const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
-
-    const SkTArray<const GrCoordTransform*, true>& coordTransforms() const {
-        return fCoordTransforms;
-    }
-
-    int numChildProcessors() const { return fChildProcessors.count(); }
-
-    const GrFragmentProcessor& childProcessor(int index) const { return *fChildProcessors[index]; }
-
-    /** Do any of the coordtransforms for this processor require local coords? */
-    bool usesLocalCoords() const { return SkToBool(fFlags & kUsesLocalCoords_Flag); }
-
-    /** Does this FP need a vector to the nearest edge? */
-    bool usesDistanceVectorField() const {
-        return SkToBool(fFlags & kUsesDistanceVectorField_Flag);
-    }
-
-    /**
-     * A GrDrawOp may premultiply its antialiasing coverage into its GrGeometryProcessor's color
-     * output under the following scenario:
-     *   * all the color fragment processors report true to this query,
-     *   * all the coverage fragment processors report true to this query,
-     *   * the blend mode arithmetic allows for it it.
-     * To be compatible a fragment processor's output must be a modulation of its input color or
-     * alpha with a computed premultiplied color or alpha that is in 0..1 range. The computed color
-     * or alpha that is modulated against the input cannot depend on the input's alpha. The computed
-     * value cannot depend on the input's color channels unless it unpremultiplies the input color
-     * channels by the input alpha.
-     */
-    bool compatibleWithCoverageAsAlpha() const {
-        return SkToBool(fFlags & kCompatibleWithCoverageAsAlpha_OptimizationFlag);
-    }
-
-    /**
-     * If this is true then all opaque input colors to the processor produce opaque output colors.
-     */
-    bool preservesOpaqueInput() const {
-        return SkToBool(fFlags & kPreservesOpaqueInput_OptimizationFlag);
-    }
-
-    /**
-     * Tests whether given a constant input color the processor produces a constant output color
-     * (for all fragments). If true outputColor will contain the constant color produces for
-     * inputColor.
-     */
-    bool hasConstantOutputForConstantInput(GrColor4f inputColor, GrColor4f* outputColor) const {
-        if (fFlags & kConstantOutputForConstantInput_OptimizationFlag) {
-            *outputColor = this->constantOutputForConstantInput(inputColor);
-            return true;
-        }
-        return false;
-    }
-    bool hasConstantOutputForConstantInput() const {
-        return SkToBool(fFlags & kConstantOutputForConstantInput_OptimizationFlag);
-    }
-
-    /** Returns true if this and other processor conservatively draw identically. It can only return
-        true when the two processor are of the same subclass (i.e. they return the same object from
-        from getFactory()).
-
-        A return value of true from isEqual() should not be used to test whether the processor would
-        generate the same shader code. To test for identical code generation use getGLSLProcessorKey
-     */
-    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);
-        const GrFragmentProcessor* next();
-
-    private:
-        SkSTArray<4, const GrFragmentProcessor*, true> fFPStack;
-    };
-
-    /**
-     * 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, typename BASE,
-              int (BASE::*COUNT)() const,
-              const T& (BASE::*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;
-    };
-
-    using CoordTransformIter = FPItemIter<GrCoordTransform,
-                                          GrFragmentProcessor,
-                                          &GrFragmentProcessor::numCoordTransforms,
-                                          &GrFragmentProcessor::coordTransform>;
-
-    using TextureAccessIter = FPItemIter<TextureSampler,
-                                         GrProcessor,
-                                         &GrProcessor::numTextureSamplers,
-                                         &GrProcessor::textureSampler>;
-
-protected:
-    enum OptimizationFlags : uint32_t {
-        kNone_OptimizationFlags,
-        kCompatibleWithCoverageAsAlpha_OptimizationFlag = 0x1,
-        kPreservesOpaqueInput_OptimizationFlag = 0x2,
-        kConstantOutputForConstantInput_OptimizationFlag = 0x4,
-        kAll_OptimizationFlags = kCompatibleWithCoverageAsAlpha_OptimizationFlag |
-                                 kPreservesOpaqueInput_OptimizationFlag |
-                                 kConstantOutputForConstantInput_OptimizationFlag
-    };
-    GR_DECL_BITFIELD_OPS_FRIENDS(OptimizationFlags)
-
-    GrFragmentProcessor(OptimizationFlags optimizationFlags) : fFlags(optimizationFlags) {
-        SkASSERT((fFlags & ~kAll_OptimizationFlags) == 0);
-    }
-
-    OptimizationFlags optimizationFlags() const {
-        return static_cast<OptimizationFlags>(kAll_OptimizationFlags & fFlags);
-    }
-
-    /**
-     * This allows one subclass to access another subclass's implementation of
-     * constantOutputForConstantInput. It must only be called when
-     * hasConstantOutputForConstantInput() is known to be true.
-     */
-    static GrColor4f ConstantOutputForConstantInput(const GrFragmentProcessor& fp,
-                                                    GrColor4f input) {
-        SkASSERT(fp.hasConstantOutputForConstantInput());
-        return fp.constantOutputForConstantInput(input);
-    }
-
-    /**
-     * Fragment Processor subclasses call this from their constructor to register coordinate
-     * transformations. Coord transforms provide a mechanism for a processor to receive coordinates
-     * in their FS code. The matrix expresses a transformation from local space. For a given
-     * fragment the matrix will be applied to the local coordinate that maps to the fragment.
-     *
-     * When the transformation has perspective, the transformed coordinates will have
-     * 3 components. Otherwise they'll have 2.
-     *
-     * This must only be called from the constructor because GrProcessors are immutable. The
-     * processor subclass manages the lifetime of the transformations (this function only stores a
-     * pointer). The GrCoordTransform is typically a member field of the GrProcessor subclass.
-     *
-     * A processor subclass that has multiple methods of construction should always add its coord
-     * transforms in a consistent order. The non-virtual implementation of isEqual() automatically
-     * compares transforms and will assume they line up across the two processor instances.
-     */
-    void addCoordTransform(const GrCoordTransform*);
-
-    /**
-     * FragmentProcessor subclasses call this from their constructor to register any child
-     * FragmentProcessors they have. This must be called AFTER all texture accesses and coord
-     * transforms have been added.
-     * This is for processors whose shader code will be composed of nested processors whose output
-     * colors will be combined somehow to produce its output color.  Registering these child
-     * processors will allow the ProgramBuilder to automatically handle their transformed coords and
-     * texture accesses and mangle their uniform and output color names.
-     */
-    int registerChildProcessor(sk_sp<GrFragmentProcessor> child);
-
-    /**
-     * Sub-classes should call this in their constructors if they need access to a distance
-     * vector field to the nearest edge
-     */
-    void setWillUseDistanceVectorField() { fFlags |= kUsesDistanceVectorField_Flag; }
-
-private:
-    void notifyRefCntIsZero() const final;
-
-    virtual GrColor4f constantOutputForConstantInput(GrColor4f /* inputColor */) const {
-        SkFAIL("Subclass must override this if advertising this optimization.");
-        return GrColor4f::TransparentBlack();
-    }
-
-    /** Returns a new instance of the appropriate *GL* implementation class
-        for the given GrFragmentProcessor; caller is responsible for deleting
-        the object. */
-    virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const = 0;
-
-    /** Implemented using GLFragmentProcessor::GenKey as described in this class's comment. */
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const = 0;
-
-    /**
-     * Subclass implements this to support isEqual(). It will only be called if it is known that
-     * the two processors are of the same subclass (i.e. they return the same object from
-     * getFactory()). The processor subclass should not compare its coord transforms as that will
-     * be performed automatically in the non-virtual isEqual().
-     */
-    virtual bool onIsEqual(const GrFragmentProcessor&) const = 0;
-
-    bool hasSameTransforms(const GrFragmentProcessor&) const;
-
-    enum PrivateFlags {
-        kFirstPrivateFlag = kAll_OptimizationFlags + 1,
-        kUsesLocalCoords_Flag = kFirstPrivateFlag,
-        kUsesDistanceVectorField_Flag = kFirstPrivateFlag << 1,
-    };
-
-    mutable uint32_t fFlags = 0;
-
-    SkSTArray<4, const GrCoordTransform*, true> fCoordTransforms;
-
-    /**
-     * This is not SkSTArray<1, sk_sp<GrFragmentProcessor>> because this class holds strong
-     * references until notifyRefCntIsZero and then it holds pending executions.
-     */
-    SkSTArray<1, GrFragmentProcessor*, true> fChildProcessors;
-
-    typedef GrProcessor INHERITED;
-};
-
-GR_MAKE_BITFIELD_OPS(GrFragmentProcessor::OptimizationFlags)
-
-#endif
diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h
index 2d84c63..1ed64ad 100644
--- a/include/gpu/GrGpuResource.h
+++ b/include/gpu/GrGpuResource.h
@@ -260,11 +260,6 @@
     // final class).
     void registerWithCacheWrapped();
 
-    // This is only called by resources that are being exported from Ganesh to client code. It
-    // ensures that the cache can no longer reach this resource, and that it no longer counts
-    // against the budget.
-    void detachFromCache();
-
     GrGpuResource(GrGpu*);
     virtual ~GrGpuResource();
 
diff --git a/include/gpu/GrGpuResourceRef.h b/include/gpu/GrGpuResourceRef.h
index d6da1d2..3e17392 100644
--- a/include/gpu/GrGpuResourceRef.h
+++ b/include/gpu/GrGpuResourceRef.h
@@ -79,7 +79,7 @@
         called. */
     void pendingIOComplete() const;
 
-    friend class GrProcessor;
+    friend class GrResourceIOProcessor;
 
     GrGpuResource*  fResource;
     mutable bool    fOwnRef;
@@ -89,6 +89,65 @@
     typedef SkNoncopyable INHERITED;
 };
 
+class GrSurfaceProxy;
+
+class GrSurfaceProxyRef : SkNoncopyable {
+public:
+    virtual ~GrSurfaceProxyRef();
+
+    GrSurfaceProxy* get() const { return fProxy; }
+
+    /** Does this object own a pending read or write on the resource it is wrapping. */
+    bool ownsPendingIO() const { return fPendingIO; }
+
+    /** What type of IO does this represent? This is independent of whether a normal ref or a
+        pending IO is currently held. */
+    GrIOType ioType() const { return fIOType; }
+
+    /** Shortcut for calling setProxy() with NULL. It cannot be called after markingPendingIO
+        is called. */
+    void reset();
+
+protected:
+    GrSurfaceProxyRef();
+
+    /** ioType expresses what type of IO operations will be marked as
+        pending on the resource when markPendingIO is called. */
+    GrSurfaceProxyRef(sk_sp<GrSurfaceProxy>, GrIOType);
+
+    /** ioType expresses what type of IO operations will be marked as
+        pending on the resource when markPendingIO is called. */
+    void setProxy(sk_sp<GrSurfaceProxy>, GrIOType);
+
+private:
+    /** Called by owning GrProgramElement when the program element is first scheduled for
+        execution. It can only be called once. */
+    void markPendingIO() const;
+
+    /** Called when the program element/draw state is no longer owned by GrOpList-client code.
+        This lets the cache know that the drawing code will no longer schedule additional reads or
+        writes to the resource using the program element or draw state. It can only be called once.
+      */
+    void removeRef() const;
+
+    /** Called to indicate that the previous pending IO is complete. Useful when the owning object
+        still has refs, so it is not about to destroy this GrGpuResourceRef, but its previously
+        pending executions have been complete. Can only be called if removeRef() was not previously
+        called. */
+    void pendingIOComplete() const;
+
+    friend class GrResourceIOProcessor;
+    friend class GrOpList;                 // for setProxy
+
+    GrSurfaceProxy* fProxy;
+    mutable bool    fOwnRef;
+    mutable bool    fPendingIO;
+    GrIOType        fIOType;
+
+    typedef SkNoncopyable INHERITED;
+};
+
+
 /**
  * Templated version of GrGpuResourceRef to enforce type safety.
  */
@@ -163,7 +222,11 @@
         this->reset(resource);
     }
 
-    void reset(T* resource) {
+    GrPendingIOResource(const GrPendingIOResource& that)
+        : GrPendingIOResource(that.get()) {
+    }
+
+    void reset(T* resource = nullptr) {
         if (resource) {
             switch (IO_TYPE) {
                 case kRead_GrIOType:
diff --git a/include/gpu/GrProcessor.h b/include/gpu/GrProcessor.h
deleted file mode 100644
index 8caec8e..0000000
--- a/include/gpu/GrProcessor.h
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrProcessor_DEFINED
-#define GrProcessor_DEFINED
-
-#include "GrColor.h"
-#include "GrBuffer.h"
-#include "GrGpuResourceRef.h"
-#include "GrProcessorUnitTest.h"
-#include "GrProgramElement.h"
-#include "GrSamplerParams.h"
-#include "GrShaderVar.h"
-#include "SkMath.h"
-#include "SkString.h"
-#include "../private/SkAtomics.h"
-
-class GrContext;
-class GrCoordTransform;
-class GrInvariantOutput;
-class GrResourceProvider;
-class GrTextureProxy;
-
-/**
- * Used by processors to build their keys. It incorporates each per-processor key into a larger
- * shader key.
- */
-class GrProcessorKeyBuilder {
-public:
-    GrProcessorKeyBuilder(SkTArray<unsigned char, true>* data) : fData(data), fCount(0) {
-        SkASSERT(0 == fData->count() % sizeof(uint32_t));
-    }
-
-    void add32(uint32_t v) {
-        ++fCount;
-        fData->push_back_n(4, reinterpret_cast<uint8_t*>(&v));
-    }
-
-    /** Inserts count uint32_ts into the key. The returned pointer is only valid until the next
-        add*() call. */
-    uint32_t* SK_WARN_UNUSED_RESULT add32n(int count) {
-        SkASSERT(count > 0);
-        fCount += count;
-        return reinterpret_cast<uint32_t*>(fData->push_back_n(4 * count));
-    }
-
-    size_t size() const { return sizeof(uint32_t) * fCount; }
-
-private:
-    SkTArray<uint8_t, true>* fData; // unowned ptr to the larger key.
-    int fCount;                     // number of uint32_ts added to fData by the processor.
-};
-
-/** Provides custom shader code to the Ganesh shading pipeline. GrProcessor objects *must* be
-    immutable: after being constructed, their fields may not change.
-
-    Dynamically allocated GrProcessors are managed by a per-thread memory pool. The ref count of an
-    processor must reach 0 before the thread terminates and the pool is destroyed.
- */
-class GrProcessor : public GrProgramElement<GrProcessor> {
-public:
-    class TextureSampler;
-    class BufferAccess;
-    class ImageStorageAccess;
-
-    virtual ~GrProcessor();
-
-    /** Human-meaningful string to identify this prcoessor; may be embedded in generated shader
-        code. */
-    virtual const char* name() const = 0;
-
-    /** Human-readable dump of all information */
-    virtual SkString dumpInfo() const {
-        SkString str;
-        str.appendf("Missing data");
-        return str;
-    }
-
-    int numTextureSamplers() const { return fTextureSamplers.count(); }
-
-    /** Returns the access pattern for the texture at index. index must be valid according to
-        numTextureSamplers(). */
-    const TextureSampler& textureSampler(int index) const { return *fTextureSamplers[index]; }
-
-    int numBuffers() const { return fBufferAccesses.count(); }
-
-    /** Returns the access pattern for the buffer at index. index must be valid according to
-        numBuffers(). */
-    const BufferAccess& bufferAccess(int index) const { return *fBufferAccesses[index]; }
-
-    int numImageStorages() const { return fImageStorageAccesses.count(); }
-
-    /** Returns the access object for the image at index. index must be valid according to
-        numImages(). */
-    const ImageStorageAccess& imageStorageAccess(int index) const {
-        return *fImageStorageAccesses[index];
-    }
-
-    /**
-     * Platform specific built-in features that a processor can request for the fragment shader.
-     */
-    enum RequiredFeatures {
-        kNone_RequiredFeatures             = 0,
-        kSampleLocations_RequiredFeature   = 1 << 0
-    };
-
-    GR_DECL_BITFIELD_OPS_FRIENDS(RequiredFeatures);
-
-    RequiredFeatures requiredFeatures() const { return fRequiredFeatures; }
-
-    void* operator new(size_t size);
-    void operator delete(void* target);
-
-    void* operator new(size_t size, void* placement) {
-        return ::operator new(size, placement);
-    }
-    void operator delete(void* target, void* placement) {
-        ::operator delete(target, placement);
-    }
-
-    /** Helper for down-casting to a GrProcessor subclass */
-    template <typename T> const T& cast() const { return *static_cast<const T*>(this); }
-
-    uint32_t classID() const { SkASSERT(kIllegalProcessorClassID != fClassID); return fClassID; }
-
-protected:
-    GrProcessor() : fClassID(kIllegalProcessorClassID), fRequiredFeatures(kNone_RequiredFeatures) {}
-
-    /**
-     * Subclasses call these from their constructor to register sampler/image sources. The processor
-     * subclass manages the lifetime of the objects (these functions only store pointers). The
-     * TextureSampler and/or BufferAccess instances are typically member fields of the GrProcessor
-     * subclass. These must only be called from the constructor because GrProcessors are immutable.
-     */
-    void addTextureSampler(const TextureSampler*);
-    void addBufferAccess(const BufferAccess*);
-    void addImageStorageAccess(const ImageStorageAccess*);
-
-    bool hasSameSamplersAndAccesses(const GrProcessor &) const;
-
-    /**
-     * If the prcoessor will generate code that uses platform specific built-in features, then it
-     * must call these methods from its constructor. Otherwise, requests to use these features will
-     * be denied.
-     */
-    void setWillUseSampleLocations() { fRequiredFeatures |= kSampleLocations_RequiredFeature; }
-
-    void combineRequiredFeatures(const GrProcessor& other) {
-        fRequiredFeatures |= other.fRequiredFeatures;
-    }
-
-    template <typename PROC_SUBCLASS> void initClassID() {
-         static uint32_t kClassID = GenClassID();
-         fClassID = kClassID;
-    }
-
-private:
-    static uint32_t GenClassID() {
-        // fCurrProcessorClassID has been initialized to kIllegalProcessorClassID. The
-        // atomic inc returns the old value not the incremented value. So we add
-        // 1 to the returned value.
-        uint32_t id = static_cast<uint32_t>(sk_atomic_inc(&gCurrProcessorClassID)) + 1;
-        if (!id) {
-            SkFAIL("This should never wrap as it should only be called once for each GrProcessor "
-                   "subclass.");
-        }
-        return id;
-    }
-
-    friend class GrProgramElement<GrProcessor>;
-    void addPendingIOs() const;
-    void removeRefs() const;
-    void pendingIOComplete() const;
-
-    enum {
-        kIllegalProcessorClassID = 0,
-    };
-    static int32_t gCurrProcessorClassID;
-
-    uint32_t                                        fClassID;
-    RequiredFeatures                                fRequiredFeatures;
-    SkSTArray<4, const TextureSampler*, true>       fTextureSamplers;
-    SkSTArray<1, const BufferAccess*, true>         fBufferAccesses;
-    SkSTArray<1, const ImageStorageAccess*, true>   fImageStorageAccesses;
-
-    typedef GrProgramElement INHERITED;
-};
-
-GR_MAKE_BITFIELD_OPS(GrProcessor::RequiredFeatures);
-
-/**
- * Used to represent a texture that is required by a GrProcessor. It holds a GrTexture along with
- * an associated GrSamplerParams. TextureSamplers don't perform any coord manipulation to account
- * for texture origin.
- */
-class GrProcessor::TextureSampler : public SkNoncopyable {
-public:
-    /**
-     * Must be initialized before adding to a GrProcessor's texture access list.
-     */
-    TextureSampler();
-
-    TextureSampler(GrTexture*, const GrSamplerParams&);
-    explicit TextureSampler(GrTexture*,
-                            GrSamplerParams::FilterMode = GrSamplerParams::kNone_FilterMode,
-                            SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
-                            GrShaderFlags visibility = kFragment_GrShaderFlag);
-    void reset(GrTexture*, const GrSamplerParams&,
-               GrShaderFlags visibility = kFragment_GrShaderFlag);
-    void reset(GrTexture*,
-               GrSamplerParams::FilterMode = GrSamplerParams::kNone_FilterMode,
-               SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
-               GrShaderFlags visibility = kFragment_GrShaderFlag);
-
-    // MDB TODO: ultimately we shouldn't need the resource provider parameter
-    TextureSampler(GrResourceProvider*, sk_sp<GrTextureProxy>, const GrSamplerParams&);
-    explicit TextureSampler(GrResourceProvider*, sk_sp<GrTextureProxy>,
-                            GrSamplerParams::FilterMode = GrSamplerParams::kNone_FilterMode,
-                            SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
-                            GrShaderFlags visibility = kFragment_GrShaderFlag);
-    void reset(GrResourceProvider*, sk_sp<GrTextureProxy>, const GrSamplerParams&,
-               GrShaderFlags visibility = kFragment_GrShaderFlag);
-    void reset(GrResourceProvider*, sk_sp<GrTextureProxy>,
-               GrSamplerParams::FilterMode = GrSamplerParams::kNone_FilterMode,
-               SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
-               GrShaderFlags visibility = kFragment_GrShaderFlag);
-
-    bool operator==(const TextureSampler& that) const {
-        return this->texture() == that.texture() &&
-               fParams == that.fParams &&
-               fVisibility == that.fVisibility;
-    }
-
-    bool operator!=(const TextureSampler& other) const { return !(*this == other); }
-
-    GrTexture* texture() const { return fTexture.get(); }
-    GrShaderFlags visibility() const { return fVisibility; }
-    const GrSamplerParams& params() const { return fParams; }
-
-    /**
-     * For internal use by GrProcessor.
-     */
-    const GrGpuResourceRef* programTexture() const { return &fTexture; }
-
-private:
-
-    typedef GrTGpuResourceRef<GrTexture> ProgramTexture;
-
-    ProgramTexture                  fTexture;
-    GrSamplerParams                 fParams;
-    GrShaderFlags                   fVisibility;
-
-    typedef SkNoncopyable INHERITED;
-};
-
-/**
- * Used to represent a texel buffer that will be read in a GrProcessor. It holds a GrBuffer along
- * with an associated offset and texel config.
- */
-class GrProcessor::BufferAccess : public SkNoncopyable {
-public:
-    BufferAccess() = default;
-    BufferAccess(GrPixelConfig texelConfig, GrBuffer* buffer,
-                 GrShaderFlags visibility = kFragment_GrShaderFlag) {
-        this->reset(texelConfig, buffer, visibility);
-    }
-    /**
-     * Must be initialized before adding to a GrProcessor's buffer access list.
-     */
-    void reset(GrPixelConfig texelConfig, GrBuffer* buffer,
-               GrShaderFlags visibility = kFragment_GrShaderFlag) {
-        fTexelConfig = texelConfig;
-        fBuffer.set(SkRef(buffer), kRead_GrIOType);
-        fVisibility = visibility;
-    }
-
-    bool operator==(const BufferAccess& that) const {
-        return fTexelConfig == that.fTexelConfig &&
-               this->buffer() == that.buffer() &&
-               fVisibility == that.fVisibility;
-    }
-
-    bool operator!=(const BufferAccess& that) const { return !(*this == that); }
-
-    GrPixelConfig texelConfig() const { return fTexelConfig; }
-    GrBuffer* buffer() const { return fBuffer.get(); }
-    GrShaderFlags visibility() const { return fVisibility; }
-
-    /**
-     * For internal use by GrProcessor.
-     */
-    const GrGpuResourceRef* programBuffer() const { return &fBuffer;}
-
-private:
-    GrPixelConfig                 fTexelConfig;
-    GrTGpuResourceRef<GrBuffer>   fBuffer;
-    GrShaderFlags                 fVisibility;
-
-    typedef SkNoncopyable INHERITED;
-};
-
-/**
- * This is used by a GrProcessor to access a texture using image load/store in its shader code.
- * ImageStorageAccesses don't perform any coord manipulation to account for texture origin.
- * Currently the format of the load/store data in the shader is inferred from the texture config,
- * though it could be made explicit.
- */
-class GrProcessor::ImageStorageAccess : public SkNoncopyable {
-public:
-    ImageStorageAccess(sk_sp<GrTexture> texture, GrIOType ioType, GrSLMemoryModel, GrSLRestrict,
-                       GrShaderFlags visibility = kFragment_GrShaderFlag);
-
-    bool operator==(const ImageStorageAccess& that) const {
-        return this->texture() == that.texture() && fVisibility == that.fVisibility;
-    }
-
-    bool operator!=(const ImageStorageAccess& that) const { return !(*this == that); }
-
-    GrTexture* texture() const { return fTexture.get(); }
-    GrShaderFlags visibility() const { return fVisibility; }
-    GrIOType ioType() const { return fTexture.ioType(); }
-    GrImageStorageFormat format() const { return fFormat; }
-    GrSLMemoryModel memoryModel() const { return fMemoryModel; }
-    GrSLRestrict restrict() const { return fRestrict; }
-
-    /**
-     * For internal use by GrProcessor.
-     */
-    const GrGpuResourceRef* programTexture() const { return &fTexture; }
-
-private:
-    GrTGpuResourceRef<GrTexture>    fTexture;
-    GrShaderFlags                   fVisibility;
-    GrImageStorageFormat            fFormat;
-    GrSLMemoryModel                 fMemoryModel;
-    GrSLRestrict                    fRestrict;
-    typedef SkNoncopyable INHERITED;
-};
-
-#endif
diff --git a/include/gpu/GrProcessorUnitTest.h b/include/gpu/GrProcessorUnitTest.h
deleted file mode 100644
index 912e393..0000000
--- a/include/gpu/GrProcessorUnitTest.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrProcessorUnitTest_DEFINED
-#define GrProcessorUnitTest_DEFINED
-
-#include "SkTypes.h"
-
-#if GR_TEST_UTILS
-
-#include "../private/GrTextureProxy.h"
-#include "../private/SkTArray.h"
-#include "GrTestUtils.h"
-
-class SkMatrix;
-class GrCaps;
-class GrContext;
-class GrRenderTargetContext;
-struct GrProcessorTestData;
-class GrTexture;
-class GrXPFactory;
-
-namespace GrProcessorUnitTest {
-
-// Used to access the dummy textures in TestCreate procs.
-enum {
-    kSkiaPMTextureIdx = 0,
-    kAlphaTextureIdx = 1,
-};
-
-/** This allows parent FPs to implement a test create with known leaf children in order to avoid
-creating an unbounded FP tree which may overflow various shader limits. */
-sk_sp<GrFragmentProcessor> MakeChildFP(GrProcessorTestData*);
-
-}
-
-/*
- * GrProcessorTestData is an argument struct to TestCreate functions
- * fTextures are valid textures that can optionally be used to construct
- * TextureSampler. The first texture has config kSkia8888_GrPixelConfig and the second has
- * kAlpha_8_GrPixelConfig. TestCreate functions are also free to create additional textures using
- * the GrContext.
- */
-struct GrProcessorTestData {
-    GrProcessorTestData(SkRandom* random,
-                        GrContext* context,
-                        const GrRenderTargetContext* renderTargetContext,
-                        GrTexture* const textures[2])
-            : fRandom(random)
-            , fRenderTargetContext(renderTargetContext)
-            , fContext(context) {
-        fProxies[0] = GrSurfaceProxy::MakeWrapped(sk_ref_sp(textures[0]));
-        fProxies[1] = GrSurfaceProxy::MakeWrapped(sk_ref_sp(textures[1]));
-    }
-    SkRandom* fRandom;
-    const GrRenderTargetContext* fRenderTargetContext;
-
-    GrContext* context() { return fContext; }
-    GrResourceProvider* resourceProvider();
-    const GrCaps* caps();
-    sk_sp<GrTextureProxy> textureProxy(int index) { return fProxies[index]; }
-
-private:
-    GrContext* fContext;
-    sk_sp<GrTextureProxy> fProxies[2];
-};
-
-#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
-class GrProcessor;
-class GrTexture;
-
-template <class Processor> class GrProcessorTestFactory : private SkNoncopyable {
-public:
-    typedef sk_sp<Processor> (*MakeProc)(GrProcessorTestData*);
-
-    GrProcessorTestFactory(MakeProc makeProc) {
-        fMakeProc = makeProc;
-        GetFactories()->push_back(this);
-    }
-
-    /** Pick a random factory function and create a processor.  */
-    static sk_sp<Processor> Make(GrProcessorTestData* data) {
-        VerifyFactoryCount();
-        SkASSERT(GetFactories()->count());
-        uint32_t idx = data->fRandom->nextRangeU(0, GetFactories()->count() - 1);
-        return MakeIdx(idx, data);
-    }
-
-    /** Number of registered factory functions */
-    static int Count() { return GetFactories()->count(); }
-
-    /** Use factory function at Index idx to create a processor. */
-    static sk_sp<Processor> MakeIdx(int idx, GrProcessorTestData* data) {
-        GrProcessorTestFactory<Processor>* factory = (*GetFactories())[idx];
-        sk_sp<Processor> processor = factory->fMakeProc(data);
-        SkASSERT(processor);
-        return processor;
-    }
-
-private:
-    /**
-     * A test function which verifies the count of factories.
-     */
-    static void VerifyFactoryCount();
-
-    MakeProc fMakeProc;
-
-    static SkTArray<GrProcessorTestFactory<Processor>*, true>* GetFactories();
-};
-
-class GrXPFactoryTestFactory : private SkNoncopyable {
-public:
-    using GetFn = const GrXPFactory*(GrProcessorTestData*);
-
-    GrXPFactoryTestFactory(GetFn* getProc) : fGetProc(getProc) { GetFactories()->push_back(this); }
-
-    static const GrXPFactory* Get(GrProcessorTestData* data) {
-        VerifyFactoryCount();
-        SkASSERT(GetFactories()->count());
-        uint32_t idx = data->fRandom->nextRangeU(0, GetFactories()->count() - 1);
-        const GrXPFactory* xpf = (*GetFactories())[idx]->fGetProc(data);
-        SkASSERT(xpf);
-        return xpf;
-    }
-
-private:
-    static void VerifyFactoryCount();
-
-    GetFn* fGetProc;
-    static SkTArray<GrXPFactoryTestFactory*, true>* GetFactories();
-};
-
-/** GrProcessor subclasses should insert this macro in their declaration to be included in the
- *  program generation unit test.
- */
-#define GR_DECLARE_GEOMETRY_PROCESSOR_TEST                                                         \
-    static GrProcessorTestFactory<GrGeometryProcessor> gTestFactory SK_UNUSED;                     \
-    static sk_sp<GrGeometryProcessor> TestCreate(GrProcessorTestData*)
-
-#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST                                                         \
-    static GrProcessorTestFactory<GrFragmentProcessor> gTestFactory SK_UNUSED;                     \
-    static sk_sp<GrFragmentProcessor> TestCreate(GrProcessorTestData*)
-
-#define GR_DECLARE_XP_FACTORY_TEST                                                                 \
-    static GrXPFactoryTestFactory gTestFactory SK_UNUSED;                                          \
-    static const GrXPFactory* TestGet(GrProcessorTestData*)
-
-/** GrProcessor subclasses should insert this macro in their implementation file. They must then
- *  also implement this static function:
- *      GrProcessor* TestCreate(GrProcessorTestData*);
- */
-#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Effect)                                                  \
-    GrProcessorTestFactory<GrFragmentProcessor> Effect::gTestFactory(Effect::TestCreate)
-
-#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(Effect)                                                  \
-    GrProcessorTestFactory<GrGeometryProcessor> Effect::gTestFactory(Effect::TestCreate)
-
-#define GR_DEFINE_XP_FACTORY_TEST(Factory)                                                         \
-    GrXPFactoryTestFactory Factory::gTestFactory(Factory::TestGet)
-
-#else // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
-// The unit test relies on static initializers. Just declare the TestCreate function so that
-// its definitions will compile.
-#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST                                                         \
-    static sk_sp<GrFragmentProcessor> TestCreate(GrProcessorTestData*)
-#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(X)
-
-// 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*)
-#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(X)
-
-// The unit test relies on static initializers. Just declare the TestGet function so that
-// its definitions will compile.
-#define GR_DECLARE_XP_FACTORY_TEST                                                                 \
-    const GrXPFactory* TestGet(GrProcessorTestData*)
-#define GR_DEFINE_XP_FACTORY_TEST(X)
-
-#endif  // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-#else   // GR_TEST_UTILS
-    #define GR_DECLARE_GEOMETRY_PROCESSOR_TEST
-    #define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-    #define GR_DECLARE_XP_FACTORY_TEST
-    #define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
-    #define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
-    #define GR_DEFINE_XP_FACTORY_TEST(...)
-    #define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-    #define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
-    #define GR_DECLARE_GEOMETRY_PROCESSOR_TEST
-    #define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
-    #define GR_DECLARE_XP_FACTORY_TEST
-    #define GR_DEFINE_XP_FACTORY_TEST(...)
-#endif  // GR_TEST_UTILS
-#endif  // GrProcessorUnitTest_DEFINED
diff --git a/include/gpu/GrProgramElement.h b/include/gpu/GrProgramElement.h
deleted file mode 100644
index 2538680..0000000
--- a/include/gpu/GrProgramElement.h
+++ /dev/null
@@ -1,124 +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 GrProgramElement_DEFINED
-#define GrProgramElement_DEFINED
-
-#include "../private/SkTArray.h"
-#include "SkRefCnt.h"
-
-class GrGpuResourceRef;
-
-/**
- * Note: We are converting GrProcessor from ref counting to a single owner model using move
- * semantics. This class will be removed.
- *
- * Base class for GrProcessor. This exists to manage transitioning a GrProcessor from being owned by
- * a client to being scheduled for execution. While a GrProcessor is ref'ed by drawing code its
- * GrGpu resources must also be ref'ed to prevent incorrectly recycling them through the cache.
- * However, once the GrProcessor is baked into a GrPipeline and the drawing code has stopped ref'ing
- * it, it's internal resources can be recycled in some cases.
- *
- * We track this using two types of refs on GrProgramElement. A regular ref is owned by any client
- * that may continue to issue draws that use the GrProgramElement. The GrPipeline owns "pending
- * executions" instead of refs. A pending execution is cleared by ~GrPipeline().
- *
- * While a GrProgramElement is ref'ed any resources it owns are also ref'ed. However, once it gets
- * into the state where it has pending executions AND no refs then it converts its ownership of
- * its GrGpuResources from refs to pending IOs. The pending IOs allow the cache to track when it is
- * safe to recycle a resource even though we still have buffered GrOps that read or write to the
- * the resource.
- *
- * To make this work the subclass, GrProcessor, implements addPendingIOs and pendingIOComplete. The
- * former adds pending reads/writes as appropriate when the processor is recorded in a GrOpList. The
- * latter removes them after the op list executes the operation. These calls must propagate to any
- * children processors. Similarly, the subclass implements a removeRefs function in order to remove
- * refs from resources once the processor is only owned for pending execution.
- */
-template<typename DERIVED> class GrProgramElement : public SkNoncopyable {
-public:
-    virtual ~GrProgramElement() {
-        // fRefCnt can be one when an effect is created statically using GR_CREATE_STATIC_EFFECT
-        SkASSERT((0 == fRefCnt || 1 == fRefCnt) && 0 == fPendingExecutions);
-        // Set to invalid values.
-        SkDEBUGCODE(fRefCnt = fPendingExecutions = -10;)
-    }
-
-    void ref() const {
-        this->validate();
-        // Once the ref cnt reaches zero it should never be ref'ed again.
-        SkASSERT(fRefCnt > 0);
-        ++fRefCnt;
-        this->validate();
-    }
-
-    void unref() const {
-        this->validate();
-        --fRefCnt;
-        if (0 == fRefCnt) {
-            this->notifyRefCntIsZero();
-            if (0 == fPendingExecutions) {
-                delete this;
-                return;
-            } else {
-                static_cast<const DERIVED*>(this)->removeRefs();
-            }
-        }
-        this->validate();
-    }
-
-    void validate() const {
-#ifdef SK_DEBUG
-        SkASSERT(fRefCnt >= 0);
-        SkASSERT(fPendingExecutions >= 0);
-        SkASSERT(fRefCnt + fPendingExecutions > 0);
-#endif
-    }
-
-protected:
-    GrProgramElement() : fRefCnt(1), fPendingExecutions(0) {}
-
-    void addPendingExecution() const {
-        this->validate();
-        if (0 == fPendingExecutions) {
-            static_cast<const DERIVED*>(this)->addPendingIOs();
-        }
-        ++fPendingExecutions;
-        this->validate();
-    }
-
-    void completedExecution() const {
-        this->validate();
-        --fPendingExecutions;
-        if (0 == fPendingExecutions) {
-            if (0 == fRefCnt) {
-                delete this;
-                return;
-            } else {
-                static_cast<const DERIVED*>(this)->pendingIOComplete();
-            }
-        }
-        this->validate();
-    }
-
-private:
-    /** This will be called when the ref cnt is zero. The object may or may not have pending
-        executions. */
-    virtual void notifyRefCntIsZero() const = 0;
-
-    mutable int32_t fRefCnt;
-    // Count of deferred executions not yet issued to the 3D API.
-    mutable int32_t fPendingExecutions;
-
-    // Only this class can access addPendingExecution() and completedExecution().
-    template <typename T> friend class GrPendingProgramElement;
-    friend class GrProcessorSet;
-
-    typedef SkNoncopyable INHERITED;
-};
-
-#endif
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
index 2d4eaf7..bd25f4f 100644
--- a/include/gpu/GrRenderTarget.h
+++ b/include/gpu/GrRenderTarget.h
@@ -30,28 +30,28 @@
     const GrRenderTarget* asRenderTarget() const  override { return this; }
 
     // GrRenderTarget
-    bool isStencilBufferMultisampled() const { return fDesc.fSampleCnt > 0; }
+    bool isStencilBufferMultisampled() const { return fSampleCnt > 0; }
 
-    /**
-     * For our purposes, "Mixed Sampled" means the stencil buffer is multisampled but the color
-     * buffer is not.
-     */
-    bool isMixedSampled() const { return fFlags & Flags::kMixedSampled; }
-
-    /**
-     * "Unified Sampled" means the stencil and color buffers are both multisampled.
-     */
-    bool isUnifiedMultisampled() const { return fDesc.fSampleCnt > 0 && !this->isMixedSampled(); }
+    GrFSAAType fsaaType() const {
+        if (!fSampleCnt) {
+            SkASSERT(!(fFlags & Flags::kMixedSampled));
+            return GrFSAAType::kNone;
+        }
+        return (fFlags & Flags::kMixedSampled) ? GrFSAAType::kMixedSamples
+                                               : GrFSAAType::kUnifiedMSAA;
+    }
 
     /**
      * Returns the number of samples/pixel in the stencil buffer (Zero if non-MSAA).
      */
-    int numStencilSamples() const { return fDesc.fSampleCnt; }
+    int numStencilSamples() const { return fSampleCnt; }
 
     /**
      * Returns the number of samples/pixel in the color buffer (Zero if non-MSAA or mixed sampled).
      */
-    int numColorSamples() const { return this->isMixedSampled() ? 0 : fDesc.fSampleCnt; }
+    int numColorSamples() const {
+        return GrFSAAType::kMixedSamples == this->fsaaType() ? 0 : fSampleCnt;
+    }
 
     /**
      * Call to indicate the multisample contents were modified such that the
@@ -86,12 +86,6 @@
      */
     const SkIRect& getResolveRect() const { return fResolveRect; }
 
-    /**
-     * Provide a performance hint that the render target's contents are allowed
-     * to become undefined.
-     */
-    void discard();
-
     // a MSAA RT may require explicit resolving , it may auto-resolve (e.g. FBO
     // 0 in GL), or be unresolvable because the client didn't give us the
     // resolve destination.
@@ -141,6 +135,7 @@
     friend class GrRenderTargetPriv;
     friend class GrRenderTargetProxy; // for Flags
 
+    int                   fSampleCnt;
     GrStencilAttachment*  fStencilAttachment;
     uint8_t               fMultisampleSpecsID;
     Flags                 fFlags;
diff --git a/include/gpu/GrShaderCaps.h b/include/gpu/GrShaderCaps.h
index e52bcf1..006efb6 100644
--- a/include/gpu/GrShaderCaps.h
+++ b/include/gpu/GrShaderCaps.h
@@ -130,6 +130,8 @@
 
     bool texelFetchSupport() const { return fTexelFetchSupport; }
 
+    bool vertexIDSupport() const { return fVertexIDSupport; }
+
     AdvBlendEqInteraction advBlendEqInteraction() const { return fAdvBlendEqInteraction; }
 
     bool mustEnableAdvBlendEqs() const {
@@ -161,6 +163,8 @@
     // On MacBook, geometry shaders break if they have more than one invocation.
     bool mustImplementGSInvocationsWithLoop() const { return fMustImplementGSInvocationsWithLoop; }
 
+    bool mustObfuscateUniformColor() const { return fMustObfuscateUniformColor; }
+
     // 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.
@@ -282,6 +286,7 @@
     bool fSampleMaskOverrideCoverageSupport : 1;
     bool fExternalTextureSupport : 1;
     bool fTexelFetchSupport : 1;
+    bool fVertexIDSupport : 1;
 
     // Used for specific driver bug work arounds
     bool fCanUseMinAndAbsTogether : 1;
@@ -289,6 +294,7 @@
     bool fAtan2ImplementedAsAtanYOverX : 1;
     bool fRequiresLocalOutputColorForFBFetch : 1;
     bool fMustImplementGSInvocationsWithLoop : 1;
+    bool fMustObfuscateUniformColor : 1;
 
     PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][kGrSLPrecisionCount];
 
diff --git a/include/gpu/GrSurface.h b/include/gpu/GrSurface.h
index 7bfed3c..4caa842 100644
--- a/include/gpu/GrSurface.h
+++ b/include/gpu/GrSurface.h
@@ -14,7 +14,6 @@
 #include "SkImageInfo.h"
 #include "SkRect.h"
 
-class GrOpList;
 class GrRenderTarget;
 class GrSurfacePriv;
 class GrTexture;
@@ -24,12 +23,12 @@
     /**
      * Retrieves the width of the surface.
      */
-    int width() const { return fDesc.fWidth; }
+    int width() const { return fWidth; }
 
     /**
      * Retrieves the height of the surface.
      */
-    int height() const { return fDesc.fHeight; }
+    int height() const { return fHeight; }
 
     /**
      * Helper that gets the width and height of the surface as a bounding rectangle.
@@ -37,8 +36,8 @@
     SkRect getBoundsRect() const { return SkRect::MakeIWH(this->width(), this->height()); }
 
     GrSurfaceOrigin origin() const {
-        SkASSERT(kTopLeft_GrSurfaceOrigin == fDesc.fOrigin || kBottomLeft_GrSurfaceOrigin == fDesc.fOrigin);
-        return fDesc.fOrigin;
+        SkASSERT(kTopLeft_GrSurfaceOrigin == fOrigin || kBottomLeft_GrSurfaceOrigin == fOrigin);
+        return fOrigin;
     }
 
     /**
@@ -47,147 +46,26 @@
      * if client asked us to render to a target that has a pixel
      * config that isn't equivalent with one of our configs.
      */
-    GrPixelConfig config() const { return fDesc.fConfig; }
+    GrPixelConfig config() const { return fConfig; }
 
     /**
-     * Return the descriptor describing the surface
+     * @return the texture associated with the surface, may be null.
      */
-    const GrSurfaceDesc& desc() const { return fDesc; }
+    virtual GrTexture* asTexture() { return nullptr; }
+    virtual const GrTexture* asTexture() const { return nullptr; }
 
     /**
-     * @return the texture associated with the surface, may be NULL.
+     * @return the render target underlying this surface, may be null.
      */
-    virtual GrTexture* asTexture() { return NULL; }
-    virtual const GrTexture* asTexture() const { return NULL; }
-
-    /**
-     * @return the render target underlying this surface, may be NULL.
-     */
-    virtual GrRenderTarget* asRenderTarget() { return NULL; }
-    virtual const GrRenderTarget* asRenderTarget() const { return NULL; }
-
-    /**
-     * Reads a rectangle of pixels from the surface, possibly performing color space conversion.
-     * @param srcColorSpace color space of the source data (this surface)
-     * @param left          left edge of the rectangle to read (inclusive)
-     * @param top           top edge of the rectangle to read (inclusive)
-     * @param width         width of rectangle to read in pixels.
-     * @param height        height of rectangle to read in pixels.
-     * @param config        the pixel config of the destination buffer
-     * @param dstColorSpace color space of the destination buffer
-     * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
-     *                      packed.
-     * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
-     *
-     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
-     *              pixel config.
-     */
-    bool readPixels(SkColorSpace* srcColorSpace,
-                    int left, int top, int width, int height,
-                    GrPixelConfig config,
-                    SkColorSpace* dstColorSpace,
-                    void* buffer,
-                    size_t rowBytes = 0,
-                    uint32_t pixelOpsFlags = 0);
-
-    /**
-     * Reads a rectangle of pixels from the surface. Does not perform any color space conversion.
-     * @param left          left edge of the rectangle to read (inclusive)
-     * @param top           top edge of the rectangle to read (inclusive)
-     * @param width         width of rectangle to read in pixels.
-     * @param height        height of rectangle to read in pixels.
-     * @param config        the pixel config of the destination buffer
-     * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
-     *                      packed.
-     * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
-     *
-     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
-     *              pixel config.
-     */
-    bool readPixels(int left, int top, int width, int height,
-                    GrPixelConfig config,
-                    void* buffer,
-                    size_t rowBytes = 0,
-                    uint32_t pixelOpsFlags = 0) {
-        return this->readPixels(nullptr, left, top, width, height, config, nullptr, buffer,
-                                rowBytes, pixelOpsFlags);
-    }
-
-    /**
-     * Copy the src pixels [buffer, rowbytes, pixelconfig] into the surface at the specified
-     * rectangle, possibly performing color space conversion.
-     * @param dstColorSpace color space of the destination (this surface)
-     * @param left          left edge of the rectangle to write (inclusive)
-     * @param top           top edge of the rectangle to write (inclusive)
-     * @param width         width of rectangle to write in pixels.
-     * @param height        height of rectangle to write in pixels.
-     * @param config        the pixel config of the source buffer
-     * @param srcColorSpace color space of the source buffer
-     * @param buffer        memory to read the rectangle from.
-     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
-     *                      packed.
-     * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
-     *
-     * @return true if the write succeeded, false if not. The write can fail because of an
-     *              unsupported pixel config.
-     */
-    bool writePixels(SkColorSpace* dstColorSpace,
-                     int left, int top, int width, int height,
-                     GrPixelConfig config,
-                     SkColorSpace* srcColorSpace,
-                     const void* buffer,
-                     size_t rowBytes = 0,
-                     uint32_t pixelOpsFlags = 0);
-
-    /**
-     * Copy the src pixels [buffer, rowbytes, pixelconfig] into the surface at the specified
-     * rectangle. Does not perform any color space conversion.
-     * @param left          left edge of the rectangle to write (inclusive)
-     * @param top           top edge of the rectangle to write (inclusive)
-     * @param width         width of rectangle to write in pixels.
-     * @param height        height of rectangle to write in pixels.
-     * @param config        the pixel config of the source buffer
-     * @param buffer        memory to read the rectangle from.
-     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
-     *                      packed.
-     * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
-     *
-     * @return true if the write succeeded, false if not. The write can fail because of an
-     *              unsupported pixel config.
-     */
-    bool writePixels(int left, int top, int width, int height,
-                     GrPixelConfig config,
-                     const void* buffer,
-                     size_t rowBytes = 0,
-                     uint32_t pixelOpsFlags = 0) {
-        return this->writePixels(nullptr, left, top, width, height, config, nullptr, buffer,
-                                 rowBytes, pixelOpsFlags);
-    }
-
-    /**
-     * After this returns any pending writes to the surface will be issued to the backend 3D API.
-     */
-    void flushWrites();
+    virtual GrRenderTarget* asRenderTarget() { return nullptr; }
+    virtual const GrRenderTarget* asRenderTarget() const { return nullptr; }
 
     /** Access methods that are only to be used within Skia code. */
     inline GrSurfacePriv surfacePriv();
     inline const GrSurfacePriv surfacePriv() const;
 
-    typedef void* ReleaseCtx;
-    typedef void (*ReleaseProc)(ReleaseCtx);
-
-    void setRelease(ReleaseProc proc, ReleaseCtx ctx) {
-        fReleaseProc = proc;
-        fReleaseCtx = ctx;
-    }
-
-    void setLastOpList(GrOpList* opList);
-    GrOpList* getLastOpList() { return fLastOpList; }
-
     static size_t WorstCaseSize(const GrSurfaceDesc& desc, bool useNextPow2 = false);
-    static size_t ComputeSize(const GrSurfaceDesc& desc, int colorSamplesPerPixel,
+    static size_t ComputeSize(GrPixelConfig config, int width, int height, int colorSamplesPerPixel,
                               bool hasMIPMaps, bool useNextPow2 = false);
 
 protected:
@@ -200,37 +78,22 @@
     friend class GrSurfacePriv;
 
     GrSurface(GrGpu* gpu, const GrSurfaceDesc& desc)
-        : INHERITED(gpu)
-        , fDesc(desc)
-        , fReleaseProc(NULL)
-        , fReleaseCtx(NULL)
-        , fLastOpList(nullptr) {
-    }
-    ~GrSurface() override;
+            : INHERITED(gpu)
+            , fConfig(desc.fConfig)
+            , fWidth(desc.fWidth)
+            , fHeight(desc.fHeight)
+            , fOrigin(desc.fOrigin) {}
+    ~GrSurface() override {}
 
-    GrSurfaceDesc fDesc;
 
     void onRelease() override;
     void onAbandon() override;
 
 private:
-    void invokeReleaseProc() {
-        if (fReleaseProc) {
-            fReleaseProc(fReleaseCtx);
-            fReleaseProc = NULL;
-        }
-    }
-
-    ReleaseProc fReleaseProc;
-    ReleaseCtx  fReleaseCtx;
-
-    // The last opList that wrote to or is currently going to write to this surface
-    // The opList can be closed (e.g., no render target or texture context is currently bound
-    // to this renderTarget or texture).
-    // This back-pointer is required so that we can add a dependancy between
-    // the opList used to create the current contents of this surface
-    // and the opList of a destination surface to which this one is being drawn or copied.
-    GrOpList* fLastOpList;
+    GrPixelConfig        fConfig;
+    int                  fWidth;
+    int                  fHeight;
+    GrSurfaceOrigin      fOrigin;
 
     typedef GrGpuResource INHERITED;
 };
diff --git a/include/gpu/GrTestUtils.h b/include/gpu/GrTestUtils.h
deleted file mode 100644
index 5bb1cc1..0000000
--- a/include/gpu/GrTestUtils.h
+++ /dev/null
@@ -1,150 +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 GrTestUtils_DEFINED
-#define GrTestUtils_DEFINED
-
-#include "SkTypes.h"
-
-#if GR_TEST_UTILS
-
-#include "GrColor.h"
-#include "GrColorSpaceXform.h"
-#include "SkPathEffect.h"
-#include "SkRandom.h"
-#include "SkShader.h"
-#include "SkStrokeRec.h"
-#include "../private/SkTemplates.h"
-
-struct GrProcessorTestData;
-class GrStyle;
-class SkMatrix;
-class SkPath;
-class SkRRect;
-struct SkRect;
-
-namespace GrTest {
-/**
- * Helpers for use in Test functions.
- */
-const SkMatrix& TestMatrix(SkRandom*);
-const SkMatrix& TestMatrixPreservesRightAngles(SkRandom*);
-const SkMatrix& TestMatrixRectStaysRect(SkRandom*);
-const SkMatrix& TestMatrixInvertible(SkRandom*);
-const SkMatrix& TestMatrixPerspective(SkRandom*);
-const SkRect& TestRect(SkRandom*);
-const SkRect& TestSquare(SkRandom*);
-const SkRRect& TestRRectSimple(SkRandom*);
-const SkPath& TestPath(SkRandom*);
-const SkPath& TestPathConvex(SkRandom*);
-SkStrokeRec TestStrokeRec(SkRandom*);
-/** Creates styles with dash path effects and null path effects */
-void TestStyle(SkRandom*, GrStyle*);
-sk_sp<SkColorSpace> TestColorSpace(SkRandom*);
-sk_sp<GrColorSpaceXform> TestColorXform(SkRandom*);
-
-class TestAsFPArgs {
-public:
-    TestAsFPArgs(GrProcessorTestData*);
-    const SkShader::AsFPArgs& args() const { return fArgs; }
-
-private:
-    SkShader::AsFPArgs fArgs;
-    SkMatrix fViewMatrixStorage;
-    sk_sp<SkColorSpace> fColorSpaceStorage;
-};
-
-// We have a simplified dash path effect here to avoid relying on SkDashPathEffect which
-// is in the optional build target effects.
-class TestDashPathEffect : public SkPathEffect {
-public:
-    static sk_sp<SkPathEffect> Make(const SkScalar* intervals, int count, SkScalar phase) {
-        return sk_sp<SkPathEffect>(new TestDashPathEffect(intervals, count, phase));
-    }
-
-    bool filterPath(SkPath* dst, const SkPath&, SkStrokeRec* , const SkRect*) const override;
-    DashType asADash(DashInfo* info) const override;
-    Factory getFactory() const override { return nullptr; }
-    void toString(SkString*) const override {}
-
-private:
-    TestDashPathEffect(const SkScalar* intervals, int count, SkScalar phase);
-
-    int                     fCount;
-    SkAutoTArray<SkScalar>  fIntervals;
-    SkScalar                fPhase;
-    SkScalar                fInitialDashLength;
-    int                     fInitialDashIndex;
-    SkScalar                fIntervalLength;
-};
-
-}  // namespace GrTest
-
-static inline GrColor GrRandomColor(SkRandom* random) {
-    // There are only a few cases of random colors which interest us
-    enum ColorMode {
-        kAllOnes_ColorMode,
-        kAllZeros_ColorMode,
-        kAlphaOne_ColorMode,
-        kRandom_ColorMode,
-        kLast_ColorMode = kRandom_ColorMode
-    };
-
-    ColorMode colorMode = ColorMode(random->nextULessThan(kLast_ColorMode + 1));
-    GrColor color SK_INIT_TO_AVOID_WARNING;
-    switch (colorMode) {
-        case kAllOnes_ColorMode:
-            color = GrColorPackRGBA(0xFF, 0xFF, 0xFF, 0xFF);
-            break;
-        case kAllZeros_ColorMode:
-            color = GrColorPackRGBA(0, 0, 0, 0);
-            break;
-        case kAlphaOne_ColorMode:
-            color = GrColorPackRGBA(random->nextULessThan(256),
-                                    random->nextULessThan(256),
-                                    random->nextULessThan(256),
-                                    0xFF);
-            break;
-        case kRandom_ColorMode: {
-                uint8_t alpha = random->nextULessThan(256);
-                color = GrColorPackRGBA(random->nextRangeU(0, alpha),
-                                        random->nextRangeU(0, alpha),
-                                        random->nextRangeU(0, alpha),
-                                        alpha);
-            break;
-        }
-    }
-    GrColorIsPMAssert(color);
-    return color;
-}
-
-static inline uint8_t GrRandomCoverage(SkRandom* random) {
-    enum CoverageMode {
-        kZero_CoverageMode,
-        kAllOnes_CoverageMode,
-        kRandom_CoverageMode,
-        kLast_CoverageMode = kRandom_CoverageMode
-    };
-
-    CoverageMode colorMode = CoverageMode(random->nextULessThan(kLast_CoverageMode + 1));
-    uint8_t coverage SK_INIT_TO_AVOID_WARNING;
-    switch (colorMode) {
-        case kZero_CoverageMode:
-            coverage = 0;
-            break;
-        case kAllOnes_CoverageMode:
-            coverage = 0xff;
-            break;
-        case kRandom_CoverageMode:
-            coverage = random->nextULessThan(256);
-            break;
-    }
-    return coverage;
-}
-
-#endif
-#endif
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index 1f63958..a7adc05 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -14,7 +14,6 @@
 #include "SkPoint.h"
 #include "SkRefCnt.h"
 
-class GrExternalTextureData;
 class GrTexturePriv;
 
 class GrTexture : virtual public GrSurface {
@@ -37,10 +36,15 @@
 #ifdef SK_DEBUG
     void validate() const {
         this->INHERITED::validate();
-        this->validateDesc();
     }
 #endif
 
+    // These match the definitions in SkImage, for whence they came
+    typedef void* ReleaseCtx;
+    typedef void (*ReleaseProc)(ReleaseCtx);
+
+    virtual void setRelease(ReleaseProc proc, ReleaseCtx ctx) = 0;
+
     /** Access methods that are only to be used within Skia code. */
     inline GrTexturePriv texturePriv();
     inline const GrTexturePriv texturePriv() const;
@@ -49,9 +53,6 @@
     GrTexture(GrGpu*, const GrSurfaceDesc&, GrSLType samplerType,
               GrSamplerParams::FilterMode highestFilterMode, bool wasMipMapDataProvided);
 
-    void validateDesc() const;
-    virtual std::unique_ptr<GrExternalTextureData> detachBackendTexture() = 0;
-
 private:
     void computeScratchKey(GrScratchKey*) const override;
     size_t onGpuMemorySize() const override;
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index 62e8a79..51f24b4 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -298,10 +298,7 @@
      * 8 bit signed integers per-channel. Byte order is b,g,r,a.
      */
     kRGBA_8888_sint_GrPixelConfig,
-    /**
-     * ETC1 Compressed Data
-     */
-    kETC1_GrPixelConfig,
+
     /**
      * Byte order is r, g, b, a.  This color format is 32 bits per channel
      */
@@ -337,58 +334,6 @@
     #error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format."
 #endif
 
-// Returns true if the pixel config is a GPU-specific compressed format
-// representation.
-static inline bool GrPixelConfigIsCompressed(GrPixelConfig config) {
-    switch (config) {
-        case kETC1_GrPixelConfig:
-            return true;
-        case kUnknown_GrPixelConfig:
-        case kAlpha_8_GrPixelConfig:
-        case kGray_8_GrPixelConfig:
-        case kRGB_565_GrPixelConfig:
-        case kRGBA_4444_GrPixelConfig:
-        case kRGBA_8888_GrPixelConfig:
-        case kBGRA_8888_GrPixelConfig:
-        case kSRGBA_8888_GrPixelConfig:
-        case kSBGRA_8888_GrPixelConfig:
-        case kRGBA_8888_sint_GrPixelConfig:
-        case kRGBA_float_GrPixelConfig:
-        case kRG_float_GrPixelConfig:
-        case kAlpha_half_GrPixelConfig:
-        case kRGBA_half_GrPixelConfig:
-            return false;
-    }
-    SkFAIL("Invalid pixel config");
-    return false;
-}
-
-/** If the pixel config is compressed, return an equivalent uncompressed format. */
-static inline GrPixelConfig GrMakePixelConfigUncompressed(GrPixelConfig config) {
-    switch (config) {
-        case kETC1_GrPixelConfig:
-            return kRGBA_8888_GrPixelConfig;
-        case kUnknown_GrPixelConfig:
-        case kAlpha_8_GrPixelConfig:
-        case kGray_8_GrPixelConfig:
-        case kRGB_565_GrPixelConfig:
-        case kRGBA_4444_GrPixelConfig:
-        case kRGBA_8888_GrPixelConfig:
-        case kBGRA_8888_GrPixelConfig:
-        case kSRGBA_8888_GrPixelConfig:
-        case kSBGRA_8888_GrPixelConfig:
-        case kRGBA_8888_sint_GrPixelConfig:
-        case kRGBA_float_GrPixelConfig:
-        case kRG_float_GrPixelConfig:
-        case kAlpha_half_GrPixelConfig:
-        case kRGBA_half_GrPixelConfig:
-            SkASSERT(!GrPixelConfigIsCompressed(config));
-            return config;
-    }
-    SkFAIL("Invalid pixel config");
-    return config;
-}
-
 // Returns true if the pixel config is 32 bits per pixel
 static inline bool GrPixelConfigIs8888Unorm(GrPixelConfig config) {
     switch (config) {
@@ -403,7 +348,6 @@
         case kRGB_565_GrPixelConfig:
         case kRGBA_4444_GrPixelConfig:
         case kRGBA_8888_sint_GrPixelConfig:
-        case kETC1_GrPixelConfig:
         case kRGBA_float_GrPixelConfig:
         case kRG_float_GrPixelConfig:
         case kAlpha_half_GrPixelConfig:
@@ -429,7 +373,6 @@
         case kRGBA_8888_GrPixelConfig:
         case kBGRA_8888_GrPixelConfig:
         case kRGBA_8888_sint_GrPixelConfig:
-        case kETC1_GrPixelConfig:
         case kRGBA_float_GrPixelConfig:
         case kRG_float_GrPixelConfig:
         case kAlpha_half_GrPixelConfig:
@@ -458,7 +401,6 @@
         case kRGB_565_GrPixelConfig:
         case kRGBA_4444_GrPixelConfig:
         case kRGBA_8888_sint_GrPixelConfig:
-        case kETC1_GrPixelConfig:
         case kRGBA_float_GrPixelConfig:
         case kRG_float_GrPixelConfig:
         case kAlpha_half_GrPixelConfig:
@@ -470,7 +412,6 @@
 }
 
 static inline size_t GrBytesPerPixel(GrPixelConfig config) {
-    SkASSERT(!GrPixelConfigIsCompressed(config));
     switch (config) {
         case kAlpha_8_GrPixelConfig:
         case kGray_8_GrPixelConfig:
@@ -492,7 +433,6 @@
         case kRG_float_GrPixelConfig:
             return 8;
         case kUnknown_GrPixelConfig:
-        case kETC1_GrPixelConfig:
             return 0;
     }
     SkFAIL("Invalid pixel config");
@@ -501,9 +441,9 @@
 
 static inline bool GrPixelConfigIsOpaque(GrPixelConfig config) {
     switch (config) {
-        case kETC1_GrPixelConfig:
         case kRGB_565_GrPixelConfig:
         case kGray_8_GrPixelConfig:
+        case kRG_float_GrPixelConfig:
             return true;
         case kAlpha_8_GrPixelConfig:
         case kRGBA_4444_GrPixelConfig:
@@ -515,7 +455,6 @@
         case kRGBA_8888_sint_GrPixelConfig:
         case kRGBA_half_GrPixelConfig:
         case kRGBA_float_GrPixelConfig:
-        case kRG_float_GrPixelConfig:
         case kUnknown_GrPixelConfig:
             return false;
     }
@@ -537,7 +476,6 @@
         case kSRGBA_8888_GrPixelConfig:
         case kSBGRA_8888_GrPixelConfig:
         case kRGBA_8888_sint_GrPixelConfig:
-        case kETC1_GrPixelConfig:
         case kRGBA_float_GrPixelConfig:
         case kRG_float_GrPixelConfig:
         case kRGBA_half_GrPixelConfig:
@@ -564,7 +502,6 @@
         case kSRGBA_8888_GrPixelConfig:
         case kSBGRA_8888_GrPixelConfig:
         case kRGBA_8888_sint_GrPixelConfig:
-        case kETC1_GrPixelConfig:
             return false;
     }
     SkFAIL("Invalid pixel config");
@@ -579,21 +516,17 @@
  * Optional bitfield flags that can be set on GrSurfaceDesc (below).
  */
 enum GrSurfaceFlags {
-    kNone_GrSurfaceFlags            = 0x0,
+    kNone_GrSurfaceFlags = 0x0,
     /**
      * Creates a texture that can be rendered to as a GrRenderTarget. Use
      * GrTexture::asRenderTarget() to access.
      */
-    kRenderTarget_GrSurfaceFlag     = 0x1,
+    kRenderTarget_GrSurfaceFlag = 0x1,
     /**
-     * Placeholder for managing zero-copy textures
+     * Clears to zero on creation. It will cause creation failure if initial data is supplied to the
+     * texture. This only affects the base level if the texture is created with MIP levels.
      */
-    kZeroCopy_GrSurfaceFlag         = 0x2,
-    /**
-     * Indicates that all allocations (color buffer, FBO completeness, etc)
-     * should be verified.
-     */
-    kCheckAllocation_GrSurfaceFlag  = 0x4,
+    kPerformInitialClear_GrSurfaceFlag = 0x2
 };
 
 GR_MAKE_BITFIELD_OPS(GrSurfaceFlags)
@@ -676,9 +609,6 @@
 
     /** Skia will assume ownership of the resource and free it. */
     kAdopt_GrWrapOwnership,
-
-    /** Skia will assume ownership of the resource, free it, and reuse it within the cache. */
-    kAdoptAndCache_GrWrapOwnership,
 };
 
 /**
@@ -792,41 +722,6 @@
 };
 
 /**
- * Returns the data size for the given compressed pixel config
- */
-static inline size_t GrCompressedFormatDataSize(GrPixelConfig config,
-                                                int width, int height) {
-    SkASSERT(GrPixelConfigIsCompressed(config));
-
-    switch (config) {
-        case kETC1_GrPixelConfig:
-            SkASSERT((width & 3) == 0);
-            SkASSERT((height & 3) == 0);
-            return (width >> 2) * (height >> 2) * 8;
-
-        case kUnknown_GrPixelConfig:
-        case kAlpha_8_GrPixelConfig:
-        case kGray_8_GrPixelConfig:
-        case kRGB_565_GrPixelConfig:
-        case kRGBA_4444_GrPixelConfig:
-        case kRGBA_8888_GrPixelConfig:
-        case kBGRA_8888_GrPixelConfig:
-        case kSRGBA_8888_GrPixelConfig:
-        case kSBGRA_8888_GrPixelConfig:
-        case kRGBA_8888_sint_GrPixelConfig:
-        case kRGBA_float_GrPixelConfig:
-        case kRG_float_GrPixelConfig:
-        case kAlpha_half_GrPixelConfig:
-        case kRGBA_half_GrPixelConfig:
-            SkFAIL("Unknown compressed pixel config");
-            return 4 * width * height;
-    }
-
-    SkFAIL("Invalid pixel config");
-    return 4 * width * height;
-}
-
-/**
  * This value translates to reseting all the context state for any backend.
  */
 static const uint32_t kAll_GrBackendState = 0xffffffff;
diff --git a/include/gpu/GrTypesPriv.h b/include/gpu/GrTypesPriv.h
index 5db1c24..42da3d4 100644
--- a/include/gpu/GrTypesPriv.h
+++ b/include/gpu/GrTypesPriv.h
@@ -51,6 +51,40 @@
     return false;
 }
 
+/** The type of full scene antialiasing supported by a render target. */
+enum class GrFSAAType {
+    /** No FSAA */
+    kNone,
+    /** Regular MSAA where each attachment has the same sample count. */
+    kUnifiedMSAA,
+    /** One color sample, N stencil samples. */
+    kMixedSamples,
+};
+
+/**
+ * Not all drawing code paths support using mixed samples when available and instead use
+ * coverage-based aa.
+ */
+enum class GrAllowMixedSamples { kNo, kYes };
+
+static inline GrAAType GrChooseAAType(GrAA aa, GrFSAAType fsaaType,
+                                      GrAllowMixedSamples allowMixedSamples) {
+    if (GrAA::kNo == aa) {
+        return GrAAType::kNone;
+    }
+    switch (fsaaType) {
+        case GrFSAAType::kNone:
+            return GrAAType::kCoverage;
+        case GrFSAAType::kUnifiedMSAA:
+            return GrAAType::kMSAA;
+        case GrFSAAType::kMixedSamples:
+            return GrAllowMixedSamples::kYes == allowMixedSamples ? GrAAType::kMixedSamples
+                                                                  : GrAAType::kCoverage;
+    }
+    SkFAIL("Unexpected fsaa type");
+    return GrAAType::kNone;
+}
+
 /**
  * Types of shader-language-specific boxed variables we can create. (Currently only GrGLShaderVars,
  * but should be applicable to other shader languages.)
@@ -98,14 +132,6 @@
 };
 GR_MAKE_BITFIELD_OPS(GrShaderFlags);
 
-enum class GrDrawFace {
-    kInvalid = -1,
-
-    kBoth,
-    kCCW,
-    kCW,
-};
-
 /**
  * Precisions of shader language variables. Not all shading languages support precisions or actually
  * vary the internal precision based on the qualifiers. These currently only apply to float types (
@@ -116,11 +142,14 @@
     kMedium_GrSLPrecision,
     kHigh_GrSLPrecision,
 
-    // Default precision is medium. This is because on OpenGL ES 2 highp support is not
-    // guaranteed. On (non-ES) OpenGL the specifiers have no effect on precision.
-    kDefault_GrSLPrecision = kMedium_GrSLPrecision,
+    // Default precision is a special tag that means "whatever the default for the program/type
+    // combination is". In other words, it maps to the empty string in shader code. There are some
+    // scenarios where kDefault is not allowed (as the default precision for a program, or for
+    // varyings, for example).
+    kDefault_GrSLPrecision,
 
-    kLast_GrSLPrecision = kHigh_GrSLPrecision
+    // We only consider the "real" precisions here
+    kLast_GrSLPrecision = kHigh_GrSLPrecision,
 };
 
 static const int kGrSLPrecisionCount = kLast_GrSLPrecision + 1;
diff --git a/include/gpu/effects/GrBlurredEdgeFragmentProcessor.h b/include/gpu/effects/GrBlurredEdgeFragmentProcessor.h
deleted file mode 100644
index d0864ed..0000000
--- a/include/gpu/effects/GrBlurredEdgeFragmentProcessor.h
+++ /dev/null
@@ -1,68 +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 GrBlurredEdgeFragmentProcessor_DEFINED
-#define GrBlurredEdgeFragmentProcessor_DEFINED
-
-#include "GrFragmentProcessor.h"
-
-/**
- * Shader for managing a blurred edge for a shadow.
- *
- * There are two blurring modes supported: Gaussian blur function and smoothstep function.
- *
- * If the primitive supports an implicit distance to the edge, the radius of the blur is specified
- * by r & g values of the color in 14.2 fixed point. For spot shadows, we increase the stroke width
- * to set the shadow against the shape. This pad is specified by b, also in 6.2 fixed point.
- * The a value represents the max final alpha.
- *
- * When not using implicit distance, then b in the input color represents the input to the
- * blur function, and r the max final alpha.
- *
- */
-class GrBlurredEdgeFP : public GrFragmentProcessor {
-public:
-    enum Mode {
-        kGaussian_Mode,
-        kSmoothstep_Mode,
-
-        kLastMode = kSmoothstep_Mode
-    };
-    static const int kModeCnt = kLastMode + 1;
-
-    static sk_sp<GrFragmentProcessor> Make(Mode mode = kGaussian_Mode) {
-        return sk_sp<GrFragmentProcessor>(new GrBlurredEdgeFP(mode));
-    }
-
-    const char* name() const override { return "BlurredEdge"; }
-
-    Mode mode() const { return fMode; }
-
-private:
-    GrBlurredEdgeFP(Mode mode)
-        : INHERITED(kNone_OptimizationFlags)
-        , fMode(mode) {
-        // enable output of distance information for shape
-        this->setWillUseDistanceVectorField();
-
-        this->initClassID<GrBlurredEdgeFP>();
-    }
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor&) const override;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    Mode   fMode;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-#endif
diff --git a/include/gpu/gl/GrGLFunctions.h b/include/gpu/gl/GrGLFunctions.h
index 0369587..930a0c1 100644
--- a/include/gpu/gl/GrGLFunctions.h
+++ b/include/gpu/gl/GrGLFunctions.h
@@ -40,6 +40,8 @@
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearProc)(GrGLbitfield mask);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearStencilProc)(GrGLint s);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearTexImageProc)(GrGLuint texture, GrGLint level, GrGLenum format, GrGLenum type,const GrGLvoid * data);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearTexSubImageProc)(GrGLuint texture, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLenum format, GrGLenum type,const GrGLvoid * data);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLColorMaskProc)(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompileShaderProc)(GrGLuint shader);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompressedTexImage2DProc)(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data);
@@ -122,6 +124,7 @@
 typedef GrGLvoid* (GR_GL_FUNCTION_TYPE* GrGLMemoryBarrierProc)(GrGLbitfield barriers);
 typedef GrGLvoid* (GR_GL_FUNCTION_TYPE* GrGLMemoryBarrierByRegionProc)(GrGLbitfield barriers);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPixelStoreiProc)(GrGLenum pname, GrGLint param);
+typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPolygonModeProc)(GrGLenum face, GrGLenum mode);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPopGroupMarkerProc)();
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPushGroupMarkerProc)(GrGLsizei length, const char* marker);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLQueryCounterProc)(GrGLuint id, GrGLenum target);
diff --git a/include/gpu/gl/GrGLInterface.h b/include/gpu/gl/GrGLInterface.h
index 0a977ea..e5479eb 100644
--- a/include/gpu/gl/GrGLInterface.h
+++ b/include/gpu/gl/GrGLInterface.h
@@ -125,6 +125,8 @@
         GrGLFunction<GrGLClearProc> fClear;
         GrGLFunction<GrGLClearColorProc> fClearColor;
         GrGLFunction<GrGLClearStencilProc> fClearStencil;
+        GrGLFunction<GrGLClearTexImageProc> fClearTexImage;
+        GrGLFunction<GrGLClearTexSubImageProc> fClearTexSubImage;
         GrGLFunction<GrGLColorMaskProc> fColorMask;
         GrGLFunction<GrGLCompileShaderProc> fCompileShader;
         GrGLFunction<GrGLCompressedTexImage2DProc> fCompressedTexImage2D;
@@ -209,6 +211,7 @@
         GrGLFunction<GrGLMultiDrawArraysIndirectProc> fMultiDrawArraysIndirect;
         GrGLFunction<GrGLMultiDrawElementsIndirectProc> fMultiDrawElementsIndirect;
         GrGLFunction<GrGLPixelStoreiProc> fPixelStorei;
+        GrGLFunction<GrGLPolygonModeProc> fPolygonMode;
         GrGLFunction<GrGLPopGroupMarkerProc> fPopGroupMarker;
         GrGLFunction<GrGLPushGroupMarkerProc> fPushGroupMarker;
         GrGLFunction<GrGLQueryCounterProc> fQueryCounter;
diff --git a/include/gpu/gl/GrGLSLPrettyPrint.h b/include/gpu/gl/GrGLSLPrettyPrint.h
deleted file mode 100644
index 52fb745..0000000
--- a/include/gpu/gl/GrGLSLPrettyPrint.h
+++ /dev/null
@@ -1,19 +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 GrGLSLPrettyPrint_DEFINED
-#define GrGLSLPrettyPrint_DEFINED
-
-#include "SkString.h"
-
-namespace GrGLSLPrettyPrint {
-    SkString PrettyPrintGLSL(const char** strings,
-                             int* lengths,
-                             int count,
-                             bool countlines);
-};
-
-#endif /* GRGLPRETTYPRINTSL_H_ */
diff --git a/include/gpu/gl/GrGLTypes.h b/include/gpu/gl/GrGLTypes.h
index 0a2346e..a615888 100644
--- a/include/gpu/gl/GrGLTypes.h
+++ b/include/gpu/gl/GrGLTypes.h
@@ -9,7 +9,6 @@
 #ifndef GrGLTypes_DEFINED
 #define GrGLTypes_DEFINED
 
-#include "GrExternalTextureData.h"
 #include "GrGLConfig.h"
 #include "SkRefCnt.h"
 
@@ -60,7 +59,7 @@
 typedef signed long int GrGLsizeiptr;
 #endif
 typedef void* GrGLeglImage;
-typedef void* GrGLsync;
+typedef struct __GLsync* GrGLsync;
 
 struct GrGLDrawArraysIndirectCommand {
     GrGLuint fCount;
@@ -114,25 +113,12 @@
     GrGLuint fID;
 };
 
-class GrSemaphore;
+GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrGLTextureInfo*));
 
-class GrGLExternalTextureData : public GrExternalTextureData {
-public:
-    GrGLExternalTextureData(const GrGLTextureInfo& info, sk_sp<GrSemaphore> semaphore, GrContext*);
-    GrBackend getBackend() const override { return kOpenGL_GrBackend; }
-
-protected:
-    GrBackendObject getBackendObject() const override {
-        return reinterpret_cast<GrBackendObject>(&fInfo);
-    }
-    void attachToContext(GrContext*) override;
-
-    GrGLTextureInfo fInfo;
-    sk_sp<GrSemaphore> fSemaphore;
-
-    typedef GrExternalTextureData INHERITED;
+struct GrGLFramebufferInfo {
+    GrGLuint fFBOID;
 };
 
-GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrGLTextureInfo*));
+GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrGLFramebufferInfo*));
 
 #endif
diff --git a/include/gpu/vk/GrVkBackendContext.h b/include/gpu/vk/GrVkBackendContext.h
index 5e51bee..429319c 100644
--- a/include/gpu/vk/GrVkBackendContext.h
+++ b/include/gpu/vk/GrVkBackendContext.h
@@ -11,8 +11,7 @@
 #include "SkRefCnt.h"
 
 #include "vk/GrVkDefines.h"
-
-struct GrVkInterface;
+#include "vk/GrVkInterface.h"
 
 enum GrVkExtensionFlags {
     kEXT_debug_report_GrVkExtensionFlag    = 0x0001,
@@ -46,15 +45,46 @@
     uint32_t                   fExtensions;
     uint32_t                   fFeatures;
     sk_sp<const GrVkInterface> fInterface;
+    /**
+     * Controls whether this object destroys the instance and device upon destruction. The default
+     * is temporarily 'true' to avoid breaking existing clients but will be changed to 'false'.
+     */
+    bool                       fOwnsInstanceAndDevice = true;
 
     using CanPresentFn = std::function<bool(VkInstance, VkPhysicalDevice,
                                             uint32_t queueFamilyIndex)>;
 
-    // Helper function to create the default Vulkan objects needed by the GrVkGpu object
-    // If presentQueueIndex is non-NULL, will try to set up presentQueue as part of device
-    // creation using the platform-specific canPresent() function.
+    /**
+     * Helper function to create the Vulkan objects needed for a Vulkan-backed GrContext.
+     * Note that the version that uses the unified "GetProc" instead of separate "GetInstanceProc"
+     * and "GetDeviceProc" functions will be removed.
+     *
+     * If presentQueueIndex is non-NULL, will try to set up presentQueue as part of device
+     * creation using the platform-specific canPresent() function.
+     *
+     * This will set fOwnsInstanceAndDevice to 'true'. If it is subsequently set to 'false' then
+     * the client owns the lifetime of the created VkDevice and VkInstance.
+     */
     static const GrVkBackendContext* Create(uint32_t* presentQueueIndex = nullptr,
-                                            CanPresentFn = CanPresentFn());
+                                            CanPresentFn = CanPresentFn(),
+                                            GrVkInterface::GetProc getProc = nullptr);
+
+    static const GrVkBackendContext* Create(const GrVkInterface::GetInstanceProc& getInstanceProc,
+                                            const GrVkInterface::GetDeviceProc& getDeviceProc,
+                                            uint32_t* presentQueueIndex = nullptr,
+                                            CanPresentFn canPresent = CanPresentFn()) {
+        if (!getInstanceProc || !getDeviceProc) {
+            return nullptr;
+        }
+        auto getProc = [&getInstanceProc, &getDeviceProc](const char* proc_name,
+                                                          VkInstance instance, VkDevice device) {
+            if (device != VK_NULL_HANDLE) {
+                return getDeviceProc(device, proc_name);
+            }
+            return getInstanceProc(instance, proc_name);
+        };
+        return Create(presentQueueIndex, canPresent, getProc);
+    }
 
     ~GrVkBackendContext() override;
 };
diff --git a/include/gpu/vk/GrVkDefines.h b/include/gpu/vk/GrVkDefines.h
index 9caf2d7..7defed2 100644
--- a/include/gpu/vk/GrVkDefines.h
+++ b/include/gpu/vk/GrVkDefines.h
@@ -9,18 +9,35 @@
 #ifndef GrVkDefines_DEFINED
 #define GrVkDefines_DEFINED
 
+#ifdef SK_VULKAN
+
 #if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_WIN32)
-#   define VK_USE_PLATFORM_WIN32_KHR
+#   if !defined(VK_USE_PLATFORM_WIN32_KHR)
+#      define VK_USE_PLATFORM_WIN32_KHR
+#   endif
 #elif defined(SK_BUILD_FOR_ANDROID)
-#   define VK_USE_PLATFORM_ANDROID_KHR
+#   if !defined(VK_USE_PLATFORM_ANDROID_KHR)
+#      define VK_USE_PLATFORM_ANDROID_KHR
+#   endif
 #elif defined(SK_BUILD_FOR_UNIX)
-#   define VK_USE_PLATFORM_XCB_KHR
+#   if defined(__Fuchsia__)
+#     if !defined(VK_USE_PLATFORM_MAGMA_KHR)
+#       define VK_USE_PLATFORM_MAGMA_KHR
+#     endif
+#   else
+#     if !defined(VK_USE_PLATFORM_XCB_KHR)
+#        define VK_USE_PLATFORM_XCB_KHR
+#     endif
+#   endif
 #endif
 
-#if defined(Bool) || defined(Status) || defined(True) || defined(False)
-#   pragma error "Macros unexpectedly defined."
 #endif
 
 #include <vulkan/vulkan.h>
 
+#define SKIA_REQUIRED_VULKAN_HEADER_VERSION 17
+#if VK_HEADER_VERSION < SKIA_REQUIRED_VULKAN_HEADER_VERSION
+#error "Vulkan header version is too low"
+#endif
+
 #endif
diff --git a/include/gpu/vk/GrVkInterface.h b/include/gpu/vk/GrVkInterface.h
index 9dc0333..05ce561 100644
--- a/include/gpu/vk/GrVkInterface.h
+++ b/include/gpu/vk/GrVkInterface.h
@@ -15,20 +15,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
- * The default interface is returned by GrVkCreateInterface. This function's
- * implementation is platform-specific.
- */
-
-struct GrVkInterface;
-
-/**
- * Creates a GrVkInterface.
- */
-const GrVkInterface* GrVkCreateInterface(VkInstance instance, VkDevice device,
-                                         uint32_t extensionFlags);
-
-
-/**
  * GrContext uses the following interface to make all calls into Vulkan. When a
  * GrContext is created it is given a GrVkInterface. All functions that should be
  * available based on the Vulkan's version must be non-NULL or GrContext creation
@@ -49,7 +35,28 @@
     typedef SkRefCnt INHERITED;
 
 public:
-    GrVkInterface();
+    using GetProc = std::function<PFN_vkVoidFunction(
+        const char*, // function name
+        VkInstance,  // instance or VK_NULL_HANDLE
+        VkDevice     // device or VK_NULL_HANDLE
+        )>;
+
+    // This is typically vkGetInstanceProcAddr.
+    using GetInstanceProc = std::function<PFN_vkVoidFunction(VkInstance, const char*)>;
+
+    // This is typically vkGetDeviceProcAddr.
+    using GetDeviceProc = std::function<PFN_vkVoidFunction(VkDevice, const char*)>;
+
+    GrVkInterface(GetProc getProc,
+                  VkInstance instance,
+                  VkDevice device,
+                  uint32_t extensionFlags);
+
+    GrVkInterface(const GetInstanceProc&,
+                  const GetDeviceProc&,
+                  VkInstance instance,
+                  VkDevice device,
+                  uint32_t extensionFlags);
 
     // Validates that the GrVkInterface supports its advertised standard. This means the necessary
     // function pointers have been initialized for Vulkan version.
diff --git a/include/gpu/vk/GrVkTypes.h b/include/gpu/vk/GrVkTypes.h
index c98a94a..aa1334a 100644
--- a/include/gpu/vk/GrVkTypes.h
+++ b/include/gpu/vk/GrVkTypes.h
@@ -9,7 +9,6 @@
 #ifndef GrVkTypes_DEFINED
 #define GrVkTypes_DEFINED
 
-#include "GrExternalTextureData.h"
 #include "GrTypes.h"
 #include "vk/GrVkDefines.h"
 
@@ -60,24 +59,6 @@
     void updateImageLayout(VkImageLayout layout) { fImageLayout = layout; }
 };
 
-class GrVkExternalTextureData : public GrExternalTextureData {
-public:
-    GrVkExternalTextureData(const GrVkImageInfo& info) : fInfo(info) {}
-    GrBackend getBackend() const override { return kVulkan_GrBackend; }
-
-protected:
-    GrBackendObject getBackendObject() const override {
-        return reinterpret_cast<GrBackendObject>(&fInfo);
-    }
-    void attachToContext(GrContext*) override {
-        // TODO: Implement this
-    }
-
-    GrVkImageInfo fInfo;
-
-    typedef GrExternalTextureData INHERITED;
-};
-
 GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrVkImageInfo*));
 
 #endif
diff --git a/include/ports/SkFontMgr.h b/include/ports/SkFontMgr.h
index b5879d3..b13e113 100644
--- a/include/ports/SkFontMgr.h
+++ b/include/ports/SkFontMgr.h
@@ -45,7 +45,9 @@
      *  The caller must call unref() on the returned object.
      *  Never returns NULL; will return an empty set if the name is not found.
      *
-     *  Passing |nullptr| as the parameter will return the default system font.
+     *  Passing nullptr as the parameter will return the default system family.
+     *  Note that most systems don't have a default system family, so passing nullptr will often
+     *  result in the empty set.
      *
      *  It is possible that this will return a style set not accessible from
      *  createStyleSet(int) due to hidden or auto-activated fonts.
diff --git a/include/private/GrAuditTrail.h b/include/private/GrAuditTrail.h
index f1ae494..05f6fc5 100644
--- a/include/private/GrAuditTrail.h
+++ b/include/private/GrAuditTrail.h
@@ -10,6 +10,7 @@
 
 #include "GrConfig.h"
 #include "GrGpuResource.h"
+#include "GrRenderTargetProxy.h"
 #include "SkRect.h"
 #include "SkString.h"
 #include "SkTArray.h"
@@ -80,7 +81,7 @@
         fCurrentStackTrace.push_back(SkString(framename));
     }
 
-    void addOp(const GrOp*, GrGpuResource::UniqueID renderTargetID);
+    void addOp(const GrOp*, GrRenderTargetProxy::UniqueID proxyID);
 
     void opsCombined(const GrOp* consumer, const GrOp* consumed);
 
@@ -103,14 +104,14 @@
     // We could just return our internal bookkeeping struct if copying the data out becomes
     // a performance issue, but until then its nice to decouple
     struct OpInfo {
-        SkRect fBounds;
-        // TODO: switch over to GrSurfaceProxy::UniqueID
-        GrGpuResource::UniqueID fRenderTargetUniqueID;
         struct Op {
-            int fClientID;
+            int    fClientID;
             SkRect fBounds;
         };
-        SkTArray<Op> fOps;
+
+        SkRect                   fBounds;
+        GrSurfaceProxy::UniqueID fProxyUniqueID;
+        SkTArray<Op>             fOps;
     };
 
     void getBoundsByClientID(SkTArray<OpInfo>* outInfo, int clientID);
@@ -136,11 +137,12 @@
     typedef SkTArray<Op*> Ops;
 
     struct OpNode {
-        OpNode(const GrGpuResource::UniqueID& id) : fRenderTargetUniqueID(id) {}
+        OpNode(const GrSurfaceProxy::UniqueID& proxyID) : fProxyUniqueID(proxyID) { }
         SkString toJson() const;
+
         SkRect                         fBounds;
-        Ops fChildren;
-        const GrGpuResource::UniqueID  fRenderTargetUniqueID;
+        Ops                            fChildren;
+        const GrSurfaceProxy::UniqueID fProxyUniqueID;
     };
     typedef SkTArray<std::unique_ptr<OpNode>, true> OpList;
 
@@ -172,8 +174,8 @@
 #define GR_AUDIT_TRAIL_RESET(audit_trail) \
     //GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, fullReset);
 
-#define GR_AUDIT_TRAIL_ADD_OP(audit_trail, op, rt_id) \
-    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addOp, op, rt_id);
+#define GR_AUDIT_TRAIL_ADD_OP(audit_trail, op, proxy_id) \
+    GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addOp, op, proxy_id);
 
 #define GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(audit_trail, combineWith, op) \
     GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, opsCombined, combineWith, op);
diff --git a/include/private/GrGLSL.h b/include/private/GrGLSL.h
index ed33c1c..26954b4 100644
--- a/include/private/GrGLSL.h
+++ b/include/private/GrGLSL.h
@@ -74,6 +74,8 @@
             return "mediump";
         case kHigh_GrSLPrecision:
             return "highp";
+        case kDefault_GrSLPrecision:
+            return "";
         default:
             SkFAIL("Unexpected precision type.");
             return "";
@@ -136,245 +138,4 @@
     return ""; // suppress warning
 }
 
-/** A generic base-class representing a GLSL expression.
- * The instance can be a variable name, expression or vecN(0) or vecN(1). Does simple constant
- * folding with help of 1 and 0.
- *
- * Clients should not use this class, rather the specific instantiations defined
- * later, for example GrGLSLExpr4.
- */
-template <typename Self>
-class GrGLSLExpr {
-public:
-    bool isOnes() const { return kOnes_ExprType == fType; }
-    bool isZeros() const { return kZeros_ExprType == fType; }
-
-    const char* c_str() const {
-        if (kZeros_ExprType == fType) {
-            return Self::ZerosStr();
-        } else if (kOnes_ExprType == fType) {
-            return Self::OnesStr();
-        }
-        SkASSERT(!fExpr.isEmpty()); // Empty expressions should not be used.
-        return fExpr.c_str();
-    }
-
-    bool isValid() const {
-        return kFullExpr_ExprType != fType || !fExpr.isEmpty();
-    }
-
-protected:
-    /** Constructs an invalid expression.
-     * Useful only as a return value from functions that never actually return
-     * this and instances that will be assigned to later. */
-    GrGLSLExpr()
-        : fType(kFullExpr_ExprType) {
-        // The only constructor that is allowed to build an empty expression.
-        SkASSERT(!this->isValid());
-    }
-
-    /** Constructs an expression with all components as value v */
-    explicit GrGLSLExpr(int v) {
-        if (v == 0) {
-            fType = kZeros_ExprType;
-        } else if (v == 1) {
-            fType = kOnes_ExprType;
-        } else {
-            fType = kFullExpr_ExprType;
-            fExpr.appendf(Self::CastIntStr(), v);
-        }
-    }
-
-    /** Constructs an expression from a string.
-     * Argument expr is a simple expression or a parenthesized expression. */
-    // TODO: make explicit once effects input Exprs.
-    GrGLSLExpr(const char expr[]) {
-        if (nullptr == expr) {  // TODO: remove this once effects input Exprs.
-            fType = kOnes_ExprType;
-        } else {
-            fType = kFullExpr_ExprType;
-            fExpr = expr;
-        }
-        SkASSERT(this->isValid());
-    }
-
-    /** Constructs an expression from a string.
-     * Argument expr is a simple expression or a parenthesized expression. */
-    // TODO: make explicit once effects input Exprs.
-    GrGLSLExpr(const SkString& expr) {
-        if (expr.isEmpty()) {  // TODO: remove this once effects input Exprs.
-            fType = kOnes_ExprType;
-        } else {
-            fType = kFullExpr_ExprType;
-            fExpr = expr;
-        }
-        SkASSERT(this->isValid());
-    }
-
-    /** Constructs an expression from a string with one substitution. */
-    GrGLSLExpr(const char format[], const char in0[])
-        : fType(kFullExpr_ExprType) {
-        fExpr.appendf(format, in0);
-    }
-
-    /** Constructs an expression from a string with two substitutions. */
-    GrGLSLExpr(const char format[], const char in0[], const char in1[])
-        : fType(kFullExpr_ExprType) {
-        fExpr.appendf(format, in0, in1);
-    }
-
-    /** Returns expression casted to another type.
-     * Generic implementation that is called for non-trivial cases of casts. */
-    template <typename T>
-    static Self VectorCastImpl(const T& other);
-
-    /** Returns a GLSL multiplication: component-wise or component-by-scalar.
-     * The multiplication will be component-wise or multiply each component by a scalar.
-     *
-     * The returned expression will compute the value of:
-     *    vecN(in0.x * in1.x, ...) if dim(T0) == dim(T1) (component-wise)
-     *    vecN(in0.x * in1, ...) if dim(T1) == 1 (vector by scalar)
-     *    vecN(in0 * in1.x, ...) if dim(T0) == 1 (scalar by vector)
-     */
-    template <typename T0, typename T1>
-    static Self Mul(T0 in0, T1 in1);
-
-    /** Returns a GLSL addition: component-wise or add a scalar to each component.
-     * Return value computes:
-     *   vecN(in0.x + in1.x, ...) or vecN(in0.x + in1, ...) or vecN(in0 + in1.x, ...).
-     */
-    template <typename T0, typename T1>
-    static Self Add(T0 in0, T1 in1);
-
-    /** Returns a GLSL subtraction: component-wise or subtract compoments by a scalar.
-     * Return value computes
-     *   vecN(in0.x - in1.x, ...) or vecN(in0.x - in1, ...) or vecN(in0 - in1.x, ...).
-     */
-    template <typename T0, typename T1>
-    static Self Sub(T0 in0, T1 in1);
-
-    /** Returns expression that accesses component(s) of the expression.
-     * format should be the form "%s.x" where 'x' is the component(s) to access.
-     * Caller is responsible for making sure the amount of components in the
-     * format string is equal to dim(T).
-     */
-    template <typename T>
-    T extractComponents(const char format[]) const;
-
-private:
-    enum ExprType {
-        kZeros_ExprType,
-        kOnes_ExprType,
-        kFullExpr_ExprType,
-    };
-    ExprType fType;
-    SkString fExpr;
-};
-
-class GrGLSLExpr1;
-class GrGLSLExpr4;
-
-/** Class representing a float GLSL expression. */
-class GrGLSLExpr1 : public GrGLSLExpr<GrGLSLExpr1> {
-public:
-    GrGLSLExpr1()
-        : INHERITED() {
-    }
-    explicit GrGLSLExpr1(int v)
-        : INHERITED(v) {
-    }
-    GrGLSLExpr1(const char* expr)
-        : INHERITED(expr) {
-    }
-    GrGLSLExpr1(const SkString& expr)
-        : INHERITED(expr) {
-    }
-
-    static GrGLSLExpr1 VectorCast(const GrGLSLExpr1& expr);
-
-private:
-    GrGLSLExpr1(const char format[], const char in0[])
-        : INHERITED(format, in0) {
-    }
-    GrGLSLExpr1(const char format[], const char in0[], const char in1[])
-        : INHERITED(format, in0, in1) {
-    }
-
-    static const char* ZerosStr();
-    static const char* OnesStr();
-    static const char* CastStr();
-    static const char* CastIntStr();
-
-    friend GrGLSLExpr1 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr1&in1);
-    friend GrGLSLExpr1 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr1&in1);
-    friend GrGLSLExpr1 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr1&in1);
-
-    friend class GrGLSLExpr<GrGLSLExpr1>;
-    friend class GrGLSLExpr<GrGLSLExpr4>;
-
-    typedef GrGLSLExpr<GrGLSLExpr1> INHERITED;
-};
-
-/** Class representing a float vector (vec4) GLSL expression. */
-class GrGLSLExpr4 : public GrGLSLExpr<GrGLSLExpr4> {
-public:
-    GrGLSLExpr4()
-        : INHERITED() {
-    }
-    explicit GrGLSLExpr4(int v)
-        : INHERITED(v) {
-    }
-    GrGLSLExpr4(const char* expr)
-        : INHERITED(expr) {
-    }
-    GrGLSLExpr4(const SkString& expr)
-        : INHERITED(expr) {
-    }
-
-    typedef GrGLSLExpr1 AExpr;
-    AExpr a() const;
-
-    /** GLSL vec4 cast / constructor, eg vec4(floatv) -> vec4(floatv, floatv, floatv, floatv) */
-    static GrGLSLExpr4 VectorCast(const GrGLSLExpr1& expr);
-    static GrGLSLExpr4 VectorCast(const GrGLSLExpr4& expr);
-
-private:
-    GrGLSLExpr4(const char format[], const char in0[])
-        : INHERITED(format, in0) {
-    }
-    GrGLSLExpr4(const char format[], const char in0[], const char in1[])
-        : INHERITED(format, in0, in1) {
-    }
-
-    static const char* ZerosStr();
-    static const char* OnesStr();
-    static const char* CastStr();
-    static const char* CastIntStr();
-
-    // The vector-by-scalar and scalar-by-vector binary operations.
-    friend GrGLSLExpr4 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr4&in1);
-    friend GrGLSLExpr4 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr4&in1);
-    friend GrGLSLExpr4 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr4&in1);
-    friend GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr1&in1);
-    friend GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr1&in1);
-    friend GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr1&in1);
-
-    // The vector-by-vector, i.e. component-wise, binary operations.
-    friend GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr4&in1);
-    friend GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr4&in1);
-    friend GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr4&in1);
-
-    friend class GrGLSLExpr<GrGLSLExpr4>;
-
-    typedef GrGLSLExpr<GrGLSLExpr4> INHERITED;
-};
-
-/**
- * Does an inplace mul, *=, of vec4VarName by mulFactor.
- * A semicolon is added after the assignment.
- */
-void GrGLSLMulVarBy4f(SkString* outAppend, const char* vec4VarName, const GrGLSLExpr4& mulFactor);
-
-#include "GrGLSL_impl.h"
-
 #endif
diff --git a/include/private/GrGLSL_impl.h b/include/private/GrGLSL_impl.h
deleted file mode 100644
index bdd69cc..0000000
--- a/include/private/GrGLSL_impl.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGLSL_impl_DEFINED
-#define GrGLSL_impl_DEFINED
-
-template<typename Self>
-template<typename T>
-inline Self GrGLSLExpr<Self>::VectorCastImpl(const T& expr) {
-    if (expr.isZeros()) {
-        return Self(0);
-    }
-    if (expr.isOnes()) {
-        return Self(1);
-    }
-    return Self(Self::CastStr(), expr.c_str());
-}
-
-template<typename Self>
-template<typename T0, typename T1>
-inline Self GrGLSLExpr<Self>::Mul(T0 in0, T1 in1) {
-    if (in0.isZeros() || in1.isZeros()) {
-        return Self(0);
-    }
-    if (in0.isOnes()) {
-        return Self::VectorCast(in1);
-    }
-    if (in1.isOnes()) {
-        return Self::VectorCast(in0);
-    }
-    return Self("(%s * %s)", in0.c_str(), in1.c_str());
-}
-
-template<typename Self>
-template<typename T0, typename T1>
-inline Self GrGLSLExpr<Self>::Add(T0 in0, T1 in1) {
-    if (in1.isZeros()) {
-        return Self::VectorCast(in0);
-    }
-    if (in0.isZeros()) {
-        return Self::VectorCast(in1);
-    }
-    if (in0.isOnes() && in1.isOnes()) {
-        return Self(2);
-    }
-    return Self("(%s + %s)", in0.c_str(), in1.c_str());
-}
-
-template<typename Self>
-template<typename T0, typename T1>
-inline Self GrGLSLExpr<Self>::Sub(T0 in0, T1 in1) {
-    if (in1.isZeros()) {
-        return Self::VectorCast(in0);
-    }
-    if (in1.isOnes()) {
-        if (in0.isOnes()) {
-            return Self(0);
-        }
-    }
-
-    return Self("(%s - %s)", in0.c_str(), in1.c_str());
-}
-
-template <typename Self>
-template <typename T>
-T GrGLSLExpr<Self>::extractComponents(const char format[]) const {
-    if (this->isZeros()) {
-        return T(0);
-    }
-    if (this->isOnes()) {
-        return T(1);
-    }
-    return T(format, this->c_str());
-}
-
-inline GrGLSLExpr1 GrGLSLExpr1::VectorCast(const GrGLSLExpr1& expr) {
-    return expr;
-}
-
-inline const char* GrGLSLExpr1::ZerosStr() {
-    return "0";
-}
-
-inline const char* GrGLSLExpr1::OnesStr() {
-    return "1.0";
-}
-
-// GrGLSLExpr1::CastStr() is unimplemented because using them is likely an
-// error. This is now caught compile-time.
-
-inline const char* GrGLSLExpr1::CastIntStr() {
-    return "%d";
-}
-
-inline GrGLSLExpr1 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr1& in1) {
-    return GrGLSLExpr1::Mul(in0, in1);
-}
-
-inline GrGLSLExpr1 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr1& in1) {
-    return GrGLSLExpr1::Add(in0, in1);
-}
-
-inline GrGLSLExpr1 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr1& in1) {
-    return GrGLSLExpr1::Sub(in0, in1);
-}
-
-inline const char* GrGLSLExpr4::ZerosStr() {
-    return "vec4(0)";
-}
-
-inline const char* GrGLSLExpr4::OnesStr() {
-    return "vec4(1)";
-}
-
-inline const char* GrGLSLExpr4::CastStr() {
-    return "vec4(%s)";
-}
-
-inline const char* GrGLSLExpr4::CastIntStr() {
-    return "vec4(%d)";
-}
-
-inline GrGLSLExpr4 GrGLSLExpr4::VectorCast(const GrGLSLExpr1& expr) {
-    return INHERITED::VectorCastImpl(expr);
-}
-
-inline GrGLSLExpr4 GrGLSLExpr4::VectorCast(const GrGLSLExpr4& expr) {
-    return expr;
-}
-
-inline GrGLSLExpr4::AExpr GrGLSLExpr4::a() const {
-    return this->extractComponents<GrGLSLExpr4::AExpr>("%s.a");
-}
-
-inline GrGLSLExpr4 operator*(const GrGLSLExpr1& in0, const GrGLSLExpr4& in1) {
-    return GrGLSLExpr4::Mul(in0, in1);
-}
-
-inline GrGLSLExpr4 operator+(const GrGLSLExpr1& in0, const GrGLSLExpr4& in1) {
-    return GrGLSLExpr4::Add(in0, in1);
-}
-
-inline GrGLSLExpr4 operator-(const GrGLSLExpr1& in0, const GrGLSLExpr4& in1) {
-    return GrGLSLExpr4::Sub(in0, in1);
-}
-
-inline GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr1& in1) {
-    return GrGLSLExpr4::Mul(in0, in1);
-}
-
-inline GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr1& in1) {
-    return GrGLSLExpr4::Add(in0, in1);
-}
-
-inline GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr1& in1) {
-    return GrGLSLExpr4::Sub(in0, in1);
-}
-
-inline GrGLSLExpr4 operator*(const GrGLSLExpr4& in0, const GrGLSLExpr4& in1) {
-    return GrGLSLExpr4::Mul(in0, in1);
-}
-
-inline GrGLSLExpr4 operator+(const GrGLSLExpr4& in0, const GrGLSLExpr4& in1) {
-    return GrGLSLExpr4::Add(in0, in1);
-}
-
-inline GrGLSLExpr4 operator-(const GrGLSLExpr4& in0, const GrGLSLExpr4& in1) {
-    return GrGLSLExpr4::Sub(in0, in1);
-}
-
-#endif
diff --git a/include/private/GrInstancedPipelineInfo.h b/include/private/GrInstancedPipelineInfo.h
index 196c35b..b89f863 100644
--- a/include/private/GrInstancedPipelineInfo.h
+++ b/include/private/GrInstancedPipelineInfo.h
@@ -16,9 +16,9 @@
  */
 struct GrInstancedPipelineInfo {
     GrInstancedPipelineInfo(const GrRenderTargetProxy* rtp)
-        : fIsMultisampled(rtp->isStencilBufferMultisampled())
-        , fIsMixedSampled(rtp->isMixedSampled())
-        , fIsRenderingToFloat(GrPixelConfigIsFloatingPoint(rtp->desc().fConfig)) {}
+            : fIsMultisampled(GrFSAAType::kNone != rtp->fsaaType())
+            , fIsMixedSampled(GrFSAAType::kMixedSamples == rtp->fsaaType())
+            , fIsRenderingToFloat(GrPixelConfigIsFloatingPoint(rtp->config())) {}
 
     bool canUseCoverageAA() const { return !fIsMultisampled || fIsMixedSampled; }
 
diff --git a/include/private/GrRenderTargetProxy.h b/include/private/GrRenderTargetProxy.h
index adc7553..31c3868 100644
--- a/include/private/GrRenderTargetProxy.h
+++ b/include/private/GrRenderTargetProxy.h
@@ -24,30 +24,33 @@
     const GrRenderTargetProxy* asRenderTargetProxy() const override { return this; }
 
     // Actually instantiate the backing rendertarget, if necessary.
-    GrRenderTarget* instantiate(GrResourceProvider* resourceProvider);
+    bool instantiate(GrResourceProvider* resourceProvider) override;
 
-    bool isStencilBufferMultisampled() const { return fDesc.fSampleCnt > 0; }
-
-    /**
-     * For our purposes, "Mixed Sampled" means the stencil buffer is multisampled but the color
-     * buffer is not.
-     */
-    bool isMixedSampled() const { return fRenderTargetFlags & GrRenderTarget::Flags::kMixedSampled; }
-
-    /**
-     * "Unified Sampled" means the stencil and color buffers are both multisampled.
-     */
-    bool isUnifiedMultisampled() const { return fDesc.fSampleCnt > 0 && !this->isMixedSampled(); }
+    GrFSAAType fsaaType() const {
+        if (!fSampleCnt) {
+            SkASSERT(!(fRenderTargetFlags & GrRenderTarget::Flags::kMixedSampled));
+            return GrFSAAType::kNone;
+        }
+        return (fRenderTargetFlags & GrRenderTarget::Flags::kMixedSampled)
+                       ? GrFSAAType::kMixedSamples
+                       : GrFSAAType::kUnifiedMSAA;
+    }
 
     /**
      * Returns the number of samples/pixel in the stencil buffer (Zero if non-MSAA).
      */
-    int numStencilSamples() const { return fDesc.fSampleCnt; }
+    int numStencilSamples() const { return fSampleCnt; }
 
     /**
      * Returns the number of samples/pixel in the color buffer (Zero if non-MSAA or mixed sampled).
      */
-    int numColorSamples() const { return this->isMixedSampled() ? 0 : fDesc.fSampleCnt; }
+    int numColorSamples() const {
+        return GrFSAAType::kMixedSamples == this->fsaaType() ? 0 : fSampleCnt;
+    }
+
+    int worstCaseWidth() const;
+
+    int worstCaseHeight() const;
 
     int maxWindowRectangles(const GrCaps& caps) const;
 
@@ -67,8 +70,9 @@
     GrRenderTargetProxy(sk_sp<GrSurface>);
 
 private:
-    size_t onGpuMemorySize() const override;
+    size_t onUninstantiatedGpuMemorySize() const override;
 
+    int fSampleCnt;
     // For wrapped render targets the actual GrRenderTarget is stored in the GrIORefProxy class.
     // For deferred proxies that pointer is filled in when we need to instantiate the
     // deferred resource.
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 0443227..bded2c1 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -13,7 +13,9 @@
 
 #include "SkRect.h"
 
+class GrBackendTexture;
 class GrCaps;
+class GrOpList;
 class GrRenderTargetOpList;
 class GrRenderTargetProxy;
 class GrResourceProvider;
@@ -22,6 +24,8 @@
 class GrTextureOpList;
 class GrTextureProxy;
 
+//#define SK_DISABLE_DEFERRED_PROXIES 1
+
 // This class replicates the functionality GrIORef<GrSurface> but tracks the
 // utilitization for later resource allocation (for the deferred case) and
 // forwards on the utilization in the wrapped case
@@ -43,27 +47,24 @@
             fTarget->unref();
         }
 
-        if (!(--fRefCnt)) {
-            delete this;
-            return;
-        }
-
-        this->validate();
+        --fRefCnt;
+        this->didRemoveRefOrPendingIO();
     }
 
     void validate() const {
-#ifdef SK_DEBUG    
-        SkASSERT(fRefCnt >= 1);
+#ifdef SK_DEBUG
+        SkASSERT(fRefCnt >= 0);
         SkASSERT(fPendingReads >= 0);
         SkASSERT(fPendingWrites >= 0);
         SkASSERT(fRefCnt + fPendingReads + fPendingWrites >= 1);
 
         if (fTarget) {
-            SkASSERT(!fPendingReads && !fPendingWrites);
             // The backing GrSurface can have more refs than the proxy if the proxy
             // started off wrapping an external resource (that came in with refs).
             // The GrSurface should never have fewer refs than the proxy however.
             SkASSERT(fTarget->fRefCnt >= fRefCnt);
+            SkASSERT(fTarget->fPendingReads >= fPendingReads);
+            SkASSERT(fTarget->fPendingWrites >= fPendingWrites);
         }
 #endif
     }
@@ -94,9 +95,6 @@
         fTarget->fRefCnt += (fRefCnt-1); // don't xfer the proxy's creation ref
         fTarget->fPendingReads += fPendingReads;
         fTarget->fPendingWrites += fPendingWrites;
-
-        fPendingReads = 0;
-        fPendingWrites = 0;
     }
 
     bool internalHasPendingIO() const {
@@ -107,24 +105,30 @@
         return SkToBool(fPendingWrites | fPendingReads);
     }
 
+    bool internalHasPendingWrite() const {
+        if (fTarget) {
+            return fTarget->internalHasPendingWrite();
+        }
+
+        return SkToBool(fPendingWrites);
+    }
+
     // For deferred proxies this will be null. For wrapped proxies it will point to the
     // wrapped resource.
     GrSurface* fTarget;
 
 private:
     // This class is used to manage conversion of refs to pending reads/writes.
-    friend class GrGpuResourceRef;
+    friend class GrSurfaceProxyRef;
     template <typename, GrIOType> friend class GrPendingIOResource;
 
     void addPendingRead() const {
         this->validate();
 
+        ++fPendingReads;
         if (fTarget) {
             fTarget->addPendingRead();
-            return;
         }
-
-        ++fPendingReads;
     }
 
     void completedRead() const {
@@ -132,21 +136,19 @@
 
         if (fTarget) {
             fTarget->completedRead();
-            return;
         }
-    
-        SkFAIL("How was the read completed if the Proxy hasn't been instantiated?");
+
+        --fPendingReads;
+        this->didRemoveRefOrPendingIO();
     }
 
     void addPendingWrite() const {
         this->validate();
 
+        ++fPendingWrites;
         if (fTarget) {
             fTarget->addPendingWrite();
-            return;
         }
-
-        ++fPendingWrites;
     }
 
     void completedWrite() const {
@@ -154,10 +156,16 @@
 
         if (fTarget) {
             fTarget->completedWrite();
-            return;
         }
-    
-        SkFAIL("How was the write completed if the Proxy hasn't been instantiated?");
+
+        --fPendingWrites;
+        this->didRemoveRefOrPendingIO();
+    }
+
+    void didRemoveRefOrPendingIO() const {
+        if (0 == fPendingReads && 0 == fPendingWrites && 0 == fRefCnt) {
+            delete this;
+        }
     }
 
     mutable int32_t fRefCnt;
@@ -180,21 +188,22 @@
                                               const GrSurfaceDesc&, SkBudgeted,
                                               const void* srcData, size_t rowBytes);
 
-    static sk_sp<GrSurfaceProxy> MakeWrappedBackend(GrContext*, GrBackendTextureDesc&);
-
-    const GrSurfaceDesc& desc() const { return fDesc; }
+    static sk_sp<GrTextureProxy> MakeWrappedBackend(GrContext*, GrBackendTexture&, GrSurfaceOrigin);
 
     GrSurfaceOrigin origin() const {
-        SkASSERT(kTopLeft_GrSurfaceOrigin == fDesc.fOrigin ||
-                 kBottomLeft_GrSurfaceOrigin == fDesc.fOrigin);
-        return fDesc.fOrigin;
+        SkASSERT(kTopLeft_GrSurfaceOrigin == fOrigin || kBottomLeft_GrSurfaceOrigin == fOrigin);
+        return fOrigin;
     }
-    int width() const { return fDesc.fWidth; }
-    int height() const { return fDesc.fHeight; }
-    GrPixelConfig config() const { return fDesc.fConfig; }
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    GrPixelConfig config() const { return fConfig; }
 
     class UniqueID {
     public:
+        static UniqueID InvalidID() {
+            return UniqueID(uint32_t(SK_InvalidUniqueID));
+        }
+
         // wrapped
         explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { }
         // deferred
@@ -209,10 +218,13 @@
             return !(*this == other);
         }
 
+        void makeInvalid() { fID = SK_InvalidUniqueID; }
         bool isInvalid() const { return SK_InvalidUniqueID == fID; }
 
     private:
-        const uint32_t fID;
+        explicit UniqueID(uint32_t id) : fID(id) {}
+
+        uint32_t fID;
     };
 
     /*
@@ -232,16 +244,13 @@
      */
     UniqueID uniqueID() const { return fUniqueID; }
 
-    GrSurface* instantiate(GrResourceProvider* resourceProvider);
+    virtual bool instantiate(GrResourceProvider* resourceProvider) = 0;
 
     /**
      * Helper that gets the width and height of the surface as a bounding rectangle.
      */
     SkRect getBoundsRect() const { return SkRect::MakeIWH(this->width(), this->height()); }
 
-    int worstCaseWidth(const GrCaps& caps) const;
-    int worstCaseHeight(const GrCaps& caps) const;
-
     /**
      * @return the texture proxy associated with the surface proxy, may be NULL.
      */
@@ -266,15 +275,18 @@
     GrTextureOpList* getLastTextureOpList();
 
     /**
-     * Retrieves the amount of GPU memory that will be or currently is used by this resource 
+     * Retrieves the amount of GPU memory that will be or currently is used by this resource
      * in bytes. It is approximate since we aren't aware of additional padding or copies made
      * by the driver.
      *
      * @return the amount of GPU memory used in bytes
      */
     size_t gpuMemorySize() const {
+        if (fTarget) {
+            return fTarget->gpuMemorySize();
+        }
         if (kInvalidGpuMemorySize == fGpuMemorySize) {
-            fGpuMemorySize = this->onGpuMemorySize();
+            fGpuMemorySize = this->onUninstantiatedGpuMemorySize();
             SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize);
         }
         return fGpuMemorySize;
@@ -282,7 +294,7 @@
 
     // Helper function that creates a temporary SurfaceContext to perform the copy
     // It always returns a kExact-backed proxy bc it is used when converting an SkSpecialImage
-    // to an SkImage.
+    // to an SkImage. The copy is is not a render target and not multisampled.
     static sk_sp<GrTextureProxy> Copy(GrContext*, GrSurfaceProxy* src,
                                       SkIRect srcRect, SkBudgeted);
 
@@ -306,14 +318,16 @@
 protected:
     // Deferred version
     GrSurfaceProxy(const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted, uint32_t flags)
-        : fDesc(desc)
-        , fFit(fit)
-        , fBudgeted(budgeted)
-        , fFlags(flags)
-        // fMipColorMode is only valid for texturable proxies
-        , fMipColorMode(SkDestinationSurfaceColorMode::kLegacy)
-        , fGpuMemorySize(kInvalidGpuMemorySize)
-        , fLastOpList(nullptr) {
+            : fConfig(desc.fConfig)
+            , fWidth(desc.fWidth)
+            , fHeight(desc.fHeight)
+            , fOrigin(desc.fOrigin)
+            , fFit(fit)
+            , fBudgeted(budgeted)
+            , fFlags(flags)
+            , fNeedsClear(SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag))
+            , fGpuMemorySize(kInvalidGpuMemorySize)
+            , fLastOpList(nullptr) {
         // Note: this ctor pulls a new uniqueID from the same pool at the GrGpuResources
     }
 
@@ -329,22 +343,34 @@
         return this->internalHasPendingIO();
     }
 
-    // For wrapped resources, 'fDesc' will always be filled in from the wrapped resource.
-    GrSurfaceDesc        fDesc;
+    bool hasPendingWrite() const {
+        return this->internalHasPendingWrite();
+    }
+
+    bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
+                         GrSurfaceFlags flags, bool isMipMapped,
+                         SkDestinationSurfaceColorMode mipColorMode);
+
+    // For wrapped resources, 'fConfig', 'fWidth', 'fHeight', and 'fOrigin; will always be filled in
+    // from the wrapped resource.
+    GrPixelConfig        fConfig;
+    int                  fWidth;
+    int                  fHeight;
+    GrSurfaceOrigin      fOrigin;
     SkBackingFit         fFit;      // always exact for wrapped resources
     mutable SkBudgeted   fBudgeted; // set from the backing resource for wrapped resources
                                     // mutable bc of SkSurface/SkImage wishy-washiness
     const uint32_t       fFlags;
 
-    SkDestinationSurfaceColorMode fMipColorMode;
-
     const UniqueID       fUniqueID; // set from the backing resource for wrapped resources
 
     static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
     SkDEBUGCODE(size_t getRawGpuMemorySize_debugOnly() const { return fGpuMemorySize; })
 
 private:
-    virtual size_t onGpuMemorySize() const = 0;
+    virtual size_t onUninstantiatedGpuMemorySize() const = 0;
+
+    bool                 fNeedsClear;
 
     // This entry is lazily evaluated so, when the proxy wraps a resource, the resource
     // will be called but, when the proxy is deferred, it will compute the answer itself.
@@ -353,14 +379,14 @@
     mutable size_t      fGpuMemorySize;
 
     // The last opList that wrote to or is currently going to write to this surface
-    // The opList can be closed (e.g., no render target context is currently bound
-    // to this renderTarget).
+    // The opList can be closed (e.g., no surface context is currently bound
+    // to this proxy).
     // This back-pointer is required so that we can add a dependancy between
     // the opList used to create the current contents of this surface
     // and the opList of a destination surface to which this one is being drawn or copied.
+    // This pointer is unreffed. OpLists own a ref on their surface proxies.
     GrOpList* fLastOpList;
 
-
     typedef GrIORefProxy INHERITED;
 };
 
diff --git a/include/private/GrTextureProxy.h b/include/private/GrTextureProxy.h
index ee954dc..dcc3afc 100644
--- a/include/private/GrTextureProxy.h
+++ b/include/private/GrTextureProxy.h
@@ -22,10 +22,22 @@
     const GrTextureProxy* asTextureProxy() const override { return this; }
 
     // Actually instantiate the backing texture, if necessary
-    GrTexture* instantiate(GrResourceProvider*);
+    bool instantiate(GrResourceProvider*) override;
 
     void setMipColorMode(SkDestinationSurfaceColorMode colorMode);
 
+    GrSamplerParams::FilterMode highestFilterMode() const;
+
+    GrSLType imageStorageType() const {
+        if (GrPixelConfigIsSint(this->config())) {
+            return kIImageStorage2D_GrSLType;
+        } else {
+            return kImageStorage2D_GrSLType;
+        }
+    }
+
+    bool isMipMapped() const { return fIsMipMapped; }
+
 protected:
     friend class GrSurfaceProxy; // for ctors
 
@@ -35,8 +47,13 @@
     // Wrapped version
     GrTextureProxy(sk_sp<GrSurface>);
 
+    SkDestinationSurfaceColorMode mipColorMode() const { return fMipColorMode;  }
+
 private:
-    size_t onGpuMemorySize() const override;
+    bool fIsMipMapped;
+    SkDestinationSurfaceColorMode fMipColorMode;
+
+    size_t onUninstantiatedGpuMemorySize() const override;
 
     // For wrapped proxies the GrTexture pointer is stored in GrIORefProxy.
     // For deferred proxies that pointer will be filled in when we need to instantiate
diff --git a/include/private/GrTextureRenderTargetProxy.h b/include/private/GrTextureRenderTargetProxy.h
deleted file mode 100644
index 09aef75..0000000
--- a/include/private/GrTextureRenderTargetProxy.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrTextureRenderTargetProxy_DEFINED
-#define GrTextureRenderTargetProxy_DEFINED
-
-#include "GrRenderTargetProxy.h"
-#include "GrTextureProxy.h"
-
-#ifdef SK_BUILD_FOR_WIN
-// Windows gives warnings about inheriting asTextureProxy/asRenderTargetProxy via dominance.
-#pragma warning(push)
-#pragma warning(disable: 4250)
-#endif
-
-// This class delays the acquisition of RenderTargets that are also textures until
-// they are actually required
-// Beware: the uniqueID of the TextureRenderTargetProxy will usually be different than
-// the uniqueID of the RenderTarget/Texture it represents!
-class GrTextureRenderTargetProxy : public GrTextureProxy, public GrRenderTargetProxy {
-private:
-    friend class GrSurfaceProxy; // for ctors
-
-    // Deferred version
-    GrTextureRenderTargetProxy(const GrCaps&, const GrSurfaceDesc&,
-                               SkBackingFit, SkBudgeted, uint32_t flags);
-
-    // Wrapped version
-    GrTextureRenderTargetProxy(sk_sp<GrSurface>);
-
-    size_t onGpuMemorySize() const override;
-};
-
-#ifdef SK_BUILD_FOR_WIN
-#pragma warning(pop)
-#endif
-
-#endif
diff --git a/include/private/SkFixed.h b/include/private/SkFixed.h
index b2eea5f..6361bda 100644
--- a/include/private/SkFixed.h
+++ b/include/private/SkFixed.h
@@ -102,19 +102,9 @@
     */
     SK_ALWAYS_INLINE SkFixed SkFloatToFixed_arm(float x)
     {
-        int32_t y, z;
-        asm("movs    %1, %3, lsl #1         \n"
-            "mov     %2, #0x8E              \n"
-            "sub     %1, %2, %1, lsr #24    \n"
-            "mov     %2, %3, lsl #8         \n"
-            "orr     %2, %2, #0x80000000    \n"
-            "mov     %1, %2, lsr %1         \n"
-            "it cs                          \n"
-            "rsbcs   %1, %1, #0             \n"
-            : "=r"(x), "=&r"(y), "=&r"(z)
-            : "r"(x)
-            : "cc"
-            );
+        int32_t y;
+        asm("vcvt.s32.f32 %0, %0, #16": "+w"(x));
+        memcpy(&y, &x, sizeof(y));
         return y;
     }
     inline SkFixed SkFixedMul_arm(SkFixed x, SkFixed y)
diff --git a/include/private/SkMiniRecorder.h b/include/private/SkMiniRecorder.h
deleted file mode 100644
index 06b35ca..0000000
--- a/include/private/SkMiniRecorder.h
+++ /dev/null
@@ -1,56 +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 SkMiniRecorder_DEFINED
-#define SkMiniRecorder_DEFINED
-
-#include "SkRecords.h"
-#include "SkScalar.h"
-#include "SkTypes.h"
-class SkCanvas;
-
-// Records small pictures, but only a limited subset of the canvas API, and may fail.
-class SkMiniRecorder : SkNoncopyable {
-public:
-    SkMiniRecorder();
-    ~SkMiniRecorder();
-
-    // Try to record an op.  Returns false on failure.
-    bool drawPath(const SkPath&, const SkPaint&);
-    bool drawRect(const SkRect&, const SkPaint&);
-    bool drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint&);
-
-    // Detach anything we've recorded as a picture, resetting this SkMiniRecorder.
-    sk_sp<SkPicture> detachAsPicture(const SkRect& cull);
-
-    // Flush anything we've recorded to the canvas, resetting this SkMiniRecorder.
-    // This is logically the same as but rather more efficient than:
-    //    sk_sp<SkPicture> pic(this->detachAsPicture(SkRect::MakeEmpty()));
-    //    pic->playback(canvas);
-    void flushAndReset(SkCanvas*);
-
-private:
-    enum class State {
-        kEmpty,
-        kDrawPath,
-        kDrawRect,
-        kDrawTextBlob,
-    };
-
-    State fState;
-
-    template <size_t A, size_t B>
-    struct Max { static const size_t val = A > B ? A : B; };
-
-    static const size_t kInlineStorage =
-        Max<sizeof(SkRecords::DrawPath),
-        Max<sizeof(SkRecords::DrawRect),
-            sizeof(SkRecords::DrawTextBlob)>::val>::val;
-    SkAlignedSStorage<kInlineStorage> fBuffer;
-};
-
-#endif//SkMiniRecorder_DEFINED
diff --git a/include/private/SkRecords.h b/include/private/SkRecords.h
deleted file mode 100644
index ac492ec..0000000
--- a/include/private/SkRecords.h
+++ /dev/null
@@ -1,359 +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 SkRecords_DEFINED
-#define SkRecords_DEFINED
-
-#include "SkData.h"
-#include "SkCanvas.h"
-#include "SkDrawable.h"
-#include "SkImageFilter.h"
-#include "SkMatrix.h"
-#include "SkPath.h"
-#include "SkPicture.h"
-#include "SkRect.h"
-#include "SkRegion.h"
-#include "SkRRect.h"
-#include "SkRSXform.h"
-#include "SkString.h"
-#include "SkTextBlob.h"
-#include "SkVertices.h"
-
-// Windows.h, will pull in all of the GDI defines.  GDI #defines
-// DrawText to DrawTextA or DrawTextW, but SkRecord has a struct
-// called DrawText. Since this file does not use GDI, undefing
-// DrawText makes things less confusing.
-#ifdef DrawText
-#undef DrawText
-#endif
-
-namespace SkRecords {
-
-// A list of all the types of canvas calls we can record.
-// Each of these is reified into a struct below.
-//
-// (We're using the macro-of-macro trick here to do several different things with the same list.)
-//
-// We leave this SK_RECORD_TYPES macro defined for use by code that wants to operate on SkRecords
-// types polymorphically.  (See SkRecord::Record::{visit,mutate} for an example.)
-//
-// Order doesn't technically matter here, but the compiler can generally generate better code if
-// you keep them semantically grouped, especially the Draws.  It's also nice to leave NoOp at 0.
-#define SK_RECORD_TYPES(M)                                          \
-    M(NoOp)                                                         \
-    M(Restore)                                                      \
-    M(Save)                                                         \
-    M(SaveLayer)                                                    \
-    M(SetMatrix)                                                    \
-    M(Translate)                                                    \
-    M(TranslateZ)                                                   \
-    M(Concat)                                                       \
-    M(ClipPath)                                                     \
-    M(ClipRRect)                                                    \
-    M(ClipRect)                                                     \
-    M(ClipRegion)                                                   \
-    M(DrawArc)                                                      \
-    M(DrawDrawable)                                                 \
-    M(DrawImage)                                                    \
-    M(DrawImageLattice)                                             \
-    M(DrawImageRect)                                                \
-    M(DrawImageNine)                                                \
-    M(DrawDRRect)                                                   \
-    M(DrawOval)                                                     \
-    M(DrawPaint)                                                    \
-    M(DrawPath)                                                     \
-    M(DrawPatch)                                                    \
-    M(DrawPicture)                                                  \
-    M(DrawShadowedPicture)                                          \
-    M(DrawPoints)                                                   \
-    M(DrawPosText)                                                  \
-    M(DrawPosTextH)                                                 \
-    M(DrawText)                                                     \
-    M(DrawTextOnPath)                                               \
-    M(DrawTextRSXform)                                              \
-    M(DrawRRect)                                                    \
-    M(DrawRect)                                                     \
-    M(DrawRegion)                                                   \
-    M(DrawTextBlob)                                                 \
-    M(DrawAtlas)                                                    \
-    M(DrawVertices)                                                 \
-    M(DrawAnnotation)
-
-// Defines SkRecords::Type, an enum of all record types.
-#define ENUM(T) T##_Type,
-enum Type { SK_RECORD_TYPES(ENUM) };
-#undef ENUM
-
-#define ACT_AS_PTR(ptr)                 \
-    operator T*() const { return ptr; } \
-    T* operator->() const { return ptr; }
-
-// An Optional doesn't own the pointer's memory, but may need to destroy non-POD data.
-template <typename T>
-class Optional : SkNoncopyable {
-public:
-    Optional() : fPtr(nullptr) {}
-    Optional(T* ptr) : fPtr(ptr) {}
-    Optional(Optional&& o) : fPtr(o.fPtr) {
-        o.fPtr = nullptr;
-    }
-    ~Optional() { if (fPtr) fPtr->~T(); }
-
-    ACT_AS_PTR(fPtr)
-private:
-    T* fPtr;
-};
-
-// Like Optional, but ptr must not be NULL.
-template <typename T>
-class Adopted : SkNoncopyable {
-public:
-    Adopted(T* ptr) : fPtr(ptr) { SkASSERT(fPtr); }
-    Adopted(Adopted* source) {
-        // Transfer ownership from source to this.
-        fPtr = source->fPtr;
-        source->fPtr = NULL;
-    }
-    ~Adopted() { if (fPtr) fPtr->~T(); }
-
-    ACT_AS_PTR(fPtr)
-private:
-    T* fPtr;
-};
-
-// PODArray doesn't own the pointer's memory, and we assume the data is POD.
-template <typename T>
-class PODArray {
-public:
-    PODArray() {}
-    PODArray(T* ptr) : fPtr(ptr) {}
-    // Default copy and assign.
-
-    ACT_AS_PTR(fPtr)
-private:
-    T* fPtr;
-};
-
-#undef ACT_AS_PTR
-
-// SkPath::getBounds() isn't thread safe unless we precache the bounds in a singlethreaded context.
-// SkPath::cheapComputeDirection() is similar.
-// Recording is a convenient time to cache these, or we can delay it to between record and playback.
-struct PreCachedPath : public SkPath {
-    PreCachedPath() {}
-    PreCachedPath(const SkPath& path);
-};
-
-// Like SkPath::getBounds(), SkMatrix::getType() isn't thread safe unless we precache it.
-// This may not cover all SkMatrices used by the picture (e.g. some could be hiding in a shader).
-struct TypedMatrix : public SkMatrix {
-    TypedMatrix() {}
-    TypedMatrix(const SkMatrix& matrix);
-};
-
-enum Tags {
-    kDraw_Tag      = 1,   // May draw something (usually named DrawFoo).
-    kHasImage_Tag  = 2,   // Contains an SkImage or SkBitmap.
-    kHasText_Tag   = 4,   // Contains text.
-    kHasPaint_Tag  = 8,   // May have an SkPaint field, at least optionally.
-};
-
-// A macro to make it a little easier to define a struct that can be stored in SkRecord.
-#define RECORD(T, tags, ...)            \
-struct T {                              \
-    static const Type kType = T##_Type; \
-    static const int kTags = tags;      \
-    __VA_ARGS__;                        \
-};
-
-RECORD(NoOp, 0);
-RECORD(Restore, 0,
-        SkIRect devBounds;
-        TypedMatrix matrix);
-RECORD(Save, 0);
-
-RECORD(SaveLayer, kHasPaint_Tag,
-       Optional<SkRect> bounds;
-       Optional<SkPaint> paint;
-       sk_sp<const SkImageFilter> backdrop;
-       SkCanvas::SaveLayerFlags saveLayerFlags);
-
-RECORD(SetMatrix, 0,
-        TypedMatrix matrix);
-RECORD(Concat, 0,
-        TypedMatrix matrix);
-
-RECORD(Translate, 0,
-        SkScalar dx;
-        SkScalar dy);
-RECORD(TranslateZ, 0, SkScalar z);
-
-struct ClipOpAndAA {
-    ClipOpAndAA() {}
-    ClipOpAndAA(SkClipOp op, bool aa) : fOp(static_cast<unsigned>(op)), fAA(aa) {}
-
-    SkClipOp op() const { return static_cast<SkClipOp>(fOp); }
-    bool aa() const { return fAA != 0; }
-
-private:
-    unsigned fOp : 31;  // This really only needs to be 3, but there's no win today to do so.
-    unsigned fAA :  1;  // MSVC won't pack an enum with an bool, so we call this an unsigned.
-};
-static_assert(sizeof(ClipOpAndAA) == 4, "ClipOpAndAASize");
-
-RECORD(ClipPath, 0,
-        SkIRect devBounds;
-        PreCachedPath path;
-        ClipOpAndAA opAA);
-RECORD(ClipRRect, 0,
-        SkIRect devBounds;
-        SkRRect rrect;
-        ClipOpAndAA opAA);
-RECORD(ClipRect, 0,
-        SkIRect devBounds;
-        SkRect rect;
-        ClipOpAndAA opAA);
-RECORD(ClipRegion, 0,
-        SkIRect devBounds;
-        SkRegion region;
-        SkClipOp op);
-
-// While not strictly required, if you have an SkPaint, it's fastest to put it first.
-RECORD(DrawArc, kDraw_Tag|kHasPaint_Tag,
-       SkPaint paint;
-       SkRect oval;
-       SkScalar startAngle;
-       SkScalar sweepAngle;
-       unsigned useCenter);
-RECORD(DrawDRRect, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        SkRRect outer;
-        SkRRect inner);
-RECORD(DrawDrawable, kDraw_Tag,
-        Optional<SkMatrix> matrix;
-        SkRect worstCaseBounds;
-        int32_t index);
-RECORD(DrawImage, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkImage> image;
-        SkScalar left;
-        SkScalar top);
-RECORD(DrawImageLattice, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkImage> image;
-        int xCount;
-        PODArray<int> xDivs;
-        int yCount;
-        PODArray<int> yDivs;
-        int flagCount;
-        PODArray<SkCanvas::Lattice::Flags> flags;
-        SkIRect src;
-        SkRect dst);
-RECORD(DrawImageRect, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkImage> image;
-        Optional<SkRect> src;
-        SkRect dst;
-        SkCanvas::SrcRectConstraint constraint);
-RECORD(DrawImageNine, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkImage> image;
-        SkIRect center;
-        SkRect dst);
-RECORD(DrawOval, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        SkRect oval);
-RECORD(DrawPaint, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint);
-RECORD(DrawPath, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PreCachedPath path);
-RECORD(DrawPicture, kDraw_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkPicture> picture;
-        TypedMatrix matrix);
-RECORD(DrawShadowedPicture, kDraw_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkPicture> picture;
-        TypedMatrix matrix;
-        const SkShadowParams& params);
-RECORD(DrawPoints, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        SkCanvas::PointMode mode;
-        unsigned count;
-        SkPoint* pts);
-RECORD(DrawPosText, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PODArray<char> text;
-        size_t byteLength;
-        PODArray<SkPoint> pos);
-RECORD(DrawPosTextH, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PODArray<char> text;
-        unsigned byteLength;
-        SkScalar y;
-        PODArray<SkScalar> xpos);
-RECORD(DrawRRect, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        SkRRect rrect);
-RECORD(DrawRect, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        SkRect rect);
-RECORD(DrawRegion, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        SkRegion region);
-RECORD(DrawText, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PODArray<char> text;
-        size_t byteLength;
-        SkScalar x;
-        SkScalar y);
-RECORD(DrawTextBlob, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        sk_sp<const SkTextBlob> blob;
-        SkScalar x;
-        SkScalar y);
-RECORD(DrawTextOnPath, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PODArray<char> text;
-        size_t byteLength;
-        PreCachedPath path;
-        TypedMatrix matrix);
-RECORD(DrawTextRSXform, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PODArray<char> text;
-        size_t byteLength;
-        PODArray<SkRSXform> xforms;
-        Optional<SkRect> cull);
-RECORD(DrawPatch, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        PODArray<SkPoint> cubics;
-        PODArray<SkColor> colors;
-        PODArray<SkPoint> texCoords;
-        SkBlendMode bmode);
-RECORD(DrawAtlas, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
-        Optional<SkPaint> paint;
-        sk_sp<const SkImage> atlas;
-        PODArray<SkRSXform> xforms;
-        PODArray<SkRect> texs;
-        PODArray<SkColor> colors;
-        int count;
-        SkBlendMode mode;
-        Optional<SkRect> cull);
-RECORD(DrawVertices, kDraw_Tag|kHasPaint_Tag,
-        SkPaint paint;
-        sk_sp<SkVertices> vertices;
-        SkBlendMode bmode);
-RECORD(DrawAnnotation, 0,  // TODO: kDraw_Tag, skia:5548
-       SkRect rect;
-       SkString key;
-       sk_sp<SkData> value);
-#undef RECORD
-
-}  // namespace SkRecords
-
-#endif//SkRecords_DEFINED
diff --git a/include/private/SkShadowFlags.h b/include/private/SkShadowFlags.h
index 0caa010..8caf632 100644
--- a/include/private/SkShadowFlags.h
+++ b/include/private/SkShadowFlags.h
@@ -14,12 +14,10 @@
     /** The occluding object is not opaque. Knowing that the occluder is opaque allows
     * us to cull shadow geometry behind it and improve performance. */
     kTransparentOccluder_ShadowFlag = 0x01,
-    /** Use a larger umbra for a darker shadow */
-    kLargerUmbra_ShadowFlag = 0x02,
-    /** Use a Gaussian for the edge function rather than smoothstep */
-    kGaussianEdge_ShadowFlag = 0x04,
+    /** Don't try to use analytic shadows. */
+    kGeometricOnly_ShadowFlag = 0x02,
     /** mask for all shadow flags */
-    kAll_ShadowFlag = 0x07
+    kAll_ShadowFlag = 0x03
 };
 
 #endif
diff --git a/include/private/SkShadowParams.h b/include/private/SkShadowParams.h
deleted file mode 100644
index 65d6c06..0000000
--- a/include/private/SkShadowParams.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkShadowParams_DEFINED
-#define SkShadowParams_DEFINED
-
-#include "SkScalar.h"
-
-/** \struct SkShadowParams
-
-    This struct holds information needed for drawing shadows.
-
-    fShadowRadius - radius of the shadow blur
-
-    fBiasingConstant - A constant used in variance shadow mapping to directly
-    0.0 - 1.0          reduce light bleeding. Essentially sets all shadows
-    ~.25               below a certain brightness equal to no light, and does
-                       a linear step on the rest. Essentially makes shadows
-                       darker and more rounded at higher values.
-
-    fMinVariance - Too low of a variance (near the outer edges of blurry
-    ~512, 1024     shadows) will lead to ugly sharp shadow brightness
-                   distortions. This enforces a minimum amount of variance
-                   in the calculation to smooth out the outside edges of
-                   blurry shadows. However, too high of a value for this will
-                   cause all shadows to be lighter by visibly different
-                   amounts varying on depth.
-
-    fType - Decides which algorithm to use to draw shadows.
-*/
-struct SkShadowParams {
-    SkScalar fShadowRadius;
-    SkScalar fBiasingConstant;
-    SkScalar fMinVariance;
-
-    enum ShadowType {
-        kNoBlur_ShadowType,
-        kVariance_ShadowType,
-
-        kLast_ShadowType = kVariance_ShadowType
-    };
-    static const int kShadowTypeCount = kLast_ShadowType + 1;
-
-    ShadowType fType;
-};
-
-#endif
diff --git a/include/private/SkTemplates.h b/include/private/SkTemplates.h
index 351fccc..919d160 100644
--- a/include/private/SkTemplates.h
+++ b/include/private/SkTemplates.h
@@ -294,8 +294,10 @@
     }
 
     SkAutoTMalloc& operator=(SkAutoTMalloc<T>&& that) {
-        sk_free(fPtr);
-        fPtr = that.release();
+        if (this != &that) {
+            sk_free(fPtr);
+            fPtr = that.release();
+        }
         return *this;
     }
 
diff --git a/include/utils/SkEventTracer.h b/include/utils/SkEventTracer.h
index f4f8676..a0289ce 100644
--- a/include/utils/SkEventTracer.h
+++ b/include/utils/SkEventTracer.h
@@ -27,9 +27,18 @@
 
     typedef uint64_t Handle;
 
-    static SkEventTracer* GetInstance();
+    /**
+     * If this is the first call to SetInstance or GetInstance then the passed instance is
+     * installed and true is returned. Otherwise, false is returned. In either case ownership of the
+     * tracer is transferred and it will be deleted when no longer needed.
+     */
+    static bool SetInstance(SkEventTracer*);
 
-    static void SetInstance(SkEventTracer*);
+    /**
+     * Gets the event tracer. If this is the first call to SetInstance or GetIntance then a default
+     * event tracer is installed and returned.
+     */
+    static SkEventTracer* GetInstance();
 
     virtual ~SkEventTracer() { }
 
diff --git a/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h
index ba0745e..7701d50 100644
--- a/include/utils/SkNWayCanvas.h
+++ b/include/utils/SkNWayCanvas.h
@@ -71,6 +71,7 @@
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
                           const SkPaint*) override;
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
+    void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/include/utils/SkNoDrawCanvas.h b/include/utils/SkNoDrawCanvas.h
index d21d678..7b3eaf6 100644
--- a/include/utils/SkNoDrawCanvas.h
+++ b/include/utils/SkNoDrawCanvas.h
@@ -28,6 +28,11 @@
     // TODO: investigate the users of this ctor.
     SkNoDrawCanvas(const SkIRect&);
 
+    // Optimization to reset state to be the same as after construction.
+    void resetCanvas(int width, int height) {
+        resetForNextPicture(SkIRect::MakeWH(width, height));
+    }
+
 protected:
     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
 
diff --git a/include/utils/SkShadowUtils.h b/include/utils/SkShadowUtils.h
index 01514a4..838a12b 100644
--- a/include/utils/SkShadowUtils.h
+++ b/include/utils/SkShadowUtils.h
@@ -21,12 +21,13 @@
 public:
     /**
      * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc
-     * light.
+     * light. The shadow may be cached, depending on the path type and canvas matrix. If the
+     * matrix is perspective or the path is volatile, it will not be cached.
      *
      * @param canvas  The canvas on which to draw the shadows.
      * @param path  The occluder used to generate the shadows.
-     * @param occluderHeight  The vertical offset of the occluder from the canvas. This is
-     *  independent of the canvas's current matrix.
+     * @param zPlaneParams  Values for the plane function which returns the Z offset of the
+     *  occluder from the canvas based on local x and y values (the current matrix is not applied).
      * @param lightPos  The 3D position of the light relative to the canvas plane. This is
      *  independent of the canvas's current matrix.
      * @param lightRadius  The radius of the disc light.
@@ -35,23 +36,22 @@
      * @param color  The shadow color.
      * @param flags  Options controlling opaque occluder optimizations and shadow appearance. See
      *               SkShadowFlags.
-     * @param cache  Used for testing purposes. Clients should pass nullptr (default).
      */
-    static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
+    static void DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
                            const SkPoint3& lightPos, SkScalar lightRadius, SkScalar ambientAlpha,
                            SkScalar spotAlpha, SkColor color,
-                           uint32_t flags = SkShadowFlags::kNone_ShadowFlag,
-                           SkResourceCache* cache = nullptr);
+                           uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
 
-   /**
+    /**
     * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc
-    * light. Takes a function to vary the z value based on the transformed x and y position.
-    * This shadow will not be cached, as the assumption is that this will be used for animation.
+    * light.
+    *
+    * Deprecated version with height value (to be removed when Flutter is updated).
     *
     * @param canvas  The canvas on which to draw the shadows.
     * @param path  The occluder used to generate the shadows.
-    * @param heightFunc  A function which returns the vertical offset of the occluder from the
-    *  canvas based on local x and y values (the current matrix is not applied).
+    * @param occluderHeight  The vertical offset of the occluder from the canvas. This is
+    *  independent of the canvas's current matrix.
     * @param lightPos  The 3D position of the light relative to the canvas plane. This is
     *  independent of the canvas's current matrix.
     * @param lightRadius  The radius of the disc light.
@@ -61,11 +61,14 @@
     * @param flags  Options controlling opaque occluder optimizations and shadow appearance. See
     *               SkShadowFlags.
     */
-    static void DrawUncachedShadow(SkCanvas* canvas, const SkPath& path,
-                                   std::function<SkScalar(SkScalar, SkScalar)> heightFunc,
-                                   const SkPoint3& lightPos, SkScalar lightRadius,
-                                   SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
-                                   uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
+    static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
+                           const SkPoint3& lightPos, SkScalar lightRadius, SkScalar ambientAlpha,
+                           SkScalar spotAlpha, SkColor color,
+                           uint32_t flags = SkShadowFlags::kNone_ShadowFlag) {
+        SkPoint3 zPlane = SkPoint3::Make(0, 0, occluderHeight);
+        DrawShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha,
+                   color, flags);
+    }
 };
 
 #endif
diff --git a/include/views/SkTouchGesture.h b/include/views/SkTouchGesture.h
index 4d4c031..6776de7 100644
--- a/include/views/SkTouchGesture.h
+++ b/include/views/SkTouchGesture.h
@@ -43,7 +43,8 @@
     const SkMatrix& localM();
     const SkMatrix& globalM() const { return fGlobalM; }
 
-    void setTransLimit(const SkRect& contentRect, const SkRect& windowRect);
+    void setTransLimit(const SkRect& contentRect, const SkRect& windowRect,
+                       const SkMatrix& preTouchM);
 
 private:
     enum State {
@@ -62,7 +63,7 @@
     SkTDArray<Rec> fTouches;
 
     State           fState;
-    SkMatrix        fLocalM, fGlobalM;
+    SkMatrix        fLocalM, fGlobalM, fPreTouchM;
     SkFlingState    fFlinger;
     double          fLastUpMillis;
     SkPoint         fLastUpP;
diff --git a/infra/bots/Makefile b/infra/bots/Makefile
new file mode 100644
index 0000000..0df77df
--- /dev/null
+++ b/infra/bots/Makefile
@@ -0,0 +1,5 @@
+test:
+	python infra_tests.py
+
+train:
+	python infra_tests.py --train
diff --git a/infra/bots/README.md b/infra/bots/README.md
index 9f184aa..9683e18 100644
--- a/infra/bots/README.md
+++ b/infra/bots/README.md
@@ -1,18 +1,102 @@
-Skia Buildbot Scripts
-=====================
+Skia Infrastructure
+===================
 
-The scripts in this directory are ported from Skia's buildbot recipes and are
-intended to run as standalone Python scripts either locally or via Swarming.
+This directory contains infrastructure elements.
 
-How to Run
-----------
 
-The scripts can be run by hand, eg:
+Tasks and Jobs
+--------------
 
-$ cd infra/bots
-$ python compile_skia.py Build-Ubuntu-GCC-x86_64-Debug ../../out
+Files in this directory define a DAG of tasks which run at every Skia commit. A
+task is a small, self-contained unit which runs via Swarming on a machine in the
+pool. Tasks may be chained together, eg. one task to compile test binaries and
+another to actually run them.
 
-Or, you can run the scripts via Swarming:
+Jobs are collections of related tasks which help define sub-sections of the DAG,
+for example, to be used as try jobs. Each job is defined as an entry point into
+the DAG.
 
-$ isolate archive --isolate-server https://isolateserver.appspot.com/ -i infra/bots/compile_skia.isolate -s ../compile-skia.isolated --verbose --config-variable BUILDER_NAME=Build-Ubuntu-GCC-x86_64-Debug
-$ swarming.py run --swarming https://chromium-swarm.appspot.com --isolate-server https://isolateserver.appspot.com --dimension os Ubuntu --dimension pool Skia --task-name compile-skia --io-timeout=3600 --hard-timeout=3600 ../compile-skia.isolated
+The tasks.json file in this directory is the master list of tasks and jobs for
+the repo. Note that tasks.json is NEVER edited by hand but generated via
+gen_task.go and the input files enumerated below. The
+[Task Scheduler](https://skia.googlesource.com/buildbot/+/master/task_scheduler/README.md)
+reads the tasks.json file at each commit to determine which jobs to run. For
+convenience, gen_tasks.go is provided to generate tasks.json and also to test it
+for correct syntax and detecting cycles and orphaned tasks. Always edit
+gen_tasks.go or one of the following input JSON files, rather than tasks.json
+itself:
+
+  * android_map.json - Maps human-friendly names of Android devices to their
+      device codename and desired OS version. Edit this file when adding a new
+      type of Android device or updating the desired OS version.
+  * cfg.json - Basic configuration information for gen_tasks.go.
+  * gpu_map.json - Maps human-friendly names of GPUs to an appropriate Swarming
+      dimension, typically the PCI ID of the GPU. Edit this file when adding a
+      new GPU.
+  * jobs.json - The master list of all jobs to run. Edit this to add or remove
+      bots.
+
+Whenever gen_tasks.go, any of the above JSON files, or assets are changed, you
+need to run gen_tasks.go to regenerate tasks.json:
+
+	$ go run infra/bots/gen_tasks.go
+
+Or:
+
+	$ cd infra/bots; make train
+
+There is also a test mode which performs sanity-checks and verifies that
+tasks.json is unchanged:
+
+	$ go run infra/bots/gen_tasks.go --test
+
+Or:
+
+	$ cd infra/bots; make test
+
+
+Recipes
+-------
+
+Recipes are the framework used by Skia's infrastructure to perform work inside
+of Swarming tasks. The main elements are:
+
+  * recipes.py - Used for running and testing recipes.
+  * recipes - These are the entry points for each type of task, eg. compiling
+      or running tests.
+  * recipe_modules - Shared modules which are used by recipes.
+  * .recipe_deps - Recipes and modules may depend on modules from other repos.
+      The recipes.py script automatically syncs those dependencies in this
+      directory.
+
+
+Isolate Files
+-------------
+
+These files determine which parts of the repository are transferred to the bot
+when a Swarming task is triggered. The
+[Isolate tool](https://github.com/luci/luci-py/tree/master/appengine/isolate/doc)
+hashes each file and will upload any new/changed files. Bots maintain a cache so
+that they can efficiently download only the files they don't have.
+
+
+Assets
+------
+
+Artifacts used by the infrastructure are versioned here, along with scripts for
+recreating/uploading/downloading them. See the README in that directory for more
+information. Any time an asset used by the bots changes, you need to re-run
+gen_tasks.go.
+
+
+Tools
+-----
+
+Assorted other infrastructure-related tools, eg. isolate and CIPD binaries.
+
+
+CT
+--
+
+Helpers for running Skia tasks in Cluster Telemetry.
+
diff --git a/infra/bots/android_map.json b/infra/bots/android_map.json
index 25062ac..938eae4 100644
--- a/infra/bots/android_map.json
+++ b/infra/bots/android_map.json
@@ -1,21 +1,22 @@
 {
   "AndroidOne":      ["sprout",      "MOB30Q"],
   "Chorizo":         ["chorizo",     "1.24_82923"],
+  "Ci20":            ["ci20",        "NRD90M"],
   "GalaxyJ5":        ["j5xnlte",     "MMB29M"],
   "GalaxyS6":        ["zerofltetmo", "MMB29K"],
-  "GalaxyS7_G930A":  ["heroqlteatt", "NRD90M_G930AUCU4BQA6"],
+  "GalaxyS7_G930A":  ["heroqlteatt", "NRD90M_G930AUCS4BQC2"],
   "GalaxyS7_G930FD": ["herolte",     "NRD90M_G930FXXU1DQAS"],
   "GalaxyTab3":      ["goyawifi",    "JDQ39"],
-  "MotoG4":          ["athene",      "MPJ24.139-64"],
-  "NVIDIA_Shield":   ["foster",      "MRA58K"],
+  "MotoG4":          ["athene",      "NPJ25.93-14"],
+  "NVIDIA_Shield":   ["foster",      "NRD90M"],
   "Nexus10":         ["manta",       "LMY49J"],
   "Nexus5":          ["hammerhead",  "M4B30Z"],
   "Nexus6":          ["shamu",       "M"],
-  "Nexus6p":         ["angler",      "NMF26C"],
+  "Nexus6p":         ["angler",      "OPP1.170223.012"],
   "Nexus7":          ["grouper",     "LMY47V"],
   "Nexus7v2":        ["flo",         "M"],
-  "NexusPlayer":     ["fugu",        "N2G10B"],
+  "NexusPlayer":     ["fugu",        "OPP2.170420.017"],
   "Pixel":           ["sailfish",    "NMF26Q"],
-  "PixelC":          ["dragon",      "NMF26H"],
+  "PixelC":          ["dragon",      "N2G47D"],
   "PixelXL":         ["marlin",      "NMF26Q"]
 }
diff --git a/infra/bots/assets.isolate b/infra/bots/assets.isolate
new file mode 100644
index 0000000..1d9ef7a
--- /dev/null
+++ b/infra/bots/assets.isolate
@@ -0,0 +1,7 @@
+{
+  'variables': {
+    'files': [
+      'assets/',
+    ],
+  },
+}
diff --git a/infra/bots/assets/armhf_sysroot/README.md b/infra/bots/assets/armhf_sysroot/README.md
new file mode 100644
index 0000000..257acd7
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/README.md
@@ -0,0 +1,9 @@
+ARM (hard float) sysroot for cross-compiling c++ code on a x86_64 Linux bot.
+
+Run create_and_upload which installs the following debian packages and turns them
+into a toolchain:
+
+    libstdc++-4.8-dev-armhf-cross libgcc-4.8-dev-armhf-cross binutils-arm-linux-gnueabihf
+
+Take a peak at `/usr/arm-linux-gnueabihf/include/c++/4.8.X` - you may need to update the
+include paths if that number changed from the previous release (currently 4.8.4).
\ No newline at end of file
diff --git a/infra/bots/assets/armhf_sysroot/VERSION b/infra/bots/assets/armhf_sysroot/VERSION
new file mode 100644
index 0000000..e440e5c
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/VERSION
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/infra/bots/assets/armhf_sysroot/common.py b/infra/bots/assets/armhf_sysroot/common.py
new file mode 100755
index 0000000..4920c9b
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/common.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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/armhf_sysroot/create.py b/infra/bots/assets/armhf_sysroot/create.py
new file mode 100755
index 0000000..7be59fb
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/create.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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 fileinput
+import os
+import shutil
+import subprocess
+import sys
+
+from distutils import dir_util
+
+
+def create_asset(target_dir):
+  """Create the asset."""
+
+  print "Installing some cross-compiling packages. Hit enter to continue."
+  raw_input()
+  subprocess.check_call([
+    "sudo","apt-get","install",
+    "libstdc++-4.8-dev-armhf-cross",
+    "libgcc-4.8-dev-armhf-cross",
+    "binutils-arm-linux-gnueabihf"
+  ])
+
+
+  shutil.copytree('/usr/arm-linux-gnueabihf', target_dir)
+  shutil.copytree('/usr/lib/gcc-cross/arm-linux-gnueabihf/4.8.4',
+    os.path.join(target_dir, 'gcc-cross'))
+  # copy_tree allows copying into a dir that exists
+  # We need to augment the toolchain with some lib*.so that help ld
+  # do its magic as well as some includes that may be useful.
+  dir_util.copy_tree('/usr/x86_64-linux-gnu/arm-linux-gnueabihf/lib',
+                     os.path.join(target_dir, 'lib'))
+  dir_util.copy_tree('/usr/x86_64-linux-gnu/arm-linux-gnueabihf/include',
+                     os.path.join(target_dir, 'include'))
+
+  # The file paths in libpthread.so and libc.so start off as absolute file
+  # paths (e.g. /usr/arm-linux-gnueabihf/lib/libpthread.so.0), which won't
+  # work on the bots. We use fileinput to replace just those lines (which
+  # start with GROUP). fileinput redirects stdout, so printing here actually
+  # writes to the file.
+  bad_libpthread = os.path.join(target_dir, "lib", "libpthread.so")
+  for line in fileinput.input(bad_libpthread, inplace=True):
+    if line.startswith("GROUP"):
+      print "GROUP ( libpthread.so.0 libpthread_nonshared.a )"
+    else:
+      print line
+
+  bad_libc = os.path.join(target_dir, "lib", "libc.so")
+  for line in fileinput.input(bad_libc, inplace=True):
+    if line.startswith("GROUP"):
+      print ("GROUP ( libc.so.6 libc_nonshared.a "
+             "AS_NEEDED ( ld-linux-armhf.so.3 ) )")
+    else:
+      print line
+
+
+def main():
+  if 'linux' not in sys.platform:
+    print >> sys.stderr, 'This script only runs on Linux.'
+    sys.exit(1)
+  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/armhf_sysroot/create_and_upload.py b/infra/bots/assets/armhf_sysroot/create_and_upload.py
new file mode 100755
index 0000000..b9e6d6f
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/create_and_upload.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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():
+  if 'linux' not in sys.platform:
+    print >> sys.stderr, 'This script only runs on Linux.'
+    sys.exit(1)
+  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:
+      cwd = os.path.join(cwd, 'sysroot')
+      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/armhf_sysroot/download.py b/infra/bots/assets/armhf_sysroot/download.py
new file mode 100755
index 0000000..96cc87d
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/download.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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/armhf_sysroot/upload.py b/infra/bots/assets/armhf_sysroot/upload.py
new file mode 100755
index 0000000..ba7fc8b
--- /dev/null
+++ b/infra/bots/assets/armhf_sysroot/upload.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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/asset_utils.py b/infra/bots/assets/asset_utils.py
index c166495..df29aec 100644
--- a/infra/bots/assets/asset_utils.py
+++ b/infra/bots/assets/asset_utils.py
@@ -122,7 +122,7 @@
         '--in', target_dir,
         '--tag', TAG_PROJECT_SKIA,
         '--tag', TAG_VERSION_TMPL % version,
-        '--compression-level', '0',
+        '--compression-level', '1',
     ])
 
   def download(self, name, version, target_dir):
diff --git a/infra/bots/assets/cast_toolchain/VERSION b/infra/bots/assets/cast_toolchain/VERSION
index d8263ee..bf0d87a 100644
--- a/infra/bots/assets/cast_toolchain/VERSION
+++ b/infra/bots/assets/cast_toolchain/VERSION
@@ -1 +1 @@
-2
\ No newline at end of file
+4
\ No newline at end of file
diff --git a/infra/bots/assets/chromebook_arm_gles/README.md b/infra/bots/assets/chromebook_arm_gles/README.md
new file mode 100644
index 0000000..f929383
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/README.md
@@ -0,0 +1,13 @@
+This asset is the necessary includes and libs to compile/link the gpu code
+for ARM Chromebooks with gpu supports EGL and GLES.
+
+Zip up the /usr/lib folder on any Arm Chromebook (e.g. Asus C100p). Extract it somewhere
+on the dev machine and use that folder as the input to create_and_upload:
+
+    ./infra/bots/assets/chromebook_arm_gles/create_and_upload.py --lib_path [dir]
+
+This script installs the following GL packages and then bundles them with the
+unzipped libs:
+
+    libgles2-mesa-dev libegl1-mesa-dev
+
diff --git a/infra/bots/assets/chromebook_arm_gles/VERSION b/infra/bots/assets/chromebook_arm_gles/VERSION
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/VERSION
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/infra/bots/assets/chromebook_arm_gles/common.py b/infra/bots/assets/chromebook_arm_gles/common.py
new file mode 100755
index 0000000..4920c9b
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/common.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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/chromebook_arm_gles/create.py b/infra/bots/assets/chromebook_arm_gles/create.py
new file mode 100755
index 0000000..2611802
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/create.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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 glob
+import os
+import shutil
+import subprocess
+import sys
+
+
+def create_asset(target_dir, gl_path):
+  """Create the asset."""
+
+  cmd = [
+    'sudo','apt-get','install',
+    'libgles2-mesa-dev',
+    'libegl1-mesa-dev'
+  ]
+  print 'About to run:'
+  print ' '.join(cmd)
+  print 'Press Enter to Continue'
+  raw_input()
+  subprocess.check_call(cmd)
+
+
+  lib_dir = os.path.join(target_dir, 'lib')
+  os.mkdir(lib_dir)
+
+  for f in glob.glob(os.path.join(gl_path,'libGL*')):
+    shutil.copy(f, lib_dir)
+
+  for f in glob.glob(os.path.join(gl_path,'libEGL*')):
+    shutil.copy(f, lib_dir)
+
+  for f in glob.glob(os.path.join(gl_path,'libmali*')):
+    shutil.copy(f, lib_dir)
+
+  include_dir = os.path.join(target_dir, 'include')
+  os.mkdir(include_dir)
+  shutil.copytree('/usr/include/EGL', os.path.join(include_dir, 'EGL'))
+  shutil.copytree('/usr/include/KHR', os.path.join(include_dir, 'KHR'))
+  shutil.copytree('/usr/include/GLES2', os.path.join(include_dir, 'GLES2'))
+  shutil.copytree('/usr/include/GLES3', os.path.join(include_dir, 'GLES3'))
+
+
+def main():
+  if 'linux' not in sys.platform:
+    print >> sys.stderr, 'This script only runs on Linux.'
+    sys.exit(1)
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--target_dir', '-t', required=True)
+  parser.add_argument('--lib_path', '-l', required=True)
+  args = parser.parse_args()
+  create_asset(args.target_dir, args.lib_path)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/infra/bots/assets/chromebook_arm_gles/create_and_upload.py b/infra/bots/assets/chromebook_arm_gles/create_and_upload.py
new file mode 100755
index 0000000..37e3ff9
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/create_and_upload.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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():
+  if 'linux' not in sys.platform:
+    print >> sys.stderr, 'This script only runs on Linux.'
+    sys.exit(1)
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--gsutil')
+  parser.add_argument('--lib_path', '-l', required=True)
+  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,
+                             '-l', args.lib_path])
+      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/chromebook_arm_gles/download.py b/infra/bots/assets/chromebook_arm_gles/download.py
new file mode 100755
index 0000000..96cc87d
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/download.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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/chromebook_arm_gles/upload.py b/infra/bots/assets/chromebook_arm_gles/upload.py
new file mode 100755
index 0000000..ba7fc8b
--- /dev/null
+++ b/infra/bots/assets/chromebook_arm_gles/upload.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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/clang_linux/VERSION b/infra/bots/assets/clang_linux/VERSION
index bf0d87a..301160a 100644
--- a/infra/bots/assets/clang_linux/VERSION
+++ b/infra/bots/assets/clang_linux/VERSION
@@ -1 +1 @@
-4
\ No newline at end of file
+8
\ No newline at end of file
diff --git a/infra/bots/assets/clang_linux/create.py b/infra/bots/assets/clang_linux/create.py
index a7c2484..f7a4a07 100755
--- a/infra/bots/assets/clang_linux/create.py
+++ b/infra/bots/assets/clang_linux/create.py
@@ -15,7 +15,7 @@
 import tempfile
 
 REPO = "https://llvm.googlesource.com/"
-BRANCH = "release_39"
+BRANCH = "release_40"
 
 def create_asset(target_dir):
   # Build Clang, lld, compiler-rt (sanitizer support) and libc++.
diff --git a/infra/bots/assets/go/VERSION b/infra/bots/assets/go/VERSION
index 56a6051..d8263ee 100644
--- a/infra/bots/assets/go/VERSION
+++ b/infra/bots/assets/go/VERSION
@@ -1 +1 @@
-1
\ No newline at end of file
+2
\ No newline at end of file
diff --git a/infra/bots/assets/go/create.py b/infra/bots/assets/go/create.py
index deeaf11..af72be8 100755
--- a/infra/bots/assets/go/create.py
+++ b/infra/bots/assets/go/create.py
@@ -12,7 +12,7 @@
 import argparse
 import subprocess
 
-GO_URL = "https://storage.googleapis.com/golang/go1.7.5.linux-amd64.tar.gz"
+GO_URL = "https://storage.googleapis.com/golang/go1.8.2.linux-amd64.tar.gz"
 
 def create_asset(target_dir):
   """Create the asset."""
diff --git a/infra/bots/assets/linux_vulkan_intel_driver_debug/VERSION b/infra/bots/assets/linux_vulkan_intel_driver_debug/VERSION
index e440e5c..bf0d87a 100644
--- a/infra/bots/assets/linux_vulkan_intel_driver_debug/VERSION
+++ b/infra/bots/assets/linux_vulkan_intel_driver_debug/VERSION
@@ -1 +1 @@
-3
\ No newline at end of file
+4
\ No newline at end of file
diff --git a/infra/bots/assets/linux_vulkan_intel_driver_release/VERSION b/infra/bots/assets/linux_vulkan_intel_driver_release/VERSION
index 7813681..62f9457 100644
--- a/infra/bots/assets/linux_vulkan_intel_driver_release/VERSION
+++ b/infra/bots/assets/linux_vulkan_intel_driver_release/VERSION
@@ -1 +1 @@
-5
\ No newline at end of file
+6
\ No newline at end of file
diff --git a/infra/bots/assets/scripts/common.py b/infra/bots/assets/scripts/common.py
index 4920c9b..caa0ad8 100755
--- a/infra/bots/assets/scripts/common.py
+++ b/infra/bots/assets/scripts/common.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 Google Inc.
+# Copyright 2017 Google Inc.
 #
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/infra/bots/assets/scripts/create.py b/infra/bots/assets/scripts/create.py
index 4f17608..7f67e12 100755
--- a/infra/bots/assets/scripts/create.py
+++ b/infra/bots/assets/scripts/create.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 Google Inc.
+# Copyright 2017 Google Inc.
 #
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/infra/bots/assets/scripts/create_and_upload.py b/infra/bots/assets/scripts/create_and_upload.py
index 1356447..de56a80 100755
--- a/infra/bots/assets/scripts/create_and_upload.py
+++ b/infra/bots/assets/scripts/create_and_upload.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 Google Inc.
+# Copyright 2017 Google Inc.
 #
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/infra/bots/assets/scripts/download.py b/infra/bots/assets/scripts/download.py
index 96cc87d..ca999e0 100755
--- a/infra/bots/assets/scripts/download.py
+++ b/infra/bots/assets/scripts/download.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 Google Inc.
+# Copyright 2017 Google Inc.
 #
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/infra/bots/assets/scripts/upload.py b/infra/bots/assets/scripts/upload.py
index ba7fc8b..bdfbda7 100755
--- a/infra/bots/assets/scripts/upload.py
+++ b/infra/bots/assets/scripts/upload.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 Google Inc.
+# Copyright 2017 Google Inc.
 #
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/infra/bots/assets/skimage/VERSION b/infra/bots/assets/skimage/VERSION
index b393560..978b4e8 100644
--- a/infra/bots/assets/skimage/VERSION
+++ b/infra/bots/assets/skimage/VERSION
@@ -1 +1 @@
-23
\ No newline at end of file
+26
\ No newline at end of file
diff --git a/infra/bots/assets/skp/VERSION b/infra/bots/assets/skp/VERSION
index f70d7bb..2b82dfe 100644
--- a/infra/bots/assets/skp/VERSION
+++ b/infra/bots/assets/skp/VERSION
@@ -1 +1 @@
-42
\ No newline at end of file
+60
\ No newline at end of file
diff --git a/infra/bots/assets/valgrind/VERSION b/infra/bots/assets/valgrind/VERSION
new file mode 100644
index 0000000..62f9457
--- /dev/null
+++ b/infra/bots/assets/valgrind/VERSION
@@ -0,0 +1 @@
+6
\ No newline at end of file
diff --git a/infra/bots/assets/valgrind/common.py b/infra/bots/assets/valgrind/common.py
new file mode 100755
index 0000000..caa0ad8
--- /dev/null
+++ b/infra/bots/assets/valgrind/common.py
@@ -0,0 +1,26 @@
+#!/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/valgrind/create.py b/infra/bots/assets/valgrind/create.py
new file mode 100755
index 0000000..618c235
--- /dev/null
+++ b/infra/bots/assets/valgrind/create.py
@@ -0,0 +1,104 @@
+#!/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."""
+
+
+import argparse
+import common
+import grp
+import os
+import pwd
+import shutil
+import subprocess
+import sys
+import tempfile
+import urllib2
+import utils
+
+
+VALGRIND = 'valgrind-3.12.0'
+TARBALL = '%s.tar.bz2' % VALGRIND
+DOWNLOAD_URL = 'http://valgrind.org/downloads/%s' % TARBALL
+TEMP_DIR = os.path.join(tempfile.gettempdir(), 'skia-%s' % VALGRIND)
+INSTALL_DIR = os.path.join(TEMP_DIR, 'valgrind_install')
+
+
+def download_tarball():
+  with utils.chdir(TEMP_DIR):
+    if os.path.isfile(TARBALL):
+      return
+    with open(TARBALL, 'wb') as f:
+      f.write(urllib2.urlopen(DOWNLOAD_URL).read())
+
+
+def unzip_tarball():
+  with utils.chdir(TEMP_DIR):
+    if os.path.isdir(VALGRIND):
+      return
+    subprocess.check_call(['tar', 'xvjf', TARBALL])
+
+
+def create_install_dir():
+  if os.path.isdir(INSTALL_DIR):
+    return
+  os.makedirs(INSTALL_DIR)
+
+
+def build_valgrind():
+  if os.path.isfile(os.path.join(INSTALL_DIR, 'bin', 'valgrind')):
+    return
+  with utils.chdir(os.path.join(TEMP_DIR, VALGRIND)):
+    subprocess.check_call(['./configure', '--prefix=%s' % INSTALL_DIR])
+    subprocess.check_call(['make'])
+    subprocess.check_call(['make', 'install'])
+
+
+def copy_files(target_dir):
+  with utils.chdir(os.path.join(TEMP_DIR, VALGRIND)):
+    os.mkdir(os.path.join(target_dir, 'bin'))
+    shutil.copy(os.path.join(INSTALL_DIR, 'bin', 'valgrind'),
+                os.path.join(target_dir, 'bin', 'valgrind'))
+    os.mkdir(os.path.join(target_dir, 'lib'))
+    os.mkdir(os.path.join(target_dir, 'lib', 'valgrind'))
+    for lib in ['memcheck-amd64-linux']:
+      shutil.copy(os.path.join(INSTALL_DIR, 'lib', 'valgrind', lib),
+                  os.path.join(target_dir, 'lib', 'valgrind', lib))
+    for lib in ['core', 'memcheck']:
+      libname = 'vgpreload_%s-amd64-linux.so' % lib
+      shutil.copy(os.path.join(INSTALL_DIR, 'lib', 'valgrind', libname),
+                  os.path.join(target_dir, 'lib', 'valgrind', libname))
+
+    shutil.copy('default.supp',
+                os.path.join(target_dir, 'lib', 'valgrind', 'default.supp'))
+
+
+def create_asset(target_dir):
+  """Create the asset."""
+  if os.name == 'nt':
+    print 'This script does not run on Windows.'
+    sys.exit(1)
+
+  create_install_dir()
+  if not os.path.isdir(TEMP_DIR):
+    os.makedirs(TEMP_DIR)
+  download_tarball()
+  unzip_tarball()
+  build_valgrind()
+  copy_files(target_dir)
+
+
+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/valgrind/create_and_upload.py b/infra/bots/assets/valgrind/create_and_upload.py
new file mode 100755
index 0000000..de56a80
--- /dev/null
+++ b/infra/bots/assets/valgrind/create_and_upload.py
@@ -0,0 +1,42 @@
+#!/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/valgrind/download.py b/infra/bots/assets/valgrind/download.py
new file mode 100755
index 0000000..ca999e0
--- /dev/null
+++ b/infra/bots/assets/valgrind/download.py
@@ -0,0 +1,16 @@
+#!/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/valgrind/upload.py b/infra/bots/assets/valgrind/upload.py
new file mode 100755
index 0000000..bdfbda7
--- /dev/null
+++ b/infra/bots/assets/valgrind/upload.py
@@ -0,0 +1,16 @@
+#!/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/bundle_recipes.isolate b/infra/bots/bundle_recipes.isolate
new file mode 100644
index 0000000..3faa6f7
--- /dev/null
+++ b/infra/bots/bundle_recipes.isolate
@@ -0,0 +1,14 @@
+{
+  'includes': [
+    'infrabots.isolate',
+  ],
+  'variables': {
+    'command': [
+      'python', 'recipes.py', '--package', '../config/recipes.cfg', 'run', '--timestamps',
+    ],
+    'files': [
+      '../../../.gclient',
+      '../config/recipes.cfg',
+    ],
+  },
+}
diff --git a/infra/bots/cfg.json b/infra/bots/cfg.json
index 048e4b4..dbc3914 100644
--- a/infra/bots/cfg.json
+++ b/infra/bots/cfg.json
@@ -8,6 +8,7 @@
     "MSAN",
     "TSAN",
     "UBSAN",
-    "Valgrind"
+    "Valgrind",
+    "AbandonGpuContext"
   ]
 }
diff --git a/infra/bots/ct/blacklists/get_images_from_skps_100k_c37e844a6f8708-eee762104c75bd.json b/infra/bots/ct/blacklists/get_images_from_skps_100k_c37e844a6f8708-eee762104c75bd.json
new file mode 100644
index 0000000..efd8a4a
--- /dev/null
+++ b/infra/bots/ct/blacklists/get_images_from_skps_100k_c37e844a6f8708-eee762104c75bd.json
@@ -0,0 +1,21 @@
+{
+    "blacklisted_skps": [
+        "http___88db_com_hk.skp",
+        "http___its_ac_id.skp",
+        "http___poltec_co_ao.skp",
+        "http___www_bananastore_com.skp",
+        "http___www_centurylink_com.skp",
+        "http___www_dandb_com.skp",
+        "http___www_kejixun_com.skp",
+        "http___www_kinozal_website.skp",
+        "http___www_muslimthaipost_com.skp",
+        "http___www_pods_com.skp",
+        "http___www_pokerstars_eu.skp",
+        "http___www_sain3_com.skp",
+        "http___www_searshomeservices_com.skp",
+        "http___www_ticketmaster_se.skp",
+        "http___www_travelguard_com.skp",
+        "http___www_uverse_com.skp",
+        "http___www_weifastpay_com.skp"
+    ]
+}
diff --git a/infra/bots/gen_tasks.go b/infra/bots/gen_tasks.go
index 8cbbd83..07e63ad 100644
--- a/infra/bots/gen_tasks.go
+++ b/infra/bots/gen_tasks.go
@@ -26,6 +26,11 @@
 )
 
 const (
+	BUNDLE_RECIPES_NAME  = "Housekeeper-PerCommit-BundleRecipes"
+	ISOLATE_SKIMAGE_NAME = "Housekeeper-PerCommit-IsolateSkImage"
+	ISOLATE_SKP_NAME     = "Housekeeper-PerCommit-IsolateSKP"
+	ISOLATE_SVG_NAME     = "Housekeeper-PerCommit-IsolateSVG"
+
 	DEFAULT_OS       = DEFAULT_OS_LINUX
 	DEFAULT_OS_LINUX = "Ubuntu-14.04"
 
@@ -57,6 +62,18 @@
 	// Defines the structure of job names.
 	jobNameSchema *JobNameSchema
 
+	// Git 2.13.
+	cipdGit1 = &specs.CipdPackage{
+		Name:    fmt.Sprintf("infra/git/${platform}"),
+		Path:    "git",
+		Version: fmt.Sprintf("version:2.13.0.chromium9"),
+	}
+	cipdGit2 = &specs.CipdPackage{
+		Name:    fmt.Sprintf("infra/tools/git/${platform}"),
+		Path:    "git",
+		Version: fmt.Sprintf("git_revision:a78b5f3658c0578a017db48df97d20ac09822bcd"),
+	}
+
 	// Flags.
 	androidMapFile        = flag.String("android_map", "", "JSON file containing a mapping of human-friendly Android device names to a pair of {device_type, device_os}.")
 	builderNameSchemaFile = flag.String("builder_name_schema", "", "Path to the builder_name_schema.json file. If not specified, uses infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json from this repo.")
@@ -84,24 +101,31 @@
 		return "Build-Ubuntu-GCC-x86_64-Release-Shared"
 	} else if parts["role"] == "Test" || parts["role"] == "Perf" {
 		task_os := parts["os"]
-		ec := parts["extra_config"]
-		ec = strings.TrimSuffix(ec, "_Skpbench")
-		ec = strings.TrimSuffix(ec, "_AbandonGpuContext")
-		ec = strings.TrimSuffix(ec, "_PreAbandonGpuContext")
-		if ec == "Valgrind" {
-			// skia:6267
-			ec = ""
+		ec := []string{}
+		if val := parts["extra_config"]; val != "" {
+			ec = strings.Split(val, "_")
+			ignore := []string{"Skpbench", "AbandonGpuContext", "PreAbandonGpuContext", "Valgrind", "ReleaseAndAbandonGpuContext"}
+			keep := make([]string, 0, len(ec))
+			for _, part := range ec {
+				if !util.In(part, ignore) {
+					keep = append(keep, part)
+				}
+			}
+			ec = keep
 		}
 		if task_os == "Android" {
-			if ec == "Vulkan" {
-				ec = "Android_Vulkan"
+			if !util.In("Android", ec) {
+				ec = append([]string{"Android"}, ec...)
 			}
 			task_os = "Ubuntu"
 		} else if task_os == "Chromecast" {
 			task_os = "Ubuntu"
-			ec = "Chromecast"
+			ec = append([]string{"Chromecast"}, ec...)
+		} else if strings.Contains(task_os, "ChromeOS") {
+			ec = append([]string{"Chromebook", "ARM", "GLES"}, ec...)
+			task_os = "Ubuntu"
 		} else if task_os == "iOS" {
-			ec = task_os
+			ec = append([]string{task_os}, ec...)
 			task_os = "Mac"
 		} else if strings.Contains(task_os, "Win") {
 			task_os = "Win"
@@ -115,8 +139,8 @@
 			"target_arch":   parts["arch"],
 			"configuration": parts["configuration"],
 		}
-		if ec != "" {
-			jobNameMap["extra_config"] = ec
+		if len(ec) > 0 {
+			jobNameMap["extra_config"] = strings.Join(ec, "_")
 		}
 		name, err := jobNameSchema.MakeJobName(jobNameMap)
 		if err != nil {
@@ -137,14 +161,16 @@
 		d["os"] = map[string]string{
 			"Android":    "Android",
 			"Chromecast": "Android",
+			"ChromeOS":   "ChromeOS",
 			"Mac":        "Mac-10.11",
 			"Ubuntu":     DEFAULT_OS_LINUX,
 			"Ubuntu16":   "Ubuntu-16.10",
 			"Win":        "Windows-2008ServerR2-SP1",
-			"Win10":      "Windows-10-14393",
+			"Win10":      "Windows-10-15063",
 			"Win2k8":     "Windows-2008ServerR2-SP1",
+			"Win7":       "Windows-7-SP1",
 			"Win8":       "Windows-8.1-SP0",
-			"iOS":        "iOS-9.3.1",
+			"iOS":        "iOS-10.3.1",
 		}[os]
 		// Chrome Golo has a different Windows image.
 		if parts["model"] == "Golo" && os == "Win10" {
@@ -166,6 +192,9 @@
 		} else if strings.Contains(parts["os"], "iOS") {
 			d["device"] = map[string]string{
 				"iPadMini4": "iPad5,1",
+				"iPhone6":   "iPhone7,2",
+				"iPhone7":   "iPhone9,1",
+				"iPadPro":   "iPad6,3",
 			}[parts["model"]]
 		} else if parts["cpu_or_gpu"] == "CPU" {
 			d["gpu"] = "none"
@@ -179,7 +208,9 @@
 				// dimensions to ensure that we correctly target machines which we know
 				// have AVX2 support.
 				d["cpu"] = "x86-64"
-				d["os"] = "Windows-2008ServerR2-SP1"
+				if parts["model"] != "GCE" {
+					glog.Fatalf("Please double-check that %q supports AVX2 and update this assertion.", parts["model"])
+				}
 			}
 		} else {
 			gpu, ok := GPU_MAPPING[parts["cpu_or_gpu_value"]]
@@ -214,6 +245,86 @@
 	return rv
 }
 
+// bundleRecipes generates the task to bundle and isolate the recipes.
+func bundleRecipes(b *specs.TasksCfgBuilder) string {
+	b.MustAddTask(BUNDLE_RECIPES_NAME, &specs.TaskSpec{
+		CipdPackages: []*specs.CipdPackage{cipdGit1, cipdGit2},
+		Dimensions:   linuxGceDimensions(),
+		ExtraArgs: []string{
+			"--workdir", "../../..", "bundle_recipes",
+			fmt.Sprintf("buildername=%s", BUNDLE_RECIPES_NAME),
+			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
+		},
+		Isolate:  "bundle_recipes.isolate",
+		Priority: 0.7,
+	})
+	return BUNDLE_RECIPES_NAME
+}
+
+// useBundledRecipes returns true iff the given bot should use bundled recipes
+// instead of syncing recipe DEPS itself.
+func useBundledRecipes(parts map[string]string) bool {
+	// Use bundled recipes for all test/perf tasks.
+	return true
+}
+
+type isolateAssetCfg struct {
+	isolateFile string
+	cipdPkg     string
+}
+
+var ISOLATE_ASSET_MAPPING = map[string]isolateAssetCfg{
+	ISOLATE_SKIMAGE_NAME: {
+		isolateFile: "isolate_skimage.isolate",
+		cipdPkg:     "skimage",
+	},
+	ISOLATE_SKP_NAME: {
+		isolateFile: "isolate_skp.isolate",
+		cipdPkg:     "skp",
+	},
+	ISOLATE_SVG_NAME: {
+		isolateFile: "isolate_svg.isolate",
+		cipdPkg:     "svg",
+	},
+}
+
+// bundleRecipes generates the task to bundle and isolate the recipes.
+func isolateCIPDAsset(b *specs.TasksCfgBuilder, name string) string {
+	b.MustAddTask(name, &specs.TaskSpec{
+		CipdPackages: []*specs.CipdPackage{
+			b.MustGetCipdPackageFromAsset(ISOLATE_ASSET_MAPPING[name].cipdPkg),
+		},
+		Dimensions: linuxGceDimensions(),
+		Isolate:    ISOLATE_ASSET_MAPPING[name].isolateFile,
+		Priority:   0.7,
+	})
+	return name
+}
+
+// getIsolatedCIPDDeps returns the slice of Isolate_* tasks a given task needs.
+// This allows us to  save time on I/O bound bots, like the RPIs.
+func getIsolatedCIPDDeps(parts map[string]string) []string {
+	deps := []string{}
+	// Only do this on the RPIs for now. Other, faster machines shouldn't see much
+	// benefit and we don't need the extra complexity, for now
+	rpiOS := []string{"Android", "ChromeOS", "iOS"}
+
+	if o := parts["os"]; strings.Contains(o, "Chromecast") {
+		// Chromecasts don't have enough disk space to fit all of the content,
+		// so we do a subset of the skps.
+		deps = append(deps, ISOLATE_SKP_NAME)
+	} else if e := parts["extra_config"]; strings.Contains(e, "Skpbench") {
+		// Skpbench only needs skps
+		deps = append(deps, ISOLATE_SKP_NAME)
+	} else if util.In(o, rpiOS) {
+		deps = append(deps, ISOLATE_SKP_NAME)
+		deps = append(deps, ISOLATE_SVG_NAME)
+		deps = append(deps, ISOLATE_SKIMAGE_NAME)
+	}
+
+	return deps
+}
+
 // compile generates a compile task. Returns the name of the last task in the
 // generated chain of tasks, which the Job should add as a dependency.
 func compile(b *specs.TasksCfgBuilder, name string, parts map[string]string) string {
@@ -233,6 +344,11 @@
 		}
 	} else if strings.Contains(name, "Chromecast") {
 		pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("cast_toolchain"))
+		pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("chromebook_arm_gles"))
+	} else if strings.Contains(name, "Chromebook") {
+		pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("clang_linux"))
+		pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("armhf_sysroot"))
+		pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("chromebook_arm_gles"))
 	} else if strings.Contains(name, "Ubuntu") {
 		if strings.Contains(name, "Clang") {
 			pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("clang_linux"))
@@ -265,15 +381,12 @@
 		CipdPackages: pkgs,
 		Dimensions:   dimensions,
 		ExtraArgs: []string{
-			"--workdir", "../../..", "swarm_compile",
+			"--workdir", "../../..", "compile",
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -294,19 +407,16 @@
 // dependency.
 func recreateSKPs(b *specs.TasksCfgBuilder, name string) string {
 	b.MustAddTask(name, &specs.TaskSpec{
-		CipdPackages:     []*specs.CipdPackage{},
+		CipdPackages:     []*specs.CipdPackage{b.MustGetCipdPackageFromAsset("go")},
 		Dimensions:       linuxGceDimensions(),
 		ExecutionTimeout: 4 * time.Hour,
 		ExtraArgs: []string{
-			"--workdir", "../../..", "swarm_RecreateSKPs",
+			"--workdir", "../../..", "recreate_skps",
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -318,6 +428,30 @@
 	return name
 }
 
+// updateMetaConfig generates a UpdateMetaConfig task. Returns the name of the
+// last task in the generated chain of tasks, which the Job should add as a
+// dependency.
+func updateMetaConfig(b *specs.TasksCfgBuilder, name string) string {
+	b.MustAddTask(name, &specs.TaskSpec{
+		CipdPackages: []*specs.CipdPackage{},
+		Dimensions:   linuxGceDimensions(),
+		ExtraArgs: []string{
+			"--workdir", "../../..", "update_meta_config",
+			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
+			fmt.Sprintf("buildername=%s", name),
+			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
+			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
+			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
+			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
+			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
+		},
+		Isolate:  "meta_config.isolate",
+		Priority: 0.8,
+	})
+	return name
+}
+
 // ctSKPs generates a CT SKPs task. Returns the name of the last task in the
 // generated chain of tasks, which the Job should add as a dependency.
 func ctSKPs(b *specs.TasksCfgBuilder, name string) string {
@@ -326,15 +460,12 @@
 		Dimensions:       []string{"pool:SkiaCT"},
 		ExecutionTimeout: 24 * time.Hour,
 		ExtraArgs: []string{
-			"--workdir", "../../..", "swarm_ct_skps",
+			"--workdir", "../../..", "ct_skps",
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -354,15 +485,12 @@
 		Dependencies: []string{compileTaskName},
 		Dimensions:   linuxGceDimensions(),
 		ExtraArgs: []string{
-			"--workdir", "../../..", "swarm_housekeeper",
+			"--workdir", "../../..", "housekeeper",
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -377,18 +505,15 @@
 // generated chain of tasks, which the Job should add as a dependency.
 func infra(b *specs.TasksCfgBuilder, name string) string {
 	b.MustAddTask(name, &specs.TaskSpec{
-		CipdPackages: []*specs.CipdPackage{},
+		CipdPackages: []*specs.CipdPackage{b.MustGetCipdPackageFromAsset("go")},
 		Dimensions:   linuxGceDimensions(),
 		ExtraArgs: []string{
-			"--workdir", "../../..", "swarm_infra",
+			"--workdir", "../../..", "infra",
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -423,15 +548,12 @@
 		ExecutionTimeout: 4 * time.Hour,
 		Expiration:       20 * time.Hour,
 		ExtraArgs: []string{
-			"--workdir", "../../..", "swarm_test",
+			"--workdir", "../../..", "test",
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -441,10 +563,22 @@
 		MaxAttempts: 1,
 		Priority:    0.8,
 	}
+	if useBundledRecipes(parts) {
+		s.Dependencies = append(s.Dependencies, BUNDLE_RECIPES_NAME)
+		if strings.Contains(parts["os"], "Win") {
+			s.Isolate = "test_skia_bundled_win.isolate"
+		} else {
+			s.Isolate = "test_skia_bundled_unix.isolate"
+		}
+	}
+	if deps := getIsolatedCIPDDeps(parts); len(deps) > 0 {
+		s.Dependencies = append(s.Dependencies, deps...)
+	}
 	if strings.Contains(parts["extra_config"], "Valgrind") {
 		s.ExecutionTimeout = 9 * time.Hour
 		s.Expiration = 48 * time.Hour
 		s.IoTimeout = time.Hour
+		s.CipdPackages = append(s.CipdPackages, b.MustGetCipdPackageFromAsset("valgrind"))
 	} else if strings.Contains(parts["extra_config"], "MSAN") {
 		s.ExecutionTimeout = 9 * time.Hour
 	}
@@ -460,12 +594,9 @@
 				"--workdir", "../../..", "upload_dm_results",
 				fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 				fmt.Sprintf("buildername=%s", name),
-				"mastername=fake-master",
-				"buildnumber=2",
-				"slavename=fake-buildslave",
-				"nobuildbot=True",
 				fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 				fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+				fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 				fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 				fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 				fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -482,11 +613,24 @@
 // perf generates a Perf task. Returns the name of the last task in the
 // generated chain of tasks, which the Job should add as a dependency.
 func perf(b *specs.TasksCfgBuilder, name string, parts map[string]string, compileTaskName string, pkgs []*specs.CipdPackage) string {
-	recipe := "swarm_perf"
+	recipe := "perf"
 	isolate := "perf_skia.isolate"
 	if strings.Contains(parts["extra_config"], "Skpbench") {
-		recipe = "swarm_skpbench"
+		recipe = "skpbench"
 		isolate = "skpbench_skia.isolate"
+		if useBundledRecipes(parts) {
+			if strings.Contains(parts["os"], "Win") {
+				isolate = "skpbench_skia_bundled_win.isolate"
+			} else {
+				isolate = "skpbench_skia_bundled_unix.isolate"
+			}
+		}
+	} else if useBundledRecipes(parts) {
+		if strings.Contains(parts["os"], "Win") {
+			isolate = "perf_skia_bundled_win.isolate"
+		} else {
+			isolate = "perf_skia_bundled_unix.isolate"
+		}
 	}
 	s := &specs.TaskSpec{
 		CipdPackages:     pkgs,
@@ -498,12 +642,9 @@
 			"--workdir", "../../..", recipe,
 			fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 			fmt.Sprintf("buildername=%s", name),
-			"mastername=fake-master",
-			"buildnumber=2",
-			"slavename=fake-buildslave",
-			"nobuildbot=True",
 			fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 			fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+			fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 			fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 			fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 			fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -513,10 +654,18 @@
 		MaxAttempts: 1,
 		Priority:    0.8,
 	}
+	if useBundledRecipes(parts) {
+		s.Dependencies = append(s.Dependencies, BUNDLE_RECIPES_NAME)
+	}
+	if deps := getIsolatedCIPDDeps(parts); len(deps) > 0 {
+		s.Dependencies = append(s.Dependencies, deps...)
+	}
+
 	if strings.Contains(parts["extra_config"], "Valgrind") {
 		s.ExecutionTimeout = 9 * time.Hour
 		s.Expiration = 48 * time.Hour
 		s.IoTimeout = time.Hour
+		s.CipdPackages = append(s.CipdPackages, b.MustGetCipdPackageFromAsset("valgrind"))
 	} else if strings.Contains(parts["extra_config"], "MSAN") {
 		s.ExecutionTimeout = 9 * time.Hour
 	}
@@ -532,12 +681,9 @@
 				"--workdir", "../../..", "upload_nano_results",
 				fmt.Sprintf("repository=%s", specs.PLACEHOLDER_REPO),
 				fmt.Sprintf("buildername=%s", name),
-				"mastername=fake-master",
-				"buildnumber=2",
-				"slavename=fake-buildslave",
-				"nobuildbot=True",
 				fmt.Sprintf("swarm_out_dir=%s", specs.PLACEHOLDER_ISOLATED_OUTDIR),
 				fmt.Sprintf("revision=%s", specs.PLACEHOLDER_REVISION),
+				fmt.Sprintf("patch_repo=%s", specs.PLACEHOLDER_PATCH_REPO),
 				fmt.Sprintf("patch_storage=%s", specs.PLACEHOLDER_PATCH_STORAGE),
 				fmt.Sprintf("patch_issue=%s", specs.PLACEHOLDER_ISSUE),
 				fmt.Sprintf("patch_set=%s", specs.PLACEHOLDER_PATCHSET),
@@ -555,6 +701,16 @@
 func process(b *specs.TasksCfgBuilder, name string) {
 	deps := []string{}
 
+	// Bundle Recipes.
+	if name == BUNDLE_RECIPES_NAME {
+		deps = append(deps, bundleRecipes(b))
+	}
+
+	// Isolate CIPD assets.
+	if _, ok := ISOLATE_ASSET_MAPPING[name]; ok {
+		deps = append(deps, isolateCIPDAsset(b, name))
+	}
+
 	parts, err := jobNameSchema.ParseJobName(name)
 	if err != nil {
 		glog.Fatal(err)
@@ -565,6 +721,11 @@
 		deps = append(deps, recreateSKPs(b, name))
 	}
 
+	// UpdateMetaConfig bot.
+	if strings.Contains(name, "UpdateMetaConfig") {
+		deps = append(deps, updateMetaConfig(b, name))
+	}
+
 	// CT bots.
 	if strings.Contains(name, "-CT_") {
 		deps = append(deps, ctSKPs(b, name))
@@ -588,8 +749,10 @@
 	}
 	// These bots do not need a compile task.
 	if parts["role"] != "Build" &&
+		name != "Housekeeper-PerCommit-BundleRecipes" &&
 		name != "Housekeeper-PerCommit-InfraTests" &&
 		!strings.Contains(name, "RecreateSKPs") &&
+		!strings.Contains(name, "UpdateMetaConfig") &&
 		!strings.Contains(name, "-CT_") {
 		compile(b, compileTaskName, compileTaskParts)
 	}
@@ -600,18 +763,17 @@
 	}
 
 	// Common assets needed by the remaining bots.
-	pkgs := []*specs.CipdPackage{
-		b.MustGetCipdPackageFromAsset("skimage"),
-		b.MustGetCipdPackageFromAsset("skp"),
-		b.MustGetCipdPackageFromAsset("svg"),
-	}
-	if strings.Contains(name, "Chromecast") {
-		// Chromecasts don't have enough disk space to fit all of the content,
-		// so we do a subset of the skps.
+
+	pkgs := []*specs.CipdPackage{}
+
+	if deps := getIsolatedCIPDDeps(parts); len(deps) == 0 {
 		pkgs = []*specs.CipdPackage{
+			b.MustGetCipdPackageFromAsset("skimage"),
 			b.MustGetCipdPackageFromAsset("skp"),
+			b.MustGetCipdPackageFromAsset("svg"),
 		}
 	}
+
 	if strings.Contains(name, "Ubuntu") && strings.Contains(name, "SAN") {
 		pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("clang_linux"))
 	}
@@ -625,12 +787,6 @@
 			pkgs = append(pkgs, b.MustGetCipdPackageFromAsset("linux_vulkan_intel_driver_debug"))
 		}
 	}
-	// Skpbench only needs skps
-	if strings.Contains(name, "Skpbench") {
-		pkgs = []*specs.CipdPackage{
-			b.MustGetCipdPackageFromAsset("skp"),
-		}
-	}
 
 	// Test bots.
 	if parts["role"] == "Test" && !strings.Contains(name, "-CT_") {
@@ -650,6 +806,9 @@
 	if name == "Housekeeper-Nightly-RecreateSKPs_Canary" {
 		j.Trigger = "nightly"
 	}
+	if name == "Housekeeper-Nightly-UpdateMetaConfig" {
+		j.Trigger = "nightly"
+	}
 	if name == "Housekeeper-Weekly-RecreateSKPs" {
 		j.Trigger = "weekly"
 	}
diff --git a/infra/bots/gpu_map.json b/infra/bots/gpu_map.json
index baa13d3..a841a34 100644
--- a/infra/bots/gpu_map.json
+++ b/infra/bots/gpu_map.json
@@ -1,17 +1,22 @@
 {
   "AMDHD7770":     "1002:683d",
-  "GeForce320M":   "10de:08a4",
   "GT610":         "10de:104a",
   "GTX1070":       "10de:1ba1",
   "GTX550Ti":      "10de:1244",
   "GTX660":        "10de:11c0",
   "GTX960":        "10de:1401",
-  "HD4000":        "8086:0a2e",
-  "iHD530":        "8086:1912",
+  "IntelHD4000":   "8086:0a2e",
+  "IntelHD530":    "8086:1912",
   "IntelBayTrail": "8086:0f31",
+  "IntelHD2000":   "8086:0102",
   "IntelHD405":    "8086:22b1",
+  "IntelHD4400":   "8086:0a16",
   "IntelHD4600":   "8086:0412",
   "IntelIris540":  "8086:1926",
   "IntelIris6100": "8086:162b",
-  "RadeonR9M470X": "1002:6646"
+  "MaliT604":      "MaliT604",
+  "MaliT764":      "MaliT764",
+  "MaliT860":      "MaliT860",
+  "RadeonR9M470X": "1002:6646",
+  "TegraK1":       "TegraK1"
 }
diff --git a/infra/bots/infra_tests.py b/infra/bots/infra_tests.py
index 779ea8d..a5a26e4 100755
--- a/infra/bots/infra_tests.py
+++ b/infra/bots/infra_tests.py
@@ -25,20 +25,26 @@
     return e.output
 
 
-def python_unit_tests():
+def python_unit_tests(train):
+  if train:
+    return None
   return test(
       ['python', '-m', 'unittest', 'discover', '-s', '.', '-p', '*_test.py'],
       INFRA_BOTS_DIR)
 
 
-def recipe_simulation_test():
-  return test(
-      ['python', os.path.join(INFRA_BOTS_DIR, 'recipes.py'), 'simulation_test'],
-      SKIA_DIR)
+def recipe_test(train):
+  cmd = [
+      'python', os.path.join(INFRA_BOTS_DIR, 'recipes.py'), 'test', 'run']
+  if train:
+    cmd.append('--train')
+  return test(cmd, SKIA_DIR)
 
 
-def gen_tasks_test():
-  cmd = ['go', 'run', 'gen_tasks.go', '--test']
+def gen_tasks_test(train):
+  cmd = ['go', 'run', 'gen_tasks.go']
+  if not train:
+    cmd.append('--test')
   try:
     output = test(cmd, INFRA_BOTS_DIR)
   except OSError:
@@ -51,14 +57,18 @@
 
 
 def main():
+  train = False
+  if '--train' in sys.argv:
+    train = True
+
   tests = (
       python_unit_tests,
-      recipe_simulation_test,
+      recipe_test,
       gen_tasks_test,
   )
   errs = []
   for t in tests:
-    err = t()
+    err = t(train)
     if err:
       errs.append(err)
 
@@ -70,7 +80,10 @@
       print >> sys.stderr, '=============================='
     sys.exit(1)
 
-  print 'All tests passed!'
+  if train:
+    print 'Trained tests successfully.'
+  else:
+    print 'All tests passed!'
 
 
 if __name__ == '__main__':
diff --git a/infra/bots/ios_bin.isolate b/infra/bots/ios_bin.isolate
index 524dad2..1994eb6 100644
--- a/infra/bots/ios_bin.isolate
+++ b/infra/bots/ios_bin.isolate
@@ -8,5 +8,13 @@
         ],
       },
     }],
+    ['OS=="iOS-10.3.1"', {
+      'variables': {
+        'files': [
+          '../../platform_tools/ios/bin/',
+          '../../gn/package_ios.py',
+        ],
+      },
+    }],
   ],
 }
diff --git a/infra/bots/isolate_skimage.isolate b/infra/bots/isolate_skimage.isolate
new file mode 100644
index 0000000..36066be
--- /dev/null
+++ b/infra/bots/isolate_skimage.isolate
@@ -0,0 +1,7 @@
+{
+  'variables': {
+    'command': [
+      '/bin/cp', '-rL', 'skimage', '${ISOLATED_OUTDIR}',
+    ],
+  },
+}
diff --git a/infra/bots/isolate_skp.isolate b/infra/bots/isolate_skp.isolate
new file mode 100644
index 0000000..d632dff
--- /dev/null
+++ b/infra/bots/isolate_skp.isolate
@@ -0,0 +1,7 @@
+{
+  'variables': {
+    'command': [
+      '/bin/cp', '-rL', 'skp', '${ISOLATED_OUTDIR}',
+    ],
+  },
+}
diff --git a/infra/bots/isolate_svg.isolate b/infra/bots/isolate_svg.isolate
new file mode 100644
index 0000000..e04c4e3
--- /dev/null
+++ b/infra/bots/isolate_svg.isolate
@@ -0,0 +1,7 @@
+{
+  'variables': {
+    'command': [
+      '/bin/cp', '-rL', 'svg', '${ISOLATED_OUTDIR}',
+    ],
+  },
+}
diff --git a/infra/bots/jobs.json b/infra/bots/jobs.json
index 98516f2..617b56a 100644
--- a/infra/bots/jobs.json
+++ b/infra/bots/jobs.json
@@ -10,7 +10,9 @@
   "Build-Mac-Clang-x86_64-Release",
   "Build-Mac-Clang-x86_64-Release-CommandBuffer",
   "Build-Ubuntu-Clang-arm-Debug-Android",
+  "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
   "Build-Ubuntu-Clang-arm-Release-Android",
+  "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
   "Build-Ubuntu-Clang-arm64-Debug-Android",
   "Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs",
   "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
@@ -36,6 +38,9 @@
   "Build-Ubuntu-Clang-x86_64-Release-Mini",
   "Build-Ubuntu-Clang-x86_64-Release-TSAN",
   "Build-Ubuntu-Clang-x86_64-Release-Vulkan",
+  "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2",
+  "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41",
+  "Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
   "Build-Ubuntu-GCC-arm-Debug-Chromecast",
   "Build-Ubuntu-GCC-arm-Release-Chromecast",
   "Build-Ubuntu-GCC-x86-Debug",
@@ -69,13 +74,16 @@
   "Build-Win-MSVC-x86_64-Release-GDI",
   "Build-Win-MSVC-x86_64-Release-Vulkan",
   "Housekeeper-Nightly-RecreateSKPs_Canary",
+  "Housekeeper-Nightly-UpdateMetaConfig",
   "Housekeeper-PerCommit",
+  "Housekeeper-PerCommit-BundleRecipes",
   "Housekeeper-PerCommit-InfraTests",
+  "Housekeeper-PerCommit-IsolateSkImage",
+  "Housekeeper-PerCommit-IsolateSKP",
+  "Housekeeper-PerCommit-IsolateSVG",
   "Housekeeper-Weekly-RecreateSKPs",
   "Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android",
   "Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android",
-  "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android",
-  "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android",
   "Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android",
   "Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android",
   "Perf-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android",
@@ -103,6 +111,8 @@
   "Perf-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android",
   "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android",
   "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android",
+  "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
+  "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android_Vulkan",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android",
@@ -111,21 +121,28 @@
   "Perf-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android",
   "Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench",
   "Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench",
-  "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug",
-  "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release",
-  "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug",
-  "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release",
+  "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug",
+  "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release",
+  "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug",
+  "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release",
+  "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug",
+  "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release",
+  "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug",
+  "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release",
+  "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug",
+  "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release",
   "Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
   "Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release",
-  "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug",
-  "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release",
-  "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer",
+  "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug",
+  "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release",
+  "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer",
   "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug",
   "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN",
   "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN",
   "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release",
   "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN",
   "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast",
+  "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
   "Perf-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Debug-ASAN",
   "Perf-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Release-ASAN",
   "Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
@@ -148,6 +165,8 @@
   "Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
   "Perf-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
   "Perf-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release",
+  "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug",
+  "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release",
   "Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug",
   "Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-ANGLE",
   "Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
@@ -165,6 +184,10 @@
   "Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
   "Perf-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Debug",
   "Perf-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release",
+  "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug",
+  "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE",
+  "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release",
+  "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE",
   "Perf-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug",
   "Perf-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Release",
   "Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug",
@@ -187,19 +210,30 @@
   "Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug",
   "Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI",
   "Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
-  "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug",
-  "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release",
+  "Perf-Win7-MSVC-Golo-CPU-AVX-x86-Debug",
+  "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug",
+  "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+  "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release",
+  "Perf-Win8-MSVC-Golo-CPU-AVX-x86-Debug",
+  "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug",
+  "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+  "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release",
+  "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug",
+  "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release",
+  "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug",
+  "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release",
+  "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug",
+  "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release",
   "Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android",
   "Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android",
-  "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android",
-  "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android",
+  "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android",
+  "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android",
   "Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android",
   "Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android",
   "Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android",
   "Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android",
   "Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-Android",
   "Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android",
-  "Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android",
   "Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android",
   "Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android",
   "Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android",
@@ -230,13 +264,19 @@
   "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan",
   "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android",
   "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android_Vulkan",
-  "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug",
-  "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release",
+  "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug",
+  "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release",
+  "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug",
+  "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release",
+  "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug",
+  "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release",
+  "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug",
+  "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release",
   "Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
   "Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release",
-  "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug",
-  "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer",
-  "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release",
+  "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug",
+  "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer",
+  "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release",
   "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug",
   "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN",
   "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN",
@@ -244,9 +284,13 @@
   "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN",
   "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast",
   "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN",
+  "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2",
+  "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41",
+  "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
   "Test-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Debug-ASAN",
   "Test-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Release-ASAN",
   "Test-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Release-TSAN",
+  "Test-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-PreAbandonGpuContext",
   "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
   "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
   "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
@@ -270,6 +314,8 @@
   "Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
   "Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
   "Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release",
+  "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug",
+  "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release",
   "Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug",
   "Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-ANGLE",
   "Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
@@ -279,6 +325,7 @@
   "Test-Win10-MSVC-Golo-GPU-GT610-x86_64-Release",
   "Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Debug-ANGLE",
   "Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ANGLE",
+  "Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext",
   "Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
   "Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE",
   "Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
@@ -287,6 +334,10 @@
   "Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
   "Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Debug",
   "Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release",
+  "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug",
+  "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE",
+  "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release",
+  "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE",
   "Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug",
   "Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Release",
   "Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug",
@@ -310,6 +361,22 @@
   "Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI",
   "Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
   "Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release-GDI",
-  "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug",
-  "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release"
+  "Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug",
+  "Test-Win7-MSVC-Golo-CPU-AVX-x86-Release",
+  "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug",
+  "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+  "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release",
+  "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI",
+  "Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug",
+  "Test-Win8-MSVC-Golo-CPU-AVX-x86-Release",
+  "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug",
+  "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+  "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release",
+  "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI",
+  "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug",
+  "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release",
+  "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug",
+  "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release",
+  "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug",
+  "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release"
 ]
diff --git a/infra/bots/meta_config.isolate b/infra/bots/meta_config.isolate
new file mode 100644
index 0000000..d4e1484
--- /dev/null
+++ b/infra/bots/meta_config.isolate
@@ -0,0 +1,5 @@
+{
+  'includes': [
+    'swarm_recipe.isolate',
+  ],
+}
diff --git a/infra/bots/perf_skia_bundled.isolate b/infra/bots/perf_skia_bundled.isolate
new file mode 100644
index 0000000..dbfe4c3
--- /dev/null
+++ b/infra/bots/perf_skia_bundled.isolate
@@ -0,0 +1,14 @@
+{
+  'includes': [
+    'android_bin.isolate',
+    'assets.isolate',
+    'ios_bin.isolate',
+    'resources.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../../.gclient',
+      '../../tools/valgrind.supp',
+    ],
+  },
+}
diff --git a/infra/bots/perf_skia_bundled_unix.isolate b/infra/bots/perf_skia_bundled_unix.isolate
new file mode 100644
index 0000000..5bb5ae1
--- /dev/null
+++ b/infra/bots/perf_skia_bundled_unix.isolate
@@ -0,0 +1,6 @@
+{
+  'includes': [
+    'perf_skia_bundled.isolate',
+    'swarm_recipe_bundled_unix.isolate',
+  ],
+}
diff --git a/infra/bots/perf_skia_bundled_win.isolate b/infra/bots/perf_skia_bundled_win.isolate
new file mode 100644
index 0000000..8f42593
--- /dev/null
+++ b/infra/bots/perf_skia_bundled_win.isolate
@@ -0,0 +1,6 @@
+{
+  'includes': [
+    'perf_skia_bundled.isolate',
+    'swarm_recipe_bundled_win.isolate',
+  ],
+}
diff --git a/infra/bots/recipe_modules/README.md b/infra/bots/recipe_modules/README.md
index 05ad2cc..5093ff0 100644
--- a/infra/bots/recipe_modules/README.md
+++ b/infra/bots/recipe_modules/README.md
@@ -1,11 +1,35 @@
 Skia Recipe Modules
 ===================
 
-This directory contains recipe modules designed to be used by recipes. They
-are all Skia-specific and some are interrelated:
+This directory contains recipe modules designed to be used by recipes (see
+infra/bots/recipes). They are all Skia-specific and some are interrelated:
 
-  * vars - Common variables used by Skia recipes.
-  * run - Utilities for running commands. Depends on vars.
-  * flavor - Run meta-commands for various platforms. Depends on vars and run.
-  * skia - Main module for Skia recipes. Depends on vars, run, and flavor.
+  * builder_name_schema - Helps to derive expected behavior from task (formerly
+      builder) names.
+  * core - Use as a starting point for most recipes: runs setup and sync steps.
+  * ct - Shared Cluster Telemetry utilities.
+  * flavor - Allows the caller to specify a high-level command to run, leaving
+      the platform-specific details to be handled by the specific flavor
+      module.
+  * infra - Shared infrastructure-related utilities.
+  * run - Utilities for running commands.
   * swarming - Utilities for running Swarming tasks.
+  * vars - Common global variables used by Skia recipes/modules.
+
+When you change a recipe module, you generally need to re-train the simulation
+test:
+
+	$ python infra/bots/recipes.py test run --train
+
+Or:
+
+	$ cd infra/bots; make train
+
+Each recipe module contains a few files:
+
+  * api.py - This is the meat of the module.
+  * \_\_init\_\_.py - Contains a single DEPS variable, indicating the other
+      recipe modules on which this module depends.
+  * example.py - Optional, this file contains examples which demonstrate how to
+      use the module and should contain enough tests to achieve 100% coverage
+      for the module. The tests are run using the recipes test command above.
diff --git a/infra/bots/recipe_modules/builder_name_schema/__init__.py b/infra/bots/recipe_modules/builder_name_schema/__init__.py
index 0b1d767..a3c9297 100644
--- a/infra/bots/recipe_modules/builder_name_schema/__init__.py
+++ b/infra/bots/recipe_modules/builder_name_schema/__init__.py
@@ -4,7 +4,3 @@
 
 DEPS = [
 ]
-
-
-# TODO(borenet): Add coverage
-DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/builder_name_schema/api.py b/infra/bots/recipe_modules/builder_name_schema/api.py
index edfd683..5ce0584 100644
--- a/infra/bots/recipe_modules/builder_name_schema/api.py
+++ b/infra/bots/recipe_modules/builder_name_schema/api.py
@@ -27,13 +27,8 @@
     self.BUILDER_ROLE_TEST = builder_name_schema.BUILDER_ROLE_TEST
     self.BUILDER_ROLES = builder_name_schema.BUILDER_ROLES
 
-    self.TRYBOT_NAME_SUFFIX = builder_name_schema.TRYBOT_NAME_SUFFIX
-
-  def MakeBuilderName(self, *args, **kwargs):  # pragma: no cover
+  def MakeBuilderName(self, *args, **kwargs):
     return builder_name_schema.MakeBuilderName(*args, **kwargs)
 
-  def IsTrybot(self, *args, **kwargs):  # pragma: no cover
-    return builder_name_schema.IsTrybot(*args, **kwargs)
-
   def DictForBuilderName(self, *args, **kwargs):
     return builder_name_schema.DictForBuilderName(*args, **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 6a8c921..62971ee 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
@@ -38,6 +38,5 @@
       "configuration"
     ]
   },
-  "builder_name_sep": "-",
-  "trybot_name_suffix": "Trybot"
+  "builder_name_sep": "-"
 }
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 8319789..2c875ba 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
@@ -33,9 +33,6 @@
                  BUILDER_ROLE_PERF,
                  BUILDER_ROLE_TEST)
 
-# Suffix which distinguishes trybots from normal bots.
-TRYBOT_NAME_SUFFIX = None
-
 
 def _LoadSchema():
   """ Load the builder naming schema from the JSON file. """
@@ -50,8 +47,6 @@
       return list(map(_UnicodeToStr, obj))
     elif isinstance(obj, tuple):
       return tuple(map(_UnicodeToStr, obj))
-    else:
-      return obj  # pragma: no cover
 
   builder_name_json_filename = os.path.join(
       os.path.dirname(__file__), 'builder_name_schema.json')
@@ -65,10 +60,6 @@
   BUILDER_NAME_SEP = _UnicodeToStr(
       builder_name_schema_json['builder_name_sep'])
 
-  global TRYBOT_NAME_SUFFIX
-  TRYBOT_NAME_SUFFIX = _UnicodeToStr(
-      builder_name_schema_json['trybot_name_suffix'])
-
   # Since the builder roles are dictionary keys, just assert that the global
   # variables above account for all of them.
   assert len(BUILDER_ROLES) == len(BUILDER_NAME_SCHEMA)
@@ -79,63 +70,25 @@
 _LoadSchema()
 
 
-def MakeBuilderName(role, extra_config=None, is_trybot=False,
-                    **kwargs):  # pragma: no cover
+def MakeBuilderName(role, extra_config=None, **kwargs):
   schema = BUILDER_NAME_SCHEMA.get(role)
-  if not schema:  # pragma: no cover
+  if not schema:
     raise ValueError('%s is not a recognized role.' % role)
   for k, v in kwargs.iteritems():
-    if BUILDER_NAME_SEP in v:  # pragma: no cover
+    if BUILDER_NAME_SEP in v:
       raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, v))
-    if not k in schema:  # pragma: no cover
+    if not k in schema:
       raise ValueError('Schema does not contain "%s": %s' %(k, schema))
-  if extra_config and BUILDER_NAME_SEP in extra_config:  # pragma: no cover
+  if extra_config and BUILDER_NAME_SEP in extra_config:
     raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP,
                                                 extra_config))
   name_parts = [role]
   name_parts.extend([kwargs[attribute] for attribute in schema])
   if extra_config:
     name_parts.append(extra_config)
-  if is_trybot:
-    name_parts.append(TRYBOT_NAME_SUFFIX)
   return BUILDER_NAME_SEP.join(name_parts)
 
 
-def IsTrybot(builder_name):  # pragma: no cover
-  """ Returns true if builder_name refers to a trybot (as opposed to a
-  waterfall bot). """
-  return builder_name.endswith(TRYBOT_NAME_SUFFIX)
-
-
-def GetWaterfallBot(builder_name):  # pragma: no cover
-  """Returns the name of the waterfall bot for this builder. If it is not a
-  trybot, builder_name is returned unchanged. If it is a trybot the name is
-  returned without the trybot suffix."""
-  if not IsTrybot(builder_name):
-    return builder_name
-  return _WithoutSuffix(builder_name, BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX)
-
-
-def TrybotName(builder_name):  # pragma: no cover
-  """Returns the name of the trybot clone of this builder.
-
-  If the given builder is a trybot, the name is returned unchanged. If not, the
-  TRYBOT_NAME_SUFFIX is appended.
-  """
-  if builder_name.endswith(TRYBOT_NAME_SUFFIX):
-    return builder_name
-  return builder_name + BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX
-
-
-def _WithoutSuffix(string, suffix):  # pragma: no cover
-  """ Returns a copy of string 'string', but with suffix 'suffix' removed.
-  Raises ValueError if string does not end with suffix. """
-  if not string.endswith(suffix):
-    raise ValueError('_WithoutSuffix: string %s does not end with suffix %s' % (
-        string, suffix))
-  return string[:-len(suffix)]
-
-
 def DictForBuilderName(builder_name):
   """Makes a dictionary containing details about the builder from its name."""
   split_name = builder_name.split(BUILDER_NAME_SEP)
@@ -143,15 +96,10 @@
   def pop_front():
     try:
       return split_name.pop(0)
-    except:  # pragma: no cover
+    except:
       raise ValueError('Invalid builder name: %s' % builder_name)
 
-  result = {'is_trybot': False}
-
-  if split_name[-1] == TRYBOT_NAME_SUFFIX:
-    result['is_trybot'] = True
-    split_name.pop()
-
+  result = {}
   if split_name[0] in BUILDER_NAME_SCHEMA.keys():
     key_list = BUILDER_NAME_SCHEMA[split_name[0]]
     result['role'] = pop_front()
@@ -159,9 +107,9 @@
       result[key] = pop_front()
     if split_name:
       result['extra_config'] = pop_front()
-    if split_name:  # pragma: no cover
+    if split_name:
       raise ValueError('Invalid builder name: %s' % builder_name)
-  else:  # pragma: no cover
+  else:
     raise ValueError('Invalid builder name: %s' % builder_name)
   return result
 
diff --git a/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json b/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json
new file mode 100644
index 0000000..7726ecb
--- /dev/null
+++ b/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json
@@ -0,0 +1,7 @@
+[
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/builder_name_schema/examples/full.py b/infra/bots/recipe_modules/builder_name_schema/examples/full.py
new file mode 100644
index 0000000..a068b42
--- /dev/null
+++ b/infra/bots/recipe_modules/builder_name_schema/examples/full.py
@@ -0,0 +1,64 @@
+# 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 = [
+  'builder_name_schema',
+]
+
+
+def RunSteps(api):
+  name = 'Build-Ubuntu-Clang-x64-Release-Android'
+  d = api.builder_name_schema.DictForBuilderName(name)
+  got = api.builder_name_schema.MakeBuilderName(**d)
+  assert got == name
+
+  # Failures.
+  try:
+    api.builder_name_schema.MakeBuilderName('nope')
+  except ValueError:
+    pass
+
+  try:
+    api.builder_name_schema.MakeBuilderName(
+        role='Build', os='a%sb' % api.builder_name_schema.BUILDER_NAME_SEP)
+  except ValueError:
+    pass
+
+  try:
+    api.builder_name_schema.MakeBuilderName(role='Build', bogus='BOGUS')
+  except ValueError:
+    pass
+
+  try:
+    api.builder_name_schema.MakeBuilderName(
+        role='Build',
+        os='Ubuntu',
+        compiler='Clang',
+        target_arch='x64',
+        configuration='Release',
+        extra_config='A%sB' % api.builder_name_schema.BUILDER_NAME_SEP)
+  except ValueError:
+    pass
+
+  try:
+    api.builder_name_schema.DictForBuilderName('Build-')
+  except ValueError:
+    pass
+
+  try:
+    api.builder_name_schema.DictForBuilderName(
+        'Build-Ubuntu-Clang-x64-Release-Android-Bogus')
+  except ValueError:
+    pass
+
+  try:
+    api.builder_name_schema.DictForBuilderName(
+        'Bogus-Ubuntu-Clang-x64-Release-Android')
+  except ValueError:
+    pass
+
+
+def GenTests(api):
+  yield api.test('test')
diff --git a/infra/bots/recipe_modules/compile/__init__.py b/infra/bots/recipe_modules/compile/__init__.py
deleted file mode 100644
index 0021aea..0000000
--- a/infra/bots/recipe_modules/compile/__init__.py
+++ /dev/null
@@ -1,16 +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.
-
-DEPS = [
-  'core',
-  'recipe_engine/json',
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/python',
-  'recipe_engine/step',
-  'flavor',
-  'run',
-  'vars',
-]
diff --git a/infra/bots/recipe_modules/compile/api.py b/infra/bots/recipe_modules/compile/api.py
deleted file mode 100644
index 917b228..0000000
--- a/infra/bots/recipe_modules/compile/api.py
+++ /dev/null
@@ -1,64 +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 compile.
-
-
-from recipe_engine import recipe_api
-
-
-def build_targets_from_builder_dict(builder_dict):
-  """Return a list of targets to build, depending on the builder type."""
-  if builder_dict.get('extra_config') == 'iOS':
-    return ['iOSShell']
-  return ['most']
-
-
-def get_extra_env_vars(builder_dict):
-  env = {}
-  if builder_dict.get('compiler') == 'Clang':
-    env['CC'] = '/usr/bin/clang'
-    env['CXX'] = '/usr/bin/clang++'
-
-  # SKNX_NO_SIMD, SK_USE_DISCARDABLE_SCALEDIMAGECACHE, etc.
-  extra_config = builder_dict.get('extra_config', '')
-  if extra_config.startswith('SK') and extra_config.isupper():
-    env['CPPFLAGS'] = '-D' + extra_config
-
-  return env
-
-
-class CompileApi(recipe_api.RecipeApi):
-  def run(self):
-    self.m.core.setup()
-
-    env = get_extra_env_vars(self.m.vars.builder_cfg)
-    build_targets = build_targets_from_builder_dict(self.m.vars.builder_cfg)
-
-    try:
-      for target in build_targets:
-        with self.m.step.context({'env': env}):
-          self.m.flavor.compile(target)
-      self.m.run.copy_build_products(
-          self.m.flavor.out_dir,
-          self.m.vars.swarming_out_dir.join(
-              'out', self.m.vars.configuration))
-      self.m.flavor.copy_extra_build_products(self.m.vars.swarming_out_dir)
-    finally:
-      if 'Win' in self.m.vars.builder_cfg.get('os', ''):
-        self.m.python.inline(
-            name='cleanup',
-            program='''import psutil
-for p in psutil.process_iter():
-  try:
-    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):
-      p.kill()
-  except psutil._error.AccessDenied:
-    pass
-''',
-            infra_step=True)
-
-    self.m.flavor.cleanup_steps()
-    self.m.run.check_failure()
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-arm64-Debug-iOS.json b/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-arm64-Debug-iOS.json
deleted file mode 100644
index e87319b..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-arm64-Debug-iOS.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug",
-      "--args=cc=\"clang\" cxx=\"clang++\" extra_cflags=[\"-O1\"] target_cpu=\"arm64\" target_os=\"ios\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-mipsel-Debug-GN_Android.json b/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-mipsel-Debug-GN_Android.json
deleted file mode 100644
index fac2e5f..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-mipsel-Debug-GN_Android.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-mipsel-Debug-GN_Android"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-mipsel-Debug-GN_Android/Debug",
-      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_darwin\" target_cpu=\"mipsel\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-mipsel-Debug-GN_Android"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-mipsel-Debug-GN_Android/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-mipsel-Debug-GN_Android"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-mipsel-Debug-GN_Android/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x64-Release-iOS.json b/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x64-Release-iOS.json
deleted file mode 100644
index 830949e..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x64-Release-iOS.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release",
-      "--args=cc=\"clang\" cxx=\"clang++\" is_debug=false target_cpu=\"x64\" target_os=\"ios\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json b/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
deleted file mode 100644
index 95e3292..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
+++ /dev/null
@@ -1,214 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--revision",
-      "src@origin/lkgr",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
-      "@@@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@  \"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",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
-      "runhooks"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GYP_CHROMIUM_NO_ACTION": "0",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]"
-    },
-    "name": "gclient runhooks"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/tools/build_command_buffer.py",
-      "--chrome-dir",
-      "[CUSTOM_/_B_WORK]",
-      "--output-dir",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
-      "--no-sync",
-      "--make-output-dir"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
-    },
-    "name": "build command_buffer"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
-      "--args=cc=\"clang\" cxx=\"clang++\" extra_cflags=[\"-O1\"] target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x86_64-Release-GN.json b/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x86_64-Release-GN.json
deleted file mode 100644
index 4852df6..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Mac-Clang-x86_64-Release-GN.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release-GN"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release-GN/Release",
-      "--args=cc=\"clang\" cxx=\"clang++\" is_debug=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release-GN"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release-GN/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release-GN"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release-GN/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot.json
deleted file mode 100644
index 580bee6..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot.json
+++ /dev/null
@@ -1,181 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--issue",
-      "500",
-      "--patchset",
-      "1",
-      "--rietveld_server",
-      "https://codereview.chromium.org",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot/Debug",
-      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"arm64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs.json
deleted file mode 100644
index 89ab1a2..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs/Debug",
-      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" skia_enable_android_framework_defines=true target_cpu=\"arm64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Release-GN_Android.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Release-GN_Android.json
deleted file mode 100644
index 5e9b82a..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Release-GN_Android.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android/Release",
-      "--args=is_debug=false ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"arm64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan.json
deleted file mode 100644
index f51b38a..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan/Release",
-      "--args=is_debug=false ndk=\"[START_DIR]/android_ndk_linux\" ndk_api=24 skia_enable_vulkan_debug_layers=false target_cpu=\"arm64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Debug-ASAN.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Debug-ASAN.json
deleted file mode 100644
index 4953d23..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Debug-ASAN.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN/Debug",
-      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-O1\"] extra_ldflags=[\"-fuse-ld=lld\"] sanitize=\"ASAN\" skia_enable_spirv_validation=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Debug-GN.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Debug-GN.json
deleted file mode 100644
index 337d684..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Debug-GN.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-GN"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-GN/Debug",
-      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-O1\"] extra_ldflags=[\"-fuse-ld=lld\"] target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-GN"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-GN/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-GN"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-GN/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json
deleted file mode 100644
index 8cf5550..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release",
-      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_ldflags=[\"-fuse-ld=lld\"] is_component_build=true is_debug=false is_official_build=true skia_enable_effects=false skia_enable_gpu=false skia_enable_pdf=false skia_use_expat=false skia_use_libjpeg_turbo=false skia_use_libpng=false skia_use_libwebp=false skia_use_zlib=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json
deleted file mode 100644
index bb048a9..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release",
-      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_ldflags=[\"-fuse-ld=lld\"] is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/linux_vulkan_sdk\" target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-arm-Release-Chromecast.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-arm-Release-Chromecast.json
deleted file mode 100644
index 03e9393..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-arm-Release-Chromecast.json
+++ /dev/null
@@ -1,170 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast/Release",
-      "--args=ar=\"[START_DIR]/cast_toolchain/bin/armv7a-cros-linux-gnueabi-ar\" cc=\"[START_DIR]/cast_toolchain/bin/armv7a-cros-linux-gnueabi-gcc\" cxx=\"[START_DIR]/cast_toolchain/bin/armv7a-cros-linux-gnueabi-g++\" extra_cflags=[\"-g0\"] extra_ldflags=[\"-static-libstdc++\", \"-static-libgcc\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_icu=false skia_use_system_freetype2=false target_cpu=\"arm\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast/Release",
-      "nanobench"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86-Debug.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86-Debug.json
deleted file mode 100644
index 63b45a2..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86-Debug.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] target_cpu=\"x86\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-GN.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-GN.json
deleted file mode 100644
index 673cedc..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-GN.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-MSAN.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-MSAN.json
deleted file mode 100644
index 05368fc..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-MSAN.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] extra_ldflags=[\"-L[START_DIR]/clang_linux/msan\"] sanitize=\"MSAN\" skia_use_fontconfig=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-NoGPU.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-NoGPU.json
deleted file mode 100644
index 27f99ba..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-NoGPU.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] skia_enable_gpu=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
deleted file mode 100644
index 0be09bd..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
+++ /dev/null
@@ -1,172 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "CPPFLAGS": "-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\", \"-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE\"] target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "CPPFLAGS": "-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "CPPFLAGS": "-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json
deleted file mode 100644
index daf8db1..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_angle=true target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json
deleted file mode 100644
index 2639a71..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-march=native\", \"-fomit-frame-pointer\", \"-O3\", \"-ffp-contract=off\"] is_debug=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
deleted file mode 100644
index de42588f..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
+++ /dev/null
@@ -1,193 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/flutter",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'src/flutter', 'url': 'https://github.com/flutter/engine.git'}]\ntarget_os = ['android']",
-      "--patch_root",
-      "src/third_party/skia",
-      "--revision_mapping_file",
-      "{\"src/flutter\": \"got_flutter_revision\", \"src/third_party/skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "src/flutter@origin/master",
-      "--revision",
-      "src/third_party/skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"src/flutter\": \"origin/master\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"src/flutter\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/flutter.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9221bca00ddbd888260084def81f09543281b952\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/third_party/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
-      "@@@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\": \"src/third_party/skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision\": \"9221bca00ddbd888260084def81f09543281b952\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision_cp\": \"refs/heads/master@{#84512}\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#143121}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"src/flutter\", @@@",
-      "@@@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@\"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#143121}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_flutter_revision_cp@\"refs/heads/master@{#84512}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_flutter_revision@\"9221bca00ddbd888260084def81f09543281b952\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gclient",
-      "runhooks"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
-    },
-    "name": "runhook"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/flutter/src/out/android_release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree android_release"
-  },
-  {
-    "cmd": [
-      "flutter/tools/gn",
-      "--runtime-mode=release",
-      "--android"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
-    },
-    "name": "gn_gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "out/android_release",
-      "-j100"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
-    },
-    "name": "build_flutter"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json
deleted file mode 100644
index b58b460..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_mesa=true target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
deleted file mode 100644
index 18130ce..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
+++ /dev/null
@@ -1,197 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
-      "--patch_root",
-      "pdfium/third_party/skia",
-      "--revision_mapping_file",
-      "{\"pdfium\": \"got_pdfium_revision\", \"pdfium/third_party/skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "pdfium@origin/master",
-      "--revision",
-      "pdfium/third_party/skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
-      "@@@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\": \"pdfium/third_party/skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gclient",
-      "runhook",
-      "gn_linux64"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "runhook"
-  },
-  {
-    "cmd": [
-      "python",
-      "build/linux/sysroot_scripts/install-sysroot.py",
-      "--arch=amd64"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "sysroot"
-  },
-  {
-    "cmd": [
-      "gn",
-      "gen",
-      "out/skia",
-      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia=true"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "gn_gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "out/skia",
-      "-j100"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "build_pdfium"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json
deleted file mode 100644
index dcb4e3a..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json
+++ /dev/null
@@ -1,197 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
-      "--patch_root",
-      "pdfium/third_party/skia",
-      "--revision_mapping_file",
-      "{\"pdfium\": \"got_pdfium_revision\", \"pdfium/third_party/skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "pdfium@origin/master",
-      "--revision",
-      "pdfium/third_party/skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
-      "@@@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\": \"pdfium/third_party/skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gclient",
-      "runhook",
-      "gn_linux64"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
-    },
-    "name": "runhook"
-  },
-  {
-    "cmd": [
-      "python",
-      "build/linux/sysroot_scripts/install-sysroot.py",
-      "--arch=amd64"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
-    },
-    "name": "sysroot"
-  },
-  {
-    "cmd": [
-      "gn",
-      "gen",
-      "out/skia",
-      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia_paths=true"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
-    },
-    "name": "gn_gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "out/skia",
-      "-j100"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
-    },
-    "name": "build_pdfium"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Shared.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Shared.json
deleted file mode 100644
index 698d7ce..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Shared.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_component_build=true is_debug=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Valgrind.json b/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Valgrind.json
deleted file mode 100644
index 073647d..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Ubuntu-GCC-x86_64-Release-Valgrind.json
+++ /dev/null
@@ -1,169 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Valgrind"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Valgrind/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false target_cpu=\"x86_64\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Valgrind"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Valgrind/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Valgrind"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Valgrind/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-Clang-arm64-Release-GN_Android.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-Clang-arm64-Release-GN_Android.json
deleted file mode 100644
index a6e75c3..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-Clang-arm64-Release-GN_Android.json
+++ /dev/null
@@ -1,194 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-GN_Android"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-GN_Android\\Release",
-      "--args=is_debug=false ndk=\"[START_DIR]\\n\" target_cpu=\"arm64\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-GN_Android"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-GN_Android\\Release"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-GN_Android"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-GN_Android\\Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug-ANGLE.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug-ANGLE.json
deleted file mode 100644
index 7f212a3..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug-ANGLE.json
+++ /dev/null
@@ -1,188 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE\\Debug",
-      "--args=skia_use_angle=true target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug-Exceptions.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug-Exceptions.json
deleted file mode 100644
index 64c359e..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug-Exceptions.json
+++ /dev/null
@@ -1,188 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions\\Debug",
-      "--args=extra_cflags=[\"/EHsc\"] target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug.json
deleted file mode 100644
index bb1bdf6..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Debug.json
+++ /dev/null
@@ -1,188 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "--args=target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Release-GDI.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Release-GDI.json
deleted file mode 100644
index 0edfd92..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Release-GDI.json
+++ /dev/null
@@ -1,188 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI\\Release",
-      "--args=is_debug=false skia_use_gdi=true target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI\\Release"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI\\Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Release-GN.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Release-GN.json
deleted file mode 100644
index 37c123c..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86-Release-GN.json
+++ /dev/null
@@ -1,188 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN\\Release",
-      "--args=is_debug=false target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN\\Release"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN\\Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
deleted file mode 100644
index 17f73f6..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
+++ /dev/null
@@ -1,226 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan\\Release_x64",
-      "--args=is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]\\win_vulkan_sdk\" target_cpu=\"x86_64\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan\\Release_x64"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan\\Release_x64",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Release_x64"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]\\win_vulkan_sdk",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Release_x64"
-    ],
-    "infra_step": true,
-    "name": "copy build products (2)",
-    "~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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/alternate_repo.json b/infra/bots/recipe_modules/compile/example.expected/alternate_repo.json
deleted file mode 100644
index b9fd3b5..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/alternate_repo.json
+++ /dev/null
@@ -1,226 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'other_repo', 'url': 'https://skia.googlesource.com/other_repo.git'}]",
-      "--patch_root",
-      "other_repo",
-      "--revision_mapping_file",
-      "{\"other_repo\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "other_repo@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"other_repo\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"other_repo\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/other_repo.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"84be67d5f1146c5b7f6d4494c36c52903754abf4\"@@@",
-      "@@@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\": \"other_repo\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"84be67d5f1146c5b7f6d4494c36c52903754abf4\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#170933}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"other_repo\", @@@",
-      "@@@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@\"84be67d5f1146c5b7f6d4494c36c52903754abf4\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#170933}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
-      "--args=is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/win_vulkan_sdk\" target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/win_vulkan_sdk",
-      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
-    ],
-    "infra_step": true,
-    "name": "copy build products (2)",
-    "~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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/big_issue_number.json b/infra/bots/recipe_modules/compile/example.expected/big_issue_number.json
deleted file mode 100644
index 2ed2b36..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/big_issue_number.json
+++ /dev/null
@@ -1,194 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--issue",
-      "2147533002",
-      "--patchset",
-      "1",
-      "--rietveld_server",
-      "https://codereview.chromium.org",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "--args=target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/buildbotless_trybot_gerrit.json b/infra/bots/recipe_modules/compile/example.expected/buildbotless_trybot_gerrit.json
deleted file mode 100644
index 6d6fe03..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/buildbotless_trybot_gerrit.json
+++ /dev/null
@@ -1,215 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--gerrit_repo",
-      "https://skia.googlesource.com/skia.git",
-      "--gerrit_ref",
-      "refs/changes/89/456789/12",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "--args=target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/buildbotless_trybot_rietveld.json b/infra/bots/recipe_modules/compile/example.expected/buildbotless_trybot_rietveld.json
deleted file mode 100644
index b0638a1..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/buildbotless_trybot_rietveld.json
+++ /dev/null
@@ -1,217 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--issue",
-      "500",
-      "--patchset",
-      "1",
-      "--rietveld_server",
-      "https://codereview.chromium.org",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "--args=target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/flutter_trybot.json b/infra/bots/recipe_modules/compile/example.expected/flutter_trybot.json
deleted file mode 100644
index 713791f..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/flutter_trybot.json
+++ /dev/null
@@ -1,197 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/flutter",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'src/flutter', 'url': 'https://github.com/flutter/engine.git'}]\ntarget_os = ['android']",
-      "--patch_root",
-      "src/third_party/skia",
-      "--revision_mapping_file",
-      "{\"src/flutter\": \"got_flutter_revision\", \"src/third_party/skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--gerrit_repo",
-      "https://skia.googlesource.com/skia.git",
-      "--gerrit_ref",
-      "refs/changes/89/456789/12",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "src/flutter@origin/master",
-      "--revision",
-      "src/third_party/skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"src/flutter\": \"origin/master\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"src/flutter\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/flutter.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9221bca00ddbd888260084def81f09543281b952\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/third_party/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
-      "@@@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\": \"src/third_party/skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision\": \"9221bca00ddbd888260084def81f09543281b952\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision_cp\": \"refs/heads/master@{#84512}\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#143121}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"src/flutter\", @@@",
-      "@@@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@\"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#143121}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_flutter_revision_cp@\"refs/heads/master@{#84512}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_flutter_revision@\"9221bca00ddbd888260084def81f09543281b952\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gclient",
-      "runhooks"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
-    },
-    "name": "runhook"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/flutter/src/out/android_release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree android_release"
-  },
-  {
-    "cmd": [
-      "flutter/tools/gn",
-      "--runtime-mode=release",
-      "--android"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
-    },
-    "name": "gn_gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "out/android_release",
-      "-j100"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
-    },
-    "name": "build_flutter"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/pdfium_trybot.json b/infra/bots/recipe_modules/compile/example.expected/pdfium_trybot.json
deleted file mode 100644
index 5d2e072..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/pdfium_trybot.json
+++ /dev/null
@@ -1,201 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
-      "--patch_root",
-      "pdfium/third_party/skia",
-      "--revision_mapping_file",
-      "{\"pdfium\": \"got_pdfium_revision\", \"pdfium/third_party/skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--gerrit_repo",
-      "https://skia.googlesource.com/skia.git",
-      "--gerrit_ref",
-      "refs/changes/89/456789/12",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "pdfium@origin/master",
-      "--revision",
-      "pdfium/third_party/skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
-      "@@@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\": \"pdfium/third_party/skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gclient",
-      "runhook",
-      "gn_linux64"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "runhook"
-  },
-  {
-    "cmd": [
-      "python",
-      "build/linux/sysroot_scripts/install-sysroot.py",
-      "--arch=amd64"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "sysroot"
-  },
-  {
-    "cmd": [
-      "gn",
-      "gen",
-      "out/skia",
-      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia=true"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "gn_gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "out/skia",
-      "-j100"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
-    },
-    "name": "build_pdfium"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.expected/recipe_with_gerrit_patch.json b/infra/bots/recipe_modules/compile/example.expected/recipe_with_gerrit_patch.json
deleted file mode 100644
index 1c72f6d..0000000
--- a/infra/bots/recipe_modules/compile/example.expected/recipe_with_gerrit_patch.json
+++ /dev/null
@@ -1,215 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_C:\\_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_C:\\_B_CACHE]",
-      "--gerrit_repo",
-      "https://skia.googlesource.com/skia.git",
-      "--gerrit_ref",
-      "refs/changes/89/456789/12",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Trybot"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
-      "gen",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Trybot\\Debug",
-      "--args=target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Trybot"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja.exe",
-      "-C",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Trybot\\Debug"
-    ],
-    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Trybot"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Trybot\\Debug",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
-    ],
-    "infra_step": true,
-    "name": "cleanup",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/compile/example.py b/infra/bots/recipe_modules/compile/example.py
deleted file mode 100644
index c369f52..0000000
--- a/infra/bots/recipe_modules/compile/example.py
+++ /dev/null
@@ -1,244 +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.
-
-
-# Example recipe w/ coverage.
-
-
-DEPS = [
-  'compile',
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-]
-
-
-TEST_BUILDERS = {
-  'client.skia.compile': {
-    'skiabot-linux-swarm-000': [
-      'Build-Mac-Clang-arm64-Debug-iOS',
-      'Build-Mac-Clang-mipsel-Debug-GN_Android',
-      'Build-Mac-Clang-x64-Release-iOS',
-      'Build-Mac-Clang-x86_64-Debug-CommandBuffer',
-      'Build-Mac-Clang-x86_64-Release-GN',
-      'Build-Ubuntu-Clang-arm64-Debug-GN_Android-Trybot',
-      'Build-Ubuntu-Clang-arm64-Debug-GN_Android_FrameworkDefs',
-      'Build-Ubuntu-Clang-arm64-Release-GN_Android',
-      'Build-Ubuntu-Clang-arm64-Release-GN_Android_Vulkan',
-      'Build-Ubuntu-Clang-x86_64-Debug-ASAN',
-      'Build-Ubuntu-Clang-x86_64-Debug-GN',
-      'Build-Ubuntu-Clang-x86_64-Release-Mini',
-      'Build-Ubuntu-Clang-x86_64-Release-Vulkan',
-      'Build-Ubuntu-GCC-arm-Release-Chromecast',
-      'Build-Ubuntu-GCC-x86-Debug',
-      'Build-Ubuntu-GCC-x86_64-Debug-GN',
-      'Build-Ubuntu-GCC-x86_64-Debug-MSAN',
-      'Build-Ubuntu-GCC-x86_64-Debug-NoGPU',
-      'Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE',
-      'Build-Ubuntu-GCC-x86_64-Release-ANGLE',
-      'Build-Ubuntu-GCC-x86_64-Release-Fast',
-      'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android',
-      'Build-Ubuntu-GCC-x86_64-Release-Mesa',
-      'Build-Ubuntu-GCC-x86_64-Release-PDFium',
-      'Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths',
-      'Build-Ubuntu-GCC-x86_64-Release-Shared',
-      'Build-Ubuntu-GCC-x86_64-Release-Valgrind',
-      'Build-Win-Clang-arm64-Release-GN_Android',
-      'Build-Win-MSVC-x86-Debug',
-      'Build-Win-MSVC-x86-Debug-ANGLE',
-      'Build-Win-MSVC-x86-Debug-Exceptions',
-      'Build-Win-MSVC-x86-Release-GDI',
-      'Build-Win-MSVC-x86-Release-GN',
-      'Build-Win-MSVC-x86_64-Release-Vulkan',
-    ],
-  },
-}
-
-
-def RunSteps(api):
-  api.compile.run()
-
-
-def GenTests(api):
-  for mastername, slaves in TEST_BUILDERS.iteritems():
-    for slavename, builders_by_slave in slaves.iteritems():
-      for builder in builders_by_slave:
-        test = (
-          api.test(builder) +
-          api.properties(buildername=builder,
-                         mastername=mastername,
-                         slavename=slavename,
-                         buildnumber=5,
-                         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('tmp', 'uninteresting_hashes.txt')
-          )
-        )
-        if 'Win' in builder:
-          test += api.platform('win', 64)
-        elif 'Mac' in builder:
-          test += api.platform('mac', 64)
-        else:
-          test += api.platform('linux', 64)
-        if 'Trybot' in builder:
-          test += api.properties(issue=500,
-                                 patchset=1,
-                                 rietveld='https://codereview.chromium.org')
-
-        yield test
-
-  mastername = 'client.skia.compile'
-  slavename = 'skiabot-win-compile-000'
-  buildername = 'Build-Win-MSVC-x86-Debug'
-  yield (
-      api.test('big_issue_number') +
-      api.properties(buildername=buildername,
-                     mastername=mastername,
-                     slavename=slavename,
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]',
-                     rietveld='https://codereview.chromium.org',
-                     patchset=1,
-                     issue=2147533002L) +
-      api.path.exists(
-          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-      ) +
-      api.platform('win', 64)
-  )
-
-  yield (
-      api.test('recipe_with_gerrit_patch') +
-      api.properties(
-          buildername=buildername + '-Trybot',
-          mastername=mastername,
-          slavename=slavename,
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          repository='https://skia.googlesource.com/skia.git',
-          revision='abc123',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=buildername + '-Trybot',
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      api.platform('win', 64)
-  )
-
-  yield (
-      api.test('buildbotless_trybot_rietveld') +
-      api.properties(
-          buildername=buildername,
-          mastername=mastername,
-          slavename=slavename,
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          repository='https://skia.googlesource.com/skia.git',
-          revision='abc123',
-          nobuildbot='True',
-          issue=500,
-          patchset=1,
-          patch_storage='rietveld',
-          rietveld='https://codereview.chromium.org') +
-      api.platform('win', 64)
-  )
-
-  yield (
-      api.test('buildbotless_trybot_gerrit') +
-      api.properties(
-          repository='https://skia.googlesource.com/skia.git',
-          buildername=buildername,
-          mastername=mastername,
-          slavename=slavename,
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          revision='abc123',
-          nobuildbot='True',
-          patch_issue=500,
-          patch_set=1,
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=buildername,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      api.platform('win', 64)
-  )
-
-  buildername = 'Build-Win-MSVC-x86_64-Release-Vulkan'
-  yield (
-      api.test('alternate_repo') +
-      api.properties(buildername=buildername,
-                     mastername=mastername,
-                     slavename=slavename,
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/other_repo.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-      api.path.exists(
-          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-      )
-    )
-
-  buildername = 'Build-Ubuntu-GCC-x86_64-Release-PDFium'
-  yield (
-      api.test('pdfium_trybot') +
-      api.properties(
-          repository='https://skia.googlesource.com/skia.git',
-          buildername=buildername,
-          mastername=mastername,
-          slavename=slavename,
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          revision='abc123',
-          nobuildbot='True',
-          patch_issue=500,
-          patch_set=1,
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=buildername,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      api.path.exists(
-          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-      )
-  )
-
-  buildername = 'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android'
-  yield (
-      api.test('flutter_trybot') +
-      api.properties(
-          repository='https://skia.googlesource.com/skia.git',
-          buildername=buildername,
-          mastername=mastername,
-          slavename=slavename,
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          revision='abc123',
-          nobuildbot='True',
-          patch_issue=500,
-          patch_set=1,
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=buildername,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      api.path.exists(
-          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-      )
-  )
diff --git a/infra/bots/recipe_modules/core/__init__.py b/infra/bots/recipe_modules/core/__init__.py
index 2c66e77..1413f2e 100644
--- a/infra/bots/recipe_modules/core/__init__.py
+++ b/infra/bots/recipe_modules/core/__init__.py
@@ -3,19 +3,16 @@
 # found in the LICENSE file.
 
 DEPS = [
-  'build/file',
   'depot_tools/bot_update',
   'depot_tools/gclient',
   'depot_tools/tryserver',
+  'file',
   'flavor',
+  'recipe_engine/context',
   'recipe_engine/path',
-  'recipe_engine/platform',
   'recipe_engine/properties',
   'recipe_engine/python',
   'recipe_engine/step',
   'run',
   'vars',
 ]
-
-# TODO(borenet): Add coverage
-DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/core/api.py b/infra/bots/recipe_modules/core/api.py
index 6231891..430fa02 100644
--- a/infra/bots/recipe_modules/core/api.py
+++ b/infra/bots/recipe_modules/core/api.py
@@ -33,28 +33,6 @@
 
     self.m.flavor.setup()
 
-  def update_repo(self, parent_dir, repo):
-    """Update an existing repo. This is safe to call without gen_steps."""
-    repo_path = parent_dir.join(repo.name)
-    if self.m.path.exists(repo_path):  # pragma: nocover
-      if self.m.platform.is_win:
-        git = 'git.bat'
-      else:
-        git = 'git'
-      with self.m.step.context({'cwd': repo_path}):
-        self.m.step('git remote set-url',
-                    cmd=[git, 'remote', 'set-url', 'origin', repo.url],
-                    infra_step=True)
-        self.m.step('git fetch',
-                    cmd=[git, 'fetch'],
-                    infra_step=True)
-        self.m.step('git reset',
-                    cmd=[git, 'reset', '--hard', repo.revision],
-                    infra_step=True)
-        self.m.step('git clean',
-                    cmd=[git, 'clean', '-d', '-f'],
-                    infra_step=True)
-
   def checkout_steps(self):
     """Run the steps to obtain a checkout of Skia."""
     cfg_kwargs = {}
@@ -94,6 +72,10 @@
     m = gclient_cfg.got_revision_mapping
     m[main_name] = 'got_revision'
     patch_root = main_name
+    patch_repo = main.url
+    if self.m.properties.get('patch_repo'):
+      patch_repo = self.m.properties['patch_repo']
+      patch_root = patch_repo.split('/')[-1].rstrip('.git')
 
     if self.m.vars.need_pdfium_checkout:
       # Skia is a DEP of PDFium; the 'revision' property is a Skia revision, and
@@ -106,6 +88,7 @@
       gclient_cfg.patch_projects['skia'] = (skia_dep_path, 'HEAD')
       gclient_cfg.revisions[skia_dep_path] = self.m.properties['revision']
       m[skia_dep_path] = 'got_revision'
+      patch_repo = 'https://skia.googlesource.com/skia.git'
       patch_root = skia_dep_path
 
     if self.m.vars.need_flutter_checkout:
@@ -121,17 +104,16 @@
       gclient_cfg.patch_projects['skia'] = (skia_dep_path, 'HEAD')
       gclient_cfg.revisions[skia_dep_path] = self.m.properties['revision']
       m[skia_dep_path] = 'got_revision'
+      patch_repo = 'https://skia.googlesource.com/skia.git'
       patch_root = skia_dep_path
 
-    self.update_repo(self.m.vars.checkout_root, main)
-
     # TODO(rmistry): Remove the below block after there is a solution for
     #                crbug.com/616443
     entries_file = self.m.vars.checkout_root.join('.gclient_entries')
-    if self.m.path.exists(entries_file):
+    if self.m.path.exists(entries_file) or self._test_data.enabled:
       self.m.file.remove('remove %s' % entries_file,
                          entries_file,
-                         infra_step=True)  # pragma: no cover
+                         infra_step=True)
 
     if self.m.vars.need_chromium_checkout:
       chromium = gclient_cfg.solutions.add()
@@ -139,27 +121,26 @@
       chromium.managed = False
       chromium.url = 'https://chromium.googlesource.com/chromium/src.git'
       chromium.revision = 'origin/lkgr'
-      self.update_repo(self.m.vars.checkout_root, chromium)
 
     # Run bot_update.
 
     # Hack the patch ref if necessary.
-    if self.m.properties.get('patch_storage', '') == 'gerrit':
-      if self.m.bot_update._issue and self.m.bot_update._patchset:
-        self.m.bot_update._gerrit_ref = 'refs/changes/%s/%d/%d' % (
-            str(self.m.bot_update._issue)[-2:],
-            self.m.bot_update._issue,
-            self.m.bot_update._patchset,
-        )
+    if self.m.bot_update._issue and self.m.bot_update._patchset:
+      self.m.bot_update._gerrit_ref = 'refs/changes/%s/%d/%d' % (
+          str(self.m.bot_update._issue)[-2:],
+          self.m.bot_update._issue,
+          self.m.bot_update._patchset,
+      )
+      self.m.bot_update._repository = patch_repo
 
     self.m.gclient.c = gclient_cfg
-    with self.m.step.context({'cwd': self.m.vars.checkout_root}):
+    with self.m.context(cwd=self.m.vars.checkout_root):
       update_step = self.m.bot_update.ensure_checkout(patch_root=patch_root)
 
     self.m.vars.got_revision = (
         update_step.presentation.properties['got_revision'])
 
     if self.m.vars.need_chromium_checkout:
-      with self.m.step.context({'cwd': self.m.vars.checkout_root,
-                                'env': self.m.vars.gclient_env}):
+      with self.m.context(cwd=self.m.vars.checkout_root,
+                          env=self.m.vars.gclient_env):
         self.m.gclient.runhooks()
diff --git a/infra/bots/recipe_modules/core/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json b/infra/bots/recipe_modules/core/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
new file mode 100644
index 0000000..182166d
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
@@ -0,0 +1,140 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
+      "--patch_root",
+      "skia",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123",
+      "--revision",
+      "src@origin/lkgr"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
+      "@@@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@  \"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",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
+    },
+    "name": "gclient runhooks"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/core/examples/full.expected/cross_repo_trybot.json b/infra/bots/recipe_modules/core/examples/full.expected/cross_repo_trybot.json
new file mode 100644
index 0000000..28f8ea3
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.expected/cross_repo_trybot.json
@@ -0,0 +1,104 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'parent_repo', 'url': 'https://skia.googlesource.com/parent_repo.git'}]",
+      "--patch_root",
+      "skia",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"parent_repo\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "parent_repo@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"parent_repo\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parent_repo\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/parent_repo.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"354f9075936db3e1e855a48538d2f8555b37ac5a\"@@@",
+      "@@@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\": \"354f9075936db3e1e855a48538d2f8555b37ac5a\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#106773}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"parent_repo\", @@@",
+      "@@@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@\"354f9075936db3e1e855a48538d2f8555b37ac5a\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#106773}\"@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/core/examples/full.expected/flutter_trybot.json b/infra/bots/recipe_modules/core/examples/full.expected/flutter_trybot.json
new file mode 100644
index 0000000..94edc1b
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.expected/flutter_trybot.json
@@ -0,0 +1,115 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/flutter",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/flutter/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/flutter/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'src/flutter', 'url': 'https://github.com/flutter/engine.git'}]\ntarget_os = ['android']",
+      "--patch_root",
+      "src/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_flutter_revision\": \"src/flutter\", \"got_revision\": \"src/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "src/flutter@origin/master",
+      "--revision",
+      "src/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src/flutter\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/flutter\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/flutter.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9221bca00ddbd888260084def81f09543281b952\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
+      "@@@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\": \"src/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision\": \"9221bca00ddbd888260084def81f09543281b952\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision_cp\": \"refs/heads/master@{#84512}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#143121}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"src/flutter\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_flutter_revision@\"9221bca00ddbd888260084def81f09543281b952\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_flutter_revision_cp@\"refs/heads/master@{#84512}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#143121}\"@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/core/examples/full.expected/no_persistent_checkout.json b/infra/bots/recipe_modules/core/examples/full.expected/no_persistent_checkout.json
new file mode 100644
index 0000000..2b14fe5
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.expected/no_persistent_checkout.json
@@ -0,0 +1,30 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/core/examples/full.expected/pdfium_trybot.json b/infra/bots/recipe_modules/core/examples/full.expected/pdfium_trybot.json
new file mode 100644
index 0000000..3a6fc9c
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.expected/pdfium_trybot.json
@@ -0,0 +1,115 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
+      "--patch_root",
+      "pdfium/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_pdfium_revision\": \"pdfium\", \"got_revision\": \"pdfium/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "pdfium@origin/master",
+      "--revision",
+      "pdfium/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@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\": \"pdfium/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/core/examples/full.expected/test.json b/infra/bots/recipe_modules/core/examples/full.expected/test.json
new file mode 100644
index 0000000..55cb172
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.expected/test.json
@@ -0,0 +1,127 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/core/examples/full.py b/infra/bots/recipe_modules/core/examples/full.py
new file mode 100644
index 0000000..cbfa7ad
--- /dev/null
+++ b/infra/bots/recipe_modules/core/examples/full.py
@@ -0,0 +1,126 @@
+# 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 = [
+  'core',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+]
+
+
+def RunSteps(api):
+  api.core.setup()
+
+
+def GenTests(api):
+  buildername = 'Build-Win-MSVC-x86_64-Release-Vulkan'
+  yield (
+      api.test('test') +
+      api.properties(buildername=buildername,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.properties(patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )
+    )
+
+  buildername = 'Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE'
+  yield (
+      api.test('no_persistent_checkout') +
+      api.properties(buildername=buildername,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.properties(patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )
+    )
+
+  buildername = 'Build-Ubuntu-GCC-x86_64-Release-PDFium'
+  yield (
+      api.test('pdfium_trybot') +
+      api.properties(
+          repository='https://skia.googlesource.com/skia.git',
+          buildername=buildername,
+          path_config='kitchen',
+          swarm_out_dir='[SWARM_OUT_DIR]',
+          revision='abc123',
+          patch_issue=500,
+          patch_set=1,
+          patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      ) +
+      api.path.exists(
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      )
+  )
+
+  buildername = 'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android'
+  yield (
+      api.test('flutter_trybot') +
+      api.properties(
+          repository='https://skia.googlesource.com/skia.git',
+          buildername=buildername,
+          path_config='kitchen',
+          swarm_out_dir='[SWARM_OUT_DIR]',
+          revision='abc123',
+          patch_issue=500,
+          patch_set=1,
+          patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      ) +
+      api.path.exists(
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      )
+  )
+
+  builder = 'Housekeeper-Weekly-RecreateSKPs'
+  yield (
+      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('skp_output'))
+  )
+
+  buildername = 'Build-Ubuntu-GCC-x86_64-Release'
+  yield (
+      api.test('cross_repo_trybot') +
+      api.properties(
+          repository='https://skia.googlesource.com/parent_repo.git',
+          buildername=buildername,
+          path_config='kitchen',
+          swarm_out_dir='[SWARM_OUT_DIR]',
+          revision='abc123',
+          patch_issue=500,
+          patch_repo='https://skia.googlesource.com/skia.git',
+          patch_set=1,
+          patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      ) +
+      api.path.exists(
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      )
+  )
diff --git a/infra/bots/recipe_modules/ct/__init__.py b/infra/bots/recipe_modules/ct/__init__.py
index f9d542c..792ca35 100644
--- a/infra/bots/recipe_modules/ct/__init__.py
+++ b/infra/bots/recipe_modules/ct/__init__.py
@@ -3,13 +3,9 @@
 # found in the LICENSE file.
 
 DEPS = [
-  'build/file',
   'depot_tools/gsutil',
+  'file',
   'recipe_engine/path',
   'recipe_engine/step',
   'run',
 ]
-
-
-# TODO(borenet): Add coverage
-DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/ct/api.py b/infra/bots/recipe_modules/ct/api.py
index 91478fb..314903f 100644
--- a/infra/bots/recipe_modules/ct/api.py
+++ b/infra/bots/recipe_modules/ct/api.py
@@ -46,6 +46,6 @@
     gsutil_args.append(str(slave_dest_dir))
     try:
       self.m.gsutil(gsutil_args, use_retry_wrapper=False)
-    except self.m.step.StepFailure:  # pragma: nocover
+    except self.m.step.StepFailure:
       # Some subdirectories might have no SKPs in them.
       pass
diff --git a/infra/bots/recipe_modules/ct/examples/full.expected/failed_gsutil.json b/infra/bots/recipe_modules/ct/examples/full.expected/failed_gsutil.json
new file mode 100644
index 0000000..dc6f888
--- /dev/null
+++ b/infra/bots/recipe_modules/ct/examples/full.expected/failed_gsutil.json
@@ -0,0 +1,160 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/skps/slave0"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave0"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/skps/slave0",
+      "511"
+    ],
+    "name": "makedirs slave0",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/100/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/101/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/102/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/103/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/104/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/105/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/106/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/107/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/108/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/109/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/110/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/111/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/112/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/113/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/114/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/115/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/116/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/117/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/118/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/119/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/120/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/121/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/122/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/123/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/124/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/125/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/126/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/127/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/128/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/129/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/130/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/131/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/132/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/133/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/134/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/135/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/136/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/137/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/138/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/139/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/140/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/141/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/142/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/143/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/144/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/145/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/146/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/147/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/148/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/149/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/150/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/151/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/152/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/153/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/154/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/155/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/156/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/157/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/158/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/159/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/160/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/161/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/162/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/163/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/164/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/165/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/166/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/167/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/168/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/169/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/170/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/171/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/172/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/173/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/174/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/175/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/176/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/177/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/178/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/179/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/180/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/181/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/182/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/183/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/184/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/185/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/186/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/187/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/188/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/189/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/190/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/191/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/192/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/193/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/194/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/195/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/196/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/197/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/198/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/199/*.skp",
+      "[START_DIR]/skps/slave0"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/ct/examples/full.expected/test.json b/infra/bots/recipe_modules/ct/examples/full.expected/test.json
new file mode 100644
index 0000000..fa9e44a
--- /dev/null
+++ b/infra/bots/recipe_modules/ct/examples/full.expected/test.json
@@ -0,0 +1,156 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/skps/slave0"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave0"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/skps/slave0",
+      "511"
+    ],
+    "name": "makedirs slave0",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/100/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/101/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/102/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/103/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/104/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/105/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/106/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/107/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/108/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/109/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/110/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/111/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/112/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/113/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/114/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/115/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/116/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/117/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/118/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/119/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/120/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/121/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/122/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/123/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/124/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/125/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/126/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/127/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/128/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/129/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/130/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/131/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/132/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/133/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/134/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/135/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/136/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/137/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/138/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/139/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/140/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/141/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/142/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/143/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/144/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/145/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/146/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/147/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/148/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/149/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/150/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/151/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/152/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/153/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/154/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/155/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/156/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/157/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/158/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/159/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/160/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/161/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/162/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/163/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/164/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/165/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/166/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/167/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/168/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/169/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/170/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/171/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/172/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/173/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/174/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/175/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/176/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/177/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/178/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/179/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/180/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/181/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/182/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/183/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/184/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/185/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/186/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/187/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/188/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/189/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/190/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/191/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/192/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/193/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/194/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/195/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/196/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/197/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/198/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/abc123/199/*.skp",
+      "[START_DIR]/skps/slave0"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/ct/examples/full.py b/infra/bots/recipe_modules/ct/examples/full.py
new file mode 100644
index 0000000..3b3c3f2
--- /dev/null
+++ b/infra/bots/recipe_modules/ct/examples/full.py
@@ -0,0 +1,25 @@
+# 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 = [
+  'ct',
+  'recipe_engine/path',
+]
+
+
+def RunSteps(api):
+  api.ct.download_swarming_skps(
+      'All', '0', 'abc123',
+      api.path['start_dir'].join('skps'),
+      start_range=100,
+      num_skps=100)
+
+
+def GenTests(api):
+  yield api.test('test')
+  yield (
+      api.test('failed_gsutil') +
+      api.step_data('gsutil cp', retcode=1)
+  )
diff --git a/infra/bots/recipe_modules/env/__init__.py b/infra/bots/recipe_modules/env/__init__.py
new file mode 100644
index 0000000..774cf5f
--- /dev/null
+++ b/infra/bots/recipe_modules/env/__init__.py
@@ -0,0 +1,9 @@
+# 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 = [
+  'recipe_engine/context',
+]
+
diff --git a/infra/bots/recipe_modules/env/api.py b/infra/bots/recipe_modules/env/api.py
new file mode 100644
index 0000000..ea9ed52
--- /dev/null
+++ b/infra/bots/recipe_modules/env/api.py
@@ -0,0 +1,20 @@
+# 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.
+
+
+from recipe_engine import recipe_api
+
+
+class EnvApi(recipe_api.RecipeApi):
+  def __call__(self, env_dict):
+    env = self.m.context.env
+    # If PATH is defined in both, merge them together, merging default_env into
+    # path by replacing %(PATH)s
+    upstream_path = env.get('PATH', '')
+    env.update(env_dict)
+    my_path = env_dict.get('PATH', '')
+    if upstream_path and my_path and upstream_path != my_path:
+      env['PATH'] = upstream_path.replace(r'%(PATH)s', my_path)
+
+    return self.m.context(env=env)
diff --git a/infra/bots/recipe_modules/env/examples/full.expected/test.json b/infra/bots/recipe_modules/env/examples/full.expected/test.json
new file mode 100644
index 0000000..10cf37c
--- /dev/null
+++ b/infra/bots/recipe_modules/env/examples/full.expected/test.json
@@ -0,0 +1,44 @@
+[
+  {
+    "cmd": [
+      "echo",
+      "hi"
+    ],
+    "name": "1"
+  },
+  {
+    "cmd": [
+      "echo",
+      "hi"
+    ],
+    "env": {
+      "MYVAR": "myval"
+    },
+    "name": "2"
+  },
+  {
+    "cmd": [
+      "echo",
+      "hi"
+    ],
+    "env": {
+      "PATH": "mypath:<PATH>"
+    },
+    "name": "3"
+  },
+  {
+    "cmd": [
+      "echo",
+      "hi"
+    ],
+    "env": {
+      "PATH": "mypath:<PATH>:otherpath"
+    },
+    "name": "4"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/env/examples/full.py b/infra/bots/recipe_modules/env/examples/full.py
new file mode 100644
index 0000000..d53ab57
--- /dev/null
+++ b/infra/bots/recipe_modules/env/examples/full.py
@@ -0,0 +1,26 @@
+# 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 = [
+  'env',
+  'recipe_engine/context',
+  'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  api.step('1', cmd=['echo', 'hi'])
+  with api.env({'MYVAR': 'myval'}):
+    api.step('2', cmd=['echo', 'hi'])
+
+  path = 'mypath:%(PATH)s'
+  with api.context(env={'PATH': path}):
+    api.step('3', cmd=['echo', 'hi'])
+    with api.env({'PATH': '%(PATH)s:otherpath'}):
+      api.step('4', cmd=['echo', 'hi'])
+
+
+def GenTests(api):
+  yield api.test('test')
diff --git a/infra/bots/recipe_modules/file/OWNERS b/infra/bots/recipe_modules/file/OWNERS
new file mode 100644
index 0000000..36f4549
--- /dev/null
+++ b/infra/bots/recipe_modules/file/OWNERS
@@ -0,0 +1,5 @@
+dnj@chromium.org
+iannucci@chromium.org
+martiniss@chromium.org
+nodir@chromium.org
+phajdan.jr@chromium.org
diff --git a/infra/bots/recipe_modules/file/__init__.py b/infra/bots/recipe_modules/file/__init__.py
new file mode 100644
index 0000000..29b55cb
--- /dev/null
+++ b/infra/bots/recipe_modules/file/__init__.py
@@ -0,0 +1,16 @@
+# 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.
+
+
+# TODO(borenet): This module belongs in the recipe engine. Remove it from this
+# repo once it has been moved.
+
+
+DEPS = [
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+]
diff --git a/infra/bots/recipe_modules/file/api.py b/infra/bots/recipe_modules/file/api.py
new file mode 100644
index 0000000..e2ec61c
--- /dev/null
+++ b/infra/bots/recipe_modules/file/api.py
@@ -0,0 +1,204 @@
+# 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.
+
+
+# TODO(borenet): This module belongs in the recipe engine. Remove it from this
+# repo once it has been moved.
+
+
+from recipe_engine import recipe_api
+
+
+class FileApi(recipe_api.RecipeApi):
+  """FileApi contains helper functions for reading and writing files."""
+
+  def __init__(self, **kwargs):
+    super(FileApi, self).__init__(**kwargs)
+
+  def _run_fileutil(self, name, fileutil_args, **kwargs):
+    # Failure to perform filesystem operations is considered an infrastructure
+    # failure.
+    kwargs = kwargs.copy()
+    kwargs.setdefault('infra_step', True)
+
+    self.m.python(
+        name,
+        self.resource('fileutil.py'),
+        args=fileutil_args,
+        **kwargs)
+
+  def copy(self, name, source, dest, step_test_data=None, **kwargs):
+    """Copy a file."""
+    return self.m.python.inline(
+        name,
+        """
+        import shutil
+        import sys
+        shutil.copy(sys.argv[1], sys.argv[2])
+        """,
+        args=[source, dest],
+        add_python_log=False,
+        step_test_data=step_test_data,
+        **kwargs
+    )
+
+  def copytree(self, name, source, dest, symlinks=False, **kwargs):
+    """Run shutil.copytree in a step."""
+    return self.m.python.inline(
+        name,
+        """
+        import shutil
+        import sys
+        shutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))
+        """,
+        args=[source, dest, int(symlinks)],
+        add_python_log=False,
+        **kwargs
+    )
+
+  def move(self, name, source, dest, **kwargs):
+    """Run shutil.move in a step."""
+    return self.m.python.inline(
+        name,
+        """
+        import shutil
+        import sys
+        shutil.move(sys.argv[1], sys.argv[2])
+        """,
+        args=[source, dest],
+        add_python_log=False,
+        **kwargs
+    )
+
+  def read(self, name, path, test_data=None, **kwargs):
+    """Read a file and return its contents."""
+    step_test_data = None
+    if test_data is not None:
+      step_test_data = lambda: self.m.raw_io.test_api.output_text(test_data)
+    return self.copy(name, path, self.m.raw_io.output_text(),
+                     step_test_data=step_test_data, **kwargs).raw_io.output_text
+
+  def write(self, name, path, data, **kwargs):
+    """Write the given data to a file."""
+    return self.m.python.inline(
+        name,
+        """
+        import shutil
+        import sys
+        shutil.copy(sys.argv[1], sys.argv[2])
+        """,
+        args=[self.m.raw_io.input_text(data), path],
+        add_python_log=False,
+        **kwargs
+    )
+
+  def glob(self, name, pattern, test_data=None, **kwargs):
+    """Performs glob search on a directory.
+
+    Returns list of Path objects for all files found.
+    """
+    step_test_data = None
+    if test_data is not None:
+      step_test_data = (
+          lambda: self.m.raw_io.test_api.output_text(
+              '\n'.join(map(str, test_data))))
+    step_result = self.m.python.inline(
+        name,
+        r"""
+        import glob
+        import sys
+        with open(sys.argv[1], 'w') as f:
+          f.write('\n'.join(glob.glob(sys.argv[2])))
+        """,
+        args=[self.m.raw_io.output_text(), pattern],
+        step_test_data=step_test_data,
+        add_python_log=False,
+        **kwargs
+    )
+    return map(self.m.path.abs_to_path,
+               step_result.raw_io.output_text.splitlines())
+
+  def remove(self, name, path, **kwargs):
+    """Remove the given file."""
+    return self.m.python.inline(
+        name,
+        """
+        import os
+        import sys
+        os.remove(sys.argv[1])
+        """,
+        args=[path],
+        **kwargs
+    )
+
+  def listdir(self, name, path, step_test_data=None, **kwargs):
+    """Wrapper for os.listdir."""
+    return self.m.python.inline('listdir %s' % name,
+      """
+      import json, os, sys
+      if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):
+        with open(sys.argv[2], 'w') as f:
+          json.dump(os.listdir(sys.argv[1]), f)
+      """,
+      args=[path, self.m.json.output()],
+      step_test_data=(step_test_data or (
+          lambda: self.m.json.test_api.output(['file 1', 'file 2']))),
+      **kwargs
+    ).json.output
+
+  def makedirs(self, name, path, mode=0777, **kwargs):
+    """
+    Like os.makedirs, except that if the directory exists, then there is no
+    error.
+    """
+    self.m.path.assert_absolute(path)
+    self.m.python.inline(
+      'makedirs ' + name,
+      """
+      import sys, os
+      path = sys.argv[1]
+      mode = int(sys.argv[2])
+      if not os.path.isdir(path):
+        if os.path.exists(path):
+          print "%s exists but is not a dir" % path
+          sys.exit(1)
+        os.makedirs(path, mode)
+      """,
+      args=[path, str(mode)],
+      **kwargs
+    )
+    self.m.path.mock_add_paths(path)
+
+  def rmtree(self, name, path, **kwargs):
+    """Wrapper for chromium_utils.RemoveDirectory."""
+    self.m.path.assert_absolute(path)
+    self._run_fileutil(
+        'rmtree ' + name,
+        ['rmtree', path],
+        **kwargs)
+
+  def rmcontents(self, name, path, **kwargs):
+    """
+    Similar to rmtree, but removes only contents not the directory.
+
+    This is useful e.g. when removing contents of current working directory.
+    Deleting current working directory makes all further getcwd calls fail
+    until chdir is called. chdir would be tricky in recipes, so we provide
+    a call that doesn't delete the directory itself.
+    """
+    self.m.path.assert_absolute(path)
+    self._run_fileutil(
+        'rmcontents ' + name,
+        ['rmcontents', path],
+        **kwargs)
+
+  def rmwildcard(self, pattern, path, **kwargs):
+    """
+    Removes all files in the subtree of path matching the glob pattern.
+    """
+    self.m.path.assert_absolute(path)
+    self._run_fileutil(
+        'rmwildcard %s in %s' % (pattern, path),
+        ['rmwildcard', path, pattern],
+        **kwargs)
diff --git a/infra/bots/recipe_modules/file/examples/full.expected/file_io.json b/infra/bots/recipe_modules/file/examples/full.expected/file_io.json
new file mode 100644
index 0000000..a4e557f
--- /dev/null
+++ b/infra/bots/recipe_modules/file/examples/full.expected/file_io.json
@@ -0,0 +1,301 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "/fake/dir",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir fake dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "some",
+      "command"
+    ],
+    "name": "manipulate file 1"
+  },
+  {
+    "cmd": [
+      "some",
+      "command"
+    ],
+    "name": "manipulate file 2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "/faker/dir",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir other",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"aaa\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "some",
+      "command"
+    ],
+    "name": "manipulate aaa"
+  },
+  {
+    "cmd": [
+      "echo",
+      "[TMP_BASE]/prefix_a_tmp_1"
+    ],
+    "name": "print prefix_a"
+  },
+  {
+    "cmd": [
+      "echo",
+      "[TMP_BASE]/prefix_b_tmp_2"
+    ],
+    "name": "print prefix_b"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.move(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/source",
+      "[START_DIR]/destination"
+    ],
+    "name": "move"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[START_DIR]/some_file"
+    ],
+    "name": "remove",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmcontents",
+      "[START_DIR]/some_dir"
+    ],
+    "infra_step": true,
+    "name": "rmcontents rmcontents"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmwildcard",
+      "[START_DIR]",
+      "*.o"
+    ],
+    "infra_step": true,
+    "name": "rmwildcard *.o in [START_DIR]"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "abcde",
+      "tmp_file.txt"
+    ],
+    "name": "write_simple"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "tmp_file.txt",
+      "/path/to/tmp/"
+    ],
+    "name": "read_simple"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "! ~&&",
+      "tmp_file.txt"
+    ],
+    "name": "write_symbols"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "tmp_file.txt",
+      "/path/to/tmp/"
+    ],
+    "name": "read_symbols"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "abcde fgh",
+      "tmp_file.txt"
+    ],
+    "name": "write_spaces"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "tmp_file.txt",
+      "/path/to/tmp/"
+    ],
+    "name": "read_spaces"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "ab\ncd\nefg\n",
+      "tmp_file.txt"
+    ],
+    "name": "write_multiline"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "tmp_file.txt",
+      "/path/to/tmp/"
+    ],
+    "name": "read_multiline"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/copytree_example_tmp",
+      "511"
+    ],
+    "name": "makedirs makedirs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "some file content",
+      "[START_DIR]/copytree_example_tmp/dummy_file"
+    ],
+    "name": "write [START_DIR]/copytree_example_tmp/dummy_file"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[START_DIR]/copytree_example_tmp",
+      "[START_DIR]/copytree_example_tmp2",
+      "0"
+    ],
+    "name": "copytree"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/copytree_example_tmp2/dummy_file",
+      "/path/to/tmp/"
+    ],
+    "name": "read [START_DIR]/copytree_example_tmp2/dummy_file"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
+      "/path/to/tmp/",
+      "[START_DIR]/copytree_example_tmp/*"
+    ],
+    "name": "glob"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/copytree_example_tmp"
+    ],
+    "infra_step": true,
+    "name": "rmtree cleanup"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/copytree_example_tmp2"
+    ],
+    "infra_step": true,
+    "name": "rmtree cleanup2"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/file/examples/full.py b/infra/bots/recipe_modules/file/examples/full.py
new file mode 100644
index 0000000..b6f00ea
--- /dev/null
+++ b/infra/bots/recipe_modules/file/examples/full.py
@@ -0,0 +1,101 @@
+# 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.
+
+
+# TODO(borenet): This module belongs in the recipe engine. Remove it from this
+# repo once it has been moved.
+
+
+from recipe_engine.types import freeze
+
+DEPS = [
+  'depot_tools/infra_paths',
+  'file',
+  'recipe_engine/path',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+]
+
+
+TEST_CONTENTS = freeze({
+  'simple': 'abcde',
+  'spaces': 'abcde fgh',
+  'symbols': '! ~&&',
+  'multiline': '''ab
+cd
+efg
+''',
+})
+
+
+def RunSteps(api):
+  # listdir demo.
+  result = api.file.listdir('fake dir', '/fake/dir')
+  for element in result:
+    api.step('manipulate %s' % str(element), ['some', 'command'])
+
+  result = api.file.listdir('other', '/faker/dir')
+  for element in result:
+    api.step('manipulate %s' % str(element), ['some', 'command'])
+
+  # mkdtemp demo.
+  for prefix in ('prefix_a', 'prefix_b'):
+    # Create temp dir.
+    temp_dir = api.path.mkdtemp(prefix)
+    assert api.path.exists(temp_dir)
+    # Make |temp_dir| surface in expectation files.
+    api.step('print %s' % prefix, ['echo', temp_dir])
+
+  # move demo
+  api.file.move(
+      'move',
+      api.path['start_dir'].join('source'),
+      api.path['start_dir'].join('destination'))
+
+  # remove demo
+  api.file.remove('remove', api.path['start_dir'].join('some_file'))
+
+  # rmcontents demo
+  api.file.rmcontents('rmcontents', api.path['start_dir'].join('some_dir'))
+
+  # rmwildcard demo
+  api.file.rmwildcard('*.o', api.path['start_dir'])
+
+  for name, content in TEST_CONTENTS.iteritems():
+    api.file.write('write_%s' % name, 'tmp_file.txt', content)
+    actual_content = api.file.read(
+        'read_%s' % name, 'tmp_file.txt',
+        test_data=content
+    )
+    msg = 'expected %s but got %s' % (content, actual_content)
+    assert actual_content == content, msg
+
+  try:
+    # copytree
+    content = 'some file content'
+    tmp_dir = api.path['start_dir'].join('copytree_example_tmp')
+    api.file.makedirs('makedirs', tmp_dir)
+    path = tmp_dir.join('dummy_file')
+    api.file.write('write %s' % path, path, content)
+    new_tmp = api.path['start_dir'].join('copytree_example_tmp2')
+    new_path = new_tmp.join('dummy_file')
+    api.file.copytree('copytree', tmp_dir, new_tmp)
+    actual_content = api.file.read('read %s' % new_path, new_path,
+                                   test_data=content)
+    assert actual_content == content
+
+    # glob.
+    files = api.file.glob(
+        'glob', tmp_dir.join('*'),
+        test_data=[tmp_dir.join('dummy_file')])
+    assert files == [tmp_dir.join('dummy_file')], files
+
+  finally:
+    api.file.rmtree('cleanup', tmp_dir)
+    api.file.rmtree('cleanup2', new_tmp)
+
+
+def GenTests(api):
+  yield api.test('file_io') + api.file.listdir('other', ['aaa'])
+
diff --git a/infra/bots/recipe_modules/file/resources/fileutil.py b/infra/bots/recipe_modules/file/resources/fileutil.py
new file mode 100755
index 0000000..ec73a9b
--- /dev/null
+++ b/infra/bots/recipe_modules/file/resources/fileutil.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+
+# TODO(borenet): This module belongs in the recipe engine. Remove it from this
+# repo once it has been moved.
+
+
+"""Utility exporting basic filesystem operations.
+
+This file was cut from "scripts/common/chromium_utils.py" at:
+91310531c31fa645256b4fb5d44b460c42b3e151
+"""
+
+import argparse
+import errno
+import fnmatch
+import os
+import shutil
+import subprocess
+import sys
+import time
+
+
+def _LocateFiles(pattern, root):
+  """Yeilds files matching pattern found in root and its subdirectories.
+
+  An exception is thrown if root doesn't exist."""
+  for path, _, files in os.walk(os.path.abspath(root)):
+    for filename in fnmatch.filter(files, pattern):
+      yield os.path.join(path, filename)
+
+
+def _RemoveFilesWildcards(file_wildcard, root):
+  """Removes files matching 'file_wildcard' in root and its subdirectories, if
+  any exists.
+
+  An exception is thrown if root doesn't exist."""
+  for item in _LocateFiles(file_wildcard, root):
+    try:
+      os.remove(item)
+    except OSError, e:
+      if e.errno != errno.ENOENT:
+        raise
+
+
+def _RemoveContents(path):
+  if os.path.exists(path):
+    for p in (os.path.join(path, x) for x in os.listdir(path)):
+      if os.path.isdir(p):
+        _RemoveDirectory(p)
+      else:
+        os.unlink(p)
+
+
+def _RemoveDirectory(*path):
+  """Recursively removes a directory, even if it's marked read-only.
+
+  Remove the directory located at *path, if it exists.
+
+  shutil.rmtree() doesn't work on Windows if any of the files or directories
+  are read-only, which svn repositories and some .svn files are.  We need to
+  be able to force the files to be writable (i.e., deletable) as we traverse
+  the tree.
+
+  Even with all this, Windows still sometimes fails to delete a file, citing
+  a permission error (maybe something to do with antivirus scans or disk
+  indexing).  The best suggestion any of the user forums had was to wait a
+  bit and try again, so we do that too.  It's hand-waving, but sometimes it
+  works. :/
+  """
+  file_path = os.path.join(*path)
+  if not os.path.exists(file_path):
+    return
+
+  if sys.platform == 'win32':
+    # Give up and use cmd.exe's rd command.
+    file_path = os.path.normcase(file_path)
+    for _ in xrange(3):
+      print 'RemoveDirectory running %s' % (' '.join(
+          ['cmd.exe', '/c', 'rd', '/q', '/s', file_path]))
+      if not subprocess.call(['cmd.exe', '/c', 'rd', '/q', '/s', file_path]):
+        break
+      print '  Failed'
+      time.sleep(3)
+    return
+
+  def RemoveWithRetry_non_win(rmfunc, path):
+    if os.path.islink(path):
+      return os.remove(path)
+    else:
+      return rmfunc(path)
+
+  remove_with_retry = RemoveWithRetry_non_win
+
+  def RmTreeOnError(function, path, excinfo):
+    r"""This works around a problem whereby python 2.x on Windows has no ability
+    to check for symbolic links.  os.path.islink always returns False.  But
+    shutil.rmtree will fail if invoked on a symbolic link whose target was
+    deleted before the link.  E.g., reproduce like this:
+    > mkdir test
+    > mkdir test\1
+    > mklink /D test\current test\1
+    > python -c "import chromium_utils; chromium_utils.RemoveDirectory('test')"
+    To avoid this issue, we pass this error-handling function to rmtree.  If
+    we see the exact sort of failure, we ignore it.  All other failures we re-
+    raise.
+    """
+
+    exception_type = excinfo[0]
+    exception_value = excinfo[1]
+    # If shutil.rmtree encounters a symbolic link on Windows, os.listdir will
+    # fail with a WindowsError exception with an ENOENT errno (i.e., file not
+    # found).  We'll ignore that error.  Note that WindowsError is not defined
+    # for non-Windows platforms, so we use OSError (of which it is a subclass)
+    # to avoid lint complaints about an undefined global on non-Windows
+    # platforms.
+    if (function is os.listdir) and issubclass(exception_type, OSError):
+      if exception_value.errno == errno.ENOENT:
+        # File does not exist, and we're trying to delete, so we can ignore the
+        # failure.
+        print 'WARNING:  Failed to list %s during rmtree.  Ignoring.\n' % path
+      else:
+        raise
+    else:
+      raise
+
+  for root, dirs, files in os.walk(file_path, topdown=False):
+    # For POSIX:  making the directory writable guarantees removability.
+    # Windows will ignore the non-read-only bits in the chmod value.
+    os.chmod(root, 0770)
+    for name in files:
+      remove_with_retry(os.remove, os.path.join(root, name))
+    for name in dirs:
+      remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
+                        os.path.join(root, name))
+
+  remove_with_retry(os.rmdir, file_path)
+
+
+def main(args):
+  parser = argparse.ArgumentParser()
+
+  subparsers = parser.add_subparsers()
+
+  # Subcommand: rmtree
+  subparser = subparsers.add_parser('rmtree',
+      help='Recursively remove a directory.')
+  subparser.add_argument('path', nargs='+', help='A path to remove.')
+  subparser.set_defaults(func=lambda opts: _RemoveDirectory(*opts.path))
+
+  # Subcommand: rmcontents
+  subparser = subparsers.add_parser('rmcontents',
+      help='Recursively remove the contents of a directory.')
+  subparser.add_argument('path', help='The target directory.')
+  subparser.set_defaults(func=lambda opts: _RemoveContents(opts.path))
+
+  # Subcommand: rmwildcard
+  subparser = subparsers.add_parser('rmwildcard',
+      help='Recursively remove the contents of a directory.')
+  subparser.add_argument('root', help='The directory to search through.')
+  subparser.add_argument('wildcard', help='The wildcard expression to remove.')
+  subparser.set_defaults(func=lambda opts:
+      _RemoveFilesWildcards(opts.wildcard, opts.root))
+
+  # Parse arguments.
+  opts = parser.parse_args(args)
+  opts.func(opts)
+
+
+if __name__ == '__main__':
+  main(sys.argv[1:])
diff --git a/infra/bots/recipe_modules/file/test_api.py b/infra/bots/recipe_modules/file/test_api.py
new file mode 100644
index 0000000..8d13cba
--- /dev/null
+++ b/infra/bots/recipe_modules/file/test_api.py
@@ -0,0 +1,18 @@
+# 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.
+
+
+# TODO(borenet): This module belongs in the recipe engine. Remove it from this
+# repo once it has been moved.
+
+
+from recipe_engine import recipe_test_api
+
+
+class FileTestApi(recipe_test_api.RecipeTestApi):
+  def listdir(self, dirname, files):
+    return self.step_data(
+      'listdir %s' % dirname,
+      self.m.json.output(files))
+
diff --git a/infra/bots/recipe_modules/flavor/__init__.py b/infra/bots/recipe_modules/flavor/__init__.py
index 408819d..ac84d20 100644
--- a/infra/bots/recipe_modules/flavor/__init__.py
+++ b/infra/bots/recipe_modules/flavor/__init__.py
@@ -3,10 +3,11 @@
 # found in the LICENSE file.
 
 DEPS = [
-  'build/adb',
-  'build/file',
   'builder_name_schema',
   'depot_tools/bot_update',
+  'env',
+  'file',
+  'recipe_engine/context',
   'recipe_engine/path',
   'recipe_engine/platform',
   'recipe_engine/properties',
@@ -16,7 +17,3 @@
   'run',
   'vars',
 ]
-
-
-# TODO(borenet): Add coverage
-DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/flavor/api.py b/infra/bots/recipe_modules/flavor/api.py
index b3093b4..a2ac1f1 100644
--- a/infra/bots/recipe_modules/flavor/api.py
+++ b/infra/bots/recipe_modules/flavor/api.py
@@ -11,6 +11,7 @@
 from . import default_flavor
 from . import flutter_flavor
 from . import gn_android_flavor
+from . import gn_chromebook_flavor
 from . import gn_chromecast_flavor
 from . import gn_flavor
 from . import ios_flavor
@@ -35,11 +36,16 @@
   return ('Chromecast' in builder_cfg.get('extra_config', '') or
           'Chromecast' in builder_cfg.get('os', ''))
 
+def is_chromebook(builder_cfg):
+  return ('Chromebook' in builder_cfg.get('extra_config', '') or
+          'ChromeOS' in builder_cfg.get('os', ''))
+
 def is_flutter(builder_cfg):
   return 'Flutter' in builder_cfg.get('extra_config', '')
 
 def is_ios(builder_cfg):
-  return 'iOS' == builder_cfg.get('os', '')
+  return ('iOS' in builder_cfg.get('extra_config', '') or
+          'iOS' == builder_cfg.get('os', ''))
 
 def is_pdfium(builder_cfg):
   return 'PDFium' in builder_cfg.get('extra_config', '')
@@ -52,19 +58,21 @@
   def get_flavor(self, builder_cfg):
     """Return a flavor utils object specific to the given builder."""
     if is_flutter(builder_cfg):
-      return flutter_flavor.FlutterFlavorUtils(self.m)
+      return flutter_flavor.FlutterFlavorUtils(self)
     if is_chromecast(builder_cfg):
-      return gn_chromecast_flavor.GNChromecastFlavorUtils(self.m)
+      return gn_chromecast_flavor.GNChromecastFlavorUtils(self)
+    if is_chromebook(builder_cfg):
+      return gn_chromebook_flavor.GNChromebookFlavorUtils(self)
     if is_android(builder_cfg):
-      return gn_android_flavor.GNAndroidFlavorUtils(self.m)
+      return gn_android_flavor.GNAndroidFlavorUtils(self)
     elif is_ios(builder_cfg):
-      return ios_flavor.iOSFlavorUtils(self.m)
+      return ios_flavor.iOSFlavorUtils(self)
     elif is_pdfium(builder_cfg):
-      return pdfium_flavor.PDFiumFlavorUtils(self.m)
+      return pdfium_flavor.PDFiumFlavorUtils(self)
     elif is_valgrind(builder_cfg):
-      return valgrind_flavor.ValgrindFlavorUtils(self.m)
+      return valgrind_flavor.ValgrindFlavorUtils(self)
     else:
-      return gn_flavor.GNFlavorUtils(self.m)
+      return gn_flavor.GNFlavorUtils(self)
 
   def setup(self):
     self._f = self.get_flavor(self.m.vars.builder_cfg)
@@ -100,8 +108,8 @@
   def create_clean_device_dir(self, path):
     return self._f.create_clean_device_dir(path)
 
-  def read_file_on_device(self, path):
-    return self._f.read_file_on_device(path)
+  def read_file_on_device(self, path, **kwargs):
+    return self._f.read_file_on_device(path, **kwargs)
 
   def remove_file_on_device(self, path):
     return self._f.remove_file_on_device(path)
@@ -137,10 +145,11 @@
     device_version_file = self.device_path_join(
         self.device_dirs.tmp_dir, version_file)
     if str(actual_version_file) != str(device_version_file):
-      try:
-        device_version = self.read_file_on_device(device_version_file)
-      except self.m.step.StepFailure:   # pragma: nocover
-        device_version = VERSION_NONE   # pragma: nocover
+      device_version = self.read_file_on_device(device_version_file,
+                                                abort_on_failure=False,
+                                                fail_build_on_failure=False)
+      if not device_version:
+        device_version = VERSION_NONE
       if device_version != host_version:
         self.remove_file_on_device(device_version_file)
         self.create_clean_device_dir(device_path)
diff --git a/infra/bots/recipe_modules/flavor/default_flavor.py b/infra/bots/recipe_modules/flavor/default_flavor.py
index dacb0fd..22a4487 100644
--- a/infra/bots/recipe_modules/flavor/default_flavor.py
+++ b/infra/bots/recipe_modules/flavor/default_flavor.py
@@ -75,8 +75,15 @@
   copying files between the host and Android device, as well as the
   'step' function, so that commands may be run through ADB.
   """
-  def __init__(self, m):
-    self.m = m
+  def __init__(self, module):
+    # Store a pointer to the parent recipe module (SkiaFlavorApi) so that
+    # FlavorUtils objects can do recipe module-like things, like run steps or
+    # access module-level resources.
+    self.module = module
+
+    # self.m is just a shortcut so that FlavorUtils objects can use the same
+    # syntax as regular recipe modules to run steps, eg: self.m.step(...)
+    self.m = module.m
     self._chrome_path = None
     self._win_toolchain_dir = self.m.vars.slave_dir.join(WIN_TOOLCHAIN_DIR)
     win_toolchain_asset_path = self.m.vars.infrabots_dir.join(
@@ -104,7 +111,7 @@
       raise ValueError('For builders who do not have attached devices, copying '
                        'from host to device is undefined and only allowed if '
                        'host_path and device_path are the same (%s vs %s).' % (
-                       str(host_dir), str(device_dir)))  # pragma: no cover
+                       str(host_dir), str(device_dir)))
 
   def copy_directory_contents_to_host(self, device_dir, host_dir):
     """Like shutil.copytree(), but for copying from a connected device."""
@@ -114,13 +121,13 @@
       raise ValueError('For builders who do not have attached devices, copying '
                        'from device to host is undefined and only allowed if '
                        'host_path and device_path are the same (%s vs %s).' % (
-                       str(host_dir), str(device_dir)))  # pragma: no cover
+                       str(host_dir), str(device_dir)))
 
   def copy_file_to_device(self, host_path, device_path):
     """Like shutil.copyfile, but for copying to a connected device."""
     # For "normal" builders who don't have an attached device, we expect
     # host_dir and device_dir to be the same.
-    if str(host_path) != str(device_path):  # pragma: no cover
+    if str(host_path) != str(device_path):
       raise ValueError('For builders who do not have attached devices, copying '
                        'from host to device is undefined and only allowed if '
                        'host_path and device_path are the same (%s vs %s).' % (
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json
new file mode 100644
index 0000000..c788a98
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android_Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android_Vulkan/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_darwin\" ndk_api=24 skia_enable_vulkan_debug_layers=false target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android_Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android_Vulkan/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android_Vulkan"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
new file mode 100644
index 0000000..27aa182
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
@@ -0,0 +1,74 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/tools/build_command_buffer.py",
+      "--chrome-dir",
+      "[CUSTOM_/_B_WORK]",
+      "--output-dir",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
+      "--no-sync",
+      "--make-output-dir"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "name": "build command_buffer"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
+      "--args=cc=\"clang\" cxx=\"clang++\" extra_cflags=[\"-O1\"] target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json
new file mode 100644
index 0000000..065b5f9
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_component_build=true is_debug=false is_official_build=true skia_enable_effects=false skia_enable_gpu=false skia_enable_pdf=false skia_use_expat=false skia_use_libjpeg_turbo=false skia_use_libpng=false skia_use_libwebp=false skia_use_zlib=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Shared.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Shared.json
new file mode 100644
index 0000000..c290ed5
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Shared.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Shared"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Shared/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_component_build=true is_debug=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Shared"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Shared/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Shared"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json
new file mode 100644
index 0000000..159bbd3
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/linux_vulkan_sdk\" target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
new file mode 100644
index 0000000..f5a57a6
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\", \"-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE\"] target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json
new file mode 100644
index 0000000..e65aa3d
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_angle=true target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json
new file mode 100644
index 0000000..42523b0
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-march=native\", \"-fomit-frame-pointer\", \"-O3\", \"-ffp-contract=off\"] is_debug=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
new file mode 100644
index 0000000..aab6966
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
@@ -0,0 +1,67 @@
+[
+  {
+    "cmd": [
+      "gclient",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/flutter/src/out/android_release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree android_release"
+  },
+  {
+    "cmd": [
+      "flutter/tools/gn",
+      "--runtime-mode=release",
+      "--android"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/android_release",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "build_flutter"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json
new file mode 100644
index 0000000..4111ae3
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_mesa=true target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
new file mode 100644
index 0000000..8a3d5be
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
@@ -0,0 +1,71 @@
+[
+  {
+    "cmd": [
+      "gclient",
+      "runhook",
+      "gn_linux64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "build/linux/sysroot_scripts/install-sysroot.py",
+      "--arch=amd64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "sysroot"
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/skia",
+      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia=true"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/skia",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "build_pdfium"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json
new file mode 100644
index 0000000..1cf1c0c
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json
@@ -0,0 +1,71 @@
+[
+  {
+    "cmd": [
+      "gclient",
+      "runhook",
+      "gn_linux64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "build/linux/sysroot_scripts/install-sysroot.py",
+      "--arch=amd64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "sysroot"
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/skia",
+      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia_paths=true"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/skia",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "build_pdfium"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-Clang-arm64-Release-Android_FrameworkDefs.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-Clang-arm64-Release-Android_FrameworkDefs.json
new file mode 100644
index 0000000..38b00ca
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-Clang-arm64-Release-Android_FrameworkDefs.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-Clang-arm64-Release-Android_FrameworkDefs"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-Clang-arm64-Release-Android_FrameworkDefs/Release",
+      "--args=is_debug=false ndk=\"[START_DIR]/n\" skia_enable_android_framework_defines=true target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-Clang-arm64-Release-Android_FrameworkDefs"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-Clang-arm64-Release-Android_FrameworkDefs/Release"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-Clang-arm64-Release-Android_FrameworkDefs"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Debug-GDI.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Debug-GDI.json
new file mode 100644
index 0000000..2d223fe
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Debug-GDI.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-GDI"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-GDI/Debug_x64",
+      "--args=skia_use_gdi=true target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-GDI"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-GDI/Debug_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-GDI"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Debug-NoGPU.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Debug-NoGPU.json
new file mode 100644
index 0000000..808efb2
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Debug-NoGPU.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-NoGPU"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-NoGPU/Debug_x64",
+      "--args=skia_enable_gpu=false target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-NoGPU"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-NoGPU/Debug_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Debug-NoGPU"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Release-Exceptions.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Release-Exceptions.json
new file mode 100644
index 0000000..e73a958
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Release-Exceptions.json
@@ -0,0 +1,54 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Exceptions"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Exceptions/Release_x64",
+      "--args=extra_cflags=[\"/EHsc\"] is_debug=false target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Exceptions"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Exceptions/Release_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Exceptions"
+    },
+    "name": "ninja"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
new file mode 100644
index 0000000..bec12ba
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
@@ -0,0 +1,92 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
+      "--args=is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/win_vulkan_sdk\" target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/win_vulkan_sdk",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android.json
new file mode 100644
index 0000000..51358ad
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android.json
@@ -0,0 +1,804 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"None\""
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "file.txt",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push file.txt file.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --some-flag; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release.json
new file mode 100644
index 0000000..07d05b2
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release.json
@@ -0,0 +1,828 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/armhf_sysroot/lib",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_asmflags=[\"--target=armv7a-linux-gnueabihf\", \"--sysroot=[START_DIR]/armhf_sysroot\", \"-march=armv7-a\", \"-mfpu=neon\", \"-mthumb\"] extra_cflags=[\"--target=armv7a-linux-gnueabihf\", \"--sysroot=[START_DIR]/armhf_sysroot\", \"-I[START_DIR]/chromebook_arm_gles/include\", \"-I[START_DIR]/armhf_sysroot/include\", \"-I[START_DIR]/armhf_sysroot/include/c++/4.8.4\", \"-I[START_DIR]/armhf_sysroot/include/c++/4.8.4/arm-linux-gnueabihf\", \"-DMESA_EGL_NO_X11_HEADERS\"] extra_ldflags=[\"--target=armv7a-linux-gnueabihf\", \"--sysroot=[START_DIR]/armhf_sysroot\", \"-B[START_DIR]/armhf_sysroot/bin\", \"-B[START_DIR]/armhf_sysroot/gcc-cross\", \"-L[START_DIR]/armhf_sysroot/gcc-cross\", \"-L[START_DIR]/armhf_sysroot/lib\", \"-L[START_DIR]/chromebook_arm_gles/lib\"] is_debug=false skia_use_egl=true skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"None\""
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/armhf_sysroot/lib",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release",
+      "nanobench",
+      "dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/armhf_sysroot/lib",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nSSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')\nwith open(SSH_MACHINE_FILE, 'r') as f:\n  print f.read()\n"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read chromeos ip",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@SSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(SSH_MACHINE_FILE, 'r') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "file.txt",
+      "foo@127.0.0.1:file.txt"
+    ],
+    "infra_step": true,
+    "name": "scp file.txt foo@127.0.0.1:file.txt",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "device_results_dir"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "device_results_dir"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/resources"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "sudo",
+      "mount",
+      "-i",
+      "-o",
+      "remount,exec",
+      "/home/chronos"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "remount /home/chronos/user/ as exec"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/bin"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/bin"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/bin"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/bin"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skia/resources",
+      "foo@127.0.0.1:/home/chronos/user/resources"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skia/resources foo@127.0.0.1:/home/chronos/user/resources",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/skps"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skp",
+      "foo@127.0.0.1:/home/chronos/user/skps"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skp foo@127.0.0.1:/home/chronos/user/skps",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SKP_VERSION foo@127.0.0.1:/home/chronos/user/SKP_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/images"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skimage",
+      "foo@127.0.0.1:/home/chronos/user/images"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skimage foo@127.0.0.1:/home/chronos/user/images",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SK_IMAGE_VERSION foo@127.0.0.1:/home/chronos/user/SK_IMAGE_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/svgs"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/svg",
+      "foo@127.0.0.1:/home/chronos/user/svgs"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/svg foo@127.0.0.1:/home/chronos/user/svgs",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SVG_VERSION foo@127.0.0.1:/home/chronos/user/SVG_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/out/Release/dm",
+      "foo@127.0.0.1:/home/chronos/user/bin/dm"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/out/Release/dm foo@127.0.0.1:/home/chronos/user/bin/dm",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "chmod",
+      "+x",
+      "/home/chronos/user/bin/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "chmod dm"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "/home/chronos/user/bin/dm",
+      "--some-flag"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "foo@127.0.0.1:/home/chronos/user/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "infra_step": true,
+    "name": "scp -r foo@127.0.0.1:/home/chronos/user/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "foo@127.0.0.1:/home/chronos/user/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release/data"
+    ],
+    "infra_step": true,
+    "name": "scp -r foo@127.0.0.1:/home/chronos/user/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release/data",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release.json
new file mode 100644
index 0000000..962231f
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release.json
@@ -0,0 +1,806 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=ar=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabi-ar\" cc=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabi-gcc\" cxx=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabi-g++\" extra_cflags=[\"-I[START_DIR]/chromebook_arm_gles/include\", \"-DMESA_EGL_NO_X11_HEADERS\", \"-DEGL_NO_IMAGE_EXTERNAL\", \"-DSK_NO_COMMAND_BUFFER\", \"-Wno-error=unused-function\", \"-g0\"] extra_ldflags=[\"-static-libstdc++\", \"-static-libgcc\", \"-L[START_DIR]/cast_toolchain/armv7a/lib\"] 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=\"None\""
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release",
+      "nanobench"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nCHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')\nwith open(CHROMECAST_IP_FILE, 'r') as f:\n  print f.read()\n"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read chromecast ip",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(CHROMECAST_IP_FILE, 'r') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "connect",
+      "192.168.1.2:5555"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "adb connect 192.168.1.2:5555"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "file.txt",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push file.txt file.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/cache/skia/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /cache/skia/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /cache/skia/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "/cache/skia/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/cache/skia/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /cache/skia/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /cache/skia/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/cache/skia/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /cache/skia/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/cache/skia/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "/cache/skia/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/cache/skia/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /cache/skia/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/cache/skia/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /cache/skia/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/cache/skia/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /cache/skia/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/cache/skia/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "/cache/skia/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/cache/skia/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /cache/skia/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/cache/skia/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /cache/skia/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/cache/skia/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "root@192.168.1.2",
+      "/cache/skia/dm",
+      "--some-flag"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/cache/skia/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /cache/skia/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/cache/skia/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /cache/skia/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN.json
new file mode 100644
index 0000000..590adde
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN.json
@@ -0,0 +1,215 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false sanitize=\"ASAN\" skia_enable_spirv_validation=false"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "device_results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree device_results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "device_results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs device_results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+      "[START_DIR]",
+      "[START_DIR]/out/Release/dm",
+      "--some-flag"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ASAN_OPTIONS": "symbolize=1 detect_leaks=1",
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LSAN_OPTIONS": "symbolize=1 print_suppressions=1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
+      "SKIA_OUT": "[START_DIR]/out",
+      "UBSAN_OPTIONS": "symbolize=1 print_stacktrace=1"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-MSAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-MSAN.json
new file mode 100644
index 0000000..b2d09d1
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-MSAN.json
@@ -0,0 +1,213 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\", \"-L[START_DIR]/clang_linux/msan\"] is_debug=false sanitize=\"MSAN\" skia_enable_gpu=false skia_use_fontconfig=false"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "device_results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree device_results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "device_results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs device_results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+      "[START_DIR]",
+      "[START_DIR]/out/Release/dm",
+      "--some-flag"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/msan",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release.json
new file mode 100644
index 0000000..c41083b
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release.json
@@ -0,0 +1,213 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "device_results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree device_results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "device_results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs device_results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+      "[START_DIR]",
+      "catchsegv",
+      "[START_DIR]/out/Release/dm",
+      "--some-flag"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
new file mode 100644
index 0000000..5ec8b5c
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
@@ -0,0 +1,215 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "device_results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree device_results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "device_results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs device_results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "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]/out/Release/dm",
+      "--some-flag"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug.json
new file mode 100644
index 0000000..9604e7c
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug.json
@@ -0,0 +1,207 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn.exe",
+      "gen",
+      "[START_DIR]/out/Debug_x64",
+      "--args=windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[START_DIR]/out/Debug_x64"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "device_results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree device_results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "device_results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs device_results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/out/Debug_x64/dm",
+      "--some-flag"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug.json
new file mode 100644
index 0000000..3614d82
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug.json
@@ -0,0 +1,597 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"clang\" cxx=\"clang++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/gn/package_ios.py",
+      "[START_DIR]/out/Debug/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "package dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/gn/package_ios.py",
+      "[START_DIR]/out/Debug/nanobench"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "package nanobench"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "file.txt",
+      "file.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file file.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "device_results_dir"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "device_results_dir"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "ios.py"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "setup_device"
+  },
+  {
+    "cmd": [
+      "ideviceinstaller",
+      "-i",
+      "[START_DIR]/out/Debug/dm.app"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "install_dm"
+  },
+  {
+    "cmd": [
+      "ideviceinstaller",
+      "-i",
+      "[START_DIR]/out/Debug/nanobench.app"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "install_nanobench"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skia/resources",
+      "resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skia/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm skps"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir skps"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skp",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skp"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm images"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir images"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skimage",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skimage"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm svgs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir svgs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/svg",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/svg"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "idevice-app-runner",
+      "-s",
+      "com.google.dm",
+      "--args",
+      "--some-flag"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_pull_if_needed",
+      "dm",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull_if_needed dm"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_pull_if_needed",
+      "perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug/data"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull_if_needed perf"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/exceptions.json b/infra/bots/recipe_modules/flavor/examples/full.expected/exceptions.json
new file mode 100644
index 0000000..7726ecb
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/exceptions.json
@@ -0,0 +1,7 @@
+[
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
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
new file mode 100644
index 0000000..7b6e6e9
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/failed_infra_step.json
@@ -0,0 +1,824 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"None\""
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "file.txt",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push file.txt file.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --some-flag; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "reboot",
+      "-p"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "shut down device to quarantine bot"
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "reason": "Failed build steps: dump log",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
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
new file mode 100644
index 0000000..4763d12
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/failed_read_version.json
@@ -0,0 +1,808 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"None\""
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "file.txt",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push file.txt file.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "results_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "results_dir",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --some-flag; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/flavor/examples/full.py b/infra/bots/recipe_modules/flavor/examples/full.py
new file mode 100644
index 0000000..066e849
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/examples/full.py
@@ -0,0 +1,139 @@
+# 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 = [
+  'flavor',
+  'recipe_engine/properties',
+  'recipe_engine/raw_io',
+  'run',
+  'vars',
+]
+
+
+def test_exceptions(api):
+  try:
+    api.flavor.copy_directory_contents_to_device('src', 'dst')
+  except ValueError:
+    pass
+  try:
+    api.flavor.copy_directory_contents_to_host('src', 'dst')
+  except ValueError:
+    pass
+  try:
+    api.flavor.copy_file_to_device('src', 'dst')
+  except ValueError:
+    pass
+
+
+def RunSteps(api):
+  api.vars.setup()
+  api.flavor.setup()
+
+  if api.properties.get('is_testing_exceptions') == 'True':
+    return test_exceptions(api)
+
+  api.flavor.compile('dm')
+  api.flavor.copy_extra_build_products(api.vars.swarming_out_dir)
+  assert str(api.flavor.out_dir) != ''
+  if 'Build' not in api.properties['buildername']:
+    try:
+      api.flavor.copy_file_to_device('file.txt', 'file.txt')
+      api.flavor.create_clean_host_dir('results_dir')
+      api.flavor.create_clean_device_dir('device_results_dir')
+      api.flavor.install_everything()
+      api.flavor.step('dm', ['dm', '--some-flag'])
+      api.flavor.copy_directory_contents_to_host(
+          api.flavor.device_dirs.dm_dir, api.vars.dm_dir)
+      api.flavor.copy_directory_contents_to_host(
+          api.flavor.device_dirs.perf_data_dir, api.vars.perf_data_dir)
+    finally:
+      api.flavor.cleanup_steps()
+  api.run.check_failure()
+
+
+TEST_BUILDERS = [
+  'Build-Mac-Clang-arm64-Debug-Android_Vulkan',
+  'Build-Mac-Clang-x86_64-Debug-CommandBuffer',
+  'Build-Ubuntu-Clang-x86_64-Release-Mini',
+  'Build-Ubuntu-Clang-x86_64-Release-Shared',
+  'Build-Ubuntu-Clang-x86_64-Release-Vulkan',
+  'Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE',
+  'Build-Ubuntu-GCC-x86_64-Release-ANGLE',
+  'Build-Ubuntu-GCC-x86_64-Release-Fast',
+  'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android',
+  'Build-Ubuntu-GCC-x86_64-Release-Mesa',
+  'Build-Ubuntu-GCC-x86_64-Release-PDFium',
+  'Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths',
+  'Build-Win-Clang-arm64-Release-Android_FrameworkDefs',
+  'Build-Win-MSVC-x86_64-Debug-GDI',
+  'Build-Win-MSVC-x86_64-Debug-NoGPU',
+  'Build-Win-MSVC-x86_64-Release-Exceptions',
+  'Build-Win-MSVC-x86_64-Release-Vulkan',
+  'Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android',
+  'Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release',
+  'Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release',
+  'Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN',
+  'Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-MSAN',
+  'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release',
+  ('Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-'
+   'Valgrind_AbandonGpuContext'),
+  'Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug',
+  'Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug',
+]
+
+
+def GenTests(api):
+  for buildername in TEST_BUILDERS:
+    test = (
+      api.test(buildername) +
+      api.properties(buildername=buildername,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]')
+    )
+    if 'Chromebook' in buildername:
+      test += api.step_data(
+          'read chromeos ip',
+          stdout=api.raw_io.output('{"user_ip":"foo@127.0.0.1"}'))
+    if 'Chromecast' in buildername:
+      test += api.step_data(
+          'read chromecast ip',
+          stdout=api.raw_io.output('192.168.1.2:5555'))
+    yield test
+
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release'
+  yield (
+      api.test('exceptions') +
+      api.properties(buildername=builder,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]',
+                     is_testing_exceptions='True')
+  )
+
+  builder = 'Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android'
+  yield (
+      api.test('failed_infra_step') +
+      api.properties(buildername=builder,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.step_data('dump log', retcode=1)
+  )
+
+  builder = 'Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android'
+  yield (
+      api.test('failed_read_version') +
+      api.properties(buildername=builder,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.step_data('read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION',
+                    retcode=1)
+  )
diff --git a/infra/bots/recipe_modules/flavor/flutter_flavor.py b/infra/bots/recipe_modules/flavor/flutter_flavor.py
index 0f0302b..6b95cb8 100644
--- a/infra/bots/recipe_modules/flavor/flutter_flavor.py
+++ b/infra/bots/recipe_modules/flavor/flutter_flavor.py
@@ -20,7 +20,7 @@
     extra_config = self.m.vars.builder_cfg.get('extra_config', '')
     out_dir = configuration
 
-    with self.m.step.context({'cwd': flutter_dir}):
+    with self.m.context(cwd=flutter_dir):
       # Runhook to generate the gn binary in buildtools.
       self.m.run(
           self.m.step,
diff --git a/infra/bots/recipe_modules/flavor/gn_android_flavor.py b/infra/bots/recipe_modules/flavor/gn_android_flavor.py
index 36c14df..1ff8b8c 100644
--- a/infra/bots/recipe_modules/flavor/gn_android_flavor.py
+++ b/infra/bots/recipe_modules/flavor/gn_android_flavor.py
@@ -24,11 +24,11 @@
         tmp_dir       = self.m.vars.android_data_dir)
 
   def _run(self, title, *cmd, **kwargs):
-    with self.m.step.context({'cwd': self.m.vars.skia_dir}):
+    with self.m.context(cwd=self.m.vars.skia_dir):
       return self.m.run(self.m.step, title, cmd=list(cmd), **kwargs)
 
   def _py(self, title, script, infra_step=True):
-    with self.m.step.context({'cwd': self.m.vars.skia_dir}):
+    with self.m.context(cwd=self.m.vars.skia_dir):
       return self.m.run(self.m.python, title, script=script,
                         infra_step=infra_step)
 
@@ -39,42 +39,6 @@
       kwargs['infra_step'] = True
     return self._run(title, 'adb', *cmd, **kwargs)
 
-  # Waits for an android device to be available
-  def _wait_for_device(self):
-    self.m.run(self.m.python.inline, 'wait for device', program="""
-      import subprocess
-      import sys
-      import time
-
-      kicks = 0
-      while True:
-
-        times = 0
-        while times < 30:
-          print 'Waiting for the device to be connected and ready.'
-          try:
-            times += 1
-            output = subprocess.check_output(['adb', 'shell',
-                                              'getprop', 'sys.boot_completed'])
-            if '1' in output:
-              print 'Connected'
-              sys.exit(0)
-          except subprocess.CalledProcessError:
-            # no device connected/authorized yet
-            pass
-          time.sleep(5)
-        if kicks >= 3:
-          break
-        print 'Giving the device a "kick" by trying to reboot it.'
-        kicks += 1
-        print subprocess.check_output(['adb', 'reboot'])
-
-      print 'Timed out waiting for device'
-      sys.exit(1)
-      """,
-      infra_step=True)
-
-
   def compile(self, unused_target):
     compiler      = self.m.vars.builder_cfg.get('compiler')
     configuration = self.m.vars.builder_cfg.get('configuration')
@@ -121,10 +85,6 @@
     self._run('ninja', ninja, '-C', self.out_dir)
 
   def install(self):
-    reboot_always = ['NexusPlayer', 'PixelC']
-    if self.m.vars.builder_cfg.get('model') in reboot_always:
-      self._adb('rebooting device', 'reboot')
-      self._wait_for_device()
     self._adb('mkdir ' + self.device_dirs.resource_dir,
               'shell', 'mkdir', '-p', self.device_dirs.resource_dir)
 
@@ -132,23 +92,24 @@
   def cleanup_steps(self):
     if self._ever_ran_adb:
       self.m.run(self.m.python.inline, 'dump log', program="""
-      import os
-      import subprocess
-      import sys
-      out = sys.argv[1]
-      log = subprocess.check_output(['adb', 'logcat', '-d'])
-      for line in log.split('\\n'):
-        tokens = line.split()
-        if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':
-          addr, path = tokens[-2:]
-          local = os.path.join(out, os.path.basename(path))
-          if os.path.exists(local):
-            sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])
-            line = line.replace(addr, addr + ' ' + sym.strip())
-        print line
-      """,
-      args=[self.m.vars.skia_out.join(self.m.vars.configuration)],
-      infra_step=True)
+          import os
+          import subprocess
+          import sys
+          out = sys.argv[1]
+          log = subprocess.check_output(['adb', 'logcat', '-d'])
+          for line in log.split('\\n'):
+            tokens = line.split()
+            if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':
+              addr, path = tokens[-2:]
+              local = os.path.join(out, os.path.basename(path))
+              if os.path.exists(local):
+                sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])
+                line = line.replace(addr, addr + ' ' + sym.strip())
+            print line
+          """,
+          args=[self.m.vars.skia_out.join(self.m.vars.configuration)],
+          infra_step=True,
+          abort_on_failure=False)
 
     # Only shutdown the device and quarantine the bot if the first failed step
     # is an infra step. If, instead, we did this for any infra failures, we
@@ -217,9 +178,11 @@
   def copy_directory_contents_to_host(self, device, host):
     self._adb('pull %s %s' % (device, host), 'pull', device, host)
 
-  def read_file_on_device(self, path):
-    return self._adb('read %s' % path,
-                     'shell', 'cat', path, stdout=self.m.raw_io.output()).stdout
+  def read_file_on_device(self, path, **kwargs):
+    rv = self._adb('read %s' % path,
+                   'shell', 'cat', path, stdout=self.m.raw_io.output(),
+                   **kwargs)
+    return rv.stdout.rstrip() if rv and rv.stdout else None
 
   def remove_file_on_device(self, path):
     self._adb('rm %s' % path, 'shell', 'rm', '-f', path)
diff --git a/infra/bots/recipe_modules/flavor/gn_chromebook_flavor.py b/infra/bots/recipe_modules/flavor/gn_chromebook_flavor.py
new file mode 100644
index 0000000..3cf2cf1
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/gn_chromebook_flavor.py
@@ -0,0 +1,202 @@
+# 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.
+
+
+from recipe_engine import recipe_api
+
+import default_flavor
+import gn_flavor
+import json
+import subprocess
+
+
+"""
+  GN Chromebook flavor utils, used for building and testing Skia for ARM
+  Chromebooks with GN
+"""
+class GNChromebookFlavorUtils(gn_flavor.GNFlavorUtils):
+
+  def __init__(self, m):
+    super(GNChromebookFlavorUtils, self).__init__(m)
+    self._user_ip = ''
+
+    self.device_dirs = default_flavor.DeviceDirs(
+      dm_dir        = self.m.vars.chromeos_homedir + 'dm_out',
+      perf_data_dir = self.m.vars.chromeos_homedir + 'perf',
+      resource_dir  = self.m.vars.chromeos_homedir + 'resources',
+      images_dir    = self.m.vars.chromeos_homedir + 'images',
+      skp_dir       = self.m.vars.chromeos_homedir + 'skps',
+      svg_dir       = self.m.vars.chromeos_homedir + 'svgs',
+      tmp_dir       = self.m.vars.chromeos_homedir)
+
+    self._bin_dir = self.m.vars.chromeos_homedir + 'bin'
+
+  @property
+  def user_ip(self):
+    if not self._user_ip:
+      ssh_info = self.m.run(self.m.python.inline, 'read chromeos ip',
+                            program="""
+      import os
+      SSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')
+      with open(SSH_MACHINE_FILE, 'r') as f:
+        print f.read()
+      """,
+      stdout=self.m.raw_io.output(),
+      infra_step=True).stdout
+
+      self._user_ip = json.loads(ssh_info).get(u'user_ip', 'ERROR')
+    return self._user_ip
+
+  def _ssh(self, title, *cmd, **kwargs):
+    if 'infra_step' not in kwargs:
+      kwargs['infra_step'] = True
+
+    ssh_cmd = ['ssh', '-oConnectTimeout=15', '-oBatchMode=yes',
+               '-t', '-t', self.user_ip] + list(cmd)
+
+    return self._run(title, ssh_cmd, **kwargs)
+
+  def install(self):
+    self._ssh('mkdir %s' % self.device_dirs.resource_dir, 'mkdir', '-p',
+              self.device_dirs.resource_dir)
+
+    # Ensure the home dir is marked executable
+    self._ssh('remount %s as exec' % self.m.vars.chromeos_homedir,
+              'sudo', 'mount', '-i', '-o', 'remount,exec', '/home/chronos')
+
+    self.create_clean_device_dir(self._bin_dir)
+
+  def compile(self, unused_target):
+    configuration = self.m.vars.builder_cfg.get('configuration')
+    os            = self.m.vars.builder_cfg.get('os')
+    target_arch   = self.m.vars.builder_cfg.get('target_arch')
+
+    clang_linux = self.m.vars.slave_dir.join('clang_linux')
+    # This is a pretty typical arm-linux-gnueabihf sysroot
+    sysroot_dir = self.m.vars.slave_dir.join('armhf_sysroot')
+    # This is the extra things needed to link against for the chromebook.
+    #  For example, the Mali GL drivers.
+    gl_dir   = self.m.vars.slave_dir.join('chromebook_arm_gles')
+
+    extra_asmflags = [
+      '--target=armv7a-linux-gnueabihf',
+      '--sysroot=%s' % sysroot_dir,
+      '-march=armv7-a',
+      '-mfpu=neon',
+      '-mthumb',
+    ]
+
+    extra_cflags = [
+      '--target=armv7a-linux-gnueabihf',
+      '--sysroot=%s' % sysroot_dir,
+      '-I%s' % gl_dir.join('include'),
+      '-I%s' % sysroot_dir.join('include'),
+      '-I%s' % sysroot_dir.join('include', 'c++', '4.8.4'),
+      '-I%s' % sysroot_dir.join('include', 'c++', '4.8.4',
+                                'arm-linux-gnueabihf'),
+      '-DMESA_EGL_NO_X11_HEADERS',
+    ]
+
+    extra_ldflags = [
+      '--target=armv7a-linux-gnueabihf',
+      '--sysroot=%s' % sysroot_dir,
+      # use sysroot's ld which can properly link things.
+      '-B%s' % sysroot_dir.join('bin'),
+      # helps locate crt*.o
+      '-B%s' % sysroot_dir.join('gcc-cross'),
+      # helps locate libgcc*.so
+      '-L%s' % sysroot_dir.join('gcc-cross'),
+      '-L%s' % sysroot_dir.join('lib'),
+      '-L%s' % gl_dir.join('lib'),
+      # Explicitly do not use lld for cross compiling like this - I observed
+      # failures like "Unrecognized reloc 41" and couldn't find out why.
+    ]
+
+    quote = lambda x: '"%s"' % x
+    args = {
+      'cc': quote(clang_linux.join('bin','clang')),
+      'cxx': quote(clang_linux.join('bin','clang++')),
+      'target_cpu': quote(target_arch),
+      'skia_use_fontconfig': 'false',
+      'skia_use_system_freetype2': 'false',
+      'skia_use_egl': 'true',
+    }
+
+    if configuration != 'Debug':
+      args['is_debug'] = 'false'
+    args['extra_asmflags'] = repr(extra_asmflags).replace("'", '"')
+    args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
+    args['extra_ldflags'] = repr(extra_ldflags).replace("'", '"')
+
+    gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.iteritems()))
+
+    gn    = 'gn.exe'    if 'Win' in os else 'gn'
+    ninja = 'ninja.exe' if 'Win' in os else 'ninja'
+    gn = self.m.vars.skia_dir.join('bin', gn)
+
+    with self.m.context(cwd=self.m.vars.skia_dir,
+                        env={'LD_LIBRARY_PATH': sysroot_dir.join('lib')}):
+      self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
+      self._run('gn gen', [gn, 'gen', self.out_dir, '--args=' + gn_args])
+      self._run('ninja', [ninja, '-C', self.out_dir, 'nanobench', 'dm'])
+
+  def create_clean_device_dir(self, path):
+    # use -f to silently return if path doesn't exist
+    self._ssh('rm %s' % path, 'rm', '-rf', path)
+    self._ssh('mkdir %s' % path, 'mkdir', '-p', path)
+
+  def read_file_on_device(self, path, **kwargs):
+    rv = self._ssh('read %s' % path,
+                   'cat', path, stdout=self.m.raw_io.output(),
+                   **kwargs)
+    return rv.stdout.rstrip() if rv and rv.stdout else None
+
+  def remove_file_on_device(self, path):
+    # use -f to silently return if path doesn't exist
+    self._ssh('rm %s' % path, 'rm', '-f', path)
+
+  def _prefix_device_path(self, device_path):
+    return '%s:%s' % (self.user_ip, device_path)
+
+  def copy_file_to_device(self, host_path, device_path):
+    device_path = self._prefix_device_path(device_path)
+    # Recipe
+    self.m.python.inline(str('scp %s %s' % (host_path, device_path)),
+    """
+    import subprocess
+    import sys
+    host = sys.argv[1]
+    device   = sys.argv[2]
+    print subprocess.check_output(['scp', host, device])
+    """, args=[host_path, device_path], infra_step=True)
+
+  def _copy_dir(self, src, dest):
+    # We can't use rsync to communicate with the chromebooks because the
+    # chromebooks don't have rsync installed on them.
+    self.m.python.inline(str('scp -r %s %s' % (src, dest)),
+    """
+    import subprocess
+    import sys
+    src = sys.argv[1] + '/*'
+    dest   = sys.argv[2]
+    print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)
+    """, args=[src, dest], infra_step=True)
+
+  def copy_directory_contents_to_device(self, host_path, device_path):
+    self._copy_dir(host_path, self._prefix_device_path(device_path))
+
+  def copy_directory_contents_to_host(self, device_path, host_path):
+    self._copy_dir(self._prefix_device_path(device_path), host_path)
+
+  def step(self, name, cmd, **kwargs):
+    # Push and run either dm or nanobench
+
+    name = cmd[0]
+    app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
+
+    cmd[0] = '%s/%s' % (self._bin_dir, cmd[0])
+    self.copy_file_to_device(app, cmd[0])
+
+    self._ssh('chmod %s' % name, 'chmod', '+x', cmd[0])
+    self._ssh(str(name), *cmd)
diff --git a/infra/bots/recipe_modules/flavor/gn_chromecast_flavor.py b/infra/bots/recipe_modules/flavor/gn_chromecast_flavor.py
index 2b5983d..ec48a7d 100644
--- a/infra/bots/recipe_modules/flavor/gn_chromecast_flavor.py
+++ b/infra/bots/recipe_modules/flavor/gn_chromecast_flavor.py
@@ -14,18 +14,52 @@
   def __init__(self, m):
     super(GNChromecastFlavorUtils, self).__init__(m)
     self._ever_ran_adb = False
+    self._user_ip = ''
+
+  @property
+  def user_ip_host(self):
+    if not self._user_ip:
+      self._user_ip = self.m.run(self.m.python.inline, 'read chromecast ip',
+                                 program="""
+      import os
+      CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')
+      with open(CHROMECAST_IP_FILE, 'r') as f:
+        print f.read()
+      """,
+      stdout=self.m.raw_io.output(),
+      infra_step=True).stdout
+
+    return self._user_ip
+
+  @property
+  def user_ip(self):
+    return self.user_ip_host.split(':')[0]
 
   def compile(self, unused_target):
     configuration = self.m.vars.builder_cfg.get('configuration')
     os            = self.m.vars.builder_cfg.get('os')
     target_arch   = self.m.vars.builder_cfg.get('target_arch')
 
-    # Makes the binary small enough to fit on the small disk.
-    extra_cflags = ['-g0']
-    # Chromecast does not package libstdc++
-    extra_ldflags = ['-static-libstdc++', '-static-libgcc']
+    # TODO(kjlubick): can this toolchain be replaced/shared with chromebook?
+    toolchain_dir = self.m.vars.slave_dir.join('cast_toolchain', 'armv7a')
+    gles_dir = self.m.vars.slave_dir.join('chromebook_arm_gles')
 
-    toolchain_dir = self.m.vars.slave_dir.join('cast_toolchain')
+    extra_cflags = [
+      '-I%s' % gles_dir.join('include'),
+      '-DMESA_EGL_NO_X11_HEADERS',
+      '-DEGL_NO_IMAGE_EXTERNAL',
+      "-DSK_NO_COMMAND_BUFFER",
+      # Avoid unused warning with yyunput
+      '-Wno-error=unused-function',
+      # Makes the binary small enough to fit on the small disk.
+      '-g0',
+    ]
+
+    extra_ldflags = [
+      # Chromecast does not package libstdc++
+      '-static-libstdc++', '-static-libgcc',
+      '-L%s' % toolchain_dir.join('lib'),
+    ]
 
     quote = lambda x: '"%s"' % x
     args = {
@@ -34,12 +68,13 @@
       'ar': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-ar')),
       'target_cpu': quote(target_arch),
       'skia_use_fontconfig': 'false',
-      'skia_enable_gpu': 'false',
+      'skia_enable_gpu': 'true',
       # The toolchain won't allow system libraries to be used
       # when cross-compiling
       'skia_use_system_freetype2': 'false',
       # Makes the binary smaller
       'skia_use_icu': 'false',
+      'skia_use_egl': 'true',
     }
 
     if configuration != 'Debug':
@@ -69,19 +104,8 @@
     return self._run(title, 'adb', *cmd, **kwargs)
 
   def _connect_to_remote(self):
-
-    ip_address = self.m.run(self.m.python.inline, 'read chromecast ip',
-               program="""
-    import os
-    CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')
-    with open(CHROMECAST_IP_FILE, 'r') as f:
-      print f.read()
-    """,
-    stdout=self.m.raw_io.output(),
-    infra_step=True).stdout
-
-    self.m.run(self.m.step, 'adb connect %s' % ip_address, cmd=['adb',
-      'connect', ip_address], infra_step=True)
+    self.m.run(self.m.step, 'adb connect %s' % self.user_ip_host, cmd=['adb',
+      'connect', self.user_ip_host], infra_step=True)
 
   def create_clean_device_dir(self, path):
     # Note: Chromecast does not support -rf
@@ -113,3 +137,19 @@
           subprocess.check_call(['adb', 'push',
                                 hp, os.path.join(device, p, f)])
     """, args=[host, device], infra_step=True)
+
+  def _ssh(self, title, *cmd, **kwargs):
+    ssh_cmd = ['ssh', '-oConnectTimeout=15', '-oBatchMode=yes',
+               '-t', '-t', 'root@%s' % self.user_ip] + list(cmd)
+
+    return self.m.run(self.m.step, title, cmd=ssh_cmd,
+               infra_step=False, **kwargs)
+
+  def step(self, name, cmd, **kwargs):
+    app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
+    self._adb('push %s' % cmd[0],
+              'push', app, self.m.vars.android_bin_dir)
+
+    cmd[0] = '%s%s' % (self.m.vars.android_bin_dir, cmd[0])
+    self._ssh(str(name), *cmd)
+
diff --git a/infra/bots/recipe_modules/flavor/gn_flavor.py b/infra/bots/recipe_modules/flavor/gn_flavor.py
index 4ee2f8e..0db02dc 100644
--- a/infra/bots/recipe_modules/flavor/gn_flavor.py
+++ b/infra/bots/recipe_modules/flavor/gn_flavor.py
@@ -6,12 +6,12 @@
 
 """GN flavor utils, used for building Skia with GN."""
 class GNFlavorUtils(default_flavor.DefaultFlavorUtils):
-  def _run(self, title, cmd, infra_step=False):
-    self.m.run(self.m.step, title, cmd=cmd,
-               infra_step=infra_step)
+  def _run(self, title, cmd, infra_step=False, **kwargs):
+    return self.m.run(self.m.step, title, cmd=cmd,
+               infra_step=infra_step, **kwargs)
 
   def _py(self, title, script, infra_step=True, args=()):
-    self.m.run(self.m.python, title, script=script, args=args,
+    return self.m.run(self.m.python, title, script=script, args=args,
                infra_step=infra_step)
 
   def build_command_buffer(self):
@@ -44,6 +44,8 @@
     if compiler == 'Clang' and os == 'Ubuntu':
       cc  = clang_linux + '/bin/clang'
       cxx = clang_linux + '/bin/clang++'
+      extra_cflags .append('-B%s/bin' % clang_linux)
+      extra_ldflags.append('-B%s/bin' % clang_linux)
       extra_ldflags.append('-fuse-ld=lld')
     elif compiler == 'Clang':
       cc, cxx = 'clang', 'clang++'
@@ -74,6 +76,7 @@
     if extra_config == 'GDI':
       args['skia_use_gdi'] = 'true'
     if extra_config == 'MSAN':
+      args['skia_enable_gpu']     = 'false'
       args['skia_use_fontconfig'] = 'false'
     if extra_config == 'ASAN':
       args['skia_enable_spirv_validation'] = 'false'
@@ -124,7 +127,7 @@
     ninja = 'ninja.exe' if 'Win' in os else 'ninja'
     gn = self.m.vars.skia_dir.join('bin', gn)
 
-    with self.m.step.context({'cwd': self.m.vars.skia_dir}):
+    with self.m.context(cwd=self.m.vars.skia_dir):
       self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
       self._run('gn gen', [gn, 'gen', self.out_dir, '--args=' + gn_args])
       self._run('ninja', [ninja, '-C', self.out_dir])
@@ -143,7 +146,7 @@
   def step(self, name, cmd):
     app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
     cmd = [app] + cmd[1:]
-    env = self.m.step.get_from_context('env', {})
+    env = self.m.context.env
 
     clang_linux = str(self.m.vars.slave_dir.join('clang_linux'))
     extra_config = self.m.vars.builder_cfg.get('extra_config', '')
@@ -168,13 +171,12 @@
       # Convert path objects or placeholders into strings such that they can
       # be passed to symbolize_stack_trace.py
       args = [self.m.vars.slave_dir] + [str(x) for x in cmd]
-      with self.m.step.context({'cwd': self.m.vars.skia_dir, 'env': env}):
+      with self.m.context(cwd=self.m.vars.skia_dir, env=env):
         self._py('symbolized %s' % name,
-                 self.m.vars.infrabots_dir.join('recipe_modules', 'core',
-                 'resources', 'symbolize_stack_trace.py'),
+                 self.module.resource('symbolize_stack_trace.py'),
                  args=args,
                  infra_step=False)
 
     else:
-      with self.m.step.context({'env': env}):
+      with self.m.context(env=env):
         self._run(name, cmd)
diff --git a/infra/bots/recipe_modules/flavor/ios_flavor.py b/infra/bots/recipe_modules/flavor/ios_flavor.py
index 2d141c1..cc6143a 100644
--- a/infra/bots/recipe_modules/flavor/ios_flavor.py
+++ b/infra/bots/recipe_modules/flavor/ios_flavor.py
@@ -7,13 +7,23 @@
 
 import default_flavor
 import gn_flavor
-
-# Infra step failures interact really annoyingly with swarming retries.
-kInfraStep = False
+import os
 
 class iOSFlavorUtils(gn_flavor.GNFlavorUtils):
 
   def install(self):
+    # Set up the device
+    self.m.run(self.m.step, 'setup_device', cmd=['ios.py'], infra_step=True)
+
+    # Install the app.
+    for app_name in ['dm', 'nanobench']:
+      app_package = self.m.vars.skia_out.join(self.m.vars.configuration,
+                                              '%s.app' % app_name)
+      self.m.run(self.m.step,
+                'install_' + app_name,
+                cmd=['ideviceinstaller', '-i', app_package],
+                infra_step=True)
+
     self.device_dirs = default_flavor.DeviceDirs(
         dm_dir='dm',
         perf_data_dir='perf',
@@ -23,22 +33,29 @@
         svg_dir='svgs',
         tmp_dir='tmp')
 
-  def step(self, name, cmd, env=None, **kwargs):
-    app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
+  def compile(self, unused_target, **kwargs):
+    """ Build Skia with GN and sign the iOS apps"""
+    # Use the generic compile sets.
+    super(iOSFlavorUtils, self).compile(unused_target, **kwargs)
 
-    self._py('package ' + name,
-             self.m.vars.skia_dir.join('gn', 'package_ios.py'),
-             args=[str(app)])
-    self._run(name,
-              ['ios-deploy', '-b', '%s.app' % app,
-               '-I', '--args', ' '.join(map(str, cmd[1:]))])
+    # Sign the apps.
+    for app in ['dm', 'nanobench']:
+      self._py('package ' + app,
+              self.m.vars.skia_dir.join('gn', 'package_ios.py'),
+              args=[self.out_dir.join(app)], infra_step=True)
+
+  def step(self, name, cmd, env=None, **kwargs):
+    bundle_id = 'com.google.%s' % cmd[0]
+    self.m.run(self.m.step, name,
+               cmd=['idevice-app-runner', '-s', bundle_id, '--args'] +
+                    map(str, cmd[1:]))
 
   def _run_ios_script(self, script, first, *rest):
     full = self.m.vars.skia_dir.join('platform_tools/ios/bin/ios_' + script)
     self.m.run(self.m.step,
                name = '%s %s' % (script, first),
                cmd = [full, first] + list(rest),
-               infra_step=kInfraStep)
+               infra_step=True)
 
   def copy_file_to_device(self, host, device):
     self._run_ios_script('push_file', host, device)
@@ -49,18 +66,19 @@
   def copy_directory_contents_to_host(self, device, host):
     self._run_ios_script('pull_if_needed', device, host)
 
-  def remove_file_on_device(self, path):  # pragma: nocover
+  def remove_file_on_device(self, path):
     self._run_ios_script('rm', path)
 
   def create_clean_device_dir(self, path):
     self._run_ios_script('rm',    path)
     self._run_ios_script('mkdir', path)
 
-  def read_file_on_device(self, path):  # pragma: nocover
+  def read_file_on_device(self, path, **kwargs):
     full = self.m.vars.skia_dir.join('platform_tools/ios/bin/ios_cat_file')
-    rc = self.m.run(self.m.step,
+    rv = self.m.run(self.m.step,
                     name = 'cat_file %s' % path,
                     cmd = [full, path],
                     stdout=self.m.raw_io.output(),
-                    infra_step=kInfraStep)
-    return rc.stdout.rstrip() if rc.stdout else rc.stdout
+                    infra_step=True,
+                    **kwargs)
+    return rv.stdout.rstrip() if rv and rv.stdout else None
diff --git a/infra/bots/recipe_modules/flavor/pdfium_flavor.py b/infra/bots/recipe_modules/flavor/pdfium_flavor.py
index 84645f2..6a0018e 100644
--- a/infra/bots/recipe_modules/flavor/pdfium_flavor.py
+++ b/infra/bots/recipe_modules/flavor/pdfium_flavor.py
@@ -17,7 +17,7 @@
     pdfium_dir = self.m.vars.checkout_root.join('pdfium')
 
     # Runhook to generate the gn binary in buildtools.
-    with self.m.step.context({'cwd': pdfium_dir}):
+    with self.m.context(cwd=pdfium_dir):
       self.m.run(
           self.m.step,
           'runhook',
@@ -43,9 +43,9 @@
         gn_args.append('pdf_use_skia=true')
 
 
-      env = self.m.step.get_from_context('env', {})
+      env = self.m.context.env
       env['CHROMIUM_BUILDTOOLS_PATH'] = str(pdfium_dir.join('buildtools'))
-      with self.m.step.context({'env': env}):
+      with self.m.context(env=env):
         self.m.run(
             self.m.step,
             'gn_gen',
diff --git a/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py b/infra/bots/recipe_modules/flavor/resources/symbolize_stack_trace.py
similarity index 100%
rename from infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py
rename to infra/bots/recipe_modules/flavor/resources/symbolize_stack_trace.py
diff --git a/infra/bots/recipe_modules/flavor/valgrind_flavor.py b/infra/bots/recipe_modules/flavor/valgrind_flavor.py
index 0ebc161..79235b0 100644
--- a/infra/bots/recipe_modules/flavor/valgrind_flavor.py
+++ b/infra/bots/recipe_modules/flavor/valgrind_flavor.py
@@ -14,14 +14,17 @@
     super(ValgrindFlavorUtils, self).__init__(m)
     self._suppressions_file = self.m.vars.skia_dir.join(
         'tools', 'valgrind.supp')
+    self._valgrind_cipd_dir = self.m.vars.slave_dir.join('valgrind')
+    self._valgrind_fake_dir = self._valgrind_cipd_dir
+    self._valgrind = self._valgrind_fake_dir.join('bin', 'valgrind')
+    self._lib_dir = self._valgrind_fake_dir.join('lib', 'valgrind')
 
   def step(self, name, cmd, **kwargs):
-    new_cmd = ['valgrind', '--gen-suppressions=all', '--leak-check=full',
+    new_cmd = [self._valgrind, '--gen-suppressions=all', '--leak-check=full',
                '--track-origins=yes', '--error-exitcode=1', '--num-callers=40',
                '--suppressions=%s' % self._suppressions_file]
     path_to_app = self.out_dir.join(cmd[0])
     new_cmd.append(path_to_app)
     new_cmd.extend(cmd[1:])
-    return self.m.run(self.m.step, name, cmd=new_cmd,
-                            **kwargs)
-
+    with self.m.env({'VALGRIND_LIB': self._lib_dir}):
+      return self.m.run(self.m.step, name, cmd=new_cmd, **kwargs)
diff --git a/infra/bots/recipe_modules/git/__init__.py b/infra/bots/recipe_modules/git/__init__.py
new file mode 100644
index 0000000..abc3791
--- /dev/null
+++ b/infra/bots/recipe_modules/git/__init__.py
@@ -0,0 +1,10 @@
+# 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 = [
+  'env',
+  'recipe_engine/path',
+]
+
diff --git a/infra/bots/recipe_modules/git/api.py b/infra/bots/recipe_modules/git/api.py
new file mode 100644
index 0000000..629937d
--- /dev/null
+++ b/infra/bots/recipe_modules/git/api.py
@@ -0,0 +1,18 @@
+# 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.
+
+
+from recipe_engine import recipe_api
+
+
+class GitApi(recipe_api.RecipeApi):
+  def env(self):
+    """Add Git to PATH
+
+    Requires the infra/git and infra/tools/git CIPD packages to be installed
+    in the 'git' relative path.
+    """
+    git_dir = self.m.path['start_dir'].join('git')
+    git_bin = git_dir.join('bin')
+    return self.m.env({'PATH': '%s:%s:%%(PATH)s' % (git_dir, git_bin)})
diff --git a/infra/bots/recipe_modules/git/examples/full.expected/test.json b/infra/bots/recipe_modules/git/examples/full.expected/test.json
new file mode 100644
index 0000000..2f5fa0e
--- /dev/null
+++ b/infra/bots/recipe_modules/git/examples/full.expected/test.json
@@ -0,0 +1,24 @@
+[
+  {
+    "cmd": [
+      "git",
+      "status"
+    ],
+    "name": "1"
+  },
+  {
+    "cmd": [
+      "git",
+      "status"
+    ],
+    "env": {
+      "PATH": "[START_DIR]/git:[START_DIR]/git/bin:<PATH>"
+    },
+    "name": "2"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/git/examples/full.py b/infra/bots/recipe_modules/git/examples/full.py
new file mode 100644
index 0000000..a24d903
--- /dev/null
+++ b/infra/bots/recipe_modules/git/examples/full.py
@@ -0,0 +1,19 @@
+# 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 = [
+  'git',
+  'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  api.step('1', cmd=['git', 'status'])
+  with api.git.env():
+    api.step('2', cmd=['git', 'status'])
+
+
+def GenTests(api):
+  yield api.test('test')
diff --git a/infra/bots/recipe_modules/infra/__init__.py b/infra/bots/recipe_modules/infra/__init__.py
index ba20b52..94aa82a 100644
--- a/infra/bots/recipe_modules/infra/__init__.py
+++ b/infra/bots/recipe_modules/infra/__init__.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 DEPS = [
+  'recipe_engine/context',
   'recipe_engine/step',
   'run',
   'vars',
diff --git a/infra/bots/recipe_modules/infra/api.py b/infra/bots/recipe_modules/infra/api.py
index 9dd7268..932541f 100644
--- a/infra/bots/recipe_modules/infra/api.py
+++ b/infra/bots/recipe_modules/infra/api.py
@@ -12,23 +12,99 @@
 
 class InfraApi(recipe_api.RecipeApi):
   @property
+  def goroot(self):
+    return self.m.vars.slave_dir.join('go', 'go')
+
+  @property
+  def go_bin(self):
+    return self.goroot.join('bin')
+
+  @property
+  def go_exe(self):
+    return self.go_bin.join('go')
+
+  @property
   def go_env(self):
-    return {'GOPATH': self.gopath}
+    return {
+        'GOPATH': self.gopath,
+        'GOROOT': self.goroot,
+        'PATH': '%s:%s:%%(PATH)s' % (self.go_bin, self.gopath),
+    }
 
   @property
   def gopath(self):
-    return self.m.vars.checkout_root.join('gopath')
+    return self.m.vars.slave_dir.join('gopath')
+
+  def go_version(self):
+    """Print the Go version."""
+    env = self.m.context.env
+    env.update(self.go_env)
+    with self.m.context(env=env):
+      self.m.run(
+          self.m.step,
+          'go version',
+          cmd=[self.go_exe, 'version'])
+      self.m.run(
+          self.m.step,
+          'env go version',
+          cmd=['go', 'version'])
 
   def update_go_deps(self):
     """Attempt to update go dependencies.
 
     This fails flakily sometimes, so perform multiple attempts.
     """
-    env = self.m.step.get_from_context('env', {})
+    self.go_version()
+    env = self.m.context.env
     env.update(self.go_env)
-    with self.m.step.context({'env': env}):
+    with self.m.context(env=env):
       self.m.run.with_retry(
           self.m.step,
           'update go pkgs',
           UPDATE_GO_ATTEMPTS,
-          cmd=['go', 'get', '-u', '-t', '%s/...' % INFRA_GO_PKG])
+          cmd=[self.go_exe, 'get', '-u', '-t', '%s/...' % INFRA_GO_PKG])
+
+  class MetadataFetch():
+    def __init__(self, api, metadata_key, local_file, **kwargs):
+      self.m = api
+      self._key = metadata_key
+      self._local_file = local_file
+
+    def __enter__(self):
+      return self.m.python.inline(
+          'download ' + self._local_file,
+        """
+import os
+import urllib2
+
+TOKEN_FILE = '%s'
+TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/%s'
+
+req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})
+contents = urllib2.urlopen(req).read()
+
+home = os.path.expanduser('~')
+token_file = os.path.join(home, TOKEN_FILE)
+
+with open(token_file, 'w') as f:
+  f.write(contents)
+        """ % (self._local_file, self._key),
+      )
+
+    def __exit__(self, t, v, tb):
+      self.m.python.inline(
+          'cleanup ' + self._local_file,
+        """
+import os
+
+
+TOKEN_FILE = '%s'
+
+
+home = os.path.expanduser('~')
+token_file = os.path.join(home, TOKEN_FILE)
+if os.path.isfile(token_file):
+  os.remove(token_file)
+          """ % (self._local_file),
+      )
+      return v is None
diff --git a/infra/bots/recipe_modules/infra/example.expected/failed_all_updates.json b/infra/bots/recipe_modules/infra/example.expected/failed_all_updates.json
deleted file mode 100644
index a5ba6b1..0000000
--- a/infra/bots/recipe_modules/infra/example.expected/failed_all_updates.json
+++ /dev/null
@@ -1,190 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 2)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 3)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 4)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 5)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "reason": "Step('update go pkgs (attempt 5)') failed with return_code 1",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/infra/example.expected/failed_one_update.json b/infra/bots/recipe_modules/infra/example.expected/failed_one_update.json
deleted file mode 100644
index 68f0e41..0000000
--- a/infra/bots/recipe_modules/infra/example.expected/failed_one_update.json
+++ /dev/null
@@ -1,133 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/infra_tests.py"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath"
-    },
-    "name": "infra_tests"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/infra/example.expected/infra_tests.json b/infra/bots/recipe_modules/infra/example.expected/infra_tests.json
deleted file mode 100644
index 1576173..0000000
--- a/infra/bots/recipe_modules/infra/example.expected/infra_tests.json
+++ /dev/null
@@ -1,112 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/infra_tests.py"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath"
-    },
-    "name": "infra_tests"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/infra/example.py b/infra/bots/recipe_modules/infra/example.py
deleted file mode 100644
index f243c1b..0000000
--- a/infra/bots/recipe_modules/infra/example.py
+++ /dev/null
@@ -1,73 +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 which runs the Skia infra tests.
-
-
-DEPS = [
-  'core',
-  'infra',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/step',
-  'run',
-  'vars',
-]
-
-
-def RunSteps(api):
-  api.vars.setup()
-  api.core.checkout_steps()
-  api.infra.update_go_deps()
-
-  # Run the infra tests.
-  infra_tests = api.vars.skia_dir.join(
-      'infra', 'bots', 'infra_tests.py')
-  with api.step.context({'cwd': api.vars.skia_dir, 'env': api.infra.go_env}):
-    api.step('infra_tests', cmd=['python', infra_tests])
-
-
-def GenTests(api):
-  yield (
-      api.test('infra_tests') +
-      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]')
-  )
-
-  yield (
-    api.test('failed_one_update') +
-      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-    api.step_data('update go pkgs', retcode=1)
-  )
-
-  yield (
-    api.test('failed_all_updates') +
-      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-    api.step_data('update go pkgs', retcode=1) +
-    api.step_data('update go pkgs (attempt 2)', retcode=1) +
-    api.step_data('update go pkgs (attempt 3)', retcode=1) +
-    api.step_data('update go pkgs (attempt 4)', retcode=1) +
-    api.step_data('update go pkgs (attempt 5)', retcode=1)
-  )
diff --git a/infra/bots/recipe_modules/infra/examples/full.expected/failed_all_updates.json b/infra/bots/recipe_modules/infra/examples/full.expected/failed_all_updates.json
new file mode 100644
index 0000000..716adcb
--- /dev/null
+++ b/infra/bots/recipe_modules/infra/examples/full.expected/failed_all_updates.json
@@ -0,0 +1,148 @@
+[
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 2)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 3)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 4)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 5)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Step('update go pkgs (attempt 5)') failed with return_code 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/infra/examples/full.expected/failed_one_update.json b/infra/bots/recipe_modules/infra/examples/full.expected/failed_one_update.json
new file mode 100644
index 0000000..a154c9d
--- /dev/null
+++ b/infra/bots/recipe_modules/infra/examples/full.expected/failed_one_update.json
@@ -0,0 +1,125 @@
+[
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'file'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/key'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
+    ],
+    "name": "download file",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'file'@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/key'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
+      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\n\n\nTOKEN_FILE = 'file'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
+    ],
+    "name": "cleanup file",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'file'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/infra/examples/full.expected/infra_tests.json b/infra/bots/recipe_modules/infra/examples/full.expected/infra_tests.json
new file mode 100644
index 0000000..0437346
--- /dev/null
+++ b/infra/bots/recipe_modules/infra/examples/full.expected/infra_tests.json
@@ -0,0 +1,103 @@
+[
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'file'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/key'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
+    ],
+    "name": "download file",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'file'@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/key'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
+      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\n\n\nTOKEN_FILE = 'file'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
+    ],
+    "name": "cleanup file",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'file'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/infra/examples/full.py b/infra/bots/recipe_modules/infra/examples/full.py
new file mode 100644
index 0000000..af1c9f2
--- /dev/null
+++ b/infra/bots/recipe_modules/infra/examples/full.py
@@ -0,0 +1,60 @@
+# 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 which runs the Skia infra tests.
+
+
+DEPS = [
+  'core',
+  'infra',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/step',
+  'run',
+  'vars',
+]
+
+
+def RunSteps(api):
+  api.vars.setup()
+  api.infra.update_go_deps()
+  with api.infra.MetadataFetch(api, 'key', 'file'):
+    pass
+
+
+def GenTests(api):
+  yield (
+      api.test('infra_tests') +
+      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]')
+  )
+
+  yield (
+    api.test('failed_one_update') +
+      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+    api.step_data('update go pkgs', retcode=1)
+  )
+
+  yield (
+    api.test('failed_all_updates') +
+      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+    api.step_data('update go pkgs', retcode=1) +
+    api.step_data('update go pkgs (attempt 2)', retcode=1) +
+    api.step_data('update go pkgs (attempt 3)', retcode=1) +
+    api.step_data('update go pkgs (attempt 4)', retcode=1) +
+    api.step_data('update go pkgs (attempt 5)', retcode=1)
+  )
diff --git a/infra/bots/recipe_modules/isolate/OWNERS b/infra/bots/recipe_modules/isolate/OWNERS
new file mode 100644
index 0000000..a24c7a5
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/OWNERS
@@ -0,0 +1,3 @@
+dpranke@chromium.org
+mcgreevy@chromium.org
+tansell@chromium.org
diff --git a/infra/bots/recipe_modules/isolate/__init__.py b/infra/bots/recipe_modules/isolate/__init__.py
new file mode 100644
index 0000000..0da966d
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/__init__.py
@@ -0,0 +1,23 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+DEPS = [
+  'file',
+  'depot_tools/git',
+  'depot_tools/gsutil',
+  'recipe_engine/context',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/step',
+  'recipe_engine/tempfile',
+  'swarming_client',
+]
diff --git a/infra/bots/recipe_modules/isolate/api.py b/infra/bots/recipe_modules/isolate/api.py
new file mode 100644
index 0000000..09aeb99
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/api.py
@@ -0,0 +1,253 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+import itertools
+from recipe_engine import recipe_api
+
+
+class IsolateApi(recipe_api.RecipeApi):
+  """APIs for interacting with isolates."""
+
+  def __init__(self, **kwargs):
+    super(IsolateApi, self).__init__(**kwargs)
+    self._isolate_server = 'https://isolateserver.appspot.com'
+    self._isolated_tests = {}
+
+  @property
+  def isolate_server(self):
+    """URL of Isolate server to use, default is a production one."""
+    return self._isolate_server
+
+  @isolate_server.setter
+  def isolate_server(self, value):
+    """Changes URL of Isolate server to use."""
+    self._isolate_server = value
+
+  def clean_isolated_files(self, build_dir):
+    """Cleans out all *.isolated files from the build directory in
+    preparation for the compile. Needed in order to ensure isolates
+    are rebuilt properly because their dependencies are currently not
+    completely described to gyp.
+    """
+    self.m.python(
+      'clean isolated files',
+      self.resource('find_isolated_tests.py'),
+      [
+        '--build-dir', build_dir,
+        '--clean-isolated-files'
+      ])
+
+  def find_isolated_tests(self, build_dir, targets=None, **kwargs):
+    """Returns a step which finds all *.isolated files in a build directory.
+
+    Useful only with 'archive' isolation mode.
+    In 'prepare' mode use 'isolate_tests' instead.
+
+    Assigns the dict {target name -> *.isolated file hash} to the swarm_hashes
+    build property. This implies this step can currently only be run once
+    per recipe.
+
+    If |targets| is None, the step will use all *.isolated files it finds.
+    Otherwise, it will verify that all |targets| are found and will use only
+    them. If some expected targets are missing, will abort the build.
+    """
+    step_result = self.m.python(
+      'find isolated tests',
+      self.resource('find_isolated_tests.py'),
+      [
+        '--build-dir', build_dir,
+        '--output-json', self.m.json.output(),
+      ],
+      step_test_data=lambda: (self.test_api.output_json(targets)),
+      **kwargs)
+
+    assert isinstance(step_result.json.output, dict)
+    self._isolated_tests = step_result.json.output
+    if targets is not None and (
+            step_result.presentation.status != self.m.step.FAILURE):
+      found = set(step_result.json.output)
+      expected = set(targets)
+      if found >= expected:  # pragma: no cover
+        # Limit result only to |expected|.
+        self._isolated_tests = {
+          target: step_result.json.output[target] for target in expected
+        }
+      else:
+        # Some expected targets are missing? Fail the step.
+        step_result.presentation.status = self.m.step.FAILURE
+        step_result.presentation.logs['missing.isolates'] = (
+            ['Failed to find *.isolated files:'] + list(expected - found))
+    step_result.presentation.properties['swarm_hashes'] = self._isolated_tests
+    # No isolated files found? That looks suspicious, emit warning.
+    if (not self._isolated_tests and
+        step_result.presentation.status != self.m.step.FAILURE):
+      step_result.presentation.status = self.m.step.WARNING
+
+  def isolate_tests(self, build_dir, targets=None, verbose=False,
+                    set_swarm_hashes=True, always_use_exparchive=False,
+                    **kwargs):
+    """Archives prepared tests in |build_dir| to isolate server.
+
+    src/tools/isolate_driver.py is invoked by ninja during compilation
+    to produce *.isolated.gen.json files that describe how to archive tests.
+
+    This step then uses *.isolated.gen.json files to actually performs the
+    archival. By archiving all tests at once it is able to reduce the total
+    amount of work. Tests share many common files, and such files are processed
+    only once.
+
+    Assigns the dict {target name -> *.isolated file hash} to the swarm_hashes
+    build property (also accessible as 'isolated_tests' property). This implies
+    this step can currently only be run once per recipe.
+    """
+    # TODO(tansell): Make all steps in this function nested under one overall
+    # 'isolate tests' master step.
+
+    # TODO(vadimsh): Always require |targets| to be passed explicitly. Currently
+    # chromium_trybot, blink_trybot and swarming/canary recipes rely on targets
+    # autodiscovery. The code path in chromium_trybot that needs it is being
+    # deprecated in favor of to *_ng builders, that pass targets explicitly.
+    if targets is None:
+      # Ninja builds <target>.isolated.gen.json files via isolate_driver.py.
+      paths = self.m.file.glob(
+          'find isolated targets',
+          build_dir.join('*.isolated.gen.json'),
+          test_data=[
+              build_dir.join('dummy_target_%d.isolated.gen.json' % i)
+              for i in (1, 2)
+          ])
+      targets = []
+      for p in paths:
+        name = self.m.path.basename(p)
+        assert name.endswith('.isolated.gen.json'), name
+        targets.append(name[:-len('.isolated.gen.json')])
+
+    # No isolated tests found.
+    if not targets:  # pragma: no cover
+      return
+
+    batch_targets = []
+    exparchive_targets = []
+    for t in targets:
+      if t.endswith('_exparchive') or always_use_exparchive:
+        exparchive_targets.append(t)
+      else:
+        batch_targets.append(t)
+
+    isolate_steps = []
+    try:
+      args = [
+          self.m.swarming_client.path,
+          'exparchive',
+          '--dump-json', self.m.json.output(),
+          '--isolate-server', self._isolate_server,
+          '--eventlog-endpoint', 'prod',
+      ] + (['--verbose'] if verbose else [])
+
+      for target in exparchive_targets:
+        isolate_steps.append(
+            self.m.python(
+                'isolate %s' % target,
+                self.resource('isolate.py'),
+                args + [
+                    '--isolate', build_dir.join('%s.isolate' % target),
+                    '--isolated', build_dir.join('%s.isolated' % target),
+                ],
+                step_test_data=lambda: self.test_api.output_json([target]),
+                **kwargs))
+
+      if batch_targets:
+        # TODO(vadimsh): Differentiate between bad *.isolate and upload errors.
+        # Raise InfraFailure on upload errors.
+        args = [
+            self.m.swarming_client.path,
+            'batcharchive',
+            '--dump-json', self.m.json.output(),
+            '--isolate-server', self._isolate_server,
+        ] + (['--verbose'] if verbose else []) +  [
+            build_dir.join('%s.isolated.gen.json' % t) for t in batch_targets
+        ]
+        isolate_steps.append(
+            self.m.python(
+                'isolate tests', self.resource('isolate.py'), args,
+                step_test_data=lambda: self.test_api.output_json(batch_targets),
+                **kwargs))
+
+      # TODO(tansell): Change this to return a dummy "isolate results" or the
+      # top level master step.
+      return isolate_steps[-1]
+    finally:
+      step_result = self.m.step.active_result
+      swarm_hashes = {}
+      for step in isolate_steps:
+        if not step.json.output:
+          continue  # pragma: no cover
+
+        for k, v in step.json.output.iteritems():
+          # TODO(tansell): Raise an error here when it can't clobber an
+          # existing error. This code is currently inside a finally block,
+          # meaning it could be executed when an existing error is occurring.
+          # See https://chromium-review.googlesource.com/c/437024/
+          #assert k not in swarm_hashes or swarm_hashes[k] == v, (
+          #    "Duplicate hash for target %s was found at step %s."
+          #    "Existing hash: %s, New hash: %s") % (
+          #        k, step, swarm_hashes[k], v)
+          swarm_hashes[k] = v
+
+      if swarm_hashes:
+        self._isolated_tests = swarm_hashes
+
+      if set_swarm_hashes:
+        step_result.presentation.properties['swarm_hashes'] = swarm_hashes
+
+      missing = sorted(
+          t for t, h in self._isolated_tests.iteritems() if not h)
+      if missing:
+        step_result.presentation.logs['failed to isolate'] = (
+            ['Failed to isolate following targets:'] +
+            missing +
+            ['', 'See logs for more information.']
+        )
+        for k in missing:
+          self._isolated_tests.pop(k)
+
+  @property
+  def isolated_tests(self):
+    """The dictionary of 'target name -> isolated hash' for this run.
+
+    These come either from the incoming swarm_hashes build property,
+    or from calling find_isolated_tests, above, at some point during the run.
+    """
+    hashes = self.m.properties.get('swarm_hashes', self._isolated_tests)
+    # Be robust in the case where swarm_hashes is an empty string
+    # instead of an empty dictionary, or similar.
+    if not hashes:
+      return {} # pragma: no covergae
+    return {
+      k.encode('ascii'): v.encode('ascii')
+      for k, v in hashes.iteritems()
+    }
+
+  @property
+  def _run_isolated_path(self):
+    """Returns the path to run_isolated.py."""
+    return self.m.swarming_client.path.join('run_isolated.py')
+
+  def run_isolated(self, name, isolate_hash, args=None, **kwargs):
+    """Runs an isolated test."""
+    cmd = [
+        '--isolated', isolate_hash,
+        '-I', self.isolate_server,
+        '--verbose',
+    ]
+    if args:
+      cmd.append('--')
+      cmd.extend(args)
+    self.m.python(name, self._run_isolated_path, cmd, **kwargs)
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/always-use-exparchive.json b/infra/bots/recipe_modules/isolate/examples/full.expected/always-use-exparchive.json
new file mode 100644
index 0000000..1f5ffa1
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/always-use-exparchive.json
@@ -0,0 +1,208 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\", \"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolated"
+    ],
+    "name": "isolate test_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated"
+    ],
+    "name": "isolate test1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated"
+    ],
+    "name": "isolate test2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\", \"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/basic.json b/infra/bots/recipe_modules/isolate/examples/full.expected/basic.json
new file mode 100644
index 0000000..9ef2ef3
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/basic.json
@@ -0,0 +1,151 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated.gen.json",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/discover.json b/infra/bots/recipe_modules/isolate/examples/full.expected/discover.json
new file mode 100644
index 0000000..d6ab964
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/discover.json
@@ -0,0 +1,124 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@null@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"dummy_target_1\": \"[dummy hash for dummy_target_1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"dummy_target_2\": \"[dummy hash for dummy_target_2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"dummy_target_1\": \"[dummy hash for dummy_target_1]\", \"dummy_target_2\": \"[dummy hash for dummy_target_2]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch-bmiss.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch-bmiss.json
new file mode 100644
index 0000000..75d1af4
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch-bmiss.json
@@ -0,0 +1,187 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@Failed to find *.isolated files:@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@test2@@@",
+      "@@@STEP_LOG_END@missing.isolates@@@",
+      "@@@STEP_FAILURE@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolated"
+    ],
+    "name": "isolate test_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated.gen.json",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": null@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@Failed to isolate following targets:@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@test2@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@See logs for more information.@@@",
+      "@@@STEP_LOG_END@failed to isolate@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch-emiss.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch-emiss.json
new file mode 100644
index 0000000..142ebd5
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch-emiss.json
@@ -0,0 +1,187 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@Failed to find *.isolated files:@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@test_exparchive@@@",
+      "@@@STEP_LOG_END@missing.isolates@@@",
+      "@@@STEP_FAILURE@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolated"
+    ],
+    "name": "isolate test_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": null@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated.gen.json",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@Failed to isolate following targets:@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@test_exparchive@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@See logs for more information.@@@",
+      "@@@STEP_LOG_END@failed to isolate@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch.json
new file mode 100644
index 0000000..e2d26d6
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-batch.json
@@ -0,0 +1,179 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\", \"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolated"
+    ],
+    "name": "isolate test_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated.gen.json",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\", \"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-miss.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-miss.json
new file mode 100644
index 0000000..06eceaa
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-miss.json
@@ -0,0 +1,159 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@Failed to find *.isolated files:@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@test_exparchive@@@",
+      "@@@STEP_LOG_END@missing.isolates@@@",
+      "@@@STEP_FAILURE@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolated"
+    ],
+    "name": "isolate test_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": null@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@Failed to isolate following targets:@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@test_exparchive@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@See logs for more information.@@@",
+      "@@@STEP_LOG_END@failed to isolate@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-multi-miss.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-multi-miss.json
new file mode 100644
index 0000000..02131c3
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-multi-miss.json
@@ -0,0 +1,188 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1_exparchive\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1_exparchive\": \"[dummy hash for test1_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@Failed to find *.isolated files:@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@test2_exparchive@@@",
+      "@@@STEP_LOG_END@missing.isolates@@@",
+      "@@@STEP_FAILURE@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1_exparchive\": \"[dummy hash for test1_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test1_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test1_exparchive.isolated"
+    ],
+    "name": "isolate test1_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1_exparchive\": \"[dummy hash for test1_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test2_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test2_exparchive.isolated"
+    ],
+    "name": "isolate test2_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2_exparchive\": null@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@Failed to isolate following targets:@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@test2_exparchive@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@See logs for more information.@@@",
+      "@@@STEP_LOG_END@failed to isolate@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1_exparchive\": \"[dummy hash for test1_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-multi.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-multi.json
new file mode 100644
index 0000000..871423d
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive-multi.json
@@ -0,0 +1,180 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1_exparchive\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1_exparchive\": \"[dummy hash for test1_exparchive]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2_exparchive\": \"[dummy hash for test2_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1_exparchive\": \"[dummy hash for test1_exparchive]\", \"test2_exparchive\": \"[dummy hash for test2_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test1_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test1_exparchive.isolated"
+    ],
+    "name": "isolate test1_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1_exparchive\": \"[dummy hash for test1_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test2_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test2_exparchive.isolated"
+    ],
+    "name": "isolate test2_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2_exparchive\": \"[dummy hash for test2_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1_exparchive\": \"[dummy hash for test1_exparchive]\", \"test2_exparchive\": \"[dummy hash for test2_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive.json b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive.json
new file mode 100644
index 0000000..33fadc1
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/exparchive.json
@@ -0,0 +1,152 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "exparchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--eventlog-endpoint",
+      "prod",
+      "--isolate",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolate",
+      "--isolated",
+      "RECIPE_PACKAGE_REPO[skia]/test_exparchive.isolated"
+    ],
+    "name": "isolate test_exparchive",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test_exparchive\": \"[dummy hash for test_exparchive]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test_exparchive\": \"[dummy hash for test_exparchive]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/extra.json b/infra/bots/recipe_modules/isolate/examples/full.expected/extra.json
new file mode 100644
index 0000000..3f1987d
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/extra.json
@@ -0,0 +1,152 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"extra_test\": \"[dummy hash for extra_test]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated.gen.json",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": \"[dummy hash for test2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\", \"test2\": \"[dummy hash for test2]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/missing.json b/infra/bots/recipe_modules/isolate/examples/full.expected/missing.json
new file mode 100644
index 0000000..90e1e95
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/missing.json
@@ -0,0 +1,159 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@Failed to find *.isolated files:@@@",
+      "@@@STEP_LOG_LINE@missing.isolates@test2@@@",
+      "@@@STEP_LOG_END@missing.isolates@@@",
+      "@@@STEP_FAILURE@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "RECIPE_PACKAGE_REPO[skia]/test1.isolated.gen.json",
+      "RECIPE_PACKAGE_REPO[skia]/test2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test1\": \"[dummy hash for test1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"test2\": null@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@Failed to isolate following targets:@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@test2@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@@@@",
+      "@@@STEP_LOG_LINE@failed to isolate@See logs for more information.@@@",
+      "@@@STEP_LOG_END@failed to isolate@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"test1\": \"[dummy hash for test1]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.expected/none.json b/infra/bots/recipe_modules/isolate/examples/full.expected/none.json
new file mode 100644
index 0000000..335b622
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.expected/none.json
@@ -0,0 +1,122 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "cat"
+    ],
+    "name": "read test spec",
+    "stdout": "/path/to/tmp/json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@null@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "RECIPE_PACKAGE_REPO[skia]",
+      "--output-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "find isolated tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_WARNINGS@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/examples/full.py b/infra/bots/recipe_modules/isolate/examples/full.py
new file mode 100644
index 0000000..7dfa009
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/examples/full.py
@@ -0,0 +1,147 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+from recipe_engine.recipe_api import Property
+
+DEPS = [
+  'isolate',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+  'swarming_client',
+]
+
+PROPERTIES = {
+  'always_use_exparchive': Property(
+    kind=bool, help="Force usage of exparchive.", default=False),
+}
+
+
+def RunSteps(api, always_use_exparchive):
+  # 'isolate_tests' step needs swarming checkout.
+  api.swarming_client.checkout('master')
+
+  # Code coverage for isolate_server property.
+  api.isolate.isolate_server = 'https://isolateserver-dev.appspot.com'
+  assert api.isolate.isolate_server == 'https://isolateserver-dev.appspot.com'
+
+  # That would read a list of files to search for, generated in GenTests.
+  step_result = api.step('read test spec', ['cat'], stdout=api.json.output())
+  expected_targets = step_result.stdout
+
+  build_path = api.isolate.package_repo_resource()
+  # Generates code coverage for find_isolated_tests corner cases.
+  # TODO(vadimsh): This step doesn't actually make any sense when the recipe
+  # is running for real via run_recipe.py.
+  api.isolate.find_isolated_tests(build_path, expected_targets)
+
+  # Code coverage for 'isolate_tests'. 'isolated_test' doesn't support discovery
+  # of isolated targets in build directory, so skip if 'expected_targets' is
+  # None.
+  if expected_targets is not None:
+    api.isolate.isolate_tests(
+        build_path, expected_targets,
+        always_use_exparchive=always_use_exparchive)
+
+
+def GenTests(api):
+  def make_test(
+          name,
+          expected_batcharchive_targets,
+          expected_exparchive_targets,
+          discovered_targets):
+
+    if expected_batcharchive_targets or expected_exparchive_targets:
+      all_expected_targets = (
+          (expected_batcharchive_targets or []) +
+          (expected_exparchive_targets or []))
+    else:
+      all_expected_targets = None
+
+    missing = set(all_expected_targets or []) - set(discovered_targets or [])
+    output = (
+        api.test(name) +
+        api.step_data(
+            'read test spec',
+            stdout=api.json.output(all_expected_targets)) +
+        api.override_step_data(
+            'find isolated tests',
+            api.isolate.output_json(discovered_targets))
+    )
+
+    # See comment around 'if expected_targets is not None' above.
+    if all_expected_targets:
+      for target in sorted(expected_exparchive_targets):
+        output += api.override_step_data(
+            'isolate %s' % target,
+            api.isolate.output_json([target], missing))
+
+      if expected_batcharchive_targets:
+        output += api.override_step_data(
+            'isolate tests',
+            api.isolate.output_json(expected_batcharchive_targets, missing))
+
+    return output
+
+  # Expected targets == found targets.
+  yield make_test(
+      'basic', ['test1', 'test2'], [], ['test1', 'test2'])
+  # No expectations, just discovering what's there returned by default mock.
+  yield make_test(
+      'discover', None, None, None)
+  # Found more than expected.
+  yield make_test(
+      'extra', ['test1', 'test2'], [], ['test1', 'test2', 'extra_test'])
+  # Didn't find something.
+  yield (
+      make_test('missing', ['test1', 'test2'], [], ['test1']) +
+      api.properties.generic(buildername='Windows Swarm Test'))
+  # No expectations, and nothing has been found, produces warning.
+  yield make_test('none', None, None, [])
+  # Test the `exparchive` cases
+  # Only exparchive
+  yield make_test(
+      'exparchive', [], ['test_exparchive'], ['test_exparchive'])
+  yield make_test(
+      'exparchive-miss', [], ['test_exparchive'], [])
+  yield make_test(
+      'exparchive-multi',
+      [],
+      ['test1_exparchive', 'test2_exparchive'],
+      ['test1_exparchive', 'test2_exparchive'])
+  yield make_test(
+      'exparchive-multi-miss',
+      [],
+      ['test1_exparchive', 'test2_exparchive'],
+      ['test1_exparchive'])
+  # Mixed
+  yield make_test(
+      'exparchive-batch',
+      ['test1', 'test2'],
+      ['test_exparchive'],
+      ['test1', 'test2', 'test_exparchive'])
+  yield make_test(
+      'exparchive-batch-bmiss',
+      ['test1', 'test2'],
+      ['test_exparchive'],
+      ['test1', 'test_exparchive'])
+  yield make_test(
+      'exparchive-batch-emiss',
+      ['test1', 'test2'],
+      ['test_exparchive'],
+      ['test1', 'test2'])
+  # Use force-exparchive
+  yield make_test(
+      'always-use-exparchive',
+      [],
+      ['test_exparchive', 'test1', 'test2'],
+      ['test_exparchive', 'test1', 'test2']) + api.properties(
+          always_use_exparchive=True)
diff --git a/infra/bots/recipe_modules/isolate/resources/find_isolated_tests.py b/infra/bots/recipe_modules/isolate/resources/find_isolated_tests.py
new file mode 100755
index 0000000..9214f29
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/resources/find_isolated_tests.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# 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.
+
+"""Scans build output directory for .isolated files, calculates their SHA1
+hashes and stores final list in JSON document.
+
+Used to figure out what tests were build in isolated mode to trigger these
+tests to run on swarming.
+
+For more info see:
+https://sites.google.com/a/chromium.org/dev/developers/testing/isolated-testing
+"""
+
+import glob
+import hashlib
+import json
+import optparse
+import os
+import re
+import sys
+
+
+def hash_file(filepath):
+  """Calculates the hash of a file without reading it all in memory at once."""
+  digest = hashlib.sha1()
+  with open(filepath, 'rb') as f:
+    while True:
+      chunk = f.read(1024*1024)
+      if not chunk:
+        break
+      digest.update(chunk)
+  return digest.hexdigest()
+
+
+def main():
+  parser = optparse.OptionParser(
+      usage='%prog --build-dir <path> '
+          '[--output-json <path> | --clean-isolated-files]',
+      description=sys.modules[__name__].__doc__)
+  parser.add_option(
+      '--build-dir',
+      help='Path to a directory to search for *.isolated files.')
+  parser.add_option(
+      '--output-json',
+      help='File to dump JSON results into. '
+          'Mutually exclusive with --clean-isolated-files.')
+  parser.add_option(
+      '--clean-isolated-files',
+      action='store_true',
+      help='Whether to clean out all .isolated and .isolated.gen.json files. '
+          'Mutually exclusive with --output-json.')
+
+  options, _ = parser.parse_args()
+  if not options.build_dir:
+    parser.error('--build-dir option is required')
+  if not (options.output_json or options.clean_isolated_files):
+    parser.error('either --output-json or --clean-isolated-files is required')
+  if options.output_json and options.clean_isolated_files:
+    parser.error('only one of --output-json and '
+                 '--clean-isolated-files is allowed')
+
+  result = {}
+
+  # Clean up generated *.isolated.gen.json files, produced by isolate_driver.py
+  # in test_isolation_mode='prepare'.
+  if options.clean_isolated_files:
+    pattern = os.path.join(options.build_dir, '*.isolated.gen.json')
+    for filepath in sorted(glob.glob(pattern)):
+      os.remove(filepath)
+
+  # Get the file hash values and output the pair.
+  pattern = os.path.join(options.build_dir, '*.isolated')
+  for filepath in sorted(glob.glob(pattern)):
+    test_name = os.path.splitext(os.path.basename(filepath))[0]
+    if re.match(r'^.+?\.\d$', test_name):
+      # It's a split .isolated file, e.g. foo.0.isolated. Ignore these.
+      continue
+
+    if options.clean_isolated_files:
+      # TODO(csharp): Remove deletion entirely once the isolate
+      # tracked dependencies are inputs for the isolated files.
+      # http://crbug.com/419031
+      os.remove(filepath)
+    else:
+      sha1_hash = hash_file(filepath)
+      result[test_name] = sha1_hash
+
+  if options.output_json:
+    with open(options.output_json, 'wb') as f:
+      json.dump(result, f)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/infra/bots/recipe_modules/isolate/resources/isolate.py b/infra/bots/recipe_modules/isolate/resources/isolate.py
new file mode 100755
index 0000000..9414df5
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/resources/isolate.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# Copyright 2015 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.
+
+"""Calls the isolate Go executable in the checkout, failing if it cannot run.
+"""
+
+# TODO(djd): make the caller invoke the Go binary directly, kill this script.
+
+import os
+import subprocess
+import sys
+
+
+def try_go(path, args):
+  """Tries to run the Go implementation of isolate.
+  """
+  luci_go = os.path.join(os.path.dirname(path), 'luci-go')
+  if sys.platform == 'win32':
+    exe = os.path.join(luci_go, 'win64', 'isolate.exe')
+  elif sys.platform == 'darwin':
+    exe = os.path.join(luci_go, 'mac64', 'isolate')
+  else:
+    exe = os.path.join(luci_go, 'linux64', 'isolate')
+
+  return subprocess.call([exe] + args)
+
+
+def main():
+  path = sys.argv[1]
+  args = sys.argv[2:]
+  return try_go(path, args)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/infra/bots/recipe_modules/isolate/test_api.py b/infra/bots/recipe_modules/isolate/test_api.py
new file mode 100644
index 0000000..e56e60b
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/test_api.py
@@ -0,0 +1,30 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+from recipe_engine import recipe_test_api
+
+class IsolateTestApi(recipe_test_api.RecipeTestApi):
+  def output_json(self, targets=None, missing=None):
+    """Mocked output of 'find_isolated_tests' and 'isolate_tests' steps.
+
+    Deterministically synthesizes json.output test data for the given targets.
+    If |targets| is None, will emit test data with some dummy targets instead,
+    emulating find_isolated_tests.py finding some files.
+
+    If |missing| is given it's a subset of |targets| that wasn't isolated in
+    'isolate_tests' due to some error.
+    """
+    missing = missing or ()
+    if targets is None:
+      targets = ['dummy_target_1', 'dummy_target_2']
+    return self.m.json.output({
+      target: None if target in missing else '[dummy hash for %s]' % target
+      for target in targets
+    })
diff --git a/infra/bots/recipe_modules/isolate/tests/clean_isolated_files.expected/basic.json b/infra/bots/recipe_modules/isolate/tests/clean_isolated_files.expected/basic.json
new file mode 100644
index 0000000..afe369a
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/clean_isolated_files.expected/basic.json
@@ -0,0 +1,18 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/find_isolated_tests.py",
+      "--build-dir",
+      "None/out/Release",
+      "--clean-isolated-files"
+    ],
+    "name": "clean isolated files"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/tests/clean_isolated_files.py b/infra/bots/recipe_modules/isolate/tests/clean_isolated_files.py
new file mode 100644
index 0000000..0757764
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/clean_isolated_files.py
@@ -0,0 +1,16 @@
+# 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 = [
+  'isolate',
+  'recipe_engine/path',
+]
+
+
+def RunSteps(api):
+  api.isolate.clean_isolated_files(api.path['checkout'].join('out', 'Release'))
+
+
+def GenTests(api):
+  yield api.test('basic')
diff --git a/infra/bots/recipe_modules/isolate/tests/isolate_tests.expected/basic.json b/infra/bots/recipe_modules/isolate/tests/isolate_tests.expected/basic.json
new file mode 100644
index 0000000..d2fcf46
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/isolate_tests.expected/basic.json
@@ -0,0 +1,41 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
+      "/path/to/tmp/",
+      "None/out/Release/*.isolated.gen.json"
+    ],
+    "name": "find isolated targets"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "None/tools/swarming_client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "None/out/Release/dummy_target_1.isolated.gen.json",
+      "None/out/Release/dummy_target_2.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"dummy_target_1\": \"[dummy hash for dummy_target_1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"dummy_target_2\": \"[dummy hash for dummy_target_2]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"dummy_target_1\": \"[dummy hash for dummy_target_1]\", \"dummy_target_2\": \"[dummy hash for dummy_target_2]\"}@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/tests/isolate_tests.py b/infra/bots/recipe_modules/isolate/tests/isolate_tests.py
new file mode 100644
index 0000000..9d0ba0f
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/isolate_tests.py
@@ -0,0 +1,16 @@
+# 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 = [
+  'isolate',
+  'recipe_engine/path',
+]
+
+
+def RunSteps(api):
+  api.isolate.isolate_tests(api.path['checkout'].join('out', 'Release'))
+
+
+def GenTests(api):
+  yield api.test('basic')
diff --git a/infra/bots/recipe_modules/isolate/tests/isolated_tests.expected/basic.json b/infra/bots/recipe_modules/isolate/tests/isolated_tests.expected/basic.json
new file mode 100644
index 0000000..e11a236
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/isolated_tests.expected/basic.json
@@ -0,0 +1,15 @@
+[
+  {
+    "cmd": [],
+    "name": "isolated_tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@details@isolated_tests: {'base_unittests': 'ffffffffffffffffffffffffffffffffffffffff'}@@@",
+      "@@@STEP_LOG_END@details@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/tests/isolated_tests.py b/infra/bots/recipe_modules/isolate/tests/isolated_tests.py
new file mode 100644
index 0000000..2101bd7
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/isolated_tests.py
@@ -0,0 +1,27 @@
+# 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 = [
+  'isolate',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  api.step('isolated_tests', [])
+  api.step.active_result.presentation.logs['details'] = [
+    'isolated_tests: %r' % api.isolate.isolated_tests
+  ]
+
+
+def GenTests(api):
+  yield (
+      api.test('basic') +
+      api.properties(
+          swarm_hashes={
+            'base_unittests': 'ffffffffffffffffffffffffffffffffffffffff',
+          }
+      )
+  )
diff --git a/infra/bots/recipe_modules/isolate/tests/run_isolated.expected/basic.json b/infra/bots/recipe_modules/isolate/tests/run_isolated.expected/basic.json
new file mode 100644
index 0000000..2ed58f1
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/run_isolated.expected/basic.json
@@ -0,0 +1,23 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "None/tools/swarming_client/run_isolated.py",
+      "--isolated",
+      "isolate_hash",
+      "-I",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "--",
+      "some",
+      "args"
+    ],
+    "name": "run_isolated"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/isolate/tests/run_isolated.py b/infra/bots/recipe_modules/isolate/tests/run_isolated.py
new file mode 100644
index 0000000..df83887
--- /dev/null
+++ b/infra/bots/recipe_modules/isolate/tests/run_isolated.py
@@ -0,0 +1,15 @@
+# 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 = [
+  'isolate',
+]
+
+
+def RunSteps(api):
+  api.isolate.run_isolated('run_isolated', 'isolate_hash', ['some', 'args'])
+
+
+def GenTests(api):
+  yield api.test('basic')
diff --git a/infra/bots/recipe_modules/perf/__init__.py b/infra/bots/recipe_modules/perf/__init__.py
deleted file mode 100644
index 97846ca..0000000
--- a/infra/bots/recipe_modules/perf/__init__.py
+++ /dev/null
@@ -1,18 +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.
-
-DEPS = [
-  'build/file',
-  'core',
-  'recipe_engine/json',
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-  'recipe_engine/step',
-  'recipe_engine/time',
-  'run',
-  'flavor',
-  'vars',
-]
diff --git a/infra/bots/recipe_modules/perf/api.py b/infra/bots/recipe_modules/perf/api.py
deleted file mode 100644
index d85a1b7..0000000
--- a/infra/bots/recipe_modules/perf/api.py
+++ /dev/null
@@ -1,289 +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 perf.
-
-
-import calendar
-
-from recipe_engine import recipe_api
-
-
-def nanobench_flags(bot):
-  args = ['--pre_log']
-
-  if 'GPU' in bot:
-    args.append('--images')
-    args.extend(['--gpuStatsDump', 'true'])
-
-  if 'Android' in bot and 'GPU' in bot:
-    args.extend(['--useThermalManager', '1,1,10,1000'])
-
-  args.extend(['--scales', '1.0', '1.1'])
-
-  if 'iOS' in bot:
-    args.extend(['--skps', 'ignore_skps'])
-
-  config = ['8888', 'nonrendering', 'hwui' ]
-
-  if '-arm-' not in bot:
-    # For Android CPU tests, these take too long and cause the task to time out.
-    config += [ 'f16', 'srgb' ]
-  if '-GCE-' in bot:
-    config += [ '565' ]
-
-  gl_prefix = 'gl'
-  sample_count = '8'
-  if 'Android' in bot or 'iOS' in bot:
-    sample_count = '4'
-    # The NVIDIA_Shield has a regular OpenGL implementation. We bench that
-    # instead of ES.
-    if 'NVIDIA_Shield' not in bot:
-      gl_prefix = 'gles'
-    # The NP produces a long error stream when we run with MSAA.
-    # iOS crashes (skia:6399)
-    if 'NexusPlayer' in bot or 'iOS' in bot:
-      sample_count = ''
-  elif 'Intel' in bot:
-    sample_count = ''
-
-  config.append(gl_prefix)
-  if sample_count is not '':
-    config.extend([gl_prefix + 'msaa' + sample_count,
-      gl_prefix + 'nvpr' + sample_count,
-      gl_prefix + 'nvprdit' + sample_count])
-
-  # We want to test both the OpenGL config and the GLES config on Linux Intel:
-  # GL is used by Chrome, GLES is used by ChromeOS.
-  if 'Intel' in bot and 'Ubuntu' in bot:
-    config.append('gles')
-
-  # Bench instanced rendering on a limited number of platforms
-  inst_config = gl_prefix + 'inst'
-  if 'Nexus6' in bot:
-    config.append(inst_config) # msaa inst isn't working yet on Adreno.
-  elif 'PixelC' in bot or 'NVIDIA_Shield' in bot or 'MacMini6.2' in bot:
-    config.extend([inst_config, inst_config + sample_count])
-
-  if 'CommandBuffer' in bot:
-    config = ['commandbuffer']
-  if 'Vulkan' in bot:
-    config = ['vk']
-
-  if 'ANGLE' in bot:
-    config.extend(['angle_d3d11_es2'])
-    # The GL backend of ANGLE crashes on the perf bot currently.
-    if 'Win' not in bot:
-      config.extend(['angle_gl_es2'])
-
-  args.append('--config')
-  args.extend(config)
-
-  if 'Valgrind' in bot:
-    # Don't care about Valgrind performance.
-    args.extend(['--loops',   '1'])
-    args.extend(['--samples', '1'])
-    # Ensure that the bot framework does not think we have timed out.
-    args.extend(['--keepAlive', 'true'])
-
-  match = []
-  if 'Android' in bot:
-    # Segfaults when run as GPU bench. Very large texture?
-    match.append('~blurroundrect')
-    match.append('~patch_grid')  # skia:2847
-    match.append('~desk_carsvg')
-  if 'NexusPlayer' in bot:
-    match.append('~desk_unicodetable')
-  if 'Nexus5' in bot:
-    match.append('~keymobi_shop_mobileweb_ebay_com.skp')  # skia:5178
-  if 'iOS' in bot:
-    match.append('~blurroundrect')
-    match.append('~patch_grid')  # skia:2847
-    match.append('~desk_carsvg')
-    match.append('~keymobi')
-    match.append('~path_hairline')
-    match.append('~GLInstancedArraysBench') # skia:4714
-  if 'IntelIris540' in bot and 'ANGLE' in bot:
-    match.append('~tile_image_filter_tiled_64')  # skia:6082
-  if 'Intel' in bot and 'Ubuntu' in bot and not 'Vulkan' in bot:
-    match.append('~native_image_to_raster_surface')  # skia:6401
-  if 'Vulkan' in bot and 'IntelIris540' in bot and 'Win' in bot:
-    # skia:6398
-    match.append('~GM_varied_text_clipped_lcd')
-    match.append('~GM_varied_text_ignorable_clip_lcd')
-    match.append('~Xfermode_DstATop_aa')
-    match.append('~Xfermode_SrcIn_aa')
-    match.append('~Xfermode_SrcOut_aa')
-    match.append('~Xfermode_Src_aa')
-    match.append('~fontscaler_lcd')
-    match.append('~rotated_rects_aa_alternating_transparent_and_opaque_src')
-    match.append('~rotated_rects_aa_changing_transparent_src')
-    match.append('~rotated_rects_aa_same_transparent_src')
-    match.append('~shadermask_LCD_FF')
-    match.append('~srcmode_rects_1')
-    match.append('~text_16_LCD_88')
-    match.append('~text_16_LCD_BK')
-    match.append('~text_16_LCD_FF')
-    match.append('~text_16_LCD_WT')
-  if 'Vulkan' in bot and 'NexusPlayer' in bot:
-    match.append('~hardstop') # skia:6037
-
-  # We do not need or want to benchmark the decodes of incomplete images.
-  # In fact, in nanobench we assert that the full image decode succeeds.
-  match.append('~inc0.gif')
-  match.append('~inc1.gif')
-  match.append('~incInterlaced.gif')
-  match.append('~inc0.jpg')
-  match.append('~incGray.jpg')
-  match.append('~inc0.wbmp')
-  match.append('~inc1.wbmp')
-  match.append('~inc0.webp')
-  match.append('~inc1.webp')
-  match.append('~inc0.ico')
-  match.append('~inc1.ico')
-  match.append('~inc0.png')
-  match.append('~inc1.png')
-  match.append('~inc2.png')
-  match.append('~inc12.png')
-  match.append('~inc13.png')
-  match.append('~inc14.png')
-  match.append('~inc0.webp')
-  match.append('~inc1.webp')
-
-  if match:
-    args.append('--match')
-    args.extend(match)
-
-  return args
-
-
-def perf_steps(api):
-  """Run Skia benchmarks."""
-  if api.vars.upload_perf_results:
-    api.flavor.create_clean_device_dir(
-        api.flavor.device_dirs.perf_data_dir)
-
-  # Run nanobench.
-  properties = [
-    '--properties',
-    'gitHash',      api.vars.got_revision,
-    'build_number', api.vars.build_number,
-  ]
-  if api.vars.is_trybot:
-    properties.extend([
-      'issue',    api.vars.issue,
-      'patchset', api.vars.patchset,
-      'patch_storage', api.vars.patch_storage,
-    ])
-  if api.vars.no_buildbot:
-    properties.extend(['no_buildbot', 'True'])
-    properties.extend(['swarming_bot_id', api.vars.swarming_bot_id])
-    properties.extend(['swarming_task_id', api.vars.swarming_task_id])
-
-  target = 'nanobench'
-  args = [
-      target,
-      '--undefok',   # This helps branches that may not know new flags.
-      '-i',       api.flavor.device_dirs.resource_dir,
-      '--skps',   api.flavor.device_dirs.skp_dir,
-      '--images', api.flavor.device_path_join(
-          api.flavor.device_dirs.images_dir, 'nanobench'),
-  ]
-
-  # Do not run svgs on Valgrind.
-  if 'Valgrind' not in api.vars.builder_name:
-    if ('Vulkan' not in api.vars.builder_name or
-        'NexusPlayer' not in api.vars.builder_name):
-      args.extend(['--svgs',  api.flavor.device_dirs.svg_dir])
-
-  skip_flag = None
-  if api.vars.builder_cfg.get('cpu_or_gpu') == 'CPU':
-    skip_flag = '--nogpu'
-  elif api.vars.builder_cfg.get('cpu_or_gpu') == 'GPU':
-    skip_flag = '--nocpu'
-  if skip_flag:
-    args.append(skip_flag)
-  args.extend(nanobench_flags(api.vars.builder_name))
-
-  if 'Chromecast' in api.vars.builder_cfg.get('os', ''):
-    # Due to limited disk space, run a watered down perf run on Chromecast.
-    args = [
-      target,
-       '-i', api.flavor.device_dirs.resource_dir,
-       '--images', api.flavor.device_path_join(
-            api.flavor.device_dirs.resource_dir, 'color_wheel.jpg'),
-       '--svgs',  api.flavor.device_dirs.svg_dir,
-    ]
-
-  if api.vars.upload_perf_results:
-    now = api.time.utcnow()
-    ts = int(calendar.timegm(now.utctimetuple()))
-    json_path = api.flavor.device_path_join(
-        api.flavor.device_dirs.perf_data_dir,
-        'nanobench_%s_%d.json' % (api.vars.got_revision, ts))
-    args.extend(['--outResultsFile', json_path])
-    args.extend(properties)
-
-    keys_blacklist = ['configuration', 'role', 'is_trybot']
-    args.append('--key')
-    for k in sorted(api.vars.builder_cfg.keys()):
-      if not k in keys_blacklist:
-        args.extend([k, api.vars.builder_cfg[k]])
-
-  env = api.step.get_from_context('env', {})
-  if 'Ubuntu16' in api.vars.builder_name:
-    # The vulkan in this asset name simply means that the graphics driver
-    # supports Vulkan. It is also the driver used for GL code.
-    dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_release')
-    if 'Debug' in api.vars.builder_name:
-      dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_debug')
-
-    if 'Vulkan' in api.vars.builder_name:
-      sdk_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'bin')
-      lib_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'lib')
-      env.update({
-        'PATH':'%%(PATH)s:%s' % sdk_path,
-        'LD_LIBRARY_PATH': '%s:%s' % (lib_path, dri_path),
-        'LIBGL_DRIVERS_PATH': dri_path,
-        'VK_ICD_FILENAMES':'%s' % dri_path.join('intel_icd.x86_64.json'),
-      })
-    else:
-      # Even the non-vulkan NUC jobs could benefit from the newer drivers.
-      env.update({
-        'LD_LIBRARY_PATH': dri_path,
-        'LIBGL_DRIVERS_PATH': dri_path,
-      })
-
-  # See skia:2789.
-  if '_AbandonGpuContext' in api.vars.builder_cfg.get('extra_config', ''):
-    args.extend(['--abandonGpuContext', '--nocpu'])
-
-  with api.step.context({'env': env}):
-    api.run(api.flavor.step, target, cmd=args,
-            abort_on_failure=False)
-
-  # Copy results to swarming out dir.
-  if api.vars.upload_perf_results:
-    api.file.makedirs('perf_dir', api.vars.perf_data_dir)
-    api.flavor.copy_directory_contents_to_host(
-        api.flavor.device_dirs.perf_data_dir,
-        api.vars.perf_data_dir)
-
-class PerfApi(recipe_api.RecipeApi):
-  def run(self):
-    self.m.core.setup()
-    env = self.m.step.get_from_context('env', {})
-    if 'iOS' in self.m.vars.builder_name:
-      env['IOS_BUNDLE_ID'] = 'com.google.nanobench'
-    with self.m.step.context({'env': env}):
-      try:
-        if 'Chromecast' in self.m.vars.builder_name:
-          self.m.flavor.install(resources=True, skps=True)
-        else:
-          self.m.flavor.install_everything()
-        perf_steps(self.m)
-      finally:
-        self.m.flavor.cleanup_steps()
-      self.m.run.check_failure()
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android_Vulkan.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android_Vulkan.json
deleted file mode 100644
index 5ebbd53..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android_Vulkan.json
+++ /dev/null
@@ -1,645 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config vk --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": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android.json
deleted file mode 100644
index 1758f6b..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android.json
+++ /dev/null
@@ -1,720 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nogpu --pre_log --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch arm compiler Clang cpu_or_gpu CPU cpu_or_gpu_value Exynos5250 extra_config Android model Nexus10 os Android; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-GN_Android.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-GN_Android.json
deleted file mode 100644
index 6d0157b..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-GN_Android.json
+++ /dev/null
@@ -1,645 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 --match ~blurroundrect ~patch_grid ~desk_carsvg ~keymobi_shop_mobileweb_ebay_com.skp ~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": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android.json
deleted file mode 100644
index fb5f1ad..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android.json
+++ /dev/null
@@ -1,720 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 glesinst --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch arm compiler Clang cpu_or_gpu GPU cpu_or_gpu_value Adreno420 extra_config GN_Android model Nexus6 os Android; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android.json
deleted file mode 100644
index ab4570f..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android.json
+++ /dev/null
@@ -1,720 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch arm compiler Clang cpu_or_gpu GPU cpu_or_gpu_value Tegra3 extra_config GN_Android model Nexus7 os Android; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android.json
deleted file mode 100644
index 2db362a..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android.json
+++ /dev/null
@@ -1,783 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui f16 srgb gles --match ~blurroundrect ~patch_grid ~desk_carsvg ~desk_unicodetable ~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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch x86 compiler Clang cpu_or_gpu GPU cpu_or_gpu_value PowerVR extra_config GN_Android model NexusPlayer os Android; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan.json
deleted file mode 100644
index eb9c0ba..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan.json
+++ /dev/null
@@ -1,783 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -i /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/nanobench --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config vk --match ~blurroundrect ~patch_grid ~desk_carsvg ~desk_unicodetable ~hardstop ~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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch x86 compiler Clang cpu_or_gpu GPU cpu_or_gpu_value PowerVR extra_config GN_Android_Vulkan model NexusPlayer os Android; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android.json
deleted file mode 100644
index be6c241..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android.json
+++ /dev/null
@@ -1,783 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui f16 srgb gles glesmsaa4 glesnvpr4 glesnvprdit4 glesinst glesinst4 --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch arm64 compiler Clang cpu_or_gpu GPU cpu_or_gpu_value TegraX1 extra_config GN_Android model PixelC os Android; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug.json
deleted file mode 100644
index dea9916..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug.json
+++ /dev/null
@@ -1,398 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nCHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')\nwith open(CHROMECAST_IP_FILE, 'r') as f:\n  print f.read()\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read chromecast ip",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')@@@",
-      "@@@STEP_LOG_LINE@python.inline@with open(CHROMECAST_IP_FILE, 'r') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "connect",
-      "192.168.1.2:5555"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "adb connect 192.168.1.2:5555"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/cache/skia/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /cache/skia/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/cache/skia/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /cache/skia/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/cache/skia/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /cache/skia/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/cache/skia/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /cache/skia/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-r",
-      "/cache/skia/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /cache/skia/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/cache/skia/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /cache/skia/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/cache/skia/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /cache/skia/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/cache/skia/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /cache/skia/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/nanobench",
-      "/cache/skia/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /cache/skia/nanobench -i /cache/skia/resources --images /cache/skia/resources/color_wheel.jpg --svgs /cache/skia/svgs; echo $? >/cache/skia/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/cache/skia/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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",
-      "/cache/skia/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release.json
deleted file mode 100644
index c244a00..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release.json
+++ /dev/null
@@ -1,473 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nCHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')\nwith open(CHROMECAST_IP_FILE, 'r') as f:\n  print f.read()\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read chromecast ip",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')@@@",
-      "@@@STEP_LOG_LINE@python.inline@with open(CHROMECAST_IP_FILE, 'r') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "connect",
-      "192.168.1.2:5555"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "adb connect 192.168.1.2:5555"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/cache/skia/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /cache/skia/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/cache/skia/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /cache/skia/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/cache/skia/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /cache/skia/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/cache/skia/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /cache/skia/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-r",
-      "/cache/skia/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /cache/skia/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/cache/skia/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /cache/skia/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/cache/skia/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /cache/skia/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/cache/skia/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /cache/skia/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-r",
-      "/cache/skia/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /cache/skia/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/cache/skia/perf"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /cache/skia/perf"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/nanobench",
-      "/cache/skia/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /cache/skia/nanobench -i /cache/skia/resources --images /cache/skia/resources/color_wheel.jpg --svgs /cache/skia/svgs --outResultsFile /cache/skia/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch arm compiler GCC cpu_or_gpu CPU cpu_or_gpu_value Cortex_A7 model Chorizo os Chromecast; echo $? >/cache/skia/rc",
-      "[START_DIR]/tmp/nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/nanobench.sh",
-      "/cache/skia/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push nanobench.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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",
-      "/cache/skia/",
-      "nanobench.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/cache/skia/perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release/data"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /cache/skia/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release/data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN.json
deleted file mode 100644
index 5175402..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN.json
+++ /dev/null
@@ -1,212 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN/data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN/data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nogpu",
-      "--pre_log",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "glinst",
-      "glinst8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN/data/nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX",
-      "extra_config",
-      "GN",
-      "model",
-      "MacMini6.2",
-      "os",
-      "Mac"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer.json
deleted file mode 100644
index b3b4a51..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer.json
+++ /dev/null
@@ -1,124 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/out/Debug/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "commandbuffer",
-      "--match",
-      "~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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN.json
deleted file mode 100644
index 86d4898..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN.json
+++ /dev/null
@@ -1,217 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN/data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN/data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nogpu",
-      "--pre_log",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "565",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN/data/nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "extra_config",
-      "GN",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE.json
deleted file mode 100644
index 832ac43..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE.json
+++ /dev/null
@@ -1,221 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE/data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE/data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "angle_d3d11_es2",
-      "angle_gl_es2",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE/data/nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX550Ti",
-      "extra_config",
-      "ANGLE",
-      "model",
-      "ShuttleA",
-      "os",
-      "Ubuntu"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
deleted file mode 100644
index 0bcd650..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
+++ /dev/null
@@ -1,143 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "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]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--loops",
-      "1",
-      "--samples",
-      "1",
-      "--keepAlive",
-      "true",
-      "--match",
-      "~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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
deleted file mode 100644
index de27344..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
+++ /dev/null
@@ -1,145 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "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]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--loops",
-      "1",
-      "--samples",
-      "1",
-      "--keepAlive",
-      "true",
-      "--match",
-      "~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",
-      "--abandonGpuContext",
-      "--nocpu"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json
deleted file mode 100644
index b939b62..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json
+++ /dev/null
@@ -1,132 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Debug/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "vk",
-      "--match",
-      "~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"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_sdk/lib:[START_DIR]/linux_vulkan_intel_driver_debug",
-      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out",
-      "VK_ICD_FILENAMES": "[START_DIR]/linux_vulkan_intel_driver_debug/intel_icd.x86_64.json"
-    },
-    "name": "symbolized nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release.json
deleted file mode 100644
index a319907..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release.json
+++ /dev/null
@@ -1,217 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release/data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release/data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "gles",
-      "--match",
-      "~native_image_to_raster_surface",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release/data/nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "model",
-      "NUC",
-      "os",
-      "Ubuntu16"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
-      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Debug.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Debug.json
deleted file mode 100644
index bd59f4e..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Debug.json
+++ /dev/null
@@ -1,130 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Debug_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nogpu",
-      "--pre_log",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "565",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release.json
deleted file mode 100644
index 4c388cc..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release.json
+++ /dev/null
@@ -1,209 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release\\data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release\\data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Release_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nogpu",
-      "--pre_log",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "565",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release\\data\\nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Win"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release\\data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE.json
deleted file mode 100644
index 38558c9..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE.json
+++ /dev/null
@@ -1,212 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE\\data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE\\data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Release_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "angle_d3d11_es2",
-      "--match",
-      "~tile_image_filter_tiled_64",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE\\data\\nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "extra_config",
-      "ANGLE",
-      "model",
-      "NUC",
-      "os",
-      "Win10"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE\\data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan.json
deleted file mode 100644
index 2b8feb7..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan.json
+++ /dev/null
@@ -1,221 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan\\data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan\\data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Release_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "vk",
-      "--match",
-      "~GM_varied_text_clipped_lcd",
-      "~GM_varied_text_ignorable_clip_lcd",
-      "~Xfermode_DstATop_aa",
-      "~Xfermode_SrcIn_aa",
-      "~Xfermode_SrcOut_aa",
-      "~Xfermode_Src_aa",
-      "~fontscaler_lcd",
-      "~rotated_rects_aa_alternating_transparent_and_opaque_src",
-      "~rotated_rects_aa_changing_transparent_src",
-      "~rotated_rects_aa_same_transparent_src",
-      "~shadermask_LCD_FF",
-      "~srcmode_rects_1",
-      "~text_16_LCD_88",
-      "~text_16_LCD_BK",
-      "~text_16_LCD_FF",
-      "~text_16_LCD_WT",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan\\data\\nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "extra_config",
-      "Vulkan",
-      "model",
-      "NUC",
-      "os",
-      "Win10"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan\\data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE.json
deleted file mode 100644
index 1c5a937..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE.json
+++ /dev/null
@@ -1,133 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Debug_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "angle_d3d11_es2",
-      "--match",
-      "~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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot.json b/infra/bots/recipe_modules/perf/example.expected/Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot.json
deleted file mode 100644
index 7239fb4..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot.json
+++ /dev/null
@@ -1,217 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Release_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data\\nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "issue",
-      "500",
-      "patchset",
-      "1",
-      "patch_storage",
-      "rietveld",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "HD4600",
-      "model",
-      "ShuttleB",
-      "os",
-      "Win8"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json b/infra/bots/recipe_modules/perf/example.expected/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
deleted file mode 100644
index fd7807f..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
+++ /dev/null
@@ -1,470 +0,0 @@
-[
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/skia/resources",
-      "resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/skia/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
-      "tmp/SKP_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "cat_file tmp/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "tmp/SKP_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm tmp/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm skps"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir skps"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/skp",
-      "skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/skp"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "tmp/SKP_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
-      "tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "cat_file tmp/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm tmp/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm images"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir images"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/skimage",
-      "images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/skimage"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
-      "tmp/SVG_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "cat_file tmp/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "tmp/SVG_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm tmp/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm svgs"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir svgs"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/svg",
-      "svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/svg"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "tmp/SVG_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "perf"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm perf"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "perf"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir perf"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/gn/package_ios.py",
-      "[START_DIR]/out/Release/nanobench"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "package nanobench"
-  },
-  {
-    "cmd": [
-      "ios-deploy",
-      "-b",
-      "[START_DIR]/out/Release/nanobench.app",
-      "-I",
-      "--args",
-      "--undefok -i resources --skps skps --images images/nanobench --svgs svgs --nocpu --pre_log --images --gpuStatsDump true --scales 1.0 1.1 --skps ignore_skps --config 8888 nonrendering hwui gles --match ~blurroundrect ~patch_grid ~desk_carsvg ~keymobi ~path_hairline ~GLInstancedArraysBench ~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 --outResultsFile perf/nanobench_abc123_1337000001.json --properties gitHash abc123 build_number 5 --key arch arm compiler Clang cpu_or_gpu GPU cpu_or_gpu_value GX6450 model iPadMini4 os iOS"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release/data",
-      "511"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.nanobench"
-    },
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_pull_if_needed",
-      "perf",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release/data"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.nanobench",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "pull_if_needed perf"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/big_issue_number.json b/infra/bots/recipe_modules/perf/example.expected/big_issue_number.json
deleted file mode 100644
index 4e9989b..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/big_issue_number.json
+++ /dev/null
@@ -1,217 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]\\out\\Release_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data\\nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "issue",
-      "2147533002",
-      "patchset",
-      "1",
-      "patch_storage",
-      "rietveld",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "HD4600",
-      "model",
-      "ShuttleB",
-      "os",
-      "Win8"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/failed_push.json b/infra/bots/recipe_modules/perf/example.expected/failed_push.json
deleted file mode 100644
index 9b12457..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/failed_push.json
+++ /dev/null
@@ -1,192 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@",
-      "@@@STEP_EXCEPTION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "reboot",
-      "-p"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "shut down device to quarantine bot"
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "reason": "Infra Failure: Step('push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources') returned 1",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/nobuildbot.json b/infra/bots/recipe_modules/perf/example.expected/nobuildbot.json
deleted file mode 100644
index e91667a..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/nobuildbot.json
+++ /dev/null
@@ -1,251 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id",
-    "stdout": "/path/to/tmp/",
-    "~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",
-    "stdout": "/path/to/tmp/",
-    "~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": [
-      "[START_DIR]\\out\\Release_x64\\nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\nanobench",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data\\nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "571",
-      "issue",
-      "456789",
-      "patchset",
-      "12",
-      "patch_storage",
-      "gerrit",
-      "no_buildbot",
-      "True",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "HD4600",
-      "model",
-      "ShuttleB",
-      "os",
-      "Win8"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot\\data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.expected/recipe_with_gerrit_patch.json b/infra/bots/recipe_modules/perf/example.expected/recipe_with_gerrit_patch.json
deleted file mode 100644
index e005777..0000000
--- a/infra/bots/recipe_modules/perf/example.expected/recipe_with_gerrit_patch.json
+++ /dev/null
@@ -1,166 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "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]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--nocpu",
-      "--pre_log",
-      "--images",
-      "--gpuStatsDump",
-      "true",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--loops",
-      "1",
-      "--samples",
-      "1",
-      "--keepAlive",
-      "true",
-      "--match",
-      "~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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "nanobench"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/perf/example.py b/infra/bots/recipe_modules/perf/example.py
deleted file mode 100644
index 900d58d..0000000
--- a/infra/bots/recipe_modules/perf/example.py
+++ /dev/null
@@ -1,195 +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.
-
-
-# Example recipe w/ coverage.
-
-
-DEPS = [
-  'perf',
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-]
-
-
-TEST_BUILDERS = {
-  'client.skia': {
-    'skiabot-linux-swarm-000': [
-      ('Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug' +
-       '-GN_Android_Vulkan'),
-      'Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android',
-      'Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-GN_Android',
-      'Perf-Android-Clang-Nexus6-GPU-Adreno420-arm-Release-GN_Android',
-      'Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-GN_Android',
-      'Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android',
-      ('Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_'
-       'Android_Vulkan'),
-      'Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android',
-      'Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug',
-      'Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release',
-      'Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release-GN',
-      'Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer',
-      'Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-GN',
-      'Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-ANGLE',
-      'Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind',
-      ('Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind' +
-       '_AbandonGpuContext'),
-      'Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan',
-      'Perf-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release',
-      'Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Debug',
-      'Perf-Win-MSVC-GCE-CPU-AVX2-x86_64-Release',
-      'Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-ANGLE',
-      'Perf-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Release-Vulkan',
-      'Perf-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE',
-      'Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot',
-      'Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release'
-    ],
-  },
-}
-
-
-def RunSteps(api):
-  api.perf.run()
-
-
-def GenTests(api):
-  for mastername, slaves in TEST_BUILDERS.iteritems():
-    for slavename, builders_by_slave in slaves.iteritems():
-      for builder in builders_by_slave:
-        test = (
-          api.test(builder) +
-          api.properties(buildername=builder,
-                         mastername=mastername,
-                         slavename=slavename,
-                         buildnumber=5,
-                         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',
-                                           'skimage', 'VERSION'),
-              api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                           'skp', 'VERSION'),
-              api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-          )
-        )
-        if 'Trybot' in builder:
-          test += api.properties(issue=500,
-                                 patchset=1,
-                                 rietveld='https://codereview.chromium.org')
-        if 'Win' in builder:
-          test += api.platform('win', 64)
-
-        if 'Chromecast' in builder:
-          test += api.step_data('read chromecast ip',
-                  stdout=api.raw_io.output('192.168.1.2:5555'))
-
-        yield test
-
-  builder = 'Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot'
-  yield (
-    api.test('big_issue_number') +
-    api.properties(buildername=builder,
-                   mastername='client.skia.compile',
-                   slavename='skiabot-linux-swarm-000',
-                   buildnumber=5,
-                   revision='abc123',
-                   path_config='kitchen',
-                   swarm_out_dir='[SWARM_OUT_DIR]',
-                   rietveld='https://codereview.chromium.org',
-                   patchset=1,
-                   issue=2147533002L) +
-    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')
-    ) +
-    api.platform('win', 64)
-  )
-
-  builder = ('Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind-'
-             'Trybot')
-  yield (
-      api.test('recipe_with_gerrit_patch') +
-      api.properties(
-          buildername=builder,
-          mastername='client.skia',
-          slavename='skiabot-linux-swarm-000',
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          revision='abc123',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=builder,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      )
-  )
-
-  builder = 'Perf-Win8-MSVC-ShuttleB-GPU-HD4600-x86_64-Release-Trybot'
-  yield (
-      api.test('nobuildbot') +
-      api.properties(
-          buildername=builder,
-          mastername='client.skia',
-          slavename='skiabot-linux-swarm-000',
-          buildnumber=5,
-          revision='abc123',
-          path_config='kitchen',
-          nobuildbot='True',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=builder,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      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')
-      ) +
-      api.platform('win', 64) +
-      api.step_data('get swarming bot id',
-          stdout=api.raw_io.output('skia-bot-123')) +
-      api.step_data('get swarming task id', stdout=api.raw_io.output('123456'))
-  )
-
-  builder = 'Perf-Android-Clang-NexusPlayer-CPU-SSE4-x86-Debug-GN_Android'
-  yield (
-    api.test('failed_push') +
-    api.properties(buildername=builder,
-                   mastername='client.skia',
-                   slavename='skiabot-linux-swarm-000',
-                   buildnumber=6,
-                   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',
-                                     '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')
-    ) +
-    api.step_data('push [START_DIR]/skia/resources/* '+
-                  '/sdcard/revenge_of_the_skiabot/resources', retcode=1)
-  )
diff --git a/infra/bots/recipe_modules/run/__init__.py b/infra/bots/recipe_modules/run/__init__.py
index e9a6f1c..7c13f4f 100644
--- a/infra/bots/recipe_modules/run/__init__.py
+++ b/infra/bots/recipe_modules/run/__init__.py
@@ -3,7 +3,8 @@
 # found in the LICENSE file.
 
 DEPS = [
-  'build/file',
+  'env',
+  'file',
   'recipe_engine/json',
   'recipe_engine/path',
   'recipe_engine/platform',
@@ -11,7 +12,3 @@
   'recipe_engine/step',
   'vars',
 ]
-
-
-# TODO(borenet): Add coverage
-DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/run/api.py b/infra/bots/recipe_modules/run/api.py
index e4e0ae8..b487a75 100644
--- a/infra/bots/recipe_modules/run/api.py
+++ b/infra/bots/recipe_modules/run/api.py
@@ -12,6 +12,8 @@
 BUILD_PRODUCTS_ISOLATE_WHITELIST = [
   'dm',
   'dm.exe',
+  'dm.app',
+  'nanobench.app',
   'get_images_from_skps',
   'get_images_from_skps.exe',
   'nanobench',
@@ -58,7 +60,7 @@
 
   def readfile(self, filename, *args, **kwargs):
     """Convenience function for reading files."""
-    name = kwargs.pop('name') or 'read %s' % self.m.path.basename(filename)
+    name = kwargs.pop('name', 'read %s' % self.m.path.basename(filename))
     return self.m.file.read(name, filename, infra_step=True, *args, **kwargs)
 
   def writefile(self, filename, contents):
@@ -68,10 +70,9 @@
 
   def rmtree(self, path):
     """Wrapper around api.file.rmtree with environment fix."""
-    env = self.m.step.get_from_context('env', {})
-    env['PYTHONPATH'] = str(self.m.path['start_dir'].join(
-        'skia', 'infra', 'bots', '.recipe_deps', 'build', 'scripts'))
-    with self.m.step.context({'env': env}):
+    env = {'PYTHONPATH': str(self.m.path['start_dir'].join(
+        'skia', 'infra', 'bots', '.recipe_deps', 'build', 'scripts'))}
+    with self.m.env(env):
       self.m.file.rmtree(self.m.path.basename(path),
                          path,
                          infra_step=True)
@@ -79,16 +80,14 @@
   def __call__(self, steptype, name, abort_on_failure=True,
                fail_build_on_failure=True, **kwargs):
     """Run a step. If it fails, keep going but mark the build status failed."""
-    env = self.m.step.get_from_context('env', {})
-    env.update(self.m.vars.default_env)
     try:
-      with self.m.step.context({'env': env}):
+      with self.m.env(self.m.vars.default_env):
         return steptype(name=name, **kwargs)
     except self.m.step.StepFailure as e:
       if abort_on_failure or fail_build_on_failure:
         self._failed.append(e)
       if abort_on_failure:
-        raise  # pragma: no cover
+        raise
 
   def copy_build_products(self, src, dst):
     """Copy whitelisted build products from src to dst."""
diff --git a/infra/bots/recipe_modules/run/examples/full.expected/test.json b/infra/bots/recipe_modules/run/examples/full.expected/test.json
new file mode 100644
index 0000000..4689d79
--- /dev/null
+++ b/infra/bots/recipe_modules/run/examples/full.expected/test.json
@@ -0,0 +1,276 @@
+[
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "fail",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "fail again",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "do the thing"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "do a thing"
+  },
+  {
+    "cmd": [
+      "echo",
+      "0"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "run 0"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "myfile.txt",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "read myfile.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "contents",
+      "myfile.txt"
+    ],
+    "infra_step": true,
+    "name": "write myfile.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "mydir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree mydir"
+  },
+  {
+    "cmd": [
+      "env"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "mydir:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "env"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "src",
+      "dst"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry fail",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry fail (attempt 2)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry fail (attempt 3)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry fail (attempt 4)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry fail (attempt 5)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry success",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry success (attempt 2)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "false"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "retry success (attempt 3)"
+  },
+  {
+    "name": "$result",
+    "reason": "Failed build steps: fail, fail again, retry fail, retry fail (attempt 2), retry fail (attempt 3), retry fail (attempt 4), retry fail (attempt 5), retry success, retry success (attempt 2)",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/run/examples/full.py b/infra/bots/recipe_modules/run/examples/full.py
new file mode 100644
index 0000000..162f679
--- /dev/null
+++ b/infra/bots/recipe_modules/run/examples/full.py
@@ -0,0 +1,75 @@
+# 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 = [
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+  'run',
+  'vars',
+]
+
+
+def myfunc(api, i):
+  api.run(api.step, 'run %d' % i, cmd=['echo', str(i)])
+
+
+def RunSteps(api):
+  api.vars.setup()
+  try:
+    api.run(api.step, 'fail', cmd=['false'])
+  except api.step.StepFailure:
+    pass
+  api.run(api.step, 'fail again', cmd=['false'], abort_on_failure=False)
+  api.run(api.step, 'do a thing', cmd=['echo', 'do the thing'])
+  assert len(api.run.failed_steps) == 2
+
+  # Run once.
+  for i in range(10):
+    api.run.run_once(myfunc, api, i)
+
+  # Read and write files.
+  api.run.readfile('myfile.txt')
+  api.run.writefile('myfile.txt', 'contents')
+  api.run.rmtree('mydir')
+
+  # Merge PATHs.
+  with api.context(env={'PATH': 'mydir:%(PATH)s'}):
+    api.run(api.step, 'env', cmd=['env'])
+
+  # Copy build products.
+  api.run.copy_build_products('src', 'dst')
+
+  # Retries.
+  try:
+    api.run.with_retry(api.step, 'retry fail', 5, cmd=['false'])
+  except api.step.StepFailure:
+    pass
+  api.run.with_retry(api.step, 'retry success', 3, cmd=['false'])
+
+  # Check failure.
+  api.run.check_failure()
+
+
+def GenTests(api):
+  buildername = 'Build-Win-MSVC-x86_64-Release-Vulkan'
+  yield (
+      api.test('test') +
+      api.properties(buildername=buildername,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.step_data('fail', retcode=1) +
+      api.step_data('fail again', retcode=1) +
+      api.step_data('retry fail', retcode=1) +
+      api.step_data('retry fail (attempt 2)', retcode=1) +
+      api.step_data('retry fail (attempt 3)', retcode=1) +
+      api.step_data('retry fail (attempt 4)', retcode=1) +
+      api.step_data('retry fail (attempt 5)', retcode=1) +
+      api.step_data('retry success', retcode=1) +
+      api.step_data('retry success (attempt 2)', retcode=1)
+    )
diff --git a/infra/bots/recipe_modules/skia_swarming/__init__.py b/infra/bots/recipe_modules/skia_swarming/__init__.py
new file mode 100644
index 0000000..435ad0b
--- /dev/null
+++ b/infra/bots/recipe_modules/skia_swarming/__init__.py
@@ -0,0 +1,19 @@
+# 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.
+
+DEPS = [
+  'depot_tools/depot_tools',
+  'file',
+  'isolate',
+  'recipe_engine/context',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'run',
+  'swarming',
+  'swarming_client',
+]
diff --git a/infra/bots/recipe_modules/skia_swarming/api.py b/infra/bots/recipe_modules/skia_swarming/api.py
new file mode 100644
index 0000000..35dddcf
--- /dev/null
+++ b/infra/bots/recipe_modules/skia_swarming/api.py
@@ -0,0 +1,220 @@
+# 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.
+
+
+from recipe_engine import recipe_api
+import shlex
+
+
+DEFAULT_TASK_EXPIRATION = 20*60*60
+DEFAULT_TASK_TIMEOUT = 4*60*60
+DEFAULT_IO_TIMEOUT = 40*60
+
+MILO_LOG_LINK = 'https://luci-milo.appspot.com/swarming/task/%s'
+
+
+class SkiaSwarmingApi(recipe_api.RecipeApi):
+  """Provides steps to run Skia tasks on swarming bots."""
+
+  @property
+  def swarming_temp_dir(self):
+    """Path where artifacts like isolate file and json output will be stored."""
+    return self.m.path['start_dir'].join('swarming_temp_dir')
+
+  @property
+  def tasks_output_dir(self):
+    """Directory where the outputs of the swarming tasks will be stored."""
+    return self.swarming_temp_dir.join('outputs')
+
+  def isolated_file_path(self, task_name):
+    """Get the path to the given task's .isolated file."""
+    return self.swarming_temp_dir.join('skia-task-%s.isolated' % task_name)
+
+  def setup(self, luci_go_dir, swarming_rev=None):
+    """Performs setup steps for swarming."""
+    self.m.swarming_client.checkout(revision=swarming_rev)
+    self.m.swarming.check_client_version(step_test_data=(0, 8, 6))
+    self.setup_go_isolate(luci_go_dir)
+    self.m.swarming.add_default_tag('allow_milo:1')
+
+  # TODO(rmistry): Remove once the Go binaries are moved to recipes or buildbot.
+  def setup_go_isolate(self, luci_go_dir):
+    """Generates and puts in place the isolate Go binary."""
+    depot_tools_path = self.m.depot_tools.package_repo_resource()
+    env = {'PATH': self.m.path.pathsep.join([
+                       str(depot_tools_path), '%(PATH)s'])}
+    with self.m.context(env=env):
+      self.m.step('download luci-go linux',
+                  ['download_from_google_storage', '--no_resume',
+                   '--platform=linux*', '--no_auth',
+                   '--bucket', 'chromium-luci',
+                   '-d', luci_go_dir.join('linux64')])
+      self.m.step('download luci-go mac',
+                  ['download_from_google_storage', '--no_resume',
+                   '--platform=darwin', '--no_auth',
+                   '--bucket', 'chromium-luci',
+                   '-d', luci_go_dir.join('mac64')])
+      self.m.step('download luci-go win',
+                  ['download_from_google_storage', '--no_resume',
+                   '--platform=win32', '--no_auth', '--bucket',
+                   'chromium-luci',
+                   '-d', luci_go_dir.join('win64')])
+    # Copy binaries to the expected location.
+    dest = self.m.path['start_dir'].join('luci-go')
+    self.m.run.rmtree(dest)
+    self.m.file.copytree('Copy Go binary',
+                         source=luci_go_dir,
+                         dest=dest)
+
+  def create_isolated_gen_json(self, isolate_path, base_dir, os_type,
+                               task_name, extra_variables, blacklist=None):
+    """Creates an isolated.gen.json file (used by the isolate recipe module).
+
+    Args:
+      isolate_path: path obj. Path to the isolate file.
+      base_dir: path obj. Dir that is the base of all paths in the isolate file.
+      os_type: str. The OS type to use when archiving the isolate file.
+          Eg: linux.
+      task_name: str. The isolated.gen.json file will be suffixed by this str.
+      extra_variables: dict of str to str. The extra vars to pass to isolate.
+          Eg: {'SLAVE_NUM': '1', 'MASTER': 'ChromiumPerfFYI'}
+      blacklist: list of regular expressions indicating which files/directories
+          not to archive.
+    """
+    self.m.file.makedirs('swarming tmp dir', self.swarming_temp_dir)
+    isolated_path = self.isolated_file_path(task_name)
+    isolate_args = [
+      '--isolate', isolate_path,
+      '--isolated', isolated_path,
+      '--config-variable', 'OS', os_type,
+    ]
+    if blacklist:
+      for b in blacklist:
+        isolate_args.extend(['--blacklist', b])
+    for k, v in extra_variables.iteritems():
+      isolate_args.extend(['--extra-variable', k, v])
+    isolated_gen_dict = {
+      'version': 1,
+      'dir': base_dir,
+      'args': isolate_args,
+    }
+    isolated_gen_json = self.swarming_temp_dir.join(
+        '%s.isolated.gen.json' % task_name)
+    self.m.file.write(
+        'Write %s.isolated.gen.json' % task_name,
+        isolated_gen_json,
+        self.m.json.dumps(isolated_gen_dict, indent=4),
+    )
+
+  def batcharchive(self, targets):
+    """Calls batcharchive on the skia.isolated.gen.json file.
+
+    Args:
+      targets: list of str. The suffixes of the isolated.gen.json files to
+               archive.
+
+    Returns:
+      list of tuples containing (task_name, swarming_hash).
+    """
+    return self.m.isolate.isolate_tests(
+        verbose=True,  # To avoid no output timeouts.
+        build_dir=self.swarming_temp_dir,
+        targets=targets).presentation.properties['swarm_hashes'].items()
+
+  def trigger_swarming_tasks(
+      self, swarm_hashes, dimensions, idempotent=False, store_output=True,
+      extra_args=None, expiration=None, hard_timeout=None, io_timeout=None,
+      cipd_packages=None):
+    """Triggers swarming tasks using swarm hashes.
+
+    Args:
+      swarm_hashes: list of str. List of swarm hashes from the isolate server.
+      dimensions: dict of str to str. The dimensions to run the task on.
+                  Eg: {'os': 'Ubuntu', 'gpu': '10de', 'pool': 'Skia'}
+      idempotent: bool. Whether or not to de-duplicate tasks.
+      store_output: bool. Whether task output should be stored.
+      extra_args: list of str. Extra arguments to pass to the task.
+      expiration: int. Task will expire if not picked up within this time.
+                  DEFAULT_TASK_EXPIRATION is used if this argument is None.
+      hard_timeout: int. Task will timeout if not completed within this time.
+                    DEFAULT_TASK_TIMEOUT is used if this argument is None.
+      io_timeout: int. Task will timeout if there is no output within this time.
+                  DEFAULT_IO_TIMEOUT is used if this argument is None.
+      cipd_packages: CIPD packages which these tasks depend on.
+
+    Returns:
+      List of swarming.SwarmingTask instances.
+    """
+    swarming_tasks = []
+    for task_name, swarm_hash in swarm_hashes:
+      swarming_task = self.m.swarming.task(
+          title=task_name,
+          cipd_packages=cipd_packages,
+          isolated_hash=swarm_hash)
+      if store_output:
+        swarming_task.task_output_dir = self.tasks_output_dir.join(task_name)
+      swarming_task.dimensions = dimensions
+      swarming_task.idempotent = idempotent
+      swarming_task.priority = 90
+      swarming_task.expiration = (
+          expiration if expiration else DEFAULT_TASK_EXPIRATION)
+      swarming_task.hard_timeout = (
+          hard_timeout if hard_timeout else DEFAULT_TASK_TIMEOUT)
+      swarming_task.io_timeout = (
+          io_timeout if io_timeout else DEFAULT_IO_TIMEOUT)
+      if extra_args:
+        swarming_task.extra_args = extra_args
+      revision = self.m.properties.get('revision')
+      if revision:
+        swarming_task.tags.add('revision:%s' % revision)
+      swarming_tasks.append(swarming_task)
+    step_results = self.m.swarming.trigger(swarming_tasks)
+    for step_result in step_results:
+      self._add_log_links(step_result, step_result.json.output)
+    return swarming_tasks
+
+  def collect_swarming_task(self, swarming_task):
+    """Collects the specified swarming task.
+
+    Args:
+      swarming_task: An instance of swarming.SwarmingTask.
+    """
+    try:
+      rv = self.m.swarming.collect_task(swarming_task)
+    except self.m.step.StepFailure as e:  # pragma: no cover
+      step_result = self.m.step.active_result
+      # Change step result to Infra failure if the swarming task failed due to
+      # expiration, time outs, bot crashes or task cancelations.
+      # Infra failures have step.EXCEPTION.
+      states_infra_failure = (
+          self.m.swarming.State.EXPIRED, self.m.swarming.State.TIMED_OUT,
+          self.m.swarming.State.BOT_DIED, self.m.swarming.State.CANCELED)
+      summary = step_result.swarming.summary
+      if summary['shards'][0]['state'] in states_infra_failure:
+        step_result.presentation.status = self.m.step.EXCEPTION
+        raise self.m.step.InfraFailure(e.name, step_result)
+      raise
+    finally:
+      step_result = self.m.step.active_result
+      # Add log link.
+      self._add_log_links(step_result, step_result.swarming.summary)
+    return rv
+
+  def _add_log_links(self, step_result, summary):
+    """Add Milo log links to all shards in the step."""
+    ids = []
+    shards = summary.get('shards')
+    if shards:
+      for shard in shards:
+        ids.append(shard['id'])
+    else:
+      for _, task in summary.get('tasks', {}).iteritems():
+        ids.append(task['task_id'])
+    for idx, task_id in enumerate(ids):
+      link = MILO_LOG_LINK % task_id
+      k = 'view steps on Milo'
+      if len(ids) > 1:  # pragma: nocover
+        k += ' (shard index %d, %d total)' % (idx, len(ids))
+      step_result.presentation.links[k] = link
+
diff --git a/infra/bots/recipe_modules/skia_swarming/examples/full.expected/test.json b/infra/bots/recipe_modules/skia_swarming/examples/full.expected/test.json
new file mode 100644
index 0000000..1ab18c6
--- /dev/null
+++ b/infra/bots/recipe_modules/skia_swarming/examples/full.expected/test.json
@@ -0,0 +1,938 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "abc123"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "lmydirimydirnmydirumydirxmydir6mydir4"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "mmydiramydircmydir6mydir4"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "wmydirimydirnmydir6mydir4"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "mydir",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"isolate_path\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-task.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--blacklist\", \n        \"*.pyc\", \n        \"--extra-variable\", \n        \"myvar\", \n        \"myval\"\n    ], \n    \"dir\": \"isolate_dir\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/task.isolated.gen.json"
+    ],
+    "name": "Write task.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/task-0.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/task-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/task-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/task-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/task-4.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"task-0\": \"[dummy hash for task-0]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"task-1\": \"[dummy hash for task-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"task-2\": \"[dummy hash for task-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"task-3\": \"[dummy hash for task-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"task-4\": \"[dummy hash for task-4]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"task-0\": \"[dummy hash for task-0]\", \"task-1\": \"[dummy hash for task-1]\", \"task-2\": \"[dummy hash for task-2]\", \"task-3\": \"[dummy hash for task-3]\", \"task-4\": \"[dummy hash for task-4]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "task-4/Linux/[dummy has",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "os",
+      "Linux",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "data:[dummy hash for task-4]",
+      "--tag",
+      "name:task-4",
+      "--tag",
+      "os:Linux",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:task-4",
+      "--isolated",
+      "[dummy hash for task-4]",
+      "--",
+      "--extra"
+    ],
+    "infra_step": true,
+    "name": "[trigger] task-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"task-4/Linux/[dummy has\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"task-4/Linux/[dummy has\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "task-2/Linux/[dummy has",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "os",
+      "Linux",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "data:[dummy hash for task-2]",
+      "--tag",
+      "name:task-2",
+      "--tag",
+      "os:Linux",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:task-2",
+      "--isolated",
+      "[dummy hash for task-2]",
+      "--",
+      "--extra"
+    ],
+    "infra_step": true,
+    "name": "[trigger] task-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"task-2/Linux/[dummy has\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"task-2/Linux/[dummy has\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "task-3/Linux/[dummy has",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "os",
+      "Linux",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "data:[dummy hash for task-3]",
+      "--tag",
+      "name:task-3",
+      "--tag",
+      "os:Linux",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:task-3",
+      "--isolated",
+      "[dummy hash for task-3]",
+      "--",
+      "--extra"
+    ],
+    "infra_step": true,
+    "name": "[trigger] task-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"task-3/Linux/[dummy has\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"task-3/Linux/[dummy has\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "task-0/Linux/[dummy has",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "os",
+      "Linux",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "data:[dummy hash for task-0]",
+      "--tag",
+      "name:task-0",
+      "--tag",
+      "os:Linux",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:task-0",
+      "--isolated",
+      "[dummy hash for task-0]",
+      "--",
+      "--extra"
+    ],
+    "infra_step": true,
+    "name": "[trigger] task-0",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"task-0/Linux/[dummy has\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"task-0/Linux/[dummy has\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "task-1/Linux/[dummy has",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "os",
+      "Linux",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "data:[dummy hash for task-1]",
+      "--tag",
+      "name:task-1",
+      "--tag",
+      "os:Linux",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:task-1",
+      "--isolated",
+      "[dummy hash for task-1]",
+      "--",
+      "--extra"
+    ],
+    "infra_step": true,
+    "name": "[trigger] task-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"task-1/Linux/[dummy has\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"task-1/Linux/[dummy has\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/task-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"task-4/Linux/[dummy has\", \"tasks\": {\"task-4/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "task-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/task-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"task-2/Linux/[dummy has\", \"tasks\": {\"task-2/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "task-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/task-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"task-3/Linux/[dummy has\", \"tasks\": {\"task-3/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "task-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/task-0",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"task-0/Linux/[dummy has\", \"tasks\": {\"task-0/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "task-0",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/task-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"task-1/Linux/[dummy has\", \"tasks\": {\"task-1/Linux/[dummy has\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "task-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Linux'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/skia_swarming/examples/full.py b/infra/bots/recipe_modules/skia_swarming/examples/full.py
new file mode 100644
index 0000000..bf928e2
--- /dev/null
+++ b/infra/bots/recipe_modules/skia_swarming/examples/full.py
@@ -0,0 +1,31 @@
+# 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 = [
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+  'skia_swarming',
+]
+
+
+def RunSteps(api):
+  api.skia_swarming.setup('mydir', swarming_rev='abc123')
+  api.skia_swarming.create_isolated_gen_json(
+      'isolate_path', 'isolate_dir', 'linux', 'task', {'myvar': 'myval'},
+      blacklist=['*.pyc'])
+  tasks_to_hashes = api.skia_swarming.batcharchive(targets=[
+      'task-%s' % num for num in range(5)])
+  tasks = api.skia_swarming.trigger_swarming_tasks(
+      tasks_to_hashes, dimensions={'os': 'Linux'}, extra_args=['--extra'])
+  for t in tasks:
+    api.skia_swarming.collect_swarming_task(t)
+
+
+def GenTests(api):
+  yield (
+      api.test('test') +
+      api.properties(revision='abc123')
+  )
diff --git a/infra/bots/recipe_modules/skpbench/__init__.py b/infra/bots/recipe_modules/skpbench/__init__.py
deleted file mode 100644
index f2a0bfe..0000000
--- a/infra/bots/recipe_modules/skpbench/__init__.py
+++ /dev/null
@@ -1,17 +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.
-
-DEPS = [
-  'build/file',
-  'core',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/python',
-  'recipe_engine/raw_io',
-  'recipe_engine/step',
-  'recipe_engine/time',
-  'run',
-  'flavor',
-  'vars',
-]
diff --git a/infra/bots/recipe_modules/skpbench/api.py b/infra/bots/recipe_modules/skpbench/api.py
deleted file mode 100644
index 5c2a696..0000000
--- a/infra/bots/recipe_modules/skpbench/api.py
+++ /dev/null
@@ -1,89 +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 skpbench.
-
-
-import calendar
-
-from recipe_engine import recipe_api
-
-
-def _run(api, title, *cmd, **kwargs):
-  with api.step.context({'cwd': api.vars.skia_dir}):
-    return api.run(api.step, title, cmd=list(cmd), **kwargs)
-
-
-def _adb(api, title, *cmd, **kwargs):
-  if 'infra_step' not in kwargs:
-    kwargs['infra_step'] = True
-  return _run(api, title, 'adb', *cmd, **kwargs)
-
-
-def skpbench_steps(api):
-  """benchmark Skia using skpbench."""
-  app = api.vars.skia_out.join(api.vars.configuration, 'skpbench')
-  _adb(api, 'push skpbench', 'push', app, api.vars.android_bin_dir)
-
-  skpbench_dir = api.vars.slave_dir.join('skia', 'tools', 'skpbench')
-  table = api.path.join(api.vars.swarming_out_dir, 'table')
-
-  config = 'gles,glesinst4'
-  if 'Vulkan' in api.vars.builder_name:
-    config = 'vk'
-
-  skpbench_args = [
-        api.path.join(api.vars.android_bin_dir, 'skpbench'),
-        api.path.join(api.vars.android_data_dir, 'skps'),
-        '--adb',
-        '--resultsfile', table,
-        '--config', config]
-
-  api.run(api.python, 'skpbench',
-      script=skpbench_dir.join('skpbench.py'),
-      args=skpbench_args)
-
-  skiaperf_args = [
-    table,
-    '--properties',
-    'gitHash',      api.vars.got_revision,
-    'build_number', api.vars.build_number,
-  ]
-
-  skiaperf_args.extend(['no_buildbot', 'True'])
-  skiaperf_args.extend(['swarming_bot_id', api.vars.swarming_bot_id])
-  skiaperf_args.extend(['swarming_task_id', api.vars.swarming_task_id])
-
-  now = api.time.utcnow()
-  ts = int(calendar.timegm(now.utctimetuple()))
-  api.file.makedirs('perf_dir', api.vars.perf_data_dir)
-  json_path = api.path.join(
-      api.vars.perf_data_dir,
-      'skpbench_%s_%d.json' % (api.vars.got_revision, ts))
-
-  skiaperf_args.extend([
-    '--outfile', json_path
-  ])
-
-  keys_blacklist = ['configuration', 'role', 'is_trybot']
-  skiaperf_args.append('--key')
-  for k in sorted(api.vars.builder_cfg.keys()):
-    if not k in keys_blacklist:
-      skiaperf_args.extend([k, api.vars.builder_cfg[k]])
-
-  api.run(api.python, 'Parse skpbench output into Perf json',
-      script=skpbench_dir.join('skiaperf.py'),
-      args=skiaperf_args)
-
-
-class SkpBenchApi(recipe_api.RecipeApi):
-  def run(self):
-    self.m.core.setup()
-    try:
-      self.m.flavor.install(skps=True)
-      skpbench_steps(self.m)
-    finally:
-      self.m.flavor.cleanup_steps()
-    self.m.run.check_failure()
diff --git a/infra/bots/recipe_modules/skpbench/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench.json b/infra/bots/recipe_modules/skpbench/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench.json
deleted file mode 100644
index 807b3d0..0000000
--- a/infra/bots/recipe_modules/skpbench/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench.json
+++ /dev/null
@@ -1,437 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/skpbench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push skpbench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/tools/skpbench/skpbench.py",
-      "/data/local/tmp/skpbench",
-      "/sdcard/revenge_of_the_skiabot/skps",
-      "--adb",
-      "--resultsfile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/table",
-      "--config",
-      "gles,glesinst4"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "skpbench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id",
-    "stdout": "/path/to/tmp/",
-    "~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",
-    "stdout": "/path/to/tmp/",
-    "~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",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/tools/skpbench/skiaperf.py",
-      "[CUSTOM_[SWARM_OUT_DIR]]/table",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "no_buildbot",
-      "True",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--outfile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench/data/skpbench_abc123_1337000001.json",
-      "--key",
-      "arch",
-      "arm64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "TegraX1",
-      "extra_config",
-      "GN_Android_Skpbench",
-      "model",
-      "PixelC",
-      "os",
-      "Android"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "Parse skpbench output into Perf json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/skpbench/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Vulkan_Skpbench.json b/infra/bots/recipe_modules/skpbench/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Vulkan_Skpbench.json
deleted file mode 100644
index 8974bc6..0000000
--- a/infra/bots/recipe_modules/skpbench/example.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Vulkan_Skpbench.json
+++ /dev/null
@@ -1,437 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/skpbench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push skpbench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/tools/skpbench/skpbench.py",
-      "/data/local/tmp/skpbench",
-      "/sdcard/revenge_of_the_skiabot/skps",
-      "--adb",
-      "--resultsfile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/table",
-      "--config",
-      "vk"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "skpbench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id",
-    "stdout": "/path/to/tmp/",
-    "~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",
-    "stdout": "/path/to/tmp/",
-    "~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",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Vulkan_Skpbench/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/tools/skpbench/skiaperf.py",
-      "[CUSTOM_[SWARM_OUT_DIR]]/table",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "no_buildbot",
-      "True",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--outfile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Vulkan_Skpbench/data/skpbench_abc123_1337000001.json",
-      "--key",
-      "arch",
-      "arm64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "TegraX1",
-      "extra_config",
-      "GN_Android_Vulkan_Skpbench",
-      "model",
-      "PixelC",
-      "os",
-      "Android"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "Parse skpbench output into Perf json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/skpbench/example.py b/infra/bots/recipe_modules/skpbench/example.py
deleted file mode 100644
index 4acf916..0000000
--- a/infra/bots/recipe_modules/skpbench/example.py
+++ /dev/null
@@ -1,56 +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.
-
-
-# Example recipe w/ coverage.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-  'skpbench',
-]
-
-
-TEST_BUILDERS = {
-  'client.skia': {
-    'skiabot-linux-swarm-000': [
-      'Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench',
-      ('Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-' +
-      'GN_Android_Vulkan_Skpbench'),
-    ],
-  },
-}
-
-
-def RunSteps(api):
-  api.skpbench.run()
-
-
-def GenTests(api):
-  for mastername, slaves in TEST_BUILDERS.iteritems():
-    for slavename, builders_by_slave in slaves.iteritems():
-      for builder in builders_by_slave:
-        test = (
-          api.test(builder) +
-          api.properties(buildername=builder,
-                         mastername=mastername,
-                         slavename=slavename,
-                         buildnumber=5,
-                         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',
-                                           'skp', 'VERSION'),
-          ) +
-          api.step_data('get swarming bot id',
-              stdout=api.raw_io.output('skia-bot-123')) +
-          api.step_data('get swarming task id',
-              stdout=api.raw_io.output('123456'))
-        )
-
-        yield test
diff --git a/infra/bots/recipe_modules/sktest/__init__.py b/infra/bots/recipe_modules/sktest/__init__.py
deleted file mode 100644
index c97fd57..0000000
--- a/infra/bots/recipe_modules/sktest/__init__.py
+++ /dev/null
@@ -1,18 +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.
-
-DEPS = [
-  'build/file',
-  'core',
-  'recipe_engine/json',
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/python',
-  'recipe_engine/raw_io',
-  'recipe_engine/step',
-  'flavor',
-  'run',
-  'vars',
-]
diff --git a/infra/bots/recipe_modules/sktest/api.py b/infra/bots/recipe_modules/sktest/api.py
deleted file mode 100644
index 70293a6..0000000
--- a/infra/bots/recipe_modules/sktest/api.py
+++ /dev/null
@@ -1,698 +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 test.
-
-
-from recipe_engine import recipe_api
-
-
-def dm_flags(bot):
-  args = []
-
-  # This enables non-deterministic random seeding of the GPU FP optimization
-  # test. Limit testing until we're sure it's not going to cause an
-  # avalanche of problems.
-  if 'Ubuntu' in bot or 'Win' in bot or 'Mac' in bot:
-    args.append('--randomProcessorTest')
-
-  # 32-bit desktop bots tend to run out of memory, because they have relatively
-  # far more cores than RAM (e.g. 32 cores, 3G RAM).  Hold them back a bit.
-  if '-x86-' in bot and not 'NexusPlayer' in bot:
-    args.extend('--threads 4'.split(' '))
-
-  # Avoid issues with dynamically exceeding resource cache limits.
-  if 'Test' in bot and 'DISCARDABLE' in bot:
-    args.extend('--threads 0'.split(' '))
-
-  # These are the canonical configs that we would ideally run on all bots. We
-  # may opt out or substitute some below for specific bots
-  configs = ['8888', 'srgb', 'pdf']
-  # Add in either gles or gl configs to the canonical set based on OS
-  sample_count = '8'
-  gl_prefix = 'gl'
-  if 'Android' in bot or 'iOS' in bot:
-    sample_count = '4'
-    # We want to test the OpenGL config not the GLES config on the Shield
-    if 'NVIDIA_Shield' not in bot:
-      gl_prefix = 'gles'
-  elif 'Intel' in bot:
-    sample_count = ''
-
-  configs.extend([gl_prefix, gl_prefix + 'dft', gl_prefix + 'srgb'])
-  if sample_count is not '':
-    configs.append(gl_prefix + 'msaa' + sample_count)
-
-  # The NP produces a long error stream when we run with MSAA. The Tegra3 just
-  # doesn't support it.
-  if ('NexusPlayer' in bot or
-      'Tegra3'      in bot or
-      # We aren't interested in fixing msaa bugs on iPad4.
-      'iPad4' in bot or
-      # skia:5792
-      'iHD530'       in bot or
-      'IntelIris540' in bot):
-    configs = [x for x in configs if 'msaa' not in x]
-
-  # The NP produces different images for dft on every run.
-  if 'NexusPlayer' in bot:
-    configs = [x for x in configs if 'dft' not in x]
-
-  # Runs out of memory on Android bots.  Everyone else seems fine.
-  if 'Android' in bot:
-    configs.remove('pdf')
-
-  if '-GCE-' in bot:
-    configs.extend(['565'])
-    configs.extend(['f16'])
-    configs.extend(['sp-8888', '2ndpic-8888'])   # Test niche uses of SkPicture.
-    configs.extend(['lite-8888'])                # Experimental display list.
-    configs.extend(['gbr-8888'])
-
-  if '-TSAN' not in bot and sample_count is not '':
-    if ('TegraK1'  in bot or
-        'TegraX1'  in bot or
-        'GTX550Ti' in bot or
-        'GTX660'   in bot or
-        'GT610'    in bot):
-      configs.append(gl_prefix + 'nvprdit' + sample_count)
-
-  # We want to test both the OpenGL config and the GLES config on Linux Intel:
-  # GL is used by Chrome, GLES is used by ChromeOS.
-  if 'Intel' in bot and 'Ubuntu' in bot:
-    configs.extend(['gles', 'glesdft', 'glessrgb'])
-
-  # NP is running out of RAM when we run all these modes.  skia:3255
-  if 'NexusPlayer' not in bot:
-    configs.extend(mode + '-8888' for mode in
-                   ['serialize', 'tiles_rt', 'pic'])
-
-  # Test instanced rendering on a limited number of platforms
-  if 'Nexus6' in bot:
-    configs.append(gl_prefix + 'inst') # inst msaa isn't working yet on Adreno.
-  elif 'NVIDIA_Shield' in bot or 'PixelC' in bot:
-    # Multisampled instanced configs use nvpr so we substitute inst msaa
-    # configs for nvpr msaa configs.
-    old = gl_prefix + 'nvpr'
-    new = gl_prefix + 'inst'
-    configs = [x.replace(old, new) for x in configs]
-    # We also test non-msaa instanced.
-    configs.append(new)
-  elif 'MacMini6.2' in bot and sample_count is not '':
-    configs.extend([gl_prefix + 'inst', gl_prefix + 'inst' + sample_count])
-
-  # CommandBuffer bot *only* runs the command_buffer config.
-  if 'CommandBuffer' in bot:
-    configs = ['commandbuffer']
-
-  # ANGLE bot *only* runs the angle configs
-  if 'ANGLE' in bot:
-    configs = ['angle_d3d11_es2',
-               'angle_d3d9_es2',
-               'angle_gl_es2']
-    if sample_count is not '':
-      configs.append('angle_d3d11_es2_msaa' + sample_count)
-
-  # Vulkan bot *only* runs the vk config.
-  if 'Vulkan' in bot:
-    configs = ['vk']
-
-  args.append('--config')
-  args.extend(configs)
-
-  # Run tests, gms, and image decoding tests everywhere.
-  args.extend('--src tests gm image colorImage svg'.split(' '))
-  if 'Vulkan' in bot and 'NexusPlayer' in bot:
-    args.remove('svg')
-    args.remove('image')
-
-  blacklisted = []
-  def blacklist(quad):
-    config, src, options, name = quad.split(' ') if type(quad) is str else quad
-    if config == '_' or config in configs:
-      blacklisted.extend([config, src, options, name])
-
-  # TODO: ???
-  blacklist('f16 _ _ dstreadshuffle')
-  blacklist('glsrgb image _ _')
-  blacklist('glessrgb image _ _')
-
-  # Decoder tests are now performing gamma correct decodes.  This means
-  # that, when viewing the results, we need to perform a gamma correct
-  # encode to PNG.  Therefore, we run the image tests in srgb mode instead
-  # of 8888.
-  blacklist('8888 image _ _')
-
-  # Not any point to running these.
-  blacklist('gbr-8888 image _ _')
-  blacklist('gbr-8888 colorImage _ _')
-
-  if 'Valgrind' in bot:
-    # These take 18+ hours to run.
-    blacklist('pdf gm _ fontmgr_iter')
-    blacklist('pdf _ _ PANO_20121023_214540.jpg')
-    blacklist('pdf skp _ worldjournal')
-    blacklist('pdf skp _ desk_baidu.skp')
-    blacklist('pdf skp _ desk_wikipedia.skp')
-    blacklist('_ svg _ _')
-
-  if 'iOS' in bot:
-    blacklist(gl_prefix + ' skp _ _')
-
-  if 'Mac' in bot or 'iOS' in bot:
-    # CG fails on questionable bmps
-    blacklist('_ image gen_platf rgba32abf.bmp')
-    blacklist('_ image gen_platf rgb24prof.bmp')
-    blacklist('_ image gen_platf rgb24lprof.bmp')
-    blacklist('_ image gen_platf 8bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf 4bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf 32bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf 24bpp-pixeldata-cropped.bmp')
-
-    # CG has unpredictable behavior on this questionable gif
-    # It's probably using uninitialized memory
-    blacklist('_ image gen_platf frame_larger_than_image.gif')
-
-    # CG has unpredictable behavior on incomplete pngs
-    # skbug.com/5774
-    blacklist('_ image gen_platf inc0.png')
-    blacklist('_ image gen_platf inc1.png')
-    blacklist('_ image gen_platf inc2.png')
-    blacklist('_ image gen_platf inc3.png')
-    blacklist('_ image gen_platf inc4.png')
-    blacklist('_ image gen_platf inc5.png')
-    blacklist('_ image gen_platf inc6.png')
-    blacklist('_ image gen_platf inc7.png')
-    blacklist('_ image gen_platf inc8.png')
-    blacklist('_ image gen_platf inc9.png')
-    blacklist('_ image gen_platf inc10.png')
-    blacklist('_ image gen_platf inc11.png')
-    blacklist('_ image gen_platf inc12.png')
-    blacklist('_ image gen_platf inc13.png')
-    blacklist('_ image gen_platf inc14.png')
-
-  # WIC fails on questionable bmps and arithmetic jpegs
-  if 'Win' in bot:
-    blacklist('_ image gen_platf rle8-height-negative.bmp')
-    blacklist('_ image gen_platf rle4-height-negative.bmp')
-    blacklist('_ image gen_platf pal8os2v2.bmp')
-    blacklist('_ image gen_platf pal8os2v2-16.bmp')
-    blacklist('_ image gen_platf rgba32abf.bmp')
-    blacklist('_ image gen_platf rgb24prof.bmp')
-    blacklist('_ image gen_platf rgb24lprof.bmp')
-    blacklist('_ image gen_platf 8bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf 4bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf 32bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf 24bpp-pixeldata-cropped.bmp')
-    blacklist('_ image gen_platf testimgari.jpg')
-    if 'x86_64' in bot and 'CPU' in bot:
-      # This GM triggers a SkSmallAllocator assert.
-      blacklist('_ gm _ composeshader_bitmap')
-
-  if 'Android' in bot or 'iOS' in bot:
-    # This test crashes the N9 (perhaps because of large malloc/frees). It also
-    # is fairly slow and not platform-specific. So we just disable it on all of
-    # Android and iOS. skia:5438
-    blacklist('_ test _ GrShape')
-
-  if 'Win8' in bot:
-    # bungeman: "Doesn't work on Windows anyway, produces unstable GMs with
-    # 'Unexpected error' from DirectWrite"
-    blacklist('_ gm _ fontscalerdistortable')
-    # skia:5636
-    blacklist('_ svg _ Nebraska-StateSeal.svg')
-
-  # skia:4095
-  bad_serialize_gms = ['bleed_image',
-                       'c_gms',
-                       'colortype',
-                       'colortype_xfermodes',
-                       'drawfilter',
-                       'fontmgr_bounds_0.75_0',
-                       'fontmgr_bounds_1_-0.25',
-                       'fontmgr_bounds',
-                       'fontmgr_match',
-                       'fontmgr_iter',
-                       'imagemasksubset']
-
-  # skia:5589
-  bad_serialize_gms.extend(['bitmapfilters',
-                            'bitmapshaders',
-                            'bleed',
-                            'bleed_alpha_bmp',
-                            'bleed_alpha_bmp_shader',
-                            'convex_poly_clip',
-                            'extractalpha',
-                            'filterbitmap_checkerboard_32_32_g8',
-                            'filterbitmap_image_mandrill_64',
-                            'shadows',
-                            'simpleaaclip_aaclip'])
-  # skia:5595
-  bad_serialize_gms.extend(['composeshader_bitmap',
-                            'scaled_tilemodes_npot',
-                            'scaled_tilemodes'])
-
-  # skia:5778
-  bad_serialize_gms.append('typefacerendering_pfaMac')
-  # skia:5942
-  bad_serialize_gms.append('parsedpaths')
-
-  # these use a custom image generator which doesn't serialize
-  bad_serialize_gms.append('ImageGeneratorExternal_rect')
-  bad_serialize_gms.append('ImageGeneratorExternal_shader')
-
-  # skia:6189
-  bad_serialize_gms.append('shadow_utils')
-
-  for test in bad_serialize_gms:
-    blacklist(['serialize-8888', 'gm', '_', test])
-
-  if 'Mac' not in bot:
-    for test in ['bleed_alpha_image', 'bleed_alpha_image_shader']:
-      blacklist(['serialize-8888', 'gm', '_', test])
-  # It looks like we skip these only for out-of-memory concerns.
-  if 'Win' in bot or 'Android' in bot:
-    for test in ['verylargebitmap', 'verylarge_picture_image']:
-      blacklist(['serialize-8888', 'gm', '_', test])
-
-  # skia:4769
-  for test in ['drawfilter']:
-    blacklist([    'sp-8888', 'gm', '_', test])
-    blacklist([   'pic-8888', 'gm', '_', test])
-    blacklist(['2ndpic-8888', 'gm', '_', test])
-    blacklist([  'lite-8888', 'gm', '_', test])
-  # skia:4703
-  for test in ['image-cacherator-from-picture',
-               'image-cacherator-from-raster',
-               'image-cacherator-from-ctable']:
-    blacklist([       'sp-8888', 'gm', '_', test])
-    blacklist([      'pic-8888', 'gm', '_', test])
-    blacklist([   '2ndpic-8888', 'gm', '_', test])
-    blacklist(['serialize-8888', 'gm', '_', test])
-
-  # GM that requires raster-backed canvas
-  for test in ['gamut', 'complexclip4_bw', 'complexclip4_aa']:
-    blacklist([       'sp-8888', 'gm', '_', test])
-    blacklist([      'pic-8888', 'gm', '_', test])
-    blacklist([     'lite-8888', 'gm', '_', test])
-    blacklist([   '2ndpic-8888', 'gm', '_', test])
-    blacklist(['serialize-8888', 'gm', '_', test])
-
-  # 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"]
-
-  # skbug.com/4888
-  # Blacklist RAW images (and a few large PNGs) on GPU bots
-  # until we can resolve failures.
-  # Also blacklisted on 32-bit Win2k8 for F16 OOM errors.
-  if 'GPU' in bot or ('Win2k8' in bot and 'x86-' in bot):
-    blacklist('_ image _ interlaced1.png')
-    blacklist('_ image _ interlaced2.png')
-    blacklist('_ image _ interlaced3.png')
-    for raw_ext in r:
-      blacklist('_ image _ .%s' % raw_ext)
-
-  # Large image that overwhelms older Mac bots
-  if 'MacMini4.1-GPU' in bot:
-    blacklist('_ image _ abnormal.wbmp')
-    blacklist([gl_prefix + 'msaa' + sample_count, 'gm', '_', 'blurcircles'])
-
-  if 'IntelHD405' in bot and 'Ubuntu16' in bot:
-    # skia:6331
-    blacklist(['glmsaa8',   'image', 'gen_codec_gpu', 'abnormal.wbmp'])
-    blacklist(['glesmsaa4', 'image', 'gen_codec_gpu', 'abnormal.wbmp'])
-
-  if 'Nexus5' in bot:
-    # skia:5876
-    blacklist(['_', 'gm', '_', 'encode-platform'])
-
-  if 'AndroidOne-GPU' in bot:  # skia:4697, skia:4704, skia:4694, skia:4705
-    blacklist(['_',            'gm', '_', 'bigblurs'])
-    blacklist(['_',            'gm', '_', 'bleed'])
-    blacklist(['_',            'gm', '_', 'bleed_alpha_bmp'])
-    blacklist(['_',            'gm', '_', 'bleed_alpha_bmp_shader'])
-    blacklist(['_',            'gm', '_', 'bleed_alpha_image'])
-    blacklist(['_',            'gm', '_', 'bleed_alpha_image_shader'])
-    blacklist(['_',            'gm', '_', 'bleed_image'])
-    blacklist(['_',            'gm', '_', 'dropshadowimagefilter'])
-    blacklist(['_',            'gm', '_', 'filterfastbounds'])
-    blacklist([gl_prefix,      'gm', '_', 'imageblurtiled'])
-    blacklist(['_',            'gm', '_', 'imagefiltersclipped'])
-    blacklist(['_',            'gm', '_', 'imagefiltersscaled'])
-    blacklist(['_',            'gm', '_', 'imageresizetiled'])
-    blacklist(['_',            'gm', '_', 'matrixconvolution'])
-    blacklist(['_',            'gm', '_', 'strokedlines'])
-    if sample_count is not '':
-      gl_msaa_config = gl_prefix + 'msaa' + sample_count
-      blacklist([gl_msaa_config, 'gm', '_', 'imageblurtiled'])
-      blacklist([gl_msaa_config, 'gm', '_', 'imagefiltersbase'])
-
-  match = []
-  if 'Valgrind' in bot: # skia:3021
-    match.append('~Threaded')
-
-  if 'AndroidOne' in bot:  # skia:4711
-    match.append('~WritePixels')
-
-  if 'NexusPlayer' in bot:
-    match.append('~ResourceCache')
-
-  if 'Nexus10' in bot:
-    match.append('~CopySurface') # skia:5509
-    match.append('~SRGBReadWritePixels') # skia:6097
-
-  if 'GalaxyJ5' in bot:
-    match.append('~SRGBReadWritePixels') # skia:6097
-
-  if 'GalaxyS6' in bot:
-    match.append('~SpecialImage') # skia:6338
-
-  if 'GalaxyS7_G930A' in bot:
-    match.append('~WritePixels') # skia:6427
-
-  if 'ANGLE' in bot and 'Debug' in bot:
-    match.append('~GLPrograms') # skia:4717
-
-  if 'MSAN' in bot:
-    match.extend(['~Once', '~Shared'])  # Not sure what's up with these tests.
-
-  if 'TSAN' in bot:
-    match.extend(['~ReadWriteAlpha'])   # Flaky on TSAN-covered on nvidia bots.
-    match.extend(['~RGBA4444TextureTest',  # Flakier than they are important.
-                  '~RGB565TextureTest'])
-
-  if 'Vulkan' in bot and 'Adreno' in bot:
-    # skia:5777
-    match.extend(['~XfermodeImageFilterCroppedInput',
-                  '~GrTextureStripAtlasFlush',
-                  '~CopySurface'])
-
-  if 'Vulkan' in bot and 'NexusPlayer' in bot:
-    match.extend(['~hardstop_gradient', # skia:6037
-                  '~gradients_dup_color_stops',  # skia:6037
-                  '~gradients_no_texture$', # skia:6132
-                  '~tilemodes', # skia:6132
-                  '~shadertext$', # skia:6132
-                  '~bitmapfilters', # skia:6132
-                  '~GrContextFactory_abandon']) #skia:6209
-
-  if 'Vulkan' in bot and 'GTX1070' in bot and 'Win' in bot:
-    # skia:6092
-    match.append('~GPUMemorySize')
-
-  if 'Vulkan' in bot and 'IntelIris540' in bot and 'Ubuntu' in bot:
-    match.extend(['~VkHeapTests']) # skia:6245
-
-  if 'Vulkan' in bot and 'IntelIris540' in bot and 'Win' in bot:
-    # skia:6398
-    blacklist(['vk', 'gm', '_', 'aarectmodes'])
-    blacklist(['vk', 'gm', '_', 'aaxfermodes'])
-    blacklist(['vk', 'gm', '_', 'arithmode'])
-    blacklist(['vk', 'gm', '_', 'composeshader_bitmap'])
-    blacklist(['vk', 'gm', '_', 'composeshader_bitmap2'])
-    blacklist(['vk', 'gm', '_', 'dftextCOLR'])
-    blacklist(['vk', 'gm', '_', 'drawregionmodes'])
-    blacklist(['vk', 'gm', '_', 'filterfastbounds'])
-    blacklist(['vk', 'gm', '_', 'fontcache'])
-    blacklist(['vk', 'gm', '_', 'fontmgr_iterWin10'])
-    blacklist(['vk', 'gm', '_', 'fontmgr_iter_factoryWin10'])
-    blacklist(['vk', 'gm', '_', 'fontmgr_matchWin10'])
-    blacklist(['vk', 'gm', '_', 'fontscalerWin'])
-    blacklist(['vk', 'gm', '_', 'fontscalerdistortable'])
-    blacklist(['vk', 'gm', '_', 'gammagradienttext'])
-    blacklist(['vk', 'gm', '_', 'gammatextWin'])
-    blacklist(['vk', 'gm', '_', 'gradtext'])
-    blacklist(['vk', 'gm', '_', 'hairmodes'])
-    blacklist(['vk', 'gm', '_', 'imagefilters_xfermodes'])
-    blacklist(['vk', 'gm', '_', 'imagefiltersclipped'])
-    blacklist(['vk', 'gm', '_', 'imagefiltersgraph'])
-    blacklist(['vk', 'gm', '_', 'imagefiltersscaled'])
-    blacklist(['vk', 'gm', '_', 'imagefiltersstroked'])
-    blacklist(['vk', 'gm', '_', 'imagefilterstransformed'])
-    blacklist(['vk', 'gm', '_', 'imageresizetiled'])
-    blacklist(['vk', 'gm', '_', 'lcdblendmodes'])
-    blacklist(['vk', 'gm', '_', 'lcdoverlap'])
-    blacklist(['vk', 'gm', '_', 'lcdtextWin'])
-    blacklist(['vk', 'gm', '_', 'lcdtextsize'])
-    blacklist(['vk', 'gm', '_', 'matriximagefilter'])
-    blacklist(['vk', 'gm', '_', 'mixedtextblobsCOLR'])
-    blacklist(['vk', 'gm', '_', 'pictureimagefilter'])
-    blacklist(['vk', 'gm', '_', 'resizeimagefilter'])
-    blacklist(['vk', 'gm', '_', 'rotate_imagefilter'])
-    blacklist(['vk', 'gm', '_', 'savelayer_lcdtext'])
-    blacklist(['vk', 'gm', '_', 'srcmode'])
-    blacklist(['vk', 'gm', '_', 'surfaceprops'])
-    blacklist(['vk', 'gm', '_', 'textblobgeometrychange'])
-    blacklist(['vk', 'gm', '_', 'textbloblooper'])
-    blacklist(['vk', 'gm', '_', 'textblobmixedsizes'])
-    blacklist(['vk', 'gm', '_', 'textblobmixedsizes_df'])
-    blacklist(['vk', 'gm', '_', 'textblobrandomfont'])
-    blacklist(['vk', 'gm', '_', 'textfilter_color'])
-    blacklist(['vk', 'gm', '_', 'textfilter_image'])
-    blacklist(['vk', 'gm', '_', 'typefacerenderingWin'])
-    blacklist(['vk', 'gm', '_', 'varied_text_clipped_lcd'])
-    blacklist(['vk', 'gm', '_', 'varied_text_ignorable_clip_lcd'])
-    blacklist(['vk', 'gm', '_', 'xfermodeimagefilter'])
-    match.append('~ApplyGamma')
-    match.append('~ComposedImageFilterBounds_Gpu')
-    match.append('~ImageFilterFailAffectsTransparentBlack_Gpu')
-    match.append('~ImageFilterZeroBlurSigma_Gpu')
-    match.append('~ImageNewShader_GPU')
-    match.append('~NewTextureFromPixmap')
-    match.append('~ReadPixels_Gpu')
-    match.append('~ReadPixels_Texture')
-    match.append('~ReadWriteAlpha')
-    match.append('~SRGBReadWritePixels')
-    match.append('~SpecialImage_DeferredGpu')
-    match.append('~SpecialImage_Gpu')
-    match.append('~WritePixels_Gpu')
-    match.append('~XfermodeImageFilterCroppedInput_Gpu')
-
-  if 'IntelIris540' in bot and 'ANGLE' in bot:
-    match.append('~IntTexture') # skia:6086
-    blacklist(['_', 'gm', '_', 'discard']) # skia:6141
-    # skia:6103
-    for config in ['angle_d3d9_es2', 'angle_d3d11_es2', 'angle_gl_es2']:
-      blacklist([config, 'gm', '_', 'multipicturedraw_invpathclip_simple'])
-      blacklist([config, 'gm', '_', 'multipicturedraw_noclip_simple'])
-      blacklist([config, 'gm', '_', 'multipicturedraw_pathclip_simple'])
-      blacklist([config, 'gm', '_', 'multipicturedraw_rectclip_simple'])
-      blacklist([config, 'gm', '_', 'multipicturedraw_rrectclip_simple'])
-
-  if 'IntelBayTrail' in bot and 'Ubuntu' in bot:
-    match.append('~ImageStorageLoad') # skia:6358
-
-  if 'Vivante' in bot:
-    # This causes the bot to spin for >3.5 hours.
-    blacklist(['_', 'gm', '_', 'scaled_tilemodes_npot'])
-
-  if blacklisted:
-    args.append('--blacklist')
-    args.extend(blacklisted)
-
-  if match:
-    args.append('--match')
-    args.extend(match)
-
-  # These bots run out of memory running RAW codec tests. Do not run them in
-  # parallel
-  if ('NexusPlayer' in bot or 'Nexus5' in bot or 'Nexus9' in bot
-      or 'Win8-MSVC-ShuttleB' in bot):
-    args.append('--noRAW_threading')
-
-  return args
-
-
-def key_params(api):
-  """Build a unique key from the builder name (as a list).
-
-  E.g.  arch x86 gpu GeForce320M mode MacMini4.1 os Mac10.6
-  """
-  # Don't bother to include role, which is always Test.
-  # TryBots are uploaded elsewhere so they can use the same key.
-  blacklist = ['role', 'is_trybot']
-
-  flat = []
-  for k in sorted(api.vars.builder_cfg.keys()):
-    if k not in blacklist:
-      flat.append(k)
-      flat.append(api.vars.builder_cfg[k])
-  return flat
-
-
-def test_steps(api):
-  """Run the DM test."""
-  use_hash_file = False
-  if api.vars.upload_dm_results:
-    # This must run before we write anything into
-    # api.flavor.device_dirs.dm_dir or we may end up deleting our
-    # output on machines where they're the same.
-    api.flavor.create_clean_host_dir(api.vars.dm_dir)
-    host_dm_dir = str(api.vars.dm_dir)
-    device_dm_dir = str(api.flavor.device_dirs.dm_dir)
-    if host_dm_dir != device_dm_dir:
-      api.flavor.create_clean_device_dir(device_dm_dir)
-
-    # Obtain the list of already-generated hashes.
-    hash_filename = 'uninteresting_hashes.txt'
-
-    # Ensure that the tmp_dir exists.
-    api.run.run_once(api.file.makedirs,
-                           'tmp_dir',
-                           api.vars.tmp_dir,
-                           infra_step=True)
-
-    host_hashes_file = api.vars.tmp_dir.join(hash_filename)
-    hashes_file = api.flavor.device_path_join(
-        api.flavor.device_dirs.tmp_dir, hash_filename)
-    api.run(
-        api.python.inline,
-        'get uninteresting hashes',
-        program="""
-        import contextlib
-        import math
-        import socket
-        import sys
-        import time
-        import urllib2
-
-        HASHES_URL = 'https://gold.skia.org/_/hashes'
-        RETRIES = 5
-        TIMEOUT = 60
-        WAIT_BASE = 15
-
-        socket.setdefaulttimeout(TIMEOUT)
-        for retry in range(RETRIES):
-          try:
-            with contextlib.closing(
-                urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:
-              hashes = w.read()
-              with open(sys.argv[1], 'w') as f:
-                f.write(hashes)
-                break
-          except Exception as e:
-            print 'Failed to get uninteresting hashes from %s:' % HASHES_URL
-            print e
-            if retry == RETRIES:
-              raise
-            waittime = WAIT_BASE * math.pow(2, retry)
-            print 'Retry in %d seconds.' % waittime
-            time.sleep(waittime)
-        """,
-        args=[host_hashes_file],
-        abort_on_failure=False,
-        fail_build_on_failure=False,
-        infra_step=True)
-
-    if api.path.exists(host_hashes_file):
-      api.flavor.copy_file_to_device(host_hashes_file, hashes_file)
-      use_hash_file = True
-
-  # Run DM.
-  properties = [
-    'gitHash',      api.vars.got_revision,
-    'master',       api.vars.master_name,
-    'builder',      api.vars.builder_name,
-    'build_number', api.vars.build_number,
-  ]
-  if api.vars.is_trybot:
-    properties.extend([
-      'issue',         api.vars.issue,
-      'patchset',      api.vars.patchset,
-      'patch_storage', api.vars.patch_storage,
-    ])
-  if api.vars.no_buildbot:
-    properties.extend(['no_buildbot', 'True'])
-    properties.extend(['swarming_bot_id', api.vars.swarming_bot_id])
-    properties.extend(['swarming_task_id', api.vars.swarming_task_id])
-
-  args = [
-    'dm',
-    '--undefok',   # This helps branches that may not know new flags.
-    '--resourcePath', api.flavor.device_dirs.resource_dir,
-    '--skps', api.flavor.device_dirs.skp_dir,
-    '--images', api.flavor.device_path_join(
-        api.flavor.device_dirs.images_dir, 'dm'),
-    '--colorImages', api.flavor.device_path_join(
-        api.flavor.device_dirs.images_dir, 'colorspace'),
-    '--nameByHash',
-    '--properties'
-  ] + properties
-
-  args.extend(['--svgs', api.flavor.device_dirs.svg_dir])
-
-  args.append('--key')
-  args.extend(key_params(api))
-  if use_hash_file:
-    args.extend(['--uninterestingHashesFile', hashes_file])
-  if api.vars.upload_dm_results:
-    args.extend(['--writePath', api.flavor.device_dirs.dm_dir])
-
-  skip_flag = None
-  if api.vars.builder_cfg.get('cpu_or_gpu') == 'CPU':
-    skip_flag = '--nogpu'
-  elif api.vars.builder_cfg.get('cpu_or_gpu') == 'GPU':
-    skip_flag = '--nocpu'
-  if skip_flag:
-    args.append(skip_flag)
-  args.extend(dm_flags(api.vars.builder_name))
-
-  env = api.step.get_from_context('env', {})
-  if 'Ubuntu16' in api.vars.builder_name:
-    # The vulkan in this asset name simply means that the graphics driver
-    # supports Vulkan. It is also the driver used for GL code.
-    dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_release')
-    if 'Debug' in api.vars.builder_name:
-      dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_debug')
-
-    if 'Vulkan' in api.vars.builder_name:
-      sdk_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'bin')
-      lib_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'lib')
-      env.update({
-        'PATH':'%%(PATH)s:%s' % sdk_path,
-        'LD_LIBRARY_PATH': '%s:%s' % (lib_path, dri_path),
-        'LIBGL_DRIVERS_PATH': dri_path,
-        'VK_ICD_FILENAMES':'%s' % dri_path.join('intel_icd.x86_64.json'),
-      })
-    else:
-      # Even the non-vulkan NUC jobs could benefit from the newer drivers.
-      env.update({
-        'LD_LIBRARY_PATH': dri_path,
-        'LIBGL_DRIVERS_PATH': dri_path,
-      })
-
-  # See skia:2789.
-  if '_AbandonGpuContext' in api.vars.builder_cfg.get('extra_config', ''):
-    args.append('--abandonGpuContext')
-  if '_PreAbandonGpuContext' in api.vars.builder_cfg.get('extra_config', ''):
-    args.append('--preAbandonGpuContext')
-
-  with api.step.context({'env': env}):
-    api.run(api.flavor.step, 'dm', cmd=args, abort_on_failure=False)
-
-  if api.vars.upload_dm_results:
-    # Copy images and JSON to host machine if needed.
-    api.flavor.copy_directory_contents_to_host(
-        api.flavor.device_dirs.dm_dir, api.vars.dm_dir)
-
-
-class TestApi(recipe_api.RecipeApi):
-  def run(self):
-    self.m.core.setup()
-    env = self.m.step.get_from_context('env', {})
-    if 'iOS' in self.m.vars.builder_name:
-      env['IOS_BUNDLE_ID'] = 'com.google.dm'
-    with self.m.step.context({'env': env}):
-      try:
-        self.m.flavor.install_everything()
-        test_steps(self.m)
-      finally:
-        self.m.flavor.cleanup_steps()
-      self.m.run.check_failure()
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-AndroidOne-CPU-MT6582-arm-Release-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-AndroidOne-CPU-MT6582-arm-Release-GN_Android.json
deleted file mode 100644
index 9309332..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-AndroidOne-CPU-MT6582-arm-Release-GN_Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-AndroidOne-CPU-MT6582-arm-Release-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu CPU cpu_or_gpu_value MT6582 extra_config GN_Android model AndroidOne os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nogpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa --match ~WritePixels; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-GN_Android.json
deleted file mode 100644
index 501f409..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-GN_Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value Mali400MP2 extra_config GN_Android model AndroidOne os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 _ gm _ bigblurs _ gm _ bleed _ gm _ bleed_alpha_bmp _ gm _ bleed_alpha_bmp_shader _ gm _ bleed_alpha_image _ gm _ bleed_alpha_image_shader _ gm _ bleed_image _ gm _ dropshadowimagefilter _ gm _ filterfastbounds gles gm _ imageblurtiled _ gm _ imagefiltersclipped _ gm _ imagefiltersscaled _ gm _ imageresizetiled _ gm _ matrixconvolution _ gm _ strokedlines glesmsaa4 gm _ imageblurtiled glesmsaa4 gm _ imagefiltersbase --match ~WritePixels; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android.json
deleted file mode 100644
index 9d981fe..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value Adreno306 extra_config Android model GalaxyJ5 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~SRGBReadWritePixels; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android.json
deleted file mode 100644
index 53702f4..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value MaliT760 extra_config Android model GalaxyS6 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~SpecialImage; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android.json
deleted file mode 100644
index 4355e6e..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Adreno530 extra_config Android model GalaxyS7_G930A os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~WritePixels; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android.json
deleted file mode 100644
index c585fdb..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Vivante extra_config Android model GalaxyTab3 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 _ gm _ scaled_tilemodes_npot; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android.json
deleted file mode 100644
index 550e9c1..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value TegraX1 extra_config GN_Android model NVIDIA_Shield os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gl gldft glsrgb glmsaa4 glinstdit4 serialize-8888 tiles_rt-8888 pic-8888 glinst --src tests gm image colorImage svg --blacklist glsrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-GN_Android.json
deleted file mode 100644
index 8394408..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-GN_Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value MaliT604 extra_config GN_Android model Nexus10 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~CopySurface ~SRGBReadWritePixels; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android.json
deleted file mode 100644
index 09bf6e5..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value Adreno330 extra_config Android model Nexus5 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 _ gm _ encode-platform --noRAW_threading; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus6-GPU-Adreno420-arm-Debug-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus6-GPU-Adreno420-arm-Debug-GN_Android.json
deleted file mode 100644
index d822aa8..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus6-GPU-Adreno420-arm-Debug-GN_Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus6-GPU-Adreno420-arm-Debug-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Adreno420 extra_config GN_Android model Nexus6 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 glesinst --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-GN_Android_Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-GN_Android_Vulkan.json
deleted file mode 100644
index 72a1a0b..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-GN_Android_Vulkan.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-GN_Android_Vulkan build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Adreno430 extra_config GN_Android_Vulkan model Nexus6p os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config vk --src tests gm image colorImage svg --blacklist _ test _ GrShape _ 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 --match ~XfermodeImageFilterCroppedInput ~GrTextureStripAtlasFlush ~CopySurface; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android.json
deleted file mode 100644
index 40f698e..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android.json
+++ /dev/null
@@ -1,824 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android build_number 5 --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 GN_Android model Nexus7 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-GN_Android.json
deleted file mode 100644
index 348065a..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-GN_Android.json
+++ /dev/null
@@ -1,887 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch x86 compiler Clang configuration Release cpu_or_gpu CPU cpu_or_gpu_value SSE4 extra_config GN_Android model NexusPlayer os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nogpu --config 8888 srgb gles glessrgb --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape --match ~ResourceCache --noRAW_threading; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan.json
deleted file mode 100644
index 76af59e..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan.json
+++ /dev/null
@@ -1,887 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-GN_Android_Vulkan build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch x86 compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value PowerVR extra_config GN_Android_Vulkan model NexusPlayer os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config vk --src tests gm colorImage --blacklist _ test _ GrShape _ 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 --match ~ResourceCache ~hardstop_gradient ~gradients_dup_color_stops ~gradients_no_texture$ ~tilemodes ~shadertext$ ~bitmapfilters ~GrContextFactory_abandon --noRAW_threading; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-PixelC-GPU-TegraX1-arm64-Debug-GN_Android.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-PixelC-GPU-TegraX1-arm64-Debug-GN_Android.json
deleted file mode 100644
index 6a4484f..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Android-Clang-PixelC-GPU-TegraX1-arm64-Debug-GN_Android.json
+++ /dev/null
@@ -1,887 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-PixelC-GPU-TegraX1-arm64-Debug-GN_Android build_number 5 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value TegraX1 extra_config GN_Android model PixelC os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 glesinstdit4 serialize-8888 tiles_rt-8888 pic-8888 glesinst --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug.json
deleted file mode 100644
index 2e39e75..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug.json
+++ /dev/null
@@ -1,634 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GeForce320M",
-      "model",
-      "MacMini4.1",
-      "os",
-      "Mac",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "frame_larger_than_image.gif",
-      "_",
-      "image",
-      "gen_platf",
-      "inc0.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc1.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc2.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc3.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc4.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc5.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc6.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc7.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc8.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc9.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc10.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc11.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc12.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc13.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc14.png",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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",
-      "_",
-      "image",
-      "_",
-      "abnormal.wbmp",
-      "glmsaa8",
-      "gm",
-      "_",
-      "blurcircles"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug.json
deleted file mode 100644
index 42c97a1..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug.json
+++ /dev/null
@@ -1,536 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX",
-      "model",
-      "MacMini6.2",
-      "os",
-      "Mac",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "glinst",
-      "glinst8",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "frame_larger_than_image.gif",
-      "_",
-      "image",
-      "gen_platf",
-      "inc0.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc1.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc2.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc3.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc4.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc5.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc6.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc7.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc8.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc9.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc10.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc11.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc12.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc13.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc14.png",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer.json
deleted file mode 100644
index 9a4890f..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer.json
+++ /dev/null
@@ -1,431 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "HD4000",
-      "extra_config",
-      "CommandBuffer",
-      "model",
-      "MacMini6.2",
-      "os",
-      "Mac",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "commandbuffer",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "frame_larger_than_image.gif",
-      "_",
-      "image",
-      "gen_platf",
-      "inc0.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc1.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc2.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc3.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc4.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc5.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc6.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc7.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc8.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc9.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc10.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc11.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc12.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc13.png",
-      "_",
-      "image",
-      "gen_platf",
-      "inc14.png",
-      "_",
-      "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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json
deleted file mode 100644
index 26c7b2c..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json
+++ /dev/null
@@ -1,548 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--threads",
-      "4",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-ASAN.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-ASAN.json
deleted file mode 100644
index 7f8ab95..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-ASAN.json
+++ /dev/null
@@ -1,437 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-ASAN",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "extra_config",
-      "ASAN",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "ASAN_OPTIONS": "symbolize=1 detect_leaks=1",
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "LSAN_OPTIONS": "symbolize=1 print_suppressions=1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out",
-      "UBSAN_OPTIONS": "symbolize=1 print_stacktrace=1"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-MSAN.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-MSAN.json
deleted file mode 100644
index f7532e0..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-MSAN.json
+++ /dev/null
@@ -1,438 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-MSAN",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "extra_config",
-      "MSAN",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "--match",
-      "~Once",
-      "~Shared"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/msan",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
deleted file mode 100644
index b56a562..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
+++ /dev/null
@@ -1,550 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "extra_config",
-      "SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--threads",
-      "0",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug.json
deleted file mode 100644
index 62bbb7b..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug.json
+++ /dev/null
@@ -1,546 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Shared.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Shared.json
deleted file mode 100644
index 34184d2..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Shared.json
+++ /dev/null
@@ -1,548 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Release/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Shared",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "extra_config",
-      "Shared",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN.json
deleted file mode 100644
index e670711..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN.json
+++ /dev/null
@@ -1,438 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Release/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "extra_config",
-      "TSAN",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "--match",
-      "~ReadWriteAlpha",
-      "~RGBA4444TextureTest",
-      "~RGB565TextureTest"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
deleted file mode 100644
index 427a4cd..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
+++ /dev/null
@@ -1,465 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "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]/out/Release/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX550Ti",
-      "extra_config",
-      "Valgrind",
-      "model",
-      "ShuttleA",
-      "os",
-      "Ubuntu",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "glnvprdit8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "pdf",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "pdf",
-      "_",
-      "_",
-      "PANO_20121023_214540.jpg",
-      "pdf",
-      "skp",
-      "_",
-      "worldjournal",
-      "pdf",
-      "skp",
-      "_",
-      "desk_baidu.skp",
-      "pdf",
-      "skp",
-      "_",
-      "desk_wikipedia.skp",
-      "_",
-      "svg",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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",
-      "--match",
-      "~Threaded"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
deleted file mode 100644
index b66ce51..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
+++ /dev/null
@@ -1,466 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "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]/out/Release/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX550Ti",
-      "extra_config",
-      "Valgrind_AbandonGpuContext",
-      "model",
-      "ShuttleA",
-      "os",
-      "Ubuntu",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "glnvprdit8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "pdf",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "pdf",
-      "_",
-      "_",
-      "PANO_20121023_214540.jpg",
-      "pdf",
-      "skp",
-      "_",
-      "worldjournal",
-      "pdf",
-      "skp",
-      "_",
-      "desk_baidu.skp",
-      "pdf",
-      "skp",
-      "_",
-      "desk_wikipedia.skp",
-      "_",
-      "svg",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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",
-      "--match",
-      "~Threaded",
-      "--abandonGpuContext"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext.json
deleted file mode 100644
index 5461cbc..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext.json
+++ /dev/null
@@ -1,466 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "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]/out/Release/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX550Ti",
-      "extra_config",
-      "Valgrind_PreAbandonGpuContext",
-      "model",
-      "ShuttleA",
-      "os",
-      "Ubuntu",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "glnvprdit8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "pdf",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "pdf",
-      "_",
-      "_",
-      "PANO_20121023_214540.jpg",
-      "pdf",
-      "skp",
-      "_",
-      "worldjournal",
-      "pdf",
-      "skp",
-      "_",
-      "desk_baidu.skp",
-      "pdf",
-      "skp",
-      "_",
-      "desk_wikipedia.skp",
-      "_",
-      "svg",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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",
-      "--match",
-      "~Threaded",
-      "--preAbandonGpuContext"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json
deleted file mode 100644
index bef6905..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json
+++ /dev/null
@@ -1,349 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "extra_config",
-      "Vulkan",
-      "model",
-      "NUC",
-      "os",
-      "Ubuntu16",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "vk",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "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",
-      "--match",
-      "~VkHeapTests"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_sdk/lib:[START_DIR]/linux_vulkan_intel_driver_debug",
-      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out",
-      "VK_ICD_FILENAMES": "[START_DIR]/linux_vulkan_intel_driver_debug/intel_icd.x86_64.json"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release.json
deleted file mode 100644
index 3b81c5a..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release.json
+++ /dev/null
@@ -1,555 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Release/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "model",
-      "NUC",
-      "os",
-      "Ubuntu16",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "gles",
-      "glesdft",
-      "glessrgb",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "glessrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
-      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug.json
deleted file mode 100644
index f26b969..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug.json
+++ /dev/null
@@ -1,555 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelHD405",
-      "model",
-      "NUC5PPYH",
-      "os",
-      "Ubuntu16",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "gles",
-      "glesdft",
-      "glessrgb",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "glessrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
-      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug.json
deleted file mode 100644
index 7094804..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug.json
+++ /dev/null
@@ -1,557 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelBayTrail",
-      "model",
-      "NUCDE3815TYKHE",
-      "os",
-      "Ubuntu16",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "gles",
-      "glesdft",
-      "glessrgb",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "glessrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "_",
-      "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",
-      "--match",
-      "~ImageStorageLoad"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
-      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan.json
deleted file mode 100644
index 360b300..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan.json
+++ /dev/null
@@ -1,387 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Debug_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "RadeonR9M470X",
-      "extra_config",
-      "Vulkan",
-      "model",
-      "AlphaR2",
-      "os",
-      "Win10",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "vk",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-ANGLE.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-ANGLE.json
deleted file mode 100644
index 22fdce3..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-ANGLE.json
+++ /dev/null
@@ -1,456 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Debug_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-ANGLE",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "extra_config",
-      "ANGLE",
-      "model",
-      "NUC",
-      "os",
-      "Win10",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "angle_d3d11_es2",
-      "angle_d3d9_es2",
-      "angle_gl_es2",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "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",
-      "_",
-      "gm",
-      "_",
-      "discard",
-      "angle_d3d9_es2",
-      "gm",
-      "_",
-      "multipicturedraw_invpathclip_simple",
-      "angle_d3d9_es2",
-      "gm",
-      "_",
-      "multipicturedraw_noclip_simple",
-      "angle_d3d9_es2",
-      "gm",
-      "_",
-      "multipicturedraw_pathclip_simple",
-      "angle_d3d9_es2",
-      "gm",
-      "_",
-      "multipicturedraw_rectclip_simple",
-      "angle_d3d9_es2",
-      "gm",
-      "_",
-      "multipicturedraw_rrectclip_simple",
-      "angle_d3d11_es2",
-      "gm",
-      "_",
-      "multipicturedraw_invpathclip_simple",
-      "angle_d3d11_es2",
-      "gm",
-      "_",
-      "multipicturedraw_noclip_simple",
-      "angle_d3d11_es2",
-      "gm",
-      "_",
-      "multipicturedraw_pathclip_simple",
-      "angle_d3d11_es2",
-      "gm",
-      "_",
-      "multipicturedraw_rectclip_simple",
-      "angle_d3d11_es2",
-      "gm",
-      "_",
-      "multipicturedraw_rrectclip_simple",
-      "angle_gl_es2",
-      "gm",
-      "_",
-      "multipicturedraw_invpathclip_simple",
-      "angle_gl_es2",
-      "gm",
-      "_",
-      "multipicturedraw_noclip_simple",
-      "angle_gl_es2",
-      "gm",
-      "_",
-      "multipicturedraw_pathclip_simple",
-      "angle_gl_es2",
-      "gm",
-      "_",
-      "multipicturedraw_rectclip_simple",
-      "angle_gl_es2",
-      "gm",
-      "_",
-      "multipicturedraw_rrectclip_simple",
-      "--match",
-      "~GLPrograms",
-      "~IntTexture"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json
deleted file mode 100644
index 38a0653..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan.json
+++ /dev/null
@@ -1,594 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Debug_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "IntelIris540",
-      "extra_config",
-      "Vulkan",
-      "model",
-      "NUC",
-      "os",
-      "Win10",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "vk",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "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",
-      "vk",
-      "gm",
-      "_",
-      "aarectmodes",
-      "vk",
-      "gm",
-      "_",
-      "aaxfermodes",
-      "vk",
-      "gm",
-      "_",
-      "arithmode",
-      "vk",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "vk",
-      "gm",
-      "_",
-      "composeshader_bitmap2",
-      "vk",
-      "gm",
-      "_",
-      "dftextCOLR",
-      "vk",
-      "gm",
-      "_",
-      "drawregionmodes",
-      "vk",
-      "gm",
-      "_",
-      "filterfastbounds",
-      "vk",
-      "gm",
-      "_",
-      "fontcache",
-      "vk",
-      "gm",
-      "_",
-      "fontmgr_iterWin10",
-      "vk",
-      "gm",
-      "_",
-      "fontmgr_iter_factoryWin10",
-      "vk",
-      "gm",
-      "_",
-      "fontmgr_matchWin10",
-      "vk",
-      "gm",
-      "_",
-      "fontscalerWin",
-      "vk",
-      "gm",
-      "_",
-      "fontscalerdistortable",
-      "vk",
-      "gm",
-      "_",
-      "gammagradienttext",
-      "vk",
-      "gm",
-      "_",
-      "gammatextWin",
-      "vk",
-      "gm",
-      "_",
-      "gradtext",
-      "vk",
-      "gm",
-      "_",
-      "hairmodes",
-      "vk",
-      "gm",
-      "_",
-      "imagefilters_xfermodes",
-      "vk",
-      "gm",
-      "_",
-      "imagefiltersclipped",
-      "vk",
-      "gm",
-      "_",
-      "imagefiltersgraph",
-      "vk",
-      "gm",
-      "_",
-      "imagefiltersscaled",
-      "vk",
-      "gm",
-      "_",
-      "imagefiltersstroked",
-      "vk",
-      "gm",
-      "_",
-      "imagefilterstransformed",
-      "vk",
-      "gm",
-      "_",
-      "imageresizetiled",
-      "vk",
-      "gm",
-      "_",
-      "lcdblendmodes",
-      "vk",
-      "gm",
-      "_",
-      "lcdoverlap",
-      "vk",
-      "gm",
-      "_",
-      "lcdtextWin",
-      "vk",
-      "gm",
-      "_",
-      "lcdtextsize",
-      "vk",
-      "gm",
-      "_",
-      "matriximagefilter",
-      "vk",
-      "gm",
-      "_",
-      "mixedtextblobsCOLR",
-      "vk",
-      "gm",
-      "_",
-      "pictureimagefilter",
-      "vk",
-      "gm",
-      "_",
-      "resizeimagefilter",
-      "vk",
-      "gm",
-      "_",
-      "rotate_imagefilter",
-      "vk",
-      "gm",
-      "_",
-      "savelayer_lcdtext",
-      "vk",
-      "gm",
-      "_",
-      "srcmode",
-      "vk",
-      "gm",
-      "_",
-      "surfaceprops",
-      "vk",
-      "gm",
-      "_",
-      "textblobgeometrychange",
-      "vk",
-      "gm",
-      "_",
-      "textbloblooper",
-      "vk",
-      "gm",
-      "_",
-      "textblobmixedsizes",
-      "vk",
-      "gm",
-      "_",
-      "textblobmixedsizes_df",
-      "vk",
-      "gm",
-      "_",
-      "textblobrandomfont",
-      "vk",
-      "gm",
-      "_",
-      "textfilter_color",
-      "vk",
-      "gm",
-      "_",
-      "textfilter_image",
-      "vk",
-      "gm",
-      "_",
-      "typefacerenderingWin",
-      "vk",
-      "gm",
-      "_",
-      "varied_text_clipped_lcd",
-      "vk",
-      "gm",
-      "_",
-      "varied_text_ignorable_clip_lcd",
-      "vk",
-      "gm",
-      "_",
-      "xfermodeimagefilter",
-      "--match",
-      "~ApplyGamma",
-      "~ComposedImageFilterBounds_Gpu",
-      "~ImageFilterFailAffectsTransparentBlack_Gpu",
-      "~ImageFilterZeroBlurSigma_Gpu",
-      "~ImageNewShader_GPU",
-      "~NewTextureFromPixmap",
-      "~ReadPixels_Gpu",
-      "~ReadPixels_Texture",
-      "~ReadWriteAlpha",
-      "~SRGBReadWritePixels",
-      "~SpecialImage_DeferredGpu",
-      "~SpecialImage_Gpu",
-      "~WritePixels_Gpu",
-      "~XfermodeImageFilterCroppedInput_Gpu"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan.json
deleted file mode 100644
index 3fb7554..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan.json
+++ /dev/null
@@ -1,387 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Debug_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX660",
-      "extra_config",
-      "Vulkan",
-      "model",
-      "ShuttleA",
-      "os",
-      "Win10",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "vk",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan.json
deleted file mode 100644
index 860cb6d..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan.json
+++ /dev/null
@@ -1,389 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Debug_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX1070",
-      "extra_config",
-      "Vulkan",
-      "model",
-      "ZBOX",
-      "os",
-      "Win10",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "vk",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "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",
-      "--match",
-      "~GPUMemorySize"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot.json
deleted file mode 100644
index eb6d6ea..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot.json
+++ /dev/null
@@ -1,525 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Release_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot",
-      "build_number",
-      "5",
-      "issue",
-      "500",
-      "patchset",
-      "1",
-      "patch_storage",
-      "rietveld",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "ShuttleB",
-      "os",
-      "Win8",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "_",
-      "gm",
-      "_",
-      "fontscalerdistortable",
-      "_",
-      "svg",
-      "_",
-      "Nebraska-StateSeal.svg",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "verylargebitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "verylarge_picture_image",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "--noRAW_threading"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE.json b/infra/bots/recipe_modules/sktest/example.expected/Test-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE.json
deleted file mode 100644
index bbdcbbc..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE.json
+++ /dev/null
@@ -1,401 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Debug_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "GTX960",
-      "extra_config",
-      "ANGLE",
-      "model",
-      "ShuttleB",
-      "os",
-      "Win8",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nocpu",
-      "--randomProcessorTest",
-      "--config",
-      "angle_d3d11_es2",
-      "angle_d3d9_es2",
-      "angle_gl_es2",
-      "angle_d3d11_es2_msaa8",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "gm",
-      "_",
-      "fontscalerdistortable",
-      "_",
-      "svg",
-      "_",
-      "Nebraska-StateSeal.svg",
-      "_",
-      "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",
-      "--match",
-      "~GLPrograms",
-      "--noRAW_threading"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json b/infra/bots/recipe_modules/sktest/example.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
deleted file mode 100644
index 85192ec..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
+++ /dev/null
@@ -1,577 +0,0 @@
-[
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/skia/resources",
-      "resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/skia/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
-      "tmp/SKP_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "cat_file tmp/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "tmp/SKP_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm tmp/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm skps"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir skps"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/skp",
-      "skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/skp"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "tmp/SKP_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
-      "tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "cat_file tmp/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm tmp/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm images"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir images"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/skimage",
-      "images"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/skimage"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "tmp/SK_IMAGE_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
-      "tmp/SVG_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "cat_file tmp/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "tmp/SVG_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm tmp/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm svgs"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir svgs"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
-      "[START_DIR]/svg",
-      "svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_if_needed [START_DIR]/svg"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "tmp/SVG_VERSION"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
-      "dm"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "rm dm"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
-      "dm"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "mkdir dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "env": {
-      "IOS_BUNDLE_ID": "com.google.dm"
-    },
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "push_file [START_DIR]/tmp/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/gn/package_ios.py",
-      "[START_DIR]/out/Release/dm"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "package dm"
-  },
-  {
-    "cmd": [
-      "ios-deploy",
-      "-b",
-      "[START_DIR]/out/Release/dm.app",
-      "-I",
-      "--args",
-      "--undefok --resourcePath resources --skps skps --images images/dm --colorImages images/colorspace --nameByHash --properties gitHash abc123 master client.skia builder Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release build_number 5 --svgs svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value GX6450 model iPadMini4 os iOS --uninterestingHashesFile tmp/uninteresting_hashes.txt --writePath dm --nocpu --config 8888 srgb pdf gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ gles skp _ _ _ image gen_platf rgba32abf.bmp _ image gen_platf rgb24prof.bmp _ image gen_platf rgb24lprof.bmp _ image gen_platf 8bpp-pixeldata-cropped.bmp _ image gen_platf 4bpp-pixeldata-cropped.bmp _ image gen_platf 32bpp-pixeldata-cropped.bmp _ image gen_platf 24bpp-pixeldata-cropped.bmp _ image gen_platf frame_larger_than_image.gif _ image gen_platf inc0.png _ image gen_platf inc1.png _ image gen_platf inc2.png _ image gen_platf inc3.png _ image gen_platf inc4.png _ image gen_platf inc5.png _ image gen_platf inc6.png _ image gen_platf inc7.png _ image gen_platf inc8.png _ image gen_platf inc9.png _ image gen_platf inc10.png _ image gen_platf inc11.png _ image gen_platf inc12.png _ image gen_platf inc13.png _ image gen_platf inc14.png _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/skia/platform_tools/ios/bin/ios_pull_if_needed",
-      "dm",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "IOS_BUNDLE_ID": "com.google.dm",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "pull_if_needed dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/big_issue_number.json b/infra/bots/recipe_modules/sktest/example.expected/big_issue_number.json
deleted file mode 100644
index a1140eb..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/big_issue_number.json
+++ /dev/null
@@ -1,525 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]\\tmp\\SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]\\resources\\fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]\\tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "[START_DIR]\\out\\Release_x64\\dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]\\skia\\resources",
-      "--skps",
-      "[START_DIR]\\skp",
-      "--images",
-      "[START_DIR]\\skimage\\dm",
-      "--colorImages",
-      "[START_DIR]\\skimage\\colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia.compile",
-      "builder",
-      "Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot",
-      "build_number",
-      "5",
-      "issue",
-      "2147533002",
-      "patchset",
-      "1",
-      "patch_storage",
-      "rietveld",
-      "--svgs",
-      "[START_DIR]\\svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "MSVC",
-      "configuration",
-      "Release",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "ShuttleB",
-      "os",
-      "Win8",
-      "--uninterestingHashesFile",
-      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "_",
-      "image",
-      "gen_platf",
-      "rle8-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rle4-height-negative.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "pal8os2v2-16.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgba32abf.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24prof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "rgb24lprof.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "8bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "4bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "32bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "24bpp-pixeldata-cropped.bmp",
-      "_",
-      "image",
-      "gen_platf",
-      "testimgari.jpg",
-      "_",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "_",
-      "gm",
-      "_",
-      "fontscalerdistortable",
-      "_",
-      "svg",
-      "_",
-      "Nebraska-StateSeal.svg",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "verylargebitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "verylarge_picture_image",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "--noRAW_threading"
-    ],
-    "env": {
-      "BUILDTYPE": "Release_x64",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s;RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]\\out"
-    },
-    "name": "dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/failed_dm.json b/infra/bots/recipe_modules/sktest/example.expected/failed_dm.json
deleted file mode 100644
index bc89138..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/failed_dm.json
+++ /dev/null
@@ -1,551 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "client.skia",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
-      "build_number",
-      "6",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "reason": "Failed build steps: symbolized dm, symbolized dm",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/failed_get_hashes.json b/infra/bots/recipe_modules/sktest/example.expected/failed_get_hashes.json
deleted file mode 100644
index 6e60c6e..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/failed_get_hashes.json
+++ /dev/null
@@ -1,826 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "get uninteresting hashes",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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@@@",
-      "@@@STEP_EXCEPTION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android build_number 6 --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 GN_Android model Nexus7 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~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@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/failed_pull.json b/infra/bots/recipe_modules/sktest/example.expected/failed_pull.json
deleted file mode 100644
index f1a2baf..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/failed_pull.json
+++ /dev/null
@@ -1,831 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skimage",
-      "/sdcard/revenge_of_the_skiabot/images"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/svg",
-      "/sdcard/revenge_of_the_skiabot/svgs"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SVG_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Debug/dm",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "set -x; /data/local/tmp/dm --undefok --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 master client.skia builder Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android build_number 6 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value MaliT604 extra_config Android model Nexus10 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ 8888 image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~CopySurface ~SRGBReadWritePixels; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "write dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/dm.sh",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push dm.sh"
-  },
-  {
-    "cmd": [
-      "adb",
-      "logcat",
-      "-c"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "clear log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "dm",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@bin_dir = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
-      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
-      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_EXCEPTION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "reason": "Infra Failure: Step('pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm') returned 1",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/failed_push.json b/infra/bots/recipe_modules/sktest/example.expected/failed_push.json
deleted file mode 100644
index 9b12457..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/failed_push.json
+++ /dev/null
@@ -1,192 +0,0 @@
-[
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skia/resources",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@",
-      "@@@STEP_EXCEPTION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Debug"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "reboot",
-      "-p"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "shut down device to quarantine bot"
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "reason": "Infra Failure: Step('push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources') returned 1",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/nobuildbot.json b/infra/bots/recipe_modules/sktest/example.expected/nobuildbot.json
deleted file mode 100644
index 55ee548..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/nobuildbot.json
+++ /dev/null
@@ -1,586 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-    "stdout": "/path/to/tmp/",
-    "~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",
-    "stdout": "/path/to/tmp/",
-    "~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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "chromium.testing.master",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug-Trybot",
-      "build_number",
-      "571",
-      "issue",
-      "456789",
-      "patchset",
-      "12",
-      "patch_storage",
-      "gerrit",
-      "no_buildbot",
-      "True",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--threads",
-      "4",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.expected/recipe_with_gerrit_patch.json b/infra/bots/recipe_modules/sktest/example.expected/recipe_with_gerrit_patch.json
deleted file mode 100644
index 003d305..0000000
--- a/infra/bots/recipe_modules/sktest/example.expected/recipe_with_gerrit_patch.json
+++ /dev/null
@@ -1,552 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "chromium.testing.master",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug-Trybot",
-      "build_number",
-      "571",
-      "issue",
-      "456789",
-      "patchset",
-      "12",
-      "patch_storage",
-      "gerrit",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--threads",
-      "4",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/sktest/example.py b/infra/bots/recipe_modules/sktest/example.py
deleted file mode 100644
index 9a3e450..0000000
--- a/infra/bots/recipe_modules/sktest/example.py
+++ /dev/null
@@ -1,268 +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.
-
-
-# Example recipe w/ coverage.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-  'sktest',
-]
-
-
-TEST_BUILDERS = {
-  'client.skia': {
-    'skiabot-linux-swarm-000': [
-      'Test-Android-Clang-AndroidOne-CPU-MT6582-arm-Release-GN_Android',
-      'Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-GN_Android',
-      'Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android',
-      'Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android',
-      'Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android',
-      'Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android',
-      'Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-GN_Android',
-      'Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-GN_Android',
-      'Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android',
-      'Test-Android-Clang-Nexus6-GPU-Adreno420-arm-Debug-GN_Android',
-      'Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-GN_Android_Vulkan',
-      'Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android',
-      'Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-GN_Android',
-      ('Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-'
-       'GN_Android_Vulkan'),
-      'Test-Android-Clang-PixelC-GPU-TegraX1-arm64-Debug-GN_Android',
-      'Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug',
-      'Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug',
-      'Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer',
-      'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug',
-      'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug',
-      'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-ASAN',
-      'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-MSAN',
-      'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Shared',
-      'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN',
-      'Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind',
-      ('Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind' +
-       '_AbandonGpuContext'),
-      ('Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind' +
-       '_PreAbandonGpuContext'),
-      'Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan',
-      'Test-Ubuntu16-Clang-NUC-GPU-IntelIris540-x86_64-Release',
-      'Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug',
-      'Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug',
-      'Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan',
-      'Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-ANGLE',
-      'Test-Win10-MSVC-NUC-GPU-IntelIris540-x86_64-Debug-Vulkan',
-      'Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan',
-      'Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan',
-      'Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot',
-      'Test-Win8-MSVC-ShuttleB-GPU-GTX960-x86_64-Debug-ANGLE',
-      'Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release',
-      ('Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_' +
-        'SCALEDIMAGECACHE'),
-    ],
-  },
-}
-
-
-def RunSteps(api):
-  api.sktest.run()
-
-
-def GenTests(api):
-  for mastername, slaves in TEST_BUILDERS.iteritems():
-    for slavename, builders_by_slave in slaves.iteritems():
-      for builder in builders_by_slave:
-        test = (
-          api.test(builder) +
-          api.properties(buildername=builder,
-                         mastername=mastername,
-                         slavename=slavename,
-                         buildnumber=5,
-                         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',
-                                           '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')
-          )
-        )
-        if 'Trybot' in builder:
-          test += api.properties(issue=500,
-                                 patchset=1,
-                                 rietveld='https://codereview.chromium.org')
-        if 'Win' in builder:
-          test += api.platform('win', 64)
-
-
-        yield test
-
-  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
-  yield (
-    api.test('failed_dm') +
-    api.properties(buildername=builder,
-                   mastername='client.skia',
-                   slavename='skiabot-linux-swarm-000',
-                   buildnumber=6,
-                   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',
-                                     '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')
-    ) +
-    api.step_data('symbolized dm', retcode=1)
-  )
-
-  builder = 'Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-GN_Android'
-  yield (
-    api.test('failed_get_hashes') +
-    api.properties(buildername=builder,
-                   mastername='client.skia',
-                   slavename='skiabot-linux-swarm-000',
-                   buildnumber=6,
-                   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',
-                                     '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')
-    ) +
-    api.step_data('get uninteresting hashes', retcode=1)
-  )
-
-  builder = 'Test-Win8-MSVC-ShuttleB-CPU-AVX2-x86_64-Release-Trybot'
-  yield (
-    api.test('big_issue_number') +
-    api.properties(buildername=builder,
-                     mastername='client.skia.compile',
-                     slavename='skiabot-linux-swarm-000',
-                     buildnumber=5,
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]',
-                     rietveld='https://codereview.chromium.org',
-                     patchset=1,
-                     issue=2147533002L) +
-    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')
-    ) +
-    api.platform('win', 64)
-  )
-
-  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug-Trybot'
-  yield (
-      api.test('recipe_with_gerrit_patch') +
-      api.properties(
-          buildername=builder,
-          mastername='client.skia',
-          slavename='skiabot-linux-swarm-000',
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          revision='abc123',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=builder,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      )
-  )
-
-  yield (
-      api.test('nobuildbot') +
-      api.properties(
-          buildername=builder,
-          mastername='client.skia',
-          slavename='skiabot-linux-swarm-000',
-          buildnumber=5,
-          path_config='kitchen',
-          swarm_out_dir='[SWARM_OUT_DIR]',
-          revision='abc123',
-          nobuildbot='True',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=builder,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      api.step_data('get swarming bot id',
-          stdout=api.raw_io.output('skia-bot-123')) +
-      api.step_data('get swarming task id', stdout=api.raw_io.output('123456'))
-  )
-
-  builder = 'Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Debug-GN_Android'
-  yield (
-    api.test('failed_push') +
-    api.properties(buildername=builder,
-                   mastername='client.skia',
-                   slavename='skiabot-linux-swarm-000',
-                   buildnumber=6,
-                   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',
-                                     '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')
-    ) +
-    api.step_data('push [START_DIR]/skia/resources/* '+
-                  '/sdcard/revenge_of_the_skiabot/resources', retcode=1)
-  )
-
-  builder = 'Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android'
-  yield (
-    api.test('failed_pull') +
-    api.properties(buildername=builder,
-                   mastername='client.skia',
-                   slavename='skiabot-linux-swarm-000',
-                   buildnumber=6,
-                   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',
-                                     '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')
-    ) +
-    api.step_data('dm', retcode=1) +
-    api.step_data('pull /sdcard/revenge_of_the_skiabot/dm_out '+
-                  '[CUSTOM_[SWARM_OUT_DIR]]/dm', retcode=1)
-  )
diff --git a/infra/bots/recipe_modules/swarming/OWNERS b/infra/bots/recipe_modules/swarming/OWNERS
new file mode 100644
index 0000000..c14640d
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/OWNERS
@@ -0,0 +1,3 @@
+maruel@chromium.org
+tansell@chromium.org
+vadimsh@chromium.org
diff --git a/infra/bots/recipe_modules/swarming/__init__.py b/infra/bots/recipe_modules/swarming/__init__.py
index cc099f6..98b20ff 100644
--- a/infra/bots/recipe_modules/swarming/__init__.py
+++ b/infra/bots/recipe_modules/swarming/__init__.py
@@ -1,22 +1,33 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# 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.
 
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
 DEPS = [
-  'build/file',
-  'build/isolate',
-  'build/swarming',
-  'build/swarming_client',
-  'depot_tools/depot_tools',
+  'isolate',
+  'recipe_engine/context',
   'recipe_engine/json',
   'recipe_engine/path',
+  'recipe_engine/platform',
   'recipe_engine/properties',
   'recipe_engine/python',
   'recipe_engine/raw_io',
   'recipe_engine/step',
-  'run',
+  'swarming_client',
 ]
 
+from recipe_engine.recipe_api import Property
 
-# TODO(borenet): Add coverage
+PROPERTIES = {
+  'show_shards_in_collect_step': Property(default=False, kind=bool),
+  'show_isolated_out_in_collect_step': Property(default=True, kind=bool),
+}
+
+
+# TODO(phajdan.jr): provide coverage (http://crbug.com/693058).
 DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/swarming/api.py b/infra/bots/recipe_modules/swarming/api.py
index c040bc8..47e2c84 100644
--- a/infra/bots/recipe_modules/swarming/api.py
+++ b/infra/bots/recipe_modules/swarming/api.py
@@ -1,220 +1,905 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# 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.
 
+import datetime
+import functools
+import hashlib
+import logging
+import os.path
 
+from recipe_engine import config_types
 from recipe_engine import recipe_api
-import shlex
+from recipe_engine import util as recipe_util
+
+import state
 
 
-DEFAULT_TASK_EXPIRATION = 20*60*60
-DEFAULT_TASK_TIMEOUT = 4*60*60
-DEFAULT_IO_TIMEOUT = 40*60
-
-MILO_LOG_LINK = 'https://luci-milo.appspot.com/swarming/task/%s'
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
 
 
-class SkiaSwarmingApi(recipe_api.RecipeApi):
-  """Provides steps to run Skia tasks on swarming bots."""
+# Minimally supported version of swarming.py script (reported by --version).
+MINIMAL_SWARMING_VERSION = (0, 8, 6)
 
-  @property
-  def swarming_temp_dir(self):
-    """Path where artifacts like isolate file and json output will be stored."""
-    return self.m.path['start_dir'].join('swarming_temp_dir')
 
-  @property
-  def tasks_output_dir(self):
-    """Directory where the outputs of the swarming tasks will be stored."""
-    return self.swarming_temp_dir.join('outputs')
+def text_for_task(task):
+  lines = []
 
-  def isolated_file_path(self, task_name):
-    """Get the path to the given task's .isolated file."""
-    return self.swarming_temp_dir.join('skia-task-%s.isolated' % task_name)
+  if task.dimensions.get('id'):  # pragma: no cover
+    lines.append('Bot id: %r' % task.dimensions['id'])
+  if task.dimensions.get('os'):
+    lines.append('Run on OS: %r' % task.dimensions['os'])
 
-  def setup(self, luci_go_dir, swarming_rev=None):
-    """Performs setup steps for swarming."""
-    self.m.swarming_client.checkout(revision=swarming_rev)
-    self.m.swarming.check_client_version(step_test_data=(0, 8, 6))
-    self.setup_go_isolate(luci_go_dir)
-    self.m.swarming.add_default_tag('allow_milo:1')
+  return '<br/>'.join(lines)
 
-  # TODO(rmistry): Remove once the Go binaries are moved to recipes or buildbot.
-  def setup_go_isolate(self, luci_go_dir):
-    """Generates and puts in place the isolate Go binary."""
-    depot_tools_path = self.m.depot_tools.package_repo_resource()
-    env = {'PATH': self.m.path.pathsep.join([
-                       str(depot_tools_path), '%(PATH)s'])}
-    with self.m.step.context({'env': env}):
-      self.m.step('download luci-go linux',
-                  ['download_from_google_storage', '--no_resume',
-                   '--platform=linux*', '--no_auth',
-                   '--bucket', 'chromium-luci',
-                   '-d', luci_go_dir.join('linux64')])
-      self.m.step('download luci-go mac',
-                  ['download_from_google_storage', '--no_resume',
-                   '--platform=darwin', '--no_auth',
-                   '--bucket', 'chromium-luci',
-                   '-d', luci_go_dir.join('mac64')])
-      self.m.step('download luci-go win',
-                  ['download_from_google_storage', '--no_resume',
-                   '--platform=win32', '--no_auth', '--bucket',
-                   'chromium-luci',
-                   '-d', luci_go_dir.join('win64')])
-    # Copy binaries to the expected location.
-    dest = self.m.path['start_dir'].join('luci-go')
-    self.m.run.rmtree(dest)
-    self.m.file.copytree('Copy Go binary',
-                         source=luci_go_dir,
-                         dest=dest)
 
-  def create_isolated_gen_json(self, isolate_path, base_dir, os_type,
-                               task_name, extra_variables, blacklist=None):
-    """Creates an isolated.gen.json file (used by the isolate recipe module).
-
-    Args:
-      isolate_path: path obj. Path to the isolate file.
-      base_dir: path obj. Dir that is the base of all paths in the isolate file.
-      os_type: str. The OS type to use when archiving the isolate file.
-          Eg: linux.
-      task_name: str. The isolated.gen.json file will be suffixed by this str.
-      extra_variables: dict of str to str. The extra vars to pass to isolate.
-          Eg: {'SLAVE_NUM': '1', 'MASTER': 'ChromiumPerfFYI'}
-      blacklist: list of regular expressions indicating which files/directories
-          not to archive.
-    """
-    self.m.file.makedirs('swarming tmp dir', self.swarming_temp_dir)
-    isolated_path = self.isolated_file_path(task_name)
-    isolate_args = [
-      '--isolate', isolate_path,
-      '--isolated', isolated_path,
-      '--config-variable', 'OS', os_type,
-    ]
-    if blacklist:  # pragma: no cover
-      for b in blacklist:
-        isolate_args.extend(['--blacklist', b])
-    for k, v in extra_variables.iteritems():
-      isolate_args.extend(['--extra-variable', k, v])
-    isolated_gen_dict = {
-      'version': 1,
-      'dir': base_dir,
-      'args': isolate_args,
-    }
-    isolated_gen_json = self.swarming_temp_dir.join(
-        '%s.isolated.gen.json' % task_name)
-    self.m.file.write(
-        'Write %s.isolated.gen.json' % task_name,
-        isolated_gen_json,
-        self.m.json.dumps(isolated_gen_dict, indent=4),
-    )
-
-  def batcharchive(self, targets):
-    """Calls batcharchive on the skia.isolated.gen.json file.
-
-    Args:
-      targets: list of str. The suffixes of the isolated.gen.json files to
-               archive.
-
-    Returns:
-      list of tuples containing (task_name, swarming_hash).
-    """
-    return self.m.isolate.isolate_tests(
-        verbose=True,  # To avoid no output timeouts.
-        build_dir=self.swarming_temp_dir,
-        targets=targets).presentation.properties['swarm_hashes'].items()
-
-  def trigger_swarming_tasks(
-      self, swarm_hashes, dimensions, idempotent=False, store_output=True,
-      extra_args=None, expiration=None, hard_timeout=None, io_timeout=None,
-      cipd_packages=None):
-    """Triggers swarming tasks using swarm hashes.
-
-    Args:
-      swarm_hashes: list of str. List of swarm hashes from the isolate server.
-      dimensions: dict of str to str. The dimensions to run the task on.
-                  Eg: {'os': 'Ubuntu', 'gpu': '10de', 'pool': 'Skia'}
-      idempotent: bool. Whether or not to de-duplicate tasks.
-      store_output: bool. Whether task output should be stored.
-      extra_args: list of str. Extra arguments to pass to the task.
-      expiration: int. Task will expire if not picked up within this time.
-                  DEFAULT_TASK_EXPIRATION is used if this argument is None.
-      hard_timeout: int. Task will timeout if not completed within this time.
-                    DEFAULT_TASK_TIMEOUT is used if this argument is None.
-      io_timeout: int. Task will timeout if there is no output within this time.
-                  DEFAULT_IO_TIMEOUT is used if this argument is None.
-      cipd_packages: CIPD packages which these tasks depend on.
-
-    Returns:
-      List of swarming.SwarmingTask instances.
-    """
-    swarming_tasks = []
-    for task_name, swarm_hash in swarm_hashes:
-      swarming_task = self.m.swarming.task(
-          title=task_name,
-          cipd_packages=cipd_packages,
-          isolated_hash=swarm_hash)
-      if store_output:
-        swarming_task.task_output_dir = self.tasks_output_dir.join(task_name)
-      swarming_task.dimensions = dimensions
-      swarming_task.idempotent = idempotent
-      swarming_task.priority = 90
-      swarming_task.expiration = (
-          expiration if expiration else DEFAULT_TASK_EXPIRATION)
-      swarming_task.hard_timeout = (
-          hard_timeout if hard_timeout else DEFAULT_TASK_TIMEOUT)
-      swarming_task.io_timeout = (
-          io_timeout if io_timeout else DEFAULT_IO_TIMEOUT)
-      if extra_args:  # pragma: no cover
-        swarming_task.extra_args = extra_args
-      revision = self.m.properties.get('revision')
-      if revision:
-        swarming_task.tags.add('revision:%s' % revision)
-      swarming_tasks.append(swarming_task)
-    step_results = self.m.swarming.trigger(swarming_tasks)
-    for step_result in step_results:
-      self._add_log_links(step_result, step_result.json.output)
-    return swarming_tasks
-
-  def collect_swarming_task(self, swarming_task):
-    """Collects the specified swarming task.
-
-    Args:
-      swarming_task: An instance of swarming.SwarmingTask.
-    """
+def parse_time(value):
+  """Converts serialized time from the API to datetime.datetime."""
+  # When microseconds are 0, the '.123456' suffix is elided. This means the
+  # serialized format is not consistent, which confuses the hell out of python.
+  # TODO(maruel): Remove third format once we enforce version >=0.8.2.
+  for fmt in ('%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S'):
     try:
-      rv = self.m.swarming.collect_task(swarming_task)
-    except self.m.step.StepFailure as e:  # pragma: no cover
-      step_result = self.m.step.active_result
-      # Change step result to Infra failure if the swarming task failed due to
-      # expiration, time outs, bot crashes or task cancelations.
-      # Infra failures have step.EXCEPTION.
-      states_infra_failure = (
-          self.m.swarming.State.EXPIRED, self.m.swarming.State.TIMED_OUT,
-          self.m.swarming.State.BOT_DIED, self.m.swarming.State.CANCELED)
-      summary = step_result.swarming.summary
-      if summary['shards'][0]['state'] in states_infra_failure:
-        step_result.presentation.status = self.m.step.EXCEPTION
-        raise self.m.step.InfraFailure(e.name, step_result)
-      raise
-    finally:
-      step_result = self.m.step.active_result
-      # Add log link.
-      self._add_log_links(step_result, step_result.swarming.summary)
-    return rv
+      return datetime.datetime.strptime(value, fmt)
+    except ValueError:  # pragma: no cover
+      pass
+  raise ValueError('Failed to parse %s' % value)  # pragma: no cover
 
-  def _add_log_links(self, step_result, summary):
-    """Add Milo log links to all shards in the step."""
-    ids = []
-    shards = summary.get('shards')
-    if shards:
-      for shard in shards:
-        ids.append(shard['id'])
+
+class ReadOnlyDict(dict):
+  def __setitem__(self, key, value):
+    raise TypeError('ReadOnlyDict is immutable')
+
+
+class SwarmingApi(recipe_api.RecipeApi):
+  """Recipe module to use swarming.py tool to run tasks on Swarming.
+
+  General usage:
+    1. Tweak default task parameters applied to all swarming tasks (such as
+       default_dimensions and default_priority).
+    2. Isolate some test using 'isolate' recipe module. Get isolated hash as
+       a result of that process.
+    3. Create a task configuration using 'task(...)' method, providing
+       isolated hash obtained previously.
+    4. Tweak the task parameters. This step is optional.
+    5. Launch the task on swarming by calling 'trigger_task(...)'.
+    6. Continue doing useful work locally while the task is running concurrently
+       on swarming.
+    7. Wait for task to finish and collect its result (exit code, logs)
+       by calling 'collect_task(...)'.
+
+  See also example.py for concrete code.
+  """
+
+  State = state.State
+
+  #############################################################################
+  # The below are helper functions to help transition between the old and new #
+  # swarming result formats. TODO(martiniss): remove these                    #
+  #############################################################################
+
+  def _is_expired(self, shard):
+    # FIXME: We really should only have one format for enums. We want to move to
+    # strings, currently have numbers.
+    return (
+        shard.get('state') == self.State.EXPIRED or
+        shard.get('state') == 'EXPIRED')
+
+  def _is_timed_out(self, shard):
+    # FIXME: We really should only have one format for enums. We want to move to
+    # strings, currently have numbers.
+    return (
+        shard.get('state') == self.State.TIMED_OUT or
+        shard.get('state') == 'TIMED_OUT')
+
+  def _get_exit_code(self, shard):
+    if shard.get('exit_code'):
+      return shard.get('exit_code')  # pragma: no cover
+    lst = shard.get('exit_codes', [])
+    return str(lst[0]) if lst else None
+
+  def __init__(self, **kwargs):
+    super(SwarmingApi, self).__init__(**kwargs)
+    # All tests default to a x86-64 bot running with no GPU. This simplifies
+    # management so that new tests are not executed on exotic bots by accidents
+    # even if misconfigured.
+    self._default_dimensions = {
+      'cpu': 'x86-64',
+      'gpu': 'none',
+    }
+    # Expirations are set to mildly good values and will be tightened soon.
+    self._default_expiration = 60*60
+    self._default_env = {}
+    self._default_hard_timeout = 60*60
+    self._default_idempotent = False
+    self._default_io_timeout = 20*60
+    # The default priority is extremely low and should be increased dependending
+    # on the type of task.
+    self._default_priority = 200
+    self._default_tags = set()
+    self._default_user = None
+    self._pending_tasks = set()
+    self._show_isolated_out_in_collect_step = True
+    self._show_shards_in_collect_step = False
+    self._swarming_server = 'https://chromium-swarm.appspot.com'
+    self._verbose = False
+
+  @recipe_util.returns_placeholder
+  def summary(self):
+    return self.m.json.output()
+
+  @property
+  def swarming_server(self):
+    """URL of Swarming server to use, default is a production one."""
+    return self._swarming_server
+
+  @swarming_server.setter
+  def swarming_server(self, value):
+    """Changes URL of Swarming server to use."""
+    self._swarming_server = value
+
+  @property
+  def verbose(self):
+    """True to run swarming scripts with verbose output."""
+    return self._verbose
+
+  @verbose.setter
+  def verbose(self, value):
+    """Enables or disables verbose output in swarming scripts."""
+    assert isinstance(value, bool), value
+    self._verbose = value
+
+  @property
+  def default_expiration(self):
+    """Number of seconds that the server will wait to find a bot able to run the
+    task.
+
+    If not bot runs the task by this number of seconds, the task is canceled as
+    EXPIRED.
+
+    This value can be changed per individual task.
+    """
+    return self._default_expiration
+
+  @default_expiration.setter
+  def default_expiration(self, value):
+    assert 30 <= value <= 24*60*60, value
+    self._default_expiration = value
+
+  @property
+  def default_hard_timeout(self):
+    """Number of seconds in which the task must complete.
+
+    If the task takes more than this amount of time, the process is assumed to
+    be hung. It forcibly killed via SIGTERM then SIGKILL after a grace period
+    (default: 30s). Then the task is marked as TIMED_OUT.
+
+    This value can be changed per individual task.
+    """
+    return self._default_hard_timeout
+
+  @default_hard_timeout.setter
+  def default_hard_timeout(self, value):
+    assert 30 <= value <= 6*60*60, value
+    self._default_hard_timeout = value
+
+  @property
+  def default_io_timeout(self):
+    """Number of seconds at which interval the task must write to stdout or
+    stderr.
+
+    If the task takes more than this amount of time between writes to stdout or
+    stderr, the process is assumed to be hung. It forcibly killed via SIGTERM
+    then SIGKILL after a grace period (default: 30s). Then the task is marked as
+    TIMED_OUT.
+
+    This value can be changed per individual task.
+    """
+    return self._default_io_timeout
+
+  @default_io_timeout.setter
+  def default_io_timeout(self, value):
+    assert 30 <= value <= 6*60*60, value
+    self._default_io_timeout = value
+
+  @property
+  def default_idempotent(self):
+    """Bool to specify if task deduplication can be done.
+
+    When set, the server will search for another task that ran in the last days
+    that had the exact same properties. If it finds one, the task will not be
+    run at all, the previous results will be returned as-is.
+
+    For more infos, see:
+    https://github.com/luci/luci-py/blob/master/appengine/swarming/doc/User-Guide.md#task-idempotency
+
+    This value can be changed per individual task.
+    """
+    return self._default_idempotent
+
+  @default_idempotent.setter
+  def default_idempotent(self, value):
+    assert isinstance(value, bool), value
+    self._default_idempotent = value
+
+  @property
+  def default_user(self):
+    """String to represent who triggered the task.
+
+    The user should be an email address when someone requested testing via
+    pre-commit or manual testing.
+
+    This value can be changed per individual task.
+    """
+    return self._default_user
+
+  @default_user.setter
+  def default_user(self, value):
+    assert value is None or isinstance(value, basestring), value
+    self._default_user = value
+
+  @property
+  def default_dimensions(self):
+    """Returns a copy of the default Swarming dimensions to run task on.
+
+    The dimensions are what is used to filter which bots are able to run the
+    task successfully. This is particularly useful to discern between OS
+    versions, type of CPU, GPU card or VM, or preallocated pool.
+
+    Example:
+      {'cpu': 'x86-64', 'os': 'Windows-XP-SP3'}
+
+    This value can be changed per individual task.
+    """
+    return ReadOnlyDict(self._default_dimensions)
+
+  def set_default_dimension(self, key, value):
+    assert isinstance(key, basestring), key
+    assert isinstance(value, basestring) or value is None, value
+    if value is None:
+      self._default_dimensions.pop(key, None)
     else:
-      for _, task in summary.get('tasks', {}).iteritems():
-        ids.append(task['task_id'])
-    for idx, task_id in enumerate(ids):
-      link = MILO_LOG_LINK % task_id
-      k = 'view steps on Milo'
-      if len(ids) > 1:  # pragma: nocover
-        k += ' (shard index %d, %d total)' % (idx, len(ids))
-      step_result.presentation.links[k] = link
+      self._default_dimensions[key] = value  # pragma: no cover
 
+  @property
+  def default_env(self):
+    """Returns a copy of the default environment variable to run tasks with.
+
+    By default the environment variable is not modified. Additional environment
+    variables can be specified for each task.
+
+    This value can be changed per individual task.
+    """
+    return ReadOnlyDict(self._default_env)
+
+  def set_default_env(self, key, value):
+    assert isinstance(key, basestring), key
+    assert isinstance(value, basestring), value
+    self._default_env[key] = value
+
+  @property
+  def default_priority(self):
+    """Swarming task priority for tasks triggered from the recipe.
+
+    Priority ranges from 1 to 255. The lower the value, the most important the
+    task is and will preempty any task with a lower priority.
+
+    This value can be changed per individual task.
+    """
+    return self._default_priority
+
+  @default_priority.setter
+  def default_priority(self, value):
+    assert 1 <= value <= 255
+    self._default_priority = value
+
+  def add_default_tag(self, tag):
+    """Adds a tag to the Swarming tasks triggered.
+
+    Tags are used for maintenance, they can be used to calculate the number of
+    tasks run for a day to calculate the cost of a type of type (CQ, ASAN, etc).
+
+    Tags can be added per individual task.
+    """
+    assert ':' in tag, tag
+    self._default_tags.add(tag)
+
+  @property
+  def show_isolated_out_in_collect_step(self):
+    """Show the shard's isolated out link in each collect step."""
+    return self._show_isolated_out_in_collect_step
+
+  @show_isolated_out_in_collect_step.setter
+  def show_isolated_out_in_collect_step(self, value):
+    self._show_isolated_out_in_collect_step = value
+
+  @property
+  def show_shards_in_collect_step(self):
+    """Show the shard link in each collect step."""
+    return self._show_shards_in_collect_step
+
+  @show_shards_in_collect_step.setter
+  def show_shards_in_collect_step(self, value):
+    self._show_shards_in_collect_step = value
+
+  @staticmethod
+  def prefered_os_dimension(platform):
+    """Given a platform name returns the prefered Swarming OS dimension.
+
+    Platform name is usually provided by 'platform' recipe module, it's one
+    of 'win', 'linux', 'mac'. This function returns more concrete Swarming OS
+    dimension that represent this platform on Swarming by default.
+
+    Recipes are free to use other OS dimension if there's a need for it. For
+    example WinXP try bot recipe may explicitly specify 'Windows-XP-SP3'
+    dimension.
+    """
+    return {
+      'linux': 'Ubuntu-14.04',
+      'mac': 'Mac-10.9',
+      'win': 'Windows-7-SP1',
+    }[platform]
+
+  def task(self, title, isolated_hash, ignore_task_failure=False, shards=1,
+           task_output_dir=None, extra_args=None, idempotent=None,
+           cipd_packages=None, build_properties=None, merge=None):
+    """Returns a new SwarmingTask instance to run an isolated executable on
+    Swarming.
+
+    For google test executables, use gtest_task() instead.
+
+    At the time of this writting, this code is used by V8, Skia and iOS.
+
+    The return value can be customized if necessary (see SwarmingTask class
+    below). Pass it to 'trigger_task' to launch it on swarming. Later pass the
+    same instance to 'collect_task' to wait for the task to finish and fetch its
+    results.
+
+    Args:
+      title: name of the test, used as part of a task ID.
+      isolated_hash: hash of isolated test on isolate server, the test should
+          be already isolated there, see 'isolate' recipe module.
+      ignore_task_failure: whether to ignore the test failure of swarming
+        tasks. By default, this is set to False.
+      shards: if defined, the number of shards to use for the task. By default
+          this value is either 1 or based on the title.
+      task_output_dir: if defined, the directory where task results are placed.
+          The caller is responsible for removing this folder when finished.
+      extra_args: list of command line arguments to pass to isolated tasks.
+      idempotent: whether this task is considered idempotent. Defaults
+          to self.default_idempotent if not specified.
+      cipd_packages: list of 3-tuples corresponding to CIPD packages needed for
+          the task: ('path', 'package_name', 'version'), defined as follows:
+              path: Path relative to the Swarming root dir in which to install
+                  the package.
+              package_name: Name of the package to install,
+                  eg. "infra/tools/authutil/${platform}"
+              version: Version of the package, either a package instance ID,
+                  ref, or tag key/value pair.
+      build_properties: An optional dict containing various build properties.
+          These are typically but not necessarily the properties emitted by
+          bot_update.
+      merge: An optional dict containing:
+          "script": path to a script to call to post process and merge the
+              collected outputs from the tasks. The script should take one
+              named (but required) parameter, '-o' (for output), that represents
+              the path that the merged results should be written to, and accept
+              N additional paths to result files to merge. The merged results
+              should be in the JSON Results File Format
+              (https://www.chromium.org/developers/the-json-test-results-format)
+              and may optionally contain a top level "links" field that
+              may contain a dict mapping link text to URLs, for a set of
+              links that will be included in the buildbot output.
+          "args": an optional list of additional arguments to pass to the
+              above script.
+    """
+    if idempotent is None:
+      idempotent = self.default_idempotent
+    return SwarmingTask(
+        title=title,
+        isolated_hash=isolated_hash,
+        dimensions=self._default_dimensions,
+        env=self._default_env,
+        priority=self.default_priority,
+        shards=shards,
+        buildername=self.m.properties.get('buildername'),
+        buildnumber=self.m.properties.get('buildnumber'),
+        user=self.default_user,
+        expiration=self.default_expiration,
+        io_timeout=self.default_io_timeout,
+        hard_timeout=self.default_hard_timeout,
+        idempotent=idempotent,
+        ignore_task_failure=ignore_task_failure,
+        extra_args=extra_args,
+        collect_step=self._default_collect_step,
+        task_output_dir=task_output_dir,
+        cipd_packages=cipd_packages,
+        build_properties=build_properties,
+        merge=merge)
+
+  def check_client_version(self, step_test_data=None):
+    """Yields steps to verify compatibility with swarming_client version."""
+    return self.m.swarming_client.ensure_script_version(
+        'swarming.py', MINIMAL_SWARMING_VERSION, step_test_data)
+
+  def trigger_task(self, task, **kwargs):
+    """Triggers one task.
+
+    It the task is sharded, will trigger all shards. This steps justs posts
+    the task and immediately returns. Use 'collect_task' to wait for a task to
+    finish and grab its result.
+
+    Behaves as a regular recipe step: returns StepData with step results
+    on success or raises StepFailure if step fails.
+
+    Args:
+      task: SwarmingTask instance.
+      kwargs: passed to recipe step constructor as-is.
+    """
+    assert isinstance(task, SwarmingTask)
+    assert task.task_name not in self._pending_tasks, (
+        'Triggered same task twice: %s' % task.task_name)
+    assert 'os' in task.dimensions, task.dimensions
+    self._pending_tasks.add(task.task_name)
+
+    # Trigger parameters.
+    args = [
+      'trigger',
+      '--swarming', self.swarming_server,
+      '--isolate-server', self.m.isolate.isolate_server,
+      '--priority', str(task.priority),
+      '--shards', str(task.shards),
+      '--task-name', task.task_name,
+      '--dump-json', self.m.json.output(),
+      '--expiration', str(task.expiration),
+      '--io-timeout', str(task.io_timeout),
+      '--hard-timeout', str(task.hard_timeout),
+    ]
+    for name, value in sorted(task.dimensions.iteritems()):
+      assert isinstance(value, basestring), value
+      args.extend(['--dimension', name, value])
+    for name, value in sorted(task.env.iteritems()):
+      assert isinstance(value, basestring), value
+      args.extend(['--env', name, value])
+
+    # Default tags.
+    tags = set(task.tags)
+    tags.update(self._default_tags)
+    tags.add('data:' + task.isolated_hash)
+    tags.add('name:' + task.title.split(' ')[0])
+    mastername = self.m.properties.get('mastername')
+    if mastername:  # pragma: no cover
+      tags.add('master:' + mastername)
+    if task.buildername:  # pragma: no cover
+      tags.add('buildername:' + task.buildername)
+    if task.buildnumber:  # pragma: no cover
+      tags.add('buildnumber:%s' % task.buildnumber)
+    if task.dimensions.get('os'):
+      tags.add('os:' + task.dimensions['os'])
+    if self.m.properties.get('bot_id'):  # pragma: no cover
+      tags.add('slavename:%s' % self.m.properties['bot_id'])
+    tags.add('stepname:%s' % self.get_step_name('', task))
+    rietveld = self.m.properties.get('rietveld')
+    issue = self.m.properties.get('issue')
+    patchset = self.m.properties.get('patchset')
+    if rietveld and issue and patchset:
+      # The expected format is strict to the usage of buildbot properties on the
+      # Chromium Try Server. Fix if necessary.
+      tags.add('rietveld:%s/%s/#ps%s' % (rietveld, issue, patchset))
+    for tag in sorted(tags):
+      assert ':' in tag, tag
+      args.extend(['--tag', tag])
+
+    if self.verbose:
+      args.append('--verbose')
+    if task.idempotent:
+      args.append('--idempotent')
+    if task.user:
+      args.extend(['--user', task.user])
+
+    if task.cipd_packages:
+      for path, pkg, version in task.cipd_packages:
+        args.extend(['--cipd-package', '%s:%s:%s' % (path, pkg, version)])
+
+    # What isolated command to trigger.
+    args.extend(('--isolated', task.isolated_hash))
+
+    # Additional command line args for isolated command.
+    if task.extra_args:  # pragma: no cover
+      args.append('--')
+      args.extend(task.extra_args)
+
+    # The step can fail only on infra failures, so mark it as 'infra_step'.
+    try:
+      return self.m.python(
+          name=self.get_step_name('trigger', task),
+          script=self.m.swarming_client.path.join('swarming.py'),
+          args=args,
+          step_test_data=functools.partial(
+              self._gen_trigger_step_test_data, task),
+          infra_step=True,
+          **kwargs)
+    finally:
+      # Store trigger output with the |task|, print links to triggered shards.
+      step_result = self.m.step.active_result
+      step_result.presentation.step_text += text_for_task(task)
+
+      if step_result.presentation != self.m.step.FAILURE:
+        task._trigger_output = step_result.json.output
+        links = step_result.presentation.links
+        for index in xrange(task.shards):
+          url = task.get_shard_view_url(index)
+          if url:
+            links['shard #%d' % index] = url
+      assert not hasattr(step_result, 'swarming_task')
+      step_result.swarming_task = task
+
+  def collect_task(self, task, **kwargs):
+    """Waits for a single triggered task to finish.
+
+    If the task is sharded, will wait for all shards to finish. Behaves as
+    a regular recipe step: returns StepData with step results on success or
+    raises StepFailure if task fails.
+
+    Args:
+      task: SwarmingTask instance, previously triggered with 'trigger' method.
+      kwargs: passed to recipe step constructor as-is.
+    """
+    # TODO(vadimsh): Raise InfraFailure on Swarming failures.
+    assert isinstance(task, SwarmingTask)
+    assert task.task_name in self._pending_tasks, (
+        'Trying to collect a task that was not triggered: %s' %
+        task.task_name)
+    self._pending_tasks.remove(task.task_name)
+
+    try:
+      return task.collect_step(task, **kwargs)
+    finally:
+      try:
+        self.m.step.active_result.swarming_task = task
+      except Exception:  # pragma: no cover
+        # If we don't have an active_result, something failed very early,
+        # so we eat this exception and let that one propagate.
+        pass
+
+  def trigger(self, tasks, **kwargs):  # pragma: no cover
+    """Batch version of 'trigger_task'.
+
+    Deprecated, to be removed soon. Use 'trigger_task' in a loop instead,
+    properly handling exceptions. This method doesn't handle trigger failures
+    well (it aborts on a first failure).
+    """
+    return [self.trigger_task(t, **kwargs) for t in tasks]
+
+  def collect(self, tasks, **kwargs):  # pragma: no cover
+    """Batch version of 'collect_task'.
+
+    Deprecated, to be removed soon. Use 'collect_task' in a loop instead,
+    properly handling exceptions. This method doesn't handle collect failures
+    well (it aborts on a first failure).
+    """
+    return [self.collect_task(t, **kwargs) for t in tasks]
+
+  # To keep compatibility with some build_internal code. To be removed as well.
+  collect_each = collect
+
+  @staticmethod
+  def _display_pending(summary_json, step_presentation):
+    """Shows max pending time in seconds across all shards if it exceeds 10s."""
+    pending_times = [
+      (parse_time(shard['started_ts']) -
+        parse_time(shard['created_ts'])).total_seconds()
+      for shard in summary_json.get('shards', []) if shard.get('started_ts')
+    ]
+    max_pending = max(pending_times) if pending_times else 0
+
+    # Only display annotation when pending more than 10 seconds to reduce noise.
+    if max_pending > 10:
+      step_presentation.step_text += '<br>swarming pending %ds' % max_pending
+
+  def _default_collect_step(
+      self, task, merged_test_output=None,
+      step_test_data=None,
+      **kwargs):
+    """Produces a step that collects a result of an arbitrary task."""
+    task_output_dir = task.task_output_dir or self.m.raw_io.output_dir()
+
+    # If we don't already have a Placeholder, wrap the task_output_dir in one
+    # so we can read out of it later w/ step_result.raw_io.output_dir.
+    if not isinstance(task_output_dir, recipe_util.Placeholder):
+      task_output_dir = self.m.raw_io.output_dir(leak_to=task_output_dir)
+
+    task_args = [
+      '-o', merged_test_output or self.m.json.output(),
+      '--task-output-dir', task_output_dir,
+    ]
+
+    merge_script = (task.merge.get('script')
+                    or self.resource('noop_merge.py'))
+    merge_args = (task.merge.get('args') or [])
+
+    task_args.extend([
+      '--merge-script', merge_script,
+      '--merge-additional-args', self.m.json.dumps(merge_args),
+    ])
+
+    if task.build_properties:  # pragma: no cover
+      properties = dict(task.build_properties)
+      properties.update(self.m.properties)
+      task_args.extend([
+          '--build-properties', self.m.json.dumps(properties),
+      ])
+
+    task_args.append('--')
+    # Arguments for the actual 'collect' command.
+    collect_cmd = [
+      'python',
+      '-u',
+      self.m.swarming_client.path.join('swarming.py'),
+    ]
+    collect_cmd.extend(self.get_collect_cmd_args(task))
+    collect_cmd.extend([
+      '--task-summary-json', self.summary(),
+    ])
+
+    task_args.extend(collect_cmd)
+
+    allowed_return_codes = {0}
+    if task.ignore_task_failure:  # pragma: no cover
+      allowed_return_codes = 'any'
+
+    # The call to collect_task emits two JSON files:
+    #  1) a task summary JSON emitted by swarming
+    #  2) a gtest results JSON emitted by the task
+    # This builds an instance of StepTestData that covers both.
+    step_test_data = step_test_data or (
+      self.test_api.canned_summary_output(task.shards) +
+      self.m.json.test_api.output({}))
+
+    try:
+      with self.m.context(cwd=self.m.path['start_dir']):
+        return self.m.python(
+            name=self.get_step_name('', task),
+            script=self.resource('collect_task.py'),
+            args=task_args,
+            ok_ret=allowed_return_codes,
+            step_test_data=lambda: step_test_data,
+            **kwargs)
+    finally:
+      step_result = None
+      try:
+        step_result = self.m.step.active_result
+        step_result.presentation.step_text = text_for_task(task)
+        summary_json = step_result.swarming.summary
+        self._handle_summary_json(task, summary_json, step_result)
+
+        links = {}
+        if hasattr(step_result, 'json') and hasattr(step_result.json, 'output'):
+          links = step_result.json.output.get('links', {})
+        for k, v in links.iteritems():  # pragma: no cover
+          step_result.presentation.links[k] = v
+      except Exception as e:
+        if step_result:
+          step_result.presentation.logs['no_results_exc'] = [str(e)]
+
+  def get_step_name(self, prefix, task):
+    """SwarmingTask -> name of a step of a waterfall.
+
+    Will take a task title (+ step name prefix) and append OS dimension to it.
+
+    Args:
+      prefix: prefix to append to task title, like 'trigger'.
+      task: SwarmingTask instance.
+
+    Returns:
+      '[<prefix>] <task title> on <OS>'
+    """
+    prefix = '[%s] ' % prefix if prefix else ''
+    task_os = task.dimensions['os']
+
+    bot_os = self.prefered_os_dimension(self.m.platform.name)
+    suffix = ('' if (
+        task_os == bot_os or task_os.lower() == self.m.platform.name.lower())
+              else ' on %s' % task_os)
+    # Note: properly detecting dimensions of the bot the recipe is running
+    # on is somewhat non-trivial. It is not safe to assume it uses default
+    # or preferred dimensions for its OS. For example, the version of the OS
+    # can differ.
+    return ''.join((prefix, task.title, suffix))
+
+  def _handle_summary_json(self, task, summary, step_result):
+    # We store this now, and add links to all shards first, before failing the
+    # build. Format is tuple of (error message, shard that failed)
+    infra_failures = []
+    links = step_result.presentation.links
+    for index, shard in enumerate(summary['shards']):
+      url = task.get_shard_view_url(index)
+      display_text = 'shard #%d' % index
+
+      if not shard or shard.get('internal_failure'):  # pragma: no cover
+        display_text = (
+          'shard #%d had an internal swarming failure' % index)
+        infra_failures.append((index, 'Internal swarming failure'))
+      elif self._is_expired(shard):
+        display_text = (
+          'shard #%d expired, not enough capacity' % index)
+        infra_failures.append((
+            index, 'There isn\'t enough capacity to run your test'))
+      elif self._is_timed_out(shard):
+        display_text = (
+          'shard #%d timed out, took too much time to complete' % index)
+      elif self._get_exit_code(shard) != '0':  # pragma: no cover
+        display_text = 'shard #%d (failed)' % index
+
+      if self.show_isolated_out_in_collect_step:
+        isolated_out = shard.get('isolated_out')
+        if isolated_out:
+          link_name = 'shard #%d isolated out' % index
+          links[link_name] = isolated_out['view_url']
+
+      if url and self.show_shards_in_collect_step:
+        links[display_text] = url
+
+    self._display_pending(summary, step_result.presentation)
+
+    if infra_failures:
+      template = 'Shard #%s failed: %s'
+
+      # Done so that raising an InfraFailure doesn't cause an error.
+      # TODO(martiniss): Remove this hack. Requires recipe engine change
+      step_result._retcode = 2
+      step_result.presentation.status = self.m.step.EXCEPTION
+      raise recipe_api.InfraFailure(
+          '\n'.join(template % f for f in infra_failures), result=step_result)
+
+  def get_collect_cmd_args(self, task):
+    """SwarmingTask -> argument list for 'swarming.py' command."""
+    args = [
+      'collect',
+      '--swarming', self.swarming_server,
+      '--decorate',
+      '--print-status-updates',
+    ]
+    if self.verbose:
+      args.append('--verbose')
+    args.extend(('--json', self.m.json.input(task.trigger_output)))
+    return args
+
+  def _gen_trigger_step_test_data(self, task):
+    """Generates an expected value of --dump-json in 'trigger' step.
+
+    Used when running recipes to generate test expectations.
+    """
+    # Suffixes of shard subtask names.
+    subtasks = []
+    if task.shards == 1:
+      subtasks = ['']
+    else:
+      subtasks = [':%d:%d' % (task.shards, i) for i in range(task.shards)]
+    return self.m.json.test_api.output({
+      'base_task_name': task.task_name,
+      'tasks': {
+        '%s%s' % (task.task_name, suffix): {
+          'task_id': '1%02d00' % i,
+          'shard_index': i,
+          'view_url': '%s/user/task/1%02d00' % (self.swarming_server, i),
+        } for i, suffix in enumerate(subtasks)
+      },
+    })
+
+
+class SwarmingTask(object):
+  """Definition of a task to run on swarming."""
+
+  def __init__(self, title, isolated_hash, ignore_task_failure, dimensions,
+               env, priority, shards, buildername, buildnumber, expiration,
+               user, io_timeout, hard_timeout, idempotent, extra_args,
+               collect_step, task_output_dir, cipd_packages=None,
+               build_properties=None, merge=None):
+    """Configuration of a swarming task.
+
+    Args:
+      title: display name of the task, hints to what task is doing. Usually
+          corresponds to a name of a test executable. Doesn't have to be unique.
+      isolated_hash: hash of isolated file that describes all files needed to
+          run the task as well as command line to launch. See 'isolate' recipe
+          module.
+      ignore_task_failure: whether to ignore the test failure of swarming
+        tasks.
+      cipd_packages: list of 3-tuples corresponding to CIPD packages needed for
+          the task: ('path', 'package_name', 'version'), defined as follows:
+              path: Path relative to the Swarming root dir in which to install
+                  the package.
+              package_name: Name of the package to install,
+                  eg. "infra/tools/authutil/${platform}"
+              version: Version of the package, either a package instance ID,
+                  ref, or tag key/value pair.
+      collect_step: callback that will be called to collect and processes
+          results of task execution, signature is collect_step(task, **kwargs).
+      dimensions: key-value mapping with swarming dimensions that specify
+          on what Swarming slaves task can run. One important dimension is 'os',
+          which defines platform flavor to run the task on. See Swarming doc.
+      env: key-value mapping with additional environment variables to add to
+          environment before launching the task executable.
+      priority: integer [0, 255] that defines how urgent the task is.
+          Lower value corresponds to higher priority. Swarming service executes
+          tasks with higher priority first.
+      shards: how many concurrent shards to run, makes sense only for
+          isolated tests based on gtest. Swarming uses GTEST_SHARD_INDEX
+          and GTEST_TOTAL_SHARDS environment variables to tell the executable
+          what shard to run.
+      buildername: buildbot builder this task was triggered from.
+      buildnumber: build number of a build this task was triggered from.
+      expiration: number of schedule until the task shouldn't even be run if it
+          hadn't started yet.
+      user: user that requested this task, if applicable.
+      io_timeout: number of seconds that the task is allowed to not emit any
+          stdout bytes, after which it is forcibly killed.
+      hard_timeout: number of seconds for which the task is allowed to run,
+          after which it is forcibly killed.
+      idempotent: True if the results from a previous task can be reused. E.g.
+          this task has no side-effects.
+      extra_args: list of command line arguments to pass to isolated tasks.
+      task_output_dir: if defined, the directory where task results are placed
+          during the collect step.
+      build_properties: An optional dict containing various build properties.
+          These are typically but not necessarily the properties emitted by
+          bot_update.
+      merge: An optional dict containing:
+          "script": path to a script to call to post process and merge the
+              collected outputs from the tasks.
+          "args": an optional list of additional arguments to pass to the
+              above script.
+    """
+    self._trigger_output = None
+    self.build_properties = build_properties
+    self.buildername = buildername
+    self.buildnumber = buildnumber
+    self.cipd_packages = cipd_packages
+    self.collect_step = collect_step
+    self.dimensions = dimensions.copy()
+    self.env = env.copy()
+    self.expiration = expiration
+    self.extra_args = tuple(extra_args or [])
+    self.hard_timeout = hard_timeout
+    self.idempotent = idempotent
+    self.ignore_task_failure = ignore_task_failure
+    self.io_timeout = io_timeout
+    self.isolated_hash = isolated_hash
+    self.merge = merge or {}
+    self.priority = priority
+    self.shards = shards
+    self.tags = set()
+    self.task_output_dir = task_output_dir
+    self.title = title
+    self.user = user
+
+  @property
+  def task_name(self):
+    """Name of this task, derived from its other properties.
+
+    The task name is purely to make sense of the task and is not used in any
+    other way.
+    """
+    out = '%s/%s/%s' % (
+        self.title, self.dimensions['os'], self.isolated_hash[:10])
+    if self.buildername:  # pragma: no cover
+      out += '/%s/%s' % (self.buildername, self.buildnumber or -1)
+    return out
+
+  @property
+  def trigger_output(self):
+    """JSON results of 'trigger' step or None if not triggered."""
+    return self._trigger_output
+
+  def get_shard_view_url(self, index):
+    """Returns URL of HTML page with shard details or None if not available.
+
+    Works only after the task has been successfully triggered.
+    """
+    if self._trigger_output and self._trigger_output.get('tasks'):
+      for shard_dict in self._trigger_output['tasks'].itervalues():
+        if shard_dict['shard_index'] == index:
+          return shard_dict['view_url']
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/basic.json b/infra/bots/recipe_modules/swarming/examples/full.expected/basic.json
new file mode 100644
index 0000000..9a55d5c
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/basic.json
@@ -0,0 +1,683 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "linux",
+      "--verbose"
+    ],
+    "name": "archive for linux",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "mac",
+      "--verbose"
+    ],
+    "name": "archive for mac",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "2",
+      "--task-name",
+      "hello_world/Ubuntu-14.04/hash_for_l",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_linux",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "os:linux",
+      "--tag",
+      "stepname:hello_world",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_linux"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Ubuntu-14.04/hash_for_l\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Ubuntu-14.04/hash_for_l:2:0\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Ubuntu-14.04/hash_for_l:2:1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 1, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10100\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10100\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@shard #1@https://chromium-swarm-dev.appspot.com/user/task/10100@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Mac-10.9/hash_for_m",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Mac-10.9",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_mac",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Mac-10.9",
+      "--tag",
+      "os:mac",
+      "--tag",
+      "stepname:hello_world on Mac-10.9",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_mac"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Mac-10.9",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Mac-10.9'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Mac-10.9/hash_for_m\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Mac-10.9/hash_for_m\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Ubuntu-14.04/hash_for_l\", \"tasks\": {\"hello_world/Ubuntu-14.04/hash_for_l:2:0\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}, \"hello_world/Ubuntu-14.04/hash_for_l:2:1\": {\"shard_index\": 1, \"task_id\": \"10100\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10100\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0100\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@shard #1 isolated out@blah@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Mac-10.9/hash_for_m\", \"tasks\": {\"hello_world/Mac-10.9/hash_for_m\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Mac-10.9",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Mac-10.9'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/show_isolated_out_in_collect_step.json b/infra/bots/recipe_modules/swarming/examples/full.expected/show_isolated_out_in_collect_step.json
new file mode 100644
index 0000000..69019a8
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/show_isolated_out_in_collect_step.json
@@ -0,0 +1,297 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "rietveld:https://codereview.chromium.org/123/#ps1001",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/show_shards_in_collect_step.json b/infra/bots/recipe_modules/swarming/examples/full.expected/show_shards_in_collect_step.json
new file mode 100644
index 0000000..f4c2e7b
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/show_shards_in_collect_step.json
@@ -0,0 +1,299 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "rietveld:https://codereview.chromium.org/123/#ps1001",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_expired_new.json b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_expired_new.json
new file mode 100644
index 0000000..5fa8e68
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_expired_new.json
@@ -0,0 +1,281 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0100\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": \"EXPIRED\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LOG_LINE@no_results_exc@Infra Failure in Shard #0 failed: There isn't enough capacity to run your test@@@",
+      "@@@STEP_LOG_END@no_results_exc@@@",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_expired_old.json b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_expired_old.json
new file mode 100644
index 0000000..f0e2d64
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_expired_old.json
@@ -0,0 +1,281 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0100\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 48, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LOG_LINE@no_results_exc@Infra Failure in Shard #0 failed: There isn't enough capacity to run your test@@@",
+      "@@@STEP_LOG_END@no_results_exc@@@",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_timeout_new.json b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_timeout_new.json
new file mode 100644
index 0000000..46ca02c
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_timeout_new.json
@@ -0,0 +1,278 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0100\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": \"TIMED_OUT\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_timeout_old.json b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_timeout_old.json
new file mode 100644
index 0000000..a2741eb
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/swarming_timeout_old.json
@@ -0,0 +1,278 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0100\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 64, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.expected/trybot.json b/infra/bots/recipe_modules/swarming/examples/full.expected/trybot.json
new file mode 100644
index 0000000..7b23e8c
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.expected/trybot.json
@@ -0,0 +1,298 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "archive",
+      "--isolate",
+      "[START_DIR]/swarming.client/example/payload/hello_world.isolate",
+      "--isolated",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/hello_world.isolated",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--config-variable",
+      "OS",
+      "win",
+      "--verbose"
+    ],
+    "name": "archive for win",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--isolate-server",
+      "https://isolateserver-dev.appspot.com",
+      "--priority",
+      "30",
+      "--shards",
+      "1",
+      "--task-name",
+      "hello_world/Windows-7-SP1/hash_for_w",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "3600",
+      "--io-timeout",
+      "1200",
+      "--hard-timeout",
+      "3600",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "none",
+      "--dimension",
+      "os",
+      "Windows-7-SP1",
+      "--env",
+      "TESTING",
+      "1",
+      "--tag",
+      "data:hash_for_win",
+      "--tag",
+      "master:tryserver",
+      "--tag",
+      "name:hello_world",
+      "--tag",
+      "os:Windows-7-SP1",
+      "--tag",
+      "os:win",
+      "--tag",
+      "rietveld:https://codereview.chromium.org/123/#ps1001",
+      "--tag",
+      "stepname:hello_world on Windows-7-SP1",
+      "--verbose",
+      "--idempotent",
+      "--user",
+      "joe",
+      "--cipd-package",
+      "bin:super/awesome/pkg:git_revision:deadbeef",
+      "--isolated",
+      "hash_for_win"
+    ],
+    "infra_step": true,
+    "name": "[trigger] hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hello_world/Windows-7-SP1/hash_for_w\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm-dev.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "running something locally"
+    ],
+    "name": "local step"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[TMP_BASE]/hello_isolated_world_tmp_1/task_output_dir",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm-dev.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--verbose",
+      "--json",
+      "{\"base_task_name\": \"hello_world/Windows-7-SP1/hash_for_w\", \"tasks\": {\"hello_world/Windows-7-SP1/hash_for_w\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm-dev.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "hello_world on Windows-7-SP1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Windows-7-SP1'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[TMP_BASE]/hello_isolated_world_tmp_1"
+    ],
+    "infra_step": true,
+    "name": "rmtree remove temp dir"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming/examples/full.py b/infra/bots/recipe_modules/swarming/examples/full.py
new file mode 100644
index 0000000..f61576a
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/examples/full.py
@@ -0,0 +1,242 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+import json
+
+DEPS = [
+  'file',
+  'isolate',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'swarming',
+  'swarming_client',
+]
+
+from recipe_engine.recipe_api import Property
+
+PROPERTIES = {
+  'platforms': Property(default=('win',)),
+  'show_isolated_out_in_collect_step': Property(default=True),
+  'show_shards_in_collect_step': Property(default=False),
+  'gtest_task': Property(default=False),
+  #'isolated_script_task': Property(default=False),
+  'merge': Property(default=None),
+}
+
+def RunSteps(api, platforms, show_isolated_out_in_collect_step,
+             show_shards_in_collect_step, gtest_task, merge):
+  # Checkout swarming client.
+  api.swarming_client.checkout('master')
+
+  # Ensure swarming_client version is fresh enough.
+  api.swarming.check_client_version(step_test_data=(0, 8, 6))
+
+  # Configure isolate & swarming modules (this is optional).
+  api.isolate.isolate_server = 'https://isolateserver-dev.appspot.com'
+  api.swarming.swarming_server = 'https://chromium-swarm-dev.appspot.com'
+  api.swarming.add_default_tag('master:tryserver')
+  api.swarming.default_expiration = 60*60
+  api.swarming.default_hard_timeout = 60*60
+  api.swarming.default_io_timeout = 20*60
+  api.swarming.default_idempotent = True
+  api.swarming.default_priority = 30
+  api.swarming.default_user = 'joe'
+  api.swarming.set_default_env('TESTING', '1')
+  api.swarming.verbose = True
+
+  api.swarming.set_default_dimension('inexistent', None)
+
+  api.swarming.show_shards_in_collect_step = show_shards_in_collect_step
+  api.swarming.show_isolated_out_in_collect_step = (
+      show_isolated_out_in_collect_step)
+
+  try:
+    # Testing ReadOnlyDict.__setattr__() coverage.
+    api.swarming.default_dimensions['invalid'] = 'foo'
+  except TypeError:
+    pass
+  try:
+    api.swarming.default_env['invalid'] = 'foo'
+  except TypeError:
+    pass
+
+  # Create a temp dir to put *.isolated files into.
+  temp_dir = api.path.mkdtemp('hello_isolated_world')
+
+  # Prepare a bunch of swarming tasks to run hello_world on multiple platforms.
+  tasks = []
+  for platform in platforms:
+    # Isolate example hello_world.isolate from swarming client repo.
+    # TODO(vadimsh): Add a thin wrapper around isolate.py to 'isolate' module?
+    step_result = api.python(
+        'archive for %s' % platform,
+        api.swarming_client.path.join('isolate.py'),
+        [
+          'archive',
+          '--isolate', api.swarming_client.path.join(
+              'example', 'payload', 'hello_world.isolate'),
+          '--isolated', temp_dir.join('hello_world.isolated'),
+          '--isolate-server', api.isolate.isolate_server,
+          '--config-variable', 'OS', platform,
+          '--verbose',
+        ], stdout=api.raw_io.output_text())
+    # TODO(vadimsh): Pass result from isolate.py though --output-json option.
+    isolated_hash = step_result.stdout.split()[0].strip()
+
+    # Create a task to run the isolated file on swarming, set OS dimension.
+    # Also generate code coverage for multi-shard case by triggering multiple
+    # shards on Linux.
+    task = api.swarming.task('hello_world', isolated_hash,
+                             task_output_dir=temp_dir.join('task_output_dir'))
+    task.dimensions['os'] = api.swarming.prefered_os_dimension(platform)
+    task.shards = 2 if platform == 'linux' else 1
+    task.tags.add('os:' + platform)
+    if api.swarming_client.get_script_version('swarming.py') >= (0, 8, 6):
+      task.cipd_packages = [
+          ('bin', 'super/awesome/pkg', 'git_revision:deadbeef')]
+    tasks.append(task)
+
+  # Launch all tasks.
+  for task in tasks:
+    step_result = api.swarming.trigger_task(task)
+    assert step_result.swarming_task in tasks
+
+  # Recipe can do something useful here locally while tasks are
+  # running on swarming.
+  api.step('local step', ['echo', 'running something locally'])
+
+  # Wait for all tasks to complete.
+  for task in tasks:
+    step_result = api.swarming.collect_task(task)
+    data = step_result.swarming.summary
+
+    state = data['shards'][0]['state']
+    if api.swarming.State.COMPLETED == state:
+      state_name = api.swarming.State.to_string(state)
+      assert 'Completed' == state_name, state_name
+    assert step_result.swarming_task in tasks
+
+  # Cleanup.
+  api.file.rmtree('remove temp dir', temp_dir)
+
+
+def GenTests(api):
+  yield (
+      api.test('basic') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.step_data(
+          'archive for linux',
+          stdout=api.raw_io.output_text(
+              'hash_for_linux hello_world.isolated')) +
+      api.step_data(
+          'archive for mac',
+          stdout=api.raw_io.output_text('hash_for_mac hello_world.isolated')) +
+      api.properties(platforms=('win', 'linux', 'mac')))
+
+  yield (
+      api.test('trybot') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.properties(
+          rietveld='https://codereview.chromium.org',
+          issue='123',
+          patchset='1001'))
+
+  yield (
+      api.test('show_shards_in_collect_step') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.properties(
+          rietveld='https://codereview.chromium.org',
+          issue='123',
+          patchset='1001',
+          show_shards_in_collect_step=True))
+
+  yield (
+      api.test('show_isolated_out_in_collect_step') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.properties(
+          rietveld='https://codereview.chromium.org',
+          issue='123',
+          patchset='1001',
+          show_isolated_out_in_collect_step=False))
+
+  data = {
+    'shards': [
+      {
+        '': '',
+      }
+    ]
+  }
+
+  data = {
+    'shards': [
+      {
+        'abandoned_ts': '2014-09-25T01:41:00.123',
+        'bot_id': 'vm30',
+        'completed_ts': None,
+        'created_ts': '2014-09-25T01:41:00.123',
+        'durations': None,
+        'exit_codes': [],
+        'failure': False,
+        'id': '148aa78d7aa0100',
+        'internal_failure': False,
+        'isolated_out': None,
+        'modified_ts': '2014-09-25 01:42:00',
+        'name': 'heartbeat-canary-2014-09-25_01:41:55-os=Windows',
+        'outputs': [],
+        'started_ts': '2014-09-25T01:42:11.123',
+        'state': 0x30, # EXPIRED (old)
+        'try_number': None,
+        'user': 'unknown',
+      }
+    ],
+  }
+
+  yield (
+      api.test('swarming_expired_old') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data)))
+
+  data['shards'][0]['state'] = 'EXPIRED'
+  yield (
+      api.test('swarming_expired_new') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data)))
+
+  data['shards'][0]['state'] = 0x40  # TIMED_OUT (old)
+  yield (
+      api.test('swarming_timeout_old') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data)))
+
+  data['shards'][0]['state'] = 'TIMED_OUT'  # TIMED_OUT (old)
+  yield (
+      api.test('swarming_timeout_new') +
+      api.step_data(
+          'archive for win',
+          stdout=api.raw_io.output_text('hash_for_win hello_world.isolated')) +
+      api.step_data('hello_world on Windows-7-SP1', api.swarming.summary(data)))
diff --git a/infra/bots/recipe_modules/swarming/resources/collect_task.py b/infra/bots/recipe_modules/swarming/resources/collect_task.py
new file mode 100755
index 0000000..cb4f15e
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/resources/collect_task.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# 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.
+
+import argparse
+import json
+import logging
+import os
+import subprocess
+import sys
+
+
+def collect_task(
+    collect_cmd, merge_script, build_properties, merge_arguments,
+    task_output_dir, output_json):
+  """Collect and merge the results of a task.
+
+  This is a relatively thin wrapper script around a `swarming.py collect`
+  command and a subsequent results merge to ensure that the recipe system
+  treats them as a single step. The results merge can either be the default
+  one provided by results_merger or a python script provided as merge_script.
+
+  Args:
+    collect_cmd: The `swarming.py collect` command to run. Should not contain
+      a --task-output-dir argument.
+    merge_script: A merge/postprocessing script that should be run to
+      merge the results. This script will be invoked as
+
+        <merge_script> \
+            [--build-properties <string JSON>] \
+            [merge arguments...] \
+            --summary-json <summary json> \
+            -o <merged json path> \
+            <shard json>...
+
+      where the merge arguments are the contents of merge_arguments_json.
+    build_properties: A string containing build information to
+      pass to the merge script in JSON form.
+    merge_arguments: A string containing additional arguments to pass to
+      the merge script in JSON form.
+    task_output_dir: A path to a directory in which swarming will write the
+      output of the task, including a summary JSON and all of the individual
+      shard results.
+    output_json: A path to a JSON file to which the merged results should be
+      written. The merged results should be in the JSON Results File Format
+      (https://www.chromium.org/developers/the-json-test-results-format)
+      and may optionally contain a top level "links" field that may contain a
+      dict mapping link text to URLs, for a set of links that will be included
+      in the buildbot output.
+  Returns:
+    The exit code of collect_cmd or merge_cmd.
+  """
+  logging.debug('Using task_output_dir: %r', task_output_dir)
+  if os.path.exists(task_output_dir):
+    logging.warn('task_output_dir %r already exists!', task_output_dir)
+    existing_contents = []
+    try:
+      for p in os.listdir(task_output_dir):
+        existing_contents.append(os.path.join(task_output_dir, p))
+    except (OSError, IOError) as e:
+      logging.error('Error while examining existing task_output_dir: %s', e)
+
+    logging.warn('task_output_dir existing content: %r', existing_contents)
+
+  collect_cmd.extend(['--task-output-dir', task_output_dir])
+
+  logging.info('collect_cmd: %s', ' '.join(collect_cmd))
+  collect_result = subprocess.call(collect_cmd)
+  if collect_result != 0:
+    logging.warn('collect_cmd had non-zero return code: %s', collect_result)
+
+  task_output_dir_contents = []
+  try:
+    task_output_dir_contents.extend(
+        os.path.join(task_output_dir, p)
+        for p in os.listdir(task_output_dir))
+  except (OSError, IOError) as e:
+    logging.error('Error while processing task_output_dir: %s', e)
+
+  logging.debug('Contents of task_output_dir: %r', task_output_dir_contents)
+  if not task_output_dir_contents:
+    logging.warn(
+        'No files found in task_output_dir: %r',
+        task_output_dir)
+
+  task_output_subdirs = (
+      p for p in task_output_dir_contents
+      if os.path.isdir(p))
+  shard_json_files = [
+      os.path.join(subdir, 'output.json')
+      for subdir in task_output_subdirs]
+  extant_shard_json_files = [
+      f for f in shard_json_files if os.path.exists(f)]
+
+  if shard_json_files != extant_shard_json_files:
+    logging.warn(
+        'Expected output.json file missing: %r\nFound: %r\nExpected: %r\n',
+        set(shard_json_files) - set(extant_shard_json_files),
+        extant_shard_json_files,
+        shard_json_files)
+
+  if not extant_shard_json_files:
+    logging.warn(
+        'No shard json files found in task_output_dir: %r\nFound %r',
+        task_output_dir, task_output_dir_contents)
+
+  logging.debug('Found shard_json_files: %r', shard_json_files)
+
+  summary_json_file = os.path.join(task_output_dir, 'summary.json')
+
+  merge_result = 0
+
+  merge_cmd = [sys.executable, merge_script]
+  if build_properties:
+    merge_cmd.extend(('--build-properties', build_properties))
+  if os.path.exists(summary_json_file):
+    merge_cmd.extend(('--summary-json', summary_json_file))
+  else:
+    logging.warn('Summary json file missing: %r', summary_json_file)
+  if merge_arguments:
+    merge_cmd.extend(json.loads(merge_arguments))
+  merge_cmd.extend(('-o', output_json))
+  merge_cmd.extend(extant_shard_json_files)
+
+  logging.info('merge_cmd: %s', ' '.join(merge_cmd))
+  merge_result = subprocess.call(merge_cmd)
+  if merge_result != 0:
+    logging.warn('merge_cmd had non-zero return code: %s', merge_result)
+
+  if not os.path.exists(output_json):
+    logging.warn(
+        'merge_cmd did not create output_json file: %r', output_json)
+
+  return collect_result or merge_result
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--build-properties')
+  parser.add_argument('--merge-additional-args')
+  parser.add_argument('--merge-script', required=True)
+  parser.add_argument('--task-output-dir', required=True)
+  parser.add_argument('-o', '--output-json', required=True)
+  parser.add_argument('--verbose', action='store_true')
+  parser.add_argument('collect_cmd', nargs='+')
+
+  args = parser.parse_args()
+  if args.verbose:
+    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
+
+  return collect_task(
+      args.collect_cmd,
+      args.merge_script, args.build_properties, args.merge_additional_args,
+      args.task_output_dir, args.output_json)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/infra/bots/recipe_modules/swarming/resources/noop_merge.py b/infra/bots/recipe_modules/swarming/resources/noop_merge.py
new file mode 100755
index 0000000..740e0d3
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/resources/noop_merge.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# 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.
+
+import argparse
+import json
+import shutil
+import sys
+
+
+def noop_merge(output_json, jsons_to_merge):
+  """Use the first supplied JSON as the output JSON.
+
+  Primarily intended for unsharded tasks.
+
+  Args:
+    output_json: A path to a JSON file to which the results should be written.
+    jsons_to_merge: A list of paths to JSON files.
+  """
+  if len(jsons_to_merge) > 1:
+    print >> sys.stderr, (
+        'Multiple JSONs provided: %s' % ','.join(jsons_to_merge))
+    return 1
+  if jsons_to_merge:
+    shutil.copyfile(jsons_to_merge[0], output_json)
+  else:
+    with open(output_json, 'w') as f:
+      json.dump({}, f)
+  return 0
+
+
+def main(raw_args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--build-properties', help=argparse.SUPPRESS)
+  parser.add_argument('--summary-json', help=argparse.SUPPRESS)
+  parser.add_argument('-o', '--output-json', required=True)
+  parser.add_argument('jsons_to_merge', nargs='*')
+
+  args = parser.parse_args(raw_args)
+
+  return noop_merge(args.output_json, args.jsons_to_merge)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/infra/bots/recipe_modules/swarming/resources/results_merger.py b/infra/bots/recipe_modules/swarming/resources/results_merger.py
new file mode 100755
index 0000000..3ea0453
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/resources/results_merger.py
@@ -0,0 +1,278 @@
+# 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.
+
+import copy
+import json
+import sys
+
+# These fields must appear in the test result output
+REQUIRED = {
+    'interrupted',
+    'num_failures_by_type',
+    'seconds_since_epoch',
+    'tests',
+    }
+
+# These fields are optional, but must have the same value on all shards
+OPTIONAL_MATCHING = (
+    'builder_name',
+    'build_number',
+    'chromium_revision',
+    'has_pretty_patch',
+    'has_wdiff',
+    'path_delimiter',
+    'pixel_tests_enabled',
+    'random_order_seed',
+    )
+
+OPTIONAL_IGNORED = (
+    'layout_tests_dir',
+    )
+
+# These fields are optional and will be summed together
+OPTIONAL_COUNTS = (
+    'fixable',
+    'num_flaky',
+    'num_passes',
+    'num_regressions',
+    'skipped',
+    'skips',
+    )
+
+
+class MergeException(Exception):
+  pass
+
+
+def merge_test_results(shard_results_list):
+  """ Merge list of results.
+
+  Args:
+    shard_results_list: list of results to merge. All the results must have the
+      same format. Supported format are simplified JSON format & Chromium JSON
+      test results format version 3 (see
+      https://www.chromium.org/developers/the-json-test-results-format)
+
+  Returns:
+    a dictionary that represent the merged results. Its format follow the same
+    format of all results in |shard_results_list|.
+  """
+  if not shard_results_list:
+    return {}
+
+  if 'seconds_since_epoch' in shard_results_list[0]:
+    return _merge_json_test_result_format(shard_results_list)
+  else:
+    return _merge_simplified_json_format(shard_results_list)
+
+
+def _merge_simplified_json_format(shard_results_list):
+  # This code is specialized to the "simplified" JSON format that used to be
+  # the standard for recipes.
+
+  # These are the only keys we pay attention to in the output JSON.
+  merged_results = {
+    'successes': [],
+    'failures': [],
+    'valid': True,
+  }
+
+  for result_json in shard_results_list:
+    successes = result_json.get('successes', [])
+    failures = result_json.get('failures', [])
+    valid = result_json.get('valid', True)
+
+    if (not isinstance(successes, list) or not isinstance(failures, list) or
+        not isinstance(valid, bool)):
+      raise MergeException(
+        'Unexpected value type in %s' % result_json)  # pragma: no cover
+
+    merged_results['successes'].extend(successes)
+    merged_results['failures'].extend(failures)
+    merged_results['valid'] = merged_results['valid'] and valid
+  return merged_results
+
+
+def _merge_json_test_result_format(shard_results_list):
+  # This code is specialized to the Chromium JSON test results format version 3:
+  # https://www.chromium.org/developers/the-json-test-results-format
+
+  # These are required fields for the JSON test result format version 3.
+  merged_results = {
+    'tests': {},
+    'interrupted': False,
+    'version': 3,
+    'seconds_since_epoch': float('inf'),
+    'num_failures_by_type': {
+    }
+  }
+
+  # To make sure that we don't mutate existing shard_results_list.
+  shard_results_list = copy.deepcopy(shard_results_list)
+  for result_json in shard_results_list:
+    # TODO(tansell): check whether this deepcopy is actually neccessary.
+    result_json = copy.deepcopy(result_json)
+
+    # Check the version first
+    version = result_json.pop('version', -1)
+    if version != 3:
+      raise MergeException(  # pragma: no cover (covered by
+                             # results_merger_unittest).
+          'Unsupported version %s. Only version 3 is supported' % version)
+
+    # Check the results for each shard have the required keys
+    missing = REQUIRED - set(result_json)
+    if missing:
+      raise MergeException(  # pragma: no cover (covered by
+                             # results_merger_unittest).
+          'Invalid json test results (missing %s)' % missing)
+
+    # Curry merge_values for this result_json.
+    merge = lambda key, merge_func: merge_value(
+        result_json, merged_results, key, merge_func)
+
+    # Traverse the result_json's test trie & merged_results's test tries in
+    # DFS order & add the n to merged['tests'].
+    merge('tests', merge_tries)
+
+    # If any were interrupted, we are interrupted.
+    merge('interrupted', lambda x,y: x|y)
+
+    # Use the earliest seconds_since_epoch value
+    merge('seconds_since_epoch', min)
+
+    # Sum the number of failure types
+    merge('num_failures_by_type', sum_dicts)
+
+    # Optional values must match
+    for optional_key in OPTIONAL_MATCHING:
+      if optional_key not in result_json:
+        continue
+
+      if optional_key not in merged_results:
+        # Set this value to None, then blindly copy over it.
+        merged_results[optional_key] = None
+        merge(optional_key, lambda src, dst: src)
+      else:
+        merge(optional_key, ensure_match)
+
+    # Optional values ignored
+    for optional_key in OPTIONAL_IGNORED:
+      if optional_key in result_json:
+        merged_results[optional_key] = result_json.pop(
+            # pragma: no cover (covered by
+            # results_merger_unittest).
+            optional_key)
+
+    # Sum optional value counts
+    for count_key in OPTIONAL_COUNTS:
+      if count_key in result_json:  # pragma: no cover
+        # TODO(mcgreevy): add coverage.
+        merged_results.setdefault(count_key, 0)
+        merge(count_key, lambda a, b: a+b)
+
+    if result_json:
+      raise MergeException(  # pragma: no cover (covered by
+                             # results_merger_unittest).
+          'Unmergable values %s' % result_json.keys())
+
+  return merged_results
+
+
+def merge_tries(source, dest):
+  """ Merges test tries.
+
+  This is intended for use as a merge_func parameter to merge_value.
+
+  Args:
+      source: A result json test trie.
+      dest: A json test trie merge destination.
+  """
+  # merge_tries merges source into dest by performing a lock-step depth-first
+  # traversal of dest and source.
+  # pending_nodes contains a list of all sub-tries which have been reached but
+  # need further merging.
+  # Each element consists of a trie prefix, and a sub-trie from each of dest
+  # and source which is reached via that prefix.
+  pending_nodes = [('', dest, source)]
+  while pending_nodes:
+    prefix, dest_node, curr_node = pending_nodes.pop()
+    for k, v in curr_node.iteritems():
+      if k in dest_node:
+        if not isinstance(v, dict):
+          raise MergeException(
+              "%s:%s: %r not mergable, curr_node: %r\ndest_node: %r" % (
+                  prefix, k, v, curr_node, dest_node))
+        pending_nodes.append(("%s:%s" % (prefix, k), dest_node[k], v))
+      else:
+        dest_node[k] = v
+  return dest
+
+
+def ensure_match(source, dest):
+  """ Returns source if it matches dest.
+
+  This is intended for use as a merge_func parameter to merge_value.
+
+  Raises:
+      MergeException if source != dest
+  """
+  if source != dest:
+    raise MergeException(  # pragma: no cover (covered by
+                           # results_merger_unittest).
+        "Values don't match: %s, %s" % (source, dest))
+  return source
+
+
+def sum_dicts(source, dest):
+  """ Adds values from source to corresponding values in dest.
+
+  This is intended for use as a merge_func parameter to merge_value.
+  """
+  for k, v in source.iteritems():
+    dest.setdefault(k, 0)
+    dest[k] += v
+
+  return dest
+
+
+def merge_value(source, dest, key, merge_func):
+  """ Merges a value from source to dest.
+
+  The value is deleted from source.
+
+  Args:
+    source: A dictionary from which to pull a value, identified by key.
+    dest: The dictionary into to which the value is to be merged.
+    key: The key which identifies the value to be merged.
+    merge_func(src, dst): A function which merges its src into dst,
+        and returns the result. May modify dst. May raise a MergeException.
+
+  Raises:
+    MergeException if the values can not be merged.
+  """
+  try:
+    dest[key] = merge_func(source[key], dest[key])
+  except MergeException as e:
+    e.message = "MergeFailure for %s\n%s" % (key, e.message)
+    e.args = tuple([e.message] + list(e.args[1:]))
+    raise
+  del source[key]
+
+
+def main(files):
+  if len(files) < 2:
+    sys.stderr.write("Not enough JSON files to merge.\n")
+    return 1
+  sys.stderr.write('Starting with %s\n' % files[0])
+  result = json.load(open(files[0]))
+  for f in files[1:]:
+    sys.stderr.write('Merging %s\n' % f)
+    result = merge_test_results([result, json.load(open(f))])
+  print json.dumps(result)
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main(sys.argv[1:]))
diff --git a/infra/bots/recipe_modules/swarming/resources/standard_gtest_merge.py b/infra/bots/recipe_modules/swarming/resources/standard_gtest_merge.py
new file mode 100755
index 0000000..ca3abcf
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/resources/standard_gtest_merge.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+# 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.
+
+import argparse
+import json
+import os
+import shutil
+import sys
+import tempfile
+import traceback
+
+from common import gtest_utils
+from slave import annotation_utils
+from slave import slave_utils
+
+
+MISSING_SHARDS_MSG = r"""Missing results from the following shard(s): %s
+
+This can happen in following cases:
+  * Test failed to start (missing *.dll/*.so dependency for example)
+  * Test crashed or hung
+  * Task expired because there are not enough bots available and are all used
+  * Swarming service experienced problems
+
+Please examine logs to figure out what happened.
+"""
+
+
+def emit_warning(title, log=None):
+  print '@@@STEP_WARNINGS@@@'
+  print title
+  if log:
+    slave_utils.WriteLogLines(title, log.split('\n'))
+
+
+def merge_shard_results(summary_json, jsons_to_merge):
+  """Reads JSON test output from all shards and combines them into one.
+
+  Returns dict with merged test output on success or None on failure. Emits
+  annotations.
+  """
+  # summary.json is produced by swarming.py itself. We are mostly interested
+  # in the number of shards.
+  try:
+    with open(summary_json) as f:
+      summary = json.load(f)
+  except (IOError, ValueError):
+    emit_warning(
+        'summary.json is missing or can not be read',
+        'Something is seriously wrong with swarming_client/ or the bot.')
+    return None
+
+  # Merge all JSON files together. Keep track of missing shards.
+  merged = {
+    'all_tests': set(),
+    'disabled_tests': set(),
+    'global_tags': set(),
+    'missing_shards': [],
+    'per_iteration_data': [],
+    'swarming_summary': summary,
+  }
+  for index, result in enumerate(summary['shards']):
+    if result is not None:
+      # Author note: this code path doesn't trigger convert_to_old_format() in
+      # client/swarming.py, which means the state enum is saved in its string
+      # name form, not in the number form.
+      state = result.get('state')
+      if state == u'BOT_DIED':
+        emit_warning('Shard #%d had a Swarming internal failure' % index)
+      elif state == u'EXPIRED':
+        emit_warning('There wasn\'t enough capacity to run your test')
+      elif state == u'TIMED_OUT':
+        emit_warning(
+            'Test runtime exceeded allocated time',
+            'Either it ran for too long (hard timeout) or it didn\'t produce '
+            'I/O for an extended period of time (I/O timeout)')
+      elif state == u'COMPLETED':
+        json_data, err_msg = load_shard_json(index, jsons_to_merge)
+        if json_data:
+          # Set-like fields.
+          for key in ('all_tests', 'disabled_tests', 'global_tags'):
+            merged[key].update(json_data.get(key), [])
+
+          # 'per_iteration_data' is a list of dicts. Dicts should be merged
+          # together, not the 'per_iteration_data' list itself.
+          merged['per_iteration_data'] = merge_list_of_dicts(
+              merged['per_iteration_data'],
+              json_data.get('per_iteration_data', []))
+          continue
+        else:
+          emit_warning('Task ran but no result was found: %s' % err_msg)
+      else:
+        emit_warning('Invalid Swarming task state: %s' % state)
+    merged['missing_shards'].append(index)
+
+  # If some shards are missing, make it known. Continue parsing anyway. Step
+  # should be red anyway, since swarming.py return non-zero exit code in that
+  # case.
+  if merged['missing_shards']:
+    as_str = ', '.join(map(str, merged['missing_shards']))
+    emit_warning(
+        'some shards did not complete: %s' % as_str,
+        MISSING_SHARDS_MSG % as_str)
+    # Not all tests run, combined JSON summary can not be trusted.
+    merged['global_tags'].add('UNRELIABLE_RESULTS')
+
+  # Convert to jsonish dict.
+  for key in ('all_tests', 'disabled_tests', 'global_tags'):
+    merged[key] = sorted(merged[key])
+  return merged
+
+
+OUTPUT_JSON_SIZE_LIMIT = 100 * 1024 * 1024  # 100 MB
+
+
+def load_shard_json(index, jsons_to_merge):
+  """Reads JSON output of the specified shard.
+
+  Args:
+    output_dir: The directory in which to look for the JSON output to load.
+    index: The index of the shard to load data for.
+
+  Returns: A tuple containing:
+    * The contents of path, deserialized into a python object.
+    * An error string.
+    (exactly one of the tuple elements will be non-None).
+  """
+  # 'output.json' is set in swarming/api.py, gtest_task method.
+  matching_json_files = [
+      j for j in jsons_to_merge
+      if (os.path.basename(j) == 'output.json'
+          and os.path.basename(os.path.dirname(j)) == str(index))]
+
+  if not matching_json_files:
+    print >> sys.stderr, 'shard %s test output missing' % index
+    return (None, 'shard %s test output was missing' % index)
+  elif len(matching_json_files) > 1:
+    print >> sys.stderr, 'duplicate test output for shard %s' % index
+    return (None, 'shard %s test output was duplicated' % index)
+
+  path = matching_json_files[0]
+
+  try:
+    filesize = os.stat(path).st_size
+    if filesize > OUTPUT_JSON_SIZE_LIMIT:
+      print >> sys.stderr, 'output.json is %d bytes. Max size is %d' % (
+           filesize, OUTPUT_JSON_SIZE_LIMIT)
+      return (None, 'shard %s test output exceeded the size limit' % index)
+
+    with open(path) as f:
+      return (json.load(f), None)
+  except (IOError, ValueError, OSError) as e:
+    print >> sys.stderr, 'Missing or invalid gtest JSON file: %s' % path
+    print >> sys.stderr, '%s: %s' % (type(e).__name__, e)
+
+    return (None, 'shard %s test output was missing or invalid' % index)
+
+
+def merge_list_of_dicts(left, right):
+  """Merges dicts left[0] with right[0], left[1] with right[1], etc."""
+  output = []
+  for i in xrange(max(len(left), len(right))):
+    left_dict = left[i] if i < len(left) else {}
+    right_dict = right[i] if i < len(right) else {}
+    merged_dict = left_dict.copy()
+    merged_dict.update(right_dict)
+    output.append(merged_dict)
+  return output
+
+
+def standard_gtest_merge(
+    output_json, summary_json, jsons_to_merge):
+
+  output = merge_shard_results(summary_json, jsons_to_merge)
+  with open(output_json, 'wb') as f:
+    json.dump(output, f)
+
+  return 0
+
+
+def main(raw_args):
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--build-properties')
+  parser.add_argument('--summary-json')
+  parser.add_argument('-o', '--output-json', required=True)
+  parser.add_argument('jsons_to_merge', nargs='*')
+
+  args = parser.parse_args(raw_args)
+
+  return standard_gtest_merge(
+      args.output_json, args.summary_json, args.jsons_to_merge)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/infra/bots/recipe_modules/swarming/resources/standard_isolated_script_merge.py b/infra/bots/recipe_modules/swarming/resources/standard_isolated_script_merge.py
new file mode 100755
index 0000000..e3c860f
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/resources/standard_isolated_script_merge.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# 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.
+
+import argparse
+import json
+import sys
+
+import results_merger
+
+
+def StandardIsolatedScriptMerge(output_json, jsons_to_merge):
+  """Merge the contents of one or more results JSONs into a single JSON.
+
+  Args:
+    output_json: A path to a JSON file to which the merged results should be
+      written.
+    jsons_to_merge: A list of paths to JSON files that should be merged.
+  """
+  shard_results_list = []
+  for j in jsons_to_merge:
+    with open(j) as f:
+      shard_results_list.append(json.load(f))
+  merged_results = results_merger.merge_test_results(shard_results_list)
+
+  with open(output_json, 'w') as f:
+    json.dump(merged_results, f)
+
+  return 0
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-o', '--output-json', required=True)
+  parser.add_argument('--build-properties', help=argparse.SUPPRESS)
+  parser.add_argument('--summary-json', help=argparse.SUPPRESS)
+  parser.add_argument('jsons_to_merge', nargs='*')
+
+  args = parser.parse_args()
+  return StandardIsolatedScriptMerge(args.output_json, args.jsons_to_merge)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/infra/bots/recipe_modules/swarming/state.py b/infra/bots/recipe_modules/swarming/state.py
new file mode 100644
index 0000000..3834b90
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/state.py
@@ -0,0 +1,46 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+class State(object):
+  """Copied from appengine/swarming/server/task_result.py.
+
+  KEEP IN SYNC.
+
+  Used to parse the 'state' value in task result.
+  """
+  RUNNING = 0x10    # 16
+  PENDING = 0x20    # 32
+  EXPIRED = 0x30    # 48
+  TIMED_OUT = 0x40  # 64
+  BOT_DIED = 0x50   # 80
+  CANCELED = 0x60   # 96
+  COMPLETED = 0x70  # 112
+
+  STATES = (
+      RUNNING, PENDING, EXPIRED, TIMED_OUT, BOT_DIED, CANCELED, COMPLETED)
+  STATES_RUNNING = (RUNNING, PENDING)
+  STATES_NOT_RUNNING = (EXPIRED, TIMED_OUT, BOT_DIED, CANCELED, COMPLETED)
+  STATES_DONE = (TIMED_OUT, COMPLETED)
+  STATES_ABANDONED = (EXPIRED, BOT_DIED, CANCELED)
+
+  _NAMES = {
+    RUNNING: 'Running',
+    PENDING: 'Pending',
+    EXPIRED: 'Expired',
+    TIMED_OUT: 'Execution timed out',
+    BOT_DIED: 'Bot died',
+    CANCELED: 'User canceled',
+    COMPLETED: 'Completed',
+  }
+
+  @classmethod
+  def to_string(cls, state):
+    """Returns a user-readable string representing a State."""
+    return cls._NAMES[state]
diff --git a/infra/bots/recipe_modules/swarming/test_api.py b/infra/bots/recipe_modules/swarming/test_api.py
new file mode 100644
index 0000000..3066fd6
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming/test_api.py
@@ -0,0 +1,56 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+from recipe_engine import recipe_test_api
+
+import state
+
+class SwarmingTestApi(recipe_test_api.RecipeTestApi):
+
+  @recipe_test_api.placeholder_step_data
+  def summary(self, data):
+    return self.m.json.output(data)
+
+  def canned_summary_output(
+      self, shards=1, failure=False, internal_failure=False):
+    return self.summary({
+      'shards': [
+        {
+          'abandoned_ts': None,
+          'bot_id': 'vm30',
+          'completed_ts': '2014-09-25T01:42:00.123',
+          'created_ts': '2014-09-25T01:41:00.123',
+          'durations': [5.7, 31.5],
+          'exit_codes': [0, 0],
+          'failure': failure,
+          'id': '148aa78d7aa%02d00' % i,
+          'internal_failure': internal_failure,
+          'isolated_out': {
+            'isolated': 'abc123',
+            'isolatedserver': 'https://isolateserver.appspot.com',
+            'namespace': 'default-gzip',
+            'view_url': 'blah',
+          },
+          'modified_ts': '2014-09-25 01:42:00',
+          'name': 'heartbeat-canary-2014-09-25_01:41:55-os=Windows',
+          'outputs': [
+            'Heart beat succeeded on win32.\n',
+            'Foo',
+          ],
+          'outputs_ref': {
+            'view_url': 'blah',
+          },
+          'started_ts': '2014-09-25T01:42:11.123',
+          'state': state.State.COMPLETED,
+          'try_number': 1,
+          'user': 'unknown',
+        } for i in xrange(shards)
+      ],
+    })
diff --git a/infra/bots/recipe_modules/swarming_client/OWNERS b/infra/bots/recipe_modules/swarming_client/OWNERS
new file mode 100644
index 0000000..8da4472
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming_client/OWNERS
@@ -0,0 +1,2 @@
+martiniss@chromium.org
+vadimsh@chromium.org
diff --git a/infra/bots/recipe_modules/swarming_client/__init__.py b/infra/bots/recipe_modules/swarming_client/__init__.py
new file mode 100644
index 0000000..76ce274
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming_client/__init__.py
@@ -0,0 +1,18 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+DEPS = [
+  'depot_tools/git',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+]
diff --git a/infra/bots/recipe_modules/swarming_client/api.py b/infra/bots/recipe_modules/swarming_client/api.py
new file mode 100644
index 0000000..13e6c01
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming_client/api.py
@@ -0,0 +1,135 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+from recipe_engine import recipe_api
+
+
+class SwarmingClientApi(recipe_api.RecipeApi):
+  """Code that both isolate and swarming recipe modules depend on.
+
+  Both swarming and isolate scripts live in a single repository called
+  'swarming client'. This module include common functionality like finding
+  existing swarming client checkout, fetching a new one, getting version of
+  a swarming script, etc.
+  """
+
+  def __init__(self, **kwargs):
+    super(SwarmingClientApi, self).__init__(**kwargs)
+    self._client_path = None
+    self._script_version = {}
+
+  def checkout(self, revision=None, curl_trace_file=None, can_fail_build=True):
+    """Returns a step to checkout swarming client into a separate directory.
+
+    Ordinarily swarming client is checked out via Chromium DEPS into
+    src/tools/swarming_client. This step configures recipe module to use
+    a separate checkout.
+
+    If |revision| is None, this requires the build property
+    'parent_got_swarming_client_revision' to be present, and raises an exception
+    otherwise. Fail-fast behavior is used because if machines silently fell back
+    to checking out the entire workspace, that would cause dramatic increases
+    in cycle time if a misconfiguration were made and it were no longer possible
+    for the bot to check out swarming_client separately.
+    """
+    # If the following line throws an exception, it either means the
+    # bot is misconfigured, or, if you're testing locally, that you
+    # need to pass in some recent legal revision for this property.
+    if revision is None:
+      revision = self.m.properties['parent_got_swarming_client_revision']
+    self._client_path = self.m.path['start_dir'].join('swarming.client')
+    self.m.git.checkout(
+        url='https://chromium.googlesource.com/external/swarming.client.git',
+        ref=revision,
+        dir_path=self._client_path,
+        step_suffix='swarming_client',
+        curl_trace_file=curl_trace_file,
+        can_fail_build=can_fail_build)
+
+  @property
+  def path(self):
+    """Returns path to a swarming client checkout.
+
+    It's subdirectory of Chromium src/ checkout or a separate directory if
+    'checkout_swarming_client' step was used.
+    """
+    if self._client_path:
+      return self._client_path
+    # Default is swarming client path in chromium src/ checkout.
+    # TODO(vadimsh): This line assumes the recipe is working with
+    # Chromium checkout.
+    return self.m.path['checkout'].join('tools', 'swarming_client')
+
+  def query_script_version(self, script, step_test_data=None):
+    """Yields a step to query a swarming script for its version.
+
+    Version tuple is later accessible via 'get_script_version' method. If
+    |step_test_data| is given, it is a tuple with version to use in expectation
+    tests by default.
+
+    Does nothing if script's version is already known.
+    """
+    # Convert |step_test_data| from tuple of ints back to a version string.
+    if step_test_data:
+      assert isinstance(step_test_data, tuple)
+      assert all(isinstance(x, int) for x in step_test_data)
+      as_text = '.'.join(map(str, step_test_data))
+      step_test_data_cb = lambda: self.m.raw_io.test_api.stream_output(as_text)
+    else:
+      step_test_data_cb = None
+
+    if script not in self._script_version:
+      try:
+        self.m.python(
+          name='%s --version' % script,
+          script=self.path.join(script),
+          args=['--version'],
+          stdout=self.m.raw_io.output_text(),
+          step_test_data=step_test_data_cb)
+      finally:
+        step_result = self.m.step.active_result
+        version = step_result.stdout.strip()
+        step_result.presentation.step_text = version
+        self._script_version[script] = tuple(map(int, version.split('.')))
+
+      return step_result
+
+  def get_script_version(self, script):
+    """Returns a version of some swarming script as a tuple (Major, Minor, Rev).
+
+    It should have been queried by 'query_script_version' step before. Raises
+    AssertionError if it wasn't.
+    """
+    assert script in self._script_version, script
+    return self._script_version[script]
+
+  def ensure_script_version(self, script, min_version, step_test_data=None):
+    """Yields steps to ensure a script version is not older than |min_version|.
+
+    Will abort recipe execution if it is.
+    """
+    step_result = self.query_script_version(
+        script, step_test_data=step_test_data or min_version)
+    version = self.get_script_version(script)
+    if version < min_version:
+      expecting = '.'.join(map(str, min_version))
+      got = '.'.join(map(str, version))
+      abort_reason = 'Expecting at least v%s, got v%s' % (expecting, got)
+
+      # TODO(martiniss) remove once recipe 1.5 migration done
+      step_result = self.m.python.inline(
+          '%s is too old' % script,
+          'import sys; sys.exit(1)',
+          add_python_log=False)
+      # TODO(martiniss) get rid of this bare string.
+      step_result.presentation.status = self.m.step.FAILURE
+      step_result.presentation.step_text = abort_reason
+
+      raise self.m.step.StepFailure(abort_reason)
diff --git a/infra/bots/recipe_modules/swarming_client/examples/full.expected/basic.json b/infra/bots/recipe_modules/swarming_client/examples/full.expected/basic.json
new file mode 100644
index 0000000..373746e
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming_client/examples/full.expected/basic.json
@@ -0,0 +1,136 @@
+[
+  {
+    "cmd": [],
+    "name": "client path"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "sample_sha"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.4.4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/isolate.py",
+      "--version"
+    ],
+    "name": "isolate.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.3.1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import sys; sys.exit(1)"
+    ],
+    "name": "swarming.py is too old",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Expecting at least v20.0.0, got v0.4.4@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Expecting at least v20.0.0, got v0.4.4",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/swarming_client/examples/full.py b/infra/bots/recipe_modules/swarming_client/examples/full.py
new file mode 100644
index 0000000..223c29d
--- /dev/null
+++ b/infra/bots/recipe_modules/swarming_client/examples/full.py
@@ -0,0 +1,46 @@
+# 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.
+
+
+# TODO(borenet): This module was copied from build.git and heavily modified to
+# remove dependencies on other modules in build.git.  It belongs in a different
+# repo. Remove this once it has been moved.
+
+
+DEPS = [
+  'recipe_engine/properties',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'swarming_client',
+]
+
+
+def RunSteps(api):
+  # Code coverage for these methods.
+  api.step('client path', [])
+  api.step.active_result.step_text = api.swarming_client.path
+  api.swarming_client.checkout()
+  #api.swarming_client.checkout('master')
+  api.swarming_client.query_script_version('swarming.py')
+  api.swarming_client.ensure_script_version('swarming.py', (0, 4, 4))
+
+  # Coverage for |step_test_data| argument.
+  api.swarming_client.query_script_version(
+      'isolate.py', step_test_data=(0, 3, 1))
+
+  # 'master' had swarming.py at v0.4.4 at the moment of writing this example.
+  assert api.swarming_client.get_script_version('swarming.py') >= (0, 4, 4)
+
+  # Coverage for 'fail' path of ensure_script_version.
+  api.swarming_client.ensure_script_version('swarming.py', (20, 0, 0))
+
+
+def GenTests(api):
+  yield (
+      api.test('basic') +
+      api.properties(parent_got_swarming_client_revision='sample_sha') +
+      api.step_data(
+          'swarming.py --version',
+          stdout=api.raw_io.output_text('0.4.4'))
+  )
diff --git a/infra/bots/recipe_modules/upload_dm_results/__init__.py b/infra/bots/recipe_modules/upload_dm_results/__init__.py
deleted file mode 100644
index df2e005..0000000
--- a/infra/bots/recipe_modules/upload_dm_results/__init__.py
+++ /dev/null
@@ -1,13 +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.
-
-DEPS = [
-  'build/file',
-  'recipe_engine/json',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/shutil',
-  'recipe_engine/step',
-  'recipe_engine/time',
-]
diff --git a/infra/bots/recipe_modules/upload_dm_results/api.py b/infra/bots/recipe_modules/upload_dm_results/api.py
deleted file mode 100644
index 3005f3e..0000000
--- a/infra/bots/recipe_modules/upload_dm_results/api.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 for uploading DM results.
-
-
-
-import calendar
-
-from recipe_engine import recipe_api
-
-
-DM_JSON = 'dm.json'
-UPLOAD_ATTEMPTS = 5
-VERBOSE_LOG = 'verbose.log'
-
-
-class UploadDmResultsApi(recipe_api.RecipeApi):
-  def cp(self, name, src, dst, extra_args=None):
-    cmd = ['gsutil', 'cp']
-    if extra_args:
-      cmd.extend(extra_args)
-    cmd.extend([src, dst])
-
-    name = 'upload %s' % name
-    for i in xrange(UPLOAD_ATTEMPTS):
-      step_name = name
-      if i > 0:
-        step_name += ' (attempt %d)' % (i+1)
-      try:
-        self.m.step(step_name, cmd=cmd)
-        break
-      except self.m.step.StepFailure:
-        if i == UPLOAD_ATTEMPTS - 1:
-          raise
-
-  def run(self):
-    builder_name = self.m.properties['buildername']
-    revision = self.m.properties['revision']
-
-    results_dir = self.m.path['start_dir'].join('dm')
-
-    # Move dm.json and verbose.log to their own directory.
-    json_file = results_dir.join(DM_JSON)
-    log_file = results_dir.join(VERBOSE_LOG)
-    tmp_dir = self.m.path['start_dir'].join('tmp_upload')
-    self.m.shutil.makedirs('tmp dir', tmp_dir, infra_step=True)
-    self.m.shutil.copy('copy dm.json', json_file, tmp_dir)
-    self.m.shutil.copy('copy verbose.log', log_file, tmp_dir)
-    self.m.shutil.remove('rm old dm.json', json_file)
-    self.m.shutil.remove('rm old verbose.log', log_file)
-
-    # Upload the images.
-    image_dest_path = 'gs://%s/dm-images-v1' % self.m.properties['gs_bucket']
-    files_to_upload = self.m.file.glob(
-        'find images',
-        results_dir.join('*'),
-        test_data=[results_dir.join('someimage.png')],
-        infra_step=True)
-    if len(files_to_upload) > 0:
-      self.cp('images', results_dir.join('*'), image_dest_path)
-
-    # Upload the JSON summary and verbose.log.
-    now = self.m.time.utcnow()
-    summary_dest_path = '/'.join([
-        'dm-json-v1',
-        str(now.year ).zfill(4),
-        str(now.month).zfill(2),
-        str(now.day  ).zfill(2),
-        str(now.hour ).zfill(2),
-        revision,
-        builder_name,
-        str(int(calendar.timegm(now.utctimetuple())))])
-
-    # Trybot results are further siloed by issue/patchset.
-    issue = str(self.m.properties.get('issue', ''))
-    patchset = str(self.m.properties.get('patchset', ''))
-    if self.m.properties.get('patch_storage', '') == 'gerrit':
-      issue = str(self.m.properties['patch_issue'])
-      patchset = str(self.m.properties['patch_set'])
-    if issue and patchset:
-      summary_dest_path = '/'.join((
-          'trybot', summary_dest_path, issue, patchset))
-
-    summary_dest_path = 'gs://%s/%s' % (self.m.properties['gs_bucket'],
-                                        summary_dest_path)
-
-    self.cp('JSON and logs', tmp_dir.join('*'), summary_dest_path,
-       ['-z', 'json,log'])
diff --git a/infra/bots/recipe_modules/upload_dm_results/example.expected/normal_bot.json b/infra/bots/recipe_modules/upload_dm_results/example.expected/normal_bot.json
deleted file mode 100644
index 7c90e99..0000000
--- a/infra/bots/recipe_modules/upload_dm_results/example.expected/normal_bot.json
+++ /dev/null
@@ -1,113 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp_upload",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/dm/dm.json",
-      "[START_DIR]/tmp_upload"
-    ],
-    "name": "copy dm.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/dm/verbose.log",
-      "[START_DIR]/tmp_upload"
-    ],
-    "name": "copy verbose.log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
-      "[START_DIR]/dm/dm.json"
-    ],
-    "name": "rm old dm.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
-      "[START_DIR]/dm/verbose.log"
-    ],
-    "name": "rm old verbose.log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
-      "/path/to/tmp/",
-      "[START_DIR]/dm/*"
-    ],
-    "infra_step": true,
-    "name": "find images"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "[START_DIR]/dm/*",
-      "gs://skia-infra-gm/dm-images-v1"
-    ],
-    "name": "upload images"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json,log",
-      "[START_DIR]/tmp_upload/*",
-      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/1337000001"
-    ],
-    "name": "upload JSON and logs"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/upload_dm_results/example.expected/recipe_with_gerrit_patch.json b/infra/bots/recipe_modules/upload_dm_results/example.expected/recipe_with_gerrit_patch.json
deleted file mode 100644
index f30b4ca..0000000
--- a/infra/bots/recipe_modules/upload_dm_results/example.expected/recipe_with_gerrit_patch.json
+++ /dev/null
@@ -1,113 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp_upload",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/dm/dm.json",
-      "[START_DIR]/tmp_upload"
-    ],
-    "name": "copy dm.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/dm/verbose.log",
-      "[START_DIR]/tmp_upload"
-    ],
-    "name": "copy verbose.log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
-      "[START_DIR]/dm/dm.json"
-    ],
-    "name": "rm old dm.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
-      "[START_DIR]/dm/verbose.log"
-    ],
-    "name": "rm old verbose.log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
-      "/path/to/tmp/",
-      "[START_DIR]/dm/*"
-    ],
-    "infra_step": true,
-    "name": "find images"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "[START_DIR]/dm/*",
-      "gs://skia-infra-gm/dm-images-v1"
-    ],
-    "name": "upload images"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json,log",
-      "[START_DIR]/tmp_upload/*",
-      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/1337000001/456789/12"
-    ],
-    "name": "upload JSON and logs"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/upload_dm_results/example.expected/trybot.json b/infra/bots/recipe_modules/upload_dm_results/example.expected/trybot.json
deleted file mode 100644
index f99a6cf..0000000
--- a/infra/bots/recipe_modules/upload_dm_results/example.expected/trybot.json
+++ /dev/null
@@ -1,113 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp_upload",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/dm/dm.json",
-      "[START_DIR]/tmp_upload"
-    ],
-    "name": "copy dm.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/dm/verbose.log",
-      "[START_DIR]/tmp_upload"
-    ],
-    "name": "copy verbose.log"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
-      "[START_DIR]/dm/dm.json"
-    ],
-    "name": "rm old dm.json",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
-      "[START_DIR]/dm/verbose.log"
-    ],
-    "name": "rm old verbose.log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
-      "/path/to/tmp/",
-      "[START_DIR]/dm/*"
-    ],
-    "infra_step": true,
-    "name": "find images"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "[START_DIR]/dm/*",
-      "gs://skia-infra-gm/dm-images-v1"
-    ],
-    "name": "upload images"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json,log",
-      "[START_DIR]/tmp_upload/*",
-      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/1337000001/12345/1002"
-    ],
-    "name": "upload JSON and logs"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/upload_dm_results/example.py b/infra/bots/recipe_modules/upload_dm_results/example.py
deleted file mode 100644
index 5e332bf..0000000
--- a/infra/bots/recipe_modules/upload_dm_results/example.py
+++ /dev/null
@@ -1,75 +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.
-
-
-# Example recipe w/ coverage.
-
-
-DEPS = [
-  'upload_dm_results',
-  'recipe_engine/properties',
-]
-
-
-def RunSteps(api):
-  api.upload_dm_results.run()
-
-
-def GenTests(api):
-  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
-  yield (
-    api.test('normal_bot') +
-    api.properties(buildername=builder,
-                   gs_bucket='skia-infra-gm',
-                   revision='abc123',
-                   path_config='kitchen')
-  )
-
-  yield (
-    api.test('failed_once') +
-    api.properties(buildername=builder,
-                   gs_bucket='skia-infra-gm',
-                   revision='abc123',
-                   path_config='kitchen') +
-    api.step_data('upload images', retcode=1)
-  )
-
-  yield (
-    api.test('failed_all') +
-    api.properties(buildername=builder,
-                   gs_bucket='skia-infra-gm',
-                   revision='abc123',
-                   path_config='kitchen') +
-    api.step_data('upload images', retcode=1) +
-    api.step_data('upload images (attempt 2)', retcode=1) +
-    api.step_data('upload images (attempt 3)', retcode=1) +
-    api.step_data('upload images (attempt 4)', retcode=1) +
-    api.step_data('upload images (attempt 5)', retcode=1)
-  )
-
-  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot'
-  yield (
-    api.test('trybot') +
-    api.properties(buildername=builder,
-                   gs_bucket='skia-infra-gm',
-                   revision='abc123',
-                   path_config='kitchen',
-                   issue='12345',
-                   patchset='1002')
-  )
-
-  yield (
-      api.test('recipe_with_gerrit_patch') +
-      api.properties(
-          buildername=builder,
-          gs_bucket='skia-infra-gm',
-          revision='abc123',
-          path_config='kitchen',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=builder,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      )
-  )
diff --git a/infra/bots/recipe_modules/upload_nano_results/__init__.py b/infra/bots/recipe_modules/upload_nano_results/__init__.py
deleted file mode 100644
index eac65b7..0000000
--- a/infra/bots/recipe_modules/upload_nano_results/__init__.py
+++ /dev/null
@@ -1,11 +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.
-
-DEPS = [
-  'build/file',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/step',
-  'recipe_engine/time',
-]
diff --git a/infra/bots/recipe_modules/upload_nano_results/api.py b/infra/bots/recipe_modules/upload_nano_results/api.py
deleted file mode 100644
index aabfdc6..0000000
--- a/infra/bots/recipe_modules/upload_nano_results/api.py
+++ /dev/null
@@ -1,50 +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 for uploading nanobench results.
-
-
-from recipe_engine import recipe_api
-
-
-class UploadNanoResultsApi(recipe_api.RecipeApi):
-  def run(self):
-    # Upload the nanobench resuls.
-    builder_name = self.m.properties['buildername']
-
-    now = self.m.time.utcnow()
-    src_path = self.m.path['start_dir'].join(
-        'perfdata', builder_name, 'data')
-    with self.m.step.context({'cwd': src_path}):
-      results = self.m.file.glob(
-          'find results',
-          src_path.join('*.json'),
-          test_data=[src_path.join('nanobench_abc123.json')],
-          infra_step=True)
-    if len(results) != 1:  # pragma: nocover
-      raise Exception('Unable to find nanobench or skpbench JSON file!')
-
-    src = results[0]
-    basename = self.m.path.basename(src)
-    gs_path = '/'.join((
-        'nano-json-v1', str(now.year).zfill(4),
-        str(now.month).zfill(2), str(now.day).zfill(2), str(now.hour).zfill(2),
-        builder_name))
-
-    issue = str(self.m.properties.get('issue', ''))
-    patchset = str(self.m.properties.get('patchset', ''))
-    if self.m.properties.get('patch_storage', '') == 'gerrit':
-      issue = str(self.m.properties['patch_issue'])
-      patchset = str(self.m.properties['patch_set'])
-    if issue and patchset:
-      gs_path = '/'.join(('trybot', gs_path, issue, patchset))
-
-    dst = '/'.join((
-        'gs://%s' % self.m.properties['gs_bucket'], gs_path, basename))
-
-    self.m.step(
-        'upload',
-        cmd=['gsutil', 'cp', '-z', 'json', src, dst],
-        infra_step=True)
diff --git a/infra/bots/recipe_modules/upload_nano_results/example.expected/normal_bot.json b/infra/bots/recipe_modules/upload_nano_results/example.expected/normal_bot.json
deleted file mode 100644
index cc175d7..0000000
--- a/infra/bots/recipe_modules/upload_nano_results/example.expected/normal_bot.json
+++ /dev/null
@@ -1,31 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
-      "/path/to/tmp/",
-      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/data/*.json"
-    ],
-    "cwd": "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/data",
-    "infra_step": true,
-    "name": "find results"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json",
-      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/data/nanobench_abc123.json",
-      "gs://skia-perf/nano-json-v1/2012/05/14/12/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/nanobench_abc123.json"
-    ],
-    "infra_step": true,
-    "name": "upload"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/upload_nano_results/example.expected/recipe_with_gerrit_patch.json b/infra/bots/recipe_modules/upload_nano_results/example.expected/recipe_with_gerrit_patch.json
deleted file mode 100644
index bbebf80..0000000
--- a/infra/bots/recipe_modules/upload_nano_results/example.expected/recipe_with_gerrit_patch.json
+++ /dev/null
@@ -1,31 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
-      "/path/to/tmp/",
-      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/data/*.json"
-    ],
-    "cwd": "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/data",
-    "infra_step": true,
-    "name": "find results"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json",
-      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/data/nanobench_abc123.json",
-      "gs://skia-perf/trybot/nano-json-v1/2012/05/14/12/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/456789/12/nanobench_abc123.json"
-    ],
-    "infra_step": true,
-    "name": "upload"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/upload_nano_results/example.expected/trybot.json b/infra/bots/recipe_modules/upload_nano_results/example.expected/trybot.json
deleted file mode 100644
index 75baf29..0000000
--- a/infra/bots/recipe_modules/upload_nano_results/example.expected/trybot.json
+++ /dev/null
@@ -1,31 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
-      "/path/to/tmp/",
-      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/data/*.json"
-    ],
-    "cwd": "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/data",
-    "infra_step": true,
-    "name": "find results"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json",
-      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/data/nanobench_abc123.json",
-      "gs://skia-perf/trybot/nano-json-v1/2012/05/14/12/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot/12345/1002/nanobench_abc123.json"
-    ],
-    "infra_step": true,
-    "name": "upload"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/upload_nano_results/example.py b/infra/bots/recipe_modules/upload_nano_results/example.py
deleted file mode 100644
index 7978fd5..0000000
--- a/infra/bots/recipe_modules/upload_nano_results/example.py
+++ /dev/null
@@ -1,53 +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.
-
-
-# Example recipe w/ coverage.
-
-
-DEPS = [
-  'recipe_engine/properties',
-  'upload_nano_results',
-]
-
-
-def RunSteps(api):
-  api.upload_nano_results.run()
-
-
-def GenTests(api):
-  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
-  yield (
-    api.test('normal_bot') +
-    api.properties(buildername=builder,
-                   gs_bucket='skia-perf',
-                   revision='abc123',
-                   path_config='kitchen')
-  )
-
-  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-Trybot'
-  yield (
-    api.test('trybot') +
-    api.properties(buildername=builder,
-                   gs_bucket='skia-perf',
-                   revision='abc123',
-                   path_config='kitchen',
-                   issue='12345',
-                   patchset='1002')
-  )
-
-  yield (
-      api.test('recipe_with_gerrit_patch') +
-      api.properties(
-          buildername=builder,
-          gs_bucket='skia-perf',
-          revision='abc123',
-          path_config='kitchen',
-          patch_storage='gerrit') +
-      api.properties.tryserver(
-          buildername=builder,
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      )
-  )
diff --git a/infra/bots/recipe_modules/vars/__init__.py b/infra/bots/recipe_modules/vars/__init__.py
index d5cf1da..98c1d3e 100644
--- a/infra/bots/recipe_modules/vars/__init__.py
+++ b/infra/bots/recipe_modules/vars/__init__.py
@@ -5,6 +5,7 @@
 DEPS = [
   'builder_name_schema',
   'depot_tools/bot_update',
+  'recipe_engine/context',
   'recipe_engine/json',
   'recipe_engine/path',
   'recipe_engine/properties',
@@ -12,7 +13,3 @@
   'recipe_engine/raw_io',
   'recipe_engine/step',
 ]
-
-
-# TODO(borenet): Add coverage
-DISABLE_STRICT_COVERAGE = True
diff --git a/infra/bots/recipe_modules/vars/api.py b/infra/bots/recipe_modules/vars/api.py
index 63e3e4f..2c23f7a 100644
--- a/infra/bots/recipe_modules/vars/api.py
+++ b/infra/bots/recipe_modules/vars/api.py
@@ -25,35 +25,22 @@
     """Prepare the variables."""
     # Setup
     self.builder_name = self.m.properties['buildername']
-    self.master_name = self.m.properties['mastername']
-    self.slave_name = self.m.properties['slavename']
-    self.build_number = self.m.properties['buildnumber']
 
     self.slave_dir = self.m.path['start_dir']
     self.checkout_root = self.slave_dir
-    self.default_env = self.m.step.get_from_context('env', {})
+    self.default_env = self.m.context.env
+    self.default_env['CHROME_HEADLESS'] = '1'
     self.default_env['PATH'] = self.m.path.pathsep.join([
         self.default_env.get('PATH', '%(PATH)s'),
         str(self.m.bot_update._module.PACKAGE_REPO_ROOT),
     ])
     self.gclient_env = {}
     self.is_compile_bot = self.builder_name.startswith('Build-')
-    self.no_buildbot = self.m.properties.get('nobuildbot', '') == 'True'
-    self.skia_task_id = self.m.properties.get('skia_task_id', None)
-
-    self.default_env['CHROME_HEADLESS'] = '1'
-    # The 'depot_tools' directory comes from recipe DEPS and isn't provided by
-    # default. We have to set it manually.
-    self.m.path.c.base_paths['depot_tools'] = (
-        self.m.path.c.base_paths['start_dir'] +
-        ('skia', 'infra', 'bots', '.recipe_deps', 'depot_tools'))
-    if 'Win' in self.builder_name:
-      self.m.path.c.base_paths['depot_tools'] = (
-          'c:\\', 'Users', 'chrome-bot', 'depot_tools')
 
     # Compile bots keep a persistent checkout.
     self.persistent_checkout = (self.is_compile_bot or
                                 'RecreateSKPs' in self.builder_name or
+                                'UpdateMetaConfig' in self.builder_name or
                                 '-CT_' in self.builder_name or
                                 'Presubmit' in self.builder_name or
                                 'InfraTests' in self.builder_name or
@@ -107,7 +94,7 @@
     if 'CommandBuffer' in self.builder_name:
       self.need_chromium_checkout = True
       self.gclient_env['GYP_CHROMIUM_NO_ACTION'] = '0'
-    if 'RecreateSKPs' in self.builder_name:  # pragma: no cover
+    if 'RecreateSKPs' in self.builder_name:
       self.need_chromium_checkout = True
       self.gclient_env['CPPFLAGS'] = (
           '-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1')
@@ -126,30 +113,15 @@
     self.default_env.update({'SKIA_OUT': self.skia_out,
                              'BUILDTYPE': self.configuration})
 
-    self.patch_storage = self.m.properties.get('patch_storage', 'rietveld')
+    self.patch_storage = self.m.properties.get('patch_storage', 'gerrit')
     self.issue = None
     self.patchset = None
-    if self.no_buildbot:
-      self.is_trybot = False
-      if (self.m.properties.get('issue', '') and
-          self.m.properties.get('patchset', '')):
-        self.is_trybot = True
-        self.issue = self.m.properties['issue']
-        self.patchset = self.m.properties['patchset']
-      elif (self.m.properties.get('patch_issue', '') and
-            self.m.properties.get('patch_set', '')):
-        self.is_trybot = True
-        self.issue = self.m.properties['patch_issue']
-        self.patchset = self.m.properties['patch_set']
-    else:
-      self.is_trybot = self.builder_cfg['is_trybot']
-      if self.is_trybot:
-        if self.patch_storage == 'gerrit':
-          self.issue = self.m.properties['patch_issue']
-          self.patchset = self.m.properties['patch_set']
-        else:
-          self.issue = self.m.properties['issue']
-          self.patchset = self.m.properties['patchset']
+    self.is_trybot = False
+    if (self.m.properties.get('patch_issue', '') and
+        self.m.properties.get('patch_set', '')):
+      self.is_trybot = True
+      self.issue = self.m.properties['patch_issue']
+      self.patchset = self.m.properties['patch_set']
 
     self.dm_dir = self.m.path.join(
         self.swarming_out_dir, 'dm')
@@ -168,6 +140,8 @@
       self.android_bin_dir  = '/cache/skia/'
       self.android_data_dir = '/cache/skia/'
 
+    self.chromeos_homedir = '/home/chronos/user/'
+
   @property
   def upload_dm_results(self):
     # TODO(borenet): Move this into the swarm_test recipe.
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
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
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug.json b/infra/bots/recipe_modules/vars/examples/full.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN.json b/infra/bots/recipe_modules/vars/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/win_test.json b/infra/bots/recipe_modules/vars/examples/full.expected/win_test.json
new file mode 100644
index 0000000..4594f9e
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/win_test.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/vars/examples/full.py b/infra/bots/recipe_modules/vars/examples/full.py
new file mode 100644
index 0000000..be15b06
--- /dev/null
+++ b/infra/bots/recipe_modules/vars/examples/full.py
@@ -0,0 +1,59 @@
+# 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 = [
+  'recipe_engine/properties',
+  'vars',
+]
+
+
+def RunSteps(api):
+  api.vars.setup()
+  info = [
+    api.vars.upload_dm_results,
+    api.vars.upload_perf_results,
+    api.vars.swarming_bot_id,
+    api.vars.swarming_task_id,
+  ]
+  assert len(info) == 4  # Make pylint happy.
+
+
+TEST_BUILDERS = [
+  'Build-Mac-Clang-x86_64-Debug-CommandBuffer',
+  'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android',
+  'Build-Ubuntu-GCC-x86_64-Release-PDFium',
+  'Build-Win-MSVC-x86_64-Release-Vulkan',
+  'Housekeeper-Weekly-RecreateSKPs',
+  'Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug',
+  'Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN',
+]
+
+
+def GenTests(api):
+  for buildername in TEST_BUILDERS:
+    yield (
+        api.test(buildername) +
+        api.properties(buildername=buildername,
+                       repository='https://skia.googlesource.com/skia.git',
+                       revision='abc123',
+                       path_config='kitchen',
+                       swarm_out_dir='[SWARM_OUT_DIR]')
+    )
+
+  buildername = 'Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug'
+  yield (
+      api.test('win_test') +
+      api.properties(buildername=buildername,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]',
+                     patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )
+  )
diff --git a/infra/bots/recipes.py b/infra/bots/recipes.py
index 7bc4107..b3ff3e7 100755
--- a/infra/bots/recipes.py
+++ b/infra/bots/recipes.py
@@ -1,54 +1,57 @@
 #!/usr/bin/env python
 
-# Copyright 2016 The LUCI Authors. All rights reserved.
+# Copyright 2017 The LUCI Authors. All rights reserved.
 # Use of this source code is governed under the Apache License, Version 2.0
 # that can be found in the LICENSE file.
 
 """Bootstrap script to clone and forward to the recipe engine tool.
 
-***********************************************************************
-** DO NOT MODIFY EXCEPT IN THE PER-REPO CONFIGURATION SECTION BELOW. **
-***********************************************************************
+*******************
+** DO NOT MODIFY **
+*******************
 
 This is a copy of https://github.com/luci/recipes-py/blob/master/doc/recipes.py.
-To fix bugs, fix in the github repo then copy it back to here and fix the
-PER-REPO CONFIGURATION section to look like this one.
+To fix bugs, fix in the github repo then run the autoroller.
 """
 
-import os
-
-# IMPORTANT: Do not alter the header or footer line for the
-# "PER-REPO CONFIGURATION" section below, or the autoroller will not be able
-# to automatically update this file! All lines between the header and footer
-# lines will be retained verbatim by the autoroller.
-
-#### PER-REPO CONFIGURATION (editable) ####
-# The root of the repository relative to the directory of this file.
-REPO_ROOT = os.path.join(os.pardir, os.pardir)
-# The path of the recipes.cfg file relative to the root of the repository.
-RECIPES_CFG = os.path.join('infra', 'config', 'recipes.cfg')
-#### END PER-REPO CONFIGURATION ####
-
-BOOTSTRAP_VERSION = 1
-
 import argparse
-import ast
 import json
 import logging
+import os
 import random
-import re
 import subprocess
 import sys
 import time
-import traceback
 import urlparse
 
+from collections import namedtuple
+
 from cStringIO import StringIO
 
+# The dependency entry for the recipe_engine in the client repo's recipes.cfg
+#
+# url (str) - the url to the engine repo we want to use.
+# revision (str) - the git revision for the engine to get.
+# path_override (str) - the subdirectory in the engine repo we should use to
+#   find it's recipes.py entrypoint. This is here for completeness, but will
+#   essentially always be empty. It would be used if the recipes-py repo was
+#   merged as a subdirectory of some other repo and you depended on that
+#   subdirectory.
+# branch (str) - the branch to fetch for the engine as an absolute ref (e.g.
+#   refs/heads/master)
+# repo_type ("GIT"|"GITILES") - An ignored enum which will be removed soon.
+EngineDep = namedtuple('EngineDep',
+                       'url revision path_override branch repo_type')
+
+
+class MalformedRecipesCfg(Exception):
+  def __init__(self, msg, path):
+    super(MalformedRecipesCfg, self).__init__('malformed recipes.cfg: %s: %r'
+                                              % (msg, path))
+
 
 def parse(repo_root, recipes_cfg_path):
-  """Parse is transitional code which parses a recipes.cfg file as either jsonpb
-  or as textpb.
+  """Parse is a lightweight a recipes.cfg file parser.
 
   Args:
     repo_root (str) - native path to the root of the repo we're trying to run
@@ -56,107 +59,49 @@
     recipes_cfg_path (str) - native path to the recipes.cfg file to process.
 
   Returns (as tuple):
-    engine_url (str) - the url to the engine repo we want to use.
-    engine_revision (str) - the git revision for the engine to get.
-    engine_subpath (str) - the subdirectory in the engine repo we should use to
-      find it's recipes.py entrypoint. This is here for completeness, but will
-      essentially always be empty. It would be used if the recipes-py repo was
-      merged as a subdirectory of some other repo and you depended on that
-      subdirectory.
+    engine_dep (EngineDep): The recipe_engine dependency.
     recipes_path (str) - native path to where the recipes live inside of the
       current repo (i.e. the folder containing `recipes/` and/or
       `recipe_modules`)
   """
   with open(recipes_cfg_path, 'rU') as fh:
-    data = fh.read()
+    pb = json.load(fh)
 
-  if data.lstrip().startswith('{'):
-    pb = json.loads(data)
-    engine = next(
-      (d for d in pb['deps'] if d['project_id'] == 'recipe_engine'), None)
-    if engine is None:
-      raise ValueError('could not find recipe_engine dep in %r'
-                       % recipes_cfg_path)
-    engine_url = engine['url']
-    engine_revision = engine.get('revision', '')
-    engine_subpath = engine.get('path_override', '')
+  try:
+    if pb['api_version'] != 2:
+      raise MalformedRecipesCfg('unknown version %d' % pb['api_version'],
+                                recipes_cfg_path)
+
+    engine = pb['deps']['recipe_engine']
+
+    if 'url' not in engine:
+      raise MalformedRecipesCfg(
+        'Required field "url" in dependency "recipe_engine" not found',
+        recipes_cfg_path)
+
+    engine.setdefault('revision', '')
+    engine.setdefault('path_override', '')
+    engine.setdefault('branch', 'refs/heads/master')
     recipes_path = pb.get('recipes_path', '')
-  else:
-    def get_unique(things):
-      if len(things) == 1:
-        return things[0]
-      elif len(things) == 0:
-        raise ValueError("Expected to get one thing, but dinna get none.")
-      else:
-        logging.warn('Expected to get one thing, but got a bunch: %s\n%s' %
-                     (things, traceback.format_stack()))
-        return things[0]
 
-    protobuf = parse_textpb(StringIO(data))
+    # TODO(iannucci): only support absolute refs
+    if not engine['branch'].startswith('refs/'):
+      engine['branch'] = 'refs/heads/' + engine['branch']
 
-    engine_buf = get_unique([
-        b for b in protobuf.get('deps', [])
-        if b.get('project_id') == ['recipe_engine'] ])
-    engine_url = get_unique(engine_buf['url'])
-    engine_revision = get_unique(engine_buf.get('revision', ['']))
-    engine_subpath = (get_unique(engine_buf.get('path_override', ['']))
-                      .replace('/', os.path.sep))
-    recipes_path = get_unique(protobuf.get('recipes_path', ['']))
+    engine.setdefault('repo_type', 'GIT')
+    if engine['repo_type'] not in ('GIT', 'GITILES'):
+      raise MalformedRecipesCfg(
+        'Unsupported "repo_type" value in dependency "recipe_engine"',
+        recipes_cfg_path)
 
-  recipes_path = os.path.join(repo_root, recipes_path.replace('/', os.path.sep))
-  return engine_url, engine_revision, engine_subpath, recipes_path
+    recipes_path = os.path.join(
+      repo_root, recipes_path.replace('/', os.path.sep))
+    return EngineDep(**engine), recipes_path
+  except KeyError as ex:
+    raise MalformedRecipesCfg(ex.message, recipes_cfg_path)
 
 
-def parse_textpb(fh):
-  """Parse the protobuf text format just well enough to understand recipes.cfg.
-
-  We don't use the protobuf library because we want to be as self-contained
-  as possible in this bootstrap, so it can be simply vendored into a client
-  repo.
-
-  We assume all fields are repeated since we don't have a proto spec to work
-  with.
-
-  Args:
-    fh: a filehandle containing the text format protobuf.
-  Returns:
-    A recursive dictionary of lists.
-  """
-  def parse_atom(field, text):
-    if text == 'true':
-      return True
-    if text == 'false':
-      return False
-
-    # repo_type is an enum. Since it does not have quotes,
-    # invoking literal_eval would fail.
-    if field == 'repo_type':
-      return text
-
-    return ast.literal_eval(text)
-
-  ret = {}
-  for line in fh:
-    line = line.strip()
-    m = re.match(r'(\w+)\s*:\s*(.*)', line)
-    if m:
-      ret.setdefault(m.group(1), []).append(parse_atom(m.group(1), m.group(2)))
-      continue
-
-    m = re.match(r'(\w+)\s*{', line)
-    if m:
-      subparse = parse_textpb(fh)
-      ret.setdefault(m.group(1), []).append(subparse)
-      continue
-
-    if line == '}':
-      return ret
-    if line == '':
-      continue
-
-    raise ValueError('Could not understand line: <%s>' % line)
-
-  return ret
+GIT = 'git.bat' if sys.platform.startswith(('win', 'cygwin')) else 'git'
 
 
 def _subprocess_call(argv, **kwargs):
@@ -164,86 +109,99 @@
   return subprocess.call(argv, **kwargs)
 
 
-def _subprocess_check_call(argv, **kwargs):
+def _git_check_call(argv, **kwargs):
+  argv = [GIT]+argv
   logging.info('Running %r', argv)
   subprocess.check_call(argv, **kwargs)
 
 
-def find_engine_override(argv):
-  """Since the bootstrap process attempts to defer all logic to the recipes-py
-  repo, we need to be aware if the user is overriding the recipe_engine
-  dependency. This looks for and returns the overridden recipe_engine path, if
-  any, or None if the user didn't override it."""
+def _git_output(argv, **kwargs):
+  argv = [GIT]+argv
+  logging.info('Running %r', argv)
+  return subprocess.check_output(argv, **kwargs)
+
+
+def parse_args(argv):
+  """This extracts a subset of the arguments that this bootstrap script cares
+  about. Currently this consists of:
+    * an override for the recipe engine in the form of `-O recipe_engin=/path`
+    * the --package option.
+  """
   PREFIX = 'recipe_engine='
 
-  p = argparse.ArgumentParser()
+  p = argparse.ArgumentParser(add_help=False)
   p.add_argument('-O', '--project-override', action='append')
+  p.add_argument('--package', type=os.path.abspath)
   args, _ = p.parse_known_args(argv)
   for override in args.project_override or ():
     if override.startswith(PREFIX):
-      return override[len(PREFIX):]
-  return None
+      return override[len(PREFIX):], args.package
+  return None, args.package
+
+
+def checkout_engine(engine_path, repo_root, recipes_cfg_path):
+  dep, recipes_path = parse(repo_root, recipes_cfg_path)
+
+  url = dep.url
+
+  if not engine_path and url.startswith('file://'):
+    engine_path = urlparse.urlparse(url).path
+
+  if not engine_path:
+    revision = dep.revision
+    subpath = dep.path_override
+    branch = dep.branch
+
+    # Ensure that we have the recipe engine cloned.
+    engine = os.path.join(recipes_path, '.recipe_deps', 'recipe_engine')
+    engine_path = os.path.join(engine, subpath)
+
+    with open(os.devnull, 'w') as NUL:
+      # Note: this logic mirrors the logic in recipe_engine/fetch.py
+      _git_check_call(['init', engine], stdout=NUL)
+
+      try:
+        _git_check_call(['rev-parse', '--verify', '%s^{commit}' % revision],
+                        cwd=engine, stdout=NUL, stderr=NUL)
+      except subprocess.CalledProcessError:
+        _git_check_call(['fetch', url, branch], cwd=engine, stdout=NUL,
+                        stderr=NUL)
+
+    try:
+      _git_check_call(['diff', '--quiet', revision], cwd=engine)
+    except subprocess.CalledProcessError:
+      _git_check_call(['reset', '-q', '--hard', revision], cwd=engine)
+
+  return engine_path
 
 
 def main():
   if '--verbose' in sys.argv:
     logging.getLogger().setLevel(logging.INFO)
 
-  if REPO_ROOT is None or RECIPES_CFG is None:
-    logging.error(
-      'In order to use this script, please copy it to your repo and '
-      'replace the REPO_ROOT and RECIPES_CFG values with approprite paths.')
-    sys.exit(1)
+  args = sys.argv[1:]
+  engine_override, recipes_cfg_path = parse_args(args)
 
-  if sys.platform.startswith(('win', 'cygwin')):
-    git = 'git.bat'
+  if recipes_cfg_path:
+    # calculate repo_root from recipes_cfg_path
+    repo_root = os.path.dirname(
+      os.path.dirname(
+        os.path.dirname(recipes_cfg_path)))
   else:
-    git = 'git'
+    # find repo_root with git and calculate recipes_cfg_path
+    repo_root = (_git_output(
+      ['rev-parse', '--show-toplevel'],
+      cwd=os.path.abspath(os.path.dirname(__file__))).strip())
+    repo_root = os.path.abspath(repo_root)
+    recipes_cfg_path = os.path.join(repo_root, 'infra', 'config', 'recipes.cfg')
+    args = ['--package', recipes_cfg_path] + args
 
-  # Find the repository and config file to operate on.
-  repo_root = os.path.abspath(
-      os.path.join(os.path.dirname(__file__), REPO_ROOT))
-  recipes_cfg_path = os.path.join(repo_root, RECIPES_CFG)
+  engine_path = checkout_engine(engine_override, repo_root, recipes_cfg_path)
 
-  engine_url, engine_revision, engine_subpath, recipes_path = parse(
-    repo_root, recipes_cfg_path)
-
-  engine_path = find_engine_override(sys.argv[1:])
-  if not engine_path and engine_url.startswith('file://'):
-    engine_path = urlparse.urlparse(engine_url).path
-
-  if not engine_path:
-    deps_path = os.path.join(recipes_path, '.recipe_deps')
-    # Ensure that we have the recipe engine cloned.
-    engine_root_path = os.path.join(deps_path, 'recipe_engine')
-    engine_path = os.path.join(engine_root_path, engine_subpath)
-    def ensure_engine():
-      if not os.path.exists(deps_path):
-        os.makedirs(deps_path)
-      if not os.path.exists(engine_root_path):
-        _subprocess_check_call([git, 'clone', engine_url, engine_root_path])
-
-      needs_fetch = _subprocess_call(
-          [git, 'rev-parse', '--verify', '%s^{commit}' % engine_revision],
-          cwd=engine_root_path, stdout=open(os.devnull, 'w'))
-      if needs_fetch:
-        _subprocess_check_call([git, 'fetch'], cwd=engine_root_path)
-      _subprocess_check_call(
-          [git, 'checkout', '--quiet', engine_revision], cwd=engine_root_path)
-
-    try:
-      ensure_engine()
-    except subprocess.CalledProcessError:
-      logging.exception('ensure_engine failed')
-
-      # Retry errors.
-      time.sleep(random.uniform(2,5))
-      ensure_engine()
-
-  args = ['--package', recipes_cfg_path] + sys.argv[1:]
   return _subprocess_call([
       sys.executable, '-u',
       os.path.join(engine_path, 'recipes.py')] + args)
 
+
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/infra/bots/recipes/README.md b/infra/bots/recipes/README.md
new file mode 100644
index 0000000..41d51bc
--- /dev/null
+++ b/infra/bots/recipes/README.md
@@ -0,0 +1,25 @@
+Skia Recipes
+============
+
+These are the top-level scripts which run inside of Swarming tasks to perform
+all of Skia's automated testing.
+
+To run a recipe locally:
+
+	$ python infra/bots/recipes.py run --workdir=/tmp/<workdir> <recipe name without .py> key1=value1 key2=value2 ...
+
+Each recipe may have its own required properties which must be entered as
+key/value pairs in the command.
+
+When you change a recipe, you generally need to re-train the simulation test:
+
+	$ python infra/bots/recipes.py test run --train
+
+Or:
+
+        $ cd infra/bots; make train
+
+The test generates expectations files for the tests contained within each
+recipe which illustrate which steps would run, given a particular set of inputs.
+Pay attention to the diffs in these files when making changes to ensure that
+your change has the intended effect.
diff --git a/infra/bots/recipes/bundle_recipes.expected/BundleRecipes.json b/infra/bots/recipes/bundle_recipes.expected/BundleRecipes.json
new file mode 100644
index 0000000..1cf232b
--- /dev/null
+++ b/infra/bots/recipes/bundle_recipes.expected/BundleRecipes.json
@@ -0,0 +1,61 @@
+[
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "PATH": "[START_DIR]/git:[START_DIR]/git/bin:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git init"
+  },
+  {
+    "cmd": [
+      "git",
+      "add",
+      "."
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "PATH": "[START_DIR]/git:[START_DIR]/git/bin:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git add"
+  },
+  {
+    "cmd": [
+      "git",
+      "commit",
+      "-m",
+      "commit recipes"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "PATH": "[START_DIR]/git:[START_DIR]/git/bin:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git commit"
+  },
+  {
+    "cmd": [
+      "python",
+      "[START_DIR]/skia/infra/bots/recipes.py",
+      "bundle",
+      "--destination",
+      "[SWARM_OUT_DIR]/recipe_bundle"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "PATH": "[START_DIR]/git:[START_DIR]/git/bin:<PATH>"
+    },
+    "infra_step": true,
+    "name": "Bundle Recipes"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/bundle_recipes.py b/infra/bots/recipes/bundle_recipes.py
new file mode 100644
index 0000000..49310af
--- /dev/null
+++ b/infra/bots/recipes/bundle_recipes.py
@@ -0,0 +1,40 @@
+# 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 compile.
+
+
+DEPS = [
+  'git',
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  bundle_dir = api.properties['swarm_out_dir'] + '/recipe_bundle'
+  skia_dir = api.path['start_dir'].join('skia')
+  recipes_py = api.path['start_dir'].join('skia', 'infra', 'bots', 'recipes.py')
+  with api.git.env():
+    with api.context(cwd=skia_dir):
+      api.step('git init', infra_step=True,
+               cmd=['git', 'init'])
+      api.step('git add', infra_step=True,
+               cmd=['git', 'add', '.'])
+      api.step('git commit', infra_step=True,
+               cmd=['git', 'commit', '-m', 'commit recipes'])
+      api.step('Bundle Recipes', infra_step=True,
+               cmd=['python', recipes_py, 'bundle',
+                    '--destination', bundle_dir])
+
+
+def GenTests(api):
+  yield (
+    api.test('BundleRecipes') +
+    api.properties(buildername='Housekeeper-PerCommit-BundleRecipes',
+                   swarm_out_dir='[SWARM_OUT_DIR]')
+  )
diff --git a/infra/bots/recipes/compile.expected/Build-Mac-Clang-arm64-Debug-Android.json b/infra/bots/recipes/compile.expected/Build-Mac-Clang-arm64-Debug-Android.json
new file mode 100644
index 0000000..b068c92
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Mac-Clang-arm64-Debug-Android.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_darwin\" target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-Android/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Mac-Clang-arm64-Debug-iOS.json b/infra/bots/recipes/compile.expected/Build-Mac-Clang-arm64-Debug-iOS.json
new file mode 100644
index 0000000..3553acb
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Mac-Clang-arm64-Debug-iOS.json
@@ -0,0 +1,227 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug",
+      "--args=cc=\"clang\" cxx=\"clang++\" extra_cflags=[\"-O1\"] target_cpu=\"arm64\" target_os=\"ios\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/gn/package_ios.py",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
+    },
+    "infra_step": true,
+    "name": "package dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/gn/package_ios.py",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug/nanobench"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS"
+    },
+    "infra_step": true,
+    "name": "package nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-arm64-Debug-iOS/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Mac-Clang-x64-Release-iOS.json b/infra/bots/recipes/compile.expected/Build-Mac-Clang-x64-Release-iOS.json
new file mode 100644
index 0000000..1141f77
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Mac-Clang-x64-Release-iOS.json
@@ -0,0 +1,227 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release",
+      "--args=cc=\"clang\" cxx=\"clang++\" is_debug=false target_cpu=\"x64\" target_os=\"ios\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/gn/package_ios.py",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
+    },
+    "infra_step": true,
+    "name": "package dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/gn/package_ios.py",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release/nanobench"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS"
+    },
+    "infra_step": true,
+    "name": "package nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x64-Release-iOS/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json b/infra/bots/recipes/compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
new file mode 100644
index 0000000..6e2903c
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
@@ -0,0 +1,230 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
+      "--patch_root",
+      "skia",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123",
+      "--revision",
+      "src@origin/lkgr"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
+      "@@@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@  \"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",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GYP_CHROMIUM_NO_ACTION": "0",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
+    },
+    "name": "gclient runhooks"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/tools/build_command_buffer.py",
+      "--chrome-dir",
+      "[CUSTOM_/_B_WORK]",
+      "--output-dir",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
+      "--no-sync",
+      "--make-output-dir"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "name": "build command_buffer"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
+      "--args=cc=\"clang\" cxx=\"clang++\" extra_cflags=[\"-O1\"] target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Debug-CommandBuffer/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Mac-Clang-x86_64-Release.json b/infra/bots/recipes/compile.expected/Build-Mac-Clang-x86_64-Release.json
new file mode 100644
index 0000000..9e7657d
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Mac-Clang-x86_64-Release.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release/Release",
+      "--args=cc=\"clang\" cxx=\"clang++\" is_debug=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-x86_64-Release/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p.json
new file mode 100644
index 0000000..228072d
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p.json
@@ -0,0 +1,196 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "LD_LIBRARY_PATH": "[START_DIR]/armhf_sysroot/lib",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_asmflags=[\"--target=armv7a-linux-gnueabihf\", \"--sysroot=[START_DIR]/armhf_sysroot\", \"-march=armv7-a\", \"-mfpu=neon\", \"-mthumb\"] extra_cflags=[\"--target=armv7a-linux-gnueabihf\", \"--sysroot=[START_DIR]/armhf_sysroot\", \"-I[START_DIR]/chromebook_arm_gles/include\", \"-I[START_DIR]/armhf_sysroot/include\", \"-I[START_DIR]/armhf_sysroot/include/c++/4.8.4\", \"-I[START_DIR]/armhf_sysroot/include/c++/4.8.4/arm-linux-gnueabihf\", \"-DMESA_EGL_NO_X11_HEADERS\"] extra_ldflags=[\"--target=armv7a-linux-gnueabihf\", \"--sysroot=[START_DIR]/armhf_sysroot\", \"-B[START_DIR]/armhf_sysroot/bin\", \"-B[START_DIR]/armhf_sysroot/gcc-cross\", \"-L[START_DIR]/armhf_sysroot/gcc-cross\", \"-L[START_DIR]/armhf_sysroot/lib\", \"-L[START_DIR]/chromebook_arm_gles/lib\"] is_debug=false skia_use_egl=true skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"arm\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "LD_LIBRARY_PATH": "[START_DIR]/armhf_sysroot/lib",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p/Release",
+      "nanobench",
+      "dm"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "LD_LIBRARY_PATH": "[START_DIR]/armhf_sysroot/lib",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm-Release-Chromebook_C100p/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs.json
new file mode 100644
index 0000000..246bf93
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" skia_enable_android_framework_defines=true target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Release-Android.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Release-Android.json
new file mode 100644
index 0000000..e56e31c
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Release-Android.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android/Release",
+      "--args=is_debug=false ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan.json
new file mode 100644
index 0000000..0caf620
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan/Release",
+      "--args=is_debug=false ndk=\"[START_DIR]/android_ndk_linux\" ndk_api=24 skia_enable_vulkan_debug_layers=false target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-arm64-Release-Android_Vulkan/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-mipsel-Debug-Android.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-mipsel-Debug-Android.json
new file mode 100644
index 0000000..6a44cca
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-mipsel-Debug-Android.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-mipsel-Debug-Android"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-mipsel-Debug-Android/Debug",
+      "--args=extra_cflags=[\"-O1\"] ndk=\"[START_DIR]/android_ndk_linux\" target_cpu=\"mipsel\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-mipsel-Debug-Android"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-mipsel-Debug-Android/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-mipsel-Debug-Android"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-mipsel-Debug-Android/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug-ASAN.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug-ASAN.json
new file mode 100644
index 0000000..076b20e
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug-ASAN.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN/Debug",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-O1\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] sanitize=\"ASAN\" skia_enable_spirv_validation=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-ASAN/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug-MSAN.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug-MSAN.json
new file mode 100644
index 0000000..d826a52
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug-MSAN.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-MSAN"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-MSAN/Debug",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-O1\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\", \"-L[START_DIR]/clang_linux/msan\"] sanitize=\"MSAN\" skia_enable_gpu=false skia_use_fontconfig=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-MSAN"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-MSAN/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-MSAN"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug-MSAN/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug.json
new file mode 100644
index 0000000..140b67e
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Debug.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug/Debug",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-O1\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Debug/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json
new file mode 100644
index 0000000..49cc323
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Release-Mini.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_component_build=true is_debug=false is_official_build=true skia_enable_effects=false skia_enable_gpu=false skia_enable_pdf=false skia_use_expat=false skia_use_libjpeg_turbo=false skia_use_libpng=false skia_use_libwebp=false skia_use_zlib=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Mini/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json
new file mode 100644
index 0000000..b5a7253
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-Clang-x86_64-Release-Vulkan.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/linux_vulkan_sdk\" target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-Clang-x86_64-Release-Vulkan/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-arm-Release-Chromecast.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-arm-Release-Chromecast.json
new file mode 100644
index 0000000..811968b
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-arm-Release-Chromecast.json
@@ -0,0 +1,186 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast/Release",
+      "--args=ar=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabi-ar\" cc=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabi-gcc\" cxx=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabi-g++\" extra_cflags=[\"-I[START_DIR]/chromebook_arm_gles/include\", \"-DMESA_EGL_NO_X11_HEADERS\", \"-DEGL_NO_IMAGE_EXTERNAL\", \"-DSK_NO_COMMAND_BUFFER\", \"-Wno-error=unused-function\", \"-g0\"] extra_ldflags=[\"-static-libstdc++\", \"-static-libgcc\", \"-L[START_DIR]/cast_toolchain/armv7a/lib\"] 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\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast/Release",
+      "nanobench"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-arm-Release-Chromecast/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86-Debug.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86-Debug.json
new file mode 100644
index 0000000..b78fe08
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86-Debug.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] target_cpu=\"x86\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86-Debug/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-GN.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-GN.json
new file mode 100644
index 0000000..9c4bdbe
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-GN.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-GN/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-MSAN.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-MSAN.json
new file mode 100644
index 0000000..7f8ad6a
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-MSAN.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] extra_ldflags=[\"-L[START_DIR]/clang_linux/msan\"] sanitize=\"MSAN\" skia_enable_gpu=false skia_use_fontconfig=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-MSAN/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-NoGPU.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-NoGPU.json
new file mode 100644
index 0000000..dd9ca48
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-NoGPU.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"] skia_enable_gpu=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-NoGPU/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
new file mode 100644
index 0000000..3ae2ab1
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
@@ -0,0 +1,188 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "CPPFLAGS": "-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\", \"-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE\"] target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "CPPFLAGS": "-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "CPPFLAGS": "-DSK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE/Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json
new file mode 100644
index 0000000..3ef9461
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-ANGLE.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_angle=true target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-ANGLE/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json
new file mode 100644
index 0000000..b7b83d1
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Fast.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-march=native\", \"-fomit-frame-pointer\", \"-O3\", \"-ffp-contract=off\"] is_debug=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Fast/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
new file mode 100644
index 0000000..5d32f3c
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android.json
@@ -0,0 +1,209 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/flutter",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/flutter/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/flutter/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'src/flutter', 'url': 'https://github.com/flutter/engine.git'}]\ntarget_os = ['android']",
+      "--patch_root",
+      "src/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_flutter_revision\": \"src/flutter\", \"got_revision\": \"src/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "src/flutter@origin/master",
+      "--revision",
+      "src/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src/flutter\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/flutter\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/flutter.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9221bca00ddbd888260084def81f09543281b952\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
+      "@@@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\": \"src/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision\": \"9221bca00ddbd888260084def81f09543281b952\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision_cp\": \"refs/heads/master@{#84512}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#143121}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"src/flutter\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_flutter_revision@\"9221bca00ddbd888260084def81f09543281b952\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_flutter_revision_cp@\"refs/heads/master@{#84512}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#143121}\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gclient",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/flutter/src/out/android_release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree android_release"
+  },
+  {
+    "cmd": [
+      "flutter/tools/gn",
+      "--runtime-mode=release",
+      "--android"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/android_release",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "build_flutter"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json
new file mode 100644
index 0000000..2934a8d
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Mesa.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_mesa=true target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Mesa/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
new file mode 100644
index 0000000..a8d358f
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium.json
@@ -0,0 +1,213 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
+      "--patch_root",
+      "pdfium/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_pdfium_revision\": \"pdfium\", \"got_revision\": \"pdfium/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "pdfium@origin/master",
+      "--revision",
+      "pdfium/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@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\": \"pdfium/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gclient",
+      "runhook",
+      "gn_linux64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "build/linux/sysroot_scripts/install-sysroot.py",
+      "--arch=amd64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "sysroot"
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/skia",
+      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia=true"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/skia",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "build_pdfium"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json
new file mode 100644
index 0000000..2f72ce3
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths.json
@@ -0,0 +1,213 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
+      "--patch_root",
+      "pdfium/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_pdfium_revision\": \"pdfium\", \"got_revision\": \"pdfium/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "pdfium@origin/master",
+      "--revision",
+      "pdfium/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@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\": \"pdfium/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gclient",
+      "runhook",
+      "gn_linux64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "build/linux/sysroot_scripts/install-sysroot.py",
+      "--arch=amd64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "sysroot"
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/skia",
+      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia_paths=true"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/skia",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths"
+    },
+    "name": "build_pdfium"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Shared.json b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Shared.json
new file mode 100644
index 0000000..2bc6839
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Ubuntu-GCC-x86_64-Release-Shared.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_component_build=true is_debug=false target_cpu=\"x86_64\""
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/skia/out/Build-Ubuntu-GCC-x86_64-Release-Shared/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-Clang-arm64-Release-Android.json b/infra/bots/recipes/compile.expected/Build-Win-Clang-arm64-Release-Android.json
new file mode 100644
index 0000000..4ec91c0
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-Clang-arm64-Release-Android.json
@@ -0,0 +1,210 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-Android"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-Android\\Release",
+      "--args=is_debug=false ndk=\"[START_DIR]\\n\" target_cpu=\"arm64\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-Android"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-Android\\Release"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CC": "/usr/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "/usr/bin/clang++",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-Android"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-Clang-arm64-Release-Android\\Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug-ANGLE.json b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug-ANGLE.json
new file mode 100644
index 0000000..6576694
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug-ANGLE.json
@@ -0,0 +1,204 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE\\Debug",
+      "--args=skia_use_angle=true target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE\\Debug"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-ANGLE\\Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug-Exceptions.json b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug-Exceptions.json
new file mode 100644
index 0000000..ec67cb2
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug-Exceptions.json
@@ -0,0 +1,204 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions\\Debug",
+      "--args=extra_cflags=[\"/EHsc\"] target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions\\Debug"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug-Exceptions\\Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug.json b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug.json
new file mode 100644
index 0000000..4334c7b
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Debug.json
@@ -0,0 +1,204 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
+      "--args=target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Debug\\Debug",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Debug"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Release-GDI.json b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Release-GDI.json
new file mode 100644
index 0000000..b927b4f
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Release-GDI.json
@@ -0,0 +1,204 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI\\Release",
+      "--args=is_debug=false skia_use_gdi=true target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI\\Release"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GDI\\Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Release-GN.json b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Release-GN.json
new file mode 100644
index 0000000..1e87a70
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86-Release-GN.json
@@ -0,0 +1,204 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN\\Release",
+      "--args=is_debug=false target_cpu=\"x86\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN\\Release"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86-Release-GN\\Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
new file mode 100644
index 0000000..8f97523
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/Build-Win-MSVC-x86_64-Release-Vulkan.json
@@ -0,0 +1,242 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]\\.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]\\.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>;RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]\\skia\\bin\\gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan\\Release_x64",
+      "--args=is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]\\win_vulkan_sdk\" target_cpu=\"x86_64\" windk=\"[START_DIR]\\t\\depot_tools\\win_toolchain\\vs_files\\d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan\\Release_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]\\skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]\\skia\\out\\Build-Win-MSVC-x86_64-Release-Vulkan\\Release_x64",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Release_x64"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]\\win_vulkan_sdk",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\out\\Release_x64"
+    ],
+    "infra_step": true,
+    "name": "copy build products (2)",
+    "~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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/alternate_repo.json b/infra/bots/recipes/compile.expected/alternate_repo.json
new file mode 100644
index 0000000..5eaed68
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/alternate_repo.json
@@ -0,0 +1,242 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'other_repo', 'url': 'https://skia.googlesource.com/other_repo.git'}]",
+      "--patch_root",
+      "other_repo",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"other_repo\"}",
+      "--git-cache-dir",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "other_repo@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"other_repo\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"other_repo\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/other_repo.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"84be67d5f1146c5b7f6d4494c36c52903754abf4\"@@@",
+      "@@@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\": \"other_repo\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"84be67d5f1146c5b7f6d4494c36c52903754abf4\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#170933}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"other_repo\", @@@",
+      "@@@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@\"84be67d5f1146c5b7f6d4494c36c52903754abf4\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#170933}\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
+      "--args=is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/win_vulkan_sdk\" target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/win_vulkan_sdk",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
+    ],
+    "infra_step": true,
+    "name": "copy build products (2)",
+    "~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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/flutter_trybot.json b/infra/bots/recipes/compile.expected/flutter_trybot.json
new file mode 100644
index 0000000..984fb5a
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/flutter_trybot.json
@@ -0,0 +1,213 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/flutter",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/flutter/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/flutter/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'src/flutter', 'url': 'https://github.com/flutter/engine.git'}]\ntarget_os = ['android']",
+      "--patch_root",
+      "src/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_flutter_revision\": \"src/flutter\", \"got_revision\": \"src/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "src/flutter@origin/master",
+      "--revision",
+      "src/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src/flutter\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/flutter\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/flutter.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9221bca00ddbd888260084def81f09543281b952\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"src/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/src/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
+      "@@@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\": \"src/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision\": \"9221bca00ddbd888260084def81f09543281b952\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_flutter_revision_cp\": \"refs/heads/master@{#84512}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#143121}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"src/flutter\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_flutter_revision@\"9221bca00ddbd888260084def81f09543281b952\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_flutter_revision_cp@\"refs/heads/master@{#84512}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"d9c4a4d173a97ef2832b65636b4200bb93ea8ee1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#143121}\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gclient",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/flutter/src/out/android_release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree android_release"
+  },
+  {
+    "cmd": [
+      "flutter/tools/gn",
+      "--runtime-mode=release",
+      "--android"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/android_release",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/flutter/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android"
+    },
+    "name": "build_flutter"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/flutter/src/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-Flutter_Android/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/pdfium_trybot.json b/infra/bots/recipes/compile.expected/pdfium_trybot.json
new file mode 100644
index 0000000..63dd1c4
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/pdfium_trybot.json
@@ -0,0 +1,217 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': True, 'name': 'pdfium', 'url': 'https://pdfium.googlesource.com/pdfium.git'}]",
+      "--patch_root",
+      "pdfium/third_party/skia",
+      "--revision_mapping_file",
+      "{\"got_pdfium_revision\": \"pdfium\", \"got_revision\": \"pdfium/third_party/skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "pdfium@origin/master",
+      "--revision",
+      "pdfium/third_party/skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"pdfium\": \"origin/master\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pdfium/third_party/skia\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/pdfium/third_party/skia.git\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@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\": \"pdfium/third_party/skia\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision\": \"d69d97171c17fdb12a52f78847e2ee2f0594eff1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_pdfium_revision_cp\": \"refs/heads/master@{#52055}\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"85501db4bcbeb8f295309fdcda1a743388f0f104\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#120212}\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"root\": \"pdfium\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision@\"d69d97171c17fdb12a52f78847e2ee2f0594eff1\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_pdfium_revision_cp@\"refs/heads/master@{#52055}\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"85501db4bcbeb8f295309fdcda1a743388f0f104\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#120212}\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gclient",
+      "runhook",
+      "gn_linux64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "runhook"
+  },
+  {
+    "cmd": [
+      "python",
+      "build/linux/sysroot_scripts/install-sysroot.py",
+      "--arch=amd64"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "sysroot"
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/skia",
+      "--args=pdf_is_standalone=true clang_use_chrome_plugins=false is_component_build=false is_debug=false pdf_use_skia=true"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "gn_gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "out/skia",
+      "-j100"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/pdfium",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CHROMIUM_BUILDTOOLS_PATH": "[CUSTOM_/_B_WORK]/pdfium/buildtools",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium"
+    },
+    "name": "build_pdfium"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_/_B_WORK]/pdfium/third_party/skia/out/Build-Ubuntu-GCC-x86_64-Release-PDFium/Release",
+      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.expected/trybot.json b/infra/bots/recipes/compile.expected/trybot.json
new file mode 100644
index 0000000..dec7f43
--- /dev/null
+++ b/infra/bots/recipes/compile.expected/trybot.json
@@ -0,0 +1,246 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_C:\\_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_C:\\_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_C:\\_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_C:\\\\_B_CACHE]'\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",
+      "[CUSTOM_C:\\_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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",
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_C:\\_B_WORK]/skia/bin/gn.exe",
+      "gen",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
+      "--args=is_debug=false skia_enable_vulkan_debug_layers=false skia_vulkan_sdk=\"[START_DIR]/win_vulkan_sdk\" target_cpu=\"x86_64\" windk=\"[START_DIR]/t/depot_tools/win_toolchain/vs_files/d3cb0e37bdd120ad0ac4650b674b09e81be45616\""
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja.exe",
+      "-C",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64"
+    ],
+    "cwd": "[CUSTOM_C:\\_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
+      "[CUSTOM_C:\\_B_WORK]/skia/out/Build-Win-MSVC-x86_64-Release-Vulkan/Release_x64",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/win_vulkan_sdk",
+      "[CUSTOM_[SWARM_OUT_DIR]]/out/Release_x64"
+    ],
+    "infra_step": true,
+    "name": "copy build products (2)",
+    "~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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import psutil\nfor p in psutil.process_iter():\n  try:\n    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):\n      p.kill()\n  except psutil._error.AccessDenied:\n    pass\n"
+    ],
+    "infra_step": true,
+    "name": "cleanup",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import psutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@for p in psutil.process_iter():@@@",
+      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      p.kill()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  except psutil._error.AccessDenied:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    pass@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/compile.py b/infra/bots/recipes/compile.py
new file mode 100644
index 0000000..222b255
--- /dev/null
+++ b/infra/bots/recipes/compile.py
@@ -0,0 +1,216 @@
+# 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 compile.
+
+
+DEPS = [
+  'core',
+  'recipe_engine/context',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/platform',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/step',
+  'flavor',
+  'run',
+  'vars',
+]
+
+
+def build_targets_from_builder_dict(builder_dict):
+  """Return a list of targets to build, depending on the builder type."""
+  if builder_dict.get('extra_config') == 'iOS':
+    return ['iOSShell']
+  return ['most']
+
+
+def get_extra_env_vars(builder_dict):
+  env = {}
+  if builder_dict.get('compiler') == 'Clang':
+    env['CC'] = '/usr/bin/clang'
+    env['CXX'] = '/usr/bin/clang++'
+
+  # SKNX_NO_SIMD, SK_USE_DISCARDABLE_SCALEDIMAGECACHE, etc.
+  extra_config = builder_dict.get('extra_config', '')
+  if extra_config.startswith('SK') and extra_config.isupper():
+    env['CPPFLAGS'] = '-D' + extra_config
+
+  return env
+
+
+def RunSteps(api):
+  api.core.setup()
+
+  env = get_extra_env_vars(api.vars.builder_cfg)
+  build_targets = build_targets_from_builder_dict(api.vars.builder_cfg)
+
+  try:
+    for target in build_targets:
+      with api.context(env=env):
+        api.flavor.compile(target)
+    api.run.copy_build_products(
+        api.flavor.out_dir,
+        api.vars.swarming_out_dir.join(
+            'out', api.vars.configuration))
+    api.flavor.copy_extra_build_products(api.vars.swarming_out_dir)
+  finally:
+    if 'Win' in api.vars.builder_cfg.get('os', ''):
+      api.python.inline(
+          name='cleanup',
+          program='''import psutil
+for p in psutil.process_iter():
+  try:
+    if p.name in ('mspdbsrv.exe', 'vctip.exe', 'cl.exe', 'link.exe'):
+      p.kill()
+  except psutil._error.AccessDenied:
+    pass
+''',
+          infra_step=True)
+
+  api.flavor.cleanup_steps()
+  api.run.check_failure()
+
+
+TEST_BUILDERS = [
+  'Build-Mac-Clang-arm64-Debug-Android',
+  'Build-Mac-Clang-arm64-Debug-iOS',
+  'Build-Mac-Clang-x64-Release-iOS',
+  'Build-Mac-Clang-x86_64-Debug-CommandBuffer',
+  'Build-Mac-Clang-x86_64-Release',
+  'Build-Ubuntu-Clang-arm-Release-Chromebook_C100p',
+  'Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs',
+  'Build-Ubuntu-Clang-arm64-Release-Android',
+  'Build-Ubuntu-Clang-arm64-Release-Android_Vulkan',
+  'Build-Ubuntu-Clang-mipsel-Debug-Android',
+  'Build-Ubuntu-Clang-x86_64-Debug',
+  'Build-Ubuntu-Clang-x86_64-Debug-ASAN',
+  'Build-Ubuntu-Clang-x86_64-Debug-MSAN',
+  'Build-Ubuntu-Clang-x86_64-Release-Mini',
+  'Build-Ubuntu-Clang-x86_64-Release-Vulkan',
+  'Build-Ubuntu-GCC-arm-Release-Chromecast',
+  'Build-Ubuntu-GCC-x86-Debug',
+  'Build-Ubuntu-GCC-x86_64-Debug-GN',
+  'Build-Ubuntu-GCC-x86_64-Debug-MSAN',
+  'Build-Ubuntu-GCC-x86_64-Debug-NoGPU',
+  'Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE',
+  'Build-Ubuntu-GCC-x86_64-Release-ANGLE',
+  'Build-Ubuntu-GCC-x86_64-Release-Fast',
+  'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android',
+  'Build-Ubuntu-GCC-x86_64-Release-Mesa',
+  'Build-Ubuntu-GCC-x86_64-Release-PDFium',
+  'Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths',
+  'Build-Ubuntu-GCC-x86_64-Release-Shared',
+  'Build-Win-Clang-arm64-Release-Android',
+  'Build-Win-MSVC-x86-Debug',
+  'Build-Win-MSVC-x86-Debug-ANGLE',
+  'Build-Win-MSVC-x86-Debug-Exceptions',
+  'Build-Win-MSVC-x86-Release-GDI',
+  'Build-Win-MSVC-x86-Release-GN',
+  'Build-Win-MSVC-x86_64-Release-Vulkan',
+]
+
+
+def GenTests(api):
+  for builder in TEST_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('tmp', 'uninteresting_hashes.txt')
+      )
+    )
+    if 'Win' in builder:
+      test += api.platform('win', 64)
+    elif 'Mac' in builder:
+      test += api.platform('mac', 64)
+    else:
+      test += api.platform('linux', 64)
+
+    yield test
+
+
+  buildername = 'Build-Win-MSVC-x86_64-Release-Vulkan'
+  yield (
+      api.test("trybot") +
+      api.properties(buildername=buildername,
+                     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('tmp', 'uninteresting_hashes.txt')
+      ) +
+      api.properties(patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )
+    )
+
+  buildername = 'Build-Win-MSVC-x86_64-Release-Vulkan'
+  yield (
+      api.test('alternate_repo') +
+      api.properties(buildername=buildername,
+                     repository='https://skia.googlesource.com/other_repo.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.path.exists(
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      )
+    )
+
+  buildername = 'Build-Ubuntu-GCC-x86_64-Release-PDFium'
+  yield (
+      api.test('pdfium_trybot') +
+      api.properties(
+          repository='https://skia.googlesource.com/skia.git',
+          buildername=buildername,
+          path_config='kitchen',
+          swarm_out_dir='[SWARM_OUT_DIR]',
+          revision='abc123',
+          patch_issue=500,
+          patch_repo='https://skia.googlesource.com/skia.git',
+          patch_set=1,
+          patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      ) +
+      api.path.exists(
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      )
+  )
+
+  buildername = 'Build-Ubuntu-GCC-x86_64-Release-Flutter_Android'
+  yield (
+      api.test('flutter_trybot') +
+      api.properties(
+          repository='https://skia.googlesource.com/skia.git',
+          buildername=buildername,
+          path_config='kitchen',
+          swarm_out_dir='[SWARM_OUT_DIR]',
+          revision='abc123',
+          patch_issue=500,
+          patch_repo='https://skia.googlesource.com/skia.git',
+          patch_set=1,
+          patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=buildername,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      ) +
+      api.path.exists(
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      )
+  )
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_10k_SKPs_UnknownBuilder.json b/infra/bots/recipes/ct_skps.expected/CT_10k_SKPs_UnknownBuilder.json
similarity index 100%
rename from infra/bots/recipes/swarm_ct_skps.expected/CT_10k_SKPs_UnknownBuilder.json
rename to infra/bots/recipes/ct_skps.expected/CT_10k_SKPs_UnknownBuilder.json
diff --git a/infra/bots/recipes/ct_skps.expected/CT_CPU_BENCH_10k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_CPU_BENCH_10k_SKPs.json
new file mode 100644
index 0000000..a357a59
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_CPU_BENCH_10k_SKPs.json
@@ -0,0 +1,1966 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Release",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-1]",
+      "--tag",
+      "name:ct-nanobench-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-1",
+      "--isolated",
+      "[dummy hash for ct-nanobench-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-2]",
+      "--tag",
+      "name:ct-nanobench-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-2",
+      "--isolated",
+      "[dummy hash for ct-nanobench-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-3]",
+      "--tag",
+      "name:ct-nanobench-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-3",
+      "--isolated",
+      "[dummy hash for ct-nanobench-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-4]",
+      "--tag",
+      "name:ct-nanobench-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-4",
+      "--isolated",
+      "[dummy hash for ct-nanobench-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-5]",
+      "--tag",
+      "name:ct-nanobench-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-5",
+      "--isolated",
+      "[dummy hash for ct-nanobench-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (2)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (3)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (4)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (5)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (6)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (7)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (8)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (9)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (10)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_DM_100k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_DM_100k_SKPs.json
new file mode 100644
index 0000000..a917509
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_DM_100k_SKPs.json
@@ -0,0 +1,1621 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"100k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-1]",
+      "--tag",
+      "name:ct-dm-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-1",
+      "--isolated",
+      "[dummy hash for ct-dm-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-2]",
+      "--tag",
+      "name:ct-dm-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-2",
+      "--isolated",
+      "[dummy hash for ct-dm-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-3]",
+      "--tag",
+      "name:ct-dm-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-3",
+      "--isolated",
+      "[dummy hash for ct-dm-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-4]",
+      "--tag",
+      "name:ct-dm-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-4",
+      "--isolated",
+      "[dummy hash for ct-dm-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-5]",
+      "--tag",
+      "name:ct-dm-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-5",
+      "--isolated",
+      "[dummy hash for ct-dm-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_DM_10k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_DM_10k_SKPs.json
new file mode 100644
index 0000000..edcc5e0
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_DM_10k_SKPs.json
@@ -0,0 +1,1621 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-1]",
+      "--tag",
+      "name:ct-dm-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-1",
+      "--isolated",
+      "[dummy hash for ct-dm-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-2]",
+      "--tag",
+      "name:ct-dm-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-2",
+      "--isolated",
+      "[dummy hash for ct-dm-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-3]",
+      "--tag",
+      "name:ct-dm-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-3",
+      "--isolated",
+      "[dummy hash for ct-dm-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-4]",
+      "--tag",
+      "name:ct-dm-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-4",
+      "--isolated",
+      "[dummy hash for ct-dm-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-5]",
+      "--tag",
+      "name:ct-dm-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-5",
+      "--isolated",
+      "[dummy hash for ct-dm-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_DM_10k_SKPs_Trybot.json b/infra/bots/recipes/ct_skps.expected/CT_DM_10k_SKPs_Trybot.json
new file mode 100644
index 0000000..4ffb9a8
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_DM_10k_SKPs_Trybot.json
@@ -0,0 +1,1645 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@origin/master"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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\": \"origin/master\"@@@",
+      "@@@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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-dm-1]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-dm-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-dm-1",
+      "--isolated",
+      "[dummy hash for ct-dm-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-dm-2]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-dm-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-dm-2",
+      "--isolated",
+      "[dummy hash for ct-dm-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-dm-3]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-dm-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-dm-3",
+      "--isolated",
+      "[dummy hash for ct-dm-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-dm-4]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-dm-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-dm-4",
+      "--isolated",
+      "[dummy hash for ct-dm-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-dm-5]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-dm-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-dm-5",
+      "--isolated",
+      "[dummy hash for ct-dm-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs.json
new file mode 100644
index 0000000..6e1cf8b
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs.json
@@ -0,0 +1,1621 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"All\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-1]",
+      "--tag",
+      "name:ct-dm-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-1",
+      "--isolated",
+      "[dummy hash for ct-dm-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-2]",
+      "--tag",
+      "name:ct-dm-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-2",
+      "--isolated",
+      "[dummy hash for ct-dm-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-3]",
+      "--tag",
+      "name:ct-dm-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-3",
+      "--isolated",
+      "[dummy hash for ct-dm-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-4]",
+      "--tag",
+      "name:ct-dm-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-4",
+      "--isolated",
+      "[dummy hash for ct-dm-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-5]",
+      "--tag",
+      "name:ct-dm-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-5",
+      "--isolated",
+      "[dummy hash for ct-dm-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs_2slaves_failure.json b/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs_2slaves_failure.json
new file mode 100644
index 0000000..079fb8e
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs_2slaves_failure.json
@@ -0,0 +1,1630 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"All\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-1]",
+      "--tag",
+      "name:ct-dm-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-1",
+      "--isolated",
+      "[dummy hash for ct-dm-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-2]",
+      "--tag",
+      "name:ct-dm-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-2",
+      "--isolated",
+      "[dummy hash for ct-dm-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-3]",
+      "--tag",
+      "name:ct-dm-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-3",
+      "--isolated",
+      "[dummy hash for ct-dm-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-4]",
+      "--tag",
+      "name:ct-dm-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-4",
+      "--isolated",
+      "[dummy hash for ct-dm-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-5]",
+      "--tag",
+      "name:ct-dm-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-5",
+      "--isolated",
+      "[dummy hash for ct-dm-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-1",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@",
+      "@@@STEP_LINK@Webpage rankings@https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv@@@",
+      "@@@STEP_LINK@Download SKPs by rank@https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-3",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@",
+      "@@@STEP_LINK@Webpage rankings@https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv@@@",
+      "@@@STEP_LINK@Download SKPs by rank@https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Failed steps: ct-dm-1, ct-dm-3",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs_slave3_failure.json b/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs_slave3_failure.json
new file mode 100644
index 0000000..efd26c3
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_DM_1m_SKPs_slave3_failure.json
@@ -0,0 +1,1626 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "Write ct-dm-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"All\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-1]",
+      "--tag",
+      "name:ct-dm-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-1",
+      "--isolated",
+      "[dummy hash for ct-dm-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-2]",
+      "--tag",
+      "name:ct-dm-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-2",
+      "--isolated",
+      "[dummy hash for ct-dm-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-3]",
+      "--tag",
+      "name:ct-dm-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-3",
+      "--isolated",
+      "[dummy hash for ct-dm-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-4]",
+      "--tag",
+      "name:ct-dm-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-4",
+      "--isolated",
+      "[dummy hash for ct-dm-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-dm-5]",
+      "--tag",
+      "name:ct-dm-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-dm-5",
+      "--isolated",
+      "[dummy hash for ct-dm-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-3",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@",
+      "@@@STEP_LINK@Webpage rankings@https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv@@@",
+      "@@@STEP_LINK@Download SKPs by rank@https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/c37e844a6f8708-eee762104c75bd/@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-dm-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Failed steps: ct-dm-3",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_SKPs_UnknownBuilder.json b/infra/bots/recipes/ct_skps.expected/CT_DM_SKPs_UnknownBuilder.json
similarity index 100%
rename from infra/bots/recipes/swarm_ct_skps.expected/CT_DM_SKPs_UnknownBuilder.json
rename to infra/bots/recipes/ct_skps.expected/CT_DM_SKPs_UnknownBuilder.json
diff --git a/infra/bots/recipes/ct_skps.expected/CT_GPU_BENCH_10k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_GPU_BENCH_10k_SKPs.json
new file mode 100644
index 0000000..4c11791
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_GPU_BENCH_10k_SKPs.json
@@ -0,0 +1,1981 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Release",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-1]",
+      "--tag",
+      "name:ct-nanobench-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-1",
+      "--isolated",
+      "[dummy hash for ct-nanobench-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-2]",
+      "--tag",
+      "name:ct-nanobench-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-2",
+      "--isolated",
+      "[dummy hash for ct-nanobench-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-3]",
+      "--tag",
+      "name:ct-nanobench-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-3",
+      "--isolated",
+      "[dummy hash for ct-nanobench-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-4]",
+      "--tag",
+      "name:ct-nanobench-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-4",
+      "--isolated",
+      "[dummy hash for ct-nanobench-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-5]",
+      "--tag",
+      "name:ct-nanobench-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-5",
+      "--isolated",
+      "[dummy hash for ct-nanobench-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (2)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (3)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (4)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (5)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (6)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (7)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (8)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\", \"tasks\": {\"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (9)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (10)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_GPU_BENCH_1k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_GPU_BENCH_1k_SKPs.json
new file mode 100644
index 0000000..2d6d63c
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_GPU_BENCH_1k_SKPs.json
@@ -0,0 +1,1981 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Release",
+      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Release",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
+    ],
+    "name": "Write ct-nanobench-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-1]",
+      "--tag",
+      "name:ct-nanobench-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-1",
+      "--isolated",
+      "[dummy hash for ct-nanobench-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-2]",
+      "--tag",
+      "name:ct-nanobench-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-2",
+      "--isolated",
+      "[dummy hash for ct-nanobench-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-3]",
+      "--tag",
+      "name:ct-nanobench-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-3",
+      "--isolated",
+      "[dummy hash for ct-nanobench-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-4]",
+      "--tag",
+      "name:ct-nanobench-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-4",
+      "--isolated",
+      "[dummy hash for ct-nanobench-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "gpu",
+      "10de:104a",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-nanobench-5]",
+      "--tag",
+      "name:ct-nanobench-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-nanobench-5",
+      "--isolated",
+      "[dummy hash for ct-nanobench-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-nanobench-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", \"tasks\": {\"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (2)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", \"tasks\": {\"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (3)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (4)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", \"tasks\": {\"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (5)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (6)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", \"tasks\": {\"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (7)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (8)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\", \"tasks\": {\"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-nanobench-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0",
+      "/path/to/tmp/json"
+    ],
+    "name": "listdir output dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 1",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (9)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "-R",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 2",
+      "gs://skia-perf/ct/10k/2012/05/14/12/"
+    ],
+    "env": {
+      "AWS_CREDENTIAL_FILE": null,
+      "BOTO_CONFIG": null
+    },
+    "infra_step": true,
+    "name": "gsutil upload json output (10)",
+    "~followup_annotations": [
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_100k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_100k_SKPs.json
new file mode 100644
index 0000000..ccad65c
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_100k_SKPs.json
@@ -0,0 +1,1621 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/100k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"100k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-1]",
+      "--tag",
+      "name:ct-get_images_from_skps-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-1",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-2]",
+      "--tag",
+      "name:ct-get_images_from_skps-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-2",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-3]",
+      "--tag",
+      "name:ct-get_images_from_skps-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-3",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-4]",
+      "--tag",
+      "name:ct-get_images_from_skps-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-4",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-5]",
+      "--tag",
+      "name:ct-get_images_from_skps-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-5",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_10k_SKPs.json b/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_10k_SKPs.json
new file mode 100644
index 0000000..cf7d7c0
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_10k_SKPs.json
@@ -0,0 +1,1621 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-1]",
+      "--tag",
+      "name:ct-get_images_from_skps-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-1",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-2]",
+      "--tag",
+      "name:ct-get_images_from_skps-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-2",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-3]",
+      "--tag",
+      "name:ct-get_images_from_skps-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-3",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-4]",
+      "--tag",
+      "name:ct-get_images_from_skps-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-4",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-5]",
+      "--tag",
+      "name:ct-get_images_from_skps-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "stepname:ct-get_images_from_skps-5",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\", \"tasks\": {\"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/-1\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_10k_SKPs_Trybot.json b/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_10k_SKPs_Trybot.json
new file mode 100644
index 0000000..974b0fa
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.expected/CT_IMG_DECODE_10k_SKPs_Trybot.json
@@ -0,0 +1,1655 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/skia/bin/gn",
+      "gen",
+      "[START_DIR]/out/Debug",
+      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/out/Debug"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
+    ],
+    "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_whitelist = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
+      "@@@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_whitelist:@@@",
+      "@@@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@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/swarming.client",
+      "--url",
+      "https://chromium.googlesource.com/external/swarming.client.git"
+    ],
+    "name": "git setup (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "retry",
+      "fetch",
+      "origin",
+      "master"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "git fetch (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git checkout (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "read revision",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "git clean (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule sync (swarming_client)"
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/swarming.client",
+    "infra_step": true,
+    "name": "submodule update (swarming_client)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "--version"
+    ],
+    "name": "swarming.py --version",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@0.8.6@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=linux*",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go linux"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=darwin",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go mac"
+  },
+  {
+    "cmd": [
+      "download_from_google_storage",
+      "--no_resume",
+      "--platform=win32",
+      "--no_auth",
+      "--bucket",
+      "chromium-luci",
+      "-d",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
+    ],
+    "env": {
+      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:<PATH>"
+    },
+    "name": "download luci-go win"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/luci-go"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree luci-go"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
+      "[START_DIR]/luci-go",
+      "0"
+    ],
+    "name": "Copy Go binary",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/\"@@@",
+      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/swarming_temp_dir"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree swarming_temp_dir"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave1"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1",
+      "511"
+    ],
+    "name": "makedirs slave1",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/1/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/2/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/3/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/4/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/5/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/6/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/7/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/8/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/9/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/10/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-1.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave2"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2",
+      "511"
+    ],
+    "name": "makedirs slave2",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/11/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/12/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/13/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/14/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/15/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/16/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/17/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/18/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/19/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/20/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-2.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave3"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3",
+      "511"
+    ],
+    "name": "makedirs slave3",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/21/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/22/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/23/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/24/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/25/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/26/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/27/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/28/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/29/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/30/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (3)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (3)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-3.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave4"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4",
+      "511"
+    ],
+    "name": "makedirs slave4",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/31/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/32/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/33/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/34/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/35/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/36/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/37/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/38/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/39/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/40/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (4)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (4)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-4.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree slave5"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5",
+      "511"
+    ],
+    "name": "makedirs slave5",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--",
+      "-m",
+      "cp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/41/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/42/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/43/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/44/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/45/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/46/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/47/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/48/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/49/*.skp",
+      "gs://cluster-telemetry/swarming/skps/10k/c37e844a6f8708-eee762104c75bd/50/*.skp",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5"
+    ],
+    "infra_step": true,
+    "name": "gsutil cp (5)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/swarming_temp_dir",
+      "511"
+    ],
+    "name": "makedirs swarming tmp dir (5)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
+    ],
+    "name": "Write ct-get_images_from_skps-5.isolated.gen.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "{\"chromium_build\": \"c37e844a6f8708-eee762104c75bd\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
+      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/skps_version"
+    ],
+    "infra_step": true,
+    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/skps_version"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::isolate]/resources/isolate.py",
+      "[START_DIR]/swarming.client",
+      "batcharchive",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--verbose",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json",
+      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
+    ],
+    "name": "isolate tests",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"}@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-1]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-get_images_from_skps-1",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-get_images_from_skps-1",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-1]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-2]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-get_images_from_skps-2",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-get_images_from_skps-2",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-2]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-3]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-get_images_from_skps-3",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-get_images_from_skps-3",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-3]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-4]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-get_images_from_skps-4",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-get_images_from_skps-4",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-4]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "trigger",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--isolate-server",
+      "https://isolateserver.appspot.com",
+      "--priority",
+      "90",
+      "--shards",
+      "1",
+      "--task-name",
+      "ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571",
+      "--dump-json",
+      "/path/to/tmp/json",
+      "--expiration",
+      "72000",
+      "--io-timeout",
+      "2400",
+      "--hard-timeout",
+      "14400",
+      "--dimension",
+      "cpu",
+      "x86-64",
+      "--dimension",
+      "os",
+      "Ubuntu-14.04",
+      "--dimension",
+      "pool",
+      "Chrome",
+      "--tag",
+      "allow_milo:1",
+      "--tag",
+      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
+      "--tag",
+      "buildnumber:571",
+      "--tag",
+      "data:[dummy hash for ct-get_images_from_skps-5]",
+      "--tag",
+      "master:chromium.testing.master",
+      "--tag",
+      "name:ct-get_images_from_skps-5",
+      "--tag",
+      "os:Ubuntu-14.04",
+      "--tag",
+      "revision:abc123",
+      "--tag",
+      "slavename:test_bot",
+      "--tag",
+      "stepname:ct-get_images_from_skps-5",
+      "--isolated",
+      "[dummy hash for ct-get_images_from_skps-5]"
+    ],
+    "infra_step": true,
+    "name": "[trigger] ct-get_images_from_skps-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-1",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", \"tasks\": {\"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-1",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-2",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", \"tasks\": {\"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-2",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-3",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", \"tasks\": {\"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-3",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-4",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", \"tasks\": {\"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-4",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::swarming]/resources/collect_task.py",
+      "-o",
+      "/path/to/tmp/json",
+      "--task-output-dir",
+      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-5",
+      "--merge-script",
+      "RECIPE_MODULE[skia::swarming]/resources/noop_merge.py",
+      "--merge-additional-args",
+      "[]",
+      "--",
+      "python",
+      "-u",
+      "[START_DIR]/swarming.client/swarming.py",
+      "collect",
+      "--swarming",
+      "https://chromium-swarm.appspot.com",
+      "--decorate",
+      "--print-status-updates",
+      "--json",
+      "{\"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\", \"tasks\": {\"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/571\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
+      "--task-summary-json",
+      "/path/to/tmp/json"
+    ],
+    "name": "ct-get_images_from_skps-5",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
+      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
+      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
+      "@@@STEP_LOG_END@swarming.summary@@@",
+      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
+      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/ct_skps.py b/infra/bots/recipes/ct_skps.py
new file mode 100644
index 0000000..048edcc
--- /dev/null
+++ b/infra/bots/recipes/ct_skps.py
@@ -0,0 +1,461 @@
+# Copyright 2015 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.
+
+
+import math
+
+
+DEPS = [
+  'depot_tools/gsutil',
+  'file',
+  'recipe_engine/context',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+  'recipe_engine/time',
+  'core',
+  'ct',
+  'flavor',
+  'run',
+  'skia_swarming',
+  'vars',
+]
+
+
+SKPS_VERSION_FILE = 'skps_version'
+CT_SKPS_ISOLATE = 'ct_skps.isolate'
+
+# Do not batch archive more slaves than this value. This is used to prevent
+# no output timeouts in the 'isolate tests' step.
+MAX_SLAVES_TO_BATCHARCHIVE = 100
+
+TOOL_TO_DEFAULT_SKPS_PER_SLAVE = {
+    'dm': 10000,
+    'nanobench': 1000,
+    'get_images_from_skps': 10000,
+}
+
+# The SKP repository to use.
+DEFAULT_SKPS_CHROMIUM_BUILD = 'c37e844a6f8708-eee762104c75bd'
+
+
+def RunSteps(api):
+  # Figure out which repository to use.
+  buildername = api.properties['buildername']
+  if '1k' in buildername:
+    ct_page_type = '10k'
+    num_pages = 1000
+  elif '10k' in buildername:
+    ct_page_type = '10k'
+    num_pages = 10000
+  elif '100k' in buildername:
+    ct_page_type = '100k'
+    num_pages = 100000
+  elif '1m' in buildername:
+    ct_page_type = 'All'
+    num_pages = 1000000
+  else:
+    raise Exception('Do not recognise the buildername %s.' % buildername)
+
+  # Figure out which tool to use.
+  if 'DM' in buildername:
+    skia_tool = 'dm'
+    build_target = 'dm'
+  elif 'BENCH' in buildername:
+    skia_tool = 'nanobench'
+    build_target = 'nanobench'
+  elif 'IMG_DECODE' in buildername:
+    skia_tool = 'get_images_from_skps'
+    build_target = 'tools'
+  else:
+    raise Exception('Do not recognise the buildername %s.' % buildername)
+
+  api.core.setup()
+  api.flavor.compile(build_target)
+
+  # Required paths.
+  infrabots_dir = api.vars.skia_dir.join('infra', 'bots')
+  isolate_dir = infrabots_dir.join('ct')
+  isolate_path = isolate_dir.join(CT_SKPS_ISOLATE)
+
+  api.run.copy_build_products(
+      api.flavor.out_dir,
+      isolate_dir)
+  api.skia_swarming.setup(
+      infrabots_dir.join('tools', 'luci-go'),
+      swarming_rev='')
+
+  skps_chromium_build = api.properties.get(
+      'skps_chromium_build', DEFAULT_SKPS_CHROMIUM_BUILD)
+
+  # Set build properties to make finding SKPs convenient.
+  webpage_rankings_link = (
+      'https://storage.cloud.google.com/%s/csv/top-1m.csv'
+          % api.ct.CT_GS_BUCKET)
+  api.step.active_result.presentation.properties['Webpage rankings'] = (
+      webpage_rankings_link)
+  download_skps_link = (
+      'https://pantheon.corp.google.com/storage/browser/%s/swarming/skps/%s/%s/'
+          % (api.ct.CT_GS_BUCKET, ct_page_type, skps_chromium_build))
+  api.step.active_result.presentation.properties['Download SKPs by rank'] = (
+      download_skps_link)
+
+  # Delete swarming_temp_dir to ensure it starts from a clean slate.
+  api.run.rmtree(api.skia_swarming.swarming_temp_dir)
+
+  num_per_slave = api.properties.get(
+      'num_per_slave',
+      min(TOOL_TO_DEFAULT_SKPS_PER_SLAVE[skia_tool], num_pages))
+  ct_num_slaves = api.properties.get(
+      'ct_num_slaves',
+      int(math.ceil(float(num_pages) / num_per_slave)))
+
+  # Try to figure out if the SKPs we are going to isolate already exist
+  # locally by reading the SKPS_VERSION_FILE.
+  download_skps = True
+  expected_version_contents = {
+      "chromium_build": skps_chromium_build,
+      "page_type": ct_page_type,
+      "num_slaves": ct_num_slaves,
+  }
+  skps_dir = api.vars.checkout_root.join('skps', buildername)
+  version_file = skps_dir.join(SKPS_VERSION_FILE)
+  if api.path.exists(version_file):  # pragma: nocover
+    version_file_contents = api.file.read(
+        "Read %s" % version_file,
+        version_file,
+        test_data=expected_version_contents,
+        infra_step=True)
+    actual_version_contents = api.json.loads(version_file_contents)
+    differences = (set(expected_version_contents.items()) ^
+                   set(actual_version_contents.items()))
+    download_skps = len(differences) != 0
+    if download_skps:
+      # Delete and recreate the skps dir.
+      api.run.rmtree(skps_dir)
+      api.file.makedirs(api.path.basename(skps_dir), skps_dir)
+
+  # If a blacklist file exists then specify SKPs to be blacklisted.
+  blacklists_dir = api.vars.skia_dir.join('infra', 'bots', 'ct', 'blacklists')
+  blacklist_file = blacklists_dir.join(
+      '%s_%s_%s.json' % (skia_tool, ct_page_type, skps_chromium_build))
+  blacklist_skps = []
+  if api.path.exists(blacklist_file):  # pragma: nocover
+    blacklist_file_contents = api.file.read(
+        "Read %s" % blacklist_file,
+        blacklist_file,
+        infra_step=True)
+    blacklist_skps = api.json.loads(blacklist_file_contents)['blacklisted_skps']
+
+  for slave_num in range(1, ct_num_slaves + 1):
+    if download_skps:
+      # Download SKPs.
+      api.ct.download_swarming_skps(
+          ct_page_type, slave_num, skps_chromium_build,
+          skps_dir,
+          start_range=((slave_num-1)*num_per_slave) + 1,
+          num_skps=num_per_slave)
+
+    # Create this slave's isolated.gen.json file to use for batcharchiving.
+    extra_variables = {
+        'SLAVE_NUM': str(slave_num),
+        'TOOL_NAME': skia_tool,
+        'GIT_HASH': api.vars.got_revision,
+        'CONFIGURATION': api.vars.configuration,
+        'BUILDER': buildername,
+    }
+    api.skia_swarming.create_isolated_gen_json(
+        isolate_path, isolate_dir, 'linux', 'ct-%s-%s' % (skia_tool, slave_num),
+        extra_variables, blacklist=blacklist_skps)
+
+  if download_skps:
+    # Since we had to download SKPs create an updated version file.
+    api.file.write("Create %s" % version_file,
+                   version_file,
+                   api.json.dumps(expected_version_contents),
+                   infra_step=True)
+
+  # Batcharchive everything on the isolate server for efficiency.
+  max_slaves_to_batcharchive = MAX_SLAVES_TO_BATCHARCHIVE
+  if '1m' in buildername:
+    # Break up the "isolate tests" step into batches with <100k files due to
+    # https://github.com/luci/luci-go/issues/9
+    max_slaves_to_batcharchive = 5
+  tasks_to_swarm_hashes = []
+  for slave_start_num in xrange(1, ct_num_slaves+1, max_slaves_to_batcharchive):
+    m = min(max_slaves_to_batcharchive, ct_num_slaves)
+    batcharchive_output = api.skia_swarming.batcharchive(
+        targets=['ct-' + skia_tool + '-%s' % num for num in range(
+            slave_start_num, slave_start_num + m)])
+    tasks_to_swarm_hashes.extend(batcharchive_output)
+  # Sort the list to go through tasks in order.
+  tasks_to_swarm_hashes.sort()
+
+  # Trigger all swarming tasks.
+  dimensions={'os': 'Ubuntu-14.04', 'cpu': 'x86-64', 'pool': 'Chrome'}
+  if 'GPU' in buildername:
+    dimensions['gpu'] = '10de:104a'
+  tasks = api.skia_swarming.trigger_swarming_tasks(
+      tasks_to_swarm_hashes, dimensions=dimensions, io_timeout=40*60)
+
+  # Now collect all tasks.
+  env = {'AWS_CREDENTIAL_FILE': None, 'BOTO_CONFIG': None}
+  failed_tasks = []
+  for task in tasks:
+    try:
+      api.skia_swarming.collect_swarming_task(task)
+
+      if skia_tool == 'nanobench':
+        output_dir = api.skia_swarming.tasks_output_dir.join(
+            task.title).join('0')
+        utc = api.time.utcnow()
+        gs_dest_dir = 'ct/%s/%d/%02d/%02d/%02d/' % (
+            ct_page_type, utc.year, utc.month, utc.day, utc.hour)
+        for json_output in api.file.listdir('output dir', output_dir):
+          with api.context(env=env):
+            api.gsutil.upload(
+                name='upload json output',
+                source=output_dir.join(json_output),
+                bucket='skia-perf',
+                dest=gs_dest_dir,
+                args=['-R']
+            )
+
+    except api.step.StepFailure as e:
+      # Add SKP links for convenience.
+      api.step.active_result.presentation.links['Webpage rankings'] = (
+          webpage_rankings_link)
+      api.step.active_result.presentation.links['Download SKPs by rank'] = (
+          download_skps_link)
+      failed_tasks.append(e)
+
+  if failed_tasks:
+    raise api.step.StepFailure(
+        'Failed steps: %s' % ', '.join([f.name for f in failed_tasks]))
+
+
+def GenTests(api):
+  ct_num_slaves = 5
+  num_per_slave = 10
+  skia_revision = 'abc123'
+  path_config = 'kitchen'
+
+  yield(
+    api.test('CT_DM_10k_SKPs') +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  yield(
+    api.test('CT_IMG_DECODE_10k_SKPs') +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_'
+                    '10k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  yield(
+    api.test('CT_DM_100k_SKPs') +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  yield(
+    api.test('CT_IMG_DECODE_100k_SKPs') +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_'
+                    '100k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  yield(
+    api.test('CT_GPU_BENCH_1k_SKPs') +
+    api.properties(
+        buildername=
+            'Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    ) +
+    api.path.exists(
+        api.path['start_dir'].join('skia'),
+        api.path['start_dir'].join('src')
+    )
+  )
+
+  yield(
+    api.test('CT_CPU_BENCH_10k_SKPs') +
+    api.properties(
+        buildername=
+            'Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    ) +
+    api.path.exists(
+        api.path['start_dir'].join('skia'),
+        api.path['start_dir'].join('src')
+    )
+  )
+
+  yield(
+    api.test('CT_GPU_BENCH_10k_SKPs') +
+    api.properties(
+        buildername=
+            'Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    ) +
+    api.path.exists(
+        api.path['start_dir'].join('skia'),
+        api.path['start_dir'].join('src')
+    )
+  )
+
+  yield(
+    api.test('CT_DM_1m_SKPs') +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  yield (
+    api.test('CT_DM_SKPs_UnknownBuilder') +
+    api.properties(
+        buildername=
+            'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_UnknownRepo_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    ) +
+    api.expect_exception('Exception')
+  )
+
+  yield (
+    api.test('CT_10k_SKPs_UnknownBuilder') +
+    api.properties(
+        buildername=
+            'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_UnknownTool_10k_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    ) +
+    api.expect_exception('Exception')
+  )
+
+  yield(
+    api.test('CT_DM_1m_SKPs_slave3_failure') +
+    api.step_data('ct-dm-3', retcode=1) +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  yield(
+    api.test('CT_DM_1m_SKPs_2slaves_failure') +
+    api.step_data('ct-dm-1', retcode=1) +
+    api.step_data('ct-dm-3', retcode=1) +
+    api.properties(
+        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs',
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+    )
+  )
+
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs'
+  yield(
+    api.test('CT_DM_10k_SKPs_Trybot') +
+    api.properties(
+        buildername=builder,
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        patch_storage='gerrit') +
+    api.properties.tryserver(
+        buildername=builder,
+        gerrit_project='skia',
+        gerrit_url='https://skia-review.googlesource.com/',
+    )
+  )
+
+  builder = ('Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_'
+             '10k_SKPs')
+  yield(
+    api.test('CT_IMG_DECODE_10k_SKPs_Trybot') +
+    api.properties(
+        buildername=builder,
+        path_config=path_config,
+        swarm_out_dir='[SWARM_OUT_DIR]',
+        ct_num_slaves=ct_num_slaves,
+        num_per_slave=num_per_slave,
+        repository='https://skia.googlesource.com/skia.git',
+        revision=skia_revision,
+        patch_storage='gerrit') +
+    api.properties.tryserver(
+        buildername=builder,
+        gerrit_project='skia',
+        gerrit_url='https://skia-review.googlesource.com/',
+    )
+  )
diff --git a/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json
new file mode 100644
index 0000000..242193e
--- /dev/null
+++ b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json
@@ -0,0 +1,149 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/89/456789/12",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "RECIPE_MODULE[skia::core]/resources/run_binary_size_analysis.py",
+      "--library",
+      "[START_DIR]/out/Release/lib/libskia.so",
+      "--githash",
+      "abc123",
+      "--gsutil_path",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
+      "--issue_number",
+      "456789"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "generate and upload binary size data"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json
new file mode 100644
index 0000000..7832395
--- /dev/null
+++ b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json
@@ -0,0 +1,157 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "RECIPE_MODULE[skia::core]/resources/generate_and_upload_doxygen.py"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "generate and upload doxygen"
+  },
+  {
+    "cmd": [
+      "python",
+      "RECIPE_MODULE[skia::core]/resources/run_binary_size_analysis.py",
+      "--library",
+      "[START_DIR]/out/Release/lib/libskia.so",
+      "--githash",
+      "abc123",
+      "--gsutil_path",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "generate and upload binary size data"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/housekeeper.py b/infra/bots/recipes/housekeeper.py
new file mode 100644
index 0000000..5d6a5f9
--- /dev/null
+++ b/infra/bots/recipes/housekeeper.py
@@ -0,0 +1,76 @@
+# 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.
+
+
+# Recipe for the Skia PerCommit Housekeeper.
+
+DEPS = [
+  'depot_tools/bot_update',
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/step',
+  'core',
+  'run',
+  'vars',
+]
+
+
+def RunSteps(api):
+  # Checkout, compile, etc.
+  api.core.setup()
+
+  cwd = api.path['checkout']
+
+  # TODO(borenet): Detect static initializers?
+
+  with api.context(cwd=cwd):
+    gsutil_path = api.bot_update._module.PACKAGE_REPO_ROOT.join('gsutil.py')
+    if not api.vars.is_trybot:
+      api.run(
+        api.step,
+        'generate and upload doxygen',
+        cmd=['python', api.core.resource('generate_and_upload_doxygen.py')],
+        abort_on_failure=False)
+
+    cmd = ['python', api.core.resource('run_binary_size_analysis.py'),
+           '--library', api.vars.skia_out.join(
+               'Release', 'lib', 'libskia.so'),
+           '--githash', api.properties['revision'],
+           '--gsutil_path', gsutil_path]
+    if api.vars.is_trybot:
+      cmd.extend(['--issue_number', str(api.properties['patch_issue'])])
+    api.run(
+      api.step,
+      'generate and upload binary size data',
+      cmd=cmd,
+      abort_on_failure=False)
+
+
+def GenTests(api):
+  yield (
+      api.test('Housekeeper-PerCommit') +
+      api.properties(buildername='Housekeeper-PerCommit',
+                     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'])
+  )
+  yield (
+      api.test('Housekeeper-PerCommit-Trybot') +
+      api.properties(buildername='Housekeeper-PerCommit',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     patch_storage='gerrit',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.properties.tryserver(
+          buildername='Housekeeper-PerCommit',
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      ) +
+      api.path.exists(api.path['start_dir'])
+  )
diff --git a/infra/bots/recipes/infra.expected/failed_all_updates.json b/infra/bots/recipes/infra.expected/failed_all_updates.json
new file mode 100644
index 0000000..e6ddbe4
--- /dev/null
+++ b/infra/bots/recipes/infra.expected/failed_all_updates.json
@@ -0,0 +1,241 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 2)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 3)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 4)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 5)",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Step('update go pkgs (attempt 5)') failed with return_code 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/infra.expected/failed_one_update.json b/infra/bots/recipes/infra.expected/failed_one_update.json
new file mode 100644
index 0000000..0d22fb5
--- /dev/null
+++ b/infra/bots/recipes/infra.expected/failed_one_update.json
@@ -0,0 +1,185 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs (attempt 2)"
+  },
+  {
+    "cmd": [
+      "make",
+      "-C",
+      "infra/bots",
+      "test"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>"
+    },
+    "name": "infra_tests"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/infra.expected/infra_tests.json b/infra/bots/recipes/infra.expected/infra_tests.json
new file mode 100644
index 0000000..a5891f5
--- /dev/null
+++ b/infra/bots/recipes/infra.expected/infra_tests.json
@@ -0,0 +1,163 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs"
+  },
+  {
+    "cmd": [
+      "make",
+      "-C",
+      "infra/bots",
+      "test"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>"
+    },
+    "name": "infra_tests"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/infra.py b/infra/bots/recipes/infra.py
new file mode 100644
index 0000000..5482a7f
--- /dev/null
+++ b/infra/bots/recipes/infra.py
@@ -0,0 +1,63 @@
+# 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 which runs the Skia infra tests.
+
+
+DEPS = [
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+  'core',
+  'infra',
+  'run',
+  'vars',
+]
+
+
+def RunSteps(api):
+  api.vars.setup()
+  api.core.checkout_steps()
+  api.infra.update_go_deps()
+
+  # Run the infra tests.
+  with api.context(cwd=api.vars.skia_dir, env=api.infra.go_env):
+    api.step('infra_tests', cmd=['make', '-C', 'infra/bots', 'test'])
+
+
+def GenTests(api):
+  yield (
+      api.test('infra_tests') +
+      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]')
+  )
+
+  yield (
+    api.test('failed_one_update') +
+      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+    api.step_data('update go pkgs', retcode=1)
+  )
+
+  yield (
+    api.test('failed_all_updates') +
+      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+    api.step_data('update go pkgs', retcode=1) +
+    api.step_data('update go pkgs (attempt 2)', retcode=1) +
+    api.step_data('update go pkgs (attempt 3)', retcode=1) +
+    api.step_data('update go pkgs (attempt 4)', retcode=1) +
+    api.step_data('update go pkgs (attempt 5)', retcode=1)
+  )
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan.json
new file mode 100644
index 0000000..440d200
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan.json
@@ -0,0 +1,673 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config vk --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": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android.json
new file mode 100644
index 0000000..8960577
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android.json
@@ -0,0 +1,748 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -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 --nogpu --pre_log --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 swarming_bot_id skia-bot-123 swarming_task_id 123456 --key arch arm compiler Clang cpu_or_gpu CPU cpu_or_gpu_value Exynos5250 extra_config Android model Nexus10 os Android; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android.json
new file mode 100644
index 0000000..4f33dc8
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android.json
@@ -0,0 +1,673 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 --match ~blurroundrect ~patch_grid ~desk_carsvg ~keymobi_shop_mobileweb_ebay_com.skp ~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": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android.json
new file mode 100644
index 0000000..757d807
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android.json
@@ -0,0 +1,748 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui gles glesmsaa4 glesnvpr4 glesnvprdit4 --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 swarming_bot_id skia-bot-123 swarming_task_id 123456 --key arch arm compiler Clang cpu_or_gpu GPU cpu_or_gpu_value Tegra3 extra_config Android model Nexus7 os Android; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android.json
new file mode 100644
index 0000000..13c8184
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android.json
@@ -0,0 +1,748 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui f16 srgb gles --match ~blurroundrect ~patch_grid ~desk_carsvg ~desk_unicodetable ~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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 swarming_bot_id skia-bot-123 swarming_task_id 123456 --key arch x86 compiler Clang cpu_or_gpu GPU cpu_or_gpu_value PowerVR extra_config Android model NexusPlayer os Android; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan.json
new file mode 100644
index 0000000..1c7dcbb
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan.json
@@ -0,0 +1,748 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -i /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/nanobench --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config vk --match ~blurroundrect ~patch_grid ~desk_carsvg ~desk_unicodetable ~Xfermode ~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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 swarming_bot_id skia-bot-123 swarming_task_id 123456 --key arch x86 compiler Clang cpu_or_gpu GPU cpu_or_gpu_value PowerVR extra_config Android_Vulkan model NexusPlayer os Android; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android.json
new file mode 100644
index 0000000..7210fa6
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android.json
@@ -0,0 +1,748 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/nanobench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/nanobench --undefok -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 --nocpu --pre_log --images --gpuStatsDump true --useThermalManager 1,1,10,1000 --scales 1.0 1.1 --config 8888 nonrendering hwui f16 srgb gles glesmsaa4 glesnvpr4 glesnvprdit4 glesinst glesinst4 --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 --outResultsFile /sdcard/revenge_of_the_skiabot/perf/nanobench_abc123_1337000001.json --properties gitHash abc123 swarming_bot_id skia-bot-123 swarming_task_id 123456 --key arch arm64 compiler Clang cpu_or_gpu GPU cpu_or_gpu_value TegraX1 extra_config Android model PixelC os Android; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/nanobench.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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/",
+      "nanobench.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release.json b/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release.json
new file mode 100644
index 0000000..f4109ee
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release.json
@@ -0,0 +1,815 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nSSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')\nwith open(SSH_MACHINE_FILE, 'r') as f:\n  print f.read()\n"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read chromeos ip",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@SSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(SSH_MACHINE_FILE, 'r') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/resources"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "sudo",
+      "mount",
+      "-i",
+      "-o",
+      "remount,exec",
+      "/home/chronos"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "remount /home/chronos/user/ as exec"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/bin"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/bin"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/bin"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/bin"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skia/resources",
+      "foo@127.0.0.1:/home/chronos/user/resources"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skia/resources foo@127.0.0.1:/home/chronos/user/resources",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/skps"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skp",
+      "foo@127.0.0.1:/home/chronos/user/skps"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skp foo@127.0.0.1:/home/chronos/user/skps",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SKP_VERSION foo@127.0.0.1:/home/chronos/user/SKP_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/images"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skimage",
+      "foo@127.0.0.1:/home/chronos/user/images"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skimage foo@127.0.0.1:/home/chronos/user/images",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SK_IMAGE_VERSION foo@127.0.0.1:/home/chronos/user/SK_IMAGE_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/svgs"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/svg",
+      "foo@127.0.0.1:/home/chronos/user/svgs"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/svg foo@127.0.0.1:/home/chronos/user/svgs",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SVG_VERSION foo@127.0.0.1:/home/chronos/user/SVG_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/perf"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/perf"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/perf"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/out/Release/nanobench",
+      "foo@127.0.0.1:/home/chronos/user/bin/nanobench"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "scp [START_DIR]/out/Release/nanobench foo@127.0.0.1:/home/chronos/user/bin/nanobench",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "chmod",
+      "+x",
+      "/home/chronos/user/bin/nanobench"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "chmod nanobench"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "/home/chronos/user/bin/nanobench",
+      "--undefok",
+      "-i",
+      "/home/chronos/user/resources",
+      "--skps",
+      "/home/chronos/user/skps",
+      "--images",
+      "/home/chronos/user/images/nanobench",
+      "--svgs",
+      "/home/chronos/user/svgs",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "gles",
+      "--match",
+      "~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",
+      "--outResultsFile",
+      "/home/chronos/user/perf/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "arm",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "MaliT764",
+      "model",
+      "Chromebook_C100p",
+      "os",
+      "ChromeOS"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "foo@127.0.0.1:/home/chronos/user/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release/data"
+    ],
+    "infra_step": true,
+    "name": "scp -r foo@127.0.0.1:/home/chronos/user/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release/data",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug.json b/infra/bots/recipes/perf.expected/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug.json
new file mode 100644
index 0000000..91ef9e2
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug.json
@@ -0,0 +1,377 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nCHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')\nwith open(CHROMECAST_IP_FILE, 'r') as f:\n  print f.read()\n"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read chromecast ip",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(CHROMECAST_IP_FILE, 'r') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "connect",
+      "192.168.1.2:5555"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "adb connect 192.168.1.2:5555"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/cache/skia/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /cache/skia/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /cache/skia/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "/cache/skia/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/cache/skia/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /cache/skia/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /cache/skia/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/nanobench",
+      "/cache/skia/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "root@192.168.1.2",
+      "/cache/skia/nanobench",
+      "--config",
+      "gles",
+      "-i",
+      "/cache/skia/resources",
+      "--images",
+      "/cache/skia/resources/color_wheel.jpg",
+      "--skps",
+      "/cache/skia/skps",
+      "--pre_log",
+      "--match",
+      "~matrixconvolution",
+      "~blur_image_filter",
+      "~blur_0.01",
+      "~GM_animated-image-blurs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release.json b/infra/bots/recipes/perf.expected/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release.json
new file mode 100644
index 0000000..303bcf0
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release.json
@@ -0,0 +1,474 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nCHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')\nwith open(CHROMECAST_IP_FILE, 'r') as f:\n  print f.read()\n"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read chromecast ip",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(CHROMECAST_IP_FILE, 'r') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "connect",
+      "192.168.1.2:5555"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "adb connect 192.168.1.2:5555"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/cache/skia/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /cache/skia/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /cache/skia/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "/cache/skia/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    hp = os.path.realpath(os.path.join(host, p, f))\n    if os.stat(hp).st_size > (3 * 1024 * 1024):\n      print \"Skipping because it is too big\"\n    else:\n      subprocess.check_call(['adb', 'push',\n                            hp, os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/cache/skia/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /cache/skia/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    hp = os.path.realpath(os.path.join(host, p, f))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.stat(hp).st_size > (3 * 1024 * 1024):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      print \"Skipping because it is too big\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@    else:@@@",
+      "@@@STEP_LOG_LINE@python.inline@      subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                            hp, os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/cache/skia/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /cache/skia/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-r",
+      "/cache/skia/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /cache/skia/perf"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/cache/skia/perf"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /cache/skia/perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/nanobench",
+      "/cache/skia/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push nanobench"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "root@192.168.1.2",
+      "/cache/skia/nanobench",
+      "--config",
+      "gles",
+      "-i",
+      "/cache/skia/resources",
+      "--images",
+      "/cache/skia/resources/color_wheel.jpg",
+      "--skps",
+      "/cache/skia/skps",
+      "--pre_log",
+      "--match",
+      "~matrixconvolution",
+      "~blur_image_filter",
+      "~blur_0.01",
+      "~GM_animated-image-blurs",
+      "--outResultsFile",
+      "/cache/skia/perf/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "arm",
+      "compiler",
+      "GCC",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "Cortex_A7",
+      "model",
+      "Chorizo",
+      "os",
+      "Chromecast"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/cache/skia/perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release/data"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /cache/skia/perf [CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release/data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release.json b/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release.json
new file mode 100644
index 0000000..21fa833
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release.json
@@ -0,0 +1,240 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release/data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release/data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]/out/Release/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--nogpu",
+      "--pre_log",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "gl",
+      "glmsaa8",
+      "glnvpr8",
+      "glnvprdit8",
+      "glinst",
+      "glinst8",
+      "--match",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release/data/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX",
+      "model",
+      "MacMini6.2",
+      "os",
+      "Mac"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer.json b/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer.json
new file mode 100644
index 0000000..2a9b6e9
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer.json
@@ -0,0 +1,152 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]/out/Debug/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "commandbuffer",
+      "--match",
+      "~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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release.json b/infra/bots/recipes/perf.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release.json
new file mode 100644
index 0000000..1d36720
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release.json
@@ -0,0 +1,245 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]/out/Release/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--nogpu",
+      "--pre_log",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "565",
+      "gl",
+      "glmsaa8",
+      "glnvpr8",
+      "glnvprdit8",
+      "--match",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json b/infra/bots/recipes/perf.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
new file mode 100644
index 0000000..5a25fc9
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
@@ -0,0 +1,172 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[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]/out/Release/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "gl",
+      "glmsaa8",
+      "glnvpr8",
+      "glnvprdit8",
+      "--loops",
+      "1",
+      "--samples",
+      "1",
+      "--keepAlive",
+      "true",
+      "--match",
+      "~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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+    },
+    "name": "nanobench"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json b/infra/bots/recipes/perf.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
new file mode 100644
index 0000000..f989118
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
@@ -0,0 +1,174 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[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]/out/Release/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "gl",
+      "glmsaa8",
+      "glnvpr8",
+      "glnvprdit8",
+      "--loops",
+      "1",
+      "--samples",
+      "1",
+      "--keepAlive",
+      "true",
+      "--match",
+      "~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",
+      "--abandonGpuContext",
+      "--nocpu"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+    },
+    "name": "nanobench"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json
new file mode 100644
index 0000000..459e004
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json
@@ -0,0 +1,160 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Debug/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "vk",
+      "--match",
+      "~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"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_sdk/lib:[START_DIR]/linux_vulkan_intel_driver_debug",
+      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/linux_vulkan_sdk/bin",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VK_ICD_FILENAMES": "[START_DIR]/linux_vulkan_intel_driver_debug/intel_icd.x86_64.json"
+    },
+    "name": "symbolized nanobench"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release.json b/infra/bots/recipes/perf.expected/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release.json
new file mode 100644
index 0000000..e1cf0cb
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release.json
@@ -0,0 +1,247 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release/data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release/data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Release/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "gl",
+      "gles",
+      "--match",
+      "~native_image_to_raster_surface",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release/data/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris540",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Ubuntu16"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
+      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE.json b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE.json
new file mode 100644
index 0000000..2def532
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE.json
@@ -0,0 +1,242 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE\\data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE\\data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Release_x64\\nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]\\skia\\resources",
+      "--skps",
+      "[START_DIR]\\skp",
+      "--images",
+      "[START_DIR]\\skimage\\nanobench",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "angle_d3d11_es2",
+      "angle_d3d11_es2_msaa8",
+      "--match",
+      "~native_image_to_raster_surface",
+      "~shapes_mixed_10000_32x33",
+      "~shapes_oval_10000_32x32",
+      "~shapes_oval_10000_32x33",
+      "~shapes_rect_100_500x500",
+      "~shapes_rrect_10000_32x32",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE\\data\\nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "RadeonR9M470X",
+      "extra_config",
+      "ANGLE",
+      "model",
+      "AlphaR2",
+      "os",
+      "Win10"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE\\data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE.json b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE.json
new file mode 100644
index 0000000..1da89e9
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE.json
@@ -0,0 +1,236 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE\\data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE\\data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Release_x64\\nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]\\skia\\resources",
+      "--skps",
+      "[START_DIR]\\skp",
+      "--images",
+      "[START_DIR]\\skimage\\nanobench",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "angle_d3d11_es2",
+      "--match",
+      "~tile_image_filter_tiled_64",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE\\data\\nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris540",
+      "extra_config",
+      "ANGLE",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Win10"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE\\data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan.json
new file mode 100644
index 0000000..476ad0a
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan.json
@@ -0,0 +1,251 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan\\data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan\\data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Release_x64\\nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]\\skia\\resources",
+      "--skps",
+      "[START_DIR]\\skp",
+      "--images",
+      "[START_DIR]\\skimage\\nanobench",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "vk",
+      "--match",
+      "~GM_varied_text_clipped_lcd",
+      "~GM_varied_text_ignorable_clip_lcd",
+      "~Xfermode_DstATop_aa",
+      "~Xfermode_SrcIn_aa",
+      "~Xfermode_SrcOut_aa",
+      "~Xfermode_Src_aa",
+      "~fontscaler_lcd",
+      "~rotated_rects_aa_alternating_transparent_and_opaque_src",
+      "~rotated_rects_aa_changing_transparent_src",
+      "~rotated_rects_aa_same_transparent_src",
+      "~shadermask_LCD_FF",
+      "~srcmode_rects_1",
+      "~text_16_LCD_88",
+      "~text_16_LCD_BK",
+      "~text_16_LCD_FF",
+      "~text_16_LCD_WT",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan\\data\\nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris540",
+      "extra_config",
+      "Vulkan",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Win10"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan\\data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE.json b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE.json
new file mode 100644
index 0000000..a0f822b
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE.json
@@ -0,0 +1,240 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE\\data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE\\data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Release_x64\\nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]\\skia\\resources",
+      "--skps",
+      "[START_DIR]\\skp",
+      "--images",
+      "[START_DIR]\\skimage\\nanobench",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "angle_d3d11_es2",
+      "angle_d3d11_es2_msaa8",
+      "--match",
+      "~native_image_to_raster_surface",
+      "~shapes_mixed_10000_32x33",
+      "~shapes_rect_100_500x500",
+      "~shapes_rrect_10000_32x32",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE\\data\\nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX960",
+      "extra_config",
+      "ANGLE",
+      "model",
+      "ShuttleC",
+      "os",
+      "Win10"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE\\data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug.json b/infra/bots/recipes/perf.expected/Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug.json
new file mode 100644
index 0000000..123de9e
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug.json
@@ -0,0 +1,158 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]\\skia\\resources",
+      "--skps",
+      "[START_DIR]\\skp",
+      "--images",
+      "[START_DIR]\\skimage\\nanobench",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--nogpu",
+      "--pre_log",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "565",
+      "gl",
+      "glmsaa8",
+      "glnvpr8",
+      "glnvprdit8",
+      "--match",
+      "~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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release.json b/infra/bots/recipes/perf.expected/Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release.json
new file mode 100644
index 0000000..d489b1a
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release.json
@@ -0,0 +1,239 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release\\data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release\\data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Release_x64\\nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]\\skia\\resources",
+      "--skps",
+      "[START_DIR]\\skp",
+      "--images",
+      "[START_DIR]\\skimage\\nanobench",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--nogpu",
+      "--pre_log",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "565",
+      "gl",
+      "glmsaa8",
+      "glnvpr8",
+      "glnvprdit8",
+      "--match",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release\\data\\nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "model",
+      "GCE",
+      "os",
+      "Win2k8"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\perfdata\\Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release\\data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json b/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
new file mode 100644
index 0000000..a9b4443
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
@@ -0,0 +1,660 @@
+[
+  {
+    "cmd": [
+      "ios.py"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "setup_device"
+  },
+  {
+    "cmd": [
+      "ideviceinstaller",
+      "-i",
+      "[START_DIR]/out/Release/dm.app"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "install_dm"
+  },
+  {
+    "cmd": [
+      "ideviceinstaller",
+      "-i",
+      "[START_DIR]/out/Release/nanobench.app"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "install_nanobench"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skia/resources",
+      "resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skia/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm skps"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir skps"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skp",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skp"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm images"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir images"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skimage",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skimage"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm svgs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir svgs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/svg",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/svg"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "perf"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm perf"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "perf"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir perf"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "name": "get swarming task id",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "idevice-app-runner",
+      "-s",
+      "com.google.nanobench",
+      "--args",
+      "--undefok",
+      "-i",
+      "resources",
+      "--skps",
+      "skps",
+      "--images",
+      "images/nanobench",
+      "--svgs",
+      "svgs",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--skps",
+      "ignore_skps",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "gles",
+      "--match",
+      "~blurroundrect",
+      "~patch_grid",
+      "~desk_carsvg",
+      "~keymobi",
+      "~path_hairline",
+      "~GLInstancedArraysBench",
+      "~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",
+      "--outResultsFile",
+      "perf/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--key",
+      "arch",
+      "arm",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GX6450",
+      "model",
+      "iPadMini4",
+      "os",
+      "iOS"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release/data",
+      "511"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_pull_if_needed",
+      "perf",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release/data"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.nanobench",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull_if_needed perf"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/failed_push.json b/infra/bots/recipes/perf.expected/failed_push.json
new file mode 100644
index 0000000..690cd54
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/failed_push.json
@@ -0,0 +1,129 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "reboot",
+      "-p"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "shut down device to quarantine bot"
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "reason": "Infra Failure: Step('push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources') returned 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.expected/trybot.json b/infra/bots/recipes/perf.expected/trybot.json
new file mode 100644
index 0000000..27b0da6
--- /dev/null
+++ b/infra/bots/recipes/perf.expected/trybot.json
@@ -0,0 +1,244 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release/data"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree data"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release/data",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs data",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]/out/Release_x64/nanobench",
+      "--undefok",
+      "-i",
+      "[START_DIR]/skia/resources",
+      "--skps",
+      "[START_DIR]/skp",
+      "--images",
+      "[START_DIR]/skimage/nanobench",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--nocpu",
+      "--pre_log",
+      "--images",
+      "--gpuStatsDump",
+      "true",
+      "--scales",
+      "1.0",
+      "1.1",
+      "--config",
+      "8888",
+      "nonrendering",
+      "hwui",
+      "f16",
+      "srgb",
+      "gl",
+      "--match",
+      "~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",
+      "--outResultsFile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release/data/nanobench_abc123_1337000001.json",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "issue",
+      "456789",
+      "patchset",
+      "12",
+      "patch_storage",
+      "gerrit",
+      "swarming_bot_id",
+      "",
+      "swarming_task_id",
+      "",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelHD4600",
+      "model",
+      "ShuttleB",
+      "os",
+      "Win10"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "nanobench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/perf.py b/infra/bots/recipes/perf.py
new file mode 100644
index 0000000..9c69499
--- /dev/null
+++ b/infra/bots/recipes/perf.py
@@ -0,0 +1,441 @@
+# 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 perf.
+
+
+import calendar
+
+
+DEPS = [
+  'core',
+  'env',
+  'file',
+  'flavor',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/platform',
+  'recipe_engine/properties',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'recipe_engine/time',
+  'run',
+  'vars',
+]
+
+
+def nanobench_flags(bot):
+  args = ['--pre_log']
+
+  if 'GPU' in bot:
+    args.append('--images')
+    args.extend(['--gpuStatsDump', 'true'])
+
+  if 'Android' in bot and 'GPU' in bot:
+    args.extend(['--useThermalManager', '1,1,10,1000'])
+
+  args.extend(['--scales', '1.0', '1.1'])
+
+  if 'iOS' in bot:
+    args.extend(['--skps', 'ignore_skps'])
+
+  configs = ['8888', 'nonrendering', 'hwui' ]
+
+  if '-arm-' not in bot:
+    # For Android CPU tests, these take too long and cause the task to time out.
+    configs += [ 'f16', 'srgb' ]
+  if '-GCE-' in bot:
+    configs += [ '565' ]
+
+  gl_prefix = 'gl'
+  sample_count = '8'
+  if 'Android' in bot or 'iOS' in bot:
+    sample_count = '4'
+    # The NVIDIA_Shield has a regular OpenGL implementation. We bench that
+    # instead of ES.
+    if 'NVIDIA_Shield' not in bot:
+      gl_prefix = 'gles'
+    # The NP produces a long error stream when we run with MSAA.
+    # iOS crashes (skia:6399)
+    if 'NexusPlayer' in bot or 'iOS' in bot:
+      sample_count = ''
+  elif 'Intel' in bot:
+    sample_count = ''
+  elif 'ChromeOS' in bot:
+    gl_prefix = 'gles'
+
+  configs.append(gl_prefix)
+  if sample_count is not '':
+    configs.extend([gl_prefix + 'msaa' + sample_count,
+      gl_prefix + 'nvpr' + sample_count,
+      gl_prefix + 'nvprdit' + sample_count])
+
+  # We want to test both the OpenGL config and the GLES config on Linux Intel:
+  # GL is used by Chrome, GLES is used by ChromeOS.
+  if 'Intel' in bot and 'Ubuntu' in bot:
+    configs.append('gles')
+
+  # Bench instanced rendering on a limited number of platforms
+  inst_config = gl_prefix + 'inst'
+  if 'PixelC' in bot or 'NVIDIA_Shield' in bot or 'MacMini6.2' in bot:
+    configs.extend([inst_config, inst_config + sample_count])
+
+  if 'CommandBuffer' in bot:
+    configs = ['commandbuffer']
+  if 'Vulkan' in bot:
+    configs = ['vk']
+
+  if 'ANGLE' in bot:
+    # Test only ANGLE configs.
+    configs = ['angle_d3d11_es2']
+    if sample_count is not '':
+      configs.append('angle_d3d11_es2_msaa' + sample_count)
+
+  if 'ChromeOS' in bot:
+    # Just run GLES for now - maybe add gles_msaa4 in the future
+    configs = ['gles']
+
+  args.append('--config')
+  args.extend(configs)
+
+  if 'Valgrind' in bot:
+    # Don't care about Valgrind performance.
+    args.extend(['--loops',   '1'])
+    args.extend(['--samples', '1'])
+    # Ensure that the bot framework does not think we have timed out.
+    args.extend(['--keepAlive', 'true'])
+
+  match = []
+  if 'Android' in bot:
+    # Segfaults when run as GPU bench. Very large texture?
+    match.append('~blurroundrect')
+    match.append('~patch_grid')  # skia:2847
+    match.append('~desk_carsvg')
+  if 'NexusPlayer' in bot:
+    match.append('~desk_unicodetable')
+  if 'Nexus5' in bot:
+    match.append('~keymobi_shop_mobileweb_ebay_com.skp')  # skia:5178
+  if 'iOS' in bot:
+    match.append('~blurroundrect')
+    match.append('~patch_grid')  # skia:2847
+    match.append('~desk_carsvg')
+    match.append('~keymobi')
+    match.append('~path_hairline')
+    match.append('~GLInstancedArraysBench') # skia:4714
+  if 'IntelIris540' in bot and 'ANGLE' in bot:
+    match.append('~tile_image_filter_tiled_64')  # skia:6082
+  if 'Intel' in bot and 'Ubuntu' in bot and not 'Vulkan' in bot:
+    match.append('~native_image_to_raster_surface')  # skia:6401
+  if 'Vulkan' in bot and 'IntelIris540' in bot and 'Win' in bot:
+    # skia:6398
+    match.append('~GM_varied_text_clipped_lcd')
+    match.append('~GM_varied_text_ignorable_clip_lcd')
+    match.append('~Xfermode_DstATop_aa')
+    match.append('~Xfermode_SrcIn_aa')
+    match.append('~Xfermode_SrcOut_aa')
+    match.append('~Xfermode_Src_aa')
+    match.append('~fontscaler_lcd')
+    match.append('~rotated_rects_aa_alternating_transparent_and_opaque_src')
+    match.append('~rotated_rects_aa_changing_transparent_src')
+    match.append('~rotated_rects_aa_same_transparent_src')
+    match.append('~shadermask_LCD_FF')
+    match.append('~srcmode_rects_1')
+    match.append('~text_16_LCD_88')
+    match.append('~text_16_LCD_BK')
+    match.append('~text_16_LCD_FF')
+    match.append('~text_16_LCD_WT')
+  if 'Vulkan' in bot and 'NexusPlayer' in bot:
+    match.append('~Xfermode') # skia:6691
+  if 'ANGLE' in bot and any('msaa' in x for x in configs):
+    match.append('~native_image_to_raster_surface')  # skia:6457
+  if 'ANGLE' in bot and 'Radeon' in bot and 'Release' in bot:
+    # skia:6534
+    match.append('~shapes_mixed_10000_32x33')
+    match.append('~shapes_oval_10000_32x32')
+    match.append('~shapes_oval_10000_32x33')
+    match.append('~shapes_rect_100_500x500')
+    match.append('~shapes_rrect_10000_32x32')
+  if 'ANGLE' in bot and 'GTX960' in bot and 'Release' in bot:
+    # skia:6534
+    match.append('~shapes_mixed_10000_32x33')
+    match.append('~shapes_rect_100_500x500')
+    match.append('~shapes_rrect_10000_32x32')
+
+  # We do not need or want to benchmark the decodes of incomplete images.
+  # In fact, in nanobench we assert that the full image decode succeeds.
+  match.append('~inc0.gif')
+  match.append('~inc1.gif')
+  match.append('~incInterlaced.gif')
+  match.append('~inc0.jpg')
+  match.append('~incGray.jpg')
+  match.append('~inc0.wbmp')
+  match.append('~inc1.wbmp')
+  match.append('~inc0.webp')
+  match.append('~inc1.webp')
+  match.append('~inc0.ico')
+  match.append('~inc1.ico')
+  match.append('~inc0.png')
+  match.append('~inc1.png')
+  match.append('~inc2.png')
+  match.append('~inc12.png')
+  match.append('~inc13.png')
+  match.append('~inc14.png')
+  match.append('~inc0.webp')
+  match.append('~inc1.webp')
+
+  if match:
+    args.append('--match')
+    args.extend(match)
+
+  return args
+
+
+def perf_steps(api):
+  """Run Skia benchmarks."""
+  if api.vars.upload_perf_results:
+    api.flavor.create_clean_device_dir(
+        api.flavor.device_dirs.perf_data_dir)
+
+  # Run nanobench.
+  properties = [
+    '--properties',
+    'gitHash',      api.vars.got_revision,
+  ]
+  if api.vars.is_trybot:
+    properties.extend([
+      'issue',    api.vars.issue,
+      'patchset', api.vars.patchset,
+      'patch_storage', api.vars.patch_storage,
+    ])
+  properties.extend(['swarming_bot_id', api.vars.swarming_bot_id])
+  properties.extend(['swarming_task_id', api.vars.swarming_task_id])
+
+  target = 'nanobench'
+  args = [
+      target,
+      '--undefok',   # This helps branches that may not know new flags.
+      '-i',       api.flavor.device_dirs.resource_dir,
+      '--skps',   api.flavor.device_dirs.skp_dir,
+      '--images', api.flavor.device_path_join(
+          api.flavor.device_dirs.images_dir, 'nanobench'),
+  ]
+
+  # Do not run svgs on Valgrind.
+  if 'Valgrind' not in api.vars.builder_name:
+    if ('Vulkan' not in api.vars.builder_name or
+        'NexusPlayer' not in api.vars.builder_name):
+      args.extend(['--svgs',  api.flavor.device_dirs.svg_dir])
+
+  skip_flag = None
+  if api.vars.builder_cfg.get('cpu_or_gpu') == 'CPU':
+    skip_flag = '--nogpu'
+  elif api.vars.builder_cfg.get('cpu_or_gpu') == 'GPU':
+    skip_flag = '--nocpu'
+  if skip_flag:
+    args.append(skip_flag)
+  args.extend(nanobench_flags(api.vars.builder_name))
+
+  if 'Chromecast' in api.vars.builder_cfg.get('os', ''):
+    # Due to limited disk space, run a watered down perf run on Chromecast.
+    args = [
+      target,
+      '--config',
+      'gles',
+      '-i', api.flavor.device_dirs.resource_dir,
+      '--images', api.flavor.device_path_join(
+          api.flavor.device_dirs.resource_dir, 'color_wheel.jpg'),
+      '--skps',  api.flavor.device_dirs.skp_dir,
+      '--pre_log',
+      '--match', # skia:6581
+      '~matrixconvolution',
+      '~blur_image_filter',
+      '~blur_0.01',
+      '~GM_animated-image-blurs',
+    ]
+
+  if api.vars.upload_perf_results:
+    now = api.time.utcnow()
+    ts = int(calendar.timegm(now.utctimetuple()))
+    json_path = api.flavor.device_path_join(
+        api.flavor.device_dirs.perf_data_dir,
+        'nanobench_%s_%d.json' % (api.vars.got_revision, ts))
+    args.extend(['--outResultsFile', json_path])
+    args.extend(properties)
+
+    keys_blacklist = ['configuration', 'role', 'is_trybot']
+    args.append('--key')
+    for k in sorted(api.vars.builder_cfg.keys()):
+      if not k in keys_blacklist:
+        args.extend([k, api.vars.builder_cfg[k]])
+
+  env = {}
+  if 'Ubuntu16' in api.vars.builder_name:
+    # The vulkan in this asset name simply means that the graphics driver
+    # supports Vulkan. It is also the driver used for GL code.
+    dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_release')
+    if 'Debug' in api.vars.builder_name:
+      dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_debug')
+
+    if 'Vulkan' in api.vars.builder_name:
+      sdk_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'bin')
+      lib_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'lib')
+      env.update({
+        'PATH':'%%(PATH)s:%s' % sdk_path,
+        'LD_LIBRARY_PATH': '%s:%s' % (lib_path, dri_path),
+        'LIBGL_DRIVERS_PATH': dri_path,
+        'VK_ICD_FILENAMES':'%s' % dri_path.join('intel_icd.x86_64.json'),
+      })
+    else:
+      # Even the non-vulkan NUC jobs could benefit from the newer drivers.
+      env.update({
+        'LD_LIBRARY_PATH': dri_path,
+        'LIBGL_DRIVERS_PATH': dri_path,
+      })
+
+  # See skia:2789.
+  extra_config_parts = api.vars.builder_cfg.get('extra_config', '').split('_')
+  if 'AbandonGpuContext' in extra_config_parts:
+    args.extend(['--abandonGpuContext', '--nocpu'])
+
+  with api.env(env):
+    api.run(api.flavor.step, target, cmd=args,
+            abort_on_failure=False)
+
+  # Copy results to swarming out dir.
+  if api.vars.upload_perf_results:
+    api.file.makedirs('perf_dir', api.vars.perf_data_dir)
+    api.flavor.copy_directory_contents_to_host(
+        api.flavor.device_dirs.perf_data_dir,
+        api.vars.perf_data_dir)
+
+
+def RunSteps(api):
+  api.core.setup()
+  env = {}
+  if 'iOS' in api.vars.builder_name:
+    env['IOS_BUNDLE_ID'] = 'com.google.nanobench'
+    env['IOS_MOUNT_POINT'] = api.vars.slave_dir.join('mnt_iosdevice')
+  with api.env(env):
+    try:
+      if 'Chromecast' in api.vars.builder_name:
+        api.flavor.install(resources=True, skps=True)
+      else:
+        api.flavor.install_everything()
+      perf_steps(api)
+    finally:
+      api.flavor.cleanup_steps()
+    api.run.check_failure()
+
+
+TEST_BUILDERS = [
+  'Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan',
+  'Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android',
+  'Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android',
+  'Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android',
+  'Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android',
+  'Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan',
+  'Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android',
+  'Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release',
+  'Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug',
+  'Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release',
+  'Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release',
+  'Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer',
+  'Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release',
+  'Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind',
+  ('Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind' +
+  '_AbandonGpuContext'),
+  'Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan',
+  'Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release',
+  'Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE',
+  'Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE',
+  'Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan',
+  'Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE',
+  'Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug',
+  'Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release',
+  'Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release'
+]
+
+
+def GenTests(api):
+  for builder in TEST_BUILDERS:
+    test = (
+      api.test(builder) +
+      api.properties(buildername=builder,
+                     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',
+                                     'skimage', 'VERSION'),
+          api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
+                                     'skp', 'VERSION'),
+          api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
+      ) +
+      api.step_data('get swarming bot id',
+          stdout=api.raw_io.output('skia-bot-123')) +
+      api.step_data('get swarming task id',
+          stdout=api.raw_io.output('123456'))
+    )
+    if 'Win' in builder:
+      test += api.platform('win', 64)
+
+    if 'Chromecast' in builder:
+      test += api.step_data(
+          'read chromecast ip',
+          stdout=api.raw_io.output('192.168.1.2:5555'))
+
+    if 'ChromeOS' in builder:
+      test += api.step_data(
+          'read chromeos ip',
+          stdout=api.raw_io.output('{"user_ip":"foo@127.0.0.1"}'))
+
+    yield test
+
+  builder = 'Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release'
+  yield (
+    api.test('trybot') +
+    api.properties(buildername=builder,
+                   revision='abc123',
+                   path_config='kitchen',
+                   swarm_out_dir='[SWARM_OUT_DIR]') +
+    api.properties(patch_storage='gerrit') +
+    api.properties.tryserver(
+          buildername=builder,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )+
+    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')
+    )
+  )
+
+  builder = 'Perf-Android-Clang-NexusPlayer-CPU-SSE4-x86-Debug-Android'
+  yield (
+    api.test('failed_push') +
+    api.properties(buildername=builder,
+                   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',
+                                     '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')
+    ) +
+    api.step_data('push [START_DIR]/skia/resources/* '+
+                  '/sdcard/revenge_of_the_skiabot/resources', retcode=1)
+  )
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
new file mode 100644
index 0000000..759fb7c
--- /dev/null
+++ b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json
@@ -0,0 +1,226 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
+      "--patch_root",
+      "skia",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123",
+      "--revision",
+      "src@origin/lkgr"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
+      "@@@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@  \"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",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
+    },
+    "name": "gclient runhooks"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/src/buildtools/linux64/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/src/out/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "GYP_GENERATORS": "ninja",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "GN"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/src/out/Release",
+      "chrome"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Build Chrome"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/skp_output"
+    ],
+    "infra_step": true,
+    "name": "rmtree skp_output"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/skp_output",
+      "511"
+    ],
+    "name": "makedirs skp_output",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/assets/skp/create.py",
+      "--chrome_src_path",
+      "[CUSTOM_/_B_WORK]/src",
+      "--browser_executable",
+      "[CUSTOM_/_B_WORK]/src/out/Release/chrome",
+      "--target_dir",
+      "[START_DIR]/skp_output"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Recreate SKPs"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json
new file mode 100644
index 0000000..ad5ef53
--- /dev/null
+++ b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json
@@ -0,0 +1,343 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
+      "--patch_root",
+      "skia",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123",
+      "--revision",
+      "src@origin/lkgr"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
+      "@@@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@  \"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",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
+    },
+    "name": "gclient runhooks"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/src/buildtools/linux64/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/src/out/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "GYP_GENERATORS": "ninja",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "GN"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/src/out/Release",
+      "chrome"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Build Chrome"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/skp_output"
+    ],
+    "infra_step": true,
+    "name": "rmtree skp_output"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/skp_output",
+      "511"
+    ],
+    "name": "makedirs skp_output",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/assets/skp/create.py",
+      "--chrome_src_path",
+      "[CUSTOM_/_B_WORK]/src",
+      "--browser_executable",
+      "[CUSTOM_/_B_WORK]/src/out/Release/chrome",
+      "--target_dir",
+      "[START_DIR]/skp_output",
+      "--upload_to_partner_bucket"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Recreate SKPs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'update_skps.git_cookies'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
+    ],
+    "name": "download update_skps.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
+      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/upload_skps.py",
+      "--target_dir",
+      "[START_DIR]/skp_output",
+      "--gitcookies",
+      "[HOME]/update_skps.git_cookies"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Upload SKPs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\n\n\nTOKEN_FILE = 'update_skps.git_cookies'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
+    ],
+    "name": "cleanup update_skps.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/recreate_skps.expected/failed_upload.json b/infra/bots/recipes/recreate_skps.expected/failed_upload.json
new file mode 100644
index 0000000..1692bd6
--- /dev/null
+++ b/infra/bots/recipes/recreate_skps.expected/failed_upload.json
@@ -0,0 +1,348 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
+      "--patch_root",
+      "skia",
+      "--revision_mapping_file",
+      "{\"got_revision\": \"skia\"}",
+      "--git-cache-dir",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123",
+      "--revision",
+      "src@origin/lkgr"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
+      "@@@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@  \"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",
+      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
+      "runhooks"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
+    },
+    "name": "gclient runhooks"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CUSTOM_/_B_WORK]/src/buildtools/linux64/gn",
+      "gen",
+      "[CUSTOM_/_B_WORK]/src/out/Release"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
+      "GYP_GENERATORS": "ninja",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "GN"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[CUSTOM_/_B_WORK]/src/out/Release",
+      "chrome"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/src",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Build Chrome"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[START_DIR]/skp_output"
+    ],
+    "infra_step": true,
+    "name": "rmtree skp_output"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/skp_output",
+      "511"
+    ],
+    "name": "makedirs skp_output",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/assets/skp/create.py",
+      "--chrome_src_path",
+      "[CUSTOM_/_B_WORK]/src",
+      "--browser_executable",
+      "[CUSTOM_/_B_WORK]/src/out/Release/chrome",
+      "--target_dir",
+      "[START_DIR]/skp_output",
+      "--upload_to_partner_bucket"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Recreate SKPs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "go version"
+  },
+  {
+    "cmd": [
+      "go",
+      "version"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "env go version"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/go/go/bin/go",
+      "get",
+      "-u",
+      "-t",
+      "go.skia.org/infra/..."
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "update go pkgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'update_skps.git_cookies'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
+    ],
+    "name": "download update_skps.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
+      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/upload_skps.py",
+      "--target_dir",
+      "[START_DIR]/skp_output",
+      "--gitcookies",
+      "[HOME]/update_skps.git_cookies"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "GOPATH": "[START_DIR]/gopath",
+      "GOROOT": "[START_DIR]/go/go",
+      "PATH": "[START_DIR]/go/go/bin:[START_DIR]/gopath:<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Upload SKPs",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\n\n\nTOKEN_FILE = 'update_skps.git_cookies'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
+    ],
+    "name": "cleanup update_skps.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Step('Upload SKPs') failed with return_code 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/recreate_skps.py b/infra/bots/recipes/recreate_skps.py
new file mode 100644
index 0000000..8ca604c
--- /dev/null
+++ b/infra/bots/recipes/recreate_skps.py
@@ -0,0 +1,122 @@
+# 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.
+
+
+"""Recipe for the Skia RecreateSKPs Bot."""
+
+
+DEPS = [
+  'depot_tools/gclient',
+  'file',
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'core',
+  'infra',
+  'run',
+  'vars',
+]
+
+
+TEST_BUILDERS = {
+  'client.skia.compile': {
+    'skiabot-linux-swarm-000': [
+      'Housekeeper-Nightly-RecreateSKPs_Canary',
+      'Housekeeper-Weekly-RecreateSKPs',
+    ],
+  },
+}
+
+
+UPDATE_SKPS_GITCOOKIES_FILE = 'update_skps.git_cookies'
+UPDATE_SKPS_KEY = 'update_skps_git_cookies'
+
+
+def RunSteps(api):
+  # Check out Chrome.
+  api.core.setup()
+
+  src_dir = api.vars.checkout_root.join('src')
+  out_dir = src_dir.join('out', 'Release')
+
+  with api.context(cwd=src_dir):
+    # Call GN.
+    platform = 'linux64'  # This bot only runs on linux; don't bother checking.
+    gn = src_dir.join('buildtools', platform, 'gn')
+    gn_env = {'CPPFLAGS': '-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1',
+              'GYP_GENERATORS': 'ninja'}
+    with api.context(env=gn_env):
+      api.run(api.step, 'GN', cmd=[gn, 'gen', out_dir])
+
+    # Build Chrome.
+    api.run(api.step, 'Build Chrome', cmd=['ninja', '-C', out_dir, 'chrome'])
+
+  # Clean up the output dir.
+  output_dir = api.path['start_dir'].join('skp_output')
+  if api.path.exists(output_dir):
+    api.file.rmtree('skp_output', output_dir)
+  api.file.makedirs('skp_output', output_dir)
+
+  # Capture the SKPs.
+  asset_dir = api.vars.infrabots_dir.join('assets', 'skp')
+  cmd = ['python', asset_dir.join('create.py'),
+         '--chrome_src_path', src_dir,
+         '--browser_executable', src_dir.join('out', 'Release', 'chrome'),
+         '--target_dir', output_dir]
+  if 'Canary' not in api.properties['buildername']:
+    cmd.append('--upload_to_partner_bucket')
+  with api.context(cwd=api.vars.skia_dir):
+    api.run(api.step, 'Recreate SKPs', cmd=cmd)
+
+  # Upload the SKPs.
+  if 'Canary' not in api.properties['buildername']:
+    api.infra.update_go_deps()
+    update_skps_gitcookies = api.path.join(api.path.expanduser('~'),
+                                           UPDATE_SKPS_GITCOOKIES_FILE)
+    cmd = ['python',
+           api.vars.skia_dir.join('infra', 'bots', 'upload_skps.py'),
+           '--target_dir', output_dir,
+           '--gitcookies', str(update_skps_gitcookies)]
+    with api.infra.MetadataFetch(
+        api, UPDATE_SKPS_KEY, UPDATE_SKPS_GITCOOKIES_FILE):
+      with api.context(cwd=api.vars.skia_dir, env=api.infra.go_env):
+        api.run(api.step, 'Upload SKPs', cmd=cmd)
+
+
+def GenTests(api):
+  builder = 'Housekeeper-Nightly-RecreateSKPs_Canary'
+  yield (
+      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('skp_output'))
+  )
+
+  builder = 'Housekeeper-Weekly-RecreateSKPs'
+  yield (
+      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('skp_output'))
+  )
+
+  yield (
+      api.test('failed_upload') +
+      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('skp_output')) +
+      api.step_data('Upload SKPs', retcode=1)
+  )
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench.json b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench.json
new file mode 100644
index 0000000..5b5ed57
--- /dev/null
+++ b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench.json
@@ -0,0 +1,370 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/skpbench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push skpbench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/tools/skpbench/skpbench.py",
+      "/data/local/tmp/skpbench",
+      "/sdcard/revenge_of_the_skiabot/skps",
+      "--adb",
+      "--resultsfile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/table",
+      "--config",
+      "gles,glesinst4"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "skpbench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/tools/skpbench/skiaperf.py",
+      "[CUSTOM_[SWARM_OUT_DIR]]/table",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--outfile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench/data/skpbench_abc123_1337000001.json",
+      "--key",
+      "arch",
+      "arm64",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "TegraX1",
+      "extra_config",
+      "Android_Skpbench",
+      "model",
+      "PixelC",
+      "os",
+      "Android"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Parse skpbench output into Perf json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench.json b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench.json
new file mode 100644
index 0000000..5163063
--- /dev/null
+++ b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench.json
@@ -0,0 +1,370 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/skpbench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push skpbench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/tools/skpbench/skpbench.py",
+      "/data/local/tmp/skpbench",
+      "/sdcard/revenge_of_the_skiabot/skps",
+      "--adb",
+      "--resultsfile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/table",
+      "--config",
+      "vk"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "skpbench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/tools/skpbench/skiaperf.py",
+      "[CUSTOM_[SWARM_OUT_DIR]]/table",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--outfile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench/data/skpbench_abc123_1337000001.json",
+      "--key",
+      "arch",
+      "arm64",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "TegraX1",
+      "extra_config",
+      "Android_Vulkan_Skpbench",
+      "model",
+      "PixelC",
+      "os",
+      "Android"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Parse skpbench output into Perf json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/skpbench.expected/trybot.json b/infra/bots/recipes/skpbench.expected/trybot.json
new file mode 100644
index 0000000..67dc3e0
--- /dev/null
+++ b/infra/bots/recipes/skpbench.expected/trybot.json
@@ -0,0 +1,376 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/skpbench",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push skpbench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/tools/skpbench/skpbench.py",
+      "/data/local/tmp/skpbench",
+      "/sdcard/revenge_of_the_skiabot/skps",
+      "--adb",
+      "--resultsfile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/table",
+      "--config",
+      "gles,glesinst4"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "skpbench"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench/data",
+      "511"
+    ],
+    "name": "makedirs perf_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/skia/tools/skpbench/skiaperf.py",
+      "[CUSTOM_[SWARM_OUT_DIR]]/table",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "issue",
+      "456789",
+      "patchset",
+      "12",
+      "patch_storage",
+      "gerrit",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--outfile",
+      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench/data/skpbench_abc123_1337000001.json",
+      "--key",
+      "arch",
+      "arm64",
+      "compiler",
+      "Clang",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "TegraX1",
+      "extra_config",
+      "Android_Skpbench",
+      "model",
+      "PixelC",
+      "os",
+      "Android"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Parse skpbench output into Perf json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/skpbench.py b/infra/bots/recipes/skpbench.py
new file mode 100644
index 0000000..1d416f7
--- /dev/null
+++ b/infra/bots/recipes/skpbench.py
@@ -0,0 +1,159 @@
+# 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 for Skia skpbench.
+
+
+import calendar
+
+
+DEPS = [
+  'core',
+  'file',
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'recipe_engine/time',
+  'run',
+  'flavor',
+  'vars',
+]
+
+
+def _run(api, title, *cmd, **kwargs):
+  with api.context(cwd=api.vars.skia_dir):
+    return api.run(api.step, title, cmd=list(cmd), **kwargs)
+
+
+def _adb(api, title, *cmd, **kwargs):
+  if 'infra_step' not in kwargs:
+    kwargs['infra_step'] = True
+  return _run(api, title, 'adb', *cmd, **kwargs)
+
+
+def skpbench_steps(api):
+  """benchmark Skia using skpbench."""
+  app = api.vars.skia_out.join(api.vars.configuration, 'skpbench')
+  _adb(api, 'push skpbench', 'push', app, api.vars.android_bin_dir)
+
+  skpbench_dir = api.vars.slave_dir.join('skia', 'tools', 'skpbench')
+  table = api.path.join(api.vars.swarming_out_dir, 'table')
+
+  config = 'gles,glesinst4'
+  if 'Vulkan' in api.vars.builder_name:
+    config = 'vk'
+
+  skpbench_args = [
+        api.path.join(api.vars.android_bin_dir, 'skpbench'),
+        api.path.join(api.vars.android_data_dir, 'skps'),
+        '--adb',
+        '--resultsfile', table,
+        '--config', config]
+
+  api.run(api.python, 'skpbench',
+      script=skpbench_dir.join('skpbench.py'),
+      args=skpbench_args)
+
+  skiaperf_args = [
+    table,
+    '--properties',
+    'gitHash',      api.vars.got_revision,
+  ]
+  if api.vars.is_trybot:
+    skiaperf_args.extend([
+      'issue',    api.vars.issue,
+      'patchset', api.vars.patchset,
+      'patch_storage', api.vars.patch_storage,
+    ])
+
+  skiaperf_args.extend(['swarming_bot_id', api.vars.swarming_bot_id])
+  skiaperf_args.extend(['swarming_task_id', api.vars.swarming_task_id])
+
+  now = api.time.utcnow()
+  ts = int(calendar.timegm(now.utctimetuple()))
+  api.file.makedirs('perf_dir', api.vars.perf_data_dir)
+  json_path = api.path.join(
+      api.vars.perf_data_dir,
+      'skpbench_%s_%d.json' % (api.vars.got_revision, ts))
+
+  skiaperf_args.extend([
+    '--outfile', json_path
+  ])
+
+  keys_blacklist = ['configuration', 'role', 'is_trybot']
+  skiaperf_args.append('--key')
+  for k in sorted(api.vars.builder_cfg.keys()):
+    if not k in keys_blacklist:
+      skiaperf_args.extend([k, api.vars.builder_cfg[k]])
+
+  api.run(api.python, 'Parse skpbench output into Perf json',
+      script=skpbench_dir.join('skiaperf.py'),
+      args=skiaperf_args)
+
+
+def RunSteps(api):
+  api.core.setup()
+  try:
+    api.flavor.install(skps=True)
+    skpbench_steps(api)
+  finally:
+    api.flavor.cleanup_steps()
+  api.run.check_failure()
+
+
+TEST_BUILDERS = [
+  'Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench',
+  ('Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-'
+   'Android_Vulkan_Skpbench'),
+]
+
+
+def GenTests(api):
+  for builder in TEST_BUILDERS:
+    test = (
+      api.test(builder) +
+      api.properties(buildername=builder,
+                     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',
+                                     'skp', 'VERSION'),
+      ) +
+      api.step_data('get swarming bot id',
+          stdout=api.raw_io.output('skia-bot-123')) +
+      api.step_data('get swarming task id',
+          stdout=api.raw_io.output('123456'))
+    )
+
+    yield test
+
+  b = 'Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench'
+  yield (
+    api.test('trybot') +
+    api.properties(buildername=b,
+                   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',
+                                   'skp', 'VERSION'),
+    ) +
+    api.step_data('get swarming bot id',
+        stdout=api.raw_io.output('skia-bot-123')) +
+    api.step_data('get swarming task id',
+        stdout=api.raw_io.output('123456')) +
+    api.properties(patch_storage='gerrit') +
+    api.properties.tryserver(
+        buildername=b,
+        gerrit_project='skia',
+        gerrit_url='https://skia-review.googlesource.com/',
+    )
+  )
diff --git a/infra/bots/recipes/swarm_RecreateSKPs.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json b/infra/bots/recipes/swarm_RecreateSKPs.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json
deleted file mode 100644
index 8d87d89..0000000
--- a/infra/bots/recipes/swarm_RecreateSKPs.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json
+++ /dev/null
@@ -1,198 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--revision",
-      "src@origin/lkgr",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
-      "@@@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@  \"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",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
-      "runhooks"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]"
-    },
-    "name": "gclient runhooks"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/src/buildtools/linux64/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/src/out/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/src",
-    "env": {
-      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
-      "GYP_GENERATORS": "ninja"
-    },
-    "name": "GN"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/src/out/Release",
-      "chrome"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/src",
-    "name": "Build Chrome"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/skp_output"
-    ],
-    "infra_step": true,
-    "name": "rmtree skp_output"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/skp_output",
-      "511"
-    ],
-    "name": "makedirs skp_output",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/assets/skp/create.py",
-      "--chrome_src_path",
-      "[CUSTOM_/_B_WORK]/src",
-      "--browser_executable",
-      "[CUSTOM_/_B_WORK]/src/out/Release/chrome",
-      "--target_dir",
-      "[START_DIR]/skp_output"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "[DEPOT_TOOLS]:%(PATH)s"
-    },
-    "name": "Recreate SKPs"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_RecreateSKPs.expected/Housekeeper-Weekly-RecreateSKPs.json b/infra/bots/recipes/swarm_RecreateSKPs.expected/Housekeeper-Weekly-RecreateSKPs.json
deleted file mode 100644
index 8c0d438..0000000
--- a/infra/bots/recipes/swarm_RecreateSKPs.expected/Housekeeper-Weekly-RecreateSKPs.json
+++ /dev/null
@@ -1,281 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--revision",
-      "src@origin/lkgr",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
-      "@@@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@  \"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",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
-      "runhooks"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]"
-    },
-    "name": "gclient runhooks"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/src/buildtools/linux64/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/src/out/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/src",
-    "env": {
-      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
-      "GYP_GENERATORS": "ninja"
-    },
-    "name": "GN"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/src/out/Release",
-      "chrome"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/src",
-    "name": "Build Chrome"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/skp_output"
-    ],
-    "infra_step": true,
-    "name": "rmtree skp_output"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/skp_output",
-      "511"
-    ],
-    "name": "makedirs skp_output",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/assets/skp/create.py",
-      "--chrome_src_path",
-      "[CUSTOM_/_B_WORK]/src",
-      "--browser_executable",
-      "[CUSTOM_/_B_WORK]/src/out/Release/chrome",
-      "--target_dir",
-      "[START_DIR]/skp_output",
-      "--upload_to_partner_bucket"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "[DEPOT_TOOLS]:%(PATH)s"
-    },
-    "name": "Recreate SKPs"
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'update_skps.git_cookies'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
-    ],
-    "name": "download update-skps.gitcookies",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
-      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
-      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
-      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/upload_skps.py",
-      "--target_dir",
-      "[START_DIR]/skp_output",
-      "--gitcookies",
-      "[HOME]/update_skps.git_cookies"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "[DEPOT_TOOLS]:%(PATH)s"
-    },
-    "name": "Upload SKPs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\n\n\nTOKEN_FILE = 'update_skps.git_cookies'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
-    ],
-    "name": "cleanup update-skps.gitcookies",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
-      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_RecreateSKPs.expected/failed_upload.json b/infra/bots/recipes/swarm_RecreateSKPs.expected/failed_upload.json
deleted file mode 100644
index 819e758..0000000
--- a/infra/bots/recipes/swarm_RecreateSKPs.expected/failed_upload.json
+++ /dev/null
@@ -1,286 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}, {'deps_file': '.DEPS.git', 'managed': False, 'name': 'src', 'url': 'https://chromium.googlesource.com/chromium/src.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--revision",
-      "src@origin/lkgr",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@    \"src\": \"origin/lkgr\"@@@",
-      "@@@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@  \"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",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gclient.py",
-      "runhooks"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]"
-    },
-    "name": "gclient runhooks"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/src/buildtools/linux64/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/src/out/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/src",
-    "env": {
-      "CPPFLAGS": "-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1",
-      "GYP_GENERATORS": "ninja"
-    },
-    "name": "GN"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/src/out/Release",
-      "chrome"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/src",
-    "name": "Build Chrome"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/skp_output"
-    ],
-    "infra_step": true,
-    "name": "rmtree skp_output"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/skp_output",
-      "511"
-    ],
-    "name": "makedirs skp_output",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/assets/skp/create.py",
-      "--chrome_src_path",
-      "[CUSTOM_/_B_WORK]/src",
-      "--browser_executable",
-      "[CUSTOM_/_B_WORK]/src/out/Release/chrome",
-      "--target_dir",
-      "[START_DIR]/skp_output",
-      "--upload_to_partner_bucket"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "[DEPOT_TOOLS]:%(PATH)s"
-    },
-    "name": "Recreate SKPs"
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'update_skps.git_cookies'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
-    ],
-    "name": "download update-skps.gitcookies",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
-      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_skps_git_cookies'@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
-      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
-      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/upload_skps.py",
-      "--target_dir",
-      "[START_DIR]/skp_output",
-      "--gitcookies",
-      "[HOME]/update_skps.git_cookies"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "[DEPOT_TOOLS]:%(PATH)s"
-    },
-    "name": "Upload SKPs",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\n\n\nTOKEN_FILE = 'update_skps.git_cookies'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
-    ],
-    "name": "cleanup update-skps.gitcookies",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_skps.git_cookies'@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
-      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "reason": "Step('Upload SKPs') failed with return_code 1",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_RecreateSKPs.py b/infra/bots/recipes/swarm_RecreateSKPs.py
deleted file mode 100644
index 8c06577..0000000
--- a/infra/bots/recipes/swarm_RecreateSKPs.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# 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.
-
-
-"""Recipe for the Skia RecreateSKPs Bot."""
-
-
-DEPS = [
-  'build/file',
-  'depot_tools/gclient',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/python',
-  'recipe_engine/raw_io',
-  'recipe_engine/step',
-  'core',
-  'infra',
-  'vars',
-]
-
-
-TEST_BUILDERS = {
-  'client.skia.compile': {
-    'skiabot-linux-swarm-000': [
-      'Housekeeper-Nightly-RecreateSKPs_Canary',
-      'Housekeeper-Weekly-RecreateSKPs',
-    ],
-  },
-}
-
-
-UPDATE_SKPS_GITCOOKIES_FILE = 'update_skps.git_cookies'
-UPDATE_SKPS_KEY = 'update_skps_git_cookies'
-
-
-class gitcookies_auth(object):
-  """Download update-skps@skia.org's .gitcookies."""
-  def __init__(self, api, metadata_key):
-    self.m = api
-    self._key = metadata_key
-
-  def __enter__(self):
-    return self.m.python.inline(
-        'download update-skps.gitcookies',
-        """
-import os
-import urllib2
-
-TOKEN_FILE = '%s'
-TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/%s'
-
-req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})
-contents = urllib2.urlopen(req).read()
-
-home = os.path.expanduser('~')
-token_file = os.path.join(home, TOKEN_FILE)
-
-with open(token_file, 'w') as f:
-  f.write(contents)
-        """ % (UPDATE_SKPS_GITCOOKIES_FILE,
-               self._key),
-    )
-
-  def __exit__(self, t, v, tb):
-    self.m.python.inline(
-        'cleanup update-skps.gitcookies',
-        """
-import os
-
-
-TOKEN_FILE = '%s'
-
-
-home = os.path.expanduser('~')
-token_file = os.path.join(home, TOKEN_FILE)
-if os.path.isfile(token_file):
-  os.remove(token_file)
-        """ % (UPDATE_SKPS_GITCOOKIES_FILE),
-    )
-    return v is None
-
-
-def RunSteps(api):
-  # Check out Chrome.
-  api.core.setup()
-
-  src_dir = api.vars.checkout_root.join('src')
-  out_dir = src_dir.join('out', 'Release')
-
-  with api.step.context({'cwd': src_dir}):
-    # Call GN.
-    platform = 'linux64'  # This bot only runs on linux; don't bother checking.
-    gn = src_dir.join('buildtools', platform, 'gn')
-    gn_env = {'CPPFLAGS': '-DSK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS=1',
-              'GYP_GENERATORS': 'ninja'}
-    with api.step.context({'env': gn_env}):
-      api.step('GN', [gn, 'gen', out_dir])
-
-    # Build Chrome.
-    api.step('Build Chrome', ['ninja', '-C', out_dir, 'chrome'])
-
-  # Clean up the output dir.
-  output_dir = api.path['start_dir'].join('skp_output')
-  if api.path.exists(output_dir):
-    api.file.rmtree('skp_output', output_dir)
-  api.file.makedirs('skp_output', output_dir)
-
-  # Capture the SKPs.
-  path_var= api.path.pathsep.join([str(api.path['depot_tools']), '%(PATH)s'])
-  env = {
-      'CHROME_HEADLESS': '1',
-      'PATH': path_var,
-  }
-  asset_dir = api.vars.infrabots_dir.join('assets', 'skp')
-  cmd = ['python', asset_dir.join('create.py'),
-         '--chrome_src_path', src_dir,
-         '--browser_executable', src_dir.join('out', 'Release', 'chrome'),
-         '--target_dir', output_dir]
-  if 'Canary' not in api.properties['buildername']:
-    cmd.append('--upload_to_partner_bucket')
-  with api.step.context({'cwd': api.vars.skia_dir, 'env': env}):
-    api.step('Recreate SKPs', cmd=cmd)
-
-  # Upload the SKPs.
-  if 'Canary' not in api.properties['buildername']:
-    api.infra.update_go_deps()
-    update_skps_gitcookies = api.path.join(api.path.expanduser('~'),
-                                           UPDATE_SKPS_GITCOOKIES_FILE)
-    cmd = ['python',
-           api.vars.skia_dir.join('infra', 'bots', 'upload_skps.py'),
-           '--target_dir', output_dir,
-           '--gitcookies', str(update_skps_gitcookies)]
-    env.update(api.infra.go_env)
-    with gitcookies_auth(api, UPDATE_SKPS_KEY):
-      with api.step.context({'cwd': api.vars.skia_dir, 'env': env}):
-        api.step('Upload SKPs', cmd=cmd)
-
-
-def GenTests(api):
-  mastername = 'client.skia.compile'
-  slavename = 'skiabot-linux-swarm-000'
-  builder = 'Housekeeper-Nightly-RecreateSKPs_Canary'
-  yield (
-      api.test(builder) +
-      api.properties(buildername=builder,
-                     mastername=mastername,
-                     slavename=slavename,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     buildnumber=2,
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-      api.path.exists(api.path['start_dir'].join('skp_output'))
-  )
-
-  builder = 'Housekeeper-Weekly-RecreateSKPs'
-  yield (
-      api.test(builder) +
-      api.properties(buildername=builder,
-                     mastername=mastername,
-                     slavename=slavename,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     buildnumber=2,
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-      api.path.exists(api.path['start_dir'].join('skp_output'))
-  )
-
-  yield (
-      api.test('failed_upload') +
-      api.properties(buildername=builder,
-                     mastername=mastername,
-                     slavename=slavename,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     buildnumber=2,
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-      api.path.exists(api.path['start_dir'].join('skp_output')) +
-      api.step_data('Upload SKPs', retcode=1)
-  )
diff --git a/infra/bots/recipes/swarm_compile.expected/Build-Mac-Clang-Arm7-Release.json b/infra/bots/recipes/swarm_compile.expected/Build-Mac-Clang-Arm7-Release.json
deleted file mode 100644
index c068ef9..0000000
--- a/infra/bots/recipes/swarm_compile.expected/Build-Mac-Clang-Arm7-Release.json
+++ /dev/null
@@ -1,175 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-Arm7-Release-iOS"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-Arm7-Release-iOS/Release",
-      "--args=cc=\"clang\" cxx=\"clang++\" is_debug=false target_cpu=\"Arm7\" target_os=\"ios\""
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-Arm7-Release-iOS"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-Arm7-Release-iOS/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CC": "/usr/bin/clang",
-      "CHROME_HEADLESS": "1",
-      "CXX": "/usr/bin/clang++",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-Arm7-Release-iOS"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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",
-      "[CUSTOM_/_B_WORK]/skia/out/Build-Mac-Clang-Arm7-Release-iOS/Release",
-      "[CUSTOM_[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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_compile.py b/infra/bots/recipes/swarm_compile.py
deleted file mode 100644
index cbf21f4..0000000
--- a/infra/bots/recipes/swarm_compile.py
+++ /dev/null
@@ -1,35 +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 compile.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'compile',
-]
-
-
-def RunSteps(api):
-  api.compile.run()
-
-
-def GenTests(api):
-  yield (
-    api.test('Build-Mac-Clang-Arm7-Release') +
-    api.properties(buildername='Build-Mac-Clang-Arm7-Release-iOS',
-                   mastername='fake-master',
-                   slavename='fake-slave',
-                   buildnumber=5,
-                   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('tmp', 'uninteresting_hashes.txt')
-    )
-  )
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_CPU_BENCH_10k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_CPU_BENCH_10k_SKPs.json
deleted file mode 100644
index 86e15bd..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_CPU_BENCH_10k_SKPs.json
+++ /dev/null
@@ -1,1905 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Release",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-1",
-      "[dummy hash for ct-nanobench-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-2",
-      "[dummy hash for ct-nanobench-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-3",
-      "[dummy hash for ct-nanobench-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-4",
-      "[dummy hash for ct-nanobench-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-5",
-      "[dummy hash for ct-nanobench-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1"
-    ],
-    "name": "ct-nanobench-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (2)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2"
-    ],
-    "name": "ct-nanobench-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (3)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (4)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3"
-    ],
-    "name": "ct-nanobench-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (5)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (6)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4"
-    ],
-    "name": "ct-nanobench-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (7)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (8)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5"
-    ],
-    "name": "ct-nanobench-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (9)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (10)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_100k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_100k_SKPs.json
deleted file mode 100644
index 1490e38..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_100k_SKPs.json
+++ /dev/null
@@ -1,1560 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/100k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"100k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-1",
-      "[dummy hash for ct-dm-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-2",
-      "[dummy hash for ct-dm-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-3",
-      "[dummy hash for ct-dm-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-4",
-      "[dummy hash for ct-dm-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-5",
-      "[dummy hash for ct-dm-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1"
-    ],
-    "name": "ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2"
-    ],
-    "name": "ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3"
-    ],
-    "name": "ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4"
-    ],
-    "name": "ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5"
-    ],
-    "name": "ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_10k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_10k_SKPs.json
deleted file mode 100644
index c9f4403..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_10k_SKPs.json
+++ /dev/null
@@ -1,1560 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-1",
-      "[dummy hash for ct-dm-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-2",
-      "[dummy hash for ct-dm-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-3",
-      "[dummy hash for ct-dm-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-4",
-      "[dummy hash for ct-dm-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-5",
-      "[dummy hash for ct-dm-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1"
-    ],
-    "name": "ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2"
-    ],
-    "name": "ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3"
-    ],
-    "name": "ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4"
-    ],
-    "name": "ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5"
-    ],
-    "name": "ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_10k_SKPs_Trybot.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_10k_SKPs_Trybot.json
deleted file mode 100644
index 847e260..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_10k_SKPs_Trybot.json
+++ /dev/null
@@ -1,1566 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--issue",
-      "1499623002",
-      "--patchset",
-      "1",
-      "--rietveld_server",
-      "codereview.chromium.org",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@origin/master",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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\": \"origin/master\"@@@",
-      "@@@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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "rietveld:codereview.chromium.org/1499623002/#ps1",
-      "--tag",
-      "stepname:ct-dm-1",
-      "[dummy hash for ct-dm-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "rietveld:codereview.chromium.org/1499623002/#ps1",
-      "--tag",
-      "stepname:ct-dm-2",
-      "[dummy hash for ct-dm-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "rietveld:codereview.chromium.org/1499623002/#ps1",
-      "--tag",
-      "stepname:ct-dm-3",
-      "[dummy hash for ct-dm-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "rietveld:codereview.chromium.org/1499623002/#ps1",
-      "--tag",
-      "stepname:ct-dm-4",
-      "[dummy hash for ct-dm-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "rietveld:codereview.chromium.org/1499623002/#ps1",
-      "--tag",
-      "stepname:ct-dm-5",
-      "[dummy hash for ct-dm-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1"
-    ],
-    "name": "ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2"
-    ],
-    "name": "ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3"
-    ],
-    "name": "ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4"
-    ],
-    "name": "ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5"
-    ],
-    "name": "ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs.json
deleted file mode 100644
index 74f6f1c..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs.json
+++ /dev/null
@@ -1,1560 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"All\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-1",
-      "[dummy hash for ct-dm-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-2",
-      "[dummy hash for ct-dm-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-3",
-      "[dummy hash for ct-dm-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-4",
-      "[dummy hash for ct-dm-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-5",
-      "[dummy hash for ct-dm-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1"
-    ],
-    "name": "ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2"
-    ],
-    "name": "ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3"
-    ],
-    "name": "ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4"
-    ],
-    "name": "ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5"
-    ],
-    "name": "ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs_2slaves_failure.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs_2slaves_failure.json
deleted file mode 100644
index d286dd7..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs_2slaves_failure.json
+++ /dev/null
@@ -1,1569 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"All\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-1",
-      "[dummy hash for ct-dm-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-2",
-      "[dummy hash for ct-dm-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-3",
-      "[dummy hash for ct-dm-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-4",
-      "[dummy hash for ct-dm-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-5",
-      "[dummy hash for ct-dm-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1"
-    ],
-    "name": "ct-dm-1",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@",
-      "@@@STEP_LINK@Webpage rankings@https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv@@@",
-      "@@@STEP_LINK@Download SKPs by rank@https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/fad657e-276e633/@@@",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2"
-    ],
-    "name": "ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3"
-    ],
-    "name": "ct-dm-3",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@",
-      "@@@STEP_LINK@Webpage rankings@https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv@@@",
-      "@@@STEP_LINK@Download SKPs by rank@https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/fad657e-276e633/@@@",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4"
-    ],
-    "name": "ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5"
-    ],
-    "name": "ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "reason": "Failed steps: ct-dm-1, ct-dm-3",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs_slave3_failure.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs_slave3_failure.json
deleted file mode 100644
index 36dd284..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_DM_1m_SKPs_slave3_failure.json
+++ /dev/null
@@ -1,1565 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/All/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-dm-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"dm\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "Write ct-dm-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"All\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-dm-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-dm-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-1\": \"[dummy hash for ct-dm-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-dm-1\": \"[dummy hash for ct-dm-1]\", \"ct-dm-2\": \"[dummy hash for ct-dm-2]\", \"ct-dm-3\": \"[dummy hash for ct-dm-3]\", \"ct-dm-4\": \"[dummy hash for ct-dm-4]\", \"ct-dm-5\": \"[dummy hash for ct-dm-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-1",
-      "[dummy hash for ct-dm-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-2",
-      "[dummy hash for ct-dm-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-3",
-      "[dummy hash for ct-dm-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-4",
-      "[dummy hash for ct-dm-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-dm-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-dm-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-dm-5",
-      "[dummy hash for ct-dm-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-1"
-    ],
-    "name": "ct-dm-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-2"
-    ],
-    "name": "ct-dm-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-3"
-    ],
-    "name": "ct-dm-3",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@",
-      "@@@STEP_LINK@Webpage rankings@https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv@@@",
-      "@@@STEP_LINK@Download SKPs by rank@https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/All/fad657e-276e633/@@@",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-4"
-    ],
-    "name": "ct-dm-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\", \"tasks\": {\"ct-dm-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-dm-5"
-    ],
-    "name": "ct-dm-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "reason": "Failed steps: ct-dm-3",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_GPU_BENCH_10k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_GPU_BENCH_10k_SKPs.json
deleted file mode 100644
index f6800c0..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_GPU_BENCH_10k_SKPs.json
+++ /dev/null
@@ -1,1920 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Release",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-1",
-      "[dummy hash for ct-nanobench-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-2",
-      "[dummy hash for ct-nanobench-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-3",
-      "[dummy hash for ct-nanobench-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-4",
-      "[dummy hash for ct-nanobench-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-5",
-      "[dummy hash for ct-nanobench-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1"
-    ],
-    "name": "ct-nanobench-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (2)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2"
-    ],
-    "name": "ct-nanobench-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (3)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (4)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3"
-    ],
-    "name": "ct-nanobench-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (5)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (6)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4"
-    ],
-    "name": "ct-nanobench-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (7)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (8)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\", \"tasks\": {\"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5"
-    ],
-    "name": "ct-nanobench-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (9)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (10)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_GPU_BENCH_1k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_GPU_BENCH_1k_SKPs.json
deleted file mode 100644
index 35e9bed..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_GPU_BENCH_1k_SKPs.json
+++ /dev/null
@@ -1,1920 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Release"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Release",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-nanobench-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Release\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"nanobench\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
-    ],
-    "name": "Write ct-nanobench-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-nanobench-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-nanobench-1\": \"[dummy hash for ct-nanobench-1]\", \"ct-nanobench-2\": \"[dummy hash for ct-nanobench-2]\", \"ct-nanobench-3\": \"[dummy hash for ct-nanobench-3]\", \"ct-nanobench-4\": \"[dummy hash for ct-nanobench-4]\", \"ct-nanobench-5\": \"[dummy hash for ct-nanobench-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-1",
-      "[dummy hash for ct-nanobench-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-2",
-      "[dummy hash for ct-nanobench-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-3",
-      "[dummy hash for ct-nanobench-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-4",
-      "[dummy hash for ct-nanobench-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "gpu",
-      "10de:104a",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-nanobench-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-nanobench-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-nanobench-5",
-      "[dummy hash for ct-nanobench-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-nanobench-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", \"tasks\": {\"ct-nanobench-1/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1"
-    ],
-    "name": "ct-nanobench-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-1/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (2)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", \"tasks\": {\"ct-nanobench-2/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2"
-    ],
-    "name": "ct-nanobench-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (3)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-2/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (4)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", \"tasks\": {\"ct-nanobench-3/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3"
-    ],
-    "name": "ct-nanobench-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (5)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-3/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (6)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", \"tasks\": {\"ct-nanobench-4/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4"
-    ],
-    "name": "ct-nanobench-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (7)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-4/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (8)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\", \"tasks\": {\"ct-nanobench-5/Ubuntu-14.04/[dummy has/Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5"
-    ],
-    "name": "ct-nanobench-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport json, os, sys\nif os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):\n  with open(sys.argv[2], 'w') as f:\n    json.dump(os.listdir(sys.argv[1]), f)\n",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0",
-      "/path/to/tmp/json"
-    ],
-    "name": "listdir output dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 1\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"file 2\"@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import json, os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(sys.argv[1]) and os.path.isdir(sys.argv[1]):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    json.dump(os.listdir(sys.argv[1]), f)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 1",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (9)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
-      "--",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "----",
-      "cp",
-      "-R",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-nanobench-5/0/file 2",
-      "gs://skia-perf/ct/10k/2012/05/14/12/"
-    ],
-    "env": {
-      "AWS_CREDENTIAL_FILE": null,
-      "BOTO_CONFIG": null
-    },
-    "infra_step": true,
-    "name": "gsutil upload json output (10)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/skia-perf/ct/10k/2012/05/14/12/@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_100k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_100k_SKPs.json
deleted file mode 100644
index aa5c871..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_100k_SKPs.json
+++ /dev/null
@@ -1,1560 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/100k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/100k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"100k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-1",
-      "[dummy hash for ct-get_images_from_skps-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-2",
-      "[dummy hash for ct-get_images_from_skps-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-3",
-      "[dummy hash for ct-get_images_from_skps-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-4",
-      "[dummy hash for ct-get_images_from_skps-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-5",
-      "[dummy hash for ct-get_images_from_skps-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-1"
-    ],
-    "name": "ct-get_images_from_skps-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-2"
-    ],
-    "name": "ct-get_images_from_skps-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-3"
-    ],
-    "name": "ct-get_images_from_skps-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-4"
-    ],
-    "name": "ct-get_images_from_skps-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-5"
-    ],
-    "name": "ct-get_images_from_skps-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_10k_SKPs.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_10k_SKPs.json
deleted file mode 100644
index d9b1c17..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_10k_SKPs.json
+++ /dev/null
@@ -1,1560 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-1",
-      "[dummy hash for ct-get_images_from_skps-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-2",
-      "[dummy hash for ct-get_images_from_skps-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-3",
-      "[dummy hash for ct-get_images_from_skps-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-4",
-      "[dummy hash for ct-get_images_from_skps-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-5",
-      "[dummy hash for ct-get_images_from_skps-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-1"
-    ],
-    "name": "ct-get_images_from_skps-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-2"
-    ],
-    "name": "ct-get_images_from_skps-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-3"
-    ],
-    "name": "ct-get_images_from_skps-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-4"
-    ],
-    "name": "ct-get_images_from_skps-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\", \"tasks\": {\"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-5"
-    ],
-    "name": "ct-get_images_from_skps-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_10k_SKPs_Trybot.json b/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_10k_SKPs_Trybot.json
deleted file mode 100644
index ec82155..0000000
--- a/infra/bots/recipes/swarm_ct_skps.expected/CT_IMG_DECODE_10k_SKPs_Trybot.json
+++ /dev/null
@@ -1,1560 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[CUSTOM_/_B_WORK]/skia/bin/fetch-gn"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[CUSTOM_/_B_WORK]/skia/bin/gn",
-      "gen",
-      "[START_DIR]/out/Debug",
-      "--args=cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-O1\"]"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/out/Debug"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products_whitelist:\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]/out/Debug",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/ct"
-    ],
-    "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_whitelist = ['dm', 'dm.exe', 'get_images_from_skps', 'get_images_from_skps.exe', 'nanobench', 'nanobench.exe', 'skpbench', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'lib/*.so', 'iOSShell.app', 'iOSShell.ipa', 'visualbench', 'visualbench.exe', 'vulkan-1.dll']@@@",
-      "@@@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_whitelist:@@@",
-      "@@@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@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
-      "--path",
-      "[START_DIR]/swarming.client",
-      "--url",
-      "https://chromium.googlesource.com/external/swarming.client.git"
-    ],
-    "name": "git setup (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "retry",
-      "fetch",
-      "origin",
-      "master"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "infra_step": true,
-    "name": "git fetch (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git checkout (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "read revision",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "git clean (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule sync (swarming_client)"
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/swarming.client",
-    "infra_step": true,
-    "name": "submodule update (swarming_client)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "--version"
-    ],
-    "name": "swarming.py --version",
-    "stdout": "/path/to/tmp/",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@0.8.6@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=linux*",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/linux64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go linux"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=darwin",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/mac64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go mac"
-  },
-  {
-    "cmd": [
-      "download_from_google_storage",
-      "--no_resume",
-      "--platform=win32",
-      "--no_auth",
-      "--bucket",
-      "chromium-luci",
-      "-d",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go/win64"
-    ],
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "download luci-go win"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/luci-go"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree luci-go"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copytree(sys.argv[1], sys.argv[2], symlinks=bool(sys.argv[3]))\n",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/tools/luci-go",
-      "[START_DIR]/luci-go",
-      "0"
-    ],
-    "name": "Copy Go binary",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@Webpage rankings@\"https://storage.cloud.google.com/cluster-telemetry/csv/top-1m.csv\"@@@",
-      "@@@SET_BUILD_PROPERTY@Download SKPs by rank@\"https://pantheon.corp.google.com/storage/browser/cluster-telemetry/swarming/skps/10k/fad657e-276e633/\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[START_DIR]/swarming_temp_dir"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree swarming_temp_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave1"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave1"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave1",
-      "511"
-    ],
-    "name": "makedirs slave1",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/1/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/2/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/3/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/4/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/5/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/6/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/7/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/8/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/9/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/10/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave1"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-1.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"1\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-1.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave2"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave2"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave2",
-      "511"
-    ],
-    "name": "makedirs slave2",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/11/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/12/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/13/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/14/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/15/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/16/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/17/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/18/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/19/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/20/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave2"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-2.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"2\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-2.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave3"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave3"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave3",
-      "511"
-    ],
-    "name": "makedirs slave3",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/21/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/22/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/23/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/24/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/25/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/26/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/27/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/28/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/29/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/30/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave3"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (3)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (3)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-3.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"3\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-3.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave4"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave4"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave4",
-      "511"
-    ],
-    "name": "makedirs slave4",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/31/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/32/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/33/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/34/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/35/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/36/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/37/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/38/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/39/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/40/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave4"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (4)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (4)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-4.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"4\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-4.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave5"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree slave5"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave5",
-      "511"
-    ],
-    "name": "makedirs slave5",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_PACKAGE_REPO[depot_tools]/gsutil.py",
-      "--",
-      "-m",
-      "cp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/41/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/42/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/43/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/44/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/45/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/46/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/47/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/48/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/49/*.skp",
-      "gs://cluster-telemetry/swarming/skps/10k/fad657e-276e633/50/*.skp",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/slave5"
-    ],
-    "infra_step": true,
-    "name": "gsutil cp (5)"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/swarming_temp_dir",
-      "511"
-    ],
-    "name": "makedirs swarming tmp dir (5)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\n    \"args\": [\n        \"--isolate\", \n        \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct/ct_skps.isolate\", \n        \"--isolated\", \n        \"[START_DIR]/swarming_temp_dir/skia-task-ct-get_images_from_skps-5.isolated\", \n        \"--config-variable\", \n        \"OS\", \n        \"linux\", \n        \"--extra-variable\", \n        \"CONFIGURATION\", \n        \"Debug\", \n        \"--extra-variable\", \n        \"BUILDER\", \n        \"Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot\", \n        \"--extra-variable\", \n        \"GIT_HASH\", \n        \"9046e2e693bb92a76e972b694580e5d17ad10748\", \n        \"--extra-variable\", \n        \"SLAVE_NUM\", \n        \"5\", \n        \"--extra-variable\", \n        \"TOOL_NAME\", \n        \"get_images_from_skps\"\n    ], \n    \"dir\": \"[CUSTOM_/_B_WORK]/skia/infra/bots/ct\", \n    \"version\": 1\n}",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
-    ],
-    "name": "Write ct-get_images_from_skps-5.isolated.gen.json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "{\"chromium_build\": \"fad657e-276e633\", \"num_slaves\": 5, \"page_type\": \"10k\"}",
-      "[CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/skps_version"
-    ],
-    "infra_step": true,
-    "name": "Create [CUSTOM_/_B_WORK]/skps/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/skps_version"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::isolate]/resources/isolate.py",
-      "[START_DIR]/swarming.client",
-      "batcharchive",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--verbose",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-1.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-2.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-3.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-4.isolated.gen.json",
-      "[START_DIR]/swarming_temp_dir/ct-get_images_from_skps-5.isolated.gen.json"
-    ],
-    "name": "isolate tests",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@swarm_hashes@{\"ct-get_images_from_skps-1\": \"[dummy hash for ct-get_images_from_skps-1]\", \"ct-get_images_from_skps-2\": \"[dummy hash for ct-get_images_from_skps-2]\", \"ct-get_images_from_skps-3\": \"[dummy hash for ct-get_images_from_skps-3]\", \"ct-get_images_from_skps-4\": \"[dummy hash for ct-get_images_from_skps-4]\", \"ct-get_images_from_skps-5\": \"[dummy hash for ct-get_images_from_skps-5]\"}@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-1]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-1",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-1",
-      "[dummy hash for ct-get_images_from_skps-1]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-2]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-2",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-2",
-      "[dummy hash for ct-get_images_from_skps-2]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-3]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-3",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-3",
-      "[dummy hash for ct-get_images_from_skps-3]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-4]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-4",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-4",
-      "[dummy hash for ct-get_images_from_skps-4]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "trigger",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--isolate-server",
-      "https://isolateserver.appspot.com",
-      "--priority",
-      "90",
-      "--shards",
-      "1",
-      "--task-name",
-      "ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2",
-      "--dump-json",
-      "/path/to/tmp/json",
-      "--expiration",
-      "72000",
-      "--io-timeout",
-      "2400",
-      "--hard-timeout",
-      "14400",
-      "--dimension",
-      "cpu",
-      "x86-64",
-      "--dimension",
-      "os",
-      "Ubuntu-14.04",
-      "--dimension",
-      "pool",
-      "Chrome",
-      "--tag",
-      "allow_milo:1",
-      "--tag",
-      "buildername:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot",
-      "--tag",
-      "buildnumber:2",
-      "--tag",
-      "data:[dummy hash for ct-get_images_from_skps-5]",
-      "--tag",
-      "master:client.skia",
-      "--tag",
-      "name:ct-get_images_from_skps-5",
-      "--tag",
-      "os:Ubuntu-14.04",
-      "--tag",
-      "revision:abc123",
-      "--tag",
-      "stepname:ct-get_images_from_skps-5",
-      "[dummy hash for ct-get_images_from_skps-5]"
-    ],
-    "infra_step": true,
-    "name": "[trigger] ct-get_images_from_skps-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"tasks\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"shard_index\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"task_id\": \"10000\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@shard #0@https://chromium-swarm.appspot.com/user/task/10000@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/10000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", \"tasks\": {\"ct-get_images_from_skps-1/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-1"
-    ],
-    "name": "ct-get_images_from_skps-1",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", \"tasks\": {\"ct-get_images_from_skps-2/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-2"
-    ],
-    "name": "ct-get_images_from_skps-2",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", \"tasks\": {\"ct-get_images_from_skps-3/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-3"
-    ],
-    "name": "ct-get_images_from_skps-3",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", \"tasks\": {\"ct-get_images_from_skps-4/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-4"
-    ],
-    "name": "ct-get_images_from_skps-4",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/swarming.client/swarming.py",
-      "collect",
-      "--swarming",
-      "https://chromium-swarm.appspot.com",
-      "--decorate",
-      "--print-status-updates",
-      "--json",
-      "{\"base_task_name\": \"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\", \"tasks\": {\"ct-get_images_from_skps-5/Ubuntu-14.04/[dummy has/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_10k_SKPs_Trybot/2\": {\"shard_index\": 0, \"task_id\": \"10000\", \"view_url\": \"https://chromium-swarm.appspot.com/user/task/10000\"}}}",
-      "--task-summary-json",
-      "/path/to/tmp/json",
-      "--task-output-dir",
-      "[START_DIR]/swarming_temp_dir/outputs/ct-get_images_from_skps-5"
-    ],
-    "name": "ct-get_images_from_skps-5",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Run on OS: 'Ubuntu-14.04'<br>swarming pending 71s@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@{@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  \"shards\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"abandoned_ts\": null, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"bot_id\": \"vm30\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"completed_ts\": \"2014-09-25T01:42:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"created_ts\": \"2014-09-25T01:41:00.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"durations\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        5.7, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        31.5@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"exit_codes\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        0@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"id\": \"148aa78d7aa0000\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"internal_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"isolated_out\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolated\": \"abc123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"namespace\": \"default-gzip\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"modified_ts\": \"2014-09-25 01:42:00\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"name\": \"heartbeat-canary-2014-09-25_01:41:55-os=Windows\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs\": [@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Heart beat succeeded on win32.\\n\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"Foo\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      ], @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"outputs_ref\": {@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@        \"view_url\": \"blah\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      }, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"started_ts\": \"2014-09-25T01:42:11.123\", @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"state\": 112, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"try_number\": 1, @@@",
-      "@@@STEP_LOG_LINE@swarming.summary@      \"user\": \"unknown\"@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@    }@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@  ]@@@",
-      "@@@STEP_LOG_LINE@swarming.summary@}@@@",
-      "@@@STEP_LOG_END@swarming.summary@@@",
-      "@@@STEP_LINK@shard #0 isolated out@blah@@@",
-      "@@@STEP_LINK@view steps on Milo@https://luci-milo.appspot.com/swarming/task/148aa78d7aa0000@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_ct_skps.py b/infra/bots/recipes/swarm_ct_skps.py
deleted file mode 100644
index 81cb33c..0000000
--- a/infra/bots/recipes/swarm_ct_skps.py
+++ /dev/null
@@ -1,497 +0,0 @@
-# Copyright 2015 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.
-
-
-import math
-
-
-DEPS = [
-  'build/file',
-  'depot_tools/gsutil',
-  'recipe_engine/json',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/step',
-  'recipe_engine/time',
-  'core',
-  'ct',
-  'flavor',
-  'run',
-  'swarming',
-  'vars',
-]
-
-
-SKPS_VERSION_FILE = 'skps_version'
-CT_SKPS_ISOLATE = 'ct_skps.isolate'
-
-# Do not batch archive more slaves than this value. This is used to prevent
-# no output timeouts in the 'isolate tests' step.
-MAX_SLAVES_TO_BATCHARCHIVE = 100
-
-TOOL_TO_DEFAULT_SKPS_PER_SLAVE = {
-    'dm': 10000,
-    'nanobench': 1000,
-    'get_images_from_skps': 10000,
-}
-
-# The SKP repository to use.
-DEFAULT_SKPS_CHROMIUM_BUILD = 'fad657e-276e633'
-
-
-def RunSteps(api):
-  # Figure out which repository to use.
-  buildername = api.properties['buildername']
-  if '1k' in buildername:
-    ct_page_type = '10k'
-    num_pages = 1000
-  elif '10k' in buildername:
-    ct_page_type = '10k'
-    num_pages = 10000
-  elif '100k' in buildername:
-    ct_page_type = '100k'
-    num_pages = 100000
-  elif '1m' in buildername:
-    ct_page_type = 'All'
-    num_pages = 1000000
-  else:
-    raise Exception('Do not recognise the buildername %s.' % buildername)
-
-  # Figure out which tool to use.
-  if 'DM' in buildername:
-    skia_tool = 'dm'
-    build_target = 'dm'
-  elif 'BENCH' in buildername:
-    skia_tool = 'nanobench'
-    build_target = 'nanobench'
-  elif 'IMG_DECODE' in buildername:
-    skia_tool = 'get_images_from_skps'
-    build_target = 'tools'
-  else:
-    raise Exception('Do not recognise the buildername %s.' % buildername)
-
-  api.core.setup()
-  api.flavor.compile(build_target)
-
-  # Required paths.
-  infrabots_dir = api.vars.skia_dir.join('infra', 'bots')
-  isolate_dir = infrabots_dir.join('ct')
-  isolate_path = isolate_dir.join(CT_SKPS_ISOLATE)
-
-  api.run.copy_build_products(
-      api.flavor.out_dir,
-      isolate_dir)
-  api.swarming.setup(
-      infrabots_dir.join('tools', 'luci-go'),
-      swarming_rev='')
-
-  skps_chromium_build = api.properties.get(
-      'skps_chromium_build', DEFAULT_SKPS_CHROMIUM_BUILD)
-
-  # Set build properties to make finding SKPs convenient.
-  webpage_rankings_link = (
-      'https://storage.cloud.google.com/%s/csv/top-1m.csv'
-          % api.ct.CT_GS_BUCKET)
-  api.step.active_result.presentation.properties['Webpage rankings'] = (
-      webpage_rankings_link)
-  download_skps_link = (
-      'https://pantheon.corp.google.com/storage/browser/%s/swarming/skps/%s/%s/'
-          % (api.ct.CT_GS_BUCKET, ct_page_type, skps_chromium_build))
-  api.step.active_result.presentation.properties['Download SKPs by rank'] = (
-      download_skps_link)
-
-  # Delete swarming_temp_dir to ensure it starts from a clean slate.
-  api.run.rmtree(api.swarming.swarming_temp_dir)
-
-  num_per_slave = api.properties.get(
-      'num_per_slave',
-      min(TOOL_TO_DEFAULT_SKPS_PER_SLAVE[skia_tool], num_pages))
-  ct_num_slaves = api.properties.get(
-      'ct_num_slaves',
-      int(math.ceil(float(num_pages) / num_per_slave)))
-
-  # Try to figure out if the SKPs we are going to isolate already exist
-  # locally by reading the SKPS_VERSION_FILE.
-  download_skps = True
-  expected_version_contents = {
-      "chromium_build": skps_chromium_build,
-      "page_type": ct_page_type,
-      "num_slaves": ct_num_slaves,
-  }
-  skps_dir = api.vars.checkout_root.join('skps', buildername)
-  version_file = skps_dir.join(SKPS_VERSION_FILE)
-  if api.path.exists(version_file):  # pragma: nocover
-    version_file_contents = api.file.read(
-        "Read %s" % version_file,
-        version_file,
-        test_data=expected_version_contents,
-        infra_step=True)
-    actual_version_contents = api.json.loads(version_file_contents)
-    differences = (set(expected_version_contents.items()) ^
-                   set(actual_version_contents.items()))
-    download_skps = len(differences) != 0
-    if download_skps:
-      # Delete and recreate the skps dir.
-      api.run.rmtree(skps_dir)
-      api.file.makedirs(api.path.basename(skps_dir), skps_dir)
-
-  # If a blacklist file exists then specify SKPs to be blacklisted.
-  blacklists_dir = api.vars.skia_dir.join('infra', 'bots', 'ct', 'blacklists')
-  blacklist_file = blacklists_dir.join(
-      '%s_%s_%s.json' % (skia_tool, ct_page_type, skps_chromium_build))
-  blacklist_skps = []
-  if api.path.exists(blacklist_file):  # pragma: nocover
-    blacklist_file_contents = api.file.read(
-        "Read %s" % blacklist_file,
-        blacklist_file,
-        infra_step=True)
-    blacklist_skps = api.json.loads(blacklist_file_contents)['blacklisted_skps']
-
-  for slave_num in range(1, ct_num_slaves + 1):
-    if download_skps:
-      # Download SKPs.
-      api.ct.download_swarming_skps(
-          ct_page_type, slave_num, skps_chromium_build,
-          skps_dir,
-          start_range=((slave_num-1)*num_per_slave) + 1,
-          num_skps=num_per_slave)
-
-    # Create this slave's isolated.gen.json file to use for batcharchiving.
-    extra_variables = {
-        'SLAVE_NUM': str(slave_num),
-        'TOOL_NAME': skia_tool,
-        'GIT_HASH': api.vars.got_revision,
-        'CONFIGURATION': api.vars.configuration,
-        'BUILDER': buildername,
-    }
-    api.swarming.create_isolated_gen_json(
-        isolate_path, isolate_dir, 'linux', 'ct-%s-%s' % (skia_tool, slave_num),
-        extra_variables, blacklist=blacklist_skps)
-
-  if download_skps:
-    # Since we had to download SKPs create an updated version file.
-    api.file.write("Create %s" % version_file,
-                   version_file,
-                   api.json.dumps(expected_version_contents),
-                   infra_step=True)
-
-  # Batcharchive everything on the isolate server for efficiency.
-  max_slaves_to_batcharchive = MAX_SLAVES_TO_BATCHARCHIVE
-  if '1m' in buildername:
-    # Break up the "isolate tests" step into batches with <100k files due to
-    # https://github.com/luci/luci-go/issues/9
-    max_slaves_to_batcharchive = 5
-  tasks_to_swarm_hashes = []
-  for slave_start_num in xrange(1, ct_num_slaves+1, max_slaves_to_batcharchive):
-    m = min(max_slaves_to_batcharchive, ct_num_slaves)
-    batcharchive_output = api.swarming.batcharchive(
-        targets=['ct-' + skia_tool + '-%s' % num for num in range(
-            slave_start_num, slave_start_num + m)])
-    tasks_to_swarm_hashes.extend(batcharchive_output)
-  # Sort the list to go through tasks in order.
-  tasks_to_swarm_hashes.sort()
-
-  # Trigger all swarming tasks.
-  dimensions={'os': 'Ubuntu-14.04', 'cpu': 'x86-64', 'pool': 'Chrome'}
-  if 'GPU' in buildername:
-    dimensions['gpu'] = '10de:104a'
-  tasks = api.swarming.trigger_swarming_tasks(
-      tasks_to_swarm_hashes, dimensions=dimensions, io_timeout=40*60)
-
-  # Now collect all tasks.
-  env = {'AWS_CREDENTIAL_FILE': None, 'BOTO_CONFIG': None}
-  failed_tasks = []
-  for task in tasks:
-    try:
-      api.swarming.collect_swarming_task(task)
-
-      if skia_tool == 'nanobench':
-        output_dir = api.swarming.tasks_output_dir.join(
-            task.title).join('0')
-        utc = api.time.utcnow()
-        gs_dest_dir = 'ct/%s/%d/%02d/%02d/%02d/' % (
-            ct_page_type, utc.year, utc.month, utc.day, utc.hour)
-        for json_output in api.file.listdir('output dir', output_dir):
-          with api.step.context({'env': env}):
-            api.gsutil.upload(
-                name='upload json output',
-                source=output_dir.join(json_output),
-                bucket='skia-perf',
-                dest=gs_dest_dir,
-                args=['-R']
-            )
-
-    except api.step.StepFailure as e:
-      # Add SKP links for convenience.
-      api.step.active_result.presentation.links['Webpage rankings'] = (
-          webpage_rankings_link)
-      api.step.active_result.presentation.links['Download SKPs by rank'] = (
-          download_skps_link)
-      failed_tasks.append(e)
-
-  if failed_tasks:
-    raise api.step.StepFailure(
-        'Failed steps: %s' % ', '.join([f.name for f in failed_tasks]))
-
-
-def GenTests(api):
-  ct_num_slaves = 5
-  num_per_slave = 10
-  skia_revision = 'abc123'
-  mastername = 'client.skia'
-  slavename = 'skiabot-linux-swarm-000'
-  buildnumber = 2
-  path_config = 'kitchen'
-
-  yield(
-    api.test('CT_DM_10k_SKPs') +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield(
-    api.test('CT_IMG_DECODE_10k_SKPs') +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_'
-                    '10k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield(
-    api.test('CT_DM_100k_SKPs') +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield(
-    api.test('CT_IMG_DECODE_100k_SKPs') +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_'
-                    '100k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield(
-    api.test('CT_GPU_BENCH_1k_SKPs') +
-    api.properties(
-        buildername=
-            'Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    ) +
-    api.path.exists(
-        api.path['start_dir'].join('skia'),
-        api.path['start_dir'].join('src')
-    )
-  )
-
-  yield(
-    api.test('CT_CPU_BENCH_10k_SKPs') +
-    api.properties(
-        buildername=
-            'Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_10k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    ) +
-    api.path.exists(
-        api.path['start_dir'].join('skia'),
-        api.path['start_dir'].join('src')
-    )
-  )
-
-  yield(
-    api.test('CT_GPU_BENCH_10k_SKPs') +
-    api.properties(
-        buildername=
-            'Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_10k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    ) +
-    api.path.exists(
-        api.path['start_dir'].join('skia'),
-        api.path['start_dir'].join('src')
-    )
-  )
-
-  yield(
-    api.test('CT_DM_1m_SKPs') +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield (
-    api.test('CT_DM_SKPs_UnknownBuilder') +
-    api.properties(
-        buildername=
-            'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_UnknownRepo_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    ) +
-    api.expect_exception('Exception')
-  )
-
-  yield (
-    api.test('CT_10k_SKPs_UnknownBuilder') +
-    api.properties(
-        buildername=
-            'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_UnknownTool_10k_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    ) +
-    api.expect_exception('Exception')
-  )
-
-  yield(
-    api.test('CT_DM_1m_SKPs_slave3_failure') +
-    api.step_data('ct-dm-3', retcode=1) +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield(
-    api.test('CT_DM_1m_SKPs_2slaves_failure') +
-    api.step_data('ct-dm-1', retcode=1) +
-    api.step_data('ct-dm-3', retcode=1) +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
-
-  yield(
-    api.test('CT_DM_10k_SKPs_Trybot') +
-    api.properties(
-        buildername=
-            'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_10k_SKPs-Trybot',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        rietveld='codereview.chromium.org',
-        issue=1499623002,
-        patchset=1,
-        repository='https://skia.googlesource.com/skia.git',
-    )
-  )
-
-  yield(
-    api.test('CT_IMG_DECODE_10k_SKPs_Trybot') +
-    api.properties(
-        buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_'
-                    '10k_SKPs_Trybot',
-        mastername=mastername,
-        slavename=slavename,
-        buildnumber=buildnumber,
-        path_config=path_config,
-        swarm_out_dir='[SWARM_OUT_DIR]',
-        ct_num_slaves=ct_num_slaves,
-        num_per_slave=num_per_slave,
-        repository='https://skia.googlesource.com/skia.git',
-        revision=skia_revision,
-    )
-  )
diff --git a/infra/bots/recipes/swarm_housekeeper.expected/Housekeeper-PerCommit-Trybot.json b/infra/bots/recipes/swarm_housekeeper.expected/Housekeeper-PerCommit-Trybot.json
deleted file mode 100644
index a85dc85..0000000
--- a/infra/bots/recipes/swarm_housekeeper.expected/Housekeeper-PerCommit-Trybot.json
+++ /dev/null
@@ -1,133 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--gerrit_repo",
-      "https://skia.googlesource.com/skia.git",
-      "--gerrit_ref",
-      "refs/changes/89/456789/12",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "RECIPE_MODULE[skia::core]/resources/run_binary_size_analysis.py",
-      "--library",
-      "[START_DIR]/out/Release/lib/libskia.so",
-      "--githash",
-      "abc123",
-      "--gsutil_path",
-      "[DEPOT_TOOLS]/gsutil.py",
-      "--issue_number",
-      "456789"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "generate and upload binary size data"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_housekeeper.expected/Housekeeper-PerCommit.json b/infra/bots/recipes/swarm_housekeeper.expected/Housekeeper-PerCommit.json
deleted file mode 100644
index fa7d584..0000000
--- a/infra/bots/recipes/swarm_housekeeper.expected/Housekeeper-PerCommit.json
+++ /dev/null
@@ -1,141 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "RECIPE_MODULE[skia::core]/resources/generate_and_upload_doxygen.py"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "generate and upload doxygen"
-  },
-  {
-    "cmd": [
-      "python",
-      "RECIPE_MODULE[skia::core]/resources/run_binary_size_analysis.py",
-      "--library",
-      "[START_DIR]/out/Release/lib/libskia.so",
-      "--githash",
-      "abc123",
-      "--gsutil_path",
-      "[DEPOT_TOOLS]/gsutil.py"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "generate and upload binary size data"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_housekeeper.py b/infra/bots/recipes/swarm_housekeeper.py
deleted file mode 100644
index 2dc248c..0000000
--- a/infra/bots/recipes/swarm_housekeeper.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# 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.
-
-
-# Recipe for the Skia PerCommit Housekeeper.
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/python',
-  'recipe_engine/step',
-  'core',
-  'run',
-  'vars',
-]
-
-
-TEST_BUILDERS = {
-  'client.skia.fyi': {
-    'skiabot-linux-housekeeper-000': [
-      'Housekeeper-PerCommit',
-      'Housekeeper-PerCommit-Trybot',
-    ],
-  },
-}
-
-
-def RunSteps(api):
-  # Checkout, compile, etc.
-  api.core.setup()
-
-  cwd = api.path['checkout']
-
-  # TODO(borenet): Detect static initializers?
-
-  with api.step.context({'cwd': cwd}):
-    gsutil_path = api.path['depot_tools'].join('gsutil.py')
-    if not api.vars.is_trybot:
-      api.run(
-        api.step,
-        'generate and upload doxygen',
-        cmd=['python', api.core.resource('generate_and_upload_doxygen.py')],
-        abort_on_failure=False)
-
-    cmd = ['python', api.core.resource('run_binary_size_analysis.py'),
-           '--library', api.vars.skia_out.join(
-               'Release', 'lib', 'libskia.so'),
-           '--githash', api.properties['revision'],
-           '--gsutil_path', gsutil_path]
-    if api.vars.is_trybot:
-      cmd.extend(['--issue_number', str(api.properties['patch_issue'])])
-    api.run(
-      api.step,
-      'generate and upload binary size data',
-      cmd=cmd,
-      abort_on_failure=False)
-
-
-def GenTests(api):
-  yield (
-      api.test('Housekeeper-PerCommit') +
-      api.properties(buildername='Housekeeper-PerCommit',
-                     mastername='client.skia.fyi',
-                     slavename='skiabot-linux-housekeeper-000',
-                     buildnumber=5,
-                     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'])
-  )
-  yield (
-      api.test('Housekeeper-PerCommit-Trybot') +
-      api.properties(buildername='Housekeeper-PerCommit',
-                     mastername='client.skia.fyi',
-                     slavename='skiabot-linux-housekeeper-000',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     patch_storage='gerrit',
-                     nobuildbot='True',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-      api.properties.tryserver(
-          buildername='Housekeeper-PerCommit',
-          gerrit_project='skia',
-          gerrit_url='https://skia-review.googlesource.com/',
-      ) +
-      api.path.exists(api.path['start_dir'])
-  )
diff --git a/infra/bots/recipes/swarm_infra.expected/failed_all_updates.json b/infra/bots/recipes/swarm_infra.expected/failed_all_updates.json
deleted file mode 100644
index a5ba6b1..0000000
--- a/infra/bots/recipes/swarm_infra.expected/failed_all_updates.json
+++ /dev/null
@@ -1,190 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 2)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 3)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 4)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 5)",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "reason": "Step('update go pkgs (attempt 5)') failed with return_code 1",
-    "recipe_result": null,
-    "status_code": 1
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_infra.expected/failed_one_update.json b/infra/bots/recipes/swarm_infra.expected/failed_one_update.json
deleted file mode 100644
index 68f0e41..0000000
--- a/infra/bots/recipes/swarm_infra.expected/failed_one_update.json
+++ /dev/null
@@ -1,133 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs",
-    "~followup_annotations": [
-      "step returned non-zero exit code: 1",
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs (attempt 2)"
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/infra_tests.py"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath"
-    },
-    "name": "infra_tests"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_infra.expected/infra_tests.json b/infra/bots/recipes/swarm_infra.expected/infra_tests.json
deleted file mode 100644
index 1576173..0000000
--- a/infra/bots/recipes/swarm_infra.expected/infra_tests.json
+++ /dev/null
@@ -1,112 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "go",
-      "get",
-      "-u",
-      "-t",
-      "go.skia.org/infra/..."
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "update go pkgs"
-  },
-  {
-    "cmd": [
-      "python",
-      "[CUSTOM_/_B_WORK]/skia/infra/bots/infra_tests.py"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "GOPATH": "[CUSTOM_/_B_WORK]/gopath"
-    },
-    "name": "infra_tests"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_infra.py b/infra/bots/recipes/swarm_infra.py
deleted file mode 100644
index ac371a8..0000000
--- a/infra/bots/recipes/swarm_infra.py
+++ /dev/null
@@ -1,72 +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 which runs the Skia infra tests.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/step',
-  'core',
-  'infra',
-  'run',
-  'vars',
-]
-
-
-def RunSteps(api):
-  api.vars.setup()
-  api.core.checkout_steps()
-  api.infra.update_go_deps()
-
-  # Run the infra tests.
-  infra_tests = api.vars.skia_dir.join('infra', 'bots', 'infra_tests.py')
-  with api.step.context({'cwd': api.vars.skia_dir, 'env': api.infra.go_env}):
-    api.step('infra_tests', cmd=['python', infra_tests])
-
-
-def GenTests(api):
-  yield (
-      api.test('infra_tests') +
-      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]')
-  )
-
-  yield (
-    api.test('failed_one_update') +
-      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-    api.step_data('update go pkgs', retcode=1)
-  )
-
-  yield (
-    api.test('failed_all_updates') +
-      api.properties(buildername='Housekeeper-PerCommit-InfraTests',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-    api.step_data('update go pkgs', retcode=1) +
-    api.step_data('update go pkgs (attempt 2)', retcode=1) +
-    api.step_data('update go pkgs (attempt 3)', retcode=1) +
-    api.step_data('update go pkgs (attempt 4)', retcode=1) +
-    api.step_data('update go pkgs (attempt 5)', retcode=1)
-  )
diff --git a/infra/bots/recipes/swarm_perf.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release.json b/infra/bots/recipes/swarm_perf.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release.json
deleted file mode 100644
index ad50d17..0000000
--- a/infra/bots/recipes/swarm_perf.expected/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release.json
+++ /dev/null
@@ -1,215 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree data"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs data",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Release/nanobench",
-      "--undefok",
-      "-i",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/nanobench",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--nogpu",
-      "--pre_log",
-      "--scales",
-      "1.0",
-      "1.1",
-      "--config",
-      "8888",
-      "nonrendering",
-      "hwui",
-      "f16",
-      "srgb",
-      "565",
-      "gl",
-      "glmsaa8",
-      "glnvpr8",
-      "glnvprdit8",
-      "--match",
-      "~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",
-      "--outResultsFile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data/nanobench_abc123_1337000001.json",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized nanobench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_perf.py b/infra/bots/recipes/swarm_perf.py
deleted file mode 100644
index b6b876f..0000000
--- a/infra/bots/recipes/swarm_perf.py
+++ /dev/null
@@ -1,40 +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 perf.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-  'perf',
-]
-
-
-def RunSteps(api):
-  api.perf.run()
-
-
-def GenTests(api):
-  yield (
-    api.test('Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release') +
-    api.properties(buildername='Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release',
-                   mastername='fake-master',
-                   slavename='fake-slave',
-                   buildnumber=5,
-                   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',
-                                   'skimage', 'VERSION'),
-        api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                   'skp', 'VERSION'),
-        api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-    )
-  )
diff --git a/infra/bots/recipes/swarm_presubmit.expected/presubmit.json b/infra/bots/recipes/swarm_presubmit.expected/presubmit.json
deleted file mode 100644
index 29f614b..0000000
--- a/infra/bots/recipes/swarm_presubmit.expected/presubmit.json
+++ /dev/null
@@ -1,146 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_/_B_WORK]",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec",
-      "cache_dir = '[CUSTOM_/_B_CACHE]'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"skia\": \"got_revision\"}",
-      "--git-cache-dir",
-      "[CUSTOM_/_B_CACHE]",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123",
-      "--output_manifest"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]",
-    "env": {
-      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
-      "GIT_HTTP_LOW_SPEED_TIME": "300",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_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@  \"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": [
-      "git",
-      "checkout",
-      "-b",
-      "tmp_00000000-0000-0000-0000-000000000000"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "name": "create git branch"
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "name": "git status"
-  },
-  {
-    "cmd": [
-      "git",
-      "cl",
-      "presubmit",
-      "--force",
-      "-v",
-      "-v"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "env": {
-      "PATH": "RECIPE_PACKAGE_REPO[depot_tools]:%(PATH)s"
-    },
-    "name": "presubmit"
-  },
-  {
-    "cmd": [
-      "git",
-      "reset",
-      "--hard",
-      "origin/master"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "name": "git reset"
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "origin/master"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "name": "checkout origin/master"
-  },
-  {
-    "cmd": [
-      "git",
-      "branch",
-      "-D",
-      "tmp_00000000-0000-0000-0000-000000000000"
-    ],
-    "cwd": "[CUSTOM_/_B_WORK]/skia",
-    "name": "delete git branch"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_presubmit.py b/infra/bots/recipes/swarm_presubmit.py
deleted file mode 100644
index 52b67ce..0000000
--- a/infra/bots/recipes/swarm_presubmit.py
+++ /dev/null
@@ -1,58 +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 which runs the Skia presubmit.
-
-
-DEPS = [
-  'depot_tools/depot_tools',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/step',
-  'recipe_engine/uuid',
-  'core',
-  'vars',
-]
-
-
-def RunSteps(api):
-  api.vars.setup()
-  api.core.checkout_steps()
-
-  with api.step.context({'cwd': api.vars.skia_dir}):
-    # git-cl wants us to be on a branch.
-    branch = 'tmp_%s' % api.uuid.random()
-    api.step('create git branch',
-             cmd=['git', 'checkout', '-b', branch])
-    try:
-      api.step('git status',
-               cmd=['git', 'status'])
-  
-      depot_tools_path = api.depot_tools.package_repo_resource()
-      env = {'PATH': api.path.pathsep.join([str(depot_tools_path), '%(PATH)s'])}
-      with api.step.context({'env': env}):
-        api.step('presubmit',
-                 cmd=['git', 'cl', 'presubmit', '--force', '-v', '-v'])
-    finally:
-      api.step('git reset',
-               cmd=['git', 'reset', '--hard', 'origin/master'])
-      api.step('checkout origin/master',
-               cmd=['git', 'checkout', 'origin/master'])
-      api.step('delete git branch',
-               cmd=['git', 'branch', '-D', branch])
-
-
-def GenTests(api):
-  yield (
-      api.test('presubmit') +
-      api.properties(buildername='Housekeeper-PerCommit-Presubmit',
-                     mastername='client.skia.fyi',
-                     slavename='dummy-slave',
-                     buildnumber=5,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]')
-  )
diff --git a/infra/bots/recipes/swarm_skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench.json b/infra/bots/recipes/swarm_skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench.json
deleted file mode 100644
index 807b3d0..0000000
--- a/infra/bots/recipes/swarm_skpbench.expected/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench.json
+++ /dev/null
@@ -1,437 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "reboot"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rebooting device"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport subprocess\nimport sys\nimport time\n\nkicks = 0\nwhile True:\n\n  times = 0\n  while times < 30:\n    print 'Waiting for the device to be connected and ready.'\n    try:\n      times += 1\n      output = subprocess.check_output(['adb', 'shell',\n                                        'getprop', 'sys.boot_completed'])\n      if '1' in output:\n        print 'Connected'\n        sys.exit(0)\n    except subprocess.CalledProcessError:\n      # no device connected/authorized yet\n      pass\n    time.sleep(5)\n  if kicks >= 3:\n    break\n  print 'Giving the device a \"kick\" by trying to reboot it.'\n  kicks += 1\n  print subprocess.check_output(['adb', 'reboot'])\n\nprint 'Timed out waiting for device'\nsys.exit(1)\n"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "wait for device",
-    "~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@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@kicks = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@while True:@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@  times = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@  while times < 30:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Waiting for the device to be connected and ready.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@    try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      times += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      output = subprocess.check_output(['adb', 'shell',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                                        'getprop', 'sys.boot_completed'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if '1' in output:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print 'Connected'@@@",
-      "@@@STEP_LOG_LINE@python.inline@        sys.exit(0)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    except subprocess.CalledProcessError:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      # no device connected/authorized yet@@@",
-      "@@@STEP_LOG_LINE@python.inline@      pass@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(5)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if kicks >= 3:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print 'Giving the device a \"kick\" by trying to reboot it.'@@@",
-      "@@@STEP_LOG_LINE@python.inline@  kicks += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print subprocess.check_output(['adb', 'reboot'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'Timed out waiting for device'@@@",
-      "@@@STEP_LOG_LINE@python.inline@sys.exit(1)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/resources"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "cat",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
-    "stdout": "/path/to/tmp/"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-f",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
-      "[START_DIR]/skp",
-      "/sdcard/revenge_of_the_skiabot/skps"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
-      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/tmp/SKP_VERSION",
-      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "adb",
-      "push",
-      "[START_DIR]/out/Release/skpbench",
-      "/data/local/tmp/"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "push skpbench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/tools/skpbench/skpbench.py",
-      "/data/local/tmp/skpbench",
-      "/sdcard/revenge_of_the_skiabot/skps",
-      "--adb",
-      "--resultsfile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/table",
-      "--config",
-      "gles,glesinst4"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "skpbench"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id",
-    "stdout": "/path/to/tmp/",
-    "~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",
-    "stdout": "/path/to/tmp/",
-    "~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",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench/data",
-      "511"
-    ],
-    "name": "makedirs perf_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/skia/tools/skpbench/skiaperf.py",
-      "[CUSTOM_[SWARM_OUT_DIR]]/table",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "build_number",
-      "5",
-      "no_buildbot",
-      "True",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--outfile",
-      "[CUSTOM_[SWARM_OUT_DIR]]/perfdata/Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench/data/skpbench_abc123_1337000001.json",
-      "--key",
-      "arch",
-      "arm64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "TegraX1",
-      "extra_config",
-      "GN_Android_Skpbench",
-      "model",
-      "PixelC",
-      "os",
-      "Android"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "Parse skpbench output into Perf json"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
-      "[START_DIR]/out/Release"
-    ],
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "dump log",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
-      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
-      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
-      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
-      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
-      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "adb",
-      "kill-server"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Release",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "infra_step": true,
-    "name": "kill adb server"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_skpbench.py b/infra/bots/recipes/swarm_skpbench.py
deleted file mode 100644
index 4561f61..0000000
--- a/infra/bots/recipes/swarm_skpbench.py
+++ /dev/null
@@ -1,41 +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 skpbench.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-  'skpbench',
-]
-
-
-def RunSteps(api):
-  api.skpbench.run()
-
-
-def GenTests(api):
-  b = 'Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-GN_Android_Skpbench'
-  yield (
-    api.test(b) +
-    api.properties(buildername=b,
-                   mastername='fake-master',
-                   slavename='fake-slave',
-                   buildnumber=5,
-                   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',
-                                   'skp', 'VERSION'),
-    ) +
-    api.step_data('get swarming bot id',
-        stdout=api.raw_io.output('skia-bot-123')) +
-    api.step_data('get swarming task id',
-        stdout=api.raw_io.output('123456'))
-  )
diff --git a/infra/bots/recipes/swarm_test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json b/infra/bots/recipes/swarm_test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json
deleted file mode 100644
index 5c003f9..0000000
--- a/infra/bots/recipes/swarm_test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json
+++ /dev/null
@@ -1,548 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SKP VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded skimage VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get downloaded SVG VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[build::file]/resources/fileutil.py",
-      "rmtree",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
-    ],
-    "env": {
-      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
-    },
-    "infra_step": true,
-    "name": "rmtree dm"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs dm",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
-      "[START_DIR]/tmp",
-      "511"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
-      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
-      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
-      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://gold.skia.org/_/hashes'\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[1], '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",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "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 = 'https://gold.skia.org/_/hashes'@@@",
-      "@@@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[1], '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",
-      "[START_DIR]/skia/infra/bots/recipe_modules/core/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/out/Debug/dm",
-      "--undefok",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "master",
-      "fake-master",
-      "builder",
-      "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
-      "build_number",
-      "5",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86",
-      "compiler",
-      "GCC",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Ubuntu",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
-      "--nogpu",
-      "--randomProcessorTest",
-      "--threads",
-      "4",
-      "--config",
-      "8888",
-      "srgb",
-      "pdf",
-      "gl",
-      "gldft",
-      "glsrgb",
-      "glmsaa8",
-      "565",
-      "f16",
-      "sp-8888",
-      "2ndpic-8888",
-      "lite-8888",
-      "gbr-8888",
-      "serialize-8888",
-      "tiles_rt-8888",
-      "pic-8888",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "f16",
-      "_",
-      "_",
-      "dstreadshuffle",
-      "glsrgb",
-      "image",
-      "_",
-      "_",
-      "8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "image",
-      "_",
-      "_",
-      "gbr-8888",
-      "colorImage",
-      "_",
-      "_",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "c_gms",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype",
-      "serialize-8888",
-      "gm",
-      "_",
-      "colortype_xfermodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_0.75_0",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds_1_-0.25",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_bounds",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_match",
-      "serialize-8888",
-      "gm",
-      "_",
-      "fontmgr_iter",
-      "serialize-8888",
-      "gm",
-      "_",
-      "imagemasksubset",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapfilters",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bitmapshaders",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_bmp_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "convex_poly_clip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "extractalpha",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_checkerboard_32_32_g8",
-      "serialize-8888",
-      "gm",
-      "_",
-      "filterbitmap_image_mandrill_64",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadows",
-      "serialize-8888",
-      "gm",
-      "_",
-      "simpleaaclip_aaclip",
-      "serialize-8888",
-      "gm",
-      "_",
-      "composeshader_bitmap",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes_npot",
-      "serialize-8888",
-      "gm",
-      "_",
-      "scaled_tilemodes",
-      "serialize-8888",
-      "gm",
-      "_",
-      "typefacerendering_pfaMac",
-      "serialize-8888",
-      "gm",
-      "_",
-      "parsedpaths",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_rect",
-      "serialize-8888",
-      "gm",
-      "_",
-      "ImageGeneratorExternal_shader",
-      "serialize-8888",
-      "gm",
-      "_",
-      "shadow_utils",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image",
-      "serialize-8888",
-      "gm",
-      "_",
-      "bleed_alpha_image_shader",
-      "sp-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "pic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "lite-8888",
-      "gm",
-      "_",
-      "drawfilter",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-picture",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-raster",
-      "sp-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "pic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "serialize-8888",
-      "gm",
-      "_",
-      "image-cacherator-from-ctable",
-      "sp-8888",
-      "gm",
-      "_",
-      "gamut",
-      "pic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "lite-8888",
-      "gm",
-      "_",
-      "gamut",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "gamut",
-      "serialize-8888",
-      "gm",
-      "_",
-      "gamut",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "sp-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "pic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "lite-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "2ndpic-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "serialize-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "BUILDTYPE": "Debug",
-      "CHROME_HEADLESS": "1",
-      "PATH": "%(PATH)s:RECIPE_PACKAGE_REPO[depot_tools]",
-      "SKIA_OUT": "[START_DIR]/out"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result",
-    "recipe_result": null,
-    "status_code": 0
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/swarm_test.py b/infra/bots/recipes/swarm_test.py
deleted file mode 100644
index 2e101ae..0000000
--- a/infra/bots/recipes/swarm_test.py
+++ /dev/null
@@ -1,42 +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 test.
-
-
-DEPS = [
-  'recipe_engine/path',
-  'recipe_engine/platform',
-  'recipe_engine/properties',
-  'recipe_engine/raw_io',
-  'sktest',
-]
-
-
-def RunSteps(api):
-  api.sktest.run()
-
-
-def GenTests(api):
-  yield (
-    api.test('Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug') +
-    api.properties(buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug',
-                   mastername='fake-master',
-                   slavename='fake-slave',
-                   buildnumber=5,
-                   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',
-                                   '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')
-    )
-  )
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android.json
new file mode 100644
index 0000000..90e6b67
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-AndroidOne-GPU-Mali400MP2-arm-Release-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value Mali400MP2 extra_config Android model AndroidOne os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 _ gm _ bigblurs _ gm _ bleed _ gm _ bleed_alpha_bmp _ gm _ bleed_alpha_bmp_shader _ gm _ bleed_alpha_image _ gm _ bleed_alpha_image_shader _ gm _ bleed_image _ gm _ dropshadowimagefilter _ gm _ filterfastbounds gles gm _ imageblurtiled _ gm _ imagefiltersclipped _ gm _ imagefiltersscaled _ gm _ imageresizetiled _ gm _ matrixconvolution _ gm _ strokedlines glesmsaa4 gm _ imageblurtiled glesmsaa4 gm _ imagefiltersbase --match ~WritePixels; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android.json
new file mode 100644
index 0000000..014bfb7
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch mipsel compiler Clang configuration Release cpu_or_gpu CPU cpu_or_gpu_value IngenicJZ4780 extra_config Android model Ci20 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nogpu --randomProcessorTest --config 8888 --src tests gm image colorImage svg --blacklist _ test _ GrShape _ gm _ fast_slow_blurimagefilter --match ~Codec_Dimensions ~FontMgrAndroidParser ~PathOpsSimplify; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android.json
new file mode 100644
index 0000000..1c87c68
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-GalaxyS6-GPU-MaliT760-arm64-Debug-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value MaliT760 extra_config Android model GalaxyS6 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~SpecialImage; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android.json
new file mode 100644
index 0000000..65bdbf7
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Adreno530 extra_config Android model GalaxyS7_G930A os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~WritePixels; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android.json
new file mode 100644
index 0000000..626a5be
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value TegraX1 extra_config Android model NVIDIA_Shield os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gl gldft glsrgb glmsaa4 glinstdit4 serialize-8888 tiles_rt-8888 pic-8888 glinst --src tests gm image colorImage svg --blacklist glsrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android.json
new file mode 100644
index 0000000..7a6226c
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Nexus10-GPU-MaliT604-arm-Release-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value MaliT604 extra_config Android model Nexus10 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~CopySurface ~SRGBReadWritePixels; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android.json
new file mode 100644
index 0000000..12a2e34
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Nexus5-GPU-Adreno330-arm-Release-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value Adreno330 extra_config Android model Nexus5 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 _ gm _ encode-platform --noRAW_threading; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan.json
new file mode 100644
index 0000000..5650f3f
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Adreno430 extra_config Android_Vulkan model Nexus6p os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config vk --src tests gm image colorImage svg --blacklist _ test _ GrShape _ 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; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android.json
new file mode 100644
index 0000000..cf56b7e
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --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 --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-Android.json
new file mode 100644
index 0000000..8732752
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-NexusPlayer-CPU-SSE4-x86-Release-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch x86 compiler Clang configuration Release cpu_or_gpu CPU cpu_or_gpu_value SSE4 extra_config Android model NexusPlayer os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nogpu --randomProcessorTest --config 8888 srgb gles glessrgb --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape --match ~ResourceCache --noRAW_threading; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan.json b/infra/bots/recipes/test.expected/Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan.json
new file mode 100644
index 0000000..85f24c9
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Release/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch x86 compiler Clang configuration Release cpu_or_gpu GPU cpu_or_gpu_value PowerVR extra_config Android_Vulkan model NexusPlayer os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config vk --src tests gm colorImage --blacklist _ test _ GrShape _ 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 --match ~ResourceCache ~gradients_no_texture$ ~tilemodes ~shadertext$ ~bitmapfilters ~GrContextFactory_abandon --noRAW_threading; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Release"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android.json
new file mode 100644
index 0000000..569b645
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-PixelC-CPU-TegraX1-arm64-Debug-Android swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu CPU cpu_or_gpu_value TegraX1 extra_config Android model PixelC os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nogpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 glesinstdit4 serialize-8888 tiles_rt-8888 pic-8888 glesinst --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan.json b/infra/bots/recipes/test.expected/Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan.json
new file mode 100644
index 0000000..0baf9d9
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan.json
@@ -0,0 +1,852 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan swarming_bot_id skia-bot-123 swarming_task_id 123456 --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm64 compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Adreno530 extra_config Android_Vulkan model PixelXL os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config vk --src tests gm image colorImage svg --blacklist _ test _ GrShape _ 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 --match ~CopySurface; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug.json b/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug.json
new file mode 100644
index 0000000..26b0a1b
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug.json
@@ -0,0 +1,1004 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nSSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')\nwith open(SSH_MACHINE_FILE, 'r') as f:\n  print f.read()\n"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read chromeos ip",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@SSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(SSH_MACHINE_FILE, 'r') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print f.read()@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/resources"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "sudo",
+      "mount",
+      "-i",
+      "-o",
+      "remount,exec",
+      "/home/chronos"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "remount /home/chronos/user/ as exec"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/bin"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/bin"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/bin"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/bin"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skia/resources",
+      "foo@127.0.0.1:/home/chronos/user/resources"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skia/resources foo@127.0.0.1:/home/chronos/user/resources",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/skps"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skp",
+      "foo@127.0.0.1:/home/chronos/user/skps"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skp foo@127.0.0.1:/home/chronos/user/skps",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SKP_VERSION foo@127.0.0.1:/home/chronos/user/SKP_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/images"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/skimage",
+      "foo@127.0.0.1:/home/chronos/user/images"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/skimage foo@127.0.0.1:/home/chronos/user/images",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SK_IMAGE_VERSION foo@127.0.0.1:/home/chronos/user/SK_IMAGE_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "cat",
+      "/home/chronos/user/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /home/chronos/user/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-f",
+      "/home/chronos/user/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/svgs"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "[START_DIR]/svg",
+      "foo@127.0.0.1:/home/chronos/user/svgs"
+    ],
+    "infra_step": true,
+    "name": "scp -r [START_DIR]/svg foo@127.0.0.1:/home/chronos/user/svgs",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "foo@127.0.0.1:/home/chronos/user/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/SVG_VERSION foo@127.0.0.1:/home/chronos/user/SVG_VERSION",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "rm",
+      "-rf",
+      "/home/chronos/user/dm_out"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /home/chronos/user/dm_out"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "mkdir",
+      "-p",
+      "/home/chronos/user/dm_out"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /home/chronos/user/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "foo@127.0.0.1:/home/chronos/user/uninteresting_hashes.txt"
+    ],
+    "infra_step": true,
+    "name": "scp [START_DIR]/tmp/uninteresting_hashes.txt foo@127.0.0.1:/home/chronos/user/uninteresting_hashes.txt",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+      "\nimport subprocess\nimport sys\nhost = sys.argv[1]\ndevice   = sys.argv[2]\nprint subprocess.check_output(['scp', host, device])\n",
+      "[START_DIR]/out/Debug/dm",
+      "foo@127.0.0.1:/home/chronos/user/bin/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "scp [START_DIR]/out/Debug/dm foo@127.0.0.1:/home/chronos/user/bin/dm",
+    "~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@host = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output(['scp', host, device])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "chmod",
+      "+x",
+      "/home/chronos/user/bin/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "chmod dm"
+  },
+  {
+    "cmd": [
+      "ssh",
+      "-oConnectTimeout=15",
+      "-oBatchMode=yes",
+      "-t",
+      "-t",
+      "foo@127.0.0.1",
+      "/home/chronos/user/bin/dm",
+      "--undefok",
+      "--resourcePath",
+      "/home/chronos/user/resources",
+      "--skps",
+      "/home/chronos/user/skps",
+      "--images",
+      "/home/chronos/user/images/dm",
+      "--colorImages",
+      "/home/chronos/user/images/colorspace",
+      "--nameByHash",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "builder",
+      "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "/home/chronos/user/svgs",
+      "--key",
+      "arch",
+      "arm",
+      "compiler",
+      "Clang",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "MaliT764",
+      "model",
+      "Chromebook_C100p",
+      "os",
+      "ChromeOS",
+      "--uninterestingHashesFile",
+      "/home/chronos/user/uninteresting_hashes.txt",
+      "--writePath",
+      "/home/chronos/user/dm_out",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "gles",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nsrc = sys.argv[1] + '/*'\ndest   = sys.argv[2]\nprint subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)\n",
+      "foo@127.0.0.1:/home/chronos/user/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "infra_step": true,
+    "name": "scp -r foo@127.0.0.1:/home/chronos/user/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm",
+    "~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@src = sys.argv[1] + '/*'@@@",
+      "@@@STEP_LOG_LINE@python.inline@dest   = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug.json b/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug.json
new file mode 100644
index 0000000..0e625b0
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug.json
@@ -0,0 +1,568 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
+      "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",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX",
+      "model",
+      "MacMini6.2",
+      "os",
+      "Mac",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "glinst",
+      "glinst8",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "frame_larger_than_image.gif",
+      "_",
+      "image",
+      "gen_platf",
+      "inc0.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc1.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc2.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc3.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc4.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc5.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc6.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc7.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc8.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc9.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc10.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc11.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc12.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc13.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc14.png",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer.json b/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer.json
new file mode 100644
index 0000000..233dee0
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer.json
@@ -0,0 +1,465 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer",
+      "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",
+      "IntelHD4000",
+      "extra_config",
+      "CommandBuffer",
+      "model",
+      "MacMini6.2",
+      "os",
+      "Mac",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "commandbuffer",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "frame_larger_than_image.gif",
+      "_",
+      "image",
+      "gen_platf",
+      "inc0.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc1.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc2.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc3.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc4.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc5.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc6.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc7.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc8.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc9.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc10.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc11.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc12.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc13.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc14.png",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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",
+      "--match",
+      "~HalfFloatAlphaTextureTest"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN.json b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN.json
new file mode 100644
index 0000000..5e4fcb6
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN.json
@@ -0,0 +1,465 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN",
+      "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",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "extra_config",
+      "ASAN",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ASAN_OPTIONS": "symbolize=1 detect_leaks=1",
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "LSAN_OPTIONS": "symbolize=1 print_suppressions=1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
+      "SKIA_OUT": "[START_DIR]/out",
+      "UBSAN_OPTIONS": "symbolize=1 print_stacktrace=1"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN.json b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN.json
new file mode 100644
index 0000000..2d1dc60
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN.json
@@ -0,0 +1,466 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN",
+      "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",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "extra_config",
+      "MSAN",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "--match",
+      "~Once",
+      "~Shared"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/msan",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER.json b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER.json
new file mode 100644
index 0000000..45afba3
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER.json
@@ -0,0 +1,280 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]/out/Release/dm",
+      "--undefok",
+      "--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-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "Clang",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "extra_config",
+      "SK_FORCE_RASTER_PIPELINE_BLITTER",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "--src",
+      "gm",
+      "image",
+      "colorImage",
+      "svg"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN.json b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN.json
new file mode 100644
index 0000000..57f6057
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN.json
@@ -0,0 +1,466 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Release/dm",
+      "--undefok",
+      "--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-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "Clang",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "extra_config",
+      "TSAN",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "--match",
+      "~ReadWriteAlpha",
+      "~RGBA4444TextureTest",
+      "~RGB565TextureTest"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json
new file mode 100644
index 0000000..3a7f99b
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug.json
@@ -0,0 +1,576 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--threads",
+      "4",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
new file mode 100644
index 0000000..f4b2d38
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
@@ -0,0 +1,578 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "extra_config",
+      "SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--threads",
+      "0",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug.json b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug.json
new file mode 100644
index 0000000..c034e37
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug.json
@@ -0,0 +1,574 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
new file mode 100644
index 0000000..264fe28
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind.json
@@ -0,0 +1,494 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[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]/out/Release/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX550Ti",
+      "extra_config",
+      "Valgrind",
+      "model",
+      "ShuttleA",
+      "os",
+      "Ubuntu",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "glnvprdit8",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "pdf",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "pdf",
+      "_",
+      "_",
+      "PANO_20121023_214540.jpg",
+      "pdf",
+      "skp",
+      "_",
+      "worldjournal",
+      "pdf",
+      "skp",
+      "_",
+      "desk_baidu.skp",
+      "pdf",
+      "skp",
+      "_",
+      "desk_wikipedia.skp",
+      "_",
+      "svg",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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",
+      "--match",
+      "~Threaded"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
new file mode 100644
index 0000000..0ea6540
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext.json
@@ -0,0 +1,495 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[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]/out/Release/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX550Ti",
+      "extra_config",
+      "Valgrind_AbandonGpuContext",
+      "model",
+      "ShuttleA",
+      "os",
+      "Ubuntu",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "glnvprdit8",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "pdf",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "pdf",
+      "_",
+      "_",
+      "PANO_20121023_214540.jpg",
+      "pdf",
+      "skp",
+      "_",
+      "worldjournal",
+      "pdf",
+      "skp",
+      "_",
+      "desk_baidu.skp",
+      "pdf",
+      "skp",
+      "_",
+      "desk_wikipedia.skp",
+      "_",
+      "svg",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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",
+      "--match",
+      "~Threaded",
+      "--abandonGpuContext"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext.json b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext.json
new file mode 100644
index 0000000..5a20892
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext.json
@@ -0,0 +1,496 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[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]/out/Release/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX550Ti",
+      "extra_config",
+      "Valgrind_PreAbandonGpuContext",
+      "model",
+      "ShuttleA",
+      "os",
+      "Ubuntu",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "glnvprdit8",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "pdf",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "pdf",
+      "_",
+      "_",
+      "PANO_20121023_214540.jpg",
+      "pdf",
+      "skp",
+      "_",
+      "worldjournal",
+      "pdf",
+      "skp",
+      "_",
+      "desk_baidu.skp",
+      "pdf",
+      "skp",
+      "_",
+      "desk_wikipedia.skp",
+      "_",
+      "svg",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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",
+      "--match",
+      "~Threaded",
+      "--verbose",
+      "--preAbandonGpuContext"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug.json b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug.json
new file mode 100644
index 0000000..e35706b
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug.json
@@ -0,0 +1,583 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug",
+      "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",
+      "IntelHD405",
+      "model",
+      "NUC5PPYH",
+      "os",
+      "Ubuntu16",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "gles",
+      "glesdft",
+      "glessrgb",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "glessrgb",
+      "image",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
+      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json
new file mode 100644
index 0000000..ef7f48d
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json
@@ -0,0 +1,377 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
+      "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",
+      "IntelIris540",
+      "extra_config",
+      "Vulkan",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Ubuntu16",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "vk",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "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",
+      "--match",
+      "~VkHeapTests"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_sdk/lib:[START_DIR]/linux_vulkan_intel_driver_debug",
+      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]:RECIPE_PACKAGE_REPO[depot_tools]:[START_DIR]/linux_vulkan_sdk/bin",
+      "SKIA_OUT": "[START_DIR]/out",
+      "VK_ICD_FILENAMES": "[START_DIR]/linux_vulkan_intel_driver_debug/intel_icd.x86_64.json"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release.json b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release.json
new file mode 100644
index 0000000..77a5c79
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release.json
@@ -0,0 +1,583 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Release/dm",
+      "--undefok",
+      "--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-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "Clang",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris540",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Ubuntu16",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "gles",
+      "glesdft",
+      "glessrgb",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "glessrgb",
+      "image",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
+      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_release",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug.json b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug.json
new file mode 100644
index 0000000..6372b50
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug.json
@@ -0,0 +1,585 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]",
+      "[START_DIR]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
+      "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",
+      "IntelBayTrail",
+      "model",
+      "NUCDE3815TYKHE",
+      "os",
+      "Ubuntu16",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "gles",
+      "glesdft",
+      "glessrgb",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "glessrgb",
+      "image",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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",
+      "--match",
+      "~ImageStorageLoad"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
+      "LIBGL_DRIVERS_PATH": "[START_DIR]/linux_vulkan_intel_driver_debug",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan.json
new file mode 100644
index 0000000..225b46a
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan.json
@@ -0,0 +1,415 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "RadeonR9M470X",
+      "extra_config",
+      "Vulkan",
+      "model",
+      "AlphaR2",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "vk",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext.json
new file mode 100644
index 0000000..5c4b78a
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext.json
@@ -0,0 +1,628 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Release_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris6100",
+      "extra_config",
+      "ReleaseAndAbandonGpuContext",
+      "model",
+      "NUC5i7RYH",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "verylargebitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "verylarge_picture_image",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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",
+      "--releaseAndAbandonGpuContext"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE.json
new file mode 100644
index 0000000..13c2f1a
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE.json
@@ -0,0 +1,484 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris540",
+      "extra_config",
+      "ANGLE",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "angle_d3d11_es2",
+      "angle_d3d9_es2",
+      "angle_gl_es2",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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",
+      "_",
+      "gm",
+      "_",
+      "discard",
+      "angle_d3d9_es2",
+      "gm",
+      "_",
+      "multipicturedraw_invpathclip_simple",
+      "angle_d3d9_es2",
+      "gm",
+      "_",
+      "multipicturedraw_noclip_simple",
+      "angle_d3d9_es2",
+      "gm",
+      "_",
+      "multipicturedraw_pathclip_simple",
+      "angle_d3d9_es2",
+      "gm",
+      "_",
+      "multipicturedraw_rectclip_simple",
+      "angle_d3d9_es2",
+      "gm",
+      "_",
+      "multipicturedraw_rrectclip_simple",
+      "angle_d3d11_es2",
+      "gm",
+      "_",
+      "multipicturedraw_invpathclip_simple",
+      "angle_d3d11_es2",
+      "gm",
+      "_",
+      "multipicturedraw_noclip_simple",
+      "angle_d3d11_es2",
+      "gm",
+      "_",
+      "multipicturedraw_pathclip_simple",
+      "angle_d3d11_es2",
+      "gm",
+      "_",
+      "multipicturedraw_rectclip_simple",
+      "angle_d3d11_es2",
+      "gm",
+      "_",
+      "multipicturedraw_rrectclip_simple",
+      "angle_gl_es2",
+      "gm",
+      "_",
+      "multipicturedraw_invpathclip_simple",
+      "angle_gl_es2",
+      "gm",
+      "_",
+      "multipicturedraw_noclip_simple",
+      "angle_gl_es2",
+      "gm",
+      "_",
+      "multipicturedraw_pathclip_simple",
+      "angle_gl_es2",
+      "gm",
+      "_",
+      "multipicturedraw_rectclip_simple",
+      "angle_gl_es2",
+      "gm",
+      "_",
+      "multipicturedraw_rrectclip_simple",
+      "--match",
+      "~GLPrograms",
+      "~IntTexture"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json
new file mode 100644
index 0000000..5232310
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan.json
@@ -0,0 +1,625 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "IntelIris540",
+      "extra_config",
+      "Vulkan",
+      "model",
+      "NUC6i5SYK",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "vk",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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",
+      "vk",
+      "gm",
+      "_",
+      "aarectmodes",
+      "vk",
+      "gm",
+      "_",
+      "aaxfermodes",
+      "vk",
+      "gm",
+      "_",
+      "arithmode",
+      "vk",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "vk",
+      "gm",
+      "_",
+      "composeshader_bitmap2",
+      "vk",
+      "gm",
+      "_",
+      "dftextCOLR",
+      "vk",
+      "gm",
+      "_",
+      "drawregionmodes",
+      "vk",
+      "gm",
+      "_",
+      "filterfastbounds",
+      "vk",
+      "gm",
+      "_",
+      "fontcache",
+      "vk",
+      "gm",
+      "_",
+      "fontmgr_iterWin10",
+      "vk",
+      "gm",
+      "_",
+      "fontmgr_iter_factoryWin10",
+      "vk",
+      "gm",
+      "_",
+      "fontmgr_matchWin10",
+      "vk",
+      "gm",
+      "_",
+      "fontscalerWin",
+      "vk",
+      "gm",
+      "_",
+      "fontscalerdistortable",
+      "vk",
+      "gm",
+      "_",
+      "gammagradienttext",
+      "vk",
+      "gm",
+      "_",
+      "gammatextWin",
+      "vk",
+      "gm",
+      "_",
+      "gradtext",
+      "vk",
+      "gm",
+      "_",
+      "hairmodes",
+      "vk",
+      "gm",
+      "_",
+      "imagefilters_xfermodes",
+      "vk",
+      "gm",
+      "_",
+      "imagefiltersclipped",
+      "vk",
+      "gm",
+      "_",
+      "imagefiltersgraph",
+      "vk",
+      "gm",
+      "_",
+      "imagefiltersscaled",
+      "vk",
+      "gm",
+      "_",
+      "imagefiltersstroked",
+      "vk",
+      "gm",
+      "_",
+      "imagefilterstransformed",
+      "vk",
+      "gm",
+      "_",
+      "imageresizetiled",
+      "vk",
+      "gm",
+      "_",
+      "lcdblendmodes",
+      "vk",
+      "gm",
+      "_",
+      "lcdoverlap",
+      "vk",
+      "gm",
+      "_",
+      "lcdtextWin",
+      "vk",
+      "gm",
+      "_",
+      "lcdtextsize",
+      "vk",
+      "gm",
+      "_",
+      "matriximagefilter",
+      "vk",
+      "gm",
+      "_",
+      "mixedtextblobsCOLR",
+      "vk",
+      "gm",
+      "_",
+      "pictureimagefilter",
+      "vk",
+      "gm",
+      "_",
+      "resizeimagefilter",
+      "vk",
+      "gm",
+      "_",
+      "rotate_imagefilter",
+      "vk",
+      "gm",
+      "_",
+      "savelayer_lcdtext",
+      "vk",
+      "gm",
+      "_",
+      "srcmode",
+      "vk",
+      "gm",
+      "_",
+      "surfaceprops",
+      "vk",
+      "gm",
+      "_",
+      "textblobgeometrychange",
+      "vk",
+      "gm",
+      "_",
+      "textbloblooper",
+      "vk",
+      "gm",
+      "_",
+      "textblobmixedsizes",
+      "vk",
+      "gm",
+      "_",
+      "textblobmixedsizes_df",
+      "vk",
+      "gm",
+      "_",
+      "textblobrandomfont",
+      "vk",
+      "gm",
+      "_",
+      "textfilter_color",
+      "vk",
+      "gm",
+      "_",
+      "textfilter_image",
+      "vk",
+      "gm",
+      "_",
+      "typefacerenderingWin",
+      "vk",
+      "gm",
+      "_",
+      "varied_text_clipped_lcd",
+      "vk",
+      "gm",
+      "_",
+      "varied_text_ignorable_clip_lcd",
+      "vk",
+      "gm",
+      "_",
+      "xfermodeimagefilter",
+      "--match",
+      "~ApplyGamma",
+      "~ComposedImageFilterBounds_Gpu",
+      "~DeferredTextureImage",
+      "~GrMeshTest",
+      "~ImageFilterFailAffectsTransparentBlack_Gpu",
+      "~ImageFilterZeroBlurSigma_Gpu",
+      "~ImageNewShader_GPU",
+      "~NewTextureFromPixmap",
+      "~ReadPixels_Gpu",
+      "~ReadPixels_Texture",
+      "~ReadWriteAlpha",
+      "~SRGBReadWritePixels",
+      "~SpecialImage_DeferredGpu",
+      "~SpecialImage_Gpu",
+      "~WritePixels_Gpu",
+      "~WritePixelsNonTexture_Gpu",
+      "~XfermodeImageFilterCroppedInput_Gpu"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan.json
new file mode 100644
index 0000000..29e9b5e
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan.json
@@ -0,0 +1,419 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX660",
+      "extra_config",
+      "Vulkan",
+      "model",
+      "ShuttleA",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "vk",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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",
+      "_",
+      "test",
+      "_",
+      "SkImage_makeTextureImage"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE.json
new file mode 100644
index 0000000..3f523de
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE.json
@@ -0,0 +1,420 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX960",
+      "extra_config",
+      "ANGLE",
+      "model",
+      "ShuttleC",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "angle_d3d11_es2",
+      "angle_d3d9_es2",
+      "angle_gl_es2",
+      "angle_d3d11_es2_msaa8",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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",
+      "--match",
+      "~GLPrograms"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan.json
new file mode 100644
index 0000000..a1b77ea
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan.json
@@ -0,0 +1,419 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug_x64\\dm",
+      "--undefok",
+      "--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-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GTX1070",
+      "extra_config",
+      "Vulkan",
+      "model",
+      "ZBOX",
+      "os",
+      "Win10",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "vk",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "_",
+      "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",
+      "_",
+      "test",
+      "_",
+      "SkImage_makeTextureImage"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug.json b/infra/bots/recipes/test.expected/Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug.json
new file mode 100644
index 0000000..3eec115
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug.json
@@ -0,0 +1,636 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skp\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\skimage\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]\\skia\\infra\\bots\\assets\\svg\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]\\tmp\\SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]\\resources\\fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]\\skia\\infra\\bots\\.recipe_deps\\build\\scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]\\tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]\\out\\Debug\\dm",
+      "--undefok",
+      "--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-Win8-MSVC-Golo-CPU-AVX-x86-Debug",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "[START_DIR]\\svg",
+      "--key",
+      "arch",
+      "x86",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX",
+      "model",
+      "Golo",
+      "os",
+      "Win8",
+      "--uninterestingHashesFile",
+      "[START_DIR]\\tmp\\uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]\\dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--threads",
+      "4",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "verylargebitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "verylarge_picture_image",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "image",
+      "f16",
+      "_",
+      "_",
+      "image",
+      "_",
+      "abnormal.wbmp",
+      "_",
+      "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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]\\out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json b/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
new file mode 100644
index 0000000..6aa00b6
--- /dev/null
+++ b/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release.json
@@ -0,0 +1,1148 @@
+[
+  {
+    "cmd": [
+      "ios.py"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "setup_device"
+  },
+  {
+    "cmd": [
+      "ideviceinstaller",
+      "-i",
+      "[START_DIR]/out/Release/dm.app"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "install_dm"
+  },
+  {
+    "cmd": [
+      "ideviceinstaller",
+      "-i",
+      "[START_DIR]/out/Release/nanobench.app"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "install_nanobench"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skia/resources",
+      "resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skia/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm skps"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir skps"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skp",
+      "skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skp"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "tmp/SKP_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm images"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir images"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/skimage",
+      "images"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/skimage"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "tmp/SK_IMAGE_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_cat_file",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "cat_file tmp/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm tmp/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm svgs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir svgs"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_if_needed",
+      "[START_DIR]/svg",
+      "svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_if_needed [START_DIR]/svg"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "tmp/SVG_VERSION"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_rm",
+      "dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm dm"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_mkdir",
+      "dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_push_file",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push_file [START_DIR]/tmp/uninteresting_hashes.txt"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "name": "get swarming bot id",
+    "stdout": "/path/to/tmp/",
+    "~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"
+    ],
+    "env": {
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
+    },
+    "name": "get swarming task id",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "idevice-app-runner",
+      "-s",
+      "com.google.dm",
+      "--args",
+      "--undefok",
+      "--resourcePath",
+      "resources",
+      "--skps",
+      "skps",
+      "--images",
+      "images/dm",
+      "--colorImages",
+      "images/colorspace",
+      "--nameByHash",
+      "--properties",
+      "gitHash",
+      "abc123",
+      "builder",
+      "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release",
+      "swarming_bot_id",
+      "skia-bot-123",
+      "swarming_task_id",
+      "123456",
+      "--svgs",
+      "svgs",
+      "--key",
+      "arch",
+      "arm",
+      "compiler",
+      "Clang",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "GPU",
+      "cpu_or_gpu_value",
+      "GX6450",
+      "model",
+      "iPadMini4",
+      "os",
+      "iOS",
+      "--uninterestingHashesFile",
+      "tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "dm",
+      "--nocpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gles",
+      "glesdft",
+      "glessrgb",
+      "glesmsaa4",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "glessrgb",
+      "image",
+      "_",
+      "_",
+      "gles",
+      "skp",
+      "_",
+      "_",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "frame_larger_than_image.gif",
+      "_",
+      "image",
+      "gen_platf",
+      "inc0.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc1.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc2.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc3.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc4.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc5.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc6.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc7.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc8.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc9.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc10.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc11.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc12.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc13.png",
+      "_",
+      "image",
+      "gen_platf",
+      "inc14.png",
+      "_",
+      "test",
+      "_",
+      "GrShape",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "_",
+      "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"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/skia/platform_tools/ios/bin/ios_pull_if_needed",
+      "dm",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "IOS_BUNDLE_ID": "com.google.dm",
+      "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull_if_needed dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/failed_dm.json b/infra/bots/recipes/test.expected/failed_dm.json
new file mode 100644
index 0000000..a6d73f0
--- /dev/null
+++ b/infra/bots/recipes/test.expected/failed_dm.json
@@ -0,0 +1,579 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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]/out/Debug/dm",
+      "--undefok",
+      "--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-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
+      "swarming_bot_id",
+      "",
+      "swarming_task_id",
+      "",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "GCC",
+      "configuration",
+      "Debug",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "model",
+      "GCE",
+      "os",
+      "Ubuntu",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "symbolized dm",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Failed build steps: symbolized dm, symbolized dm",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/failed_get_hashes.json b/infra/bots/recipes/test.expected/failed_get_hashes.json
new file mode 100644
index 0000000..bcb9e35
--- /dev/null
+++ b/infra/bots/recipes/test.expected/failed_get_hashes.json
@@ -0,0 +1,854 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "get uninteresting hashes",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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@@@",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Android 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 --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~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@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/failed_pull.json b/infra/bots/recipes/test.expected/failed_pull.json
new file mode 100644
index 0000000..9b83ecb
--- /dev/null
+++ b/infra/bots/recipes/test.expected/failed_pull.json
@@ -0,0 +1,859 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SKP_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/skps"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skp",
+      "/sdcard/revenge_of_the_skiabot/skps"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skp/* /sdcard/revenge_of_the_skiabot/skps",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SKP_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SKP_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SKP_VERSION /sdcard/revenge_of_the_skiabot/SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/images"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skimage",
+      "/sdcard/revenge_of_the_skiabot/images"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skimage/* /sdcard/revenge_of_the_skiabot/images",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SK_IMAGE_VERSION /sdcard/revenge_of_the_skiabot/SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/SVG_VERSION",
+    "stdout": "/path/to/tmp/"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/svgs"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/svg",
+      "/sdcard/revenge_of_the_skiabot/svgs"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/svg/* /sdcard/revenge_of_the_skiabot/svgs",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/SVG_VERSION",
+      "/sdcard/revenge_of_the_skiabot/SVG_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/SVG_VERSION /sdcard/revenge_of_the_skiabot/SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/dm_out"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "adb",
+      "push",
+      "[START_DIR]/out/Debug/dm",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "set -x; /data/local/tmp/dm --undefok --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-Nexus10-GPU-MaliT604-arm-Debug-Android 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 MaliT604 extra_config Android model Nexus10 os Android --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --nocpu --randomProcessorTest --config 8888 srgb gles glesdft glessrgb glesmsaa4 serialize-8888 tiles_rt-8888 pic-8888 --src tests gm image colorImage svg --blacklist glessrgb image _ _ _ test _ GrShape serialize-8888 gm _ bleed_image serialize-8888 gm _ c_gms serialize-8888 gm _ colortype serialize-8888 gm _ colortype_xfermodes serialize-8888 gm _ drawfilter serialize-8888 gm _ fontmgr_bounds_0.75_0 serialize-8888 gm _ fontmgr_bounds_1_-0.25 serialize-8888 gm _ fontmgr_bounds serialize-8888 gm _ fontmgr_match serialize-8888 gm _ fontmgr_iter serialize-8888 gm _ imagemasksubset serialize-8888 gm _ bitmapfilters serialize-8888 gm _ bitmapshaders serialize-8888 gm _ bleed serialize-8888 gm _ bleed_alpha_bmp serialize-8888 gm _ bleed_alpha_bmp_shader serialize-8888 gm _ convex_poly_clip serialize-8888 gm _ extractalpha serialize-8888 gm _ filterbitmap_checkerboard_32_32_g8 serialize-8888 gm _ filterbitmap_image_mandrill_64 serialize-8888 gm _ shadows serialize-8888 gm _ simpleaaclip_aaclip serialize-8888 gm _ composeshader_bitmap serialize-8888 gm _ scaled_tilemodes_npot serialize-8888 gm _ scaled_tilemodes serialize-8888 gm _ typefacerendering_pfaMac serialize-8888 gm _ parsedpaths serialize-8888 gm _ ImageGeneratorExternal_rect serialize-8888 gm _ ImageGeneratorExternal_shader serialize-8888 gm _ shadow_utils serialize-8888 gm _ makecolorspace serialize-8888 gm _ bleed_alpha_image serialize-8888 gm _ bleed_alpha_image_shader serialize-8888 gm _ verylargebitmap serialize-8888 gm _ verylarge_picture_image pic-8888 gm _ drawfilter pic-8888 gm _ image-cacherator-from-picture serialize-8888 gm _ image-cacherator-from-picture pic-8888 gm _ image-cacherator-from-raster serialize-8888 gm _ image-cacherator-from-raster pic-8888 gm _ image-cacherator-from-ctable serialize-8888 gm _ image-cacherator-from-ctable pic-8888 gm _ gamut serialize-8888 gm _ gamut pic-8888 gm _ complexclip4_bw serialize-8888 gm _ complexclip4_bw pic-8888 gm _ complexclip4_aa serialize-8888 gm _ complexclip4_aa tiles_rt-8888 gm _ complexclip4_bw tiles_rt-8888 gm _ complexclip4_aa _ 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 --match ~CopySurface ~SRGBReadWritePixels; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/dm.sh"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "write dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "push",
+      "[START_DIR]/tmp/dm.sh",
+      "/data/local/tmp/"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push dm.sh"
+  },
+  {
+    "cmd": [
+      "adb",
+      "logcat",
+      "-c"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "clear log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['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"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@bin_dir = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@sh      = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@subprocess.check_call(['adb', 'shell', 'sh', bin_dir + sh])@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(int(subprocess.check_output(['adb', 'shell', 'cat',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                                        bin_dir + 'rc'])))@@@",
+      "@@@STEP_LOG_LINE@python.inline@except ValueError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print \"Couldn't read the return code.  Probably killed for OOM.\"@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "pull",
+      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "reason": "Infra Failure: Step('pull /sdcard/revenge_of_the_skiabot/dm_out [CUSTOM_[SWARM_OUT_DIR]]/dm') returned 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/failed_push.json b/infra/bots/recipes/test.expected/failed_push.json
new file mode 100644
index 0000000..690cd54
--- /dev/null
+++ b/infra/bots/recipes/test.expected/failed_push.json
@@ -0,0 +1,129 @@
+[
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nhost   = sys.argv[1]\ndevice = sys.argv[2]\nfor d, _, fs in os.walk(host):\n  p = os.path.relpath(d, host)\n  if p != '.' and p.startswith('.'):\n    continue\n  for f in fs:\n    print os.path.join(p,f)\n    subprocess.check_call(['adb', 'push',\n                           os.path.realpath(os.path.join(host, p, f)),\n                           os.path.join(device, p, f)])\n",
+      "[START_DIR]/skia/resources",
+      "/sdcard/revenge_of_the_skiabot/resources"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@host   = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@device = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@for d, _, fs in os.walk(host):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  p = os.path.relpath(d, host)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if p != '.' and p.startswith('.'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    continue@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in fs:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print os.path.join(p,f)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    subprocess.check_call(['adb', 'push',@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.realpath(os.path.join(host, p, f)),@@@",
+      "@@@STEP_LOG_LINE@python.inline@                           os.path.join(device, p, f)])@@@",
+      "@@@STEP_LOG_END@python.inline@@@",
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport subprocess\nimport sys\nout = sys.argv[1]\nlog = subprocess.check_output(['adb', 'logcat', '-d'])\nfor line in log.split('\\n'):\n  tokens = line.split()\n  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':\n    addr, path = tokens[-2:]\n    local = os.path.join(out, os.path.basename(path))\n    if os.path.exists(local):\n      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])\n      line = line.replace(addr, addr + ' ' + sym.strip())\n  print line\n",
+      "[START_DIR]/out/Debug"
+    ],
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "dump log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@out = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@log = subprocess.check_output(['adb', 'logcat', '-d'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@for line in log.split('\\n'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  tokens = line.split()@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':@@@",
+      "@@@STEP_LOG_LINE@python.inline@    addr, path = tokens[-2:]@@@",
+      "@@@STEP_LOG_LINE@python.inline@    local = os.path.join(out, os.path.basename(path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if os.path.exists(local):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])@@@",
+      "@@@STEP_LOG_LINE@python.inline@      line = line.replace(addr, addr + ' ' + sym.strip())@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print line@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "adb",
+      "shell",
+      "reboot",
+      "-p"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "shut down device to quarantine bot"
+  },
+  {
+    "cmd": [
+      "adb",
+      "kill-server"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "BUILDTYPE": "Debug",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "infra_step": true,
+    "name": "kill adb server"
+  },
+  {
+    "name": "$result",
+    "reason": "Infra Failure: Step('push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources') returned 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/trybot.json b/infra/bots/recipes/test.expected/trybot.json
new file mode 100644
index 0000000..c08e0d5
--- /dev/null
+++ b/infra/bots/recipes/test.expected/trybot.json
@@ -0,0 +1,634 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SKP VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SKP_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SKP_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded skimage VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SK_IMAGE_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SK_IMAGE_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get downloaded SVG VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "42",
+      "[START_DIR]/tmp/SVG_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write SVG_VERSION"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[skia::file]/resources/fileutil.py",
+      "rmtree",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm"
+    ],
+    "env": {
+      "PYTHONPATH": "[START_DIR]/skia/infra/bots/.recipe_deps/build/scripts"
+    },
+    "infra_step": true,
+    "name": "rmtree dm"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs dm",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\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[1], '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",
+      "[START_DIR]/tmp/uninteresting_hashes.txt"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "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 = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
+      "@@@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[1], '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",
+    "stdout": "/path/to/tmp/",
+    "~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",
+    "stdout": "/path/to/tmp/",
+    "~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": [
+      "[START_DIR]/out/Release_x64/dm",
+      "--undefok",
+      "--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-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
+      "issue",
+      "456789",
+      "patchset",
+      "12",
+      "patch_storage",
+      "gerrit",
+      "swarming_bot_id",
+      "",
+      "swarming_task_id",
+      "",
+      "--svgs",
+      "[START_DIR]/svg",
+      "--key",
+      "arch",
+      "x86_64",
+      "compiler",
+      "MSVC",
+      "configuration",
+      "Release",
+      "cpu_or_gpu",
+      "CPU",
+      "cpu_or_gpu_value",
+      "AVX2",
+      "model",
+      "GCE",
+      "os",
+      "Win2k8",
+      "--uninterestingHashesFile",
+      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "--writePath",
+      "[CUSTOM_[SWARM_OUT_DIR]]/dm",
+      "--nogpu",
+      "--randomProcessorTest",
+      "--config",
+      "8888",
+      "srgb",
+      "pdf",
+      "gl",
+      "gldft",
+      "glsrgb",
+      "glmsaa8",
+      "565",
+      "f16",
+      "sp-8888",
+      "2ndpic-8888",
+      "lite-8888",
+      "gbr-8888",
+      "serialize-8888",
+      "tiles_rt-8888",
+      "pic-8888",
+      "--src",
+      "tests",
+      "gm",
+      "image",
+      "colorImage",
+      "svg",
+      "--blacklist",
+      "f16",
+      "_",
+      "_",
+      "dstreadshuffle",
+      "glsrgb",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "image",
+      "_",
+      "_",
+      "gbr-8888",
+      "colorImage",
+      "_",
+      "_",
+      "_",
+      "image",
+      "gen_platf",
+      "rle8-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rle4-height-negative.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "pal8os2v2-16.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgba32abf.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24prof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "rgb24lprof.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "8bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "4bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "32bpp-pixeldata-cropped.bmp",
+      "_",
+      "image",
+      "gen_platf",
+      "24bpp-pixeldata-cropped.bmp",
+      "_",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "_",
+      "image",
+      "gen_platf",
+      "testimgari.jpg",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "c_gms",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype",
+      "serialize-8888",
+      "gm",
+      "_",
+      "colortype_xfermodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_0.75_0",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds_1_-0.25",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_bounds",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_match",
+      "serialize-8888",
+      "gm",
+      "_",
+      "fontmgr_iter",
+      "serialize-8888",
+      "gm",
+      "_",
+      "imagemasksubset",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapfilters",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bitmapshaders",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_bmp_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "convex_poly_clip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "extractalpha",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_checkerboard_32_32_g8",
+      "serialize-8888",
+      "gm",
+      "_",
+      "filterbitmap_image_mandrill_64",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadows",
+      "serialize-8888",
+      "gm",
+      "_",
+      "simpleaaclip_aaclip",
+      "serialize-8888",
+      "gm",
+      "_",
+      "composeshader_bitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes_npot",
+      "serialize-8888",
+      "gm",
+      "_",
+      "scaled_tilemodes",
+      "serialize-8888",
+      "gm",
+      "_",
+      "typefacerendering_pfaMac",
+      "serialize-8888",
+      "gm",
+      "_",
+      "parsedpaths",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_rect",
+      "serialize-8888",
+      "gm",
+      "_",
+      "ImageGeneratorExternal_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "shadow_utils",
+      "serialize-8888",
+      "gm",
+      "_",
+      "makecolorspace",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image",
+      "serialize-8888",
+      "gm",
+      "_",
+      "bleed_alpha_image_shader",
+      "serialize-8888",
+      "gm",
+      "_",
+      "verylargebitmap",
+      "serialize-8888",
+      "gm",
+      "_",
+      "verylarge_picture_image",
+      "sp-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "pic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "lite-8888",
+      "gm",
+      "_",
+      "drawfilter",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-picture",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-raster",
+      "sp-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "pic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "serialize-8888",
+      "gm",
+      "_",
+      "image-cacherator-from-ctable",
+      "sp-8888",
+      "gm",
+      "_",
+      "gamut",
+      "pic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "lite-8888",
+      "gm",
+      "_",
+      "gamut",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "gamut",
+      "serialize-8888",
+      "gm",
+      "_",
+      "gamut",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "sp-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "pic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "lite-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "2ndpic-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "serialize-8888",
+      "gm",
+      "_",
+      "complexclip4_aa",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_bw",
+      "tiles_rt-8888",
+      "gm",
+      "_",
+      "complexclip4_aa"
+    ],
+    "env": {
+      "BUILDTYPE": "Release_x64",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "dm"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.py b/infra/bots/recipes/test.py
new file mode 100644
index 0000000..5994eaf
--- /dev/null
+++ b/infra/bots/recipes/test.py
@@ -0,0 +1,924 @@
+# 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 test.
+
+
+DEPS = [
+  'core',
+  'env',
+  'file',
+  'flavor',
+  'recipe_engine/context',
+  'recipe_engine/json',
+  'recipe_engine/path',
+  'recipe_engine/platform',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'run',
+  'vars',
+]
+
+
+def dm_flags(bot):
+  args = []
+
+  # This enables non-deterministic random seeding of the GPU FP optimization
+  # test.
+  args.append('--randomProcessorTest')
+
+  # 32-bit desktop bots tend to run out of memory, because they have relatively
+  # far more cores than RAM (e.g. 32 cores, 3G RAM).  Hold them back a bit.
+  if '-x86-' in bot and not 'NexusPlayer' in bot:
+    args.extend('--threads 4'.split(' '))
+
+  # Avoid issues with dynamically exceeding resource cache limits.
+  if 'Test' in bot and 'DISCARDABLE' in bot:
+    args.extend('--threads 0'.split(' '))
+
+  # These are the canonical configs that we would ideally run on all bots. We
+  # may opt out or substitute some below for specific bots
+  configs = ['8888', 'srgb', 'pdf']
+  # Add in either gles or gl configs to the canonical set based on OS
+  sample_count = '8'
+  gl_prefix = 'gl'
+  if 'Android' in bot or 'iOS' in bot:
+    sample_count = '4'
+    # We want to test the OpenGL config not the GLES config on the Shield
+    if 'NVIDIA_Shield' not in bot:
+      gl_prefix = 'gles'
+  elif 'Intel' in bot:
+    sample_count = ''
+  elif 'ChromeOS' in bot:
+    gl_prefix = 'gles'
+
+  configs.extend([gl_prefix, gl_prefix + 'dft', gl_prefix + 'srgb'])
+  if sample_count is not '':
+    configs.append(gl_prefix + 'msaa' + sample_count)
+
+  # The NP produces a long error stream when we run with MSAA. The Tegra3 just
+  # doesn't support it.
+  if ('NexusPlayer' in bot or
+      'Tegra3'      in bot or
+      # We aren't interested in fixing msaa bugs on current iOS devices.
+      'iPad4' in bot or
+      'iPadPro' in bot or
+      'iPhone6' in bot or
+      'iPhone7' in bot or
+      # skia:5792
+      'IntelHD530'   in bot or
+      'IntelIris540' in bot):
+    configs = [x for x in configs if 'msaa' not in x]
+
+  # The NP produces different images for dft on every run.
+  if 'NexusPlayer' in bot:
+    configs = [x for x in configs if 'dft' not in x]
+
+  # Runs out of memory on Android bots.  Everyone else seems fine.
+  if 'Android' in bot:
+    configs.remove('pdf')
+
+  if '-GCE-' in bot:
+    configs.extend(['565'])
+    configs.extend(['f16'])
+    configs.extend(['sp-8888', '2ndpic-8888'])   # Test niche uses of SkPicture.
+    configs.extend(['lite-8888'])                # Experimental display list.
+    configs.extend(['gbr-8888'])
+
+  if '-TSAN' not in bot and sample_count is not '':
+    if ('TegraK1'  in bot or
+        'TegraX1'  in bot or
+        'GTX550Ti' in bot or
+        'GTX660'   in bot or
+        'GT610'    in bot):
+      configs.append(gl_prefix + 'nvprdit' + sample_count)
+
+  # We want to test both the OpenGL config and the GLES config on Linux Intel:
+  # GL is used by Chrome, GLES is used by ChromeOS.
+  if 'Intel' in bot and 'Ubuntu' in bot:
+    configs.extend(['gles', 'glesdft', 'glessrgb'])
+
+  # NP is running out of RAM when we run all these modes.  skia:3255
+  if 'NexusPlayer' not in bot:
+    configs.extend(mode + '-8888' for mode in
+                   ['serialize', 'tiles_rt', 'pic'])
+
+  # Test instanced rendering on a limited number of platforms
+  if 'Nexus6' in bot:
+    configs.append(gl_prefix + 'inst') # inst msaa isn't working yet on Adreno.
+  elif 'NVIDIA_Shield' in bot or 'PixelC' in bot:
+    # Multisampled instanced configs use nvpr so we substitute inst msaa
+    # configs for nvpr msaa configs.
+    old = gl_prefix + 'nvpr'
+    new = gl_prefix + 'inst'
+    configs = [x.replace(old, new) for x in configs]
+    # We also test non-msaa instanced.
+    configs.append(new)
+  elif 'MacMini6.2' in bot and sample_count is not '':
+    configs.extend([gl_prefix + 'inst', gl_prefix + 'inst' + sample_count])
+
+  # CommandBuffer bot *only* runs the command_buffer config.
+  if 'CommandBuffer' in bot:
+    configs = ['commandbuffer']
+
+  # ANGLE bot *only* runs the angle configs
+  if 'ANGLE' in bot:
+    configs = ['angle_d3d11_es2',
+               'angle_d3d9_es2',
+               'angle_gl_es2']
+    if sample_count is not '':
+      configs.append('angle_d3d11_es2_msaa' + sample_count)
+
+  # Vulkan bot *only* runs the vk config.
+  if 'Vulkan' in bot:
+    configs = ['vk']
+
+  if 'ChromeOS' in bot:
+    # Just run GLES for now - maybe add gles_msaa4 in the future
+    configs = ['gles']
+
+  if 'Ci20' in bot:
+    # This bot is really slow, cut it down to just 8888.
+    configs = ['8888']
+
+  # This bot only differs from vanilla CPU bots in 8888 config.
+  if 'SK_FORCE_RASTER_PIPELINE_BLITTER' in bot:
+    configs = ['8888', 'srgb']
+
+  args.append('--config')
+  args.extend(configs)
+
+  # Run tests, gms, and image decoding tests everywhere.
+  args.extend('--src tests gm image colorImage svg'.split(' '))
+  if 'Vulkan' in bot and 'NexusPlayer' in bot:
+    args.remove('svg')
+    args.remove('image')
+
+  # Eventually I'd like these to pass, but for now just skip 'em.
+  if 'SK_FORCE_RASTER_PIPELINE_BLITTER' in bot:
+    args.remove('tests')
+
+  blacklisted = []
+  def blacklist(quad):
+    config, src, options, name = quad.split(' ') if type(quad) is str else quad
+    if config == '_' or config in configs:
+      blacklisted.extend([config, src, options, name])
+
+  # TODO: ???
+  blacklist('f16 _ _ dstreadshuffle')
+  blacklist('glsrgb image _ _')
+  blacklist('glessrgb image _ _')
+
+  # Not any point to running these.
+  blacklist('gbr-8888 image _ _')
+  blacklist('gbr-8888 colorImage _ _')
+
+  if 'Valgrind' in bot:
+    # These take 18+ hours to run.
+    blacklist('pdf gm _ fontmgr_iter')
+    blacklist('pdf _ _ PANO_20121023_214540.jpg')
+    blacklist('pdf skp _ worldjournal')
+    blacklist('pdf skp _ desk_baidu.skp')
+    blacklist('pdf skp _ desk_wikipedia.skp')
+    blacklist('_ svg _ _')
+
+  if 'iOS' in bot:
+    blacklist(gl_prefix + ' skp _ _')
+
+  if 'Mac' in bot or 'iOS' in bot:
+    # CG fails on questionable bmps
+    blacklist('_ image gen_platf rgba32abf.bmp')
+    blacklist('_ image gen_platf rgb24prof.bmp')
+    blacklist('_ image gen_platf rgb24lprof.bmp')
+    blacklist('_ image gen_platf 8bpp-pixeldata-cropped.bmp')
+    blacklist('_ image gen_platf 4bpp-pixeldata-cropped.bmp')
+    blacklist('_ image gen_platf 32bpp-pixeldata-cropped.bmp')
+    blacklist('_ image gen_platf 24bpp-pixeldata-cropped.bmp')
+
+    # CG has unpredictable behavior on this questionable gif
+    # It's probably using uninitialized memory
+    blacklist('_ image gen_platf frame_larger_than_image.gif')
+
+    # CG has unpredictable behavior on incomplete pngs
+    # skbug.com/5774
+    blacklist('_ image gen_platf inc0.png')
+    blacklist('_ image gen_platf inc1.png')
+    blacklist('_ image gen_platf inc2.png')
+    blacklist('_ image gen_platf inc3.png')
+    blacklist('_ image gen_platf inc4.png')
+    blacklist('_ image gen_platf inc5.png')
+    blacklist('_ image gen_platf inc6.png')
+    blacklist('_ image gen_platf inc7.png')
+    blacklist('_ image gen_platf inc8.png')
+    blacklist('_ image gen_platf inc9.png')
+    blacklist('_ image gen_platf inc10.png')
+    blacklist('_ image gen_platf inc11.png')
+    blacklist('_ image gen_platf inc12.png')
+    blacklist('_ image gen_platf inc13.png')
+    blacklist('_ image gen_platf inc14.png')
+
+  # WIC fails on questionable bmps
+  if 'Win' in bot:
+    blacklist('_ image gen_platf rle8-height-negative.bmp')
+    blacklist('_ image gen_platf rle4-height-negative.bmp')
+    blacklist('_ image gen_platf pal8os2v2.bmp')
+    blacklist('_ image gen_platf pal8os2v2-16.bmp')
+    blacklist('_ image gen_platf rgba32abf.bmp')
+    blacklist('_ image gen_platf rgb24prof.bmp')
+    blacklist('_ image gen_platf rgb24lprof.bmp')
+    blacklist('_ image gen_platf 8bpp-pixeldata-cropped.bmp')
+    blacklist('_ image gen_platf 4bpp-pixeldata-cropped.bmp')
+    blacklist('_ image gen_platf 32bpp-pixeldata-cropped.bmp')
+    blacklist('_ image gen_platf 24bpp-pixeldata-cropped.bmp')
+    if 'x86_64' in bot and 'CPU' in bot:
+      # This GM triggers a SkSmallAllocator assert.
+      blacklist('_ gm _ composeshader_bitmap')
+
+  # WIC and CG fail on arithmetic jpegs
+  if 'Win' in bot or 'Mac' in bot:
+    blacklist('_ image gen_platf testimgari.jpg')
+
+  if 'Android' in bot or 'iOS' in bot:
+    # This test crashes the N9 (perhaps because of large malloc/frees). It also
+    # is fairly slow and not platform-specific. So we just disable it on all of
+    # Android and iOS. skia:5438
+    blacklist('_ test _ GrShape')
+
+  # skia:4095
+  bad_serialize_gms = ['bleed_image',
+                       'c_gms',
+                       'colortype',
+                       'colortype_xfermodes',
+                       'drawfilter',
+                       'fontmgr_bounds_0.75_0',
+                       'fontmgr_bounds_1_-0.25',
+                       'fontmgr_bounds',
+                       'fontmgr_match',
+                       'fontmgr_iter',
+                       'imagemasksubset']
+
+  # skia:5589
+  bad_serialize_gms.extend(['bitmapfilters',
+                            'bitmapshaders',
+                            'bleed',
+                            'bleed_alpha_bmp',
+                            'bleed_alpha_bmp_shader',
+                            'convex_poly_clip',
+                            'extractalpha',
+                            'filterbitmap_checkerboard_32_32_g8',
+                            'filterbitmap_image_mandrill_64',
+                            'shadows',
+                            'simpleaaclip_aaclip'])
+  # skia:5595
+  bad_serialize_gms.extend(['composeshader_bitmap',
+                            'scaled_tilemodes_npot',
+                            'scaled_tilemodes'])
+
+  # skia:5778
+  bad_serialize_gms.append('typefacerendering_pfaMac')
+  # skia:5942
+  bad_serialize_gms.append('parsedpaths')
+
+  # these use a custom image generator which doesn't serialize
+  bad_serialize_gms.append('ImageGeneratorExternal_rect')
+  bad_serialize_gms.append('ImageGeneratorExternal_shader')
+
+  # skia:6189
+  bad_serialize_gms.append('shadow_utils')
+
+  # Not expected to round trip encoding/decoding.
+  bad_serialize_gms.append('makecolorspace')
+
+  for test in bad_serialize_gms:
+    blacklist(['serialize-8888', 'gm', '_', test])
+
+  if 'Mac' not in bot:
+    for test in ['bleed_alpha_image', 'bleed_alpha_image_shader']:
+      blacklist(['serialize-8888', 'gm', '_', test])
+  # It looks like we skip these only for out-of-memory concerns.
+  if 'Win' in bot or 'Android' in bot:
+    for test in ['verylargebitmap', 'verylarge_picture_image']:
+      blacklist(['serialize-8888', 'gm', '_', test])
+
+  # skia:4769
+  for test in ['drawfilter']:
+    blacklist([    'sp-8888', 'gm', '_', test])
+    blacklist([   'pic-8888', 'gm', '_', test])
+    blacklist(['2ndpic-8888', 'gm', '_', test])
+    blacklist([  'lite-8888', 'gm', '_', test])
+  # skia:4703
+  for test in ['image-cacherator-from-picture',
+               'image-cacherator-from-raster',
+               'image-cacherator-from-ctable']:
+    blacklist([       'sp-8888', 'gm', '_', test])
+    blacklist([      'pic-8888', 'gm', '_', test])
+    blacklist([   '2ndpic-8888', 'gm', '_', test])
+    blacklist(['serialize-8888', 'gm', '_', test])
+
+  # GM that requires raster-backed canvas
+  for test in ['gamut', 'complexclip4_bw', 'complexclip4_aa']:
+    blacklist([       'sp-8888', 'gm', '_', test])
+    blacklist([      'pic-8888', 'gm', '_', test])
+    blacklist([     'lite-8888', 'gm', '_', test])
+    blacklist([   '2ndpic-8888', 'gm', '_', test])
+    blacklist(['serialize-8888', 'gm', '_', test])
+
+  # 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"]
+
+  # skbug.com/4888
+  # Blacklist RAW images (and a few large PNGs) on GPU bots
+  # until we can resolve failures.
+  if 'GPU' in bot:
+    blacklist('_ image _ interlaced1.png')
+    blacklist('_ image _ interlaced2.png')
+    blacklist('_ image _ interlaced3.png')
+    for raw_ext in r:
+      blacklist('_ image _ .%s' % raw_ext)
+
+  # Blacklist memory intensive tests on 32-bit bots.
+  if ('Win2k8' in bot or 'Win8' in bot) and 'x86-' in bot:
+    blacklist('_ image f16 _')
+    blacklist('_ image _ abnormal.wbmp')
+    blacklist('_ image _ interlaced1.png')
+    blacklist('_ image _ interlaced2.png')
+    blacklist('_ image _ interlaced3.png')
+    for raw_ext in r:
+      blacklist('_ image _ .%s' % raw_ext)
+
+  if 'IntelHD405' in bot and 'Ubuntu16' in bot:
+    # skia:6331
+    blacklist(['glmsaa8',   'image', 'gen_codec_gpu', 'abnormal.wbmp'])
+    blacklist(['glesmsaa4', 'image', 'gen_codec_gpu', 'abnormal.wbmp'])
+
+  if 'Nexus5' in bot:
+    # skia:5876
+    blacklist(['_', 'gm', '_', 'encode-platform'])
+
+  if 'AndroidOne-GPU' in bot:  # skia:4697, skia:4704, skia:4694, skia:4705
+    blacklist(['_',            'gm', '_', 'bigblurs'])
+    blacklist(['_',            'gm', '_', 'bleed'])
+    blacklist(['_',            'gm', '_', 'bleed_alpha_bmp'])
+    blacklist(['_',            'gm', '_', 'bleed_alpha_bmp_shader'])
+    blacklist(['_',            'gm', '_', 'bleed_alpha_image'])
+    blacklist(['_',            'gm', '_', 'bleed_alpha_image_shader'])
+    blacklist(['_',            'gm', '_', 'bleed_image'])
+    blacklist(['_',            'gm', '_', 'dropshadowimagefilter'])
+    blacklist(['_',            'gm', '_', 'filterfastbounds'])
+    blacklist([gl_prefix,      'gm', '_', 'imageblurtiled'])
+    blacklist(['_',            'gm', '_', 'imagefiltersclipped'])
+    blacklist(['_',            'gm', '_', 'imagefiltersscaled'])
+    blacklist(['_',            'gm', '_', 'imageresizetiled'])
+    blacklist(['_',            'gm', '_', 'matrixconvolution'])
+    blacklist(['_',            'gm', '_', 'strokedlines'])
+    if sample_count is not '':
+      gl_msaa_config = gl_prefix + 'msaa' + sample_count
+      blacklist([gl_msaa_config, 'gm', '_', 'imageblurtiled'])
+      blacklist([gl_msaa_config, 'gm', '_', 'imagefiltersbase'])
+
+  match = []
+  if 'Valgrind' in bot: # skia:3021
+    match.append('~Threaded')
+
+  if 'CommandBuffer' in bot:
+    # https://crbug.com/697030
+    match.append('~HalfFloatAlphaTextureTest')
+
+  if 'AndroidOne' in bot:  # skia:4711
+    match.append('~WritePixels')
+
+  if 'NexusPlayer' in bot:
+    match.append('~ResourceCache')
+
+  if 'Nexus10' in bot:
+    match.append('~CopySurface') # skia:5509
+    match.append('~SRGBReadWritePixels') # skia:6097
+
+  if 'GalaxyS6' in bot:
+    match.append('~SpecialImage') # skia:6338
+
+  if 'GalaxyS7_G930A' in bot:
+    match.append('~WritePixels') # skia:6427
+
+  if 'ANGLE' in bot and 'Debug' in bot:
+    match.append('~GLPrograms') # skia:4717
+
+  if 'MSAN' in bot:
+    match.extend(['~Once', '~Shared'])  # Not sure what's up with these tests.
+
+  if 'TSAN' in bot:
+    match.extend(['~ReadWriteAlpha'])   # Flaky on TSAN-covered on nvidia bots.
+    match.extend(['~RGBA4444TextureTest',  # Flakier than they are important.
+                  '~RGB565TextureTest'])
+
+  if 'Vulkan' in bot and 'Adreno530' in bot:
+      # skia:5777
+      match.extend(['~CopySurface'])
+
+  if 'Vulkan' in bot and 'NexusPlayer' in bot:
+    match.extend(['~gradients_no_texture$', # skia:6132
+                  '~tilemodes', # skia:6132
+                  '~shadertext$', # skia:6132
+                  '~bitmapfilters', # skia:6132
+                  '~GrContextFactory_abandon']) #skia:6209
+
+  if 'Vulkan' in bot and 'IntelIris540' in bot and 'Ubuntu' in bot:
+    match.extend(['~VkHeapTests']) # skia:6245
+
+  if 'Vulkan' in bot and 'IntelIris540' in bot and 'Win' in bot:
+    # skia:6398
+    blacklist(['vk', 'gm', '_', 'aarectmodes'])
+    blacklist(['vk', 'gm', '_', 'aaxfermodes'])
+    blacklist(['vk', 'gm', '_', 'arithmode'])
+    blacklist(['vk', 'gm', '_', 'composeshader_bitmap'])
+    blacklist(['vk', 'gm', '_', 'composeshader_bitmap2'])
+    blacklist(['vk', 'gm', '_', 'dftextCOLR'])
+    blacklist(['vk', 'gm', '_', 'drawregionmodes'])
+    blacklist(['vk', 'gm', '_', 'filterfastbounds'])
+    blacklist(['vk', 'gm', '_', 'fontcache'])
+    blacklist(['vk', 'gm', '_', 'fontmgr_iterWin10'])
+    blacklist(['vk', 'gm', '_', 'fontmgr_iter_factoryWin10'])
+    blacklist(['vk', 'gm', '_', 'fontmgr_matchWin10'])
+    blacklist(['vk', 'gm', '_', 'fontscalerWin'])
+    blacklist(['vk', 'gm', '_', 'fontscalerdistortable'])
+    blacklist(['vk', 'gm', '_', 'gammagradienttext'])
+    blacklist(['vk', 'gm', '_', 'gammatextWin'])
+    blacklist(['vk', 'gm', '_', 'gradtext'])
+    blacklist(['vk', 'gm', '_', 'hairmodes'])
+    blacklist(['vk', 'gm', '_', 'imagefilters_xfermodes'])
+    blacklist(['vk', 'gm', '_', 'imagefiltersclipped'])
+    blacklist(['vk', 'gm', '_', 'imagefiltersgraph'])
+    blacklist(['vk', 'gm', '_', 'imagefiltersscaled'])
+    blacklist(['vk', 'gm', '_', 'imagefiltersstroked'])
+    blacklist(['vk', 'gm', '_', 'imagefilterstransformed'])
+    blacklist(['vk', 'gm', '_', 'imageresizetiled'])
+    blacklist(['vk', 'gm', '_', 'lcdblendmodes'])
+    blacklist(['vk', 'gm', '_', 'lcdoverlap'])
+    blacklist(['vk', 'gm', '_', 'lcdtextWin'])
+    blacklist(['vk', 'gm', '_', 'lcdtextsize'])
+    blacklist(['vk', 'gm', '_', 'matriximagefilter'])
+    blacklist(['vk', 'gm', '_', 'mixedtextblobsCOLR'])
+    blacklist(['vk', 'gm', '_', 'pictureimagefilter'])
+    blacklist(['vk', 'gm', '_', 'resizeimagefilter'])
+    blacklist(['vk', 'gm', '_', 'rotate_imagefilter'])
+    blacklist(['vk', 'gm', '_', 'savelayer_lcdtext'])
+    blacklist(['vk', 'gm', '_', 'srcmode'])
+    blacklist(['vk', 'gm', '_', 'surfaceprops'])
+    blacklist(['vk', 'gm', '_', 'textblobgeometrychange'])
+    blacklist(['vk', 'gm', '_', 'textbloblooper'])
+    blacklist(['vk', 'gm', '_', 'textblobmixedsizes'])
+    blacklist(['vk', 'gm', '_', 'textblobmixedsizes_df'])
+    blacklist(['vk', 'gm', '_', 'textblobrandomfont'])
+    blacklist(['vk', 'gm', '_', 'textfilter_color'])
+    blacklist(['vk', 'gm', '_', 'textfilter_image'])
+    blacklist(['vk', 'gm', '_', 'typefacerenderingWin'])
+    blacklist(['vk', 'gm', '_', 'varied_text_clipped_lcd'])
+    blacklist(['vk', 'gm', '_', 'varied_text_ignorable_clip_lcd'])
+    blacklist(['vk', 'gm', '_', 'xfermodeimagefilter'])
+    match.append('~ApplyGamma')
+    match.append('~ComposedImageFilterBounds_Gpu')
+    match.append('~DeferredTextureImage')
+    match.append('~GrMeshTest')
+    match.append('~ImageFilterFailAffectsTransparentBlack_Gpu')
+    match.append('~ImageFilterZeroBlurSigma_Gpu')
+    match.append('~ImageNewShader_GPU')
+    match.append('~NewTextureFromPixmap')
+    match.append('~ReadPixels_Gpu')
+    match.append('~ReadPixels_Texture')
+    match.append('~ReadWriteAlpha')
+    match.append('~SRGBReadWritePixels')
+    match.append('~SpecialImage_DeferredGpu')
+    match.append('~SpecialImage_Gpu')
+    match.append('~WritePixels_Gpu')
+    match.append('~WritePixelsNonTexture_Gpu')
+    match.append('~XfermodeImageFilterCroppedInput_Gpu')
+
+  if 'IntelIris540' in bot and 'ANGLE' in bot:
+    match.append('~IntTexture') # skia:6086
+    blacklist(['_', 'gm', '_', 'discard']) # skia:6141
+    # skia:6103
+    for config in ['angle_d3d9_es2', 'angle_d3d11_es2', 'angle_gl_es2']:
+      blacklist([config, 'gm', '_', 'multipicturedraw_invpathclip_simple'])
+      blacklist([config, 'gm', '_', 'multipicturedraw_noclip_simple'])
+      blacklist([config, 'gm', '_', 'multipicturedraw_pathclip_simple'])
+      blacklist([config, 'gm', '_', 'multipicturedraw_rectclip_simple'])
+      blacklist([config, 'gm', '_', 'multipicturedraw_rrectclip_simple'])
+
+  if 'IntelBayTrail' in bot and 'Ubuntu' in bot:
+    match.append('~ImageStorageLoad') # skia:6358
+
+  if 'Ci20' in bot:
+    match.append('~Codec_Dimensions') # skia:6477
+    match.append('~FontMgrAndroidParser') # skia:6478
+    match.append('~PathOpsSimplify') # skia:6479
+    blacklist(['_', 'gm', '_', 'fast_slow_blurimagefilter']) # skia:6480
+
+  if ('Win10' in bot and 'Vulkan' in bot
+      and ('GTX1070' in bot or 'GTX660' in bot)):
+    blacklist('_ test _ SkImage_makeTextureImage') # skia:6554
+
+  if blacklisted:
+    args.append('--blacklist')
+    args.extend(blacklisted)
+
+  if match:
+    args.append('--match')
+    args.extend(match)
+
+  # These bots run out of memory running RAW codec tests. Do not run them in
+  # parallel
+  if ('NexusPlayer' in bot or 'Nexus5' in bot or 'Nexus9' in bot
+      or 'Win8-MSVC-ShuttleB' in bot):
+    args.append('--noRAW_threading')
+
+  if 'Valgrind' in bot and 'PreAbandonGpuContext' in bot:
+    args.append('--verbose')
+
+  return args
+
+
+def key_params(api):
+  """Build a unique key from the builder name (as a list).
+
+  E.g.  arch x86 gpu GeForce320M mode MacMini4.1 os Mac10.6
+  """
+  # Don't bother to include role, which is always Test.
+  # TryBots are uploaded elsewhere so they can use the same key.
+  blacklist = ['role', 'is_trybot']
+
+  flat = []
+  for k in sorted(api.vars.builder_cfg.keys()):
+    if k not in blacklist:
+      flat.append(k)
+      flat.append(api.vars.builder_cfg[k])
+  return flat
+
+
+def test_steps(api):
+  """Run the DM test."""
+  use_hash_file = False
+  if api.vars.upload_dm_results:
+    # This must run before we write anything into
+    # api.flavor.device_dirs.dm_dir or we may end up deleting our
+    # output on machines where they're the same.
+    api.flavor.create_clean_host_dir(api.vars.dm_dir)
+    host_dm_dir = str(api.vars.dm_dir)
+    device_dm_dir = str(api.flavor.device_dirs.dm_dir)
+    if host_dm_dir != device_dm_dir:
+      api.flavor.create_clean_device_dir(device_dm_dir)
+
+    # Obtain the list of already-generated hashes.
+    hash_filename = 'uninteresting_hashes.txt'
+
+    # Ensure that the tmp_dir exists.
+    api.run.run_once(api.file.makedirs,
+                           'tmp_dir',
+                           api.vars.tmp_dir,
+                           infra_step=True)
+
+    host_hashes_file = api.vars.tmp_dir.join(hash_filename)
+    hashes_file = api.flavor.device_path_join(
+        api.flavor.device_dirs.tmp_dir, hash_filename)
+    api.run(
+        api.python.inline,
+        'get uninteresting hashes',
+        program="""
+        import contextlib
+        import math
+        import socket
+        import sys
+        import time
+        import urllib2
+
+        HASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'
+        RETRIES = 5
+        TIMEOUT = 60
+        WAIT_BASE = 15
+
+        socket.setdefaulttimeout(TIMEOUT)
+        for retry in range(RETRIES):
+          try:
+            with contextlib.closing(
+                urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:
+              hashes = w.read()
+              with open(sys.argv[1], 'w') as f:
+                f.write(hashes)
+                break
+          except Exception as e:
+            print 'Failed to get uninteresting hashes from %s:' % HASHES_URL
+            print e
+            if retry == RETRIES:
+              raise
+            waittime = WAIT_BASE * math.pow(2, retry)
+            print 'Retry in %d seconds.' % waittime
+            time.sleep(waittime)
+        """,
+        args=[host_hashes_file],
+        abort_on_failure=False,
+        fail_build_on_failure=False,
+        infra_step=True)
+
+    if api.path.exists(host_hashes_file):
+      api.flavor.copy_file_to_device(host_hashes_file, hashes_file)
+      use_hash_file = True
+
+  # Run DM.
+  properties = [
+    'gitHash',      api.vars.got_revision,
+    'builder',      api.vars.builder_name,
+  ]
+  if api.vars.is_trybot:
+    properties.extend([
+      'issue',         api.vars.issue,
+      'patchset',      api.vars.patchset,
+      'patch_storage', api.vars.patch_storage,
+    ])
+  properties.extend(['swarming_bot_id', api.vars.swarming_bot_id])
+  properties.extend(['swarming_task_id', api.vars.swarming_task_id])
+
+  args = [
+    'dm',
+    '--undefok',   # This helps branches that may not know new flags.
+    '--resourcePath', api.flavor.device_dirs.resource_dir,
+    '--skps', api.flavor.device_dirs.skp_dir,
+    '--images', api.flavor.device_path_join(
+        api.flavor.device_dirs.images_dir, 'dm'),
+    '--colorImages', api.flavor.device_path_join(
+        api.flavor.device_dirs.images_dir, 'colorspace'),
+    '--nameByHash',
+    '--properties'
+  ] + properties
+
+  args.extend(['--svgs', api.flavor.device_dirs.svg_dir])
+
+  args.append('--key')
+  args.extend(key_params(api))
+  if use_hash_file:
+    args.extend(['--uninterestingHashesFile', hashes_file])
+  if api.vars.upload_dm_results:
+    args.extend(['--writePath', api.flavor.device_dirs.dm_dir])
+
+  skip_flag = None
+  if api.vars.builder_cfg.get('cpu_or_gpu') == 'CPU':
+    skip_flag = '--nogpu'
+  elif api.vars.builder_cfg.get('cpu_or_gpu') == 'GPU':
+    skip_flag = '--nocpu'
+  if skip_flag:
+    args.append(skip_flag)
+  args.extend(dm_flags(api.vars.builder_name))
+
+  env = {}
+  if 'Ubuntu16' in api.vars.builder_name:
+    # The vulkan in this asset name simply means that the graphics driver
+    # supports Vulkan. It is also the driver used for GL code.
+    dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_release')
+    if 'Debug' in api.vars.builder_name:
+      dri_path = api.vars.slave_dir.join('linux_vulkan_intel_driver_debug')
+
+    if 'Vulkan' in api.vars.builder_name:
+      sdk_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'bin')
+      lib_path = api.vars.slave_dir.join('linux_vulkan_sdk', 'lib')
+      env.update({
+        'PATH':'%%(PATH)s:%s' % sdk_path,
+        'LD_LIBRARY_PATH': '%s:%s' % (lib_path, dri_path),
+        'LIBGL_DRIVERS_PATH': dri_path,
+        'VK_ICD_FILENAMES':'%s' % dri_path.join('intel_icd.x86_64.json'),
+      })
+    else:
+      # Even the non-vulkan NUC jobs could benefit from the newer drivers.
+      env.update({
+        'LD_LIBRARY_PATH': dri_path,
+        'LIBGL_DRIVERS_PATH': dri_path,
+      })
+
+  # See skia:2789.
+  extra_config_parts = api.vars.builder_cfg.get('extra_config', '').split('_')
+  if 'AbandonGpuContext' in extra_config_parts:
+    args.append('--abandonGpuContext')
+  if 'PreAbandonGpuContext' in extra_config_parts:
+    args.append('--preAbandonGpuContext')
+  if 'ReleaseAndAbandonGpuContext' in extra_config_parts:
+    args.append('--releaseAndAbandonGpuContext')
+
+  with api.env(env):
+    api.run(api.flavor.step, 'dm', cmd=args, abort_on_failure=False)
+
+  if api.vars.upload_dm_results:
+    # Copy images and JSON to host machine if needed.
+    api.flavor.copy_directory_contents_to_host(
+        api.flavor.device_dirs.dm_dir, api.vars.dm_dir)
+
+
+def RunSteps(api):
+  api.core.setup()
+  env = {}
+  if 'iOS' in api.vars.builder_name:
+    env['IOS_BUNDLE_ID'] = 'com.google.dm'
+    env['IOS_MOUNT_POINT'] = api.vars.slave_dir.join('mnt_iosdevice')
+  with api.context(env=env):
+    try:
+      api.flavor.install_everything()
+      test_steps(api)
+    finally:
+      api.flavor.cleanup_steps()
+    api.run.check_failure()
+
+
+TEST_BUILDERS = [
+  'Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android',
+  'Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android',
+  'Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android',
+  'Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android',
+  'Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android',
+  'Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android',
+  'Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android',
+  'Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan',
+  'Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan',
+  'Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android',
+  'Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Release-Android',
+  'Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan',
+  'Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android',
+  'Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug',
+  'Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug',
+  'Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer',
+  'Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN',
+  'Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN',
+  'Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN',
+  'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug',
+  'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug',
+  'Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind',
+  ('Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind' +
+   '_AbandonGpuContext'),
+  ('Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind' +
+   '_PreAbandonGpuContext'),
+  ('Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_' +
+    'SCALEDIMAGECACHE'),
+  'Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug',
+  'Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan',
+  'Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release',
+  'Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug',
+  'Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug',
+  'Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan',
+  ('Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-'
+   'ReleaseAndAbandonGpuContext'),
+  'Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE',
+  'Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan',
+  'Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan',
+  'Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE',
+  'Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan',
+  'Test-iOS-Clang-iPadMini4-GPU-GX6450-arm-Release',
+  ('Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-'
+   'SK_FORCE_RASTER_PIPELINE_BLITTER'),
+]
+
+
+def GenTests(api):
+  for builder in TEST_BUILDERS:
+    test = (
+      api.test(builder) +
+      api.properties(buildername=builder,
+                     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',
+                                     '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')
+      ) +
+      api.step_data('get swarming bot id',
+          stdout=api.raw_io.output('skia-bot-123')) +
+      api.step_data('get swarming task id',
+          stdout=api.raw_io.output('123456'))
+    )
+    if 'Win' in builder:
+      test += api.platform('win', 64)
+
+    if 'ChromeOS' in builder:
+      test += api.step_data(
+          'read chromeos ip',
+          stdout=api.raw_io.output('{"user_ip":"foo@127.0.0.1"}'))
+
+
+    yield test
+
+  builder = 'Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release'
+  yield (
+    api.test('trybot') +
+    api.properties(buildername=builder,
+                   revision='abc123',
+                   path_config='kitchen',
+                   swarm_out_dir='[SWARM_OUT_DIR]') +
+    api.properties(patch_storage='gerrit') +
+    api.properties.tryserver(
+          buildername=builder,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )+
+    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')
+    )
+  )
+
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
+  yield (
+    api.test('failed_dm') +
+    api.properties(buildername=builder,
+                   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',
+                                     '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')
+    ) +
+    api.step_data('symbolized dm', retcode=1)
+  )
+
+  builder = 'Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android'
+  yield (
+    api.test('failed_get_hashes') +
+    api.properties(buildername=builder,
+                   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',
+                                     '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')
+    ) +
+    api.step_data('get uninteresting hashes', retcode=1)
+  )
+
+  builder = 'Test-Android-Clang-NexusPlayer-CPU-SSE4-x86-Debug-Android'
+  yield (
+    api.test('failed_push') +
+    api.properties(buildername=builder,
+                   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',
+                                     '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')
+    ) +
+    api.step_data('push [START_DIR]/skia/resources/* '+
+                  '/sdcard/revenge_of_the_skiabot/resources', retcode=1)
+  )
+
+  builder = 'Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android'
+  yield (
+    api.test('failed_pull') +
+    api.properties(buildername=builder,
+                   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',
+                                     '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')
+    ) +
+    api.step_data('dm', retcode=1) +
+    api.step_data('pull /sdcard/revenge_of_the_skiabot/dm_out '+
+                  '[CUSTOM_[SWARM_OUT_DIR]]/dm', retcode=1)
+  )
diff --git a/infra/bots/recipes/update_meta_config.expected/Housekeeper-Nightly-UpdateMetaConfig.json b/infra/bots/recipes/update_meta_config.expected/Housekeeper-Nightly-UpdateMetaConfig.json
new file mode 100644
index 0000000..a93e5b6
--- /dev/null
+++ b/infra/bots/recipes/update_meta_config.expected/Housekeeper-Nightly-UpdateMetaConfig.json
@@ -0,0 +1,191 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'update_meta_config.git_cookies'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_meta_config_git_cookies'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
+    ],
+    "name": "download update_meta_config.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_meta_config.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_meta_config_git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
+      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/update_meta_config.py",
+      "--repo_name",
+      "skia",
+      "--tasks_json",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tasks.json",
+      "--gitcookies",
+      "[HOME]/update_meta_config.git_cookies"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Update meta/config"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\n\n\nTOKEN_FILE = 'update_meta_config.git_cookies'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
+    ],
+    "name": "cleanup update_meta_config.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_meta_config.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/update_meta_config.expected/failed_update.json b/infra/bots/recipes/update_meta_config.expected/failed_update.json
new file mode 100644
index 0000000..aa97971
--- /dev/null
+++ b/infra/bots/recipes/update_meta_config.expected/failed_update.json
@@ -0,0 +1,196 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport urllib2\n\nTOKEN_FILE = 'update_meta_config.git_cookies'\nTOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_meta_config_git_cookies'\n\nreq = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})\ncontents = urllib2.urlopen(req).read()\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\n\nwith open(token_file, 'w') as f:\n  f.write(contents)\n"
+    ],
+    "name": "download update_meta_config.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_meta_config.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_URL = 'http://metadata/computeMetadata/v1/project/attributes/update_meta_config_git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@req = urllib2.Request(TOKEN_URL, headers={'Metadata-Flavor': 'Google'})@@@",
+      "@@@STEP_LOG_LINE@python.inline@contents = urllib2.urlopen(req).read()@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@with open(token_file, 'w') as f:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  f.write(contents)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/update_meta_config.py",
+      "--repo_name",
+      "skia",
+      "--tasks_json",
+      "[CUSTOM_/_B_WORK]/skia/infra/bots/tasks.json",
+      "--gitcookies",
+      "[HOME]/update_meta_config.git_cookies"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]/skia",
+    "env": {
+      "BUILDTYPE": "Release",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]",
+      "SKIA_OUT": "[START_DIR]/out"
+    },
+    "name": "Update meta/config",
+    "~followup_annotations": [
+      "step returned non-zero exit code: 1",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\n\n\nTOKEN_FILE = 'update_meta_config.git_cookies'\n\n\nhome = os.path.expanduser('~')\ntoken_file = os.path.join(home, TOKEN_FILE)\nif os.path.isfile(token_file):\n  os.remove(token_file)\n"
+    ],
+    "name": "cleanup update_meta_config.git_cookies",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@TOKEN_FILE = 'update_meta_config.git_cookies'@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@home = os.path.expanduser('~')@@@",
+      "@@@STEP_LOG_LINE@python.inline@token_file = os.path.join(home, TOKEN_FILE)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if os.path.isfile(token_file):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.remove(token_file)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Step('Update meta/config') failed with return_code 1",
+    "recipe_result": null,
+    "status_code": 1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/update_meta_config.expected/trybot_test.json b/infra/bots/recipes/update_meta_config.expected/trybot_test.json
new file mode 100644
index 0000000..c6fd37a
--- /dev/null
+++ b/infra/bots/recipes/update_meta_config.expected/trybot_test.json
@@ -0,0 +1,127 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[CUSTOM_/_B_WORK]",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs checkout_path",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[CUSTOM_/_B_WORK]/.gclient_entries"
+    ],
+    "infra_step": true,
+    "name": "remove [CUSTOM_/_B_WORK]/.gclient_entries",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
+      "--spec",
+      "cache_dir = '[CUSTOM_/_B_CACHE]'\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",
+      "[CUSTOM_/_B_CACHE]",
+      "--gerrit_repo",
+      "https://skia.googlesource.com/skia.git",
+      "--gerrit_ref",
+      "refs/changes/23/123/3",
+      "--output_json",
+      "/path/to/tmp/json",
+      "--revision",
+      "skia@abc123"
+    ],
+    "cwd": "[CUSTOM_/_B_WORK]",
+    "env": {
+      "GIT_HTTP_LOW_SPEED_LIMIT": "1000",
+      "GIT_HTTP_LOW_SPEED_TIME": "300",
+      "PATH": "<PATH>:RECIPE_PACKAGE_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@  \"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 sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp_dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "reason": "Uncaught Exception: Exception('Cannot run update_meta_config recipe as a trybot',)",
+    "status_code": -1
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/update_meta_config.py b/infra/bots/recipes/update_meta_config.py
new file mode 100644
index 0000000..a3ca4f6
--- /dev/null
+++ b/infra/bots/recipes/update_meta_config.py
@@ -0,0 +1,88 @@
+# 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 the Bot that updates meta config."""
+
+
+DEPS = [
+  'depot_tools/gclient',
+  'file',
+  'recipe_engine/context',
+  'recipe_engine/path',
+  'recipe_engine/properties',
+  'recipe_engine/python',
+  'recipe_engine/raw_io',
+  'recipe_engine/step',
+  'core',
+  'infra',
+  'run',
+  'vars',
+]
+
+
+TEST_BUILDERS = {
+  'client.skia.compile': {
+    'skiabot-linux-swarm-000': [
+      'Housekeeper-Nightly-UpdateMetaConfig',
+    ],
+  },
+}
+
+
+UPDATE_META_CONFIG_GITCOOKIES_FILE = 'update_meta_config.git_cookies'
+UPDATE_META_CONFIG_KEY = 'update_meta_config_git_cookies'
+
+
+def RunSteps(api):
+  api.core.setup()
+
+  if api.vars.is_trybot:
+    raise Exception('Cannot run update_meta_config recipe as a trybot')
+  update_meta_config_gitcookies = api.path.join(
+      api.path.expanduser('~'), UPDATE_META_CONFIG_GITCOOKIES_FILE)
+  repo_name = api.properties.get('repository').split('/')[-1].rstrip('.git')
+  cmd = ['python',
+         api.vars.skia_dir.join('infra', 'bots', 'update_meta_config.py'),
+         '--repo_name', repo_name,
+         '--tasks_json', api.vars.skia_dir.join('infra', 'bots', 'tasks.json'),
+         '--gitcookies', str(update_meta_config_gitcookies)]
+  with api.infra.MetadataFetch(
+      api, UPDATE_META_CONFIG_KEY, UPDATE_META_CONFIG_GITCOOKIES_FILE):
+    with api.context(cwd=api.vars.skia_dir):
+      api.run(api.step, 'Update meta/config', cmd=cmd)
+
+
+def GenTests(api):
+  builder = 'Housekeeper-Nightly-UpdateMetaConfig'
+  yield (
+      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]')
+  )
+
+  yield (
+      api.test('failed_update') +
+      api.properties(buildername=builder,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]') +
+      api.step_data('Update meta/config', retcode=1)
+  )
+
+  yield (
+      api.test('trybot_test') +
+      api.properties(buildername=builder,
+                     repository='https://skia.googlesource.com/skia.git',
+                     revision='abc123',
+                     path_config='kitchen',
+                     swarm_out_dir='[SWARM_OUT_DIR]',
+                     patch_issue=123,
+                     patch_set=3) +
+      api.expect_exception('Exception')
+  )
diff --git a/infra/bots/recipe_modules/upload_dm_results/example.expected/failed_all.json b/infra/bots/recipes/upload_dm_results.expected/failed_all.json
similarity index 100%
rename from infra/bots/recipe_modules/upload_dm_results/example.expected/failed_all.json
rename to infra/bots/recipes/upload_dm_results.expected/failed_all.json
diff --git a/infra/bots/recipe_modules/upload_dm_results/example.expected/failed_once.json b/infra/bots/recipes/upload_dm_results.expected/failed_once.json
similarity index 100%
rename from infra/bots/recipe_modules/upload_dm_results/example.expected/failed_once.json
rename to infra/bots/recipes/upload_dm_results.expected/failed_once.json
diff --git a/infra/bots/recipes/upload_dm_results.expected/upload.json b/infra/bots/recipes/upload_dm_results.expected/normal_bot.json
similarity index 100%
rename from infra/bots/recipes/upload_dm_results.expected/upload.json
rename to infra/bots/recipes/upload_dm_results.expected/normal_bot.json
diff --git a/infra/bots/recipes/upload_dm_results.expected/trybot.json b/infra/bots/recipes/upload_dm_results.expected/trybot.json
new file mode 100644
index 0000000..529b0d8
--- /dev/null
+++ b/infra/bots/recipes/upload_dm_results.expected/trybot.json
@@ -0,0 +1,113 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport sys, os\npath = sys.argv[1]\nmode = int(sys.argv[2])\nif not os.path.isdir(path):\n  if os.path.exists(path):\n    print \"%s exists but is not a dir\" % path\n    sys.exit(1)\n  os.makedirs(path, mode)\n",
+      "[START_DIR]/tmp_upload",
+      "511"
+    ],
+    "infra_step": true,
+    "name": "makedirs tmp dir",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys, os@@@",
+      "@@@STEP_LOG_LINE@python.inline@path = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@mode = int(sys.argv[2])@@@",
+      "@@@STEP_LOG_LINE@python.inline@if not os.path.isdir(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if os.path.exists(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print \"%s exists but is not a dir\" % path@@@",
+      "@@@STEP_LOG_LINE@python.inline@    sys.exit(1)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(path, mode)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/dm/dm.json",
+      "[START_DIR]/tmp_upload"
+    ],
+    "name": "copy dm.json"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/dm/verbose.log",
+      "[START_DIR]/tmp_upload"
+    ],
+    "name": "copy verbose.log"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[START_DIR]/dm/dm.json"
+    ],
+    "name": "rm old dm.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport os\nimport sys\nos.remove(sys.argv[1])\n",
+      "[START_DIR]/dm/verbose.log"
+    ],
+    "name": "rm old verbose.log",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@os.remove(sys.argv[1])@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
+      "/path/to/tmp/",
+      "[START_DIR]/dm/*"
+    ],
+    "infra_step": true,
+    "name": "find images"
+  },
+  {
+    "cmd": [
+      "gsutil",
+      "cp",
+      "[START_DIR]/dm/*",
+      "gs://skia-infra-gm/dm-images-v1"
+    ],
+    "name": "upload images"
+  },
+  {
+    "cmd": [
+      "gsutil",
+      "cp",
+      "-z",
+      "json,log",
+      "[START_DIR]/tmp_upload/*",
+      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/1337000001/456789/12"
+    ],
+    "name": "upload JSON and logs"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/upload_dm_results.py b/infra/bots/recipes/upload_dm_results.py
index c161225..d919f59 100644
--- a/infra/bots/recipes/upload_dm_results.py
+++ b/infra/bots/recipes/upload_dm_results.py
@@ -6,21 +6,140 @@
 # Recipe for uploading DM results.
 
 
+import calendar
+
+
 DEPS = [
+  'file',
+  'recipe_engine/json',
+  'recipe_engine/path',
   'recipe_engine/properties',
-  'upload_dm_results',
+  'recipe_engine/shutil',
+  'recipe_engine/step',
+  'recipe_engine/time',
 ]
 
 
+DM_JSON = 'dm.json'
+UPLOAD_ATTEMPTS = 5
+VERBOSE_LOG = 'verbose.log'
+
+
+def cp(api, name, src, dst, extra_args=None):
+  cmd = ['gsutil', 'cp']
+  if extra_args:
+    cmd.extend(extra_args)
+  cmd.extend([src, dst])
+
+  name = 'upload %s' % name
+  for i in xrange(UPLOAD_ATTEMPTS):
+    step_name = name
+    if i > 0:
+      step_name += ' (attempt %d)' % (i+1)
+    try:
+      api.step(step_name, cmd=cmd)
+      break
+    except api.step.StepFailure:
+      if i == UPLOAD_ATTEMPTS - 1:
+        raise
+
+
 def RunSteps(api):
-  api.upload_dm_results.run()
+  builder_name = api.properties['buildername']
+  revision = api.properties['revision']
+
+  results_dir = api.path['start_dir'].join('dm')
+
+  # Move dm.json and verbose.log to their own directory.
+  json_file = results_dir.join(DM_JSON)
+  log_file = results_dir.join(VERBOSE_LOG)
+  tmp_dir = api.path['start_dir'].join('tmp_upload')
+  api.shutil.makedirs('tmp dir', tmp_dir, infra_step=True)
+  api.shutil.copy('copy dm.json', json_file, tmp_dir)
+  api.shutil.copy('copy verbose.log', log_file, tmp_dir)
+  api.shutil.remove('rm old dm.json', json_file)
+  api.shutil.remove('rm old verbose.log', log_file)
+
+  # Upload the images.
+  image_dest_path = 'gs://%s/dm-images-v1' % api.properties['gs_bucket']
+  files_to_upload = api.file.glob(
+      'find images',
+      results_dir.join('*'),
+      test_data=[results_dir.join('someimage.png')],
+      infra_step=True)
+  if len(files_to_upload) > 0:
+    cp(api, 'images', results_dir.join('*'), image_dest_path)
+
+  # Upload the JSON summary and verbose.log.
+  now = api.time.utcnow()
+  summary_dest_path = '/'.join([
+      'dm-json-v1',
+      str(now.year ).zfill(4),
+      str(now.month).zfill(2),
+      str(now.day  ).zfill(2),
+      str(now.hour ).zfill(2),
+      revision,
+      builder_name,
+      str(int(calendar.timegm(now.utctimetuple())))])
+
+  # Trybot results are further siloed by issue/patchset.
+  issue = api.properties.get('patch_issue')
+  patchset = api.properties.get('patch_set')
+  if issue and patchset:
+    summary_dest_path = '/'.join((
+        'trybot', summary_dest_path, str(issue), str(patchset)))
+
+  summary_dest_path = 'gs://%s/%s' % (api.properties['gs_bucket'],
+                                      summary_dest_path)
+
+  cp(api, 'JSON and logs', tmp_dir.join('*'), summary_dest_path,
+     ['-z', 'json,log'])
 
 
 def GenTests(api):
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
   yield (
-    api.test('upload') +
-    api.properties(buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug',
+    api.test('normal_bot') +
+    api.properties(buildername=builder,
                    gs_bucket='skia-infra-gm',
                    revision='abc123',
                    path_config='kitchen')
   )
+
+  yield (
+    api.test('failed_once') +
+    api.properties(buildername=builder,
+                   gs_bucket='skia-infra-gm',
+                   revision='abc123',
+                   path_config='kitchen') +
+    api.step_data('upload images', retcode=1)
+  )
+
+  yield (
+    api.test('failed_all') +
+    api.properties(buildername=builder,
+                   gs_bucket='skia-infra-gm',
+                   revision='abc123',
+                   path_config='kitchen') +
+    api.step_data('upload images', retcode=1) +
+    api.step_data('upload images (attempt 2)', retcode=1) +
+    api.step_data('upload images (attempt 3)', retcode=1) +
+    api.step_data('upload images (attempt 4)', retcode=1) +
+    api.step_data('upload images (attempt 5)', retcode=1)
+  )
+
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
+  yield (
+      api.test('trybot') +
+      api.properties(
+          buildername=builder,
+          gs_bucket='skia-infra-gm',
+          revision='abc123',
+          path_config='kitchen',
+          patch_storage='gerrit') +
+      api.properties.tryserver(
+          buildername=builder,
+          gerrit_project='skia',
+          gerrit_url='https://skia-review.googlesource.com/',
+      )
+  )
diff --git a/infra/bots/recipes/upload_nano_results.expected/upload.json b/infra/bots/recipes/upload_nano_results.expected/normal_bot.json
similarity index 100%
rename from infra/bots/recipes/upload_nano_results.expected/upload.json
rename to infra/bots/recipes/upload_nano_results.expected/normal_bot.json
diff --git a/infra/bots/recipes/upload_nano_results.expected/trybot.json b/infra/bots/recipes/upload_nano_results.expected/trybot.json
new file mode 100644
index 0000000..a601095
--- /dev/null
+++ b/infra/bots/recipes/upload_nano_results.expected/trybot.json
@@ -0,0 +1,31 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport glob\nimport sys\nwith open(sys.argv[1], 'w') as f:\n  f.write('\\n'.join(glob.glob(sys.argv[2])))\n",
+      "/path/to/tmp/",
+      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/data/*.json"
+    ],
+    "cwd": "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/data",
+    "infra_step": true,
+    "name": "find results"
+  },
+  {
+    "cmd": [
+      "gsutil",
+      "cp",
+      "-z",
+      "json",
+      "[START_DIR]/perfdata/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/data/nanobench_abc123.json",
+      "gs://skia-perf/trybot/nano-json-v1/2012/05/14/12/Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug/456789/12/nanobench_abc123.json"
+    ],
+    "infra_step": true,
+    "name": "upload"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipes/upload_nano_results.py b/infra/bots/recipes/upload_nano_results.py
index 4929efa..1bf4ef1 100644
--- a/infra/bots/recipes/upload_nano_results.py
+++ b/infra/bots/recipes/upload_nano_results.py
@@ -7,20 +7,73 @@
 
 
 DEPS = [
+  'file',
+  'recipe_engine/context',
+  'recipe_engine/path',
   'recipe_engine/properties',
-  'upload_nano_results',
+  'recipe_engine/step',
+  'recipe_engine/time',
 ]
 
 
 def RunSteps(api):
-  api.upload_nano_results.run()
+  # Upload the nanobench resuls.
+  builder_name = api.properties['buildername']
+
+  now = api.time.utcnow()
+  src_path = api.path['start_dir'].join(
+      'perfdata', builder_name, 'data')
+  with api.context(cwd=src_path):
+    results = api.file.glob(
+        'find results',
+        src_path.join('*.json'),
+        test_data=[src_path.join('nanobench_abc123.json')],
+        infra_step=True)
+  if len(results) != 1:  # pragma: nocover
+    raise Exception('Unable to find nanobench or skpbench JSON file!')
+
+  src = results[0]
+  basename = api.path.basename(src)
+  gs_path = '/'.join((
+      'nano-json-v1', str(now.year).zfill(4),
+      str(now.month).zfill(2), str(now.day).zfill(2), str(now.hour).zfill(2),
+      builder_name))
+
+  issue = api.properties.get('patch_issue')
+  patchset = api.properties.get('patch_set')
+  if issue and patchset:
+    gs_path = '/'.join(('trybot', gs_path, str(issue), str(patchset)))
+
+  dst = '/'.join((
+      'gs://%s' % api.properties['gs_bucket'], gs_path, basename))
+
+  api.step(
+      'upload',
+      cmd=['gsutil', 'cp', '-z', 'json', src, dst],
+      infra_step=True)
 
 
 def GenTests(api):
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
   yield (
-    api.test('upload') +
-    api.properties(buildername='Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug',
+    api.test('normal_bot') +
+    api.properties(buildername=builder,
                    gs_bucket='skia-perf',
                    revision='abc123',
                    path_config='kitchen')
   )
+
+  builder = 'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug'
+  yield (
+    api.test('trybot') +
+    api.properties(buildername=builder,
+                   gs_bucket='skia-perf',
+                   revision='abc123',
+                   path_config='kitchen',
+                   patch_storage='gerrit') +
+    api.properties.tryserver(
+        buildername=builder,
+        gerrit_project='skia',
+        gerrit_url='https://skia-review.googlesource.com/',
+    )
+  )
diff --git a/infra/bots/skpbench_skia.isolate b/infra/bots/skpbench_skia.isolate
index f506012..008aaa7 100644
--- a/infra/bots/skpbench_skia.isolate
+++ b/infra/bots/skpbench_skia.isolate
@@ -8,4 +8,4 @@
       '../../tools/skpbench/',
     ],
   },
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/infra/bots/skpbench_skia_bundled.isolate b/infra/bots/skpbench_skia_bundled.isolate
new file mode 100644
index 0000000..5b1ec15
--- /dev/null
+++ b/infra/bots/skpbench_skia_bundled.isolate
@@ -0,0 +1,12 @@
+{
+  'includes': [
+    'assets.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../../.gclient',
+      '../../tools/valgrind.supp',
+      '../../tools/skpbench/',
+    ],
+  },
+}
diff --git a/infra/bots/skpbench_skia_bundled_unix.isolate b/infra/bots/skpbench_skia_bundled_unix.isolate
new file mode 100644
index 0000000..c396b30
--- /dev/null
+++ b/infra/bots/skpbench_skia_bundled_unix.isolate
@@ -0,0 +1,6 @@
+{
+  'includes': [
+    'skpbench_skia_bundled.isolate',
+    'swarm_recipe_bundled_unix.isolate',
+  ],
+}
diff --git a/infra/bots/skpbench_skia_bundled_win.isolate b/infra/bots/skpbench_skia_bundled_win.isolate
new file mode 100644
index 0000000..befba1b
--- /dev/null
+++ b/infra/bots/skpbench_skia_bundled_win.isolate
@@ -0,0 +1,6 @@
+{
+  'includes': [
+    'skpbench_skia_bundled.isolate',
+    'swarm_recipe_bundled_win.isolate',
+  ],
+}
diff --git a/infra/bots/swarm_recipe.isolate b/infra/bots/swarm_recipe.isolate
index 9e8f516..c21a321 100644
--- a/infra/bots/swarm_recipe.isolate
+++ b/infra/bots/swarm_recipe.isolate
@@ -4,7 +4,7 @@
   ],
   'variables': {
     'command': [
-      'python', 'recipes.py', 'run', '--timestamps',
+      'python', 'recipes.py', '--package', '../config/recipes.cfg', 'run', '--timestamps',
     ],
     'files': [
       '../config/recipes.cfg',
diff --git a/infra/bots/swarm_recipe_bundled_unix.isolate b/infra/bots/swarm_recipe_bundled_unix.isolate
new file mode 100644
index 0000000..731b369
--- /dev/null
+++ b/infra/bots/swarm_recipe_bundled_unix.isolate
@@ -0,0 +1,7 @@
+{
+  'variables': {
+    'command': [
+      '../../../recipe_bundle/recipes', 'run', '--timestamps',
+    ],
+  },
+}
diff --git a/infra/bots/swarm_recipe_bundled_win.isolate b/infra/bots/swarm_recipe_bundled_win.isolate
new file mode 100644
index 0000000..19932fe
--- /dev/null
+++ b/infra/bots/swarm_recipe_bundled_win.isolate
@@ -0,0 +1,7 @@
+{
+  'variables': {
+    'command': [
+      '../../../recipe_bundle/recipes.bat', 'run', '--timestamps',
+    ],
+  },
+}
diff --git a/infra/bots/tasks.json b/infra/bots/tasks.json
index 6ee2708..5d22648 100644
--- a/infra/bots/tasks.json
+++ b/infra/bots/tasks.json
@@ -66,12 +66,24 @@
         "Build-Ubuntu-Clang-arm-Debug-Android"
       ]
     },
+    "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES": {
+      "priority": 0.8,
+      "tasks": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES"
+      ]
+    },
     "Build-Ubuntu-Clang-arm-Release-Android": {
       "priority": 0.8,
       "tasks": [
         "Build-Ubuntu-Clang-arm-Release-Android"
       ]
     },
+    "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES": {
+      "priority": 0.8,
+      "tasks": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES"
+      ]
+    },
     "Build-Ubuntu-Clang-arm64-Debug-Android": {
       "priority": 0.8,
       "tasks": [
@@ -210,6 +222,24 @@
         "Build-Ubuntu-Clang-x86_64-Release-Mini"
       ]
     },
+    "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2": {
+      "priority": 0.8,
+      "tasks": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2"
+      ]
+    },
+    "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41": {
+      "priority": 0.8,
+      "tasks": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ]
+    },
+    "Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "priority": 0.8,
+      "tasks": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER"
+      ]
+    },
     "Build-Ubuntu-Clang-x86_64-Release-TSAN": {
       "priority": 0.8,
       "tasks": [
@@ -421,18 +451,49 @@
       ],
       "trigger": "nightly"
     },
+    "Housekeeper-Nightly-UpdateMetaConfig": {
+      "priority": 0.8,
+      "tasks": [
+        "Housekeeper-Nightly-UpdateMetaConfig"
+      ],
+      "trigger": "nightly"
+    },
     "Housekeeper-PerCommit": {
       "priority": 0.8,
       "tasks": [
         "Housekeeper-PerCommit"
       ]
     },
+    "Housekeeper-PerCommit-BundleRecipes": {
+      "priority": 0.8,
+      "tasks": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ]
+    },
     "Housekeeper-PerCommit-InfraTests": {
       "priority": 0.8,
       "tasks": [
         "Housekeeper-PerCommit-InfraTests"
       ]
     },
+    "Housekeeper-PerCommit-IsolateSKP": {
+      "priority": 0.8,
+      "tasks": [
+        "Housekeeper-PerCommit-IsolateSKP"
+      ]
+    },
+    "Housekeeper-PerCommit-IsolateSVG": {
+      "priority": 0.8,
+      "tasks": [
+        "Housekeeper-PerCommit-IsolateSVG"
+      ]
+    },
+    "Housekeeper-PerCommit-IsolateSkImage": {
+      "priority": 0.8,
+      "tasks": [
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ]
+    },
     "Housekeeper-Weekly-RecreateSKPs": {
       "priority": 0.8,
       "tasks": [
@@ -452,18 +513,6 @@
         "Upload-Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android"
       ]
     },
-    "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android": {
-      "priority": 0.8,
-      "tasks": [
-        "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android"
-      ]
-    },
-    "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android": {
-      "priority": 0.8,
-      "tasks": [
-        "Upload-Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android"
-      ]
-    },
     "Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android": {
       "priority": 0.8,
       "tasks": [
@@ -620,12 +669,24 @@
         "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android"
       ]
     },
+    "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan"
+      ]
+    },
     "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android": {
       "priority": 0.8,
       "tasks": [
         "Upload-Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android"
       ]
     },
+    "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan"
+      ]
+    },
     "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android": {
       "priority": 0.8,
       "tasks": [
@@ -674,28 +735,64 @@
         "Upload-Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench"
       ]
     },
-    "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug": {
+    "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug"
+        "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug"
       ]
     },
-    "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release": {
+    "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release"
+        "Upload-Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release"
       ]
     },
-    "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug": {
+    "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug"
+        "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug"
       ]
     },
-    "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release": {
+    "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release"
+        "Upload-Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release"
+      ]
+    },
+    "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug"
+      ]
+    },
+    "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release"
+      ]
+    },
+    "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug"
+      ]
+    },
+    "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release"
+      ]
+    },
+    "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug"
+      ]
+    },
+    "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release"
       ]
     },
     "Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug": {
@@ -710,22 +807,22 @@
         "Upload-Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release"
       ]
     },
-    "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug": {
+    "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug"
+        "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug"
       ]
     },
-    "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release": {
+    "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release"
+        "Upload-Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release"
       ]
     },
-    "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer": {
+    "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer"
+        "Upload-Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer"
       ]
     },
     "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug": {
@@ -764,6 +861,12 @@
         "Upload-Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast"
       ]
     },
+    "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER"
+      ]
+    },
     "Perf-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Debug-ASAN": {
       "priority": 0.8,
       "tasks": [
@@ -896,6 +999,18 @@
         "Upload-Perf-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release"
       ]
     },
+    "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug"
+      ]
+    },
+    "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release"
+      ]
+    },
     "Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
@@ -998,6 +1113,30 @@
         "Upload-Perf-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release"
       ]
     },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug"
+      ]
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE"
+      ]
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release"
+      ]
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE"
+      ]
+    },
     "Perf-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
@@ -1130,16 +1269,88 @@
         "Upload-Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release"
       ]
     },
-    "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug": {
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug"
+        "Perf-Win7-MSVC-Golo-CPU-AVX-x86-Debug"
       ]
     },
-    "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release": {
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release"
+        "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug"
+      ]
+    },
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI"
+      ]
+    },
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ]
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Win8-MSVC-Golo-CPU-AVX-x86-Debug"
+      ]
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug"
+      ]
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI"
+      ]
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ]
+    },
+    "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug"
+      ]
+    },
+    "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release"
+      ]
+    },
+    "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug"
+      ]
+    },
+    "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release"
+      ]
+    },
+    "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug"
+      ]
+    },
+    "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release"
       ]
     },
     "Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android": {
@@ -1154,16 +1365,16 @@
         "Upload-Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android"
       ]
     },
-    "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android": {
+    "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android"
+        "Upload-Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android"
       ]
     },
-    "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android": {
+    "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android"
+        "Upload-Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android"
       ]
     },
     "Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android": {
@@ -1202,12 +1413,6 @@
         "Upload-Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android"
       ]
     },
-    "Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android": {
-      "priority": 0.8,
-      "tasks": [
-        "Upload-Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android"
-      ]
-    },
     "Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android": {
       "priority": 0.8,
       "tasks": [
@@ -1388,16 +1593,52 @@
         "Upload-Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android_Vulkan"
       ]
     },
-    "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug": {
+    "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug"
+        "Upload-Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug"
       ]
     },
-    "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release": {
+    "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release"
+        "Upload-Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release"
+      ]
+    },
+    "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug"
+      ]
+    },
+    "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release"
+      ]
+    },
+    "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug"
+      ]
+    },
+    "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release"
+      ]
+    },
+    "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug"
+      ]
+    },
+    "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release"
       ]
     },
     "Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug": {
@@ -1412,22 +1653,22 @@
         "Upload-Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release"
       ]
     },
-    "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug": {
+    "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug"
+        "Upload-Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug"
       ]
     },
-    "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer": {
+    "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer"
+        "Upload-Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer"
       ]
     },
-    "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release": {
+    "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release"
+        "Upload-Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release"
       ]
     },
     "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug": {
@@ -1466,6 +1707,24 @@
         "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast"
       ]
     },
+    "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2"
+      ]
+    },
+    "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ]
+    },
+    "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER"
+      ]
+    },
     "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN": {
       "priority": 0.8,
       "tasks": [
@@ -1539,6 +1798,12 @@
         "Upload-Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD"
       ]
     },
+    "Test-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-PreAbandonGpuContext": {
+      "priority": 0.8,
+      "tasks": [
+        "Test-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-PreAbandonGpuContext"
+      ]
+    },
     "Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind": {
       "priority": 0.8,
       "tasks": [
@@ -1629,6 +1894,18 @@
         "Upload-Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release"
       ]
     },
+    "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug"
+      ]
+    },
+    "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release"
+      ]
+    },
     "Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
@@ -1683,6 +1960,12 @@
         "Upload-Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ANGLE"
       ]
     },
+    "Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext": {
+      "priority": 0.8,
+      "tasks": [
+        "Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext"
+      ]
+    },
     "Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
@@ -1731,6 +2014,30 @@
         "Upload-Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release"
       ]
     },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug"
+      ]
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE"
+      ]
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release"
+      ]
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE"
+      ]
+    },
     "Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug": {
       "priority": 0.8,
       "tasks": [
@@ -1869,16 +2176,112 @@
         "Upload-Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release-GDI"
       ]
     },
-    "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug": {
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug"
+        "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug"
       ]
     },
-    "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release": {
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86-Release": {
       "priority": 0.8,
       "tasks": [
-        "Upload-Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release"
+        "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86-Release"
+      ]
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug"
+      ]
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI"
+      ]
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ]
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI"
+      ]
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug"
+      ]
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86-Release"
+      ]
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug"
+      ]
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI"
+      ]
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ]
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI"
+      ]
+    },
+    "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug"
+      ]
+    },
+    "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release"
+      ]
+    },
+    "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug"
+      ]
+    },
+    "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release"
+      ]
+    },
+    "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug"
+      ]
+    },
+    "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release": {
+      "priority": 0.8,
+      "tasks": [
+        "Upload-Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release"
       ]
     }
   },
@@ -1892,15 +2295,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-arm-Debug-iOS",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -1917,15 +2317,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-arm-Release-iOS",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -1949,15 +2346,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -1974,15 +2368,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-arm64-Debug-iOS",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -1999,15 +2390,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-arm64-Release-iOS",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2024,15 +2412,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-x64-Release-iOS",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2049,15 +2434,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2074,15 +2456,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-x86_64-Debug-CommandBuffer",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2099,15 +2478,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2124,15 +2500,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Mac-Clang-x86_64-Release-CommandBuffer",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2157,15 +2530,52 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "compile_skia.isolate",
+      "priority": 0.8
+    },
+    "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:8"
+        },
+        {
+          "name": "skia/bots/armhf_sysroot",
+          "path": "armhf_sysroot",
+          "version": "version:3"
+        },
+        {
+          "name": "skia/bots/chromebook_arm_gles",
+          "path": "chromebook_arm_gles",
+          "version": "version:0"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "compile",
+        "repository=<(REPO)",
+        "buildername=Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2190,15 +2600,52 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "compile_skia.isolate",
+      "priority": 0.8
+    },
+    "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:8"
+        },
+        {
+          "name": "skia/bots/armhf_sysroot",
+          "path": "armhf_sysroot",
+          "version": "version:3"
+        },
+        {
+          "name": "skia/bots/chromebook_arm_gles",
+          "path": "chromebook_arm_gles",
+          "version": "version:0"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "compile",
+        "repository=<(REPO)",
+        "buildername=Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2223,15 +2670,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2256,15 +2700,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm64-Debug-Android_FrameworkDefs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2289,15 +2730,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2322,15 +2760,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2355,15 +2790,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2388,15 +2820,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-mips64el-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2421,15 +2850,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-mips64el-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2454,15 +2880,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-mipsel-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2487,15 +2910,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-mipsel-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2520,15 +2940,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2553,15 +2970,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2586,15 +3000,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2619,15 +3030,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2652,15 +3060,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2685,15 +3090,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2706,7 +3108,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2718,15 +3120,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2739,7 +3138,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2751,15 +3150,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Debug-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2772,7 +3168,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2784,15 +3180,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Debug-MSAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2805,7 +3198,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -2822,15 +3215,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2843,7 +3233,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2855,15 +3245,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2876,7 +3263,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2888,15 +3275,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Release-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2909,7 +3293,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2921,15 +3305,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Release-Fast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2942,7 +3323,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2954,15 +3335,102 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Release-Mini",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "compile_skia.isolate",
+      "priority": 0.8
+    },
+    "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:8"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "compile",
+        "repository=<(REPO)",
+        "buildername=Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "compile_skia.isolate",
+      "priority": 0.8
+    },
+    "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:8"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "compile",
+        "repository=<(REPO)",
+        "buildername=Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "compile_skia.isolate",
+      "priority": 0.8
+    },
+    "Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:8"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "compile",
+        "repository=<(REPO)",
+        "buildername=Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -2975,7 +3443,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dimensions": [
@@ -2987,15 +3455,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Release-TSAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3008,7 +3473,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -3025,15 +3490,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-Clang-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3046,7 +3508,12 @@
         {
           "name": "skia/bots/cast_toolchain",
           "path": "cast_toolchain",
-          "version": "version:2"
+          "version": "version:4"
+        },
+        {
+          "name": "skia/bots/chromebook_arm_gles",
+          "path": "chromebook_arm_gles",
+          "version": "version:0"
         }
       ],
       "dimensions": [
@@ -3058,15 +3525,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-arm-Debug-Chromecast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3079,7 +3543,12 @@
         {
           "name": "skia/bots/cast_toolchain",
           "path": "cast_toolchain",
-          "version": "version:2"
+          "version": "version:4"
+        },
+        {
+          "name": "skia/bots/chromebook_arm_gles",
+          "path": "chromebook_arm_gles",
+          "version": "version:0"
         }
       ],
       "dimensions": [
@@ -3091,15 +3560,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-arm-Release-Chromecast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3117,15 +3583,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3143,15 +3606,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3169,15 +3629,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3195,15 +3652,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Debug-NoGPU",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3221,15 +3675,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3247,15 +3698,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3273,15 +3721,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3306,15 +3751,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-Flutter_Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3332,15 +3774,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-Mesa",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3358,15 +3797,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-NoGPU",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3384,15 +3820,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-PDFium",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3410,15 +3843,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-PDFium_SkiaPaths",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3436,15 +3866,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-SKNX_NO_SIMD",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3462,15 +3889,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Ubuntu-GCC-x86_64-Release-Shared",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3494,15 +3918,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-Clang-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3526,15 +3947,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3558,15 +3976,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3590,15 +4005,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Debug-Exceptions",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3622,15 +4034,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Debug-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3654,15 +4063,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3686,15 +4092,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3718,15 +4121,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86-Release-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3750,15 +4150,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3782,15 +4179,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3814,15 +4208,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Debug-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3851,15 +4242,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3883,15 +4271,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3915,15 +4300,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3947,15 +4329,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Release-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -3984,15 +4363,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_compile",
+        "compile",
         "repository=<(REPO)",
         "buildername=Build-Win-MSVC-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -4001,6 +4377,13 @@
       "priority": 0.8
     },
     "Housekeeper-Nightly-RecreateSKPs_Canary": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/go",
+          "path": "go",
+          "version": "version:2"
+        }
+      ],
       "dimensions": [
         "cpu:x86-64-avx2",
         "gpu:none",
@@ -4011,15 +4394,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_RecreateSKPs",
+        "recreate_skps",
         "repository=<(REPO)",
         "buildername=Housekeeper-Nightly-RecreateSKPs_Canary",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -4028,12 +4408,35 @@
       "isolate": "compile_skia.isolate",
       "priority": 0.8
     },
+    "Housekeeper-Nightly-UpdateMetaConfig": {
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "update_meta_config",
+        "repository=<(REPO)",
+        "buildername=Housekeeper-Nightly-UpdateMetaConfig",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "isolate": "meta_config.isolate",
+      "priority": 0.8
+    },
     "Housekeeper-PerCommit": {
       "cipd_packages": [
         {
           "name": "skia/bots/go",
           "path": "go",
-          "version": "version:1"
+          "version": "version:2"
         }
       ],
       "dependencies": [
@@ -4048,15 +4451,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_housekeeper",
+        "housekeeper",
         "repository=<(REPO)",
         "buildername=Housekeeper-PerCommit",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -4064,7 +4464,19 @@
       "isolate": "housekeeper_skia.isolate",
       "priority": 0.8
     },
-    "Housekeeper-PerCommit-InfraTests": {
+    "Housekeeper-PerCommit-BundleRecipes": {
+      "cipd_packages": [
+        {
+          "name": "infra/git/${platform}",
+          "path": "git",
+          "version": "version:2.13.0.chromium9"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "git",
+          "version": "git_revision:a78b5f3658c0578a017db48df97d20ac09822bcd"
+        }
+      ],
       "dimensions": [
         "cpu:x86-64-avx2",
         "gpu:none",
@@ -4074,15 +4486,36 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_infra",
+        "bundle_recipes",
+        "buildername=Housekeeper-PerCommit-BundleRecipes",
+        "swarm_out_dir=${ISOLATED_OUTDIR}"
+      ],
+      "isolate": "bundle_recipes.isolate",
+      "priority": 0.7
+    },
+    "Housekeeper-PerCommit-InfraTests": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/go",
+          "path": "go",
+          "version": "version:2"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "infra",
         "repository=<(REPO)",
         "buildername=Housekeeper-PerCommit-InfraTests",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -4090,7 +4523,65 @@
       "isolate": "infra_skia.isolate",
       "priority": 0.8
     },
+    "Housekeeper-PerCommit-IsolateSKP": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "isolate": "isolate_skp.isolate",
+      "priority": 0.7
+    },
+    "Housekeeper-PerCommit-IsolateSVG": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "isolate": "isolate_svg.isolate",
+      "priority": 0.7
+    },
+    "Housekeeper-PerCommit-IsolateSkImage": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        }
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "isolate": "isolate_skimage.isolate",
+      "priority": 0.7
+    },
     "Housekeeper-Weekly-RecreateSKPs": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/go",
+          "path": "go",
+          "version": "version:2"
+        }
+      ],
       "dimensions": [
         "cpu:x86-64-avx2",
         "gpu:none",
@@ -4101,15 +4592,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_RecreateSKPs",
+        "recreate_skps",
         "repository=<(REPO)",
         "buildername=Housekeeper-Weekly-RecreateSKPs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -4119,25 +4607,12 @@
       "priority": 0.8
     },
     "Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MOB30Q",
@@ -4150,44 +4625,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MOB30Q",
@@ -4200,144 +4659,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
-      "max_attempts": 1,
-      "priority": 0.8
-    },
-    "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
-      "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
-      ],
-      "dimensions": [
-        "device_os:MMB29M",
-        "device_type:j5xnlte",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "swarm_perf",
-        "repository=<(REPO)",
-        "buildername=Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)"
-      ],
-      "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
-      "max_attempts": 1,
-      "priority": 0.8
-    },
-    "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
-      "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
-      ],
-      "dimensions": [
-        "device_os:MMB29M",
-        "device_type:j5xnlte",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "swarm_perf",
-        "repository=<(REPO)",
-        "buildername=Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)"
-      ],
-      "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MMB29K",
@@ -4350,44 +4693,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MMB29K",
@@ -4400,47 +4727,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NRD90M_G930AUCU4BQA6",
+        "device_os:NRD90M_G930AUCS4BQC2",
         "device_type:heroqlteatt",
         "os:Android",
         "pool:Skia"
@@ -4450,47 +4761,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NRD90M_G930AUCU4BQA6",
+        "device_os:NRD90M_G930AUCS4BQC2",
         "device_type:heroqlteatt",
         "os:Android",
         "pool:Skia"
@@ -4500,44 +4795,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NRD90M_G930FXXU1DQAS",
@@ -4550,44 +4829,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NRD90M_G930FXXU1DQAS",
@@ -4600,47 +4863,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MPJ24.139-64",
+        "device_os:NPJ25.93-14",
         "device_type:athene",
         "os:Android",
         "pool:Skia"
@@ -4650,47 +4897,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MPJ24.139-64",
+        "device_os:NPJ25.93-14",
         "device_type:athene",
         "os:Android",
         "pool:Skia"
@@ -4700,47 +4931,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -4750,47 +4965,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -4800,47 +4999,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -4850,47 +5033,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -4900,44 +5067,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -4950,44 +5101,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -5000,44 +5135,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -5050,44 +5169,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -5100,44 +5203,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:M4B30Z",
@@ -5150,44 +5237,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:M4B30Z",
@@ -5200,47 +5271,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -5250,47 +5305,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -5300,47 +5339,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -5350,47 +5373,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -5400,44 +5407,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY47V",
@@ -5450,44 +5441,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY47V",
@@ -5500,47 +5475,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Debug-Android"
+        "Build-Ubuntu-Clang-x86-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -5550,47 +5509,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Debug-Android"
+        "Build-Ubuntu-Clang-x86-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -5600,47 +5543,65 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:OPP2.170420.017",
+        "device_type:fugu",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Release-Android"
+        "Build-Ubuntu-Clang-x86-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -5650,44 +5611,62 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:OPP2.170420.017",
+        "device_type:fugu",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -5700,44 +5679,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -5750,44 +5713,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -5800,44 +5747,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -5850,47 +5781,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26H",
+        "device_os:N2G47D",
         "device_type:dragon",
         "os:Android",
         "pool:Skia"
@@ -5900,47 +5815,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26H",
+        "device_os:N2G47D",
         "device_type:dragon",
         "os:Android",
         "pool:Skia"
@@ -5950,37 +5849,29 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP"
       ],
       "dimensions": [
-        "device_os:NMF26H",
+        "device_os:N2G47D",
         "device_type:dragon",
         "os:Android",
         "pool:Skia"
@@ -5990,37 +5881,29 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_skpbench",
+        "skpbench",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "skpbench_skia.isolate",
+      "isolate": "skpbench_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP"
       ],
       "dimensions": [
-        "device_os:NMF26H",
+        "device_os:N2G47D",
         "device_type:dragon",
         "os:Android",
         "pool:Skia"
@@ -6030,34 +5913,290 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_skpbench",
+        "skpbench",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "skpbench_skia.isolate",
+      "isolate": "skpbench_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        }
-      ],
+    "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug": {
       "dependencies": [
-        "Build-Ubuntu-GCC-arm-Debug-Chromecast"
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT604",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT604",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT860",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT860",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT764",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT764",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:TegraK1",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:TegraK1",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-GCC-arm-Debug-Chromecast",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP"
       ],
       "dimensions": [
         "device_os:1.24_82923",
@@ -6070,34 +6209,26 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        }
-      ],
+    "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release": {
       "dependencies": [
-        "Build-Ubuntu-GCC-arm-Release-Chromecast"
+        "Build-Ubuntu-GCC-arm-Release-Chromecast",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP"
       ],
       "dimensions": [
         "device_os:1.24_82923",
@@ -6110,119 +6241,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
-      "max_attempts": 1,
-      "priority": 0.8
-    },
-    "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
-      "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug"
-      ],
-      "dimensions": [
-        "gpu:10de:08a4",
-        "os:Mac-10.11",
-        "pool:Skia"
-      ],
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "swarm_perf",
-        "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)"
-      ],
-      "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
-      "max_attempts": 1,
-      "priority": 0.8
-    },
-    "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
-      "dependencies": [
-        "Build-Mac-Clang-x86_64-Release"
-      ],
-      "dimensions": [
-        "gpu:10de:08a4",
-        "os:Mac-10.11",
-        "pool:Skia"
-      ],
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "swarm_perf",
-        "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)"
-      ],
-      "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6231,12 +6261,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6245,7 +6275,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug"
+        "Build-Mac-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -6258,21 +6289,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6281,12 +6309,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6295,7 +6323,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Release"
+        "Build-Mac-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -6308,35 +6337,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug": {
+    "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6345,7 +6371,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug"
+        "Build-Mac-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0a2e",
@@ -6357,35 +6384,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release": {
+    "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6394,7 +6418,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Release"
+        "Build-Mac-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0a2e",
@@ -6406,35 +6431,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer": {
+    "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6443,7 +6465,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Release-CommandBuffer"
+        "Build-Mac-Clang-x86_64-Release-CommandBuffer",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0a2e",
@@ -6455,21 +6478,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6478,12 +6498,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6492,7 +6512,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6505,21 +6526,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6528,12 +6546,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6543,11 +6561,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Debug-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6560,21 +6579,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6583,12 +6599,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6598,11 +6614,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-MSAN"
+        "Build-Ubuntu-Clang-x86_64-Debug-MSAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6615,21 +6632,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6638,12 +6652,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6652,7 +6666,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6665,21 +6680,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6688,12 +6700,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6703,11 +6715,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Release-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6720,21 +6733,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6743,12 +6753,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6757,7 +6767,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-Fast"
+        "Build-Ubuntu-Clang-x86_64-Release-Fast",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6770,21 +6781,66 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6793,12 +6849,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6808,11 +6864,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Debug-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -6824,21 +6881,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Debug-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6847,12 +6901,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6862,11 +6916,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Release-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -6878,21 +6933,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Release-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6901,12 +6953,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6915,7 +6967,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86-Debug"
+        "Build-Ubuntu-GCC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6928,21 +6981,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -6951,12 +7001,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -6965,7 +7015,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug"
+        "Build-Ubuntu-GCC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -6978,21 +7029,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7001,12 +7049,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7015,7 +7063,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+        "Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -7028,21 +7077,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7051,12 +7097,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7065,7 +7111,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -7078,21 +7125,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7104,15 +7148,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_ct_skps",
+        "ct_skps",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-CT_BENCH_1k_SKPs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -7129,15 +7170,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_ct_skps",
+        "ct_skps",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-CT_BENCH_1k_SKPs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -7151,21 +7189,27 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
           "path": "svg",
           "version": "version:5"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1244",
@@ -7177,21 +7221,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 3600000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7200,21 +7241,27 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
           "path": "svg",
           "version": "version:5"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1244",
@@ -7226,21 +7273,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 3600000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7249,12 +7293,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7263,7 +7307,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug"
+        "Build-Ubuntu-GCC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
@@ -7275,21 +7320,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7298,12 +7340,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7312,7 +7354,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
@@ -7324,21 +7367,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7347,12 +7387,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7361,7 +7401,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug"
+        "Build-Ubuntu-GCC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
@@ -7373,21 +7414,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7396,12 +7434,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7410,7 +7448,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
@@ -7422,21 +7461,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7445,12 +7481,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7460,11 +7496,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:22b1",
@@ -7476,21 +7513,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7499,12 +7533,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7514,11 +7548,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:22b1",
@@ -7530,21 +7565,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7553,12 +7585,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7568,11 +7600,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -7584,21 +7617,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7607,12 +7637,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7627,11 +7657,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-Vulkan"
+        "Build-Ubuntu-Clang-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -7643,21 +7674,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7666,12 +7694,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7681,11 +7709,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -7697,21 +7726,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7720,12 +7746,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7740,11 +7766,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+        "Build-Ubuntu-Clang-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -7756,21 +7783,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7779,12 +7803,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7794,11 +7818,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0f31",
@@ -7810,21 +7835,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7833,12 +7855,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7848,11 +7870,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0f31",
@@ -7864,21 +7887,122 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        },
+        {
+          "name": "skia/bots/linux_vulkan_intel_driver_debug",
+          "path": "linux_vulkan_intel_driver_debug",
+          "version": "version:4"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0102",
+        "os:Ubuntu-16.10",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        },
+        {
+          "name": "skia/bots/linux_vulkan_intel_driver_release",
+          "path": "linux_vulkan_intel_driver_release",
+          "version": "version:6"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0102",
+        "os:Ubuntu-16.10",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7887,12 +8011,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7901,11 +8025,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -7913,21 +8038,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7936,12 +8058,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7950,11 +8072,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -7962,21 +8085,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -7985,12 +8105,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -7999,11 +8119,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8011,21 +8132,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8034,12 +8152,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8048,11 +8166,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8060,21 +8179,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8083,12 +8199,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8097,11 +8213,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8109,21 +8226,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8132,12 +8246,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8146,11 +8260,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8158,21 +8273,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8181,12 +8293,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8195,7 +8307,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -8207,21 +8320,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-Golo-GPU-GT610-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8230,12 +8340,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8244,11 +8354,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:162b",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8256,21 +8367,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8279,12 +8387,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8293,11 +8401,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:162b",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8305,21 +8414,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8328,12 +8434,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8342,11 +8448,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8354,21 +8461,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8377,12 +8481,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8391,11 +8495,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8403,21 +8508,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8426,12 +8528,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8440,11 +8542,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8452,21 +8555,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8475,12 +8575,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8489,11 +8589,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8501,21 +8602,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8524,12 +8622,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8538,11 +8636,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8550,21 +8649,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8573,12 +8669,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8587,11 +8683,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8599,21 +8696,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8622,12 +8716,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8636,12 +8730,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-highcpu-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8649,21 +8744,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8672,12 +8764,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8686,12 +8778,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-highcpu-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8699,21 +8792,206 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8722,12 +9000,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8736,11 +9014,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:683d",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8748,21 +9027,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8771,12 +9047,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8785,11 +9061,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:683d",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8797,21 +9074,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8820,12 +9094,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8834,11 +9108,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8846,21 +9121,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8869,12 +9141,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8883,11 +9155,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8895,21 +9168,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8918,12 +9188,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8932,11 +9202,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8944,21 +9215,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -8967,12 +9235,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -8981,11 +9249,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -8993,21 +9262,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9016,12 +9282,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9030,11 +9296,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0412",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9042,21 +9309,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9065,12 +9329,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9079,11 +9343,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0412",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9091,21 +9356,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9114,12 +9376,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9128,12 +9390,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9141,21 +9404,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9164,12 +9424,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9178,12 +9438,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9191,21 +9452,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9214,12 +9472,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9228,12 +9486,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9241,21 +9500,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9264,12 +9520,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9278,12 +9534,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9291,21 +9548,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9314,12 +9568,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9328,11 +9582,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9340,21 +9595,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9363,12 +9615,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9377,11 +9629,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9389,21 +9642,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9412,12 +9662,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9426,11 +9676,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9438,21 +9689,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9461,12 +9709,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9475,11 +9723,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9487,21 +9736,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9510,12 +9756,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9524,11 +9770,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9536,21 +9783,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9559,12 +9803,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9573,11 +9817,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9585,21 +9830,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9608,12 +9850,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9622,7 +9864,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86-Debug"
+        "Build-Win-MSVC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -9635,21 +9878,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9658,12 +9898,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9672,7 +9912,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -9685,21 +9926,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9708,12 +9946,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9722,7 +9960,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-GDI"
+        "Build-Win-MSVC-x86_64-Debug-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -9735,21 +9974,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -9758,12 +9994,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9772,7 +10008,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -9785,35 +10022,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
         "buildername=Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug": {
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86-Debug": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9822,11 +10056,13 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-arm64-Debug-iOS"
+        "Build-Win-MSVC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
-        "device:iPad5,1",
-        "os:iOS-9.3.1",
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9834,35 +10070,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Win7-MSVC-Golo-CPU-AVX-x86-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release": {
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -9871,11 +10104,13 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-arm64-Release-iOS"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
-        "device:iPad5,1",
-        "os:iOS-9.3.1",
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -9883,44 +10118,514 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_perf",
+        "perf",
         "repository=<(REPO)",
-        "buildername=Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "perf_skia.isolate",
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win8-MSVC-Golo-CPU-AVX-x86-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Debug-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPad6,3",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Release-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPad6,3",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Debug-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone7,2",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Release-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone7,2",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Debug-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone9,1",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Release-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone9,1",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "perf",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "perf_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MOB30Q",
@@ -9933,44 +10638,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MOB30Q",
@@ -9983,48 +10672,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
+    "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android": {
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-mipsel-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MMB29M",
-        "device_type:j5xnlte",
+        "device_os:NRD90M",
+        "device_type:ci20",
         "os:Android",
         "pool:Skia"
       ],
@@ -10033,48 +10706,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
+    "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android": {
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-mipsel-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MMB29M",
-        "device_type:j5xnlte",
+        "device_os:NRD90M",
+        "device_type:ci20",
         "os:Android",
         "pool:Skia"
       ],
@@ -10083,44 +10740,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MMB29K",
@@ -10133,44 +10774,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:MMB29K",
@@ -10183,47 +10808,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NRD90M_G930AUCU4BQA6",
+        "device_os:NRD90M_G930AUCS4BQC2",
         "device_type:heroqlteatt",
         "os:Android",
         "pool:Skia"
@@ -10233,47 +10842,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NRD90M_G930AUCU4BQA6",
+        "device_os:NRD90M_G930AUCS4BQC2",
         "device_type:heroqlteatt",
         "os:Android",
         "pool:Skia"
@@ -10283,44 +10876,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NRD90M_G930FXXU1DQAS",
@@ -10333,44 +10910,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NRD90M_G930FXXU1DQAS",
@@ -10383,97 +10944,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
-      "max_attempts": 1,
-      "priority": 0.8
-    },
-    "Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
-      "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
-      ],
-      "dimensions": [
-        "device_os:JDQ39",
-        "device_type:goyawifi",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "swarm_test",
-        "repository=<(REPO)",
-        "buildername=Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)"
-      ],
-      "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MPJ24.139-64",
+        "device_os:NPJ25.93-14",
         "device_type:athene",
         "os:Android",
         "pool:Skia"
@@ -10483,47 +10978,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MPJ24.139-64",
+        "device_os:NPJ25.93-14",
         "device_type:athene",
         "os:Android",
         "pool:Skia"
@@ -10533,47 +11012,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -10583,47 +11046,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -10633,47 +11080,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -10683,47 +11114,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:MRA58K",
+        "device_os:NRD90M",
         "device_type:foster",
         "os:Android",
         "pool:Skia"
@@ -10733,44 +11148,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus10-CPU-Exynos5250-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -10783,44 +11182,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-CPU-Exynos5250-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -10833,44 +11216,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -10883,44 +11250,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY49J",
@@ -10933,44 +11284,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:M4B30Z",
@@ -10983,44 +11318,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:M4B30Z",
@@ -11033,47 +11352,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -11083,47 +11386,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -11133,47 +11420,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -11183,47 +11454,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26C",
+        "device_os:OPP1.170223.012",
         "device_type:angler",
         "os:Android",
         "pool:Skia"
@@ -11233,44 +11488,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Debug-Android"
+        "Build-Ubuntu-Clang-arm-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY47V",
@@ -11283,44 +11522,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm-Release-Android"
+        "Build-Ubuntu-Clang-arm-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:LMY47V",
@@ -11333,47 +11556,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Debug-Android"
+        "Build-Ubuntu-Clang-x86-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -11383,47 +11590,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Release-Android"
+        "Build-Ubuntu-Clang-x86-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -11433,47 +11624,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Debug-Android"
+        "Build-Ubuntu-Clang-x86-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -11483,47 +11658,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-x86-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -11533,47 +11692,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Release-Android"
+        "Build-Ubuntu-Clang-x86-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -11583,47 +11726,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-x86-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:N2G10B",
+        "device_os:OPP2.170420.017",
         "device_type:fugu",
         "os:Android",
         "pool:Skia"
@@ -11633,47 +11760,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26H",
+        "device_os:N2G47D",
         "device_type:dragon",
         "os:Android",
         "pool:Skia"
@@ -11683,47 +11794,31 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:NMF26H",
+        "device_os:N2G47D",
         "device_type:dragon",
         "os:Android",
         "pool:Skia"
@@ -11733,44 +11828,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android"
+        "Build-Ubuntu-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -11783,44 +11862,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -11833,44 +11896,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android"
+        "Build-Ubuntu-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -11883,44 +11930,28 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
     "Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android_Vulkan": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
       "dependencies": [
-        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan"
+        "Build-Ubuntu-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
         "device_os:NMF26Q",
@@ -11933,48 +11964,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
+    "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug": {
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "gpu:10de:08a4",
-        "os:Mac-10.11",
+        "gpu:MaliT604",
+        "os:ChromeOS",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -11982,48 +11997,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release": {
-      "cipd_packages": [
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:23"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:42"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:5"
-        }
-      ],
+    "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release": {
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "gpu:10de:08a4",
-        "os:Mac-10.11",
+        "gpu:MaliT604",
+        "os:ChromeOS",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -12031,21 +12030,216 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT860",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT860",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT764",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:MaliT764",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Debug-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:TegraK1",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release": {
+      "dependencies": [
+        "Build-Ubuntu-Clang-arm-Release-Chromebook_ARM_GLES",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "gpu:TegraK1",
+        "os:ChromeOS",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12054,12 +12248,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12068,7 +12262,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug"
+        "Build-Mac-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -12081,21 +12276,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12104,12 +12296,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12118,7 +12310,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Release"
+        "Build-Mac-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -12131,35 +12324,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug": {
+    "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12168,7 +12358,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug"
+        "Build-Mac-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0a2e",
@@ -12180,35 +12371,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer": {
+    "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12217,7 +12405,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+        "Build-Mac-Clang-x86_64-Debug-CommandBuffer",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0a2e",
@@ -12229,35 +12418,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release": {
+    "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12266,7 +12452,8 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-x86_64-Release"
+        "Build-Mac-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0a2e",
@@ -12278,21 +12465,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12301,12 +12485,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12315,7 +12499,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12328,21 +12513,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12351,12 +12533,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12366,11 +12548,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Debug-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12383,21 +12566,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12406,12 +12586,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12421,11 +12601,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-MSAN"
+        "Build-Ubuntu-Clang-x86_64-Debug-MSAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12438,21 +12619,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug-MSAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12461,12 +12639,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12475,7 +12653,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12488,21 +12667,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12511,12 +12687,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12526,11 +12702,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Release-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12543,21 +12720,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12566,12 +12740,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12580,7 +12754,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-Fast"
+        "Build-Ubuntu-Clang-x86_64-Release-Fast",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12593,21 +12768,162 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12616,12 +12932,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12631,11 +12947,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-TSAN"
+        "Build-Ubuntu-Clang-x86_64-Release-TSAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12648,21 +12965,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-TSAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12671,12 +12985,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12686,11 +13000,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Debug-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -12702,21 +13017,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Debug-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12725,12 +13037,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12740,11 +13052,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-ASAN"
+        "Build-Ubuntu-Clang-x86_64-Release-ASAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -12756,21 +13069,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Release-ASAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12779,12 +13089,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12794,11 +13104,12 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:4"
+          "version": "version:8"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-TSAN"
+        "Build-Ubuntu-Clang-x86_64-Release-TSAN",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -12810,21 +13121,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-Golo-GPU-GT610-x86_64-Release-TSAN",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12833,12 +13141,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12847,7 +13155,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86-Debug"
+        "Build-Ubuntu-GCC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12860,21 +13169,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12883,12 +13189,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -12897,7 +13203,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug"
+        "Build-Ubuntu-GCC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -12910,21 +13217,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -12936,15 +13240,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_ct_skps",
+        "ct_skps",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_100k_SKPs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -12961,15 +13262,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_ct_skps",
+        "ct_skps",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_DM_1m_SKPs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -12986,15 +13284,12 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_ct_skps",
+        "ct_skps",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-CT_IMG_DECODE_100k_SKPs",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
@@ -13008,12 +13303,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13022,7 +13317,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE"
+        "Build-Ubuntu-GCC-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -13035,21 +13331,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13058,12 +13351,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13072,7 +13365,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -13085,21 +13379,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13108,12 +13399,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13122,7 +13413,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release-SKNX_NO_SIMD"
+        "Build-Ubuntu-GCC-x86_64-Release-SKNX_NO_SIMD",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -13135,21 +13427,65 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-PreAbandonGpuContext": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:10de:104a",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-GCC-Golo-GPU-GT610-x86_64-Release-PreAbandonGpuContext",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13158,21 +13494,27 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
           "path": "svg",
           "version": "version:5"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1244",
@@ -13184,21 +13526,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 3600000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13207,21 +13546,27 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
           "path": "svg",
           "version": "version:5"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1244",
@@ -13233,21 +13578,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_AbandonGpuContext",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 3600000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13256,21 +13598,27 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
           "path": "svg",
           "version": "version:5"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1244",
@@ -13282,21 +13630,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind_PreAbandonGpuContext",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 3600000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13305,12 +13650,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13319,7 +13664,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug"
+        "Build-Ubuntu-GCC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
@@ -13331,21 +13677,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13354,12 +13697,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13368,7 +13711,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
@@ -13380,21 +13724,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13403,12 +13744,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13417,7 +13758,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Debug"
+        "Build-Ubuntu-GCC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
@@ -13429,21 +13771,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13452,12 +13791,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13466,7 +13805,8 @@
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-GCC-x86_64-Release"
+        "Build-Ubuntu-GCC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
@@ -13478,21 +13818,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13501,12 +13838,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13516,11 +13853,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:22b1",
@@ -13532,21 +13870,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13555,12 +13890,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13570,11 +13905,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:22b1",
@@ -13586,21 +13922,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13609,12 +13942,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13624,11 +13957,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -13640,21 +13974,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13663,12 +13994,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13683,11 +14014,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug-Vulkan"
+        "Build-Ubuntu-Clang-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -13699,21 +14031,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13722,12 +14051,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13737,11 +14066,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -13753,21 +14083,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13776,12 +14103,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13796,11 +14123,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release-Vulkan"
+        "Build-Ubuntu-Clang-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
@@ -13812,21 +14140,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13835,12 +14160,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13850,11 +14175,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_debug",
           "path": "linux_vulkan_intel_driver_debug",
-          "version": "version:3"
+          "version": "version:4"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Debug"
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0f31",
@@ -13866,21 +14192,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13889,12 +14212,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13904,11 +14227,12 @@
         {
           "name": "skia/bots/linux_vulkan_intel_driver_release",
           "path": "linux_vulkan_intel_driver_release",
-          "version": "version:5"
+          "version": "version:6"
         }
       ],
       "dependencies": [
-        "Build-Ubuntu-Clang-x86_64-Release"
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0f31",
@@ -13920,21 +14244,122 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        },
+        {
+          "name": "skia/bots/linux_vulkan_intel_driver_debug",
+          "path": "linux_vulkan_intel_driver_debug",
+          "version": "version:4"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0102",
+        "os:Ubuntu-16.10",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        },
+        {
+          "name": "skia/bots/linux_vulkan_intel_driver_release",
+          "path": "linux_vulkan_intel_driver_release",
+          "version": "version:6"
+        }
+      ],
+      "dependencies": [
+        "Build-Ubuntu-Clang-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0102",
+        "os:Ubuntu-16.10",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13943,12 +14368,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -13957,11 +14382,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -13969,21 +14395,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -13992,12 +14415,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14006,11 +14429,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14018,21 +14442,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14041,12 +14462,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14055,11 +14476,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14067,21 +14489,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14090,12 +14509,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14104,11 +14523,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14116,21 +14536,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14139,12 +14556,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14153,11 +14570,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14165,21 +14583,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14188,12 +14603,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14202,11 +14617,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:6646",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14214,21 +14630,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14237,12 +14650,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14251,7 +14664,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:104a",
@@ -14263,21 +14677,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-Golo-GPU-GT610-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14286,12 +14697,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14300,11 +14711,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:162b",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14312,21 +14724,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14335,12 +14744,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14349,11 +14758,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:162b",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14361,21 +14771,65 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:162b",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ReleaseAndAbandonGpuContext",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14384,12 +14838,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14398,11 +14852,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14410,21 +14865,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14433,12 +14885,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14447,11 +14899,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14459,21 +14912,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14482,12 +14932,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14496,11 +14946,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14508,21 +14959,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14531,12 +14979,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14545,11 +14993,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14557,21 +15006,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14580,12 +15026,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14594,11 +15040,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14606,21 +15053,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14629,12 +15073,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14643,11 +15087,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:1926",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14655,21 +15100,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14678,12 +15120,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14692,12 +15134,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-highcpu-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14705,21 +15148,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14728,12 +15168,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14742,12 +15182,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-highcpu-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14755,21 +15196,206 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "gpu:8086:0a16",
+        "os:Windows-10-15063",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14778,12 +15404,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14792,11 +15418,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:683d",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14804,21 +15431,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14827,12 +15451,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14841,11 +15465,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:1002:683d",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14853,21 +15478,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14876,12 +15498,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14890,11 +15512,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14902,21 +15525,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14925,12 +15545,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14939,11 +15559,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -14951,21 +15572,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -14974,12 +15592,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -14988,11 +15606,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:11c0",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15000,21 +15619,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15023,12 +15639,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15037,11 +15653,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0412",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15049,21 +15666,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15072,12 +15686,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15086,11 +15700,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:8086:0412",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15098,21 +15713,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15121,12 +15733,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15135,12 +15747,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15148,21 +15761,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15171,12 +15781,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15185,12 +15795,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15198,21 +15809,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15221,12 +15829,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15235,12 +15843,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15248,21 +15857,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15271,12 +15877,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15285,12 +15891,13 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1401",
         "machine_type:n1-standard-8",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15298,21 +15905,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15321,12 +15925,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15335,11 +15939,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15347,21 +15952,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15370,12 +15972,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15384,11 +15986,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-ANGLE"
+        "Build-Win-MSVC-x86_64-Debug-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15396,21 +15999,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15419,12 +16019,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15433,11 +16033,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-Vulkan"
+        "Build-Win-MSVC-x86_64-Debug-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15445,21 +16046,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15468,12 +16066,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15482,11 +16080,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15494,21 +16093,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15517,12 +16113,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15531,11 +16127,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-ANGLE"
+        "Build-Win-MSVC-x86_64-Release-ANGLE",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15543,21 +16140,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15566,12 +16160,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15580,11 +16174,12 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-Vulkan"
+        "Build-Win-MSVC-x86_64-Release-Vulkan",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "gpu:10de:1ba1",
-        "os:Windows-10-14393",
+        "os:Windows-10-15063",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15592,21 +16187,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15615,12 +16207,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15629,7 +16221,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86-Debug"
+        "Build-Win-MSVC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -15642,21 +16235,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15665,12 +16255,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15679,7 +16269,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86-Release"
+        "Build-Win-MSVC-x86-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -15692,21 +16283,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15715,12 +16303,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15729,7 +16317,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug"
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -15742,21 +16331,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15765,12 +16351,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15779,7 +16365,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Debug-GDI"
+        "Build-Win-MSVC-x86_64-Debug-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -15792,21 +16379,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15815,12 +16399,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15829,7 +16413,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release"
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -15842,21 +16427,18 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -15865,12 +16447,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15879,7 +16461,8 @@
         }
       ],
       "dependencies": [
-        "Build-Win-MSVC-x86_64-Release-GDI"
+        "Build-Win-MSVC-x86_64-Release-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
         "cpu:x86-64",
@@ -15892,35 +16475,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug": {
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15929,11 +16509,13 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-arm64-Debug-iOS"
+        "Build-Win-MSVC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
-        "device:iPad5,1",
-        "os:iOS-9.3.1",
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15941,35 +16523,32 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
-    "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release": {
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86-Release": {
       "cipd_packages": [
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:23"
+          "version": "version:26"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:42"
+          "version": "version:60"
         },
         {
           "name": "skia/bots/svg",
@@ -15978,11 +16557,13 @@
         }
       ],
       "dependencies": [
-        "Build-Mac-Clang-arm64-Release-iOS"
+        "Build-Win-MSVC-x86-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
       ],
       "dimensions": [
-        "device:iPad5,1",
-        "os:iOS-9.3.1",
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
         "pool:Skia"
       ],
       "execution_timeout_ns": 14400000000000,
@@ -15990,21 +16571,696 @@
       "extra_args": [
         "--workdir",
         "../../..",
-        "swarm_test",
+        "test",
         "repository=<(REPO)",
-        "buildername=Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)"
       ],
       "io_timeout_ns": 2400000000000,
-      "isolate": "test_skia.isolate",
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-7-SP1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Debug-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI": {
+      "cipd_packages": [
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:26"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:60"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:5"
+        }
+      ],
+      "dependencies": [
+        "Build-Win-MSVC-x86_64-Release-GDI",
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64",
+        "gpu:none",
+        "os:Windows-8.1-SP0",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_win.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Debug-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPad6,3",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Release-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPad6,3",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Debug-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone7,2",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Release-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone7,2",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Debug-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone9,1",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
+      "max_attempts": 1,
+      "priority": 0.8
+    },
+    "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release": {
+      "dependencies": [
+        "Build-Mac-Clang-arm64-Release-iOS",
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device:iPhone9,1",
+        "os:iOS-10.3.1",
+        "pool:Skia"
+      ],
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "test",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)"
+      ],
+      "io_timeout_ns": 2400000000000,
+      "isolate": "test_skia_bundled_unix.isolate",
       "max_attempts": 1,
       "priority": 0.8
     },
@@ -16024,42 +17280,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)",
-        "gs_bucket=skia-perf"
-      ],
-      "isolate": "upload_nano_results.isolate",
-      "priority": 0.8
-    },
-    "Upload-Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android": {
-      "dependencies": [
-        "Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android"
-      ],
-      "dimensions": [
-        "cpu:x86-64-avx2",
-        "gpu:none",
-        "os:Ubuntu-14.04",
-        "pool:Skia"
-      ],
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "upload_nano_results",
-        "repository=<(REPO)",
-        "buildername=Perf-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16084,12 +17307,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16114,12 +17334,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16144,12 +17361,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16174,12 +17388,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16204,12 +17415,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16234,12 +17442,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16264,12 +17469,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16294,12 +17496,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16324,12 +17523,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16354,12 +17550,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16384,12 +17577,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16414,12 +17604,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16444,12 +17631,36 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan": {
+      "dependencies": [
+        "Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16474,12 +17685,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16504,12 +17712,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16534,12 +17739,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16564,12 +17766,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Skpbench",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16594,12 +17793,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Android-Clang-PixelC-GPU-TegraX1-arm64-Release-Android_Vulkan_Skpbench",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16608,9 +17804,9 @@
       "isolate": "upload_nano_results.isolate",
       "priority": 0.8
     },
-    "Upload-Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release": {
+    "Upload-Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release": {
       "dependencies": [
-        "Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release"
+        "Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -16623,13 +17819,10 @@
         "../../..",
         "upload_nano_results",
         "repository=<(REPO)",
-        "buildername=Perf-Chromecast-GCC-Chorizo-CPU-Cortex_A7-arm-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16638,9 +17831,9 @@
       "isolate": "upload_nano_results.isolate",
       "priority": 0.8
     },
-    "Upload-Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release": {
+    "Upload-Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release": {
       "dependencies": [
-        "Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release"
+        "Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -16653,13 +17846,91 @@
         "../../..",
         "upload_nano_results",
         "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release": {
+      "dependencies": [
+        "Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release": {
+      "dependencies": [
+        "Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release": {
+      "dependencies": [
+        "Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Chromecast-GCC-Chorizo-GPU-Cortex_A7-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16684,12 +17955,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16698,9 +17966,9 @@
       "isolate": "upload_nano_results.isolate",
       "priority": 0.8
     },
-    "Upload-Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release": {
+    "Upload-Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release": {
       "dependencies": [
-        "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release"
+        "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -16713,13 +17981,10 @@
         "../../..",
         "upload_nano_results",
         "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16728,9 +17993,9 @@
       "isolate": "upload_nano_results.isolate",
       "priority": 0.8
     },
-    "Upload-Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer": {
+    "Upload-Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer": {
       "dependencies": [
-        "Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer"
+        "Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -16743,13 +18008,10 @@
         "../../..",
         "upload_nano_results",
         "repository=<(REPO)",
-        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release-CommandBuffer",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release-CommandBuffer",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16774,12 +18036,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16804,12 +18063,36 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "dependencies": [
+        "Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16834,12 +18117,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16864,12 +18144,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16894,12 +18171,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16924,12 +18198,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16954,12 +18225,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -16984,12 +18252,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17014,12 +18279,36 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release": {
+      "dependencies": [
+        "Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17044,12 +18333,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17074,12 +18360,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17104,12 +18387,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17134,12 +18414,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-Golo-GPU-GT610-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17164,12 +18441,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17194,12 +18468,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17224,12 +18495,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17254,12 +18522,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17284,12 +18549,63 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release": {
+      "dependencies": [
+        "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE": {
+      "dependencies": [
+        "Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17314,12 +18630,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17344,12 +18657,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17374,12 +18684,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17404,12 +18711,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17434,12 +18738,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17464,12 +18765,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17494,12 +18792,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17524,12 +18819,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17554,12 +18846,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17584,12 +18873,9 @@
         "upload_nano_results",
         "repository=<(REPO)",
         "buildername=Perf-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17598,9 +18884,9 @@
       "isolate": "upload_nano_results.isolate",
       "priority": 0.8
     },
-    "Upload-Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release": {
+    "Upload-Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release": {
       "dependencies": [
-        "Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release"
+        "Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -17613,13 +18899,118 @@
         "../../..",
         "upload_nano_results",
         "repository=<(REPO)",
-        "buildername=Perf-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Perf-Win7-MSVC-Golo-CPU-AVX-x86_64-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "dependencies": [
+        "Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-Win8-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release": {
+      "dependencies": [
+        "Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release": {
+      "dependencies": [
+        "Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-perf"
+      ],
+      "isolate": "upload_nano_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release": {
+      "dependencies": [
+        "Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_nano_results",
+        "repository=<(REPO)",
+        "buildername=Perf-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17644,12 +19035,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17674,12 +19062,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17688,9 +19073,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android": {
+    "Upload-Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android": {
       "dependencies": [
-        "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android"
+        "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -17703,13 +19088,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Debug-Android",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17718,9 +19100,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android": {
+    "Upload-Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android": {
       "dependencies": [
-        "Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android"
+        "Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -17733,13 +19115,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Android-Clang-GalaxyJ5-GPU-Adreno306-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Android-Clang-Ci20-CPU-IngenicJZ4780-mipsel-Release-Android",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17764,12 +19143,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17794,12 +19170,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17824,12 +19197,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17854,12 +19224,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930A-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17884,12 +19251,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17914,42 +19278,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
-        "patch_storage=<(PATCH_STORAGE)",
-        "patch_issue=<(ISSUE)",
-        "patch_set=<(PATCHSET)",
-        "gs_bucket=skia-infra-gm"
-      ],
-      "isolate": "upload_dm_results.isolate",
-      "priority": 0.8
-    },
-    "Upload-Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android": {
-      "dependencies": [
-        "Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android"
-      ],
-      "dimensions": [
-        "cpu:x86-64-avx2",
-        "gpu:none",
-        "os:Ubuntu-14.04",
-        "pool:Skia"
-      ],
-      "extra_args": [
-        "--workdir",
-        "../../..",
-        "upload_dm_results",
-        "repository=<(REPO)",
-        "buildername=Test-Android-Clang-GalaxyTab3-GPU-Vivante-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
-        "swarm_out_dir=${ISOLATED_OUTDIR}",
-        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -17974,12 +19305,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18004,12 +19332,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-MotoG4-GPU-Adreno405-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18034,12 +19359,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18064,12 +19386,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18094,12 +19413,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18124,12 +19440,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18154,12 +19467,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-CPU-Exynos5250-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18184,12 +19494,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-CPU-Exynos5250-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18214,12 +19521,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18244,12 +19548,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus10-GPU-MaliT604-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18274,12 +19575,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18304,12 +19602,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18334,12 +19629,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18364,12 +19656,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18394,12 +19683,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18424,12 +19710,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus6p-GPU-Adreno430-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18454,12 +19737,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18484,12 +19764,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18514,12 +19791,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18544,12 +19818,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-CPU-Moorefield-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18574,12 +19845,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18604,12 +19872,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18634,12 +19899,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18664,12 +19926,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-NexusPlayer-GPU-PowerVR-x86-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18694,12 +19953,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18724,12 +19980,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelC-CPU-TegraX1-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18754,12 +20007,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18784,12 +20034,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Debug-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18814,12 +20061,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18844,12 +20088,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Android-Clang-PixelXL-GPU-Adreno530-arm64-Release-Android_Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18858,9 +20099,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug": {
+    "Upload-Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug": {
       "dependencies": [
-        "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug"
+        "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -18873,13 +20114,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18888,9 +20126,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release": {
+    "Upload-Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release": {
       "dependencies": [
-        "Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release"
+        "Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -18903,13 +20141,172 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini4.1-GPU-GeForce320M-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-ChromeOS-Clang-Chromebook_303C12-GPU-MaliT604-arm-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug": {
+      "dependencies": [
+        "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release": {
+      "dependencies": [
+        "Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_513C24_K01-GPU-MaliT860-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug": {
+      "dependencies": [
+        "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release": {
+      "dependencies": [
+        "Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_C100p-GPU-MaliT764-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug": {
+      "dependencies": [
+        "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release": {
+      "dependencies": [
+        "Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-ChromeOS-Clang-Chromebook_CB5_311-GPU-TegraK1-arm-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18934,12 +20331,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18964,12 +20358,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Mac-Clang-MacMini6.2-CPU-AVX-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -18978,9 +20369,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug": {
+    "Upload-Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug": {
       "dependencies": [
-        "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug"
+        "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -18993,13 +20384,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19008,9 +20396,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer": {
+    "Upload-Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer": {
       "dependencies": [
-        "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer"
+        "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -19023,13 +20411,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Debug-CommandBuffer",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Debug-CommandBuffer",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19038,9 +20423,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release": {
+    "Upload-Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release": {
       "dependencies": [
-        "Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release"
+        "Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -19053,13 +20438,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-Mac-Clang-MacMini6.2-GPU-HD4000-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Mac-Clang-MacMini6.2-GPU-IntelHD4000-x86_64-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19084,12 +20466,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19114,12 +20493,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19144,12 +20520,90 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-Fast",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2": {
+      "dependencies": [
+        "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE2",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41": {
+      "dependencies": [
+        "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_CPU_LIMIT_SSE41",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER": {
+      "dependencies": [
+        "Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu-Clang-GCE-CPU-AVX2-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19174,12 +20628,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19204,12 +20655,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19234,12 +20682,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19264,12 +20709,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19294,12 +20736,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19324,12 +20763,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19354,12 +20790,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19384,12 +20817,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19414,12 +20844,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu-GCC-ShuttleA-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19444,12 +20871,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19474,12 +20898,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19504,12 +20925,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19534,12 +20952,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19564,12 +20979,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19594,12 +21006,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19624,12 +21033,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19654,12 +21060,63 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Ubuntu16-Clang-NUCDE3815TYKHE-GPU-IntelBayTrail-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug": {
+      "dependencies": [
+        "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release": {
+      "dependencies": [
+        "Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Ubuntu16-Clang-ShuttleA-GPU-IntelHD2000-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19684,12 +21141,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19714,12 +21168,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19744,12 +21195,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19774,12 +21222,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19804,12 +21249,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19834,12 +21276,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19864,12 +21303,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-Golo-GPU-GT610-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19894,12 +21330,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19924,12 +21357,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19954,12 +21384,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -19984,12 +21411,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20014,12 +21438,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20044,12 +21465,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20074,12 +21492,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20104,12 +21519,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20134,12 +21546,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20164,12 +21573,117 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-NUC6i7KYK-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug": {
+      "dependencies": [
+        "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE": {
+      "dependencies": [
+        "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Debug-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release": {
+      "dependencies": [
+        "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE": {
+      "dependencies": [
+        "Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win10-MSVC-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-ANGLE",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20194,12 +21708,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20224,12 +21735,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-AMDHD7770-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20254,12 +21762,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20284,12 +21789,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20314,12 +21816,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleA-GPU-GTX660-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20344,12 +21843,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20374,12 +21870,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleB-GPU-IntelHD4600-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20404,12 +21897,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20434,12 +21924,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20464,12 +21951,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20494,12 +21978,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ShuttleC-GPU-GTX960-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20524,12 +22005,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20554,12 +22032,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20584,12 +22059,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Debug-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20614,12 +22086,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20644,12 +22113,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-ANGLE",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20674,12 +22140,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win10-MSVC-ZBOX-GPU-GTX1070-x86_64-Release-Vulkan",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20704,12 +22167,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20734,12 +22194,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20764,12 +22221,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20794,12 +22248,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20824,12 +22275,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20854,12 +22302,9 @@
         "upload_dm_results",
         "repository=<(REPO)",
         "buildername=Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release-GDI",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20868,9 +22313,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug": {
+    "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug": {
       "dependencies": [
-        "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug"
+        "Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -20883,13 +22328,10 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Debug",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86-Debug",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
@@ -20898,9 +22340,9 @@
       "isolate": "upload_dm_results.isolate",
       "priority": 0.8
     },
-    "Upload-Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release": {
+    "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86-Release": {
       "dependencies": [
-        "Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release"
+        "Test-Win7-MSVC-Golo-CPU-AVX-x86-Release"
       ],
       "dimensions": [
         "cpu:x86-64-avx2",
@@ -20913,13 +22355,442 @@
         "../../..",
         "upload_dm_results",
         "repository=<(REPO)",
-        "buildername=Test-iOS-Clang-iPadMini4-GPU-GX6450-arm64-Release",
-        "mastername=fake-master",
-        "buildnumber=2",
-        "slavename=fake-buildslave",
-        "nobuildbot=True",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86-Release",
         "swarm_out_dir=${ISOLATED_OUTDIR}",
         "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "dependencies": [
+        "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "dependencies": [
+        "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "dependencies": [
+        "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI": {
+      "dependencies": [
+        "Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win7-MSVC-Golo-CPU-AVX-x86_64-Release-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug": {
+      "dependencies": [
+        "Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86-Release": {
+      "dependencies": [
+        "Test-Win8-MSVC-Golo-CPU-AVX-x86-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug": {
+      "dependencies": [
+        "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI": {
+      "dependencies": [
+        "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Debug-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release": {
+      "dependencies": [
+        "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI": {
+      "dependencies": [
+        "Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-Win8-MSVC-Golo-CPU-AVX-x86_64-Release-GDI",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug": {
+      "dependencies": [
+        "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release": {
+      "dependencies": [
+        "Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPadPro-GPU-GT7800-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug": {
+      "dependencies": [
+        "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release": {
+      "dependencies": [
+        "Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone6-GPU-GX6450-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug": {
+      "dependencies": [
+        "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Debug",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
+        "patch_storage=<(PATCH_STORAGE)",
+        "patch_issue=<(ISSUE)",
+        "patch_set=<(PATCHSET)",
+        "gs_bucket=skia-infra-gm"
+      ],
+      "isolate": "upload_dm_results.isolate",
+      "priority": 0.8
+    },
+    "Upload-Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release": {
+      "dependencies": [
+        "Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release"
+      ],
+      "dimensions": [
+        "cpu:x86-64-avx2",
+        "gpu:none",
+        "os:Ubuntu-14.04",
+        "pool:Skia"
+      ],
+      "extra_args": [
+        "--workdir",
+        "../../..",
+        "upload_dm_results",
+        "repository=<(REPO)",
+        "buildername=Test-iOS-Clang-iPhone7-GPU-GT7600-arm64-Release",
+        "swarm_out_dir=${ISOLATED_OUTDIR}",
+        "revision=<(REVISION)",
+        "patch_repo=<(PATCH_REPO)",
         "patch_storage=<(PATCH_STORAGE)",
         "patch_issue=<(ISSUE)",
         "patch_set=<(PATCHSET)",
diff --git a/infra/bots/test_skia_bundled.isolate b/infra/bots/test_skia_bundled.isolate
new file mode 100644
index 0000000..dbfe4c3
--- /dev/null
+++ b/infra/bots/test_skia_bundled.isolate
@@ -0,0 +1,14 @@
+{
+  'includes': [
+    'android_bin.isolate',
+    'assets.isolate',
+    'ios_bin.isolate',
+    'resources.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../../.gclient',
+      '../../tools/valgrind.supp',
+    ],
+  },
+}
diff --git a/infra/bots/test_skia_bundled_unix.isolate b/infra/bots/test_skia_bundled_unix.isolate
new file mode 100644
index 0000000..f3be6ea
--- /dev/null
+++ b/infra/bots/test_skia_bundled_unix.isolate
@@ -0,0 +1,6 @@
+{
+  'includes': [
+    'test_skia_bundled.isolate',
+    'swarm_recipe_bundled_unix.isolate',
+  ],
+}
diff --git a/infra/bots/test_skia_bundled_win.isolate b/infra/bots/test_skia_bundled_win.isolate
new file mode 100644
index 0000000..cdffa56
--- /dev/null
+++ b/infra/bots/test_skia_bundled_win.isolate
@@ -0,0 +1,6 @@
+{
+  'includes': [
+    'test_skia_bundled.isolate',
+    'swarm_recipe_bundled_win.isolate',
+  ],
+}
diff --git a/infra/bots/update_meta_config.py b/infra/bots/update_meta_config.py
new file mode 100644
index 0000000..7a91ae7
--- /dev/null
+++ b/infra/bots/update_meta_config.py
@@ -0,0 +1,115 @@
+# 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.
+
+"""Update meta/config of the specified Skia repo."""
+
+
+import argparse
+import json
+import os
+import subprocess
+import sys
+import urllib2
+
+import git_utils
+
+
+SKIA_COMMITTER_EMAIL = 'update-meta-config@skia.org'
+SKIA_COMMITTER_NAME = 'Update Meta Config'
+SKIA_REPO_TEMPLATE = 'https://skia.googlesource.com/%s.git'
+
+CQ_INCLUDE_CHROMIUM_TRYBOTS = [
+    ('master.tryserver.blink', [
+        'linux_trusty_blink_rel',
+        'linux_trusty_blink_dbg',
+    ]),
+    ('master.tryserver.chromium.linux', [
+        'linux_chromium_compile_dbg_ng',
+        'linux_chromium_compile_rel_ng',
+        'linux_chromium_dbg_ng',
+        'linux_chromium_rel_ng',
+        'linux_optional_gpu_tests_rel',
+    ]),
+    ('master.tryserver.chromium.mac', [
+        'mac_chromium_compile_dbg_ng',
+        'mac_chromium_compile_rel_ng',
+        'mac_chromium_dbg_ng',
+        'mac_chromium_rel_ng',
+        'mac_optional_gpu_tests_rel',
+    ]),
+    ('master.tryserver.chromium.win', [
+        'win_chromium_compile_dbg_ng',
+        'win_chromium_compile_rel_ng',
+        'win_chromium_dbg_ng',
+        'win_chromium_rel_ng',
+        'win_optional_gpu_tests_rel',
+    ]),
+    ('master.tryserver.chromium.android', [
+        'android_compile_dbg',
+        'android_compile_rel',
+        'android_optional_gpu_tests_rel',
+    ])
+]
+
+
+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 main(gitcookies, repo_name, tasks_json):
+  skia_repo = SKIA_REPO_TEMPLATE % repo_name
+  with git_utils.NewGitCheckout(repository=skia_repo):
+    # Fetch and checkout the meta/config branch.
+    subprocess.check_call(['git', 'fetch', skia_repo, 'refs/meta/config:cfg'])
+    subprocess.check_call(['git', 'checkout', 'cfg'])
+
+    # Create list of tryjobs from tasks_json.
+    tryjobs = []
+    with open(tasks_json) as tasks_json:
+      data = json.load(tasks_json)
+      for job in data['jobs'].keys():
+        if not job.startswith('Upload-'):
+          tryjobs.append(job)
+    tryjobs.sort()
+
+    # Write to buildbucket.config.
+    buildbucket_config = os.path.join(os.getcwd(), 'buildbucket.config')
+    with open(buildbucket_config, 'w') as f:
+
+      if repo_name == 'skia':
+        addChromiumTrybots(f)
+
+      # Adding all Skia jobs.
+      f.write('[bucket "skia.primary"]\n')
+      for job in tryjobs:
+        f.write('\tbuilder = ' + job + '\n')
+
+    # Push the change as the update-meta-config user.
+    config_dict = {
+      'user.name': SKIA_COMMITTER_NAME,
+      'user.email': SKIA_COMMITTER_EMAIL,
+      'http.cookiefile': gitcookies,
+    }
+    with git_utils.GitLocalConfig(config_dict):
+      subprocess.check_call(['git', 'add', 'buildbucket.config'])
+      try:
+        subprocess.check_call(
+            ['git', 'commit', '-m', 'Update builders in buildbucket.config'])
+      except subprocess.CalledProcessError:
+        print 'No changes to buildbucket.config'
+        return
+
+      subprocess.check_call(['git', 'push', skia_repo, 'cfg:refs/meta/config'])
+
+
+if '__main__' == __name__:
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--gitcookies")
+  parser.add_argument("--repo_name")
+  parser.add_argument("--tasks_json")
+  args = parser.parse_args()
+  main(args.gitcookies, args.repo_name, args.tasks_json)
diff --git a/infra/branch-config/cq.cfg b/infra/branch-config/cq.cfg
index 8a2ffe9..2f52926 100644
--- a/infra/branch-config/cq.cfg
+++ b/infra/branch-config/cq.cfg
@@ -54,9 +54,10 @@
       builders { name: "Build-Win-MSVC-x86-Debug" }
       builders { name: "Build-Win-MSVC-x86_64-Release-Vulkan" }
       builders { name: "Housekeeper-PerCommit-InfraTests" }
-      builders { name: "Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-Android" }
+      builders { name: "Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Release-Android" }
       builders { name: "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Debug" }
       builders { name: "Test-Ubuntu-GCC-ShuttleA-GPU-GTX660-x86_64-Release" }
+      builders { name: "Test-Win2k8-MSVC-GCE-CPU-AVX2-x86_64-Release" }
     }
     try_job_retry_config {
       try_job_retry_quota: 1
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index 6d16c76..f6e8301 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -1,21 +1,28 @@
-api_version: 1
-project_id: "skia"
-recipes_path: "infra/bots"
-deps {
-  project_id: "build"
-  url: "https://chromium.googlesource.com/chromium/tools/build.git"
-  branch: "master"
-  revision: "4dbb302e82655768d714dff8b58601a321bef8a7"
-}
-deps {
-  project_id: "depot_tools"
-  url: "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
-  branch: "master"
-  revision: "5be3bdd70e1d95b88c93ca38c0c870e2399dede7"
-}
-deps {
-  project_id: "recipe_engine"
-  url: "https://chromium.googlesource.com/external/github.com/luci/recipes-py.git"
-  branch: "master"
-  revision: "84b34416f3e1b1582cf81ab7ffea34f1e84b1c95"
+{
+  "api_version": 2,
+  "autoroll_recipe_options": {
+    "nontrivial": {
+      "automatic_commit_dry_run": true
+    },
+    "trivial": {
+      "automatic_commit": true,
+      "tbr_emails": [
+        "borenet@google.com"
+      ]
+    }
+  },
+  "deps": {
+    "depot_tools": {
+      "branch": "master",
+      "revision": "577e70d6fed8bc7d82dcbfa2b6f0a87359e4815b",
+      "url": "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
+    },
+    "recipe_engine": {
+      "branch": "master",
+      "revision": "1ec7ad4df3efbdd7224f249179390aab4a66c562",
+      "url": "https://chromium.googlesource.com/external/github.com/luci/recipes-py.git"
+    }
+  },
+  "project_id": "skia",
+  "recipes_path": "infra/bots"
 }
diff --git a/platform_tools/ios/bin/ios_setup.sh b/platform_tools/ios/bin/ios_setup.sh
index 3768396..0988722 100755
--- a/platform_tools/ios/bin/ios_setup.sh
+++ b/platform_tools/ios/bin/ios_setup.sh
@@ -9,7 +9,9 @@
 # ios_setup.sh: Sets environment variables used by other iOS scripts.
 
 # File system location where we mount the ios devices.
-IOS_MOUNT_POINT="/tmp/mnt_iosdevice"
+if [[ -z "${IOS_MOUNT_POINT}" ]]; then
+  IOS_MOUNT_POINT="/tmp/mnt_iosdevice"
+fi
 
 # Location on the ios device where all data are stored. This is
 # relative to the mount point.
@@ -88,10 +90,12 @@
 
 ios_cat() {
   local TARGET="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$1"
+  >&2 echo "target: '${TARGET}''"
   ios_mount
-  RET="$(cat $TARGET)"
+  RET="$( cat ${TARGET} )"
   ios_umount
-  echo -e "$RET"
+  >&2 echo "Result: '${RET}'"
+  echo -e "${RET}"
 }
 
 # ios_mount: mounts the iOS device for reading or writing.
@@ -107,14 +111,18 @@
     mkdir -p $IOS_MOUNT_POINT
   fi
   ifuse --container $IOS_BUNDLE_ID $IOS_MOUNT_POINT
-  sleep 1
+
+  sleep 2
+  if [[ ! -d "${IOS_MOUNT_POINT}/${IOS_DOCS_DIR}" ]]; then
+    exit 1
+  fi
   >&2 echo "Successfully mounted device."
   #find $IOS_MOUNT_POINT
 }
 
 # ios_umount: unmounts the ios device.
 ios_umount() {
-  umount $IOS_MOUNT_POINT
+  sudo umount $IOS_MOUNT_POINT
   sleep 1
 }
 
@@ -133,7 +141,7 @@
 
   ios_mount
   if [[ -d "${HOST_DST}" ]]; then
-    cp -r "$IOS_SRC/" "$HOST_DST"
+    cp -r "$IOS_SRC/." "$HOST_DST"
   else
     cp -r "$IOS_SRC" "$HOST_DST"
   fi
@@ -149,7 +157,7 @@
   ios_mount
   rm -rf $IOS_DST
   mkdir -p "$(dirname $IOS_DST)"
-  cp -r "$HOST_SRC" "$IOS_DST"
+  cp -r -L "$HOST_SRC" "$IOS_DST"
   ios_umount
 }
 
diff --git a/public.bzl b/public.bzl
index 89b80a4..a4ff410 100644
--- a/public.bzl
+++ b/public.bzl
@@ -62,6 +62,7 @@
         "src/**/*.h",
         "src/**/*.cpp",
         "src/**/*.inc",
+        "src/jumper/SkJumper_generated.S",
 
         # Third Party
         "third_party/etc1/*.cpp",
@@ -82,10 +83,9 @@
         "src/gpu/gl/iOS/*",
         "src/gpu/gl/mac/*",
         "src/gpu/gl/win/*",
-        "src/images/*",
-        "src/jumper/*",
         "src/opts/**/*",
         "src/ports/**/*",
+        "src/ports/SkImageEncoder_none.cpp",
         "src/utils/android/**/*",
         "src/utils/mac/**/*",
         "src/utils/SkThreadUtils_win.cpp",  # Windows-only. Move to ports?
@@ -117,6 +117,9 @@
 
         # Defines main.
         "src/sksl/SkSLMain.cpp",
+
+        # Only pre-compiled into SkJumper_generated.S.
+        "src/jumper/SkJumper_stages_lowp.cpp",
     ],
 )
 
@@ -126,7 +129,6 @@
         "src/android/*",
         "src/codec/*",
         "src/gpu/gl/GrGLDefaultInterface_none.cpp",
-        "src/images/*",
         "src/opts/**/*.cpp",
         "src/opts/**/*.h",
         "src/ports/**/*.cpp",
@@ -180,7 +182,6 @@
         "src/android/*",
         "src/codec/*",
         "src/gpu/gl/GrGLDefaultInterface_none.cpp",
-        "src/images/*",
         # TODO(benjaminwagner): Figure out how to compile with EGL.
         "src/opts/**/*.cpp",
         "src/opts/**/*.h",
@@ -270,6 +271,7 @@
         "src/ports/SkFontMgr_custom_empty_factory.cpp",
         "src/ports/SkFontMgr_empty_factory.cpp",
         "src/ports/SkGlobalInitialization_none.cpp",
+        "src/ports/SkImageEncoder_none.cpp",
         "src/ports/SkImageGenerator_none.cpp",
         "src/ports/SkTLS_none.cpp",
     ],
@@ -384,6 +386,8 @@
 
 SKIA_CPU_UNSPECIFIED = "UNSPECIFIED"
 
+SKIA_CPU_ARM = "ARM"
+
 SKIA_CPU_PPC = "PPC"
 
 def skia_srcs(os=SKIA_OS_UNIX, cpu=SKIA_CPU_UNSPECIFIED):
@@ -400,7 +404,7 @@
   elif os == SKIA_OS_UNIX:
     if cpu == SKIA_CPU_UNSPECIFIED:
       srcs = srcs + ["src/opts/opts_check_x86.cpp"] + skia_glob(BASE_SRCS_UNIX)
-    elif cpu == SKIA_CPU_PPC:
+    elif cpu == SKIA_CPU_PPC or cpu == SKIA_CPU_ARM:
       srcs = srcs + skia_glob(BASE_SRCS_UNIX)
     else:
       fail("cpu must be one of SKIA_CPU_*")
@@ -421,6 +425,7 @@
     "include/config",
     "include/core",
     "include/effects",
+    "include/encode",
     "include/gpu",
     "include/images",
     "include/pathops",
@@ -441,6 +446,7 @@
     "src/ports",
     "src/pdf",
     "src/sfnt",
+    "src/shaders",
     "src/sksl",
     "src/utils",
     "third_party/etc1",
@@ -543,12 +549,13 @@
     "src/codec",
     "src/core",
     "src/effects",
-    "src/effects/gradients",
     "src/fonts",
     "src/images",
     "src/pathops",
     "src/pipe/utils",
     "src/ports",
+    "src/shaders",
+    "src/shaders/gradients",
     "src/xml",
     "tests",
     "tools",
@@ -583,6 +590,7 @@
     # The ASAN we use with Bazel has some strict checks, so omit tests that
     # trigger them.
     match += [
+        "~bigrect",
         "~clippedcubic2",
         "~conicpaths",
         "~^gradients",
@@ -610,7 +618,11 @@
     "-Wno-deprecated-declarations",  # Internal use of deprecated methods. :(
 ]
 
-COPTS_ANDROID = ["-mfpu=neon"]
+COPTS_ANDROID = [
+    "-mfpu=neon",
+    "-Wno-error=attributes",  # 'GrResourceCache' declared with greater visibility than the
+                              # type of its field 'GrResourceCache::fPurgeableQueue'... bogus.
+]
 
 COPTS_IOS = []
 
@@ -643,7 +655,6 @@
     "SK_BUILD_FOR_IOS",
     "SK_BUILD_NO_OPTS",
     "SK_HAS_JPEG_LIBRARY",
-    "SK_IGNORE_ETC1_SUPPORT",
     "SKNX_NO_SIMD",
 ]
 
@@ -654,9 +665,10 @@
     # Turn on a few Google3-specific build fixes.
     "GOOGLE3",
     # Staging flags for API changes
+    # Should remove after we update golden images
+    "SK_WEBP_ENCODER_USE_DEFAULT_METHOD",
     # Temporarily Disable analytic AA for Google3
     "SK_NO_ANALYTIC_AA",
-    "SK_SUPPORT_LEGACY_BITMAP_SETPIXELREF",
 ]
 
 ################################################################################
diff --git a/resources/alphabetAnim.gif b/resources/alphabetAnim.gif
new file mode 100644
index 0000000..d6b7d85
--- /dev/null
+++ b/resources/alphabetAnim.gif
Binary files differ
diff --git a/resources/blendBG.webp b/resources/blendBG.webp
new file mode 100644
index 0000000..46e4ce2
--- /dev/null
+++ b/resources/blendBG.webp
Binary files differ
diff --git a/resources/box.gif b/resources/box.gif
index bcf8186..72884ce 100644
--- a/resources/box.gif
+++ b/resources/box.gif
Binary files differ
diff --git a/resources/dog.jpg b/resources/dog.jpg
new file mode 100644
index 0000000..2598ecb
--- /dev/null
+++ b/resources/dog.jpg
Binary files differ
diff --git a/resources/flutter_logo.jpg b/resources/flutter_logo.jpg
new file mode 100644
index 0000000..f049c22
--- /dev/null
+++ b/resources/flutter_logo.jpg
Binary files differ
diff --git a/resources/icc_profiles/invalid_color_lut.icc b/resources/icc_profiles/invalid_color_lut.icc
new file mode 100644
index 0000000..33384cd
--- /dev/null
+++ b/resources/icc_profiles/invalid_color_lut.icc
Binary files differ
diff --git a/resources/invalid_images/bad_palette.png b/resources/invalid_images/bad_palette.png
new file mode 100644
index 0000000..984b8f6
--- /dev/null
+++ b/resources/invalid_images/bad_palette.png
Binary files differ
diff --git a/resources/rainbow-gradient.png b/resources/rainbow-gradient.png
new file mode 100644
index 0000000..4e18a32
--- /dev/null
+++ b/resources/rainbow-gradient.png
Binary files differ
diff --git a/resources/randPixelsAnim2.gif b/resources/randPixelsAnim2.gif
new file mode 100644
index 0000000..2b2b456
--- /dev/null
+++ b/resources/randPixelsAnim2.gif
Binary files differ
diff --git a/resources/shadowreference.png b/resources/shadowreference.png
new file mode 100755
index 0000000..cca6fff
--- /dev/null
+++ b/resources/shadowreference.png
Binary files differ
diff --git a/resources/ship.png b/resources/ship.png
new file mode 100755
index 0000000..acb4fd4
--- /dev/null
+++ b/resources/ship.png
Binary files differ
diff --git a/resources/webp-animated.webp b/resources/webp-animated.webp
new file mode 100755
index 0000000..35a8dfc
--- /dev/null
+++ b/resources/webp-animated.webp
Binary files differ
diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
index 2721a5b..f4d2324 100644
--- a/samplecode/ClockFaceView.cpp
+++ b/samplecode/ClockFaceView.cpp
@@ -19,46 +19,6 @@
 #include "SkStrokeRec.h"
 #include "SkTypeface.h"
 
-static inline SkPMColor rgb2gray(SkPMColor c) {
-    unsigned r = SkGetPackedR32(c);
-    unsigned g = SkGetPackedG32(c);
-    unsigned b = SkGetPackedB32(c);
-
-    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
-
-    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
-}
-
-class SkGrayScaleColorFilter : public SkColorFilter {
-public:
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const override {
-        for (int i = 0; i < count; i++) {
-            result[i] = rgb2gray(src[i]);
-        }
-    }
-};
-
-class SkChannelMaskColorFilter : public SkColorFilter {
-public:
-    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
-        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
-    }
-
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const override {
-        SkPMColor mask = fMask;
-        for (int i = 0; i < count; i++) {
-            result[i] = src[i] & mask;
-        }
-    }
-
-private:
-    SkPMColor   fMask;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
 #include "SkGradientShader.h"
 #include "SkLayerRasterizer.h"
 #include "SkBlurMaskFilter.h"
@@ -235,7 +195,7 @@
         paint.setTypeface(fFace);
 
         apply_shader(&paint, SkScalarToFloat(fInterp));
-        canvas->drawText(str.c_str(), str.size(), x, y, paint);
+        canvas->drawString(str, x, y, paint);
 
     //    drawdots(canvas, paint);
 
diff --git a/samplecode/GMSampleView.cpp b/samplecode/GMSampleView.cpp
index 19cbcc8..ffa33ce 100644
--- a/samplecode/GMSampleView.cpp
+++ b/samplecode/GMSampleView.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GMSampleView.h"
+#include "SkData.h"
 
 GMSampleView::GMSampleView(GM* gm) : fShowSize(false), fGM(gm) {}
 
diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp
index 62e8c2a..cb56783 100644
--- a/samplecode/OverView.cpp
+++ b/samplecode/OverView.cpp
@@ -167,7 +167,7 @@
     for (int i = 0; i < fCount; ++i) {
         if (draw_this_name(fNames[i], fMatchStr)) {
             canvas->drawRect(this->bounds(loc), paint);
-            canvas->drawText(fNames[i].c_str(), fNames[i].size(), loc.x(), loc.y(), fNamePaint);
+            canvas->drawString(fNames[i], loc.x(), loc.y(), fNamePaint);
             this->next(&loc);
         }
     }
diff --git a/samplecode/PerlinPatch.cpp b/samplecode/PerlinPatch.cpp
index 9adb08a..59337c5 100644
--- a/samplecode/PerlinPatch.cpp
+++ b/samplecode/PerlinPatch.cpp
@@ -10,19 +10,19 @@
 #include "SkCanvas.h"
 #include "SkGradientShader.h"
 #include "SkPatchUtils.h"
-#include "SkPerlinNoiseShader2/SkPerlinNoiseShader2.h"
+#include "SkPerlinNoiseShader.h"
 
 static void draw_control_points(SkCanvas* canvas, const SkPoint cubics[12]) {
     //draw control points
     SkPaint paint;
     SkPoint bottom[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getBottomCubic(cubics, bottom);
+    SkPatchUtils::GetBottomCubic(cubics, bottom);
     SkPoint top[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getTopCubic(cubics, top);
+    SkPatchUtils::GetTopCubic(cubics, top);
     SkPoint left[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getLeftCubic(cubics, left);
+    SkPatchUtils::GetLeftCubic(cubics, left);
     SkPoint right[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::getRightCubic(cubics, right);
+    SkPatchUtils::GetRightCubic(cubics, right);
 
     paint.setColor(SK_ColorBLACK);
     paint.setStrokeWidth(0.5f);
@@ -149,7 +149,7 @@
         ;
         
         SkScalar scaleFreq = 2.0;
-        fShader1 = SkPerlinNoiseShader2::MakeImprovedNoise(fXFreq/scaleFreq, fYFreq/scaleFreq, 4,
+        fShader1 = SkPerlinNoiseShader::MakeImprovedNoise(fXFreq/scaleFreq, fYFreq/scaleFreq, 4,
                                                              fSeed);
         fShaderCompose = SkShader::MakeComposeShader(fShader0, fShader1, SkBlendMode::kSrcOver);
 
diff --git a/samplecode/SampleAAGeometry.cpp b/samplecode/SampleAAGeometry.cpp
index 875db26..08a518c 100644
--- a/samplecode/SampleAAGeometry.cpp
+++ b/samplecode/SampleAAGeometry.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SampleCode.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkGeometry.h"
 #include "SkIntersections.h"
@@ -606,8 +607,8 @@
         canvas->drawLine(fBounds.fLeft - 5, fYLo, fBounds.fRight + 5, fYLo, paints.fIndicator);
         SkString label;
         label.printf("%0.3g", fValLo);
-        canvas->drawText(label.c_str(), label.size(), fBounds.fLeft + 5, fYLo - 5, paints.fValue);
-        canvas->drawText(fName.c_str(), fName.size(), fBounds.fLeft, fBounds.bottom() + 11,
+        canvas->drawString(label, fBounds.fLeft + 5, fYLo - 5, paints.fValue);
+        canvas->drawString(fName, fBounds.fLeft, fBounds.bottom() + 11,
                 paints.fLabel);
     }
 };
@@ -634,7 +635,7 @@
         if (yPos < fYLo + 10) {
             yPos = fYLo + 10;
         }
-        canvas->drawText(label.c_str(), label.size(), fBounds.fLeft + 5, yPos - 5, paints.fValue);
+        canvas->drawString(label, fBounds.fLeft + 5, yPos - 5, paints.fValue);
         SkRect fill = { fBounds.fLeft, fYLo, fBounds.fRight, yPos };
         canvas->drawRect(fill, paints.fFill);
     }
@@ -1055,15 +1056,15 @@
         SkVector bisect = { (lastV.fX + nextV.fX) / 2, (lastV.fY + nextV.fY) / 2 };
         bisect.setLength(fWidthControl.fValLo * 2);
         if (fBisectButton.enabled()) {
-            canvas->drawLine(pt.fX, pt.fY, pt.fX + bisect.fX, pt.fY + bisect.fY, fSkeletonPaint);
+            canvas->drawLine(pt, pt + bisect, fSkeletonPaint);
         }
         lastV.setLength(fWidthControl.fValLo);
         if (fBisectButton.enabled()) {
-            canvas->drawLine(pt.fX, pt.fY, pt.fX - lastV.fY, pt.fY + lastV.fX, fSkeletonPaint);
+            canvas->drawLine(pt, {pt.fX - lastV.fY, pt.fY + lastV.fX}, fSkeletonPaint);
         }
         nextV.setLength(fWidthControl.fValLo);
         if (fBisectButton.enabled()) {
-            canvas->drawLine(pt.fX, pt.fY, pt.fX + nextV.fY, pt.fY - nextV.fX, fSkeletonPaint);
+            canvas->drawLine(pt, {pt.fX + nextV.fY, pt.fY - nextV.fX}, fSkeletonPaint);
         }
         if (fJoinButton.enabled()) {
             SkScalar r = fWidthControl.fValLo;
@@ -1116,8 +1117,8 @@
                         SkPoint maxPt = SkEvalQuadAt(pts, t);
                         SkVector tangent = SkEvalQuadTangentAt(pts, t);
                         tangent.setLength(fWidthControl.fValLo * 2);
-                        canvas->drawLine(maxPt.fX, maxPt.fY,
-                                maxPt.fX + tangent.fY, maxPt.fY - tangent.fX, fSkeletonPaint);
+                        canvas->drawLine(maxPt, {maxPt.fX + tangent.fY, maxPt.fY - tangent.fX},
+                                         fSkeletonPaint);
                     }
                     } break;
                 case SkPath::kConic_Verb:
@@ -1162,8 +1163,8 @@
                         SkVector tangent;
                         SkEvalCubicAt(pts, tMax[tIndex], &maxPt, &tangent, NULL);
                         tangent.setLength(fWidthControl.fValLo * 2);
-                        canvas->drawLine(maxPt.fX, maxPt.fY,
-                                maxPt.fX + tangent.fY, maxPt.fY - tangent.fX, fSkeletonPaint);
+                        canvas->drawLine(maxPt, {maxPt.fX + tangent.fY, maxPt.fY - tangent.fX},
+                                         fSkeletonPaint);
                     }
                     } break;
                 case SkPath::kClose_Verb:
@@ -1819,11 +1820,11 @@
     SkScalar bottomOffset = this->height() - 10;
     for (int index = kKeyCommandCount - 1; index >= 0; --index) {
         bottomOffset -= 15;
-        canvas->drawText(kKeyCommandList[index].fDescriptionL,
-                strlen(kKeyCommandList[index].fDescriptionL), this->width() - 160, bottomOffset,
+        canvas->drawString(kKeyCommandList[index].fDescriptionL,
+                this->width() - 160, bottomOffset,
                 fLegendLeftPaint);
-        canvas->drawText(kKeyCommandList[index].fDescriptionR,
-                strlen(kKeyCommandList[index].fDescriptionR), this->width() - 20, bottomOffset,
+        canvas->drawString(kKeyCommandList[index].fDescriptionR,
+                this->width() - 20, bottomOffset,
                 fLegendRightPaint);
     }
 }
diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp
index 37a0c4b..f7a5a3a 100644
--- a/samplecode/SampleAll.cpp
+++ b/samplecode/SampleAll.cpp
@@ -36,45 +36,6 @@
 #include <math.h>
 #include "DecodeFile.h"
 
-static inline SkPMColor rgb2gray(SkPMColor c) {
-    unsigned r = SkGetPackedR32(c);
-    unsigned g = SkGetPackedG32(c);
-    unsigned b = SkGetPackedB32(c);
-
-    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
-
-    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
-}
-
-class SkGrayScaleColorFilter : public SkColorFilter {
-public:
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const override {
-        for (int i = 0; i < count; i++)
-            result[i] = rgb2gray(src[i]);
-    }
-};
-
-class SkChannelMaskColorFilter : public SkColorFilter {
-public:
-    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
-        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
-    }
-
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const override {
-        SkPMColor mask = fMask;
-        for (int i = 0; i < count; i++) {
-            result[i] = src[i] & mask;
-        }
-    }
-
-private:
-    SkPMColor   fMask;
-};
-
-///////////////////////////////////////////////////////////
-
 static void r0(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
     p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
                                            SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3)),
@@ -402,7 +363,7 @@
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setBlendMode(SkBlendMode::kXor);
         paint.setColorFilter(lightingFilter);
-        canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green
+        canvas->drawLine(start, stop, paint); // should not be green
         paint.setBlendMode(SkBlendMode::kSrcOver);
         paint.setColorFilter(nullptr);
 
@@ -544,7 +505,7 @@
                                     gLightingColors[index].fAdd));
 #endif
 
-            canvas->drawText(str.c_str(), str.size(), x, y, paint);
+            canvas->drawString(str, x, y, paint);
             SkRect  oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y };
             paint.setStyle(SkPaint::kStroke_Style);
             canvas->drawOval(oval, paint);
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 0c0baab..0f13340 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -12,7 +12,6 @@
 #include "SkColorFilter.h"
 #include "SkCamera.h"
 #include "SkCanvas.h"
-#include "SkGaussianEdgeShader.h"
 #include "SkPath.h"
 #include "SkPathOps.h"
 #include "SkPoint3.h"
@@ -21,8 +20,6 @@
 #include "SkView.h"
 #include "sk_tool_utils.h"
 
-#define USE_SHADOW_UTILS
-
 ////////////////////////////////////////////////////////////////////////////
 
 class ShadowsView : public SampleView {
@@ -52,7 +49,7 @@
         , fAnimAngle(0)
         , fShowAmbient(true)
         , fShowSpot(true)
-        , fUseAlt(true)
+        , fUseAlt(false)
         , fShowObject(true)
         , fIgnoreShadowAlpha(false) {}
 
@@ -72,7 +69,7 @@
         fWideRectPath.addRect(SkRect::MakeXYWH(0, 0, 630, 70));
         fWideOvalPath.addOval(SkRect::MakeXYWH(0, 0, 630, 70));
 
-        fLightPos = SkPoint3::Make(-700, -700, 2800);
+        fLightPos = SkPoint3::Make(350, 0, 600);
     }
 
     // overrides from SkEventSink
@@ -129,327 +126,27 @@
         canvas->drawColor(0xFFDDDDDD);
     }
 
-    static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
-        SkRect pathRect;
-        SkRRect pathRRect;
-        if (path.isOval(&pathRect)) {
-            *occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
-        } else if (path.isRRect(&pathRRect)) {
-            *occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
-        } else if (path.isRect(occlRect)) {
-            // the inverse transform for the spot shadow occluder doesn't always get us
-            // back to exactly the same position, so deducting a little slop
-            occlRect->inset(1, 1);
-        } else {
-            *occlRect = SkRect::MakeEmpty();
-        }
-    }
-
-    void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue, 
-                           SkScalar ambientAlpha) {
-
-        if (ambientAlpha <= 0) {
-            return;
-        }
-
-        const SkScalar kHeightFactor = 1.f / 128.f;
-        const SkScalar kGeomFactor = 64;
-
-        SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
-        SkScalar radius = zValue*kHeightFactor*kGeomFactor;
-
-        // occlude blur
-        SkRect occlRect;
-        GetOcclRect(path, &occlRect);
-        sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
-                                                        SkBlurMask::ConvertRadiusToSigma(radius),
-                                                        occlRect,
-                                                        SkBlurMaskFilter::kNone_BlurFlag);
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setMaskFilter(std::move(mf));
-        paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
-                                    ? 255
-                                    : (unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
-        canvas->drawPath(path, paint);
-
-        // draw occlusion rect
-#if DRAW_OCCL_RECT
-        SkPaint stroke;
-        stroke.setStyle(SkPaint::kStroke_Style);
-        stroke.setColor(SK_ColorBLUE);
-        canvas->drawRect(occlRect, stroke);
-#endif
-    }
-
-    void drawAmbientShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
-                              SkScalar ambientAlpha) {
-
-        if (ambientAlpha <= 0) {
-            return;
-        }
-
-        const SkScalar kHeightFactor = 1.f / 128.f;
-        const SkScalar kGeomFactor = 64;
-
-        SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
-        SkScalar radius = zValue*kHeightFactor*kGeomFactor;
-        // distance to outer of edge of geometry from original shape edge
-        SkScalar offset = radius*umbraAlpha;
-
-        SkRect pathRect;
-        SkRRect pathRRect;
-        SkScalar scaleFactors[2];
-        if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
-            return;
-        }
-        if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
-            !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
-              (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
-              path.isRect(&pathRect))) {
-            this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
-            return;
-        }
-
-        // For all of these, we inset the offset rect by half the radius to get our stroke shape.
-        SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
-        // Make sure we'll have a radius of at least 0.5 after xform
-        if (strokeOutset*scaleFactors[0] < 0.5f) {
-            strokeOutset = 0.5f / scaleFactors[0];
-        }
-        if (path.isOval(nullptr)) {
-            pathRect.outset(strokeOutset, strokeOutset);
-            pathRRect = SkRRect::MakeOval(pathRect);
-        } else if (path.isRect(nullptr)) {
-            pathRect.outset(strokeOutset, strokeOutset);
-            pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
-        } else {
-            pathRRect.outset(strokeOutset, strokeOutset);
-        }
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        // we outset the stroke a little to cover up AA on the interior edge
-        SkScalar pad = 0.5f;
-        paint.setStrokeWidth(radius + 2*pad);
-        // handle scale of radius and pad due to CTM
-        radius *= scaleFactors[0];
-        pad *= scaleFactors[0];
-        SkASSERT(radius < 16384);
-        SkASSERT(pad < 64);
-        // Convert radius to 14.2 fixed point and place in the R & G components.
-        // Convert pad to 6.2 fixed point and place in the B component.
-        uint16_t iRadius = (uint16_t)(radius*4.0f);
-        unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
-        paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
-                                      iRadius >> 8, iRadius & 0xff,
-                                      (unsigned char)(4.0f*pad)));
-
-        paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
-        paint.setShader(SkGaussianEdgeShader::Make());
-        canvas->drawRRect(pathRRect, paint);
-    }
-
-    void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
-                        SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
-        if (spotAlpha <= 0) {
-            return;
-        }
-
-        SkScalar zRatio = zValue / (lightPos.fZ - zValue);
-        if (zRatio < 0.0f) {
-            zRatio = 0.0f;
-        } else if (zRatio > 0.95f) {
-            zRatio = 0.95f;
-        }
-        SkScalar blurRadius = lightWidth*zRatio;
-
-        // compute the transformation params
-        SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
-        SkMatrix ctmInverse;
-        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
-            return;
-        }
-        SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
-        ctmInverse.mapPoints(&lightPos2D, 1);
-        SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
-                                       zRatio*(center.fY - lightPos2D.fY));
-        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
-
-        SkAutoCanvasRestore acr(canvas, true);
-
-        sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
-                                                        SkBlurMask::ConvertRadiusToSigma(blurRadius),
-                                                        SkBlurMaskFilter::kNone_BlurFlag);
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setMaskFilter(std::move(mf));
-        paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
-                                                ? 255 
-                                                : (unsigned char)(spotAlpha*255.999f), 0, 0, 0));
-
-        // apply transformation to shadow
-        canvas->scale(scale, scale);
-        canvas->translate(offset.fX, offset.fY);
-        canvas->drawPath(path, paint);
-    }
-
-    void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
-                        SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
-        if (spotAlpha <= 0) {
-            return;
-        }
-
-        SkScalar zRatio = zValue / (lightPos.fZ - zValue);
-        if (zRatio < 0.0f) {
-            zRatio = 0.0f;
-        } else if (zRatio > 0.95f) {
-            zRatio = 0.95f;
-        }
-        SkScalar radius = 2.0f*lightWidth*zRatio;
-
-        SkRect pathRect;
-        SkRRect pathRRect;
-        SkScalar scaleFactors[2];
-        if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
-            return;
-        }
-        if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
-            !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
-              (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
-              path.isRect(&pathRect))) {
-            this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
-            return;
-        }
-
-        // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
-        const SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
-        if (path.isOval(nullptr)) {
-            pathRRect = SkRRect::MakeOval(pathRect);
-        } else if (path.isRect(nullptr)) {
-            pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
-        } else {
-            if (pathRRect.getSimpleRadii().fX < minRadius) {
-                pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
-            }
-        }
-
-        // compute the scale and translation for the shadow
-        SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
-        SkRRect shadowRRect;
-        pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
-        SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
-        SkMatrix ctmInverse;
-        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
-            return;
-        }
-        SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
-        ctmInverse.mapPoints(&lightPos2D, 1);
-        SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
-                                       zRatio*(center.fY - lightPos2D.fY));
-        SkAutoCanvasRestore acr(canvas, true);
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        // We want to extend the stroked area in so that it meets up with the caster
-        // geometry. The stroked geometry will, by definition already be inset half the
-        // stroke width but we also have to account for the scaling.
-        // We also add 1/2 to cover up AA on the interior edge.
-        SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(pathRect.fLeft),
-                                                              SkTAbs(pathRect.fRight)),
-                                                       SkTMax(SkTAbs(pathRect.fTop),
-                                                              SkTAbs(pathRect.fBottom)));
-        SkScalar insetAmount = offset.length() - (0.5f * radius) + scaleOffset + 0.5f;
-
-        // compute area
-        SkScalar strokeWidth = radius + insetAmount;
-        SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
-        SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
-        // If the area of the stroked geometry is larger than the fill geometry, or
-        // if our pad is too big to convert to 6.2 fixed point, just fill it.
-        if (strokedArea > filledArea) {
-            paint.setStyle(SkPaint::kStrokeAndFill_Style);
-            paint.setStrokeWidth(radius);
-        } else {
-            // Since we can't have unequal strokes, inset the shadow rect so the inner
-            // and outer edges of the stroke will land where we want.
-            SkRect insetRect = shadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
-            SkScalar insetRad = SkTMax(shadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
-                                       minRadius);
-
-            shadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
-            paint.setStyle(SkPaint::kStroke_Style);
-            paint.setStrokeWidth(strokeWidth);
-        }
-        paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
-        paint.setShader(SkGaussianEdgeShader::Make());
-        // handle scale of radius due to CTM
-        radius *= scaleFactors[0];
-        // don't need to scale pad as it was computed from the transformed offset
-        SkASSERT(radius < 16384);
-        SkScalar pad = 0;
-        SkASSERT(pad < 64);
-        // Convert radius to 14.2 fixed point and place in the R & G components.
-        // Convert pad to 6.2 fixed point and place in the B component.
-        uint16_t iRadius = (uint16_t)(radius*4.0f);
-        unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
-        paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
-                                      iRadius >> 8, iRadius & 0xff,
-                                      (unsigned char)(4.0f*pad)));
-
-        // apply transformation to shadow
-        canvas->translate(offset.fX, offset.fY);
-        canvas->drawRRect(shadowRRect, paint);
-    }
-
     void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
-                          std::function<SkScalar(SkScalar, SkScalar)> zFunc,
+                          const SkPoint3& zPlaneParams,
                           const SkPaint& paint, SkScalar ambientAlpha,
                           const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
-#ifdef USE_SHADOW_UTILS
-        SkScalar zValue = zFunc(0, 0);
+        if (fIgnoreShadowAlpha) {
+            ambientAlpha = 255;
+            spotAlpha = 255;
+        }
+        if (!fShowAmbient) {
+            ambientAlpha = 0;
+        }
+        if (!fShowSpot) {
+            spotAlpha = 0;
+        }
+        uint32_t flags = 0;
         if (fUseAlt) {
-            if (fShowAmbient) {
-                this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
-            }
-            if (fShowSpot) {
-                this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
-            }
-        } else {
-            if (!fShowAmbient) {
-                ambientAlpha = 0;
-            }
-            if (!fShowSpot) {
-                spotAlpha = 0;
-            }
-
-            //SkShadowUtils::DrawShadow(canvas, path,
-            //                          zValue,
-            //                          lightPos, lightWidth,
-            //                          ambientAlpha, spotAlpha, SK_ColorBLACK);
-            SkShadowUtils::DrawUncachedShadow(canvas, path, zFunc,
-                                              lightPos, lightWidth,
-                                              ambientAlpha, spotAlpha, SK_ColorBLACK);
+            flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
         }
-#else
-        if (fShowAmbient) {
-            if (fUseAlt) {
-                this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
-            } else {
-                this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
-            }
-        }
-        if (fShowSpot) {
-            if (fUseAlt) {
-                this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
-            } else {
-                this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
-            }
-        }
-#endif
+        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+                                  lightPos, lightWidth,
+                                  ambientAlpha, spotAlpha, SK_ColorBLACK, flags);
 
         if (fShowObject) {
             canvas->drawPath(path, paint);
@@ -465,65 +162,50 @@
 
     void onDrawContent(SkCanvas* canvas) override {
         this->drawBG(canvas);
-        const SkScalar kLightWidth = 2800;
-        const SkScalar kAmbientAlpha = 0.25f;
+        const SkScalar kLightWidth = 800;
+        const SkScalar kAmbientAlpha = 0.1f;
         const SkScalar kSpotAlpha = 0.25f;
 
         SkPaint paint;
         paint.setAntiAlias(true);
 
         SkPoint3 lightPos = fLightPos;
+        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, 0);
 
         paint.setColor(SK_ColorWHITE);
         canvas->translate(200, 90);
-        lightPos.fX += 200;
-        lightPos.fY += 90;
-        SkScalar zValue = SkTMax(1.0f, 2 + fZDelta);
-        std::function<SkScalar(SkScalar, SkScalar)> zFunc = 
-            [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
+        zPlaneParams.fZ = SkTMax(1.0f, 2 + fZDelta);
+        this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, kAmbientAlpha,
                                lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorRED);
         canvas->translate(250, 0);
-        lightPos.fX += 250;
-        zValue = SkTMax(1.0f, 4 + fZDelta);
-        zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, fRectPath, zFunc, paint, kAmbientAlpha,
+        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
+        this->drawShadowedPath(canvas, fRectPath, zPlaneParams, paint, kAmbientAlpha,
                                lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorBLUE);
         canvas->translate(-250, 110);
-        lightPos.fX -= 250;
-        lightPos.fY += 110;
-        zValue = SkTMax(1.0f, 8 + fZDelta);
-        zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, fCirclePath, zFunc, paint, kAmbientAlpha,
+        zPlaneParams.fZ = SkTMax(1.0f, 12 + fZDelta);
+        this->drawShadowedPath(canvas, fCirclePath, zPlaneParams, paint, kAmbientAlpha,
                                lightPos, kLightWidth, 0.5f);
 
         paint.setColor(SK_ColorGREEN);
         canvas->translate(250, 0);
-        lightPos.fX += 250;
-        zValue = SkTMax(1.0f, 64 + fZDelta);
-        zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
+        zPlaneParams.fZ = SkTMax(1.0f, 64 + fZDelta);
+        this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, kAmbientAlpha,
                                lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorYELLOW);
         canvas->translate(-250, 110);
-        lightPos.fX -= 250;
-        lightPos.fY += 110;
-        zValue = SkTMax(1.0f, 8 + fZDelta);
-        zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, fFunkyRRPath, zFunc, paint, kAmbientAlpha,
+        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
+        this->drawShadowedPath(canvas, fFunkyRRPath, zPlaneParams, paint, kAmbientAlpha,
                                lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorCYAN);
         canvas->translate(250, 0);
-        lightPos.fX += 250;
-        zValue = SkTMax(1.0f, 16 + fZDelta);
-        zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, fCubicPath, zFunc, paint,
+        zPlaneParams.fZ = SkTMax(1.0f, 16 + fZDelta);
+        this->drawShadowedPath(canvas, fCubicPath, zPlaneParams, paint,
                                kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
 
         // circular reveal
@@ -534,11 +216,8 @@
 
         paint.setColor(SK_ColorMAGENTA);
         canvas->translate(-125, 60);
-        lightPos.fX -= 125;
-        lightPos.fY += 60;
-        zValue = SkTMax(1.0f, 32 + fZDelta);
-        zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
-        this->drawShadowedPath(canvas, tmpPath, zFunc, paint, .1f,
+        zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
+        this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
         // perspective paths
@@ -554,16 +233,11 @@
         persp.preTranslate(-pivot.fX, -pivot.fY);
         persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
         canvas->setMatrix(persp);
-        lightPos = fLightPos;
-        lightPos.fX += pivot.fX + translate.fX;
-        lightPos.fY += pivot.fY + translate.fY;
-        zValue = SkTMax(1.0f, 16 + fZDelta);
         SkScalar radians = SkDegreesToRadians(fAnimAngle);
-        zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
-            return SkScalarSin(-radians)*y +
-                   zValue - SkScalarSin(-radians)*pivot.fY;
-        };
-        this->drawShadowedPath(canvas, fWideRectPath, zFunc, paint, .1f,
+        zPlaneParams = SkPoint3::Make(0,
+                                      SkScalarSin(-radians),
+                                      SkTMax(1.0f, 16 + fZDelta) - SkScalarSin(-radians)*pivot.fY);
+        this->drawShadowedPath(canvas, fWideRectPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
         pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
@@ -575,15 +249,10 @@
         persp.preTranslate(-pivot.fX, -pivot.fY);
         persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
         canvas->setMatrix(persp);
-        lightPos = fLightPos;
-        lightPos.fX += pivot.fX + translate.fX;
-        lightPos.fY += pivot.fY + translate.fY;
-        zValue = SkTMax(1.0f, 32 + fZDelta);
-        zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
-            return -SkScalarSin(radians)*x +
-                zValue + SkScalarSin(radians)*pivot.fX;
-        };
-        this->drawShadowedPath(canvas, fWideOvalPath, zFunc, paint, .1f,
+        zPlaneParams = SkPoint3::Make(-SkScalarSin(radians),
+                                      0,
+                                      SkTMax(1.0f, 32 + fZDelta) + SkScalarSin(radians)*pivot.fX);
+        this->drawShadowedPath(canvas, fWideOvalPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
     }
 
@@ -594,27 +263,6 @@
         return true;
     }
 
-protected:
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
-        return new SkView::Click(this);
-    }
-
-    bool onClick(Click *click) override {
-        SkScalar x = click->fCurr.fX;
-        SkScalar y = click->fCurr.fY;
-
-        SkScalar dx = x - click->fPrev.fX;
-        SkScalar dy = y - click->fPrev.fY;
-
-        if (dx != 0 || dy != 0) {
-            fLightPos.fX += dx;
-            fLightPos.fY += dy;
-            this->inval(nullptr);
-        }
-
-        return true;
-    }
-
 private:
     typedef SampleView INHERITED;
 };
diff --git a/samplecode/SampleAnimatedText.cpp b/samplecode/SampleAnimatedText.cpp
index 2b047c2..29bd192 100644
--- a/samplecode/SampleAnimatedText.cpp
+++ b/samplecode/SampleAnimatedText.cpp
@@ -11,6 +11,7 @@
 #include "SkUtils.h"
 #include "SkColorPriv.h"
 #include "SkColorFilter.h"
+#include "SkImage.h"
 #include "SkRandom.h"
 #include "SkSystemEventTypes.h"
 #include "SkTime.h"
@@ -126,8 +127,8 @@
         canvas->restore();
 
         paint.setTextSize(16);
-//        canvas->drawText(outString.c_str(), outString.size(), 512.f, 540.f, paint);
-        canvas->drawText(modeString.c_str(), modeString.size(), 768.f, 540.f, paint);
+//        canvas->drawString(outString, 512.f, 540.f, paint);
+        canvas->drawString(modeString, 768.f, 540.f, paint);
     }
 
     bool onAnimate(const SkAnimTimer& timer) override {
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index bd7d567..16dbef2 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -35,6 +35,7 @@
 #include "sk_tool_utils.h"
 #include "SkScan.h"
 #include "SkClipOpPriv.h"
+#include "SkThreadedBMPDevice.h"
 
 #include "SkReadBuffer.h"
 #include "SkStream.h"
@@ -382,6 +383,7 @@
     }
 
     void windowSizeChanged(SampleWindow* win) override {
+        win->resetFPS();
 #if SK_SUPPORT_GPU
         if (fCurContext) {
             AttachmentInfo attachmentInfo;
@@ -1063,7 +1065,7 @@
 static SkBitmap capture_bitmap(SkCanvas* canvas) {
     SkBitmap bm;
     if (bm.tryAllocPixels(canvas->imageInfo())) {
-        canvas->readPixels(&bm, 0, 0);
+        canvas->readPixels(bm, 0, 0);
     }
     return bm;
 }
@@ -1090,6 +1092,14 @@
 #include "SkDumpCanvas.h"
 
 void SampleWindow::draw(SkCanvas* canvas) {
+    std::unique_ptr<SkThreadedBMPDevice> tDev;
+    std::unique_ptr<SkCanvas> tCanvas;
+    if (fTiles > 0 && fDeviceType == kRaster_DeviceType) {
+        tDev.reset(new SkThreadedBMPDevice(this->getBitmap(), fTiles, fThreads));
+        tCanvas.reset(new SkCanvas(tDev.get()));
+        canvas = tCanvas.get();
+    }
+
     gAnimTimer.updateTime();
 
     if (fGesture.isActive()) {
@@ -1153,6 +1163,8 @@
         this->inval(nullptr);
     }
 
+    canvas->flush();
+
     // do this last
     fDevManager->publishCanvas(fDeviceType, canvas, this);
 }
@@ -1271,7 +1283,6 @@
     else if (fMouseY < 0) fMouseY = 0;
 
     SkBitmap bitmap = capture_bitmap(canvas);
-    bitmap.lockPixels();
 
     // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle.
     int zoomedWidth = (width >> 1) | 1;
@@ -1470,6 +1481,8 @@
         orig->flush();
         fTimer.end();
         fMeasureFPS_Time += fTimer.fWall;
+        fCumulativeFPS_Time += fTimer.fWall;
+        fCumulativeFPS_Count += FPS_REPEAT_COUNT;
     }
 }
 
@@ -1572,6 +1585,7 @@
     this->inval(nullptr);
 }
 bool SampleWindow::previousSample() {
+    this->resetFPS();
     fCurrIndex = (fCurrIndex - 1 + fSamples.count()) % fSamples.count();
     this->loadView((*fSamples[fCurrIndex])());
     return true;
@@ -1580,6 +1594,7 @@
 #include "SkResourceCache.h"
 #include "SkGlyphCache.h"
 bool SampleWindow::nextSample() {
+    this->resetFPS();
     fCurrIndex = (fCurrIndex + 1) % fSamples.count();
     this->loadView((*fSamples[fCurrIndex])());
 
@@ -1593,6 +1608,7 @@
 }
 
 bool SampleWindow::goToSample(int i) {
+    this->resetFPS();
     fCurrIndex = (i) % fSamples.count();
     this->loadView((*fSamples[fCurrIndex])());
     return true;
@@ -1843,12 +1859,35 @@
                 this->inval(nullptr);
             }
             break;
+        case '+':
+            gSampleWindow->setTiles(gSampleWindow->getTiles() + 1);
+            this->inval(nullptr);
+            this->updateTitle();
+            break;
+        case '-':
+            gSampleWindow->setTiles(SkTMax(0, gSampleWindow->getTiles() - 1));
+            this->inval(nullptr);
+            this->updateTitle();
+            break;
+        case '>':
+            gSampleWindow->setThreads(gSampleWindow->getThreads() + 1);
+            this->inval(nullptr);
+            this->updateTitle();
+            break;
+        case '<':
+            gSampleWindow->setThreads(SkTMax(0, gSampleWindow->getThreads() - 1));
+            this->inval(nullptr);
+            this->updateTitle();
+            break;
         case ' ':
             gAnimTimer.togglePauseResume();
             if (this->sendAnimatePulse()) {
                 this->inval(nullptr);
             }
             break;
+        case '0':
+            this->resetFPS();
+            break;
         case 'A':
             if (gSkUseAnalyticAA.load() && !gSkForceAnalyticAA.load()) {
                 gSkForceAnalyticAA = true;
@@ -1981,6 +2020,11 @@
     this->inval(nullptr);
 }
 
+void SampleWindow::resetFPS() {
+    fCumulativeFPS_Time = 0;
+    fCumulativeFPS_Count = 0;
+}
+
 void SampleWindow::toggleDistanceFieldFonts() {
     // reset backend
     fDevManager->tearDownBackend(this);
@@ -2208,6 +2252,10 @@
 
     title.prepend(gDeviceTypePrefix[fDeviceType]);
 
+    if (gSampleWindow->getTiles()) {
+        title.prependf("[T%d/%d] ", gSampleWindow->getTiles(), gSampleWindow->getThreads());
+    }
+
     if (gSkUseAnalyticAA) {
         if (gSkForceAnalyticAA) {
             title.prepend("<FAAA> ");
@@ -2255,6 +2303,7 @@
 
     if (fMeasureFPS) {
         title.appendf(" %8.4f ms", fMeasureFPS_Time / (float)FPS_REPEAT_COUNT);
+        title.appendf(" -> %4.4f ms", fCumulativeFPS_Time / (float)SkTMax(1, fCumulativeFPS_Count));
     }
 
 #if SK_SUPPORT_GPU
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
index e4c1f38..6ddb5c5 100644
--- a/samplecode/SampleApp.h
+++ b/samplecode/SampleApp.h
@@ -134,6 +134,7 @@
     void toggleRendering();
     void toggleSlideshow();
     void toggleFPS();
+    void resetFPS();
     void showOverview();
     void toggleDistanceFieldFonts();
     void setPixelGeometry(int pixelGeometryIndex);
@@ -156,6 +157,11 @@
     DeviceType getDeviceType() const { return fDeviceType; }
     int getColorConfigIndex() const { return fColorConfigIndex; }
 
+    int getTiles() const { return fTiles; }
+    void setTiles(int tiles) { fTiles = tiles; }
+    int getThreads() const { return fThreads; }
+    void setThreads(int threads) { fThreads = threads; }
+
 protected:
     void onDraw(SkCanvas* canvas) override;
     bool onHandleKey(SkKey key) override;
@@ -210,6 +216,8 @@
     bool fUseDeferredCanvas;
     WallTimer fTimer;
     double fMeasureFPS_Time;
+    double fCumulativeFPS_Time;
+    int    fCumulativeFPS_Count;
     bool fMagnify;
     int fTilingMode;
 
@@ -240,6 +248,9 @@
     //Stores slide specific settings
     SkOSMenu* fSlideMenu; // We pass ownership to SkWindow, when we call addMenu
 
+    int fTiles = 0;
+    int fThreads = 0;
+
     void loadView(SkView*);
     void updateTitle();
     bool getRawTitle(SkString*);
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
index f95833e..abdcdec 100644
--- a/samplecode/SampleArc.cpp
+++ b/samplecode/SampleArc.cpp
@@ -127,7 +127,7 @@
         str.appendScalar(start);
         str.append(", ");
         str.appendScalar(sweep);
-        canvas->drawText(str.c_str(), str.size(), rect.centerX(),
+        canvas->drawString(str, rect.centerX(),
                          rect.fBottom + paint.getTextSize() * 5/4, paint);
     }
 
diff --git a/samplecode/SampleBevel.cpp b/samplecode/SampleBevel.cpp
deleted file mode 100644
index fce8885..0000000
--- a/samplecode/SampleBevel.cpp
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
- * 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 "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkLightingShader.h"
-#include "SkNormalSource.h"
-#include "sk_tool_utils.h"
-
-class ParentControl;
-
-// Abstract base class for all components that a control panel must have
-class Control : public SkRefCnt {
-public:
-    Control(SkString name)
-        : fName(name)
-        , fParent(nullptr)
-        , fRelativePos(SkPoint::Make(0.0f, 0.0f)) {}
-
-    // Use this to propagate a click's position down to a control. Gets modulated by the component's
-    // relative position
-    bool click(const SkPoint& clickPos) {
-        SkPoint relativeClickPos = SkPoint::Make(clickPos.fX - fRelativePos.fX,
-                                                 clickPos.fY - fRelativePos.fY);
-        return this->onClick(relativeClickPos);
-    }
-
-    // Use this to draw the control and its appropriate children. Gets modulated by the component's
-    // relative position.
-    void drawContent(SkCanvas *canvas) {
-        canvas->save();
-        canvas->translate(fRelativePos.fX, fRelativePos.fY);
-        this->onDrawContent(canvas);
-        canvas->restore();
-    }
-
-    /* Returns true when click position argumend lands over a control region in this control. Click
-     * position gets modulated by the component's relative position.
-     *
-     * @param click The position of the click in the coordinate space relative to the parent
-     */
-    bool isInCtrlRegion(const SkPoint& click) {
-        SkPoint relativeClickPos = SkPoint::Make(click.fX - fRelativePos.fX,
-                                                 click.fY - fRelativePos.fY);
-        return this->onIsInCtrlRegion(relativeClickPos);
-    }
-
-    // Returns height of content drawn
-    virtual SkScalar height() const = 0;
-
-    // Sets the parent of this component. May only be used once. Height must remain constant after
-    // parent is set.
-    void setParent(ParentControl *parent, const SkPoint& relativePos) {
-        SkASSERT(parent);
-        SkASSERT(!fParent); // No chidren transfer since relativeY would get invalid for younger kid
-
-        fParent = parent;
-        fRelativePos = relativePos;
-        this->onSetParent();
-    }
-
-    // Overriden by sub-classes that need to recompute fields after parent is set. Called after
-    // setting fParent.
-    virtual void onSetParent() {}
-
-    // Overriden by sub-classes that need to know when a click is released.
-    virtual void onClickRelease() {}
-
-protected:
-
-    // Draws a label for the component, using its name and a passed value. Does NOT modulate by
-    // relative height, expects CTM to have been adjusted in advance.
-    void drawLabel(SkCanvas *canvas, const SkString& valueStr) const {
-        // TODO Cache this
-        sk_sp<SkTypeface> fLabelTypeface =
-                sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
-
-        SkString label;
-        label.append(fName);
-        label.append(": ");
-        label.append(valueStr);
-
-        SkPaint labelPaint;
-        labelPaint.setTypeface(fLabelTypeface);
-        labelPaint.setAntiAlias(true);
-        labelPaint.setColor(0xFFFFFFFF);
-        labelPaint.setTextSize(12.0f);
-
-        canvas->drawText(label.c_str(), label.size(), 0, kLabelHeight - 6.0f, labelPaint);
-    }
-
-    SkString fName;
-    ParentControl* fParent;
-
-    static constexpr SkScalar kLabelHeight = 20.0f;
-
-private:
-    // Overriden by sub-class to draw component. Do not call directly, drawContent() modulates by
-    // relative position.
-    virtual void onDrawContent(SkCanvas *canvas) = 0;
-
-    // Overriden by sub-class to handle clicks. Do not call directly, click() modulates by relative
-    // position. Return true if holding mouse capture
-    virtual bool onClick(const SkPoint& clickPos) { return false; }
-
-    // Overriden by sub-classes with controls. Should return true if clickPos lands inside a control
-    // region, to enable mouse caputre.
-    virtual bool onIsInCtrlRegion(const SkPoint& clickPos) const { return false; }
-
-    // The position of the control relative to it's parent
-    SkPoint fRelativePos;
-};
-
-class ParentControl : public Control { // Interface for all controls that have children
-public:
-    ParentControl(const SkString& name) : INHERITED(name) {}
-
-    // Adds a child
-    virtual void add(sk_sp<Control> control) = 0;
-
-    // Returns the control's width. Used to propagate width down to components that don't specify it
-    virtual SkScalar width() const = 0;
-
-private:
-    typedef Control INHERITED;
-};
-
-class ControlPanel : public ParentControl {
-public:
-
-    ControlPanel(SkScalar width)
-        : ParentControl(SkString("ControlPanel"))
-        , fWidth(width)
-        , fHeight(0.0f)
-        , fSelectedControl(-1) {}
-
-    // Width unspecified, expectation is inheritance from parent
-    ControlPanel() : ControlPanel(-1.0f) {}
-
-    // Use this for introducing clicks on a ControlPanel from outside of the framework. It
-    // propagates click release or position down the chain. Returns false when click capture is
-    // being released.
-    bool inClick(SkView::Click *inClick) {
-        if (SkView::Click::State::kUp_State == inClick->fState) {
-            this->onClickRelease();
-            return false;
-        }
-        return this->click(inClick->fCurr);
-    }
-
-    // Add children
-    void add(sk_sp<Control> control) override {
-        SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability
-        fControls.push_back(control);
-        control->setParent(this, SkPoint::Make(0.0f, fHeight));
-        fHeight += control->height();
-    }
-
-    SkScalar width() const override {
-        return fParent ? fParent->width() : fWidth; // Width inherited from parent if there is one
-    }
-
-    SkScalar height() const override {
-        return fHeight;
-    }
-
-    // Propagate click release to selected control, deselect control
-    void onClickRelease() override {
-        if (fSelectedControl >= 0) {
-            fControls[fSelectedControl]->onClickRelease();
-        }
-        fSelectedControl = -1;
-    }
-
-    // Propagate onSetParent() down to children, some might need fParent->width() refresh
-    void onSetParent() override {
-        for (int i = 0; i < fControls.count(); i++) {
-            fControls[i]->onSetParent();
-        }
-    }
-
-    // Holds a vertical shelf of controls. Can't be hierarchy root if not given a width value.
-    static sk_sp<ParentControl> Make() {
-        return sk_sp<ParentControl>(new ControlPanel());
-    }
-
-    // Holds a vertical shelf of controls. Only control that can be hooked from outside the
-    // framework.
-    static sk_sp<ParentControl> Make(SkScalar width) {
-        return sk_sp<ParentControl>(new ControlPanel(width));
-    }
-
-protected:
-    // Returns true if control panel has mouse captured, false when it is ready to release
-    // capture
-    bool onClick(const SkPoint& click) override {
-
-        if (fSelectedControl == -1) { // If no child control selected, check every child
-            for (int i = 0; i < fControls.count(); i++) {
-                if (fControls[i]->isInCtrlRegion(click)) {
-                    fSelectedControl = i;
-                    break;
-                }
-            }
-        }
-
-        if (fSelectedControl >= 0) { // If child control selected, propagate click
-            bool keepSelection = fControls[fSelectedControl]->click(click);
-            if (!keepSelection) {
-                fSelectedControl = -1;
-            }
-            return keepSelection;
-        }
-
-        return false;
-    }
-
-    // Draw all children
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->save();
-        for (int i = 0; i < fControls.count(); i++) {
-            fControls[i]->drawContent(canvas);
-        }
-        canvas->restore();
-    }
-
-    // Check all children's control regions
-    bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
-        for (int i = 0; i < fControls.count(); i++) {
-            if (fControls[i]->isInCtrlRegion(clickPos)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-private:
-    SkScalar fWidth;
-    SkScalar fHeight;
-
-    SkTArray<sk_sp<Control>> fControls;
-    int fSelectedControl;
-};
-
-class DiscreteSliderControl : public Control {
-public:
-    SkScalar height() const override {
-        return 2.0f * kLabelHeight;
-    }
-
-    // Set width-dependant variables when new parent is set
-    void onSetParent() override {
-        fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight);
-        fSliderRange = fParent->width() - kSliderWidth;
-    }
-
-    /* Make a slider for an integer value. Snaps to discrete positions.
-     *
-     * @params name    The name of the control, displayed in the label
-     * @params output  Pointer to the integer that will be set by the slider
-     * @params min     Min value for output.
-     * @params max     Max value for output.
-     */
-    static sk_sp<Control> Make(SkString name, int* output, int min, int max) {
-        return sk_sp<Control>(new DiscreteSliderControl(name, output, min, max));
-    }
-
-protected:
-    void onDrawContent(SkCanvas* canvas) override {
-        SkASSERT(fParent);
-        int numChoices = fMax - fMin + 1;
-        fSlider.offsetTo(fSliderRange * ( (*fOutput)/SkIntToScalar(numChoices)
-                                          + 1.0f/(2.0f * numChoices) ),
-                         fSlider.fTop);
-
-        SkString valueStr;
-        valueStr.appendS32(*fOutput);
-        this->drawLabel(canvas, valueStr);
-
-        SkPaint sliderPaint;
-        sliderPaint.setColor(0xFFF3F3F3);
-        canvas->drawRect(fSlider, sliderPaint);
-
-        SkPaint ctrlRegionPaint;
-        ctrlRegionPaint.setColor(0xFFFFFFFF);
-        ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
-        ctrlRegionPaint.setStrokeWidth(2.0f);
-        canvas->drawRect(fCtrlRegion, ctrlRegionPaint);
-    }
-
-    bool onClick(const SkPoint& clickPos) override {
-        SkASSERT(fParent);
-        SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange);
-        int numChoices = fMax - fMin + 1;
-        *fOutput = SkTMin(SkScalarFloorToInt(numChoices * x / fSliderRange) + fMin, fMax);
-
-        return true;
-    }
-
-    bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
-        SkASSERT(fParent);
-        return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1));
-    }
-
-private:
-    DiscreteSliderControl(SkString name, int* output, int min, int max)
-            : INHERITED(name)
-            , fOutput(output)
-            , fMin(min)
-            , fMax(max) {
-        fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight);
-    }
-
-    int* fOutput;
-    int fMin;
-    int fMax;
-    SkRect fSlider; // The rectangle that slides
-    // The region in which the rectangle slides. Also the region in which mouse is caputred
-    SkRect fCtrlRegion;
-    SkScalar fSliderRange; // The width in pixels over which the slider can slide
-
-    static constexpr SkScalar kSliderHeight = 20.0f;
-    static constexpr SkScalar kSliderWidth = 10.0f;
-
-    typedef Control INHERITED;
-};
-
-class ControlSwitcher : public ParentControl {
-public:
-    // Add children
-    void add(sk_sp<Control> control) override {
-        SkASSERT(!fParent); // Validity of parent's relativeY and fHeight depends on immutability
-        fControls.push_back(control);
-        control->setParent(this, SkPoint::Make(0.0f, kSelectorHeight));
-        fHeight = SkMaxScalar(fHeight, control->height()); // Setting height to max child height.
-    }
-
-    SkScalar width() const override { return fParent ? (fParent->width()) : 0; }
-
-    SkScalar height() const override {
-        return fHeight;
-    }
-
-    // Propagate onClickRelease to control that currently captures mouse
-    void onClickRelease() override {
-        if (fCtrlOnClick) {
-            fCtrlOnClick->onClickRelease();
-        }
-        fCtrlOnClick = nullptr;
-    }
-
-    void onSetParent() override {
-        for (int i = 0; i < fControls.count(); i++) {
-            fControls[i]->onSetParent(); // Propagate to children
-        }
-
-        // Finalize control selector
-        // TODO can be moved to constructor if list-initialized
-        if (!finalizedChildren) {
-            fControlSelector = DiscreteSliderControl::Make(
-                    SkString(fName), &fSelectedControl, 0, fControls.count()-1);
-            fControlSelector->setParent(this, SkPoint::Make(0.0f, 0.0f));
-            fHeight += kSelectorHeight;
-
-            SkASSERT(fControlSelector->height() <= kSelectorHeight);
-        }
-    }
-
-    /* A set of a selector and a list of controls. Displays the control from the list of controls
-     * with the index set by the aforementioned selector.
-     *
-     * @param name The name of the switcher. Will be displayed in the selector's label.
-     */
-    static sk_sp<ParentControl> Make(const SkString& name) {
-        return sk_sp<ParentControl>(new ControlSwitcher(name));
-    }
-
-protected:
-    // Draw selector and currently selected control
-    void onDrawContent(SkCanvas* canvas) override {
-        fControlSelector->drawContent(canvas);
-        fControls[fSelectedControl]->drawContent(canvas);
-    }
-
-    // Returns true if control panel has mouse captured, false when it is ready to release
-    // capture
-    bool onClick(const SkPoint& click) override {
-        if (!fCtrlOnClick) {
-            if (fControlSelector->isInCtrlRegion(click)) {
-                fCtrlOnClick = fControlSelector.get();
-            } else if (fControls[fSelectedControl]->isInCtrlRegion(click)) {
-                fCtrlOnClick = fControls[fSelectedControl].get();
-            }
-        }
-        if (fCtrlOnClick) {
-            return fCtrlOnClick->click(click);
-        }
-
-        return false;
-    }
-
-    // Is in control region of selector or currently selected control
-    bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
-        if (fControlSelector->isInCtrlRegion(clickPos)) {
-            return true;
-        }
-        if (fControls[fSelectedControl]->isInCtrlRegion(clickPos)) {
-            return true;
-        }
-
-        return false;
-    }
-
-private:
-    ControlSwitcher(const SkString& name)
-        : INHERITED(name)
-        , fHeight(0.0)
-        , fSelectedControl(0)
-        , fCtrlOnClick(nullptr){}
-
-    bool finalizedChildren = false;
-
-    sk_sp<Control> fControlSelector;
-    SkScalar fHeight;
-    SkTArray<sk_sp<Control>> fControls;
-    int fSelectedControl;
-
-    Control* fCtrlOnClick;
-
-    static constexpr SkScalar kSelectorHeight = 40.0f;
-
-    typedef ParentControl INHERITED;
-};
-
-class ContinuousSliderControl : public Control {
-public:
-    SkScalar height() const override {
-        return 2.0f * kLabelHeight;
-    }
-
-    void onSetParent() override {
-        fSlider = SkRect::MakeXYWH(0, kLabelHeight, kSliderWidth, kSliderHeight);
-        fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight, fParent->width(), kSliderHeight);
-        fSliderRange = fParent->width() - kSliderWidth;
-    }
-
-    /* Make a slider for an SkScalar.
-     *
-     * @params name    The name of the control, displayed in the label
-     * @params output  Pointer to the SkScalar that will be set by the slider
-     * @params min     Min value for output
-     * @params max     Max value for output
-     */
-    static sk_sp<Control> Make(const SkString& name, SkScalar* output, SkScalar min, SkScalar max) {
-       return sk_sp<Control>(new ContinuousSliderControl(name, output, min, max));
-    }
-
-protected:
-    void onDrawContent(SkCanvas* canvas) override {
-        SkASSERT(fParent);
-        SkScalar x = fSliderRange * (*fOutput - fMin) / (fMax - fMin);
-        fSlider.offsetTo(SkScalarPin(x, 0.0f, fSliderRange), fSlider.fTop);
-
-        SkString valueStr;
-        valueStr.appendScalar(*fOutput);
-        this->drawLabel(canvas, valueStr);
-
-        SkPaint sliderPaint;
-        sliderPaint.setColor(0xFFF3F3F3);
-        canvas->drawRect(fSlider, sliderPaint);
-
-        SkPaint ctrlRegionPaint;
-        ctrlRegionPaint.setColor(0xFFFFFFFF);
-        ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
-        ctrlRegionPaint.setStrokeWidth(2.0f);
-        canvas->drawRect(fCtrlRegion, ctrlRegionPaint);
-    }
-
-    bool onClick(const SkPoint& clickPos) override {
-        SkASSERT(fParent);
-        SkScalar x = SkScalarPin(clickPos.fX, 0.0f, fSliderRange);
-        *fOutput = (x/fSliderRange) * (fMax - fMin) + fMin;
-        return true;
-    }
-
-    bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
-        SkASSERT(fParent);
-        return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY, 1, 1));
-    }
-
-private:
-    ContinuousSliderControl(const SkString& name, SkScalar* output, SkScalar min, SkScalar max)
-            : INHERITED(name)
-            , fOutput(output)
-            , fMin(min)
-            , fMax(max) {}
-
-    SkScalar* fOutput;
-    SkScalar fMin;
-    SkScalar fMax;
-    SkRect fSlider;
-    SkRect fCtrlRegion;
-    SkScalar fSliderRange;
-
-    static constexpr SkScalar kSliderHeight = 20.0f;
-    static constexpr SkScalar kSliderWidth = 10.0f;
-
-    typedef Control INHERITED;
-};
-
-class RadialDirectionControl : public Control {
-public:
-    SkScalar height() const override {
-        return kLabelHeight + 2.0f * kRegionRadius;
-    }
-
-    /* Make a direction selector.
-     *
-     * @params name    The name of the control, displayed in the label
-     * @params output  Pointer to the SkVector that will be set by the slider
-     */
-    static sk_sp<Control> Make(const SkString& name, SkVector* output) {
-        return sk_sp<Control>(new RadialDirectionControl(name, output));
-    }
-
-protected:
-    void onDrawContent(SkCanvas* canvas) override {
-        SkASSERT(fParent);
-
-        SkString valueStr;
-        valueStr.appendf("%.2f, %.2f", fOutput->fX, fOutput->fY);
-        this->drawLabel(canvas, valueStr);
-
-        SkPoint lineEnd = SkPoint::Make(fCtrlRegion.centerX(), fCtrlRegion.centerY())
-                          + (*fOutput * (kRegionRadius - kCapRadius));
-        SkPaint linePaint;
-        linePaint.setColor(0xFFF3F3F3);
-        linePaint.setStrokeWidth(kStrokeWidth);
-        linePaint.setAntiAlias(true);
-        linePaint.setStrokeCap(SkPaint::kRound_Cap);
-        canvas->drawLine(fCtrlRegion.centerX(), fCtrlRegion.centerY(),
-                         lineEnd.fX, lineEnd.fY, linePaint);
-
-        SkPaint ctrlRegionPaint;
-        ctrlRegionPaint.setColor(0xFFFFFFFF);
-        ctrlRegionPaint.setStyle(SkPaint::kStroke_Style);
-        ctrlRegionPaint.setStrokeWidth(2.0f);
-        ctrlRegionPaint.setAntiAlias(true);
-        canvas->drawCircle(fCtrlRegion.centerX(), fCtrlRegion.centerY(), kRegionRadius,
-                           ctrlRegionPaint);
-    }
-
-    bool onClick(const SkPoint& clickPos) override {
-        SkASSERT(fParent);
-        fOutput->fX = clickPos.fX - fCtrlRegion.centerX();
-        fOutput->fY = clickPos.fY - fCtrlRegion.centerY();
-        fOutput->normalize();
-
-        return true;
-    }
-
-    bool onIsInCtrlRegion(const SkPoint& clickPos) const override {
-        SkASSERT(fParent);
-        return fCtrlRegion.contains(SkRect::MakeXYWH(clickPos.fX, clickPos.fY,
-                                                     1, 1));
-    }
-
-private:
-    RadialDirectionControl(const SkString& name, SkVector* output)
-            : INHERITED(name)
-            , fOutput(output) {
-        fCtrlRegion = SkRect::MakeXYWH(0.0f, kLabelHeight,
-                                       kRegionRadius * 2.0f, kRegionRadius * 2.0f);
-    }
-
-    SkVector* fOutput;
-    SkRect fCtrlRegion;
-
-    static constexpr SkScalar kRegionRadius = 50.0f;
-    static constexpr SkScalar kStrokeWidth = 6.0f;
-    static constexpr SkScalar kCapRadius = kStrokeWidth / 2.0f;
-
-    typedef Control INHERITED;
-};
-
-class ColorDisplay: public Control {
-public:
-    SkScalar height() const override {
-        return kHeight;
-    }
-
-    void onSetParent() override {
-        fDisplayRect = SkRect::MakeXYWH(0.0f, kPadding, fParent->width(), kHeight - kPadding);
-    }
-
-    /* Make a display that shows an SkColor3f.
-     *
-     * @params output  Pointer to the SkColor3f that will be displayed
-     */
-    static sk_sp<Control> Make(SkColor3f* input) {
-        return sk_sp<Control>(new ColorDisplay(SkString("ColorDisplay"), input));
-    }
-
-protected:
-    void onDrawContent(SkCanvas* canvas) override {
-        SkASSERT(fParent);
-
-        SkPaint displayPaint;
-        displayPaint.setColor(SkColor4f::FromColor3f(*fInput, 1.0f).toSkColor());
-        canvas->drawRect(fDisplayRect, displayPaint);
-    }
-
-private:
-    ColorDisplay(const SkString& name, SkColor3f* input)
-            : INHERITED(name)
-            , fInput(input) {}
-
-    SkColor3f* fInput;
-    SkRect fDisplayRect;
-
-    static constexpr SkScalar kHeight = 24.0f;
-    static constexpr SkScalar kPadding = 4.0f;
-
-    typedef Control INHERITED;
-};
-
-class BevelView : public SampleView {
-public:
-    BevelView()
-        : fShapeBounds(SkRect::MakeWH(kShapeBoundsSize, kShapeBoundsSize))
-        , fControlPanel(kCtrlRange) {
-        this->setBGColor(0xFF666868); // Slightly colorized gray for contrast
-
-        // Controls
-        fBevelWidth = 25.0f;
-        fBevelHeight = 25.0f;
-        fBevelType = 0;
-
-        int currLight = 0;
-        fLightDefs[currLight++] =
-                {SkVector::Make(0.0f, 1.0f), 1.0f, SkColor3f::Make(0.6f, 0.45f, 0.3f)};
-        fLightDefs[currLight++] =
-                {SkVector::Make(0.0f, -1.0f), 1.0f, SkColor3f::Make(0.3f, 0.45f, 0.6f)};
-        fLightDefs[currLight++] =
-                {SkVector::Make(1.0f, 0.0f), 1.0f, SkColor3f::Make(0.0f, 0.0f, 0.0f)};
-        // Making sure we initialized all lights
-        SkASSERT(currLight == kNumLights);
-
-        fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelWidth"), &fBevelWidth,
-                                                        1.0f, kShapeBoundsSize));
-        fControlPanel.add(ContinuousSliderControl::Make(SkString("BevelHeight"), &fBevelHeight,
-                                                        -50.0f, 50.0f));
-        fControlPanel.add(DiscreteSliderControl::Make(SkString("BevelType"), &fBevelType,
-                                                      0, 2));
-        sk_sp<ParentControl> lightCtrlSelector = ControlSwitcher::Make(SkString("SelectedLight"));
-        for (int i = 0; i < kNumLights; i++) {
-            SkString name("Light");
-            name.appendS32(i);
-            sk_sp<ParentControl> currLightPanel = ControlPanel::Make();
-            SkString dirName(name);
-            dirName.append("Dir");
-            currLightPanel->add(RadialDirectionControl::Make(dirName, &(fLightDefs[i].fDirXY)));
-            SkString heightName(name);
-            heightName.append("Height");
-            currLightPanel->add(ContinuousSliderControl::Make(heightName, &(fLightDefs[i].fDirZ),
-                                                             0.0f, 2.0f));
-            SkString redName(name);
-            redName.append("Red");
-            currLightPanel->add(ContinuousSliderControl::Make(redName, &(fLightDefs[i].fColor.fX),
-                                                              0.0f, 1.0f));
-            SkString greenName(name);
-            greenName.append("Green");
-            currLightPanel->add(ContinuousSliderControl::Make(greenName, &(fLightDefs[i].fColor.fY),
-                                                              0.0f, 1.0f));
-            SkString blueName(name);
-            blueName.append("Blue");
-            currLightPanel->add(ContinuousSliderControl::Make(blueName, &(fLightDefs[i].fColor.fZ),
-                                                              0.0f, 1.0f));
-            currLightPanel->add(ColorDisplay::Make(&(fLightDefs[i].fColor)));
-            lightCtrlSelector->add(currLightPanel);
-        }
-        fControlPanel.add(lightCtrlSelector);
-
-        fControlPanelSelected = false;
-        fDirtyNormalSource = true;
-
-        fLabelTypeface = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle());
-    }
-
-protected:
-    bool onQuery(SkEvent *evt) override {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Bevel");
-            return true;
-        }
-
-        return this->INHERITED::onQuery(evt);
-    }
-
-    enum Shape {
-        kCircle_Shape,
-        kRect_Shape,
-    };
-    void drawShape(enum Shape shape, SkCanvas* canvas) {
-        canvas->save();
-
-        SkPaint paint;
-
-        if (fDirtyNormalSource) {
-            fNormalSource = SkNormalSource::MakeBevel((SkNormalSource::BevelType)fBevelType,
-                                                      fBevelWidth, fBevelHeight);
-            fDirtyNormalSource = false;
-        }
-
-        paint.setShader(SkLightingShader::Make(nullptr, fNormalSource, fLights));
-        paint.setAntiAlias(true);
-        paint.setColor(0xFFDDDDDD);
-        switch (shape) {
-            case kCircle_Shape:
-                canvas->drawCircle(fShapeBounds.centerX(), fShapeBounds.centerY(),
-                                   fShapeBounds.width()/2.0f, paint);
-                break;
-            case kRect_Shape:
-                canvas->drawRect(fShapeBounds, paint);
-                break;
-            default:
-                SkDEBUGFAIL("Invalid shape enum for drawShape");
-        }
-
-        canvas->restore();
-    }
-
-    void onDrawContent(SkCanvas *canvas) override {
-
-        canvas->save();
-        canvas->resetMatrix(); // Force static control panel position
-        fControlPanel.drawContent(canvas);
-        canvas->restore();
-
-        SkLights::Builder builder;
-        for (int i = 0; i < kNumLights; i++) {
-            builder.add(SkLights::Light::MakeDirectional(fLightDefs[i].fColor,
-                                                         SkPoint3::Make(fLightDefs[i].fDirXY.fX,
-                                                                        fLightDefs[i].fDirXY.fY,
-                                                                        fLightDefs[i].fDirZ)));
-        }
-        builder.setAmbientLightColor(SkColor3f::Make(0.4f, 0.4f, 0.4f));
-        fLights = builder.finish();
-
-        // Draw shapes
-        SkScalar xPos = kCtrlRange + 25.0f;
-        SkScalar yPos = fShapeBounds.height();
-        for (Shape shape : { kCircle_Shape, kRect_Shape }) {
-            canvas->save();
-            canvas->translate(xPos, yPos);
-            this->drawShape(shape, canvas);
-            canvas->restore();
-
-            xPos += 1.2f * fShapeBounds.width();
-        }
-    }
-
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
-        return new SkView::Click(this);
-    }
-
-    bool onClick(Click *click) override {
-        // Control panel mouse handling
-        fControlPanelSelected = fControlPanel.inClick(click);
-
-        if (fControlPanelSelected) { // Control modification
-            fDirtyNormalSource = true;
-
-            this->inval(nullptr);
-            return true;
-        }
-
-        // TODO move shapes
-        this->inval(nullptr);
-        return true;
-    }
-
-private:
-    static constexpr int kNumTestRects = 3;
-
-    static constexpr SkScalar kShapeBoundsSize = 120.0f;
-
-    static constexpr SkScalar kCtrlRange = 150.0f;
-
-    static constexpr int kNumLights = 3;
-
-    const SkRect fShapeBounds;
-
-    SkScalar fBevelWidth;
-    SkScalar fBevelHeight;
-    int      fBevelType;
-
-    sk_sp<SkNormalSource> fNormalSource;
-    bool fDirtyNormalSource;
-
-    sk_sp<SkLights> fLights;
-
-    struct LightDef {
-        SkVector fDirXY;
-        SkScalar fDirZ;
-        SkColor3f fColor;
-
-        LightDef() {}
-        LightDef(SkVector dirXY, SkScalar dirZ, SkColor3f color)
-            : fDirXY(dirXY)
-            , fDirZ(dirZ)
-            , fColor(color) {}
-    };
-    LightDef fLightDefs[kNumLights];
-
-    ControlPanel fControlPanel;
-    bool fControlPanelSelected;
-
-    sk_sp<SkTypeface> fLabelTypeface;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new BevelView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
index ebfe3bb..ccf2d26 100644
--- a/samplecode/SampleBitmapRect.cpp
+++ b/samplecode/SampleBitmapRect.cpp
@@ -171,7 +171,7 @@
 
     SkCanvas canvas(*bm);
 
-    canvas.drawText(gText, strlen(gText), 0, paint.getTextSize()*4/5, paint);
+    canvas.drawString(gText, 0, paint.getTextSize()*4/5, paint);
 }
 
 class BitmapRectView2 : public SampleView {
diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp
index 7e6a325..a2f90a7 100644
--- a/samplecode/SampleBlur.cpp
+++ b/samplecode/SampleBlur.cpp
@@ -4,6 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SampleCode.h"
 #include "SkBlurMask.h"
 #include "SkBlurMaskFilter.h"
@@ -20,14 +21,10 @@
     }
 
     SkBitmap bm;
-    SkColorTable* ctable = new SkColorTable(c, 256);
-
     bm.allocPixels(SkImageInfo::Make(256, 256, kIndex_8_SkColorType,
                                      kPremul_SkAlphaType),
-                   nullptr, ctable);
-    ctable->unref();
+                   SkColorTable::Make(c, 256));
 
-    bm.lockPixels();
     const float cx = bm.width() * 0.5f;
     const float cy = bm.height() * 0.5f;
     for (int y = 0; y < bm.height(); y++) {
@@ -45,7 +42,6 @@
             p[x] = id;
         }
     }
-    bm.unlockPixels();
     return bm;
 }
 
@@ -115,13 +111,13 @@
                 SkScalar x = SkIntToScalar(70);
                 SkScalar y = SkIntToScalar(400);
                 paint.setColor(SK_ColorBLACK);
-                canvas->drawText("Hamburgefons Style", 18, x, y, paint);
-                canvas->drawText("Hamburgefons Style", 18, x, y + SkIntToScalar(50), paint);
+                canvas->drawString("Hamburgefons Style", x, y, paint);
+                canvas->drawString("Hamburgefons Style", x, y + SkIntToScalar(50), paint);
                 paint.setMaskFilter(nullptr);
                 paint.setColor(SK_ColorWHITE);
                 x -= SkIntToScalar(2);
                 y -= SkIntToScalar(2);
-                canvas->drawText("Hamburgefons Style", 18, x, y, paint);
+                canvas->drawString("Hamburgefons Style", x, y, paint);
             }
             canvas->restore();
             flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
diff --git a/samplecode/SampleClip.cpp b/samplecode/SampleClip.cpp
index 41192fb..05b5a25 100644
--- a/samplecode/SampleClip.cpp
+++ b/samplecode/SampleClip.cpp
@@ -27,7 +27,7 @@
 
     for (int i = 0; i < 200; ++i) {
         paint.setColor((SK_A32_MASK << SK_A32_SHIFT) | rand.nextU());
-        canvas->drawText("Hamburgefons", 12,
+        canvas->drawString("Hamburgefons",
                          rand.nextSScalar1() * W, rand.nextSScalar1() * H + 20,
                          paint);
     }
diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp
index b9c9307..af7f38e 100644
--- a/samplecode/SampleComplexClip.cpp
+++ b/samplecode/SampleComplexClip.cpp
@@ -129,14 +129,14 @@
                 SkScalar txtX = SkIntToScalar(55);
                 paint.setColor(colorA);
                 const char* aTxt = invA ? "InverseA " : "A ";
-                canvas->drawText(aTxt, strlen(aTxt), txtX, SkIntToScalar(220), paint);
+                canvas->drawString(aTxt, txtX, SkIntToScalar(220), paint);
                 txtX += paint.measureText(aTxt, strlen(aTxt));
                 paint.setColor(SK_ColorBLACK);
-                canvas->drawText(gOps[op].fName, strlen(gOps[op].fName),
+                canvas->drawString(gOps[op].fName,
                                     txtX, SkIntToScalar(220), paint);
                 txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName));
                 paint.setColor(colorB);
-                canvas->drawText("B", 1, txtX, SkIntToScalar(220), paint);
+                canvas->drawString("B", txtX, SkIntToScalar(220), paint);
 
                 canvas->translate(SkIntToScalar(250),0);
             }
diff --git a/samplecode/SampleDegenerateTwoPtRadials.cpp b/samplecode/SampleDegenerateTwoPtRadials.cpp
index f171202..cdd5e1c 100644
--- a/samplecode/SampleDegenerateTwoPtRadials.cpp
+++ b/samplecode/SampleDegenerateTwoPtRadials.cpp
@@ -72,7 +72,7 @@
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setColor(SK_ColorBLACK);
-        canvas->drawText(txt.c_str(), txt.size(), l + w/2 + w*DELTA_SCALE*delta, t + h + SK_Scalar1 * 10, paint);
+        canvas->drawString(txt, l + w/2 + w*DELTA_SCALE*delta, t + h + SK_Scalar1 * 10, paint);
     }
 
     bool onAnimate(const SkAnimTimer& timer) override {
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
index a1b751b..6d91fda 100644
--- a/samplecode/SampleDither.cpp
+++ b/samplecode/SampleDither.cpp
@@ -19,6 +19,7 @@
 #include "SkColorPriv.h"
 #include "SkColorFilter.h"
 #include "SkDither.h"
+#include "sk_tool_utils.h"
 
 static void draw_sweep(SkCanvas* c, int width, int height, SkScalar angle) {
     SkRect  r;
@@ -73,8 +74,6 @@
 }
 
 static void pre_dither(const SkBitmap& bm) {
-    SkAutoLockPixels alp(bm);
-
     for (int y = 0; y < bm.height(); y++) {
         DITHER_4444_SCAN(y);
 
@@ -113,7 +112,7 @@
         make_bm(&fBM);
         make_bm(&fBMPreDither);
         pre_dither(fBMPreDither);
-        fBM.copyTo(&fBM16, kARGB_4444_SkColorType);
+        sk_tool_utils::copy_to(&fBM16, kARGB_4444_SkColorType, fBM);
 
         fAngle = 0;
 
diff --git a/samplecode/SampleDitherBitmap.cpp b/samplecode/SampleDitherBitmap.cpp
index 676d4c3..2f3de35 100644
--- a/samplecode/SampleDitherBitmap.cpp
+++ b/samplecode/SampleDitherBitmap.cpp
@@ -13,6 +13,7 @@
 #include "SkRegion.h"
 #include "SkUtils.h"
 #include "SkView.h"
+#include "sk_tool_utils.h"
 
 static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
     canvas->drawRect(r, p);
@@ -56,21 +57,16 @@
     for (int i = 0; i < 256; i++) {
         c[i] = SkPackARGB32(0xFF, i, 0, 0);
     }
-    SkColorTable* ctable = new SkColorTable(c, 256);
-
     SkBitmap bm;
     bm.allocPixels(SkImageInfo::Make(256, 32, kIndex_8_SkColorType, kPremul_SkAlphaType),
-                   nullptr, ctable);
-    ctable->unref();
+                   SkColorTable::Make(c, 256));
 
-    bm.lockPixels();
     for (int y = 0; y < bm.height(); y++) {
         uint8_t* p = bm.getAddr8(0, y);
         for (int x = 0; x < 256; x++) {
             p[x] = x;
         }
     }
-    bm.unlockPixels();
     return bm;
 }
 
@@ -82,7 +78,7 @@
     DitherBitmapView() {
         fResult = test_pathregion();
         fBM8 = make_bitmap();
-        fBM8.copyTo(&fBM32, kN32_SkColorType);
+        sk_tool_utils::copy_to(&fBM32, kN32_SkColorType, fBM8);
 
         this->setBGColor(0xFFDDDDDD);
     }
@@ -98,7 +94,6 @@
     }
 
     static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) {
-        SkAutoLockPixels alp(*bm);  // needed for ctable
         bm->setAlphaType(isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
     }
 
diff --git a/samplecode/SampleFatBits.cpp b/samplecode/SampleFatBits.cpp
index c49cdad..4e44a7f 100644
--- a/samplecode/SampleFatBits.cpp
+++ b/samplecode/SampleFatBits.cpp
@@ -261,7 +261,7 @@
         r.inset(SK_Scalar1/3, SK_Scalar1/3);
         fMinSurface->getCanvas()->clipRect(r, kIntersect_SkClipOp, true);
     }
-    fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+    fMinSurface->getCanvas()->drawLine(pts[0], pts[1], paint);
     if (fUseClip) {
         fMinSurface->getCanvas()->restore();
     }
@@ -462,7 +462,7 @@
             paint.setAntiAlias(true);
             paint.setTextSize(16);
             paint.setColor(SK_ColorBLUE);
-            canvas->drawText(str.c_str(), str.size(), 10, 16, paint);
+            canvas->drawString(str, 10, 16, paint);
         }
     }
 
diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp
index 66de8f3..60dd7e0 100644
--- a/samplecode/SampleFilter.cpp
+++ b/samplecode/SampleFilter.cpp
@@ -26,11 +26,9 @@
         SkPreMultiplyColor(SK_ColorRED), SkPreMultiplyColor(SK_ColorGREEN),
         SkPreMultiplyColor(SK_ColorBLUE), SkPreMultiplyColor(SK_ColorWHITE)
     };
-    SkColorTable* ctable = new SkColorTable(colors, 4);
     bm->allocPixels(SkImageInfo::Make(2, 2, kIndex_8_SkColorType,
                                       kOpaque_SkAlphaType),
-                    nullptr, ctable);
-    ctable->unref();
+                    SkColorTable::Make(colors, 4));
 
     *bm->getAddr8(0, 0) = 0;
     *bm->getAddr8(1, 0) = 1;
@@ -61,7 +59,7 @@
 
     paint.setAntiAlias(true);
     const char* name = sk_tool_utils::colortype_name(bm.colorType());
-    canvas->drawText(name, strlen(name), x, SkIntToScalar(bm.height())*scale*5/8,
+    canvas->drawString(name, x, SkIntToScalar(bm.height())*scale*5/8,
                      paint);
     canvas->translate(SkIntToScalar(48), 0);
 
@@ -80,9 +78,9 @@
 
     FilterView() {
         make_bm(&fBM8);
-        fBM8.copyTo(&fBM4444, kARGB_4444_SkColorType);
-        fBM8.copyTo(&fBM16, kRGB_565_SkColorType);
-        fBM8.copyTo(&fBM32, kN32_SkColorType);
+        sk_tool_utils::copy_to(&fBM4444, kARGB_4444_SkColorType, fBM8);
+        sk_tool_utils::copy_to(&fBM16, kRGB_565_SkColorType, fBM8);
+        sk_tool_utils::copy_to(&fBM32, kN32_SkColorType, fBM8);
 
         this->setBGColor(0xFFDDDDDD);
     }
diff --git a/samplecode/SampleFilter2.cpp b/samplecode/SampleFilter2.cpp
index 9b2298a..e6134bd 100644
--- a/samplecode/SampleFilter2.cpp
+++ b/samplecode/SampleFilter2.cpp
@@ -89,7 +89,7 @@
                         s.appendS32(paint.isDither());
                         s.append(" filter=");
                         s.appendS32(paint.getFilterQuality() != kNone_SkFilterQuality);
-                        canvas->drawText(s.c_str(), s.size(), x + W/2,
+                        canvas->drawString(s, x + W/2,
                                          y - p.getTextSize(), p);
                     }
                     if (k+j == 2) {
@@ -99,7 +99,7 @@
                         SkString s;
                         s.append(" depth=");
                         s.appendS32(fBitmaps[i].colorType() == kRGB_565_SkColorType ? 16 : 32);
-                        canvas->drawText(s.c_str(), s.size(), x + W + SkIntToScalar(4),
+                        canvas->drawString(s, x + W + SkIntToScalar(4),
                                          y + H/2, p);
                     }
                 }
diff --git a/samplecode/SampleFilterFuzz.cpp b/samplecode/SampleFilterFuzz.cpp
index 5524f1e..7254192 100644
--- a/samplecode/SampleFilterFuzz.cpp
+++ b/samplecode/SampleFilterFuzz.cpp
@@ -39,6 +39,7 @@
 #include "SkPictureRecorder.h"
 #include "SkPoint3.h"
 #include "SkRandom.h"
+#include "SkRegion.h"
 #include "SkTableColorFilter.h"
 #include "SkTileImageFilter.h"
 #include "SkTypeface.h"
@@ -257,7 +258,7 @@
     paint.setColor(0xFF884422);
     paint.setTextSize(SkIntToScalar(kBitmapSize/2));
     const char* str = "g";
-    canvas.drawText(str, strlen(str), SkIntToScalar(kBitmapSize/8),
+    canvas.drawString(str, SkIntToScalar(kBitmapSize/8),
                     SkIntToScalar(kBitmapSize/4), paint);
 }
 
@@ -339,7 +340,7 @@
     canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint);
     paint.setColor(SK_ColorBLACK);
     paint.setTextSize(SkIntToScalar(kBitmapSize/3));
-    canvas->drawText("Picture", 7, SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
+    canvas->drawString("Picture", SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
 }
 
 static void rand_color_table(uint8_t* table) {
diff --git a/samplecode/SampleFilterQuality.cpp b/samplecode/SampleFilterQuality.cpp
index 2c3eb4c..43cd505 100644
--- a/samplecode/SampleFilterQuality.cpp
+++ b/samplecode/SampleFilterQuality.cpp
@@ -29,10 +29,7 @@
 
 static sk_sp<SkShader> make_shader(const SkRect& bounds) {
     sk_sp<SkImage> image(GetResourceAsImage("mandrill_128.png"));
-    if (!image) {
-        return nullptr;
-    }
-    return image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    return image ? image->makeShader() : nullptr;
 }
 
 #define N   128
@@ -280,14 +277,14 @@
         paint.setTextSize(36);
         SkString str;
         str.appendScalar(fScale);
-        canvas->drawText(str.c_str(), str.size(), textX, 100, paint);
+        canvas->drawString(str, textX, 100, paint);
         str.reset(); str.appendScalar(fAngle);
-        canvas->drawText(str.c_str(), str.size(), textX, 150, paint);
+        canvas->drawString(str, textX, 150, paint);
 
         str.reset(); str.appendScalar(trans[0]);
-        canvas->drawText(str.c_str(), str.size(), textX, 200, paint);
+        canvas->drawString(str, textX, 200, paint);
         str.reset(); str.appendScalar(trans[1]);
-        canvas->drawText(str.c_str(), str.size(), textX, 250, paint);
+        canvas->drawString(str, textX, 250, paint);
     }
 
     bool onAnimate(const SkAnimTimer& timer) override {
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
index 169804d..fe9e07c 100644
--- a/samplecode/SampleHairline.cpp
+++ b/samplecode/SampleHairline.cpp
@@ -77,7 +77,7 @@
     for (int i = 0; i < 400; i++) {
         generate_pts(pts, N, WIDTH, HEIGHT);
 
-        canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+        canvas->drawLine(pts[0], pts[1], paint);
         if (!check_bitmap_margin(bm, MARGIN)) {
             SkDebugf("---- hairline failure (%g %g) (%g %g)\n",
                      pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
diff --git a/samplecode/SampleIdentityScale.cpp b/samplecode/SampleIdentityScale.cpp
index 95970e7..5fbba69 100644
--- a/samplecode/SampleIdentityScale.cpp
+++ b/samplecode/SampleIdentityScale.cpp
@@ -71,7 +71,7 @@
         }
         canvas->drawBitmap( fBM, 100, 100, &paint );
         canvas->restore();
-        canvas->drawText( text, strlen(text), 100, 400, paint );
+        canvas->drawString(text, 100, 400, paint );
         this->inval(nullptr);
     }
 
diff --git a/samplecode/SampleLayerMask.cpp b/samplecode/SampleLayerMask.cpp
index f0c6a40..b039421 100644
--- a/samplecode/SampleLayerMask.cpp
+++ b/samplecode/SampleLayerMask.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SampleCode.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkPaint.h"
 #include "SkPath.h"
diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp
index ab75ddb..3ba6d69 100644
--- a/samplecode/SampleLayers.cpp
+++ b/samplecode/SampleLayers.cpp
@@ -260,7 +260,7 @@
 
         SkPaint paint;
         paint.setAlpha(0xCC);
-        canvas->saveLayer({ &bounds, &paint, fFilter.get(), 0 });
+        canvas->saveLayer({ &bounds, &paint, fFilter.get(), nullptr, nullptr, 0 });
 
         canvas->restore();
     }
diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp
index 54de942..47786bf 100644
--- a/samplecode/SamplePatch.cpp
+++ b/samplecode/SamplePatch.cpp
@@ -21,6 +21,7 @@
 #include "SkColorFilter.h"
 #include "SkTime.h"
 #include "SkTypeface.h"
+#include "SkVertices.h"
 
 #include "SkOSFile.h"
 #include "SkStream.h"
@@ -179,9 +180,10 @@
             s += ds;
         }
         t += dt;
-        canvas->drawVertices(SkCanvas::kTriangleStrip_VertexMode, stripCount,
-                             strip, doTextures ? tex : nullptr,
-                             doColors ? colors : nullptr, nullptr, 0, paint);
+        canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleStrip_VertexMode, stripCount,
+                                                  strip, doTextures ? tex : nullptr,
+                                                  doColors ? colors : nullptr),
+                             SkBlendMode::kModulate, paint);
     }
 }
 
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp
index b425884..5fdaeed 100644
--- a/samplecode/SamplePathClip.cpp
+++ b/samplecode/SamplePathClip.cpp
@@ -202,7 +202,7 @@
             const int j = (i + 1) % N;
             p.setColor(fEdgeColor[i]);
             p.setAlpha(0x88);
-            canvas->drawLine(fPoly[i].x(), fPoly[i].y(), fPoly[j].x(), fPoly[j].y(), p);
+            canvas->drawLine(fPoly[i], fPoly[j], p);
         }
         p.setStyle(SkPaint::kFill_Style);
 
diff --git a/samplecode/SamplePictFile.cpp b/samplecode/SamplePictFile.cpp
index 32cc723..0c08fd7 100644
--- a/samplecode/SamplePictFile.cpp
+++ b/samplecode/SamplePictFile.cpp
@@ -30,18 +30,6 @@
 
 #include "SkGlyphCache.h"
 
-#include "SkDrawFilter.h"
-class SkCounterDrawFilter : public SkDrawFilter {
-public:
-    SkCounterDrawFilter(int count) : fCount(count) {}
-
-    bool filter(SkPaint*, Type t) override {
-        return --fCount >= 0;
-    }
-
-    int fCount;
-};
-
 class PictFileView : public SampleView {
 public:
     PictFileView(const char name[] = nullptr)
@@ -129,13 +117,9 @@
         if (!*picture) {
             *picture = LoadPicture(fFilename.c_str(), fBBox).release();
         }
+
         if (*picture) {
-            SkCounterDrawFilter filter(fCount);
-            if (fCount > 0) {
-                canvas->setDrawFilter(&filter);
-            }
             canvas->drawPicture(*picture);
-            canvas->setDrawFilter(nullptr);
         }
 
 #ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
diff --git a/samplecode/SamplePolyToPoly.cpp b/samplecode/SamplePolyToPoly.cpp
index 8594c2b..f947251 100644
--- a/samplecode/SamplePolyToPoly.cpp
+++ b/samplecode/SamplePolyToPoly.cpp
@@ -110,7 +110,7 @@
         float y = D/2 - (fm.fAscent + fm.fDescent)/2;
         SkString str;
         str.appendS32(count);
-        canvas->drawText(str.c_str(), str.size(),
+        canvas->drawString(str,
                          x, y,
                          *paint);
 
diff --git a/samplecode/SampleQuadStroker.cpp b/samplecode/SampleQuadStroker.cpp
index 30b8603..ea5f97f 100644
--- a/samplecode/SampleQuadStroker.cpp
+++ b/samplecode/SampleQuadStroker.cpp
@@ -321,7 +321,7 @@
                     label.appendS32(index);
                     SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4);
                     canvas->drawRect(dot, labelP);
-                    canvas->drawText(label.c_str(), label.size(),
+                    canvas->drawString(label,
                         pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, labelP);
                 }
             }
@@ -381,7 +381,7 @@
             if (0 == index % 10) {
                 SkString label;
                 label.appendS32(index);
-                canvas->drawText(label.c_str(), label.size(),
+                canvas->drawString(label,
                     pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, paint);
             }
         }
@@ -509,9 +509,9 @@
         paint.setColor(0xFF000000);
         paint.setTextSize(11.0f);
         paint.setStyle(SkPaint::kFill_Style);
-        canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5, paint);
+        canvas->drawString(label, bounds.fLeft + 5, yPos - 5, paint);
         paint.setTextSize(13.0f);
-        canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11, paint);
+        canvas->drawString(name, bounds.fLeft, bounds.bottom() + 11, paint);
     }
 
     void setForGeometry() {
diff --git a/samplecode/SampleRectanizer.cpp b/samplecode/SampleRectanizer.cpp
index 4ff28b2..0657464 100644
--- a/samplecode/SampleRectanizer.cpp
+++ b/samplecode/SampleRectanizer.cpp
@@ -119,13 +119,13 @@
                    100.0f * totArea / ((float)kWidth*kHeight),
                    fCurRandRect,
                    kNumRandRects);
-        canvas->drawText(str.c_str(), str.size(), 50, kHeight + 50, blackBigFont);
+        canvas->drawString(str, 50, kHeight + 50, blackBigFont);
 
         str.printf("Press \'j\' to toggle rectanizer");
-        canvas->drawText(str.c_str(), str.size(), 50, kHeight + 100, blackBigFont);
+        canvas->drawString(str, 50, kHeight + 100, blackBigFont);
 
         str.printf("Press \'h\' to toggle rects");
-        canvas->drawText(str.c_str(), str.size(), 50, kHeight + 150, blackBigFont);
+        canvas->drawString(str, 50, kHeight + 150, blackBigFont);
 
         this->inval(nullptr);
     }
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
index 082ff22..1fee2fa 100644
--- a/samplecode/SampleRegion.cpp
+++ b/samplecode/SampleRegion.cpp
@@ -226,7 +226,7 @@
         paint.setAntiAlias(true);
         paint.setTextSize(SkIntToScalar(20));
         paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
-        canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint);
+        canvas->drawString(text, loc.fX, loc.fY, paint);
     }
 
     void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
@@ -373,7 +373,7 @@
         canvas->translate(0, SkIntToScalar(200));
 
         for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
-            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
+            canvas->drawString(gOps[op].fName, SkIntToScalar(75), SkIntToScalar(50), textPaint);
 
             this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
 
diff --git a/samplecode/SampleShadowReference.cpp b/samplecode/SampleShadowReference.cpp
new file mode 100755
index 0000000..99ff4b2
--- /dev/null
+++ b/samplecode/SampleShadowReference.cpp
@@ -0,0 +1,205 @@
+
+/*
+ * 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 "SampleCode.h"
+#include "Resources.h"
+#include "SkCanvas.h"
+#include "SkImage.h"
+#include "SkPath.h"
+#include "SkPoint3.h"
+#include "SkShadowUtils.h"
+
+////////////////////////////////////////////////////////////////////////////
+// Sample to compare the Material Design shadow reference to our results
+
+class ShadowRefView : public SampleView {
+    SkPath         fRRectPath;
+    sk_sp<SkImage> fReferenceImage;
+
+    bool      fShowAmbient;
+    bool      fShowSpot;
+    bool      fUseAlt;
+    bool      fShowObject;
+
+public:
+    ShadowRefView()
+        : fShowAmbient(true)
+        , fShowSpot(true)
+        , fUseAlt(false)
+        , fShowObject(true) {}
+
+protected:
+    void onOnceBeforeDraw() override {
+        fRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-130, -128.5, 130, 128.5), 4, 4));
+        fReferenceImage = GetResourceAsImage("shadowreference.png");
+    }
+
+    // overrides from SkEventSink
+    bool onQuery(SkEvent* evt) override {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "ShadowReference");
+            return true;
+        }
+
+        SkUnichar uni;
+        if (SampleCode::CharQ(*evt, &uni)) {
+            bool handled = false;
+            switch (uni) {
+                case 'W':
+                    fShowAmbient = !fShowAmbient;
+                    handled = true;
+                    break;
+                case 'S':
+                    fShowSpot = !fShowSpot;
+                    handled = true;
+                    break;
+                case 'T':
+                    fUseAlt = !fUseAlt;
+                    handled = true;
+                    break;
+                case 'O':
+                    fShowObject = !fShowObject;
+                    handled = true;
+                    break;
+                default:
+                    break;
+            }
+            if (handled) {
+                this->inval(nullptr);
+                return true;
+            }
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFFFFFFF);
+        canvas->drawImage(fReferenceImage, 10, 30);
+    }
+
+    void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
+                          const SkPoint3& zPlaneParams,
+                          const SkPaint& paint, SkScalar ambientAlpha,
+                          const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
+        if (!fShowAmbient) {
+            ambientAlpha = 0;
+        }
+        if (!fShowSpot) {
+            spotAlpha = 0;
+        }
+        uint32_t flags = 0;
+        if (fUseAlt) {
+            flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
+        }
+        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+                                  lightPos, lightWidth,
+                                  ambientAlpha, spotAlpha, SK_ColorBLACK, flags);
+
+        if (fShowObject) {
+            canvas->drawPath(path, paint);
+        } else {
+            SkPaint strokePaint;
+
+            strokePaint.setColor(paint.getColor());
+            strokePaint.setStyle(SkPaint::kStroke_Style);
+
+            canvas->drawPath(path, strokePaint);
+        }
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        this->drawBG(canvas);
+        const SkScalar kDP = 4;  // the reference image is 4x bigger than it is displayed on
+                                 // on the web page, so we need to reflect that here and
+                                 // multiply the heights and light params accordingly
+        const SkScalar kLightWidth = kDP*400;
+        const SkScalar kAmbientAlpha = 0.03f;
+        const SkScalar kSpotAlpha = 0.35f;
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorWHITE);
+
+        SkPoint3 lightPos = { 175, -800, kDP * 600 };
+        SkScalar xPos = 230;
+        SkScalar yPos = 254.25f;
+        SkRect clipRect = SkRect::MakeXYWH(45, 75, 122, 250);
+        SkPoint clipDelta = SkPoint::Make(320, 0);
+        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, kDP * 2);
+
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->translate(xPos, yPos);
+        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
+        canvas->restore();
+
+        lightPos.fX += 320;
+        xPos += 320;
+        clipRect.offset(clipDelta);
+        zPlaneParams.fZ = kDP * 3;
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->translate(xPos, yPos);
+        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
+        canvas->restore();
+
+        lightPos.fX += 320;
+        xPos += 320;
+        clipRect.offset(clipDelta);
+        zPlaneParams.fZ = kDP * 4;
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->translate(xPos, yPos);
+        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
+        canvas->restore();
+
+        lightPos.fX += 320;
+        xPos += 320;
+        clipRect.offset(clipDelta);
+        zPlaneParams.fZ = kDP * 6;
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->translate(xPos, yPos);
+        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
+        canvas->restore();
+
+        lightPos.fX += 320;
+        xPos += 320;
+        clipRect.offset(clipDelta);
+        zPlaneParams.fZ = kDP * 8;
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->translate(xPos, yPos);
+        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
+        canvas->restore();
+
+        lightPos.fX += 320;
+        xPos += 320;
+        clipRect.offset(clipDelta);
+        zPlaneParams.fZ = kDP * 16;
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->translate(xPos, yPos);
+        this->drawShadowedPath(canvas, fRRectPath, zPlaneParams, paint, kAmbientAlpha,
+                               lightPos, kLightWidth, kSpotAlpha);
+        canvas->restore();
+
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShadowRefView; }
+static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleShadowUtils.cpp b/samplecode/SampleShadowUtils.cpp
new file mode 100755
index 0000000..c37d8d2
--- /dev/null
+++ b/samplecode/SampleShadowUtils.cpp
@@ -0,0 +1,218 @@
+
+/*
+ * 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 "SampleCode.h"
+#include "SkAnimTimer.h"
+#include "SkBlurMask.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorFilter.h"
+#include "SkCamera.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkPathOps.h"
+#include "SkPoint3.h"
+#include "SkShadowUtils.h"
+#include "SkUtils.h"
+#include "SkView.h"
+#include "sk_tool_utils.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+class ShadowUtilsView : public SampleView {
+    SkTArray<SkPath> fPaths;
+    SkScalar         fZDelta;
+
+    bool      fShowAmbient;
+    bool      fShowSpot;
+    bool      fUseAlt;
+    bool      fShowObject;
+    bool      fIgnoreShadowAlpha;
+
+public:
+    ShadowUtilsView()
+        : fZDelta(0)
+        , fShowAmbient(true)
+        , fShowSpot(true)
+        , fUseAlt(false)
+        , fShowObject(false)
+        , fIgnoreShadowAlpha(false) {}
+
+protected:
+    void onOnceBeforeDraw() override {
+        fPaths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
+        SkRRect oddRRect;
+        oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
+        fPaths.push_back().addRRect(oddRRect);
+        fPaths.push_back().addRect(SkRect::MakeWH(50, 50));
+        fPaths.push_back().addCircle(25, 25, 25);
+        fPaths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
+        fPaths.push_back().addOval(SkRect::MakeWH(20, 60));
+    }
+
+    // overrides from SkEventSink
+    bool onQuery(SkEvent* evt) override {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "ShadowUtils");
+            return true;
+        }
+
+        SkUnichar uni;
+        if (SampleCode::CharQ(*evt, &uni)) {
+            bool handled = false;
+            switch (uni) {
+                case 'W':
+                    fShowAmbient = !fShowAmbient;
+                    handled = true;
+                    break;
+                case 'S':
+                    fShowSpot = !fShowSpot;
+                    handled = true;
+                    break;
+                case 'T':
+                    fUseAlt = !fUseAlt;
+                    handled = true;
+                    break;
+                case 'O':
+                    fShowObject = !fShowObject;
+                    handled = true;
+                    break;
+                case '>':
+                    fZDelta += 0.5f;
+                    handled = true;
+                    break;
+                case '<':
+                    fZDelta -= 0.5f;
+                    handled = true;
+                    break;
+                case '?':
+                    fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
+                    handled = true;
+                    break;
+                default:
+                    break;
+            }
+            if (handled) {
+                this->inval(nullptr);
+                return true;
+            }
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFFFFFFF);
+    }
+
+    void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
+                          const SkPoint3& zPlaneParams,
+                          const SkPaint& paint, SkScalar ambientAlpha,
+                          const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha,
+                          uint32_t flags) {
+        if (fIgnoreShadowAlpha) {
+            ambientAlpha = 255;
+            spotAlpha = 255;
+        }
+        if (!fShowAmbient) {
+            ambientAlpha = 0;
+        }
+        if (!fShowSpot) {
+            spotAlpha = 0;
+        }
+        if (fUseAlt) {
+            flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
+        }
+        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+                                  lightPos, lightWidth,
+                                  ambientAlpha, 0, SK_ColorRED, flags);
+        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+                                  lightPos, lightWidth,
+                                  0, spotAlpha, SK_ColorBLUE, flags);
+
+        if (fShowObject) {
+            canvas->drawPath(path, paint);
+        } else {
+            SkPaint strokePaint;
+
+            strokePaint.setColor(paint.getColor());
+            strokePaint.setStyle(SkPaint::kStroke_Style);
+
+            canvas->drawPath(path, strokePaint);
+        }
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        this->drawBG(canvas);
+
+        static constexpr int kW = 800;
+        static constexpr SkScalar kPad = 15.f;
+        static constexpr SkScalar kLightR = 100.f;
+        static constexpr SkScalar kHeight = 50.f;
+        static constexpr SkScalar kAmbientAlpha = 0.5f;
+        static constexpr SkScalar kSpotAlpha = 0.5f;
+        static constexpr SkPoint3 lightPos = { 250, 400, 500 };
+
+        canvas->translate(3 * kPad, 3 * kPad);
+        canvas->save();
+        SkScalar x = 0;
+        SkScalar dy = 0;
+        SkTDArray<SkMatrix> matrices;
+        matrices.push()->reset();
+        SkMatrix* m = matrices.push();
+        m->setRotate(33.f, 25.f, 25.f);
+        m->postScale(1.2f, 0.8f, 25.f, 25.f);
+        SkPaint paint;
+        paint.setColor(SK_ColorGREEN);
+        paint.setAntiAlias(true);
+        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, SkTMax(1.0f, kHeight + fZDelta));
+        for (auto& m : matrices) {
+            for (auto flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) {
+                for (const auto& path : fPaths) {
+                    SkRect postMBounds = path.getBounds();
+                    m.mapRect(&postMBounds);
+                    SkScalar w = postMBounds.width() + kHeight;
+                    SkScalar dx = w + kPad;
+                    if (x + dx > kW - 3 * kPad) {
+                        canvas->restore();
+                        canvas->translate(0, dy);
+                        canvas->save();
+                        x = 0;
+                        dy = 0;
+                    }
+
+                    canvas->save();
+                    canvas->concat(m);
+                    drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha, lightPos,
+                                     kLightR, kSpotAlpha, flags);
+                    canvas->restore();
+
+                    canvas->translate(dx, 0);
+                    x += dx;
+                    dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
+                }
+            }
+        }
+        // Show where the light is in x,y as a circle (specified in device space).
+        SkMatrix invCanvasM = canvas->getTotalMatrix();
+        if (invCanvasM.invert(&invCanvasM)) {
+            canvas->save();
+            canvas->concat(invCanvasM);
+            SkPaint paint;
+            paint.setColor(SK_ColorBLACK);
+            paint.setAntiAlias(true);
+            canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
+            canvas->restore();
+        }
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShadowUtilsView; }
+static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleShadowing.cpp b/samplecode/SampleShadowing.cpp
deleted file mode 100644
index 929d62b..0000000
--- a/samplecode/SampleShadowing.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * 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 "SampleCode.h"
-#include "SkPictureRecorder.h"
-#include "SkShadowPaintFilterCanvas.h"
-#include "SkShadowShader.h"
-#include "SkSurface.h"
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-class ShadowingView : public SampleView {
-public:
-    ShadowingView()
-        : fSceneChanged(true)
-        , fLightsChanged(true)
-        , fMoveLight(false)
-        , fClearShadowMaps(false)
-        , fSelectedRectID(-1)
-        , fSelectedSliderID(-1)
-        , fLightDepth(400.0f)  {
-        this->setBGColor(0xFFCCCCCC);
-
-        this->updateLights(100, 100);
-
-        fTestRects[0].fColor = 0xFFEE8888;
-        fTestRects[0].fDepth = 80;
-        fTestRects[0].fGeometry = SkRect::MakeLTRB(300,200,350,250);
-
-        fTestRects[1].fColor = 0xFF88EE88;
-        fTestRects[1].fDepth = 160;
-        fTestRects[1].fGeometry = SkRect::MakeLTRB(200,300,250,350);
-
-        fTestRects[2].fColor = 0xFF8888EE;
-        fTestRects[2].fDepth = 240;
-        fTestRects[2].fGeometry = SkRect::MakeLTRB(100,100,150,150);
-
-        fSliders[0].fGeometry = SkRect::MakeLTRB(20, 400, 30, 420);
-        fSliders[0].fOffset = 0.0f;
-        fSliders[0].fScale = 0.1f;
-
-        fSliders[1].fGeometry = SkRect::MakeLTRB(100, 420, 110, 440);
-        fSliders[1].fOffset = 0.0f;
-        fSliders[1].fScale = 10.0f;
-
-        fSliders[2].fGeometry = SkRect::MakeLTRB(0, 440, 10, 460);
-        fSliders[2].fOffset = 0.0f;
-        fSliders[2].fScale = 0.0025f;
-
-        fShadowParams.fShadowRadius = 4.0f;
-        fShadowParams.fBiasingConstant = 0.3f;
-        fShadowParams.fMinVariance = 2048; // we need a higher min variance for point lights
-        fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType;
-    }
-
-protected:
-    bool onQuery(SkEvent *evt) override {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "shadowing");
-            return true;
-        }
-
-        SkUnichar uni;
-        if (SampleCode::CharQ(*evt, &uni)) {
-            switch (uni) {
-                case 'L':
-                    fMoveLight = !fMoveLight;
-                    break;
-                case 'd':
-                    // Raster generated shadow maps have their origin in the UL corner
-                    // GPU shadow maps can have an arbitrary origin.
-                    // We override the 'd' keypress so that when the device is cycled,
-                    // the shadow maps will be re-generated according to the new backend.
-                    fClearShadowMaps = true;
-                    break;
-                case 'q':
-                    fLightDepth += 5.0f;
-                    fMoveLight = true;
-                    break;
-                case 'B':
-                    if (SkShadowParams::kVariance_ShadowType == fShadowParams.fType) {
-                        fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType;
-                    } else if (SkShadowParams::kNoBlur_ShadowType ==
-                               fShadowParams.fType) {
-                        fShadowParams.fType = SkShadowParams::kVariance_ShadowType;
-                    }
-                    fLightsChanged = true;
-                    break;
-                case 'w':
-                    fLightDepth -= 5.0f;
-                    fMoveLight = true;
-                    break;
-                default:
-                    break;
-            }
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    sk_sp<SkPicture> makeTestPicture(int width, int height) {
-        SkPictureRecorder recorder;
-
-        // LONG RANGE TODO: eventually add SkBBHFactory (bounding box factory)
-        SkCanvas* canvas = recorder.beginRecording(SkRect::MakeIWH(width, height));
-
-        SkASSERT(canvas->getTotalMatrix().isIdentity());
-        SkPaint paint;
-        paint.setColor(SK_ColorGRAY);
-
-        // LONG RANGE TODO: tag occluders
-        // LONG RANGE TODO: track number of IDs we need (hopefully less than 256)
-        //                  and determinate the mapping from z to id
-
-        // universal receiver, "ground"
-        canvas->drawRect(SkRect::MakeIWH(width, height), paint);
-
-        for (int i = 0; i < kNumTestRects; i++) {
-            paint.setColor(fTestRects[i].fColor);
-            if (i == 0) {
-                canvas->translateZ(fTestRects[0].fDepth);
-            } else {
-                canvas->translateZ(fTestRects[i].fDepth - fTestRects[i-1].fDepth);
-            }
-            canvas->drawRect(fTestRects[i].fGeometry, paint);
-        }
-
-        return recorder.finishRecordingAsPicture();
-    }
-
-    void onDrawContent(SkCanvas *canvas) override {
-        if (fSceneChanged) {
-            fPicture = this->makeTestPicture(kWidth, kHeight);
-        }
-
-        if (fSceneChanged || fLightsChanged || fClearShadowMaps) {
-            for (int i = 0; i < fLights->numLights(); i++) {
-                fLights->light(i).setShadowMap(nullptr);
-            }
-
-            fSceneChanged = false;
-            fLightsChanged = false;
-            fClearShadowMaps = false;
-        }
-
-        canvas->setLights(fLights);
-        canvas->drawShadowedPicture(fPicture, nullptr, nullptr, fShadowParams);
-
-        for (int i = 0; i < kNumSliders; i++) {
-            SkPaint paint;
-            paint.setColor(SK_ColorBLACK);
-            canvas->drawRect(fSliders[i].fGeometry, paint);
-        }
-    }
-
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
-        return new SkView::Click(this);
-    }
-
-    void updateLights(int x, int y) {
-        SkLights::Builder builder;
-        builder.add(SkLights::Light::MakePoint(SkColor3f::Make(1.0f, 1.0f, 1.0f),
-                                               SkVector3::Make(x,
-                                                               kHeight - y,
-                                                               fLightDepth),
-                                               400, true));
-        fLights = builder.finish();
-    }
-
-    void updateFromSelectedSlider() {
-        SkScalar newValue = fSliders[fSelectedSliderID].fGeometry.fLeft *
-                            fSliders[fSelectedSliderID].fScale +
-                            fSliders[fSelectedSliderID].fOffset;
-
-        switch (fSelectedSliderID) {
-            case 0:
-                fShadowParams.fShadowRadius = newValue;
-                break;
-            case 1:
-                fShadowParams.fMinVariance = newValue;
-                break;
-            case 2:
-                fShadowParams.fBiasingConstant = newValue;
-                break;
-            default:
-                break;
-        }
-    }
-
-    bool onClick(Click *click) override {
-        SkScalar x = click->fCurr.fX;
-        SkScalar y = click->fCurr.fY;
-
-        SkScalar dx = x - click->fPrev.fX;
-        SkScalar dy = y - click->fPrev.fY;
-
-        if (fMoveLight) {
-            if (dx != 0 || dy != 0) {
-                this->updateLights(x, y);
-                fLightsChanged = true;
-                this->inval(nullptr);
-            }
-            return true;
-        }
-
-        if (click->fState == Click::State::kUp_State) {
-            fSelectedRectID = -1;
-            fSelectedSliderID = -1;
-            return true;
-        }
-
-        if (fSelectedRectID > -1) {
-            fTestRects[fSelectedRectID].fGeometry.offset(dx, dy);
-
-            fSceneChanged = true;
-            this->inval(nullptr);
-            return true;
-        }
-
-        if (fSelectedSliderID > -1) {
-            fSliders[fSelectedSliderID].fGeometry.offset(dx, 0);
-
-            this->updateFromSelectedSlider();
-
-            fLightsChanged = true;
-            this->inval(nullptr);
-            return true;
-        }
-
-        // assume last elements are highest
-        for (int i = kNumTestRects - 1; i >= 0; i--) {
-            if (fTestRects[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
-                fSelectedRectID = i;
-                fTestRects[i].fGeometry.offset(dx, dy);
-
-                fSceneChanged = true;
-                this->inval(nullptr);
-                break;
-            }
-        }
-
-        for (int i = 0; i <= kNumSliders; i++) {
-            if (fSliders[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) {
-                fSelectedSliderID = i;
-                fSliders[i].fGeometry.offset(dx, 0);
-
-                this->updateFromSelectedSlider();
-
-                fLightsChanged = true;
-
-                this->inval(nullptr);
-                break;
-            }
-        }
-
-        return true;
-    }
-
-private:
-    static constexpr int kNumTestRects = 3;
-    static constexpr int kNumSliders = 3;
-
-    static const int kWidth = 400;
-    static const int kHeight = 400;
-
-    bool fSceneChanged;
-    bool fLightsChanged;
-    bool fMoveLight;
-    bool fClearShadowMaps;
-
-    struct {
-        SkRect  fGeometry;
-        int     fDepth;
-        SkColor fColor;
-    } fTestRects[kNumTestRects];
-    int fSelectedRectID;
-
-    struct {
-        SkRect   fGeometry;
-        SkScalar fOffset;
-        SkScalar fScale;
-    } fSliders[kNumSliders];
-    int fSelectedSliderID;
-
-    SkScalar fLightDepth;
-
-    sk_sp<SkPicture> fPicture;
-    SkShadowParams fShadowParams;
-    sk_sp<SkLights> fLights;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-static SkView* MyFactory() { return new ShadowingView; }
-static SkViewRegister reg(MyFactory);
-
-#endif
diff --git a/samplecode/SampleShip.cpp b/samplecode/SampleShip.cpp
index 9d65b6f..08f794a 100644
--- a/samplecode/SampleShip.cpp
+++ b/samplecode/SampleShip.cpp
@@ -150,7 +150,7 @@
         paint.setColor(SK_ColorBLACK);
         canvas->drawRect(SkRect::MakeXYWH(0, 0, 200, 24), paint);
         paint.setColor(SK_ColorWHITE);
-        canvas->drawText(outString.c_str(), outString.size(), 5, 15, paint);
+        canvas->drawString(outString, 5, 15, paint);
 
         this->inval(nullptr);
     }
diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp
index 2322c24..b8cf380 100644
--- a/samplecode/SampleSlides.cpp
+++ b/samplecode/SampleSlides.cpp
@@ -13,6 +13,7 @@
 #include "SkGradientShader.h"
 #include "SkLayerRasterizer.h"
 #include "SkPaint.h"
+#include "SkVertices.h"
 #include "SkView.h"
 
 #include "sk_tool_utils.h"
@@ -328,7 +329,7 @@
 
 class Rec {
 public:
-    SkCanvas::VertexMode    fMode;
+    SkVertices::VertexMode  fMode;
     int                     fCount;
     SkPoint*                fVerts;
     SkPoint*                fTexs;
@@ -341,7 +342,7 @@
     int n = 10;
     SkRandom    rand;
 
-    rec->fMode = SkCanvas::kTriangles_VertexMode;
+    rec->fMode = SkVertices::kTriangles_VertexMode;
     rec->fCount = n * 3;
     rec->fVerts = new SkPoint[rec->fCount];
 
@@ -358,7 +359,7 @@
     const SkScalar ty = SkIntToScalar(texHeight);
     const int n = 24;
 
-    rec->fMode = SkCanvas::kTriangleFan_VertexMode;
+    rec->fMode = SkVertices::kTriangleFan_VertexMode;
     rec->fCount = n + 2;
     rec->fVerts = new SkPoint[rec->fCount];
     rec->fTexs  = new SkPoint[rec->fCount];
@@ -388,7 +389,7 @@
     const SkScalar ty = SkIntToScalar(texHeight);
     const int n = 24;
 
-    rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
+    rec->fMode = SkVertices::kTriangleStrip_VertexMode;
     rec->fCount = 2 * (n + 1);
     rec->fVerts = new SkPoint[rec->fCount];
     rec->fTexs  = new SkPoint[rec->fCount];
@@ -433,26 +434,22 @@
     paint.setFilterQuality(kLow_SkFilterQuality);
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
+        auto verts = SkVertices::MakeCopy(fRecs[i].fMode, fRecs[i].fCount,
+                                          fRecs[i].fVerts, fRecs[i].fTexs, nullptr);
         canvas->save();
 
         paint.setShader(nullptr);
-        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                             fRecs[i].fVerts, fRecs[i].fTexs,
-                             nullptr, nullptr, 0, paint);
+        canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
 
         canvas->translate(SkIntToScalar(210), 0);
 
         paint.setShader(fShader0);
-        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                             fRecs[i].fVerts, fRecs[i].fTexs,
-                             nullptr, nullptr, 0, paint);
+        canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
 
         canvas->translate(SkIntToScalar(210), 0);
 
         paint.setShader(fShader1);
-        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                             fRecs[i].fVerts, fRecs[i].fTexs,
-                             nullptr, nullptr, 0, paint);
+        canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
         canvas->restore();
 
         canvas->translate(0, SkIntToScalar(250));
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
index e69625e..3f84482 100644
--- a/samplecode/SampleStrokePath.cpp
+++ b/samplecode/SampleStrokePath.cpp
@@ -170,7 +170,7 @@
                     if (x) {
                         paint.setMaskFilter(SkBlurMaskFilter::Make(gStyle[x - 1], sigma));
                     }
-                    canvas->drawText("Title Bar", 9, x*SkIntToScalar(100), y*SkIntToScalar(30), paint);
+                    canvas->drawString("Title Bar", x*SkIntToScalar(100), y*SkIntToScalar(30), paint);
                     sigma *= 0.75f;
                 }
 
diff --git a/samplecode/SampleSubpixelTranslate.cpp b/samplecode/SampleSubpixelTranslate.cpp
index 3bb9056..fd66de2 100644
--- a/samplecode/SampleSubpixelTranslate.cpp
+++ b/samplecode/SampleSubpixelTranslate.cpp
@@ -71,7 +71,7 @@
             canvas->drawBitmapRect( fBM, r, &paint );
         }
 
-        canvas->drawText( "AA Scaled", strlen("AA Scaled"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10), fCurPos.fY + fSize/2, paint );
+        canvas->drawString( "AA Scaled", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10), fCurPos.fY + fSize/2, paint );
 
         paint.setAntiAlias(false);
         for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
@@ -79,7 +79,7 @@
             SkRect r = SkRect::MakeXYWH( fCurPos.fX + i * (fSize + 10), fCurPos.fY + fSize + 10, fSize, fSize );
             canvas->drawBitmapRect( fBM, r, &paint );
         }
-        canvas->drawText( "Scaled", strlen("Scaled"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10), fCurPos.fY + fSize + 10 + fSize/2, paint );
+        canvas->drawString( "Scaled", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10), fCurPos.fY + fSize + 10 + fSize/2, paint );
 
         paint.setAntiAlias(true);
         for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
@@ -87,7 +87,7 @@
             canvas->drawBitmap( fBM, fCurPos.fX + i * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10), &paint );
         }
 
-        canvas->drawText( "AA No Scale", strlen("AA No Scale"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fSize/2, paint );
+        canvas->drawString( "AA No Scale", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fSize/2, paint );
 
         paint.setAntiAlias(false);
         for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
@@ -95,7 +95,7 @@
             canvas->drawBitmap( fBM, fCurPos.fX + i * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10, &paint );
         }
 
-        canvas->drawText( "No Scale", strlen("No Scale"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10 + fSize/2, paint );
+        canvas->drawString( "No Scale", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10 + fSize/2, paint );
 
 
         fCurPos.fX += fHorizontalVelocity;
diff --git a/samplecode/SampleText.cpp b/samplecode/SampleText.cpp
index 8325cd7..12f345c 100644
--- a/samplecode/SampleText.cpp
+++ b/samplecode/SampleText.cpp
@@ -95,7 +95,7 @@
         paint.setFlags(paint.getFlags() | SkPaint::kAntiAlias_Flag
                                         | SkPaint::kDevKernText_Flag);
         paint.setTextSize(SkIntToScalar(14));
-        canvas.drawText(s, strlen(s), SkIntToScalar(8), SkIntToScalar(14), paint);
+        canvas.drawString(s, SkIntToScalar(8), SkIntToScalar(14), paint);
     }
 
     static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
@@ -119,7 +119,7 @@
 
 //        canvas->translate(0, SkIntToScalar(50));
 
-  //      canvas->drawText(style, strlen(style), SkIntToScalar(20), SkIntToScalar(20), paint);
+  //      canvas->drawString(style, SkIntToScalar(20), SkIntToScalar(20), paint);
 
         paint.setTypeface(SkTypeface::MakeFromFile("/skimages/samplefont.ttf"));
         paint.setAntiAlias(true);
diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp
index 8286177..c357e6d 100644
--- a/samplecode/SampleTextAlpha.cpp
+++ b/samplecode/SampleTextAlpha.cpp
@@ -59,7 +59,7 @@
             paint.setColor(rand.nextU() | (0xFF << 24));
             paint.setTextSize(SkIntToScalar(ps));
             paint.setTextSize(SkIntToScalar(24));
-            canvas->drawText(str, strlen(str), x, y, paint);
+            canvas->drawString(str, x, y, paint);
             y += paint.getFontMetrics(nullptr);
         }
     }
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
index e615235..9c7f3de 100644
--- a/samplecode/SampleTextureDomain.cpp
+++ b/samplecode/SampleTextureDomain.cpp
@@ -21,7 +21,6 @@
             p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
         }
     }
-    bm.unlockPixels();
     return bm;
 }
 
diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp
index 84346b0..e09354e 100644
--- a/samplecode/SampleTiling.cpp
+++ b/samplecode/SampleTiling.cpp
@@ -111,7 +111,7 @@
                     str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
 
                     p.setTextAlign(SkPaint::kCenter_Align);
-                    textCanvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+                    textCanvas->drawString(str, x + r.width()/2, y, p);
 
                     x += r.width() * 4 / 3;
                 }
@@ -143,7 +143,7 @@
                     p.setAntiAlias(true);
                     p.setLooper(fLooper);
                     str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
-                    textCanvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
+                    textCanvas->drawString(str, x, y + r.height() * 2 / 3, p);
                 }
 
                 y += r.height() * 4 / 3;
diff --git a/samplecode/SampleTinyBitmap.cpp b/samplecode/SampleTinyBitmap.cpp
index 4fb508b..2212cbe 100644
--- a/samplecode/SampleTinyBitmap.cpp
+++ b/samplecode/SampleTinyBitmap.cpp
@@ -19,22 +19,18 @@
     for (int i = 0; i < N; i++) {
         c[i] = SkPackARGB32(0x80, 0x80, 0, 0);
     }
-    SkColorTable* ctable = new SkColorTable(c, N);
 
     SkBitmap bm;
     bm.allocPixels(SkImageInfo::Make(1, 1, kIndex_8_SkColorType,
                                      kPremul_SkAlphaType),
-                   nullptr, ctable);
-    ctable->unref();
+                   SkColorTable::Make(c, N));
 
-    bm.lockPixels();
     for (int y = 0; y < bm.height(); y++) {
         uint8_t* p = bm.getAddr8(0, y);
         for (int x = 0; x < bm.width(); x++) {
             p[x] = 0;
         }
     }
-    bm.unlockPixels();
     return bm;
 }
 
@@ -56,7 +52,6 @@
     }
 
     static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) {
-        SkAutoLockPixels alp(*bm);  // needed for ctable
         bm->setAlphaType(isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
     }
 
diff --git a/samplecode/SampleUnpremul.cpp b/samplecode/SampleUnpremul.cpp
index 558bf93..cd05fcf 100644
--- a/samplecode/SampleUnpremul.cpp
+++ b/samplecode/SampleUnpremul.cpp
@@ -93,7 +93,7 @@
             } else {
                 failure.printf("Failed to decode %s", fCurrFile.c_str());
             }
-            canvas->drawText(failure.c_str(), failure.size(), 0, height, paint);
+            canvas->drawString(failure, 0, height, paint);
             return;
         }
 
@@ -101,22 +101,21 @@
         SkString header(SkOSPath::Basename(fCurrFile.c_str()));
         header.appendf("     [%dx%d]     %s", fBitmap.width(), fBitmap.height(),
                        (fPremul ? "premultiplied" : "unpremultiplied"));
-        canvas->drawText(header.c_str(), header.size(), 0, height, paint);
+        canvas->drawString(header, 0, height, paint);
         canvas->translate(0, height);
 
         // Help messages
         header.printf("Press '%c' to move to the next image.'", fNextImageChar);
-        canvas->drawText(header.c_str(), header.size(), 0, height, paint);
+        canvas->drawString(header, 0, height, paint);
         canvas->translate(0, height);
 
         header.printf("Press '%c' to toggle premultiplied decode.", fTogglePremulChar);
-        canvas->drawText(header.c_str(), header.size(), 0, height, paint);
+        canvas->drawString(header, 0, height, paint);
 
         // Now draw the image itself.
         canvas->translate(height * 2, height * 2);
         if (!fPremul) {
             // A premultiplied bitmap cannot currently be drawn.
-            SkAutoLockPixels alp(fBitmap);
             // Copy it to a bitmap which can be drawn, converting
             // to premultiplied:
             SkBitmap bm;
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
index d08726c..9c6eeb8 100644
--- a/samplecode/SampleVertices.cpp
+++ b/samplecode/SampleVertices.cpp
@@ -18,6 +18,7 @@
 #include "SkColorFilter.h"
 #include "SkTime.h"
 #include "SkTypeface.h"
+#include "SkVertices.h"
 
 #include "SkOSFile.h"
 #include "SkStream.h"
@@ -29,11 +30,9 @@
     SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff);
     bm.allocN32Pixels(size->fX, size->fY);
     bm.eraseColor(color0);
-    bm.lockPixels();
     uint32_t* pixels = (uint32_t*) bm.getPixels();
     pixels[0] = pixels[2] = color0;
     pixels[1] = pixels[3] = color1;
-    bm.unlockPixels();
 
     return SkShader::MakeBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
 }
@@ -84,26 +83,23 @@
         paint.setFilterQuality(kLow_SkFilterQuality);
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
+            auto verts = SkVertices::MakeCopy(fRecs[i].fMode, fRecs[i].fCount,
+                                              fRecs[i].fVerts, fRecs[i].fTexs,
+                                              nullptr);
             canvas->save();
 
             paint.setShader(nullptr);
-            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                                 fRecs[i].fVerts, fRecs[i].fTexs,
-                                 nullptr, nullptr, 0, paint);
+            canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
 
             canvas->translate(SkIntToScalar(250), 0);
 
             paint.setShader(fShader0);
-            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                                 fRecs[i].fVerts, fRecs[i].fTexs,
-                                 nullptr, nullptr, 0, paint);
+            canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
 
             canvas->translate(SkIntToScalar(250), 0);
 
             paint.setShader(fShader1);
-            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                                 fRecs[i].fVerts, fRecs[i].fTexs,
-                                 nullptr, nullptr, 0, paint);
+            canvas->drawVertices(verts, SkBlendMode::kModulate, paint);
             canvas->restore();
 
             canvas->translate(0, SkIntToScalar(250));
@@ -123,7 +119,7 @@
 
 private:
     struct Rec {
-        SkCanvas::VertexMode    fMode;
+        SkVertices::VertexMode  fMode;
         int                     fCount;
         SkPoint*                fVerts;
         SkPoint*                fTexs;
@@ -136,7 +132,7 @@
         int n = 10;
         SkRandom    rand;
 
-        rec->fMode = SkCanvas::kTriangles_VertexMode;
+        rec->fMode = SkVertices::kTriangles_VertexMode;
         rec->fCount = n * 3;
         rec->fVerts = new SkPoint[rec->fCount];
 
@@ -153,7 +149,7 @@
         const SkScalar ty = SkIntToScalar(texHeight);
         const int n = 24;
 
-        rec->fMode = SkCanvas::kTriangleFan_VertexMode;
+        rec->fMode = SkVertices::kTriangleFan_VertexMode;
         rec->fCount = n + 2;
         rec->fVerts = new SkPoint[rec->fCount];
         rec->fTexs  = new SkPoint[rec->fCount];
@@ -183,7 +179,7 @@
         const SkScalar ty = SkIntToScalar(texHeight);
         const int n = 24;
 
-        rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
+        rec->fMode = SkVertices::kTriangleStrip_VertexMode;
         rec->fCount = 2 * (n + 1);
         rec->fVerts = new SkPoint[rec->fCount];
         rec->fTexs  = new SkPoint[rec->fCount];
diff --git a/samplecode/SampleXfer.cpp b/samplecode/SampleXfer.cpp
index 9ca2e78..87427fc 100644
--- a/samplecode/SampleXfer.cpp
+++ b/samplecode/SampleXfer.cpp
@@ -75,7 +75,7 @@
         paint.setTextSize(16);
         paint.setTextAlign(SkPaint::kCenter_Align);
         paint.setLCDRenderText(true);
-        canvas->drawText(fLabel.c_str(), fLabel.size(), r.centerX(), r.fTop + 0.68f * r.height(), paint);
+        canvas->drawString(fLabel, r.centerX(), r.fTop + 0.68f * r.height(), paint);
     }
 
     Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
index 2a17e6b..ff198bc 100644
--- a/samplecode/SampleXfermodesBlur.cpp
+++ b/samplecode/SampleXfermodesBlur.cpp
@@ -160,7 +160,7 @@
                 canvas->drawRect(r, p);
 
                 const char* label = SkBlendMode_Name(gModes[i]);
-                canvas->drawText(label, strlen(label),
+                canvas->drawString(label,
                                  x + w/2, y - labelP.getTextSize()/2, labelP);
                 x += w + SkIntToScalar(10);
                 if ((i % W) == W - 1) {
diff --git a/site/dev/chrome/multi_repo_trybots.md b/site/dev/chrome/multi_repo_trybots.md
index c3ffbe2..4c6f09d 100644
--- a/site/dev/chrome/multi_repo_trybots.md
+++ b/site/dev/chrome/multi_repo_trybots.md
@@ -9,43 +9,42 @@
 
 Skia only changes
 -----------------
-If the Skia patch is already in Rietveld and there are no associated Chromium
+If the Skia patch is already in Gerrit and there are no associated Chromium
 changes, then it is possible to just run the Chromium trybots. This will apply
 the Skia patch and run the bot.
 
 Skia and Chromium changes
 -------------------------
-If the Skia patch is already in Rietveld and there are associated Chromium
+If the Skia patch is already in Gerrit and there are associated Chromium
 changes, then in the Chromium CL add the following to
-\<chromium>/src/DEPS in the 'hooks' array just before the 'gyp' hook.
+\<chromium>/src/DEPS in the 'hooks' array.
 
       {
+        'name': 'fetch_custom_patch',
+        'pattern': '.',
+        'action': [ 'git', '-C', 'src/third_party/skia/',
+                    'fetch', 'https://skia.googlesource.com/skia', 'refs/changes/13/10513/13',
+        ],
+      },
+      {
         'name': 'apply_custom_patch',
         'pattern': '.',
-        'action': ['apply_issue',
-                   '--root_dir', 'src/third_party/skia',
-                   '--issue', '1873923002',
-                   '--patchset', '160001',
-                   '--server', 'https://codereview.chromium.org',
-                   '--force',
-                   '--ignore_deps',
-                   '-v',
-                   '-v',
-                   '--no-auth',
-                   '--blacklist', 'DEPS'
+        'action': ['git', '-C', 'src/third_party/skia/',
+                   'cherry-pick', 'FETCH_HEAD',
         ],
       },
 
-Modify the 'issue' and 'patchset' to the appropriate values.
-If this is for a project other than Skia, update the 'root_dir' and 'server'.
-Note that this can be used multiple times to apply multiple issues.
+Modify the 'refs/changes/XX/YYYY/ZZ' to the appropriate values (where YYYY is
+the numeric change number, ZZ is the patch set number and XX is the last two
+digits of the numeric change number). This can be seen in the 'Download' link on
+Gerrit.
 
-To find the patchset number in Rietveld use the URL of the '[raw]' (old UI) or
-'Raw Patch' (new UI) link on the desired patch. The last segment of this URL
-has the form 'issue\<issue>_\<patchset>.diff'.
+If this is for a project other than Skia, update the checkout directory and
+fetch source. Note that this can be used multiple times to apply multiple
+issues.
 
 An example of this being used can be seen at
-https://crrev.com/1877673002/#ps120001 .
+https://crrev.com/2786433004/#ps1 .
 
 To test locally, run `gclient runhooks` to update the Skia source code.
 Note that if your local skia patch in `third_party/skia` isn't clean (e.g., you
diff --git a/site/dev/contrib/cqkeywords.md b/site/dev/contrib/cqkeywords.md
index e10dc9b..b21b787 100644
--- a/site/dev/contrib/cqkeywords.md
+++ b/site/dev/contrib/cqkeywords.md
@@ -14,10 +14,10 @@
 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
+No-Dependency-Checks
 --------------------
 
-    NO_DEPENDENCY_CHECKS=true
+    No-Dependency-Checks: true
 
 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.
@@ -38,40 +38,31 @@
 
     CQ_INCLUDE_TRYBOTS=skia.primary:Test-Win-MSVC-ShuttleC-GPU-GTX960-x86_64-Debug-ANGLE
 
-TBR
----
 
-If you are a Skia committer and cannot wait for a review,
-then you can include the TBR keyword in your CL's description.
-
-Example:
-
-    TBR=rmistry@google.com
-
-NOTREECHECKS
-------------
+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:
 
-    NOTREECHECKS=true
+    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).
 
-NOPRESUBMIT
------------
+No-Presubmit
+------------
 
 If you want to skip the presubmit checks, add the following line to the CL description:
 
-    NOPRESUBMIT=true
+    No-Presubmit: true
 
-NOTRY
------
+No-Try
+------
 
 If you cannot wait for the try job results, you can add the following line to the CL description:
 
-    NOTRY=true
+    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.
 
diff --git a/site/dev/contrib/jumper.md b/site/dev/contrib/jumper.md
new file mode 100644
index 0000000..520876c
--- /dev/null
+++ b/site/dev/contrib/jumper.md
@@ -0,0 +1,112 @@
+Contributing to SkJumper
+========================
+
+SkJumper is the execution engine of SkRasterPipeline, a system we've been using
+to accelerate CPU-bound work inside Skia, most notably color-space conversions
+and color-correct drawing.
+
+(This is where I'd put my link to design document if I had one...)
+
+SkJumper is more annoying to contribute to than most Skia code because of its
+offline compilation step.  `src/jumper/build_stages.py` compiles
+`src/jumper/SkJumper_stages.cpp` several different ways and parses the object
+files it generates into `src/jumper/SkJumper_generated.S` and
+`src/jumper/SkJumper_generated_win.S`.  This document is designed to guide you
+through this process and ease some of that annoyance.
+
+One-time Setup
+--------------
+
+To run `build_stages.py` you need Clang 4.0 and objdump, and probably want
+ccache.  It's best that Clang is exactly the same version we typically use (as
+of writing 4.0.0) and you'll need objdump to be compiled with support for
+x86-64, ARMv7, and ARMv8.
+
+The easiest way to satisfy these contraints is to get your hands on a Mac and
+install [Homebrew](https://brew.sh).  Once you have `brew` installed, run this
+to get the tools you need:
+
+<!--?prettify lang=sh?-->
+
+    brew install llvm binutils ccache
+
+Running `build_stages.py`
+-------------------------
+
+With your tools installed, try a no-op run of `build_stages.py`:
+
+<!--?prettify lang=sh?-->
+
+    python src/jumper/build_stages.py path/to/clang-4.0 path/to/gobjdump path/to/ccache
+    git status
+
+When you run `git status` you should see a bunch of untracked `.o` files
+sitting in skia/, and no changes to `src/jumper/SkJumper_generated*.S`.
+That's good.  Those object files are the intermediates we parse to produce
+the assembly files.  We just leave them around in case you want to look at
+them yourself.  If you don't like them, it's safe to just
+
+<!--?prettify lang=sh?-->
+
+    rm *.o
+
+If `clang-4.0`, `gobjdump`, and `ccache` are on your path, `build_stages.py`
+should find them without you needing to pass them on the command line.
+
+Make A Change
+-------------
+
+Let's use the `from_srgb` stage as a little playground to make a real change.
+Linearizing sRGB encoded bytes is slow, so let's pretend we've decided to trade
+quality for speed, approximating the existing implementation with a simple square.
+
+Open up `SkJumper_stages.cpp` and find the `from_srgb` stage.  It'll look like
+
+<!--?prettify lang=cc?-->
+
+    STAGE(from_srgb) {
+        ...
+    }
+
+Let's replace whatever's there with our fast approximation:
+
+<!--?prettify lang=cc?-->
+
+    STAGE(from_srgb) {
+        r *= r;
+        g *= g;
+        b *= b;
+    }
+
+When you save and re-run `build_stages.py`, you should now see changes to
+`src/jumper/SkJumper_generated.S` and `src/jumper/SkJumper_generated_win.S`.
+If you can't read assembly, no big deal.  If you can, run `git diff`.  You
+should see the various `sk_from_srgb_*` functions get dramatically simpler,
+something like three multiplies and a couple other bookkeeping instructions.
+
+It's not unusual for isolated changes in one stage to cause seemingly unrelated
+changes in another.  When adding or removing any code you'll usually see all
+the comments in branch instructions change a little bit, but the actual
+instruction on the left won't change.  When adding or removing uses of
+constants, you'll often see both the comment and instruction on the left change
+for other loads of constants from memory, especially on x86-64.  You'll also
+see some code that looks like garbage change; those are the constants.  If
+any of this worries you, please do go running to someone who knows more for
+help, but odds are everything is fine.
+
+At this point you can re-build Skia, run DM, compare images, etc. as normal.
+Any time you change `SkJumper_stages.cpp`, you need to re-run `build_stages.py`
+for those changes to take effect.  Believe me, I'd bake this into our GN build
+if I could figure out a way.
+
+Adding a new Stage
+------------------
+
+Adding a new stage is a lot like changing an existing stage.  Edit
+`SkJumper_stages.cpp`, run `build_stages.py`, build Skia, test, repeat until
+correct.
+
+You'll just need to also edit `SkRasterPipeline.h` to add your new stage to the
+macro listing all the stages.  The stage name is the handle normal Skia code
+uses to refer to the stage abstractly, and the wiring between
+`SkRasterPipeline::foo` and `STAGE(foo) { ... }` should work automatically.
diff --git a/site/dev/sheriffing/android.md b/site/dev/sheriffing/android.md
index fd2544e..73c1f6e 100644
--- a/site/dev/sheriffing/android.md
+++ b/site/dev/sheriffing/android.md
@@ -4,7 +4,7 @@
 ### Contents ###
 
 *   [What does a Android RoboCop do?](#what_is_a_robocop)
-*   [Android Autoroller](#autoroller_doc)
+*   [Android Autorollers](#autoroller_doc)
 *   [View current and upcoming RoboCops](#view_current_upcoming_robocops)
 *   [How to swap RoboCop shifts](#how_to_swap)
 
@@ -23,22 +23,29 @@
 
 
 <a name="autoroller_doc"></a>
-Android Autoroller
-------------------
+Android Autorollers
+-------------------
 
-The Android autoroller runs on the [client.skia.internal](https://chromegw.corp.google.com/i/client.skia.internal/console) master using the [merge_into_android.py](https://chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave/+/master/skia/merge_into_android.py) recipe.
+The Android autoroller into the master branch runs on [https://android-master-roll.skia.org](android-master-roll.skia.org) and is accessible only to Googlers.<br/>
+The autoroller's status is displayed on Skia's [status page](https://status.skia.org/).
 
-If you need to stop the autoroller then do the following steps:
+You can send the autoroller into dry run mode via the UI. The uploaded change will not autosubmit when it is in dry run mode.
 
-* echo stop > /tmp/action
-* gsutil cp /tmp/action gs://skia-android-autoroller/action
+You can also stop the autoroller via the UI. This is useful in cases where a failure needs to be investigated and you do not want to waste TH resources by running unnecessary tests.
 
-To turn the autoroller back on:
+If you need any more information about the autoroller please look at [skia:5538](https://bugs.chromium.org/p/skia/issues/detail?id=5538) or ask rmistry@ / skiabot@.
 
-* echo start > /tmp/action
-* gsutil cp /tmp/action gs://skia-android-autoroller/action
+We also have autorollers into release branches (also restricted only to Googlers):
 
-If you need any more information about the autoroller please look at [skia:6065](https://bugs.chromium.org/p/skia/issues/detail?id=6065) or ask rmistry@ / skiabot@.
+* [https://android-o-roll.skia.org](https://android-o-roll.skia.org).
+
+Changes created by these rollers need to be manually approved.<br/>
+The changes created by the release rollers:
+
+* Include all authors of merged changes so that they can watch the roll.
+* Extracts all buganizer bugs of the form 'BUG=b/123' or 'Bug: b/456' and creates a single line in the merge change 'Bug: 123, 456'.
+* Collects all 'Test: ' lines and carries them over to the merge change.
+
 
 <a name="view_current_upcoming_robocops"></a>
 View current and upcoming RoboCops
diff --git a/site/dev/sheriffing/index.md b/site/dev/sheriffing/index.md
index af3fda7..c4f3b8a 100644
--- a/site/dev/sheriffing/index.md
+++ b/site/dev/sheriffing/index.md
@@ -29,7 +29,7 @@
 
 <a name="skia_tree"></a>
 ### Skia tree
-* Understand the [buildbots infrastructure](https://skia.org/dev/testing/buildbot).
+* Understand the [testing infrastructure](https://skia.org/dev/testing/automated_testing).
 * Start watching the [status page](https://status.skia.org) for bot breakages.
 * Track down people responsible for breakages and revert broken changes if there is no easy fix. You can use [blamer](#blamer) to help track down such changes.
 * Close and open the [tree](http://skia-tree-status.appspot.com).
@@ -114,7 +114,7 @@
 ### How to submit when the tree is closed
 
 * Submit manually using the "git cl land" with the --bypass-hooks flag.
-* Add "NOTREECHECKS=true" to your CL description and use the CQ as usual.
+* Add "No-Tree-Checks: true" to your CL description and use the CQ as usual.
 
 
 <a name="tree_closers"></a>
diff --git a/site/dev/testing/xsan.md b/site/dev/testing/xsan.md
index 56cc2be..8b71a13 100644
--- a/site/dev/testing/xsan.md
+++ b/site/dev/testing/xsan.md
@@ -3,8 +3,14 @@
 
 *Testing Skia with memory, address, and thread santizers.*
 
-Get Clang binaries
-------------------
+Downloading Clang Binaries (Googlers Only)
+------------------------------------------
+
+    CLANGDIR="${HOME}/clang"
+    python infra/bots/assets/clang_linux/download.py -t $CLANGDIR
+
+Building Clang from scratch
+---------------------------
 
     CLANGDIR="${HOME}/clang"
 
diff --git a/site/user/api/canvas.md b/site/user/api/canvas.md
index 3c92905..d9e1559 100644
--- a/site/user/api/canvas.md
+++ b/site/user/api/canvas.md
@@ -35,13 +35,13 @@
     #include "SkStream.h"
     #include "SkSurface.h"
     void raster(int width, int height,
-                void(*draw)(SkCanvas*),
+                void (*draw)(SkCanvas*),
                 const char* path) {
-        sk_sp<SkSurface> rasterSurface(
-                SkSurface::MakeRasterN32Premul(width, height));
+        sk_sp<SkSurface> rasterSurface =
+                SkSurface::MakeRasterN32Premul(width, height);
         SkCanvas* rasterCanvas = rasterSurface->getCanvas();
         draw(rasterCanvas);
-        sk_sp<SkImage> img(s->newImageSnapshot());
+        sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
         if (!img) { return; }
         sk_sp<SkData> png(img->encode());
         if (!png) { return; }
@@ -54,18 +54,20 @@
 
 <!--?prettify lang=cc?-->
 
+    #include <vector>
+    #include "SkSurface.h"
     std::vector<char> raster_direct(int width, int height,
-                                    void(*draw)(SkCanvas*)) {
-        SkImageInfo info = SkImageInfo::MakeN32(width, height);
+                                    void (*draw)(SkCanvas*)) {
+        SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
         size_t rowBytes = info.minRowBytes();
         size_t size = info.getSafeSize(rowBytes);
         std::vector<char> pixelMemory(size);  // allocate memory
-        sk_sp<SkSurface> surface(
+        sk_sp<SkSurface> surface =
                 SkSurface::MakeRasterDirect(
-                        info, &pixelMemory[0], rowBytes));
-        SkCanvas* canvas = surface.getCanvas();
+                        info, &pixelMemory[0], rowBytes);
+        SkCanvas* canvas = surface->getCanvas();
         draw(canvas);
-        return std::move(pixelMemory);
+        return pixelMemory;
     }
 
 <span id="gpu"></span>
@@ -81,6 +83,7 @@
 has been made current to the current thread when Skia calls are made.
 
 <!--?prettify lang=cc?-->
+
     #include "GrContext.h"
     #include "gl/GrGLInterface.h"
     #include "SkData.h"
@@ -88,7 +91,7 @@
     #include "SkStream.h"
     #include "SkSurface.h"
 
-    void gl_example(int width, int height, void(*draw)(SkCanvas*), const char* path) {
+    void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
         // You've already created your OpenGL context and bound it.
         const GrGLInterface* interface = nullptr;
         // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
@@ -125,10 +128,10 @@
     #include "SkDocument.h"
     #include "SkStream.h"
     void skpdf(int width, int height,
-               void(*draw)(SkCanvas*),
+               void (*draw)(SkCanvas*),
                const char* path) {
         SkFILEWStream pdfStream(path);
-        sk_sp<SkDocument> pdfDoc(SkDocument::MakePDF(&pdfStream));
+        sk_sp<SkDocument> pdfDoc = SkDocument::MakePDF(&pdfStream);
         SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
                                                 SkIntToScalar(height));
         draw(pdfCanvas);
@@ -143,17 +146,17 @@
 
 <!--?prettify lang=cc?-->
 
-    #include "SkPictureRecorder"
-    #include "SkPicture"
+    #include "SkPictureRecorder.h"
+    #include "SkPicture.h"
     #include "SkStream.h"
     void picture(int width, int height,
-                 void(*draw)(SkCanvas*),
+                 void (*draw)(SkCanvas*),
                  const char* path) {
         SkPictureRecorder recorder;
         SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
                                                             SkIntToScalar(height));
         draw(recordingCanvas);
-        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+        sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
         SkFILEWStream skpStream(path);
         // Open SKP files with `SampleApp --picture SKP_FILE`
         picture->serialize(&skpStream);
@@ -169,9 +172,9 @@
 <!--?prettify lang=cc?-->
 
     #include "SkNullCanvas.h"
-    void picture(int, int, void(*draw)(SkCanvas*), const char*) {
-        sk_sp<SkCanvas> nullCanvas(SkCreateNullCanvas());
-        draw(nullCanvas);  // NoOp
+    void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
+        std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
+        draw(nullCanvas.get());  // NoOp
     }
 
 <span id="skxps"></span>
@@ -184,16 +187,19 @@
 
     #include "SkDocument.h"
     #include "SkStream.h"
-    void skxps(int width, int height,
-               void(*draw)(SkCanvas*),
+    #ifdef SK_BUILD_FOR_WIN
+    void skxps(IXpsOMObjectFactory* factory;
+               int width, int height,
+               void (*draw)(SkCanvas*),
                const char* path) {
         SkFILEWStream xpsStream(path);
-        sk_sp<SkDocument> xpsDoc(SkDocument::MakeXPS(&pdfStream));
+        sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
         SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
                                                 SkIntToScalar(height));
         draw(xpsCanvas);
         xpsDoc->close();
     }
+    #endif
 
 <span id="sksvg"></span>
 SkSVG
@@ -207,16 +213,15 @@
     #include "SkSVGCanvas.h"
     #include "SkXMLWriter.h"
     void sksvg(int width, int height,
-               void(*draw)(SkCanvas*),
+               void (*draw)(SkCanvas*),
                const char* path) {
         SkFILEWStream svgStream(path);
         std::unique_ptr<SkXMLWriter> xmlWriter(
                 new SkXMLStreamWriter(&svgStream));
-        sk_sp<SkCanvas> svgCanvas(SkSVGCanvas::Create(
-                SkRect::MakeWH(SkIntToScalar(src.size().width()),
-                               SkIntToScalar(src.size().height())),
-                xmlWriter));
-        draw(svgCanvas);
+        SkRect bounds = SkRect::MakeIWH(width, height);
+        std::unique_ptr<SkCanvas> svgCanvas =
+            SkSVGCanvas::Make(bounds, xmlWriter.get());
+        draw(svgCanvas.get());
     }
 
 <span id="example"></span>
@@ -252,8 +257,8 @@
         canvas->drawPath(path, p);
     }
     DEF_TEST(FourBackends, r) {
-        raster( 256, 256, example, "out_raster.png" );
-        ganesh( 256, 256, example, "out_ganesh.png" );
-        skpdf(  256, 256, example, "out_skpdf.pdf"  );
-        picture(256, 256, example, "out_picture.skp");
+        raster(     256, 256, example, "out_raster.png" );
+        gl_example( 256, 256, example, "out_gpu.png"    );
+        skpdf(      256, 256, example, "out_skpdf.pdf"  );
+        picture(    256, 256, example, "out_picture.skp");
     }
diff --git a/site/user/api/index.md b/site/user/api/index.md
index e0e4450..3d02edd 100644
--- a/site/user/api/index.md
+++ b/site/user/api/index.md
@@ -44,9 +44,6 @@
 *   [SkImage](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkImage.html)
 *   [SkSurface](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkSurface.html)
 *   [SkPaint](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkPaint.html)
-*   [SkXfermode](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkXfermode.html)
-    -   [SkLerpXfermode](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkLerpXfermode.html)
-    -   [SkPixelXorXfermode](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkPixelXorXfermode.html)
 *   [SkShader](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkShader.html)
     -   [SkComposeShader](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkComposeShader.html)
     -   [SkPerlinNoiseShader](http://skia-doc.commondatastorage.googleapis.com/doxygen/doxygen/html/classSkPerlinNoiseShader.html)
diff --git a/site/user/build.md b/site/user/build.md
index 9d43fc2..bfcb18a 100644
--- a/site/user/build.md
+++ b/site/user/build.md
@@ -133,15 +133,17 @@
 Windows
 -------
 
-Skia can build on Windows with Visual Studio 2015 Update 3.  No older or newer
-version is supported. The bots use a packaged toolchain, which you may be able
-to download like this:
+Skia can build on Windows with Visual Studio 2015 Update 3, or Visual Studio
+2017 by setting `msvc = 2017` in GN.  No older versions are supported. The bots
+use a packaged 2015 toolchain, which Googlers can download like this:
 
     python infra/bots/assets/win_toolchain/download.py -t C:/toolchain
 
 If you pass that downloaded path to GN via `windk`, you can build using that
 toolchain instead of your own from Visual Studio.  This toolchain is the only
-way we support 32-bit builds, by also setting `target_cpu="x86"`.
+way we support 32-bit builds with 2015, by also setting `target_cpu="x86"`.
+32-bit builds should work with the default 2017 install if you follow the
+directions GN prints to set up your environment.
 
 ### Visual Studio Solutions
 
diff --git a/site/user/sample/architecture.png b/site/user/sample/architecture.png
new file mode 100644
index 0000000..a2b74cf
--- /dev/null
+++ b/site/user/sample/architecture.png
Binary files differ
diff --git a/site/user/sample/color.md b/site/user/sample/color.md
new file mode 100644
index 0000000..28d86d3
--- /dev/null
+++ b/site/user/sample/color.md
@@ -0,0 +1,193 @@
+Color Correct Skia
+==================
+
+Why is Skia Color Correct?
+--------------------------
+
+A color space is a **gamut** and a **transfer function**.
+
+Gamut refers to the **available range of colors** of a particular in an image or on a display
+device.  Being gamut correct means that we will display colors as the designer intended and
+consistently across display devices.  A common problem with new “wide gamut” devices and
+uncorrected colors is illustrated below.
+
+Device Dependent Color (Wrong)
+
+<img src='gamut_wrong.png'>
+
+Gamut Corrected Color
+
+<img src='gamut_correct.png'>
+
+Transfer function refers to **a non-linear encoding of pixel values**.  A common transfer function
+is shown below.
+
+<img src='transfer_fn.png'>
+
+If we ignore the transfer function and treat non-linear values as if they are linear (when
+filtering, blending, anti-aliasing, multiplying), everything gets “too dark”.
+
+For example, we should see yellow (not brown) as the average of red and green light.
+
+Ignore Transfer Function
+
+<img src='gradient_wrong.png'>
+
+Apply Transfer Function
+
+<img src='gradient_correct.png'>
+
+Also, we should maintain fine detail when anti-aliasing (or downscaling).
+
+Ignore Transfer Function
+
+<img src='detail_wrong.png'>
+
+Apply Transfer Function
+
+<img src='detail_correct.png'>
+
+Skia Architecture for Color Correctness
+---------------------------------------
+
+<img src='architecture.png'>
+
+The major stages of the Skia drawing pipeline (premultiplication, filtering, blending) all assume
+linear inputs and linear outputs.  Also, because they are linear operations, they are
+interchangeable.
+
+The gamut transform is a new operation (3x3 matrix) in the pipeline, but with similar properties:
+it is a linear operation with linear inputs and linear outputs.
+
+The important shift in logic from the legacy pipeline is that we actually apply the transfer
+function to transform the pixels to linear values before performing the linear operations.
+
+The most common transfer function, sRGB, is actually free on GPU!  GPU hardware can transform sRGB
+to linear on reads and linear to sRGB on writes.
+
+Best Practices for Color Correct Skia
+-------------------------------------
+
+In order to perform color correct rendering, Skia needs to know the **SkColorSpace** of the content
+that you draw and the **SkColorSpace** of the surface that you draw to.  There are useful factories
+to make color spaces.
+
+<!--?prettify lang=cc?-->
+
+	// Really common color spaces
+	sk_sp<SkColorSpace> MakeSRGB();
+	sk_sp<SkColorSpace> MakeSRGBLinear();
+	
+	// Choose a common gamut and a common transfer function
+	sk_sp<SkColorSpace> MakeRGB(RenderTargetGamma, Gamut);
+	
+	// Create a color space from an ICC profile
+	sk_sp<SkColorSpace> MakeICC();
+
+Starting with **sources** (the things that draw you draw), there are a number of ways to make sure
+that they are tagged with a color space.
+
+**SkColor** (stored on **SkPaint**) is assumed to be in the sRGB color space - meaning that it
+is in the sRGB gamut and encoded with the sRGB transfer function.
+
+**SkShaders** (also stored on **SkPaint**) can be used to create more complex colors.  Color and
+gradient shaders typically accept **SkColor4f** (float colors).  These high precision colors
+can be in any gamut, but must have a linear transfer function.
+
+<!--?prettify lang=cc?-->
+
+	// Create a high precision color in a particular color space
+	sk_sp<SkShader> MakeColorShader(const SkColor4f&, sk_sp<SkColorSpace>);
+	
+	// Create a gradient shader in a particular color space
+	sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const SkColor4f colors[2],
+	                           sk_sp<SkColorSpace>, ...);
+	
+	// Many more variations of shaders...
+	// Remember that SkColor is always assumed to be sRGB as a convenience
+
+**SkImage** is the preferred representation for image sources.  It is easy to create **SkImages**
+ that are tagged with color spaces.
+
+<!--?prettify lang=cc?-->
+	
+	// Create an image from encoded data (jpeg, png, etc.)
+	// Will be tagged with the color space of the encoded data
+	sk_sp<SkImage> MakeFromEncoded(sk_sp<SkData> encoded);
+	
+	// Create an image from a texture in a particular color space
+	// Caution: There are versions of this constructor that do not take an
+	//          SkColorSpace.  But without an SkColorSpace, Skia does not have
+	//          enough information to draw correctly.
+	sk_sp<SkImage> MakeFromTexture(GrContext*, const GrBackendTextureDesc&,
+	                               SkAlphaType, sk_sp<SkColorSpace>, ...);
+
+**SkBitmap** is another (not preferred) representation for image sources.  Be careful to not forget
+the color space.
+
+<!--?prettify lang=cc?-->
+
+	SkBitmap bitmap;
+	bitmap.allocN32Pixels(); // Bad: What is the color space?
+	
+	SkBitmap bitmap;
+	SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
+	bitmap.allocPixels(info); // Bad: N32 is shorthand for 8888, no color space
+	
+	SkBitmap bitmap;
+	SkImageInfo info = SkImageInfo::MakeS32(width, height, kPremul_SkAlphaType);
+	bitmap.allocPixels(info); // Good: S32 is shorthand for 8888, sRGB
+
+**SkImageInfo** is a useful struct for providing information about pixel buffers.  Remember to use
+the color correct variants.
+
+<!--?prettify lang=cc?-->
+
+	// sRGB, 8888
+	SkImageInfo MakeS32(int width, int height, SkAlphaType);
+	
+	// Create an SkImageInfo in a particular color space
+	SkImageInfo Make(int width, int height, SkColorType, SkAlphaType,
+	                 sk_sp<SkColorSpace>);
+
+Moving to **destinations** (the surfaces that you draw to), there are also constructors that allow
+them to be tagged with color spaces.
+
+<!--?prettify lang=cc?-->
+
+	// Raster backed: Make sure |info| has a non-null color space
+	sk_sp<SkSurface> MakeRaster(const SkImageInfo& info);
+	
+	// Gpu backed: Make sure |info| has a non-null color space
+	sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext, SkBudgeted,
+	                                             const SkImageInfo& info);
+
+Opting In To Color Correct Skia
+-------------------------------
+
+By itself, **adding a color space tag to a source will not change draw behavior**.  In fact,
+tagging sources with color spaces is always a best practice, regardless of whether we want Skia’s
+color correct behavior.
+
+Adding a color space tag to the **destination is the trigger that turns on Skia color correct
+behavior**.
+
+Drawing a source without a color space to a destination with a color space is undefined.  Skia
+cannot know how to draw without knowing the color space of the source.
+
+<style scoped><!--
+#colortable {border-collapse:collapse;}
+#colortable tr th, #colortable tr td {border:#888888 2px solid;padding: 5px;}
+--></style>
+<table id="colortable">
+<tr><th>Source SkColorSpace</th> <th>Destination SkColorSpace</th>  <th>Behavior</th></tr>
+<tr><td>Non-null</td>            <td>Non-null</td>                  <td>Color Correct Skia</td></tr>
+<tr><td>Null</td>                <td>Non-null</td>                  <td>Undefined</td></tr>
+<tr><td>Non-null</td>            <td>Null</td>                      <td>Legacy Skia</td></tr>
+<tr><td>Null</td>                <td>Null</td>                      <td>Legacy Skia</td></tr>
+</table>
+
+It is possible to create **an object that is both a source and destination**, if Skia will both
+draw into it and then draw it somewhere else.  The same rules from above still apply, but it is
+subtle that the color space tag could have an effect (or no effect) depending on how the object is
+used.
\ No newline at end of file
diff --git a/site/user/sample/detail_correct.png b/site/user/sample/detail_correct.png
new file mode 100644
index 0000000..3f14885e
--- /dev/null
+++ b/site/user/sample/detail_correct.png
Binary files differ
diff --git a/site/user/sample/detail_wrong.png b/site/user/sample/detail_wrong.png
new file mode 100644
index 0000000..bd3c836
--- /dev/null
+++ b/site/user/sample/detail_wrong.png
Binary files differ
diff --git a/site/user/sample/gamut_correct.png b/site/user/sample/gamut_correct.png
new file mode 100644
index 0000000..bb94caf
--- /dev/null
+++ b/site/user/sample/gamut_correct.png
Binary files differ
diff --git a/site/user/sample/gamut_wrong.png b/site/user/sample/gamut_wrong.png
new file mode 100644
index 0000000..a5e584d
--- /dev/null
+++ b/site/user/sample/gamut_wrong.png
Binary files differ
diff --git a/site/user/sample/gradient_correct.png b/site/user/sample/gradient_correct.png
new file mode 100644
index 0000000..5649995
--- /dev/null
+++ b/site/user/sample/gradient_correct.png
Binary files differ
diff --git a/site/user/sample/gradient_wrong.png b/site/user/sample/gradient_wrong.png
new file mode 100644
index 0000000..8b70450
--- /dev/null
+++ b/site/user/sample/gradient_wrong.png
Binary files differ
diff --git a/site/user/sample/transfer_fn.png b/site/user/sample/transfer_fn.png
new file mode 100644
index 0000000..14d38bd
--- /dev/null
+++ b/site/user/sample/transfer_fn.png
Binary files differ
diff --git a/site/user/sample/viewer.md b/site/user/sample/viewer.md
index 66330eb..0f47b98 100644
--- a/site/user/sample/viewer.md
+++ b/site/user/sample/viewer.md
@@ -26,10 +26,13 @@
 
     ./platform_tools/android/bin/android_build_app -C <out_dir> viewer
 
-*   **out_dir** is the ninja out directory that you want to use to build the app
+*   **out_dir** is the ninja out directory for android (e.g., `out/arm64`) that you want to use to
+build the app
 
 Upon completion of the script the APK can be found at <out_dir>/viewer.apk
 
+To load SKPs in the Android viewer place them in /data/local/tmp/skps.
+
 iOS
 ---
 The viewer is currently not supported on iOS.
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 96ad72a..b30dd52 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -209,7 +209,7 @@
 
 SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
     if (!is_valid_sample_size(sampleSize)) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
 
     // Fast path for when we are not scaling.
@@ -230,7 +230,7 @@
 
 SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
     if (!is_valid_sample_size(sampleSize)) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
 
     // We require that the input subset is a subset that is supported by SkAndroidCodec.
@@ -238,7 +238,7 @@
     // are made to the subset.
     SkIRect copySubset = subset;
     if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
-        return SkISize::Make(0, 0);
+        return {0, 0};
     }
 
     // If the subset is the entire image, for consistency, use getSampledDimensions().
@@ -248,8 +248,8 @@
 
     // This should perhaps call a virtual function, but currently both of our subclasses
     // want the same implementation.
-    return SkISize::Make(get_scaled_dimension(subset.width(), sampleSize),
-                get_scaled_dimension(subset.height(), sampleSize));
+    return {get_scaled_dimension(subset.width(), sampleSize),
+            get_scaled_dimension(subset.height(), sampleSize)};
 }
 
 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
diff --git a/src/codec/SkBmpBaseCodec.cpp b/src/codec/SkBmpBaseCodec.cpp
new file mode 100644
index 0000000..0e74435
--- /dev/null
+++ b/src/codec/SkBmpBaseCodec.cpp
@@ -0,0 +1,16 @@
+/*
+ * 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 "SkBmpBaseCodec.h"
+#include "../private/SkMalloc.h"
+
+SkBmpBaseCodec::~SkBmpBaseCodec() {}
+
+SkBmpBaseCodec::SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
+                               uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
+    : INHERITED(width, height, info, stream, bitsPerPixel, rowOrder)
+    , fSrcBuffer(sk_malloc_flags(this->srcRowBytes(), 0))
+{}
diff --git a/src/codec/SkBmpBaseCodec.h b/src/codec/SkBmpBaseCodec.h
new file mode 100644
index 0000000..0b9f919
--- /dev/null
+++ b/src/codec/SkBmpBaseCodec.h
@@ -0,0 +1,38 @@
+/*
+ * 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 SkBmpBaseCodec_DEFINED
+#define SkBmpBaseCodec_DEFINED
+
+#include "SkBmpCodec.h"
+#include "SkTemplates.h"
+
+/*
+ * Common base class for SkBmpStandardCodec and SkBmpMaskCodec.
+ */
+class SkBmpBaseCodec : public SkBmpCodec {
+public:
+    ~SkBmpBaseCodec() override;
+
+    /*
+     * Whether fSrcBuffer was successfully created.
+     *
+     * If false, this Codec must not be used.
+     */
+    bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; }
+
+protected:
+    SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
+                   uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
+
+    uint8_t* srcBuffer() { return reinterpret_cast<uint8_t*>(fSrcBuffer.get()); }
+
+private:
+    SkAutoFree fSrcBuffer;
+
+    typedef SkBmpCodec INHERITED;
+};
+#endif // SkBmpBaseCodec_DEFINED
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 3d3782b..c21a863 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -151,15 +151,15 @@
     // Bmps embedded in Icos skip the first Bmp header
     if (!inIco) {
         // Read the first header and the size of the second header
-        std::unique_ptr<uint8_t[]> hBuffer(new uint8_t[kBmpHeaderBytesPlusFour]);
-        if (stream->read(hBuffer.get(), kBmpHeaderBytesPlusFour) !=
+        uint8_t hBuffer[kBmpHeaderBytesPlusFour];
+        if (stream->read(hBuffer, kBmpHeaderBytesPlusFour) !=
                 kBmpHeaderBytesPlusFour) {
             SkCodecPrintf("Error: unable to read first bitmap header.\n");
             return false;
         }
 
-        totalBytes = get_int(hBuffer.get(), 2);
-        offset = get_int(hBuffer.get(), 10);
+        totalBytes = get_int(hBuffer, 2);
+        offset = get_int(hBuffer, 10);
         if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
             SkCodecPrintf("Error: invalid starting location for pixel data\n");
             return false;
@@ -168,7 +168,7 @@
         // The size of the second (info) header in bytes
         // The size is the first field of the second header, so we have already
         // read the first four infoBytes.
-        infoBytes = get_int(hBuffer.get(), 14);
+        infoBytes = get_int(hBuffer, 14);
         if (infoBytes < kBmpOS2V1Bytes) {
             SkCodecPrintf("Error: invalid second header size.\n");
             return false;
@@ -185,12 +185,12 @@
         offset = 0;
 
         // Read the size of the second header
-        std::unique_ptr<uint8_t[]> hBuffer(new uint8_t[4]);
-        if (stream->read(hBuffer.get(), 4) != 4) {
+        uint8_t hBuffer[4];
+        if (stream->read(hBuffer, 4) != 4) {
             SkCodecPrintf("Error: unable to read size of second bitmap header.\n");
             return false;
         }
-        infoBytes = get_int(hBuffer.get(), 0);
+        infoBytes = get_int(hBuffer, 0);
         if (infoBytes < kBmpOS2V1Bytes) {
             SkCodecPrintf("Error: invalid second header size.\n");
             return false;
@@ -332,16 +332,15 @@
             switch (headerType) {
                 case kInfoV1_BmpHeaderType: {
                     // The V1 header stores the bit masks after the header
-                    std::unique_ptr<uint8_t[]> mBuffer(new uint8_t[kBmpMaskBytes]);
-                    if (stream->read(mBuffer.get(), kBmpMaskBytes) !=
-                            kBmpMaskBytes) {
+                    uint8_t buffer[kBmpMaskBytes];
+                    if (stream->read(buffer, kBmpMaskBytes) != kBmpMaskBytes) {
                         SkCodecPrintf("Error: unable to read bit inputMasks.\n");
                         return false;
                     }
                     maskBytes = kBmpMaskBytes;
-                    inputMasks.red = get_int(mBuffer.get(), 0);
-                    inputMasks.green = get_int(mBuffer.get(), 4);
-                    inputMasks.blue = get_int(mBuffer.get(), 8);
+                    inputMasks.red = get_int(buffer, 0);
+                    inputMasks.green = get_int(buffer, 4);
+                    inputMasks.blue = get_int(buffer, 8);
                     break;
                 }
                 case kInfoV2_BmpHeaderType:
@@ -482,8 +481,13 @@
 
                 // Set the image info and create a codec.
                 const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, bitsPerComponent);
-                *codecOut = new SkBmpStandardCodec(width, height, info, stream, bitsPerPixel,
-                        numColors, bytesPerColor, offset - bytesRead, rowOrder, isOpaque, inIco);
+                std::unique_ptr<SkBmpStandardCodec> codec(new SkBmpStandardCodec(width, height,
+                        info, stream, bitsPerPixel, numColors, bytesPerColor, offset - bytesRead,
+                        rowOrder, isOpaque, inIco));
+                if (!codec->didCreateSrcBuffer()) {
+                    return false;
+                }
+                *codecOut = codec.release();
             }
             return true;
         }
@@ -535,8 +539,12 @@
                     alpha = SkEncodedInfo::kOpaque_Alpha;
                 }
                 const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
-                *codecOut = new SkBmpMaskCodec(width, height, info, stream, bitsPerPixel,
-                        masks.release(), rowOrder);
+                std::unique_ptr<SkBmpMaskCodec> codec(new SkBmpMaskCodec(width, height, info,
+                        stream, bitsPerPixel, masks.release(), rowOrder));
+                if (!codec->didCreateSrcBuffer()) {
+                    return false;
+                }
+                *codecOut = codec.release();
             }
             return true;
         }
@@ -594,7 +602,7 @@
 
 SkBmpCodec::SkBmpCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
         uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(width, height, info, stream, SkColorSpace::MakeSRGB())
+    : INHERITED(width, height, info, kXformSrcColorFormat, stream, SkColorSpace::MakeSRGB())
     , fBitsPerPixel(bitsPerPixel)
     , fRowOrder(rowOrder)
     , fSrcRowBytes(SkAlign4(compute_row_bytes(width, fBitsPerPixel)))
@@ -645,15 +653,3 @@
 bool SkBmpCodec::onSkipScanlines(int count) {
     return this->skipRows(count);
 }
-
-void SkBmpCodec::applyColorXform(const SkImageInfo& dstInfo, void* dst, void* src) const {
-    SkColorSpaceXform* xform = this->colorXform();
-    if (xform) {
-        const SkColorSpaceXform::ColorFormat dstFormat = select_xform_format(dstInfo.colorType());
-        const SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(kXformSrcColorType);
-        const SkAlphaType alphaType = select_xform_alpha(dstInfo.alphaType(),
-                                                         this->getInfo().alphaType());
-        SkAssertResult(xform->apply(dstFormat, dst, srcFormat, src, dstInfo.width(),
-                                    alphaType));
-    }
-}
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index 75740f7..e956a9f 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -106,7 +106,6 @@
             const SkCodec::Options& options, SkPMColor inputColorPtr[],
             int* inputColorCount);
 
-    void applyColorXform(const SkImageInfo& dstInfo, void* dst, void* src) const;
     uint32_t* xformBuffer() const { return fXformBuffer.get(); }
     void resetXformBuffer(int count) { fXformBuffer.reset(new uint32_t[count]); }
 
@@ -115,6 +114,7 @@
      * than RGBA.
      */
     static const SkColorType kXformSrcColorType = kBGRA_8888_SkColorType;
+    static const auto kXformSrcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
 
 private:
 
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index 9612a23..1fc98a2 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -18,7 +18,6 @@
     : INHERITED(width, height, info, stream, bitsPerPixel, rowOrder)
     , fMasks(masks)
     , fMaskSwizzler(nullptr)
-    , fSrcBuffer(new uint8_t [this->srcRowBytes()])
 {}
 
 /*
@@ -81,7 +80,7 @@
                                            void* dst, size_t dstRowBytes,
                                            const Options& opts) {
     // Iterate over rows of the image
-    uint8_t* srcRow = fSrcBuffer.get();
+    uint8_t* srcRow = this->srcBuffer();
     const int height = dstInfo.height();
     for (int y = 0; y < height; y++) {
         // Read a row of the input
@@ -95,9 +94,8 @@
         void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
 
         if (this->colorXform()) {
-            SkImageInfo xformInfo = dstInfo.makeWH(fMaskSwizzler->swizzleWidth(), dstInfo.height());
             fMaskSwizzler->swizzle(this->xformBuffer(), srcRow);
-            this->applyColorXform(xformInfo, dstRow, this->xformBuffer());
+            this->applyColorXform(dstRow, this->xformBuffer(), fMaskSwizzler->swizzleWidth());
         } else {
             fMaskSwizzler->swizzle(dstRow, srcRow);
         }
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 50e285d..2f8c060 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -8,7 +8,7 @@
 #ifndef SkBmpMaskCodec_DEFINED
 #define SkBmpMaskCodec_DEFINED
 
-#include "SkBmpCodec.h"
+#include "SkBmpBaseCodec.h"
 #include "SkImageInfo.h"
 #include "SkMaskSwizzler.h"
 #include "SkTypes.h"
@@ -16,7 +16,7 @@
 /*
  * This class implements the decoding for bmp images using bit masks
  */
-class SkBmpMaskCodec : public SkBmpCodec {
+class SkBmpMaskCodec : public SkBmpBaseCodec {
 public:
 
     /*
@@ -57,8 +57,7 @@
 
     std::unique_ptr<SkMasks>        fMasks;
     std::unique_ptr<SkMaskSwizzler> fMaskSwizzler;
-    std::unique_ptr<uint8_t[]>      fSrcBuffer;
 
-    typedef SkBmpCodec INHERITED;
+    typedef SkBmpBaseCodec INHERITED;
 };
 #endif  // SkBmpMaskCodec_DEFINED
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index 1968616..c6d788b 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -331,7 +331,7 @@
     int decodedHeight = this->decodeRLE(decodeInfo, decodeDst, decodeRowBytes);
     if (this->colorXform() && decodeDst) {
         for (int y = 0; y < decodedHeight; y++) {
-            this->applyColorXform(dstInfo, dst, decodeDst);
+            this->applyColorXform(dst, decodeDst, dstInfo.width());
             decodeDst = SkTAddOffset<void>(decodeDst, decodeRowBytes);
             dst = SkTAddOffset<void>(dst, dstRowBytes);
         }
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index a0ad787..959e75b 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -25,11 +25,9 @@
     , fBytesPerColor(bytesPerColor)
     , fOffset(offset)
     , fSwizzler(nullptr)
-    , fSrcBuffer(new uint8_t[this->srcRowBytes()])
     , fIsOpaque(isOpaque)
     , fInIco(inIco)
     , fAndMaskRowBytes(fInIco ? SkAlign4(compute_row_bytes(this->getInfo().width(), 1)) : 0)
-    , fXformOnDecode(false)
 {}
 
 /*
@@ -124,13 +122,8 @@
             colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
         }
 
-        if (this->colorXform() && !fXformOnDecode) {
-            SkColorSpaceXform::ColorFormat dstFormat = select_xform_format_ct(dstColorType);
-            SkColorSpaceXform::ColorFormat srcFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
-            SkAlphaType xformAlphaType = select_xform_alpha(dstAlphaType,
-                                                            this->getInfo().alphaType());
-            SkAssertResult(this->colorXform()->apply(dstFormat, colorTable, srcFormat, colorTable,
-                                                     maxColors, xformAlphaType));
+        if (this->colorXform() && !this->xformOnDecode()) {
+            this->applyColorXform(colorTable, colorTable, maxColors);
         }
 
         // Set the color table
@@ -199,12 +192,8 @@
 
 SkCodec::Result SkBmpStandardCodec::onPrepareToDecode(const SkImageInfo& dstInfo,
         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
-    fXformOnDecode = false;
-    if (this->colorXform()) {
-        fXformOnDecode = apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color());
-        if (fXformOnDecode) {
-            this->resetXformBuffer(dstInfo.width());
-        }
+    if (this->xformOnDecode()) {
+        this->resetXformBuffer(dstInfo.width());
     }
 
     // Create the color table if necessary and prepare the stream for decode
@@ -231,7 +220,7 @@
     const int height = dstInfo.height();
     for (int y = 0; y < height; y++) {
         // Read a row of the input
-        if (this->stream()->read(fSrcBuffer.get(), this->srcRowBytes()) != this->srcRowBytes()) {
+        if (this->stream()->read(this->srcBuffer(), this->srcRowBytes()) != this->srcRowBytes()) {
             SkCodecPrintf("Warning: incomplete input stream.\n");
             return y;
         }
@@ -241,13 +230,12 @@
 
         void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
 
-        if (fXformOnDecode) {
+        if (this->xformOnDecode()) {
             SkASSERT(this->colorXform());
-            SkImageInfo xformInfo = dstInfo.makeWH(fSwizzler->swizzleWidth(), dstInfo.height());
-            fSwizzler->swizzle(this->xformBuffer(), fSrcBuffer.get());
-            this->applyColorXform(xformInfo, dstRow, this->xformBuffer());
+            fSwizzler->swizzle(this->xformBuffer(), this->srcBuffer());
+            this->applyColorXform(dstRow, this->xformBuffer(), fSwizzler->swizzleWidth());
         } else {
-            fSwizzler->swizzle(dstRow, fSrcBuffer.get());
+            fSwizzler->swizzle(dstRow, this->srcBuffer());
         }
     }
 
@@ -321,7 +309,7 @@
     SkPMColor* dstPtr = (SkPMColor*) dst;
     for (int y = 0; y < dstInfo.height(); y++) {
         // The srcBuffer will at least be large enough
-        if (stream->read(fSrcBuffer.get(), fAndMaskRowBytes) != fAndMaskRowBytes) {
+        if (stream->read(this->srcBuffer(), fAndMaskRowBytes) != fAndMaskRowBytes) {
             SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
             return;
         }
@@ -346,7 +334,7 @@
             int modulus;
             SkTDivMod(srcX, 8, &quotient, &modulus);
             uint32_t shift = 7 - modulus;
-            uint64_t alphaBit = (fSrcBuffer.get()[quotient] >> shift) & 0x1;
+            uint64_t alphaBit = (this->srcBuffer()[quotient] >> shift) & 0x1;
             applyMask(dstRow, dstX, alphaBit);
             srcX += sampleX;
         }
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
index 740db8e..ec3d707 100644
--- a/src/codec/SkBmpStandardCodec.h
+++ b/src/codec/SkBmpStandardCodec.h
@@ -7,7 +7,7 @@
 #ifndef SkBmpStandardCodec_DEFINED
 #define SkBmpStandardCodec_DEFINED
 
-#include "SkBmpCodec.h"
+#include "SkBmpBaseCodec.h"
 #include "SkColorTable.h"
 #include "SkImageInfo.h"
 #include "SkSwizzler.h"
@@ -17,7 +17,7 @@
  * This class implements the decoding for bmp images that use "standard" modes,
  * which essentially means they do not contain bit masks or RLE codes.
  */
-class SkBmpStandardCodec : public SkBmpCodec {
+class SkBmpStandardCodec : public SkBmpBaseCodec {
 public:
 
     /*
@@ -92,12 +92,10 @@
     const uint32_t              fBytesPerColor;
     const uint32_t              fOffset;
     std::unique_ptr<SkSwizzler> fSwizzler;
-    std::unique_ptr<uint8_t[]>  fSrcBuffer;
     const bool                  fIsOpaque;
     const bool                  fInIco;
     const size_t                fAndMaskRowBytes; // only used for fInIco decodes
-    bool                        fXformOnDecode;
 
-    typedef SkBmpCodec INHERITED;
+    typedef SkBmpBaseCodec INHERITED;
 };
 #endif  // SkBmpStandardCodec_DEFINED
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 5bdb3e2..2cae6db 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -114,10 +114,12 @@
     return NewFromStream(new SkMemoryStream(data), reader);
 }
 
-SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
+SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info,
+        XformFormat srcFormat, SkStream* stream,
         sk_sp<SkColorSpace> colorSpace, Origin origin)
     : fEncodedInfo(info)
     , fSrcInfo(info.makeImageInfo(width, height, std::move(colorSpace)))
+    , fSrcXformFormat(srcFormat)
     , fStream(stream)
     , fNeedsRewind(false)
     , fOrigin(origin)
@@ -126,10 +128,11 @@
     , fCurrScanline(-1)
 {}
 
-SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo, SkStream* stream,
-        Origin origin)
+SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
+        XformFormat srcFormat, SkStream* stream, Origin origin)
     : fEncodedInfo(info)
     , fSrcInfo(imageInfo)
+    , fSrcXformFormat(srcFormat)
     , fStream(stream)
     , fNeedsRewind(false)
     , fOrigin(origin)
@@ -474,9 +477,29 @@
     }
 }
 
+static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType colorType) {
+    switch (colorType) {
+        case kRGBA_8888_SkColorType:
+            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+        case kBGRA_8888_SkColorType:
+            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
+        case kRGB_565_SkColorType:
+        case kIndex_8_SkColorType:
+#ifdef SK_PMCOLOR_IS_RGBA
+            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+#else
+            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
+#endif
+        default:
+            SkASSERT(false);
+            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+    }
+}
+
 bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo,
                                    SkTransferFunctionBehavior premulBehavior) {
     fColorXform = nullptr;
+    fXformOnDecode = false;
     bool needsColorCorrectPremul = needs_premul(dstInfo, fEncodedInfo) &&
                                    SkTransferFunctionBehavior::kRespect == premulBehavior;
     if (needs_color_xform(dstInfo, fSrcInfo, needsColorCorrectPremul)) {
@@ -485,7 +508,47 @@
         if (!fColorXform) {
             return false;
         }
+
+        // We will apply the color xform when reading the color table unless F16 is requested.
+        fXformOnDecode = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
+            || kRGBA_F16_SkColorType == dstInfo.colorType();
+        if (fXformOnDecode) {
+            fDstXformFormat = select_xform_format(dstInfo.colorType());
+        } else {
+            fDstXformFormat = select_xform_format_ct(dstInfo.colorType());
+        }
     }
 
     return true;
 }
+
+void SkCodec::applyColorXform(void* dst, const void* src, int count, SkAlphaType at) const {
+    SkASSERT(fColorXform);
+    SkAssertResult(fColorXform->apply(fDstXformFormat, dst,
+                                      fSrcXformFormat, src,
+                                      count, at));
+}
+
+void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
+    auto alphaType = select_xform_alpha(fDstInfo.alphaType(), fSrcInfo.alphaType());
+    this->applyColorXform(dst, src, count, alphaType);
+}
+
+std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {
+    const int frameCount = this->getFrameCount();
+    SkASSERT(frameCount >= 0);
+    if (frameCount <= 0) {
+        return std::vector<FrameInfo>{};
+    }
+
+    if (frameCount == 1 && !this->onGetFrameInfo(0, nullptr)) {
+        // Not animated.
+        return std::vector<FrameInfo>{};
+    }
+
+    std::vector<FrameInfo> result(frameCount);
+    for (int i = 0; i < frameCount; ++i) {
+        SkAssertResult(this->onGetFrameInfo(i, &result[i]));
+    }
+    return result;
+}
diff --git a/src/codec/SkCodecAnimation.h b/src/codec/SkCodecAnimation.h
index b0495b8..46a878a 100644
--- a/src/codec/SkCodecAnimation.h
+++ b/src/codec/SkCodecAnimation.h
@@ -44,6 +44,25 @@
         RestorePrevious_DisposalMethod  = 3,
     };
 
+    /**
+     * How to blend the current frame.
+     */
+    enum class Blend {
+        /**
+         *  Blend with the prior frame. This is the typical case, supported
+         *  by all animated image types.
+         */
+        kPriorFrame,
+
+        /**
+         *  Do not blend.
+         *
+         *  This frames pixels overwrite previous pixels "blending" with
+         *  the background color of transparent.
+         */
+        kBG,
+    };
+
 private:
     SkCodecAnimation();
 };
diff --git a/src/codec/SkCodecImageGenerator.cpp b/src/codec/SkCodecImageGenerator.cpp
index 56ca21e..bf794d6 100644
--- a/src/codec/SkCodecImageGenerator.cpp
+++ b/src/codec/SkCodecImageGenerator.cpp
@@ -17,28 +17,35 @@
     return std::unique_ptr<SkImageGenerator>(new SkCodecImageGenerator(codec, data));
 }
 
-static SkImageInfo make_premul(const SkImageInfo& info) {
+static SkImageInfo adjust_info(const SkImageInfo& info) {
+    SkImageInfo newInfo = info;
     if (kUnpremul_SkAlphaType == info.alphaType()) {
-        return info.makeAlphaType(kPremul_SkAlphaType);
+        newInfo = newInfo.makeAlphaType(kPremul_SkAlphaType);
     }
 
-    return info;
+    if (kIndex_8_SkColorType == info.colorType()) {
+        newInfo = newInfo.makeColorType(kN32_SkColorType);
+    }
+
+    return newInfo;
 }
 
 SkCodecImageGenerator::SkCodecImageGenerator(SkCodec* codec, sk_sp<SkData> data)
-    : INHERITED(make_premul(codec->getInfo()))
+    : INHERITED(adjust_info(codec->getInfo()))
     , fCodec(codec)
     , fData(std::move(data))
 {}
 
-SkData* SkCodecImageGenerator::onRefEncodedData(GrContext* ctx) {
+SkData* SkCodecImageGenerator::onRefEncodedData() {
     return SkRef(fData.get());
 }
 
 bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-        SkPMColor ctable[], int* ctableCount) {
-    SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, nullptr, ctable,
-            ctableCount);
+                                        const Options& opts) {
+    SkCodec::Options codecOpts;
+    codecOpts.fPremulBehavior = opts.fBehavior;
+    SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, &codecOpts, nullptr,
+                                               nullptr);
     switch (result) {
         case SkCodec::kSuccess:
         case SkCodec::kIncompleteInput:
diff --git a/src/codec/SkCodecImageGenerator.h b/src/codec/SkCodecImageGenerator.h
index 727a747..4d0c078 100644
--- a/src/codec/SkCodecImageGenerator.h
+++ b/src/codec/SkCodecImageGenerator.h
@@ -21,10 +21,10 @@
     static std::unique_ptr<SkImageGenerator> MakeFromEncodedCodec(sk_sp<SkData>);
 
 protected:
-    SkData* onRefEncodedData(GrContext* ctx) override;
+    SkData* onRefEncodedData() override;
 
-    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
-            int* ctableCount) override;
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options& opts)
+                     override;
 
     bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const override;
 
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index 5c8dc87..b69e488 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -323,11 +323,6 @@
     return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType;
 }
 
-static inline bool apply_xform_on_decode(SkColorType dstColorType, SkEncodedInfo::Color srcColor) {
-    // We will apply the color xform when reading the color table unless F16 is requested.
-    return SkEncodedInfo::kPalette_Color != srcColor || kRGBA_F16_SkColorType == dstColorType;
-}
-
 /*
  * Alpha Type Conversions
  * - kOpaque to kOpaque, kUnpremul, kPremul is valid
@@ -365,23 +360,4 @@
     }
 }
 
-static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType colorType) {
-    switch (colorType) {
-        case kRGBA_8888_SkColorType:
-            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
-        case kBGRA_8888_SkColorType:
-            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
-        case kRGB_565_SkColorType:
-        case kIndex_8_SkColorType:
-#ifdef SK_PMCOLOR_IS_RGBA
-            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
-#else
-            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
-#endif
-        default:
-            SkASSERT(false);
-            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
-    }
-}
-
 #endif // SkCodecPriv_DEFINED
diff --git a/src/codec/SkFrameHolder.h b/src/codec/SkFrameHolder.h
new file mode 100644
index 0000000..e92b40f
--- /dev/null
+++ b/src/codec/SkFrameHolder.h
@@ -0,0 +1,186 @@
+/*
+ * 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 SkFrameHolder_DEFINED
+#define SkFrameHolder_DEFINED
+
+#include "SkTypes.h"
+#include "SkCodecAnimation.h"
+
+/**
+ *  Base class for a single frame of an animated image.
+ *
+ *  Separate from SkCodec::FrameInfo, which is a pared down
+ *  interface that only contains the info the client needs.
+ */
+class SkFrame : public SkNoncopyable {
+public:
+    SkFrame(int id)
+        : fId(id)
+        , fHasAlpha(false)
+        , fRequiredFrame(kUninitialized)
+        , fDisposalMethod(SkCodecAnimation::Keep_DisposalMethod)
+        , fDuration(0)
+        , fBlend(SkCodecAnimation::Blend::kPriorFrame)
+    {
+        fRect.setEmpty();
+    }
+
+    virtual ~SkFrame() {}
+
+    /**
+     *  0-based index of the frame in the image sequence.
+     */
+    int frameId() const { return fId; }
+
+    /**
+     *  Whether this frame reports alpha.
+     *
+     *  This only considers the rectangle of this frame, and
+     *  considers it to have alpha even if it is opaque once
+     *  blended with the frame behind it.
+     */
+    bool reportsAlpha() const {
+        return this->onReportsAlpha();
+    }
+
+    /**
+     *  Cached value representing whether the frame has alpha,
+     *  after compositing with the prior frame.
+     */
+    bool hasAlpha() const { return fHasAlpha; }
+
+    /**
+     *  Cache whether the finished frame has alpha.
+     */
+    void setHasAlpha(bool alpha) { fHasAlpha = alpha; }
+
+    /**
+     *  Whether enough of the frame has been read to determine
+     *  fRequiredFrame and fHasAlpha.
+     */
+    bool reachedStartOfData() const { return fRequiredFrame != kUninitialized; }
+
+    /**
+     *  The frame this one depends on.
+     *
+     *  Must not be called until fRequiredFrame has been set properly.
+     */
+    int getRequiredFrame() const {
+        SkASSERT(this->reachedStartOfData());
+        return fRequiredFrame;
+    }
+
+    /**
+     *  Set the frame that this frame depends on.
+     */
+    void setRequiredFrame(int req) { fRequiredFrame = req; }
+
+    /**
+     *  Set the rectangle that is updated by this frame.
+     */
+    void setXYWH(int x, int y, int width, int height) {
+        fRect.setXYWH(x, y, width, height);
+    }
+
+    /**
+     *  The rectangle that is updated by this frame.
+     */
+    SkIRect frameRect() const { return fRect; }
+
+    int xOffset() const { return fRect.x(); }
+    int yOffset() const { return fRect.y(); }
+    int width()   const { return fRect.width(); }
+    int height()  const { return fRect.height(); }
+
+    SkCodecAnimation::DisposalMethod getDisposalMethod() const {
+        return fDisposalMethod;
+    }
+
+    void setDisposalMethod(SkCodecAnimation::DisposalMethod disposalMethod) {
+        fDisposalMethod = disposalMethod;
+    }
+
+    /**
+     * Set the duration (in ms) to show this frame.
+     */
+    void setDuration(int duration) {
+        fDuration = duration;
+    }
+
+    /**
+     *  Duration in ms to show this frame.
+     */
+    int getDuration() const {
+        return fDuration;
+    }
+
+    void setBlend(SkCodecAnimation::Blend blend) {
+        fBlend = blend;
+    }
+
+    SkCodecAnimation::Blend getBlend() const {
+        return fBlend;
+    }
+
+protected:
+    virtual bool onReportsAlpha() const = 0;
+
+private:
+    static constexpr int kUninitialized = -2;
+
+    const int                           fId;
+    bool                                fHasAlpha;
+    int                                 fRequiredFrame;
+    SkIRect                             fRect;
+    SkCodecAnimation::DisposalMethod    fDisposalMethod;
+    int                                 fDuration;
+    SkCodecAnimation::Blend             fBlend;
+};
+
+/**
+ *  Base class for an object which holds the SkFrames of an
+ *  image sequence.
+ */
+class SkFrameHolder : public SkNoncopyable {
+public:
+    SkFrameHolder()
+        : fScreenWidth(0)
+        , fScreenHeight(0)
+    {}
+
+    virtual ~SkFrameHolder() {}
+
+    /**
+     *  Size of the image. Each frame will be contained in
+     *  these dimensions (possibly after clipping).
+     */
+    int screenWidth() const { return fScreenWidth; }
+    int screenHeight() const { return fScreenHeight; }
+
+    /**
+     *  Compute the opacity and required frame, based on
+     *  whether the frame reportsAlpha and how it blends
+     *  with prior frames.
+     */
+    void setAlphaAndRequiredFrame(SkFrame*);
+
+    /**
+     *  Return the frame with frameId i.
+     */
+    const SkFrame* getFrame(int i) const {
+        return this->onGetFrame(i);
+    }
+
+protected:
+    int fScreenWidth;
+    int fScreenHeight;
+
+    virtual const SkFrame* onGetFrame(int i) const = 0;
+};
+
+#endif // SkFrameHolder_DEFINED
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index 40339b5..3f4e551 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -117,7 +117,7 @@
 
 SkGifCodec::SkGifCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
                        SkGifImageReader* reader)
-    : INHERITED(encodedInfo, imageInfo, nullptr)
+    : INHERITED(encodedInfo, imageInfo, SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr)
     , fReader(reader)
     , fTmpBuffer(nullptr)
     , fSwizzler(nullptr)
@@ -132,17 +132,29 @@
     reader->setClient(this);
 }
 
-std::vector<SkCodec::FrameInfo> SkGifCodec::onGetFrameInfo() {
+int SkGifCodec::onGetFrameCount() {
     fReader->parse(SkGifImageReader::SkGIFFrameCountQuery);
-    const size_t size = fReader->imagesCount();
-    std::vector<FrameInfo> result(size);
-    for (size_t i = 0; i < size; i++) {
-        const SkGIFFrameContext* frameContext = fReader->frameContext(i);
-        result[i].fDuration = frameContext->delayTime();
-        result[i].fRequiredFrame = frameContext->getRequiredFrame();
-        result[i].fFullyReceived = frameContext->isComplete();
+    return fReader->imagesCount();
+}
+
+bool SkGifCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
+    if (i >= fReader->imagesCount()) {
+        return false;
     }
-    return result;
+
+    const SkGIFFrameContext* frameContext = fReader->frameContext(i);
+    if (!frameContext->reachedStartOfData()) {
+        return false;
+    }
+
+    if (frameInfo) {
+        frameInfo->fDuration = frameContext->getDuration();
+        frameInfo->fRequiredFrame = frameContext->getRequiredFrame();
+        frameInfo->fFullyReceived = frameContext->isComplete();
+        frameInfo->fAlphaType = frameContext->hasAlpha() ? kUnpremul_SkAlphaType
+                                                         : kOpaque_SkAlphaType;
+    }
+    return true;
 }
 
 int SkGifCodec::onGetRepetitionCount() {
@@ -152,7 +164,7 @@
 
 static const SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
 
-void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, size_t frameIndex) {
+void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) {
     SkColorType colorTableColorType = dstInfo.colorType();
     if (this->colorXform()) {
         colorTableColorType = kXformSrcColorType;
@@ -164,16 +176,9 @@
         // 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() && !fXformOnDecode) {
+    } else if (this->colorXform() && !this->xformOnDecode()) {
         SkPMColor dstColors[256];
-        const SkColorSpaceXform::ColorFormat dstFormat =
-                select_xform_format_ct(dstInfo.colorType());
-        const SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(kXformSrcColorType);
-        const SkAlphaType xformAlphaType = select_xform_alpha(dstInfo.alphaType(),
-                                                              this->getInfo().alphaType());
-        SkAssertResult(this->colorXform()->apply(dstFormat, dstColors, srcFormat,
-                                                 currColorTable->readColors(),
-                                                 currColorTable->count(), xformAlphaType));
+        this->applyColorXform(dstColors, currColorTable->readColors(), currColorTable->count());
         fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
     } else {
         fCurrColorTable = std::move(currColorTable);
@@ -190,20 +195,16 @@
         return gif_error("Cannot convert input type to output type.\n", kInvalidConversion);
     }
 
-    fXformOnDecode = false;
-    if (this->colorXform()) {
-        fXformOnDecode = apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color());
-        if (fXformOnDecode) {
-            fXformBuffer.reset(new uint32_t[dstInfo.width()]);
-            sk_bzero(fXformBuffer.get(), dstInfo.width() * sizeof(uint32_t));
-        }
+    if (this->xformOnDecode()) {
+        fXformBuffer.reset(new uint32_t[dstInfo.width()]);
+        sk_bzero(fXformBuffer.get(), dstInfo.width() * sizeof(uint32_t));
     }
 
     if (opts.fSubset) {
         return gif_error("Subsets not supported.\n", kUnimplemented);
     }
 
-    const size_t frameIndex = opts.fFrameIndex;
+    const int frameIndex = opts.fFrameIndex;
     if (frameIndex > 0) {
         switch (dstInfo.colorType()) {
             case kIndex_8_SkColorType:
@@ -264,14 +265,13 @@
     return kSuccess;
 }
 
-void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, size_t frameIndex) {
+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(static_cast<int>(frame->xOffset() + frame->width()),
-                              static_cast<int>(fReader->screenWidth()));
+    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.
@@ -353,7 +353,7 @@
 SkCodec::Result SkGifCodec::onIncrementalDecode(int* rowsDecoded) {
     // It is possible the client has appended more data. Parse, if needed.
     const auto& options = this->options();
-    const size_t frameIndex = options.fFrameIndex;
+    const int frameIndex = options.fFrameIndex;
     fReader->parse((SkGifImageReader::SkGIFParseQuery) frameIndex);
 
     const bool firstCallToIncrementalDecode = fFirstCallToIncrementalDecode;
@@ -363,7 +363,7 @@
 
 SkCodec::Result SkGifCodec::decodeFrame(bool firstAttempt, const Options& opts, int* rowsDecoded) {
     const SkImageInfo& dstInfo = this->dstInfo();
-    const size_t frameIndex = opts.fFrameIndex;
+    const int frameIndex = opts.fFrameIndex;
     SkASSERT(frameIndex < fReader->imagesCount());
     const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
     if (firstAttempt) {
@@ -488,8 +488,8 @@
         // compatibity on Android, so we are using the color table for the first frame.
         SkASSERT(this->options().fFrameIndex == 0);
         // Use the transparent index for the first frame.
-        const size_t transPixel = fReader->frameContext(0)->transparentPixel();
-        if (transPixel < (size_t) fCurrColorTable->count()) {
+        const int transPixel = fReader->frameContext(0)->transparentPixel();
+        if (transPixel >= 0 && transPixel < fCurrColorTable->count()) {
             return transPixel;
         }
         // Fall through to return SK_ColorTRANSPARENT (i.e. 0). This choice is arbitrary,
@@ -505,23 +505,19 @@
 }
 
 void SkGifCodec::applyXformRow(const SkImageInfo& dstInfo, void* dst, const uint8_t* src) const {
-    if (this->colorXform() && fXformOnDecode) {
+    if (this->xformOnDecode()) {
+        SkASSERT(this->colorXform());
         fSwizzler->swizzle(fXformBuffer.get(), src);
 
-        const SkColorSpaceXform::ColorFormat dstFormat = select_xform_format(dstInfo.colorType());
-        const SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(kXformSrcColorType);
-        const SkAlphaType xformAlphaType = select_xform_alpha(dstInfo.alphaType(),
-                                                              this->getInfo().alphaType());
         const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
-        SkAssertResult(this->colorXform()->apply(dstFormat, dst, srcFormat, fXformBuffer.get(),
-                                                 xformWidth, xformAlphaType));
+        this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
     } else {
         fSwizzler->swizzle(dst, src);
     }
 }
 
-bool SkGifCodec::haveDecodedRow(size_t frameIndex, const unsigned char* rowBegin,
-                                size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels)
+bool 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
@@ -530,13 +526,11 @@
     // 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 size_t width = frameContext->width();
+    const int width = frameContext->width();
     const int xBegin = frameContext->xOffset();
     const int yBegin = frameContext->yOffset() + rowNumber;
-    const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width),
-                              this->getInfo().width());
-    const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumber + repeatCount),
-                              this->getInfo().height());
+    const int xEnd = std::min(xBegin + width, this->getInfo().width());
+    const int yEnd = std::min(yBegin + rowNumber + repeatCount, this->getInfo().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))
@@ -551,7 +545,7 @@
         // Check to see whether this row or one that falls in the repeatCount is needed in the
         // output.
         bool foundNecessaryRow = false;
-        for (unsigned i = 0; i < repeatCount; i++) {
+        for (int i = 0; i < repeatCount; i++) {
             const int potentialRow = yBegin + i;
             if (fSwizzler->rowNeeded(potentialRow)) {
                 dstRow = potentialRow / sampleY;
@@ -566,7 +560,7 @@
                 repeatCount = (repeatCount - 1) / sampleY + 1;
 
                 // Make sure the repeatCount does not take us beyond the end of the dst
-                if (dstRow + (int) repeatCount > scaledHeight) {
+                if (dstRow + repeatCount > scaledHeight) {
                     repeatCount = scaledHeight - dstRow;
                     SkASSERT(repeatCount >= 1);
                 }
@@ -580,7 +574,7 @@
     } else {
         // Make sure the repeatCount does not take us beyond the end of the dst
         SkASSERT(this->dstInfo().height() >= yBegin);
-        repeatCount = SkTMin(repeatCount, (unsigned) (this->dstInfo().height() - yBegin));
+        repeatCount = SkTMin(repeatCount, this->dstInfo().height() - yBegin);
     }
 
     if (!fFilledBackground) {
@@ -653,7 +647,7 @@
         const size_t bytesToCopy = fSwizzler->swizzleWidth() * bytesPerPixel;
         void* copiedLine = SkTAddOffset<void>(dstLine, fSwizzler->swizzleOffsetBytes());
         void* dst = copiedLine;
-        for (unsigned i = 1; i < repeatCount; i++) {
+        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
index 67654d3..314b50b 100644
--- a/src/codec/SkGifCodec.h
+++ b/src/codec/SkGifCodec.h
@@ -33,8 +33,8 @@
     static SkCodec* NewFromStream(SkStream*);
 
     // Callback for SkGifImageReader when a row is available.
-    bool haveDecodedRow(size_t frameIndex, const unsigned char* rowBegin,
-                        size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels);
+    bool haveDecodedRow(int frameIndex, const unsigned char* rowBegin,
+                        int rowNumber, int repeatCount, bool writeTransparentPixels);
 protected:
     /*
      * Performs the full gif decode
@@ -50,7 +50,8 @@
 
     uint64_t onGetFillValue(const SkImageInfo&) const override;
 
-    std::vector<FrameInfo> onGetFrameInfo() override;
+    int onGetFrameCount() override;
+    bool onGetFrameInfo(int, FrameInfo*) const override;
     int onGetRepetitionCount() override;
 
     Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t,
@@ -66,7 +67,7 @@
      * @param dstInfo         Contains the requested dst color type.
      * @param frameIndex      Frame whose color table to use.
      */
-    void initializeColorTable(const SkImageInfo& dstInfo, size_t frameIndex);
+    void initializeColorTable(const SkImageInfo& dstInfo, int frameIndex);
 
    /*
     * Does necessary setup, including setting up the color table and swizzler,
@@ -84,7 +85,7 @@
      * @param frameIndex Which frame we are decoding. This determines the frameRect
      *                   to use.
      */
-    void initializeSwizzler(const SkImageInfo& dstInfo, size_t frameIndex);
+    void initializeSwizzler(const SkImageInfo& dstInfo, int frameIndex);
 
     SkSampler* getSampler(bool createIfNecessary) override {
         SkASSERT(fSwizzler);
@@ -150,7 +151,6 @@
     // the background, in which case it is set once and left alone.
     int                                 fRowsDecoded;
     std::unique_ptr<uint32_t[]>         fXformBuffer;
-    bool                                fXformOnDecode;
 
     typedef SkCodec INHERITED;
 };
diff --git a/src/codec/SkIcoCodec.cpp b/src/codec/SkIcoCodec.cpp
index d585dcf..14c6fc4 100644
--- a/src/codec/SkIcoCodec.cpp
+++ b/src/codec/SkIcoCodec.cpp
@@ -192,7 +192,10 @@
 SkIcoCodec::SkIcoCodec(int width, int height, const SkEncodedInfo& info,
                        SkTArray<std::unique_ptr<SkCodec>, true>* codecs,
                        sk_sp<SkColorSpace> colorSpace)
-    : INHERITED(width, height, info, nullptr, std::move(colorSpace))
+    // The source SkColorSpaceXform::ColorFormat will not be used. The embedded
+    // codec's will be used instead.
+    : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), nullptr,
+                std::move(colorSpace))
     , fEmbeddedCodecs(codecs)
     , fCurrScanlineCodec(nullptr)
     , fCurrIncrementalCodec(nullptr)
@@ -201,7 +204,7 @@
 /*
  * Chooses the best dimensions given the desired scale
  */
-SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { 
+SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const {
     // We set the dimensions to the largest candidate image by default.
     // Regardless of the scale request, this is the largest image that we
     // will decode.
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 75fe113..a428b64 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "SkCodec.h"
-#include "SkMSAN.h"
 #include "SkJpegCodec.h"
 #include "SkJpegDecoderMgr.h"
 #include "SkCodecPriv.h"
@@ -271,7 +270,7 @@
 SkCodec* SkJpegCodec::NewFromStream(SkStream* stream, sk_sp<SkColorSpace> defaultColorSpace) {
     std::unique_ptr<SkStream> streamDeleter(stream);
     SkCodec* codec = nullptr;
-    if (ReadHeader(stream,  &codec, nullptr, std::move(defaultColorSpace))) {
+    if (ReadHeader(stream, &codec, nullptr, std::move(defaultColorSpace))) {
         // Codec has taken ownership of the stream, we do not need to delete it
         SkASSERT(codec);
         streamDeleter.release();
@@ -282,7 +281,8 @@
 
 SkJpegCodec::SkJpegCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream,
         JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Origin origin)
-    : INHERITED(width, height, info, stream, std::move(colorSpace), origin)
+    : INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat, stream,
+                std::move(colorSpace), origin)
     , fDecoderMgr(decoderMgr)
     , fReadyState(decoderMgr->dinfo()->global_state)
     , fSwizzleSrcRow(nullptr)
@@ -521,8 +521,6 @@
 
     for (int y = 0; y < count; y++) {
         uint32_t lines = jpeg_read_scanlines(fDecoderMgr->dinfo(), &decodeDst, 1);
-        size_t srcRowBytes = get_row_bytes(fDecoderMgr->dinfo());
-        sk_msan_mark_initialized(decodeDst, decodeDst + srcRowBytes, "skbug.com/4550");
         if (0 == lines) {
             return y;
         }
@@ -532,9 +530,7 @@
         }
 
         if (this->colorXform()) {
-            SkAssertResult(this->colorXform()->apply(select_xform_format(dstInfo.colorType()), dst,
-                    SkColorSpaceXform::kRGBA_8888_ColorFormat, swizzleDst, dstWidth,
-                    kOpaque_SkAlphaType));
+            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
             dst = SkTAddOffset<void>(dst, rowBytes);
         }
 
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
index b6f418f..29057cb 100644
--- a/src/codec/SkPngCodec.cpp
+++ b/src/codec/SkPngCodec.cpp
@@ -296,15 +296,8 @@
         }
     }
 
-    if (this->colorXform() &&
-            !apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color())) {
-        const SkColorSpaceXform::ColorFormat dstFormat =
-                select_xform_format_ct(dstInfo.colorType());
-        const SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(kXformSrcColorType);
-        const SkAlphaType xformAlphaType = select_xform_alpha(dstInfo.alphaType(),
-                                                              this->getInfo().alphaType());
-        SkAssertResult(this->colorXform()->apply(dstFormat, colorTable, srcFormat, colorTable,
-                       numColors, xformAlphaType));
+    if (this->colorXform() && !this->xformOnDecode()) {
+        this->applyColorXform(colorTable, colorTable, numColors);
     }
 
     // Pad the color table with the last color in the table (or black) in the case that
@@ -476,20 +469,16 @@
 }
 
 void SkPngCodec::applyXformRow(void* dst, const void* src) {
-    const SkColorSpaceXform::ColorFormat srcColorFormat =
-            png_select_xform_format(this->getEncodedInfo());
     switch (fXformMode) {
         case kSwizzleOnly_XformMode:
             fSwizzler->swizzle(dst, (const uint8_t*) src);
             break;
         case kColorOnly_XformMode:
-            SkAssertResult(this->colorXform()->apply(fXformColorFormat, dst, srcColorFormat, src,
-                    fXformWidth, fXformAlphaType));
+            this->applyColorXform(dst, src, fXformWidth);
             break;
         case kSwizzleColor_XformMode:
             fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src);
-            SkAssertResult(this->colorXform()->apply(fXformColorFormat, dst, srcColorFormat,
-                    fColorXformSrcRow, fXformWidth, fXformAlphaType));
+            this->applyColorXform(dst, fColorXformSrcRow, fXformWidth);
             break;
     }
 }
@@ -958,7 +947,7 @@
 SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
                        SkStream* stream, SkPngChunkReader* chunkReader, void* png_ptr,
                        void* info_ptr, int bitDepth)
-    : INHERITED(encodedInfo, imageInfo, stream)
+    : INHERITED(encodedInfo, imageInfo, png_select_xform_format(encodedInfo), stream)
     , fPngChunkReader(SkSafeRef(chunkReader))
     , fPng_ptr(png_ptr)
     , fInfo_ptr(info_ptr)
@@ -986,11 +975,11 @@
 // Getting the pixels
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options,
-                                  SkPMColor ctable[], int* ctableCount) {
+SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options,
+                                             SkPMColor ctable[], int* ctableCount) {
     if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
         SkCodecPrintf("Failed on png_read_update_info.\n");
-        return false;
+        return kInvalidInput;
     }
     png_read_update_info(fPng_ptr, fInfo_ptr);
 
@@ -999,7 +988,7 @@
     fSwizzler.reset(nullptr);
 
     if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
-        return false;
+        return kInvalidConversion;
     }
 
     // If SkColorSpaceXform directly supports the encoded PNG format, we should skip format
@@ -1020,12 +1009,12 @@
     }
     if (skipFormatConversion && !options.fSubset) {
         fXformMode = kColorOnly_XformMode;
-        return true;
+        return kSuccess;
     }
 
     if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
         if (!this->createColorTable(dstInfo, ctableCount)) {
-            return false;
+            return kInvalidInput;
         }
     }
 
@@ -1033,21 +1022,15 @@
     copy_color_table(dstInfo, fColorTable.get(), ctable, ctableCount);
 
     this->initializeSwizzler(dstInfo, options, skipFormatConversion);
-    return true;
+    return kSuccess;
 }
 
 void SkPngCodec::initializeXformParams() {
     switch (fXformMode) {
         case kColorOnly_XformMode:
-            fXformColorFormat = select_xform_format(this->dstInfo().colorType());
-            fXformAlphaType = select_xform_alpha(this->dstInfo().alphaType(),
-                                                 this->getInfo().alphaType());
             fXformWidth = this->dstInfo().width();
             break;
         case kSwizzleColor_XformMode:
-            fXformColorFormat = select_xform_format(this->dstInfo().colorType());
-            fXformAlphaType = select_xform_alpha(this->dstInfo().alphaType(),
-                                                 this->getInfo().alphaType());
             fXformWidth = this->swizzler()->swizzleWidth();
             break;
         default:
@@ -1060,9 +1043,7 @@
     SkImageInfo swizzlerInfo = dstInfo;
     Options swizzlerOptions = options;
     fXformMode = kSwizzleOnly_XformMode;
-    if (this->colorXform() &&
-        apply_xform_on_decode(dstInfo.colorType(), this->getEncodedInfo().color()))
-    {
+    if (this->colorXform() && this->xformOnDecode()) {
         swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
         if (kPremul_SkAlphaType == dstInfo.alphaType()) {
             swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
@@ -1115,12 +1096,15 @@
                                         size_t rowBytes, const Options& options,
                                         SkPMColor ctable[], int* ctableCount,
                                         int* rowsDecoded) {
-    if (!conversion_possible(dstInfo, this->getInfo()) ||
-        !this->initializeXforms(dstInfo, options, ctable, ctableCount))
-    {
+    if (!conversion_possible(dstInfo, this->getInfo())) {
         return kInvalidConversion;
     }
 
+    Result result = this->initializeXforms(dstInfo, options, ctable, ctableCount);
+    if (kSuccess != result) {
+        return result;
+    }
+
     if (options.fSubset) {
         return kUnimplemented;
     }
@@ -1133,12 +1117,15 @@
 SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
         void* dst, size_t rowBytes, const SkCodec::Options& options,
         SkPMColor* ctable, int* ctableCount) {
-    if (!conversion_possible(dstInfo, this->getInfo()) ||
-        !this->initializeXforms(dstInfo, options, ctable, ctableCount))
-    {
+    if (!conversion_possible(dstInfo, this->getInfo())) {
         return kInvalidConversion;
     }
 
+    Result result = this->initializeXforms(dstInfo, options, ctable, ctableCount);
+    if (kSuccess != result) {
+        return result;
+    }
+
     this->allocateStorage(dstInfo);
 
     int firstRow, lastRow;
diff --git a/src/codec/SkPngCodec.h b/src/codec/SkPngCodec.h
index 4809723..b329fef 100644
--- a/src/codec/SkPngCodec.h
+++ b/src/codec/SkPngCodec.h
@@ -103,8 +103,8 @@
 
     bool createColorTable(const SkImageInfo& dstInfo, int* ctableCount);
     // Helper to set up swizzler, color xforms, and color table. Also calls png_read_update_info.
-    bool initializeXforms(const SkImageInfo& dstInfo, const Options&, SkPMColor* colorPtr,
-                          int* colorCount);
+    SkCodec::Result initializeXforms(const SkImageInfo& dstInfo, const Options&,
+                                     SkPMColor* colorPtr, int* colorCount);
     void initializeSwizzler(const SkImageInfo& dstInfo, const Options&, bool skipFormatConversion);
     void allocateStorage(const SkImageInfo& dstInfo);
     void destroyReadStruct();
@@ -114,8 +114,6 @@
     virtual Result decode(int* rowsDecoded) = 0;
 
     XformMode                      fXformMode;
-    SkColorSpaceXform::ColorFormat fXformColorFormat;
-    SkAlphaType                    fXformAlphaType;
     int                            fXformWidth;
 
     size_t                         fIdatLength;
diff --git a/src/codec/SkRawCodec.cpp b/src/codec/SkRawCodec.cpp
index 272f737..b8bcf19 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -697,11 +697,10 @@
         return kInvalidConversion;
     }
 
-    static const SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
     SkImageInfo swizzlerInfo = dstInfo;
     std::unique_ptr<uint32_t[]> xformBuffer = nullptr;
     if (this->colorXform()) {
-        swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
+        swizzlerInfo = swizzlerInfo.makeColorType(kRGBA_8888_SkColorType);
         xformBuffer.reset(new uint32_t[dstInfo.width()]);
     }
 
@@ -751,12 +750,7 @@
         if (this->colorXform()) {
             swizzler->swizzle(xformBuffer.get(), &srcRow[0]);
 
-            const SkColorSpaceXform::ColorFormat srcFormat =
-                    select_xform_format(kXformSrcColorType);
-            const SkColorSpaceXform::ColorFormat dstFormat =
-                    select_xform_format(dstInfo.colorType());
-            this->colorXform()->apply(dstFormat, dstRow, srcFormat, xformBuffer.get(),
-                                      dstInfo.width(), kOpaque_SkAlphaType);
+            this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width(), kOpaque_SkAlphaType);
             dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
         } else {
             swizzler->swizzle(dstRow, &srcRow[0]);
@@ -807,6 +801,7 @@
 SkRawCodec::~SkRawCodec() {}
 
 SkRawCodec::SkRawCodec(SkDngImage* dngImage)
-    : INHERITED(dngImage->width(), dngImage->height(), dngImage->getEncodedInfo(), nullptr,
+    : INHERITED(dngImage->width(), dngImage->height(), dngImage->getEncodedInfo(),
+                SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr,
                 SkColorSpace::MakeSRGB())
     , fDngImage(dngImage) {}
diff --git a/src/codec/SkWbmpCodec.cpp b/src/codec/SkWbmpCodec.cpp
index d59789b..780ae5e 100644
--- a/src/codec/SkWbmpCodec.cpp
+++ b/src/codec/SkWbmpCodec.cpp
@@ -107,7 +107,9 @@
 }
 
 SkWbmpCodec::SkWbmpCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream)
-    : INHERITED(width, height, info, stream, SkColorSpace::MakeSRGB())
+    // Wbmp does not need a colorXform, so choose an arbitrary srcFormat.
+    : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(),
+                stream, SkColorSpace::MakeSRGB())
     , fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
     , fSwizzler(nullptr)
     , fColorTable(nullptr)
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index ab0b91b..21d45da 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -5,8 +5,11 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
+#include "SkCanvas.h"
 #include "SkCodecPriv.h"
 #include "SkColorSpaceXform.h"
+#include "SkRasterPipeline.h"
 #include "SkSampler.h"
 #include "SkStreamPriv.h"
 #include "SkTemplates.h"
@@ -32,10 +35,12 @@
     return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6);
 }
 
+static SkAlphaType alpha_type(bool hasAlpha) {
+    return hasAlpha ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
+}
+
 // Parse headers of RIFF container, and check for valid Webp (VP8) content.
-// NOTE: This calls peek instead of read, since onGetPixels will need these
-// bytes again.
-// Returns an SkWebpCodec on success;
+// Returns an SkWebpCodec on success
 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
     std::unique_ptr<SkStream> streamDeleter(stream);
 
@@ -90,8 +95,6 @@
     }
 
     // Get the first frame and its "features" to determine the color and alpha types.
-    // Since we do not yet support animated webp, this is the only frame that we will
-    // decode.
     WebPIterator frame;
     SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
     if (!WebPDemuxGetFrame(demux, 1, &frame)) {
@@ -104,23 +107,32 @@
         return nullptr;
     }
 
+    const bool hasAlpha = SkToBool(frame.has_alpha)
+            || frame.width != width || frame.height != height;
     SkEncodedInfo::Color color;
     SkEncodedInfo::Alpha alpha;
     switch (features.format) {
         case 0:
             // This indicates a "mixed" format.  We could see this for
             // animated webps (multiple fragments).
-            // I believe that this is a rare case.
             // We could also guess kYUV here, but I think it makes more
             // sense to guess kBGRA which is likely closer to the final
             // output.  Otherwise, we might end up converting
             // BGRA->YUVA->BGRA.
-            color = SkEncodedInfo::kBGRA_Color;
-            alpha = SkEncodedInfo::kUnpremul_Alpha;
+            // Fallthrough:
+        case 2:
+            // This is the lossless format (BGRA).
+            if (hasAlpha) {
+                color = SkEncodedInfo::kBGRA_Color;
+                alpha = SkEncodedInfo::kUnpremul_Alpha;
+            } else {
+                color = SkEncodedInfo::kBGRX_Color;
+                alpha = SkEncodedInfo::kOpaque_Alpha;
+            }
             break;
         case 1:
             // This is the lossy format (YUV).
-            if (SkToBool(features.has_alpha) || frame.width != width || frame.height != height) {
+            if (hasAlpha) {
                 color = SkEncodedInfo::kYUVA_Color;
                 alpha = SkEncodedInfo::kUnpremul_Alpha;
             } else {
@@ -128,11 +140,6 @@
                 alpha = SkEncodedInfo::kOpaque_Alpha;
             }
             break;
-        case 2:
-            // This is the lossless format (BGRA).
-            color = SkEncodedInfo::kBGRA_Color;
-            alpha = SkEncodedInfo::kUnpremul_Alpha;
-            break;
         default:
             return nullptr;
     }
@@ -160,8 +167,9 @@
             && dim.height() >= 1 && dim.height() <= info.height();
 }
 
-static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
-    switch (ct) {
+static WEBP_CSP_MODE webp_decode_mode(const SkImageInfo& info) {
+    const bool premultiply = info.alphaType() == kPremul_SkAlphaType;
+    switch (info.colorType()) {
         case kBGRA_8888_SkColorType:
             return premultiply ? MODE_bgrA : MODE_BGRA;
         case kRGBA_8888_SkColorType:
@@ -173,6 +181,12 @@
     }
 }
 
+SkWebpCodec::Frame* SkWebpCodec::FrameHolder::appendNewFrame(bool hasAlpha) {
+    const int i = this->size();
+    fFrames.emplace_back(i, hasAlpha);
+    return &fFrames[i];
+}
+
 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
     if (!desiredSubset) {
         return false;
@@ -191,13 +205,210 @@
     return true;
 }
 
+int SkWebpCodec::onGetRepetitionCount() {
+    auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS);
+    if (!(flags & ANIMATION_FLAG)) {
+        return 0;
+    }
+
+    const int repCount = WebPDemuxGetI(fDemux.get(), WEBP_FF_LOOP_COUNT);
+    if (0 == repCount) {
+        return kRepetitionCountInfinite;
+    }
+
+    return repCount;
+}
+
+int SkWebpCodec::onGetFrameCount() {
+    auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS);
+    if (!(flags & ANIMATION_FLAG)) {
+        return 1;
+    }
+
+    const uint32_t oldFrameCount = fFrameHolder.size();
+    if (fFailed) {
+        return oldFrameCount;
+    }
+
+    const uint32_t frameCount = WebPDemuxGetI(fDemux, WEBP_FF_FRAME_COUNT);
+    if (oldFrameCount == frameCount) {
+        // We have already parsed this.
+        return frameCount;
+    }
+
+    fFrameHolder.reserve(frameCount);
+
+    for (uint32_t i = oldFrameCount; i < frameCount; i++) {
+        WebPIterator iter;
+        SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoIter(&iter);
+
+        if (!WebPDemuxGetFrame(fDemux.get(), i + 1, &iter)) {
+            fFailed = true;
+            break;
+        }
+
+        // libwebp only reports complete frames of an animated image.
+        SkASSERT(iter.complete);
+
+        Frame* frame = fFrameHolder.appendNewFrame(iter.has_alpha);
+        frame->setXYWH(iter.x_offset, iter.y_offset, iter.width, iter.height);
+        frame->setDisposalMethod(iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ?
+                SkCodecAnimation::RestoreBGColor_DisposalMethod :
+                SkCodecAnimation::Keep_DisposalMethod);
+        frame->setDuration(iter.duration);
+        if (WEBP_MUX_BLEND != iter.blend_method) {
+            frame->setBlend(SkCodecAnimation::Blend::kBG);
+        }
+        fFrameHolder.setAlphaAndRequiredFrame(frame);
+    }
+
+    return fFrameHolder.size();
+
+}
+
+const SkFrame* SkWebpCodec::FrameHolder::onGetFrame(int i) const {
+    return static_cast<const SkFrame*>(this->frame(i));
+}
+
+const SkWebpCodec::Frame* SkWebpCodec::FrameHolder::frame(int i) const {
+    SkASSERT(i >= 0 && i < this->size());
+    return &fFrames[i];
+}
+
+bool SkWebpCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
+    if (i >= fFrameHolder.size()) {
+        return false;
+    }
+
+    const Frame* frame = fFrameHolder.frame(i);
+    if (!frame) {
+        return false;
+    }
+
+    if (frameInfo) {
+        frameInfo->fRequiredFrame = frame->getRequiredFrame();
+        frameInfo->fDuration = frame->getDuration();
+        // libwebp only reports fully received frames for an
+        // animated image.
+        frameInfo->fFullyReceived = true;
+        frameInfo->fAlphaType = alpha_type(frame->hasAlpha());
+    }
+
+    return true;
+}
+
+static bool is_8888(SkColorType colorType) {
+    switch (colorType) {
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            return true;
+        default:
+            return false;
+    }
+}
+
+static void pick_memory_stages(SkColorType ct, SkRasterPipeline::StockStage* load,
+                                               SkRasterPipeline::StockStage* store) {
+    switch(ct) {
+        case kUnknown_SkColorType:
+        case kAlpha_8_SkColorType:
+        case kARGB_4444_SkColorType:
+        case kIndex_8_SkColorType:
+        case kGray_8_SkColorType:
+            SkASSERT(false);
+            break;
+        case kRGB_565_SkColorType:
+            if (load) *load = SkRasterPipeline::load_565;
+            if (store) *store = SkRasterPipeline::store_565;
+            break;
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            if (load) *load = SkRasterPipeline::load_8888;
+            if (store) *store = SkRasterPipeline::store_8888;
+            break;
+        case kRGBA_F16_SkColorType:
+            if (load) *load = SkRasterPipeline::load_f16;
+            if (store) *store = SkRasterPipeline::store_f16;
+            break;
+    }
+}
+
+static void blend_line(SkColorType dstCT, void* dst,
+                       SkColorType srcCT, void* src,
+                       bool needsSrgbToLinear, SkAlphaType at,
+                       int width) {
+    // Setup conversion from the source and dest, which will be the same.
+    SkRasterPipeline_<256> convert_to_linear_premul;
+    if (needsSrgbToLinear) {
+        convert_to_linear_premul.append_from_srgb(at);
+    }
+    if (kUnpremul_SkAlphaType == at) {
+        // srcover assumes premultiplied inputs.
+        convert_to_linear_premul.append(SkRasterPipeline::premul);
+    }
+
+    SkRasterPipeline_<256> p;
+    SkRasterPipeline::StockStage load_dst, store_dst;
+    pick_memory_stages(dstCT, &load_dst, &store_dst);
+
+    // Load the final dst.
+    p.append(load_dst, dst);
+    p.extend(convert_to_linear_premul);
+    p.append(SkRasterPipeline::move_src_dst);
+
+    // Load the src.
+    SkRasterPipeline::StockStage load_src;
+    pick_memory_stages(srcCT, &load_src, nullptr);
+    p.append(load_src, src);
+    if (dstCT != srcCT) {
+        SkASSERT(kBGRA_8888_SkColorType == srcCT);
+        p.append(SkRasterPipeline::swap_rb);
+    }
+    p.extend(convert_to_linear_premul);
+
+    p.append(SkRasterPipeline::srcover);
+
+    // Convert back to dst.
+    if (kUnpremul_SkAlphaType == at) {
+        p.append(SkRasterPipeline::unpremul);
+    }
+    if (needsSrgbToLinear) {
+        p.append(SkRasterPipeline::to_srgb);
+    }
+    p.append(store_dst, dst);
+
+    p.run(0,0, width);
+}
+
 SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
                                          const Options& options, SkPMColor*, int*,
                                          int* rowsDecodedPtr) {
-    if (!conversion_possible(dstInfo, this->getInfo()) ||
-        !this->initializeColorXform(dstInfo, options.fPremulBehavior))
+    // Ensure that we have parsed this far.
+    const int index = options.fFrameIndex;
+    if (index >= this->onGetFrameCount()) {
+        return kIncompleteInput;
+    }
+
+    const auto& srcInfo = this->getInfo();
     {
-        return kInvalidConversion;
+        auto info = srcInfo;
+        if (index > 0) {
+            auto alphaType = alpha_type(fFrameHolder.frame(index)->hasAlpha());
+            info = info.makeAlphaType(alphaType);
+        }
+        if (!conversion_possible(dstInfo, info) ||
+            !this->initializeColorXform(dstInfo, options.fPremulBehavior))
+        {
+            return kInvalidConversion;
+        }
+    }
+
+    if (index > 0 && (options.fSubset || dstInfo.dimensions() != srcInfo.dimensions())) {
+        // Subsetting and scaling are tricky when asking for frames beyond frame 0. In order to
+        // support it, we'll need to determine the proper rectangle for a
+        // WEBP_MUX_DISPOSE_BACKGROUND required frame before erasing it. (Currently the order
+        // is backwards.) Disable until it becomes clear that supporting it is important.
+        return kUnimplemented;
     }
 
     WebPDecoderConfig config;
@@ -212,16 +423,46 @@
 
     WebPIterator frame;
     SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
-    // If this succeeded in NewFromStream(), it should succeed again here.
-    SkAssertResult(WebPDemuxGetFrame(fDemux, 1, &frame));
+    // If this succeeded in onGetFrameCount(), it should succeed again here.
+    SkAssertResult(WebPDemuxGetFrame(fDemux, index + 1, &frame));
 
+    const int requiredFrame = index == 0 ? kNone : fFrameHolder.frame(index)->getRequiredFrame();
     // Get the frameRect.  libwebp will have already signaled an error if this is not fully
     // contained by the canvas.
     auto frameRect = SkIRect::MakeXYWH(frame.x_offset, frame.y_offset, frame.width, frame.height);
-    SkASSERT(this->getInfo().bounds().contains(frameRect));
-    bool frameIsSubset = frameRect.size() != this->getInfo().dimensions();
-    if (frameIsSubset) {
-        SkSampler::Fill(dstInfo, dst, rowBytes, 0, options.fZeroInitialized);
+    SkASSERT(srcInfo.bounds().contains(frameRect));
+    const bool frameIsSubset = frameRect != srcInfo.bounds();
+    if (kNone == requiredFrame) {
+        if (frameIsSubset) {
+            SkSampler::Fill(dstInfo, dst, rowBytes, 0, options.fZeroInitialized);
+        }
+    } else {
+        if (!options.fHasPriorFrame) {
+            Options prevFrameOpts(options);
+            prevFrameOpts.fFrameIndex = requiredFrame;
+            const auto result = this->getPixels(dstInfo, dst, rowBytes, &prevFrameOpts,
+                                                nullptr, nullptr);
+            switch (result) {
+                case kSuccess:
+                    break;
+                case kIncompleteInput:
+                    return kInvalidInput;
+                default:
+                    return result;
+            }
+        }
+
+        // Dispose bg color
+        const Frame* priorFrame = fFrameHolder.frame(requiredFrame);
+        if (priorFrame->getDisposalMethod() == SkCodecAnimation::RestoreBGColor_DisposalMethod) {
+            // FIXME: If we add support for scaling/subsets, this rectangle needs to be adjusted.
+            const auto priorRect = priorFrame->frameRect();
+            const auto info = dstInfo.makeWH(priorRect.width(), priorRect.height());
+            const size_t bpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
+            const size_t offset = priorRect.x() * bpp + priorRect.y() * rowBytes;
+            auto* eraseDst = SkTAddOffset<void>(dst, offset);
+            SkSampler::Fill(info, eraseDst, rowBytes, 0, kNo_ZeroInitialized);
+        }
     }
 
     int dstX = frameRect.x();
@@ -265,7 +506,7 @@
     // Ignore the frame size and offset when determining if scaling is necessary.
     int scaledWidth = subsetWidth;
     int scaledHeight = subsetHeight;
-    SkISize srcSize = options.fSubset ? options.fSubset->size() : this->getInfo().dimensions();
+    SkISize srcSize = options.fSubset ? options.fSubset->size() : srcInfo.dimensions();
     if (srcSize != dstInfo.dimensions()) {
         config.options.use_scaling = 1;
 
@@ -291,32 +532,51 @@
         config.options.scaled_height = scaledHeight;
     }
 
-    // Swizzling between RGBA and BGRA is zero cost in a color transform.  So when we have a
-    // color transform, we should decode to whatever is easiest for libwebp, and then let the
-    // color transform swizzle if necessary.
-    // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost).  Lossless webp is
-    // encoded as BGRA. This means decoding to BGRA is either faster or the same cost as RGBA.
-    config.output.colorspace = this->colorXform() ? MODE_BGRA :
-            webp_decode_mode(dstInfo.colorType(), dstInfo.alphaType() == kPremul_SkAlphaType);
+    const bool blendWithPrevFrame = requiredFrame != kNone && frame.blend_method == WEBP_MUX_BLEND
+        && frame.has_alpha;
+    if (blendWithPrevFrame && options.fPremulBehavior == SkTransferFunctionBehavior::kRespect) {
+        // Blending is done with SkRasterPipeline, which requires a color space that is valid for
+        // rendering.
+        const auto* cs = dstInfo.colorSpace();
+        if (!cs || (!cs->gammaCloseToSRGB() && !cs->gammaIsLinear())) {
+            return kInvalidConversion;
+        }
+    }
+
+    SkBitmap webpDst;
+    auto webpInfo = dstInfo;
+    if (!frame.has_alpha) {
+        webpInfo = webpInfo.makeAlphaType(kOpaque_SkAlphaType);
+    }
+    if (this->colorXform()) {
+        // Swizzling between RGBA and BGRA is zero cost in a color transform.  So when we have a
+        // color transform, we should decode to whatever is easiest for libwebp, and then let the
+        // color transform swizzle if necessary.
+        // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost).  Lossless webp is
+        // encoded as BGRA. This means decoding to BGRA is either faster or the same cost as RGBA.
+        webpInfo = webpInfo.makeColorType(kBGRA_8888_SkColorType);
+
+        if (webpInfo.alphaType() == kPremul_SkAlphaType) {
+            webpInfo = webpInfo.makeAlphaType(kUnpremul_SkAlphaType);
+        }
+    }
+
+    if ((this->colorXform() && !is_8888(dstInfo.colorType())) || blendWithPrevFrame) {
+        // We will decode the entire image and then perform the color transform.  libwebp
+        // does not provide a row-by-row API.  This is a shame particularly when we do not want
+        // 8888, since we will need to create another image sized buffer.
+        webpDst.allocPixels(webpInfo);
+    } else {
+        // libwebp can decode directly into the output memory.
+        webpDst.installPixels(webpInfo, dst, rowBytes);
+    }
+
+    config.output.colorspace = webp_decode_mode(webpInfo);
     config.output.is_external_memory = 1;
 
-    // We will decode the entire image and then perform the color transform.  libwebp
-    // does not provide a row-by-row API.  This is a shame particularly when we do not want
-    // 8888, since we will need to create another image sized buffer.
-    SkAutoTMalloc<uint32_t> pixels;
-    bool needsCopy = this->colorXform() && kRGBA_8888_SkColorType != dstInfo.colorType() &&
-                                           kBGRA_8888_SkColorType != dstInfo.colorType();
-    void* webpDst = needsCopy ? pixels.reset(dstInfo.width() * dstInfo.height()) : dst;
-    size_t webpRowBytes = needsCopy ? dstInfo.width() * sizeof(uint32_t) : rowBytes;
-    size_t totalBytes = needsCopy ? webpRowBytes * dstInfo.height()
-                                  : dstInfo.getSafeSize(webpRowBytes);
-    size_t dstBpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
-    size_t webpBpp = needsCopy ? sizeof(uint32_t) : dstBpp;
-
-    size_t offset = dstX * webpBpp + dstY * webpRowBytes;
-    config.output.u.RGBA.rgba = SkTAddOffset<uint8_t>(webpDst, offset);
-    config.output.u.RGBA.stride = (int) webpRowBytes;
-    config.output.u.RGBA.size = totalBytes - offset;
+    config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY));
+    config.output.u.RGBA.stride = static_cast<int>(webpDst.rowBytes());
+    config.output.u.RGBA.size = webpDst.getSafeSize();
 
     SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &config));
     if (!idec) {
@@ -339,21 +599,50 @@
             return kInvalidInput;
     }
 
-    if (this->colorXform()) {
-        SkColorSpaceXform::ColorFormat dstColorFormat = select_xform_format(dstInfo.colorType());
-        SkAlphaType xformAlphaType = select_xform_alpha(dstInfo.alphaType(),
-                                                        this->getInfo().alphaType());
+    // We're only transforming the new part of the frame, so no need to worry about the
+    // final composited alpha.
+    const auto srcAlpha = 0 == index ? srcInfo.alphaType() : alpha_type(frame.has_alpha);
+    const auto xformAlphaType = select_xform_alpha(dstInfo.alphaType(), srcAlpha);
+    const bool needsSrgbToLinear = dstInfo.gammaCloseToSRGB() &&
+            options.fPremulBehavior == SkTransferFunctionBehavior::kRespect;
 
+    const size_t dstBpp = SkColorTypeBytesPerPixel(dstInfo.colorType());
+    dst = SkTAddOffset<void>(dst, dstBpp * dstX + rowBytes * dstY);
+    const size_t srcRowBytes = config.output.u.RGBA.stride;
+
+    const auto dstCT = dstInfo.colorType();
+    if (this->colorXform()) {
         uint32_t* xformSrc = (uint32_t*) config.output.u.RGBA.rgba;
-        void* xformDst = SkTAddOffset<void>(dst, dstBpp * dstX + rowBytes * dstY);
-        size_t srcRowBytes = config.output.u.RGBA.stride;
+        SkBitmap tmp;
+        void* xformDst;
+
+        if (blendWithPrevFrame) {
+            // Xform into temporary bitmap big enough for one row.
+            tmp.allocPixels(dstInfo.makeWH(scaledWidth, 1));
+            xformDst = tmp.getPixels();
+        } else {
+            xformDst = dst;
+        }
         for (int y = 0; y < rowsDecoded; y++) {
-            SkAssertResult(this->colorXform()->apply(dstColorFormat, xformDst,
-                    SkColorSpaceXform::kBGRA_8888_ColorFormat, xformSrc, scaledWidth,
-                    xformAlphaType));
-            xformDst = SkTAddOffset<void>(xformDst, rowBytes);
+            this->applyColorXform(xformDst, xformSrc, scaledWidth, xformAlphaType);
+            if (blendWithPrevFrame) {
+                blend_line(dstCT, &dst, dstCT, &xformDst, needsSrgbToLinear, xformAlphaType,
+                        scaledWidth);
+                dst = SkTAddOffset<void>(dst, rowBytes);
+            } else {
+                xformDst = SkTAddOffset<void>(xformDst, rowBytes);
+            }
             xformSrc = SkTAddOffset<uint32_t>(xformSrc, srcRowBytes);
         }
+    } else if (blendWithPrevFrame) {
+        const uint8_t* src = config.output.u.RGBA.rgba;
+
+        for (int y = 0; y < rowsDecoded; y++) {
+            blend_line(dstCT, &dst, webpDst.colorType(), &src, needsSrgbToLinear,
+                    xformAlphaType, scaledWidth);
+            src = SkTAddOffset<const uint8_t>(src, srcRowBytes);
+            dst = SkTAddOffset<void>(dst, rowBytes);
+        }
     }
 
     return result;
@@ -362,7 +651,11 @@
 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
                          sk_sp<SkColorSpace> colorSpace, SkStream* stream, WebPDemuxer* demux,
                          sk_sp<SkData> data)
-    : INHERITED(width, height, info, stream, std::move(colorSpace))
+    : INHERITED(width, height, info, SkColorSpaceXform::kBGRA_8888_ColorFormat, stream,
+                std::move(colorSpace))
     , fDemux(demux)
     , fData(std::move(data))
-{}
+    , fFailed(false)
+{
+    fFrameHolder.setScreenSize(width, height);
+}
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index 93b60f6..f4c7910 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -11,9 +11,12 @@
 #include "SkCodec.h"
 #include "SkColorSpace.h"
 #include "SkEncodedImageFormat.h"
+#include "SkFrameHolder.h"
 #include "SkImageInfo.h"
 #include "SkTypes.h"
 
+#include <vector>
+
 class SkStream;
 extern "C" {
     struct WebPDemuxer;
@@ -37,6 +40,11 @@
     bool onDimensionsSupported(const SkISize&) override;
 
     bool onGetValidSubset(SkIRect* /* desiredSubset */) const override;
+
+    int onGetFrameCount() override;
+    bool onGetFrameInfo(int, FrameInfo*) const override;
+    int onGetRepetitionCount() override;
+
 private:
     SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp<SkColorSpace>, SkStream*,
                 WebPDemuxer*, sk_sp<SkData>);
@@ -47,6 +55,57 @@
     // This should not be freed until the decode is completed.
     sk_sp<SkData> fData;
 
+    class Frame : public SkFrame {
+    public:
+        Frame(int i, bool alpha)
+            : INHERITED(i)
+            , fReportsAlpha(alpha)
+        {}
+        Frame(Frame&& other)
+            : INHERITED(other.frameId())
+            , fReportsAlpha(other.fReportsAlpha)
+        {}
+
+    protected:
+        bool onReportsAlpha() const override {
+            return fReportsAlpha;
+        }
+
+    private:
+        const bool fReportsAlpha;
+
+        typedef SkFrame INHERITED;
+    };
+
+    class FrameHolder : public SkFrameHolder {
+    public:
+        ~FrameHolder() override {}
+        void setScreenSize(int w, int h) {
+            fScreenWidth = w;
+            fScreenHeight = h;
+        }
+        Frame* appendNewFrame(bool hasAlpha);
+        const Frame* frame(int i) const;
+        int size() const {
+            return static_cast<int>(fFrames.size());
+        }
+        void reserve(int size) {
+            fFrames.reserve(size);
+        }
+
+    protected:
+        const SkFrame* onGetFrame(int i) const override;
+
+    private:
+        std::vector<Frame> fFrames;
+    };
+
+    FrameHolder fFrameHolder;
+    // Set to true if WebPDemuxGetFrame fails. This only means
+    // that we will cap the frame count to the frames that
+    // succeed.
+    bool        fFailed;
+
     typedef SkCodec INHERITED;
 };
 #endif // SkWebpCodec_DEFINED
diff --git a/src/core/SkAdvancedTypefaceMetrics.h b/src/core/SkAdvancedTypefaceMetrics.h
index 61f1f1e..db8e697 100644
--- a/src/core/SkAdvancedTypefaceMetrics.h
+++ b/src/core/SkAdvancedTypefaceMetrics.h
@@ -18,45 +18,16 @@
 
     The SkAdvancedTypefaceMetrics class is used by the PDF backend to correctly
     embed typefaces. This class is created and filled in with information by
-    SkTypeface::getAdvancedTypefaceMetrics.
+    SkTypeface::getAdvancedMetrics.
 */
-class SkAdvancedTypefaceMetrics : public SkRefCnt {
-public:
-
-    SkAdvancedTypefaceMetrics()
-        : fType(SkAdvancedTypefaceMetrics::kOther_Font)
-        , fFlags((FontFlags)0)
-        , fStyle((StyleFlags)0)
-        , fItalicAngle(0)
-        , fAscent(0)
-        , fDescent(0)
-        , fStemV(0)
-        , fCapHeight(0)
-        , fBBox(SkIRect::MakeEmpty()) {}
-
+struct SkAdvancedTypefaceMetrics {
+    SkAdvancedTypefaceMetrics() {}
+    SkAdvancedTypefaceMetrics(const SkAdvancedTypefaceMetrics&) = delete;
+    SkAdvancedTypefaceMetrics& operator=(const SkAdvancedTypefaceMetrics&) = delete;
     ~SkAdvancedTypefaceMetrics() {}
 
     SkString fFontName;
 
-    enum FontType : uint8_t {
-        kType1_Font,
-        kType1CID_Font,
-        kCFF_Font,
-        kTrueType_Font,
-        kOther_Font,
-    };
-    // The type of the underlying font program.  This field determines which
-    // of the following fields are valid.  If it is kOther_Font the per glyph
-    // information will never be populated.
-    FontType fType;
-
-    enum FontFlags : uint8_t {
-        kMultiMaster_FontFlag    = 0x01,  //!<May be true for Type1, CFF, or TrueType fonts.
-        kNotEmbeddable_FontFlag  = 0x02,  //!<May not be embedded.
-        kNotSubsettable_FontFlag = 0x04,  //!<May not be subset.
-    };
-    FontFlags fFlags;  // Global font flags.
-
     // These enum values match the values used in the PDF file format.
     enum StyleFlags : uint32_t {
         kFixedPitch_Style  = 0x00000001,
@@ -67,27 +38,42 @@
         kSmallCaps_Style   = 0x00020000,
         kForceBold_Style   = 0x00040000
     };
-    StyleFlags fStyle;        // Font style characteristics.
+    StyleFlags fStyle = (StyleFlags)0;        // Font style characteristics.
 
-    int16_t fItalicAngle;   // Counterclockwise degrees from vertical of the
-                            // dominant vertical stroke for an Italic face.
+    enum FontType : uint8_t {
+        kType1_Font,
+        kType1CID_Font,
+        kCFF_Font,
+        kTrueType_Font,
+        kOther_Font,
+    };
+    // The type of the underlying font program.  This field determines which
+    // of the following fields are valid.  If it is kOther_Font the per glyph
+    // information will never be populated.
+    FontType fType = kOther_Font;
+
+    enum FontFlags : uint8_t {
+        kMultiMaster_FontFlag    = 0x01,  //!<May be true for Type1, CFF, or TrueType fonts.
+        kNotEmbeddable_FontFlag  = 0x02,  //!<May not be embedded.
+        kNotSubsettable_FontFlag = 0x04,  //!<May not be subset.
+    };
+    FontFlags fFlags = (FontFlags)0;  // Global font flags.
+
+    int16_t fItalicAngle = 0;  // Counterclockwise degrees from vertical of the
+                               // dominant vertical stroke for an Italic face.
     // The following fields are all in font units.
-    int16_t fAscent;       // Max height above baseline, not including accents.
-    int16_t fDescent;      // Max depth below baseline (negative).
-    int16_t fStemV;        // Thickness of dominant vertical stem.
-    int16_t fCapHeight;    // Height (from baseline) of top of flat capitals.
+    int16_t fAscent = 0;       // Max height above baseline, not including accents.
+    int16_t fDescent = 0;      // Max depth below baseline (negative).
+    int16_t fStemV = 0;        // Thickness of dominant vertical stem.
+    int16_t fCapHeight = 0;    // Height (from baseline) of top of flat capitals.
 
-    SkIRect fBBox;  // The bounding box of all glyphs (in font units).
+    SkIRect fBBox = {0, 0, 0, 0};  // The bounding box of all glyphs (in font units).
 
     // The names of each glyph, only populated for postscript fonts.
     SkTArray<SkString> fGlyphNames;
 
-    // The mapping from glyph to Unicode, only populated if
-    // kToUnicode_PerGlyphInfo is passed to GetAdvancedTypefaceMetrics.
+    // The mapping from glyph to Unicode; array indices are glyph ids.
     SkTDArray<SkUnichar> fGlyphToUnicode;
-
-private:
-    typedef SkRefCnt INHERITED;
 };
 
 namespace skstd {
diff --git a/src/core/SkAnalyticEdge.h b/src/core/SkAnalyticEdge.h
index 65fb11c..6c75d15 100644
--- a/src/core/SkAnalyticEdge.h
+++ b/src/core/SkAnalyticEdge.h
@@ -131,6 +131,9 @@
 };
 
 bool SkAnalyticEdge::setLine(const SkPoint& p0, const SkPoint& p1) {
+#if defined(__arm__)
+    asm volatile("dsb");  // crbug.com/710131
+#endif
     fRiteE = nullptr;
 
     // We must set X/Y using the same way (e.g., times 4, to FDot6, then to Fixed) as Quads/Cubics.
diff --git a/src/core/SkArenaAlloc.cpp b/src/core/SkArenaAlloc.cpp
index eca3aa9..5d02d85 100644
--- a/src/core/SkArenaAlloc.cpp
+++ b/src/core/SkArenaAlloc.cpp
@@ -11,6 +11,64 @@
 
 static char* end_chain(char*) { return nullptr; }
 
+SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize, Tracking tracking)
+    : fDtorCursor {block}
+    , fCursor     {block}
+    , fEnd        {block + SkTo<uint32_t>(size)}
+    , fFirstBlock {block}
+    , fFirstSize  {SkTo<uint32_t>(size)}
+    , fExtraSize  {SkTo<uint32_t>(extraSize)}
+{
+    if (size < sizeof(Footer)) {
+        fEnd = fCursor = fDtorCursor = nullptr;
+    }
+
+    if (tracking == kTrack) {
+        fTotalSlop = 0;
+    }
+
+    if (fCursor != nullptr) {
+        this->installFooter(end_chain, 0);
+        if (fTotalSlop >= 0) {
+            fTotalAlloc += fFirstSize;
+        }
+    }
+}
+
+SkArenaAlloc::~SkArenaAlloc() {
+    if (fTotalSlop >= 0) {
+        int32_t lastSlop = fEnd - fCursor;
+        fTotalSlop += lastSlop;
+        SkDebugf("SkArenaAlloc initial: %p %u %u total alloc: %u total slop: %d last slop: %d\n",
+            fFirstBlock, fFirstSize, fExtraSize, fTotalAlloc, fTotalSlop, lastSlop);
+    }
+    RunDtorsOnBlock(fDtorCursor);
+}
+
+void SkArenaAlloc::reset() {
+    this->~SkArenaAlloc();
+    new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize,
+                            fTotalSlop < 0 ? kDontTrack : kTrack};
+}
+
+void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
+    SkASSERT(padding < 64);
+    int64_t actionInt = (int64_t)(intptr_t)action;
+
+    // The top 14 bits should be either all 0s or all 1s. Check this.
+    SkASSERT((actionInt << 6) >> 6 == actionInt);
+    Footer encodedFooter = (actionInt << 6) | padding;
+    memmove(fCursor, &encodedFooter, sizeof(Footer));
+    fCursor += sizeof(Footer);
+    fDtorCursor = fCursor;
+}
+
+void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
+    memmove(fCursor, &ptr, sizeof(char*));
+    fCursor += sizeof(char*);
+    this->installFooter(action, padding);
+}
+
 char* SkArenaAlloc::SkipPod(char* footerEnd) {
     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
     int32_t skip;
@@ -39,50 +97,6 @@
     return nullptr;
 }
 
-SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
-    : fDtorCursor {block}
-    , fCursor     {block}
-    , fEnd        {block + SkTo<uint32_t>(size)}
-    , fFirstBlock {block}
-    , fFirstSize  {SkTo<uint32_t>(size)}
-    , fExtraSize  {SkTo<uint32_t>(extraSize)}
-{
-    if (size < sizeof(Footer)) {
-        fEnd = fCursor = fDtorCursor = nullptr;
-    }
-
-    if (fCursor != nullptr) {
-        this->installFooter(end_chain, 0);
-    }
-}
-
-SkArenaAlloc::~SkArenaAlloc() {
-    RunDtorsOnBlock(fDtorCursor);
-}
-
-void SkArenaAlloc::reset() {
-    this->~SkArenaAlloc();
-    new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize};
-}
-
-void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
-    SkASSERT(padding < 64);
-    int64_t actionInt = (int64_t)(intptr_t)action;
-
-    // The top 14 bits should be either all 0s or all 1s. Check this.
-    SkASSERT((actionInt << 6) >> 6 == actionInt);
-    Footer encodedFooter = (actionInt << 6) | padding;
-    memmove(fCursor, &encodedFooter, sizeof(Footer));
-    fCursor += sizeof(Footer);
-    fDtorCursor = fCursor;
-}
-
-void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
-    memmove(fCursor, &ptr, sizeof(char*));
-    fCursor += sizeof(char*);
-    this->installFooter(action, padding);
-}
-
 void SkArenaAlloc::installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding) {
     memmove(fCursor, &value, sizeof(uint32_t));
     fCursor += sizeof(uint32_t);
@@ -113,6 +127,11 @@
 
     char* newBlock = new char[allocationSize];
 
+    if (fTotalSlop >= 0) {
+        fTotalAlloc += allocationSize;
+        fTotalSlop += fEnd - fCursor;
+    }
+
     auto previousDtor = fDtorCursor;
     fCursor = newBlock;
     fDtorCursor = newBlock;
@@ -120,16 +139,6 @@
     this->installPtrFooter(NextBlock, previousDtor, 0);
 }
 
-char* SkArenaAlloc::allocObject(uint32_t size, uint32_t alignment) {
-    uintptr_t mask = alignment - 1;
-    char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
-    if ((ptrdiff_t)size > fEnd - objStart) {
-        this->ensureSpace(size, alignment);
-        objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
-    }
-    return objStart;
-}
-
 char* SkArenaAlloc::allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment) {
     uintptr_t mask = alignment - 1;
 
diff --git a/src/core/SkArenaAlloc.h b/src/core/SkArenaAlloc.h
index 494696c..f102cf6 100644
--- a/src/core/SkArenaAlloc.h
+++ b/src/core/SkArenaAlloc.h
@@ -53,21 +53,25 @@
 // For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an
 // addition overhead when switching from POD data to non-POD data of typically 8 bytes.
 //
+// You can track memory use by adding SkArenaAlloc::kTrack as the last parameter to any constructor.
+//
+//   char storage[someNumber];
+//   SkArenaAlloc alloc{storage, SkArenaAlloc::kTrack};
+//
+// This will print out a line for every destructor or reset call that has the total memory
+// allocated, the total slop (the unused portion of a block), and the slop of the last block.
+//
 // If additional blocks are needed they are increased exponentially. This strategy bounds the
 // recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using
 // the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48
 // there are 71 allocations.
 class SkArenaAlloc {
 public:
-    SkArenaAlloc(char* block, size_t size, size_t extraSize = 0);
+    enum Tracking {kDontTrack, kTrack};
+    SkArenaAlloc(char* block, size_t size, size_t, Tracking tracking = kDontTrack);
 
-    template <size_t kSize>
-    SkArenaAlloc(char (&block)[kSize], size_t extraSize = kSize)
-        : SkArenaAlloc(block, kSize, extraSize)
-    {}
-
-    SkArenaAlloc(size_t extraSize)
-        : SkArenaAlloc(nullptr, 0, extraSize)
+    SkArenaAlloc(size_t extraSize, Tracking tracking = kDontTrack)
+        : SkArenaAlloc(nullptr, 0, extraSize, tracking)
     {}
 
     ~SkArenaAlloc();
@@ -150,7 +154,15 @@
 
     void ensureSpace(uint32_t size, uint32_t alignment);
 
-    char* allocObject(uint32_t size, uint32_t alignment);
+    char* allocObject(uint32_t size, uint32_t alignment) {
+        uintptr_t mask = alignment - 1;
+        char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
+        if ((ptrdiff_t)size > fEnd - objStart) {
+            this->ensureSpace(size, alignment);
+            objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
+        }
+        return objStart;
+    }
 
     char* allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment);
 
@@ -197,9 +209,28 @@
     char* const    fFirstBlock;
     const uint32_t fFirstSize;
     const uint32_t fExtraSize;
+
+    // Track some useful stats. Track stats if fTotalSlop is >= 0;
+    uint32_t       fTotalAlloc { 0};
+    int32_t        fTotalSlop  {-1};
+
     // Use the Fibonacci sequence as the growth factor for block size. The size of the block
     // allocated is fFib0 * fExtraSize. Using 2 ^ n * fExtraSize had too much slop for Android.
     uint32_t       fFib0 {1}, fFib1 {1};
 };
 
+// Helper for defining allocators with inline/reserved storage.
+// For argument declarations, stick to the base type (SkArenaAlloc).
+template <size_t InlineStorageSize>
+class SkSTArenaAlloc : public SkArenaAlloc {
+public:
+    explicit SkSTArenaAlloc(size_t extraSize = InlineStorageSize, Tracking tracking = kDontTrack)
+        : INHERITED(fInlineStorage, InlineStorageSize, extraSize, tracking) {}
+
+private:
+    char fInlineStorage[InlineStorageSize];
+
+    using INHERITED = SkArenaAlloc;
+};
+
 #endif//SkFixedAlloc_DEFINED
diff --git a/src/core/SkAutoBlitterChoose.h b/src/core/SkAutoBlitterChoose.h
new file mode 100644
index 0000000..3698598
--- /dev/null
+++ b/src/core/SkAutoBlitterChoose.h
@@ -0,0 +1,45 @@
+/*
+ * 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 SkAutoBlitterChoose_DEFINED
+#define SkAutoBlitterChoose_DEFINED
+
+#include "SkArenaAlloc.h"
+#include "SkBlitter.h"
+
+class SkMatrix;
+class SkPaint;
+class SkPixmap;
+
+class SkAutoBlitterChoose : SkNoncopyable {
+public:
+    SkAutoBlitterChoose() {
+        fBlitter = nullptr;
+    }
+    SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix,
+                        const SkPaint& paint, bool drawCoverage = false) {
+        fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
+    }
+
+    SkBlitter*  operator->() { return fBlitter; }
+    SkBlitter*  get() const { return fBlitter; }
+
+    void choose(const SkPixmap& dst, const SkMatrix& matrix,
+                const SkPaint& paint, bool drawCoverage = false) {
+        SkASSERT(!fBlitter);
+        fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
+    }
+
+private:
+    // Owned by fAlloc, which will handle the delete.
+    SkBlitter*          fBlitter;
+
+    SkSTArenaAlloc<kSkBlitterContextSize> fAlloc;
+};
+#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
+
+#endif
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 7134bb4..e88fa5e 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -33,19 +33,14 @@
 }
 
 SkBitmap::SkBitmap()
-    : fPixelLockCount(0)
-    , fPixels        (nullptr)
-    , fColorTable    (nullptr)
+    : fPixels        (nullptr)
     , fPixelRefOrigin{0, 0}
     , fRowBytes      (0)
     , fFlags         (0) {}
 
-// copy pixelref, but don't copy lock.
 SkBitmap::SkBitmap(const SkBitmap& src)
     : fPixelRef      (src.fPixelRef)
-    , fPixelLockCount(0)
-    , fPixels        (nullptr)
-    , fColorTable    (nullptr)
+    , fPixels        (src.fPixels)
     , fPixelRefOrigin(src.fPixelRefOrigin)
     , fInfo          (src.fInfo)
     , fRowBytes      (src.fRowBytes)
@@ -55,38 +50,28 @@
     SkDEBUGCODE(this->validate();)
 }
 
-// take lock and lockcount from other.
 SkBitmap::SkBitmap(SkBitmap&& other)
     : fPixelRef      (std::move(other.fPixelRef))
-    , fPixelLockCount          (other.fPixelLockCount)
     , fPixels                  (other.fPixels)
-    , fColorTable              (other.fColorTable)
     , fPixelRefOrigin          (other.fPixelRefOrigin)
     , fInfo          (std::move(other.fInfo))
     , fRowBytes                (other.fRowBytes)
-    , fFlags                   (other.fFlags) {
+    , fFlags                   (other.fFlags)
+{
     SkASSERT(!other.fPixelRef);
     other.fInfo.reset();
-    other.fPixelLockCount = 0;
     other.fPixels         = nullptr;
-    other.fColorTable     = nullptr;
     other.fPixelRefOrigin = SkIPoint{0, 0};
     other.fRowBytes       = 0;
     other.fFlags          = 0;
 }
 
-SkBitmap::~SkBitmap() {
-    SkDEBUGCODE(this->validate();)
-    this->freePixels();
-}
+SkBitmap::~SkBitmap() {}
 
 SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
     if (this != &src) {
-        this->freePixels();
-        SkASSERT(!fPixels);
-        SkASSERT(!fColorTable);
-        SkASSERT(!fPixelLockCount);
         fPixelRef       = src.fPixelRef;
+        fPixels         = src.fPixels;
         fPixelRefOrigin = src.fPixelRefOrigin;
         fInfo           = src.fInfo;
         fRowBytes       = src.fRowBytes;
@@ -98,23 +83,15 @@
 
 SkBitmap& SkBitmap::operator=(SkBitmap&& other) {
     if (this != &other) {
-        this->freePixels();
-        SkASSERT(!fPixels);
-        SkASSERT(!fColorTable);
-        SkASSERT(!fPixelLockCount);
         fPixelRef       = std::move(other.fPixelRef);
         fInfo           = std::move(other.fInfo);
-        fPixelLockCount = other.fPixelLockCount;
         fPixels         = other.fPixels;
-        fColorTable     = other.fColorTable;
         fPixelRefOrigin = other.fPixelRefOrigin;
         fRowBytes       = other.fRowBytes;
         fFlags          = other.fFlags;
         SkASSERT(!other.fPixelRef);
         other.fInfo.reset();
-        other.fPixelLockCount = 0;
         other.fPixels         = nullptr;
-        other.fColorTable     = nullptr;
         other.fPixelRefOrigin = SkIPoint{0, 0};
         other.fRowBytes       = 0;
         other.fFlags          = 0;
@@ -144,6 +121,10 @@
     bounds->set(0, 0, fInfo.width(), fInfo.height());
 }
 
+SkColorTable* SkBitmap::getColorTable() const {
+    return fPixelRef ? fPixelRef->colorTable() : nullptr;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkBitmap::setInfo(const SkImageInfo& info, size_t rowBytes) {
@@ -178,6 +159,7 @@
 
     fInfo = info.makeAlphaType(newAT);
     fRowBytes = SkToU32(rowBytes);
+    SkDEBUGCODE(this->validate();)
     return true;
 }
 
@@ -187,108 +169,49 @@
     }
     if (fInfo.alphaType() != newAlphaType) {
         fInfo = fInfo.makeAlphaType(newAlphaType);
-        if (fPixelRef) {
-            fPixelRef->changeAlphaType(newAlphaType);
-        }
     }
+    SkDEBUGCODE(this->validate();)
     return true;
 }
 
-void SkBitmap::updatePixelsFromRef() const {
+void SkBitmap::updatePixelsFromRef() {
+    void* p = nullptr;
     if (fPixelRef) {
-        if (fPixelLockCount > 0) {
-            SkASSERT(fPixelRef->isLocked());
-
-            void* p = fPixelRef->pixels();
-            if (p) {
-                p = (char*)p
-                    + fPixelRefOrigin.fY * fRowBytes
-                    + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
-            }
-            fPixels = p;
-            fColorTable = fPixelRef->colorTable();
-        } else {
-            SkASSERT(0 == fPixelLockCount);
-            fPixels = nullptr;
-            fColorTable = nullptr;
+        // wish we could assert that a pixelref *always* has pixels
+        p = fPixelRef->pixels();
+        if (p) {
+            SkASSERT(fRowBytes == fPixelRef->rowBytes());
+            p = (char*)p
+                + fPixelRefOrigin.fY * fRowBytes
+                + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
         }
     }
+    fPixels = p;
 }
 
-#ifdef SK_SUPPORT_LEGACY_BITMAP_SETPIXELREF
-SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
-    this->setPixelRef(sk_ref_sp(pr), dx, dy);
-    return pr;
-}
-#endif
-
 void SkBitmap::setPixelRef(sk_sp<SkPixelRef> pr, int dx, int dy) {
 #ifdef SK_DEBUG
     if (pr) {
         if (kUnknown_SkColorType != fInfo.colorType()) {
-            const SkImageInfo& prInfo = pr->info();
-            SkASSERT(fInfo.width() <= prInfo.width());
-            SkASSERT(fInfo.height() <= prInfo.height());
-            SkASSERT(fInfo.colorType() == prInfo.colorType());
-            switch (prInfo.alphaType()) {
-                case kUnknown_SkAlphaType:
-                    SkASSERT(fInfo.alphaType() == kUnknown_SkAlphaType);
-                    break;
-                case kOpaque_SkAlphaType:
-                case kPremul_SkAlphaType:
-                    SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
-                             fInfo.alphaType() == kPremul_SkAlphaType);
-                    break;
-                case kUnpremul_SkAlphaType:
-                    SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
-                             fInfo.alphaType() == kUnpremul_SkAlphaType);
-                    break;
-            }
+            SkASSERT(fInfo.width() + dx <= pr->width());
+            SkASSERT(fInfo.height() + dy <= pr->height());
         }
     }
 #endif
 
-    if (pr) {
-        const SkImageInfo& info = pr->info();
-        fPixelRefOrigin.set(SkTPin(dx, 0, info.width()), SkTPin(dy, 0, info.height()));
+    fPixelRef = std::move(pr);
+    if (fPixelRef) {
+        fPixelRefOrigin.set(SkTPin(dx, 0, fPixelRef->width()), SkTPin(dy, 0, fPixelRef->height()));
+        this->updatePixelsFromRef();
     } else {
         // ignore dx,dy if there is no pixelref
         fPixelRefOrigin.setZero();
-    }
-
-    if (fPixelRef != pr) {
-        this->freePixels();
-        SkASSERT(!fPixelRef);
-
-        fPixelRef = std::move(pr);
-        this->updatePixelsFromRef();
+        fPixels = nullptr;
     }
 
     SkDEBUGCODE(this->validate();)
 }
 
-void SkBitmap::lockPixels() const {
-    if (fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
-        fPixelRef->lockPixels();
-        this->updatePixelsFromRef();
-    }
-    SkDEBUGCODE(this->validate();)
-}
-
-void SkBitmap::unlockPixels() const {
-    SkASSERT(!fPixelRef || fPixelLockCount > 0);
-
-    if (fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
-        fPixelRef->unlockPixels();
-        this->updatePixelsFromRef();
-    }
-    SkDEBUGCODE(this->validate();)
-}
-
-bool SkBitmap::lockPixelsAreWritable() const {
-    return fPixelRef ? fPixelRef->lockPixelsAreWritable() : false;
-}
-
 void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
     if (nullptr == p) {
         this->setPixelRef(nullptr, 0, 0);
@@ -300,13 +223,10 @@
         return;
     }
 
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewDirect(fInfo, p, fRowBytes, ctable));
-    this->setPixelRef(std::move(pr), 0, 0);
+    this->setPixelRef(SkMallocPixelRef::MakeDirect(fInfo, p, fRowBytes, sk_ref_sp(ctable)), 0, 0);
     if (!fPixelRef) {
         return;
     }
-    // since we're already allocated, we lockPixels right away
-    this->lockPixels();
     SkDEBUGCODE(this->validate();)
 }
 
@@ -334,24 +254,20 @@
     // setInfo may have computed a valid rowbytes if 0 were passed in
     rowBytes = this->rowBytes();
 
-    SkMallocPixelRef::PRFactory defaultFactory;
-
-    sk_sp<SkPixelRef> pr(defaultFactory.create(correctedInfo, rowBytes, nullptr));
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(correctedInfo, rowBytes, nullptr);
     if (!pr) {
         return reset_return_false(this);
     }
     this->setPixelRef(std::move(pr), 0, 0);
-
-    // TODO: lockPixels could/should return bool or void*/nullptr
-    this->lockPixels();
     if (nullptr == this->getPixels()) {
         return reset_return_false(this);
     }
+    SkDEBUGCODE(this->validate();)
     return true;
 }
 
-bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, SkPixelRefFactory* factory,
-                                SkColorTable* ctable) {
+bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, sk_sp<SkColorTable> ctable,
+                              uint32_t allocFlags) {
     if (kIndex_8_SkColorType == requestedInfo.colorType() && nullptr == ctable) {
         return reset_return_false(this);
     }
@@ -362,22 +278,17 @@
     // setInfo may have corrected info (e.g. 565 is always opaque).
     const SkImageInfo& correctedInfo = this->info();
 
-    SkMallocPixelRef::PRFactory defaultFactory;
-    if (nullptr == factory) {
-        factory = &defaultFactory;
-    }
-
-    sk_sp<SkPixelRef> pr(factory->create(correctedInfo, correctedInfo.minRowBytes(), ctable));
+    sk_sp<SkPixelRef> pr = (allocFlags & kZeroPixels_AllocFlag) ?
+        SkMallocPixelRef::MakeZeroed(correctedInfo, correctedInfo.minRowBytes(), ctable) :
+        SkMallocPixelRef::MakeAllocate(correctedInfo, correctedInfo.minRowBytes(), ctable);
     if (!pr) {
         return reset_return_false(this);
     }
     this->setPixelRef(std::move(pr), 0, 0);
-
-    // TODO: lockPixels could/should return bool or void*/nullptr
-    this->lockPixels();
     if (nullptr == this->getPixels()) {
         return reset_return_false(this);
     }
+    SkDEBUGCODE(this->validate();)
     return true;
 }
 
@@ -403,17 +314,14 @@
     // setInfo may have corrected info (e.g. 565 is always opaque).
     const SkImageInfo& correctedInfo = this->info();
 
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewWithProc(correctedInfo, rb, ct, pixels, releaseProc,
-                                                       context));
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithProc(correctedInfo, rb, sk_ref_sp(ct),
+                                                          pixels, releaseProc, context);
     if (!pr) {
         this->reset();
         return false;
     }
 
     this->setPixelRef(std::move(pr), 0, 0);
-
-    // since we're already allocated, we lockPixels right away
-    this->lockPixels();
     SkDEBUGCODE(this->validate();)
     return true;
 }
@@ -437,16 +345,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkBitmap::freePixels() {
-    if (fPixelRef) {
-        if (fPixelLockCount > 0) {
-            fPixelRef->unlockPixels();
-        }
-        fPixelRef = nullptr;
-        fPixelRefOrigin.setZero();
-    }
-    fPixelLockCount = 0;
+    fPixelRef = nullptr;
+    fPixelRefOrigin.setZero();
     fPixels = nullptr;
-    fColorTable = nullptr;
 }
 
 uint32_t SkBitmap::getGenerationID() const {
@@ -473,74 +374,18 @@
         return false;
     }
 
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), ctable));
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, dst->rowBytes(), sk_ref_sp(ctable));
     if (!pr) {
         return false;
     }
 
     dst->setPixelRef(std::move(pr), 0, 0);
-    // since we're already allocated, we lockPixels right away
-    dst->lockPixels();
+    SkDEBUGCODE(dst->validate();)
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize,
-                           size_t dstRowBytes, bool preserveDstPad) {
-    const SkImageInfo& info = src.info();
-
-    if (0 == dstRowBytes) {
-        dstRowBytes = src.rowBytes();
-    }
-    if (dstRowBytes < info.minRowBytes()) {
-        return false;
-    }
-
-    if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) {
-        size_t safeSize = src.getSafeSize();
-        if (safeSize > dstSize || safeSize == 0)
-            return false;
-        else {
-            // This implementation will write bytes beyond the end of each row,
-            // excluding the last row, if the bitmap's stride is greater than
-            // strictly required by the current config.
-            memcpy(dst, src.addr(), safeSize);
-            return true;
-        }
-    } else {
-        // If destination has different stride than us, then copy line by line.
-        if (info.getSafeSize(dstRowBytes) > dstSize) {
-            return false;
-        } else {
-            // Just copy what we need on each line.
-            size_t rowBytes = info.minRowBytes();
-            const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr());
-            uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
-            for (int row = 0; row < info.height(); ++row) {
-                memcpy(dstP, srcP, rowBytes);
-                srcP += src.rowBytes();
-                dstP += dstRowBytes;
-            }
-
-            return true;
-        }
-    }
-}
-
-bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const {
-    if (nullptr == dst) {
-        return false;
-    }
-    SkAutoPixmapUnlock result;
-    if (!this->requestLock(&result)) {
-        return false;
-    }
-    return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 bool SkBitmap::isImmutable() const {
     return fPixelRef ? fPixelRef->isImmutable() : false;
 }
@@ -611,12 +456,12 @@
             break;
     }
 
-    SkAutoPixmapUnlock result;
-    if (!this->requestLock(&result)) {
+    SkPixmap result;
+    if (!this->peekPixels(&result)) {
         return;
     }
 
-    if (result.pixmap().erase(c, area)) {
+    if (result.erase(c, area)) {
         this->notifyPixelsChanged();
     }
 }
@@ -666,44 +511,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkBitmap::canCopyTo(SkColorType dstCT) const {
-    const SkColorType srcCT = this->colorType();
-
-    if (srcCT == kUnknown_SkColorType) {
-        return false;
-    }
-    if (srcCT == kAlpha_8_SkColorType && dstCT != kAlpha_8_SkColorType) {
-        return false;   // can't convert from alpha to non-alpha
-    }
-
-    bool sameConfigs = (srcCT == dstCT);
-    switch (dstCT) {
-        case kAlpha_8_SkColorType:
-        case kRGB_565_SkColorType:
-        case kRGBA_8888_SkColorType:
-        case kBGRA_8888_SkColorType:
-        case kRGBA_F16_SkColorType:
-            break;
-        case kGray_8_SkColorType:
-            if (!sameConfigs) {
-                return false;
-            }
-            break;
-        case kARGB_4444_SkColorType:
-            return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
-        default:
-            return false;
-    }
-    return true;
-}
-
 bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
-                          int x, int y) const {
-    SkAutoPixmapUnlock src;
-    if (!this->requestLock(&src)) {
+                          int x, int y, SkTransferFunctionBehavior behavior) const {
+    SkPixmap src;
+    if (!this->peekPixels(&src)) {
         return false;
     }
-    return src.pixmap().readPixels(requestedDstInfo, dstPixels, dstRB, x, y);
+    return src.readPixels(requestedDstInfo, dstPixels, dstRB, x, y, behavior);
 }
 
 bool SkBitmap::readPixels(const SkPixmap& dst, int srcX, int srcY) const {
@@ -712,11 +526,6 @@
 
 bool SkBitmap::writePixels(const SkPixmap& src, int dstX, int dstY,
                            SkTransferFunctionBehavior behavior) {
-    SkAutoPixmapUnlock dst;
-    if (!this->requestLock(&dst)) {
-        return false;
-    }
-
     if (!SkImageInfoValidConversion(fInfo, src.info())) {
         return false;
     }
@@ -733,126 +542,20 @@
     return true;
 }
 
-bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const {
-    if (!this->canCopyTo(dstColorType)) {
-        return false;
-    }
-
-    SkAutoPixmapUnlock srcUnlocker;
-    if (!this->requestLock(&srcUnlocker)) {
-        return false;
-    }
-    SkPixmap srcPM = srcUnlocker.pixmap();
-
-    // Various Android specific compatibility modes.
-    // TODO:
-    // Move the logic of this entire function into the framework, then call readPixels() directly.
-    SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
-    switch (dstColorType) {
-        case kRGB_565_SkColorType:
-            // copyTo() is not strict on alpha type.  Here we set the src to opaque to allow
-            // the call to readPixels() to succeed and preserve this lenient behavior.
-            if (kOpaque_SkAlphaType != srcPM.alphaType()) {
-                srcPM = SkPixmap(srcPM.info().makeAlphaType(kOpaque_SkAlphaType), srcPM.addr(),
-                                 srcPM.rowBytes(), srcPM.ctable());
-                dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
-            }
-            break;
-        case kRGBA_F16_SkColorType:
-            // The caller does not have an opportunity to pass a dst color space.  Assume that
-            // they want linear sRGB.
-            dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
-
-            if (!srcPM.colorSpace()) {
-                // We can't do a sane conversion to F16 without a dst color space.  Guess sRGB
-                // in this case.
-                srcPM.setColorSpace(SkColorSpace::MakeSRGB());
-            }
-            break;
-        default:
-            break;
-    }
-
-    SkBitmap tmpDst;
-    if (!tmpDst.setInfo(dstInfo)) {
-        return false;
-    }
-
-    // allocate colortable if srcConfig == kIndex8_Config
-    sk_sp<SkColorTable> ctable;
-    if (dstColorType == kIndex_8_SkColorType) {
-        ctable.reset(SkRef(srcPM.ctable()));
-    }
-    if (!tmpDst.tryAllocPixels(alloc, ctable.get())) {
-        return false;
-    }
-
-    SkAutoPixmapUnlock dstUnlocker;
-    if (!tmpDst.requestLock(&dstUnlocker)) {
-        return false;
-    }
-
-    SkPixmap dstPM = dstUnlocker.pixmap();
-
-    // We can't do a sane conversion from F16 without a src color space.  Guess sRGB in this case.
-    if (kRGBA_F16_SkColorType == srcPM.colorType() && !dstPM.colorSpace()) {
-        dstPM.setColorSpace(SkColorSpace::MakeSRGB());
-    }
-
-    // readPixels does not yet support color spaces with parametric transfer functions.  This
-    // works around that restriction when the color spaces are equal.
-    if (kRGBA_F16_SkColorType != dstColorType && kRGBA_F16_SkColorType != srcPM.colorType() &&
-            dstPM.colorSpace() == srcPM.colorSpace()) {
-        dstPM.setColorSpace(nullptr);
-        srcPM.setColorSpace(nullptr);
-    }
-
-    if (!srcPM.readPixels(dstPM)) {
-        return false;
-    }
-
-    //  (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref.
-    //  The old copyTo impl did this, so we continue it for now.
-    //
-    //  TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be
-    //      if (src_pixelref->info == dst_pixelref->info)
-    //
-    if (srcPM.colorType() == dstColorType && tmpDst.getSize() == srcPM.getSize64()) {
-        SkPixelRef* dstPixelRef = tmpDst.pixelRef();
-        if (dstPixelRef->info() == fPixelRef->info()) {
-            dstPixelRef->cloneGenID(*fPixelRef);
-        }
-    }
-
-    dst->swap(tmpDst);
-    return true;
-}
-
-// TODO: can we merge this with copyTo?
-bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
-    const SkColorType dstCT = this->colorType();
-
-    if (!this->canCopyTo(dstCT)) {
-        return false;
-    }
-    return this->copyTo(dst, dstCT, nullptr);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
     SkASSERT(alpha != nullptr);
     SkASSERT(alphaRowBytes >= src.width());
 
-    SkAutoPixmapUnlock apl;
-    if (!src.requestLock(&apl)) {
+    SkPixmap pmap;
+    if (!src.peekPixels(&pmap)) {
         for (int y = 0; y < src.height(); ++y) {
             memset(alpha, 0, src.width());
             alpha += alphaRowBytes;
         }
         return false;
     }
-    const SkPixmap& pmap = apl.pixmap();
     SkConvertPixels(SkImageInfo::MakeA8(pmap.width(), pmap.height()), alpha, alphaRowBytes,
                     pmap.info(), pmap.addr(), pmap.rowBytes(), pmap.ctable(),
                     SkTransferFunctionBehavior::kRespect);
@@ -964,13 +667,13 @@
         return;
     }
 
-    SkAutoPixmapUnlock result;
-    if (!bitmap.requestLock(&result)) {
+    SkPixmap result;
+    if (!bitmap.peekPixels(&result)) {
         buffer->writeUInt(0); // instead of snugRB, signaling no pixels
         return;
     }
 
-    write_raw_pixels(buffer, result.pixmap());
+    write_raw_pixels(buffer, result);
 }
 
 bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
@@ -1019,7 +722,7 @@
 
     sk_sp<SkColorTable> ctable;
     if (buffer->readBool()) {
-        ctable.reset(SkColorTable::Create(*buffer));
+        ctable = SkColorTable::Create(*buffer);
         if (!ctable) {
             return false;
         }
@@ -1043,12 +746,12 @@
         }
     }
 
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewWithData(info, info.minRowBytes(),
-                                                       ctable.get(), data.get()));
-    if (!pr.get()) {
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithData(info, info.minRowBytes(),
+                                                          std::move(ctable), std::move(data));
+    if (!pr) {
         return false;
     }
-    bitmap->setInfo(pr->info());
+    bitmap->setInfo(info);
     bitmap->setPixelRef(std::move(pr), 0, 0);
     return true;
 }
@@ -1076,20 +779,21 @@
     allFlags |= kHasHardwareMipMap_Flag;
 #endif
     SkASSERT((~allFlags & fFlags) == 0);
-    SkASSERT(fPixelLockCount >= 0);
+
+    if (fPixelRef && fPixelRef->pixels()) {
+        SkASSERT(fPixels);
+    } else {
+        SkASSERT(!fPixels);
+    }
 
     if (fPixels) {
         SkASSERT(fPixelRef);
-        SkASSERT(fPixelLockCount > 0);
-        SkASSERT(fPixelRef->isLocked());
         SkASSERT(fPixelRef->rowBytes() == fRowBytes);
         SkASSERT(fPixelRefOrigin.fX >= 0);
         SkASSERT(fPixelRefOrigin.fY >= 0);
-        SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
-        SkASSERT(fPixelRef->info().height() >= (int)this->height() + fPixelRefOrigin.fY);
+        SkASSERT(fPixelRef->width() >= (int)this->width() + fPixelRefOrigin.fX);
+        SkASSERT(fPixelRef->height() >= (int)this->height() + fPixelRefOrigin.fY);
         SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
-    } else {
-        SkASSERT(nullptr == fColorTable);
     }
 }
 #endif
@@ -1118,58 +822,17 @@
     }
     str->append(")");
 
-    SkPixelRef* pr = this->pixelRef();
-    if (nullptr == pr) {
-        // show null or the explicit pixel address (rare)
-        str->appendf(" pixels:%p", this->getPixels());
-    } else {
-        const char* uri = pr->getURI();
-        if (uri) {
-            str->appendf(" uri:\"%s\"", uri);
-        } else {
-            str->appendf(" pixelref:%p", pr);
-        }
-    }
-
+    str->appendf(" pixelref:%p", this->pixelRef());
     str->append(")");
 }
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const {
-    SkASSERT(result);
-
-    SkPixelRef* pr = fPixelRef.get();
-    if (nullptr == pr) {
-        return false;
-    }
-
-    // We have to lock the whole thing (using the pixelref's dimensions) until the api supports
-    // a partial lock (with offset/origin). Hence we can't use our fInfo.
-    SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality };
-    SkPixelRef::LockResult res;
-    if (pr->requestLock(req, &res)) {
-        SkASSERT(res.fPixels);
-        // The bitmap may be a subset of the pixelref's dimensions
-        SkASSERT(fPixelRefOrigin.x() + fInfo.width()  <= res.fSize.width());
-        SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height());
-        const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(),
-                                                                               fPixelRefOrigin.x(),
-                                                                               fPixelRefOrigin.y(),
-                                                                               res.fRowBytes);
-
-        result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable),
-                      res.fUnlockProc, res.fUnlockContext);
-        return true;
-    }
-    return false;
-}
-
 bool SkBitmap::peekPixels(SkPixmap* pmap) const {
     if (fPixels) {
         if (pmap) {
-            pmap->reset(fInfo, fPixels, fRowBytes, fColorTable);
+            pmap->reset(fInfo, fPixels, fRowBytes, this->getColorTable());
         }
         return true;
     }
diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp
index 064fc46..50553a0 100644
--- a/src/core/SkBitmapCache.cpp
+++ b/src/core/SkBitmapCache.cpp
@@ -26,10 +26,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkBitmap::Allocator* SkBitmapCache::GetAllocator() {
-    return SkResourceCache::GetAllocator();
-}
-
 /**
  This function finds the bounds of the bitmap *within its pixelRef*.
  If the bitmap lacks a pixelRef, it will return an empty rect, since
@@ -109,61 +105,227 @@
 
     const SkBitmapCacheDesc fDesc;
 };
+}
 
-struct BitmapRec : public SkResourceCache::Rec {
-    BitmapRec(const SkBitmapCacheDesc& desc, const SkBitmap& result)
+//////////////////////
+#include "SkDiscardableMemory.h"
+#include "SkNextID.h"
+
+void SkBitmapCache_setImmutableWithID(SkPixelRef* pr, uint32_t id) {
+    pr->setImmutableWithID(id);
+}
+
+//#define REC_TRACE   SkDebugf
+static void REC_TRACE(const char format[], ...) {}
+
+// for diagnostics
+static int32_t gRecCounter;
+
+class SkBitmapCache::Rec : public SkResourceCache::Rec {
+public:
+    Rec(const SkBitmapCacheDesc& desc, const SkImageInfo& info, size_t rowBytes,
+        std::unique_ptr<SkDiscardableMemory> dm, void* block)
         : fKey(desc)
-        , fBitmap(result)
+        , fDM(std::move(dm))
+        , fMalloc(block)
+        , fInfo(info)
+        , fRowBytes(rowBytes)
+        , fExternalCounter(kBeforeFirstInstall_ExternalCounter)
     {
-#ifdef TRACE_NEW_BITMAP_CACHE_RECS
-        fKey.dump();
-#endif
+        SkASSERT(!(fDM && fMalloc));    // can't have both
+
+        // We need an ID to return with the bitmap/pixelref.
+        // If they are not scaling, we can return the same ID as the key/desc
+        // If they are scaling, we need a new ID
+        if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) {
+            fPrUniqueID = desc.fImageID;
+        } else {
+            fPrUniqueID = SkNextID::ImageID();
+        }
+        REC_TRACE(" Rec(%d): [%d %d] %d\n",
+                  sk_atomic_inc(&gRecCounter), fInfo.width(), fInfo.height(), fPrUniqueID);
+    }
+
+    ~Rec() override {
+        SkASSERT(0 == fExternalCounter || kBeforeFirstInstall_ExternalCounter == fExternalCounter);
+        if (fDM && kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
+            // we never installed, so we need to unlock before we destroy the DM
+            SkASSERT(fDM->data());
+            fDM->unlock();
+        }
+        REC_TRACE("~Rec(%d): [%d %d] %d\n",
+                  sk_atomic_dec(&gRecCounter) - 1, fInfo.width(), fInfo.height(), fPrUniqueID);
+        sk_free(fMalloc);   // may be null
     }
 
     const Key& getKey() const override { return fKey; }
-    size_t bytesUsed() const override { return sizeof(fKey) + fBitmap.getSize(); }
+    size_t bytesUsed() const override {
+        return sizeof(fKey) + fInfo.getSafeSize(fRowBytes);
+    }
+    bool canBePurged() override {
+        SkAutoMutexAcquire ama(fMutex);
+        return fExternalCounter == 0;
+    }
+    void postAddInstall(void* payload) override {
+        SkAssertResult(this->install(static_cast<SkBitmap*>(payload)));
+    }
 
     const char* getCategory() const override { return "bitmap"; }
     SkDiscardableMemory* diagnostic_only_getDiscardable() const override {
-        return fBitmap.pixelRef()->diagnostic_only_getDiscardable();
+        return fDM.get();
+    }
+
+    static void ReleaseProc(void* addr, void* ctx) {
+        Rec* rec = static_cast<Rec*>(ctx);
+        SkAutoMutexAcquire ama(rec->fMutex);
+
+        REC_TRACE(" Rec: [%d] releaseproc\n", rec->fPrUniqueID);
+
+        SkASSERT(rec->fExternalCounter > 0);
+        rec->fExternalCounter -= 1;
+        if (rec->fDM) {
+            SkASSERT(rec->fMalloc == nullptr);
+            if (rec->fExternalCounter == 0) {
+                REC_TRACE(" Rec [%d] unlock\n", rec->fPrUniqueID);
+                rec->fDM->unlock();
+            }
+        } else {
+            SkASSERT(rec->fMalloc != nullptr);
+        }
+    }
+    
+    bool install(SkBitmap* bitmap) {
+        SkAutoMutexAcquire ama(fMutex);
+
+        // are we still valid
+        if (!fDM && !fMalloc) {
+            REC_TRACE(" Rec: [%d] invalid\n", fPrUniqueID);
+            return false;
+        }
+
+        /*
+            constructor      fExternalCount < 0     fDM->data()
+            after install    fExternalCount > 0     fDM->data()
+            after Release    fExternalCount == 0    !fDM->data()
+        */
+        if (fDM) {
+            if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
+                SkASSERT(fDM->data());
+            } else if (fExternalCounter > 0) {
+                SkASSERT(fDM->data());
+            } else {
+                SkASSERT(fExternalCounter == 0);
+                if (!fDM->lock()) {
+                    REC_TRACE(" Rec [%d] re-lock failed\n", fPrUniqueID);
+                    fDM.reset(nullptr);
+                    return false;
+                }
+                REC_TRACE(" Rec [%d] re-lock succeeded\n", fPrUniqueID);
+            }
+            SkASSERT(fDM->data());
+        }
+
+        bitmap->installPixels(fInfo, fDM ? fDM->data() : fMalloc, fRowBytes, nullptr,
+                              ReleaseProc, this);
+        SkBitmapCache_setImmutableWithID(bitmap->pixelRef(), fPrUniqueID);
+
+        REC_TRACE(" Rec: [%d] install new pr\n", fPrUniqueID);
+
+        if (kBeforeFirstInstall_ExternalCounter == fExternalCounter) {
+            fExternalCounter = 1;
+        } else {
+            fExternalCounter += 1;
+        }
+        SkASSERT(fExternalCounter > 0);
+        return true;
     }
 
     static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
-        const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec);
+        Rec* rec = (Rec*)&baseRec;
         SkBitmap* result = (SkBitmap*)contextBitmap;
-
-        *result = rec.fBitmap;
-        result->lockPixels();
-        return SkToBool(result->getPixels());
+        REC_TRACE(" Rec: [%d] found\n", rec->fPrUniqueID);
+        return rec->install(result);
     }
 
 private:
     BitmapKey   fKey;
-    SkBitmap    fBitmap;
+
+    SkMutex     fMutex;
+
+    // either fDM or fMalloc can be non-null, but not both
+    std::unique_ptr<SkDiscardableMemory> fDM;
+    void*       fMalloc;
+
+    SkImageInfo fInfo;
+    size_t      fRowBytes;
+    uint32_t    fPrUniqueID;
+
+    // This field counts the number of external pixelrefs we have created. They notify us when
+    // they are destroyed so we can decrement this.
+    //
+    //  > 0     we have outstanding pixelrefs
+    // == 0     we have no outstanding pixelrefs, and can be safely purged
+    //  < 0     we have been created, but not yet "installed" the first time.
+    //
+    int         fExternalCounter;
+
+    enum {
+        kBeforeFirstInstall_ExternalCounter = -1
+    };
 };
-} // namespace
+
+void SkBitmapCache::PrivateDeleteRec(Rec* rec) { delete rec; }
+
+SkBitmapCache::RecPtr SkBitmapCache::Alloc(const SkBitmapCacheDesc& desc, const SkImageInfo& info,
+                                           SkPixmap* pmap) {
+    // Ensure that the caller is self-consistent:
+    //  - if they are scaling, the info matches the scaled size
+    //  - if they are not, the info matches the subset (i.e. the subset is the entire image)
+    if (desc.fScaledWidth == 0 && desc.fScaledHeight == 0) {
+        SkASSERT(info.width() == desc.fSubset.width());
+        SkASSERT(info.height() == desc.fSubset.height());
+    } else {
+        SkASSERT(info.width() == desc.fScaledWidth);
+        SkASSERT(info.height() == desc.fScaledHeight);
+    }
+
+    const size_t rb = info.minRowBytes();
+    size_t size = info.getSafeSize(rb);
+    if (0 == size) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SkDiscardableMemory> dm;
+    void* block = nullptr;
+
+    auto factory = SkResourceCache::GetDiscardableFactory();
+    if (factory) {
+        dm.reset(factory(size));
+    } else {
+        block = sk_malloc_flags(size, 0);
+    }
+    if (!dm && !block) {
+        return nullptr;
+    }
+    *pmap = SkPixmap(info, dm ? dm->data() : block, rb);
+    return RecPtr(new Rec(desc, info, rb, std::move(dm), block));
+}
+
+void SkBitmapCache::Add(RecPtr rec, SkBitmap* bitmap) {
+    SkResourceCache::Add(rec.release(), bitmap);
+}
+
+bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result) {
+    desc.validate();
+    return SkResourceCache::Find(BitmapKey(desc), SkBitmapCache::Rec::Finder, result);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
 
 #define CHECK_LOCAL(localCache, localName, globalName, ...) \
     ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__))
 
-bool SkBitmapCache::Find(const SkBitmapCacheDesc& desc, SkBitmap* result,
-                         SkResourceCache* localCache) {
-    desc.validate();
-    return CHECK_LOCAL(localCache, find, Find, BitmapKey(desc), BitmapRec::Finder, result);
-}
-
-bool SkBitmapCache::Add(const SkBitmapCacheDesc& desc, const SkBitmap& result,
-                        SkResourceCache* localCache) {
-    desc.validate();
-    SkASSERT(result.isImmutable());
-    BitmapRec* rec = new BitmapRec(desc, result);
-    CHECK_LOCAL(localCache, add, Add, rec);
-    return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////
-
 namespace {
 static unsigned gMipMapKeyNamespaceLabel;
 
diff --git a/src/core/SkBitmapCache.h b/src/core/SkBitmapCache.h
index 907a546..a59dcb5 100644
--- a/src/core/SkBitmapCache.h
+++ b/src/core/SkBitmapCache.h
@@ -45,23 +45,20 @@
 class SkBitmapCache {
 public:
     /**
-     * Use this allocator for bitmaps, so they can use ashmem when available.
-     * Returns nullptr if the ResourceCache has not been initialized with a DiscardableFactory.
-     */
-    static SkBitmap::Allocator* GetAllocator();
-
-    /**
      *  Search based on the desc. If found, returns true and
      *  result will be set to the matching bitmap with its pixels already locked.
      */
-    static bool Find(const SkBitmapCacheDesc&, SkBitmap* result,
-                    SkResourceCache* localCache = nullptr);
+    static bool Find(const SkBitmapCacheDesc&, SkBitmap* result);
 
-    /*
-     *  result must be marked isImmutable()
-     */
-    static bool Add(const SkBitmapCacheDesc&, const SkBitmap& result,
-                    SkResourceCache* localCache = nullptr);
+    class Rec;
+    struct RecDeleter { void operator()(Rec* r) { PrivateDeleteRec(r); } };
+    typedef std::unique_ptr<Rec, RecDeleter> RecPtr;
+
+    static RecPtr Alloc(const SkBitmapCacheDesc&, const SkImageInfo&, SkPixmap*);
+    static void Add(RecPtr, SkBitmap*);
+
+private:
+    static void PrivateDeleteRec(Rec*);
 };
 
 class SkMipMapCache {
diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp
index 7ef0a99..d44f89c 100644
--- a/src/core/SkBitmapController.cpp
+++ b/src/core/SkBitmapController.cpp
@@ -85,9 +85,16 @@
     return false;
 #endif
 
-    if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
-        fInvMatrix.hasPerspective())
-    {
+    bool supported = false;
+    switch (provider.info().colorType()) {
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            supported = true;
+            break;
+        default:
+            break;
+    }
+    if (!supported || !cache_size_okay(provider, fInvMatrix) || fInvMatrix.hasPerspective()) {
         return false; // can't handle the reqeust
     }
 
@@ -116,7 +123,6 @@
     if (fCanShadeHQ) {
         fQuality = kHigh_SkFilterQuality;
         SkAssertResult(provider.asBitmap(&fResultBitmap));
-        fResultBitmap.lockPixels();
         return true;
     }
 
@@ -129,25 +135,40 @@
         if (!provider.asBitmap(&orig)) {
             return false;
         }
-        SkAutoPixmapUnlock src;
-        if (!orig.requestLock(&src)) {
+        SkPixmap src;
+        if (!orig.peekPixels(&src)) {
             return false;
         }
-        if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
-                                    dstW, dstH, SkResourceCache::GetAllocator())) {
+
+        SkPixmap dst;
+        SkBitmapCache::RecPtr rec;
+        const SkImageInfo info = SkImageInfo::Make(desc.fScaledWidth, desc.fScaledHeight,
+                                                   src.colorType(), src.alphaType());
+        if (provider.isVolatile()) {
+            if (!fResultBitmap.tryAllocPixels(info)) {
+                return false;
+            }
+            SkASSERT(fResultBitmap.getPixels());
+            fResultBitmap.peekPixels(&dst);
+            fResultBitmap.setImmutable();   // a little cheat, as we haven't resized yet, but ok
+        } else {
+            rec = SkBitmapCache::Alloc(desc, info, &dst);
+            if (!rec) {
+                return false;
+            }
+        }
+        if (!SkBitmapScaler::Resize(dst, src, kHQ_RESIZE_METHOD)) {
             return false; // we failed to create fScaledBitmap
         }
-
-        SkASSERT(fResultBitmap.getPixels());
-        fResultBitmap.setImmutable();
-        if (!provider.isVolatile()) {
-            if (SkBitmapCache::Add(desc, fResultBitmap)) {
-                provider.notifyAddedToCache();
-            }
+        if (rec) {
+            SkBitmapCache::Add(std::move(rec), &fResultBitmap);
+            SkASSERT(fResultBitmap.getPixels());
+            provider.notifyAddedToCache();
         }
     }
 
     SkASSERT(fResultBitmap.getPixels());
+    SkASSERT(fResultBitmap.isImmutable());
 
     fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
                          SkIntToScalar(dstH) / provider.height());
@@ -226,8 +247,6 @@
         SkASSERT(fResultBitmap.getPixels());
     } else {
         (void)provider.asBitmap(&fResultBitmap);
-        fResultBitmap.lockPixels();
-        // lock may fail to give us pixels
     }
     SkASSERT(fCanShadeHQ || fQuality <= kLow_SkFilterQuality);
 
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 3be63ba..981273a 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -20,6 +20,7 @@
 #include "SkShader.h"
 #include "SkSpecialImage.h"
 #include "SkSurface.h"
+#include "SkTLazy.h"
 #include "SkVertices.h"
 
 class SkColorTable;
@@ -74,7 +75,6 @@
     , fRCStack(bitmap.width(), bitmap.height())
 {
     SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
-    fBitmap.lockPixels();
 }
 
 SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) {
@@ -89,7 +89,6 @@
     , fRCStack(bitmap.width(), bitmap.height())
 {
     SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
-    fBitmap.lockPixels();
 }
 
 SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
@@ -121,9 +120,8 @@
         }
     } else {
         // This bitmap has transparency, so we'll zero the pixels (to transparent).
-        // We use a ZeroedPRFactory as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT).
-        SkMallocPixelRef::ZeroedPRFactory factory;
-        if (!bitmap.tryAllocPixels(info, &factory, nullptr/*color table*/)) {
+        // We use the flag as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT).
+        if (!bitmap.tryAllocPixels(info, nullptr/*colortable*/, SkBitmap::kZeroPixels_AllocFlag)) {
             return nullptr;
         }
     }
@@ -135,7 +133,6 @@
     SkASSERT(bm.width() == fBitmap.width());
     SkASSERT(bm.height() == fBitmap.height());
     fBitmap = bm;   // intent is to use bm's pixelRef (and rowbytes/config)
-    fBitmap.lockPixels();
     this->privateResize(fBitmap.info().width(), fBitmap.info().height());
 }
 
@@ -316,8 +313,14 @@
             matrix.preTranslate(dx, dy);
         }
 
+#ifdef SK_DRAWBITMAPRECT_FAST_OFFSET
+        SkRect extractedBitmapBounds = SkRect::MakeXYWH(dx, dy,
+                                                        SkIntToScalar(bitmapPtr->width()),
+                                                        SkIntToScalar(bitmapPtr->height()));
+#else
         SkRect extractedBitmapBounds;
         extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
+#endif
         if (extractedBitmapBounds == tmpSrc) {
             // no fractional part in src, we can just call drawBitmap
             goto USE_DRAWBITMAP;
@@ -385,35 +388,116 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkBitmapDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
-                                 const SkPaint& paint) {
-    SkASSERT(!srcImg->isTextureBacked());
+namespace {
 
-    SkBitmap resultBM;
+class SkAutoDeviceClipRestore {
+public:
+    SkAutoDeviceClipRestore(SkBaseDevice* device, const SkIRect& clip)
+        : fDevice(device)
+        , fPrevCTM(device->ctm()) {
+        fDevice->save();
+        fDevice->setCTM(SkMatrix::I());
+        fDevice->clipRect(SkRect::Make(clip), SkClipOp::kIntersect, false);
+        fDevice->setCTM(fPrevCTM);
+    }
 
-    SkImageFilter* filter = paint.getImageFilter();
-    if (filter) {
+    ~SkAutoDeviceClipRestore() {
+        fDevice->restore(fPrevCTM);
+    }
+
+private:
+    SkBaseDevice*  fDevice;
+    const SkMatrix fPrevCTM;
+};
+
+}  // anonymous ns
+
+void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint,
+                                 SkImage* clipImage, const SkMatrix& clipMatrix) {
+    SkASSERT(!src->isTextureBacked());
+
+    sk_sp<SkSpecialImage> filteredImage;
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+
+    if (SkImageFilter* filter = paint->getImageFilter()) {
         SkIPoint offset = SkIPoint::Make(0, 0);
-        SkMatrix matrix = this->ctm();
-        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+        const SkMatrix matrix = SkMatrix::Concat(
+            SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm());
         const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y);
         sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
         SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace());
         SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
 
-        sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset));
-        if (resultImg) {
-            SkPaint tmpUnfiltered(paint);
-            tmpUnfiltered.setImageFilter(nullptr);
-            if (resultImg->getROPixels(&resultBM)) {
-                this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
-            }
+        filteredImage = filter->filterImage(src, ctx, &offset);
+        if (!filteredImage) {
+            return;
         }
-    } else {
-        if (srcImg->getROPixels(&resultBM)) {
-            this->drawSprite(resultBM, x, y, paint);
-        }
+
+        src = filteredImage.get();
+        paint.writable()->setImageFilter(nullptr);
+        x += offset.x();
+        y += offset.y();
     }
+
+    if (!clipImage) {
+        SkBitmap resultBM;
+        if (src->getROPixels(&resultBM)) {
+            this->drawSprite(resultBM, x, y, *paint);
+        }
+        return;
+    }
+
+    // Clip image case.
+    sk_sp<SkImage> srcImage(src->asImage());
+    if (!srcImage) {
+        return;
+    }
+
+    const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix);
+    SkRect clipBounds;
+    totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds()));
+    const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y);
+
+    SkIRect maskBounds = fRCStack.rc().getBounds();
+    if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) {
+        return;
+    }
+
+    sk_sp<SkImage> mask;
+    SkMatrix maskMatrix, shaderMatrix;
+    SkTLazy<SkAutoDeviceClipRestore> autoClipRestore;
+
+    SkMatrix totalInverse;
+    if (clipImage->isAlphaOnly() && totalMatrix.invert(&totalInverse)) {
+        // If the mask is already in A8 format, we can draw it directly
+        // (while compensating in the shader matrix).
+        mask = sk_ref_sp(clipImage);
+        maskMatrix = totalMatrix;
+        shaderMatrix = SkMatrix::Concat(totalInverse, SkMatrix::MakeTrans(x, y));
+
+        // If the mask is not fully contained within the src layer, we must clip.
+        if (!srcBounds.contains(clipBounds)) {
+            autoClipRestore.init(this, srcBounds);
+        }
+
+        maskBounds.offsetTo(0, 0);
+    } else {
+        // Otherwise, we convert the mask to A8 explicitly.
+        sk_sp<SkSurface> surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(),
+                                                                          maskBounds.height()));
+        SkCanvas* canvas = surf->getCanvas();
+        canvas->translate(-maskBounds.x(), -maskBounds.y());
+        canvas->concat(totalMatrix);
+        canvas->drawImage(clipImage, 0, 0);
+
+        mask = surf->makeImageSnapshot();
+        maskMatrix = SkMatrix::I();
+        shaderMatrix = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y());
+    }
+
+    SkAutoDeviceCTMRestore adctmr(this, maskMatrix);
+    paint.writable()->setShader(srcImage->makeShader(&shaderMatrix));
+    this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint);
 }
 
 sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index d0f8be4..3e9473d 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -111,8 +111,9 @@
     void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override;
 
     ///////////////////////////////////////////////////////////////////////////
-    
-    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
+
+    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                     SkImage*, const SkMatrix&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
     sk_sp<SkSpecialImage> snapSpecial() override;
@@ -143,6 +144,7 @@
     friend class SkDrawIter;
     friend class SkDeviceFilteredPaint;
     friend class SkSurface_Raster;
+    friend class SkThreadedBMPDevice; // to copy fRCStack
 
     class BDDraw;
 
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
deleted file mode 100644
index 3472bf0..0000000
--- a/src/core/SkBitmapProcShader.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmapProcShader.h"
-
-#include "SkArenaAlloc.h"
-#include "SkBitmapProcState.h"
-#include "SkBitmapProvider.h"
-#include "SkXfermodePriv.h"
-
-static bool only_scale_and_translate(const SkMatrix& matrix) {
-    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
-    return (matrix.getType() & ~mask) == 0;
-}
-
-class BitmapProcInfoContext : public SkShader::Context {
-public:
-    // The info has been allocated elsewhere, but we are responsible for calling its destructor.
-    BitmapProcInfoContext(const SkShader& shader, const SkShader::ContextRec& rec,
-                            SkBitmapProcInfo* info)
-        : INHERITED(shader, rec)
-        , fInfo(info)
-    {
-        fFlags = 0;
-        if (fInfo->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) {
-            fFlags |= SkShader::kOpaqueAlpha_Flag;
-        }
-
-        if (1 == fInfo->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) {
-            fFlags |= SkShader::kConstInY32_Flag;
-        }
-    }
-
-    uint32_t getFlags() const override { return fFlags; }
-
-private:
-    SkBitmapProcInfo*   fInfo;
-    uint32_t            fFlags;
-
-    typedef SkShader::Context INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class BitmapProcShaderContext : public BitmapProcInfoContext {
-public:
-    BitmapProcShaderContext(const SkShader& shader, const SkShader::ContextRec& rec,
-                            SkBitmapProcState* state)
-        : INHERITED(shader, rec, state)
-        , fState(state)
-    {}
-
-    void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
-        const SkBitmapProcState& state = *fState;
-        if (state.getShaderProc32()) {
-            state.getShaderProc32()(&state, x, y, dstC, count);
-            return;
-        }
-
-        const int BUF_MAX = 128;
-        uint32_t buffer[BUF_MAX];
-        SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
-        SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
-        const int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
-
-        SkASSERT(state.fPixmap.addr());
-
-        for (;;) {
-            int n = SkTMin(count, max);
-            SkASSERT(n > 0 && n < BUF_MAX*2);
-            mproc(state, buffer, n, x, y);
-            sproc(state, buffer, n, dstC);
-
-            if ((count -= n) == 0) {
-                break;
-            }
-            SkASSERT(count > 0);
-            x += n;
-            dstC += n;
-        }
-    }
-
-    ShadeProc asAShadeProc(void** ctx) override {
-        if (fState->getShaderProc32()) {
-            *ctx = fState;
-            return (ShadeProc)fState->getShaderProc32();
-        }
-        return nullptr;
-    }
-
-private:
-    SkBitmapProcState*  fState;
-
-    typedef BitmapProcInfoContext INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-#include "SkLinearBitmapPipeline.h"
-#include "SkPM4f.h"
-
-class LinearPipelineContext : public BitmapProcInfoContext {
-public:
-    LinearPipelineContext(const SkShader& shader, const SkShader::ContextRec& rec,
-                          SkBitmapProcInfo* info, SkArenaAlloc* alloc)
-        : INHERITED(shader, rec, info), fAllocator{alloc}
-    {
-        // Save things off in case we need to build a blitter pipeline.
-        fSrcPixmap = info->fPixmap;
-        fAlpha = SkColorGetA(info->fPaintColor) / 255.0f;
-        fFilterQuality = info->fFilterQuality;
-        fMatrixTypeMask = info->fRealInvMatrix.getType();
-
-        fShaderPipeline = alloc->make<SkLinearBitmapPipeline>(
-            info->fRealInvMatrix, info->fFilterQuality,
-            info->fTileModeX, info->fTileModeY,
-            info->fPaintColor,
-            info->fPixmap,
-            fAllocator);
-
-        // To implement the old shadeSpan entry-point, we need to efficiently convert our native
-        // floats into SkPMColor. The SkXfermode::D32Procs do exactly that.
-        //
-        fSrcModeProc = SkXfermode::GetD32Proc(SkBlendMode::kSrc, 0);
-    }
-
-    void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override {
-        fShaderPipeline->shadeSpan4f(x, y, dstC, count);
-    }
-
-    void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
-        const int N = 128;
-        SkPM4f  tmp[N];
-
-        while (count > 0) {
-            const int n = SkTMin(count, N);
-            fShaderPipeline->shadeSpan4f(x, y, tmp, n);
-            fSrcModeProc(SkBlendMode::kSrc, dstC, tmp, n, nullptr);
-            dstC += n;
-            x += n;
-            count -= n;
-        }
-    }
-
-    bool onChooseBlitProcs(const SkImageInfo& dstInfo, BlitState* state) override {
-        if ((fBlitterPipeline = SkLinearBitmapPipeline::ClonePipelineForBlitting(
-            *fShaderPipeline,
-            fMatrixTypeMask,
-            fFilterQuality, fSrcPixmap,
-            fAlpha, state->fMode, dstInfo, fAllocator)))
-        {
-            state->fStorage[0] = fBlitterPipeline;
-            state->fBlitBW = &LinearPipelineContext::ForwardToPipeline;
-
-            return true;
-        }
-
-        return false;
-    }
-
-    static void ForwardToPipeline(BlitState* state, int x, int y, const SkPixmap& dst, int count) {
-        SkLinearBitmapPipeline* pipeline = static_cast<SkLinearBitmapPipeline*>(state->fStorage[0]);
-        void* addr = dst.writable_addr32(x, y);
-        pipeline->blitSpan(x, y, addr, count);
-    }
-
-private:
-    // Store the allocator from the context creation incase we are asked to build a blitter.
-    SkArenaAlloc*           fAllocator;
-    SkLinearBitmapPipeline* fShaderPipeline;
-    SkLinearBitmapPipeline* fBlitterPipeline;
-    SkXfermode::D32Proc     fSrcModeProc;
-    SkPixmap                fSrcPixmap;
-    float                   fAlpha;
-    SkMatrix::TypeMask      fMatrixTypeMask;
-    SkFilterQuality         fFilterQuality;
-
-    typedef BitmapProcInfoContext INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static bool choose_linear_pipeline(const SkShader::ContextRec& rec, const SkImageInfo& srcInfo) {
-    // If we get here, we can reasonably use either context, respect the caller's preference
-    //
-    bool needsPremul = srcInfo.alphaType() == kUnpremul_SkAlphaType;
-    bool needsSwizzle = srcInfo.bytesPerPixel() == 4 && srcInfo.colorType() != kN32_SkColorType;
-    return SkShader::ContextRec::kPM4f_DstType == rec.fPreferredDstType
-           || needsPremul || needsSwizzle;
-}
-
-size_t SkBitmapProcLegacyShader::ContextSize(const ContextRec& rec, const SkImageInfo& srcInfo) {
-    size_t size0 = sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState);
-    size_t size1 = sizeof(LinearPipelineContext) + sizeof(SkBitmapProcInfo);
-    size_t s = SkTMax(size0, size1);
-    return s;
-}
-
-SkShader::Context* SkBitmapProcLegacyShader::MakeContext(
-    const SkShader& shader, TileMode tmx, TileMode tmy,
-    const SkBitmapProvider& provider, const ContextRec& rec, SkArenaAlloc* alloc)
-{
-    SkMatrix totalInverse;
-    // Do this first, so we know the matrix can be inverted.
-    if (!shader.computeTotalInverse(rec, &totalInverse)) {
-        return nullptr;
-    }
-
-    // Decide if we can/want to use the new linear pipeline
-    bool useLinearPipeline = choose_linear_pipeline(rec, provider.info());
-
-    if (useLinearPipeline) {
-        SkBitmapProcInfo* info = alloc->make<SkBitmapProcInfo>(provider, tmx, tmy);
-        if (!info->init(totalInverse, *rec.fPaint)) {
-            return nullptr;
-        }
-
-        return alloc->make<LinearPipelineContext>(shader, rec, info, alloc);
-    } else {
-        SkBitmapProcState* state = alloc->make<SkBitmapProcState>(provider, tmx, tmy);
-        if (!state->setup(totalInverse, *rec.fPaint)) {
-            return nullptr;
-        }
-        return alloc->make<BitmapProcShaderContext>(shader, rec, state);
-    }
-}
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
deleted file mode 100644
index 204b27d..0000000
--- a/src/core/SkBitmapProcShader.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkBitmapProcShader_DEFINED
-#define SkBitmapProcShader_DEFINED
-
-#include "SkImagePriv.h"
-#include "SkShader.h"
-
-class SkBitmapProvider;
-
-class SkBitmapProcLegacyShader : public SkShader {
-private:
-    friend class SkImageShader;
-
-    static size_t ContextSize(const ContextRec&, const SkImageInfo& srcInfo);
-    static Context* MakeContext(const SkShader&, TileMode tmx, TileMode tmy,
-                                const SkBitmapProvider&, const ContextRec&, SkArenaAlloc* alloc);
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/core/SkBitmapProvider.cpp b/src/core/SkBitmapProvider.cpp
index 928214c..90c4e62 100644
--- a/src/core/SkBitmapProvider.cpp
+++ b/src/core/SkBitmapProvider.cpp
@@ -7,7 +7,6 @@
 
 #include "SkBitmapProvider.h"
 #include "SkImage_Base.h"
-#include "SkImageCacherator.h"
 #include "SkPixelRef.h"
 
 int SkBitmapProvider::width() const {
diff --git a/src/core/SkBitmapScaler.cpp b/src/core/SkBitmapScaler.cpp
index b4ade85..c803da7 100644
--- a/src/core/SkBitmapScaler.cpp
+++ b/src/core/SkBitmapScaler.cpp
@@ -249,7 +249,6 @@
     }
 
     *resultPtr = result;
-    resultPtr->lockPixels();
     SkASSERT(resultPtr->getPixels());
     return true;
 }
diff --git a/src/core/SkBlendMode.cpp b/src/core/SkBlendMode.cpp
new file mode 100644
index 0000000..d7ebfb8
--- /dev/null
+++ b/src/core/SkBlendMode.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "SkBlendModePriv.h"
+#include "SkRasterPipeline.h"
+
+bool SkBlendMode_CanOverflow(SkBlendMode mode) {
+    return mode == SkBlendMode::kPlus;
+}
+
+bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode) {
+    switch (mode) {
+        case SkBlendMode::kDst:
+        case SkBlendMode::kSrcOver:
+        case SkBlendMode::kDstOver:
+        case SkBlendMode::kDstOut:
+        case SkBlendMode::kSrcATop:
+        case SkBlendMode::kXor:
+        case SkBlendMode::kPlus:
+            return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+struct CoeffRec {
+    SkBlendModeCoeff    fSrc;
+    SkBlendModeCoeff    fDst;
+};
+
+const CoeffRec gCoeffs[] = {
+    { SkBlendModeCoeff::kZero,    SkBlendModeCoeff::kZero },
+    { SkBlendModeCoeff::kOne,     SkBlendModeCoeff::kZero },
+    { SkBlendModeCoeff::kZero,    SkBlendModeCoeff::kOne  },
+    { SkBlendModeCoeff::kOne,     SkBlendModeCoeff::kISA  },
+    { SkBlendModeCoeff::kIDA,     SkBlendModeCoeff::kOne  },
+    { SkBlendModeCoeff::kDA,      SkBlendModeCoeff::kZero },
+    { SkBlendModeCoeff::kZero,    SkBlendModeCoeff::kSA   },
+    { SkBlendModeCoeff::kIDA,     SkBlendModeCoeff::kZero },
+    { SkBlendModeCoeff::kZero,    SkBlendModeCoeff::kISA  },
+    { SkBlendModeCoeff::kDA,      SkBlendModeCoeff::kISA  },
+    { SkBlendModeCoeff::kIDA,     SkBlendModeCoeff::kSA   },
+    { SkBlendModeCoeff::kIDA,     SkBlendModeCoeff::kISA  },
+
+    { SkBlendModeCoeff::kOne,     SkBlendModeCoeff::kOne  },
+    { SkBlendModeCoeff::kZero,    SkBlendModeCoeff::kSC   },
+    { SkBlendModeCoeff::kOne,     SkBlendModeCoeff::kISC  },    // screen
+};
+
+bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst) {
+    if (mode > SkBlendMode::kScreen) {
+        return false;
+    }
+    if (src) {
+        *src = gCoeffs[static_cast<int>(mode)].fSrc;
+    }
+    if (dst) {
+        *dst = gCoeffs[static_cast<int>(mode)].fDst;
+    }
+    return true;
+}
+
+void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) {
+    auto stage = SkRasterPipeline::srcover;
+    switch (mode) {
+        case SkBlendMode::kClear:    stage = SkRasterPipeline::clear; break;
+        case SkBlendMode::kSrc:      return;  // This stage is a no-op.
+        case SkBlendMode::kDst:      stage = SkRasterPipeline::move_dst_src; break;
+        case SkBlendMode::kSrcOver:  stage = SkRasterPipeline::srcover; break;
+        case SkBlendMode::kDstOver:  stage = SkRasterPipeline::dstover; break;
+        case SkBlendMode::kSrcIn:    stage = SkRasterPipeline::srcin; break;
+        case SkBlendMode::kDstIn:    stage = SkRasterPipeline::dstin; break;
+        case SkBlendMode::kSrcOut:   stage = SkRasterPipeline::srcout; break;
+        case SkBlendMode::kDstOut:   stage = SkRasterPipeline::dstout; break;
+        case SkBlendMode::kSrcATop:  stage = SkRasterPipeline::srcatop; break;
+        case SkBlendMode::kDstATop:  stage = SkRasterPipeline::dstatop; break;
+        case SkBlendMode::kXor:      stage = SkRasterPipeline::xor_; break;
+        case SkBlendMode::kPlus:     stage = SkRasterPipeline::plus_; break;
+        case SkBlendMode::kModulate: stage = SkRasterPipeline::modulate; break;
+
+        case SkBlendMode::kScreen:     stage = SkRasterPipeline::screen; break;
+        case SkBlendMode::kOverlay:    stage = SkRasterPipeline::overlay; break;
+        case SkBlendMode::kDarken:     stage = SkRasterPipeline::darken; break;
+        case SkBlendMode::kLighten:    stage = SkRasterPipeline::lighten; break;
+        case SkBlendMode::kColorDodge: stage = SkRasterPipeline::colordodge; break;
+        case SkBlendMode::kColorBurn:  stage = SkRasterPipeline::colorburn; break;
+        case SkBlendMode::kHardLight:  stage = SkRasterPipeline::hardlight; break;
+        case SkBlendMode::kSoftLight:  stage = SkRasterPipeline::softlight; break;
+        case SkBlendMode::kDifference: stage = SkRasterPipeline::difference; break;
+        case SkBlendMode::kExclusion:  stage = SkRasterPipeline::exclusion; break;
+        case SkBlendMode::kMultiply:   stage = SkRasterPipeline::multiply; break;
+
+        case SkBlendMode::kHue:        stage = SkRasterPipeline::hue; break;
+        case SkBlendMode::kSaturation: stage = SkRasterPipeline::saturation; break;
+        case SkBlendMode::kColor:      stage = SkRasterPipeline::color; break;
+        case SkBlendMode::kLuminosity: stage = SkRasterPipeline::luminosity; break;
+    }
+    p->append(stage);
+}
diff --git a/src/core/SkBlendModePriv.h b/src/core/SkBlendModePriv.h
index 0d0589c..c965fe2 100644
--- a/src/core/SkBlendModePriv.h
+++ b/src/core/SkBlendModePriv.h
@@ -13,12 +13,28 @@
 
 bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode);
 bool SkBlendMode_CanOverflow(SkBlendMode);
-bool SkBlendMode_AppendStages(SkBlendMode, SkRasterPipeline* = nullptr);
+void SkBlendMode_AppendStages(SkBlendMode, SkRasterPipeline*);
 
 #if SK_SUPPORT_GPU
 #include "GrXferProcessor.h"
 const GrXPFactory* SkBlendMode_AsXPFactory(SkBlendMode);
 #endif
 
+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);
 
 #endif
diff --git a/src/core/SkBlitRow.h b/src/core/SkBlitRow.h
index 56121eb..35f0a0a 100644
--- a/src/core/SkBlitRow.h
+++ b/src/core/SkBlitRow.h
@@ -78,9 +78,6 @@
 
     static Proc32 PlatformProcs32(unsigned flags);
 
-    static Proc16 PlatformFactory565(unsigned flags);
-    static ColorProc16 PlatformColorFactory565(unsigned flags);
-
 private:
     enum {
         kFlags16_Mask = 7,
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
index 648e0ea..3c5aba5 100644
--- a/src/core/SkBlitRow_D16.cpp
+++ b/src/core/SkBlitRow_D16.cpp
@@ -245,11 +245,7 @@
     // just so we don't crash
     flags &= kFlags16_Mask;
 
-    SkBlitRow::Proc16 proc = PlatformFactory565(flags);
-    if (nullptr == proc) {
-        proc = gDefault_565_Procs[flags];
-    }
-    return proc;
+    return gDefault_565_Procs[flags];
 }
 
 static const SkBlitRow::ColorProc16 gDefault_565_ColorProcs[] = {
@@ -273,9 +269,5 @@
 
     SkASSERT(flags < SK_ARRAY_COUNT(gDefault_565_ColorProcs));
 
-    SkBlitRow::ColorProc16 proc = PlatformColorFactory565(flags);
-    if (nullptr == proc) {
-        proc = gDefault_565_ColorProcs[flags];
-    }
-    return proc;
+    return gDefault_565_ColorProcs[flags];
 }
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index 0e8bd2d..a439b35 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -14,14 +14,12 @@
 #include "SkWriteBuffer.h"
 #include "SkMask.h"
 #include "SkMaskFilter.h"
+#include "SkShaderBase.h"
 #include "SkString.h"
 #include "SkTLazy.h"
 #include "SkUtils.h"
 #include "SkXfermodeInterpretation.h"
 
-// define this for testing srgb blits
-//#define SK_FORCE_PM4f_FOR_L32_BLITS
-
 SkBlitter::~SkBlitter() {}
 
 bool SkBlitter::isNullBlitter() const { return false; }
@@ -582,14 +580,14 @@
 #include "SkColorShader.h"
 #include "SkColorPriv.h"
 
-class Sk3DShader : public SkShader {
+class Sk3DShader : public SkShaderBase {
 public:
     Sk3DShader(sk_sp<SkShader> proxy) : fProxy(std::move(proxy)) {}
 
     Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
-        SkShader::Context* proxyContext = nullptr;
+        SkShaderBase::Context* proxyContext = nullptr;
         if (fProxy) {
-            proxyContext = fProxy->makeContext(rec, alloc);
+            proxyContext = as_SB(fProxy)->makeContext(rec, alloc);
             if (!proxyContext) {
                 return nullptr;
             }
@@ -597,11 +595,11 @@
         return alloc->make<Sk3DShaderContext>(*this, rec, proxyContext);
     }
 
-    class Sk3DShaderContext : public SkShader::Context {
+    class Sk3DShaderContext : public Context {
     public:
         // Calls proxyContext's destructor but will NOT free its memory.
         Sk3DShaderContext(const Sk3DShader& shader, const ContextRec& rec,
-                          SkShader::Context* proxyContext)
+                          Context* proxyContext)
             : INHERITED(shader, rec)
             , fMask(nullptr)
             , fProxyContext(proxyContext)
@@ -685,12 +683,12 @@
 
     private:
         // Unowned.
-        const SkMask*       fMask;
+        const SkMask* fMask;
         // Memory is unowned, but we need to call the destructor.
-        SkShader::Context*  fProxyContext;
-        SkPMColor           fPMColor;
+        Context*      fProxyContext;
+        SkPMColor     fPMColor;
 
-        typedef SkShader::Context INHERITED;
+        typedef Context INHERITED;
     };
 
 #ifndef SK_IGNORE_TO_STRING
@@ -699,7 +697,7 @@
 
         if (fProxy) {
             str->append("Proxy: ");
-            fProxy->toString(str);
+            as_SB(fProxy)->toString(str);
         }
 
         this->INHERITED::toString(str);
@@ -718,7 +716,7 @@
 private:
     sk_sp<SkShader> fProxy;
 
-    typedef SkShader INHERITED;
+    typedef SkShaderBase INHERITED;
 };
 
 sk_sp<SkFlattenable> Sk3DShader::CreateProc(SkReadBuffer& buffer) {
@@ -727,7 +725,7 @@
 
 class Sk3DBlitter : public SkBlitter {
 public:
-    Sk3DBlitter(SkBlitter* proxy, SkShader::Context* shaderContext)
+    Sk3DBlitter(SkBlitter* proxy, SkShaderBase::Context* shaderContext)
         : fProxy(proxy)
         , fShaderContext(shaderContext)
     {}
@@ -764,21 +762,45 @@
 
 private:
     // Both pointers are unowned. They will be deleted by SkSmallAllocator.
-    SkBlitter*          fProxy;
-    SkShader::Context*  fShaderContext;
+    SkBlitter*              fProxy;
+    SkShaderBase::Context*  fShaderContext;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkCoreBlitters.h"
 
-SkShader::ContextRec::DstType SkBlitter::PreferredShaderDest(const SkImageInfo& dstInfo) {
-#ifdef SK_FORCE_PM4f_FOR_L32_BLITS
-    return SkShader::ContextRec::kPM4f_DstType;
-#else
+SkShaderBase::ContextRec::DstType SkBlitter::PreferredShaderDest(const SkImageInfo& dstInfo) {
     return (dstInfo.gammaCloseToSRGB() || dstInfo.colorType() == kRGBA_F16_SkColorType)
-            ? SkShader::ContextRec::kPM4f_DstType
-            : SkShader::ContextRec::kPMColor_DstType;
+            ? SkShaderBase::ContextRec::kPM4f_DstType
+            : SkShaderBase::ContextRec::kPMColor_DstType;
+}
+
+// hack for testing, not to be exposed to clients
+bool gSkForceRasterPipelineBlitter;
+
+bool SkBlitter::UseRasterPipelineBlitter(const SkPixmap& device, const SkPaint& paint) {
+    if (gSkForceRasterPipelineBlitter) {
+        return true;
+    }
+#if 0 || defined(SK_FORCE_RASTER_PIPELINE_BLITTER)
+    return true;
+#else
+    // By policy we choose not to handle legacy 8888 with SkRasterPipelineBlitter.
+    if (device.colorSpace()) {
+        return true;
+    }
+    // ... unless the shader is raster pipeline-only.
+    if (paint.getShader() && as_SB(paint.getShader())->isRasterPipelineOnly()) {
+        return true;
+    }
+    #ifndef SK_SUPPORT_LEGACY_RASTERPIPELINE
+    // ... or unless the blend mode is complicated enough.
+    if (paint.getBlendMode() > SkBlendMode::kLastSeparableMode) {
+        return true;
+    }
+    #endif
+    return device.colorType() != kN32_SkColorType;
 #endif
 }
 
@@ -796,7 +818,7 @@
         return alloc->make<SkNullBlitter>();
     }
 
-    SkShader* shader = origPaint.getShader();
+    auto* shader = as_SB(origPaint.getShader());
     SkColorFilter* cf = origPaint.getColorFilter();
     SkBlendMode mode = origPaint.getBlendMode();
     sk_sp<Sk3DShader> shader3D;
@@ -808,7 +830,7 @@
         shader3D = sk_make_sp<Sk3DShader>(sk_ref_sp(shader));
         // we know we haven't initialized lazyPaint yet, so just do it
         paint.writable()->setShader(shader3D);
-        shader = shader3D.get();
+        shader = as_SB(shader3D.get());
     }
 
     if (mode != SkBlendMode::kSrcOver) {
@@ -847,7 +869,9 @@
         return alloc->make<SkA8_Coverage_Blitter>(device, *paint);
     }
 
-    if (SkBlitter* blitter = SkCreateRasterPipelineBlitter(device, *paint, matrix, alloc)) {
+    if (UseRasterPipelineBlitter(device, *paint)) {
+        auto blitter = SkCreateRasterPipelineBlitter(device, *paint, matrix, alloc);
+        SkASSERT(blitter);
         return blitter;
     }
 
@@ -856,7 +880,7 @@
             // xfermodes (and filters) require shaders for our current blitters
             paint.writable()->setShader(SkShader::MakeColorShader(paint->getColor()));
             paint.writable()->setAlpha(0xFF);
-            shader = paint->getShader();
+            shader = as_SB(paint->getShader());
         } else if (cf) {
             // if no shader && no xfermode, we just apply the colorfilter to
             // our color and move on.
@@ -870,7 +894,7 @@
     if (cf) {
         SkASSERT(shader);
         paint.writable()->setShader(shader->makeWithColorFilter(sk_ref_sp(cf)));
-        shader = paint->getShader();
+        shader = as_SB(paint->getShader());
         // blitters should ignore the presence/absence of a filter, since
         // if there is one, the shader will take care of it.
     }
@@ -878,9 +902,9 @@
     /*
      *  We create a SkShader::Context object, and store it on the blitter.
      */
-    SkShader::Context* shaderContext = nullptr;
+    SkShaderBase::Context* shaderContext = nullptr;
     if (shader) {
-        const SkShader::ContextRec rec(*paint, matrix, nullptr,
+        const SkShaderBase::ContextRec rec(*paint, matrix, nullptr,
                                        PreferredShaderDest(device.info()),
                                        device.colorSpace());
         // Try to create the ShaderContext
@@ -893,46 +917,24 @@
 
     SkBlitter*  blitter = nullptr;
     switch (device.colorType()) {
-        case kAlpha_8_SkColorType:
-            SkASSERT(!drawCoverage);  // Handled above.
-            if (shader) {
-                blitter = alloc->make<SkA8_Shader_Blitter>(device, *paint, shaderContext);
-            } else {
-                blitter = alloc->make<SkA8_Blitter>(device, *paint);
-            }
-            break;
-
-        case kRGB_565_SkColorType:
-            blitter = SkBlitter_ChooseD565(device, *paint, shaderContext, alloc);
-            break;
-
         case kN32_SkColorType:
-#ifdef SK_FORCE_PM4f_FOR_L32_BLITS
-            if (true)
-#else
-            if (device.info().gammaCloseToSRGB())
-#endif
-            {
-                blitter = SkBlitter_ARGB32_Create(device, *paint, shaderContext, alloc);
-            } else {
-                if (shader) {
-                        blitter = alloc->make<SkARGB32_Shader_Blitter>(
-                                device, *paint, shaderContext);
-                } else if (paint->getColor() == SK_ColorBLACK) {
-                    blitter = alloc->make<SkARGB32_Black_Blitter>(device, *paint);
-                } else if (paint->getAlpha() == 0xFF) {
-                    blitter = alloc->make<SkARGB32_Opaque_Blitter>(device, *paint);
-                } else {
-                    blitter = alloc->make<SkARGB32_Blitter>(device, *paint);
-                }
-            }
-            break;
+            // sRGB and general color spaces are handled via raster pipeline.
+            SkASSERT(!device.colorSpace());
 
-        case kRGBA_F16_SkColorType:
-            blitter = SkBlitter_F16_Create(device, *paint, shaderContext, alloc);
+            if (shader) {
+                blitter = alloc->make<SkARGB32_Shader_Blitter>(device, *paint, shaderContext);
+            } else if (paint->getColor() == SK_ColorBLACK) {
+                blitter = alloc->make<SkARGB32_Black_Blitter>(device, *paint);
+            } else if (paint->getAlpha() == 0xFF) {
+                blitter = alloc->make<SkARGB32_Opaque_Blitter>(device, *paint);
+            } else {
+                blitter = alloc->make<SkARGB32_Blitter>(device, *paint);
+            }
             break;
 
         default:
+            // should have been handled via raster pipeline.
+            SkASSERT(false);
             break;
     }
 
@@ -953,23 +955,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkZeroShaderContext : public SkShader::Context {
-public:
-    SkZeroShaderContext(const SkShader& shader, const SkShader::ContextRec& rec)
-        // Override rec with the identity matrix, so it is guaranteed to be invertible.
-        : INHERITED(shader, SkShader::ContextRec(*rec.fPaint, SkMatrix::I(), nullptr,
-                                                 rec.fPreferredDstType, rec.fDstColorSpace)) {}
-
-    void shadeSpan(int x, int y, SkPMColor colors[], int count) override {
-        sk_bzero(colors, count * sizeof(SkPMColor));
-    }
-
-private:
-    typedef SkShader::Context INHERITED;
-};
-
 SkShaderBlitter::SkShaderBlitter(const SkPixmap& device, const SkPaint& paint,
-                                 SkShader::Context* shaderContext)
+                                 SkShaderBase::Context* shaderContext)
         : INHERITED(device)
         , fShader(paint.getShader())
         , fShaderContext(shaderContext) {
@@ -978,7 +965,7 @@
 
     fShader->ref();
     fShaderFlags = fShaderContext->getFlags();
-    fConstInY = SkToBool(fShaderFlags & SkShader::kConstInY32_Flag);
+    fConstInY = SkToBool(fShaderFlags & SkShaderBase::kConstInY32_Flag);
 }
 
 SkShaderBlitter::~SkShaderBlitter() {
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
index 1fc4959..6558045 100644
--- a/src/core/SkBlitter.h
+++ b/src/core/SkBlitter.h
@@ -13,7 +13,7 @@
 #include "SkColor.h"
 #include "SkRect.h"
 #include "SkRegion.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 
 class SkArenaAlloc;
 class SkMatrix;
@@ -148,7 +148,9 @@
                                    SkArenaAlloc*);
     ///@}
 
-    static SkShader::ContextRec::DstType PreferredShaderDest(const SkImageInfo&);
+    static SkShaderBase::ContextRec::DstType PreferredShaderDest(const SkImageInfo&);
+
+    static bool UseRasterPipelineBlitter(const SkPixmap&, const SkPaint&);
 
 protected:
     SkAutoMalloc fBlitMemory;
diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
index 1fd4d5f..a82d3f4 100644
--- a/src/core/SkBlitter_A8.cpp
+++ b/src/core/SkBlitter_A8.cpp
@@ -5,350 +5,11 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkCoreBlitters.h"
 #include "SkColorPriv.h"
 #include "SkShader.h"
 #include "SkXfermodePriv.h"
 
-SkA8_Blitter::SkA8_Blitter(const SkPixmap& device, const SkPaint& paint) : INHERITED(device) {
-    fSrcA = paint.getAlpha();
-}
-
-const SkPixmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value) {
-    if (255 == fSrcA) {
-        *value = 255;
-        return &fDevice;
-    }
-    return nullptr;
-}
-
-void SkA8_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(x >= 0 && y >= 0 &&
-             (unsigned)(x + width) <= (unsigned)fDevice.width());
-
-    if (fSrcA == 0) {
-        return;
-    }
-
-    uint8_t* device = fDevice.writable_addr8(x, y);
-
-    if (fSrcA == 255) {
-        memset(device, 0xFF, width);
-    } else {
-        unsigned scale = 256 - SkAlpha255To256(fSrcA);
-        unsigned srcA = fSrcA;
-
-        for (int i = 0; i < width; i++) {
-            device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
-        }
-    }
-}
-
-void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
-                             const int16_t runs[]) {
-    if (fSrcA == 0) {
-        return;
-    }
-
-    uint8_t*    device = fDevice.writable_addr8(x, y);
-    unsigned    srcA = fSrcA;
-
-    for (;;) {
-        int count = runs[0];
-        SkASSERT(count >= 0);
-        if (count == 0) {
-            return;
-        }
-        unsigned aa = antialias[0];
-
-        if (aa == 255 && srcA == 255) {
-            memset(device, 0xFF, count);
-        } else {
-            unsigned sa = SkAlphaMul(srcA, SkAlpha255To256(aa));
-            unsigned scale = 256 - sa;
-
-            for (int i = 0; i < count; i++) {
-                device[i] = SkToU8(sa + SkAlphaMul(device[i], scale));
-            }
-        }
-        runs += count;
-        antialias += count;
-        device += count;
-    }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
-#define solid_8_pixels(mask, dst)           \
-    do {                                    \
-        if (mask & 0x80) dst[0] = 0xFF;     \
-        if (mask & 0x40) dst[1] = 0xFF;     \
-        if (mask & 0x20) dst[2] = 0xFF;     \
-        if (mask & 0x10) dst[3] = 0xFF;     \
-        if (mask & 0x08) dst[4] = 0xFF;     \
-        if (mask & 0x04) dst[5] = 0xFF;     \
-        if (mask & 0x02) dst[6] = 0xFF;     \
-        if (mask & 0x01) dst[7] = 0xFF;     \
-    } while (0)
-
-#define SK_BLITBWMASK_NAME                  SkA8_BlitBW
-#define SK_BLITBWMASK_ARGS
-#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst)
-#define SK_BLITBWMASK_GETADDR               writable_addr8
-#define SK_BLITBWMASK_DEVTYPE               uint8_t
-#include "SkBlitBWMaskTemplate.h"
-
-static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa,
-                                  unsigned dst_scale) {
-    if (bw & 0x80) dst[0] = SkToU8(sa + SkAlphaMul(dst[0], dst_scale));
-    if (bw & 0x40) dst[1] = SkToU8(sa + SkAlphaMul(dst[1], dst_scale));
-    if (bw & 0x20) dst[2] = SkToU8(sa + SkAlphaMul(dst[2], dst_scale));
-    if (bw & 0x10) dst[3] = SkToU8(sa + SkAlphaMul(dst[3], dst_scale));
-    if (bw & 0x08) dst[4] = SkToU8(sa + SkAlphaMul(dst[4], dst_scale));
-    if (bw & 0x04) dst[5] = SkToU8(sa + SkAlphaMul(dst[5], dst_scale));
-    if (bw & 0x02) dst[6] = SkToU8(sa + SkAlphaMul(dst[6], dst_scale));
-    if (bw & 0x01) dst[7] = SkToU8(sa + SkAlphaMul(dst[7], dst_scale));
-}
-
-#define SK_BLITBWMASK_NAME                  SkA8_BlendBW
-#define SK_BLITBWMASK_ARGS                  , U8CPU sa, unsigned dst_scale
-#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sa, dst_scale)
-#define SK_BLITBWMASK_GETADDR               writable_addr8
-#define SK_BLITBWMASK_DEVTYPE               uint8_t
-#include "SkBlitBWMaskTemplate.h"
-
-void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
-    if (fSrcA == 0) {
-        return;
-    }
-
-    if (mask.fFormat == SkMask::kBW_Format) {
-        if (fSrcA == 0xFF) {
-            SkA8_BlitBW(fDevice, mask, clip);
-        } else {
-            SkA8_BlendBW(fDevice, mask, clip, fSrcA,
-                         SkAlpha255To256(255 - fSrcA));
-        }
-        return;
-    }
-
-    int x = clip.fLeft;
-    int y = clip.fTop;
-    int width = clip.width();
-    int height = clip.height();
-    uint8_t* device = fDevice.writable_addr8(x, y);
-    const uint8_t* alpha = mask.getAddr8(x, y);
-    unsigned    srcA = fSrcA;
-
-    while (--height >= 0) {
-        for (int i = width - 1; i >= 0; --i) {
-            unsigned sa;
-            // scale our src by the alpha value
-            {
-                int aa = alpha[i];
-                if (aa == 0) {
-                    continue;
-                }
-                if (aa == 255) {
-                    if (srcA == 255) {
-                        device[i] = 0xFF;
-                        continue;
-                    }
-                    sa = srcA;
-                } else {
-                    sa = SkAlphaMul(srcA, SkAlpha255To256(aa));
-                }
-            }
-
-            int scale = 256 - SkAlpha255To256(sa);
-            device[i] = SkToU8(sa + SkAlphaMul(device[i], scale));
-        }
-        device += fDevice.rowBytes();
-        alpha += mask.fRowBytes;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
-    if (fSrcA == 0) {
-        return;
-    }
-
-    unsigned sa = SkAlphaMul(fSrcA, SkAlpha255To256(alpha));
-    uint8_t* device = fDevice.writable_addr8(x, y);
-    size_t   rowBytes = fDevice.rowBytes();
-
-    if (sa == 0xFF) {
-        for (int i = 0; i < height; i++) {
-            *device = SkToU8(sa);
-            device += rowBytes;
-        }
-    } else {
-        unsigned scale = 256 - SkAlpha255To256(sa);
-
-        for (int i = 0; i < height; i++) {
-            *device = SkToU8(sa + SkAlphaMul(*device, scale));
-            device += rowBytes;
-        }
-    }
-}
-
-void SkA8_Blitter::blitRect(int x, int y, int width, int height) {
-    SkASSERT(x >= 0 && y >= 0 &&
-             (unsigned)(x + width) <= (unsigned)fDevice.width() &&
-             (unsigned)(y + height) <= (unsigned)fDevice.height());
-
-    if (fSrcA == 0) {
-        return;
-    }
-
-    uint8_t*    device = fDevice.writable_addr8(x, y);
-    unsigned    srcA = fSrcA;
-
-    if (srcA == 255) {
-        while (--height >= 0) {
-            memset(device, 0xFF, width);
-            device += fDevice.rowBytes();
-        }
-    } else {
-        unsigned scale = 256 - SkAlpha255To256(srcA);
-
-        while (--height >= 0) {
-            for (int i = 0; i < width; i++) {
-                device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
-            }
-            device += fDevice.rowBytes();
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////
-
-SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkPixmap& device, const SkPaint& paint,
-                                         SkShader::Context* shaderContext)
-    : INHERITED(device, paint, shaderContext)
-{
-    fXfermode = SkXfermode::Peek(paint.getBlendMode());
-    SkASSERT(!fXfermode || fShaderContext);
-
-    int width = device.width();
-    fBuffer = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * (width + (SkAlign4(width) >> 2)));
-    fAAExpand = (uint8_t*)(fBuffer + width);
-}
-
-SkA8_Shader_Blitter::~SkA8_Shader_Blitter() {
-    sk_free(fBuffer);
-}
-
-void SkA8_Shader_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(x >= 0 && y >= 0 &&
-             (unsigned)(x + width) <= (unsigned)fDevice.width());
-
-    uint8_t* device = fDevice.writable_addr8(x, y);
-    SkShader::Context* shaderContext = fShaderContext;
-
-    if ((shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag) && !fXfermode) {
-        memset(device, 0xFF, width);
-    } else {
-        SkPMColor*  span = fBuffer;
-
-        shaderContext->shadeSpan(x, y, span, width);
-        if (fXfermode) {
-            fXfermode->xferA8(device, span, width, nullptr);
-        } else {
-            for (int i = width - 1; i >= 0; --i) {
-                unsigned    srcA = SkGetPackedA32(span[i]);
-                unsigned    scale = 256 - SkAlpha255To256(srcA);
-
-                device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
-            }
-        }
-    }
-}
-
-static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa) {
-    SkASSERT((unsigned)aa <= 255);
-
-    int src_scale = SkAlpha255To256(aa);
-    int sa = SkGetPackedA32(src);
-    int dst_scale = SkAlphaMulInv256(sa, src_scale);
-
-    return SkToU8((sa * src_scale + da * dst_scale) >> 8);
-}
-
-void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
-                                    const int16_t runs[]) {
-    SkShader::Context* shaderContext = fShaderContext;
-    SkXfermode*        mode = fXfermode;
-    uint8_t*           aaExpand = fAAExpand;
-    SkPMColor*         span = fBuffer;
-    uint8_t*           device = fDevice.writable_addr8(x, y);
-    int                opaque = shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag;
-
-    for (;;) {
-        int count = *runs;
-        if (count == 0) {
-            break;
-        }
-        int aa = *antialias;
-        if (aa) {
-            if (opaque && aa == 255 && mode == nullptr) {
-                memset(device, 0xFF, count);
-            } else {
-                shaderContext->shadeSpan(x, y, span, count);
-                if (mode) {
-                    memset(aaExpand, aa, count);
-                    mode->xferA8(device, span, count, aaExpand);
-                } else {
-                    for (int i = count - 1; i >= 0; --i) {
-                        device[i] = aa_blend8(span[i], device[i], aa);
-                    }
-                }
-            }
-        }
-        device += count;
-        runs += count;
-        antialias += count;
-        x += count;
-    }
-}
-
-void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
-    if (mask.fFormat == SkMask::kBW_Format) {
-        this->INHERITED::blitMask(mask, clip);
-        return;
-    }
-
-    int x = clip.fLeft;
-    int y = clip.fTop;
-    int width = clip.width();
-    int height = clip.height();
-    uint8_t* device = fDevice.writable_addr8(x, y);
-    const uint8_t* alpha = mask.getAddr8(x, y);
-    SkShader::Context* shaderContext = fShaderContext;
-
-    SkPMColor*  span = fBuffer;
-
-    while (--height >= 0) {
-        shaderContext->shadeSpan(x, y, span, width);
-        if (fXfermode) {
-            fXfermode->xferA8(device, span, width, alpha);
-        } else {
-            for (int i = width - 1; i >= 0; --i) {
-                device[i] = aa_blend8(span[i], device[i], alpha[i]);
-            }
-        }
-
-        y += 1;
-        device += fDevice.rowBytes();
-        alpha += mask.fRowBytes;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 SkA8_Coverage_Blitter::SkA8_Coverage_Blitter(const SkPixmap& device,
                              const SkPaint& paint) : SkRasterBlitter(device) {
     SkASSERT(nullptr == paint.getShader());
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
index 4478b2b..aef1044 100644
--- a/src/core/SkBlitter_ARGB32.cpp
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -330,7 +330,7 @@
 }
 
 SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkPixmap& device,
-        const SkPaint& paint, SkShader::Context* shaderContext)
+        const SkPaint& paint, SkShaderBase::Context* shaderContext)
     : INHERITED(device, paint, shaderContext)
 {
     fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
@@ -338,7 +338,7 @@
     fXfermode = SkXfermode::Peek(paint.getBlendMode());
 
     int flags = 0;
-    if (!(shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+    if (!(shaderContext->getFlags() & SkShaderBase::kOpaqueAlpha_Flag)) {
         flags |= SkBlitRow::kSrcPixelAlpha_Flag32;
     }
     // we call this on the output from the shader
@@ -348,7 +348,7 @@
 
     fShadeDirectlyIntoDevice = false;
     if (fXfermode == nullptr) {
-        if (shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+        if (shaderContext->getFlags() & SkShaderBase::kOpaqueAlpha_Flag) {
             fShadeDirectlyIntoDevice = true;
         }
     } else {
@@ -361,7 +361,7 @@
         }
     }
 
-    fConstInY = SkToBool(shaderContext->getFlags() & SkShader::kConstInY32_Flag);
+    fConstInY = SkToBool(shaderContext->getFlags() & SkShaderBase::kConstInY32_Flag);
 }
 
 SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
@@ -390,10 +390,10 @@
     SkASSERT(x >= 0 && y >= 0 &&
              x + width <= fDevice.width() && y + height <= fDevice.height());
 
-    uint32_t*          device = fDevice.writable_addr32(x, y);
-    size_t             deviceRB = fDevice.rowBytes();
-    SkShader::Context* shaderContext = fShaderContext;
-    SkPMColor*         span = fBuffer;
+    uint32_t*  device = fDevice.writable_addr32(x, y);
+    size_t     deviceRB = fDevice.rowBytes();
+    auto*      shaderContext = fShaderContext;
+    SkPMColor* span = fBuffer;
 
     if (fConstInY) {
         if (fShadeDirectlyIntoDevice) {
@@ -427,7 +427,7 @@
 
     if (fShadeDirectlyIntoDevice) {
         void* ctx;
-        SkShader::Context::ShadeProc shadeProc = shaderContext->asAShadeProc(&ctx);
+        auto shadeProc = shaderContext->asAShadeProc(&ctx);
         if (shadeProc) {
             do {
                 shadeProc(ctx, x, y, device, width);
@@ -464,9 +464,9 @@
 
 void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
                                         const int16_t runs[]) {
-    SkPMColor*         span = fBuffer;
-    uint32_t*          device = fDevice.writable_addr32(x, y);
-    SkShader::Context* shaderContext = fShaderContext;
+    SkPMColor* span = fBuffer;
+    uint32_t*  device = fDevice.writable_addr32(x, y);
+    auto*      shaderContext = fShaderContext;
 
     if (fXfermode && !fShadeDirectlyIntoDevice) {
         for (;;) {
@@ -493,7 +493,7 @@
             x += count;
         }
     } else if (fShadeDirectlyIntoDevice ||
-               (shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+               (shaderContext->getFlags() & SkShaderBase::kOpaqueAlpha_Flag)) {
         for (;;) {
             int count = *runs;
             if (count <= 0) {
@@ -546,11 +546,11 @@
 
     SkASSERT(mask.fBounds.contains(clip));
 
-    SkShader::Context*  shaderContext = fShaderContext;
+    auto* shaderContext = fShaderContext;
     SkBlitMask::RowProc proc = nullptr;
     if (!fXfermode) {
         unsigned flags = 0;
-        if (shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+        if (shaderContext->getFlags() & SkShaderBase::kOpaqueAlpha_Flag) {
             flags |= SkBlitMask::kSrcIsOpaque_RowFlag;
         }
         proc = SkBlitMask::RowFactory(kN32_SkColorType, mask.fFormat,
@@ -597,9 +597,9 @@
 void SkARGB32_Shader_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
     SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height());
 
-    uint32_t*          device = fDevice.writable_addr32(x, y);
-    size_t             deviceRB = fDevice.rowBytes();
-    SkShader::Context* shaderContext = fShaderContext;
+    uint32_t* device = fDevice.writable_addr32(x, y);
+    size_t    deviceRB = fDevice.rowBytes();
+    auto*     shaderContext = fShaderContext;
 
     if (fConstInY) {
         SkPMColor c;
@@ -637,7 +637,7 @@
 
     if (fShadeDirectlyIntoDevice) {
         void* ctx;
-        SkShader::Context::ShadeProc shadeProc = shaderContext->asAShadeProc(&ctx);
+        auto shadeProc = shaderContext->asAShadeProc(&ctx);
         if (255 == alpha) {
             if (shadeProc) {
                 do {
diff --git a/src/core/SkBlitter_PM4f.cpp b/src/core/SkBlitter_PM4f.cpp
deleted file mode 100644
index 61105ce..0000000
--- a/src/core/SkBlitter_PM4f.cpp
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * 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 "SkCoreBlitters.h"
-
-#include "SkArenaAlloc.h"
-#include "SkColorPriv.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermodePriv.h"
-#include "SkBlitMask.h"
-#include "SkTemplates.h"
-#include "SkPM4f.h"
-
-template <typename State> class SkState_Blitter : public SkRasterBlitter {
-    typedef SkRasterBlitter INHERITED;
-    State fState;
-
-public:
-    SkState_Blitter(const SkPixmap& device, const SkPaint& paint)
-        : INHERITED(device)
-        , fState(device.info(), paint, nullptr)
-    {}
-
-    void blitH(int x, int y, int width) override {
-        SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
-
-        fState.fProc1(fState.fMode, State::WritableAddr(fDevice, x, y),
-                      &fState.fPM4f, width, nullptr);
-    }
-
-    void blitV(int x, int y, int height, SkAlpha alpha) override {
-        SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height());
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        size_t                 deviceRB = fDevice.rowBytes();
-
-        for (int i = 0; i < height; ++i) {
-            fState.fProc1(fState.fMode, device, &fState.fPM4f, 1, &alpha);
-            device = (typename State::DstType*)((char*)device + deviceRB);
-        }
-    }
-
-    void blitRect(int x, int y, int width, int height) override {
-        SkASSERT(x >= 0 && y >= 0 &&
-                 x + width <= fDevice.width() && y + height <= fDevice.height());
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        size_t        deviceRB = fDevice.rowBytes();
-
-        do {
-            fState.fProc1(fState.fMode, device, &fState.fPM4f, width, nullptr);
-            y += 1;
-            device = (typename State::DstType*)((char*)device + deviceRB);
-        } while (--height > 0);
-    }
-
-    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override {
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-
-        for (;;) {
-            int count = *runs;
-            if (count <= 0) {
-                break;
-            }
-            int aa = *antialias;
-            if (aa) {
-                if (aa == 255) {
-                    fState.fProc1(fState.fMode, device, &fState.fPM4f, count, nullptr);
-                } else {
-                    for (int i = 0; i < count; ++i) {
-                        fState.fProc1(fState.fMode, &device[i], &fState.fPM4f, 1, antialias);
-                    }
-                }
-            }
-            device += count;
-            runs += count;
-            antialias += count;
-            x += count;
-        }
-    }
-
-    void blitLCDMask(const SkMask& mask, const SkIRect& clip) {
-        auto proc = fState.getLCDProc(SkXfermode::kSrcIsSingle_LCDFlag);
-
-        const int x = clip.fLeft;
-        const int width = clip.width();
-        const int y = clip.fTop;
-        const int height = clip.height();
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        const size_t dstRB = fDevice.rowBytes();
-        const uint16_t* maskRow = (const uint16_t*)mask.getAddr(x, y);
-        const size_t maskRB = mask.fRowBytes;
-
-        for (int i = 0; i < height; ++i) {
-            proc(device, &fState.fPM4f, width, maskRow);
-            device = (typename State::DstType*)((char*)device + dstRB);
-            maskRow = (const uint16_t*)((const char*)maskRow + maskRB);
-        }
-    }
-
-    void blitMask(const SkMask& mask, const SkIRect& clip) override {
-        if (SkMask::kLCD16_Format == mask.fFormat) {
-            this->blitLCDMask(mask, clip);
-            return;
-        }
-        if (SkMask::kA8_Format != mask.fFormat) {
-            this->INHERITED::blitMask(mask, clip);
-            return;
-        }
-
-        SkASSERT(mask.fBounds.contains(clip));
-
-        const int x = clip.fLeft;
-        const int width = clip.width();
-        const int y = clip.fTop;
-        const int height = clip.height();
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        const size_t dstRB = fDevice.rowBytes();
-        const uint8_t* maskRow = (const uint8_t*)mask.getAddr(x, y);
-        const size_t maskRB = mask.fRowBytes;
-
-        for (int i = 0; i < height; ++i) {
-            fState.fProc1(fState.fMode, device, &fState.fPM4f, width, maskRow);
-            device = (typename State::DstType*)((char*)device + dstRB);
-            maskRow += maskRB;
-        }
-    }
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-template <typename State> class SkState_Shader_Blitter : public SkShaderBlitter {
-public:
-    SkState_Shader_Blitter(const SkPixmap& device, const SkPaint& paint,
-                           const SkShader::Context::BlitState& bstate)
-        : INHERITED(device, paint, bstate.fCtx)
-        , fState(device.info(), paint, bstate.fCtx)
-        , fBState(bstate)
-        , fBlitBW(bstate.fBlitBW)
-        , fBlitAA(bstate.fBlitAA)
-    {}
-
-    void blitH(int x, int y, int width) override {
-        SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
-
-        if (fBlitBW) {
-            fBlitBW(&fBState, x, y, fDevice, width);
-            return;
-        }
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-        fState.fProcN(fState.fMode, device, fState.fBuffer, width, nullptr);
-    }
-
-    void blitV(int x, int y, int height, SkAlpha alpha) override {
-        SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height());
-
-        if (fBlitAA) {
-            for (const int bottom = y + height; y < bottom; ++y) {
-                fBlitAA(&fBState, x, y, fDevice, 1, &alpha);
-            }
-            return;
-        }
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        size_t                   deviceRB = fDevice.rowBytes();
-
-        if (fConstInY) {
-            fShaderContext->shadeSpan4f(x, y, fState.fBuffer, 1);
-        }
-        for (const int bottom = y + height; y < bottom; ++y) {
-            if (!fConstInY) {
-                fShaderContext->shadeSpan4f(x, y, fState.fBuffer, 1);
-            }
-            fState.fProcN(fState.fMode, device, fState.fBuffer, 1, &alpha);
-            device = (typename State::DstType*)((char*)device + deviceRB);
-        }
-    }
-
-    void blitRect(int x, int y, int width, int height) override {
-        SkASSERT(x >= 0 && y >= 0 &&
-                 x + width <= fDevice.width() && y + height <= fDevice.height());
-
-        if (fBlitBW) {
-            for (const int bottom = y + height; y < bottom; ++y) {
-                fBlitBW(&fBState, x, y, fDevice, width);
-            }
-            return;
-        }
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        size_t                   deviceRB = fDevice.rowBytes();
-
-        if (fConstInY) {
-            fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-        }
-        for (const int bottom = y + height; y < bottom; ++y) {
-            if (!fConstInY) {
-                fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-            }
-            fState.fProcN(fState.fMode, device, fState.fBuffer, width, nullptr);
-            device = (typename State::DstType*)((char*)device + deviceRB);
-        }
-    }
-
-    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override {
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-
-        for (;;) {
-            int count = *runs;
-            if (count <= 0) {
-                break;
-            }
-            int aa = *antialias;
-            if (aa) {
-                if (fBlitBW && (aa == 255)) {
-                    fBlitBW(&fBState, x, y, fDevice, count);
-                } else {
-                    fShaderContext->shadeSpan4f(x, y, fState.fBuffer, count);
-                    if (aa == 255) {
-                        fState.fProcN(fState.fMode, device, fState.fBuffer, count, nullptr);
-                    } else {
-                        for (int i = 0; i < count; ++i) {
-                            fState.fProcN(fState.fMode, &device[i], &fState.fBuffer[i], 1, antialias);
-                        }
-                    }
-                }
-            }
-            device += count;
-            runs += count;
-            antialias += count;
-            x += count;
-        }
-    }
-
-    void blitLCDMask(const SkMask& mask, const SkIRect& clip) {
-        auto proc = fState.getLCDProc(0);
-
-        const int x = clip.fLeft;
-        const int width = clip.width();
-        int y = clip.fTop;
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        const size_t deviceRB = fDevice.rowBytes();
-        const uint16_t* maskRow = (const uint16_t*)mask.getAddr(x, y);
-        const size_t maskRB = mask.fRowBytes;
-
-        if (fConstInY) {
-            fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-        }
-        for (; y < clip.fBottom; ++y) {
-            if (!fConstInY) {
-                fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-            }
-            proc(device, fState.fBuffer, width, maskRow);
-            device = (typename State::DstType*)((char*)device + deviceRB);
-            maskRow = (const uint16_t*)((const char*)maskRow + maskRB);
-        }
-    }
-
-    void blitMask(const SkMask& mask, const SkIRect& clip) override {
-        if (SkMask::kLCD16_Format == mask.fFormat) {
-            this->blitLCDMask(mask, clip);
-            return;
-        }
-        if (SkMask::kA8_Format != mask.fFormat) {
-            this->INHERITED::blitMask(mask, clip);
-            return;
-        }
-
-        SkASSERT(mask.fBounds.contains(clip));
-
-        const int x = clip.fLeft;
-        const int width = clip.width();
-        int y = clip.fTop;
-        const uint8_t* maskRow = (const uint8_t*)mask.getAddr(x, y);
-        const size_t maskRB = mask.fRowBytes;
-
-        if (fBlitAA) {
-            for (; y < clip.fBottom; ++y) {
-                fBlitAA(&fBState, x, y, fDevice, width, maskRow);
-                maskRow += maskRB;
-            }
-            return;
-        }
-
-        typename State::DstType* device = State::WritableAddr(fDevice, x, y);
-        const size_t deviceRB = fDevice.rowBytes();
-
-        if (fConstInY) {
-            fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-        }
-        for (; y < clip.fBottom; ++y) {
-            if (!fConstInY) {
-                fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width);
-            }
-            fState.fProcN(fState.fMode, device, fState.fBuffer, width, maskRow);
-            device = (typename State::DstType*)((char*)device + deviceRB);
-            maskRow += maskRB;
-        }
-    }
-
-protected:
-    State                        fState;
-    SkShader::Context::BlitState fBState;
-    SkShader::Context::BlitBW    fBlitBW;
-    SkShader::Context::BlitAA    fBlitAA;
-
-    typedef SkShaderBlitter INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static bool is_opaque(const SkPaint& paint, const SkShader::Context* shaderContext) {
-    return shaderContext ? SkToBool(shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag)
-    : 0xFF == paint.getAlpha();
-}
-
-struct State4f {
-    State4f(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext) {
-        fMode = paint.getBlendMode();
-        if (shaderContext) {
-            fBuffer.reset(info.width());
-        } else {
-            fPM4f = SkColor4f::FromColor(paint.getColor()).premul();
-        }
-        fFlags = 0;
-    }
-
-    SkPM4f                  fPM4f;
-    SkAutoTMalloc<SkPM4f>   fBuffer;
-    uint32_t                fFlags;
-    SkBlendMode             fMode;
-
-    SkShader::Context::BlitState fBState;
-};
-
-struct State32 : State4f {
-    typedef uint32_t    DstType;
-
-    SkXfermode::D32Proc fProc1;
-    SkXfermode::D32Proc fProcN;
-
-    State32(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext)
-        : State4f(info, paint, shaderContext)
-    {
-        if (is_opaque(paint, shaderContext)) {
-            fFlags |= SkXfermode::kSrcIsOpaque_D32Flag;
-        }
-        if (info.gammaCloseToSRGB()) {
-            fFlags |= SkXfermode::kDstIsSRGB_D32Flag;
-        }
-        fProc1 = SkXfermode::GetD32Proc(fMode, fFlags | SkXfermode::kSrcIsSingle_D32Flag);
-        fProcN = SkXfermode::GetD32Proc(fMode, fFlags);
-    }
-
-    SkXfermode::LCD32Proc getLCDProc(uint32_t oneOrManyFlag) const {
-        uint32_t flags = fFlags & 1;
-        if (fFlags & SkXfermode::kDstIsSRGB_D32Flag) {
-            flags |= SkXfermode::kDstIsSRGB_LCDFlag;
-        }
-        return SkXfermode::GetLCD32Proc(flags | oneOrManyFlag);
-    }
-
-    static DstType* WritableAddr(const SkPixmap& device, int x, int y) {
-        return device.writable_addr32(x, y);
-    }
-};
-
-struct StateF16 : State4f {
-    typedef uint64_t    DstType;
-
-    SkXfermode::F16Proc fProc1;
-    SkXfermode::F16Proc fProcN;
-
-    StateF16(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext)
-        : State4f(info, paint, shaderContext)
-    {
-        if (is_opaque(paint, shaderContext)) {
-            fFlags |= SkXfermode::kSrcIsOpaque_F16Flag;
-        }
-        SkASSERT(kRGBA_F16_SkColorType == info.colorType());
-        fProc1 = SkXfermode::GetF16Proc(fMode, fFlags | SkXfermode::kSrcIsSingle_F16Flag);
-        fProcN = SkXfermode::GetF16Proc(fMode, fFlags);
-    }
-
-    SkXfermode::LCDF16Proc getLCDProc(uint32_t oneOrManyFlag) const {
-        uint32_t flags = fFlags & 1;
-        return SkXfermode::GetLCDF16Proc(flags | oneOrManyFlag);
-    }
-
-    static DstType* WritableAddr(const SkPixmap& device, int x, int y) {
-        return device.writable_addr64(x, y);
-    }
-};
-
-template <typename State> SkBlitter* create(const SkPixmap& device, const SkPaint& paint,
-                                            SkShader::Context* shaderContext,
-                                            SkArenaAlloc* alloc) {
-    SkASSERT(alloc != nullptr);
-
-    if (shaderContext) {
-        SkShader::Context::BlitState bstate;
-        sk_bzero(&bstate, sizeof(bstate));
-        bstate.fCtx = shaderContext;
-        bstate.fMode = paint.getBlendMode();
-
-        (void)shaderContext->chooseBlitProcs(device.info(), &bstate);
-        return alloc->make<SkState_Shader_Blitter<State>>(device, paint, bstate);
-    } else {
-        SkColor color = paint.getColor();
-        if (0 == SkColorGetA(color)) {
-            return nullptr;
-        }
-        return alloc->make<SkState_Blitter<State>>(device, paint);
-    }
-}
-
-SkBlitter* SkBlitter_ARGB32_Create(const SkPixmap& device, const SkPaint& paint,
-                                   SkShader::Context* shaderContext,
-                                   SkArenaAlloc* alloc) {
-    return create<State32>(device, paint, shaderContext, alloc);
-}
-
-SkBlitter* SkBlitter_F16_Create(const SkPixmap& device, const SkPaint& paint,
-                                SkShader::Context* shaderContext,
-                                SkArenaAlloc* alloc) {
-    return create<StateF16>(device, paint, shaderContext, alloc);
-}
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
deleted file mode 100644
index 6330a39..0000000
--- a/src/core/SkBlitter_RGB16.cpp
+++ /dev/null
@@ -1,919 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkArenaAlloc.h"
-#include "SkBlitRow.h"
-#include "SkCoreBlitters.h"
-#include "SkColorPriv.h"
-#include "SkDither.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkUtilsArm.h"
-#include "SkXfermodePriv.h"
-
-#if defined(SK_ARM_HAS_NEON) && defined(SK_CPU_LENDIAN)
-    #include <arm_neon.h>
-extern void SkRGB16BlitterBlitV_neon(uint16_t* device,
-                                     int height,
-                                     size_t deviceRB,
-                                     unsigned scale,
-                                     uint32_t src32);
-#else
-    // if we don't have neon, then our black blitter is worth the extra code
-    #define USE_BLACK_BLITTER
-#endif
-
-void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
-                        int count) {
-    if (count > 0) {
-        // see if we need to write one short before we can cast to an 4byte ptr
-        // (we do this subtract rather than (unsigned)dst so we don't get warnings
-        //  on 64bit machines)
-        if (((char*)dst - (char*)0) & 2) {
-            *dst++ = value;
-            count -= 1;
-            SkTSwap(value, other);
-        }
-
-        // fast way to set [value,other] pairs
-#ifdef SK_CPU_BENDIAN
-        sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
-#else
-        sk_memset32((uint32_t*)dst, (other << 16) | value, count >> 1);
-#endif
-
-        if (count & 1) {
-            dst[count - 1] = value;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkRGB16_Blitter : public SkRasterBlitter {
-public:
-    SkRGB16_Blitter(const SkPixmap& device, const SkPaint& paint);
-    void blitH(int x, int y, int width) override;
-    virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) override;
-    void blitV(int x, int y, int height, SkAlpha alpha) override;
-    void blitRect(int x, int y, int width, int height) override;
-    void blitMask(const SkMask&, const SkIRect&) override;
-    const SkPixmap* justAnOpaqueColor(uint32_t*) override;
-
-protected:
-    SkPMColor   fSrcColor32;
-    uint32_t    fExpandedRaw16;
-    unsigned    fScale;
-    uint16_t    fColor16;       // already scaled by fScale
-    uint16_t    fRawColor16;    // unscaled
-    uint16_t    fRawDither16;   // unscaled
-    SkBool8     fDoDither;
-
-    SkBlitRow::ColorProc16 fColorProc16;
-
-    // illegal
-    SkRGB16_Blitter& operator=(const SkRGB16_Blitter&);
-
-    typedef SkRasterBlitter INHERITED;
-};
-
-class SkRGB16_Opaque_Blitter : public SkRGB16_Blitter {
-public:
-    SkRGB16_Opaque_Blitter(const SkPixmap& device, const SkPaint& paint);
-    void blitH(int x, int y, int width) override;
-    void blitAntiH(int x, int y, const SkAlpha* antialias, const int16_t* runs) override;
-    void blitV(int x, int y, int height, SkAlpha alpha) override;
-    void blitRect(int x, int y, int width, int height) override;
-    void blitMask(const SkMask&, const SkIRect&) override;
-
-private:
-    typedef SkRGB16_Blitter INHERITED;
-};
-
-#ifdef USE_BLACK_BLITTER
-class SkRGB16_Black_Blitter : public SkRGB16_Opaque_Blitter {
-public:
-    SkRGB16_Black_Blitter(const SkPixmap& device, const SkPaint& paint);
-    void blitMask(const SkMask&, const SkIRect&) override;
-    void blitAntiH(int x, int y, const SkAlpha* antialias, const int16_t* runs) override;
-
-private:
-    typedef SkRGB16_Opaque_Blitter INHERITED;
-};
-#endif
-
-class SkRGB16_Shader_Blitter : public SkShaderBlitter {
-public:
-    SkRGB16_Shader_Blitter(const SkPixmap& device, const SkPaint& paint,
-                           SkShader::Context* shaderContext);
-    ~SkRGB16_Shader_Blitter() override;
-    void blitH(int x, int y, int width) override;
-    virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) override;
-    void blitRect(int x, int y, int width, int height) override;
-
-protected:
-    SkPMColor*          fBuffer;
-    SkBlitRow::Proc16   fOpaqueProc;
-    SkBlitRow::Proc16   fAlphaProc;
-
-private:
-    // illegal
-    SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&);
-
-    typedef SkShaderBlitter INHERITED;
-};
-
-class SkRGB16_Shader_Xfermode_Blitter : public SkShaderBlitter {
-public:
-    SkRGB16_Shader_Xfermode_Blitter(const SkPixmap& device, const SkPaint& paint,
-                                    SkShader::Context* shaderContext);
-    ~SkRGB16_Shader_Xfermode_Blitter() override;
-    void blitH(int x, int y, int width) override;
-    virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) override;
-
-private:
-    SkXfermode* fXfermode;
-    SkPMColor*  fBuffer;
-    uint8_t*    fAAExpand;
-
-    // illegal
-    SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&);
-
-    typedef SkShaderBlitter INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-#ifdef USE_BLACK_BLITTER
-SkRGB16_Black_Blitter::SkRGB16_Black_Blitter(const SkPixmap& device, const SkPaint& paint)
-    : INHERITED(device, paint) {
-    SkASSERT(paint.getShader() == nullptr);
-    SkASSERT(paint.getColorFilter() == nullptr);
-    SkASSERT(paint.isSrcOver());
-    SkASSERT(paint.getColor() == SK_ColorBLACK);
-}
-
-#if 1
-#define black_8_pixels(mask, dst)       \
-    do {                                \
-        if (mask & 0x80) dst[0] = 0;    \
-        if (mask & 0x40) dst[1] = 0;    \
-        if (mask & 0x20) dst[2] = 0;    \
-        if (mask & 0x10) dst[3] = 0;    \
-        if (mask & 0x08) dst[4] = 0;    \
-        if (mask & 0x04) dst[5] = 0;    \
-        if (mask & 0x02) dst[6] = 0;    \
-        if (mask & 0x01) dst[7] = 0;    \
-    } while (0)
-#else
-static inline black_8_pixels(U8CPU mask, uint16_t dst[])
-{
-    if (mask & 0x80) dst[0] = 0;
-    if (mask & 0x40) dst[1] = 0;
-    if (mask & 0x20) dst[2] = 0;
-    if (mask & 0x10) dst[3] = 0;
-    if (mask & 0x08) dst[4] = 0;
-    if (mask & 0x04) dst[5] = 0;
-    if (mask & 0x02) dst[6] = 0;
-    if (mask & 0x01) dst[7] = 0;
-}
-#endif
-
-#define SK_BLITBWMASK_NAME                  SkRGB16_Black_BlitBW
-#define SK_BLITBWMASK_ARGS
-#define SK_BLITBWMASK_BLIT8(mask, dst)      black_8_pixels(mask, dst)
-#define SK_BLITBWMASK_GETADDR               writable_addr16
-#define SK_BLITBWMASK_DEVTYPE               uint16_t
-#include "SkBlitBWMaskTemplate.h"
-
-void SkRGB16_Black_Blitter::blitMask(const SkMask& mask,
-                                     const SkIRect& clip) {
-    if (mask.fFormat == SkMask::kBW_Format) {
-        SkRGB16_Black_BlitBW(fDevice, mask, clip);
-    } else {
-        uint16_t* SK_RESTRICT device = fDevice.writable_addr16(clip.fLeft, clip.fTop);
-        const uint8_t* SK_RESTRICT alpha = mask.getAddr8(clip.fLeft, clip.fTop);
-        unsigned width = clip.width();
-        unsigned height = clip.height();
-        size_t deviceRB = fDevice.rowBytes() - (width << 1);
-        unsigned maskRB = mask.fRowBytes - width;
-
-        SkASSERT((int)height > 0);
-        SkASSERT((int)width > 0);
-        SkASSERT((int)deviceRB >= 0);
-        SkASSERT((int)maskRB >= 0);
-
-        do {
-            unsigned w = width;
-            do {
-                unsigned aa = *alpha++;
-                *device = SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa));
-                device += 1;
-            } while (--w != 0);
-            device = (uint16_t*)((char*)device + deviceRB);
-            alpha += maskRB;
-        } while (--height != 0);
-    }
-}
-
-void SkRGB16_Black_Blitter::blitAntiH(int x, int y,
-                                      const SkAlpha* SK_RESTRICT antialias,
-                                      const int16_t* SK_RESTRICT runs) {
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-
-    for (;;) {
-        int count = runs[0];
-        SkASSERT(count >= 0);
-        if (count <= 0) {
-            return;
-        }
-        runs += count;
-
-        unsigned aa = antialias[0];
-        antialias += count;
-        if (aa) {
-            if (aa == 255) {
-                memset(device, 0, count << 1);
-            } else {
-                aa = SkAlpha255To256(255 - aa);
-                do {
-                    *device = SkAlphaMulRGB16(*device, aa);
-                    device += 1;
-                } while (--count != 0);
-                continue;
-            }
-        }
-        device += count;
-    }
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-SkRGB16_Opaque_Blitter::SkRGB16_Opaque_Blitter(const SkPixmap& device, const SkPaint& paint)
-    : INHERITED(device, paint) {}
-
-void SkRGB16_Opaque_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(width > 0);
-    SkASSERT(x + width <= fDevice.width());
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    uint16_t srcColor = fColor16;
-
-    SkASSERT(fRawColor16 == srcColor);
-    if (fDoDither) {
-        uint16_t ditherColor = fRawDither16;
-        if ((x ^ y) & 1) {
-            SkTSwap(ditherColor, srcColor);
-        }
-        sk_dither_memset16(device, srcColor, ditherColor, width);
-    } else {
-        sk_memset16(device, srcColor, width);
-    }
-}
-
-// return 1 or 0 from a bool
-static inline int Bool2Int(int value) {
-    return !!value;
-}
-
-void SkRGB16_Opaque_Blitter::blitAntiH(int x, int y,
-                                       const SkAlpha* SK_RESTRICT antialias,
-                                       const int16_t* SK_RESTRICT runs) {
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    uint16_t    srcColor = fRawColor16;
-    uint32_t    srcExpanded = fExpandedRaw16;
-    int         ditherInt = Bool2Int(fDoDither);
-    uint16_t    ditherColor = fRawDither16;
-    // if we have no dithering, this will always fail
-    if ((x ^ y) & ditherInt) {
-        SkTSwap(ditherColor, srcColor);
-    }
-    for (;;) {
-        int count = runs[0];
-        SkASSERT(count >= 0);
-        if (count <= 0) {
-            return;
-        }
-        runs += count;
-
-        unsigned aa = antialias[0];
-        antialias += count;
-        if (aa) {
-            if (aa == 255) {
-                if (ditherInt) {
-                    sk_dither_memset16(device, srcColor,
-                                       ditherColor, count);
-                } else {
-                    sk_memset16(device, srcColor, count);
-                }
-            } else {
-                // TODO: respect fDoDither
-                unsigned scale5 = SkAlpha255To256(aa) >> 3;
-                uint32_t src32 = srcExpanded * scale5;
-                scale5 = 32 - scale5; // now we can use it on the device
-                int n = count;
-                do {
-                    uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
-                    *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
-                } while (--n != 0);
-                goto DONE;
-            }
-        }
-        device += count;
-
-    DONE:
-        // if we have no dithering, this will always fail
-        if (count & ditherInt) {
-            SkTSwap(ditherColor, srcColor);
-        }
-    }
-}
-
-#define solid_8_pixels(mask, dst, color)    \
-    do {                                    \
-        if (mask & 0x80) dst[0] = color;    \
-        if (mask & 0x40) dst[1] = color;    \
-        if (mask & 0x20) dst[2] = color;    \
-        if (mask & 0x10) dst[3] = color;    \
-        if (mask & 0x08) dst[4] = color;    \
-        if (mask & 0x04) dst[5] = color;    \
-        if (mask & 0x02) dst[6] = color;    \
-        if (mask & 0x01) dst[7] = color;    \
-    } while (0)
-
-#define SK_BLITBWMASK_NAME                  SkRGB16_BlitBW
-#define SK_BLITBWMASK_ARGS                  , uint16_t color
-#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
-#define SK_BLITBWMASK_GETADDR               writable_addr16
-#define SK_BLITBWMASK_DEVTYPE               uint16_t
-#include "SkBlitBWMaskTemplate.h"
-
-static U16CPU blend_compact(uint32_t src32, uint32_t dst32, unsigned scale5) {
-    return SkCompact_rgb_16(dst32 + ((src32 - dst32) * scale5 >> 5));
-}
-
-void SkRGB16_Opaque_Blitter::blitMask(const SkMask& mask,
-                                      const SkIRect& clip) {
-    if (mask.fFormat == SkMask::kBW_Format) {
-        SkRGB16_BlitBW(fDevice, mask, clip, fColor16);
-        return;
-    }
-
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(clip.fLeft, clip.fTop);
-    const uint8_t* SK_RESTRICT alpha = mask.getAddr8(clip.fLeft, clip.fTop);
-    int width = clip.width();
-    int height = clip.height();
-    size_t      deviceRB = fDevice.rowBytes() - (width << 1);
-    unsigned    maskRB = mask.fRowBytes - width;
-    uint32_t    expanded32 = fExpandedRaw16;
-
-#if defined(SK_ARM_HAS_NEON) && defined(SK_CPU_LENDIAN)
-#define    UNROLL    8
-    do {
-        int w = width;
-        if (w >= UNROLL) {
-            uint32x4_t color, dev_lo, dev_hi;
-            uint32x4_t wn1, wn2, tmp;
-            uint32x4_t vmask_g16, vmask_ng16;
-            uint16x8_t valpha, vdev;
-            uint16x4_t odev_lo, odev_hi, valpha_lo, valpha_hi;
-
-            // prepare constants
-            vmask_g16 = vdupq_n_u32(SK_G16_MASK_IN_PLACE);
-            vmask_ng16 = vdupq_n_u32(~SK_G16_MASK_IN_PLACE);
-            color = vdupq_n_u32(expanded32);
-
-            do {
-                // alpha is 8x8, widen and split to get a pair of 16x4
-                valpha = vaddw_u8(vdupq_n_u16(1), vld1_u8(alpha));
-                valpha = vshrq_n_u16(valpha, 3);
-                valpha_lo = vget_low_u16(valpha);
-                valpha_hi = vget_high_u16(valpha);
-
-                // load pixels
-                vdev = vld1q_u16(device);
-                dev_lo = vmovl_u16(vget_low_u16(vdev));
-                dev_hi = vmovl_u16(vget_high_u16(vdev));
-
-                // unpack them in 32 bits
-                dev_lo = (dev_lo & vmask_ng16) | vshlq_n_u32(dev_lo & vmask_g16, 16);
-                dev_hi = (dev_hi & vmask_ng16) | vshlq_n_u32(dev_hi & vmask_g16, 16);
-
-                // blend with color
-                tmp = (color - dev_lo) * vmovl_u16(valpha_lo);
-                tmp = vshrq_n_u32(tmp, 5);
-                dev_lo += tmp;
-
-                tmp = vmulq_u32(color - dev_hi, vmovl_u16(valpha_hi));
-                tmp = vshrq_n_u32(tmp, 5);
-                dev_hi += tmp;
-
-                // re-compact
-                wn1 = dev_lo & vmask_ng16;
-                wn2 = vshrq_n_u32(dev_lo, 16) & vmask_g16;
-                odev_lo = vmovn_u32(wn1 | wn2);
-
-                wn1 = dev_hi & vmask_ng16;
-                wn2 = vshrq_n_u32(dev_hi, 16) & vmask_g16;
-                odev_hi = vmovn_u32(wn1 | wn2);
-
-                // store
-                vst1q_u16(device, vcombine_u16(odev_lo, odev_hi));
-
-                device += UNROLL;
-                alpha += UNROLL;
-                w -= UNROLL;
-            } while (w >= UNROLL);
-        }
-
-        // residuals
-        while (w > 0) {
-            *device = blend_compact(expanded32, SkExpand_rgb_16(*device),
-                                    SkAlpha255To256(*alpha++) >> 3);
-            device += 1;
-            --w;
-        }
-        device = (uint16_t*)((char*)device + deviceRB);
-        alpha += maskRB;
-    } while (--height != 0);
-#undef    UNROLL
-#else   // non-neon code
-    do {
-        int w = width;
-        do {
-            *device = blend_compact(expanded32, SkExpand_rgb_16(*device),
-                                    SkAlpha255To256(*alpha++) >> 3);
-            device += 1;
-        } while (--w != 0);
-        device = (uint16_t*)((char*)device + deviceRB);
-        alpha += maskRB;
-    } while (--height != 0);
-#endif
-}
-
-void SkRGB16_Opaque_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    size_t    deviceRB = fDevice.rowBytes();
-
-    // TODO: respect fDoDither
-    unsigned scale5 = SkAlpha255To256(alpha) >> 3;
-    uint32_t src32 =  fExpandedRaw16 * scale5;
-    scale5 = 32 - scale5;
-#if defined(SK_ARM_HAS_NEON) && defined(SK_CPU_LENDIAN)
-    SkRGB16BlitterBlitV_neon(device, height, deviceRB, scale5, src32);
-#else
-    do {
-        uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
-        *device = SkCompact_rgb_16((src32 + dst32) >> 5);
-        device = (uint16_t*)((char*)device + deviceRB);
-    } while (--height != 0);
-#endif
-}
-
-void SkRGB16_Opaque_Blitter::blitRect(int x, int y, int width, int height) {
-    SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height());
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    size_t      deviceRB = fDevice.rowBytes();
-    uint16_t    color16 = fColor16;
-
-    if (fDoDither) {
-        uint16_t ditherColor = fRawDither16;
-        if ((x ^ y) & 1) {
-            SkTSwap(ditherColor, color16);
-        }
-        while (--height >= 0) {
-            sk_dither_memset16(device, color16, ditherColor, width);
-            SkTSwap(ditherColor, color16);
-            device = (uint16_t*)((char*)device + deviceRB);
-        }
-    } else {  // no dither
-        while (--height >= 0) {
-            sk_memset16(device, color16, width);
-            device = (uint16_t*)((char*)device + deviceRB);
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkRGB16_Blitter::SkRGB16_Blitter(const SkPixmap& device, const SkPaint& paint)
-    : INHERITED(device) {
-    SkColor color = paint.getColor();
-
-    fSrcColor32 = SkPreMultiplyColor(color);
-    fScale = SkAlpha255To256(SkColorGetA(color));
-
-    int r = SkColorGetR(color);
-    int g = SkColorGetG(color);
-    int b = SkColorGetB(color);
-
-    fRawColor16 = fRawDither16 = SkPack888ToRGB16(r, g, b);
-    // if we're dithered, use fRawDither16 to hold that.
-    if ((fDoDither = paint.isDither()) != false) {
-        fRawDither16 = SkDitherPack888ToRGB16(r, g, b);
-    }
-
-    fExpandedRaw16 = SkExpand_rgb_16(fRawColor16);
-
-    fColor16 = SkPackRGB16( SkAlphaMul(r, fScale) >> (8 - SK_R16_BITS),
-                            SkAlphaMul(g, fScale) >> (8 - SK_G16_BITS),
-                            SkAlphaMul(b, fScale) >> (8 - SK_B16_BITS));
-
-    // compute SkBlitRow::Procs
-    unsigned flags = 0;
-
-    if (SkGetPackedA32(fSrcColor32) < 0xFF) {
-        flags |= SkBlitRow::kSrcPixelAlpha_Flag;
-    }
-
-    if (fDoDither) {
-        flags |= SkBlitRow::kDither_Flag;
-    }
-
-    fColorProc16 = SkBlitRow::ColorFactory16(flags);
-}
-
-const SkPixmap* SkRGB16_Blitter::justAnOpaqueColor(uint32_t* value) {
-    if (!fDoDither && 256 == fScale) {
-        *value = fRawColor16;
-        return &fDevice;
-    }
-    return nullptr;
-}
-
-void SkRGB16_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(width > 0);
-    SkASSERT(x + width <= fDevice.width());
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-
-    fColorProc16(device, fSrcColor32, width, x, y);
-}
-
-void SkRGB16_Blitter::blitAntiH(int x, int y,
-                                const SkAlpha* SK_RESTRICT antialias,
-                                const int16_t* SK_RESTRICT runs) {
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    uint32_t    srcExpanded = fExpandedRaw16;
-    unsigned    scale = fScale;
-
-    // TODO: respect fDoDither
-    for (;;) {
-        int count = runs[0];
-        SkASSERT(count >= 0);
-        if (count <= 0) {
-            return;
-        }
-        runs += count;
-
-        unsigned aa = antialias[0];
-        antialias += count;
-        if (aa) {
-            unsigned scale5 = SkAlpha255To256(aa) * scale >> (8 + 3);
-            uint32_t src32 =  srcExpanded * scale5;
-            scale5 = 32 - scale5;
-            do {
-                uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
-                *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
-            } while (--count != 0);
-            continue;
-        }
-        device += count;
-    }
-}
-
-static inline void blend_8_pixels(U8CPU bw, uint16_t dst[], unsigned dst_scale,
-                                  U16CPU srcColor) {
-    if (bw & 0x80) dst[0] = srcColor + SkAlphaMulRGB16(dst[0], dst_scale);
-    if (bw & 0x40) dst[1] = srcColor + SkAlphaMulRGB16(dst[1], dst_scale);
-    if (bw & 0x20) dst[2] = srcColor + SkAlphaMulRGB16(dst[2], dst_scale);
-    if (bw & 0x10) dst[3] = srcColor + SkAlphaMulRGB16(dst[3], dst_scale);
-    if (bw & 0x08) dst[4] = srcColor + SkAlphaMulRGB16(dst[4], dst_scale);
-    if (bw & 0x04) dst[5] = srcColor + SkAlphaMulRGB16(dst[5], dst_scale);
-    if (bw & 0x02) dst[6] = srcColor + SkAlphaMulRGB16(dst[6], dst_scale);
-    if (bw & 0x01) dst[7] = srcColor + SkAlphaMulRGB16(dst[7], dst_scale);
-}
-
-#define SK_BLITBWMASK_NAME                  SkRGB16_BlendBW
-#define SK_BLITBWMASK_ARGS                  , unsigned dst_scale, U16CPU src_color
-#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, dst_scale, src_color)
-#define SK_BLITBWMASK_GETADDR               writable_addr16
-#define SK_BLITBWMASK_DEVTYPE               uint16_t
-#include "SkBlitBWMaskTemplate.h"
-
-void SkRGB16_Blitter::blitMask(const SkMask& mask,
-                               const SkIRect& clip) {
-    if (mask.fFormat == SkMask::kBW_Format) {
-        SkRGB16_BlendBW(fDevice, mask, clip, 256 - fScale, fColor16);
-        return;
-    }
-
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(clip.fLeft, clip.fTop);
-    const uint8_t* SK_RESTRICT alpha = mask.getAddr8(clip.fLeft, clip.fTop);
-    int width = clip.width();
-    int height = clip.height();
-    size_t      deviceRB = fDevice.rowBytes() - (width << 1);
-    unsigned    maskRB = mask.fRowBytes - width;
-    uint32_t    color32 = fExpandedRaw16;
-
-    unsigned scale256 = fScale;
-    do {
-        int w = width;
-        do {
-            unsigned aa = *alpha++;
-            unsigned scale = SkAlpha255To256(aa) * scale256 >> (8 + 3);
-            uint32_t src32 = color32 * scale;
-            uint32_t dst32 = SkExpand_rgb_16(*device) * (32 - scale);
-            *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
-        } while (--w != 0);
-        device = (uint16_t*)((char*)device + deviceRB);
-        alpha += maskRB;
-    } while (--height != 0);
-}
-
-void SkRGB16_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    size_t    deviceRB = fDevice.rowBytes();
-
-    // TODO: respect fDoDither
-    unsigned scale5 = SkAlpha255To256(alpha) * fScale >> (8 + 3);
-    uint32_t src32 =  fExpandedRaw16 * scale5;
-    scale5 = 32 - scale5;
-#if defined(SK_ARM_HAS_NEON) && defined(SK_CPU_LENDIAN)
-    SkRGB16BlitterBlitV_neon(device, height, deviceRB, scale5, src32);
-#else
-    do {
-        uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
-        *device = SkCompact_rgb_16((src32 + dst32) >> 5);
-        device = (uint16_t*)((char*)device + deviceRB);
-    } while (--height != 0);
-#endif
-}
-
-void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) {
-    SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height());
-    uint16_t* SK_RESTRICT device = fDevice.writable_addr16(x, y);
-    size_t    deviceRB = fDevice.rowBytes();
-
-    while (--height >= 0) {
-        fColorProc16(device, fSrcColor32, width, x, y);
-        device = (uint16_t*)((char*)device + deviceRB);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkPixmap& device,
-                                               const SkPaint& paint,
-                                               SkShader::Context* shaderContext)
-    : INHERITED(device, paint, shaderContext)
-{
-    SkASSERT(paint.isSrcOver());
-
-    fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor));
-
-    // compute SkBlitRow::Procs
-    unsigned flags = 0;
-
-    uint32_t shaderFlags = fShaderFlags;
-    // shaders take care of global alpha, so we never set it in SkBlitRow
-    if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) {
-        flags |= SkBlitRow::kSrcPixelAlpha_Flag;
-    }
-    if (paint.isDither()) {
-        flags |= SkBlitRow::kDither_Flag;
-    }
-    // used when we know our global alpha is 0xFF
-    fOpaqueProc = SkBlitRow::Factory16(flags);
-    // used when we know our global alpha is < 0xFF
-    fAlphaProc  = SkBlitRow::Factory16(flags | SkBlitRow::kGlobalAlpha_Flag);
-}
-
-SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() {
-    sk_free(fBuffer);
-}
-
-void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(x + width <= fDevice.width());
-
-    fShaderContext->shadeSpan(x, y, fBuffer, width);
-    // shaders take care of global alpha, so we pass 0xFF (should be ignored)
-    fOpaqueProc(fDevice.writable_addr16(x, y), fBuffer, width, 0xFF, x, y);
-}
-
-void SkRGB16_Shader_Blitter::blitRect(int x, int y, int width, int height) {
-    SkShader::Context* shaderContext = fShaderContext;
-    SkBlitRow::Proc16  proc = fOpaqueProc;
-    SkPMColor*         buffer = fBuffer;
-    uint16_t*          dst = fDevice.writable_addr16(x, y);
-    size_t             dstRB = fDevice.rowBytes();
-
-    if (fShaderFlags & SkShader::kConstInY32_Flag) {
-        shaderContext->shadeSpan(x, y, buffer, width);
-        do {
-            proc(dst, buffer, width, 0xFF, x, y);
-            y += 1;
-            dst = (uint16_t*)((char*)dst + dstRB);
-        } while (--height);
-    } else {
-        do {
-            shaderContext->shadeSpan(x, y, buffer, width);
-            proc(dst, buffer, width, 0xFF, x, y);
-            y += 1;
-            dst = (uint16_t*)((char*)dst + dstRB);
-        } while (--height);
-    }
-}
-
-static inline int count_nonzero_span(const int16_t runs[], const SkAlpha aa[]) {
-    int count = 0;
-    for (;;) {
-        int n = *runs;
-        if (n == 0 || *aa == 0) {
-            break;
-        }
-        runs += n;
-        aa += n;
-        count += n;
-    }
-    return count;
-}
-
-void SkRGB16_Shader_Blitter::blitAntiH(int x, int y,
-                                       const SkAlpha* SK_RESTRICT antialias,
-                                       const int16_t* SK_RESTRICT runs) {
-    SkShader::Context*     shaderContext = fShaderContext;
-    SkPMColor* SK_RESTRICT span = fBuffer;
-    uint16_t* SK_RESTRICT  device = fDevice.writable_addr16(x, y);
-
-    for (;;) {
-        int count = *runs;
-        if (count <= 0) {
-            break;
-        }
-        int aa = *antialias;
-        if (0 == aa) {
-            device += count;
-            runs += count;
-            antialias += count;
-            x += count;
-            continue;
-        }
-
-        int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count);
-
-        SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
-        shaderContext->shadeSpan(x, y, span, nonZeroCount);
-
-        SkPMColor* localSpan = span;
-        for (;;) {
-            SkBlitRow::Proc16 proc = (aa == 0xFF) ? fOpaqueProc : fAlphaProc;
-            proc(device, localSpan, count, aa, x, y);
-
-            x += count;
-            device += count;
-            runs += count;
-            antialias += count;
-            nonZeroCount -= count;
-            if (nonZeroCount == 0) {
-                break;
-            }
-            localSpan += count;
-            SkASSERT(nonZeroCount > 0);
-            count = *runs;
-            SkASSERT(count > 0);
-            aa = *antialias;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////
-
-SkRGB16_Shader_Xfermode_Blitter::SkRGB16_Shader_Xfermode_Blitter(
-                                const SkPixmap& device, const SkPaint& paint,
-                                SkShader::Context* shaderContext)
-    : INHERITED(device, paint, shaderContext)
-{
-    fXfermode = SkXfermode::Peek(paint.getBlendMode());
-    SkASSERT(fXfermode);
-
-    int width = device.width();
-    fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor));
-    fAAExpand = (uint8_t*)(fBuffer + width);
-}
-
-SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() {
-    sk_free(fBuffer);
-}
-
-void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(x + width <= fDevice.width());
-
-    uint16_t*   device = fDevice.writable_addr16(x, y);
-    SkPMColor*  span = fBuffer;
-
-    fShaderContext->shadeSpan(x, y, span, width);
-    fXfermode->xfer16(device, span, width, nullptr);
-}
-
-void SkRGB16_Shader_Xfermode_Blitter::blitAntiH(int x, int y,
-                                const SkAlpha* SK_RESTRICT antialias,
-                                const int16_t* SK_RESTRICT runs) {
-    SkShader::Context*     shaderContext = fShaderContext;
-    SkXfermode*            mode = fXfermode;
-    SkPMColor* SK_RESTRICT span = fBuffer;
-    uint8_t* SK_RESTRICT   aaExpand = fAAExpand;
-    uint16_t* SK_RESTRICT  device = fDevice.writable_addr16(x, y);
-
-    for (;;) {
-        int count = *runs;
-        if (count <= 0) {
-            break;
-        }
-        int aa = *antialias;
-        if (0 == aa) {
-            device += count;
-            runs += count;
-            antialias += count;
-            x += count;
-            continue;
-        }
-
-        int nonZeroCount = count + count_nonzero_span(runs + count,
-                                                      antialias + count);
-
-        SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
-        shaderContext->shadeSpan(x, y, span, nonZeroCount);
-
-        x += nonZeroCount;
-        SkPMColor* localSpan = span;
-        for (;;) {
-            if (aa == 0xFF) {
-                mode->xfer16(device, localSpan, count, nullptr);
-            } else {
-                SkASSERT(aa);
-                memset(aaExpand, aa, count);
-                mode->xfer16(device, localSpan, count, aaExpand);
-            }
-            device += count;
-            runs += count;
-            antialias += count;
-            nonZeroCount -= count;
-            if (nonZeroCount == 0) {
-                break;
-            }
-            localSpan += count;
-            SkASSERT(nonZeroCount > 0);
-            count = *runs;
-            SkASSERT(count > 0);
-            aa = *antialias;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint,
-        SkShader::Context* shaderContext,
-        SkArenaAlloc* alloc) {
-    SkASSERT(alloc != nullptr);
-
-    SkBlitter* blitter;
-    SkShader* shader = paint.getShader();
-    bool is_srcover = paint.isSrcOver();
-
-    // we require a shader if there is an xfermode, handled by our caller
-    SkASSERT(is_srcover || shader);
-
-    if (shader) {
-        SkASSERT(shaderContext != nullptr);
-        if (!is_srcover) {
-            blitter = alloc->make<SkRGB16_Shader_Xfermode_Blitter>(device, paint,
-                                                                          shaderContext);
-        } else {
-            blitter = alloc->make<SkRGB16_Shader_Blitter>(device, paint, shaderContext);
-        }
-    } else {
-        // no shader, no xfermode, (and we always ignore colorfilter)
-        SkColor color = paint.getColor();
-        if (0 == SkColorGetA(color)) {
-            blitter = alloc->make<SkNullBlitter>();
-#ifdef USE_BLACK_BLITTER
-        } else if (SK_ColorBLACK == color) {
-            blitter = alloc->make<SkRGB16_Black_Blitter>(device, paint);
-#endif
-        } else if (0xFF == SkColorGetA(color)) {
-            blitter = alloc->make<SkRGB16_Opaque_Blitter>(device, paint);
-        } else {
-            blitter = alloc->make<SkRGB16_Blitter>(device, paint);
-        }
-    }
-
-    return blitter;
-}
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
index 4c184bd..d82f3db 100644
--- a/src/core/SkBlitter_Sprite.cpp
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -6,7 +6,11 @@
  */
 
 #include "SkArenaAlloc.h"
+#include "SkColorSpace.h"
+#include "SkCoreBlitters.h"
 #include "SkOpts.h"
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
 #include "SkSpriteBlitter.h"
 
 SkSpriteBlitter::SkSpriteBlitter(const SkPixmap& source)
@@ -48,18 +52,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-//  Only valid if...
-//      1. src == dst format
-//      2. paint has no modifiers (i.e. alpha, colorfilter, etc.)
-//      3. xfermode needs no blending: e.g. kSrc_Mode or kSrcOver_Mode + opaque src
-//
-class SkSpriteBlitter_Src_SrcOver final : public SkSpriteBlitter {
+class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter {
 public:
     static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) {
         if (dst.colorType() != src.colorType()) {
             return false;
         }
-        if (dst.info().gammaCloseToSRGB() != src.info().gammaCloseToSRGB()) {
+        if (!SkColorSpace::Equals(dst.colorSpace(), src.colorSpace())) {
             return false;
         }
         if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) {
@@ -69,72 +68,107 @@
             return false;
         }
         SkBlendMode mode = paint.getBlendMode();
-        if (SkBlendMode::kSrc == mode) {
-            return true;
-        }
-        if (SkBlendMode::kSrcOver == mode && src.isOpaque()) {
-            return true;
-        }
-
-        // At this point memcpy can't be used. The following check for using SrcOver.
-
-        if (dst.colorType() != kN32_SkColorType || !dst.info().gammaCloseToSRGB()) {
-            return false;
-        }
-
-        return SkBlendMode::kSrcOver == mode;
+        return SkBlendMode::kSrc == mode || (SkBlendMode::kSrcOver == mode && src.isOpaque());
     }
 
-    SkSpriteBlitter_Src_SrcOver(const SkPixmap& src)
+    SkSpriteBlitter_Memcpy(const SkPixmap& src)
         : INHERITED(src) {}
 
-    void setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override {
-        SkASSERT(Supports(dst, fSource, paint));
-        this->INHERITED::setup(dst, left, top, paint);
-        SkBlendMode mode = paint.getBlendMode();
-
-        SkASSERT(mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc);
-
-        if (mode == SkBlendMode::kSrcOver && !fSource.isOpaque()) {
-            fUseMemcpy = false;
-        }
-    }
-
     void blitRect(int x, int y, int width, int height) override {
         SkASSERT(fDst.colorType() == fSource.colorType());
-        SkASSERT(fDst.info().gammaCloseToSRGB() == fSource.info().gammaCloseToSRGB());
         SkASSERT(width > 0 && height > 0);
 
-        if (fUseMemcpy) {
-            char* dst = (char*)fDst.writable_addr(x, y);
-            const char* src = (const char*)fSource.addr(x - fLeft, y - fTop);
-            const size_t dstRB = fDst.rowBytes();
-            const size_t srcRB = fSource.rowBytes();
-            const size_t bytesToCopy = width << fSource.shiftPerPixel();
+        char* dst = (char*)fDst.writable_addr(x, y);
+        const char* src = (const char*)fSource.addr(x - fLeft, y - fTop);
+        const size_t dstRB = fDst.rowBytes();
+        const size_t srcRB = fSource.rowBytes();
+        const size_t bytesToCopy = width << fSource.shiftPerPixel();
 
-            while (height --> 0) {
-                memcpy(dst, src, bytesToCopy);
-                dst += dstRB;
-                src += srcRB;
-            }
-        } else {
-            uint32_t* dst       = fDst.writable_addr32(x, y);
-            const uint32_t* src = fSource.addr32(x - fLeft, y - fTop);
-            const int dstStride = fDst.rowBytesAsPixels();
-            const int srcStride = fSource.rowBytesAsPixels();
-
-            while (height --> 0) {
-                SkOpts::srcover_srgb_srgb(dst, src, width, width);
-                dst += dstStride;
-                src += srcStride;
-            }
+        while (height --> 0) {
+            memcpy(dst, src, bytesToCopy);
+            dst += dstRB;
+            src += srcRB;
         }
     }
 
 private:
     typedef SkSpriteBlitter INHERITED;
+};
 
-    bool fUseMemcpy {true};
+class SkRasterPipelineSpriteBlitter : public SkSpriteBlitter {
+public:
+    SkRasterPipelineSpriteBlitter(const SkPixmap& src, SkArenaAlloc* alloc)
+        : INHERITED(src)
+        , fAlloc(alloc)
+        , fBlitter(nullptr)
+        , fSrcPtr(nullptr)
+    {}
+
+    static bool Supports(const SkPixmap& src) {
+        // We'd need to add a load_i8 stage.
+        return src.colorType() != kIndex_8_SkColorType;
+    }
+
+    void setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override {
+        fDst  = dst;
+        fLeft = left;
+        fTop  = top;
+
+        fPaintColor = SkColor4f_from_SkColor(paint.getColor(), fDst.colorSpace());
+
+        SkRasterPipeline p(fAlloc);
+        switch (fSource.colorType()) {
+            case kAlpha_8_SkColorType:   p.append(SkRasterPipeline::load_a8,   &fSrcPtr); break;
+            case kGray_8_SkColorType:    p.append(SkRasterPipeline::load_g8,   &fSrcPtr); break;
+            case kRGB_565_SkColorType:   p.append(SkRasterPipeline::load_565,  &fSrcPtr); break;
+            case kARGB_4444_SkColorType: p.append(SkRasterPipeline::load_4444, &fSrcPtr); break;
+            case kRGBA_8888_SkColorType:
+            case kBGRA_8888_SkColorType: p.append(SkRasterPipeline::load_8888, &fSrcPtr); break;
+            case kRGBA_F16_SkColorType:  p.append(SkRasterPipeline::load_f16,  &fSrcPtr); break;
+            default: SkASSERT(false);
+        }
+        if (fDst.colorSpace() &&
+                (!fSource.colorSpace() || fSource.colorSpace()->gammaCloseToSRGB())) {
+            p.append_from_srgb(fSource.alphaType());
+        }
+        if (fSource.colorType() == kBGRA_8888_SkColorType) {
+            p.append(SkRasterPipeline::swap_rb);
+        }
+        if (fSource.colorType() == kAlpha_8_SkColorType) {
+            p.append(SkRasterPipeline::set_rgb, &fPaintColor);
+            p.append(SkRasterPipeline::premul);
+        }
+        append_gamut_transform(&p, fAlloc,
+                               fSource.colorSpace(), fDst.colorSpace(), kPremul_SkAlphaType);
+        if (fPaintColor.fA != 1.0f) {
+            p.append(SkRasterPipeline::scale_1_float, &fPaintColor.fA);
+        }
+
+        bool is_opaque    = fSource.isOpaque() && fPaintColor.fA == 1.0f,
+             wants_dither = paint.isDither();
+        fBlitter = SkCreateRasterPipelineBlitter(fDst, paint, p, is_opaque, wants_dither, fAlloc);
+    }
+
+    void blitRect(int x, int y, int width, int height) override {
+        fSrcPtr = (const char*)fSource.addr(x-fLeft,y-fTop);
+
+        // Our pipeline will load from fSrcPtr+x, fSrcPtr+x+1, etc.,
+        // so we back up an extra x pixels to start at 0.
+        fSrcPtr -= fSource.info().bytesPerPixel() * x;
+
+        while (height --> 0) {
+            fBlitter->blitH(x,y++, width);
+            fSrcPtr += fSource.rowBytes();
+        }
+    }
+
+private:
+    SkArenaAlloc* fAlloc;
+    SkBlitter*    fBlitter;
+    const char*   fSrcPtr;
+    SkColor4f     fPaintColor;
+
+    typedef SkSpriteBlitter INHERITED;
 };
 
 // returning null means the caller will call SkBlitter::Choose() and
@@ -152,34 +186,20 @@
     */
     SkASSERT(allocator != nullptr);
 
-    // Defer to the general code if the pixels are unpremultipled. This case is not common,
-    // and this simplifies the code.
     if (source.alphaType() == kUnpremul_SkAlphaType) {
         return nullptr;
     }
 
     SkSpriteBlitter* blitter = nullptr;
 
-    if (SkSpriteBlitter_Src_SrcOver::Supports(dst, source, paint)) {
-        blitter = allocator->make<SkSpriteBlitter_Src_SrcOver>(source);
-    } else {
-        switch (dst.colorType()) {
-            case kRGB_565_SkColorType:
-                blitter = SkSpriteBlitter::ChooseD16(source, paint, allocator);
-                break;
-            case kN32_SkColorType:
-                if (dst.info().gammaCloseToSRGB()) {
-                    blitter = SkSpriteBlitter::ChooseS32(source, paint, allocator);
-                } else {
-                    blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator);
-                }
-                break;
-            case kRGBA_F16_SkColorType:
-                blitter = SkSpriteBlitter::ChooseF16(source, paint, allocator);
-                break;
-            default:
-                break;
-        }
+    if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) {
+        blitter = allocator->make<SkSpriteBlitter_Memcpy>(source);
+    }
+    if (!blitter && !dst.colorSpace() && dst.colorType() == kN32_SkColorType) {
+        blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator);
+    }
+    if (!blitter && SkRasterPipelineSpriteBlitter::Supports(source)) {
+        blitter = allocator->make<SkRasterPipelineSpriteBlitter>(source, allocator);
     }
 
     if (blitter) {
diff --git a/src/core/SkBlurImageFilter.cpp b/src/core/SkBlurImageFilter.cpp
index 4c68437..320097b 100644
--- a/src/core/SkBlurImageFilter.cpp
+++ b/src/core/SkBlurImageFilter.cpp
@@ -35,6 +35,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
 
 private:
@@ -74,13 +75,9 @@
     return sigma;
 }
 
-SkBlurImageFilterImpl::SkBlurImageFilterImpl(SkScalar sigmaX,
-                                     SkScalar sigmaY,
-                                     sk_sp<SkImageFilter> input,
-                                     const CropRect* cropRect)
-    : INHERITED(&input, 1, cropRect)
-    , fSigma(SkSize::Make(sigmaX, sigmaY)) {
-}
+SkBlurImageFilterImpl::SkBlurImageFilterImpl(
+        SkScalar sigmaX, SkScalar sigmaY, sk_sp<SkImageFilter> input, const CropRect* cropRect)
+        : INHERITED(&input, 1, cropRect), fSigma{sigmaX, sigmaY} {}
 
 sk_sp<SkFlattenable> SkBlurImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
@@ -217,8 +214,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels inputLock(inputBM), tmpLock(tmp), dstLock(dst);
-
     offset->fX = dstBounds.fLeft;
     offset->fY = dstBounds.fTop;
     SkPMColor* t = tmp.getAddr32(0, 0);
@@ -273,6 +268,17 @@
                                           dst, &source->props());
 }
 
+sk_sp<SkImageFilter> SkBlurImageFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkBlurImageFilterImpl*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkImageFilter::MakeBlur(fSigma.width(), fSigma.height(), std::move(input),
+                                   this->getCropRectIfSet());
+}
 
 SkRect SkBlurImageFilterImpl::computeFastBounds(const SkRect& src) const {
     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index ebd031c..6fded02 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -20,6 +20,7 @@
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkLatticeIter.h"
+#include "SkLights.h"
 #include "SkMakeUnique.h"
 #include "SkMatrixUtils.h"
 #include "SkMetaData.h"
@@ -28,13 +29,11 @@
 #include "SkPaintPriv.h"
 #include "SkPatchUtils.h"
 #include "SkPicture.h"
-#include "SkRadialShadowMapShader.h"
 #include "SkRasterClip.h"
 #include "SkRasterHandleAllocator.h"
 #include "SkRRect.h"
-#include "SkShadowPaintFilterCanvas.h"
-#include "SkShadowShader.h"
 #include "SkSpecialImage.h"
+#include "SkString.h"
 #include "SkSurface_Base.h"
 #include "SkTextBlob.h"
 #include "SkTextFormatParams.h"
@@ -247,27 +246,23 @@
     by the device's XY offset and bitmap-bounds.
 */
 struct DeviceCM {
-    DeviceCM*           fNext;
-    SkBaseDevice*       fDevice;
-    SkRasterClip        fClip;
-    SkPaint*            fPaint; // may be null (in the future)
-    const SkMatrix*     fMatrix;
-    SkMatrix            fMatrixStorage;
-    SkMatrix            fStashedMatrix; // original CTM; used by imagefilter in saveLayer
+    DeviceCM*                      fNext;
+    sk_sp<SkBaseDevice>            fDevice;
+    SkRasterClip                   fClip;
+    std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
+    SkMatrix                       fStashedMatrix; // original CTM; used by imagefilter in saveLayer
+    sk_sp<SkImage>                 fClipImage;
+    SkMatrix                       fClipMatrix;
 
-    DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas, const SkMatrix& stashed)
+    DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
+             const SkImage* clipImage, const SkMatrix* clipMatrix)
         : fNext(nullptr)
+        , fDevice(std::move(device))
+        , fPaint(paint ? skstd::make_unique<SkPaint>(*paint) : nullptr)
         , fStashedMatrix(stashed)
-    {
-        SkSafeRef(device);
-        fDevice = device;
-        fPaint = paint ? new SkPaint(*paint) : nullptr;
-    }
-
-    ~DeviceCM() {
-        SkSafeUnref(fDevice);
-        delete fPaint;
-    }
+        , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage)))
+        , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
+    {}
 
     void reset(const SkIRect& bounds) {
         SkASSERT(!fPaint);
@@ -299,22 +294,17 @@
     SkMatrix            fMatrix;
     int                 fDeferredSaveCount;
 
-    // This is the current cumulative depth (aggregate of all done translateZ calls)
-    SkScalar        fCurDrawDepth;
-
     MCRec() {
         fFilter     = nullptr;
         fLayer      = nullptr;
         fTopLayer   = nullptr;
         fMatrix.reset();
         fDeferredSaveCount = 0;
-        fCurDrawDepth      = 0;
 
         // don't bother initializing fNext
         inc_rec();
     }
-    MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
-                               fCurDrawDepth(prev.fCurDrawDepth) {
+    MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
         fFilter = SkSafeRef(prev.fFilter);
         fLayer = nullptr;
         fTopLayer = prev.fTopLayer;
@@ -348,8 +338,8 @@
     bool next() {
         const DeviceCM* rec = fCurrLayer;
         if (rec && rec->fDevice) {
-            fDevice = rec->fDevice;
-            fPaint  = rec->fPaint;
+            fDevice = rec->fDevice.get();
+            fPaint  = rec->fPaint.get();
             fCurrLayer = rec->fNext;
             // fCurrLayer may be nullptr now
             return true;
@@ -368,16 +358,16 @@
     const SkPaint*  fPaint;     // May be null.
 };
 
-#define FOR_EACH_TOP_DEVICE( code )                 \
-    do {                                            \
-        DeviceCM* layer = fMCRec->fTopLayer;        \
-        while (layer) {                             \
-            SkBaseDevice* device = layer->fDevice;  \
-            if (device) {                           \
-                code;                               \
-            }                                       \
-            layer = layer->fNext;                   \
-        }                                           \
+#define FOR_EACH_TOP_DEVICE( code )                       \
+    do {                                                  \
+        DeviceCM* layer = fMCRec->fTopLayer;              \
+        while (layer) {                                   \
+            SkBaseDevice* device = layer->fDevice.get();  \
+            if (device) {                                 \
+                code;                                     \
+            }                                             \
+            layer = layer->fNext;                         \
+        }                                                 \
     } while (0)
 
 /////////////////////////////////////////////////////////////////////////////
@@ -536,8 +526,7 @@
     bool            fDone;
     bool            fIsSimple;
     SkDrawLooper::Context* fLooperContext;
-    char            fStorage[48];
-    SkArenaAlloc    fAlloc {fStorage};
+    SkSTArenaAlloc<48>     fAlloc;
 
     bool doNext(SkDrawFilter::Type drawType);
 };
@@ -633,7 +622,7 @@
 
     // We're peering through a lot of structs here.  Only at this scope do we
     // know that the device is a SkNoPixelsDevice.
-    static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice)->resetForNextPicture(bounds);
+    static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds);
     fDeviceClipBounds = qr_clip_bounds(bounds);
     fIsScaleTranslate = true;
 }
@@ -646,9 +635,6 @@
     fAllowSimplifyClip = false;
     fSaveCount = 1;
     fMetaData = nullptr;
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    fLights = nullptr;
-#endif
 
     fMCRec = (MCRec*)fMCStack.push_back();
     new (fMCRec) MCRec;
@@ -657,7 +643,7 @@
 
     SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
     fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
-    new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fMCRec->fMatrix);
+    new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix, nullptr, nullptr);
 
     fMCRec->fTopLayer = fMCRec->fLayer;
 
@@ -666,7 +652,6 @@
     if (device) {
         // The root device and the canvas should always have the same pixel geometry
         SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
-        fMCRec->fLayer->fDevice = SkRef(device);
         fMCRec->fRasterClip.setRect(device->getGlobalBounds());
         fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
 
@@ -824,53 +809,11 @@
     // return root device
     MCRec* rec = (MCRec*) fMCStack.front();
     SkASSERT(rec && rec->fLayer);
-    return rec->fLayer->fDevice;
+    return rec->fLayer->fDevice.get();
 }
 
 SkBaseDevice* SkCanvas::getTopDevice() const {
-    return fMCRec->fTopLayer->fDevice;
-}
-
-bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
-    bool weAllocated = false;
-    if (nullptr == bitmap->pixelRef()) {
-        if (!bitmap->tryAllocPixels()) {
-            return false;
-        }
-        weAllocated = true;
-    }
-
-    SkAutoPixmapUnlock unlocker;
-    if (bitmap->requestLock(&unlocker)) {
-        const SkPixmap& pm = unlocker.pixmap();
-        if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
-            return true;
-        }
-    }
-
-    if (weAllocated) {
-        bitmap->setPixelRef(nullptr, 0, 0);
-    }
-    return false;
-}
-
-bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
-    SkIRect r = srcRect;
-    const SkISize size = this->getBaseLayerSize();
-    if (!r.intersect(0, 0, size.width(), size.height())) {
-        bitmap->reset();
-        return false;
-    }
-
-    if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
-        // bitmap will already be reset.
-        return false;
-    }
-    if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
-        bitmap->reset();
-        return false;
-    }
-    return true;
+    return fMCRec->fTopLayer->fDevice.get();
 }
 
 bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
@@ -882,10 +825,18 @@
     return device->readPixels(dstInfo, dstP, rowBytes, x, y);
 }
 
+bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
+    return pm.addr() && this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y);
+}
+
+bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
+    SkPixmap pm;
+    return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
+}
+
 bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
-    SkAutoPixmapUnlock unlocker;
-    if (bitmap.requestLock(&unlocker)) {
-        const SkPixmap& pm = unlocker.pixmap();
+    SkPixmap pm;
+    if (bitmap.peekPixels(&pm)) {
         return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
     }
     return false;
@@ -1053,13 +1004,14 @@
 }
 
 int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
-    SaveLayerRec rec(origRec);
+    SkTCopyOnFirstWrite<SaveLayerRec> rec(origRec);
     if (gIgnoreSaveLayerBounds) {
-        rec.fBounds = nullptr;
+        rec.writable()->fBounds = nullptr;
     }
-    SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
+
+    SaveLayerStrategy strategy = this->getSaveLayerStrategy(*rec);
     fSaveCount += 1;
-    this->internalSaveLayer(rec, strategy);
+    this->internalSaveLayer(*rec, strategy);
     return this->getSaveCount() - 1;
 }
 
@@ -1084,7 +1036,7 @@
     int y = src->getOrigin().y() - dstOrigin.y();
     auto special = src->snapSpecial();
     if (special) {
-        dst->drawSpecial(special.get(), x, y, p);
+        dst->drawSpecial(special.get(), x, y, p, nullptr, SkMatrix::I());
     }
 }
 
@@ -1191,8 +1143,7 @@
             return;
         }
     }
-    DeviceCM* layer =
-            new DeviceCM(newDevice.get(), paint, this, stashedMatrix);
+    DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
 
     // only have a "next" if this new layer doesn't affect the clip (rare)
     layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
@@ -1252,7 +1203,9 @@
     if (layer) {
         if (fMCRec) {
             const SkIPoint& origin = layer->fDevice->getOrigin();
-            this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
+            this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
+                                     layer->fPaint.get(),
+                                     layer->fClipImage.get(), layer->fClipMatrix);
             // restore what we smashed in internalSaveLayer
             fMCRec->fMatrix = layer->fStashedMatrix;
             // reset this, since internalDrawDevice will have set it to true
@@ -1345,7 +1298,8 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
+void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
+                                  SkImage* clipImage, const SkMatrix& clipMatrix) {
     SkPaint tmp;
     if (nullptr == paint) {
         paint = &tmp;
@@ -1358,10 +1312,11 @@
         paint = &looper.paint();
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
-        if (filter) {
+        if (filter || clipImage) {
             sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
             if (specialImage) {
-                dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint);
+                dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
+                                    clipImage, clipMatrix);
             }
         } else {
             dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
@@ -1442,26 +1397,6 @@
     this->setMatrix(SkMatrix::I());
 }
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-void SkCanvas::translateZ(SkScalar z) {
-    this->checkForDeferredSave();
-    this->fMCRec->fCurDrawDepth += z;
-    this->didTranslateZ(z);
-}
-
-SkScalar SkCanvas::getZ() const {
-    return this->fMCRec->fCurDrawDepth;
-}
-
-void SkCanvas::setLights(sk_sp<SkLights> lights) {
-    this->fLights = lights;
-}
-
-sk_sp<SkLights> SkCanvas::getLights() const {
-    return this->fLights;
-}
-#endif
-
 //////////////////////////////////////////////////////////////////////////////
 
 void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
@@ -1582,17 +1517,6 @@
 }
 #endif
 
-void SkCanvas::replayClips(ClipVisitor* visitor) const {
-#if 0
-    SkClipStack::B2TIter                iter(*fClipStack);
-    const SkClipStack::Element*         element;
-
-    while ((element = iter.next()) != nullptr) {
-        element->replay(visitor);
-    }
-#endif
-}
-
 bool SkCanvas::androidFramework_isClipAA() const {
     bool containsAA = false;
 
@@ -1768,11 +1692,13 @@
     }
 
     // We don't have this method (yet), but technically this is what we should
-    // be able to assert...
-    // SkASSERT(outer.contains(inner));
+    // be able to return ...
+    // if (!outer.contains(inner))) {
     //
     // For now at least check for containment of bounds
-    SkASSERT(outer.getBounds().contains(inner.getBounds()));
+    if (!outer.getBounds().contains(inner.getBounds())) {
+        return;
+    }
 
     this->onDrawDRRect(outer, inner, paint);
 }
@@ -1784,7 +1710,9 @@
 }
 
 void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
-    this->onDrawRect(r, paint);
+    // To avoid redundant logic in our culling code and various backends, we always sort rects
+    // before passing them along.
+    this->onDrawRect(r.makeSorted(), paint);
 }
 
 void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
@@ -1800,7 +1728,9 @@
 }
 
 void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
-    this->onDrawOval(r, paint);
+    // To avoid redundant logic in our culling code and various backends, we always sort rects
+    // before passing them along.
+    this->onDrawOval(r.makeSorted(), paint);
 }
 
 void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
@@ -1811,16 +1741,6 @@
     this->onDrawPoints(mode, count, pts, paint);
 }
 
-void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint positions[],
-                            const SkPoint texs[], const SkColor colors[], SkBlendMode bmode,
-                            const uint16_t indices[], int indexCount, const SkPaint& paint) {
-    auto vertices = SkVertices::MakeCopy(vmode, vertexCount, positions, texs, colors,
-                                         indexCount, indices);
-    if (vertices) {
-        this->onDrawVerticesObject(vertices.get(), bmode, paint);
-    }
-}
-
 void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
                             const SkPaint& paint) {
     RETURN_ON_NULL(vertices);
@@ -1992,16 +1912,19 @@
     }
 }
 
-void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
-    SkIRect layer_bounds = this->getTopLayerBounds();
-    if (matrix) {
-        *matrix = this->getTotalMatrix();
-        matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
+void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
+    this->onDrawShadowRec(path, rec);
+}
+
+void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+    SkPaint paint;
+    const SkRect& pathBounds = path.getBounds();
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, &pathBounds)
+    while (iter.next()) {
+        iter.fDevice->drawShadow(path, rec);
     }
-    if (clip_bounds) {
-        *clip_bounds = this->getDeviceClipBounds();
-        clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
-    }
+    LOOPER_END
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -2073,14 +1996,10 @@
 
 void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
+    SkASSERT(r.isSorted());
     if (paint.canComputeFastBounds()) {
-        // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
-        // To prevent accidental rejecting at this stage, we have to sort it before we check.
-        SkRect tmp(r);
-        tmp.sort();
-
         SkRect storage;
-        if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
+        if (this->quickReject(paint.computeFastBounds(r, &storage))) {
             return;
         }
     }
@@ -2122,6 +2041,7 @@
 
 void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
+    SkASSERT(oval.isSorted());
     if (paint.canComputeFastBounds()) {
         SkRect storage;
         if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
@@ -2142,6 +2062,7 @@
                          SkScalar sweepAngle, bool useCenter,
                          const SkPaint& paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
+    SkASSERT(oval.isSorted());
     if (paint.canComputeFastBounds()) {
         SkRect storage;
         // Note we're using the entire oval as the bounds.
@@ -2293,7 +2214,8 @@
             iter.fDevice->ctm().mapXY(x, y, &pt);
             iter.fDevice->drawSpecial(special.get(),
                                       SkScalarRoundToInt(pt.fX),
-                                      SkScalarRoundToInt(pt.fY), pnt);
+                                      SkScalarRoundToInt(pt.fY), pnt,
+                                      nullptr, SkMatrix::I());
         } else {
             iter.fDevice->drawImage(image, x, y, pnt);
         }
@@ -2374,7 +2296,8 @@
             iter.fDevice->ctm().mapXY(x, y, &pt);
             iter.fDevice->drawSpecial(special.get(),
                                       SkScalarRoundToInt(pt.fX),
-                                      SkScalarRoundToInt(pt.fY), pnt);
+                                      SkScalarRoundToInt(pt.fY), pnt,
+                                      nullptr, SkMatrix::I());
         } else {
             iter.fDevice->drawBitmap(bitmap, matrix, looper.paint());
         }
@@ -2638,6 +2561,10 @@
     fMCRec->fFilter = drawFilter;
 }
 
+void SkCanvas::drawString(const SkString& string, SkScalar x, SkScalar y, const SkPaint& paint) {
+    this->drawText(string.c_str(), string.size(), x, y, paint);
+}
+
 // These will become non-virtual, so they always call the (virtual) onDraw... method
 void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
                         const SkPaint& paint) {
@@ -2716,10 +2643,12 @@
         return;
     }
 
+    const bool interpColorsLinearly = (this->imageInfo().colorSpace() != nullptr);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
 
     while (iter.next()) {
-        iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint);
+        iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, interpColorsLinearly, paint);
     }
 
     LOOPER_END
@@ -2784,17 +2713,6 @@
 // methods, rather than actually drawing themselves.
 //////////////////////////////////////////////////////////////////////////////
 
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
-    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
-    SkPaint paint;
-
-    paint.setARGB(a, r, g, b);
-    paint.setBlendMode(mode);
-    this->drawPaint(paint);
-}
-#endif
-
 void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
     SkPaint paint;
@@ -2810,18 +2728,6 @@
     this->drawPoints(kPoints_PointMode, 1, &pt, paint);
 }
 
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
-    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
-    SkPoint pt;
-    SkPaint paint;
-
-    pt.set(x, y);
-    paint.setColor(color);
-    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
-}
-#endif
-
 void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
     SkPoint pts[2];
@@ -2831,18 +2737,6 @@
     this->drawPoints(kLines_PointMode, 2, pts, paint);
 }
 
-#ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
-                              SkScalar right, SkScalar bottom,
-                              const SkPaint& paint) {
-    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
-    SkRect  r;
-
-    r.set(left, top, right, bottom);
-    this->drawRect(r, paint);
-}
-#endif
-
 void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
     if (radius < 0) {
@@ -2931,199 +2825,6 @@
     picture->playback(this);
 }
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-void SkCanvas::drawShadowedPicture(const SkPicture* picture,
-                                   const SkMatrix* matrix,
-                                   const SkPaint* paint,
-                                   const SkShadowParams& params) {
-    RETURN_ON_NULL(picture);
-
-    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
-
-    this->onDrawShadowedPicture(picture, matrix, paint, params);
-}
-
-void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
-                                     const SkMatrix* matrix,
-                                     const SkPaint* paint,
-                                     const SkShadowParams& params) {
-    if (!paint || paint->canComputeFastBounds()) {
-        SkRect bounds = picture->cullRect();
-        if (paint) {
-            paint->computeFastBounds(bounds, &bounds);
-        }
-        if (matrix) {
-            matrix->mapRect(&bounds);
-        }
-        if (this->quickReject(bounds)) {
-            return;
-        }
-    }
-
-    SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
-
-    sk_sp<SkImage> povDepthMap;
-    sk_sp<SkImage> diffuseMap;
-
-    // povDepthMap
-    {
-        SkLights::Builder builder;
-        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
-                                                     SkVector3::Make(0.0f, 0.0f, 1.0f)));
-        sk_sp<SkLights> povLight = builder.finish();
-
-        SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
-                                             picture->cullRect().height(),
-                                             kBGRA_8888_SkColorType,
-                                             kOpaque_SkAlphaType);
-
-        // Create a new surface (that matches the backend of canvas)
-        // to create the povDepthMap
-        sk_sp<SkSurface> surf(this->makeSurface(info));
-
-        // Wrap another SPFCanvas around the surface
-        sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
-                sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
-
-        // set the depth map canvas to have the light as the user's POV
-        depthMapCanvas->setLights(std::move(povLight));
-
-        depthMapCanvas->drawPicture(picture);
-        povDepthMap = surf->makeImageSnapshot();
-    }
-
-    // diffuseMap
-    {
-        SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
-                                             picture->cullRect().height(),
-                                             kBGRA_8888_SkColorType,
-                                             kOpaque_SkAlphaType);
-
-        sk_sp<SkSurface> surf(this->makeSurface(info));
-        surf->getCanvas()->drawPicture(picture);
-
-        diffuseMap = surf->makeImageSnapshot();
-    }
-
-    sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
-                                                             SkShader::kClamp_TileMode);
-    sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
-                                                           SkShader::kClamp_TileMode);
-
-    // TODO: pass the depth to the shader in vertices, or uniforms
-    //       so we don't have to render depth and color separately
-    for (int i = 0; i < fLights->numLights(); ++i) {
-        // skip over ambient lights; they don't cast shadows
-        // lights that have shadow maps do not need updating (because lights are immutable)
-        sk_sp<SkImage> depthMap;
-        SkISize shMapSize;
-
-        if (fLights->light(i).getShadowMap() != nullptr) {
-            continue;
-        }
-
-        if (fLights->light(i).isRadial()) {
-            shMapSize.fHeight = 1;
-            shMapSize.fWidth = (int) picture->cullRect().width();
-
-            SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
-                                                 kBGRA_8888_SkColorType,
-                                                 kOpaque_SkAlphaType);
-
-            // Create new surface (that matches the backend of canvas)
-            // for each shadow map
-            sk_sp<SkSurface> surf(this->makeSurface(info));
-
-            // Wrap another SPFCanvas around the surface
-            SkCanvas* depthMapCanvas = surf->getCanvas();
-
-            SkLights::Builder builder;
-            builder.add(fLights->light(i));
-            sk_sp<SkLights> curLight = builder.finish();
-
-            sk_sp<SkShader> shadowMapShader;
-            shadowMapShader = SkRadialShadowMapShader::Make(
-                    povDepthShader, curLight,
-                    (int) picture->cullRect().width(),
-                    (int) picture->cullRect().height());
-
-            SkPaint shadowMapPaint;
-            shadowMapPaint.setShader(std::move(shadowMapShader));
-
-            depthMapCanvas->setLights(curLight);
-
-            depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
-                                                     diffuseMap->height()),
-                                     shadowMapPaint);
-
-            depthMap = surf->makeImageSnapshot();
-
-        } else {
-            // TODO: compute the correct size of the depth map from the light properties
-            // TODO: maybe add a kDepth_8_SkColorType
-            // TODO: find actual max depth of picture
-            shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
-                    fLights->light(i), 255,
-                    (int) picture->cullRect().width(),
-                    (int) picture->cullRect().height());
-
-            SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
-                                                 kBGRA_8888_SkColorType,
-                                                 kOpaque_SkAlphaType);
-
-            // Create a new surface (that matches the backend of canvas)
-            // for each shadow map
-            sk_sp<SkSurface> surf(this->makeSurface(info));
-
-            // Wrap another SPFCanvas around the surface
-            sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
-                    sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
-            depthMapCanvas->setShadowParams(params);
-
-            // set the depth map canvas to have the light we're drawing.
-            SkLights::Builder builder;
-            builder.add(fLights->light(i));
-            sk_sp<SkLights> curLight = builder.finish();
-            depthMapCanvas->setLights(std::move(curLight));
-
-            depthMapCanvas->drawPicture(picture);
-            depthMap = surf->makeImageSnapshot();
-        }
-
-        if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
-            fLights->light(i).setShadowMap(std::move(depthMap));
-        } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
-            // we blur the variance map
-            SkPaint blurPaint;
-            blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
-                                                             params.fShadowRadius, nullptr));
-
-            SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
-                                                     kBGRA_8888_SkColorType,
-                                                     kOpaque_SkAlphaType);
-
-            sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
-
-            blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
-
-            fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
-        }
-    }
-
-    SkPaint shadowPaint;
-    sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
-                                                        std::move(diffuseShader),
-                                                        fLights,
-                                                        diffuseMap->width(),
-                                                        diffuseMap->height(),
-                                                        params);
-
-    shadowPaint.setShader(shadowShader);
-
-    this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
-}
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -3169,10 +2870,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
-
-///////////////////////////////////////////////////////////////////////////////
-
 static bool supported_for_raster_canvas(const SkImageInfo& info) {
     switch (info.alphaType()) {
         case kPremul_SkAlphaType:
@@ -3260,7 +2957,7 @@
 
 SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
     if (fAllocator && fMCRec->fTopLayer->fDevice) {
-        const SkBaseDevice* dev = fMCRec->fTopLayer->fDevice;
+        const auto& dev = fMCRec->fTopLayer->fDevice;
         SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
         SkIPoint origin = dev->getOrigin();
         SkMatrix ctm = this->getTotalMatrix();
diff --git a/src/core/SkClipOpPriv.h b/src/core/SkClipOpPriv.h
index 21695b9..a90f59d 100644
--- a/src/core/SkClipOpPriv.h
+++ b/src/core/SkClipOpPriv.h
@@ -13,9 +13,9 @@
 const SkClipOp kDifference_SkClipOp         = SkClipOp::kDifference;
 const SkClipOp kIntersect_SkClipOp          = SkClipOp::kIntersect;
 
-const SkClipOp kUnion_SkClipOp              = SkClipOp::kUnion_deprecated;
-const SkClipOp kXOR_SkClipOp                = SkClipOp::kXOR_deprecated;
-const SkClipOp kReverseDifference_SkClipOp  = SkClipOp::kReverseDifference_deprecated;
-const SkClipOp kReplace_SkClipOp            = SkClipOp::kReplace_deprecated;
+const SkClipOp kUnion_SkClipOp              = (SkClipOp)2;
+const SkClipOp kXOR_SkClipOp                = (SkClipOp)3;
+const SkClipOp kReverseDifference_SkClipOp  = (SkClipOp)4;
+const SkClipOp kReplace_SkClipOp            = (SkClipOp)5;
 
 #endif
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index a2621a9..8da54e1 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -70,25 +70,6 @@
     }
 }
 
-void SkClipStack::Element::replay(SkCanvasClipVisitor* visitor) const {
-    static const SkRect kEmptyRect = { 0, 0, 0, 0 };
-
-    switch (fType) {
-        case kPath_Type:
-            visitor->clipPath(this->getPath(), this->getOp(), this->isAA());
-            break;
-        case kRRect_Type:
-            visitor->clipRRect(this->getRRect(), this->getOp(), this->isAA());
-            break;
-        case kRect_Type:
-            visitor->clipRect(this->getRect(), this->getOp(), this->isAA());
-            break;
-        case kEmpty_Type:
-            visitor->clipRect(kEmptyRect, kIntersect_SkClipOp, false);
-            break;
-    }
-}
-
 void SkClipStack::Element::invertShapeFillType() {
     switch (fType) {
         case kRect_Type:
diff --git a/src/core/SkClipStack.h b/src/core/SkClipStack.h
index 9360f4d..4502ecd 100644
--- a/src/core/SkClipStack.h
+++ b/src/core/SkClipStack.h
@@ -10,6 +10,7 @@
 
 #include "../private/SkMessageBus.h"
 #include "SkCanvas.h"
+#include "SkClipOpPriv.h"
 #include "SkDeque.h"
 #include "SkPath.h"
 #include "SkRRect.h"
@@ -21,8 +22,6 @@
 #include "GrResourceKey.h"
 #endif
 
-class SkCanvasClipVisitor;
-
 // Because a single save/restore state can have multiple clips, this class
 // stores the stack depth (fSaveCount) and clips (fDeque) separately.
 // Each clip in fDeque stores the stack state to which it belongs
@@ -59,7 +58,7 @@
         static const int kTypeCnt = kLastType + 1;
 
         Element() {
-            this->initCommon(0, SkClipOp::kReplace_deprecated, false);
+            this->initCommon(0, kReplace_SkClipOp, false);
             this->setEmpty();
         }
 
@@ -196,11 +195,6 @@
             return kPath_Type == fType && fPath.get()->isInverseFillType();
         }
 
-        /**
-        * Replay this clip into the visitor.
-        */
-        void replay(SkCanvasClipVisitor*) const;
-
 #ifdef SK_DEBUG
         /**
          * Dumps the element to SkDebugf. This is intended for Skia development debugging
@@ -252,7 +246,7 @@
         mutable SkTArray<std::unique_ptr<GrUniqueKeyInvalidatedMessage>> fMessages;
 #endif
         Element(int saveCount) {
-            this->initCommon(saveCount, SkClipOp::kReplace_deprecated, false);
+            this->initCommon(saveCount, kReplace_SkClipOp, false);
             this->setEmpty();
         }
 
@@ -567,7 +561,7 @@
     void restoreTo(int saveCount);
 
     inline bool hasClipRestriction(SkClipOp op) {
-        return op >= SkClipOp::kUnion_deprecated && !fClipRestrictionRect.isEmpty();
+        return op >= kUnion_SkClipOp && !fClipRestrictionRect.isEmpty();
     }
 
     /**
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 8f660e9..a0b03c4 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -5,16 +5,19 @@
  * found in the LICENSE file.
  */
 
-#include "SkColorFilter.h"
 #include "SkArenaAlloc.h"
+#include "SkColorFilter.h"
+#include "SkColorSpaceXformer.h"
+#include "SkNx.h"
+#include "SkPM4f.h"
+#include "SkRasterPipeline.h"
 #include "SkReadBuffer.h"
 #include "SkRefCnt.h"
 #include "SkString.h"
 #include "SkTDArray.h"
 #include "SkUnPreMultiply.h"
 #include "SkWriteBuffer.h"
-#include "SkPM4f.h"
-#include "SkNx.h"
+#include "../jumper/SkJumper.h"
 
 #if SK_SUPPORT_GPU
 #include "GrFragmentProcessor.h"
@@ -38,33 +41,11 @@
 }
 #endif
 
-bool SkColorFilter::appendStages(SkRasterPipeline* pipeline,
-                                 SkColorSpace* dst,
-                                 SkArenaAlloc* scratch,
+void SkColorFilter::appendStages(SkRasterPipeline* p,
+                                 SkColorSpace* dstCS,
+                                 SkArenaAlloc* alloc,
                                  bool shaderIsOpaque) const {
-    return this->onAppendStages(pipeline, dst, scratch, shaderIsOpaque);
-}
-
-bool SkColorFilter::onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, bool) const {
-    return false;
-}
-
-void SkColorFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f result[]) const {
-    const int N = 128;
-    SkPMColor tmp[N];
-    while (count > 0) {
-        int n = SkTMin(count, N);
-        for (int i = 0; i < n; ++i) {
-            tmp[i] = src[i].toPMColor();
-        }
-        this->filterSpan(tmp, n, tmp);
-        for (int i = 0; i < n; ++i) {
-            result[i] = SkPM4f::FromPMColor(tmp[i]);
-        }
-        src += n;
-        result += n;
-        count -= n;
-    }
+    this->onAppendStages(p, dstCS, alloc, shaderIsOpaque);
 }
 
 SkColor SkColorFilter::filterColor(SkColor c) const {
@@ -73,9 +54,19 @@
     return SkUnPreMultiply::PMColorToColor(dst);
 }
 
+#include "SkRasterPipeline.h"
 SkColor4f SkColorFilter::filterColor4f(const SkColor4f& c) const {
     SkPM4f dst, src = c.premul();
-    this->filterSpan4f(&src, 1, &dst);
+
+    SkSTArenaAlloc<128> alloc;
+    SkRasterPipeline    pipeline(&alloc);
+
+    pipeline.append(SkRasterPipeline::constant_color, &src);
+    this->onAppendStages(&pipeline, nullptr, &alloc, c.fA == 1);
+    SkPM4f* dstPtr = &dst;
+    pipeline.append(SkRasterPipeline::store_f32, &dstPtr);
+    pipeline.run(0,0, 1);
+
     return dst.unpremul();
 }
 
@@ -103,11 +94,6 @@
         fOuter->filterSpan(result, count, result);
     }
 
-    void filterSpan4f(const SkPM4f shader[], int count, SkPM4f result[]) const override {
-        fInner->filterSpan4f(shader, count, result);
-        fOuter->filterSpan4f(result, count, result);
-    }
-
 #ifndef SK_IGNORE_TO_STRING
     void toString(SkString* str) const override {
         SkString outerS, innerS;
@@ -119,6 +105,16 @@
     }
 #endif
 
+    void onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkArenaAlloc* scratch,
+                        bool shaderIsOpaque) const override {
+        bool innerIsOpaque = shaderIsOpaque;
+        if (!(fInner->getFlags() & kAlphaUnchanged_Flag)) {
+            innerIsOpaque = false;
+        }
+        fInner->appendStages(p, dst, scratch, shaderIsOpaque);
+        fOuter->appendStages(p, dst, scratch, innerIsOpaque);
+    }
+
 #if SK_SUPPORT_GPU
     sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext* context,
                                                    SkColorSpace* dstColorSpace) const override {
@@ -155,6 +151,17 @@
         return fComposedFilterCount;
     }
 
+    bool asACompose(SkColorFilter** outer, SkColorFilter** inner) const override {
+        *outer = fOuter.get();
+        *inner = fInner.get();
+        return true;
+    }
+
+    sk_sp<SkColorFilter> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return SkColorFilter::MakeComposeFilter(xformer->apply(fOuter.get()),
+                                                xformer->apply(fInner.get()));
+    }
+
     sk_sp<SkColorFilter> fOuter;
     sk_sp<SkColorFilter> fInner;
     const int            fComposedFilterCount;
diff --git a/src/core/SkColorFilterShader.cpp b/src/core/SkColorFilterShader.cpp
deleted file mode 100644
index 5e96b24..0000000
--- a/src/core/SkColorFilterShader.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkArenaAlloc.h"
-#include "SkColorFilterShader.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkShader.h"
-#include "SkString.h"
-
-#if SK_SUPPORT_GPU
-#include "GrFragmentProcessor.h"
-#endif
-
-SkColorFilterShader::SkColorFilterShader(sk_sp<SkShader> shader, sk_sp<SkColorFilter> filter)
-    : fShader(std::move(shader))
-    , fFilter(std::move(filter))
-{
-    SkASSERT(fShader);
-    SkASSERT(fFilter);
-}
-
-sk_sp<SkFlattenable> SkColorFilterShader::CreateProc(SkReadBuffer& buffer) {
-    auto shader = buffer.readShader();
-    auto filter = buffer.readColorFilter();
-    if (!shader || !filter) {
-        return nullptr;
-    }
-    return sk_make_sp<SkColorFilterShader>(shader, filter);
-}
-
-void SkColorFilterShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeFlattenable(fShader.get());
-    buffer.writeFlattenable(fFilter.get());
-}
-
-uint32_t SkColorFilterShader::FilterShaderContext::getFlags() const {
-    const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader);
-
-    uint32_t shaderF = fShaderContext->getFlags();
-    uint32_t filterF = filterShader.fFilter->getFlags();
-
-    // If the filter does not support a given feature, but sure to clear the corresponding flag
-    // in the shader flags.
-    //
-    if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) {
-        shaderF &= ~SkShader::kOpaqueAlpha_Flag;
-    }
-    return shaderF;
-}
-
-SkShader::Context* SkColorFilterShader::onMakeContext(const ContextRec& rec,
-                                                      SkArenaAlloc* alloc) const {
-    SkShader::Context* shaderContext = fShader->makeContext(rec, alloc);
-    if (nullptr == shaderContext) {
-        return nullptr;
-    }
-    return alloc->make<FilterShaderContext>(*this, shaderContext, rec);
-}
-
-
-SkColorFilterShader::FilterShaderContext::FilterShaderContext(
-                                                         const SkColorFilterShader& filterShader,
-                                                         SkShader::Context* shaderContext,
-                                                         const ContextRec& rec)
-    : INHERITED(filterShader, rec)
-    , fShaderContext(shaderContext)
-{}
-
-void SkColorFilterShader::FilterShaderContext::shadeSpan(int x, int y, SkPMColor result[],
-                                                         int count) {
-    const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader);
-
-    fShaderContext->shadeSpan(x, y, result, count);
-    filterShader.fFilter->filterSpan(result, count, result);
-}
-
-void SkColorFilterShader::FilterShaderContext::shadeSpan4f(int x, int y, SkPM4f result[],
-                                                          int count) {
-    const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader);
-
-    fShaderContext->shadeSpan4f(x, y, result, count);
-    filterShader.fFilter->filterSpan4f(result, count, result);
-}
-
-#if SK_SUPPORT_GPU
-/////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkColorFilterShader::asFragmentProcessor(const AsFPArgs& args) const {
-
-    sk_sp<GrFragmentProcessor> fp1(fShader->asFragmentProcessor(args));
-    if (!fp1) {
-        return nullptr;
-    }
-
-    sk_sp<GrFragmentProcessor> fp2(fFilter->asFragmentProcessor(args.fContext,
-                                                                args.fDstColorSpace));
-    if (!fp2) {
-        return fp1;
-    }
-
-    sk_sp<GrFragmentProcessor> fpSeries[] = { std::move(fp1), std::move(fp2) };
-    return GrFragmentProcessor::RunInSeries(fpSeries, 2);
-}
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkColorFilterShader::toString(SkString* str) const {
-    str->append("SkColorFilterShader: (");
-
-    str->append("Shader: ");
-    fShader->toString(str);
-    str->append(" Filter: ");
-    // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkShader> SkShader::makeWithColorFilter(sk_sp<SkColorFilter> filter) const {
-    SkShader* base = const_cast<SkShader*>(this);
-    if (!filter) {
-        return sk_ref_sp(base);
-    }
-    return sk_make_sp<SkColorFilterShader>(sk_ref_sp(base), filter);
-}
diff --git a/src/core/SkColorFilterShader.h b/src/core/SkColorFilterShader.h
deleted file mode 100644
index e697736..0000000
--- a/src/core/SkColorFilterShader.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkColorFilterShader_DEFINED
-#define SkColorFilterShader_DEFINED
-
-#include "SkColorFilter.h"
-#include "SkShader.h"
-
-class SkArenaAlloc;
-
-class SkColorFilterShader : public SkShader {
-public:
-    SkColorFilterShader(sk_sp<SkShader> shader, sk_sp<SkColorFilter> filter);
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    class FilterShaderContext : public SkShader::Context {
-    public:
-        // Takes ownership of shaderContext and calls its destructor.
-        FilterShaderContext(const SkColorFilterShader&, SkShader::Context*, const ContextRec&);
-
-        uint32_t getFlags() const override;
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-        void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
-
-        void set3DMask(const SkMask* mask) override {
-            // forward to our proxy
-            fShaderContext->set3DMask(mask);
-        }
-
-    private:
-        SkShader::Context* fShaderContext;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterShader)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc* alloc) const override;
-
-private:
-    sk_sp<SkShader>      fShader;
-    sk_sp<SkColorFilter> fFilter;
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/core/SkColorLookUpTable.cpp b/src/core/SkColorLookUpTable.cpp
index 8a55018..b8bb346 100644
--- a/src/core/SkColorLookUpTable.cpp
+++ b/src/core/SkColorLookUpTable.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkColorLookUpTable.h"
+#include "SkColorSpaceXformPriv.h"
 #include "SkFloatingPoint.h"
 
 void SkColorLookUpTable::interp(float* dst, const float* src) const {
@@ -114,11 +115,7 @@
         // look up tables, we don't check for it.
         // And for arbitrary, non-increasing tables, it is easy to see how
         // the output might not be 0-1.  So we clamp here.
-        if (dst[i] > 1.f) {
-            dst[i] = 1.f;
-        } else if (dst[i] < 0.f) {
-            dst[i] = 0.f;
-        }
+        dst[i] = clamp_0_1(dst[i]);
 
         // Increment the table ptr in order to handle the next component.
         // Note that this is the how table is designed: all of nXXX
@@ -158,5 +155,5 @@
     // and recursively LERP all sub-dimensions with the current dimension fixed to the high point
     const float hi = interpDimension(src, inputDimension - 1, outputDimension, index);
     // then LERP the results based on the current dimension
-    return (1 - diff) * lo + diff * hi;
+    return clamp_0_1((1 - diff) * lo + diff * hi);
 }
diff --git a/src/core/SkColorLookUpTable.h b/src/core/SkColorLookUpTable.h
index 0cde767..09d8188 100644
--- a/src/core/SkColorLookUpTable.h
+++ b/src/core/SkColorLookUpTable.h
@@ -21,6 +21,10 @@
         : fInputChannels(inputChannels) {
         SkASSERT(inputChannels >= 1 && inputChannels <= kMaxColorChannels);
         memcpy(fGridPoints, gridPoints, fInputChannels * sizeof(uint8_t));
+
+        for (int i = 0; i < inputChannels; i++) {
+            SkASSERT(fGridPoints[i] > 1);
+        }
     }
 
     /**
@@ -36,7 +40,7 @@
     int inputChannels() const { return fInputChannels; }
 
     int outputChannels() const { return kOutputChannels; }
-    
+
     int gridPoints(int dimension) const {
         SkASSERT(dimension >= 0 && dimension < inputChannels());
         return fGridPoints[dimension];
diff --git a/src/core/SkColorMatrixFilterRowMajor255.cpp b/src/core/SkColorMatrixFilterRowMajor255.cpp
index 6809890..aa11746 100644
--- a/src/core/SkColorMatrixFilterRowMajor255.cpp
+++ b/src/core/SkColorMatrixFilterRowMajor255.cpp
@@ -137,24 +137,6 @@
     filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst);
 }
 
-struct SkPM4fAdaptor {
-    enum {
-        R = SkPM4f::R,
-        G = SkPM4f::G,
-        B = SkPM4f::B,
-        A = SkPM4f::A,
-    };
-    static SkPM4f From4f(const Sk4f& c4) {
-        return SkPM4f::From4f(c4);
-    }
-    static Sk4f To4f(const SkPM4f& c) {
-        return c.to4f();
-    }
-};
-void SkColorMatrixFilterRowMajor255::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
-    filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const {
@@ -231,7 +213,7 @@
 //  End duplication
 //////
 
-bool SkColorMatrixFilterRowMajor255::onAppendStages(SkRasterPipeline* p,
+void SkColorMatrixFilterRowMajor255::onAppendStages(SkRasterPipeline* p,
                                                     SkColorSpace* dst,
                                                     SkArenaAlloc* scratch,
                                                     bool shaderIsOpaque) const {
@@ -254,7 +236,6 @@
     if (!willStayOpaque) { p->append(SkRasterPipeline::premul); }
     if (    needsClamp0) { p->append(SkRasterPipeline::clamp_0); }
     if (    needsClamp1) { p->append(SkRasterPipeline::clamp_a); }
-    return true;
 }
 
 sk_sp<SkColorFilter>
@@ -320,7 +301,7 @@
 
     protected:
         void onSetData(const GrGLSLProgramDataManager& uniManager,
-                       const GrProcessor& proc) override {
+                       const GrFragmentProcessor& proc) override {
             const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
             const float* m = cme.fMatrix;
             // The GL matrix is transposed from SkColorMatrix.
diff --git a/src/core/SkColorMatrixFilterRowMajor255.h b/src/core/SkColorMatrixFilterRowMajor255.h
index 5c2d616..43e1851 100644
--- a/src/core/SkColorMatrixFilterRowMajor255.h
+++ b/src/core/SkColorMatrixFilterRowMajor255.h
@@ -19,7 +19,6 @@
     static sk_sp<SkColorFilter> MakeSingleChannelOutput(const SkScalar row[5]);
 
     void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override;
-    void filterSpan4f(const SkPM4f src[], int count, SkPM4f[]) const override;
     uint32_t getFlags() const override;
     bool asColorMatrix(SkScalar matrix[20]) const override;
     sk_sp<SkColorFilter> makeComposed(sk_sp<SkColorFilter>) const override;
@@ -36,7 +35,7 @@
     void flatten(SkWriteBuffer&) const override;
 
 private:
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+    void onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         bool shaderIsOpaque) const override;
 
     SkScalar        fMatrix[20];
diff --git a/src/core/SkColorShader.cpp b/src/core/SkColorShader.cpp
deleted file mode 100644
index 5c8bde6..0000000
--- a/src/core/SkColorShader.cpp
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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 "SkArenaAlloc.h"
-#include "SkColorShader.h"
-#include "SkColorSpace.h"
-#include "SkPM4fPriv.h"
-#include "SkRasterPipeline.h"
-#include "SkReadBuffer.h"
-#include "SkUtils.h"
-
-SkColorShader::SkColorShader(SkColor c) : fColor(c) {}
-
-bool SkColorShader::isOpaque() const {
-    return SkColorGetA(fColor) == 255;
-}
-
-sk_sp<SkFlattenable> SkColorShader::CreateProc(SkReadBuffer& buffer) {
-    return sk_make_sp<SkColorShader>(buffer.readColor());
-}
-
-void SkColorShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeColor(fColor);
-}
-
-uint32_t SkColorShader::ColorShaderContext::getFlags() const {
-    return fFlags;
-}
-
-SkShader::Context* SkColorShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
-    return alloc->make<ColorShaderContext>(*this, rec);
-}
-
-SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shader,
-                                                      const ContextRec& rec)
-    : INHERITED(shader, rec)
-{
-    SkColor color = shader.fColor;
-    unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha()));
-
-    unsigned r = SkColorGetR(color);
-    unsigned g = SkColorGetG(color);
-    unsigned b = SkColorGetB(color);
-
-    if (a != 255) {
-        r = SkMulDiv255Round(r, a);
-        g = SkMulDiv255Round(g, a);
-        b = SkMulDiv255Round(b, a);
-    }
-    fPMColor = SkPackARGB32(a, r, g, b);
-
-    SkColor4f c4 = SkColor4f::FromColor(shader.fColor);
-    c4.fA *= rec.fPaint->getAlpha() / 255.0f;
-    fPM4f = c4.premul();
-
-    fFlags = kConstInY32_Flag;
-    if (255 == a) {
-        fFlags |= kOpaqueAlpha_Flag;
-    }
-}
-
-void SkColorShader::ColorShaderContext::shadeSpan(int x, int y, SkPMColor span[], int count) {
-    sk_memset32(span, fPMColor, count);
-}
-
-void SkColorShader::ColorShaderContext::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
-    memset(alpha, SkGetPackedA32(fPMColor), count);
-}
-
-void SkColorShader::ColorShaderContext::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
-    for (int i = 0; i < count; ++i) {
-        span[i] = fPM4f;
-    }
-}
-
-SkShader::GradientType SkColorShader::asAGradient(GradientInfo* info) const {
-    if (info) {
-        if (info->fColors && info->fColorCount >= 1) {
-            info->fColors[0] = fColor;
-        }
-        info->fColorCount = 1;
-        info->fTileMode = SkShader::kRepeat_TileMode;
-    }
-    return kColor_GradientType;
-}
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "effects/GrConstColorProcessor.h"
-sk_sp<GrFragmentProcessor> SkColorShader::asFragmentProcessor(const AsFPArgs& args) const {
-    GrColor4f color = SkColorToPremulGrColor4f(fColor, args.fDstColorSpace);
-    return GrConstColorProcessor::Make(color, GrConstColorProcessor::kModulateA_InputMode);
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkColorShader::toString(SkString* str) const {
-    str->append("SkColorShader: (");
-
-    str->append("Color: ");
-    str->appendHex(fColor);
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static unsigned unit_to_byte(float unit) {
-    SkASSERT(unit >= 0 && unit <= 1);
-    return (unsigned)(unit * 255 + 0.5);
-}
-
-static SkColor unit_to_skcolor(const SkColor4f& unit, SkColorSpace* cs) {
-    return SkColorSetARGB(unit_to_byte(unit.fA), unit_to_byte(unit.fR),
-                          unit_to_byte(unit.fG), unit_to_byte(unit.fB));
-}
-
-SkColor4Shader::SkColor4Shader(const SkColor4f& color, sk_sp<SkColorSpace> space)
-    : fColorSpace(std::move(space))
-    , fColor4(color)
-    , fCachedByteColor(unit_to_skcolor(color.pin(), space.get()))
-{}
-
-sk_sp<SkFlattenable> SkColor4Shader::CreateProc(SkReadBuffer& buffer) {
-    SkColor4f color;
-    buffer.readColor4f(&color);
-    if (buffer.readBool()) {
-        // TODO how do we unflatten colorspaces
-    }
-    return SkShader::MakeColorShader(color, nullptr);
-}
-
-void SkColor4Shader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeColor4f(fColor4);
-    buffer.writeBool(false);    // TODO how do we flatten colorspaces?
-}
-
-uint32_t SkColor4Shader::Color4Context::getFlags() const {
-    return fFlags;
-}
-
-SkShader::Context* SkColor4Shader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
-    return alloc->make<Color4Context>(*this, rec);
-}
-
-SkColor4Shader::Color4Context::Color4Context(const SkColor4Shader& shader,
-                                                      const ContextRec& rec)
-: INHERITED(shader, rec)
-{
-    SkColor color = shader.fCachedByteColor;
-    unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha()));
-
-    unsigned r = SkColorGetR(color);
-    unsigned g = SkColorGetG(color);
-    unsigned b = SkColorGetB(color);
-
-    if (a != 255) {
-        r = SkMulDiv255Round(r, a);
-        g = SkMulDiv255Round(g, a);
-        b = SkMulDiv255Round(b, a);
-    }
-    fPMColor = SkPackARGB32(a, r, g, b);
-
-    SkColor4f c4 = shader.fColor4;
-    c4.fA *= rec.fPaint->getAlpha() * (1 / 255.0f);
-    fPM4f = c4.premul();
-
-    fFlags = kConstInY32_Flag;
-    if (255 == a) {
-        fFlags |= kOpaqueAlpha_Flag;
-    }
-}
-
-void SkColor4Shader::Color4Context::shadeSpan(int x, int y, SkPMColor span[], int count) {
-    sk_memset32(span, fPMColor, count);
-}
-
-void SkColor4Shader::Color4Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
-    memset(alpha, SkGetPackedA32(fPMColor), count);
-}
-
-void SkColor4Shader::Color4Context::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
-    for (int i = 0; i < count; ++i) {
-        span[i] = fPM4f;
-    }
-}
-
-// TODO: do we need an updated version of this method for color4+colorspace?
-SkShader::GradientType SkColor4Shader::asAGradient(GradientInfo* info) const {
-    if (info) {
-        if (info->fColors && info->fColorCount >= 1) {
-            info->fColors[0] = fCachedByteColor;
-        }
-        info->fColorCount = 1;
-        info->fTileMode = SkShader::kRepeat_TileMode;
-    }
-    return kColor_GradientType;
-}
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "effects/GrConstColorProcessor.h"
-#include "GrColorSpaceXform.h"
-sk_sp<GrFragmentProcessor> SkColor4Shader::asFragmentProcessor(const AsFPArgs& args) const {
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    GrColor4f color = GrColor4f::FromSkColor4f(fColor4);
-    if (colorSpaceXform) {
-        color = colorSpaceXform->apply(color);
-    }
-    return GrConstColorProcessor::Make(color.premul(), GrConstColorProcessor::kModulateA_InputMode);
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkColor4Shader::toString(SkString* str) const {
-    str->append("SkColor4Shader: (");
-
-    str->append("RGBA:");
-    for (int i = 0; i < 4; ++i) {
-        str->appendf(" %g", fColor4.vec()[i]);
-    }
-    str->append(" )");
-}
-#endif
-
-sk_sp<SkShader> SkShader::MakeColorShader(const SkColor4f& color, sk_sp<SkColorSpace> space) {
-    if (!SkScalarsAreFinite(color.vec(), 4)) {
-        return nullptr;
-    }
-    return sk_make_sp<SkColor4Shader>(color, std::move(space));
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static void D32_BlitBW(SkShader::Context::BlitState* state, int x, int y, const SkPixmap& dst,
-                       int count) {
-    SkXfermode::D32Proc proc = (SkXfermode::D32Proc)state->fStorage[0];
-    const SkPM4f* src = (const SkPM4f*)state->fStorage[1];
-    proc(state->fMode, dst.writable_addr32(x, y), src, count, nullptr);
-}
-
-static void D32_BlitAA(SkShader::Context::BlitState* state, int x, int y, const SkPixmap& dst,
-                       int count, const SkAlpha aa[]) {
-    SkXfermode::D32Proc proc = (SkXfermode::D32Proc)state->fStorage[0];
-    const SkPM4f* src = (const SkPM4f*)state->fStorage[1];
-    proc(state->fMode, dst.writable_addr32(x, y), src, count, aa);
-}
-
-static void F16_BlitBW(SkShader::Context::BlitState* state, int x, int y, const SkPixmap& dst,
-                       int count) {
-    SkXfermode::F16Proc proc = (SkXfermode::F16Proc)state->fStorage[0];
-    const SkPM4f* src = (const SkPM4f*)state->fStorage[1];
-    proc(state->fMode, dst.writable_addr64(x, y), src, count, nullptr);
-}
-
-static void F16_BlitAA(SkShader::Context::BlitState* state, int x, int y, const SkPixmap& dst,
-                       int count, const SkAlpha aa[]) {
-    SkXfermode::F16Proc proc = (SkXfermode::F16Proc)state->fStorage[0];
-    const SkPM4f* src = (const SkPM4f*)state->fStorage[1];
-    proc(state->fMode, dst.writable_addr64(x, y), src, count, aa);
-}
-
-static bool choose_blitprocs(const SkPM4f* pm4, const SkImageInfo& info,
-                             SkShader::Context::BlitState* state) {
-    uint32_t flags = SkXfermode::kSrcIsSingle_D32Flag;
-    if (pm4->a() == 1) {
-        flags |= SkXfermode::kSrcIsOpaque_D32Flag;
-    }
-    switch (info.colorType()) {
-        case kN32_SkColorType:
-            if (info.gammaCloseToSRGB()) {
-                flags |= SkXfermode::kDstIsSRGB_D32Flag;
-            }
-            state->fStorage[0] = (void*)SkXfermode::GetD32Proc(state->fMode, flags);
-            state->fStorage[1] = (void*)pm4;
-            state->fBlitBW = D32_BlitBW;
-            state->fBlitAA = D32_BlitAA;
-            return true;
-        case kRGBA_F16_SkColorType:
-            state->fStorage[0] = (void*)SkXfermode::GetF16Proc(state->fMode, flags);
-            state->fStorage[1] = (void*)pm4;
-            state->fBlitBW = F16_BlitBW;
-            state->fBlitAA = F16_BlitAA;
-            return true;
-        default:
-            return false;
-    }
-}
-
-bool SkColorShader::ColorShaderContext::onChooseBlitProcs(const SkImageInfo& info,
-                                                          BlitState* state) {
-    return choose_blitprocs(&fPM4f, info, state);
-}
-
-bool SkColor4Shader::Color4Context::onChooseBlitProcs(const SkImageInfo& info, BlitState* state) {
-    return choose_blitprocs(&fPM4f, info, state);
-}
-
-bool SkColorShader::onAppendStages(SkRasterPipeline* p,
-                                   SkColorSpace* dst,
-                                   SkArenaAlloc* scratch,
-                                   const SkMatrix&,
-                                   const SkPaint&,
-                                   const SkMatrix*) const {
-    auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
-    p->append(SkRasterPipeline::constant_color, color);
-    return append_gamut_transform(p, scratch,
-                                  SkColorSpace::MakeSRGB().get(), dst);
-}
-
-bool SkColor4Shader::onAppendStages(SkRasterPipeline* p,
-                                    SkColorSpace* dst,
-                                    SkArenaAlloc* scratch,
-                                    const SkMatrix&,
-                                    const SkPaint&,
-                                    const SkMatrix*) const {
-    auto color = scratch->make<SkPM4f>(fColor4.premul());
-    p->append(SkRasterPipeline::constant_color, color);
-    return append_gamut_transform(p, scratch, fColorSpace.get(), dst);
-}
diff --git a/src/core/SkColorShader.h b/src/core/SkColorShader.h
deleted file mode 100644
index b9db657..0000000
--- a/src/core/SkColorShader.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2007 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkColorShader_DEFINED
-#define SkColorShader_DEFINED
-
-#include "SkShader.h"
-#include "SkPM4f.h"
-
-/** \class SkColorShader
-    A Shader that represents a single color. In general, this effect can be
-    accomplished by just using the color field on the paint, but if an
-    actual shader object is needed, this provides that feature.
-*/
-class SK_API SkColorShader : public SkShader {
-public:
-    /** Create a ColorShader that ignores the color in the paint, and uses the
-        specified color. Note: like all shaders, at draw time the paint's alpha
-        will be respected, and is applied to the specified color.
-    */
-    explicit SkColorShader(SkColor c);
-
-    bool isOpaque() const override;
-    bool isConstant() const override { return true; }
-
-    class ColorShaderContext : public SkShader::Context {
-    public:
-        ColorShaderContext(const SkColorShader& shader, const ContextRec&);
-
-        uint32_t getFlags() const override;
-        void shadeSpan(int x, int y, SkPMColor span[], int count) override;
-        void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override;
-        void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
-
-    protected:
-        bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override;
-
-    private:
-        SkPM4f      fPM4f;
-        SkPMColor   fPMColor;
-        uint32_t    fFlags;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    GradientType asAGradient(GradientInfo* info) const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
-
-protected:
-    SkColorShader(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override;
-
-    bool onAsLuminanceColor(SkColor* lum) const override {
-        *lum = fColor;
-        return true;
-    }
-
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                        const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
-
-private:
-    SkColor fColor;
-
-    typedef SkShader INHERITED;
-};
-
-class SkColor4Shader : public SkShader {
-public:
-    SkColor4Shader(const SkColor4f&, sk_sp<SkColorSpace>);
-
-    bool isOpaque() const override {
-        return SkColorGetA(fCachedByteColor) == 255;
-    }
-    bool isConstant() const override { return true; }
-
-    class Color4Context : public SkShader::Context {
-    public:
-        Color4Context(const SkColor4Shader& shader, const ContextRec&);
-
-        uint32_t getFlags() const override;
-        void shadeSpan(int x, int y, SkPMColor span[], int count) override;
-        void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override;
-        void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
-
-    protected:
-        bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override;
-
-    private:
-        SkPM4f      fPM4f;
-        SkPMColor   fPMColor;
-        uint32_t    fFlags;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    GradientType asAGradient(GradientInfo* info) const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
-
-protected:
-    SkColor4Shader(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-    bool onAsLuminanceColor(SkColor* lum) const override {
-        *lum = fCachedByteColor;
-        return true;
-    }
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                        const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
-
-private:
-    sk_sp<SkColorSpace> fColorSpace;
-    const SkColor4f     fColor4;
-    const SkColor       fCachedByteColor;
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp
index 82b442b..8887bc2 100644
--- a/src/core/SkColorSpaceXform.cpp
+++ b/src/core/SkColorSpaceXform.cpp
@@ -1137,13 +1137,25 @@
                                                      alphaType);
 }
 
+bool SkColorSpaceXform::Apply(SkColorSpace* dstCS, ColorFormat dstFormat, void* dst,
+                              SkColorSpace* srcCS, ColorFormat srcFormat, const void* src,
+                              int count, AlphaOp op) {
+    SkAlphaType at;
+    switch (op) {
+        case kPreserve_AlphaOp:    at = kUnpremul_SkAlphaType; break;
+        case kPremul_AlphaOp:      at = kPremul_SkAlphaType;   break;
+        case kSrcIsOpaque_AlphaOp: at = kOpaque_SkAlphaType;   break;
+    }
+    return New(srcCS, dstCS)->apply(dstFormat, dst, srcFormat, src, count, at);
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 template <ColorSpaceMatch kCSM>
 bool SkColorSpaceXform_XYZ<kCSM>
 ::applyPipeline(ColorFormat dstColorFormat, void* dst, ColorFormat srcColorFormat,
                 const void* src, int len, SkAlphaType alphaType) const {
-    SkRasterPipeline pipeline;
+    SkRasterPipeline_<256> pipeline;
 
     LoadTablesContext loadTables;
     switch (srcColorFormat) {
@@ -1227,9 +1239,8 @@
     if (kNone_ColorSpaceMatch == kCSM) {
         pipeline.append(SkRasterPipeline::matrix_3x4, fSrcToDst);
 
-        if (kRGBA_8888_ColorFormat == dstColorFormat ||
-            kBGRA_8888_ColorFormat == dstColorFormat ||
-            kBGR_565_ColorFormat == dstColorFormat)
+        if (kRGBA_F16_ColorFormat != dstColorFormat &&
+            kRGBA_F32_ColorFormat != dstColorFormat)
         {
             bool need_clamp_0, need_clamp_1;
             analyze_3x4_matrix(fSrcToDst, &need_clamp_0, &need_clamp_1);
@@ -1245,12 +1256,17 @@
     }
 
     TablesContext tables;
+    SkColorSpaceTransferFn to_2dot2 = {0,0,0,0,0,0,0};
+    to_2dot2.fG = 1/2.2f;
+    to_2dot2.fA = 1;
     switch (fDstGamma) {
         case kSRGB_DstGamma:
             pipeline.append(SkRasterPipeline::to_srgb);
             break;
         case k2Dot2_DstGamma:
-            pipeline.append(SkRasterPipeline::to_2dot2);
+            pipeline.append(SkRasterPipeline::parametric_r, &to_2dot2);
+            pipeline.append(SkRasterPipeline::parametric_g, &to_2dot2);
+            pipeline.append(SkRasterPipeline::parametric_b, &to_2dot2);
             break;
         case kTable_DstGamma:
             tables.fR = fDstGammaTables[0];
@@ -1297,7 +1313,7 @@
             return false;
     }
 
-    pipeline.run(0, len);
+    pipeline.run(0,0, len);
     return true;
 }
 
diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp
index bedefd4..5305528 100644
--- a/src/core/SkColorSpaceXformCanvas.cpp
+++ b/src/core/SkColorSpaceXformCanvas.cpp
@@ -8,9 +8,11 @@
 #include "SkColorFilter.h"
 #include "SkColorSpaceXformCanvas.h"
 #include "SkColorSpaceXformer.h"
+#include "SkDrawShadowRec.h"
 #include "SkGradientShader.h"
-#include "SkImage_Base.h"
+#include "SkImageFilter.h"
 #include "SkImagePriv.h"
+#include "SkImage_Base.h"
 #include "SkMakeUnique.h"
 #include "SkNoDrawCanvas.h"
 #include "SkSurface.h"
@@ -38,7 +40,7 @@
     {
         // Set the matrix and clip to match |fTarget|.  Otherwise, we'll answer queries for
         // bounds/matrix differently than |fTarget| would.
-        SkCanvas::onClipRect(SkRect::MakeFromIRect(fTarget->getDeviceClipBounds()),
+        SkCanvas::onClipRect(SkRect::Make(fTarget->getDeviceClipBounds()),
                              SkClipOp::kIntersect, kHard_ClipEdgeStyle);
         SkCanvas::setMatrix(fTarget->getTotalMatrix());
     }
@@ -216,8 +218,11 @@
         fTarget->drawImageLattice(fXformer->apply(bitmap).get(), lattice, dst,
                                   MaybePaint(paint, fXformer.get()));
     }
-
-    // TODO: May not be ideal to unfurl pictures.
+    void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override {
+        SkDrawShadowRec newRec(rec);
+        newRec.fColor = fXformer->apply(rec.fColor);
+        fTarget->private_draw_shadow_rec(path, newRec);
+    }
     void onDrawPicture(const SkPicture* pic,
                        const SkMatrix* matrix,
                        const SkPaint* paint) override {
@@ -228,10 +233,14 @@
     }
 
     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
+        sk_sp<SkImageFilter> backdrop = rec.fBackdrop ? fXformer->apply(rec.fBackdrop) : nullptr;
+        sk_sp<SkImage> clipMask = rec.fClipMask ? fXformer->apply(rec.fClipMask) : nullptr;
         fTarget->saveLayer({
             rec.fBounds,
             MaybePaint(rec.fPaint, fXformer.get()),
-            rec.fBackdrop,  // TODO: this is an image filter
+            backdrop.get(),
+            clipMask.get(),
+            rec.fClipMatrix,
             rec.fSaveLayerFlags,
         });
         return kNoLayer_SaveLayerStrategy;
@@ -294,6 +303,7 @@
         return false;
     }
 
+    GrContext* getGrContext() override { return fTarget->getGrContext(); }
     bool onGetProps(SkSurfaceProps* props) const override { return fTarget->getProps(props); }
     void onFlush() override { return fTarget->flush(); }
 
diff --git a/src/core/SkColorSpaceXformImageGenerator.cpp b/src/core/SkColorSpaceXformImageGenerator.cpp
new file mode 100644
index 0000000..d9bec78
--- /dev/null
+++ b/src/core/SkColorSpaceXformImageGenerator.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "SkColorSpaceXformImageGenerator.h"
+
+std::unique_ptr<SkImageGenerator> SkColorSpaceXformImageGenerator::Make(
+        const SkBitmap& src, sk_sp<SkColorSpace> dst, SkCopyPixelsMode mode) {
+    if (!dst) {
+        return nullptr;
+    }
+
+    const SkBitmap* srcPtr = &src;
+    SkBitmap copy;
+    if (kAlways_SkCopyPixelsMode == mode ||
+            (kNever_SkCopyPixelsMode != mode && !src.isImmutable())) {
+        if (!copy.tryAllocPixels(src.info())) {
+            return nullptr;
+        }
+
+        SkAssertResult(src.readPixels(copy.info(), copy.getPixels(), copy.rowBytes(), 0, 0));
+        copy.setImmutable();
+        srcPtr = &copy;
+    }
+
+
+    return std::unique_ptr<SkImageGenerator>(
+            new SkColorSpaceXformImageGenerator(*srcPtr, std::move(dst)));
+}
+
+SkColorSpaceXformImageGenerator::SkColorSpaceXformImageGenerator(const SkBitmap& src,
+                                                                 sk_sp<SkColorSpace> dst)
+    : INHERITED(src.info().makeColorSpace(dst), kNeedNewImageUniqueID)
+    , fSrc(src)
+    , fDst(dst)
+{}
+
+bool SkColorSpaceXformImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels,
+                                                  size_t rowBytes, const Options& opts) {
+    SkImageInfo dstInfo = info;
+    if (!info.colorSpace()) {
+        dstInfo = dstInfo.makeColorSpace(fDst);
+    }
+    return fSrc.readPixels(dstInfo, pixels, rowBytes, 0, 0, opts.fBehavior);
+}
+
+#if SK_SUPPORT_GPU
+
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrPaint.h"
+#include "GrRenderTargetContext.h"
+#include "GrTextureProxy.h"
+#include "SkGr.h"
+#include "effects/GrNonlinearColorSpaceXformEffect.h"
+
+sk_sp<GrTextureProxy> SkColorSpaceXformImageGenerator::onGenerateTexture(GrContext* ctx,
+                                                                         const SkImageInfo& info,
+                                                                         const SkIPoint& origin) {
+    // FIXME:
+    // This always operates as if SkTranferFunctionBehavior is kIgnore.  Should we add
+    // options so that caller can also request kRespect?
+
+    SkASSERT(ctx);
+
+    sk_sp<GrTextureProxy> proxy =
+            GrUploadBitmapToTextureProxy(ctx->resourceProvider(), fSrc, nullptr);
+
+    sk_sp<SkColorSpace> srcSpace =
+            fSrc.colorSpace() ? sk_ref_sp(fSrc.colorSpace()) : SkColorSpace::MakeSRGB();
+    auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), fDst.get());
+    if (!xform) {
+        return nullptr;
+    }
+
+    sk_sp<GrRenderTargetContext> renderTargetContext = ctx->makeDeferredRenderTargetContext(
+            SkBackingFit::kExact, fSrc.width(), fSrc.height(), kRGBA_8888_GrPixelConfig, nullptr);
+    if (!renderTargetContext) {
+        return nullptr;
+    }
+
+    GrPaint paint;
+    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint.addColorTextureProcessor(ctx->resourceProvider(), proxy, nullptr,
+            SkMatrix::MakeTrans(origin.fX, origin.fY));
+    paint.addColorFragmentProcessor(std::move(xform));
+
+    const SkRect rect = SkRect::MakeWH(info.width(), info.height());
+    renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
+    return sk_ref_sp(renderTargetContext->asTextureProxy());
+}
+
+#endif
diff --git a/src/core/SkColorSpaceXformImageGenerator.h b/src/core/SkColorSpaceXformImageGenerator.h
new file mode 100644
index 0000000..29bf85b
--- /dev/null
+++ b/src/core/SkColorSpaceXformImageGenerator.h
@@ -0,0 +1,40 @@
+/*
+ * 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 SkColorSpaceXformImageGenerator_DEFINED
+#define SkColorSpaceXformImageGenerator_DEFINED
+
+#include "SkImageGenerator.h"
+#include "SkImagePriv.h"
+
+class SkColorSpaceXformImageGenerator : public SkImageGenerator {
+public:
+
+    static std::unique_ptr<SkImageGenerator> Make(
+            const SkBitmap& src, sk_sp<SkColorSpace> dst, SkCopyPixelsMode);
+
+protected:
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+                     const Options& opts) override;
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
+                                            const SkIPoint&) override;
+#endif
+
+private:
+    SkBitmap            fSrc;
+    sk_sp<SkColorSpace> fDst;
+
+    SkColorSpaceXformImageGenerator(const SkBitmap& src, sk_sp<SkColorSpace> dst);
+
+    friend class SkImageGenerator;
+
+    typedef SkImageGenerator INHERITED;
+};
+
+#endif // SkColorSpaceXformImageGenerator_DEFINED
diff --git a/src/core/SkColorSpaceXformPriv.h b/src/core/SkColorSpaceXformPriv.h
index c020a0f..405b1da 100644
--- a/src/core/SkColorSpaceXformPriv.h
+++ b/src/core/SkColorSpaceXformPriv.h
@@ -39,6 +39,8 @@
 }
 
 static inline float clamp_0_1(float v) {
+    // The ordering of the logic is a little strange here in order
+    // to make sure we convert NaNs to 0.
     if (v >= 1.0f) {
         return 1.0f;
     } else if (v >= 0.0f) {
diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp
index 85fd42c..2e710f5 100644
--- a/src/core/SkColorSpaceXform_A2B.cpp
+++ b/src/core/SkColorSpaceXform_A2B.cpp
@@ -16,10 +16,11 @@
 #include "SkNx.h"
 #include "SkSRGB.h"
 #include "SkTypes.h"
+#include "../jumper/SkJumper.h"
 
 bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorFormat srcFormat,
                                     const void* src, int count, SkAlphaType alphaType) const {
-    SkRasterPipeline pipeline;
+    SkRasterPipeline_<256> pipeline;
     switch (srcFormat) {
         case kBGRA_8888_ColorFormat:
             pipeline.append(SkRasterPipeline::load_8888, &src);
@@ -74,7 +75,7 @@
         default:
             return false;
     }
-    pipeline.run(0,count);
+    pipeline.run(0,0, count);
 
     return true;
 }
@@ -97,7 +98,8 @@
 
 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
                                              SkColorSpace_XYZ* dstSpace)
-    : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
+    : fElementsPipeline(&fAlloc)
+    , fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
 #if (SkCSXformPrintfDefined)
     static const char* debugGammaNamed[4] = {
         "Linear", "SRGB", "2.2", "NonStandard"
@@ -111,13 +113,22 @@
         case SkColorSpace_Base::kRGB_ICCTypeFlag:
             currentChannels = 3;
             break;
-        case SkColorSpace_Base::kCMYK_ICCTypeFlag:
+        case SkColorSpace_Base::kCMYK_ICCTypeFlag: {
             currentChannels = 4;
             // CMYK images from JPEGs (the only format that supports it) are actually
             // inverted CMYK, so we need to invert every channel.
             // TransferFn is y = -x + 1 for x < 1.f, otherwise 0x + 0, ie y = 1 - x for x in [0,1]
-            this->addTransferFns({1.f, 0.f, 0.f, -1.f, 1.f, 0.f, 1.f}, 4);
+            SkColorSpaceTransferFn fn = {0,0,0,0,0,0,0};
+            fn.fG =  1;
+            fn.fA =  0;
+            fn.fB =  0;
+            fn.fC = -1;
+            fn.fD =  1;
+            fn.fE =  0;
+            fn.fF =  1;
+            this->addTransferFns(fn,4);
             break;
+        }
         default:
             currentChannels = 0;
             SkASSERT(false);
@@ -133,18 +144,12 @@
                     break;
                 }
 
-                // take the fast path for 3-channel named gammas
-                if (3 == currentChannels) {
-                    if (k2Dot2Curve_SkGammaNamed == e.gammaNamed()) {
-                        SkCSXformPrintf("fast path from 2.2\n");
-                        fElementsPipeline.append(SkRasterPipeline::from_2dot2);
-                        break;
-                    } else if (kSRGB_SkGammaNamed == e.gammaNamed()) {
-                        SkCSXformPrintf("fast path from sRGB\n");
-                        // Images should always start the pipeline as unpremul
-                        fElementsPipeline.append_from_srgb(kUnpremul_SkAlphaType);
-                        break;
-                    }
+                // Take the fast path for ordinary sRGB.
+                if (3 == currentChannels && kSRGB_SkGammaNamed == e.gammaNamed()) {
+                    SkCSXformPrintf("fast path from sRGB\n");
+                    // Images should always start the pipeline as unpremul
+                    fElementsPipeline.append_from_srgb(kUnpremul_SkAlphaType);
+                    break;
                 }
 
                 SkCSXformPrintf("Gamma stage added: %s\n", debugGammaNamed[(int)e.gammaNamed()]);
@@ -167,8 +172,8 @@
                                 gammas.data(channel).fTable.fSize,
                         };
 
+                        gammaNeedsRef |= !this->buildTableFn(&table);
                         this->addTableFn(table, channel);
-                        gammaNeedsRef = true;
                     } else {
                         SkColorSpaceTransferFn fn;
                         SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
@@ -176,17 +181,36 @@
                     }
                 }
                 if (gammaNeedsRef) {
-                    fGammaRefs.push_back(sk_ref_sp(&gammas));
+                    this->copy(sk_ref_sp(&gammas));
                 }
                 break;
             }
-            case SkColorSpace_A2B::Element::Type::kCLUT:
+            case SkColorSpace_A2B::Element::Type::kCLUT: {
                 SkCSXformPrintf("CLUT (%d -> %d) stage added\n", e.colorLUT().inputChannels(),
                                                                  e.colorLUT().outputChannels());
-                fCLUTs.push_back(sk_ref_sp(&e.colorLUT()));
-                fElementsPipeline.append(SkRasterPipeline::color_lookup_table,
-                                         fCLUTs.back().get());
+                struct CallbackCtx : SkJumper_CallbackCtx {
+                    sk_sp<const SkColorLookUpTable> clut;
+                    // clut->interp() can't always safely alias its arguments,
+                    // so we allocate a second buffer to hold our results.
+                    float results[4*SkJumper_kMaxStride];
+                };
+                auto cb = fAlloc.make<CallbackCtx>();
+                cb->clut      = sk_ref_sp(&e.colorLUT());
+                cb->read_from = cb->results;
+                cb->fn        = [](SkJumper_CallbackCtx* ctx, int active_pixels) {
+                    auto c = (CallbackCtx*)ctx;
+                    for (int i = 0; i < active_pixels; i++) {
+                        // Look up red, green, and blue for this pixel using 3-4 values from rgba.
+                        c->clut->interp(c->results+4*i, c->rgba+4*i);
+
+                        // If we used 3 inputs (rgb) preserve the fourth as alpha.
+                        // If we used 4 inputs (cmyk) force alpha to 1.
+                        c->results[4*i+3] = (3 == c->clut->inputChannels()) ? c->rgba[4*i+3] : 1.0f;
+                    }
+                };
+                fElementsPipeline.append(SkRasterPipeline::callback, cb);
                 break;
+            }
             case SkColorSpace_A2B::Element::Type::kMatrix:
                 if (!e.matrix().isIdentity()) {
                     SkCSXformPrintf("Matrix stage added\n");
@@ -214,9 +238,16 @@
         case kLinear_SkGammaNamed:
             // do nothing
             break;
-        case k2Dot2Curve_SkGammaNamed:
-            fElementsPipeline.append(SkRasterPipeline::to_2dot2);
+        case k2Dot2Curve_SkGammaNamed: {
+            SkColorSpaceTransferFn fn = {0,0,0,0,0,0,0};
+            fn.fG = 1/2.2f;
+            fn.fA = 1;
+            auto to_2dot2 = this->copy(fn);
+            fElementsPipeline.append(SkRasterPipeline::parametric_r, to_2dot2);
+            fElementsPipeline.append(SkRasterPipeline::parametric_g, to_2dot2);
+            fElementsPipeline.append(SkRasterPipeline::parametric_b, to_2dot2);
             break;
+        }
         case kSRGB_SkGammaNamed:
             fElementsPipeline.append(SkRasterPipeline::to_srgb);
             break;
@@ -225,16 +256,11 @@
                 const SkGammas& gammas = *dstSpace->gammas();
                 if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
                     static constexpr int kInvTableSize = 256;
-                    std::vector<float> storage(kInvTableSize);
-                    invert_table_gamma(storage.data(), nullptr, storage.size(),
+                    auto storage = fAlloc.makeArray<float>(kInvTableSize);
+                    invert_table_gamma(storage, nullptr, kInvTableSize,
                                        gammas.table(channel),
                                        gammas.data(channel).fTable.fSize);
-                    SkTableTransferFn table = {
-                            storage.data(),
-                            (int) storage.size(),
-                    };
-                    fTableStorage.push_front(std::move(storage));
-
+                    SkTableTransferFn table = { storage, kInvTableSize };
                     this->addTableFn(table, channel);
                 } else {
                     SkColorSpaceTransferFn fn;
@@ -254,65 +280,83 @@
 }
 
 void SkColorSpaceXform_A2B::addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex) {
-    fTransferFns.push_front(fn);
     switch (channelIndex) {
         case 0:
-            fElementsPipeline.append(SkRasterPipeline::parametric_r, &fTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::parametric_r, this->copy(fn));
             break;
         case 1:
-            fElementsPipeline.append(SkRasterPipeline::parametric_g, &fTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::parametric_g, this->copy(fn));
             break;
         case 2:
-            fElementsPipeline.append(SkRasterPipeline::parametric_b, &fTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::parametric_b, this->copy(fn));
             break;
         case 3:
-            fElementsPipeline.append(SkRasterPipeline::parametric_a, &fTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::parametric_a, this->copy(fn));
             break;
         default:
             SkASSERT(false);
     }
 }
 
+/**
+ *  |fn| is an in-out parameter.  If the table is too small to perform reasonable table-lookups
+ *  without interpolation, we will build a bigger table.
+ *
+ *  This returns false if we use the original table, meaning we do nothing here but need to keep
+ *  a reference to the original table.  This returns true if we build a new table and the original
+ *  table can be discarded.
+ */
+bool SkColorSpaceXform_A2B::buildTableFn(SkTableTransferFn* fn) {
+    // Arbitrary, but seems like a reasonable guess.
+    static constexpr int kMinTableSize = 256;
+
+    if (fn->fSize >= kMinTableSize) {
+        return false;
+    }
+
+    float* outTable = fAlloc.makeArray<float>(kMinTableSize);
+    float step = 1.0f / (kMinTableSize - 1);
+    for (int i = 0; i < kMinTableSize; i++) {
+        outTable[i] = interp_lut(i * step, fn->fData, fn->fSize);
+    }
+
+    fn->fData = outTable;
+    fn->fSize = kMinTableSize;
+    return true;
+}
+
 void SkColorSpaceXform_A2B::addTableFn(const SkTableTransferFn& fn, int channelIndex) {
-    fTableTransferFns.push_front(fn);
     switch (channelIndex) {
         case 0:
-            fElementsPipeline.append(SkRasterPipeline::table_r, &fTableTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::table_r, this->copy(fn));
             break;
         case 1:
-            fElementsPipeline.append(SkRasterPipeline::table_g, &fTableTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::table_g, this->copy(fn));
             break;
         case 2:
-            fElementsPipeline.append(SkRasterPipeline::table_b, &fTableTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::table_b, this->copy(fn));
             break;
         case 3:
-            fElementsPipeline.append(SkRasterPipeline::table_a, &fTableTransferFns.front());
+            fElementsPipeline.append(SkRasterPipeline::table_a, this->copy(fn));
             break;
         default:
             SkASSERT(false);
     }
 }
 
-void SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& matrix) {
-    fMatrices.push_front(std::vector<float>(12));
-    auto& m = fMatrices.front();
-    m[ 0] = matrix.get(0, 0);
-    m[ 1] = matrix.get(1, 0);
-    m[ 2] = matrix.get(2, 0);
-    m[ 3] = matrix.get(0, 1);
-    m[ 4] = matrix.get(1, 1);
-    m[ 5] = matrix.get(2, 1);
-    m[ 6] = matrix.get(0, 2);
-    m[ 7] = matrix.get(1, 2);
-    m[ 8] = matrix.get(2, 2);
-    m[ 9] = matrix.get(0, 3);
-    m[10] = matrix.get(1, 3);
-    m[11] = matrix.get(2, 3);
-    SkASSERT(matrix.get(3, 0) == 0.f);
-    SkASSERT(matrix.get(3, 1) == 0.f);
-    SkASSERT(matrix.get(3, 2) == 0.f);
-    SkASSERT(matrix.get(3, 3) == 1.f);
-    fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m.data());
+void SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& m44) {
+    auto m = fAlloc.makeArray<float>(12);
+    m[0] = m44.get(0,0); m[ 1] = m44.get(1,0); m[ 2] = m44.get(2,0);
+    m[3] = m44.get(0,1); m[ 4] = m44.get(1,1); m[ 5] = m44.get(2,1);
+    m[6] = m44.get(0,2); m[ 7] = m44.get(1,2); m[ 8] = m44.get(2,2);
+    m[9] = m44.get(0,3); m[10] = m44.get(1,3); m[11] = m44.get(2,3);
+
+    SkASSERT(m44.get(3,0) == 0.0f);
+    SkASSERT(m44.get(3,1) == 0.0f);
+    SkASSERT(m44.get(3,2) == 0.0f);
+    SkASSERT(m44.get(3,3) == 1.0f);
+
+    fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m);
     fElementsPipeline.append(SkRasterPipeline::clamp_0);
     fElementsPipeline.append(SkRasterPipeline::clamp_1);
 }
diff --git a/src/core/SkColorSpaceXform_A2B.h b/src/core/SkColorSpaceXform_A2B.h
index 9374020..7d7bb49 100644
--- a/src/core/SkColorSpaceXform_A2B.h
+++ b/src/core/SkColorSpaceXform_A2B.h
@@ -8,14 +8,11 @@
 #ifndef SkColorSpaceXform_A2B_DEFINED
 #define SkColorSpaceXform_A2B_DEFINED
 
-#include "SkColorSpace_Base.h"
+#include "SkArenaAlloc.h"
 #include "SkColorSpaceXform_Base.h"
+#include "SkColorSpace_Base.h"
 #include "SkRasterPipeline.h"
 
-#include <forward_list>
-#include <functional>
-#include <vector>
-
 class SkColorSpace_A2B;
 class SkColorSpace_XYZ;
 
@@ -36,22 +33,17 @@
 
     void addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex);
 
+    bool buildTableFn(SkTableTransferFn* table);
     void addTableFn(const SkTableTransferFn& table, int channelIndex);
 
     void addMatrix(const SkMatrix44& matrix);
 
-    SkRasterPipeline                             fElementsPipeline;
-    bool                                         fLinearDstGamma;
+    SkRasterPipeline fElementsPipeline;
+    bool             fLinearDstGamma;
+    SkArenaAlloc     fAlloc{128};  // TODO: tune?
 
-    // storage used by the pipeline
-    std::forward_list<SkColorSpaceTransferFn>    fTransferFns;
-    std::forward_list<SkTableTransferFn>         fTableTransferFns;
-    std::forward_list<std::vector<float>>        fMatrices;
-    std::vector<sk_sp<const SkColorLookUpTable>> fCLUTs;
-
-    // these are here to maintain ownership of tables used in the pipeline
-    std::forward_list<std::vector<float>>        fTableStorage;
-    std::vector<sk_sp<const SkGammas>>           fGammaRefs;
+    template <typename T>
+    T* copy(const T& val) { return fAlloc.make<T>(val); }
 
     friend class SkColorSpaceXform_Base;
 };
diff --git a/src/core/SkColorSpaceXformer.cpp b/src/core/SkColorSpaceXformer.cpp
index 893c375..74daf66 100644
--- a/src/core/SkColorSpaceXformer.cpp
+++ b/src/core/SkColorSpaceXformer.cpp
@@ -11,8 +11,10 @@
 #include "SkDrawLooper.h"
 #include "SkGradientShader.h"
 #include "SkImage_Base.h"
+#include "SkImageFilter.h"
 #include "SkImagePriv.h"
 #include "SkMakeUnique.h"
+#include "SkShaderBase.h"
 
 std::unique_ptr<SkColorSpaceXformer> SkColorSpaceXformer::Make(sk_sp<SkColorSpace> dst) {
     std::unique_ptr<SkColorSpaceXform> fromSRGB = SkColorSpaceXform_Base::New(
@@ -28,7 +30,7 @@
 }
 
 sk_sp<SkImage> SkColorSpaceXformer::apply(const SkImage* src) {
-    return as_IB(src)->makeColorSpace(fDst);
+    return src->makeColorSpace(fDst, SkTransferFunctionBehavior::kIgnore);
 }
 
 sk_sp<SkImage> SkColorSpaceXformer::apply(const SkBitmap& src) {
@@ -37,12 +39,24 @@
         return nullptr;
     }
 
-    sk_sp<SkImage> xformed = as_IB(image)->makeColorSpace(fDst);
+    sk_sp<SkImage> xformed = image->makeColorSpace(fDst, SkTransferFunctionBehavior::kIgnore);
     // We want to be sure we don't let the kNever_SkCopyPixelsMode image escape this stack frame.
     SkASSERT(xformed != image);
     return xformed;
 }
 
+sk_sp<SkColorFilter> SkColorSpaceXformer::apply(const SkColorFilter* colorFilter) {
+    return colorFilter->makeColorSpace(this);
+}
+
+sk_sp<SkImageFilter> SkColorSpaceXformer::apply(const SkImageFilter* imageFilter) {
+    return imageFilter->makeColorSpace(this);
+}
+
+sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) {
+    return as_SB(shader)->makeColorSpace(this);
+}
+
 void SkColorSpaceXformer::apply(SkColor* xformed, const SkColor* srgb, int n) {
     SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, xformed,
                                     SkColorSpaceXform::kBGRA_8888_ColorFormat, srgb,
@@ -55,91 +69,6 @@
     return xformed;
 }
 
-// TODO: Is this introspection going to be enough, or do we need a new SkShader method?
-sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) {
-    SkColor color;
-    if (shader->isConstant() && shader->asLuminanceColor(&color)) {
-        return SkShader::MakeColorShader(this->apply(color))
-                ->makeWithLocalMatrix(shader->getLocalMatrix());
-    }
-
-    SkShader::TileMode xy[2];
-    SkMatrix local;
-    if (auto img = shader->isAImage(&local, xy)) {
-        return this->apply(img)->makeShader(xy[0], xy[1], &local);
-    }
-
-    SkShader::ComposeRec compose;
-    if (shader->asACompose(&compose)) {
-        auto A = this->apply(compose.fShaderA),
-             B = this->apply(compose.fShaderB);
-        if (A && B) {
-            return SkShader::MakeComposeShader(std::move(A), std::move(B), compose.fBlendMode)
-                    ->makeWithLocalMatrix(shader->getLocalMatrix());
-        }
-    }
-
-    SkShader::GradientInfo gradient;
-    sk_bzero(&gradient, sizeof(gradient));
-    if (auto type = shader->asAGradient(&gradient)) {
-        SkSTArray<8, SkColor>  colors(gradient.fColorCount);
-        SkSTArray<8, SkScalar>    pos(gradient.fColorCount);
-
-        gradient.fColors       = colors.begin();
-        gradient.fColorOffsets =    pos.begin();
-        shader->asAGradient(&gradient);
-
-        SkSTArray<8, SkColor> xformed(gradient.fColorCount);
-        this->apply(xformed.begin(), gradient.fColors, gradient.fColorCount);
-
-        switch (type) {
-            case SkShader::kNone_GradientType:
-            case SkShader::kColor_GradientType:
-                SkASSERT(false);  // Should be unreachable.
-                break;
-
-            case SkShader::kLinear_GradientType:
-                return SkGradientShader::MakeLinear(gradient.fPoint,
-                                                    xformed.begin(),
-                                                    gradient.fColorOffsets,
-                                                    gradient.fColorCount,
-                                                    gradient.fTileMode,
-                                                    gradient.fGradientFlags,
-                                                    &shader->getLocalMatrix());
-            case SkShader::kRadial_GradientType:
-                return SkGradientShader::MakeRadial(gradient.fPoint[0],
-                                                    gradient.fRadius[0],
-                                                    xformed.begin(),
-                                                    gradient.fColorOffsets,
-                                                    gradient.fColorCount,
-                                                    gradient.fTileMode,
-                                                    gradient.fGradientFlags,
-                                                    &shader->getLocalMatrix());
-            case SkShader::kSweep_GradientType:
-                return SkGradientShader::MakeSweep(gradient.fPoint[0].fX,
-                                                   gradient.fPoint[0].fY,
-                                                   xformed.begin(),
-                                                   gradient.fColorOffsets,
-                                                   gradient.fColorCount,
-                                                   gradient.fGradientFlags,
-                                                   &shader->getLocalMatrix());
-            case SkShader::kConical_GradientType:
-                return SkGradientShader::MakeTwoPointConical(gradient.fPoint[0],
-                                                             gradient.fRadius[0],
-                                                             gradient.fPoint[1],
-                                                             gradient.fRadius[1],
-                                                             xformed.begin(),
-                                                             gradient.fColorOffsets,
-                                                             gradient.fColorCount,
-                                                             gradient.fTileMode,
-                                                             gradient.fGradientFlags,
-                                                             &shader->getLocalMatrix());
-        }
-    }
-
-    return sk_ref_sp(const_cast<SkShader*>(shader));
-}
-
 SkPaint SkColorSpaceXformer::apply(const SkPaint& src) {
     SkPaint dst = src;
 
@@ -152,20 +81,17 @@
         dst.setShader(this->apply(shader));
     }
 
-    // As far as I know, SkModeColorFilter is the only color filter that holds a color.
     if (auto cf = src.getColorFilter()) {
-        SkColor color;
-        SkBlendMode mode;
-        if (cf->asColorMode(&color, &mode)) {
-            dst.setColorFilter(SkColorFilter::MakeModeFilter(this->apply(color), mode));
-        }
+        dst.setColorFilter(this->apply(cf));
     }
 
     if (auto looper = src.getDrawLooper()) {
         dst.setDrawLooper(looper->makeColorSpace(this));
     }
 
-    // TODO:
-    //    - image filters?
+    if (auto imageFilter = src.getImageFilter()) {
+        dst.setImageFilter(this->apply(imageFilter));
+    }
+
     return dst;
 }
diff --git a/src/core/SkColorSpaceXformer.h b/src/core/SkColorSpaceXformer.h
index 8099ba7..92d9260 100644
--- a/src/core/SkColorSpaceXformer.h
+++ b/src/core/SkColorSpaceXformer.h
@@ -16,15 +16,18 @@
 public:
     static std::unique_ptr<SkColorSpaceXformer> Make(sk_sp<SkColorSpace> dst);
 
-    sk_sp<SkImage> apply(const SkImage* src);
-    sk_sp<SkImage> apply(const SkBitmap& bitmap);
-    SkPaint apply(const SkPaint& src);
+    sk_sp<SkImage> apply(const SkImage*);
+    sk_sp<SkImage> apply(const SkBitmap&);
+    sk_sp<SkColorFilter> apply(const SkColorFilter*);
+    sk_sp<SkImageFilter> apply(const SkImageFilter*);
+    sk_sp<SkShader>      apply(const SkShader*);
+    SkPaint apply(const SkPaint&);
     void apply(SkColor dst[], const SkColor src[], int n);
+    SkColor apply(SkColor srgb);
+
+    sk_sp<SkColorSpace> dst() const { return fDst; }
 
 private:
-    SkColor apply(SkColor srgb);
-    sk_sp<SkShader> apply(const SkShader* shader);
-
     SkColorSpaceXformer() {}
 
     sk_sp<SkColorSpace>                fDst;
diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h
index 61f65a5..9055cce 100644
--- a/src/core/SkColorSpace_Base.h
+++ b/src/core/SkColorSpace_Base.h
@@ -110,7 +110,7 @@
         SkASSERT(i >= 0 && i < fChannels);
         return fType[i];
     }
-    
+
     uint8_t channels() const { return fChannels; }
 
     SkGammas(uint8_t channels)
diff --git a/src/core/SkColorSpace_ICC.cpp b/src/core/SkColorSpace_ICC.cpp
index 9c2082a..5a51090 100644
--- a/src/core/SkColorSpace_ICC.cpp
+++ b/src/core/SkColorSpace_ICC.cpp
@@ -327,6 +327,16 @@
                 return set_gamma_value(outData, value);
             }
 
+            // This optimization is especially important for A2B profiles, where we do
+            // not resize tables or interpolate lookups.
+            if (2 == count) {
+                if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
+                        65535 == read_big_endian_u16((const uint8_t*) &table[1])) {
+                    outData->fNamed = kLinear_SkGammaNamed;
+                    return SkGammas::Type::kNamed_Type;
+                }
+            }
+
             // Check for frequently occurring sRGB curves.
             // We do this by sampling a few values and see if they match our expectation.
             // A more robust solution would be to compare each value in this curve against
@@ -595,8 +605,8 @@
 
     uint32_t numEntries = SkColorLookUpTable::kOutputChannels;
     for (uint32_t i = 0; i < inputChannels; i++) {
-        if (0 == gridPoints[i]) {
-            SkColorSpacePrintf("Each input channel must have at least one grid point.");
+        if (1 >= gridPoints[i]) {
+            SkColorSpacePrintf("Each input channel must have at least two grid points.");
             return false;
         }
 
@@ -1185,7 +1195,11 @@
 static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
                       size_t len, SkColorSpace_A2B::PCS pcs,
                       SkColorSpace_Base::ICCTypeFlag iccType) {
+    if (len < 4) {
+        return false;
+    }
     const uint32_t type = read_big_endian_u32(src);
+
     switch (type) {
         case kTAG_AtoBType:
             if (len < 32) {
diff --git a/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
index 9710346..928f515 100644
--- a/src/core/SkColorTable.cpp
+++ b/src/core/SkColorTable.cpp
@@ -23,11 +23,7 @@
 
 SkColorTable::SkColorTable(const SkPMColor colors[], int count) {
     SkASSERT(0 == count || colors);
-    if (count < 0) {
-        count = 0;
-    } else if (count > 256) {
-        count = 256;
-    }
+    SkASSERT(count >= 0 && count <= 256);
     this->init(colors, count);
 }
 
@@ -56,6 +52,16 @@
     return f16BitCache;
 }
 
+sk_sp<SkColorTable> SkColorTable::Make(const SkPMColor colors[], int count) {
+    if (count < 0 || count > 256) {
+        return nullptr;
+    }
+    if (count && !colors) {
+        return nullptr;
+    }
+    return sk_make_sp<SkColorTable>(colors, count);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #if 0
@@ -85,14 +91,14 @@
     buffer.writeColorArray(fColors, fCount);
 }
 
-SkColorTable* SkColorTable::Create(SkReadBuffer& buffer) {
+sk_sp<SkColorTable> SkColorTable::Create(SkReadBuffer& buffer) {
     if (buffer.isVersionLT(SkReadBuffer::kRemoveColorTableAlpha_Version)) {
         /*fAlphaType = */buffer.readUInt();
     }
 
     const int count = buffer.getArrayCount();
     if (0 == count) {
-        return new SkColorTable(nullptr, 0);
+        return sk_sp<SkColorTable>(new SkColorTable(nullptr, 0));
     }
 
     if (count < 0 || count > 256) {
@@ -106,5 +112,5 @@
         return nullptr;
     }
 
-    return new SkColorTable(colors.release(), count, kAllocatedWithMalloc);
+    return sk_sp<SkColorTable>(new SkColorTable(colors.release(), count, kAllocatedWithMalloc));
 }
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
deleted file mode 100644
index dd95c3e..0000000
--- a/src/core/SkComposeShader.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkArenaAlloc.h"
-#include "SkComposeShader.h"
-#include "SkColorFilter.h"
-#include "SkColorPriv.h"
-#include "SkColorShader.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkString.h"
-
-sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
-                                            SkBlendMode mode) {
-    if (!src || !dst) {
-        return nullptr;
-    }
-    if (SkBlendMode::kSrc == mode) {
-        return src;
-    }
-    if (SkBlendMode::kDst == mode) {
-        return dst;
-    }
-    return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkAutoAlphaRestore {
-public:
-    SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
-        fAlpha = paint->getAlpha();
-        fPaint = paint;
-        paint->setAlpha(newAlpha);
-    }
-
-    ~SkAutoAlphaRestore() {
-        fPaint->setAlpha(fAlpha);
-    }
-private:
-    SkPaint*    fPaint;
-    uint8_t     fAlpha;
-};
-#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
-
-sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
-    sk_sp<SkShader> shaderA(buffer.readShader());
-    sk_sp<SkShader> shaderB(buffer.readShader());
-    SkBlendMode mode;
-    if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) {
-        sk_sp<SkXfermode> xfer = buffer.readXfermode();
-        mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver;
-    } else {
-        mode = (SkBlendMode)buffer.read32();
-    }
-    if (!shaderA || !shaderB) {
-        return nullptr;
-    }
-    return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
-}
-
-void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeFlattenable(fShaderA.get());
-    buffer.writeFlattenable(fShaderB.get());
-    buffer.write32((int)fMode);
-}
-
-SkShader::Context* SkComposeShader::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const
-{
-    // we preconcat our localMatrix (if any) with the device matrix
-    // before calling our sub-shaders
-    SkMatrix tmpM;
-    tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
-
-    // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
-    // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
-    // sub-shaders.
-    SkPaint opaquePaint(*rec.fPaint);
-    opaquePaint.setAlpha(0xFF);
-
-    ContextRec newRec(rec);
-    newRec.fMatrix = &tmpM;
-    newRec.fPaint = &opaquePaint;
-
-    SkShader::Context* contextA = fShaderA->makeContext(newRec, alloc);
-    SkShader::Context* contextB = fShaderB->makeContext(newRec, alloc);
-    if (!contextA || !contextB) {
-        return nullptr;
-    }
-
-    return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB);
-}
-
-SkComposeShader::ComposeShaderContext::ComposeShaderContext(
-        const SkComposeShader& shader, const ContextRec& rec,
-        SkShader::Context* contextA, SkShader::Context* contextB)
-    : INHERITED(shader, rec)
-    , fShaderContextA(contextA)
-    , fShaderContextB(contextB) {}
-
-bool SkComposeShader::asACompose(ComposeRec* rec) const {
-    if (rec) {
-        rec->fShaderA   = fShaderA.get();
-        rec->fShaderB   = fShaderB.get();
-        rec->fBlendMode = fMode;
-    }
-    return true;
-}
-
-
-// larger is better (fewer times we have to loop), but we shouldn't
-// take up too much stack-space (each element is 4 bytes)
-#define TMP_COLOR_COUNT     64
-
-void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
-    SkShader::Context* shaderContextA = fShaderContextA;
-    SkShader::Context* shaderContextB = fShaderContextB;
-    SkBlendMode        mode = static_cast<const SkComposeShader&>(fShader).fMode;
-    unsigned           scale = SkAlpha255To256(this->getPaintAlpha());
-
-    SkPMColor   tmp[TMP_COLOR_COUNT];
-
-    SkXfermode* xfer = SkXfermode::Peek(mode);
-    if (nullptr == xfer) {   // implied SRC_OVER
-        // TODO: when we have a good test-case, should use SkBlitRow::Proc32
-        // for these loops
-        do {
-            int n = count;
-            if (n > TMP_COLOR_COUNT) {
-                n = TMP_COLOR_COUNT;
-            }
-
-            shaderContextA->shadeSpan(x, y, result, n);
-            shaderContextB->shadeSpan(x, y, tmp, n);
-
-            if (256 == scale) {
-                for (int i = 0; i < n; i++) {
-                    result[i] = SkPMSrcOver(tmp[i], result[i]);
-                }
-            } else {
-                for (int i = 0; i < n; i++) {
-                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
-                                            scale);
-                }
-            }
-
-            result += n;
-            x += n;
-            count -= n;
-        } while (count > 0);
-    } else {    // use mode for the composition
-        do {
-            int n = count;
-            if (n > TMP_COLOR_COUNT) {
-                n = TMP_COLOR_COUNT;
-            }
-
-            shaderContextA->shadeSpan(x, y, result, n);
-            shaderContextB->shadeSpan(x, y, tmp, n);
-            xfer->xfer32(result, tmp, n, nullptr);
-
-            if (256 != scale) {
-                for (int i = 0; i < n; i++) {
-                    result[i] = SkAlphaMulQ(result[i], scale);
-                }
-            }
-
-            result += n;
-            x += n;
-            count -= n;
-        } while (count > 0);
-    }
-}
-
-#if SK_SUPPORT_GPU
-
-#include "effects/GrConstColorProcessor.h"
-#include "effects/GrXfermodeFragmentProcessor.h"
-
-/////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
-    switch (fMode) {
-        case SkBlendMode::kClear:
-            return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
-                                               GrConstColorProcessor::kIgnore_InputMode);
-            break;
-        case SkBlendMode::kSrc:
-            return fShaderB->asFragmentProcessor(args);
-            break;
-        case SkBlendMode::kDst:
-            return fShaderA->asFragmentProcessor(args);
-            break;
-        default:
-            sk_sp<GrFragmentProcessor> fpA(fShaderA->asFragmentProcessor(args));
-            if (!fpA) {
-                return nullptr;
-            }
-            sk_sp<GrFragmentProcessor> fpB(fShaderB->asFragmentProcessor(args));
-            if (!fpB) {
-                return nullptr;
-            }
-            return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
-                                                                      std::move(fpA), fMode);
-    }
-}
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkComposeShader::toString(SkString* str) const {
-    str->append("SkComposeShader: (");
-
-    str->append("ShaderA: ");
-    fShaderA->toString(str);
-    str->append(" ShaderB: ");
-    fShaderB->toString(str);
-    if (SkBlendMode::kSrcOver != fMode) {
-        str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode));
-    }
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/src/core/SkComposeShader.h b/src/core/SkComposeShader.h
deleted file mode 100644
index be788af..0000000
--- a/src/core/SkComposeShader.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkComposeShader_DEFINED
-#define SkComposeShader_DEFINED
-
-#include "SkShader.h"
-#include "SkBlendMode.h"
-
-///////////////////////////////////////////////////////////////////////////////////////////
-
-/** \class SkComposeShader
-    This subclass of shader returns the composition of two other shaders, combined by
-    a xfermode.
-*/
-class SK_API SkComposeShader : public SkShader {
-public:
-    /** Create a new compose shader, given shaders A, B, and a combining xfermode mode.
-        When the xfermode is called, it will be given the result from shader A as its
-        "dst", and the result from shader B as its "src".
-        mode->xfer32(sA_result, sB_result, ...)
-        @param shaderA  The colors from this shader are seen as the "dst" by the xfermode
-        @param shaderB  The colors from this shader are seen as the "src" by the xfermode
-        @param mode     The xfermode that combines the colors from the two shaders. If mode
-                        is null, then SRC_OVER is assumed.
-    */
-    SkComposeShader(sk_sp<SkShader> sA, sk_sp<SkShader> sB, SkBlendMode mode)
-        : fShaderA(std::move(sA))
-        , fShaderB(std::move(sB))
-        , fMode(mode)
-    {}
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    class ComposeShaderContext : public SkShader::Context {
-    public:
-        // When this object gets destroyed, it will call contextA and contextB's destructor
-        // but it will NOT free the memory.
-        ComposeShaderContext(const SkComposeShader&, const ContextRec&,
-                             SkShader::Context* contextA, SkShader::Context* contextB);
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
-    private:
-        SkShader::Context* fShaderContextA;
-        SkShader::Context* fShaderContextB;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-#ifdef SK_DEBUG
-    SkShader* getShaderA() { return fShaderA.get(); }
-    SkShader* getShaderB() { return fShaderB.get(); }
-#endif
-
-    bool asACompose(ComposeRec* rec) const override;
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader)
-
-protected:
-    SkComposeShader(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    sk_sp<SkShader>     fShaderA;
-    sk_sp<SkShader>     fShaderB;
-    SkBlendMode         fMode;
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/core/SkConvertPixels.cpp b/src/core/SkConvertPixels.cpp
index b919f5d..b3a6a06 100644
--- a/src/core/SkConvertPixels.cpp
+++ b/src/core/SkConvertPixels.cpp
@@ -16,6 +16,7 @@
 #include "SkRasterPipeline.h"
 #include "SkUnPreMultiply.h"
 #include "SkUnPreMultiplyPriv.h"
+#include "../jumper/SkJumper.h"
 
 // Fast Path 1: The memcpy() case.
 static inline bool can_memcpy(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) {
@@ -276,7 +277,7 @@
 static void convert_with_pipeline(const SkImageInfo& dstInfo, void* dstRow, size_t dstRB,
                                   const SkImageInfo& srcInfo, const void* srcRow, size_t srcRB,
                                   bool isColorAware, SkTransferFunctionBehavior behavior) {
-    SkRasterPipeline pipeline;
+    SkRasterPipeline_<256> pipeline;
     switch (srcInfo.colorType()) {
         case kRGBA_8888_SkColorType:
             pipeline.append(SkRasterPipeline::load_8888, &srcRow);
@@ -308,14 +309,20 @@
         premulState = kUnpremul_SkAlphaType;
     }
 
+    SkColorSpaceTransferFn srcFn;
     if (isColorAware && srcInfo.gammaCloseToSRGB()) {
         pipeline.append_from_srgb(srcInfo.alphaType());
+    } else if (isColorAware && !srcInfo.colorSpace()->gammaIsLinear()) {
+        SkAssertResult(srcInfo.colorSpace()->isNumericalTransferFn(&srcFn));
+        pipeline.append(SkRasterPipeline::parametric_r, &srcFn);
+        pipeline.append(SkRasterPipeline::parametric_g, &srcFn);
+        pipeline.append(SkRasterPipeline::parametric_b, &srcFn);
     }
 
     float matrix[12];
     if (isColorAware) {
-        SkAssertResult(append_gamut_transform(&pipeline, matrix, srcInfo.colorSpace(),
-                                              dstInfo.colorSpace()));
+        append_gamut_transform(&pipeline, matrix, srcInfo.colorSpace(), dstInfo.colorSpace(),
+                               premulState);
     }
 
     SkAlphaType dat = dstInfo.alphaType();
@@ -329,8 +336,15 @@
         }
     }
 
+    SkColorSpaceTransferFn dstFn;
     if (isColorAware && dstInfo.gammaCloseToSRGB()) {
         pipeline.append(SkRasterPipeline::to_srgb);
+    } else if (isColorAware && !dstInfo.colorSpace()->gammaIsLinear()) {
+        SkAssertResult(dstInfo.colorSpace()->isNumericalTransferFn(&dstFn));
+        dstFn = dstFn.invert();
+        pipeline.append(SkRasterPipeline::parametric_r, &dstFn);
+        pipeline.append(SkRasterPipeline::parametric_g, &dstFn);
+        pipeline.append(SkRasterPipeline::parametric_b, &dstFn);
     }
 
     if (kUnpremul_SkAlphaType == premulState && kPremul_SkAlphaType == dat &&
@@ -344,6 +358,19 @@
     // opaque to another alpha type, there's no need to worry about multiplication.
     SkASSERT(premulState == dat || kOpaque_SkAlphaType == srcInfo.alphaType());
 
+    // We'll dither if we're decreasing precision below 32-bit.
+    float dither_rate = 0.0f;
+    if (srcInfo.bytesPerPixel() > dstInfo.bytesPerPixel()) {
+        switch (dstInfo.colorType()) {
+            case   kRGB_565_SkColorType: dither_rate = 1/63.0f; break;
+            case kARGB_4444_SkColorType: dither_rate = 1/15.0f; break;
+            default:                     dither_rate =    0.0f; break;
+        }
+    }
+    if (dither_rate > 0) {
+        pipeline.append(SkRasterPipeline::dither, &dither_rate);
+    }
+
     switch (dstInfo.colorType()) {
         case kRGBA_8888_SkColorType:
             pipeline.append(SkRasterPipeline::store_8888, &dstRow);
@@ -366,8 +393,9 @@
             break;
     }
 
+    auto run = pipeline.compile();
     for (int y = 0; y < srcInfo.height(); ++y) {
-        pipeline.run(0,srcInfo.width());
+        run(0,y, srcInfo.width());
         // The pipeline has pointers to srcRow and dstRow, so we just need to update them in the
         // loop to move between rows of src/dst.
         dstRow = SkTAddOffset<void>(dstRow, dstRB);
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index 0e8b819..8ef0a85 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -11,7 +11,7 @@
 #include "SkBitmapProcShader.h"
 #include "SkBlitter.h"
 #include "SkBlitRow.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 #include "SkXfermodePriv.h"
 
 class SkRasterBlitter : public SkBlitter {
@@ -33,14 +33,14 @@
       *  exchange that object.
       */
     SkShaderBlitter(const SkPixmap& device, const SkPaint& paint,
-                    SkShader::Context* shaderContext);
+                    SkShaderBase::Context* shaderContext);
     virtual ~SkShaderBlitter();
 
 protected:
-    uint32_t            fShaderFlags;
-    const SkShader*     fShader;
-    SkShader::Context*  fShaderContext;
-    bool                fConstInY;
+    uint32_t                fShaderFlags;
+    const SkShader*         fShader;
+    SkShaderBase::Context*  fShaderContext;
+    bool                    fConstInY;
 
 private:
     // illegal
@@ -62,45 +62,6 @@
     const SkPixmap* justAnOpaqueColor(uint32_t*) override;
 };
 
-class SkA8_Blitter : public SkRasterBlitter {
-public:
-    SkA8_Blitter(const SkPixmap& device, const SkPaint& paint);
-    void blitH(int x, int y, int width) override;
-    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override;
-    void blitV(int x, int y, int height, SkAlpha alpha) override;
-    void blitRect(int x, int y, int width, int height) override;
-    void blitMask(const SkMask&, const SkIRect&) override;
-    const SkPixmap* justAnOpaqueColor(uint32_t*) override;
-
-private:
-    unsigned fSrcA;
-
-    // illegal
-    SkA8_Blitter& operator=(const SkA8_Blitter&);
-
-    typedef SkRasterBlitter INHERITED;
-};
-
-class SkA8_Shader_Blitter : public SkShaderBlitter {
-public:
-    SkA8_Shader_Blitter(const SkPixmap& device, const SkPaint& paint,
-                        SkShader::Context* shaderContext);
-    ~SkA8_Shader_Blitter() override;
-    void blitH(int x, int y, int width) override;
-    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override;
-    void blitMask(const SkMask&, const SkIRect&) override;
-
-private:
-    SkXfermode* fXfermode;
-    SkPMColor*  fBuffer;
-    uint8_t*    fAAExpand;
-
-    // illegal
-    SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&);
-
-    typedef SkShaderBlitter INHERITED;
-};
-
 ////////////////////////////////////////////////////////////////
 
 class SkARGB32_Blitter : public SkRasterBlitter {
@@ -155,7 +116,7 @@
 class SkARGB32_Shader_Blitter : public SkShaderBlitter {
 public:
     SkARGB32_Shader_Blitter(const SkPixmap& device, const SkPaint& paint,
-                            SkShader::Context* shaderContext);
+                            SkShaderBase::Context* shaderContext);
     ~SkARGB32_Shader_Blitter() override;
     void blitH(int x, int y, int width) override;
     void blitV(int x, int y, int height, SkAlpha alpha) override;
@@ -176,34 +137,17 @@
     typedef SkShaderBlitter INHERITED;
 };
 
-SkBlitter* SkBlitter_ARGB32_Create(const SkPixmap& device, const SkPaint&, SkShader::Context*,
-                                   SkArenaAlloc*);
-
-SkBlitter* SkBlitter_F16_Create(const SkPixmap& device, const SkPaint&, SkShader::Context*,
-                                SkArenaAlloc*);
-
 ///////////////////////////////////////////////////////////////////////////////
 
-/*  These return the correct subclass of blitter for their device config.
-
-    Currently, they make the following assumptions about the state of the
-    paint:
-
-    1. If there is an xfermode, there will also be a shader
-    2. If there is a colorfilter, there will be a shader that itself handles
-       calling the filter, so the blitter can always ignore the colorfilter obj
-
-    These pre-conditions must be handled by the caller, in our case
-    SkBlitter::Choose(...)
- */
-
-SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint,
-                                SkShader::Context* shaderContext,
-                                SkArenaAlloc* allocator);
-
-
-// Returns nullptr if no SkRasterPipeline blitter can be constructed for this paint.
+// Neither of these ever returns nullptr, but this first factory may return a SkNullBlitter.
 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
                                          SkArenaAlloc*);
+// Use this if you've pre-baked a shader pipeline, including modulating with paint alpha.
+// This factory never returns an SkNullBlitter.
+SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&,
+                                         const SkRasterPipeline& shaderPipeline,
+                                         bool shader_is_opaque,
+                                         bool shader_wants_dither,
+                                         SkArenaAlloc*);
 
 #endif
diff --git a/src/core/SkCpu.cpp b/src/core/SkCpu.cpp
index d90e482..42b754f 100644
--- a/src/core/SkCpu.cpp
+++ b/src/core/SkCpu.cpp
@@ -74,23 +74,25 @@
         return features;
     }
 
-#elif defined(SK_CPU_ARM64) && __has_include(<asm/hwcap.h>) && __has_include(<sys/auxv.h>)
-    #include <asm/hwcap.h>
+#elif defined(SK_CPU_ARM64) && __has_include(<sys/auxv.h>)
     #include <sys/auxv.h>
 
     static uint32_t read_cpu_features() {
+        const uint32_t HWCAP_CRC32 = (1<<7);
+
         uint32_t features = 0;
         uint32_t hwcaps = getauxval(AT_HWCAP);
         if (hwcaps & HWCAP_CRC32) { features |= SkCpu::CRC32; }
         return features;
     }
 
-#elif defined(SK_CPU_ARM32) && __has_include(<asm/hwcap.h>) && __has_include(<sys/auxv.h>)
-    // asm/hwcap.h and sys/auxv.h won't be present on NDK builds before API v21.
-    #include <asm/hwcap.h>
+#elif defined(SK_CPU_ARM32) && __has_include(<sys/auxv.h>)
+    // sys/auxv.h won't be present on NDK builds before API v21.
     #include <sys/auxv.h>
 
     static uint32_t read_cpu_features() {
+        const uint32_t HWCAP_VFPv4 = (1<<16);
+
         uint32_t features = 0;
         uint32_t hwcaps = getauxval(AT_HWCAP);
         if (hwcaps & HWCAP_VFPv4) { features |= SkCpu::NEON|SkCpu::NEON_FMA|SkCpu::VFP_FP16; }
diff --git a/src/core/SkCpu.h b/src/core/SkCpu.h
index 24428d9..a9ed906 100644
--- a/src/core/SkCpu.h
+++ b/src/core/SkCpu.h
@@ -87,6 +87,12 @@
     // It's available on Haswell+ just like AVX2, but it's technically a different bit.
     // TODO: circle back on this if we find ourselves limited by lack of compile-time FMA
 
+    #if defined(SK_CPU_LIMIT_SSE41)
+    features &= (SkCpu::SSE1 | SkCpu::SSE2 | SkCpu::SSE3 | SkCpu::SSSE3 | SkCpu::SSE41);
+    #elif defined(SK_CPU_LIMIT_SSE2)
+    features &= (SkCpu::SSE1 | SkCpu::SSE2);
+    #endif
+
 #else
     #if defined(SK_ARM_HAS_NEON)
     features |= NEON;
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 21287a2..e210e36 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -9,7 +9,6 @@
 #include "SkColorFilter.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
-#include "SkImageCacherator.h"
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkImagePriv.h"
@@ -25,6 +24,7 @@
 #include "SkTLazy.h"
 #include "SkTextBlobRunIterator.h"
 #include "SkTextToPathIter.h"
+#include "SkUtils.h"
 #include "SkVertices.h"
 
 SkBaseDevice::SkBaseDevice(const SkImageInfo& info, const SkSurfaceProps& surfaceProps)
@@ -125,11 +125,11 @@
 }
 
 void SkBaseDevice::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                             const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) {
-    SkPatchUtils::VertexData data;
-
+                             const SkPoint texCoords[4], SkBlendMode bmode,
+                             bool interpColorsLinearly, const SkPaint& paint) {
     SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &this->ctm());
-    auto vertices = SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height());
+    auto vertices = SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height(),
+                                               interpColorsLinearly);
     if (vertices) {
         this->drawVertices(vertices.get(), bmode, paint);
     }
@@ -238,43 +238,54 @@
     }
 }
 
+static SkPoint* quad_to_tris(SkPoint tris[6], const SkPoint quad[4]) {
+    tris[0] = quad[0];
+    tris[1] = quad[1];
+    tris[2] = quad[2];
+
+    tris[3] = quad[0];
+    tris[4] = quad[2];
+    tris[5] = quad[3];
+
+    return tris + 6;
+}
+
 void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[],
-                             const SkRect tex[], const SkColor colors[], int count,
+                             const SkRect tex[], const SkColor colors[], int quadCount,
                              SkBlendMode mode, const SkPaint& paint) {
-    SkPath path;
-    path.setIsVolatile(true);
+    const int triCount = quadCount << 1;
+    const int vertexCount = triCount * 3;
+    uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag;
+    if (colors) {
+        flags |= SkVertices::kHasColors_BuilderFlag;
+    }
+    SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vertexCount, 0, flags);
 
-    for (int i = 0; i < count; ++i) {
-        SkPoint quad[4];
-        xform[i].toQuad(tex[i].width(), tex[i].height(), quad);
+    SkPoint* vPos = builder.positions();
+    SkPoint* vTex = builder.texCoords();
+    SkColor* vCol = builder.colors();
+    for (int i = 0; i < quadCount; ++i) {
+        SkPoint tmp[4];
+        xform[i].toQuad(tex[i].width(), tex[i].height(), tmp);
+        vPos = quad_to_tris(vPos, tmp);
 
-        SkMatrix localM;
-        localM.setRSXform(xform[i]);
-        localM.preTranslate(-tex[i].left(), -tex[i].top());
-
-        SkPaint pnt(paint);
-        sk_sp<SkShader> shader = atlas->makeShader(SkShader::kClamp_TileMode,
-                                                   SkShader::kClamp_TileMode,
-                                                   &localM);
-        if (!shader) {
-            break;
-        }
-        pnt.setShader(std::move(shader));
+        tex[i].toQuad(tmp);
+        vTex = quad_to_tris(vTex, tmp);
 
         if (colors) {
-            pnt.setColorFilter(SkColorFilter::MakeModeFilter(colors[i], (SkBlendMode)mode));
+            sk_memset32(vCol, colors[i], 6);
+            vCol += 6;
         }
-
-        path.rewind();
-        path.addPoly(quad, 4, true);
-        path.setConvexity(SkPath::kConvex_Convexity);
-        this->drawPath(path, pnt, nullptr, true);
     }
+    SkPaint p(paint);
+    p.setShader(atlas->makeShader());
+    this->drawVertices(builder.detach().get(), mode, p);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) {}
+void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                               SkImage*, const SkMatrix&) {}
 sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkBitmap&) { return nullptr; }
 sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkImage*) { return nullptr; }
 sk_sp<SkSpecialImage> SkBaseDevice::snapSpecial() { return nullptr; }
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index f1f5488..82c1897 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -15,6 +15,7 @@
 
 class SkBitmap;
 class SkDrawFilter;
+struct SkDrawShadowRec;
 class SkImageFilterCache;
 struct SkIRect;
 class SkMatrix;
@@ -234,12 +235,15 @@
                              const SkScalar pos[], int scalarsPerPos,
                              const SkPoint& offset, const SkPaint& paint) = 0;
     virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) = 0;
+    virtual void drawShadow(const SkPath&, const SkDrawShadowRec&);
+
     // default implementation unrolls the blob runs.
     virtual void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
                               const SkPaint& paint, SkDrawFilter* drawFilter);
     // default implementation calls drawVertices
     virtual void drawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                           const SkPoint texCoords[4], SkBlendMode, const SkPaint& paint);
+                           const SkPoint texCoords[4], SkBlendMode, bool interpColorsLinearly,
+                           const SkPaint& paint);
 
     // default implementation calls drawPath
     virtual void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
@@ -258,7 +262,8 @@
     virtual void drawTextRSXform(const void* text, size_t len, const SkRSXform[],
                                  const SkPaint&);
 
-    virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&);
+    virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                             SkImage* clipImage, const SkMatrix& clipMatrix);
     virtual sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&);
     virtual sk_sp<SkSpecialImage> makeSpecial(const SkImage*);
     virtual sk_sp<SkSpecialImage> snapSpecial();
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 689e5b7..877563c 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -4,23 +4,23 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #define __STDC_LIMIT_MACROS
 
-#include "SkDraw.h"
-
 #include "SkArenaAlloc.h"
+#include "SkAutoBlitterChoose.h"
 #include "SkBlendModePriv.h"
 #include "SkBlitter.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
-#include "SkColorShader.h"
 #include "SkDevice.h"
 #include "SkDeviceLooper.h"
+#include "SkDraw.h"
+#include "SkDrawProcs.h"
 #include "SkFindAndPlaceGlyph.h"
-#include "SkFixed.h"
-#include "SkLocalMatrixShader.h"
 #include "SkMaskFilter.h"
 #include "SkMatrix.h"
+#include "SkMatrixUtils.h"
 #include "SkPaint.h"
 #include "SkPathEffect.h"
 #include "SkRasterClip.h"
@@ -34,50 +34,12 @@
 #include "SkTemplates.h"
 #include "SkTextMapStateProc.h"
 #include "SkTLazy.h"
-#include "SkUnPreMultiply.h"
 #include "SkUtils.h"
-#include "SkVertState.h"
-
-#include "SkBitmapProcShader.h"
-#include "SkDrawProcs.h"
-#include "SkMatrixUtils.h"
-
-//#define TRACE_BITMAP_DRAWS
 
 // Helper function to fix code gen bug on ARM64.
 // See SkFindAndPlaceGlyph.h for more details.
 void FixGCC49Arm64Bug(int v) { }
 
-/** Helper for allocating small blitters on the stack.
- */
-class SkAutoBlitterChoose : SkNoncopyable {
-public:
-    SkAutoBlitterChoose() {
-        fBlitter = nullptr;
-    }
-    SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix,
-                        const SkPaint& paint, bool drawCoverage = false) {
-        fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
-    }
-
-    SkBlitter*  operator->() { return fBlitter; }
-    SkBlitter*  get() const { return fBlitter; }
-
-    void choose(const SkPixmap& dst, const SkMatrix& matrix,
-                const SkPaint& paint, bool drawCoverage = false) {
-        SkASSERT(!fBlitter);
-        fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
-    }
-
-private:
-    // Owned by fAlloc, which will handle the delete.
-    SkBlitter*          fBlitter;
-
-    char fStorage[kSkBlitterContextSize];
-    SkArenaAlloc fAlloc{fStorage};
-};
-#define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
-
 static SkPaint make_paint_with_image(
     const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
     SkPaint paint(origPaint);
@@ -136,7 +98,7 @@
                                            uint32_t* data) {
     // todo: we can apply colorfilter up front if no shader, so we wouldn't
     // need to abort this fastpath
-    if (paint.getShader() || paint.getColorFilter()) {
+    if (paint.getShader() || paint.getColorFilter() || dst.colorSpace()) {
         return nullptr;
     }
 
@@ -1170,11 +1132,10 @@
         int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
         int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
 
-        SkAutoPixmapUnlock result;
-        if (!bitmap.requestLock(&result)) {
+        SkPixmap pmap;
+        if (!bitmap.peekPixels(&pmap)) {
             return;
         }
-        const SkPixmap& pmap = result.pixmap();
         SkMask  mask;
         mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
         mask.fFormat = SkMask::kA8_Format;
@@ -1290,16 +1251,14 @@
         // It is safe to call lock pixels now, since we know the matrix is
         // (more or less) identity.
         //
-        SkAutoPixmapUnlock unlocker;
-        if (!bitmap.requestLock(&unlocker)) {
+        SkPixmap pmap;
+        if (!bitmap.peekPixels(&pmap)) {
             return;
         }
-        const SkPixmap& pmap = unlocker.pixmap();
         int ix = SkScalarRoundToInt(matrix.getTranslateX());
         int iy = SkScalarRoundToInt(matrix.getTranslateY());
         if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
-            char storage[kSkBlitterContextSize];
-            SkArenaAlloc allocator{storage};
+            SkSTArenaAlloc<kSkBlitterContextSize> allocator;
             // blitter will be owned by the allocator.
             SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
             if (blitter) {
@@ -1348,16 +1307,14 @@
     SkPaint paint(origPaint);
     paint.setStyle(SkPaint::kFill_Style);
 
-    SkAutoPixmapUnlock unlocker;
-    if (!bitmap.requestLock(&unlocker)) {
+    SkPixmap pmap;
+    if (!bitmap.peekPixels(&pmap)) {
         return;
     }
-    const SkPixmap& pmap = unlocker.pixmap();
 
     if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
         // blitter will be owned by the allocator.
-        char storage[kSkBlitterContextSize];
-        SkArenaAlloc allocator{storage};
+        SkSTArenaAlloc<kSkBlitterContextSize> allocator;
         SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
         if (blitter) {
             SkScan::FillIRect(bounds, *fRC, blitter);
@@ -1384,6 +1341,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#include "SkPaintPriv.h"
 #include "SkScalerContext.h"
 #include "SkGlyphCache.h"
 #include "SkTextToPathIter.h"
@@ -1401,7 +1359,8 @@
     }
 
     SkMatrix textM;
-    return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM));
+    SkPaintPriv::MakeTextMatrix(&textM, paint);
+    return SkPaint::TooBigToUseCache(ctm, textM);
 }
 
 void SkDraw::drawText_asPaths(const char text[], size_t byteLength, SkScalar x, SkScalar y,
@@ -1671,399 +1630,7 @@
 #pragma warning ( pop )
 #endif
 
-///////////////////////////////////////////////////////////////////////////////
-
-static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
-    return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
-}
-
-static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
-                              const SkPoint texs[], SkMatrix* matrix) {
-    SkPoint src[3], dst[3];
-
-    src[0] = texs[state.f0];
-    src[1] = texs[state.f1];
-    src[2] = texs[state.f2];
-    dst[0] = verts[state.f0];
-    dst[1] = verts[state.f1];
-    dst[2] = verts[state.f2];
-    return matrix->setPolyToPoly(src, dst, 3);
-}
-
-class SkTriColorShader : public SkShader {
-public:
-    SkTriColorShader();
-
-    class TriColorShaderContext : public SkShader::Context {
-    public:
-        TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&);
-        ~TriColorShaderContext() override;
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-
-    private:
-        bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
-
-        SkMatrix    fDstToUnit;
-        SkPMColor   fColors[3];
-        bool fSetup;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    struct TriColorShaderData {
-        const SkPoint* pts;
-        const SkColor* colors;
-        const VertState *state;
-    };
-
-    SK_TO_STRING_OVERRIDE()
-
-    // For serialization.  This will never be called.
-    Factory getFactory() const override { sk_throw(); return nullptr; }
-
-    // Supply setup data to context from drawing setup
-    void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; }
-
-    // Take the setup data from context when needed.
-    TriColorShaderData* takeSetupData() {
-        TriColorShaderData *data = fSetupData;
-        fSetupData = NULL;
-        return data;
-    }
-
-protected:
-    Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
-        return alloc->make<TriColorShaderContext>(*this, rec);
-    }
-
-private:
-    TriColorShaderData *fSetupData;
-
-    typedef SkShader INHERITED;
-};
-
-bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const SkColor colors[],
-                                                    int index0, int index1, int index2) {
-
-    fColors[0] = SkPreMultiplyColor(colors[index0]);
-    fColors[1] = SkPreMultiplyColor(colors[index1]);
-    fColors[2] = SkPreMultiplyColor(colors[index2]);
-
-    SkMatrix m, im;
-    m.reset();
-    m.set(0, pts[index1].fX - pts[index0].fX);
-    m.set(1, pts[index2].fX - pts[index0].fX);
-    m.set(2, pts[index0].fX);
-    m.set(3, pts[index1].fY - pts[index0].fY);
-    m.set(4, pts[index2].fY - pts[index0].fY);
-    m.set(5, pts[index0].fY);
-    if (!m.invert(&im)) {
-        return false;
-    }
-    // We can't call getTotalInverse(), because we explicitly don't want to look at the localmatrix
-    // as our interators are intrinsically tied to the vertices, and nothing else.
-    SkMatrix ctmInv;
-    if (!this->getCTM().invert(&ctmInv)) {
-        return false;
-    }
-    // TODO replace INV(m) * INV(ctm) with INV(ctm * m)
-    fDstToUnit.setConcat(im, ctmInv);
-    return true;
-}
-
-#include "SkColorPriv.h"
-#include "SkComposeShader.h"
-
-static int ScalarTo256(SkScalar v) {
-    return static_cast<int>(SkScalarPin(v, 0, 1) * 256 + 0.5);
-}
-
-SkTriColorShader::SkTriColorShader()
-    : INHERITED(NULL)
-    , fSetupData(NULL) {}
-
-SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader,
-                                                               const ContextRec& rec)
-    : INHERITED(shader, rec)
-    , fSetup(false) {}
-
-SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {}
-
-void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
-    SkTriColorShader* parent = static_cast<SkTriColorShader*>(const_cast<SkShader*>(&fShader));
-    TriColorShaderData* set = parent->takeSetupData();
-    if (set) {
-        fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2);
-    }
-
-    if (!fSetup) {
-        // Invalid matrices. Not checked before so no need to assert.
-        return;
-    }
-
-    const int alphaScale = Sk255To256(this->getPaintAlpha());
-
-    SkPoint src;
-
-    for (int i = 0; i < count; i++) {
-        fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
-        x += 1;
-
-        int scale1 = ScalarTo256(src.fX);
-        int scale2 = ScalarTo256(src.fY);
-        int scale0 = 256 - scale1 - scale2;
-        if (scale0 < 0) {
-            if (scale1 > scale2) {
-                scale2 = 256 - scale1;
-            } else {
-                scale1 = 256 - scale2;
-            }
-            scale0 = 0;
-        }
-
-        if (256 != alphaScale) {
-            scale0 = SkAlphaMul(scale0, alphaScale);
-            scale1 = SkAlphaMul(scale1, alphaScale);
-            scale2 = SkAlphaMul(scale2, alphaScale);
-        }
-
-        dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
-                  SkAlphaMulQ(fColors[1], scale1) +
-                  SkAlphaMulQ(fColors[2], scale2);
-    }
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void SkTriColorShader::toString(SkString* str) const {
-    str->append("SkTriColorShader: (");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
-
-namespace {
-
-// Similar to SkLocalMatrixShader, but composes the local matrix with the CTM (instead
-// of composing with the inherited local matrix):
-//
-//   rec' = {rec.ctm x localMatrix, rec.localMatrix}
-//
-// (as opposed to rec' = {rec.ctm, rec.localMatrix x localMatrix})
-//
-class SkLocalInnerMatrixShader final : public SkShader {
-public:
-    SkLocalInnerMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix)
-    : INHERITED(&localMatrix)
-    , fProxyShader(std::move(proxy)) {}
-
-    Factory getFactory() const override {
-        SkASSERT(false);
-        return nullptr;
-    }
-
-protected:
-    void flatten(SkWriteBuffer&) const override {
-        SkASSERT(false);
-    }
-
-    Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
-        SkMatrix adjustedCTM = SkMatrix::Concat(*rec.fMatrix, this->getLocalMatrix());
-        ContextRec newRec(rec);
-        newRec.fMatrix = &adjustedCTM;
-        return fProxyShader->makeContext(newRec, alloc);
-    }
-
-    bool onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
-                        const SkMatrix& ctm, const SkPaint& paint,
-                        const SkMatrix* localM) const override {
-        // We control the shader graph ancestors, so we know there's no local matrix being
-        // injected before this.
-        SkASSERT(!localM);
-
-        SkMatrix adjustedCTM = SkMatrix::Concat(ctm, this->getLocalMatrix());
-        return fProxyShader->appendStages(p, cs, alloc, adjustedCTM, paint);
-    }
-
-private:
-    sk_sp<SkShader> fProxyShader;
-
-    typedef SkShader INHERITED;
-};
-
-sk_sp<SkShader> MakeTextureShader(const VertState& state, const SkPoint verts[],
-                                         const SkPoint texs[], const SkPaint& paint,
-                                         SkColorSpace* dstColorSpace,
-                                         SkArenaAlloc* alloc) {
-    SkASSERT(paint.getShader());
-
-    const auto& p0 = texs[state.f0],
-                p1 = texs[state.f1],
-                p2 = texs[state.f2];
-
-    if (p0 != p1 || p0 != p2) {
-        // Common case (non-collapsed texture coordinates).
-        // Map the texture to vertices using a local transform.
-
-        // We cannot use a plain SkLocalMatrix shader, because we need the texture matrix
-        // to compose next to the CTM.
-        SkMatrix localMatrix;
-        return texture_to_matrix(state, verts, texs, &localMatrix)
-            ? alloc->makeSkSp<SkLocalInnerMatrixShader>(paint.refShader(), localMatrix)
-            : nullptr;
-    }
-
-    // Collapsed texture coordinates special case.
-    // The texture is a solid color, sampled at the given point.
-    SkMatrix shaderInvLocalMatrix;
-    SkAssertResult(paint.getShader()->getLocalMatrix().invert(&shaderInvLocalMatrix));
-
-    const auto sample       = SkPoint::Make(0.5f, 0.5f);
-    const auto mappedSample = shaderInvLocalMatrix.mapXY(sample.x(), sample.y()),
-               mappedPoint  = shaderInvLocalMatrix.mapXY(p0.x(), p0.y());
-    const auto localMatrix  = SkMatrix::MakeTrans(mappedSample.x() - mappedPoint.x(),
-                                                  mappedSample.y() - mappedPoint.y());
-
-    SkShader::ContextRec rec(paint, SkMatrix::I(), &localMatrix,
-                             SkShader::ContextRec::kPMColor_DstType, dstColorSpace);
-    auto* ctx = paint.getShader()->makeContext(rec, alloc);
-    if (!ctx) {
-        return nullptr;
-    }
-
-    SkPMColor pmColor;
-    ctx->shadeSpan(SkScalarFloorToInt(sample.x()), SkScalarFloorToInt(sample.y()), &pmColor, 1);
-
-    // no need to keep this temp context around.
-    alloc->reset();
-
-    return alloc->makeSkSp<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor));
-}
-
-} // anonymous ns
-
-void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
-                          const SkPoint vertices[], const SkPoint textures[],
-                          const SkColor colors[], SkBlendMode bmode,
-                          const uint16_t indices[], int indexCount,
-                          const SkPaint& paint) const {
-    SkASSERT(0 == count || vertices);
-
-    // abort early if there is nothing to draw
-    if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
-        return;
-    }
-
-    // transform out vertices into device coordinates
-    SkAutoSTMalloc<16, SkPoint> storage(count);
-    SkPoint* devVerts = storage.get();
-    fMatrix->mapPoints(devVerts, vertices, count);
-
-    /*
-        We can draw the vertices in 1 of 4 ways:
-
-        - solid color (no shader/texture[], no colors[])
-        - just colors (no shader/texture[], has colors[])
-        - just texture (has shader/texture[], no colors[])
-        - colors * texture (has shader/texture[], has colors[])
-
-        Thus for texture drawing, we need both texture[] and a shader.
-    */
-
-    auto triShader = sk_make_sp<SkTriColorShader>();
-    SkPaint p(paint);
-
-    SkShader* shader = p.getShader();
-    if (nullptr == shader) {
-        // if we have no shader, we ignore the texture coordinates
-        textures = nullptr;
-    } else if (nullptr == textures) {
-        // if we don't have texture coordinates, ignore the shader
-        p.setShader(nullptr);
-        shader = nullptr;
-    }
-
-    // setup the custom shader (if needed)
-    if (colors) {
-        if (nullptr == textures) {
-            // just colors (no texture)
-            p.setShader(triShader);
-        } else {
-            // colors * texture
-            SkASSERT(shader);
-            p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), bmode));
-        }
-    }
-
-    SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
-    // Abort early if we failed to create a shader context.
-    if (blitter->isNullBlitter()) {
-        return;
-    }
-
-    // setup our state and function pointer for iterating triangles
-    VertState       state(count, indices, indexCount);
-    VertState::Proc vertProc = state.chooseProc(vmode);
-
-    if (textures || colors) {
-        SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state };
-
-        while (vertProc(&state)) {
-            auto* blitterPtr = blitter.get();
-
-            // We're going to allocate at most
-            //
-            //   * one SkLocalMatrixShader OR one SkColorShader
-            //   * one SkComposeShader
-            //   * one SkAutoBlitterChoose
-            //
-            static constexpr size_t kAllocSize =
-                sizeof(SkAutoBlitterChoose) + sizeof(SkComposeShader) +
-                SkTMax(sizeof(SkLocalInnerMatrixShader), sizeof(SkColorShader));
-            char allocBuffer[kAllocSize];
-            SkArenaAlloc alloc(allocBuffer);
-
-            if (textures) {
-                sk_sp<SkShader> texShader = MakeTextureShader(state, vertices, textures, paint,
-                                                              fDst.colorSpace(), &alloc);
-                if (texShader) {
-                    SkPaint localPaint(p);
-                    localPaint.setShader(colors
-                        ? alloc.makeSkSp<SkComposeShader>(triShader, std::move(texShader), bmode)
-                        : std::move(texShader));
-
-                    blitterPtr = alloc.make<SkAutoBlitterChoose>(fDst, *fMatrix, localPaint)->get();
-                    if (blitterPtr->isNullBlitter()) {
-                        continue;
-                    }
-                }
-            }
-            if (colors) {
-                triShader->bindSetupData(&verticesSetup);
-            }
-
-            SkPoint tmp[] = {
-                devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
-            };
-            SkScan::FillTriangle(tmp, *fRC, blitterPtr);
-            triShader->bindSetupData(nullptr);
-        }
-    } else {
-        // no colors[] and no texture, stroke hairlines with paint's color.
-        SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
-        const SkRasterClip& clip = *fRC;
-        while (vertProc(&state)) {
-            SkPoint array[] = {
-                devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
-            };
-            hairProc(array, 4, clip, blitter.get());
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_DEBUG
 
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index 0b29468..f2d5946 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -13,7 +13,9 @@
 #include "SkCanvas.h"
 #include "SkMask.h"
 #include "SkPaint.h"
+#include "SkPixmap.h"
 #include "SkStrokeRec.h"
+#include "SkVertices.h"
 
 class SkBitmap;
 class SkClipStack;
@@ -68,7 +70,7 @@
     void    drawPosText(const char text[], size_t byteLength,
                         const SkScalar pos[], int scalarsPerPosition,
                         const SkPoint& offset, const SkPaint&, const SkSurfaceProps*) const;
-    void    drawVertices(SkCanvas::VertexMode mode, int count,
+    void    drawVertices(SkVertices::VertexMode mode, int count,
                          const SkPoint vertices[], const SkPoint textures[],
                          const SkColor colors[], SkBlendMode bmode,
                          const uint16_t indices[], int ptCount,
@@ -95,6 +97,8 @@
                            SkMask* mask, SkMask::CreateMode mode,
                            SkStrokeRec::InitStyle style);
 
+    void drawDevMask(const SkMask& mask, const SkPaint&) const;
+
     enum RectType {
         kHair_RectType,
         kFill_RectType,
@@ -121,7 +125,6 @@
                                     const SkPaint&, const SkSurfaceProps*) const;
     static SkScalar ComputeResScaleForStroking(const SkMatrix& );
 private:
-    void    drawDevMask(const SkMask& mask, const SkPaint&) const;
     void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
 
     void    drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix,
diff --git a/src/core/SkDrawLooper.cpp b/src/core/SkDrawLooper.cpp
index 54c9af1..db6bd54 100644
--- a/src/core/SkDrawLooper.cpp
+++ b/src/core/SkDrawLooper.cpp
@@ -14,8 +14,7 @@
 
 bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) const {
     SkCanvas canvas;
-    char storage[48];
-    SkArenaAlloc alloc {storage};
+    SkSTArenaAlloc<48> alloc;
 
     SkDrawLooper::Context* context = this->makeContext(&canvas, &alloc);
     for (;;) {
@@ -38,8 +37,7 @@
     const SkRect src = s;
 
     SkCanvas canvas;
-    char storage[48];
-    SkArenaAlloc alloc {storage};
+    SkSTArenaAlloc<48> alloc;
 
     *dst = src;   // catch case where there are no loops
     SkDrawLooper::Context* context = this->makeContext(&canvas, &alloc);
diff --git a/src/core/SkDrawShadowRec.h b/src/core/SkDrawShadowRec.h
new file mode 100644
index 0000000..62f1460
--- /dev/null
+++ b/src/core/SkDrawShadowRec.h
@@ -0,0 +1,23 @@
+/*
+ * 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 SkDrawShadowRec_DEFINED
+#define SkDrawShadowRec_DEFINED
+
+#include "SkPath.h"
+
+struct SkDrawShadowRec {
+    SkPoint3    fZPlaneParams;
+    SkPoint3    fLightPos;
+    SkScalar    fLightRadius;
+    SkScalar    fAmbientAlpha;
+    SkScalar    fSpotAlpha;
+    SkColor     fColor;
+    uint32_t    fFlags;
+};
+
+#endif
diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp
new file mode 100644
index 0000000..d0ef2d7
--- /dev/null
+++ b/src/core/SkDraw_vertices.cpp
@@ -0,0 +1,318 @@
+/*
+ * 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 "SkArenaAlloc.h"
+#include "SkAutoBlitterChoose.h"
+#include "SkComposeShader.h"
+#include "SkDraw.h"
+#include "SkNx.h"
+#include "SkPM4fPriv.h"
+#include "SkRasterClip.h"
+#include "SkScan.h"
+#include "SkShaderBase.h"
+#include "SkString.h"
+#include "SkVertState.h"
+
+#include "SkArenaAlloc.h"
+#include "SkCoreBlitters.h"
+#include "SkColorSpaceXform.h"
+#include "SkColorSpace_Base.h"
+
+struct Matrix43 {
+    float fMat[12];    // column major
+
+    Sk4f map(float x, float y) const {
+        return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]);
+    }
+
+    void setConcat(const Matrix43& a, const SkMatrix& b) {
+        fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY());
+        fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY());
+        fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY());
+        fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY());
+
+        fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY());
+        fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY());
+        fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY());
+        fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY());
+
+        fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8];
+        fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9];
+        fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10];
+        fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11];
+    }
+
+private:
+    float dot(int index, float x, float y) const {
+        return fMat[index + 0] * x + fMat[index + 4] * y;
+    }
+};
+
+static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
+    return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
+}
+
+static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
+                              const SkPoint texs[], SkMatrix* matrix) {
+    SkPoint src[3], dst[3];
+
+    src[0] = texs[state.f0];
+    src[1] = texs[state.f1];
+    src[2] = texs[state.f2];
+    dst[0] = verts[state.f0];
+    dst[1] = verts[state.f1];
+    dst[2] = verts[state.f2];
+    return matrix->setPolyToPoly(src, dst, 3);
+}
+
+class SkTriColorShader : public SkShaderBase {
+public:
+    SkTriColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
+
+    Matrix43* getMatrix43() { return &fM43; }
+
+    bool isOpaque() const override { return fIsOpaque; }
+
+    SK_TO_STRING_OVERRIDE()
+
+    // For serialization.  This will never be called.
+    Factory getFactory() const override { sk_throw(); return nullptr; }
+
+protected:
+    Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
+        return nullptr;
+    }
+    bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
+                        const SkMatrix&, const SkPaint&, const SkMatrix*) const override {
+        pipeline->append(SkRasterPipeline::seed_shader);
+        pipeline->append(SkRasterPipeline::matrix_4x3, &fM43);
+        // In theory we should never need to clamp. However, either due to imprecision in our
+        // matrix43, or the scan converter passing us pixel centers that in fact are not within
+        // the triangle, we do see occasional (slightly) out-of-range values, so we add these
+        // clamp stages. It would be nice to find a way to detect when these are not needed.
+        pipeline->append(SkRasterPipeline::clamp_0);
+        pipeline->append(SkRasterPipeline::clamp_a);
+        return true;
+    }
+
+private:
+    Matrix43 fM43;
+    const bool fIsOpaque;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#ifndef SK_IGNORE_TO_STRING
+void SkTriColorShader::toString(SkString* str) const {
+    str->append("SkTriColorShader: (");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+static bool update_tricolor_matrix(const SkMatrix& ctmInv,
+                                   const SkPoint pts[], const SkPM4f colors[],
+                                   int index0, int index1, int index2, Matrix43* result) {
+    SkMatrix m, im;
+    m.reset();
+    m.set(0, pts[index1].fX - pts[index0].fX);
+    m.set(1, pts[index2].fX - pts[index0].fX);
+    m.set(2, pts[index0].fX);
+    m.set(3, pts[index1].fY - pts[index0].fY);
+    m.set(4, pts[index2].fY - pts[index0].fY);
+    m.set(5, pts[index0].fY);
+    if (!m.invert(&im)) {
+        return false;
+    }
+
+    SkMatrix dstToUnit;
+    dstToUnit.setConcat(im, ctmInv);
+
+    Sk4f c0 = colors[index0].to4f(),
+         c1 = colors[index1].to4f(),
+         c2 = colors[index2].to4f();
+
+    Matrix43 colorm;
+    (c1 - c0).store(&colorm.fMat[0]);
+    (c2 - c0).store(&colorm.fMat[4]);
+    c0.store(&colorm.fMat[8]);
+    result->setConcat(colorm, dstToUnit);
+    return true;
+}
+
+// Convert the SkColors into float colors. The conversion depends on some conditions:
+// - If the pixmap has a dst colorspace, we have to be "color-correct".
+//   Do we map into dst-colorspace before or after we interpolate?
+// - We have to decide when to apply per-color alpha (before or after we interpolate)
+//
+// For now, we will take a simple approach, but recognize this is just a start:
+// - convert colors into dst colorspace before interpolation (matches gradients)
+// - apply per-color alpha before interpolation (matches old version of vertices)
+//
+static SkPM4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
+                              SkArenaAlloc* alloc) {
+    SkPM4f* dst = alloc->makeArray<SkPM4f>(count);
+    if (!deviceCS) {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = SkPM4f_from_SkColor(src[i], nullptr);
+        }
+    } else {
+        auto srcCS = SkColorSpace::MakeSRGB();
+        auto dstCS = as_CSB(deviceCS)->makeLinearGamma();
+        SkColorSpaceXform::Apply(dstCS.get(), SkColorSpaceXform::kRGBA_F32_ColorFormat, dst,
+                                 srcCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, src,
+                                 count, SkColorSpaceXform::kPremul_AlphaOp);
+    }
+    return dst;
+}
+
+static bool compute_is_opaque(const SkColor colors[], int count) {
+    uint32_t c = ~0;
+    for (int i = 0; i < count; ++i) {
+        c &= colors[i];
+    }
+    return SkColorGetA(c) == 0xFF;
+}
+
+void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
+                          const SkPoint vertices[], const SkPoint textures[],
+                          const SkColor colors[], SkBlendMode bmode,
+                          const uint16_t indices[], int indexCount,
+                          const SkPaint& paint) const {
+    SkASSERT(0 == count || vertices);
+
+    // abort early if there is nothing to draw
+    if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
+        return;
+    }
+    SkMatrix ctmInv;
+    if (!fMatrix->invert(&ctmInv)) {
+        return;
+    }
+
+    // make textures and shader mutually consistent
+    SkShader* shader = paint.getShader();
+    if (!(shader && textures)) {
+        shader = nullptr;
+        textures = nullptr;
+    }
+
+    // We can simplify things for certain blendmodes. This is for speed, and SkComposeShader
+    // itself insists we don't pass kSrc or kDst to it.
+    //
+    if (colors && textures) {
+        switch (bmode) {
+            case SkBlendMode::kSrc:
+                colors = nullptr;
+                break;
+            case SkBlendMode::kDst:
+                textures = nullptr;
+                break;
+            default: break;
+        }
+    }
+
+    // we don't use the shader if there are no textures
+    if (!textures) {
+        shader = nullptr;
+    }
+
+    constexpr size_t defCount = 16;
+    constexpr size_t outerSize = sizeof(SkTriColorShader) +
+                                 sizeof(SkComposeShader) +
+                                 (sizeof(SkPoint) + sizeof(SkPM4f)) * defCount;
+    SkSTArenaAlloc<outerSize> outerAlloc;
+
+    SkPoint* devVerts = outerAlloc.makeArray<SkPoint>(count);
+    fMatrix->mapPoints(devVerts, vertices, count);
+
+    VertState       state(count, indices, indexCount);
+    VertState::Proc vertProc = state.chooseProc(vmode);
+
+    if (colors || textures) {
+        SkPM4f*     dstColors = nullptr;
+        Matrix43*   matrix43 = nullptr;
+
+        if (colors) {
+            dstColors = convert_colors(colors, count, fDst.colorSpace(), &outerAlloc);
+
+            SkTriColorShader* triShader = outerAlloc.make<SkTriColorShader>(
+                                                                compute_is_opaque(colors, count));
+            matrix43 = triShader->getMatrix43();
+            if (shader) {
+                shader = outerAlloc.make<SkComposeShader>(sk_ref_sp(triShader), sk_ref_sp(shader),
+                                                          bmode);
+            } else {
+                shader = triShader;
+            }
+        }
+
+        SkPaint p(paint);
+        p.setShader(sk_ref_sp(shader));
+
+        if (!textures) {    // only tricolor shader
+            SkASSERT(matrix43);
+            auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *fMatrix, &outerAlloc);
+            while (vertProc(&state)) {
+                if (!update_tricolor_matrix(ctmInv, vertices, dstColors,
+                                            state.f0, state.f1, state.f2,
+                                            matrix43)) {
+                    continue;
+                }
+
+                SkPoint tmp[] = {
+                    devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
+                };
+                SkScan::FillTriangle(tmp, *fRC, blitter);
+            }
+        } else {
+            while (vertProc(&state)) {
+                SkSTArenaAlloc<2048> innerAlloc;
+
+                const SkMatrix* ctm = fMatrix;
+                SkMatrix tmpCtm;
+                if (textures) {
+                    SkMatrix localM;
+                    texture_to_matrix(state, vertices, textures, &localM);
+                    tmpCtm = SkMatrix::Concat(*fMatrix, localM);
+                    ctm = &tmpCtm;
+                }
+
+                if (matrix43 && !update_tricolor_matrix(ctmInv, vertices, dstColors,
+                                                        state.f0, state.f1, state.f2,
+                                                        matrix43)) {
+                    continue;
+                }
+
+                SkPoint tmp[] = {
+                    devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
+                };
+                auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *ctm, &innerAlloc);
+                SkScan::FillTriangle(tmp, *fRC, blitter);
+            }
+        }
+    } else {
+        // no colors[] and no texture, stroke hairlines with paint's color.
+        SkPaint p;
+        p.setStyle(SkPaint::kStroke_Style);
+        SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
+        // Abort early if we failed to create a shader context.
+        if (blitter->isNullBlitter()) {
+            return;
+        }
+        SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
+        const SkRasterClip& clip = *fRC;
+        while (vertProc(&state)) {
+            SkPoint array[] = {
+                devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
+            };
+            hairProc(array, 4, clip, blitter.get());
+        }
+    }
+}
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
index ceb8f1a..3851930 100644
--- a/src/core/SkEdgeBuilder.cpp
+++ b/src/core/SkEdgeBuilder.cpp
@@ -14,7 +14,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) {
+SkEdgeBuilder::SkEdgeBuilder() {
     fEdgeList = nullptr;
 }
 
@@ -441,3 +441,18 @@
     fEdgeList = fList.begin();
     return fList.count();
 }
+
+int SkEdgeBuilder::build_edges(const SkPath& path, const SkIRect* shiftedClip,
+        int shiftEdgesUp, bool pathContainedInClip, bool analyticAA) {
+    // If we're convex, then we need both edges, even the right edge is past the clip
+    const bool canCullToTheRight = !path.isConvex();
+
+    const SkIRect* builderClip = pathContainedInClip ? nullptr : shiftedClip;
+    int count = this->build(path, builderClip, shiftEdgesUp, canCullToTheRight, analyticAA);
+    SkASSERT(count >= 0);
+
+    // canCullToRight == false should imply count != 1
+    SkASSERT(canCullToTheRight || count != 1);
+
+    return count;
+}
diff --git a/src/core/SkEdgeBuilder.h b/src/core/SkEdgeBuilder.h
index 413d873..2f2b351 100644
--- a/src/core/SkEdgeBuilder.h
+++ b/src/core/SkEdgeBuilder.h
@@ -25,6 +25,9 @@
     int build(const SkPath& path, const SkIRect* clip, int shiftUp, bool clipToTheRight,
               bool analyticAA = false);
 
+    int build_edges(const SkPath& path, const SkIRect* shiftedClip,
+            int shiftEdgesUp, bool pathContainedInClip, bool analyticAA = false);
+
     SkEdge** edgeList() { return (SkEdge**)fEdgeList; }
     SkAnalyticEdge** analyticEdgeList() { return (SkAnalyticEdge**)fEdgeList; }
 
@@ -42,7 +45,7 @@
     bool vertical_line(const SkEdge* edge);
     bool vertical_line(const SkAnalyticEdge* edge);
 
-    SkArenaAlloc        fAlloc;
+    SkSTArenaAlloc<512> fAlloc;
     SkTDArray<void*>    fList;
 
     /*
diff --git a/src/core/SkEmptyShader.h b/src/core/SkEmptyShader.h
deleted file mode 100644
index b2c9b76..0000000
--- a/src/core/SkEmptyShader.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkEmptyShader_DEFINED
-#define SkEmptyShader_DEFINED
-
-#include "SkShader.h"
-
-// TODO: move this to private, as there is a public factory on SkShader
-
-/**
- *  \class SkEmptyShader
- *  A Shader that always draws nothing. Its createContext always returns nullptr.
- */
-class SK_API SkEmptyShader : public SkShader {
-public:
-    SkEmptyShader() {}
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader)
-
-protected:
-    SkShader::Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override {
-        return nullptr;
-    }
-
-    void flatten(SkWriteBuffer& buffer) const override {
-        // Do nothing.
-        // We just don't want to fall through to SkShader::flatten(),
-        // which will write data we don't care to serialize or decode.
-    }
-
-private:
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/core/SkExecutor.cpp b/src/core/SkExecutor.cpp
index 6144dfd..681841d 100644
--- a/src/core/SkExecutor.cpp
+++ b/src/core/SkExecutor.cpp
@@ -13,7 +13,7 @@
 #include "SkTArray.h"
 #include "SkThreadUtils.h"
 
-#if defined(_MSC_VER)
+#if defined(SK_BUILD_FOR_WIN32)
     #include <windows.h>
     static int num_cores() {
         SYSTEM_INFO sysinfo;
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index c2e9b60..17ff43c 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -531,30 +531,34 @@
     return count + 1;
 }
 
-// See http://http.developer.nvidia.com/GPUGems3/gpugems3_ch25.html (from the book GPU Gems 3)
-// discr(I) = d0^2 * (3*d1^2 - 4*d0*d2)
+// See "Resolution Independent Curve Rendering using Programmable Graphics Hardware"
+// https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
+// discr(I) = 3*d2^2 - 4*d1*d3
 // Classification:
-// discr(I) > 0        Serpentine
-// discr(I) = 0        Cusp
-// discr(I) < 0        Loop
-// d0 = d1 = 0         Quadratic
-// d0 = d1 = d2 = 0    Line
-// p0 = p1 = p2 = p3   Point
-static SkCubicType classify_cubic(const SkPoint p[4], const SkScalar d[3]) {
-    if (p[0] == p[1] && p[0] == p[2] && p[0] == p[3]) {
-        return kPoint_SkCubicType;
-    }
-    const SkScalar discr = d[0] * d[0] * (3.f * d[1] * d[1] - 4.f * d[0] * d[2]);
-    if (discr > SK_ScalarNearlyZero) {
-        return kSerpentine_SkCubicType;
-    } else if (discr < -SK_ScalarNearlyZero) {
-        return kLoop_SkCubicType;
-    } else {
-        if (SkScalarAbs(d[0]) < SK_ScalarNearlyZero && SkScalarAbs(d[1]) < SK_ScalarNearlyZero) {
-            return ((SkScalarAbs(d[2]) < SK_ScalarNearlyZero) ? kLine_SkCubicType
-                                                              : kQuadratic_SkCubicType);
+// d1 != 0, discr(I) > 0        Serpentine
+// d1 != 0, discr(I) < 0        Loop
+// d1 != 0, discr(I) = 0        Cusp (with inflection at infinity)
+// d1 = 0, d2 != 0              Cusp (with cusp at infinity)
+// d1 = d2 = 0, d3 != 0         Quadratic
+// d1 = d2 = d3 = 0             Line or Point
+static SkCubicType classify_cubic(SkScalar d[4]) {
+    if (!SkScalarNearlyZero(d[1])) {
+        d[0] = 3 * d[2] * d[2] - 4 * d[1] * d[3];
+        if (d[0] > 0) {
+            return SkCubicType::kSerpentine;
+        } else if (d[0] < 0) {
+            return SkCubicType::kLoop;
         } else {
-            return kCusp_SkCubicType;
+            SkASSERT(0 == d[0]); // Detect NaN.
+            return SkCubicType::kLocalCusp;
+        }
+    } else {
+        if (!SkScalarNearlyZero(d[2])) {
+            return SkCubicType::kInfiniteCusp;
+        } else if (!SkScalarNearlyZero(d[3])) {
+            return SkCubicType::kQuadratic;
+        } else {
+            return SkCubicType::kLineOrPoint;
         }
     }
 }
@@ -577,7 +581,7 @@
 // a2 = p1 . (p0 x p3)
 // a3 = p2 . (p1 x p0)
 // Places the values of d1, d2, d3 in array d passed in
-static void calc_cubic_inflection_func(const SkPoint p[4], SkScalar d[3]) {
+static void calc_cubic_inflection_func(const SkPoint p[4], SkScalar d[4]) {
     SkScalar a1 = calc_dot_cross_cubic(p[0], p[3], p[2]);
     SkScalar a2 = calc_dot_cross_cubic(p[1], p[0], p[3]);
     SkScalar a3 = calc_dot_cross_cubic(p[2], p[1], p[0]);
@@ -586,19 +590,22 @@
     SkScalar max = SkScalarAbs(a1);
     max = SkMaxScalar(max, SkScalarAbs(a2));
     max = SkMaxScalar(max, SkScalarAbs(a3));
-    max = 1.f/max;
-    a1 = a1 * max;
-    a2 = a2 * max;
-    a3 = a3 * max;
+    if (0 != max) {
+        max = 1.f/max;
+        a1 = a1 * max;
+        a2 = a2 * max;
+        a3 = a3 * max;
+    }
 
-    d[2] = 3.f * a3;
-    d[1] = d[2] - a2;
-    d[0] = d[1] - a2 + a1;
+    d[3] = 3.f * a3;
+    d[2] = d[3] - a2;
+    d[1] = d[2] - a2 + a1;
+    d[0] = 0;
 }
 
-SkCubicType SkClassifyCubic(const SkPoint src[4], SkScalar d[3]) {
+SkCubicType SkClassifyCubic(const SkPoint src[4], SkScalar d[4]) {
     calc_cubic_inflection_func(src, d);
-    return classify_cubic(src, d);
+    return classify_cubic(d);
 }
 
 template <typename T> void bubble_sort(T array[], int count) {
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index 55d763b..91b4d2d 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -158,19 +158,26 @@
 bool SkChopMonoCubicAtX(SkPoint src[4], SkScalar y, SkPoint dst[7]);
 bool SkChopMonoCubicAtY(SkPoint src[4], SkScalar x, SkPoint dst[7]);
 
-enum SkCubicType {
-    kSerpentine_SkCubicType,
-    kCusp_SkCubicType,
-    kLoop_SkCubicType,
-    kQuadratic_SkCubicType,
-    kLine_SkCubicType,
-    kPoint_SkCubicType
+enum class SkCubicType {
+    kSerpentine,
+    kLoop,
+    kLocalCusp,     // Cusp at a non-infinite parameter value with an inflection at t=infinity.
+    kInfiniteCusp,  // Cusp with a cusp at t=infinity and a local inflection.
+    kQuadratic,
+    kLineOrPoint
 };
 
-/** Returns the cubic classification. Pass scratch storage for computing inflection data,
-    which can be used with additional work to find the loop intersections and so on.
+/** Returns the cubic classification.
+
+    d[] is filled with the cubic inflection function coefficients. Furthermore, since d0 is always
+    zero for integral curves, if the cubic type is kSerpentine, kLoop, or kLocalCusp then d[0] will
+    instead contain the cubic discriminant: 3*d2^2 - 4*d1*d3.
+
+    See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
+    4.2 Curve Categorization
+    https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
 */
-SkCubicType SkClassifyCubic(const SkPoint p[4], SkScalar inflection[3]);
+SkCubicType SkClassifyCubic(const SkPoint p[4], SkScalar d[4]);
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/core/SkGlobalInitialization_core.cpp b/src/core/SkGlobalInitialization_core.cpp
index 298357e..9fa128f 100644
--- a/src/core/SkGlobalInitialization_core.cpp
+++ b/src/core/SkGlobalInitialization_core.cpp
@@ -19,6 +19,7 @@
 #include "SkPathEffect.h"
 #include "SkPictureShader.h"
 #include "SkRecordedDrawable.h"
+#include "SkShaderBase.h"
 
 /*
  *  Registers all of the required effects subclasses for picture deserialization.
@@ -42,7 +43,7 @@
 
     SkColorFilter::InitializeFlattenables();
     SkPathEffect::InitializeFlattenables();
-    SkShader::InitializeFlattenables();
+    SkShaderBase::InitializeFlattenables();
     SkXfermode::InitializeFlattenables();
 
     // Drawable
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
index e20343d..6a54f92 100644
--- a/src/core/SkGlyphCache.cpp
+++ b/src/core/SkGlyphCache.cpp
@@ -269,7 +269,7 @@
     }
 }
 
-void SkGlyphCache::AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis,
+void SkGlyphCache::AddQuad(const SkPoint pts[3], SkScalar axis, bool yAxis,
                      SkGlyph::Intercept* intercept) {
     SkDQuad quad;
     quad.set(pts);
@@ -282,7 +282,7 @@
     }
 }
 
-void SkGlyphCache::AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis,
+void SkGlyphCache::AddCubic(const SkPoint pts[4], SkScalar axis, bool yAxis,
                       SkGlyph::Intercept* intercept) {
     SkDCubic cubic;
     cubic.set(pts);
@@ -448,6 +448,23 @@
     return prevCount;
 }
 
+int SkGlyphCache_Globals::getCachePointSizeLimit() const {
+    SkAutoExclusive ac(fLock);
+    return fPointSizeLimit;
+}
+
+int SkGlyphCache_Globals::setCachePointSizeLimit(int newLimit) {
+    if (newLimit < 0) {
+        newLimit = 0;
+    }
+
+    SkAutoExclusive ac(fLock);
+
+    int prevLimit = fPointSizeLimit;
+    fPointSizeLimit = newLimit;
+    return prevLimit;
+}
+
 void SkGlyphCache_Globals::purgeAll() {
     SkAutoExclusive ac(fLock);
     this->internalPurge(fTotalMemoryUsed);
@@ -775,6 +792,14 @@
     return get_globals().getCacheCountUsed();
 }
 
+int SkGraphics::GetFontCachePointSizeLimit() {
+    return get_globals().getCachePointSizeLimit();
+}
+
+int SkGraphics::SetFontCachePointSizeLimit(int limit) {
+    return get_globals().setCachePointSizeLimit(limit);
+}
+
 void SkGraphics::PurgeFontCache() {
     get_globals().purgeAll();
     SkTypefaceCache::PurgeAll();
diff --git a/src/core/SkGlyphCache_Globals.h b/src/core/SkGlyphCache_Globals.h
index 4d7fe22..a1e0ac0 100644
--- a/src/core/SkGlyphCache_Globals.h
+++ b/src/core/SkGlyphCache_Globals.h
@@ -21,6 +21,10 @@
     #define SK_DEFAULT_FONT_CACHE_LIMIT     (2 * 1024 * 1024)
 #endif
 
+#ifndef SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT
+    #define SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT  256
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkGlyphCache_Globals {
@@ -31,6 +35,7 @@
         fCacheSizeLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
         fCacheCount = 0;
         fCacheCountLimit = SK_DEFAULT_FONT_CACHE_COUNT_LIMIT;
+        fPointSizeLimit = SK_DEFAULT_FONT_CACHE_POINT_SIZE_LIMIT;
     }
 
     ~SkGlyphCache_Globals() {
@@ -62,6 +67,9 @@
     size_t  getCacheSizeLimit() const;
     size_t  setCacheSizeLimit(size_t limit);
 
+    int  getCachePointSizeLimit() const;
+    int  setCachePointSizeLimit(int limit);
+
     void purgeAll(); // does not change budget
 
     // call when a glyphcache is available for caching (i.e. not in use)
@@ -77,6 +85,7 @@
     size_t  fCacheSizeLimit;
     int32_t fCacheCountLimit;
     int32_t fCacheCount;
+    int32_t fPointSizeLimit;
 
     // Checkout budgets, modulated by the specified min-bytes-needed-to-purge,
     // and attempt to purge caches to match.
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index e832c87..64bf203 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -198,11 +198,11 @@
     SkASSERT(context);
 
     {
+        // MDB TODO: remove this
         // Chrome is crashing with proxies when they need to be instantiated.
         // Force an instantiation here (where, in olden days, we used to require a GrTexture)
         // to see if the input is already un-instantiable.
-        GrTexture* temp = srcProxy->instantiate(context->resourceProvider());
-        if (!temp) {
+        if (!srcProxy->instantiate(context->resourceProvider())) {
             return nullptr;
         }
     }
diff --git a/src/core/SkHalf.h b/src/core/SkHalf.h
index e71cb87..f6c7615 100644
--- a/src/core/SkHalf.h
+++ b/src/core/SkHalf.h
@@ -11,16 +11,12 @@
 #include "SkNx.h"
 #include "SkTypes.h"
 
-#if !defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
-    #include <x86intrin.h>
-#endif
-
 // 16-bit floating point value
 // format is 1 bit sign, 5 bits exponent, 10 bits mantissa
 // only used for storage
 typedef uint16_t SkHalf;
 
-static constexpr uint16_t SK_HalfMin     = 0x0400; // 2^-24  (minimum positive normal value)
+static constexpr uint16_t SK_HalfMin     = 0x0400; // 2^-14  (minimum positive normal value)
 static constexpr uint16_t SK_HalfMax     = 0x7bff; // 65504
 static constexpr uint16_t SK_HalfEpsilon = 0x1400; // 2^-10
 static constexpr uint16_t SK_Half1       = 0x3C00; // 1
@@ -89,29 +85,4 @@
 #endif
 }
 
-static inline Sk8f SkHalfToFloat_finite_ftz(const Sk8h& hs) {
-#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
-    return _mm256_cvtph_ps(hs.fVec);
-
-#else
-    uint64_t parts[2];
-    hs.store(parts);
-    return SkNx_join(SkHalfToFloat_finite_ftz(parts[0]),
-                     SkHalfToFloat_finite_ftz(parts[1]));
-
-#endif
-}
-
-static inline Sk8h SkFloatToHalf_finite_ftz(const Sk8f& fs) {
-#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
-    return _mm256_cvtps_ph(fs.fVec, _MM_FROUND_CUR_DIRECTION);
-
-#else
-    uint64_t parts[2];
-    SkFloatToHalf_finite_ftz(fs.fLo).store(parts+0);
-    SkFloatToHalf_finite_ftz(fs.fHi).store(parts+1);
-    return Sk8h::Load(parts);
-#endif
-}
-
 #endif
diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp
deleted file mode 100644
index 76da0e7..0000000
--- a/src/core/SkImageCacherator.cpp
+++ /dev/null
@@ -1,636 +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.
- */
-
-#include "SkBitmap.h"
-#include "SkBitmapCache.h"
-#include "SkColorSpace_Base.h"
-#include "SkImage_Base.h"
-#include "SkImageCacherator.h"
-#include "SkMallocPixelRef.h"
-#include "SkNextID.h"
-#include "SkPixelRef.h"
-#include "SkResourceCache.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrContextPriv.h"
-#include "GrGpuResourcePriv.h"
-#include "GrImageTextureMaker.h"
-#include "GrResourceKey.h"
-#include "GrResourceProvider.h"
-#include "GrSamplerParams.h"
-#include "GrYUVProvider.h"
-#include "SkGr.h"
-#endif
-
-// Until we actually have codecs/etc. that can contain/support a GPU texture format
-// skip this step, since for some generators, returning their encoded data as a SkData
-// can be somewhat expensive, and this call doesn't indicate to the generator that we're
-// only interested in GPU datas...
-// see skbug.com/ 4971, 5128, ...
-//#define SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
-
-// Helper for exclusive access to a shared generator.
-class SkImageCacherator::ScopedGenerator {
-public:
-    ScopedGenerator(const sk_sp<SharedGenerator>& gen)
-      : fSharedGenerator(gen)
-      , fAutoAquire(gen->fMutex) {}
-
-    SkImageGenerator* operator->() const {
-        fSharedGenerator->fMutex.assertHeld();
-        return fSharedGenerator->fGenerator.get();
-    }
-
-    operator SkImageGenerator*() const {
-        fSharedGenerator->fMutex.assertHeld();
-        return fSharedGenerator->fGenerator.get();
-    }
-
-private:
-    const sk_sp<SharedGenerator>& fSharedGenerator;
-    SkAutoExclusive               fAutoAquire;
-};
-
-SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
-    : fSharedGenerator(std::move(gen)) {
-
-    if (!fSharedGenerator) {
-        return;
-    }
-
-    // The following generator accessors are safe without acquiring the mutex (const getters).
-    // TODO: refactor to use a ScopedGenerator instead, for clarity.
-    const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
-    if (info.isEmpty()) {
-        fSharedGenerator.reset();
-        return;
-    }
-
-    fUniqueID = fSharedGenerator->fGenerator->uniqueID();
-    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
-    if (subset) {
-        if (!bounds.contains(*subset)) {
-            fSharedGenerator.reset();
-            return;
-        }
-        if (*subset != bounds) {
-            // we need a different uniqueID since we really are a subset of the raw generator
-            fUniqueID = SkNextID::ImageID();
-        }
-    } else {
-        subset = &bounds;
-    }
-
-    fInfo   = info.makeWH(subset->width(), subset->height());
-    fOrigin = SkIPoint::Make(subset->x(), subset->y());
-
-    // If the encoded data is in a strange color space (it's not an XYZ matrix space), we won't be
-    // able to preserve the gamut of the encoded data when we decode it. Instead, we'll have to
-    // decode to a known color space (linear sRGB is a good choice). But we need to adjust the
-    // stored color space, because drawing code will ask the SkImage for its color space, which
-    // will in turn ask the cacherator. If we return the A2B color space, then we will be unable to
-    // construct a source-to-dest gamut transformation matrix.
-    if (fInfo.colorSpace() &&
-        SkColorSpace_Base::Type::kXYZ != as_CSB(fInfo.colorSpace())->type()) {
-        fInfo = fInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
-    }
-}
-
-SkImageCacherator* SkImageCacherator::NewFromGenerator(std::unique_ptr<SkImageGenerator> gen,
-                                                       const SkIRect* subset) {
-    Validator validator(SharedGenerator::Make(std::move(gen)), subset);
-
-    return validator ? new SkImageCacherator(&validator) : nullptr;
-}
-
-SkImageCacherator::SkImageCacherator(Validator* validator)
-    : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
-    , fInfo(validator->fInfo)
-    , fOrigin(validator->fOrigin)
-{
-    fUniqueIDs[kLegacy_CachedFormat] = validator->fUniqueID;
-    for (int i = 1; i < kNumCachedFormats; ++i) {
-        // We lazily allocate IDs for non-default caching cases
-        fUniqueIDs[i] = kNeedNewImageUniqueID;
-    }
-    SkASSERT(fSharedGenerator);
-}
-
-SkImageCacherator::~SkImageCacherator() {}
-
-SkData* SkImageCacherator::refEncoded(GrContext* ctx) {
-    ScopedGenerator generator(fSharedGenerator);
-    return generator->refEncodedData(ctx);
-}
-
-static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
-    SkASSERT(bitmap.getGenerationID() == expectedID);
-    SkASSERT(bitmap.isImmutable());
-    SkASSERT(bitmap.getPixels());
-    return true;
-}
-
-// Note, this returns a new, mutable, bitmap, with a new genID.
-// If you want the immutable bitmap with the same ID as our cacherator, call tryLockAsBitmap()
-//
-bool SkImageCacherator::generateBitmap(SkBitmap* bitmap, const SkImageInfo& decodeInfo) {
-    SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
-
-    ScopedGenerator generator(fSharedGenerator);
-    const SkImageInfo& genInfo = generator->getInfo();
-    if (decodeInfo.dimensions() == genInfo.dimensions()) {
-        SkASSERT(fOrigin.x() == 0 && fOrigin.y() == 0);
-        // fast-case, no copy needed
-        return generator->tryGenerateBitmap(bitmap, decodeInfo, allocator);
-    } else {
-        // need to handle subsetting, so we first generate the full size version, and then
-        // "read" from it to get our subset. See https://bug.skia.org/4213
-
-        SkBitmap full;
-        if (!generator->tryGenerateBitmap(&full,
-                                          decodeInfo.makeWH(genInfo.width(), genInfo.height()),
-                                          allocator)) {
-            return false;
-        }
-        if (!bitmap->tryAllocPixels(decodeInfo, nullptr, full.getColorTable())) {
-            return false;
-        }
-        return full.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
-                               fOrigin.x(), fOrigin.y());
-    }
-}
-
-bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
-                                             int srcX, int srcY) {
-    ScopedGenerator generator(fSharedGenerator);
-    const SkImageInfo& genInfo = generator->getInfo();
-    // Currently generators do not natively handle subsets, so check that first.
-    if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
-        return false;
-    }
-    return generator->getPixels(info, pixels, rb);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
-    return kNeedNewImageUniqueID != fUniqueIDs[format] &&
-        SkBitmapCache::Find(SkBitmapCacheDesc::Make(fUniqueIDs[format],
-                                                    fInfo.width(), fInfo.height()), bitmap) &&
-        check_output_bitmap(*bitmap, fUniqueIDs[format]);
-}
-
-bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
-                                        SkImage::CachingHint chint, CachedFormat format,
-                                        const SkImageInfo& info) {
-    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
-        return true;
-    }
-    if (!this->generateBitmap(bitmap, info)) {
-        return false;
-    }
-
-    if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
-        fUniqueIDs[format] = SkNextID::ImageID();
-    }
-    bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
-    if (SkImage::kAllow_CachingHint == chint) {
-        SkBitmapCache::Add(SkBitmapCacheDesc::Make(fUniqueIDs[format],
-                                                   fInfo.width(), fInfo.height()), *bitmap);
-        if (client) {
-            as_IB(client)->notifyAddedToCache();
-        }
-    }
-    return true;
-}
-
-bool SkImageCacherator::lockAsBitmap(GrContext* context, SkBitmap* bitmap, const SkImage* client,
-                                     SkColorSpace* dstColorSpace,
-                                     SkImage::CachingHint chint) {
-    CachedFormat format = this->chooseCacheFormat(dstColorSpace);
-    SkImageInfo cacheInfo = this->buildCacheInfo(format);
-
-    if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
-        fUniqueIDs[format] = SkNextID::ImageID();
-    }
-
-    if (this->tryLockAsBitmap(bitmap, client, chint, format, cacheInfo)) {
-        return check_output_bitmap(*bitmap, fUniqueIDs[format]);
-    }
-
-#if SK_SUPPORT_GPU
-    if (!context) {
-        bitmap->reset();
-        return false;
-    }
-
-    // Try to get a texture and read it back to raster (and then cache that with our ID)
-    sk_sp<GrTextureProxy> proxy;
-
-    {
-        ScopedGenerator generator(fSharedGenerator);
-        proxy = generator->generateTexture(context, cacheInfo, fOrigin);
-    }
-    if (!proxy) {
-        bitmap->reset();
-        return false;
-    }
-
-    if (!bitmap->tryAllocPixels(cacheInfo)) {
-        bitmap->reset();
-        return false;
-    }
-
-    sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
-                                                    proxy,
-                                                    fInfo.refColorSpace())); // src colorSpace
-    if (!sContext) {
-        bitmap->reset();
-        return false;
-    }
-
-    if (!sContext->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
-        bitmap->reset();
-        return false;
-    }
-
-    bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
-    if (SkImage::kAllow_CachingHint == chint) {
-        SkBitmapCache::Add(SkBitmapCacheDesc::Make(fUniqueIDs[format],
-                                                   fInfo.width(), fInfo.height()), *bitmap);
-        if (client) {
-            as_IB(client)->notifyAddedToCache();
-        }
-    }
-    return check_output_bitmap(*bitmap, fUniqueIDs[format]);
-#else
-    return false;
-#endif
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
-// we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
-// chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
-// won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
-// works, so we require that the formats we choose are renderable (as a proxy for being readable).
-struct CacheCaps {
-    CacheCaps(const GrCaps* caps) : fCaps(caps) {}
-
-#if SK_SUPPORT_GPU
-    bool supportsHalfFloat() const {
-        return !fCaps ||
-            (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
-             fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
-    }
-
-    bool supportsSRGB() const {
-        return !fCaps ||
-            (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
-    }
-
-    bool supportsSBGR() const {
-        return !fCaps || fCaps->srgbSupport();
-    }
-#else
-    bool supportsHalfFloat() const { return true; }
-    bool supportsSRGB() const { return true; }
-    bool supportsSBGR() const { return true; }
-#endif
-
-    const GrCaps* fCaps;
-};
-
-SkImageCacherator::CachedFormat SkImageCacherator::chooseCacheFormat(SkColorSpace* dstColorSpace,
-                                                                     const GrCaps* grCaps) {
-    SkColorSpace* cs = fInfo.colorSpace();
-    if (!cs || !dstColorSpace) {
-        return kLegacy_CachedFormat;
-    }
-
-    CacheCaps caps(grCaps);
-    switch (fInfo.colorType()) {
-        case kUnknown_SkColorType:
-        case kAlpha_8_SkColorType:
-        case kRGB_565_SkColorType:
-        case kARGB_4444_SkColorType:
-            // We don't support color space on these formats, so always decode in legacy mode:
-            // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
-            return kLegacy_CachedFormat;
-
-        case kIndex_8_SkColorType:
-            // We can't draw from indexed textures with a color space, so ask the codec to expand
-            if (cs->gammaCloseToSRGB()) {
-                if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            } else {
-                if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            }
-
-        case kGray_8_SkColorType:
-            // TODO: What do we do with grayscale sources that have strange color spaces attached?
-            // The codecs and color space xform don't handle this correctly (yet), so drop it on
-            // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
-            // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
-            // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
-            if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
-                return kSRGB8888_CachedFormat;
-            } else {
-                return kLegacy_CachedFormat;
-            }
-
-        case kRGBA_8888_SkColorType:
-            if (cs->gammaCloseToSRGB()) {
-                if (caps.supportsSRGB()) {
-                    return kAsIs_CachedFormat;
-                } else if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            } else {
-                if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            }
-
-        case kBGRA_8888_SkColorType:
-            // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
-            if (caps.supportsSBGR()) {
-                if (cs->gammaCloseToSRGB()) {
-                    return kAsIs_CachedFormat;
-                } else if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else {
-                    // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
-                    return kLegacy_CachedFormat;
-                }
-            } else {
-                if (cs->gammaCloseToSRGB()) {
-                    if (caps.supportsSRGB()) {
-                        return kSRGB8888_CachedFormat;
-                    } else if (caps.supportsHalfFloat()) {
-                        return kLinearF16_CachedFormat;
-                    } else {
-                        return kLegacy_CachedFormat;
-                    }
-                } else {
-                    if (caps.supportsHalfFloat()) {
-                        return kLinearF16_CachedFormat;
-                    } else if (caps.supportsSRGB()) {
-                        return kSRGB8888_CachedFormat;
-                    } else {
-                        return kLegacy_CachedFormat;
-                    }
-                }
-            }
-
-        case kRGBA_F16_SkColorType:
-            if (!caps.supportsHalfFloat()) {
-                if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            } else if (cs->gammaIsLinear()) {
-                return kAsIs_CachedFormat;
-            } else {
-                return kLinearF16_CachedFormat;
-            }
-    }
-    SkDEBUGFAIL("Unreachable");
-    return kLegacy_CachedFormat;
-}
-
-SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
-    switch (format) {
-        case kLegacy_CachedFormat:
-            return fInfo.makeColorSpace(nullptr);
-        case kAsIs_CachedFormat:
-            return fInfo;
-        case kLinearF16_CachedFormat:
-            return fInfo
-                .makeColorType(kRGBA_F16_SkColorType)
-                .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
-        case kSRGB8888_CachedFormat:
-            return fInfo
-                .makeColorType(kRGBA_8888_SkColorType)
-                .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
-        default:
-            SkDEBUGFAIL("Invalid cached format");
-            return fInfo;
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-void SkImageCacherator::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
-                                                GrUniqueKey* cacheKey) {
-    SkASSERT(!cacheKey->isValid());
-    if (origKey.isValid()) {
-        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-        GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
-        builder[0] = format;
-    }
-}
-
-#ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
-static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
-    const void* rawStart;
-    GrPixelConfig config = GrIsCompressedTextureDataSupported(ctx, data, desc.fWidth, desc.fHeight,
-                                                              &rawStart);
-    if (kUnknown_GrPixelConfig == config) {
-        return nullptr;
-    }
-
-    desc.fConfig = config;
-    return ctx->resourceProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0);
-}
-#endif
-
-class Generator_GrYUVProvider : public GrYUVProvider {
-    SkImageGenerator* fGen;
-
-public:
-    Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
-
-    uint32_t onGetID() override { return fGen->uniqueID(); }
-    bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
-        return fGen->queryYUV8(sizeInfo, colorSpace);
-    }
-    bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
-        return fGen->getYUV8Planes(sizeInfo, planes);
-    }
-};
-
-static void set_key_on_proxy(GrResourceProvider* resourceProvider,
-                             GrTextureProxy* proxy, const GrUniqueKey& key) {
-    if (key.isValid()) {
-        resourceProvider->assignUniqueKeyToProxy(key, proxy);
-    }
-}
-
-sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
-    // TODO: This isn't always correct. Picture generator currently produces textures in N32,
-    // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
-    // information in/on the key so we can return the correct space in case #1 of lockTexture.
-    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
-    SkImageInfo cacheInfo = this->buildCacheInfo(format);
-    return sk_ref_sp(cacheInfo.colorSpace());
-}
-
-/*
- *  We have a 5 ways to try to return a texture (in sorted order)
- *
- *  1. Check the cache for a pre-existing one
- *  2. Ask the generator to natively create one
- *  3. Ask the generator to return a compressed form that the GPU might support
- *  4. Ask the generator to return YUV planes, which the GPU can convert
- *  5. Ask the generator to return RGB(A) data, which the GPU can convert
- */
-sk_sp<GrTextureProxy> SkImageCacherator::lockTextureProxy(GrContext* ctx,
-                                                          const GrUniqueKey& origKey,
-                                                          const SkImage* client,
-                                                          SkImage::CachingHint chint,
-                                                          bool willBeMipped,
-                                                          SkColorSpace* dstColorSpace) {
-    // Values representing the various texture lock paths we can take. Used for logging the path
-    // taken to a histogram.
-    enum LockTexturePath {
-        kFailure_LockTexturePath,
-        kPreExisting_LockTexturePath,
-        kNative_LockTexturePath,
-        kCompressed_LockTexturePath,
-        kYUV_LockTexturePath,
-        kRGBA_LockTexturePath,
-    };
-
-    enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
-
-    // Determine which cached format we're going to use (which may involve decoding to a different
-    // info than the generator provides).
-    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
-
-    // Fold the cache format into our texture key
-    GrUniqueKey key;
-    this->makeCacheKeyFromOrigKey(origKey, format, &key);
-
-    // 1. Check the cache for a pre-existing one
-    if (key.isValid()) {
-        if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
-                                     kLockTexturePathCount);
-            return proxy;
-        }
-    }
-
-    // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
-    // decoded variant of the encoded data, and also a recipe for how to transform the original
-    // info to get the one that we're going to decode to.
-    SkImageInfo cacheInfo = this->buildCacheInfo(format);
-
-    // 2. Ask the generator to natively create one
-    {
-        ScopedGenerator generator(fSharedGenerator);
-        if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
-                                     kLockTexturePathCount);
-            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
-            return proxy;
-        }
-    }
-
-    const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
-
-#ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
-    // 3. Ask the generator to return a compressed form that the GPU might support
-    sk_sp<SkData> data(this->refEncoded(ctx));
-    if (data) {
-        GrTexture* tex = load_compressed_into_texture(ctx, data, desc);
-        if (tex) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath,
-                                     kLockTexturePathCount);
-            return set_key_and_return(tex, key);
-        }
-    }
-#endif
-
-    // 4. Ask the generator to return YUV planes, which the GPU can convert
-    if (!ctx->contextPriv().disableGpuYUVConversion()) {
-        ScopedGenerator generator(fSharedGenerator);
-        Generator_GrYUVProvider provider(generator);
-        if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
-                                     kLockTexturePathCount);
-            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
-            return proxy;
-        }
-    }
-
-    // 5. Ask the generator to return RGB(A) data, which the GPU can convert
-    SkBitmap bitmap;
-    if (this->tryLockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
-        sk_sp<GrTextureProxy> proxy;
-        if (willBeMipped) {
-            proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
-        }
-        if (!proxy) {
-            proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap);
-        }
-        if (proxy) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
-                                     kLockTexturePathCount);
-            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
-            return proxy;
-        }
-    }
-    SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
-                             kLockTexturePathCount);
-    return nullptr;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrTextureProxy> SkImageCacherator::lockAsTextureProxy(GrContext* ctx,
-                                                            const GrSamplerParams& params,
-                                                            SkColorSpace* dstColorSpace,
-                                                            sk_sp<SkColorSpace>* texColorSpace,
-                                                            const SkImage* client,
-                                                            SkScalar scaleAdjust[2],
-                                                            SkImage::CachingHint chint) {
-    if (!ctx) {
-        return nullptr;
-    }
-
-    return GrImageTextureMaker(ctx, this, client, chint).refTextureProxyForParams(params,
-                                                                                  dstColorSpace,
-                                                                                  texColorSpace,
-                                                                                  scaleAdjust);
-}
-
-#endif
diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h
index e179236..3587446 100644
--- a/src/core/SkImageCacherator.h
+++ b/src/core/SkImageCacherator.h
@@ -8,155 +8,52 @@
 #ifndef SkImageCacherator_DEFINED
 #define SkImageCacherator_DEFINED
 
-#include "SkImageGenerator.h"
-#include "SkMutex.h"
-#include "SkTemplates.h"
+#include "SkImage.h"
+#include "SkImageInfo.h"
 
 class GrCaps;
 class GrContext;
-class GrSamplerParams;
 class GrTextureProxy;
 class GrUniqueKey;
-class SkBitmap;
-class SkImage;
+class SkColorSpace;
 
 /*
- *  Internal class to manage caching the output of an ImageGenerator.
+ *  Interface used by GrImageTextureMaker to construct textures from instances of SkImage
+ *  (specifically, SkImage_Lazy).
  */
 class SkImageCacherator {
 public:
-    static SkImageCacherator* NewFromGenerator(std::unique_ptr<SkImageGenerator>,
-                                               const SkIRect* subset = nullptr);
-
-    ~SkImageCacherator();
-
-    const SkImageInfo& info() const { return fInfo; }
-    uint32_t uniqueID() const { return fUniqueIDs[kLegacy_CachedFormat]; }
+    virtual ~SkImageCacherator() {}
 
     enum CachedFormat {
         kLegacy_CachedFormat,    // The format from the generator, with any color space stripped out
-        kAsIs_CachedFormat,      // The format from the generator, with no modification
         kLinearF16_CachedFormat, // Half float RGBA with linear gamma
         kSRGB8888_CachedFormat,  // sRGB bytes
+        kSBGR8888_CachedFormat,  // sRGB bytes, in BGR order
 
         kNumCachedFormats,
     };
 
-    /**
-     *  On success (true), bitmap will point to the pixels for this generator. If this returns
-     *  false, the bitmap will be reset to empty.
-     *
-     *  If not NULL, the client will be notified (->notifyAddedToCache()) when resources are
-     *  added to the cache on its behalf.
-     */
-    bool lockAsBitmap(GrContext*, SkBitmap*, const SkImage* client, SkColorSpace* dstColorSpace,
-                      SkImage::CachingHint = SkImage::kAllow_CachingHint);
+    virtual CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace,
+                                           const GrCaps* = nullptr) const = 0;
+    virtual SkImageInfo buildCacheInfo(CachedFormat) const = 0;
 
 #if SK_SUPPORT_GPU
-    /**
-     *  Returns a ref() on the texture produced by this generator. The caller must call unref()
-     *  when it is done. Will return nullptr on failure.
-     *
-     *  If not NULL, the client will be notified (->notifyAddedToCache()) when resources are
-     *  added to the cache on its behalf.
-     *
-     *  The caller is responsible for calling proxy->unref() when they are done.
-     *
-     *  The scaleAdjust in/out parameter will return any scale adjustment that needs
-     *  to be applied to the absolute texture coordinates in the case where the image
-     *  was resized to meet the sampling requirements (e.g., resized out to the next power of 2).
-     *  It can be null if the caller knows resizing will not be required.
-     */
-    sk_sp<GrTextureProxy> lockAsTextureProxy(GrContext*, const GrSamplerParams&,
-                                             SkColorSpace* dstColorSpace,
-                                             sk_sp<SkColorSpace>* texColorSpace,
-                                             const SkImage* client,
-                                             SkScalar scaleAdjust[2],
-                                             SkImage::CachingHint = SkImage::kAllow_CachingHint);
-#endif
-
-    /**
-     *  If the underlying src naturally is represented by an encoded blob (in SkData), this returns
-     *  a ref to that data. If not, it returns null.
-     *
-     *  If a GrContext is specified, then the caller is only interested in gpu-specific encoded
-     *  formats, so others (e.g. PNG) can just return nullptr.
-     */
-    SkData* refEncoded(GrContext*);
-
-    // Only return true if the generate has already been cached.
-    bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat);
-    // Call the underlying generator directly
-    bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
-                              int srcX, int srcY);
-
-private:
-    // Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing of one generator
-    // among several cacherators.
-    class SharedGenerator final : public SkNVRefCnt<SharedGenerator> {
-    public:
-        static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) {
-            return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr;
-        }
-
-    private:
-        explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen)
-            : fGenerator(std::move(gen))
-        {
-            SkASSERT(fGenerator);
-        }
-
-        friend class ScopedGenerator;
-        friend class SkImageCacherator;
-
-        std::unique_ptr<SkImageGenerator> fGenerator;
-        SkMutex                           fMutex;
-    };
-    class ScopedGenerator;
-
-    struct Validator {
-        Validator(sk_sp<SharedGenerator>, const SkIRect* subset);
-
-        operator bool() const { return fSharedGenerator.get(); }
-
-        sk_sp<SharedGenerator> fSharedGenerator;
-        SkImageInfo            fInfo;
-        SkIPoint               fOrigin;
-        uint32_t               fUniqueID;
-    };
-
-    SkImageCacherator(Validator*);
-
-    CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace, const GrCaps* = nullptr);
-    SkImageInfo buildCacheInfo(CachedFormat);
-
-    bool generateBitmap(SkBitmap*, const SkImageInfo&);
-    bool tryLockAsBitmap(SkBitmap*, const SkImage*, SkImage::CachingHint, CachedFormat,
-                         const SkImageInfo&);
-#if SK_SUPPORT_GPU
     // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it,
     // it should use the passed in key (if the key is valid).
-    sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
-                                           const GrUniqueKey& key,
-                                           const SkImage* client,
-                                           SkImage::CachingHint,
-                                           bool willBeMipped,
-                                           SkColorSpace* dstColorSpace);
+    virtual sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
+                                                   const GrUniqueKey& key,
+                                                   SkImage::CachingHint,
+                                                   bool willBeMipped,
+                                                   SkColorSpace* dstColorSpace) = 0;
+
     // Returns the color space of the texture that would be returned if you called lockTexture.
     // Separate code path to allow querying of the color space for textures that cached (even
     // externally).
-    sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace);
-    void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat, GrUniqueKey* cacheKey);
+    virtual sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace) = 0;
+    virtual void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat,
+                                         GrUniqueKey* cacheKey) = 0;
 #endif
-
-    sk_sp<SharedGenerator> fSharedGenerator;
-    const SkImageInfo      fInfo;
-    const SkIPoint         fOrigin;
-    uint32_t               fUniqueIDs[kNumCachedFormats];
-
-    friend class GrImageTextureMaker;
-    friend class SkImage;
-    friend class SkImage_Generator;
 };
 
 #endif
diff --git a/src/core/SkImageGenerator.cpp b/src/core/SkImageGenerator.cpp
index 18a6ce2..9e5f78e 100644
--- a/src/core/SkImageGenerator.cpp
+++ b/src/core/SkImageGenerator.cpp
@@ -15,8 +15,8 @@
 {}
 
 bool SkImageGenerator::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                                 SkPMColor ctable[], int* ctableCount) {
-    if (kUnknown_SkColorType == info.colorType()) {
+                                 const Options* opts) {
+    if (kUnknown_SkColorType == info.colorType() || kIndex_8_SkColorType == info.colorType()) {
         return false;
     }
     if (nullptr == pixels) {
@@ -26,31 +26,15 @@
         return false;
     }
 
-    if (kIndex_8_SkColorType == info.colorType()) {
-        if (nullptr == ctable || nullptr == ctableCount) {
-            return false;
-        }
-    } else {
-        if (ctableCount) {
-            *ctableCount = 0;
-        }
-        ctableCount = nullptr;
-        ctable = nullptr;
+    Options defaultOpts;
+    if (!opts) {
+        opts = &defaultOpts;
     }
-
-    const bool success = this->onGetPixels(info, pixels, rowBytes, ctable, ctableCount);
-    if (success && ctableCount) {
-        SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
-    }
-    return success;
+    return this->onGetPixels(info, pixels, rowBytes, *opts);
 }
 
 bool SkImageGenerator::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
-    SkASSERT(kIndex_8_SkColorType != info.colorType());
-    if (kIndex_8_SkColorType == info.colorType()) {
-        return false;
-    }
-    return this->getPixels(info, pixels, rowBytes, nullptr, nullptr);
+    return this->getPixels(info, pixels, rowBytes, nullptr);
 }
 
 bool SkImageGenerator::queryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const {
@@ -95,92 +79,11 @@
 }
 #endif
 
-/////////////////////////////////////////////////////////////////////////////////////////////
-
-SkData* SkImageGenerator::onRefEncodedData(GrContext* ctx) {
-    return nullptr;
-}
-
-bool SkImageGenerator::onGetPixels(const SkImageInfo& info, void* dst, size_t rb,
-                                   SkPMColor* colors, int* colorCount) {
-    return false;
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #include "SkBitmap.h"
 #include "SkColorTable.h"
 
-static bool reset_and_return_false(SkBitmap* bitmap) {
-    bitmap->reset();
-    return false;
-}
-
-bool SkImageGenerator::tryGenerateBitmap(SkBitmap* bitmap, const SkImageInfo& info,
-                                         SkBitmap::Allocator* allocator) {
-    if (0 == info.getSafeSize(info.minRowBytes())) {
-        return false;
-    }
-    if (!bitmap->setInfo(info)) {
-        return reset_and_return_false(bitmap);
-    }
-
-    SkPMColor ctStorage[256];
-    memset(ctStorage, 0xFF, sizeof(ctStorage)); // init with opaque-white for the moment
-    sk_sp<SkColorTable> ctable(new SkColorTable(ctStorage, 256));
-    if (!bitmap->tryAllocPixels(allocator, ctable.get())) {
-        // SkResourceCache's custom allcator can'thandle ctables, so it may fail on
-        // kIndex_8_SkColorTable.
-        // https://bug.skia.org/4355
-#if 1
-        // ignore the allocator, and see if we can succeed without it
-        if (!bitmap->tryAllocPixels(nullptr, ctable.get())) {
-            return reset_and_return_false(bitmap);
-        }
-#else
-        // this is the up-scale technique, not fully debugged, but we keep it here at the moment
-        // to remind ourselves that this might be better than ignoring the allocator.
-
-        info = SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType());
-        if (!bitmap->setInfo(info)) {
-            return reset_and_return_false(bitmap);
-        }
-        // we pass nullptr for the ctable arg, since we are now explicitly N32
-        if (!bitmap->tryAllocPixels(allocator, nullptr)) {
-            return reset_and_return_false(bitmap);
-        }
-#endif
-    }
-
-    bitmap->lockPixels();
-    if (!bitmap->getPixels()) {
-        return reset_and_return_false(bitmap);
-    }
-
-    int ctCount = 0;
-    if (!this->getPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
-                         ctStorage, &ctCount)) {
-        return reset_and_return_false(bitmap);
-    }
-
-    if (ctCount > 0) {
-        SkASSERT(kIndex_8_SkColorType == bitmap->colorType());
-        // we and bitmap should be owners
-        SkASSERT(!ctable->unique());
-
-        // Now we need to overwrite the ctable we built earlier, with the correct colors.
-        // This does mean that we may have made the table too big, but that cannot be avoided
-        // until we can change SkImageGenerator's API to return us the ctable *before* we have to
-        // allocate space for all the pixels.
-        ctable->dangerous_overwriteColors(ctStorage, ctCount);
-    } else {
-        SkASSERT(kIndex_8_SkColorType != bitmap->colorType());
-        // we should be the only owner
-        SkASSERT(ctable->unique());
-    }
-    return true;
-}
-
 #include "SkGraphics.h"
 
 static SkGraphics::ImageGeneratorFromEncodedDataFactory gFactory;
diff --git a/src/core/SkImageInfoPriv.h b/src/core/SkImageInfoPriv.h
index 855e506..c9b4b6c 100644
--- a/src/core/SkImageInfoPriv.h
+++ b/src/core/SkImageInfoPriv.h
@@ -11,10 +11,10 @@
 #include "SkImageInfo.h"
 
 /**
- *  Returns true if |info| contains a valid combination of width, height, colorType, alphaType,
- *  colorSpace.  Returns false otherwise.
+ *  This contains shared checks on SkImageInfo.  Depending on the desired color space behavior,
+ *  the caller should choose one of the two versions below.
  */
-static inline bool SkImageInfoIsValid(const SkImageInfo& info) {
+static inline bool SkImageInfoIsValidCommon(const SkImageInfo& info) {
     if (info.width() <= 0 || info.height() <= 0) {
         return false;
     }
@@ -38,6 +38,35 @@
         return false;
     }
 
+    return true;
+}
+
+/**
+ *  Returns true if |info| contains a valid combination of width, height, colorType, alphaType,
+ *  colorSpace.  Allows numerical color spaces.  Returns false otherwise.
+ */
+static inline bool SkImageInfoIsValidAllowNumericalCS(const SkImageInfo& info) {
+    if (!SkImageInfoIsValidCommon(info)) {
+        return false;
+    }
+
+    SkColorSpaceTransferFn fn;
+    if (info.colorSpace() && !info.colorSpace()->isNumericalTransferFn(&fn)) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ *  Returns true if |info| contains a valid combination of width, height, colorType, alphaType,
+ *  colorSpace.  Only supports rendering color spaces.  Returns false otherwise.
+ */
+static inline bool SkImageInfoIsValidRenderingCS(const SkImageInfo& info) {
+    if (!SkImageInfoIsValidCommon(info)) {
+        return false;
+    }
+
     if (info.colorSpace() &&
        (!info.colorSpace()->gammaCloseToSRGB() && !info.colorSpace()->gammaIsLinear())) {
         return false;
@@ -47,6 +76,19 @@
 }
 
 /**
+ *  Returns true if |info| contains a valid combination of width, height, colorType, alphaType,
+ *  colorSpace.  Uses |colorMode| to decide how to treat color spaces.
+ */
+static inline bool SkImageInfoIsValid(const SkImageInfo& info,
+                                      SkDestinationSurfaceColorMode colorMode) {
+    if (SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware == colorMode) {
+        return SkImageInfoIsValidRenderingCS(info);
+    }
+
+    return SkImageInfoIsValidAllowNumericalCS(info);
+}
+
+/**
  *  Returns true if Skia has defined a pixel conversion from the |src| to the |dst|.
  *  Returns false otherwise.  Some discussion of false cases:
  *      We will not convert to kIndex8 unless it exactly matches the src, since color tables
@@ -65,7 +107,7 @@
  *      conversion is not well-defined.
  */
 static inline bool SkImageInfoValidConversion(const SkImageInfo& dst, const SkImageInfo& src) {
-    if (!SkImageInfoIsValid(dst) || !SkImageInfoIsValid(src)) {
+    if (!SkImageInfoIsValidAllowNumericalCS(dst) || !SkImageInfoIsValidAllowNumericalCS(src)) {
         return false;
     }
 
diff --git a/src/core/SkImagePriv.h b/src/core/SkImagePriv.h
index 921fdc8..2e80019 100644
--- a/src/core/SkImagePriv.h
+++ b/src/core/SkImagePriv.h
@@ -50,11 +50,6 @@
 // in which case the surface may need to perform a copy-on-write.
 extern const SkPixelRef* SkBitmapImageGetPixelRef(const SkImage* rasterImage);
 
-// Update the texture wrapped by an image created with NewTexture. This
-// is called when a surface and image share the same GrTexture and the
-// surface needs to perform a copy-on-write
-extern void SkTextureImageSetTexture(SkImage* image, GrTexture* texture);
-
 /**
  *  Will attempt to upload and lock the contents of the image as a texture, so that subsequent
  *  draws to a gpu-target will come from that texture (and not by looking at the original image
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp
deleted file mode 100644
index e1e1882..0000000
--- a/src/core/SkLightingShader.cpp
+++ /dev/null
@@ -1,477 +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.
- */
-
-#include "SkArenaAlloc.h"
-#include "SkBitmapProcShader.h"
-#include "SkBitmapProcState.h"
-#include "SkColor.h"
-#include "SkEmptyShader.h"
-#include "SkLightingShader.h"
-#include "SkMathPriv.h"
-#include "SkNormalSource.h"
-#include "SkPoint3.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-
-////////////////////////////////////////////////////////////////////////////
-
-/*
-   SkLightingShader TODOs:
-        support different light types
-        support multiple lights
-        fix non-opaque diffuse textures
-
-    To Test:
-        A8 diffuse textures
-        down & upsampled draws
-*/
-
-
-
-/** \class SkLightingShaderImpl
-    This subclass of shader applies lighting.
-*/
-class SkLightingShaderImpl : public SkShader {
-public:
-    /** Create a new lighting shader that uses the provided normal map and
-        lights to light the diffuse bitmap.
-        @param diffuseShader     the shader that provides the diffuse colors
-        @param normalSource      the source of normals for lighting computation
-        @param lights            the lights applied to the geometry
-    */
-    SkLightingShaderImpl(sk_sp<SkShader> diffuseShader,
-                         sk_sp<SkNormalSource> normalSource,
-                         sk_sp<SkLights> lights)
-        : fDiffuseShader(std::move(diffuseShader))
-        , fNormalSource(std::move(normalSource))
-        , fLights(std::move(lights)) {}
-
-    bool isOpaque() const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    class LightingShaderContext : public SkShader::Context {
-    public:
-        // The context takes ownership of the context and provider. It will call their destructors
-        // and then indirectly free their memory by calling free() on heapAllocated
-        LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&,
-                              SkShader::Context* diffuseContext, SkNormalSource::Provider*,
-                              void* heapAllocated);
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
-        uint32_t getFlags() const override { return fFlags; }
-
-    private:
-        SkShader::Context*        fDiffuseContext;
-        SkNormalSource::Provider* fNormalProvider;
-        SkColor                   fPaintColor;
-        uint32_t                  fFlags;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingShaderImpl)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    sk_sp<SkShader> fDiffuseShader;
-    sk_sp<SkNormalSource> fNormalSource;
-    sk_sp<SkLights> fLights;
-
-    friend class SkLightingShader;
-
-    typedef SkShader INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "GrCoordTransform.h"
-#include "GrFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#include "SkGr.h"
-
-// This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is
-// handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it
-// premul'd.
-class LightingFP : public GrFragmentProcessor {
-public:
-    LightingFP(sk_sp<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights)
-            : INHERITED(kPreservesOpaqueInput_OptimizationFlag) {
-        // fuse all ambient lights into a single one
-        fAmbientColor = lights->ambientLightColor();
-        for (int i = 0; i < lights->numLights(); ++i) {
-            if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) {
-                fDirectionalLights.push_back(lights->light(i));
-                // TODO get the handle to the shadow map if there is one
-            } else {
-                SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP");
-            }
-        }
-
-        this->registerChildProcessor(std::move(normalFP));
-        this->initClassID<LightingFP>();
-    }
-
-    class GLSLLightingFP : public GrGLSLFragmentProcessor {
-    public:
-        GLSLLightingFP() {
-            fAmbientColor.fX = 0.0f;
-        }
-
-        void emitCode(EmitArgs& args) override {
-
-            GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
-            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-            const LightingFP& lightingFP = args.fFp.cast<LightingFP>();
-
-            const char *lightDirsUniName = nullptr;
-            const char *lightColorsUniName = nullptr;
-            if (lightingFP.fDirectionalLights.count() != 0) {
-                fLightDirsUni = uniformHandler->addUniformArray(
-                        kFragment_GrShaderFlag,
-                        kVec3f_GrSLType,
-                        kDefault_GrSLPrecision,
-                        "LightDir",
-                        lightingFP.fDirectionalLights.count(),
-                        &lightDirsUniName);
-                fLightColorsUni = uniformHandler->addUniformArray(
-                        kFragment_GrShaderFlag,
-                        kVec3f_GrSLType,
-                        kDefault_GrSLPrecision,
-                        "LightColor",
-                        lightingFP.fDirectionalLights.count(),
-                        &lightColorsUniName);
-            }
-
-            const char* ambientColorUniName = nullptr;
-            fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                          kVec3f_GrSLType, kDefault_GrSLPrecision,
-                                                          "AmbientColor", &ambientColorUniName);
-
-            fragBuilder->codeAppendf("vec4 diffuseColor = %s;", args.fInputColor);
-
-            SkString dstNormalName("dstNormal");
-            this->emitChild(0, nullptr, &dstNormalName, args);
-
-            fragBuilder->codeAppendf("vec3 normal = %s.xyz;", dstNormalName.c_str());
-
-            fragBuilder->codeAppend( "vec3 result = vec3(0.0);");
-
-            // diffuse light
-            if (lightingFP.fDirectionalLights.count() != 0) {
-                fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {",
-                                         lightingFP.fDirectionalLights.count());
-                // TODO: modulate the contribution from each light based on the shadow map
-                fragBuilder->codeAppendf("    float NdotL = clamp(dot(normal, %s[i]), 0.0, 1.0);",
-                                         lightDirsUniName);
-                fragBuilder->codeAppendf("    result += %s[i]*diffuseColor.rgb*NdotL;",
-                                         lightColorsUniName);
-                fragBuilder->codeAppend("}");
-            }
-
-            // ambient light
-            fragBuilder->codeAppendf("result += %s * diffuseColor.rgb;", ambientColorUniName);
-
-            // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0)
-            fragBuilder->codeAppendf("%s = vec4(clamp(result.rgb, 0.0, diffuseColor.a), "
-                                               "diffuseColor.a);", args.fOutputColor);
-        }
-
-        static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-            const LightingFP& lightingFP = proc.cast<LightingFP>();
-            b->add32(lightingFP.fDirectionalLights.count());
-        }
-
-    protected:
-        void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
-            const LightingFP& lightingFP = proc.cast<LightingFP>();
-
-            const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights();
-            if (directionalLights != fDirectionalLights) {
-                SkTArray<SkColor3f> lightDirs(directionalLights.count());
-                SkTArray<SkVector3> lightColors(directionalLights.count());
-                for (const SkLights::Light& light : directionalLights) {
-                    lightDirs.push_back(light.dir());
-                    lightColors.push_back(light.color());
-                }
-
-                pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX));
-                pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX));
-
-                fDirectionalLights = directionalLights;
-            }
-
-            const SkColor3f& ambientColor = lightingFP.ambientColor();
-            if (ambientColor != fAmbientColor) {
-                pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
-                fAmbientColor = ambientColor;
-            }
-        }
-
-    private:
-        SkTArray<SkLights::Light> fDirectionalLights;
-        GrGLSLProgramDataManager::UniformHandle fLightDirsUni;
-        GrGLSLProgramDataManager::UniformHandle fLightColorsUni;
-
-        SkColor3f fAmbientColor;
-        GrGLSLProgramDataManager::UniformHandle fAmbientColorUni;
-    };
-
-    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLLightingFP::GenKey(*this, caps, b);
-    }
-
-    const char* name() const override { return "LightingFP"; }
-
-    const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; }
-    const SkColor3f& ambientColor() const { return fAmbientColor; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; }
-
-    bool onIsEqual(const GrFragmentProcessor& proc) const override {
-        const LightingFP& lightingFP = proc.cast<LightingFP>();
-        return fDirectionalLights == lightingFP.fDirectionalLights &&
-               fAmbientColor == lightingFP.fAmbientColor;
-    }
-
-    SkTArray<SkLights::Light> fDirectionalLights;
-    SkColor3f                 fAmbientColor;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const AsFPArgs& args) const {
-    sk_sp<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args));
-    if (!normalFP) {
-        return nullptr;
-    }
-
-    if (fDiffuseShader) {
-        sk_sp<GrFragmentProcessor> fpPipeline[] = {
-            fDiffuseShader->asFragmentProcessor(args),
-            sk_make_sp<LightingFP>(std::move(normalFP), fLights)
-        };
-        if(!fpPipeline[0]) {
-            return nullptr;
-        }
-
-        sk_sp<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2);
-        // FP is wrapped because paint's alpha needs to be applied to output
-        return GrFragmentProcessor::MulOutputByInputAlpha(std::move(innerLightFP));
-    } else {
-        // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP
-        // expects premul'd color.
-        return GrFragmentProcessor::PremulInput(sk_make_sp<LightingFP>(std::move(normalFP),
-                                                                       fLights));
-    }
-}
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////
-
-bool SkLightingShaderImpl::isOpaque() const {
-    return (fDiffuseShader ? fDiffuseShader->isOpaque() : false);
-}
-
-SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(
-        const SkLightingShaderImpl& shader, const ContextRec& rec,
-        SkShader::Context* diffuseContext, SkNormalSource::Provider* normalProvider,
-        void* heapAllocated)
-    : INHERITED(shader, rec)
-    , fDiffuseContext(diffuseContext)
-    , fNormalProvider(normalProvider) {
-    bool isOpaque = shader.isOpaque();
-
-    // update fFlags
-    uint32_t flags = 0;
-    if (isOpaque && (255 == this->getPaintAlpha())) {
-        flags |= kOpaqueAlpha_Flag;
-    }
-
-    fPaintColor = rec.fPaint->getColor();
-    fFlags = flags;
-}
-
-static inline SkPMColor convert(SkColor3f color, U8CPU a) {
-    if (color.fX <= 0.0f) {
-        color.fX = 0.0f;
-    } else if (color.fX >= 255.0f) {
-        color.fX = 255.0f;
-    }
-
-    if (color.fY <= 0.0f) {
-        color.fY = 0.0f;
-    } else if (color.fY >= 255.0f) {
-        color.fY = 255.0f;
-    }
-
-    if (color.fZ <= 0.0f) {
-        color.fZ = 0.0f;
-    } else if (color.fZ >= 255.0f) {
-        color.fZ = 255.0f;
-    }
-
-    return SkPreMultiplyARGB(a, (int) color.fX,  (int) color.fY, (int) color.fZ);
-}
-
-// larger is better (fewer times we have to loop), but we shouldn't
-// take up too much stack-space (each one here costs 16 bytes)
-#define BUFFER_MAX 16
-void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y,
-                                                            SkPMColor result[], int count) {
-    const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader);
-
-    SkPMColor diffuse[BUFFER_MAX];
-    SkPoint3 normals[BUFFER_MAX];
-
-    SkColor diffColor = fPaintColor;
-
-    do {
-        int n = SkTMin(count, BUFFER_MAX);
-
-        fNormalProvider->fillScanLine(x, y, normals, n);
-
-        if (fDiffuseContext) {
-            fDiffuseContext->shadeSpan(x, y, diffuse, n);
-        }
-
-        for (int i = 0; i < n; ++i) {
-            if (fDiffuseContext) {
-                diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]);
-            }
-
-            SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f);
-
-            // Adding ambient light
-            accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor);
-            accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor);
-            accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor);
-
-            // This is all done in linear unpremul color space (each component 0..255.0f though)
-            for (int l = 0; l < lightShader.fLights->numLights(); ++l) {
-                const SkLights::Light& light = lightShader.fLights->light(l);
-
-                SkScalar illuminanceScalingFactor = 1.0f;
-
-                if (SkLights::Light::kDirectional_LightType == light.type()) {
-                    illuminanceScalingFactor = normals[i].dot(light.dir());
-                    if (illuminanceScalingFactor < 0.0f) {
-                        illuminanceScalingFactor = 0.0f;
-                    }
-                }
-
-                accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor;
-                accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor;
-                accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor;
-            }
-
-            // convert() premultiplies the accumulate color with alpha
-            result[i] = convert(accum, SkColorGetA(diffColor));
-        }
-
-        result += n;
-        x += n;
-        count -= n;
-    } while (count > 0);
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-#ifndef SK_IGNORE_TO_STRING
-void SkLightingShaderImpl::toString(SkString* str) const {
-    str->appendf("LightingShader: ()");
-}
-#endif
-
-sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
-
-    // Discarding SkShader flattenable params
-    bool hasLocalMatrix = buf.readBool();
-    SkAssertResult(!hasLocalMatrix);
-
-    sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf);
-
-    sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>());
-
-    bool hasDiffuse = buf.readBool();
-    sk_sp<SkShader> diffuseShader = nullptr;
-    if (hasDiffuse) {
-        diffuseShader = buf.readFlattenable<SkShader>();
-    }
-
-    return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
-                                            std::move(lights));
-}
-
-void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const {
-    this->INHERITED::flatten(buf);
-
-    fLights->flatten(buf);
-
-    buf.writeFlattenable(fNormalSource.get());
-    buf.writeBool(fDiffuseShader);
-    if (fDiffuseShader) {
-        buf.writeFlattenable(fDiffuseShader.get());
-    }
-}
-
-SkShader::Context* SkLightingShaderImpl::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const
-{
-    SkShader::Context *diffuseContext = nullptr;
-    if (fDiffuseShader) {
-        diffuseContext = fDiffuseShader->makeContext(rec, alloc);
-        if (!diffuseContext) {
-            return nullptr;
-        }
-    }
-
-    SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc);
-    if (!normalProvider) {
-        return nullptr;
-    }
-
-    return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader,
-                                       sk_sp<SkNormalSource> normalSource,
-                                       sk_sp<SkLights> lights) {
-    SkASSERT(lights);
-    if (!normalSource) {
-        normalSource = SkNormalSource::MakeFlat();
-    }
-
-    return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
-                                            std::move(lights));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingShaderImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkLights.cpp b/src/core/SkLights.cpp
index 56c9299..1073ba0 100644
--- a/src/core/SkLights.cpp
+++ b/src/core/SkLights.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkColorSpaceXformer.h"
 #include "SkLights.h"
 #include "SkReadBuffer.h"
 
@@ -59,6 +60,28 @@
     return builder.finish();
 }
 
+static SkColor3f xform_color(const SkColor3f& color, SkColorSpaceXformer* xformer) {
+    SkColor origColor = SkColorSetARGBInline(0xFF,
+                                             SkScalarRoundToInt(color.fX),
+                                             SkScalarRoundToInt(color.fY),
+                                             SkScalarRoundToInt(color.fZ));
+    SkColor xformedColor = xformer->apply(origColor);
+    return SkColor3f::Make(SkIntToScalar(SkGetPackedR32(xformedColor)),
+                           SkIntToScalar(SkGetPackedG32(xformedColor)),
+                           SkIntToScalar(SkGetPackedB32(xformedColor)));
+}
+
+sk_sp<SkLights> SkLights::makeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkLights::Builder builder;
+    for (int i = 0; i < this->numLights(); i++) {
+        Light light(fLights[i].type(), xform_color(fLights[i].color(), xformer),
+                    fLights[i].fDirOrPos, fLights[i].fIntensity, fLights[i].isRadial());
+        builder.add(light);
+    }
+    builder.setAmbientLightColor(xform_color(fAmbientLightColor, xformer));
+    return builder.finish();
+}
+
 void SkLights::flatten(SkWriteBuffer& buf) const {
     buf.writeScalarArray(&this->ambientLightColor().fX, 3);
 
diff --git a/src/core/SkLinearBitmapPipeline.cpp b/src/core/SkLinearBitmapPipeline.cpp
index 2878f19..cf2dfdc 100644
--- a/src/core/SkLinearBitmapPipeline.cpp
+++ b/src/core/SkLinearBitmapPipeline.cpp
@@ -417,38 +417,6 @@
     fFirstStage = matrixStage;
 }
 
-SkLinearBitmapPipeline* SkLinearBitmapPipeline::ClonePipelineForBlitting(
-    const SkLinearBitmapPipeline& pipeline,
-    SkMatrix::TypeMask matrixMask,
-    SkFilterQuality filterQuality,
-    const SkPixmap& srcPixmap,
-    float finalAlpha,
-    SkBlendMode blendMode,
-    const SkImageInfo& dstInfo,
-    SkArenaAlloc* allocator)
-{
-    if (blendMode == SkBlendMode::kSrcOver && srcPixmap.info().alphaType() == kOpaque_SkAlphaType) {
-        blendMode = SkBlendMode::kSrc;
-    }
-
-    if (matrixMask & ~SkMatrix::kTranslate_Mask ) { return nullptr; }
-    if (filterQuality != SkFilterQuality::kNone_SkFilterQuality) { return nullptr; }
-    if (finalAlpha != 1.0f) { return nullptr; }
-    if (srcPixmap.info().colorType() != kRGBA_8888_SkColorType
-        || dstInfo.colorType() != kRGBA_8888_SkColorType) { return nullptr; }
-
-    if (!srcPixmap.info().gammaCloseToSRGB() || !dstInfo.gammaCloseToSRGB()) {
-        return nullptr;
-    }
-
-    if (blendMode != SkBlendMode::kSrc && blendMode != SkBlendMode::kSrcOver) {
-        return nullptr;
-    }
-
-    return allocator->make<SkLinearBitmapPipeline>(
-        pipeline, srcPixmap, blendMode, dstInfo, allocator);
-}
-
 void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) {
     SkASSERT(count > 0);
     this->blitSpan(x, y, dst, count);
diff --git a/src/core/SkLinearBitmapPipeline.h b/src/core/SkLinearBitmapPipeline.h
index 8ce0200..6f6e2ae 100644
--- a/src/core/SkLinearBitmapPipeline.h
+++ b/src/core/SkLinearBitmapPipeline.h
@@ -43,16 +43,6 @@
         const SkImageInfo& dstInfo,
         SkArenaAlloc* allocator);
 
-    static SkLinearBitmapPipeline* ClonePipelineForBlitting(
-        const SkLinearBitmapPipeline& pipeline,
-        SkMatrix::TypeMask matrixMask,
-        SkFilterQuality filterQuality,
-        const SkPixmap& srcPixmap,
-        float finalAlpha,
-        SkBlendMode,
-        const SkImageInfo& dstInfo,
-        SkArenaAlloc* allocator);
-
     ~SkLinearBitmapPipeline();
 
     void shadeSpan4f(int x, int y, SkPM4f* dst, int count);
diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp
index 6751634..f1101b1 100644
--- a/src/core/SkLiteDL.cpp
+++ b/src/core/SkLiteDL.cpp
@@ -8,6 +8,8 @@
 #include "SkCanvas.h"
 #include "SkData.h"
 #include "SkDrawFilter.h"
+#include "SkDrawShadowRec.h"
+#include "SkImage.h"
 #include "SkImageFilter.h"
 #include "SkLiteDL.h"
 #include "SkMath.h"
@@ -40,22 +42,21 @@
 
 // Helper for getting back at arrays which have been copy_v'd together after an Op.
 template <typename D, typename T>
-static D* pod(T* op, size_t offset = 0) {
-    return SkTAddOffset<D>(op+1, offset);
+static const D* pod(const T* op, size_t offset = 0) {
+    return SkTAddOffset<const D>(op+1, offset);
 }
 
 namespace {
 #define TYPES(M)                                                                \
     M(SetDrawFilter) M(Save) M(Restore) M(SaveLayer)                            \
-    M(Concat) M(SetMatrix) M(Translate) M(TranslateZ)                           \
+    M(Concat) M(SetMatrix) M(Translate)                                         \
     M(ClipPath) M(ClipRect) M(ClipRRect) M(ClipRegion)                          \
     M(DrawPaint) M(DrawPath) M(DrawRect) M(DrawRegion) M(DrawOval) M(DrawArc)   \
     M(DrawRRect) M(DrawDRRect) M(DrawAnnotation) M(DrawDrawable) M(DrawPicture) \
-    M(DrawShadowedPicture)                                                      \
     M(DrawImage) M(DrawImageNine) M(DrawImageRect) M(DrawImageLattice)          \
     M(DrawText) M(DrawPosText) M(DrawPosTextH)                                  \
     M(DrawTextOnPath) M(DrawTextRSXform) M(DrawTextBlob)                        \
-    M(DrawPatch) M(DrawPoints) M(DrawVertices) M(DrawAtlas)
+    M(DrawPatch) M(DrawPoints) M(DrawVertices) M(DrawAtlas) M(DrawShadowRec)
 
 #define M(T) T,
     enum class Type : uint8_t { TYPES(M) };
@@ -73,7 +74,7 @@
         SetDrawFilter(SkDrawFilter* df) : drawFilter(sk_ref_sp(df)) {}
         sk_sp<SkDrawFilter> drawFilter;
 #endif
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
 #ifdef SK_SUPPORT_LEGACY_DRAWFILTER
             c->setDrawFilter(drawFilter.get());
 #endif
@@ -82,27 +83,33 @@
 
     struct Save final : Op {
         static const auto kType = Type::Save;
-        void draw(SkCanvas* c, const SkMatrix&) { c->save(); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->save(); }
     };
     struct Restore final : Op {
         static const auto kType = Type::Restore;
-        void draw(SkCanvas* c, const SkMatrix&) { c->restore(); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->restore(); }
     };
     struct SaveLayer final : Op {
         static const auto kType = Type::SaveLayer;
         SaveLayer(const SkRect* bounds, const SkPaint* paint,
-                  const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
+                  const SkImageFilter* backdrop, const SkImage* clipMask,
+                  const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
             if (bounds) { this->bounds = *bounds; }
             if (paint)  { this->paint  = *paint;  }
             this->backdrop = sk_ref_sp(backdrop);
+            this->clipMask = sk_ref_sp(clipMask);
+            this->clipMatrix = clipMatrix ? *clipMatrix : SkMatrix::I();
             this->flags = flags;
         }
         SkRect                     bounds = kUnset;
         SkPaint                    paint;
         sk_sp<const SkImageFilter> backdrop;
+        sk_sp<const SkImage>       clipMask;
+        SkMatrix                   clipMatrix;
         SkCanvas::SaveLayerFlags   flags;
-        void draw(SkCanvas* c, const SkMatrix&) {
-            c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), flags });
+        void draw(SkCanvas* c, const SkMatrix&) const {
+            c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), clipMask.get(),
+                           clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags });
         }
     };
 
@@ -110,13 +117,13 @@
         static const auto kType = Type::Concat;
         Concat(const SkMatrix& matrix) : matrix(matrix) {}
         SkMatrix matrix;
-        void draw(SkCanvas* c, const SkMatrix&) { c->concat(matrix); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); }
     };
     struct SetMatrix final : Op {
         static const auto kType = Type::SetMatrix;
         SetMatrix(const SkMatrix& matrix) : matrix(matrix) {}
         SkMatrix matrix;
-        void draw(SkCanvas* c, const SkMatrix& original) {
+        void draw(SkCanvas* c, const SkMatrix& original) const {
             c->setMatrix(SkMatrix::Concat(original, matrix));
         }
     };
@@ -124,20 +131,10 @@
         static const auto kType = Type::Translate;
         Translate(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
         SkScalar dx,dy;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->translate(dx, dy);
         }
     };
-    struct TranslateZ final : Op {
-        static const auto kType = Type::TranslateZ;
-        TranslateZ(SkScalar dz) : dz(dz) {}
-        SkScalar dz;
-        void draw(SkCanvas* c, const SkMatrix&) {
-        #ifdef SK_EXPERIMENTAL_SHADOWING
-            c->translateZ(dz);
-        #endif
-        }
-    };
 
     struct ClipPath final : Op {
         static const auto kType = Type::ClipPath;
@@ -145,7 +142,7 @@
         SkPath   path;
         SkClipOp op;
         bool     aa;
-        void draw(SkCanvas* c, const SkMatrix&) { c->clipPath(path, op, aa); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->clipPath(path, op, aa); }
     };
     struct ClipRect final : Op {
         static const auto kType = Type::ClipRect;
@@ -153,7 +150,7 @@
         SkRect   rect;
         SkClipOp op;
         bool     aa;
-        void draw(SkCanvas* c, const SkMatrix&) { c->clipRect(rect, op, aa); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->clipRect(rect, op, aa); }
     };
     struct ClipRRect final : Op {
         static const auto kType = Type::ClipRRect;
@@ -161,49 +158,49 @@
         SkRRect  rrect;
         SkClipOp op;
         bool     aa;
-        void draw(SkCanvas* c, const SkMatrix&) { c->clipRRect(rrect, op, aa); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->clipRRect(rrect, op, aa); }
     };
     struct ClipRegion final : Op {
         static const auto kType = Type::ClipRegion;
         ClipRegion(const SkRegion& region, SkClipOp op) : region(region), op(op) {}
         SkRegion region;
         SkClipOp op;
-        void draw(SkCanvas* c, const SkMatrix&) { c->clipRegion(region, op); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); }
     };
 
     struct DrawPaint final : Op {
         static const auto kType = Type::DrawPaint;
         DrawPaint(const SkPaint& paint) : paint(paint) {}
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawPaint(paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawPaint(paint); }
     };
     struct DrawPath final : Op {
         static const auto kType = Type::DrawPath;
         DrawPath(const SkPath& path, const SkPaint& paint) : path(path), paint(paint) {}
         SkPath  path;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawPath(path, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawPath(path, paint); }
     };
     struct DrawRect final : Op {
         static const auto kType = Type::DrawRect;
         DrawRect(const SkRect& rect, const SkPaint& paint) : rect(rect), paint(paint) {}
         SkRect  rect;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawRect(rect, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawRect(rect, paint); }
     };
     struct DrawRegion final : Op {
         static const auto kType = Type::DrawRegion;
         DrawRegion(const SkRegion& region, const SkPaint& paint) : region(region), paint(paint) {}
         SkRegion region;
         SkPaint  paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawRegion(region, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawRegion(region, paint); }
     };
     struct DrawOval final : Op {
         static const auto kType = Type::DrawOval;
         DrawOval(const SkRect& oval, const SkPaint& paint) : oval(oval), paint(paint) {}
         SkRect  oval;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawOval(oval, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawOval(oval, paint); }
     };
     struct DrawArc final : Op {
         static const auto kType = Type::DrawArc;
@@ -216,15 +213,15 @@
         SkScalar sweepAngle;
         bool useCenter;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawArc(oval, startAngle, sweepAngle,
-                                                             useCenter, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawArc(oval, startAngle, sweepAngle,
+                                                                   useCenter, paint); }
     };
     struct DrawRRect final : Op {
         static const auto kType = Type::DrawRRect;
         DrawRRect(const SkRRect& rrect, const SkPaint& paint) : rrect(rrect), paint(paint) {}
         SkRRect rrect;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawRRect(rrect, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawRRect(rrect, paint); }
     };
     struct DrawDRRect final : Op {
         static const auto kType = Type::DrawDRRect;
@@ -232,7 +229,7 @@
             : outer(outer), inner(inner), paint(paint) {}
         SkRRect outer, inner;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawDRRect(outer, inner, paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawDRRect(outer, inner, paint); }
     };
 
     struct DrawAnnotation final : Op {
@@ -240,7 +237,7 @@
         DrawAnnotation(const SkRect& rect, SkData* value) : rect(rect), value(sk_ref_sp(value)) {}
         SkRect        rect;
         sk_sp<SkData> value;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawAnnotation(rect, pod<char>(this), value.get());
         }
     };
@@ -251,7 +248,7 @@
         }
         sk_sp<SkDrawable> drawable;
         SkMatrix          matrix = SkMatrix::I();
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawDrawable(drawable.get(), &matrix);
         }
     };
@@ -266,29 +263,10 @@
         SkMatrix               matrix = SkMatrix::I();
         SkPaint                paint;
         bool                   has_paint = false;  // TODO: why is a default paint not the same?
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawPicture(picture.get(), &matrix, has_paint ? &paint : nullptr);
         }
     };
-    struct DrawShadowedPicture final : Op {
-        static const auto kType = Type::DrawShadowedPicture;
-        DrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix,
-                            const SkPaint* paint, const SkShadowParams& params)
-            : picture(sk_ref_sp(picture)) {
-            if (matrix) { this->matrix = *matrix; }
-            if (paint)  { this->paint  = *paint;  }
-            this->params = params;
-        }
-        sk_sp<const SkPicture> picture;
-        SkMatrix               matrix = SkMatrix::I();
-        SkPaint                paint;
-        SkShadowParams         params;
-        void draw(SkCanvas* c, const SkMatrix&) {
-        #ifdef SK_EXPERIMENTAL_SHADOWING
-            c->drawShadowedPicture(picture.get(), &matrix, &paint, params);
-        #endif
-        }
-    };
 
     struct DrawImage final : Op {
         static const auto kType = Type::DrawImage;
@@ -299,7 +277,7 @@
         sk_sp<const SkImage> image;
         SkScalar x,y;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) { c->drawImage(image.get(), x,y, &paint); }
+        void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x,y, &paint); }
     };
     struct DrawImageNine final : Op {
         static const auto kType = Type::DrawImageNine;
@@ -312,7 +290,7 @@
         SkIRect center;
         SkRect  dst;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawImageNine(image.get(), center, dst, &paint);
         }
     };
@@ -328,7 +306,7 @@
         SkRect src, dst;
         SkPaint paint;
         SkCanvas::SrcRectConstraint constraint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawImageRect(image.get(), src, dst, &paint, constraint);
         }
     };
@@ -344,7 +322,7 @@
         SkIRect              src;
         SkRect               dst;
         SkPaint              paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             auto xdivs = pod<int>(this, 0),
                  ydivs = pod<int>(this, xs*sizeof(int));
             auto flags = (0 == fs) ? nullptr :
@@ -360,7 +338,7 @@
         size_t bytes;
         SkScalar x,y;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawText(pod<void>(this), bytes, x,y, paint);
         }
     };
@@ -371,7 +349,7 @@
         size_t bytes;
         SkPaint paint;
         int n;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             auto points = pod<SkPoint>(this);
             auto text   = pod<void>(this, n*sizeof(SkPoint));
             c->drawPosText(text, bytes, points, paint);
@@ -385,7 +363,7 @@
         SkScalar y;
         SkPaint  paint;
         int n;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             auto xs   = pod<SkScalar>(this);
             auto text = pod<void>(this, n*sizeof(SkScalar));
             c->drawPosTextH(text, bytes, xs, y, paint);
@@ -402,7 +380,7 @@
         SkPath   path;
         SkMatrix matrix = SkMatrix::I();
         SkPaint  paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawTextOnPath(pod<void>(this), bytes, path, &matrix, paint);
         }
     };
@@ -415,7 +393,7 @@
         size_t  bytes;
         SkRect  cull = kUnset;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawTextRSXform(pod<void>(this), bytes, pod<SkRSXform>(this, bytes),
                                maybe_unset(cull), paint);
         }
@@ -427,7 +405,7 @@
         sk_sp<const SkTextBlob> blob;
         SkScalar x,y;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawTextBlob(blob.get(), x,y, paint);
         }
     };
@@ -449,7 +427,7 @@
         SkPaint           paint;
         bool              has_colors = false;
         bool              has_texs   = false;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawPatch(cubics, has_colors ? colors : nullptr, has_texs ? texs : nullptr,
                          xfermode, paint);
         }
@@ -461,7 +439,7 @@
         SkCanvas::PointMode mode;
         size_t              count;
         SkPaint             paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawPoints(mode, count, pod<SkPoint>(this), paint);
         }
     };
@@ -472,7 +450,7 @@
         sk_sp<SkVertices> vertices;
         SkBlendMode mode;
         SkPaint paint;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             c->drawVertices(vertices, mode, paint);
         }
     };
@@ -490,7 +468,7 @@
         SkRect               cull = kUnset;
         SkPaint              paint;
         bool                 has_colors;
-        void draw(SkCanvas* c, const SkMatrix&) {
+        void draw(SkCanvas* c, const SkMatrix&) const {
             auto xforms = pod<SkRSXform>(this, 0);
             auto   texs = pod<SkRect>(this, count*sizeof(SkRSXform));
             auto colors = has_colors
@@ -500,6 +478,17 @@
                          maybe_unset(cull), &paint);
         }
     };
+    struct DrawShadowRec final : Op {
+        static const auto kType = Type::DrawShadowRec;
+        DrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec)
+            : fPath(path), fRec(rec)
+        {}
+        SkPath          fPath;
+        SkDrawShadowRec fRec;
+        void draw(SkCanvas* c, const SkMatrix&) const {
+            c->private_draw_shadow_rec(fPath, fRec);
+        }
+    };
 }
 
 template <typename T, typename... Args>
@@ -522,10 +511,10 @@
 }
 
 template <typename Fn, typename... Args>
-inline void SkLiteDL::map(const Fn fns[], Args... args) {
+inline void SkLiteDL::map(const Fn fns[], Args... args) const {
     auto end = fBytes.get() + fUsed;
-    for (uint8_t* ptr = fBytes.get(); ptr < end; ) {
-        auto op = (Op*)ptr;
+    for (const uint8_t* ptr = fBytes.get(); ptr < end; ) {
+        auto op = (const Op*)ptr;
         auto type = op->type;
         auto skip = op->skip;
         if (auto fn = fns[type]) {  // We replace no-op functions with nullptrs
@@ -544,14 +533,14 @@
 void SkLiteDL::   save() { this->push   <Save>(0); }
 void SkLiteDL::restore() { this->push<Restore>(0); }
 void SkLiteDL::saveLayer(const SkRect* bounds, const SkPaint* paint,
-                         const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
-    this->push<SaveLayer>(0, bounds, paint, backdrop, flags);
+                         const SkImageFilter* backdrop, const SkImage* clipMask,
+                         const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
+    this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
 }
 
 void SkLiteDL::   concat(const SkMatrix& matrix)   { this->push   <Concat>(0, matrix); }
 void SkLiteDL::setMatrix(const SkMatrix& matrix)   { this->push<SetMatrix>(0, matrix); }
 void SkLiteDL::translate(SkScalar dx, SkScalar dy) { this->push<Translate>(0, dx, dy); }
-void SkLiteDL::translateZ(SkScalar dz) { this->push<TranslateZ>(0, dz); }
 
 void SkLiteDL::clipPath(const SkPath& path, SkClipOp op, bool aa) {
     this->push<ClipPath>(0, path, op, aa);
@@ -604,11 +593,6 @@
                            const SkMatrix* matrix, const SkPaint* paint) {
     this->push<DrawPicture>(0, picture, matrix, paint);
 }
-void SkLiteDL::drawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix,
-                                   const SkPaint* paint, const SkShadowParams& params) {
-    push<DrawShadowedPicture>(0, picture, matrix, paint, params);
-}
-
 void SkLiteDL::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y, const SkPaint* paint) {
     this->push<DrawImage>(0, std::move(image), x,y, paint);
 }
@@ -690,12 +674,17 @@
                   texs, count,
                 colors, colors ? count : 0);
 }
+void SkLiteDL::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+    this->push<DrawShadowRec>(0, path, rec);
+}
 
-typedef void(*draw_fn)(void*,  SkCanvas*, const SkMatrix&);
-typedef void(*void_fn)(void*);
+typedef void(*draw_fn)(const void*,  SkCanvas*, const SkMatrix&);
+typedef void(*void_fn)(const void*);
 
 // All ops implement draw().
-#define M(T) [](void* op, SkCanvas* c, const SkMatrix& original) { ((T*)op)->draw(c, original); },
+#define M(T) [](const void* op, SkCanvas* c, const SkMatrix& original) { \
+    ((const T*)op)->draw(c, original);                                         \
+},
 static const draw_fn draw_fns[] = { TYPES(M) };
 #undef M
 
@@ -707,11 +696,12 @@
 #endif
 
 // Most state ops (matrix, clip, save, restore) have a trivial destructor.
-#define M(T) !can_skip_destructor<T>::value ? [](void* op) { ((T*)op)->~T(); } : (void_fn)nullptr,
+#define M(T) !can_skip_destructor<T>::value ? [](const void* op) { ((const T*)op)->~T(); } \
+                                            : (void_fn)nullptr,
 static const void_fn dtor_fns[] = { TYPES(M) };
 #undef M
 
-void SkLiteDL::draw(SkCanvas* canvas) {
+void SkLiteDL::draw(SkCanvas* canvas) const {
     SkAutoCanvasRestore acr(canvas, false);
     this->map(draw_fns, canvas, canvas->getTotalMatrix());
 }
diff --git a/src/core/SkLiteDL.h b/src/core/SkLiteDL.h
index f5f7b21..31ef38e 100644
--- a/src/core/SkLiteDL.h
+++ b/src/core/SkLiteDL.h
@@ -14,12 +14,13 @@
 #include "SkDrawable.h"
 #include "SkRect.h"
 #include "SkTDArray.h"
+#include "SkTemplates.h"
 
 class SkLiteDL final {
 public:
     ~SkLiteDL();
 
-    void draw(SkCanvas* canvas);
+    void draw(SkCanvas* canvas) const;
 
     void reset();
     bool empty() const { return fUsed == 0; }
@@ -29,7 +30,8 @@
 #endif
 
     void save();
-    void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags);
+    void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
+                   const SkMatrix*, SkCanvas::SaveLayerFlags);
     void restore();
 
     void    concat (const SkMatrix&);
@@ -54,8 +56,6 @@
     void drawAnnotation     (const SkRect&, const char*, SkData*);
     void drawDrawable       (SkDrawable*, const SkMatrix*);
     void drawPicture        (const SkPicture*, const SkMatrix*, const SkPaint*);
-    void drawShadowedPicture(const SkPicture*, const SkMatrix*,
-                             const SkPaint*, const SkShadowParams& params);
 
     void drawText       (const void*, size_t, SkScalar, SkScalar, const SkPaint&);
     void drawPosText    (const void*, size_t, const SkPoint[], const SkPaint&);
@@ -77,13 +77,14 @@
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&);
     void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
                    SkBlendMode, const SkRect*, const SkPaint*);
+    void drawShadowRec(const SkPath&, const SkDrawShadowRec&);
 
 private:
     template <typename T, typename... Args>
     void* push(size_t, Args&&...);
 
     template <typename Fn, typename... Args>
-    void map(const Fn[], Args...);
+    void map(const Fn[], Args...) const;
 
     SkAutoTMalloc<uint8_t> fBytes;
     size_t                 fUsed = 0;
diff --git a/src/core/SkLiteRecorder.cpp b/src/core/SkLiteRecorder.cpp
index f899f88..dbee48e 100644
--- a/src/core/SkLiteRecorder.cpp
+++ b/src/core/SkLiteRecorder.cpp
@@ -14,7 +14,7 @@
     , fDL(nullptr) {}
 
 void SkLiteRecorder::reset(SkLiteDL* dl, const SkIRect& bounds) {
-    this->resetForNextPicture(bounds);
+    this->resetCanvas(bounds.right(), bounds.bottom());
     fDL = dl;
 }
 
@@ -31,7 +31,8 @@
 
 void SkLiteRecorder::willSave() { fDL->save(); }
 SkCanvas::SaveLayerStrategy SkLiteRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) {
-    fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fSaveLayerFlags);
+    fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fClipMask, rec.fClipMatrix,
+                   rec.fSaveLayerFlags);
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 void SkLiteRecorder::willRestore() { fDL->restore(); }
@@ -193,13 +194,6 @@
                                  const SkPaint* paint) {
     fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, cull, paint);
 }
-
-void SkLiteRecorder::didTranslateZ(SkScalar dz) {
-    fDL->translateZ(dz);
-}
-void SkLiteRecorder::onDrawShadowedPicture(const SkPicture* picture,
-                                           const SkMatrix* matrix,
-                                           const SkPaint* paint,
-                                           const SkShadowParams& params) {
-    fDL->drawShadowedPicture(picture, matrix, paint, params);
+void SkLiteRecorder::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+    fDL->drawShadowRec(path, rec);
 }
diff --git a/src/core/SkLiteRecorder.h b/src/core/SkLiteRecorder.h
index ea7cc78..4d49eb5 100644
--- a/src/core/SkLiteRecorder.h
+++ b/src/core/SkLiteRecorder.h
@@ -77,16 +77,7 @@
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int, SkBlendMode, const SkRect*, const SkPaint*) override;
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void didTranslateZ(SkScalar) override;
-    void onDrawShadowedPicture(const SkPicture*, const SkMatrix*,
-                               const SkPaint*, const SkShadowParams& params) override;
-#else
-    void didTranslateZ(SkScalar);
-    void onDrawShadowedPicture(const SkPicture*, const SkMatrix*,
-                               const SkPaint*, const SkShadowParams& params);
-#endif
+    void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
 
 private:
     typedef SkNoDrawCanvas INHERITED;
diff --git a/src/core/SkLocalMatrixImageFilter.cpp b/src/core/SkLocalMatrixImageFilter.cpp
index 864b24b..1e50101 100644
--- a/src/core/SkLocalMatrixImageFilter.cpp
+++ b/src/core/SkLocalMatrixImageFilter.cpp
@@ -55,6 +55,14 @@
     return this->getInput(0)->filterBounds(src, SkMatrix::Concat(matrix, fLocalM), direction);
 }
 
+sk_sp<SkImageFilter> SkLocalMatrixImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs() && this->getInput(0));
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkLocalMatrixImageFilter::Make(fLocalM, std::move(input));
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkLocalMatrixImageFilter::toString(SkString* str) const {
     str->append("SkLocalMatrixImageFilter: (");
diff --git a/src/core/SkLocalMatrixImageFilter.h b/src/core/SkLocalMatrixImageFilter.h
index 5d69a20..b19c065 100644
--- a/src/core/SkLocalMatrixImageFilter.h
+++ b/src/core/SkLocalMatrixImageFilter.h
@@ -25,6 +25,7 @@
     void flatten(SkWriteBuffer&) const override;
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
 
 private:
diff --git a/src/core/SkLocalMatrixShader.cpp b/src/core/SkLocalMatrixShader.cpp
deleted file mode 100644
index 33472f1..0000000
--- a/src/core/SkLocalMatrixShader.cpp
+++ /dev/null
@@ -1,111 +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 "SkLocalMatrixShader.h"
-
-#if SK_SUPPORT_GPU
-#include "GrFragmentProcessor.h"
-#endif
-
-#if SK_SUPPORT_GPU
-sk_sp<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(const AsFPArgs& args) const {
-    SkMatrix tmp = this->getLocalMatrix();
-    if (args.fLocalMatrix) {
-        tmp.preConcat(*args.fLocalMatrix);
-    }
-    return fProxyShader->asFragmentProcessor(AsFPArgs(
-        args.fContext, args.fViewMatrix, &tmp, args.fFilterQuality, args.fDstColorSpace));
-}
-#endif
-
-sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) {
-    SkMatrix lm;
-    buffer.readMatrix(&lm);
-    auto baseShader(buffer.readShader());
-    if (!baseShader) {
-        return nullptr;
-    }
-    return baseShader->makeWithLocalMatrix(lm);
-}
-
-void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeMatrix(this->getLocalMatrix());
-    buffer.writeFlattenable(fProxyShader.get());
-}
-
-SkShader::Context* SkLocalMatrixShader::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const
-{
-    ContextRec newRec(rec);
-    SkMatrix tmp;
-    if (rec.fLocalMatrix) {
-        tmp.setConcat(*rec.fLocalMatrix, this->getLocalMatrix());
-        newRec.fLocalMatrix = &tmp;
-    } else {
-        newRec.fLocalMatrix = &this->getLocalMatrix();
-    }
-    return fProxyShader->makeContext(newRec, alloc);
-}
-
-SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, enum TileMode* mode) const {
-    SkMatrix imageMatrix;
-    SkImage* image = fProxyShader->isAImage(&imageMatrix, mode);
-    if (image && outMatrix) {
-        // Local matrix must be applied first so it is on the right side of the concat.
-        *outMatrix = SkMatrix::Concat(imageMatrix, this->getLocalMatrix());
-    }
-
-    return image;
-}
-
-bool SkLocalMatrixShader::onAppendStages(SkRasterPipeline* p,
-                                         SkColorSpace* dst,
-                                         SkArenaAlloc* scratch,
-                                         const SkMatrix& ctm,
-                                         const SkPaint& paint,
-                                         const SkMatrix* localM) const {
-    SkMatrix tmp;
-    if (localM) {
-        tmp.setConcat(*localM, this->getLocalMatrix());
-    }
-
-    return fProxyShader->onAppendStages(p, dst, scratch, ctm, paint,
-                                        localM ? &tmp : &this->getLocalMatrix());
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void SkLocalMatrixShader::toString(SkString* str) const {
-    str->append("SkLocalMatrixShader: (");
-
-    fProxyShader->toString(str);
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
-
-sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
-    if (localMatrix.isIdentity()) {
-        return sk_ref_sp(const_cast<SkShader*>(this));
-    }
-
-    const SkMatrix* lm = &localMatrix;
-
-    sk_sp<SkShader> baseShader;
-    SkMatrix otherLocalMatrix;
-    sk_sp<SkShader> proxy(this->makeAsALocalMatrixShader(&otherLocalMatrix));
-    if (proxy) {
-        otherLocalMatrix.preConcat(localMatrix);
-        lm = &otherLocalMatrix;
-        baseShader = proxy;
-    } else {
-        baseShader = sk_ref_sp(const_cast<SkShader*>(this));
-    }
-
-    return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
-}
diff --git a/src/core/SkLocalMatrixShader.h b/src/core/SkLocalMatrixShader.h
deleted file mode 100644
index cba1409..0000000
--- a/src/core/SkLocalMatrixShader.h
+++ /dev/null
@@ -1,65 +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 SkLocalMatrixShader_DEFINED
-#define SkLocalMatrixShader_DEFINED
-
-#include "SkShader.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-
-class GrFragmentProcessor;
-class SkArenaAlloc;
-
-class SkLocalMatrixShader : public SkShader {
-public:
-    SkLocalMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix)
-    : INHERITED(&localMatrix)
-    , fProxyShader(std::move(proxy))
-    {}
-
-    GradientType asAGradient(GradientInfo* info) const override {
-        return fProxyShader->asAGradient(info);
-    }
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    sk_sp<SkShader> makeAsALocalMatrixShader(SkMatrix* localMatrix) const override {
-        if (localMatrix) {
-            *localMatrix = this->getLocalMatrix();
-        }
-        return fProxyShader;
-    }
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLocalMatrixShader)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-    SkImage* onIsAImage(SkMatrix* matrix, TileMode* mode) const override;
-
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                        const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
-
-#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
-    bool onIsABitmap(SkBitmap* bitmap, SkMatrix* matrix, TileMode* mode) const override {
-        return fProxyShader->isABitmap(bitmap, matrix, mode);
-    }
-#endif
-
-private:
-    sk_sp<SkShader> fProxyShader;
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp
index 0d758fc..cf4fac7 100644
--- a/src/core/SkMallocPixelRef.cpp
+++ b/src/core/SkMallocPixelRef.cpp
@@ -37,22 +37,23 @@
     return true;
 }
 
-SkMallocPixelRef* SkMallocPixelRef::NewDirect(const SkImageInfo& info,
-                                              void* addr,
-                                              size_t rowBytes,
-                                              SkColorTable* ctable) {
-    if (!is_valid(info, ctable)) {
+sk_sp<SkPixelRef> SkMallocPixelRef::MakeDirect(const SkImageInfo& info,
+                                               void* addr,
+                                               size_t rowBytes,
+                                               sk_sp<SkColorTable> ctable) {
+    if (!is_valid(info, ctable.get())) {
         return nullptr;
     }
-    return new SkMallocPixelRef(info, addr, rowBytes, ctable, nullptr, nullptr);
+    return sk_sp<SkPixelRef>(new SkMallocPixelRef(info, addr, rowBytes, std::move(ctable),
+                                                  nullptr, nullptr));
 }
 
 
- SkMallocPixelRef* SkMallocPixelRef::NewUsing(void*(*alloc)(size_t),
-                                              const SkImageInfo& info,
-                                              size_t requestedRowBytes,
-                                              SkColorTable* ctable) {
-    if (!is_valid(info, ctable)) {
+ sk_sp<SkPixelRef> SkMallocPixelRef::MakeUsing(void*(*alloc)(size_t),
+                                               const SkImageInfo& info,
+                                               size_t requestedRowBytes,
+                                               sk_sp<SkColorTable> ctable) {
+    if (!is_valid(info, ctable.get())) {
         return nullptr;
     }
 
@@ -84,142 +85,85 @@
         return nullptr;
     }
 
-    return new SkMallocPixelRef(info, addr, rowBytes, ctable, sk_free_releaseproc, nullptr);
+     return sk_sp<SkPixelRef>(new SkMallocPixelRef(info, addr, rowBytes, std::move(ctable),
+                                                   sk_free_releaseproc, nullptr));
 }
 
-SkMallocPixelRef* SkMallocPixelRef::NewAllocate(const SkImageInfo& info,
+sk_sp<SkPixelRef> SkMallocPixelRef::MakeAllocate(const SkImageInfo& info,
                                                 size_t rowBytes,
-                                                SkColorTable* ctable) {
+                                                sk_sp<SkColorTable> ctable) {
     auto sk_malloc_nothrow = [](size_t size) { return sk_malloc_flags(size, 0); };
-    return NewUsing(sk_malloc_nothrow, info, rowBytes, ctable);
+    return MakeUsing(sk_malloc_nothrow, info, rowBytes, std::move(ctable));
 }
 
-SkMallocPixelRef* SkMallocPixelRef::NewZeroed(const SkImageInfo& info,
-                                              size_t rowBytes,
-                                              SkColorTable* ctable) {
-    return NewUsing(sk_calloc, info, rowBytes, ctable);
-}
-
-SkMallocPixelRef* SkMallocPixelRef::NewWithProc(const SkImageInfo& info,
-                                                size_t rowBytes,
-                                                SkColorTable* ctable,
-                                                void* addr,
-                                                SkMallocPixelRef::ReleaseProc proc,
-                                                void* context) {
-    if (!is_valid(info, ctable)) {
-        if (proc) {
-            proc(addr, context);
-        }
-        return nullptr;
-    }
-    return new SkMallocPixelRef(info, addr, rowBytes, ctable, proc, context);
+sk_sp<SkPixelRef> SkMallocPixelRef::MakeZeroed(const SkImageInfo& info,
+                                               size_t rowBytes,
+                                               sk_sp<SkColorTable> ctable) {
+    return MakeUsing(sk_calloc, info, rowBytes, std::move(ctable));
 }
 
 static void sk_data_releaseproc(void*, void* dataPtr) {
     (static_cast<SkData*>(dataPtr))->unref();
 }
 
-SkMallocPixelRef* SkMallocPixelRef::NewWithData(const SkImageInfo& info,
+sk_sp<SkPixelRef> SkMallocPixelRef::MakeWithProc(const SkImageInfo& info,
+                                                 size_t rowBytes,
+                                                 sk_sp<SkColorTable> ctable,
+                                                 void* addr,
+                                                 SkMallocPixelRef::ReleaseProc proc,
+                                                 void* context) {
+    if (!is_valid(info, ctable.get())) {
+        if (proc) {
+            proc(addr, context);
+        }
+        return nullptr;
+    }
+    return sk_sp<SkPixelRef>(new SkMallocPixelRef(info, addr, rowBytes, std::move(ctable),
+                                                  proc, context));
+}
+
+sk_sp<SkPixelRef> SkMallocPixelRef::MakeWithData(const SkImageInfo& info,
                                                 size_t rowBytes,
-                                                SkColorTable* ctable,
-                                                SkData* data) {
+                                                sk_sp<SkColorTable> ctable,
+                                                sk_sp<SkData> data) {
     SkASSERT(data != nullptr);
-    if (!is_valid(info, ctable)) {
+    if (!is_valid(info, ctable.get())) {
         return nullptr;
     }
-    if ((rowBytes < info.minRowBytes())
-        || (data->size() < info.getSafeSize(rowBytes))) {
+    if ((rowBytes < info.minRowBytes()) || (data->size() < info.getSafeSize(rowBytes))) {
         return nullptr;
     }
-    data->ref();
-    SkMallocPixelRef* pr =
-            new SkMallocPixelRef(info, const_cast<void*>(data->data()), rowBytes, ctable,
-                                 sk_data_releaseproc, static_cast<void*>(data));
-    SkASSERT(pr != nullptr);
-    // We rely on the immutability of the pixels to make the
-    // const_cast okay.
-    pr->setImmutable();
-    return pr;
+    // must get this address before we call release
+    void* pixels = const_cast<void*>(data->data());
+    SkPixelRef* pr = new SkMallocPixelRef(info, pixels, rowBytes, std::move(ctable),
+                                          sk_data_releaseproc, data.release());
+    pr->setImmutable(); // since we were created with (immutable) data
+    return sk_sp<SkPixelRef>(pr);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkMallocPixelRef::SkMallocPixelRef(const SkImageInfo& info, void* storage,
-                                   size_t rowBytes, SkColorTable* ctable,
-                                   bool ownsPixels)
-    : INHERITED(info)
-    , fReleaseProc(ownsPixels ? sk_free_releaseproc : nullptr)
-    , fReleaseProcContext(nullptr) {
-    // This constructor is now DEPRICATED.
-    SkASSERT(is_valid(info, ctable));
-    SkASSERT(rowBytes >= info.minRowBytes());
-
-    if (kIndex_8_SkColorType != info.colorType()) {
-        ctable = nullptr;
+static sk_sp<SkColorTable> sanitize(const SkImageInfo& info, sk_sp<SkColorTable> ctable) {
+    if (kIndex_8_SkColorType == info.colorType()) {
+        SkASSERT(ctable);
+    } else {
+        ctable.reset(nullptr);
     }
-
-    fStorage = storage;
-    fCTable = ctable;
-    fRB = rowBytes;
-    SkSafeRef(ctable);
-
-    this->setPreLocked(fStorage, rowBytes, fCTable);
+    return ctable;
 }
 
 SkMallocPixelRef::SkMallocPixelRef(const SkImageInfo& info, void* storage,
-                                   size_t rowBytes, SkColorTable* ctable,
+                                   size_t rowBytes, sk_sp<SkColorTable> ctable,
                                    SkMallocPixelRef::ReleaseProc proc,
                                    void* context)
-    : INHERITED(info)
+    : INHERITED(info.width(), info.height(), storage, rowBytes, sanitize(info, std::move(ctable)))
     , fReleaseProc(proc)
     , fReleaseProcContext(context)
-{
-    SkASSERT(is_valid(info, ctable));
-    SkASSERT(rowBytes >= info.minRowBytes());
-
-    if (kIndex_8_SkColorType != info.colorType()) {
-        ctable = nullptr;
-    }
-
-    fStorage = storage;
-    fCTable = ctable;
-    fRB = rowBytes;
-    SkSafeRef(ctable);
-
-    this->setPreLocked(fStorage, rowBytes, fCTable);
-}
+{}
 
 
 SkMallocPixelRef::~SkMallocPixelRef() {
-    SkSafeUnref(fCTable);
     if (fReleaseProc != nullptr) {
-        fReleaseProc(fStorage, fReleaseProcContext);
+        fReleaseProc(this->pixels(), fReleaseProcContext);
     }
 }
-
-bool SkMallocPixelRef::onNewLockPixels(LockRec* rec) {
-    rec->fPixels = fStorage;
-    rec->fRowBytes = fRB;
-    rec->fColorTable = fCTable;
-    return true;
-}
-
-void SkMallocPixelRef::onUnlockPixels() {
-    // nothing to do
-}
-
-size_t SkMallocPixelRef::getAllocatedSizeInBytes() const {
-    return this->info().getSafeSize(fRB);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkPixelRef* SkMallocPixelRef::PRFactory::create(const SkImageInfo& info, size_t rowBytes,
-                                                SkColorTable* ctable) {
-    return SkMallocPixelRef::NewAllocate(info, rowBytes, ctable);
-}
-
-SkPixelRef* SkMallocPixelRef::ZeroedPRFactory::create(const SkImageInfo& info, size_t rowBytes,
-                                                      SkColorTable* ctable) {
-    return SkMallocPixelRef::NewZeroed(info, rowBytes, ctable);
-}
diff --git a/src/core/SkMaskCache.cpp b/src/core/SkMaskCache.cpp
index ccfae18..f998a92 100644
--- a/src/core/SkMaskCache.cpp
+++ b/src/core/SkMaskCache.cpp
@@ -110,15 +110,15 @@
         SkASSERT(1 == count || 2 == count);
         SkIRect ir;
         rects[0].roundOut(&ir);
-        fSizes[0] = SkSize::Make(rects[0].width(), rects[0].height());
+        fSizes[0] = SkSize{rects[0].width(), rects[0].height()};
         if (2 == count) {
-            fSizes[1] = SkSize::Make(rects[1].width(), rects[1].height());
-            fSizes[2] = SkSize::Make(rects[0].x() - rects[1].x(), rects[0].y() - rects[1].y());
+            fSizes[1] = SkSize{rects[1].width(), rects[1].height()};
+            fSizes[2] = SkSize{rects[0].x() - rects[1].x(), rects[0].y() - rects[1].y()};
         } else {
-            fSizes[1] = SkSize::Make(0, 0);
-            fSizes[2] = SkSize::Make(0, 0);
+            fSizes[1] = SkSize{0, 0};
+            fSizes[2] = SkSize{0, 0};
         }
-        fSizes[3] = SkSize::Make(rects[0].x() - ir.x(), rects[0].y() - ir.y());
+        fSizes[3] = SkSize{rects[0].x() - ir.x(), rects[0].y() - ir.y()};
 
         this->init(&gRectsBlurKeyNamespaceLabel, 0,
                    sizeof(fSigma) + sizeof(fStyle) + sizeof(fQuality) + sizeof(fSizes));
diff --git a/src/core/SkMathPriv.h b/src/core/SkMathPriv.h
index 5ef0cb2..ddb2315 100644
--- a/src/core/SkMathPriv.h
+++ b/src/core/SkMathPriv.h
@@ -200,8 +200,4 @@
     return n + 1;
 }
 
-static inline int GrNextPow2(int n) {
-    SkASSERT(n >= 0); // this impl only works for non-neg.
-    return n ? (1 << (32 - SkCLZ(n - 1))) : 1;
-}
 #endif
diff --git a/src/core/SkMatrixImageFilter.cpp b/src/core/SkMatrixImageFilter.cpp
index 0a33280..4d9ea21 100644
--- a/src/core/SkMatrixImageFilter.cpp
+++ b/src/core/SkMatrixImageFilter.cpp
@@ -95,6 +95,16 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkMatrixImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkMatrixImageFilter*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkMatrixImageFilter::Make(fTransform, fFilterQuality, std::move(input));
+}
+
 SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const {
     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
     SkRect dst;
diff --git a/src/core/SkMatrixImageFilter.h b/src/core/SkMatrixImageFilter.h
index f688087..3b451a4 100644
--- a/src/core/SkMatrixImageFilter.h
+++ b/src/core/SkMatrixImageFilter.h
@@ -42,6 +42,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
 
 private:
diff --git a/src/core/SkMiniRecorder.cpp b/src/core/SkMiniRecorder.cpp
index 51ff5ac..b5d21a9 100644
--- a/src/core/SkMiniRecorder.cpp
+++ b/src/core/SkMiniRecorder.cpp
@@ -27,10 +27,27 @@
     bool   willPlayBackBitmaps()  const override { return false; }
 };
 
+// Calculate conservative bounds for each type of draw op that can be its own mini picture.
+// These are fairly easy because we know they can't be affected by any matrix or saveLayers.
+static SkRect adjust_for_paint(SkRect bounds, const SkPaint& paint) {
+    return paint.canComputeFastBounds() ? paint.computeFastBounds(bounds, &bounds)
+                                        : SkRect::MakeLargest();
+}
+static SkRect bounds(const DrawRect& op) {
+    return adjust_for_paint(op.rect, op.paint);
+}
+static SkRect bounds(const DrawPath& op) {
+    return op.path.isInverseFillType() ? SkRect::MakeLargest()
+                                       : adjust_for_paint(op.path.getBounds(), op.paint);
+}
+static SkRect bounds(const DrawTextBlob& op) {
+    return adjust_for_paint(op.blob->bounds().makeOffset(op.x, op.y), op.paint);
+}
+
 template <typename T>
 class SkMiniPicture final : public SkPicture {
 public:
-    SkMiniPicture(SkRect cull, T* op) : fCull(cull) {
+    SkMiniPicture(const SkRect* cull, T* op) : fCull(cull ? *cull : bounds(*op)) {
         memcpy(&fOp, op, sizeof(fOp));  // We take ownership of op's guts.
     }
 
@@ -59,7 +76,7 @@
     if (fState != State::kEmpty) {
         // We have internal state pending.
         // Detaching then deleting a picture is an easy way to clean up.
-        (void)this->detachAsPicture(SkRect::MakeEmpty());
+        (void)this->detachAsPicture(nullptr);
     }
     SkASSERT(fState == State::kEmpty);
 }
@@ -84,7 +101,7 @@
 #undef TRY_TO_STORE
 
 
-sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect& cull) {
+sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect* cull) {
 #define CASE(Type)              \
     case State::k##Type:        \
         fState = State::kEmpty; \
diff --git a/src/core/SkMiniRecorder.h b/src/core/SkMiniRecorder.h
new file mode 100644
index 0000000..fd1e8f6
--- /dev/null
+++ b/src/core/SkMiniRecorder.h
@@ -0,0 +1,57 @@
+/*
+ * 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 SkMiniRecorder_DEFINED
+#define SkMiniRecorder_DEFINED
+
+#include "SkRecords.h"
+#include "SkScalar.h"
+#include "SkTypes.h"
+class SkCanvas;
+
+// Records small pictures, but only a limited subset of the canvas API, and may fail.
+class SkMiniRecorder : SkNoncopyable {
+public:
+    SkMiniRecorder();
+    ~SkMiniRecorder();
+
+    // Try to record an op.  Returns false on failure.
+    bool drawPath(const SkPath&, const SkPaint&);
+    bool drawRect(const SkRect&, const SkPaint&);
+    bool drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint&);
+
+    // Detach anything we've recorded as a picture, resetting this SkMiniRecorder.
+    // If cull is nullptr we'll calculate it.
+    sk_sp<SkPicture> detachAsPicture(const SkRect* cull);
+
+    // Flush anything we've recorded to the canvas, resetting this SkMiniRecorder.
+    // This is logically the same as but rather more efficient than:
+    //    sk_sp<SkPicture> pic(this->detachAsPicture(nullptr));
+    //    pic->playback(canvas);
+    void flushAndReset(SkCanvas*);
+
+private:
+    enum class State {
+        kEmpty,
+        kDrawPath,
+        kDrawRect,
+        kDrawTextBlob,
+    };
+
+    State fState;
+
+    template <size_t A, size_t B>
+    struct Max { static const size_t val = A > B ? A : B; };
+
+    static const size_t kInlineStorage =
+        Max<sizeof(SkRecords::DrawPath),
+        Max<sizeof(SkRecords::DrawRect),
+            sizeof(SkRecords::DrawTextBlob)>::val>::val;
+    SkAlignedSStorage<kInlineStorage> fBuffer;
+};
+
+#endif//SkMiniRecorder_DEFINED
diff --git a/src/core/SkMipMap.cpp b/src/core/SkMipMap.cpp
index 03341c6..b3766e5 100644
--- a/src/core/SkMipMap.cpp
+++ b/src/core/SkMipMap.cpp
@@ -242,18 +242,30 @@
     auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
     auto d = static_cast<typename F::Type*>(dst);
 
-    auto c02 = F::Expand(p0[0]);
-    auto c12 = F::Expand(p1[0]);
-    for (int i = 0; i < count; ++i) {
-        auto c00 = c02;
-        auto c01 = F::Expand(p0[1]);
-             c02 = F::Expand(p0[2]);
-        auto c10 = c12;
-        auto c11 = F::Expand(p1[1]);
-             c12 = F::Expand(p1[2]);
+    // Given pixels:
+    // a0 b0 c0 d0 e0 ...
+    // a1 b1 c1 d1 e1 ...
+    // We want:
+    // (a0 + 2*b0 + c0 + a1 + 2*b1 + c1) / 8
+    // (c0 + 2*d0 + e0 + c1 + 2*d1 + e1) / 8
+    // ...
 
-        auto c = add_121(c00, c01, c02) + add_121(c10, c11, c12);
-        d[i] = F::Compact(shift_right(c, 3));
+    auto c0 = F::Expand(p0[0]);
+    auto c1 = F::Expand(p1[0]);
+    auto c = c0 + c1;
+    for (int i = 0; i < count; ++i) {
+        auto a = c;
+
+        auto b0 = F::Expand(p0[1]);
+        auto b1 = F::Expand(p1[1]);
+        auto b = b0 + b0 + b1 + b1;
+
+        c0 = F::Expand(p0[2]);
+        c1 = F::Expand(p1[2]);
+        c = c0 + c1;
+
+        auto sum = a + b + c;
+        d[i] = F::Compact(shift_right(sum, 3));
         p0 += 2;
         p1 += 2;
     }
@@ -324,7 +336,7 @@
                          sk_linear12_from_srgb[p0[ 8]],
                          sk_linear12_from_srgb[p0[ 9]],
                          sk_linear12_from_srgb[p0[10]],
-                         p0[11] << 4               );
+                         p0[11] << 4                 );
         Sk8h b0d0 = Sk8h(sk_linear12_from_srgb[p0[ 4]],
                          sk_linear12_from_srgb[p0[ 5]],
                          sk_linear12_from_srgb[p0[ 6]],
@@ -371,6 +383,92 @@
     }
 }
 
+void downsample_2_3_srgb(void* dst, const void* src, size_t srcRB, int count) {
+    const uint8_t* p0 = ((const uint8_t*) src);
+    const uint8_t* p1 = p0 + srcRB;
+    const uint8_t* p2 = p1 + srcRB;
+    uint8_t* d = (uint8_t*) dst;
+
+    // Given pixels:
+    // a0 b0 c0 d0 ...
+    // a1 b1 c1 d1 ...
+    // a2 b2 c2 d2 ...
+    // We want:
+    // (a0 + b0 + 2*a1 + 2*b1 + a2 + b2) / 8
+    // (c0 + d0 + 2*c1 + 2*d1 + c2 + d2) / 8
+    // ...
+    while (count >= 2) {
+        Sk8h a0c0 = Sk8h(sk_linear12_from_srgb[p0[ 0]],
+                         sk_linear12_from_srgb[p0[ 1]],
+                         sk_linear12_from_srgb[p0[ 2]],
+                         p0[ 3] << 4                  ,
+                         sk_linear12_from_srgb[p0[ 8]],
+                         sk_linear12_from_srgb[p0[ 9]],
+                         sk_linear12_from_srgb[p0[10]],
+                         p0[11] << 4                 );
+        Sk8h b0d0 = Sk8h(sk_linear12_from_srgb[p0[ 4]],
+                         sk_linear12_from_srgb[p0[ 5]],
+                         sk_linear12_from_srgb[p0[ 6]],
+                         p0[ 7] << 4                  ,
+                         sk_linear12_from_srgb[p0[12]],
+                         sk_linear12_from_srgb[p0[13]],
+                         sk_linear12_from_srgb[p0[14]],
+                         p0[15] << 4                 );
+        Sk8h a1c1 = Sk8h(sk_linear12_from_srgb[p1[ 0]],
+                         sk_linear12_from_srgb[p1[ 1]],
+                         sk_linear12_from_srgb[p1[ 2]],
+                         p1[ 3] << 4                  ,
+                         sk_linear12_from_srgb[p1[ 8]],
+                         sk_linear12_from_srgb[p1[ 9]],
+                         sk_linear12_from_srgb[p1[10]],
+                         p1[11] << 4                 );
+        Sk8h b1d1 = Sk8h(sk_linear12_from_srgb[p1[ 4]],
+                         sk_linear12_from_srgb[p1[ 5]],
+                         sk_linear12_from_srgb[p1[ 6]],
+                         p1[ 7] << 4                  ,
+                         sk_linear12_from_srgb[p1[12]],
+                         sk_linear12_from_srgb[p1[13]],
+                         sk_linear12_from_srgb[p1[14]],
+                         p1[15] << 4                 );
+        Sk8h a2c2 = Sk8h(sk_linear12_from_srgb[p2[ 0]],
+                         sk_linear12_from_srgb[p2[ 1]],
+                         sk_linear12_from_srgb[p2[ 2]],
+                         p2[ 3] << 4                  ,
+                         sk_linear12_from_srgb[p2[ 8]],
+                         sk_linear12_from_srgb[p2[ 9]],
+                         sk_linear12_from_srgb[p2[10]],
+                         p2[11] << 4                 );
+        Sk8h b2d2 = Sk8h(sk_linear12_from_srgb[p2[ 4]],
+                         sk_linear12_from_srgb[p2[ 5]],
+                         sk_linear12_from_srgb[p2[ 6]],
+                         p2[ 7] << 4                  ,
+                         sk_linear12_from_srgb[p2[12]],
+                         sk_linear12_from_srgb[p2[13]],
+                         sk_linear12_from_srgb[p2[14]],
+                         p2[15] << 4                 );
+
+        Sk8h avg = (a0c0 + b0d0 + a1c1 + a1c1 + b1d1 + b1d1 + a2c2 + b2d2) >> 3;
+        d[0] = sk_linear12_to_srgb[avg[0]];
+        d[1] = sk_linear12_to_srgb[avg[1]];
+        d[2] = sk_linear12_to_srgb[avg[2]];
+        d[3] = avg[3] >> 4;
+        d[4] = sk_linear12_to_srgb[avg[4]];
+        d[5] = sk_linear12_to_srgb[avg[5]];
+        d[6] = sk_linear12_to_srgb[avg[6]];
+        d[7] = avg[7] >> 4;
+
+        p0 += 16;
+        p1 += 16;
+        p2 += 16;
+        d += 8;
+        count -= 2;
+    }
+
+    if (count) {
+        downsample_2_3<ColorTypeFilter_S32>(d, p0, srcRB, count);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) {
@@ -410,7 +508,7 @@
                 proc_1_3 = downsample_1_3<ColorTypeFilter_S32>;
                 proc_2_1 = downsample_2_1<ColorTypeFilter_S32>;
                 proc_2_2 = downsample_2_2_srgb;
-                proc_2_3 = downsample_2_3<ColorTypeFilter_S32>;
+                proc_2_3 = downsample_2_3_srgb;
                 proc_3_1 = downsample_3_1<ColorTypeFilter_S32>;
                 proc_3_2 = downsample_3_2<ColorTypeFilter_S32>;
                 proc_3_3 = downsample_3_3<ColorTypeFilter_S32>;
@@ -681,15 +779,10 @@
 //
 SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDestinationSurfaceColorMode colorMode,
                           SkDiscardableFactoryProc fact) {
-    SkAutoPixmapUnlock srcUnlocker;
-    if (!src.requestLock(&srcUnlocker)) {
+    SkPixmap srcPixmap;
+    if (!src.peekPixels(&srcPixmap)) {
         return nullptr;
     }
-    const SkPixmap& srcPixmap = srcUnlocker.pixmap();
-    // Try to catch where we might have returned nullptr for src crbug.com/492818
-    if (nullptr == srcPixmap.addr()) {
-        sk_throw();
-    }
     return Build(srcPixmap, colorMode, fact);
 }
 
diff --git a/src/core/SkMipMap.h b/src/core/SkMipMap.h
index f3425cb..4ca9cbd 100644
--- a/src/core/SkMipMap.h
+++ b/src/core/SkMipMap.h
@@ -12,7 +12,7 @@
 #include "SkPixmap.h"
 #include "SkScalar.h"
 #include "SkSize.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 
 class SkBitmap;
 class SkDiscardableMemory;
@@ -33,8 +33,8 @@
     static SkMipMap* Build(const SkBitmap& src, SkDestinationSurfaceColorMode,
                            SkDiscardableFactoryProc);
 
-    static SkDestinationSurfaceColorMode DeduceColorMode(const SkShader::ContextRec& rec) {
-        return (SkShader::ContextRec::kPMColor_DstType == rec.fPreferredDstType)
+    static SkDestinationSurfaceColorMode DeduceColorMode(const SkShaderBase::ContextRec& rec) {
+        return (SkShaderBase::ContextRec::kPMColor_DstType == rec.fPreferredDstType)
             ? SkDestinationSurfaceColorMode::kLegacy
             : SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
     }
diff --git a/src/core/SkModeColorFilter.cpp b/src/core/SkModeColorFilter.cpp
index d074482..f30b6a5 100644
--- a/src/core/SkModeColorFilter.cpp
+++ b/src/core/SkModeColorFilter.cpp
@@ -5,21 +5,22 @@
  * found in the LICENSE file.
  */
 
-#include "SkBlitRow.h"
+#include "SkArenaAlloc.h"
 #include "SkBlendModePriv.h"
+#include "SkBlitRow.h"
 #include "SkColorFilter.h"
 #include "SkColorPriv.h"
-#include "SkArenaAlloc.h"
+#include "SkColorSpaceXformer.h"
 #include "SkModeColorFilter.h"
+#include "SkPM4f.h"
 #include "SkPM4fPriv.h"
+#include "SkRandom.h"
 #include "SkRasterPipeline.h"
 #include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkUtils.h"
-#include "SkRandom.h"
 #include "SkString.h"
+#include "SkUtils.h"
 #include "SkValidationUtils.h"
-#include "SkPM4f.h"
+#include "SkWriteBuffer.h"
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -63,14 +64,6 @@
     }
 }
 
-void SkModeColorFilter::filterSpan4f(const SkPM4f shader[], int count, SkPM4f result[]) const {
-    SkXfermodeProc4f  proc = SkXfermode::GetProc4f(fMode);
-    auto pm4f = SkColor4f::FromColor(fColor).premul();
-    for (int i = 0; i < count; i++) {
-        result[i] = proc(pm4f, shader[i]);
-    }
-}
-
 void SkModeColorFilter::flatten(SkWriteBuffer& buffer) const {
     buffer.writeColor(fColor);
     buffer.writeUInt((int)fMode);
@@ -79,6 +72,7 @@
 void SkModeColorFilter::updateCache() {
     fPMColor = SkPreMultiplyColor(fColor);
     fProc = SkXfermode::GetProc(fMode);
+    fPMColor4f = SkColor4f::FromColor(fColor).premul();
 }
 
 sk_sp<SkFlattenable> SkModeColorFilter::CreateProc(SkReadBuffer& buffer) {
@@ -87,7 +81,7 @@
     return SkColorFilter::MakeModeFilter(color, mode);
 }
 
-bool SkModeColorFilter::onAppendStages(SkRasterPipeline* p,
+void SkModeColorFilter::onAppendStages(SkRasterPipeline* p,
                                        SkColorSpace* dst,
                                        SkArenaAlloc* scratch,
                                        bool shaderIsOpaque) const {
@@ -96,11 +90,14 @@
     p->append(SkRasterPipeline::move_src_dst);
     p->append(SkRasterPipeline::constant_color, color);
     auto mode = (SkBlendMode)fMode;
-    if (!SkBlendMode_AppendStages(mode, p)) {
-        return false;
+    SkBlendMode_AppendStages(mode, p);
+    if (SkBlendMode_CanOverflow(mode)) {
+        p->append(SkRasterPipeline::clamp_a);
     }
-    if (SkBlendMode_CanOverflow(mode)) { p->append(SkRasterPipeline::clamp_a); }
-    return true;
+}
+
+sk_sp<SkColorFilter> SkModeColorFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return SkColorFilter::MakeModeFilter(xformer->apply(fColor), fMode);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkModeColorFilter.h b/src/core/SkModeColorFilter.h
index 4d0b172..66dda38 100644
--- a/src/core/SkModeColorFilter.h
+++ b/src/core/SkModeColorFilter.h
@@ -24,7 +24,6 @@
     bool asColorMode(SkColor*, SkBlendMode*) const override;
     uint32_t getFlags() const override;
     void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override;
-    void filterSpan4f(const SkPM4f shader[], int count, SkPM4f result[]) const override;
 
 #ifndef SK_IGNORE_TO_STRING
     void toString(SkString* str) const override;
@@ -44,9 +43,14 @@
 
     void flatten(SkWriteBuffer&) const override;
 
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+    void onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         bool shaderIsOpaque) const override;
 
+    sk_sp<SkColorFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
+
+    // cache
+    SkPM4f              fPMColor4f;
+
 private:
     SkColor             fColor;
     SkBlendMode         fMode;
diff --git a/src/core/SkNormalBevelSource.cpp b/src/core/SkNormalBevelSource.cpp
deleted file mode 100644
index 39e25e0..0000000
--- a/src/core/SkNormalBevelSource.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * 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 "SkNormalBevelSource.h"
-
-#include "SkArenaAlloc.h"
-#include "SkNormalSource.h"
-#include "SkNormalSourcePriv.h"
-#include "SkPoint3.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-
-#if SK_SUPPORT_GPU
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "SkGr.h"
-
-/** \class NormalBevelFP
- *
- *  Fragment processor for the SkNormalBevelSource.
- *
- *  @param bevelType    type of the bevel
- *  @param bevelWidth   width of the bevel in device space
- *  @param bevelHeight  height of the bevel in device space
- */
-class NormalBevelFP : public GrFragmentProcessor {
-public:
-    NormalBevelFP(SkNormalSource::BevelType bevelType, SkScalar bevelWidth, SkScalar bevelHeight)
-            : INHERITED(kNone_OptimizationFlags)
-            , fBevelType(bevelType)
-            , fBevelWidth(bevelWidth)
-            , fBevelHeight(bevelHeight) {
-        this->initClassID<NormalBevelFP>();
-
-        this->setWillUseDistanceVectorField();
-    }
-
-    class GLSLNormalBevelFP : public GLSLNormalFP {
-    public:
-        GLSLNormalBevelFP() {
-            fPrevWidth = SkFloatToScalar(0.0f);
-            fPrevHeight = SkFloatToScalar(0.0f);
-        }
-
-        void onEmitCode(EmitArgs& args) override {
-            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-            const NormalBevelFP& fp = args.fFp.cast<NormalBevelFP>();
-            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-
-            // Determining necessary uniforms and initializing them
-            bool needWidth = true;
-            bool needHeight = (fp.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
-                               fp.fBevelType == SkNormalSource::BevelType::kRoundedIn);
-            bool needNormalized = (fp.fBevelType == SkNormalSource::BevelType::kLinear);
-
-            const char *widthUniName = nullptr;
-            if (needWidth) {
-                fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
-                                                       kDefault_GrSLPrecision, "Width",
-                                                       &widthUniName);
-            }
-
-            const char* heightUniName = nullptr;
-            if (needHeight) {
-                fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
-                                                        kDefault_GrSLPrecision, "Height",
-                                                        &heightUniName);
-            }
-
-            const char* normalizedWidthUniName = nullptr;
-            const char* normalizedHeightUniName = nullptr;
-            if (needNormalized) {
-                fNormalizedWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                                 kFloat_GrSLType,
-                                                                 kDefault_GrSLPrecision,
-                                                                 "NormalizedWidth",
-                                                                 &normalizedWidthUniName);
-                fNormalizedHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                                  kFloat_GrSLType,
-                                                                  kDefault_GrSLPrecision,
-                                                                  "NormalizedHeight",
-                                                                  &normalizedHeightUniName);
-            }
-
-            // Here we are splitting the distance vector into length and normalized direction
-            fragBuilder->codeAppendf("float dv_length = %s.z;",
-                                     fragBuilder->distanceVectorName());
-            fragBuilder->codeAppendf("vec2 dv_norm = %s.xy;",
-                                     fragBuilder->distanceVectorName());
-
-            // Asserting presence of necessary uniforms
-            SkASSERT(widthUniName);
-
-            fragBuilder->codeAppend( "vec3 normal;");
-            fragBuilder->codeAppendf("if (dv_length >= %s) {", widthUniName);
-            fragBuilder->codeAppend( "    normal = vec3(0.0, 0.0, 1.0);");
-            fragBuilder->codeAppend( "} else {");
-            this->emitMath(fragBuilder, fp.fBevelType, widthUniName, heightUniName,
-                           normalizedWidthUniName, normalizedHeightUniName);
-            fragBuilder->codeAppend( "}");
-            fragBuilder->codeAppendf("%s = vec4(normal, 0.0);", args.fOutputColor);
-        }
-
-        static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-            const NormalBevelFP& fp = proc.cast<NormalBevelFP>();
-            b->add32(static_cast<int>(fp.fBevelType));
-        }
-
-    protected:
-        void setNormalData(const GrGLSLProgramDataManager& pdman,
-                           const GrProcessor& proc) override {
-            const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
-
-            // Updating uniform if bevel type requires it and data has changed
-
-            bool needWidth = true;
-            bool needHeight = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedOut ||
-                               normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedIn);
-            bool needNormalized = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kLinear);
-
-            bool dirtyWidth = (fPrevWidth  != normalBevelFP.fBevelWidth);
-            bool dirtyHeight = (fPrevHeight != normalBevelFP.fBevelHeight);
-            bool dirtyNormalized = (dirtyHeight || dirtyWidth);
-
-
-            if (needWidth && dirtyWidth) {
-                pdman.set1f(fWidthUni, normalBevelFP.fBevelWidth);
-                fPrevWidth = normalBevelFP.fBevelWidth;
-            }
-            if (needHeight && dirtyHeight) {
-                pdman.set1f(fHeightUni, normalBevelFP.fBevelHeight);
-                fPrevHeight = normalBevelFP.fBevelHeight;
-            }
-            if (needNormalized && dirtyNormalized) {
-                SkScalar height = normalBevelFP.fBevelHeight;
-                SkScalar width  = normalBevelFP.fBevelWidth;
-
-                SkScalar length = SkScalarSqrt(SkScalarSquare(height) + SkScalarSquare(width));
-                pdman.set1f(fNormalizedHeightUni, height/length);
-                pdman.set1f(fNormalizedWidthUni, width/length);
-            }
-        }
-
-        // This method emits the code that calculates the normal orthgonal to the simulated beveled
-        // surface. In the comments inside the function, the math involved is described. For this
-        // purpose, the d-axis is defined to be the axis co-linear to the distance vector, where the
-        // origin is the end of the bevel inside the shape.
-        void emitMath(GrGLSLFPFragmentBuilder* fb, SkNormalSource::BevelType type,
-                      const char* width, const char* height, const char* normalizedWidth,
-                      const char* normalizedHeight) {
-            switch (type) {
-                case SkNormalSource::BevelType::kLinear:
-                    // Asserting presence of necessary uniforms
-                    SkASSERT(normalizedHeight);
-                    SkASSERT(normalizedWidth);
-
-                    // Because the slope of the bevel is -height/width, the vector
-                    // normalized(vec2(height, width)) is the d- and z-components of the normal
-                    // vector that is orthogonal to the linear bevel. Multiplying the d-component
-                    // to the normalized distance vector splits it into x- and y-components.
-                    fb->codeAppendf("normal = vec3(%s * dv_norm, %s);",
-                                    normalizedHeight, normalizedWidth);
-                    break;
-                case SkNormalSource::BevelType::kRoundedOut:
-                    // Fall through
-                case SkNormalSource::BevelType::kRoundedIn:
-                    // Asserting presence of necessary uniforms
-                    SkASSERT(height);
-                    SkASSERT(width);
-
-                    // Setting the current position in the d-axis to the distance from the end of
-                    // the bevel as opposed to the beginning if the bevel is rounded in, essentially
-                    // flipping the bevel calculations.
-                    if ( type == SkNormalSource::BevelType::kRoundedIn ) {
-                        fb->codeAppendf("float currentPos_d = %s - dv_length;", width);
-                    } else if (type == SkNormalSource::BevelType::kRoundedOut) {
-                        fb->codeAppendf("float currentPos_d = dv_length;");
-                    }
-
-                    fb->codeAppendf("float rootDOverW = sqrt(currentPos_d/%s);", width);
-
-                    // Calculating the d- and z-components of the normal, where 'd' is the axis
-                    // co-linear to the distance vector. Equation was derived from the formula for
-                    // a bezier curve by solving the parametric equation for d(t) and z(t), then
-                    // with those, calculate d'(t), z'(t) and t(d), and from these, d'(d) and z'(d).
-                    // z'(d)/d'(d) results in the slope of the bevel at d, so we construct an
-                    // orthogonal vector of slope -d'(d)/z'(d) and length 1.
-                    fb->codeAppendf("vec2 unnormalizedNormal_dz = vec2(%s*(1.0-rootDOverW), "
-                                                                       "%s*rootDOverW);",
-                                    height, width);
-                    fb->codeAppendf("vec2 normal_dz = normalize(unnormalizedNormal_dz);");
-
-                    // Multiplying the d-component to the normalized distance vector splits it into
-                    // x- and y-components.
-                    fb->codeAppendf("normal = vec3(normal_dz.x*dv_norm, normal_dz.y);");
-
-                    break;
-                default:
-                    SkDEBUGFAIL("Invalid bevel type passed to emitMath");
-            }
-        }
-
-    private:
-        SkScalar fPrevWidth;
-        GrGLSLProgramDataManager::UniformHandle fWidthUni;
-
-        SkScalar fPrevHeight;
-        GrGLSLProgramDataManager::UniformHandle fHeightUni;
-
-        // width / length(<width,height>)
-        GrGLSLProgramDataManager::UniformHandle fNormalizedWidthUni;
-        // height / length(<width,height>)
-        GrGLSLProgramDataManager::UniformHandle fNormalizedHeightUni;
-    };
-
-    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLNormalBevelFP::GenKey(*this, caps, b);
-    }
-
-    const char* name() const override { return "NormalBevelFP"; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalBevelFP; }
-
-    bool onIsEqual(const GrFragmentProcessor& proc) const override {
-        const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
-        return fBevelType   == normalBevelFP.fBevelType &&
-               fBevelWidth  == normalBevelFP.fBevelWidth &&
-               fBevelHeight == normalBevelFP.fBevelHeight;
-    }
-
-    SkNormalSource::BevelType fBevelType;
-    SkScalar fBevelWidth;
-    SkScalar fBevelHeight;
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-sk_sp<GrFragmentProcessor> SkNormalBevelSourceImpl::asFragmentProcessor(
-        const SkShader::AsFPArgs& args) const {
-
-    // This assumes a uniform scale. Anisotropic scaling might not be handled gracefully.
-    SkScalar maxScale = args.fViewMatrix->getMaxScale();
-
-    // Providing device-space width and height
-    return sk_make_sp<NormalBevelFP>(fType, maxScale * fWidth, maxScale * fHeight);
-}
-
-#endif // SK_SUPPORT_GPU
-
-////////////////////////////////////////////////////////////////////////////
-
-SkNormalBevelSourceImpl::Provider::Provider() {}
-
-SkNormalBevelSourceImpl::Provider::~Provider() {}
-
-SkNormalSource::Provider* SkNormalBevelSourceImpl::asProvider(const SkShader::ContextRec &rec,
-                                                              SkArenaAlloc* alloc) const {
-    return alloc->make<Provider>();
-}
-
-// TODO Implement feature for the CPU pipeline
-void SkNormalBevelSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[],
-                                                     int count) const {
-    for (int i = 0; i < count; i++) {
-        output[i] = {0.0f, 0.0f, 1.0f};
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkFlattenable> SkNormalBevelSourceImpl::CreateProc(SkReadBuffer& buf) {
-
-    auto type = static_cast<SkNormalSource::BevelType>(buf.readInt());
-    SkScalar width = buf.readScalar();
-    SkScalar height = buf.readScalar();
-
-    return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height);
-}
-
-void SkNormalBevelSourceImpl::flatten(SkWriteBuffer& buf) const {
-    this->INHERITED::flatten(buf);
-
-    buf.writeInt(static_cast<int>(fType));
-    buf.writeScalar(fWidth);
-    buf.writeScalar(fHeight);
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkNormalSource> SkNormalSource::MakeBevel(BevelType type, SkScalar width, SkScalar height) {
-    /* TODO make sure these checks are tolerant enough to account for loss of conversion when GPUs
-       use 16-bit float types. We don't want to assume stuff is non-zero on the GPU and be wrong.*/
-    SkASSERT(width > 0.0f && !SkScalarNearlyZero(width));
-    if (SkScalarNearlyZero(height)) {
-        return SkNormalSource::MakeFlat();
-    }
-
-    return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height);
-}
diff --git a/src/core/SkNormalBevelSource.h b/src/core/SkNormalBevelSource.h
deleted file mode 100644
index 2fefacd..0000000
--- a/src/core/SkNormalBevelSource.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkNormalBevelSource_DEFINED
-#define SkNormalBevelSource_DEFINED
-
-#include "SkNormalSource.h"
-
-class SK_API SkNormalBevelSourceImpl : public SkNormalSource {
-public:
-    SkNormalBevelSourceImpl(BevelType type, SkScalar width, SkScalar height)
-        : fType(type)
-        , fWidth(width)
-        , fHeight(height) {}
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override;
-#endif
-
-    SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec,
-                                         SkArenaAlloc*) const override;
-
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalBevelSourceImpl)
-
-protected:
-    void flatten(SkWriteBuffer& buf) const override;
-
-private:
-    class Provider : public SkNormalSource::Provider {
-    public:
-        Provider();
-
-        ~Provider() override;
-
-        void fillScanLine(int x, int y, SkPoint3 output[], int count) const override;
-
-    private:
-        typedef SkNormalSource::Provider INHERITED;
-
-    };
-
-    SkNormalSource::BevelType fType;
-    SkScalar fWidth;
-    SkScalar fHeight;
-
-    friend class SkNormalSource;
-
-    typedef SkNormalSource INHERITED;
-};
-
-
-#endif
diff --git a/src/core/SkNormalFlatSource.cpp b/src/core/SkNormalFlatSource.cpp
index c08813d..c7cde03 100644
--- a/src/core/SkNormalFlatSource.cpp
+++ b/src/core/SkNormalFlatSource.cpp
@@ -9,7 +9,6 @@
 
 #include "SkArenaAlloc.h"
 #include "SkNormalSource.h"
-#include "SkNormalSourcePriv.h"
 #include "SkPoint3.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
@@ -24,31 +23,24 @@
         this->initClassID<NormalFlatFP>();
     }
 
-    class GLSLNormalFlatFP : public GLSLNormalFP {
+    class GLSLNormalFlatFP : public GrGLSLFragmentProcessor {
     public:
         GLSLNormalFlatFP() {}
 
-        void onEmitCode(EmitArgs& args) override {
+        void emitCode(EmitArgs& args) override {
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
             fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
         }
 
-        static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-            b->add32(0x0);
-        }
-
-    protected:
-        void setNormalData(const GrGLSLProgramDataManager& pdman,
-                           const GrProcessor& proc) override {}
+    private:
+        void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override {}
     };
 
     const char* name() const override { return "NormalFlatFP"; }
 
 private:
-    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLNormalFlatFP::GenKey(*this, caps, b);
-    }
+    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {}
 
     GrColor4f constantOutputForConstantInput(GrColor4f) const override {
         return GrColor4f(0, 0, 1, 0);
@@ -61,7 +53,7 @@
 };
 
 sk_sp<GrFragmentProcessor> SkNormalFlatSourceImpl::asFragmentProcessor(
-        const SkShader::AsFPArgs&) const {
+        const SkShaderBase::AsFPArgs&) const {
 
     return sk_make_sp<NormalFlatFP>();
 }
@@ -74,7 +66,7 @@
 
 SkNormalFlatSourceImpl::Provider::~Provider() {}
 
-SkNormalSource::Provider* SkNormalFlatSourceImpl::asProvider(const SkShader::ContextRec &rec,
+SkNormalSource::Provider* SkNormalFlatSourceImpl::asProvider(const SkShaderBase::ContextRec &rec,
                                                              SkArenaAlloc *alloc) const {
     return alloc->make<Provider>();
 }
diff --git a/src/core/SkNormalFlatSource.h b/src/core/SkNormalFlatSource.h
index 82b56f1..938e28f 100644
--- a/src/core/SkNormalFlatSource.h
+++ b/src/core/SkNormalFlatSource.h
@@ -15,10 +15,10 @@
     SkNormalFlatSourceImpl(){}
 
 #if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override;
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShaderBase::AsFPArgs&) const override;
 #endif
 
-    SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec,
+    SkNormalSource::Provider* asProvider(const SkShaderBase::ContextRec& rec,
                                          SkArenaAlloc* alloc) const override;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalFlatSourceImpl)
diff --git a/src/core/SkNormalMapSource.cpp b/src/core/SkNormalMapSource.cpp
index afa4698..741db66 100644
--- a/src/core/SkNormalMapSource.cpp
+++ b/src/core/SkNormalMapSource.cpp
@@ -11,7 +11,6 @@
 #include "SkLightingShader.h"
 #include "SkMatrix.h"
 #include "SkNormalSource.h"
-#include "SkNormalSourcePriv.h"
 #include "SkPM4f.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
@@ -32,12 +31,12 @@
         this->initClassID<NormalMapFP>();
     }
 
-    class GLSLNormalMapFP : public GLSLNormalFP {
+    class GLSLNormalMapFP : public GrGLSLFragmentProcessor {
     public:
         GLSLNormalMapFP()
             : fColumnMajorInvCTM22{0.0f} {}
 
-        void onEmitCode(EmitArgs& args) override {
+        void emitCode(EmitArgs& args) override {
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
 
@@ -47,7 +46,7 @@
                                                    kDefault_GrSLPrecision, "Xform", &xformUniName);
 
             SkString dstNormalColorName("dstNormalColor");
-            this->emitChild(0, nullptr, &dstNormalColorName, args);
+            this->emitChild(0, &dstNormalColorName, args);
             fragBuilder->codeAppendf("vec3 normal = normalize(%s.rgb - vec3(0.5));",
                                      dstNormalColorName.c_str());
 
@@ -77,9 +76,9 @@
             b->add32(0x0);
         }
 
-    protected:
-        void setNormalData(const GrGLSLProgramDataManager& pdman,
-                           const GrProcessor& proc) override {
+    private:
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& proc) override {
             const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
 
             const SkMatrix& invCTM = normalMapFP.invCTM();
@@ -118,8 +117,8 @@
 };
 
 sk_sp<GrFragmentProcessor> SkNormalMapSourceImpl::asFragmentProcessor(
-        const SkShader::AsFPArgs& args) const {
-    sk_sp<GrFragmentProcessor> mapFP = fMapShader->asFragmentProcessor(args);
+        const SkShaderBase::AsFPArgs& args) const {
+    sk_sp<GrFragmentProcessor> mapFP = as_SB(fMapShader)->asFragmentProcessor(args);
     if (!mapFP) {
         return nullptr;
     }
@@ -132,11 +131,11 @@
 ////////////////////////////////////////////////////////////////////////////
 
 SkNormalMapSourceImpl::Provider::Provider(const SkNormalMapSourceImpl& source,
-                                          SkShader::Context* mapContext)
+                                          SkShaderBase::Context* mapContext)
     : fSource(source)
     , fMapContext(mapContext) {}
 
-SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider(const SkShader::ContextRec &rec,
+SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider(const SkShaderBase::ContextRec &rec,
                                                             SkArenaAlloc* alloc) const {
     SkMatrix normTotalInv;
     if (!this->computeNormTotalInverse(rec, &normTotalInv)) {
@@ -146,10 +145,10 @@
     // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd
     SkPaint overridePaint {*(rec.fPaint)};
     overridePaint.setAlpha(0xFF);
-    SkShader::ContextRec overrideRec(overridePaint, *(rec.fMatrix), rec.fLocalMatrix,
-                                     rec.fPreferredDstType, rec.fDstColorSpace);
+    SkShaderBase::ContextRec overrideRec(overridePaint, *(rec.fMatrix), rec.fLocalMatrix,
+                                         rec.fPreferredDstType, rec.fDstColorSpace);
 
-    SkShader::Context* context = fMapShader->makeContext(overrideRec, alloc);
+    auto* context = as_SB(fMapShader)->makeContext(overrideRec, alloc);
     if (!context) {
         return nullptr;
     }
@@ -157,7 +156,7 @@
     return alloc->make<Provider>(*this, context);
 }
 
-bool SkNormalMapSourceImpl::computeNormTotalInverse(const SkShader::ContextRec& rec,
+bool SkNormalMapSourceImpl::computeNormTotalInverse(const SkShaderBase::ContextRec& rec,
                                                     SkMatrix* normTotalInverse) const {
     SkMatrix total = SkMatrix::Concat(*rec.fMatrix, fMapShader->getLocalMatrix());
     if (rec.fLocalMatrix) {
@@ -221,7 +220,7 @@
 
 sk_sp<SkFlattenable> SkNormalMapSourceImpl::CreateProc(SkReadBuffer& buf) {
 
-    sk_sp<SkShader> mapShader = buf.readFlattenable<SkShader>();
+    sk_sp<SkShader> mapShader = buf.readFlattenable<SkShaderBase>();
 
     SkMatrix invCTM;
     buf.readMatrix(&invCTM);
diff --git a/src/core/SkNormalMapSource.h b/src/core/SkNormalMapSource.h
index f2b07f2..a02e6ab 100644
--- a/src/core/SkNormalMapSource.h
+++ b/src/core/SkNormalMapSource.h
@@ -17,10 +17,10 @@
             , fInvCTM(invCTM) {}
 
 #if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override;
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShaderBase::AsFPArgs&) const override;
 #endif
 
-    SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec,
+    SkNormalSource::Provider* asProvider(const SkShaderBase::ContextRec& rec,
                                          SkArenaAlloc* alloc) const override;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalMapSourceImpl)
@@ -28,18 +28,19 @@
 protected:
     void flatten(SkWriteBuffer& buf) const override;
 
-    bool computeNormTotalInverse(const SkShader::ContextRec& rec, SkMatrix* normTotalInverse) const;
+    bool computeNormTotalInverse(const SkShaderBase::ContextRec& rec,
+                                 SkMatrix* normTotalInverse) const;
 
 private:
     class Provider : public SkNormalSource::Provider {
     public:
-        Provider(const SkNormalMapSourceImpl& source, SkShader::Context* mapContext);
+        Provider(const SkNormalMapSourceImpl& source, SkShaderBase::Context* mapContext);
 
         void fillScanLine(int x, int y, SkPoint3 output[], int count) const override;
 
     private:
         const SkNormalMapSourceImpl& fSource;
-        SkShader::Context* fMapContext;
+        SkShaderBase::Context*       fMapContext;
 
         typedef SkNormalSource::Provider INHERITED;
     };
diff --git a/src/core/SkNormalSource.cpp b/src/core/SkNormalSource.cpp
index 2bea7ba..ad1f5a3 100644
--- a/src/core/SkNormalSource.cpp
+++ b/src/core/SkNormalSource.cpp
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-#include "SkNormalBevelSource.h"
 #include "SkNormalFlatSource.h"
 #include "SkNormalMapSource.h"
 #include "SkNormalSource.h"
@@ -18,7 +17,6 @@
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource)
 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalMapSourceImpl)
 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalFlatSourceImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalBevelSourceImpl)
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
 
 ////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkNormalSource.h b/src/core/SkNormalSource.h
index 221c09d..e9879fe 100644
--- a/src/core/SkNormalSource.h
+++ b/src/core/SkNormalSource.h
@@ -9,7 +9,7 @@
 #define SkNormalSource_DEFINED
 
 #include "SkFlattenable.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 
 class SkMatrix;
 struct SkPoint3;
@@ -28,7 +28,7 @@
     /** Returns a fragment processor that takes no input and outputs a normal (already rotated)
         as its output color. To be used as a child fragment processor.
     */
-    virtual sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const = 0;
+    virtual sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShaderBase::AsFPArgs&) const = 0;
 #endif
 
     class Provider {
@@ -44,7 +44,7 @@
     /** Returns an instance of 'Provider' that provides normals for the CPU pipeline. The
         necessary data will be initialized in place at 'storage'.
     */
-    virtual Provider* asProvider(const SkShader::ContextRec&, SkArenaAlloc*) const = 0;
+    virtual Provider* asProvider(const SkShaderBase::ContextRec&, SkArenaAlloc*) const = 0;
 
     /** Returns a normal source that provides normals sourced from the the normal map argument.
 
@@ -68,54 +68,6 @@
     */
     static sk_sp<SkNormalSource> MakeFlat();
 
-    /** This enum specifies the shape of the bevel. All bevels output <0, 0, 1> as the surface
-     *  normal for any point more than 'width' away from any edge.
-     *
-     *  Mathematical details:
-     *  For the purpose of describing the shape of the bevel, we define 'w' to be the given width of
-     *  the bevel, and 'h' to be the given height. We will assume the shape is rotated such that the
-     *  point being shaded as well as the closest point in the shape's edge to that point are in the
-     *  x-axis, and the shape is translated so that the aforementioned point in the edge is at
-     *  coordinates (w, 0, 0) and the end of the bevel is at (0, 0, h).
-     *
-     */
-    enum class BevelType {
-        /* This bevel simulates a surface that is slanted from the shape's edges inwards, linearly.
-         *
-         * Mathematical details:
-         * This bevel follows a straight line from (w, 0, 0) to (0, 0, h).
-         */
-        kLinear,
-        /* This bevel simulates a surface that rounds off at the shape's edges, smoothly becoming
-         * perpendicular to the x-y plane.
-         *
-         * Mathematical details:
-         * This bevel follows the only quadratic bezier curve whose start point is at (w, 0, 0),
-         * control point is at (w, 0, h), and end point is at (0, 0, h).
-         */
-        kRoundedOut,
-        /* This bevel simulates a surface that sharply becomes perpendicular to the x-y plane when
-         * at 'width' units from the nearest edge, and then rounds off towards the shape's
-         * edge, smoothly becoming parallel to the x-y plane.
-         *
-         * Mathematical details:
-         * This bevel follows the only quadratic bezier curve whose start point is at (w, 0, 0),
-         * control point is at (0, 0, 0), and end point is at (0, 0, h).
-         */
-        kRoundedIn
-    };
-
-    /** Returns a normal source that generates a bevel for the shape being drawn. Currently this is
-        not implemented on CPU rendering. On GPU this currently only works for anti-aliased circles
-        and rectangles.
-
-        @param  type   the type of bevel to add.
-        @param  width  the width of the bevel, in source space. Must be positive.
-        @param  height the height of the plateau, in source space. Can be positive, negative,
-                       or zero. A negative height means the simulated bevels slope downwards.
-    */
-    static sk_sp<SkNormalSource> MakeBevel(BevelType, SkScalar width, SkScalar height);
-
     SK_DEFINE_FLATTENABLE_TYPE(SkNormalSource)
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 };
diff --git a/src/core/SkNormalSourcePriv.h b/src/core/SkNormalSourcePriv.h
deleted file mode 100644
index ce8baf6..0000000
--- a/src/core/SkNormalSourcePriv.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkNormalSourcePriv_DEFINED
-#define SkNormalSourcePriv_DEFINED
-
-#if SK_SUPPORT_GPU
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-
-/* GLSLFragmentProcessors for NormalSourceImpls must sub-class this class and override onEmitCode,
- * and setNormalData calls, as well as all other calls FPs normally override, except for the 2
- * defined in this superclass.
- * This class exists to intercept emitCode calls and emit <0, 0, 1> if the FP requires a distance
- * vector but the GP doesn't provide it. onSetData calls need to be intercepted too because
- * uniform handlers will be invalid in subclasses where onEmitCode isn't called.
- * We don't need to adjust the key here since the use of a given GP (through its class ID already in
- * the key), will determine what code gets emitted here.
- */
-class GLSLNormalFP : public GrGLSLFragmentProcessor {
-public:
-    GLSLNormalFP()
-        : fDidIntercept(false) {}
-
-    void emitCode(EmitArgs& args) final override {
-        if (args.fFp.usesDistanceVectorField() && !args.fGpImplementsDistanceVector) {
-            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-            fragBuilder->codeAppendf("// GLSLNormalFP intercepted emitCode call, GP does not "
-                                             "implement required distance vector feature\n");
-            fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
-
-            fDidIntercept = true;
-        } else {
-            this->onEmitCode(args);
-        }
-    }
-
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) final override {
-        if (!fDidIntercept) {
-            this->setNormalData(pdman, proc);
-        }
-    }
-
-protected:
-    virtual void onEmitCode(EmitArgs& args) = 0;
-    virtual void setNormalData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) = 0;
-
-private:
-    bool fDidIntercept;
-};
-#endif
-
-#endif
diff --git a/src/core/SkOpts.cpp b/src/core/SkOpts.cpp
index 273c654..33c3690 100644
--- a/src/core/SkOpts.cpp
+++ b/src/core/SkOpts.cpp
@@ -43,8 +43,8 @@
 #include "SkBlurImageFilter_opts.h"
 #include "SkChecksum_opts.h"
 #include "SkMorphologyImageFilter_opts.h"
-#include "SkRasterPipeline_opts.h"
 #include "SkSwizzler_opts.h"
+#include "SkUtils_opts.h"
 #include "SkXfermode_opts.h"
 
 namespace SkOpts {
@@ -82,9 +82,11 @@
 
     DEFINE_DEFAULT(srcover_srgb_srgb);
 
-    DEFINE_DEFAULT(hash_fn);
+    DEFINE_DEFAULT(memset16);
+    DEFINE_DEFAULT(memset32);
+    DEFINE_DEFAULT(memset64);
 
-    DEFINE_DEFAULT(run_pipeline);
+    DEFINE_DEFAULT(hash_fn);
 
     DEFINE_DEFAULT(convolve_vertically);
     DEFINE_DEFAULT(convolve_horizontally);
diff --git a/src/core/SkOpts.h b/src/core/SkOpts.h
index 801b96d..31153f9 100644
--- a/src/core/SkOpts.h
+++ b/src/core/SkOpts.h
@@ -53,14 +53,16 @@
     // If nsrc < ndst, we loop over src to create a pattern.
     extern void (*srcover_srgb_srgb)(uint32_t* dst, const uint32_t* src, int ndst, int nsrc);
 
+    extern void (*memset16)(uint16_t[], uint16_t, int);
+    extern void (*memset32)(uint32_t[], uint32_t, int);
+    extern void (*memset64)(uint64_t[], uint64_t, int);
+
     // The fastest high quality 32-bit hash we can provide on this platform.
     extern uint32_t (*hash_fn)(const void*, size_t, uint32_t seed);
     static inline uint32_t hash(const void* data, size_t bytes, uint32_t seed=0) {
         return hash_fn(data, bytes, seed);
     }
 
-    extern void (*run_pipeline)(size_t, size_t, const SkRasterPipeline::Stage*, int);
-
     extern void (*convolve_vertically)(const SkConvolutionFilter1D::ConvolutionFixed* filter_values,
                                        int filter_length, unsigned char* const* source_data_rows,
                                        int pixel_width, unsigned char* out_row, bool has_alpha);
diff --git a/src/core/SkPM4f.h b/src/core/SkPM4f.h
index f983101..ab2258b 100644
--- a/src/core/SkPM4f.h
+++ b/src/core/SkPM4f.h
@@ -37,6 +37,15 @@
     float b() const { return fVec[B]; }
     float a() const { return fVec[A]; }
 
+    static SkPM4f FromPremulRGBA(float r, float g, float b, float a) {
+        SkPM4f p;
+        p.fVec[R] = r;
+        p.fVec[G] = g;
+        p.fVec[B] = b;
+        p.fVec[A] = a;
+        return p;
+    }
+
     static SkPM4f From4f(const Sk4f& x) {
         SkPM4f pm;
         x.store(pm.fVec);
diff --git a/src/core/SkPM4fPriv.h b/src/core/SkPM4fPriv.h
index 821c882..700e128 100644
--- a/src/core/SkPM4fPriv.h
+++ b/src/core/SkPM4fPriv.h
@@ -100,17 +100,24 @@
 
 
 // N.B. scratch_matrix_3x4 must live at least as long as p.
-static inline bool append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12],
-                                          SkColorSpace* src, SkColorSpace* dst) {
-    if (src == dst) { return true; }
-    if (!dst)       { return true; }   // Legacy modes intentionally ignore color gamut.
-    if (!src)       { return true; }   // A null src color space means linear gamma, dst gamut.
+static inline void append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12],
+                                          SkColorSpace* src, SkColorSpace* dst,
+                                          SkAlphaType alphaType) {
+    if (src == dst) { return; }   // That was easy.
+    if (!dst)       { return; }   // Legacy modes intentionally ignore color gamut.
+    if (!src)       { return; }   // A null src color space means linear gamma, dst gamut.
 
     auto toXYZ = as_CSB(src)->  toXYZD50(),
        fromXYZ = as_CSB(dst)->fromXYZD50();
-    if (!toXYZ || !fromXYZ) { return false; }  // Unsupported color space type.
+    if (!toXYZ || !fromXYZ) {
+        SkASSERT(false);  // We really don't want to get here with a weird colorspace.
+        return;
+    }
 
-    if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { return true; }
+    // Slightly more sophisticated version of if (src == dst)
+    if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) {
+        return;
+    }
 
     SkMatrix44 m44(*fromXYZ, *toXYZ);
 
@@ -121,18 +128,21 @@
     *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2);
     *ptr++ = m44.get(0,3); *ptr++ = m44.get(1,3); *ptr++ = m44.get(2,3);
 
-    bool needs_clamp_0, needs_clamp_a;
-    analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_a);
+    bool needs_clamp_0, needs_clamp_1;
+    analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_1);
 
     p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4);
     if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); }
-    if (needs_clamp_a) { p->append(SkRasterPipeline::clamp_a); }
-    return true;
+    if (needs_clamp_1) {
+        (kPremul_SkAlphaType == alphaType) ? p->append(SkRasterPipeline::clamp_a)
+                                           : p->append(SkRasterPipeline::clamp_1);
+    }
 }
 
-static inline bool append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch,
-                                          SkColorSpace* src, SkColorSpace* dst) {
-    return append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst);
+static inline void append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch,
+                                          SkColorSpace* src, SkColorSpace* dst,
+                                          SkAlphaType alphaType) {
+    append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst, alphaType);
 }
 
 static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
@@ -142,12 +152,12 @@
 
         float scratch_matrix_3x4[12];
 
-        SkRasterPipeline p;
+        SkRasterPipeline_<256> p;
         p.append(SkRasterPipeline::constant_color, color4f_ptr);
-        append_gamut_transform(&p, scratch_matrix_3x4, src, dst);
+        append_gamut_transform(&p, scratch_matrix_3x4, src, dst, kUnpremul_SkAlphaType);
         p.append(SkRasterPipeline::store_f32, &color4f_ptr);
 
-        p.run(0,1);
+        p.run(0,0,1);
     }
     return color4f;
 }
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 568ba6a..7875495 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -12,6 +12,7 @@
 #include "SkData.h"
 #include "SkDraw.h"
 #include "SkFontDescriptor.h"
+#include "SkGraphics.h"
 #include "SkGlyphCache.h"
 #include "SkImageFilter.h"
 #include "SkMaskFilter.h"
@@ -26,6 +27,7 @@
 #include "SkScalar.h"
 #include "SkScalerContext.h"
 #include "SkShader.h"
+#include "SkShaderBase.h"
 #include "SkStringUtils.h"
 #include "SkStroke.h"
 #include "SkStrokeRec.h"
@@ -391,6 +393,12 @@
     return tooBig(matrix, MaxCacheSize2());
 }
 
+SkScalar SkPaint::MaxCacheSize2() {
+    // we have a self-imposed maximum, just for memory-usage sanity
+    const int limit = SkMin32(SkGraphics::GetFontCachePointSizeLimit(), 1024);
+    const SkScalar maxSize = SkIntToScalar(limit);
+    return maxSize * maxSize;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1250,7 +1258,7 @@
 static bool justAColor(const SkPaint& paint, SkColor* color) {
     SkColor c = paint.getColor();
 
-    SkShader* shader = paint.getShader();
+    const auto* shader = as_SB(paint.getShader());
     if (shader && !shader->asLuminanceColor(&c)) {
         return false;
     }
@@ -2071,8 +2079,7 @@
         str->append("</dd>");
     }
 
-    SkShader* shader = this->getShader();
-    if (shader) {
+    if (const auto* shader = as_SB(this->getShader())) {
         str->append("<dt>Shader:</dt><dd>");
         shader->toString(str);
         str->append("</dd>");
diff --git a/src/core/SkPaintPriv.h b/src/core/SkPaintPriv.h
index de8e6c7..e5c98f5 100644
--- a/src/core/SkPaintPriv.h
+++ b/src/core/SkPaintPriv.h
@@ -9,6 +9,7 @@
 #define SkPaintPriv_DEFINED
 
 #include "SkPaint.h"
+#include "SkMatrix.h"
 
 class SkBitmap;
 class SkImage;
@@ -46,6 +47,21 @@
     static bool Overwrites(const SkImage*, const SkPaint* paint);
 
     static void ScaleFontMetrics(SkPaint::FontMetrics*, SkScalar);
+
+    /**
+     *  Return a matrix that applies the paint's text values: size, scale, skew
+     */
+    static void MakeTextMatrix(SkMatrix* matrix, SkScalar size, SkScalar scaleX, SkScalar skewX) {
+        matrix->setScale(size * scaleX, size);
+        if (skewX) {
+            matrix->postSkew(skewX, 0);
+        }
+    }
+
+    static void MakeTextMatrix(SkMatrix* matrix, const SkPaint& paint) {
+        MakeTextMatrix(matrix, paint.getTextSize(), paint.getTextScaleX(), paint.getTextSkewX());
+    }
+    
 };
 
 #endif
diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h
index 6f6a9f1..43805fd 100644
--- a/src/core/SkPictureCommon.h
+++ b/src/core/SkPictureCommon.h
@@ -14,6 +14,7 @@
 
 #include "SkPathEffect.h"
 #include "SkRecords.h"
+#include "SkShader.h"
 #include "SkTLogic.h"
 
 // N.B. This name is slightly historical: hunting season is now open for SkImages too.
@@ -80,7 +81,6 @@
     void operator()(const SkRecords::DrawPicture& op) {
         fNumSlowPathsAndDashEffects += op.picture->numSlowPaths();
     }
-    void operator()(const SkRecords::DrawDrawable&) { /* TODO */ }
 
     void checkPaint(const SkPaint* paint) {
         if (paint && paint->getPathEffect()) {
@@ -131,12 +131,13 @@
     }
 
     template <typename T>
-    SK_WHEN(T::kTags & SkRecords::kDraw_Tag, void) operator()(const T& op) {
+    SK_WHEN(T::kTags & SkRecords::kHasPaint_Tag, void) operator()(const T& op) {
         this->checkPaint(AsPtr(op.paint));
     }
 
     template <typename T>
-    SK_WHEN(!(T::kTags & SkRecords::kDraw_Tag), void) operator()(const T& op) { /* do nothing */ }
+    SK_WHEN(!(T::kTags & SkRecords::kHasPaint_Tag), void)
+      operator()(const T& op) { /* do nothing */ }
 
     int fNumSlowPathsAndDashEffects;
 };
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index a164746..3c257b9 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -87,9 +87,9 @@
     DRAW_DRAWABLE_MATRIX,
     DRAW_TEXT_RSXFORM,
 
-    TRANSLATE_Z,
+    TRANSLATE_Z, // deprecated (M60)
 
-    DRAW_SHADOWED_PICTURE_LIGHTS,
+    DRAW_SHADOW_REC,
     DRAW_IMAGE_LATTICE,
     DRAW_ARC,
     DRAW_REGION,
@@ -122,6 +122,8 @@
     SAVELAYERREC_HAS_PAINT      = 1 << 1,
     SAVELAYERREC_HAS_BACKDROP   = 1 << 2,
     SAVELAYERREC_HAS_FLAGS      = 1 << 3,
+    SAVELAYERREC_HAS_CLIPMASK   = 1 << 4,
+    SAVELAYERREC_HAS_CLIPMATRIX = 1 << 5,
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkPictureImageGenerator.cpp b/src/core/SkPictureImageGenerator.cpp
index a1d919a..86e98d8 100644
--- a/src/core/SkPictureImageGenerator.cpp
+++ b/src/core/SkPictureImageGenerator.cpp
@@ -7,6 +7,7 @@
 
 #include "SkImage_Base.h"
 #include "SkCanvas.h"
+#include "SkColorSpaceXformCanvas.h"
 #include "SkMakeUnique.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
@@ -58,20 +59,25 @@
 }
 
 bool SkPictureImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                                          SkPMColor ctable[], int* ctableCount) {
-    if (ctable || ctableCount) {
+                                          const Options& opts) {
+    bool useXformCanvas =
+            SkTransferFunctionBehavior::kIgnore == opts.fBehavior && info.colorSpace();
+
+    SkImageInfo canvasInfo = useXformCanvas ? info.makeColorSpace(nullptr) : info;
+    std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(canvasInfo, pixels, rowBytes);
+    if (!canvas) {
         return false;
     }
+    canvas->clear(0);
 
-    SkBitmap bitmap;
-    if (!bitmap.installPixels(info, pixels, rowBytes)) {
-        return false;
+    SkCanvas* canvasPtr = canvas.get();
+    std::unique_ptr<SkCanvas> xformCanvas;
+    if (useXformCanvas) {
+        xformCanvas = SkCreateColorSpaceXformCanvas(canvas.get(), info.refColorSpace());
+        canvasPtr = xformCanvas.get();
     }
 
-    bitmap.eraseColor(SK_ColorTRANSPARENT);
-    SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
-    canvas.drawPicture(fPicture.get(), &fMatrix, fPaint.getMaybeNull());
-
+    canvasPtr->drawPicture(fPicture, &fMatrix, fPaint.getMaybeNull());
     return true;
 }
 
diff --git a/src/core/SkPictureImageGenerator.h b/src/core/SkPictureImageGenerator.h
index 8387260..6dba29c 100644
--- a/src/core/SkPictureImageGenerator.h
+++ b/src/core/SkPictureImageGenerator.h
@@ -17,10 +17,11 @@
                                                   sk_sp<SkColorSpace>);
 
 protected:
-    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
-                     int* ctableCount) override;
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options& opts)
+                     override;
 
 #if SK_SUPPORT_GPU
+    bool onCanGenerateTexture() const override { return true; }
     sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
                                             const SkIPoint&) override;
 #endif
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index e5fc4fd..1e4fe11 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkCanvas.h"
+#include "SkDrawShadowRec.h"
 #include "SkPatchUtils.h"
 #include "SkPictureData.h"
 #include "SkPicturePlayback.h"
@@ -570,6 +571,20 @@
                 canvas->drawRRect(rrect, *paint);
             }
         } break;
+        case DRAW_SHADOW_REC: {
+            const auto& path = fPictureData->getPath(reader);
+            SkDrawShadowRec rec;
+            reader->readPoint3(&rec.fZPlaneParams);
+            reader->readPoint3(&rec.fLightPos);
+            rec.fLightRadius = reader->readScalar();
+            rec.fAmbientAlpha = reader->readScalar();
+            rec.fSpotAlpha = reader->readScalar();
+            rec.fColor = reader->read32();
+            rec.fFlags = reader->read32();
+            BREAK_ON_READ_ERROR(reader);
+
+            canvas->private_draw_shadow_rec(path, rec);
+        } break;
         case DRAW_SPRITE: {
             /* const SkPaint* paint = */ fPictureData->getPaint(reader);
             /* const SkImage* image = */ fPictureData->getBitmapAsImage(reader);
@@ -651,7 +666,7 @@
         case DRAW_VERTICES_RETIRED_03_2017: {
             const SkPaint* paint = fPictureData->getPaint(reader);
             DrawVertexFlags flags = (DrawVertexFlags)reader->readInt();
-            SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader->readInt();
+            SkVertices::VertexMode vmode = (SkVertices::VertexMode)reader->readInt();
             int vCount = reader->readInt();
             const SkPoint* verts = (const SkPoint*)reader->skip(vCount * sizeof(SkPoint));
             const SkPoint* texs = nullptr;
@@ -724,7 +739,8 @@
             canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags));
         } break;
         case SAVE_LAYER_SAVELAYERREC: {
-            SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0);
+            SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, nullptr, nullptr, 0);
+            SkMatrix clipMatrix;
             const uint32_t flatFlags = reader->readInt();
             SkRect bounds;
             if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
@@ -742,6 +758,13 @@
             if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
                 rec.fSaveLayerFlags = reader->readInt();
             }
+            if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) {
+                rec.fClipMask = fPictureData->getImage(reader);
+            }
+            if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) {
+                reader->readMatrix(&clipMatrix);
+                rec.fClipMatrix = &clipMatrix;
+            }
             BREAK_ON_READ_ERROR(reader);
 
             canvas->saveLayer(rec);
@@ -775,14 +798,6 @@
 
             canvas->translate(dx, dy);
         } break;
-        case TRANSLATE_Z: {
-#ifdef SK_EXPERIMENTAL_SHADOWING
-            SkScalar dz = reader->readScalar();
-            BREAK_ON_READ_ERROR(reader);
-
-            canvas->translateZ(dz);
-#endif
-        } break;
         default:
             SkASSERTF(false, "Unknown draw type: %d", op);
     }
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 9bdb6c5..0e1faca 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkPictureRecord.h"
+#include "SkDrawShadowRec.h"
 #include "SkImage_Base.h"
 #include "SkPatchUtils.h"
 #include "SkPixelRef.h"
@@ -98,6 +99,14 @@
         flatFlags |= SAVELAYERREC_HAS_FLAGS;
         size += sizeof(uint32_t);
     }
+    if (rec.fClipMask) {
+        flatFlags |= SAVELAYERREC_HAS_CLIPMASK;
+        size += sizeof(uint32_t); // clip image index
+    }
+    if (rec.fClipMatrix) {
+        flatFlags |= SAVELAYERREC_HAS_CLIPMATRIX;
+        size += rec.fClipMatrix->writeToMemory(nullptr);
+    }
 
     const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size);
     this->addInt(flatFlags);
@@ -116,6 +125,12 @@
     if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
         this->addInt(rec.fSaveLayerFlags);
     }
+    if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) {
+        this->addImage(rec.fClipMask);
+    }
+    if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) {
+        this->addMatrix(*rec.fClipMatrix);
+    }
     this->validate(initialOffset, size);
 }
 
@@ -220,18 +235,6 @@
     this->INHERITED::didSetMatrix(matrix);
 }
 
-void SkPictureRecord::didTranslateZ(SkScalar z) {
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    this->validate(fWriter.bytesWritten(), 0);
-    // op + scalar
-    size_t size = 1 * kUInt32Size + 1 * sizeof(SkScalar);
-    size_t initialOffset = this->addDraw(TRANSLATE_Z, &size);
-    this->addScalar(z);
-    this->validate(initialOffset, size);
-    this->INHERITED::didTranslateZ(z);
-#endif
-}
-
 static bool clipOpExpands(SkClipOp op) {
     switch (op) {
         case kUnion_SkClipOp:
@@ -682,29 +685,6 @@
     this->validate(initialOffset, size);
 }
 
-void SkPictureRecord::onDrawShadowedPicture(const SkPicture* picture,
-                                            const SkMatrix* matrix,
-                                            const SkPaint* paint,
-                                            const SkShadowParams& params) {
-    // op + picture index
-    size_t size = 2 * kUInt32Size;
-    size_t initialOffset;
-
-    // TODO: handle recording params.
-    if (nullptr == matrix && nullptr == paint) {
-        initialOffset = this->addDraw(DRAW_PICTURE, &size);
-        this->addPicture(picture);
-    } else {
-        const SkMatrix& m = matrix ? *matrix : SkMatrix::I();
-        size += m.writeToMemory(nullptr) + kUInt32Size;    // matrix + paint
-        initialOffset = this->addDraw(DRAW_PICTURE_MATRIX_PAINT, &size);
-        this->addPaintPtr(paint);
-        this->addMatrix(m);
-        this->addPicture(picture);
-    }
-    this->validate(initialOffset, size);
-}
-
 void SkPictureRecord::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
     // op + drawable index
     size_t size = 2 * kUInt32Size;
@@ -807,6 +787,24 @@
     this->validate(initialOffset, size);
 }
 
+void SkPictureRecord::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+    // op + path index + zParams + lightPos + lightRadius + spot/ambient alphas + color + flags
+    size_t size = 2 * kUInt32Size + 2 * sizeof(SkPoint3) + 3 * sizeof(SkScalar) + 2 * kUInt32Size;
+    size_t initialOffset = this->addDraw(DRAW_SHADOW_REC, &size);
+
+    this->addPath(path);
+
+    fWriter.writePoint3(rec.fZPlaneParams);
+    fWriter.writePoint3(rec.fLightPos);
+    fWriter.writeScalar(rec.fLightRadius);
+    fWriter.writeScalar(rec.fAmbientAlpha);
+    fWriter.writeScalar(rec.fSpotAlpha);
+    fWriter.write32(rec.fColor);
+    fWriter.write32(rec.fFlags);
+
+    this->validate(initialOffset, size);
+}
+
 void SkPictureRecord::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
     size_t keyLen = fWriter.WriteStringSize(key);
     size_t valueLen = fWriter.WriteDataSize(value);
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index dc9a002..9a93bf9 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -164,12 +164,6 @@
     void didConcat(const SkMatrix&) override;
     void didSetMatrix(const SkMatrix&) override;
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void didTranslateZ(SkScalar) override;
-#else
-    void didTranslateZ(SkScalar);
-#endif
-
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
 
     void onDrawText(const void* text, size_t, SkScalar x, SkScalar y, const SkPaint&) override;
@@ -203,6 +197,7 @@
                          const SkPaint*) override;
     void onDrawImageLattice(const SkImage*, const SkCanvas::Lattice& lattice, const SkRect& dst,
                             const SkPaint*) override;
+    void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
@@ -212,14 +207,6 @@
 
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void onDrawShadowedPicture(const SkPicture*, const SkMatrix*,
-                               const SkPaint*, const SkShadowParams& params) override;
-#else
-    void onDrawShadowedPicture(const SkPicture*, const SkMatrix*,
-                               const SkPaint*, const SkShadowParams& params);
-#endif
-
     void onDrawDrawable(SkDrawable*, const SkMatrix*) override;
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
 
diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp
index aaf1d0d..a37cd48 100644
--- a/src/core/SkPictureRecorder.cpp
+++ b/src/core/SkPictureRecorder.cpp
@@ -8,6 +8,7 @@
 #include "SkBigPicture.h"
 #include "SkData.h"
 #include "SkDrawable.h"
+#include "SkMiniRecorder.h"
 #include "SkPictureRecorder.h"
 #include "SkRecord.h"
 #include "SkRecordDraw.h"
@@ -18,7 +19,8 @@
 
 SkPictureRecorder::SkPictureRecorder() {
     fActivelyRecording = false;
-    fRecorder.reset(new SkRecorder(nullptr, SkRect::MakeEmpty(), &fMiniRecorder));
+    fMiniRecorder.reset(new SkMiniRecorder);
+    fRecorder.reset(new SkRecorder(nullptr, SkRect::MakeEmpty(), fMiniRecorder.get()));
 }
 
 SkPictureRecorder::~SkPictureRecorder() {}
@@ -42,7 +44,7 @@
     SkRecorder::DrawPictureMode dpm = (recordFlags & kPlaybackDrawPicture_RecordFlag)
         ? SkRecorder::Playback_DrawPictureMode
         : SkRecorder::Record_DrawPictureMode;
-    fRecorder->reset(fRecord.get(), cullRect, dpm, &fMiniRecorder);
+    fRecorder->reset(fRecord.get(), cullRect, dpm, fMiniRecorder.get());
     fActivelyRecording = true;
     return this->getRecordingCanvas();
 }
@@ -56,21 +58,14 @@
     fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.
 
     if (fRecord->count() == 0) {
-        if (finishFlags & kReturnNullForEmpty_FinishFlag) {
-            return nullptr;
-        }
-        return fMiniRecorder.detachAsPicture(fCullRect);
+        auto pic = fMiniRecorder->detachAsPicture(fBBH ? nullptr : &fCullRect);
+        fBBH.reset(nullptr);
+        return pic;
     }
 
     // TODO: delay as much of this work until just before first playback?
     SkRecordOptimize(fRecord.get());
 
-    if (fRecord->count() == 0) {
-        if (finishFlags & kReturnNullForEmpty_FinishFlag) {
-            return nullptr;
-        }
-    }
-
     SkDrawableList* drawableList = fRecorder->getDrawableList();
     SkBigPicture::SnapshotArray* pictList =
         drawableList ? drawableList->newDrawableSnapshot() : nullptr;
@@ -125,12 +120,6 @@
 
     SkRecordOptimize(fRecord.get());
 
-    if (fRecord->count() == 0) {
-        if (finishFlags & kReturnNullForEmpty_FinishFlag) {
-            return nullptr;
-        }
-    }
-
     if (fBBH.get()) {
         SkAutoTMalloc<SkRect> bounds(fRecord->count());
         SkRecordFillBounds(fCullRect, *fRecord, bounds);
diff --git a/src/core/SkPictureShader.cpp b/src/core/SkPictureShader.cpp
deleted file mode 100644
index 50c33a8..0000000
--- a/src/core/SkPictureShader.cpp
+++ /dev/null
@@ -1,333 +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 "SkPictureShader.h"
-
-#include "SkArenaAlloc.h"
-#include "SkBitmap.h"
-#include "SkBitmapProcShader.h"
-#include "SkCanvas.h"
-#include "SkImage.h"
-#include "SkImageShader.h"
-#include "SkMatrixUtils.h"
-#include "SkPicture.h"
-#include "SkPictureImageGenerator.h"
-#include "SkReadBuffer.h"
-#include "SkResourceCache.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrCaps.h"
-#include "GrFragmentProcessor.h"
-#endif
-
-namespace {
-static unsigned gBitmapSkaderKeyNamespaceLabel;
-
-struct BitmapShaderKey : public SkResourceCache::Key {
-public:
-    BitmapShaderKey(uint32_t pictureID,
-                    const SkRect& tile,
-                    SkShader::TileMode tmx,
-                    SkShader::TileMode tmy,
-                    const SkSize& scale,
-                    const SkMatrix& localMatrix)
-        : fPictureID(pictureID)
-        , fTile(tile)
-        , fTmx(tmx)
-        , fTmy(tmy)
-        , fScale(scale) {
-
-        for (int i = 0; i < 9; ++i) {
-            fLocalMatrixStorage[i] = localMatrix[i];
-        }
-
-        static const size_t keySize = sizeof(fPictureID) +
-                                      sizeof(fTile) +
-                                      sizeof(fTmx) + sizeof(fTmy) +
-                                      sizeof(fScale) +
-                                      sizeof(fLocalMatrixStorage);
-        // This better be packed.
-        SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize);
-        this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize);
-    }
-
-private:
-    uint32_t           fPictureID;
-    SkRect             fTile;
-    SkShader::TileMode fTmx, fTmy;
-    SkSize             fScale;
-    SkScalar           fLocalMatrixStorage[9];
-
-    SkDEBUGCODE(uint32_t fEndOfStruct;)
-};
-
-struct BitmapShaderRec : public SkResourceCache::Rec {
-    BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
-        : fKey(key)
-        , fShader(SkRef(tileShader)) {}
-
-    BitmapShaderKey fKey;
-    sk_sp<SkShader> fShader;
-    size_t          fBitmapBytes;
-
-    const Key& getKey() const override { return fKey; }
-    size_t bytesUsed() const override {
-        // Just the record overhead -- the actual pixels are accounted by SkImageCacherator.
-        return sizeof(fKey) + sizeof(SkImageShader);
-    }
-    const char* getCategory() const override { return "bitmap-shader"; }
-    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
-
-    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
-        const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
-        sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
-
-        *result = rec.fShader;
-
-        // The bitmap shader is backed by an image generator, thus it can always re-generate its
-        // pixels if discarded.
-        return true;
-    }
-};
-
-} // namespace
-
-SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
-                                 const SkMatrix* localMatrix, const SkRect* tile)
-    : INHERITED(localMatrix)
-    , fPicture(std::move(picture))
-    , fTile(tile ? *tile : fPicture->cullRect())
-    , fTmx(tmx)
-    , fTmy(tmy) {
-}
-
-sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
-                                      const SkMatrix* localMatrix, const SkRect* tile) {
-    if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
-        return SkShader::MakeEmptyShader();
-    }
-    return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
-}
-
-sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
-    SkMatrix lm;
-    buffer.readMatrix(&lm);
-    TileMode mx = (TileMode)buffer.read32();
-    TileMode my = (TileMode)buffer.read32();
-    SkRect tile;
-    buffer.readRect(&tile);
-
-    sk_sp<SkPicture> picture;
-
-    if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
-        if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) {
-            // Older code blindly serialized pictures.  We don't trust them.
-            buffer.validate(false);
-            return nullptr;
-        }
-        // Newer code won't serialize pictures in disallow-cross-process-picture mode.
-        // Assert that they didn't serialize anything except a false here.
-        buffer.validate(!buffer.readBool());
-    } else {
-        // Old code always serialized the picture.  New code writes a 'true' first if it did.
-        if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) ||
-            buffer.readBool()) {
-            picture = SkPicture::MakeFromBuffer(buffer);
-        }
-    }
-    return SkPictureShader::Make(picture, mx, my, &lm, &tile);
-}
-
-void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeMatrix(this->getLocalMatrix());
-    buffer.write32(fTmx);
-    buffer.write32(fTmy);
-    buffer.writeRect(fTile);
-
-    // The deserialization code won't trust that our serialized picture is safe to deserialize.
-    // So write a 'false' telling it that we're not serializing a picture.
-    if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
-        buffer.writeBool(false);
-    } else {
-        buffer.writeBool(true);
-        fPicture->flatten(buffer);
-    }
-}
-
-sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM,
-                                                 SkColorSpace* dstColorSpace,
-                                                 const int maxTextureSize) const {
-    SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
-
-    SkMatrix m;
-    m.setConcat(viewMatrix, this->getLocalMatrix());
-    if (localM) {
-        m.preConcat(*localM);
-    }
-
-    // Use a rotation-invariant scale
-    SkPoint scale;
-    //
-    // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
-    //
-    if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
-        // Decomposition failed, use an approximation.
-        scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
-                  SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
-    }
-    SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
-                                     SkScalarAbs(scale.y() * fTile.height()));
-
-    // Clamp the tile size to about 4M pixels
-    static const SkScalar kMaxTileArea = 2048 * 2048;
-    SkScalar tileArea = scaledSize.width() * scaledSize.height();
-    if (tileArea > kMaxTileArea) {
-        SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
-        scaledSize.set(scaledSize.width() * clampScale,
-                       scaledSize.height() * clampScale);
-    }
-#if SK_SUPPORT_GPU
-    // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
-    if (maxTextureSize) {
-        if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
-            SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height());
-            scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
-                           SkScalarFloorToScalar(scaledSize.height() * downScale));
-        }
-    }
-#endif
-
-#ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING
-    const SkISize tileSize = scaledSize.toRound();
-#else
-    const SkISize tileSize = scaledSize.toCeil();
-#endif
-    if (tileSize.isEmpty()) {
-        return SkShader::MakeEmptyShader();
-    }
-
-    // The actual scale, compensating for rounding & clamping.
-    const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
-                                          SkIntToScalar(tileSize.height()) / fTile.height());
-
-    sk_sp<SkShader> tileShader;
-    BitmapShaderKey key(fPicture->uniqueID(),
-                        fTile,
-                        fTmx,
-                        fTmy,
-                        tileScale,
-                        this->getLocalMatrix());
-
-    if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
-        SkMatrix tileMatrix;
-        tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
-                                 SkMatrix::kFill_ScaleToFit);
-
-        sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator(
-                SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr,
-                                              SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace)));
-        if (!tileImage) {
-            return nullptr;
-        }
-
-        SkMatrix shaderMatrix = this->getLocalMatrix();
-        shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
-        tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix);
-
-        SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
-    }
-
-    return tileShader;
-}
-
-bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
-                                     const SkMatrix& ctm, const SkPaint& paint,
-                                     const SkMatrix* localMatrix) const {
-    // Keep bitmapShader alive by using alloc instead of stack memory
-    auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
-    bitmapShader = this->refBitmapShader(ctm, localMatrix, cs);
-    return bitmapShader && bitmapShader->appendStages(p, cs, alloc, ctm, paint);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-SkShader::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
-const {
-    sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix,
-                                                       rec.fDstColorSpace));
-    if (!bitmapShader) {
-        return nullptr;
-    }
-
-    PictureShaderContext* ctx =
-        alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc);
-    if (nullptr == ctx->fBitmapShaderContext) {
-        ctx = nullptr;
-    }
-    return ctx;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-SkPictureShader::PictureShaderContext::PictureShaderContext(
-        const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
-        SkArenaAlloc* alloc)
-    : INHERITED(shader, rec)
-    , fBitmapShader(std::move(bitmapShader))
-{
-    fBitmapShaderContext = fBitmapShader->makeContext(rec, alloc);
-    //if fBitmapShaderContext is null, we are invalid
-}
-
-uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
-    SkASSERT(fBitmapShaderContext);
-    return fBitmapShaderContext->getFlags();
-}
-
-SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
-    SkASSERT(fBitmapShaderContext);
-    return fBitmapShaderContext->asAShadeProc(ctx);
-}
-
-void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
-    SkASSERT(fBitmapShaderContext);
-    fBitmapShaderContext->shadeSpan(x, y, dstC, count);
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void SkPictureShader::toString(SkString* str) const {
-    static const char* gTileModeName[SkShader::kTileModeCount] = {
-        "clamp", "repeat", "mirror"
-    };
-
-    str->appendf("PictureShader: [%f:%f:%f:%f] ",
-                 fPicture->cullRect().fLeft,
-                 fPicture->cullRect().fTop,
-                 fPicture->cullRect().fRight,
-                 fPicture->cullRect().fBottom);
-
-    str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
-
-    this->INHERITED::toString(str);
-}
-#endif
-
-#if SK_SUPPORT_GPU
-sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const {
-    int maxTextureSize = 0;
-    if (args.fContext) {
-        maxTextureSize = args.fContext->caps()->maxTextureSize();
-    }
-    sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix,
-                                                       args.fDstColorSpace, maxTextureSize));
-    if (!bitmapShader) {
-        return nullptr;
-    }
-    return bitmapShader->asFragmentProcessor(SkShader::AsFPArgs(
-        args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace));
-}
-#endif
diff --git a/src/core/SkPictureShader.h b/src/core/SkPictureShader.h
deleted file mode 100644
index 9807cd9..0000000
--- a/src/core/SkPictureShader.h
+++ /dev/null
@@ -1,73 +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 SkPictureShader_DEFINED
-#define SkPictureShader_DEFINED
-
-#include "SkShader.h"
-
-class SkArenaAlloc;
-class SkBitmap;
-class SkPicture;
-
-/*
- * An SkPictureShader can be used to draw SkPicture-based patterns.
- *
- * The SkPicture is first rendered into a tile, which is then used to shade the area according
- * to specified tiling rules.
- */
-class SkPictureShader : public SkShader {
-public:
-    static sk_sp<SkShader> Make(sk_sp<SkPicture>, TileMode, TileMode, const SkMatrix*,
-                                const SkRect*);
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureShader)
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-protected:
-    SkPictureShader(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const override;
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                        const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    SkPictureShader(sk_sp<SkPicture>, TileMode, TileMode, const SkMatrix*, const SkRect*);
-
-    sk_sp<SkShader> refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix,
-                                    SkColorSpace* dstColorSpace,
-                                    const int maxTextureSize = 0) const;
-
-    sk_sp<SkPicture>    fPicture;
-    SkRect              fTile;
-    TileMode            fTmx, fTmy;
-
-    class PictureShaderContext : public SkShader::Context {
-    public:
-        PictureShaderContext(
-            const SkPictureShader&, const ContextRec&, sk_sp<SkShader> bitmapShader, SkArenaAlloc*);
-
-        uint32_t getFlags() const override;
-
-        ShadeProc asAShadeProc(void** ctx) override;
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-
-        sk_sp<SkShader>     fBitmapShader;
-        SkShader::Context*  fBitmapShaderContext;
-        void*               fBitmapShaderContextStorage;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    typedef SkShader INHERITED;
-};
-
-#endif // SkPictureShader_DEFINED
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index 5e459d3..4583c18 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -10,7 +10,6 @@
 #include "SkPixelRef.h"
 #include "SkTraceEvent.h"
 
-//#define SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
 //#define SK_TRACE_PIXELREF_LIFETIME
 
 #include "SkNextID.h"
@@ -27,204 +26,59 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// just need a > 0 value, so pick a funny one to aid in debugging
-#define SKPIXELREF_PRELOCKED_LOCKCOUNT     123456789
-
-static SkImageInfo validate_info(const SkImageInfo& info) {
-    SkAlphaType newAlphaType = info.alphaType();
-    SkAssertResult(SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType));
-    return info.makeAlphaType(newAlphaType);
-}
-
 #ifdef SK_TRACE_PIXELREF_LIFETIME
     static int32_t gInstCounter;
 #endif
 
-SkPixelRef::SkPixelRef(const SkImageInfo& info)
-    : fInfo(validate_info(info))
+SkPixelRef::SkPixelRef(int width, int height, void* pixels, size_t rowBytes,
+                       sk_sp<SkColorTable> ctable)
+    : fWidth(width)
+    , fHeight(height)
+    , fCTable(std::move(ctable))
+    , fPixels(pixels)
+    , fRowBytes(rowBytes)
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     , fStableID(SkNextID::ImageID())
 #endif
-
 {
 #ifdef SK_TRACE_PIXELREF_LIFETIME
     SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter));
 #endif
-    fRec.zero();
-    fLockCount = 0;
+
     this->needsNewGenID();
     fMutability = kMutable;
-    fPreLocked = false;
     fAddedToCache.store(false);
 }
 
 SkPixelRef::~SkPixelRef() {
-#ifndef SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
-    SkASSERT(SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount || 0 == fLockCount);
-#endif
-
 #ifdef SK_TRACE_PIXELREF_LIFETIME
     SkDebugf("~pixelref %d\n", sk_atomic_dec(&gInstCounter) - 1);
 #endif
     this->callGenIDChangeListeners();
 }
 
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+
+// This is undefined if there are clients in-flight trying to use us
+void SkPixelRef::android_only_reset(int width, int height, size_t rowBytes,
+                                    sk_sp<SkColorTable> ctable) {
+    fWidth = width;
+    fHeight = height;
+    fRowBytes = rowBytes;
+    fCTable = std::move(ctable);
+    // note: we do not change fPixels
+
+    // conservative, since its possible the "new" settings are the same as the old.
+    this->notifyPixelsChanged();
+}
+
+#endif
+
 void SkPixelRef::needsNewGenID() {
     fTaggedGenID.store(0);
     SkASSERT(!this->genIDIsUnique()); // This method isn't threadsafe, so the assert should be fine.
 }
 
-void SkPixelRef::cloneGenID(const SkPixelRef& that) {
-    // This is subtle.  We must call that.getGenerationID() to make sure its genID isn't 0.
-    uint32_t genID = that.getGenerationID();
-
-    // Neither ID is unique any more.
-    // (These & ~1u are actually redundant.  that.getGenerationID() just did it for us.)
-    this->fTaggedGenID.store(genID & ~1u);
-    that. fTaggedGenID.store(genID & ~1u);
-
-    // This method isn't threadsafe, so these asserts should be fine.
-    SkASSERT(!this->genIDIsUnique());
-    SkASSERT(!that. genIDIsUnique());
-}
-
-static void validate_pixels_ctable(const SkImageInfo& info, const SkColorTable* ctable) {
-    if (info.isEmpty()) {
-        return; // can't require ctable if the dimensions are empty
-    }
-    if (kIndex_8_SkColorType == info.colorType()) {
-        SkASSERT(ctable);
-    } else {
-        SkASSERT(nullptr == ctable);
-    }
-}
-
-void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) {
-    SkASSERT(pixels);
-    validate_pixels_ctable(fInfo, ctable);
-    // only call me in your constructor, otherwise fLockCount tracking can get
-    // out of sync.
-    fRec.fPixels = pixels;
-    fRec.fColorTable = ctable;
-    fRec.fRowBytes = rowBytes;
-    fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
-    fPreLocked = true;
-}
-
-// Increments fLockCount only on success
-bool SkPixelRef::lockPixelsInsideMutex() {
-    fMutex.assertHeld();
-
-    if (1 == ++fLockCount) {
-        SkASSERT(fRec.isZero());
-        if (!this->onNewLockPixels(&fRec)) {
-            fRec.zero();
-            fLockCount -= 1;    // we return fLockCount unchanged if we fail.
-            return false;
-        }
-    }
-    if (fRec.fPixels) {
-        validate_pixels_ctable(fInfo, fRec.fColorTable);
-        return true;
-    }
-    // no pixels, so we failed (somehow)
-    --fLockCount;
-    return false;
-}
-
-// For historical reasons, we always inc fLockCount, even if we return false.
-// It would be nice to change this (it seems), and only inc if we actually succeed...
-bool SkPixelRef::lockPixels() {
-    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
-
-    if (!fPreLocked) {
-        TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex");
-        SkAutoMutexAcquire  ac(fMutex);
-        TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex");
-        SkDEBUGCODE(int oldCount = fLockCount;)
-        bool success = this->lockPixelsInsideMutex();
-        // lockPixelsInsideMutex only increments the count if it succeeds.
-        SkASSERT(oldCount + (int)success == fLockCount);
-
-        if (!success) {
-            // For compatibility with SkBitmap calling lockPixels, we still want to increment
-            // fLockCount even if we failed. If we updated SkBitmap we could remove this oddity.
-            fLockCount += 1;
-            return false;
-        }
-    }
-    if (fRec.fPixels) {
-        validate_pixels_ctable(fInfo, fRec.fColorTable);
-        return true;
-    }
-    return false;
-}
-
-bool SkPixelRef::lockPixels(LockRec* rec) {
-    if (this->lockPixels()) {
-        *rec = fRec;
-        return true;
-    }
-    return false;
-}
-
-void SkPixelRef::unlockPixels() {
-    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
-
-    if (!fPreLocked) {
-        SkAutoMutexAcquire  ac(fMutex);
-
-        SkASSERT(fLockCount > 0);
-        if (0 == --fLockCount) {
-            // don't call onUnlockPixels unless onLockPixels succeeded
-            if (fRec.fPixels) {
-                this->onUnlockPixels();
-                fRec.zero();
-            } else {
-                SkASSERT(fRec.isZero());
-            }
-        }
-    }
-}
-
-bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) {
-    SkASSERT(result);
-    if (request.fSize.isEmpty()) {
-        return false;
-    }
-    // until we support subsets, we have to check this...
-    if (request.fSize.width() != fInfo.width() || request.fSize.height() != fInfo.height()) {
-        return false;
-    }
-
-    if (fPreLocked) {
-        result->fUnlockProc = nullptr;
-        result->fUnlockContext = nullptr;
-        result->fCTable = fRec.fColorTable;
-        result->fPixels = fRec.fPixels;
-        result->fRowBytes = fRec.fRowBytes;
-        result->fSize.set(fInfo.width(), fInfo.height());
-    } else {
-        SkAutoMutexAcquire  ac(fMutex);
-        if (!this->onRequestLock(request, result)) {
-            return false;
-        }
-    }
-    if (result->fPixels) {
-        validate_pixels_ctable(fInfo, result->fCTable);
-        return true;
-    }
-    return false;
-}
-
-bool SkPixelRef::lockPixelsAreWritable() const {
-    return this->onLockPixelsAreWritable();
-}
-
-bool SkPixelRef::onLockPixelsAreWritable() const {
-    return true;
-}
-
 uint32_t SkPixelRef::getGenerationID() const {
     uint32_t id = fTaggedGenID.load();
     if (0 == id) {
@@ -278,10 +132,6 @@
     this->onNotifyPixelsChanged();
 }
 
-void SkPixelRef::changeAlphaType(SkAlphaType at) {
-    *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeAlphaType(at);
-}
-
 void SkPixelRef::setImmutable() {
     fMutability = kImmutable;
 }
@@ -307,31 +157,4 @@
     fMutability = kMutable;
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-
 void SkPixelRef::onNotifyPixelsChanged() { }
-
-size_t SkPixelRef::getAllocatedSizeInBytes() const {
-    return 0;
-}
-
-static void unlock_legacy_result(void* ctx) {
-    SkPixelRef* pr = (SkPixelRef*)ctx;
-    pr->unlockPixels();
-    pr->unref();    // balancing the Ref in onRequestLoc
-}
-
-bool SkPixelRef::onRequestLock(const LockRequest& request, LockResult* result) {
-    if (!this->lockPixelsInsideMutex()) {
-        return false;
-    }
-
-    result->fUnlockProc = unlock_legacy_result;
-    result->fUnlockContext = SkRef(this);   // this is balanced in our fUnlockProc
-    result->fCTable = fRec.fColorTable;
-    result->fPixels = fRec.fPixels;
-    result->fRowBytes = fRec.fRowBytes;
-    result->fSize.set(fInfo.width(), fInfo.height());
-    return true;
-}
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 7eac6c4..6425b29 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -20,16 +20,6 @@
 #include "SkSurface.h"
 #include "SkUtils.h"
 
-void SkAutoPixmapUnlock::reset(const SkPixmap& pm, void (*unlock)(void*), void* ctx) {
-    SkASSERT(pm.addr() != nullptr);
-
-    this->unlock();
-    fPixmap = pm;
-    fUnlockProc = unlock;
-    fUnlockContext = ctx;
-    fIsLocked = true;
-}
-
 /////////////////////////////////////////////////////////////////////////////////////////////////
 
 void SkPixmap::reset() {
@@ -84,8 +74,8 @@
     return true;
 }
 
-bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int x, int y)
-const {
+bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int x, int y,
+                          SkTransferFunctionBehavior behavior) const {
     if (!SkImageInfoValidConversion(dstInfo, fInfo)) {
         return false;
     }
@@ -98,7 +88,7 @@
     const void* srcPixels = this->addr(rec.fX, rec.fY);
     const SkImageInfo srcInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
     SkConvertPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, srcInfo, srcPixels, this->rowBytes(),
-                    this->ctable(), SkTransferFunctionBehavior::kRespect);
+                    this->ctable(), behavior);
     return true;
 }
 
diff --git a/src/core/SkRRect.cpp b/src/core/SkRRect.cpp
index 2ed8bbf..8d69f4a 100644
--- a/src/core/SkRRect.cpp
+++ b/src/core/SkRRect.cpp
@@ -251,11 +251,11 @@
     return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY);
 }
 
-bool SkRRect::allCornersCircular() const {
-    return fRadii[0].fX == fRadii[0].fY &&
-        fRadii[1].fX == fRadii[1].fY &&
-        fRadii[2].fX == fRadii[2].fY &&
-        fRadii[3].fX == fRadii[3].fY;
+bool SkRRect::allCornersCircular(SkScalar tolerance) const {
+    return SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY, tolerance) &&
+           SkScalarNearlyEqual(fRadii[1].fX, fRadii[1].fY, tolerance) &&
+           SkScalarNearlyEqual(fRadii[2].fX, fRadii[2].fY, tolerance) &&
+           SkScalarNearlyEqual(fRadii[3].fX, fRadii[3].fY, tolerance);
 }
 
 bool SkRRect::contains(const SkRect& rect) const {
@@ -456,10 +456,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 size_t SkRRect::writeToMemory(void* buffer) const {
-    SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
-
-    memcpy(buffer, &fRect, sizeof(SkRect));
-    memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
+    // Serialize only the rect and corners, but not the derived type tag.
+    memcpy(buffer, this, kSizeInMemory);
     return kSizeInMemory;
 }
 
@@ -468,14 +466,10 @@
         return 0;
     }
 
-    SkScalar storage[12];
-    SkASSERT(sizeof(storage) == kSizeInMemory);
+    // Deserialize rect and corners, then rederive the type tag.
+    memcpy(this, buffer, kSizeInMemory);
+    this->computeType();
 
-    // we make a local copy, to ensure alignment before we cast
-    memcpy(storage, buffer, kSizeInMemory);
-
-    this->setRectRadii(*(const SkRect*)&storage[0],
-                       (const SkVector*)&storage[4]);
     return kSizeInMemory;
 }
 
diff --git a/src/core/SkRTree.cpp b/src/core/SkRTree.cpp
index bae2fdc..4d1787e 100644
--- a/src/core/SkRTree.cpp
+++ b/src/core/SkRTree.cpp
@@ -7,7 +7,8 @@
 
 #include "SkRTree.h"
 
-SkRTree::SkRTree(SkScalar aspectRatio) : fCount(0), fAspectRatio(aspectRatio) {}
+SkRTree::SkRTree(SkScalar aspectRatio)
+    : fCount(0), fAspectRatio(isfinite(aspectRatio) ? aspectRatio : 1) {}
 
 SkRect SkRTree::getRootBound() const {
     if (fCount) {
diff --git a/src/core/SkRWBuffer.cpp b/src/core/SkRWBuffer.cpp
index 7770b20..3ac9677 100644
--- a/src/core/SkRWBuffer.cpp
+++ b/src/core/SkRWBuffer.cpp
@@ -9,6 +9,7 @@
 
 #include "SkAtomics.h"
 #include "SkMalloc.h"
+#include "SkMakeUnique.h"
 #include "SkStream.h"
 
 // Force small chunks to be a page's worth
@@ -145,6 +146,10 @@
     this->reset(buffer);
 }
 
+SkROBuffer::Iter::Iter(const sk_sp<SkROBuffer>& buffer) {
+    this->reset(buffer.get());
+}
+
 void SkROBuffer::Iter::reset(const SkROBuffer* buffer) {
     fBuffer = buffer;
     if (buffer && buffer->fHead) {
@@ -240,10 +245,6 @@
 }
 #endif
 
-SkROBuffer* SkRWBuffer::newRBufferSnapshot() const {
-    return new SkROBuffer(fHead, fTotalUsed, fTail);
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class SkROBufferStreamAsset : public SkStreamAsset {
@@ -268,17 +269,15 @@
 #endif
 
 public:
-    SkROBufferStreamAsset(const SkROBuffer* buffer) : fBuffer(SkRef(buffer)), fIter(buffer) {
+    SkROBufferStreamAsset(sk_sp<SkROBuffer> buffer) : fBuffer(std::move(buffer)), fIter(fBuffer) {
         fGlobalOffset = fLocalOffset = 0;
     }
 
-    ~SkROBufferStreamAsset() override { fBuffer->unref(); }
-
     size_t getLength() const override { return fBuffer->size(); }
 
     bool rewind() override {
         AUTO_VALIDATE
-        fIter.reset(fBuffer);
+        fIter.reset(fBuffer.get());
         fGlobalOffset = fLocalOffset = 0;
         return true;
     }
@@ -350,13 +349,12 @@
 
 
 private:
-    const SkROBuffer*   fBuffer;
-    SkROBuffer::Iter    fIter;
-    size_t              fLocalOffset;
-    size_t              fGlobalOffset;
+    sk_sp<SkROBuffer> fBuffer;
+    SkROBuffer::Iter  fIter;
+    size_t            fLocalOffset;
+    size_t            fGlobalOffset;
 };
 
-SkStreamAsset* SkRWBuffer::newStreamSnapshot() const {
-    sk_sp<SkROBuffer> buffer(this->newRBufferSnapshot());
-    return new SkROBufferStreamAsset(buffer.get());
+std::unique_ptr<SkStreamAsset> SkRWBuffer::makeStreamSnapshot() const {
+    return skstd::make_unique<SkROBufferStreamAsset>(this->makeROBufferSnapshot());
 }
diff --git a/src/core/SkRadialShadowMapShader.cpp b/src/core/SkRadialShadowMapShader.cpp
deleted file mode 100644
index 45cc1d9..0000000
--- a/src/core/SkRadialShadowMapShader.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * 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 "SkLights.h"
-#include "SkPoint3.h"
-#include "SkRadialShadowMapShader.h"
-
-////////////////////////////////////////////////////////////////////////////
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-
-/** \class SkRadialShadowMapShaderImpl
-    This subclass of shader applies shadowing radially around a light
-*/
-class SkRadialShadowMapShaderImpl : public SkShader {
-public:
-    /** Create a new shadowing shader that shadows radially around a light
-    */
-    SkRadialShadowMapShaderImpl(sk_sp<SkShader> occluderShader,
-                                sk_sp<SkLights> lights,
-                                int diffuseWidth, int diffuseHeight)
-        : fOccluderShader(std::move(occluderShader))
-        , fLight(std::move(lights))
-        , fWidth(diffuseWidth)
-        , fHeight(diffuseHeight) { }
-
-    bool isOpaque() const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    class ShadowMapRadialShaderContext : public SkShader::Context {
-    public:
-        // The context takes ownership of the states. It will call their destructors
-        // but will NOT free the memory.
-        ShadowMapRadialShaderContext(const SkRadialShadowMapShaderImpl&, const ContextRec&,
-                                 SkShader::Context* occluderContext,
-                                 void* heapAllocated);
-
-        ~ShadowMapRadialShaderContext() override;
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
-        uint32_t getFlags() const override { return fFlags; }
-
-    private:
-        SkShader::Context*        fOccluderContext;
-        uint32_t                  fFlags;
-
-        void* fHeapAllocated;
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialShadowMapShaderImpl)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    size_t onContextSize(const ContextRec&) const override;
-    Context* onCreateContext(const ContextRec&, void*) const override;
-
-private:
-    sk_sp<SkShader> fOccluderShader;
-    sk_sp<SkLights> fLight;
-
-    int fWidth;
-    int fHeight;
-
-    friend class SkRadialShadowMapShader;
-
-    typedef SkShader INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "GrContext.h"
-#include "GrCoordTransform.h"
-#include "GrFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "SkGr.h"
-#include "SkImage_Base.h"
-#include "GrInvariantOutput.h"
-#include "SkSpecialImage.h"
-
-class RadialShadowMapFP : public GrFragmentProcessor {
-public:
-    RadialShadowMapFP(sk_sp<GrFragmentProcessor> occluder,
-                      sk_sp<SkLights> light,
-                      int diffuseWidth, int diffuseHeight,
-                      GrContext* context) {
-        fLightPos = light->light(0).pos();
-
-        fWidth = diffuseWidth;
-        fHeight = diffuseHeight;
-
-        this->registerChildProcessor(std::move(occluder));
-        this->initClassID<RadialShadowMapFP>();
-    }
-
-    class GLSLRadialShadowMapFP : public GrGLSLFragmentProcessor {
-    public:
-        GLSLRadialShadowMapFP() { }
-
-        void emitCode(EmitArgs& args) override {
-
-            GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
-            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-
-            const char* lightPosUniName = nullptr;
-
-            fLightPosUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                      kVec3f_GrSLType,
-                                                      kDefault_GrSLPrecision,
-                                                      "lightPos",
-                                                      &lightPosUniName);
-
-            const char* widthUniName = nullptr;
-            const char* heightUniName = nullptr;
-
-            fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kInt_GrSLType,
-                                                   kDefault_GrSLPrecision,
-                                                   "width", &widthUniName);
-            fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                    kInt_GrSLType,
-                                                    kDefault_GrSLPrecision,
-                                                    "height", &heightUniName);
-
-
-            SkString occluder("occluder");
-            this->emitChild(0, nullptr, &occluder, args);
-
-            // Modify the input texture coordinates to index into our 1D output
-            fragBuilder->codeAppend("float distHere;");
-
-            // we use a max shadow distance of 2 times the max of width/height
-            fragBuilder->codeAppend("float closestDistHere = 2;");
-            fragBuilder->codeAppend("vec2 coords = vMatrixCoord_0_0_Stage0;");
-            fragBuilder->codeAppend("coords.y = 0;");
-            fragBuilder->codeAppend("vec2 destCoords = vec2(0,0);");
-            fragBuilder->codeAppendf("float step = 1.0 / %s;", heightUniName);
-
-            // assume that we are at 0, 0 light pos
-            // TODO use correct light positions
-
-            // this goes through each depth value in the final output buffer,
-            // basically raycasting outwards, and finding the first collision.
-            // we also increment coords.y to 2 instead 1 so our shadows stretch the whole screen.
-            fragBuilder->codeAppendf("for (coords.y = 0; coords.y <= 2; coords.y += step) {");
-
-                fragBuilder->codeAppend("float theta = (coords.x * 2.0 - 1.0) * 3.1415;");
-                fragBuilder->codeAppend("float r = coords.y;");
-                fragBuilder->codeAppend("destCoords = "
-                        "vec2(r * cos(theta), - r * sin(theta)) /2.0 + 0.5;");
-                fragBuilder->codeAppendf("vec2 lightOffset = (vec2(%s)/vec2(%s,%s) - 0.5)"
-                                                            "* vec2(1.0, 1.0);",
-                                         lightPosUniName, widthUniName, heightUniName);
-
-                fragBuilder->codeAppend("distHere = texture(uTextureSampler0_Stage1,"
-                                                           "destCoords + lightOffset).b;");
-                fragBuilder->codeAppend("if (distHere > 0.0) {"
-                                            "closestDistHere = coords.y;"
-                                        "break;}");
-            fragBuilder->codeAppend("}");
-
-            fragBuilder->codeAppendf("%s = vec4(vec3(closestDistHere / 2.0),1);", args.fOutputColor);
-        }
-
-        static void GenKey(const GrProcessor& proc, const GrShaderCaps&,
-                           GrProcessorKeyBuilder* b) {
-            b->add32(0); // nothing to add here
-        }
-
-    protected:
-        void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
-            const RadialShadowMapFP &radialShadowMapFP = proc.cast<RadialShadowMapFP>();
-
-            const SkVector3& lightPos = radialShadowMapFP.lightPos();
-            if (lightPos != fLightPos) {
-                pdman.set3fv(fLightPosUni, 1, &lightPos.fX);
-                fLightPos = lightPos;
-            }
-
-            int width = radialShadowMapFP.width();
-            if (width != fWidth) {
-                pdman.set1i(fWidthUni, width);
-                fWidth = width;
-            }
-            int height = radialShadowMapFP.height();
-            if (height != fHeight) {
-                pdman.set1i(fHeightUni, height);
-                fHeight = height;
-            }
-        }
-
-    private:
-        SkVector3 fLightPos;
-        GrGLSLProgramDataManager::UniformHandle fLightPosUni;
-
-        int fWidth;
-        GrGLSLProgramDataManager::UniformHandle fWidthUni;
-        int fHeight;
-        GrGLSLProgramDataManager::UniformHandle fHeightUni;
-    };
-
-    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLRadialShadowMapFP::GenKey(*this, caps, b);
-    }
-
-    const char* name() const override { return "RadialShadowMapFP"; }
-
-    const SkVector3& lightPos() const {
-        return fLightPos;
-    }
-
-    int width() const { return fWidth; }
-    int height() const { return fHeight; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
-        return new GLSLRadialShadowMapFP;
-    }
-
-    bool onIsEqual(const GrFragmentProcessor& proc) const override {
-        const RadialShadowMapFP& radialShadowMapFP = proc.cast<RadialShadowMapFP>();
-
-        if (fWidth != radialShadowMapFP.fWidth || fHeight != radialShadowMapFP.fHeight) {
-            return false;
-        }
-
-        if (fLightPos != radialShadowMapFP.fLightPos) {
-            return false;
-        }
-
-        return true;
-    }
-
-    SkVector3        fLightPos;
-
-    int              fHeight;
-    int              fWidth;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkRadialShadowMapShaderImpl::asFragmentProcessor
-        (const AsFPArgs& fpargs) const {
-
-    sk_sp<GrFragmentProcessor> occluderFP = fOccluderShader->asFragmentProcessor(fpargs);
-
-    sk_sp<GrFragmentProcessor> shadowFP = sk_make_sp<RadialShadowMapFP>(std::move(occluderFP),
-                                                                        fLight, fWidth, fHeight,
-                                                                        fpargs.fContext);
-    return shadowFP;
-}
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////
-
-bool SkRadialShadowMapShaderImpl::isOpaque() const {
-    return fOccluderShader->isOpaque();
-}
-
-SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::ShadowMapRadialShaderContext(
-        const SkRadialShadowMapShaderImpl& shader, const ContextRec& rec,
-        SkShader::Context* occluderContext,
-        void* heapAllocated)
-        : INHERITED(shader, rec)
-        , fOccluderContext(occluderContext)
-        , fHeapAllocated(heapAllocated) {
-    bool isOpaque = shader.isOpaque();
-
-    // update fFlags
-    uint32_t flags = 0;
-    if (isOpaque && (255 == this->getPaintAlpha())) {
-        flags |= kOpaqueAlpha_Flag;
-    }
-
-    fFlags = flags;
-}
-
-SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::~ShadowMapRadialShaderContext() {
-    // The dependencies have been created outside of the context on memory that was allocated by
-    // the onCreateContext() method. Call the destructors and free the memory.
-    fOccluderContext->~Context();
-
-    sk_free(fHeapAllocated);
-}
-
-static inline SkPMColor convert(SkColor3f color, U8CPU a) {
-    if (color.fX <= 0.0f) {
-        color.fX = 0.0f;
-    } else if (color.fX >= 255.0f) {
-        color.fX = 255.0f;
-    }
-
-    if (color.fY <= 0.0f) {
-        color.fY = 0.0f;
-    } else if (color.fY >= 255.0f) {
-        color.fY = 255.0f;
-    }
-
-    if (color.fZ <= 0.0f) {
-        color.fZ = 0.0f;
-    } else if (color.fZ >= 255.0f) {
-        color.fZ = 255.0f;
-    }
-
-    return SkPreMultiplyARGB(a, (int) color.fX,  (int) color.fY, (int) color.fZ);
-}
-
-// larger is better (fewer times we have to loop), but we shouldn't
-// take up too much stack-space (each one here costs 16 bytes)
-#define BUFFER_MAX 16
-void SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::shadeSpan
-        (int x, int y, SkPMColor result[], int count) {
-    do {
-        int n = SkTMin(count, BUFFER_MAX);
-
-        // just fill with white for now
-        SkPMColor accum = convert(SkColor3f::Make(1.0f, 1.0f, 1.0f), 0xFF);
-
-        for (int i = 0; i < n; ++i) {
-            result[i] = accum;
-        }
-
-        result += n;
-        x += n;
-        count -= n;
-    } while (count > 0);
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-#ifndef SK_IGNORE_TO_STRING
-void SkRadialShadowMapShaderImpl::toString(SkString* str) const {
-    str->appendf("RadialShadowMapShader: ()");
-}
-#endif
-
-sk_sp<SkFlattenable> SkRadialShadowMapShaderImpl::CreateProc(SkReadBuffer& buf) {
-
-    // Discarding SkShader flattenable params
-    bool hasLocalMatrix = buf.readBool();
-    SkAssertResult(!hasLocalMatrix);
-
-    sk_sp<SkLights> light = SkLights::MakeFromBuffer(buf);
-
-    int diffuseWidth = buf.readInt();
-    int diffuseHeight = buf.readInt();
-
-    sk_sp<SkShader> occluderShader(buf.readFlattenable<SkShader>());
-
-    return sk_make_sp<SkRadialShadowMapShaderImpl>(std::move(occluderShader),
-                                                   std::move(light),
-                                                   diffuseWidth, diffuseHeight);
-}
-
-void SkRadialShadowMapShaderImpl::flatten(SkWriteBuffer& buf) const {
-    this->INHERITED::flatten(buf);
-
-    fLight->flatten(buf);
-
-    buf.writeInt(fWidth);
-    buf.writeInt(fHeight);
-
-    buf.writeFlattenable(fOccluderShader.get());
-}
-
-size_t SkRadialShadowMapShaderImpl::onContextSize(const ContextRec& rec) const {
-    return sizeof(ShadowMapRadialShaderContext);
-}
-
-SkShader::Context* SkRadialShadowMapShaderImpl::onCreateContext(const ContextRec& rec,
-                                                                void* storage) const {
-    size_t heapRequired = fOccluderShader->contextSize(rec);
-
-    void* heapAllocated = sk_malloc_throw(heapRequired);
-
-    void* occluderContextStorage = heapAllocated;
-
-    SkShader::Context* occluderContext =
-            fOccluderShader->createContext(rec, occluderContextStorage);
-
-    if (!occluderContext) {
-        sk_free(heapAllocated);
-        return nullptr;
-    }
-
-    return new (storage) ShadowMapRadialShaderContext(*this, rec, occluderContext, heapAllocated);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkShader> SkRadialShadowMapShader::Make(sk_sp<SkShader> occluderShader,
-                                              sk_sp<SkLights> light,
-                                              int diffuseWidth, int diffuseHeight) {
-    if (!occluderShader) {
-        // TODO: Use paint's color in absence of a diffuseShader
-        // TODO: Use a default implementation of normalSource instead
-        return nullptr;
-    }
-
-    return sk_make_sp<SkRadialShadowMapShaderImpl>(std::move(occluderShader),
-                                                   std::move(light),
-                                                   diffuseWidth, diffuseHeight);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRadialShadowMapShader)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialShadowMapShaderImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-///////////////////////////////////////////////////////////////////////////////
-
-#endif
diff --git a/src/core/SkRadialShadowMapShader.h b/src/core/SkRadialShadowMapShader.h
deleted file mode 100644
index 4d6956c..0000000
--- a/src/core/SkRadialShadowMapShader.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 "SkReadBuffer.h"
-
-#ifndef SkRadialShadowMapShader_DEFINED
-#define SkRadialShadowMapShader_DEFINED
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-class SkLights;
-class SkShader;
-
-class SK_API SkRadialShadowMapShader {
-public:
-    /** This shader creates a 1D strip depth map for radial lights.
-     *  It can only take in 1 light to generate one shader at a time.
-     */
-    static sk_sp<SkShader> Make(sk_sp<SkShader> occluderShader,
-                                sk_sp<SkLights> light,
-                                int diffuseWidth, int diffuseHeight);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-};
-
-#endif
-#endif
diff --git a/src/core/SkRasterPipeline.cpp b/src/core/SkRasterPipeline.cpp
index 42f0212..dc25e5a 100644
--- a/src/core/SkRasterPipeline.cpp
+++ b/src/core/SkRasterPipeline.cpp
@@ -5,37 +5,53 @@
  * found in the LICENSE file.
  */
 
-#include "SkOpts.h"
 #include "SkRasterPipeline.h"
 
-SkRasterPipeline::SkRasterPipeline() {}
+SkRasterPipeline::SkRasterPipeline(SkArenaAlloc* alloc) : fAlloc(alloc) {
+    this->reset();
+}
+void SkRasterPipeline::reset() {
+    fStages      = nullptr;
+    fNumStages   = 0;
+    fSlotsNeeded = 1;  // We always need one extra slot for just_return().
+}
 
 void SkRasterPipeline::append(StockStage stage, void* ctx) {
     SkASSERT(stage != from_srgb);
-    fStages.push_back({stage, ctx});
+    this->unchecked_append(stage, ctx);
+}
+void SkRasterPipeline::unchecked_append(StockStage stage, void* ctx) {
+    fStages = fAlloc->make<StageList>( StageList{fStages, stage, ctx} );
+    fNumStages   += 1;
+    fSlotsNeeded += ctx ? 2 : 1;
 }
 
 void SkRasterPipeline::extend(const SkRasterPipeline& src) {
-    fStages.insert(fStages.end(),
-                   src.fStages.begin(), src.fStages.end());
-}
-
-void SkRasterPipeline::run(size_t x, size_t n) const {
-    if (!fStages.empty()) {
-    #if defined(SK_JUMPER)
-        if (this->run_with_jumper(x, n)) {
-            return;
-        }
-    #endif
-        SkOpts::run_pipeline(x,n, fStages.data(), SkToInt(fStages.size()));
+    if (src.empty()) {
+        return;
     }
+    auto stages = fAlloc->makeArrayDefault<StageList>(src.fNumStages);
+
+    int n = src.fNumStages;
+    const StageList* st = src.fStages;
+    while (n --> 1) {
+        stages[n]      = *st;
+        stages[n].prev = &stages[n-1];
+        st = st->prev;
+    }
+    stages[0]      = *st;
+    stages[0].prev = fStages;
+
+    fStages = &stages[src.fNumStages - 1];
+    fNumStages   += src.fNumStages;
+    fSlotsNeeded += src.fSlotsNeeded - 1;  // Don't double count just_returns().
 }
 
 void SkRasterPipeline::dump() const {
-    SkDebugf("SkRasterPipeline, %d stages\n", SkToInt(fStages.size()));
-    for (auto&& st : fStages) {
+    SkDebugf("SkRasterPipeline, %d stages (in reverse)\n", fNumStages);
+    for (auto st = fStages; st; st = st->prev) {
         const char* name = "";
-        switch (st.stage) {
+        switch (st->stage) {
         #define M(x) case x: name = #x; break;
             SK_RASTER_PIPELINE_STAGES(M)
         #undef M
@@ -54,9 +70,7 @@
 // This is an annoying problem with no known good solution.  So apply the clamp hammer.
 
 void SkRasterPipeline::append_from_srgb(SkAlphaType at) {
-    //this->append(from_srgb);
-    fStages.push_back({from_srgb, nullptr});
-
+    this->unchecked_append(from_srgb, nullptr);
     if (at == kPremul_SkAlphaType) {
         this->append(SkRasterPipeline::clamp_a);
     }
diff --git a/src/core/SkRasterPipeline.h b/src/core/SkRasterPipeline.h
index 01bd683..bd54e3f 100644
--- a/src/core/SkRasterPipeline.h
+++ b/src/core/SkRasterPipeline.h
@@ -8,12 +8,16 @@
 #ifndef SkRasterPipeline_DEFINED
 #define SkRasterPipeline_DEFINED
 
+#include "SkArenaAlloc.h"
 #include "SkImageInfo.h"
 #include "SkNx.h"
 #include "SkTArray.h"
 #include "SkTypes.h"
+#include <functional>
 #include <vector>
 
+struct SkJumper_constants;
+
 /**
  * SkRasterPipeline provides a cheap way to chain together a pixel processing pipeline.
  *
@@ -56,14 +60,13 @@
 // the Stage*.  This mostly matters on 64-bit Windows where every register is precious.
 
 #define SK_RASTER_PIPELINE_STAGES(M)                             \
-    M(trace) M(registers)                                        \
+    M(callback)                                                  \
     M(move_src_dst) M(move_dst_src) M(swap)                      \
     M(clamp_0) M(clamp_1) M(clamp_a)                             \
     M(unpremul) M(premul)                                        \
     M(set_rgb) M(swap_rb)                                        \
     M(from_srgb) M(to_srgb)                                      \
-    M(from_2dot2) M(to_2dot2)                                    \
-    M(constant_color) M(seed_shader)                             \
+    M(constant_color) M(seed_shader) M(dither)                   \
     M(load_a8)   M(store_a8)                                     \
     M(load_g8)                                                   \
     M(load_565)  M(store_565)                                    \
@@ -73,7 +76,7 @@
     M(load_8888) M(store_8888)                                   \
     M(load_u16_be) M(load_rgb_u16_be) M(store_u16_be)            \
     M(load_tables_u16_be) M(load_tables_rgb_u16_be)              \
-    M(load_tables)                                               \
+    M(load_tables) M(load_rgba) M(store_rgba)                    \
     M(scale_u8) M(scale_1_float)                                 \
     M(lerp_u8) M(lerp_565) M(lerp_1_float)                       \
     M(dstatop) M(dstin) M(dstout) M(dstover)                     \
@@ -81,30 +84,44 @@
     M(clear) M(modulate) M(multiply) M(plus_) M(screen) M(xor_)  \
     M(colorburn) M(colordodge) M(darken) M(difference)           \
     M(exclusion) M(hardlight) M(lighten) M(overlay) M(softlight) \
+    M(hue) M(saturation) M(color) M(luminosity)                  \
+    M(srcover_rgba_8888)                                         \
     M(luminance_to_alpha)                                        \
-    M(matrix_2x3) M(matrix_3x4) M(matrix_4x5)                    \
+    M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) M(matrix_4x3)      \
     M(matrix_perspective)                                        \
     M(parametric_r) M(parametric_g) M(parametric_b)              \
     M(parametric_a)                                              \
     M(table_r) M(table_g) M(table_b) M(table_a)                  \
-    M(color_lookup_table) M(lab_to_xyz)                          \
-    M(clamp_x) M(mirror_x) M(repeat_x)                           \
-    M(clamp_y) M(mirror_y) M(repeat_y)                           \
+    M(lab_to_xyz)                                                \
+    M(clamp_x)   M(mirror_x)   M(repeat_x)                       \
+    M(clamp_y)   M(mirror_y)   M(repeat_y)                       \
+    M(clamp_x_1) M(mirror_x_1) M(repeat_x_1)                     \
     M(gather_a8) M(gather_g8) M(gather_i8)                       \
     M(gather_565) M(gather_4444) M(gather_8888) M(gather_f16)    \
     M(bilinear_nx) M(bilinear_px) M(bilinear_ny) M(bilinear_py)  \
     M(bicubic_n3x) M(bicubic_n1x) M(bicubic_p1x) M(bicubic_p3x)  \
     M(bicubic_n3y) M(bicubic_n1y) M(bicubic_p1y) M(bicubic_p3y)  \
     M(save_xy) M(accumulate)                                     \
-    M(linear_gradient_2stops)                                    \
+    M(evenly_spaced_gradient)                                    \
+    M(gauss_a_to_rgba) M(gradient)                               \
+    M(evenly_spaced_2_stop_gradient)                             \
+    M(xy_to_unit_angle)                                          \
+    M(xy_to_radius)                                              \
     M(byte_tables) M(byte_tables_rgb)                            \
-    M(shader_adapter)                                            \
     M(rgb_to_hsl)                                                \
     M(hsl_to_rgb)
 
 class SkRasterPipeline {
 public:
-    SkRasterPipeline();
+    explicit SkRasterPipeline(SkArenaAlloc*);
+
+    SkRasterPipeline(const SkRasterPipeline&) = delete;
+    SkRasterPipeline(SkRasterPipeline&&)      = default;
+
+    SkRasterPipeline& operator=(const SkRasterPipeline&) = delete;
+    SkRasterPipeline& operator=(SkRasterPipeline&&)      = default;
+
+    void reset();
 
     enum StockStage {
     #define M(stage) stage,
@@ -118,25 +135,46 @@
     void extend(const SkRasterPipeline&);
 
     // Runs the pipeline walking x through [x,x+n).
-    void run(size_t x, size_t n) const;
+    void run(size_t x, size_t y, size_t n) const;
+
+    // Allocates a thunk which amortizes run() setup cost in alloc.
+    std::function<void(size_t, size_t, size_t)> compile() const;
 
     void dump() const;
 
-    struct Stage {
-        StockStage stage;
-        void*        ctx;
-    };
-
     // Conversion from sRGB can be subtly tricky when premultiplication is involved.
     // Use these helpers to keep things sane.
     void append_from_srgb(SkAlphaType);
 
-    bool empty() const { return fStages.empty(); }
+    bool empty() const { return fStages == nullptr; }
 
 private:
-    bool run_with_jumper(size_t x, size_t n) const;
+    using StartPipelineFn = void(size_t,size_t,size_t,void**,const SkJumper_constants*);
 
-    std::vector<Stage> fStages;
+    struct StageList {
+        StageList* prev;
+        StockStage stage;
+        void*      ctx;
+    };
+
+    StartPipelineFn* build_pipeline(void**) const;
+    void unchecked_append(StockStage, void*);
+
+    SkArenaAlloc* fAlloc;
+    StageList*    fStages;
+    int           fNumStages;
+    int           fSlotsNeeded;
 };
 
+template <size_t bytes>
+class SkRasterPipeline_ : public SkRasterPipeline {
+public:
+    SkRasterPipeline_()
+        : SkRasterPipeline(&fBuiltinAlloc) {}
+
+private:
+    SkSTArenaAlloc<bytes> fBuiltinAlloc;
+};
+
+
 #endif//SkRasterPipeline_DEFINED
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index a926b43..3502174 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -10,28 +10,39 @@
 #include "SkBlendModePriv.h"
 #include "SkColor.h"
 #include "SkColorFilter.h"
+#include "SkColorSpaceXformer.h"
 #include "SkOpts.h"
 #include "SkPM4f.h"
 #include "SkPM4fPriv.h"
 #include "SkRasterPipeline.h"
 #include "SkShader.h"
+#include "SkShaderBase.h"
 #include "SkUtils.h"
+#include "../jumper/SkJumper.h"
 
-
-class SkRasterPipelineBlitter : public SkBlitter {
+class SkRasterPipelineBlitter final : public SkBlitter {
 public:
-    static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
-                             SkArenaAlloc*);
+    // This is our common entrypoint for creating the blitter once we've sorted out shaders.
+    static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*,
+                             const SkRasterPipeline& shaderPipeline,
+                             SkShaderBase::Context*,
+                             bool is_opaque, bool is_constant, bool wants_dither);
 
-    SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
+    SkRasterPipelineBlitter(SkPixmap dst,
+                            SkBlendMode blend,
+                            SkArenaAlloc* alloc,
+                            SkShaderBase::Context* burstCtx)
         : fDst(dst)
         , fBlend(blend)
-        , fPaintColor(paintColor)
+        , fAlloc(alloc)
+        , fBurstCtx(burstCtx)
+        , fColorPipeline(alloc)
     {}
 
-    void blitH    (int x, int y, int w)                            override;
-    void blitAntiH(int x, int y, const SkAlpha[], const int16_t[]) override;
-    void blitMask (const SkMask&, const SkIRect& clip)             override;
+    void blitH     (int x, int y, int w)                            override;
+    void blitAntiH (int x, int y, const SkAlpha[], const int16_t[]) override;
+    void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1)               override;
+    void blitMask  (const SkMask&, const SkIRect& clip)             override;
 
     // TODO: The default implementations of the other blits look fine,
     // but some of them like blitV could probably benefit from custom
@@ -43,27 +54,34 @@
     void maybe_clamp  (SkRasterPipeline*) const;
     void append_store (SkRasterPipeline*) const;
 
-    SkPixmap         fDst;
-    SkBlendMode      fBlend;
-    SkPM4f           fPaintColor;
-    SkRasterPipeline fShader;
+    // If we have an burst context, use it to fill our shader buffer.
+    void maybe_shade(int x, int y, int w);
+
+    SkPixmap               fDst;
+    SkBlendMode            fBlend;
+    SkArenaAlloc*          fAlloc;
+    SkShaderBase::Context* fBurstCtx;
+    SkRasterPipeline       fColorPipeline;
 
     // We may be able to specialize blitH() into a memset.
     bool     fCanMemsetInBlitH = false;
     uint64_t fMemsetColor      = 0;     // Big enough for largest dst format, F16.
 
     // Built lazily on first use.
-    SkRasterPipeline fBlitH,
-                     fBlitAntiH,
-                     fBlitMaskA8,
-                     fBlitMaskLCD16;
+    std::function<void(size_t, size_t, size_t)> fBlitH,
+                                                fBlitAntiH,
+                                                fBlitMaskA8,
+                                                fBlitMaskLCD16;
 
     // These values are pointed to by the blit pipelines above,
     // which allows us to adjust them from call to call.
-    void*       fDstPtr          = nullptr;
-    const void* fMaskPtr         = nullptr;
-    float       fCurrentCoverage = 0.0f;
-    int         fCurrentY        = 0;
+    void*              fShaderOutput    = nullptr;
+    void*              fDstPtr          = nullptr;
+    const void*        fMaskPtr         = nullptr;
+    float              fCurrentCoverage = 0.0f;
+    float              fDitherRate      = 0.0f;
+
+    std::vector<SkPM4f> fShaderBuffer;
 
     typedef SkBlitter INHERITED;
 };
@@ -72,86 +90,134 @@
                                          const SkPaint& paint,
                                          const SkMatrix& ctm,
                                          SkArenaAlloc* alloc) {
-    return SkRasterPipelineBlitter::Create(dst, paint, ctm, alloc);
+    SkColorSpace* dstCS = dst.colorSpace();
+    auto paintColor = alloc->make<SkPM4f>(SkPM4f_from_SkColor(paint.getColor(), dstCS));
+    auto shader = as_SB(paint.getShader());
+    bool wants_dither = paint.isDither();
+
+#ifdef SK_SUPPORT_LEGACY_RASTERPIPELINE
+    wants_dither = shader && shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
+#endif
+
+    SkRasterPipeline_<256> shaderPipeline;
+    if (!shader) {
+        // Having no shader makes things nice and easy... just use the paint color.
+        shaderPipeline.append(SkRasterPipeline::constant_color, paintColor);
+        bool is_opaque    = paintColor->a() == 1.0f,
+             is_constant  = true;
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc,
+                                               shaderPipeline, nullptr,
+                                               is_opaque, is_constant, wants_dither);
+    }
+
+    bool is_opaque    = shader->isOpaque() && paintColor->a() == 1.0f;
+    bool is_constant  = shader->isConstant();
+
+    // Check whether the shader prefers to run in burst mode.
+    if (auto* burstCtx = shader->makeBurstPipelineContext(
+        SkShaderBase::ContextRec(paint, ctm, nullptr, SkShaderBase::ContextRec::kPM4f_DstType,
+                                 dstCS), alloc)) {
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc,
+                                               shaderPipeline, burstCtx,
+                                               is_opaque, is_constant, wants_dither);
+    }
+
+    if (shader->appendStages(&shaderPipeline, dstCS, alloc, ctm, paint)) {
+        if (paintColor->a() != 1.0f) {
+            shaderPipeline.append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
+        }
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, nullptr,
+                                               is_opaque, is_constant, wants_dither);
+    }
+
+    // The shader has opted out of drawing anything.
+    return alloc->make<SkNullBlitter>();
 }
 
-static bool supported(const SkImageInfo& info) {
-    switch (info.colorType()) {
-        case kAlpha_8_SkColorType:  return true;
-        case kRGB_565_SkColorType:  return true;
-        case kN32_SkColorType:      return info.gammaCloseToSRGB();
-        case kRGBA_F16_SkColorType: return true;
-        default:                    return false;
-    }
+SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
+                                         const SkPaint& paint,
+                                         const SkRasterPipeline& shaderPipeline,
+                                         bool is_opaque,
+                                         bool wants_dither,
+                                         SkArenaAlloc* alloc) {
+    bool is_constant = false;  // If this were the case, it'd be better to just set a paint color.
+    return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, nullptr,
+                                           is_opaque, is_constant, wants_dither);
 }
 
 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
                                            const SkPaint& paint,
-                                           const SkMatrix& ctm,
-                                           SkArenaAlloc* alloc) {
-    auto blitter = alloc->make<SkRasterPipelineBlitter>(
-            dst,
-            paint.getBlendMode(),
-            SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
+                                           SkArenaAlloc* alloc,
+                                           const SkRasterPipeline& shaderPipeline,
+                                           SkShaderBase::Context* burstCtx,
+                                           bool is_opaque,
+                                           bool is_constant,
+                                           bool wants_dither) {
+    auto blitter = alloc->make<SkRasterPipelineBlitter>(dst,
+                                                        paint.getBlendMode(),
+                                                        alloc,
+                                                        burstCtx);
 
+    // Our job in this factory is to fill out the blitter's color pipeline.
+    // This is the common front of the full blit pipelines, each constructed lazily on first use.
+    // The full blit pipelines handle reading and writing the dst, blending, coverage, dithering.
+    auto colorPipeline = &blitter->fColorPipeline;
 
-    SkBlendMode*      blend       = &blitter->fBlend;
-    SkPM4f*           paintColor  = &blitter->fPaintColor;
-    SkRasterPipeline* pipeline    = &blitter->fShader;
-
-    SkShader*      shader      = paint.getShader();
-    SkColorFilter* colorFilter = paint.getColorFilter();
-
-    // TODO: all temporary
-    if (!supported(dst.info()) || !SkBlendMode_AppendStages(*blend)) {
-        return nullptr;
-    }
-
-    bool is_opaque   = paintColor->a() == 1.0f,
-         is_constant = true;
-    if (shader) {
-        pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
-        if (!shader->appendStages(pipeline, dst.colorSpace(), alloc, ctm, paint)) {
-            return nullptr;
-        }
-        if (!is_opaque) {
-            pipeline->append(SkRasterPipeline::scale_1_float,
-                             &paintColor->fVec[SkPM4f::A]);
-        }
-
-        is_opaque   = is_opaque && shader->isOpaque();
-        is_constant = shader->isConstant();
+    // Let's get the shader in first.
+    if (burstCtx) {
+        colorPipeline->append(SkRasterPipeline::load_f32, &blitter->fShaderOutput);
     } else {
-        pipeline->append(SkRasterPipeline::constant_color, paintColor);
+        colorPipeline->extend(shaderPipeline);
     }
 
-    if (colorFilter) {
-        if (!colorFilter->appendStages(pipeline, dst.colorSpace(), alloc, is_opaque)) {
-            return nullptr;
-        }
+    // If there's a color filter it comes next.
+    if (auto colorFilter = paint.getColorFilter()) {
+        colorFilter->appendStages(colorPipeline, dst.colorSpace(), alloc, is_opaque);
         is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
     }
 
+    // We'll dither if the shader wants to, or if we're drawing 565 and the paint wants to.
+    // Not all formats make sense to dither (think, F16).  We set their dither rate to zero.
+    // We need to decide if we're going to dither now to keep is_constant accurate.
+    if (wants_dither ||
+            (paint.isDither() && dst.info().colorType() == kRGB_565_SkColorType)) {
+        switch (dst.info().colorType()) {
+            default:                     blitter->fDitherRate =     0.0f; break;
+            case   kRGB_565_SkColorType: blitter->fDitherRate =  1/63.0f; break;
+            case kRGBA_8888_SkColorType:
+            case kBGRA_8888_SkColorType: blitter->fDitherRate = 1/255.0f; break;
+        }
+    }
+    is_constant = is_constant && (blitter->fDitherRate == 0.0f);
+
+    // We're logically done here.  The code between here and return blitter is all optimization.
+
+    // A pipeline that's still constant here can collapse back into a constant color.
     if (is_constant) {
-        pipeline->append(SkRasterPipeline::store_f32, &paintColor);
-        pipeline->run(0,1);
+        auto constantColor = alloc->make<SkPM4f>();
+        colorPipeline->append(SkRasterPipeline::store_f32, &constantColor);
+        colorPipeline->run(0,0,1);
+        colorPipeline->reset();
+        colorPipeline->append(SkRasterPipeline::constant_color, constantColor);
 
-        *pipeline = SkRasterPipeline();
-        pipeline->append(SkRasterPipeline::constant_color, paintColor);
-
-        is_opaque = paintColor->a() == 1.0f;
+        is_opaque = constantColor->a() == 1.0f;
     }
 
-    if (is_opaque && *blend == SkBlendMode::kSrcOver) {
-        *blend = SkBlendMode::kSrc;
+    // We can strength-reduce SrcOver into Src when opaque.
+    if (is_opaque && blitter->fBlend == SkBlendMode::kSrcOver) {
+        blitter->fBlend = SkBlendMode::kSrc;
     }
 
-    if (is_constant && *blend == SkBlendMode::kSrc) {
-        SkRasterPipeline p;
-        p.extend(*pipeline);
+    // When we're drawing a constant color in Src mode, we can sometimes just memset.
+    // (The previous two optimizations help find more opportunities for this one.)
+    if (is_constant && blitter->fBlend == SkBlendMode::kSrc) {
+        // Run our color pipeline all the way through to produce what we'd memset when we can.
+        // Not all blits can memset, so we need to keep colorPipeline too.
+        SkRasterPipeline_<256> p;
+        p.extend(*colorPipeline);
         blitter->fDstPtr = &blitter->fMemsetColor;
         blitter->append_store(&p);
-        p.run(0,1);
+        p.run(0,0,1);
 
         blitter->fCanMemsetInBlitH = true;
     }
@@ -160,8 +226,6 @@
 }
 
 void SkRasterPipelineBlitter::append_load_d(SkRasterPipeline* p) const {
-    SkASSERT(supported(fDst.info()));
-
     p->append(SkRasterPipeline::move_src_dst);
     switch (fDst.info().colorType()) {
         case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::load_a8,   &fDstPtr); break;
@@ -184,11 +248,15 @@
     if (fDst.info().gammaCloseToSRGB()) {
         p->append(SkRasterPipeline::to_srgb);
     }
+    if (fDitherRate > 0.0f) {
+        // We dither after any sRGB transfer function to make sure our 1/255.0f is sensible
+        // over the whole range.  If we did it before, 1/255.0f is too big a rate near zero.
+        p->append(SkRasterPipeline::dither, &fDitherRate);
+    }
+
     if (fDst.info().colorType() == kBGRA_8888_SkColorType) {
         p->append(SkRasterPipeline::swap_rb);
     }
-
-    SkASSERT(supported(fDst.info()));
     switch (fDst.info().colorType()) {
         case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::store_a8,   &fDstPtr); break;
         case kRGB_565_SkColorType:   p->append(SkRasterPipeline::store_565,  &fDstPtr); break;
@@ -200,7 +268,7 @@
 }
 
 void SkRasterPipelineBlitter::append_blend(SkRasterPipeline* p) const {
-    SkAssertResult(SkBlendMode_AppendStages(fBlend, p));
+    SkBlendMode_AppendStages(fBlend, p);
 }
 
 void SkRasterPipelineBlitter::maybe_clamp(SkRasterPipeline* p) const {
@@ -209,9 +277,19 @@
     }
 }
 
+void SkRasterPipelineBlitter::maybe_shade(int x, int y, int w) {
+    if (fBurstCtx) {
+        if (w > SkToInt(fShaderBuffer.size())) {
+            fShaderBuffer.resize(w);
+        }
+        fBurstCtx->shadeSpan4f(x,y, fShaderBuffer.data(), w);
+        // We'll be reading from fShaderOutput + x.
+        fShaderOutput = fShaderBuffer.data() - x;
+    }
+}
+
 void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
     fDstPtr = fDst.writable_addr(0,y);
-    fCurrentY = y;
 
     if (fCanMemsetInBlitH) {
         switch (fDst.shiftPerPixel()) {
@@ -223,23 +301,32 @@
         }
     }
 
-    auto& p = fBlitH;
-    if (p.empty()) {
-        p.extend(fShader);
-        if (fBlend != SkBlendMode::kSrc) {
-            this->append_load_d(&p);
-            this->append_blend(&p);
-            this->maybe_clamp(&p);
+    if (!fBlitH) {
+        SkRasterPipeline p(fAlloc);
+        p.extend(fColorPipeline);
+        if (fBlend == SkBlendMode::kSrcOver
+                && fDst.info().colorType() == kRGBA_8888_SkColorType
+                && !fDst.colorSpace()
+                && fDitherRate == 0.0f) {
+            p.append(SkRasterPipeline::srcover_rgba_8888, &fDstPtr);
+        } else {
+            if (fBlend != SkBlendMode::kSrc) {
+                this->append_load_d(&p);
+                this->append_blend(&p);
+                this->maybe_clamp(&p);
+            }
+            this->append_store(&p);
         }
-        this->append_store(&p);
+        fBlitH = p.compile();
     }
-    p.run(x,w);
+    this->maybe_shade(x,y,w);
+    fBlitH(x,y,w);
 }
 
 void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
-    auto& p = fBlitAntiH;
-    if (p.empty()) {
-        p.extend(fShader);
+    if (!fBlitAntiH) {
+        SkRasterPipeline p(fAlloc);
+        p.extend(fColorPipeline);
         if (fBlend == SkBlendMode::kSrcOver) {
             p.append(SkRasterPipeline::scale_1_float, &fCurrentCoverage);
             this->append_load_d(&p);
@@ -251,17 +338,18 @@
         }
         this->maybe_clamp(&p);
         this->append_store(&p);
+        fBlitAntiH = p.compile();
     }
 
     fDstPtr = fDst.writable_addr(0,y);
-    fCurrentY = y;
     for (int16_t run = *runs; run > 0; run = *runs) {
         switch (*aa) {
             case 0x00:                       break;
             case 0xff: this->blitH(x,y,run); break;
             default:
+                this->maybe_shade(x,y,run);
                 fCurrentCoverage = *aa * (1/255.0f);
-                p.run(x,run);
+                fBlitAntiH(x,y,run);
         }
         x    += run;
         runs += run;
@@ -269,15 +357,28 @@
     }
 }
 
+void SkRasterPipelineBlitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
+    SkIRect clip = {x,y, x+2,y+1};
+    uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
+
+    SkMask mask;
+    mask.fImage    = coverage;
+    mask.fBounds   = clip;
+    mask.fRowBytes = 2;
+    mask.fFormat   = SkMask::kA8_Format;
+
+    this->blitMask(mask, clip);
+}
+
 void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
     if (mask.fFormat == SkMask::kBW_Format) {
         // TODO: native BW masks?
         return INHERITED::blitMask(mask, clip);
     }
 
-    if (mask.fFormat == SkMask::kA8_Format && fBlitMaskA8.empty()) {
-        auto& p = fBlitMaskA8;
-        p.extend(fShader);
+    if (mask.fFormat == SkMask::kA8_Format && !fBlitMaskA8) {
+        SkRasterPipeline p(fAlloc);
+        p.extend(fColorPipeline);
         if (fBlend == SkBlendMode::kSrcOver) {
             p.append(SkRasterPipeline::scale_u8, &fMaskPtr);
             this->append_load_d(&p);
@@ -289,31 +390,33 @@
         }
         this->maybe_clamp(&p);
         this->append_store(&p);
+        fBlitMaskA8 = p.compile();
     }
 
-    if (mask.fFormat == SkMask::kLCD16_Format && fBlitMaskLCD16.empty()) {
-        auto& p = fBlitMaskLCD16;
-        p.extend(fShader);
+    if (mask.fFormat == SkMask::kLCD16_Format && !fBlitMaskLCD16) {
+        SkRasterPipeline p(fAlloc);
+        p.extend(fColorPipeline);
         this->append_load_d(&p);
         this->append_blend(&p);
         p.append(SkRasterPipeline::lerp_565, &fMaskPtr);
         this->maybe_clamp(&p);
         this->append_store(&p);
+        fBlitMaskLCD16 = p.compile();
     }
 
     int x = clip.left();
     for (int y = clip.top(); y < clip.bottom(); y++) {
         fDstPtr = fDst.writable_addr(0,y);
-        fCurrentY = y;
 
+        this->maybe_shade(x,y,clip.width());
         switch (mask.fFormat) {
             case SkMask::kA8_Format:
                 fMaskPtr = mask.getAddr8(x,y)-x;
-                fBlitMaskA8.run(x,clip.width());
+                fBlitMaskA8(x,y,clip.width());
                 break;
             case SkMask::kLCD16_Format:
                 fMaskPtr = mask.getAddrLCD16(x,y)-x;
-                fBlitMaskLCD16.run(x,clip.width());
+                fBlitMaskLCD16(x,y,clip.width());
                 break;
             default:
                 // TODO
diff --git a/src/core/SkReadBuffer.cpp b/src/core/SkReadBuffer.cpp
index 33e1d02..2f5b264 100644
--- a/src/core/SkReadBuffer.cpp
+++ b/src/core/SkReadBuffer.cpp
@@ -151,6 +151,12 @@
     point->fY = fReader.readScalar();
 }
 
+void SkReadBuffer::readPoint3(SkPoint3* point) {
+    point->fX = fReader.readScalar();
+    point->fY = fReader.readScalar();
+    point->fZ = fReader.readScalar();
+}
+
 void SkReadBuffer::readMatrix(SkMatrix* matrix) {
     fReader.readMatrix(matrix);
 }
diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h
index 014e034..453d22f 100644
--- a/src/core/SkReadBuffer.h
+++ b/src/core/SkReadBuffer.h
@@ -20,7 +20,7 @@
 #include "SkReadBuffer.h"
 #include "SkReader32.h"
 #include "SkRefCnt.h"
-#include "SkShader.h"
+#include "SkShaderBase.h"
 #include "SkTHash.h"
 #include "SkWriteBuffer.h"
 #include "SkXfermodePriv.h"
@@ -130,6 +130,7 @@
     virtual void readColor4f(SkColor4f* color);
     virtual void readPoint(SkPoint* point);
     SkPoint readPoint() { SkPoint p; this->readPoint(&p); return p; }
+    virtual void readPoint3(SkPoint3* point);
     virtual void readMatrix(SkMatrix* matrix);
     virtual void readIRect(SkIRect* rect);
     virtual void readRect(SkRect* rect);
@@ -149,7 +150,7 @@
     sk_sp<SkMaskFilter> readMaskFilter() { return this->readFlattenable<SkMaskFilter>(); }
     sk_sp<SkPathEffect> readPathEffect() { return this->readFlattenable<SkPathEffect>(); }
     sk_sp<SkRasterizer> readRasterizer() { return this->readFlattenable<SkRasterizer>(); }
-    sk_sp<SkShader> readShader() { return this->readFlattenable<SkShader>(); }
+    sk_sp<SkShader> readShader() { return this->readFlattenable<SkShaderBase>(); }
     sk_sp<SkXfermode> readXfermode() { return this->readFlattenable<SkXfermode>(); }
 
     // binary data and arrays
diff --git a/src/core/SkRecord.cpp b/src/core/SkRecord.cpp
index 3685b2d..3553ff5 100644
--- a/src/core/SkRecord.cpp
+++ b/src/core/SkRecord.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkRecord.h"
+#include "SkImage.h"
 #include <algorithm>
 
 SkRecord::~SkRecord() {
@@ -17,18 +18,12 @@
 
 void SkRecord::grow() {
     SkASSERT(fCount == fReserved);
-    SkASSERT(fReserved > 0);
-    fReserved *= 2;
+    fReserved = fReserved ? fReserved * 2 : 4;
     fRecords.realloc(fReserved);
 }
 
 size_t SkRecord::bytesUsed() const {
-    size_t bytes = fAlloc.approxBytesAllocated() + sizeof(SkRecord);
-    // If fReserved <= kInlineRecords, we've already accounted for fRecords with sizeof(SkRecord).
-    // When we go over that limit, they're allocated on the heap (and the inline space is wasted).
-    if (fReserved > kInlineRecords) {
-        bytes += fReserved * sizeof(Record);
-    }
+    size_t bytes = fApproxBytesAllocated + sizeof(SkRecord);
     return bytes;
 }
 
diff --git a/src/core/SkRecord.h b/src/core/SkRecord.h
index 79fe523..1360f35 100644
--- a/src/core/SkRecord.h
+++ b/src/core/SkRecord.h
@@ -8,10 +8,10 @@
 #ifndef SkRecord_DEFINED
 #define SkRecord_DEFINED
 
+#include "SkArenaAlloc.h"
 #include "SkRecords.h"
 #include "SkTLogic.h"
 #include "SkTemplates.h"
-#include "SkVarAlloc.h"
 
 // SkRecord represents a sequence of SkCanvas calls, saved for future use.
 // These future uses may include: replay, optimization, serialization, or combinations of those.
@@ -25,18 +25,9 @@
 // only with SkRecords::* structs defined in SkRecords.h.  Your compiler will helpfully yell if you
 // get this wrong.
 
-class SkRecord : public SkNVRefCnt<SkRecord> {
-    enum {
-        // TODO: tune these two constants.
-        kInlineRecords      = 4, // Ideally our lower limit on recorded ops per picture.
-        kInlineAllocLgBytes = 8, // 1<<8 == 256 bytes inline, then SkVarAlloc starting at 512 bytes.
-    };
+class SkRecord : public SkRefCnt {
 public:
-    SkRecord()
-        : fCount(0)
-        , fReserved(kInlineRecords)
-        , fAlloc(kInlineAllocLgBytes+1,  // First malloc'd block is 2x as large as fInlineAlloc.
-                 fInlineAlloc, sizeof(fInlineAlloc)) {}
+    SkRecord() = default;
     ~SkRecord();
 
     // Returns the number of canvas commands in this SkRecord.
@@ -64,7 +55,11 @@
     // Here T can be any class, not just those from SkRecords.  Throws on failure.
     template <typename T>
     T* alloc(size_t count = 1) {
-        return (T*)fAlloc.alloc(sizeof(T) * count);
+        struct RawBytes {
+            alignas(T) char data[sizeof(T)];
+        };
+        fApproxBytesAllocated += count * sizeof(T) + alignof(T);
+        return (T*)fAlloc.makeArrayDefault<RawBytes>(count);
     }
 
     // Add a new command of type T to the end of this SkRecord.
@@ -186,13 +181,14 @@
 
     // fRecords needs to be a data structure that can append fixed length data, and need to
     // support efficient random access and forward iteration.  (It doesn't need to be contiguous.)
-    int fCount, fReserved;
-    SkAutoSTMalloc<kInlineRecords, Record> fRecords;
+    int fCount{0},
+        fReserved{0};
+    SkAutoTMalloc<Record> fRecords;
 
     // fAlloc needs to be a data structure which can append variable length data in contiguous
     // chunks, returning a stable handle to that data for later retrieval.
-    SkVarAlloc fAlloc;
-    char fInlineAlloc[1 << kInlineAllocLgBytes];
+    SkArenaAlloc fAlloc{256};
+    size_t       fApproxBytesAllocated{0};
 };
 
 #endif//SkRecord_DEFINED
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index 21f9de9..953e874 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkRecordDraw.h"
+#include "SkImage.h"
 #include "SkPatchUtils.h"
 
 void SkRecordDraw(const SkRecord& record,
@@ -77,6 +78,8 @@
 DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds,
                                                  r.paint,
                                                  r.backdrop.get(),
+                                                 r.clipMask.get(),
+                                                 r.clipMatrix,
                                                  r.saveLayerFlags)));
 DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
 DRAW(Concat, concat(r.matrix));
@@ -87,12 +90,6 @@
 DRAW(ClipRect, clipRect(r.rect, r.opAA.op(), r.opAA.aa()));
 DRAW(ClipRegion, clipRegion(r.region, r.op));
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-DRAW(TranslateZ, SkCanvas::translateZ(r.z));
-#else
-template <> void Draw::draw(const TranslateZ& r) { }
-#endif
-
 DRAW(DrawArc, drawArc(r.oval, r.startAngle, r.sweepAngle, r.useCenter, r.paint));
 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
 DRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.paint));
@@ -115,13 +112,6 @@
 DRAW(DrawPath, drawPath(r.path, r.paint));
 DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint));
 DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint));
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-DRAW(DrawShadowedPicture, drawShadowedPicture(r.picture.get(), &r.matrix, r.paint, r.params));
-#else
-template <> void Draw::draw(const DrawShadowedPicture& r) { }
-#endif
-
 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint));
 DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint));
 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
@@ -135,6 +125,7 @@
 DRAW(DrawAtlas, drawAtlas(r.atlas.get(),
                           r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint));
 DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint));
+DRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec));
 DRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get()));
 #undef DRAW
 
@@ -302,7 +293,6 @@
     void trackBounds(const SetMatrix&)         { this->pushControl(); }
     void trackBounds(const Concat&)            { this->pushControl(); }
     void trackBounds(const Translate&)         { this->pushControl(); }
-    void trackBounds(const TranslateZ&)        { this->pushControl(); }
     void trackBounds(const ClipRect&)          { this->pushControl(); }
     void trackBounds(const ClipRRect&)         { this->pushControl(); }
     void trackBounds(const ClipPath&)          { this->pushControl(); }
@@ -466,13 +456,11 @@
         }
     }
 
-    Bounds bounds(const DrawPicture& op) const {
-        SkRect dst = op.picture->cullRect();
-        op.matrix.mapRect(&dst);
-        return this->adjustAndMap(dst, op.paint);
+    Bounds bounds(const DrawShadowRec& op) const {
+        return this->adjustAndMap(op.path.getBounds(), nullptr);
     }
 
-    Bounds bounds(const DrawShadowedPicture& op) const {
+    Bounds bounds(const DrawPicture& op) const {
         SkRect dst = op.picture->cullRect();
         op.matrix.mapRect(&dst);
         return this->adjustAndMap(dst, op.paint);
diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp
index 07ef19d..8a1ac59 100644
--- a/src/core/SkRecordOpts.cpp
+++ b/src/core/SkRecordOpts.cpp
@@ -188,8 +188,8 @@
     typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
 
     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
-        if (match->first<SaveLayer>()->backdrop) {
-            // can't throw away the layer if we have a backdrop
+        if (match->first<SaveLayer>()->backdrop || match->first<SaveLayer>()->clipMask) {
+            // can't throw away the layer if we have a backdrop or clip mask
             return false;
         }
 
diff --git a/src/core/SkRecordPattern.h b/src/core/SkRecordPattern.h
index 73fe763..dcce993 100644
--- a/src/core/SkRecordPattern.h
+++ b/src/core/SkRecordPattern.h
@@ -49,13 +49,13 @@
     type* get() { return fPaint; }
 
     template <typename T>
-    SK_WHEN(T::kTags & kDraw_Tag, bool) operator()(T* draw) {
+    SK_WHEN((T::kTags & kDrawWithPaint_Tag) == kDrawWithPaint_Tag, bool) operator()(T* draw) {
         fPaint = AsPtr(draw->paint);
         return true;
     }
 
-    bool operator()(DrawDrawable*) {
-        static_assert(DrawDrawable::kTags & kDraw_Tag, "");
+    template <typename T>
+    SK_WHEN((T::kTags & kDrawWithPaint_Tag) == kDraw_Tag, bool) operator()(T* draw) {
         fPaint = nullptr;
         return true;
     }
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 0f2891a..1eeef53 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -54,7 +54,8 @@
     this->forgetRecord();
     fDrawPictureMode = dpm;
     fRecord = record;
-    this->resetForNextPicture(bounds.roundOut());
+    SkIRect rounded = bounds.roundOut();
+    this->resetCanvas(rounded.right(), rounded.bottom());
     fMiniRecorder = mr;
 }
 
@@ -309,23 +310,6 @@
     }
 }
 
-void SkRecorder::onDrawShadowedPicture(const SkPicture* pic, const SkMatrix* matrix,
-                                       const SkPaint* paint, const SkShadowParams& params) {
-    if (fDrawPictureMode == Record_DrawPictureMode) {
-        fApproxBytesUsedBySubPictures += pic->approximateBytesUsed();
-        APPEND(DrawShadowedPicture, this->copy(paint),
-                                    sk_ref_sp(pic),
-                                    matrix ? *matrix : SkMatrix::I(),
-                                    params);
-    } else {
-        // TODO update pic->playback(this) to draw the shadowed pic
-        SkASSERT(fDrawPictureMode == Playback_DrawPictureMode);
-        SkAutoCanvasMatrixPaint acmp(this,  matrix, paint, pic->cullRect());
-        pic->playback(this);
-    }
-}
-
-
 void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
                                       const SkPaint& paint) {
     APPEND(DrawVertices, paint, sk_ref_sp(const_cast<SkVertices*>(vertices)), bmode);
@@ -354,6 +338,10 @@
            this->copy(cull));
 }
 
+void SkRecorder::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+    APPEND(DrawShadowRec, path, rec);
+}
+
 void SkRecorder::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
     APPEND(DrawAnnotation, rect, SkString(key), sk_ref_sp(value));
 }
@@ -366,6 +354,8 @@
     APPEND(SaveLayer, this->copy(rec.fBounds)
                     , this->copy(rec.fPaint)
                     , sk_ref_sp(rec.fBackdrop)
+                    , sk_ref_sp(rec.fClipMask)
+                    , this->copy(rec.fClipMatrix)
                     , rec.fSaveLayerFlags);
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
@@ -386,12 +376,6 @@
     APPEND(Translate, dx, dy);
 }
 
-void SkRecorder::didTranslateZ(SkScalar z) {
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    APPEND(TranslateZ, z);
-#endif
-}
-
 void SkRecorder::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
     INHERITED(onClipRect, rect, op, edgeStyle);
     SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle);
diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h
index 3407fc7..04e75ed 100644
--- a/src/core/SkRecorder.h
+++ b/src/core/SkRecorder.h
@@ -62,12 +62,6 @@
     void didSetMatrix(const SkMatrix&) override;
     void didTranslate(SkScalar, SkScalar) override;
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void didTranslateZ(SkScalar) override;
-#else
-    void didTranslateZ(SkScalar);
-#endif
-
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     void onDrawDrawable(SkDrawable*, const SkMatrix*) override;
     void onDrawText(const void* text,
@@ -127,6 +121,7 @@
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int count, SkBlendMode, const SkRect* cull, const SkPaint*) override;
+    void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
 
     void onClipRect(const SkRect& rect, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) override;
@@ -135,18 +130,6 @@
 
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void onDrawShadowedPicture(const SkPicture*,
-                               const SkMatrix*,
-                               const SkPaint*,
-                               const SkShadowParams& params) override;
-#else
-    void onDrawShadowedPicture(const SkPicture*,
-                               const SkMatrix*,
-                               const SkPaint*,
-                               const SkShadowParams& params);
-#endif
-
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
 
     sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
diff --git a/src/core/SkRecords.cpp b/src/core/SkRecords.cpp
index 81dd92f..e4b9086 100644
--- a/src/core/SkRecords.cpp
+++ b/src/core/SkRecords.cpp
@@ -11,6 +11,7 @@
 namespace SkRecords {
     PreCachedPath::PreCachedPath(const SkPath& path) : SkPath(path) {
         this->updateBoundsCache();
+        (void)this->getGenerationID();
 #if 0  // Disabled to see if we ever really race on this.  It costs time, chromium:496982.
         SkPathPriv::FirstDirection junk;
         (void)SkPathPriv::CheapComputeFirstDirection(*this, &junk);
diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h
new file mode 100644
index 0000000..3897304
--- /dev/null
+++ b/src/core/SkRecords.h
@@ -0,0 +1,361 @@
+/*
+ * 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 SkRecords_DEFINED
+#define SkRecords_DEFINED
+
+#include "SkData.h"
+#include "SkCanvas.h"
+#include "SkDrawable.h"
+#include "SkDrawShadowRec.h"
+#include "SkImage.h"
+#include "SkImageFilter.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "SkRRect.h"
+#include "SkRSXform.h"
+#include "SkString.h"
+#include "SkTextBlob.h"
+#include "SkVertices.h"
+
+// Windows.h, will pull in all of the GDI defines.  GDI #defines
+// DrawText to DrawTextA or DrawTextW, but SkRecord has a struct
+// called DrawText. Since this file does not use GDI, undefing
+// DrawText makes things less confusing.
+#ifdef DrawText
+#undef DrawText
+#endif
+
+namespace SkRecords {
+
+// A list of all the types of canvas calls we can record.
+// Each of these is reified into a struct below.
+//
+// (We're using the macro-of-macro trick here to do several different things with the same list.)
+//
+// We leave this SK_RECORD_TYPES macro defined for use by code that wants to operate on SkRecords
+// types polymorphically.  (See SkRecord::Record::{visit,mutate} for an example.)
+//
+// Order doesn't technically matter here, but the compiler can generally generate better code if
+// you keep them semantically grouped, especially the Draws.  It's also nice to leave NoOp at 0.
+#define SK_RECORD_TYPES(M)                                          \
+    M(NoOp)                                                         \
+    M(Restore)                                                      \
+    M(Save)                                                         \
+    M(SaveLayer)                                                    \
+    M(SetMatrix)                                                    \
+    M(Translate)                                                    \
+    M(Concat)                                                       \
+    M(ClipPath)                                                     \
+    M(ClipRRect)                                                    \
+    M(ClipRect)                                                     \
+    M(ClipRegion)                                                   \
+    M(DrawArc)                                                      \
+    M(DrawDrawable)                                                 \
+    M(DrawImage)                                                    \
+    M(DrawImageLattice)                                             \
+    M(DrawImageRect)                                                \
+    M(DrawImageNine)                                                \
+    M(DrawDRRect)                                                   \
+    M(DrawOval)                                                     \
+    M(DrawPaint)                                                    \
+    M(DrawPath)                                                     \
+    M(DrawPatch)                                                    \
+    M(DrawPicture)                                                  \
+    M(DrawPoints)                                                   \
+    M(DrawPosText)                                                  \
+    M(DrawPosTextH)                                                 \
+    M(DrawText)                                                     \
+    M(DrawTextOnPath)                                               \
+    M(DrawTextRSXform)                                              \
+    M(DrawRRect)                                                    \
+    M(DrawRect)                                                     \
+    M(DrawRegion)                                                   \
+    M(DrawTextBlob)                                                 \
+    M(DrawAtlas)                                                    \
+    M(DrawVertices)                                                 \
+    M(DrawShadowRec)                                                \
+    M(DrawAnnotation)
+
+// Defines SkRecords::Type, an enum of all record types.
+#define ENUM(T) T##_Type,
+enum Type { SK_RECORD_TYPES(ENUM) };
+#undef ENUM
+
+#define ACT_AS_PTR(ptr)                 \
+    operator T*() const { return ptr; } \
+    T* operator->() const { return ptr; }
+
+// An Optional doesn't own the pointer's memory, but may need to destroy non-POD data.
+template <typename T>
+class Optional : SkNoncopyable {
+public:
+    Optional() : fPtr(nullptr) {}
+    Optional(T* ptr) : fPtr(ptr) {}
+    Optional(Optional&& o) : fPtr(o.fPtr) {
+        o.fPtr = nullptr;
+    }
+    ~Optional() { if (fPtr) fPtr->~T(); }
+
+    ACT_AS_PTR(fPtr)
+private:
+    T* fPtr;
+};
+
+// Like Optional, but ptr must not be NULL.
+template <typename T>
+class Adopted : SkNoncopyable {
+public:
+    Adopted(T* ptr) : fPtr(ptr) { SkASSERT(fPtr); }
+    Adopted(Adopted* source) {
+        // Transfer ownership from source to this.
+        fPtr = source->fPtr;
+        source->fPtr = NULL;
+    }
+    ~Adopted() { if (fPtr) fPtr->~T(); }
+
+    ACT_AS_PTR(fPtr)
+private:
+    T* fPtr;
+};
+
+// PODArray doesn't own the pointer's memory, and we assume the data is POD.
+template <typename T>
+class PODArray {
+public:
+    PODArray() {}
+    PODArray(T* ptr) : fPtr(ptr) {}
+    // Default copy and assign.
+
+    ACT_AS_PTR(fPtr)
+private:
+    T* fPtr;
+};
+
+#undef ACT_AS_PTR
+
+// SkPath::getBounds() isn't thread safe unless we precache the bounds in a singlethreaded context.
+// SkPath::cheapComputeDirection() is similar.
+// Recording is a convenient time to cache these, or we can delay it to between record and playback.
+struct PreCachedPath : public SkPath {
+    PreCachedPath() {}
+    PreCachedPath(const SkPath& path);
+};
+
+// Like SkPath::getBounds(), SkMatrix::getType() isn't thread safe unless we precache it.
+// This may not cover all SkMatrices used by the picture (e.g. some could be hiding in a shader).
+struct TypedMatrix : public SkMatrix {
+    TypedMatrix() {}
+    TypedMatrix(const SkMatrix& matrix);
+};
+
+enum Tags {
+    kDraw_Tag      = 1,   // May draw something (usually named DrawFoo).
+    kHasImage_Tag  = 2,   // Contains an SkImage or SkBitmap.
+    kHasText_Tag   = 4,   // Contains text.
+    kHasPaint_Tag  = 8,   // May have an SkPaint field, at least optionally.
+
+    kDrawWithPaint_Tag = kDraw_Tag | kHasPaint_Tag,
+};
+
+// A macro to make it a little easier to define a struct that can be stored in SkRecord.
+#define RECORD(T, tags, ...)            \
+struct T {                              \
+    static const Type kType = T##_Type; \
+    static const int kTags = tags;      \
+    __VA_ARGS__;                        \
+};
+
+RECORD(NoOp, 0);
+RECORD(Restore, 0,
+        SkIRect devBounds;
+        TypedMatrix matrix);
+RECORD(Save, 0);
+
+RECORD(SaveLayer, kHasPaint_Tag,
+       Optional<SkRect> bounds;
+       Optional<SkPaint> paint;
+       sk_sp<const SkImageFilter> backdrop;
+       sk_sp<const SkImage> clipMask;
+       Optional<SkMatrix> clipMatrix;
+       SkCanvas::SaveLayerFlags saveLayerFlags);
+
+RECORD(SetMatrix, 0,
+        TypedMatrix matrix);
+RECORD(Concat, 0,
+        TypedMatrix matrix);
+
+RECORD(Translate, 0,
+        SkScalar dx;
+        SkScalar dy);
+
+struct ClipOpAndAA {
+    ClipOpAndAA() {}
+    ClipOpAndAA(SkClipOp op, bool aa) : fOp(static_cast<unsigned>(op)), fAA(aa) {}
+
+    SkClipOp op() const { return static_cast<SkClipOp>(fOp); }
+    bool aa() const { return fAA != 0; }
+
+private:
+    unsigned fOp : 31;  // This really only needs to be 3, but there's no win today to do so.
+    unsigned fAA :  1;  // MSVC won't pack an enum with an bool, so we call this an unsigned.
+};
+static_assert(sizeof(ClipOpAndAA) == 4, "ClipOpAndAASize");
+
+RECORD(ClipPath, 0,
+        SkIRect devBounds;
+        PreCachedPath path;
+        ClipOpAndAA opAA);
+RECORD(ClipRRect, 0,
+        SkIRect devBounds;
+        SkRRect rrect;
+        ClipOpAndAA opAA);
+RECORD(ClipRect, 0,
+        SkIRect devBounds;
+        SkRect rect;
+        ClipOpAndAA opAA);
+RECORD(ClipRegion, 0,
+        SkIRect devBounds;
+        SkRegion region;
+        SkClipOp op);
+
+// While not strictly required, if you have an SkPaint, it's fastest to put it first.
+RECORD(DrawArc, kDraw_Tag|kHasPaint_Tag,
+       SkPaint paint;
+       SkRect oval;
+       SkScalar startAngle;
+       SkScalar sweepAngle;
+       unsigned useCenter);
+RECORD(DrawDRRect, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        SkRRect outer;
+        SkRRect inner);
+RECORD(DrawDrawable, kDraw_Tag,
+        Optional<SkMatrix> matrix;
+        SkRect worstCaseBounds;
+        int32_t index);
+RECORD(DrawImage, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
+        Optional<SkPaint> paint;
+        sk_sp<const SkImage> image;
+        SkScalar left;
+        SkScalar top);
+RECORD(DrawImageLattice, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
+        Optional<SkPaint> paint;
+        sk_sp<const SkImage> image;
+        int xCount;
+        PODArray<int> xDivs;
+        int yCount;
+        PODArray<int> yDivs;
+        int flagCount;
+        PODArray<SkCanvas::Lattice::Flags> flags;
+        SkIRect src;
+        SkRect dst);
+RECORD(DrawImageRect, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
+        Optional<SkPaint> paint;
+        sk_sp<const SkImage> image;
+        Optional<SkRect> src;
+        SkRect dst;
+        SkCanvas::SrcRectConstraint constraint);
+RECORD(DrawImageNine, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
+        Optional<SkPaint> paint;
+        sk_sp<const SkImage> image;
+        SkIRect center;
+        SkRect dst);
+RECORD(DrawOval, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        SkRect oval);
+RECORD(DrawPaint, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint);
+RECORD(DrawPath, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PreCachedPath path);
+RECORD(DrawPicture, kDraw_Tag|kHasPaint_Tag,
+        Optional<SkPaint> paint;
+        sk_sp<const SkPicture> picture;
+        TypedMatrix matrix);
+RECORD(DrawPoints, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        SkCanvas::PointMode mode;
+        unsigned count;
+        SkPoint* pts);
+RECORD(DrawPosText, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PODArray<char> text;
+        size_t byteLength;
+        PODArray<SkPoint> pos);
+RECORD(DrawPosTextH, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PODArray<char> text;
+        unsigned byteLength;
+        SkScalar y;
+        PODArray<SkScalar> xpos);
+RECORD(DrawRRect, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        SkRRect rrect);
+RECORD(DrawRect, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        SkRect rect);
+RECORD(DrawRegion, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        SkRegion region);
+RECORD(DrawText, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PODArray<char> text;
+        size_t byteLength;
+        SkScalar x;
+        SkScalar y);
+RECORD(DrawTextBlob, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        sk_sp<const SkTextBlob> blob;
+        SkScalar x;
+        SkScalar y);
+RECORD(DrawTextOnPath, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PODArray<char> text;
+        size_t byteLength;
+        PreCachedPath path;
+        TypedMatrix matrix);
+RECORD(DrawTextRSXform, kDraw_Tag|kHasText_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PODArray<char> text;
+        size_t byteLength;
+        PODArray<SkRSXform> xforms;
+        Optional<SkRect> cull);
+RECORD(DrawPatch, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        PODArray<SkPoint> cubics;
+        PODArray<SkColor> colors;
+        PODArray<SkPoint> texCoords;
+        SkBlendMode bmode);
+RECORD(DrawAtlas, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
+        Optional<SkPaint> paint;
+        sk_sp<const SkImage> atlas;
+        PODArray<SkRSXform> xforms;
+        PODArray<SkRect> texs;
+        PODArray<SkColor> colors;
+        int count;
+        SkBlendMode mode;
+        Optional<SkRect> cull);
+RECORD(DrawVertices, kDraw_Tag|kHasPaint_Tag,
+        SkPaint paint;
+        sk_sp<SkVertices> vertices;
+        SkBlendMode bmode);
+RECORD(DrawShadowRec, kDraw_Tag,
+       PreCachedPath path;
+       SkDrawShadowRec rec);
+RECORD(DrawAnnotation, 0,  // TODO: kDraw_Tag, skia:5548
+       SkRect rect;
+       SkString key;
+       sk_sp<SkData> value);
+#undef RECORD
+
+}  // namespace SkRecords
+
+#endif//SkRecords_DEFINED
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
index d868bbb..900b872 100644
--- a/src/core/SkRect.cpp
+++ b/src/core/SkRect.cpp
@@ -26,15 +26,6 @@
     }
 }
 
-void SkIRect::sort() {
-    if (fLeft > fRight) {
-        SkTSwap<int32_t>(fLeft, fRight);
-    }
-    if (fTop > fBottom) {
-        SkTSwap<int32_t>(fTop, fBottom);
-    }
-}
-
 /////////////////////////////////////////////////////////////////////////////
 
 void SkRect::toQuad(SkPoint quad[4]) const {
diff --git a/src/core/SkResourceCache.cpp b/src/core/SkResourceCache.cpp
index 9f5bc25..5928b48 100644
--- a/src/core/SkResourceCache.cpp
+++ b/src/core/SkResourceCache.cpp
@@ -5,11 +5,11 @@
  * found in the LICENSE file.
  */
 
+#include "SkDiscardableMemory.h"
 #include "SkMessageBus.h"
 #include "SkMipMap.h"
 #include "SkMutex.h"
 #include "SkOpts.h"
-#include "SkPixelRef.h"
 #include "SkResourceCache.h"
 #include "SkTraceMemoryDump.h"
 
@@ -75,140 +75,15 @@
     fTotalBytesUsed = 0;
     fCount = 0;
     fSingleAllocationByteLimit = 0;
-    fAllocator = nullptr;
 
     // One of these should be explicit set by the caller after we return.
     fTotalByteLimit = 0;
     fDiscardableFactory = nullptr;
 }
 
-#include "SkDiscardableMemory.h"
-
-class SkOneShotDiscardablePixelRef : public SkPixelRef {
-public:
-
-    // Ownership of the discardablememory is transfered to the pixelref
-    // The pixelref will ref() the colortable (if not NULL), and unref() in destructor
-    SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes,
-                                 SkColorTable*);
-    ~SkOneShotDiscardablePixelRef() override;
-
-protected:
-    bool onNewLockPixels(LockRec*) override;
-    void onUnlockPixels() override;
-    size_t getAllocatedSizeInBytes() const override;
-
-    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return fDM; }
-
-private:
-    SkDiscardableMemory* fDM;
-    size_t               fRB;
-    bool                 fFirstTime;
-    SkColorTable*        fCTable;
-
-    typedef SkPixelRef INHERITED;
-};
-
-SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
-                                             SkDiscardableMemory* dm,
-                                             size_t rowBytes,
-                                             SkColorTable* ctable)
-    : INHERITED(info)
-    , fDM(dm)
-    , fRB(rowBytes)
-    , fCTable(ctable)
-{
-    SkASSERT(dm->data());
-    fFirstTime = true;
-    SkSafeRef(ctable);
-}
-
-SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
-    delete fDM;
-    SkSafeUnref(fCTable);
-}
-
-bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
-    if (fFirstTime) {
-        // we're already locked
-        SkASSERT(fDM->data());
-        fFirstTime = false;
-        goto SUCCESS;
-    }
-
-    // A previous call to onUnlock may have deleted our DM, so check for that
-    if (nullptr == fDM) {
-        return false;
-    }
-
-    if (!fDM->lock()) {
-        // since it failed, we delete it now, to free-up the resource
-        delete fDM;
-        fDM = nullptr;
-        return false;
-    }
-
-SUCCESS:
-    rec->fPixels = fDM->data();
-    rec->fColorTable = fCTable;
-    rec->fRowBytes = fRB;
-    return true;
-}
-
-void SkOneShotDiscardablePixelRef::onUnlockPixels() {
-    SkASSERT(!fFirstTime);
-    fDM->unlock();
-}
-
-size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
-    return this->info().getSafeSize(fRB);
-}
-
-class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator {
-public:
-    SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) {
-        SkASSERT(factory);
-        fFactory = factory;
-    }
-
-    bool allocPixelRef(SkBitmap*, SkColorTable*) override;
-
-private:
-    SkResourceCache::DiscardableFactory fFactory;
-};
-
-bool SkResourceCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    size_t size = bitmap->getSize();
-    uint64_t size64 = bitmap->computeSize64();
-    if (0 == size || size64 > (uint64_t)size) {
-        return false;
-    }
-
-    if (kIndex_8_SkColorType == bitmap->colorType()) {
-        if (!ctable) {
-            return false;
-        }
-    } else {
-        ctable = nullptr;
-    }
-
-    SkDiscardableMemory* dm = fFactory(size);
-    if (nullptr == dm) {
-        return false;
-    }
-
-    SkImageInfo info = bitmap->info();
-    bitmap->setPixelRef(
-            sk_make_sp<SkOneShotDiscardablePixelRef>(info, dm, bitmap->rowBytes(), ctable), 0, 0);
-    bitmap->lockPixels();
-    return bitmap->readyToDraw();
-}
-
 SkResourceCache::SkResourceCache(DiscardableFactory factory) {
     this->init();
     fDiscardableFactory = factory;
-
-    fAllocator = new SkResourceCacheDiscardableAllocator(factory);
 }
 
 SkResourceCache::SkResourceCache(size_t byteLimit) {
@@ -217,8 +92,6 @@
 }
 
 SkResourceCache::~SkResourceCache() {
-    SkSafeUnref(fAllocator);
-
     Rec* rec = fHead;
     while (rec) {
         Rec* next = rec->fNext;
@@ -258,18 +131,27 @@
 
 static bool gDumpCacheTransactions;
 
-void SkResourceCache::add(Rec* rec) {
+void SkResourceCache::add(Rec* rec, void* payload) {
     this->checkMessages();
 
     SkASSERT(rec);
     // See if we already have this key (racy inserts, etc.)
-    if (nullptr != fHash->find(rec->getKey())) {
-        delete rec;
-        return;
+    if (Rec** preexisting = fHash->find(rec->getKey())) {
+        Rec* prev = *preexisting;
+        if (prev->canBePurged()) {
+            // if it can be purged, the install may fail, so we have to remove it
+            this->remove(prev);
+        } else {
+            // if it cannot be purged, we reuse it and delete the new one
+            prev->postAddInstall(payload);
+            delete rec;
+            return;
+        }
     }
 
     this->addToHead(rec);
     fHash->set(rec);
+    rec->postAddInstall(payload);
 
     if (gDumpCacheTransactions) {
         SkString bytesStr, totalStr;
@@ -284,6 +166,7 @@
 }
 
 void SkResourceCache::remove(Rec* rec) {
+    SkASSERT(rec->canBePurged());
     size_t used = rec->bytesUsed();
     SkASSERT(used <= fTotalBytesUsed);
 
@@ -293,6 +176,8 @@
     fTotalBytesUsed -= used;
     fCount -= 1;
 
+    //SkDebugf("-RC count [%3d] bytes %d\n", fCount, fTotalBytesUsed);
+
     if (gDumpCacheTransactions) {
         SkString bytesStr, totalStr;
         make_size_str(used, &bytesStr);
@@ -323,7 +208,9 @@
         }
 
         Rec* prev = rec->fPrev;
-        this->remove(rec);
+        if (rec->canBePurged()) {
+            this->remove(rec);
+        }
         rec = prev;
     }
 }
@@ -350,8 +237,11 @@
     while (rec) {
         Rec* prev = rec->fPrev;
         if (rec->getKey().getSharedID() == sharedID) {
-//            SkDebugf("purgeSharedID id=%llx rec=%p\n", sharedID, rec);
-            this->remove(rec);
+            // even though the "src" is now dead, caches could still be in-flight, so
+            // we have to check if it can be removed.
+            if (rec->canBePurged()) {
+                this->remove(rec);
+            }
 #ifdef SK_TRACK_PURGE_SHAREDID_HITRATE
             found = true;
 #endif
@@ -587,11 +477,6 @@
     return get_cache()->discardableFactory();
 }
 
-SkBitmap::Allocator* SkResourceCache::GetAllocator() {
-    SkAutoMutexAcquire am(gMutex);
-    return get_cache()->allocator();
-}
-
 SkCachedData* SkResourceCache::NewCachedData(size_t bytes) {
     SkAutoMutexAcquire am(gMutex);
     return get_cache()->newCachedData(bytes);
@@ -627,9 +512,9 @@
     return get_cache()->find(key, visitor, context);
 }
 
-void SkResourceCache::Add(Rec* rec) {
+void SkResourceCache::Add(Rec* rec, void* payload) {
     SkAutoMutexAcquire am(gMutex);
-    get_cache()->add(rec);
+    get_cache()->add(rec, payload);
 }
 
 void SkResourceCache::VisitAll(Visitor visitor, void* context) {
diff --git a/src/core/SkResourceCache.h b/src/core/SkResourceCache.h
index 0ff6271..6087be7 100644
--- a/src/core/SkResourceCache.h
+++ b/src/core/SkResourceCache.h
@@ -82,6 +82,22 @@
         virtual const Key& getKey() const = 0;
         virtual size_t bytesUsed() const = 0;
 
+        // Called if the cache needs to purge/remove/delete the Rec. Default returns true.
+        // Subclass may return false if there are outstanding references to it (e.g. bitmaps).
+        // Will only be deleted/removed-from-the-cache when this returns true.
+        virtual bool canBePurged() { return true; }
+
+        // A rec is first created/initialized, and then added to the cache. As part of the add(),
+        // the cache will callback into the rec with postAddInstall, passing in whatever payload
+        // was passed to add/Add.
+        //
+        // This late-install callback exists because the process of add-ing might end up deleting
+        // the new rec (if an existing rec in the cache has the same key and cannot be purged).
+        // If the new rec will be deleted during add, the pre-existing one (with the same key)
+        // will have postAddInstall() called on it instead, so that either way an "install" will
+        // happen during the add.
+        virtual void postAddInstall(void*) {}
+
         // for memory usage diagnostics
         virtual const char* getCategory() const = 0;
         virtual SkDiscardableMemory* diagnostic_only_getDiscardable() const { return nullptr; }
@@ -135,7 +151,7 @@
      *      false : Rec is "stale" -- the cache will purge it.
      */
     static bool Find(const Key& key, FindVisitor, void* context);
-    static void Add(Rec*);
+    static void Add(Rec*, void* payload = nullptr);
 
     typedef void (*Visitor)(const Rec&, void* context);
     // Call the visitor for every Rec in the cache.
@@ -163,12 +179,6 @@
      */
     static DiscardableFactory GetDiscardableFactory();
 
-    /**
-     * Use this allocator for bitmaps, so they can use ashmem when available.
-     * Returns nullptr if the ResourceCache has not been initialized with a DiscardableFactory.
-     */
-    static SkBitmap::Allocator* GetAllocator();
-
     static SkCachedData* NewCachedData(size_t bytes);
 
     static void PostPurgeSharedID(uint64_t sharedID);
@@ -208,7 +218,7 @@
      *      false : Rec is "stale" -- the cache will purge it.
      */
     bool find(const Key&, FindVisitor, void* context);
-    void add(Rec*);
+    void add(Rec*, void* payload = nullptr);
     void visitAll(Visitor, void* context);
 
     size_t getTotalBytesUsed() const { return fTotalBytesUsed; }
@@ -239,7 +249,6 @@
     }
 
     DiscardableFactory discardableFactory() const { return fDiscardableFactory; }
-    SkBitmap::Allocator* allocator() const { return fAllocator; }
 
     SkCachedData* newCachedData(size_t bytes);
 
@@ -256,8 +265,6 @@
     Hash*   fHash;
 
     DiscardableFactory  fDiscardableFactory;
-    // the allocator is nullptr or one that matches discardables
-    SkBitmap::Allocator* fAllocator;
 
     size_t  fTotalBytesUsed;
     size_t  fTotalByteLimit;
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index a5a47e4..f310011 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -17,6 +17,7 @@
 #include "SkMaskFilter.h"
 #include "SkMaskGamma.h"
 #include "SkMatrix22.h"
+#include "SkPaintPriv.h"
 #include "SkPathEffect.h"
 #include "SkRasterClip.h"
 #include "SkRasterizer.h"
@@ -678,7 +679,7 @@
 }
 
 void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
-    SkPaint::SetTextMatrix(m, fTextSize, fPreScaleX, fPreSkewX);
+    SkPaintPriv::MakeTextMatrix(m, fTextSize, fPreScaleX, fPreSkewX);
 }
 
 void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const {
diff --git a/src/core/SkScan_AAAPath.cpp b/src/core/SkScan_AAAPath.cpp
index 3eca137..cb38d61 100644
--- a/src/core/SkScan_AAAPath.cpp
+++ b/src/core/SkScan_AAAPath.cpp
@@ -1592,17 +1592,9 @@
         bool isUsingMask, bool forceRLE) { // forceRLE implies that SkAAClip is calling us
     SkASSERT(blitter);
 
-    SkEdgeBuilder   builder;
-
-    // If we're convex, then we need both edges, even the right edge is past the clip
-    const bool canCullToTheRight = !path.isConvex();
-
-    SkASSERT(gSkUseAnalyticAA.load());
-    const SkIRect* builderClip = pathContainedInClip ? nullptr : &clipRect;
-    int count = builder.build(path, builderClip, 0, canCullToTheRight, true);
-    SkASSERT(count >= 0);
-
-    SkAnalyticEdge** list = (SkAnalyticEdge**)builder.analyticEdgeList();
+    SkEdgeBuilder builder;
+    int count = builder.build_edges(path, &clipRect, 0, pathContainedInClip, true);
+    SkAnalyticEdge** list = builder.analyticEdgeList();
 
     SkIRect rect = clipRect;
     if (0 == count) {
@@ -1671,8 +1663,7 @@
         rightBound = SkTMin(rightBound, SkIntToFixed(ir.fRight));
     }
 
-    if (!path.isInverseFillType() && path.isConvex()) {
-        SkASSERT(count >= 2);   // convex walker does not handle missing right edges
+    if (!path.isInverseFillType() && path.isConvex() && count >= 2) {
         aaa_walk_convex_edges(&headEdge, blitter, start_y, stop_y,
                               leftBound, rightBound, isUsingMask);
     } else {
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index 0415b2f..0975af8 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -396,16 +396,9 @@
     shiftedClip.fTop <<= shiftEdgesUp;
     shiftedClip.fBottom <<= shiftEdgesUp;
 
-    SkEdgeBuilder   builder;
-
-    // If we're convex, then we need both edges, even the right edge is past the clip
-    const bool canCullToTheRight = !path.isConvex();
-
-    SkIRect* builderClip = pathContainedInClip ? nullptr : &shiftedClip;
-    int count = builder.build(path, builderClip, shiftEdgesUp, canCullToTheRight);
-    SkASSERT(count >= 0);
-
-    SkEdge**    list = builder.edgeList();
+    SkEdgeBuilder builder;
+    int count = builder.build_edges(path, &shiftedClip, shiftEdgesUp, pathContainedInClip);
+    SkEdge** list = builder.edgeList();
 
     if (0 == count) {
         if (path.isInverseFillType()) {
@@ -467,8 +460,8 @@
         proc = PrePostInverseBlitterProc;
     }
 
-    if (path.isConvex() && (nullptr == proc)) {
-        SkASSERT(count >= 2);   // convex walker does not handle missing right edges
+    // count >= 2 is required as the convex walker does not handle missing right edges
+    if (path.isConvex() && (nullptr == proc) && count >= 2) {
         walk_convex_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, nullptr);
     } else {
         walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc,
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
deleted file mode 100644
index cfdd8b9..0000000
--- a/src/core/SkShader.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkArenaAlloc.h"
-#include "SkAtomics.h"
-#include "SkBitmapProcShader.h"
-#include "SkColorShader.h"
-#include "SkEmptyShader.h"
-#include "SkMallocPixelRef.h"
-#include "SkPaint.h"
-#include "SkPicture.h"
-#include "SkPictureShader.h"
-#include "SkPM4fPriv.h"
-#include "SkRasterPipeline.h"
-#include "SkReadBuffer.h"
-#include "SkScalar.h"
-#include "SkShader.h"
-#include "SkTLazy.h"
-#include "SkWriteBuffer.h"
-
-#if SK_SUPPORT_GPU
-#include "GrFragmentProcessor.h"
-#endif
-
-//#define SK_TRACK_SHADER_LIFETIME
-
-#ifdef SK_TRACK_SHADER_LIFETIME
-    static int32_t gShaderCounter;
-#endif
-
-static inline void inc_shader_counter() {
-#ifdef SK_TRACK_SHADER_LIFETIME
-    int32_t prev = sk_atomic_inc(&gShaderCounter);
-    SkDebugf("+++ shader counter %d\n", prev + 1);
-#endif
-}
-static inline void dec_shader_counter() {
-#ifdef SK_TRACK_SHADER_LIFETIME
-    int32_t prev = sk_atomic_dec(&gShaderCounter);
-    SkDebugf("--- shader counter %d\n", prev - 1);
-#endif
-}
-
-SkShader::SkShader(const SkMatrix* localMatrix) {
-    inc_shader_counter();
-    if (localMatrix) {
-        fLocalMatrix = *localMatrix;
-    } else {
-        fLocalMatrix.reset();
-    }
-    // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe.
-    (void)fLocalMatrix.getType();
-}
-
-SkShader::~SkShader() {
-    dec_shader_counter();
-}
-
-void SkShader::flatten(SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    bool hasLocalM = !fLocalMatrix.isIdentity();
-    buffer.writeBool(hasLocalM);
-    if (hasLocalM) {
-        buffer.writeMatrix(fLocalMatrix);
-    }
-}
-
-bool SkShader::computeTotalInverse(const ContextRec& rec, SkMatrix* totalInverse) const {
-    SkMatrix total = SkMatrix::Concat(*rec.fMatrix, fLocalMatrix);
-    if (rec.fLocalMatrix) {
-        total.preConcat(*rec.fLocalMatrix);
-    }
-
-    return total.invert(totalInverse);
-}
-
-bool SkShader::asLuminanceColor(SkColor* colorPtr) const {
-    SkColor storage;
-    if (nullptr == colorPtr) {
-        colorPtr = &storage;
-    }
-    if (this->onAsLuminanceColor(colorPtr)) {
-        *colorPtr = SkColorSetA(*colorPtr, 0xFF);   // we only return opaque
-        return true;
-    }
-    return false;
-}
-
-SkShader::Context* SkShader::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
-    if (!this->computeTotalInverse(rec, nullptr)) {
-        return nullptr;
-    }
-    return this->onMakeContext(rec, alloc);
-}
-
-SkShader::Context::Context(const SkShader& shader, const ContextRec& rec)
-    : fShader(shader), fCTM(*rec.fMatrix)
-{
-    // Because the context parameters must be valid at this point, we know that the matrix is
-    // invertible.
-    SkAssertResult(fShader.computeTotalInverse(rec, &fTotalInverse));
-    fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse);
-
-    fPaintAlpha = rec.fPaint->getAlpha();
-}
-
-SkShader::Context::~Context() {}
-
-SkShader::Context::ShadeProc SkShader::Context::asAShadeProc(void** ctx) {
-    return nullptr;
-}
-
-void SkShader::Context::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
-    const int N = 128;
-    SkPMColor tmp[N];
-    while (count > 0) {
-        int n = SkTMin(count, N);
-        this->shadeSpan(x, y, tmp, n);
-        for (int i = 0; i < n; ++i) {
-            dst[i] = SkPM4f::FromPMColor(tmp[i]);
-        }
-        dst += n;
-        x += n;
-        count -= n;
-    }
-}
-
-#include "SkColorPriv.h"
-
-#define kTempColorQuadCount 6   // balance between speed (larger) and saving stack-space
-#define kTempColorCount     (kTempColorQuadCount << 2)
-
-#ifdef SK_CPU_BENDIAN
-    #define SkU32BitShiftToByteOffset(shift)    (3 - ((shift) >> 3))
-#else
-    #define SkU32BitShiftToByteOffset(shift)    ((shift) >> 3)
-#endif
-
-void SkShader::Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
-    SkASSERT(count > 0);
-
-    SkPMColor   colors[kTempColorCount];
-
-    while ((count -= kTempColorCount) >= 0) {
-        this->shadeSpan(x, y, colors, kTempColorCount);
-        x += kTempColorCount;
-
-        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
-        int quads = kTempColorQuadCount;
-        do {
-            U8CPU a0 = srcA[0];
-            U8CPU a1 = srcA[4];
-            U8CPU a2 = srcA[8];
-            U8CPU a3 = srcA[12];
-            srcA += 4*4;
-            *alpha++ = SkToU8(a0);
-            *alpha++ = SkToU8(a1);
-            *alpha++ = SkToU8(a2);
-            *alpha++ = SkToU8(a3);
-        } while (--quads != 0);
-    }
-    SkASSERT(count < 0);
-    SkASSERT(count + kTempColorCount >= 0);
-    if (count += kTempColorCount) {
-        this->shadeSpan(x, y, colors, count);
-
-        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
-        do {
-            *alpha++ = *srcA;
-            srcA += 4;
-        } while (--count != 0);
-    }
-#if 0
-    do {
-        int n = count;
-        if (n > kTempColorCount)
-            n = kTempColorCount;
-        SkASSERT(n > 0);
-
-        this->shadeSpan(x, y, colors, n);
-        x += n;
-        count -= n;
-
-        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
-        do {
-            *alpha++ = *srcA;
-            srcA += 4;
-        } while (--n != 0);
-    } while (count > 0);
-#endif
-}
-
-SkShader::Context::MatrixClass SkShader::Context::ComputeMatrixClass(const SkMatrix& mat) {
-    MatrixClass mc = kLinear_MatrixClass;
-
-    if (mat.hasPerspective()) {
-        if (mat.isFixedStepInX()) {
-            mc = kFixedStepInX_MatrixClass;
-        } else {
-            mc = kPerspective_MatrixClass;
-        }
-    }
-    return mc;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
-    return kNone_GradientType;
-}
-
-#if SK_SUPPORT_GPU
-sk_sp<GrFragmentProcessor> SkShader::asFragmentProcessor(const AsFPArgs&) const {
-    return nullptr;
-}
-#endif
-
-sk_sp<SkShader> SkShader::makeAsALocalMatrixShader(SkMatrix*) const {
-    return nullptr;
-}
-
-sk_sp<SkShader> SkShader::MakeEmptyShader() { return sk_make_sp<SkEmptyShader>(); }
-
-sk_sp<SkShader> SkShader::MakeColorShader(SkColor color) { return sk_make_sp<SkColorShader>(color); }
-
-sk_sp<SkShader> SkShader::MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy,
-                                           const SkMatrix* localMatrix) {
-    if (localMatrix && !localMatrix->invert(nullptr)) {
-        return nullptr;
-    }
-    return SkMakeBitmapShader(src, tmx, tmy, localMatrix, kIfMutable_SkCopyPixelsMode);
-}
-
-sk_sp<SkShader> SkShader::MakePictureShader(sk_sp<SkPicture> src, TileMode tmx, TileMode tmy,
-                                            const SkMatrix* localMatrix, const SkRect* tile) {
-    if (localMatrix && !localMatrix->invert(nullptr)) {
-        return nullptr;
-    }
-    return SkPictureShader::Make(std::move(src), tmx, tmy, localMatrix, tile);
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void SkShader::toString(SkString* str) const {
-    if (!fLocalMatrix.isIdentity()) {
-        str->append(" ");
-        fLocalMatrix.toString(str);
-    }
-}
-#endif
-
-bool SkShader::appendStages(SkRasterPipeline* pipeline,
-                            SkColorSpace* dst,
-                            SkArenaAlloc* scratch,
-                            const SkMatrix& ctm,
-                            const SkPaint& paint) const {
-    return this->onAppendStages(pipeline, dst, scratch, ctm, paint, nullptr);
-}
-
-bool SkShader::onAppendStages(SkRasterPipeline* p,
-                              SkColorSpace* cs,
-                              SkArenaAlloc* alloc,
-                              const SkMatrix& ctm,
-                              const SkPaint& paint,
-                              const SkMatrix* localM) const {
-    // Legacy shaders handle the paint opacity internally,
-    // but RP applies it as a separate stage.
-    SkTCopyOnFirstWrite<SkPaint> opaquePaint(paint);
-    if (paint.getAlpha() != SK_AlphaOPAQUE) {
-        opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE);
-    }
-
-    ContextRec rec(*opaquePaint, ctm, localM, ContextRec::kPM4f_DstType, cs);
-    if (auto* ctx = this->makeContext(rec, alloc)) {
-        p->append(SkRasterPipeline::shader_adapter, ctx);
-
-        // Legacy shaders aren't aware of color spaces. We can pretty
-        // safely assume they're in sRGB gamut.
-        return append_gamut_transform(p, alloc,
-                                      SkColorSpace::MakeSRGB().get(), cs);
-    }
-    return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
-    return SkShader::MakeEmptyShader();
-}
-
-#ifndef SK_IGNORE_TO_STRING
-#include "SkEmptyShader.h"
-
-void SkEmptyShader::toString(SkString* str) const {
-    str->append("SkEmptyShader: (");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/src/core/SkShadowShader.cpp b/src/core/SkShadowShader.cpp
deleted file mode 100644
index 5f4ce66..0000000
--- a/src/core/SkShadowShader.cpp
+++ /dev/null
@@ -1,954 +0,0 @@
-/*
- * 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 "SkCanvas.h"
-#include "SkReadBuffer.h"
-#include "SkShadowShader.h"
-
-////////////////////////////////////////////////////////////////////////////
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-
-/** \class SkShadowShaderImpl
-    This subclass of shader applies shadowing
-*/
-class SkShadowShaderImpl : public SkShader {
-public:
-    /** Create a new shadowing shader that shadows
-        @param to do        to do
-    */
-    SkShadowShaderImpl(sk_sp<SkShader> povDepthShader,
-                       sk_sp<SkShader> diffuseShader,
-                       sk_sp<SkLights> lights,
-                       int diffuseWidth, int diffuseHeight,
-                       const SkShadowParams& params)
-            : fPovDepthShader(std::move(povDepthShader))
-            , fDiffuseShader(std::move(diffuseShader))
-            , fLights(std::move(lights))
-            , fDiffuseWidth(diffuseWidth)
-            , fDiffuseHeight(diffuseHeight)
-            , fShadowParams(params) { }
-
-    bool isOpaque() const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    class ShadowShaderContext : public SkShader::Context {
-    public:
-        // The context takes ownership of the states. It will call their destructors
-        // but will NOT free the memory.
-        ShadowShaderContext(const SkShadowShaderImpl&, const ContextRec&,
-                            SkShader::Context* povDepthContext,
-                            SkShader::Context* diffuseContext,
-                            void* heapAllocated);
-
-        ~ShadowShaderContext() override;
-
-        void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
-        uint32_t getFlags() const override { return fFlags; }
-
-    private:
-        SkShader::Context*        fPovDepthContext;
-        SkShader::Context*        fDiffuseContext;
-        uint32_t                  fFlags;
-
-        void* fHeapAllocated;
-
-        int fNonAmbLightCnt;
-        SkPixmap* fShadowMapPixels;
-
-
-        typedef SkShader::Context INHERITED;
-    };
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkShadowShaderImpl)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    size_t onContextSize(const ContextRec&) const override;
-    Context* onCreateContext(const ContextRec&, void*) const override;
-
-private:
-    sk_sp<SkShader> fPovDepthShader;
-    sk_sp<SkShader> fDiffuseShader;
-    sk_sp<SkLights> fLights;
-
-    int fDiffuseWidth;
-    int fDiffuseHeight;
-
-    SkShadowParams fShadowParams;
-
-    friend class SkShadowShader;
-
-    typedef SkShader INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "GrCoordTransform.h"
-#include "GrFragmentProcessor.h"
-#include "GrInvariantOutput.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "SkGr.h"
-#include "SkSpecialImage.h"
-#include "SkImage_Base.h"
-#include "GrContext.h"
-
-class ShadowFP : public GrFragmentProcessor {
-public:
-    ShadowFP(sk_sp<GrFragmentProcessor> povDepth,
-             sk_sp<GrFragmentProcessor> diffuse,
-             sk_sp<SkLights> lights,
-             int diffuseWidth, int diffuseHeight,
-             const SkShadowParams& params,
-             GrContext* context) {
-
-        fAmbientColor = lights->ambientLightColor();
-
-        fNumNonAmbLights = 0; // count of non-ambient lights
-        for (int i = 0; i < lights->numLights(); ++i) {
-            if (fNumNonAmbLights < SkShadowShader::kMaxNonAmbientLights) {
-                fLightColor[fNumNonAmbLights] = lights->light(i).color();
-
-                if (SkLights::Light::kPoint_LightType == lights->light(i).type()) {
-                    fLightDirOrPos[fNumNonAmbLights] = lights->light(i).pos();
-                    fLightColor[fNumNonAmbLights].scale(lights->light(i).intensity());
-                } else {
-                    fLightDirOrPos[fNumNonAmbLights] = lights->light(i).dir();
-                }
-
-                fIsPointLight[fNumNonAmbLights] =
-                        SkLights::Light::kPoint_LightType == lights->light(i).type();
-
-                fIsRadialLight[fNumNonAmbLights] = lights->light(i).isRadial();
-
-                SkImage_Base* shadowMap = ((SkImage_Base*)lights->light(i).getShadowMap());
-
-                // gets deleted when the ShadowFP is destroyed, and frees the GrTexture*
-                fTexture[fNumNonAmbLights] = sk_sp<GrTexture>(shadowMap->asTextureRef(context,
-                                                           GrSamplerParams::ClampNoFilter(),
-                                                           SkDestinationSurfaceColorMode::kLegacy,
-                                                           nullptr));
-                fDepthMapSampler[fNumNonAmbLights].reset(fTexture[fNumNonAmbLights].get());
-                this->addTextureSampler(&fDepthMapSampler[fNumNonAmbLights]);
-
-                fDepthMapHeight[fNumNonAmbLights] = shadowMap->height();
-                fDepthMapWidth[fNumNonAmbLights] = shadowMap->width();
-
-                fNumNonAmbLights++;
-            }
-        }
-
-        fWidth = diffuseWidth;
-        fHeight = diffuseHeight;
-
-        fShadowParams = params;
-
-        this->registerChildProcessor(std::move(povDepth));
-        this->registerChildProcessor(std::move(diffuse));
-        this->initClassID<ShadowFP>();
-    }
-
-    class GLSLShadowFP : public GrGLSLFragmentProcessor {
-    public:
-        GLSLShadowFP() { }
-
-        void emitCode(EmitArgs& args) override {
-            GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
-            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-            const ShadowFP& shadowFP = args.fFp.cast<ShadowFP>();
-
-            SkASSERT(shadowFP.fNumNonAmbLights <= SkShadowShader::kMaxNonAmbientLights);
-
-            // add uniforms
-            int32_t numLights = shadowFP.fNumNonAmbLights;
-            SkASSERT(numLights <= SkShadowShader::kMaxNonAmbientLights);
-
-            int blurAlgorithm = shadowFP.fShadowParams.fType;
-
-            const char* lightDirOrPosUniName[SkShadowShader::kMaxNonAmbientLights] = {nullptr};
-            const char* lightColorUniName[SkShadowShader::kMaxNonAmbientLights] = {nullptr};
-            const char* ambientColorUniName = nullptr;
-
-            const char* depthMapWidthUniName[SkShadowShader::kMaxNonAmbientLights] = {nullptr};
-            const char* depthMapHeightUniName[SkShadowShader::kMaxNonAmbientLights] = {nullptr};
-            const char* widthUniName = nullptr; // dimensions of povDepth
-            const char* heightUniName = nullptr;
-
-            const char* shBiasUniName = nullptr;
-            const char* minVarianceUniName = nullptr;
-
-            // setting uniforms
-            for (int i = 0; i < shadowFP.fNumNonAmbLights; i++) {
-                SkString lightDirOrPosUniNameStr("lightDir");
-                lightDirOrPosUniNameStr.appendf("%d", i);
-                SkString lightColorUniNameStr("lightColor");
-                lightColorUniNameStr.appendf("%d", i);
-                SkString lightIntensityUniNameStr("lightIntensity");
-                lightIntensityUniNameStr.appendf("%d", i);
-
-                SkString depthMapWidthUniNameStr("dmapWidth");
-                depthMapWidthUniNameStr.appendf("%d", i);
-                SkString depthMapHeightUniNameStr("dmapHeight");
-                depthMapHeightUniNameStr.appendf("%d", i);
-
-                fLightDirOrPosUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                             kVec3f_GrSLType,
-                                                             kDefault_GrSLPrecision,
-                                                             lightDirOrPosUniNameStr.c_str(),
-                                                             &lightDirOrPosUniName[i]);
-                fLightColorUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                               kVec3f_GrSLType,
-                                                               kDefault_GrSLPrecision,
-                                                               lightColorUniNameStr.c_str(),
-                                                               &lightColorUniName[i]);
-
-                fDepthMapWidthUni[i]  = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kInt_GrSLType,
-                                                   kDefault_GrSLPrecision,
-                                                   depthMapWidthUniNameStr.c_str(),
-                                                   &depthMapWidthUniName[i]);
-                fDepthMapHeightUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kInt_GrSLType,
-                                                   kDefault_GrSLPrecision,
-                                                   depthMapHeightUniNameStr.c_str(),
-                                                   &depthMapHeightUniName[i]);
-            }
-
-            fBiasingConstantUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                             kFloat_GrSLType,
-                                                             kDefault_GrSLPrecision,
-                                                             "shadowBias", &shBiasUniName);
-            fMinVarianceUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                         kFloat_GrSLType,
-                                                         kDefault_GrSLPrecision,
-                                                         "minVariance", &minVarianceUniName);
-
-            fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kInt_GrSLType,
-                                                   kDefault_GrSLPrecision,
-                                                   "width", &widthUniName);
-            fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                    kInt_GrSLType,
-                                                    kDefault_GrSLPrecision,
-                                                    "height", &heightUniName);
-
-            fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                          kVec3f_GrSLType, kDefault_GrSLPrecision,
-                                                          "AmbientColor", &ambientColorUniName);
-
-            SkString povDepthSampler("_povDepth");
-            SkString povDepth("povDepth");
-            this->emitChild(0, nullptr, &povDepthSampler, args);
-            fragBuilder->codeAppendf("vec4 %s = %s;", povDepth.c_str(), povDepthSampler.c_str());
-
-            SkString diffuseColorSampler("_inDiffuseColor");
-            SkString diffuseColor("inDiffuseColor");
-            this->emitChild(1, nullptr, &diffuseColorSampler, args);
-            fragBuilder->codeAppendf("vec4 %s = %s;", diffuseColor.c_str(),
-                                     diffuseColorSampler.c_str());
-
-            SkString depthMaps[SkShadowShader::kMaxNonAmbientLights];
-
-            fragBuilder->codeAppendf("vec4 resultDiffuseColor = %s;", diffuseColor.c_str());
-            fragBuilder->codeAppend ("vec3 totalLightColor = vec3(0);");
-
-            // probability that a fragment is lit. For each light, we multiply this by the
-            // light's color to get its contribution to totalLightColor.
-            fragBuilder->codeAppend ("float lightProbability;");
-
-            // coordinates of current fragment in world space
-            fragBuilder->codeAppend ("vec3 worldCor;");
-
-            // Multiply by 255 to transform from sampler coordinates to world
-            // coordinates (since 1 channel is 0xFF)
-            // Note: vMatrixCoord_0_1_Stage0 is the texture sampler coordinates.
-            fragBuilder->codeAppendf("worldCor = vec3(vMatrixCoord_0_1_Stage0 * "
-                                                "vec2(%s, %s), %s.b * 255);",
-                                     widthUniName, heightUniName, povDepth.c_str());
-
-            // Applies the offset indexing that goes from our view space into the light's space.
-            for (int i = 0; i < shadowFP.fNumNonAmbLights; i++) {
-                SkString povCoord("povCoord");
-                povCoord.appendf("%d", i);
-
-                SkString offset("offset");
-                offset.appendf("%d", i);
-                fragBuilder->codeAppendf("vec2 %s;", offset.c_str());
-
-                if (shadowFP.fIsPointLight[i]) {
-                    fragBuilder->codeAppendf("vec3 fragToLight%d = %s - worldCor;",
-                                             i, lightDirOrPosUniName[i]);
-                    fragBuilder->codeAppendf("float dist%d = length(fragToLight%d);",
-                                             i, i);
-                    fragBuilder->codeAppendf("%s = vec2(-fragToLight%d) * povDepth.b;",
-                                             offset.c_str(), i);
-                    fragBuilder->codeAppendf("fragToLight%d = normalize(fragToLight%d);",
-                                             i, i);
-                }
-
-                if (shadowFP.fIsRadialLight[i]) {
-                    fragBuilder->codeAppendf("vec2 %s = vec2(vMatrixCoord_0_1_Stage0.x, "
-                                                            "1 - vMatrixCoord_0_1_Stage0.y);\n",
-                                             povCoord.c_str());
-
-                    fragBuilder->codeAppendf("%s = (%s) * 2.0 - 1.0 + (vec2(%s)/vec2(%s,%s) - 0.5)"
-                                                                      "* vec2(-2.0, 2.0);\n",
-                                             povCoord.c_str(), povCoord.c_str(),
-                                             lightDirOrPosUniName[i],
-                                             widthUniName, heightUniName);
-
-                    fragBuilder->codeAppendf("float theta = atan(%s.y, %s.x);",
-                                             povCoord.c_str(), povCoord.c_str());
-                    fragBuilder->codeAppendf("float r = length(%s);", povCoord.c_str());
-
-                    // map output of atan to [0, 1]
-                    fragBuilder->codeAppendf("%s.x = (theta + 3.1415) / (2.0 * 3.1415);",
-                                             povCoord.c_str());
-                    fragBuilder->codeAppendf("%s.y = 0.0;", povCoord.c_str());
-                } else {
-                    // note that we flip the y-coord of the offset and then later add
-                    // a value just to the y-coord of povCoord. This is to account for
-                    // the shifted origins from switching from raster into GPU.
-                    if (shadowFP.fIsPointLight[i]) {
-                        // the 0.375s are precalculated transform values, given that the depth
-                        // maps for pt lights are 4x the size (linearly) as diffuse maps.
-                        // The vec2(0.375, -0.375) is used to transform us to
-                        // the center of the map.
-                        fragBuilder->codeAppendf("vec2 %s = ((vec2(%s, %s) *"
-                                                         "vMatrixCoord_0_1_Stage0 +"
-                                                         "vec2(0,%s - %s)"
-                                                         "+ %s) / (vec2(%s, %s))) +"
-                                                         "vec2(0.375, -0.375);",
-                                                 povCoord.c_str(),
-                                                 widthUniName, heightUniName,
-                                                 depthMapHeightUniName[i], heightUniName,
-                                                 offset.c_str(),
-                                                 depthMapWidthUniName[i],
-                                                 depthMapWidthUniName[i]);
-                    } else {
-                        fragBuilder->codeAppendf("%s = vec2(%s) * povDepth.b * "
-                                                      "vec2(255.0, -255.0);",
-                                                 offset.c_str(), lightDirOrPosUniName[i]);
-
-                        fragBuilder->codeAppendf("vec2 %s = ((vec2(%s, %s) *"
-                                                         "vMatrixCoord_0_1_Stage0 +"
-                                                         "vec2(0,%s - %s)"
-                                                         "+ %s) / vec2(%s, %s));",
-                                                 povCoord.c_str(),
-                                                 widthUniName, heightUniName,
-                                                 depthMapHeightUniName[i], heightUniName,
-                                                 offset.c_str(),
-                                                 depthMapWidthUniName[i],
-                                                 depthMapWidthUniName[i]);
-                    }
-                }
-
-                fragBuilder->appendTextureLookup(&depthMaps[i], args.fTexSamplers[i],
-                                                 povCoord.c_str(),
-                                                 kVec2f_GrSLType);
-            }
-
-            // helper variables for calculating shadowing
-
-            // variance of depth at this fragment in the context of surrounding area
-            // (area size and weighting dependent on blur size and type)
-            fragBuilder->codeAppendf("float variance;");
-
-            // the difference in depth between the user POV and light POV.
-            fragBuilder->codeAppendf("float d;");
-
-            // add up light contributions from all lights to totalLightColor
-            for (int i = 0; i < numLights; i++) {
-                fragBuilder->codeAppendf("lightProbability = 1;");
-
-                if (shadowFP.fIsRadialLight[i]) {
-                    fragBuilder->codeAppend("totalLightColor = vec3(0);");
-
-                    fragBuilder->codeAppend("vec2 tc = vec2(povCoord0.x, 0.0);");
-                    fragBuilder->codeAppend("float depth = texture(uTextureSampler0_Stage1,"
-                                                                  "povCoord0).b * 2.0;");
-
-                    fragBuilder->codeAppendf("lightProbability = step(r, depth);");
-
-                    // 2 is the maximum depth. If this is reached, probably we have
-                    // not intersected anything. So values after this should be unshadowed.
-                    fragBuilder->codeAppendf("if (%s.b != 0 || depth == 2) {"
-                                                     "lightProbability = 1.0; }",
-                                             povDepth.c_str());
-                } else {
-                    // 1/512 == .00195... is less than half a pixel; imperceptible
-                    fragBuilder->codeAppendf("if (%s.b <= %s.b + .001953125) {",
-                                             povDepth.c_str(), depthMaps[i].c_str());
-                    if (blurAlgorithm == SkShadowParams::kVariance_ShadowType) {
-                        // We mess with depth and depth^2 in their given scales.
-                        // (i.e. between 0 and 1)
-                        fragBuilder->codeAppendf("vec2 moments%d = vec2(%s.b, %s.g);",
-                                                 i, depthMaps[i].c_str(), depthMaps[i].c_str());
-
-                        // variance biasing lessens light bleeding
-                        fragBuilder->codeAppendf("variance = max(moments%d.y - "
-                                                         "(moments%d.x * moments%d.x),"
-                                                         "%s);", i, i, i,
-                                                 minVarianceUniName);
-
-                        fragBuilder->codeAppendf("d = (%s.b) - moments%d.x;",
-                                                 povDepth.c_str(), i);
-                        fragBuilder->codeAppendf("lightProbability = "
-                                                         "(variance / (variance + d * d));");
-
-                        SkString clamp("clamp");
-                        clamp.appendf("%d", i);
-
-                        // choosing between light artifacts or correct shape shadows
-                        // linstep
-                        fragBuilder->codeAppendf("float %s = clamp((lightProbability - %s) /"
-                                                         "(1 - %s), 0, 1);",
-                                                 clamp.c_str(), shBiasUniName, shBiasUniName);
-
-                        fragBuilder->codeAppendf("lightProbability = %s;", clamp.c_str());
-                    } else {
-                        fragBuilder->codeAppendf("if (%s.b >= %s.b) {",
-                                                 povDepth.c_str(), depthMaps[i].c_str());
-                        fragBuilder->codeAppendf("lightProbability = 1;");
-                        fragBuilder->codeAppendf("} else { lightProbability = 0; }");
-                    }
-
-                    // VSM: The curved shadows near plane edges are artifacts from blurring
-                    // lightDir.z is equal to the lightDir dot the surface normal.
-                    fragBuilder->codeAppendf("}");
-                }
-
-                if (shadowFP.isPointLight(i)) {
-                    fragBuilder->codeAppendf("totalLightColor += max(fragToLight%d.z, 0) * %s /"
-                                                     "(1 + dist%d) * lightProbability;",
-                                             i, lightColorUniName[i], i);
-                } else {
-                    fragBuilder->codeAppendf("totalLightColor += %s.z * %s * lightProbability;",
-                                             lightDirOrPosUniName[i],
-                                             lightColorUniName[i]);
-                }
-
-                fragBuilder->codeAppendf("totalLightColor += %s;", ambientColorUniName);
-                fragBuilder->codeAppendf("%s = resultDiffuseColor * vec4(totalLightColor, 1);",
-                                         args.fOutputColor);
-            }
-
-        }
-
-        static void GenKey(const GrProcessor& proc, const GrShaderCaps&,
-                           GrProcessorKeyBuilder* b) {
-            const ShadowFP& shadowFP = proc.cast<ShadowFP>();
-            b->add32(shadowFP.fNumNonAmbLights);
-            int isPLR = 0;
-            for (int i = 0; i < SkShadowShader::kMaxNonAmbientLights; i++) {
-                isPLR = isPLR | ((shadowFP.fIsPointLight[i] ? 1 : 0) << i);
-                isPLR = isPLR | ((shadowFP.fIsRadialLight[i] ? 1 : 0) << (i+4));
-            }
-            b->add32(isPLR);
-            b->add32(shadowFP.fShadowParams.fType);
-        }
-
-    protected:
-        void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
-            const ShadowFP &shadowFP = proc.cast<ShadowFP>();
-
-            for (int i = 0; i < shadowFP.numLights(); i++) {
-                const SkVector3& lightDirOrPos = shadowFP.lightDirOrPos(i);
-                if (lightDirOrPos != fLightDirOrPos[i]) {
-                    pdman.set3fv(fLightDirOrPosUni[i], 1, &lightDirOrPos.fX);
-                    fLightDirOrPos[i] = lightDirOrPos;
-                }
-
-                const SkColor3f& lightColor = shadowFP.lightColor(i);
-                if (lightColor != fLightColor[i]) {
-                    pdman.set3fv(fLightColorUni[i], 1, &lightColor.fX);
-                    fLightColor[i] = lightColor;
-                }
-
-                int depthMapWidth = shadowFP.depthMapWidth(i);
-                if (depthMapWidth != fDepthMapWidth[i]) {
-                    pdman.set1i(fDepthMapWidthUni[i], depthMapWidth);
-                    fDepthMapWidth[i] = depthMapWidth;
-                }
-                int depthMapHeight = shadowFP.depthMapHeight(i);
-                if (depthMapHeight != fDepthMapHeight[i]) {
-                    pdman.set1i(fDepthMapHeightUni[i], depthMapHeight);
-                    fDepthMapHeight[i] = depthMapHeight;
-                }
-            }
-
-            SkScalar biasingConstant = shadowFP.shadowParams().fBiasingConstant;
-            if (biasingConstant != fBiasingConstant) {
-                pdman.set1f(fBiasingConstantUni, biasingConstant);
-                fBiasingConstant = biasingConstant;
-            }
-
-            SkScalar minVariance = shadowFP.shadowParams().fMinVariance;
-            if (minVariance != fMinVariance) {
-                // transform variance from pixel-scale to normalized scale
-                pdman.set1f(fMinVarianceUni, minVariance / 65536.0f);
-                fMinVariance = minVariance / 65536.0f;
-            }
-
-            int width = shadowFP.width();
-            if (width != fWidth) {
-                pdman.set1i(fWidthUni, width);
-                fWidth = width;
-            }
-            int height = shadowFP.height();
-            if (height != fHeight) {
-                pdman.set1i(fHeightUni, height);
-                fHeight = height;
-            }
-
-            const SkColor3f& ambientColor = shadowFP.ambientColor();
-            if (ambientColor != fAmbientColor) {
-                pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
-                fAmbientColor = ambientColor;
-            }
-        }
-
-    private:
-        SkVector3 fLightDirOrPos[SkShadowShader::kMaxNonAmbientLights];
-        GrGLSLProgramDataManager::UniformHandle
-                fLightDirOrPosUni[SkShadowShader::kMaxNonAmbientLights];
-
-        SkColor3f fLightColor[SkShadowShader::kMaxNonAmbientLights];
-        GrGLSLProgramDataManager::UniformHandle
-                fLightColorUni[SkShadowShader::kMaxNonAmbientLights];
-
-        int fDepthMapWidth[SkShadowShader::kMaxNonAmbientLights];
-        GrGLSLProgramDataManager::UniformHandle
-                fDepthMapWidthUni[SkShadowShader::kMaxNonAmbientLights];
-
-        int fDepthMapHeight[SkShadowShader::kMaxNonAmbientLights];
-        GrGLSLProgramDataManager::UniformHandle
-                fDepthMapHeightUni[SkShadowShader::kMaxNonAmbientLights];
-
-        int fWidth;
-        GrGLSLProgramDataManager::UniformHandle fWidthUni;
-        int fHeight;
-        GrGLSLProgramDataManager::UniformHandle fHeightUni;
-
-        SkScalar fBiasingConstant;
-        GrGLSLProgramDataManager::UniformHandle fBiasingConstantUni;
-        SkScalar fMinVariance;
-        GrGLSLProgramDataManager::UniformHandle fMinVarianceUni;
-
-        SkColor3f fAmbientColor;
-        GrGLSLProgramDataManager::UniformHandle fAmbientColorUni;
-    };
-
-    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLShadowFP::GenKey(*this, caps, b);
-    }
-
-    const char* name() const override { return "shadowFP"; }
-
-    int32_t numLights() const { return fNumNonAmbLights; }
-    const SkColor3f& ambientColor() const { return fAmbientColor; }
-    bool isPointLight(int i) const {
-        SkASSERT(i < fNumNonAmbLights);
-        return fIsPointLight[i];
-    }
-    bool isRadialLight(int i) const {
-        SkASSERT(i < fNumNonAmbLights);
-        return fIsRadialLight[i];
-    }
-    const SkVector3& lightDirOrPos(int i) const {
-        SkASSERT(i < fNumNonAmbLights);
-        return fLightDirOrPos[i];
-    }
-    const SkVector3& lightColor(int i) const {
-        SkASSERT(i < fNumNonAmbLights);
-        return fLightColor[i];
-    }
-    int depthMapWidth(int i) const {
-        SkASSERT(i < fNumNonAmbLights);
-        return fDepthMapWidth[i];
-    }
-    int depthMapHeight(int i) const {
-        SkASSERT(i < fNumNonAmbLights);
-        return fDepthMapHeight[i];
-    }
-    int width() const {return fWidth; }
-    int height() const {return fHeight; }
-
-    const SkShadowParams& shadowParams() const {return fShadowParams; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLShadowFP; }
-
-    bool onIsEqual(const GrFragmentProcessor& proc) const override {
-        const ShadowFP& shadowFP = proc.cast<ShadowFP>();
-        if (fAmbientColor != shadowFP.fAmbientColor ||
-            fNumNonAmbLights != shadowFP.fNumNonAmbLights) {
-            return false;
-        }
-
-        if (fWidth != shadowFP.fWidth || fHeight != shadowFP.fHeight) {
-            return false;
-        }
-
-        for (int i = 0; i < fNumNonAmbLights; i++) {
-            if (fLightDirOrPos[i] != shadowFP.fLightDirOrPos[i] ||
-                fLightColor[i] != shadowFP.fLightColor[i] ||
-                fIsPointLight[i] != shadowFP.fIsPointLight[i] ||
-                fIsRadialLight[i] != shadowFP.fIsRadialLight[i]) {
-                return false;
-            }
-
-            if (fDepthMapWidth[i] != shadowFP.fDepthMapWidth[i] ||
-                fDepthMapHeight[i] != shadowFP.fDepthMapHeight[i]) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    int              fNumNonAmbLights;
-
-    bool             fIsPointLight[SkShadowShader::kMaxNonAmbientLights];
-    bool             fIsRadialLight[SkShadowShader::kMaxNonAmbientLights];
-    SkVector3        fLightDirOrPos[SkShadowShader::kMaxNonAmbientLights];
-    SkColor3f        fLightColor[SkShadowShader::kMaxNonAmbientLights];
-    TextureSampler   fDepthMapSampler[SkShadowShader::kMaxNonAmbientLights];
-    sk_sp<GrTexture> fTexture[SkShadowShader::kMaxNonAmbientLights];
-
-    int              fDepthMapWidth[SkShadowShader::kMaxNonAmbientLights];
-    int              fDepthMapHeight[SkShadowShader::kMaxNonAmbientLights];
-
-    int              fHeight;
-    int              fWidth;
-
-    SkShadowParams   fShadowParams;
-
-    SkColor3f        fAmbientColor;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkShadowShaderImpl::asFragmentProcessor(const AsFPArgs& fpargs) const {
-
-    sk_sp<GrFragmentProcessor> povDepthFP = fPovDepthShader->asFragmentProcessor(fpargs);
-
-    sk_sp<GrFragmentProcessor> diffuseFP = fDiffuseShader->asFragmentProcessor(fpargs);
-
-    sk_sp<GrFragmentProcessor> shadowfp = sk_make_sp<ShadowFP>(std::move(povDepthFP),
-                                                               std::move(diffuseFP),
-                                                               std::move(fLights),
-                                                               fDiffuseWidth, fDiffuseHeight,
-                                                               fShadowParams, fpargs.fContext);
-    return shadowfp;
-}
-
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////
-
-bool SkShadowShaderImpl::isOpaque() const {
-    return fDiffuseShader->isOpaque();
-}
-
-SkShadowShaderImpl::ShadowShaderContext::ShadowShaderContext(
-        const SkShadowShaderImpl& shader, const ContextRec& rec,
-        SkShader::Context* povDepthContext,
-        SkShader::Context* diffuseContext,
-        void* heapAllocated)
-        : INHERITED(shader, rec)
-        , fPovDepthContext(povDepthContext)
-        , fDiffuseContext(diffuseContext)
-        , fHeapAllocated(heapAllocated) {
-    bool isOpaque = shader.isOpaque();
-
-    // update fFlags
-    uint32_t flags = 0;
-    if (isOpaque && (255 == this->getPaintAlpha())) {
-        flags |= kOpaqueAlpha_Flag;
-    }
-
-    fFlags = flags;
-
-    const SkShadowShaderImpl& lightShader = static_cast<const SkShadowShaderImpl&>(fShader);
-
-    fNonAmbLightCnt = lightShader.fLights->numLights();
-    fShadowMapPixels = new SkPixmap[fNonAmbLightCnt];
-
-    for (int i = 0; i < fNonAmbLightCnt; i++) {
-        if (lightShader.fLights->light(i).type() == SkLights::Light::kDirectional_LightType) {
-            lightShader.fLights->light(i).getShadowMap()->
-                    peekPixels(&fShadowMapPixels[i]);
-        }
-    }
-}
-
-SkShadowShaderImpl::ShadowShaderContext::~ShadowShaderContext() {
-    delete[] fShadowMapPixels;
-
-    // The dependencies have been created outside of the context on memory that was allocated by
-    // the onCreateContext() method. Call the destructors and free the memory.
-    fPovDepthContext->~Context();
-    fDiffuseContext->~Context();
-
-    sk_free(fHeapAllocated);
-}
-
-static inline SkPMColor convert(SkColor3f color, U8CPU a) {
-    if (color.fX <= 0.0f) {
-        color.fX = 0.0f;
-    } else if (color.fX >= 255.0f) {
-        color.fX = 255.0f;
-    }
-
-    if (color.fY <= 0.0f) {
-        color.fY = 0.0f;
-    } else if (color.fY >= 255.0f) {
-        color.fY = 255.0f;
-    }
-
-    if (color.fZ <= 0.0f) {
-        color.fZ = 0.0f;
-    } else if (color.fZ >= 255.0f) {
-        color.fZ = 255.0f;
-    }
-
-    return SkPreMultiplyARGB(a, (int) color.fX,  (int) color.fY, (int) color.fZ);
-}
-
-// larger is better (fewer times we have to loop), but we shouldn't
-// take up too much stack-space (each one here costs 16 bytes)
-#define BUFFER_MAX 16
-void SkShadowShaderImpl::ShadowShaderContext::shadeSpan(int x, int y,
-                                                        SkPMColor result[], int count) {
-    const SkShadowShaderImpl& lightShader = static_cast<const SkShadowShaderImpl&>(fShader);
-
-    SkPMColor diffuse[BUFFER_MAX];
-    SkPMColor povDepth[BUFFER_MAX];
-
-    do {
-        int n = SkTMin(count, BUFFER_MAX);
-
-        fDiffuseContext->shadeSpan(x, y, diffuse, n);
-        fPovDepthContext->shadeSpan(x, y, povDepth, n);
-
-        for (int i = 0; i < n; ++i) {
-            SkColor diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]);
-            SkColor povDepthColor = povDepth[i];
-
-            SkColor3f totalLight = lightShader.fLights->ambientLightColor();
-            // This is all done in linear unpremul color space (each component 0..255.0f though)
-
-            for (int l = 0; l < lightShader.fLights->numLights(); ++l) {
-                const SkLights::Light& light = lightShader.fLights->light(l);
-
-                int pvDepth = SkColorGetB(povDepthColor); // depth stored in blue channel
-
-                if (light.type() == SkLights::Light::kDirectional_LightType) {
-
-                    int xOffset = SkScalarRoundToInt(light.dir().fX * pvDepth);
-                    int yOffset = SkScalarRoundToInt(light.dir().fY * pvDepth);
-
-                    int shX = SkClampMax(x + i + xOffset, light.getShadowMap()->width() - 1);
-                    int shY = SkClampMax(y + yOffset, light.getShadowMap()->height() - 1);
-
-                    int shDepth = 0;
-                    int shDepthsq = 0;
-
-                    // pixmaps that point to things have nonzero heights
-                    if (fShadowMapPixels[l].height() > 0) {
-                        uint32_t pix = *fShadowMapPixels[l].addr32(shX, shY);
-                        SkColor shColor(pix);
-
-                        shDepth = SkColorGetB(shColor);
-                        shDepthsq = SkColorGetG(shColor) * 256;
-                    } else {
-                        // Make lights w/o a shadow map receive the full light contribution
-                        shDepth = pvDepth;
-                    }
-
-                    SkScalar lightProb = 1.0f;
-                    if (pvDepth < shDepth) {
-                        if (lightShader.fShadowParams.fType ==
-                            SkShadowParams::ShadowType::kVariance_ShadowType) {
-                            int variance = SkMaxScalar(shDepthsq - shDepth * shDepth,
-                                                       lightShader.fShadowParams.fMinVariance);
-                            int d = pvDepth - shDepth;
-
-                            lightProb = (SkScalar) variance / ((SkScalar) (variance + d * d));
-
-                            SkScalar bias = lightShader.fShadowParams.fBiasingConstant;
-
-                            lightProb = SkMaxScalar((lightProb - bias) / (1.0f - bias), 0.0f);
-                        } else {
-                            lightProb = 0.0f;
-                        }
-                    }
-
-                    // assume object normals are pointing straight up
-                    totalLight.fX += light.dir().fZ * light.color().fX * lightProb;
-                    totalLight.fY += light.dir().fZ * light.color().fY * lightProb;
-                    totalLight.fZ += light.dir().fZ * light.color().fZ * lightProb;
-
-                } else {
-                    // right now we only expect directional and point light types.
-                    SkASSERT(light.type() == SkLights::Light::kPoint_LightType);
-
-                    int height = lightShader.fDiffuseHeight;
-
-                    SkVector3 fragToLight = SkVector3::Make(light.pos().fX - x - i,
-                                                            light.pos().fY - (height - y),
-                                                            light.pos().fZ - pvDepth);
-
-                    SkScalar dist = fragToLight.length();
-                    SkScalar normalizedZ = fragToLight.fZ / dist;
-
-                    SkScalar distAttenuation = light.intensity() / (1.0f + dist);
-
-                    // assume object normals are pointing straight up
-                    totalLight.fX += normalizedZ * light.color().fX * distAttenuation;
-                    totalLight.fY += normalizedZ * light.color().fY * distAttenuation;
-                    totalLight.fZ += normalizedZ * light.color().fZ * distAttenuation;
-                }
-            }
-
-            SkColor3f totalColor = SkColor3f::Make(SkColorGetR(diffColor) * totalLight.fX,
-                                                   SkColorGetG(diffColor) * totalLight.fY,
-                                                   SkColorGetB(diffColor) * totalLight.fZ);
-
-            result[i] = convert(totalColor, SkColorGetA(diffColor));
-        }
-
-        result += n;
-        x += n;
-        count -= n;
-    } while (count > 0);
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-#ifndef SK_IGNORE_TO_STRING
-void SkShadowShaderImpl::toString(SkString* str) const {
-    str->appendf("ShadowShader: ()");
-}
-#endif
-
-sk_sp<SkFlattenable> SkShadowShaderImpl::CreateProc(SkReadBuffer& buf) {
-
-    // Discarding SkShader flattenable params
-    bool hasLocalMatrix = buf.readBool();
-    SkAssertResult(!hasLocalMatrix);
-
-    sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf);
-
-    SkShadowParams params;
-    params.fMinVariance = buf.readScalar();
-    params.fBiasingConstant = buf.readScalar();
-    params.fType = (SkShadowParams::ShadowType) buf.readInt();
-    params.fShadowRadius = buf.readScalar();
-
-    int diffuseWidth = buf.readInt();
-    int diffuseHeight = buf.readInt();
-
-    sk_sp<SkShader> povDepthShader(buf.readFlattenable<SkShader>());
-    sk_sp<SkShader> diffuseShader(buf.readFlattenable<SkShader>());
-
-    return sk_make_sp<SkShadowShaderImpl>(std::move(povDepthShader),
-                                          std::move(diffuseShader),
-                                          std::move(lights),
-                                          diffuseWidth, diffuseHeight,
-                                          params);
-}
-
-void SkShadowShaderImpl::flatten(SkWriteBuffer& buf) const {
-    this->INHERITED::flatten(buf);
-
-    fLights->flatten(buf);
-
-    buf.writeScalar(fShadowParams.fMinVariance);
-    buf.writeScalar(fShadowParams.fBiasingConstant);
-    buf.writeInt(fShadowParams.fType);
-    buf.writeScalar(fShadowParams.fShadowRadius);
-
-    buf.writeInt(fDiffuseWidth);
-    buf.writeInt(fDiffuseHeight);
-
-    buf.writeFlattenable(fPovDepthShader.get());
-    buf.writeFlattenable(fDiffuseShader.get());
-}
-
-size_t SkShadowShaderImpl::onContextSize(const ContextRec& rec) const {
-    return sizeof(ShadowShaderContext);
-}
-
-SkShader::Context* SkShadowShaderImpl::onCreateContext(const ContextRec& rec,
-                                                       void* storage) const {
-    size_t heapRequired = fPovDepthShader->contextSize(rec) +
-                          fDiffuseShader->contextSize(rec);
-
-    void* heapAllocated = sk_malloc_throw(heapRequired);
-
-    void* povDepthContextStorage = heapAllocated;
-
-    SkShader::Context* povDepthContext =
-            fPovDepthShader->createContext(rec, povDepthContextStorage);
-
-    if (!povDepthContext) {
-        sk_free(heapAllocated);
-        return nullptr;
-    }
-
-    void* diffuseContextStorage = (char*)heapAllocated + fPovDepthShader->contextSize(rec);
-
-    SkShader::Context* diffuseContext = fDiffuseShader->createContext(rec, diffuseContextStorage);
-    if (!diffuseContext) {
-        sk_free(heapAllocated);
-        return nullptr;
-    }
-
-    return new (storage) ShadowShaderContext(*this, rec, povDepthContext, diffuseContext,
-                                             heapAllocated);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkShader> SkShadowShader::Make(sk_sp<SkShader> povDepthShader,
-                                     sk_sp<SkShader> diffuseShader,
-                                     sk_sp<SkLights> lights,
-                                     int diffuseWidth, int diffuseHeight,
-                                     const SkShadowParams& params) {
-    if (!povDepthShader || !diffuseShader) {
-        // TODO: Use paint's color in absence of a diffuseShader
-        // TODO: Use a default implementation of normalSource instead
-        return nullptr;
-    }
-
-    return sk_make_sp<SkShadowShaderImpl>(std::move(povDepthShader),
-                                          std::move(diffuseShader),
-                                          std::move(lights),
-                                          diffuseWidth, diffuseHeight,
-                                          params);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShadowShader)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkShadowShaderImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-///////////////////////////////////////////////////////////////////////////////
-
-#endif
diff --git a/src/core/SkShadowShader.h b/src/core/SkShadowShader.h
deleted file mode 100644
index ea05cca..0000000
--- a/src/core/SkShadowShader.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkShadowShader_DEFINED
-#define SkShadowShader_DEFINED
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-class SkLights;
-class SkShader;
-
-class SK_API SkShadowShader {
-public:
-    /** This shader combines the diffuse color in 'diffuseShader' with the shadows
-     *  determined by the 'povDepthShader' and the shadow maps stored in each of the
-     *  lights in 'lights'
-     *
-     *  Please note that the shadow shader is required to be in Stage0, otherwise
-     *  the texture coords will be wrong within the shader.
-     */
-    static sk_sp<SkShader> Make(sk_sp<SkShader> povDepthShader,
-                                sk_sp<SkShader> diffuseShader,
-                                sk_sp<SkLights> lights,
-                                int diffuseWidth, int diffuseHeight,
-                                const SkShadowParams& params);
-
-    // The shadow shader supports any number of ambient lights, but only
-    // 4 non-ambient lights (currently just refers to directional lights).
-    static constexpr int kMaxNonAmbientLights = 4;
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-};
-
-#endif
-#endif
diff --git a/src/core/SkSpanProcs.cpp b/src/core/SkSpanProcs.cpp
deleted file mode 100644
index 32237d5..0000000
--- a/src/core/SkSpanProcs.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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 "SkColorFilter.h"
-#include "SkHalf.h"
-#include "SkNx.h"
-#include "SkPaint.h"
-#include "SkPixmap.h"
-#include "SkPM4f.h"
-#include "SkPM4fPriv.h"
-#include "SkSpanProcs.h"
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static void load_l32(const SkPixmap& src, int x, int y, SkPM4f span[], int count) {
-    SkASSERT(count > 0);
-    const uint32_t* addr = src.addr32(x, y);
-    SkASSERT(src.addr32(x + count - 1, y));
-
-    for (int i = 0; i < count; ++i) {
-        swizzle_rb_if_bgra(Sk4f_fromL32(addr[i])).store(span[i].fVec);
-    }
-}
-
-static void load_s32(const SkPixmap& src, int x, int y, SkPM4f span[], int count) {
-    SkASSERT(count > 0);
-    const uint32_t* addr = src.addr32(x, y);
-    SkASSERT(src.addr32(x + count - 1, y));
-
-    for (int i = 0; i < count; ++i) {
-        swizzle_rb_if_bgra(Sk4f_fromS32(addr[i])).store(span[i].fVec);
-    }
-}
-
-static void load_f16(const SkPixmap& src, int x, int y, SkPM4f span[], int count) {
-    SkASSERT(count > 0);
-    const uint64_t* addr = src.addr64(x, y);
-    SkASSERT(src.addr64(x + count - 1, y));
-
-    for (int i = 0; i < count; ++i) {
-        SkHalfToFloat_finite_ftz(addr[i]).store(span[i].fVec);
-    }
-}
-
-SkLoadSpanProc SkLoadSpanProc_Choose(const SkImageInfo& info) {
-    switch (info.colorType()) {
-        case kN32_SkColorType:
-            return info.gammaCloseToSRGB() ? load_s32 : load_l32;
-        case kRGBA_F16_SkColorType:
-            return load_f16;
-        default:
-            return nullptr;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static void noop_filterspan(const SkPaint& paint, SkPM4f[], int) {
-    SkASSERT(!paint.getColorFilter());
-    SkASSERT(0xFF == paint.getAlpha());
-}
-
-static void alpha_filterspan(const SkPaint& paint, SkPM4f span[], int count) {
-    SkASSERT(!paint.getColorFilter());
-    SkASSERT(0xFF != paint.getAlpha());
-    const Sk4f scale = Sk4f(paint.getAlpha() * (1.0f/255));
-    for (int i = 0; i < count; ++i) {
-        (Sk4f::Load(span[i].fVec) * scale).store(span[i].fVec);
-    }
-}
-
-static void colorfilter_filterspan(const SkPaint& paint, SkPM4f span[], int count) {
-    SkASSERT(paint.getColorFilter());
-    SkASSERT(0xFF == paint.getAlpha());
-    paint.getColorFilter()->filterSpan4f(span, count, span);
-}
-
-static void colorfilter_alpha_filterspan(const SkPaint& paint, SkPM4f span[], int count) {
-    SkASSERT(paint.getColorFilter());
-    SkASSERT(0xFF != paint.getAlpha());
-    alpha_filterspan(paint, span, count);
-    paint.getColorFilter()->filterSpan4f(span, count, span);
-}
-
-SkFilterSpanProc SkFilterSpanProc_Choose(const SkPaint& paint) {
-    if (paint.getColorFilter()) {
-        return 0xFF == paint.getAlpha() ? colorfilter_filterspan : colorfilter_alpha_filterspan;
-    } else {
-        return 0xFF == paint.getAlpha() ? noop_filterspan : alpha_filterspan;
-    }
-}
diff --git a/src/core/SkSpanProcs.h b/src/core/SkSpanProcs.h
deleted file mode 100644
index 891f4e2..0000000
--- a/src/core/SkSpanProcs.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkSpanProcs_DEFINED
-#define SkSpanProcs_DEFINED
-
-#include "SkPM4f.h"
-
-struct SkImageInfo;
-class SkPaint;
-class SkPixmap;
-struct SkPM4f;
-
-typedef void (*SkLoadSpanProc)(const SkPixmap&, int x, int y, SkPM4f span[], int count);
-typedef void (*SkFilterSpanProc)(const SkPaint& paint, SkPM4f span[], int count);
-
-SkLoadSpanProc SkLoadSpanProc_Choose(const SkImageInfo&);
-SkFilterSpanProc SkFilterSpanProc_Choose(const SkPaint&);
-
-#endif
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 339341a..b22335b 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -17,6 +17,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrResourceProvider.h"
 #include "GrSurfaceContext.h"
 #include "GrSurfaceProxyPriv.h"
@@ -217,11 +218,6 @@
         , fBitmap(bm)
     {
         SkASSERT(bm.pixelRef());
-
-        // We have to lock now, while bm is still in scope, since it may have come from our
-        // cache, which means we need to keep it locked until we (the special) are done, since
-        // we cannot re-generate the cache entry (if bm came from a generator).
-        fBitmap.lockPixels();
         SkASSERT(fBitmap.getPixels());
     }
 
@@ -331,13 +327,15 @@
     }
 
     const SkBitmap* srcBM = &bm;
-    SkBitmap tmpStorage;
+    SkBitmap tmp;
     // ImageFilters only handle N32 at the moment, so force our src to be that
     if (!valid_for_imagefilters(bm.info())) {
-        if (!bm.copyTo(&tmpStorage, kN32_SkColorType)) {
+        if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
+            !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
+        {
             return nullptr;
         }
-        srcBM = &tmpStorage;
+        srcBM = &tmp;
     }
     return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
 }
@@ -409,26 +407,25 @@
             return true;
         }
 
+        SkPixmap pmap;
         SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
                                                 this->alphaType(), fColorSpace);
-
-        if (!dst->tryAllocPixels(info)) {
+        auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
+        if (!rec) {
             return false;
         }
 
-        // Reading back to an SkBitmap ends deferral
-        GrTexture* texture = fTextureProxy->instantiate(fContext->resourceProvider());
-        if (!texture) {
+        sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
+                                                                          fTextureProxy, nullptr);
+        if (!sContext) {
             return false;
         }
 
-        if (!texture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
-                                 dst->getPixels(), dst->rowBytes())) {
+        if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
             return false;
         }
 
-        dst->pixelRef()->setImmutableWithID(this->uniqueID());
-        SkBitmapCache::Add(desc, *dst);
+        SkBitmapCache::Add(std::move(rec), dst);
         fAddedRasterVersionToCache.store(true);
         return true;
     }
diff --git a/src/core/SkSpecialSurface.cpp b/src/core/SkSpecialSurface.cpp
index 381e3d8..3e812d4 100644
--- a/src/core/SkSpecialSurface.cpp
+++ b/src/core/SkSpecialSurface.cpp
@@ -63,12 +63,12 @@
 
 class SkSpecialSurface_Raster : public SkSpecialSurface_Base {
 public:
-    SkSpecialSurface_Raster(sk_sp<SkPixelRef> pr,
+    SkSpecialSurface_Raster(const SkImageInfo& info,
+                            sk_sp<SkPixelRef> pr,
                             const SkIRect& subset,
                             const SkSurfaceProps* props)
         : INHERITED(subset, props) {
-        const SkImageInfo& info = pr->info();
-
+        SkASSERT(info.width() == pr->width() && info.height() == pr->height());
         fBitmap.setInfo(info, info.minRowBytes());
         fBitmap.setPixelRef(std::move(pr), 0, 0);
 
@@ -93,19 +93,26 @@
 
 sk_sp<SkSpecialSurface> SkSpecialSurface::MakeFromBitmap(const SkIRect& subset, SkBitmap& bm,
                                                          const SkSurfaceProps* props) {
-    return sk_make_sp<SkSpecialSurface_Raster>(sk_ref_sp(bm.pixelRef()), subset, props);
+    if (subset.isEmpty() || !SkSurfaceValidateRasterInfo(bm.info(), bm.rowBytes())) {
+        return nullptr;
+    }
+    return sk_make_sp<SkSpecialSurface_Raster>(bm.info(), sk_ref_sp(bm.pixelRef()), subset, props);
 }
 
 sk_sp<SkSpecialSurface> SkSpecialSurface::MakeRaster(const SkImageInfo& info,
                                                      const SkSurfaceProps* props) {
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewZeroed(info, 0, nullptr));
-    if (nullptr == pr.get()) {
+    if (!SkSurfaceValidateRasterInfo(info)) {
         return nullptr;
     }
 
-    const SkIRect subset = SkIRect::MakeWH(pr->info().width(), pr->info().height());
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeZeroed(info, 0, nullptr);
+    if (!pr) {
+        return nullptr;
+    }
 
-    return sk_make_sp<SkSpecialSurface_Raster>(std::move(pr), subset, props);
+    const SkIRect subset = SkIRect::MakeWH(info.width(), info.height());
+
+    return sk_make_sp<SkSpecialSurface_Raster>(info, std::move(pr), subset, props);
 }
 
 #if SK_SUPPORT_GPU
@@ -136,7 +143,7 @@
     ~SkSpecialSurface_Gpu() override { }
 
     sk_sp<SkSpecialImage> onMakeImageSnapshot() override {
-        if (!fRenderTargetContext->asTexture()) {
+        if (!fRenderTargetContext->asTextureProxy()) {
             return nullptr;
         }
         sk_sp<SkSpecialImage> tmp(SkSpecialImage::MakeDeferredFromGpu(
@@ -164,7 +171,7 @@
         return nullptr;
     }
 
-    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
         SkBackingFit::kApprox, width, height, config, std::move(colorSpace)));
     if (!renderTargetContext) {
         return nullptr;
diff --git a/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
index 3cec674..9f10dbf 100644
--- a/src/core/SkSpriteBlitter.h
+++ b/src/core/SkSpriteBlitter.h
@@ -32,10 +32,7 @@
     // A SkSpriteBlitter must implement blitRect.
     void blitRect(int x, int y, int width, int height) override = 0;
 
-    static SkSpriteBlitter* ChooseD16(const SkPixmap& source, const SkPaint&, SkArenaAlloc*);
     static SkSpriteBlitter* ChooseL32(const SkPixmap& source, const SkPaint&, SkArenaAlloc*);
-    static SkSpriteBlitter* ChooseS32(const SkPixmap& source, const SkPaint&, SkArenaAlloc*);
-    static SkSpriteBlitter* ChooseF16(const SkPixmap& source, const SkPaint&, SkArenaAlloc*);
 
 protected:
     SkPixmap        fDst;
diff --git a/src/core/SkSpriteBlitter4f.cpp b/src/core/SkSpriteBlitter4f.cpp
deleted file mode 100644
index f893478..0000000
--- a/src/core/SkSpriteBlitter4f.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * 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 "SkSpriteBlitter.h"
-#include "SkArenaAlloc.h"
-#include "SkSpanProcs.h"
-#include "SkTemplates.h"
-#include "SkXfermodePriv.h"
-
-class Sprite_4f : public SkSpriteBlitter {
-public:
-    Sprite_4f(const SkPixmap& src, const SkPaint& paint) : INHERITED(src) {
-        fMode = paint.getBlendMode();
-        fLoader = SkLoadSpanProc_Choose(src.info());
-        fFilter = SkFilterSpanProc_Choose(paint);
-        fBuffer.reset(src.width());
-    }
-
-protected:
-    SkBlendMode             fMode;
-    SkLoadSpanProc          fLoader;
-    SkFilterSpanProc        fFilter;
-    SkAutoTMalloc<SkPM4f>   fBuffer;
-
-private:
-    typedef SkSpriteBlitter INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Sprite_F16 : public Sprite_4f {
-public:
-    Sprite_F16(const SkPixmap& src, const SkPaint& paint) : INHERITED(src, paint) {
-        uint32_t flags = 0;
-        if (src.isOpaque()) {
-            flags |= SkXfermode::kSrcIsOpaque_F16Flag;
-        }
-        fWriter = SkXfermode::GetF16Proc(fMode, flags);
-    }
-
-    void blitRect(int x, int y, int width, int height) override {
-        SkASSERT(width > 0 && height > 0);
-        uint64_t* SK_RESTRICT dst = fDst.writable_addr64(x, y);
-        size_t dstRB = fDst.rowBytes();
-
-        for (int bottom = y + height; y < bottom; ++y) {
-            fLoader(fSource, x - fLeft, y - fTop, fBuffer, width);
-            fFilter(*fPaint, fBuffer, width);
-            fWriter(fMode, dst, fBuffer, width, nullptr);
-            dst = (uint64_t* SK_RESTRICT)((char*)dst + dstRB);
-        }
-    }
-
-private:
-    SkXfermode::F16Proc fWriter;
-
-    typedef Sprite_4f INHERITED;
-};
-
-
-SkSpriteBlitter* SkSpriteBlitter::ChooseF16(const SkPixmap& source, const SkPaint& paint,
-                                            SkArenaAlloc* allocator) {
-    SkASSERT(allocator != nullptr);
-
-    if (paint.getMaskFilter() != nullptr) {
-        return nullptr;
-    }
-
-    switch (source.colorType()) {
-        case kN32_SkColorType:
-        case kRGBA_F16_SkColorType:
-            return allocator->make<Sprite_F16>(source, paint);
-        default:
-            return nullptr;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Sprite_sRGB : public Sprite_4f {
-public:
-    Sprite_sRGB(const SkPixmap& src, const SkPaint& paint) : INHERITED(src, paint) {
-        uint32_t flags = SkXfermode::kDstIsSRGB_D32Flag;
-        if (src.isOpaque()) {
-            flags |= SkXfermode::kSrcIsOpaque_D32Flag;
-        }
-        fWriter = SkXfermode::GetD32Proc(fMode, flags);
-    }
-
-    void blitRect(int x, int y, int width, int height) override {
-        SkASSERT(width > 0 && height > 0);
-        uint32_t* SK_RESTRICT dst = fDst.writable_addr32(x, y);
-        size_t dstRB = fDst.rowBytes();
-
-        for (int bottom = y + height; y < bottom; ++y) {
-            fLoader(fSource, x - fLeft, y - fTop, fBuffer, width);
-            fFilter(*fPaint, fBuffer, width);
-            fWriter(fMode, dst, fBuffer, width, nullptr);
-            dst = (uint32_t* SK_RESTRICT)((char*)dst + dstRB);
-        }
-    }
-
-protected:
-    SkXfermode::D32Proc fWriter;
-
-private:
-    typedef Sprite_4f INHERITED;
-};
-
-
-SkSpriteBlitter* SkSpriteBlitter::ChooseS32(const SkPixmap& source, const SkPaint& paint,
-                                            SkArenaAlloc* allocator) {
-    SkASSERT(allocator != nullptr);
-
-    if (paint.getMaskFilter() != nullptr) {
-        return nullptr;
-    }
-
-    switch (source.colorType()) {
-        case kN32_SkColorType:
-        case kRGBA_F16_SkColorType:
-            return allocator->make<Sprite_sRGB>(source, paint);
-        default:
-            return nullptr;
-    }
-}
diff --git a/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
deleted file mode 100644
index c616e22..0000000
--- a/src/core/SkSpriteBlitter_RGB16.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkSpriteBlitter.h"
-#include "SkArenaAlloc.h"
-#include "SkBlitRow.h"
-#include "SkTemplates.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-
-#define D16_S32A_Opaque_Pixel(dst, sc)                                        \
-do {                                                                          \
-    if (sc) {                                                                 \
-        *dst = SkSrcOver32To16(sc, *dst);                                     \
-    }                                                                         \
-} while (0)
-
-static inline void D16_S32A_Blend_Pixel_helper(uint16_t* dst, SkPMColor sc,
-                                               unsigned src_scale) {
-    uint16_t dc = *dst;
-    unsigned sa = SkGetPackedA32(sc);
-    unsigned dr, dg, db;
-
-    if (255 == sa) {
-        dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale);
-        dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale);
-        db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale);
-    } else {
-        unsigned dst_scale = SkAlphaMulInv256(sa, src_scale);
-        dr = (SkPacked32ToR16(sc) * src_scale + SkGetPackedR16(dc) * dst_scale) >> 8;
-        dg = (SkPacked32ToG16(sc) * src_scale + SkGetPackedG16(dc) * dst_scale) >> 8;
-        db = (SkPacked32ToB16(sc) * src_scale + SkGetPackedB16(dc) * dst_scale) >> 8;
-    }
-    *dst = SkPackRGB16(dr, dg, db);
-}
-
-#define D16_S32A_Blend_Pixel(dst, sc, src_scale) \
-    do { if (sc) D16_S32A_Blend_Pixel_helper(dst, sc, src_scale); } while (0)
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-class Sprite_D16_S16_Opaque : public SkSpriteBlitter {
-public:
-    Sprite_D16_S16_Opaque(const SkPixmap& source) : SkSpriteBlitter(source) {}
-
-    // overrides
-    void blitRect(int x, int y, int width, int height) override {
-        uint16_t* SK_RESTRICT dst = fDst.writable_addr16(x, y);
-        const uint16_t* SK_RESTRICT src = fSource.addr16(x - fLeft, y - fTop);
-        size_t dstRB = fDst.rowBytes();
-        size_t srcRB = fSource.rowBytes();
-
-        while (--height >= 0) {
-            memcpy(dst, src, width << 1);
-            dst = (uint16_t*)((char*)dst + dstRB);
-            src = (const uint16_t*)((const char*)src + srcRB);
-        }
-    }
-};
-
-#define D16_S16_Blend_Pixel(dst, sc, scale)     \
-    do {                                        \
-        uint16_t dc = *dst;                     \
-        *dst = SkBlendRGB16(sc, dc, scale);     \
-    } while (0)
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_S16_Blend
-#define SkSPRITE_ARGS                       , uint8_t alpha
-#define SkSPRITE_FIELDS                     uint8_t  fSrcAlpha;
-#define SkSPRITE_INIT                       fSrcAlpha = alpha;
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   uint16_t
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr16
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      int scale = SkAlpha255To256(fSrcAlpha);
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S16_Blend_Pixel(dst, src, scale)
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define D16_S4444_Opaque(dst, sc)           \
-    do {                                    \
-        uint16_t dc = *dst;                 \
-        *dst = SkSrcOver4444To16(sc, dc);   \
-    } while (0)
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Opaque
-#define SkSPRITE_ARGS
-#define SkSPRITE_FIELDS
-#define SkSPRITE_INIT
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   SkPMColor16
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr16
-#define SkSPRITE_PREAMBLE(srcBM, x, y)
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S4444_Opaque(dst, src)
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-#define D16_S4444_Blend(dst, sc, scale16)           \
-    do {                                            \
-        uint16_t dc = *dst;                         \
-        *dst = SkBlend4444To16(sc, dc, scale16);    \
-    } while (0)
-
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Blend
-#define SkSPRITE_ARGS                       , uint8_t alpha
-#define SkSPRITE_FIELDS                     uint8_t  fSrcAlpha;
-#define SkSPRITE_INIT                       fSrcAlpha = alpha;
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   uint16_t
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr16
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      int scale = SkAlpha15To16(fSrcAlpha);
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S4444_Blend(dst, src, scale)
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8A_Opaque
-#define SkSPRITE_ARGS
-#define SkSPRITE_FIELDS
-#define SkSPRITE_INIT
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   uint8_t
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr8
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      const SkPMColor* ctable = srcBM.ctable()->readColors()
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S32A_Opaque_Pixel(dst, ctable[src])
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8A_Blend
-#define SkSPRITE_ARGS                       , uint8_t alpha
-#define SkSPRITE_FIELDS                     uint8_t fSrcAlpha;
-#define SkSPRITE_INIT                       fSrcAlpha = alpha;
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   uint8_t
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr8
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      const SkPMColor* ctable = srcBM.ctable()->readColors(); unsigned src_scale = SkAlpha255To256(fSrcAlpha);
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S32A_Blend_Pixel(dst, ctable[src], src_scale)
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-static intptr_t asint(const void* ptr) {
-    return reinterpret_cast<const char*>(ptr) - (const char*)0;
-}
-
-static void blitrow_d16_si8(uint16_t* SK_RESTRICT dst,
-                            const uint8_t* SK_RESTRICT src, int count,
-                            const uint16_t* SK_RESTRICT ctable) {
-    if (count <= 8) {
-        do {
-            *dst++ = ctable[*src++];
-        } while (--count);
-        return;
-    }
-
-    // eat src until we're on a 4byte boundary
-    while (asint(src) & 3) {
-        *dst++ = ctable[*src++];
-        count -= 1;
-    }
-
-    int qcount = count >> 2;
-    SkASSERT(qcount > 0);
-    const uint32_t* qsrc = reinterpret_cast<const uint32_t*>(src);
-    if (asint(dst) & 2) {
-        do {
-            uint32_t s4 = *qsrc++;
-#ifdef SK_CPU_LENDIAN
-            *dst++ = ctable[s4 & 0xFF];
-            *dst++ = ctable[(s4 >> 8) & 0xFF];
-            *dst++ = ctable[(s4 >> 16) & 0xFF];
-            *dst++ = ctable[s4 >> 24];
-#else   // BENDIAN
-            *dst++ = ctable[s4 >> 24];
-            *dst++ = ctable[(s4 >> 16) & 0xFF];
-            *dst++ = ctable[(s4 >> 8) & 0xFF];
-            *dst++ = ctable[s4 & 0xFF];
-#endif
-        } while (--qcount);
-    } else {    // dst is on a 4byte boundary
-        uint32_t* ddst = reinterpret_cast<uint32_t*>(dst);
-        do {
-            uint32_t s4 = *qsrc++;
-#ifdef SK_CPU_LENDIAN
-            *ddst++ = (ctable[(s4 >> 8) & 0xFF] << 16) | ctable[s4 & 0xFF];
-            *ddst++ = (ctable[s4 >> 24] << 16) | ctable[(s4 >> 16) & 0xFF];
-#else   // BENDIAN
-            *ddst++ = (ctable[s4 >> 24] << 16) | ctable[(s4 >> 16) & 0xFF];
-            *ddst++ = (ctable[(s4 >> 8) & 0xFF] << 16) | ctable[s4 & 0xFF];
-#endif
-        } while (--qcount);
-        dst = reinterpret_cast<uint16_t*>(ddst);
-    }
-    src = reinterpret_cast<const uint8_t*>(qsrc);
-    count &= 3;
-    // catch any remaining (will be < 4)
-    while (--count >= 0) {
-        *dst++ = ctable[*src++];
-    }
-}
-
-#define SkSPRITE_ROW_PROC(d, s, n, x, y)    blitrow_d16_si8(d, s, n, ctable)
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8_Opaque
-#define SkSPRITE_ARGS
-#define SkSPRITE_FIELDS
-#define SkSPRITE_INIT
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   uint8_t
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr8
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      const uint16_t* ctable = srcBM.ctable()->read16BitCache()
-#define SkSPRITE_BLIT_PIXEL(dst, src)       *dst = ctable[src]
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8_Blend
-#define SkSPRITE_ARGS                       , uint8_t alpha
-#define SkSPRITE_FIELDS                     uint8_t fSrcAlpha;
-#define SkSPRITE_INIT                       fSrcAlpha = alpha;
-#define SkSPRITE_DST_TYPE                   uint16_t
-#define SkSPRITE_SRC_TYPE                   uint8_t
-#define SkSPRITE_DST_GETADDR                writable_addr16
-#define SkSPRITE_SRC_GETADDR                addr8
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      const uint16_t* ctable = srcBM.ctable()->read16BitCache(); unsigned src_scale = SkAlpha255To256(fSrcAlpha);
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S16_Blend_Pixel(dst, ctable[src], src_scale)
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-class Sprite_D16_S32_BlitRowProc : public SkSpriteBlitter {
-public:
-    Sprite_D16_S32_BlitRowProc(const SkPixmap& source) : SkSpriteBlitter(source) {}
-
-    void setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override {
-        this->INHERITED::setup(dst, left, top, paint);
-
-        unsigned flags = 0;
-
-        if (paint.getAlpha() < 0xFF) {
-            flags |= SkBlitRow::kGlobalAlpha_Flag;
-        }
-        if (!fSource.isOpaque()) {
-            flags |= SkBlitRow::kSrcPixelAlpha_Flag;
-        }
-        if (paint.isDither()) {
-            flags |= SkBlitRow::kDither_Flag;
-        }
-        fProc = SkBlitRow::Factory16(flags);
-    }
-
-    void blitRect(int x, int y, int width, int height) override {
-        uint16_t* SK_RESTRICT dst = fDst.writable_addr16(x, y);
-        const SkPMColor* SK_RESTRICT src = fSource.addr32(x - fLeft, y - fTop);
-        size_t dstRB = fDst.rowBytes();
-        size_t srcRB = fSource.rowBytes();
-        SkBlitRow::Proc16 proc = fProc;
-        U8CPU alpha = fPaint->getAlpha();
-
-        while (--height >= 0) {
-            proc(dst, src, width, alpha, x, y);
-            y += 1;
-            dst = (uint16_t* SK_RESTRICT)((char*)dst + dstRB);
-            src = (const SkPMColor* SK_RESTRICT)((const char*)src + srcRB);
-        }
-    }
-
-private:
-    SkBlitRow::Proc16 fProc;
-
-    typedef SkSpriteBlitter INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkPixmap& source, const SkPaint& paint,
-                                            SkArenaAlloc* allocator) {
-
-    SkASSERT(allocator != nullptr);
-
-    if (paint.getMaskFilter() != nullptr) { // may add cases for this
-        return nullptr;
-    }
-    if (!paint.isSrcOver()) { // may add cases for this
-        return nullptr;
-    }
-    if (paint.getColorFilter() != nullptr) { // may add cases for this
-        return nullptr;
-    }
-
-    const SkAlphaType at = source.alphaType();
-
-    SkSpriteBlitter* blitter = nullptr;
-    unsigned alpha = paint.getAlpha();
-
-    switch (source.colorType()) {
-        case kN32_SkColorType: {
-            if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
-                break;
-            }
-            blitter = allocator->make<Sprite_D16_S32_BlitRowProc>(source);
-            break;
-        }
-        case kARGB_4444_SkColorType:
-            if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
-                break;
-            }
-            if (255 == alpha) {
-                blitter = allocator->make<Sprite_D16_S4444_Opaque>(source);
-            } else {
-                blitter = allocator->make<Sprite_D16_S4444_Blend>(source, alpha >> 4);
-            }
-            break;
-        case kRGB_565_SkColorType:
-            if (255 == alpha) {
-                blitter = allocator->make<Sprite_D16_S16_Opaque>(source);
-            } else {
-                blitter = allocator->make<Sprite_D16_S16_Blend>(source, alpha);
-            }
-            break;
-        case kIndex_8_SkColorType:
-            if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
-                break;
-            }
-            if (paint.isDither()) {
-                // we don't support dither yet in these special cases
-                break;
-            }
-            if (source.isOpaque()) {
-                if (255 == alpha) {
-                    blitter = allocator->make<Sprite_D16_SIndex8_Opaque>(source);
-                } else {
-                    blitter = allocator->make<Sprite_D16_SIndex8_Blend>(source, alpha);
-                }
-            } else {
-                if (255 == alpha) {
-                    blitter = allocator->make<Sprite_D16_SIndex8A_Opaque>(source);
-                } else {
-                    blitter = allocator->make<Sprite_D16_SIndex8A_Blend>(source, alpha);
-                }
-            }
-            break;
-        default:
-            break;
-    }
-    return blitter;
-}
diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp
index f0e16a6..7bb2079 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -242,10 +242,6 @@
     return fSize;
 }
 
-const void* SkFILEStream::getMemoryBase() {
-    return nullptr;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static sk_sp<SkData> newFromParams(const void* src, size_t size, bool copyData) {
@@ -549,10 +545,13 @@
     }
 }
 
-void SkDynamicMemoryWStream::writeToStream(SkWStream* dst) const {
+bool SkDynamicMemoryWStream::writeToStream(SkWStream* dst) const {
     for (Block* block = fHead; block != nullptr; block = block->fNext) {
-        dst->write(block->start(), block->written());
+        if (!dst->write(block->start(), block->written())) {
+            return false;
+        }
     }
+    return true;
 }
 
 void SkDynamicMemoryWStream::padToAlign4() {
@@ -588,6 +587,23 @@
     fBytesWrittenBeforeTail = 0;
 }
 
+bool SkDynamicMemoryWStream::writeToAndReset(SkWStream* dst) {
+    // By looping through the source and freeing as we copy, we
+    // can reduce real memory use with large streams.
+    bool dstStreamGood = true;
+    for (Block* block = fHead; block != nullptr; ) {
+        if (dstStreamGood && !dst->write(block->start(), block->written())) {
+            dstStreamGood = false;
+        }
+        Block* next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+    fHead = fTail = nullptr;
+    fBytesWrittenBeforeTail = 0;
+    return dstStreamGood;
+}
+
 sk_sp<SkData> SkDynamicMemoryWStream::detachAsData() {
     const size_t size = this->bytesWritten();
     if (0 == size) {
diff --git a/src/core/SkSurfacePriv.h b/src/core/SkSurfacePriv.h
index 74d19a6..a41b583 100644
--- a/src/core/SkSurfacePriv.h
+++ b/src/core/SkSurfacePriv.h
@@ -10,6 +10,8 @@
 
 #include "SkSurfaceProps.h"
 
+struct SkImageInfo;
+
 static inline SkSurfaceProps SkSurfacePropsCopyOrDefault(const SkSurfaceProps* props) {
     if (props) {
         return *props;
@@ -22,4 +24,8 @@
     return SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType).pixelGeometry();
 }
 
+constexpr size_t kIgnoreRowBytesValue = static_cast<size_t>(~0);
+
+bool SkSurfaceValidateRasterInfo(const SkImageInfo&, size_t rb = kIgnoreRowBytesValue);
+
 #endif
diff --git a/src/core/SkTDPQueue.h b/src/core/SkTDPQueue.h
index 294c9f4..5f6fd3b 100644
--- a/src/core/SkTDPQueue.h
+++ b/src/core/SkTDPQueue.h
@@ -9,6 +9,7 @@
 #define SkTDPQueue_DEFINED
 
 #include "SkTDArray.h"
+#include "SkTSort.h"
 
 /**
  * This class implements a priority queue. T is the type of the elements in the queue. LESS is a
@@ -24,10 +25,16 @@
 template <typename T,
           bool (*LESS)(const T&, const T&),
           int* (*INDEX)(const T&) = (int* (*)(const T&))nullptr>
-class SkTDPQueue : public SkNoncopyable {
+class SkTDPQueue {
 public:
     SkTDPQueue() {}
 
+    SkTDPQueue(SkTDPQueue&&) = default;
+    SkTDPQueue& operator =(SkTDPQueue&&) = default;
+
+    SkTDPQueue(const SkTDPQueue&) = delete;
+    SkTDPQueue& operator=(const SkTDPQueue&) = delete;
+
     /** Number of items in the queue. */
     int count() const { return fArray.count(); }
 
@@ -96,6 +103,19 @@
         to peek(). Otherwise, there is no guarantee about ordering of elements in the queue. */
     T at(int i) const { return fArray[i]; }
 
+    /** Sorts the queue into priority order.  The queue is only guarenteed to remain in sorted order
+     *  until any other operation, other than at(), is performed.
+     */
+    void sort() {
+        if (fArray.count() > 1) {
+            SkTQSort<T>(fArray.begin(), fArray.end() - 1, LESS);
+            for (int i = 0; i < fArray.count(); i++) {
+                this->setIndex(i);
+            }
+            this->validate();
+        }
+    }
+
 private:
     static int LeftOf(int x) { SkASSERT(x >= 0); return 2 * x + 1; }
     static int ParentOf(int x) { SkASSERT(x > 0); return (x - 1) >> 1; }
@@ -188,8 +208,6 @@
     }
 
     SkTDArray<T> fArray;
-
-    typedef SkNoncopyable INHERITED;
 };
 
 #endif
diff --git a/src/core/SkThreadedBMPDevice.cpp b/src/core/SkThreadedBMPDevice.cpp
new file mode 100644
index 0000000..0e45b9f
--- /dev/null
+++ b/src/core/SkThreadedBMPDevice.cpp
@@ -0,0 +1,372 @@
+/*
+ * 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 "SkThreadedBMPDevice.h"
+
+#include "SkPath.h"
+#include "SkTaskGroup.h"
+#include "SkVertices.h"
+
+#include <mutex>
+#include <vector>
+
+constexpr int MAX_CACHE_LINE = 64;
+
+// Some basic logics and data structures that are shared across the current experimental schedulers.
+class TiledDrawSchedulerBase : public TiledDrawScheduler {
+public:
+    TiledDrawSchedulerBase(int tiles, WorkFunc work)
+            : fTileCnt(tiles), fIsFinishing(false), fDrawCnt(0), fWork(work) {}
+
+    void signal() override {
+        fDrawCnt++;
+    }
+    void finish() override {
+        fIsFinishing.store(true, std::memory_order_relaxed);
+    }
+
+protected:
+    const int                   fTileCnt;
+    std::atomic<bool>           fIsFinishing;
+    std::atomic<int>            fDrawCnt;
+    WorkFunc                    fWork;
+};
+
+class TiledDrawSchedulerBySpinning : public TiledDrawSchedulerBase {
+public:
+    TiledDrawSchedulerBySpinning(int tiles, WorkFunc work)
+            : TiledDrawSchedulerBase(tiles, work), fScheduleData(tiles) {}
+
+    void signal() final { this->TiledDrawSchedulerBase::signal(); }
+    void finish() final { this->TiledDrawSchedulerBase::finish(); }
+
+    bool next(int& tileIndex) final {
+        int& drawIndex = fScheduleData[tileIndex].fDrawIndex;
+        SkASSERT(drawIndex <= fDrawCnt);
+        while (true) {
+            bool isFinishing = fIsFinishing.load(std::memory_order_relaxed);
+            if (isFinishing && drawIndex >= fDrawCnt) {
+                return false;
+            } else if (drawIndex < fDrawCnt) {
+                fWork(tileIndex, drawIndex++);
+                return true;
+            }
+        }
+    }
+
+private:
+    // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines
+    struct alignas(MAX_CACHE_LINE) TileScheduleData {
+        TileScheduleData() : fDrawIndex(0) {}
+
+        int fDrawIndex; // next draw index for this tile
+    };
+
+    std::vector<TileScheduleData>  fScheduleData;
+};
+
+class TiledDrawSchedulerFlexible : public TiledDrawSchedulerBase {
+public:
+    TiledDrawSchedulerFlexible(int tiles, WorkFunc work)
+            : TiledDrawSchedulerBase(tiles, work), fScheduleData(tiles) {}
+
+    void signal() final { this->TiledDrawSchedulerBase::signal(); }
+    void finish() final { this->TiledDrawSchedulerBase::finish(); }
+
+    bool next(int& tileIndex) final {
+        int failCnt = 0;
+        while (true) {
+            TileScheduleData& scheduleData = fScheduleData[tileIndex];
+            bool locked = scheduleData.fMutex.try_lock();
+            bool processed = false;
+
+            if (locked) {
+                if (scheduleData.fDrawIndex < fDrawCnt) {
+                    fWork(tileIndex, scheduleData.fDrawIndex++);
+                    processed = true;
+                } else {
+                    failCnt += fIsFinishing.load(std::memory_order_relaxed);
+                }
+                scheduleData.fMutex.unlock();
+            }
+
+            if (processed) {
+                return true;
+            } else {
+                if (failCnt >= fTileCnt) {
+                    return false;
+                }
+                tileIndex = (tileIndex + 1) % fTileCnt;
+            }
+        }
+    }
+
+private:
+    // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines
+    struct alignas(MAX_CACHE_LINE) TileScheduleData {
+        TileScheduleData() : fDrawIndex(0) {}
+
+        int         fDrawIndex; // next draw index for this tile
+        std::mutex  fMutex;     // the mutex for the thread to acquire
+    };
+
+    std::vector<TileScheduleData>  fScheduleData;
+};
+
+class TiledDrawSchedulerBySemaphores : public TiledDrawSchedulerBase {
+public:
+    TiledDrawSchedulerBySemaphores(int tiles, WorkFunc work)
+            : TiledDrawSchedulerBase(tiles, work), fScheduleData(tiles) {}
+
+
+    void signal() final {
+        this->TiledDrawSchedulerBase::signal();
+        signalRoot();
+    }
+
+    void finish() final {
+        this->TiledDrawSchedulerBase::finish();
+        signalRoot();
+    }
+
+    bool next(int& tileIndex) final {
+        SkASSERT(tileIndex >= 0 && tileIndex < fTileCnt);
+        TileScheduleData& scheduleData = fScheduleData[tileIndex];
+        while (true) {
+            scheduleData.fSemaphore.wait();
+            int leftChild = (tileIndex + 1) * 2 - 1;
+            int rightChild = leftChild + 1;
+            if (leftChild < fTileCnt) {
+                fScheduleData[leftChild].fSemaphore.signal();
+            }
+            if (rightChild < fTileCnt) {
+                fScheduleData[rightChild].fSemaphore.signal();
+            }
+
+            bool isFinishing = fIsFinishing.load(std::memory_order_relaxed);
+            if (isFinishing && scheduleData.fDrawIndex >= fDrawCnt) {
+                return false;
+            } else {
+                SkASSERT(scheduleData.fDrawIndex < fDrawCnt);
+                fWork(tileIndex, scheduleData.fDrawIndex++);
+                return true;
+            }
+        }
+    }
+
+private:
+    // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines
+    struct alignas(MAX_CACHE_LINE) TileScheduleData {
+        TileScheduleData() : fDrawIndex(0) {}
+
+        int         fDrawIndex;
+        SkSemaphore fSemaphore;
+    };
+
+    void signalRoot() {
+        SkASSERT(fTileCnt > 0);
+        fScheduleData[0].fSemaphore.signal();
+    }
+
+    std::vector<TileScheduleData> fScheduleData;
+};
+
+void SkThreadedBMPDevice::startThreads() {
+    SkASSERT(fThreadFutures.count() == 0);
+    SkASSERT(fQueueSize == 0);
+
+    TiledDrawScheduler::WorkFunc work = [this](int tileIndex, int drawIndex){
+        auto& element = fQueue[drawIndex];
+        if (SkIRect::Intersects(fTileBounds[tileIndex], element.fDrawBounds)) {
+            element.fDrawFn(fTileBounds[tileIndex]);
+        }
+    };
+
+    // using Scheduler = TiledDrawSchedulerBySemaphores;
+    // using Scheduler = TiledDrawSchedulerBySpinning;
+    using Scheduler = TiledDrawSchedulerFlexible;
+    fScheduler.reset(new Scheduler(fTileCnt, work));
+    for(int i = 0; i < fThreadCnt; ++i) {
+        fThreadFutures.push_back(std::async(std::launch::async, [this, i]() {
+            int tileIndex = i;
+            while (fScheduler->next(tileIndex)) {}
+        }));
+    }
+}
+
+void SkThreadedBMPDevice::finishThreads() {
+    fScheduler->finish();
+    for(auto& future : fThreadFutures) {
+        future.wait();
+    }
+    fThreadFutures.reset();
+    fQueueSize = 0;
+    fScheduler.reset(nullptr);
+}
+
+SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap, int tiles, int threads)
+        : INHERITED(bitmap)
+        , fTileCnt(tiles)
+        , fThreadCnt(threads <= 0 ? tiles : threads)
+{
+    // Tiling using stripes for now; we'll explore better tiling in the future.
+    int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1);
+    int w = bitmap.width();
+    int top = 0;
+    for(int tid = 0; tid < fTileCnt; ++tid, top += h) {
+        fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h));
+    }
+    fQueueSize = 0;
+    startThreads();
+}
+
+void SkThreadedBMPDevice::flush() {
+    finishThreads();
+    startThreads();
+}
+
+// Having this captured in lambda seems to be faster than saving this in DrawElement
+struct SkThreadedBMPDevice::DrawState {
+    SkPixmap fDst;
+    SkMatrix fMatrix;
+    SkRasterClip fRC;
+
+    explicit DrawState(SkThreadedBMPDevice* dev) {
+        // we need fDst to be set, and if we're actually drawing, to dirty the genID
+        if (!dev->accessPixels(&fDst)) {
+            // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
+            fDst.reset(dev->imageInfo(), nullptr, 0);
+        }
+        fMatrix = dev->ctm();
+        fRC = dev->fRCStack.rc();
+    }
+
+    SkDraw getThreadDraw(SkRasterClip& threadRC, const SkIRect& threadBounds) const {
+        SkDraw draw;
+        draw.fDst = fDst;
+        draw.fMatrix = &fMatrix;
+        threadRC = fRC;
+        threadRC.op(threadBounds, SkRegion::kIntersect_Op);
+        draw.fRC = &threadRC;
+        return draw;
+    }
+};
+
+SkIRect SkThreadedBMPDevice::transformDrawBounds(const SkRect& drawBounds) const {
+    if (drawBounds.isLargest()) {
+        return SkIRect::MakeLargest();
+    }
+    SkRect transformedBounds;
+    this->ctm().mapRect(&transformedBounds, drawBounds);
+    return transformedBounds.roundOut();
+}
+
+// The do {...} while (false) is to enforce trailing semicolon as suggested by mtklein@
+#define THREADED_DRAW(drawBounds, actualDrawCall)                                                  \
+    do {                                                                                           \
+        DrawState ds(this);                                                                        \
+        SkASSERT(fQueueSize < MAX_QUEUE_SIZE);                                                     \
+        fQueue[fQueueSize++] = {                                                                   \
+            this->transformDrawBounds(drawBounds),                                                 \
+            [=](const SkIRect& tileBounds) {                                                       \
+                SkRasterClip tileRC;                                                               \
+                SkDraw draw = ds.getThreadDraw(tileRC, tileBounds);                                \
+                draw.actualDrawCall;                                                               \
+            },                                                                                     \
+        };                                                                                         \
+        fScheduler->signal();                                                                      \
+    } while (false)
+
+static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) {
+    SkRect result;
+    if (p.canComputeFastBounds()) {
+        result = p.computeFastBounds(r, &result);
+    } else {
+        result = SkRect::MakeLargest();
+    }
+    return result;
+}
+
+void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) {
+    THREADED_DRAW(SkRect::MakeLargest(), drawPaint(paint));
+}
+
+void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
+        const SkPoint pts[], const SkPaint& paint) {
+    // TODO tighter drawBounds
+    SkRect drawBounds = SkRect::MakeLargest();
+    THREADED_DRAW(drawBounds, drawPoints(mode, count, pts, paint, nullptr));
+}
+
+void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) {
+    SkRect drawBounds = get_fast_bounds(r, paint);
+    THREADED_DRAW(drawBounds, drawRect(r, paint));
+}
+
+void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+#ifdef SK_IGNORE_BLURRED_RRECT_OPT
+    SkPath  path;
+
+    path.addRRect(rrect);
+    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
+    // required to override drawRRect.
+    this->drawPath(path, paint, nullptr, false);
+#else
+    SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint);
+    THREADED_DRAW(drawBounds, drawRRect(rrect, paint));
+#endif
+}
+
+void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint,
+        const SkMatrix* prePathMatrix, bool pathIsMutable) {
+    SkRect drawBounds = path.isInverseFillType() ? SkRect::MakeLargest()
+                                                 : get_fast_bounds(path.getBounds(), paint);
+    // For thread safety, make path imutable
+    THREADED_DRAW(drawBounds, drawPath(path, paint, prePathMatrix, false));
+}
+
+void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+        const SkPaint& paint) {
+    LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
+    SkRect drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height());
+    matrix.mapRect(&drawBounds);
+    THREADED_DRAW(drawBounds, drawBitmap(bitmap, matrix, nullptr, paint));
+}
+
+void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
+    SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
+    THREADED_DRAW(drawBounds, drawSprite(bitmap, x, y, paint));
+}
+
+void SkThreadedBMPDevice::drawText(const void* text, size_t len, SkScalar x, SkScalar y,
+        const SkPaint& paint) {
+    SkRect drawBounds = SkRect::MakeLargest(); // TODO tighter drawBounds
+    THREADED_DRAW(drawBounds, drawText((const char*)text, len, x, y, paint, &this->surfaceProps()));
+}
+
+void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
+        int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
+    SkRect drawBounds = SkRect::MakeLargest(); // TODO tighter drawBounds
+    THREADED_DRAW(drawBounds, drawPosText((const char*)text, len, xpos, scalarsPerPos, offset,
+                                          paint, &surfaceProps()));
+}
+
+void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
+        const SkPaint& paint) {
+    SkRect drawBounds = SkRect::MakeLargest(); // TODO tighter drawBounds
+    THREADED_DRAW(drawBounds, drawVertices(vertices->mode(), vertices->vertexCount(),
+                                           vertices->positions(), vertices->texCoords(),
+                                           vertices->colors(), bmode, vertices->indices(),
+                                           vertices->indexCount(), paint));
+}
+
+void SkThreadedBMPDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) {
+    SkASSERT(!paint.getImageFilter());
+    SkRect drawBounds = SkRect::MakeXYWH(x, y, device->width(), device->height());
+    THREADED_DRAW(drawBounds,
+                  drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint));
+}
diff --git a/src/core/SkThreadedBMPDevice.h b/src/core/SkThreadedBMPDevice.h
new file mode 100644
index 0000000..e973df2
--- /dev/null
+++ b/src/core/SkThreadedBMPDevice.h
@@ -0,0 +1,93 @@
+/*
+ * 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 SkThreadedBMPDevice_DEFINED
+#define SkThreadedBMPDevice_DEFINED
+
+#include "SkDraw.h"
+#include "SkBitmapDevice.h"
+
+#include <future>
+
+class TiledDrawScheduler {
+public:
+    using WorkFunc = std::function<void(int, int)>;
+
+    virtual ~TiledDrawScheduler() {}
+
+    virtual void signal() = 0; // signal that one more draw is available for all tiles
+
+    // Tell scheduler that no more draw calls will be added (no signal will be called).
+    virtual void finish() = 0;
+
+    // Handle the next draw available. This method will block until
+    //   (1) the next draw is finished, or
+    //   (2) the finish is called
+    // The method will return true for case (1) and false for case (2).
+    // When there's no draw available and we haven't called finish, we will just wait.
+    // In many cases, the parameter tileIndex specifies the tile that the next draw should happen.
+    // However, for some schedulers, that tileIndex may only be a hint and the scheduler is free
+    // to find another tile to draw. In that case, tileIndex will be changed to the actual tileIndex
+    // where the draw happens.
+    virtual bool next(int& tileIndex) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+class SkThreadedBMPDevice : public SkBitmapDevice {
+public:
+    // When threads = 0, we make fThreadCnt = fTileCnt
+    SkThreadedBMPDevice(const SkBitmap& bitmap, int tiles, int threads = 0);
+    ~SkThreadedBMPDevice() { finishThreads(); }
+
+protected:
+    void drawPaint(const SkPaint& paint) override;
+    void drawPoints(SkCanvas::PointMode mode, size_t count,
+                            const SkPoint[], const SkPaint& paint) override;
+    void drawRect(const SkRect& r, const SkPaint& paint) override;
+    void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
+
+    void drawPath(const SkPath&, const SkPaint&, const SkMatrix* prePathMatrix,
+                  bool pathIsMutable) override;
+    void drawBitmap(const SkBitmap&, const SkMatrix&, const SkPaint&) override;
+    void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) override;
+
+    void drawText(const void* text, size_t len, SkScalar x, SkScalar y,
+                  const SkPaint&) override;
+    void drawPosText(const void* text, size_t len, const SkScalar pos[],
+                     int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override;
+    void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
+    void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override;
+
+    void flush() override;
+
+private:
+    struct DrawElement {
+        SkIRect fDrawBounds;
+        std::function<void(const SkIRect& threadBounds)> fDrawFn;
+    };
+
+    struct DrawState;
+
+    SkIRect transformDrawBounds(const SkRect& drawBounds) const;
+
+    void startThreads();
+    void finishThreads();
+
+    static constexpr int MAX_QUEUE_SIZE = 100000;
+
+    const int fTileCnt;
+    const int fThreadCnt;
+    std::unique_ptr<TiledDrawScheduler> fScheduler;
+    SkTArray<SkIRect> fTileBounds;
+    SkTArray<std::future<void>> fThreadFutures;
+    DrawElement fQueue[MAX_QUEUE_SIZE];
+    int fQueueSize;
+
+    typedef SkBitmapDevice INHERITED;
+};
+
+#endif // SkThreadedBMPDevice_DEFINED
diff --git a/src/core/SkTime.cpp b/src/core/SkTime.cpp
index 9527090..ca67f4c 100644
--- a/src/core/SkTime.cpp
+++ b/src/core/SkTime.cpp
@@ -64,8 +64,19 @@
 }
 #endif // SK_BUILD_FOR_WIN32
 
+#if !defined(__has_feature)
+    #define  __has_feature(x) 0
+#endif
+
 double SkTime::GetNSecs() {
+#if __has_feature(memory_sanitizer)
+    // See skia:6504
+    struct timespec tp;
+    clock_gettime(CLOCK_MONOTONIC, &tp);
+    return tp.tv_sec * 1e9 + tp.tv_nsec;
+#else
     auto now = std::chrono::high_resolution_clock::now();
     std::chrono::duration<double, std::nano> ns = now.time_since_epoch();
     return ns.count();
+#endif
 }
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
index 2d8f920..1afcfd0 100644
--- a/src/core/SkTypeface.cpp
+++ b/src/core/SkTypeface.cpp
@@ -50,9 +50,9 @@
         return nullptr;
     }
     void onFilterRec(SkScalerContextRec*) const override { }
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo,
-                                const uint32_t*, uint32_t) const override { return nullptr; }
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
+        return nullptr;
+    }
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
     virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
                                 uint16_t glyphs[], int glyphCount) const override {
@@ -296,12 +296,8 @@
     this->onGetFamilyName(name);
 }
 
-SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics(
-                                PerGlyphInfo info,
-                                const uint32_t* glyphIDs,
-                                uint32_t glyphIDsCount) const {
-    SkAdvancedTypefaceMetrics* result =
-            this->onGetAdvancedTypefaceMetrics(info, glyphIDs, glyphIDsCount);
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface::getAdvancedMetrics() const {
+    std::unique_ptr<SkAdvancedTypefaceMetrics> result = this->onGetAdvancedMetrics();
     if (result && result->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
         SkOTTableOS2::Version::V2::Type::Field fsType;
         constexpr SkFontTableTag os2Tag = SkTEndian_SwapBE32(SkOTTableOS2::TAG);
@@ -368,3 +364,8 @@
                 fm.fXMax * invTextSize, fm.fBottom * invTextSize);
     return true;
 }
+
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface::onGetAdvancedMetrics() const {
+    SkDEBUGFAIL("Typefaces that need to work with PDF backend must override this.");
+    return nullptr;
+}
diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
index 311394d..85ebb3e 100644
--- a/src/core/SkUtils.cpp
+++ b/src/core/SkUtils.cpp
@@ -8,22 +8,6 @@
 
 #include "SkUtils.h"
 
-void sk_memset16(uint16_t buffer[], uint16_t value, int count) {
-    for (int i = 0; i < count; i++) {
-        buffer[i] = value;
-    }
-}
-void sk_memset32(uint32_t buffer[], uint32_t value, int count) {
-    for (int i = 0; i < count; i++) {
-        buffer[i] = value;
-    }
-}
-void sk_memset64(uint64_t buffer[], uint64_t value, int count) {
-    for (int i = 0; i < count; i++) {
-        buffer[i] = value;
-    }
-}
-
 /*  0xxxxxxx    1 total
     10xxxxxx    // never a leading byte
     110xxxxx    2 total
diff --git a/src/core/SkUtils.h b/src/core/SkUtils.h
index 241a61a..2ae6f87 100644
--- a/src/core/SkUtils.h
+++ b/src/core/SkUtils.h
@@ -10,15 +10,22 @@
 
 #include "SkTypes.h"
 #include "SkMath.h"
+#include "SkOpts.h"
 
 /** Similar to memset(), but it assigns a 16, 32, or 64-bit value into the buffer.
     @param buffer   The memory to have value copied into it
     @param value    The value to be copied into buffer
     @param count    The number of times value should be copied into the buffer.
 */
-void sk_memset16(uint16_t buffer[], uint16_t value, int count);
-void sk_memset32(uint32_t buffer[], uint32_t value, int count);
-void sk_memset64(uint64_t buffer[], uint64_t value, int count);
+static inline void sk_memset16(uint16_t buffer[], uint16_t value, int count) {
+    SkOpts::memset16(buffer, value, count);
+}
+static inline void sk_memset32(uint32_t buffer[], uint32_t value, int count) {
+    SkOpts::memset32(buffer, value, count);
+}
+static inline void sk_memset64(uint64_t buffer[], uint64_t value, int count) {
+    SkOpts::memset64(buffer, value, count);
+}
 ///////////////////////////////////////////////////////////////////////////////
 
 #define kMaxBytesInUTF8Sequence     4
diff --git a/src/core/SkValidatingReadBuffer.cpp b/src/core/SkValidatingReadBuffer.cpp
index 71dbc45..253b3f2 100644
--- a/src/core/SkValidatingReadBuffer.cpp
+++ b/src/core/SkValidatingReadBuffer.cpp
@@ -124,6 +124,12 @@
     point->fY = this->readScalar();
 }
 
+void SkValidatingReadBuffer::readPoint3(SkPoint3* point) {
+    point->fX = this->readScalar();
+    point->fY = this->readScalar();
+    point->fZ = this->readScalar();
+}
+
 void SkValidatingReadBuffer::readMatrix(SkMatrix* matrix) {
     size_t size = 0;
     if (!fError) {
@@ -139,6 +145,8 @@
     const void* ptr = this->skip(sizeof(SkIRect));
     if (!fError) {
         memcpy(rect, ptr, sizeof(SkIRect));
+    } else {
+        rect->setEmpty();
     }
 }
 
@@ -146,6 +154,8 @@
     const void* ptr = this->skip(sizeof(SkRect));
     if (!fError) {
         memcpy(rect, ptr, sizeof(SkRect));
+    } else {
+        rect->setEmpty();
     }
 }
 
diff --git a/src/core/SkValidatingReadBuffer.h b/src/core/SkValidatingReadBuffer.h
index 825c4b9..fdf7a3f 100644
--- a/src/core/SkValidatingReadBuffer.h
+++ b/src/core/SkValidatingReadBuffer.h
@@ -46,6 +46,7 @@
     SkFlattenable* readFlattenable(SkFlattenable::Type type) override;
     void readColor4f(SkColor4f* color) override;
     void readPoint(SkPoint* point) override;
+    void readPoint3(SkPoint3* point) override;
     void readMatrix(SkMatrix* matrix) override;
     void readIRect(SkIRect* rect) override;
     void readRect(SkRect* rect) override;
diff --git a/src/core/SkValidationUtils.h b/src/core/SkValidationUtils.h
index 5e2b91d..84b906a 100644
--- a/src/core/SkValidationUtils.h
+++ b/src/core/SkValidationUtils.h
@@ -12,12 +12,6 @@
 #include "SkBlendMode.h"
 #include "SkXfermodePriv.h"
 
-/** Returns true if coeff's value is in the SkXfermode::Coeff enum.
-  */
-static inline bool SkIsValidCoeff(SkXfermode::Coeff coeff) {
-    return coeff >= 0 && coeff < SkXfermode::kCoeffCount;
-}
-
 /** Returns true if mode's value is in the SkBlendMode enum.
   */
 static inline bool SkIsValidMode(SkBlendMode mode) {
diff --git a/src/core/SkVarAlloc.cpp b/src/core/SkVarAlloc.cpp
deleted file mode 100644
index cfa1188..0000000
--- a/src/core/SkVarAlloc.cpp
+++ /dev/null
@@ -1,58 +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.
- */
-
-#include "SkVarAlloc.h"
-
-#include "SkMalloc.h"
-
-struct SkVarAlloc::Block {
-    Block* prev;
-    char* data() { return (char*)(this + 1); }
-
-    static Block* Alloc(Block* prev, size_t size) {
-        SkASSERT(size >= sizeof(Block));
-        Block* b = (Block*)sk_malloc_throw(size);
-        b->prev = prev;
-        return b;
-    }
-};
-
-SkVarAlloc::SkVarAlloc(size_t minLgSize)
-    : fBytesAllocated(0)
-    , fByte(nullptr)
-    , fRemaining(0)
-    , fLgSize(minLgSize)
-    , fBlock(nullptr) {}
-
-SkVarAlloc::SkVarAlloc(size_t minLgSize, char* storage, size_t len)
-    : fBytesAllocated(0)
-    , fByte(storage)
-    , fRemaining(len)
-    , fLgSize(minLgSize)
-    , fBlock(nullptr) {}
-
-SkVarAlloc::~SkVarAlloc() {
-    Block* b = fBlock;
-    while (b) {
-        Block* prev = b->prev;
-        sk_free(b);
-        b = prev;
-    }
-}
-
-void SkVarAlloc::makeSpace(size_t bytes) {
-    SkASSERT(SkIsAlignPtr(bytes));
-
-    size_t alloc = static_cast<size_t>(1)<<fLgSize++;
-    while (alloc < bytes + sizeof(Block)) {
-        alloc *= 2;
-    }
-    fBytesAllocated += alloc;
-    fBlock = Block::Alloc(fBlock, alloc);
-    fByte = fBlock->data();
-    fRemaining = alloc - sizeof(Block);
-}
diff --git a/src/core/SkVarAlloc.h b/src/core/SkVarAlloc.h
deleted file mode 100644
index 3729bad..0000000
--- a/src/core/SkVarAlloc.h
+++ /dev/null
@@ -1,55 +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 SkVarAlloc_DEFINED
-#define SkVarAlloc_DEFINED
-
-#include "SkTypes.h"
-
-class SkVarAlloc : SkNoncopyable {
-public:
-    // Smallest block we'll allocate is 2**N bytes.
-    explicit SkVarAlloc(size_t minLgSize);
-    // Same as above, but first uses up to len bytes from storage.
-    SkVarAlloc(size_t minLgSize, char* storage, size_t len);
-
-    ~SkVarAlloc();
-
-    // Returns contiguous bytes aligned at least for pointers.
-    char* alloc(size_t bytes) {
-        bytes = SkAlignPtr(bytes);
-
-        if (bytes > fRemaining) {
-            this->makeSpace(bytes);
-        }
-        SkASSERT(bytes <= fRemaining);
-
-        char* ptr = fByte;
-        fByte += bytes;
-        fRemaining = SkToU32(fRemaining - bytes);
-        return ptr;
-    }
-
-    // Returns our best estimate of the number of bytes we've allocated.
-    // (We may not track this precisely to save space.)
-    size_t approxBytesAllocated() const { return fBytesAllocated; }
-
-private:
-    void makeSpace(size_t bytes);
-
-    size_t fBytesAllocated;
-
-    char* fByte;
-    unsigned fRemaining;
-    unsigned fLgSize;
-
-    struct Block;
-    Block* fBlock;
-};
-static_assert(sizeof(SkVarAlloc) <= 32, "SkVarAllocSize");
-
-#endif//SkVarAlloc_DEFINED
diff --git a/src/core/SkVertState.cpp b/src/core/SkVertState.cpp
index 7c3047e..ef3604d 100644
--- a/src/core/SkVertState.cpp
+++ b/src/core/SkVertState.cpp
@@ -92,13 +92,13 @@
     return true;
 }
 
-VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
+VertState::Proc VertState::chooseProc(SkVertices::VertexMode mode) {
     switch (mode) {
-        case SkCanvas::kTriangles_VertexMode:
+        case SkVertices::kTriangles_VertexMode:
             return fIndices ? TrianglesX : Triangles;
-        case SkCanvas::kTriangleStrip_VertexMode:
+        case SkVertices::kTriangleStrip_VertexMode:
             return fIndices ? TriangleStripX : TriangleStrip;
-        case SkCanvas::kTriangleFan_VertexMode:
+        case SkVertices::kTriangleFan_VertexMode:
             return fIndices ? TriangleFanX : TriangleFan;
         default:
             return nullptr;
diff --git a/src/core/SkVertState.h b/src/core/SkVertState.h
index ab79452..89d224e 100644
--- a/src/core/SkVertState.h
+++ b/src/core/SkVertState.h
@@ -8,7 +8,7 @@
 #ifndef SkVertState_DEFINED
 #define SkVertState_DEFINED
 
-#include "SkCanvas.h"
+#include "SkVertices.h"
 
 /** \struct VertState
     This is a helper for drawVertices(). It is used to iterate over the triangles
@@ -40,7 +40,7 @@
      *  Choose an appropriate function to traverse the vertices.
      *  @param mode    Specifies the SkCanvas::VertexMode.
      */
-    Proc chooseProc(SkCanvas::VertexMode mode);
+    Proc chooseProc(SkVertices::VertexMode mode);
 
 private:
     int             fCount;
diff --git a/src/core/SkVertices.cpp b/src/core/SkVertices.cpp
index 297b424..8dadad0 100644
--- a/src/core/SkVertices.cpp
+++ b/src/core/SkVertices.cpp
@@ -50,7 +50,7 @@
     size_t fISize;
 };
 
-SkVertices::Builder::Builder(SkCanvas::VertexMode mode, int vertexCount, int indexCount,
+SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
                              uint32_t builderFlags) {
     bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag);
     bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag);
@@ -58,12 +58,12 @@
                SkVertices::Sizes(vertexCount, indexCount, hasTexs, hasColors));
 }
 
-SkVertices::Builder::Builder(SkCanvas::VertexMode mode, int vertexCount, int indexCount,
+SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
                              const SkVertices::Sizes& sizes) {
     this->init(mode, vertexCount, indexCount, sizes);
 }
 
-void SkVertices::Builder::init(SkCanvas::VertexMode mode, int vertexCount, int indexCount,
+void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount,
                                const SkVertices::Sizes& sizes) {
     if (!sizes.isValid()) {
         return; // fVertices will already be null
@@ -120,7 +120,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-sk_sp<SkVertices> SkVertices::MakeCopy(SkCanvas::VertexMode mode, int vertexCount,
+sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
                                        const SkPoint pos[], const SkPoint texs[],
                                        const SkColor colors[], int indexCount,
                                        const uint16_t indices[]) {
@@ -169,7 +169,8 @@
 
     Sizes sizes(fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors());
     SkASSERT(sizes.isValid());
-    const size_t size = kHeaderSize + sizes.fArrays;
+    // need to force alignment to 4 for SkWriter32 -- will pad w/ 0s as needed
+    const size_t size = SkAlign4(kHeaderSize + sizes.fArrays);
 
     sk_sp<SkData> data = SkData::MakeUninitialized(size);
     SkWriter32 writer(data->writable_data(), data->size());
@@ -180,7 +181,8 @@
     writer.write(fPositions, sizes.fVSize);
     writer.write(fTexs, sizes.fTSize);
     writer.write(fColors, sizes.fCSize);
-    writer.write(fIndices, sizes.fISize);
+    // if index-count is odd, we won't be 4-bytes aligned, so we call the pad version
+    writer.writePad(fIndices, sizes.fISize);
 
     return data;
 }
@@ -196,14 +198,15 @@
     const int vertexCount = reader.readInt();
     const int indexCount = reader.readInt();
 
-    const SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(packed & kMode_Mask);
+    const VertexMode mode = static_cast<VertexMode>(packed & kMode_Mask);
     const bool hasTexs = SkToBool(packed & kHasTexs_Mask);
     const bool hasColors = SkToBool(packed & kHasColors_Mask);
     Sizes sizes(vertexCount, indexCount, hasTexs, hasColors);
     if (!sizes.isValid()) {
         return nullptr;
     }
-    if (kHeaderSize + sizes.fArrays != length) {
+    // logically we can be only 2-byte aligned, but our buffer is always 4-byte aligned
+    if (SkAlign4(kHeaderSize + sizes.fArrays) != length) {
         return nullptr;
     }
 
diff --git a/src/core/SkWriteBuffer.cpp b/src/core/SkWriteBuffer.cpp
index 79422ab..3322e8a 100644
--- a/src/core/SkWriteBuffer.cpp
+++ b/src/core/SkWriteBuffer.cpp
@@ -154,9 +154,9 @@
     this->writeBool(false);
 
     // see if the caller wants to manually encode
-    SkAutoPixmapUnlock result;
-    if (fPixelSerializer && bitmap.requestLock(&result)) {
-        sk_sp<SkData> data(fPixelSerializer->encode(result.pixmap()));
+    SkPixmap result;
+    if (fPixelSerializer && bitmap.peekPixels(&result)) {
+        sk_sp<SkData> data(fPixelSerializer->encode(result));
         if (data) {
             // if we have to "encode" the bitmap, then we assume there is no
             // offset to share, since we are effectively creating a new pixelref
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 6f470f6..31f822f 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBlendModePriv.h"
 #include "SkXfermode_proccoeff.h"
 #include "SkColorPriv.h"
 #include "SkMathPriv.h"
@@ -954,36 +955,36 @@
 }
 
 const ProcCoeff gProcCoeffs[] = {
-    { clear_modeproc,       proc_4f<clear_4f>,      SkXfermode::kZero_Coeff,    SkXfermode::kZero_Coeff },
-    { src_modeproc,         proc_4f<src_4f>,        SkXfermode::kOne_Coeff,     SkXfermode::kZero_Coeff },
-    { dst_modeproc,         proc_4f<dst_4f>,        SkXfermode::kZero_Coeff,    SkXfermode::kOne_Coeff  },
-    { srcover_modeproc,     proc_4f<srcover_4f>,    SkXfermode::kOne_Coeff,     SkXfermode::kISA_Coeff  },
-    { dstover_modeproc,     proc_4f<dstover_4f>,    SkXfermode::kIDA_Coeff,     SkXfermode::kOne_Coeff  },
-    { srcin_modeproc,       proc_4f<srcin_4f>,      SkXfermode::kDA_Coeff,      SkXfermode::kZero_Coeff },
-    { dstin_modeproc,       proc_4f<dstin_4f>,      SkXfermode::kZero_Coeff,    SkXfermode::kSA_Coeff   },
-    { srcout_modeproc,      proc_4f<srcout_4f>,     SkXfermode::kIDA_Coeff,     SkXfermode::kZero_Coeff },
-    { dstout_modeproc,      proc_4f<dstout_4f>,     SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff  },
-    { srcatop_modeproc,     proc_4f<srcatop_4f>,    SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff  },
-    { dstatop_modeproc,     proc_4f<dstatop_4f>,    SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff   },
-    { xor_modeproc,         proc_4f<xor_4f>,        SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff  },
+    { clear_modeproc,       proc_4f<clear_4f>       },
+    { src_modeproc,         proc_4f<src_4f>         },
+    { dst_modeproc,         proc_4f<dst_4f>         },
+    { srcover_modeproc,     proc_4f<srcover_4f>     },
+    { dstover_modeproc,     proc_4f<dstover_4f>     },
+    { srcin_modeproc,       proc_4f<srcin_4f>       },
+    { dstin_modeproc,       proc_4f<dstin_4f>       },
+    { srcout_modeproc,      proc_4f<srcout_4f>      },
+    { dstout_modeproc,      proc_4f<dstout_4f>      },
+    { srcatop_modeproc,     proc_4f<srcatop_4f>     },
+    { dstatop_modeproc,     proc_4f<dstatop_4f>     },
+    { xor_modeproc,         proc_4f<xor_4f>         },
 
-    { plus_modeproc,        proc_4f<plus_4f>,       SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff  },
-    { modulate_modeproc,    proc_4f<modulate_4f>,   SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff   },
-    { screen_modeproc,      proc_4f<screen_4f>,     SkXfermode::kOne_Coeff,     SkXfermode::kISC_Coeff  },
-    { overlay_modeproc,     proc_4f<overlay_4f>,    CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { darken_modeproc,      proc_4f<darken_4f>,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { lighten_modeproc,     proc_4f<lighten_4f>,    CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { colordodge_modeproc,  proc_4f<colordodge_4f>, CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { colorburn_modeproc,   proc_4f<colorburn_4f>,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { hardlight_modeproc,   proc_4f<hardlight_4f>,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { softlight_modeproc,   proc_4f<softlight_4f>,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { difference_modeproc,  proc_4f<difference_4f>, CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { exclusion_modeproc,   proc_4f<exclusion_4f>,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { multiply_modeproc,    proc_4f<multiply_4f>,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { hue_modeproc,         proc_4f<hue_4f>,        CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { saturation_modeproc,  proc_4f<saturation_4f>, CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { color_modeproc,       proc_4f<color_4f>,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
-    { luminosity_modeproc,  proc_4f<luminosity_4f>, CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { plus_modeproc,        proc_4f<plus_4f>        },
+    { modulate_modeproc,    proc_4f<modulate_4f>    },
+    { screen_modeproc,      proc_4f<screen_4f>      },
+    { overlay_modeproc,     proc_4f<overlay_4f>     },
+    { darken_modeproc,      proc_4f<darken_4f>      },
+    { lighten_modeproc,     proc_4f<lighten_4f>     },
+    { colordodge_modeproc,  proc_4f<colordodge_4f>  },
+    { colorburn_modeproc,   proc_4f<colorburn_4f>   },
+    { hardlight_modeproc,   proc_4f<hardlight_4f>   },
+    { softlight_modeproc,   proc_4f<softlight_4f>   },
+    { difference_modeproc,  proc_4f<difference_4f>  },
+    { exclusion_modeproc,   proc_4f<exclusion_4f>   },
+    { multiply_modeproc,    proc_4f<multiply_4f>    },
+    { hue_modeproc,         proc_4f<hue_4f>         },
+    { saturation_modeproc,  proc_4f<saturation_4f>  },
+    { color_modeproc,       proc_4f<color_4f>       },
+    { luminosity_modeproc,  proc_4f<luminosity_4f>  },
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1038,57 +1039,6 @@
     }
 }
 
-void SkXfermode::xfer16(uint16_t* dst,
-                        const SkPMColor* SK_RESTRICT src, int count,
-                        const SkAlpha* SK_RESTRICT aa) const {
-    SkASSERT(dst && src && count >= 0);
-
-    if (nullptr == aa) {
-        for (int i = count - 1; i >= 0; --i) {
-            SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-            dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
-        }
-    } else {
-        for (int i = count - 1; i >= 0; --i) {
-            unsigned a = aa[i];
-            if (0 != a) {
-                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-                SkPMColor C = this->xferColor(src[i], dstC);
-                if (0xFF != a) {
-                    C = SkFourByteInterp(C, dstC, a);
-                }
-                dst[i] = SkPixel32ToPixel16_ToU16(C);
-            }
-        }
-    }
-}
-
-void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
-                        const SkPMColor src[], int count,
-                        const SkAlpha* SK_RESTRICT aa) const {
-    SkASSERT(dst && src && count >= 0);
-
-    if (nullptr == aa) {
-        for (int i = count - 1; i >= 0; --i) {
-            SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
-            dst[i] = SkToU8(SkGetPackedA32(res));
-        }
-    } else {
-        for (int i = count - 1; i >= 0; --i) {
-            unsigned a = aa[i];
-            if (0 != a) {
-                SkAlpha dstA = dst[i];
-                unsigned A = SkGetPackedA32(this->xferColor(src[i],
-                                            (SkPMColor)(dstA << SK_A32_SHIFT)));
-                if (0xFF != a) {
-                    A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
-                }
-                dst[i] = SkToU8(A);
-            }
-        }
-    }
-}
-
 bool SkXfermode::supportsCoverageAsAlpha() const {
     return false;
 }
@@ -1120,44 +1070,11 @@
 }
 
 bool SkProcCoeffXfermode::supportsCoverageAsAlpha() const {
-    if (CANNOT_USE_COEFF == fSrcCoeff) {
-        return false;
-    }
-
-    switch (fDstCoeff) {
-        case SkXfermode::kOne_Coeff:
-        case SkXfermode::kISA_Coeff:
-        case SkXfermode::kISC_Coeff:
-            return true;
-        default:
-            return false;
-    }
+    return SkBlendMode_SupportsCoverageAsAlpha(fMode);
 }
 
 bool SkProcCoeffXfermode::isOpaque(SkXfermode::SrcColorOpacity opacityType) const {
-    if (CANNOT_USE_COEFF == fSrcCoeff) {
-        return false;
-    }
-
-    if (SkXfermode::kDA_Coeff == fSrcCoeff || SkXfermode::kDC_Coeff == fSrcCoeff ||
-        SkXfermode::kIDA_Coeff == fSrcCoeff || SkXfermode::kIDC_Coeff == fSrcCoeff) {
-        return false;
-    }
-
-    switch (fDstCoeff) {
-        case SkXfermode::kZero_Coeff:
-            return true;
-        case SkXfermode::kISA_Coeff:
-            return SkXfermode::kOpaque_SrcColorOpacity == opacityType;
-        case SkXfermode::kSA_Coeff:
-            return SkXfermode::kTransparentBlack_SrcColorOpacity == opacityType ||
-                   SkXfermode::kTransparentAlpha_SrcColorOpacity == opacityType;
-        case SkXfermode::kSC_Coeff:
-            return SkXfermode::kTransparentBlack_SrcColorOpacity == opacityType;
-        default:
-            return false;
-    }
-
+    return SkXfermode::IsOpaque(fMode, opacityType);
 }
 
 void SkProcCoeffXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
@@ -1188,65 +1105,6 @@
     }
 }
 
-void SkProcCoeffXfermode::xfer16(uint16_t* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src, int count,
-                                 const SkAlpha* SK_RESTRICT aa) const {
-    SkASSERT(dst && src && count >= 0);
-
-    SkXfermodeProc proc = fProc;
-
-    if (proc) {
-        if (nullptr == aa) {
-            for (int i = count - 1; i >= 0; --i) {
-                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-                dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
-            }
-        } else {
-            for (int i = count - 1; i >= 0; --i) {
-                unsigned a = aa[i];
-                if (0 != a) {
-                    SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
-                    SkPMColor C = proc(src[i], dstC);
-                    if (0xFF != a) {
-                        C = SkFourByteInterp(C, dstC, a);
-                    }
-                    dst[i] = SkPixel32ToPixel16_ToU16(C);
-                }
-            }
-        }
-    }
-}
-
-void SkProcCoeffXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src, int count,
-                                 const SkAlpha* SK_RESTRICT aa) const {
-    SkASSERT(dst && src && count >= 0);
-
-    SkXfermodeProc proc = fProc;
-
-    if (proc) {
-        if (nullptr == aa) {
-            for (int i = count - 1; i >= 0; --i) {
-                SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
-                dst[i] = SkToU8(SkGetPackedA32(res));
-            }
-        } else {
-            for (int i = count - 1; i >= 0; --i) {
-                unsigned a = aa[i];
-                if (0 != a) {
-                    SkAlpha dstA = dst[i];
-                    SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
-                    unsigned A = SkGetPackedA32(res);
-                    if (0xFF != a) {
-                        A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
-                    }
-                    dst[i] = SkToU8(A);
-                }
-            }
-        }
-    }
-}
-
 #if SK_SUPPORT_GPU
 sk_sp<GrFragmentProcessor> SkProcCoeffXfermode::makeFragmentProcessorForImageFilter(
                                                             sk_sp<GrFragmentProcessor> dst) const {
@@ -1255,7 +1113,7 @@
 }
 
 const GrXPFactory* SkProcCoeffXfermode::asXPFactory() const {
-    if (CANNOT_USE_COEFF != fSrcCoeff) {
+    if (SkBlendMode_AsCoeff(fMode, nullptr, nullptr)) {
         const GrXPFactory* result(GrPorterDuffXPFactory::Get(fMode));
         SkASSERT(result);
         return result;
@@ -1289,24 +1147,6 @@
 
     str->append("mode: ");
     str->append(ModeName(fMode));
-
-    static const char* gCoeffStrings[kCoeffCount] = {
-        "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
-    };
-
-    str->append(" src: ");
-    if (CANNOT_USE_COEFF == fSrcCoeff) {
-        str->append("can't use");
-    } else {
-        str->append(gCoeffStrings[fSrcCoeff]);
-    }
-
-    str->append(" dst: ");
-    if (CANNOT_USE_COEFF == fDstCoeff) {
-        str->append("can't use");
-    } else {
-        str->append(gCoeffStrings[fDstCoeff]);
-    }
 }
 #endif
 
@@ -1356,30 +1196,6 @@
     return proc;
 }
 
-bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
-    SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
-
-    if ((unsigned)mode >= (unsigned)kModeCount) {
-        // illegal mode parameter
-        return false;
-    }
-
-    const ProcCoeff& rec = gProcCoeffs[mode];
-
-    if (CANNOT_USE_COEFF == rec.fSC) {
-        return false;
-    }
-
-    SkASSERT(CANNOT_USE_COEFF != rec.fDC);
-    if (src) {
-        *src = rec.fSC;
-    }
-    if (dst) {
-        *dst = rec.fDC;
-    }
-    return true;
-}
-
 bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
     if (nullptr == xfer) {
         if (mode) {
@@ -1423,44 +1239,31 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode) {
-    switch (mode) {
-        case SkBlendMode::kDst:
-        case SkBlendMode::kSrcOver:
-        case SkBlendMode::kDstOver:
-        case SkBlendMode::kDstOut:
-        case SkBlendMode::kSrcATop:
-        case SkBlendMode::kXor:
-        case SkBlendMode::kPlus:
-            return true;
-        default:
-            break;
-    }
-    return false;
-}
-
 bool SkXfermode::IsOpaque(SkBlendMode mode, SrcColorOpacity opacityType) {
-    const ProcCoeff rec = gProcCoeffs[(int)mode];
+    SkBlendModeCoeff src, dst;
+    if (!SkBlendMode_AsCoeff(mode, &src, &dst)) {
+        return false;
+    }
 
-    switch (rec.fSC) {
-        case kDA_Coeff:
-        case kDC_Coeff:
-        case kIDA_Coeff:
-        case kIDC_Coeff:
+    switch (src) {
+        case SkBlendModeCoeff::kDA:
+        case SkBlendModeCoeff::kDC:
+        case SkBlendModeCoeff::kIDA:
+        case SkBlendModeCoeff::kIDC:
             return false;
         default:
             break;
     }
 
-    switch (rec.fDC) {
-        case kZero_Coeff:
+    switch (dst) {
+        case SkBlendModeCoeff::kZero:
             return true;
-        case kISA_Coeff:
+        case SkBlendModeCoeff::kISA:
             return kOpaque_SrcColorOpacity == opacityType;
-        case kSA_Coeff:
+        case SkBlendModeCoeff::kSA:
             return kTransparentBlack_SrcColorOpacity == opacityType ||
             kTransparentAlpha_SrcColorOpacity == opacityType;
-        case kSC_Coeff:
+        case SkBlendModeCoeff::kSC:
             return kTransparentBlack_SrcColorOpacity == opacityType;
         default:
             return false;
@@ -1470,8 +1273,7 @@
 
 #if SK_SUPPORT_GPU
 const GrXPFactory* SkBlendMode_AsXPFactory(SkBlendMode mode) {
-    const ProcCoeff rec = gProcCoeffs[(int)mode];
-    if (CANNOT_USE_COEFF != rec.fSC) {
+    if (SkBlendMode_AsCoeff(mode, nullptr, nullptr)) {
         const GrXPFactory* result = GrPorterDuffXPFactory::Get(mode);
         SkASSERT(result);
         return result;
@@ -1482,45 +1284,3 @@
 }
 #endif
 
-bool SkBlendMode_CanOverflow(SkBlendMode mode) { return mode == SkBlendMode::kPlus; }
-
-bool SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) {
-    auto stage = SkRasterPipeline::srcover;
-    switch (mode) {
-        case SkBlendMode::kClear:    stage = SkRasterPipeline::clear; break;
-        case SkBlendMode::kSrc:      return true;  // This stage is a no-op.
-        case SkBlendMode::kDst:      stage = SkRasterPipeline::move_dst_src; break;
-        case SkBlendMode::kSrcOver:  stage = SkRasterPipeline::srcover; break;
-        case SkBlendMode::kDstOver:  stage = SkRasterPipeline::dstover; break;
-        case SkBlendMode::kSrcIn:    stage = SkRasterPipeline::srcin; break;
-        case SkBlendMode::kDstIn:    stage = SkRasterPipeline::dstin; break;
-        case SkBlendMode::kSrcOut:   stage = SkRasterPipeline::srcout; break;
-        case SkBlendMode::kDstOut:   stage = SkRasterPipeline::dstout; break;
-        case SkBlendMode::kSrcATop:  stage = SkRasterPipeline::srcatop; break;
-        case SkBlendMode::kDstATop:  stage = SkRasterPipeline::dstatop; break;
-        case SkBlendMode::kXor:      stage = SkRasterPipeline::xor_; break;
-        case SkBlendMode::kPlus:     stage = SkRasterPipeline::plus_; break;
-        case SkBlendMode::kModulate: stage = SkRasterPipeline::modulate; break;
-
-        case SkBlendMode::kScreen:     stage = SkRasterPipeline::screen; break;
-        case SkBlendMode::kOverlay:    stage = SkRasterPipeline::overlay; break;
-        case SkBlendMode::kDarken:     stage = SkRasterPipeline::darken; break;
-        case SkBlendMode::kLighten:    stage = SkRasterPipeline::lighten; break;
-        case SkBlendMode::kColorDodge: stage = SkRasterPipeline::colordodge; break;
-        case SkBlendMode::kColorBurn:  stage = SkRasterPipeline::colorburn; break;
-        case SkBlendMode::kHardLight:  stage = SkRasterPipeline::hardlight; break;
-        case SkBlendMode::kSoftLight:  stage = SkRasterPipeline::softlight; break;
-        case SkBlendMode::kDifference: stage = SkRasterPipeline::difference; break;
-        case SkBlendMode::kExclusion:  stage = SkRasterPipeline::exclusion; break;
-        case SkBlendMode::kMultiply:   stage = SkRasterPipeline::multiply; break;
-
-        case SkBlendMode::kHue:
-        case SkBlendMode::kSaturation:
-        case SkBlendMode::kColor:
-        case SkBlendMode::kLuminosity: return false;  // TODO
-    }
-    if (p) {
-        p->append(stage);
-    }
-    return true;
-}
diff --git a/src/core/SkXfermodePriv.h b/src/core/SkXfermodePriv.h
index 2fae2c0..f5cee5a 100644
--- a/src/core/SkXfermodePriv.h
+++ b/src/core/SkXfermodePriv.h
@@ -38,27 +38,6 @@
 public:
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
                         const SkAlpha aa[]) const;
-    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const;
-    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const;
-
-    /** Enum of possible coefficients to describe some xfermodes
-     */
-    enum Coeff {
-        kZero_Coeff,    /** 0 */
-        kOne_Coeff,     /** 1 */
-        kSC_Coeff,      /** src color */
-        kISC_Coeff,     /** inverse src color (i.e. 1 - sc) */
-        kDC_Coeff,      /** dst color */
-        kIDC_Coeff,     /** inverse dst color (i.e. 1 - dc) */
-        kSA_Coeff,      /** src alpha */
-        kISA_Coeff,     /** inverse src alpha (i.e. 1 - sa) */
-        kDA_Coeff,      /** dst alpha */
-        kIDA_Coeff,     /** inverse dst alpha (i.e. 1 - da) */
-
-        kCoeffCount
-    };
 
     /** List of predefined xfermodes.
         The algebra for the modes uses the following symbols:
@@ -67,9 +46,6 @@
         [a, c] - Resulting (alpha, color) values
         For these equations, the colors are in premultiplied state.
         If no xfermode is specified, kSrcOver is assumed.
-        The modes are ordered by those that can be expressed as a pair of Coeffs, followed by those
-        that aren't Coeffs but have separable r,g,b computations, and finally
-        those that are not separable.
      */
     enum Mode {
         kClear_Mode,    //!< [0, 0]
@@ -180,17 +156,6 @@
     static SkXfermodeProc4f GetProc4f(SkBlendMode);
 
     /**
-     *  If the specified mode can be represented by a pair of Coeff, then return
-     *  true and set (if not NULL) the corresponding coeffs. If the mode is
-     *  not representable as a pair of Coeffs, return false and ignore the
-     *  src and dst parameters.
-     */
-    static bool ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst);
-    static bool ModeAsCoeff(SkBlendMode mode, Coeff* src, Coeff* dst) {
-        return ModeAsCoeff((Mode)mode, src, dst);
-    }
-
-    /**
      * Returns whether or not the xfer mode can support treating coverage as alpha
      */
     virtual bool supportsCoverageAsAlpha() const;
diff --git a/src/core/SkXfermode_proccoeff.h b/src/core/SkXfermode_proccoeff.h
index 3724532..6704a82 100644
--- a/src/core/SkXfermode_proccoeff.h
+++ b/src/core/SkXfermode_proccoeff.h
@@ -14,8 +14,6 @@
 struct ProcCoeff {
     SkXfermodeProc      fProc;
     SkXfermodeProc4f    fProc4f;
-    SkXfermode::Coeff   fSC;
-    SkXfermode::Coeff   fDC;
 };
 
 #define CANNOT_USE_COEFF    SkXfermode::Coeff(-1)
@@ -25,17 +23,10 @@
     SkProcCoeffXfermode(const ProcCoeff& rec, SkBlendMode mode) {
         fMode = mode;
         fProc = rec.fProc;
-        // these may be valid, or may be CANNOT_USE_COEFF
-        fSrcCoeff = rec.fSC;
-        fDstCoeff = rec.fDC;
     }
 
     void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
                 const SkAlpha aa[]) const override;
-    void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                const SkAlpha aa[]) const override;
-    void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                const SkAlpha aa[]) const override;
 
     bool asMode(Mode* mode) const override;
 
@@ -62,7 +53,6 @@
 private:
     SkXfermodeProc  fProc;
     SkBlendMode     fMode;
-    Coeff           fSrcCoeff, fDstCoeff;
 
     friend class SkXfermode;
 
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.cpp b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
index b4ad0cc..de1b74e 100644
--- a/src/effects/GrAlphaThresholdFragmentProcessor.cpp
+++ b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
@@ -37,15 +37,13 @@
         : INHERITED(OptFlags(outerThreshold))
         , fInnerThreshold(innerThreshold)
         , fOuterThreshold(outerThreshold)
-        , fImageCoordTransform(resourceProvider, SkMatrix::I(), proxy.get(),
-                               GrSamplerParams::kNone_FilterMode)
+        , fImageCoordTransform(resourceProvider, SkMatrix::I(), proxy.get())
         , fImageTextureSampler(resourceProvider, std::move(proxy))
         , fColorSpaceXform(std::move(colorSpaceXform))
         , fMaskCoordTransform(
                   resourceProvider,
                   SkMatrix::MakeTrans(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y())),
-                  maskProxy.get(),
-                  GrSamplerParams::kNone_FilterMode)
+                  maskProxy.get())
         , fMaskTextureSampler(resourceProvider, maskProxy) {
     this->initClassID<GrAlphaThresholdFragmentProcessor>();
     this->addCoordTransform(&fImageCoordTransform);
@@ -74,7 +72,7 @@
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fInnerThresholdVar;
@@ -129,12 +127,11 @@
                             "color.a = inner_thresh;"
                             "}");
 
-    fragBuilder->codeAppendf("%s = %s;", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr4("color")).c_str());
+    fragBuilder->codeAppendf("%s = %s * color;", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLAlphaThresholdFragmentProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
-                                                    const GrProcessor& proc) {
+                                                    const GrFragmentProcessor& proc) {
     const GrAlphaThresholdFragmentProcessor& atfp = proc.cast<GrAlphaThresholdFragmentProcessor>();
     pdman.set1f(fInnerThresholdVar, atfp.innerThreshold());
     pdman.set1f(fOuterThresholdVar, atfp.outerThreshold());
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp
index 96a19c3..56dd456 100644
--- a/src/effects/GrCircleBlurFragmentProcessor.cpp
+++ b/src/effects/GrCircleBlurFragmentProcessor.cpp
@@ -23,7 +23,7 @@
     void emitCode(EmitArgs&) override;
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fDataUniform;
@@ -68,7 +68,7 @@
 }
 
 void GrCircleBlurFragmentProcessor::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
-                                                             const GrProcessor& proc) {
+                                                             const GrFragmentProcessor& proc) {
     const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentProcessor>();
     const SkRect& circle = cbfp.fCircle;
 
diff --git a/src/effects/SkAlphaThresholdFilter.cpp b/src/effects/SkAlphaThresholdFilter.cpp
index cf3896d..b725257 100644
--- a/src/effects/SkAlphaThresholdFilter.cpp
+++ b/src/effects/SkAlphaThresholdFilter.cpp
@@ -37,8 +37,10 @@
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
 
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
+
 #if SK_SUPPORT_GPU
-    sk_sp<GrTextureProxy> createMaskTexture(GrContext*, 
+    sk_sp<GrTextureProxy> createMaskTexture(GrContext*,
                                             const SkMatrix&,
                                             const SkIRect& bounds) const;
 #endif
@@ -196,8 +198,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels inputLock(inputBM);
-
     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
         return nullptr;
     }
@@ -216,8 +216,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels dstLock(dst);
-
     U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
     U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
     SkColor* dptr = dst.getAddr32(0, 0);
@@ -262,6 +260,18 @@
                                           dst);
 }
 
+sk_sp<SkImageFilter> SkAlphaThresholdFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkAlphaThresholdFilterImpl*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkAlphaThresholdFilter::Make(fRegion, fInnerThreshold, fOuterThreshold,
+                                        std::move(input), this->getCropRectIfSet());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkAlphaThresholdFilterImpl::toString(SkString* str) const {
     str->appendf("SkAlphaThresholdImageFilter: (");
diff --git a/src/effects/SkArithmeticImageFilter.cpp b/src/effects/SkArithmeticImageFilter.cpp
index 8ad4157..d9c2b60 100644
--- a/src/effects/SkArithmeticImageFilter.cpp
+++ b/src/effects/SkArithmeticImageFilter.cpp
@@ -28,7 +28,6 @@
 #include "glsl/GrGLSLUniformHandler.h"
 #endif
 
-namespace {
 class ArithmeticImageFilterImpl : public SkImageFilter {
 public:
     ArithmeticImageFilterImpl(float k1, float k2, float k3, float k4, bool enforcePMColor,
@@ -62,6 +61,8 @@
 
     void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const;
 
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
+
 private:
     const float fK[4];
     const bool fEnforcePMColor;
@@ -70,7 +71,6 @@
 
     typedef SkImageFilter INHERITED;
 };
-}
 
 sk_sp<SkFlattenable> ArithmeticImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
@@ -238,7 +238,7 @@
 
                 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
                 SkString dstColor("dstColor");
-                this->emitChild(0, nullptr, &dstColor, args);
+                this->emitChild(0, &dstColor, args);
 
                 fKUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType,
                                                          kDefault_GrSLPrecision, "k");
@@ -264,7 +264,7 @@
 
         protected:
             void onSetData(const GrGLSLProgramDataManager& pdman,
-                           const GrProcessor& proc) override {
+                           const GrFragmentProcessor& proc) override {
                 const ArithmeticFP& arith = proc.cast<ArithmeticFP>();
                 pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
             }
@@ -431,7 +431,6 @@
         if (!img->getROPixels(&srcBM)) {
             return;
         }
-        srcBM.lockPixels();
         if (!srcBM.peekPixels(&src)) {
             return;
         }
@@ -457,6 +456,22 @@
     }
 }
 
+sk_sp<SkImageFilter> ArithmeticImageFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(2 == this->countInputs());
+    if (!this->getInput(0) && !this->getInput(1)) {
+        return sk_ref_sp(const_cast<ArithmeticImageFilterImpl*>(this));
+    }
+
+    sk_sp<SkImageFilter> background =
+            this->getInput(0) ? this->getInput(0)->makeColorSpace(xformer) : nullptr;
+    sk_sp<SkImageFilter> foreground =
+            this->getInput(1) ? this->getInput(1)->makeColorSpace(xformer) : nullptr;
+    return SkArithmeticImageFilter::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor,
+                                         std::move(background), std::move(foreground),
+                                         getCropRectIfSet());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void ArithmeticImageFilterImpl::toString(SkString* str) const {
     str->appendf("SkArithmeticImageFilter: (");
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index ce3c504..eee1631 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -75,17 +75,17 @@
  *          }
  *      }
  */
+template <bool Transpose>
 static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst,
-                   int leftRadius, int rightRadius, int width, int height,
-                   bool transpose)
+                   int leftRadius, int rightRadius, int width, int height)
 {
     int diameter = leftRadius + rightRadius;
     int kernelSize = diameter + 1;
     int border = SkMin32(width, diameter);
     uint32_t scale = (1 << 24) / kernelSize;
     int new_width = width + SkMax32(leftRadius, rightRadius) * 2;
-    int dst_x_stride = transpose ? height : 1;
-    int dst_y_stride = transpose ? 1 : new_width;
+    int dst_x_stride = Transpose ? height : 1;
+    int dst_y_stride = Transpose ? 1 : new_width;
     uint32_t half = 1 << 23;
     for (int y = 0; y < height; ++y) {
         uint32_t sum = 0;
@@ -277,9 +277,10 @@
  *  return new_width;
  */
 
+template <bool Transpose>
 static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
                          int radius, int width, int height,
-                         bool transpose, uint8_t outer_weight)
+                         uint8_t outer_weight)
 {
     int diameter = radius * 2;
     int kernelSize = diameter + 1;
@@ -291,8 +292,8 @@
     uint32_t inner_scale = (inner_weight << 16) / (kernelSize - 2);
     uint32_t half = 1 << 23;
     int new_width = width + diameter;
-    int dst_x_stride = transpose ? height : 1;
-    int dst_y_stride = transpose ? 1 : new_width;
+    int dst_x_stride = Transpose ? height : 1;
+    int dst_y_stride = Transpose ? 1 : new_width;
     for (int y = 0; y < height; ++y) {
         uint32_t outer_sum = 0, inner_sum = 0;
         uint8_t* dptr = dst + y * dst_y_stride;
@@ -553,30 +554,30 @@
             get_adjusted_radii(passRadius, &loRadius, &hiRadius);
             if (kHigh_SkBlurQuality == quality) {
                 // Do three X blurs, with a transpose on the final one.
-                w = boxBlur(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h, false);
-                w = boxBlur(tp, w,             dp, hiRadius, loRadius, w, h, false);
-                w = boxBlur(dp, w,             tp, hiRadius, hiRadius, w, h, true);
+                w = boxBlur<false>(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h);
+                w = boxBlur<false>(tp, w,             dp, hiRadius, loRadius, w, h);
+                w = boxBlur<true>(dp, w,             tp, hiRadius, hiRadius, w, h);
                 // Do three Y blurs, with a transpose on the final one.
-                h = boxBlur(tp, h,             dp, loRadius, hiRadius, h, w, false);
-                h = boxBlur(dp, h,             tp, hiRadius, loRadius, h, w, false);
-                h = boxBlur(tp, h,             dp, hiRadius, hiRadius, h, w, true);
+                h = boxBlur<false>(tp, h,             dp, loRadius, hiRadius, h, w);
+                h = boxBlur<false>(dp, h,             tp, hiRadius, loRadius, h, w);
+                h = boxBlur<true>(tp, h,             dp, hiRadius, hiRadius, h, w);
             } else {
-                w = boxBlur(sp, src.fRowBytes, tp, rx, rx, w, h, true);
-                h = boxBlur(tp, h,             dp, ry, ry, h, w, true);
+                w = boxBlur<true>(sp, src.fRowBytes, tp, rx, rx, w, h);
+                h = boxBlur<true>(tp, h,             dp, ry, ry, h, w);
             }
         } else {
             if (kHigh_SkBlurQuality == quality) {
                 // Do three X blurs, with a transpose on the final one.
-                w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outerWeight);
-                w = boxBlurInterp(tp, w,             dp, rx, w, h, false, outerWeight);
-                w = boxBlurInterp(dp, w,             tp, rx, w, h, true, outerWeight);
+                w = boxBlurInterp<false>(sp, src.fRowBytes, tp, rx, w, h, outerWeight);
+                w = boxBlurInterp<false>(tp, w,             dp, rx, w, h, outerWeight);
+                w = boxBlurInterp<true>(dp, w,             tp, rx, w, h, outerWeight);
                 // Do three Y blurs, with a transpose on the final one.
-                h = boxBlurInterp(tp, h,             dp, ry, h, w, false, outerWeight);
-                h = boxBlurInterp(dp, h,             tp, ry, h, w, false, outerWeight);
-                h = boxBlurInterp(tp, h,             dp, ry, h, w, true, outerWeight);
+                h = boxBlurInterp<false>(tp, h,             dp, ry, h, w, outerWeight);
+                h = boxBlurInterp<false>(dp, h,             tp, ry, h, w, outerWeight);
+                h = boxBlurInterp<true>(tp, h,             dp, ry, h, w, outerWeight);
             } else {
-                w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outerWeight);
-                h = boxBlurInterp(tp, h,             dp, ry, h, w, true, outerWeight);
+                w = boxBlurInterp<true>(sp, src.fRowBytes, tp, rx, w, h, outerWeight);
+                h = boxBlurInterp<true>(tp, h,             dp, ry, h, w, outerWeight);
             }
         }
 
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 1206cff..a44105d 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -14,6 +14,7 @@
 #include "SkRRect.h"
 #include "SkStringUtils.h"
 #include "SkStrokeRec.h"
+#include "SkVertices.h"
 
 #if SK_SUPPORT_GPU
 #include "GrCircleBlurFragmentProcessor.h"
@@ -844,7 +845,7 @@
     static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
@@ -935,7 +936,7 @@
 }
 
 void GrGLRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                   const GrProcessor& proc) {
+                                   const GrFragmentProcessor& proc) {
     const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
     SkRect rect = rbe.getRect();
 
@@ -1036,7 +1037,7 @@
 
     SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
 
-    GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider();
+    GrResourceProvider* resourceProvider = context->resourceProvider();
     sk_sp<GrFragmentProcessor> fp;
 
     SkRect rect;
@@ -1249,7 +1250,7 @@
     void emitCode(EmitArgs&) override;
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fProxyRectUniform;
@@ -1315,7 +1316,7 @@
 }
 
 void GrGLRRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                    const GrProcessor& proc) {
+                                    const GrFragmentProcessor& proc) {
     const GrRRectBlurEffect& brre = proc.cast<GrRRectBlurEffect>();
     const SkRRect& rrect = brre.getRRect();
 
@@ -1359,14 +1360,10 @@
         return false;
     }
 
-    GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider();
+    GrResourceProvider* resourceProvider = context->resourceProvider();
     SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
 
     if (devRRect.isRect() || devRRect.isCircle()) {
-        if (this->ignoreXform()) {
-            return false;
-        }
-
         sk_sp<GrFragmentProcessor> fp;
         if (devRRect.isRect()) {
             SkScalar pad = 3.0f * xformedSigma;
@@ -1381,11 +1378,19 @@
         if (!fp) {
             return false;
         }
-
         paint.addCoverageFragmentProcessor(std::move(fp));
 
         SkRect srcProxyRect = srcRRect.rect();
-        srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
+        SkScalar outsetX = 3.0f*fSigma;
+        SkScalar outsetY = 3.0f*fSigma;
+        if (this->ignoreXform()) {
+            // When we're ignoring the CTM the padding added to the source rect also needs to ignore
+            // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we
+            // can just grab the X and Y scales off the matrix and pre-undo the scale.
+            outsetX /= viewMatrix.getScaleX();
+            outsetY /= viewMatrix.getScaleY();
+        }
+        srcProxyRect.outset(outsetX, outsetY);
 
         renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
         return true;
@@ -1401,38 +1406,32 @@
         SkRect srcProxyRect = srcRRect.rect();
         srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
 
-        SkPoint points[8];
-        uint16_t indices[24];
-        int numPoints, numIndices;
-
+        sk_sp<SkVertices> vertices = nullptr;
         SkRect temp = fOccluder;
 
         if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
-            srcProxyRect.toQuad(points);
-            temp.toQuad(&points[4]);
-            numPoints = 8;
+            SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 8, 24, 0);
+            srcProxyRect.toQuad(builder.positions());
+            temp.toQuad(builder.positions() + 4);
 
             static const uint16_t ringI[24] = { 0, 1, 5, 5, 4, 0,
                                                 1, 2, 6, 6, 5, 1,
                                                 2, 3, 7, 7, 6, 2,
                                                 3, 0, 4, 4, 7, 3 };
-            memcpy(indices, ringI, sizeof(ringI));
-            numIndices = 24;
+            memcpy(builder.indices(), ringI, sizeof(ringI));
+            vertices = builder.detach();
         } else {
             // full rect case
-            srcProxyRect.toQuad(points);
-            numPoints = 4;
+            SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0);
+            srcProxyRect.toQuad(builder.positions());
 
             static const uint16_t fullI[6] = { 0, 1, 2, 0, 2, 3 };
-            memcpy(indices, fullI, sizeof(fullI));
-            numIndices = 6;
+            memcpy(builder.indices(), fullI, sizeof(fullI));
+            vertices = builder.detach();
         }
 
         paint.addCoverageFragmentProcessor(std::move(fp));
-        renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix,
-                                          kTriangles_GrPrimitiveType, numPoints, points, nullptr,
-                                          nullptr, indices, numIndices);
-
+        renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices));
     } else {
         SkMatrix inverse;
         if (!viewMatrix.invert(&inverse)) {
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index 507a805..75dccfe 100644
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -9,6 +9,7 @@
 
 #include "SkCanvas.h"
 #include "SkColorFilter.h"
+#include "SkColorSpaceXformer.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
@@ -116,6 +117,18 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkColorFilterImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs());
+
+    sk_sp<SkImageFilter> input =
+            this->getInput(0) ? this->getInput(0)->makeColorSpace(xformer) : nullptr;
+    sk_sp<SkColorFilter> colorFilter = xformer->apply(fColorFilter.get());
+
+    return SkColorFilterImageFilter::Make(std::move(colorFilter), std::move(input),
+                                          this->getCropRectIfSet());
+}
+
 bool SkColorFilterImageFilter::onIsColorFilterNode(SkColorFilter** filter) const {
     SkASSERT(1 == this->countInputs());
     if (!this->cropRectIsSet()) {
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index 0a2f08f..12cfbb3 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -6,6 +6,10 @@
  */
 
 #include "SkColorMatrixFilter.h"
+#include "SkColorSpaceXformer.h"
+#if SK_SUPPORT_GPU
+    #include "GrFragmentProcessor.h"
+#endif
 
 static SkScalar byte_to_scale(U8CPU byte) {
     if (0xFF == byte) {
@@ -16,21 +20,69 @@
     }
 }
 
+// If we can't reduce to a mode filter in MakeLightingFilter(), this is the general case.
+// We operate as a matrix color filter, but remember our input colors in case we're asked
+// to onMakeColorSpace() a new filter.
+class SkLightingColorFilter : public SkColorFilter {
+public:
+    SkLightingColorFilter(SkColor mul, SkColor add) : fMul(mul), fAdd(add) {
+        SkColorMatrix matrix;
+        matrix.setScale(byte_to_scale(SkColorGetR(mul)),
+                        byte_to_scale(SkColorGetG(mul)),
+                        byte_to_scale(SkColorGetB(mul)),
+                        1);
+        matrix.postTranslate(SkIntToScalar(SkColorGetR(add)),
+                             SkIntToScalar(SkColorGetG(add)),
+                             SkIntToScalar(SkColorGetB(add)),
+                             0);
+        fMatrixFilter = SkColorFilter::MakeMatrixFilterRowMajor255(matrix.fMat);
+    }
+
+    // Overriding this method is the class' raison d'etre.
+    sk_sp<SkColorFilter> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return sk_make_sp<SkLightingColorFilter>(xformer->apply(fMul), xformer->apply(fAdd));
+    }
+
+    // Let fMatrixFilter handle all the other calls directly.
+    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override {
+        fMatrixFilter->filterSpan(src, count, dst);
+    }
+    uint32_t getFlags() const override {
+        return fMatrixFilter->getFlags();
+    }
+    bool asColorMatrix(SkScalar matrix[20]) const override {
+        return fMatrixFilter->asColorMatrix(matrix);
+    }
+    void onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
+                        bool shaderIsOpaque) const override {
+        fMatrixFilter->appendStages(p, cs, alloc, shaderIsOpaque);
+    }
+
+    // TODO: might want to remember we're a lighting color filter through serialization?
+    void flatten(SkWriteBuffer& buf) const override { return fMatrixFilter->flatten(buf); }
+    Factory getFactory() const override             { return fMatrixFilter->getFactory(); }
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext* ctx,
+                                                   SkColorSpace* cs) const override {
+        return fMatrixFilter->asFragmentProcessor(ctx, cs);
+    }
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+    void toString(SkString* str) const override { fMatrixFilter->toString(str); }
+#endif
+
+private:
+    SkColor              fMul, fAdd;
+    sk_sp<SkColorFilter> fMatrixFilter;
+};
+
 sk_sp<SkColorFilter> SkColorMatrixFilter::MakeLightingFilter(SkColor mul, SkColor add) {
     const SkColor opaqueAlphaMask = SK_ColorBLACK;
     // omit the alpha and compare only the RGB values
     if (0 == (add & ~opaqueAlphaMask)) {
         return SkColorFilter::MakeModeFilter(mul | opaqueAlphaMask, SkBlendMode::kModulate);
     }
-
-    SkColorMatrix matrix;
-    matrix.setScale(byte_to_scale(SkColorGetR(mul)),
-                    byte_to_scale(SkColorGetG(mul)),
-                    byte_to_scale(SkColorGetB(mul)),
-                    1);
-    matrix.postTranslate(SkIntToScalar(SkColorGetR(add)),
-                         SkIntToScalar(SkColorGetG(add)),
-                         SkIntToScalar(SkColorGetB(add)),
-                         0);
-    return SkColorFilter::MakeMatrixFilterRowMajor255(matrix.fMat);
+    return sk_make_sp<SkLightingColorFilter>(mul, add);
 }
diff --git a/src/effects/SkComposeImageFilter.cpp b/src/effects/SkComposeImageFilter.cpp
index a5b9190..9397c83 100644
--- a/src/effects/SkComposeImageFilter.cpp
+++ b/src/effects/SkComposeImageFilter.cpp
@@ -61,6 +61,13 @@
     return outer;
 }
 
+sk_sp<SkImageFilter> SkComposeImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(2 == this->countInputs() && this->getInput(0) && this->getInput(1));
+
+    return SkComposeImageFilter::Make(this->getInput(0)->makeColorSpace(xformer),
+                                      this->getInput(1)->makeColorSpace(xformer));
+}
+
 SkIRect SkComposeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                              MapDirection direction) const {
     SkImageFilter* outer = this->getInput(0);
diff --git a/src/effects/SkDashImpl.h b/src/effects/SkDashImpl.h
new file mode 100644
index 0000000..cf74529
--- /dev/null
+++ b/src/effects/SkDashImpl.h
@@ -0,0 +1,48 @@
+/*
+ * 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 SkDashImpl_DEFINED
+#define SkDashImpl_DEFINED
+
+#include "SkPathEffect.h"
+
+class SK_API SkDashImpl : public SkPathEffect {
+public:
+    SkDashImpl(const SkScalar intervals[], int count, SkScalar phase);
+
+    bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override;
+
+    bool asPoints(PointData* results, const SkPath& src, const SkStrokeRec&, const SkMatrix&,
+                  const SkRect*) const override;
+
+    DashType asADash(DashInfo* info) const override;
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDashImpl)
+
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    bool exposedInAndroidJavaAPI() const override { return true; }
+#endif
+
+protected:
+    ~SkDashImpl() override;
+    void flatten(SkWriteBuffer&) const override;
+
+private:
+    SkScalar*   fIntervals;
+    int32_t     fCount;
+    SkScalar    fPhase;
+    // computed from phase
+
+    SkScalar    fInitialDashLength;
+    int32_t     fInitialDashIndex;
+    SkScalar    fIntervalLength;
+
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
index b1029e1..04f9e9e 100644
--- a/src/effects/SkDashPathEffect.cpp
+++ b/src/effects/SkDashPathEffect.cpp
@@ -6,13 +6,13 @@
  */
 
 #include "SkDashPathEffect.h"
-
+#include "SkDashImpl.h"
 #include "SkDashPathPriv.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkStrokeRec.h"
 
-SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase)
+SkDashImpl::SkDashImpl(const SkScalar intervals[], int count, SkScalar phase)
         : fPhase(0)
         , fInitialDashLength(-1)
         , fInitialDashIndex(0)
@@ -31,12 +31,12 @@
             &fInitialDashLength, &fInitialDashIndex, &fIntervalLength, &fPhase);
 }
 
-SkDashPathEffect::~SkDashPathEffect() {
+SkDashImpl::~SkDashImpl() {
     sk_free(fIntervals);
 }
 
-bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                              SkStrokeRec* rec, const SkRect* cullRect) const {
+bool SkDashImpl::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
+                            const SkRect* cullRect) const {
     return SkDashPath::InternalFilter(dst, src, rec, cullRect, fIntervals, fCount,
                                       fInitialDashLength, fInitialDashIndex, fIntervalLength);
 }
@@ -155,11 +155,8 @@
 // we need to:
 //      allow kRound_Cap capping (could allow rotations in the matrix with this)
 //      allow paths to be returned
-bool SkDashPathEffect::asPoints(PointData* results,
-                                const SkPath& src,
-                                const SkStrokeRec& rec,
-                                const SkMatrix& matrix,
-                                const SkRect* cullRect) const {
+bool SkDashImpl::asPoints(PointData* results, const SkPath& src, const SkStrokeRec& rec,
+                          const SkMatrix& matrix, const SkRect* cullRect) const {
     // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out
     if (0 >= rec.getWidth()) {
         return false;
@@ -351,7 +348,7 @@
     return true;
 }
 
-SkPathEffect::DashType SkDashPathEffect::asADash(DashInfo* info) const {
+SkPathEffect::DashType SkDashImpl::asADash(DashInfo* info) const {
     if (info) {
         if (info->fCount >= fCount && info->fIntervals) {
             memcpy(info->fIntervals, fIntervals, fCount * sizeof(SkScalar));
@@ -362,23 +359,23 @@
     return kDash_DashType;
 }
 
-void SkDashPathEffect::flatten(SkWriteBuffer& buffer) const {
+void SkDashImpl::flatten(SkWriteBuffer& buffer) const {
     buffer.writeScalar(fPhase);
     buffer.writeScalarArray(fIntervals, fCount);
 }
 
-sk_sp<SkFlattenable> SkDashPathEffect::CreateProc(SkReadBuffer& buffer) {
+sk_sp<SkFlattenable> SkDashImpl::CreateProc(SkReadBuffer& buffer) {
     const SkScalar phase = buffer.readScalar();
     uint32_t count = buffer.getArrayCount();
     SkAutoSTArray<32, SkScalar> intervals(count);
     if (buffer.readScalarArray(intervals.get(), count)) {
-        return Make(intervals.get(), SkToInt(count), phase);
+        return SkDashPathEffect::Make(intervals.get(), SkToInt(count), phase);
     }
     return nullptr;
 }
 
 #ifndef SK_IGNORE_TO_STRING
-void SkDashPathEffect::toString(SkString* str) const {
+void SkDashImpl::toString(SkString* str) const {
     str->appendf("SkDashPathEffect: (");
     str->appendf("count: %d phase %.2f intervals: (", fCount, fPhase);
     for (int i = 0; i < fCount; ++i) {
@@ -397,5 +394,5 @@
     if (!SkDashPath::ValidDashPath(phase, intervals, count)) {
         return nullptr;
     }
-    return sk_sp<SkPathEffect>(new SkDashPathEffect(intervals, count, phase));
+    return sk_sp<SkPathEffect>(new SkDashImpl(intervals, count, phase));
 }
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 9bd159d..cd481a1 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -388,7 +388,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels colorLock(colorBM), displLock(displBM);
     if (!colorBM.getPixels() || !displBM.getPixels()) {
         return nullptr;
     }
@@ -401,8 +400,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels dstLock(dst);
-
     computeDisplacement(fXChannelSelector, fYChannelSelector, scale, &dst,
                         displBM, colorOffset - displOffset, colorBM, colorBounds);
 
@@ -412,6 +409,23 @@
                                           dst);
 }
 
+sk_sp<SkImageFilter> SkDisplacementMapEffect::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(2 == this->countInputs());
+    if (!this->getInput(1)) {
+        return sk_ref_sp(const_cast<SkDisplacementMapEffect*>(this));
+    }
+
+    // Intentionally avoid xforming the displacement filter.  The values will be used as
+    // offsets, not as colors.
+    sk_sp<SkImageFilter> displacement = sk_ref_sp(const_cast<SkImageFilter*>(this->getInput(0)));
+    sk_sp<SkImageFilter> color =
+            this->getInput(1) ? this->getInput(1)->makeColorSpace(xformer) : nullptr;
+
+    return SkDisplacementMapEffect::Make(fXChannelSelector, fYChannelSelector, fScale,
+                                         std::move(displacement), std::move(color),
+                                         this->getCropRectIfSet());
+}
+
 SkRect SkDisplacementMapEffect::computeFastBounds(const SkRect& src) const {
     SkRect bounds = this->getColorInput() ? this->getColorInput()->computeFastBounds(src) : src;
     bounds.outset(SkScalarAbs(fScale) * SK_ScalarHalf, SkScalarAbs(fScale) * SK_ScalarHalf);
@@ -461,7 +475,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
@@ -496,10 +510,9 @@
         const SkISize& colorDimensions)
         : INHERITED(GrPixelConfigIsOpaque(color->config()) ? kPreservesOpaqueInput_OptimizationFlag
                                                            : kNone_OptimizationFlags)
-        , fDisplacementTransform(resourceProvider, offsetMatrix, displacement.get(),
-                                 GrSamplerParams::kNone_FilterMode)
+        , fDisplacementTransform(resourceProvider, offsetMatrix, displacement.get())
         , fDisplacementSampler(resourceProvider, displacement)
-        , fColorTransform(resourceProvider, color.get(), GrSamplerParams::kNone_FilterMode)
+        , fColorTransform(resourceProvider, color.get())
         , fDomain(color.get(), GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(colorDimensions)),
                   GrTextureDomain::kDecal_Mode)
         , fColorSampler(resourceProvider, color)
@@ -638,9 +651,10 @@
 }
 
 void GrGLDisplacementMapEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                          const GrProcessor& proc) {
+                                          const GrFragmentProcessor& proc) {
     const GrDisplacementMapEffect& displacementMap = proc.cast<GrDisplacementMapEffect>();
-    GrTexture* colorTex = displacementMap.textureSampler(1).texture();
+    GrTexture* colorTex = displacementMap.textureSampler(1).peekTexture();
+
     SkScalar scaleX = displacementMap.scale().fX / colorTex->width();
     SkScalar scaleY = displacementMap.scale().fY / colorTex->height();
     pdman.set2f(fScaleUni, SkScalarToFloat(scaleX),
diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp
index 5d47107..87e465f 100644
--- a/src/effects/SkDropShadowImageFilter.cpp
+++ b/src/effects/SkDropShadowImageFilter.cpp
@@ -9,6 +9,7 @@
 
 #include "SkBlurImageFilter.h"
 #include "SkCanvas.h"
+#include "SkColorSpaceXformer.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
@@ -112,6 +113,16 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkDropShadowImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(1 == this->countInputs());
+
+    sk_sp<SkImageFilter> input =
+            this->getInput(0) ? this->getInput(0)->makeColorSpace(xformer) : nullptr;
+
+    return SkDropShadowImageFilter::Make(fDx, fDy, fSigmaX, fSigmaY, xformer->apply(fColor),
+                                         fShadowMode, std::move(input), this->getCropRectIfSet());
+}
+
 SkRect SkDropShadowImageFilter::computeFastBounds(const SkRect& src) const {
     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
     SkRect shadowBounds = bounds;
diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp
deleted file mode 100644
index 7bbbb79..0000000
--- a/src/effects/SkGaussianEdgeShader.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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 "SkGaussianEdgeShader.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-
-class SkArenaAlloc;
-
- /** \class SkGaussianEdgeShaderImpl
- This subclass of shader applies a Gaussian to shadow edge
-
- If the primitive supports an implicit distance to the edge, the radius of the blur is specified
- by r & g values of the color in 14.2 fixed point. For spot shadows, we increase the stroke width
- to set the shadow against the shape. This pad is specified by b, also in 6.2 fixed point.
-
- When not using implicit distance, then b in the input color represents the input to the
- blur function.
- */
-class SkGaussianEdgeShaderImpl : public SkShader {
-public:
-    SkGaussianEdgeShaderImpl() {}
-
-    bool isOpaque() const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianEdgeShaderImpl)
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* storage) const override {
-        return nullptr;
-    }
-private:
-    friend class SkGaussianEdgeShader;
-
-    typedef SkShader INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "effects/GrBlurredEdgeFragmentProcessor.h"
-
-////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkGaussianEdgeShaderImpl::asFragmentProcessor(const AsFPArgs&) const {
-    return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
-}
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////
-
-bool SkGaussianEdgeShaderImpl::isOpaque() const {
-    return false;
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-#ifndef SK_IGNORE_TO_STRING
-void SkGaussianEdgeShaderImpl::toString(SkString* str) const {
-    str->appendf("GaussianEdgeShader: ()");
-}
-#endif
-
-sk_sp<SkFlattenable> SkGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& buf) {
-    return sk_make_sp<SkGaussianEdgeShaderImpl>();
-}
-
-void SkGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkShader> SkGaussianEdgeShader::Make() {
-    return sk_make_sp<SkGaussianEdgeShaderImpl>();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGaussianEdgeShader)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkGaussianEdgeShaderImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkHighContrastFilter.cpp b/src/effects/SkHighContrastFilter.cpp
index c3937cc..c6f1328 100644
--- a/src/effects/SkHighContrastFilter.cpp
+++ b/src/effects/SkHighContrastFilter.cpp
@@ -6,12 +6,13 @@
 */
 
 #include "SkHighContrastFilter.h"
-
+#include "SkPM4f.h"
 #include "SkArenaAlloc.h"
 #include "SkRasterPipeline.h"
 #include "SkReadBuffer.h"
 #include "SkString.h"
 #include "SkWriteBuffer.h"
+#include "../jumper/SkJumper.h"
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
@@ -45,33 +46,18 @@
     return p;
 }
 
-uint8_t SkScalarToUint8Clamp(SkScalar f) {
-    if (f <= 0) {
-        return 0;
-    } else if (f >= 1) {
-        return 255;
-    }
-    return static_cast<unsigned char>(255 * f);
-}
-
 SkScalar IncreaseContrast(SkScalar f, SkScalar contrast) {
     SkScalar m = (1 + contrast) / (1 - contrast);
     SkScalar b = (-0.5f * m + 0.5f);
     return m * f + b;
 }
 
-static SkPMColor ApplyHighContrastFilter(const SkHighContrastConfig& config,
-                                         SkPMColor pmColor) {
-    SkColor color = SkUnPreMultiply::PMColorToColor(pmColor);
-    SkScalar rf = SkColorGetR(color) / 255.f;
-    SkScalar gf = SkColorGetG(color) / 255.f;
-    SkScalar bf = SkColorGetB(color) / 255.f;
-
+SkColor4f ApplyHighContrastFilter(const SkHighContrastConfig& config, const SkColor4f& src) {
     // Apply a gamma of 2.0 so that the rest of the calculations
     // happen roughly in linear space.
-    rf *= rf;
-    gf *= gf;
-    bf *= bf;
+    float rf = src.fR * src.fR;
+    float gf = src.fG * src.fG;
+    float bf = src.fB * src.fB;
 
     // Convert to grayscale using luminance coefficients.
     if (config.fGrayscale) {
@@ -136,14 +122,7 @@
     }
 
     // Convert back from linear to a color space with a gamma of ~2.0.
-    rf = SkScalarSqrt(rf);
-    gf = SkScalarSqrt(gf);
-    bf = SkScalarSqrt(bf);
-
-    return SkPremultiplyARGBInline(SkColorGetA(color),
-                                   SkScalarToUint8Clamp(rf),
-                                   SkScalarToUint8Clamp(gf),
-                                   SkScalarToUint8Clamp(bf));
+    return SkColor4f::Pin(SkScalarSqrt(rf), SkScalarSqrt(gf), SkScalarSqrt(bf), src.fA);
 }
 
 }  // namespace
@@ -164,9 +143,8 @@
     sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override;
  #endif
 
-    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const
-          override;
-    bool onAppendStages(SkRasterPipeline* p,
+    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
+    void onAppendStages(SkRasterPipeline* p,
                         SkColorSpace* dst,
                         SkArenaAlloc* scratch,
                         bool shaderIsOpaque) const override;
@@ -186,25 +164,45 @@
     typedef SkColorFilter INHERITED;
 };
 
-void SkHighContrast_Filter::filterSpan(const SkPMColor src[], int count,
-                                       SkPMColor dst[]) const {
-    for (int i = 0; i < count; ++i)
-        dst[i] = ApplyHighContrastFilter(fConfig, src[i]);
+void SkHighContrast_Filter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
+    const float oneOver255 = 1.0f / 255;
+    for (int i = 0; i < count; ++i) {
+        SkColor color = SkUnPreMultiply::PMColorToColor(src[i]);
+        // be sure to NOT treat color as sRGB, as we are in legacy mode here
+        SkColor4f s4 {
+            SkColorGetR(color) * oneOver255, SkColorGetG(color) * oneOver255,
+            SkColorGetB(color) * oneOver255, SkColorGetA(color) * oneOver255,
+        };
+        SkColor4f d4 = ApplyHighContrastFilter(fConfig, s4);
+        dst[i] = d4.premul().toPMColor();
+    }
 }
 
-bool SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p,
-                                           SkColorSpace* dst,
-                                           SkArenaAlloc* scratch,
+void SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p,
+                                           SkColorSpace* dstCS,
+                                           SkArenaAlloc* alloc,
                                            bool shaderIsOpaque) const {
     if (!shaderIsOpaque) {
         p->append(SkRasterPipeline::unpremul);
     }
 
+    if (!dstCS) {
+        // In legacy draws this effect approximately linearizes by squaring.
+        // When non-legacy, we're already (better) linearized.
+        auto square = alloc->make<SkJumper_ParametricTransferFunction>();
+        square->G = 2.0f; square->A = 1.0f;
+        square->B = square->C = square->D = square->E = square->F = 0;
+
+        p->append(SkRasterPipeline::parametric_r, square);
+        p->append(SkRasterPipeline::parametric_g, square);
+        p->append(SkRasterPipeline::parametric_b, square);
+    }
+
     if (fConfig.fGrayscale) {
         float r = SK_LUM_COEFF_R;
         float g = SK_LUM_COEFF_G;
         float b = SK_LUM_COEFF_B;
-        float* matrix = scratch->makeArray<float>(12);
+        float* matrix = alloc->makeArray<float>(12);
         matrix[0] = matrix[1] = matrix[2] = r;
         matrix[3] = matrix[4] = matrix[5] = g;
         matrix[6] = matrix[7] = matrix[8] = b;
@@ -212,13 +210,13 @@
     }
 
     if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) {
-        float* matrix = scratch->makeArray<float>(12);
+        float* matrix = alloc->makeArray<float>(12);
         matrix[0] = matrix[4] = matrix[8] = -1;
         matrix[9] = matrix[10] = matrix[11] = 1;
         p->append(SkRasterPipeline::matrix_3x4, matrix);
     } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) {
         p->append(SkRasterPipeline::rgb_to_hsl);
-        float* matrix = scratch->makeArray<float>(12);
+        float* matrix = alloc->makeArray<float>(12);
         matrix[0] = matrix[4] = matrix[11] = 1;
         matrix[8] = -1;
         p->append(SkRasterPipeline::matrix_3x4, matrix);
@@ -226,7 +224,7 @@
     }
 
     if (fConfig.fContrast != 0.0) {
-        float* matrix = scratch->makeArray<float>(12);
+        float* matrix = alloc->makeArray<float>(12);
         float c = fConfig.fContrast;
         float m = (1 + c) / (1 - c);
         float b = (-0.5f * m + 0.5f);
@@ -238,11 +236,20 @@
     p->append(SkRasterPipeline::clamp_0);
     p->append(SkRasterPipeline::clamp_1);
 
+    if (!dstCS) {
+        // See the previous if(!dstCS) { ... }
+        auto sqrt = alloc->make<SkJumper_ParametricTransferFunction>();
+        sqrt->G = 0.5f; sqrt->A = 1.0f;
+        sqrt->B = sqrt->C = sqrt->D = sqrt->E = sqrt->F = 0;
+
+        p->append(SkRasterPipeline::parametric_r, sqrt);
+        p->append(SkRasterPipeline::parametric_g, sqrt);
+        p->append(SkRasterPipeline::parametric_b, sqrt);
+    }
+
     if (!shaderIsOpaque) {
         p->append(SkRasterPipeline::premul);
     }
-
-    return true;
 }
 
 void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const {
@@ -318,7 +325,7 @@
     GLHighContrastFilterEffect(const SkHighContrastConfig& config);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
     void emitCode(EmitArgs& args) override;
 
 private:
@@ -337,7 +344,8 @@
     GLHighContrastFilterEffect::GenKey(*this, caps, b);
 }
 
-void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) {
+void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm,
+                                           const GrFragmentProcessor& proc) {
     const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
     pdm.set1f(fContrastUni, hcfe.config().fContrast);
 }
diff --git a/src/effects/SkImageSource.cpp b/src/effects/SkImageSource.cpp
index 96f5bf4..e051160 100644
--- a/src/effects/SkImageSource.cpp
+++ b/src/effects/SkImageSource.cpp
@@ -8,6 +8,7 @@
 #include "SkImageSource.h"
 
 #include "SkCanvas.h"
+#include "SkColorSpaceXformer.h"
 #include "SkImage.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
@@ -131,6 +132,12 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkImageSource::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(0 == this->countInputs());
+
+    return SkImageSource::Make(xformer->apply(fImage.get()), fSrcRect, fDstRect, fFilterQuality);
+}
+
 SkRect SkImageSource::computeFastBounds(const SkRect& src) const {
     return fDstRect;
 }
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 54b8cb1..c856841 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -8,6 +8,7 @@
 #include "SkLightingImageFilter.h"
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
+#include "SkColorSpaceXformer.h"
 #include "SkPoint3.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
@@ -38,28 +39,26 @@
 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
 #endif
 
-namespace {
-
 const SkScalar gOneThird = SkIntToScalar(1) / 3;
 const SkScalar gTwoThirds = SkIntToScalar(2) / 3;
 const SkScalar gOneHalf = 0.5f;
 const SkScalar gOneQuarter = 0.25f;
 
 #if SK_SUPPORT_GPU
-void setUniformPoint3(const GrGLSLProgramDataManager& pdman, UniformHandle uni,
-                      const SkPoint3& point) {
+static void setUniformPoint3(const GrGLSLProgramDataManager& pdman, UniformHandle uni,
+                             const SkPoint3& point) {
     GR_STATIC_ASSERT(sizeof(SkPoint3) == 3 * sizeof(float));
     pdman.set3fv(uni, 1, &point.fX);
 }
 
-void setUniformNormal3(const GrGLSLProgramDataManager& pdman, UniformHandle uni,
-                       const SkPoint3& point) {
+static void setUniformNormal3(const GrGLSLProgramDataManager& pdman, UniformHandle uni,
+                              const SkPoint3& point) {
     setUniformPoint3(pdman, uni, point);
 }
 #endif
 
 // Shift matrix components to the left, as we advance pixels to the right.
-inline void shiftMatrixLeft(int m[9]) {
+static inline void shiftMatrixLeft(int m[9]) {
     m[0] = m[1];
     m[3] = m[4];
     m[6] = m[7];
@@ -121,66 +120,66 @@
     SkScalar fShininess;
 };
 
-inline SkScalar sobel(int a, int b, int c, int d, int e, int f, SkScalar scale) {
+static inline SkScalar sobel(int a, int b, int c, int d, int e, int f, SkScalar scale) {
     return (-a + b - 2 * c + 2 * d -e + f) * scale;
 }
 
-inline SkPoint3 pointToNormal(SkScalar x, SkScalar y, SkScalar surfaceScale) {
+static inline SkPoint3 pointToNormal(SkScalar x, SkScalar y, SkScalar surfaceScale) {
     SkPoint3 vector = SkPoint3::Make(-x * surfaceScale, -y * surfaceScale, 1);
     fast_normalize(&vector);
     return vector;
 }
 
-inline SkPoint3 topLeftNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 topLeftNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(0, 0, m[4], m[5], m[7], m[8], gTwoThirds),
                          sobel(0, 0, m[4], m[7], m[5], m[8], gTwoThirds),
                          surfaceScale);
 }
 
-inline SkPoint3 topNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 topNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(   0,    0, m[3], m[5], m[6], m[8], gOneThird),
                          sobel(m[3], m[6], m[4], m[7], m[5], m[8], gOneHalf),
                          surfaceScale);
 }
 
-inline SkPoint3 topRightNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 topRightNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(   0,    0, m[3], m[4], m[6], m[7], gTwoThirds),
                          sobel(m[3], m[6], m[4], m[7],    0,    0, gTwoThirds),
                          surfaceScale);
 }
 
-inline SkPoint3 leftNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 leftNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(m[1], m[2], m[4], m[5], m[7], m[8], gOneHalf),
                          sobel(   0,    0, m[1], m[7], m[2], m[8], gOneThird),
                          surfaceScale);
 }
 
 
-inline SkPoint3 interiorNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 interiorNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(m[0], m[2], m[3], m[5], m[6], m[8], gOneQuarter),
                          sobel(m[0], m[6], m[1], m[7], m[2], m[8], gOneQuarter),
                          surfaceScale);
 }
 
-inline SkPoint3 rightNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 rightNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(m[0], m[1], m[3], m[4], m[6], m[7], gOneHalf),
                          sobel(m[0], m[6], m[1], m[7],    0,    0, gOneThird),
                          surfaceScale);
 }
 
-inline SkPoint3 bottomLeftNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 bottomLeftNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(m[1], m[2], m[4], m[5],    0,    0, gTwoThirds),
                          sobel(   0,    0, m[1], m[4], m[2], m[5], gTwoThirds),
                          surfaceScale);
 }
 
-inline SkPoint3 bottomNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 bottomNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(m[0], m[2], m[3], m[5],    0,    0, gOneThird),
                          sobel(m[0], m[3], m[1], m[4], m[2], m[5], gOneHalf),
                          surfaceScale);
 }
 
-inline SkPoint3 bottomRightNormal(int m[9], SkScalar surfaceScale) {
+static inline SkPoint3 bottomRightNormal(int m[9], SkScalar surfaceScale) {
     return pointToNormal(sobel(m[0], m[1], m[3], m[4], 0,  0, gTwoThirds),
                          sobel(m[0], m[3], m[1], m[4], 0,  0, gTwoThirds),
                          surfaceScale);
@@ -207,7 +206,7 @@
 };
 
 template <class LightingType, class LightType, class PixelFetcher>
-void lightBitmap(const LightingType& lightingType,
+static void lightBitmap(const LightingType& lightingType,
                  const SkImageFilterLight* light,
                  const SkBitmap& src,
                  SkBitmap* dst,
@@ -299,7 +298,7 @@
 }
 
 template <class LightingType, class LightType>
-void lightBitmap(const LightingType& lightingType,
+static void lightBitmap(const LightingType& lightingType,
                  const SkImageFilterLight* light,
                  const SkBitmap& src,
                  SkBitmap* dst,
@@ -314,7 +313,7 @@
     }
 }
 
-SkPoint3 readPoint3(SkReadBuffer& buffer) {
+static SkPoint3 readPoint3(SkReadBuffer& buffer) {
     SkPoint3 point;
     point.fX = buffer.readScalar();
     point.fY = buffer.readScalar();
@@ -325,7 +324,7 @@
     return point;
 };
 
-void writePoint3(const SkPoint3& point, SkWriteBuffer& buffer) {
+static void writePoint3(const SkPoint3& point, SkWriteBuffer& buffer) {
     buffer.writeScalar(point.fX);
     buffer.writeScalar(point.fY);
     buffer.writeScalar(point.fZ);
@@ -490,6 +489,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 #if SK_SUPPORT_GPU
     sk_sp<GrFragmentProcessor> makeFragmentProcessor(GrResourceProvider*, sk_sp<GrTextureProxy>,
@@ -526,6 +526,7 @@
 
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 #if SK_SUPPORT_GPU
     sk_sp<GrFragmentProcessor> makeFragmentProcessor(GrResourceProvider*, sk_sp<GrTextureProxy>,
@@ -752,8 +753,6 @@
 
 #endif
 
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkImageFilterLight : public SkRefCnt {
@@ -771,6 +770,8 @@
     }
     virtual SkImageFilterLight* transform(const SkMatrix& matrix) const = 0;
 
+    virtual sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer*) const = 0;
+
     // Defined below SkLight's subclasses.
     void flattenLight(SkWriteBuffer& buffer) const;
     static SkImageFilterLight* UnflattenLight(SkReadBuffer& buffer);
@@ -797,6 +798,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static SkColor xform_color(const SkPoint3& color, SkColorSpaceXformer* xformer) {
+    SkColor origColor = SkColorSetARGBInline(0xFF,
+                                             SkScalarRoundToInt(color.fX),
+                                             SkScalarRoundToInt(color.fY),
+                                             SkScalarRoundToInt(color.fZ));
+    return xformer->apply(origColor);
+}
+
 class SkDistantLight : public SkImageFilterLight {
 public:
     SkDistantLight(const SkPoint3& direction, SkColor color)
@@ -818,6 +827,10 @@
 #endif
     }
 
+    sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return sk_make_sp<SkDistantLight>(fDirection, xform_color(this->color(), xformer));
+    }
+
     bool isEqual(const SkImageFilterLight& other) const override {
         if (other.type() != kDistant_LightType) {
             return false;
@@ -874,6 +887,11 @@
         return nullptr;
 #endif
     }
+
+    sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return sk_make_sp<SkPointLight>(fLocation, xform_color(this->color(), xformer));
+    }
+
     bool isEqual(const SkImageFilterLight& other) const override {
         if (other.type() != kPoint_LightType) {
             return false;
@@ -923,7 +941,8 @@
      : INHERITED(color),
        fLocation(location),
        fTarget(target),
-       fSpecularExponent(SkScalarPin(specularExponent, kSpecularExponentMin, kSpecularExponentMax))
+       fSpecularExponent(SkScalarPin(specularExponent, kSpecularExponentMin, kSpecularExponentMax)),
+       fCutoffAngle(cutoffAngle)
     {
        fS = target - location;
        fast_normalize(&fS);
@@ -933,6 +952,11 @@
        fConeScale = SkScalarInvert(antiAliasThreshold);
     }
 
+    sk_sp<SkImageFilterLight> makeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return sk_make_sp<SkSpotLight>(fLocation, fTarget, fSpecularExponent, fCutoffAngle,
+                                       xform_color(this->color(), xformer));
+    }
+
     SkImageFilterLight* transform(const SkMatrix& matrix) const override {
         SkPoint location2 = SkPoint::Make(fLocation.fX, fLocation.fY);
         matrix.mapPoints(&location2, 1);
@@ -1056,6 +1080,7 @@
     SkPoint3 fLocation;
     SkPoint3 fTarget;
     SkScalar fSpecularExponent;
+    SkScalar fCutoffAngle;
     SkScalar fCosOuterConeAngle;
     SkScalar fCosInnerConeAngle;
     SkScalar fConeScale;
@@ -1278,7 +1303,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels alp(inputBM);
     if (!inputBM.getPixels()) {
         return nullptr;
     }
@@ -1290,8 +1314,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels dstLock(dst);
-
     SkMatrix matrix(ctx.ctm());
     matrix.postTranslate(SkIntToScalar(-inputOffset.x()), SkIntToScalar(-inputOffset.y()));
 
@@ -1329,6 +1351,16 @@
                                           dst);
 }
 
+sk_sp<SkImageFilter> SkDiffuseLightingImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs());
+    sk_sp<SkImageFilter> input =
+            this->getInput(0) ? this->getInput(0)->onMakeColorSpace(xformer) : nullptr;
+    return SkDiffuseLightingImageFilter::Make(this->light()->makeColorSpace(xformer),
+                                              255.0f * this->surfaceScale(), fKD, std::move(input),
+                                              this->getCropRectIfSet());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkDiffuseLightingImageFilter::toString(SkString* str) const {
     str->appendf("SkDiffuseLightingImageFilter: (");
@@ -1445,7 +1477,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels alp(inputBM);
     if (!inputBM.getPixels()) {
         return nullptr;
     }
@@ -1457,8 +1488,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels dstLock(dst);
-
     SpecularLightingType lightingType(fKS, fShininess);
 
     SkMatrix matrix(ctx.ctm());
@@ -1496,6 +1525,17 @@
     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst);
 }
 
+sk_sp<SkImageFilter> SkSpecularLightingImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs());
+
+    sk_sp<SkImageFilter> input =
+            this->getInput(0) ? this->getInput(0)->onMakeColorSpace(xformer) : nullptr;
+    return SkSpecularLightingImageFilter::Make(this->light()->makeColorSpace(xformer),
+                                               255.0f * this->surfaceScale(), fKS, fShininess,
+                                               std::move(input), this->getCropRectIfSet());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkSpecularLightingImageFilter::toString(SkString* str) const {
     str->appendf("SkSpecularLightingImageFilter: (");
@@ -1522,14 +1562,13 @@
 
 #if SK_SUPPORT_GPU
 
-namespace {
-SkPoint3 random_point3(SkRandom* random) {
+static SkPoint3 random_point3(SkRandom* random) {
     return SkPoint3::Make(SkScalarToFloat(random->nextSScalar1()),
                           SkScalarToFloat(random->nextSScalar1()),
                           SkScalarToFloat(random->nextSScalar1()));
 }
 
-SkImageFilterLight* create_random_light(SkRandom* random) {
+static SkImageFilterLight* create_random_light(SkRandom* random) {
     int type = random->nextULessThan(3);
     switch (type) {
         case 0: {
@@ -1548,9 +1587,9 @@
     }
 }
 
-SkString emitNormalFunc(BoundaryMode mode,
-                        const char* pointToNormalName,
-                        const char* sobelFuncName) {
+static SkString emitNormalFunc(BoundaryMode mode,
+                               const char* pointToNormalName,
+                               const char* sobelFuncName) {
     SkString result;
     switch (mode) {
     case kTopLeft_BoundaryMode:
@@ -1623,8 +1662,6 @@
     return result;
 }
 
-}
-
 class GrGLLightingEffect : public GrGLSLFragmentProcessor {
 public:
     GrGLLightingEffect() : fLight(nullptr) { }
@@ -1638,7 +1675,7 @@
     /**
      * Subclasses of GrGLLightingEffect must call INHERITED::onSetData();
      */
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
     virtual void emitLightFunc(GrGLSLUniformHandler*,
                                GrGLSLFPFragmentBuilder*,
@@ -1660,7 +1697,7 @@
     void emitLightFunc(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, SkString* funcName) override;
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     typedef GrGLLightingEffect INHERITED;
@@ -1675,7 +1712,7 @@
     void emitLightFunc(GrGLSLUniformHandler*, GrGLSLFPFragmentBuilder*, SkString* funcName) override;
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     typedef GrGLLightingEffect INHERITED;
@@ -1878,9 +1915,7 @@
                              args.fOutputColor, lightFunc.c_str(), normalName.c_str(), surfScale);
     fLight->emitLightColor(uniformHandler, fragBuilder, "surfaceToLight");
     fragBuilder->codeAppend(");\n");
-    SkString modulate;
-    GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
-    fragBuilder->codeAppend(modulate.c_str());
+    fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLLightingEffect::GenKey(const GrProcessor& proc,
@@ -1891,13 +1926,14 @@
 }
 
 void GrGLLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                   const GrProcessor& proc) {
+                                   const GrFragmentProcessor& proc) {
     const GrLightingEffect& lighting = proc.cast<GrLightingEffect>();
     if (!fLight) {
         fLight = lighting.light()->createGLLight();
     }
 
-    GrTexture* texture = lighting.textureSampler(0).texture();
+    GrTexture* texture = lighting.textureSampler(0).peekTexture();
+
     float ySign = texture->origin() == kTopLeft_GrSurfaceOrigin ? -1.0f : 1.0f;
     pdman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
     pdman.set1f(fSurfaceScaleUni, lighting.surfaceScale());
@@ -1936,7 +1972,7 @@
 }
 
 void GrGLDiffuseLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                          const GrProcessor& proc) {
+                                          const GrFragmentProcessor& proc) {
     INHERITED::onSetData(pdman, proc);
     const GrDiffuseLightingEffect& diffuse = proc.cast<GrDiffuseLightingEffect>();
     pdman.set1f(fKDUni, diffuse.kd());
@@ -2038,7 +2074,7 @@
 }
 
 void GrGLSpecularLightingEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                           const GrProcessor& effect) {
+                                           const GrFragmentProcessor& effect) {
     INHERITED::onSetData(pdman, effect);
     const GrSpecularLightingEffect& spec = effect.cast<GrSpecularLightingEffect>();
     pdman.set1f(fKSUni, spec.ks());
diff --git a/src/effects/SkLumaColorFilter.cpp b/src/effects/SkLumaColorFilter.cpp
index 2809c8b..efe44a4 100644
--- a/src/effects/SkLumaColorFilter.cpp
+++ b/src/effects/SkLumaColorFilter.cpp
@@ -6,7 +6,7 @@
  */
 
 #include "SkLumaColorFilter.h"
-
+#include "SkPM4f.h"
 #include "SkColorPriv.h"
 #include "SkRasterPipeline.h"
 #include "SkString.h"
@@ -17,8 +17,7 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #endif
 
-void SkLumaColorFilter::filterSpan(const SkPMColor src[], int count,
-                                   SkPMColor dst[]) const {
+void SkLumaColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
     for (int i = 0; i < count; ++i) {
         SkPMColor c = src[i];
 
@@ -37,12 +36,11 @@
     }
 }
 
-bool SkLumaColorFilter::onAppendStages(SkRasterPipeline* p,
+void SkLumaColorFilter::onAppendStages(SkRasterPipeline* p,
                                        SkColorSpace* dst,
                                        SkArenaAlloc* scratch,
                                        bool shaderIsOpaque) const {
     p->append(SkRasterPipeline::luminance_to_alpha);
-    return true;
 }
 
 sk_sp<SkColorFilter> SkLumaColorFilter::Make() {
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index aaee498..197b805 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -136,7 +136,7 @@
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     UniformHandle       fOffsetVar;
@@ -196,18 +196,15 @@
                                      &fColorSpaceHelper);
     fragBuilder->codeAppend(";\n");
 
-    fragBuilder->codeAppendf("\t\t%s = output_color;", args.fOutputColor);
-    SkString modulate;
-    GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
-    fragBuilder->codeAppend(modulate.c_str());
+    fragBuilder->codeAppendf("\t\t%s = output_color;\n", args.fOutputColor);
+    fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLMagnifierEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                    const GrProcessor& effect) {
+                                    const GrFragmentProcessor& effect) {
     const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>();
 
-    GrTexture* tex = zoom.textureSampler(0).texture();
-    SkASSERT(tex);
+    GrTexture* tex = zoom.textureSampler(0).peekTexture();
 
     SkScalar invW = 1.0f / tex->width();
     SkScalar invH = 1.0f / tex->height();
@@ -389,7 +386,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels alp(inputBM);
     SkASSERT(inputBM.getPixels());
     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
         return nullptr;
@@ -402,8 +398,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels dstLock(dst);
-
     SkColor* dptr = dst.getAddr32(0, 0);
     int dstWidth = dst.width(), dstHeight = dst.height();
     for (int y = 0; y < dstHeight; ++y) {
@@ -447,6 +441,17 @@
                                           dst);
 }
 
+sk_sp<SkImageFilter> SkMagnifierImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkMagnifierImageFilter*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkMagnifierImageFilter::Make(fSrcRect, fInset, std::move(input),
+                                        this->getCropRectIfSet());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkMagnifierImageFilter::toString(SkString* str) const {
     str->appendf("SkMagnifierImageFilter: (");
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index 5c5ddc6..6644ef8 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -244,9 +244,7 @@
 // FIXME:  This should be refactored to SkImageFilterUtils for
 // use by other filters.  For now, we assume the input is always
 // premultiplied and unpremultiply it
-static SkBitmap unpremultiply_bitmap(const SkBitmap& src)
-{
-    SkAutoLockPixels alp(src);
+static SkBitmap unpremultiply_bitmap(const SkBitmap& src) {
     if (!src.getPixels()) {
         return SkBitmap();
     }
@@ -256,7 +254,6 @@
     if (!result.tryAllocPixels(info)) {
         return SkBitmap();
     }
-    SkAutoLockPixels resultLock(result);
     for (int y = 0; y < src.height(); ++y) {
         const uint32_t* srcRow = src.getAddr32(0, y);
         uint32_t* dstRow = result.getAddr32(0, y);
@@ -350,7 +347,6 @@
         inputBM = unpremultiply_bitmap(inputBM);
     }
 
-    SkAutoLockPixels alp(inputBM);
     if (!inputBM.getPixels()) {
         return nullptr;
     }
@@ -363,8 +359,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels dstLock(dst);
-
     offset->fX = bounds.fLeft;
     offset->fY = bounds.fTop;
     bounds.offset(-inputOffset);
@@ -388,6 +382,19 @@
                                           dst);
 }
 
+sk_sp<SkImageFilter> SkMatrixConvolutionImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkMatrixConvolutionImageFilter*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkMatrixConvolutionImageFilter::Make(fKernelSize, fKernel, fGain, fBias, fKernelOffset,
+                                                fTileMode, fConvolveAlpha, std::move(input),
+                                                this->getCropRectIfSet());
+}
+
 SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
                                                            MapDirection direction) const {
     SkIRect dst = src;
diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp
index c2c18ac..e7c3f51 100644
--- a/src/effects/SkMergeImageFilter.cpp
+++ b/src/effects/SkMergeImageFilter.cpp
@@ -143,6 +143,18 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkMergeImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<5, sk_sp<SkImageFilter>> inputs(this->countInputs());
+    SkSTArray<5, SkBlendMode> modes(this->countInputs());
+    for (int i = 0; i < this->countInputs(); i++) {
+        inputs.push_back(this->getInput(i) ? this->getInput(i)->makeColorSpace(xformer) : nullptr);
+        modes.push_back(fModes ? (SkBlendMode) fModes[i] : SkBlendMode::kSrcOver);
+    }
+
+    return SkMergeImageFilter::MakeN(inputs.begin(), this->countInputs(), modes.begin(),
+                                     this->getCropRectIfSet());
+}
+
 sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
     Common common;
     if (!common.unflatten(buffer, -1)) {
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 7e7c125..57abec9 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -199,7 +199,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fPixelSizeUni;
@@ -276,9 +276,7 @@
         fragBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir);
     }
     fragBuilder->codeAppend("\t\t}\n");
-    SkString modulate;
-    GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
-    fragBuilder->codeAppend(modulate.c_str());
+    fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
@@ -294,9 +292,9 @@
 }
 
 void GrGLMorphologyEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                     const GrProcessor& proc) {
+                                     const GrFragmentProcessor& proc) {
     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
-    GrTexture& texture = *m.textureSampler(0).texture();
+    GrTexture& texture = *m.textureSampler(0).peekTexture();
 
     float pixelSize = 0.0f;
     switch (m.direction()) {
@@ -621,8 +619,6 @@
         return nullptr;
     }
 
-    SkAutoLockPixels inputLock(inputBM), dstLock(dst);
-
     SkMorphologyImageFilter::Proc procX, procY;
 
     if (kDilate_Op == this->op()) {
@@ -639,8 +635,6 @@
             return nullptr;
         }
 
-        SkAutoLockPixels tmpLock(tmp);
-
         call_proc_X(procX, inputBM, &tmp, width, srcBounds);
         SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
         call_proc_Y(procY,
@@ -660,3 +654,17 @@
     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
                                           dst, &source->props());
 }
+
+sk_sp<SkImageFilter> SkMorphologyImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkMorphologyImageFilter*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return (SkMorphologyImageFilter::kDilate_Op == this->op())
+            ? SkDilateImageFilter::Make(fRadius.width(), fRadius.height(), std::move(input),
+                                        this->getCropRectIfSet())
+            : SkErodeImageFilter::Make(fRadius.width(), fRadius.height(), std::move(input),
+                                       this->getCropRectIfSet());
+}
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
index 2e8b0d9..973b2a6 100644
--- a/src/effects/SkOffsetImageFilter.cpp
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -73,6 +73,17 @@
     }
 }
 
+sk_sp<SkImageFilter> SkOffsetImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkOffsetImageFilter*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkOffsetImageFilter::Make(fOffset.fX, fOffset.fY, std::move(input),
+                                     this->getCropRectIfSet());
+}
+
 SkRect SkOffsetImageFilter::computeFastBounds(const SkRect& src) const {
     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
     bounds.offset(fOffset.fX, fOffset.fY);
diff --git a/src/effects/SkOverdrawColorFilter.cpp b/src/effects/SkOverdrawColorFilter.cpp
index 7c5212d..a4e13f9 100644
--- a/src/effects/SkOverdrawColorFilter.cpp
+++ b/src/effects/SkOverdrawColorFilter.cpp
@@ -5,7 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "SkArenaAlloc.h"
 #include "SkOverdrawColorFilter.h"
+#include "SkPM4f.h"
+#include "SkRasterPipeline.h"
+#include "SkReadBuffer.h"
+#include "../jumper/SkJumper.h"
 
 void SkOverdrawColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
     for (int x = 0; x < count; x++) {
@@ -18,6 +23,30 @@
     }
 }
 
+void SkOverdrawColorFilter::onAppendStages(SkRasterPipeline* p,
+                                           SkColorSpace* dstCS,
+                                           SkArenaAlloc* alloc,
+                                           bool shader_is_opaque) const {
+    struct Ctx : public SkJumper_CallbackCtx {
+        const SkPMColor* colors;
+    };
+    // TODO: do we care about transforming to dstCS?
+    auto ctx = alloc->make<Ctx>();
+    ctx->colors = fColors;
+    ctx->fn = [](SkJumper_CallbackCtx* arg, int active_pixels) {
+        auto ctx = (Ctx*)arg;
+        auto pixels = (SkPM4f*)ctx->rgba;
+        for (int i = 0; i < active_pixels; i++) {
+            uint8_t alpha = (int)(pixels[i].a() * 255);
+            if (alpha >= kNumColors) {
+                alpha = kNumColors - 1;
+            }
+            pixels[i] = SkPM4f::FromPMColor(ctx->colors[alpha]);
+        }
+    };
+    p->append(SkRasterPipeline::callback, ctx);
+}
+
 void SkOverdrawColorFilter::toString(SkString* str) const {
     str->append("SkOverdrawColorFilter (");
     for (int i = 0; i < kNumColors; i++) {
@@ -77,7 +106,7 @@
     void emitCode(EmitArgs&) override;
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override {}
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override {}
 
 private:
     GrColor4f fColors[SkOverdrawColorFilter::kNumColors];
diff --git a/src/effects/SkOverdrawColorFilter.h b/src/effects/SkOverdrawColorFilter.h
index 6c5ce59..41d7988 100644
--- a/src/effects/SkOverdrawColorFilter.h
+++ b/src/effects/SkOverdrawColorFilter.h
@@ -6,7 +6,7 @@
  */
 
 #include "SkColorFilter.h"
-#include "../../src/core/SkReadBuffer.h"
+#include "SkFlattenable.h"
 
 #ifndef SkOverdrawColorFilter_DEFINED
 #define SkOverdrawColorFilter_DEFINED
@@ -46,6 +46,8 @@
         memcpy(fColors, colors, kNumColors * sizeof(SkPMColor));
     }
 
+    void onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, bool) const override;
+
     SkPMColor fColors[kNumColors];
 
     typedef SkColorFilter INHERITED;
diff --git a/src/effects/SkPaintImageFilter.cpp b/src/effects/SkPaintImageFilter.cpp
index 0a0e4e9..c793858 100644
--- a/src/effects/SkPaintImageFilter.cpp
+++ b/src/effects/SkPaintImageFilter.cpp
@@ -7,6 +7,7 @@
 
 #include "SkPaintImageFilter.h"
 #include "SkCanvas.h"
+#include "SkColorSpaceXformer.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
@@ -68,6 +69,10 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkPaintImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return SkPaintImageFilter::Make(xformer->apply(fPaint), this->getCropRectIfSet());
+}
+
 bool SkPaintImageFilter::affectsTransparentBlack() const {
     return true;
 }
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
deleted file mode 100644
index 1e02ced..0000000
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkPerlinNoiseShader.h"
-
-#include "SkArenaAlloc.h"
-#include "SkColorFilter.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkShader.h"
-#include "SkUnPreMultiply.h"
-#include "SkString.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrCoordTransform.h"
-#include "SkGr.h"
-#include "effects/GrConstColorProcessor.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#endif
-
-static const int kBlockSize = 256;
-static const int kBlockMask = kBlockSize - 1;
-static const int kPerlinNoise = 4096;
-static const int kRandMaximum = SK_MaxS32; // 2**31 - 1
-
-namespace {
-
-// noiseValue is the color component's value (or color)
-// limitValue is the maximum perlin noise array index value allowed
-// newValue is the current noise dimension (either width or height)
-inline int checkNoise(int noiseValue, int limitValue, int newValue) {
-    // If the noise value would bring us out of bounds of the current noise array while we are
-    // stiching noise tiles together, wrap the noise around the current dimension of the noise to
-    // stay within the array bounds in a continuous fashion (so that tiling lines are not visible)
-    if (noiseValue >= limitValue) {
-        noiseValue -= newValue;
-    }
-    return noiseValue;
-}
-
-inline SkScalar smoothCurve(SkScalar t) {
-    return t * t * (3 - 2 * t);
-}
-
-} // end namespace
-
-struct SkPerlinNoiseShader::StitchData {
-    StitchData()
-      : fWidth(0)
-      , fWrapX(0)
-      , fHeight(0)
-      , fWrapY(0)
-    {}
-
-    bool operator==(const StitchData& other) const {
-        return fWidth == other.fWidth &&
-               fWrapX == other.fWrapX &&
-               fHeight == other.fHeight &&
-               fWrapY == other.fWrapY;
-    }
-
-    int fWidth; // How much to subtract to wrap for stitching.
-    int fWrapX; // Minimum value to wrap.
-    int fHeight;
-    int fWrapY;
-};
-
-struct SkPerlinNoiseShader::PaintingData {
-    PaintingData(const SkISize& tileSize, SkScalar seed,
-                 SkScalar baseFrequencyX, SkScalar baseFrequencyY,
-                 const SkMatrix& matrix)
-    {
-        SkVector vec[2] = {
-            { SkScalarInvert(baseFrequencyX),   SkScalarInvert(baseFrequencyY)  },
-            { SkIntToScalar(tileSize.fWidth),   SkIntToScalar(tileSize.fHeight) },
-        };
-        matrix.mapVectors(vec, 2);
-
-        fBaseFrequency.set(SkScalarInvert(vec[0].fX), SkScalarInvert(vec[0].fY));
-        fTileSize.set(SkScalarRoundToInt(vec[1].fX), SkScalarRoundToInt(vec[1].fY));
-        this->init(seed);
-        if (!fTileSize.isEmpty()) {
-            this->stitch();
-        }
-
-#if SK_SUPPORT_GPU
-        fPermutationsBitmap.setInfo(SkImageInfo::MakeA8(kBlockSize, 1));
-        fPermutationsBitmap.setPixels(fLatticeSelector);
-
-        fNoiseBitmap.setInfo(SkImageInfo::MakeN32Premul(kBlockSize, 4));
-        fNoiseBitmap.setPixels(fNoise[0][0]);
-#endif
-    }
-
-    int         fSeed;
-    uint8_t     fLatticeSelector[kBlockSize];
-    uint16_t    fNoise[4][kBlockSize][2];
-    SkPoint     fGradient[4][kBlockSize];
-    SkISize     fTileSize;
-    SkVector    fBaseFrequency;
-    StitchData  fStitchDataInit;
-
-private:
-
-#if SK_SUPPORT_GPU
-    SkBitmap   fPermutationsBitmap;
-    SkBitmap   fNoiseBitmap;
-#endif
-
-    inline int random()  {
-        static const int gRandAmplitude = 16807; // 7**5; primitive root of m
-        static const int gRandQ = 127773; // m / a
-        static const int gRandR = 2836; // m % a
-
-        int result = gRandAmplitude * (fSeed % gRandQ) - gRandR * (fSeed / gRandQ);
-        if (result <= 0)
-            result += kRandMaximum;
-        fSeed = result;
-        return result;
-    }
-
-    // Only called once. Could be part of the constructor.
-    void init(SkScalar seed)
-    {
-        static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize));
-
-        // According to the SVG spec, we must truncate (not round) the seed value.
-        fSeed = SkScalarTruncToInt(seed);
-        // The seed value clamp to the range [1, kRandMaximum - 1].
-        if (fSeed <= 0) {
-            fSeed = -(fSeed % (kRandMaximum - 1)) + 1;
-        }
-        if (fSeed > kRandMaximum - 1) {
-            fSeed = kRandMaximum - 1;
-        }
-        for (int channel = 0; channel < 4; ++channel) {
-            for (int i = 0; i < kBlockSize; ++i) {
-                fLatticeSelector[i] = i;
-                fNoise[channel][i][0] = (random() % (2 * kBlockSize));
-                fNoise[channel][i][1] = (random() % (2 * kBlockSize));
-            }
-        }
-        for (int i = kBlockSize - 1; i > 0; --i) {
-            int k = fLatticeSelector[i];
-            int j = random() % kBlockSize;
-            SkASSERT(j >= 0);
-            SkASSERT(j < kBlockSize);
-            fLatticeSelector[i] = fLatticeSelector[j];
-            fLatticeSelector[j] = k;
-        }
-
-        // Perform the permutations now
-        {
-            // Copy noise data
-            uint16_t noise[4][kBlockSize][2];
-            for (int i = 0; i < kBlockSize; ++i) {
-                for (int channel = 0; channel < 4; ++channel) {
-                    for (int j = 0; j < 2; ++j) {
-                        noise[channel][i][j] = fNoise[channel][i][j];
-                    }
-                }
-            }
-            // Do permutations on noise data
-            for (int i = 0; i < kBlockSize; ++i) {
-                for (int channel = 0; channel < 4; ++channel) {
-                    for (int j = 0; j < 2; ++j) {
-                        fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j];
-                    }
-                }
-            }
-        }
-
-        // Half of the largest possible value for 16 bit unsigned int
-        static const SkScalar gHalfMax16bits = 32767.5f;
-
-        // Compute gradients from permutated noise data
-        for (int channel = 0; channel < 4; ++channel) {
-            for (int i = 0; i < kBlockSize; ++i) {
-                fGradient[channel][i] = SkPoint::Make(
-                    (fNoise[channel][i][0] - kBlockSize) * gInvBlockSizef,
-                    (fNoise[channel][i][1] - kBlockSize) * gInvBlockSizef);
-                fGradient[channel][i].normalize();
-                // Put the normalized gradient back into the noise data
-                fNoise[channel][i][0] = SkScalarRoundToInt(
-                                                (fGradient[channel][i].fX + 1) * gHalfMax16bits);
-                fNoise[channel][i][1] = SkScalarRoundToInt(
-                                                (fGradient[channel][i].fY + 1) * gHalfMax16bits);
-            }
-        }
-    }
-
-    // Only called once. Could be part of the constructor.
-    void stitch() {
-        SkScalar tileWidth  = SkIntToScalar(fTileSize.width());
-        SkScalar tileHeight = SkIntToScalar(fTileSize.height());
-        SkASSERT(tileWidth > 0 && tileHeight > 0);
-        // When stitching tiled turbulence, the frequencies must be adjusted
-        // so that the tile borders will be continuous.
-        if (fBaseFrequency.fX) {
-            SkScalar lowFrequencx =
-                SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
-            SkScalar highFrequencx =
-                SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
-            // BaseFrequency should be non-negative according to the standard.
-            if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) {
-                fBaseFrequency.fX = lowFrequencx;
-            } else {
-                fBaseFrequency.fX = highFrequencx;
-            }
-        }
-        if (fBaseFrequency.fY) {
-            SkScalar lowFrequency =
-                SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
-            SkScalar highFrequency =
-                SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
-            if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) {
-                fBaseFrequency.fY = lowFrequency;
-            } else {
-                fBaseFrequency.fY = highFrequency;
-            }
-        }
-        // Set up TurbulenceInitial stitch values.
-        fStitchDataInit.fWidth  =
-            SkScalarRoundToInt(tileWidth * fBaseFrequency.fX);
-        fStitchDataInit.fWrapX  = kPerlinNoise + fStitchDataInit.fWidth;
-        fStitchDataInit.fHeight =
-            SkScalarRoundToInt(tileHeight * fBaseFrequency.fY);
-        fStitchDataInit.fWrapY  = kPerlinNoise + fStitchDataInit.fHeight;
-    }
-
-public:
-
-#if SK_SUPPORT_GPU
-    const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; }
-
-    const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; }
-#endif
-};
-
-sk_sp<SkShader> SkPerlinNoiseShader::MakeFractalNoise(SkScalar baseFrequencyX,
-                                                      SkScalar baseFrequencyY,
-                                                      int numOctaves, SkScalar seed,
-                                                      const SkISize* tileSize) {
-    return sk_sp<SkShader>(new SkPerlinNoiseShader(kFractalNoise_Type, baseFrequencyX,
-                                                   baseFrequencyY, numOctaves,
-                                                   seed, tileSize));
-}
-
-sk_sp<SkShader> SkPerlinNoiseShader::MakeTurbulence(SkScalar baseFrequencyX,
-                                                    SkScalar baseFrequencyY,
-                                                    int numOctaves, SkScalar seed,
-                                                    const SkISize* tileSize) {
-    return sk_sp<SkShader>(new SkPerlinNoiseShader(kTurbulence_Type, baseFrequencyX, baseFrequencyY,
-                                                   numOctaves, seed, tileSize));
-}
-
-SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShader::Type type,
-                                         SkScalar baseFrequencyX,
-                                         SkScalar baseFrequencyY,
-                                         int numOctaves,
-                                         SkScalar seed,
-                                         const SkISize* tileSize)
-  : fType(type)
-  , fBaseFrequencyX(baseFrequencyX)
-  , fBaseFrequencyY(baseFrequencyY)
-  , fNumOctaves(SkTPin<int>(numOctaves, 0, 255)) // [0,255] octaves allowed
-  , fSeed(seed)
-  , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize)
-  , fStitchTiles(!fTileSize.isEmpty())
-{
-    SkASSERT(fNumOctaves >= 0 && fNumOctaves < 256);
-}
-
-SkPerlinNoiseShader::~SkPerlinNoiseShader() {
-}
-
-sk_sp<SkFlattenable> SkPerlinNoiseShader::CreateProc(SkReadBuffer& buffer) {
-    Type type = (Type)buffer.readInt();
-    SkScalar freqX = buffer.readScalar();
-    SkScalar freqY = buffer.readScalar();
-    int octaves = buffer.readInt();
-    SkScalar seed = buffer.readScalar();
-    SkISize tileSize;
-    tileSize.fWidth = buffer.readInt();
-    tileSize.fHeight = buffer.readInt();
-
-    switch (type) {
-        case kFractalNoise_Type:
-            return SkPerlinNoiseShader::MakeFractalNoise(freqX, freqY, octaves, seed,
-                                                         &tileSize);
-        case kTurbulence_Type:
-            return SkPerlinNoiseShader::MakeTurbulence(freqX, freqY, octaves, seed,
-                                                       &tileSize);
-        default:
-            return nullptr;
-    }
-}
-
-void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeInt((int) fType);
-    buffer.writeScalar(fBaseFrequencyX);
-    buffer.writeScalar(fBaseFrequencyY);
-    buffer.writeInt(fNumOctaves);
-    buffer.writeScalar(fSeed);
-    buffer.writeInt(fTileSize.fWidth);
-    buffer.writeInt(fTileSize.fHeight);
-}
-
-SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::noise2D(
-        int channel, const StitchData& stitchData, const SkPoint& noiseVector) const {
-    struct Noise {
-        int noisePositionIntegerValue;
-        int nextNoisePositionIntegerValue;
-        SkScalar noisePositionFractionValue;
-        Noise(SkScalar component)
-        {
-            SkScalar position = component + kPerlinNoise;
-            noisePositionIntegerValue = SkScalarFloorToInt(position);
-            noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue);
-            nextNoisePositionIntegerValue = noisePositionIntegerValue + 1;
-        }
-    };
-    Noise noiseX(noiseVector.x());
-    Noise noiseY(noiseVector.y());
-    SkScalar u, v;
-    const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
-    // If stitching, adjust lattice points accordingly.
-    if (perlinNoiseShader.fStitchTiles) {
-        noiseX.noisePositionIntegerValue =
-            checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
-        noiseY.noisePositionIntegerValue =
-            checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
-        noiseX.nextNoisePositionIntegerValue =
-            checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
-        noiseY.nextNoisePositionIntegerValue =
-            checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
-    }
-    noiseX.noisePositionIntegerValue &= kBlockMask;
-    noiseY.noisePositionIntegerValue &= kBlockMask;
-    noiseX.nextNoisePositionIntegerValue &= kBlockMask;
-    noiseY.nextNoisePositionIntegerValue &= kBlockMask;
-    int i =
-        fPaintingData->fLatticeSelector[noiseX.noisePositionIntegerValue];
-    int j =
-        fPaintingData->fLatticeSelector[noiseX.nextNoisePositionIntegerValue];
-    int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask;
-    int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask;
-    int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
-    int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
-    SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue);
-    SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue);
-    // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
-    SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue,
-                                          noiseY.noisePositionFractionValue); // Offset (0,0)
-    u = fPaintingData->fGradient[channel][b00].dot(fractionValue);
-    fractionValue.fX -= SK_Scalar1; // Offset (-1,0)
-    v = fPaintingData->fGradient[channel][b10].dot(fractionValue);
-    SkScalar a = SkScalarInterp(u, v, sx);
-    fractionValue.fY -= SK_Scalar1; // Offset (-1,-1)
-    v = fPaintingData->fGradient[channel][b11].dot(fractionValue);
-    fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1)
-    u = fPaintingData->fGradient[channel][b01].dot(fractionValue);
-    SkScalar b = SkScalarInterp(u, v, sx);
-    return SkScalarInterp(a, b, sy);
-}
-
-SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint(
-        int channel, StitchData& stitchData, const SkPoint& point) const {
-    const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
-    if (perlinNoiseShader.fStitchTiles) {
-        // Set up TurbulenceInitial stitch values.
-        stitchData = fPaintingData->fStitchDataInit;
-    }
-    SkScalar turbulenceFunctionResult = 0;
-    SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData->fBaseFrequency.fX,
-                                      point.y() * fPaintingData->fBaseFrequency.fY));
-    SkScalar ratio = SK_Scalar1;
-    for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
-        SkScalar noise = noise2D(channel, stitchData, noiseVector);
-        SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ?
-                            noise : SkScalarAbs(noise);
-        turbulenceFunctionResult += numer / ratio;
-        noiseVector.fX *= 2;
-        noiseVector.fY *= 2;
-        ratio *= 2;
-        if (perlinNoiseShader.fStitchTiles) {
-            // Update stitch values
-            stitchData.fWidth  *= 2;
-            stitchData.fWrapX   = stitchData.fWidth + kPerlinNoise;
-            stitchData.fHeight *= 2;
-            stitchData.fWrapY   = stitchData.fHeight + kPerlinNoise;
-        }
-    }
-
-    // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
-    // by fractalNoise and (turbulenceFunctionResult) by turbulence.
-    if (perlinNoiseShader.fType == kFractalNoise_Type) {
-        turbulenceFunctionResult = turbulenceFunctionResult * SK_ScalarHalf + SK_ScalarHalf;
-    }
-
-    if (channel == 3) { // Scale alpha by paint value
-        turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255;
-    }
-
-    // Clamp result
-    return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1);
-}
-
-SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade(
-        const SkPoint& point, StitchData& stitchData) const {
-    SkPoint newPoint;
-    fMatrix.mapPoints(&newPoint, &point, 1);
-    newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
-    newPoint.fY = SkScalarRoundToScalar(newPoint.fY);
-
-    U8CPU rgba[4];
-    for (int channel = 3; channel >= 0; --channel) {
-        rgba[channel] = SkScalarFloorToInt(255 *
-            calculateTurbulenceValueForPoint(channel, stitchData, newPoint));
-    }
-    return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
-}
-
-SkShader::Context* SkPerlinNoiseShader::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const {
-    return alloc->make<PerlinNoiseShaderContext>(*this, rec);
-}
-
-SkPerlinNoiseShader::PerlinNoiseShaderContext::PerlinNoiseShaderContext(
-        const SkPerlinNoiseShader& shader, const ContextRec& rec)
-    : INHERITED(shader, rec)
-{
-    SkMatrix newMatrix = SkMatrix::Concat(*rec.fMatrix, shader.getLocalMatrix());
-    if (rec.fLocalMatrix) {
-        newMatrix.preConcat(*rec.fLocalMatrix);
-    }
-    // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
-    // (as opposed to 0 based, usually). The same adjustment is in the setData() function.
-    fMatrix.setTranslate(-newMatrix.getTranslateX() + SK_Scalar1, -newMatrix.getTranslateY() + SK_Scalar1);
-    fPaintingData = new PaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX,
-                                     shader.fBaseFrequencyY, newMatrix);
-}
-
-SkPerlinNoiseShader::PerlinNoiseShaderContext::~PerlinNoiseShaderContext() { delete fPaintingData; }
-
-void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan(
-        int x, int y, SkPMColor result[], int count) {
-    SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
-    StitchData stitchData;
-    for (int i = 0; i < count; ++i) {
-        result[i] = shade(point, stitchData);
-        point.fX += SK_Scalar1;
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-class GrGLPerlinNoise : public GrGLSLFragmentProcessor {
-public:
-    void emitCode(EmitArgs&) override;
-
-    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-private:
-    GrGLSLProgramDataManager::UniformHandle fStitchDataUni;
-    GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
-
-    typedef GrGLSLFragmentProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrPerlinNoiseEffect : public GrFragmentProcessor {
-public:
-    static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
-                                           SkPerlinNoiseShader::Type type,
-                                           int numOctaves, bool stitchTiles,
-                                           SkPerlinNoiseShader::PaintingData* paintingData,
-                                           sk_sp<GrTextureProxy> permutationsProxy,
-                                           sk_sp<GrTextureProxy> noiseProxy,
-                                           const SkMatrix& matrix) {
-        return sk_sp<GrFragmentProcessor>(
-            new GrPerlinNoiseEffect(resourceProvider, type, numOctaves, stitchTiles, paintingData,
-                                    std::move(permutationsProxy), std::move(noiseProxy), matrix));
-    }
-
-    ~GrPerlinNoiseEffect() override { delete fPaintingData; }
-
-    const char* name() const override { return "PerlinNoise"; }
-
-    const SkPerlinNoiseShader::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; }
-
-    SkPerlinNoiseShader::Type type() const { return fType; }
-    bool stitchTiles() const { return fStitchTiles; }
-    const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
-    int numOctaves() const { return fNumOctaves; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
-        return new GrGLPerlinNoise;
-    }
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override {
-        GrGLPerlinNoise::GenKey(*this, caps, b);
-    }
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const GrPerlinNoiseEffect& s = sBase.cast<GrPerlinNoiseEffect>();
-        return fType == s.fType &&
-               fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency &&
-               fNumOctaves == s.fNumOctaves &&
-               fStitchTiles == s.fStitchTiles &&
-               fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
-    }
-
-    GrPerlinNoiseEffect(GrResourceProvider* resourceProvider,
-                        SkPerlinNoiseShader::Type type, int numOctaves, bool stitchTiles,
-                        SkPerlinNoiseShader::PaintingData* paintingData,
-                        sk_sp<GrTextureProxy> permutationsProxy, sk_sp<GrTextureProxy> noiseProxy,
-                        const SkMatrix& matrix)
-            : INHERITED(kNone_OptimizationFlags)
-            , fType(type)
-            , fCoordTransform(matrix)
-            , fNumOctaves(numOctaves)
-            , fStitchTiles(stitchTiles)
-            , fPermutationsSampler(resourceProvider, std::move(permutationsProxy))
-            , fNoiseSampler(resourceProvider, std::move(noiseProxy))
-            , fPaintingData(paintingData) {
-        this->initClassID<GrPerlinNoiseEffect>();
-        this->addTextureSampler(&fPermutationsSampler);
-        this->addTextureSampler(&fNoiseSampler);
-        this->addCoordTransform(&fCoordTransform);
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    SkPerlinNoiseShader::Type       fType;
-    GrCoordTransform                fCoordTransform;
-    int                             fNumOctaves;
-    bool                            fStitchTiles;
-    TextureSampler                  fPermutationsSampler;
-    TextureSampler                  fNoiseSampler;
-    SkPerlinNoiseShader::PaintingData *fPaintingData;
-
-private:
-    typedef GrFragmentProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoiseEffect);
-
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrPerlinNoiseEffect::TestCreate(GrProcessorTestData* d) {
-    int      numOctaves = d->fRandom->nextRangeU(2, 10);
-    bool     stitchTiles = d->fRandom->nextBool();
-    SkScalar seed = SkIntToScalar(d->fRandom->nextU());
-    SkISize  tileSize = SkISize::Make(d->fRandom->nextRangeU(4, 4096),
-                                      d->fRandom->nextRangeU(4, 4096));
-    SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f,
-                                                          0.99f);
-    SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f,
-                                                          0.99f);
-
-    sk_sp<SkShader> shader(d->fRandom->nextBool() ?
-        SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed,
-                                              stitchTiles ? &tileSize : nullptr) :
-        SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed,
-                                            stitchTiles ? &tileSize : nullptr));
-
-    GrTest::TestAsFPArgs asFPArgs(d);
-    return shader->asFragmentProcessor(asFPArgs.args());
-}
-#endif
-
-void GrGLPerlinNoise::emitCode(EmitArgs& args) {
-    const GrPerlinNoiseEffect& pne = args.fFp.cast<GrPerlinNoiseEffect>();
-
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-
-    fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                   kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                                   "baseFrequency");
-    const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
-
-    const char* stitchDataUni = nullptr;
-    if (pne.stitchTiles()) {
-        fStitchDataUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                    kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                                    "stitchData");
-        stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni);
-    }
-
-    // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8
-    const char* chanCoordR  = "0.125";
-    const char* chanCoordG  = "0.375";
-    const char* chanCoordB  = "0.625";
-    const char* chanCoordA  = "0.875";
-    const char* chanCoord   = "chanCoord";
-    const char* stitchData  = "stitchData";
-    const char* ratio       = "ratio";
-    const char* noiseVec    = "noiseVec";
-    const char* noiseSmooth = "noiseSmooth";
-    const char* floorVal    = "floorVal";
-    const char* fractVal    = "fractVal";
-    const char* uv          = "uv";
-    const char* ab          = "ab";
-    const char* latticeIdx  = "latticeIdx";
-    const char* bcoords     = "bcoords";
-    const char* lattice     = "lattice";
-    const char* inc8bit     = "0.00390625";  // 1.0 / 256.0
-    // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
-    // [-1,1] vector and perform a dot product between that vector and the provided vector.
-    const char* dotLattice  = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);";
-
-    // Add noise function
-    static const GrShaderVar gPerlinNoiseArgs[] =  {
-        GrShaderVar(chanCoord, kFloat_GrSLType),
-        GrShaderVar(noiseVec, kVec2f_GrSLType)
-    };
-
-    static const GrShaderVar gPerlinNoiseStitchArgs[] =  {
-        GrShaderVar(chanCoord, kFloat_GrSLType),
-        GrShaderVar(noiseVec, kVec2f_GrSLType),
-        GrShaderVar(stitchData, kVec2f_GrSLType)
-    };
-
-    SkString noiseCode;
-
-    noiseCode.appendf("\tvec4 %s;\n", floorVal);
-    noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec);
-    noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal);
-    noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec);
-
-    // smooth curve : t * t * (3 - 2 * t)
-    noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);",
-        noiseSmooth, fractVal, fractVal, fractVal);
-
-    // Adjust frequencies if we're stitching tiles
-    if (pne.stitchTiles()) {
-        noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }",
-                          floorVal, stitchData, floorVal, stitchData);
-        noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }",
-                          floorVal, stitchData, floorVal, stitchData);
-        noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }",
-                          floorVal, stitchData, floorVal, stitchData);
-        noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }",
-                          floorVal, stitchData, floorVal, stitchData);
-    }
-
-    // Get texture coordinates and normalize
-    noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n",
-                      floorVal, floorVal);
-
-    // Get permutation for x
-    {
-        SkString xCoords("");
-        xCoords.appendf("vec2(%s.x, 0.5)", floorVal);
-
-        noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx);
-        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(),
-                                         kVec2f_GrSLType);
-        noiseCode.append(".r;");
-    }
-
-    // Get permutation for x + 1
-    {
-        SkString xCoords("");
-        xCoords.appendf("vec2(%s.z, 0.5)", floorVal);
-
-        noiseCode.appendf("\n\t%s.y = ", latticeIdx);
-        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(),
-                                         kVec2f_GrSLType);
-        noiseCode.append(".r;");
-    }
-
-#if defined(SK_BUILD_FOR_ANDROID)
-    // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3).
-    // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit
-    // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725
-    // (or 0.484368 here). The following rounding operation prevents these precision issues from
-    // affecting the result of the noise by making sure that we only have multiples of 1/255.
-    // (Note that 1/255 is about 0.003921569, which is the value used here).
-    noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);",
-                      latticeIdx, latticeIdx);
-#endif
-
-    // Get (x,y) coordinates with the permutated x
-    noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal);
-
-    noiseCode.appendf("\n\n\tvec2 %s;", uv);
-    // Compute u, at offset (0,0)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord);
-        noiseCode.appendf("\n\tvec4 %s = ", lattice);
-        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-                                         kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal);
-    // Compute v, at offset (-1,0)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord);
-        noiseCode.append("\n\tlattice = ");
-        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-                                         kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    // Compute 'a' as a linear interpolation of 'u' and 'v'
-    noiseCode.appendf("\n\tvec2 %s;", ab);
-    noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
-
-    noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal);
-    // Compute v, at offset (-1,-1)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord);
-        noiseCode.append("\n\tlattice = ");
-        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-                                         kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    noiseCode.appendf("\n\t%s.x += 1.0;", fractVal);
-    // Compute u, at offset (0,-1)
-    {
-        SkString latticeCoords("");
-        latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord);
-        noiseCode.append("\n\tlattice = ");
-        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
-                                         kVec2f_GrSLType);
-        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
-        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
-    }
-
-    // Compute 'b' as a linear interpolation of 'u' and 'v'
-    noiseCode.appendf("\n\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
-    // Compute the noise as a linear interpolation of 'a' and 'b'
-    noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth);
-
-    SkString noiseFuncName;
-    if (pne.stitchTiles()) {
-        fragBuilder->emitFunction(kFloat_GrSLType,
-                                  "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs),
-                                  gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName);
-    } else {
-        fragBuilder->emitFunction(kFloat_GrSLType,
-                                  "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs),
-                                  gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName);
-    }
-
-    // There are rounding errors if the floor operation is not performed here
-    fragBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;",
-                             noiseVec, vCoords.c_str(), baseFrequencyUni);
-
-    // Clear the color accumulator
-    fragBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", args.fOutputColor);
-
-    if (pne.stitchTiles()) {
-        // Set up TurbulenceInitial stitch values.
-        fragBuilder->codeAppendf("vec2 %s = %s;", stitchData, stitchDataUni);
-    }
-
-    fragBuilder->codeAppendf("float %s = 1.0;", ratio);
-
-    // Loop over all octaves
-    fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves());
-
-    fragBuilder->codeAppendf("%s += ", args.fOutputColor);
-    if (pne.type() != SkPerlinNoiseShader::kFractalNoise_Type) {
-        fragBuilder->codeAppend("abs(");
-    }
-    if (pne.stitchTiles()) {
-        fragBuilder->codeAppendf(
-            "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s),"
-                 "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))",
-            noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData,
-            noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData,
-            noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData,
-            noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData);
-    } else {
-        fragBuilder->codeAppendf(
-            "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s),"
-                 "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))",
-            noiseFuncName.c_str(), chanCoordR, noiseVec,
-            noiseFuncName.c_str(), chanCoordG, noiseVec,
-            noiseFuncName.c_str(), chanCoordB, noiseVec,
-            noiseFuncName.c_str(), chanCoordA, noiseVec);
-    }
-    if (pne.type() != SkPerlinNoiseShader::kFractalNoise_Type) {
-        fragBuilder->codeAppendf(")"); // end of "abs("
-    }
-    fragBuilder->codeAppendf(" * %s;", ratio);
-
-    fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec);
-    fragBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio);
-
-    if (pne.stitchTiles()) {
-        fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData);
-    }
-    fragBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves
-
-    if (pne.type() == SkPerlinNoiseShader::kFractalNoise_Type) {
-        // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
-        // by fractalNoise and (turbulenceFunctionResult) by turbulence.
-        fragBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);",
-                                 args.fOutputColor,args.fOutputColor);
-    }
-
-    // Clamp values
-    fragBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor);
-
-    // Pre-multiply the result
-    fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
-                             args.fOutputColor, args.fOutputColor,
-                             args.fOutputColor, args.fOutputColor);
-}
-
-void GrGLPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&,
-                             GrProcessorKeyBuilder* b) {
-    const GrPerlinNoiseEffect& turbulence = processor.cast<GrPerlinNoiseEffect>();
-
-    uint32_t key = turbulence.numOctaves();
-
-    key = key << 3; // Make room for next 3 bits
-
-    switch (turbulence.type()) {
-        case SkPerlinNoiseShader::kFractalNoise_Type:
-            key |= 0x1;
-            break;
-        case SkPerlinNoiseShader::kTurbulence_Type:
-            key |= 0x2;
-            break;
-        default:
-            // leave key at 0
-            break;
-    }
-
-    if (turbulence.stitchTiles()) {
-        key |= 0x4; // Flip the 3rd bit if tile stitching is on
-    }
-
-    b->add32(key);
-}
-
-void GrGLPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman,
-                                const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-
-    const GrPerlinNoiseEffect& turbulence = processor.cast<GrPerlinNoiseEffect>();
-
-    const SkVector& baseFrequency = turbulence.baseFrequency();
-    pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
-
-    if (turbulence.stitchTiles()) {
-        const SkPerlinNoiseShader::StitchData& stitchData = turbulence.stitchData();
-        pdman.set2f(fStitchDataUni, SkIntToScalar(stitchData.fWidth),
-                                   SkIntToScalar(stitchData.fHeight));
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-sk_sp<GrFragmentProcessor> SkPerlinNoiseShader::asFragmentProcessor(const AsFPArgs& args) const {
-    SkASSERT(args.fContext);
-
-    SkMatrix localMatrix = this->getLocalMatrix();
-    if (args.fLocalMatrix) {
-        localMatrix.preConcat(*args.fLocalMatrix);
-    }
-
-    SkMatrix matrix = *args.fViewMatrix;
-    matrix.preConcat(localMatrix);
-
-    if (0 == fNumOctaves) {
-        if (kFractalNoise_Type == fType) {
-            // Extract the incoming alpha and emit rgba = (a/4, a/4, a/4, a/2)
-            // TODO: Either treat the output of this shader as sRGB or allow client to specify a
-            // color space of the noise. Either way, this case (and the GLSL) need to convert to
-            // the destination.
-            sk_sp<GrFragmentProcessor> inner(
-                GrConstColorProcessor::Make(GrColor4f::FromGrColor(0x80404040),
-                                            GrConstColorProcessor::kModulateRGBA_InputMode));
-            return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-        }
-        // Emit zero.
-        return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
-                                           GrConstColorProcessor::kIgnore_InputMode);
-    }
-
-    // Either we don't stitch tiles, either we have a valid tile size
-    SkASSERT(!fStitchTiles || !fTileSize.isEmpty());
-
-    SkPerlinNoiseShader::PaintingData* paintingData =
-            new PaintingData(fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix);
-    sk_sp<GrTextureProxy> permutationsProxy(GrMakeCachedBitmapProxy(
-                                                            args.fContext->resourceProvider(),
-                                                            paintingData->getPermutationsBitmap()));
-    sk_sp<GrTextureProxy> noiseProxy(GrMakeCachedBitmapProxy(args.fContext->resourceProvider(),
-                                                             paintingData->getNoiseBitmap()));
-
-    SkMatrix m = *args.fViewMatrix;
-    m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1);
-    m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1);
-    if (permutationsProxy && noiseProxy) {
-        sk_sp<GrFragmentProcessor> inner(
-            GrPerlinNoiseEffect::Make(args.fContext->resourceProvider(),
-                                      fType,
-                                      fNumOctaves,
-                                      fStitchTiles,
-                                      paintingData,
-                                      std::move(permutationsProxy),
-                                      std::move(noiseProxy),
-                                      m));
-        return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-    }
-    delete paintingData;
-    return nullptr;
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkPerlinNoiseShader::toString(SkString* str) const {
-    str->append("SkPerlinNoiseShader: (");
-
-    str->append("type: ");
-    switch (fType) {
-        case kFractalNoise_Type:
-            str->append("\"fractal noise\"");
-            break;
-        case kTurbulence_Type:
-            str->append("\"turbulence\"");
-            break;
-        default:
-            str->append("\"unknown\"");
-            break;
-    }
-    str->append(" base frequency: (");
-    str->appendScalar(fBaseFrequencyX);
-    str->append(", ");
-    str->appendScalar(fBaseFrequencyY);
-    str->append(") number of octaves: ");
-    str->appendS32(fNumOctaves);
-    str->append(" seed: ");
-    str->appendScalar(fSeed);
-    str->append(" stitch tiles: ");
-    str->append(fStitchTiles ? "true " : "false ");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/src/effects/SkPictureImageFilter.cpp b/src/effects/SkPictureImageFilter.cpp
index 6539104..2a86354 100644
--- a/src/effects/SkPictureImageFilter.cpp
+++ b/src/effects/SkPictureImageFilter.cpp
@@ -8,6 +8,8 @@
 #include "SkPictureImageFilter.h"
 
 #include "SkCanvas.h"
+#include "SkColorSpaceXformCanvas.h"
+#include "SkColorSpaceXformer.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
@@ -23,7 +25,8 @@
     return sk_sp<SkImageFilter>(new SkPictureImageFilter(std::move(picture), 
                                                          cropRect,
                                                          kDeviceSpace_PictureResolution,
-                                                         kLow_SkFilterQuality));
+                                                         kLow_SkFilterQuality,
+                                                         nullptr));
 }
 
 sk_sp<SkImageFilter> SkPictureImageFilter::MakeForLocalSpace(sk_sp<SkPicture> picture,
@@ -32,7 +35,8 @@
     return sk_sp<SkImageFilter>(new SkPictureImageFilter(std::move(picture),
                                                          cropRect,
                                                          kLocalSpace_PictureResolution,
-                                                         filterQuality));
+                                                         filterQuality,
+                                                         nullptr));
 }
 
 SkPictureImageFilter::SkPictureImageFilter(sk_sp<SkPicture> picture)
@@ -45,12 +49,14 @@
 
 SkPictureImageFilter::SkPictureImageFilter(sk_sp<SkPicture> picture, const SkRect& cropRect,
                                            PictureResolution pictureResolution,
-                                           SkFilterQuality filterQuality)
+                                           SkFilterQuality filterQuality,
+                                           sk_sp<SkColorSpace> colorSpace)
     : INHERITED(nullptr, 0, nullptr)
     , fPicture(std::move(picture))
     , fCropRect(cropRect)
     , fPictureResolution(pictureResolution)
-    , fFilterQuality(filterQuality) {
+    , fFilterQuality(filterQuality)
+    , fColorSpace(std::move(colorSpace)) {
 }
 
 sk_sp<SkFlattenable> SkPictureImageFilter::CreateProc(SkReadBuffer& buffer) {
@@ -125,7 +131,6 @@
 
     SkCanvas* canvas = surf->getCanvas();
     SkASSERT(canvas);
-
     canvas->clear(0x0);
 
     if (kDeviceSpace_PictureResolution == fPictureResolution ||
@@ -140,9 +145,21 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkPictureImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return sk_sp<SkImageFilter>(new SkPictureImageFilter(fPicture, fCropRect, fPictureResolution,
+            fFilterQuality, xformer->dst()));
+}
+
 void SkPictureImageFilter::drawPictureAtDeviceResolution(SkCanvas* canvas,
                                                          const SkIRect& deviceBounds,
                                                          const Context& ctx) const {
+    std::unique_ptr<SkCanvas> xformCanvas = nullptr;
+    if (fColorSpace) {
+        // Only non-null in the case where onMakeColorSpace() was called.  This instructs
+        // us to do the color space xform on playback.
+        xformCanvas = SkCreateColorSpaceXformCanvas(canvas, fColorSpace);
+        canvas = xformCanvas.get();
+    }
     canvas->translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
     canvas->concat(ctx.ctm());
     canvas->drawPicture(fPicture);
@@ -174,7 +191,14 @@
 
         SkCanvas* localCanvas = localSurface->getCanvas();
         SkASSERT(localCanvas);
-        
+        std::unique_ptr<SkCanvas> xformCanvas = nullptr;
+        if (fColorSpace) {
+            // Only non-null in the case where onMakeColorSpace() was called.  This instructs
+            // us to do the color space xform on playback.
+            xformCanvas = SkCreateColorSpaceXformCanvas(localCanvas, fColorSpace);
+            localCanvas = xformCanvas.get();
+        }
+
         localCanvas->clear(0x0);
 
         localCanvas->translate(-SkIntToScalar(localIBounds.fLeft),
diff --git a/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp b/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp
index 609f37e..1c7134e 100644
--- a/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp
+++ b/src/effects/SkRRectsGaussianEdgeMaskFilter.cpp
@@ -398,7 +398,8 @@
         }
 
     protected:
-        void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& proc) override {
             const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>();
 
             const SkRRect& first = edgeFP.first();
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index f09d5ed..93d784f 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -1,12 +1,12 @@
 /*
-* 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 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 #include "SkTableColorFilter.h"
-
+#include "SkPM4f.h"
 #include "SkArenaAlloc.h"
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
@@ -102,7 +102,7 @@
         kB_Flag = 1 << 3,
     };
 
-    bool onAppendStages(SkRasterPipeline* p, SkColorSpace*, SkArenaAlloc* alloc,
+    void onAppendStages(SkRasterPipeline* p, SkColorSpace*, SkArenaAlloc* alloc,
                         bool shaderIsOpaque) const override {
         const uint8_t *r = gIdentityTable,
                       *g = gIdentityTable,
@@ -125,7 +125,6 @@
         if (!definitelyOpaque) {
             p->append(SkRasterPipeline::premul);
         }
-        return true;
     }
 
 protected:
@@ -321,7 +320,6 @@
         return nullptr;
     }
 
-    innerBM.lockPixels();
     if (nullptr == innerBM.getPixels()) {
         return nullptr;
     }
@@ -407,14 +405,15 @@
     static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     UniformHandle fRGBAYValuesUni;
     typedef GrGLSLFragmentProcessor INHERITED;
 };
 
-void GLColorTableEffect::onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& proc) {
+void GLColorTableEffect::onSetData(const GrGLSLProgramDataManager& pdm,
+                                   const GrFragmentProcessor& proc) {
     // The textures are organized in a strip where the rows are ordered a, r, g, b.
     float rgbaYValues[4];
     const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp
index b36f742..262ad65 100644
--- a/src/effects/SkTileImageFilter.cpp
+++ b/src/effects/SkTileImageFilter.cpp
@@ -116,6 +116,16 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkTileImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkASSERT(1 == this->countInputs());
+    if (!this->getInput(0)) {
+        return sk_ref_sp(const_cast<SkTileImageFilter*>(this));
+    }
+
+    sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
+    return SkTileImageFilter::Make(fSrcRect, fDstRect, std::move(input));
+}
+
 SkIRect SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
                                               MapDirection direction) const {
     SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect;
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 514cfd5..2af6d22 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -38,6 +38,7 @@
 protected:
     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
                                         SkIPoint* offset) const override;
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
 
 #if SK_SUPPORT_GPU
     sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
@@ -187,6 +188,22 @@
     return surf->makeImageSnapshot();
 }
 
+sk_sp<SkImageFilter> SkXfermodeImageFilter_Base::onMakeColorSpace(SkColorSpaceXformer* xformer)
+const {
+    SkASSERT(2 == this->countInputs());
+    if (!this->getInput(0) && !this->getInput(1)) {
+        return sk_ref_sp(const_cast<SkXfermodeImageFilter_Base*>(this));
+    }
+
+    sk_sp<SkImageFilter> background =
+            this->getInput(0) ? this->getInput(0)->makeColorSpace(xformer) : nullptr;
+    sk_sp<SkImageFilter> foreground =
+            this->getInput(1) ? this->getInput(1)->makeColorSpace(xformer) : nullptr;
+
+    return SkXfermodeImageFilter::Make(fMode, std::move(background), std::move(foreground),
+                                       this->getCropRectIfSet());
+}
+
 void SkXfermodeImageFilter_Base::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
                                                 const SkIRect& fgBounds) const {
     SkPaint paint;
@@ -324,7 +341,6 @@
         // have to get one the hard way.
         struct ProcCoeff rec;
         rec.fProc = SkXfermode::GetProc(SkBlendMode::kSrcOver);
-        SkXfermode::ModeAsCoeff(SkBlendMode::kSrcOver, &rec.fSC, &rec.fDC);
 
         srcover.reset(new SkProcCoeffXfermode(rec, SkBlendMode::kSrcOver));
         xfer = srcover.get();
diff --git a/src/effects/gradients/Sk4fGradientBase.cpp b/src/effects/gradients/Sk4fGradientBase.cpp
deleted file mode 100644
index b385d8c..0000000
--- a/src/effects/gradients/Sk4fGradientBase.cpp
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * 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 "Sk4fGradientBase.h"
-
-#include <functional>
-
-namespace {
-
-Sk4f pack_color(SkColor c, bool premul, const Sk4f& component_scale) {
-    const SkColor4f c4f = SkColor4f::FromColor(c);
-    const Sk4f pm4f = premul
-        ? c4f.premul().to4f()
-        : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA};
-
-    return pm4f * component_scale;
-}
-
-class IntervalIterator {
-public:
-    IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse)
-        : fColors(colors)
-        , fPos(pos)
-        , fCount(count)
-        , fFirstPos(reverse ? SK_Scalar1 : 0)
-        , fBegin(reverse ? count - 1 : 0)
-        , fAdvance(reverse ? -1 : 1) {
-        SkASSERT(colors);
-        SkASSERT(count > 0);
-    }
-
-    void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
-        if (!fPos) {
-            this->iterateImplicitPos(func);
-            return;
-        }
-
-        const int end = fBegin + fAdvance * (fCount - 1);
-        const SkScalar lastPos = 1 - fFirstPos;
-        int prev = fBegin;
-        SkScalar prevPos = fFirstPos;
-
-        do {
-            const int curr = prev + fAdvance;
-            SkASSERT(curr >= 0 && curr < fCount);
-
-            // TODO: this sanitization should be done in SkGradientShaderBase
-            const SkScalar currPos = (fAdvance > 0)
-                ? SkTPin(fPos[curr], prevPos, lastPos)
-                : SkTPin(fPos[curr], lastPos, prevPos);
-
-            if (currPos != prevPos) {
-                SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
-                func(fColors[prev], fColors[curr], prevPos, currPos);
-            }
-
-            prev = curr;
-            prevPos = currPos;
-        } while (prev != end);
-    }
-
-private:
-    void iterateImplicitPos(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const {
-        // When clients don't provide explicit color stop positions (fPos == nullptr),
-        // the color stops are distributed evenly across the unit interval
-        // (implicit positioning).
-        const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1);
-        const int end = fBegin + fAdvance * (fCount - 2);
-        int prev = fBegin;
-        SkScalar prevPos = fFirstPos;
-
-        while (prev != end) {
-            const int curr = prev + fAdvance;
-            SkASSERT(curr >= 0 && curr < fCount);
-
-            const SkScalar currPos = prevPos + dt;
-            func(fColors[prev], fColors[curr], prevPos, currPos);
-            prev = curr;
-            prevPos = currPos;
-        }
-
-        // emit the last interval with a pinned end position, to avoid precision issues
-        func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos);
-    }
-
-    const SkColor*  fColors;
-    const SkScalar* fPos;
-    const int       fCount;
-    const SkScalar  fFirstPos;
-    const int       fBegin;
-    const int       fAdvance;
-};
-
-void addMirrorIntervals(const SkColor colors[],
-                        const SkScalar pos[], int count,
-                        const Sk4f& componentScale,
-                        bool premulColors, bool reverse,
-                        Sk4fGradientIntervalBuffer::BufferType* buffer) {
-    const IntervalIterator iter(colors, pos, count, reverse);
-    iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
-        SkASSERT(buffer->empty() || buffer->back().fP1 == 2 - p0);
-
-        const auto mirror_p0 = 2 - p0;
-        const auto mirror_p1 = 2 - p1;
-        // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
-        // triggering Interval asserts.
-        if (mirror_p0 != mirror_p1) {
-            buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_p0,
-                                 pack_color(c1, premulColors, componentScale), mirror_p1);
-        }
-    });
-}
-
-} // anonymous namespace
-
-Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar p0,
-                                           const Sk4f& c1, SkScalar p1)
-    : fP0(p0)
-    , fP1(p1)
-    , fZeroRamp((c0 == c1).allTrue()) {
-    SkASSERT(p0 != p1);
-    // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
-    SkASSERT(SkScalarIsFinite(p0) || SkScalarIsFinite(p1));
-
-    const auto dp = p1 - p0;
-
-    // Clamp edge intervals are always zero-ramp.
-    SkASSERT(SkScalarIsFinite(dp) || fZeroRamp);
-    const Sk4f dc = SkScalarIsFinite(dp) ? (c1 - c0) / dp : 0;
-
-    c0.store(&fC0.fVec);
-    dc.store(&fDc.fVec);
-}
-
-void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count,
-                                      SkShader::TileMode tileMode, bool premulColors,
-                                      SkScalar alpha, bool reverse) {
-    // The main job here is to build a specialized interval list: a different
-    // representation of the color stops data, optimized for efficient scan line
-    // access during shading.
-    //
-    //   [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
-    //
-    // The list may be inverted when requested (such that e.g. points are sorted
-    // in increasing x order when dx < 0).
-    //
-    // Note: the current representation duplicates pos data; we could refactor to
-    //       avoid this if interval storage size becomes a concern.
-    //
-    // Aside from reordering, we also perform two more pre-processing steps at
-    // this stage:
-    //
-    //   1) scale the color components depending on paint alpha and the requested
-    //      interpolation space (note: the interval color storage is SkPM4f, but
-    //      that doesn't necessarily mean the colors are premultiplied; that
-    //      property is tracked in fColorsArePremul)
-    //
-    //   2) inject synthetic intervals to support tiling.
-    //
-    //      * for kRepeat, no extra intervals are needed - the iterator just
-    //        wraps around at the end:
-    //
-    //          ->[P0,P1)->..[Pn-1,Pn)->
-    //
-    //      * for kClamp, we add two "infinite" intervals before/after:
-    //
-    //          [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
-    //
-    //        (the iterator should never run off the end in this mode)
-    //
-    //      * for kMirror, we extend the range to [0..2] and add a flipped
-    //        interval series - then the iterator operates just as in the
-    //        kRepeat case:
-    //
-    //          ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
-    //
-    // TODO: investigate collapsing intervals << 1px.
-
-    SkASSERT(count > 0);
-    SkASSERT(colors);
-
-    fIntervals.reset();
-
-    const Sk4f componentScale = premulColors
-        ? Sk4f(alpha)
-        : Sk4f(1.0f, 1.0f, 1.0f, alpha);
-    const int first_index = reverse ? count - 1 : 0;
-    const int last_index = count - 1 - first_index;
-    const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
-    const SkScalar last_pos = SK_Scalar1 - first_pos;
-
-    if (tileMode == SkShader::kClamp_TileMode) {
-        // synthetic edge interval: -/+inf .. P0
-        const Sk4f clamp_color = pack_color(colors[first_index],
-                                            premulColors, componentScale);
-        const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
-        fIntervals.emplace_back(clamp_color, clamp_pos,
-                                clamp_color, first_pos);
-    } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
-        // synthetic mirror intervals injected before main intervals: (2 .. 1]
-        addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals);
-    }
-
-    const IntervalIterator iter(colors, pos, count, reverse);
-    iter.iterate([&] (SkColor c0, SkColor c1, SkScalar p0, SkScalar p1) {
-        SkASSERT(fIntervals.empty() || fIntervals.back().fP1 == p0);
-
-        fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), p0,
-                                pack_color(c1, premulColors, componentScale), p1);
-    });
-
-    if (tileMode == SkShader::kClamp_TileMode) {
-        // synthetic edge interval: Pn .. +/-inf
-        const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale);
-        const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
-        fIntervals.emplace_back(clamp_color, last_pos,
-                                clamp_color, clamp_pos);
-    } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
-        // synthetic mirror intervals injected after main intervals: [1 .. 2)
-        addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals);
-    }
-}
-
-const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
-    // Binary search.
-    const auto* i0 = fIntervals.begin();
-    const auto* i1 = fIntervals.end() - 1;
-
-    while (i0 != i1) {
-        SkASSERT(i0 < i1);
-        SkASSERT(t >= i0->fP0 && t <= i1->fP1);
-
-        const auto* i = i0 + ((i1 - i0) >> 1);
-
-        if (t > i->fP1) {
-            i0 = i + 1;
-        } else {
-            i1 = i;
-        }
-    }
-
-    SkASSERT(i0->contains(t));
-    return i0;
-}
-
-const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
-    SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
-
-    SkASSERT(!prev->contains(t));
-    SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
-    SkASSERT(t >= fIntervals.front().fP0 && t <= fIntervals.back().fP1);
-
-    const auto* i = prev;
-
-    // Use the |increasing| signal to figure which direction we should search for
-    // the next interval, then perform a linear search.
-    if (increasing) {
-        do {
-            i += 1;
-            if (i >= fIntervals.end()) {
-                i = fIntervals.begin();
-            }
-        } while (!i->contains(t));
-    } else {
-        do {
-            i -= 1;
-            if (i < fIntervals.begin()) {
-                i = fIntervals.end() - 1;
-            }
-        } while (!i->contains(t));
-    }
-
-    return i;
-}
-
-SkGradientShaderBase::
-GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
-                                                         const ContextRec& rec)
-    : INHERITED(shader, rec)
-    , fFlags(this->INHERITED::getFlags())
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
-    , fDither(true)
-#else
-    , fDither(rec.fPaint->isDither())
-#endif
-{
-    const SkMatrix& inverse = this->getTotalInverse();
-    fDstToPos.setConcat(shader.fPtsToUnit, inverse);
-    fDstToPosProc = fDstToPos.getMapXYProc();
-    fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
-
-    if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
-        fFlags |= kOpaqueAlpha_Flag;
-    }
-
-    fColorsArePremul =
-        (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
-        || shader.fColorsAreOpaque;
-}
-
-bool SkGradientShaderBase::
-GradientShaderBase4fContext::isValid() const {
-    return fDstToPos.isFinite();
-}
-
-void SkGradientShaderBase::
-GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
-    if (fColorsArePremul) {
-        this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count);
-    } else {
-        this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count);
-    }
-}
-
-void SkGradientShaderBase::
-GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
-    if (fColorsArePremul) {
-        this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count);
-    } else {
-        this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count);
-    }
-}
-
-template<DstType dstType, ApplyPremul premul>
-void SkGradientShaderBase::
-GradientShaderBase4fContext::shadePremulSpan(int x, int y,
-                                             typename DstTraits<dstType, premul>::Type dst[],
-                                             int count) const {
-    const SkGradientShaderBase& shader =
-        static_cast<const SkGradientShaderBase&>(fShader);
-
-    switch (shader.fTileMode) {
-    case kClamp_TileMode:
-        this->shadeSpanInternal<dstType,
-                                premul,
-                                kClamp_TileMode>(x, y, dst, count);
-        break;
-    case kRepeat_TileMode:
-        this->shadeSpanInternal<dstType,
-                                premul,
-                                kRepeat_TileMode>(x, y, dst, count);
-        break;
-    case kMirror_TileMode:
-        this->shadeSpanInternal<dstType,
-                                premul,
-                                kMirror_TileMode>(x, y, dst, count);
-        break;
-    }
-}
-
-template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
-void SkGradientShaderBase::
-GradientShaderBase4fContext::shadeSpanInternal(int x, int y,
-                                               typename DstTraits<dstType, premul>::Type dst[],
-                                               int count) const {
-    static const int kBufSize = 128;
-    SkScalar ts[kBufSize];
-    TSampler<dstType, premul, tileMode> sampler(*this);
-
-    SkASSERT(count > 0);
-    do {
-        const int n = SkTMin(kBufSize, count);
-        this->mapTs(x, y, ts, n);
-        for (int i = 0; i < n; ++i) {
-            const Sk4f c = sampler.sample(ts[i]);
-            DstTraits<dstType, premul>::store(c, dst++);
-        }
-        x += n;
-        count -= n;
-    } while (count > 0);
-}
-
-template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
-class SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
-public:
-    TSampler(const GradientShaderBase4fContext& ctx)
-        : fCtx(ctx)
-        , fInterval(nullptr) {
-        switch (tileMode) {
-        case kClamp_TileMode:
-            fLargestIntervalValue = SK_ScalarInfinity;
-            break;
-        case kRepeat_TileMode:
-            fLargestIntervalValue = nextafterf(1, 0);
-            break;
-        case kMirror_TileMode:
-            fLargestIntervalValue = nextafterf(2.0f, 0);
-            break;
-        }
-    }
-
-    Sk4f sample(SkScalar t) {
-        const auto tiled_t = tileProc(t);
-
-        if (!fInterval) {
-            // Very first sample => locate the initial interval.
-            // TODO: maybe do this in ctor to remove a branch?
-            fInterval = fCtx.fIntervals.find(tiled_t);
-            this->loadIntervalData(fInterval);
-        } else if (!fInterval->contains(tiled_t)) {
-            fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT);
-            this->loadIntervalData(fInterval);
-        }
-
-        fPrevT = t;
-        return lerp(tiled_t);
-    }
-
-private:
-    SkScalar tileProc(SkScalar t) const {
-        switch (tileMode) {
-        case kClamp_TileMode:
-            // synthetic clamp-mode edge intervals allow for a free-floating t:
-            //   [-inf..0)[0..1)[1..+inf)
-            return t;
-        case kRepeat_TileMode:
-            // t % 1  (intervals range: [0..1))
-            // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1.
-            return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue);
-        case kMirror_TileMode:
-            // t % 2  (synthetic mirror intervals expand the range to [0..2)
-            // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2.
-            return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue);
-        }
-
-        SK_ABORT("Unhandled tile mode.");
-        return 0;
-    }
-
-    Sk4f lerp(SkScalar t) {
-        SkASSERT(fInterval->contains(t));
-        return fCc + fDc * (t - fInterval->fP0);
-    }
-
-    void loadIntervalData(const Sk4fGradientInterval* i) {
-        fCc = DstTraits<dstType, premul>::load(i->fC0);
-        fDc = DstTraits<dstType, premul>::load(i->fDc);
-    }
-
-    const GradientShaderBase4fContext& fCtx;
-    const Sk4fGradientInterval*        fInterval;
-    SkScalar                           fPrevT;
-    SkScalar                           fLargestIntervalValue;
-    Sk4f                               fCc;
-    Sk4f                               fDc;
-};
diff --git a/src/effects/gradients/Sk4fGradientBase.h b/src/effects/gradients/Sk4fGradientBase.h
deleted file mode 100644
index 2826f9e..0000000
--- a/src/effects/gradients/Sk4fGradientBase.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef Sk4fGradientBase_DEFINED
-#define Sk4fGradientBase_DEFINED
-
-#include "Sk4fGradientPriv.h"
-#include "SkColor.h"
-#include "SkGradientShaderPriv.h"
-#include "SkMatrix.h"
-#include "SkNx.h"
-#include "SkPM4f.h"
-#include "SkShader.h"
-#include "SkTArray.h"
-
-struct Sk4fGradientInterval {
-    Sk4fGradientInterval(const Sk4f& c0, SkScalar p0,
-                         const Sk4f& c1, SkScalar p1);
-
-    bool contains(SkScalar t) const {
-        // True if t is in [p0,p1].  Note: this helper assumes a
-        // natural/increasing interval - so it's not usable in Sk4fLinearGradient.
-        SkASSERT(fP0 < fP1);
-        return t >= fP0 && t <= fP1;
-    }
-
-    SkPM4f   fC0, fDc;
-    SkScalar fP0, fP1;
-    bool     fZeroRamp;
-};
-
-class Sk4fGradientIntervalBuffer {
-public:
-    void init(const SkColor colors[], const SkScalar pos[], int count,
-              SkShader::TileMode tileMode, bool premulColors, SkScalar alpha, bool reverse);
-
-    const Sk4fGradientInterval* find(SkScalar t) const;
-    const Sk4fGradientInterval* findNext(SkScalar t, const Sk4fGradientInterval* prev,
-                                         bool increasing) const;
-
-    using BufferType = SkSTArray<8, Sk4fGradientInterval, true>;
-
-    const BufferType* operator->() const { return &fIntervals; }
-
-private:
-    BufferType fIntervals;
-};
-
-class SkGradientShaderBase::
-GradientShaderBase4fContext : public SkShader::Context {
-public:
-    GradientShaderBase4fContext(const SkGradientShaderBase&,
-                                const ContextRec&);
-
-    uint32_t getFlags() const override { return fFlags; }
-
-    void shadeSpan(int x, int y, SkPMColor dst[], int count) override;
-    void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override;
-
-    bool isValid() const;
-
-protected:
-    virtual void mapTs(int x, int y, SkScalar ts[], int count) const = 0;
-
-    Sk4fGradientIntervalBuffer fIntervals;
-    SkMatrix                   fDstToPos;
-    SkMatrix::MapXYProc        fDstToPosProc;
-    uint8_t                    fDstToPosClass;
-    uint8_t                    fFlags;
-    bool                       fDither;
-    bool                       fColorsArePremul;
-
-private:
-    using INHERITED = SkShader::Context;
-
-    void addMirrorIntervals(const SkGradientShaderBase&,
-                            const Sk4f& componentScale, bool reverse);
-
-    template<DstType, ApplyPremul, SkShader::TileMode tileMode>
-    class TSampler;
-
-    template <DstType dstType, ApplyPremul premul>
-    void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[],
-                         int count) const;
-
-    template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
-    void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[],
-                           int count) const;
-};
-
-#endif // Sk4fGradientBase_DEFINED
diff --git a/src/effects/gradients/Sk4fGradientPriv.h b/src/effects/gradients/Sk4fGradientPriv.h
deleted file mode 100644
index b8f4bca..0000000
--- a/src/effects/gradients/Sk4fGradientPriv.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef Sk4fGradientPriv_DEFINED
-#define Sk4fGradientPriv_DEFINED
-
-#include "SkColor.h"
-#include "SkHalf.h"
-#include "SkImageInfo.h"
-#include "SkNx.h"
-#include "SkPM4f.h"
-#include "SkPM4fPriv.h"
-#include "SkUtils.h"
-
-// Templates shared by various 4f gradient flavors.
-
-namespace {
-
-enum class ApplyPremul { True, False };
-
-enum class DstType {
-    L32,  // Linear 32bit.  Used for both shader/blitter paths.
-    S32,  // SRGB 32bit.  Used for the blitter path only.
-    F16,  // Linear half-float.  Used for blitters only.
-    F32,  // Linear float.  Used for shaders only.
-};
-
-template <ApplyPremul>
-struct PremulTraits;
-
-template <>
-struct PremulTraits<ApplyPremul::False> {
-    static Sk4f apply(const Sk4f& c) { return c; }
-};
-
-template <>
-struct PremulTraits<ApplyPremul::True> {
-    static Sk4f apply(const Sk4f& c) {
-        const float alpha = c[SkPM4f::A];
-        // FIXME: portable swizzle?
-        return c * Sk4f(alpha, alpha, alpha, 1);
-    }
-};
-
-// Struct encapsulating various dest-dependent ops:
-//
-//   - load()       Load a SkPM4f value into Sk4f.  Normally called once per interval
-//                  advance.  Also applies a scale and swizzle suitable for DstType.
-//
-//   - store()      Store one Sk4f to dest.  Optionally handles premul, color space
-//                  conversion, etc.
-//
-//   - store(count) Store the Sk4f value repeatedly to dest, count times.
-//
-//   - store4x()    Store 4 Sk4f values to dest (opportunistic optimization).
-//
-template <DstType, ApplyPremul premul>
-struct DstTraits;
-
-template <ApplyPremul premul>
-struct DstTraits<DstType::L32, premul> {
-    using PM   = PremulTraits<premul>;
-    using Type = SkPMColor;
-
-    // For L32, prescaling by 255 saves a per-pixel multiplication when premul is not needed.
-    static Sk4f load(const SkPM4f& c) {
-        return premul == ApplyPremul::False
-            ? c.to4f_pmorder() * Sk4f(255)
-            : c.to4f_pmorder();
-    }
-
-    static void store(const Sk4f& c, Type* dst) {
-        if (premul == ApplyPremul::False) {
-            // c is prescaled by 255, just store.
-            SkNx_cast<uint8_t>(c).store(dst);
-        } else {
-            *dst = Sk4f_toL32(PM::apply(c));
-        }
-    }
-
-    static void store(const Sk4f& c, Type* dst, int n) {
-        Type pmc;
-        store(c, &pmc);
-        sk_memset32(dst, pmc, n);
-    }
-
-    static void store4x(const Sk4f& c0, const Sk4f& c1,
-                        const Sk4f& c2, const Sk4f& c3,
-                        Type* dst) {
-        if (premul == ApplyPremul::False) {
-            Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3);
-        } else {
-            store(c0, dst + 0);
-            store(c1, dst + 1);
-            store(c2, dst + 2);
-            store(c3, dst + 3);
-        }
-    }
-};
-
-template <ApplyPremul premul>
-struct DstTraits<DstType::S32, premul> {
-    using PM   = PremulTraits<premul>;
-    using Type = SkPMColor;
-
-    static Sk4f load(const SkPM4f& c) {
-        return c.to4f_pmorder();
-    }
-
-    static void store(const Sk4f& c, Type* dst) {
-        // FIXME: this assumes opaque colors.  Handle unpremultiplication.
-        *dst = Sk4f_toS32(PM::apply(c));
-    }
-
-    static void store(const Sk4f& c, Type* dst, int n) {
-        sk_memset32(dst, Sk4f_toS32(PM::apply(c)), n);
-    }
-
-    static void store4x(const Sk4f& c0, const Sk4f& c1,
-                        const Sk4f& c2, const Sk4f& c3,
-                        Type* dst) {
-        store(c0, dst + 0);
-        store(c1, dst + 1);
-        store(c2, dst + 2);
-        store(c3, dst + 3);
-    }
-};
-
-template <ApplyPremul premul>
-struct DstTraits<DstType::F16, premul> {
-    using PM   = PremulTraits<premul>;
-    using Type = uint64_t;
-
-    static Sk4f load(const SkPM4f& c) {
-        return c.to4f();
-    }
-
-    static void store(const Sk4f& c, Type* dst) {
-        SkFloatToHalf_finite_ftz(PM::apply(c)).store(dst);
-    }
-
-    static void store(const Sk4f& c, Type* dst, int n) {
-        uint64_t color;
-        SkFloatToHalf_finite_ftz(PM::apply(c)).store(&color);
-        sk_memset64(dst, color, n);
-    }
-
-    static void store4x(const Sk4f& c0, const Sk4f& c1,
-                        const Sk4f& c2, const Sk4f& c3,
-                        Type* dst) {
-        store(c0, dst + 0);
-        store(c1, dst + 1);
-        store(c2, dst + 2);
-        store(c3, dst + 3);
-    }
-};
-
-template <ApplyPremul premul>
-struct DstTraits<DstType::F32, premul> {
-    using PM   = PremulTraits<premul>;
-    using Type = SkPM4f;
-
-    static Sk4f load(const SkPM4f& c) {
-        return c.to4f();
-    }
-
-    static void store(const Sk4f& c, Type* dst) {
-        PM::apply(c).store(dst->fVec);
-    }
-
-    static void store(const Sk4f& c, Type* dst, int n) {
-        const Sk4f pmc = PM::apply(c);
-        for (int i = 0; i < n; ++i) {
-            pmc.store(dst[i].fVec);
-        }
-    }
-
-    static void store4x(const Sk4f& c0, const Sk4f& c1,
-                        const Sk4f& c2, const Sk4f& c3,
-                        Type* dst) {
-        store(c0, dst + 0);
-        store(c1, dst + 1);
-        store(c2, dst + 2);
-        store(c3, dst + 3);
-    }
-};
-
-} // anonymous namespace
-
-#endif // Sk4fGradientPriv_DEFINED
diff --git a/src/effects/gradients/Sk4fLinearGradient.cpp b/src/effects/gradients/Sk4fLinearGradient.cpp
deleted file mode 100644
index 1256b8f..0000000
--- a/src/effects/gradients/Sk4fLinearGradient.cpp
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * 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 "Sk4fLinearGradient.h"
-#include "Sk4x4f.h"
-
-#include <cmath>
-
-namespace {
-
-template<DstType dstType, ApplyPremul premul>
-void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) {
-    SkASSERT(n > 0);
-
-    const Sk4f dc2 = dc + dc;
-    const Sk4f dc4 = dc2 + dc2;
-
-    Sk4f c0 = c ;
-    Sk4f c1 = c + dc;
-    Sk4f c2 = c0 + dc2;
-    Sk4f c3 = c1 + dc2;
-
-    while (n >= 4) {
-        DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst);
-        dst += 4;
-
-        c0 = c0 + dc4;
-        c1 = c1 + dc4;
-        c2 = c2 + dc4;
-        c3 = c3 + dc4;
-        n -= 4;
-    }
-    if (n & 2) {
-        DstTraits<dstType, premul>::store(c0, dst++);
-        DstTraits<dstType, premul>::store(c1, dst++);
-        c0 = c0 + dc2;
-    }
-    if (n & 1) {
-        DstTraits<dstType, premul>::store(c0, dst);
-    }
-}
-
-// Planar version of ramp (S32 no-premul only).
-template<>
-void ramp<DstType::S32, ApplyPremul::False>(const Sk4f& c, const Sk4f& dc, SkPMColor dst[], int n) {
-    SkASSERT(n > 0);
-
-    const Sk4f    dc4 = dc * 4;
-    const Sk4x4f dc4x = { Sk4f(dc4[0]), Sk4f(dc4[1]), Sk4f(dc4[2]), Sk4f(dc4[3]) };
-    Sk4x4f        c4x = Sk4x4f::Transpose(c, c + dc, c + dc * 2, c + dc * 3);
-
-    while (n >= 4) {
-        ( sk_linear_to_srgb(c4x.r) <<  0
-        | sk_linear_to_srgb(c4x.g) <<  8
-        | sk_linear_to_srgb(c4x.b) << 16
-        | Sk4f_round(255.0f*c4x.a) << 24).store(dst);
-
-        c4x.r += dc4x.r;
-        c4x.g += dc4x.g;
-        c4x.b += dc4x.b;
-        c4x.a += dc4x.a;
-
-        dst += 4;
-        n   -= 4;
-    }
-
-    if (n & 2) {
-        DstTraits<DstType::S32, ApplyPremul::False>
-            ::store(Sk4f(c4x.r[0], c4x.g[0], c4x.b[0], c4x.a[0]), dst++);
-        DstTraits<DstType::S32, ApplyPremul::False>
-            ::store(Sk4f(c4x.r[1], c4x.g[1], c4x.b[1], c4x.a[1]), dst++);
-    }
-
-    if (n & 1) {
-        DstTraits<DstType::S32, ApplyPremul::False>
-            ::store(Sk4f(c4x.r[n & 2], c4x.g[n & 2], c4x.b[n & 2], c4x.a[n & 2]), dst);
-    }
-}
-
-template<SkShader::TileMode>
-SkScalar pinFx(SkScalar);
-
-template<>
-SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
-    return fx;
-}
-
-template<>
-SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
-    SkScalar f = SkScalarFraction(fx);
-    if (f < 0) {
-        f = SkTMin(f + 1, nextafterf(1, 0));
-    }
-    SkASSERT(f >= 0);
-    SkASSERT(f < 1.0f);
-    return f;
-}
-
-template<>
-SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
-    SkScalar f = SkScalarMod(fx, 2.0f);
-    if (f < 0) {
-        f = SkTMin(f + 2, nextafterf(2, 0));
-    }
-    SkASSERT(f >= 0);
-    SkASSERT(f < 2.0f);
-    return f;
-}
-
-// true when x is in [k1,k2], or [k2, k1] when the interval is reversed.
-// TODO(fmalita): hoist the reversed interval check out of this helper.
-bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
-    SkASSERT(k1 != k2);
-    return (k1 < k2)
-        ? (x >= k1 && x <= k2)
-        : (x >= k2 && x <= k1);
-}
-
-} // anonymous namespace
-
-SkLinearGradient::
-LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
-                                                 const ContextRec& rec)
-    : INHERITED(shader, rec) {
-
-    // Our fast path expects interval points to be monotonically increasing in x.
-    const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX());
-    fIntervals.init(shader.fOrigColors, shader.fOrigPos, shader.fColorCount, shader.fTileMode,
-                    fColorsArePremul, rec.fPaint->getAlpha() * (1.0f / 255), reverseIntervals);
-
-    SkASSERT(fIntervals->count() > 0);
-    fCachedInterval = fIntervals->begin();
-}
-
-const Sk4fGradientInterval*
-SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
-    SkASSERT(in_range(fx, fIntervals->front().fP0, fIntervals->back().fP1));
-
-    if (1) {
-        // Linear search, using the last scanline interval as a starting point.
-        SkASSERT(fCachedInterval >= fIntervals->begin());
-        SkASSERT(fCachedInterval < fIntervals->end());
-        const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
-        while (!in_range(fx, fCachedInterval->fP0, fCachedInterval->fP1)) {
-            fCachedInterval += search_dir;
-            if (fCachedInterval >= fIntervals->end()) {
-                fCachedInterval = fIntervals->begin();
-            } else if (fCachedInterval < fIntervals->begin()) {
-                fCachedInterval = fIntervals->end() - 1;
-            }
-        }
-        return fCachedInterval;
-    } else {
-        // Binary search.  Seems less effective than linear + caching.
-        const auto* i0 = fIntervals->begin();
-        const auto* i1 = fIntervals->end() - 1;
-
-        while (i0 != i1) {
-            SkASSERT(i0 < i1);
-            SkASSERT(in_range(fx, i0->fP0, i1->fP1));
-
-            const auto* i = i0 + ((i1 - i0) >> 1);
-
-            if (in_range(fx, i0->fP0, i->fP1)) {
-                i1 = i;
-            } else {
-                SkASSERT(in_range(fx, i->fP1, i1->fP1));
-                i0 = i + 1;
-            }
-        }
-
-        SkASSERT(in_range(fx, i0->fP0, i0->fP1));
-        return i0;
-    }
-}
-
-void SkLinearGradient::
-LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
-    if (!this->isFast()) {
-        this->INHERITED::shadeSpan(x, y, dst, count);
-        return;
-    }
-
-    // TODO: plumb dithering
-    SkASSERT(count > 0);
-    if (fColorsArePremul) {
-        this->shadePremulSpan<DstType::L32,
-                              ApplyPremul::False>(x, y, dst, count);
-    } else {
-        this->shadePremulSpan<DstType::L32,
-                              ApplyPremul::True>(x, y, dst, count);
-    }
-}
-
-void SkLinearGradient::
-LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
-    if (!this->isFast()) {
-        this->INHERITED::shadeSpan4f(x, y, dst, count);
-        return;
-    }
-
-    // TONOTDO: plumb dithering
-    SkASSERT(count > 0);
-    if (fColorsArePremul) {
-        this->shadePremulSpan<DstType::F32,
-                              ApplyPremul::False>(x, y, dst, count);
-    } else {
-        this->shadePremulSpan<DstType::F32,
-                              ApplyPremul::True>(x, y, dst, count);
-    }
-}
-
-template<DstType dstType, ApplyPremul premul>
-void SkLinearGradient::
-LinearGradient4fContext::shadePremulSpan(int x, int y,
-                                         typename DstTraits<dstType, premul>::Type dst[],
-                                         int count) const {
-    const SkLinearGradient& shader =
-        static_cast<const SkLinearGradient&>(fShader);
-    switch (shader.fTileMode) {
-    case kClamp_TileMode:
-        this->shadeSpanInternal<dstType,
-                                premul,
-                                kClamp_TileMode>(x, y, dst, count);
-        break;
-    case kRepeat_TileMode:
-        this->shadeSpanInternal<dstType,
-                                premul,
-                                kRepeat_TileMode>(x, y, dst, count);
-        break;
-    case kMirror_TileMode:
-        this->shadeSpanInternal<dstType,
-                                premul,
-                                kMirror_TileMode>(x, y, dst, count);
-        break;
-    }
-}
-
-template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
-void SkLinearGradient::
-LinearGradient4fContext::shadeSpanInternal(int x, int y,
-                                           typename DstTraits<dstType, premul>::Type dst[],
-                                           int count) const {
-    SkPoint pt;
-    fDstToPosProc(fDstToPos,
-                  x + SK_ScalarHalf,
-                  y + SK_ScalarHalf,
-                  &pt);
-    const SkScalar fx = pinFx<tileMode>(pt.x());
-    const SkScalar dx = fDstToPos.getScaleX();
-    LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(),
-                                                            fIntervals->end() - 1,
-                                                            this->findInterval(fx),
-                                                            fx,
-                                                            dx,
-                                                            SkScalarNearlyZero(dx * count));
-    while (count > 0) {
-        // What we really want here is SkTPin(advance, 1, count)
-        // but that's a significant perf hit for >> stops; investigate.
-        const int n = SkScalarTruncToInt(
-            SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
-
-        // The current interval advance can be +inf (e.g. when reaching
-        // the clamp mode end intervals) - when that happens, we expect to
-        //   a) consume all remaining count in one swoop
-        //   b) return a zero color gradient
-        SkASSERT(SkScalarIsFinite(proc.currentAdvance())
-            || (n == count && proc.currentRampIsZero()));
-
-        if (proc.currentRampIsZero()) {
-            DstTraits<dstType, premul>::store(proc.currentColor(),
-                                              dst, n);
-        } else {
-            ramp<dstType, premul>(proc.currentColor(),
-                                  proc.currentColorGrad(),
-                                  dst, n);
-        }
-
-        proc.advance(SkIntToScalar(n));
-        count -= n;
-        dst   += n;
-    }
-}
-
-template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
-class SkLinearGradient::
-LinearGradient4fContext::LinearIntervalProcessor {
-public:
-    LinearIntervalProcessor(const Sk4fGradientInterval* firstInterval,
-                            const Sk4fGradientInterval* lastInterval,
-                            const Sk4fGradientInterval* i,
-                            SkScalar fx,
-                            SkScalar dx,
-                            bool is_vertical)
-        : fAdvX(is_vertical ? SK_ScalarInfinity : (i->fP1 - fx) / dx)
-        , fFirstInterval(firstInterval)
-        , fLastInterval(lastInterval)
-        , fInterval(i)
-        , fDx(dx)
-        , fIsVertical(is_vertical)
-    {
-        SkASSERT(fAdvX >= 0);
-        SkASSERT(firstInterval <= lastInterval);
-        SkASSERT(in_range(fx, i->fP0, i->fP1));
-
-        if (tileMode != kClamp_TileMode && !is_vertical) {
-            const auto spanX = (lastInterval->fP1 - firstInterval->fP0) / dx;
-            SkASSERT(spanX >= 0);
-
-            // If we're in a repeating tile mode and the whole gradient is compressed into a
-            // fraction of a pixel, we just use the average color in zero-ramp mode.
-            // This also avoids cases where we make no progress due to interval advances being
-            // close to zero.
-            static constexpr SkScalar kMinSpanX = .25f;
-            if (spanX < kMinSpanX) {
-                this->init_average_props();
-                return;
-            }
-        }
-
-        this->compute_interval_props(fx - i->fP0);
-    }
-
-    SkScalar currentAdvance() const {
-        SkASSERT(fAdvX >= 0);
-        SkASSERT(fAdvX <= (fInterval->fP1 - fInterval->fP0) / fDx || !std::isfinite(fAdvX));
-        return fAdvX;
-    }
-
-    bool currentRampIsZero() const { return fZeroRamp; }
-    const Sk4f& currentColor() const { return fCc; }
-    const Sk4f& currentColorGrad() const { return fDcDx; }
-
-    void advance(SkScalar advX) {
-        SkASSERT(advX > 0);
-        SkASSERT(fAdvX >= 0);
-
-        if (advX >= fAdvX) {
-            advX = this->advance_interval(advX);
-        }
-        SkASSERT(advX < fAdvX);
-
-        fCc = fCc + fDcDx * Sk4f(advX);
-        fAdvX -= advX;
-    }
-
-private:
-    void compute_interval_props(SkScalar t) {
-        fZeroRamp     = fIsVertical || fInterval->fZeroRamp;
-        fCc           = DstTraits<dstType, premul>::load(fInterval->fC0);
-
-        if (fInterval->fZeroRamp) {
-            fDcDx = 0;
-        } else {
-            const Sk4f dC = DstTraits<dstType, premul>::load(fInterval->fDc);
-            fCc           = fCc + dC * Sk4f(t);
-            fDcDx         = dC * fDx;
-        }
-    }
-
-    void init_average_props() {
-        fAdvX     = SK_ScalarInfinity;
-        fZeroRamp = true;
-        fDcDx     = 0;
-        fCc       = Sk4f(0);
-
-        // TODO: precompute the average at interval setup time?
-        for (const auto* i = fFirstInterval; i <= fLastInterval; ++i) {
-            // Each interval contributes its average color to the total/weighted average:
-            //
-            //   C = (c0 + c1) / 2 = (c0 + c0 + dc * (p1 - p0)) / 2
-            //
-            //   Avg += C * (p1 - p0)
-            //
-            const auto dp = i->fP1 - i->fP0;
-            auto c = DstTraits<dstType, premul>::load(i->fC0);
-            if (!i->fZeroRamp) {
-                c = c + DstTraits<dstType, premul>::load(i->fDc) * dp * 0.5f;
-            }
-            fCc = fCc + c * dp;
-        }
-    }
-
-    const Sk4fGradientInterval* next_interval(const Sk4fGradientInterval* i) const {
-        SkASSERT(i >= fFirstInterval);
-        SkASSERT(i <= fLastInterval);
-        i++;
-
-        if (tileMode == kClamp_TileMode) {
-            SkASSERT(i <= fLastInterval);
-            return i;
-        }
-
-        return (i <= fLastInterval) ? i : fFirstInterval;
-    }
-
-    SkScalar advance_interval(SkScalar advX) {
-        SkASSERT(advX >= fAdvX);
-
-        do {
-            advX -= fAdvX;
-            fInterval = this->next_interval(fInterval);
-            fAdvX = (fInterval->fP1 - fInterval->fP0) / fDx;
-            SkASSERT(fAdvX > 0);
-        } while (advX >= fAdvX);
-
-        compute_interval_props(0);
-
-        SkASSERT(advX >= 0);
-        return advX;
-    }
-
-    // Current interval properties.
-    Sk4f            fDcDx;      // dst color gradient (dc/dx)
-    Sk4f            fCc;        // current color, interpolated in dst
-    SkScalar        fAdvX;      // remaining interval advance in dst
-    bool            fZeroRamp;  // current interval color grad is 0
-
-    const Sk4fGradientInterval* fFirstInterval;
-    const Sk4fGradientInterval* fLastInterval;
-    const Sk4fGradientInterval* fInterval;  // current interval
-    const SkScalar              fDx;        // 'dx' for consistency with other impls; actually dt/dx
-    const bool                  fIsVertical;
-};
-
-void SkLinearGradient::
-LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
-    SkASSERT(count > 0);
-    SkASSERT(fDstToPosClass != kLinear_MatrixClass);
-
-    SkScalar sx = x + SK_ScalarHalf;
-    const SkScalar sy = y + SK_ScalarHalf;
-    SkPoint pt;
-
-    if (fDstToPosClass != kPerspective_MatrixClass) {
-        // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
-        const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
-        fDstToPosProc(fDstToPos, sx, sy, &pt);
-
-        const Sk4f dtdx4 = Sk4f(4 * dtdx);
-        Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
-                       pt.x() + 1 * dtdx,
-                       pt.x() + 2 * dtdx,
-                       pt.x() + 3 * dtdx);
-
-        while (count >= 4) {
-            t4.store(ts);
-            t4 = t4 + dtdx4;
-            ts += 4;
-            count -= 4;
-        }
-
-        if (count & 2) {
-            *ts++ = t4[0];
-            *ts++ = t4[1];
-            t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
-        }
-
-        if (count & 1) {
-            *ts++ = t4[0];
-        }
-    } else {
-        for (int i = 0; i < count; ++i) {
-            fDstToPosProc(fDstToPos, sx, sy, &pt);
-            // Perspective may yield NaN values.
-            // Short of a better idea, drop to 0.
-            ts[i] = SkScalarIsNaN(pt.x()) ? 0 : pt.x();
-            sx += SK_Scalar1;
-        }
-    }
-}
-
-bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info,
-                                                                  BlitState* state) {
-    if (state->fMode != SkBlendMode::kSrc &&
-        !(state->fMode == SkBlendMode::kSrcOver && (fFlags & kOpaqueAlpha_Flag))) {
-        return false;
-    }
-
-    switch (info.colorType()) {
-        case kN32_SkColorType:
-            state->fBlitBW = D32_BlitBW;
-            return true;
-        case kRGBA_F16_SkColorType:
-            state->fBlitBW = D64_BlitBW;
-            return true;
-        default:
-            return false;
-    }
-}
-
-void SkLinearGradient::
-LinearGradient4fContext::D32_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
-                                    int count) {
-    // FIXME: ignoring coverage for now
-    const LinearGradient4fContext* ctx =
-        static_cast<const LinearGradient4fContext*>(state->fCtx);
-
-    if (!dst.info().gammaCloseToSRGB()) {
-        if (ctx->fColorsArePremul) {
-            ctx->shadePremulSpan<DstType::L32, ApplyPremul::False>(
-                x, y, dst.writable_addr32(x, y), count);
-        } else {
-            ctx->shadePremulSpan<DstType::L32, ApplyPremul::True>(
-                x, y, dst.writable_addr32(x, y), count);
-        }
-    } else {
-        if (ctx->fColorsArePremul) {
-            ctx->shadePremulSpan<DstType::S32, ApplyPremul::False>(
-                x, y, dst.writable_addr32(x, y), count);
-        } else {
-            ctx->shadePremulSpan<DstType::S32, ApplyPremul::True>(
-                x, y, dst.writable_addr32(x, y), count);
-        }
-    }
-}
-
-void SkLinearGradient::
-LinearGradient4fContext::D64_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst,
-                                    int count) {
-    // FIXME: ignoring coverage for now
-    const LinearGradient4fContext* ctx =
-        static_cast<const LinearGradient4fContext*>(state->fCtx);
-
-    if (ctx->fColorsArePremul) {
-        ctx->shadePremulSpan<DstType::F16, ApplyPremul::False>(
-            x, y, dst.writable_addr64(x, y), count);
-    } else {
-        ctx->shadePremulSpan<DstType::F16, ApplyPremul::True>(
-            x, y, dst.writable_addr64(x, y), count);
-    }
-}
diff --git a/src/effects/gradients/Sk4fLinearGradient.h b/src/effects/gradients/Sk4fLinearGradient.h
deleted file mode 100644
index eebd30f..0000000
--- a/src/effects/gradients/Sk4fLinearGradient.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef Sk4fLinearGradient_DEFINED
-#define Sk4fLinearGradient_DEFINED
-
-#include "Sk4fGradientBase.h"
-#include "SkLinearGradient.h"
-
-class SkLinearGradient::
-LinearGradient4fContext final : public GradientShaderBase4fContext {
-public:
-    LinearGradient4fContext(const SkLinearGradient&, const ContextRec&);
-
-    void shadeSpan(int x, int y, SkPMColor dst[], int count) override;
-    void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override;
-
-protected:
-    void mapTs(int x, int y, SkScalar ts[], int count) const override;
-
-    bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override;
-
-private:
-    using INHERITED = GradientShaderBase4fContext;
-
-    template<DstType, ApplyPremul, TileMode>
-    class LinearIntervalProcessor;
-
-    template <DstType dstType, ApplyPremul premul>
-    void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[],
-                         int count) const;
-
-    template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
-    void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[],
-                           int count) const;
-
-    const Sk4fGradientInterval* findInterval(SkScalar fx) const;
-
-    bool isFast() const { return fDstToPosClass == kLinear_MatrixClass; }
-
-    static void D32_BlitBW(BlitState*, int x, int y, const SkPixmap& dst, int count);
-    static void D64_BlitBW(BlitState*, int x, int y, const SkPixmap& dst, int count);
-
-    mutable const Sk4fGradientInterval* fCachedInterval;
-};
-
-#endif // Sk4fLinearGradient_DEFINED
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
deleted file mode 100644
index 0840d7c..0000000
--- a/src/effects/gradients/SkGradientShader.cpp
+++ /dev/null
@@ -1,1820 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Sk4fLinearGradient.h"
-#include "SkColorSpace_XYZ.h"
-#include "SkGradientShaderPriv.h"
-#include "SkHalf.h"
-#include "SkLinearGradient.h"
-#include "SkRadialGradient.h"
-#include "SkTwoPointConicalGradient.h"
-#include "SkSweepGradient.h"
-
-enum GradientSerializationFlags {
-    // Bits 29:31 used for various boolean flags
-    kHasPosition_GSF    = 0x80000000,
-    kHasLocalMatrix_GSF = 0x40000000,
-    kHasColorSpace_GSF  = 0x20000000,
-
-    // Bits 12:28 unused
-
-    // Bits 8:11 for fTileMode
-    kTileModeShift_GSF  = 8,
-    kTileModeMask_GSF   = 0xF,
-
-    // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
-    kGradFlagsShift_GSF = 0,
-    kGradFlagsMask_GSF  = 0xFF,
-};
-
-void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
-    uint32_t flags = 0;
-    if (fPos) {
-        flags |= kHasPosition_GSF;
-    }
-    if (fLocalMatrix) {
-        flags |= kHasLocalMatrix_GSF;
-    }
-    sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
-    if (colorSpaceData) {
-        flags |= kHasColorSpace_GSF;
-    }
-    SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
-    flags |= (fTileMode << kTileModeShift_GSF);
-    SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
-    flags |= (fGradFlags << kGradFlagsShift_GSF);
-
-    buffer.writeUInt(flags);
-
-    buffer.writeColor4fArray(fColors, fCount);
-    if (colorSpaceData) {
-        buffer.writeDataAsByteArray(colorSpaceData.get());
-    }
-    if (fPos) {
-        buffer.writeScalarArray(fPos, fCount);
-    }
-    if (fLocalMatrix) {
-        buffer.writeMatrix(*fLocalMatrix);
-    }
-}
-
-bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
-    if (buffer.isVersionLT(SkReadBuffer::kGradientShaderFloatColor_Version)) {
-        fCount = buffer.getArrayCount();
-        if (fCount > kStorageCount) {
-            size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
-            fDynamicStorage.reset(allocSize);
-            fColors = (SkColor4f*)fDynamicStorage.get();
-            fPos = (SkScalar*)(fColors + fCount);
-        } else {
-            fColors = fColorStorage;
-            fPos = fPosStorage;
-        }
-
-        // Old gradients serialized SkColor. Read that to a temporary location, then convert.
-        SkSTArray<2, SkColor, true> colors;
-        colors.resize_back(fCount);
-        if (!buffer.readColorArray(colors.begin(), fCount)) {
-            return false;
-        }
-        for (int i = 0; i < fCount; ++i) {
-            mutableColors()[i] = SkColor4f::FromColor(colors[i]);
-        }
-
-        if (buffer.readBool()) {
-            if (!buffer.readScalarArray(const_cast<SkScalar*>(fPos), fCount)) {
-                return false;
-            }
-        } else {
-            fPos = nullptr;
-        }
-
-        fColorSpace = nullptr;
-        fTileMode = (SkShader::TileMode)buffer.read32();
-        fGradFlags = buffer.read32();
-
-        if (buffer.readBool()) {
-            fLocalMatrix = &fLocalMatrixStorage;
-            buffer.readMatrix(&fLocalMatrixStorage);
-        } else {
-            fLocalMatrix = nullptr;
-        }
-    } else {
-        // New gradient format. Includes floating point color, color space, densely packed flags
-        uint32_t flags = buffer.readUInt();
-
-        fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
-        fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
-
-        fCount = buffer.getArrayCount();
-        if (fCount > kStorageCount) {
-            size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
-            fDynamicStorage.reset(allocSize);
-            fColors = (SkColor4f*)fDynamicStorage.get();
-            fPos = (SkScalar*)(fColors + fCount);
-        } else {
-            fColors = fColorStorage;
-            fPos = fPosStorage;
-        }
-        if (!buffer.readColor4fArray(mutableColors(), fCount)) {
-            return false;
-        }
-        if (SkToBool(flags & kHasColorSpace_GSF)) {
-            sk_sp<SkData> data = buffer.readByteArrayAsData();
-            fColorSpace = SkColorSpace::Deserialize(data->data(), data->size());
-        } else {
-            fColorSpace = nullptr;
-        }
-        if (SkToBool(flags & kHasPosition_GSF)) {
-            if (!buffer.readScalarArray(mutablePos(), fCount)) {
-                return false;
-            }
-        } else {
-            fPos = nullptr;
-        }
-        if (SkToBool(flags & kHasLocalMatrix_GSF)) {
-            fLocalMatrix = &fLocalMatrixStorage;
-            buffer.readMatrix(&fLocalMatrixStorage);
-        } else {
-            fLocalMatrix = nullptr;
-        }
-    }
-    return buffer.isValid();
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////
-
-SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
-    : INHERITED(desc.fLocalMatrix)
-    , fPtsToUnit(ptsToUnit)
-{
-    fPtsToUnit.getType();  // Precache so reads are threadsafe.
-    SkASSERT(desc.fCount > 1);
-
-    fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
-
-    SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount);
-    SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
-    fTileMode = desc.fTileMode;
-    fTileProc = gTileProcs[desc.fTileMode];
-
-    /*  Note: we let the caller skip the first and/or last position.
-        i.e. pos[0] = 0.3, pos[1] = 0.7
-        In these cases, we insert dummy entries to ensure that the final data
-        will be bracketed by [0, 1].
-        i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
-
-        Thus colorCount (the caller's value, and fColorCount (our value) may
-        differ by up to 2. In the above example:
-            colorCount = 2
-            fColorCount = 4
-     */
-    fColorCount = desc.fCount;
-    // check if we need to add in dummy start and/or end position/colors
-    bool dummyFirst = false;
-    bool dummyLast = false;
-    if (desc.fPos) {
-        dummyFirst = desc.fPos[0] != 0;
-        dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
-        fColorCount += dummyFirst + dummyLast;
-    }
-
-    if (fColorCount > kColorStorageCount) {
-        size_t size = sizeof(SkColor) + sizeof(SkColor4f) + sizeof(Rec);
-        if (desc.fPos) {
-            size += sizeof(SkScalar);
-        }
-        fOrigColors = reinterpret_cast<SkColor*>(sk_malloc_throw(size * fColorCount));
-    }
-    else {
-        fOrigColors = fStorage;
-    }
-
-    fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount);
-
-    // Now copy over the colors, adding the dummies as needed
-    SkColor4f* origColors = fOrigColors4f;
-    if (dummyFirst) {
-        *origColors++ = desc.fColors[0];
-    }
-    memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor4f));
-    if (dummyLast) {
-        origColors += desc.fCount;
-        *origColors = desc.fColors[desc.fCount - 1];
-    }
-
-    // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the
-    // source colors are not in sRGB gamut. We would need to do a gamut transformation, but
-    // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU
-    // support compiled in here. For the common case (sRGB colors), this does the right thing.
-    for (int i = 0; i < fColorCount; ++i) {
-        fOrigColors[i] = fOrigColors4f[i].toSkColor();
-    }
-
-    if (!desc.fColorSpace) {
-        // This happens if we were constructed from SkColors, so our colors are really sRGB
-        fColorSpace = SkColorSpace::MakeSRGBLinear();
-    } else {
-        // The color space refers to the float colors, so it must be linear gamma
-        SkASSERT(desc.fColorSpace->gammaIsLinear());
-        fColorSpace = desc.fColorSpace;
-    }
-
-    if (desc.fPos && fColorCount) {
-        fOrigPos = (SkScalar*)(fOrigColors4f + fColorCount);
-        fRecs = (Rec*)(fOrigPos + fColorCount);
-    } else {
-        fOrigPos = nullptr;
-        fRecs = (Rec*)(fOrigColors4f + fColorCount);
-    }
-
-    if (fColorCount > 2) {
-        Rec* recs = fRecs;
-        recs->fPos = 0;
-        //  recs->fScale = 0; // unused;
-        recs += 1;
-        if (desc.fPos) {
-            SkScalar* origPosPtr = fOrigPos;
-            *origPosPtr++ = 0;
-
-            /*  We need to convert the user's array of relative positions into
-                fixed-point positions and scale factors. We need these results
-                to be strictly monotonic (no two values equal or out of order).
-                Hence this complex loop that just jams a zero for the scale
-                value if it sees a segment out of order, and it assures that
-                we start at 0 and end at 1.0
-            */
-            SkScalar prev = 0;
-            int startIndex = dummyFirst ? 0 : 1;
-            int count = desc.fCount + dummyLast;
-            for (int i = startIndex; i < count; i++) {
-                // force the last value to be 1.0
-                SkScalar curr;
-                if (i == desc.fCount) {  // we're really at the dummyLast
-                    curr = 1;
-                } else {
-                    curr = SkScalarPin(desc.fPos[i], 0, 1);
-                }
-                *origPosPtr++ = curr;
-
-                recs->fPos = SkScalarToFixed(curr);
-                SkFixed diff = SkScalarToFixed(curr - prev);
-                if (diff > 0) {
-                    recs->fScale = (1 << 24) / diff;
-                } else {
-                    recs->fScale = 0; // ignore this segment
-                }
-                // get ready for the next value
-                prev = curr;
-                recs += 1;
-            }
-        } else {    // assume even distribution
-            fOrigPos = nullptr;
-
-            SkFixed dp = SK_Fixed1 / (desc.fCount - 1);
-            SkFixed p = dp;
-            SkFixed scale = (desc.fCount - 1) << 8;  // (1 << 24) / dp
-            for (int i = 1; i < desc.fCount - 1; i++) {
-                recs->fPos   = p;
-                recs->fScale = scale;
-                recs += 1;
-                p += dp;
-            }
-            recs->fPos = SK_Fixed1;
-            recs->fScale = scale;
-        }
-    } else if (desc.fPos) {
-        SkASSERT(2 == fColorCount);
-        fOrigPos[0] = SkScalarPin(desc.fPos[0], 0, 1);
-        fOrigPos[1] = SkScalarPin(desc.fPos[1], fOrigPos[0], 1);
-        if (0 == fOrigPos[0] && 1 == fOrigPos[1]) {
-            fOrigPos = nullptr;
-        }
-    }
-    this->initCommon();
-}
-
-SkGradientShaderBase::~SkGradientShaderBase() {
-    if (fOrigColors != fStorage) {
-        sk_free(fOrigColors);
-    }
-}
-
-void SkGradientShaderBase::initCommon() {
-    unsigned colorAlpha = 0xFF;
-    for (int i = 0; i < fColorCount; i++) {
-        colorAlpha &= SkColorGetA(fOrigColors[i]);
-    }
-    fColorsAreOpaque = colorAlpha == 0xFF;
-}
-
-void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
-    Descriptor desc;
-    desc.fColors = fOrigColors4f;
-    desc.fColorSpace = fColorSpace;
-    desc.fPos = fOrigPos;
-    desc.fCount = fColorCount;
-    desc.fTileMode = fTileMode;
-    desc.fGradFlags = fGradFlags;
-
-    const SkMatrix& m = this->getLocalMatrix();
-    desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
-    desc.flatten(buffer);
-}
-
-void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst,
-                                              SkColor* colorSrc, Rec* recSrc,
-                                              int count) {
-    SkAutoSTArray<8, SkColor> colorsTemp(count);
-    for (int i = 0; i < count; ++i) {
-        int offset = count - i - 1;
-        colorsTemp[i] = colorSrc[offset];
-    }
-    if (count > 2) {
-        SkAutoSTArray<8, Rec> recsTemp(count);
-        for (int i = 0; i < count; ++i) {
-            int offset = count - i - 1;
-            recsTemp[i].fPos = SK_Fixed1 - recSrc[offset].fPos;
-            recsTemp[i].fScale = recSrc[offset].fScale;
-        }
-        memcpy(recDst, recsTemp.get(), count * sizeof(Rec));
-    }
-    memcpy(colorDst, colorsTemp.get(), count * sizeof(SkColor));
-}
-
-bool SkGradientShaderBase::isOpaque() const {
-    return fColorsAreOpaque;
-}
-
-static unsigned rounded_divide(unsigned numer, unsigned denom) {
-    return (numer + (denom >> 1)) / denom;
-}
-
-bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
-    // we just compute an average color.
-    // possibly we could weight this based on the proportional width for each color
-    //   assuming they are not evenly distributed in the fPos array.
-    int r = 0;
-    int g = 0;
-    int b = 0;
-    const int n = fColorCount;
-    for (int i = 0; i < n; ++i) {
-        SkColor c = fOrigColors[i];
-        r += SkColorGetR(c);
-        g += SkColorGetG(c);
-        b += SkColorGetB(c);
-    }
-    *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
-    return true;
-}
-
-SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext(
-        const SkGradientShaderBase& shader, const ContextRec& rec)
-    : INHERITED(shader, rec)
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
-    , fDither(true)
-#else
-    , fDither(rec.fPaint->isDither())
-#endif
-    , fCache(shader.refCache(getPaintAlpha(), fDither))
-{
-    const SkMatrix& inverse = this->getTotalInverse();
-
-    fDstToIndex.setConcat(shader.fPtsToUnit, inverse);
-
-    fDstToIndexProc = fDstToIndex.getMapXYProc();
-    fDstToIndexClass = (uint8_t)SkShader::Context::ComputeMatrixClass(fDstToIndex);
-
-    // now convert our colors in to PMColors
-    unsigned paintAlpha = this->getPaintAlpha();
-
-    fFlags = this->INHERITED::getFlags();
-    if (shader.fColorsAreOpaque && paintAlpha == 0xFF) {
-        fFlags |= kOpaqueAlpha_Flag;
-    }
-}
-
-bool SkGradientShaderBase::GradientShaderBaseContext::isValid() const {
-    return fDstToIndex.isFinite();
-}
-
-SkGradientShaderBase::GradientShaderCache::GradientShaderCache(
-        U8CPU alpha, bool dither, const SkGradientShaderBase& shader)
-    : fCacheAlpha(alpha)
-    , fCacheDither(dither)
-    , fShader(shader)
-{
-    // Only initialize the cache in getCache32.
-    fCache32 = nullptr;
-    fCache32PixelRef = nullptr;
-}
-
-SkGradientShaderBase::GradientShaderCache::~GradientShaderCache() {
-    SkSafeUnref(fCache32PixelRef);
-}
-
-/*
- *  r,g,b used to be SkFixed, but on gcc (4.2.1 mac and 4.6.3 goobuntu) in
- *  release builds, we saw a compiler error where the 0xFF parameter in
- *  SkPackARGB32() was being totally ignored whenever it was called with
- *  a non-zero add (e.g. 0x8000).
- *
- *  We found two work-arounds:
- *      1. change r,g,b to unsigned (or just one of them)
- *      2. change SkPackARGB32 to + its (a << SK_A32_SHIFT) value instead
- *         of using |
- *
- *  We chose #1 just because it was more localized.
- *  See http://code.google.com/p/skia/issues/detail?id=1113
- *
- *  The type SkUFixed encapsulate this need for unsigned, but logically Fixed.
- */
-typedef uint32_t SkUFixed;
-
-void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
-        SkPMColor cache[], SkColor c0, SkColor c1,
-        int count, U8CPU paintAlpha, uint32_t gradFlags, bool dither) {
-    SkASSERT(count > 1);
-
-    // need to apply paintAlpha to our two endpoints
-    uint32_t a0 = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
-    uint32_t a1 = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
-
-
-    const bool interpInPremul = SkToBool(gradFlags &
-                           SkGradientShader::kInterpolateColorsInPremul_Flag);
-
-    uint32_t r0 = SkColorGetR(c0);
-    uint32_t g0 = SkColorGetG(c0);
-    uint32_t b0 = SkColorGetB(c0);
-
-    uint32_t r1 = SkColorGetR(c1);
-    uint32_t g1 = SkColorGetG(c1);
-    uint32_t b1 = SkColorGetB(c1);
-
-    if (interpInPremul) {
-        r0 = SkMulDiv255Round(r0, a0);
-        g0 = SkMulDiv255Round(g0, a0);
-        b0 = SkMulDiv255Round(b0, a0);
-
-        r1 = SkMulDiv255Round(r1, a1);
-        g1 = SkMulDiv255Round(g1, a1);
-        b1 = SkMulDiv255Round(b1, a1);
-    }
-
-    SkFixed da = SkIntToFixed(a1 - a0) / (count - 1);
-    SkFixed dr = SkIntToFixed(r1 - r0) / (count - 1);
-    SkFixed dg = SkIntToFixed(g1 - g0) / (count - 1);
-    SkFixed db = SkIntToFixed(b1 - b0) / (count - 1);
-
-    /*  We pre-add 1/8 to avoid having to add this to our [0] value each time
-        in the loop. Without this, the bias for each would be
-            0x2000  0xA000  0xE000  0x6000
-        With this trick, we can add 0 for the first (no-op) and just adjust the
-        others.
-     */
-    const SkUFixed bias0 = dither ? 0x2000 : 0x8000;
-    const SkUFixed bias1 = dither ? 0x8000 : 0;
-    const SkUFixed bias2 = dither ? 0xC000 : 0;
-    const SkUFixed bias3 = dither ? 0x4000 : 0;
-
-    SkUFixed a = SkIntToFixed(a0) + bias0;
-    SkUFixed r = SkIntToFixed(r0) + bias0;
-    SkUFixed g = SkIntToFixed(g0) + bias0;
-    SkUFixed b = SkIntToFixed(b0) + bias0;
-
-    /*
-     *  Our dither-cell (spatially) is
-     *      0 2
-     *      3 1
-     *  Where
-     *      [0] -> [-1/8 ... 1/8 ) values near 0
-     *      [1] -> [ 1/8 ... 3/8 ) values near 1/4
-     *      [2] -> [ 3/8 ... 5/8 ) values near 1/2
-     *      [3] -> [ 5/8 ... 7/8 ) values near 3/4
-     */
-
-    if (0xFF == a0 && 0 == da) {
-        do {
-            cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0    ) >> 16,
-                                                        (g + 0    ) >> 16,
-                                                        (b + 0    ) >> 16);
-            cache[kCache32Count*1] = SkPackARGB32(0xFF, (r + bias1) >> 16,
-                                                        (g + bias1) >> 16,
-                                                        (b + bias1) >> 16);
-            cache[kCache32Count*2] = SkPackARGB32(0xFF, (r + bias2) >> 16,
-                                                        (g + bias2) >> 16,
-                                                        (b + bias2) >> 16);
-            cache[kCache32Count*3] = SkPackARGB32(0xFF, (r + bias3) >> 16,
-                                                        (g + bias3) >> 16,
-                                                        (b + bias3) >> 16);
-            cache += 1;
-            r += dr;
-            g += dg;
-            b += db;
-        } while (--count != 0);
-    } else if (interpInPremul) {
-        do {
-            cache[kCache32Count*0] = SkPackARGB32((a + 0    ) >> 16,
-                                                  (r + 0    ) >> 16,
-                                                  (g + 0    ) >> 16,
-                                                  (b + 0    ) >> 16);
-            cache[kCache32Count*1] = SkPackARGB32((a + bias1) >> 16,
-                                                  (r + bias1) >> 16,
-                                                  (g + bias1) >> 16,
-                                                  (b + bias1) >> 16);
-            cache[kCache32Count*2] = SkPackARGB32((a + bias2) >> 16,
-                                                  (r + bias2) >> 16,
-                                                  (g + bias2) >> 16,
-                                                  (b + bias2) >> 16);
-            cache[kCache32Count*3] = SkPackARGB32((a + bias3) >> 16,
-                                                  (r + bias3) >> 16,
-                                                  (g + bias3) >> 16,
-                                                  (b + bias3) >> 16);
-            cache += 1;
-            a += da;
-            r += dr;
-            g += dg;
-            b += db;
-        } while (--count != 0);
-    } else {    // interpolate in unpreml space
-        do {
-            cache[kCache32Count*0] = SkPremultiplyARGBInline((a + 0     ) >> 16,
-                                                             (r + 0     ) >> 16,
-                                                             (g + 0     ) >> 16,
-                                                             (b + 0     ) >> 16);
-            cache[kCache32Count*1] = SkPremultiplyARGBInline((a + bias1) >> 16,
-                                                             (r + bias1) >> 16,
-                                                             (g + bias1) >> 16,
-                                                             (b + bias1) >> 16);
-            cache[kCache32Count*2] = SkPremultiplyARGBInline((a + bias2) >> 16,
-                                                             (r + bias2) >> 16,
-                                                             (g + bias2) >> 16,
-                                                             (b + bias2) >> 16);
-            cache[kCache32Count*3] = SkPremultiplyARGBInline((a + bias3) >> 16,
-                                                             (r + bias3) >> 16,
-                                                             (g + bias3) >> 16,
-                                                             (b + bias3) >> 16);
-            cache += 1;
-            a += da;
-            r += dr;
-            g += dg;
-            b += db;
-        } while (--count != 0);
-    }
-}
-
-static inline int SkFixedToFFFF(SkFixed x) {
-    SkASSERT((unsigned)x <= SK_Fixed1);
-    return x - (x >> 16);
-}
-
-const SkPMColor* SkGradientShaderBase::GradientShaderCache::getCache32() {
-    fCache32InitOnce(SkGradientShaderBase::GradientShaderCache::initCache32, this);
-    SkASSERT(fCache32);
-    return fCache32;
-}
-
-void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache* cache) {
-    const int kNumberOfDitherRows = 4;
-    const SkImageInfo info = SkImageInfo::MakeN32Premul(kCache32Count, kNumberOfDitherRows);
-
-    SkASSERT(nullptr == cache->fCache32PixelRef);
-    cache->fCache32PixelRef = SkMallocPixelRef::NewAllocate(info, 0, nullptr);
-    cache->fCache32 = (SkPMColor*)cache->fCache32PixelRef->getAddr();
-    if (cache->fShader.fColorCount == 2) {
-        Build32bitCache(cache->fCache32, cache->fShader.fOrigColors[0],
-                        cache->fShader.fOrigColors[1], kCache32Count, cache->fCacheAlpha,
-                        cache->fShader.fGradFlags, cache->fCacheDither);
-    } else {
-        Rec* rec = cache->fShader.fRecs;
-        int prevIndex = 0;
-        for (int i = 1; i < cache->fShader.fColorCount; i++) {
-            int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
-            SkASSERT(nextIndex < kCache32Count);
-
-            if (nextIndex > prevIndex)
-                Build32bitCache(cache->fCache32 + prevIndex, cache->fShader.fOrigColors[i-1],
-                                cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1,
-                                cache->fCacheAlpha, cache->fShader.fGradFlags, cache->fCacheDither);
-            prevIndex = nextIndex;
-        }
-    }
-}
-
-void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap) const {
-    const bool interpInPremul = SkToBool(fGradFlags &
-                                         SkGradientShader::kInterpolateColorsInPremul_Flag);
-    bitmap->lockPixels();
-    SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels());
-    uint32_t* pixelsS32 = reinterpret_cast<uint32_t*>(bitmap->getPixels());
-
-    typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t;
-
-    pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) {
-        Sk4h c = SkFloatToHalf_finite_ftz(x);
-        pixelsF16[4*index+0] = c[0];
-        pixelsF16[4*index+1] = c[1];
-        pixelsF16[4*index+2] = c[2];
-        pixelsF16[4*index+3] = c[3];
-    };
-    pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) {
-        pixelsS32[index] = Sk4f_toS32(c);
-    };
-
-    pixelWriteFn_t writeSizedPixel =
-        (kRGBA_F16_SkColorType == bitmap->colorType()) ? writeF16Pixel : writeS32Pixel;
-    pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) {
-        writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index);
-    };
-
-    pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel;
-
-    int prevIndex = 0;
-    for (int i = 1; i < fColorCount; i++) {
-        int nextIndex = (fColorCount == 2) ? (kCache32Count - 1)
-            : SkFixedToFFFF(fRecs[i].fPos) >> kCache32Shift;
-        SkASSERT(nextIndex < kCache32Count);
-
-        if (nextIndex > prevIndex) {
-            Sk4f c0 = Sk4f::Load(fOrigColors4f[i - 1].vec());
-            Sk4f c1 = Sk4f::Load(fOrigColors4f[i].vec());
-            if (interpInPremul) {
-                c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f);
-                c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f);
-            }
-
-            Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex));
-            Sk4f delta = (c1 - c0) * step;
-
-            for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) {
-                writePixel(c0, curIndex);
-                c0 += delta;
-            }
-        }
-        prevIndex = nextIndex;
-    }
-    SkASSERT(prevIndex == kCache32Count - 1);
-    bitmap->unlockPixels();
-}
-
-/*
- *  The gradient holds a cache for the most recent value of alpha. Successive
- *  callers with the same alpha value will share the same cache.
- */
-sk_sp<SkGradientShaderBase::GradientShaderCache> SkGradientShaderBase::refCache(U8CPU alpha,
-                                                                          bool dither) const {
-    SkAutoMutexAcquire ama(fCacheMutex);
-    if (!fCache || fCache->getAlpha() != alpha || fCache->getDither() != dither) {
-        fCache.reset(new GradientShaderCache(alpha, dither, *this));
-    }
-    // Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
-    // Otherwise, the pointer may have been overwritten on a different thread before the object's
-    // ref count was incremented.
-    return fCache;
-}
-
-SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
-/*
- *  Because our caller might rebuild the same (logically the same) gradient
- *  over and over, we'd like to return exactly the same "bitmap" if possible,
- *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
- *  To do that, we maintain a private cache of built-bitmaps, based on our
- *  colors and positions. Note: we don't try to flatten the fMapper, so if one
- *  is present, we skip the cache for now.
- */
-void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
-                                                  GradientBitmapType bitmapType) const {
-    // our caller assumes no external alpha, so we ensure that our cache is built with 0xFF
-    sk_sp<GradientShaderCache> cache(this->refCache(0xFF, true));
-
-    // build our key: [numColors + colors[] + {positions[]} + flags + colorType ]
-    int count = 1 + fColorCount + 1 + 1;
-    if (fColorCount > 2) {
-        count += fColorCount - 1;    // fRecs[].fPos
-    }
-
-    SkAutoSTMalloc<16, int32_t> storage(count);
-    int32_t* buffer = storage.get();
-
-    *buffer++ = fColorCount;
-    memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
-    buffer += fColorCount;
-    if (fColorCount > 2) {
-        for (int i = 1; i < fColorCount; i++) {
-            *buffer++ = fRecs[i].fPos;
-        }
-    }
-    *buffer++ = fGradFlags;
-    *buffer++ = static_cast<int32_t>(bitmapType);
-    SkASSERT(buffer - storage.get() == count);
-
-    ///////////////////////////////////
-
-    static SkGradientBitmapCache* gCache;
-    // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp
-    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
-    SkAutoMutexAcquire ama(gGradientCacheMutex);
-
-    if (nullptr == gCache) {
-        gCache = new SkGradientBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
-    }
-    size_t size = count * sizeof(int32_t);
-
-    if (!gCache->find(storage.get(), size, bitmap)) {
-        if (GradientBitmapType::kLegacy == bitmapType) {
-            // force our cache32pixelref to be built
-            (void)cache->getCache32();
-            bitmap->setInfo(SkImageInfo::MakeN32Premul(kCache32Count, 1));
-            bitmap->setPixelRef(sk_ref_sp(cache->getCache32PixelRef()), 0, 0);
-        } else {
-            // For these cases we use the bitmap cache, but not the GradientShaderCache. So just
-            // allocate and populate the bitmap's data directly.
-
-            SkImageInfo info;
-            switch (bitmapType) {
-                case GradientBitmapType::kSRGB:
-                    info = SkImageInfo::Make(kCache32Count, 1, kRGBA_8888_SkColorType,
-                                             kPremul_SkAlphaType,
-                                             SkColorSpace::MakeSRGB());
-                    break;
-                case GradientBitmapType::kHalfFloat:
-                    info = SkImageInfo::Make(
-                        kCache32Count, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType,
-                        SkColorSpace::MakeSRGBLinear());
-                    break;
-                default:
-                    SkFAIL("Unexpected bitmap type");
-                    return;
-            }
-            bitmap->allocPixels(info);
-            this->initLinearBitmap(bitmap);
-        }
-        gCache->add(storage.get(), size, *bitmap);
-    }
-}
-
-void SkGradientShaderBase::commonAsAGradient(GradientInfo* info, bool flipGrad) const {
-    if (info) {
-        if (info->fColorCount >= fColorCount) {
-            SkColor* colorLoc;
-            Rec*     recLoc;
-            SkAutoSTArray<8, SkColor> colorStorage;
-            SkAutoSTArray<8, Rec> recStorage;
-            if (flipGrad && (info->fColors || info->fColorOffsets)) {
-                colorStorage.reset(fColorCount);
-                recStorage.reset(fColorCount);
-                colorLoc = colorStorage.get();
-                recLoc = recStorage.get();
-                FlipGradientColors(colorLoc, recLoc, fOrigColors, fRecs, fColorCount);
-            } else {
-                colorLoc = fOrigColors;
-                recLoc = fRecs;
-            }
-            if (info->fColors) {
-                memcpy(info->fColors, colorLoc, fColorCount * sizeof(SkColor));
-            }
-            if (info->fColorOffsets) {
-                if (fColorCount == 2) {
-                    info->fColorOffsets[0] = 0;
-                    info->fColorOffsets[1] = SK_Scalar1;
-                } else if (fColorCount > 2) {
-                    for (int i = 0; i < fColorCount; ++i) {
-                        info->fColorOffsets[i] = SkFixedToScalar(recLoc[i].fPos);
-                    }
-                }
-            }
-        }
-        info->fColorCount = fColorCount;
-        info->fTileMode = fTileMode;
-        info->fGradientFlags = fGradFlags;
-    }
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void SkGradientShaderBase::toString(SkString* str) const {
-
-    str->appendf("%d colors: ", fColorCount);
-
-    for (int i = 0; i < fColorCount; ++i) {
-        str->appendHex(fOrigColors[i], 8);
-        if (i < fColorCount-1) {
-            str->append(", ");
-        }
-    }
-
-    if (fColorCount > 2) {
-        str->append(" points: (");
-        for (int i = 0; i < fColorCount; ++i) {
-            str->appendScalar(SkFixedToScalar(fRecs[i].fPos));
-            if (i < fColorCount-1) {
-                str->append(", ");
-            }
-        }
-        str->append(")");
-    }
-
-    static const char* gTileModeName[SkShader::kTileModeCount] = {
-        "clamp", "repeat", "mirror"
-    };
-
-    str->append(" ");
-    str->append(gTileModeName[fTileMode]);
-
-    this->INHERITED::toString(str);
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-// Return true if these parameters are valid/legal/safe to construct a gradient
-//
-static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
-                       unsigned tileMode) {
-    return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount;
-}
-
-static void desc_init(SkGradientShaderBase::Descriptor* desc,
-                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
-                      const SkScalar pos[], int colorCount,
-                      SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
-    SkASSERT(colorCount > 1);
-
-    desc->fColors       = colors;
-    desc->fColorSpace   = std::move(colorSpace);
-    desc->fPos          = pos;
-    desc->fCount        = colorCount;
-    desc->fTileMode     = mode;
-    desc->fGradFlags    = flags;
-    desc->fLocalMatrix  = localMatrix;
-}
-
-// assumes colors is SkColor4f* and pos is SkScalar*
-#define EXPAND_1_COLOR(count)                \
-     SkColor4f tmp[2];                       \
-     do {                                    \
-         if (1 == count) {                   \
-             tmp[0] = tmp[1] = colors[0];    \
-             colors = tmp;                   \
-             pos = nullptr;                  \
-             count = 2;                      \
-         }                                   \
-     } while (0)
-
-struct ColorStopOptimizer {
-    ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos,
-                       int count, SkShader::TileMode mode)
-        : fColors(colors)
-        , fPos(pos)
-        , fCount(count) {
-
-            if (!pos || count != 3) {
-                return;
-            }
-
-            if (SkScalarNearlyEqual(pos[0], 0.0f) &&
-                SkScalarNearlyEqual(pos[1], 0.0f) &&
-                SkScalarNearlyEqual(pos[2], 1.0f)) {
-
-                if (SkShader::kRepeat_TileMode == mode ||
-                    SkShader::kMirror_TileMode == mode ||
-                    colors[0] == colors[1]) {
-
-                    // Ignore the leftmost color/pos.
-                    fColors += 1;
-                    fPos    += 1;
-                    fCount   = 2;
-                }
-            } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
-                       SkScalarNearlyEqual(pos[1], 1.0f) &&
-                       SkScalarNearlyEqual(pos[2], 1.0f)) {
-
-                if (SkShader::kRepeat_TileMode == mode ||
-                    SkShader::kMirror_TileMode == mode ||
-                    colors[1] == colors[2]) {
-
-                    // Ignore the rightmost color/pos.
-                    fCount  = 2;
-                }
-            }
-    }
-
-    const SkColor4f* fColors;
-    const SkScalar*  fPos;
-    int              fCount;
-};
-
-struct ColorConverter {
-    ColorConverter(const SkColor* colors, int count) {
-        for (int i = 0; i < count; ++i) {
-            fColors4f.push_back(SkColor4f::FromColor(colors[i]));
-        }
-    }
-
-    SkSTArray<2, SkColor4f, true> fColors4f;
-};
-
-sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
-                                             const SkColor colors[],
-                                             const SkScalar pos[], int colorCount,
-                                             SkShader::TileMode mode,
-                                             uint32_t flags,
-                                             const SkMatrix* localMatrix) {
-    ColorConverter converter(colors, colorCount);
-    return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
-                      localMatrix);
-}
-
-sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
-                                             const SkColor4f colors[],
-                                             sk_sp<SkColorSpace> colorSpace,
-                                             const SkScalar pos[], int colorCount,
-                                             SkShader::TileMode mode,
-                                             uint32_t flags,
-                                             const SkMatrix* localMatrix) {
-    if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
-        return nullptr;
-    }
-    if (!valid_grad(colors, pos, colorCount, mode)) {
-        return nullptr;
-    }
-    if (1 == colorCount) {
-        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
-    }
-    if (localMatrix && !localMatrix->invert(nullptr)) {
-        return nullptr;
-    }
-
-    ColorStopOptimizer opt(colors, pos, colorCount, mode);
-
-    SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
-              localMatrix);
-    return sk_make_sp<SkLinearGradient>(pts, desc);
-}
-
-sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
-                                             const SkColor colors[],
-                                             const SkScalar pos[], int colorCount,
-                                             SkShader::TileMode mode,
-                                             uint32_t flags,
-                                             const SkMatrix* localMatrix) {
-    ColorConverter converter(colors, colorCount);
-    return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
-                      flags, localMatrix);
-}
-
-sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
-                                             const SkColor4f colors[],
-                                             sk_sp<SkColorSpace> colorSpace,
-                                             const SkScalar pos[], int colorCount,
-                                             SkShader::TileMode mode,
-                                             uint32_t flags,
-                                             const SkMatrix* localMatrix) {
-    if (radius <= 0) {
-        return nullptr;
-    }
-    if (!valid_grad(colors, pos, colorCount, mode)) {
-        return nullptr;
-    }
-    if (1 == colorCount) {
-        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
-    }
-    if (localMatrix && !localMatrix->invert(nullptr)) {
-        return nullptr;
-    }
-
-    ColorStopOptimizer opt(colors, pos, colorCount, mode);
-
-    SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
-              localMatrix);
-    return sk_make_sp<SkRadialGradient>(center, radius, desc);
-}
-
-sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
-                                                      SkScalar startRadius,
-                                                      const SkPoint& end,
-                                                      SkScalar endRadius,
-                                                      const SkColor colors[],
-                                                      const SkScalar pos[],
-                                                      int colorCount,
-                                                      SkShader::TileMode mode,
-                                                      uint32_t flags,
-                                                      const SkMatrix* localMatrix) {
-    ColorConverter converter(colors, colorCount);
-    return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
-                               nullptr, pos, colorCount, mode, flags, localMatrix);
-}
-
-sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
-                                                      SkScalar startRadius,
-                                                      const SkPoint& end,
-                                                      SkScalar endRadius,
-                                                      const SkColor4f colors[],
-                                                      sk_sp<SkColorSpace> colorSpace,
-                                                      const SkScalar pos[],
-                                                      int colorCount,
-                                                      SkShader::TileMode mode,
-                                                      uint32_t flags,
-                                                      const SkMatrix* localMatrix) {
-    if (startRadius < 0 || endRadius < 0) {
-        return nullptr;
-    }
-    if (!valid_grad(colors, pos, colorCount, mode)) {
-        return nullptr;
-    }
-    if (startRadius == endRadius) {
-        if (start == end || startRadius == 0) {
-            return SkShader::MakeEmptyShader();
-        }
-    }
-    if (localMatrix && !localMatrix->invert(nullptr)) {
-        return nullptr;
-    }
-    EXPAND_1_COLOR(colorCount);
-
-    ColorStopOptimizer opt(colors, pos, colorCount, mode);
-
-    bool flipGradient = startRadius > endRadius;
-
-    SkGradientShaderBase::Descriptor desc;
-
-    if (!flipGradient) {
-        desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
-                  localMatrix);
-        return sk_make_sp<SkTwoPointConicalGradient>(start, startRadius, end, endRadius,
-                                                     flipGradient, desc);
-    } else {
-        SkAutoSTArray<8, SkColor4f> colorsNew(opt.fCount);
-        SkAutoSTArray<8, SkScalar> posNew(opt.fCount);
-        for (int i = 0; i < opt.fCount; ++i) {
-            colorsNew[i] = opt.fColors[opt.fCount - i - 1];
-        }
-
-        if (pos) {
-            for (int i = 0; i < opt.fCount; ++i) {
-                posNew[i] = 1 - opt.fPos[opt.fCount - i - 1];
-            }
-            desc_init(&desc, colorsNew.get(), std::move(colorSpace), posNew.get(), opt.fCount, mode,
-                      flags, localMatrix);
-        } else {
-            desc_init(&desc, colorsNew.get(), std::move(colorSpace), nullptr, opt.fCount, mode,
-                      flags, localMatrix);
-        }
-
-        return sk_make_sp<SkTwoPointConicalGradient>(end, endRadius, start, startRadius,
-                                                     flipGradient, desc);
-    }
-}
-
-sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
-                                            const SkColor colors[],
-                                            const SkScalar pos[],
-                                            int colorCount,
-                                            uint32_t flags,
-                                            const SkMatrix* localMatrix) {
-    ColorConverter converter(colors, colorCount);
-    return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, flags,
-                     localMatrix);
-}
-
-sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
-                                            const SkColor4f colors[],
-                                            sk_sp<SkColorSpace> colorSpace,
-                                            const SkScalar pos[],
-                                            int colorCount,
-                                            uint32_t flags,
-                                            const SkMatrix* localMatrix) {
-    if (!valid_grad(colors, pos, colorCount, SkShader::kClamp_TileMode)) {
-        return nullptr;
-    }
-    if (1 == colorCount) {
-        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
-    }
-    if (localMatrix && !localMatrix->invert(nullptr)) {
-        return nullptr;
-    }
-
-    auto mode = SkShader::kClamp_TileMode;
-
-    ColorStopOptimizer opt(colors, pos, colorCount, mode);
-
-    SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
-              localMatrix);
-    return sk_make_sp<SkSweepGradient>(cx, cy, desc);
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "GrContext.h"
-#include "GrShaderCaps.h"
-#include "GrTextureStripAtlas.h"
-#include "gl/GrGLContext.h"
-#include "glsl/GrGLSLColorSpaceXformHelper.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#include "SkGr.h"
-
-static inline bool close_to_one_half(const SkFixed& val) {
-    return SkScalarNearlyEqual(SkFixedToScalar(val), SK_ScalarHalf);
-}
-
-static inline int color_type_to_color_count(GrGradientEffect::ColorType colorType) {
-    switch (colorType) {
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        case GrGradientEffect::kSingleHardStop_ColorType:
-            return 4;
-        case GrGradientEffect::kHardStopLeftEdged_ColorType:
-        case GrGradientEffect::kHardStopRightEdged_ColorType:
-            return 3;
-#endif
-        case GrGradientEffect::kTwo_ColorType:
-            return 2;
-        case GrGradientEffect::kThree_ColorType:
-            return 3;
-        case GrGradientEffect::kTexture_ColorType:
-            return 0;
-    }
-
-    SkDEBUGFAIL("Unhandled ColorType in color_type_to_color_count()");
-    return -1;
-}
-
-GrGradientEffect::ColorType GrGradientEffect::determineColorType(
-        const SkGradientShaderBase& shader) {
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-    if (shader.fOrigPos) {
-        if (4 == shader.fColorCount) {
-            if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
-                SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2]) &&
-                SkScalarNearlyEqual(shader.fOrigPos[3], 1.0f)) {
-
-                return kSingleHardStop_ColorType;
-            }
-        } else if (3 == shader.fColorCount) {
-            if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
-                SkScalarNearlyEqual(shader.fOrigPos[1], 0.0f) &&
-                SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
-
-                return kHardStopLeftEdged_ColorType;
-            } else if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
-                       SkScalarNearlyEqual(shader.fOrigPos[1], 1.0f) &&
-                       SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
-                
-                return kHardStopRightEdged_ColorType;
-            }
-        }
-    }
-#endif
-
-    if (SkShader::kClamp_TileMode == shader.getTileMode()) {
-        if (2 == shader.fColorCount) {
-            return kTwo_ColorType;
-        } else if (3 == shader.fColorCount &&
-                   close_to_one_half(shader.getRecs()[1].fPos)) {
-            return kThree_ColorType;
-        }
-    }
-
-    return kTexture_ColorType;
-}
-
-void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler,
-                                                   const GrGradientEffect& ge) {
-    if (int colorCount = color_type_to_color_count(ge.getColorType())) {
-        fColorsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
-                                                     kVec4f_GrSLType,
-                                                     kDefault_GrSLPrecision,
-                                                     "Colors",
-                                                     colorCount);
-        if (ge.fColorType == kSingleHardStop_ColorType) {
-            fHardStopT = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
-                                                    kDefault_GrSLPrecision, "HardStopT");
-        }
-    } else {
-        fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                             kFloat_GrSLType, kDefault_GrSLPrecision,
-                                             "GradientYCoordFS");
-    }
-}
-
-static inline void set_after_interp_color_uni_array(
-                                                  const GrGLSLProgramDataManager& pdman,
-                                                  const GrGLSLProgramDataManager::UniformHandle uni,
-                                                  const SkTDArray<SkColor4f>& colors,
-                                                  const GrColorSpaceXform* colorSpaceXform) {
-    int count = colors.count();
-    if (colorSpaceXform) {
-        constexpr int kSmallCount = 10;
-        SkAutoSTArray<4 * kSmallCount, float> vals(4 * count);
-
-        for (int i = 0; i < count; i++) {
-            colorSpaceXform->srcToDst().mapScalars(colors[i].vec(), &vals[4 * i]);
-        }
-
-        pdman.set4fv(uni, count, vals.get());
-    } else {
-        pdman.set4fv(uni, count, (float*)&colors[0]);
-    }
-}
-
-static inline void set_before_interp_color_uni_array(
-                                                  const GrGLSLProgramDataManager& pdman,
-                                                  const GrGLSLProgramDataManager::UniformHandle uni,
-                                                  const SkTDArray<SkColor4f>& colors,
-                                                  const GrColorSpaceXform* colorSpaceXform) {
-    int count = colors.count();
-    constexpr int kSmallCount = 10;
-    SkAutoSTArray<4 * kSmallCount, float> vals(4 * count);
-
-    for (int i = 0; i < count; i++) {
-        float a = colors[i].fA;
-        vals[4 * i + 0] = colors[i].fR * a;
-        vals[4 * i + 1] = colors[i].fG * a;
-        vals[4 * i + 2] = colors[i].fB * a;
-        vals[4 * i + 3] = a;
-    }
-
-    if (colorSpaceXform) {
-        for (int i = 0; i < count; i++) {
-            colorSpaceXform->srcToDst().mapScalars(&vals[4 * i]);
-        }
-    }
-
-    pdman.set4fv(uni, count, vals.get());
-}
-
-static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
-                                       const GrGLSLProgramDataManager::UniformHandle uni,
-                                       const SkTDArray<SkColor>& colors) {
-    int count = colors.count();
-    constexpr int kSmallCount = 10;
-
-    SkAutoSTArray<4*kSmallCount, float> vals(4*count);
-
-    for (int i = 0; i < colors.count(); i++) {
-        // RGBA
-        vals[4*i + 0] = SkColorGetR(colors[i]) / 255.f;
-        vals[4*i + 1] = SkColorGetG(colors[i]) / 255.f;
-        vals[4*i + 2] = SkColorGetB(colors[i]) / 255.f;
-        vals[4*i + 3] = SkColorGetA(colors[i]) / 255.f;
-    }
-
-    pdman.set4fv(uni, colors.count(), vals.get());
-}
-
-static inline void set_before_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
-                                              const GrGLSLProgramDataManager::UniformHandle uni,
-                                              const SkTDArray<SkColor>& colors) {
-    int count = colors.count();
-    constexpr int kSmallCount = 10;
-
-    SkAutoSTArray<4*kSmallCount, float> vals(4*count);
-
-    for (int i = 0; i < count; i++) {
-        float a = SkColorGetA(colors[i]) / 255.f;
-        float aDiv255 = a / 255.f;
-
-        // RGBA
-        vals[4*i + 0] = SkColorGetR(colors[i]) * aDiv255;
-        vals[4*i + 1] = SkColorGetG(colors[i]) * aDiv255;
-        vals[4*i + 2] = SkColorGetB(colors[i]) * aDiv255;
-        vals[4*i + 3] = a;
-    }
-
-    pdman.set4fv(uni, count, vals.get());
-}
-
-void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
-                                                const GrProcessor& processor) {
-    const GrGradientEffect& e = processor.cast<GrGradientEffect>();
-
-    switch (e.getColorType()) {
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        case GrGradientEffect::kSingleHardStop_ColorType:
-            pdman.set1f(fHardStopT, e.fPositions[1]);
-            // fall through
-        case GrGradientEffect::kHardStopLeftEdged_ColorType:
-        case GrGradientEffect::kHardStopRightEdged_ColorType:
-#endif
-        case GrGradientEffect::kTwo_ColorType:
-        case GrGradientEffect::kThree_ColorType: {
-            if (e.fColors4f.count() > 0) {
-                // Gamma-correct / color-space aware
-                if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
-                    set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors4f,
-                                                      e.fColorSpaceXform.get());
-                } else {
-                    set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors4f,
-                                                     e.fColorSpaceXform.get());
-                }
-            } else {
-                // Legacy mode. Would be nice if we had converted the 8-bit colors to float earlier
-                if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
-                    set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors);
-                } else {
-                    set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors);
-                }
-            }
-
-            break;
-        }
-
-        case GrGradientEffect::kTexture_ColorType: {
-            SkScalar yCoord = e.getYCoord();
-            if (yCoord != fCachedYCoord) {
-                pdman.set1f(fFSYUni, yCoord);
-                fCachedYCoord = yCoord;
-            }
-            if (SkToBool(e.fColorSpaceXform)) {
-                fColorSpaceHelper.setData(pdman, e.fColorSpaceXform.get());
-            }
-            break;
-        }
-    }
-}
-
-uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) {
-    const GrGradientEffect& e = processor.cast<GrGradientEffect>();
-
-    uint32_t key = 0;
-
-    if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
-        key |= kPremulBeforeInterpKey;
-    }
-
-    if (GrGradientEffect::kTwo_ColorType == e.getColorType()) {
-        key |= kTwoColorKey;
-    } else if (GrGradientEffect::kThree_ColorType == e.getColorType()) {
-        key |= kThreeColorKey;
-    }
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-    else if (GrGradientEffect::kSingleHardStop_ColorType == e.getColorType()) {
-        key |= kHardStopCenteredKey;
-    } else if (GrGradientEffect::kHardStopLeftEdged_ColorType == e.getColorType()) {
-        key |= kHardStopZeroZeroOneKey;
-    } else if (GrGradientEffect::kHardStopRightEdged_ColorType == e.getColorType()) {
-        key |= kHardStopZeroOneOneKey;
-    }
-   
-    if (SkShader::TileMode::kClamp_TileMode == e.fTileMode) {
-        key |= kClampTileMode;
-    } else if (SkShader::TileMode::kRepeat_TileMode == e.fTileMode) {
-        key |= kRepeatTileMode;
-    } else {
-        key |= kMirrorTileMode;
-    }
-#endif
-
-    key |= GrColorSpaceXform::XformKey(e.fColorSpaceXform.get()) << kReservedBits;
-
-    return key;
-}
-
-void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
-                                                GrGLSLUniformHandler* uniformHandler,
-                                                const GrShaderCaps* shaderCaps,
-                                                const GrGradientEffect& ge,
-                                                const char* gradientTValue,
-                                                const char* outputColor,
-                                                const char* inputColor,
-                                                const TextureSamplers& texSamplers) {
-    switch (ge.getColorType()) {
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        case kSingleHardStop_ColorType: {
-            const char* t      = gradientTValue;
-            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
-            const char* stopT = uniformHandler->getUniformCStr(fHardStopT);
-
-            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
-
-            // Account for tile mode
-            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
-            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
-                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
-                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
-                fragBuilder->codeAppendf("    } else {");
-                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
-                fragBuilder->codeAppendf("    }");
-                fragBuilder->codeAppendf("}");
-            }
-
-            // Calculate color
-            fragBuilder->codeAppend ("vec4 start, end;");
-            fragBuilder->codeAppend ("float relative_t;");
-            fragBuilder->codeAppendf("if (clamp_t < %s) {", stopT);
-            fragBuilder->codeAppendf("    start = %s[0];", colors);
-            fragBuilder->codeAppendf("    end   = %s[1];", colors);
-            fragBuilder->codeAppendf("    relative_t = clamp_t / %s;", stopT);
-            fragBuilder->codeAppend ("} else {");
-            fragBuilder->codeAppendf("    start = %s[2];", colors);
-            fragBuilder->codeAppendf("    end   = %s[3];", colors);
-            fragBuilder->codeAppendf("    relative_t = (clamp_t - %s) / (1 - %s);", stopT, stopT);
-            fragBuilder->codeAppend ("}");
-            fragBuilder->codeAppend ("vec4 colorTemp = mix(start, end, relative_t);");
-
-            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
-            }
-            if (ge.fColorSpaceXform) {
-                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
-            }
-            fragBuilder->codeAppendf("%s = %s;", outputColor,
-                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-
-            break;
-        }
-
-        case kHardStopLeftEdged_ColorType: {
-            const char* t      = gradientTValue;
-            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
-
-            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
-
-            // Account for tile mode
-            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
-            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
-                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
-                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
-                fragBuilder->codeAppendf("    } else {");
-                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
-                fragBuilder->codeAppendf("    }");
-                fragBuilder->codeAppendf("}");
-            }
-
-            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[1], %s[2], clamp_t);", colors,
-                                     colors);
-            if (SkShader::kClamp_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("if (%s < 0.0) {", t);
-                fragBuilder->codeAppendf("    colorTemp = %s[0];", colors);
-                fragBuilder->codeAppendf("}");
-            }
-
-            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
-            }
-            if (ge.fColorSpaceXform) {
-                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
-            }
-            fragBuilder->codeAppendf("%s = %s;", outputColor,
-                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-
-            break;
-        }
-
-        case kHardStopRightEdged_ColorType: {
-            const char* t      = gradientTValue;
-            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
-
-            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
-
-            // Account for tile mode
-            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
-            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
-                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
-                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
-                fragBuilder->codeAppendf("    } else {");
-                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
-                fragBuilder->codeAppendf("    }");
-                fragBuilder->codeAppendf("}");
-            }
-
-            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp_t);", colors,
-                                     colors);
-            if (SkShader::kClamp_TileMode == ge.fTileMode) {
-                fragBuilder->codeAppendf("if (%s > 1.0) {", t);
-                fragBuilder->codeAppendf("    colorTemp = %s[2];", colors);
-                fragBuilder->codeAppendf("}");
-            }
-
-            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
-            }
-            if (ge.fColorSpaceXform) {
-                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
-            }
-            fragBuilder->codeAppendf("%s = %s;", outputColor,
-                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-
-            break;
-        }
-#endif
-
-        case kTwo_ColorType: {
-            const char* t      = gradientTValue;
-            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
-
-            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp(%s, 0.0, 1.0));",
-                                     colors, colors, t);
-
-            // We could skip this step if both colors are known to be opaque. Two
-            // considerations:
-            // The gradient SkShader reporting opaque is more restrictive than necessary in the two
-            // pt case. Make sure the key reflects this optimization (and note that it can use the
-            // same shader as thekBeforeIterp case). This same optimization applies to the 3 color
-            // case below.
-            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
-            }
-            if (ge.fColorSpaceXform) {
-                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
-            }
-
-            fragBuilder->codeAppendf("%s = %s;", outputColor,
-                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-
-            break;
-        }
-
-        case kThree_ColorType: {
-            const char* t      = gradientTValue;
-            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
-
-            fragBuilder->codeAppendf("float oneMinus2t = 1.0 - (2.0 * %s);", t);
-            fragBuilder->codeAppendf("vec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s[0];",
-                                     colors);
-            if (!shaderCaps->canUseMinAndAbsTogether()) {
-                // The Tegra3 compiler will sometimes never return if we have
-                // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression.
-                fragBuilder->codeAppendf("float minAbs = abs(oneMinus2t);");
-                fragBuilder->codeAppendf("minAbs = minAbs > 1.0 ? 1.0 : minAbs;");
-                fragBuilder->codeAppendf("colorTemp += (1.0 - minAbs) * %s[1];", colors);
-            } else {
-                fragBuilder->codeAppendf("colorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s[1];",
-                                         colors);
-            }
-            fragBuilder->codeAppendf("colorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s[2];", colors);
-
-            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
-                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
-            }
-            if (ge.fColorSpaceXform) {
-                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
-            }
-
-            fragBuilder->codeAppendf("%s = %s;", outputColor,
-                                     (GrGLSLExpr4(inputColor) * GrGLSLExpr4("colorTemp")).c_str());
-
-            break;
-        }
-
-        case kTexture_ColorType: {
-            fColorSpaceHelper.emitCode(uniformHandler, ge.fColorSpaceXform.get());
-
-            const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
-
-            fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni);
-            fragBuilder->codeAppendf("%s = ", outputColor);
-            fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord",
-                                                        kVec2f_GrSLType, &fColorSpaceHelper);
-            fragBuilder->codeAppend(";");
-
-            break;
-        }
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-inline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool isOpaque) {
-    return isOpaque
-                   ? kPreservesOpaqueInput_OptimizationFlag |
-                             kCompatibleWithCoverageAsAlpha_OptimizationFlag
-                   : kCompatibleWithCoverageAsAlpha_OptimizationFlag;
-}
-
-GrGradientEffect::GrGradientEffect(const CreateArgs& args, bool isOpaque)
-        : INHERITED(OptFlags(isOpaque)) {
-    const SkGradientShaderBase& shader(*args.fShader);
-
-    fIsOpaque = shader.isOpaque();
-
-    fColorType = this->determineColorType(shader);
-    fColorSpaceXform = std::move(args.fColorSpaceXform);
-
-    if (kTexture_ColorType != fColorType) {
-        SkASSERT(shader.fOrigColors && shader.fOrigColors4f);
-        if (args.fGammaCorrect) {
-            fColors4f = SkTDArray<SkColor4f>(shader.fOrigColors4f, shader.fColorCount);
-        } else {
-            fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount);
-        }
-
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        if (shader.fOrigPos) {
-            fPositions = SkTDArray<SkScalar>(shader.fOrigPos, shader.fColorCount);
-        }
-#endif
-    }
-
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-    fTileMode = args.fTileMode;
-#endif
-
-    switch (fColorType) {
-        // The two and three color specializations do not currently support tiling.
-        case kTwo_ColorType:
-        case kThree_ColorType:
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        case kHardStopLeftEdged_ColorType:
-        case kHardStopRightEdged_ColorType:
-        case kSingleHardStop_ColorType:
-#endif
-            fRow = -1;
-
-            if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) {
-                fPremulType = kBeforeInterp_PremulType;
-            } else {
-                fPremulType = kAfterInterp_PremulType;
-            }
-
-            fCoordTransform.reset(*args.fMatrix);
-
-            break;
-        case kTexture_ColorType:
-            // doesn't matter how this is set, just be consistent because it is part of the
-            // effect key.
-            fPremulType = kBeforeInterp_PremulType;
-
-            SkGradientShaderBase::GradientBitmapType bitmapType =
-                SkGradientShaderBase::GradientBitmapType::kLegacy;
-            if (args.fGammaCorrect) {
-                // Try to use F16 if we can
-                if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
-                    bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
-                } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
-                    bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB;
-                } else {
-                    // This can happen, but only if someone explicitly creates an unsupported
-                    // (eg sRGB) surface. Just fall back to legacy behavior.
-                }
-            }
-
-            SkBitmap bitmap;
-            shader.getGradientTableBitmap(&bitmap, bitmapType);
-            SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
-
-
-            GrTextureStripAtlas::Desc desc;
-            desc.fWidth  = bitmap.width();
-            desc.fHeight = 32;
-            desc.fRowHeight = bitmap.height();
-            desc.fContext = args.fContext;
-            desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps());
-            fAtlas = GrTextureStripAtlas::GetAtlas(desc);
-            SkASSERT(fAtlas);
-
-            // We always filter the gradient table. Each table is one row of a texture, always
-            // y-clamp.
-            GrSamplerParams params;
-            params.setFilterMode(GrSamplerParams::kBilerp_FilterMode);
-            params.setTileModeX(args.fTileMode);
-
-            fRow = fAtlas->lockRow(bitmap);
-            if (-1 != fRow) {
-                fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
-                // This is 1/2 places where auto-normalization is disabled
-                fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix,
-                                      fAtlas->asTextureProxyRef().get(),
-                                      params.filterMode(), false);
-                fTextureSampler.reset(args.fContext->resourceProvider(),
-                                      fAtlas->asTextureProxyRef(), params);
-            } else {
-                // In this instance we know the params are:
-                //   clampY, bilerp
-                // and the proxy is:
-                //   exact fit, power of two in both dimensions
-                // Only the x-tileMode is unknown. However, given all the other knowns we know
-                // that GrMakeCachedBitmapProxy is sufficient (i.e., it won't need to be
-                // extracted to a subset or mipmapped).
-                sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(
-                                                                args.fContext->resourceProvider(),
-                                                                bitmap);
-                if (!proxy) {
-                    return;
-                }
-                // This is 2/2 places where auto-normalization is disabled
-                fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix,
-                                      proxy.get(), params.filterMode(), false);
-                fTextureSampler.reset(args.fContext->resourceProvider(),
-                                      std::move(proxy), params);
-                fYCoord = SK_ScalarHalf;
-            }
-
-            this->addTextureSampler(&fTextureSampler);
-
-            break;
-    }
-
-    this->addCoordTransform(&fCoordTransform);
-}
-
-GrGradientEffect::~GrGradientEffect() {
-    if (this->useAtlas()) {
-        fAtlas->unlockRow(fRow);
-    }
-}
-
-bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
-    const GrGradientEffect& ge = processor.cast<GrGradientEffect>();
-
-    if (this->fColorType != ge.getColorType()) {
-        return false;
-    }
-    SkASSERT(this->useAtlas() == ge.useAtlas());
-    if (kTexture_ColorType == fColorType) {
-        if (fYCoord != ge.getYCoord()) {
-            return false;
-        }
-    } else {
-        if (kSingleHardStop_ColorType == fColorType) {
-            if (!SkScalarNearlyEqual(ge.fPositions[1], fPositions[1])) {
-                return false;
-            }
-        }
-        if (this->getPremulType() != ge.getPremulType() ||
-            this->fColors.count() != ge.fColors.count() ||
-            this->fColors4f.count() != ge.fColors4f.count()) {
-            return false;
-        }
-
-        for (int i = 0; i < this->fColors.count(); i++) {
-            if (*this->getColors(i) != *ge.getColors(i)) {
-                return false;
-            }
-        }
-        for (int i = 0; i < this->fColors4f.count(); i++) {
-            if (*this->getColors4f(i) != *ge.getColors4f(i)) {
-                return false;
-            }
-        }
-    }
-    return GrColorSpaceXform::Equals(this->fColorSpaceXform.get(), ge.fColorSpaceXform.get());
-}
-
-#if GR_TEST_UTILS
-GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) {
-    // Set color count to min of 2 so that we don't trigger the const color optimization and make
-    // a non-gradient processor.
-    fColorCount = random->nextRangeU(2, kMaxRandomGradientColors);
-    fUseColors4f = random->nextBool();
-
-    // if one color, omit stops, otherwise randomly decide whether or not to
-    if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) {
-        fStops = nullptr;
-    } else {
-        fStops = fStopStorage;
-    }
-
-    // if using SkColor4f, attach a random (possibly null) color space (with linear gamma)
-    if (fUseColors4f) {
-        fColorSpace = GrTest::TestColorSpace(random);
-        if (fColorSpace) {
-            SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(fColorSpace)->type());
-            fColorSpace = static_cast<SkColorSpace_XYZ*>(fColorSpace.get())->makeLinearGamma();
-        }
-    }
-
-    SkScalar stop = 0.f;
-    for (int i = 0; i < fColorCount; ++i) {
-        if (fUseColors4f) {
-            fColors4f[i].fR = random->nextUScalar1();
-            fColors4f[i].fG = random->nextUScalar1();
-            fColors4f[i].fB = random->nextUScalar1();
-            fColors4f[i].fA = random->nextUScalar1();
-        } else {
-            fColors[i] = random->nextU();
-        }
-        if (fStops) {
-            fStops[i] = stop;
-            stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f;
-        }
-    }
-    fTileMode = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount));
-}
-#endif
-
-#endif
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
deleted file mode 100644
index f64b439..0000000
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkGradientShaderPriv_DEFINED
-#define SkGradientShaderPriv_DEFINED
-
-#include "SkGradientBitmapCache.h"
-#include "SkGradientShader.h"
-
-#include "SkArenaAlloc.h"
-#include "SkAutoMalloc.h"
-#include "SkClampRange.h"
-#include "SkColorPriv.h"
-#include "SkColorSpace.h"
-#include "SkMallocPixelRef.h"
-#include "SkOnce.h"
-#include "SkReadBuffer.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkWriteBuffer.h"
-
-#if SK_SUPPORT_GPU
-    #define GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS 1
-#endif
-
-static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
-                               int count) {
-    if (count > 0) {
-        if (v0 == v1) {
-            sk_memset32(dst, v0, count);
-        } else {
-            int pairs = count >> 1;
-            for (int i = 0; i < pairs; i++) {
-                *dst++ = v0;
-                *dst++ = v1;
-            }
-            if (count & 1) {
-                *dst = v0;
-            }
-        }
-    }
-}
-
-//  Clamp
-
-static inline SkFixed clamp_tileproc(SkFixed x) {
-    return SkClampMax(x, 0xFFFF);
-}
-
-// Repeat
-
-static inline SkFixed repeat_tileproc(SkFixed x) {
-    return x & 0xFFFF;
-}
-
-// Mirror
-
-static inline SkFixed mirror_tileproc(SkFixed x) {
-    int s = SkLeftShift(x, 15) >> 31;
-    return (x ^ s) & 0xFFFF;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-typedef SkFixed (*TileProc)(SkFixed);
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const TileProc gTileProcs[] = {
-    clamp_tileproc,
-    repeat_tileproc,
-    mirror_tileproc
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkGradientShaderBase : public SkShader {
-public:
-    struct Descriptor {
-        Descriptor() {
-            sk_bzero(this, sizeof(*this));
-            fTileMode = SkShader::kClamp_TileMode;
-        }
-
-        const SkMatrix*     fLocalMatrix;
-        const SkColor4f*    fColors;
-        sk_sp<SkColorSpace> fColorSpace;
-        const SkScalar*     fPos;
-        int                 fCount;
-        SkShader::TileMode  fTileMode;
-        uint32_t            fGradFlags;
-
-        void flatten(SkWriteBuffer&) const;
-    };
-
-    class DescriptorScope : public Descriptor {
-    public:
-        DescriptorScope() {}
-
-        bool unflatten(SkReadBuffer&);
-
-        // fColors and fPos always point into local memory, so they can be safely mutated
-        //
-        SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); }
-        SkScalar* mutablePos() { return const_cast<SkScalar*>(fPos); }
-
-    private:
-        enum {
-            kStorageCount = 16
-        };
-        SkColor4f fColorStorage[kStorageCount];
-        SkScalar fPosStorage[kStorageCount];
-        SkMatrix fLocalMatrixStorage;
-        SkAutoMalloc fDynamicStorage;
-    };
-
-    SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit);
-    ~SkGradientShaderBase() override;
-
-    // The cache is initialized on-demand when getCache32 is called.
-    class GradientShaderCache : public SkRefCnt {
-    public:
-        GradientShaderCache(U8CPU alpha, bool dither, const SkGradientShaderBase& shader);
-        ~GradientShaderCache();
-
-        const SkPMColor*    getCache32();
-
-        SkMallocPixelRef* getCache32PixelRef() const { return fCache32PixelRef; }
-
-        unsigned getAlpha() const { return fCacheAlpha; }
-        bool getDither() const { return fCacheDither; }
-
-    private:
-        // Working pointer. If it's nullptr, we need to recompute the cache values.
-        SkPMColor*  fCache32;
-
-        SkMallocPixelRef* fCache32PixelRef;
-        const unsigned    fCacheAlpha;        // The alpha value we used when we computed the cache.
-                                              // Larger than 8bits so we can store uninitialized
-                                              // value.
-        const bool        fCacheDither;       // The dither flag used when we computed the cache.
-
-        const SkGradientShaderBase& fShader;
-
-        // Make sure we only initialize the cache once.
-        SkOnce fCache32InitOnce;
-
-        static void initCache32(GradientShaderCache* cache);
-
-        static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
-                                    U8CPU alpha, uint32_t gradFlags, bool dither);
-    };
-
-    class GradientShaderBaseContext : public SkShader::Context {
-    public:
-        GradientShaderBaseContext(const SkGradientShaderBase& shader, const ContextRec&);
-
-        uint32_t getFlags() const override { return fFlags; }
-
-        bool isValid() const;
-
-    protected:
-        SkMatrix    fDstToIndex;
-        SkMatrix::MapXYProc fDstToIndexProc;
-        uint8_t     fDstToIndexClass;
-        uint8_t     fFlags;
-        bool        fDither;
-
-        sk_sp<GradientShaderCache> fCache;
-
-    private:
-        typedef SkShader::Context INHERITED;
-    };
-
-    bool isOpaque() const override;
-
-    enum class GradientBitmapType : uint8_t {
-        kLegacy,
-        kSRGB,
-        kHalfFloat,
-    };
-
-    void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const;
-
-    enum {
-        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
-        /// it, use a larger cache.
-        kCache32Bits    = 8,
-        kCache32Count   = (1 << kCache32Bits),
-        kCache32Shift   = 16 - kCache32Bits,
-        kSqrt32Shift    = 8 - kCache32Bits,
-
-        /// This value is used to *read* the dither cache; it may be 0
-        /// if dithering is disabled.
-        kDitherStride32 = kCache32Count,
-    };
-
-    uint32_t getGradFlags() const { return fGradFlags; }
-
-protected:
-    class GradientShaderBase4fContext;
-
-    SkGradientShaderBase(SkReadBuffer& );
-    void flatten(SkWriteBuffer&) const override;
-    SK_TO_STRING_OVERRIDE()
-
-    const SkMatrix fPtsToUnit;
-    TileMode    fTileMode;
-    TileProc    fTileProc;
-    uint8_t     fGradFlags;
-
-    struct Rec {
-        SkFixed     fPos;   // 0...1
-        uint32_t    fScale; // (1 << 24) / range
-    };
-    Rec*        fRecs;
-
-    void commonAsAGradient(GradientInfo*, bool flipGrad = false) const;
-
-    bool onAsLuminanceColor(SkColor*) const override;
-
-
-    void initLinearBitmap(SkBitmap* bitmap) const;
-
-    /*
-     * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively.
-     * Count is the number of colors in the gradient
-     * It will then flip all the color and rec information and return in their respective Dst
-     * pointers. It is assumed that space has already been allocated for the Dst pointers.
-     * The rec src and dst are only assumed to be valid if count > 2
-     */
-    static void FlipGradientColors(SkColor* colorDst, Rec* recDst,
-                                   SkColor* colorSrc, Rec* recSrc,
-                                   int count);
-
-    template <typename T, typename... Args>
-    static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {
-        auto* ctx = alloc->make<T>(std::forward<Args>(args)...);
-        if (!ctx->isValid()) {
-            return nullptr;
-        }
-        return ctx;
-    }
-
-private:
-    enum {
-        kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
-
-        kStorageSize = kColorStorageCount *
-                       (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec) + sizeof(SkColor4f))
-    };
-    SkColor             fStorage[(kStorageSize + 3) >> 2];
-public:
-    SkColor*            fOrigColors;   // original colors, before modulation by paint in context.
-    SkColor4f*          fOrigColors4f; // original colors, as linear floats
-    SkScalar*           fOrigPos;      // original positions
-    int                 fColorCount;
-    sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops
-
-    bool colorsAreOpaque() const { return fColorsAreOpaque; }
-
-    TileMode getTileMode() const { return fTileMode; }
-    Rec* getRecs() const { return fRecs; }
-
-private:
-    bool                fColorsAreOpaque;
-
-    sk_sp<GradientShaderCache> refCache(U8CPU alpha, bool dither) const;
-    mutable SkMutex                    fCacheMutex;
-    mutable sk_sp<GradientShaderCache> fCache;
-
-    void initCommon();
-
-    typedef SkShader INHERITED;
-};
-
-static inline int init_dither_toggle(int x, int y) {
-    x &= 1;
-    y = (y & 1) << 1;
-    return (x | y) * SkGradientShaderBase::kDitherStride32;
-}
-
-static inline int next_dither_toggle(int toggle) {
-    return toggle ^ SkGradientShaderBase::kDitherStride32;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "GrColorSpaceXform.h"
-#include "GrCoordTransform.h"
-#include "GrFragmentProcessor.h"
-#include "glsl/GrGLSLColorSpaceXformHelper.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-
-class GrInvariantOutput;
-
-/*
- * The interpretation of the texture matrix depends on the sample mode. The
- * texture matrix is applied both when the texture coordinates are explicit
- * and  when vertex positions are used as texture  coordinates. In the latter
- * case the texture matrix is applied to the pre-view-matrix position
- * values.
- *
- * Normal SampleMode
- *  The post-matrix texture coordinates are in normalize space with (0,0) at
- *  the top-left and (1,1) at the bottom right.
- * RadialGradient
- *  The matrix specifies the radial gradient parameters.
- *  (0,0) in the post-matrix space is center of the radial gradient.
- * Radial2Gradient
- *   Matrix transforms to space where first circle is centered at the
- *   origin. The second circle will be centered (x, 0) where x may be
- *   0 and is provided by setRadial2Params. The post-matrix space is
- *   normalized such that 1 is the second radius - first radius.
- * SweepGradient
- *  The angle from the origin of texture coordinates in post-matrix space
- *  determines the gradient value.
- */
-
- class GrTextureStripAtlas;
-
-// Base class for Gr gradient effects
-class GrGradientEffect : public GrFragmentProcessor {
-public:
-    struct CreateArgs {
-        CreateArgs(GrContext* context,
-                   const SkGradientShaderBase* shader,
-                   const SkMatrix* matrix,
-                   SkShader::TileMode tileMode,
-                   sk_sp<GrColorSpaceXform> colorSpaceXform,
-                   bool gammaCorrect)
-            : fContext(context)
-            , fShader(shader)
-            , fMatrix(matrix)
-            , fTileMode(tileMode)
-            , fColorSpaceXform(std::move(colorSpaceXform))
-            , fGammaCorrect(gammaCorrect) {}
-
-        GrContext*                  fContext;
-        const SkGradientShaderBase* fShader;
-        const SkMatrix*             fMatrix;
-        SkShader::TileMode          fTileMode;
-        sk_sp<GrColorSpaceXform>    fColorSpaceXform;
-        bool                        fGammaCorrect;
-    };
-
-    class GLSLProcessor;
-
-    ~GrGradientEffect() override;
-
-    bool useAtlas() const { return SkToBool(-1 != fRow); }
-    SkScalar getYCoord() const { return fYCoord; }
-
-    enum ColorType {
-        kTwo_ColorType,
-        kThree_ColorType, // Symmetric three color
-        kTexture_ColorType,
-
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        kSingleHardStop_ColorType,     // 0, t, t, 1
-        kHardStopLeftEdged_ColorType,  // 0, 0, 1
-        kHardStopRightEdged_ColorType, // 0, 1, 1
-#endif
-    };
-
-    ColorType getColorType() const { return fColorType; }
-
-    // Determines the type of gradient, one of:
-    //    - Two-color
-    //    - Symmetric three-color
-    //    - Texture
-    //    - Centered hard stop
-    //    - Left-edged hard stop
-    //    - Right-edged hard stop
-    ColorType determineColorType(const SkGradientShaderBase& shader);
-
-    enum PremulType {
-        kBeforeInterp_PremulType,
-        kAfterInterp_PremulType,
-    };
-
-    PremulType getPremulType() const { return fPremulType; }
-
-    const SkColor* getColors(int pos) const {
-        SkASSERT(fColorType != kTexture_ColorType);
-        SkASSERT(pos < fColors.count());
-        return &fColors[pos];
-    }
-
-    const SkColor4f* getColors4f(int pos) const {
-        SkASSERT(fColorType != kTexture_ColorType);
-        SkASSERT(pos < fColors4f.count());
-        return &fColors4f[pos];
-    }
-
-protected:
-    GrGradientEffect(const CreateArgs&, bool isOpaque);
-
-    #if GR_TEST_UTILS
-    /** Helper struct that stores (and populates) parameters to construct a random gradient.
-        If fUseColors4f is true, then the SkColor4f factory should be called, with fColors4f and
-        fColorSpace. Otherwise, the SkColor factory should be called, with fColors. fColorCount
-        will be the number of color stops in either case, and fColors and fStops can be passed to
-        the gradient factory. (The constructor may decide not to use stops, in which case fStops
-        will be nullptr). */
-    struct RandomGradientParams {
-        static const int kMaxRandomGradientColors = 5;
-
-        RandomGradientParams(SkRandom* r);
-
-        bool fUseColors4f;
-        SkColor fColors[kMaxRandomGradientColors];
-        SkColor4f fColors4f[kMaxRandomGradientColors];
-        sk_sp<SkColorSpace> fColorSpace;
-        SkScalar fStopStorage[kMaxRandomGradientColors];
-        SkShader::TileMode fTileMode;
-        int fColorCount;
-        SkScalar* fStops;
-    };
-    #endif
-
-    bool onIsEqual(const GrFragmentProcessor&) const override;
-
-    const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
-
-private:
-    static OptimizationFlags OptFlags(bool isOpaque);
-
-    // If we're in legacy mode, then fColors will be populated. If we're gamma-correct, then
-    // fColors4f and fColorSpaceXform will be populated.
-    SkTDArray<SkColor>       fColors;
-
-    SkTDArray<SkColor4f>     fColors4f;
-    sk_sp<GrColorSpaceXform> fColorSpaceXform;
-
-    SkTDArray<SkScalar>      fPositions;
-    SkShader::TileMode       fTileMode;
-
-    GrCoordTransform fCoordTransform;
-    TextureSampler fTextureSampler;
-    SkScalar fYCoord;
-    GrTextureStripAtlas* fAtlas;
-    int fRow;
-    bool fIsOpaque;
-    ColorType fColorType;
-    PremulType fPremulType; // This is already baked into the table for texture gradients, and
-                            // only changes behavior for gradients that don't use a texture.
-    typedef GrFragmentProcessor INHERITED;
-
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-// Base class for GL gradient effects
-class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor {
-public:
-    GLSLProcessor() {
-        fCachedYCoord = SK_ScalarMax;
-    }
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-protected:
-    /**
-     * Subclasses must call this. It will return a key for the part of the shader code controlled
-     * by the base class. The subclasses must stick it in their key and then pass it to the below
-     * emit* functions from their emitCode function.
-     */
-    static uint32_t GenBaseGradientKey(const GrProcessor&);
-
-    // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
-    // should call this method from their emitCode().
-    void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&);
-
-    // Emit code that gets a fragment's color from an expression for t; has branches for
-    // several control flows inside -- 2-color gradients, 3-color symmetric gradients, 4+
-    // color gradients that use the traditional texture lookup, as well as several varieties
-    // of hard stop gradients
-    void emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
-                   GrGLSLUniformHandler* uniformHandler,
-                   const GrShaderCaps* shaderCaps,
-                   const GrGradientEffect&,
-                   const char* gradientTValue,
-                   const char* outputColor,
-                   const char* inputColor,
-                   const TextureSamplers&);
-
-private:
-    enum {
-        // First bit for premul before/after interp
-        kPremulBeforeInterpKey  =  1,
-
-        // Next three bits for 2/3 color type or different special
-        // hard stop cases (neither means using texture atlas)
-        kTwoColorKey            =  2,
-        kThreeColorKey          =  4,
-#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
-        kHardStopCenteredKey    =  6,
-        kHardStopZeroZeroOneKey =  8,
-        kHardStopZeroOneOneKey  = 10,
-
-        // Next two bits for tile mode
-        kClampTileMode          = 16,
-        kRepeatTileMode         = 32,
-        kMirrorTileMode         = 48,
-
-        // Lower six bits for premul, 2/3 color type, and tile mode
-        kReservedBits           = 6,
-#endif
-    };
-
-    SkScalar fCachedYCoord;
-    GrGLSLProgramDataManager::UniformHandle fColorsUni;
-    GrGLSLProgramDataManager::UniformHandle fHardStopT;
-    GrGLSLProgramDataManager::UniformHandle fFSYUni;
-    GrGLSLColorSpaceXformHelper             fColorSpaceHelper;
-
-    typedef GrGLSLFragmentProcessor INHERITED;
-};
-
-#endif
-
-#endif
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
deleted file mode 100644
index b58cb29..0000000
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ /dev/null
@@ -1,853 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Sk4fLinearGradient.h"
-#include "SkLinearGradient.h"
-#include "SkRefCnt.h"
-
-// define to test the 4f gradient path
-// #define FORCE_4F_CONTEXT
-
-static const float kInv255Float = 1.0f / 255;
-
-static inline int repeat_8bits(int x) {
-    return x & 0xFF;
-}
-
-static inline int mirror_8bits(int x) {
-    if (x & 256) {
-        x = ~x;
-    }
-    return x & 255;
-}
-
-static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
-    SkVector    vec = pts[1] - pts[0];
-    SkScalar    mag = vec.length();
-    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
-
-    vec.scale(inv);
-    SkMatrix matrix;
-    matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
-    matrix.postTranslate(-pts[0].fX, -pts[0].fY);
-    matrix.postScale(inv, inv);
-    return matrix;
-}
-
-static bool use_4f_context(const SkShader::ContextRec& rec, uint32_t flags) {
-#ifdef FORCE_4F_CONTEXT
-    return true;
-#else
-    return rec.fPreferredDstType == SkShader::ContextRec::kPM4f_DstType
-        || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag);
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
-    : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
-    , fStart(pts[0])
-    , fEnd(pts[1]) {
-}
-
-sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
-    DescriptorScope desc;
-    if (!desc.unflatten(buffer)) {
-        return nullptr;
-    }
-    SkPoint pts[2];
-    pts[0] = buffer.readPoint();
-    pts[1] = buffer.readPoint();
-    return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
-                                        desc.fCount, desc.fTileMode, desc.fGradFlags,
-                                        desc.fLocalMatrix);
-}
-
-void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writePoint(fStart);
-    buffer.writePoint(fEnd);
-}
-
-SkShader::Context* SkLinearGradient::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const
-{
-    return use_4f_context(rec, fGradFlags)
-           ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
-           : CheckedMakeContext<  LinearGradientContext>(alloc, *this, rec);
-}
-
-// For now, only a 2-stop raster pipeline specialization.
-//
-// Stages:
-//
-//   * matrix (map dst -> grad space)
-//   * clamp/repeat/mirror (tiling)
-//   * linear_gradient_2stops (lerp c0/c1)
-//   * optional premul
-//
-bool SkLinearGradient::onAppendStages(SkRasterPipeline* p,
-                                      SkColorSpace* cs,
-                                      SkArenaAlloc* alloc,
-                                      const SkMatrix& ctm,
-                                      const SkPaint&,
-                                      const SkMatrix* localM) const {
-    if (fColorCount > 2) {
-        return false;
-    }
-
-    // Local matrix not supported currently.  Remove once we have a generic RP wrapper.
-    if (localM || !getLocalMatrix().isIdentity()) {
-        return false;
-    }
-
-    SkASSERT(fColorCount == 2);
-    SkASSERT(fOrigPos == nullptr || (fOrigPos[0] == 0 && fOrigPos[1] == 1));
-
-    SkMatrix dstToPts;
-    if (!ctm.invert(&dstToPts)) {
-        return false;
-    }
-
-    const auto dstToUnit = SkMatrix::Concat(fPtsToUnit, dstToPts);
-
-    auto* m = alloc->makeArrayDefault<float>(9);
-    if (dstToUnit.asAffine(m)) {
-        // TODO: mapping y is not needed; split the matrix stages to save some math?
-        p->append(SkRasterPipeline::matrix_2x3, m);
-    } else {
-        dstToUnit.get9(m);
-        p->append(SkRasterPipeline::matrix_perspective, m);
-    }
-
-    // TODO: clamp/repeat/mirror const 1f stages?
-    auto* limit = alloc->make<float>(1.0f);
-
-    switch (fTileMode) {
-        case kClamp_TileMode:  p->append(SkRasterPipeline:: clamp_x, limit); break;
-        case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit); break;
-        case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit); break;
-    }
-
-    const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
-    const SkColor4f c0 = to_colorspace(fOrigColors4f[0], fColorSpace.get(), cs),
-                    c1 = to_colorspace(fOrigColors4f[1], fColorSpace.get(), cs);
-    const SkPM4f  pmc0 = premulGrad ? c0.premul() : SkPM4f::From4f(Sk4f::Load(&c0)),
-                  pmc1 = premulGrad ? c1.premul() : SkPM4f::From4f(Sk4f::Load(&c1));
-
-    auto* c0_and_dc = alloc->makeArrayDefault<SkPM4f>(2);
-    c0_and_dc[0] = pmc0;
-    c0_and_dc[1] = SkPM4f::From4f(pmc1.to4f() - pmc0.to4f());
-
-    p->append(SkRasterPipeline::linear_gradient_2stops, c0_and_dc);
-
-    if (!premulGrad && !this->colorsAreOpaque()) {
-        p->append(SkRasterPipeline::premul);
-    }
-
-    return true;
-}
-
-// This swizzles SkColor into the same component order as SkPMColor, but does not actually
-// "pre" multiply the color components.
-//
-// This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
-// SkPMColor from the floats, without having to swizzle each time.
-//
-static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
-    return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
-}
-
-SkLinearGradient::LinearGradientContext::LinearGradientContext(
-        const SkLinearGradient& shader, const ContextRec& ctx)
-    : INHERITED(shader, ctx)
-{
-    // setup for Sk4f
-    const int count = shader.fColorCount;
-    SkASSERT(count > 1);
-
-    fRecs.setCount(count);
-    Rec* rec = fRecs.begin();
-    if (shader.fOrigPos) {
-        rec[0].fPos = 0;
-        SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;)   // should never get used
-        for (int i = 1; i < count; ++i) {
-            rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
-            float diff = rec[i].fPos - rec[i - 1].fPos;
-            if (diff > 0) {
-                rec[i].fPosScale = 1.0f / diff;
-            } else {
-                rec[i].fPosScale = 0;
-            }
-        }
-    } else {
-        // no pos specified, so we compute evenly spaced values
-        const float scale = float(count - 1);
-        const float invScale = 1.0f / scale;
-        for (int i = 0; i < count; ++i) {
-            rec[i].fPos = i * invScale;
-            rec[i].fPosScale = scale;
-        }
-    }
-    rec[count - 1].fPos = 1;    // overwrite the last value just to be sure we end at 1.0
-
-    fApplyAlphaAfterInterp = true;
-    if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
-        shader.colorsAreOpaque())
-    {
-        fApplyAlphaAfterInterp = false;
-    }
-
-    if (fApplyAlphaAfterInterp) {
-        // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
-        // interpolate in unpremultiplied space first, and then scale by alpha right before we
-        // convert to SkPMColor bytes.
-        const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
-        const Sk4f scale(1, 1, 1, paintAlpha);
-        for (int i = 0; i < count; ++i) {
-            uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
-            rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
-            if (i > 0) {
-                SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
-            }
-        }
-    } else {
-        // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
-        // of converting the floats down to bytes.
-        unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
-        for (int i = 0; i < count; ++i) {
-            SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
-            pmc = SkAlphaMulQ(pmc, alphaScale);
-            rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
-            if (i > 0) {
-                SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
-            }
-        }
-    }
-}
-
-#define NO_CHECK_ITER               \
-    do {                            \
-    unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
-    SkASSERT(fi <= 0xFF);           \
-    fx += dx;                       \
-    *dstC++ = cache[toggle + fi];   \
-    toggle = next_dither_toggle(toggle); \
-    } while (0)
-
-namespace {
-
-typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
-                                SkPMColor* dstC, const SkPMColor* cache,
-                                int toggle, int count);
-
-// Linear interpolation (lerp) is unnecessary if there are no sharp
-// discontinuities in the gradient - which must be true if there are
-// only 2 colors - but it's cheap.
-void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
-                                    SkPMColor* SK_RESTRICT dstC,
-                                    const SkPMColor* SK_RESTRICT cache,
-                                    int toggle, int count) {
-    // We're a vertical gradient, so no change in a span.
-    // If colors change sharply across the gradient, dithering is
-    // insufficient (it subsamples the color space) and we need to lerp.
-    unsigned fullIndex = proc(SkGradFixedToFixed(fx));
-    unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
-    unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
-
-    int index0 = fi + toggle;
-    int index1 = index0;
-    if (fi < SkGradientShaderBase::kCache32Count - 1) {
-        index1 += 1;
-    }
-    SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
-    index0 ^= SkGradientShaderBase::kDitherStride32;
-    index1 ^= SkGradientShaderBase::kDitherStride32;
-    SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
-    sk_memset32_dither(dstC, lerp, dlerp, count);
-}
-
-void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
-                            SkPMColor* SK_RESTRICT dstC,
-                            const SkPMColor* SK_RESTRICT cache,
-                            int toggle, int count) {
-    SkClampRange range;
-    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
-    range.validate(count);
-
-    if ((count = range.fCount0) > 0) {
-        sk_memset32_dither(dstC,
-            cache[toggle + range.fV0],
-            cache[next_dither_toggle(toggle) + range.fV0],
-            count);
-        dstC += count;
-    }
-    if ((count = range.fCount1) > 0) {
-        int unroll = count >> 3;
-        fx = range.fFx1;
-        for (int i = 0; i < unroll; i++) {
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-        }
-        if ((count &= 7) > 0) {
-            do {
-                NO_CHECK_ITER;
-            } while (--count != 0);
-        }
-    }
-    if ((count = range.fCount2) > 0) {
-        sk_memset32_dither(dstC,
-            cache[toggle + range.fV1],
-            cache[next_dither_toggle(toggle) + range.fV1],
-            count);
-    }
-}
-
-void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
-                             SkPMColor* SK_RESTRICT dstC,
-                             const SkPMColor* SK_RESTRICT cache,
-                             int toggle, int count) {
-    do {
-        unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
-        SkASSERT(fi <= 0xFF);
-        fx += dx;
-        *dstC++ = cache[toggle + fi];
-        toggle = next_dither_toggle(toggle);
-    } while (--count != 0);
-}
-
-void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
-        SkPMColor* SK_RESTRICT dstC,
-        const SkPMColor* SK_RESTRICT cache,
-        int toggle, int count) {
-    do {
-        unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
-        SkASSERT(fi <= 0xFF);
-        fx += dx;
-        *dstC++ = cache[toggle + fi];
-        toggle = next_dither_toggle(toggle);
-    } while (--count != 0);
-}
-
-}
-
-void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                                                        int count) {
-    SkASSERT(count > 0);
-    const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
-
-    if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
-        kLinear_MatrixClass == fDstToIndexClass)
-    {
-        this->shade4_clamp(x, y, dstC, count);
-        return;
-    }
-
-    SkPoint             srcPt;
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = linearGradient.fTileProc;
-    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
-    int                 toggle = init_dither_toggle(x, y);
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkGradFixed dx, fx = SkScalarPinToGradFixed(srcPt.fX);
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
-            // todo: do we need a real/high-precision value for dx here?
-            dx = SkScalarPinToGradFixed(step.fX);
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX());
-        }
-
-        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
-        if (0 == dx) {
-            shadeProc = shadeSpan_linear_vertical_lerp;
-        } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
-            shadeProc = shadeSpan_linear_clamp;
-        } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
-            shadeProc = shadeSpan_linear_mirror;
-        } else {
-            SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
-        }
-        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
-    } else {
-        SkScalar    dstX = SkIntToScalar(x);
-        SkScalar    dstY = SkIntToScalar(y);
-        do {
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
-            SkASSERT(fi <= 0xFFFF);
-            *dstC++ = cache[toggle + (fi >> kCache32Shift)];
-            toggle = next_dither_toggle(toggle);
-            dstX += SK_Scalar1;
-        } while (--count != 0);
-    }
-}
-
-SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
-    if (info) {
-        commonAsAGradient(info);
-        info->fPoint[0] = fStart;
-        info->fPoint[1] = fEnd;
-    }
-    return kLinear_GradientType;
-}
-
-#if SK_SUPPORT_GPU
-
-#include "GrColorSpaceXform.h"
-#include "GrShaderCaps.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "SkGr.h"
-
-/////////////////////////////////////////////////////////////////////
-
-class GrLinearGradient : public GrGradientEffect {
-public:
-    class GLSLLinearProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
-        return sk_sp<GrFragmentProcessor>(new GrLinearGradient(args));
-    }
-
-    ~GrLinearGradient() override {}
-
-    const char* name() const override { return "Linear Gradient"; }
-
-private:
-    GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
-        this->initClassID<GrLinearGradient>();
-    }
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLLinearProcessor(const GrProcessor&) {}
-
-    ~GLSLLinearProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-        b->add32(GenBaseGradientKey(processor));
-    }
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const {
-    return new GrLinearGradient::GLSLLinearProcessor(*this);
-}
-
-void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                             GrProcessorKeyBuilder* b) const {
-    GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
-
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) {
-    SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
-                        {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops,
-                                     params.fColorCount, params.fTileMode) :
-        SkGradientShader::MakeLinear(points, params.fColors, params.fStops,
-                                     params.fColorCount, params.fTileMode);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-/////////////////////////////////////////////////////////////////////
-
-void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) {
-    const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
-    this->emitUniforms(args.fUniformHandler, ge);
-    SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    t.append(".x");
-    this->emitColor(args.fFragBuilder,
-                    args.fUniformHandler,
-                    args.fShaderCaps,
-                    ge,
-                    t.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const {
-    SkASSERT(args.fContext);
-
-    SkMatrix matrix;
-    if (!this->getLocalMatrix().invert(&matrix)) {
-        return nullptr;
-    }
-    if (args.fLocalMatrix) {
-        SkMatrix inv;
-        if (!args.fLocalMatrix->invert(&inv)) {
-            return nullptr;
-        }
-        matrix.postConcat(inv);
-    }
-    matrix.postConcat(fPtsToUnit);
-
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make(
-        GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
-                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-}
-
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkLinearGradient::toString(SkString* str) const {
-    str->append("SkLinearGradient (");
-
-    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
-    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-#include "SkNx.h"
-
-static const SkLinearGradient::LinearGradientContext::Rec*
-find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
-    SkASSERT(tiledX >= 0 && tiledX <= 1);
-
-    SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
-    SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
-    SkASSERT(rec[0].fPos <= rec[1].fPos);
-    rec += 1;
-    while (rec->fPos < tiledX || rec->fPosScale == 0) {
-        SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
-        SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
-        SkASSERT(rec[0].fPos <= rec[1].fPos);
-        rec += 1;
-    }
-    return rec - 1;
-}
-
-static const SkLinearGradient::LinearGradientContext::Rec*
-find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
-    SkASSERT(tiledX >= 0 && tiledX <= 1);
-
-    SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
-    SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
-    SkASSERT(rec[0].fPos <= rec[1].fPos);
-    while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
-        rec -= 1;
-        SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
-        SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
-        SkASSERT(rec[0].fPos <= rec[1].fPos);
-    }
-    return rec;
-}
-
-// As an optimization, we can apply the dither bias before interpolation -- but only when
-// operating in premul space (apply_alpha == false).  When apply_alpha == true, we must
-// defer the bias application until after premul.
-//
-// The following two helpers encapsulate this logic: pre_bias is called before interpolation,
-// and effects the bias when apply_alpha == false, while post_bias is called after premul and
-// effects the bias for the apply_alpha == true case.
-
-template <bool apply_alpha>
-Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) {
-    return apply_alpha ? x : x + bias;
-}
-
-template <bool apply_alpha>
-Sk4f post_bias(const Sk4f& x, const Sk4f& bias) {
-    return apply_alpha ? x + bias : x;
-}
-
-template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) {
-    SkPMColor c;
-    Sk4f c4f255 = x;
-    if (apply_alpha) {
-        const float scale = x[SkPM4f::A] * (1 / 255.f);
-        c4f255 *= Sk4f(scale, scale, scale, 1);
-    }
-    SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c);
-
-    return c;
-}
-
-template <bool apply_alpha> void fill(SkPMColor dst[], int count,
-                                      const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) {
-    const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0);
-    const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1);
-    sk_memset32_dither(dst, c0, c1, count);
-}
-
-template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
-    // Assumes that c4 does not need to be dithered.
-    sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count);
-}
-
-/*
- *  TODOs
- *
- *  - tilemodes
- *  - interp before or after premul
- *  - perspective
- *  - optimizations
- *      - use fixed (32bit or 16bit) instead of floats?
- */
-
-static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
-    SkASSERT(fx >= rec[0].fPos);
-    SkASSERT(fx <= rec[1].fPos);
-
-    const float p0 = rec[0].fPos;
-    const Sk4f c0 = rec[0].fColor;
-    const Sk4f c1 = rec[1].fColor;
-    const Sk4f diffc = c1 - c0;
-    const float scale = rec[1].fPosScale;
-    const float t = (fx - p0) * scale;
-    return c0 + Sk4f(t) * diffc;
-}
-
-template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
-                                      const Sk4f& dither0, const Sk4f& dither1) {
-    Sk4f dc2 = dc + dc;
-    Sk4f dc4 = dc2 + dc2;
-    Sk4f cd0 = pre_bias<apply_alpha>(c     , dither0);
-    Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1);
-    Sk4f cd2 = cd0 + dc2;
-    Sk4f cd3 = cd1 + dc2;
-    while (n >= 4) {
-        if (!apply_alpha) {
-            Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
-            dstC += 4;
-        } else {
-            *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
-            *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
-            *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0);
-            *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1);
-        }
-        cd0 = cd0 + dc4;
-        cd1 = cd1 + dc4;
-        cd2 = cd2 + dc4;
-        cd3 = cd3 + dc4;
-        n -= 4;
-    }
-    if (n & 2) {
-        *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
-        *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
-        cd0 = cd0 + dc2;
-    }
-    if (n & 1) {
-        *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
-    }
-}
-
-template <bool apply_alpha, bool dx_is_pos>
-void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
-                                                              float fx, float dx, float invDx,
-                                                              const float dither[2]) {
-    Sk4f dither0(dither[0]);
-    Sk4f dither1(dither[1]);
-    const Rec* rec = fRecs.begin();
-
-    const Sk4f dx4 = Sk4f(dx);
-    SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
-
-    if (dx_is_pos) {
-        if (fx < 0) {
-            // count is guaranteed to be positive, but the first arg may overflow int32 after
-            // increment => casting to uint32 ensures correct clamping.
-            int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1,
-                                     count);
-            SkASSERT(n > 0);
-            fill<apply_alpha>(dstC, n, rec[0].fColor);
-            count -= n;
-            dstC += n;
-            fx += n * dx;
-            SkASSERT(0 == count || fx >= 0);
-            if (n & 1) {
-                SkTSwap(dither0, dither1);
-            }
-        }
-    } else { // dx < 0
-        if (fx > 1) {
-            // count is guaranteed to be positive, but the first arg may overflow int32 after
-            // increment => casting to uint32 ensures correct clamping.
-            int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1,
-                                     count);
-            SkASSERT(n > 0);
-            fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
-            count -= n;
-            dstC += n;
-            fx += n * dx;
-            SkASSERT(0 == count || fx <= 1);
-            if (n & 1) {
-                SkTSwap(dither0, dither1);
-            }
-        }
-    }
-    SkASSERT(count >= 0);
-
-    const Rec* r;
-    if (dx_is_pos) {
-        r = fRecs.begin();                      // start at the beginning
-    } else {
-        r = fRecs.begin() + fRecs.count() - 2;  // start at the end
-    }
-
-    while (count > 0) {
-        if (dx_is_pos) {
-            if (fx >= 1) {
-                fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
-                return;
-            }
-        } else {    // dx < 0
-            if (fx <= 0) {
-                fill<apply_alpha>(dstC, count, rec[0].fColor);
-                return;
-            }
-        }
-
-        if (dx_is_pos) {
-            r = find_forward(r, fx);
-        } else {
-            r = find_backward(r, fx);
-        }
-        SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
-
-        const float p0 = r[0].fPos;
-        const Sk4f c0 = r[0].fColor;
-        const float p1 = r[1].fPos;
-        const Sk4f diffc = Sk4f(r[1].fColor) - c0;
-        const float scale = r[1].fPosScale;
-        const float t = (fx - p0) * scale;
-        const Sk4f c = c0 + Sk4f(t) * diffc;
-        const Sk4f dc = diffc * dx4 * Sk4f(scale);
-
-        int n;
-        if (dx_is_pos) {
-            n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
-        } else {
-            n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
-        }
-
-        fx += n * dx;
-        // fx should now outside of the p0..p1 interval. However, due to float precision loss,
-        // its possible that fx is slightly too small/large, so we clamp it.
-        if (dx_is_pos) {
-            fx = SkTMax(fx, p1);
-        } else {
-            fx = SkTMin(fx, p0);
-        }
-
-        ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
-        dstC += n;
-        SkASSERT(dstC <= endDstC);
-
-        if (n & 1) {
-            SkTSwap(dither0, dither1);
-        }
-
-        count -= n;
-        SkASSERT(count >= 0);
-    }
-}
-
-void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
-                                                           int count) {
-    SkASSERT(count > 0);
-    SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
-
-    SkPoint srcPt;
-    fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
-    float fx = srcPt.x();
-    const float dx = fDstToIndex.getScaleX();
-
-    // Default our dither bias values to 1/2, (rounding), which is no dithering
-    float dither0 = 0.5f;
-    float dither1 = 0.5f;
-    if (fDither) {
-        const float ditherCell[] = {
-            1/8.0f,   5/8.0f,
-            7/8.0f,   3/8.0f,
-        };
-        const int rowIndex = (y & 1) << 1;
-        dither0 = ditherCell[rowIndex];
-        dither1 = ditherCell[rowIndex + 1];
-        if (x & 1) {
-            SkTSwap(dither0, dither1);
-        }
-    }
-    const float dither[2] = { dither0, dither1 };
-
-    if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
-        const float pinFx = SkTPin(fx, 0.0f, 1.0f);
-        Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
-        if (fApplyAlphaAfterInterp) {
-            fill<true>(dstC, count, c, dither0, dither1);
-        } else {
-            fill<false>(dstC, count, c, dither0, dither1);
-        }
-        return;
-    }
-
-    SkASSERT(0.f != dx);
-    const float invDx = 1 / dx;
-    if (dx > 0) {
-        if (fApplyAlphaAfterInterp) {
-            this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
-        } else {
-            this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
-        }
-    } else {
-        if (fApplyAlphaAfterInterp) {
-            this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
-        } else {
-            this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
-        }
-    }
-}
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
deleted file mode 100644
index 4118dee..0000000
--- a/src/effects/gradients/SkLinearGradient.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkLinearGradient_DEFINED
-#define SkLinearGradient_DEFINED
-
-#include "SkGradientShaderPriv.h"
-#include "SkNx.h"
-
-struct Sk4fStorage {
-    float fArray[4];
-
-    operator Sk4f() const {
-        return Sk4f::Load(fArray);
-    }
-
-    Sk4fStorage& operator=(const Sk4f& src) {
-        src.store(fArray);
-        return *this;
-    }
-};
-
-class SkLinearGradient : public SkGradientShaderBase {
-public:
-    enum {
-        // Temp flag for testing the 4f impl.
-        kForce4fContext_PrivateFlag     = 1 << 7,
-    };
-
-    SkLinearGradient(const SkPoint pts[2], const Descriptor&);
-
-    class LinearGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
-    public:
-        LinearGradientContext(const SkLinearGradient&, const ContextRec&);
-
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-
-        struct Rec {
-            Sk4fStorage fColor;
-            float       fPos;
-            float       fPosScale;
-        };
-    private:
-        SkTDArray<Rec>  fRecs;
-        bool            fApplyAlphaAfterInterp;
-
-        void shade4_clamp(int x, int y, SkPMColor dstC[], int count);
-        template <bool, bool> void shade4_dx_clamp(SkPMColor dstC[], int count, float fx, float dx,
-                                                   float invDx, const float dither[2]);
-
-        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
-    };
-
-    GradientType asAGradient(GradientInfo* info) const override;
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient)
-
-protected:
-    SkLinearGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                        const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
-
-private:
-    class LinearGradient4fContext;
-
-    friend class SkGradientShader;
-    typedef SkGradientShaderBase INHERITED;
-    const SkPoint fStart;
-    const SkPoint fEnd;
-};
-
-#endif
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
deleted file mode 100644
index 2caf905..0000000
--- a/src/effects/gradients/SkRadialGradient.cpp
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRadialGradient.h"
-#include "SkNx.h"
-
-namespace {
-
-// GCC doesn't like using static functions as template arguments.  So force these to be non-static.
-inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
-    return mirror_tileproc(x);
-}
-
-inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
-    return repeat_tileproc(x);
-}
-
-SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
-    SkScalar    inv = SkScalarInvert(radius);
-
-    SkMatrix matrix;
-    matrix.setTranslate(-center.fX, -center.fY);
-    matrix.postScale(inv, inv);
-    return matrix;
-}
-
-
-}  // namespace
-
-/////////////////////////////////////////////////////////////////////
-
-SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
-    : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
-    , fCenter(center)
-    , fRadius(radius) {
-}
-
-SkShader::Context* SkRadialGradient::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const
-{
-    return CheckedMakeContext<RadialGradientContext>(alloc, *this, rec);
-}
-
-SkRadialGradient::RadialGradientContext::RadialGradientContext(
-        const SkRadialGradient& shader, const ContextRec& rec)
-    : INHERITED(shader, rec) {}
-
-SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
-    if (info) {
-        commonAsAGradient(info);
-        info->fPoint[0] = fCenter;
-        info->fRadius[0] = fRadius;
-    }
-    return kRadial_GradientType;
-}
-
-sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
-    DescriptorScope desc;
-    if (!desc.unflatten(buffer)) {
-        return nullptr;
-    }
-    const SkPoint center = buffer.readPoint();
-    const SkScalar radius = buffer.readScalar();
-    return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace),
-                                        desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags,
-                                        desc.fLocalMatrix);
-}
-
-void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writePoint(fCenter);
-    buffer.writeScalar(fRadius);
-}
-
-namespace {
-
-inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
-    // fast, overly-conservative test: checks unit square instead of unit circle
-    bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
-    bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
-    return xClamped || yClamped;
-}
-
-typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        SkPMColor* dstC, const SkPMColor* cache,
-        int count, int toggle);
-
-static inline Sk4f fast_sqrt(const Sk4f& R) {
-    return R * R.rsqrt();
-}
-
-static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
-    return a * a + b * b;
-}
-
-void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
-                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-                             int count, int toggle) {
-    if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
-        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
-        sk_memset32_dither(dstC,
-                           cache[toggle + fi],
-                           cache[next_dither_toggle(toggle) + fi],
-                           count);
-    } else {
-        const Sk4f min(SK_ScalarNearlyZero);
-        const Sk4f max(255);
-        const float scale = 255;
-        sfx *= scale;
-        sfy *= scale;
-        sdx *= scale;
-        sdy *= scale;
-        const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
-        const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
-        const Sk4f dx4(sdx * 4);
-        const Sk4f dy4(sdy * 4);
-
-        Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
-        Sk4f tmpdxdy = sum_squares(dx4, dy4);
-        Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min);
-        Sk4f dR = tmpxy + tmpxy + tmpdxdy;
-        const Sk4f ddR = tmpdxdy + tmpdxdy;
-
-        for (int i = 0; i < (count >> 2); ++i) {
-            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
-            R = Sk4f::Max(R + dR, min);
-            dR = dR + ddR;
-
-            uint8_t fi[4];
-            SkNx_cast<uint8_t>(dist).store(fi);
-
-            for (int i = 0; i < 4; i++) {
-                *dstC++ = cache[toggle + fi[i]];
-                toggle = next_dither_toggle(toggle);
-            }
-        }
-        count &= 3;
-        if (count) {
-            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
-
-            uint8_t fi[4];
-            SkNx_cast<uint8_t>(dist).store(fi);
-            for (int i = 0; i < count; i++) {
-                *dstC++ = cache[toggle + fi[i]];
-                toggle = next_dither_toggle(toggle);
-            }
-        }
-    }
-}
-
-// Unrolling this loop doesn't seem to help (when float); we're stalling to
-// get the results of the sqrt (?), and don't have enough extra registers to
-// have many in flight.
-template <SkFixed (*TileProc)(SkFixed)>
-void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
-                      SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-                      int count, int toggle) {
-    do {
-        const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
-        const unsigned fi = TileProc(dist);
-        SkASSERT(fi <= 0xFFFF);
-        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
-        toggle = next_dither_toggle(toggle);
-        fx += dx;
-        fy += dy;
-    } while (--count != 0);
-}
-
-void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
-                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-                             int count, int toggle) {
-    shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
-}
-
-void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
-                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-                             int count, int toggle) {
-    shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
-}
-
-}  // namespace
-
-void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
-                                                        SkPMColor* SK_RESTRICT dstC, int count) {
-    SkASSERT(count > 0);
-
-    const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
-
-    SkPoint             srcPt;
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = radialGradient.fTileProc;
-    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
-    int toggle = init_dither_toggle(x, y);
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar sdx = fDstToIndex.getScaleX();
-        SkScalar sdy = fDstToIndex.getSkewY();
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
-            sdx = step.fX;
-            sdy = step.fY;
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-        }
-
-        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
-        if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
-            shadeProc = shadeSpan_radial_clamp2;
-        } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
-            shadeProc = shadeSpan_radial_mirror;
-        } else {
-            SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
-        }
-        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
-    } else {    // perspective case
-        SkScalar dstX = SkIntToScalar(x);
-        SkScalar dstY = SkIntToScalar(y);
-        do {
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
-            SkASSERT(fi <= 0xFFFF);
-            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
-            dstX += SK_Scalar1;
-        } while (--count != 0);
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "GrShaderCaps.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-
-class GrRadialGradient : public GrGradientEffect {
-public:
-    class GLSLRadialProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
-        return sk_sp<GrFragmentProcessor>(new GrRadialGradient(args));
-    }
-
-    ~GrRadialGradient() override {}
-
-    const char* name() const override { return "Radial Gradient"; }
-
-private:
-    GrRadialGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
-        this->initClassID<GrRadialGradient>();
-    }
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLRadialProcessor(const GrProcessor&) {}
-    ~GLSLRadialProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-        b->add32(GenBaseGradientKey(processor));
-    }
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-/////////////////////////////////////////////////////////////////////
-
-GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const {
-    return new GrRadialGradient::GLSLRadialProcessor(*this);
-}
-
-void GrRadialGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                             GrProcessorKeyBuilder* b) const {
-    GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
-
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) {
-    sk_sp<SkShader> shader;
-    do {
-        RandomGradientParams params(d->fRandom);
-        SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-        SkScalar radius = d->fRandom->nextUScalar1();
-        shader = params.fUseColors4f
-                         ? SkGradientShader::MakeRadial(center, radius, params.fColors4f,
-                                                        params.fColorSpace, params.fStops,
-                                                        params.fColorCount, params.fTileMode)
-                         : SkGradientShader::MakeRadial(center, radius, params.fColors,
-                                                        params.fStops, params.fColorCount,
-                                                        params.fTileMode);
-    } while (!shader);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-/////////////////////////////////////////////////////////////////////
-
-void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) {
-    const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
-    this->emitUniforms(args.fUniformHandler, ge);
-    SkString t("length(");
-    t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]));
-    t.append(")");
-    this->emitColor(args.fFragBuilder,
-                    args.fUniformHandler,
-                    args.fShaderCaps,
-                    ge, t.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const {
-    SkASSERT(args.fContext);
-
-    SkMatrix matrix;
-    if (!this->getLocalMatrix().invert(&matrix)) {
-        return nullptr;
-    }
-    if (args.fLocalMatrix) {
-        SkMatrix inv;
-        if (!args.fLocalMatrix->invert(&inv)) {
-            return nullptr;
-        }
-        matrix.postConcat(inv);
-    }
-    matrix.postConcat(fPtsToUnit);
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make(
-        GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
-                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkRadialGradient::toString(SkString* str) const {
-    str->append("SkRadialGradient: (");
-
-    str->append("center: (");
-    str->appendScalar(fCenter.fX);
-    str->append(", ");
-    str->appendScalar(fCenter.fY);
-    str->append(") radius: ");
-    str->appendScalar(fRadius);
-    str->append(" ");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h
deleted file mode 100644
index f92fbb3..0000000
--- a/src/effects/gradients/SkRadialGradient.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRadialGradient_DEFINED
-#define SkRadialGradient_DEFINED
-
-#include "SkGradientShaderPriv.h"
-
-class SkRadialGradient : public SkGradientShaderBase {
-public:
-    SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&);
-
-    class RadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
-    public:
-        RadialGradientContext(const SkRadialGradient&, const ContextRec&);
-
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-
-    private:
-        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
-    };
-
-    GradientType asAGradient(GradientInfo* info) const override;
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient)
-
-protected:
-    SkRadialGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    const SkPoint fCenter;
-    const SkScalar fRadius;
-
-    friend class SkGradientShader;
-    typedef SkGradientShaderBase INHERITED;
-};
-
-#endif
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
deleted file mode 100644
index 79013b3..0000000
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkSweepGradient.h"
-
-static SkMatrix translate(SkScalar dx, SkScalar dy) {
-    SkMatrix matrix;
-    matrix.setTranslate(dx, dy);
-    return matrix;
-}
-
-SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor& desc)
-    : SkGradientShaderBase(desc, translate(-cx, -cy))
-    , fCenter(SkPoint::Make(cx, cy))
-{
-    // overwrite the tilemode to a canonical value (since sweep ignores it)
-    fTileMode = SkShader::kClamp_TileMode;
-}
-
-SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
-    if (info) {
-        commonAsAGradient(info);
-        info->fPoint[0] = fCenter;
-    }
-    return kSweep_GradientType;
-}
-
-sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) {
-    DescriptorScope desc;
-    if (!desc.unflatten(buffer)) {
-        return nullptr;
-    }
-    const SkPoint center = buffer.readPoint();
-    return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors,
-                                       std::move(desc.fColorSpace), desc.fPos, desc.fCount,
-                                       desc.fGradFlags, desc.fLocalMatrix);
-}
-
-void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writePoint(fCenter);
-}
-
-SkShader::Context* SkSweepGradient::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const
-{
-    return CheckedMakeContext<SweepGradientContext>(alloc, *this, rec);
-}
-
-SkSweepGradient::SweepGradientContext::SweepGradientContext(
-        const SkSweepGradient& shader, const ContextRec& rec)
-    : INHERITED(shader, rec) {}
-
-//  returns angle in a circle [0..2PI) -> [0..255]
-static unsigned SkATan2_255(float y, float x) {
-    //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
-    static const float g255Over2PI = 40.584510488433314f;
-
-    float result = sk_float_atan2(y, x);
-    if (!SkScalarIsFinite(result)) {
-        return 0;
-    }
-    if (result < 0) {
-        result += 2 * SK_ScalarPI;
-    }
-    SkASSERT(result >= 0);
-    // since our value is always >= 0, we can cast to int, which is faster than
-    // calling floorf()
-    int ir = (int)(result * g255Over2PI);
-    SkASSERT(ir >= 0 && ir <= 255);
-    return ir;
-}
-
-void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                                                      int count) {
-    SkMatrix::MapXYProc proc = fDstToIndexProc;
-    const SkMatrix&     matrix = fDstToIndex;
-    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
-    int                 toggle = init_dither_toggle(x, y);
-    SkPoint             srcPt;
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
-                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar dx, fx = srcPt.fX;
-        SkScalar dy, fy = srcPt.fY;
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            const auto step = matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf);
-            dx = step.fX;
-            dy = step.fY;
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = matrix.getScaleX();
-            dy = matrix.getSkewY();
-        }
-
-        for (; count > 0; --count) {
-            *dstC++ = cache[toggle + SkATan2_255(fy, fx)];
-            fx += dx;
-            fy += dy;
-            toggle = next_dither_toggle(toggle);
-        }
-    } else {  // perspective case
-        for (int stop = x + count; x < stop; x++) {
-            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
-                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-            *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)];
-            toggle = next_dither_toggle(toggle);
-        }
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "GrShaderCaps.h"
-#include "gl/GrGLContext.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-
-class GrSweepGradient : public GrGradientEffect {
-public:
-    class GLSLSweepProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
-        return sk_sp<GrFragmentProcessor>(new GrSweepGradient(args));
-    }
-    ~GrSweepGradient() override {}
-
-    const char* name() const override { return "Sweep Gradient"; }
-
-private:
-    GrSweepGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
-        this->initClassID<GrSweepGradient>();
-    }
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override;
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrSweepGradient::GLSLSweepProcessor : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLSweepProcessor(const GrProcessor&) {}
-    ~GLSLSweepProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-        b->add32(GenBaseGradientKey(processor));
-    }
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-/////////////////////////////////////////////////////////////////////
-
-GrGLSLFragmentProcessor* GrSweepGradient::onCreateGLSLInstance() const {
-    return new GrSweepGradient::GLSLSweepProcessor(*this);
-}
-
-void GrSweepGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                            GrProcessorKeyBuilder* b) const {
-    GrSweepGradient::GLSLSweepProcessor::GenKey(*this, caps, b);
-}
-
-
-/////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSweepGradient);
-
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrSweepGradient::TestCreate(GrProcessorTestData* d) {
-    SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors4f, params.fColorSpace,
-                                    params.fStops, params.fColorCount) :
-        SkGradientShader::MakeSweep(center.fX, center.fY,  params.fColors,
-                                    params.fStops, params.fColorCount);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-/////////////////////////////////////////////////////////////////////
-
-void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) {
-    const GrSweepGradient& ge = args.fFp.cast<GrSweepGradient>();
-    this->emitUniforms(args.fUniformHandler, ge);
-    SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    SkString t;
-    // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
-    if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) {
-        // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
-        // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in
-        // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
-        // handle the undefined behavior of the second paramenter being 0 instead of doing the
-        // divide ourselves and using atan instead.
-        t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)",
-                 coords2D.c_str(), coords2D.c_str(), coords2D.c_str());
-    } else {
-        t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
-                 coords2D.c_str(), coords2D.c_str());
-    }
-    this->emitColor(args.fFragBuilder,
-                    args.fUniformHandler,
-                    args.fShaderCaps,
-                    ge, t.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs& args) const {
-
-    SkMatrix matrix;
-    if (!this->getLocalMatrix().invert(&matrix)) {
-        return nullptr;
-    }
-    if (args.fLocalMatrix) {
-        SkMatrix inv;
-        if (!args.fLocalMatrix->invert(&inv)) {
-            return nullptr;
-        }
-        matrix.postConcat(inv);
-    }
-    matrix.postConcat(fPtsToUnit);
-
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make(
-        GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode,
-                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkSweepGradient::toString(SkString* str) const {
-    str->append("SkSweepGradient: (");
-
-    str->append("center: (");
-    str->appendScalar(fCenter.fX);
-    str->append(", ");
-    str->appendScalar(fCenter.fY);
-    str->append(") ");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
deleted file mode 100644
index 30ebb1ad..0000000
--- a/src/effects/gradients/SkSweepGradient.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkSweepGradient_DEFINED
-#define SkSweepGradient_DEFINED
-
-#include "SkGradientShaderPriv.h"
-
-class SkSweepGradient : public SkGradientShaderBase {
-public:
-    SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&);
-
-    class SweepGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
-    public:
-        SweepGradientContext(const SkSweepGradient& shader, const ContextRec&);
-
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-
-    private:
-        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
-    };
-
-    GradientType asAGradient(GradientInfo* info) const override;
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient)
-
-protected:
-    void flatten(SkWriteBuffer& buffer) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    const SkPoint fCenter;
-
-    friend class SkGradientShader;
-    typedef SkGradientShaderBase INHERITED;
-};
-
-#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
deleted file mode 100644
index a9740aa..0000000
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTwoPointConicalGradient.h"
-
-struct TwoPtRadialContext {
-    const TwoPtRadial&  fRec;
-    float               fRelX, fRelY;
-    const float         fIncX, fIncY;
-    float               fB;
-    const float         fDB;
-
-    TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
-                       SkScalar dfx, SkScalar dfy);
-    SkFixed nextT();
-};
-
-static int valid_divide(float numer, float denom, float* ratio) {
-    SkASSERT(ratio);
-    if (0 == denom) {
-        return 0;
-    }
-    *ratio = numer / denom;
-    return 1;
-}
-
-// Return the number of distinct real roots, and write them into roots[] in
-// ascending order
-static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
-    SkASSERT(roots);
-
-    if (A == 0) {
-        return valid_divide(-C, B, roots);
-    }
-
-    float R = B*B - 4*A*C;
-    if (R < 0) {
-        return 0;
-    }
-    R = sk_float_sqrt(R);
-
-#if 1
-    float Q = B;
-    if (Q < 0) {
-        Q -= R;
-    } else {
-        Q += R;
-    }
-#else
-    // on 10.6 this was much slower than the above branch :(
-    float Q = B + copysignf(R, B);
-#endif
-    Q *= -0.5f;
-    if (0 == Q) {
-        roots[0] = 0;
-        return 1;
-    }
-
-    float r0 = Q / A;
-    float r1 = C / Q;
-    roots[0] = r0 < r1 ? r0 : r1;
-    roots[1] = r0 > r1 ? r0 : r1;
-    if (descendingOrder) {
-        SkTSwap(roots[0], roots[1]);
-    }
-    return 2;
-}
-
-static float lerp(float x, float dx, float t) {
-    return x + t * dx;
-}
-
-static float sqr(float x) { return x * x; }
-
-void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
-                       const SkPoint& center1, SkScalar rad1,
-                       bool flipped) {
-    fCenterX = SkScalarToFloat(center0.fX);
-    fCenterY = SkScalarToFloat(center0.fY);
-    fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
-    fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
-    fRadius = SkScalarToFloat(rad0);
-    fDRadius = SkScalarToFloat(rad1) - fRadius;
-
-    fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
-    fRadius2 = sqr(fRadius);
-    fRDR = fRadius * fDRadius;
-
-    fFlipped = flipped;
-}
-
-TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
-                                       SkScalar dfx, SkScalar dfy)
-    : fRec(rec)
-    , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
-    , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
-    , fIncX(SkScalarToFloat(dfx))
-    , fIncY(SkScalarToFloat(dfy))
-    , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
-    , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
-
-SkFixed TwoPtRadialContext::nextT() {
-    float roots[2];
-
-    float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
-    int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
-
-    fRelX += fIncX;
-    fRelY += fIncY;
-    fB += fDB;
-
-    if (0 == countRoots) {
-        return TwoPtRadial::kDontDrawT;
-    }
-
-    // Prefer the bigger t value if both give a radius(t) > 0
-    // find_quad_roots returns the values sorted, so we start with the last
-    float t = roots[countRoots - 1];
-    float r = lerp(fRec.fRadius, fRec.fDRadius, t);
-    if (r < 0) {
-        t = roots[0];   // might be the same as roots[countRoots-1]
-        r = lerp(fRec.fRadius, fRec.fDRadius, t);
-        if (r < 0) {
-            return TwoPtRadial::kDontDrawT;
-        }
-    }
-    return SkFloatToFixed(t);
-}
-
-typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
-                                    const SkPMColor* cache, int toggle, int count);
-
-static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
-                           const SkPMColor* SK_RESTRICT cache, int toggle,
-                           int count) {
-    for (; count > 0; --count) {
-        SkFixed t = rec->nextT();
-        if (TwoPtRadial::DontDrawT(t)) {
-            *dstC++ = 0;
-        } else {
-            SkFixed index = SkClampMax(t, 0xFFFF);
-            SkASSERT(index <= 0xFFFF);
-            *dstC++ = cache[toggle +
-                            (index >> SkGradientShaderBase::kCache32Shift)];
-        }
-        toggle = next_dither_toggle(toggle);
-    }
-}
-
-static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
-                            const SkPMColor* SK_RESTRICT cache, int toggle,
-                            int count) {
-    for (; count > 0; --count) {
-        SkFixed t = rec->nextT();
-        if (TwoPtRadial::DontDrawT(t)) {
-            *dstC++ = 0;
-        } else {
-            SkFixed index = repeat_tileproc(t);
-            SkASSERT(index <= 0xFFFF);
-            *dstC++ = cache[toggle +
-                            (index >> SkGradientShaderBase::kCache32Shift)];
-        }
-        toggle = next_dither_toggle(toggle);
-    }
-}
-
-static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
-                            const SkPMColor* SK_RESTRICT cache, int toggle,
-                            int count) {
-    for (; count > 0; --count) {
-        SkFixed t = rec->nextT();
-        if (TwoPtRadial::DontDrawT(t)) {
-            *dstC++ = 0;
-        } else {
-            SkFixed index = mirror_tileproc(t);
-            SkASSERT(index <= 0xFFFF);
-            *dstC++ = cache[toggle +
-                            (index >> SkGradientShaderBase::kCache32Shift)];
-        }
-        toggle = next_dither_toggle(toggle);
-    }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-SkTwoPointConicalGradient::SkTwoPointConicalGradient(
-        const SkPoint& start, SkScalar startRadius,
-        const SkPoint& end, SkScalar endRadius,
-        bool flippedGrad, const Descriptor& desc)
-    : SkGradientShaderBase(desc, SkMatrix::I())
-    , fCenter1(start)
-    , fCenter2(end)
-    , fRadius1(startRadius)
-    , fRadius2(endRadius)
-    , fFlippedGrad(flippedGrad)
-{
-    // this is degenerate, and should be caught by our caller
-    SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
-    fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
-}
-
-bool SkTwoPointConicalGradient::isOpaque() const {
-    // Because areas outside the cone are left untouched, we cannot treat the
-    // shader as opaque even if the gradient itself is opaque.
-    // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
-    return false;
-}
-
-SkShader::Context* SkTwoPointConicalGradient::onMakeContext(
-    const ContextRec& rec, SkArenaAlloc* alloc) const {
-    return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec);
-}
-
-SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
-        const SkTwoPointConicalGradient& shader, const ContextRec& rec)
-    : INHERITED(shader, rec)
-{
-    // in general, we might discard based on computed-radius, so clear
-    // this flag (todo: sometimes we can detect that we never discard...)
-    fFlags &= ~kOpaqueAlpha_Flag;
-}
-
-void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
-        int x, int y, SkPMColor* dstCParam, int count) {
-    const SkTwoPointConicalGradient& twoPointConicalGradient =
-            static_cast<const SkTwoPointConicalGradient&>(fShader);
-
-    int toggle = init_dither_toggle(x, y);
-
-    SkASSERT(count > 0);
-
-    SkPMColor* SK_RESTRICT dstC = dstCParam;
-
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-
-    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
-
-    TwoPointConicalProc shadeProc = twopoint_repeat;
-    if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
-        shadeProc = twopoint_clamp;
-    } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
-        shadeProc = twopoint_mirror;
-    } else {
-        SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
-    }
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        SkPoint srcPt;
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar dx, fx = srcPt.fX;
-        SkScalar dy, fy = srcPt.fY;
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
-            dx = step.fX;
-            dy = step.fY;
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = fDstToIndex.getScaleX();
-            dy = fDstToIndex.getSkewY();
-        }
-
-        TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
-        (*shadeProc)(&rec, dstC, cache, toggle, count);
-    } else {    // perspective case
-        SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
-        SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
-        for (; count > 0; --count) {
-            SkPoint srcPt;
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
-            (*shadeProc)(&rec, dstC, cache, toggle, 1);
-
-            dstX += SK_Scalar1;
-            toggle = next_dither_toggle(toggle);
-            dstC += 1;
-        }
-    }
-}
-
-// Returns the original non-sorted version of the gradient
-SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
-    GradientInfo* info) const {
-    if (info) {
-        commonAsAGradient(info, fFlippedGrad);
-        info->fPoint[0] = fCenter1;
-        info->fPoint[1] = fCenter2;
-        info->fRadius[0] = fRadius1;
-        info->fRadius[1] = fRadius2;
-        if (fFlippedGrad) {
-            SkTSwap(info->fPoint[0], info->fPoint[1]);
-            SkTSwap(info->fRadius[0], info->fRadius[1]);
-        }
-    }
-    return kConical_GradientType;
-}
-
-sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
-    DescriptorScope desc;
-    if (!desc.unflatten(buffer)) {
-        return nullptr;
-    }
-    SkPoint c1 = buffer.readPoint();
-    SkPoint c2 = buffer.readPoint();
-    SkScalar r1 = buffer.readScalar();
-    SkScalar r2 = buffer.readScalar();
-
-    if (buffer.readBool()) {    // flipped
-        SkTSwap(c1, c2);
-        SkTSwap(r1, r2);
-
-        SkColor4f* colors = desc.mutableColors();
-        SkScalar* pos = desc.mutablePos();
-        const int last = desc.fCount - 1;
-        const int half = desc.fCount >> 1;
-        for (int i = 0; i < half; ++i) {
-            SkTSwap(colors[i], colors[last - i]);
-            if (pos) {
-                SkScalar tmp = pos[i];
-                pos[i] = SK_Scalar1 - pos[last - i];
-                pos[last - i] = SK_Scalar1 - tmp;
-            }
-        }
-        if (pos) {
-            if (desc.fCount & 1) {
-                pos[half] = SK_Scalar1 - pos[half];
-            }
-        }
-    }
-
-    return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
-                                                 std::move(desc.fColorSpace), desc.fPos,
-                                                 desc.fCount, desc.fTileMode, desc.fGradFlags,
-                                                 desc.fLocalMatrix);
-}
-
-void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writePoint(fCenter1);
-    buffer.writePoint(fCenter2);
-    buffer.writeScalar(fRadius1);
-    buffer.writeScalar(fRadius2);
-    buffer.writeBool(fFlippedGrad);
-}
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "SkTwoPointConicalGradient_gpu.h"
-
-sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
-        const AsFPArgs& args) const {
-    SkASSERT(args.fContext);
-    SkASSERT(fPtsToUnit.isIdentity());
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
-        GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
-                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
-    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkTwoPointConicalGradient::toString(SkString* str) const {
-    str->append("SkTwoPointConicalGradient: (");
-
-    str->append("center1: (");
-    str->appendScalar(fCenter1.fX);
-    str->append(", ");
-    str->appendScalar(fCenter1.fY);
-    str->append(") radius1: ");
-    str->appendScalar(fRadius1);
-    str->append(" ");
-
-    str->append("center2: (");
-    str->appendScalar(fCenter2.fX);
-    str->append(", ");
-    str->appendScalar(fCenter2.fY);
-    str->append(") radius2: ");
-    str->appendScalar(fRadius2);
-    str->append(" ");
-
-    this->INHERITED::toString(str);
-
-    str->append(")");
-}
-#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h
deleted file mode 100644
index d73ba11..0000000
--- a/src/effects/gradients/SkTwoPointConicalGradient.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkTwoPointConicalGradient_DEFINED
-#define SkTwoPointConicalGradient_DEFINED
-
-#include "SkGradientShaderPriv.h"
-
-// TODO(dominikg): Worth making it truly immutable (i.e. set values in constructor)?
-// Should only be initialized once via init(). Immutable afterwards.
-struct TwoPtRadial {
-    enum {
-        // This value is outside the range SK_FixedMin to SK_FixedMax.
-        kDontDrawT  = 0x80000000
-    };
-
-    float   fCenterX, fCenterY;
-    float   fDCenterX, fDCenterY;
-    float   fRadius;
-    float   fDRadius;
-    float   fA;
-    float   fRadius2;
-    float   fRDR;
-    bool    fFlipped;
-
-    void init(const SkPoint& center0, SkScalar rad0,
-              const SkPoint& center1, SkScalar rad1,
-              bool flipped);
-
-    static bool DontDrawT(SkFixed t) {
-        return kDontDrawT == (uint32_t)t;
-    }
-};
-
-
-class SkTwoPointConicalGradient : public SkGradientShaderBase {
-    TwoPtRadial fRec;
-public:
-    SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius,
-                              const SkPoint& end, SkScalar endRadius,
-                              bool flippedGrad, const Descriptor&);
-
-    class TwoPointConicalGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
-    public:
-        TwoPointConicalGradientContext(const SkTwoPointConicalGradient&, const ContextRec&);
-        ~TwoPointConicalGradientContext() override {}
-
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
-
-    private:
-        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
-    };
-
-    SkShader::GradientType asAGradient(GradientInfo* info) const  override;
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-    bool isOpaque() const override;
-
-    SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
-    SkScalar getStartRadius() const { return fRadius1; }
-    SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
-    const SkPoint& getStartCenter() const { return fCenter1; }
-    const SkPoint& getEndCenter() const { return fCenter2; }
-    SkScalar getEndRadius() const { return fRadius2; }
-    bool isFlippedGrad() const { return fFlippedGrad; }
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient)
-
-protected:
-    SkTwoPointConicalGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-
-private:
-    SkPoint fCenter1;
-    SkPoint fCenter2;
-    SkScalar fRadius1;
-    SkScalar fRadius2;
-    bool fFlippedGrad;
-
-    friend class SkGradientShader;
-    typedef SkGradientShaderBase INHERITED;
-};
-
-#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
deleted file mode 100644
index 9ac8528..0000000
--- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ /dev/null
@@ -1,1346 +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 "SkTwoPointConicalGradient.h"
-
-#if SK_SUPPORT_GPU
-#include "GrCoordTransform.h"
-#include "GrPaint.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#include "SkTwoPointConicalGradient_gpu.h"
-
-// For brevity
-typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-
-static const SkScalar kErrorTol = 0.00001f;
-static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
-
-/**
- * We have three general cases for 2pt conical gradients. First we always assume that
- * the start radius <= end radius. Our first case (kInside_) is when the start circle
- * is completely enclosed by the end circle. The second case (kOutside_) is the case
- * when the start circle is either completely outside the end circle or the circles
- * overlap. The final case (kEdge_) is when the start circle is inside the end one,
- * but the two are just barely touching at 1 point along their edges.
- */
-enum ConicalType {
-    kInside_ConicalType,
-    kOutside_ConicalType,
-    kEdge_ConicalType,
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
-                                    SkMatrix* invLMatrix) {
-    // Inverse of the current local matrix is passed in then,
-    // translate to center1, rotate so center2 is on x axis.
-    const SkPoint& center1 = shader.getStartCenter();
-    const SkPoint& center2 = shader.getEndCenter();
-
-    invLMatrix->postTranslate(-center1.fX, -center1.fY);
-
-    SkPoint diff = center2 - center1;
-    SkScalar diffLen = diff.length();
-    if (0 != diffLen) {
-        SkScalar invDiffLen = SkScalarInvert(diffLen);
-        SkMatrix rot;
-        rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX);
-        invLMatrix->postConcat(rot);
-    }
-}
-
-class Edge2PtConicalEffect : public GrGradientEffect {
-public:
-    class GLSLEdge2PtConicalProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
-        return sk_sp<GrFragmentProcessor>(new Edge2PtConicalEffect(args));
-    }
-
-    ~Edge2PtConicalEffect() override {}
-
-    const char* name() const override {
-        return "Two-Point Conical Gradient Edge Touching";
-    }
-
-    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
-    SkScalar center() const { return fCenterX1; }
-    SkScalar diffRadius() const { return fDiffRadius; }
-    SkScalar radius() const { return fRadius0; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
-        return (INHERITED::onIsEqual(sBase) &&
-                this->fCenterX1 == s.fCenterX1 &&
-                this->fRadius0 == s.fRadius0 &&
-                this->fDiffRadius == s.fDiffRadius);
-    }
-
-    Edge2PtConicalEffect(const CreateArgs& args)
-            : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) {
-        const SkTwoPointConicalGradient& shader =
-            *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
-        fCenterX1 = shader.getCenterX1();
-        fRadius0 = shader.getStartRadius();
-        fDiffRadius = shader.getDiffRadius();
-        this->initClassID<Edge2PtConicalEffect>();
-        // We should only be calling this shader if we are degenerate case with touching circles
-        // When deciding if we are in edge case, we scaled by the end radius for cases when the
-        // start radius was close to zero, otherwise we scaled by the start radius.  In addition
-        // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
-        // need the sqrt value below
-        SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
-                 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
-                                         fRadius0 * sqrt(kEdgeErrorTol)));
-
-        // We pass the linear part of the quadratic as a varying.
-        //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
-        fBTransform = this->getCoordTransform();
-        SkMatrix& bMatrix = *fBTransform.accessMatrix();
-        SkScalar r0dr = fRadius0 * fDiffRadius;
-        bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] +
-                                            r0dr * bMatrix[SkMatrix::kMPersp0]);
-        bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] +
-                                           r0dr * bMatrix[SkMatrix::kMPersp1]);
-        bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] +
-                                            r0dr * bMatrix[SkMatrix::kMPersp2]);
-        this->addCoordTransform(&fBTransform);
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    // @{
-    // Cache of values - these can change arbitrarily, EXCEPT
-    // we shouldn't change between degenerate and non-degenerate?!
-
-    GrCoordTransform fBTransform;
-    SkScalar         fCenterX1;
-    SkScalar         fRadius0;
-    SkScalar         fDiffRadius;
-
-    // @}
-
-    typedef GrGradientEffect INHERITED;
-};
-
-class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLEdge2PtConicalProcessor(const GrProcessor&);
-    ~GLSLEdge2PtConicalProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-    UniformHandle fParamUni;
-
-    const char* fVSVaryingName;
-    const char* fFSVaryingName;
-
-    // @{
-    /// Values last uploaded as uniforms
-
-    SkScalar fCachedRadius;
-    SkScalar fCachedDiffRadius;
-
-    // @}
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                 GrProcessorKeyBuilder* b) const {
-    Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
-    return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
-    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-    SkScalar radius1 = d->fRandom->nextUScalar1();
-    SkPoint center2;
-    SkScalar radius2;
-    do {
-        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
-        // If the circles are identical the factory will give us an empty shader.
-        // This will happen if we pick identical centers
-    } while (center1 == center2);
-
-    // Below makes sure that circle one is contained within circle two
-    // and both circles are touching on an edge
-    SkPoint diff = center2 - center1;
-    SkScalar diffLen = diff.length();
-    radius2 = radius1 + diffLen;
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors4f, params.fColorSpace, params.fStops,
-                                              params.fColorCount, params.fTileMode) :
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors, params.fStops,
-                                              params.fColorCount, params.fTileMode);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&)
-    : fVSVaryingName(nullptr)
-    , fFSVaryingName(nullptr)
-    , fCachedRadius(-SK_ScalarMax)
-    , fCachedDiffRadius(-SK_ScalarMax) {}
-
-void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) {
-    const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    this->emitUniforms(uniformHandler, ge);
-    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                           kVec3f_GrSLType, kDefault_GrSLPrecision,
-                                           "Conical2FSParams");
-
-    SkString cName("c");
-    SkString tName("t");
-    SkString p0; // start radius
-    SkString p1; // start radius squared
-    SkString p2; // difference in radii (r1 - r0)
-
-
-    p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-    p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-    p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-
-    // We interpolate the linear component in coords[1].
-    SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType());
-    const char* coords2D;
-    SkString bVar;
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    if (kVec3f_GrSLType == args.fTransformedCoords[0].getType()) {
-        fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
-                                 args.fTransformedCoords[0].c_str(),
-                                 args.fTransformedCoords[0].c_str(),
-                                 args.fTransformedCoords[1].c_str(),
-                                 args.fTransformedCoords[1].c_str());
-        coords2D = "interpolants.xy";
-        bVar = "interpolants.z";
-    } else {
-        coords2D = args.fTransformedCoords[0].c_str();
-        bVar.printf("%s.x", args.fTransformedCoords[1].c_str());
-    }
-
-    // output will default to transparent black (we simply won't write anything
-    // else to it if invalid, instead of discarding or returning prematurely)
-    fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-
-    // c = (x^2)+(y^2) - params[1]
-    fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
-                           cName.c_str(), coords2D, coords2D, p1.c_str());
-
-    // linear case: t = -c/b
-    fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
-                           cName.c_str(), bVar.c_str());
-
-    // if r(t) > 0, then t will be the x coordinate
-    fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
-                           p2.c_str(), p0.c_str());
-    fragBuilder->codeAppend("\t");
-    this->emitColor(fragBuilder,
-                    uniformHandler,
-                    args.fShaderCaps,
-                    ge,
-                    tName.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-    fragBuilder->codeAppend("\t}\n");
-}
-
-void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData(
-                    const GrGLSLProgramDataManager& pdman,
-                    const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-    const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
-    SkScalar radius0 = data.radius();
-    SkScalar diffRadius = data.diffRadius();
-
-    if (fCachedRadius != radius0 ||
-        fCachedDiffRadius != diffRadius) {
-
-        pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius);
-        fCachedRadius = radius0;
-        fCachedDiffRadius = diffRadius;
-    }
-}
-
-void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(const GrProcessor& processor,
-                                    const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-    b->add32(GenBaseGradientKey(processor));
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Focal Conical Gradients
-//////////////////////////////////////////////////////////////////////////////
-
-static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
-                                            SkMatrix* invLMatrix, SkScalar* focalX) {
-    // Inverse of the current local matrix is passed in then,
-    // translate, scale, and rotate such that endCircle is unit circle on x-axis,
-    // and focal point is at the origin.
-    ConicalType conicalType;
-    const SkPoint& focal = shader.getStartCenter();
-    const SkPoint& centerEnd = shader.getEndCenter();
-    SkScalar radius = shader.getEndRadius();
-    SkScalar invRadius = 1.f / radius;
-
-    SkMatrix matrix;
-
-    matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
-    matrix.postScale(invRadius, invRadius);
-
-    SkPoint focalTrans;
-    matrix.mapPoints(&focalTrans, &focal, 1);
-    *focalX = focalTrans.length();
-
-    if (0.f != *focalX) {
-        SkScalar invFocalX = SkScalarInvert(*focalX);
-        SkMatrix rot;
-        rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX);
-        matrix.postConcat(rot);
-    }
-
-    matrix.postTranslate(-(*focalX), 0.f);
-
-    // If the focal point is touching the edge of the circle it will
-    // cause a degenerate case that must be handled separately
-    // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
-    // stability trade off versus the linear approx used in the Edge Shader
-    if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
-        return kEdge_ConicalType;
-    }
-
-    // Scale factor 1 / (1 - focalX * focalX)
-    SkScalar oneMinusF2 = 1.f - *focalX * *focalX;
-    SkScalar s = SkScalarInvert(oneMinusF2);
-
-
-    if (s >= 0.f) {
-        conicalType = kInside_ConicalType;
-        matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
-    } else {
-        conicalType = kOutside_ConicalType;
-        matrix.postScale(s, s);
-    }
-
-    invLMatrix->postConcat(matrix);
-
-    return conicalType;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class FocalOutside2PtConicalEffect : public GrGradientEffect {
-public:
-    class GLSLFocalOutside2PtConicalProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
-        return sk_sp<GrFragmentProcessor>(
-            new FocalOutside2PtConicalEffect(args, focalX));
-    }
-
-    ~FocalOutside2PtConicalEffect() override {}
-
-    const char* name() const override {
-        return "Two-Point Conical Gradient Focal Outside";
-    }
-
-    bool isFlipped() const { return fIsFlipped; }
-    SkScalar focal() const { return fFocalX; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
-        return (INHERITED::onIsEqual(sBase) &&
-                this->fFocalX == s.fFocalX &&
-                this->fIsFlipped == s.fIsFlipped);
-    }
-
-    static bool IsFlipped(const CreateArgs& args) {
-        // eww.
-        return static_cast<const SkTwoPointConicalGradient*>(args.fShader)->isFlippedGrad();
-    }
-
-    FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
-            : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
-            , fFocalX(focalX)
-            , fIsFlipped(IsFlipped(args)) {
-        this->initClassID<FocalOutside2PtConicalEffect>();
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    SkScalar         fFocalX;
-    bool             fIsFlipped;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
-    : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLFocalOutside2PtConicalProcessor(const GrProcessor&);
-    ~GLSLFocalOutside2PtConicalProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-    UniformHandle fParamUni;
-
-    const char* fVSVaryingName;
-    const char* fFSVaryingName;
-
-    bool fIsFlipped;
-
-    // @{
-    /// Values last uploaded as uniforms
-
-    SkScalar fCachedFocal;
-
-    // @}
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                         GrProcessorKeyBuilder* b) const {
-    FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
-    return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
-    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-    SkScalar radius1 = 0.f;
-    SkPoint center2;
-    SkScalar radius2;
-    do {
-        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
-        // Need to make sure the centers are not the same or else focal point will be inside
-    } while (center1 == center2);
-
-    SkPoint diff = center2 - center1;
-    SkScalar diffLen = diff.length();
-    // Below makes sure that the focal point is not contained within circle two
-    radius2 = d->fRandom->nextRangeF(0.f, diffLen);
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors4f, params.fColorSpace, params.fStops,
-                                              params.fColorCount, params.fTileMode) :
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors, params.fStops,
-                                              params.fColorCount, params.fTileMode);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
-                            ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor)
-    : fVSVaryingName(nullptr)
-    , fFSVaryingName(nullptr)
-    , fCachedFocal(SK_ScalarMax) {
-    const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
-    fIsFlipped = data.isFlipped();
-}
-
-void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
-    const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    this->emitUniforms(uniformHandler, ge);
-    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                           kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                           "Conical2FSParams");
-    SkString tName("t");
-    SkString p0; // focalX
-    SkString p1; // 1 - focalX * focalX
-
-    p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-    p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-
-    // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    const char* coords2D = coords2DString.c_str();
-
-    // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
-
-    // output will default to transparent black (we simply won't write anything
-    // else to it if invalid, instead of discarding or returning prematurely)
-    fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-
-    fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
-    fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
-    fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
-
-    // Must check to see if we flipped the circle order (to make sure start radius < end radius)
-    // If so we must also flip sign on sqrt
-    if (!fIsFlipped) {
-        fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  + sqrt(d);\n", tName.c_str(),
-                                 coords2D, p0.c_str());
-    } else {
-        fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  - sqrt(d);\n", tName.c_str(),
-                                 coords2D, p0.c_str());
-    }
-
-    fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
-    fragBuilder->codeAppend("\t\t");
-    this->emitColor(fragBuilder,
-                    uniformHandler,
-                    args.fShaderCaps,
-                    ge,
-                    tName.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-    fragBuilder->codeAppend("\t}\n");
-}
-
-void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData(
-                                            const GrGLSLProgramDataManager& pdman,
-                                            const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-    const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
-    SkASSERT(data.isFlipped() == fIsFlipped);
-    SkScalar focal = data.focal();
-
-    if (fCachedFocal != focal) {
-        SkScalar oneMinus2F = 1.f - focal * focal;
-
-        pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F));
-        fCachedFocal = focal;
-    }
-}
-
-void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(
-                                            const GrProcessor& processor,
-                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-    uint32_t* key = b->add32n(2);
-    key[0] = GenBaseGradientKey(processor);
-    key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class FocalInside2PtConicalEffect : public GrGradientEffect {
-public:
-    class GLSLFocalInside2PtConicalProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
-        return sk_sp<GrFragmentProcessor>(
-            new FocalInside2PtConicalEffect(args, focalX));
-    }
-
-    ~FocalInside2PtConicalEffect() override {}
-
-    const char* name() const override {
-        return "Two-Point Conical Gradient Focal Inside";
-    }
-
-    SkScalar focal() const { return fFocalX; }
-
-    typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor;
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
-        return (INHERITED::onIsEqual(sBase) &&
-                this->fFocalX == s.fFocalX);
-    }
-
-    FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
-            : INHERITED(args, args.fShader->colorsAreOpaque()), fFocalX(focalX) {
-        this->initClassID<FocalInside2PtConicalEffect>();
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    SkScalar         fFocalX;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor 
-    : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLFocalInside2PtConicalProcessor(const GrProcessor&);
-    ~GLSLFocalInside2PtConicalProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-    UniformHandle fFocalUni;
-
-    const char* fVSVaryingName;
-    const char* fFSVaryingName;
-
-    // @{
-    /// Values last uploaded as uniforms
-
-    SkScalar fCachedFocal;
-
-    // @}
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                        GrProcessorKeyBuilder* b) const {
-    FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
-    return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
-    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-    SkScalar radius1 = 0.f;
-    SkPoint center2;
-    SkScalar radius2;
-    do {
-        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
-        // Below makes sure radius2 is larger enouch such that the focal point
-        // is inside the end circle
-        SkScalar increase = d->fRandom->nextUScalar1();
-        SkPoint diff = center2 - center1;
-        SkScalar diffLen = diff.length();
-        radius2 = diffLen + increase;
-        // If the circles are identical the factory will give us an empty shader.
-    } while (radius1 == radius2 && center1 == center2);
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors4f, params.fColorSpace, params.fStops,
-                                              params.fColorCount, params.fTileMode) :
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors, params.fStops,
-                                              params.fColorCount, params.fTileMode);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
-                           ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&)
-    : fVSVaryingName(nullptr)
-    , fFSVaryingName(nullptr)
-    , fCachedFocal(SK_ScalarMax) {}
-
-void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) {
-    const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    this->emitUniforms(uniformHandler, ge);
-    fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                           kFloat_GrSLType, kDefault_GrSLPrecision,
-                                           "Conical2FSParams");
-    SkString tName("t");
-
-    // this is the distance along x-axis from the end center to focal point in
-    // transformed coordinates
-    GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
-
-    // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    const char* coords2D = coords2DString.c_str();
-
-    // t = p.x * focalX + length(p)
-    fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  + length(%s);\n", tName.c_str(),
-                             coords2D, focal.c_str(), coords2D);
-
-    this->emitColor(fragBuilder,
-                    uniformHandler,
-                    args.fShaderCaps,
-                    ge,
-                    tName.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-}
-
-void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData(
-                                            const GrGLSLProgramDataManager& pdman,
-                                            const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-    const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
-    SkScalar focal = data.focal();
-
-    if (fCachedFocal != focal) {
-        pdman.set1f(fFocalUni, SkScalarToFloat(focal));
-        fCachedFocal = focal;
-    }
-}
-
-void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(
-                                            const GrProcessor& processor,
-                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-    b->add32(GenBaseGradientKey(processor));
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Circle Conical Gradients
-//////////////////////////////////////////////////////////////////////////////
-
-struct CircleConicalInfo {
-    SkPoint fCenterEnd;
-    SkScalar fA;
-    SkScalar fB;
-    SkScalar fC;
-};
-
-// Returns focal distance along x-axis in transformed coords
-static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
-                                             SkMatrix* invLMatrix, CircleConicalInfo* info) {
-    // Inverse of the current local matrix is passed in then,
-    // translate and scale such that start circle is on the origin and has radius 1
-    const SkPoint& centerStart = shader.getStartCenter();
-    const SkPoint& centerEnd = shader.getEndCenter();
-    SkScalar radiusStart = shader.getStartRadius();
-    SkScalar radiusEnd = shader.getEndRadius();
-
-    SkMatrix matrix;
-
-    matrix.setTranslate(-centerStart.fX, -centerStart.fY);
-
-    SkScalar invStartRad = 1.f / radiusStart;
-    matrix.postScale(invStartRad, invStartRad);
-
-    radiusEnd /= radiusStart;
-
-    SkPoint centerEndTrans;
-    matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
-
-    SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
-                 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
-
-    // Check to see if start circle is inside end circle with edges touching.
-    // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
-    // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
-    // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
-    // still accurate.
-    if (SkScalarAbs(A) < kEdgeErrorTol) {
-        return kEdge_ConicalType;
-    }
-
-    SkScalar C = 1.f / A;
-    SkScalar B = (radiusEnd - 1.f) * C;
-
-    matrix.postScale(C, C);
-
-    invLMatrix->postConcat(matrix);
-
-    info->fCenterEnd = centerEndTrans;
-    info->fA = A;
-    info->fB = B;
-    info->fC = C;
-
-    // if A ends up being negative, the start circle is contained completely inside the end cirlce
-    if (A < 0.f) {
-        return kInside_ConicalType;
-    }
-    return kOutside_ConicalType;
-}
-
-class CircleInside2PtConicalEffect : public GrGradientEffect {
-public:
-    class GLSLCircleInside2PtConicalProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
-        return sk_sp<GrFragmentProcessor>(
-            new CircleInside2PtConicalEffect(args, info));
-    }
-
-    ~CircleInside2PtConicalEffect() override {}
-
-    const char* name() const override { return "Two-Point Conical Gradient Inside"; }
-
-    SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
-    SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
-    SkScalar A() const { return fInfo.fA; }
-    SkScalar B() const { return fInfo.fB; }
-    SkScalar C() const { return fInfo.fC; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
-        return (INHERITED::onIsEqual(sBase) &&
-                this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
-                this->fInfo.fA == s.fInfo.fA &&
-                this->fInfo.fB == s.fInfo.fB &&
-                this->fInfo.fC == s.fInfo.fC);
-    }
-
-    CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
-            : INHERITED(args, args.fShader->colorsAreOpaque()), fInfo(info) {
-        this->initClassID<CircleInside2PtConicalEffect>();
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    const CircleConicalInfo fInfo;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor 
-    : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLCircleInside2PtConicalProcessor(const GrProcessor&);
-    ~GLSLCircleInside2PtConicalProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-    UniformHandle fCenterUni;
-    UniformHandle fParamUni;
-
-    const char* fVSVaryingName;
-    const char* fFSVaryingName;
-
-    // @{
-    /// Values last uploaded as uniforms
-
-    SkScalar fCachedCenterX;
-    SkScalar fCachedCenterY;
-    SkScalar fCachedA;
-    SkScalar fCachedB;
-    SkScalar fCachedC;
-
-    // @}
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                         GrProcessorKeyBuilder* b) const {
-    CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
-    return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
-    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-    SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
-    SkPoint center2;
-    SkScalar radius2;
-    do {
-        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
-        // Below makes sure that circle one is contained within circle two
-        SkScalar increase = d->fRandom->nextUScalar1();
-        SkPoint diff = center2 - center1;
-        SkScalar diffLen = diff.length();
-        radius2 = radius1 + diffLen + increase;
-        // If the circles are identical the factory will give us an empty shader.
-    } while (radius1 == radius2 && center1 == center2);
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors4f, params.fColorSpace, params.fStops,
-                                              params.fColorCount, params.fTileMode) :
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors, params.fStops,
-                                              params.fColorCount, params.fTileMode);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
-                            ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor)
-    : fVSVaryingName(nullptr)
-    , fFSVaryingName(nullptr)
-    , fCachedCenterX(SK_ScalarMax)
-    , fCachedCenterY(SK_ScalarMax)
-    , fCachedA(SK_ScalarMax)
-    , fCachedB(SK_ScalarMax)
-    , fCachedC(SK_ScalarMax) {}
-
-void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) {
-    const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    this->emitUniforms(uniformHandler, ge);
-    fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                            "Conical2FSCenter");
-    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                           kVec3f_GrSLType, kDefault_GrSLPrecision,
-                                           "Conical2FSParams");
-    SkString tName("t");
-
-    GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
-    // params.x = A
-    // params.y = B
-    // params.z = C
-    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
-
-    // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    const char* coords2D = coords2DString.c_str();
-
-    // p = coords2D
-    // e = center end
-    // r = radius end
-    // A = dot(e, e) - r^2 + 2 * r - 1
-    // B = (r -1) / A
-    // C = 1 / A
-    // d = dot(e, p) + B
-    // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
-    fragBuilder->codeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
-    fragBuilder->codeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
-                             params.c_str());
-    fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
-                             tName.c_str(), params.c_str(), params.c_str());
-
-    this->emitColor(fragBuilder,
-                    uniformHandler,
-                    args.fShaderCaps,
-                    ge,
-                    tName.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-}
-
-void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData(
-                                            const GrGLSLProgramDataManager& pdman,
-                                            const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-    const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
-    SkScalar centerX = data.centerX();
-    SkScalar centerY = data.centerY();
-    SkScalar A = data.A();
-    SkScalar B = data.B();
-    SkScalar C = data.C();
-
-    if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
-        fCachedA != A || fCachedB != B || fCachedC != C) {
-
-        pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
-        pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
-
-        fCachedCenterX = centerX;
-        fCachedCenterY = centerY;
-        fCachedA = A;
-        fCachedB = B;
-        fCachedC = C;
-    }
-}
-
-void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(
-                                            const GrProcessor& processor,
-                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-    b->add32(GenBaseGradientKey(processor));
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class CircleOutside2PtConicalEffect : public GrGradientEffect {
-public:
-    class GLSLCircleOutside2PtConicalProcessor;
-
-    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
-        return sk_sp<GrFragmentProcessor>(
-            new CircleOutside2PtConicalEffect(args, info));
-    }
-
-    ~CircleOutside2PtConicalEffect() override {}
-
-    const char* name() const override { return "Two-Point Conical Gradient Outside"; }
-
-    SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
-    SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
-    SkScalar A() const { return fInfo.fA; }
-    SkScalar B() const { return fInfo.fB; }
-    SkScalar C() const { return fInfo.fC; }
-    SkScalar tLimit() const { return fTLimit; }
-    bool isFlipped() const { return fIsFlipped; }
-
-private:
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
-        const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
-        return (INHERITED::onIsEqual(sBase) &&
-                this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
-                this->fInfo.fA == s.fInfo.fA &&
-                this->fInfo.fB == s.fInfo.fB &&
-                this->fInfo.fC == s.fInfo.fC &&
-                this->fTLimit == s.fTLimit &&
-                this->fIsFlipped == s.fIsFlipped);
-    }
-
-    CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
-            : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
-            , fInfo(info) {
-        this->initClassID<CircleOutside2PtConicalEffect>();
-        const SkTwoPointConicalGradient& shader =
-            *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
-        if (shader.getStartRadius() != shader.getEndRadius()) {
-            fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
-        } else {
-            fTLimit = SK_ScalarMin;
-        }
-
-        fIsFlipped = shader.isFlippedGrad();
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    const CircleConicalInfo fInfo;
-    SkScalar fTLimit;
-    bool fIsFlipped;
-
-    typedef GrGradientEffect INHERITED;
-};
-
-class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
-    : public GrGradientEffect::GLSLProcessor {
-public:
-    GLSLCircleOutside2PtConicalProcessor(const GrProcessor&);
-    ~GLSLCircleOutside2PtConicalProcessor() override {}
-
-    virtual void emitCode(EmitArgs&) override;
-
-    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
-
-    UniformHandle fCenterUni;
-    UniformHandle fParamUni;
-
-    const char* fVSVaryingName;
-    const char* fFSVaryingName;
-
-    bool fIsFlipped;
-
-    // @{
-    /// Values last uploaded as uniforms
-
-    SkScalar fCachedCenterX;
-    SkScalar fCachedCenterY;
-    SkScalar fCachedA;
-    SkScalar fCachedB;
-    SkScalar fCachedC;
-    SkScalar fCachedTLimit;
-
-    // @}
-
-private:
-    typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                          GrProcessorKeyBuilder* b) const {
-    CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(*this, caps, b);
-}
-
-GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
-    return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
-    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
-    SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
-    SkPoint center2;
-    SkScalar radius2;
-    SkScalar diffLen;
-    do {
-        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
-        // If the circles share a center than we can't be in the outside case
-    } while (center1 == center2);
-    SkPoint diff = center2 - center1;
-    diffLen = diff.length();
-    // Below makes sure that circle one is not contained within circle two
-    // and have radius2 >= radius to match sorting on cpu side
-    radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
-
-    RandomGradientParams params(d->fRandom);
-    auto shader = params.fUseColors4f ?
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors4f, params.fColorSpace, params.fStops,
-                                              params.fColorCount, params.fTileMode) :
-        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
-                                              params.fColors, params.fStops,
-                                              params.fColorCount, params.fTileMode);
-    GrTest::TestAsFPArgs asFPArgs(d);
-    sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
-    GrAlwaysAssert(fp);
-    return fp;
-}
-#endif
-
-CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
-                             ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor)
-    : fVSVaryingName(nullptr)
-    , fFSVaryingName(nullptr)
-    , fCachedCenterX(SK_ScalarMax)
-    , fCachedCenterY(SK_ScalarMax)
-    , fCachedA(SK_ScalarMax)
-    , fCachedB(SK_ScalarMax)
-    , fCachedC(SK_ScalarMax)
-    , fCachedTLimit(SK_ScalarMax) {
-    const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
-    fIsFlipped = data.isFlipped();
-    }
-
-void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
-    const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
-    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-    this->emitUniforms(uniformHandler, ge);
-    fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                            "Conical2FSCenter");
-    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
-                                           kVec4f_GrSLType, kDefault_GrSLPrecision,
-                                           "Conical2FSParams");
-    SkString tName("t");
-
-    GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
-    // params.x = A
-    // params.y = B
-    // params.z = C
-    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
-
-    // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
-    const char* coords2D = coords2DString.c_str();
-
-    // output will default to transparent black (we simply won't write anything
-    // else to it if invalid, instead of discarding or returning prematurely)
-    fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-
-    // p = coords2D
-    // e = center end
-    // r = radius end
-    // A = dot(e, e) - r^2 + 2 * r - 1
-    // B = (r -1) / A
-    // C = 1 / A
-    // d = dot(e, p) + B
-    // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
-
-    fragBuilder->codeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
-    fragBuilder->codeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
-                             params.c_str());
-    fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
-                             params.c_str());
-
-    // Must check to see if we flipped the circle order (to make sure start radius < end radius)
-    // If so we must also flip sign on sqrt
-    if (!fIsFlipped) {
-        fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
-    } else {
-        fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
-    }
-
-    fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n",
-                             tName.c_str(), params.c_str());
-    fragBuilder->codeAppend("\t\t");
-    this->emitColor(fragBuilder,
-                    uniformHandler,
-                    args.fShaderCaps,
-                    ge,
-                    tName.c_str(),
-                    args.fOutputColor,
-                    args.fInputColor,
-                    args.fTexSamplers);
-    fragBuilder->codeAppend("\t}\n");
-}
-
-void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData(
-                                                const GrGLSLProgramDataManager& pdman,
-                                                const GrProcessor& processor) {
-    INHERITED::onSetData(pdman, processor);
-    const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
-    SkASSERT(data.isFlipped() == fIsFlipped);
-    SkScalar centerX = data.centerX();
-    SkScalar centerY = data.centerY();
-    SkScalar A = data.A();
-    SkScalar B = data.B();
-    SkScalar C = data.C();
-    SkScalar tLimit = data.tLimit();
-
-    if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
-        fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
-
-        pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
-        pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
-                   SkScalarToFloat(tLimit));
-
-        fCachedCenterX = centerX;
-        fCachedCenterY = centerY;
-        fCachedA = A;
-        fCachedB = B;
-        fCachedC = C;
-        fCachedTLimit = tLimit;
-    }
-}
-
-void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(
-                                            const GrProcessor& processor,
-                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
-    uint32_t* key = b->add32n(2);
-    key[0] = GenBaseGradientKey(processor);
-    key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
-                                                         const GrGradientEffect::CreateArgs& args) {
-    const SkTwoPointConicalGradient& shader =
-        *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
-
-    SkMatrix matrix;
-    if (!shader.getLocalMatrix().invert(&matrix)) {
-        return nullptr;
-    }
-    if (args.fMatrix) {
-        SkMatrix inv;
-        if (!args.fMatrix->invert(&inv)) {
-            return nullptr;
-        }
-        matrix.postConcat(inv);
-    }
-
-    GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode,
-                                         std::move(args.fColorSpaceXform), args.fGammaCorrect);
-
-    if (shader.getStartRadius() < kErrorTol) {
-        SkScalar focalX;
-        ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
-        if (type == kInside_ConicalType) {
-            return FocalInside2PtConicalEffect::Make(newArgs, focalX);
-        } else if(type == kEdge_ConicalType) {
-            set_matrix_edge_conical(shader, &matrix);
-            return Edge2PtConicalEffect::Make(newArgs);
-        } else {
-            return FocalOutside2PtConicalEffect::Make(newArgs, focalX);
-        }
-    }
-
-    CircleConicalInfo info;
-    ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
-
-    if (type == kInside_ConicalType) {
-        return CircleInside2PtConicalEffect::Make(newArgs, info);
-    } else if (type == kEdge_ConicalType) {
-        set_matrix_edge_conical(shader, &matrix);
-        return Edge2PtConicalEffect::Make(newArgs);
-    } else {
-        return CircleOutside2PtConicalEffect::Make(newArgs, info);
-    }
-}
-
-#endif
diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp
deleted file mode 100644
index e6a8de4..0000000
--- a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp
+++ /dev/null
@@ -1,298 +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 "SkAmbientShadowMaskFilter.h"
-#include "SkReadBuffer.h"
-#include "SkStringUtils.h"
-#include "SkWriteBuffer.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrRenderTargetContext.h"
-#include "GrFragmentProcessor.h"
-#include "GrStyle.h"
-#include "GrTexture.h"
-#include "GrTextureProxy.h"
-#include "SkStrokeRec.h"
-#endif
-
-class SkAmbientShadowMaskFilterImpl : public SkMaskFilter {
-public:
-    SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight, SkScalar ambientAlpha, uint32_t flags);
-
-    // overrides from SkMaskFilter
-    SkMask::Format getFormat() const override;
-    bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                    SkIPoint* margin) const override;
-
-#if SK_SUPPORT_GPU
-    bool canFilterMaskGPU(const SkRRect& devRRect,
-                          const SkIRect& clipBounds,
-                          const SkMatrix& ctm,
-                          SkRect* maskRect) const override;
-    bool directFilterMaskGPU(GrContext*,
-                             GrRenderTargetContext* drawContext,
-                             GrPaint&&,
-                             const GrClip&,
-                             const SkMatrix& viewMatrix,
-                             const SkStrokeRec& strokeRec,
-                             const SkPath& path) const override;
-    bool directFilterRRectMaskGPU(GrContext*,
-                                  GrRenderTargetContext* drawContext,
-                                  GrPaint&&,
-                                  const GrClip&,
-                                  const SkMatrix& viewMatrix,
-                                  const SkStrokeRec& strokeRec,
-                                  const SkRRect& rrect,
-                                  const SkRRect& devRRect) const override;
-    sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
-                                        sk_sp<GrTextureProxy> srcProxy,
-                                        const SkMatrix& ctm,
-                                        const SkIRect& maskRect) const override;
-#endif
-
-    void computeFastBounds(const SkRect&, SkRect*) const override;
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAmbientShadowMaskFilterImpl)
-
-private:
-    SkScalar fOccluderHeight;
-    SkScalar fAmbientAlpha;
-    uint32_t fFlags;
-
-    SkAmbientShadowMaskFilterImpl(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const override;
-
-    friend class SkAmbientShadowMaskFilter;
-
-    typedef SkMaskFilter INHERITED;
-};
-
-sk_sp<SkMaskFilter> SkAmbientShadowMaskFilter::Make(SkScalar occluderHeight, SkScalar ambientAlpha,
-                                                    uint32_t flags) {
-    // add some param checks here for early exit
-
-    return sk_sp<SkMaskFilter>(new SkAmbientShadowMaskFilterImpl(occluderHeight, ambientAlpha,
-                                                                 flags));
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkAmbientShadowMaskFilterImpl::SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight,
-                                                             SkScalar ambientAlpha,
-                                                             uint32_t flags)
-    : fOccluderHeight(occluderHeight)
-    , fAmbientAlpha(ambientAlpha)
-    , fFlags(flags) {
-    SkASSERT(fOccluderHeight > 0);
-    SkASSERT(fAmbientAlpha >= 0);
-}
-
-SkMask::Format SkAmbientShadowMaskFilterImpl::getFormat() const {
-    return SkMask::kA8_Format;
-}
-
-bool SkAmbientShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
-                                               const SkMatrix& matrix,
-                                               SkIPoint* margin) const {
-    // TODO something
-    return false;
-}
-
-void SkAmbientShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
-    // TODO compute based on ambient data
-    dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
-}
-
-sk_sp<SkFlattenable> SkAmbientShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
-    const SkScalar occluderHeight = buffer.readScalar();
-    const SkScalar ambientAlpha = buffer.readScalar();
-    const uint32_t flags = buffer.readUInt();
-
-    return SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha, flags);
-}
-
-void SkAmbientShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeScalar(fOccluderHeight);
-    buffer.writeScalar(fAmbientAlpha);
-    buffer.writeUInt(fFlags);
-}
-
-#if SK_SUPPORT_GPU
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool SkAmbientShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
-                                                     const SkIRect& clipBounds,
-                                                     const SkMatrix& ctm,
-                                                     SkRect* maskRect) const {
-    // TODO
-    *maskRect = devRRect.rect();
-    return true;
-}
-
-static const float kHeightFactor = 1.0f / 128.0f;
-static const float kGeomFactor = 64.0f;
-
-bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
-                                                        GrRenderTargetContext* rtContext,
-                                                        GrPaint&& paint,
-                                                        const GrClip& clip,
-                                                        const SkMatrix& viewMatrix,
-                                                        const SkStrokeRec& strokeRec,
-                                                        const SkPath& path) const {
-    SkASSERT(rtContext);
-    // TODO: this will not handle local coordinates properly
-
-    if (fAmbientAlpha <= 0.0f) {
-        return true;
-    }
-
-    // only convex paths for now
-    if (!path.isConvex()) {
-        return false;
-    }
-
-    if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
-        return false;
-    }
-
-    // if circle
-    // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
-    // have our own GeometryProc.
-    if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
-        SkRRect rrect = SkRRect::MakeOval(path.getBounds());
-        return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
-                                              SkMatrix::I(), strokeRec, rrect, rrect);
-    } else if (path.isRect(nullptr)) {
-        SkRRect rrect = SkRRect::MakeRect(path.getBounds());
-        return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
-                                              SkMatrix::I(), strokeRec, rrect, rrect);
-    }
-
-    // TODO
-    return false;
-}
-
-bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
-                                                             GrRenderTargetContext* rtContext,
-                                                             GrPaint&& paint,
-                                                             const GrClip& clip,
-                                                             const SkMatrix& viewMatrix,
-                                                             const SkStrokeRec& strokeRec,
-                                                             const SkRRect& rrect,
-                                                             const SkRRect& devRRect) const {
-    // It's likely the caller has already done these checks, but we have to be sure.
-    // TODO: support analytic blurring of general rrect
-
-    // Fast path only supports filled rrects for now.
-    // TODO: fill and stroke as well.
-    if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
-        return false;
-    }
-    // Fast path only supports simple rrects with circular corners.
-    SkASSERT(devRRect.allCornersCircular());
-    if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
-        return false;
-    }
-    // Fast path only supports uniform scale.
-    SkScalar scaleFactors[2];
-    if (!viewMatrix.getMinMaxScales(scaleFactors)) {
-        // matrix is degenerate
-        return false;
-    }
-    if (scaleFactors[0] != scaleFactors[1]) {
-        return false;
-    }
-    SkScalar scaleFactor = scaleFactors[0];
-
-    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
-    const SkScalar minRadius = 0.5f / scaleFactor;
-    bool isRect = rrect.getSimpleRadii().fX <= minRadius;
-
-    // TODO: take flags into account when generating shadow data
-
-    if (fAmbientAlpha > 0.0f) {
-        SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
-        const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
-        const SkScalar strokeWidth = srcSpaceAmbientRadius * umbraAlpha;
-
-        // For the ambient rrect, we outset the offset rect by srcSpaceAmbientRadius
-        // minus half the strokeWidth to get our stroke shape.
-        SkScalar ambientPathOutset = SkTMax(srcSpaceAmbientRadius - strokeWidth * 0.5f,
-                                            minRadius);
-
-        SkRRect ambientRRect;
-        if (isRect) {
-            const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
-            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
-        } else {
-             rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
-        }
-
-        const SkScalar devSpaceAmbientRadius = strokeWidth * scaleFactor;
-
-        GrPaint newPaint(paint);
-        GrColor4f color = newPaint.getColor4f();
-        color.fRGBA[3] *= fAmbientAlpha;
-        newPaint.setColor4f(color);
-        SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
-        ambientStrokeRec.setStrokeStyle(strokeWidth, false);
-
-        rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
-                                   devSpaceAmbientRadius,
-                                   GrStyle(ambientStrokeRec, nullptr));
-    }
-
-    return true;
-}
-
-sk_sp<GrTextureProxy> SkAmbientShadowMaskFilterImpl::filterMaskGPU(GrContext*,
-                                                                   sk_sp<GrTextureProxy> srcProxy,
-                                                                   const SkMatrix& ctm,
-                                                                   const SkIRect& maskRect) const {
-    // This filter is generative and doesn't operate on pre-existing masks
-    return nullptr;
-}
-
-#endif // SK_SUPPORT_GPU
-
-#ifndef SK_IGNORE_TO_STRING
-void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
-    str->append("SkAmbientShadowMaskFilterImpl: (");
-
-    str->append("occluderHeight: ");
-    str->appendScalar(fOccluderHeight);
-    str->append(" ");
-
-    str->append("ambientAlpha: ");
-    str->appendScalar(fAmbientAlpha);
-    str->append(" ");
-
-    str->append("flags: (");
-    if (fFlags) {
-        bool needSeparator = false;
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
-                          "TransparentOccluder", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
-                          "GaussianEdge", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
-                          "LargerUmbra", &needSeparator);
-    } else {
-        str->append("None");
-    }
-    str->append("))");
-}
-#endif
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAmbientShadowMaskFilter)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAmbientShadowMaskFilterImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.h b/src/effects/shadows/SkAmbientShadowMaskFilter.h
deleted file mode 100644
index cd77b10..0000000
--- a/src/effects/shadows/SkAmbientShadowMaskFilter.h
+++ /dev/null
@@ -1,33 +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 SkAmbientShadowMaskFilter_DEFINED
-#define SkAmbientShadowMaskFilter_DEFINED
-
-#include "SkMaskFilter.h"
-#include "SkShadowFlags.h"
-
-/*
- * This filter implements a shadow representing ambient occlusion for an occluding object.
- */
-class SK_API SkAmbientShadowMaskFilter {
-public:
-    /** Create a shadow maskfilter.
-     *  @param occluderHeight Height of occluding object off of ground plane.
-     *  @param ambientAlpha   Base opacity of the ambient occlusion shadow.
-     *  @param flags          Flags to use - defaults to none
-     *  @return The new shadow maskfilter
-     */
-    static sk_sp<SkMaskFilter> Make(SkScalar occluderHeight, SkScalar ambientAlpha,
-                                    uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-
-private:
-    SkAmbientShadowMaskFilter(); // can't be instantiated
-};
-#endif
diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.cpp b/src/effects/shadows/SkSpotShadowMaskFilter.cpp
deleted file mode 100644
index ee82342..0000000
--- a/src/effects/shadows/SkSpotShadowMaskFilter.cpp
+++ /dev/null
@@ -1,370 +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 "SkSpotShadowMaskFilter.h"
-#include "SkReadBuffer.h"
-#include "SkStringUtils.h"
-#include "SkWriteBuffer.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrRenderTargetContext.h"
-#include "GrFragmentProcessor.h"
-#include "GrStyle.h"
-#include "GrTexture.h"
-#include "GrTextureProxy.h"
-#include "SkStrokeRec.h"
-#endif
-
-class SkSpotShadowMaskFilterImpl : public SkMaskFilter {
-public:
-    SkSpotShadowMaskFilterImpl(SkScalar occluderHeight, const SkPoint3& lightPos,
-                               SkScalar lightRadius, SkScalar spotAlpha, uint32_t flags);
-
-    // overrides from SkMaskFilter
-    SkMask::Format getFormat() const override;
-    bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                    SkIPoint* margin) const override;
-
-#if SK_SUPPORT_GPU
-    bool canFilterMaskGPU(const SkRRect& devRRect,
-                          const SkIRect& clipBounds,
-                          const SkMatrix& ctm,
-                          SkRect* maskRect) const override;
-    bool directFilterMaskGPU(GrContext*,
-                             GrRenderTargetContext* drawContext,
-                             GrPaint&&,
-                             const GrClip&,
-                             const SkMatrix& viewMatrix,
-                             const SkStrokeRec& strokeRec,
-                             const SkPath& path) const override;
-    bool directFilterRRectMaskGPU(GrContext*,
-                                  GrRenderTargetContext* drawContext,
-                                  GrPaint&&,
-                                  const GrClip&,
-                                  const SkMatrix& viewMatrix,
-                                  const SkStrokeRec& strokeRec,
-                                  const SkRRect& rrect,
-                                  const SkRRect& devRRect) const override;
-    sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
-                                        sk_sp<GrTextureProxy> srcProxy,
-                                        const SkMatrix& ctm,
-                                        const SkIRect& maskRect) const override;
-#endif
-
-    void computeFastBounds(const SkRect&, SkRect*) const override;
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotShadowMaskFilterImpl)
-
-private:
-    SkScalar fOccluderHeight;
-    SkPoint3 fLightPos;
-    SkScalar fLightRadius;
-    SkScalar fSpotAlpha;
-    uint32_t fFlags;
-
-    SkSpotShadowMaskFilterImpl(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const override;
-
-    friend class SkSpotShadowMaskFilter;
-
-    typedef SkMaskFilter INHERITED;
-};
-
-sk_sp<SkMaskFilter> SkSpotShadowMaskFilter::Make(SkScalar occluderHeight, const SkPoint3& lightPos,
-                                                 SkScalar lightRadius, SkScalar spotAlpha,
-                                                 uint32_t flags) {
-    // add some param checks here for early exit
-
-    return sk_sp<SkMaskFilter>(new SkSpotShadowMaskFilterImpl(occluderHeight, lightPos,
-                                                              lightRadius, spotAlpha, flags));
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkSpotShadowMaskFilterImpl::SkSpotShadowMaskFilterImpl(SkScalar occluderHeight,
-                                                       const SkPoint3& lightPos,
-                                                       SkScalar lightRadius,
-                                                       SkScalar spotAlpha,
-                                                       uint32_t flags)
-    : fOccluderHeight(occluderHeight)
-    , fLightPos(lightPos)
-    , fLightRadius(lightRadius)
-    , fSpotAlpha(spotAlpha)
-    , fFlags(flags) {
-    SkASSERT(fOccluderHeight > 0);
-    SkASSERT(fLightPos.z() > 0 && fLightPos.z() > fOccluderHeight);
-    SkASSERT(fLightRadius > 0);
-    SkASSERT(fSpotAlpha >= 0);
-}
-
-SkMask::Format SkSpotShadowMaskFilterImpl::getFormat() const {
-    return SkMask::kA8_Format;
-}
-
-bool SkSpotShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
-                                            const SkMatrix& matrix,
-                                            SkIPoint* margin) const {
-    // TODO something
-    return false;
-}
-
-void SkSpotShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
-    // TODO compute based on ambient + spot data
-    dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
-}
-
-sk_sp<SkFlattenable> SkSpotShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
-    const SkScalar occluderHeight = buffer.readScalar();
-    const SkScalar lightX = buffer.readScalar();
-    const SkScalar lightY = buffer.readScalar();
-    const SkScalar lightZ = buffer.readScalar();
-    const SkPoint3 lightPos = SkPoint3::Make(lightX, lightY, lightZ);
-    const SkScalar lightRadius = buffer.readScalar();
-    const SkScalar spotAlpha = buffer.readScalar();
-    const uint32_t flags = buffer.readUInt();
-
-    return SkSpotShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
-                                        spotAlpha, flags);
-}
-
-void SkSpotShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeScalar(fOccluderHeight);
-    buffer.writeScalar(fLightPos.fX);
-    buffer.writeScalar(fLightPos.fY);
-    buffer.writeScalar(fLightPos.fZ);
-    buffer.writeScalar(fLightRadius);
-    buffer.writeScalar(fSpotAlpha);
-    buffer.writeUInt(fFlags);
-}
-
-#if SK_SUPPORT_GPU
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool SkSpotShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
-                                                  const SkIRect& clipBounds,
-                                                  const SkMatrix& ctm,
-                                                  SkRect* maskRect) const {
-    // TODO
-    *maskRect = devRRect.rect();
-    return true;
-}
-
-bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
-                                                     GrRenderTargetContext* rtContext,
-                                                     GrPaint&& paint,
-                                                     const GrClip& clip,
-                                                     const SkMatrix& viewMatrix,
-                                                     const SkStrokeRec& strokeRec,
-                                                     const SkPath& path) const {
-    SkASSERT(rtContext);
-    // TODO: this will not handle local coordinates properly
-
-    if (fSpotAlpha <= 0.0f) {
-        return true;
-    }
-
-    // only convex paths for now
-    if (!path.isConvex()) {
-        return false;
-    }
-
-    if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
-        return false;
-    }
-
-    // if circle
-    // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
-    // have our own GeometryProc.
-    if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
-        SkRRect rrect = SkRRect::MakeOval(path.getBounds());
-        return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
-                                              SkMatrix::I(), strokeRec, rrect, rrect);
-    } else if (path.isRect(nullptr)) {
-        SkRRect rrect = SkRRect::MakeRect(path.getBounds());
-        return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
-                                              SkMatrix::I(), strokeRec, rrect, rrect);
-    }
-
-    return false;
-}
-
-bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
-                                                          GrRenderTargetContext* rtContext,
-                                                          GrPaint&& paint,
-                                                          const GrClip& clip,
-                                                          const SkMatrix& viewMatrix,
-                                                          const SkStrokeRec& strokeRec,
-                                                          const SkRRect& rrect,
-                                                          const SkRRect& devRRect) const {
-    // It's likely the caller has already done these checks, but we have to be sure.
-    // TODO: support analytic blurring of general rrect
-
-    // Fast path only supports filled rrects for now.
-    // TODO: fill and stroke as well.
-    if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
-        return false;
-    }
-    // Fast path only supports simple rrects with circular corners.
-    SkASSERT(devRRect.allCornersCircular());
-    if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
-        return false;
-    }
-    // Fast path only supports uniform scale.
-    SkScalar scaleFactors[2];
-    if (!viewMatrix.getMinMaxScales(scaleFactors)) {
-        // matrix is degenerate
-        return false;
-    }
-    if (scaleFactors[0] != scaleFactors[1]) {
-        return false;
-    }
-    SkScalar scaleFactor = scaleFactors[0];
-
-    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
-    const SkScalar minRadius = 0.5f / scaleFactor;
-    bool isRect = rrect.getSimpleRadii().fX <= minRadius;
-
-    // TODO: take flags into account when generating shadow data
-
-    if (fSpotAlpha > 0.0f) {
-        float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
-
-        SkScalar srcSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
-
-        SkRRect spotRRect;
-        if (isRect) {
-            spotRRect = SkRRect::MakeRectXY(rrect.rect(), minRadius, minRadius);
-        } else {
-            spotRRect = rrect;
-        }
-
-        SkRRect spotShadowRRect;
-        // Compute the scale and translation for the spot shadow.
-        const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
-        spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
-
-        SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
-                                       spotShadowRRect.rect().centerY());
-        SkMatrix ctmInverse;
-        if (!viewMatrix.invert(&ctmInverse)) {
-            SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
-            //**** TODO: this is not good
-            return true;
-        }
-        SkPoint lightPos2D = SkPoint::Make(fLightPos.fX, fLightPos.fY);
-        ctmInverse.mapPoints(&lightPos2D, 1);
-        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
-                                                 zRatio*(center.fY - lightPos2D.fY));
-
-        // We want to extend the stroked area in so that it meets up with the caster
-        // geometry. The stroked geometry will, by definition already be inset half the
-        // stroke width but we also have to account for the scaling.
-        SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(rrect.rect().fLeft),
-                                                              SkTAbs(rrect.rect().fRight)),
-                                                       SkTMax(SkTAbs(rrect.rect().fTop),
-                                                              SkTAbs(rrect.rect().fBottom)));
-        SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) + scaleOffset;
-
-        // Compute area
-        SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
-        SkScalar strokedArea = 2.0f*strokeWidth *
-                               (spotShadowRRect.width() + spotShadowRRect.height());
-        SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius) *
-                              (spotShadowRRect.width() + srcSpaceSpotRadius);
-
-        GrColor4f color = paint.getColor4f();
-        color.fRGBA[3] *= fSpotAlpha;
-        paint.setColor4f(color);
-
-        SkStrokeRec spotStrokeRec(SkStrokeRec::kFill_InitStyle);
-        // If the area of the stroked geometry is larger than the fill geometry,
-        // or if the caster is transparent, just fill it.
-        if (strokedArea > filledArea ||
-            fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
-            spotStrokeRec.setStrokeStyle(srcSpaceSpotRadius, true);
-        } else {
-            // Since we can't have unequal strokes, inset the shadow rect so the inner
-            // and outer edges of the stroke will land where we want.
-            SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount / 2.0f,
-                                                                insetAmount / 2.0f);
-            SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount / 2.0f,
-                                       minRadius);
-            spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
-            spotStrokeRec.setStrokeStyle(strokeWidth, false);
-        }
-
-        // handle scale of radius and pad due to CTM
-        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
-
-        spotShadowRRect.offset(spotOffset.fX, spotOffset.fY);
-
-        rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
-                                   devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
-    }
-
-    return true;
-}
-
-sk_sp<GrTextureProxy> SkSpotShadowMaskFilterImpl::filterMaskGPU(GrContext*,
-                                                                sk_sp<GrTextureProxy> srcProxy,
-                                                                const SkMatrix& ctm,
-                                                                const SkIRect& maskRect) const {
-    // This filter is generative and doesn't operate on pre-existing masks
-    return nullptr;
-}
-
-#endif
-
-#ifndef SK_IGNORE_TO_STRING
-void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
-    str->append("SkSpotShadowMaskFilterImpl: (");
-
-    str->append("occluderHeight: ");
-    str->appendScalar(fOccluderHeight);
-    str->append(" ");
-
-    str->append("lightPos: (");
-    str->appendScalar(fLightPos.fX);
-    str->append(", ");
-    str->appendScalar(fLightPos.fY);
-    str->append(", ");
-    str->appendScalar(fLightPos.fZ);
-    str->append(") ");
-
-    str->append("lightRadius: ");
-    str->appendScalar(fLightRadius);
-    str->append(" ");
-
-    str->append("spotAlpha: ");
-    str->appendScalar(fSpotAlpha);
-    str->append(" ");
-
-    str->append("flags: (");
-    if (fFlags) {
-        bool needSeparator = false;
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
-                          "TransparentOccluder", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
-                          "GaussianEdge", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
-                          "LargerUmbra", &needSeparator);
-    } else {
-        str->append("None");
-    }
-    str->append("))");
-}
-#endif
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkSpotShadowMaskFilter)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotShadowMaskFilterImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.h b/src/effects/shadows/SkSpotShadowMaskFilter.h
deleted file mode 100644
index 5e1a4a9..0000000
--- a/src/effects/shadows/SkSpotShadowMaskFilter.h
+++ /dev/null
@@ -1,37 +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 SkSpotShadowMaskFilter_DEFINED
-#define SkSpotShadowMaskFilter_DEFINED
-
-#include "SkMaskFilter.h"
-#include "SkShadowFlags.h"
-
-/*
- * This filter implements a shadow for an occluding object
- * representing a displaced shadow from a point light.
- */
-class SK_API SkSpotShadowMaskFilter {
-public:
-    /** Create a shadow maskfilter.
-     *  @param occluderHeight Height of occluding object off of ground plane.
-     *  @param lightPos       Position of the light applied to this object.
-     *  @param lightRadius    Radius of the light (light is assumed to be spherical).
-     *  @param spotAlpha      Base opacity of the displaced spot shadow.
-     *  @param flags          Flags to use - defaults to none
-     *  @return The new shadow maskfilter
-     */
-    static sk_sp<SkMaskFilter> Make(SkScalar occluderHeight, const SkPoint3& lightPos,
-                                    SkScalar lightRadius, SkScalar spotAlpha,
-                                    uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-
-private:
-    SkSpotShadowMaskFilter(); // can't be instantiated
-};
-#endif
diff --git a/src/fonts/SkGScalerContext.cpp b/src/fonts/SkGScalerContext.cpp
deleted file mode 100644
index 3b9c660..0000000
--- a/src/fonts/SkGScalerContext.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkCanvas.h"
-#include "SkDescriptor.h"
-#include "SkGScalerContext.h"
-#include "SkGlyph.h"
-#include "SkPaintPriv.h"
-#include "SkPath.h"
-#include "SkMakeUnique.h"
-
-#define STD_SIZE    1
-
-class SkGScalerContext : public SkScalerContext {
-public:
-    SkGScalerContext(sk_sp<SkGTypeface> face, const SkScalerContextEffects& effects,
-                     const SkDescriptor* desc)
-        : SkScalerContext(std::move(face), effects, desc)
-    {
-
-        size_t  descSize = SkDescriptor::ComputeOverhead(1) + sizeof(SkScalerContext::Rec);
-        SkAutoDescriptor ad(descSize);
-        SkDescriptor*    newDesc = ad.getDesc();
-
-        newDesc->init();
-        void* entry = newDesc->addEntry(kRec_SkDescriptorTag,
-                                        sizeof(SkScalerContext::Rec), &fRec);
-        {
-            SkScalerContext::Rec* rec = (SkScalerContext::Rec*)entry;
-            rec->fTextSize = STD_SIZE;
-            rec->fPreScaleX = SK_Scalar1;
-            rec->fPreSkewX = 0;
-            rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
-            rec->fPost2x2[1][0] = rec->fPost2x2[0][1] = 0;
-        }
-        SkASSERT(descSize == newDesc->getLength());
-        newDesc->computeChecksum();
-
-        fProxy = this->getGTypeface()->proxy()->createScalerContext(effects, newDesc);
-
-        fRec.getSingleMatrix(&fMatrix);
-        fMatrix.preScale(SK_Scalar1 / STD_SIZE, SK_Scalar1 / STD_SIZE);
-    }
-
-protected:
-    SkGTypeface* getGTypeface() { return static_cast<SkGTypeface*>(this->getTypeface()); }
-
-    unsigned generateGlyphCount() override;
-    uint16_t generateCharToGlyph(SkUnichar) override;
-    void generateAdvance(SkGlyph*) override;
-    void generateMetrics(SkGlyph*) override;
-    void generateImage(const SkGlyph&) override;
-    void generatePath(SkGlyphID, SkPath*) override;
-    void generateFontMetrics(SkPaint::FontMetrics*) override;
-
-private:
-    std::unique_ptr<SkScalerContext> fProxy;
-    SkMatrix         fMatrix;
-};
-
-unsigned SkGScalerContext::generateGlyphCount() {
-    return fProxy->getGlyphCount();
-}
-
-uint16_t SkGScalerContext::generateCharToGlyph(SkUnichar uni) {
-    return fProxy->charToGlyphID(uni);
-}
-
-void SkGScalerContext::generateAdvance(SkGlyph* glyph) {
-    fProxy->getAdvance(glyph);
-
-    SkVector advance;
-    fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX),
-                  SkFloatToScalar(glyph->fAdvanceY), &advance);
-    glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-    glyph->fAdvanceY = SkScalarToFloat(advance.fY);
-}
-
-void SkGScalerContext::generateMetrics(SkGlyph* glyph) {
-    fProxy->getMetrics(glyph);
-
-    SkVector advance;
-    fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX),
-                  SkFloatToScalar(glyph->fAdvanceY), &advance);
-    glyph->fAdvanceX = SkScalarToFloat(advance.fX);
-    glyph->fAdvanceY = SkScalarToFloat(advance.fY);
-
-    SkPath path;
-    fProxy->getPath(glyph->getPackedID(), &path);
-    path.transform(fMatrix);
-
-    SkRect storage;
-    const SkPaint& paint = this->getGTypeface()->paint();
-    const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(),
-                                                        &storage,
-                                                        SkPaint::kFill_Style);
-    SkIRect ibounds;
-    newBounds.roundOut(&ibounds);
-    glyph->fLeft = ibounds.fLeft;
-    glyph->fTop = ibounds.fTop;
-    glyph->fWidth = ibounds.width();
-    glyph->fHeight = ibounds.height();
-    glyph->fMaskFormat = SkMask::kARGB32_Format;
-}
-
-void SkGScalerContext::generateImage(const SkGlyph& glyph) {
-    if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
-        SkPath path;
-        fProxy->getPath(glyph.getPackedID(), &path);
-
-        SkBitmap bm;
-        bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight),
-                         glyph.fImage, glyph.rowBytes());
-        bm.eraseColor(0);
-
-        SkCanvas canvas(bm);
-        canvas.translate(-SkIntToScalar(glyph.fLeft),
-                         -SkIntToScalar(glyph.fTop));
-        canvas.concat(fMatrix);
-        canvas.drawPath(path, this->getGTypeface()->paint());
-    } else {
-        fProxy->getImage(glyph);
-    }
-}
-
-void SkGScalerContext::generatePath(SkGlyphID glyph, SkPath* path) {
-    fProxy->getPath(SkPackedGlyphID(glyph), path);
-    path->transform(fMatrix);
-}
-
-void SkGScalerContext::generateFontMetrics(SkPaint::FontMetrics* metrics) {
-    fProxy->getFontMetrics(metrics);
-    SkPaintPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkTypefaceCache.h"
-
-SkGTypeface::SkGTypeface(sk_sp<SkTypeface> proxy, const SkPaint& paint)
-    : SkTypeface(proxy->fontStyle(), false)
-    , fProxy(std::move(proxy))
-    , fPaint(paint)
-{}
-
-SkScalerContext* SkGTypeface::onCreateScalerContext(const SkScalerContextEffects& effects,
-                                                    const SkDescriptor* desc) const {
-    return new SkGScalerContext(sk_ref_sp(const_cast<SkGTypeface*>(this)), effects, desc);
-}
-
-void SkGTypeface::onFilterRec(SkScalerContextRec* rec) const {
-    fProxy->filterRec(rec);
-    rec->setHinting(SkPaint::kNo_Hinting);
-    rec->fMaskFormat = SkMask::kARGB32_Format;
-}
-
-SkAdvancedTypefaceMetrics* SkGTypeface::onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo info,
-                                const uint32_t* glyphIDs,
-                                uint32_t glyphIDsCount) const {
-    return fProxy->getAdvancedTypefaceMetrics(info, glyphIDs, glyphIDsCount);
-}
-
-SkStreamAsset* SkGTypeface::onOpenStream(int* ttcIndex) const {
-    return fProxy->openStream(ttcIndex);
-}
-
-void SkGTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
-                                      bool* isLocal) const {
-    fProxy->getFontDescriptor(desc, isLocal);
-}
-
-int SkGTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
-                                 uint16_t glyphs[], int glyphCount) const {
-    return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount);
-}
-
-int SkGTypeface::onCountGlyphs() const {
-    return fProxy->countGlyphs();
-}
-
-int SkGTypeface::onGetUPEM() const {
-    return fProxy->getUnitsPerEm();
-}
-
-void SkGTypeface::onGetFamilyName(SkString* familyName) const {
-    fProxy->getFamilyName(familyName);
-}
-
-SkTypeface::LocalizedStrings* SkGTypeface::onCreateFamilyNameIterator() const {
-    return fProxy->createFamilyNameIterator();
-}
-
-int SkGTypeface::onGetVariationDesignPosition(
-        SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
-{
-    return fProxy->onGetVariationDesignPosition(coordinates, coordinateCount);
-}
-
-int SkGTypeface::onGetTableTags(SkFontTableTag tags[]) const {
-    return fProxy->getTableTags(tags);
-}
-
-size_t SkGTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
-                                    size_t length, void* data) const {
-    return fProxy->getTableData(tag, offset, length, data);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if 0
-// under construction -- defining a font purely in terms of skia primitives
-// ala an SVG-font.
-class SkGFont : public SkRefCnt {
-public:
-    virtual ~SkGFont();
-
-    int unicharToGlyph(SkUnichar) const;
-
-    int countGlyphs() const { return fCount; }
-
-    float getAdvance(int index) const {
-        SkASSERT((unsigned)index < (unsigned)fCount);
-        return fGlyphs[index].fAdvance;
-    }
-
-    const SkPath& getPath(int index) const {
-        SkASSERT((unsigned)index < (unsigned)fCount);
-        return fGlyphs[index].fPath;
-    }
-
-private:
-    struct Glyph {
-        SkUnichar   fUni;
-        float       fAdvance;
-        SkPath      fPath;
-    };
-    int fCount;
-    Glyph* fGlyphs;
-
-    friend class SkGFontBuilder;
-    SkGFont(int count, Glyph* array);
-};
-
-class SkGFontBuilder {
-public:
-
-};
-#endif
diff --git a/src/fonts/SkGScalerContext.h b/src/fonts/SkGScalerContext.h
deleted file mode 100644
index 562513c..0000000
--- a/src/fonts/SkGScalerContext.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkGScalerContext_DEFINED
-#define SkGScalerContext_DEFINED
-
-#include "SkScalerContext.h"
-#include "SkTypeface.h"
-
-class SkGTypeface : public SkTypeface {
-public:
-    SkGTypeface(sk_sp<SkTypeface> proxy, const SkPaint&);
-
-    SkTypeface* proxy() const { return fProxy.get(); }
-    const SkPaint& paint() const { return fPaint; }
-
-protected:
-    SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
-                                           const SkDescriptor*) const override;
-    void onFilterRec(SkScalerContextRec*) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const override;
-    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
-    void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override;
-
-    int onCharsToGlyphs(const void* chars, Encoding encoding,
-                        uint16_t glyphs[], int glyphCount) const override;
-    int onCountGlyphs() const override;
-    int onGetUPEM() const override;
-
-    void onGetFamilyName(SkString* familyName) const override;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
-
-    int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
-                                     int coordinateCount) const override;
-    int onGetTableTags(SkFontTableTag tags[]) const override;
-    size_t onGetTableData(SkFontTableTag, size_t offset,
-                          size_t length, void* data) const override;
-
-private:
-    sk_sp<SkTypeface>   fProxy;
-    SkPaint             fPaint;
-};
-
-#endif
diff --git a/src/fonts/SkRandomScalerContext.cpp b/src/fonts/SkRandomScalerContext.cpp
index 3a292dc..49d9ab4 100644
--- a/src/fonts/SkRandomScalerContext.cpp
+++ b/src/fonts/SkRandomScalerContext.cpp
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkGlyph.h"
 #include "SkMakeUnique.h"
@@ -205,11 +207,8 @@
     rec->fMaskFormat = SkMask::kARGB32_Format;
 }
 
-SkAdvancedTypefaceMetrics* SkRandomTypeface::onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo info,
-                                const uint32_t* glyphIDs,
-                                uint32_t glyphIDsCount) const {
-    return fProxy->getAdvancedTypefaceMetrics(info, glyphIDs, glyphIDsCount);
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkRandomTypeface::onGetAdvancedMetrics() const {
+    return fProxy->getAdvancedMetrics();
 }
 
 SkStreamAsset* SkRandomTypeface::onOpenStream(int* ttcIndex) const {
diff --git a/src/fonts/SkRandomScalerContext.h b/src/fonts/SkRandomScalerContext.h
index c84b764..b71689d 100644
--- a/src/fonts/SkRandomScalerContext.h
+++ b/src/fonts/SkRandomScalerContext.h
@@ -27,10 +27,7 @@
     SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
                                            const SkDescriptor*) const override;
     void onFilterRec(SkScalerContextRec*) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     SkStreamAsset* onOpenStream(int* ttcIndex) const override;
     void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override;
 
diff --git a/src/fonts/SkTestScalerContext.cpp b/src/fonts/SkTestScalerContext.cpp
index db72637..ec53c54 100644
--- a/src/fonts/SkTestScalerContext.cpp
+++ b/src/fonts/SkTestScalerContext.cpp
@@ -146,12 +146,8 @@
     rec->setHinting(SkPaint::kNo_Hinting);
 }
 
-SkAdvancedTypefaceMetrics* SkTestTypeface::onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo ,
-                                const uint32_t* glyphIDs,
-                                uint32_t glyphIDsCount) const {
-// pdf only
-    SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTestTypeface::onGetAdvancedMetrics() const { // pdf only
+    std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
     info->fFontName.set(fTestFont->fName);
     int glyphCount = this->onCountGlyphs();
 
diff --git a/src/fonts/SkTestScalerContext.h b/src/fonts/SkTestScalerContext.h
index 5b2ec4f..ec7ee85 100644
--- a/src/fonts/SkTestScalerContext.h
+++ b/src/fonts/SkTestScalerContext.h
@@ -65,10 +65,7 @@
     SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
                                            const SkDescriptor* desc) const override;
     void onFilterRec(SkScalerContextRec* rec) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
 
     SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         return nullptr;
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp
new file mode 100644
index 0000000..94861d4
--- /dev/null
+++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 "SkTypes.h"
+
+//TODO: This define is temporary and we will compile with NDK after
+//TODO: Skia bug 6672 is resolved.
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+#include "GrAHardwareBufferImageGenerator.h"
+
+#include "GrBackendSurface.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+class BufferCleanupHelper {
+public:
+    BufferCleanupHelper(EGLImageKHR image, EGLDisplay display)
+        : fImage(image)
+        , fDisplay(display) { }
+    ~BufferCleanupHelper() {
+        eglDestroyImageKHR(fDisplay, fImage);
+    }
+private:
+    EGLImageKHR fImage;
+    EGLDisplay fDisplay;
+};
+
+std::unique_ptr<SkImageGenerator> GrAHardwareBufferImageGenerator::Make(
+        AHardwareBuffer* graphicBuffer, SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
+    AHardwareBuffer_Desc bufferDesc;
+    AHardwareBuffer_describe(graphicBuffer, &bufferDesc);
+    SkColorType colorType;
+    switch (bufferDesc.format) {
+    case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+        colorType = kRGBA_8888_SkColorType;
+        break;
+    case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+        colorType = kRGBA_F16_SkColorType;
+        break;
+    case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+        colorType = kRGB_565_SkColorType;
+        break;
+    default:
+        return nullptr;
+    }
+    SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType,
+                                         alphaType, std::move(colorSpace));
+    return std::unique_ptr<SkImageGenerator>(new GrAHardwareBufferImageGenerator(info, graphicBuffer,
+            alphaType));
+}
+
+GrAHardwareBufferImageGenerator::GrAHardwareBufferImageGenerator(const SkImageInfo& info,
+        AHardwareBuffer* graphicBuffer, SkAlphaType alphaType)
+    : INHERITED(info)
+    , fGraphicBuffer(graphicBuffer)
+    , fAlphaType(alphaType) {
+    AHardwareBuffer_acquire(fGraphicBuffer);
+}
+
+GrAHardwareBufferImageGenerator::~GrAHardwareBufferImageGenerator() {
+    AHardwareBuffer_release(fGraphicBuffer);
+}
+
+void GrAHardwareBufferImageGenerator::deleteImageTexture(void* context) {
+     BufferCleanupHelper* cleanupHelper = static_cast<BufferCleanupHelper*>(context);
+     delete cleanupHelper;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+
+sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
+        GrContext* context, const SkImageInfo& info, const SkIPoint& origin) {
+    // TODO: return a cached GrTextureProxy if invoked with the same context
+    // TODO: if we cache GrTextureProxy, then deleteImageTexture may be invoked on the wrong thread
+
+    if (!context->getGpu() || kOpenGL_GrBackend != context->contextPriv().getBackend()) {
+        // Check if GrContext is not abandoned and the backend is GL.
+        return nullptr;
+    }
+
+    while (GL_NO_ERROR != glGetError()) {} //clear GL errors
+
+    EGLClientBuffer  clientBuffer = eglGetNativeClientBufferANDROID(fGraphicBuffer);
+    EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+                         EGL_NONE };
+    EGLDisplay display = eglGetCurrentDisplay();
+    EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                                          clientBuffer, attribs);
+    if (EGL_NO_IMAGE_KHR == image) {
+        SkDebugf("Could not create EGL image, err = (%#x)", (int) eglGetError() );
+        return nullptr;
+    }
+    GrGLuint texID;
+    glGenTextures(1, &texID);
+    if (!texID) {
+        eglDestroyImageKHR(display, image);
+        return nullptr;
+    }
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, texID);
+    GLenum status = GL_NO_ERROR;
+    if ((status = glGetError()) != GL_NO_ERROR) {
+        SkDebugf("glBindTexture failed (%#x)", (int) status);
+        glDeleteTextures(1, &texID);
+        eglDestroyImageKHR(display, image);
+        return nullptr;
+    }
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
+    if ((status = glGetError()) != GL_NO_ERROR) {
+        SkDebugf("glEGLImageTargetTexture2DOES failed (%#x)", (int) status);
+        glDeleteTextures(1, &texID);
+        eglDestroyImageKHR(display, image);
+        return nullptr;
+    }
+    context->resetContext(kTextureBinding_GrGLBackendState);
+
+    GrGLTextureInfo textureInfo;
+    textureInfo.fTarget = GL_TEXTURE_EXTERNAL_OES;
+    textureInfo.fID = texID;
+
+    GrPixelConfig pixelConfig;
+    switch (getInfo().colorType()) {
+    case kRGBA_8888_SkColorType:
+        pixelConfig = kRGBA_8888_GrPixelConfig;
+        break;
+    case kRGBA_F16_SkColorType:
+        pixelConfig = kRGBA_half_GrPixelConfig;
+        break;
+    case kRGB_565_SkColorType:
+        pixelConfig = kRGB_565_GrPixelConfig;
+        break;
+    default:
+        glDeleteTextures(1, &texID);
+        eglDestroyImageKHR(display, image);
+        return nullptr;
+    }
+
+    GrBackendTexture backendTex(getInfo().width(), getInfo().height(), pixelConfig, textureInfo);
+    if (backendTex.width() <= 0 || backendTex.height() <= 0) {
+        glDeleteTextures(1, &texID);
+        eglDestroyImageKHR(display, image);
+        return nullptr;
+    }
+    GrBackendTextureFlags flags = kNone_GrBackendTextureFlag;
+    sk_sp<GrTexture> tex = context->resourceProvider()->wrapBackendTexture(backendTex,
+                                                                       kTopLeft_GrSurfaceOrigin,
+                                                                       flags,
+                                                                       0,
+                                                                       kAdopt_GrWrapOwnership);
+    if (!tex) {
+        glDeleteTextures(1, &texID);
+        eglDestroyImageKHR(display, image);
+        return nullptr;
+    }
+    tex->setRelease(deleteImageTexture, new BufferCleanupHelper(image, display));
+    sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex)));
+
+    if (0 == origin.fX && 0 == origin.fY &&
+            info.width() == backendTex.width() && info.height() == backendTex.height()) {
+        // If the caller wants the entire texture, we're done
+        return proxy;
+    } else {
+        // Otherwise, make a copy of the requested subset.
+        GrSurfaceDesc desc;
+        desc.fConfig = proxy->config();
+        desc.fWidth = info.width();
+        desc.fHeight = info.height();
+        desc.fOrigin = proxy->origin();
+        desc.fIsMipMapped = proxy->isMipMapped();
+
+        sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeDeferredSurfaceContext(
+            desc, SkBackingFit::kExact, SkBudgeted::kYes));
+        if (!sContext) {
+            return nullptr;
+        }
+
+        SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
+        if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
+            return nullptr;
+        }
+
+        return sContext->asTextureProxyRef();
+    }
+}
+#endif
+
+bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
+    if (nullptr == context) {
+        return false; //CPU backend is not supported, because hardware buffer can be swizzled
+    }
+    // TODO: add Vulkan support
+    return kOpenGL_GrBackend == context->contextPriv().getBackend();
+}
+
+#endif //SK_BUILD_FOR_ANDROID_FRAMEWORK
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.h b/src/gpu/GrAHardwareBufferImageGenerator.h
new file mode 100644
index 0000000..4a5004b
--- /dev/null
+++ b/src/gpu/GrAHardwareBufferImageGenerator.h
@@ -0,0 +1,52 @@
+/*
+ * 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 GrAHardwareBufferImageGenerator_DEFINED
+#define GrAHardwareBufferImageGenerator_DEFINED
+
+#include "SkImageGenerator.h"
+
+#include <android/hardware_buffer.h>
+
+/**
+ *  GrAHardwareBufferImageGenerator allows to create an SkImage attached to
+ *  an existing android native hardware buffer. A hardware buffer has to be
+ *  created with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE usage, because it is
+ *  bound to an external texture using an EGLImage. The image generator will
+ *  keep a reference to the hardware buffer for its lifetime. A hardware buffer
+ *  can be shared between processes and same buffer can be used in multiple GPU
+ *  contexts.
+ *  To implement certain features like tiling, Skia may copy the texture to
+ *  avoid OpenGL API limitations.
+ */
+class GrAHardwareBufferImageGenerator : public SkImageGenerator {
+public:
+    static std::unique_ptr<SkImageGenerator> Make(AHardwareBuffer*, SkAlphaType,
+                                                  sk_sp<SkColorSpace>);
+
+    ~GrAHardwareBufferImageGenerator() override;
+
+protected:
+
+    bool onIsValid(GrContext*) const override;
+
+#if SK_SUPPORT_GPU
+    bool onCanGenerateTexture() const override { return true; }
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
+                                            const SkIPoint&) override;
+#endif
+
+private:
+    GrAHardwareBufferImageGenerator(const SkImageInfo&, AHardwareBuffer*, SkAlphaType);
+
+    static void deleteImageTexture(void* ctx);
+
+    AHardwareBuffer* fGraphicBuffer;
+    SkAlphaType fAlphaType;
+
+    typedef SkImageGenerator INHERITED;
+};
+#endif  // GrAHardwareBufferImageGenerator_DEFINED
diff --git a/src/gpu/GrAuditTrail.cpp b/src/gpu/GrAuditTrail.cpp
index 133ffea..411be3f 100644
--- a/src/gpu/GrAuditTrail.cpp
+++ b/src/gpu/GrAuditTrail.cpp
@@ -10,7 +10,7 @@
 
 const int GrAuditTrail::kGrAuditTrailInvalidID = -1;
 
-void GrAuditTrail::addOp(const GrOp* op, GrGpuResource::UniqueID renderTargetID) {
+void GrAuditTrail::addOp(const GrOp* op, GrRenderTargetProxy::UniqueID proxyID) {
     SkASSERT(fEnabled);
     Op* auditOp = new Op;
     fOpPool.emplace_back(auditOp);
@@ -44,7 +44,7 @@
 
     // We use the op pointer as a key to find the OpNode we are 'glomming' ops onto
     fIDLookup.set(op->uniqueID(), auditOp->fOpListID);
-    OpNode* opNode = new OpNode(renderTargetID);
+    OpNode* opNode = new OpNode(proxyID);
     opNode->fBounds = op->bounds();
     opNode->fChildren.push_back(auditOp);
     fOpList.emplace_back(opNode);
@@ -89,7 +89,7 @@
     const OpNode* bn = fOpList[opListID].get();
     SkASSERT(bn);
     outOpInfo->fBounds = bn->fBounds;
-    outOpInfo->fRenderTargetUniqueID = bn->fRenderTargetUniqueID;
+    outOpInfo->fProxyUniqueID    = bn->fProxyUniqueID;
     for (int j = 0; j < bn->fChildren.count(); j++) {
         OpInfo::Op& outOp = outOpInfo->fOps.push_back();
         const Op* currentOp = bn->fChildren[j];
@@ -286,7 +286,7 @@
 SkString GrAuditTrail::OpNode::toJson() const {
     SkString json;
     json.append("{");
-    json.appendf("\"RenderTarget\": \"%u\",", fRenderTargetUniqueID.asUInt());
+    json.appendf("\"ProxyID\": \"%u\",", fProxyUniqueID.asUInt());
     skrect_to_json(&json, "Bounds", fBounds);
     JsonifyTArray(&json, "Ops", fChildren, true);
     json.append("}");
diff --git a/src/gpu/GrAutoLocaleSetter.h b/src/gpu/GrAutoLocaleSetter.h
index cec041e..9cfa637 100644
--- a/src/gpu/GrAutoLocaleSetter.h
+++ b/src/gpu/GrAutoLocaleSetter.h
@@ -54,7 +54,7 @@
             name = nullptr;
         }
 #endif
-        fLocale = newlocale(LC_ALL, name, 0);
+        fLocale = newlocale(LC_ALL_MASK, name, 0);
         if (fLocale) {
             fOldLocale = uselocale(fLocale);
         } else {
diff --git a/src/gpu/GrBackendSurface.cpp b/src/gpu/GrBackendSurface.cpp
new file mode 100644
index 0000000..6304475
--- /dev/null
+++ b/src/gpu/GrBackendSurface.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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 "GrBackendSurface.h"
+
+#ifdef SK_VULKAN
+#include "vk/GrVkTypes.h"
+#include "vk/GrVkUtil.h"
+#endif
+
+GrBackendTexture::GrBackendTexture(int width,
+                                   int height,
+                                   const GrVkImageInfo& vkInfo)
+        : fWidth(width)
+        , fHeight(height)
+        , fConfig(
+#ifdef SK_VULKAN
+                  GrVkFormatToPixelConfig(vkInfo.fFormat)
+#else
+                  kUnknown_GrPixelConfig
+#endif
+                  )
+        , fBackend(kVulkan_GrBackend)
+        , fVkInfo(vkInfo) {}
+
+GrBackendTexture::GrBackendTexture(int width,
+                                   int height,
+                                   GrPixelConfig config,
+                                   const GrGLTextureInfo& glInfo)
+        : fWidth(width)
+        , fHeight(height)
+        , fConfig(config)
+        , fBackend(kOpenGL_GrBackend)
+        , fGLInfo(glInfo) {}
+
+GrBackendTexture::GrBackendTexture(const GrBackendTextureDesc& desc, GrBackend backend)
+        : fWidth(desc.fWidth)
+        , fHeight(desc.fHeight)
+        , fConfig(desc.fConfig)
+        , fBackend(backend) {
+    if (kOpenGL_GrBackend == backend) {
+        fGLInfo = *reinterpret_cast<const GrGLTextureInfo*>(desc.fTextureHandle);
+    } else {
+        SkASSERT(kVulkan_GrBackend == backend);
+#ifdef SK_VULKAN
+        const GrVkImageInfo* vkInfo =
+                reinterpret_cast<const GrVkImageInfo*>(desc.fTextureHandle);
+        fConfig = GrVkFormatToPixelConfig(vkInfo->fFormat);
+        fVkInfo = *vkInfo;
+#else
+        fConfig = kUnknown_GrPixelConfig;
+#endif
+    }
+}
+
+const GrVkImageInfo* GrBackendTexture::getVkImageInfo() const {
+    if (kVulkan_GrBackend == fBackend) {
+        return &fVkInfo;
+    }
+    return nullptr;
+}
+
+const GrGLTextureInfo* GrBackendTexture::getGLTextureInfo() const {
+    if (kOpenGL_GrBackend == fBackend) {
+        return &fGLInfo;
+    }
+    return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+GrBackendRenderTarget::GrBackendRenderTarget(int width,
+                                             int height,
+                                             int sampleCnt,
+                                             int stencilBits,
+                                             const GrVkImageInfo& vkInfo)
+        : fWidth(width)
+        , fHeight(height)
+        , fSampleCnt(sampleCnt)
+        , fStencilBits(stencilBits)
+        , fConfig(
+#ifdef SK_VULKAN
+                  GrVkFormatToPixelConfig(vkInfo.fFormat)
+#else
+                  kUnknown_GrPixelConfig
+#endif
+                  )
+        , fBackend(kVulkan_GrBackend)
+        , fVkInfo(vkInfo) {}
+
+GrBackendRenderTarget::GrBackendRenderTarget(int width,
+                                             int height,
+                                             int sampleCnt,
+                                             int stencilBits,
+                                             GrPixelConfig config,
+                                             const GrGLFramebufferInfo& glInfo)
+        : fWidth(width)
+        , fHeight(height)
+        , fSampleCnt(sampleCnt)
+        , fStencilBits(stencilBits)
+        , fConfig(config)
+        , fBackend(kOpenGL_GrBackend)
+        , fGLInfo(glInfo) {}
+
+GrBackendRenderTarget::GrBackendRenderTarget(const GrBackendRenderTargetDesc& desc,
+                                             GrBackend backend)
+        : fWidth(desc.fWidth)
+        , fHeight(desc.fHeight)
+        , fSampleCnt(desc.fSampleCnt)
+        , fStencilBits(desc.fStencilBits)
+        , fConfig(desc.fConfig)
+        , fBackend(backend) {
+    if (kOpenGL_GrBackend == backend) {
+        fGLInfo.fFBOID = static_cast<GrGLuint>(desc.fRenderTargetHandle);
+    } else {
+        SkASSERT(kVulkan_GrBackend == backend);
+#ifdef SK_VULKAN
+        const GrVkImageInfo* vkInfo =
+                reinterpret_cast<const GrVkImageInfo*>(desc.fRenderTargetHandle);
+        fConfig = GrVkFormatToPixelConfig(vkInfo->fFormat);
+        fVkInfo = *vkInfo;
+#else
+        fConfig = kUnknown_GrPixelConfig;
+#endif
+    }
+}
+
+const GrVkImageInfo* GrBackendRenderTarget::getVkImageInfo() const {
+    if (kVulkan_GrBackend == fBackend) {
+        return &fVkInfo;
+    }
+    return nullptr;
+}
+
+const GrGLFramebufferInfo* GrBackendRenderTarget::getGLFramebufferInfo() const {
+    if (kOpenGL_GrBackend == fBackend) {
+        return &fGLInfo;
+    }
+    return nullptr;
+}
+
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
new file mode 100644
index 0000000..02f172b
--- /dev/null
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 "GrBackendTextureImageGenerator.h"
+
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceCache.h"
+#include "GrResourceProvider.h"
+#include "GrSemaphore.h"
+
+#include "SkGr.h"
+#include "SkMessageBus.h"
+
+GrBackendTextureImageGenerator::RefHelper::~RefHelper() {
+    SkASSERT(nullptr == fBorrowedTexture);
+    SkASSERT(SK_InvalidGenID == fBorrowingContextID);
+
+    // Generator has been freed, and no one is borrowing the texture. Notify the original cache
+    // that it can free the last ref, so it happens on the correct thread.
+    GrGpuResourceFreedMessage msg { fOriginalTexture, fOwningContextID };
+    SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
+}
+
+// TODO: I copied this from SkImage_Gpu, perhaps we put a version of this somewhere else?
+static GrBackendTexture make_backend_texture_from_handle(GrBackend backend,
+                                                         int width, int height,
+                                                         GrPixelConfig config,
+                                                         GrBackendObject handle) {
+    if (kOpenGL_GrBackend == backend) {
+        GrGLTextureInfo* glInfo = (GrGLTextureInfo*)(handle);
+        return GrBackendTexture(width, height, config, *glInfo);
+    } else {
+        SkASSERT(kVulkan_GrBackend == backend);
+        GrVkImageInfo* vkInfo = (GrVkImageInfo*)(handle);
+        return GrBackendTexture(width, height, *vkInfo);
+    }
+}
+
+std::unique_ptr<SkImageGenerator>
+GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, sk_sp<GrSemaphore> semaphore,
+                                     SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
+    if (colorSpace && (!colorSpace->gammaCloseToSRGB() && !colorSpace->gammaIsLinear())) {
+        return nullptr;
+    }
+
+    SkColorType colorType = kUnknown_SkColorType;
+    if (!GrPixelConfigToColorType(texture->config(), &colorType)) {
+        return nullptr;
+    }
+
+    GrContext* context = texture->getContext();
+
+    // Attach our texture to this context's resource cache. This ensures that deletion will happen
+    // in the correct thread/context. This adds the only ref to the texture that will persist from
+    // this point. That ref will be released when the generator's RefHelper is freed.
+    context->getResourceCache()->insertCrossContextGpuResource(texture.get());
+
+    GrBackend backend = context->contextPriv().getBackend();
+    GrBackendTexture backendTexture = make_backend_texture_from_handle(backend,
+                                                                       texture->width(),
+                                                                       texture->height(),
+                                                                       texture->config(),
+                                                                       texture->getTextureHandle());
+
+    SkImageInfo info = SkImageInfo::Make(texture->width(), texture->height(), colorType, alphaType,
+                                         std::move(colorSpace));
+    return std::unique_ptr<SkImageGenerator>(new GrBackendTextureImageGenerator(
+            info, texture.get(), context->uniqueID(), std::move(semaphore), backendTexture));
+}
+
+GrBackendTextureImageGenerator::GrBackendTextureImageGenerator(const SkImageInfo& info,
+                                                               GrTexture* texture,
+                                                               uint32_t owningContextID,
+                                                               sk_sp<GrSemaphore> semaphore,
+                                                               const GrBackendTexture& backendTex)
+    : INHERITED(info)
+    , fRefHelper(new RefHelper(texture, owningContextID))
+    , fSemaphore(std::move(semaphore))
+    , fLastBorrowingContextID(SK_InvalidGenID)
+    , fBackendTexture(backendTex)
+    , fSurfaceOrigin(texture->origin()) { }
+
+GrBackendTextureImageGenerator::~GrBackendTextureImageGenerator() {
+    fRefHelper->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+void GrBackendTextureImageGenerator::ReleaseRefHelper_TextureReleaseProc(void* ctx) {
+    RefHelper* refHelper = static_cast<RefHelper*>(ctx);
+    SkASSERT(refHelper);
+
+    // Release texture so another context can use it
+    refHelper->fBorrowedTexture = nullptr;
+    refHelper->fBorrowingContextID = SK_InvalidGenID;
+    refHelper->unref();
+}
+
+sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(GrContext* context,
+                                                                        const SkImageInfo& info,
+                                                                        const SkIPoint& origin) {
+    SkASSERT(context);
+
+    if (context->contextPriv().getBackend() != fBackendTexture.backend()) {
+        return nullptr;
+    }
+
+    sk_sp<GrTexture> tex;
+
+    if (fRefHelper->fBorrowingContextID == context->uniqueID()) {
+        // If a client re-draws the same image multiple times, the texture we return will be cached
+        // and re-used. If they draw a subset, though, we may be re-called. In that case, we want
+        // to re-use the borrowed texture we've previously created.
+        tex = sk_ref_sp(fRefHelper->fBorrowedTexture);
+        SkASSERT(tex);
+    } else {
+        // The texture is available or borrwed by another context. Try for exclusive access.
+        uint32_t expectedID = SK_InvalidGenID;
+        if (!fRefHelper->fBorrowingContextID.compare_exchange(&expectedID, context->uniqueID())) {
+            // Some other context is currently borrowing the texture. We aren't allowed to use it.
+            return nullptr;
+        } else {
+            // Wait on a semaphore when a new context has just started borrowing the texture. This
+            // is conservative, but shouldn't be too expensive.
+            if (fSemaphore && fLastBorrowingContextID != context->uniqueID()) {
+                context->getGpu()->waitSemaphore(fSemaphore);
+                fLastBorrowingContextID = context->uniqueID();
+            }
+        }
+
+        // We just gained access to the texture. If we're on the original context, we could use the
+        // original texture, but we'd have no way of detecting that it's no longer in-use. So we
+        // always make a wrapped copy, where the release proc informs us that the context is done
+        // with it. This is unfortunate - we'll have two texture objects referencing the same GPU
+        // object. However, no client can ever see the original texture, so this should be safe.
+        tex = context->resourceProvider()->wrapBackendTexture(fBackendTexture, fSurfaceOrigin,
+                                                              kNone_GrBackendTextureFlag, 0,
+                                                              kBorrow_GrWrapOwnership);
+        if (!tex) {
+            fRefHelper->fBorrowingContextID = SK_InvalidGenID;
+            return nullptr;
+        }
+        fRefHelper->fBorrowedTexture = tex.get();
+
+        tex->setRelease(ReleaseRefHelper_TextureReleaseProc, fRefHelper);
+        fRefHelper->ref();
+    }
+
+    SkASSERT(fRefHelper->fBorrowingContextID == context->uniqueID());
+
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
+
+    if (0 == origin.fX && 0 == origin.fY &&
+        info.width() == fBackendTexture.width() && info.height() == fBackendTexture.height()) {
+        // If the caller wants the entire texture, we're done
+        return proxy;
+    } else {
+        // Otherwise, make a copy of the requested subset. Make sure our temporary is renderable,
+        // because Vulkan will want to do the copy as a draw.
+        GrSurfaceDesc desc;
+        desc.fConfig = proxy->config();
+        desc.fWidth = info.width();
+        desc.fHeight = info.height();
+        desc.fOrigin = proxy->origin();
+        desc.fIsMipMapped = proxy->isMipMapped();
+        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+
+        sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeDeferredSurfaceContext(
+            desc, SkBackingFit::kExact, SkBudgeted::kYes));
+        if (!sContext) {
+            return nullptr;
+        }
+
+        SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
+        if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
+            return nullptr;
+        }
+
+        return sContext->asTextureProxyRef();
+    }
+}
+#endif
diff --git a/src/gpu/GrBackendTextureImageGenerator.h b/src/gpu/GrBackendTextureImageGenerator.h
new file mode 100644
index 0000000..2ada687
--- /dev/null
+++ b/src/gpu/GrBackendTextureImageGenerator.h
@@ -0,0 +1,72 @@
+/*
+ * 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 GrBackendTextureImageGenerator_DEFINED
+#define GrBackendTextureImageGenerator_DEFINED
+
+#include "SkImageGenerator.h"
+
+#include "GrBackendSurface.h"
+#include "SkAtomics.h"
+
+class GrSemaphore;
+
+class GrBackendTextureImageGenerator : public SkImageGenerator {
+public:
+    static std::unique_ptr<SkImageGenerator> Make(sk_sp<GrTexture>, sk_sp<GrSemaphore>,
+                                                  SkAlphaType, sk_sp<SkColorSpace>);
+
+    ~GrBackendTextureImageGenerator() override;
+
+protected:
+    // NOTE: We would like to validate that the owning context hasn't been abandoned, but we can't
+    // do that safely (we might be on another thread). So assume everything is fine.
+    bool onIsValid(GrContext*) const override { return true; }
+
+#if SK_SUPPORT_GPU
+    bool onCanGenerateTexture() const override { return true; }
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
+                                            const SkIPoint&) override;
+#endif
+
+private:
+    GrBackendTextureImageGenerator(const SkImageInfo& info, GrTexture*,
+                                   uint32_t owningContextID, sk_sp<GrSemaphore>,
+                                   const GrBackendTexture&);
+
+    static void ReleaseRefHelper_TextureReleaseProc(void* ctx);
+
+    class RefHelper : public SkNVRefCnt<RefHelper> {
+    public:
+        RefHelper(GrTexture* texture, uint32_t owningContextID)
+            : fOriginalTexture(texture)
+            , fOwningContextID(owningContextID)
+            , fBorrowedTexture(nullptr)
+            , fBorrowingContextID(SK_InvalidGenID) { }
+
+        ~RefHelper();
+
+        GrTexture* fOriginalTexture;
+        uint32_t fOwningContextID;
+
+        // There is never a 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 lets us
+        // avoid releasing a ref from another thread, or get into races during context shutdown.
+        GrTexture* fBorrowedTexture;
+        SkAtomic<uint32_t> fBorrowingContextID;
+    };
+
+    RefHelper* fRefHelper;
+
+    sk_sp<GrSemaphore> fSemaphore;
+    uint32_t fLastBorrowingContextID;
+
+    GrBackendTexture fBackendTexture;
+    GrSurfaceOrigin fSurfaceOrigin;
+
+    typedef SkImageGenerator INHERITED;
+};
+#endif  // GrBackendTextureImageGenerator_DEFINED
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp
index 6c77167..bde8276 100644
--- a/src/gpu/GrBitmapTextureMaker.cpp
+++ b/src/gpu/GrBitmapTextureMaker.cpp
@@ -41,7 +41,8 @@
         proxy = GrGenerateMipMapsAndUploadToTextureProxy(this->context(), fBitmap, dstColorSpace);
     }
     if (!proxy) {
-        proxy = GrUploadBitmapToTextureProxy(this->context()->resourceProvider(), fBitmap);
+        proxy = GrUploadBitmapToTextureProxy(this->context()->resourceProvider(), fBitmap,
+                                             dstColorSpace);
     }
     if (proxy && fOriginalKey.isValid()) {
         this->context()->resourceProvider()->assignUniqueKeyToProxy(fOriginalKey, proxy.get());
diff --git a/include/gpu/GrBuffer.h b/src/gpu/GrBuffer.h
similarity index 100%
rename from include/gpu/GrBuffer.h
rename to src/gpu/GrBuffer.h
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index ade64d6..ef6d5bb 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -21,7 +21,6 @@
         case kSRGBA_8888_GrPixelConfig: return "SRGBA8888";
         case kSBGRA_8888_GrPixelConfig: return "SBGRA8888";
         case kRGBA_8888_sint_GrPixelConfig: return "RGBA8888_sint";
-        case kETC1_GrPixelConfig: return "ETC1";
         case kRGBA_float_GrPixelConfig: return "RGBAFloat";
         case kRG_float_GrPixelConfig: return "RGFloat";
         case kAlpha_half_GrPixelConfig: return "AlphaHalf";
@@ -36,17 +35,15 @@
     fNPOTTextureTileSupport = false;
     fSRGBSupport = false;
     fSRGBWriteControl = false;
-    fTwoSidedStencilSupport = false;
-    fStencilWrapOpsSupport = false;
     fDiscardRenderTargetSupport = false;
     fReuseScratchTextures = true;
     fReuseScratchBuffers = true;
     fGpuTracingSupport = false;
-    fCompressedTexSubImageSupport = false;
     fOversizedStencilSupport = false;
     fTextureBarrierSupport = false;
     fSampleLocationsSupport = false;
     fMultisampleDisableSupport = false;
+    fInstanceAttribSupport = false;
     fUsesMixedSamples = false;
     fPreferClientSideDynamicBuffers = false;
     fFullClearIsFree = false;
@@ -73,11 +70,12 @@
     fMaxWindowRectangles = 0;
 
     fSuppressPrints = options.fSuppressPrints;
-    fImmediateFlush = options.fImmediateMode;
+    fWireframeMode = options.fWireframeMode;
     fBufferMapThreshold = options.fBufferMapThreshold;
     fUseDrawInsteadOfPartialRenderTargetWrite = options.fUseDrawInsteadOfPartialRenderTargetWrite;
     fUseDrawInsteadOfAllRenderTargetWrites = false;
     fAvoidInstancedDrawsToFPTargets = false;
+    fAvoidStencilBuffers = false;
 
     fPreferVRAMUseOverFlushes = true;
 }
@@ -96,6 +94,7 @@
                  GrWindowRectangles::kMaxWindows, fMaxWindowRectangles);
         fMaxWindowRectangles = GrWindowRectangles::kMaxWindows;
     }
+    fAvoidStencilBuffers = options.fAvoidStencilBuffers;
 }
 
 static SkString map_flags_to_string(uint32_t flags) {
@@ -125,17 +124,15 @@
     r.appendf("NPOT Texture Tile Support          : %s\n", gNY[fNPOTTextureTileSupport]);
     r.appendf("sRGB Support                       : %s\n", gNY[fSRGBSupport]);
     r.appendf("sRGB Write Control                 : %s\n", gNY[fSRGBWriteControl]);
-    r.appendf("Two Sided Stencil Support          : %s\n", gNY[fTwoSidedStencilSupport]);
-    r.appendf("Stencil Wrap Ops  Support          : %s\n", gNY[fStencilWrapOpsSupport]);
     r.appendf("Discard Render Target Support      : %s\n", gNY[fDiscardRenderTargetSupport]);
     r.appendf("Reuse Scratch Textures             : %s\n", gNY[fReuseScratchTextures]);
     r.appendf("Reuse Scratch Buffers              : %s\n", gNY[fReuseScratchBuffers]);
     r.appendf("Gpu Tracing Support                : %s\n", gNY[fGpuTracingSupport]);
-    r.appendf("Compressed Update Support          : %s\n", gNY[fCompressedTexSubImageSupport]);
     r.appendf("Oversized Stencil Support          : %s\n", gNY[fOversizedStencilSupport]);
     r.appendf("Texture Barrier Support            : %s\n", gNY[fTextureBarrierSupport]);
     r.appendf("Sample Locations Support           : %s\n", gNY[fSampleLocationsSupport]);
     r.appendf("Multisample disable support        : %s\n", gNY[fMultisampleDisableSupport]);
+    r.appendf("Instance Attrib Support            : %s\n", gNY[fInstanceAttribSupport]);
     r.appendf("Uses Mixed Samples                 : %s\n", gNY[fUsesMixedSamples]);
     r.appendf("Prefer client-side dynamic buffers : %s\n", gNY[fPreferClientSideDynamicBuffers]);
     r.appendf("Full screen clear is free          : %s\n", gNY[fFullClearIsFree]);
diff --git a/src/gpu/GrClip.h b/src/gpu/GrClip.h
index c44653b..2e247c8 100644
--- a/src/gpu/GrClip.h
+++ b/src/gpu/GrClip.h
@@ -90,12 +90,16 @@
      */
     template <typename TRect>
     constexpr static bool IsOutsideClip(const TRect& outerClipBounds, const SkRect& queryBounds) {
-        return outerClipBounds.fRight - outerClipBounds.fLeft <= kBoundsTolerance ||
-               outerClipBounds.fBottom - outerClipBounds.fTop <= kBoundsTolerance ||
-               outerClipBounds.fLeft >= queryBounds.fRight - kBoundsTolerance ||
-               outerClipBounds.fTop >= queryBounds.fBottom - kBoundsTolerance ||
-               outerClipBounds.fRight <= queryBounds.fLeft + kBoundsTolerance ||
-               outerClipBounds.fBottom <= queryBounds.fTop + kBoundsTolerance;
+        return
+            // Is the clip so small that it is effectively empty?
+            outerClipBounds.fRight - outerClipBounds.fLeft <= kBoundsTolerance ||
+            outerClipBounds.fBottom - outerClipBounds.fTop <= kBoundsTolerance ||
+
+            // Are the query bounds effectively outside the clip?
+            outerClipBounds.fLeft >= queryBounds.fRight - kBoundsTolerance ||
+            outerClipBounds.fTop >= queryBounds.fBottom - kBoundsTolerance ||
+            outerClipBounds.fRight <= queryBounds.fLeft + kBoundsTolerance ||
+            outerClipBounds.fBottom <= queryBounds.fTop + kBoundsTolerance;
     }
 
     /**
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index b010981..15f9767 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -115,18 +115,12 @@
 
         GrShape shape(path, GrStyle::SimpleFill());
         GrPathRenderer::CanDrawPathArgs canDrawArgs;
-        canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
+        canDrawArgs.fCaps = context->caps();
         canDrawArgs.fViewMatrix = &viewMatrix;
         canDrawArgs.fShape = &shape;
-        if (!element->isAA()) {
-            canDrawArgs.fAAType = GrAAType::kNone;
-        } else if (renderTargetContext->isUnifiedMultisampled()) {
-            canDrawArgs.fAAType = GrAAType::kMSAA;
-        } else if (renderTargetContext->isStencilBufferMultisampled()){
-            canDrawArgs.fAAType = GrAAType::kMixedSamples;
-        } else {
-            canDrawArgs.fAAType = GrAAType::kCoverage;
-        }
+        canDrawArgs.fAAType = GrChooseAAType(GrBoolToAA(element->isAA()),
+                                             renderTargetContext->fsaaType(),
+                                             GrAllowMixedSamples::kYes);
         canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
 
         // the 'false' parameter disallows use of the SW path renderer
@@ -152,6 +146,10 @@
     // a clip gets complex enough it can just be done in SW regardless
     // of whether it would invoke the GrSoftwarePathRenderer.
 
+    // If we're avoiding stencils, always use SW:
+    if (context->caps()->avoidStencilBuffers())
+        return true;
+
     // Set the matrix so that rendered clip elements are transformed to mask space from clip
     // space.
     SkMatrix translate;
@@ -283,6 +281,8 @@
     SkASSERT(rtIBounds.contains(clipIBounds)); // Mask shouldn't be larger than the RT.
 #endif
 
+    bool avoidStencilBuffers = context->caps()->avoidStencilBuffers();
+
     // An element count of 4 was chosen because of the common pattern in Blink of:
     //   isect RR
     //   diff  RR
@@ -294,7 +294,8 @@
     if (reducedClip.elements().count() <= kMaxAnalyticElements) {
         // When there are multiple samples we want to do per-sample clipping, not compute a
         // fractional pixel coverage.
-        bool disallowAnalyticAA = renderTargetContext->isStencilBufferMultisampled();
+        bool disallowAnalyticAA =
+                GrFSAAType::kNone != renderTargetContext->fsaaType() && !avoidStencilBuffers;
         if (disallowAnalyticAA && !renderTargetContext->numColorSamples()) {
             // With a single color sample, any coverage info is lost from color once it hits the
             // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe
@@ -302,7 +303,7 @@
             disallowAnalyticAA = useHWAA || hasUserStencilSettings;
         }
         sk_sp<GrFragmentProcessor> clipFP;
-        if (reducedClip.requiresAA() &&
+        if ((reducedClip.requiresAA() || avoidStencilBuffers) &&
             get_analytic_clip_processor(reducedClip.elements(), disallowAnalyticAA, devBounds,
                                         &clipFP)) {
             out->addCoverageFP(std::move(clipFP));
@@ -311,7 +312,8 @@
     }
 
     // If the stencil buffer is multisampled we can use it to do everything.
-    if (!renderTargetContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) {
+    if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.requiresAA()) ||
+        avoidStencilBuffers) {
         sk_sp<GrTextureProxy> result;
         if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) {
             // The clip geometry is complex enough that it will be more efficient to create it
@@ -328,7 +330,13 @@
                                                   reducedClip.ibounds()));
             return true;
         }
-        // if alpha clip mask creation fails fall through to the non-AA code paths
+
+        // If alpha or software clip mask creation fails, fall through to the stencil code paths,
+        // unless stencils are disallowed.
+        if (context->caps()->avoidStencilBuffers()) {
+            SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. Clip will be ignored.\n");
+            return false;
+        }
     }
 
     GrRenderTarget* rt = renderTargetContext->accessRenderTarget();
@@ -392,7 +400,7 @@
         return proxy;
     }
 
-    sk_sp<GrRenderTargetContext> rtc(context->makeRenderTargetContextWithFallback(
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContextWithFallback(
                                                                              SkBackingFit::kApprox,
                                                                              reducedClip.width(),
                                                                              reducedClip.height(),
diff --git a/include/gpu/GrColorSpaceXform.h b/src/gpu/GrColorSpaceXform.h
similarity index 100%
rename from include/gpu/GrColorSpaceXform.h
rename to src/gpu/GrColorSpaceXform.h
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 56d8117..17db956 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -26,13 +26,20 @@
 #include "effects/GrConfigConversionEffect.h"
 #include "text/GrTextBlobCache.h"
 
+#define ASSERT_OWNED_PROXY(P) \
+SkASSERT(!(P) || !((P)->priv().peekTexture()) || (P)->priv().peekTexture()->getContext() == this)
+#define ASSERT_OWNED_PROXY_PRIV(P) \
+SkASSERT(!(P) || !((P)->priv().peekTexture()) || (P)->priv().peekTexture()->getContext() == fContext)
+
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
 #define ASSERT_SINGLE_OWNER \
     SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(&fSingleOwner);)
 #define ASSERT_SINGLE_OWNER_PRIV \
     SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(&fContext->fSingleOwner);)
 #define RETURN_IF_ABANDONED if (fDrawingManager->wasAbandoned()) { return; }
+#define RETURN_IF_ABANDONED_PRIV if (fContext->fDrawingManager->wasAbandoned()) { return; }
 #define RETURN_FALSE_IF_ABANDONED if (fDrawingManager->wasAbandoned()) { return false; }
+#define RETURN_FALSE_IF_ABANDONED_PRIV if (fContext->fDrawingManager->wasAbandoned()) { return false; }
 #define RETURN_NULL_IF_ABANDONED if (fDrawingManager->wasAbandoned()) { return nullptr; }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -76,6 +83,8 @@
     ASSERT_SINGLE_OWNER
     SkASSERT(!fGpu);
 
+    fBackend = backend;
+
     fGpu = GrGpu::Create(backend, backendContext, options, this);
     if (!fGpu) {
         return false;
@@ -88,22 +97,18 @@
     ASSERT_SINGLE_OWNER
 
     fCaps = SkRef(fGpu->caps());
-    fResourceCache = new GrResourceCache(fCaps);
+    fResourceCache = new GrResourceCache(fCaps, fUniqueID);
     fResourceProvider = new GrResourceProvider(fGpu, fResourceCache, &fSingleOwner);
 
     fDisableGpuYUVConversion = options.fDisableGpuYUVConversion;
     fDidTestPMConversions = false;
 
-    GrRenderTargetOpList::Options rtOpListOptions;
-    rtOpListOptions.fMaxOpCombineLookback = options.fMaxOpCombineLookback;
-    rtOpListOptions.fMaxOpCombineLookahead = options.fMaxOpCombineLookahead;
     GrPathRendererChain::Options prcOptions;
     prcOptions.fAllowPathMaskCaching = options.fAllowPathMaskCaching;
     prcOptions.fGpuPathRenderers = options.fGpuPathRenderers;
-    fDrawingManager.reset(new GrDrawingManager(this, rtOpListOptions, prcOptions,
-                                               options.fImmediateMode, &fSingleOwner));
+    fDrawingManager.reset(new GrDrawingManager(this, prcOptions, &fSingleOwner));
 
-    fAtlasGlyphCache = new GrAtlasGlyphCache(this);
+    fAtlasGlyphCache = new GrAtlasGlyphCache(this, options.fGlyphCacheTextureMaximumBytes);
 
     fTextBlobCache.reset(new GrTextBlobCache(TextBlobCacheOverBudgetCB, this));
 }
@@ -198,6 +203,11 @@
     fResourceCache->purgeResourcesNotUsedSince(GrStdSteadyClock::now() - ms);
 }
 
+void GrContext::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
+    ASSERT_SINGLE_OWNER
+    fResourceCache->purgeUnlockedResources(bytesToPurge, preferScratchResources);
+}
+
 void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
     ASSERT_SINGLE_OWNER
 
@@ -209,6 +219,11 @@
     }
 }
 
+size_t GrContext::getResourceCachePurgeableBytes() const {
+    ASSERT_SINGLE_OWNER
+    return fResourceCache->getPurgeableBytes();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrContext::TextBlobCacheOverBudgetCB(void* data) {
@@ -226,7 +241,16 @@
 void GrContext::flush() {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
-    fDrawingManager->flush();
+
+    fDrawingManager->flush(nullptr);
+}
+
+void GrContextPriv::flush(GrSurfaceProxy* proxy) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    ASSERT_OWNED_PROXY_PRIV(proxy);
+
+    fContext->fDrawingManager->flush(proxy);
 }
 
 bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
@@ -247,61 +271,90 @@
     return true;
 }
 
-static bool valid_unpremul_config(GrPixelConfig config) {
+static bool valid_premul_config(GrPixelConfig config) {
     return GrPixelConfigIs8888Unorm(config) || kRGBA_half_GrPixelConfig == config;
 }
 
-bool GrContext::writeSurfacePixels(GrSurface* surface, SkColorSpace* dstColorSpace,
-                                   int left, int top, int width, int height,
-                                   GrPixelConfig srcConfig, SkColorSpace* srcColorSpace,
-                                   const void* buffer, size_t rowBytes, uint32_t pixelOpsFlags) {
+static bool valid_pixel_conversion(GrPixelConfig srcConfig, GrPixelConfig dstConfig,
+                                   bool premulConversion) {
+    // We don't allow conversion between integer configs and float/fixed configs.
+    if (GrPixelConfigIsSint(srcConfig) != GrPixelConfigIsSint(dstConfig)) {
+        return false;
+    }
+
+    // We only allow premul <-> unpremul conversions for some formats
+    if (premulConversion && (!valid_premul_config(srcConfig) || !valid_premul_config(dstConfig))) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool pm_upm_must_round_trip(GrPixelConfig config, SkColorSpace* colorSpace) {
+    return !colorSpace &&
+           (kRGBA_8888_GrPixelConfig == config || kBGRA_8888_GrPixelConfig == config);
+}
+
+bool GrContextPriv::writeSurfacePixels(GrSurfaceContext* dst,
+                                       int left, int top, int width, int height,
+                                       GrPixelConfig srcConfig, SkColorSpace* srcColorSpace,
+                                       const void* buffer, size_t rowBytes,
+                                       uint32_t pixelOpsFlags) {
     // TODO: Color space conversion
 
-    ASSERT_SINGLE_OWNER
-    RETURN_FALSE_IF_ABANDONED
-    ASSERT_OWNED_RESOURCE(surface);
-    SkASSERT(surface);
-    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::writeSurfacePixels");
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_FALSE_IF_ABANDONED_PRIV
+    SkASSERT(dst);
+    ASSERT_OWNED_PROXY_PRIV(dst->asSurfaceProxy());
+    GR_AUDIT_TRAIL_AUTO_FRAME(&fContext->fAuditTrail, "GrContextPriv::writeSurfacePixels");
 
-    this->testPMConversionsIfNecessary(pixelOpsFlags);
+    if (!dst->asSurfaceProxy()->instantiate(fContext->resourceProvider())) {
+        return false;
+    }
+
+    GrSurface* dstSurface = dst->asSurfaceProxy()->priv().peekSurface();
+
+    // The src is unpremul but the dst is premul -> premul the src before or as part of the write
+    const bool premul = SkToBool(kUnpremul_PixelOpsFlag & pixelOpsFlags);
+    if (!valid_pixel_conversion(srcConfig, dstSurface->config(), premul)) {
+        return false;
+    }
+
+    // We need to guarantee round-trip conversion if we are reading and writing 8888 non-sRGB data,
+    // without any color spaces attached, and the caller wants us to premul.
+    bool useConfigConversionEffect =
+                        premul &&
+                        pm_upm_must_round_trip(srcConfig, srcColorSpace) &&
+                        pm_upm_must_round_trip(dstSurface->config(), dst->getColorSpace());
+
+    // Are we going to try to premul as part of a draw? For the non-legacy case, we always allow
+    // this. GrConfigConversionEffect fails on some GPUs, so only allow this if it works perfectly.
+    bool premulOnGpu = premul &&
+                       (!useConfigConversionEffect || fContext->validPMUPMConversionExists());
 
     // Trim the params here so that if we wind up making a temporary surface it can be as small as
     // necessary and because GrGpu::getWritePixelsInfo requires it.
-    if (!GrSurfacePriv::AdjustWritePixelParams(surface->width(), surface->height(),
+    if (!GrSurfacePriv::AdjustWritePixelParams(dstSurface->width(), dstSurface->height(),
                                                GrBytesPerPixel(srcConfig), &left, &top, &width,
                                                &height, &buffer, &rowBytes)) {
         return false;
     }
 
-    bool applyPremulToSrc = SkToBool(kUnpremul_PixelOpsFlag & pixelOpsFlags);
-    if (applyPremulToSrc && !valid_unpremul_config(srcConfig)) {
-        return false;
-    }
-    // We don't allow conversion between integer configs and float/fixed configs.
-    if (GrPixelConfigIsSint(surface->config()) != GrPixelConfigIsSint(srcConfig)) {
-        return false;
-    }
-
-    GrGpu::DrawPreference drawPreference = GrGpu::kNoDraw_DrawPreference;
-    // Don't prefer to draw for the conversion (and thereby access a texture from the cache) when
-    // we've already determined that there isn't a roundtrip preserving conversion processor pair.
-    if (applyPremulToSrc && this->validPMUPMConversionExists(srcConfig)) {
-        drawPreference = GrGpu::kCallerPrefersDraw_DrawPreference;
-    }
-
+    GrGpu::DrawPreference drawPreference = premulOnGpu ? GrGpu::kCallerPrefersDraw_DrawPreference
+                                                       : GrGpu::kNoDraw_DrawPreference;
     GrGpu::WritePixelTempDrawInfo tempDrawInfo;
-    if (!fGpu->getWritePixelsInfo(surface, width, height, srcConfig, &drawPreference,
-                                  &tempDrawInfo)) {
+    if (!fContext->fGpu->getWritePixelsInfo(dstSurface, width, height, srcConfig,
+                                            &drawPreference, &tempDrawInfo)) {
         return false;
     }
 
-    if (!(kDontFlush_PixelOpsFlag & pixelOpsFlags) && surface->surfacePriv().hasPendingIO()) {
-        this->flush();
+    if (!(kDontFlush_PixelOpsFlag & pixelOpsFlags) && dstSurface->surfacePriv().hasPendingIO()) {
+        this->flush(nullptr); // MDB TODO: tighten this
     }
 
     sk_sp<GrTextureProxy> tempProxy;
     if (GrGpu::kNoDraw_DrawPreference != drawPreference) {
-        tempProxy = GrSurfaceProxy::MakeDeferred(this->resourceProvider(),
+        tempProxy = GrSurfaceProxy::MakeDeferred(fContext->resourceProvider(),
                                                  tempDrawInfo.fTempSurfaceDesc,
                                                  SkBackingFit::kApprox,
                                                  SkBudgeted::kYes);
@@ -312,159 +365,135 @@
 
     // temp buffer for doing sw premul conversion, if needed.
     SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
-    if (tempProxy) {
-        sk_sp<GrFragmentProcessor> fp;
-        if (applyPremulToSrc) {
-            fp = this->createUPMToPMEffect(tempProxy, SkMatrix::I());
-            fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), tempDrawInfo.fSwizzle);
-            // If premultiplying was the only reason for the draw, fall back to a straight write.
-            if (!fp) {
-                if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference) {
-                    tempProxy.reset(nullptr);
-                }
-            } else {
-                applyPremulToSrc = false;
-            }
+    // We need to do sw premul if we were unable to create a RT for drawing, or if we can't do the
+    // premul on the GPU
+    if (premul && (!tempProxy || !premulOnGpu)) {
+        size_t tmpRowBytes = 4 * width;
+        tmpPixels.reset(width * height);
+        if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes,
+                                  tmpPixels.get())) {
+            return false;
         }
-        if (tempProxy) {
-            if (!fp) {
-                fp = GrSimpleTextureEffect::Make(this->resourceProvider(), tempProxy, nullptr,
-                                                 SkMatrix::I());
-                fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), tempDrawInfo.fSwizzle);
-
-                if (!fp) {
-                    return false;
-                }
-            }
-            GrTexture* texture = tempProxy->instantiate(this->resourceProvider());
-            if (!texture) {
-                return false;
-            }
-            if (texture->surfacePriv().hasPendingIO()) {
-                this->flush();
-            }
-            if (applyPremulToSrc) {
-                size_t tmpRowBytes = 4 * width;
-                tmpPixels.reset(width * height);
-                if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes,
-                                          tmpPixels.get())) {
-                    return false;
-                }
-                rowBytes = tmpRowBytes;
-                buffer = tmpPixels.get();
-                applyPremulToSrc = false;
-            }
-            if (!fGpu->writePixels(texture, 0, 0, width, height,
-                                   tempDrawInfo.fWriteConfig, buffer,
-                                   rowBytes)) {
-                return false;
-            }
-            SkMatrix matrix;
-            matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
-            // TODO: Need to decide the semantics of this function for color spaces. Do we support
-            // conversion from a passed-in color space? For now, specifying nullptr means that this
-            // path will do no conversion, so it will match the behavior of the non-draw path.
-            GrRenderTarget* renderTarget = surface->asRenderTarget();
-            SkASSERT(renderTarget);
-            sk_sp<GrRenderTargetContext> renderTargetContext(
-                this->contextPriv().makeWrappedRenderTargetContext(sk_ref_sp(renderTarget),
-                                                                   nullptr));
-            if (!renderTargetContext) {
-                return false;
-            }
-            GrPaint paint;
-            paint.addColorFragmentProcessor(std::move(fp));
-            paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-            paint.setAllowSRGBInputs(true);
-            SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
-            renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix, rect,
-                                          nullptr);
-
-            if (kFlushWrites_PixelOp & pixelOpsFlags) {
-                this->flushSurfaceWrites(surface);
-            }
-        }
+        rowBytes = tmpRowBytes;
+        buffer = tmpPixels.get();
     }
-    if (!tempProxy) {
-        if (applyPremulToSrc) {
-            size_t tmpRowBytes = 4 * width;
-            tmpPixels.reset(width * height);
-            if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes,
-                                      tmpPixels.get())) {
-                return false;
-            }
-            rowBytes = tmpRowBytes;
-            buffer = tmpPixels.get();
-            applyPremulToSrc = false;
+
+    if (tempProxy) {
+        sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(
+                fContext->resourceProvider(), tempProxy, nullptr, SkMatrix::I());
+        if (premulOnGpu) {
+            fp = fContext->createUPMToPMEffect(std::move(fp), useConfigConversionEffect);
         }
-        return fGpu->writePixels(surface, left, top, width, height, srcConfig, buffer, rowBytes);
+        fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), tempDrawInfo.fSwizzle);
+        SkASSERT(fp);
+
+        if (tempProxy->priv().hasPendingIO()) {
+            this->flush(tempProxy.get());
+        }
+        if (!tempProxy->instantiate(fContext->resourceProvider())) {
+            return false;
+        }
+        GrTexture* texture = tempProxy->priv().peekTexture();
+        if (!fContext->fGpu->writePixels(texture, 0, 0, width, height, tempDrawInfo.fWriteConfig,
+                                         buffer, rowBytes)) {
+            return false;
+        }
+        SkMatrix matrix;
+        matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
+        GrRenderTargetContext* renderTargetContext = dst->asRenderTargetContext();
+        if (!renderTargetContext) {
+            return false;
+        }
+        GrPaint paint;
+        paint.addColorFragmentProcessor(std::move(fp));
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.setAllowSRGBInputs(SkToBool(dst->getColorSpace()) ||
+                                 GrPixelConfigIsSRGB(renderTargetContext->config()));
+        SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+        renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix, rect,
+                                        nullptr);
+
+        if (kFlushWrites_PixelOp & pixelOpsFlags) {
+            this->flushSurfaceWrites(renderTargetContext->asRenderTargetProxy());
+        }
+    } else {
+        return fContext->fGpu->writePixels(dstSurface, left, top, width, height, srcConfig,
+                                           buffer, rowBytes);
     }
     return true;
 }
 
-bool GrContext::readSurfacePixels(GrSurface* src, SkColorSpace* srcColorSpace,
-                                  int left, int top, int width, int height,
-                                  GrPixelConfig dstConfig, SkColorSpace* dstColorSpace,
-                                  void* buffer, size_t rowBytes, uint32_t flags) {
+bool GrContextPriv::readSurfacePixels(GrSurfaceContext* src,
+                                      int left, int top, int width, int height,
+                                      GrPixelConfig dstConfig, SkColorSpace* dstColorSpace,
+                                      void* buffer, size_t rowBytes, uint32_t flags) {
     // TODO: Color space conversion
 
-    ASSERT_SINGLE_OWNER
-    RETURN_FALSE_IF_ABANDONED
-    ASSERT_OWNED_RESOURCE(src);
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_FALSE_IF_ABANDONED_PRIV
     SkASSERT(src);
-    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::readSurfacePixels");
+    ASSERT_OWNED_PROXY_PRIV(src->asSurfaceProxy());
+    GR_AUDIT_TRAIL_AUTO_FRAME(&fContext->fAuditTrail, "GrContextPriv::readSurfacePixels");
 
-    this->testPMConversionsIfNecessary(flags);
+    // MDB TODO: delay this instantiation until later in the method
+    if (!src->asSurfaceProxy()->instantiate(fContext->resourceProvider())) {
+        return false;
+    }
+
+    GrSurface* srcSurface = src->asSurfaceProxy()->priv().peekSurface();
+
+    // The src is premul but the dst is unpremul -> unpremul the src after or as part of the read
+    bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
+    if (!valid_pixel_conversion(srcSurface->config(), dstConfig, unpremul)) {
+        return false;
+    }
+
+    // We need to guarantee round-trip conversion if we are reading and writing 8888 non-sRGB data,
+    // without any color spaces attached, and the caller wants us to unpremul.
+    bool useConfigConversionEffect =
+                    unpremul &&
+                    pm_upm_must_round_trip(srcSurface->config(), src->getColorSpace()) &&
+                    pm_upm_must_round_trip(dstConfig, dstColorSpace);
+
+    // Are we going to try to unpremul as part of a draw? For the non-legacy case, we always allow
+    // this. GrConfigConversionEffect fails on some GPUs, so only allow this if it works perfectly.
+    bool unpremulOnGpu = unpremul &&
+                         (!useConfigConversionEffect || fContext->validPMUPMConversionExists());
 
     // Adjust the params so that if we wind up using an intermediate surface we've already done
     // all the trimming and the temporary can be the min size required.
-    if (!GrSurfacePriv::AdjustReadPixelParams(src->width(), src->height(),
+    if (!GrSurfacePriv::AdjustReadPixelParams(srcSurface->width(), srcSurface->height(),
                                               GrBytesPerPixel(dstConfig), &left,
                                               &top, &width, &height, &buffer, &rowBytes)) {
         return false;
     }
 
-    if (!(kDontFlush_PixelOpsFlag & flags) && src->surfacePriv().hasPendingWrite()) {
-        this->flush();
-    }
-
-    bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
-    if (unpremul && !valid_unpremul_config(dstConfig)) {
-        // The unpremul flag is only allowed for 8888 and F16 configs.
-        return false;
-    }
-    // We don't allow conversion between integer configs and float/fixed configs.
-    if (GrPixelConfigIsSint(src->config()) != GrPixelConfigIsSint(dstConfig)) {
-        return false;
-    }
-
-    GrGpu::DrawPreference drawPreference = GrGpu::kNoDraw_DrawPreference;
-    // Don't prefer to draw for the conversion (and thereby access a texture from the cache) when
-    // we've already determined that there isn't a roundtrip preserving conversion processor pair.
-    if (unpremul && this->validPMUPMConversionExists(src->config())) {
-        drawPreference = GrGpu::kCallerPrefersDraw_DrawPreference;
-    }
-
+    GrGpu::DrawPreference drawPreference = unpremulOnGpu ? GrGpu::kCallerPrefersDraw_DrawPreference
+                                                         : GrGpu::kNoDraw_DrawPreference;
     GrGpu::ReadPixelTempDrawInfo tempDrawInfo;
-    if (!fGpu->getReadPixelsInfo(src, width, height, rowBytes, dstConfig, &drawPreference,
-                                 &tempDrawInfo)) {
+    if (!fContext->fGpu->getReadPixelsInfo(srcSurface, width, height, rowBytes, dstConfig,
+                                           &drawPreference, &tempDrawInfo)) {
         return false;
     }
 
-    sk_sp<GrSurface> surfaceToRead(SkRef(src));
+    if (!(kDontFlush_PixelOpsFlag & flags) && srcSurface->surfacePriv().hasPendingWrite()) {
+        this->flush(nullptr); // MDB TODO: tighten this
+    }
+
+    sk_sp<GrSurfaceProxy> proxyToRead = src->asSurfaceProxyRef();
     bool didTempDraw = false;
     if (GrGpu::kNoDraw_DrawPreference != drawPreference) {
         if (SkBackingFit::kExact == tempDrawInfo.fTempSurfaceFit) {
             // We only respect this when the entire src is being read. Otherwise we can trigger too
             // many odd ball texture sizes and trash the cache.
-            if (width != src->width() || height != src->height()) {
+            if (width != srcSurface->width() || height != srcSurface->height()) {
                 tempDrawInfo.fTempSurfaceFit= SkBackingFit::kApprox;
             }
         }
         // TODO: Need to decide the semantics of this function for color spaces. Do we support
         // conversion to a passed-in color space? For now, specifying nullptr means that this
         // path will do no conversion, so it will match the behavior of the non-draw path.
-        sk_sp<GrRenderTargetContext> tempRTC = this->makeRenderTargetContext(
+        sk_sp<GrRenderTargetContext> tempRTC = fContext->makeDeferredRenderTargetContext(
                                                            tempDrawInfo.fTempSurfaceFit,
                                                            tempDrawInfo.fTempSurfaceDesc.fWidth,
                                                            tempDrawInfo.fTempSurfaceDesc.fHeight,
@@ -474,52 +503,51 @@
                                                            tempDrawInfo.fTempSurfaceDesc.fOrigin);
         if (tempRTC) {
             SkMatrix textureMatrix = SkMatrix::MakeTrans(SkIntToScalar(left), SkIntToScalar(top));
-            sk_sp<GrFragmentProcessor> fp;
-            if (unpremul) {
-                fp = this->createPMToUPMEffect(src->asTexture(), textureMatrix);
-                fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), tempDrawInfo.fSwizzle);
-                if (fp) {
-                    unpremul = false; // we no longer need to do this on CPU after the read back.
-                } else if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference) {
-                    // We only wanted to do the draw in order to perform the unpremul so don't
-                    // bother.
-                    tempRTC.reset(nullptr);
-                }
+            sk_sp<GrTextureProxy> proxy = src->asTextureProxyRef();
+            sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(
+                    fContext->resourceProvider(), std::move(proxy), nullptr, textureMatrix);
+            if (unpremulOnGpu) {
+                fp = fContext->createPMToUPMEffect(std::move(fp), useConfigConversionEffect);
+                // We no longer need to do this on CPU after the read back.
+                unpremul = false;
             }
-            if (!fp && tempRTC) {
-                fp = GrSimpleTextureEffect::Make(src->asTexture(), nullptr, textureMatrix);
-                fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), tempDrawInfo.fSwizzle);
-            }
-            if (fp) {
-                GrPaint paint;
-                paint.addColorFragmentProcessor(std::move(fp));
-                paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-                paint.setAllowSRGBInputs(true);
-                SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
-                tempRTC->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect,
-                                  nullptr);
-                surfaceToRead.reset(tempRTC->asTexture().release());
-                left = 0;
-                top = 0;
-                didTempDraw = true;
-            }
+            fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), tempDrawInfo.fSwizzle);
+            SkASSERT(fp);
+
+            GrPaint paint;
+            paint.addColorFragmentProcessor(std::move(fp));
+            paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+            paint.setAllowSRGBInputs(true);
+            SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+            tempRTC->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect,
+                                nullptr);
+            proxyToRead = tempRTC->asTextureProxyRef();
+            left = 0;
+            top = 0;
+            didTempDraw = true;
         }
     }
 
-    if (!surfaceToRead) {
+    if (!proxyToRead) {
         return false;
     }
 
+    if (!proxyToRead->instantiate(fContext->resourceProvider())) {
+        return false;
+    }
+
+    GrSurface* surfaceToRead = proxyToRead->priv().peekSurface();
+
     if (GrGpu::kRequireDraw_DrawPreference == drawPreference && !didTempDraw) {
         return false;
     }
     GrPixelConfig configToRead = dstConfig;
     if (didTempDraw) {
-        this->flushSurfaceWrites(surfaceToRead.get());
+        this->flushSurfaceWrites(proxyToRead.get());
         configToRead = tempDrawInfo.fReadConfig;
     }
-    if (!fGpu->readPixels(surfaceToRead.get(), left, top, width, height, configToRead, buffer,
-                          rowBytes)) {
+    if (!fContext->fGpu->readPixels(surfaceToRead, left, top, width, height, configToRead,
+                                    buffer, rowBytes)) {
         return false;
     }
 
@@ -540,27 +568,31 @@
     return true;
 }
 
-void GrContext::prepareSurfaceForExternalIO(GrSurface* surface) {
-    ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
-    SkASSERT(surface);
-    ASSERT_OWNED_RESOURCE(surface);
-    fDrawingManager->prepareSurfaceForExternalIO(surface);
+void GrContextPriv::prepareSurfaceForExternalIO(GrSurfaceProxy* proxy) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    SkASSERT(proxy);
+    ASSERT_OWNED_PROXY_PRIV(proxy);
+    fContext->fDrawingManager->prepareSurfaceForExternalIO(proxy);
 }
 
-void GrContext::flushSurfaceWrites(GrSurface* surface) {
-    ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
-    if (surface->surfacePriv().hasPendingWrite()) {
-        this->flush();
+void GrContextPriv::flushSurfaceWrites(GrSurfaceProxy* proxy) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    SkASSERT(proxy);
+    ASSERT_OWNED_PROXY_PRIV(proxy);
+    if (proxy->priv().hasPendingWrite()) {
+        this->flush(proxy);
     }
 }
 
-void GrContext::flushSurfaceIO(GrSurface* surface) {
-    ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
-    if (surface->surfacePriv().hasPendingIO()) {
-        this->flush();
+void GrContextPriv::flushSurfaceIO(GrSurfaceProxy* proxy) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    SkASSERT(proxy);
+    ASSERT_OWNED_PROXY_PRIV(proxy);
+    if (proxy->priv().hasPendingIO()) {
+        this->flush(proxy);
     }
 }
 
@@ -583,22 +615,6 @@
     return chosenSampleCount <= fGpu->caps()->maxSampleCount() ? chosenSampleCount : 0;
 }
 
-sk_sp<GrRenderTargetContext> GrContextPriv::makeWrappedRenderTargetContext(
-                                                               sk_sp<GrRenderTarget> rt,
-                                                               sk_sp<SkColorSpace> colorSpace,
-                                                               const SkSurfaceProps* surfaceProps) {
-    ASSERT_SINGLE_OWNER_PRIV
-
-    sk_sp<GrSurfaceProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(rt)));
-    if (!proxy) {
-        return nullptr;
-    }
-
-    return this->drawingManager()->makeRenderTargetContext(std::move(proxy),
-                                                           std::move(colorSpace),
-                                                           surfaceProps);
-}
-
 sk_sp<GrSurfaceContext> GrContextPriv::makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy> proxy,
                                                                  sk_sp<SkColorSpace> colorSpace) {
     ASSERT_SINGLE_OWNER_PRIV
@@ -612,17 +628,6 @@
     }
 }
 
-sk_sp<GrSurfaceContext> GrContextPriv::makeWrappedSurfaceContext(sk_sp<GrSurface> surface) {
-    ASSERT_SINGLE_OWNER_PRIV
-
-    sk_sp<GrSurfaceProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(surface)));
-    if (!proxy) {
-        return nullptr;
-    }
-
-    return this->makeWrappedSurfaceContext(std::move(proxy), nullptr);
-}
-
 sk_sp<GrSurfaceContext> GrContextPriv::makeDeferredSurfaceContext(const GrSurfaceDesc& dstDesc,
                                                                   SkBackingFit fit,
                                                                   SkBudgeted isDstBudgeted) {
@@ -636,11 +641,15 @@
     return this->makeWrappedSurfaceContext(std::move(proxy), nullptr);
 }
 
-sk_sp<GrSurfaceContext> GrContextPriv::makeBackendSurfaceContext(const GrBackendTextureDesc& desc,
+sk_sp<GrSurfaceContext> GrContextPriv::makeBackendSurfaceContext(const GrBackendTexture& tex,
+                                                                 GrSurfaceOrigin origin,
+                                                                 GrBackendTextureFlags flags,
+                                                                 int sampleCnt,
                                                                  sk_sp<SkColorSpace> colorSpace) {
     ASSERT_SINGLE_OWNER_PRIV
 
-    sk_sp<GrSurface> surface(fContext->resourceProvider()->wrapBackendTexture(desc));
+    sk_sp<GrSurface> surface(fContext->resourceProvider()->wrapBackendTexture(tex, origin,
+                                                                              flags, sampleCnt));
     if (!surface) {
         return nullptr;
     }
@@ -654,13 +663,16 @@
 }
 
 sk_sp<GrRenderTargetContext> GrContextPriv::makeBackendTextureRenderTargetContext(
-                                                                   const GrBackendTextureDesc& desc,
+                                                                   const GrBackendTexture& tex,
+                                                                   GrSurfaceOrigin origin,
+                                                                   int sampleCnt,
                                                                    sk_sp<SkColorSpace> colorSpace,
                                                                    const SkSurfaceProps* props) {
     ASSERT_SINGLE_OWNER_PRIV
-    SkASSERT(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
 
-    sk_sp<GrSurface> surface(fContext->resourceProvider()->wrapBackendTexture(desc));
+    static const GrBackendTextureFlags kForceRT = kRenderTarget_GrBackendTextureFlag;
+    sk_sp<GrSurface> surface(fContext->resourceProvider()->wrapBackendTexture(tex, origin, kForceRT,
+                                                                              sampleCnt));
     if (!surface) {
         return nullptr;
     }
@@ -675,12 +687,14 @@
 }
 
 sk_sp<GrRenderTargetContext> GrContextPriv::makeBackendRenderTargetRenderTargetContext(
-                                                const GrBackendRenderTargetDesc& desc,
+                                                const GrBackendRenderTarget& backendRT,
+                                                GrSurfaceOrigin origin,
                                                 sk_sp<SkColorSpace> colorSpace,
                                                 const SkSurfaceProps* surfaceProps) {
     ASSERT_SINGLE_OWNER_PRIV
 
-    sk_sp<GrRenderTarget> rt(fContext->resourceProvider()->wrapBackendRenderTarget(desc));
+    sk_sp<GrRenderTarget> rt(fContext->resourceProvider()->wrapBackendRenderTarget(backendRT,
+                                                                                   origin));
     if (!rt) {
         return nullptr;
     }
@@ -696,13 +710,17 @@
 }
 
 sk_sp<GrRenderTargetContext> GrContextPriv::makeBackendTextureAsRenderTargetRenderTargetContext(
-                                                     const GrBackendTextureDesc& desc,
+                                                     const GrBackendTexture& tex,
+                                                     GrSurfaceOrigin origin,
+                                                     int sampleCnt,
                                                      sk_sp<SkColorSpace> colorSpace,
                                                      const SkSurfaceProps* surfaceProps) {
     ASSERT_SINGLE_OWNER_PRIV
-    SkASSERT(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
 
-    sk_sp<GrSurface> surface(fContext->resourceProvider()->wrapBackendTextureAsRenderTarget(desc));
+    sk_sp<GrSurface> surface(fContext->resourceProvider()->wrapBackendTextureAsRenderTarget(
+                                                                                        tex,
+                                                                                        origin,
+                                                                                        sampleCnt));
     if (!surface) {
         return nullptr;
     }
@@ -717,8 +735,8 @@
                                                            surfaceProps);
 }
 
-void GrContextPriv::addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject) {
-    fContext->fDrawingManager->addPreFlushCallbackObject(std::move(preFlushCBObject));
+void GrContextPriv::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlushCBObject) {
+    fContext->fDrawingManager->addOnFlushCallbackObject(onFlushCBObject);
 }
 
 
@@ -738,23 +756,6 @@
     }
 }
 
-sk_sp<GrRenderTargetContext> GrContext::makeRenderTargetContextWithFallback(
-                                                                 SkBackingFit fit,
-                                                                 int width, int height,
-                                                                 GrPixelConfig config,
-                                                                 sk_sp<SkColorSpace> colorSpace,
-                                                                 int sampleCnt,
-                                                                 GrSurfaceOrigin origin,
-                                                                 const SkSurfaceProps* surfaceProps,
-                                                                 SkBudgeted budgeted) {
-    if (!this->caps()->isConfigRenderable(config, sampleCnt > 0)) {
-        config = GrPixelConfigFallback(config);
-    }
-
-    return this->makeRenderTargetContext(fit, width, height, config, std::move(colorSpace),
-                                         sampleCnt, origin, surfaceProps, budgeted);
-}
-
 sk_sp<GrRenderTargetContext> GrContext::makeDeferredRenderTargetContextWithFallback(
                                                                  SkBackingFit fit,
                                                                  int width, int height,
@@ -772,46 +773,6 @@
                                                  sampleCnt, origin, surfaceProps, budgeted);
 }
 
-sk_sp<GrRenderTargetContext> GrContext::makeRenderTargetContext(SkBackingFit fit,
-                                                                int width, int height,
-                                                                GrPixelConfig config,
-                                                                sk_sp<SkColorSpace> colorSpace,
-                                                                int sampleCnt,
-                                                                GrSurfaceOrigin origin,
-                                                                const SkSurfaceProps* surfaceProps,
-                                                                SkBudgeted budgeted) {
-    if (!this->caps()->isConfigRenderable(config, sampleCnt > 0)) {
-        return nullptr;
-    }
-
-    GrSurfaceDesc desc;
-    desc.fFlags = kRenderTarget_GrSurfaceFlag;
-    desc.fOrigin = origin;
-    desc.fWidth = width;
-    desc.fHeight = height;
-    desc.fConfig = config;
-    desc.fSampleCnt = sampleCnt;
-
-    sk_sp<GrTexture> tex;
-    if (SkBackingFit::kExact == fit) {
-        tex.reset(this->resourceProvider()->createTexture(desc, budgeted));
-    } else {
-        tex.reset(this->resourceProvider()->createApproxTexture(desc, 0));
-    }
-    if (!tex) {
-        return nullptr;
-    }
-
-    sk_sp<GrRenderTargetContext> renderTargetContext(
-        this->contextPriv().makeWrappedRenderTargetContext(sk_ref_sp(tex->asRenderTarget()),
-                                                           std::move(colorSpace), surfaceProps));
-    if (!renderTargetContext) {
-        return nullptr;
-    }
-
-    return renderTargetContext;
-}
-
 sk_sp<GrRenderTargetContext> GrContext::makeDeferredRenderTargetContext(
                                                         SkBackingFit fit,
                                                         int width, int height,
@@ -821,6 +782,8 @@
                                                         GrSurfaceOrigin origin,
                                                         const SkSurfaceProps* surfaceProps,
                                                         SkBudgeted budgeted) {
+    SkASSERT(kDefault_GrSurfaceOrigin != origin);
+
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fOrigin = origin;
@@ -835,9 +798,17 @@
         return nullptr;
     }
 
-    return fDrawingManager->makeRenderTargetContext(std::move(rtp),
-                                                    std::move(colorSpace),
-                                                    surfaceProps);
+    sk_sp<GrRenderTargetContext> renderTargetContext(
+        fDrawingManager->makeRenderTargetContext(std::move(rtp),
+                                                 std::move(colorSpace),
+                                                 surfaceProps));
+    if (!renderTargetContext) {
+        return nullptr;
+    }
+
+    renderTargetContext->discard();
+
+    return renderTargetContext;
 }
 
 bool GrContext::abandoned() const {
@@ -845,95 +816,53 @@
     return fDrawingManager->wasAbandoned();
 }
 
-namespace {
-void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
-    GrConfigConversionEffect::PMConversion pmToUPM;
-    GrConfigConversionEffect::PMConversion upmToPM;
-    GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM);
-    *pmToUPMValue = pmToUPM;
-    *upmToPMValue = upmToPM;
-}
-}
-
-void GrContext::testPMConversionsIfNecessary(uint32_t flags) {
+sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrFragmentProcessor> fp,
+                                                          bool useConfigConversionEffect) {
     ASSERT_SINGLE_OWNER
-    if (SkToBool(kUnpremul_PixelOpsFlag & flags)) {
-        if (!fDidTestPMConversions) {
-            test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
-            fDidTestPMConversions = true;
-        }
-    }
-}
+    // We have specialized effects that guarantee round-trip conversion for some formats
+    if (useConfigConversionEffect) {
+        // We should have already called this->validPMUPMConversionExists() in this case
+        SkASSERT(fDidTestPMConversions);
+        // ...and it should have succeeded
+        SkASSERT(this->validPMUPMConversionExists());
 
-sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(GrTexture* texture,
-                                                          const SkMatrix& matrix) {
-    ASSERT_SINGLE_OWNER
-    // We should have already called this->testPMConversionsIfNecessary().
-    SkASSERT(fDidTestPMConversions);
-    if (kRGBA_half_GrPixelConfig == texture->config()) {
-        return GrFragmentProcessor::UnpremulOutput(
-                GrSimpleTextureEffect::Make(texture, nullptr, matrix));
+        return GrConfigConversionEffect::Make(std::move(fp),
+                                              GrConfigConversionEffect::kToUnpremul_PMConversion);
     } else {
-        GrConfigConversionEffect::PMConversion pmToUPM =
-            static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
-        if (GrConfigConversionEffect::kPMConversionCnt != pmToUPM) {
-            return GrConfigConversionEffect::Make(texture, pmToUPM, matrix);
-        } else {
-            return nullptr;
-        }
+        // For everything else (sRGB, half-float, etc...), it doesn't make sense to try and
+        // explicitly round the results. Just do the obvious, naive thing in the shader.
+        return GrFragmentProcessor::UnpremulOutput(std::move(fp));
     }
 }
 
-sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrTextureProxy> proxy,
-                                                          const SkMatrix& matrix) {
+sk_sp<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrFragmentProcessor> fp,
+                                                          bool useConfigConversionEffect) {
     ASSERT_SINGLE_OWNER
-    // We should have already called this->testPMConversionsIfNecessary().
-    SkASSERT(fDidTestPMConversions);
-    if (kRGBA_half_GrPixelConfig == proxy->config()) {
-        return GrFragmentProcessor::UnpremulOutput(
-                GrSimpleTextureEffect::Make(this->resourceProvider(), std::move(proxy),
-                                            nullptr, matrix));
+    // We have specialized effects that guarantee round-trip conversion for these formats
+    if (useConfigConversionEffect) {
+        // We should have already called this->validPMUPMConversionExists() in this case
+        SkASSERT(fDidTestPMConversions);
+        // ...and it should have succeeded
+        SkASSERT(this->validPMUPMConversionExists());
+
+        return GrConfigConversionEffect::Make(std::move(fp),
+                                              GrConfigConversionEffect::kToPremul_PMConversion);
     } else {
-        GrConfigConversionEffect::PMConversion pmToUPM =
-            static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
-        if (GrConfigConversionEffect::kPMConversionCnt != pmToUPM) {
-            return GrConfigConversionEffect::Make(this->resourceProvider(), std::move(proxy),
-                                                  pmToUPM, matrix);
-        } else {
-            return nullptr;
-        }
+        // For everything else (sRGB, half-float, etc...), it doesn't make sense to try and
+        // explicitly round the results. Just do the obvious, naive thing in the shader.
+        return GrFragmentProcessor::PremulOutput(std::move(fp));
     }
 }
 
-sk_sp<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrTextureProxy> proxy,
-                                                          const SkMatrix& matrix) {
+bool GrContext::validPMUPMConversionExists() {
     ASSERT_SINGLE_OWNER
-    // We should have already called this->testPMConversionsIfNecessary().
-    SkASSERT(fDidTestPMConversions);
-    if (kRGBA_half_GrPixelConfig == proxy->config()) {
-        return GrFragmentProcessor::PremulOutput(
-                GrSimpleTextureEffect::Make(this->resourceProvider(), std::move(proxy),
-                                            nullptr, matrix));
-    } else {
-        GrConfigConversionEffect::PMConversion upmToPM =
-            static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
-        if (GrConfigConversionEffect::kPMConversionCnt != upmToPM) {
-            return GrConfigConversionEffect::Make(this->resourceProvider(), std::move(proxy),
-                                                  upmToPM, matrix);
-        } else {
-            return nullptr;
-        }
+    if (!fDidTestPMConversions) {
+        fPMUPMConversionsRoundTrip = GrConfigConversionEffect::TestForPreservingPMConversions(this);
+        fDidTestPMConversions = true;
     }
-}
 
-bool GrContext::validPMUPMConversionExists(GrPixelConfig config) const {
-    ASSERT_SINGLE_OWNER
-    // We should have already called this->testPMConversionsIfNecessary().
-    SkASSERT(fDidTestPMConversions);
     // The PM<->UPM tests fail or succeed together so we only need to check one.
-    // For F16, we always allow PM/UPM conversion on the GPU, even if it doesn't round-trip.
-    return GrConfigConversionEffect::kPMConversionCnt != fPMToUPMConversion ||
-           kRGBA_half_GrPixelConfig == config;
+    return fPMUPMConversionsRoundTrip;
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 3357636..a043b64 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -11,9 +11,10 @@
 #include "GrContext.h"
 #include "GrSurfaceContext.h"
 
+class GrBackendRenderTarget;
 class GrSemaphore;
 class GrSurfaceProxy;
-class GrPreFlushCallbackObject;
+class GrOnFlushCallbackObject;
 
 /** Class that adds methods to GrContext that are only intended for use internal to Skia.
     This class is purely a privileged window into GrContext. It should never have additional
@@ -22,48 +23,144 @@
 public:
     GrDrawingManager* drawingManager() { return fContext->fDrawingManager.get(); }
 
-    // Create a renderTargetContext that wraps an existing renderTarget
-    sk_sp<GrRenderTargetContext> makeWrappedRenderTargetContext(sk_sp<GrRenderTarget> rt,
-                                                                sk_sp<SkColorSpace> colorSpace,
-                                                                const SkSurfaceProps* = nullptr);
+    sk_sp<GrSurfaceContext> makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy>, sk_sp<SkColorSpace>);
 
-    // Create a surfaceContext that wraps an existing texture or renderTarget
-    sk_sp<GrSurfaceContext> makeWrappedSurfaceContext(sk_sp<GrSurface> tex);
-
-    sk_sp<GrSurfaceContext> makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy> proxy,
-                                                      sk_sp<SkColorSpace>);
-
-    sk_sp<GrSurfaceContext> makeDeferredSurfaceContext(const GrSurfaceDesc& dstDesc,
-                                                       SkBackingFit dstFit,
-                                                       SkBudgeted isDstBudgeted);
+    sk_sp<GrSurfaceContext> makeDeferredSurfaceContext(const GrSurfaceDesc&,
+                                                       SkBackingFit,
+                                                       SkBudgeted);
 
     // TODO: Maybe add a 'surfaceProps' param (that is ignored for non-RTs) and remove
     // makeBackendTextureRenderTargetContext & makeBackendTextureAsRenderTargetRenderTargetContext
-    sk_sp<GrSurfaceContext> makeBackendSurfaceContext(const GrBackendTextureDesc& desc,
+    sk_sp<GrSurfaceContext> makeBackendSurfaceContext(const GrBackendTexture& tex,
+                                                      GrSurfaceOrigin origin,
+                                                      GrBackendTextureFlags flags,
+                                                      int sampleCnt,
                                                       sk_sp<SkColorSpace> colorSpace);
 
     sk_sp<GrRenderTargetContext> makeBackendTextureRenderTargetContext(
-                                                         const GrBackendTextureDesc& desc,
+                                                         const GrBackendTexture& tex,
+                                                         GrSurfaceOrigin origin,
+                                                         int sampleCnt,
                                                          sk_sp<SkColorSpace> colorSpace,
                                                          const SkSurfaceProps* = nullptr);
 
     sk_sp<GrRenderTargetContext> makeBackendRenderTargetRenderTargetContext(
-                                                              const GrBackendRenderTargetDesc& desc,
+                                                              const GrBackendRenderTarget&,
+                                                              GrSurfaceOrigin origin,
                                                               sk_sp<SkColorSpace> colorSpace,
                                                               const SkSurfaceProps* = nullptr);
 
     sk_sp<GrRenderTargetContext> makeBackendTextureAsRenderTargetRenderTargetContext(
-                                                                 const GrBackendTextureDesc& desc,
+                                                                 const GrBackendTexture& tex,
+                                                                 GrSurfaceOrigin origin,
+                                                                 int sampleCnt,
                                                                  sk_sp<SkColorSpace> colorSpace,
                                                                  const SkSurfaceProps* = nullptr);
 
     bool disableGpuYUVConversion() const { return fContext->fDisableGpuYUVConversion; }
 
-    /*
-     * A ref will be taken on the preFlushCallbackObject which will be removed when the
-     * context is destroyed.
+    /**
+     * Call to ensure all drawing to the context has been issued to the
+     * underlying 3D API.
+     * The 'proxy' parameter is a hint. If it is supplied the context will guarantee that
+     * the draws required for that proxy are flushed but it could do more. If no 'proxy' is
+     * provided then all current work will be flushed.
      */
-    void addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject>);
+    void flush(GrSurfaceProxy*);
+
+    /**
+     * Registers an object for flush-related callbacks. (See GrOnFlushCallbackObject.)
+     *
+     * NOTE: the drawing manager tracks this object as a raw pointer; it is up to the caller to
+     * ensure its lifetime is tied to that of the context.
+     */
+    void addOnFlushCallbackObject(GrOnFlushCallbackObject*);
+
+    void testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject*);
+
+    /**
+     * After this returns any pending writes to the surface will have been issued to the
+     * backend 3D API.
+     */
+    void flushSurfaceWrites(GrSurfaceProxy*);
+
+    /**
+     * After this returns any pending reads or writes to the surface will have been issued to the
+     * backend 3D API.
+     */
+    void flushSurfaceIO(GrSurfaceProxy*);
+
+    /**
+     * Finalizes all pending reads and writes to the surface and also performs an MSAA resolve
+     * if necessary.
+     *
+     * It is not necessary to call this before reading the render target via Skia/GrContext.
+     * GrContext will detect when it must perform a resolve before reading pixels back from the
+     * surface or using it as a texture.
+     */
+    void prepareSurfaceForExternalIO(GrSurfaceProxy*);
+
+   /**
+    * These flags can be used with the read/write pixels functions below.
+    */
+    enum PixelOpsFlags {
+        /** The GrContext will not be flushed before the surface read or write. This means that
+            the read or write may occur before previous draws have executed. */
+        kDontFlush_PixelOpsFlag = 0x1,
+        /** Any surface writes should be flushed to the backend 3D API after the surface operation
+            is complete */
+        kFlushWrites_PixelOp = 0x2,
+        /** The src for write or dst read is unpremultiplied. This is only respected if both the
+            config src and dst configs are an RGBA/BGRA 8888 format. */
+        kUnpremul_PixelOpsFlag  = 0x4,
+    };
+
+    /**
+     * Reads a rectangle of pixels from a surface.
+     * @param src           the surface context to read from.
+     * @param left          left edge of the rectangle to read (inclusive)
+     * @param top           top edge of the rectangle to read (inclusive)
+     * @param width         width of rectangle to read in pixels.
+     * @param height        height of rectangle to read in pixels.
+     * @param dstConfig     the pixel config of the destination buffer
+     * @param dstColorSpace color space of the destination buffer
+     * @param buffer        memory to read the rectangle into.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero means rows are tightly
+     *                      packed.
+     * @param pixelOpsFlags see PixelOpsFlags enum above.
+     *
+     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
+     *         pixel configs
+     */
+    bool readSurfacePixels(GrSurfaceContext* src,
+                           int left, int top, int width, int height,
+                           GrPixelConfig dstConfig, SkColorSpace* dstColorSpace, void* buffer,
+                           size_t rowBytes = 0,
+                           uint32_t pixelOpsFlags = 0);
+
+    /**
+     * Writes a rectangle of pixels to a surface.
+     * @param dst           the surface context to write to.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param srcConfig     the pixel config of the source buffer
+     * @param srcColorSpace color space of the source buffer
+     * @param buffer        memory to read pixels from
+     * @param rowBytes      number of bytes between consecutive rows. Zero
+     *                      means rows are tightly packed.
+     * @param pixelOpsFlags see PixelOpsFlags enum above.
+     * @return true if the write succeeded, false if not. The write can fail because of an
+     *         unsupported combination of surface and src configs.
+     */
+    bool writeSurfacePixels(GrSurfaceContext* dst,
+                            int left, int top, int width, int height,
+                            GrPixelConfig srcConfig, SkColorSpace* srcColorSpace, const void* buffer,
+                            size_t rowBytes,
+                            uint32_t pixelOpsFlags = 0);
+
+    GrBackend getBackend() const { return fContext->fBackend; }
 
 private:
     explicit GrContextPriv(GrContext* context) : fContext(context) {}
diff --git a/src/gpu/GrCoordTransform.cpp b/src/gpu/GrCoordTransform.cpp
index 7b280f3..de8b8cc 100644
--- a/src/gpu/GrCoordTransform.cpp
+++ b/src/gpu/GrCoordTransform.cpp
@@ -6,85 +6,17 @@
  */
 
 #include "GrCoordTransform.h"
-#include "GrCaps.h"
-#include "GrContext.h"
-#include "GrGpu.h"
 #include "GrResourceProvider.h"
 #include "GrTextureProxy.h"
 
-static GrSLPrecision compute_precision(const GrShaderCaps* caps,
-                                       int width, int height,
-                                       GrSamplerParams::FilterMode filter) {
-    // Always start at kDefault. Then if precisions differ we see if the precision needs to be
-    // increased. Our rule is that we want at least 4 subpixel values in the representation for
-    // coords between 0 to 1 when bi- or tri-lerping and 1 value when nearest filtering. Note that
-    // this still might not be enough when drawing with repeat or mirror-repeat modes but that case
-    // can be arbitrarily bad.
-    int subPixelThresh = filter > GrSamplerParams::kNone_FilterMode ? 4 : 1;
-    GrSLPrecision precision = kDefault_GrSLPrecision;
-    if (caps) {
-        if (caps->floatPrecisionVaries()) {
-            int maxD = SkTMax(width, height);
-            const GrShaderCaps::PrecisionInfo* info;
-            info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, precision);
-            do {
-                SkASSERT(info->supported());
-                // Make sure there is at least 2 bits of subpixel precision in the range of
-                // texture coords from 0.5 to 1.0.
-                if ((2 << info->fBits) / maxD > subPixelThresh) {
-                    break;
-                }
-                if (kHigh_GrSLPrecision == precision) {
-                    break;
-                }
-                GrSLPrecision nextP = static_cast<GrSLPrecision>(precision + 1);
-                info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, nextP);
-                if (!info->supported()) {
-                    break;
-                }
-                precision = nextP;
-            } while (true);
-        }
-    }
-
-    return precision;
-}
-
-void GrCoordTransform::reset(const SkMatrix& m, const GrTexture* texture,
-                             GrSamplerParams::FilterMode filter, bool normalize) {
-    SkASSERT(texture);
-    SkASSERT(!fInProcessor);
-
-    fMatrix = m;
-    fTexture = texture;
-    fNormalize = normalize;
-    fReverseY = kBottomLeft_GrSurfaceOrigin == texture->origin();
-
-    if (texture->getContext()) {
-        fPrecision = compute_precision(texture->getContext()->caps()->shaderCaps(),
-                                       texture->width(), texture->height(), filter);
-    } else {
-        fPrecision = kDefault_GrSLPrecision;
-    }
-}
-
 void GrCoordTransform::reset(GrResourceProvider* resourceProvider, const SkMatrix& m,
-                             GrTextureProxy* proxy,
-                             GrSamplerParams::FilterMode filter, bool normalize) {
+                             GrTextureProxy* proxy, bool normalize) {
     SkASSERT(proxy);
     SkASSERT(!fInProcessor);
 
     fMatrix = m;
     // MDB TODO: just GrCaps is needed for this method
-    // MDB TODO: once all the coord transforms take a proxy just store it here and
-    // instantiate later
-    fTexture = proxy->instantiate(resourceProvider);
+    fProxy = proxy;
     fNormalize = normalize;
     fReverseY = kBottomLeft_GrSurfaceOrigin == proxy->origin();
-
-    const GrCaps* caps = resourceProvider->caps();
-    fPrecision = compute_precision(caps->shaderCaps(),
-                                   proxy->worstCaseWidth(*caps),
-                                   proxy->worstCaseHeight(*caps), filter);
 }
-
diff --git a/src/gpu/GrCoordTransform.h b/src/gpu/GrCoordTransform.h
new file mode 100644
index 0000000..325dbbd
--- /dev/null
+++ b/src/gpu/GrCoordTransform.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCoordTransform_DEFINED
+#define GrCoordTransform_DEFINED
+
+#include "SkMatrix.h"
+#include "GrSurfaceProxyPriv.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+
+class GrResourceProvider;
+
+/**
+ * A class representing a linear transformation of local coordinates. GrFragnentProcessors
+ * these transformations, and the GrGeometryProcessor implements the transformation.
+ */
+class GrCoordTransform : SkNoncopyable {
+public:
+    GrCoordTransform()
+        : fProxy(nullptr)
+        , fNormalize(false)
+        , fReverseY(false) {
+        SkDEBUGCODE(fInProcessor = false);
+    }
+
+    /**
+     * Create a transformation that maps [0, 1] to a proxy's boundaries. The proxy origin also
+     * implies whether a y-reversal should be performed.
+     */
+    GrCoordTransform(GrResourceProvider* resourceProvider, GrTextureProxy* proxy) {
+        SkASSERT(proxy);
+        SkDEBUGCODE(fInProcessor = false);
+        this->reset(resourceProvider, SkMatrix::I(), proxy, true);
+    }
+
+    /**
+     * Create a transformation from a matrix. The proxy origin also implies whether a y-reversal
+     * should be performed.
+     */
+    GrCoordTransform(GrResourceProvider* resourceProvider, const SkMatrix& m,
+                     GrTextureProxy* proxy) {
+        SkASSERT(proxy);
+        SkDEBUGCODE(fInProcessor = false);
+        this->reset(resourceProvider, m, proxy, true);
+    }
+
+    /**
+     * Create a transformation that applies the matrix to a coord set.
+     */
+    GrCoordTransform(const SkMatrix& m) {
+        SkDEBUGCODE(fInProcessor = false);
+        this->reset(m);
+    }
+
+    void reset(GrResourceProvider*, const SkMatrix&, GrTextureProxy*, bool normalize);
+
+    void reset(const SkMatrix& m) {
+        SkASSERT(!fInProcessor);
+        fMatrix = m;
+        fProxy = nullptr;
+        fNormalize = false;
+        fReverseY = false;
+    }
+
+    GrCoordTransform& operator= (const GrCoordTransform& that) {
+        SkASSERT(!fInProcessor);
+        fMatrix = that.fMatrix;
+        fProxy = that.fProxy;
+        fNormalize = that.fNormalize;
+        fReverseY = that.fReverseY;
+        return *this;
+    }
+
+    /**
+     * 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;
+    }
+
+    bool hasSameEffectAs(const GrCoordTransform& that) const {
+        if (fNormalize != that.fNormalize ||
+            fReverseY != that.fReverseY ||
+            !fMatrix.cheapEqualTo(that.fMatrix)) {
+            return false;
+        }
+
+        if (fNormalize) {
+            if (fProxy != that.fProxy) {
+                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; }
+
+    // This should only ever be called at flush time after the backing texture has been
+    // successfully instantiated
+    GrTexture* peekTexture() const {
+        SkASSERT(fProxy->priv().peekTexture());
+        return fProxy->priv().peekTexture();
+    }
+
+private:
+    // 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;
+    typedef SkNoncopyable INHERITED;
+
+#ifdef SK_DEBUG
+public:
+    void setInProcessor() const { fInProcessor = true; }
+private:
+    mutable bool fInProcessor;
+#endif
+};
+
+#endif
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index 90d3239..3f50bea 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -8,6 +8,7 @@
 #include "GrDefaultGeoProcFactory.h"
 
 #include "SkRefCnt.h"
+#include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLVertexShaderBuilder.h"
@@ -26,18 +27,22 @@
     kColorAttributeIsSkColor_GPFlag = 0x2,
     kLocalCoordAttribute_GPFlag     = 0x4,
     kCoverageAttribute_GPFlag       = 0x8,
+
+    kLinearizeColorAttribute_GPFlag = 0x10,
 };
 
 class DefaultGeoProc : public GrGeometryProcessor {
 public:
     static sk_sp<GrGeometryProcessor> Make(uint32_t gpTypeFlags,
                                            GrColor color,
+                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& viewMatrix,
                                            const SkMatrix& localMatrix,
                                            bool localCoordsWillBeRead,
                                            uint8_t coverage) {
         return sk_sp<GrGeometryProcessor>(new DefaultGeoProc(
-                gpTypeFlags, color, viewMatrix, localMatrix, coverage, localCoordsWillBeRead));
+                gpTypeFlags, color, std::move(colorSpaceXform), viewMatrix, localMatrix, coverage,
+                localCoordsWillBeRead));
     }
 
     const char* name() const override { return "DefaultGeometryProcessor"; }
@@ -53,6 +58,12 @@
     bool localCoordsWillBeRead() const { return fLocalCoordsWillBeRead; }
     uint8_t coverage() const { return fCoverage; }
     bool hasVertexCoverage() const { return SkToBool(fInCoverage); }
+    bool linearizeColor() const {
+        // Linearization should only happen with SkColor
+        bool linearize = SkToBool(fFlags & kLinearizeColorAttribute_GPFlag);
+        SkASSERT(!linearize || (fFlags & kColorAttributeIsSkColor_GPFlag));
+        return linearize;
+    }
 
     class GLSLProcessor : public GrGLSLGeometryProcessor {
     public:
@@ -73,14 +84,47 @@
             if (gp.hasVertexColor()) {
                 GrGLSLVertToFrag varying(kVec4f_GrSLType);
                 varyingHandler->addVarying("color", &varying);
-                if (gp.fFlags & kColorAttributeIsSkColor_GPFlag) {
-                    // Do a red/blue swap and premul the color.
-                    vertBuilder->codeAppendf("%s = vec4(%s.a*%s.bgr, %s.a);", varying.vsOut(),
-                                             gp.inColor()->fName, gp.inColor()->fName,
+
+                // There are several optional steps to process the color. Start with the attribute:
+                vertBuilder->codeAppendf("vec4 color = %s;", gp.inColor()->fName);
+
+                // Linearize
+                if (gp.linearizeColor()) {
+                    SkString srgbFuncName;
+                    static const GrShaderVar gSrgbArgs[] = {
+                        GrShaderVar("x", kFloat_GrSLType),
+                    };
+                    vertBuilder->emitFunction(kFloat_GrSLType,
+                                              "srgb_to_linear",
+                                              SK_ARRAY_COUNT(gSrgbArgs),
+                                              gSrgbArgs,
+                                              "return (x <= 0.04045) ? (x / 12.92) "
+                                              ": pow((x + 0.055) / 1.055, 2.4);",
+                                              &srgbFuncName);
+                    vertBuilder->codeAppendf("color = vec4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);",
+                                             srgbFuncName.c_str(), gp.inColor()->fName,
+                                             srgbFuncName.c_str(), gp.inColor()->fName,
+                                             srgbFuncName.c_str(), gp.inColor()->fName,
                                              gp.inColor()->fName);
-                } else {
-                    vertBuilder->codeAppendf("%s = %s;\n", varying.vsOut(), gp.inColor()->fName);
                 }
+
+                // For SkColor, do a red/blue swap and premul
+                if (gp.fFlags & kColorAttributeIsSkColor_GPFlag) {
+                    vertBuilder->codeAppend("color = vec4(color.a * color.bgr, color.a);");
+                }
+
+                // Do color-correction to destination gamut
+                if (gp.linearizeColor()) {
+                    fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(),
+                                               kVertex_GrShaderFlag);
+                    if (fColorSpaceHelper.isValid()) {
+                        SkString xformedColor;
+                        vertBuilder->appendColorGamutXform(&xformedColor, "color",
+                                                           &fColorSpaceHelper);
+                        vertBuilder->codeAppendf("color = %s;", xformedColor.c_str());
+                    }
+                }
+                vertBuilder->codeAppendf("%s = color;\n", varying.vsOut());
                 fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
             } else {
                 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
@@ -142,6 +186,9 @@
             key |= (def.localCoordsWillBeRead() && def.localMatrix().hasPerspective()) ? 0x20 : 0x0;
             key |= ComputePosKey(def.viewMatrix()) << 20;
             b->add32(key);
+            if (def.linearizeColor()) {
+                b->add32(GrColorSpaceXform::XformKey(def.fColorSpaceXform.get()));
+            }
         }
 
         void setData(const GrGLSLProgramDataManager& pdman,
@@ -168,6 +215,10 @@
                 fCoverage = dgp.coverage();
             }
             this->setTransformDataHelper(dgp.fLocalMatrix, pdman, &transformIter);
+
+            if (dgp.linearizeColor() && dgp.fColorSpaceXform) {
+                fColorSpaceHelper.setData(pdman, dgp.fColorSpaceXform.get());
+            }
         }
 
     private:
@@ -177,6 +228,7 @@
         UniformHandle fViewMatrixUniform;
         UniformHandle fColorUniform;
         UniformHandle fCoverageUniform;
+        GrGLSLColorSpaceXformHelper fColorSpaceHelper;
 
         typedef GrGLSLGeometryProcessor INHERITED;
     };
@@ -192,6 +244,7 @@
 private:
     DefaultGeoProc(uint32_t gpTypeFlags,
                    GrColor color,
+                   sk_sp<GrColorSpaceXform> colorSpaceXform,
                    const SkMatrix& viewMatrix,
                    const SkMatrix& localMatrix,
                    uint8_t coverage,
@@ -201,7 +254,8 @@
             , fLocalMatrix(localMatrix)
             , fCoverage(coverage)
             , fFlags(gpTypeFlags)
-            , fLocalCoordsWillBeRead(localCoordsWillBeRead) {
+            , fLocalCoordsWillBeRead(localCoordsWillBeRead)
+            , fColorSpaceXform(std::move(colorSpaceXform)) {
         this->initClassID<DefaultGeoProc>();
         fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
                                              kHigh_GrSLPrecision);
@@ -228,6 +282,7 @@
     uint8_t fCoverage;
     uint32_t fFlags;
     bool fLocalCoordsWillBeRead;
+    sk_sp<GrColorSpaceXform> fColorSpaceXform;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
 
@@ -254,9 +309,9 @@
 
     return DefaultGeoProc::Make(flags,
                                 GrRandomColor(d->fRandom),
+                                GrTest::TestColorXform(d->fRandom),
                                 GrTest::TestMatrix(d->fRandom),
                                 GrTest::TestMatrix(d->fRandom),
-
                                 d->fRandom->nextBool(),
                                 GrRandomCoverage(d->fRandom));
 }
@@ -272,6 +327,12 @@
     } else if (Color::kUnpremulSkColorAttribute_Type == color.fType) {
         flags |= kColorAttribute_GPFlag | kColorAttributeIsSkColor_GPFlag;
     }
+    if (color.fLinearize) {
+        // It only makes sense to linearize SkColors (which are always sRGB). GrColor values should
+        // have been linearized and gamut-converted during paint conversion
+        SkASSERT(Color::kUnpremulSkColorAttribute_Type == color.fType);
+        flags |= kLinearizeColorAttribute_GPFlag;
+    }
     flags |= coverage.fType == Coverage::kAttribute_Type ? kCoverageAttribute_GPFlag : 0;
     flags |= localCoords.fType == LocalCoords::kHasExplicit_Type ? kLocalCoordAttribute_GPFlag : 0;
 
@@ -281,6 +342,7 @@
     GrColor inColor = color.fColor;
     return DefaultGeoProc::Make(flags,
                                 inColor,
+                                color.fColorSpaceXform,
                                 viewMatrix,
                                 localCoords.fMatrix ? *localCoords.fMatrix : SkMatrix::I(),
                                 localCoordsWillBeRead,
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index 00ee90d..6a3c0b7 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -66,13 +66,26 @@
             kPremulGrColorAttribute_Type,
             kUnpremulSkColorAttribute_Type,
         };
-        explicit Color(GrColor color) : fType(kPremulGrColorUniform_Type), fColor(color) {}
-        Color(Type type) : fType(type), fColor(GrColor_ILLEGAL) {
+        explicit Color(GrColor color)
+                : fType(kPremulGrColorUniform_Type)
+                , fColor(color)
+                , fLinearize(false)
+                , fColorSpaceXform(nullptr) {}
+        Color(Type type)
+                : fType(type)
+                , fColor(GrColor_ILLEGAL)
+                , fLinearize(false)
+                , fColorSpaceXform(nullptr) {
             SkASSERT(type != kPremulGrColorUniform_Type);
         }
 
         Type fType;
         GrColor fColor;
+
+        // These options only apply to SkColor. Any GrColors are assumed to have been color managed
+        // during paint conversion.
+        bool fLinearize;
+        sk_sp<GrColorSpaceXform> fColorSpaceXform;
     };
 
     struct Coverage {
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp
index 143bdd8..78f6ba4 100644
--- a/src/gpu/GrDrawOpAtlas.cpp
+++ b/src/gpu/GrDrawOpAtlas.cpp
@@ -172,9 +172,6 @@
 
     SkDEBUGCODE(fNumPlots = numPlotsX * numPlotsY;)
 
-    // We currently do not support compressed atlases...
-    SkASSERT(!GrPixelConfigIsCompressed(fProxy->desc().fConfig));
-
     // set up allocated plots
     fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
 
@@ -183,7 +180,7 @@
         for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
             uint32_t index = r * numPlotsX + c;
             currPlot->reset(
-                    new Plot(index, 1, x, y, fPlotWidth, fPlotHeight, fProxy->desc().fConfig));
+                    new Plot(index, 1, x, y, fPlotWidth, fPlotHeight, fProxy->config()));
 
             // build LRU list
             fPlotList.addToHead(currPlot->get());
@@ -210,10 +207,10 @@
 
         // MDB TODO: this is currently fine since the atlas' proxy is always pre-instantiated.
         // Once it is deferred more care must be taken upon instantiation failure.
-        GrTexture* texture = fProxy->instantiate(fContext->resourceProvider());
-        if (!texture) {
+        if (!fProxy->instantiate(fContext->resourceProvider())) {
             return false;
         }
+        GrTexture* texture = fProxy->priv().peekTexture();
 
         GrDrawOpUploadToken lastUploadToken = target->addAsapUpload(
             [plotsp, texture] (GrDrawOp::WritePixelsFn& writePixels) {
@@ -239,7 +236,7 @@
     plotIter.init(fPlotList, PlotList::Iter::kHead_IterStart);
     Plot* plot;
     while ((plot = plotIter.get())) {
-        SkASSERT(GrBytesPerPixel(fProxy->desc().fConfig) == plot->bpp());
+        SkASSERT(GrBytesPerPixel(fProxy->config()) == plot->bpp());
         if (plot->addSubImage(width, height, image, loc)) {
             return this->updatePlot(target, id, plot);
         }
@@ -253,7 +250,7 @@
     if (target->hasDrawBeenFlushed(plot->lastUseToken())) {
         this->processEviction(plot->id());
         plot->resetRects();
-        SkASSERT(GrBytesPerPixel(fProxy->desc().fConfig) == plot->bpp());
+        SkASSERT(GrBytesPerPixel(fProxy->config()) == plot->bpp());
         SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc);
         SkASSERT(verify);
         if (!this->updatePlot(target, id, plot)) {
@@ -279,7 +276,7 @@
     newPlot.reset(plot->clone());
 
     fPlotList.addToHead(newPlot.get());
-    SkASSERT(GrBytesPerPixel(fProxy->desc().fConfig) == newPlot->bpp());
+    SkASSERT(GrBytesPerPixel(fProxy->config()) == newPlot->bpp());
     SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc);
     SkASSERT(verify);
 
@@ -289,10 +286,10 @@
     sk_sp<Plot> plotsp(SkRef(newPlot.get()));
     // MDB TODO: this is currently fine since the atlas' proxy is always pre-instantiated.
     // Once it is deferred more care must be taken upon instantiation failure.
-    GrTexture* texture = fProxy->instantiate(fContext->resourceProvider());
-    if (!texture) {
+    if (!fProxy->instantiate(fContext->resourceProvider())) {
         return false;
     }
+    GrTexture* texture = fProxy->priv().peekTexture();
 
     GrDrawOpUploadToken lastUploadToken = target->addInlineUpload(
         [plotsp, texture] (GrDrawOp::WritePixelsFn& writePixels) {
diff --git a/src/gpu/GrDrawOpTest.cpp b/src/gpu/GrDrawOpTest.cpp
index 6e63f5d..7aba424 100644
--- a/src/gpu/GrDrawOpTest.cpp
+++ b/src/gpu/GrDrawOpTest.cpp
@@ -6,62 +6,53 @@
  */
 
 #include "GrDrawOpTest.h"
+#include "GrCaps.h"
+#include "GrContext.h"
+#include "GrUserStencilSettings.h"
 #include "SkRandom.h"
 #include "SkTypes.h"
-#include "ops/GrMeshDrawOp.h"
 
 #if GR_TEST_UTILS
 
-#define DRAW_OP_TEST_EXTERN(Op) \
-    extern std::unique_ptr<GrMeshDrawOp> Op##__Test(SkRandom*, GrContext* context);
+const GrUserStencilSettings* GrGetRandomStencil(SkRandom* random, GrContext* context) {
+    if (context->caps()->avoidStencilBuffers()) {
+        return &GrUserStencilSettings::kUnused;
+    }
+    static constexpr GrUserStencilSettings kReads(
+        GrUserStencilSettings::StaticInit<
+            0x8080,
+            GrUserStencilTest::kLess,
+            0xffff,
+            GrUserStencilOp::kKeep,
+            GrUserStencilOp::kKeep,
+            0xffff>()
+    );
+    static constexpr GrUserStencilSettings kWrites(
+        GrUserStencilSettings::StaticInit<
+            0xffff,
+            GrUserStencilTest::kAlways,
+            0xffff,
+            GrUserStencilOp::kReplace,
+            GrUserStencilOp::kReplace,
+            0xffff>()
+    );
+    static constexpr GrUserStencilSettings kReadsAndWrites(
+        GrUserStencilSettings::StaticInit<
+            0x8000,
+            GrUserStencilTest::kEqual,
+            0x6000,
+            GrUserStencilOp::kIncWrap,
+            GrUserStencilOp::kInvert,
+            0x77ff>()
+    );
 
-#define DRAW_OP_TEST_ENTRY(Op) Op##__Test
-
-DRAW_OP_TEST_EXTERN(AAConvexPathOp);
-DRAW_OP_TEST_EXTERN(AAFillRectOp);
-DRAW_OP_TEST_EXTERN(AAFillRectOpLocalMatrix);
-DRAW_OP_TEST_EXTERN(AAFlatteningConvexPathOp)
-DRAW_OP_TEST_EXTERN(AAHairlineOp);
-DRAW_OP_TEST_EXTERN(AAStrokeRectOp);
-DRAW_OP_TEST_EXTERN(AnalyticRectOp);
-DRAW_OP_TEST_EXTERN(DashOp);
-DRAW_OP_TEST_EXTERN(DefaultPathOp);
-DRAW_OP_TEST_EXTERN(CircleOp);
-DRAW_OP_TEST_EXTERN(DIEllipseOp);
-DRAW_OP_TEST_EXTERN(EllipseOp);
-DRAW_OP_TEST_EXTERN(GrDrawAtlasOp);
-DRAW_OP_TEST_EXTERN(NonAAStrokeRectOp);
-DRAW_OP_TEST_EXTERN(RRectOp);
-DRAW_OP_TEST_EXTERN(SmallPathOp);
-DRAW_OP_TEST_EXTERN(TesselatingPathOp);
-DRAW_OP_TEST_EXTERN(TextBlobOp);
-DRAW_OP_TEST_EXTERN(VerticesOp);
-
-std::unique_ptr<GrMeshDrawOp> GrRandomDrawOp(SkRandom* random, GrContext* context) {
-    using MakeTestDrawOpFn = std::unique_ptr<GrMeshDrawOp>(SkRandom* random, GrContext* context);
-    static constexpr MakeTestDrawOpFn* gFactories[] = {
-        DRAW_OP_TEST_ENTRY(AAConvexPathOp),
-        DRAW_OP_TEST_ENTRY(AAFillRectOp),
-        DRAW_OP_TEST_ENTRY(AAFillRectOpLocalMatrix),
-        DRAW_OP_TEST_ENTRY(AAFlatteningConvexPathOp),
-        DRAW_OP_TEST_ENTRY(AAHairlineOp),
-        DRAW_OP_TEST_ENTRY(AAStrokeRectOp),
-        DRAW_OP_TEST_ENTRY(AnalyticRectOp),
-        DRAW_OP_TEST_ENTRY(DashOp),
-        DRAW_OP_TEST_ENTRY(DefaultPathOp),
-        DRAW_OP_TEST_ENTRY(CircleOp),
-        DRAW_OP_TEST_ENTRY(DIEllipseOp),
-        DRAW_OP_TEST_ENTRY(EllipseOp),
-        DRAW_OP_TEST_ENTRY(GrDrawAtlasOp),
-        DRAW_OP_TEST_ENTRY(NonAAStrokeRectOp),
-        DRAW_OP_TEST_ENTRY(RRectOp),
-        DRAW_OP_TEST_ENTRY(SmallPathOp),
-        DRAW_OP_TEST_ENTRY(TesselatingPathOp),
-        DRAW_OP_TEST_ENTRY(TextBlobOp),
-        DRAW_OP_TEST_ENTRY(VerticesOp)
+    static const GrUserStencilSettings* kStencilSettings[] = {
+            &GrUserStencilSettings::kUnused,
+            &kReads,
+            &kWrites,
+            &kReadsAndWrites,
     };
-
-    uint32_t index = random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gFactories)));
-    return gFactories[index](random, context);
+    return kStencilSettings[random->nextULessThan(SK_ARRAY_COUNT(kStencilSettings))];
 }
+
 #endif
diff --git a/src/gpu/GrDrawOpTest.h b/src/gpu/GrDrawOpTest.h
index 43bd21a..a30ca28 100644
--- a/src/gpu/GrDrawOpTest.h
+++ b/src/gpu/GrDrawOpTest.h
@@ -13,20 +13,29 @@
 
 #if GR_TEST_UTILS
 
-class GrContext;
-class GrMeshDrawOp;
+class GrDrawOp;
+class GrLegacyMeshDrawOp;
+class GrPaint;
+class GrRenderTargetContext;
+struct GrUserStencilSettings;
 class SkRandom;
 
-/**  This function returns a randomly configured GrDrawOp for testing purposes. */
-std::unique_ptr<GrMeshDrawOp> GrRandomDrawOp(SkRandom*, GrContext*);
+/**  This function draws a randomly configured GrDrawOp for testing purposes. */
+void GrDrawRandomOp(SkRandom*, GrRenderTargetContext*, GrPaint&&);
 
 /** GrDrawOp subclasses should define test factory functions using this macro. */
-#define DRAW_OP_TEST_DEFINE(Op) \
-    std::unique_ptr<GrMeshDrawOp> Op##__Test(SkRandom* random, GrContext* context)
+#define GR_DRAW_OP_TEST_DEFINE(Op)                                                              \
+    std::unique_ptr<GrDrawOp> Op##__Test(GrPaint&& paint, SkRandom* random, GrContext* context, \
+                                         GrFSAAType fsaaType)
 
-/** This macro may be used if the test factory function must be made a friend of a class. */
-#define DRAW_OP_TEST_FRIEND(Op) \
-    friend std::unique_ptr<GrMeshDrawOp> Op##__Test(SkRandom* random, GrContext* context);
+/** Variations for GrLegacyMeshDrawOps. To be deleted. */
+#define GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(Op) \
+    std::unique_ptr<GrLegacyMeshDrawOp> Op##__Test(SkRandom* random, GrContext* context)
+#define GR_LEGACY_MESH_DRAW_OP_TEST_FRIEND(Op) \
+    friend std::unique_ptr<GrLegacyMeshDrawOp> Op##__Test(SkRandom* random, GrContext* context);
+
+/** Helper for op test factories to pick a random stencil state. */
+const GrUserStencilSettings* GrGetRandomStencil(SkRandom* random, GrContext*);
 
 #endif
 #endif
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 449cae6..67b5e8f 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -14,6 +14,7 @@
 #include "GrResourceProvider.h"
 #include "GrSoftwarePathRenderer.h"
 #include "GrSurfacePriv.h"
+#include "GrSurfaceProxyPriv.h"
 #include "GrTextureContext.h"
 #include "GrTextureOpList.h"
 #include "SkSurface_Gpu.h"
@@ -24,13 +25,13 @@
 
 void GrDrawingManager::cleanup() {
     for (int i = 0; i < fOpLists.count(); ++i) {
-        fOpLists[i]->makeClosed();  // no opList should receive a new command after this
-        fOpLists[i]->clearTarget();
+        // no opList should receive a new command after this
+        fOpLists[i]->makeClosed(*fContext->caps());
 
         // We shouldn't need to do this, but it turns out some clients still hold onto opLists
-        // after a cleanup
+        // after a cleanup.
+        // MDB TODO: is this still true?
         fOpLists[i]->reset();
-        fOpLists[i]->unref();
     }
 
     fOpLists.reset();
@@ -69,7 +70,17 @@
     fFlushState.reset();
 }
 
-void GrDrawingManager::internalFlush(GrResourceCache::FlushType type) {
+gr_instanced::OpAllocator* GrDrawingManager::instancingAllocator() {
+    if (fInstancingAllocator) {
+        return fInstancingAllocator.get();
+    }
+
+    fInstancingAllocator = fContext->getGpu()->createInstancedRenderingAllocator();
+    return fInstancingAllocator.get();
+}
+
+// MDB TODO: make use of the 'proxy' parameter.
+void GrDrawingManager::internalFlush(GrSurfaceProxy*, GrResourceCache::FlushType type) {
     if (fFlushing || this->wasAbandoned()) {
         return;
     }
@@ -81,16 +92,35 @@
         // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
         // but need to be flushed anyway. Closing such GrOpLists here will mean new
         // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
-        fOpLists[i]->makeClosed();
+        fOpLists[i]->makeClosed(*fContext->caps());
     }
 
+#ifdef SK_DEBUG
+    // This block checks for any unnecessary splits in the opLists. If two sequential opLists
+    // share the same backing GrSurfaceProxy it means the opList was artificially split.
+    if (fOpLists.count()) {
+        GrRenderTargetOpList* prevOpList = fOpLists[0]->asRenderTargetOpList();
+        for (int i = 1; i < fOpLists.count(); ++i) {
+            GrRenderTargetOpList* curOpList = fOpLists[i]->asRenderTargetOpList();
+
+            if (prevOpList && curOpList) {
+                SkASSERT(prevOpList->fTarget.get() != curOpList->fTarget.get());
+            }
+
+            prevOpList = curOpList;
+        }
+    }
+#endif
+
+#ifdef ENABLE_MDB
     SkDEBUGCODE(bool result =)
                         SkTTopoSort<GrOpList, GrOpList::TopoSortTraits>(&fOpLists);
     SkASSERT(result);
+#endif
 
-    GrPreFlushResourceProvider preFlushProvider(this);
+    GrOnFlushResourceProvider onFlushProvider(this);
 
-    if (fPreFlushCBObjects.count()) {
+    if (!fOnFlushCBObjects.empty()) {
         // MDB TODO: pre-MDB '1' is the correct pre-allocated size. Post-MDB it will need
         // to be larger.
         SkAutoSTArray<1, uint32_t> opListIds(fOpLists.count());
@@ -99,10 +129,10 @@
         }
 
         SkSTArray<1, sk_sp<GrRenderTargetContext>> renderTargetContexts;
-        for (int i = 0; i < fPreFlushCBObjects.count(); ++i) {
-            fPreFlushCBObjects[i]->preFlush(&preFlushProvider,
-                                            opListIds.get(), opListIds.count(),
-                                            &renderTargetContexts);
+        for (GrOnFlushCallbackObject* onFlushCBObject : fOnFlushCBObjects) {
+            onFlushCBObject->preFlush(&onFlushProvider,
+                                      opListIds.get(), opListIds.count(),
+                                      &renderTargetContexts);
             if (!renderTargetContexts.count()) {
                 continue;       // This is fine. No atlases of this type are required for this flush
             }
@@ -112,7 +142,7 @@
                 if (!opList) {
                     continue;   // Odd - but not a big deal
                 }
-                SkDEBUGCODE(opList->validateTargetsSingleRenderTarget());
+                opList->makeClosed(*fContext->caps());
                 opList->prepareOps(&fFlushState);
                 if (!opList->executeOps(&fFlushState)) {
                     continue;         // This is bad
@@ -122,10 +152,6 @@
         }
     }
 
-    for (int i = 0; i < fOpLists.count(); ++i) {
-        fOpLists[i]->prepareOps(&fFlushState);
-    }
-
 #if 0
     // Enable this to print out verbose GrOp information
     for (int i = 0; i < fOpLists.count(); ++i) {
@@ -133,110 +159,109 @@
     }
 #endif
 
+    for (int i = 0; i < fOpLists.count(); ++i) {
+        if (!fOpLists[i]->instantiate(fContext->resourceProvider())) {
+            fOpLists[i] = nullptr;
+            continue;
+        }
+
+        fOpLists[i]->prepareOps(&fFlushState);
+    }
+
     // Upload all data to the GPU
     fFlushState.preIssueDraws();
 
     for (int i = 0; i < fOpLists.count(); ++i) {
+        if (!fOpLists[i]) {
+            continue;
+        }
+
         if (fOpLists[i]->executeOps(&fFlushState)) {
             flushed = true;
         }
+        fOpLists[i]->reset();
     }
+    fOpLists.reset();
 
     SkASSERT(fFlushState.nextDrawToken() == fFlushState.nextTokenToFlush());
 
-    for (int i = 0; i < fOpLists.count(); ++i) {
-        fOpLists[i]->reset();
-#ifdef ENABLE_MDB
-        fOpLists[i]->unref();
-#endif
-    }
-
-#ifndef ENABLE_MDB
-    // When MDB is disabled we keep reusing the same GrOpList
-    if (fOpLists.count()) {
-        SkASSERT(fOpLists.count() == 1);
-        // Clear out this flag so the topological sort's SkTTopoSort_CheckAllUnmarked check
-        // won't bark
-        fOpLists[0]->resetFlag(GrOpList::kWasOutput_Flag);
-    }
-#else
-    fOpLists.reset();
-#endif
+    fContext->getGpu()->finishFlush();
 
     fFlushState.reset();
     // We always have to notify the cache when it requested a flush so it can reset its state.
     if (flushed || type == GrResourceCache::FlushType::kCacheRequested) {
         fContext->getResourceCache()->notifyFlushOccurred(type);
     }
+    for (GrOnFlushCallbackObject* onFlushCBObject : fOnFlushCBObjects) {
+        onFlushCBObject->postFlush();
+    }
     fFlushing = false;
 }
 
-void GrDrawingManager::prepareSurfaceForExternalIO(GrSurface* surface) {
+void GrDrawingManager::prepareSurfaceForExternalIO(GrSurfaceProxy* proxy) {
     if (this->wasAbandoned()) {
         return;
     }
-    SkASSERT(surface);
-    SkASSERT(surface->getContext() == fContext);
+    SkASSERT(proxy);
 
-    if (surface->surfacePriv().hasPendingIO()) {
-        this->flush();
+    if (proxy->priv().hasPendingIO()) {
+        this->flush(proxy);
     }
 
-    GrRenderTarget* rt = surface->asRenderTarget();
-    if (fContext->getGpu() && rt) {
-        fContext->getGpu()->resolveRenderTarget(rt);
+    if (!proxy->instantiate(fContext->resourceProvider())) {
+        return;
+    }
+
+    GrSurface* surface = proxy->priv().peekSurface();
+
+    if (fContext->getGpu() && surface->asRenderTarget()) {
+        fContext->getGpu()->resolveRenderTarget(surface->asRenderTarget());
     }
 }
 
-void GrDrawingManager::addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject) {
-    fPreFlushCBObjects.push_back(preFlushCBObject);
+void GrDrawingManager::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlushCBObject) {
+    fOnFlushCBObjects.push_back(onFlushCBObject);
 }
 
-GrRenderTargetOpList* GrDrawingManager::newOpList(GrRenderTargetProxy* rtp) {
+sk_sp<GrRenderTargetOpList> GrDrawingManager::newRTOpList(GrRenderTargetProxy* rtp) {
     SkASSERT(fContext);
 
-#ifndef ENABLE_MDB
-    // When MDB is disabled we always just return the single GrOpList
-    if (fOpLists.count()) {
-        SkASSERT(fOpLists.count() == 1);
-        // In the non-MDB-world the same GrOpList gets reused for multiple render targets.
-        // Update this pointer so all the asserts are happy
-        rtp->setLastOpList(fOpLists[0]);
-        // DrawingManager gets the creation ref - this ref is for the caller
-
-        // TODO: although this is true right now it isn't cool
-        return SkRef((GrRenderTargetOpList*) fOpLists[0]);
+    // This is  a temporary fix for the partial-MDB world. In that world we're not reordering
+    // so ops that (in the single opList world) would've just glommed onto the end of the single
+    // opList but referred to a far earlier RT need to appear in their own opList.
+    if (!fOpLists.empty()) {
+        fOpLists.back()->makeClosed(*fContext->caps());
     }
-#endif
 
-    GrRenderTargetOpList* opList = new GrRenderTargetOpList(rtp,
-                                                            fContext->getGpu(),
-                                                            fContext->resourceProvider(),
-                                                            fContext->getAuditTrail(),
-                                                            fOptionsForOpLists);
+    sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(rtp,
+                                                                fContext->getGpu(),
+                                                                fContext->getAuditTrail()));
+    SkASSERT(rtp->getLastOpList() == opList.get());
 
-    *fOpLists.append() = opList;
+    fOpLists.push_back() = opList;
 
-    // DrawingManager gets the creation ref - this ref is for the caller
-    return SkRef(opList);
-}
-
-GrTextureOpList* GrDrawingManager::newOpList(GrTextureProxy* textureProxy) {
-    SkASSERT(fContext);
-
-    GrTextureOpList* opList = new GrTextureOpList(textureProxy, fContext->getGpu(),
-                                                  fContext->getAuditTrail());
-
-#ifndef ENABLE_MDB
-    // When MDB is disabled we still create a new GrOpList, but don't store or ref it - we rely
-    // on the caller to immediately execute and free it.
     return opList;
-#else
-    *fOpLists.append() = opList;
+}
 
-    // Drawing manager gets the creation ref - this ref is for the caller
-    return SkRef(opList);
-#endif
+sk_sp<GrTextureOpList> GrDrawingManager::newTextureOpList(GrTextureProxy* textureProxy) {
+    SkASSERT(fContext);
+
+    // This is  a temporary fix for the partial-MDB world. In that world we're not reordering
+    // so ops that (in the single opList world) would've just glommed onto the end of the single
+    // opList but referred to a far earlier RT need to appear in their own opList.
+    if (!fOpLists.empty()) {
+        fOpLists.back()->makeClosed(*fContext->caps());
+    }
+
+    sk_sp<GrTextureOpList> opList(new GrTextureOpList(fContext->resourceProvider(),
+                                                      textureProxy,
+                                                      fContext->getAuditTrail()));
+
+    SkASSERT(textureProxy->getLastOpList() == opList.get());
+
+    fOpLists.push_back() = opList;
+
+    return opList;
 }
 
 GrAtlasTextContext* GrDrawingManager::getAtlasTextContext() {
@@ -301,13 +326,14 @@
     }
 
     if (useDIF && fContext->caps()->shaderCaps()->pathRenderingSupport() &&
-        rtp->isStencilBufferMultisampled()) {
+        GrFSAAType::kNone != rtp->fsaaType()) {
         // TODO: defer stencil buffer attachment for PathRenderingDrawContext
-        sk_sp<GrRenderTarget> rt(sk_ref_sp(rtp->instantiate(fContext->resourceProvider())));
-        if (!rt) {
+        if (!rtp->instantiate(fContext->resourceProvider())) {
             return nullptr;
         }
-        GrStencilAttachment* sb = fContext->resourceProvider()->attachStencilAttachment(rt.get());
+        GrRenderTarget* rt = rtp->priv().peekRenderTarget();
+
+        GrStencilAttachment* sb = fContext->resourceProvider()->attachStencilAttachment(rt);
         if (sb) {
             return sk_sp<GrRenderTargetContext>(new GrPathRenderingRenderTargetContext(
                                                         fContext, this, std::move(rtp),
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index d914b03..15211ef 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -11,10 +11,11 @@
 #include "GrOpFlushState.h"
 #include "GrPathRenderer.h"
 #include "GrPathRendererChain.h"
-#include "GrPreFlushResourceProvider.h"
+#include "GrOnFlushResourceProvider.h"
 #include "GrRenderTargetOpList.h"
 #include "GrResourceCache.h"
 #include "SkTArray.h"
+#include "instanced/InstancedRendering.h"
 #include "text/GrAtlasTextContext.h"
 
 class GrContext;
@@ -37,6 +38,8 @@
     bool wasAbandoned() const { return fAbandoned; }
     void freeGpuResources();
 
+    gr_instanced::OpAllocator* instancingAllocator();
+
     sk_sp<GrRenderTargetContext> makeRenderTargetContext(sk_sp<GrSurfaceProxy>,
                                                          sk_sp<SkColorSpace>,
                                                          const SkSurfaceProps*);
@@ -44,8 +47,8 @@
 
     // The caller automatically gets a ref on the returned opList. It must
     // be balanced by an unref call.
-    GrRenderTargetOpList* newOpList(GrRenderTargetProxy* rtp);
-    GrTextureOpList* newOpList(GrTextureProxy* textureProxy);
+    sk_sp<GrRenderTargetOpList> newRTOpList(GrRenderTargetProxy* rtp);
+    sk_sp<GrTextureOpList> newTextureOpList(GrTextureProxy* textureProxy);
 
     GrContext* getContext() { return fContext; }
 
@@ -58,25 +61,22 @@
 
     void flushIfNecessary() {
         if (fContext->getResourceCache()->requestsFlush()) {
-            this->internalFlush(GrResourceCache::kCacheRequested);
-        } else if (fIsImmediateMode) {
-            this->internalFlush(GrResourceCache::kImmediateMode);
+            this->internalFlush(nullptr, GrResourceCache::kCacheRequested);
         }
     }
 
     static bool ProgramUnitTest(GrContext* context, int maxStages);
 
-    void prepareSurfaceForExternalIO(GrSurface*);
+    void prepareSurfaceForExternalIO(GrSurfaceProxy*);
 
-    void addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject);
+    void addOnFlushCallbackObject(GrOnFlushCallbackObject*);
+    void testingOnly_removeOnFlushCallbackObject(GrOnFlushCallbackObject*);
 
 private:
     GrDrawingManager(GrContext* context,
-                     const GrRenderTargetOpList::Options& optionsForOpLists,
                      const GrPathRendererChain::Options& optionsForPathRendererChain,
-                     bool isImmediateMode, GrSingleOwner* singleOwner)
+                     GrSingleOwner* singleOwner)
         : fContext(context)
-        , fOptionsForOpLists(optionsForOpLists)
         , fOptionsForPathRendererChain(optionsForPathRendererChain)
         , fSingleOwner(singleOwner)
         , fAbandoned(false)
@@ -84,31 +84,32 @@
         , fPathRendererChain(nullptr)
         , fSoftwarePathRenderer(nullptr)
         , fFlushState(context->getGpu(), context->resourceProvider())
-        , fFlushing(false)
-        , fIsImmediateMode(isImmediateMode) {
+        , fFlushing(false) {
     }
 
     void abandon();
     void cleanup();
     void reset();
-    void flush() { this->internalFlush(GrResourceCache::FlushType::kExternal); }
-    void internalFlush(GrResourceCache::FlushType);
+    void flush(GrSurfaceProxy* proxy) {
+        this->internalFlush(proxy, GrResourceCache::FlushType::kExternal);
+    }
+    void internalFlush(GrSurfaceProxy*, GrResourceCache::FlushType);
 
     friend class GrContext;  // for access to: ctor, abandon, reset & flush
-    friend class GrPreFlushResourceProvider; // this is just a shallow wrapper around this class
+    friend class GrContextPriv; // access to: flush
+    friend class GrOnFlushResourceProvider; // this is just a shallow wrapper around this class
 
     static const int kNumPixelGeometries = 5; // The different pixel geometries
     static const int kNumDFTOptions = 2;      // DFT or no DFT
 
     GrContext*                        fContext;
-    GrRenderTargetOpList::Options     fOptionsForOpLists;
     GrPathRendererChain::Options      fOptionsForPathRendererChain;
 
     // In debug builds we guard against improper thread handling
     GrSingleOwner*                    fSingleOwner;
 
     bool                              fAbandoned;
-    SkTDArray<GrOpList*>              fOpLists;
+    SkTArray<sk_sp<GrOpList>>         fOpLists;
 
     std::unique_ptr<GrAtlasTextContext> fAtlasTextContext;
 
@@ -118,9 +119,10 @@
     GrOpFlushState                    fFlushState;
     bool                              fFlushing;
 
-    bool                              fIsImmediateMode;
+    SkTArray<GrOnFlushCallbackObject*> fOnFlushCBObjects;
 
-    SkTArray<sk_sp<GrPreFlushCallbackObject>> fPreFlushCBObjects;
+    // Lazily allocated
+    std::unique_ptr<gr_instanced::OpAllocator> fInstancingAllocator;
 };
 
 #endif
diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp
index fdec93b..d69b34d 100644
--- a/src/gpu/GrFragmentProcessor.cpp
+++ b/src/gpu/GrFragmentProcessor.cpp
@@ -8,7 +8,7 @@
 #include "GrFragmentProcessor.h"
 #include "GrCoordTransform.h"
 #include "GrPipeline.h"
-#include "GrPipelineAnalysis.h"
+#include "GrProcessorAnalysis.h"
 #include "effects/GrConstColorProcessor.h"
 #include "effects/GrXfermodeFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
@@ -61,15 +61,26 @@
     SkDEBUGCODE(transform->setInProcessor();)
 }
 
+bool GrFragmentProcessor::instantiate(GrResourceProvider* resourceProvider) const {
+    if (!INHERITED::instantiate(resourceProvider)) {
+        return false;
+    }
+
+    for (int i = 0; i < this->numChildProcessors(); ++i) {
+        if (!this->childProcessor(i).instantiate(resourceProvider)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 int GrFragmentProcessor::registerChildProcessor(sk_sp<GrFragmentProcessor> child) {
     this->combineRequiredFeatures(*child);
 
     if (child->usesLocalCoords()) {
         fFlags |= kUsesLocalCoords_Flag;
     }
-    if (child->usesDistanceVectorField()) {
-        fFlags |= kUsesDistanceVectorField_Flag;
-    }
 
     int index = fChildProcessors.count();
     fChildProcessors.push_back(child.release());
@@ -283,7 +294,7 @@
             public:
                 void emitCode(EmitArgs& args) override {
                     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-                    this->emitChild(0, nullptr, args);
+                    this->emitChild(0, args);
                     fragBuilder->codeAppendf("%s.rgb *= %s.rgb;", args.fOutputColor,
                                                                 args.fInputColor);
                     fragBuilder->codeAppendf("%s *= %s.a;", args.fOutputColor, args.fInputColor);
@@ -353,7 +364,7 @@
 
             private:
                 void onSetData(const GrGLSLProgramDataManager& pdman,
-                               const GrProcessor& fp) override {
+                               const GrFragmentProcessor& fp) override {
                     GrColor4f color = fp.cast<ReplaceInputFragmentProcessor>().fColor;
                     if (!fHaveSetColor || color != fPreviousColor) {
                         pdman.set4fv(fColorUni, 1, color.fRGBA);
diff --git a/src/gpu/GrFragmentProcessor.h b/src/gpu/GrFragmentProcessor.h
new file mode 100644
index 0000000..a4b28c2
--- /dev/null
+++ b/src/gpu/GrFragmentProcessor.h
@@ -0,0 +1,343 @@
+/*
+ * 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 GrFragmentProcessor_DEFINED
+#define GrFragmentProcessor_DEFINED
+
+#include "GrProcessor.h"
+
+class GrCoordTransform;
+class GrGLSLFragmentProcessor;
+class GrInvariantOutput;
+class GrPipeline;
+class GrProcessorKeyBuilder;
+class GrShaderCaps;
+class GrSwizzle;
+
+/** Provides custom fragment shader code. Fragment processors receive an input color (vec4f) and
+    produce an output color. They may reference textures and uniforms. They may use
+    GrCoordTransforms to receive a transformation of the local coordinates that map from local space
+    to the fragment being processed.
+ */
+class GrFragmentProcessor : public GrResourceIOProcessor, public GrProgramElement {
+public:
+    /**
+    *  In many instances (e.g. SkShader::asFragmentProcessor() implementations) it is desirable to
+    *  only consider the input color's alpha. However, there is a competing desire to have reusable
+    *  GrFragmentProcessor subclasses that can be used in other scenarios where the entire input
+    *  color is considered. This function exists to filter the input color and pass it to a FP. It
+    *  does so by returning a parent FP that multiplies the passed in FPs output by the parent's
+    *  input alpha. The passed in FP will not receive an input color.
+    */
+    static sk_sp<GrFragmentProcessor> MulOutputByInputAlpha(sk_sp<GrFragmentProcessor>);
+
+    /**
+     *  This assumes that the input color to the returned processor will be unpremul and that the
+     *  passed processor (which becomes the returned processor's child) produces a premul output.
+     *  The result of the returned processor is a premul of its input color modulated by the child
+     *  processor's premul output.
+     */
+    static sk_sp<GrFragmentProcessor> MakeInputPremulAndMulByOutput(sk_sp<GrFragmentProcessor>);
+
+    /**
+     *  Returns a parent fragment processor that adopts the passed fragment processor as a child.
+     *  The parent will ignore its input color and instead feed the passed in color as input to the
+     *  child.
+     */
+    static sk_sp<GrFragmentProcessor> OverrideInput(sk_sp<GrFragmentProcessor>, GrColor4f);
+
+    /**
+     *  Returns a fragment processor that premuls the input before calling the passed in fragment
+     *  processor.
+     */
+    static sk_sp<GrFragmentProcessor> PremulInput(sk_sp<GrFragmentProcessor>);
+
+    /**
+     *  Returns a fragment processor that calls the passed in fragment processor, and then premuls
+     *  the output.
+     */
+    static sk_sp<GrFragmentProcessor> PremulOutput(sk_sp<GrFragmentProcessor>);
+
+    /**
+     *  Returns a fragment processor that calls the passed in fragment processor, and then unpremuls
+     *  the output.
+     */
+    static sk_sp<GrFragmentProcessor> UnpremulOutput(sk_sp<GrFragmentProcessor>);
+
+    /**
+     *  Returns a fragment processor that calls the passed in fragment processor, and then swizzles
+     *  the output.
+     */
+    static sk_sp<GrFragmentProcessor> SwizzleOutput(sk_sp<GrFragmentProcessor>, const GrSwizzle&);
+
+    /**
+     * Returns a fragment processor that runs the passed in array of fragment processors in a
+     * series. The original input is passed to the first, the first's output is passed to the
+     * second, etc. The output of the returned processor is the output of the last processor of the
+     * series.
+     *
+     * The array elements with be moved.
+     */
+    static sk_sp<GrFragmentProcessor> RunInSeries(sk_sp<GrFragmentProcessor>*, int cnt);
+
+    ~GrFragmentProcessor() override;
+
+    GrGLSLFragmentProcessor* createGLSLInstance() const;
+
+    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
+        this->onGetGLSLProcessorKey(caps, b);
+        for (int i = 0; i < fChildProcessors.count(); ++i) {
+            fChildProcessors[i]->getGLSLProcessorKey(caps, b);
+        }
+    }
+
+    int numCoordTransforms() const { return fCoordTransforms.count(); }
+
+    /** Returns the coordinate transformation at index. index must be valid according to
+        numTransforms(). */
+    const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
+
+    const SkTArray<const GrCoordTransform*, true>& coordTransforms() const {
+        return fCoordTransforms;
+    }
+
+    int numChildProcessors() const { return fChildProcessors.count(); }
+
+    const GrFragmentProcessor& childProcessor(int index) const { return *fChildProcessors[index]; }
+
+    bool instantiate(GrResourceProvider*) const;
+
+    /** Do any of the coordtransforms for this processor require local coords? */
+    bool usesLocalCoords() const { return SkToBool(fFlags & kUsesLocalCoords_Flag); }
+
+    /**
+     * A GrDrawOp may premultiply its antialiasing coverage into its GrGeometryProcessor's color
+     * output under the following scenario:
+     *   * all the color fragment processors report true to this query,
+     *   * all the coverage fragment processors report true to this query,
+     *   * the blend mode arithmetic allows for it it.
+     * To be compatible a fragment processor's output must be a modulation of its input color or
+     * alpha with a computed premultiplied color or alpha that is in 0..1 range. The computed color
+     * or alpha that is modulated against the input cannot depend on the input's alpha. The computed
+     * value cannot depend on the input's color channels unless it unpremultiplies the input color
+     * channels by the input alpha.
+     */
+    bool compatibleWithCoverageAsAlpha() const {
+        return SkToBool(fFlags & kCompatibleWithCoverageAsAlpha_OptimizationFlag);
+    }
+
+    /**
+     * If this is true then all opaque input colors to the processor produce opaque output colors.
+     */
+    bool preservesOpaqueInput() const {
+        return SkToBool(fFlags & kPreservesOpaqueInput_OptimizationFlag);
+    }
+
+    /**
+     * Tests whether given a constant input color the processor produces a constant output color
+     * (for all fragments). If true outputColor will contain the constant color produces for
+     * inputColor.
+     */
+    bool hasConstantOutputForConstantInput(GrColor4f inputColor, GrColor4f* outputColor) const {
+        if (fFlags & kConstantOutputForConstantInput_OptimizationFlag) {
+            *outputColor = this->constantOutputForConstantInput(inputColor);
+            return true;
+        }
+        return false;
+    }
+    bool hasConstantOutputForConstantInput() const {
+        return SkToBool(fFlags & kConstantOutputForConstantInput_OptimizationFlag);
+    }
+
+    /** Returns true if this and other processor conservatively draw identically. It can only return
+        true when the two processor are of the same subclass (i.e. they return the same object from
+        from getFactory()).
+
+        A return value of true from isEqual() should not be used to test whether the processor would
+        generate the same shader code. To test for identical code generation use getGLSLProcessorKey
+     */
+    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);
+        const GrFragmentProcessor* next();
+
+    private:
+        SkSTArray<4, const GrFragmentProcessor*, true> fFPStack;
+    };
+
+    /**
+     * 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, typename BASE,
+              int (BASE::*COUNT)() const,
+              const T& (BASE::*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;
+    };
+
+    using CoordTransformIter = FPItemIter<GrCoordTransform,
+                                          GrFragmentProcessor,
+                                          &GrFragmentProcessor::numCoordTransforms,
+                                          &GrFragmentProcessor::coordTransform>;
+
+    using TextureAccessIter = FPItemIter<TextureSampler,
+                                         GrResourceIOProcessor,
+                                         &GrResourceIOProcessor::numTextureSamplers,
+                                         &GrResourceIOProcessor::textureSampler>;
+
+protected:
+    enum OptimizationFlags : uint32_t {
+        kNone_OptimizationFlags,
+        kCompatibleWithCoverageAsAlpha_OptimizationFlag = 0x1,
+        kPreservesOpaqueInput_OptimizationFlag = 0x2,
+        kConstantOutputForConstantInput_OptimizationFlag = 0x4,
+        kAll_OptimizationFlags = kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+                                 kPreservesOpaqueInput_OptimizationFlag |
+                                 kConstantOutputForConstantInput_OptimizationFlag
+    };
+    GR_DECL_BITFIELD_OPS_FRIENDS(OptimizationFlags)
+
+    GrFragmentProcessor(OptimizationFlags optimizationFlags) : fFlags(optimizationFlags) {
+        SkASSERT((fFlags & ~kAll_OptimizationFlags) == 0);
+    }
+
+    OptimizationFlags optimizationFlags() const {
+        return static_cast<OptimizationFlags>(kAll_OptimizationFlags & fFlags);
+    }
+
+    /**
+     * This allows one subclass to access another subclass's implementation of
+     * constantOutputForConstantInput. It must only be called when
+     * hasConstantOutputForConstantInput() is known to be true.
+     */
+    static GrColor4f ConstantOutputForConstantInput(const GrFragmentProcessor& fp,
+                                                    GrColor4f input) {
+        SkASSERT(fp.hasConstantOutputForConstantInput());
+        return fp.constantOutputForConstantInput(input);
+    }
+
+    /**
+     * Fragment Processor subclasses call this from their constructor to register coordinate
+     * transformations. Coord transforms provide a mechanism for a processor to receive coordinates
+     * in their FS code. The matrix expresses a transformation from local space. For a given
+     * fragment the matrix will be applied to the local coordinate that maps to the fragment.
+     *
+     * When the transformation has perspective, the transformed coordinates will have
+     * 3 components. Otherwise they'll have 2.
+     *
+     * This must only be called from the constructor because GrProcessors are immutable. The
+     * processor subclass manages the lifetime of the transformations (this function only stores a
+     * pointer). The GrCoordTransform is typically a member field of the GrProcessor subclass.
+     *
+     * A processor subclass that has multiple methods of construction should always add its coord
+     * transforms in a consistent order. The non-virtual implementation of isEqual() automatically
+     * compares transforms and will assume they line up across the two processor instances.
+     */
+    void addCoordTransform(const GrCoordTransform*);
+
+    /**
+     * FragmentProcessor subclasses call this from their constructor to register any child
+     * FragmentProcessors they have. This must be called AFTER all texture accesses and coord
+     * transforms have been added.
+     * This is for processors whose shader code will be composed of nested processors whose output
+     * colors will be combined somehow to produce its output color.  Registering these child
+     * processors will allow the ProgramBuilder to automatically handle their transformed coords and
+     * texture accesses and mangle their uniform and output color names.
+     */
+    int registerChildProcessor(sk_sp<GrFragmentProcessor> child);
+
+private:
+    void addPendingIOs() const override { GrResourceIOProcessor::addPendingIOs(); }
+    void removeRefs() const override { GrResourceIOProcessor::removeRefs(); }
+    void pendingIOComplete() const override { GrResourceIOProcessor::pendingIOComplete(); }
+
+    void notifyRefCntIsZero() const final;
+
+    virtual GrColor4f constantOutputForConstantInput(GrColor4f /* inputColor */) const {
+        SkFAIL("Subclass must override this if advertising this optimization.");
+        return GrColor4f::TransparentBlack();
+    }
+
+    /** Returns a new instance of the appropriate *GL* implementation class
+        for the given GrFragmentProcessor; caller is responsible for deleting
+        the object. */
+    virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const = 0;
+
+    /** Implemented using GLFragmentProcessor::GenKey as described in this class's comment. */
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const = 0;
+
+    /**
+     * Subclass implements this to support isEqual(). It will only be called if it is known that
+     * the two processors are of the same subclass (i.e. they return the same object from
+     * getFactory()). The processor subclass should not compare its coord transforms as that will
+     * be performed automatically in the non-virtual isEqual().
+     */
+    virtual bool onIsEqual(const GrFragmentProcessor&) const = 0;
+
+    bool hasSameTransforms(const GrFragmentProcessor&) const;
+
+    enum PrivateFlags {
+        kFirstPrivateFlag = kAll_OptimizationFlags + 1,
+        kUsesLocalCoords_Flag = kFirstPrivateFlag,
+    };
+
+    mutable uint32_t fFlags = 0;
+
+    SkSTArray<4, const GrCoordTransform*, true> fCoordTransforms;
+
+    /**
+     * This is not SkSTArray<1, sk_sp<GrFragmentProcessor>> because this class holds strong
+     * references until notifyRefCntIsZero and then it holds pending executions.
+     */
+    SkSTArray<1, GrFragmentProcessor*, true> fChildProcessors;
+
+    typedef GrResourceIOProcessor INHERITED;
+};
+
+GR_MAKE_BITFIELD_OPS(GrFragmentProcessor::OptimizationFlags)
+
+#endif
diff --git a/src/gpu/GrGeometryProcessor.h b/src/gpu/GrGeometryProcessor.h
index 2711b9a..086d9ba 100644
--- a/src/gpu/GrGeometryProcessor.h
+++ b/src/gpu/GrGeometryProcessor.h
@@ -40,22 +40,6 @@
     }
 
 protected:
-    /**
-     * Subclasses call this from their constructor to register vertex attributes.  Attributes
-     * will be padded to the nearest 4 bytes for performance reasons.
-     * TODO After deferred geometry, we should do all of this inline in GenerateGeometry alongside
-     * the struct used to actually populate the attributes.  This is all extremely fragile, vertex
-     * attributes have to be added in the order they will appear in the struct which maps memory.
-     * The processor key should reflect the vertex attributes, or there lack thereof in the
-     * GrGeometryProcessor.
-     */
-    const Attribute& addVertexAttrib(const char* name, GrVertexAttribType type,
-                                     GrSLPrecision precision = kDefault_GrSLPrecision) {
-        fAttribs.emplace_back(name, type, precision);
-        fVertexStride += fAttribs.back().fOffset;
-        return fAttribs.back();
-    }
-
     void setWillUseGeoShader() { fWillUseGeoShader = true; }
 
     /**
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 40988c3..e9f4f93 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -8,6 +8,7 @@
 
 #include "GrGpu.h"
 
+#include "GrBackendSurface.h"
 #include "GrBuffer.h"
 #include "GrCaps.h"
 #include "GrContext.h"
@@ -24,24 +25,6 @@
 #include "GrTexturePriv.h"
 #include "SkMathPriv.h"
 
-GrMesh& GrMesh::operator =(const GrMesh& di) {
-    fPrimitiveType  = di.fPrimitiveType;
-    fStartVertex    = di.fStartVertex;
-    fStartIndex     = di.fStartIndex;
-    fVertexCount    = di.fVertexCount;
-    fIndexCount     = di.fIndexCount;
-
-    fInstanceCount          = di.fInstanceCount;
-    fVerticesPerInstance    = di.fVerticesPerInstance;
-    fIndicesPerInstance     = di.fIndicesPerInstance;
-    fMaxInstancesPerDraw    = di.fMaxInstancesPerDraw;
-
-    fVertexBuffer.reset(di.vertexBuffer());
-    fIndexBuffer.reset(di.indexBuffer());
-
-    return *this;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 GrGpu::GrGpu(GrContext* context)
@@ -143,8 +126,8 @@
     return true;
 }
 
-GrTexture* GrGpu::createTexture(const GrSurfaceDesc& origDesc, SkBudgeted budgeted,
-                                const SkTArray<GrMipLevel>& texels) {
+sk_sp<GrTexture> GrGpu::createTexture(const GrSurfaceDesc& origDesc, SkBudgeted budgeted,
+                                      const SkTArray<GrMipLevel>& texels) {
     GrSurfaceDesc desc = origDesc;
 
     const GrCaps* caps = this->caps();
@@ -155,29 +138,17 @@
     }
 
     desc.fSampleCnt = SkTMin(desc.fSampleCnt, caps->maxSampleCount());
-    // Attempt to catch un- or wrongly intialized sample counts;
+    // Attempt to catch un- or wrongly initialized sample counts.
     SkASSERT(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
 
     desc.fOrigin = resolve_origin(desc.fOrigin, isRT);
 
-    GrTexture* tex = nullptr;
-
-    if (GrPixelConfigIsCompressed(desc.fConfig)) {
-        // We shouldn't be rendering into this
-        SkASSERT(!isRT);
-        SkASSERT(0 == desc.fSampleCnt);
-
-        if (!caps->npotTextureTileSupport() &&
-            (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
-            return nullptr;
-        }
-
-        this->handleDirtyContext();
-        tex = this->onCreateCompressedTexture(desc, budgeted, texels);
-    } else {
-        this->handleDirtyContext();
-        tex = this->onCreateTexture(desc, budgeted, texels);
+    if (texels.count() && (desc.fFlags & kPerformInitialClear_GrSurfaceFlag)) {
+        return nullptr;
     }
+
+    this->handleDirtyContext();
+    sk_sp<GrTexture> tex = this->onCreateTexture(desc, budgeted, texels);
     if (tex) {
         if (!caps->reuseScratchTextures() && !isRT) {
             tex->resourcePriv().removeScratchKey();
@@ -188,65 +159,64 @@
                 fStats.incTextureUploads();
             }
         }
-        // This is a current work around to get discards into newly created textures. Once we are in
-        // MDB world, we should remove this code a rely on the draw target having specified load
-        // operations.
-        if (isRT && texels.empty()) {
-            GrRenderTarget* rt = tex->asRenderTarget();
-            SkASSERT(rt);
-            rt->discard();
+    }
+    return tex;
+}
+
+sk_sp<GrTexture> GrGpu::wrapBackendTexture(const GrBackendTexture& backendTex,
+                                           GrSurfaceOrigin origin,
+                                           GrBackendTextureFlags flags,
+                                           int sampleCnt,
+                                           GrWrapOwnership ownership) {
+    this->handleDirtyContext();
+    if (!this->caps()->isConfigTexturable(backendTex.config())) {
+        return nullptr;
+    }
+    if ((flags & kRenderTarget_GrBackendTextureFlag) &&
+        !this->caps()->isConfigRenderable(backendTex.config(), sampleCnt > 0)) {
+        return nullptr;
+    }
+    int maxSize = this->caps()->maxTextureSize();
+    if (backendTex.width() > maxSize || backendTex.height() > maxSize) {
+        return nullptr;
+    }
+    sk_sp<GrTexture> tex = this->onWrapBackendTexture(backendTex, origin, flags, sampleCnt,
+                                                      ownership);
+    if (!tex) {
+        return nullptr;
+    }
+
+    if (!this->caps()->avoidStencilBuffers()) {
+        // TODO: defer this and attach dynamically
+        GrRenderTarget* tgt = tex->asRenderTarget();
+        if (tgt && !fContext->resourceProvider()->attachStencilAttachment(tgt)) {
+            return nullptr;
         }
     }
     return tex;
 }
 
-sk_sp<GrTexture> GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc,
-                                           GrWrapOwnership ownership) {
-    this->handleDirtyContext();
-    if (!this->caps()->isConfigTexturable(desc.fConfig)) {
+sk_sp<GrRenderTarget> GrGpu::wrapBackendRenderTarget(const GrBackendRenderTarget& backendRT,
+                                                     GrSurfaceOrigin origin) {
+    if (!this->caps()->isConfigRenderable(backendRT.config(), backendRT.sampleCnt() > 0)) {
         return nullptr;
     }
-    if ((desc.fFlags & kRenderTarget_GrBackendTextureFlag) &&
-        !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
+    this->handleDirtyContext();
+    return this->onWrapBackendRenderTarget(backendRT, origin);
+}
+
+sk_sp<GrRenderTarget> GrGpu::wrapBackendTextureAsRenderTarget(const GrBackendTexture& tex,
+                                                              GrSurfaceOrigin origin,
+                                                              int sampleCnt) {
+    this->handleDirtyContext();
+    if (!this->caps()->isConfigRenderable(tex.config(), sampleCnt > 0)) {
         return nullptr;
     }
     int maxSize = this->caps()->maxTextureSize();
-    if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
+    if (tex.width() > maxSize || tex.height() > maxSize) {
         return nullptr;
     }
-    sk_sp<GrTexture> tex = this->onWrapBackendTexture(desc, ownership);
-    if (!tex) {
-        return nullptr;
-    }
-    // TODO: defer this and attach dynamically
-    GrRenderTarget* tgt = tex->asRenderTarget();
-    if (tgt && !fContext->resourceProvider()->attachStencilAttachment(tgt)) {
-        return nullptr;
-    }
-    return tex;
-}
-
-sk_sp<GrRenderTarget> GrGpu::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
-    if (!this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
-        return nullptr;
-    }
-    this->handleDirtyContext();
-    return this->onWrapBackendRenderTarget(desc);
-}
-
-sk_sp<GrRenderTarget> GrGpu::wrapBackendTextureAsRenderTarget(const GrBackendTextureDesc& desc) {
-    this->handleDirtyContext();
-    if (!(desc.fFlags & kRenderTarget_GrBackendTextureFlag)) {
-      return nullptr;
-    }
-    if (!this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
-        return nullptr;
-    }
-    int maxSize = this->caps()->maxTextureSize();
-    if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
-        return nullptr;
-    }
-    return this->onWrapBackendTextureAsRenderTarget(desc);
+    return this->onWrapBackendTextureAsRenderTarget(tex, origin, sampleCnt);
 }
 
 GrBuffer* GrGpu::createBuffer(size_t size, GrBufferType intendedType,
@@ -259,6 +229,11 @@
     return buffer;
 }
 
+std::unique_ptr<gr_instanced::OpAllocator> GrGpu::createInstancedRenderingAllocator() {
+    SkASSERT(GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport());
+    return this->onCreateInstancedRenderingAllocator();
+}
+
 gr_instanced::InstancedRendering* GrGpu::createInstancedRendering() {
     SkASSERT(GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport());
     return this->onCreateInstancedRendering();
@@ -274,9 +249,6 @@
     if (GrPixelConfigIsSint(dst->config()) != GrPixelConfigIsSint(src->config())) {
         return false;
     }
-    if (GrPixelConfigIsCompressed(dst->config())) {
-        return false;
-    }
     return this->onCopySurface(dst, src, srcRect, dstPoint);
 }
 
@@ -288,23 +260,13 @@
     SkASSERT(srcSurface);
     SkASSERT(kGpuPrefersDraw_DrawPreference != *drawPreference);
 
-    // We don't allow conversion between integer configs and float/fixed configs.
-    if (GrPixelConfigIsSint(srcSurface->config()) != GrPixelConfigIsSint(readConfig)) {
-        return false;
-    }
-
-    // We currently do not support reading into a compressed buffer
-    if (GrPixelConfigIsCompressed(readConfig)) {
-        return false;
-    }
-
     // We currently do not support reading into the packed formats 565 or 4444 as they are not
     // required to have read back support on all devices and backends.
     if (kRGB_565_GrPixelConfig == readConfig || kRGBA_4444_GrPixelConfig == readConfig) {
         return false;
     }
 
-    if (!this->onGetReadPixelsInfo(srcSurface, width, height, rowBytes, readConfig, drawPreference,
+   if (!this->onGetReadPixelsInfo(srcSurface, width, height, rowBytes, readConfig, drawPreference,
                                    tempDrawInfo)) {
         return false;
     }
@@ -329,16 +291,6 @@
     SkASSERT(dstSurface);
     SkASSERT(kGpuPrefersDraw_DrawPreference != *drawPreference);
 
-    if (GrPixelConfigIsCompressed(dstSurface->desc().fConfig) &&
-        dstSurface->desc().fConfig != srcConfig) {
-        return false;
-    }
-
-    // We don't allow conversion between integer configs and float/fixed configs.
-    if (GrPixelConfigIsSint(dstSurface->config()) != GrPixelConfigIsSint(srcConfig)) {
-        return false;
-    }
-
     if (SkToBool(dstSurface->asRenderTarget())) {
         if (this->caps()->useDrawInsteadOfAllRenderTargetWrites()) {
             ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
@@ -377,11 +329,6 @@
         return false;
     }
 
-    // We cannot read pixels into a compressed buffer
-    if (GrPixelConfigIsCompressed(config)) {
-        return false;
-    }
-
     size_t bpp = GrBytesPerPixel(config);
     if (!GrSurfacePriv::AdjustReadPixelParams(surface->width(), surface->height(), bpp,
                                               &left, &top, &width, &height,
@@ -402,6 +349,7 @@
                         int left, int top, int width, int height,
                         GrPixelConfig config, const SkTArray<GrMipLevel>& texels) {
     SkASSERT(surface);
+
     for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
         if (!texels[currentMipLevel].fPixels ) {
             return false;
@@ -487,7 +435,7 @@
 
 const GrGpu::MultisampleSpecs& GrGpu::queryMultisampleSpecs(const GrPipeline& pipeline) {
     GrRenderTarget* rt = pipeline.getRenderTarget();
-    SkASSERT(rt->desc().fSampleCnt > 1);
+    SkASSERT(rt->numStencilSamples() > 1);
 
     GrStencilSettings stencil;
     if (pipeline.isStencilEnabled()) {
@@ -500,7 +448,7 @@
     int effectiveSampleCnt;
     SkSTArray<16, SkPoint, true> pattern;
     this->onQueryMultisampleSpecs(rt, stencil, &effectiveSampleCnt, &pattern);
-    SkASSERT(effectiveSampleCnt >= rt->desc().fSampleCnt);
+    SkASSERT(effectiveSampleCnt >= rt->numStencilSamples());
 
     uint8_t id;
     if (this->caps()->sampleLocationsSupport()) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index df8cb08..8b8c7b3 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -15,16 +15,17 @@
 #include "GrTextureProducer.h"
 #include "GrTypes.h"
 #include "GrXferProcessor.h"
+#include "instanced/InstancedRendering.h"
 #include "SkPath.h"
 #include "SkTArray.h"
 #include <map>
 
+class GrBackendRenderTarget;
 class GrBuffer;
 class GrContext;
 struct GrContextOptions;
 class GrGLContext;
 class GrMesh;
-class GrNonInstancedVertices;
 class GrPath;
 class GrPathRange;
 class GrPathRenderer;
@@ -39,7 +40,11 @@
 class GrSurface;
 class GrTexture;
 
-namespace gr_instanced { class InstancedRendering; }
+namespace gr_instanced {
+    class InstancedOp;
+    class InstancedRendering;
+    class OpAllocator;
+}
 
 class GrGpu : public SkRefCnt {
 public:
@@ -95,25 +100,25 @@
      * @param budgeted    does this texture count against the resource cache budget?
      * @param texels      array of mipmap levels containing texel data to load.
      *                    Each level begins with full-size palette data for paletted textures.
-     *                    For compressed formats the level contains the compressed pixel data.
-     *                    Otherwise, it contains width*height texels. If there is only one
+     *                    It contains width*height texels. If there is only one
      *                    element and it contains nullptr fPixels, texture data is
      *                    uninitialized.
      * @return    The texture object if successful, otherwise nullptr.
      */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                             const SkTArray<GrMipLevel>& texels);
+    sk_sp<GrTexture> createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                   const SkTArray<GrMipLevel>& texels);
 
     /**
      * Simplified createTexture() interface for when there is no initial texel data to upload.
      */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted) {
+    sk_sp<GrTexture> createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted) {
         return this->createTexture(desc, budgeted, SkTArray<GrMipLevel>());
     }
 
     /** Simplified createTexture() interface for when there is only a base level */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, const void* level0Data,
-                             size_t rowBytes) {
+    sk_sp<GrTexture> createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                   const void* level0Data,
+                                   size_t rowBytes) {
         SkASSERT(level0Data);
         GrMipLevel level = { level0Data, rowBytes };
         SkSTArray<1, GrMipLevel> array;
@@ -124,17 +129,20 @@
     /**
      * Implements GrResourceProvider::wrapBackendTexture
      */
-    sk_sp<GrTexture> wrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership);
+    sk_sp<GrTexture> wrapBackendTexture(const GrBackendTexture&, GrSurfaceOrigin,
+                                        GrBackendTextureFlags, int sampleCnt, GrWrapOwnership);
 
     /**
      * Implements GrResourceProvider::wrapBackendRenderTarget
      */
-    sk_sp<GrRenderTarget> wrapBackendRenderTarget(const GrBackendRenderTargetDesc&);
+    sk_sp<GrRenderTarget> wrapBackendRenderTarget(const GrBackendRenderTarget&, GrSurfaceOrigin);
 
     /**
      * Implements GrResourceProvider::wrapBackendTextureAsRenderTarget
      */
-    sk_sp<GrRenderTarget> wrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&);
+    sk_sp<GrRenderTarget> wrapBackendTextureAsRenderTarget(const GrBackendTexture&,
+                                                           GrSurfaceOrigin,
+                                                           int sampleCnt);
 
     /**
      * Creates a buffer in GPU memory. For a client-side buffer use GrBuffer::CreateCPUBacked.
@@ -152,6 +160,7 @@
     /**
      * Creates an instanced rendering object if it is supported on this platform.
      */
+    std::unique_ptr<gr_instanced::OpAllocator> createInstancedRenderingAllocator();
     gr_instanced::InstancedRendering* createInstancedRendering();
 
     /**
@@ -367,21 +376,24 @@
             const GrGpuCommandBuffer::LoadAndStoreInfo& colorInfo,
             const GrGpuCommandBuffer::LoadAndStoreInfo& stencilInfo) = 0;
 
-    // Called by GrOpList when flushing.
+    // Called by GrDrawingManager when flushing.
     // Provides a hook for post-flush actions (e.g. Vulkan command buffer submits).
-    virtual void finishOpList() {}
+    virtual void finishFlush() {}
 
     virtual GrFence SK_WARN_UNUSED_RESULT insertFence() = 0;
     virtual bool waitFence(GrFence, uint64_t timeout = 1000) = 0;
     virtual void deleteFence(GrFence) const = 0;
 
     virtual sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() = 0;
-    virtual void insertSemaphore(sk_sp<GrSemaphore> semaphore) = 0;
+    virtual void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush = false) = 0;
     virtual void waitSemaphore(sk_sp<GrSemaphore> semaphore) = 0;
 
-    // Ensures that all queued up driver-level commands have been sent to the GPU. For example, on
-    // OpenGL, this calls glFlush.
-    virtual void flush() = 0;
+    /**
+     *  Put this texture in a safe and known state for use across multiple GrContexts. Depending on
+     *  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;
 
     ///////////////////////////////////////////////////////////////////////////
     // Debugging and Stats
@@ -467,9 +479,6 @@
     // clears target's entire stencil buffer to 0
     virtual void clearStencil(GrRenderTarget* target) = 0;
 
-    // draws an outline rectangle for debugging/visualization purposes.
-    virtual void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) = 0;
-
     // Determines whether a texture will need to be rescaled in order to be used with the
     // GrSamplerParams. This variation is called when the caller will create a new texture using the
     // resource provider from a non-texture src (cpu-backed image, ...).
@@ -534,21 +543,28 @@
 
     // overridden by backend-specific derived class to create objects.
     // Texture size and sample size will have already been validated in base class before
-    // onCreateTexture/CompressedTexture are called.
-    virtual GrTexture* onCreateTexture(const GrSurfaceDesc& desc,
-                                       SkBudgeted budgeted,
-                                       const SkTArray<GrMipLevel>& texels) = 0;
-    virtual GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc,
-                                                 SkBudgeted budgeted,
-                                                 const SkTArray<GrMipLevel>& texels) = 0;
+    // onCreateTexture is called.
+    virtual sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc,
+                                             SkBudgeted budgeted,
+                                             const SkTArray<GrMipLevel>& texels) = 0;
 
-    virtual sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) = 0;
-    virtual sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) = 0;
-    virtual sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&)=0;
+    virtual sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
+                                                  GrSurfaceOrigin,
+                                                  GrBackendTextureFlags,
+                                                  int sampleCnt,
+                                                  GrWrapOwnership) = 0;
+    virtual sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTarget&,
+                                                            GrSurfaceOrigin) = 0;
+    virtual sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTexture&,
+                                                                     GrSurfaceOrigin,
+                                                                     int sampleCnt)=0;
     virtual GrBuffer* onCreateBuffer(size_t size, GrBufferType intendedType, GrAccessPattern,
                                      const void* data) = 0;
 
     virtual gr_instanced::InstancedRendering* onCreateInstancedRendering() = 0;
+    virtual std::unique_ptr<gr_instanced::OpAllocator> onCreateInstancedRenderingAllocator() {
+        return nullptr;
+    }
 
     virtual bool onIsACopyNeededForTextureParams(GrTextureProxy* proxy, const GrSamplerParams&,
                                                  GrTextureProducer::CopyParams*,
@@ -616,7 +632,7 @@
     GrContext*                             fContext;
 
     friend class GrPathRendering;
-    friend class gr_instanced::InstancedRendering;
+    friend class gr_instanced::InstancedOp; // for xferBarrier
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/GrGpuCommandBuffer.cpp b/src/gpu/GrGpuCommandBuffer.cpp
index 4eb9843..5570a5a 100644
--- a/src/gpu/GrGpuCommandBuffer.cpp
+++ b/src/gpu/GrGpuCommandBuffer.cpp
@@ -7,9 +7,11 @@
 
 #include "GrGpuCommandBuffer.h"
 
+#include "GrContext.h"
 #include "GrCaps.h"
 #include "GrFixedClip.h"
 #include "GrGpu.h"
+#include "GrMesh.h"
 #include "GrPrimitiveProcessor.h"
 #include "GrRenderTarget.h"
 #include "SkRect.h"
@@ -36,15 +38,27 @@
 
 bool GrGpuCommandBuffer::draw(const GrPipeline& pipeline,
                               const GrPrimitiveProcessor& primProc,
-                              const GrMesh* mesh,
+                              const GrMesh meshes[],
                               int meshCount,
                               const SkRect& bounds) {
+#ifdef SK_DEBUG
+    SkASSERT(!primProc.hasInstanceAttribs() || this->gpu()->caps()->instanceAttribSupport());
+    for (int i = 0; i < meshCount; ++i) {
+        SkASSERT(primProc.hasVertexAttribs() == meshes[i].hasVertexData());
+        SkASSERT(primProc.hasInstanceAttribs() == meshes[i].isInstanced());
+    }
+#endif
+
+    if (pipeline.isBad() || !primProc.instantiate(this->gpu()->getContext()->resourceProvider())) {
+        return false;
+    }
+
     SkASSERT(pipeline.isInitialized());
     if (primProc.numAttribs() > this->gpu()->caps()->maxVertexAttributes()) {
         this->gpu()->stats()->incNumFailedDraws();
         return false;
     }
-    this->onDraw(pipeline, primProc, mesh, meshCount, bounds);
+    this->onDraw(pipeline, primProc, meshes, meshCount, bounds);
     return true;
 }
 
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index 23a300c..fb5dc00 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -68,7 +68,7 @@
     // number of vertex attributes is too large).
     bool draw(const GrPipeline&,
               const GrPrimitiveProcessor&,
-              const GrMesh*,
+              const GrMesh[],
               int meshCount,
               const SkRect& bounds);
 
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index dcc4e62..df8e72c 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -43,17 +43,6 @@
     get_resource_cache(fGpu)->resourceAccess().insertResource(this);
 }
 
-void GrGpuResource::detachFromCache() {
-    if (this->wasDestroyed()) {
-        return;
-    }
-    if (fUniqueKey.isValid()) {
-        this->removeUniqueKey();
-    }
-    this->removeScratchKey();
-    this->makeUnbudgeted();
-}
-
 GrGpuResource::~GrGpuResource() {
     // The cache should have released or destroyed this resource.
     SkASSERT(this->wasDestroyed());
@@ -134,8 +123,11 @@
     SkASSERT(this->internalHasRef());
     SkASSERT(key.isValid());
 
-    // Wrapped and uncached resources can never have a unique key.
-    if (SkBudgeted::kNo == this->resourcePriv().isBudgeted()) {
+    // Uncached resources can never have a unique key, unless they're wrapped resources. Wrapped
+    // resources are a special case: the unique keys give us a weak ref so that we can reuse the
+    // same resource (rather than re-wrapping). When a wrapped resource is no longer referenced,
+    // it will always be released - it is never converted to a scratch resource.
+    if (SkBudgeted::kNo == this->resourcePriv().isBudgeted() && !this->fRefsWrappedObjects) {
         return;
     }
 
@@ -169,8 +161,7 @@
     }
 
     GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
-    uint32_t flags =
-        GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag;
+    uint32_t flags = GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag;
     if (!this->internalHasPendingIO()) {
         flags |= GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
     }
diff --git a/src/gpu/GrGpuResourcePriv.h b/src/gpu/GrGpuResourcePriv.h
index 82bf072..a1e207d 100644
--- a/src/gpu/GrGpuResourcePriv.h
+++ b/src/gpu/GrGpuResourcePriv.h
@@ -45,7 +45,7 @@
      */
     SkBudgeted isBudgeted() const {
         bool ret = SkBudgeted::kYes == fResource->fBudgeted;
-        SkASSERT(ret || !fResource->getUniqueKey().isValid());
+        SkASSERT(ret || !fResource->getUniqueKey().isValid() || fResource->fRefsWrappedObjects);
         return SkBudgeted(ret);
     }
 
diff --git a/src/gpu/GrGpuResourceRef.cpp b/src/gpu/GrGpuResourceRef.cpp
index 405679d..115e0b1 100644
--- a/src/gpu/GrGpuResourceRef.cpp
+++ b/src/gpu/GrGpuResourceRef.cpp
@@ -66,10 +66,13 @@
 }
 
 void GrGpuResourceRef::markPendingIO() const {
+    if (!fResource) {
+        return;
+    }
+
     // This should only be called when the owning GrProgramElement gets its first
     // pendingExecution ref.
     SkASSERT(!fPendingIO);
-    SkASSERT(fResource);
     fPendingIO = true;
     switch (fIOType) {
         case kRead_GrIOType:
@@ -86,6 +89,10 @@
 }
 
 void GrGpuResourceRef::pendingIOComplete() const {
+    if (!fResource) {
+        return;
+    }
+
     // This should only be called when the owner's pending executions have ocurred but it is still
     // reffed.
     SkASSERT(fOwnRef);
@@ -107,11 +114,127 @@
 }
 
 void GrGpuResourceRef::removeRef() const {
+    if (!fResource) {
+        return;
+    }
+
     // This should only be called once, when the owners last ref goes away and
     // there is a pending execution.
     SkASSERT(fOwnRef);
     SkASSERT(fPendingIO);
-    SkASSERT(fResource);
     fResource->unref();
     fOwnRef = false;
 }
+
+///////////////////////////////////////////////////////////////////////////////
+#include "GrTextureProxy.h"
+
+GrSurfaceProxyRef::GrSurfaceProxyRef() {
+    fProxy = nullptr;
+    fOwnRef = false;
+    fPendingIO = false;
+}
+
+GrSurfaceProxyRef::GrSurfaceProxyRef(sk_sp<GrSurfaceProxy> proxy, GrIOType ioType) {
+    fProxy = nullptr;
+    fOwnRef = false;
+    fPendingIO = false;
+    this->setProxy(std::move(proxy), ioType);
+}
+
+GrSurfaceProxyRef::~GrSurfaceProxyRef() {
+    if (fOwnRef) {
+        SkASSERT(fProxy);
+        fProxy->unref();
+    }
+    if (fPendingIO) {
+        switch (fIOType) {
+            case kRead_GrIOType:
+                fProxy->completedRead();
+                break;
+            case kWrite_GrIOType:
+                fProxy->completedWrite();
+                break;
+            case kRW_GrIOType:
+                fProxy->completedRead();
+                fProxy->completedWrite();
+                break;
+        }
+    }
+}
+
+void GrSurfaceProxyRef::reset() {
+    SkASSERT(!fPendingIO);
+    SkASSERT(SkToBool(fProxy) == fOwnRef);
+    if (fOwnRef) {
+        fProxy->unref();
+        fOwnRef = false;
+        fProxy = nullptr;
+    }
+}
+
+void GrSurfaceProxyRef::setProxy(sk_sp<GrSurfaceProxy> proxy, GrIOType ioType) {
+    SkASSERT(!fPendingIO);
+    SkASSERT(SkToBool(fProxy) == fOwnRef);
+    SkSafeUnref(fProxy);
+    if (!proxy) {
+        fProxy = nullptr;
+        fOwnRef = false;
+    } else {
+        fProxy = proxy.release();   // due to the semantics of this class we unpack from sk_sp
+        fOwnRef = true;
+        fIOType = ioType;
+    }
+}
+
+void GrSurfaceProxyRef::markPendingIO() const {
+    // This should only be called when the owning GrProgramElement gets its first
+    // pendingExecution ref.
+    SkASSERT(!fPendingIO);
+    SkASSERT(fProxy);
+    fPendingIO = true;
+    switch (fIOType) {
+        case kRead_GrIOType:
+            fProxy->addPendingRead();
+            break;
+        case kWrite_GrIOType:
+            fProxy->addPendingWrite();
+            break;
+        case kRW_GrIOType:
+            fProxy->addPendingRead();
+            fProxy->addPendingWrite();
+            break;
+    }
+}
+
+void GrSurfaceProxyRef::pendingIOComplete() const {
+    // This should only be called when the owner's pending executions have ocurred but it is still
+    // reffed.
+    SkASSERT(fOwnRef);
+    SkASSERT(fPendingIO);
+    switch (fIOType) {
+        case kRead_GrIOType:
+            fProxy->completedRead();
+            break;
+        case kWrite_GrIOType:
+            fProxy->completedWrite();
+            break;
+        case kRW_GrIOType:
+            fProxy->completedRead();
+            fProxy->completedWrite();
+            break;
+
+    }
+    fPendingIO = false;
+}
+
+void GrSurfaceProxyRef::removeRef() const {
+    // This should only be called once, when the owners last ref goes away and
+    // there is a pending execution.
+    SkASSERT(fOwnRef);
+    SkASSERT(fPendingIO);
+    SkASSERT(fProxy);
+    fProxy->unref();
+    fOwnRef = false;
+}
+
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index a1a6053..bbdc9c1 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -14,26 +14,20 @@
 #include "SkImageCacherator.h"
 #include "SkPixelRef.h"
 
-static bool cacher_is_alpha_only(const SkImageCacherator& cacher) {
-    return kAlpha_8_SkColorType == cacher.info().colorType();
-}
-
-GrImageTextureMaker::GrImageTextureMaker(GrContext* context, SkImageCacherator* cacher,
-                                         const SkImage* client, SkImage::CachingHint chint)
-    : INHERITED(context, cacher->info().width(), cacher->info().height(),
-                cacher_is_alpha_only(*cacher))
-    , fCacher(cacher)
-    , fClient(client)
-    , fCachingHint(chint) {
-    if (client) {
-        GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
-                             SkIRect::MakeWH(this->width(), this->height()));
-    }
+GrImageTextureMaker::GrImageTextureMaker(GrContext* context, const SkImage* client,
+                                         SkImage::CachingHint chint)
+        : INHERITED(context, client->width(), client->height(), client->isAlphaOnly())
+        , fCacher(as_IB(client)->peekCacherator())
+        , fClient(client)
+        , fCachingHint(chint) {
+    SkASSERT(fCacher);
+    GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
+                         SkIRect::MakeWH(this->width(), this->height()));
 }
 
 sk_sp<GrTextureProxy> GrImageTextureMaker::refOriginalTextureProxy(bool willBeMipped,
                                                                    SkColorSpace* dstColorSpace) {
-    return fCacher->lockTextureProxy(this->context(), fOriginalKey, fClient, fCachingHint,
+    return fCacher->lockTextureProxy(this->context(), fOriginalKey, fCachingHint,
                                      willBeMipped, dstColorSpace);
 }
 
@@ -49,13 +43,11 @@
 }
 
 void GrImageTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) {
-    if (fClient) {
-        as_IB(fClient)->notifyAddedToCache();
-    }
+    as_IB(fClient)->notifyAddedToCache();
 }
 
 SkAlphaType GrImageTextureMaker::alphaType() const {
-    return fCacher->info().alphaType();
+    return fClient->alphaType();
 }
 sk_sp<SkColorSpace> GrImageTextureMaker::getColorSpace(SkColorSpace* dstColorSpace) {
     return fCacher->getColorSpace(this->context(), dstColorSpace);
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index 5145903..4f3e8bd 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -17,8 +17,7 @@
     is kAllow the image's ID is used for the cache key. */
 class GrImageTextureMaker : public GrTextureMaker {
 public:
-    GrImageTextureMaker(GrContext* context, SkImageCacherator* cacher, const SkImage* client,
-                        SkImage::CachingHint chint);
+    GrImageTextureMaker(GrContext* context, const SkImage* client, SkImage::CachingHint chint);
 
 protected:
     // TODO: consider overriding this, for the case where the underlying generator might be
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp
index 91cecbd..32a3612 100644
--- a/src/gpu/GrMemoryPool.cpp
+++ b/src/gpu/GrMemoryPool.cpp
@@ -6,8 +6,10 @@
  */
 
 #include "GrMemoryPool.h"
-
 #include "SkMalloc.h"
+#ifdef SK_DEBUG
+#include "SkAtomics.h"
+#endif
 
 #ifdef SK_DEBUG
     #define VALIDATE this->validate()
@@ -36,6 +38,19 @@
 
 GrMemoryPool::~GrMemoryPool() {
     VALIDATE;
+#ifdef SK_DEBUG
+    int i = 0;
+    int n = fAllocatedIDs.count();
+    fAllocatedIDs.foreach([&i, n] (int32_t id) {
+        if (++i == 1) {
+            SkDebugf("Leaked IDs (in no particular order): %d", id);
+        } else if (i < 11) {
+            SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
+        } else if (i == 11) {
+            SkDebugf(", ...\n");
+        }
+    });
+#endif
     SkASSERT(0 == fAllocationCnt);
     SkASSERT(fHead == fTail);
     SkASSERT(0 == fHead->fLiveCount);
@@ -66,13 +81,15 @@
     // so that we can decrement the live count on delete in constant time.
     AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
     SkDEBUGCODE(allocData->fSentinal = kAssignedMarker);
+    SkDEBUGCODE(allocData->fID = []{static int32_t gID; return sk_atomic_inc(&gID) + 1;}());
+    // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
+    SkDEBUGCODE(fAllocatedIDs.add(allocData->fID));
     allocData->fHeader = fTail;
     ptr += kPerAllocPad;
     fTail->fPrevPtr = fTail->fCurrPtr;
     fTail->fCurrPtr += size;
     fTail->fFreeSize -= size;
     fTail->fLiveCount += 1;
-
     SkDEBUGCODE(++fAllocationCnt);
     VALIDATE;
     return reinterpret_cast<void*>(ptr);
@@ -84,6 +101,7 @@
     AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
     SkASSERT(kAssignedMarker == allocData->fSentinal);
     SkDEBUGCODE(allocData->fSentinal = kFreedMarker);
+    SkDEBUGCODE(fAllocatedIDs.remove(allocData->fID));
     BlockHeader* block = allocData->fHeader;
     SkASSERT(kAssignedMarker == block->fBlockSentinal);
     if (1 == block->fLiveCount) {
@@ -181,6 +199,7 @@
         prev = block;
     } while ((block = block->fNext));
     SkASSERT(allocCount == fAllocationCnt);
+    SkASSERT(fAllocationCnt == fAllocatedIDs.count());
     SkASSERT(prev == fTail);
     SkASSERT(fAllocBlockCnt != 0 || fSize == 0);
 #endif
diff --git a/src/gpu/GrMemoryPool.h b/src/gpu/GrMemoryPool.h
index e483aab..26a7634 100644
--- a/src/gpu/GrMemoryPool.h
+++ b/src/gpu/GrMemoryPool.h
@@ -9,6 +9,9 @@
 #define GrMemoryPool_DEFINED
 
 #include "GrTypes.h"
+#ifdef SK_DEBUG
+#include "SkTHash.h"
+#endif
 
 /**
  * Allocates memory in blocks and parcels out space in the blocks for allocation
@@ -94,6 +97,7 @@
     struct AllocHeader {
 #ifdef SK_DEBUG
         uint32_t fSentinal;      ///< known value to check for memory stomping (e.g., (CD)*)
+        int32_t fID;             ///< ID that can be used to track down leaks by clients.
 #endif
         BlockHeader* fHeader;    ///< pointer back to the block header in which an alloc resides
     };
@@ -105,6 +109,7 @@
 #ifdef SK_DEBUG
     int                               fAllocationCnt;
     int                               fAllocBlockCnt;
+    SkTHashSet<int32_t>               fAllocatedIDs;
 #endif
 
 protected:
diff --git a/src/gpu/GrMesh.h b/src/gpu/GrMesh.h
index 5d1ce6f..bdb62f5 100644
--- a/src/gpu/GrMesh.h
+++ b/src/gpu/GrMesh.h
@@ -11,28 +11,7 @@
 #include "GrBuffer.h"
 #include "GrGpuResourceRef.h"
 
-class GrNonInstancedMesh {
-public:
-    GrPrimitiveType primitiveType() const { return fPrimitiveType; }
-    int startVertex() const { return fStartVertex; }
-    int startIndex() const { return fStartIndex; }
-    int vertexCount() const { return fVertexCount; }
-    int indexCount() const { return fIndexCount; }
-    bool isIndexed() const { return fIndexCount > 0; }
-
-    const GrBuffer* vertexBuffer() const { return fVertexBuffer.get(); }
-    const GrBuffer* indexBuffer() const { return fIndexBuffer.get(); }
-
-protected:
-    GrPrimitiveType         fPrimitiveType;
-    int                     fStartVertex;
-    int                     fStartIndex;
-    int                     fVertexCount;
-    int                     fIndexCount;
-    GrPendingIOResource<const GrBuffer, kRead_GrIOType> fVertexBuffer;
-    GrPendingIOResource<const GrBuffer, kRead_GrIOType> fIndexBuffer;
-    friend class GrMesh;
-};
+class GrPrimitiveProcessor;
 
 /**
  * Used to communicate index and vertex buffers, counts, and offsets for a draw from GrOp to
@@ -40,140 +19,233 @@
  * and draw-issuing responsibility to GrPrimitiveProcessor. The rest of the vertex info lives there
  * already (stride, attribute mappings).
  */
-class GrMesh : public GrNonInstancedMesh {
+class GrMesh {
 public:
-    GrMesh() {}
-    GrMesh(const GrMesh& di) { (*this) = di; }
-    GrMesh& operator =(const GrMesh& di);
-
-    void init(GrPrimitiveType primType, const GrBuffer* vertexBuffer, int startVertex,
-                int vertexCount) {
-        SkASSERT(vertexBuffer);
-        SkASSERT(vertexCount);
-        SkASSERT(startVertex >= 0);
-        fPrimitiveType = primType;
-        fVertexBuffer.reset(vertexBuffer);
-        fIndexBuffer.reset(nullptr);
-        fStartVertex = startVertex;
-        fStartIndex = 0;
-        fVertexCount = vertexCount;
-        fIndexCount = 0;
-        fInstanceCount = 0;
-        fVerticesPerInstance = 0;
-        fIndicesPerInstance = 0;
-        fMaxInstancesPerDraw = 0;
+    GrMesh(GrPrimitiveType primitiveType)
+        : fPrimitiveType(primitiveType)
+        , fBaseVertex(0) {
+        SkDEBUGCODE(fNonIndexNonInstanceData.fVertexCount = -1;)
     }
 
-    void initIndexed(GrPrimitiveType primType,
-                        const GrBuffer* vertexBuffer,
-                        const GrBuffer* indexBuffer,
-                        int startVertex,
-                        int startIndex,
-                        int vertexCount,
-                        int indexCount) {
-        SkASSERT(indexBuffer);
-        SkASSERT(vertexBuffer);
-        SkASSERT(indexCount);
-        SkASSERT(vertexCount);
-        SkASSERT(startIndex >= 0);
-        SkASSERT(startVertex >= 0);
-        fPrimitiveType = primType;
-        fVertexBuffer.reset(vertexBuffer);
-        fIndexBuffer.reset(indexBuffer);
-        fStartVertex = startVertex;
-        fStartIndex = startIndex;
-        fVertexCount = vertexCount;
-        fIndexCount = indexCount;
-        fInstanceCount = 0;
-        fVerticesPerInstance = 0;
-        fIndicesPerInstance = 0;
-        fMaxInstancesPerDraw = 0;
-    }
+    GrPrimitiveType primitiveType() const { return fPrimitiveType; }
+    bool isIndexed() const { return SkToBool(fIndexBuffer.get()); }
+    bool isInstanced() const { return SkToBool(fInstanceBuffer.get()); }
+    bool hasVertexData() const { return SkToBool(fVertexBuffer.get()); }
 
+    void setNonIndexedNonInstanced(int vertexCount);
 
-    /** Variation of the above that may be used when the total number of instances may exceed
-        the number of instances supported by the index buffer. To be used with
-        nextInstances() to draw in max-sized batches.*/
-    void initInstanced(GrPrimitiveType primType,
-                        const GrBuffer* vertexBuffer,
-                        const GrBuffer* indexBuffer,
-                        int startVertex,
-                        int verticesPerInstance,
-                        int indicesPerInstance,
-                        int instanceCount,
-                        int maxInstancesPerDraw) {
-        SkASSERT(vertexBuffer);
-        SkASSERT(indexBuffer);
-        SkASSERT(instanceCount);
-        SkASSERT(verticesPerInstance);
-        SkASSERT(indicesPerInstance);
-        SkASSERT(startVertex >= 0);
-        fPrimitiveType = primType;
-        fVertexBuffer.reset(vertexBuffer);
-        fIndexBuffer.reset(indexBuffer);
-        fStartVertex = startVertex;
-        fStartIndex = 0;
-        fVerticesPerInstance = verticesPerInstance;
-        fIndicesPerInstance = indicesPerInstance;
-        fInstanceCount = instanceCount;
-        fVertexCount = instanceCount * fVerticesPerInstance;
-        fIndexCount = instanceCount * fIndicesPerInstance;
-        fMaxInstancesPerDraw = maxInstancesPerDraw;
-    }
+    void setIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                    uint16_t minIndexValue, uint16_t maxIndexValue);
+    void setIndexedPatterned(const GrBuffer* indexBuffer, int indexCount, int vertexCount,
+                             int patternRepeatCount, int maxPatternRepetitionsInIndexBuffer);
 
+    void setInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
+                      int vertexCount);
+    void setIndexedInstanced(const GrBuffer* indexBuffer, int indexCount,
+                             const GrBuffer* instanceBuffer, int instanceCount, int baseInstance=0);
 
-    /** These return 0 if initInstanced was not used to initialize the GrVertices. */
-    int verticesPerInstance() const { return fVerticesPerInstance; }
-    int indicesPerInstance() const { return fIndicesPerInstance; }
-    int instanceCount() const { return fInstanceCount; }
+    void setVertexData(const GrBuffer* vertexBuffer, int baseVertex = 0);
 
-    bool isInstanced() const { return fInstanceCount > 0; }
-
-    class Iterator {
+    class SendToGpuImpl {
     public:
-        const GrNonInstancedMesh* init(const GrMesh& mesh) {
-            fMesh = &mesh;
-            if (mesh.fInstanceCount <= mesh.fMaxInstancesPerDraw) {
-                fInstancesRemaining = 0;
-                // Note, this also covers the non-instanced case!
-                return &mesh;
-            }
-            SkASSERT(mesh.isInstanced());
-            fInstanceBatch.fIndexBuffer.reset(mesh.fIndexBuffer.get());
-            fInstanceBatch.fVertexBuffer.reset(mesh.fVertexBuffer.get());
-            fInstanceBatch.fIndexCount = mesh.fMaxInstancesPerDraw *
-                                         mesh.fIndicesPerInstance;
-            fInstanceBatch.fVertexCount = mesh.fMaxInstancesPerDraw *
-                                          mesh.fVerticesPerInstance;
-            fInstanceBatch.fPrimitiveType = mesh.fPrimitiveType;
-            fInstanceBatch.fStartIndex = mesh.fStartIndex;
-            fInstanceBatch.fStartVertex = mesh.fStartVertex;
-            fInstancesRemaining = mesh.fInstanceCount - mesh.fMaxInstancesPerDraw;
-            return &fInstanceBatch;
-        }
+        virtual void sendMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                   const GrBuffer* vertexBuffer, int vertexCount,
+                                   int baseVertex) = 0;
 
-        const GrNonInstancedMesh* next() {
-            if (!fInstancesRemaining) {
-                return nullptr;
-            }
-            fInstanceBatch.fStartVertex += fInstanceBatch.fVertexCount;
-            int instances = SkTMin(fInstancesRemaining, fMesh->fMaxInstancesPerDraw);
-            fInstanceBatch.fIndexCount = instances * fMesh->fIndicesPerInstance;
-            fInstanceBatch.fVertexCount = instances * fMesh->fVerticesPerInstance;
-            fInstancesRemaining -= instances;
-            return &fInstanceBatch;
-        }
-    private:
-        GrNonInstancedMesh fInstanceBatch;
-        const GrMesh* fMesh;
-        int fInstancesRemaining;
+        virtual void sendIndexedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                          const GrBuffer* indexBuffer, int indexCount,
+                                          int baseIndex, uint16_t minIndexValue,
+                                          uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
+                                          int baseVertex) = 0;
+
+        virtual void sendInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                            const GrBuffer* vertexBuffer, int vertexCount,
+                                            int baseVertex, const GrBuffer* instanceBuffer,
+                                            int instanceCount, int baseInstance) = 0;
+
+        virtual void sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                                   const GrBuffer* indexBuffer, int indexCount,
+                                                   int baseIndex, const GrBuffer* vertexBuffer,
+                                                   int baseVertex, const GrBuffer* instanceBuffer,
+                                                   int instanceCount, int baseInstance) = 0;
+
+        virtual ~SendToGpuImpl() {}
     };
 
+    void sendToGpu(const GrPrimitiveProcessor&, SendToGpuImpl*) const;
+
+    struct PatternBatch;
+
 private:
-    int                     fInstanceCount;
-    int                     fVerticesPerInstance;
-    int                     fIndicesPerInstance;
-    int                     fMaxInstancesPerDraw;
+    using PendingBuffer = GrPendingIOResource<const GrBuffer, kRead_GrIOType>;
+
+    GrPrimitiveType   fPrimitiveType;
+    PendingBuffer     fIndexBuffer;
+    PendingBuffer     fInstanceBuffer;
+    PendingBuffer     fVertexBuffer;
+    int               fBaseVertex;
+
+    union {
+        struct { // When fIndexBuffer == nullptr and fInstanceBuffer == nullptr.
+            int   fVertexCount;
+        } fNonIndexNonInstanceData;
+
+        struct { // When fIndexBuffer != nullptr and fInstanceBuffer == nullptr.
+            struct {
+                int   fIndexCount;
+                int   fPatternRepeatCount;
+            } fIndexData;
+
+            union {
+                struct { // When fPatternRepeatCount == 0.
+                    int        fBaseIndex;
+                    uint16_t   fMinIndexValue;
+                    uint16_t   fMaxIndexValue;
+                } fNonPatternIndexData;
+
+                struct { // When fPatternRepeatCount != 0.
+                    int   fVertexCount;
+                    int   fMaxPatternRepetitionsInIndexBuffer;
+                } fPatternData;
+            };
+        };
+
+        struct { // When fInstanceBuffer != nullptr.
+            struct {
+                int   fInstanceCount;
+                int   fBaseInstance;
+            } fInstanceData;
+
+            union { // When fIndexBuffer == nullptr.
+                struct {
+                    int   fVertexCount;
+                } fInstanceNonIndexData;
+
+                struct { // When fIndexBuffer != nullptr.
+                    int   fIndexCount;
+                } fInstanceIndexData;
+            };
+        };
+    };
 };
 
+inline void GrMesh::setNonIndexedNonInstanced(int vertexCount) {
+    fIndexBuffer.reset(nullptr);
+    fInstanceBuffer.reset(nullptr);
+    fNonIndexNonInstanceData.fVertexCount = vertexCount;
+}
+
+inline void GrMesh::setIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                               uint16_t minIndexValue, uint16_t maxIndexValue) {
+    SkASSERT(indexBuffer);
+    SkASSERT(indexCount >= 1);
+    SkASSERT(baseIndex >= 0);
+    SkASSERT(maxIndexValue >= minIndexValue);
+    fIndexBuffer.reset(indexBuffer);
+    fInstanceBuffer.reset(nullptr);
+    fIndexData.fIndexCount = indexCount;
+    fIndexData.fPatternRepeatCount = 0;
+    fNonPatternIndexData.fBaseIndex = baseIndex;
+    fNonPatternIndexData.fMinIndexValue = minIndexValue;
+    fNonPatternIndexData.fMaxIndexValue = maxIndexValue;
+}
+
+inline void GrMesh::setIndexedPatterned(const GrBuffer* indexBuffer, int indexCount,
+                                        int vertexCount, int patternRepeatCount,
+                                        int maxPatternRepetitionsInIndexBuffer) {
+    SkASSERT(indexBuffer);
+    SkASSERT(indexCount >= 1);
+    SkASSERT(vertexCount >= 1);
+    SkASSERT(patternRepeatCount >= 1);
+    SkASSERT(maxPatternRepetitionsInIndexBuffer >= 1);
+    fIndexBuffer.reset(indexBuffer);
+    fInstanceBuffer.reset(nullptr);
+    fIndexData.fIndexCount = indexCount;
+    fIndexData.fPatternRepeatCount = patternRepeatCount;
+    fPatternData.fVertexCount = vertexCount;
+    fPatternData.fMaxPatternRepetitionsInIndexBuffer = maxPatternRepetitionsInIndexBuffer;
+}
+
+inline void GrMesh::setInstanced(const GrBuffer* instanceBuffer, int instanceCount,
+                                 int baseInstance, int vertexCount) {
+    SkASSERT(instanceBuffer);
+    SkASSERT(instanceCount >= 1);
+    SkASSERT(baseInstance >= 0);
+    fIndexBuffer.reset(nullptr);
+    fInstanceBuffer.reset(instanceBuffer);
+    fInstanceData.fInstanceCount = instanceCount;
+    fInstanceData.fBaseInstance = baseInstance;
+    fInstanceNonIndexData.fVertexCount = vertexCount;
+}
+
+inline void GrMesh::setIndexedInstanced(const GrBuffer* indexBuffer, int indexCount,
+                                        const GrBuffer* instanceBuffer, int instanceCount,
+                                        int baseInstance) {
+    SkASSERT(indexBuffer);
+    SkASSERT(indexCount >= 1);
+    SkASSERT(instanceBuffer);
+    SkASSERT(instanceCount >= 1);
+    SkASSERT(baseInstance >= 0);
+    fIndexBuffer.reset(indexBuffer);
+    fInstanceBuffer.reset(instanceBuffer);
+    fInstanceData.fInstanceCount = instanceCount;
+    fInstanceData.fBaseInstance = baseInstance;
+    fInstanceIndexData.fIndexCount = indexCount;
+}
+
+inline void GrMesh::setVertexData(const GrBuffer* vertexBuffer, int baseVertex) {
+    SkASSERT(baseVertex >= 0);
+    fVertexBuffer.reset(vertexBuffer);
+    fBaseVertex = baseVertex;
+}
+
+inline void GrMesh::sendToGpu(const GrPrimitiveProcessor& primProc, SendToGpuImpl* impl) const {
+    if (this->isInstanced()) {
+        if (!this->isIndexed()) {
+            impl->sendInstancedMeshToGpu(primProc, fPrimitiveType, fVertexBuffer.get(),
+                                         fInstanceNonIndexData.fVertexCount, fBaseVertex,
+                                         fInstanceBuffer.get(), fInstanceData.fInstanceCount,
+                                         fInstanceData.fBaseInstance);
+        } else {
+            impl->sendIndexedInstancedMeshToGpu(primProc, fPrimitiveType, fIndexBuffer.get(),
+                                                fInstanceIndexData.fIndexCount, 0,
+                                                fVertexBuffer.get(), fBaseVertex,
+                                                fInstanceBuffer.get(), fInstanceData.fInstanceCount,
+                                                fInstanceData.fBaseInstance);
+        }
+        return;
+    }
+
+    if (!this->isIndexed()) {
+        SkASSERT(fNonIndexNonInstanceData.fVertexCount > 0);
+        impl->sendMeshToGpu(primProc, fPrimitiveType, fVertexBuffer.get(),
+                            fNonIndexNonInstanceData.fVertexCount, fBaseVertex);
+        return;
+    }
+
+    if (0 == fIndexData.fPatternRepeatCount) {
+        impl->sendIndexedMeshToGpu(primProc, fPrimitiveType, fIndexBuffer.get(),
+                                   fIndexData.fIndexCount, fNonPatternIndexData.fBaseIndex,
+                                   fNonPatternIndexData.fMinIndexValue,
+                                   fNonPatternIndexData.fMaxIndexValue, fVertexBuffer.get(),
+                                   fBaseVertex);
+        return;
+    }
+
+    SkASSERT(fIndexData.fPatternRepeatCount > 0);
+    int baseRepetition = 0;
+    do {
+        int repeatCount = SkTMin(fPatternData.fMaxPatternRepetitionsInIndexBuffer,
+                                 fIndexData.fPatternRepeatCount - baseRepetition);
+        // A patterned index buffer must contain indices in the range [0..vertexCount].
+        int minIndexValue = 0;
+        int maxIndexValue = fPatternData.fVertexCount * repeatCount - 1;
+        impl->sendIndexedMeshToGpu(primProc, fPrimitiveType, fIndexBuffer.get(),
+                                   fIndexData.fIndexCount * repeatCount, 0, minIndexValue,
+                                   maxIndexValue, fVertexBuffer.get(),
+                                   fBaseVertex + fPatternData.fVertexCount * baseRepetition);
+        baseRepetition += repeatCount;
+    } while (baseRepetition < fIndexData.fPatternRepeatCount);
+}
+
 #endif
diff --git a/src/gpu/GrOnFlushResourceProvider.cpp b/src/gpu/GrOnFlushResourceProvider.cpp
new file mode 100644
index 0000000..8b031f4
--- /dev/null
+++ b/src/gpu/GrOnFlushResourceProvider.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "GrOnFlushResourceProvider.h"
+
+#include "GrDrawingManager.h"
+#include "GrSurfaceProxy.h"
+
+sk_sp<GrRenderTargetContext> GrOnFlushResourceProvider::makeRenderTargetContext(
+                                                        const GrSurfaceDesc& desc,
+                                                        sk_sp<SkColorSpace> colorSpace,
+                                                        const SkSurfaceProps* props) {
+    GrSurfaceDesc tmpDesc = desc;
+    tmpDesc.fFlags |= kRenderTarget_GrSurfaceFlag;
+
+    // Because this is being allocated at the start of a flush we must ensure the proxy
+    // will, when instantiated, have no pending IO.
+    // TODO: fold the kNoPendingIO_Flag into GrSurfaceFlags?
+    sk_sp<GrSurfaceProxy> proxy = GrSurfaceProxy::MakeDeferred(
+                                                    fDrawingMgr->getContext()->resourceProvider(),
+                                                    tmpDesc,
+                                                    SkBackingFit::kExact,
+                                                    SkBudgeted::kYes,
+                                                    GrResourceProvider::kNoPendingIO_Flag);
+    if (!proxy->asRenderTargetProxy()) {
+        return nullptr;
+    }
+
+    sk_sp<GrRenderTargetContext> renderTargetContext(
+        fDrawingMgr->makeRenderTargetContext(std::move(proxy),
+                                             std::move(colorSpace),
+                                             props));
+
+    if (!renderTargetContext) {
+        return nullptr;
+    }
+
+    renderTargetContext->discard();
+
+    return renderTargetContext;
+}
+
+// TODO: we only need this entry point as long as we have to pre-allocate the atlas.
+// Remove it ASAP.
+sk_sp<GrRenderTargetContext> GrOnFlushResourceProvider::makeRenderTargetContext(
+                                                        sk_sp<GrSurfaceProxy> proxy,
+                                                        sk_sp<SkColorSpace> colorSpace,
+                                                        const SkSurfaceProps* props) {
+    sk_sp<GrRenderTargetContext> renderTargetContext(
+        fDrawingMgr->makeRenderTargetContext(std::move(proxy),
+                                             std::move(colorSpace),
+                                             props));
+
+    if (!renderTargetContext) {
+        return nullptr;
+    }
+
+    renderTargetContext->discard();
+
+    return renderTargetContext;
+}
+
diff --git a/src/gpu/GrOnFlushResourceProvider.h b/src/gpu/GrOnFlushResourceProvider.h
new file mode 100644
index 0000000..b29dc9c
--- /dev/null
+++ b/src/gpu/GrOnFlushResourceProvider.h
@@ -0,0 +1,80 @@
+/*
+ * 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 GrOnFlushResourceProvider_DEFINED
+#define GrOnFlushResourceProvider_DEFINED
+
+#include "GrTypes.h"
+#include "SkRefCnt.h"
+#include "SkTArray.h"
+
+class GrDrawingManager;
+class GrOpList;
+class GrOnFlushResourceProvider;
+class GrRenderTargetOpList;
+class GrRenderTargetContext;
+class GrSurfaceProxy;
+
+class SkColorSpace;
+class SkSurfaceProps;
+
+/*
+ * This is the base class from which all pre-flush callback objects must be derived. It
+ * provides the "preFlush" / "postFlush" interface.
+ */
+class GrOnFlushCallbackObject {
+public:
+    virtual ~GrOnFlushCallbackObject() { }
+
+    /*
+     * The onFlush callback allows subsystems (e.g., text, path renderers) to create atlases
+     * for a specific flush. All the GrOpList IDs required for the flush are passed into the
+     * callback. The callback should return the render target contexts used to render the atlases
+     * in 'results'.
+     */
+    virtual void preFlush(GrOnFlushResourceProvider*,
+                          const uint32_t* opListIDs, int numOpListIDs,
+                          SkTArray<sk_sp<GrRenderTargetContext>>* results) = 0;
+
+    /**
+     * Called once flushing is complete and all ops indicated by preFlush have been executed and
+     * released.
+     */
+    virtual void postFlush() {}
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+/*
+ * This class is a shallow wrapper around the drawing manager. It is passed into the
+ * onFlush callbacks and is intended to limit the functionality available to them.
+ * It should never have additional data members or virtual methods.
+ */
+class GrOnFlushResourceProvider {
+public:
+    sk_sp<GrRenderTargetContext> makeRenderTargetContext(const GrSurfaceDesc& desc,
+                                                         sk_sp<SkColorSpace> colorSpace,
+                                                         const SkSurfaceProps* props);
+
+    // TODO: we only need this entry point as long as we have to pre-allocate the atlas.
+    // Remove it ASAP.
+    sk_sp<GrRenderTargetContext> makeRenderTargetContext(sk_sp<GrSurfaceProxy> proxy,
+                                                         sk_sp<SkColorSpace> colorSpace,
+                                                         const SkSurfaceProps* props);
+
+private:
+    explicit GrOnFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {}
+    GrOnFlushResourceProvider(const GrOnFlushResourceProvider&) = delete;
+    GrOnFlushResourceProvider& operator=(const GrOnFlushResourceProvider&) = delete;
+
+    GrDrawingManager* fDrawingMgr;
+
+    friend class GrDrawingManager; // to construct this type.
+};
+
+#endif
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index d7ed4e0..85a356e 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -10,6 +10,7 @@
 
 #include "GrBufferAllocPool.h"
 #include "GrGpu.h"
+#include "SkArenaAlloc.h"
 #include "ops/GrMeshDrawOp.h"
 
 class GrGpuCommandBuffer;
@@ -94,13 +95,14 @@
     void reset() {
         fVertexPool.reset();
         fIndexPool.reset();
+        fPipelines.reset();
     }
 
     /** Additional data required on a per-op basis when executing GrDrawOps. */
     struct DrawOpArgs {
-        GrRenderTarget* fRenderTarget;
-        const GrAppliedClip* fAppliedClip;
-        GrXferProcessor::DstTexture fDstTexture;
+        GrRenderTarget*           fRenderTarget;
+        const GrAppliedClip*      fAppliedClip;
+        GrXferProcessor::DstProxy fDstProxy;
     };
 
     void setDrawOpArgs(DrawOpArgs* opArgs) { fOpArgs = opArgs; }
@@ -110,16 +112,22 @@
         return *fOpArgs;
     }
 
+    template <typename... Args>
+    GrPipeline* allocPipeline(Args... args) {
+        return fPipelines.make<GrPipeline>(std::forward<Args>(args)...);
+    }
+
 private:
-    GrGpu*                                      fGpu;
-    GrResourceProvider*                         fResourceProvider;
-    GrGpuCommandBuffer*                         fCommandBuffer;
-    GrVertexBufferAllocPool                     fVertexPool;
-    GrIndexBufferAllocPool                      fIndexPool;
-    SkSTArray<4, GrDrawOp::DeferredUploadFn>    fAsapUploads;
-    GrDrawOpUploadToken                         fLastIssuedToken;
-    GrDrawOpUploadToken                         fLastFlushedToken;
-    DrawOpArgs*                                 fOpArgs;
+    GrGpu* fGpu;
+    GrResourceProvider* fResourceProvider;
+    GrGpuCommandBuffer* fCommandBuffer;
+    GrVertexBufferAllocPool fVertexPool;
+    GrIndexBufferAllocPool fIndexPool;
+    SkSTArray<4, GrDrawOp::DeferredUploadFn> fAsapUploads;
+    GrDrawOpUploadToken fLastIssuedToken;
+    GrDrawOpUploadToken fLastFlushedToken;
+    DrawOpArgs* fOpArgs;
+    SkArenaAlloc fPipelines{sizeof(GrPipeline) * 100};
 };
 
 /**
@@ -182,6 +190,7 @@
 protected:
     GrDrawOp* op() { return fOp; }
     GrOpFlushState* state() { return fState; }
+    const GrOpFlushState* state() const { return fState; }
 
 private:
     GrOpFlushState* fState;
@@ -194,7 +203,7 @@
 public:
     Target(GrOpFlushState* state, GrMeshDrawOp* op) : INHERITED(state, op) {}
 
-    void draw(const GrGeometryProcessor* gp, const GrMesh& mesh);
+    void draw(const GrGeometryProcessor* gp, const GrPipeline* pipeline, const GrMesh& mesh);
 
     void* makeVertexSpace(size_t vertexSize, int vertexCount,
                           const GrBuffer** buffer, int* startVertex) {
@@ -211,6 +220,19 @@
         this->state()->putBackVertexSpace(vertices * vertexStride);
     }
 
+    GrRenderTarget* renderTarget() const { return this->state()->drawOpArgs().fRenderTarget; }
+
+    const GrAppliedClip* clip() const { return this->state()->drawOpArgs().fAppliedClip; }
+
+    const GrXferProcessor::DstProxy& dstProxy() const {
+        return this->state()->drawOpArgs().fDstProxy;
+    }
+
+    template <typename... Args>
+    GrPipeline* allocPipeline(Args... args) {
+        return this->state()->allocPipeline(std::forward<Args>(args)...);
+    }
+
 private:
     GrMeshDrawOp* meshDrawOp() { return static_cast<GrMeshDrawOp*>(this->op()); }
     typedef GrDrawOp::Target INHERITED;
diff --git a/src/gpu/GrOpList.cpp b/src/gpu/GrOpList.cpp
index 1f90f43..5160be1 100644
--- a/src/gpu/GrOpList.cpp
+++ b/src/gpu/GrOpList.cpp
@@ -7,10 +7,11 @@
 
 #include "GrOpList.h"
 
-#include "GrRenderTargetOpList.h"
-#include "GrSurface.h"
+#include "GrContext.h"
 #include "GrSurfaceProxy.h"
 
+#include "SkAtomics.h"
+
 uint32_t GrOpList::CreateUniqueID() {
     static int32_t gUniqueID = SK_InvalidUniqueID;
     uint32_t id;
@@ -21,21 +22,48 @@
     return id;
 }
 
-GrOpList::GrOpList(GrSurfaceProxy* surfaceProxy, GrAuditTrail* auditTrail)
-    : fUniqueID(CreateUniqueID())
-    , fFlags(0)
-    , fTarget(surfaceProxy)
-    , fAuditTrail(auditTrail) {
+GrOpList::GrOpList(GrResourceProvider* resourceProvider,
+                   GrSurfaceProxy* surfaceProxy, GrAuditTrail* auditTrail)
+    : fAuditTrail(auditTrail)
+    , fUniqueID(CreateUniqueID())
+    , fFlags(0) {
+    fTarget.setProxy(sk_ref_sp(surfaceProxy), kWrite_GrIOType);
+    fTarget.get()->setLastOpList(this);
 
-    surfaceProxy->setLastOpList(this);
+    // MDB TODO: remove this! We are currently moving to having all the ops that target
+    // the RT as a dest (e.g., clear, etc.) rely on the opList's 'fTarget' pointer
+    // for the IO Ref. This works well but until they are all swapped over (and none
+    // are pre-emptively instantiating proxies themselves) we need to instantiate
+    // here so that the GrSurfaces are created in an order that preserves the GrSurface
+    // re-use assumptions.
+    fTarget.get()->instantiate(resourceProvider);
+    fTarget.markPendingIO();
 }
 
 GrOpList::~GrOpList() {
-    if (fTarget && this == fTarget->getLastOpList()) {
-        fTarget->setLastOpList(nullptr);
+    if (fTarget.get()) {
+        if (this == fTarget.get()->getLastOpList()) {
+            fTarget.get()->setLastOpList(nullptr);
+        }
+
+        fTarget.pendingIOComplete();
     }
 }
 
+bool GrOpList::instantiate(GrResourceProvider* resourceProvider) {
+    return SkToBool(fTarget.get()->instantiate(resourceProvider));
+}
+
+void GrOpList::reset() {
+    if (fTarget.get() && this == fTarget.get()->getLastOpList()) {
+        fTarget.get()->setLastOpList(nullptr);
+    }
+
+    fTarget.pendingIOComplete();
+    fTarget.reset();
+    fAuditTrail = nullptr;
+}
+
 // Add a GrOpList-based dependency
 void GrOpList::addDependency(GrOpList* dependedOn) {
     SkASSERT(!dependedOn->dependsOn(this));  // loops are bad
@@ -48,7 +76,7 @@
 }
 
 // Convert from a GrSurface-based dependency to a GrOpList one
-void GrOpList::addDependency(GrSurface* dependedOn) {
+void GrOpList::addDependency(GrSurfaceProxy* dependedOn, const GrCaps& caps) {
     if (dependedOn->getLastOpList()) {
         // If it is still receiving dependencies, this GrOpList shouldn't be closed
         SkASSERT(!this->isClosed());
@@ -60,7 +88,7 @@
             this->addDependency(opList);
 
             // Can't make it closed in the self-read case
-            opList->makeClosed();
+            opList->makeClosed(caps);
         }
     }
 }
@@ -68,7 +96,8 @@
 #ifdef SK_DEBUG
 void GrOpList::dump() const {
     SkDebugf("--------------------------------------------------------------\n");
-    SkDebugf("node: %d -> RT: %d\n", fUniqueID, fTarget ? fTarget->uniqueID().asUInt() : -1);
+    SkDebugf("node: %d -> RT: %d\n", fUniqueID, fTarget.get() ? fTarget.get()->uniqueID().asUInt()
+                                                              : -1);
     SkDebugf("relies On (%d): ", fDependencies.count());
     for (int i = 0; i < fDependencies.count(); ++i) {
         SkDebugf("%d, ", fDependencies[i]->fUniqueID);
diff --git a/src/gpu/GrOpList.h b/src/gpu/GrOpList.h
index 5571266..dc62441 100644
--- a/src/gpu/GrOpList.h
+++ b/src/gpu/GrOpList.h
@@ -8,53 +8,49 @@
 #ifndef GrOpList_DEFINED
 #define GrOpList_DEFINED
 
+#include "GrGpuResourceRef.h"
+
 #include "SkRefCnt.h"
 #include "SkTDArray.h"
 
 //#define ENABLE_MDB 1
 
 class GrAuditTrail;
+class GrCaps;
 class GrOpFlushState;
 class GrRenderTargetOpList;
-class GrSurface;
+class GrResourceProvider;
 class GrSurfaceProxy;
+class GrTextureProxy;
 class GrTextureOpList;
 
 class GrOpList : public SkRefCnt {
 public:
-    GrOpList(GrSurfaceProxy* surfaceProxy, GrAuditTrail* auditTrail);
+    GrOpList(GrResourceProvider*, GrSurfaceProxy*, GrAuditTrail*);
     ~GrOpList() override;
 
-    // These two methods are invoked as flush time
+    // These three methods are invoked at flush time
+    bool instantiate(GrResourceProvider* resourceProvider);
     virtual void prepareOps(GrOpFlushState* flushState) = 0;
     virtual bool executeOps(GrOpFlushState* flushState) = 0;
 
-    virtual void makeClosed() {
-        // We only close GrOpLists when MDB is enabled. When MDB is disabled there is only
-        // ever one GrOpLists and all calls will be funnelled into it.
-#ifdef ENABLE_MDB
+    virtual void makeClosed(const GrCaps&) {
         this->setFlag(kClosed_Flag);
-#endif    
     }
 
-    // TODO: it seems a bit odd that GrOpList has nothing to clear on reset
-    virtual void reset() = 0;
+    virtual void reset();
 
     // TODO: in an MDB world, where the OpLists don't allocate GPU resources, it seems like
     // these could go away
     virtual void abandonGpuResources() = 0;
     virtual void freeGpuResources() = 0;
 
-    // TODO: this entry point is only needed in the non-MDB world. Remove when
-    // we make the switch to MDB
-    void clearTarget() { fTarget = nullptr; }
-
     bool isClosed() const { return this->isSetFlag(kClosed_Flag); }
 
     /*
      * Notify this GrOpList that it relies on the contents of 'dependedOn'
      */
-    void addDependency(GrSurface* dependedOn);
+    void addDependency(GrSurfaceProxy* dependedOn, const GrCaps& caps);
 
     /*
      * Does this opList depend on 'dependedOn'?
@@ -80,6 +76,13 @@
      */
     SkDEBUGCODE(virtual void dump() const;)
 
+    SkDEBUGCODE(virtual int numOps() const = 0;)
+    SkDEBUGCODE(virtual int numClips() const { return 0; })
+
+protected:
+    GrSurfaceProxyRef fTarget;
+    GrAuditTrail*     fAuditTrail;
+
 private:
     friend class GrDrawingManager; // for resetFlag & TopoSortTraits
 
@@ -130,15 +133,11 @@
 
     void addDependency(GrOpList* dependedOn);
 
-    uint32_t             fUniqueID;
-    uint32_t             fFlags;
-    GrSurfaceProxy*      fTarget;
+    uint32_t              fUniqueID;
+    uint32_t              fFlags;
 
     // 'this' GrOpList relies on the output of the GrOpLists in 'fDependencies'
-    SkTDArray<GrOpList*> fDependencies;
-
-protected:
-    GrAuditTrail*        fAuditTrail;
+    SkTDArray<GrOpList*>  fDependencies;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/src/gpu/GrPaint.cpp b/src/gpu/GrPaint.cpp
index 6c5a041..9bbeec5 100644
--- a/src/gpu/GrPaint.cpp
+++ b/src/gpu/GrPaint.cpp
@@ -12,11 +12,11 @@
 #include "effects/GrSimpleTextureEffect.h"
 
 void GrPaint::setPorterDuffXPFactory(SkBlendMode mode) {
-    fXPFactory = GrPorterDuffXPFactory::Get(mode);
+    this->setXPFactory(GrPorterDuffXPFactory::Get(mode));
 }
 
 void GrPaint::setCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage) {
-    fXPFactory = GrCoverageSetOpXPFactory::Get(regionOp, invertCoverage);
+    this->setXPFactory(GrCoverageSetOpXPFactory::Get(regionOp, invertCoverage));
 }
 
 void GrPaint::addColorTextureProcessor(GrResourceProvider* resourceProvider,
diff --git a/src/gpu/GrPaint.h b/src/gpu/GrPaint.h
index bcf6858..e2b494f 100644
--- a/src/gpu/GrPaint.h
+++ b/src/gpu/GrPaint.h
@@ -70,20 +70,18 @@
     bool getAllowSRGBInputs() const { return fAllowSRGBInputs; }
 
     /**
-     * Does one of the fragment processors need a field of distance vectors to the nearest edge?
-     */
-    bool usesDistanceVectorField() const { return fUsesDistanceVectorField; }
-
-    /**
      * Should rendering be gamma-correct, end-to-end. Causes sRGB render targets to behave
      * as such (with linear blending), and sRGB inputs to be filtered and decoded correctly.
      */
     void setGammaCorrect(bool gammaCorrect) {
-        setDisableOutputConversionToSRGB(!gammaCorrect);
-        setAllowSRGBInputs(gammaCorrect);
+        this->setDisableOutputConversionToSRGB(!gammaCorrect);
+        this->setAllowSRGBInputs(gammaCorrect);
     }
 
-    void setXPFactory(const GrXPFactory* xpFactory) { fXPFactory = xpFactory; }
+    void setXPFactory(const GrXPFactory* xpFactory) {
+        fXPFactory = xpFactory;
+        fTrivial &= !SkToBool(xpFactory);
+    }
 
     void setPorterDuffXPFactory(SkBlendMode mode);
 
@@ -94,8 +92,8 @@
      */
     void addColorFragmentProcessor(sk_sp<GrFragmentProcessor> fp) {
         SkASSERT(fp);
-        fUsesDistanceVectorField |= fp->usesDistanceVectorField();
         fColorFragmentProcessors.push_back(std::move(fp));
+        fTrivial = false;
     }
 
     /**
@@ -103,8 +101,8 @@
      */
     void addCoverageFragmentProcessor(sk_sp<GrFragmentProcessor> fp) {
         SkASSERT(fp);
-        fUsesDistanceVectorField |= fp->usesDistanceVectorField();
         fCoverageFragmentProcessors.push_back(std::move(fp));
+        fTrivial = false;
     }
 
     /**
@@ -143,6 +141,12 @@
      */
     bool isConstantBlendedColor(GrColor* constantColor) const;
 
+    /**
+     * A trivial paint is one that uses src-over and has no fragment processors.
+     * It may have variable sRGB settings.
+     **/
+    bool isTrivial() const { return fTrivial; }
+
 private:
     template <bool> class MoveOrImpl;
 
@@ -171,7 +175,7 @@
     SkSTArray<2, sk_sp<GrFragmentProcessor>>  fCoverageFragmentProcessors;
     bool fDisableOutputConversionToSRGB = false;
     bool fAllowSRGBInputs = false;
-    bool fUsesDistanceVectorField = false;
+    bool fTrivial = true;
     GrColor4f fColor = GrColor4f::OpaqueWhite();
 };
 
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 05673d5..2f08f43 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -74,14 +74,14 @@
 
     /** Args to canDrawPath()
      *
-     * fShaderCaps       The shader caps
+     * fCaps             The context caps
      * fPipelineBuilder  The pipelineBuilder
      * fViewMatrix       The viewMatrix
      * fShape            The shape to draw
      * fAntiAlias        The type of anti aliasing required.
      */
     struct CanDrawPathArgs {
-        const GrShaderCaps*         fShaderCaps;
+        const GrCaps*               fCaps;
         const SkMatrix*             fViewMatrix;
         const GrShape*              fShape;
         GrAAType                    fAAType;
@@ -91,7 +91,7 @@
 
 #ifdef SK_DEBUG
         void validate() const {
-            SkASSERT(fShaderCaps);
+            SkASSERT(fCaps);
             SkASSERT(fViewMatrix);
             SkASSERT(fShape);
         }
@@ -153,16 +153,16 @@
         SkDEBUGCODE(args.validate();)
 #ifdef SK_DEBUG
         CanDrawPathArgs canArgs;
-        canArgs.fShaderCaps = args.fContext->caps()->shaderCaps();
+        canArgs.fCaps = args.fContext->caps();
         canArgs.fViewMatrix = args.fViewMatrix;
         canArgs.fShape = args.fShape;
         canArgs.fAAType = args.fAAType;
 
         canArgs.fHasUserStencilSettings = !args.fUserStencilSettings->isUnused();
         SkASSERT(!(canArgs.fAAType == GrAAType::kMSAA &&
-                   !args.fRenderTargetContext->isUnifiedMultisampled()));
+                   GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType()));
         SkASSERT(!(canArgs.fAAType == GrAAType::kMixedSamples &&
-                   !args.fRenderTargetContext->isStencilBufferMultisampled()));
+                   GrFSAAType::kMixedSamples != args.fRenderTargetContext->fsaaType()));
         SkASSERT(this->canDrawPath(canArgs));
         if (!args.fUserStencilSettings->isUnused()) {
             SkPath path;
diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp
index 55c4a30..076dea7 100644
--- a/src/gpu/GrPathRendererChain.cpp
+++ b/src/gpu/GrPathRendererChain.cpp
@@ -60,8 +60,7 @@
         fChain.push_back(sk_make_sp<GrTessellatingPathRenderer>());
     }
     if (options.fGpuPathRenderers & GpuPathRenderers::kDefault) {
-        fChain.push_back(sk_make_sp<GrDefaultPathRenderer>(caps.twoSidedStencilSupport(),
-                                                           caps.stencilWrapOpsSupport()));
+        fChain.push_back(sk_make_sp<GrDefaultPathRenderer>());
     }
 }
 
diff --git a/src/gpu/GrPathRendering.cpp b/src/gpu/GrPathRendering.cpp
index 18d000a..ad60726 100644
--- a/src/gpu/GrPathRendering.cpp
+++ b/src/gpu/GrPathRendering.cpp
@@ -70,10 +70,10 @@
 #endif
 };
 
-GrPathRange* GrPathRendering::createGlyphs(const SkTypeface* typeface,
-                                           const SkScalerContextEffects& effects,
-                                           const SkDescriptor* desc,
-                                           const GrStyle& style) {
+sk_sp<GrPathRange> GrPathRendering::createGlyphs(const SkTypeface* typeface,
+                                                 const SkScalerContextEffects& effects,
+                                                 const SkDescriptor* desc,
+                                                 const GrStyle& style) {
     if (nullptr == typeface) {
         typeface = SkTypeface::GetDefaultTypeface();
         SkASSERT(nullptr != typeface);
diff --git a/src/gpu/GrPathRendering.h b/src/gpu/GrPathRendering.h
index f2c02d9..23c731f 100644
--- a/src/gpu/GrPathRendering.h
+++ b/src/gpu/GrPathRendering.h
@@ -81,30 +81,27 @@
 
     /**
      * Creates a new gpu path, based on the specified path and stroke and returns it.
-     * The caller owns a ref on the returned path which must be balanced by a call to unref.
      *
      * @param SkPath    the geometry.
      * @param GrStyle   the style applied to the path. Styles with non-dash path effects are not
      *                  allowed.
      * @return a new GPU path object.
      */
-    virtual GrPath* createPath(const SkPath&, const GrStyle&) = 0;
+    virtual sk_sp<GrPath> createPath(const SkPath&, const GrStyle&) = 0;
 
     /**
-     * Creates a range of gpu paths with a common style. The caller owns a ref on the
-     * returned path range which must be balanced by a call to unref.
+     * Creates a range of gpu paths with a common style.
      *
      * @param PathGenerator class that generates SkPath objects for each path in the range.
      * @param GrStyle   the common style applied to each path in the range. Styles with non-dash
      *                  path effects are not allowed.
      * @return a new path range.
      */
-    virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*, const GrStyle&) = 0;
+    virtual sk_sp<GrPathRange> createPathRange(GrPathRange::PathGenerator*, const GrStyle&) = 0;
 
     /**
      * Creates a range of glyph paths, indexed by glyph id. The glyphs will have an
-     * inverted y-direction in order to match the raw font path data. The caller owns
-     * a ref on the returned path range which must be balanced by a call to unref.
+     * inverted y-direction in order to match the raw font path data.
      *
      * @param SkTypeface   Typeface that defines the glyphs.
      *                     If null, the default typeface will be used.
@@ -129,8 +126,8 @@
      *
      * @return a new path range populated with glyphs.
      */
-    GrPathRange* createGlyphs(const SkTypeface*, const SkScalerContextEffects&,
-                              const SkDescriptor*, const GrStyle&);
+    sk_sp<GrPathRange> createGlyphs(const SkTypeface*, const SkScalerContextEffects&,
+                                    const SkDescriptor*, const GrStyle&);
 
     /** None of these params are optional, pointers used just to avoid making copies. */
     struct StencilPathArgs {
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 6dbac62..91a48f7 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -11,13 +11,15 @@
 #include "SkGeometry.h"
 #include "SkMathPriv.h"
 
+static const int MAX_POINTS_PER_CURVE = 1 << 10;
+static const SkScalar gMinCurveTol = 0.0001f;
+
 SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
                                           const SkMatrix& viewM,
                                           const SkRect& pathBounds) {
     // In order to tesselate the path we get a bound on how much the matrix can
     // scale when mapping to screen coordinates.
     SkScalar stretch = viewM.getMaxScale();
-    SkScalar srcTol = devTol;
 
     if (stretch < 0) {
         // take worst case mapRadius amoung four corners.
@@ -30,18 +32,16 @@
             stretch = SkMaxScalar(stretch, mat.mapRadius(SK_Scalar1));
         }
     }
-    return srcTol / stretch;
+    SkScalar srcTol = devTol / stretch;
+    if (srcTol < gMinCurveTol) {
+        srcTol = gMinCurveTol;
+    }
+    return srcTol;
 }
 
-static const int MAX_POINTS_PER_CURVE = 1 << 10;
-static const SkScalar gMinCurveTol = 0.0001f;
-
-uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[],
-                                          SkScalar tol) {
-    if (tol < gMinCurveTol) {
-        tol = gMinCurveTol;
-    }
-    SkASSERT(tol > 0);
+uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) {
+    // You should have called scaleToleranceToSrc, which guarantees this
+    SkASSERT(tol >= gMinCurveTol);
 
     SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
     if (!SkScalarIsFinite(d)) {
@@ -97,10 +97,8 @@
 
 uint32_t GrPathUtils::cubicPointCount(const SkPoint points[],
                                            SkScalar tol) {
-    if (tol < gMinCurveTol) {
-        tol = gMinCurveTol;
-    }
-    SkASSERT(tol > 0);
+    // You should have called scaleToleranceToSrc, which guarantees this
+    SkASSERT(tol >= gMinCurveTol);
 
     SkScalar d = SkTMax(
         points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
@@ -158,12 +156,9 @@
     return a + b;
 }
 
-int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths,
-                                     SkScalar tol) {
-    if (tol < gMinCurveTol) {
-        tol = gMinCurveTol;
-    }
-    SkASSERT(tol > 0);
+int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths, SkScalar tol) {
+    // You should have called scaleToleranceToSrc, which guarantees this
+    SkASSERT(tol >= gMinCurveTol);
 
     int pointCount = 0;
     *subpaths = 1;
@@ -183,7 +178,7 @@
             case SkPath::kConic_Verb: {
                 SkScalar weight = iter.conicWeight();
                 SkAutoConicToQuads converter;
-                const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
+                const SkPoint* quadPts = converter.computeQuads(pts, weight, tol);
                 for (int i = 0; i < converter.countQuads(); ++i) {
                     pointCount += quadraticPointCount(quadPts + 2*i, tol);
                 }
@@ -617,15 +612,16 @@
     }
 
     // The matrix is 3x4. In order to invert it, we first need to make it square by throwing out one
-    // of the top three rows. We toss the row that leaves us with the largest determinant. Since the
-    // right column will be [0 0 1], the determinant reduces to x0*y1 - y0*x1.
-    SkScalar det[4];
-    SkScalar4 DETX1 = SkNx_shuffle<1,0,0,3>(X), DETY1 = SkNx_shuffle<1,0,0,3>(Y);
-    SkScalar4 DETX2 = SkNx_shuffle<2,2,1,3>(X), DETY2 = SkNx_shuffle<2,2,1,3>(Y);
-    (DETX1 * DETY2 - DETY1 * DETX2).store(det);
-    const int skipRow = det[0] > det[2] ? (det[0] > det[1] ? 0 : 1)
-                                        : (det[1] > det[2] ? 1 : 2);
-    const SkScalar rdet = 1 / det[skipRow];
+    // of the top three rows. We toss the row that leaves us with the largest absolute determinant.
+    // Since the right column will be [0 0 1], the determinant reduces to x0*y1 - y0*x1.
+    SkScalar absDet[4];
+    const SkScalar4 DETX1 = SkNx_shuffle<1,0,0,3>(X), DETY1 = SkNx_shuffle<1,0,0,3>(Y);
+    const SkScalar4 DETX2 = SkNx_shuffle<2,2,1,3>(X), DETY2 = SkNx_shuffle<2,2,1,3>(Y);
+    const SkScalar4 DET = DETX1 * DETY2 - DETY1 * DETX2;
+    DET.abs().store(absDet);
+    const int skipRow = absDet[0] > absDet[2] ? (absDet[0] > absDet[1] ? 0 : 1)
+                                              : (absDet[1] > absDet[2] ? 1 : 2);
+    const SkScalar rdet = 1 / DET[skipRow];
     const int row0 = (0 != skipRow) ? 0 : 1;
     const int row1 = (2 == skipRow) ? 1 : 2;
 
@@ -652,24 +648,11 @@
     return skipRow;
 }
 
-static void negate_kl(SkMatrix* klm) {
-    // We could use klm->postScale(-1, -1), but it ends up doing a full matrix multiply.
-    for (int i = 0; i < 6; ++i) {
-        (*klm)[i] = -(*klm)[i];
-    }
-}
-
-static void calc_serp_klm(const SkPoint pts[4], const SkScalar d[3], SkMatrix* klm) {
+static void calc_serp_klm(const SkPoint pts[4], SkScalar tl, SkScalar sl, SkScalar tm, SkScalar sm,
+                          SkMatrix* klm) {
     SkMatrix CIT;
     int skipCol = calc_inverse_transpose_power_basis_matrix(pts, &CIT);
 
-    const SkScalar root = SkScalarSqrt(9 * d[1] * d[1] - 12 * d[0] * d[2]);
-
-    const SkScalar tl = 3 * d[1] + root;
-    const SkScalar sl = 6 * d[0];
-    const SkScalar tm = 3 * d[1] - root;
-    const SkScalar sm = 6 * d[0];
-
     SkMatrix klmCoeffs;
     int col = 0;
     if (0 != skipCol) {
@@ -697,17 +680,10 @@
     klmCoeffs[8] = tm * tm * tm;
 
     klm->setConcat(klmCoeffs, CIT);
-
-    // If d0 > 0 we need to flip the orientation of our curve
-    // This is done by negating the k and l values
-    // We want negative distance values to be on the inside
-    if (d[0] > 0) {
-        negate_kl(klm);
-    }
 }
 
-static void calc_loop_klm(const SkPoint pts[4], SkScalar d1, SkScalar td, SkScalar sd,
-                          SkScalar te, SkScalar se, SkMatrix* klm) {
+static void calc_loop_klm(const SkPoint pts[4], SkScalar td, SkScalar sd, SkScalar te, SkScalar se,
+                          SkMatrix* klm) {
     SkMatrix CIT;
     int skipCol = calc_inverse_transpose_power_basis_matrix(pts, &CIT);
 
@@ -741,25 +717,13 @@
     klmCoeffs[8] = te * te * td;
 
     klm->setConcat(klmCoeffs, CIT);
-
-    // For the general loop curve, we flip the orientation in the same pattern as the serp case
-    // above. Thus we only check d1. Technically we should check the value of the hessian as well
-    // cause we care about the sign of d1*Hessian. However, the Hessian is always negative outside
-    // the loop section and positive inside. We take care of the flipping for the loop sections
-    // later on.
-    if (d1 > 0) {
-        negate_kl(klm);
-    }
 }
 
 // For the case when we have a cusp at a parameter value of infinity (discr == 0, d1 == 0).
-static void calc_inf_cusp_klm(const SkPoint pts[4], SkScalar d2, SkScalar d3, SkMatrix* klm) {
+static void calc_inf_cusp_klm(const SkPoint pts[4], SkScalar tn, SkScalar sn, SkMatrix* klm) {
     SkMatrix CIT;
     int skipCol = calc_inverse_transpose_power_basis_matrix(pts, &CIT);
 
-    const SkScalar tn = d3;
-    const SkScalar sn = 3 * d2;
-
     SkMatrix klmCoeffs;
     int col = 0;
     if (0 != skipCol) {
@@ -817,7 +781,7 @@
     // If d3 > 0 we need to flip the orientation of our curve
     // This is done by negating the k and l values
     if (d3 > 0) {
-        negate_kl(klm);
+        klm->postScale(-1, -1);
     }
 }
 
@@ -843,16 +807,17 @@
     // Homogeneous parametric values at the loop double point.
     SkScalar td, sd, te, se;
 
-    SkScalar d[3];
+    SkScalar d[4];
     SkCubicType cType = SkClassifyCubic(src, d);
 
     int chop_count = 0;
-    if (kLoop_SkCubicType == cType) {
-        SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
-        td = d[1] + tempSqrt;
-        sd = 2.f * d[0];
-        te = d[1] - tempSqrt;
-        se = 2.f * d[0];
+    if (SkCubicType::kLoop == cType) {
+        SkASSERT(d[0] < 0);
+        const SkScalar q = d[2] + SkScalarCopySign(SkScalarSqrt(-d[0]), d[2]);
+        td = q;
+        sd = 2 * d[1];
+        te = 2 * (d[2] * d[2] - d[3] * d[1]);
+        se = d[1] * q;
 
         t1 = td / sd;
         t2 = te / se;
@@ -897,26 +862,42 @@
 
     if (klm) {
         switch (cType) {
-            case kSerpentine_SkCubicType:
-                calc_serp_klm(src, d, klm);
+            case SkCubicType::kSerpentine: {
+                SkASSERT(d[0] >= 0);
+                const SkScalar q = 3 * d[2] + SkScalarCopySign(SkScalarSqrt(3 * d[0]), d[2]);
+                const SkScalar tl = q;
+                const SkScalar sl = 6 * d[1];
+                const SkScalar tm = 2 * d[3];
+                const SkScalar sm = q;
+                // This copysign/abs business orients the implicit function so positive values are
+                // always on the "left" side of the curve.
+                calc_serp_klm(src, tl, sl, -SkScalarCopySign(tm, tm * sm), -SkScalarAbs(sm), klm);
                 break;
-            case kLoop_SkCubicType:
-                calc_loop_klm(src, d[0], td, sd, te, se, klm);
+            }
+            case SkCubicType::kLocalCusp: {
+                SkASSERT(0 == d[0]);
+                const SkScalar t = d[2];
+                const SkScalar s = 2 * d[1];
+                // This copysign/abs business orients the implicit function so positive values are
+                // always on the "left" side of the curve.
+                calc_serp_klm(src, t, s, -SkScalarCopySign(t, t * s), -SkScalarAbs(s), klm);
                 break;
-            case kCusp_SkCubicType:
-                if (0 != d[0]) {
-                    // FIXME: SkClassifyCubic has a tolerance, but we need an exact classification
-                    // here to be sure we won't get a negative in the square root.
-                    calc_serp_klm(src, d, klm);
-                } else {
-                    calc_inf_cusp_klm(src, d[1], d[2], klm);
-                }
+            }
+            case SkCubicType::kLoop:
+                // This copysign/abs business orients the implicit function so positive values are
+                // always on the "left" side of the curve.
+                calc_loop_klm(src, td, sd, -SkScalarCopySign(te, te * se), -SkScalarAbs(se), klm);
                 break;
-            case kQuadratic_SkCubicType:
-                calc_quadratic_klm(src, d[2], klm);
+            case SkCubicType::kInfiniteCusp: {
+                const SkScalar tn = d[3];
+                const SkScalar sn = 3 * d[2];
+                calc_inf_cusp_klm(src, tn, sn, klm);
                 break;
-            case kLine_SkCubicType:
-            case kPoint_SkCubicType:
+            }
+            case SkCubicType::kQuadratic:
+                calc_quadratic_klm(src, d[3], klm);
+                break;
+            case SkCubicType::kLineOrPoint:
                 calc_line_klm(src, klm);
                 break;
         };
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index 7dea3a1..fdfd375 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -18,18 +18,16 @@
  *  Utilities for evaluating paths.
  */
 namespace GrPathUtils {
+    // Very small tolerances will be increased to a minimum threshold value, to avoid division
+    // problems in subsequent math.
     SkScalar scaleToleranceToSrc(SkScalar devTol,
                                  const SkMatrix& viewM,
                                  const SkRect& pathBounds);
 
-    /// Since we divide by tol if we're computing exact worst-case bounds,
-    /// very small tolerances will be increased to gMinCurveTol.
     int worstCasePointCount(const SkPath&,
                             int* subpaths,
                             SkScalar tol);
 
-    /// Since we divide by tol if we're computing exact worst-case bounds,
-    /// very small tolerances will be increased to gMinCurveTol.
     uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol);
 
     uint32_t generateQuadraticPoints(const SkPoint& p0,
@@ -39,8 +37,6 @@
                                      SkPoint** points,
                                      uint32_t pointsLeft);
 
-    /// Since we divide by tol if we're computing exact worst-case bounds,
-    /// very small tolerances will be increased to gMinCurveTol.
     uint32_t cubicPointCount(const SkPoint points[], SkScalar tol);
 
     uint32_t generateCubicPoints(const SkPoint& p0,
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index adcdd0f..8d1b926 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -18,9 +18,10 @@
 
 #include "ops/GrOp.h"
 
-GrPipelineOptimizations GrPipeline::init(const InitArgs& args) {
-    SkASSERT(args.fAnalysis);
+void GrPipeline::init(const InitArgs& args) {
     SkASSERT(args.fRenderTarget);
+    SkASSERT(args.fProcessors);
+    SkASSERT(args.fProcessors->isFinalized());
 
     fRenderTarget.reset(args.fRenderTarget);
 
@@ -32,63 +33,26 @@
         }
         fWindowRectsState = args.fAppliedClip->windowRectsState();
     }
-    if (args.fProcessors->usesDistanceVectorField()) {
-        fFlags |= kUsesDistanceVectorField_Flag;
-    }
-    if (args.fProcessors->disableOutputConversionToSRGB()) {
-        fFlags |= kDisableOutputConversionToSRGB_Flag;
-    }
-    if (args.fProcessors->allowSRGBInputs()) {
-        fFlags |= kAllowSRGBInputs_Flag;
-    }
     if (!args.fUserStencil->isDisabled(fFlags & kHasStencilClip_Flag)) {
         fFlags |= kStencilEnabled_Flag;
     }
 
     fUserStencilSettings = args.fUserStencil;
 
-    fDrawFace = static_cast<int16_t>(args.fDrawFace);
+    fXferProcessor = args.fProcessors->refXferProcessor();
 
-    bool isHWAA = kHWAntialias_Flag & args.fFlags;
+    if (args.fDstProxy.proxy()) {
+        if (!args.fDstProxy.proxy()->instantiate(args.fResourceProvider)) {
+            this->markAsBad();
+        }
 
-    // Create XferProcessor from DS's XPFactory
-    bool hasMixedSamples = args.fRenderTarget->isMixedSampled() && (isHWAA || isStencilEnabled());
-    const GrXPFactory* xpFactory = args.fProcessors->xpFactory();
-    sk_sp<GrXferProcessor> xferProcessor;
-    if (xpFactory) {
-        xferProcessor.reset(xpFactory->createXferProcessor(*args.fAnalysis, hasMixedSamples,
-                                                           &args.fDstTexture, *args.fCaps));
-        SkASSERT(xferProcessor);
-    } else {
-        // This may return nullptr in the common case of src-over implemented using hw blending.
-        xferProcessor.reset(GrPorterDuffXPFactory::CreateSrcOverXferProcessor(
-                *args.fCaps, *args.fAnalysis, hasMixedSamples, &args.fDstTexture));
-    }
-    GrColor overrideColor = GrColor_ILLEGAL;
-    int colorFPsToEliminate =
-            args.fAnalysis->getInputColorOverrideAndColorProcessorEliminationCount(&overrideColor);
-    colorFPsToEliminate = SkTMax(colorFPsToEliminate, 0);
-
-    GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_OptFlags;
-
-    const GrXferProcessor* xpForOpts = xferProcessor ? xferProcessor.get() :
-                                                       &GrPorterDuffXPFactory::SimpleSrcOverXP();
-    optFlags = xpForOpts->getOptimizations(*args.fAnalysis);
-
-    // No need to have an override color if it isn't even going to be used.
-    if (SkToBool(GrXferProcessor::kIgnoreColor_OptFlag & optFlags)) {
-        overrideColor = GrColor_ILLEGAL;
-    }
-
-    fXferProcessor.reset(xferProcessor.get());
-
-    if ((optFlags & GrXferProcessor::kIgnoreColor_OptFlag)) {
-        colorFPsToEliminate = args.fProcessors->numColorFragmentProcessors();
+        fDstTextureProxy.reset(args.fDstProxy.proxy());
+        fDstTextureOffset = args.fDstProxy.offset();
     }
 
     // Copy GrFragmentProcessors from GrPipelineBuilder to Pipeline, possibly removing some of the
     // color fragment processors.
-    fNumColorProcessors = args.fProcessors->numColorFragmentProcessors() - colorFPsToEliminate;
+    fNumColorProcessors = args.fProcessors->numColorFragmentProcessors();
     int numTotalProcessors =
             fNumColorProcessors + args.fProcessors->numCoverageFragmentProcessors();
     if (args.fAppliedClip && args.fAppliedClip->clipCoverageFragmentProcessor()) {
@@ -96,71 +60,67 @@
     }
     fFragmentProcessors.reset(numTotalProcessors);
     int currFPIdx = 0;
-    for (int i = colorFPsToEliminate; i < args.fProcessors->numColorFragmentProcessors();
-         ++i, ++currFPIdx) {
+    for (int i = 0; i < args.fProcessors->numColorFragmentProcessors(); ++i, ++currFPIdx) {
         const GrFragmentProcessor* fp = args.fProcessors->colorFragmentProcessor(i);
         fFragmentProcessors[currFPIdx].reset(fp);
+        if (!fp->instantiate(args.fResourceProvider)) {
+            this->markAsBad();
+        }
     }
 
     for (int i = 0; i < args.fProcessors->numCoverageFragmentProcessors(); ++i, ++currFPIdx) {
         const GrFragmentProcessor* fp = args.fProcessors->coverageFragmentProcessor(i);
         fFragmentProcessors[currFPIdx].reset(fp);
+        if (!fp->instantiate(args.fResourceProvider)) {
+            this->markAsBad();
+        }
     }
     if (args.fAppliedClip) {
         if (const GrFragmentProcessor* fp = args.fAppliedClip->clipCoverageFragmentProcessor()) {
             fFragmentProcessors[currFPIdx].reset(fp);
+            if (!fp->instantiate(args.fResourceProvider)) {
+                this->markAsBad();
+            }
         }
     }
-
-    // Setup info we need to pass to GrPrimitiveProcessors that are used with this GrPipeline.
-    GrPipelineOptimizations optimizations;
-    optimizations.fFlags = 0;
-    if (GrColor_ILLEGAL != overrideColor) {
-        optimizations.fFlags |= GrPipelineOptimizations::kUseOverrideColor_Flag;
-        optimizations.fOverrideColor = overrideColor;
-    }
-    if (args.fAnalysis->usesLocalCoords()) {
-        optimizations.fFlags |= GrPipelineOptimizations::kReadsLocalCoords_Flag;
-    }
-    if (SkToBool(optFlags & GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag)) {
-        optimizations.fFlags |= GrPipelineOptimizations::kCanTweakAlphaForCoverage_Flag;
-    }
-    return optimizations;
 }
 
-static void add_dependencies_for_processor(const GrFragmentProcessor* proc, GrRenderTarget* rt) {
+// MDB TODO: re-enable when TextureSamplers store texture proxies
+#if 0
+static void add_dependencies_for_processor(const GrFragmentProcessor* proc,
+                                           GrRenderTargetProxy* rtp) {
     GrFragmentProcessor::TextureAccessIter iter(proc);
-    while (const GrProcessor::TextureSampler* sampler = iter.next()) {
-        SkASSERT(rt->getLastOpList());
-        rt->getLastOpList()->addDependency(sampler->texture());
+    while (const GrResourceIOProcessor::TextureSampler* sampler = iter.next()) {
+        SkASSERT(rtp->getLastOpList());
+        rtp->getLastOpList()->addDependency(sampler->proxy());
     }
 }
+#endif
 
-void GrPipeline::addDependenciesTo(GrRenderTarget* rt) const {
+void GrPipeline::addDependenciesTo(GrRenderTargetProxy* rtp) const {
+    // MDB TODO: re-enable when TextureSamplers store texture proxies
+#if 0
     for (int i = 0; i < fFragmentProcessors.count(); ++i) {
-        add_dependencies_for_processor(fFragmentProcessors[i].get(), rt);
+        add_dependencies_for_processor(fFragmentProcessors[i].get(), rtp);
     }
+#endif
 
-    const GrXferProcessor& xfer = this->getXferProcessor();
-
-    for (int i = 0; i < xfer.numTextureSamplers(); ++i) {
-        GrTexture* texture = xfer.textureSampler(i).texture();
-        SkASSERT(rt->getLastOpList());
-        rt->getLastOpList()->addDependency(texture);
+    if (fDstTextureProxy) {
+        //SkASSERT(rtp->getLastOpList());
+        // MDB TODO: re-enable when TextureSamplers store texture proxies
+        //rtp->getLastOpList()->addDependency(fDstTexture.get());
     }
 }
 
 GrPipeline::GrPipeline(GrRenderTarget* rt, SkBlendMode blendmode)
-    : fRenderTarget(rt)
-    , fScissorState()
-    , fWindowRectsState()
-    , fUserStencilSettings(&GrUserStencilSettings::kUnused)
-    , fDrawFace(static_cast<uint16_t>(GrDrawFace::kBoth))
-    , fFlags()
-    , fXferProcessor(GrPorterDuffXPFactory::CreateNoCoverageXP(blendmode).get())
-    , fFragmentProcessors()
-    , fNumColorProcessors(0) {
-}
+        : fRenderTarget(rt)
+        , fScissorState()
+        , fWindowRectsState()
+        , fUserStencilSettings(&GrUserStencilSettings::kUnused)
+        , fFlags()
+        , fXferProcessor(GrPorterDuffXPFactory::MakeNoCoverageXP(blendmode))
+        , fFragmentProcessors()
+        , fNumColorProcessors(0) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -173,8 +133,7 @@
         a.fScissorState != b.fScissorState ||
         a.fWindowRectsState != b.fWindowRectsState ||
         a.fFlags != b.fFlags ||
-        a.fUserStencilSettings != b.fUserStencilSettings ||
-        a.fDrawFace != b.fDrawFace) {
+        a.fUserStencilSettings != b.fUserStencilSettings) {
         return false;
     }
 
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index 2169342..078cb68 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -12,9 +12,9 @@
 #include "GrFragmentProcessor.h"
 #include "GrNonAtomicRef.h"
 #include "GrPendingProgramElement.h"
-#include "GrPrimitiveProcessor.h"
 #include "GrProcessorSet.h"
 #include "GrProgramDesc.h"
+#include "GrRect.h"
 #include "GrScissorState.h"
 #include "GrUserStencilSettings.h"
 #include "GrWindowRectsState.h"
@@ -47,23 +47,36 @@
          * the 3D API.
          */
         kHWAntialias_Flag = 0x1,
-
         /**
          * Modifies the vertex shader so that vertices will be positioned at pixel centers.
          */
         kSnapVerticesToPixelCenters_Flag = 0x2,
+        /** Disables conversion to sRGB from linear when writing to a sRGB destination. */
+        kDisableOutputConversionToSRGB_Flag = 0x4,
+        /** Allows conversion from sRGB to linear when reading from processor's sRGB texture. */
+        kAllowSRGBInputs_Flag = 0x8,
     };
 
+    static uint32_t SRGBFlagsFromPaint(const GrPaint& paint) {
+        uint32_t flags = 0;
+        if (paint.getAllowSRGBInputs()) {
+            flags |= kAllowSRGBInputs_Flag;
+        }
+        if (paint.getDisableOutputConversionToSRGB()) {
+            flags |= kDisableOutputConversionToSRGB_Flag;
+        }
+        return flags;
+    }
+
     struct InitArgs {
         uint32_t fFlags = 0;
-        GrDrawFace fDrawFace = GrDrawFace::kBoth;
-        const GrProcessorSet* fProcessors = nullptr;
-        const GrProcessorSet::FragmentProcessorAnalysis* fAnalysis;
+        const GrProcessorSet* fProcessors = nullptr;  // Must be finalized
         const GrUserStencilSettings* fUserStencil = &GrUserStencilSettings::kUnused;
         const GrAppliedClip* fAppliedClip = nullptr;
         GrRenderTarget* fRenderTarget = nullptr;
         const GrCaps* fCaps = nullptr;
-        GrXferProcessor::DstTexture fDstTexture;
+        GrResourceProvider* fResourceProvider = nullptr;
+        GrXferProcessor::DstProxy fDstProxy;
     };
 
     /**
@@ -78,8 +91,10 @@
      **/
     GrPipeline(GrRenderTarget*, SkBlendMode);
 
+    GrPipeline(const InitArgs& args) { this->init(args); }
+
     /** (Re)initializes a pipeline. After initialization the pipeline can be used. */
-    GrPipelineOptimizations init(const InitArgs&);
+    void init(const InitArgs&);
 
     /** True if the pipeline has been initialized. */
     bool isInitialized() const { return SkToBool(fRenderTarget.get()); }
@@ -109,10 +124,7 @@
             return false;
         }
         if (a.xferBarrierType(caps)) {
-            return aBounds.fRight <= bBounds.fLeft ||
-                   aBounds.fBottom <= bBounds.fTop ||
-                   bBounds.fRight <= aBounds.fLeft ||
-                   bBounds.fBottom <= aBounds.fTop;
+            return !GrRectsTouchOrOverlap(aBounds, bBounds);
         }
         return true;
     }
@@ -124,7 +136,7 @@
 
     // Make the renderTarget's GrOpList (if it exists) be dependent on any
     // GrOpLists in this pipeline
-    void addDependenciesTo(GrRenderTarget* rt) const;
+    void addDependenciesTo(GrRenderTargetProxy*) const;
 
     int numColorFragmentProcessors() const { return fNumColorProcessors; }
     int numCoverageFragmentProcessors() const {
@@ -133,7 +145,7 @@
     int numFragmentProcessors() const { return fFragmentProcessors.count(); }
 
     const GrXferProcessor& getXferProcessor() const {
-        if (fXferProcessor.get()) {
+        if (fXferProcessor) {
             return *fXferProcessor.get();
         } else {
             // A null xp member means the common src-over case. GrXferProcessor's ref'ing
@@ -142,6 +154,25 @@
         }
     }
 
+    /**
+     * 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 {
+        if (offset) {
+            *offset = fDstTextureOffset;
+        }
+        return fDstTextureProxy.get();
+    }
+
+    GrTexture* peekDstTexture(SkIPoint* offset = nullptr) const {
+        if (GrTextureProxy* dstProxy = this->dstTextureProxy(offset)) {
+            return dstProxy->priv().peekTexture();
+        }
+
+        return nullptr;
+    }
+
     const GrFragmentProcessor& getColorFragmentProcessor(int idx) const {
         SkASSERT(idx < this->numColorFragmentProcessors());
         return *fFragmentProcessors[idx].get();
@@ -181,52 +212,49 @@
     bool getAllowSRGBInputs() const {
         return SkToBool(fFlags & kAllowSRGBInputs_Flag);
     }
-    bool usesDistanceVectorField() const {
-        return SkToBool(fFlags & kUsesDistanceVectorField_Flag);
-    }
     bool hasStencilClip() const {
         return SkToBool(fFlags & kHasStencilClip_Flag);
     }
     bool isStencilEnabled() const {
         return SkToBool(fFlags & kStencilEnabled_Flag);
     }
+    bool isBad() const { return SkToBool(fFlags & kIsBad_Flag); }
 
     GrXferBarrierType xferBarrierType(const GrCaps& caps) const {
-        return this->getXferProcessor().xferBarrierType(fRenderTarget.get(), caps);
+        if (fDstTextureProxy.get() &&
+            fDstTextureProxy.get()->priv().peekTexture() == fRenderTarget.get()->asTexture()) {
+            return kTexture_GrXferBarrierType;
+        }
+        return this->getXferProcessor().xferBarrierType(caps);
     }
 
-    /**
-     * Gets whether the target is drawing clockwise, counterclockwise,
-     * or both faces.
-     * @return the current draw face(s).
-     */
-    GrDrawFace getDrawFace() const { return static_cast<GrDrawFace>(fDrawFace); }
-
 private:
+    void markAsBad() { fFlags |= kIsBad_Flag; }
+
     /** This is a continuation of the public "Flags" enum. */
     enum PrivateFlags {
-        kDisableOutputConversionToSRGB_Flag = 0x4,
-        kAllowSRGBInputs_Flag = 0x8,
-        kUsesDistanceVectorField_Flag = 0x10,
-        kHasStencilClip_Flag = 0x20,
-        kStencilEnabled_Flag = 0x40,
+        kHasStencilClip_Flag = 0x10,
+        kStencilEnabled_Flag = 0x20,
+        kIsBad_Flag = 0x40,
     };
 
-    typedef GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> RenderTarget;
-    typedef GrPendingProgramElement<const GrFragmentProcessor> PendingFragmentProcessor;
-    typedef SkAutoSTArray<8, PendingFragmentProcessor> FragmentProcessorArray;
-    typedef GrPendingProgramElement<const GrXferProcessor> ProgramXferProcessor;
-    RenderTarget                        fRenderTarget;
-    GrScissorState                      fScissorState;
-    GrWindowRectsState                  fWindowRectsState;
-    const GrUserStencilSettings*        fUserStencilSettings;
-    uint16_t                            fDrawFace;
-    uint16_t                            fFlags;
-    ProgramXferProcessor                fXferProcessor;
-    FragmentProcessorArray              fFragmentProcessors;
+    using RenderTarget = GrPendingIOResource<GrRenderTarget, kWrite_GrIOType>;
+    using DstTextureProxy = GrPendingIOResource<GrTextureProxy, kRead_GrIOType>;
+    using PendingFragmentProcessor = GrPendingProgramElement<const GrFragmentProcessor>;
+    using FragmentProcessorArray = SkAutoSTArray<8, PendingFragmentProcessor>;
+
+    DstTextureProxy fDstTextureProxy;
+    SkIPoint fDstTextureOffset;
+    RenderTarget fRenderTarget;
+    GrScissorState fScissorState;
+    GrWindowRectsState fWindowRectsState;
+    const GrUserStencilSettings* fUserStencilSettings;
+    uint16_t fFlags;
+    sk_sp<const GrXferProcessor> fXferProcessor;
+    FragmentProcessorArray fFragmentProcessors;
 
     // This value is also the index in fFragmentProcessors where coverage processors begin.
-    int                                 fNumColorProcessors;
+    int fNumColorProcessors;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/src/gpu/GrPipelineAnalysis.cpp b/src/gpu/GrPipelineAnalysis.cpp
deleted file mode 100644
index 3512c94..0000000
--- a/src/gpu/GrPipelineAnalysis.cpp
+++ /dev/null
@@ -1,41 +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 "GrPipelineAnalysis.h"
-#include "GrGeometryProcessor.h"
-#include "ops/GrDrawOp.h"
-
-void GrColorFragmentProcessorAnalysis::analyzeProcessors(
-        const GrFragmentProcessor* const* processors, int cnt) {
-    for (int i = 0; i < cnt; ++i) {
-        bool knowCurrentOutput = fProcessorsVisitedWithKnownOutput == fTotalProcessorsVisited;
-        if (fUsesLocalCoords && !knowCurrentOutput &&
-            !fAllProcessorsCompatibleWithCoverageAsAlpha && !fIsOpaque) {
-            fTotalProcessorsVisited += cnt - i;
-            return;
-        }
-        const GrFragmentProcessor* fp = processors[i];
-        if (knowCurrentOutput &&
-            fp->hasConstantOutputForConstantInput(fLastKnownOutputColor, &fLastKnownOutputColor)) {
-            ++fProcessorsVisitedWithKnownOutput;
-            fIsOpaque = fLastKnownOutputColor.isOpaque();
-            // We reset these since the caller is expected to not use the earlier fragment
-            // processors.
-            fAllProcessorsCompatibleWithCoverageAsAlpha = true;
-            fUsesLocalCoords = false;
-        } else if (fIsOpaque && !fp->preservesOpaqueInput()) {
-            fIsOpaque = false;
-        }
-        if (fAllProcessorsCompatibleWithCoverageAsAlpha && !fp->compatibleWithCoverageAsAlpha()) {
-            fAllProcessorsCompatibleWithCoverageAsAlpha = false;
-        }
-        if (fp->usesLocalCoords()) {
-            fUsesLocalCoords = true;
-        }
-        ++fTotalProcessorsVisited;
-    }
-}
diff --git a/src/gpu/GrPipelineAnalysis.h b/src/gpu/GrPipelineAnalysis.h
deleted file mode 100644
index 4f553b0..0000000
--- a/src/gpu/GrPipelineAnalysis.h
+++ /dev/null
@@ -1,149 +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 GrPipelineAnalysis_DEFINED
-#define GrPipelineAnalysis_DEFINED
-
-#include "GrColor.h"
-
-class GrDrawOp;
-class GrFragmentProcessor;
-class GrPrimitiveProcessor;
-
-class GrPipelineAnalysisColor {
-public:
-    enum class Opaque {
-        kNo,
-        kYes,
-    };
-
-    GrPipelineAnalysisColor(Opaque opaque = Opaque::kNo)
-            : fFlags(opaque == Opaque::kYes ? kIsOpaque_Flag : 0) {}
-
-    GrPipelineAnalysisColor(GrColor color) { this->setToConstant(color); }
-
-    void setToConstant(GrColor color) {
-        fColor = color;
-        if (GrColorIsOpaque(color)) {
-            fFlags = kColorIsKnown_Flag | kIsOpaque_Flag;
-        } else {
-            fFlags = kColorIsKnown_Flag;
-        }
-    }
-
-    void setToUnknown() { fFlags = 0; }
-
-    void setToUnknownOpaque() { fFlags = kIsOpaque_Flag; }
-
-    bool isOpaque() const { return SkToBool(kIsOpaque_Flag & fFlags); }
-
-    bool isConstant(GrColor* color) const {
-        if (kColorIsKnown_Flag & fFlags) {
-            *color = fColor;
-            return true;
-        }
-        return false;
-    }
-
-private:
-    enum Flags {
-        kColorIsKnown_Flag = 0x1,
-        kIsOpaque_Flag = 0x2,
-    };
-    uint32_t fFlags;
-    GrColor fColor;
-};
-
-enum class GrPipelineAnalysisCoverage { kNone, kSingleChannel, kLCD };
-
-/**
- * GrColorFragmentProcessorAnalysis gathers invariant data from a set of color fragment processor.
- * It is used to recognize optimizations that can simplify the generated shader or make blending
- * more effecient.
- */
-class GrColorFragmentProcessorAnalysis {
-public:
-    GrColorFragmentProcessorAnalysis() = default;
-
-    GrColorFragmentProcessorAnalysis(const GrPipelineAnalysisColor& input)
-            : GrColorFragmentProcessorAnalysis() {
-        fAllProcessorsCompatibleWithCoverageAsAlpha = true;
-        fIsOpaque = input.isOpaque();
-        GrColor color;
-        if (input.isConstant(&color)) {
-            fLastKnownOutputColor = GrColor4f::FromGrColor(color);
-            fProcessorsVisitedWithKnownOutput = 0;
-        }
-    }
-
-    void reset(const GrPipelineAnalysisColor& input) {
-        *this = GrColorFragmentProcessorAnalysis(input);
-    }
-
-    /**
-     * Runs through a series of processors and updates calculated values. This can be called
-     * repeatedly for cases when the sequence of processors is not in a contiguous array.
-     */
-    void analyzeProcessors(const GrFragmentProcessor* const* processors, int cnt);
-
-    bool isOpaque() const { return fIsOpaque; }
-
-    /**
-     * Are all the fragment processors compatible with conflating coverage with color prior to the
-     * the first fragment processor. This result does not consider processors that should be
-     * eliminated as indicated by initialProcessorsToEliminate().
-     */
-    bool allProcessorsCompatibleWithCoverageAsAlpha() const {
-        return fAllProcessorsCompatibleWithCoverageAsAlpha;
-    }
-
-    /**
-     * Do any of the fragment processors require local coords. This result does not consider
-     * processors that should be eliminated as indicated by initialProcessorsToEliminate().
-     */
-    bool usesLocalCoords() const { return fUsesLocalCoords; }
-
-    /**
-     * If we detected that the result after the first N processors is a known color then we
-     * eliminate those N processors and replace the GrDrawOp's color input to the GrPipeline with
-     * the known output of the Nth processor, so that the Nth+1 fragment processor (or the XP if
-     * there are only N processors) sees its expected input. If this returns 0 then there are no
-     * processors to eliminate.
-     */
-    int initialProcessorsToEliminate(GrColor* newPipelineInputColor) const {
-        if (fProcessorsVisitedWithKnownOutput > 0) {
-            *newPipelineInputColor = fLastKnownOutputColor.toGrColor();
-        }
-        return SkTMax(0, fProcessorsVisitedWithKnownOutput);
-    }
-
-    int initialProcessorsToEliminate(GrColor4f* newPipelineInputColor) const {
-        if (fProcessorsVisitedWithKnownOutput > 0) {
-            *newPipelineInputColor = fLastKnownOutputColor;
-        }
-        return SkTMax(0, fProcessorsVisitedWithKnownOutput);
-    }
-
-    GrPipelineAnalysisColor outputColor() const {
-        if (fProcessorsVisitedWithKnownOutput != fTotalProcessorsVisited) {
-            return GrPipelineAnalysisColor(fIsOpaque ? GrPipelineAnalysisColor::Opaque::kYes
-                                                     : GrPipelineAnalysisColor::Opaque::kNo);
-        }
-        return GrPipelineAnalysisColor(fLastKnownOutputColor.toGrColor());
-    }
-
-private:
-    int fTotalProcessorsVisited = 0;
-    // negative one means even the color is unknown before adding the first processor.
-    int fProcessorsVisitedWithKnownOutput = -1;
-    bool fIsOpaque = false;
-    bool fAllProcessorsCompatibleWithCoverageAsAlpha = true;
-    bool fUsesLocalCoords = false;
-    GrColor4f fLastKnownOutputColor;
-};
-
-#endif
diff --git a/src/gpu/GrPipelineBuilder.h b/src/gpu/GrPipelineBuilder.h
index 17dfe90..9bb2c49 100644
--- a/src/gpu/GrPipelineBuilder.h
+++ b/src/gpu/GrPipelineBuilder.h
@@ -29,8 +29,7 @@
      * which is unmodified by this function and clipping which will be enabled.
      */
     GrPipelineBuilder(GrPaint&& paint, GrAAType aaType)
-            : fFlags(0x0)
-            , fDrawFace(GrDrawFace::kBoth)
+            : fFlags(GrPipeline::SRGBFlagsFromPaint(paint))
             , fUserStencilSettings(&GrUserStencilSettings::kUnused)
             , fProcessors(std::move(paint)) {
         if (GrAATypeIsHW(aaType)) {
@@ -63,18 +62,12 @@
 
     const GrProcessorSet& processors() const { return fProcessors; }
 
-    /// @}
-
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Blending
-    ////
-
-    /**
-     * Checks whether the xp will need destination in a texture to correctly blend.
-     */
-    bool willXPNeedDstTexture(const GrCaps& caps,
-                              const GrProcessorSet::FragmentProcessorAnalysis& analysis) const {
-        return GrXPFactory::WillNeedDstTexture(fProcessors.xpFactory(), caps, analysis);
+    GrProcessorSet::Analysis finalizeProcessors(const GrProcessorAnalysisColor& colorInput,
+                                                const GrProcessorAnalysisCoverage coverageInput,
+                                                const GrAppliedClip* clip, bool isMixedSamples,
+                                                const GrCaps& caps, GrColor* overrideColor) {
+        return fProcessors.finalize(colorInput, coverageInput, clip, isMixedSamples, caps,
+                                    overrideColor);
     }
 
     /// @}
@@ -113,31 +106,14 @@
 
     /// @}
 
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Face Culling
-    ////
-
-    /**
-     * Controls whether clockwise, counterclockwise, or both faces are drawn.
-     * @param face  the face(s) to draw.
-     */
-    void setDrawFace(GrDrawFace face) {
-        SkASSERT(GrDrawFace::kInvalid != face);
-        fDrawFace = face;
-    }
-
-    /// @}
-
     void getPipelineInitArgs(GrPipeline::InitArgs* args) const {
         args->fFlags = fFlags;
         args->fUserStencil = fUserStencilSettings;
-        args->fDrawFace = fDrawFace;
         args->fProcessors = &fProcessors;
     }
 
 private:
     uint32_t fFlags;
-    GrDrawFace fDrawFace;
     const GrUserStencilSettings* fUserStencilSettings;
     GrProcessorSet fProcessors;
 };
diff --git a/src/gpu/GrPreFlushResourceProvider.cpp b/src/gpu/GrPreFlushResourceProvider.cpp
deleted file mode 100644
index e907f39..0000000
--- a/src/gpu/GrPreFlushResourceProvider.cpp
+++ /dev/null
@@ -1,65 +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 "GrPreFlushResourceProvider.h"
-
-#include "GrDrawingManager.h"
-#include "GrSurfaceProxy.h"
-
-sk_sp<GrRenderTargetContext> GrPreFlushResourceProvider::makeRenderTargetContext(
-                                                        const GrSurfaceDesc& desc,
-                                                        sk_sp<SkColorSpace> colorSpace,
-                                                        const SkSurfaceProps* props) {
-    GrSurfaceDesc tmpDesc = desc;
-    tmpDesc.fFlags |= kRenderTarget_GrSurfaceFlag;
-
-    // Because this is being allocated at the start of a flush we must ensure the proxy
-    // will, when instantiated, have no pending IO.
-    // TODO: fold the kNoPendingIO_Flag into GrSurfaceFlags?
-    sk_sp<GrSurfaceProxy> proxy = GrSurfaceProxy::MakeDeferred(
-                                                    fDrawingMgr->getContext()->resourceProvider(),
-                                                    tmpDesc,
-                                                    SkBackingFit::kExact,
-                                                    SkBudgeted::kYes,
-                                                    GrResourceProvider::kNoPendingIO_Flag);
-    if (!proxy->asRenderTargetProxy()) {
-        return nullptr;
-    }
-
-    sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(
-                                                    proxy->asRenderTargetProxy(),
-                                                    fDrawingMgr->fContext->getGpu(),
-                                                    fDrawingMgr->fContext->resourceProvider(),
-                                                    fDrawingMgr->fContext->getAuditTrail(),
-                                                    fDrawingMgr->fOptionsForOpLists));
-    proxy->setLastOpList(opList.get());
-
-    return fDrawingMgr->makeRenderTargetContext(std::move(proxy),
-                                                std::move(colorSpace),
-                                                props);
-}
-
-// TODO: we only need this entry point as long as we have to pre-allocate the atlas.
-// Remove it ASAP.
-sk_sp<GrRenderTargetContext> GrPreFlushResourceProvider::makeRenderTargetContext(
-                                                        sk_sp<GrSurfaceProxy> proxy,
-                                                        sk_sp<SkColorSpace> colorSpace,
-                                                        const SkSurfaceProps* props) {
-
-    sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(
-                                                    proxy->asRenderTargetProxy(),
-                                                    fDrawingMgr->fContext->getGpu(),
-                                                    fDrawingMgr->fContext->resourceProvider(),
-                                                    fDrawingMgr->fContext->getAuditTrail(),
-                                                    fDrawingMgr->fOptionsForOpLists));
-    proxy->setLastOpList(opList.get());
-
-    return fDrawingMgr->makeRenderTargetContext(std::move(proxy),
-                                                std::move(colorSpace),
-                                                props);
-}
-
diff --git a/src/gpu/GrPreFlushResourceProvider.h b/src/gpu/GrPreFlushResourceProvider.h
deleted file mode 100644
index 86a299a..0000000
--- a/src/gpu/GrPreFlushResourceProvider.h
+++ /dev/null
@@ -1,77 +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 GrPreFlushResourceProvider_DEFINED
-#define GrPreFlushResourceProvider_DEFINED
-
-#include "GrTypes.h"
-#include "GrNonAtomicRef.h"
-
-// These two are just for GrPreFlushCallbackObject
-#include "SkRefCnt.h"
-#include "SkTDArray.h"
-
-class GrDrawingManager;
-class GrOpList;
-class GrPreFlushResourceProvider;
-class GrRenderTargetOpList;
-class GrRenderTargetContext;
-class GrSurfaceProxy;
-
-class SkColorSpace;
-class SkSurfaceProps;
-
-/*
- * This is the base class from which all per-flush callback objects must be derived. It
- * provides the "preFlush" interface.
- */
-class GrPreFlushCallbackObject : public GrNonAtomicRef<GrPreFlushCallbackObject> {
-public:
-    virtual ~GrPreFlushCallbackObject() { }
-
-    /*
-     * The preFlush callback allows subsystems (e.g., text, path renderers) to create atlases
-     * for a specific flush. All the GrOpList IDs required for the flush are passed into the
-     * callback. The callback should return the render target contexts used to render the atlases
-     * in 'results'.
-     */
-    virtual void preFlush(GrPreFlushResourceProvider*,
-                          const uint32_t* opListIDs, int numOpListIDs,
-                          SkTArray<sk_sp<GrRenderTargetContext>>* results) = 0;
-
-private:
-    typedef SkRefCnt INHERITED;
-};
-
-/*
- * This class is a shallow wrapper around the drawing manager. It is passed into the
- * preFlush callbacks and is intended to limit the functionality available to them.
- * It should never have additional data members or virtual methods.
- */
-class GrPreFlushResourceProvider {
-public:
-    sk_sp<GrRenderTargetContext> makeRenderTargetContext(const GrSurfaceDesc& desc,
-                                                         sk_sp<SkColorSpace> colorSpace,
-                                                         const SkSurfaceProps* props);
-
-    // TODO: we only need this entry point as long as we have to pre-allocate the atlas.
-    // Remove it ASAP.
-    sk_sp<GrRenderTargetContext> makeRenderTargetContext(sk_sp<GrSurfaceProxy> proxy,
-                                                         sk_sp<SkColorSpace> colorSpace,
-                                                         const SkSurfaceProps* props);
-
-private:
-    explicit GrPreFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {}
-    GrPreFlushResourceProvider(const GrPreFlushResourceProvider&); // unimpl
-    GrPreFlushResourceProvider& operator=(const GrPreFlushResourceProvider&); // unimpl
-
-    GrDrawingManager* fDrawingMgr;
-
-    friend class GrDrawingManager; // to construct this type.
-};
-
-#endif
diff --git a/src/gpu/GrPrimitiveProcessor.cpp b/src/gpu/GrPrimitiveProcessor.cpp
index 2aaaa04..b981977 100644
--- a/src/gpu/GrPrimitiveProcessor.cpp
+++ b/src/gpu/GrPrimitiveProcessor.cpp
@@ -10,23 +10,15 @@
 #include "GrCoordTransform.h"
 
 /**
- * The key for an individual coord transform is made up of a matrix type, a precision, and a bit
- * that indicates the source of the input coords.
+ * The key for an individual coord transform is made up of a matrix type, and a bit that indicates
+ * the source of the input coords.
  */
 enum {
     kMatrixTypeKeyBits   = 1,
-    kMatrixTypeKeyMask   = (1 << kMatrixTypeKeyBits) - 1,
-
-    kPrecisionBits       = 2,
-    kPrecisionShift      = kMatrixTypeKeyBits,
-
-    kPositionCoords_Flag = (1 << (kPrecisionShift + kPrecisionBits)),
-
-    kTransformKeyBits    = kMatrixTypeKeyBits + kPrecisionBits + 2,
+    kPositionCoords_Flag = 1 << kMatrixTypeKeyBits,
+    kTransformKeyBits    = kMatrixTypeKeyBits + 1,
 };
 
-GR_STATIC_ASSERT(kHigh_GrSLPrecision < (1 << kPrecisionBits));
-
 /**
  * We specialize the vertex code for each of these matrix types.
  */
@@ -52,9 +44,6 @@
             key |= kPositionCoords_Flag;
         }
 
-        GR_STATIC_ASSERT(kGrSLPrecisionCount <= (1 << kPrecisionBits));
-        key |= (coordTransform->precision() << kPrecisionShift);
-
         key <<= kTransformKeyBits * t;
 
         SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to overlap
diff --git a/src/gpu/GrPrimitiveProcessor.h b/src/gpu/GrPrimitiveProcessor.h
index 4c43943..3b90689 100644
--- a/src/gpu/GrPrimitiveProcessor.h
+++ b/src/gpu/GrPrimitiveProcessor.h
@@ -24,7 +24,7 @@
  * GrPrimitiveProcessor. These loops run on the CPU and to determine known properties of the final
  * color and coverage inputs to the GrXferProcessor in order to perform optimizations that preserve
  * correctness. The GrDrawOp seeds these loops with initial color and coverage, in its
- * getFragmentProcessorAnalysisInputs implementation. These seed values are processed by the
+ * getProcessorAnalysisInputs implementation. These seed values are processed by the
  * subsequent
  * stages of the rendering pipeline and the output is then fed back into the GrDrawOp in
  * the applyPipelineOptimizations call, where the op can use the information to inform decisions
@@ -33,97 +33,52 @@
 
 class GrGLSLPrimitiveProcessor;
 
-struct GrInitInvariantOutput;
-
-/*
- * This class allows the GrPipeline to communicate information about the pipeline to a GrOp which
- * inform its decisions for GrPrimitiveProcessor setup. These are not properly part of the pipeline
- * because they reflect the specific inputs that the op provided to perform the analysis (e.g. that
- * the GrGeometryProcessor would output an opaque color).
- *
- * The pipeline analysis that produced this may have decided to elide some GrProcessors. However,
- * those elisions may depend upon changing the color output by the GrGeometryProcessor used by the
- * GrDrawOp. The op must check getOverrideColorIfSet() for this.
- */
-class GrPipelineOptimizations {
-public:
-    /** Does the pipeline require access to (implicit or explicit) local coordinates? */
-    bool readsLocalCoords() const {
-        return SkToBool(kReadsLocalCoords_Flag & fFlags);
-    }
-
-    /** Does the pipeline allow the GrPrimitiveProcessor to combine color and coverage into one
-        color output ? */
-    bool canTweakAlphaForCoverage() const {
-        return SkToBool(kCanTweakAlphaForCoverage_Flag & fFlags);
-    }
-
-    /** Does the pipeline require the GrPrimitiveProcessor to specify a specific color (and if
-        so get the color)? */
-    bool getOverrideColorIfSet(GrColor* overrideColor) const {
-        if (SkToBool(kUseOverrideColor_Flag & fFlags)) {
-            if (overrideColor) {
-                *overrideColor = fOverrideColor;
-            }
-            return true;
-        }
-        return false;
-    }
-
-private:
-    enum {
-        // If this is not set the primitive processor need not produce local coordinates
-        kReadsLocalCoords_Flag = 0x1,
-
-        // If this flag is set then the primitive processor may produce color*coverage as
-        // its color output (and not output a separate coverage).
-        kCanTweakAlphaForCoverage_Flag = 0x2,
-
-        // If this flag is set the GrPrimitiveProcessor must produce fOverrideColor as its
-        // output color. If not set fOverrideColor is to be ignored.
-        kUseOverrideColor_Flag = 0x4,
-    };
-
-    uint32_t    fFlags;
-    GrColor     fOverrideColor;
-
-    friend class GrPipeline; // To initialize this
-};
-
 /*
  * GrPrimitiveProcessor defines an interface which all subclasses must implement.  All
  * GrPrimitiveProcessors must proivide seed color and coverage for the Ganesh color / coverage
  * pipelines, and they must provide some notion of equality
  */
-class GrPrimitiveProcessor : public GrProcessor {
+class GrPrimitiveProcessor : public GrResourceIOProcessor, public GrProgramElement {
 public:
-    // Only the GrGeometryProcessor subclass actually has a geo shader or vertex attributes, but
-    // we put these calls on the base class to prevent having to cast
-    virtual bool willUseGeoShader() const = 0;
-
     struct Attribute {
-        Attribute()
-            : fName(nullptr)
-            , fType(kFloat_GrVertexAttribType)
-            , fOffset(0) {}
-        Attribute(const char* name, GrVertexAttribType type, GrSLPrecision precision)
-            : fName(name)
-            , fType(type)
-            , fOffset(SkAlign4(GrVertexAttribTypeSize(type)))
-            , fPrecision(precision) {}
-        const char* fName;
-        GrVertexAttribType fType;
-        size_t fOffset;
-        GrSLPrecision fPrecision;
+        enum class InputRate : bool {
+            kPerVertex,
+            kPerInstance
+        };
+
+        const char*          fName;
+        GrVertexAttribType   fType;
+        int                  fOffsetInRecord;
+        GrSLPrecision        fPrecision;
+        InputRate            fInputRate;
     };
 
     int numAttribs() const { return fAttribs.count(); }
     const Attribute& getAttrib(int index) const { return fAttribs[index]; }
 
-    // Returns the vertex stride of the GP.  A common use case is to request geometry from a
-    // GrOpList based off of the stride, and to populate this memory using an implicit array of
-    // structs.  In this case, it is best to assert the vertexstride == sizeof(VertexStruct).
-    size_t getVertexStride() const { return fVertexStride; }
+    bool hasVertexAttribs() const { return SkToBool(fVertexStride); }
+    bool hasInstanceAttribs() const { return SkToBool(fInstanceStride); }
+
+    /**
+     * These return the strides of the vertex and instance buffers. Attributes are expected to be
+     * laid out interleaved in their corresponding buffer (vertex or instance). fOffsetInRecord
+     * indicates an attribute's location in bytes relative to the first attribute. (These are padded
+     * to the nearest 4 bytes for performance reasons.)
+     *
+     * A common practice is to populate the buffer's memory using an implicit array of structs. In
+     * this case, it is best to assert:
+     *
+     *     stride == sizeof(struct) and
+     *     offsetof(struct, field[i]) == attrib[i].fOffsetInRecord
+     *
+     * NOTE: for instanced draws the vertex buffer has a single record that each instance reuses.
+     */
+    int getVertexStride() const { return fVertexStride; }
+    int getInstanceStride() const { return fInstanceStride; }
+
+    // Only the GrGeometryProcessor subclass actually has a geo shader or vertex attributes, but
+    // we put these calls on the base class to prevent having to cast
+    virtual bool willUseGeoShader() const = 0;
 
     /**
      * Computes a transformKey from an array of coord transforms. Will only look at the first
@@ -159,21 +114,38 @@
         return 0.0;
     }
 
-    /* Sub-class should override and return true if this primitive processor implements the distance
-     * vector field, a field of vectors to the nearest point in the edge of the shape.  */
-    virtual bool implementsDistanceVector() const { return false; }
-
 protected:
-    GrPrimitiveProcessor() : fVertexStride(0) {}
-
-    enum { kPreallocAttribCnt = 8 };
-    SkSTArray<kPreallocAttribCnt, Attribute> fAttribs;
-    size_t fVertexStride;
+    /**
+     * Subclasses call these from their constructor to register vertex and instance attributes.
+     */
+    const Attribute& addVertexAttrib(const char* name, GrVertexAttribType type,
+                                     GrSLPrecision precision = kDefault_GrSLPrecision) {
+        precision = (kDefault_GrSLPrecision == precision) ? kMedium_GrSLPrecision : precision;
+        fAttribs.push_back() = {name, type, fVertexStride, precision,
+                                Attribute::InputRate::kPerVertex};
+        fVertexStride += static_cast<int>(SkAlign4(GrVertexAttribTypeSize(type)));
+        return fAttribs.back();
+    }
+    const Attribute& addInstanceAttrib(const char* name, GrVertexAttribType type,
+                                       GrSLPrecision precision = kDefault_GrSLPrecision) {
+        precision = (kDefault_GrSLPrecision == precision) ? kMedium_GrSLPrecision : precision;
+        fAttribs.push_back() = {name, type, fInstanceStride, precision,
+                                Attribute::InputRate::kPerInstance};
+        fInstanceStride += static_cast<int>(SkAlign4(GrVertexAttribTypeSize(type)));
+        return fAttribs.back();
+    }
 
 private:
+    void addPendingIOs() const override { GrResourceIOProcessor::addPendingIOs(); }
+    void removeRefs() const override { GrResourceIOProcessor::removeRefs(); }
+    void pendingIOComplete() const override { GrResourceIOProcessor::pendingIOComplete(); }
     void notifyRefCntIsZero() const final {}
     virtual bool hasExplicitLocalCoords() const = 0;
 
+    SkSTArray<8, Attribute>   fAttribs;
+    int                       fVertexStride = 0;
+    int                       fInstanceStride = 0;
+
     typedef GrProcessor INHERITED;
 };
 
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 4ab5468..51cfdfb 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -57,7 +57,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 41;
+static const int kFPFactoryCount = 42;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 4;
 
@@ -120,65 +120,82 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrProcessor::~GrProcessor() {}
-
-void GrProcessor::addTextureSampler(const TextureSampler* access) {
-    fTextureSamplers.push_back(access);
-}
-
-void GrProcessor::addBufferAccess(const BufferAccess* access) {
-    fBufferAccesses.push_back(access);
-}
-
-void GrProcessor::addImageStorageAccess(const ImageStorageAccess* access) {
-    fImageStorageAccesses.push_back(access);
-}
-
-void GrProcessor::addPendingIOs() const {
-    for (const auto& sampler : fTextureSamplers) {
-        sampler->programTexture()->markPendingIO();
-    }
-    for (const auto& buffer : fBufferAccesses) {
-        buffer->programBuffer()->markPendingIO();
-    }
-    for (const auto& imageStorage : fImageStorageAccesses) {
-        imageStorage->programTexture()->markPendingIO();
-    }
-}
-
-void GrProcessor::removeRefs() const {
-    for (const auto& sampler : fTextureSamplers) {
-        sampler->programTexture()->removeRef();
-    }
-    for (const auto& buffer : fBufferAccesses) {
-        buffer->programBuffer()->removeRef();
-    }
-    for (const auto& imageStorage : fImageStorageAccesses) {
-        imageStorage->programTexture()->removeRef();
-    }
-}
-
-void GrProcessor::pendingIOComplete() const {
-    for (const auto& sampler : fTextureSamplers) {
-        sampler->programTexture()->pendingIOComplete();
-    }
-    for (const auto& buffer : fBufferAccesses) {
-        buffer->programBuffer()->pendingIOComplete();
-    }
-    for (const auto& imageStorage : fImageStorageAccesses) {
-        imageStorage->programTexture()->pendingIOComplete();
-    }
-}
-
-void* GrProcessor::operator new(size_t size) {
-    return MemoryPoolAccessor().pool()->allocate(size);
-}
+void* GrProcessor::operator new(size_t size) { return MemoryPoolAccessor().pool()->allocate(size); }
 
 void GrProcessor::operator delete(void* target) {
     return MemoryPoolAccessor().pool()->release(target);
 }
 
-bool GrProcessor::hasSameSamplersAndAccesses(const GrProcessor &that) const {
+///////////////////////////////////////////////////////////////////////////////
+
+void GrResourceIOProcessor::addTextureSampler(const TextureSampler* access) {
+    fTextureSamplers.push_back(access);
+}
+
+void GrResourceIOProcessor::addBufferAccess(const BufferAccess* access) {
+    fBufferAccesses.push_back(access);
+}
+
+void GrResourceIOProcessor::addImageStorageAccess(GrResourceProvider* resourceProvider,
+                                                  const ImageStorageAccess* access) {
+    fImageStorageAccesses.push_back(access);
+}
+
+void GrResourceIOProcessor::addPendingIOs() const {
+    for (const auto& sampler : fTextureSamplers) {
+        sampler->programProxy()->markPendingIO();
+    }
+    for (const auto& buffer : fBufferAccesses) {
+        buffer->programBuffer()->markPendingIO();
+    }
+    for (const auto& imageStorage : fImageStorageAccesses) {
+        imageStorage->programProxy()->markPendingIO();
+    }
+}
+
+void GrResourceIOProcessor::removeRefs() const {
+    for (const auto& sampler : fTextureSamplers) {
+        sampler->programProxy()->removeRef();
+    }
+    for (const auto& buffer : fBufferAccesses) {
+        buffer->programBuffer()->removeRef();
+    }
+    for (const auto& imageStorage : fImageStorageAccesses) {
+        imageStorage->programProxy()->removeRef();
+    }
+}
+
+void GrResourceIOProcessor::pendingIOComplete() const {
+    for (const auto& sampler : fTextureSamplers) {
+        sampler->programProxy()->pendingIOComplete();
+    }
+    for (const auto& buffer : fBufferAccesses) {
+        buffer->programBuffer()->pendingIOComplete();
+    }
+    for (const auto& imageStorage : fImageStorageAccesses) {
+        imageStorage->programProxy()->pendingIOComplete();
+    }
+}
+
+bool GrResourceIOProcessor::instantiate(GrResourceProvider* resourceProvider) const {
+    for (const auto& sampler : fTextureSamplers) {
+        if (!sampler->instantiate(resourceProvider)) {
+            return false;
+        }
+    }
+
+    // MDB TODO: instantiate 'fBufferAccesses' here as well
+
+    for (const auto& imageStorage : fImageStorageAccesses) {
+        if (!imageStorage->instantiate(resourceProvider)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool GrResourceIOProcessor::hasSameSamplersAndAccesses(const GrResourceIOProcessor& that) const {
     if (this->numTextureSamplers() != that.numTextureSamplers() ||
         this->numBuffers() != that.numBuffers() ||
         this->numImageStorages() != that.numImageStorages()) {
@@ -204,97 +221,59 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-GrProcessor::TextureSampler::TextureSampler() {}
+GrResourceIOProcessor::TextureSampler::TextureSampler() {}
 
-GrProcessor::TextureSampler::TextureSampler(GrTexture* texture, const GrSamplerParams& params) {
-    this->reset(texture, params);
-}
-
-GrProcessor::TextureSampler::TextureSampler(GrTexture* texture,
-                                            GrSamplerParams::FilterMode filterMode,
-                                            SkShader::TileMode tileXAndY,
-                                            GrShaderFlags visibility) {
-    this->reset(texture, filterMode, tileXAndY, visibility);
-}
-
-GrProcessor::TextureSampler::TextureSampler(GrResourceProvider* resourceProvider,
-                                            sk_sp<GrTextureProxy> proxy,
-                                            const GrSamplerParams& params) {
+GrResourceIOProcessor::TextureSampler::TextureSampler(GrResourceProvider* resourceProvider,
+                                                      sk_sp<GrTextureProxy> proxy,
+                                                      const GrSamplerParams& params) {
     this->reset(resourceProvider, std::move(proxy), params);
 }
 
-GrProcessor::TextureSampler::TextureSampler(GrResourceProvider* resourceProvider,
-                                            sk_sp<GrTextureProxy> proxy,
-                                            GrSamplerParams::FilterMode filterMode,
-                                            SkShader::TileMode tileXAndY,
-                                            GrShaderFlags visibility) {
+GrResourceIOProcessor::TextureSampler::TextureSampler(GrResourceProvider* resourceProvider,
+                                                      sk_sp<GrTextureProxy> proxy,
+                                                      GrSamplerParams::FilterMode filterMode,
+                                                      SkShader::TileMode tileXAndY,
+                                                      GrShaderFlags visibility) {
     this->reset(resourceProvider, std::move(proxy), filterMode, tileXAndY, visibility);
 }
 
-void GrProcessor::TextureSampler::reset(GrTexture* texture,
-                                        const GrSamplerParams& params,
-                                        GrShaderFlags visibility) {
-    SkASSERT(texture);
-    fTexture.set(SkRef(texture), kRead_GrIOType);
+void GrResourceIOProcessor::TextureSampler::reset(GrResourceProvider* resourceProvider,
+                                                  sk_sp<GrTextureProxy> proxy,
+                                                  const GrSamplerParams& params,
+                                                  GrShaderFlags visibility) {
     fParams = params;
-    fParams.setFilterMode(SkTMin(params.filterMode(), texture->texturePriv().highestFilterMode()));
+    fProxyRef.setProxy(std::move(proxy), kRead_GrIOType);
+    fParams.setFilterMode(SkTMin(params.filterMode(), this->proxy()->highestFilterMode()));
     fVisibility = visibility;
 }
 
-void GrProcessor::TextureSampler::reset(GrTexture* texture,
-                                        GrSamplerParams::FilterMode filterMode,
-                                        SkShader::TileMode tileXAndY,
-                                        GrShaderFlags visibility) {
-    SkASSERT(texture);
-    fTexture.set(SkRef(texture), kRead_GrIOType);
-    filterMode = SkTMin(filterMode, texture->texturePriv().highestFilterMode());
-    fParams.reset(tileXAndY, filterMode);
-    fVisibility = visibility;
-}
-
-void GrProcessor::TextureSampler::reset(GrResourceProvider* resourceProvider,
-                                        sk_sp<GrTextureProxy> proxy,
-                                        const GrSamplerParams& params,
-                                        GrShaderFlags visibility) {
-    // For now, end the deferral at this time. Once all the TextureSamplers are swapped over
-    // to taking a GrSurfaceProxy just use the IORefs on the proxy
-    GrTexture* texture = proxy->instantiate(resourceProvider);
-    SkASSERT(texture);
-    fTexture.set(SkRef(texture), kRead_GrIOType);
-    fParams = params;
-    fParams.setFilterMode(SkTMin(params.filterMode(), texture->texturePriv().highestFilterMode()));
-    fVisibility = visibility;
-}
-
-void GrProcessor::TextureSampler::reset(GrResourceProvider* resourceProvider,
-                                        sk_sp<GrTextureProxy> proxy,
-                                        GrSamplerParams::FilterMode filterMode,
-                                        SkShader::TileMode tileXAndY,
-                                        GrShaderFlags visibility) {
-    // For now, end the deferral at this time. Once all the TextureSamplers are swapped over
-    // to taking a GrSurfaceProxy just use the IORefs on the proxy
-    GrTexture* texture = proxy->instantiate(resourceProvider);
-    SkASSERT(texture);
-    fTexture.set(SkRef(texture), kRead_GrIOType);
-    filterMode = SkTMin(filterMode, texture->texturePriv().highestFilterMode());
+void GrResourceIOProcessor::TextureSampler::reset(GrResourceProvider* resourceProvider,
+                                                  sk_sp<GrTextureProxy> proxy,
+                                                  GrSamplerParams::FilterMode filterMode,
+                                                  SkShader::TileMode tileXAndY,
+                                                  GrShaderFlags visibility) {
+    fProxyRef.setProxy(std::move(proxy), kRead_GrIOType);
+    filterMode = SkTMin(filterMode, this->proxy()->highestFilterMode());
     fParams.reset(tileXAndY, filterMode);
     fVisibility = visibility;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-GrProcessor::ImageStorageAccess::ImageStorageAccess(sk_sp<GrTexture> texture, GrIOType ioType,
-                                                    GrSLMemoryModel memoryModel,
-                                                    GrSLRestrict restrict,
-                                                    GrShaderFlags visibility) {
-    SkASSERT(texture);
-    fTexture.set(texture.release(), ioType);
+GrResourceIOProcessor::ImageStorageAccess::ImageStorageAccess(sk_sp<GrTextureProxy> proxy,
+                                                              GrIOType ioType,
+                                                              GrSLMemoryModel memoryModel,
+                                                              GrSLRestrict restrict,
+                                                              GrShaderFlags visibility)
+        : fProxyRef(std::move(proxy), ioType) {
+    SkASSERT(fProxyRef.get());
+
     fMemoryModel = memoryModel;
     fRestrict = restrict;
     fVisibility = visibility;
     // We currently infer this from the config. However, we could allow the client to specify
     // a format that is different but compatible with the config.
-    switch (fTexture.get()->config()) {
+    switch (fProxyRef.get()->config()) {
         case kRGBA_8888_GrPixelConfig:
             fFormat = GrImageStorageFormat::kRGBA8;
             break;
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
new file mode 100644
index 0000000..7a12b6c
--- /dev/null
+++ b/src/gpu/GrProcessor.h
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrProcessor_DEFINED
+#define GrProcessor_DEFINED
+
+#include "GrColor.h"
+#include "GrBuffer.h"
+#include "GrGpuResourceRef.h"
+#include "GrProcessorUnitTest.h"
+#include "GrProgramElement.h"
+#include "GrSamplerParams.h"
+#include "GrShaderVar.h"
+#include "GrSurfaceProxyPriv.h"
+#include "SkMath.h"
+#include "SkString.h"
+#include "../private/SkAtomics.h"
+
+class GrContext;
+class GrCoordTransform;
+class GrInvariantOutput;
+class GrResourceProvider;
+class GrTextureProxy;
+
+/**
+ * Used by processors to build their keys. It incorporates each per-processor key into a larger
+ * shader key.
+ */
+class GrProcessorKeyBuilder {
+public:
+    GrProcessorKeyBuilder(SkTArray<unsigned char, true>* data) : fData(data), fCount(0) {
+        SkASSERT(0 == fData->count() % sizeof(uint32_t));
+    }
+
+    void add32(uint32_t v) {
+        ++fCount;
+        fData->push_back_n(4, reinterpret_cast<uint8_t*>(&v));
+    }
+
+    /** Inserts count uint32_ts into the key. The returned pointer is only valid until the next
+        add*() call. */
+    uint32_t* SK_WARN_UNUSED_RESULT add32n(int count) {
+        SkASSERT(count > 0);
+        fCount += count;
+        return reinterpret_cast<uint32_t*>(fData->push_back_n(4 * count));
+    }
+
+    size_t size() const { return sizeof(uint32_t) * fCount; }
+
+private:
+    SkTArray<uint8_t, true>* fData; // unowned ptr to the larger key.
+    int fCount;                     // number of uint32_ts added to fData by the processor.
+};
+
+/** Provides custom shader code to the Ganesh shading pipeline. GrProcessor objects *must* be
+    immutable: after being constructed, their fields may not change.
+
+    Dynamically allocated GrProcessors are managed by a per-thread memory pool. The ref count of an
+    processor must reach 0 before the thread terminates and the pool is destroyed.
+ */
+class GrProcessor {
+public:
+    virtual ~GrProcessor() = default;
+
+    /** Human-meaningful string to identify this prcoessor; may be embedded in generated shader
+        code. */
+    virtual const char* name() const = 0;
+
+    /** Human-readable dump of all information */
+    virtual SkString dumpInfo() const {
+        SkString str;
+        str.appendf("Missing data");
+        return str;
+    }
+
+    /**
+     * Platform specific built-in features that a processor can request for the fragment shader.
+     */
+    enum RequiredFeatures {
+        kNone_RequiredFeatures             = 0,
+        kSampleLocations_RequiredFeature   = 1 << 0
+    };
+
+    GR_DECL_BITFIELD_OPS_FRIENDS(RequiredFeatures);
+
+    RequiredFeatures requiredFeatures() const { return fRequiredFeatures; }
+
+    void* operator new(size_t size);
+    void operator delete(void* target);
+
+    void* operator new(size_t size, void* placement) {
+        return ::operator new(size, placement);
+    }
+    void operator delete(void* target, void* placement) {
+        ::operator delete(target, placement);
+    }
+
+    /** Helper for down-casting to a GrProcessor subclass */
+    template <typename T> const T& cast() const { return *static_cast<const T*>(this); }
+
+    uint32_t classID() const { SkASSERT(kIllegalProcessorClassID != fClassID); return fClassID; }
+
+protected:
+    GrProcessor() : fClassID(kIllegalProcessorClassID), fRequiredFeatures(kNone_RequiredFeatures) {}
+
+    /**
+     * If the prcoessor will generate code that uses platform specific built-in features, then it
+     * must call these methods from its constructor. Otherwise, requests to use these features will
+     * be denied.
+     */
+    void setWillUseSampleLocations() { fRequiredFeatures |= kSampleLocations_RequiredFeature; }
+
+    void combineRequiredFeatures(const GrProcessor& other) {
+        fRequiredFeatures |= other.fRequiredFeatures;
+    }
+
+    template <typename PROC_SUBCLASS> void initClassID() {
+         static uint32_t kClassID = GenClassID();
+         fClassID = kClassID;
+    }
+
+private:
+    GrProcessor(const GrProcessor&) = delete;
+    GrProcessor& operator=(const GrProcessor&) = delete;
+
+    static uint32_t GenClassID() {
+        // fCurrProcessorClassID has been initialized to kIllegalProcessorClassID. The
+        // atomic inc returns the old value not the incremented value. So we add
+        // 1 to the returned value.
+        uint32_t id = static_cast<uint32_t>(sk_atomic_inc(&gCurrProcessorClassID)) + 1;
+        if (!id) {
+            SkFAIL("This should never wrap as it should only be called once for each GrProcessor "
+                   "subclass.");
+        }
+        return id;
+    }
+
+    enum {
+        kIllegalProcessorClassID = 0,
+    };
+    static int32_t gCurrProcessorClassID;
+
+    uint32_t                                        fClassID;
+    RequiredFeatures                                fRequiredFeatures;
+};
+
+GR_MAKE_BITFIELD_OPS(GrProcessor::RequiredFeatures);
+
+/** A GrProcessor with the ability to access textures, buffers, and image storages. */
+class GrResourceIOProcessor : public GrProcessor {
+public:
+    class TextureSampler;
+    class BufferAccess;
+    class ImageStorageAccess;
+
+    int numTextureSamplers() const { return fTextureSamplers.count(); }
+
+    /** Returns the access pattern for the texture at index. index must be valid according to
+        numTextureSamplers(). */
+    const TextureSampler& textureSampler(int index) const { return *fTextureSamplers[index]; }
+
+    int numBuffers() const { return fBufferAccesses.count(); }
+
+    /** Returns the access pattern for the buffer at index. index must be valid according to
+        numBuffers(). */
+    const BufferAccess& bufferAccess(int index) const { return *fBufferAccesses[index]; }
+
+    int numImageStorages() const { return fImageStorageAccesses.count(); }
+
+    /** Returns the access object for the image at index. index must be valid according to
+        numImages(). */
+    const ImageStorageAccess& imageStorageAccess(int index) const {
+        return *fImageStorageAccesses[index];
+    }
+
+    bool instantiate(GrResourceProvider* resourceProvider) const;
+
+protected:
+    GrResourceIOProcessor() {}
+
+    /**
+     * Subclasses call these from their constructor to register sampler/image sources. The processor
+     * subclass manages the lifetime of the objects (these functions only store pointers). The
+     * TextureSampler and/or BufferAccess instances are typically member fields of the GrProcessor
+     * subclass. These must only be called from the constructor because GrProcessors are immutable.
+     */
+    void addTextureSampler(const TextureSampler*);
+    void addBufferAccess(const BufferAccess*);
+    void addImageStorageAccess(GrResourceProvider* resourceProvider, const ImageStorageAccess*);
+
+    bool hasSameSamplersAndAccesses(const GrResourceIOProcessor&) const;
+
+    // These methods can be used by derived classes that also derive from GrProgramElement.
+    void addPendingIOs() const;
+    void removeRefs() const;
+    void pendingIOComplete() const;
+
+private:
+    SkSTArray<4, const TextureSampler*, true> fTextureSamplers;
+    SkSTArray<1, const BufferAccess*, true> fBufferAccesses;
+    SkSTArray<1, const ImageStorageAccess*, true> fImageStorageAccesses;
+
+    typedef GrProcessor INHERITED;
+};
+
+/**
+ * Used to represent a texture that is required by a GrResourceIOProcessor. It holds a GrTexture
+ * along with an associated GrSamplerParams. TextureSamplers don't perform any coord manipulation to
+ * account for texture origin.
+ */
+class GrResourceIOProcessor::TextureSampler : public SkNoncopyable {
+public:
+    /**
+     * Must be initialized before adding to a GrProcessor's texture access list.
+     */
+    TextureSampler();
+
+    // MDB TODO: ultimately we shouldn't need the resource provider parameter
+    TextureSampler(GrResourceProvider*, sk_sp<GrTextureProxy>, const GrSamplerParams&);
+    explicit TextureSampler(GrResourceProvider*, sk_sp<GrTextureProxy>,
+                            GrSamplerParams::FilterMode = GrSamplerParams::kNone_FilterMode,
+                            SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
+                            GrShaderFlags visibility = kFragment_GrShaderFlag);
+    void reset(GrResourceProvider*, sk_sp<GrTextureProxy>, const GrSamplerParams&,
+               GrShaderFlags visibility = kFragment_GrShaderFlag);
+    void reset(GrResourceProvider*, sk_sp<GrTextureProxy>,
+               GrSamplerParams::FilterMode = GrSamplerParams::kNone_FilterMode,
+               SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode,
+               GrShaderFlags visibility = kFragment_GrShaderFlag);
+
+    bool operator==(const TextureSampler& that) const {
+        return this->proxy() == that.proxy() &&
+               fParams == that.fParams &&
+               fVisibility == that.fVisibility;
+    }
+
+    bool operator!=(const TextureSampler& other) const { return !(*this == other); }
+
+    // 'instantiate' should only ever be called at flush time.
+    bool instantiate(GrResourceProvider* resourceProvider) const {
+        return SkToBool(fProxyRef.get()->instantiate(resourceProvider));
+    }
+
+    // 'peekTexture' should only ever be called after a successful 'instantiate' call
+    GrTexture* peekTexture() const {
+        SkASSERT(fProxyRef.get()->priv().peekTexture());
+        return fProxyRef.get()->priv().peekTexture();
+    }
+
+    GrTextureProxy* proxy() const { return fProxyRef.get()->asTextureProxy(); }
+    GrShaderFlags visibility() const { return fVisibility; }
+    const GrSamplerParams& params() const { return fParams; }
+
+    /**
+     * For internal use by GrProcessor.
+     */
+    const GrSurfaceProxyRef* programProxy() const { return &fProxyRef; }
+
+private:
+    GrSurfaceProxyRef               fProxyRef;
+    GrSamplerParams                 fParams;
+    GrShaderFlags                   fVisibility;
+
+    typedef SkNoncopyable INHERITED;
+};
+
+/**
+ * Used to represent a texel buffer that will be read in a GrResourceIOProcessor. It holds a
+ * GrBuffer along with an associated offset and texel config.
+ */
+class GrResourceIOProcessor::BufferAccess : public SkNoncopyable {
+public:
+    BufferAccess() = default;
+    BufferAccess(GrPixelConfig texelConfig, GrBuffer* buffer,
+                 GrShaderFlags visibility = kFragment_GrShaderFlag) {
+        this->reset(texelConfig, buffer, visibility);
+    }
+    /**
+     * Must be initialized before adding to a GrProcessor's buffer access list.
+     */
+    void reset(GrPixelConfig texelConfig, GrBuffer* buffer,
+               GrShaderFlags visibility = kFragment_GrShaderFlag) {
+        fTexelConfig = texelConfig;
+        fBuffer.set(SkRef(buffer), kRead_GrIOType);
+        fVisibility = visibility;
+    }
+
+    bool operator==(const BufferAccess& that) const {
+        return fTexelConfig == that.fTexelConfig &&
+               this->buffer() == that.buffer() &&
+               fVisibility == that.fVisibility;
+    }
+
+    bool operator!=(const BufferAccess& that) const { return !(*this == that); }
+
+    GrPixelConfig texelConfig() const { return fTexelConfig; }
+    GrBuffer* buffer() const { return fBuffer.get(); }
+    GrShaderFlags visibility() const { return fVisibility; }
+
+    /**
+     * For internal use by GrProcessor.
+     */
+    const GrGpuResourceRef* programBuffer() const { return &fBuffer;}
+
+private:
+    GrPixelConfig fTexelConfig;
+    GrTGpuResourceRef<GrBuffer> fBuffer;
+    GrShaderFlags fVisibility;
+
+    typedef SkNoncopyable INHERITED;
+};
+
+/**
+ * This is used by a GrProcessor to access a texture using image load/store in its shader code.
+ * ImageStorageAccesses don't perform any coord manipulation to account for texture origin.
+ * Currently the format of the load/store data in the shader is inferred from the texture config,
+ * though it could be made explicit.
+ */
+class GrResourceIOProcessor::ImageStorageAccess : public SkNoncopyable {
+public:
+    ImageStorageAccess(sk_sp<GrTextureProxy>, GrIOType, GrSLMemoryModel, GrSLRestrict,
+                       GrShaderFlags visibility = kFragment_GrShaderFlag);
+
+    bool operator==(const ImageStorageAccess& that) const {
+        return this->proxy() == that.proxy() && fVisibility == that.fVisibility;
+    }
+
+    bool operator!=(const ImageStorageAccess& that) const { return !(*this == that); }
+
+    GrTextureProxy* proxy() const { return fProxyRef.get()->asTextureProxy(); }
+    GrShaderFlags visibility() const { return fVisibility; }
+    GrIOType ioType() const { return fProxyRef.ioType(); }
+    GrImageStorageFormat format() const { return fFormat; }
+    GrSLMemoryModel memoryModel() const { return fMemoryModel; }
+    GrSLRestrict restrict() const { return fRestrict; }
+
+    // 'instantiate' should only ever be called at flush time.
+    bool instantiate(GrResourceProvider* resourceProvider) const {
+        return SkToBool(fProxyRef.get()->instantiate(resourceProvider));
+    }
+    // 'peekTexture' should only ever be called after a successful 'instantiate' call
+    GrTexture* peekTexture() const {
+        SkASSERT(fProxyRef.get()->priv().peekTexture());
+        return fProxyRef.get()->priv().peekTexture();
+    }
+
+    /**
+     * For internal use by GrProcessor.
+     */
+    const GrSurfaceProxyRef* programProxy() const { return &fProxyRef; }
+
+private:
+    GrSurfaceProxyRef    fProxyRef;
+    GrShaderFlags        fVisibility;
+    GrImageStorageFormat fFormat;
+    GrSLMemoryModel      fMemoryModel;
+    GrSLRestrict         fRestrict;
+    typedef SkNoncopyable INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrProcessorAnalysis.cpp b/src/gpu/GrProcessorAnalysis.cpp
new file mode 100644
index 0000000..118e93d
--- /dev/null
+++ b/src/gpu/GrProcessorAnalysis.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 "GrProcessorAnalysis.h"
+#include "GrGeometryProcessor.h"
+#include "ops/GrDrawOp.h"
+
+void GrColorFragmentProcessorAnalysis::analyzeProcessors(
+        const GrFragmentProcessor* const* processors, int cnt) {
+    for (int i = 0; i < cnt; ++i) {
+        bool knowCurrentOutput = fProcessorsVisitedWithKnownOutput == fTotalProcessorsVisited;
+        if (fUsesLocalCoords && !knowCurrentOutput &&
+            !fAllProcessorsCompatibleWithCoverageAsAlpha && !fIsOpaque) {
+            fTotalProcessorsVisited += cnt - i;
+            return;
+        }
+        const GrFragmentProcessor* fp = processors[i];
+        if (knowCurrentOutput &&
+            fp->hasConstantOutputForConstantInput(fLastKnownOutputColor, &fLastKnownOutputColor)) {
+            ++fProcessorsVisitedWithKnownOutput;
+            fIsOpaque = fLastKnownOutputColor.isOpaque();
+            // We reset these since the caller is expected to not use the earlier fragment
+            // processors.
+            fAllProcessorsCompatibleWithCoverageAsAlpha = true;
+            fUsesLocalCoords = false;
+        } else {
+            if (fIsOpaque && !fp->preservesOpaqueInput()) {
+                fIsOpaque = false;
+            }
+            if (fAllProcessorsCompatibleWithCoverageAsAlpha &&
+                !fp->compatibleWithCoverageAsAlpha()) {
+                fAllProcessorsCompatibleWithCoverageAsAlpha = false;
+            }
+            if (fp->usesLocalCoords()) {
+                fUsesLocalCoords = true;
+            }
+        }
+        ++fTotalProcessorsVisited;
+    }
+}
diff --git a/src/gpu/GrProcessorAnalysis.h b/src/gpu/GrProcessorAnalysis.h
new file mode 100644
index 0000000..f5e4065
--- /dev/null
+++ b/src/gpu/GrProcessorAnalysis.h
@@ -0,0 +1,173 @@
+/*
+ * 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 GrProcessorAnalysis_DEFINED
+#define GrProcessorAnalysis_DEFINED
+
+#include "GrColor.h"
+
+class GrDrawOp;
+class GrFragmentProcessor;
+class GrPrimitiveProcessor;
+
+class GrProcessorAnalysisColor {
+public:
+    enum class Opaque {
+        kNo,
+        kYes,
+    };
+
+    GrProcessorAnalysisColor(Opaque opaque = Opaque::kNo)
+            : fFlags(opaque == Opaque::kYes ? kIsOpaque_Flag : 0) {}
+
+    GrProcessorAnalysisColor(GrColor color) { this->setToConstant(color); }
+
+    void setToConstant(GrColor color) {
+        fColor = color;
+        if (GrColorIsOpaque(color)) {
+            fFlags = kColorIsKnown_Flag | kIsOpaque_Flag;
+        } else {
+            fFlags = kColorIsKnown_Flag;
+        }
+    }
+
+    void setToUnknown() { fFlags = 0; }
+
+    void setToUnknownOpaque() { fFlags = kIsOpaque_Flag; }
+
+    bool isOpaque() const { return SkToBool(kIsOpaque_Flag & fFlags); }
+
+    bool isConstant(GrColor* color = nullptr) const {
+        if (kColorIsKnown_Flag & fFlags) {
+            if (color) {
+                *color = fColor;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    bool operator==(const GrProcessorAnalysisColor& that) const {
+        if (fFlags != that.fFlags) {
+            return false;
+        }
+        return (kColorIsKnown_Flag & fFlags) ? fColor == that.fColor : true;
+    }
+
+    /** The returned value reflects the common properties of the two inputs. */
+    static GrProcessorAnalysisColor Combine(const GrProcessorAnalysisColor& a,
+                                            const GrProcessorAnalysisColor& b) {
+        GrProcessorAnalysisColor result;
+        uint32_t commonFlags = a.fFlags & b.fFlags;
+        if ((kColorIsKnown_Flag & commonFlags) && a.fColor == b.fColor) {
+            result.fColor = a.fColor;
+            result.fFlags = a.fFlags;
+        } else if (kIsOpaque_Flag & commonFlags) {
+            result.fFlags = kIsOpaque_Flag;
+        }
+        return result;
+    }
+
+private:
+    enum Flags {
+        kColorIsKnown_Flag = 0x1,
+        kIsOpaque_Flag = 0x2,
+    };
+    uint32_t fFlags;
+    GrColor fColor;
+};
+
+enum class GrProcessorAnalysisCoverage { kNone, kSingleChannel, kLCD };
+
+/**
+ * GrColorFragmentProcessorAnalysis gathers invariant data from a set of color fragment processor.
+ * It is used to recognize optimizations that can simplify the generated shader or make blending
+ * more effecient.
+ */
+class GrColorFragmentProcessorAnalysis {
+public:
+    GrColorFragmentProcessorAnalysis() = default;
+
+    GrColorFragmentProcessorAnalysis(const GrProcessorAnalysisColor& input)
+            : GrColorFragmentProcessorAnalysis() {
+        fAllProcessorsCompatibleWithCoverageAsAlpha = true;
+        fIsOpaque = input.isOpaque();
+        GrColor color;
+        if (input.isConstant(&color)) {
+            fLastKnownOutputColor = GrColor4f::FromGrColor(color);
+            fProcessorsVisitedWithKnownOutput = 0;
+        }
+    }
+
+    void reset(const GrProcessorAnalysisColor& input) {
+        *this = GrColorFragmentProcessorAnalysis(input);
+    }
+
+    /**
+     * Runs through a series of processors and updates calculated values. This can be called
+     * repeatedly for cases when the sequence of processors is not in a contiguous array.
+     */
+    void analyzeProcessors(const GrFragmentProcessor* const* processors, int cnt);
+
+    bool isOpaque() const { return fIsOpaque; }
+
+    /**
+     * Are all the fragment processors compatible with conflating coverage with color prior to the
+     * the first fragment processor. This result assumes that processors that should be eliminated
+     * as indicated by initialProcessorsToEliminate() are in fact eliminated.
+     */
+    bool allProcessorsCompatibleWithCoverageAsAlpha() const {
+        return fAllProcessorsCompatibleWithCoverageAsAlpha;
+    }
+
+    /**
+     * Do any of the fragment processors require local coords. This result assumes that
+     * processors that should be eliminated as indicated by initialProcessorsToEliminate() are in
+     * fact eliminated.
+     */
+    bool usesLocalCoords() const { return fUsesLocalCoords; }
+
+    /**
+     * If we detected that the result after the first N processors is a known color then we
+     * eliminate those N processors and replace the GrDrawOp's color input to the GrPipeline with
+     * the known output of the Nth processor, so that the Nth+1 fragment processor (or the XP if
+     * there are only N processors) sees its expected input. If this returns 0 then there are no
+     * processors to eliminate.
+     */
+    int initialProcessorsToEliminate(GrColor* newPipelineInputColor) const {
+        if (fProcessorsVisitedWithKnownOutput > 0) {
+            *newPipelineInputColor = fLastKnownOutputColor.toGrColor();
+        }
+        return SkTMax(0, fProcessorsVisitedWithKnownOutput);
+    }
+
+    int initialProcessorsToEliminate(GrColor4f* newPipelineInputColor) const {
+        if (fProcessorsVisitedWithKnownOutput > 0) {
+            *newPipelineInputColor = fLastKnownOutputColor;
+        }
+        return SkTMax(0, fProcessorsVisitedWithKnownOutput);
+    }
+
+    GrProcessorAnalysisColor outputColor() const {
+        if (fProcessorsVisitedWithKnownOutput != fTotalProcessorsVisited) {
+            return GrProcessorAnalysisColor(fIsOpaque ? GrProcessorAnalysisColor::Opaque::kYes
+                                                      : GrProcessorAnalysisColor::Opaque::kNo);
+        }
+        return GrProcessorAnalysisColor(fLastKnownOutputColor.toGrColor());
+    }
+
+private:
+    int fTotalProcessorsVisited = 0;
+    // negative one means even the color is unknown before adding the first processor.
+    int fProcessorsVisitedWithKnownOutput = -1;
+    bool fIsOpaque = false;
+    bool fAllProcessorsCompatibleWithCoverageAsAlpha = true;
+    bool fUsesLocalCoords = false;
+    GrColor4f fLastKnownOutputColor;
+};
+
+#endif
diff --git a/src/gpu/GrProcessorSet.cpp b/src/gpu/GrProcessorSet.cpp
index 4a50892..01de53d 100644
--- a/src/gpu/GrProcessorSet.cpp
+++ b/src/gpu/GrProcessorSet.cpp
@@ -8,10 +8,15 @@
 #include "GrProcessorSet.h"
 #include "GrAppliedClip.h"
 #include "GrCaps.h"
-#include "GrPipelineAnalysis.h"
+#include "GrXferProcessor.h"
+#include "effects/GrPorterDuffXferProcessor.h"
 
-GrProcessorSet::GrProcessorSet(GrPaint&& paint) {
-    fXPFactory = paint.fXPFactory;
+const GrProcessorSet& GrProcessorSet::EmptySet() {
+    static const GrProcessorSet gEmpty(GrProcessorSet::Empty::kEmpty);
+    return gEmpty;
+}
+
+GrProcessorSet::GrProcessorSet(GrPaint&& paint) : fXP(paint.getXPFactory()) {
     fFlags = 0;
     if (paint.numColorFragmentProcessors() <= kMaxColorProcessors) {
         fColorFragmentProcessorCnt = paint.numColorFragmentProcessors();
@@ -23,44 +28,30 @@
         for (auto& fp : paint.fCoverageFragmentProcessors) {
             fFragmentProcessors[i++] = fp.release();
         }
-        if (paint.usesDistanceVectorField()) {
-            fFlags |= kUseDistanceVectorField_Flag;
-        }
     } else {
         SkDebugf("Insane number of color fragment processors in paint. Dropping all processors.");
         fColorFragmentProcessorCnt = 0;
     }
-    if (paint.getDisableOutputConversionToSRGB()) {
-        fFlags |= kDisableOutputConversionToSRGB_Flag;
-    }
-    if (paint.getAllowSRGBInputs()) {
-        fFlags |= kAllowSRGBInputs_Flag;
-    }
 }
 
 GrProcessorSet::~GrProcessorSet() {
     for (int i = fFragmentProcessorOffset; i < fFragmentProcessors.count(); ++i) {
-        if (this->isPendingExecution()) {
+        if (this->isFinalized()) {
             fFragmentProcessors[i]->completedExecution();
         } else {
             fFragmentProcessors[i]->unref();
         }
     }
-}
-
-void GrProcessorSet::makePendingExecution() {
-    SkASSERT(!(kPendingExecution_Flag & fFlags));
-    fFlags |= kPendingExecution_Flag;
-    for (int i = fFragmentProcessorOffset; i < fFragmentProcessors.count(); ++i) {
-        fFragmentProcessors[i]->addPendingExecution();
-        fFragmentProcessors[i]->unref();
+    if (this->isFinalized() && this->xferProcessor()) {
+        this->xferProcessor()->unref();
     }
 }
 
 bool GrProcessorSet::operator==(const GrProcessorSet& that) const {
+    SkASSERT(this->isFinalized());
+    SkASSERT(that.isFinalized());
     int fpCount = this->numFragmentProcessors();
-    if (((fFlags ^ that.fFlags) & ~kPendingExecution_Flag) ||
-        fpCount != that.numFragmentProcessors() ||
+    if (((fFlags ^ that.fFlags) & ~kFinalized_Flag) || fpCount != that.numFragmentProcessors() ||
         fColorFragmentProcessorCnt != that.fColorFragmentProcessorCnt) {
         return false;
     }
@@ -72,108 +63,110 @@
             return false;
         }
     }
-    if (fXPFactory != that.fXPFactory) {
-        return false;
+    // Most of the time both of these are null
+    if (!this->xferProcessor() && !that.xferProcessor()) {
+        return true;
     }
-    return true;
+    const GrXferProcessor& thisXP = this->xferProcessor()
+                                            ? *this->xferProcessor()
+                                            : GrPorterDuffXPFactory::SimpleSrcOverXP();
+    const GrXferProcessor& thatXP = that.xferProcessor()
+                                            ? *that.xferProcessor()
+                                            : GrPorterDuffXPFactory::SimpleSrcOverXP();
+    return thisXP.isEqual(thatXP);
 }
 
-//////////////////////////////////////////////////////////////////////////////
+GrProcessorSet::Analysis GrProcessorSet::finalize(const GrProcessorAnalysisColor& colorInput,
+                                                  const GrProcessorAnalysisCoverage coverageInput,
+                                                  const GrAppliedClip* clip, bool isMixedSamples,
+                                                  const GrCaps& caps, GrColor* overrideInputColor) {
+    SkASSERT(!this->isFinalized());
+    SkASSERT(!fFragmentProcessorOffset);
 
-void GrProcessorSet::FragmentProcessorAnalysis::internalInit(
-        const GrPipelineAnalysisColor& colorInput,
-        const GrPipelineAnalysisCoverage coverageInput,
-        const GrProcessorSet& processors,
-        const GrFragmentProcessor* clipFP,
-        const GrCaps& caps) {
-    GrColorFragmentProcessorAnalysis colorInfo(colorInput);
-    fCompatibleWithCoverageAsAlpha = GrPipelineAnalysisCoverage::kLCD != coverageInput;
-    fValidInputColor = colorInput.isConstant(&fInputColor);
+    GrProcessorSet::Analysis analysis;
 
-    const GrFragmentProcessor* const* fps =
-            processors.fFragmentProcessors.get() + processors.fFragmentProcessorOffset;
-    colorInfo.analyzeProcessors(fps, processors.fColorFragmentProcessorCnt);
-    fCompatibleWithCoverageAsAlpha &= colorInfo.allProcessorsCompatibleWithCoverageAsAlpha();
-    fps += processors.fColorFragmentProcessorCnt;
-    int n = processors.numCoverageFragmentProcessors();
+    const GrFragmentProcessor* clipFP = clip ? clip->clipCoverageFragmentProcessor() : nullptr;
+    GrColorFragmentProcessorAnalysis colorAnalysis(colorInput);
+    analysis.fCompatibleWithCoverageAsAlpha = GrProcessorAnalysisCoverage::kLCD != coverageInput;
+
+    const GrFragmentProcessor* const* fps = fFragmentProcessors.get() + fFragmentProcessorOffset;
+    colorAnalysis.analyzeProcessors(fps, fColorFragmentProcessorCnt);
+    analysis.fCompatibleWithCoverageAsAlpha &=
+            colorAnalysis.allProcessorsCompatibleWithCoverageAsAlpha();
+    fps += fColorFragmentProcessorCnt;
+    int n = this->numCoverageFragmentProcessors();
     bool hasCoverageFP = n > 0;
-    fUsesLocalCoords = colorInfo.usesLocalCoords();
+    bool coverageUsesLocalCoords = false;
     for (int i = 0; i < n; ++i) {
         if (!fps[i]->compatibleWithCoverageAsAlpha()) {
-            fCompatibleWithCoverageAsAlpha = false;
+            analysis.fCompatibleWithCoverageAsAlpha = false;
             // Other than tests that exercise atypical behavior we expect all coverage FPs to be
             // compatible with the coverage-as-alpha optimization.
             GrCapsDebugf(&caps, "Coverage FP is not compatible with coverage as alpha.\n");
         }
-        fUsesLocalCoords |= fps[i]->usesLocalCoords();
+        coverageUsesLocalCoords |= fps[i]->usesLocalCoords();
     }
 
     if (clipFP) {
-        fCompatibleWithCoverageAsAlpha &= clipFP->compatibleWithCoverageAsAlpha();
-        fUsesLocalCoords |= clipFP->usesLocalCoords();
+        analysis.fCompatibleWithCoverageAsAlpha &= clipFP->compatibleWithCoverageAsAlpha();
+        coverageUsesLocalCoords |= clipFP->usesLocalCoords();
         hasCoverageFP = true;
     }
-    fInitialColorProcessorsToEliminate = colorInfo.initialProcessorsToEliminate(&fInputColor);
-    fValidInputColor |= SkToBool(fInitialColorProcessorsToEliminate);
+    int colorFPsToEliminate = colorAnalysis.initialProcessorsToEliminate(overrideInputColor);
+    analysis.fInputColorType = static_cast<Analysis::PackedInputColorType>(
+            colorFPsToEliminate ? Analysis::kOverridden_InputColorType
+                                : Analysis::kOriginal_InputColorType);
 
-    GrPipelineAnalysisColor outputColor = colorInfo.outputColor();
-    if (outputColor.isConstant(&fKnownOutputColor)) {
-        fOutputColorType = static_cast<unsigned>(outputColor.isOpaque() ? ColorType::kOpaqueConstant
-                                                                        : ColorType::kConstant);
-    } else if (outputColor.isOpaque()) {
-        fOutputColorType = static_cast<unsigned>(ColorType::kOpaque);
+    GrProcessorAnalysisCoverage outputCoverage;
+    if (GrProcessorAnalysisCoverage::kLCD == coverageInput) {
+        outputCoverage = GrProcessorAnalysisCoverage::kLCD;
+    } else if (hasCoverageFP || GrProcessorAnalysisCoverage::kSingleChannel == coverageInput) {
+        outputCoverage = GrProcessorAnalysisCoverage::kSingleChannel;
     } else {
-        fOutputColorType = static_cast<unsigned>(ColorType::kUnknown);
+        outputCoverage = GrProcessorAnalysisCoverage::kNone;
     }
 
-    if (GrPipelineAnalysisCoverage::kLCD == coverageInput) {
-        fOutputCoverageType = static_cast<unsigned>(GrPipelineAnalysisCoverage::kLCD);
-    } else if (hasCoverageFP || GrPipelineAnalysisCoverage::kSingleChannel == coverageInput) {
-        fOutputCoverageType = static_cast<unsigned>(GrPipelineAnalysisCoverage::kSingleChannel);
+    GrXPFactory::AnalysisProperties props = GrXPFactory::GetAnalysisProperties(
+            this->xpFactory(), colorAnalysis.outputColor(), outputCoverage, caps);
+    if (!this->numCoverageFragmentProcessors() &&
+        GrProcessorAnalysisCoverage::kNone == coverageInput) {
+        analysis.fCanCombineOverlappedStencilAndCover = SkToBool(
+                props & GrXPFactory::AnalysisProperties::kCanCombineOverlappedStencilAndCover);
     } else {
-        fOutputCoverageType = static_cast<unsigned>(GrPipelineAnalysisCoverage::kNone);
+        // If we have non-clipping coverage processors we don't try to merge stencil steps as its
+        // unclear whether it will be correct. We don't expect this to happen in practice.
+        analysis.fCanCombineOverlappedStencilAndCover = false;
     }
-}
-
-void GrProcessorSet::FragmentProcessorAnalysis::init(const GrPipelineAnalysisColor& colorInput,
-                                                     const GrPipelineAnalysisCoverage coverageInput,
-                                                     const GrProcessorSet& processors,
-                                                     const GrAppliedClip* appliedClip,
-                                                     const GrCaps& caps) {
-    const GrFragmentProcessor* clipFP =
-            appliedClip ? appliedClip->clipCoverageFragmentProcessor() : nullptr;
-    this->internalInit(colorInput, coverageInput, processors, clipFP, caps);
-    fIsInitializedWithProcessorSet = true;
-}
-
-GrProcessorSet::FragmentProcessorAnalysis::FragmentProcessorAnalysis(
-        const GrPipelineAnalysisColor& colorInput,
-        const GrPipelineAnalysisCoverage coverageInput,
-        const GrCaps& caps)
-        : FragmentProcessorAnalysis() {
-    this->internalInit(colorInput, coverageInput, GrProcessorSet(GrPaint()), nullptr, caps);
-}
-
-void GrProcessorSet::analyzeAndEliminateFragmentProcessors(
-        FragmentProcessorAnalysis* analysis,
-        const GrPipelineAnalysisColor& colorInput,
-        const GrPipelineAnalysisCoverage coverageInput,
-        const GrAppliedClip* clip,
-        const GrCaps& caps) {
-    analysis->init(colorInput, coverageInput, *this, clip, caps);
-    if (analysis->fInitialColorProcessorsToEliminate > 0) {
-        for (unsigned i = 0; i < analysis->fInitialColorProcessorsToEliminate; ++i) {
-            if (this->isPendingExecution()) {
-                fFragmentProcessors[i + fFragmentProcessorOffset]->completedExecution();
-            } else {
-                fFragmentProcessors[i + fFragmentProcessorOffset]->unref();
-            }
-            fFragmentProcessors[i + fFragmentProcessorOffset] = nullptr;
-        }
-        fFragmentProcessorOffset += analysis->fInitialColorProcessorsToEliminate;
-        fColorFragmentProcessorCnt -= analysis->fInitialColorProcessorsToEliminate;
-        SkASSERT(fFragmentProcessorOffset + fColorFragmentProcessorCnt <=
-                 fFragmentProcessors.count());
-        analysis->fInitialColorProcessorsToEliminate = 0;
+    analysis.fRequiresDstTexture =
+            SkToBool(props & GrXPFactory::AnalysisProperties::kRequiresDstTexture);
+    analysis.fCompatibleWithCoverageAsAlpha &=
+            SkToBool(props & GrXPFactory::AnalysisProperties::kCompatibleWithAlphaAsCoverage);
+    analysis.fRequiresBarrierBetweenOverlappingDraws = SkToBool(
+            props & GrXPFactory::AnalysisProperties::kRequiresBarrierBetweenOverlappingDraws);
+    if (props & GrXPFactory::AnalysisProperties::kIgnoresInputColor) {
+        colorFPsToEliminate = this->numColorFragmentProcessors();
+        analysis.fInputColorType =
+                static_cast<Analysis::PackedInputColorType>(Analysis::kIgnored_InputColorType);
+        analysis.fUsesLocalCoords = coverageUsesLocalCoords;
+    } else {
+        analysis.fUsesLocalCoords = coverageUsesLocalCoords | colorAnalysis.usesLocalCoords();
     }
+    for (int i = 0; i < colorFPsToEliminate; ++i) {
+        fFragmentProcessors[i]->unref();
+        fFragmentProcessors[i] = nullptr;
+    }
+    for (int i = colorFPsToEliminate; i < fFragmentProcessors.count(); ++i) {
+        fFragmentProcessors[i]->addPendingExecution();
+        fFragmentProcessors[i]->unref();
+    }
+    fFragmentProcessorOffset = colorFPsToEliminate;
+    fColorFragmentProcessorCnt -= colorFPsToEliminate;
+
+    auto xp = GrXPFactory::MakeXferProcessor(this->xpFactory(), colorAnalysis.outputColor(),
+                                             outputCoverage, isMixedSamples, caps);
+    fXP.fProcessor = xp.release();
+
+    fFlags |= kFinalized_Flag;
+    analysis.fIsInitialized = true;
+    return analysis;
 }
diff --git a/src/gpu/GrProcessorSet.h b/src/gpu/GrProcessorSet.h
index bfad769..d7ceced 100644
--- a/src/gpu/GrProcessorSet.h
+++ b/src/gpu/GrProcessorSet.h
@@ -10,26 +10,23 @@
 
 #include "GrFragmentProcessor.h"
 #include "GrPaint.h"
-#include "GrPipelineAnalysis.h"
+#include "GrProcessorAnalysis.h"
 #include "SkTemplates.h"
+#include "GrXferProcessor.h"
 
 class GrAppliedClip;
 class GrXPFactory;
 
 class GrProcessorSet : private SkNoncopyable {
+private:
+    // Arbitrary constructor arg for empty set and analysis
+    enum class Empty { kEmpty };
+
 public:
     GrProcessorSet(GrPaint&& paint);
 
     ~GrProcessorSet();
 
-    /**
-     * If an op is recorded with this processor set then this must be called to ensure pending
-     * reads and writes are propagated to resources referred to by the processors. Otherwise,
-     * data hazards may occur.
-     */
-    void makePendingExecution();
-    bool isPendingExecution() const { return SkToBool(kPendingExecution_Flag & fFlags); }
-
     int numColorFragmentProcessors() const { return fColorFragmentProcessorCnt; }
     int numCoverageFragmentProcessors() const {
         return this->numFragmentProcessors() - fColorFragmentProcessorCnt;
@@ -46,144 +43,121 @@
         return fFragmentProcessors[idx + fColorFragmentProcessorCnt + fFragmentProcessorOffset];
     }
 
-    const GrXPFactory* xpFactory() const { return fXPFactory; }
-
-    bool usesDistanceVectorField() const { return SkToBool(fFlags & kUseDistanceVectorField_Flag); }
-    bool disableOutputConversionToSRGB() const {
-        return SkToBool(fFlags & kDisableOutputConversionToSRGB_Flag);
+    const GrXferProcessor* xferProcessor() const {
+        SkASSERT(this->isFinalized());
+        return fXP.fProcessor;
     }
-    bool allowSRGBInputs() const { return SkToBool(fFlags & kAllowSRGBInputs_Flag); }
+    sk_sp<const GrXferProcessor> refXferProcessor() const {
+        SkASSERT(this->isFinalized());
+        return sk_ref_sp(fXP.fProcessor);
+    }
 
+    /** Comparisons are only legal on finalized processor sets. */
     bool operator==(const GrProcessorSet& that) const;
     bool operator!=(const GrProcessorSet& that) const { return !(*this == that); }
 
     /**
-     * This is used to track analysis of color and coverage values through the fragment processors.
+     * This is used to report results of processor analysis when a processor set is finalized (see
+     * below).
      */
-    class FragmentProcessorAnalysis {
+    class Analysis {
     public:
-        /**
-         * This constructor allows an op to record its initial color in a FragmentProcessorAnalysis
-         * member and then run analysis later when the analysis inputs are available. If the
-         * analysis produces color fragment processor elimination then the input color is replaced
-         * by the expected input to the first non-eliminated processor. Otherwise, the original
-         * input color is preserved. The only reason to use this is to save space on the op by not
-         * separately storing the initial color.
-         */
-        explicit FragmentProcessorAnalysis(GrColor initialColor) : FragmentProcessorAnalysis() {
-            fInputColor = initialColor;
-            fValidInputColor = true;
-        }
+        Analysis(const Analysis&) = default;
+        Analysis() { *reinterpret_cast<uint32_t*>(this) = 0; }
 
-        FragmentProcessorAnalysis()
-                : fIsInitializedWithProcessorSet(false)
-                , fCompatibleWithCoverageAsAlpha(true)
-                , fValidInputColor(false)
-                , fOutputCoverageType(static_cast<unsigned>(GrPipelineAnalysisCoverage::kNone))
-                , fOutputColorType(static_cast<unsigned>(ColorType::kUnknown))
-                , fInitialColorProcessorsToEliminate(0) {}
-
-        // This version is used by a unit test that assumes no clip, no processors, and no PLS.
-        FragmentProcessorAnalysis(const GrPipelineAnalysisColor&, GrPipelineAnalysisCoverage,
-                                  const GrCaps&);
-
-        void init(const GrPipelineAnalysisColor&, GrPipelineAnalysisCoverage, const GrProcessorSet&,
-                  const GrAppliedClip*, const GrCaps&);
-
-        bool isInitializedWithProcessorSet() const { return fIsInitializedWithProcessorSet; }
-
-        /**
-         * If the return is greater than or equal to zero then 'newInputColor' should be used as the
-         * input color to the GrPipeline derived from this processor set, replacing the GrDrawOp's
-         * initial color. If the return is less than zero then newInputColor has not been
-         * modified and no modification need be made to the pipeline's input color by the op.
-         */
-        int getInputColorOverrideAndColorProcessorEliminationCount(GrColor* newInputColor) const {
-            if (fValidInputColor) {
-                *newInputColor = fInputColor;
-                return fInitialColorProcessorsToEliminate;
-            }
-            SkASSERT(!fInitialColorProcessorsToEliminate);
-            return -1;
-        }
-
-        /**
-         * Valid if initialProcessorsToEliminate returns true or this analysis was initialized with
-         * a known color via constructor or init(). If color fragment processors are eliminated then
-         * this returns the expected input to the first non-eliminated processors. Otherwise it is
-         * the color passed to the constructor or init().
-         */
-        GrColor inputColor() const {
-            SkASSERT(fValidInputColor);
-            return fInputColor;
-        }
-
+        bool isInitialized() const { return fIsInitialized; }
         bool usesLocalCoords() const { return fUsesLocalCoords; }
+        bool requiresDstTexture() const { return fRequiresDstTexture; }
+        bool canCombineOverlappedStencilAndCover() const {
+            return fCanCombineOverlappedStencilAndCover;
+        }
+        bool requiresBarrierBetweenOverlappingDraws() const {
+            return fRequiresBarrierBetweenOverlappingDraws;
+        }
         bool isCompatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; }
-        bool isOutputColorOpaque() const {
-            return ColorType::kOpaque == this->outputColorType() ||
-                   ColorType::kOpaqueConstant == this->outputColorType();
-        }
-        bool hasKnownOutputColor(GrColor* color = nullptr) const {
-            bool constant = ColorType::kConstant == this->outputColorType() ||
-                            ColorType::kOpaqueConstant == this->outputColorType();
-            if (constant && color) {
-                *color = fKnownOutputColor;
-            }
-            return constant;
-        }
-        GrPipelineAnalysisCoverage outputCoverageType() const {
-            return static_cast<GrPipelineAnalysisCoverage>(fOutputCoverageType);
-        }
-        bool hasCoverage() const {
-            return this->outputCoverageType() != GrPipelineAnalysisCoverage::kNone;
+
+        bool inputColorIsIgnored() const { return fInputColorType == kIgnored_InputColorType; }
+        bool inputColorIsOverridden() const {
+            return fInputColorType == kOverridden_InputColorType;
         }
 
     private:
-        enum class ColorType : unsigned { kUnknown, kOpaqueConstant, kConstant, kOpaque };
+        constexpr Analysis(Empty)
+                : fUsesLocalCoords(false)
+                , fCompatibleWithCoverageAsAlpha(true)
+                , fRequiresDstTexture(false)
+                , fCanCombineOverlappedStencilAndCover(true)
+                , fRequiresBarrierBetweenOverlappingDraws(false)
+                , fIsInitialized(true)
+                , fInputColorType(kOriginal_InputColorType) {}
+        enum InputColorType : uint32_t {
+            kOriginal_InputColorType,
+            kOverridden_InputColorType,
+            kIgnored_InputColorType
+        };
 
-        ColorType outputColorType() const { return static_cast<ColorType>(fOutputColorType); }
+        // MSVS 2015 won't pack different underlying types
+        using PackedBool = uint32_t;
+        using PackedInputColorType = uint32_t;
 
-        void internalInit(const GrPipelineAnalysisColor&, const GrPipelineAnalysisCoverage,
-                          const GrProcessorSet&, const GrFragmentProcessor* clipFP, const GrCaps&);
-
-        // MSVS 2015 won't pack a bool with an unsigned.
-        using PackedBool = unsigned;
-
-        PackedBool fIsInitializedWithProcessorSet : 1;
         PackedBool fUsesLocalCoords : 1;
         PackedBool fCompatibleWithCoverageAsAlpha : 1;
-        PackedBool fValidInputColor : 1;
-        unsigned fOutputCoverageType : 2;
-        unsigned fOutputColorType : 2;
-        unsigned fInitialColorProcessorsToEliminate : 32 - 8;
-
-        GrColor fInputColor;
-        GrColor fKnownOutputColor;
+        PackedBool fRequiresDstTexture : 1;
+        PackedBool fCanCombineOverlappedStencilAndCover : 1;
+        PackedBool fRequiresBarrierBetweenOverlappingDraws : 1;
+        PackedBool fIsInitialized : 1;
+        PackedInputColorType fInputColorType : 2;
 
         friend class GrProcessorSet;
     };
-    GR_STATIC_ASSERT(sizeof(FragmentProcessorAnalysis) == 2 * sizeof(GrColor) + sizeof(uint32_t));
+    GR_STATIC_ASSERT(sizeof(Analysis) <= sizeof(uint32_t));
 
-    void analyzeAndEliminateFragmentProcessors(FragmentProcessorAnalysis*,
-                                               const GrPipelineAnalysisColor& colorInput,
-                                               const GrPipelineAnalysisCoverage coverageInput,
-                                               const GrAppliedClip*, const GrCaps&);
+    /**
+     * This analyzes the processors given an op's input color and coverage as well as a clip. The
+     * state of the processor set may change to an equivalent but more optimal set of processors.
+     * This new state requires that the caller respect the returned 'inputColorOverride'. This is
+     * indicated by the returned Analysis's inputColorIsOverriden(). 'inputColorOverride' will not
+     * be written if the analysis does not override the input color.
+     *
+     * This must be called before the processor set is used to construct a GrPipeline and may only
+     * be called once.
+     *
+     * This also puts the processors in "pending execution" state and must be called when an op
+     * that owns a processor set is recorded to ensure pending and writes are propagated to
+     * resources referred to by the processors. Otherwise, data hazards may occur.
+     */
+    Analysis finalize(const GrProcessorAnalysisColor& colorInput,
+                      const GrProcessorAnalysisCoverage coverageInput, const GrAppliedClip*,
+                      bool isMixedSamples, const GrCaps&, GrColor* inputColorOverride);
+
+    bool isFinalized() const { return SkToBool(kFinalized_Flag & fFlags); }
+
+    static const GrProcessorSet& EmptySet();
+    static constexpr const Analysis EmptySetAnalysis() { return Analysis(Empty::kEmpty); }
 
 private:
-    // This absurdly large limit allows FragmentProcessorAnalysis and this to pack fields together.
+    GrProcessorSet(Empty) : fXP((const GrXferProcessor*)nullptr), fFlags(kFinalized_Flag) {}
+
+    // This absurdly large limit allows Analysis and this to pack fields together.
     static constexpr int kMaxColorProcessors = UINT8_MAX;
 
-    enum Flags : uint16_t {
-        kUseDistanceVectorField_Flag = 0x1,
-        kDisableOutputConversionToSRGB_Flag = 0x2,
-        kAllowSRGBInputs_Flag = 0x4,
-        kPendingExecution_Flag = 0x8
+    enum Flags : uint16_t { kFinalized_Flag = 0x1 };
+
+    union XP {
+        XP(const GrXPFactory* factory) : fFactory(factory) {}
+        XP(const GrXferProcessor* processor) : fProcessor(processor) {}
+        const GrXPFactory* fFactory;
+        const GrXferProcessor* fProcessor;
     };
 
-    const GrXPFactory* fXPFactory = nullptr;
+    const GrXPFactory* xpFactory() const {
+        SkASSERT(!this->isFinalized());
+        return fXP.fFactory;
+    }
+
     SkAutoSTArray<4, const GrFragmentProcessor*> fFragmentProcessors;
-    uint8_t fColorFragmentProcessorCnt;
+    XP fXP;
+    uint8_t fColorFragmentProcessorCnt = 0;
     uint8_t fFragmentProcessorOffset = 0;
     uint8_t fFlags;
 };
diff --git a/src/gpu/GrProcessorUnitTest.h b/src/gpu/GrProcessorUnitTest.h
new file mode 100644
index 0000000..a368823
--- /dev/null
+++ b/src/gpu/GrProcessorUnitTest.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrProcessorUnitTest_DEFINED
+#define GrProcessorUnitTest_DEFINED
+
+#include "SkTypes.h"
+
+#if GR_TEST_UTILS
+
+#include "../private/GrTextureProxy.h"
+#include "../private/SkTArray.h"
+#include "GrTestUtils.h"
+
+class SkMatrix;
+class GrCaps;
+class GrContext;
+class GrRenderTargetContext;
+struct GrProcessorTestData;
+class GrTexture;
+class GrXPFactory;
+
+namespace GrProcessorUnitTest {
+
+// Used to access the dummy textures in TestCreate procs.
+enum {
+    kSkiaPMTextureIdx = 0,
+    kAlphaTextureIdx = 1,
+};
+
+/** This allows parent FPs to implement a test create with known leaf children in order to avoid
+creating an unbounded FP tree which may overflow various shader limits. */
+sk_sp<GrFragmentProcessor> MakeChildFP(GrProcessorTestData*);
+
+}
+
+/*
+ * GrProcessorTestData is an argument struct to TestCreate functions
+ * fTextures are valid textures that can optionally be used to construct
+ * TextureSampler. The first texture has config kSkia8888_GrPixelConfig and the second has
+ * kAlpha_8_GrPixelConfig. TestCreate functions are also free to create additional textures using
+ * the GrContext.
+ */
+struct GrProcessorTestData {
+    GrProcessorTestData(SkRandom* random,
+                        GrContext* context,
+                        const GrRenderTargetContext* renderTargetContext,
+                        sk_sp<GrTextureProxy> proxies[2])
+            : fRandom(random)
+            , fRenderTargetContext(renderTargetContext)
+            , fContext(context) {
+        SkASSERT(proxies[0] && proxies[1]);
+        fProxies[0] = proxies[0];
+        fProxies[1] = proxies[1];
+    }
+    SkRandom* fRandom;
+    const GrRenderTargetContext* fRenderTargetContext;
+
+    GrContext* context() { return fContext; }
+    GrResourceProvider* resourceProvider();
+    const GrCaps* caps();
+    sk_sp<GrTextureProxy> textureProxy(int index) { return fProxies[index]; }
+
+private:
+    GrContext* fContext;
+    sk_sp<GrTextureProxy> fProxies[2];
+};
+
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+class GrProcessor;
+class GrTexture;
+
+template <class Processor> class GrProcessorTestFactory : private SkNoncopyable {
+public:
+    typedef sk_sp<Processor> (*MakeProc)(GrProcessorTestData*);
+
+    GrProcessorTestFactory(MakeProc makeProc) {
+        fMakeProc = makeProc;
+        GetFactories()->push_back(this);
+    }
+
+    /** Pick a random factory function and create a processor.  */
+    static sk_sp<Processor> Make(GrProcessorTestData* data) {
+        VerifyFactoryCount();
+        SkASSERT(GetFactories()->count());
+        uint32_t idx = data->fRandom->nextRangeU(0, GetFactories()->count() - 1);
+        return MakeIdx(idx, data);
+    }
+
+    /** Number of registered factory functions */
+    static int Count() { return GetFactories()->count(); }
+
+    /** Use factory function at Index idx to create a processor. */
+    static sk_sp<Processor> MakeIdx(int idx, GrProcessorTestData* data) {
+        GrProcessorTestFactory<Processor>* factory = (*GetFactories())[idx];
+        sk_sp<Processor> processor = factory->fMakeProc(data);
+        SkASSERT(processor);
+        return processor;
+    }
+
+private:
+    /**
+     * A test function which verifies the count of factories.
+     */
+    static void VerifyFactoryCount();
+
+    MakeProc fMakeProc;
+
+    static SkTArray<GrProcessorTestFactory<Processor>*, true>* GetFactories();
+};
+
+class GrXPFactoryTestFactory : private SkNoncopyable {
+public:
+    using GetFn = const GrXPFactory*(GrProcessorTestData*);
+
+    GrXPFactoryTestFactory(GetFn* getProc) : fGetProc(getProc) { GetFactories()->push_back(this); }
+
+    static const GrXPFactory* Get(GrProcessorTestData* data) {
+        VerifyFactoryCount();
+        SkASSERT(GetFactories()->count());
+        uint32_t idx = data->fRandom->nextRangeU(0, GetFactories()->count() - 1);
+        const GrXPFactory* xpf = (*GetFactories())[idx]->fGetProc(data);
+        SkASSERT(xpf);
+        return xpf;
+    }
+
+private:
+    static void VerifyFactoryCount();
+
+    GetFn* fGetProc;
+    static SkTArray<GrXPFactoryTestFactory*, true>* GetFactories();
+};
+
+/** GrProcessor subclasses should insert this macro in their declaration to be included in the
+ *  program generation unit test.
+ */
+#define GR_DECLARE_GEOMETRY_PROCESSOR_TEST                                                         \
+    static GrProcessorTestFactory<GrGeometryProcessor> gTestFactory SK_UNUSED;                     \
+    static sk_sp<GrGeometryProcessor> TestCreate(GrProcessorTestData*)
+
+#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST                                                         \
+    static GrProcessorTestFactory<GrFragmentProcessor> gTestFactory SK_UNUSED;                     \
+    static sk_sp<GrFragmentProcessor> TestCreate(GrProcessorTestData*)
+
+#define GR_DECLARE_XP_FACTORY_TEST                                                                 \
+    static GrXPFactoryTestFactory gTestFactory SK_UNUSED;                                          \
+    static const GrXPFactory* TestGet(GrProcessorTestData*)
+
+/** GrProcessor subclasses should insert this macro in their implementation file. They must then
+ *  also implement this static function:
+ *      GrProcessor* TestCreate(GrProcessorTestData*);
+ */
+#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Effect)                                                  \
+    GrProcessorTestFactory<GrFragmentProcessor> Effect::gTestFactory(Effect::TestCreate)
+
+#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(Effect)                                                  \
+    GrProcessorTestFactory<GrGeometryProcessor> Effect::gTestFactory(Effect::TestCreate)
+
+#define GR_DEFINE_XP_FACTORY_TEST(Factory)                                                         \
+    GrXPFactoryTestFactory Factory::gTestFactory(Factory::TestGet)
+
+#else // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+// The unit test relies on static initializers. Just declare the TestCreate function so that
+// its definitions will compile.
+#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST                                                         \
+    static sk_sp<GrFragmentProcessor> TestCreate(GrProcessorTestData*)
+#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(X)
+
+// 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*)
+#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(X)
+
+// The unit test relies on static initializers. Just declare the TestGet function so that
+// its definitions will compile.
+#define GR_DECLARE_XP_FACTORY_TEST                                                                 \
+    const GrXPFactory* TestGet(GrProcessorTestData*)
+#define GR_DEFINE_XP_FACTORY_TEST(X)
+
+#endif  // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+#else   // GR_TEST_UTILS
+    #define GR_DECLARE_GEOMETRY_PROCESSOR_TEST
+    #define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+    #define GR_DECLARE_XP_FACTORY_TEST
+    #define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
+    #define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
+    #define GR_DEFINE_XP_FACTORY_TEST(...)
+    #define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+    #define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
+    #define GR_DECLARE_GEOMETRY_PROCESSOR_TEST
+    #define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
+    #define GR_DECLARE_XP_FACTORY_TEST
+    #define GR_DEFINE_XP_FACTORY_TEST(...)
+#endif  // GR_TEST_UTILS
+#endif  // GrProcessorUnitTest_DEFINED
diff --git a/src/gpu/GrProgramDesc.cpp b/src/gpu/GrProgramDesc.cpp
index d17c187..ca81e5a 100644
--- a/src/gpu/GrProgramDesc.cpp
+++ b/src/gpu/GrProgramDesc.cpp
@@ -5,9 +5,9 @@
  * found in the LICENSE file.
  */
 #include "GrProgramDesc.h"
-
-#include "GrProcessor.h"
 #include "GrPipeline.h"
+#include "GrPrimitiveProcessor.h"
+#include "GrProcessor.h"
 #include "GrRenderTargetPriv.h"
 #include "GrShaderCaps.h"
 #include "GrTexturePriv.h"
@@ -61,13 +61,13 @@
                    (caps.samplerPrecision(config, visibility) << (8 + kSamplerOrImageTypeKeyBits)));
 }
 
-static uint16_t storage_image_key(const GrProcessor::ImageStorageAccess& imageAccess) {
-    GrSLType type = imageAccess.texture()->texturePriv().imageStorageType();
+static uint16_t storage_image_key(const GrResourceIOProcessor::ImageStorageAccess& imageAccess) {
+    GrSLType type = imageAccess.proxy()->imageStorageType();
     return image_storage_or_sampler_uniform_type_key(type) |
            (int)imageAccess.format() << kSamplerOrImageTypeKeyBits;
 }
 
-static void add_sampler_and_image_keys(GrProcessorKeyBuilder* b, const GrProcessor& proc,
+static void add_sampler_and_image_keys(GrProcessorKeyBuilder* b, const GrResourceIOProcessor& proc,
                                        const GrShaderCaps& caps) {
     int numTextureSamplers = proc.numTextureSamplers();
     int numBuffers = proc.numBuffers();
@@ -81,13 +81,14 @@
     uint16_t* k16 = SkTCast<uint16_t*>(b->add32n(word32Count));
     int j = 0;
     for (int i = 0; i < numTextureSamplers; ++i, ++j) {
-        const GrProcessor::TextureSampler& sampler = proc.textureSampler(i);
-        const GrTexture* tex = sampler.texture();
+        const GrResourceIOProcessor::TextureSampler& sampler = proc.textureSampler(i);
+        const GrTexture* tex = sampler.peekTexture();
+
         k16[j] = sampler_key(tex->texturePriv().samplerType(), tex->config(), sampler.visibility(),
                              caps);
     }
     for (int i = 0; i < numBuffers; ++i, ++j) {
-        const GrProcessor::BufferAccess& access = proc.bufferAccess(i);
+        const GrResourceIOProcessor::BufferAccess& access = proc.bufferAccess(i);
         k16[j] = sampler_key(kBufferSampler_GrSLType, access.texelConfig(), access.visibility(),
                              caps);
     }
@@ -109,7 +110,7 @@
  * transforms, etc, for the space allotted in the meta-key.  NOTE, both FPs and GPs share this
  * function because it is hairy, though FPs do not have attribs, and GPs do not have transforms
  */
-static bool gen_meta_key(const GrProcessor& proc,
+static bool gen_meta_key(const GrResourceIOProcessor& proc,
                          const GrShaderCaps& shaderCaps,
                          uint32_t transformKey,
                          GrProcessorKeyBuilder* b) {
@@ -130,6 +131,22 @@
     return true;
 }
 
+static bool gen_meta_key(const GrXferProcessor& xp,
+                         const GrShaderCaps& shaderCaps,
+                         GrProcessorKeyBuilder* b) {
+    size_t processorKeySize = b->size();
+    uint32_t classID = xp.classID();
+
+    // Currently we allow 16 bits for the class id and the overall processor key size.
+    static const uint32_t kMetaKeyInvalidMask = ~((uint32_t)SK_MaxU16);
+    if ((processorKeySize | classID) & kMetaKeyInvalidMask) {
+        return false;
+    }
+
+    b->add32((classID << 16) | SkToU32(processorKeySize));
+    return true;
+}
+
 static bool gen_frag_proc_and_meta_keys(const GrPrimitiveProcessor& primProc,
                                         const GrFragmentProcessor& fp,
                                         const GrShaderCaps& shaderCaps,
@@ -180,8 +197,14 @@
     }
 
     const GrXferProcessor& xp = pipeline.getXferProcessor();
-    xp.getGLSLProcessorKey(shaderCaps, &b);
-    if (!gen_meta_key(xp, shaderCaps, 0, &b)) {
+    const GrSurfaceOrigin* originIfDstTexture = nullptr;
+    GrSurfaceOrigin origin;
+    if (pipeline.dstTextureProxy()) {
+        origin = pipeline.dstTextureProxy()->origin();
+        originIfDstTexture = &origin;
+    }
+    xp.getGLSLProcessorKey(shaderCaps, &b, originIfDstTexture);
+    if (!gen_meta_key(xp, shaderCaps, &b)) {
         desc->key().reset();
         return false;
     }
diff --git a/src/gpu/GrProgramElement.h b/src/gpu/GrProgramElement.h
new file mode 100644
index 0000000..425f57a
--- /dev/null
+++ b/src/gpu/GrProgramElement.h
@@ -0,0 +1,128 @@
+/*
+ * 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 GrProgramElement_DEFINED
+#define GrProgramElement_DEFINED
+
+#include "../private/SkTArray.h"
+#include "SkRefCnt.h"
+
+class GrGpuResourceRef;
+
+/**
+ * Note: We are converting GrProcessor from ref counting to a single owner model using move
+ * semantics. This class will be removed.
+ *
+ * This is used to track "refs" for two separate types GrProcessor ownership. A regular ref is owned
+ * by any client that may continue to issue draws that use the GrProgramElement. A recorded op or
+ * GrPipeline uses "pending executions" instead of refs. A pending execution is cleared after the
+ * draw is executed (or aborted).
+ *
+ * While a GrProgramElement is ref'ed any resources it owns are also ref'ed. However, once it gets
+ * into the state where it has pending executions AND no refs then it converts its ownership of
+ * its GrGpuResources from refs to pending IOs. The pending IOs allow the cache to track when it is
+ * safe to recycle a resource even though we still have buffered GrOps that read or write to the
+ * the resource.
+ *
+ * To make this work the subclass GrProcessor implements addPendingIOs, removeRefs, and
+ * pendingIOComplete. addPendingIOs adds pending reads/writes to GrGpuResources owned by the
+ * processor as appropriate when the processor is recorded in a GrOpList. removeRefs is called when
+ * the ref count reaches 0 and the GrProcessor is only owned by "pending executions".
+ * pendingIOComplete occurs if the resource is still owned by a ref but all recorded draws have been
+ * completed. Whenever pending executions and refs reach zero the processor is deleted.
+ *
+ * The GrProcessor may also implement notifyRefCntIsZero in order to change its ownership of child
+ * processors from ref to pending execution when the processor is first owned exclusively in pending
+ * execution mode.
+ */
+class GrProgramElement : public SkNoncopyable {
+public:
+    virtual ~GrProgramElement() {
+        // fRefCnt can be one when an effect is created statically using GR_CREATE_STATIC_EFFECT
+        SkASSERT((0 == fRefCnt || 1 == fRefCnt) && 0 == fPendingExecutions);
+        // Set to invalid values.
+        SkDEBUGCODE(fRefCnt = fPendingExecutions = -10;)
+    }
+
+    void ref() const {
+        this->validate();
+        // Once the ref cnt reaches zero it should never be ref'ed again.
+        SkASSERT(fRefCnt > 0);
+        ++fRefCnt;
+        this->validate();
+    }
+
+    void unref() const {
+        this->validate();
+        --fRefCnt;
+        if (0 == fRefCnt) {
+            this->notifyRefCntIsZero();
+            if (0 == fPendingExecutions) {
+                delete this;
+                return;
+            } else {
+                this->removeRefs();
+            }
+        }
+        this->validate();
+    }
+
+    void validate() const {
+#ifdef SK_DEBUG
+        SkASSERT(fRefCnt >= 0);
+        SkASSERT(fPendingExecutions >= 0);
+        SkASSERT(fRefCnt + fPendingExecutions > 0);
+#endif
+    }
+
+protected:
+    GrProgramElement() : fRefCnt(1), fPendingExecutions(0) {}
+
+    void addPendingExecution() const {
+        this->validate();
+        if (0 == fPendingExecutions) {
+            this->addPendingIOs();
+        }
+        ++fPendingExecutions;
+        this->validate();
+    }
+
+    void completedExecution() const {
+        this->validate();
+        --fPendingExecutions;
+        if (0 == fPendingExecutions) {
+            if (0 == fRefCnt) {
+                delete this;
+                return;
+            } else {
+                this->pendingIOComplete();
+            }
+        }
+        this->validate();
+    }
+
+private:
+    virtual void addPendingIOs() const = 0;
+    virtual void removeRefs() const = 0;
+    virtual void pendingIOComplete() const = 0;
+
+    /** This will be called when the ref cnt is zero. The object may or may not have pending
+        executions. */
+    virtual void notifyRefCntIsZero() const = 0;
+
+    mutable int32_t fRefCnt;
+    // Count of deferred executions not yet issued to the 3D API.
+    mutable int32_t fPendingExecutions;
+
+    // Only these classes can access addPendingExecution() and completedExecution().
+    template <typename T> friend class GrPendingProgramElement;
+    friend class GrProcessorSet;
+
+    typedef SkNoncopyable INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrRect.h b/src/gpu/GrRect.h
index 14130f8..95102a8 100644
--- a/src/gpu/GrRect.h
+++ b/src/gpu/GrRect.h
@@ -54,4 +54,21 @@
     }
 };
 
+/** Returns true if the rectangles have a nonzero area of overlap. It assumed that rects can be
+    infinitely small but not "inverted". */
+static inline bool GrRectsOverlap(const SkRect& a, const SkRect& b) {
+    // See skbug.com/6607 about the isFinite() checks.
+    SkASSERT(!a.isFinite() || (a.fLeft <= a.fRight && a.fTop <= a.fBottom));
+    SkASSERT(!b.isFinite() || (b.fLeft <= b.fRight && b.fTop <= b.fBottom));
+    return a.fRight > b.fLeft && a.fBottom > b.fTop && b.fRight > a.fLeft && b.fBottom > a.fTop;
+}
+
+/** Returns true if the rectangles overlap or share an edge or corner. It assumed that rects can be
+    infinitely small but not "inverted". */
+static inline bool GrRectsTouchOrOverlap(const SkRect& a, const SkRect& b) {
+    // See skbug.com/6607 about the isFinite() checks.
+    SkASSERT(!a.isFinite() || (a.fLeft <= a.fRight && a.fTop <= a.fBottom));
+    SkASSERT(!b.isFinite() || (b.fLeft <= b.fRight && b.fTop <= b.fBottom));
+    return a.fRight >= b.fLeft && a.fBottom >= b.fTop && b.fRight >= a.fLeft && b.fBottom >= a.fTop;
+}
 #endif
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 4b7e69b..8c98ac1 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -45,7 +45,7 @@
     bool iior;
     stack.getBounds(&stackBounds, &stackBoundsType, &iior);
 
-    if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)) {
+    if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
         bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
         fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
         return;
@@ -72,7 +72,10 @@
         SkRect tightBounds;
         SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
         fIBounds = GrClip::GetPixelIBounds(tightBounds);
-        SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above.
+        if (fIBounds.isEmpty()) {
+            fInitialState = InitialState::kAllOut;
+            return;
+        }
         fHasIBounds = true;
 
         // Implement the clip with an AA rect element.
@@ -92,7 +95,10 @@
     }
 
     fIBounds = GrClip::GetPixelIBounds(tighterQuery);
-    SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above.
+    if (fIBounds.isEmpty()) {
+        fInitialState = InitialState::kAllOut;
+        return;
+    }
     fHasIBounds = true;
 
     // Now that we have determined the bounds to use and filtered out the trivial cases, call the
@@ -704,7 +710,7 @@
     for (ElementList::Iter iter(fElements); iter.get(); iter.next()) {
         const Element* element = iter.get();
         GrAAType aaType = GrAAType::kNone;
-        if (element->isAA() && renderTargetContext->isStencilBufferMultisampled()) {
+        if (element->isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) {
             aaType = GrAAType::kMSAA;
         }
 
@@ -730,7 +736,7 @@
 
             GrShape shape(clipPath, GrStyle::SimpleFill());
             GrPathRenderer::CanDrawPathArgs canDrawArgs;
-            canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
+            canDrawArgs.fCaps = context->caps();
             canDrawArgs.fViewMatrix = &SkMatrix::I();
             canDrawArgs.fShape = &shape;
             canDrawArgs.fAAType = aaType;
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index f495cd3..8b1fde6 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -19,31 +19,17 @@
 
 GrRenderTarget::GrRenderTarget(GrGpu* gpu, const GrSurfaceDesc& desc, Flags flags,
                                GrStencilAttachment* stencil)
-    : INHERITED(gpu, desc)
-    , fStencilAttachment(stencil)
-    , fMultisampleSpecsID(0)
-    , fFlags(flags) {
-    SkASSERT(!(fFlags & Flags::kMixedSampled) || fDesc.fSampleCnt > 0);
+        : INHERITED(gpu, desc)
+        , fSampleCnt(desc.fSampleCnt)
+        , fStencilAttachment(stencil)
+        , fMultisampleSpecsID(0)
+        , fFlags(flags) {
+    SkASSERT(desc.fFlags & kRenderTarget_GrSurfaceFlag);
+    SkASSERT(!(fFlags & Flags::kMixedSampled) || fSampleCnt > 0);
     SkASSERT(!(fFlags & Flags::kWindowRectsSupport) || gpu->caps()->maxWindowRectangles() > 0);
     fResolveRect.setLargestInverted();
 }
 
-void GrRenderTarget::discard() {
-    // go through context so that all necessary flushing occurs
-    GrContext* context = this->getContext();
-    if (!context) {
-        return;
-    }
-
-    sk_sp<GrRenderTargetContext> renderTargetContext(
-        context->contextPriv().makeWrappedRenderTargetContext(sk_ref_sp(this), nullptr));
-    if (!renderTargetContext) {
-        return;
-    }
-
-    renderTargetContext->discard();
-}
-
 void GrRenderTarget::flagAsNeedingResolve(const SkIRect* rect) {
     if (kCanResolve_ResolveType == getResolveType()) {
         if (rect) {
@@ -77,10 +63,6 @@
 void GrRenderTarget::onAbandon() {
     SkSafeSetNull(fStencilAttachment);
 
-    // The contents of this renderTarget are gone/invalid. It isn't useful to point back
-    // the creating opList.
-    this->setLastOpList(nullptr);
-
     INHERITED::onAbandon();
 }
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 70d4485..0bbfbf6 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -6,8 +6,11 @@
  */
 
 #include "GrRenderTargetContext.h"
+#include "../private/GrAuditTrail.h"
+#include "../private/SkShadowFlags.h"
 #include "GrAppliedClip.h"
 #include "GrColor.h"
+#include "GrContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrFixedClip.h"
 #include "GrGpuResourcePriv.h"
@@ -18,16 +21,20 @@
 #include "GrRenderTargetPriv.h"
 #include "GrResourceProvider.h"
 #include "GrStencilAttachment.h"
+#include "SkDrawShadowRec.h"
 #include "SkLatticeIter.h"
 #include "SkMatrixPriv.h"
 #include "SkSurfacePriv.h"
 #include "effects/GrRRectEffect.h"
 #include "instanced/InstancedRendering.h"
 #include "ops/GrClearOp.h"
-#include "ops/GrDrawOp.h"
+#include "ops/GrClearStencilClipOp.h"
+#include "ops/GrDiscardOp.h"
 #include "ops/GrDrawAtlasOp.h"
+#include "ops/GrDrawOp.h"
 #include "ops/GrDrawVerticesOp.h"
 #include "ops/GrLatticeOp.h"
+#include "ops/GrNonAAFillRectOp.h"
 #include "ops/GrOp.h"
 #include "ops/GrOvalOpFactory.h"
 #include "ops/GrRectOpFactory.h"
@@ -36,21 +43,18 @@
 #include "ops/GrStencilPathOp.h"
 #include "text/GrAtlasTextContext.h"
 #include "text/GrStencilAndCoverTextContext.h"
-#include "../private/GrAuditTrail.h"
 
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this->drawingManager()->getContext())
 #define ASSERT_SINGLE_OWNER \
-    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
+    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(this->singleOwner());)
 #define ASSERT_SINGLE_OWNER_PRIV \
-    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fRenderTargetContext->fSingleOwner);)
+    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fRenderTargetContext->singleOwner());)
 #define RETURN_IF_ABANDONED        if (this->drawingManager()->wasAbandoned()) { return; }
 #define RETURN_IF_ABANDONED_PRIV   if (fRenderTargetContext->drawingManager()->wasAbandoned()) { return; }
 #define RETURN_FALSE_IF_ABANDONED  if (this->drawingManager()->wasAbandoned()) { return false; }
 #define RETURN_FALSE_IF_ABANDONED_PRIV  if (fRenderTargetContext->drawingManager()->wasAbandoned()) { return false; }
 #define RETURN_NULL_IF_ABANDONED   if (this->drawingManager()->wasAbandoned()) { return nullptr; }
 
-using gr_instanced::InstancedRendering;
-
 class AutoCheckFlush {
 public:
     AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) {
@@ -79,7 +83,7 @@
                                              GrSingleOwner* singleOwner)
     : GrSurfaceContext(context, drawingMgr, std::move(colorSpace), auditTrail, singleOwner)
     , fRenderTargetProxy(std::move(rtp))
-    , fOpList(SkSafeRef(fRenderTargetProxy->getLastRenderTargetOpList()))
+    , fOpList(sk_ref_sp(fRenderTargetProxy->getLastRenderTargetOpList()))
     , fInstancedPipelineInfo(fRenderTargetProxy.get())
     , fColorXformFromSRGB(nullptr)
     , fSurfaceProps(SkSurfacePropsCopyOrDefault(surfaceProps)) {
@@ -97,18 +101,13 @@
     fRenderTargetProxy->validate(fContext);
 
     if (fOpList && !fOpList->isClosed()) {
-        SkASSERT(fRenderTargetProxy->getLastOpList() == fOpList);
+        SkASSERT(fRenderTargetProxy->getLastOpList() == fOpList.get());
     }
 }
 #endif
 
 GrRenderTargetContext::~GrRenderTargetContext() {
     ASSERT_SINGLE_OWNER
-    SkSafeUnref(fOpList);
-}
-
-GrRenderTarget* GrRenderTargetContext::instantiate() {
-    return fRenderTargetProxy->instantiate(fContext->resourceProvider());
 }
 
 GrTextureProxy* GrRenderTargetContext::asTextureProxy() {
@@ -124,86 +123,25 @@
     SkDEBUGCODE(this->validate();)
 
     if (!fOpList || fOpList->isClosed()) {
-        fOpList = this->drawingManager()->newOpList(fRenderTargetProxy.get());
+        fOpList = this->drawingManager()->newRTOpList(fRenderTargetProxy.get());
     }
 
-    return fOpList;
+    return fOpList.get();
 }
 
-// TODO: move this (and GrTextContext::copy) to GrSurfaceContext?
+// MDB TODO: move this (and GrTextContext::copy) to GrSurfaceContext?
 bool GrRenderTargetContext::onCopy(GrSurfaceProxy* srcProxy,
                                    const SkIRect& srcRect,
                                    const SkIPoint& dstPoint) {
     ASSERT_SINGLE_OWNER
     RETURN_FALSE_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::copy");
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::onCopy");
 
-    // TODO: defer instantiation until flush time
-    sk_sp<GrSurface> src(sk_ref_sp(srcProxy->instantiate(fContext->resourceProvider())));
-    if (!src) {
-        return false;
-    }
-
-    // TODO: This needs to be fixed up since it ends the deferral of the GrRenderTarget.
-    sk_sp<GrRenderTarget> rt(
-                        sk_ref_sp(fRenderTargetProxy->instantiate(fContext->resourceProvider())));
-    if (!rt) {
-        return false;
-    }
-
-    return this->getOpList()->copySurface(rt.get(), src.get(), srcRect, dstPoint);
+    return this->getOpList()->copySurface(*this->caps(),
+                                          this->asSurfaceProxy(), srcProxy, srcRect, dstPoint);
 }
 
-// TODO: move this (and GrTextureContext::onReadPixels) to GrSurfaceContext?
-bool GrRenderTargetContext::onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                                         size_t dstRowBytes, int x, int y, uint32_t flags) {
-    // TODO: teach GrRenderTarget to take ImageInfo directly to specify the src pixels
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo, *fContext->caps());
-    if (kUnknown_GrPixelConfig == config) {
-        return false;
-    }
-
-    // TODO: this seems to duplicate code in SkImage_Gpu::onReadPixels
-    if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
-        flags |= GrContext::kUnpremul_PixelOpsFlag;
-    }
-
-    // Deferral of the VRAM resources must end in this instance anyway
-    sk_sp<GrRenderTarget> rt(
-                        sk_ref_sp(fRenderTargetProxy->instantiate(fContext->resourceProvider())));
-    if (!rt) {
-        return false;
-    }
-
-    return rt->readPixels(this->getColorSpace(), x, y, dstInfo.width(), dstInfo.height(),
-                          config, dstInfo.colorSpace(), dstBuffer, dstRowBytes, flags);
-}
-
-// TODO: move this (and GrTextureContext::onReadPixels) to GrSurfaceContext?
-bool GrRenderTargetContext::onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
-                                          size_t srcRowBytes, int x, int y, uint32_t flags) {
-    // TODO: teach GrRenderTarget to take ImageInfo directly to specify the src pixels
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(srcInfo, *fContext->caps());
-    if (kUnknown_GrPixelConfig == config) {
-        return false;
-    }
-    if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
-        flags |= GrContext::kUnpremul_PixelOpsFlag;
-    }
-
-    // Deferral of the VRAM resources must end in this instance anyway
-    sk_sp<GrRenderTarget> rt(
-                        sk_ref_sp(fRenderTargetProxy->instantiate(fContext->resourceProvider())));
-    if (!rt) {
-        return false;
-    }
-
-    return rt->writePixels(this->getColorSpace(), x, y, srcInfo.width(), srcInfo.height(),
-                           config, srcInfo.colorSpace(), srcBuffer, srcRowBytes, flags);
-}
-
-
 void GrRenderTargetContext::drawText(const GrClip& clip, const SkPaint& skPaint,
                                      const SkMatrix& viewMatrix, const char text[],
                                      size_t byteLength, SkScalar x, SkScalar y,
@@ -255,14 +193,15 @@
 
     AutoCheckFlush acf(this->drawingManager());
 
-    // TODO: This needs to be fixed up since it ends the deferral of the GrRenderTarget.
-    sk_sp<GrRenderTarget> rt(
-                        sk_ref_sp(fRenderTargetProxy->instantiate(fContext->resourceProvider())));
-    if (!rt) {
-        return;
+    // Currently this just inserts a discard op. However, once in MDB this can remove all the
+    // previously recorded ops and change the load op to discard.
+    if (this->caps()->discardRenderTargetSupport()) {
+        std::unique_ptr<GrOp> op(GrDiscardOp::Make(fRenderTargetProxy.get()));
+        if (!op) {
+            return;
+        }
+        this->getOpList()->addOp(std::move(op), *this->caps());
     }
-
-    this->getOpList()->discard(this);
 }
 
 void GrRenderTargetContext::clear(const SkIRect* rect,
@@ -286,10 +225,8 @@
 
     AutoCheckFlush acf(fRenderTargetContext->drawingManager());
 
-    SkIRect rtRect = SkIRect::MakeWH(fRenderTargetContext->fRenderTargetProxy->worstCaseWidth(
-                                            *fRenderTargetContext->caps()),
-                                     fRenderTargetContext->fRenderTargetProxy->worstCaseHeight(
-                                            *fRenderTargetContext->caps()));
+    SkIRect rtRect = SkIRect::MakeWH(fRenderTargetContext->fRenderTargetProxy->worstCaseWidth(),
+                                     fRenderTargetContext->fRenderTargetProxy->worstCaseHeight());
 
     if (clearRect) {
         if (clearRect->contains(rtRect)) {
@@ -319,19 +256,14 @@
                                                   GrAAType::kNone);
 
     } else {
-        if (!fRenderTargetContext->accessRenderTarget()) {
-            return;
-        }
-
         // This path doesn't handle coalescing of full screen clears b.c. it
         // has to clear the entire render target - not just the content area.
         // It could be done but will take more finagling.
-        std::unique_ptr<GrOp> op(GrClearOp::Make(
-                rtRect, color, fRenderTargetContext->accessRenderTarget(), !clearRect));
+        std::unique_ptr<GrOp> op(GrClearOp::Make(rtRect, color, !clearRect));
         if (!op) {
             return;
         }
-        fRenderTargetContext->getOpList()->addOp(std::move(op), fRenderTargetContext);
+        fRenderTargetContext->getOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
     }
 }
 
@@ -375,18 +307,13 @@
 
         this->drawRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), SkRect::Make(clearRect));
     } else if (isFull) {
-        if (this->accessRenderTarget()) {
-            this->getOpList()->fullClear(this, color);
-        }
+        this->getOpList()->fullClear(*this->caps(), color);
     } else {
-        if (!this->accessRenderTarget()) {
-            return;
-        }
-        std::unique_ptr<GrOp> op(GrClearOp::Make(clip, color, this->accessRenderTarget()));
+        std::unique_ptr<GrOp> op(GrClearOp::Make(clip, color, this->asSurfaceProxy()));
         if (!op) {
             return;
         }
-        this->getOpList()->addOp(std::move(op), this);
+        this->getOpList()->addOp(std::move(op), *this->caps());
     }
 }
 
@@ -509,28 +436,28 @@
 
     if (GrCaps::InstancedSupport::kNone != fContext->caps()->instancedSupport() &&
         (!ss || ss->isDisabled(false))) {
-        InstancedRendering* ir = this->getOpList()->instancedRendering();
-        std::unique_ptr<GrDrawOp> op = ir->recordRect(croppedRect, viewMatrix, std::move(paint), aa,
-                                                      fInstancedPipelineInfo);
+        gr_instanced::OpAllocator* oa = this->drawingManager()->instancingAllocator();
+        std::unique_ptr<GrDrawOp> op = oa->recordRect(croppedRect, viewMatrix, std::move(paint),
+                                                      aa, fInstancedPipelineInfo);
         if (op) {
             this->addDrawOp(clip, std::move(op));
             return true;
         }
     }
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
         // The fill path can handle rotation but not skew.
         if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
             SkRect devBoundRect;
             viewMatrix.mapRect(&devBoundRect, croppedRect);
-            std::unique_ptr<GrMeshDrawOp> op =
+            std::unique_ptr<GrLegacyMeshDrawOp> op =
                     GrRectOpFactory::MakeAAFill(paint, viewMatrix, rect, croppedRect, devBoundRect);
             if (op) {
                 GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
                 if (ss) {
                     pipelineBuilder.setUserStencil(ss);
                 }
-                this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+                this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
                 return true;
             }
         }
@@ -631,10 +558,10 @@
         }
 
         bool snapToPixelCenters = false;
-        std::unique_ptr<GrMeshDrawOp> op;
+        std::unique_ptr<GrLegacyMeshDrawOp> op;
 
         GrColor color = paint.getColor();
-        GrAAType aaType = this->decideAAType(aa);
+        GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
         if (GrAAType::kCoverage == aaType) {
             // The stroke path needs the rect to remain axis aligned (no rotation or skew).
             if (viewMatrix.rectStaysRect()) {
@@ -645,7 +572,7 @@
             // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
             // when MSAA is enabled because it can cause ugly artifacts.
             snapToPixelCenters = stroke.getStyle() == SkStrokeRec::kHairline_Style &&
-                                 !fRenderTargetProxy->isUnifiedMultisampled();
+                                 GrFSAAType::kUnifiedMSAA != fRenderTargetProxy->fsaaType();
             op = GrRectOpFactory::MakeNonAAStroke(color, viewMatrix, rect, stroke,
                                                   snapToPixelCenters);
         }
@@ -653,7 +580,7 @@
         if (op) {
             GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
             pipelineBuilder.setSnapVerticesToPixelCenters(snapToPixelCenters);
-            this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+            this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
             return;
         }
     }
@@ -677,12 +604,14 @@
                               "GrRenderTargetContextPriv::clearStencilClip");
 
     AutoCheckFlush acf(fRenderTargetContext->drawingManager());
-    // TODO: This needs to be fixed up since it ends the deferral of the GrRenderTarget.
-    if (!fRenderTargetContext->accessRenderTarget()) {
+
+    std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(
+                                                 clip, insideStencilMask,
+                                                 fRenderTargetContext->fRenderTargetProxy.get()));
+    if (!op) {
         return;
     }
-    fRenderTargetContext->getOpList()->clearStencilClip(clip, insideStencilMask,
-                                                        fRenderTargetContext);
+    fRenderTargetContext->getOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
 }
 
 void GrRenderTargetContextPriv::stencilPath(const GrClip& clip,
@@ -734,10 +663,12 @@
                                                      appliedClip.hasStencilClip(),
                                                      stencilAttachment->bits(),
                                                      appliedClip.scissorState(),
-                                                     fRenderTargetContext->accessRenderTarget(),
                                                      path);
+    if (!op) {
+        return;
+    }
     op->setClippedBounds(bounds);
-    fRenderTargetContext->getOpList()->recordOp(std::move(op), fRenderTargetContext);
+    fRenderTargetContext->getOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
 }
 
 void GrRenderTargetContextPriv::stencilRect(const GrClip& clip,
@@ -808,8 +739,8 @@
     AutoCheckFlush acf(this->drawingManager());
 
     if (GrCaps::InstancedSupport::kNone != fContext->caps()->instancedSupport()) {
-        InstancedRendering* ir = this->getOpList()->instancedRendering();
-        std::unique_ptr<GrDrawOp> op(ir->recordRect(croppedRect, viewMatrix, std::move(paint),
+        gr_instanced::OpAllocator* oa = this->drawingManager()->instancingAllocator();
+        std::unique_ptr<GrDrawOp> op(oa->recordRect(croppedRect, viewMatrix, std::move(paint),
                                                     croppedLocalRect, aa, fInstancedPipelineInfo));
         if (op) {
             this->addDrawOp(clip, std::move(op));
@@ -817,7 +748,7 @@
         }
     }
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage != aaType) {
         this->drawNonAAFilledRect(clip, std::move(paint), viewMatrix, croppedRect,
                                   &croppedLocalRect, nullptr, nullptr, aaType);
@@ -825,10 +756,10 @@
     }
 
     if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
-        std::unique_ptr<GrMeshDrawOp> op = GrAAFillRectOp::MakeWithLocalRect(
+        std::unique_ptr<GrLegacyMeshDrawOp> op = GrAAFillRectOp::MakeWithLocalRect(
                 paint.getColor(), viewMatrix, croppedRect, croppedLocalRect);
         GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-        this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+        this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
         return;
     }
 
@@ -864,8 +795,8 @@
     AutoCheckFlush acf(this->drawingManager());
 
     if (GrCaps::InstancedSupport::kNone != fContext->caps()->instancedSupport()) {
-        InstancedRendering* ir = this->getOpList()->instancedRendering();
-        std::unique_ptr<GrDrawOp> op(ir->recordRect(croppedRect, viewMatrix, std::move(paint),
+        gr_instanced::OpAllocator* oa = this->drawingManager()->instancingAllocator();
+        std::unique_ptr<GrDrawOp> op(oa->recordRect(croppedRect, viewMatrix, std::move(paint),
                                                     localMatrix, aa, fInstancedPipelineInfo));
         if (op) {
             this->addDrawOp(clip, std::move(op));
@@ -873,7 +804,7 @@
         }
     }
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage != aaType) {
         this->drawNonAAFilledRect(clip, std::move(paint), viewMatrix, croppedRect, nullptr,
                                   &localMatrix, nullptr, aaType);
@@ -881,10 +812,10 @@
     }
 
     if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
-        std::unique_ptr<GrMeshDrawOp> op =
+        std::unique_ptr<GrLegacyMeshDrawOp> op =
                 GrAAFillRectOp::Make(paint.getColor(), viewMatrix, localMatrix, croppedRect);
         GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-        this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+        this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
         return;
     }
 
@@ -905,42 +836,8 @@
 void GrRenderTargetContext::drawVertices(const GrClip& clip,
                                          GrPaint&& paint,
                                          const SkMatrix& viewMatrix,
-                                         GrPrimitiveType primitiveType,
-                                         int vertexCount,
-                                         const SkPoint positions[],
-                                         const SkPoint texCoords[],
-                                         const uint32_t colors[],
-                                         const uint16_t indices[],
-                                         int indexCount,
-                                         ColorArrayType colorArrayType) {
-    ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
-    SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::drawVertices");
-
-    AutoCheckFlush acf(this->drawingManager());
-
-    // TODO clients should give us bounds
-    SkRect bounds;
-    if (!bounds.setBoundsCheck(positions, vertexCount)) {
-        SkDebugf("drawVertices call empty bounds\n");
-        return;
-    }
-
-    std::unique_ptr<GrMeshDrawOp> op = GrDrawVerticesOp::Make(
-            paint.getColor(), primitiveType, viewMatrix, positions, vertexCount, indices,
-            indexCount, colors, texCoords, bounds, colorArrayType);
-    if (!op) {
-        return;
-    }
-    GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
-}
-
-void GrRenderTargetContext::drawVertices(const GrClip& clip,
-                                         GrPaint&& paint,
-                                         const SkMatrix& viewMatrix,
-                                         sk_sp<SkVertices> vertices) {
+                                         sk_sp<SkVertices> vertices,
+                                         GrPrimitiveType* overridePrimType) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
@@ -949,13 +846,16 @@
     AutoCheckFlush acf(this->drawingManager());
 
     SkASSERT(vertices);
-    std::unique_ptr<GrMeshDrawOp> op =
-            GrDrawVerticesOp::Make(paint.getColor(), std::move(vertices), viewMatrix);
+    std::unique_ptr<GrLegacyMeshDrawOp> op = GrDrawVerticesOp::Make(paint.getColor(),
+                                                                    std::move(vertices), viewMatrix,
+                                                                    this->isGammaCorrect(),
+                                                                    fColorXformFromSRGB,
+                                                                    overridePrimType);
     if (!op) {
         return;
     }
     GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -974,10 +874,10 @@
 
     AutoCheckFlush acf(this->drawingManager());
 
-    std::unique_ptr<GrMeshDrawOp> op =
+    std::unique_ptr<GrLegacyMeshDrawOp> op =
             GrDrawAtlasOp::Make(paint.getColor(), viewMatrix, spriteCount, xform, texRect, colors);
     GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1016,28 +916,25 @@
 
     if (GrCaps::InstancedSupport::kNone != fContext->caps()->instancedSupport() &&
         stroke.isFillStyle()) {
-        InstancedRendering* ir = this->getOpList()->instancedRendering();
+        gr_instanced::OpAllocator* oa = this->drawingManager()->instancingAllocator();
         std::unique_ptr<GrDrawOp> op(
-                ir->recordRRect(rrect, viewMatrix, std::move(paint), aa, fInstancedPipelineInfo));
+                oa->recordRRect(rrect, viewMatrix, std::move(paint), aa, fInstancedPipelineInfo));
         if (op) {
             this->addDrawOp(*clip, std::move(op));
             return;
         }
     }
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
         const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
-        std::unique_ptr<GrMeshDrawOp> op =
-                GrOvalOpFactory::MakeRRectOp(paint.getColor(),
-                                             paint.usesDistanceVectorField(),
-                                             viewMatrix,
-                                             rrect,
-                                             stroke,
-                                             shaderCaps);
+        std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeRRectOp(std::move(paint),
+                                                                    viewMatrix,
+                                                                    rrect,
+                                                                    stroke,
+                                                                    shaderCaps);
         if (op) {
-            GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-            this->addMeshDrawOp(pipelineBuilder, *clip, std::move(op));
+            this->addDrawOp(*clip, std::move(op));
             return;
         }
     }
@@ -1050,34 +947,210 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrRenderTargetContext::drawShadowRRect(const GrClip& clip,
-                                            GrPaint&& paint,
-                                            const SkMatrix& viewMatrix,
-                                            const SkRRect& rrect,
-                                            SkScalar blurRadius,
-                                            const GrStyle& style) {
+static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
+    SkPoint3 result;
+    m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
+    result.fZ = pt.fZ;
+    return result;
+}
+
+bool GrRenderTargetContext::drawFastShadow(const GrClip& clip,
+                                           GrPaint&& paint,
+                                           const SkMatrix& viewMatrix,
+                                           const SkPath& path,
+                                           const SkDrawShadowRec& rec) {
     ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
+    if (this->drawingManager()->wasAbandoned()) {
+        return true;
+    }
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::drawShadowRRect");
-    if (rrect.isEmpty()) {
-        return;
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::drawFastShadow");
+
+    // check z plane
+    bool tiltZPlane = SkToBool(!SkScalarNearlyZero(rec.fZPlaneParams.fX) ||
+                               !SkScalarNearlyZero(rec.fZPlaneParams.fY));
+    bool skipAnalytic = SkToBool(rec.fFlags & SkShadowFlags::kGeometricOnly_ShadowFlag);
+    if (tiltZPlane || skipAnalytic || !viewMatrix.rectStaysRect() || !viewMatrix.isSimilarity()) {
+        return false;
     }
 
-    SkASSERT(!style.pathEffect()); // this should've been devolved to a path in SkGpuDevice
+    SkRRect rrect;
+    SkRect rect;
+    // we can only handle rects, circles, and rrects with circular corners
+    bool isRRect = path.isRRect(&rrect) && rrect.isSimpleCircular() &&
+        rrect.radii(SkRRect::kUpperLeft_Corner).fX > SK_ScalarNearlyZero;
+    if (!isRRect &&
+        path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height()) &&
+        rect.width() > SK_ScalarNearlyZero) {
+        rrect.setOval(rect);
+        isRRect = true;
+    }
+    if (!isRRect && path.isRect(&rect)) {
+        rrect.setRect(rect);
+        isRRect = true;
+    }
+
+    if (!isRRect) {
+        return false;
+    }
+
+    if (rrect.isEmpty()) {
+        return true;
+    }
 
     AutoCheckFlush acf(this->drawingManager());
-    const SkStrokeRec stroke = style.strokeRec();
-    // TODO: add instancing support?
 
-    const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
-    std::unique_ptr<GrMeshDrawOp> op = GrShadowRRectOp::Make(paint.getColor(), viewMatrix, rrect,
-                                                             blurRadius, stroke, shaderCaps);
-    if (op) {
-        GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-        this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
-        return;
+    // transform light
+    SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
+
+    // 1/scale
+    SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ?
+        SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
+        sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
+                       viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
+
+    SkScalar occluderHeight = rec.fZPlaneParams.fZ;
+    GrColor4f color = paint.getColor4f();
+    bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
+
+    if (rec.fAmbientAlpha > 0) {
+        static constexpr float kHeightFactor = 1.0f / 128.0f;
+        static constexpr float kGeomFactor = 64.0f;
+
+        SkScalar devSpaceInsetWidth = occluderHeight * kHeightFactor * kGeomFactor;
+        const float umbraAlpha = (1.0f + SkTMax(occluderHeight * kHeightFactor, 0.0f));
+        const SkScalar devSpaceAmbientBlur = devSpaceInsetWidth * umbraAlpha;
+
+        // Outset the shadow rrect to the border of the penumbra
+        SkScalar ambientPathOutset = devSpaceInsetWidth * devToSrcScale;
+        SkRRect ambientRRect;
+        SkRect outsetRect = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
+        // If the rrect was an oval then its outset will also be one.
+        // We set it explicitly to avoid errors.
+        if (rrect.isOval()) {
+            ambientRRect = SkRRect::MakeOval(outsetRect);
+        } else {
+            SkScalar outsetRad = rrect.getSimpleRadii().fX + ambientPathOutset;
+            ambientRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad);
+        }
+
+        GrColor ambientColor = color.mulByScalar(rec.fAmbientAlpha).toGrColor();
+        if (transparent) {
+            // set a large inset to force a fill
+            devSpaceInsetWidth = ambientRRect.width();
+        }
+        // the fraction of the blur we want to apply is devSpaceInsetWidth/devSpaceAmbientBlur,
+        // which is just 1/umbraAlpha.
+        SkScalar blurClamp = SkScalarInvert(umbraAlpha);
+
+        std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(ambientColor, viewMatrix,
+                                                                       ambientRRect,
+                                                                       devSpaceAmbientBlur,
+                                                                       devSpaceInsetWidth,
+                                                                       blurClamp);
+        if (op) {
+            GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
+            this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+        }
     }
+
+    if (rec.fSpotAlpha > 0) {
+        float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
+
+        SkScalar devSpaceSpotBlur = 2.0f * rec.fLightRadius * zRatio;
+        // handle scale of radius and pad due to CTM
+        const SkScalar srcSpaceSpotBlur = devSpaceSpotBlur * devToSrcScale;
+
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar spotScale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
+        SkPoint spotOffset = SkPoint::Make(zRatio*(-devLightPos.fX), zRatio*(-devLightPos.fY));
+        // Adjust translate for the effect of the scale.
+        spotOffset.fX += spotScale*viewMatrix[SkMatrix::kMTransX];
+        spotOffset.fY += spotScale*viewMatrix[SkMatrix::kMTransY];
+        // This offset is in dev space, need to transform it into source space.
+        SkMatrix ctmInverse;
+        if (viewMatrix.invert(&ctmInverse)) {
+            ctmInverse.mapPoints(&spotOffset, 1);
+        } else {
+            // Since the matrix is a similarity, this should never happen, but just in case...
+            SkDebugf("Matrix is degenerate. Will not render spot shadow correctly!\n");
+            SkASSERT(false);
+        }
+
+        // Compute the transformed shadow rrect
+        SkRRect spotShadowRRect;
+        SkMatrix shadowTransform;
+        shadowTransform.setScaleTranslate(spotScale, spotScale, spotOffset.fX, spotOffset.fY);
+        rrect.transform(shadowTransform, &spotShadowRRect);
+        SkScalar spotRadius = spotShadowRRect.getSimpleRadii().fX;
+
+        // Compute the insetWidth
+        SkScalar blurOutset = 0.5f*srcSpaceSpotBlur;
+        SkScalar insetWidth = blurOutset;
+        if (transparent) {
+            // If transparent, just do a fill
+            insetWidth += spotShadowRRect.width();
+        } else {
+            // For shadows, instead of using a stroke we specify an inset from the penumbra
+            // border. We want to extend this inset area so that it meets up with the caster
+            // geometry. The inset geometry will by default already be inset by the blur width.
+            //
+            // We compare the min and max corners inset by the radius between the original
+            // rrect and the shadow rrect. The distance between the two plus the difference
+            // between the scaled radius and the original radius gives the distance from the
+            // transformed shadow shape to the original shape in that corner. The max
+            // of these gives the maximum distance we need to cover.
+            //
+            // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+            // that to get the full insetWidth.
+            SkScalar maxOffset;
+            if (rrect.isRect()) {
+                // Manhattan distance works better for rects
+                maxOffset = SkTMax(SkTMax(SkTAbs(spotShadowRRect.rect().fLeft -
+                                                 rrect.rect().fLeft),
+                                          SkTAbs(spotShadowRRect.rect().fTop -
+                                                 rrect.rect().fTop)),
+                                   SkTMax(SkTAbs(spotShadowRRect.rect().fRight -
+                                                 rrect.rect().fRight),
+                                          SkTAbs(spotShadowRRect.rect().fBottom -
+                                                 rrect.rect().fBottom)));
+            } else {
+                SkScalar dr = spotRadius - rrect.getSimpleRadii().fX;
+                SkPoint upperLeftOffset = SkPoint::Make(spotShadowRRect.rect().fLeft -
+                                                        rrect.rect().fLeft + dr,
+                                                        spotShadowRRect.rect().fTop -
+                                                        rrect.rect().fTop + dr);
+                SkPoint lowerRightOffset = SkPoint::Make(spotShadowRRect.rect().fRight -
+                                                         rrect.rect().fRight - dr,
+                                                         spotShadowRRect.rect().fBottom -
+                                                         rrect.rect().fBottom - dr);
+                maxOffset = SkScalarSqrt(SkTMax(upperLeftOffset.lengthSqd(),
+                                                lowerRightOffset.lengthSqd())) + dr;
+            }
+            insetWidth += maxOffset;
+        }
+
+        // Outset the shadow rrect to the border of the penumbra
+        SkRect outsetRect = spotShadowRRect.rect().makeOutset(blurOutset, blurOutset);
+        if (spotShadowRRect.isOval()) {
+            spotShadowRRect = SkRRect::MakeOval(outsetRect);
+        } else {
+            SkScalar outsetRad = spotRadius + blurOutset;
+            spotShadowRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad);
+        }
+
+        GrColor spotColor = color.mulByScalar(rec.fSpotAlpha).toGrColor();
+        std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(spotColor, viewMatrix,
+                                                                       spotShadowRRect,
+                                                                       devSpaceSpotBlur,
+                                                                       insetWidth);
+        if (op) {
+            GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
+            this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+        }
+    }
+
+    return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1092,8 +1165,8 @@
     SkASSERT(!origOuter.isEmpty());
 
     if (GrCaps::InstancedSupport::kNone != fContext->caps()->instancedSupport()) {
-        InstancedRendering* ir = this->getOpList()->instancedRendering();
-        std::unique_ptr<GrDrawOp> op(ir->recordDRRect(
+        gr_instanced::OpAllocator* oa = this->drawingManager()->instancingAllocator();
+        std::unique_ptr<GrDrawOp> op(oa->recordDRRect(
                 origOuter, origInner, viewMatrix, std::move(paint), aa, fInstancedPipelineInfo));
         if (op) {
             this->addDrawOp(clip, std::move(op));
@@ -1101,7 +1174,7 @@
         }
     }
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
 
     GrPrimitiveEdgeType innerEdgeType, outerEdgeType;
     if (GrAAType::kCoverage == aaType) {
@@ -1214,9 +1287,9 @@
         return this->drawPath(clip, std::move(paint), aa, viewMatrix, path, style);
     }
 
-    std::unique_ptr<GrMeshDrawOp> op = GrRegionOp::Make(paint.getColor(), viewMatrix, region);
+    std::unique_ptr<GrLegacyMeshDrawOp> op = GrRegionOp::Make(paint.getColor(), viewMatrix, region);
     GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
 }
 
 void GrRenderTargetContext::drawOval(const GrClip& clip,
@@ -1241,23 +1314,22 @@
 
     if (GrCaps::InstancedSupport::kNone != fContext->caps()->instancedSupport() &&
         stroke.isFillStyle()) {
-        InstancedRendering* ir = this->getOpList()->instancedRendering();
+        gr_instanced::OpAllocator* oa = this->drawingManager()->instancingAllocator();
         std::unique_ptr<GrDrawOp> op(
-                ir->recordOval(oval, viewMatrix, std::move(paint), aa, fInstancedPipelineInfo));
+                oa->recordOval(oval, viewMatrix, std::move(paint), aa, fInstancedPipelineInfo));
         if (op) {
             this->addDrawOp(clip, std::move(op));
             return;
         }
     }
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
         const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
-        std::unique_ptr<GrMeshDrawOp> op =
-                GrOvalOpFactory::MakeOvalOp(paint.getColor(), viewMatrix, oval, stroke, shaderCaps);
+        std::unique_ptr<GrDrawOp> op =
+                GrOvalOpFactory::MakeOvalOp(std::move(paint), viewMatrix, oval, stroke, shaderCaps);
         if (op) {
-            GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-            this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+            this->addDrawOp(clip, std::move(op));
             return;
         }
     }
@@ -1284,20 +1356,19 @@
 
     AutoCheckFlush acf(this->drawingManager());
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType) {
         const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
-        std::unique_ptr<GrMeshDrawOp> op = GrOvalOpFactory::MakeArcOp(paint.getColor(),
-                                                                      viewMatrix,
-                                                                      oval,
-                                                                      startAngle,
-                                                                      sweepAngle,
-                                                                      useCenter,
-                                                                      style,
-                                                                      shaderCaps);
+        std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeArcOp(std::move(paint),
+                                                                  viewMatrix,
+                                                                  oval,
+                                                                  startAngle,
+                                                                  sweepAngle,
+                                                                  useCenter,
+                                                                  style,
+                                                                  shaderCaps);
         if (op) {
-            GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-            this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+            this->addDrawOp(clip, std::move(op));
             return;
         }
     }
@@ -1321,11 +1392,11 @@
 
     AutoCheckFlush acf(this->drawingManager());
 
-    std::unique_ptr<GrMeshDrawOp> op = GrLatticeOp::MakeNonAA(
+    std::unique_ptr<GrLegacyMeshDrawOp> op = GrLatticeOp::MakeNonAA(
             paint.getColor(), viewMatrix, imageWidth, imageHeight, std::move(iter), dst);
 
     GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
 }
 
 void GrRenderTargetContext::prepareForExternalIO() {
@@ -1334,16 +1405,7 @@
     SkDEBUGCODE(this->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::prepareForExternalIO");
 
-    // Deferral of the VRAM resources must end in this instance anyway
-    sk_sp<GrRenderTarget> rt(
-                        sk_ref_sp(fRenderTargetProxy->instantiate(fContext->resourceProvider())));
-    if (!rt) {
-        return;
-    }
-
-    ASSERT_OWNED_RESOURCE(rt);
-
-    this->drawingManager()->prepareSurfaceForExternalIO(rt.get());
+    this->drawingManager()->prepareSurfaceForExternalIO(fRenderTargetProxy.get());
 }
 
 void GrRenderTargetContext::drawNonAAFilledRect(const GrClip& clip,
@@ -1355,14 +1417,10 @@
                                                 const GrUserStencilSettings* ss,
                                                 GrAAType hwOrNoneAAType) {
     SkASSERT(GrAAType::kCoverage != hwOrNoneAAType);
-    SkASSERT(hwOrNoneAAType == GrAAType::kNone || this->isStencilBufferMultisampled());
-    std::unique_ptr<GrMeshDrawOp> op = GrRectOpFactory::MakeNonAAFill(paint.getColor(), viewMatrix,
-                                                                      rect, localRect, localMatrix);
-    GrPipelineBuilder pipelineBuilder(std::move(paint), hwOrNoneAAType);
-    if (ss) {
-        pipelineBuilder.setUserStencil(ss);
-    }
-    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    SkASSERT(GrAAType::kNone == hwOrNoneAAType || GrFSAAType::kNone != this->fsaaType());
+    std::unique_ptr<GrDrawOp> op = GrNonAAFillRectOp::Make(
+            std::move(paint), viewMatrix, rect, localRect, localMatrix, hwOrNoneAAType, ss);
+    this->addDrawOp(clip, std::move(op));
 }
 
 // Can 'path' be drawn as a pair of filled nested rectangles?
@@ -1431,18 +1489,18 @@
 
     AutoCheckFlush acf(this->drawingManager());
 
-    GrAAType aaType = this->decideAAType(aa);
+    GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage == aaType && !style.pathEffect()) {
         if (style.isSimpleFill() && !path.isConvex()) {
             // Concave AA paths are expensive - try to avoid them for special cases
             SkRect rects[2];
 
             if (fills_as_nested_rects(viewMatrix, path, rects)) {
-                std::unique_ptr<GrMeshDrawOp> op =
+                std::unique_ptr<GrLegacyMeshDrawOp> op =
                         GrRectOpFactory::MakeAAFillNestedRects(paint.getColor(), viewMatrix, rects);
                 if (op) {
                     GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-                    this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+                    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
                 }
                 return;
             }
@@ -1452,11 +1510,10 @@
 
         if (isOval && !path.isInverseFillType()) {
             const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
-            std::unique_ptr<GrMeshDrawOp> op = GrOvalOpFactory::MakeOvalOp(
-                    paint.getColor(), viewMatrix, ovalRect, style.strokeRec(), shaderCaps);
+            std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeOvalOp(
+                    std::move(paint), viewMatrix, ovalRect, style.strokeRec(), shaderCaps);
             if (op) {
-                GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-                this->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+                this->addDrawOp(clip, std::move(op));
                 return;
             }
         }
@@ -1496,13 +1553,12 @@
     // the src color (either the input alpha or in the frag shader) to implement
     // aa. If we have some future driver-mojo path AA that can do the right
     // thing WRT to the blend then we'll need some query on the PR.
-    GrAAType aaType = fRenderTargetContext->decideAAType(aa);
+    GrAAType aaType = fRenderTargetContext->chooseAAType(aa, GrAllowMixedSamples::kNo);
     bool hasUserStencilSettings = !ss->isUnused();
 
     GrShape shape(path, GrStyle::SimpleFill());
     GrPathRenderer::CanDrawPathArgs canDrawArgs;
-    canDrawArgs.fShaderCaps =
-        fRenderTargetContext->drawingManager()->getContext()->caps()->shaderCaps();
+    canDrawArgs.fCaps = fRenderTargetContext->drawingManager()->getContext()->caps();
     canDrawArgs.fViewMatrix = &viewMatrix;
     canDrawArgs.fShape = &shape;
     canDrawArgs.fAAType = aaType;
@@ -1554,16 +1610,14 @@
     RETURN_IF_ABANDONED
     SkASSERT(!path.isEmpty());
     GrShape shape;
-
-    GrAAType aaType = this->decideAAType(aa, /*allowMixedSamples*/ true);
-    if (style.isSimpleHairline() && aaType == GrAAType::kMixedSamples) {
-        // NVPR cannot handle hairlines, so this will would get picked up by a different stencil and
-        // cover path renderer (i.e. default path renderer). The hairline renderer produces much
-        // smoother hairlines than MSAA.
-        aaType = GrAAType::kCoverage;
-    }
+    // NVPR cannot handle hairlines, so this would get picked up by a different stencil and
+    // cover path renderer (i.e. default path renderer). The hairline renderer produces much
+    // smoother hairlines than MSAA.
+    GrAllowMixedSamples allowMixedSamples =
+            style.isSimpleHairline() ? GrAllowMixedSamples::kNo : GrAllowMixedSamples::kYes;
+    GrAAType aaType = this->chooseAAType(aa, allowMixedSamples);
     GrPathRenderer::CanDrawPathArgs canDrawArgs;
-    canDrawArgs.fShaderCaps = this->drawingManager()->getContext()->caps()->shaderCaps();
+    canDrawArgs.fCaps = this->drawingManager()->getContext()->caps();
     canDrawArgs.fViewMatrix = &viewMatrix;
     canDrawArgs.fShape = &shape;
     canDrawArgs.fHasUserStencilSettings = false;
@@ -1673,41 +1727,41 @@
         return SK_InvalidUniqueID;
     }
 
-    // This forces instantiation of the render target.
-    GrRenderTarget* rt = this->accessRenderTarget();
-    if (!rt) {
-        return SK_InvalidUniqueID;
-    }
-
     if (fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil ||
         appliedClip.hasStencilClip()) {
+        // This forces instantiation of the render target.
+        GrRenderTarget* rt = this->accessRenderTarget();
+        if (!rt) {
+            return SK_InvalidUniqueID;
+        }
+
         if (!fContext->resourceProvider()->attachStencilAttachment(rt)) {
             SkDebugf("ERROR creating stencil attachment. Draw skipped.\n");
             return SK_InvalidUniqueID;
         }
     }
 
-    GrXferProcessor::DstTexture dstTexture;
+    GrXferProcessor::DstProxy dstProxy;
     if (op->xpRequiresDstTexture(*this->caps(), &appliedClip)) {
-        this->setupDstTexture(rt, clip, op->bounds(), &dstTexture);
-        if (!dstTexture.texture()) {
+        if (!this->setupDstProxy(this->asRenderTargetProxy(), clip, op->bounds(), &dstProxy)) {
             return SK_InvalidUniqueID;
         }
     }
 
     op->setClippedBounds(bounds);
-    return this->getOpList()->addOp(std::move(op), this, std::move(appliedClip), dstTexture);
+    return this->getOpList()->addOp(std::move(op), *this->caps(),
+                                    std::move(appliedClip), dstProxy);
 }
 
-uint32_t GrRenderTargetContext::addMeshDrawOp(const GrPipelineBuilder& pipelineBuilder,
-                                              const GrClip& clip,
-                                              std::unique_ptr<GrMeshDrawOp> op) {
+uint32_t GrRenderTargetContext::addLegacyMeshDrawOp(GrPipelineBuilder&& pipelineBuilder,
+                                                    const GrClip& clip,
+                                                    std::unique_ptr<GrLegacyMeshDrawOp> op) {
     ASSERT_SINGLE_OWNER
     if (this->drawingManager()->wasAbandoned()) {
         return SK_InvalidUniqueID;
     }
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::addMeshDrawOp");
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::addLegacyMeshDrawOp");
 
     // Setup clip
     SkRect bounds;
@@ -1726,84 +1780,117 @@
     }
 
     GrResourceProvider* resourceProvider = fContext->resourceProvider();
-    if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) {
+    bool usesStencil = pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip();
+    if (usesStencil) {
         if (!resourceProvider->attachStencilAttachment(this->accessRenderTarget())) {
             SkDebugf("ERROR creating stencil attachment. Draw skipped.\n");
             return SK_InvalidUniqueID;
         }
     }
 
-    GrProcessorSet::FragmentProcessorAnalysis analysis;
-    op->analyzeProcessors(&analysis, pipelineBuilder.processors(), &appliedClip, *this->caps());
+    bool isMixedSamples = GrFSAAType::kMixedSamples == this->fsaaType() &&
+                          (pipelineBuilder.isHWAntialias() || usesStencil);
+
+    GrColor overrideColor;
+    GrProcessorSet::Analysis analysis = op->analyzeUpdateAndRecordProcessors(
+            &pipelineBuilder, &appliedClip, isMixedSamples, *this->caps(), &overrideColor);
 
     GrPipeline::InitArgs args;
     pipelineBuilder.getPipelineInitArgs(&args);
     args.fAppliedClip = &appliedClip;
     args.fRenderTarget = rt;
     args.fCaps = this->caps();
-    args.fAnalysis = &analysis;
+    args.fResourceProvider = this->resourceProvider();
 
-    if (pipelineBuilder.willXPNeedDstTexture(*this->caps(), analysis)) {
-        this->setupDstTexture(rt, clip, bounds, &args.fDstTexture);
-        if (!args.fDstTexture.texture()) {
+    if (analysis.requiresDstTexture()) {
+        if (!this->setupDstProxy(this->asRenderTargetProxy(), clip, bounds, &args.fDstProxy)) {
             return SK_InvalidUniqueID;
         }
     }
-    op->initPipeline(args);
-    // TODO: We need to add pipeline dependencies on textures, etc before recording this op.
+    op->initPipeline(args, analysis, overrideColor);
+
+    // Add the pipeline dependencies on textures, etc before recording this op.
+    op->addDependenciesTo(fRenderTargetProxy.get());
+
     op->setClippedBounds(bounds);
-    return this->getOpList()->addOp(std::move(op), this);
+    return this->getOpList()->addOp(std::move(op), *this->caps());
 }
 
-void GrRenderTargetContext::setupDstTexture(GrRenderTarget* rt, const GrClip& clip,
+bool GrRenderTargetContext::setupDstProxy(GrRenderTargetProxy* rtProxy, const GrClip& clip,
                                             const SkRect& opBounds,
-                                            GrXferProcessor::DstTexture* dstTexture) {
+                                            GrXferProcessor::DstProxy* dstProxy) {
     if (this->caps()->textureBarrierSupport()) {
-        if (GrTexture* rtTex = rt->asTexture()) {
+        if (GrTextureProxy* texProxy = rtProxy->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.
-            dstTexture->setTexture(sk_ref_sp(rtTex));
-            dstTexture->setOffset(0, 0);
-            return;
+            dstProxy->setProxy(sk_ref_sp(texProxy));
+            dstProxy->setOffset(0, 0);
+            return true;
         }
     }
 
-    SkIRect copyRect;
-    clip.getConservativeBounds(rt->width(), rt->height(), &copyRect);
+    SkIRect copyRect = SkIRect::MakeWH(rtProxy->width(), rtProxy->height());
 
+    SkIRect clippedRect;
+    clip.getConservativeBounds(rtProxy->width(), rtProxy->height(), &clippedRect);
     SkIRect drawIBounds;
     opBounds.roundOut(&drawIBounds);
     // Cover up for any precision issues by outsetting the op bounds a pixel in each direction.
     drawIBounds.outset(1, 1);
-    if (!copyRect.intersect(drawIBounds)) {
+    if (!clippedRect.intersect(drawIBounds)) {
 #ifdef SK_DEBUG
-        GrCapsDebugf(this->caps(), "Missed an early reject. "
-                                   "Bailing on draw from setupDstTexture.\n");
+        GrCapsDebugf(this->caps(), "setupDstTexture: Missed an early reject bailing on draw.");
 #endif
-        return;
+        return false;
     }
 
     // MSAA consideration: When there is support for reading MSAA samples in the shader we could
     // have per-sample dst values by making the copy multisampled.
     GrSurfaceDesc desc;
-    if (!this->caps()->initDescForDstCopy(rt, &desc)) {
-        desc.fOrigin = kDefault_GrSurfaceOrigin;
+    bool rectsMustMatch = false;
+    bool disallowSubrect = false;
+    if (!this->caps()->initDescForDstCopy(rtProxy, &desc, &rectsMustMatch, &disallowSubrect)) {
+        desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
         desc.fFlags = kRenderTarget_GrSurfaceFlag;
-        desc.fConfig = rt->config();
+        desc.fConfig = rtProxy->config();
     }
 
-    desc.fWidth = copyRect.width();
-    desc.fHeight = copyRect.height();
-
-    static const uint32_t kFlags = 0;
-    sk_sp<GrTexture> copy(fContext->resourceProvider()->createApproxTexture(desc, kFlags));
-
-    if (!copy) {
-        SkDebugf("Failed to create temporary copy of destination texture.\n");
-        return;
+    if (!disallowSubrect) {
+        copyRect = clippedRect;
     }
-    SkIPoint dstPoint = {0, 0};
-    this->getOpList()->copySurface(copy.get(), rt, copyRect, dstPoint);
-    dstTexture->setTexture(std::move(copy));
-    dstTexture->setOffset(copyRect.fLeft, copyRect.fTop);
+
+    SkIPoint dstPoint, dstOffset;
+    SkBackingFit fit;
+    if (rectsMustMatch) {
+        SkASSERT(desc.fOrigin == rtProxy->origin());
+        desc.fWidth = rtProxy->width();
+        desc.fHeight = rtProxy->height();
+        dstPoint = {copyRect.fLeft, copyRect.fTop};
+        dstOffset = {0, 0};
+        fit = SkBackingFit::kExact;
+    } else {
+        desc.fWidth = copyRect.width();
+        desc.fHeight = copyRect.height();
+        dstPoint = {0, 0};
+        dstOffset = {copyRect.fLeft, copyRect.fTop};
+        fit = SkBackingFit::kApprox;
+    }
+
+    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeDeferredSurfaceContext(
+                                                                                desc,
+                                                                                fit,
+                                                                                SkBudgeted::kYes);
+    if (!sContext) {
+        SkDebugf("setupDstTexture: surfaceContext creation failed.\n");
+        return false;
+    }
+
+    if (!sContext->copy(rtProxy, copyRect, dstPoint)) {
+        SkDebugf("setupDstTexture: copy failed.\n");
+        return false;
+    }
+
+    dstProxy->setProxy(sContext->asTextureProxyRef());
+    dstProxy->setOffset(dstOffset);
+    return true;
 }
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index acb7f77..bcc5323 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -8,30 +8,31 @@
 #ifndef GrRenderTargetContext_DEFINED
 #define GrRenderTargetContext_DEFINED
 
+#include "../private/GrInstancedPipelineInfo.h"
+#include "../private/GrRenderTargetProxy.h"
 #include "GrColor.h"
 #include "GrContext.h"
 #include "GrPaint.h"
 #include "GrSurfaceContext.h"
+#include "GrTypesPriv.h"
 #include "GrXferProcessor.h"
 #include "SkRefCnt.h"
 #include "SkSurfaceProps.h"
-#include "../private/GrInstancedPipelineInfo.h"
-#include "../private/GrRenderTargetProxy.h"
 
 class GrClip;
 class GrDrawingManager;
 class GrDrawOp;
 class GrFixedClip;
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class GrPipelineBuilder;
 class GrRenderTarget;
 class GrRenderTargetContextPriv;
 class GrRenderTargetOpList;
 class GrStyle;
-class GrSurface;
 class GrTextureProxy;
 struct GrUserStencilSettings;
 class SkDrawFilter;
+struct SkDrawShadowRec;
 struct SkIPoint;
 struct SkIRect;
 class SkLatticeIter;
@@ -53,6 +54,8 @@
 public:
     ~GrRenderTargetContext() override;
 
+    // MDB TODO: This access is mainly provided for the image filters. Remove it when they
+    // no longer need to pass it to the FragmentProcessor ctors.
     GrResourceProvider* resourceProvider() { return fContext->resourceProvider(); }
 
     // We use SkPaint rather than GrPaint here for two reasons:
@@ -151,21 +154,19 @@
                    const GrStyle& style);
 
     /**
-     * Draw a roundrect using a paint and a shadow shader. This is separate from drawRRect
-     * because it uses different underlying geometry and GeometryProcessor
+     * Use a fast method to render the ambient and spot shadows for a path.
+     * Will return false if not possible for the given path.
      *
      * @param paint        describes how to color pixels.
      * @param viewMatrix   transformation matrix
-     * @param rrect        the roundrect to draw
-     * @param blurRadius   amount of shadow blur to apply (in device space)
-     * @param style        style to apply to the rrect. Currently path effects are not allowed.
+     * @param path         the path to shadow
+     * @param rec          parameters for shadow rendering
      */
-    void drawShadowRRect(const GrClip&,
-                         GrPaint&&,
-                         const SkMatrix& viewMatrix,
-                         const SkRRect& rrect,
-                         SkScalar blurRadius,
-                         const GrStyle& style);
+    bool drawFastShadow(const GrClip&,
+                        GrPaint&&,
+                        const SkMatrix& viewMatrix,
+                        const SkPath& path,
+                        const SkDrawShadowRec& rec);
 
     /**
      * Shortcut for filling a SkPath consisting of nested rrects using a paint. The result is
@@ -200,52 +201,19 @@
                   const SkPath&,
                   const GrStyle& style);
 
-    enum class ColorArrayType {
-        kPremulGrColor,
-        kSkColor,
-    };
     /**
      * Draws vertices with a paint.
      *
-     * @param   paint           describes how to color pixels.
-     * @param   viewMatrix      transformation matrix
-     * @param   primitiveType   primitives type to draw.
-     * @param   vertexCount     number of vertices.
-     * @param   positions       array of vertex positions, required.
-     * @param   texCoords       optional array of texture coordinates used
-     *                          to access the paint.
-     * @param   colors          optional array of per-vertex colors, supercedes
-     *                          the paint's color field.
-     * @param   indices         optional array of indices. If NULL vertices
-     *                          are drawn non-indexed.
-     * @param   indexCount      if indices is non-null then this is the
-     *                          number of indices.
-     * @param   ColorArrayType  Determines how the color array should be interpreted.
+     * @param   paint            describes how to color pixels.
+     * @param   viewMatrix       transformation matrix
+     * @param   vertices         specifies the mesh to draw.
+     * @param   overridePrimType primitive type to draw. If NULL, derive prim type from vertices.
      */
     void drawVertices(const GrClip&,
                       GrPaint&& paint,
                       const SkMatrix& viewMatrix,
-                      GrPrimitiveType primitiveType,
-                      int vertexCount,
-                      const SkPoint positions[],
-                      const SkPoint texs[],
-                      const uint32_t colors[],
-                      const uint16_t indices[],
-                      int indexCount,
-                      ColorArrayType = ColorArrayType::kPremulGrColor);
-
-    /**
-     * Draws vertices with a paint.
-     *
-     * @param   paint           describes how to color pixels.
-     * @param   viewMatrix      transformation matrix
-     * @param   veritces        specifies the mesh to draw.
-     * @param   flags           A bitfield of options specified by SkCanvas::VerticesFlags.
-     */
-    void drawVertices(const GrClip&,
-                      GrPaint&& paint,
-                      const SkMatrix& viewMatrix,
-                      sk_sp<SkVertices> vertices);
+                      sk_sp<SkVertices> vertices,
+                      GrPrimitiveType* overridePrimType = nullptr);
 
     /**
      * Draws textured sprites from an atlas with a paint. This currently does not support AA for the
@@ -340,30 +308,26 @@
      */
     void prepareForExternalIO();
 
-    bool isStencilBufferMultisampled() const {
-        return fRenderTargetProxy->isStencilBufferMultisampled();
-    }
-    bool isUnifiedMultisampled() const { return fRenderTargetProxy->isUnifiedMultisampled(); }
-    bool hasMixedSamples() const { return fRenderTargetProxy->isMixedSampled(); }
-
+    GrFSAAType fsaaType() const { return fRenderTargetProxy->fsaaType(); }
     const GrCaps* caps() const { return fContext->caps(); }
-    const GrSurfaceDesc& desc() const { return fRenderTargetProxy->desc(); }
     int width() const { return fRenderTargetProxy->width(); }
     int height() const { return fRenderTargetProxy->height(); }
     GrPixelConfig config() const { return fRenderTargetProxy->config(); }
     int numColorSamples() const { return fRenderTargetProxy->numColorSamples(); }
+    int numStencilSamples() const { return fRenderTargetProxy->numStencilSamples(); }
     const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; }
     GrColorSpaceXform* getColorXformFromSRGB() const { return fColorXformFromSRGB.get(); }
     GrSurfaceOrigin origin() const { return fRenderTargetProxy->origin(); }
 
     bool wasAbandoned() const;
 
-    GrRenderTarget* instantiate();
-
     GrRenderTarget* accessRenderTarget() {
         // TODO: usage of this entry point needs to be reduced and potentially eliminated
         // since it ends the deferral of the GrRenderTarget's allocation
-        return fRenderTargetProxy->instantiate(fContext->resourceProvider());
+        if (!fRenderTargetProxy->instantiate(fContext->resourceProvider())) {
+            return nullptr;
+        }
+        return fRenderTargetProxy->priv().peekRenderTarget();
     }
 
     GrSurfaceProxy* asSurfaceProxy() override { return fRenderTargetProxy.get(); }
@@ -378,17 +342,6 @@
 
     GrRenderTargetContext* asRenderTargetContext() override { return this; }
 
-    sk_sp<GrTexture> asTexture() {
-        if (!this->accessRenderTarget()) {
-            return nullptr;
-        }
-
-        // TODO: usage of this entry point needs to be reduced and potentially eliminated
-        // since it ends the deferral of the GrRenderTarget's allocation
-        // It's usage should migrate to asTextureProxyRef
-        return sk_ref_sp(this->accessRenderTarget()->asTexture());
-    }
-
     // Provides access to functions that aren't part of the public API.
     GrRenderTargetContextPriv priv();
     const GrRenderTargetContextPriv priv() const;
@@ -403,17 +356,8 @@
     SkDEBUGCODE(void validate() const;)
 
 private:
-    inline GrAAType decideAAType(GrAA aa, bool allowMixedSamples = false) {
-        if (GrAA::kNo == aa) {
-            return GrAAType::kNone;
-        }
-        if (this->isUnifiedMultisampled()) {
-            return GrAAType::kMSAA;
-        }
-        if (allowMixedSamples && this->isStencilBufferMultisampled()) {
-            return GrAAType::kMixedSamples;
-        }
-        return GrAAType::kCoverage;
+    inline GrAAType chooseAAType(GrAA aa, GrAllowMixedSamples allowMixedSamples) {
+        return GrChooseAAType(aa, this->fsaaType(), allowMixedSamples);
     }
 
     friend class GrAtlasTextBlob;               // for access to add[Mesh]DrawOp
@@ -469,23 +413,21 @@
             const GrClip&, GrPaint&&, GrAA, const SkMatrix&, const SkPath&, const GrStyle&);
 
     bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
-    bool onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                      size_t dstRowBytes, int x, int y, uint32_t flags) override;
-    bool onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
-                       size_t srcRowBytes, int x, int y, uint32_t flags) override;
 
     // These perform processing specific to Gr[Mesh]DrawOp-derived ops before recording them into
     // the op list. They return the id of the opList to which the op was added, or 0, if it was
     // dropped (e.g., due to clipping).
     uint32_t addDrawOp(const GrClip&, std::unique_ptr<GrDrawOp>);
-    uint32_t addMeshDrawOp(const GrPipelineBuilder&, const GrClip&, std::unique_ptr<GrMeshDrawOp>);
+    uint32_t addLegacyMeshDrawOp(GrPipelineBuilder&&, const GrClip&,
+                                 std::unique_ptr<GrLegacyMeshDrawOp>);
 
-    // Makes a copy of the dst if it is necessary for the draw and returns the texture that should
-    // be used by GrXferProcessor to access the destination color. If the texture is nullptr then
-    // a texture copy could not be made.
-    void setupDstTexture(GrRenderTarget*, const GrClip&, const SkRect& opBounds,
-                         GrXferProcessor::DstTexture*);
-
+    // 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(GrRenderTargetProxy*,
+                                             const GrClip&,
+                                             const SkRect& opBounds,
+                                             GrXferProcessor::DstProxy* result);
 
     GrRenderTargetOpList* getOpList();
 
@@ -493,7 +435,7 @@
 
     // In MDB-mode the GrOpList can be closed by some other renderTargetContext that has picked
     // it up. For this reason, the GrOpList should only ever be accessed via 'getOpList'.
-    GrRenderTargetOpList*             fOpList;
+    sk_sp<GrRenderTargetOpList>       fOpList;
     GrInstancedPipelineInfo           fInstancedPipelineInfo;
 
     sk_sp<GrColorSpaceXform>          fColorXformFromSRGB;
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index dcde608..2acda23 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -102,9 +102,12 @@
         return fRenderTargetContext->fRenderTargetProxy->uniqueID();
     }
 
-    uint32_t testingOnly_addMeshDrawOp(GrPaint&&, GrAAType, std::unique_ptr<GrMeshDrawOp>,
-                                       const GrUserStencilSettings* = nullptr,
-                                       bool snapToCenters = false);
+    uint32_t testingOnly_addLegacyMeshDrawOp(GrPaint&&, GrAAType,
+                                             std::unique_ptr<GrLegacyMeshDrawOp>,
+                                             const GrUserStencilSettings* = nullptr,
+                                             bool snapToCenters = false);
+
+    uint32_t testingOnly_addDrawOp(std::unique_ptr<GrDrawOp>);
 
     bool refsWrappedObjects() const {
         return fRenderTargetContext->fRenderTargetProxy->refsWrappedObjects();
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index d1aca0c..f7aecdd 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -10,45 +10,33 @@
 #include "GrCaps.h"
 #include "GrGpu.h"
 #include "GrGpuCommandBuffer.h"
+#include "GrRect.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetContext.h"
 #include "GrResourceProvider.h"
-#include "ops/GrClearOp.h"
-#include "ops/GrClearStencilClipOp.h"
-#include "ops/GrCopySurfaceOp.h"
-#include "ops/GrDiscardOp.h"
 #include "instanced/InstancedRendering.h"
+#include "ops/GrClearOp.h"
+#include "ops/GrCopySurfaceOp.h"
 
 using gr_instanced::InstancedRendering;
 
 ////////////////////////////////////////////////////////////////////////////////
 
 // Experimentally we have found that most combining occurs within the first 10 comparisons.
-static const int kDefaultMaxOpLookback = 10;
-static const int kDefaultMaxOpLookahead = 10;
+static const int kMaxOpLookback = 10;
+static const int kMaxOpLookahead = 10;
 
-GrRenderTargetOpList::GrRenderTargetOpList(GrRenderTargetProxy* rtp, GrGpu* gpu,
-                                           GrResourceProvider* resourceProvider,
-                                           GrAuditTrail* auditTrail, const Options& options)
-        : INHERITED(rtp, auditTrail)
-        , fGpu(SkRef(gpu))
-        , fResourceProvider(resourceProvider)
+GrRenderTargetOpList::GrRenderTargetOpList(GrRenderTargetProxy* proxy, GrGpu* gpu,
+                                           GrAuditTrail* auditTrail)
+        : INHERITED(gpu->getContext()->resourceProvider(), proxy, auditTrail)
         , fLastClipStackGenID(SK_InvalidUniqueID)
-        , fClipAllocator(fClipAllocatorStorage, sizeof(fClipAllocatorStorage),
-                         sizeof(fClipAllocatorStorage)) {
-
-    fMaxOpLookback = (options.fMaxOpCombineLookback < 0) ? kDefaultMaxOpLookback
-                                                         : options.fMaxOpCombineLookback;
-    fMaxOpLookahead = (options.fMaxOpCombineLookahead < 0) ? kDefaultMaxOpLookahead
-                                                           : options.fMaxOpCombineLookahead;
-
-    if (GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport()) {
-        fInstancedRendering.reset(fGpu->createInstancedRendering());
+        SkDEBUGCODE(, fNumClips(0)) {
+    if (GrCaps::InstancedSupport::kNone != gpu->caps()->instancedSupport()) {
+        fInstancedRendering.reset(gpu->createInstancedRendering());
     }
 }
 
 GrRenderTargetOpList::~GrRenderTargetOpList() {
-    fGpu->unref();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -72,37 +60,21 @@
         }
     }
 }
-
-void GrRenderTargetOpList::validateTargetsSingleRenderTarget() const {
-    GrRenderTarget* rt = nullptr;
-    for (int i = 0; i < fRecordedOps.count(); ++i) {
-        if (!fRecordedOps[i].fOp) {
-            continue;       // combined forward
-        }
-
-        if (!rt) {
-            rt = fRecordedOps[i].fRenderTarget.get();
-        } else {
-            SkASSERT(fRecordedOps[i].fRenderTarget.get() == rt);
-        }
-    }
-}
 #endif
 
 void GrRenderTargetOpList::prepareOps(GrOpFlushState* flushState) {
-    // MDB TODO: add SkASSERT(this->isClosed());
+    SkASSERT(fTarget.get()->priv().peekRenderTarget());
+    SkASSERT(this->isClosed());
 
     // Loop over the ops that haven't yet been prepared.
     for (int i = 0; i < fRecordedOps.count(); ++i) {
         if (fRecordedOps[i].fOp) {
-            GrOpFlushState::DrawOpArgs opArgs;
-            if (fRecordedOps[i].fRenderTarget) {
-                opArgs = {
-                    fRecordedOps[i].fRenderTarget.get(),
-                    fRecordedOps[i].fAppliedClip,
-                    fRecordedOps[i].fDstTexture
-                };
-            }
+            GrOpFlushState::DrawOpArgs opArgs = {
+                fTarget.get()->priv().peekRenderTarget(),
+                fRecordedOps[i].fAppliedClip,
+                fRecordedOps[i].fDstProxy
+            };
+
             flushState->setDrawOpArgs(&opArgs);
             fRecordedOps[i].fOp->prepare(flushState);
             flushState->setDrawOpArgs(nullptr);
@@ -114,6 +86,28 @@
     }
 }
 
+static std::unique_ptr<GrGpuCommandBuffer> create_command_buffer(GrGpu* gpu) {
+    static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo {
+        GrGpuCommandBuffer::LoadOp::kLoad,
+        GrGpuCommandBuffer::StoreOp::kStore,
+        GrColor_ILLEGAL
+    };
+
+    std::unique_ptr<GrGpuCommandBuffer> buffer(
+                            gpu->createCommandBuffer(kBasicLoadStoreInfo,   // Color
+                                                     kBasicLoadStoreInfo)); // Stencil
+    return buffer;
+}
+
+static inline void finish_command_buffer(GrGpuCommandBuffer* buffer) {
+    if (!buffer) {
+        return;
+    }
+
+    buffer->end();
+    buffer->submit();
+}
+
 // TODO: this is where GrOp::renderTarget is used (which is fine since it
 // is at flush time). However, we need to store the RenderTargetProxy in the
 // Ops and instantiate them here.
@@ -121,111 +115,106 @@
     if (0 == fRecordedOps.count()) {
         return false;
     }
+
+    SkASSERT(fTarget.get()->priv().peekRenderTarget());
+
+    std::unique_ptr<GrGpuCommandBuffer> commandBuffer = create_command_buffer(flushState->gpu());
+    flushState->setCommandBuffer(commandBuffer.get());
+
     // Draw all the generated geometry.
-    SkRandom random;
-    const GrRenderTarget* currentRenderTarget = nullptr;
-    std::unique_ptr<GrGpuCommandBuffer> commandBuffer;
     for (int i = 0; i < fRecordedOps.count(); ++i) {
         if (!fRecordedOps[i].fOp) {
             continue;
         }
-        if (fRecordedOps[i].fRenderTarget.get() != currentRenderTarget) {
-            if (commandBuffer) {
-                commandBuffer->end();
-                commandBuffer->submit();
-                commandBuffer.reset();
-            }
-            currentRenderTarget = fRecordedOps[i].fRenderTarget.get();
-            if (currentRenderTarget) {
-                static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo
-                    { GrGpuCommandBuffer::LoadOp::kLoad,GrGpuCommandBuffer::StoreOp::kStore,
-                      GrColor_ILLEGAL };
-                commandBuffer.reset(fGpu->createCommandBuffer(kBasicLoadStoreInfo,   // Color
-                                                              kBasicLoadStoreInfo)); // Stencil
-            }
+
+        if (fRecordedOps[i].fOp->needsCommandBufferIsolation()) {
+            // This op is a special snowflake and must occur between command buffers
+            // TODO: make this go through the command buffer
+            finish_command_buffer(commandBuffer.get());
+
+            commandBuffer.reset();
+            flushState->setCommandBuffer(commandBuffer.get());
+        } else if (!commandBuffer) {
+            commandBuffer = create_command_buffer(flushState->gpu());
             flushState->setCommandBuffer(commandBuffer.get());
         }
-        GrOpFlushState::DrawOpArgs opArgs;
-        if (fRecordedOps[i].fRenderTarget) {
-            opArgs = {
-                fRecordedOps[i].fRenderTarget.get(),
-                fRecordedOps[i].fAppliedClip,
-                fRecordedOps[i].fDstTexture
-            };
-            flushState->setDrawOpArgs(&opArgs);
-        }
+
+        GrOpFlushState::DrawOpArgs opArgs {
+            fTarget.get()->priv().peekRenderTarget(),
+            fRecordedOps[i].fAppliedClip,
+            fRecordedOps[i].fDstProxy
+        };
+
+        flushState->setDrawOpArgs(&opArgs);
         fRecordedOps[i].fOp->execute(flushState);
         flushState->setDrawOpArgs(nullptr);
     }
-    if (commandBuffer) {
-        commandBuffer->end();
-        commandBuffer->submit();
-        flushState->setCommandBuffer(nullptr);
-    }
 
-    fGpu->finishOpList();
+    finish_command_buffer(commandBuffer.get());
+    flushState->setCommandBuffer(nullptr);
+
     return true;
 }
 
 void GrRenderTargetOpList::reset() {
     fLastFullClearOp = nullptr;
-    fLastFullClearRenderTargetID.makeInvalid();
+    fLastClipStackGenID = SK_InvalidUniqueID;
     fRecordedOps.reset();
     if (fInstancedRendering) {
         fInstancedRendering->endFlush();
+        fInstancedRendering = nullptr;
     }
+
+    INHERITED::reset();
 }
 
 void GrRenderTargetOpList::abandonGpuResources() {
-    if (GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport()) {
-        InstancedRendering* ir = this->instancedRendering();
-        ir->resetGpuResources(InstancedRendering::ResetType::kAbandon);
+    if (fInstancedRendering) {
+        fInstancedRendering->resetGpuResources(InstancedRendering::ResetType::kAbandon);
     }
 }
 
 void GrRenderTargetOpList::freeGpuResources() {
-    if (GrCaps::InstancedSupport::kNone != this->caps()->instancedSupport()) {
-        InstancedRendering* ir = this->instancedRendering();
-        ir->resetGpuResources(InstancedRendering::ResetType::kDestroy);
+    if (fInstancedRendering) {
+        fInstancedRendering->resetGpuResources(InstancedRendering::ResetType::kDestroy);
     }
 }
 
-void GrRenderTargetOpList::fullClear(GrRenderTargetContext* renderTargetContext, GrColor color) {
-    GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
+void GrRenderTargetOpList::fullClear(const GrCaps& caps, GrColor color) {
     // Currently this just inserts or updates the last clear op. However, once in MDB this can
     // remove all the previously recorded ops and change the load op to clear with supplied
     // color.
-    // TODO: this needs to be updated to use GrSurfaceProxy::UniqueID
-    if (fLastFullClearRenderTargetID == renderTarget->uniqueID()) {
+    if (fLastFullClearOp) {
         // As currently implemented, fLastFullClearOp should be the last op because we would
         // have cleared it when another op was recorded.
         SkASSERT(fRecordedOps.back().fOp.get() == fLastFullClearOp);
+        GrOP_INFO("opList: %d Fusing clears (opID: %d Color: 0x%08x -> 0x%08x)\n",
+                  this->uniqueID(),
+                  fLastFullClearOp->uniqueID(),
+                  fLastFullClearOp->color(), color);
         fLastFullClearOp->setColor(color);
         return;
     }
-    std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, renderTarget));
-    if (GrOp* clearOp = this->recordOp(std::move(op), renderTargetContext)) {
+    std::unique_ptr<GrClearOp> op(GrClearOp::Make(GrFixedClip::Disabled(), color, fTarget.get()));
+    if (!op) {
+        return;
+    }
+
+    if (GrOp* clearOp = this->recordOp(std::move(op), caps)) {
         // This is either the clear op we just created or another one that it combined with.
         fLastFullClearOp = static_cast<GrClearOp*>(clearOp);
-        fLastFullClearRenderTargetID = renderTarget->uniqueID();
-    }
-}
-
-void GrRenderTargetOpList::discard(GrRenderTargetContext* renderTargetContext) {
-    // Currently this just inserts a discard op. However, once in MDB this can remove all the
-    // previously recorded ops and change the load op to discard.
-    if (this->caps()->discardRenderTargetSupport()) {
-        this->recordOp(GrDiscardOp::Make(renderTargetContext->accessRenderTarget()),
-                       renderTargetContext);
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrRenderTargetOpList::copySurface(GrSurface* dst,
-                                       GrSurface* src,
+// MDB TODO: fuse with GrTextureOpList::copySurface
+bool GrRenderTargetOpList::copySurface(const GrCaps& caps,
+                                       GrSurfaceProxy* dst,
+                                       GrSurfaceProxy* src,
                                        const SkIRect& srcRect,
                                        const SkIPoint& dstPoint) {
+    SkASSERT(dst->asRenderTargetProxy() == fTarget.get());
     std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(dst, src, srcRect, dstPoint);
     if (!op) {
         return false;
@@ -234,21 +223,16 @@
     this->addDependency(src);
 #endif
 
-    // Copy surface doesn't work through a GrGpuCommandBuffer. By passing nullptr for the context we
-    // force this to occur between command buffers and execute directly on GrGpu. This workaround
-    // goes away with MDB.
-    this->recordOp(std::move(op), nullptr);
+    this->recordOp(std::move(op), caps);
     return true;
 }
 
-static inline bool can_reorder(const SkRect& a, const SkRect& b) {
-    return a.fRight <= b.fLeft || a.fBottom <= b.fTop ||
-           b.fRight <= a.fLeft || b.fBottom <= a.fTop;
-}
+static inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); }
 
 bool GrRenderTargetOpList::combineIfPossible(const RecordedOp& a, GrOp* b,
                                              const GrAppliedClip* bClip,
-                                             const DstTexture* bDstTexture) {
+                                             const DstProxy* bDstProxy,
+                                             const GrCaps& caps) {
     if (a.fAppliedClip) {
         if (!bClip) {
             return false;
@@ -259,23 +243,21 @@
     } else if (bClip) {
         return false;
     }
-    if (bDstTexture) {
-        if (a.fDstTexture != *bDstTexture) {
+    if (bDstProxy) {
+        if (a.fDstProxy != *bDstProxy) {
             return false;
         }
-    } else if (a.fDstTexture.texture()) {
+    } else if (a.fDstProxy.proxy()) {
         return false;
     }
-    return a.fOp->combineIfPossible(b, *this->caps());
+    return a.fOp->combineIfPossible(b, caps);
 }
 
 GrOp* GrRenderTargetOpList::recordOp(std::unique_ptr<GrOp> op,
-                                     GrRenderTargetContext* renderTargetContext,
+                                     const GrCaps& caps,
                                      GrAppliedClip* clip,
-                                     const DstTexture* dstTexture) {
-    GrRenderTarget* renderTarget =
-            renderTargetContext ? renderTargetContext->accessRenderTarget()
-                                : nullptr;
+                                     const DstProxy* dstProxy) {
+    SkASSERT(fTarget.get());
 
     // A closed GrOpList should never receive new/more ops
     SkASSERT(!this->isClosed());
@@ -284,113 +266,93 @@
     // 1) check every op
     // 2) intersect with something
     // 3) find a 'blocker'
-    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), renderTarget->uniqueID());
-    GrOP_INFO("Recording (%s, B%u)\n"
-              "\tBounds LRTB (%f, %f, %f, %f)\n",
+    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget.get()->uniqueID());
+    GrOP_INFO("opList: %d Recording (%s, opID: %u)\n"
+              "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n",
+               this->uniqueID(),
                op->name(),
                op->uniqueID(),
-               op->bounds().fLeft, op->bounds().fRight,
-               op->bounds().fTop, op->bounds().fBottom);
+               op->bounds().fLeft, op->bounds().fTop,
+               op->bounds().fRight, op->bounds().fBottom);
     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
-    GrOP_INFO("\tClipped Bounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", op->bounds().fLeft,
-              op->bounds().fTop, op->bounds().fRight, op->bounds().fBottom);
     GrOP_INFO("\tOutcome:\n");
-    int maxCandidates = SkTMin(fMaxOpLookback, fRecordedOps.count());
+    int maxCandidates = SkTMin(kMaxOpLookback, fRecordedOps.count());
     // If we don't have a valid destination render target then we cannot reorder.
-    if (maxCandidates && renderTarget) {
+    if (maxCandidates) {
         int i = 0;
         while (true) {
             const RecordedOp& candidate = fRecordedOps.fromBack(i);
-            // We cannot continue to search backwards if the render target changes
-            if (candidate.fRenderTarget.get() != renderTarget) {
-                GrOP_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n", candidate.fOp->name(),
+
+            if (this->combineIfPossible(candidate, op.get(), clip, dstProxy, caps)) {
+                GrOP_INFO("\t\tBackward: Combining with (%s, opID: %u)\n", candidate.fOp->name(),
                           candidate.fOp->uniqueID());
-                break;
-            }
-            if (this->combineIfPossible(candidate, op.get(), clip, dstTexture)) {
-                GrOP_INFO("\t\tCombining with (%s, B%u)\n", candidate.fOp->name(),
-                          candidate.fOp->uniqueID());
-                GrOP_INFO("\t\t\tCombined op info:\n");
+                GrOP_INFO("\t\t\tBackward: Combined op info:\n");
                 GrOP_INFO(SkTabString(candidate.fOp->dumpInfo(), 4).c_str());
                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, candidate.fOp.get(), op.get());
                 return candidate.fOp.get();
             }
             // Stop going backwards if we would cause a painter's order violation.
             if (!can_reorder(fRecordedOps.fromBack(i).fOp->bounds(), op->bounds())) {
-                GrOP_INFO("\t\tIntersects with (%s, B%u)\n", candidate.fOp->name(),
+                GrOP_INFO("\t\tBackward: Intersects with (%s, opID: %u)\n", candidate.fOp->name(),
                           candidate.fOp->uniqueID());
                 break;
             }
             ++i;
             if (i == maxCandidates) {
-                GrOP_INFO("\t\tReached max lookback or beginning of op array %d\n", i);
+                GrOP_INFO("\t\tBackward: Reached max lookback or beginning of op array %d\n", i);
                 break;
             }
         }
     } else {
-        GrOP_INFO("\t\tFirstOp\n");
+        GrOP_INFO("\t\tBackward: FirstOp\n");
     }
     GR_AUDIT_TRAIL_OP_RESULT_NEW(fAuditTrail, op);
     if (clip) {
         clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip));
+        SkDEBUGCODE(fNumClips++;)
     }
-    fRecordedOps.emplace_back(std::move(op), renderTarget, clip, dstTexture);
-    fRecordedOps.back().fOp->wasRecorded();
+    fRecordedOps.emplace_back(std::move(op), clip, dstProxy);
+    fRecordedOps.back().fOp->wasRecorded(this);
     fLastFullClearOp = nullptr;
-    fLastFullClearRenderTargetID.makeInvalid();
     return fRecordedOps.back().fOp.get();
 }
 
-void GrRenderTargetOpList::forwardCombine() {
-    if (fMaxOpLookahead <= 0) {
-        return;
-    }
+void GrRenderTargetOpList::forwardCombine(const GrCaps& caps) {
+    SkASSERT(!this->isClosed());
+
+    GrOP_INFO("opList: %d ForwardCombine %d ops:\n", this->uniqueID(), fRecordedOps.count());
+
     for (int i = 0; i < fRecordedOps.count() - 1; ++i) {
         GrOp* op = fRecordedOps[i].fOp.get();
-        GrRenderTarget* renderTarget = fRecordedOps[i].fRenderTarget.get();
-        // If we don't have a valid destination render target ID then we cannot reorder.
-        if (!renderTarget) {
-            continue;
-        }
-        int maxCandidateIdx = SkTMin(i + fMaxOpLookahead, fRecordedOps.count() - 1);
+
+        int maxCandidateIdx = SkTMin(i + kMaxOpLookahead, fRecordedOps.count() - 1);
         int j = i + 1;
         while (true) {
             const RecordedOp& candidate = fRecordedOps[j];
-            // We cannot continue to search if the render target changes
-            if (candidate.fRenderTarget.get() != renderTarget) {
-                GrOP_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n", candidate.fOp->name(),
-                          candidate.fOp->uniqueID());
-                break;
-            }
+
             if (this->combineIfPossible(fRecordedOps[i], candidate.fOp.get(),
-                                        candidate.fAppliedClip, &candidate.fDstTexture)) {
-                GrOP_INFO("\t\tCombining with (%s, B%u)\n", candidate.fOp->name(),
-                          candidate.fOp->uniqueID());
+                                        candidate.fAppliedClip, &candidate.fDstProxy, caps)) {
+                GrOP_INFO("\t\t%d: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
+                          i, op->name(), op->uniqueID(),
+                          candidate.fOp->name(), candidate.fOp->uniqueID());
                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, op, candidate.fOp.get());
                 fRecordedOps[j].fOp = std::move(fRecordedOps[i].fOp);
                 break;
             }
-            // Stop going traversing if we would cause a painter's order violation.
+            // Stop traversing if we would cause a painter's order violation.
             if (!can_reorder(fRecordedOps[j].fOp->bounds(), op->bounds())) {
-                GrOP_INFO("\t\tIntersects with (%s, B%u)\n", candidate.fOp->name(),
-                          candidate.fOp->uniqueID());
+                GrOP_INFO("\t\t%d: (%s opID: %u) -> Intersects with (%s, opID: %u)\n",
+                          i, op->name(), op->uniqueID(),
+                          candidate.fOp->name(), candidate.fOp->uniqueID());
                 break;
             }
             ++j;
             if (j > maxCandidateIdx) {
-                GrOP_INFO("\t\tReached max lookahead or end of op array %d\n", i);
+                GrOP_INFO("\t\t%d: (%s opID: %u) -> Reached max lookahead or end of array\n",
+                          i, op->name(), op->uniqueID());
                 break;
             }
         }
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-void GrRenderTargetOpList::clearStencilClip(const GrFixedClip& clip,
-                                            bool insideStencilMask,
-                                            GrRenderTargetContext* renderTargetContext) {
-    this->recordOp(GrClearStencilClipOp::Make(clip, insideStencilMask,
-                                              renderTargetContext->accessRenderTarget()),
-                   renderTargetContext);
-}
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index c44c341..6945f47 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -30,27 +30,26 @@
 
 class GrRenderTargetOpList final : public GrOpList {
 private:
-    using DstTexture = GrXferProcessor::DstTexture;
+    using DstProxy = GrXferProcessor::DstProxy;
 
 public:
-    /** Options for GrRenderTargetOpList behavior. */
-    struct Options {
-        int fMaxOpCombineLookback = -1;
-        int fMaxOpCombineLookahead = -1;
-    };
-
-    GrRenderTargetOpList(GrRenderTargetProxy*, GrGpu*, GrResourceProvider*,
-                         GrAuditTrail*, const Options&);
+    GrRenderTargetOpList(GrRenderTargetProxy*, GrGpu*, GrAuditTrail*);
 
     ~GrRenderTargetOpList() override;
 
-    void makeClosed() override {
-        INHERITED::makeClosed();
+    void makeClosed(const GrCaps& caps) override {
+        if (this->isClosed()) {
+            return;
+        }
 
         fLastFullClearOp = nullptr;
-        this->forwardCombine();
+        this->forwardCombine(caps);
+
+        INHERITED::makeClosed(caps);
     }
 
+    bool isEmpty() const { return fRecordedOps.empty(); }
+
     /**
      * Empties the draw buffer of any queued up draws.
      */
@@ -66,27 +65,18 @@
     void prepareOps(GrOpFlushState* flushState) override;
     bool executeOps(GrOpFlushState* flushState) override;
 
-    /**
-     * Gets the capabilities of the draw target.
-     */
-    const GrCaps* caps() const { return fGpu->caps(); }
-
-    uint32_t addOp(std::unique_ptr<GrOp> op, GrRenderTargetContext* renderTargetContext) {
-        this->recordOp(std::move(op), renderTargetContext, nullptr, nullptr);
+    uint32_t addOp(std::unique_ptr<GrOp> op, const GrCaps& caps) {
+        this->recordOp(std::move(op), caps, nullptr, nullptr);
         return this->uniqueID();
     }
-    uint32_t addOp(std::unique_ptr<GrOp> op, GrRenderTargetContext* renderTargetContext,
-                   GrAppliedClip&& clip, const DstTexture& dstTexture) {
-        this->recordOp(std::move(op), renderTargetContext, clip.doesClip() ? &clip : nullptr,
-                       &dstTexture);
+    uint32_t addOp(std::unique_ptr<GrOp> op, const GrCaps& caps,
+                   GrAppliedClip&& clip, const DstProxy& dstProxy) {
+        this->recordOp(std::move(op), caps, clip.doesClip() ? &clip : nullptr, &dstProxy);
         return this->uniqueID();
     }
 
     /** Clears the entire render target */
-    void fullClear(GrRenderTargetContext*, GrColor color);
-
-    /** Discards the contents render target. */
-    void discard(GrRenderTargetContext*);
+    void fullClear(const GrCaps& caps, GrColor color);
 
     /**
      * Copies a pixel rectangle from one surface to another. This call may finalize
@@ -98,8 +88,9 @@
      * depending on the type of surface, configs, etc, and the backend-specific
      * limitations.
      */
-    bool copySurface(GrSurface* dst,
-                     GrSurface* src,
+    bool copySurface(const GrCaps& caps,
+                     GrSurfaceProxy* dst,
+                     GrSurfaceProxy* src,
                      const SkIRect& srcRect,
                      const SkIPoint& dstPoint);
 
@@ -112,58 +103,52 @@
 
     SkDEBUGCODE(void dump() const override;)
 
-    SkDEBUGCODE(void validateTargetsSingleRenderTarget() const;)
+    SkDEBUGCODE(int numOps() const override { return fRecordedOps.count(); })
+    SkDEBUGCODE(int numClips() const override { return fNumClips; })
 
 private:
-    friend class GrRenderTargetContextPriv; // for clearStencilClip and stencil clip state.
+    friend class GrRenderTargetContextPriv; // for stencil clip state. TODO: this is invasive
 
     struct RecordedOp {
-        RecordedOp(std::unique_ptr<GrOp> op, GrRenderTarget* rt, const GrAppliedClip* appliedClip,
-                   const DstTexture* dstTexture)
-                : fOp(std::move(op)), fRenderTarget(rt), fAppliedClip(appliedClip) {
-            if (dstTexture) {
-                fDstTexture = *dstTexture;
+        RecordedOp(std::unique_ptr<GrOp> op,
+                   const GrAppliedClip* appliedClip,
+                   const DstProxy* dstProxy)
+                : fOp(std::move(op))
+                , fAppliedClip(appliedClip) {
+            if (dstProxy) {
+                fDstProxy = *dstProxy;
             }
         }
         std::unique_ptr<GrOp> fOp;
-        // TODO: These ops will all to target the same render target and this won't be needed.
-        GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
-        DstTexture fDstTexture;
-        const GrAppliedClip* fAppliedClip;
+        DstProxy              fDstProxy;
+        const GrAppliedClip*  fAppliedClip;
     };
 
     // If the input op is combined with an earlier op, this returns the combined op. Otherwise, it
     // returns the input op.
-    GrOp* recordOp(std::unique_ptr<GrOp>, GrRenderTargetContext*, GrAppliedClip* = nullptr,
-                   const DstTexture* = nullptr);
+    GrOp* recordOp(std::unique_ptr<GrOp>, const GrCaps& caps,
+                   GrAppliedClip* = nullptr, const DstProxy* = nullptr);
 
-    void forwardCombine();
-
-    // Used only via GrRenderTargetContextPriv.
-    void clearStencilClip(const GrFixedClip&, bool insideStencilMask, GrRenderTargetContext*);
+    void forwardCombine(const GrCaps&);
 
     // If this returns true then b has been merged into a's op.
     bool combineIfPossible(const RecordedOp& a, GrOp* b, const GrAppliedClip* bClip,
-                           const DstTexture* bDstTexture);
+                           const DstProxy* bDstTexture, const GrCaps&);
 
-    GrClearOp* fLastFullClearOp = nullptr;
-    GrGpuResource::UniqueID fLastFullClearRenderTargetID = GrGpuResource::UniqueID::InvalidID();
-
-    GrGpu* fGpu;
-    GrResourceProvider* fResourceProvider;
-
-    int fMaxOpLookback;
-    int fMaxOpLookahead;
+    GrClearOp*                     fLastFullClearOp = nullptr;
 
     std::unique_ptr<gr_instanced::InstancedRendering> fInstancedRendering;
 
-    int32_t fLastClipStackGenID;
-    SkIRect fLastDevClipBounds;
+    int32_t                        fLastClipStackGenID;
+    SkIRect                        fLastDevClipBounds;
 
-    SkSTArray<256, RecordedOp, true> fRecordedOps;
+    // For ops/opList we have mean: 5 stdDev: 28
+    SkSTArray<5, RecordedOp, true> fRecordedOps;
 
-    char fClipAllocatorStorage[4096];
-    SkArenaAlloc fClipAllocator;
+    // MDB TODO: 4096 for the first allocation of the clip space will be huge overkill.
+    // Gather statistics to determine the correct size.
+    SkArenaAlloc                   fClipAllocator{4096};
+    SkDEBUGCODE(int                fNumClips;)
 
     typedef GrOpList INHERITED;
 };
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index 44f23d4..b582455 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -13,17 +13,19 @@
 #include "GrRenderTargetPriv.h"
 #include "GrResourceProvider.h"
 #include "GrTextureRenderTargetProxy.h"
+#include "SkMathPriv.h"
 
 // Deferred version
 // TODO: we can probably munge the 'desc' in both the wrapped and deferred
 // cases to make the sampleConfig/numSamples stuff more rational.
 GrRenderTargetProxy::GrRenderTargetProxy(const GrCaps& caps, const GrSurfaceDesc& desc,
                                          SkBackingFit fit, SkBudgeted budgeted, uint32_t flags)
-    : INHERITED(desc, fit, budgeted, flags)
-    , fRenderTargetFlags(GrRenderTarget::Flags::kNone) {
+        : INHERITED(desc, fit, budgeted, flags)
+        , fSampleCnt(desc.fSampleCnt)
+        , fRenderTargetFlags(GrRenderTarget::Flags::kNone) {
     // Since we know the newly created render target will be internal, we are able to precompute
     // what the flags will ultimately end up being.
-    if (caps.usesMixedSamples() && fDesc.fSampleCnt > 0) {
+    if (caps.usesMixedSamples() && fSampleCnt > 0) {
         fRenderTargetFlags |= GrRenderTarget::Flags::kMixedSampled;
     }
     if (caps.maxWindowRectangles() > 0) {
@@ -33,9 +35,9 @@
 
 // Wrapped version
 GrRenderTargetProxy::GrRenderTargetProxy(sk_sp<GrSurface> surf)
-    : INHERITED(std::move(surf), SkBackingFit::kExact)
-    , fRenderTargetFlags(fTarget->asRenderTarget()->renderTargetPriv().flags()) {
-}
+        : INHERITED(std::move(surf), SkBackingFit::kExact)
+        , fSampleCnt(fTarget->asRenderTarget()->numStencilSamples())
+        , fRenderTargetFlags(fTarget->asRenderTarget()->renderTargetPriv().flags()) {}
 
 int GrRenderTargetProxy::maxWindowRectangles(const GrCaps& caps) const {
     return (fRenderTargetFlags & GrRenderTarget::Flags::kWindowRectsSupport)
@@ -43,27 +45,48 @@
                    : 0;
 }
 
-GrRenderTarget* GrRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) {
-    SkASSERT(fDesc.fFlags & GrSurfaceFlags::kRenderTarget_GrSurfaceFlag);
+bool GrRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) {
+    static constexpr GrSurfaceFlags kFlags = kRenderTarget_GrSurfaceFlag;
 
-    GrSurface* surf = INHERITED::instantiate(resourceProvider);
-    if (!surf || !surf->asRenderTarget()) {
-        return nullptr;
+    if (!this->instantiateImpl(resourceProvider, fSampleCnt, kFlags,
+                               /* isMipped = */ false,
+                               SkDestinationSurfaceColorMode::kLegacy)) {
+        return false;
     }
-
+    SkASSERT(fTarget->asRenderTarget());
     // Check that our a priori computation matched the ultimate reality
-    SkASSERT(fRenderTargetFlags == surf->asRenderTarget()->renderTargetPriv().flags());
+    SkASSERT(fRenderTargetFlags == fTarget->asRenderTarget()->renderTargetPriv().flags());
 
-    return surf->asRenderTarget();
+    return true;
 }
 
-size_t GrRenderTargetProxy::onGpuMemorySize() const {
+int GrRenderTargetProxy::worstCaseWidth() const {
     if (fTarget) {
-        return fTarget->gpuMemorySize();
+        return fTarget->width();
     }
 
+    if (SkBackingFit::kExact == fFit) {
+        return fWidth;
+    }
+    return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fWidth));
+}
+
+int GrRenderTargetProxy::worstCaseHeight() const {
+    if (fTarget) {
+        return fTarget->height();
+    }
+
+    if (SkBackingFit::kExact == fFit) {
+        return fHeight;
+    }
+    return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fHeight));
+}
+
+size_t GrRenderTargetProxy::onUninstantiatedGpuMemorySize() const {
+    int colorSamplesPerPixel = this->numColorSamples() + 1;
     // TODO: do we have enough information to improve this worst case estimate?
-    return GrSurface::ComputeSize(fDesc, fDesc.fSampleCnt+1, false, SkBackingFit::kApprox == fFit);
+    return GrSurface::ComputeSize(fConfig, fWidth, fHeight, colorSamplesPerPixel, false,
+                                  SkBackingFit::kApprox == fFit);
 }
 
 bool GrRenderTargetProxy::refsWrappedObjects() const {
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 596af6d..7c371a8 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -18,6 +18,8 @@
 
 DECLARE_SKMESSAGEBUS_MESSAGE(GrUniqueKeyInvalidatedMessage);
 
+DECLARE_SKMESSAGEBUS_MESSAGE(GrGpuResourceFreedMessage);
+
 //////////////////////////////////////////////////////////////////////////////
 
 GrScratchKey::ResourceType GrScratchKey::GenerateResourceType() {
@@ -59,7 +61,7 @@
  //////////////////////////////////////////////////////////////////////////////
 
 
-GrResourceCache::GrResourceCache(const GrCaps* caps)
+GrResourceCache::GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID)
     : fTimestamp(0)
     , fMaxCount(kDefaultMaxCount)
     , fMaxBytes(kDefaultMaxSize)
@@ -73,8 +75,10 @@
     , fBytes(0)
     , fBudgetedCount(0)
     , fBudgetedBytes(0)
+    , fPurgeableBytes(0)
     , fRequestFlush(false)
     , fExternalFlushCnt(0)
+    , fContextUniqueID(contextUniqueID)
     , fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
     SkDEBUGCODE(fCount = 0;)
     SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
@@ -133,13 +137,14 @@
     this->validate();
     SkASSERT(this->isInCache(resource));
 
+    size_t size = resource->gpuMemorySize();
     if (resource->isPurgeable()) {
         fPurgeableQueue.remove(resource);
+        fPurgeableBytes -= size;
     } else {
         this->removeFromNonpurgeableArray(resource);
     }
 
-    size_t size = resource->gpuMemorySize();
     SkDEBUGCODE(--fCount;)
     fBytes -= size;
     if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
@@ -181,11 +186,14 @@
     SkASSERT(!fBytes);
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
+    SkASSERT(!fPurgeableBytes);
 }
 
 void GrResourceCache::releaseAll() {
     AutoValidate av(this);
 
+    this->processFreedGpuResources();
+
     while(fNonpurgeableResources.count()) {
         GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
         SkASSERT(!back->wasDestroyed());
@@ -205,6 +213,7 @@
     SkASSERT(!fBytes);
     SkASSERT(!fBudgetedCount);
     SkASSERT(!fBudgetedBytes);
+    SkASSERT(!fPurgeableBytes);
 }
 
 class GrResourceCache::AvailableForScratchUse {
@@ -325,6 +334,7 @@
 
     if (resource->isPurgeable()) {
         // It's about to become unpurgeable.
+        fPurgeableBytes -= resource->gpuMemorySize();
         fPurgeableQueue.remove(resource);
         this->addToNonpurgeableArray(resource);
     }
@@ -366,6 +376,7 @@
     fPurgeableQueue.insert(resource);
     resource->cacheAccess().setFlushCntWhenResourceBecamePurgeable(fExternalFlushCnt);
     resource->cacheAccess().setTimeWhenResourceBecomePurgeable();
+    fPurgeableBytes += resource->gpuMemorySize();
 
     if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
         // Check whether this resource could still be used as a scratch resource.
@@ -450,6 +461,8 @@
         this->processInvalidUniqueKeys(invalidKeyMsgs);
     }
 
+    this->processFreedGpuResources();
+
     if (fMaxUnusedFlushes > 0) {
         // We want to know how many complete flushes have occurred without the resource being used.
         // If the resource was tagged when fExternalFlushCnt was N then this means it became
@@ -523,6 +536,47 @@
     }
 }
 
+void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) {
+
+    const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge);
+    bool stillOverbudget = tmpByteBudget < fBytes;
+
+    if (preferScratchResources && bytesToPurge < fPurgeableBytes) {
+        // Sort the queue
+        fPurgeableQueue.sort();
+
+        // Make a list of the scratch resources to delete
+        SkTDArray<GrGpuResource*> scratchResources;
+        size_t scratchByteCount = 0;
+        for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) {
+            GrGpuResource* resource = fPurgeableQueue.at(i);
+            SkASSERT(resource->isPurgeable());
+            if (!resource->getUniqueKey().isValid()) {
+                *scratchResources.append() = resource;
+                scratchByteCount += resource->gpuMemorySize();
+                stillOverbudget = tmpByteBudget < fBytes - scratchByteCount;
+            }
+        }
+
+        // Delete the scratch resources. This must be done as a separate pass
+        // to avoid messing up the sorted order of the queue
+        for (int i = 0; i < scratchResources.count(); i++) {
+            scratchResources.getAt(i)->cacheAccess().release();
+        }
+        stillOverbudget = tmpByteBudget < fBytes;
+
+        this->validate();
+    }
+
+    // Purge any remaining resources in LRU order
+    if (stillOverbudget) {
+        const size_t cachedByteCount = fMaxBytes;
+        fMaxBytes = tmpByteBudget;
+        this->purgeAsNeeded();
+        fMaxBytes = cachedByteCount;
+    }
+}
+
 void GrResourceCache::processInvalidUniqueKeys(
     const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) {
     for (int i = 0; i < msgs.count(); ++i) {
@@ -534,6 +588,20 @@
     }
 }
 
+void GrResourceCache::insertCrossContextGpuResource(GrGpuResource* resource) {
+    resource->ref();
+}
+
+void GrResourceCache::processFreedGpuResources() {
+    SkTArray<GrGpuResourceFreedMessage> msgs;
+    fFreedGpuResourceInbox.poll(&msgs);
+    for (int i = 0; i < msgs.count(); ++i) {
+        if (msgs[i].fOwningUniqueID == fContextUniqueID) {
+            msgs[i].fResource->unref();
+        }
+    }
+}
+
 void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
     int index = fNonpurgeableResources.count();
     *fNonpurgeableResources.append() = resource;
@@ -616,8 +684,6 @@
 
 void GrResourceCache::notifyFlushOccurred(FlushType type) {
     switch (type) {
-        case FlushType::kImmediateMode:
-            break;
         case FlushType::kCacheRequested:
             SkASSERT(fRequestFlush);
             fRequestFlush = false;
@@ -697,8 +763,8 @@
             if (uniqueKey.isValid()) {
                 ++fContent;
                 SkASSERT(fUniqueHash->find(uniqueKey) == resource);
-                SkASSERT(!resource->resourcePriv().refsWrappedObjects());
-                SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
+                SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
+                         resource->resourcePriv().refsWrappedObjects());
 
                 if (scratchKey.isValid()) {
                     SkASSERT(!fScratchMap->has(resource, scratchKey));
@@ -726,6 +792,7 @@
     }
 
     Stats stats(this);
+    size_t purgeableBytes = 0;
 
     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
         SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
@@ -739,6 +806,7 @@
         SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
         SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
         stats.update(fPurgeableQueue.at(i));
+        purgeableBytes += fPurgeableQueue.at(i)->gpuMemorySize();
     }
 
     SkASSERT(fCount == this->getResourceCount());
@@ -747,6 +815,7 @@
     SkASSERT(stats.fBytes == fBytes);
     SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
     SkASSERT(stats.fBudgetedCount == fBudgetedCount);
+    SkASSERT(purgeableBytes == fPurgeableBytes);
 #if GR_CACHE_STATS
     SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
     SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index d871c9a..5cdc563 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -24,6 +24,11 @@
 class SkString;
 class SkTraceMemoryDump;
 
+struct GrGpuResourceFreedMessage {
+    GrGpuResource* fResource;
+    uint32_t fOwningUniqueID;
+};
+
 /**
  * Manages the lifetime of all GrGpuResource instances.
  *
@@ -43,7 +48,7 @@
  */
 class GrResourceCache {
 public:
-    GrResourceCache(const GrCaps* caps);
+    GrResourceCache(const GrCaps* caps, uint32_t contextUniqueID);
     ~GrResourceCache();
 
     // Default maximum number of budgeted resources in the cache.
@@ -88,6 +93,11 @@
     size_t getResourceBytes() const { return fBytes; }
 
     /**
+     * Returns the number of bytes held by unlocked reosources which are available for purging.
+     */
+    size_t getPurgeableBytes() const { return fPurgeableBytes; }
+
+    /**
      * Returns the number of bytes consumed by budgeted resources.
      */
     size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
@@ -163,17 +173,31 @@
     /** Purge all resources not used since the passed in time. */
     void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
 
+    /**
+     * Purge unlocked resources from the cache until the the provided byte count has been reached
+     * or we have purged all unlocked resources. The default policy is to purge in LRU order, but
+     * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other
+     * resource types.
+     *
+     * @param maxBytesToPurge the desired number of bytes to be purged.
+     * @param preferScratchResources If true scratch resources will be purged prior to other
+     *                               resource types.
+     */
+    void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources);
+
     /** Returns true if the cache would like a flush to occur in order to make more resources
         purgeable. */
     bool requestsFlush() const { return fRequestFlush; }
 
     enum FlushType {
         kExternal,
-        kImmediateMode,
         kCacheRequested,
     };
     void notifyFlushOccurred(FlushType);
 
+    /** Maintain a ref to this resource until we receive a GrGpuResourceFreedMessage. */
+    void insertCrossContextGpuResource(GrGpuResource* resource);
+
 #if GR_CACHE_STATS
     struct Stats {
         int fTotal;
@@ -241,6 +265,7 @@
     /// @}
 
     void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
+    void processFreedGpuResources();
     void addToNonpurgeableArray(GrGpuResource*);
     void removeFromNonpurgeableArray(GrGpuResource*);
     bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
@@ -287,6 +312,7 @@
     }
 
     typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
+    typedef SkMessageBus<GrGpuResourceFreedMessage>::Inbox FreedGpuResourceInbox;
     typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
     typedef SkTDArray<GrGpuResource*> ResourceArray;
 
@@ -321,11 +347,15 @@
     // our current stats for resources that count against the budget
     int                                 fBudgetedCount;
     size_t                              fBudgetedBytes;
+    size_t                              fPurgeableBytes;
 
     bool                                fRequestFlush;
     uint32_t                            fExternalFlushCnt;
 
     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
+    FreedGpuResourceInbox               fFreedGpuResourceInbox;
+
+    uint32_t                            fContextUniqueID;
 
     // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
     // we're in the midst of converting it to purgeable status.
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 407fffb..ea699fb 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -10,7 +10,9 @@
 #include "GrBuffer.h"
 #include "GrCaps.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGpu.h"
+#include "GrPath.h"
 #include "GrPathRendering.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
@@ -21,11 +23,12 @@
 #include "GrSurfaceProxyPriv.h"
 #include "GrTexturePriv.h"
 #include "../private/GrSingleOwner.h"
+#include "SkGr.h"
 #include "SkMathPriv.h"
 
 GR_DECLARE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
 
-const int GrResourceProvider::kMinScratchTextureSize = 16;
+const uint32_t GrResourceProvider::kMinScratchTextureSize = 16;
 
 #define ASSERT_SINGLE_OWNER \
     SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
@@ -43,106 +46,190 @@
     fQuadIndexBufferKey = gQuadIndexBufferKey;
 }
 
-bool GrResourceProvider::IsFunctionallyExact(GrTextureProxy* proxy) {
+bool GrResourceProvider::IsFunctionallyExact(GrSurfaceProxy* proxy) {
     return proxy->priv().isExact() || (SkIsPow2(proxy->width()) && SkIsPow2(proxy->height()));
 }
 
-GrTexture* GrResourceProvider::createMipMappedTexture(const GrSurfaceDesc& desc,
-                                                      SkBudgeted budgeted, const GrMipLevel* texels,
-                                                      int mipLevelCount, uint32_t flags,
+bool validate_desc(const GrSurfaceDesc& desc, const GrCaps& caps, int levelCount = 0) {
+    if (desc.fWidth <= 0 || desc.fHeight <= 0) {
+        return false;
+    }
+    if (!caps.isConfigTexturable(desc.fConfig)) {
+        return false;
+    }
+    if (desc.fFlags & kRenderTarget_GrSurfaceFlag) {
+        if (!caps.isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
+            return false;
+        }
+    } else {
+        if (desc.fSampleCnt) {
+            return false;
+        }
+    }
+    if (levelCount > 1 && (GrPixelConfigIsSint(desc.fConfig) || !caps.mipMapSupport())) {
+        return false;
+    }
+    return true;
+}
+
+// MDB TODO: this should probably be a factory on GrSurfaceProxy
+sk_sp<GrTextureProxy> GrResourceProvider::createMipMappedTexture(
+                                                      const GrSurfaceDesc& desc,
+                                                      SkBudgeted budgeted,
+                                                      const GrMipLevel* texels,
+                                                      int mipLevelCount,
                                                       SkDestinationSurfaceColorMode mipColorMode) {
     ASSERT_SINGLE_OWNER
 
+    if (!mipLevelCount) {
+        if (texels) {
+            return nullptr;
+        }
+        return GrSurfaceProxy::MakeDeferred(this, desc, budgeted, nullptr, 0);
+    } else if (1 == mipLevelCount) {
+        if (!texels) {
+            return nullptr;
+        }
+        return this->createTextureProxy(desc, budgeted, texels[0]);
+    }
+
     if (this->isAbandoned()) {
         return nullptr;
     }
-    if (mipLevelCount && !texels) {
+
+    if (!validate_desc(desc, *fCaps, mipLevelCount)) {
         return nullptr;
     }
-    for (int i = 0; i < mipLevelCount; ++i) {
-        if (!texels[i].fPixels) {
-            return nullptr;
-        }
-    }
-    if (mipLevelCount > 1 && GrPixelConfigIsSint(desc.fConfig)) {
-        return nullptr;
-    }
-    if ((desc.fFlags & kRenderTarget_GrSurfaceFlag) &&
-        !fGpu->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
-        return nullptr;
-    }
-    if (!GrPixelConfigIsCompressed(desc.fConfig)) {
-        if (mipLevelCount < 2) {
-            flags |= kExact_Flag | kNoCreate_Flag;
-            if (GrTexture* texture = this->refScratchTexture(desc, flags)) {
-                if (!mipLevelCount ||
-                    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                                         texels[0].fPixels, texels[0].fRowBytes)) {
-                    if (SkBudgeted::kNo == budgeted) {
-                        texture->resourcePriv().makeUnbudgeted();
-                    }
-                    texture->texturePriv().setMipColorMode(mipColorMode);
-                    return texture;
-                }
-                texture->unref();
-            }
-        }
-    }
 
     SkTArray<GrMipLevel> texelsShallowCopy(mipLevelCount);
     for (int i = 0; i < mipLevelCount; ++i) {
+        if (!texels[i].fPixels) {
+            return nullptr;
+        }
+
         texelsShallowCopy.push_back(texels[i]);
     }
-    GrTexture* texture = fGpu->createTexture(desc, budgeted, texelsShallowCopy);
-    if (texture) {
-        texture->texturePriv().setMipColorMode(mipColorMode);
+    sk_sp<GrTexture> tex(fGpu->createTexture(desc, budgeted, texelsShallowCopy));
+    if (tex) {
+        tex->texturePriv().setMipColorMode(mipColorMode);
     }
-    return texture;
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
 }
 
-GrTexture* GrResourceProvider::createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                                             const void* srcData, size_t rowBytes, uint32_t flags) {
-    GrMipLevel tempTexels;
-    GrMipLevel* texels = nullptr;
-    int levelCount = 0;
-    if (srcData) {
-        tempTexels.fPixels = srcData;
-        tempTexels.fRowBytes = rowBytes;
-        texels = &tempTexels;
-        levelCount = 1;
+sk_sp<GrTexture> GrResourceProvider::getExactScratch(const GrSurfaceDesc& desc,
+                                                     SkBudgeted budgeted, uint32_t flags) {
+
+    flags |= kExact_Flag | kNoCreate_Flag;
+    sk_sp<GrTexture> tex(this->refScratchTexture(desc, flags));
+    if (tex && SkBudgeted::kNo == budgeted) {
+        tex->resourcePriv().makeUnbudgeted();
     }
-    return this->createMipMappedTexture(desc, budgeted, texels, levelCount, flags);
+
+    return tex;
 }
 
-GrTexture* GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc, uint32_t flags) {
-    ASSERT_SINGLE_OWNER
-    SkASSERT(0 == flags || kNoPendingIO_Flag == flags);
-    return this->internalCreateApproxTexture(desc, flags);
+static bool make_info(int w, int h, GrPixelConfig config, SkImageInfo* ii) {
+    SkColorType colorType;
+    if (!GrPixelConfigToColorType(config, &colorType)) {
+        return false;
+    }
+
+    *ii = SkImageInfo::Make(w, h, colorType, kUnknown_SkAlphaType, nullptr);
+    return true;
 }
 
-GrTexture* GrResourceProvider::internalCreateApproxTexture(const GrSurfaceDesc& desc,
-                                                           uint32_t scratchFlags) {
+sk_sp<GrTextureProxy> GrResourceProvider::createTextureProxy(const GrSurfaceDesc& desc,
+                                                             SkBudgeted budgeted,
+                                                             const GrMipLevel& mipLevel) {
     ASSERT_SINGLE_OWNER
+
     if (this->isAbandoned()) {
         return nullptr;
     }
-    // Currently we don't recycle compressed textures as scratch.
-    if (GrPixelConfigIsCompressed(desc.fConfig)) {
+
+    if (!mipLevel.fPixels) {
         return nullptr;
-    } else {
-        return this->refScratchTexture(desc, scratchFlags);
     }
+
+    if (!validate_desc(desc, *fCaps)) {
+        return nullptr;
+    }
+
+    GrContext* context = fGpu->getContext();
+
+    SkImageInfo srcInfo;
+
+    if (make_info(desc.fWidth, desc.fHeight, desc.fConfig, &srcInfo)) {
+        sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, 0);
+        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
+        if (proxy) {
+            sk_sp<GrSurfaceContext> sContext =
+                       context->contextPriv().makeWrappedSurfaceContext(std::move(proxy), nullptr);
+            if (sContext) {
+                if (sContext->writePixels(srcInfo, mipLevel.fPixels, mipLevel.fRowBytes, 0, 0)) {
+                    return sContext->asTextureProxyRef();
+                }
+            }
+        }
+    }
+
+    SkTArray<GrMipLevel> texels(1);
+    texels.push_back(mipLevel);
+
+    sk_sp<GrTexture> tex(fGpu->createTexture(desc, budgeted, texels));
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
 }
 
-GrTexture* GrResourceProvider::refScratchTexture(const GrSurfaceDesc& inDesc,
-                                                 uint32_t flags) {
+
+sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                                   uint32_t flags) {
+    ASSERT_SINGLE_OWNER
+
+    if (this->isAbandoned()) {
+        return nullptr;
+    }
+
+    if (!validate_desc(desc, *fCaps)) {
+        return nullptr;
+    }
+
+    sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, flags);
+    if (tex) {
+        return tex;
+    }
+
+    return fGpu->createTexture(desc, budgeted);
+}
+
+sk_sp<GrTexture> GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc,
+                                                         uint32_t flags) {
+    ASSERT_SINGLE_OWNER
+    SkASSERT(0 == flags || kNoPendingIO_Flag == flags);
+
+    if (this->isAbandoned()) {
+        return nullptr;
+    }
+
+    if (!validate_desc(desc, *fCaps)) {
+        return nullptr;
+    }
+
+    return this->refScratchTexture(desc, flags);
+}
+
+sk_sp<GrTexture> GrResourceProvider::refScratchTexture(const GrSurfaceDesc& inDesc,
+                                                       uint32_t flags) {
     ASSERT_SINGLE_OWNER
     SkASSERT(!this->isAbandoned());
-    SkASSERT(!GrPixelConfigIsCompressed(inDesc.fConfig));
+    SkASSERT(validate_desc(inDesc, *fCaps));
 
     SkTCopyOnFirstWrite<GrSurfaceDesc> desc(inDesc);
 
-    if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
+    // We could make initial clears work with scratch textures but it is a rare case so we just opt
+    // to fall back to making a new texture.
+    if (!SkToBool(inDesc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
+        (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag))) {
         if (!(kExact_Flag & flags)) {
             // bin by pow2 with a reasonable min
             GrSurfaceDesc* wdesc = desc.writable();
@@ -165,11 +252,7 @@
                                                                    scratchFlags);
         if (resource) {
             GrSurface* surface = static_cast<GrSurface*>(resource);
-            GrRenderTarget* rt = surface->asRenderTarget();
-            if (rt && fGpu->caps()->discardRenderTargetSupport()) {
-                rt->discard();
-            }
-            return surface->asTexture();
+            return sk_sp<GrTexture>(surface->asTexture());
         }
     }
 
@@ -180,20 +263,23 @@
     return nullptr;
 }
 
-sk_sp<GrTexture> GrResourceProvider::wrapBackendTexture(const GrBackendTextureDesc& desc,
+sk_sp<GrTexture> GrResourceProvider::wrapBackendTexture(const GrBackendTexture& tex,
+                                                        GrSurfaceOrigin origin,
+                                                        GrBackendTextureFlags flags,
+                                                        int sampleCnt,
                                                         GrWrapOwnership ownership) {
     ASSERT_SINGLE_OWNER
     if (this->isAbandoned()) {
         return nullptr;
     }
-    return fGpu->wrapBackendTexture(desc, ownership);
+    return fGpu->wrapBackendTexture(tex, origin, flags, sampleCnt, ownership);
 }
 
 sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendRenderTarget(
-        const GrBackendRenderTargetDesc& desc)
+        const GrBackendRenderTarget& backendRT, GrSurfaceOrigin origin)
 {
     ASSERT_SINGLE_OWNER
-    return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(desc);
+    return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(backendRT, origin);
 }
 
 void GrResourceProvider::assignUniqueKeyToResource(const GrUniqueKey& key,
@@ -229,10 +315,10 @@
         return;
     }
 
-    GrTexture* texture = proxy->instantiate(this);
-    if (!texture) {
+    if (!proxy->instantiate(this)) {
         return;
     }
+    GrTexture* texture = proxy->priv().peekTexture();
 
     this->assignUniqueKeyToResource(key, texture);
 }
@@ -249,7 +335,7 @@
     return GrSurfaceProxy::MakeWrapped(std::move(texture));
 }
 
-const GrBuffer* GrResourceProvider::createInstancedIndexBuffer(const uint16_t* pattern,
+const GrBuffer* GrResourceProvider::createPatternedIndexBuffer(const uint16_t* pattern,
                                                                int patternSize,
                                                                int reps,
                                                                int vertCount,
@@ -292,24 +378,24 @@
     GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
     static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 };
 
-    return this->createInstancedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
+    return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
 }
 
-GrPath* GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
+sk_sp<GrPath> GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
     SkASSERT(this->gpu()->pathRendering());
     return this->gpu()->pathRendering()->createPath(path, style);
 }
 
-GrPathRange* GrResourceProvider::createPathRange(GrPathRange::PathGenerator* gen,
-                                                 const GrStyle& style) {
+sk_sp<GrPathRange> GrResourceProvider::createPathRange(GrPathRange::PathGenerator* gen,
+                                                       const GrStyle& style) {
     SkASSERT(this->gpu()->pathRendering());
     return this->gpu()->pathRendering()->createPathRange(gen, style);
 }
 
-GrPathRange* GrResourceProvider::createGlyphs(const SkTypeface* tf,
-                                              const SkScalerContextEffects& effects,
-                                              const SkDescriptor* desc,
-                                              const GrStyle& style) {
+sk_sp<GrPathRange> GrResourceProvider::createGlyphs(const SkTypeface* tf,
+                                                    const SkScalerContextEffects& effects,
+                                                    const SkDescriptor* desc,
+                                                    const GrStyle& style) {
 
     SkASSERT(this->gpu()->pathRendering());
     return this->gpu()->pathRendering()->createGlyphs(tf, effects, desc, style);
@@ -406,12 +492,12 @@
 }
 
 sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendTextureAsRenderTarget(
-        const GrBackendTextureDesc& desc)
+        const GrBackendTexture& tex, GrSurfaceOrigin origin, int sampleCnt)
 {
     if (this->isAbandoned()) {
         return nullptr;
     }
-    return this->gpu()->wrapBackendTextureAsRenderTarget(desc);
+    return this->gpu()->wrapBackendTextureAsRenderTarget(tex, origin, sampleCnt);
 }
 
 sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore() {
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 6b3633c..2e9b8a8 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -12,6 +12,7 @@
 #include "GrGpu.h"
 #include "GrPathRange.h"
 
+class GrBackendRenderTarget;
 class GrPath;
 class GrRenderTarget;
 class GrSingleOwner;
@@ -47,29 +48,11 @@
      * @param texels        A contiguous array of mipmap levels
      * @param mipLevelCount The amount of elements in the texels array
      */
-    GrTexture* createMipMappedTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                                      const GrMipLevel* texels, int mipLevelCount,
-                                      uint32_t flags = 0,
-                                      SkDestinationSurfaceColorMode mipColorMode =
+    sk_sp<GrTextureProxy> createMipMappedTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                                 const GrMipLevel* texels, int mipLevelCount,
+                                                 SkDestinationSurfaceColorMode mipColorMode =
                                                         SkDestinationSurfaceColorMode::kLegacy);
 
-    /**
-     * This function is a shim which creates a SkTArray<GrMipLevel> of size 1.
-     * It then calls createTexture with that SkTArray.
-     *
-     * @param srcData   Pointer to the pixel values (optional).
-     * @param rowBytes  The number of bytes between rows of the texture. Zero
-     *                  implies tightly packed rows. For compressed pixel configs, this
-     *                  field is ignored.
-     */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, const void* srcData,
-                             size_t rowBytes, uint32_t flags = 0);
-
-    /** Shortcut for creating a texture with no initial data to upload. */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, uint32_t flags = 0) {
-        return this->createTexture(desc, budgeted, nullptr, 0, flags);
-    }
-
     /** Assigns a unique key to the texture. The texture will be findable via this key using
     findTextureByUniqueKey(). If an existing texture has this key, it's key will be removed. */
     void assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy*);
@@ -81,10 +64,15 @@
      * Finds a texture that approximately matches the descriptor. Will be at least as large in width
      * and height as desc specifies. If desc specifies that the texture should be a render target
      * then result will be a render target. Format and sample count will always match the request.
-     * The contents of the texture are undefined. The caller owns a ref on the returned texture and
-     * must balance with a call to unref.
+     * The contents of the texture are undefined.
      */
-    GrTexture* createApproxTexture(const GrSurfaceDesc&, uint32_t flags);
+    sk_sp<GrTexture> createApproxTexture(const GrSurfaceDesc&, uint32_t flags);
+
+    /** Create an exact fit texture with no initial data to upload.
+     */
+    sk_sp<GrTexture> createTexture(const GrSurfaceDesc&, SkBudgeted, uint32_t flags = 0);
+
+    sk_sp<GrTextureProxy> createTextureProxy(const GrSurfaceDesc&, SkBudgeted, const GrMipLevel&);
 
     ///////////////////////////////////////////////////////////////////////////
     // Wrapped Backend Surfaces
@@ -97,7 +85,10 @@
      *
      * @return GrTexture object or NULL on failure.
      */
-    sk_sp<GrTexture> wrapBackendTexture(const GrBackendTextureDesc& desc,
+    sk_sp<GrTexture> wrapBackendTexture(const GrBackendTexture& tex,
+                                        GrSurfaceOrigin origin,
+                                        GrBackendTextureFlags flags,
+                                        int sampleCnt,
                                         GrWrapOwnership = kBorrow_GrWrapOwnership);
 
     /**
@@ -109,14 +100,14 @@
      *
      * @return GrRenderTarget object or NULL on failure.
      */
-    sk_sp<GrRenderTarget> wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc);
+    sk_sp<GrRenderTarget> wrapBackendRenderTarget(const GrBackendRenderTarget&, GrSurfaceOrigin);
 
-    static const int kMinScratchTextureSize;
+    static const uint32_t kMinScratchTextureSize;
 
     /**
-     * Either finds and refs, or creates an index buffer for instanced drawing with a specific
-     * pattern if the index buffer is not found. If the return is non-null, the caller owns
-     * a ref on the returned GrBuffer.
+     * Either finds and refs, or creates an index buffer with a repeating pattern for drawing
+     * contiguous vertices of a repeated mesh. If the return is non-null, the caller owns a ref on
+     * the returned GrBuffer.
      *
      * @param pattern     the pattern of indices to repeat
      * @param patternSize size in bytes of the pattern
@@ -126,7 +117,7 @@
      *
      * @return The index buffer if successful, otherwise nullptr.
      */
-    const GrBuffer* findOrCreateInstancedIndexBuffer(const uint16_t* pattern,
+    const GrBuffer* findOrCreatePatternedIndexBuffer(const uint16_t* pattern,
                                                      int patternSize,
                                                      int reps,
                                                      int vertCount,
@@ -134,7 +125,7 @@
         if (GrBuffer* buffer = this->findAndRefTByUniqueKey<GrBuffer>(key)) {
             return buffer;
         }
-        return this->createInstancedIndexBuffer(pattern, patternSize, reps, vertCount, key);
+        return this->createPatternedIndexBuffer(pattern, patternSize, reps, vertCount, key);
     }
 
     /**
@@ -156,10 +147,10 @@
      * Factories for GrPath and GrPathRange objects. It's an error to call these if path rendering
      * is not supported.
      */
-    GrPath* createPath(const SkPath&, const GrStyle&);
-    GrPathRange* createPathRange(GrPathRange::PathGenerator*, const GrStyle&);
-    GrPathRange* createGlyphs(const SkTypeface*, const SkScalerContextEffects&,
-                              const SkDescriptor*, const GrStyle&);
+    sk_sp<GrPath> createPath(const SkPath&, const GrStyle&);
+    sk_sp<GrPathRange> createPathRange(GrPathRange::PathGenerator*, const GrStyle&);
+    sk_sp<GrPathRange> createGlyphs(const SkTypeface*, const SkScalerContextEffects&,
+                                    const SkDescriptor*, const GrStyle&);
 
     /** These flags govern which scratch resources we are allowed to return */
     enum Flags {
@@ -211,7 +202,9 @@
       *
       * @return GrRenderTarget object or NULL on failure.
       */
-     sk_sp<GrRenderTarget> wrapBackendTextureAsRenderTarget(const GrBackendTextureDesc& desc);
+     sk_sp<GrRenderTarget> wrapBackendTextureAsRenderTarget(const GrBackendTexture&,
+                                                            GrSurfaceOrigin origin,
+                                                            int sampleCnt);
 
     /**
      * Assigns a unique key to a resource. If the key is associated with another resource that
@@ -242,22 +235,26 @@
         fGpu = nullptr;
     }
 
-    // 'Proxy' is about to be used as a texture src. This query can be used to determine if
-    // it is going to need a texture domain.
-    static bool IsFunctionallyExact(GrTextureProxy* proxy);
+    // 'proxy' is about to be used as a texture src or drawn to. This query can be used to
+    // determine if it is going to need a texture domain or a full clear.
+    static bool IsFunctionallyExact(GrSurfaceProxy* proxy);
 
     const GrCaps* caps() const { return fCaps.get(); }
 
 private:
-    GrTexture* internalCreateApproxTexture(const GrSurfaceDesc& desc, uint32_t scratchTextureFlags);
-
     GrTexture* findAndRefTextureByUniqueKey(const GrUniqueKey& key);
     void assignUniqueKeyToTexture(const GrUniqueKey& key, GrTexture* texture) {
         SkASSERT(key.isValid());
         this->assignUniqueKeyToResource(key, texture);
     }
 
-    GrTexture* refScratchTexture(const GrSurfaceDesc&, uint32_t scratchTextureFlags);
+    sk_sp<GrTexture> refScratchTexture(const GrSurfaceDesc&, uint32_t scratchTextureFlags);
+
+    /*
+     * Try to find an existing scratch texture that exactly matches 'desc'. If successful
+     * update the budgeting accordingly.
+     */
+    sk_sp<GrTexture> getExactScratch(const GrSurfaceDesc&, SkBudgeted, uint32_t flags);
 
     GrResourceCache* cache() { return fCache; }
     const GrResourceCache* cache() const { return fCache; }
@@ -270,7 +267,7 @@
         return !SkToBool(fCache);
     }
 
-    const GrBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
+    const GrBuffer* createPatternedIndexBuffer(const uint16_t* pattern,
                                                int patternSize,
                                                int reps,
                                                int vertCount,
diff --git a/src/gpu/GrSKSLPrettyPrint.cpp b/src/gpu/GrSKSLPrettyPrint.cpp
new file mode 100644
index 0000000..65175c9
--- /dev/null
+++ b/src/gpu/GrSKSLPrettyPrint.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 "GrSKSLPrettyPrint.h"
+
+namespace GrSKSLPrettyPrint {
+
+class GLSLPrettyPrint {
+public:
+    GLSLPrettyPrint() {}
+
+    SkString prettify(const char** strings, int* lengths, int count, bool countlines) {
+        fCountlines = countlines;
+        fTabs = 0;
+        fLinecount = 1;
+        fFreshline = true;
+
+        // If a string breaks while in the middle 'parse until' we need to continue parsing on the
+        // next string
+        fInParseUntilNewline = false;
+        fInParseUntil = false;
+
+        int parensDepth = 0;
+
+        // number 1st line
+        this->lineNumbering();
+        for (int i = 0; i < count; i++) {
+            // setup pretty state
+            fIndex = 0;
+            fLength = lengths[i];
+            fInput = strings[i];
+
+            while (fLength > fIndex) {
+                /* the heart and soul of our prettification algorithm.  The rules should hopefully
+                 * be self explanatory.  For '#' and '//' tokens we parse until we reach a newline.
+                 *
+                 * For long style comments like this one, we search for the ending token.  We also
+                 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
+                 * ourselves.  This allows us to remain in control of line numbers, and matching
+                 * tabs Existing tabs in the input string are copied over too, but this will look
+                 *  funny
+                 *
+                 * '{' and '}' are handled in basically the same way.  We add a newline if we aren't
+                 * on a fresh line, dirty the line, then add a second newline, ie braces are always
+                 * on their own lines indented properly.  The one funkiness here is structs print
+                 * with the semicolon on its own line.  Its not a problem for a glsl compiler though
+                 *
+                 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
+                 * in for loops.
+                 *
+                 * ';' means add a new line
+                 *
+                 * '\t' and '\n' are ignored in general parsing for backwards compatability with
+                 * existing shader code and we also have a special case for handling whitespace
+                 * at the beginning of fresh lines.
+                 *
+                 * Otherwise just add the new character to the pretty string, indenting if
+                 * necessary.
+                 */
+                if (fInParseUntilNewline) {
+                    this->parseUntilNewline();
+                } else if (fInParseUntil) {
+                    this->parseUntil(fInParseUntilToken);
+                } else if (this->hasToken("#") || this->hasToken("//")) {
+                    this->parseUntilNewline();
+                } else if (this->hasToken("/*")) {
+                    this->parseUntil("*/");
+                } else if ('{' == fInput[fIndex]) {
+                    this->newline();
+                    this->appendChar('{');
+                    fTabs++;
+                    this->newline();
+                } else if ('}' == fInput[fIndex]) {
+                    fTabs--;
+                    this->newline();
+                    this->appendChar('}');
+                    this->newline();
+                } else if (this->hasToken(")")) {
+                    parensDepth--;
+                } else if (this->hasToken("(")) {
+                    parensDepth++;
+                } else if (!parensDepth && this->hasToken(";")) {
+                    this->newline();
+                } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
+                           (fFreshline && ' ' == fInput[fIndex])) {
+                    fIndex++;
+                } else {
+                    this->appendChar(fInput[fIndex]);
+                }
+            }
+        }
+        return fPretty;
+    }
+
+private:
+    void appendChar(char c) {
+        this->tabString();
+        fPretty.appendf("%c", fInput[fIndex++]);
+        fFreshline = false;
+    }
+
+    // hasToken automatically consumes the next token, if it is a match, and then tabs
+    // if necessary, before inserting the token into the pretty string
+    bool hasToken(const char* token) {
+        size_t i = fIndex;
+        for (size_t j = 0; token[j] && fLength > i; i++, j++) {
+            if (token[j] != fInput[i]) {
+                return false;
+            }
+        }
+        this->tabString();
+        fIndex = i;
+        fPretty.append(token);
+        fFreshline = false;
+        return true;
+    }
+
+    void parseUntilNewline() {
+        while (fLength > fIndex) {
+            if ('\n' == fInput[fIndex]) {
+                fIndex++;
+                this->newline();
+                fInParseUntilNewline = false;
+                break;
+            }
+            fPretty.appendf("%c", fInput[fIndex++]);
+            fInParseUntilNewline = true;
+        }
+    }
+
+    // this code assumes it is not actually searching for a newline.  If you need to search for a
+    // newline, then use the function above.  If you do search for a newline with this function
+    // it will consume the entire string and the output will certainly not be prettified
+    void parseUntil(const char* token) {
+        while (fLength > fIndex) {
+            // For embedded newlines,  this code will make sure to embed the newline in the
+            // pretty string, increase the linecount, and tab out the next line to the appropriate
+            // place
+            if ('\n' == fInput[fIndex]) {
+                this->newline();
+                this->tabString();
+                fIndex++;
+            }
+            if (this->hasToken(token)) {
+                fInParseUntil = false;
+                break;
+            }
+            fFreshline = false;
+            fPretty.appendf("%c", fInput[fIndex++]);
+            fInParseUntil = true;
+            fInParseUntilToken = token;
+        }
+    }
+
+    // We only tab if on a newline, otherwise consider the line tabbed
+    void tabString() {
+        if (fFreshline) {
+            for (int t = 0; t < fTabs; t++) {
+                fPretty.append("\t");
+            }
+        }
+    }
+
+    // newline is really a request to add a newline, if we are on a fresh line there is no reason
+    // to add another newline
+    void newline() {
+        if (!fFreshline) {
+            fFreshline = true;
+            fPretty.append("\n");
+            this->lineNumbering();
+        }
+    }
+
+    void lineNumbering() {
+        if (fCountlines) {
+            fPretty.appendf("%4d\t", fLinecount++);
+        }
+    }
+
+    bool fCountlines, fFreshline;
+    int fTabs, fLinecount;
+    size_t fIndex, fLength;
+    const char* fInput;
+    SkString fPretty;
+
+    // Some helpers for parseUntil when we go over a string length
+    bool fInParseUntilNewline;
+    bool fInParseUntil;
+    const char* fInParseUntilToken;
+};
+
+SkString PrettyPrint(const char** strings, int* lengths, int count, bool countlines) {
+    GLSLPrettyPrint pp;
+    return pp.prettify(strings, lengths, count, countlines);
+}
+
+}  // namespace GrSKSLPrettyPrint
diff --git a/src/gpu/GrSKSLPrettyPrint.h b/src/gpu/GrSKSLPrettyPrint.h
new file mode 100644
index 0000000..8fa4c1e
--- /dev/null
+++ b/src/gpu/GrSKSLPrettyPrint.h
@@ -0,0 +1,16 @@
+/*
+ * 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 GrSKSLPrettyPrint_DEFINED
+#define GrSKSLPrettyPrint_DEFINED
+
+#include "SkString.h"
+
+namespace GrSKSLPrettyPrint {
+SkString PrettyPrint(const char** strings, int* lengths, int count, bool countlines);
+};
+
+#endif
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 647496d..141f357 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -19,7 +19,7 @@
 
 #include "SkDistanceFieldGen.h"
 
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 /*
  * Convert a boolean operation into a transfer mode code
@@ -173,12 +173,10 @@
     SkMatrix maskMatrix = SkMatrix::MakeTrans(SkIntToScalar(-textureOriginInDeviceSpace.fX),
                                               SkIntToScalar(-textureOriginInDeviceSpace.fY));
     maskMatrix.preConcat(viewMatrix);
-    std::unique_ptr<GrMeshDrawOp> op = GrRectOpFactory::MakeNonAAFill(
-            paint.getColor(), SkMatrix::I(), dstRect, nullptr, &invert);
     paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(
             resourceProvider, std::move(proxy), nullptr, maskMatrix,
             GrSamplerParams::kNone_FilterMode));
-    GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    pipelineBuilder.setUserStencil(&userStencilSettings);
-    renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    renderTargetContext->addDrawOp(
+            clip, GrNonAAFillRectOp::Make(std::move(paint), SkMatrix::I(), dstRect, nullptr,
+                                          &invert, GrAAType::kNone, &userStencilSettings));
 }
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index e3e6b07..6f5dc7f 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -32,8 +32,10 @@
         return "medium";
     case kHigh_GrSLPrecision:
         return "high";
+    default:
+        SkFAIL("Unexpected precision type.");
+        return "";
     }
-    return "";
 }
 
 GrShaderCaps::GrShaderCaps(const GrContextOptions& options) {
@@ -58,6 +60,7 @@
     fAtan2ImplementedAsAtanYOverX = false;
     fRequiresLocalOutputColorForFBFetch = false;
     fMustImplementGSInvocationsWithLoop = false;
+    fMustObfuscateUniformColor = false;
     fFlatInterpolationSupport = false;
     fNoPerspectiveInterpolationSupport = false;
     fMultisampleInterpolationSupport = false;
@@ -65,6 +68,7 @@
     fSampleMaskOverrideCoverageSupport = false;
     fExternalTextureSupport = false;
     fTexelFetchSupport = false;
+    fVertexIDSupport = false;
 
     fVersionDeclString = nullptr;
     fShaderDerivativeExtensionString = nullptr;
@@ -144,6 +148,7 @@
                                                              "YES" : "NO"));
     r.appendf("Must implement geo shader invocations with loop : %s\n",
               (fMustImplementGSInvocationsWithLoop ? "YES" : "NO"));
+    r.appendf("Must obfuscate uniform color: %s\n", (fMustObfuscateUniformColor ? "YES" : "NO"));
     r.appendf("Flat interpolation support: %s\n", (fFlatInterpolationSupport ?  "YES" : "NO"));
     r.appendf("No perspective interpolation support: %s\n", (fNoPerspectiveInterpolationSupport ?
                                                              "YES" : "NO"));
@@ -154,6 +159,7 @@
                                                               "YES" : "NO"));
     r.appendf("External texture support: %s\n", (fExternalTextureSupport ? "YES" : "NO"));
     r.appendf("texelFetch support: %s\n", (fTexelFetchSupport ? "YES" : "NO"));
+    r.appendf("sk_VertexID support: %s\n", (fVertexIDSupport ? "YES" : "NO"));
     r.appendf("Max VS Samplers: %d\n", fMaxVertexSamplers);
     r.appendf("Max GS Samplers: %d\n", fMaxGeometrySamplers);
     r.appendf("Max FS Samplers: %d\n", fMaxFragmentSamplers);
@@ -201,7 +207,7 @@
         }
 
         uint8_t* table = fSamplerPrecisions[visibility];
-        table[kUnknown_GrPixelConfig]        = kDefault_GrSLPrecision;
+        table[kUnknown_GrPixelConfig]        = lowp;
         table[kAlpha_8_GrPixelConfig]        = lowp;
         table[kGray_8_GrPixelConfig]         = lowp;
         table[kRGB_565_GrPixelConfig]        = lowp;
@@ -211,13 +217,12 @@
         table[kSRGBA_8888_GrPixelConfig]     = lowp;
         table[kSBGRA_8888_GrPixelConfig]     = lowp;
         table[kRGBA_8888_sint_GrPixelConfig] = lowp;
-        table[kETC1_GrPixelConfig]           = lowp;
         table[kRGBA_float_GrPixelConfig]     = kHigh_GrSLPrecision;
         table[kRG_float_GrPixelConfig]       = kHigh_GrSLPrecision;
         table[kAlpha_half_GrPixelConfig]     = mediump;
         table[kRGBA_half_GrPixelConfig]      = mediump;
 
-        GR_STATIC_ASSERT(15 == kGrPixelConfigCnt);
+        GR_STATIC_ASSERT(14 == kGrPixelConfigCnt);
     }
 }
 
diff --git a/include/gpu/GrShaderVar.h b/src/gpu/GrShaderVar.h
similarity index 100%
rename from include/gpu/GrShaderVar.h
rename to src/gpu/GrShaderVar.h
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 0228937..f823e13 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -12,7 +12,7 @@
 #include "GrPipelineBuilder.h"
 #include "GrResourceProvider.h"
 #include "GrSWMaskHelper.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
@@ -76,12 +76,9 @@
                                            const SkMatrix& viewMatrix,
                                            const SkRect& rect,
                                            const SkMatrix& localMatrix) {
-    std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(paint.getColor(), viewMatrix,
-                                                                    rect, nullptr, &localMatrix));
-
-    GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    pipelineBuilder.setUserStencil(&userStencilSettings);
-    renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+    renderTargetContext->addDrawOp(
+            clip, GrNonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, &localMatrix,
+                                          GrAAType::kNone, &userStencilSettings));
 }
 
 void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
@@ -136,7 +133,9 @@
     bool inverseFilled = false;
     SkTLazy<GrShape> tmpShape;
     SkASSERT(!args.fShape->style().applies());
-    inverseFilled = args.fShape->inverseFilled();
+    // If the path is hairline, ignore inverse fill.
+    inverseFilled = args.fShape->inverseFilled() &&
+                    !IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
 
     SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
     // To prevent overloading the cache with entries during animations we limit the cache of masks
diff --git a/src/gpu/GrStencilSettings.cpp b/src/gpu/GrStencilSettings.cpp
index 7790c55..689557e 100644
--- a/src/gpu/GrStencilSettings.cpp
+++ b/src/gpu/GrStencilSettings.cpp
@@ -455,6 +455,20 @@
     return gUserToClipTable[invertedFill][op];
 }
 
+static constexpr GrUserStencilSettings gZeroStencilClipBit(
+    GrUserStencilSettings::StaticInit<
+        0x0000,
+        GrUserStencilTest::kAlways,
+        0xffff,
+        GrUserStencilOp::kZeroClipBit,
+        GrUserStencilOp::kZeroClipBit,
+        0x0000>()
+);
+
+const GrUserStencilSettings* GrStencilSettings::SetClipBitSettings(bool setToInside) {
+    return setToInside ? &gReplaceClip : &gZeroStencilClipBit;
+}
+
 void GrStencilSettings::genKey(GrProcessorKeyBuilder* b) const {
     b->add32(fFlags);
     if (this->isDisabled()) {
diff --git a/src/gpu/GrStencilSettings.h b/src/gpu/GrStencilSettings.h
index 15a8cac..1fed6b8 100644
--- a/src/gpu/GrStencilSettings.h
+++ b/src/gpu/GrStencilSettings.h
@@ -109,6 +109,9 @@
                                                              bool invertedFill,
                                                              bool* drawDirectToClip);
 
+    /** Gets the user stencil settings to directly set the clip bit. */
+    static const GrUserStencilSettings* SetClipBitSettings(bool setToInside);
+
 private:
     // Internal flag for backends to optionally mark their tracked stencil state as invalid.
     enum { kInvalid_PrivateFlag = (kLast_StencilFlag << 1) };
diff --git a/src/gpu/GrSurface.cpp b/src/gpu/GrSurface.cpp
index 302ba43..635e74e 100644
--- a/src/gpu/GrSurface.cpp
+++ b/src/gpu/GrSurface.cpp
@@ -14,16 +14,6 @@
 #include "SkGr.h"
 #include "SkMathPriv.h"
 
-GrSurface::~GrSurface() {
-    if (fLastOpList) {
-        fLastOpList->clearTarget();
-    }
-    SkSafeUnref(fLastOpList);
-
-    // check that invokeReleaseProc has been called (if needed)
-    SkASSERT(NULL == fReleaseProc);
-}
-
 size_t GrSurface::WorstCaseSize(const GrSurfaceDesc& desc, bool useNextPow2) {
     size_t size;
 
@@ -39,7 +29,6 @@
             colorValuesPerPixel += 1;
         }
         SkASSERT(kUnknown_GrPixelConfig != desc.fConfig);
-        SkASSERT(!GrPixelConfigIsCompressed(desc.fConfig));
         size_t colorBytes = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
 
         // This would be a nice assert to have (i.e., we aren't creating 0 width/height surfaces).
@@ -49,11 +38,7 @@
         size = colorValuesPerPixel * colorBytes;
         size += colorBytes/3; // in case we have to mipmap
     } else {
-        if (GrPixelConfigIsCompressed(desc.fConfig)) {
-            size = GrCompressedFormatDataSize(desc.fConfig, width, height);
-        } else {
-            size = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
-        }
+        size = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
 
         size += size/3;  // in case we have to mipmap
     }
@@ -61,21 +46,17 @@
     return size;
 }
 
-size_t GrSurface::ComputeSize(const GrSurfaceDesc& desc,
+size_t GrSurface::ComputeSize(GrPixelConfig config,
+                              int width,
+                              int height,
                               int colorSamplesPerPixel,
                               bool hasMIPMaps,
                               bool useNextPow2) {
-    size_t colorSize;
+    width = useNextPow2 ? GrNextPow2(width) : width;
+    height = useNextPow2 ? GrNextPow2(height) : height;
 
-    int width = useNextPow2 ? GrNextPow2(desc.fWidth) : desc.fWidth;
-    int height = useNextPow2 ? GrNextPow2(desc.fHeight) : desc.fHeight;
-
-    SkASSERT(kUnknown_GrPixelConfig != desc.fConfig);
-    if (GrPixelConfigIsCompressed(desc.fConfig)) {
-        colorSize = GrCompressedFormatDataSize(desc.fConfig, width, height);
-    } else {
-        colorSize = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
-    }
+    SkASSERT(kUnknown_GrPixelConfig != config);
+    size_t colorSize = (size_t)width * height * GrBytesPerPixel(config);
     SkASSERT(colorSize > 0);
 
     size_t finalSize = colorSamplesPerPixel * colorSize;
@@ -85,8 +66,6 @@
         // we'd expect because we never change fDesc.fWidth/fHeight.
         finalSize += colorSize/3;
     }
-
-    SkASSERT(finalSize <= WorstCaseSize(desc, useNextPow2));
     return finalSize;
 }
 
@@ -139,36 +118,6 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-bool GrSurface::writePixels(SkColorSpace* dstColorSpace, int left, int top, int width, int height,
-                            GrPixelConfig config, SkColorSpace* srcColorSpace, const void* buffer,
-                            size_t rowBytes, uint32_t pixelOpsFlags) {
-    // go through context so that all necessary flushing occurs
-    GrContext* context = this->getContext();
-    if (nullptr == context) {
-        return false;
-    }
-    return context->writeSurfacePixels(this, dstColorSpace, left, top, width, height, config,
-                                       srcColorSpace, buffer, rowBytes, pixelOpsFlags);
-}
-
-bool GrSurface::readPixels(SkColorSpace* srcColorSpace, int left, int top, int width, int height,
-                           GrPixelConfig config, SkColorSpace* dstColorSpace, void* buffer,
-                           size_t rowBytes, uint32_t pixelOpsFlags) {
-    // go through context so that all necessary flushing occurs
-    GrContext* context = this->getContext();
-    if (nullptr == context) {
-        return false;
-    }
-    return context->readSurfacePixels(this, srcColorSpace, left, top, width, height, config,
-                                      dstColorSpace, buffer, rowBytes, pixelOpsFlags);
-}
-
-void GrSurface::flushWrites() {
-    if (!this->wasDestroyed()) {
-        this->getContext()->flushSurfaceWrites(this);
-    }
-}
-
 bool GrSurface::hasPendingRead() const {
     const GrTexture* thisTex = this->asTexture();
     if (thisTex && thisTex->internalHasPendingRead()) {
@@ -206,23 +155,9 @@
 }
 
 void GrSurface::onRelease() {
-    this->invokeReleaseProc();
     this->INHERITED::onRelease();
 }
 
 void GrSurface::onAbandon() {
-    this->invokeReleaseProc();
     this->INHERITED::onAbandon();
 }
-
-void GrSurface::setLastOpList(GrOpList* opList) {
-    if (fLastOpList) {
-        // The non-MDB world never closes so we can't check this condition
-#ifdef ENABLE_MDB
-        SkASSERT(fLastOpList->isClosed());
-#endif
-        fLastOpList->clearTarget();
-    }
-
-    SkRefCnt_SafeAssign(fLastOpList, opList);
-}
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index a28c27a..2bde24b 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -6,7 +6,10 @@
  */
 
 #include "GrSurfaceContext.h"
+
+#include "GrContextPriv.h"
 #include "SkColorSpace_Base.h"
+#include "SkGr.h"
 
 #include "../private/GrAuditTrail.h"
 
@@ -23,8 +26,45 @@
     : fContext(context)
     , fColorSpace(std::move(colorSpace))
     , fAuditTrail(auditTrail)
+    , fDrawingManager(drawingMgr)
 #ifdef SK_DEBUG
     , fSingleOwner(singleOwner)
 #endif
-    , fDrawingManager(drawingMgr) {
+{
+}
+
+bool GrSurfaceContext::readPixels(const SkImageInfo& dstInfo, void* dstBuffer,
+                                  size_t dstRowBytes, int x, int y, uint32_t flags) {
+    // TODO: teach GrRenderTarget to take ImageInfo directly to specify the src pixels
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo, *fContext->caps());
+    if (kUnknown_GrPixelConfig == config) {
+        return false;
+    }
+
+    // TODO: this seems to duplicate code in SkImage_Gpu::onReadPixels
+    if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
+        flags |= GrContextPriv::kUnpremul_PixelOpsFlag;
+    }
+
+    return fContext->contextPriv().readSurfacePixels(this, x, y,
+                                                     dstInfo.width(), dstInfo.height(), config,
+                                                     dstInfo.colorSpace(),
+                                                     dstBuffer, dstRowBytes, flags);
+}
+
+bool GrSurfaceContext::writePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
+                                   size_t srcRowBytes, int x, int y, uint32_t flags) {
+    // TODO: teach GrRenderTarget to take ImageInfo directly to specify the src pixels
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(srcInfo, *fContext->caps());
+    if (kUnknown_GrPixelConfig == config) {
+        return false;
+    }
+    if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
+        flags |= GrContextPriv::kUnpremul_PixelOpsFlag;
+    }
+
+    return fContext->contextPriv().writeSurfacePixels(this, x, y,
+                                                      srcInfo.width(), srcInfo.height(),
+                                                      config, srcInfo.colorSpace(),
+                                                      srcBuffer, srcRowBytes, flags);
 }
diff --git a/src/gpu/GrSurfaceContext.h b/src/gpu/GrSurfaceContext.h
index 88badc1..bab753c 100644
--- a/src/gpu/GrSurfaceContext.h
+++ b/src/gpu/GrSurfaceContext.h
@@ -74,9 +74,7 @@
      *              unsupported pixel config.
      */
     bool readPixels(const SkImageInfo& dstInfo, void* dstBuffer, size_t dstRowBytes,
-                    int x, int y, uint32_t flags = 0) {
-        return this->onReadPixels(dstInfo, dstBuffer, dstRowBytes, x, y, flags);
-    }
+                    int x, int y, uint32_t flags = 0);
 
     /**
      * Writes a rectangle of pixels [srcInfo, srcBuffer, srcRowbytes] into the
@@ -91,9 +89,7 @@
      *              unsupported pixel config.
      */
     bool writePixels(const SkImageInfo& srcInfo, const void* srcBuffer, size_t srcRowBytes,
-                     int x, int y, uint32_t flags = 0) {
-        return this->onWritePixels(srcInfo, srcBuffer, srcRowBytes, x, y, flags);
-    }
+                     int x, int y, uint32_t flags = 0);
 
     // TODO: this is virtual b.c. this object doesn't have a pointer to the wrapped GrSurfaceProxy?
     virtual GrSurfaceProxy* asSurfaceProxy() = 0;
@@ -129,20 +125,14 @@
     sk_sp<SkColorSpace>   fColorSpace;
     GrAuditTrail*         fAuditTrail;
 
-    // In debug builds we guard against improper thread handling
-    SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
-
 private:
-    virtual bool onCopy(GrSurfaceProxy* src,
-                        const SkIRect& srcRect,
-                        const SkIPoint& dstPoint) = 0;
-    virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                              size_t dstRowBytes, int x, int y, uint32_t flags) = 0;
-    virtual bool onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
-                               size_t srcRowBytes, int x, int y, uint32_t flags) = 0;
+    virtual bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) = 0;
 
     GrDrawingManager*     fDrawingManager;
 
+    // In debug builds we guard against improper thread handling
+    SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
+
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/GrSurfacePriv.h b/src/gpu/GrSurfacePriv.h
index c7ae6a4..ce144c8 100644
--- a/src/gpu/GrSurfacePriv.h
+++ b/src/gpu/GrSurfacePriv.h
@@ -17,7 +17,7 @@
     implemented privately in GrSurface with a inline public method here). */
 class GrSurfacePriv {
 public:
-    /** Helpers used in read/write pixels implementations. The paramters are adjusted so that the
+    /** Helpers used in read/write pixels implementations. The parameters are adjusted so that the
         read/write respects the bounds of a surface. If the input *rowBytes is 0 it will be
         the tight row bytes (based on width and bpp) on output. */
     static bool AdjustReadPixelParams(int surfaceWidth,
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 2506540..53529c2 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -21,38 +21,53 @@
 #include "SkMathPriv.h"
 
 GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, SkBackingFit fit)
-    : INHERITED(std::move(surface))
-    , fDesc(fTarget->desc())
-    , fFit(fit)
-    , fBudgeted(fTarget->resourcePriv().isBudgeted())
-    , fFlags(0)
-    , fUniqueID(fTarget->uniqueID()) // Note: converting from unique resource ID to a proxy ID!
-    , fGpuMemorySize(kInvalidGpuMemorySize)
-    , fLastOpList(nullptr) {
-}
+        : INHERITED(std::move(surface))
+        , fConfig(fTarget->config())
+        , fWidth(fTarget->width())
+        , fHeight(fTarget->height())
+        , fOrigin(fTarget->origin())
+        , fFit(fit)
+        , fBudgeted(fTarget->resourcePriv().isBudgeted())
+        , fFlags(0)
+        , fUniqueID(fTarget->uniqueID())  // Note: converting from unique resource ID to a proxy ID!
+        , fNeedsClear(false)
+        , fGpuMemorySize(kInvalidGpuMemorySize)
+        , fLastOpList(nullptr) {}
 
 GrSurfaceProxy::~GrSurfaceProxy() {
-    if (fLastOpList) {
-        fLastOpList->clearTarget();
-    }
-    SkSafeUnref(fLastOpList);
+    // For this to be deleted the opList that held a ref on it (if there was one) must have been
+    // deleted. Which would have cleared out this back pointer.
+    SkASSERT(!fLastOpList);
 }
 
-GrSurface* GrSurfaceProxy::instantiate(GrResourceProvider* resourceProvider) {
+bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
+                                     GrSurfaceFlags flags, bool isMipMapped,
+                                     SkDestinationSurfaceColorMode mipColorMode) {
     if (fTarget) {
-        return fTarget;
+        return true;
+    }
+    GrSurfaceDesc desc;
+    desc.fConfig = fConfig;
+    desc.fWidth = fWidth;
+    desc.fHeight = fHeight;
+    desc.fOrigin = fOrigin;
+    desc.fSampleCnt = sampleCnt;
+    desc.fIsMipMapped = isMipMapped;
+    desc.fFlags = flags;
+    if (fNeedsClear) {
+        desc.fFlags |= kPerformInitialClear_GrSurfaceFlag;
     }
 
     if (SkBackingFit::kApprox == fFit) {
-        fTarget = resourceProvider->createApproxTexture(fDesc, fFlags);
+        fTarget = resourceProvider->createApproxTexture(desc, fFlags).release();
     } else {
-        fTarget = resourceProvider->createTexture(fDesc, fBudgeted, fFlags);
+        fTarget = resourceProvider->createTexture(desc, fBudgeted, fFlags).release();
     }
     if (!fTarget) {
-        return nullptr;
+        return false;
     }
 
-    fTarget->asTexture()->texturePriv().setMipColorMode(fMipColorMode);
+    fTarget->asTexture()->texturePriv().setMipColorMode(mipColorMode);
     this->INHERITED::transferRefs();
 
 #ifdef SK_DEBUG
@@ -61,51 +76,18 @@
     }
 #endif
 
-    return fTarget;
-}
-
-int GrSurfaceProxy::worstCaseWidth(const GrCaps& caps) const {
-    if (fTarget) {
-        return fTarget->width();
-    }
-
-    if (SkBackingFit::kExact == fFit) {
-        return fDesc.fWidth;
-    }
-
-    if (caps.reuseScratchTextures() || fDesc.fFlags & kRenderTarget_GrSurfaceFlag) {
-        return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fDesc.fWidth));
-    }
-
-    return fDesc.fWidth;
-}
-
-int GrSurfaceProxy::worstCaseHeight(const GrCaps& caps) const {
-    if (fTarget) {
-        return fTarget->height();
-    }
-
-    if (SkBackingFit::kExact == fFit) {
-        return fDesc.fHeight;
-    }
-
-    if (caps.reuseScratchTextures() || fDesc.fFlags & kRenderTarget_GrSurfaceFlag) {
-        return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fDesc.fHeight));
-    }
-
-    return fDesc.fHeight;
+    return true;
 }
 
 void GrSurfaceProxy::setLastOpList(GrOpList* opList) {
+#ifdef SK_DEBUG
     if (fLastOpList) {
-        // The non-MDB world never closes so we can't check this condition
-#ifdef ENABLE_MDB
         SkASSERT(fLastOpList->isClosed());
-#endif
-        fLastOpList->clearTarget();
     }
+#endif
 
-    SkRefCnt_SafeAssign(fLastOpList, opList);
+    // Un-reffed
+    fLastOpList = opList;
 }
 
 GrRenderTargetOpList* GrSurfaceProxy::getLastRenderTargetOpList() {
@@ -147,8 +129,6 @@
     }
 }
 
-#include "GrResourceProvider.h"
-
 sk_sp<GrTextureProxy> GrSurfaceProxy::MakeDeferred(GrResourceProvider* resourceProvider,
                                                    const GrSurfaceDesc& desc,
                                                    SkBackingFit fit,
@@ -160,18 +140,6 @@
 
     // TODO: move this logic into GrResourceProvider!
     // TODO: share this testing code with check_texture_creation_params
-    if (GrPixelConfigIsCompressed(desc.fConfig)) {
-        if (SkBackingFit::kApprox == fit || kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
-            // We don't allow scratch compressed textures and, apparently can't Y-flip compressed
-            // textures
-            return nullptr;
-        }
-
-        if (!caps->npotTextureTileSupport() && (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
-            return nullptr;
-        }
-    }
-
     if (!caps->isConfigTexturable(desc.fConfig)) {
         return nullptr;
     }
@@ -193,13 +161,28 @@
         maxSize = caps->maxTextureSize();
     }
 
-    if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
+    if (desc.fWidth > maxSize || desc.fHeight > maxSize || desc.fWidth <= 0 || desc.fHeight <= 0) {
         return nullptr;
     }
 
     GrSurfaceDesc copyDesc = desc;
     copyDesc.fSampleCnt = SkTMin(desc.fSampleCnt, caps->maxSampleCount());
 
+#ifdef SK_DISABLE_DEFERRED_PROXIES
+    sk_sp<GrTexture> tex;
+
+    if (SkBackingFit::kApprox == fit) {
+        tex = resourceProvider->createApproxTexture(copyDesc, flags);
+    } else {
+        tex = resourceProvider->createTexture(copyDesc, budgeted, flags);
+    }
+
+    if (!tex) {
+        return nullptr;
+    }
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+#else
     if (willBeRT) {
         // We know anything we instantiate later from this deferred path will be
         // both texturable and renderable
@@ -208,6 +191,7 @@
     }
 
     return sk_sp<GrTextureProxy>(new GrTextureProxy(copyDesc, fit, budgeted, nullptr, 0, flags));
+#endif
 }
 
 sk_sp<GrTextureProxy> GrSurfaceProxy::MakeDeferred(GrResourceProvider* resourceProvider,
@@ -216,17 +200,19 @@
                                                    const void* srcData,
                                                    size_t rowBytes) {
     if (srcData) {
-        // If we have srcData, for now, we create a wrapped GrTextureProxy
-        sk_sp<GrTexture> tex(resourceProvider->createTexture(desc, budgeted, srcData, rowBytes));
-        return GrSurfaceProxy::MakeWrapped(std::move(tex));
+        GrMipLevel mipLevel = { srcData, rowBytes };
+
+        return resourceProvider->createTextureProxy(desc, budgeted, mipLevel);
     }
 
     return GrSurfaceProxy::MakeDeferred(resourceProvider, desc, SkBackingFit::kExact, budgeted);
 }
 
-sk_sp<GrSurfaceProxy> GrSurfaceProxy::MakeWrappedBackend(GrContext* context,
-                                                         GrBackendTextureDesc& desc) {
-    sk_sp<GrTexture> tex(context->resourceProvider()->wrapBackendTexture(desc));
+sk_sp<GrTextureProxy> GrSurfaceProxy::MakeWrappedBackend(GrContext* context,
+                                                         GrBackendTexture& backendTex,
+                                                         GrSurfaceOrigin origin) {
+    sk_sp<GrTexture> tex(context->resourceProvider()->wrapBackendTexture(
+            backendTex, origin, kNone_GrBackendTextureFlag, 0));
     return GrSurfaceProxy::MakeWrapped(std::move(tex));
 }
 
@@ -248,9 +234,11 @@
         return nullptr;
     }
 
-    GrSurfaceDesc dstDesc = src->desc();
+    GrSurfaceDesc dstDesc;
+    dstDesc.fConfig = src->config();
     dstDesc.fWidth = srcRect.width();
     dstDesc.fHeight = srcRect.height();
+    dstDesc.fOrigin = src->origin();
 
     sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
                                                                             dstDesc,
@@ -303,8 +291,8 @@
         // obliterating the area of interest information. This call (exactify) only used 
         // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
         // used for additional draws.
-        fProxy->fDesc.fWidth = fProxy->fTarget->width();
-        fProxy->fDesc.fHeight = fProxy->fTarget->height();
+        fProxy->fWidth = fProxy->fTarget->width();
+        fProxy->fHeight = fProxy->fTarget->height();
         return;
     }
 
@@ -312,7 +300,7 @@
     // It could mess things up if prior decisions were based on the approximate size.
     fProxy->fFit = SkBackingFit::kExact;
     // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
-    // already been computed we want to leave it alone so that amount will be removed when 
+    // already been computed we want to leave it alone so that amount will be removed when
     // the special image goes away. If it hasn't been computed yet it might as well compute the
     // exact amount.
 }
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 64f1dc5..b10cb34 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -15,17 +15,34 @@
     data members or virtual methods. */
 class GrSurfaceProxyPriv {
 public:
+    // This should only be called after a successful call to instantiate
+    GrSurface* peekSurface() const {
+        SkASSERT(fProxy->fTarget);
+        return fProxy->fTarget;
+    }
+
     // If the proxy is already instantiated, return its backing GrTexture; if not,
     // return null
-    const GrTexture* peekTexture() const {
+    GrTexture* peekTexture() const {
         return fProxy->fTarget ? fProxy->fTarget->asTexture() : nullptr;
     }
 
+    // This should only be called after a successful call to instantiate
+    GrRenderTarget* peekRenderTarget() const {
+        SkASSERT(fProxy->fTarget && fProxy->fTarget->asRenderTarget());
+        return fProxy->fTarget ? fProxy->fTarget->asRenderTarget() : nullptr;
+    }
+
     // Beware! This call is only guaranteed to tell you if the proxy in question has
     // any pending IO in its current state. It won't tell you about the IO state in the
     // future when the proxy is actually used/instantiated.
     bool hasPendingIO() const { return fProxy->hasPendingIO(); }
 
+    // Beware! This call is only guaranteed to tell you if the proxy in question has
+    // any pending writes in its current state. It won't tell you about the IO state in the
+    // future when the proxy is actually used/instantiated.
+    bool hasPendingWrite() const { return fProxy->hasPendingWrite(); }
+
     // Don't abuse this call!!!!!!!
     bool isExact() const { return SkBackingFit::kExact == fProxy->fFit; }
 
diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp
index d7dd357..6e9665f 100644
--- a/src/gpu/GrTessellator.cpp
+++ b/src/gpu/GrTessellator.cpp
@@ -50,17 +50,15 @@
  * line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
  * not exact and may violate the mesh topology or active edge list ordering. We
  * accommodate this by adjusting the topology of the mesh and AEL to match the intersection
- * points. This occurs in three ways:
+ * points. This occurs in two ways:
  *
  * A) Intersections may cause a shortened edge to no longer be ordered with respect to its
  *    neighbouring edges at the top or bottom vertex. This is handled by merging the
  *    edges (merge_collinear_edges()).
  * B) Intersections may cause an edge to violate the left-to-right ordering of the
- *    active edge list. This is handled by splitting the neighbour edge on the
- *    intersected vertex (cleanup_active_edges()).
- * C) Shortening an edge may cause an active edge to become inactive or an inactive edge
- *    to become active. This is handled by removing or inserting the edge in the active
- *    edge list (fix_active_state()).
+ *    active edge list. This is handled by detecting potential violati0ns and rewinding
+ *    the active edge list to the vertex before they occur (rewind() during merging,
+ *    rewind_if_necessary() during splitting).
  *
  * The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
  * Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
@@ -145,25 +143,26 @@
     : fPoint(point), fPrev(nullptr), fNext(nullptr)
     , fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr)
     , fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr)
+    , fLeftEnclosingEdge(nullptr), fRightEnclosingEdge(nullptr)
     , fPartner(nullptr)
-    , fProcessed(false)
     , fAlpha(alpha)
 #if LOGGING_ENABLED
     , fID (-1.0f)
 #endif
     {}
-    SkPoint fPoint;           // Vertex position
-    Vertex* fPrev;            // Linked list of contours, then Y-sorted vertices.
-    Vertex* fNext;            // "
-    Edge*   fFirstEdgeAbove;  // Linked list of edges above this vertex.
-    Edge*   fLastEdgeAbove;   // "
-    Edge*   fFirstEdgeBelow;  // Linked list of edges below this vertex.
-    Edge*   fLastEdgeBelow;   // "
-    Vertex* fPartner;         // Corresponding inner or outer vertex (for AA).
-    bool    fProcessed;       // Has this vertex been seen in simplify()?
+    SkPoint fPoint;               // Vertex position
+    Vertex* fPrev;                // Linked list of contours, then Y-sorted vertices.
+    Vertex* fNext;                // "
+    Edge*   fFirstEdgeAbove;      // Linked list of edges above this vertex.
+    Edge*   fLastEdgeAbove;       // "
+    Edge*   fFirstEdgeBelow;      // Linked list of edges below this vertex.
+    Edge*   fLastEdgeBelow;       // "
+    Edge*   fLeftEnclosingEdge;   // Nearest edge in the AEL left of this vertex.
+    Edge*   fRightEnclosingEdge;  // Nearest edge in the AEL right of this vertex.
+    Vertex* fPartner;             // Corresponding inner or outer vertex (for AA).
     uint8_t fAlpha;
 #if LOGGING_ENABLED
-    float   fID;              // Identifier used for logging.
+    float   fID;                  // Identifier used for logging.
 #endif
 };
 
@@ -312,7 +311,7 @@
  * "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
  * Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
  * point). For speed, that case is only tested by the callers that require it (e.g.,
- * cleanup_active_edges()). Edges also handle checking for intersection with other edges.
+ * rewind_if_necessary()). Edges also handle checking for intersection with other edges.
  * Currently, this converts the edges to the parametric form, in order to avoid doing a division
  * until an intersection has been confirmed. This is slightly slower in the "found" case, but
  * a lot faster in the "not found" case.
@@ -628,28 +627,35 @@
     contour->append(v);
 }
 
-void generate_quadratic_points(const SkPoint& p0,
-                               const SkPoint& p1,
-                               const SkPoint& p2,
-                               SkScalar tolSqd,
-                               VertexList* contour,
-                               int pointsLeft,
-                               SkArenaAlloc& alloc) {
-    SkScalar d = p1.distanceToLineSegmentBetweenSqd(p0, p2);
-    if (pointsLeft < 2 || d < tolSqd || !SkScalarIsFinite(d)) {
-        append_point_to_contour(p2, contour, alloc);
-        return;
+SkScalar quad_error_at(const SkPoint pts[3], SkScalar t, SkScalar u) {
+    SkQuadCoeff quad(pts);
+    SkPoint p0 = to_point(quad.eval(t - 0.5f * u));
+    SkPoint mid = to_point(quad.eval(t));
+    SkPoint p1 = to_point(quad.eval(t + 0.5f * u));
+    return mid.distanceToLineSegmentBetweenSqd(p0, p1);
+}
+
+void append_quadratic_to_contour(const SkPoint pts[3], SkScalar toleranceSqd, VertexList* contour,
+                                 SkArenaAlloc& alloc) {
+    SkQuadCoeff quad(pts);
+    Sk2s aa = quad.fA * quad.fA;
+    SkScalar denom = 2.0f * (aa[0] + aa[1]);
+    Sk2s ab = quad.fA * quad.fB;
+    SkScalar t = denom ? (-ab[0] - ab[1]) / denom : 0.0f;
+    int nPoints = 1;
+    SkScalar u;
+    // Test possible subdivision values only at the point of maximum curvature.
+    // If it passes the flatness metric there, it'll pass everywhere.
+    for (;;) {
+        u = 1.0f / nPoints;
+        if (quad_error_at(pts, t, u) < toleranceSqd) {
+            break;
+        }
+        nPoints++;
     }
-
-    const SkPoint q[] = {
-        { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
-        { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
-    };
-    const SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) };
-
-    pointsLeft >>= 1;
-    generate_quadratic_points(p0, q[0], r, tolSqd, contour, pointsLeft, alloc);
-    generate_quadratic_points(r, q[1], p2, tolSqd, contour, pointsLeft, alloc);
+    for (int j = 1; j <= nPoints; j++) {
+        append_point_to_contour(to_point(quad.eval(j * u)), contour, alloc);
+    }
 }
 
 void generate_cubic_points(const SkPoint& p0,
@@ -708,9 +714,7 @@
                 SkScalar weight = iter.conicWeight();
                 const SkPoint* quadPts = converter.computeQuads(pts, weight, toleranceSqd);
                 for (int i = 0; i < converter.countQuads(); ++i) {
-                    int pointsLeft = GrPathUtils::quadraticPointCount(quadPts, tolerance);
-                    generate_quadratic_points(quadPts[0], quadPts[1], quadPts[2], toleranceSqd,
-                                              contour, pointsLeft, alloc);
+                    append_quadratic_to_contour(quadPts, toleranceSqd, contour, alloc);
                     quadPts += 2;
                 }
                 *isLinear = false;
@@ -727,9 +731,7 @@
                 break;
             }
             case SkPath::kQuad_Verb: {
-                int pointsLeft = GrPathUtils::quadraticPointCount(pts, tolerance);
-                generate_quadratic_points(pts[0], pts[1], pts[2], toleranceSqd, contour,
-                                          pointsLeft, alloc);
+                append_quadratic_to_contour(pts, toleranceSqd, contour, alloc);
                 *isLinear = false;
                 break;
             }
@@ -805,40 +807,6 @@
     *right = next;
 }
 
-void find_enclosing_edges(Edge* edge, EdgeList* edges, Comparator& c, Edge** left, Edge** right) {
-    Edge* prev = nullptr;
-    Edge* next;
-    for (next = edges->fHead; next != nullptr; next = next->fRight) {
-        if ((c.sweep_lt(next->fTop->fPoint, edge->fTop->fPoint) && next->isRightOf(edge->fTop)) ||
-            (c.sweep_lt(edge->fTop->fPoint, next->fTop->fPoint) && edge->isLeftOf(next->fTop)) ||
-            (c.sweep_lt(edge->fBottom->fPoint, next->fBottom->fPoint) &&
-             next->isRightOf(edge->fBottom)) ||
-            (c.sweep_lt(next->fBottom->fPoint, edge->fBottom->fPoint) &&
-             edge->isLeftOf(next->fBottom))) {
-            break;
-        }
-        prev = next;
-    }
-    *left = prev;
-    *right = next;
-}
-
-void fix_active_state(Edge* edge, EdgeList* activeEdges, Comparator& c) {
-    if (!activeEdges) {
-        return;
-    }
-    if (activeEdges->contains(edge)) {
-        if (edge->fBottom->fProcessed || !edge->fTop->fProcessed) {
-            remove_edge(edge, activeEdges);
-        }
-    } else if (edge->fTop->fProcessed && !edge->fBottom->fProcessed) {
-        Edge* left;
-        Edge* right;
-        find_enclosing_edges(edge, activeEdges, c, &left, &right);
-        insert_edge(edge, left, activeEdges);
-    }
-}
-
 void insert_edge_above(Edge* edge, Vertex* v, Comparator& c) {
     if (edge->fTop->fPoint == edge->fBottom->fPoint ||
         c.sweep_lt(edge->fBottom->fPoint, edge->fTop->fPoint)) {
@@ -895,136 +863,171 @@
     remove_edge_below(edge);
 }
 
-void erase_edge(Edge* edge, EdgeList* edges) {
-    LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID);
-    disconnect(edge);
-    if (edges && edges->contains(edge)) {
-        remove_edge(edge, edges);
+void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Vertex** current, Comparator& c);
+
+void rewind(EdgeList* activeEdges, Vertex** current, Vertex* dst, Comparator& c) {
+    if (!current || *current == dst || c.sweep_lt((*current)->fPoint, dst->fPoint)) {
+        return;
     }
-}
-
-void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c);
-
-void set_top(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) {
-    remove_edge_below(edge);
-    edge->fTop = v;
-    edge->recompute();
-    insert_edge_below(edge, v, c);
-    fix_active_state(edge, activeEdges, c);
-    merge_collinear_edges(edge, activeEdges, c);
-}
-
-void set_bottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) {
-    remove_edge_above(edge);
-    edge->fBottom = v;
-    edge->recompute();
-    insert_edge_above(edge, v, c);
-    fix_active_state(edge, activeEdges, c);
-    merge_collinear_edges(edge, activeEdges, c);
-}
-
-void merge_edges_above(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) {
-    if (coincident(edge->fTop->fPoint, other->fTop->fPoint)) {
-        LOG("merging coincident above edges (%g, %g) -> (%g, %g)\n",
-            edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
-            edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
-        other->fWinding += edge->fWinding;
-        erase_edge(edge, activeEdges);
-    } else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
-        other->fWinding += edge->fWinding;
-        set_bottom(edge, other->fTop, activeEdges, c);
-    } else {
-        edge->fWinding += other->fWinding;
-        set_bottom(other, edge->fTop, activeEdges, c);
+    Vertex* v = *current;
+    LOG("rewinding active edges from vertex %g to vertex %g\n", v->fID, dst->fID);
+    while (v != dst) {
+        v = v->fPrev;
+        for (Edge* e = v->fFirstEdgeBelow; e; e = e->fNextEdgeBelow) {
+            remove_edge(e, activeEdges);
+        }
+        Edge* leftEdge = v->fLeftEnclosingEdge;
+        for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
+            insert_edge(e, leftEdge, activeEdges);
+            leftEdge = e;
+        }
     }
+    *current = v;
 }
 
-void merge_edges_below(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) {
-    if (coincident(edge->fBottom->fPoint, other->fBottom->fPoint)) {
-        LOG("merging coincident below edges (%g, %g) -> (%g, %g)\n",
-            edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
-            edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
-        other->fWinding += edge->fWinding;
-        erase_edge(edge, activeEdges);
-    } else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
-        edge->fWinding += other->fWinding;
-        set_top(other, edge->fBottom, activeEdges, c);
-    } else {
-        other->fWinding += edge->fWinding;
-        set_top(edge, other->fBottom, activeEdges, c);
+void rewind_if_necessary(Edge* edge, EdgeList* activeEdges, Vertex** current, Comparator& c) {
+    if (!activeEdges || !current) {
+        return;
     }
-}
-
-void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c) {
-    if (edge->fPrevEdgeAbove && (edge->fTop == edge->fPrevEdgeAbove->fTop ||
-                                 !edge->fPrevEdgeAbove->isLeftOf(edge->fTop))) {
-        merge_edges_above(edge, edge->fPrevEdgeAbove, activeEdges, c);
-    } else if (edge->fNextEdgeAbove && (edge->fTop == edge->fNextEdgeAbove->fTop ||
-                                        !edge->isLeftOf(edge->fNextEdgeAbove->fTop))) {
-        merge_edges_above(edge, edge->fNextEdgeAbove, activeEdges, c);
-    }
-    if (edge->fPrevEdgeBelow && (edge->fBottom == edge->fPrevEdgeBelow->fBottom ||
-                                 !edge->fPrevEdgeBelow->isLeftOf(edge->fBottom))) {
-        merge_edges_below(edge, edge->fPrevEdgeBelow, activeEdges, c);
-    } else if (edge->fNextEdgeBelow && (edge->fBottom == edge->fNextEdgeBelow->fBottom ||
-                                        !edge->isLeftOf(edge->fNextEdgeBelow->fBottom))) {
-        merge_edges_below(edge, edge->fNextEdgeBelow, activeEdges, c);
-    }
-}
-
-void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkArenaAlloc& alloc);
-
-void cleanup_active_edges(Edge* edge, EdgeList* activeEdges, Comparator& c, SkArenaAlloc& alloc) {
     Vertex* top = edge->fTop;
     Vertex* bottom = edge->fBottom;
     if (edge->fLeft) {
         Vertex* leftTop = edge->fLeft->fTop;
         Vertex* leftBottom = edge->fLeft->fBottom;
         if (c.sweep_lt(leftTop->fPoint, top->fPoint) && !edge->fLeft->isLeftOf(top)) {
-            split_edge(edge->fLeft, edge->fTop, activeEdges, c, alloc);
+            rewind(activeEdges, current, leftTop, c);
         } else if (c.sweep_lt(top->fPoint, leftTop->fPoint) && !edge->isRightOf(leftTop)) {
-            split_edge(edge, leftTop, activeEdges, c, alloc);
+            rewind(activeEdges, current, top, c);
         } else if (c.sweep_lt(bottom->fPoint, leftBottom->fPoint) &&
                    !edge->fLeft->isLeftOf(bottom)) {
-            split_edge(edge->fLeft, bottom, activeEdges, c, alloc);
+            rewind(activeEdges, current, leftTop, c);
         } else if (c.sweep_lt(leftBottom->fPoint, bottom->fPoint) && !edge->isRightOf(leftBottom)) {
-            split_edge(edge, leftBottom, activeEdges, c, alloc);
+            rewind(activeEdges, current, top, c);
         }
     }
     if (edge->fRight) {
         Vertex* rightTop = edge->fRight->fTop;
         Vertex* rightBottom = edge->fRight->fBottom;
         if (c.sweep_lt(rightTop->fPoint, top->fPoint) && !edge->fRight->isRightOf(top)) {
-            split_edge(edge->fRight, top, activeEdges, c, alloc);
+            rewind(activeEdges, current, rightTop, c);
         } else if (c.sweep_lt(top->fPoint, rightTop->fPoint) && !edge->isLeftOf(rightTop)) {
-            split_edge(edge, rightTop, activeEdges, c, alloc);
+            rewind(activeEdges, current, top, c);
         } else if (c.sweep_lt(bottom->fPoint, rightBottom->fPoint) &&
                    !edge->fRight->isRightOf(bottom)) {
-            split_edge(edge->fRight, bottom, activeEdges, c, alloc);
+            rewind(activeEdges, current, rightTop, c);
         } else if (c.sweep_lt(rightBottom->fPoint, bottom->fPoint) &&
                    !edge->isLeftOf(rightBottom)) {
-            split_edge(edge, rightBottom, activeEdges, c, alloc);
+            rewind(activeEdges, current, top, c);
         }
     }
 }
 
-void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkArenaAlloc& alloc) {
+void set_top(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, Comparator& c) {
+    remove_edge_below(edge);
+    edge->fTop = v;
+    edge->recompute();
+    insert_edge_below(edge, v, c);
+    rewind_if_necessary(edge, activeEdges, current, c);
+    merge_collinear_edges(edge, activeEdges, current, c);
+}
+
+void set_bottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, Comparator& c) {
+    remove_edge_above(edge);
+    edge->fBottom = v;
+    edge->recompute();
+    insert_edge_above(edge, v, c);
+    rewind_if_necessary(edge, activeEdges, current, c);
+    merge_collinear_edges(edge, activeEdges, current, c);
+}
+
+void merge_edges_above(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
+                       Comparator& c) {
+    if (coincident(edge->fTop->fPoint, other->fTop->fPoint)) {
+        LOG("merging coincident above edges (%g, %g) -> (%g, %g)\n",
+            edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
+            edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
+        rewind(activeEdges, current, edge->fTop, c);
+        other->fWinding += edge->fWinding;
+        disconnect(edge);
+    } else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
+        rewind(activeEdges, current, edge->fTop, c);
+        other->fWinding += edge->fWinding;
+        set_bottom(edge, other->fTop, activeEdges, current, c);
+    } else {
+        rewind(activeEdges, current, other->fTop, c);
+        edge->fWinding += other->fWinding;
+        set_bottom(other, edge->fTop, activeEdges, current, c);
+    }
+}
+
+void merge_edges_below(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
+                       Comparator& c) {
+    if (coincident(edge->fBottom->fPoint, other->fBottom->fPoint)) {
+        LOG("merging coincident below edges (%g, %g) -> (%g, %g)\n",
+            edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
+            edge->fBottom->fPoint.fX, edge->fBottom->fPoint.fY);
+        rewind(activeEdges, current, edge->fTop, c);
+        other->fWinding += edge->fWinding;
+        disconnect(edge);
+    } else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
+        rewind(activeEdges, current, other->fTop, c);
+        edge->fWinding += other->fWinding;
+        set_top(other, edge->fBottom, activeEdges, current, c);
+    } else {
+        rewind(activeEdges, current, edge->fTop, c);
+        other->fWinding += edge->fWinding;
+        set_top(edge, other->fBottom, activeEdges, current, c);
+    }
+}
+
+void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Vertex** current, Comparator& c) {
+    for (;;) {
+        if (edge->fPrevEdgeAbove && (edge->fTop == edge->fPrevEdgeAbove->fTop ||
+                                     !edge->fPrevEdgeAbove->isLeftOf(edge->fTop))) {
+            merge_edges_above(edge, edge->fPrevEdgeAbove, activeEdges, current, c);
+        } else if (edge->fNextEdgeAbove && (edge->fTop == edge->fNextEdgeAbove->fTop ||
+                                            !edge->isLeftOf(edge->fNextEdgeAbove->fTop))) {
+            merge_edges_above(edge, edge->fNextEdgeAbove, activeEdges, current, c);
+        } else if (edge->fPrevEdgeBelow && (edge->fBottom == edge->fPrevEdgeBelow->fBottom ||
+                                     !edge->fPrevEdgeBelow->isLeftOf(edge->fBottom))) {
+            merge_edges_below(edge, edge->fPrevEdgeBelow, activeEdges, current, c);
+        } else if (edge->fNextEdgeBelow && (edge->fBottom == edge->fNextEdgeBelow->fBottom ||
+                                            !edge->isLeftOf(edge->fNextEdgeBelow->fBottom))) {
+            merge_edges_below(edge, edge->fNextEdgeBelow, activeEdges, current, c);
+        } else {
+            break;
+        }
+    }
+    SkASSERT(!edge->fPrevEdgeAbove || edge->fPrevEdgeAbove->isLeftOf(edge->fTop));
+    SkASSERT(!edge->fPrevEdgeBelow || edge->fPrevEdgeBelow->isLeftOf(edge->fBottom));
+    SkASSERT(!edge->fNextEdgeAbove || edge->fNextEdgeAbove->isRightOf(edge->fTop));
+    SkASSERT(!edge->fNextEdgeBelow || edge->fNextEdgeBelow->isRightOf(edge->fBottom));
+}
+
+void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, Comparator& c,
+                SkArenaAlloc& alloc) {
     LOG("splitting edge (%g -> %g) at vertex %g (%g, %g)\n",
         edge->fTop->fID, edge->fBottom->fID,
         v->fID, v->fPoint.fX, v->fPoint.fY);
+    Vertex* top;
+    Vertex* bottom;
     if (c.sweep_lt(v->fPoint, edge->fTop->fPoint)) {
-        set_top(edge, v, activeEdges, c);
+        top = v;
+        bottom = edge->fTop;
+        set_top(edge, v, activeEdges, current, c);
     } else if (c.sweep_lt(edge->fBottom->fPoint, v->fPoint)) {
-        set_bottom(edge, v, activeEdges, c);
+        top = edge->fBottom;
+        bottom = v;
+        set_bottom(edge, v, activeEdges, current, c);
     } else {
-        Edge* newEdge = alloc.make<Edge>(v, edge->fBottom, edge->fWinding, edge->fType);
-        insert_edge_below(newEdge, v, c);
-        insert_edge_above(newEdge, edge->fBottom, c);
-        set_bottom(edge, v, activeEdges, c);
-        cleanup_active_edges(edge, activeEdges, c, alloc);
-        fix_active_state(newEdge, activeEdges, c);
-        merge_collinear_edges(newEdge, activeEdges, c);
+        top = v;
+        bottom = edge->fBottom;
+        set_bottom(edge, v, activeEdges, current, c);
     }
+    Edge* newEdge = alloc.make<Edge>(top, bottom, edge->fWinding, edge->fType);
+    insert_edge_below(newEdge, top, c);
+    insert_edge_above(newEdge, bottom, c);
+    merge_collinear_edges(newEdge, activeEdges, current, c);
 }
 
 Edge* connect(Vertex* prev, Vertex* next, Edge::Type type, Comparator& c, SkArenaAlloc& alloc,
@@ -1033,7 +1036,7 @@
     insert_edge_below(edge, edge->fTop, c);
     insert_edge_above(edge, edge->fBottom, c);
     edge->fWinding *= winding_scale;
-    merge_collinear_edges(edge, nullptr, c);
+    merge_collinear_edges(edge, nullptr, nullptr, c);
     return edge;
 }
 
@@ -1047,12 +1050,12 @@
     }
     for (Edge* edge = src->fFirstEdgeAbove; edge;) {
         Edge* next = edge->fNextEdgeAbove;
-        set_bottom(edge, dst, nullptr, c);
+        set_bottom(edge, dst, nullptr, nullptr, c);
         edge = next;
     }
     for (Edge* edge = src->fFirstEdgeBelow; edge;) {
         Edge* next = edge->fNextEdgeBelow;
-        set_top(edge, dst, nullptr, c);
+        set_top(edge, dst, nullptr, nullptr, c);
         edge = next;
     }
     mesh->remove(src);
@@ -1069,61 +1072,69 @@
     }
 }
 
-Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c,
-                               SkArenaAlloc& alloc) {
+bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
+                            Comparator& c, SkArenaAlloc& alloc) {
     if (!edge || !other) {
-        return nullptr;
+        return false;
     }
     SkPoint p;
     uint8_t alpha;
     if (edge->intersect(*other, &p, &alpha)) {
         Vertex* v;
         LOG("found intersection, pt is %g, %g\n", p.fX, p.fY);
-        if (p == edge->fTop->fPoint || c.sweep_lt(p, edge->fTop->fPoint)) {
-            split_edge(other, edge->fTop, activeEdges, c, alloc);
+        Vertex* top = *current;
+        // If the intersection point is above the current vertex, rewind to the vertex above the
+        // intersection.
+        while (c.sweep_lt(p, top->fPoint) && top->fPrev) {
+            top = top->fPrev;
+        }
+        rewind(activeEdges, current, top, c);
+        if (p == edge->fTop->fPoint) {
+            split_edge(other, edge->fTop, activeEdges, current, c, alloc);
             v = edge->fTop;
-        } else if (p == edge->fBottom->fPoint || c.sweep_lt(edge->fBottom->fPoint, p)) {
-            split_edge(other, edge->fBottom, activeEdges, c, alloc);
+        } else if (p == edge->fBottom->fPoint) {
+            split_edge(other, edge->fBottom, activeEdges, current, c, alloc);
             v = edge->fBottom;
-        } else if (p == other->fTop->fPoint || c.sweep_lt(p, other->fTop->fPoint)) {
-            split_edge(edge, other->fTop, activeEdges, c, alloc);
+        } else if (p == other->fTop->fPoint) {
+            split_edge(edge, other->fTop, activeEdges, current, c, alloc);
             v = other->fTop;
-        } else if (p == other->fBottom->fPoint || c.sweep_lt(other->fBottom->fPoint, p)) {
-            split_edge(edge, other->fBottom, activeEdges, c, alloc);
+        } else if (p == other->fBottom->fPoint) {
+            split_edge(edge, other->fBottom, activeEdges, current, c, alloc);
             v = other->fBottom;
         } else {
-            Vertex* nextV = edge->fTop;
-            while (c.sweep_lt(p, nextV->fPoint)) {
-                nextV = nextV->fPrev;
-            }
-            while (c.sweep_lt(nextV->fPoint, p)) {
+            Vertex* prevV = top;
+            Vertex* nextV = top->fNext;
+            while (nextV && c.sweep_lt(nextV->fPoint, p)) {
+                prevV = nextV;
                 nextV = nextV->fNext;
             }
-            Vertex* prevV = nextV->fPrev;
-            if (coincident(prevV->fPoint, p)) {
+            if (prevV && coincident(prevV->fPoint, p)) {
                 v = prevV;
-            } else if (coincident(nextV->fPoint, p)) {
+            } else if (nextV && coincident(nextV->fPoint, p)) {
                 v = nextV;
             } else {
                 v = alloc.make<Vertex>(p, alpha);
-                LOG("inserting between %g (%g, %g) and %g (%g, %g)\n",
-                    prevV->fID, prevV->fPoint.fX, prevV->fPoint.fY,
-                    nextV->fID, nextV->fPoint.fX, nextV->fPoint.fY);
 #if LOGGING_ENABLED
-                v->fID = (nextV->fID + prevV->fID) * 0.5f;
+                float prevID = prevV ? prevV->fID : 0.0f;
+                float nextID = nextV ? nextV->fID : prevV->fID + 1.0f;
+                v->fID = (prevID + nextID) * 0.5f;
 #endif
                 v->fPrev = prevV;
                 v->fNext = nextV;
-                prevV->fNext = v;
-                nextV->fPrev = v;
+                if (prevV) {
+                    prevV->fNext = v;
+                }
+                if (nextV) {
+                    nextV->fPrev = v;
+                }
             }
-            split_edge(edge, v, activeEdges, c, alloc);
-            split_edge(other, v, activeEdges, c, alloc);
+            split_edge(edge, v, activeEdges, current, c, alloc);
+            split_edge(other, v, activeEdges, current, c, alloc);
         }
         v->fAlpha = SkTMax(v->fAlpha, alpha);
-        return v;
+        return true;
     }
-    return nullptr;
+    return false;
 }
 
 void sanitize_contours(VertexList* contours, int contourCnt, bool approximate) {
@@ -1214,6 +1225,12 @@
     } else {
         sorted_merge<sweep_lt_vert>(front, back, result);
     }
+#if LOGGING_ENABLED
+    float id = 0.0f;
+    for (Vertex* v = result->fHead; v; v = v->fNext) {
+        v->fID = id++;
+    }
+#endif
 }
 
 // Stage 3: sort the vertices by increasing sweep direction.
@@ -1255,32 +1272,37 @@
         if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) {
             continue;
         }
-#if LOGGING_ENABLED
-        LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
-#endif
         Edge* leftEnclosingEdge;
         Edge* rightEnclosingEdge;
         bool restartChecks;
         do {
+            LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
             restartChecks = false;
             find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
+            if (rightEnclosingEdge && !rightEnclosingEdge->isRightOf(v)) {
+                split_edge(rightEnclosingEdge, v, &activeEdges, &v, c, alloc);
+                restartChecks = true;
+                continue;
+            }
+            SkASSERT(!rightEnclosingEdge || rightEnclosingEdge->isRightOf(v));
+            v->fLeftEnclosingEdge = leftEnclosingEdge;
+            v->fRightEnclosingEdge = rightEnclosingEdge;
             if (v->fFirstEdgeBelow) {
                 for (Edge* edge = v->fFirstEdgeBelow; edge; edge = edge->fNextEdgeBelow) {
-                    if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, c, alloc)) {
+                    if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, &v, c,
+                                               alloc)) {
                         restartChecks = true;
                         break;
                     }
-                    if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, c, alloc)) {
+                    if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, &v, c,
+                                               alloc)) {
                         restartChecks = true;
                         break;
                     }
                 }
             } else {
-                if (Vertex* pv = check_for_intersection(leftEnclosingEdge, rightEnclosingEdge,
-                                                        &activeEdges, c, alloc)) {
-                    if (c.sweep_lt(pv->fPoint, v->fPoint)) {
-                        v = pv;
-                    }
+                if (check_for_intersection(leftEnclosingEdge, rightEnclosingEdge,
+                                           &activeEdges, &v, c, alloc)) {
                     restartChecks = true;
                 }
 
@@ -1300,7 +1322,6 @@
             insert_edge(e, leftEdge, &activeEdges);
             leftEdge = e;
         }
-        v->fProcessed = true;
     }
 }
 
@@ -1392,7 +1413,6 @@
             }
             for (Edge* e = v->fFirstEdgeAbove; e != v->fLastEdgeAbove; e = e->fNextEdgeAbove) {
                 Edge* rightEdge = e->fNextEdgeAbove;
-                SkASSERT(rightEdge->isRightOf(e->fTop));
                 remove_edge(e, &activeEdges);
                 if (e->fRightPoly) {
                     e->fRightPoly->addEdge(e, Poly::kLeft_Side, alloc);
@@ -1826,12 +1846,12 @@
                                 isLinear, &outerMesh);
     SkPath::FillType fillType = antialias ? SkPath::kWinding_FillType : path.getFillType();
     int count = count_points(polys, fillType);
-    if (0 == count) {
-        return 0;
-    }
     if (antialias) {
         count += count_outer_mesh_points(outerMesh);
     }
+    if (0 == count) {
+        return 0;
+    }
 
     void* verts = vertexAllocator->lock(count);
     if (!verts) {
diff --git a/src/gpu/GrTestUtils.h b/src/gpu/GrTestUtils.h
new file mode 100644
index 0000000..fd9398d
--- /dev/null
+++ b/src/gpu/GrTestUtils.h
@@ -0,0 +1,150 @@
+/*
+ * 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 GrTestUtils_DEFINED
+#define GrTestUtils_DEFINED
+
+#include "SkTypes.h"
+
+#if GR_TEST_UTILS
+
+#include "GrColor.h"
+#include "GrColorSpaceXform.h"
+#include "SkPathEffect.h"
+#include "SkRandom.h"
+#include "SkShaderBase.h"
+#include "SkStrokeRec.h"
+#include "../private/SkTemplates.h"
+
+struct GrProcessorTestData;
+class GrStyle;
+class SkMatrix;
+class SkPath;
+class SkRRect;
+struct SkRect;
+
+namespace GrTest {
+/**
+ * Helpers for use in Test functions.
+ */
+const SkMatrix& TestMatrix(SkRandom*);
+const SkMatrix& TestMatrixPreservesRightAngles(SkRandom*);
+const SkMatrix& TestMatrixRectStaysRect(SkRandom*);
+const SkMatrix& TestMatrixInvertible(SkRandom*);
+const SkMatrix& TestMatrixPerspective(SkRandom*);
+const SkRect& TestRect(SkRandom*);
+const SkRect& TestSquare(SkRandom*);
+const SkRRect& TestRRectSimple(SkRandom*);
+const SkPath& TestPath(SkRandom*);
+const SkPath& TestPathConvex(SkRandom*);
+SkStrokeRec TestStrokeRec(SkRandom*);
+/** Creates styles with dash path effects and null path effects */
+void TestStyle(SkRandom*, GrStyle*);
+sk_sp<SkColorSpace> TestColorSpace(SkRandom*);
+sk_sp<GrColorSpaceXform> TestColorXform(SkRandom*);
+
+class TestAsFPArgs {
+public:
+    TestAsFPArgs(GrProcessorTestData*);
+    const SkShaderBase::AsFPArgs& args() const { return fArgs; }
+
+private:
+    SkShaderBase::AsFPArgs fArgs;
+    SkMatrix fViewMatrixStorage;
+    sk_sp<SkColorSpace> fColorSpaceStorage;
+};
+
+// We have a simplified dash path effect here to avoid relying on SkDashPathEffect which
+// is in the optional build target effects.
+class TestDashPathEffect : public SkPathEffect {
+public:
+    static sk_sp<SkPathEffect> Make(const SkScalar* intervals, int count, SkScalar phase) {
+        return sk_sp<SkPathEffect>(new TestDashPathEffect(intervals, count, phase));
+    }
+
+    bool filterPath(SkPath* dst, const SkPath&, SkStrokeRec* , const SkRect*) const override;
+    DashType asADash(DashInfo* info) const override;
+    Factory getFactory() const override { return nullptr; }
+    void toString(SkString*) const override {}
+
+private:
+    TestDashPathEffect(const SkScalar* intervals, int count, SkScalar phase);
+
+    int                     fCount;
+    SkAutoTArray<SkScalar>  fIntervals;
+    SkScalar                fPhase;
+    SkScalar                fInitialDashLength;
+    int                     fInitialDashIndex;
+    SkScalar                fIntervalLength;
+};
+
+}  // namespace GrTest
+
+static inline GrColor GrRandomColor(SkRandom* random) {
+    // There are only a few cases of random colors which interest us
+    enum ColorMode {
+        kAllOnes_ColorMode,
+        kAllZeros_ColorMode,
+        kAlphaOne_ColorMode,
+        kRandom_ColorMode,
+        kLast_ColorMode = kRandom_ColorMode
+    };
+
+    ColorMode colorMode = ColorMode(random->nextULessThan(kLast_ColorMode + 1));
+    GrColor color SK_INIT_TO_AVOID_WARNING;
+    switch (colorMode) {
+        case kAllOnes_ColorMode:
+            color = GrColorPackRGBA(0xFF, 0xFF, 0xFF, 0xFF);
+            break;
+        case kAllZeros_ColorMode:
+            color = GrColorPackRGBA(0, 0, 0, 0);
+            break;
+        case kAlphaOne_ColorMode:
+            color = GrColorPackRGBA(random->nextULessThan(256),
+                                    random->nextULessThan(256),
+                                    random->nextULessThan(256),
+                                    0xFF);
+            break;
+        case kRandom_ColorMode: {
+                uint8_t alpha = random->nextULessThan(256);
+                color = GrColorPackRGBA(random->nextRangeU(0, alpha),
+                                        random->nextRangeU(0, alpha),
+                                        random->nextRangeU(0, alpha),
+                                        alpha);
+            break;
+        }
+    }
+    GrColorIsPMAssert(color);
+    return color;
+}
+
+static inline uint8_t GrRandomCoverage(SkRandom* random) {
+    enum CoverageMode {
+        kZero_CoverageMode,
+        kAllOnes_CoverageMode,
+        kRandom_CoverageMode,
+        kLast_CoverageMode = kRandom_CoverageMode
+    };
+
+    CoverageMode colorMode = CoverageMode(random->nextULessThan(kLast_CoverageMode + 1));
+    uint8_t coverage SK_INIT_TO_AVOID_WARNING;
+    switch (colorMode) {
+        case kZero_CoverageMode:
+            coverage = 0;
+            break;
+        case kAllOnes_CoverageMode:
+            coverage = 0xff;
+            break;
+        case kRandom_CoverageMode:
+            coverage = random->nextULessThan(256);
+            break;
+    }
+    return coverage;
+}
+
+#endif
+#endif
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index ea0ed76..064cc0a 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -36,21 +36,11 @@
 }
 
 size_t GrTexture::onGpuMemorySize() const {
-    return GrSurface::ComputeSize(fDesc, 1, this->texturePriv().hasMipMaps());
+    return GrSurface::ComputeSize(this->config(), this->width(), this->height(), 1,
+                                  this->texturePriv().hasMipMaps(), false);
 }
 
-void GrTexture::validateDesc() const {
-    if (this->asRenderTarget()) {
-        // This texture has a render target
-        SkASSERT(0 != (fDesc.fFlags & kRenderTarget_GrSurfaceFlag));
-        SkASSERT(fDesc.fSampleCnt == this->asRenderTarget()->numColorSamples());
-    } else {
-        SkASSERT(0 == (fDesc.fFlags & kRenderTarget_GrSurfaceFlag));
-        SkASSERT(0 == fDesc.fSampleCnt);
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
 
 namespace {
 
@@ -78,7 +68,7 @@
     , fMipColorMode(SkDestinationSurfaceColorMode::kLegacy) {
     if (wasMipMapDataProvided) {
         fMipMapsStatus = kValid_MipMapsStatus;
-        fMaxMipMapLevel = SkMipMap::ComputeLevelCount(fDesc.fWidth, fDesc.fHeight);
+        fMaxMipMapLevel = SkMipMap::ComputeLevelCount(this->width(), this->height());
     } else {
         fMipMapsStatus = kNotAllocated_MipMapsStatus;
         fMaxMipMapLevel = 0;
@@ -86,27 +76,40 @@
 }
 
 void GrTexture::computeScratchKey(GrScratchKey* key) const {
-    if (!GrPixelConfigIsCompressed(fDesc.fConfig)) {
-        GrTexturePriv::ComputeScratchKey(fDesc, key);
+    const GrRenderTarget* rt = this->asRenderTarget();
+    int sampleCount = 0;
+    if (rt) {
+        sampleCount = rt->numStencilSamples();
     }
+    GrTexturePriv::ComputeScratchKey(this->config(), this->width(), this->height(),
+                                     this->origin(), SkToBool(rt), sampleCount,
+                                     this->texturePriv().hasMipMaps(), key);
 }
 
-void GrTexturePriv::ComputeScratchKey(const GrSurfaceDesc& desc, GrScratchKey* key) {
+void GrTexturePriv::ComputeScratchKey(GrPixelConfig config, int width, int height,
+                                      GrSurfaceOrigin origin, bool isRenderTarget, int sampleCnt,
+                                      bool isMipMapped, GrScratchKey* key) {
     static const GrScratchKey::ResourceType kType = GrScratchKey::GenerateResourceType();
+    uint32_t flags = isRenderTarget;
 
-    GrSurfaceOrigin origin = resolve_origin(desc);
-    uint32_t flags = desc.fFlags & ~kCheckAllocation_GrSurfaceFlag;
+    SkASSERT(0 == sampleCnt || isRenderTarget);
 
     // make sure desc.fConfig fits in 5 bits
     SkASSERT(sk_float_log2(kLast_GrPixelConfig) <= 5);
-    SkASSERT(static_cast<int>(desc.fConfig) < (1 << 5));
-    SkASSERT(desc.fSampleCnt < (1 << 8));
+    SkASSERT(static_cast<int>(config) < (1 << 5));
+    SkASSERT(sampleCnt < (1 << 8));
     SkASSERT(flags < (1 << 10));
     SkASSERT(static_cast<int>(origin) < (1 << 8));
 
     GrScratchKey::Builder builder(key, kType, 3);
-    builder[0] = desc.fWidth;
-    builder[1] = desc.fHeight;
-    builder[2] = desc.fConfig | (desc.fIsMipMapped << 5) | (desc.fSampleCnt << 6) | (flags << 14)
-                 | (origin << 24);
+    builder[0] = width;
+    builder[1] = height;
+    builder[2] = config | (isMipMapped << 5) | (sampleCnt << 6) | (flags << 14) | (origin << 24);
+}
+
+void GrTexturePriv::ComputeScratchKey(const GrSurfaceDesc& desc, GrScratchKey* key) {
+    GrSurfaceOrigin origin = resolve_origin(desc);
+    return ComputeScratchKey(desc.fConfig, desc.fWidth, desc.fHeight, origin,
+                             SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag), desc.fSampleCnt,
+                             desc.fIsMipMapped, key);
 }
diff --git a/src/gpu/GrTextureAdjuster.h b/src/gpu/GrTextureAdjuster.h
index 392af44..eb500f7 100644
--- a/src/gpu/GrTextureAdjuster.h
+++ b/src/gpu/GrTextureAdjuster.h
@@ -9,6 +9,7 @@
 #define GrTextureAdjuster_DEFINED
 
 #include "GrTextureProducer.h"
+#include "GrTextureProxy.h"
 #include "SkTLazy.h"
 
 /**
diff --git a/src/gpu/GrTextureContext.cpp b/src/gpu/GrTextureContext.cpp
index 0fbc951..b4e76cb 100644
--- a/src/gpu/GrTextureContext.cpp
+++ b/src/gpu/GrTextureContext.cpp
@@ -6,6 +6,8 @@
  */
 
 #include "GrTextureContext.h"
+
+#include "GrContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrResourceProvider.h"
 #include "GrTextureOpList.h"
@@ -13,7 +15,7 @@
 #include "../private/GrAuditTrail.h"
 
 #define ASSERT_SINGLE_OWNER \
-    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
+    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(this->singleOwner());)
 #define RETURN_FALSE_IF_ABANDONED  if (this->drawingManager()->wasAbandoned()) { return false; }
 
 GrTextureContext::GrTextureContext(GrContext* context,
@@ -24,7 +26,7 @@
                                    GrSingleOwner* singleOwner)
     : GrSurfaceContext(context, drawingMgr, std::move(colorSpace), auditTrail, singleOwner)
     , fTextureProxy(std::move(textureProxy))
-    , fOpList(SkSafeRef(fTextureProxy->getLastTextureOpList())) {
+    , fOpList(sk_ref_sp(fTextureProxy->getLastTextureOpList())) {
     SkDEBUGCODE(this->validate();)
 }
 
@@ -34,14 +36,13 @@
     fTextureProxy->validate(fContext);
 
     if (fOpList && !fOpList->isClosed()) {
-        SkASSERT(fTextureProxy->getLastOpList() == fOpList);
+        SkASSERT(fTextureProxy->getLastOpList() == fOpList.get());
     }
 }
 #endif
 
 GrTextureContext::~GrTextureContext() {
     ASSERT_SINGLE_OWNER
-    SkSafeUnref(fOpList);
 }
 
 GrRenderTargetProxy* GrTextureContext::asRenderTargetProxy() {
@@ -61,95 +62,22 @@
     SkDEBUGCODE(this->validate();)
 
     if (!fOpList || fOpList->isClosed()) {
-        fOpList = this->drawingManager()->newOpList(fTextureProxy.get());
+        fOpList = this->drawingManager()->newTextureOpList(fTextureProxy.get());
     }
 
-    return fOpList;
+    return fOpList.get();
 }
 
-// TODO: move this (and GrRenderTargetContext::copy) to GrSurfaceContext?
+// MDB TODO: move this (and GrRenderTargetContext::copy) to GrSurfaceContext?
 bool GrTextureContext::onCopy(GrSurfaceProxy* srcProxy,
                               const SkIRect& srcRect,
                               const SkIPoint& dstPoint) {
     ASSERT_SINGLE_OWNER
     RETURN_FALSE_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrTextureContext::copy");
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrTextureContext::onCopy");
 
-    // TODO: defer instantiation until flush time
-    sk_sp<GrSurface> src(sk_ref_sp(srcProxy->instantiate(fContext->resourceProvider())));
-    if (!src) {
-        return false;
-    }
-
-#ifndef ENABLE_MDB
-    // We can't yet fully defer copies to textures, so GrTextureContext::copySurface will
-    // execute the copy immediately. Ensure the data is ready.
-    src->flushWrites();
-#endif
-
-    // TODO: this needs to be fixed up since it ends the deferrable of the GrTexture
-    sk_sp<GrTexture> tex(sk_ref_sp(fTextureProxy->instantiate(fContext->resourceProvider())));
-    if (!tex) {
-        return false;
-    }
-
-    GrTextureOpList* opList = this->getOpList();
-    bool result = opList->copySurface(tex.get(), src.get(), srcRect, dstPoint);
-
-#ifndef ENABLE_MDB
-    GrOpFlushState flushState(fContext->getGpu(), nullptr);
-    opList->prepareOps(&flushState);
-    opList->executeOps(&flushState);
-    opList->reset();
-#endif
-
-    return result;
+    return this->getOpList()->copySurface(*fContext->caps(),
+                                          fTextureProxy.get(), srcProxy, srcRect, dstPoint);
 }
 
-// TODO: move this (and GrRenderTargetContext::onReadPixels) to GrSurfaceContext?
-bool GrTextureContext::onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                                    size_t dstRowBytes, int x, int y, uint32_t flags) {
-    // TODO: teach GrTexture to take ImageInfo directly to specify the src pixels
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo, *fContext->caps());
-    if (kUnknown_GrPixelConfig == config) {
-        return false;
-    }
-
-    // TODO: this seems to duplicate code in SkImage_Gpu::onReadPixels
-    if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
-        flags |= GrContext::kUnpremul_PixelOpsFlag;
-    }
-
-    // Deferral of the VRAM resources must end in this instance anyway
-    sk_sp<GrTexture> tex(sk_ref_sp(fTextureProxy->instantiate(fContext->resourceProvider())));
-    if (!tex) {
-        return false;
-    }
-
-    return tex->readPixels(this->getColorSpace(), x, y, dstInfo.width(), dstInfo.height(),
-                           config, dstInfo.colorSpace(), dstBuffer, dstRowBytes, flags);
-}
-
-// TODO: move this (and GrRenderTargetContext::onReadPixels) to GrSurfaceContext?
-bool GrTextureContext::onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
-                                     size_t srcRowBytes, int x, int y,
-                                     uint32_t flags) {
-    // TODO: teach GrTexture to take ImageInfo directly to specify the src pixels
-    GrPixelConfig config = SkImageInfo2GrPixelConfig(srcInfo, *fContext->caps());
-    if (kUnknown_GrPixelConfig == config) {
-        return false;
-    }
-    if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
-        flags |= GrContext::kUnpremul_PixelOpsFlag;
-    }
-
-    // Deferral of the VRAM resources must end in this instance anyway
-    sk_sp<GrTexture> tex(sk_ref_sp(fTextureProxy->instantiate(fContext->resourceProvider())));
-    if (!tex) {
-        return false;
-    }
-
-    return tex->writePixels(this->getColorSpace(), x, y, srcInfo.width(), srcInfo.height(),
-                            config, srcInfo.colorSpace(), srcBuffer, srcRowBytes, flags);
-}
diff --git a/src/gpu/GrTextureContext.h b/src/gpu/GrTextureContext.h
index d5e1eb9..4cde61b 100644
--- a/src/gpu/GrTextureContext.h
+++ b/src/gpu/GrTextureContext.h
@@ -47,10 +47,6 @@
     friend class GrDrawingManager; // for ctor
 
     bool onCopy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) override;
-    bool onReadPixels(const SkImageInfo& dstInfo, void* dstBuffer,
-                      size_t dstRowBytes, int x, int y, uint32_t flags) override;
-    bool onWritePixels(const SkImageInfo& srcInfo, const void* srcBuffer,
-                       size_t srcRowBytes, int x, int y, uint32_t flags) override;
 
     GrTextureOpList* getOpList();
 
@@ -58,7 +54,7 @@
 
     // In MDB-mode the GrOpList can be closed by some other renderTargetContext that has picked
     // it up. For this reason, the GrOpList should only ever be accessed via 'getOpList'.
-    GrTextureOpList*             fOpList;
+    sk_sp<GrTextureOpList>       fOpList;
 
     typedef GrSurfaceContext INHERITED;
 };
diff --git a/src/gpu/GrTextureOpList.cpp b/src/gpu/GrTextureOpList.cpp
index 2a021d2..3c127f6 100644
--- a/src/gpu/GrTextureOpList.cpp
+++ b/src/gpu/GrTextureOpList.cpp
@@ -15,13 +15,13 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrTextureOpList::GrTextureOpList(GrTextureProxy* tex, GrGpu* gpu, GrAuditTrail* auditTrail)
-    : INHERITED(tex, auditTrail)
-    , fGpu(SkRef(gpu)) {
+GrTextureOpList::GrTextureOpList(GrResourceProvider* resourceProvider,
+                                 GrTextureProxy* proxy,
+                                 GrAuditTrail* auditTrail)
+    : INHERITED(resourceProvider, proxy, auditTrail) {
 }
 
 GrTextureOpList::~GrTextureOpList() {
-    fGpu->unref();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -42,10 +42,11 @@
                     clippedBounds.fBottom);
     }
 }
+
 #endif
 
 void GrTextureOpList::prepareOps(GrOpFlushState* flushState) {
-    // MDB TODO: add SkASSERT(this->isClosed());
+    SkASSERT(this->isClosed());
 
     // Loop over the ops that haven't yet generated their geometry
     for (int i = 0; i < fRecordedOps.count(); ++i) {
@@ -66,20 +67,24 @@
         fRecordedOps[i]->execute(flushState);
     }
 
-    fGpu->finishOpList();
     return true;
 }
 
 void GrTextureOpList::reset() {
     fRecordedOps.reset();
+    INHERITED::reset();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrTextureOpList::copySurface(GrSurface* dst,
-                                  GrSurface* src,
+// MDB TODO: fuse with GrRenderTargetOpList::copySurface
+bool GrTextureOpList::copySurface(const GrCaps& caps,
+                                  GrSurfaceProxy* dst,
+                                  GrSurfaceProxy* src,
                                   const SkIRect& srcRect,
                                   const SkIPoint& dstPoint) {
+    SkASSERT(dst == fTarget.get());
+
     std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(dst, src, srcRect, dstPoint);
     if (!op) {
         return false;
@@ -88,17 +93,17 @@
     this->addDependency(src);
 #endif
 
-    // See the comment in GrRenderTargetOpList about why we pass the invalid ID here.
-    this->recordOp(std::move(op), GrGpuResource::UniqueID::InvalidID());
+    this->recordOp(std::move(op));
     return true;
 }
 
-void GrTextureOpList::recordOp(std::unique_ptr<GrOp> op, GrGpuResource::UniqueID renderTargetID) {
+void GrTextureOpList::recordOp(std::unique_ptr<GrOp> op) {
+    SkASSERT(fTarget.get());
     // A closed GrOpList should never receive new/more ops
     SkASSERT(!this->isClosed());
 
-    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), renderTargetID);
-    GrOP_INFO("Re-Recording (%s, B%u)\n"
+    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget.get()->uniqueID());
+    GrOP_INFO("Re-Recording (%s, opID: %u)\n"
         "\tBounds LRTB (%f, %f, %f, %f)\n",
         op->name(),
         op->uniqueID(),
diff --git a/src/gpu/GrTextureOpList.h b/src/gpu/GrTextureOpList.h
index 325ab5d..1ca45b5 100644
--- a/src/gpu/GrTextureOpList.h
+++ b/src/gpu/GrTextureOpList.h
@@ -10,6 +10,7 @@
 
 #include "GrGpuResource.h"
 #include "GrOpList.h"
+#include "GrSurfaceProxy.h"
 
 #include "SkTArray.h"
 
@@ -22,8 +23,7 @@
 
 class GrTextureOpList final : public GrOpList {
 public:
-    GrTextureOpList(GrTextureProxy*, GrGpu*, GrAuditTrail*);
-
+    GrTextureOpList(GrResourceProvider*, GrTextureProxy*, GrAuditTrail*);
     ~GrTextureOpList() override;
 
     /**
@@ -51,8 +51,9 @@
      * depending on the type of surface, configs, etc, and the backend-specific
      * limitations.
      */
-    bool copySurface(GrSurface* dst,
-                     GrSurface* src,
+    bool copySurface(const GrCaps& caps,
+                     GrSurfaceProxy* dst,
+                     GrSurfaceProxy* src,
                      const SkIRect& srcRect,
                      const SkIPoint& dstPoint);
 
@@ -60,12 +61,12 @@
 
     SkDEBUGCODE(void dump() const override;)
 
+    SkDEBUGCODE(int numOps() const override { return fRecordedOps.count(); })
+
 private:
-    // The unique ID is only needed for the audit trail. This should be removed with MDB.
-    void recordOp(std::unique_ptr<GrOp>, GrGpuResource::UniqueID renderTargetID);
+    void recordOp(std::unique_ptr<GrOp>);
 
     SkSTArray<2, std::unique_ptr<GrOp>, true> fRecordedOps;
-    GrGpu*                          fGpu;
 
     typedef GrOpList INHERITED;
 };
diff --git a/src/gpu/GrTexturePriv.h b/src/gpu/GrTexturePriv.h
index cc0a05e..67631fc 100644
--- a/src/gpu/GrTexturePriv.h
+++ b/src/gpu/GrTexturePriv.h
@@ -8,7 +8,6 @@
 #ifndef GrTexturePriv_DEFINED
 #define GrTexturePriv_DEFINED
 
-#include "GrExternalTextureData.h"
 #include "GrTexture.h"
 
 /** Class that adds methods to GrTexture that are only intended for use internal to Skia.
@@ -18,18 +17,6 @@
     implemented privately in GrTexture with a inline public method here). */
 class GrTexturePriv {
 public:
-    void setFlag(GrSurfaceFlags flags) {
-        fTexture->fDesc.fFlags = fTexture->fDesc.fFlags | flags;
-    }
-
-    void resetFlag(GrSurfaceFlags flags) {
-        fTexture->fDesc.fFlags = fTexture->fDesc.fFlags & ~flags;
-    }
-
-    bool isSetFlag(GrSurfaceFlags flags) const {
-        return 0 != (fTexture->fDesc.fFlags & flags);
-    }
-
     void dirtyMipMaps(bool mipMapsDirty) {
         fTexture->dirtyMipMaps(mipMapsDirty);
     }
@@ -68,18 +55,13 @@
     }
     SkDestinationSurfaceColorMode mipColorMode() const { return fTexture->fMipColorMode; }
 
-    /**
-     *  Return the native bookkeeping data for this texture, and detach the backend object from
-     *  this GrTexture. It's lifetime will no longer be managed by Ganesh, and this GrTexture will
-     *  no longer refer to it. Leaves this GrTexture in an orphan state.
-     */
-    std::unique_ptr<GrExternalTextureData> detachBackendTexture() {
-        return fTexture->detachBackendTexture();
-    }
-
     static void ComputeScratchKey(const GrSurfaceDesc&, GrScratchKey*);
 
 private:
+    static void ComputeScratchKey(GrPixelConfig config, int width, int height,
+                                  GrSurfaceOrigin origin, bool isRenderTarget, int sampleCnt,
+                                  bool isMipMapped, GrScratchKey* key);
+
     GrTexturePriv(GrTexture* texture) : fTexture(texture) { }
     GrTexturePriv(const GrTexturePriv& that) : fTexture(that.fTexture) { }
     GrTexturePriv& operator=(const GrTexturePriv&); // unimpl
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index d226c3f..b9e7dfc 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -23,12 +23,10 @@
     SkASSERT(!subset || !subset->isEmpty());
     SkASSERT(context);
 
-    GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config());
-
     const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
 
-    sk_sp<GrRenderTargetContext> copyRTC = context->makeRenderTargetContextWithFallback(
-        SkBackingFit::kExact, dstRect.width(), dstRect.height(), config, nullptr);
+    sk_sp<GrRenderTargetContext> copyRTC = context->makeDeferredRenderTargetContextWithFallback(
+        SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(), nullptr);
     if (!copyRTC) {
         return nullptr;
     }
diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp
index ce7770d..98cc75b 100644
--- a/src/gpu/GrTextureProxy.cpp
+++ b/src/gpu/GrTextureProxy.cpp
@@ -12,21 +12,26 @@
 
 GrTextureProxy::GrTextureProxy(const GrSurfaceDesc& srcDesc, SkBackingFit fit, SkBudgeted budgeted,
                                const void* srcData, size_t /*rowBytes*/, uint32_t flags)
-    : INHERITED(srcDesc, fit, budgeted, flags) {
-    SkASSERT(!srcData);   // currently handled in Make()
+        : INHERITED(srcDesc, fit, budgeted, flags)
+        , fIsMipMapped(srcDesc.fIsMipMapped)
+        , fMipColorMode(SkDestinationSurfaceColorMode::kLegacy) {
+    SkASSERT(!srcData);  // currently handled in Make()
 }
 
 GrTextureProxy::GrTextureProxy(sk_sp<GrSurface> surf)
-    : INHERITED(std::move(surf), SkBackingFit::kExact) {
+        : INHERITED(std::move(surf), SkBackingFit::kExact)
+        , fIsMipMapped(fTarget->asTexture()->texturePriv().hasMipMaps())
+        , fMipColorMode(fTarget->asTexture()->texturePriv().mipColorMode()) {
 }
 
-GrTexture* GrTextureProxy::instantiate(GrResourceProvider* resourceProvider) {
-    GrSurface* surf = this->INHERITED::instantiate(resourceProvider);
-    if (!surf) {
-        return nullptr;
+bool GrTextureProxy::instantiate(GrResourceProvider* resourceProvider) {
+    if (!this->instantiateImpl(resourceProvider, 0, kNone_GrSurfaceFlags, fIsMipMapped,
+                               fMipColorMode)) {
+        return false;
     }
 
-    return fTarget->asTexture();
+    SkASSERT(fTarget->asTexture());
+    return true;
 }
 
 void GrTextureProxy::setMipColorMode(SkDestinationSurfaceColorMode colorMode) {
@@ -39,12 +44,27 @@
     fMipColorMode = colorMode;
 }
 
-size_t GrTextureProxy::onGpuMemorySize() const {
+// This method parallels the highest_filter_mode functions in GrGLTexture & GrVkTexture.
+GrSamplerParams::FilterMode GrTextureProxy::highestFilterMode() const {
     if (fTarget) {
-        return fTarget->gpuMemorySize();
+        return fTarget->asTexture()->texturePriv().highestFilterMode();
     }
 
+    if (GrPixelConfigIsSint(this->config())) {
+        // We only ever want to nearest-neighbor sample signed int textures.
+        return GrSamplerParams::kNone_FilterMode;
+    }
+
+    // In OpenGL, GR_GL_TEXTURE_RECTANGLE and GR_GL_TEXTURE_EXTERNAL (which have a highest filter
+    // mode of bilerp) can only be created via wrapping.
+
+    return GrSamplerParams::kMipMap_FilterMode;
+}
+
+size_t GrTextureProxy::onUninstantiatedGpuMemorySize() const {
     static const bool kHasMipMaps = true;
-    // TODO: add tracking of mipmap state to improve the estimate
-    return GrSurface::ComputeSize(fDesc, 1, kHasMipMaps, SkBackingFit::kApprox == fFit);
+    // TODO: add tracking of mipmap state to improve the estimate. We track whether we are created
+    // with mip maps but not whether a texture read from the proxy will lazily generate mip maps.
+    return GrSurface::ComputeSize(fConfig, fWidth, fHeight, 1, kHasMipMaps,
+                                  SkBackingFit::kApprox == fFit);
 }
diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp
index 432d008..3981f17 100644
--- a/src/gpu/GrTextureRenderTargetProxy.cpp
+++ b/src/gpu/GrTextureRenderTargetProxy.cpp
@@ -32,12 +32,27 @@
     SkASSERT(surf->asRenderTarget());
 }
 
-size_t GrTextureRenderTargetProxy::onGpuMemorySize() const {
-    if (fTarget) {
-        return fTarget->gpuMemorySize();
-    }
+size_t GrTextureRenderTargetProxy::onUninstantiatedGpuMemorySize() const {
+    int colorSamplesPerPixel = this->numColorSamples() + 1;
+
+    static const bool kHasMipMaps = true;
+    // TODO: add tracking of mipmap state to improve the estimate. We track whether we are created
+    // with mip maps but not whether a texture read from the proxy will lazily generate mip maps.
 
     // TODO: do we have enough information to improve this worst case estimate?
-    return GrSurface::ComputeSize(fDesc, fDesc.fSampleCnt+1, true, SkBackingFit::kApprox == fFit);
+    return GrSurface::ComputeSize(fConfig, fWidth, fHeight, colorSamplesPerPixel, kHasMipMaps,
+                                  SkBackingFit::kApprox == fFit);
 }
 
+bool GrTextureRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) {
+    static constexpr GrSurfaceFlags kFlags = kRenderTarget_GrSurfaceFlag;
+
+    if (!this->instantiateImpl(resourceProvider, this->numStencilSamples(), kFlags,
+                               this->isMipMapped(), this->mipColorMode())) {
+        return false;
+    }
+    SkASSERT(fTarget->asRenderTarget());
+    SkASSERT(fTarget->asTexture());
+
+    return true;
+}
diff --git a/src/gpu/GrTextureRenderTargetProxy.h b/src/gpu/GrTextureRenderTargetProxy.h
new file mode 100644
index 0000000..a9e8b52
--- /dev/null
+++ b/src/gpu/GrTextureRenderTargetProxy.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextureRenderTargetProxy_DEFINED
+#define GrTextureRenderTargetProxy_DEFINED
+
+#include "GrRenderTargetProxy.h"
+#include "GrTextureProxy.h"
+
+#ifdef SK_BUILD_FOR_WIN
+// Windows gives warnings about inheriting asTextureProxy/asRenderTargetProxy via dominance.
+#pragma warning(push)
+#pragma warning(disable: 4250)
+#endif
+
+// This class delays the acquisition of RenderTargets that are also textures until
+// they are actually required
+// Beware: the uniqueID of the TextureRenderTargetProxy will usually be different than
+// the uniqueID of the RenderTarget/Texture it represents!
+class GrTextureRenderTargetProxy : public GrTextureProxy, public GrRenderTargetProxy {
+private:
+    friend class GrSurfaceProxy; // for ctors
+
+    // Deferred version
+    GrTextureRenderTargetProxy(const GrCaps&, const GrSurfaceDesc&,
+                               SkBackingFit, SkBudgeted, uint32_t flags);
+
+    // Wrapped version
+    GrTextureRenderTargetProxy(sk_sp<GrSurface>);
+
+    bool instantiate(GrResourceProvider*) override;
+
+    size_t onUninstantiatedGpuMemorySize() const override;
+};
+
+#ifdef SK_BUILD_FOR_WIN
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/src/gpu/GrTextureToYUVPlanes.cpp b/src/gpu/GrTextureToYUVPlanes.cpp
deleted file mode 100644
index 0a0edee..0000000
--- a/src/gpu/GrTextureToYUVPlanes.cpp
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * 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 "GrTextureToYUVPlanes.h"
-#include "effects/GrSimpleTextureEffect.h"
-#include "effects/GrYUVEffect.h"
-#include "GrClip.h"
-#include "GrContext.h"
-#include "GrPaint.h"
-#include "GrRenderTargetContext.h"
-#include "GrResourceProvider.h"
-
-namespace {
-    using MakeFPProc = sk_sp<GrFragmentProcessor> (*)(sk_sp<GrFragmentProcessor>,
-                                                      SkYUVColorSpace colorSpace);
-};
-
-static bool convert_proxy(sk_sp<GrTextureProxy> src,
-                          GrRenderTargetContext* dst, int dstW, int dstH,
-                          SkYUVColorSpace colorSpace, MakeFPProc proc) {
-
-    SkScalar xScale = SkIntToScalar(src->width()) / dstW;
-    SkScalar yScale = SkIntToScalar(src->height()) / dstH;
-    GrSamplerParams::FilterMode filter;
-    if (dstW == src->width() && dstW == src->height()) {
-        filter = GrSamplerParams::kNone_FilterMode;
-    } else {
-        filter = GrSamplerParams::kBilerp_FilterMode;
-    }
-
-    GrResourceProvider* resourceProvider = dst->resourceProvider();
-
-    sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(resourceProvider, std::move(src),
-                                                              nullptr,
-                                                              SkMatrix::MakeScale(xScale, yScale),
-                                                              filter));
-    if (!fp) {
-        return false;
-    }
-    fp = proc(std::move(fp), colorSpace);
-    if (!fp) {
-        return false;
-    }
-    GrPaint paint;
-    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    paint.addColorFragmentProcessor(std::move(fp));
-    dst->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
-                  SkRect::MakeIWH(dstW, dstH));
-    return true;
-}
-
-bool GrTextureToYUVPlanes(GrContext* context, sk_sp<GrTextureProxy> proxy,
-                          const SkISize sizes[3], void* const planes[3],
-                          const size_t rowBytes[3], SkYUVColorSpace colorSpace) {
-    if (!context) {
-        return false;
-    }
-
-    {
-        // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/
-        // readbacks.
-        sk_sp<GrRenderTargetContext> yuvRenderTargetContext;
-        sk_sp<GrRenderTargetContext> yRenderTargetContext;
-        sk_sp<GrRenderTargetContext> uvRenderTargetContext;
-        sk_sp<GrRenderTargetContext> uRenderTargetContext;
-        sk_sp<GrRenderTargetContext> vRenderTargetContext;
-
-        // We issue draw(s) to convert from RGBA to Y, U, and V. All three planes may have different
-        // sizes however we optimize for two other cases - all planes are the same (1 draw to YUV),
-        // and U and V are the same but Y differs (2 draws, one for Y, one for UV).
-        if (sizes[0] == sizes[1] && sizes[1] == sizes[2]) {
-            yuvRenderTargetContext = context->makeRenderTargetContextWithFallback(
-                                                                           SkBackingFit::kApprox,
-                                                                           sizes[0].fWidth,
-                                                                           sizes[0].fHeight,
-                                                                           kRGBA_8888_GrPixelConfig,
-                                                                           nullptr);
-            if (!yuvRenderTargetContext) {
-                return false;
-            }
-        } else {
-            yRenderTargetContext = context->makeRenderTargetContextWithFallback(
-                                                                             SkBackingFit::kApprox,
-                                                                             sizes[0].fWidth,
-                                                                             sizes[0].fHeight,
-                                                                             kAlpha_8_GrPixelConfig,
-                                                                             nullptr);
-            if (!yRenderTargetContext) {
-                return false;
-            }
-            if (sizes[1] == sizes[2]) {
-                // TODO: Add support for GL_RG when available.
-                uvRenderTargetContext = context->makeRenderTargetContextWithFallback(
-                                                                           SkBackingFit::kApprox,
-                                                                           sizes[1].fWidth,
-                                                                           sizes[1].fHeight,
-                                                                           kRGBA_8888_GrPixelConfig,
-                                                                           nullptr);
-                if (!uvRenderTargetContext) {
-                    return false;
-                }
-            } else {
-                uRenderTargetContext = context->makeRenderTargetContextWithFallback(
-                                                                             SkBackingFit::kApprox,
-                                                                             sizes[1].fWidth,
-                                                                             sizes[1].fHeight,
-                                                                             kAlpha_8_GrPixelConfig,
-                                                                             nullptr);
-                vRenderTargetContext = context->makeRenderTargetContextWithFallback(
-                                                                             SkBackingFit::kApprox,
-                                                                             sizes[2].fWidth,
-                                                                             sizes[2].fHeight,
-                                                                             kAlpha_8_GrPixelConfig,
-                                                                             nullptr);
-                if (!uRenderTargetContext || !vRenderTargetContext) {
-                    return false;
-                }
-            }
-        }
-
-        // Do all the draws before any readback.
-        if (yuvRenderTargetContext) {
-            if (!convert_proxy(std::move(proxy), yuvRenderTargetContext.get(),
-                               sizes[0].fWidth, sizes[0].fHeight,
-                               colorSpace, GrYUVEffect::MakeRGBToYUV)) {
-                return false;
-            }
-        } else {
-            SkASSERT(yRenderTargetContext);
-            if (!convert_proxy(proxy, yRenderTargetContext.get(),
-                               sizes[0].fWidth, sizes[0].fHeight,
-                               colorSpace, GrYUVEffect::MakeRGBToY)) {
-                return false;
-            }
-            if (uvRenderTargetContext) {
-                if (!convert_proxy(std::move(proxy), uvRenderTargetContext.get(),
-                                   sizes[1].fWidth, sizes[1].fHeight,
-                                   colorSpace,  GrYUVEffect::MakeRGBToUV)) {
-                    return false;
-                }
-            } else {
-                SkASSERT(uRenderTargetContext && vRenderTargetContext);
-                if (!convert_proxy(proxy, uRenderTargetContext.get(),
-                                   sizes[1].fWidth, sizes[1].fHeight,
-                                   colorSpace, GrYUVEffect::MakeRGBToU)) {
-                    return false;
-                }
-                if (!convert_proxy(std::move(proxy), vRenderTargetContext.get(),
-                                   sizes[2].fWidth, sizes[2].fHeight,
-                                   colorSpace, GrYUVEffect::MakeRGBToV)) {
-                    return false;
-                }
-            }
-        }
-
-        if (yuvRenderTargetContext) {
-            SkASSERT(sizes[0] == sizes[1] && sizes[1] == sizes[2]);
-            SkISize yuvSize = sizes[0];
-            // We have no kRGB_888 pixel format, so readback rgba and then copy three channels.
-            SkAutoSTMalloc<128 * 128, uint32_t> tempYUV(yuvSize.fWidth * yuvSize.fHeight);
-
-            const SkImageInfo ii = SkImageInfo::Make(yuvSize.fWidth, yuvSize.fHeight,
-                                                     kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
-            if (!yuvRenderTargetContext->readPixels(ii, tempYUV.get(), 0, 0, 0)) {
-                return false;
-            }
-            size_t yRowBytes = rowBytes[0] ? rowBytes[0] : yuvSize.fWidth;
-            size_t uRowBytes = rowBytes[1] ? rowBytes[1] : yuvSize.fWidth;
-            size_t vRowBytes = rowBytes[2] ? rowBytes[2] : yuvSize.fWidth;
-            if (yRowBytes < (size_t)yuvSize.fWidth || uRowBytes < (size_t)yuvSize.fWidth ||
-                vRowBytes < (size_t)yuvSize.fWidth) {
-                return false;
-            }
-            for (int j = 0; j < yuvSize.fHeight; ++j) {
-                for (int i = 0; i < yuvSize.fWidth; ++i) {
-                    // These writes could surely be made more efficient.
-                    uint32_t y = GrColorUnpackR(tempYUV.get()[j * yuvSize.fWidth + i]);
-                    uint32_t u = GrColorUnpackG(tempYUV.get()[j * yuvSize.fWidth + i]);
-                    uint32_t v = GrColorUnpackB(tempYUV.get()[j * yuvSize.fWidth + i]);
-                    uint8_t* yLoc = ((uint8_t*)planes[0]) + j * yRowBytes + i;
-                    uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
-                    uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
-                    *yLoc = y;
-                    *uLoc = u;
-                    *vLoc = v;
-                }
-            }
-            return true;
-        } else {
-            SkASSERT(yRenderTargetContext);
-
-            SkImageInfo ii = SkImageInfo::MakeA8(sizes[0].fWidth, sizes[0].fHeight);
-            if (!yRenderTargetContext->readPixels(ii, planes[0], rowBytes[0], 0, 0)) {
-                return false;
-            }
-
-            if (uvRenderTargetContext) {
-                SkASSERT(sizes[1].fWidth == sizes[2].fWidth);
-                SkISize uvSize = sizes[1];
-                // We have no kRG_88 pixel format, so readback rgba and then copy two channels.
-                SkAutoSTMalloc<128 * 128, uint32_t> tempUV(uvSize.fWidth * uvSize.fHeight);
-
-                ii = SkImageInfo::Make(uvSize.fWidth, uvSize.fHeight,
-                                       kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
-
-                if (!uvRenderTargetContext->readPixels(ii, tempUV.get(), 0, 0, 0)) {
-                    return false;
-                }
-
-                size_t uRowBytes = rowBytes[1] ? rowBytes[1] : uvSize.fWidth;
-                size_t vRowBytes = rowBytes[2] ? rowBytes[2] : uvSize.fWidth;
-                if (uRowBytes < (size_t)uvSize.fWidth || vRowBytes < (size_t)uvSize.fWidth) {
-                    return false;
-                }
-                for (int j = 0; j < uvSize.fHeight; ++j) {
-                    for (int i = 0; i < uvSize.fWidth; ++i) {
-                        // These writes could surely be made more efficient.
-                        uint32_t u = GrColorUnpackR(tempUV.get()[j * uvSize.fWidth + i]);
-                        uint32_t v = GrColorUnpackG(tempUV.get()[j * uvSize.fWidth + i]);
-                        uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
-                        uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
-                        *uLoc = u;
-                        *vLoc = v;
-                    }
-                }
-                return true;
-            } else {
-                SkASSERT(uRenderTargetContext && vRenderTargetContext);
-
-                ii = SkImageInfo::MakeA8(sizes[1].fWidth, sizes[1].fHeight);
-                if (!uRenderTargetContext->readPixels(ii, planes[1], rowBytes[1], 0, 0)) {
-                    return false;
-                }
-
-                ii = SkImageInfo::MakeA8(sizes[2].fWidth, sizes[2].fHeight);
-                if (!vRenderTargetContext->readPixels(ii, planes[2], rowBytes[2], 0, 0)) {
-                    return false;
-                }
-
-                return true;
-            }
-        }
-    }
-    return false;
-}
diff --git a/src/gpu/GrTextureToYUVPlanes.h b/src/gpu/GrTextureToYUVPlanes.h
deleted file mode 100644
index 1dcbea3..0000000
--- a/src/gpu/GrTextureToYUVPlanes.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrTextureToYUVPlanes_DEFINED
-#define GrTextureToYUVPlanes_DEFINED
-
-#include "SkImageInfo.h"
-#include "SkSize.h"
-
-class GrContext;
-class GrTextureProxy;
-
-bool GrTextureToYUVPlanes(GrContext*, sk_sp<GrTextureProxy>,
-                          const SkISize[3], void* const planes[3],
-                          const size_t rowBytes[3], SkYUVColorSpace);
-
-#endif
diff --git a/src/gpu/GrXferProcessor.cpp b/src/gpu/GrXferProcessor.cpp
index bd63a0a..8c0568b 100644
--- a/src/gpu/GrXferProcessor.cpp
+++ b/src/gpu/GrXferProcessor.cpp
@@ -10,29 +10,15 @@
 #include "gl/GrGLCaps.h"
 
 GrXferProcessor::GrXferProcessor()
-    : fWillReadDstColor(false)
-    , fDstReadUsesMixedSamples(false)
-    , fDstTextureOffset() {
-}
+        : fWillReadDstColor(false)
+        , fDstReadUsesMixedSamples(false)
+        , fIsLCD(false) {}
 
-GrXferProcessor::GrXferProcessor(const DstTexture* dstTexture,
-                                 bool willReadDstColor,
-                                 bool hasMixedSamples)
-    : fWillReadDstColor(willReadDstColor)
-    , fDstReadUsesMixedSamples(willReadDstColor && hasMixedSamples)
-    , fDstTextureOffset() {
-    if (dstTexture && dstTexture->texture()) {
-        SkASSERT(willReadDstColor);
-        fDstTexture.reset(dstTexture->texture());
-        fDstTextureOffset = dstTexture->offset();
-        this->addTextureSampler(&fDstTexture);
-    }
-}
-
-GrXferProcessor::OptFlags GrXferProcessor::getOptimizations(
-        const FragmentProcessorAnalysis& analysis) const {
-    return this->onGetOptimizations(analysis);
-}
+GrXferProcessor::GrXferProcessor(bool willReadDstColor, bool hasMixedSamples,
+                                 GrProcessorAnalysisCoverage coverage)
+        : fWillReadDstColor(willReadDstColor)
+        , fDstReadUsesMixedSamples(willReadDstColor && hasMixedSamples)
+        , fIsLCD(GrProcessorAnalysisCoverage::kLCD == coverage) {}
 
 bool GrXferProcessor::hasSecondaryOutput() const {
     if (!this->willReadDstColor()) {
@@ -50,13 +36,13 @@
     }
 }
 
-void GrXferProcessor::getGLSLProcessorKey(const GrShaderCaps& caps,
-                                          GrProcessorKeyBuilder* b) const {
+void GrXferProcessor::getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b,
+                                          const GrSurfaceOrigin* originIfDstTexture) const {
     uint32_t key = this->willReadDstColor() ? 0x1 : 0x0;
     if (key) {
-        if (const GrTexture* dstTexture = this->getDstTexture()) {
+        if (originIfDstTexture) {
             key |= 0x2;
-            if (kTopLeft_GrSurfaceOrigin == dstTexture->origin()) {
+            if (kTopLeft_GrSurfaceOrigin == *originIfDstTexture) {
                 key |= 0x4;
             }
         }
@@ -64,21 +50,13 @@
             key |= 0x8;
         }
     }
+    if (fIsLCD) {
+        key |= 0x10;
+    }
     b->add32(key);
     this->onGetGLSLProcessorKey(caps, b);
 }
 
-GrXferBarrierType GrXferProcessor::xferBarrierType(const GrRenderTarget* rt,
-                                                   const GrCaps& caps) const {
-    SkASSERT(rt);
-    if (static_cast<const GrSurface*>(rt) == this->getDstTexture()) {
-        // Texture barriers are required when a shader reads and renders to the same texture.
-        SkASSERT(caps.textureBarrierSupport());
-        return kTexture_GrXferBarrierType;
-    }
-    return this->onXferBarrier(rt, caps);
-}
-
 #ifdef SK_DEBUG
 static const char* equation_string(GrBlendEquation eq) {
     switch (eq) {
@@ -175,48 +153,38 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool GrXPFactory::WillNeedDstTexture(const GrXPFactory* factory, const GrCaps& caps,
-                                     const GrProcessorSet::FragmentProcessorAnalysis& analysis) {
-    bool result;
+GrXPFactory::AnalysisProperties GrXPFactory::GetAnalysisProperties(
+        const GrXPFactory* factory,
+        const GrProcessorAnalysisColor& color,
+        const GrProcessorAnalysisCoverage& coverage,
+        const GrCaps& caps) {
+    AnalysisProperties result;
     if (factory) {
-        result = !caps.shaderCaps()->dstReadInShaderSupport() &&
-                 factory->willReadDstInShader(caps, analysis);
+        result = factory->analysisProperties(color, coverage, caps);
     } else {
-        result = GrPorterDuffXPFactory::WillSrcOverNeedDstTexture(caps, analysis);
+        result = GrPorterDuffXPFactory::SrcOverAnalysisProperties(color, coverage, caps);
+    }
+    SkASSERT(!(result & AnalysisProperties::kRequiresDstTexture));
+    if ((result & AnalysisProperties::kReadsDstInShader) &&
+        !caps.shaderCaps()->dstReadInShaderSupport()) {
+        result |= AnalysisProperties::kRequiresDstTexture;
+        if (caps.textureBarrierSupport()) {
+            result |= AnalysisProperties::kRequiresBarrierBetweenOverlappingDraws;
+        }
     }
     return result;
 }
 
-bool GrXPFactory::CompatibleWithCoverageAsAlpha(const GrXPFactory* factory, bool colorIsOpaque) {
-    if (factory) {
-        return factory->compatibleWithCoverageAsAlpha(colorIsOpaque);
-    }
-    return GrPorterDuffXPFactory::SrcOverIsCompatibleWithCoverageAsAlpha();
-}
-
-bool GrXPFactory::CanCombineOverlappedStencilAndCover(const GrXPFactory* factory,
-                                                      bool colorIsOpaque) {
-    if (factory) {
-        return factory->canCombineOverlappedStencilAndCover(colorIsOpaque);
-    }
-    return GrPorterDuffXPFactory::SrcOverCanCombineOverlappedStencilAndCover(colorIsOpaque);
-}
-
-GrXferProcessor* GrXPFactory::createXferProcessor(const FragmentProcessorAnalysis& analysis,
-                                                  bool hasMixedSamples,
-                                                  const DstTexture* dstTexture,
-                                                  const GrCaps& caps) const {
-#ifdef SK_DEBUG
-    if (this->willReadDstInShader(caps, analysis)) {
-        if (!caps.shaderCaps()->dstReadInShaderSupport()) {
-            SkASSERT(dstTexture && dstTexture->texture());
-        } else {
-            SkASSERT(!dstTexture || !dstTexture->texture());
-        }
-    } else {
-        SkASSERT(!dstTexture || !dstTexture->texture());
-    }
+sk_sp<const GrXferProcessor> GrXPFactory::MakeXferProcessor(const GrXPFactory* factory,
+                                                            const GrProcessorAnalysisColor& color,
+                                                            GrProcessorAnalysisCoverage coverage,
+                                                            bool hasMixedSamples,
+                                                            const GrCaps& caps) {
     SkASSERT(!hasMixedSamples || caps.shaderCaps()->dualSourceBlendingSupport());
-#endif
-    return this->onCreateXferProcessor(caps, analysis, hasMixedSamples, dstTexture);
+    if (factory) {
+        return factory->makeXferProcessor(color, coverage, hasMixedSamples, caps);
+    } else {
+        return GrPorterDuffXPFactory::MakeSrcOverXferProcessor(color, coverage, hasMixedSamples,
+                                                               caps);
+    }
 }
diff --git a/src/gpu/GrXferProcessor.h b/src/gpu/GrXferProcessor.h
index bdf6f7d..b7f465f 100644
--- a/src/gpu/GrXferProcessor.h
+++ b/src/gpu/GrXferProcessor.h
@@ -10,13 +10,15 @@
 
 #include "GrBlend.h"
 #include "GrColor.h"
+#include "GrNonAtomicRef.h"
 #include "GrProcessor.h"
-#include "GrProcessorSet.h"
+#include "GrProcessorAnalysis.h"
 #include "GrTexture.h"
 #include "GrTypes.h"
 
-class GrShaderCaps;
 class GrGLSLXferProcessor;
+class GrProcessorSet;
+class GrShaderCaps;
 
 /**
  * Barriers for blending. When a shader reads the dst directly, an Xfer barrier is sometimes
@@ -46,61 +48,71 @@
  * A GrXferProcessor is never installed directly into our draw state, but instead is created from a
  * GrXPFactory once we have finalized the state of our draw.
  */
-class GrXferProcessor : public GrProcessor {
+class GrXferProcessor : public GrProcessor, public GrNonAtomicRef<GrXferProcessor> {
 public:
-    using FragmentProcessorAnalysis = GrProcessorSet::FragmentProcessorAnalysis;
-
     /**
      * A texture that contains the dst pixel values and an integer coord offset from device space
      * 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 DstTexture {
+    class DstProxy {
     public:
-        DstTexture() { fOffset.set(0, 0); }
+        DstProxy() { fOffset.set(0, 0); }
 
-        DstTexture(const DstTexture& other) {
+        DstProxy(const DstProxy& other) {
             *this = other;
         }
 
-        DstTexture(GrTexture* texture, const SkIPoint& offset)
-                : fTexture(SkSafeRef(texture)), fOffset(texture ? offset : SkIPoint{0, 0}) {}
+        DstProxy(sk_sp<GrTextureProxy> proxy, const SkIPoint& offset)
+            : fProxy(std::move(proxy)) {
+            if (fProxy) {
+                fOffset = offset;
+            } else {
+                fOffset.set(0, 0);
+            }
+        }
 
-        DstTexture& operator=(const DstTexture& other) {
-            fTexture = other.fTexture;
+        DstProxy& operator=(const DstProxy& other) {
+            fProxy = other.fProxy;
             fOffset = other.fOffset;
             return *this;
         }
 
-        bool operator==(const DstTexture& that) const {
-            return fTexture == that.fTexture && fOffset == that.fOffset;
+        bool operator==(const DstProxy& that) const {
+            return fProxy == that.fProxy && fOffset == that.fOffset;
         }
-        bool operator!=(const DstTexture& that) const { return !(*this == that); }
+        bool operator!=(const DstProxy& 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); }
 
-        GrTexture* texture() const { return fTexture.get(); }
+        GrTextureProxy* proxy() const { return fProxy.get(); }
 
-        void setTexture(sk_sp<GrTexture> texture) {
-            fTexture = std::move(texture);
-            if (!fTexture) {
+        void setProxy(sk_sp<GrTextureProxy> proxy) {
+            fProxy = std::move(proxy);
+            if (!fProxy) {
                 fOffset = {0, 0};
             }
         }
 
+        bool instantiate(GrResourceProvider* resourceProvider) {
+            return SkToBool(fProxy->instantiate(resourceProvider));
+        }
+
     private:
-        sk_sp<GrTexture> fTexture;
-        SkIPoint         fOffset;
+        sk_sp<GrTextureProxy> fProxy;
+        SkIPoint              fOffset;
     };
 
     /**
      * Sets a unique key on the GrProcessorKeyBuilder calls onGetGLSLProcessorKey(...) to get the
      * specific subclass's key.
-     */ 
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const;
+     */
+    void getGLSLProcessorKey(const GrShaderCaps&,
+                             GrProcessorKeyBuilder*,
+                             const GrSurfaceOrigin* originIfDstTexture) const;
 
     /** Returns a new instance of the appropriate *GL* implementation class
         for the given GrXferProcessor; caller is responsible for deleting
@@ -108,36 +120,13 @@
     virtual GrGLSLXferProcessor* createGLSLInstance() const = 0;
 
     /**
-     * Optimizations for blending / coverage that an OptDrawState should apply to itself.
+     * Returns the barrier type, if any, that this XP will require. Note that the possibility
+     * that a kTexture type barrier is required is handled by the GrPipeline and need not be
+     * considered by subclass overrides of this function.
      */
-    enum OptFlags {
-        /**
-         * GrXferProcessor will ignore color, thus no need to provide
-         */
-        kIgnoreColor_OptFlag = 0x1,
-        /**
-         * Can tweak alpha for coverage.
-         */
-        kCanTweakAlphaForCoverage_OptFlag = 0x2,
-    };
-
-    static const OptFlags kNone_OptFlags = (OptFlags)0;
-
-    GR_DECL_BITFIELD_OPS_FRIENDS(OptFlags);
-
-    /**
-     * Determines which optimizations (as described by the ptFlags above) can be performed by
-     * the draw with this xfer processor. If this function is called, the xfer processor may change
-     * its state to reflected the given blend optimizations. Callers are required to honor the
-     * returned OptFlags.
-     */
-    OptFlags getOptimizations(const FragmentProcessorAnalysis&) const;
-
-    /**
-     * Returns whether this XP will require an Xfer barrier on the given rt. If true, outBarrierType
-     * is updated to contain the type of barrier needed.
-     */
-    GrXferBarrierType xferBarrierType(const GrRenderTarget* rt, const GrCaps& caps) const;
+    virtual GrXferBarrierType xferBarrierType(const GrCaps& caps) const {
+        return kNone_GrXferBarrierType;
+    }
 
     struct BlendInfo {
         void reset() {
@@ -162,22 +151,6 @@
     bool willReadDstColor() const { return fWillReadDstColor; }
 
     /**
-     * Returns the texture to be used as the destination when reading the dst in the fragment
-     * shader. If the returned texture is NULL then the XP is either not reading the dst or we have
-     * extentions that support framebuffer fetching and thus don't need a copy of the dst texture.
-     */
-    const GrTexture* getDstTexture() const { return fDstTexture.texture(); }
-
-    /**
-     * Returns the offset in device coords to use when accessing the dst texture to get the dst
-     * pixel color in the shader. This value is only valid if getDstTexture() != NULL.
-     */
-    const SkIPoint& dstTextureOffset() const {
-        SkASSERT(this->getDstTexture());
-        return fDstTextureOffset;
-    }
-
-    /**
      * If we are performing a dst read, returns whether the base class will use mixed samples to
      * antialias the shader's final output. If not doing a dst read, the subclass is responsible
      * for antialiasing and this returns false.
@@ -190,6 +163,8 @@
      */
     bool hasSecondaryOutput() const;
 
+    bool isLCD() const { return fIsLCD; }
+
     /** Returns true if this and other processor conservatively draw identically. It can only return
         true when the two processor are of the same subclass (i.e. they return the same object from
         from getFactory()).
@@ -197,7 +172,7 @@
         A return value of true from isEqual() should not be used to test whether the processor would
         generate the same shader code. To test for identical code generation use getGLSLProcessorKey
       */
-    
+
     bool isEqual(const GrXferProcessor& that) const {
         if (this->classID() != that.classID()) {
             return false;
@@ -205,27 +180,20 @@
         if (this->fWillReadDstColor != that.fWillReadDstColor) {
             return false;
         }
-        if (this->fDstTexture.texture() != that.fDstTexture.texture()) {
-            return false;
-        }
-        if (this->fDstTextureOffset != that.fDstTextureOffset) {
-            return false;
-        }
         if (this->fDstReadUsesMixedSamples != that.fDstReadUsesMixedSamples) {
             return false;
         }
+        if (fIsLCD != that.fIsLCD) {
+            return false;
+        }
         return this->onIsEqual(that);
     }
 
 protected:
     GrXferProcessor();
-    GrXferProcessor(const DstTexture*, bool willReadDstColor, bool hasMixedSamples);
+    GrXferProcessor(bool willReadDstColor, bool hasMixedSamples, GrProcessorAnalysisCoverage);
 
 private:
-    void notifyRefCntIsZero() const final {}
-
-    virtual OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const = 0;
-
     /**
      * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this xfer
      * processor's GL backend implementation.
@@ -233,15 +201,6 @@
     virtual void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const = 0;
 
     /**
-     * Determines the type of barrier (if any) required by the subclass. Note that the possibility
-     * that a kTexture type barrier is required is handled by the base class and need not be
-     * considered by subclass overrides of this function.
-     */
-    virtual GrXferBarrierType onXferBarrier(const GrRenderTarget*, const GrCaps&) const {
-        return kNone_GrXferBarrierType;
-    }
-
-    /**
      * If we are not performing a dst read, returns whether the subclass will set a secondary
      * output. When using dst reads, the base class controls the secondary output and this method
      * will not be called.
@@ -257,18 +216,13 @@
 
     virtual bool onIsEqual(const GrXferProcessor&) const = 0;
 
-    bool                    fWillReadDstColor;
-    bool                    fDstReadUsesMixedSamples;
-    SkIPoint                fDstTextureOffset;
-    TextureSampler          fDstTexture;
+    bool fWillReadDstColor;
+    bool fDstReadUsesMixedSamples;
+    bool fIsLCD;
 
     typedef GrFragmentProcessor INHERITED;
 };
 
-GR_MAKE_BITFIELD_OPS(GrXferProcessor::OptFlags);
-
-///////////////////////////////////////////////////////////////////////////////
-
 /**
  * We install a GrXPFactory (XPF) early on in the pipeline before all the final draw information is
  * known (e.g. whether there is fractional pixel coverage, will coverage be 1 or 4 channel, is the
@@ -296,53 +250,72 @@
 #endif
 class GrXPFactory {
 public:
-    using FragmentProcessorAnalysis = GrProcessorSet::FragmentProcessorAnalysis;
+    typedef GrXferProcessor::DstProxy DstProxy;
 
-    typedef GrXferProcessor::DstTexture DstTexture;
+    enum class AnalysisProperties : unsigned {
+        kNone = 0x0,
+        /**
+         * The fragment shader will require the destination color.
+         */
+        kReadsDstInShader = 0x1,
+        /**
+         * The op may apply coverage as alpha and still blend correctly.
+         */
+        kCompatibleWithAlphaAsCoverage = 0x2,
+        /**
+         * The color input to the GrXferProcessor will be ignored.
+         */
+        kIgnoresInputColor = 0x4,
+        /**
+         * If set overlapping stencil and cover operations can be replaced by a combined stencil
+         * followed by a combined cover.
+         */
+        kCanCombineOverlappedStencilAndCover = 0x8,
+        /**
+         * The destination color will be provided to the fragment processor using a texture. This is
+         * additional information about the implementation of kReadsDstInShader.
+         */
+        kRequiresDstTexture = 0x10,
+        /**
+         * If set overlapping draws may not be combined because a barrier must be inserted between
+         * them.
+         */
+        kRequiresBarrierBetweenOverlappingDraws = 0x20,
+    };
+    GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(AnalysisProperties);
 
-    GrXferProcessor* createXferProcessor(const FragmentProcessorAnalysis&,
-                                         bool hasMixedSamples,
-                                         const DstTexture*,
-                                         const GrCaps& caps) const;
+    static sk_sp<const GrXferProcessor> MakeXferProcessor(const GrXPFactory*,
+                                                          const GrProcessorAnalysisColor&,
+                                                          GrProcessorAnalysisCoverage,
+                                                          bool hasMixedSamples,
+                                                          const GrCaps& caps);
 
-    /**
-    * This will return true if the xfer processor needs the dst color in the shader and the way
-    * that the color will be made available to the xfer processor is by sampling a texture.
-    */
-    static bool WillNeedDstTexture(const GrXPFactory*,
-                                   const GrCaps&,
-                                   const FragmentProcessorAnalysis&);
-
-    static bool CompatibleWithCoverageAsAlpha(const GrXPFactory*, bool colorIsOpaque);
-
-    /**
-     * This indicates whether the the xfer processor will produce the same bleneded color result
-     * if a series of overlapping stencil and cover operations are replaced by a series of stencil
-     * operations and a single cover. A uniform src color is assumed.
-     **/
-    static bool CanCombineOverlappedStencilAndCover(const GrXPFactory*, bool colorIsOpaque);
+    static AnalysisProperties GetAnalysisProperties(const GrXPFactory*,
+                                                    const GrProcessorAnalysisColor&,
+                                                    const GrProcessorAnalysisCoverage&,
+                                                    const GrCaps&);
 
 protected:
     constexpr GrXPFactory() {}
 
 private:
-    virtual GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
-                                                   const FragmentProcessorAnalysis&,
-                                                   bool hasMixedSamples,
-                                                   const DstTexture*) const = 0;
+    virtual sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
+                                                           GrProcessorAnalysisCoverage,
+                                                           bool hasMixedSamples,
+                                                           const GrCaps&) const = 0;
 
     /**
-     *  Returns true if the XP generated by this factory will explicitly read dst in the fragment
-     *  shader.
+     * Subclass analysis implementation. This should not return kNeedsDstInTexture as that will be
+     * inferred by the base class based on kReadsDstInShader and the caps.
      */
-    virtual bool willReadDstInShader(const GrCaps&, const FragmentProcessorAnalysis&) const = 0;
-
-    virtual bool compatibleWithCoverageAsAlpha(bool colorIsOpaque) const = 0;
-    virtual bool canCombineOverlappedStencilAndCover(bool colorIsOpaque) const { return false; }
+    virtual AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
+                                                  const GrProcessorAnalysisCoverage&,
+                                                  const GrCaps&) const = 0;
 };
 #if defined(__GNUC__) || defined(__clang)
 #pragma GCC diagnostic pop
 #endif
 
-#endif
+GR_MAKE_BITFIELD_CLASS_OPS(GrXPFactory::AnalysisProperties);
 
+#endif
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index cfd27d0..6b51303 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -121,7 +121,7 @@
     }
 
     // We never want to perform color-space conversion during the decode
-    sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext(
                                                                           SkBackingFit::kExact,
                                                                           desc.fWidth, desc.fHeight,
                                                                           desc.fConfig, nullptr,
diff --git a/src/gpu/GrYUVProvider.h b/src/gpu/GrYUVProvider.h
index af5a4a4..b5be426 100644
--- a/src/gpu/GrYUVProvider.h
+++ b/src/gpu/GrYUVProvider.h
@@ -19,7 +19,7 @@
 /**
  *  There are at least 2 different ways to extract/retrieve YUV planar data...
  *  - SkPixelRef
- *  - SkImageGeneartor
+ *  - SkImageGenerator
  *
  *  To share common functionality around using the planar data, we use this abstract base-class
  *  to represent accessing that data.
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 4e16d59..6585e85 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -22,7 +22,6 @@
 #include "SkDraw.h"
 #include "SkGlyphCache.h"
 #include "SkGr.h"
-#include "SkImageCacherator.h"
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkImageInfoPriv.h"
@@ -48,6 +47,7 @@
 #include "effects/GrBicubicEffect.h"
 #include "effects/GrSimpleTextureEffect.h"
 #include "effects/GrTextureDomain.h"
+#include "../private/SkShadowFlags.h"
 #include "text/GrTextUtils.h"
 
 #if SK_SUPPORT_GPU
@@ -168,7 +168,8 @@
     GrPixelConfig config = SkImageInfo2GrPixelConfig(origInfo, *context->caps());
     // This method is used to create SkGpuDevice's for SkSurface_Gpus. In this case
     // they need to be exact.
-    return context->makeRenderTargetContext(SkBackingFit::kExact,
+    return context->makeDeferredRenderTargetContext(
+                                    SkBackingFit::kExact,
                                     origInfo.width(), origInfo.height(),
                                     config, origInfo.refColorSpace(), sampleCount,
                                     origin, surfaceProps, budgeted);
@@ -388,16 +389,12 @@
         return;
     }
 
-    fRenderTargetContext->drawVertices(this->clip(),
-                                       std::move(grPaint),
-                                       *viewMatrix,
-                                       primitiveType,
-                                       SkToS32(count),
-                                       (SkPoint*)pts,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       0);
+    static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
+    sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, SkToS32(count), pts, nullptr,
+                                                      nullptr);
+
+    fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), *viewMatrix,
+                                       std::move(vertices), &primitiveType);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -862,9 +859,8 @@
 
     // The tile code path doesn't currently support AA, so if the paint asked for aa and we could
     // draw untiled, then we bypass checking for tiling purely for optimization reasons.
-    bool drawAA = !fRenderTargetContext->isUnifiedMultisampled() &&
-                  paint.isAntiAlias() &&
-                  bitmap.width() <= maxTileSize &&
+    bool drawAA = GrFSAAType::kUnifiedMSAA != fRenderTargetContext->fsaaType() &&
+                  paint.isAntiAlias() && bitmap.width() <= maxTileSize &&
                   bitmap.height() <= maxTileSize;
 
     bool skipTileCheck = drawAA || paint.getMaskFilter();
@@ -957,15 +953,9 @@
     SK_HISTOGRAM_BOOLEAN("DrawTiled", true);
     LogDrawScaleFactor(viewMatrix, origPaint.getFilterQuality());
 
-    // The following pixel lock is technically redundant, but it is desirable
-    // to lock outside of the tile loop to prevent redecoding the whole image
-    // at each tile in cases where 'bitmap' holds an SkDiscardablePixelRef that
-    // is larger than the limit of the discardable memory pool.
-    SkAutoLockPixels alp(bitmap);
-
     const SkPaint* paint = &origPaint;
     SkPaint tempPaint;
-    if (origPaint.isAntiAlias() && !fRenderTargetContext->isUnifiedMultisampled()) {
+    if (origPaint.isAntiAlias() && GrFSAAType::kUnifiedMSAA != fRenderTargetContext->fsaaType()) {
         // Drop antialiasing to avoid seams at tile boundaries.
         tempPaint = origPaint;
         tempPaint.setAntiAlias(false);
@@ -1112,7 +1102,7 @@
 
     // Coverage-based AA would cause seams between tiles.
     GrAA aa = GrBoolToAA(paint.isAntiAlias() &&
-                         fRenderTargetContext->isStencilBufferMultisampled());
+                         GrFSAAType::kNone != fRenderTargetContext->fsaaType());
     fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix, dstRect);
 }
 
@@ -1131,17 +1121,18 @@
         return;
     }
 
-    this->drawSpecial(srcImg.get(), left, top, paint);
+    this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
 }
 
 
-void SkGpuDevice::drawSpecial(SkSpecialImage* special1,
-                              int left, int top,
-                              const SkPaint& paint) {
+void SkGpuDevice::drawSpecial(SkSpecialImage* special1, int left, int top, const SkPaint& paint,
+                              SkImage* clipImage,const SkMatrix& clipMatrix) {
     ASSERT_SINGLE_OWNER
     CHECK_SHOULD_DRAW();
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext.get());
 
+    // TODO: clipImage support.
+
     SkIPoint offset = { 0, 0 };
 
     sk_sp<SkSpecialImage> result;
@@ -1236,12 +1227,11 @@
 
     // The tile code path doesn't currently support AA, so if the paint asked for aa and we could
     // draw untiled, then we bypass checking for tiling purely for optimization reasons.
-    bool drawAA = !fRenderTargetContext->isUnifiedMultisampled() &&
-        paint.isAntiAlias() &&
-        bitmap.width() <= maxTileSize &&
-        bitmap.height() <= maxTileSize;
+    bool useCoverageAA = GrFSAAType::kUnifiedMSAA != fRenderTargetContext->fsaaType() &&
+                         paint.isAntiAlias() && bitmap.width() <= maxTileSize &&
+                         bitmap.height() <= maxTileSize;
 
-    bool skipTileCheck = drawAA || paint.getMaskFilter();
+    bool skipTileCheck = useCoverageAA || paint.getMaskFilter();
 
     if (!skipTileCheck) {
         int tileSize;
@@ -1335,7 +1325,6 @@
     const SkImageInfo ii = this->imageInfo();
     const SkIRect srcRect = SkIRect::MakeWH(ii.width(), ii.height());
 
-    SkASSERT(proxy->priv().isExact());
     return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
                                                srcRect,
                                                kNeedNewImageUniqueID_SpecialImage,
@@ -1361,7 +1350,7 @@
         return;
     }
 
-    this->drawSpecial(srcImg.get(), left, top, paint);
+    this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
 }
 
 void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y,
@@ -1388,9 +1377,9 @@
                 return;
             }
             this->drawBitmap(bm, SkMatrix::MakeTrans(x, y), paint);
-        } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
+        } else if (image->isLazyGenerated()) {
             CHECK_SHOULD_DRAW();
-            GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+            GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
                                       viewMatrix, this->clip(), paint);
         } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
@@ -1424,9 +1413,9 @@
             return;
         }
         this->drawBitmapRect(bm, src, dst, paint, constraint);
-    } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
+    } else if (image->isLazyGenerated()) {
         CHECK_SHOULD_DRAW();
-        GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+        GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
         this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), this->clip(), paint);
     } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
         this->drawBitmapRect(bm, src, dst, paint, constraint);
@@ -1440,7 +1429,7 @@
     CHECK_SHOULD_DRAW();
 
     bool useFallback = paint.getMaskFilter() || paint.isAntiAlias() ||
-                       fRenderTargetContext->isUnifiedMultisampled();
+                       GrFSAAType::kUnifiedMSAA == fRenderTargetContext->fsaaType();
     bool doBicubic;
     GrSamplerParams::FilterMode textureFilterMode =
         GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), this->ctm(), SkMatrix::I(),
@@ -1488,8 +1477,8 @@
         this->drawProducerNine(&adjuster, center, dst, paint);
     } else {
         SkBitmap bm;
-        if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
-            GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+        if (image->isLazyGenerated()) {
+            GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawProducerNine(&maker, center, dst, paint);
         } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
             this->drawBitmapNine(bm, center, dst, paint);
@@ -1544,8 +1533,8 @@
         this->drawProducerLattice(&adjuster, lattice, dst, paint);
     } else {
         SkBitmap bm;
-        if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
-            GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+        if (image->isLazyGenerated()) {
+            GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawProducerLattice(&maker, lattice, dst, paint);
         } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
             this->drawBitmapLattice(bm, lattice, dst, paint);
@@ -1568,8 +1557,7 @@
     if (hasTexs && skPaint.getShader()) {
         if (hasColors) {
             // When there are texs and colors the shader and colors are combined using bmode.
-            return SkPaintToGrPaintWithXfermode(context, rtc, skPaint, matrix, bmode, false,
-                                                grPaint);
+            return SkPaintToGrPaintWithXfermode(context, rtc, skPaint, matrix, bmode, grPaint);
         } else {
             // We have a shader, but no colors to blend it against.
             return SkPaintToGrPaint(context, rtc, skPaint, matrix, grPaint);
@@ -1581,12 +1569,12 @@
             return SkPaintToGrPaintWithPrimitiveColor(context, rtc, skPaint, grPaint);
         } else {
             // No colors and no shaders. Just draw with the paint color.
-            return (!SkPaintToGrPaintNoShader(context, rtc, skPaint, grPaint));
+            return SkPaintToGrPaintNoShader(context, rtc, skPaint, grPaint);
         }
     }
 }
 
-void SkGpuDevice::wireframeVertices(SkCanvas::VertexMode vmode, int vertexCount,
+void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCount,
                                     const SkPoint vertices[], SkBlendMode bmode,
                                     const uint16_t indices[], int indexCount,
                                     const SkPaint& paint) {
@@ -1607,11 +1595,11 @@
     int triangleCount = 0;
     int n = (nullptr == indices) ? vertexCount : indexCount;
     switch (vmode) {
-        case SkCanvas::kTriangles_VertexMode:
+        case SkVertices::kTriangles_VertexMode:
             triangleCount = n / 3;
             break;
-        case SkCanvas::kTriangleStrip_VertexMode:
-        case SkCanvas::kTriangleFan_VertexMode:
+        case SkVertices::kTriangleStrip_VertexMode:
+        case SkVertices::kTriangleFan_VertexMode:
             triangleCount = n - 2;
             break;
     }
@@ -1622,7 +1610,11 @@
     //number of indices for lines per triangle with kLines
     indexCount = triangleCount * 6;
 
-    std::unique_ptr<uint16_t[]> lineIndices(new uint16_t[indexCount]);
+    static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
+    SkVertices::Builder builder(kIgnoredMode, vertexCount, indexCount, 0);
+    memcpy(builder.positions(), vertices, vertexCount * sizeof(SkPoint));
+
+    uint16_t* lineIndices = builder.indices();
     int i = 0;
     while (vertProc(&state)) {
         lineIndices[i]     = state.f0;
@@ -1633,20 +1625,16 @@
         lineIndices[i + 5] = state.f0;
         i += 6;
     }
+
+    GrPrimitiveType primitiveType = kLines_GrPrimitiveType;
     fRenderTargetContext->drawVertices(this->clip(),
                                        std::move(grPaint),
                                        this->ctm(),
-                                       kLines_GrPrimitiveType,
-                                       vertexCount,
-                                       vertices,
-                                       nullptr,
-                                       nullptr,
-                                       lineIndices.get(),
-                                       indexCount);
+                                       builder.detach(),
+                                       &primitiveType);
 }
 
-void SkGpuDevice::drawVertices(const SkVertices* vertices, SkBlendMode mode,
-                                     const SkPaint& paint) {
+void SkGpuDevice::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     CHECK_SHOULD_DRAW();
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext.get());
@@ -1655,10 +1643,11 @@
     GrPaint grPaint;
     bool hasColors = vertices->hasColors();
     bool hasTexs = vertices->hasTexCoords();
-    if (!hasTexs && !hasColors) {
+    if ((!hasTexs || !paint.getShader()) && !hasColors) {
         // The dreaded wireframe mode. Fallback to drawVertices and go so slooooooow.
         this->wireframeVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
                                 mode, vertices->indices(), vertices->indexCount(), paint);
+        return;
     }
     if (!init_vertices_paint(fContext.get(), fRenderTargetContext.get(), paint, this->ctm(),
                              mode, hasTexs, hasColors, &grPaint)) {
@@ -1670,6 +1659,29 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+void SkGpuDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
+
+    ASSERT_SINGLE_OWNER
+    CHECK_SHOULD_DRAW();
+    GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawShadow", fContext.get());
+
+    SkPaint p;
+    p.setColor(rec.fColor);
+    GrPaint grPaint;
+    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext.get(), p, this->ctm(),
+                          &grPaint)) {
+        return;
+    }
+
+    if (!fRenderTargetContext->drawFastShadow(this->clip(), std::move(grPaint),
+                                              this->ctm(), path, rec)) {
+        // failed to find an accelerated case
+        this->INHERITED::drawShadow(path, rec);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void SkGpuDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[],
                             const SkRect texRect[], const SkColor colors[], int count,
                             SkBlendMode mode, const SkPaint& paint) {
@@ -1683,12 +1695,12 @@
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawText", fContext.get());
 
     SkPaint p(paint);
-    p.setShader(atlas->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode));
+    p.setShader(atlas->makeShader());
 
     GrPaint grPaint;
     if (colors) {
         if (!SkPaintToGrPaintWithXfermode(this->context(), fRenderTargetContext.get(), p,
-                                          this->ctm(), (SkBlendMode)mode, true, &grPaint)) {
+                                          this->ctm(), (SkBlendMode)mode, &grPaint)) {
             return;
         }
     } else {
@@ -1766,13 +1778,13 @@
     SkBackingFit fit = kNever_TileUsage == cinfo.fTileUsage ? SkBackingFit::kApprox
                                                             : SkBackingFit::kExact;
 
-    sk_sp<GrRenderTargetContext> rtc(fContext->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> rtc(fContext->makeDeferredRenderTargetContext(
                                                    fit,
                                                    cinfo.fInfo.width(), cinfo.fInfo.height(),
-                                                   fRenderTargetContext->config(), 
-                                                   fRenderTargetContext->refColorSpace(), 
-                                                   fRenderTargetContext->desc().fSampleCnt, 
-                                                   kDefault_GrSurfaceOrigin,
+                                                   fRenderTargetContext->config(),
+                                                   fRenderTargetContext->refColorSpace(),
+                                                   fRenderTargetContext->numStencilSamples(),
+                                                   kBottomLeft_GrSurfaceOrigin,
                                                    &props));
     if (!rtc) {
         return nullptr;
@@ -1790,7 +1802,7 @@
     // TODO: Change the signature of newSurface to take a budgeted parameter.
     static const SkBudgeted kBudgeted = SkBudgeted::kNo;
     return SkSurface::MakeRenderTarget(fContext.get(), kBudgeted, info,
-                                       fRenderTargetContext->desc().fSampleCnt,
+                                       fRenderTargetContext->numStencilSamples(),
                                        fRenderTargetContext->origin(), &props);
 }
 
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index b40eadd..0b60a96 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -92,6 +92,7 @@
     void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
                       const SkPaint& paint, SkDrawFilter* drawFilter) override;
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
+    void drawShadow(const SkPath&, const SkDrawShadowRec&) override;
     void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
                    const SkColor[], int count, SkBlendMode, const SkPaint&) override;
     void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override;
@@ -110,8 +111,8 @@
     void drawBitmapLattice(const SkBitmap&, const SkCanvas::Lattice&,
                            const SkRect& dst, const SkPaint&) override;
 
-    void drawSpecial(SkSpecialImage*,
-                     int left, int top, const SkPaint& paint) override;
+    void drawSpecial(SkSpecialImage*, int left, int top, const SkPaint& paint,
+                     SkImage*, const SkMatrix&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
     sk_sp<SkSpecialImage> snapSpecial() override;
@@ -230,7 +231,7 @@
     bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
     void drawStrokedLine(const SkPoint pts[2], const SkPaint&);
 
-    void wireframeVertices(SkCanvas::VertexMode, int vertexCount, const SkPoint verts[],
+    void wireframeVertices(SkVertices::VertexMode, int vertexCount, const SkPoint verts[],
                            SkBlendMode, const uint16_t indices[], int indexCount, const SkPaint&);
 
     static sk_sp<GrRenderTargetContext> MakeRenderTargetContext(GrContext*,
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index f0dbeb9..48015a3 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -42,12 +42,12 @@
 static bool may_color_bleed(const SkRect& srcRect,
                             const SkRect& transformedRect,
                             const SkMatrix& m,
-                            bool isMSAA) {
+                            GrFSAAType fsaaType) {
     // Only gets called if has_aligned_samples returned false.
     // So we can assume that sampling is axis aligned but not texel aligned.
     SkASSERT(!has_aligned_samples(srcRect, transformedRect));
     SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
-    if (isMSAA) {
+    if (GrFSAAType::kUnifiedMSAA == fsaaType) {
         innerSrcRect.inset(SK_Scalar1, SK_Scalar1);
     } else {
         innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
@@ -71,14 +71,14 @@
 static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
                                          const SkRect& srcRect,
                                          const SkMatrix& srcRectToDeviceSpace,
-                                         bool isMSAA) {
+                                         GrFSAAType fsaaType) {
     if (srcRectToDeviceSpace.rectStaysRect()) {
         // sampling is axis-aligned
         SkRect transformedRect;
         srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
 
         if (has_aligned_samples(srcRect, transformedRect) ||
-            !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, isMSAA)) {
+            !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) {
             return true;
         }
     }
@@ -186,7 +186,7 @@
         SkMatrix combinedMatrix;
         combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
         if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
-                                         fRenderTargetContext->isUnifiedMultisampled())) {
+                                         fRenderTargetContext->fsaaType())) {
             constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
         }
     }
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 56ce545..fd9be57 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -30,6 +30,7 @@
 #include "SkPM4fPriv.h"
 #include "SkPixelRef.h"
 #include "SkResourceCache.h"
+#include "SkShaderBase.h"
 #include "SkTemplates.h"
 #include "effects/GrBicubicEffect.h"
 #include "effects/GrConstColorProcessor.h"
@@ -37,10 +38,6 @@
 #include "effects/GrPorterDuffXferProcessor.h"
 #include "effects/GrXfermodeFragmentProcessor.h"
 
-#ifndef SK_IGNORE_ETC1_SUPPORT
-#  include "etc1.h"
-#endif
-
 GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo& info, const GrCaps& caps) {
     GrSurfaceDesc desc;
     desc.fFlags = kNone_GrSurfaceFlags;
@@ -64,36 +61,10 @@
     builder[4] = imageBounds.fBottom;
 }
 
-GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
-                                                 int expectedW, int expectedH,
-                                                 const void** outStartOfDataToUpload) {
-    *outStartOfDataToUpload = nullptr;
-#ifndef SK_IGNORE_ETC1_SUPPORT
-    if (!ctx->caps()->isConfigTexturable(kETC1_GrPixelConfig)) {
-        return kUnknown_GrPixelConfig;
-    }
-
-    const uint8_t* bytes = data->bytes();
-    if (data->size() > ETC_PKM_HEADER_SIZE && etc1_pkm_is_valid(bytes)) {
-        // Does the data match the dimensions of the bitmap? If not,
-        // then we don't know how to scale the image to match it...
-        if (etc1_pkm_get_width(bytes) != (unsigned)expectedW ||
-            etc1_pkm_get_height(bytes) != (unsigned)expectedH)
-        {
-            return kUnknown_GrPixelConfig;
-        }
-
-        *outStartOfDataToUpload = bytes + ETC_PKM_HEADER_SIZE;
-        return kETC1_GrPixelConfig;
-    }
-#endif
-    return kUnknown_GrPixelConfig;
-}
-
 //////////////////////////////////////////////////////////////////////////////
 sk_sp<GrTextureProxy> GrUploadBitmapToTextureProxy(GrResourceProvider* resourceProvider,
-                                                   const SkBitmap& bitmap) {
-    SkAutoLockPixels alp(bitmap);
+                                                   const SkBitmap& bitmap,
+                                                   SkColorSpace* dstColorSpace) {
     if (!bitmap.readyToDraw()) {
         return nullptr;
     }
@@ -101,7 +72,7 @@
     if (!bitmap.peekPixels(&pixmap)) {
         return nullptr;
     }
-    return GrUploadPixmapToTextureProxy(resourceProvider, pixmap, SkBudgeted::kYes);
+    return GrUploadPixmapToTextureProxy(resourceProvider, pixmap, SkBudgeted::kYes, dstColorSpace);
 }
 
 static const SkPixmap* compute_desc(const GrCaps& caps, const SkPixmap& pixmap,
@@ -160,8 +131,13 @@
 
 sk_sp<GrTextureProxy> GrUploadPixmapToTextureProxy(GrResourceProvider* resourceProvider,
                                                    const SkPixmap& pixmap,
-                                                   SkBudgeted budgeted) {
-    if (!SkImageInfoIsValid(pixmap.info())) {
+                                                   SkBudgeted budgeted,
+                                                   SkColorSpace* dstColorSpace) {
+    SkDestinationSurfaceColorMode colorMode = dstColorSpace
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
+
+    if (!SkImageInfoIsValid(pixmap.info(), colorMode)) {
         return nullptr;
     }
 
@@ -200,21 +176,16 @@
         ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
         : SkDestinationSurfaceColorMode::kLegacy;
 
-    if (!SkImageInfoIsValid(bitmap.info())) {
+    if (!SkImageInfoIsValid(bitmap.info(), colorMode)) {
         return nullptr;
     }
 
     GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap.info(), *ctx->caps());
 
-    SkAutoPixmapUnlock srcUnlocker;
-    if (!bitmap.requestLock(&srcUnlocker)) {
+    SkPixmap pixmap;
+    if (!bitmap.peekPixels(&pixmap)) {
         return nullptr;
     }
-    const SkPixmap& pixmap = srcUnlocker.pixmap();
-    // Try to catch where we might have returned nullptr for src crbug.com/492818
-    if (nullptr == pixmap.addr()) {
-        sk_throw();
-    }
 
     std::unique_ptr<SkMipMap> mipmaps(SkMipMap::Build(pixmap, colorMode, nullptr));
     if (!mipmaps) {
@@ -241,29 +212,25 @@
         texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
     }
 
-    sk_sp<GrTexture> tex(ctx->resourceProvider()->createMipMappedTexture(desc,
-                                                                         SkBudgeted::kYes,
-                                                                         texels.get(),
-                                                                         mipLevelCount,
-                                                                         0, colorMode));
-
-    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+    return ctx->resourceProvider()->createMipMappedTexture(desc,
+                                                           SkBudgeted::kYes,
+                                                           texels.get(),
+                                                           mipLevelCount,
+                                                           colorMode);
 }
 
 sk_sp<GrTextureProxy> GrUploadMipMapToTextureProxy(GrContext* ctx, const SkImageInfo& info,
                                                    const GrMipLevel* texels,
                                                    int mipLevelCount,
                                                    SkDestinationSurfaceColorMode colorMode) {
-    if (!SkImageInfoIsValid(info)) {
+    if (!SkImageInfoIsValid(info, colorMode)) {
         return nullptr;
     }
 
     const GrCaps* caps = ctx->caps();
-    sk_sp<GrTexture> tex(ctx->resourceProvider()->createMipMappedTexture(
-                                                           GrImageInfoToSurfaceDesc(info, *caps),
+    return ctx->resourceProvider()->createMipMappedTexture(GrImageInfoToSurfaceDesc(info, *caps),
                                                            SkBudgeted::kYes, texels,
-                                                           mipLevelCount, 0, colorMode));
-    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+                                                           mipLevelCount, colorMode);
 }
 
 sk_sp<GrTextureProxy> GrRefCachedBitmapTextureProxy(GrContext* ctx,
@@ -291,7 +258,10 @@
         proxy = resourceProvider->findProxyByUniqueKey(originalKey);
     }
     if (!proxy) {
-        proxy = GrUploadBitmapToTextureProxy(resourceProvider, bitmap);
+        // Pass nullptr for |dstColorSpace|.  This is lenient - we allow a wider range of
+        // color spaces in legacy mode.  Unfortunately, we have to be lenient here, since
+        // we can't necessarily know the |dstColorSpace| at this time.
+        proxy = GrUploadBitmapToTextureProxy(resourceProvider, bitmap, nullptr);
         if (proxy && originalKey.isValid()) {
             resourceProvider->assignUniqueKeyToProxy(originalKey, proxy.get());
             // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching
@@ -432,12 +402,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////
 
-static inline bool blend_requires_shader(const SkBlendMode mode, bool primitiveIsSrc) {
-    if (primitiveIsSrc) {
-        return SkBlendMode::kSrc != mode;
-    } else {
-        return SkBlendMode::kDst != mode;
-    }
+static inline bool blend_requires_shader(const SkBlendMode mode) {
+    return SkBlendMode::kDst != mode;
 }
 
 static inline bool skpaint_to_grpaint_impl(GrContext* context,
@@ -446,7 +412,6 @@
                                            const SkMatrix& viewM,
                                            sk_sp<GrFragmentProcessor>* shaderProcessor,
                                            SkBlendMode* primColorMode,
-                                           bool primitiveIsSrc,
                                            GrPaint* grPaint) {
     grPaint->setAllowSRGBInputs(rtc->isGammaCorrect());
 
@@ -457,13 +422,13 @@
     // Setup the initial color considering the shader, the SkPaint color, and the presence or not
     // of per-vertex colors.
     sk_sp<GrFragmentProcessor> shaderFP;
-    if (!primColorMode || blend_requires_shader(*primColorMode, primitiveIsSrc)) {
+    if (!primColorMode || blend_requires_shader(*primColorMode)) {
         if (shaderProcessor) {
             shaderFP = *shaderProcessor;
-        } else if (const SkShader* shader = skPaint.getShader()) {
-            shaderFP = shader->asFragmentProcessor(SkShader::AsFPArgs(context, &viewM, nullptr,
-                                                                      skPaint.getFilterQuality(),
-                                                                      rtc->getColorSpace()));
+        } else if (const auto* shader = as_SB(skPaint.getShader())) {
+            shaderFP = shader->asFragmentProcessor(
+                SkShaderBase::AsFPArgs(context, &viewM, nullptr, skPaint.getFilterQuality(),
+                                       rtc->getColorSpace()));
             if (!shaderFP) {
                 return false;
             }
@@ -485,13 +450,9 @@
 
             GrColor4f shaderInput = origColor.opaque();
             shaderFP = GrFragmentProcessor::OverrideInput(shaderFP, shaderInput);
-            if (primitiveIsSrc) {
-                shaderFP = GrXfermodeFragmentProcessor::MakeFromDstProcessor(std::move(shaderFP),
-                                                                             *primColorMode);
-            } else {
-                shaderFP = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(shaderFP),
-                                                                             *primColorMode);
-            }
+            shaderFP = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(shaderFP),
+                                                                         *primColorMode);
+
             // The above may return null if compose results in a pass through of the prim color.
             if (shaderFP) {
                 grPaint->addColorFragmentProcessor(shaderFP);
@@ -518,13 +479,8 @@
             sk_sp<GrFragmentProcessor> processor(
                 GrConstColorProcessor::Make(origColor.opaque(),
                                             GrConstColorProcessor::kIgnore_InputMode));
-            if (primitiveIsSrc) {
-                processor = GrXfermodeFragmentProcessor::MakeFromDstProcessor(std::move(processor),
-                                                                              *primColorMode);
-            } else {
-                processor = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(processor),
-                                                                              *primColorMode);
-            }
+            processor = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(processor),
+                                                                          *primColorMode);
             if (processor) {
                 grPaint->addColorFragmentProcessor(std::move(processor));
             }
@@ -595,7 +551,7 @@
 
 bool SkPaintToGrPaint(GrContext* context, GrRenderTargetContext* rtc, const SkPaint& skPaint,
                       const SkMatrix& viewM, GrPaint* grPaint) {
-    return skpaint_to_grpaint_impl(context, rtc, skPaint, viewM, nullptr, nullptr, false, grPaint);
+    return skpaint_to_grpaint_impl(context, rtc, skPaint, viewM, nullptr, nullptr, grPaint);
 }
 
 /** Replaces the SkShader (if any) on skPaint with the passed in GrFragmentProcessor. */
@@ -607,7 +563,7 @@
     if (!shaderFP) {
         return false;
     }
-    return skpaint_to_grpaint_impl(context, rtc, skPaint, SkMatrix::I(), &shaderFP, nullptr, false,
+    return skpaint_to_grpaint_impl(context, rtc, skPaint, SkMatrix::I(), &shaderFP, nullptr,
                                    grPaint);
 }
 
@@ -620,7 +576,7 @@
     static sk_sp<GrFragmentProcessor> kNullShaderFP(nullptr);
     static sk_sp<GrFragmentProcessor>* kIgnoreShader = &kNullShaderFP;
     return skpaint_to_grpaint_impl(context, rtc, skPaint, SkMatrix::I(), kIgnoreShader, nullptr,
-                                   false, grPaint);
+                                   grPaint);
 }
 
 /** Blends the SkPaint's shader (or color if no shader) with a per-primitive color which must
@@ -630,10 +586,9 @@
                                   const SkPaint& skPaint,
                                   const SkMatrix& viewM,
                                   SkBlendMode primColorMode,
-                                  bool primitiveIsSrc,
                                   GrPaint* grPaint) {
     return skpaint_to_grpaint_impl(context, rtc, skPaint, viewM, nullptr, &primColorMode,
-                                   primitiveIsSrc, grPaint);
+                                   grPaint);
 }
 
 bool SkPaintToGrPaintWithTexture(GrContext* context,
@@ -645,12 +600,10 @@
                                  GrPaint* grPaint) {
     sk_sp<GrFragmentProcessor> shaderFP;
     if (textureIsAlphaOnly) {
-        if (const SkShader* shader = paint.getShader()) {
-            shaderFP = shader->asFragmentProcessor(SkShader::AsFPArgs(context,
-                                                                      &viewM,
-                                                                      nullptr,
-                                                                      paint.getFilterQuality(),
-                                                                      rtc->getColorSpace()));
+        if (const auto* shader = as_SB(paint.getShader())) {
+            shaderFP = shader->asFragmentProcessor(
+                SkShaderBase::AsFPArgs(context, &viewM, nullptr, paint.getFilterQuality(),
+                                       rtc->getColorSpace()));
             if (!shaderFP) {
                 return false;
             }
diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h
index 5a7e1e2..7232c80 100644
--- a/src/gpu/SkGr.h
+++ b/src/gpu/SkGr.h
@@ -19,7 +19,8 @@
 #include "SkImageInfo.h"
 #include "SkMatrix.h"
 #include "SkPM4f.h"
-#include "SkXfermodePriv.h"
+#include "SkVertices.h"
+#include "SkBlendModePriv.h"
 
 class GrCaps;
 class GrColorSpaceXform;
@@ -118,15 +119,12 @@
                                    GrPaint* grPaint);
 
 /** Blends the SkPaint's shader (or color if no shader) with the color which specified via a
-    GrOp's GrPrimitiveProcesssor. Currently there is a bool param to indicate whether the
-    primitive color is the dst or src color to the blend in order to work around differences between
-    drawVertices and drawAtlas. */
+    GrOp's GrPrimitiveProcesssor. */
 bool SkPaintToGrPaintWithXfermode(GrContext* context,
                                   GrRenderTargetContext* rtc,
                                   const SkPaint& skPaint,
                                   const SkMatrix& viewM,
                                   SkBlendMode primColorMode,
-                                  bool primitiveIsSrc,
                                   GrPaint* grPaint);
 
 /** This is used when there is a primitive color, but the shader should be ignored. Currently,
@@ -136,7 +134,7 @@
 inline bool SkPaintToGrPaintWithPrimitiveColor(GrContext* context, GrRenderTargetContext* rtc,
                                                const SkPaint& skPaint, GrPaint* grPaint) {
     return SkPaintToGrPaintWithXfermode(context, rtc, skPaint, SkMatrix::I(), SkBlendMode::kDst,
-                                        false, grPaint);
+                                        grPaint);
 }
 
 /** This is used when there may or may not be a shader, and the caller wants to plugin a texture
@@ -164,13 +162,13 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-static inline GrPrimitiveType SkVertexModeToGrPrimitiveType(const SkCanvas::VertexMode mode) {
+static inline GrPrimitiveType SkVertexModeToGrPrimitiveType(SkVertices::VertexMode mode) {
     switch (mode) {
-        case SkCanvas::kTriangles_VertexMode:
+        case SkVertices::kTriangles_VertexMode:
             return kTriangles_GrPrimitiveType;
-        case SkCanvas::kTriangleStrip_VertexMode:
+        case SkVertices::kTriangleStrip_VertexMode:
             return kTriangleStrip_GrPrimitiveType;
-        case SkCanvas::kTriangleFan_VertexMode:
+        case SkVertices::kTriangleFan_VertexMode:
             return kTriangleFan_GrPrimitiveType;
     }
     SkFAIL("Invalid mode");
@@ -179,17 +177,17 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-GR_STATIC_ASSERT((int)kZero_GrBlendCoeff == (int)SkXfermode::kZero_Coeff);
-GR_STATIC_ASSERT((int)kOne_GrBlendCoeff == (int)SkXfermode::kOne_Coeff);
-GR_STATIC_ASSERT((int)kSC_GrBlendCoeff == (int)SkXfermode::kSC_Coeff);
-GR_STATIC_ASSERT((int)kISC_GrBlendCoeff == (int)SkXfermode::kISC_Coeff);
-GR_STATIC_ASSERT((int)kDC_GrBlendCoeff == (int)SkXfermode::kDC_Coeff);
-GR_STATIC_ASSERT((int)kIDC_GrBlendCoeff == (int)SkXfermode::kIDC_Coeff);
-GR_STATIC_ASSERT((int)kSA_GrBlendCoeff == (int)SkXfermode::kSA_Coeff);
-GR_STATIC_ASSERT((int)kISA_GrBlendCoeff == (int)SkXfermode::kISA_Coeff);
-GR_STATIC_ASSERT((int)kDA_GrBlendCoeff == (int)SkXfermode::kDA_Coeff);
-GR_STATIC_ASSERT((int)kIDA_GrBlendCoeff == (int)SkXfermode::kIDA_Coeff);
-GR_STATIC_ASSERT(SkXfermode::kCoeffCount == 10);
+GR_STATIC_ASSERT((int)kZero_GrBlendCoeff == (int)SkBlendModeCoeff::kZero);
+GR_STATIC_ASSERT((int)kOne_GrBlendCoeff == (int)SkBlendModeCoeff::kOne);
+GR_STATIC_ASSERT((int)kSC_GrBlendCoeff == (int)SkBlendModeCoeff::kSC);
+GR_STATIC_ASSERT((int)kISC_GrBlendCoeff == (int)SkBlendModeCoeff::kISC);
+GR_STATIC_ASSERT((int)kDC_GrBlendCoeff == (int)SkBlendModeCoeff::kDC);
+GR_STATIC_ASSERT((int)kIDC_GrBlendCoeff == (int)SkBlendModeCoeff::kIDC);
+GR_STATIC_ASSERT((int)kSA_GrBlendCoeff == (int)SkBlendModeCoeff::kSA);
+GR_STATIC_ASSERT((int)kISA_GrBlendCoeff == (int)SkBlendModeCoeff::kISA);
+GR_STATIC_ASSERT((int)kDA_GrBlendCoeff == (int)SkBlendModeCoeff::kDA);
+GR_STATIC_ASSERT((int)kIDA_GrBlendCoeff == (int)SkBlendModeCoeff::kIDA);
+//GR_STATIC_ASSERT(SkXfermode::kCoeffCount == 10);
 
 #define SkXfermodeCoeffToGrBlendCoeff(X) ((GrBlendCoeff)(X))
 
@@ -211,9 +209,10 @@
 /**
  * Creates a new texture for the bitmap. Does not concern itself with cache keys or texture params.
  * The bitmap must have CPU-accessible pixels. Attempts to take advantage of faster paths for
- * compressed textures and yuv planes.
+ * yuv planes.
  */
-sk_sp<GrTextureProxy> GrUploadBitmapToTextureProxy(GrResourceProvider*, const SkBitmap&);
+sk_sp<GrTextureProxy> GrUploadBitmapToTextureProxy(GrResourceProvider*, const SkBitmap&,
+                                                   SkColorSpace* dstColorSpace);
 
 sk_sp<GrTextureProxy> GrGenerateMipMapsAndUploadToTextureProxy(GrContext*, const SkBitmap&,
                                                                SkColorSpace* dstColorSpace);
@@ -222,7 +221,7 @@
  * Creates a new texture for the pixmap.
  */
 sk_sp<GrTextureProxy> GrUploadPixmapToTextureProxy(GrResourceProvider*,
-                                                   const SkPixmap&, SkBudgeted);
+                                                   const SkPixmap&, SkBudgeted, SkColorSpace*);
 
 /**
  * Creates a new texture populated with the mipmap levels.
@@ -271,18 +270,4 @@
     to use. */
 GrPixelConfig GrRenderableConfigForColorSpace(const SkColorSpace*);
 
-/**
- *  If the compressed data in the SkData is supported (as a texture format, this returns
- *  the pixel-config that should be used, and sets outStartOfDataToUpload to the ptr into
- *  the data where the actual raw data starts (skipping any header bytes).
- *
- *  If the compressed data is not supported, this returns kUnknown_GrPixelConfig, and
- *  ignores outStartOfDataToUpload.
- */
-GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
-                                                 int expectedW, int expectedH,
-                                                 const void** outStartOfDataToUpload);
-
-
-
 #endif
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index b418096..7d1eba3 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -27,7 +27,7 @@
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
@@ -112,15 +112,15 @@
         fragBuilder->appendColorGamutXform(&xformedColor, bicubicColor.c_str(), &fColorSpaceHelper);
         bicubicColor.swap(xformedColor);
     }
-    fragBuilder->codeAppendf("%s = %s;",
-                             args.fOutputColor, (GrGLSLExpr4(bicubicColor.c_str()) *
-                                                 GrGLSLExpr4(args.fInputColor)).c_str());
+    fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputColor, bicubicColor.c_str(),
+                             args.fInputColor);
 }
 
 void GrGLBicubicEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                  const GrProcessor& processor) {
+                                  const GrFragmentProcessor& processor) {
     const GrBicubicEffect& bicubicEffect = processor.cast<GrBicubicEffect>();
-    GrTexture* texture = processor.textureSampler(0).texture();
+    GrTexture* texture = processor.textureSampler(0).peekTexture();
+
     float imageIncrement[2];
     imageIncrement[0] = 1.0f / texture->width();
     imageIncrement[1] = 1.0f / texture->height();
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index 527d5ce..eb9b7b8 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -31,7 +31,7 @@
 
         // compute numbers to be hardcoded to convert texture coordinates from int to float
         SkASSERT(cte.numTextureSamplers() == 1);
-        SkDEBUGCODE(GrTexture* atlas = cte.textureSampler(0).texture());
+        SkDEBUGCODE(GrTexture* atlas = cte.textureSampler(0).peekTexture());
         SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
 
         GrGLSLVertToFrag v(kVec2f_GrSLType);
@@ -72,12 +72,6 @@
             fragBuilder->codeAppendf("%s = ", args.fOutputCoverage);
             fragBuilder->appendTextureLookup(args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType);
             fragBuilder->codeAppend(";");
-            if (cte.maskFormat() == kA565_GrMaskFormat) {
-                // set alpha to be max of rgb coverage
-                fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);",
-                                         args.fOutputCoverage, args.fOutputCoverage,
-                                         args.fOutputCoverage, args.fOutputCoverage);
-            }
         }
     }
 
@@ -104,10 +98,11 @@
 
         // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
         SkASSERT(gp.numTextureSamplers() == 1);
-        GrTexture* atlas = gp.textureSampler(0).texture();
-        SkASSERT(atlas);
-        b->add32(atlas->width());
-        b->add32(atlas->height());
+        GrTextureProxy* atlas = gp.textureSampler(0).proxy();
+        if (atlas) {
+            b->add32(atlas->width());
+            b->add32(atlas->height());
+        }
     }
 
 private:
diff --git a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
index 2d49883..1996a94 100644
--- a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
@@ -21,34 +21,22 @@
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
         fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
-        if (!args.fGpImplementsDistanceVector) {
-            fragBuilder->codeAppendf("// assuming interpolant is set in vertex colors\n");
-            fragBuilder->codeAppendf("float factor = 1.0 - color.b;");
-        } else {
-            fragBuilder->codeAppendf("// using distance to edge to compute interpolant\n");
-            fragBuilder->codeAppend("float radius = color.r*256.0*64.0 + color.g*64.0;");
-            fragBuilder->codeAppend("float pad = color.b*64.0;");
-
-            fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius, 0.0, 1.0);",
-                                     fragBuilder->distanceVectorName());
-        }
+        fragBuilder->codeAppendf("// assuming interpolant is set in vertex colors\n");
+        fragBuilder->codeAppendf("float factor = 1.0 - color.a;");
         switch (mode) {
             case GrBlurredEdgeFP::kGaussian_Mode:
                 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
                 break;
             case GrBlurredEdgeFP::kSmoothstep_Mode:
-                fragBuilder->codeAppend("factor = smoothstep(factor, 0.0, 1.0);");
+                fragBuilder->codeAppend("factor = smoothstep(1.0, 0.0, factor);");
                 break;
         }
-        if (!args.fGpImplementsDistanceVector) {
-            fragBuilder->codeAppendf("%s = vec4(factor*color.g);", args.fOutputColor);
-        } else {
-            fragBuilder->codeAppendf("%s = vec4(factor*color.a);", args.fOutputColor);
-        }
+        fragBuilder->codeAppendf("%s = vec4(factor);", args.fOutputColor);
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& proc) override {}
 
     GrBlurredEdgeFP::Mode fMode;
 };
diff --git a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.h b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.h
new file mode 100644
index 0000000..cfcc17a
--- /dev/null
+++ b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.h
@@ -0,0 +1,65 @@
+/*
+ * 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 GrBlurredEdgeFragmentProcessor_DEFINED
+#define GrBlurredEdgeFragmentProcessor_DEFINED
+
+#include "GrFragmentProcessor.h"
+
+/**
+ * Shader for managing a blurred edge for a shadow.
+ *
+ * There are two blurring modes supported: Gaussian blur function and smoothstep function.
+ *
+ * If the primitive supports an implicit distance to the edge, the radius of the blur is specified
+ * by r & g values of the color in 14.2 fixed point. For spot shadows, we increase the stroke width
+ * to set the shadow against the shape. This pad is specified by b, also in 6.2 fixed point.
+ * The a value represents the max final alpha.
+ *
+ * When not using implicit distance, then b in the input color represents the input to the
+ * blur function, and r the max final alpha.
+ *
+ */
+class GrBlurredEdgeFP : public GrFragmentProcessor {
+public:
+    enum Mode {
+        kGaussian_Mode,
+        kSmoothstep_Mode,
+
+        kLastMode = kSmoothstep_Mode
+    };
+    static const int kModeCnt = kLastMode + 1;
+
+    static sk_sp<GrFragmentProcessor> Make(Mode mode = kGaussian_Mode) {
+        return sk_sp<GrFragmentProcessor>(new GrBlurredEdgeFP(mode));
+    }
+
+    const char* name() const override { return "BlurredEdge"; }
+
+    Mode mode() const { return fMode; }
+
+private:
+    GrBlurredEdgeFP(Mode mode)
+        : INHERITED(kNone_OptimizationFlags)
+        , fMode(mode) {
+        this->initClassID<GrBlurredEdgeFP>();
+    }
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    Mode   fMode;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 2c6589d..41c7e6f 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -10,7 +10,6 @@
 #include "GrClip.h"
 #include "GrContext.h"
 #include "GrRenderTargetContext.h"
-#include "GrSimpleTextureEffect.h"
 #include "SkMatrix.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
@@ -19,59 +18,36 @@
 public:
     void emitCode(EmitArgs& args) override {
         const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>();
-        GrConfigConversionEffect::PMConversion pmConversion = cce.pmConversion();
-
-        // Using highp for GLES here in order to avoid some precision issues on specific GPUs.
-        GrShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision);
-        SkString tmpDecl;
-        tmpVar.appendDecl(args.fShaderCaps, &tmpDecl);
-
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
-        fragBuilder->codeAppendf("%s;", tmpDecl.c_str());
+        // Use highp throughout the shader to avoid some precision issues on specific GPUs.
+        fragBuilder->elevateDefaultPrecision(kHigh_GrSLPrecision);
 
-        fragBuilder->codeAppendf("%s = ", tmpVar.c_str());
-        fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fTransformedCoords[0].c_str(),
-                                         args.fTransformedCoords[0].getType());
-        fragBuilder->codeAppend(";");
+        if (nullptr == args.fInputColor) {
+            // could optimize this case, but we aren't for now.
+            args.fInputColor = "vec4(1)";
+        }
 
-        switch (pmConversion) {
-            case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
-                fragBuilder->codeAppendf(
-                    "%s = vec4(ceil(%s.rgb * %s.a * 255.0) / 255.0, %s.a);",
-                    tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
-                break;
-            case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
-                // Add a compensation(0.001) here to avoid the side effect of the floor operation.
-                // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0
-                // is less than the integer value converted from  %s.r by 1 when the %s.r is
-                // converted from the integer value 2^n, such as 1, 2, 4, 8, etc.
-                fragBuilder->codeAppendf(
-                    "%s = vec4(floor(%s.rgb * %s.a * 255.0 + 0.001) / 255.0, %s.a);",
-                    tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
+        // Aggressively round to the nearest exact (N / 255) floating point value. This lets us
+        // find a round-trip preserving pair on some GPUs that do odd byte to float conversion.
+        fragBuilder->codeAppendf("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;", args.fInputColor);
 
+        switch (cce.pmConversion()) {
+            case GrConfigConversionEffect::kToPremul_PMConversion:
+                fragBuilder->codeAppend(
+                    "color.rgb = floor(color.rgb * color.a * 255.0 + 0.5) / 255.0;");
                 break;
-            case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
-                fragBuilder->codeAppendf(
-                    "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0) / 255.0, %s.a);",
-                    tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(),
-                    tmpVar.c_str());
+
+            case GrConfigConversionEffect::kToUnpremul_PMConversion:
+                fragBuilder->codeAppend(
+                    "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;");
                 break;
-            case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
-                fragBuilder->codeAppendf(
-                    "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0) / 255.0, %s.a);",
-                    tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(),
-                    tmpVar.c_str());
-                break;
+
             default:
                 SkFAIL("Unknown conversion op.");
                 break;
         }
-        fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, tmpVar.c_str());
-
-        SkString modulate;
-        GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
-        fragBuilder->codeAppend(modulate.c_str());
+        fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
     }
 
     static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
@@ -87,29 +63,11 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
-GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
-                                                   PMConversion pmConversion,
-                                                   const SkMatrix& matrix)
-        : INHERITED(texture, nullptr, matrix, kNone_OptimizationFlags)
-        , fPMConversion(pmConversion) {
-    this->initClassID<GrConfigConversionEffect>();
-    // We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
-    // conversion.
-    SkASSERT(kRGBA_8888_GrPixelConfig == texture->config() ||
-             kBGRA_8888_GrPixelConfig == texture->config());
-}
 
-GrConfigConversionEffect::GrConfigConversionEffect(GrResourceProvider* resourceProvider,
-                                                   sk_sp<GrTextureProxy> proxy,
-                                                   PMConversion pmConversion,
-                                                   const SkMatrix& matrix)
-        : INHERITED(resourceProvider, kNone_OptimizationFlags, proxy, nullptr, matrix)
+GrConfigConversionEffect::GrConfigConversionEffect(PMConversion pmConversion)
+        : INHERITED(kNone_OptimizationFlags)
         , fPMConversion(pmConversion) {
     this->initClassID<GrConfigConversionEffect>();
-    // We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
-    // conversion.
-    SkASSERT(kRGBA_8888_GrPixelConfig == proxy->config() ||
-             kBGRA_8888_GrPixelConfig == proxy->config());
 }
 
 bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
@@ -121,27 +79,13 @@
 
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
 
-#if !defined(__clang__) && _MSC_FULL_VER >= 190024213
-// Work around VS 2015 Update 3 optimizer bug that causes internal compiler error
-//https://connect.microsoft.com/VisualStudio/feedback/details/3100520/internal-compiler-error
-#pragma optimize("t", off)
-#endif
-
 #if GR_TEST_UTILS
 sk_sp<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(GrProcessorTestData* d) {
     PMConversion pmConv = static_cast<PMConversion>(d->fRandom->nextULessThan(kPMConversionCnt));
-    return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(
-                            d->resourceProvider(),
-                            d->textureProxy(GrProcessorUnitTest::kSkiaPMTextureIdx),
-                            pmConv, GrTest::TestMatrix(d->fRandom)));
+    return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
 }
 #endif
 
-#if !defined(__clang__) && _MSC_FULL_VER >= 190024213
-// Restore optimization settings.
-#pragma optimize("", on)
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
@@ -154,12 +98,7 @@
 }
 
 
-
-void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
-                                                              PMConversion* pmToUPMRule,
-                                                              PMConversion* upmToPMRule) {
-    *pmToUPMRule = kPMConversionCnt;
-    *upmToPMRule = kPMConversionCnt;
+bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) {
     static constexpr int kSize = 256;
     static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
     SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
@@ -182,14 +121,16 @@
     const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
                                              kRGBA_8888_SkColorType, kPremul_SkAlphaType);
 
-    sk_sp<GrRenderTargetContext> readRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
+    sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext(
+                                                                          SkBackingFit::kExact,
                                                                           kSize, kSize,
                                                                           kConfig, nullptr));
-    sk_sp<GrRenderTargetContext> tempRTC(context->makeRenderTargetContext(SkBackingFit::kExact,
+    sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext(
+                                                                          SkBackingFit::kExact,
                                                                           kSize, kSize,
                                                                           kConfig, nullptr));
-    if (!readRTC || !tempRTC) {
-        return;
+    if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
+        return false;
     }
     GrSurfaceDesc desc;
     desc.fWidth = kSize;
@@ -200,103 +141,65 @@
     sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
                                                                    SkBudgeted::kYes, data, 0);
     if (!dataProxy) {
-        return;
+        return false;
     }
 
-    static const PMConversion kConversionRules[][2] = {
-        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
-        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
-    };
+    static const SkRect kRect = SkRect::MakeIWH(kSize, kSize);
 
-    bool failed = true;
+    // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
+    // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
+    // We then verify that two reads produced the same values.
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) {
-        *pmToUPMRule = kConversionRules[i][0];
-        *upmToPMRule = kConversionRules[i][1];
+    GrPaint paint1;
+    GrPaint paint2;
+    GrPaint paint3;
+    sk_sp<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(kToUnpremul_PMConversion));
+    sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(kToPremul_PMConversion));
 
-        static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize);
-        static const SkRect kSrcRect = SkRect::MakeIWH(kSize, kSize);
-        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
-        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
-        // We then verify that two reads produced the same values.
+    paint1.addColorTextureProcessor(resourceProvider, dataProxy, nullptr, SkMatrix::I());
+    paint1.addColorFragmentProcessor(pmToUPM);
+    paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-        if (!readRTC->asTexture()) {
-            continue;
-        }
-        GrPaint paint1;
-        GrPaint paint2;
-        GrPaint paint3;
-        sk_sp<GrFragmentProcessor> pmToUPM1(new GrConfigConversionEffect(
-                resourceProvider, dataProxy, *pmToUPMRule, SkMatrix::I()));
-        sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(
-                resourceProvider, readRTC->asTextureProxyRef(), *upmToPMRule, SkMatrix::I()));
-        sk_sp<GrFragmentProcessor> pmToUPM2(new GrConfigConversionEffect(
-                resourceProvider, tempRTC->asTextureProxyRef(), *pmToUPMRule, SkMatrix::I()));
+    readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+    if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
+        return false;
+    }
 
-        paint1.addColorFragmentProcessor(std::move(pmToUPM1));
-        paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    paint2.addColorTextureProcessor(resourceProvider, readRTC->asTextureProxyRef(), nullptr,
+                                    SkMatrix::I());
+    paint2.addColorFragmentProcessor(std::move(upmToPM));
+    paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-        readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kDstRect,
-                                kSrcRect);
+    tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect);
 
-        if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
-            continue;
-        }
+    paint3.addColorTextureProcessor(resourceProvider, tempRTC->asTextureProxyRef(), nullptr,
+                                    SkMatrix::I());
+    paint3.addColorFragmentProcessor(std::move(pmToUPM));
+    paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-        paint2.addColorFragmentProcessor(std::move(upmToPM));
-        paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, kRect);
 
-        tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kDstRect,
-                                kSrcRect);
+    if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
+        return false;
+    }
 
-        paint3.addColorFragmentProcessor(std::move(pmToUPM2));
-        paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
-
-        readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kDstRect,
-                                kSrcRect);
-
-        if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
-            continue;
-        }
-
-        failed = false;
-        for (int y = 0; y < kSize && !failed; ++y) {
-            for (int x = 0; x <= y; ++x) {
-                if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
-                    failed = true;
-                    break;
-                }
+    for (int y = 0; y < kSize; ++y) {
+        for (int x = 0; x <= y; ++x) {
+            if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
+                return false;
             }
         }
     }
-    if (failed) {
-        *pmToUPMRule = kPMConversionCnt;
-        *upmToPMRule = kPMConversionCnt;
-    }
+
+    return true;
 }
 
-sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(GrTexture* texture,
-                                                          PMConversion pmConversion,
-                                                          const SkMatrix& matrix) {
-    if (kRGBA_8888_GrPixelConfig != texture->config() &&
-        kBGRA_8888_GrPixelConfig != texture->config()) {
-        // The PM conversions assume colors are 0..255
+sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(sk_sp<GrFragmentProcessor> fp,
+                                                          PMConversion pmConversion) {
+    if (!fp) {
         return nullptr;
     }
-    return sk_sp<GrFragmentProcessor>(
-        new GrConfigConversionEffect(texture, pmConversion, matrix));
-}
-
-sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(GrResourceProvider* resourceProvider,
-                                                          sk_sp<GrTextureProxy> proxy,
-                                                          PMConversion pmConversion,
-                                                          const SkMatrix& matrix) {
-    if (kRGBA_8888_GrPixelConfig != proxy->config() &&
-        kBGRA_8888_GrPixelConfig != proxy->config()) {
-        // The PM conversions assume colors are 0..255
-        return nullptr;
-    }
-    return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(resourceProvider,
-                                                                   std::move(proxy),
-                                                                   pmConversion, matrix));
+    sk_sp<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
+    sk_sp<GrFragmentProcessor> fpPipeline[] = { fp, ccFP };
+    return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
 }
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index d71b228..4b0c02d 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -8,32 +8,28 @@
 #ifndef GrConfigConversionEffect_DEFINED
 #define GrConfigConversionEffect_DEFINED
 
-#include "GrSingleTextureEffect.h"
-
-class GrInvariantOutput;
+#include "GrFragmentProcessor.h"
 
 /**
  * This class is used to perform config conversions. Clients may want to read/write data that is
  * unpremultiplied.
  */
-class GrConfigConversionEffect : public GrSingleTextureEffect {
+class GrConfigConversionEffect : public GrFragmentProcessor {
 public:
     /**
      * The PM->UPM or UPM->PM conversions to apply.
      */
     enum PMConversion {
-        kMulByAlpha_RoundUp_PMConversion = 0,
-        kMulByAlpha_RoundDown_PMConversion,
-        kDivByAlpha_RoundUp_PMConversion,
-        kDivByAlpha_RoundDown_PMConversion,
-
+        kToPremul_PMConversion = 0,
+        kToUnpremul_PMConversion,
         kPMConversionCnt
     };
 
-    static sk_sp<GrFragmentProcessor> Make(GrTexture*, PMConversion, const SkMatrix&);
-
-    static sk_sp<GrFragmentProcessor> Make(GrResourceProvider*, sk_sp<GrTextureProxy>,
-                                           PMConversion, const SkMatrix&);
+    /**
+     *  Returns a fragment processor that calls the passed in fragment processor, and then performs
+     *  the requested premul or unpremul conversion.
+     */
+    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor>, PMConversion);
 
     const char* name() const override { return "Config Conversion"; }
 
@@ -44,14 +40,10 @@
     // if pixels are read back to a UPM buffer, written back to PM to the GPU, and read back again
     // both reads will produce the same result. This test is quite expensive and should not be run
     // multiple times for a given context.
-    static void TestForPreservingPMConversions(GrContext* context,
-                                               PMConversion* PMToUPMRule,
-                                               PMConversion* UPMToPMRule);
-private:
-    GrConfigConversionEffect(GrTexture*, PMConversion, const SkMatrix& matrix);
+    static bool TestForPreservingPMConversions(GrContext* context);
 
-    GrConfigConversionEffect(GrResourceProvider*, sk_sp<GrTextureProxy>,
-                             PMConversion, const SkMatrix& matrix);
+private:
+    GrConfigConversionEffect(PMConversion);
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
 
@@ -63,7 +55,7 @@
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
-    typedef GrSingleTextureEffect INHERITED;
+    typedef GrFragmentProcessor INHERITED;
 };
 
 #endif
diff --git a/src/gpu/effects/GrConstColorProcessor.cpp b/src/gpu/effects/GrConstColorProcessor.cpp
index 1013ff3..9f46712 100644
--- a/src/gpu/effects/GrConstColorProcessor.cpp
+++ b/src/gpu/effects/GrConstColorProcessor.cpp
@@ -42,7 +42,8 @@
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& processor) override {
+    void onSetData(const GrGLSLProgramDataManager& pdm,
+                   const GrFragmentProcessor& processor) override {
         GrColor4f color = processor.cast<GrConstColorProcessor>().color();
         // We use the "illegal" color value as an uninit sentinel. With GrColor4f, the "illegal"
         // color is *really* illegal (not just unpremultiplied), so this check is simple.
diff --git a/include/gpu/effects/GrConstColorProcessor.h b/src/gpu/effects/GrConstColorProcessor.h
similarity index 100%
rename from include/gpu/effects/GrConstColorProcessor.h
rename to src/gpu/effects/GrConstColorProcessor.h
diff --git a/src/gpu/effects/GrConvexPolyEffect.cpp b/src/gpu/effects/GrConvexPolyEffect.cpp
index d94d94a..4b9599e 100644
--- a/src/gpu/effects/GrConvexPolyEffect.cpp
+++ b/src/gpu/effects/GrConvexPolyEffect.cpp
@@ -85,7 +85,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fRectUniform;
@@ -132,12 +132,11 @@
     if (GrProcessorEdgeTypeIsInverseFill(aare.getEdgeType())) {
         fragBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n");
     }
-    fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
+    fragBuilder->codeAppendf("\t\t%s = %s * alpha;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GLAARectEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                               const GrProcessor& processor) {
+                               const GrFragmentProcessor& processor) {
     const AARectEffect& aare = processor.cast<AARectEffect>();
     const SkRect& rect = aare.getRect();
     if (rect != fPrevRect) {
@@ -176,7 +175,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fEdgeUniform;
@@ -212,12 +211,11 @@
     if (GrProcessorEdgeTypeIsInverseFill(cpe.getEdgeType())) {
         fragBuilder->codeAppend("\talpha = 1.0 - alpha;\n");
     }
-    fragBuilder->codeAppendf("\t%s = %s;\n", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
+    fragBuilder->codeAppendf("\t%s = %s * alpha;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLConvexPolyEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                     const GrProcessor& effect) {
+                                     const GrFragmentProcessor& effect) {
     const GrConvexPolyEffect& cpe = effect.cast<GrConvexPolyEffect>();
     size_t byteSize = 3 * cpe.getEdgeCount() * sizeof(SkScalar);
     if (0 != memcmp(fPrevEdges, cpe.getEdges(), byteSize)) {
diff --git a/src/gpu/effects/GrCoverageSetOpXP.cpp b/src/gpu/effects/GrCoverageSetOpXP.cpp
index 1013706..344762d 100644
--- a/src/gpu/effects/GrCoverageSetOpXP.cpp
+++ b/src/gpu/effects/GrCoverageSetOpXP.cpp
@@ -18,12 +18,11 @@
 
 class CoverageSetOpXP : public GrXferProcessor {
 public:
-    static GrXferProcessor* Create(SkRegion::Op regionOp, bool invertCoverage) {
-        return new CoverageSetOpXP(regionOp, invertCoverage);
+    CoverageSetOpXP(SkRegion::Op regionOp, bool invertCoverage)
+            : fRegionOp(regionOp), fInvertCoverage(invertCoverage) {
+        this->initClassID<CoverageSetOpXP>();
     }
 
-    ~CoverageSetOpXP() override;
-
     const char* name() const override { return "Coverage Set Op"; }
 
     GrGLSLXferProcessor* createGLSLInstance() const override;
@@ -31,9 +30,6 @@
     bool invertCoverage() const { return fInvertCoverage; }
 
 private:
-    CoverageSetOpXP(SkRegion::Op regionOp, bool fInvertCoverage);
-
-    GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override;
 
     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
 
@@ -85,15 +81,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-CoverageSetOpXP::CoverageSetOpXP(SkRegion::Op regionOp, bool invertCoverage)
-    : fRegionOp(regionOp)
-    , fInvertCoverage(invertCoverage) {
-    this->initClassID<CoverageSetOpXP>();
-}
-
-CoverageSetOpXP::~CoverageSetOpXP() {
-}
-
 void CoverageSetOpXP::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                             GrProcessorKeyBuilder* b) const {
     GLCoverageSetOpXP::GenKey(*this, caps, b);
@@ -103,12 +90,6 @@
     return new GLCoverageSetOpXP(*this);
 }
 
-GrXferProcessor::OptFlags CoverageSetOpXP::onGetOptimizations(
-        const FragmentProcessorAnalysis&) const {
-    // We never look at the color input
-    return GrXferProcessor::kIgnoreColor_OptFlag;
-}
-
 void CoverageSetOpXP::onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const {
     switch (fRegionOp) {
         case SkRegion::kReplace_Op:
@@ -226,11 +207,11 @@
     return nullptr;
 }
 
-GrXferProcessor* GrCoverageSetOpXPFactory::onCreateXferProcessor(
-        const GrCaps& caps,
-        const FragmentProcessorAnalysis& analysis,
+sk_sp<const GrXferProcessor> GrCoverageSetOpXPFactory::makeXferProcessor(
+        const GrProcessorAnalysisColor&,
+        GrProcessorAnalysisCoverage,
         bool hasMixedSamples,
-        const DstTexture* dst) const {
+        const GrCaps& caps) const {
     // We don't support inverting coverage with mixed samples. We don't expect to ever want this in
     // the future, however we could at some point make this work using an inverted coverage
     // modulation table. Note that an inverted table still won't work if there are coverage procs.
@@ -239,7 +220,7 @@
         return nullptr;
     }
 
-    return CoverageSetOpXP::Create(fRegionOp, fInvertCoverage);
+    return sk_sp<GrXferProcessor>(new CoverageSetOpXP(fRegionOp, fInvertCoverage));
 }
 
 GR_DEFINE_XP_FACTORY_TEST(GrCoverageSetOpXPFactory);
@@ -247,7 +228,8 @@
 #if GR_TEST_UTILS
 const GrXPFactory* GrCoverageSetOpXPFactory::TestGet(GrProcessorTestData* d) {
     SkRegion::Op regionOp = SkRegion::Op(d->fRandom->nextULessThan(SkRegion::kLastOp + 1));
-    bool invertCoverage = !d->fRenderTargetContext->hasMixedSamples() && d->fRandom->nextBool();
+    bool isMixedSamples = GrFSAAType::kMixedSamples == d->fRenderTargetContext->fsaaType();
+    bool invertCoverage = !isMixedSamples && d->fRandom->nextBool();
     return GrCoverageSetOpXPFactory::Get(regionOp, invertCoverage);
 }
 #endif
diff --git a/src/gpu/effects/GrCoverageSetOpXP.h b/src/gpu/effects/GrCoverageSetOpXP.h
index a0cb0c5..ba04a2e 100644
--- a/src/gpu/effects/GrCoverageSetOpXP.h
+++ b/src/gpu/effects/GrCoverageSetOpXP.h
@@ -30,16 +30,17 @@
 private:
     constexpr GrCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage);
 
-    GrXferProcessor* onCreateXferProcessor(const GrCaps&,
-                                           const FragmentProcessorAnalysis&,
-                                           bool hasMixedSamples,
-                                           const DstTexture*) const override;
+    sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
+                                                   GrProcessorAnalysisCoverage,
+                                                   bool hasMixedSamples,
+                                                   const GrCaps&) const override;
 
-    bool willReadDstInShader(const GrCaps&, const FragmentProcessorAnalysis&) const override {
-        return false;
+    AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
+                                          const GrProcessorAnalysisCoverage&,
+                                          const GrCaps&) const override {
+        return AnalysisProperties::kIgnoresInputColor;
     }
 
-    bool compatibleWithCoverageAsAlpha(bool colorIsOpaque) const override { return false; }
 
     GR_DECLARE_XP_FACTORY_TEST;
 
diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp
index 1f2771c..a2302e0 100644
--- a/src/gpu/effects/GrCustomXfermode.cpp
+++ b/src/gpu/effects/GrCustomXfermode.cpp
@@ -52,12 +52,12 @@
 #undef EQ_OFFSET
 }
 
-static bool can_use_hw_blend_equation(GrBlendEquation equation, GrPipelineAnalysisCoverage coverage,
-                                      const GrCaps& caps) {
+static bool can_use_hw_blend_equation(GrBlendEquation equation,
+                                      GrProcessorAnalysisCoverage coverage, const GrCaps& caps) {
     if (!caps.advancedBlendEquationSupport()) {
         return false;
     }
-    if (GrPipelineAnalysisCoverage::kLCD == coverage) {
+    if (GrProcessorAnalysisCoverage::kLCD == coverage) {
         return false; // LCD coverage must be applied after the blend equation.
     }
     if (caps.canUseAdvancedBlendEquation(equation)) {
@@ -73,15 +73,15 @@
 class CustomXP : public GrXferProcessor {
 public:
     CustomXP(SkBlendMode mode, GrBlendEquation hwBlendEquation)
-        : fMode(mode),
-          fHWBlendEquation(hwBlendEquation) {
+        : fMode(mode)
+        , fHWBlendEquation(hwBlendEquation) {
         this->initClassID<CustomXP>();
     }
 
-    CustomXP(const DstTexture* dstTexture, bool hasMixedSamples, SkBlendMode mode)
-        : INHERITED(dstTexture, true, hasMixedSamples),
-          fMode(mode),
-          fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
+    CustomXP(bool hasMixedSamples, SkBlendMode mode, GrProcessorAnalysisCoverage coverage)
+            : INHERITED(true, hasMixedSamples, coverage)
+            , fMode(mode)
+            , fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
         this->initClassID<CustomXP>();
     }
 
@@ -97,13 +97,11 @@
         return fHWBlendEquation;
     }
 
+    GrXferBarrierType xferBarrierType(const GrCaps&) const override;
+
 private:
-    GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override;
-
     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
 
-    GrXferBarrierType onXferBarrier(const GrRenderTarget*, const GrCaps&) const override;
-
     void onGetBlendInfo(BlendInfo*) const override;
 
     bool onIsEqual(const GrXferProcessor& xpBase) const override;
@@ -189,11 +187,68 @@
     return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
 }
 
-GrXferProcessor::OptFlags CustomXP::onGetOptimizations(
-        const FragmentProcessorAnalysis& analysis) const {
-    /*
-      Most the optimizations we do here are based on tweaking alpha for coverage.
+GrXferBarrierType CustomXP::xferBarrierType(const GrCaps& caps) const {
+    if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
+        return kBlend_GrXferBarrierType;
+    }
+    return kNone_GrXferBarrierType;
+}
 
+void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
+    if (this->hasHWBlendEquation()) {
+        blendInfo->fEquation = this->hwBlendEquation();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// See the comment above GrXPFactory's definition about this warning suppression.
+#if defined(__GNUC__) || defined(__clang)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+class CustomXPFactory : public GrXPFactory {
+public:
+    constexpr CustomXPFactory(SkBlendMode mode)
+            : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
+
+private:
+    sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
+                                                   GrProcessorAnalysisCoverage,
+                                                   bool hasMixedSamples,
+                                                   const GrCaps&) const override;
+
+    AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
+                                          const GrProcessorAnalysisCoverage&,
+                                          const GrCaps&) const override;
+
+    GR_DECLARE_XP_FACTORY_TEST;
+
+    SkBlendMode fMode;
+    GrBlendEquation fHWBlendEquation;
+
+    typedef GrXPFactory INHERITED;
+};
+#if defined(__GNUC__) || defined(__clang)
+#pragma GCC diagnostic pop
+#endif
+
+sk_sp<const GrXferProcessor> CustomXPFactory::makeXferProcessor(
+        const GrProcessorAnalysisColor&,
+        GrProcessorAnalysisCoverage coverage,
+        bool hasMixedSamples,
+        const GrCaps& caps) const {
+    SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
+    if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
+        return sk_sp<GrXferProcessor>(new CustomXP(fMode, fHWBlendEquation));
+    }
+    return sk_sp<GrXferProcessor>(new CustomXP(hasMixedSamples, fMode, coverage));
+}
+
+GrXPFactory::AnalysisProperties CustomXPFactory::analysisProperties(
+        const GrProcessorAnalysisColor&, const GrProcessorAnalysisCoverage& coverage,
+        const GrCaps& caps) const {
+    /*
       The general SVG blend equation is defined in the spec as follows:
 
         Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
@@ -287,76 +342,17 @@
           = f*Sa - f*Sa * Da + Da
           = f*Sa + Da - f*Sa * Da
           = blend(f*Sa, Da)
-     */
-
-    OptFlags flags = kNone_OptFlags;
-    if (analysis.isCompatibleWithCoverageAsAlpha()) {
-        flags |= kCanTweakAlphaForCoverage_OptFlag;
+    */
+    if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
+        if (caps.blendEquationSupport() == GrCaps::kAdvancedCoherent_BlendEquationSupport) {
+            return AnalysisProperties::kCompatibleWithAlphaAsCoverage;
+        } else {
+            return AnalysisProperties::kCompatibleWithAlphaAsCoverage |
+                   AnalysisProperties::kRequiresBarrierBetweenOverlappingDraws;
+        }
     }
-    return flags;
-}
-
-GrXferBarrierType CustomXP::onXferBarrier(const GrRenderTarget* rt, const GrCaps& caps) const {
-    if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
-        return kBlend_GrXferBarrierType;
-    }
-    return kNone_GrXferBarrierType;
-}
-
-void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
-    if (this->hasHWBlendEquation()) {
-        blendInfo->fEquation = this->hwBlendEquation();
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// See the comment above GrXPFactory's definition about this warning suppression.
-#if defined(__GNUC__) || defined(__clang)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
-#endif
-class CustomXPFactory : public GrXPFactory {
-public:
-    constexpr CustomXPFactory(SkBlendMode mode)
-            : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
-
-private:
-    GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
-                                           const FragmentProcessorAnalysis&,
-                                           bool hasMixedSamples,
-                                           const DstTexture*) const override;
-
-    bool willReadDstInShader(const GrCaps&, const FragmentProcessorAnalysis&) const override;
-
-    bool compatibleWithCoverageAsAlpha(bool colorIsOpaque) const override { return true; }
-
-    GR_DECLARE_XP_FACTORY_TEST;
-
-    SkBlendMode     fMode;
-    GrBlendEquation fHWBlendEquation;
-
-    typedef GrXPFactory INHERITED;
-};
-#if defined(__GNUC__) || defined(__clang)
-#pragma GCC diagnostic pop
-#endif
-
-GrXferProcessor* CustomXPFactory::onCreateXferProcessor(const GrCaps& caps,
-                                                        const FragmentProcessorAnalysis& analysis,
-                                                        bool hasMixedSamples,
-                                                        const DstTexture* dstTexture) const {
-    SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
-    if (can_use_hw_blend_equation(fHWBlendEquation, analysis.outputCoverageType(), caps)) {
-        SkASSERT(!dstTexture || !dstTexture->texture());
-        return new CustomXP(fMode, fHWBlendEquation);
-    }
-    return new CustomXP(dstTexture, hasMixedSamples, fMode);
-}
-
-bool CustomXPFactory::willReadDstInShader(const GrCaps& caps,
-                                          const FragmentProcessorAnalysis& analysis) const {
-    return !can_use_hw_blend_equation(fHWBlendEquation, analysis.outputCoverageType(), caps);
+    return AnalysisProperties::kCompatibleWithAlphaAsCoverage |
+           AnalysisProperties::kReadsDstInShader;
 }
 
 GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory);
diff --git a/src/gpu/effects/GrDisableColorXP.cpp b/src/gpu/effects/GrDisableColorXP.cpp
index f95cd9b..14464f7 100644
--- a/src/gpu/effects/GrDisableColorXP.cpp
+++ b/src/gpu/effects/GrDisableColorXP.cpp
@@ -18,20 +18,13 @@
  */
 class DisableColorXP : public GrXferProcessor {
 public:
-    static GrXferProcessor* Create() { return new DisableColorXP; }
-
-    ~DisableColorXP() override {}
+    DisableColorXP() { this->initClassID<DisableColorXP>(); }
 
     const char* name() const override { return "Disable Color"; }
 
     GrGLSLXferProcessor* createGLSLInstance() const override;
 
 private:
-    DisableColorXP();
-
-    GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override {
-        return GrXferProcessor::kIgnoreColor_OptFlag;
-    }
 
     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
 
@@ -70,10 +63,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-DisableColorXP::DisableColorXP() {
-    this->initClassID<DisableColorXP>();
-}
-
 void DisableColorXP::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
     GLDisableColorXP::GenKey(*this, caps, b);
 }
@@ -85,12 +74,12 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-GrXferProcessor* GrDisableColorXPFactory::onCreateXferProcessor(
-        const GrCaps& caps,
-        const FragmentProcessorAnalysis& analysis,
+sk_sp<const GrXferProcessor> GrDisableColorXPFactory::makeXferProcessor(
+        const GrProcessorAnalysisColor&,
+        GrProcessorAnalysisCoverage,
         bool hasMixedSamples,
-        const DstTexture* dst) const {
-    return DisableColorXP::Create();
+        const GrCaps& caps) const {
+    return sk_sp<const GrXferProcessor>(new DisableColorXP);
 }
 
 GR_DEFINE_XP_FACTORY_TEST(GrDisableColorXPFactory);
diff --git a/src/gpu/effects/GrDisableColorXP.h b/src/gpu/effects/GrDisableColorXP.h
index 821ad27..7470444 100644
--- a/src/gpu/effects/GrDisableColorXP.h
+++ b/src/gpu/effects/GrDisableColorXP.h
@@ -24,16 +24,17 @@
 private:
     constexpr GrDisableColorXPFactory() {}
 
-    bool willReadDstInShader(const GrCaps&, const FragmentProcessorAnalysis&) const override {
-        return false;
+    AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
+                                          const GrProcessorAnalysisCoverage&,
+                                          const GrCaps&) const override {
+        return AnalysisProperties::kCompatibleWithAlphaAsCoverage |
+               AnalysisProperties::kIgnoresInputColor;
     }
 
-    bool compatibleWithCoverageAsAlpha(bool colorIsOpaque) const override { return true; }
-
-    GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
-                                           const FragmentProcessorAnalysis&,
-                                           bool hasMixedSamples,
-                                           const DstTexture* dstTexture) const override;
+    sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
+                                                   GrProcessorAnalysisCoverage,
+                                                   bool hasMixedSamples,
+                                                   const GrCaps&) const override;
 
     GR_DECLARE_XP_FACTORY_TEST;
 
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
index 82f8880..51c4b55 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -82,7 +82,7 @@
 
         // compute numbers to be hardcoded to convert texture coordinates from float to int
         SkASSERT(dfTexEffect.numTextureSamplers() == 1);
-        GrTexture* atlas = dfTexEffect.textureSampler(0).texture();
+        GrTexture* atlas = dfTexEffect.textureSampler(0).peekTexture();
         SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
 
         GrGLSLVertToFrag st(kVec2f_GrSLType);
@@ -163,7 +163,7 @@
         // mapped linearly to coverage, so use a linear step:
         if (isGammaCorrect) {
             fragBuilder->codeAppend(
-                "float val = clamp(distance + afwidth / (2.0 * afwidth), 0.0, 1.0);");
+                "float val = clamp((distance + afwidth) / (2.0 * afwidth), 0.0, 1.0);");
         } else {
             fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
         }
@@ -202,10 +202,11 @@
 
         // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
         SkASSERT(gp.numTextureSamplers() == 1);
-        GrTexture* atlas = gp.textureSampler(0).texture();
-        SkASSERT(atlas);
-        b->add32(atlas->width());
-        b->add32(atlas->height());
+        GrTextureProxy* atlas = gp.textureSampler(0).proxy();
+        if (atlas) {
+            b->add32(atlas->width());
+            b->add32(atlas->height());
+        }
     }
 
 private:
@@ -418,7 +419,7 @@
         // mapped linearly to coverage, so use a linear step:
         if (isGammaCorrect) {
             fragBuilder->codeAppend(
-                "float val = clamp(distance + afwidth / (2.0 * afwidth), 0.0, 1.0);");
+                "float val = clamp((distance + afwidth) / (2.0 * afwidth), 0.0, 1.0);");
         } else {
             fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
         }
@@ -430,7 +431,8 @@
                  FPCoordTransformIter&& transformIter) override {
         SkASSERT(fTextureSizeUni.isValid());
 
-        GrTexture* texture = proc.textureSampler(0).texture();
+        GrTexture* texture = proc.textureSampler(0).peekTexture();
+
         if (texture->width() != fTextureSize.width() ||
             texture->height() != fTextureSize.height()) {
             fTextureSize = SkISize::Make(texture->width(), texture->height());
@@ -595,7 +597,7 @@
 
         // compute numbers to be hardcoded to convert texture coordinates from float to int
         SkASSERT(dfTexEffect.numTextureSamplers() == 1);
-        GrTexture* atlas = dfTexEffect.textureSampler(0).texture();
+        GrTexture* atlas = dfTexEffect.textureSampler(0).peekTexture();
         SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
 
         GrGLSLVertToFrag st(kVec2f_GrSLType);
@@ -711,17 +713,14 @@
         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
         // mapped linearly to coverage, so use a linear step:
         if (isGammaCorrect) {
-            fragBuilder->codeAppend("vec4 val = "
-                "vec4(clamp(distance + vec3(afwidth) / vec3(2.0 * afwidth), 0.0, 1.0), 1.0);");
+            fragBuilder->codeAppendf("%s = "
+                "vec4(clamp((distance + vec3(afwidth)) / vec3(2.0 * afwidth), 0.0, 1.0), 1.0);",
+                args.fOutputCoverage);
         } else {
-            fragBuilder->codeAppend(
-                "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);");
+            fragBuilder->codeAppendf(
+                "%s = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);",
+                args.fOutputCoverage);
         }
-
-        // set alpha to be max of rgb coverage
-        fragBuilder->codeAppend("val.a = max(max(val.r, val.g), val.b);");
-
-        fragBuilder->codeAppendf("%s = val;", args.fOutputCoverage);
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor,
@@ -758,10 +757,11 @@
 
         // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
         SkASSERT(gp.numTextureSamplers() == 1);
-        GrTexture* atlas = gp.textureSampler(0).texture();
-        SkASSERT(atlas);
-        b->add32(atlas->width());
-        b->add32(atlas->height());
+        GrTextureProxy* atlas = gp.textureSampler(0).proxy();
+        if (atlas) {
+            b->add32(atlas->width());
+            b->add32(atlas->height());
+        }
     }
 
 private:
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp
index 864a5d2..0686750 100644
--- a/src/gpu/effects/GrDitherEffect.cpp
+++ b/src/gpu/effects/GrDitherEffect.cpp
@@ -74,7 +74,7 @@
                              "fract(sin(dot(sk_FragCoord.xy, vec2(12.9898,78.233))) * "
                                                             "43758.5453);\n");
     fragBuilder->codeAppendf("\t\t%s = clamp((1.0/255.0) * vec4(r, r, r, r) + %s, 0, 1);\n",
-                             args.fOutputColor, GrGLSLExpr4(args.fInputColor).c_str());
+                             args.fOutputColor, args.fInputColor);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
index 4fa462e..9a0936c 100644
--- a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
+++ b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
@@ -25,7 +25,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     UniformHandle fKernelUni;
@@ -91,17 +91,14 @@
         }
         fragBuilder->codeAppendf("coord += %s;\n", imgInc);
     }
-
-    SkString modulate;
-    GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
-    fragBuilder->codeAppend(modulate.c_str());
+    fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                      const GrProcessor& processor) {
+                                      const GrFragmentProcessor& processor) {
     const GrGaussianConvolutionFragmentProcessor& conv =
             processor.cast<GrGaussianConvolutionFragmentProcessor>();
-    GrTexture& texture = *conv.textureSampler(0).texture();
+    GrTexture& texture = *conv.textureSampler(0).peekTexture();
 
     float imageIncrement[2] = {0};
     float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index 62a53dd..776eff0 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -20,7 +20,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
@@ -114,10 +114,7 @@
         fragBuilder->codeAppendf("%s.rgb = clamp(sum.rgb * %s + %s, 0, 1);", args.fOutputColor, gain, bias);
         fragBuilder->codeAppendf("%s.rgb *= %s.a;", args.fOutputColor, args.fOutputColor);
     }
-
-    SkString modulate;
-    GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
-    fragBuilder->codeAppend(modulate.c_str());
+    fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLMatrixConvolutionEffect::GenKey(const GrProcessor& processor,
@@ -131,9 +128,9 @@
 }
 
 void GrGLMatrixConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                            const GrProcessor& processor) {
+                                            const GrFragmentProcessor& processor) {
     const GrMatrixConvolutionEffect& conv = processor.cast<GrMatrixConvolutionEffect>();
-    GrTexture* texture = conv.textureSampler(0).texture();
+    GrTexture* texture = conv.textureSampler(0).peekTexture();
 
     float imageIncrement[2];
     float ySign = texture->origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
diff --git a/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp b/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
index d4c0a39..ab03268 100644
--- a/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
+++ b/src/gpu/effects/GrNonlinearColorSpaceXformEffect.cpp
@@ -44,27 +44,30 @@
                                                         &gamutXformName);
         }
 
-        // Helper function to apply a transfer function to a single value
-        SkString tfFuncNameString;
-        static const GrShaderVar gTransferFnFuncArgs[] = {
-            GrShaderVar("x", kFloat_GrSLType),
-            GrShaderVar("coeffs", kFloat_GrSLType,
-                        GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs),
-        };
-        SkString transferFnBody;
-        // Temporaries to make evaluation line readable
-        transferFnBody.printf("float A = coeffs[0];");
-        transferFnBody.append("float B = coeffs[1];");
-        transferFnBody.append("float C = coeffs[2];");
-        transferFnBody.append("float D = coeffs[3];");
-        transferFnBody.append("float E = coeffs[4];");
-        transferFnBody.append("float F = coeffs[5];");
-        transferFnBody.append("float G = coeffs[6];");
-        transferFnBody.appendf("return (x < D) ? (C * x) + F : pow(A * x + B, G) + E;");
-        fragBuilder->emitFunction(kFloat_GrSLType, "transfer_fn",
-                                  SK_ARRAY_COUNT(gTransferFnFuncArgs), gTransferFnFuncArgs,
-                                  transferFnBody.c_str(), &tfFuncNameString);
-        const char* tfFuncName = tfFuncNameString.c_str();
+        // Helper function to apply the src or dst transfer function to a single value
+        SkString tfFuncNames[2];
+        for (size_t i = 0; i < 2; ++i) {
+            const char* coeffsName = i ? dstCoeffsName : srcCoeffsName;
+            if (!coeffsName) {
+                continue;
+            }
+            const char* fnName = i ? "dst_transfer_fn" : "src_transfer_fn";
+            static const GrShaderVar gTransferFnFuncArgs[] = {
+                    GrShaderVar("x", kFloat_GrSLType),
+            };
+            SkString transferFnBody;
+            // Temporaries to make evaluation line readable
+            transferFnBody.printf("float A = %s[0];", coeffsName);
+            transferFnBody.appendf("float B = %s[1];", coeffsName);
+            transferFnBody.appendf("float C = %s[2];", coeffsName);
+            transferFnBody.appendf("float D = %s[3];", coeffsName);
+            transferFnBody.appendf("float E = %s[4];", coeffsName);
+            transferFnBody.appendf("float F = %s[5];", coeffsName);
+            transferFnBody.appendf("float G = %s[6];", coeffsName);
+            transferFnBody.appendf("return (x < D) ? (C * x) + F : pow(A * x + B, G) + E;");
+            fragBuilder->emitFunction(kFloat_GrSLType, fnName, SK_ARRAY_COUNT(gTransferFnFuncArgs),
+                                      gTransferFnFuncArgs, transferFnBody.c_str(), &tfFuncNames[i]);
+        }
 
         if (nullptr == args.fInputColor) {
             args.fInputColor = "vec4(1)";
@@ -77,9 +80,9 @@
 
         // 2: Apply src transfer function (to get to linear RGB)
         if (srcCoeffsName) {
-            fragBuilder->codeAppendf("color.r = %s(color.r, %s);", tfFuncName, srcCoeffsName);
-            fragBuilder->codeAppendf("color.g = %s(color.g, %s);", tfFuncName, srcCoeffsName);
-            fragBuilder->codeAppendf("color.b = %s(color.b, %s);", tfFuncName, srcCoeffsName);
+            fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[0].c_str());
+            fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[0].c_str());
+            fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[0].c_str());
         }
 
         // 3: Apply gamut matrix
@@ -91,9 +94,9 @@
 
         // 4: Apply dst transfer fn
         if (dstCoeffsName) {
-            fragBuilder->codeAppendf("color.r = %s(color.r, %s);", tfFuncName, dstCoeffsName);
-            fragBuilder->codeAppendf("color.g = %s(color.g, %s);", tfFuncName, dstCoeffsName);
-            fragBuilder->codeAppendf("color.b = %s(color.b, %s);", tfFuncName, dstCoeffsName);
+            fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[1].c_str());
+            fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[1].c_str());
+            fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[1].c_str());
         }
 
         // 5: Premultiply again
@@ -108,7 +111,8 @@
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& processor) override {
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& processor) override {
         const GrNonlinearColorSpaceXformEffect& csxe =
                 processor.cast<GrNonlinearColorSpaceXformEffect>();
         if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
diff --git a/src/gpu/effects/GrOvalEffect.cpp b/src/gpu/effects/GrOvalEffect.cpp
index a8b0c84..2eb1d85 100644
--- a/src/gpu/effects/GrOvalEffect.cpp
+++ b/src/gpu/effects/GrOvalEffect.cpp
@@ -98,7 +98,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fCircleUniform;
@@ -140,8 +140,7 @@
         fragBuilder->codeAppend("d = d > 0.5 ? 1.0 : 0.0;");
     }
 
-    fragBuilder->codeAppendf("%s = %s;", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("d")).c_str());
+    fragBuilder->codeAppendf("%s = %s * d;", args.fOutputColor, args.fInputColor);
 }
 
 void GLCircleEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
@@ -151,7 +150,7 @@
 }
 
 void GLCircleEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                               const GrProcessor& processor) {
+                               const GrFragmentProcessor& processor) {
     const CircleEffect& ce = processor.cast<CircleEffect>();
     if (ce.getRadius() != fPrevRadius || ce.getCenter() != fPrevCenter) {
         SkScalar radius = ce.getRadius();
@@ -266,7 +265,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fEllipseUniform;
@@ -333,8 +332,7 @@
             SkFAIL("Hairline not expected here.");
     }
 
-    fragBuilder->codeAppendf("%s = %s;", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
+    fragBuilder->codeAppendf("%s = %s * alpha;", args.fOutputColor, args.fInputColor);
 }
 
 void GLEllipseEffect::GenKey(const GrProcessor& effect, const GrShaderCaps&,
@@ -344,7 +342,7 @@
 }
 
 void GLEllipseEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                const GrProcessor& effect) {
+                                const GrFragmentProcessor& effect) {
     const EllipseEffect& ee = effect.cast<EllipseEffect>();
     if (ee.getRadii() != fPrevRadii || ee.getCenter() != fPrevCenter) {
         float invRXSqd;
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
index aa416e0..254b44b 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
@@ -10,8 +10,8 @@
 #include "GrBlend.h"
 #include "GrCaps.h"
 #include "GrPipeline.h"
-#include "GrPipelineAnalysis.h"
 #include "GrProcessor.h"
+#include "GrProcessorAnalysis.h"
 #include "GrTypes.h"
 #include "GrXferProcessor.h"
 #include "glsl/GrGLSLBlend.h"
@@ -23,7 +23,7 @@
 /**
  * Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage.
  */
-struct BlendFormula {
+class BlendFormula {
 public:
     /**
      * Values the shader can write to primary and secondary outputs. These must all be modulated by
@@ -40,6 +40,76 @@
         kLast_OutputType = kISCModulate_OutputType
     };
 
+    BlendFormula() = default;
+
+    constexpr BlendFormula(OutputType primaryOut, OutputType secondaryOut, GrBlendEquation equation,
+                           GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff)
+            : fPrimaryOutputType(primaryOut)
+            , fSecondaryOutputType(secondaryOut)
+            , fBlendEquation(equation)
+            , fSrcCoeff(srcCoeff)
+            , fDstCoeff(dstCoeff)
+            , fProps(GetProperties(primaryOut, secondaryOut, equation, srcCoeff, dstCoeff)) {}
+
+    BlendFormula& operator=(const BlendFormula& other) {
+        SkDEBUGCODE(other.validatePreoptimized());
+        fData = other.fData;
+        return *this;
+    }
+
+    bool operator==(const BlendFormula& other) const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        SkDEBUGCODE(other.validatePreoptimized());
+        return fData == other.fData;
+    }
+
+    bool hasSecondaryOutput() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return kNone_OutputType != fSecondaryOutputType;
+    }
+    bool modifiesDst() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return SkToBool(fProps & kModifiesDst_Property);
+    }
+    bool usesDstColor() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return SkToBool(fProps & kUsesDstColor_Property);
+    }
+    bool usesInputColor() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return SkToBool(fProps & kUsesInputColor_Property);
+    }
+    bool canTweakAlphaForCoverage() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return SkToBool(fProps & kCanTweakAlphaForCoverage_Property);
+    }
+
+    GrBlendEquation equation() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return fBlendEquation;
+    }
+
+    GrBlendCoeff srcCoeff() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return fSrcCoeff;
+    }
+
+    GrBlendCoeff dstCoeff() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return fDstCoeff;
+    }
+
+    OutputType primaryOutput() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return fPrimaryOutputType;
+    }
+
+    OutputType secondaryOutput() const {
+        SkDEBUGCODE(this->validatePreoptimized());
+        return fSecondaryOutputType;
+    }
+
+private:
     enum Properties {
         kModifiesDst_Property              = 1,
         kUsesDstColor_Property             = 1 << 1,
@@ -48,55 +118,30 @@
 
         kLast_Property = kCanTweakAlphaForCoverage_Property
     };
+    GR_DECL_BITFIELD_OPS_FRIENDS(Properties)
 
-    BlendFormula& operator =(const BlendFormula& other) {
-        fData = other.fData;
-        return *this;
+#ifdef SK_DEBUG
+    void validatePreoptimized() const {
+        // The provided formula should already be optimized before a BlendFormula is constructed.
+        // Preferably these asserts would be done statically in the constexpr constructor, but this
+        // is not allowed in C++11.
+        SkASSERT((kNone_OutputType == fPrimaryOutputType) ==
+                 !GrBlendCoeffsUseSrcColor(fSrcCoeff, fDstCoeff));
+        SkASSERT(!GrBlendCoeffRefsSrc2(fSrcCoeff));
+        SkASSERT((kNone_OutputType == fSecondaryOutputType) == !GrBlendCoeffRefsSrc2(fDstCoeff));
+        SkASSERT(fPrimaryOutputType != fSecondaryOutputType ||
+                 kNone_OutputType == fPrimaryOutputType);
+        SkASSERT(kNone_OutputType != fPrimaryOutputType ||
+                 kNone_OutputType == fSecondaryOutputType);
     }
-
-    bool operator ==(const BlendFormula& other) const {
-        return fData == other.fData;
-    }
-
-    bool hasSecondaryOutput() const { return kNone_OutputType != fSecondaryOutputType; }
-    bool modifiesDst() const { return SkToBool(fProps & kModifiesDst_Property); }
-    bool usesDstColor() const { return SkToBool(fProps & kUsesDstColor_Property); }
-    bool usesInputColor() const { return SkToBool(fProps & kUsesInputColor_Property); }
-    bool canTweakAlphaForCoverage() const {
-        return SkToBool(fProps & kCanTweakAlphaForCoverage_Property);
-    }
+#endif
 
     /**
-     * Deduce the properties of a compile-time constant BlendFormula.
+     * Deduce the properties of a BlendFormula.
      */
-    template<OutputType PrimaryOut, OutputType SecondaryOut,
-             GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
-    struct get_properties : std::integral_constant<Properties, static_cast<Properties>(
-
-        (GR_BLEND_MODIFIES_DST(BlendEquation, SrcCoeff, DstCoeff) ?
-            kModifiesDst_Property : 0) |
-
-        (GR_BLEND_COEFFS_USE_DST_COLOR(SrcCoeff, DstCoeff) ?
-            kUsesDstColor_Property : 0) |
-
-        ((PrimaryOut >= kModulate_OutputType && GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff,DstCoeff)) ||
-         (SecondaryOut >= kModulate_OutputType && GR_BLEND_COEFF_REFS_SRC2(DstCoeff)) ?
-            kUsesInputColor_Property : 0) |  // We assert later that SrcCoeff doesn't ref src2.
-
-        (kModulate_OutputType == PrimaryOut &&
-         kNone_OutputType == SecondaryOut &&
-         GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(BlendEquation, SrcCoeff, DstCoeff) ?
-            kCanTweakAlphaForCoverage_Property : 0))> {
-
-        // The provided formula should already be optimized.
-        GR_STATIC_ASSERT((kNone_OutputType == PrimaryOut) ==
-                         !GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff, DstCoeff));
-        GR_STATIC_ASSERT(!GR_BLEND_COEFF_REFS_SRC2(SrcCoeff));
-        GR_STATIC_ASSERT((kNone_OutputType == SecondaryOut) ==
-                         !GR_BLEND_COEFF_REFS_SRC2(DstCoeff));
-        GR_STATIC_ASSERT(PrimaryOut != SecondaryOut || kNone_OutputType == PrimaryOut);
-        GR_STATIC_ASSERT(kNone_OutputType != PrimaryOut || kNone_OutputType == SecondaryOut);
-    };
+    static constexpr Properties GetProperties(OutputType PrimaryOut, OutputType SecondaryOut,
+                                              GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff,
+                                              GrBlendCoeff DstCoeff);
 
     union {
         struct {
@@ -122,51 +167,48 @@
 
 GR_MAKE_BITFIELD_OPS(BlendFormula::Properties);
 
-/**
- * Initialize a compile-time constant BlendFormula and automatically deduce fProps.
- */
-#define INIT_BLEND_FORMULA(PRIMARY_OUT, SECONDARY_OUT, BLEND_EQUATION, SRC_COEFF, DST_COEFF) \
-    {{{PRIMARY_OUT, \
-       SECONDARY_OUT, \
-       BLEND_EQUATION, SRC_COEFF, DST_COEFF, \
-       BlendFormula::get_properties<PRIMARY_OUT, SECONDARY_OUT, \
-                                    BLEND_EQUATION, SRC_COEFF, DST_COEFF>::value}}}
+constexpr BlendFormula::Properties BlendFormula::GetProperties(OutputType PrimaryOut,
+                                                               OutputType SecondaryOut,
+                                                               GrBlendEquation BlendEquation,
+                                                               GrBlendCoeff SrcCoeff,
+                                                               GrBlendCoeff DstCoeff) {
+    return static_cast<Properties>(
+            (GrBlendModifiesDst(BlendEquation, SrcCoeff, DstCoeff) ? kModifiesDst_Property : 0) |
+            (GrBlendCoeffsUseDstColor(SrcCoeff, DstCoeff) ? kUsesDstColor_Property : 0) |
+            ((PrimaryOut >= kModulate_OutputType && GrBlendCoeffsUseSrcColor(SrcCoeff, DstCoeff)) ||
+                             (SecondaryOut >= kModulate_OutputType &&
+                              GrBlendCoeffRefsSrc2(DstCoeff))
+                     ? kUsesInputColor_Property
+                     : 0) |  // We assert later that SrcCoeff doesn't ref src2.
+            ((kModulate_OutputType == PrimaryOut || kNone_OutputType == PrimaryOut) &&
+                             kNone_OutputType == SecondaryOut &&
+                             GrBlendAllowsCoverageAsAlpha(BlendEquation, SrcCoeff, DstCoeff)
+                     ? kCanTweakAlphaForCoverage_Property
+                     : 0));
+}
 
 /**
  * When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard
  * Porter Duff formula.
  */
-#define COEFF_FORMULA(SRC_COEFF, DST_COEFF) \
-    INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \
-                       BlendFormula::kNone_OutputType, \
-                       kAdd_GrBlendEquation, SRC_COEFF, DST_COEFF)
+static constexpr BlendFormula MakeCoeffFormula(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+    // When the coeffs are (Zero, Zero) or (Zero, One) we set the primary output to none.
+    return (kZero_GrBlendCoeff == srcCoeff &&
+            (kZero_GrBlendCoeff == dstCoeff || kOne_GrBlendCoeff == dstCoeff))
+           ? BlendFormula(BlendFormula::kNone_OutputType, BlendFormula::kNone_OutputType,
+                          kAdd_GrBlendEquation, kZero_GrBlendCoeff, dstCoeff)
+           : BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kNone_OutputType,
+                        kAdd_GrBlendEquation, srcCoeff, dstCoeff);
+}
 
 /**
- * Basic coeff formula similar to COEFF_FORMULA but we will make the src f*Sa. This is used in
+ * Basic coeff formula similar to MakeCoeffFormula but we will make the src f*Sa. This is used in
  * LCD dst-out.
  */
-#define COEFF_FORMULA_SA_MODULATE(SRC_COEFF, DST_COEFF) \
-    INIT_BLEND_FORMULA(BlendFormula::kSAModulate_OutputType, \
-                       BlendFormula::kNone_OutputType, \
-                       kAdd_GrBlendEquation, SRC_COEFF, DST_COEFF)
-
-/**
- * When the coeffs are (Zero, Zero), we clear the dst. This formula has its own macro so we can set
- * the primary output type to none.
- */
-#define DST_CLEAR_FORMULA \
-    INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \
-                       BlendFormula::kNone_OutputType, \
-                       kAdd_GrBlendEquation, kZero_GrBlendCoeff, kZero_GrBlendCoeff)
-
-/**
- * When the coeffs are (Zero, One), we don't write to the dst at all. This formula has its own macro
- * so we can set the primary output type to none.
- */
-#define NO_DST_WRITE_FORMULA \
-    INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \
-                       BlendFormula::kNone_OutputType, \
-                       kAdd_GrBlendEquation, kZero_GrBlendCoeff, kOne_GrBlendCoeff)
+static constexpr BlendFormula MakeSAModulateFormula(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+    return BlendFormula(BlendFormula::kSAModulate_OutputType, BlendFormula::kNone_OutputType,
+                        kAdd_GrBlendEquation, srcCoeff, dstCoeff);
+}
 
 /**
  * When there is coverage, the equation with f=coverage is:
@@ -182,10 +224,11 @@
  *
  * Xfer modes: dst-atop (Sa!=1)
  */
-#define COVERAGE_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, SRC_COEFF) \
-    INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \
-                       ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \
-                       kAdd_GrBlendEquation, SRC_COEFF, kIS2C_GrBlendCoeff)
+static constexpr BlendFormula MakeCoverageFormula(
+        BlendFormula::OutputType oneMinusDstCoeffModulateOutput, GrBlendCoeff srcCoeff) {
+    return BlendFormula(BlendFormula::kModulate_OutputType, oneMinusDstCoeffModulateOutput,
+                        kAdd_GrBlendEquation, srcCoeff, kIS2C_GrBlendCoeff);
+}
 
 /**
  * When there is coverage and the src coeff is Zero, the equation with f=coverage becomes:
@@ -201,10 +244,11 @@
  *
  * Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1)
  */
-#define COVERAGE_SRC_COEFF_ZERO_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT) \
-    INIT_BLEND_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \
-                       BlendFormula::kNone_OutputType, \
-                       kReverseSubtract_GrBlendEquation, kDC_GrBlendCoeff, kOne_GrBlendCoeff)
+static constexpr BlendFormula MakeCoverageSrcCoeffZeroFormula(
+        BlendFormula::OutputType oneMinusDstCoeffModulateOutput) {
+    return BlendFormula(oneMinusDstCoeffModulateOutput, BlendFormula::kNone_OutputType,
+                        kReverseSubtract_GrBlendEquation, kDC_GrBlendCoeff, kOne_GrBlendCoeff);
+}
 
 /**
  * When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes:
@@ -216,109 +260,123 @@
  *
  * Xfer modes (Sa!=1): src, src-in, src-out
  */
-#define COVERAGE_DST_COEFF_ZERO_FORMULA(SRC_COEFF) \
-    INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \
-                       BlendFormula::kCoverage_OutputType, \
-                       kAdd_GrBlendEquation, SRC_COEFF, kIS2A_GrBlendCoeff)
+static constexpr BlendFormula MakeCoverageDstCoeffZeroFormula(GrBlendCoeff srcCoeff) {
+    return BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kCoverage_OutputType,
+                        kAdd_GrBlendEquation, srcCoeff, kIS2A_GrBlendCoeff);
+}
+
+// Older GCC won't like the constexpr arrays because of
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61484.
+// MSVC crashes with an internal compiler error.
+#if !defined(__clang__) && ((defined(__GNUC__) && __GNUC__ < 5) || defined(_MSC_VER))
+#   define MAYBE_CONSTEXPR const
+#else
+#   define MAYBE_CONSTEXPR constexpr
+#endif
 
 /**
  * This table outlines the blend formulas we will use with each xfermode, with and without coverage,
  * with and without an opaque input color. Optimization properties are deduced at compile time so we
  * can make runtime decisions quickly. RGB coverage is not supported.
  */
-static const BlendFormula gBlendTable[2][2][(int)SkBlendMode::kLastCoeffMode + 1] = {
-
+static MAYBE_CONSTEXPR BlendFormula gBlendTable[2][2][(int)SkBlendMode::kLastCoeffMode + 1] = {
                      /*>> No coverage, input color unknown <<*/ {{
 
-    /* clear */      DST_CLEAR_FORMULA,
-    /* src */        COEFF_FORMULA(   kOne_GrBlendCoeff,    kZero_GrBlendCoeff),
-    /* dst */        NO_DST_WRITE_FORMULA,
-    /* src-over */   COEFF_FORMULA(   kOne_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* dst-over */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* src-in */     COEFF_FORMULA(   kDA_GrBlendCoeff,     kZero_GrBlendCoeff),
-    /* dst-in */     COEFF_FORMULA(   kZero_GrBlendCoeff,   kSA_GrBlendCoeff),
-    /* src-out */    COEFF_FORMULA(   kIDA_GrBlendCoeff,    kZero_GrBlendCoeff),
-    /* dst-out */    COEFF_FORMULA(   kZero_GrBlendCoeff,   kISA_GrBlendCoeff),
-    /* src-atop */   COEFF_FORMULA(   kDA_GrBlendCoeff,     kISA_GrBlendCoeff),
-    /* dst-atop */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kSA_GrBlendCoeff),
-    /* xor */        COEFF_FORMULA(   kIDA_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* plus */       COEFF_FORMULA(   kOne_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* modulate */   COEFF_FORMULA(   kZero_GrBlendCoeff,   kSC_GrBlendCoeff),
-    /* screen */     COEFF_FORMULA(   kOne_GrBlendCoeff,    kISC_GrBlendCoeff),
+    /* clear */      MakeCoeffFormula(kZero_GrBlendCoeff, kZero_GrBlendCoeff),
+    /* src */        MakeCoeffFormula(kOne_GrBlendCoeff,  kZero_GrBlendCoeff),
+    /* dst */        MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-over */   MakeCoeffFormula(kOne_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* dst-over */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* src-in */     MakeCoeffFormula(kDA_GrBlendCoeff,   kZero_GrBlendCoeff),
+    /* dst-in */     MakeCoeffFormula(kZero_GrBlendCoeff, kSA_GrBlendCoeff),
+    /* src-out */    MakeCoeffFormula(kIDA_GrBlendCoeff,  kZero_GrBlendCoeff),
+    /* dst-out */    MakeCoeffFormula(kZero_GrBlendCoeff, kISA_GrBlendCoeff),
+    /* src-atop */   MakeCoeffFormula(kDA_GrBlendCoeff,   kISA_GrBlendCoeff),
+    /* dst-atop */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kSA_GrBlendCoeff),
+    /* xor */        MakeCoeffFormula(kIDA_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* plus */       MakeCoeffFormula(kOne_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* modulate */   MakeCoeffFormula(kZero_GrBlendCoeff, kSC_GrBlendCoeff),
+    /* screen */     MakeCoeffFormula(kOne_GrBlendCoeff,  kISC_GrBlendCoeff),
 
                      }, /*>> Has coverage, input color unknown <<*/ {
 
-    /* clear */      COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
-    /* src */        COVERAGE_DST_COEFF_ZERO_FORMULA(kOne_GrBlendCoeff),
-    /* dst */        NO_DST_WRITE_FORMULA,
-    /* src-over */   COEFF_FORMULA(   kOne_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* dst-over */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* src-in */     COVERAGE_DST_COEFF_ZERO_FORMULA(kDA_GrBlendCoeff),
-    /* dst-in */     COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISAModulate_OutputType),
-    /* src-out */    COVERAGE_DST_COEFF_ZERO_FORMULA(kIDA_GrBlendCoeff),
-    /* dst-out */    COEFF_FORMULA(   kZero_GrBlendCoeff,   kISA_GrBlendCoeff),
-    /* src-atop */   COEFF_FORMULA(   kDA_GrBlendCoeff,     kISA_GrBlendCoeff),
-    /* dst-atop */   COVERAGE_FORMULA(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff),
-    /* xor */        COEFF_FORMULA(   kIDA_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* plus */       COEFF_FORMULA(   kOne_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* modulate */   COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType),
-    /* screen */     COEFF_FORMULA(   kOne_GrBlendCoeff,    kISC_GrBlendCoeff),
+    /* clear */      MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
+    /* src */        MakeCoverageDstCoeffZeroFormula(kOne_GrBlendCoeff),
+    /* dst */        MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-over */   MakeCoeffFormula(kOne_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* dst-over */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* src-in */     MakeCoverageDstCoeffZeroFormula(kDA_GrBlendCoeff),
+    /* dst-in */     MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
+    /* src-out */    MakeCoverageDstCoeffZeroFormula(kIDA_GrBlendCoeff),
+    /* dst-out */    MakeCoeffFormula(kZero_GrBlendCoeff, kISA_GrBlendCoeff),
+    /* src-atop */   MakeCoeffFormula(kDA_GrBlendCoeff,   kISA_GrBlendCoeff),
+    /* dst-atop */   MakeCoverageFormula(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff),
+    /* xor */        MakeCoeffFormula(kIDA_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* plus */       MakeCoeffFormula(kOne_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* modulate */   MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
+    /* screen */     MakeCoeffFormula(kOne_GrBlendCoeff,  kISC_GrBlendCoeff),
 
                      }}, /*>> No coverage, input color opaque <<*/ {{
 
-    /* clear */      DST_CLEAR_FORMULA,
-    /* src */        COEFF_FORMULA(   kOne_GrBlendCoeff,    kZero_GrBlendCoeff),
-    /* dst */        NO_DST_WRITE_FORMULA,
-    /* src-over */   COEFF_FORMULA(   kOne_GrBlendCoeff,    kZero_GrBlendCoeff),
-    /* dst-over */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* src-in */     COEFF_FORMULA(   kDA_GrBlendCoeff,     kZero_GrBlendCoeff),
-    /* dst-in */     NO_DST_WRITE_FORMULA,
-    /* src-out */    COEFF_FORMULA(   kIDA_GrBlendCoeff,    kZero_GrBlendCoeff),
-    /* dst-out */    DST_CLEAR_FORMULA,
-    /* src-atop */   COEFF_FORMULA(   kDA_GrBlendCoeff,     kZero_GrBlendCoeff),
-    /* dst-atop */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* xor */        COEFF_FORMULA(   kIDA_GrBlendCoeff,    kZero_GrBlendCoeff),
-    /* plus */       COEFF_FORMULA(   kOne_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* modulate */   COEFF_FORMULA(   kZero_GrBlendCoeff,   kSC_GrBlendCoeff),
-    /* screen */     COEFF_FORMULA(   kOne_GrBlendCoeff,    kISC_GrBlendCoeff),
+    /* clear */      MakeCoeffFormula(kZero_GrBlendCoeff, kZero_GrBlendCoeff),
+    /* src */        MakeCoeffFormula(kOne_GrBlendCoeff,  kZero_GrBlendCoeff),
+    /* dst */        MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-over */   MakeCoeffFormula(kOne_GrBlendCoeff,  kISA_GrBlendCoeff), // see comment below
+    /* dst-over */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* src-in */     MakeCoeffFormula(kDA_GrBlendCoeff,   kZero_GrBlendCoeff),
+    /* dst-in */     MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-out */    MakeCoeffFormula(kIDA_GrBlendCoeff,  kZero_GrBlendCoeff),
+    /* dst-out */    MakeCoeffFormula(kZero_GrBlendCoeff, kZero_GrBlendCoeff),
+    /* src-atop */   MakeCoeffFormula(kDA_GrBlendCoeff,   kZero_GrBlendCoeff),
+    /* dst-atop */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* xor */        MakeCoeffFormula(kIDA_GrBlendCoeff,  kZero_GrBlendCoeff),
+    /* plus */       MakeCoeffFormula(kOne_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* modulate */   MakeCoeffFormula(kZero_GrBlendCoeff, kSC_GrBlendCoeff),
+    /* screen */     MakeCoeffFormula(kOne_GrBlendCoeff,  kISC_GrBlendCoeff),
 
                      }, /*>> Has coverage, input color opaque <<*/ {
 
-    /* clear */      COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
-    /* src */        COEFF_FORMULA(   kOne_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* dst */        NO_DST_WRITE_FORMULA,
-    /* src-over */   COEFF_FORMULA(   kOne_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* dst-over */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* src-in */     COEFF_FORMULA(   kDA_GrBlendCoeff,     kISA_GrBlendCoeff),
-    /* dst-in */     NO_DST_WRITE_FORMULA,
-    /* src-out */    COEFF_FORMULA(   kIDA_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* dst-out */    COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
-    /* src-atop */   COEFF_FORMULA(   kDA_GrBlendCoeff,     kISA_GrBlendCoeff),
-    /* dst-atop */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* xor */        COEFF_FORMULA(   kIDA_GrBlendCoeff,    kISA_GrBlendCoeff),
-    /* plus */       COEFF_FORMULA(   kOne_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* modulate */   COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType),
-    /* screen */     COEFF_FORMULA(   kOne_GrBlendCoeff,    kISC_GrBlendCoeff),
+    /* clear */      MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
+    /* src */        MakeCoeffFormula(kOne_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* dst */        MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-over */   MakeCoeffFormula(kOne_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* dst-over */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* src-in */     MakeCoeffFormula(kDA_GrBlendCoeff,   kISA_GrBlendCoeff),
+    /* dst-in */     MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-out */    MakeCoeffFormula(kIDA_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* dst-out */    MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
+    /* src-atop */   MakeCoeffFormula(kDA_GrBlendCoeff,   kISA_GrBlendCoeff),
+    /* dst-atop */   MakeCoeffFormula(kIDA_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* xor */        MakeCoeffFormula(kIDA_GrBlendCoeff,  kISA_GrBlendCoeff),
+    /* plus */       MakeCoeffFormula(kOne_GrBlendCoeff,  kOne_GrBlendCoeff),
+    /* modulate */   MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
+    /* screen */     MakeCoeffFormula(kOne_GrBlendCoeff,  kISC_GrBlendCoeff),
 }}};
+// In the above table src-over is not optimized to src mode when the color is opaque because we
+// found no advantage to doing so. Also, we are using a global src-over XP in most cases which is
+// not specialized for opaque input. If the table were set to use the src formula then we'd have to
+// change when we use this global XP to keep analysis and practice in sync.
 
-static const BlendFormula gLCDBlendTable[(int)SkBlendMode::kLastCoeffMode + 1] = {
-    /* clear */      COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
-    /* src */        COVERAGE_FORMULA(BlendFormula::kCoverage_OutputType, kOne_GrBlendCoeff),
-    /* dst */        NO_DST_WRITE_FORMULA,
-    /* src-over */   COVERAGE_FORMULA(BlendFormula::kSAModulate_OutputType, kOne_GrBlendCoeff),
-    /* dst-over */   COEFF_FORMULA(   kIDA_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* src-in */     COVERAGE_FORMULA(BlendFormula::kCoverage_OutputType, kDA_GrBlendCoeff),
-    /* dst-in */     COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISAModulate_OutputType),
-    /* src-out */    COVERAGE_FORMULA(BlendFormula::kCoverage_OutputType, kIDA_GrBlendCoeff),
-    /* dst-out */    COEFF_FORMULA_SA_MODULATE(   kZero_GrBlendCoeff,   kISC_GrBlendCoeff),
-    /* src-atop */   COVERAGE_FORMULA(BlendFormula::kSAModulate_OutputType, kDA_GrBlendCoeff),
-    /* dst-atop */   COVERAGE_FORMULA(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff),
-    /* xor */        COVERAGE_FORMULA(BlendFormula::kSAModulate_OutputType, kIDA_GrBlendCoeff),
-    /* plus */       COEFF_FORMULA(   kOne_GrBlendCoeff,    kOne_GrBlendCoeff),
-    /* modulate */   COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType),
-    /* screen */     COEFF_FORMULA(   kOne_GrBlendCoeff,    kISC_GrBlendCoeff),
+static MAYBE_CONSTEXPR BlendFormula gLCDBlendTable[(int)SkBlendMode::kLastCoeffMode + 1] = {
+    /* clear */      MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
+    /* src */        MakeCoverageFormula(BlendFormula::kCoverage_OutputType, kOne_GrBlendCoeff),
+    /* dst */        MakeCoeffFormula(kZero_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-over */   MakeCoverageFormula(BlendFormula::kSAModulate_OutputType, kOne_GrBlendCoeff),
+    /* dst-over */   MakeCoeffFormula(kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* src-in */     MakeCoverageFormula(BlendFormula::kCoverage_OutputType, kDA_GrBlendCoeff),
+    /* dst-in */     MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
+    /* src-out */    MakeCoverageFormula(BlendFormula::kCoverage_OutputType, kIDA_GrBlendCoeff),
+    /* dst-out */    MakeSAModulateFormula(kZero_GrBlendCoeff, kISC_GrBlendCoeff),
+    /* src-atop */   MakeCoverageFormula(BlendFormula::kSAModulate_OutputType, kDA_GrBlendCoeff),
+    /* dst-atop */   MakeCoverageFormula(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff),
+    /* xor */        MakeCoverageFormula(BlendFormula::kSAModulate_OutputType, kIDA_GrBlendCoeff),
+    /* plus */       MakeCoeffFormula(kOne_GrBlendCoeff, kOne_GrBlendCoeff),
+    /* modulate */   MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
+    /* screen */     MakeCoeffFormula(kOne_GrBlendCoeff, kISC_GrBlendCoeff),
 };
 
+#undef MAYBE_CONSTEXPR
+
 static BlendFormula get_blend_formula(bool isOpaque,
                                       bool hasCoverage,
                                       bool hasMixedSamples,
@@ -338,7 +396,9 @@
 
 class PorterDuffXferProcessor : public GrXferProcessor {
 public:
-    PorterDuffXferProcessor(BlendFormula blendFormula) : fBlendFormula(blendFormula) {
+    PorterDuffXferProcessor(BlendFormula blendFormula, GrProcessorAnalysisCoverage coverage)
+            : INHERITED(false, false, coverage)
+            , fBlendFormula(blendFormula) {
         this->initClassID<PorterDuffXferProcessor>();
     }
 
@@ -349,16 +409,14 @@
     BlendFormula getBlendFormula() const { return fBlendFormula; }
 
 private:
-    GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override;
-
     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
 
     bool onHasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); }
 
     void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
-        blendInfo->fEquation = fBlendFormula.fBlendEquation;
-        blendInfo->fSrcBlend = fBlendFormula.fSrcCoeff;
-        blendInfo->fDstBlend = fBlendFormula.fDstCoeff;
+        blendInfo->fEquation = fBlendFormula.equation();
+        blendInfo->fSrcBlend = fBlendFormula.srcCoeff();
+        blendInfo->fDstBlend = fBlendFormula.dstCoeff();
         blendInfo->fWriteColor = fBlendFormula.modifiesDst();
     }
 
@@ -410,8 +468,8 @@
 public:
     static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) {
         const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>();
-        b->add32(xp.getBlendFormula().fPrimaryOutputType |
-                 (xp.getBlendFormula().fSecondaryOutputType << 3));
+        b->add32(xp.getBlendFormula().primaryOutput() |
+                 (xp.getBlendFormula().secondaryOutput() << 3));
         GR_STATIC_ASSERT(BlendFormula::kLast_OutputType < 8);
     }
 
@@ -422,11 +480,11 @@
 
         BlendFormula blendFormula = xp.getBlendFormula();
         if (blendFormula.hasSecondaryOutput()) {
-            append_color_output(xp, fragBuilder, blendFormula.fSecondaryOutputType,
+            append_color_output(xp, fragBuilder, blendFormula.secondaryOutput(),
                                 args.fOutputSecondary, args.fInputColor, args.fInputCoverage);
         }
-        append_color_output(xp, fragBuilder, blendFormula.fPrimaryOutputType,
-                            args.fOutputPrimary, args.fInputColor, args.fInputCoverage);
+        append_color_output(xp, fragBuilder, blendFormula.primaryOutput(), args.fOutputPrimary,
+                            args.fInputColor, args.fInputCoverage);
     }
 
     void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) override {}
@@ -445,33 +503,13 @@
     return new GLPorterDuffXferProcessor;
 }
 
-GrXferProcessor::OptFlags PorterDuffXferProcessor::onGetOptimizations(
-        const FragmentProcessorAnalysis& analysis) const {
-    GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_OptFlags;
-    if (!fBlendFormula.modifiesDst()) {
-        optFlags |= (GrXferProcessor::kIgnoreColor_OptFlag |
-                     GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag);
-    } else {
-        if (!fBlendFormula.usesInputColor()) {
-            optFlags |= GrXferProcessor::kIgnoreColor_OptFlag;
-        }
-        if (analysis.isCompatibleWithCoverageAsAlpha() &&
-            fBlendFormula.canTweakAlphaForCoverage()) {
-            optFlags |= GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
-        }
-    }
-    return optFlags;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 class ShaderPDXferProcessor : public GrXferProcessor {
 public:
-    ShaderPDXferProcessor(const DstTexture* dstTexture,
-                          bool hasMixedSamples,
-                          SkBlendMode xfermode)
-        : INHERITED(dstTexture, true, hasMixedSamples)
-        , fXfermode(xfermode) {
+    ShaderPDXferProcessor(bool hasMixedSamples, SkBlendMode xfermode,
+                          GrProcessorAnalysisCoverage coverage)
+            : INHERITED(true, hasMixedSamples, coverage), fXfermode(xfermode) {
         this->initClassID<ShaderPDXferProcessor>();
     }
 
@@ -482,10 +520,6 @@
     SkBlendMode getXfermode() const { return fXfermode; }
 
 private:
-    GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override {
-        return kNone_OptFlags;
-    }
-
     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
 
     bool onIsEqual(const GrXferProcessor& xpBase) const override {
@@ -545,7 +579,8 @@
 
 class PDLCDXferProcessor : public GrXferProcessor {
 public:
-    static GrXferProcessor* Create(SkBlendMode xfermode, const FragmentProcessorAnalysis& analysis);
+    static sk_sp<const GrXferProcessor> Make(SkBlendMode mode,
+                                             const GrProcessorAnalysisColor& inputColor);
 
     ~PDLCDXferProcessor() override;
 
@@ -558,8 +593,6 @@
 private:
     PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha);
 
-    GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override;
-
     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
 
     void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
@@ -623,24 +656,25 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha)
-    : fBlendConstant(blendConstant)
+    : INHERITED(false, false, GrProcessorAnalysisCoverage::kLCD)
+    , fBlendConstant(blendConstant)
     , fAlpha(alpha) {
     this->initClassID<PDLCDXferProcessor>();
 }
 
-GrXferProcessor* PDLCDXferProcessor::Create(SkBlendMode xfermode,
-                                            const FragmentProcessorAnalysis& analysis) {
-    if (SkBlendMode::kSrcOver != xfermode) {
+sk_sp<const GrXferProcessor> PDLCDXferProcessor::Make(SkBlendMode mode,
+                                                      const GrProcessorAnalysisColor& color) {
+    if (SkBlendMode::kSrcOver != mode) {
         return nullptr;
     }
     GrColor blendConstant;
-    if (!analysis.hasKnownOutputColor(&blendConstant)) {
+    if (!color.isConstant(&blendConstant)) {
         return nullptr;
     }
     blendConstant = GrUnpremulColor(blendConstant);
     uint8_t alpha = GrColorUnpackA(blendConstant);
     blendConstant |= (0xff << GrColor_SHIFT_A);
-    return new PDLCDXferProcessor(blendConstant, alpha);
+    return sk_sp<GrXferProcessor>(new PDLCDXferProcessor(blendConstant, alpha));
 }
 
 PDLCDXferProcessor::~PDLCDXferProcessor() {
@@ -655,11 +689,6 @@
     return new GLPDLCDXferProcessor(*this);
 }
 
-GrXferProcessor::OptFlags PDLCDXferProcessor::onGetOptimizations(
-        const FragmentProcessorAnalysis&) const {
-    return GrXferProcessor::kIgnoreColor_OptFlag;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 constexpr GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkBlendMode xfermode)
@@ -729,71 +758,100 @@
     }
 }
 
-GrXferProcessor* GrPorterDuffXPFactory::onCreateXferProcessor(
-        const GrCaps& caps,
-        const FragmentProcessorAnalysis& analysis,
-        bool hasMixedSamples,
-        const DstTexture* dstTexture) const {
+sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::makeXferProcessor(
+        const GrProcessorAnalysisColor& color, GrProcessorAnalysisCoverage coverage,
+        bool hasMixedSamples, const GrCaps& caps) const {
     BlendFormula blendFormula;
-    if (analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kLCD) {
-        if (SkBlendMode::kSrcOver == fBlendMode && analysis.hasKnownOutputColor() &&
+    bool isLCD = coverage == GrProcessorAnalysisCoverage::kLCD;
+    if (isLCD) {
+        if (SkBlendMode::kSrcOver == fBlendMode && color.isConstant() && color.isOpaque() &&
             !caps.shaderCaps()->dualSourceBlendingSupport() &&
             !caps.shaderCaps()->dstReadInShaderSupport()) {
             // If we don't have dual source blending or in shader dst reads, we fall back to this
             // trick for rendering SrcOver LCD text instead of doing a dst copy.
-            SkASSERT(!dstTexture || !dstTexture->texture());
-            return PDLCDXferProcessor::Create(fBlendMode, analysis);
+            return PDLCDXferProcessor::Make(fBlendMode, color);
         }
         blendFormula = get_lcd_blend_formula(fBlendMode);
     } else {
-        blendFormula = get_blend_formula(analysis.isOutputColorOpaque(), analysis.hasCoverage(),
-                                         hasMixedSamples, fBlendMode);
+        blendFormula =
+                get_blend_formula(color.isOpaque(), GrProcessorAnalysisCoverage::kNone != coverage,
+                                  hasMixedSamples, fBlendMode);
     }
 
-    if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
-        return new ShaderPDXferProcessor(dstTexture, hasMixedSamples, fBlendMode);
+    if ((blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) ||
+        (isLCD && (SkBlendMode::kSrcOver != fBlendMode || !color.isOpaque()))) {
+        return sk_sp<const GrXferProcessor>(new ShaderPDXferProcessor(hasMixedSamples, fBlendMode,
+                                                                      coverage));
     }
-
-    SkASSERT(!dstTexture || !dstTexture->texture());
-    return new PorterDuffXferProcessor(blendFormula);
+    return sk_sp<const GrXferProcessor>(new PorterDuffXferProcessor(blendFormula, coverage));
 }
 
-bool GrPorterDuffXPFactory::canCombineOverlappedStencilAndCover(bool colorIsOpaque) const {
-    // Ignore the effect of coverage here.
-    BlendFormula colorFormula = gBlendTable[colorIsOpaque][0][(int)fBlendMode];
-    SkASSERT(kAdd_GrBlendEquation == colorFormula.fBlendEquation);
-    return !colorFormula.usesDstColor();
-}
-
-bool GrPorterDuffXPFactory::willReadDstInShader(const GrCaps& caps,
-                                                const FragmentProcessorAnalysis& analysis) const {
-    if (caps.shaderCaps()->dualSourceBlendingSupport()) {
-        return false;
+static inline GrXPFactory::AnalysisProperties analysis_properties(
+        const GrProcessorAnalysisColor& color, const GrProcessorAnalysisCoverage& coverage,
+        const GrCaps& caps, SkBlendMode mode) {
+    using AnalysisProperties = GrXPFactory::AnalysisProperties;
+    AnalysisProperties props = AnalysisProperties::kNone;
+    bool hasCoverage = GrProcessorAnalysisCoverage::kNone != coverage;
+    bool isLCD = GrProcessorAnalysisCoverage::kLCD == coverage;
+    BlendFormula formula;
+    if (isLCD) {
+        formula = gLCDBlendTable[(int)mode];
+    } else {
+        formula = gBlendTable[color.isOpaque()][hasCoverage][(int)mode];
     }
 
-    // When we have four channel coverage we always need to read the dst in order to correctly
-    // blend. The one exception is when we are using srcover mode and we know the input color into
-    // the XP.
-    if (analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kLCD) {
-        if (SkBlendMode::kSrcOver == fBlendMode && analysis.hasKnownOutputColor() &&
+    if (formula.canTweakAlphaForCoverage() && !isLCD) {
+        props |= AnalysisProperties::kCompatibleWithAlphaAsCoverage;
+    }
+
+    if (isLCD) {
+        // See comment in MakeSrcOverXferProcessor about color.isOpaque here
+        if (SkBlendMode::kSrcOver == mode && color.isConstant() && /*color.isOpaque() &&*/
+            !caps.shaderCaps()->dualSourceBlendingSupport() &&
             !caps.shaderCaps()->dstReadInShaderSupport()) {
-            return false;
+            props |= AnalysisProperties::kIgnoresInputColor;
+        } else {
+            // For LCD blending, if the color is not opaque we must read the dst in shader even if
+            // we have dual source blending. The opaqueness check must be done after blending so for
+            // simplicity we only allow src-over to not take the dst read path (though src, src-in,
+            // and DstATop would also work). We also fall into the dst read case for src-over if we
+            // do not have dual source blending.
+            if (SkBlendMode::kSrcOver != mode ||
+                !color.isOpaque() ||
+                !caps.shaderCaps()->dualSourceBlendingSupport()) {
+                props |= AnalysisProperties::kReadsDstInShader;
+            }
         }
-        return get_lcd_blend_formula(fBlendMode).hasSecondaryOutput();
+    } else {
+        // With dual-source blending we never need the destination color in the shader.
+        if (!caps.shaderCaps()->dualSourceBlendingSupport()) {
+            // Mixed samples implicity computes a fractional coverage from sample coverage. This
+            // could affect the formula used. However, we don't expect to have mixed samples without
+            // dual source blending.
+            SkASSERT(!caps.usesMixedSamples());
+            if (formula.hasSecondaryOutput()) {
+                props |= AnalysisProperties::kReadsDstInShader;
+            }
+        }
     }
 
-    // We fallback on the shader XP when the blend formula would use dual source blending but we
-    // don't have support for it.
-    static const bool kHasMixedSamples = false;
-    SkASSERT(!caps.usesMixedSamples()); // We never use mixed samples without dual source blending.
-    auto formula = get_blend_formula(analysis.isOutputColorOpaque(), analysis.hasCoverage(),
-                                     kHasMixedSamples, fBlendMode);
-    return formula.hasSecondaryOutput();
+    if (!formula.modifiesDst() || !formula.usesInputColor()) {
+        props |= AnalysisProperties::kIgnoresInputColor;
+    }
+    // Ignore the effect of coverage here for overlap stencil and cover property
+    auto colorFormula = gBlendTable[color.isOpaque()][0][(int)mode];
+    SkASSERT(kAdd_GrBlendEquation == colorFormula.equation());
+    if (!colorFormula.usesDstColor()) {
+        props |= AnalysisProperties::kCanCombineOverlappedStencilAndCover;
+    }
+    return props;
 }
 
-bool GrPorterDuffXPFactory::compatibleWithCoverageAsAlpha(bool colorIsOpaque) const {
-    // We assume we have coverage (or else this doesn't matter).
-    return gBlendTable[colorIsOpaque][1][(int)fBlendMode].canTweakAlphaForCoverage();
+GrXPFactory::AnalysisProperties GrPorterDuffXPFactory::analysisProperties(
+        const GrProcessorAnalysisColor& color,
+        const GrProcessorAnalysisCoverage& coverage,
+        const GrCaps& caps) const {
+    return analysis_properties(color, coverage, caps, fBlendMode);
 }
 
 GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory);
@@ -813,31 +871,29 @@
         return;
     }
     BlendFormula blendFormula = static_cast<const PorterDuffXferProcessor*>(xp)->getBlendFormula();
-    *outPrimary = blendFormula.fPrimaryOutputType;
-    *outSecondary = blendFormula.fSecondaryOutputType;
+    *outPrimary = blendFormula.primaryOutput();
+    *outSecondary = blendFormula.secondaryOutput();
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////
 // SrcOver Global functions
 ////////////////////////////////////////////////////////////////////////////////////////////////
 const GrXferProcessor& GrPorterDuffXPFactory::SimpleSrcOverXP() {
-    static BlendFormula gSrcOverBlendFormula = COEFF_FORMULA(kOne_GrBlendCoeff,
-                                                             kISA_GrBlendCoeff);
-    static PorterDuffXferProcessor gSrcOverXP(gSrcOverBlendFormula);
+    static BlendFormula gSrcOverBlendFormula =
+            MakeCoeffFormula(kOne_GrBlendCoeff, kISA_GrBlendCoeff);
+    static PorterDuffXferProcessor gSrcOverXP(gSrcOverBlendFormula,
+                                              GrProcessorAnalysisCoverage::kSingleChannel);
     return gSrcOverXP;
 }
 
-GrXferProcessor* GrPorterDuffXPFactory::CreateSrcOverXferProcessor(
-        const GrCaps& caps,
-        const FragmentProcessorAnalysis& analysis,
-        bool hasMixedSamples,
-        const GrXferProcessor::DstTexture* dstTexture) {
-
+sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::MakeSrcOverXferProcessor(
+        const GrProcessorAnalysisColor& color, GrProcessorAnalysisCoverage coverage,
+        bool hasMixedSamples, const GrCaps& caps) {
     // We want to not make an xfer processor if possible. Thus for the simple case where we are not
     // doing lcd blending we will just use our global SimpleSrcOverXP. This slightly differs from
     // the general case where we convert a src-over blend that has solid coverage and an opaque
     // color to src-mode, which allows disabling of blending.
-    if (analysis.outputCoverageType() != GrPipelineAnalysisCoverage::kLCD) {
+    if (coverage != GrProcessorAnalysisCoverage::kLCD) {
         // We return nullptr here, which our caller interprets as meaning "use SimpleSrcOverXP".
         // We don't simply return the address of that XP here because our caller would have to unref
         // it and since it is a global object and GrProgramElement's ref-cnting system is not thread
@@ -845,55 +901,40 @@
         return nullptr;
     }
 
-    if (analysis.hasKnownOutputColor() && !caps.shaderCaps()->dualSourceBlendingSupport() &&
+    // Currently up the stack Skia is requiring that the dst is opaque or that the client has said
+    // the opaqueness doesn't matter. Thus for src-over we don't need to worry about the src color
+    // being opaque or not. For now we disable the check for opaqueness, but in the future we should
+    // pass down the knowledge about dst opaqueness and make the correct decision here.
+    //
+    // This also fixes a chrome bug on macs where we are getting random fuzziness when doing
+    // blending in the shader for non opaque sources.
+    if (color.isConstant() && /*color.isOpaque() &&*/
+        !caps.shaderCaps()->dualSourceBlendingSupport() &&
         !caps.shaderCaps()->dstReadInShaderSupport()) {
         // If we don't have dual source blending or in shader dst reads, we fall
         // back to this trick for rendering SrcOver LCD text instead of doing a
         // dst copy.
-        SkASSERT(!dstTexture || !dstTexture->texture());
-        return PDLCDXferProcessor::Create(SkBlendMode::kSrcOver, analysis);
+        return PDLCDXferProcessor::Make(SkBlendMode::kSrcOver, color);
     }
 
     BlendFormula blendFormula;
     blendFormula = get_lcd_blend_formula(SkBlendMode::kSrcOver);
-    if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
-        return new ShaderPDXferProcessor(dstTexture, hasMixedSamples, SkBlendMode::kSrcOver);
+    if (!color.isOpaque() ||
+        (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport())) {
+        return sk_sp<GrXferProcessor>(
+                new ShaderPDXferProcessor(hasMixedSamples, SkBlendMode::kSrcOver, coverage));
     }
-
-    SkASSERT(!dstTexture || !dstTexture->texture());
-    return new PorterDuffXferProcessor(blendFormula);
+    return sk_sp<GrXferProcessor>(new PorterDuffXferProcessor(blendFormula, coverage));
 }
 
-sk_sp<GrXferProcessor> GrPorterDuffXPFactory::CreateNoCoverageXP(SkBlendMode blendmode) {
+sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::MakeNoCoverageXP(SkBlendMode blendmode) {
     BlendFormula formula = get_blend_formula(false, false, false, blendmode);
-    return sk_make_sp<PorterDuffXferProcessor>(formula);
+    return sk_make_sp<PorterDuffXferProcessor>(formula, GrProcessorAnalysisCoverage::kNone);
 }
 
-bool GrPorterDuffXPFactory::WillSrcOverNeedDstTexture(const GrCaps& caps,
-                                                      const FragmentProcessorAnalysis& analysis) {
-    if (caps.shaderCaps()->dstReadInShaderSupport() ||
-        caps.shaderCaps()->dualSourceBlendingSupport()) {
-        return false;
-    }
-
-    // When we have four channel coverage we always need to read the dst in order to correctly
-    // blend. The one exception is when we are using srcover mode and we know the input color
-    // into the XP.
-    if (analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kLCD) {
-        if (analysis.hasKnownOutputColor() && !caps.shaderCaps()->dstReadInShaderSupport()) {
-            return false;
-        }
-        auto formula = get_lcd_blend_formula(SkBlendMode::kSrcOver);
-        return formula.hasSecondaryOutput();
-    }
-
-    // We fallback on the shader XP when the blend formula would use dual source blending but we
-    // don't have support for it.
-    static const bool kHasMixedSamples = false;
-    bool isOpaque = analysis.isOutputColorOpaque();
-    bool hasCoverage = analysis.hasCoverage();
-    SkASSERT(!caps.usesMixedSamples()); // We never use mixed samples without dual source blending.
-    auto formula =
-            get_blend_formula(isOpaque, hasCoverage, kHasMixedSamples, SkBlendMode::kSrcOver);
-    return formula.hasSecondaryOutput();
+GrXPFactory::AnalysisProperties GrPorterDuffXPFactory::SrcOverAnalysisProperties(
+        const GrProcessorAnalysisColor& color,
+        const GrProcessorAnalysisCoverage& coverage,
+        const GrCaps& caps) {
+    return analysis_properties(color, coverage, caps, SkBlendMode::kSrcOver);
 }
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.h b/src/gpu/effects/GrPorterDuffXferProcessor.h
index 23f1e9c..c5680ca 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.h
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.h
@@ -23,37 +23,33 @@
 
     /** Because src-over is so common we special case it for performance reasons. If this returns
         null then the SimpleSrcOverXP() below should be used. */
-    static GrXferProcessor* CreateSrcOverXferProcessor(const GrCaps& caps,
-                                                       const FragmentProcessorAnalysis&,
-                                                       bool hasMixedSamples,
-                                                       const GrXferProcessor::DstTexture*);
+    static sk_sp<const GrXferProcessor> MakeSrcOverXferProcessor(const GrProcessorAnalysisColor&,
+                                                                 GrProcessorAnalysisCoverage,
+                                                                 bool hasMixedSamples,
+                                                                 const GrCaps&);
 
     /** Returns a simple non-LCD porter duff blend XP with no optimizations or coverage. */
-    static sk_sp<GrXferProcessor> CreateNoCoverageXP(SkBlendMode);
+    static sk_sp<const GrXferProcessor> MakeNoCoverageXP(SkBlendMode);
 
     /** This XP implements non-LCD src-over using hw blend with no optimizations. It is returned
         by reference because it is global and its ref-cnting methods are not thread safe. */
     static const GrXferProcessor& SimpleSrcOverXP();
 
-    static bool WillSrcOverNeedDstTexture(const GrCaps&, const FragmentProcessorAnalysis&);
-    static bool SrcOverIsCompatibleWithCoverageAsAlpha() { return true; }
-    static bool SrcOverCanCombineOverlappedStencilAndCover(bool colorIsOpaque) {
-        return colorIsOpaque;
-    }
+    static AnalysisProperties SrcOverAnalysisProperties(const GrProcessorAnalysisColor&,
+                                                        const GrProcessorAnalysisCoverage&,
+                                                        const GrCaps&);
 
 private:
     constexpr GrPorterDuffXPFactory(SkBlendMode);
 
-    bool canCombineOverlappedStencilAndCover(bool colorIsOpaque) const override;
+    sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
+                                                   GrProcessorAnalysisCoverage,
+                                                   bool hasMixedSamples,
+                                                   const GrCaps&) const override;
 
-    GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
-                                           const FragmentProcessorAnalysis&,
-                                           bool hasMixedSamples,
-                                           const DstTexture*) const override;
-
-    bool willReadDstInShader(const GrCaps&, const FragmentProcessorAnalysis&) const override;
-
-    bool compatibleWithCoverageAsAlpha(bool colorIsOpaque) const override;
+    AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
+                                          const GrProcessorAnalysisCoverage&,
+                                          const GrCaps&) const override;
 
     GR_DECLARE_XP_FACTORY_TEST;
     static void TestGetXPOutputTypes(const GrXferProcessor*, int* outPrimary, int* outSecondary);
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp
index 1b4772d..8d18150 100644
--- a/src/gpu/effects/GrRRectEffect.cpp
+++ b/src/gpu/effects/GrRRectEffect.cpp
@@ -135,7 +135,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fInnerRectUniform;
@@ -279,8 +279,7 @@
         fragBuilder->codeAppend("alpha = 1.0 - alpha;");
     }
 
-    fragBuilder->codeAppendf("%s = %s;", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
+    fragBuilder->codeAppendf("%s = %s * alpha;", args.fOutputColor, args.fInputColor);
 }
 
 void GLCircularRRectEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
@@ -291,7 +290,7 @@
 }
 
 void GLCircularRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                      const GrProcessor& processor) {
+                                      const GrFragmentProcessor& processor) {
     const CircularRRectEffect& crre = processor.cast<CircularRRectEffect>();
     const SkRRect& rrect = crre.getRRect();
     if (rrect != fPrevRRect) {
@@ -487,7 +486,7 @@
     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
 
 private:
     GrGLSLProgramDataManager::UniformHandle fInnerRectUniform;
@@ -590,8 +589,7 @@
         fragBuilder->codeAppend("float alpha = clamp(0.5 + approx_dist, 0.0, 1.0);");
     }
 
-    fragBuilder->codeAppendf("%s = %s;", args.fOutputColor,
-                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
+    fragBuilder->codeAppendf("%s = %s * alpha;", args.fOutputColor, args.fInputColor);
 }
 
 void GLEllipticalRRectEffect::GenKey(const GrProcessor& effect, const GrShaderCaps&,
@@ -602,7 +600,7 @@
 }
 
 void GLEllipticalRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman,
-                                        const GrProcessor& effect) {
+                                        const GrFragmentProcessor& effect) {
     const EllipticalRRectEffect& erre = effect.cast<EllipticalRRectEffect>();
     const SkRRect& rrect = erre.getRRect();
     // If we're using a scale factor to work around precision issues, choose the largest radius
diff --git a/src/gpu/effects/GrShadowGeoProc.cpp b/src/gpu/effects/GrShadowGeoProc.cpp
index e11a672..5ff7ec1 100644
--- a/src/gpu/effects/GrShadowGeoProc.cpp
+++ b/src/gpu/effects/GrShadowGeoProc.cpp
@@ -47,9 +47,7 @@
         fragBuilder->codeAppend("float d = length(shadowParams.xy);");
         fragBuilder->codeAppend("float distance = shadowParams.z * (1.0 - d);");
 
-        fragBuilder->codeAppend("float radius = shadowParams.w;");
-        
-        fragBuilder->codeAppend("float factor = 1.0 - clamp(distance/radius, 0.0, 1.0);");
+        fragBuilder->codeAppend("float factor = 1.0 - clamp(distance, 0.0, shadowParams.w);");
         fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
         fragBuilder->codeAppendf("%s = vec4(factor);",
                                  args.fOutputCoverage);
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 5d2c288..a41ed8b 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -63,7 +63,8 @@
     }
 
 protected:
-    void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& processor) override {
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& processor) override {
         const GrSimpleTextureEffect& textureEffect = processor.cast<GrSimpleTextureEffect>();
         if (SkToBool(textureEffect.colorSpaceXform())) {
             fColorSpaceHelper.setData(pdman, textureEffect.colorSpaceXform());
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
index b3030bf..0b01ac7 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -21,36 +21,17 @@
 class GrSimpleTextureEffect : public GrSingleTextureEffect {
 public:
     /* unfiltered, clamp mode */
-    static sk_sp<GrFragmentProcessor> Make(GrTexture* tex,
-                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                           const SkMatrix& matrix) {
-        return sk_sp<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(tex, std::move(colorSpaceXform), matrix,
-                                      GrSamplerParams::kNone_FilterMode));
-    }
-
-    /* clamp mode */
-    static sk_sp<GrFragmentProcessor> Make(GrTexture* tex,
-                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                           const SkMatrix& matrix,
-                                           GrSamplerParams::FilterMode filterMode) {
-        return sk_sp<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(tex, std::move(colorSpaceXform), matrix, filterMode));
-    }
-
-    static sk_sp<GrFragmentProcessor> Make(GrTexture* tex,
-                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                           const SkMatrix& matrix,
-                                           const GrSamplerParams& p) {
-        return sk_sp<GrFragmentProcessor>(new GrSimpleTextureEffect(tex, std::move(colorSpaceXform),
-                                                                    matrix, p));
-    }
-
-    /* unfiltered, clamp mode */
     static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
                                            sk_sp<GrTextureProxy> proxy,
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& matrix) {
+        // MDB TODO: remove this instantiation once instantiation is pushed past the
+        // TextureSamplers. Instantiation failure in the TextureSampler is difficult to
+        // recover from.
+        if (!proxy->instantiate(resourceProvider)) {
+            return nullptr;
+        }
+
         return sk_sp<GrFragmentProcessor>(
             new GrSimpleTextureEffect(resourceProvider, std::move(proxy),
                                       std::move(colorSpaceXform), matrix,
@@ -63,6 +44,13 @@
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& matrix,
                                            GrSamplerParams::FilterMode filterMode) {
+        // MDB TODO: remove this instantiation once instantiation is pushed past the
+        // TextureSamplers. Instantiation failure in the TextureSampler is difficult to
+        // recover from.
+        if (!proxy->instantiate(resourceProvider)) {
+            return nullptr;
+        }
+
         return sk_sp<GrFragmentProcessor>(
             new GrSimpleTextureEffect(resourceProvider, std::move(proxy),
                                       std::move(colorSpaceXform),
@@ -74,6 +62,13 @@
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& matrix,
                                            const GrSamplerParams& p) {
+        // MDB TODO: remove this instantiation once instantiation is pushed past the
+        // TextureSamplers. Instantiation failure in the TextureSampler is difficult to
+        // recover from.
+        if (!proxy->instantiate(resourceProvider)) {
+            return nullptr;
+        }
+
         return sk_sp<GrFragmentProcessor>(new GrSimpleTextureEffect(resourceProvider,
                                                                     std::move(proxy),
                                                                     std::move(colorSpaceXform),
@@ -85,24 +80,6 @@
     const char* name() const override { return "SimpleTexture"; }
 
 private:
-    GrSimpleTextureEffect(GrTexture* texture,
-                          sk_sp<GrColorSpaceXform> colorSpaceXform,
-                          const SkMatrix& matrix,
-                          GrSamplerParams::FilterMode filterMode)
-            : INHERITED(texture, std::move(colorSpaceXform), matrix, filterMode,
-                        ModulationFlags(texture->config())) {
-        this->initClassID<GrSimpleTextureEffect>();
-    }
-
-    GrSimpleTextureEffect(GrTexture* texture,
-                          sk_sp<GrColorSpaceXform>colorSpaceXform,
-                          const SkMatrix& matrix,
-                          const GrSamplerParams& params)
-            : INHERITED(texture, std::move(colorSpaceXform), matrix, params,
-                        ModulationFlags(texture->config())) {
-        this->initClassID<GrSimpleTextureEffect>();
-    }
-
     GrSimpleTextureEffect(GrResourceProvider*, sk_sp<GrTextureProxy>,
                           sk_sp<GrColorSpaceXform>, const SkMatrix& matrix,
                           GrSamplerParams::FilterMode);
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
index 4f624b3..f5d7000 100644
--- a/src/gpu/effects/GrSingleTextureEffect.cpp
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -9,49 +9,13 @@
 
 #include "GrTextureProxy.h"
 
-GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
-                                             sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                             const SkMatrix& m, OptimizationFlags optFlags)
-        : INHERITED(optFlags)
-        , fCoordTransform(m, texture, GrSamplerParams::kNone_FilterMode)
-        , fTextureSampler(texture)
-        , fColorSpaceXform(std::move(colorSpaceXform)) {
-    this->addCoordTransform(&fCoordTransform);
-    this->addTextureSampler(&fTextureSampler);
-}
-
-GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
-                                             sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                             const SkMatrix& m,
-                                             GrSamplerParams::FilterMode filterMode,
-                                             OptimizationFlags optFlags)
-        : INHERITED(optFlags)
-        , fCoordTransform(m, texture, filterMode)
-        , fTextureSampler(texture, filterMode)
-        , fColorSpaceXform(std::move(colorSpaceXform)) {
-    this->addCoordTransform(&fCoordTransform);
-    this->addTextureSampler(&fTextureSampler);
-}
-
-GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
-                                             sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                             const SkMatrix& m, const GrSamplerParams& params,
-                                             OptimizationFlags optFlags)
-        : INHERITED(optFlags)
-        , fCoordTransform(m, texture, params.filterMode())
-        , fTextureSampler(texture, params)
-        , fColorSpaceXform(std::move(colorSpaceXform)) {
-    this->addCoordTransform(&fCoordTransform);
-    this->addTextureSampler(&fTextureSampler);
-}
-
 GrSingleTextureEffect::GrSingleTextureEffect(GrResourceProvider* resourceProvider,
                                              OptimizationFlags optFlags,
                                              sk_sp<GrTextureProxy> proxy,
                                              sk_sp<GrColorSpaceXform> colorSpaceXform,
                                              const SkMatrix& m)
         : INHERITED(optFlags)
-        , fCoordTransform(resourceProvider, m, proxy.get(), GrSamplerParams::kNone_FilterMode)
+        , fCoordTransform(resourceProvider, m, proxy.get())
         , fTextureSampler(resourceProvider, std::move(proxy))
         , fColorSpaceXform(std::move(colorSpaceXform)) {
     this->addCoordTransform(&fCoordTransform);
@@ -65,7 +29,7 @@
                                              const SkMatrix& m,
                                              GrSamplerParams::FilterMode filterMode)
         : INHERITED(optFlags)
-        , fCoordTransform(resourceProvider, m, proxy.get(), filterMode)
+        , fCoordTransform(resourceProvider, m, proxy.get())
         , fTextureSampler(resourceProvider, std::move(proxy), filterMode)
         , fColorSpaceXform(std::move(colorSpaceXform)) {
     this->addCoordTransform(&fCoordTransform);
@@ -78,7 +42,7 @@
                                              sk_sp<GrColorSpaceXform> colorSpaceXform,
                                              const SkMatrix& m, const GrSamplerParams& params)
         : INHERITED(optFlags)
-        , fCoordTransform(resourceProvider, m, proxy.get(), params.filterMode())
+        , fCoordTransform(resourceProvider, m, proxy.get())
         , fTextureSampler(resourceProvider, std::move(proxy), params)
         , fColorSpaceXform(std::move(colorSpaceXform)) {
     this->addCoordTransform(&fCoordTransform);
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index 0ae5447..8c16d33 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -24,7 +24,7 @@
 public:
     SkString dumpInfo() const override {
         SkString str;
-        str.appendf("Texture: %d", fTextureSampler.texture()->uniqueID().asUInt());
+        str.appendf("Texture: %d", fTextureSampler.proxy()->uniqueID().asUInt());
         return str;
     }
 
@@ -32,18 +32,6 @@
 
 protected:
     /** unfiltered, clamp mode */
-    GrSingleTextureEffect(GrTexture*, sk_sp<GrColorSpaceXform>, const SkMatrix&,
-                          OptimizationFlags optFlags);
-    /** clamp mode */
-    GrSingleTextureEffect(GrTexture*, sk_sp<GrColorSpaceXform>, const SkMatrix&,
-                          GrSamplerParams::FilterMode filterMode, OptimizationFlags optFlags);
-    GrSingleTextureEffect(GrTexture*,
-                          sk_sp<GrColorSpaceXform>,
-                          const SkMatrix&,
-                          const GrSamplerParams&,
-                          OptimizationFlags optFlags);
-
-    /** unfiltered, clamp mode */
     GrSingleTextureEffect(GrResourceProvider*, OptimizationFlags, sk_sp<GrTextureProxy>,
                           sk_sp<GrColorSpaceXform>, const SkMatrix&);
     /** clamp mode */
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index 91b1e55..ebe4f04 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -305,10 +305,13 @@
         }
 
     protected:
-        void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& fp) override {
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& fp) override {
             const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
             const GrTextureDomain& domain = tde.fTextureDomain;
-            fGLDomain.setData(pdman, domain, tde.textureSampler(0).texture());
+            GrTexture* texture =  tde.textureSampler(0).peekTexture();
+
+            fGLDomain.setData(pdman, domain, texture);
             if (SkToBool(tde.colorSpaceXform())) {
                 fColorSpaceHelper.setData(pdman, tde.colorSpaceXform());
             }
@@ -407,10 +410,12 @@
         }
 
     protected:
-        void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& fp) override {
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& fp) override {
             const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
                     fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
-            GrTexture* texture = dstdfp.textureSampler(0).texture();
+            GrTexture* texture = dstdfp.textureSampler(0).peekTexture();
+
             fGLDomain.setData(pdman, dstdfp.fTextureDomain, texture);
             float iw = 1.f / texture->width();
             float ih = 1.f / texture->height();
@@ -436,7 +441,7 @@
 bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
     const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
             fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
-    return dstdfp.fTextureSampler.texture() == fTextureSampler.texture() &&
+    return dstdfp.fTextureSampler.proxy() == fTextureSampler.proxy() &&
            dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
            dstdfp.fTextureDomain == fTextureDomain;
 }
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index 0f6ca58..72817b2 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -122,7 +122,7 @@
 
         if (nullptr == row) {
             // force a flush, which should unlock all the rows; then try again
-            fDesc.fContext->flush();
+            fDesc.fContext->contextPriv().flush(nullptr); // tighten this up?
             row = this->getLRU();
             if (nullptr == row) {
                 --fLockedRows;
@@ -153,8 +153,6 @@
         fKeyTable.insert(index, 1, &row);
         rowNumber = static_cast<int>(row - fRows);
 
-        SkAutoLockPixels lock(bitmap);
-
         SkASSERT(bitmap.width() == fDesc.fWidth);
         SkASSERT(bitmap.height() == fDesc.fRowHeight);
 
@@ -162,7 +160,7 @@
         // that is not currently in use
         fTexContext->writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(),
                                  0, rowNumber * fDesc.fRowHeight,
-                                 GrContext::kDontFlush_PixelOpsFlag);
+                                 GrContextPriv::kDontFlush_PixelOpsFlag);
     }
 
     SkASSERT(rowNumber >= 0);
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index 9671919..aa1c1c7 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -5,14 +5,15 @@
  * found in the LICENSE file.
  */
 
-#include "effects/GrXfermodeFragmentProcessor.h"
+#include "GrXfermodeFragmentProcessor.h"
 
+#include "GrConstColorProcessor.h"
 #include "GrFragmentProcessor.h"
-#include "effects/GrConstColorProcessor.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLBlend.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "SkGr.h"
+#include "SkXfermodePriv.h"
 
 // Some of the cpu implementations of blend modes differ too much from the GPU enough that
 // we can't use the cpu implementation to implement constantOutputForConstantInput.
@@ -415,7 +416,7 @@
         ComposeOneFragmentProcessor::Child child =
             args.fFp.cast<ComposeOneFragmentProcessor>().child();
         SkString childColor("child");
-        this->emitChild(0, nullptr, &childColor, args);
+        this->emitChild(0, &childColor, args);
 
         const char* inputColor = args.fInputColor;
         // We don't try to optimize for this case at all
diff --git a/include/gpu/effects/GrXfermodeFragmentProcessor.h b/src/gpu/effects/GrXfermodeFragmentProcessor.h
similarity index 100%
rename from include/gpu/effects/GrXfermodeFragmentProcessor.h
rename to src/gpu/effects/GrXfermodeFragmentProcessor.h
diff --git a/src/gpu/effects/GrYUVEffect.cpp b/src/gpu/effects/GrYUVEffect.cpp
index 14fa8c0..be4d6c2 100644
--- a/src/gpu/effects/GrYUVEffect.cpp
+++ b/src/gpu/effects/GrYUVEffect.cpp
@@ -131,7 +131,7 @@
 
     protected:
         void onSetData(const GrGLSLProgramDataManager& pdman,
-                       const GrProcessor& processor) override {
+                       const GrFragmentProcessor& processor) override {
             const YUVtoRGBEffect& yuvEffect = processor.cast<YUVtoRGBEffect>();
             switch (yuvEffect.getColorSpace()) {
                 case kJPEG_SkYUVColorSpace:
@@ -158,10 +158,9 @@
                    sk_sp<GrTextureProxy> vProxy, const SkMatrix yuvMatrix[3],
                    GrSamplerParams::FilterMode uvFilterMode, SkYUVColorSpace colorSpace, bool nv12)
             : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
-            , fYTransform(resourceProvider, yuvMatrix[0], yProxy.get(),
-                          GrSamplerParams::kNone_FilterMode)
+            , fYTransform(resourceProvider, yuvMatrix[0], yProxy.get())
             , fYSampler(resourceProvider, std::move(yProxy))
-            , fUTransform(resourceProvider, yuvMatrix[1], uProxy.get(), uvFilterMode)
+            , fUTransform(resourceProvider, yuvMatrix[1], uProxy.get())
             , fUSampler(resourceProvider, std::move(uProxy), uvFilterMode)
             , fVSampler(resourceProvider, vProxy, uvFilterMode)
             , fColorSpace(colorSpace)
@@ -172,8 +171,7 @@
         this->addCoordTransform(&fUTransform);
         this->addTextureSampler(&fUSampler);
         if (!fNV12) {
-            fVTransform = GrCoordTransform(resourceProvider, yuvMatrix[2], vProxy.get(),
-                                           uvFilterMode);
+            fVTransform = GrCoordTransform(resourceProvider, yuvMatrix[2], vProxy.get());
             this->addCoordTransform(&fVTransform);
             this->addTextureSampler(&fVSampler);
         }
@@ -287,7 +285,7 @@
 
     private:
         void onSetData(const GrGLSLProgramDataManager& pdman,
-                       const GrProcessor& processor) override {
+                       const GrFragmentProcessor& processor) override {
             const RGBToYUVEffect& effect = processor.cast<RGBToYUVEffect>();
             OutputChannels oc = effect.outputChannels();
             if (effect.getColorSpace() != fLastColorSpace || oc != fLastOutputChannels) {
diff --git a/src/gpu/gl/GrGLAssembleInterface.cpp b/src/gpu/gl/GrGLAssembleInterface.cpp
index efd1536..7305696 100644
--- a/src/gpu/gl/GrGLAssembleInterface.cpp
+++ b/src/gpu/gl/GrGLAssembleInterface.cpp
@@ -64,8 +64,8 @@
     const char* versionString = (const char*) GetString(GR_GL_VERSION);
     GrGLVersion glVer = GrGLGetVersionFromString(versionString);
 
-    if (glVer < GR_GL_VER(1,5) || GR_GL_INVALID_VER == glVer) {
-        // We must have array and element_array buffer objects.
+    if (glVer < GR_GL_VER(2,0) || GR_GL_INVALID_VER == glVer) {
+        // This is our minimum for non-ES GL.
         return nullptr;
     }
 
@@ -97,26 +97,18 @@
         GET_PROC_SUFFIX(BlendBarrier, NV);
     }
 
-    if (glVer >= GR_GL_VER(1,4) ||
-        extensions.has("GL_ARB_imaging")) {
-        GET_PROC(BlendColor);
-    } else if (extensions.has("GL_EXT_blend_color")) {
-        GET_PROC_SUFFIX(BlendColor, EXT);
-    }
-
-    if (glVer >= GR_GL_VER(1,4) ||
-        extensions.has("GL_ARB_imaging")) {
-        GET_PROC(BlendEquation);
-    } else if (extensions.has("GL_EXT_blend_subtract")) {
-        GET_PROC_SUFFIX(BlendEquation, EXT);
-    }
-
+    GET_PROC(BlendColor);
+    GET_PROC(BlendEquation);
     GET_PROC(BlendFunc);
     GET_PROC(BufferData);
     GET_PROC(BufferSubData);
     GET_PROC(Clear);
     GET_PROC(ClearColor);
     GET_PROC(ClearStencil);
+    if (glVer >= GR_GL_VER(4,4) || extensions.has("GL_ARB_clear_texture")) {
+        GET_PROC(ClearTexImage);
+        GET_PROC(ClearTexSubImage);
+    }
     GET_PROC(ColorMask);
     GET_PROC(CompileShader);
     GET_PROC(CompressedTexImage2D);
@@ -148,9 +140,7 @@
         GET_PROC(DrawArraysIndirect);
         GET_PROC(DrawElementsIndirect);
     }
-    if (glVer >= GR_GL_VER(2,0)) {
-        GET_PROC(DrawRangeElements);
-    }
+    GET_PROC(DrawRangeElements);
     GET_PROC(Enable);
     GET_PROC(EnableVertexAttribArray);
     GET_PROC(EndQuery);
@@ -197,6 +187,7 @@
     }
 
     GET_PROC(PixelStorei);
+    GET_PROC(PolygonMode);
     if (extensions.has("GL_EXT_raster_multisample")) {
         GET_PROC_SUFFIX(RasterSamples, EXT);
     }
@@ -405,11 +396,37 @@
         GET_PROC_SUFFIX(CopyTextureImage2D, EXT);
         GET_PROC_SUFFIX(CopyTextureSubImage1D, EXT);
         GET_PROC_SUFFIX(CopyTextureSubImage2D, EXT);
+        GET_PROC_SUFFIX(GetNamedBufferParameteriv, EXT);
+        GET_PROC_SUFFIX(GetNamedBufferPointerv, EXT);
+        GET_PROC_SUFFIX(GetNamedBufferSubData, EXT);
         GET_PROC_SUFFIX(GetTextureImage, EXT);
         GET_PROC_SUFFIX(GetTextureParameterfv, EXT);
         GET_PROC_SUFFIX(GetTextureParameteriv, EXT);
         GET_PROC_SUFFIX(GetTextureLevelParameterfv, EXT);
         GET_PROC_SUFFIX(GetTextureLevelParameteriv, EXT);
+        GET_PROC_SUFFIX(MapNamedBuffer, EXT);
+        GET_PROC_SUFFIX(NamedBufferData, EXT);
+        GET_PROC_SUFFIX(NamedBufferSubData, EXT);
+        GET_PROC_SUFFIX(ProgramUniform1f, EXT);
+        GET_PROC_SUFFIX(ProgramUniform2f, EXT);
+        GET_PROC_SUFFIX(ProgramUniform3f, EXT);
+        GET_PROC_SUFFIX(ProgramUniform4f, EXT);
+        GET_PROC_SUFFIX(ProgramUniform1i, EXT);
+        GET_PROC_SUFFIX(ProgramUniform2i, EXT);
+        GET_PROC_SUFFIX(ProgramUniform3i, EXT);
+        GET_PROC_SUFFIX(ProgramUniform4i, EXT);
+        GET_PROC_SUFFIX(ProgramUniform1fv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform2fv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform3fv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform4fv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform1iv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform2iv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform3iv, EXT);
+        GET_PROC_SUFFIX(ProgramUniform4iv, EXT);
+        GET_PROC_SUFFIX(ProgramUniformMatrix2fv, EXT);
+        GET_PROC_SUFFIX(ProgramUniformMatrix3fv, EXT);
+        GET_PROC_SUFFIX(ProgramUniformMatrix4fv, EXT);
+        GET_PROC_SUFFIX(UnmapNamedBuffer, EXT);
         if (glVer >= GR_GL_VER(1,2)) {
             GET_PROC_SUFFIX(TextureImage3D, EXT);
             GET_PROC_SUFFIX(TextureSubImage3D, EXT);
@@ -422,36 +439,6 @@
             GET_PROC_SUFFIX(CompressedTextureSubImage1D, EXT);
             GET_PROC_SUFFIX(GetCompressedTextureImage, EXT);
         }
-        if (glVer >= GR_GL_VER(1,5)) {
-            GET_PROC_SUFFIX(NamedBufferData, EXT);
-            GET_PROC_SUFFIX(NamedBufferSubData, EXT);
-            GET_PROC_SUFFIX(MapNamedBuffer, EXT);
-            GET_PROC_SUFFIX(UnmapNamedBuffer, EXT);
-            GET_PROC_SUFFIX(GetNamedBufferParameteriv, EXT);
-            GET_PROC_SUFFIX(GetNamedBufferPointerv, EXT);
-            GET_PROC_SUFFIX(GetNamedBufferSubData, EXT);
-        }
-        if (glVer >= GR_GL_VER(2,0)) {
-            GET_PROC_SUFFIX(ProgramUniform1f, EXT);
-            GET_PROC_SUFFIX(ProgramUniform2f, EXT);
-            GET_PROC_SUFFIX(ProgramUniform3f, EXT);
-            GET_PROC_SUFFIX(ProgramUniform4f, EXT);
-            GET_PROC_SUFFIX(ProgramUniform1i, EXT);
-            GET_PROC_SUFFIX(ProgramUniform2i, EXT);
-            GET_PROC_SUFFIX(ProgramUniform3i, EXT);
-            GET_PROC_SUFFIX(ProgramUniform4i, EXT);
-            GET_PROC_SUFFIX(ProgramUniform1fv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform2fv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform3fv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform4fv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform1iv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform2iv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform3iv, EXT);
-            GET_PROC_SUFFIX(ProgramUniform4iv, EXT);
-            GET_PROC_SUFFIX(ProgramUniformMatrix2fv, EXT);
-            GET_PROC_SUFFIX(ProgramUniformMatrix3fv, EXT);
-            GET_PROC_SUFFIX(ProgramUniformMatrix4fv, EXT);
-        }
         if (glVer >= GR_GL_VER(2,1)) {
             GET_PROC_SUFFIX(ProgramUniformMatrix2x3fv, EXT);
             GET_PROC_SUFFIX(ProgramUniformMatrix3x2fv, EXT);
@@ -601,6 +588,10 @@
     GET_PROC(Clear);
     GET_PROC(ClearColor);
     GET_PROC(ClearStencil);
+    if (extensions.has("GL_EXT_clear_texture")) {
+        GET_PROC_SUFFIX(ClearTexImage, EXT);
+        GET_PROC_SUFFIX(ClearTexSubImage, EXT);
+    }
     GET_PROC(ColorMask);
     GET_PROC(CompileShader);
     GET_PROC(CompressedTexImage2D);
@@ -621,6 +612,7 @@
 
     if (version >= GR_GL_VER(3,0)) {
         GET_PROC(DrawArraysInstanced);
+        GET_PROC(DrawBuffers);
         GET_PROC(DrawElementsInstanced);
     } else if (extensions.has("GL_EXT_draw_instanced")) {
         GET_PROC_SUFFIX(DrawArraysInstanced, EXT);
@@ -660,6 +652,9 @@
     GET_PROC(GetShaderiv);
     GET_PROC(GetString);
     GET_PROC(GetStringi);
+    if (version >= GR_GL_VER(3,1)) {
+        GET_PROC(GetTexLevelParameteriv);
+    }
     GET_PROC(GetUniformLocation);
     GET_PROC(IsTexture);
     GET_PROC(LineWidth);
@@ -676,6 +671,9 @@
         GET_PROC_SUFFIX(RasterSamples, EXT);
     }
 
+    if (version >= GR_GL_VER(3,0)) {
+        GET_PROC(ReadBuffer);
+    }
     GET_PROC(ReadPixels);
     GET_PROC(Scissor);
     GET_PROC(ShaderSource);
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 2ec9534..44d2529 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -11,6 +11,7 @@
 #include "GrGLRenderTarget.h"
 #include "GrGLTexture.h"
 #include "GrShaderCaps.h"
+#include "GrSurfaceProxyPriv.h"
 #include "SkTSearch.h"
 #include "SkTSort.h"
 #include "instanced/GLInstancedRendering.h"
@@ -37,7 +38,6 @@
     fDirectStateAccessSupport = false;
     fDebugSupport = false;
     fES2CompatibilitySupport = false;
-    fDrawInstancedSupport = false;
     fDrawIndirectSupport = false;
     fMultiDrawIndirectSupport = false;
     fBaseInstanceSupport = false;
@@ -52,6 +52,10 @@
     fDoManualMipmapping = false;
     fSRGBDecodeDisableSupport = false;
     fSRGBDecodeDisableAffectsMipmaps = false;
+    fClearToBoundaryValuesIsBroken = false;
+    fClearTextureSupport = false;
+    fDrawArraysBaseVertexIsBroken = false;
+    fUseDrawToClearStencilClip = false;
 
     fBlitFramebufferFlags = kNoSupport_BlitFramebufferFlag;
 
@@ -66,10 +70,6 @@
     GrGLStandard standard = ctxInfo.standard();
     GrGLVersion version = ctxInfo.version();
 
-    /**************************************************************************
-     * Caps specific to GrGLCaps
-     **************************************************************************/
-
     if (kGLES_GrGLStandard == standard) {
         GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
                           &fMaxFragmentUniformVectors);
@@ -185,6 +185,20 @@
     }
 
     if (kGL_GrGLStandard == standard) {
+        // 3.1 has draw_instanced but not instanced_arrays, for the time being we only care about
+        // instanced arrays, but we could make this more granular if we wanted
+        fInstanceAttribSupport =
+                version >= GR_GL_VER(3, 2) ||
+                (ctxInfo.hasExtension("GL_ARB_draw_instanced") &&
+                 ctxInfo.hasExtension("GL_ARB_instanced_arrays"));
+    } else {
+        fInstanceAttribSupport =
+                version >= GR_GL_VER(3, 0) ||
+                (ctxInfo.hasExtension("GL_EXT_draw_instanced") &&
+                 ctxInfo.hasExtension("GL_EXT_instanced_arrays"));
+    }
+
+    if (kGL_GrGLStandard == standard) {
         if (version >= GR_GL_VER(3, 0)) {
             fBindFragDataLocationSupport = true;
         }
@@ -249,6 +263,22 @@
     // vis-versa.
     fRGBAToBGRAReadbackConversionsAreSlow = isMESA || isMAC;
 
+    if (kGL_GrGLStandard == standard) {
+        if (version >= GR_GL_VER(4,4) || ctxInfo.hasExtension("GL_ARB_clear_texture")) {
+            // glClearTexImage seems to have a bug in NVIDIA drivers that was fixed sometime between
+            // 340.96 and 367.57.
+            if (ctxInfo.driver() != kNVIDIA_GrGLDriver ||
+                ctxInfo.driverVersion() >= GR_GL_DRIVER_VER(367, 57)) {
+                fClearTextureSupport = true;
+            }
+        }
+    } else if (ctxInfo.hasExtension("GL_EXT_clear_texture")) {
+        // Calling glClearTexImage crashes on the NexusPlayer.
+        if (kPowerVRRogue_GrGLRenderer != ctxInfo.renderer()) {
+            fClearTextureSupport = true;
+        }
+    }
+
     /**************************************************************************
     * GrShaderCaps fields
     **************************************************************************/
@@ -339,27 +369,6 @@
                                                         shaderCaps->fMaxFragmentImageStorages);
     }
 
-    /**************************************************************************
-     * GrCaps fields
-     **************************************************************************/
-
-    // We need dual source blending and the ability to disable multisample in order to support mixed
-    // samples in every corner case. We only use mixed samples if the stencil-and-cover path
-    // renderer is available and enabled; no other path renderers support this feature.
-    if (fMultisampleDisableSupport &&
-        shaderCaps->dualSourceBlendingSupport() &&
-        fShaderCaps->pathRenderingSupport() &&
-        (contextOptions.fGpuPathRenderers & GrContextOptions::GpuPathRenderers::kStencilAndCover)) {
-        fUsesMixedSamples = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") ||
-                ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples");
-        // Workaround NVIDIA bug related to glInvalidateFramebuffer and mixed samples.
-        if (fUsesMixedSamples && (kNVIDIA_GrGLDriver == ctxInfo.driver() ||
-                                  kChromium_GrGLDriver == ctxInfo.driver())) {
-            fDiscardRenderTargetSupport = false;
-            fInvalidateFBType = kNone_InvalidateFBType;
-        }
-    }
-
     // SGX and Mali GPUs that are based on a tiled-deferred architecture that have trouble with
     // frequently changing VBOs. We've measured a performance increase using non-VBO vertex
     // data for dynamic content on these GPUs. Perhaps we should read the renderer string and
@@ -372,25 +381,39 @@
         fPreferClientSideDynamicBuffers = true;
     }
 
-    // fUsesMixedSamples must be set before calling initFSAASupport.
-    this->initFSAASupport(ctxInfo, gli);
-    this->initBlendEqationSupport(ctxInfo);
-    this->initStencilFormats(ctxInfo);
-
-    if (kGL_GrGLStandard == standard) {
-        // we could also look for GL_ATI_separate_stencil extension or
-        // GL_EXT_stencil_two_side but they use different function signatures
-        // than GL2.0+ (and than each other).
-        fTwoSidedStencilSupport = (ctxInfo.version() >= GR_GL_VER(2,0));
-        // supported on GL 1.4 and higher or by extension
-        fStencilWrapOpsSupport = (ctxInfo.version() >= GR_GL_VER(1,4)) ||
-                                  ctxInfo.hasExtension("GL_EXT_stencil_wrap");
-    } else {
-        // ES 2 has two sided stencil and stencil wrap
-        fTwoSidedStencilSupport = true;
-        fStencilWrapOpsSupport = true;
+    if (!contextOptions.fAvoidStencilBuffers) {
+        // To reduce surface area, if we avoid stencil buffers, we also disable MSAA.
+        this->initFSAASupport(contextOptions, ctxInfo, gli);
+        this->initStencilSupport(ctxInfo);
     }
 
+    // Setup blit framebuffer
+    if (kGL_GrGLStandard != ctxInfo.standard()) {
+        if (ctxInfo.version() >= GR_GL_VER(3, 0)) {
+            fBlitFramebufferFlags = kNoFormatConversionForMSAASrc_BlitFramebufferFlag |
+                                    kNoMSAADst_BlitFramebufferFlag |
+                                    kRectsMustMatchForMSAASrc_BlitFramebufferFlag;
+        } else if (ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_multisample") ||
+                   ctxInfo.hasExtension("GL_ANGLE_framebuffer_blit")) {
+            // The CHROMIUM extension uses the ANGLE version of glBlitFramebuffer and includes its
+            // limitations.
+            fBlitFramebufferFlags = kNoScalingOrMirroring_BlitFramebufferFlag |
+                                    kResolveMustBeFull_BlitFrambufferFlag |
+                                    kNoMSAADst_BlitFramebufferFlag |
+                                    kNoFormatConversion_BlitFramebufferFlag |
+                                    kRectsMustMatchForMSAASrc_BlitFramebufferFlag;
+        }
+    } else {
+        if (fUsesMixedSamples ||
+            ctxInfo.version() >= GR_GL_VER(3,0) ||
+            ctxInfo.hasExtension("GL_ARB_framebuffer_object") ||
+            ctxInfo.hasExtension("GL_EXT_framebuffer_blit")) {
+            fBlitFramebufferFlags = 0;
+        }
+    }
+
+    this->initBlendEqationSupport(ctxInfo);
+
     if (kGL_GrGLStandard == standard) {
         fMapBufferFlags = kCanMap_MapFlag; // we require VBO support and the desktop VBO
                                             // extension includes glMapBuffer.
@@ -441,8 +464,6 @@
     }
 
     if (kGL_GrGLStandard == standard) {
-        SkASSERT(ctxInfo.version() >= GR_GL_VER(2,0) ||
-                 ctxInfo.hasExtension("GL_ARB_texture_non_power_of_two"));
         fNPOTTextureTileSupport = true;
         fMipMapSupport = true;
     } else {
@@ -478,21 +499,6 @@
                            kQualcomm_GrGLVendor != ctxInfo.vendor();
 #endif
 
-    // initFSAASupport() must have been called before this point
-    if (GrGLCaps::kES_IMG_MsToTexture_MSFBOType == fMSFBOType) {
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxStencilSampleCount);
-    } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) {
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES, &fMaxStencilSampleCount);
-    }
-    // We only have a use for raster multisample if there is coverage modulation from mixed samples.
-    if (fUsesMixedSamples && ctxInfo.hasExtension("GL_EXT_raster_multisample")) {
-        GR_GL_GetIntegerv(gli, GR_GL_MAX_RASTER_SAMPLES, &fMaxRasterSamples);
-        // This is to guard against platforms that may not support as many samples for
-        // glRasterSamples as they do for framebuffers.
-        fMaxStencilSampleCount = SkTMin(fMaxStencilSampleCount, fMaxRasterSamples);
-    }
-    fMaxColorSampleCount = fMaxStencilSampleCount;
-
     if (ctxInfo.hasExtension("GL_EXT_window_rectangles")) {
         GR_GL_GetIntegerv(gli, GR_GL_MAX_WINDOW_RECTANGLES, &fMaxWindowRectangles);
     }
@@ -506,6 +512,7 @@
 
     if (kAdreno4xx_GrGLRenderer == ctxInfo.renderer()) {
         fUseDrawInsteadOfPartialRenderTargetWrite = true;
+        fUseDrawToClearStencilClip = true;
     }
 
     // Texture uploads sometimes seem to be ignored to textures bound to FBOS on Tegra3.
@@ -537,20 +544,6 @@
     }
 
     if (kGL_GrGLStandard == standard) {
-        // 3.1 has draw_instanced but not instanced_arrays, for the time being we only care about
-        // instanced arrays, but we could make this more granular if we wanted
-        fDrawInstancedSupport =
-                version >= GR_GL_VER(3, 2) ||
-                (ctxInfo.hasExtension("GL_ARB_draw_instanced") &&
-                 ctxInfo.hasExtension("GL_ARB_instanced_arrays"));
-    } else {
-        fDrawInstancedSupport =
-                version >= GR_GL_VER(3, 0) ||
-                (ctxInfo.hasExtension("GL_EXT_draw_instanced") &&
-                 ctxInfo.hasExtension("GL_EXT_instanced_arrays"));
-    }
-
-    if (kGL_GrGLStandard == standard) {
         fDrawIndirectSupport = version >= GR_GL_VER(4,0) ||
                                ctxInfo.hasExtension("GL_ARB_draw_indirect");
         fBaseInstanceSupport = version >= GR_GL_VER(4,2);
@@ -592,14 +585,8 @@
         fFenceSyncSupport = true;
     }
 
-    // Safely moving textures between contexts requires fences. The Windows Intel driver has a
-    // bug with deleting and reusing texture IDs across contexts, so disallow this feature.
+    // Safely moving textures between contexts requires fences.
     fCrossContextTextureSupport = fFenceSyncSupport;
-#ifdef SK_BUILD_FOR_WIN
-    if (kIntel_GrGLVendor == ctxInfo.vendor()) {
-        fCrossContextTextureSupport = false;
-    }
-#endif
 
     // We support manual mip-map generation (via iterative downsampling draw calls). This fixes
     // bugs on some cards/drivers that produce incorrect mip-maps for sRGB textures when using
@@ -617,6 +604,16 @@
     fSRGBDecodeDisableAffectsMipmaps = fSRGBDecodeDisableSupport &&
         kChromium_GrGLDriver != ctxInfo.driver();
 
+    // See http://crbug.com/710443
+#ifdef SK_BUILD_FOR_MAC
+    if (kIntel6xxx_GrGLRenderer == ctxInfo.renderer()) {
+        fClearToBoundaryValuesIsBroken = true;
+    }
+#endif
+    if (kQualcomm_GrGLVendor == ctxInfo.vendor()) {
+        fDrawArraysBaseVertexIsBroken = true;
+    }
+
     // Requires fTextureRedSupport, fTextureSwizzleSupport, msaa support, ES compatibility have
     // already been detected.
     this->initConfigTable(contextOptions, ctxInfo, gli, shaderCaps);
@@ -854,6 +851,13 @@
         }
     }
 
+    if (kGL_GrGLStandard == standard) {
+        shaderCaps->fVertexIDSupport = true;
+    } else {
+        // Desktop GLSL 3.30 == ES GLSL 3.00.
+        shaderCaps->fVertexIDSupport = ctxInfo.glslGeneration() >= k330_GrGLSLGeneration;
+    }
+
     // The Tegra3 compiler will sometimes never return if we have min(abs(x), 1.0), so we must do
     // the abs first in a separate expression.
     if (kTegra3_GrGLRenderer == ctxInfo.renderer()) {
@@ -879,6 +883,16 @@
     // term plan for this WAR is for it to eventually be baked into SkSL.
     shaderCaps->fMustImplementGSInvocationsWithLoop = true;
 #endif
+
+    // Newer Mali GPUs do incorrect static analysis in specific situations: If there is uniform
+    // color, and that uniform contains an opaque color, and the output of the shader is only based
+    // on that uniform plus soemthing un-trackable (like a texture read), the compiler will deduce
+    // that the shader always outputs opaque values. In that case, it appears to remove the shader
+    // based blending code it normally injects, turning SrcOver into Src. To fix this, we always
+    // insert an extra bit of math on the uniform that confuses the compiler just enough...
+    if (kMaliT_GrGLRenderer == ctxInfo.renderer()) {
+        shaderCaps->fMustObfuscateUniformColor = true;
+    }
 }
 
 bool GrGLCaps::hasPathRenderingSupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
@@ -996,7 +1010,25 @@
            fConfigTable[surfaceConfig].fSecondReadPixelsFormat.fType == readType;
 }
 
-void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
+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. We only use mixed samples if the stencil-and-cover path
+    // renderer is available and enabled; no other path renderers support this feature.
+    if (fMultisampleDisableSupport &&
+        this->shaderCaps()->dualSourceBlendingSupport() &&
+        this->shaderCaps()->pathRenderingSupport() &&
+        (contextOptions.fGpuPathRenderers & GrContextOptions::GpuPathRenderers::kStencilAndCover)) {
+        fUsesMixedSamples = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") ||
+                            ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples");
+        // Workaround NVIDIA bug related to glInvalidateFramebuffer and mixed samples.
+        if (fUsesMixedSamples && (kNVIDIA_GrGLDriver == ctxInfo.driver() ||
+                                  kChromium_GrGLDriver == ctxInfo.driver())) {
+            fDiscardRenderTargetSupport = false;
+            fInvalidateFBType = kNone_InvalidateFBType;
+        }
+    }
+
     if (kGL_GrGLStandard != ctxInfo.standard()) {
         // We prefer the EXT/IMG extension over ES3 MSAA because we've observed
         // ES3 driver bugs on at least one device with a tiled GPU (N10).
@@ -1007,41 +1039,43 @@
         } else if (fUsesMixedSamples) {
             fMSFBOType = kMixedSamples_MSFBOType;
         } else if (ctxInfo.version() >= GR_GL_VER(3,0) ||
-                   ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_multisample") ||
-                   ctxInfo.hasExtension("GL_ANGLE_framebuffer_multisample")) {
+                   ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_multisample")) {
             fMSFBOType = kStandard_MSFBOType;
+        } else if (ctxInfo.hasExtension("GL_ANGLE_framebuffer_multisample")) {
+            fMSFBOType = kEXT_MSFBOType;
         } else if (ctxInfo.hasExtension("GL_APPLE_framebuffer_multisample")) {
             fMSFBOType = kES_Apple_MSFBOType;
         }
-
-        // Above determined the preferred MSAA approach, now decide whether glBlitFramebuffer
-        // is available.
-        if (ctxInfo.version() >= GR_GL_VER(3, 0)) {
-            fBlitFramebufferFlags = kNoFormatConversionForMSAASrc_BlitFramebufferFlag |
-                                    kRectsMustMatchForMSAASrc_BlitFramebufferFlag;
-        } else if (ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_multisample") ||
-                   ctxInfo.hasExtension("GL_ANGLE_framebuffer_blit")) {
-            // The CHROMIUM extension uses the ANGLE version of glBlitFramebuffer and includes its
-            // limitations.
-            fBlitFramebufferFlags = kNoScalingOrMirroring_BlitFramebufferFlag |
-                                    kResolveMustBeFull_BlitFrambufferFlag |
-                                    kNoMSAADst_BlitFramebufferFlag |
-                                    kNoFormatConversion_BlitFramebufferFlag;
-        }
     } else {
         if (fUsesMixedSamples) {
             fMSFBOType = kMixedSamples_MSFBOType;
-            fBlitFramebufferFlags = 0;
         } else if (ctxInfo.version() >= GR_GL_VER(3,0) ||
                    ctxInfo.hasExtension("GL_ARB_framebuffer_object")) {
             fMSFBOType = kStandard_MSFBOType;
-            fBlitFramebufferFlags = 0;
         } else if (ctxInfo.hasExtension("GL_EXT_framebuffer_multisample") &&
                    ctxInfo.hasExtension("GL_EXT_framebuffer_blit")) {
             fMSFBOType = kEXT_MSFBOType;
-            fBlitFramebufferFlags = 0;
         }
     }
+
+    // We disable MSAA across the board for Intel GPUs
+    if (kIntel_GrGLVendor == ctxInfo.vendor()) {
+        fMSFBOType = kNone_MSFBOType;
+    }
+
+    if (GrGLCaps::kES_IMG_MsToTexture_MSFBOType == fMSFBOType) {
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxStencilSampleCount);
+    } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) {
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES, &fMaxStencilSampleCount);
+    }
+    // We only have a use for raster multisample if there is coverage modulation from mixed samples.
+    if (fUsesMixedSamples && ctxInfo.hasExtension("GL_EXT_raster_multisample")) {
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_RASTER_SAMPLES, &fMaxRasterSamples);
+        // This is to guard against platforms that may not support as many samples for
+        // glRasterSamples as they do for framebuffers.
+        fMaxStencilSampleCount = SkTMin(fMaxStencilSampleCount, fMaxRasterSamples);
+    }
+    fMaxColorSampleCount = fMaxStencilSampleCount;
 }
 
 void GrGLCaps::initBlendEqationSupport(const GrGLContextInfo& ctxInfo) {
@@ -1096,7 +1130,7 @@
 const GrGLuint kUnknownBitCount = GrGLStencilAttachment::kUnknownBitCount;
 }
 
-void GrGLCaps::initStencilFormats(const GrGLContextInfo& ctxInfo) {
+void GrGLCaps::initStencilSupport(const GrGLContextInfo& ctxInfo) {
 
     // Build up list of legal stencil formats (though perhaps not supported on
     // the particular gpu/driver) from most preferred to least.
@@ -1216,7 +1250,6 @@
     r.appendf("Vertex array object support: %s\n", (fVertexArrayObjectSupport ? "YES": "NO"));
     r.appendf("Direct state access support: %s\n", (fDirectStateAccessSupport ? "YES": "NO"));
     r.appendf("Debug support: %s\n", (fDebugSupport ? "YES": "NO"));
-    r.appendf("Draw instanced support: %s\n", (fDrawInstancedSupport ? "YES" : "NO"));
     r.appendf("Draw indirect support: %s\n", (fDrawIndirectSupport ? "YES" : "NO"));
     r.appendf("Multi draw indirect support: %s\n", (fMultiDrawIndirectSupport ? "YES" : "NO"));
     r.appendf("Base instance support: %s\n", (fBaseInstanceSupport ? "YES" : "NO"));
@@ -1255,9 +1288,10 @@
         return GR_GL_MEDIUM_FLOAT;
     case kHigh_GrSLPrecision:
         return GR_GL_HIGH_FLOAT;
+    default:
+        SkFAIL("Unexpected precision type.");
+        return -1;
     }
-    SkFAIL("Unknown precision.");
-    return -1;
 }
 
 static GrGLenum shader_type_to_gl_shader(GrShaderType type) {
@@ -1347,15 +1381,6 @@
     return true;
 }
 
-bool GrGLCaps::getCompressedTexImageFormats(GrPixelConfig surfaceConfig,
-                                            GrGLenum* internalFormat) const {
-    if (!GrPixelConfigIsCompressed(surfaceConfig)) {
-        return false;
-    }
-    *internalFormat = fConfigTable[surfaceConfig].fFormats.fInternalFormatTexImage;
-    return true;
-}
-
 bool GrGLCaps::getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig,
                                    GrGLenum* externalFormat, GrGLenum* externalType) const {
     if (!this->getExternalFormat(surfaceConfig, externalConfig, kOther_ExternalFormatUsage,
@@ -1366,9 +1391,6 @@
 }
 
 bool GrGLCaps::getRenderbufferFormat(GrPixelConfig config, GrGLenum* internalFormat) const {
-    if (GrPixelConfigIsCompressed(config)) {
-        return false;
-    }
     *internalFormat = fConfigTable[config].fFormats.fInternalFormatRenderbuffer;
     return true;
 }
@@ -1377,9 +1399,6 @@
                                  ExternalFormatUsage usage, GrGLenum* externalFormat,
                                  GrGLenum* externalType) const {
     SkASSERT(externalFormat && externalType);
-    if (GrPixelConfigIsCompressed(memoryConfig)) {
-        return false;
-    }
 
     bool surfaceIsAlphaOnly = GrPixelConfigIsAlphaOnly(surfaceConfig);
     bool memoryIsAlphaOnly = GrPixelConfigIsAlphaOnly(memoryConfig);
@@ -1554,14 +1573,16 @@
         fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_BGRA;
         fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_BGRA8;
         if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) {
-            // The APPLE extension doesn't make this renderable.
-            fConfigTable[kBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
-            if (version < GR_GL_VER(3,0) && !ctxInfo.hasExtension("GL_EXT_texture_storage")) {
-                // On ES2 the internal format of a BGRA texture is RGBA with the APPLE extension.
-                // Though, that seems to not be the case if the texture storage extension is
-                // present. The specs don't exactly make that clear.
-                fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_RGBA;
-                fConfigTable[kBGRA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGBA8;
+            // This APPLE extension introduces complexity on ES2. It leaves the internal format
+            // as RGBA, but allows BGRA as the external format. From testing, it appears that the
+            // driver remembers the external format when the texture is created (with TexImage).
+            // If you then try to upload data in the other swizzle (with TexSubImage), it fails.
+            // We could work around this, but it adds even more state tracking to code that is
+            // already too tricky. Instead, we opt not to support BGRA on ES2 with this extension.
+            // This also side-steps some ambiguous interactions with the texture storage extension.
+            if (version >= GR_GL_VER(3,0)) {
+                // The APPLE extension doesn't make this renderable.
+                fConfigTable[kBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
             }
         } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) {
             fConfigTable[kBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag |
@@ -1573,8 +1594,21 @@
             }
         }
     }
-    if (texStorageSupported) {
-        fConfigTable[kBGRA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
+
+    bool isX86PowerVR = false;
+#if defined(SK_CPU_X86)
+    if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) {
+        isX86PowerVR = true;
+    }
+#endif
+
+    // Adreno 3xx, 4xx, 5xx, and NexusPlayer all fail if we try to use TexStorage with BGRA
+    if (texStorageSupported &&
+        kAdreno3xx_GrGLRenderer != ctxInfo.renderer() &&
+        kAdreno4xx_GrGLRenderer != ctxInfo.renderer() &&
+        kAdreno5xx_GrGLRenderer != ctxInfo.renderer() &&
+        !isX86PowerVR) {
+            fConfigTable[kBGRA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
     fConfigTable[kBGRA_8888_GrPixelConfig].fSwizzle = GrSwizzle::RGBA();
 
@@ -1595,13 +1629,11 @@
         }
     } else {
         fSRGBSupport = ctxInfo.version() >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_EXT_sRGB");
-#if defined(SK_CPU_X86)
-        if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) {
-            // NexusPlayer has strange bugs with sRGB (skbug.com/4148). This is a targeted fix to
-            // blacklist that device (and any others that might be sharing the same driver).
+        // NexusPlayer has strange bugs with sRGB (skbug.com/4148). This is a targeted fix to
+        // blacklist that device (and any others that might be sharing the same driver).
+        if (isX86PowerVR) {
             fSRGBSupport = false;
         }
-#endif
         // ES through 3.1 requires EXT_srgb_write_control to support toggling
         // sRGB writing for destinations.
         // See https://bug.skia.org/5329 for Adreno4xx issue.
@@ -1625,6 +1657,18 @@
         fSRGBSupport = false;
     }
 
+    // ES2 Command Buffer has several TexStorage restrictions. It appears to fail for any format
+    // not explicitly allowed by GL_EXT_texture_storage, particularly those from other extensions.
+    bool isCommandBufferES2 = kChromium_GrGLDriver == ctxInfo.driver() && version < GR_GL_VER(3, 0);
+
+    uint32_t srgbRenderFlags = allRenderFlags;
+    // MacPro devices with AMD cards fail to create MSAA sRGB render buffers
+#if defined(SK_BUILD_FOR_MAC)
+    if (kATI_GrGLVendor == ctxInfo.vendor()) {
+        srgbRenderFlags &= ~ConfigInfo::kRenderableWithMSAA_Flag;
+    }
+#endif
+
     fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_SRGB_ALPHA;
     fConfigTable[kSRGBA_8888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_SRGB8_ALPHA8;
     // GL does not do srgb<->rgb conversions when transferring between cpu and gpu. Thus, the
@@ -1635,13 +1679,13 @@
     fConfigTable[kSRGBA_8888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
     if (fSRGBSupport) {
         fConfigTable[kSRGBA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag |
-                                                         allRenderFlags;
+                                                         srgbRenderFlags;
     }
-    if (texStorageSupported) {
+    // ES2 Command Buffer does not allow TexStorage with SRGB8_ALPHA8_EXT
+    if (texStorageSupported && !isCommandBufferES2) {
         fConfigTable[kSRGBA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
     fConfigTable[kSRGBA_8888_GrPixelConfig].fSwizzle = GrSwizzle::RGBA();
-
     // sBGRA is not a "real" thing in OpenGL, but GPUs support it, and on platforms where
     // kN32 == BGRA, we need some way to work with it. (The default framebuffer on Windows
     // is in this format, for example).
@@ -1653,10 +1697,11 @@
         GR_GL_BGRA;
     fConfigTable[kSBGRA_8888_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
     fConfigTable[kSBGRA_8888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
-    if (fSRGBSupport) {
+    if (fSRGBSupport && kGL_GrGLStandard == standard) {
         fConfigTable[kSBGRA_8888_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag |
-            allRenderFlags;
+                                                         srgbRenderFlags;
     }
+
     if (texStorageSupported) {
         fConfigTable[kSBGRA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
@@ -1760,11 +1805,12 @@
     if (this->textureRedSupport() ||
         (kStandard_MSFBOType == this->msFBOType() && ctxInfo.renderer() != kOSMesa_GrGLRenderer)) {
         // OpenGL 3.0+ (and GL_ARB_framebuffer_object) supports ALPHA8 as renderable.
-        // However, osmesa fails if it used even when GL_ARB_framebuffer_object is present.
+        // However, osmesa fails if it is used even when GL_ARB_framebuffer_object is present.
         // Core profile removes ALPHA8 support, but we should have chosen R8 in that case.
         fConfigTable[kAlpha_8_GrPixelConfig].fFlags |= allRenderFlags;
     }
-    if (texStorageSupported) {
+    // ES2 Command Buffer does not allow TexStorage with R8_EXT (so Alpha_8 and Gray_8)
+    if (texStorageSupported && !isCommandBufferES2) {
         fConfigTable[kAlpha_8_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
 
@@ -1797,7 +1843,7 @@
         fConfigTable[kGray_8_GrPixelConfig].fFlags |= allRenderFlags;
     }
 #endif
-    if (texStorageSupported) {
+    if (texStorageSupported && !isCommandBufferES2) {
         fConfigTable[kGray_8_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
     }
 
@@ -1807,6 +1853,7 @@
     // [half] floating point textures became part of the standard in ES3.1 / OGL 3.0.
     bool hasFPTextures = false;
     bool hasHalfFPTextures = false;
+    bool rgIsTexturable = false;
     // for now we don't support floating point MSAA on ES
     uint32_t fpRenderFlags = (kGL_GrGLStandard == standard) ? allRenderFlags : nonMSAARenderFlags;
 
@@ -1814,11 +1861,13 @@
         if (version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_ARB_texture_float")) {
             hasFPTextures = true;
             hasHalfFPTextures = true;
+            rgIsTexturable = true;
         }
     } else {
         if (version >= GR_GL_VER(3, 1)) {
             hasFPTextures = true;
             hasHalfFPTextures = true;
+            rgIsTexturable = true;
         } else {
             if (ctxInfo.hasExtension("GL_OES_texture_float_linear") &&
                 ctxInfo.hasExtension("GL_OES_texture_float")) {
@@ -1840,7 +1889,7 @@
         fConfigTable[fpconfig].fFormats.fExternalType = GR_GL_FLOAT;
         fConfigTable[fpconfig].fFormatType = kFloat_FormatType;
         if (hasFPTextures) {
-            fConfigTable[fpconfig].fFlags = ConfigInfo::kTextureable_Flag;
+            fConfigTable[fpconfig].fFlags = rgIsTexturable ? ConfigInfo::kTextureable_Flag : 0;
             // For now we only enable rendering to float on desktop, because on ES we'd have to
             // solve many precision issues and no clients actually want this yet.
             if (kGL_GrGLStandard == standard /* || version >= GR_GL_VER(3,2) ||
@@ -1926,39 +1975,6 @@
     }
     fConfigTable[kRGBA_half_GrPixelConfig].fSwizzle = GrSwizzle::RGBA();
 
-    // Compressed texture support
-
-    // glCompressedTexImage2D is available on all OpenGL ES devices. It is available on standard
-    // OpenGL after version 1.3. We'll assume at least that level of OpenGL support.
-
-    // TODO: Fix command buffer bindings and remove this.
-    fCompressedTexSubImageSupport = SkToBool(gli->fFunctions.fCompressedTexSubImage2D);
-
-    // No sized/unsized internal format distinction for compressed formats, no external format.
-    // Below we set the external formats and types to 0.
-    {
-        fConfigTable[kETC1_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_COMPRESSED_ETC1_RGB8;
-        fConfigTable[kETC1_GrPixelConfig].fFormats.fSizedInternalFormat =
-                                                                         GR_GL_COMPRESSED_ETC1_RGB8;
-        fConfigTable[kETC1_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0;
-        fConfigTable[kETC1_GrPixelConfig].fFormats.fExternalType = 0;
-        fConfigTable[kETC1_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
-        if (kGL_GrGLStandard == standard) {
-            if (version >= GR_GL_VER(4, 3) || ctxInfo.hasExtension("GL_ARB_ES3_compatibility")) {
-                fConfigTable[kETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
-            }
-        } else {
-            if (version >= GR_GL_VER(3, 0) ||
-                ctxInfo.hasExtension("GL_OES_compressed_ETC1_RGB8_texture") ||
-                // ETC2 is a superset of ETC1, so we can just check for that, too.
-                (ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGB8_texture") &&
-                 ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGBA8_texture"))) {
-                fConfigTable[kETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
-            }
-        }
-        fConfigTable[kETC1_GrPixelConfig].fSwizzle = GrSwizzle::RGBA();
-    }
-
     // Bulk populate the texture internal/external formats here and then deal with exceptions below.
 
     // ES 2.0 requires that the internal/external formats match.
@@ -2068,28 +2084,53 @@
 #endif
 }
 
-bool GrGLCaps::initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const {
+bool GrGLCaps::initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                                  bool* rectsMustMatch, bool* disallowSubrect) const {
+    // By default, we don't require rects to match.
+    *rectsMustMatch = false;
+
+    // By default, we allow subrects.
+    *disallowSubrect = false;
+
     // If the src is a texture, we can implement the blit as a draw assuming the config is
     // renderable.
-    if (src->asTexture() && this->isConfigRenderable(src->config(), false)) {
-        desc->fOrigin = kDefault_GrSurfaceOrigin;
+    if (src->asTextureProxy() && this->isConfigRenderable(src->config(), false)) {
+        desc->fOrigin = kBottomLeft_GrSurfaceOrigin;
         desc->fFlags = kRenderTarget_GrSurfaceFlag;
         desc->fConfig = src->config();
         return true;
     }
 
-    const GrGLTexture* srcTexture = static_cast<const GrGLTexture*>(src->asTexture());
-    if (srcTexture && srcTexture->target() != GR_GL_TEXTURE_2D) {
-        // Not supported for FBO blit or CopyTexSubImage
-        return false;
+    {
+        // The only way we could see a non-GR_GL_TEXTURE_2D texture would be if it were
+        // wrapped. In that case the proxy would already be instantiated.
+        const GrTexture* srcTexture = src->priv().peekTexture();
+        const GrGLTexture* glSrcTexture = static_cast<const GrGLTexture*>(srcTexture);
+        if (glSrcTexture && glSrcTexture->target() != GR_GL_TEXTURE_2D) {
+            // Not supported for FBO blit or CopyTexSubImage
+            return false;
+        }
     }
 
     // We look for opportunities to use CopyTexSubImage, or fbo blit. If neither are
     // possible and we return false to fallback to creating a render target dst for render-to-
     // texture. This code prefers CopyTexSubImage to fbo blit and avoids triggering temporary fbo
     // creation. It isn't clear that avoiding temporary fbo creation is actually optimal.
-    GrSurfaceOrigin originForBlitFramebuffer = kDefault_GrSurfaceOrigin;
-    if (this->blitFramebufferSupportFlags() & kNoScalingOrMirroring_BlitFramebufferFlag) {
+    GrSurfaceOrigin originForBlitFramebuffer = kTopLeft_GrSurfaceOrigin;
+    bool rectsMustMatchForBlitFramebuffer = false;
+    bool disallowSubrectForBlitFramebuffer = false;
+    if (src->numColorSamples() &&
+        (this->blitFramebufferSupportFlags() & kResolveMustBeFull_BlitFrambufferFlag)) {
+        rectsMustMatchForBlitFramebuffer = true;
+        disallowSubrectForBlitFramebuffer = true;
+        // Mirroring causes rects to mismatch later, don't allow it.
+        originForBlitFramebuffer = src->origin();
+    } else if (src->numColorSamples() && (this->blitFramebufferSupportFlags() &
+                                          kRectsMustMatchForMSAASrc_BlitFramebufferFlag)) {
+        rectsMustMatchForBlitFramebuffer = true;
+        // Mirroring causes rects to mismatch later, don't allow it.
+        originForBlitFramebuffer = src->origin();
+    } else if (this->blitFramebufferSupportFlags() & kNoScalingOrMirroring_BlitFramebufferFlag) {
         originForBlitFramebuffer = src->origin();
     }
 
@@ -2100,21 +2141,28 @@
         if (this->canConfigBeFBOColorAttachment(kBGRA_8888_GrPixelConfig)) {
             desc->fOrigin = originForBlitFramebuffer;
             desc->fConfig = kBGRA_8888_GrPixelConfig;
+            *rectsMustMatch = rectsMustMatchForBlitFramebuffer;
+            *disallowSubrect = disallowSubrectForBlitFramebuffer;
             return true;
         }
         return false;
     }
 
-    const GrGLRenderTarget* srcRT = static_cast<const GrGLRenderTarget*>(src);
-    if (srcRT->renderFBOID() != srcRT->textureFBOID()) {
-        // It's illegal to call CopyTexSubImage2D on a MSAA renderbuffer. Set up for FBO blit or
-        // fail.
-        if (this->canConfigBeFBOColorAttachment(src->config())) {
-            desc->fOrigin = originForBlitFramebuffer;
-            desc->fConfig = src->config();
-            return true;
+    {
+        bool srcIsMSAARenderbuffer = GrFSAAType::kUnifiedMSAA == src->fsaaType() &&
+                                     this->usesMSAARenderBuffers();
+        if (srcIsMSAARenderbuffer) {
+            // It's illegal to call CopyTexSubImage2D on a MSAA renderbuffer. Set up for FBO
+            // blit or fail.
+            if (this->canConfigBeFBOColorAttachment(src->config())) {
+                desc->fOrigin = originForBlitFramebuffer;
+                desc->fConfig = src->config();
+                *rectsMustMatch = rectsMustMatchForBlitFramebuffer;
+                *disallowSubrect = disallowSubrectForBlitFramebuffer;
+                return true;
+            }
+            return false;
         }
-        return false;
     }
 
     // We'll do a CopyTexSubImage. Make the dst a plain old texture.
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index a659435..efb324b 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -153,8 +153,6 @@
                             GrGLenum* internalFormat, GrGLenum* externalFormat,
                             GrGLenum* externalType) const;
 
-    bool getCompressedTexImageFormats(GrPixelConfig surfaceConfig, GrGLenum* internalFormat) const;
-
     bool getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig,
                              GrGLenum* externalFormat, GrGLenum* externalType) const;
 
@@ -359,7 +357,24 @@
         return fRGBAToBGRAReadbackConversionsAreSlow;
     }
 
-    bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const override;
+    // Certain Intel GPUs on Mac fail to clear if the glClearColor is made up of only 1s and 0s.
+    bool clearToBoundaryValuesIsBroken() const { return fClearToBoundaryValuesIsBroken; }
+
+    /// glClearTex(Sub)Image support
+    bool clearTextureSupport() const { return fClearTextureSupport; }
+
+    // Adreno/MSAA drops a draw on the imagefiltersbase GM if the base vertex param to
+    // glDrawArrays is nonzero.
+    // https://bugs.chromium.org/p/skia/issues/detail?id=6650
+    bool drawArraysBaseVertexIsBroken() const { return fDrawArraysBaseVertexIsBroken; }
+
+    /// 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.
+    bool useDrawToClearStencilClip() const { return fUseDrawToClearStencilClip; }
+
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                            bool* rectsMustMatch, bool* disallowSubrect) const override;
 
 private:
     enum ExternalFormatUsage {
@@ -379,9 +394,10 @@
 
     void onApplyOptionsOverrides(const GrContextOptions& options) override;
 
-    void initFSAASupport(const GrGLContextInfo&, const GrGLInterface*);
+    void initFSAASupport(const GrContextOptions& contextOptions, const GrGLContextInfo&,
+                         const GrGLInterface*);
     void initBlendEqationSupport(const GrGLContextInfo&);
-    void initStencilFormats(const GrGLContextInfo&);
+    void initStencilSupport(const GrGLContextInfo&);
     // This must be called after initFSAASupport().
     void initConfigTable(const GrContextOptions&, const GrGLContextInfo&, const GrGLInterface*,
                          GrShaderCaps*);
@@ -428,6 +444,10 @@
     bool fDoManualMipmapping : 1;
     bool fSRGBDecodeDisableSupport : 1;
     bool fSRGBDecodeDisableAffectsMipmaps : 1;
+    bool fClearToBoundaryValuesIsBroken : 1;
+    bool fClearTextureSupport : 1;
+    bool fDrawArraysBaseVertexIsBroken : 1;
+    bool fUseDrawToClearStencilClip : 1;
 
     uint32_t fBlitFramebufferFlags;
 
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index 522a036..f3c5f5c 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -416,6 +416,11 @@
 #define GR_GL_LINE_WIDTH_GRANULARITY         0x0B23
 #define GR_GL_LINE_WIDTH_RANGE               0x0B22
 
+/* PolygonMode */
+#define GR_GL_POINT                          0x1B00
+#define GR_GL_LINE                           0x1B01
+#define GR_GL_FILL                           0x1B02
+
 /* Unsized formats */
 #define GR_GL_STENCIL_INDEX                  0x1901
 #define GR_GL_DEPTH_COMPONENT                0x1902
diff --git a/src/gpu/gl/GrGLExternalTextureData.cpp b/src/gpu/gl/GrGLExternalTextureData.cpp
deleted file mode 100644
index 1087830..0000000
--- a/src/gpu/gl/GrGLExternalTextureData.cpp
+++ /dev/null
@@ -1,30 +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 "GrContext.h"
-#include "GrContextPriv.h"
-#include "GrGpu.h"
-#include "GrResourceProvider.h"
-#include "GrSemaphore.h"
-#include "gl/GrGLTypes.h"
-
-GrGLExternalTextureData::GrGLExternalTextureData(const GrGLTextureInfo& info,
-                                                 sk_sp<GrSemaphore> semaphore,
-                                                 GrContext* context)
-        : fInfo(info)
-        , fSemaphore(std::move(semaphore)) {
-    SkASSERT(fSemaphore->unique());
-    context->resourceProvider()->releaseOwnershipOfSemaphore(fSemaphore);
-}
-
-void GrGLExternalTextureData::attachToContext(GrContext* context) {
-    context->resourceProvider()->takeOwnershipOfSemaphore(fSemaphore);
-    // Ideally we don't want to get the Gpu off of the context here. However, eventually this
-    // semaphore will live on a GrTexture object and the wait will be called from there. At that
-    // point we can use the GrGpu already stored directly on the GrTexture.
-    context->getGpu()->waitSemaphore(fSemaphore);
-}
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 19d3197..3160eca 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -7,7 +7,9 @@
 
 #include "GrGLGpu.h"
 
+#include <cmath>
 #include "../private/GrGLSL.h"
+#include "GrBackendSurface.h"
 #include "GrFixedClip.h"
 #include "GrGLBuffer.h"
 #include "GrGLGpuCommandBuffer.h"
@@ -56,6 +58,9 @@
 using gr_instanced::InstancedRendering;
 using gr_instanced::GLInstancedRendering;
 
+using gr_instanced::OpAllocator;
+using gr_instanced::GLOpAllocator;
+
 static const GrGLenum gXfermodeEquation2Blend[] = {
     // Basic OpenGL blend equations.
     GR_GL_FUNC_ADD,
@@ -187,6 +192,9 @@
     if (!glInterface) {
         return nullptr;
     }
+#ifdef USE_NSIGHT
+    const_cast<GrContextOptions&>(options).fSuppressPathRendering = true;
+#endif
     GrGLContext* glContext = GrGLContext::Create(glInterface.get(), options);
     if (glContext) {
         return new GrGLGpu(glContext, context);
@@ -212,7 +220,7 @@
     for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) {
         fMipmapPrograms[i].fProgram = 0;
     }
-    fWireRectProgram.fProgram = 0;
+    fStencilClipClearProgram = 0;
 
     SkASSERT(ctx);
     fCaps.reset(SkRef(ctx->caps()));
@@ -269,7 +277,7 @@
     fPathRendering.reset();
     fCopyProgramArrayBuffer.reset();
     fMipmapProgramArrayBuffer.reset();
-    fWireRectArrayBuffer.reset();
+    fStencilClipClearArrayBuffer.reset();
 
     if (0 != fHWProgramID) {
         // detach the current program so there is no confusion on OpenGL's part
@@ -299,8 +307,8 @@
         }
     }
 
-    if (0 != fWireRectProgram.fProgram) {
-        GL_CALL(DeleteProgram(fWireRectProgram.fProgram));
+    if (0 != fStencilClipClearProgram) {
+        GL_CALL(DeleteProgram(fStencilClipClearProgram));
     }
 
     delete fProgramCache;
@@ -331,8 +339,8 @@
                 GL_CALL(DeleteProgram(fMipmapPrograms[i].fProgram));
             }
         }
-        if (fWireRectProgram.fProgram) {
-            GL_CALL(DeleteProgram(fWireRectProgram.fProgram));
+        if (fStencilClipClearProgram) {
+            GL_CALL(DeleteProgram(fStencilClipClearProgram));
         }
     } else {
         if (fProgramCache) {
@@ -355,8 +363,8 @@
     for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) {
         fMipmapPrograms[i].fProgram = 0;
     }
-    fWireRectProgram.fProgram = 0;
-    fWireRectArrayBuffer.reset();
+    fStencilClipClearProgram = 0;
+    fStencilClipClearArrayBuffer.reset();
     if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
         this->glPathRendering()->disconnect(type);
     }
@@ -365,17 +373,22 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrGLGpu::onResetContext(uint32_t resetBits) {
-    // we don't use the zb at all
     if (resetBits & kMisc_GrGLBackendState) {
+        // we don't use the zb at all
         GL_CALL(Disable(GR_GL_DEPTH_TEST));
         GL_CALL(DepthMask(GR_GL_FALSE));
 
+        // We don't use face culling.
+        GL_CALL(Disable(GR_GL_CULL_FACE));
+        // We do use separate stencil. Our algorithms don't care which face is front vs. back so
+        // just set this to the default for self-consistency.
+        GL_CALL(FrontFace(GR_GL_CCW));
+
         fHWBufferState[kTexel_GrBufferType].invalidate();
         fHWBufferState[kDrawIndirect_GrBufferType].invalidate();
         fHWBufferState[kXferCpuToGpu_GrBufferType].invalidate();
         fHWBufferState[kXferGpuToCpu_GrBufferType].invalidate();
 
-        fHWDrawFace = GrDrawFace::kInvalid;
         if (kGL_GrGLStandard == this->glStandard()) {
 #ifndef USE_NSIGHT
             // Desktop-only state that we never change
@@ -394,14 +407,17 @@
                 GL_CALL(Disable(GR_GL_COLOR_TABLE));
             }
             GL_CALL(Disable(GR_GL_POLYGON_OFFSET_FILL));
+
+            if (this->caps()->wireframeMode()) {
+                GL_CALL(PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
+            } else {
+                GL_CALL(PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_FILL));
+            }
 #endif
             // Since ES doesn't support glPointSize at all we always use the VS to
             // set the point size
             GL_CALL(Enable(GR_GL_VERTEX_PROGRAM_POINT_SIZE));
 
-            // We should set glPolygonMode(FRONT_AND_BACK,FILL) here, too. It isn't
-            // currently part of our gl interface. There are probably others as
-            // well.
         }
 
         if (kGLES_GrGLStandard == this->glStandard() &&
@@ -501,27 +517,18 @@
     }
 }
 
-static GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) {
-    // By default, GrRenderTargets are GL's normal orientation so that they
-    // can be drawn to by the outside world without the client having
-    // to render upside down.
-    if (kDefault_GrSurfaceOrigin == origin) {
-        return renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
-    } else {
-        return origin;
-    }
-}
-
-sk_sp<GrTexture> GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc,
+sk_sp<GrTexture> GrGLGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
+                                               GrSurfaceOrigin origin,
+                                               GrBackendTextureFlags flags,
+                                               int sampleCnt,
                                                GrWrapOwnership ownership) {
-    const GrGLTextureInfo* info = reinterpret_cast<const GrGLTextureInfo*>(desc.fTextureHandle);
+    const GrGLTextureInfo* info = backendTex.getGLTextureInfo();
     if (!info || !info->fID) {
         return nullptr;
     }
 
-    // next line relies on GrBackendTextureDesc's flags matching GrTexture's
-    bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
-    SkASSERT(!renderTarget || kAdoptAndCache_GrWrapOwnership != ownership);  // Not supported
+    // next line relies on GrBackendTextureFlags matching GrTexture's
+    bool renderTarget = SkToBool(flags & kRenderTarget_GrBackendTextureFlag);
 
     GrGLTexture::IDDesc idDesc;
     idDesc.fInfo = *info;
@@ -544,7 +551,7 @@
 
     // Sample count is interpreted to mean the number of samples that Gr code should allocate
     // for a render buffer that resolves to the texture. We don't support MSAA textures.
-    if (desc.fSampleCnt && !renderTarget) {
+    if (sampleCnt && !renderTarget) {
         return nullptr;
     }
 
@@ -555,19 +562,19 @@
     }
 
     GrSurfaceDesc surfDesc;
-    surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags;
-    surfDesc.fWidth = desc.fWidth;
-    surfDesc.fHeight = desc.fHeight;
-    surfDesc.fConfig = desc.fConfig;
-    surfDesc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
+    surfDesc.fFlags = (GrSurfaceFlags) flags;
+    surfDesc.fWidth = backendTex.width();
+    surfDesc.fHeight = backendTex.height();
+    surfDesc.fConfig = backendTex.config();
+    surfDesc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
     // FIXME:  this should be calling resolve_origin(), but Chrome code is currently
     // assuming the old behaviour, which is that backend textures are always
     // BottomLeft, even for non-RT's.  Once Chrome is fixed, change this to:
     // glTexDesc.fOrigin = resolve_origin(desc.fOrigin, renderTarget);
-    if (kDefault_GrSurfaceOrigin == desc.fOrigin) {
+    if (kDefault_GrSurfaceOrigin == origin) {
         surfDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
     } else {
-        surfDesc.fOrigin = desc.fOrigin;
+        surfDesc.fOrigin = origin;
     }
 
     if (renderTarget) {
@@ -578,34 +585,39 @@
         return GrGLTextureRenderTarget::MakeWrapped(this, surfDesc, idDesc, rtIDDesc);
     }
 
-    if (kAdoptAndCache_GrWrapOwnership == ownership) {
-        return sk_sp<GrTexture>(new GrGLTexture(this, SkBudgeted::kYes, surfDesc, idDesc));
-    } else {
-        return GrGLTexture::MakeWrapped(this, surfDesc, idDesc);
-    }
+    return GrGLTexture::MakeWrapped(this, surfDesc, idDesc);
 }
 
-sk_sp<GrRenderTarget> GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDesc& wrapDesc){
+sk_sp<GrRenderTarget> GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT,
+                                                         GrSurfaceOrigin origin) {
+    const GrGLFramebufferInfo* info = backendRT.getGLFramebufferInfo();
+    if (!info) {
+        return nullptr;
+    }
+
     GrGLRenderTarget::IDDesc idDesc;
-    idDesc.fRTFBOID = static_cast<GrGLuint>(wrapDesc.fRenderTargetHandle);
+    idDesc.fRTFBOID = info->fFBOID;
     idDesc.fMSColorRenderbufferID = 0;
     idDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID;
     idDesc.fRTFBOOwnership = GrBackendObjectOwnership::kBorrowed;
     idDesc.fIsMixedSampled = false;
 
     GrSurfaceDesc desc;
-    desc.fConfig = wrapDesc.fConfig;
-    desc.fFlags = kCheckAllocation_GrSurfaceFlag | kRenderTarget_GrSurfaceFlag;
-    desc.fWidth = wrapDesc.fWidth;
-    desc.fHeight = wrapDesc.fHeight;
-    desc.fSampleCnt = SkTMin(wrapDesc.fSampleCnt, this->caps()->maxSampleCount());
-    desc.fOrigin = resolve_origin(wrapDesc.fOrigin, true);
+    desc.fConfig = backendRT.config();
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = backendRT.width();
+    desc.fHeight = backendRT.height();
+    desc.fSampleCnt = SkTMin(backendRT.sampleCnt(), this->caps()->maxSampleCount());
+    SkASSERT(kDefault_GrSurfaceOrigin != origin);
+    desc.fOrigin = origin;
 
-    return GrGLRenderTarget::MakeWrapped(this, desc, idDesc, wrapDesc.fStencilBits);
+    return GrGLRenderTarget::MakeWrapped(this, desc, idDesc, backendRT.stencilBits());
 }
 
-sk_sp<GrRenderTarget> GrGLGpu::onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc& desc){
-    const GrGLTextureInfo* info = reinterpret_cast<const GrGLTextureInfo*>(desc.fTextureHandle);
+sk_sp<GrRenderTarget> GrGLGpu::onWrapBackendTextureAsRenderTarget(const GrBackendTexture& tex,
+                                                                  GrSurfaceOrigin origin,
+                                                                  int sampleCnt) {
+    const GrGLTextureInfo* info = tex.getGLTextureInfo();
     if (!info || !info->fID) {
         return nullptr;
     }
@@ -622,19 +634,19 @@
     }
 
     GrSurfaceDesc surfDesc;
-    surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags;
-    surfDesc.fWidth = desc.fWidth;
-    surfDesc.fHeight = desc.fHeight;
-    surfDesc.fConfig = desc.fConfig;
-    surfDesc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
+    surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    surfDesc.fWidth = tex.width();
+    surfDesc.fHeight = tex.height();
+    surfDesc.fConfig = tex.config();
+    surfDesc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
     // FIXME:  this should be calling resolve_origin(), but Chrome code is currently
     // assuming the old behaviour, which is that backend textures are always
     // BottomLeft, even for non-RT's.  Once Chrome is fixed, change this to:
     // glTexDesc.fOrigin = resolve_origin(desc.fOrigin, renderTarget);
-    if (kDefault_GrSurfaceOrigin == desc.fOrigin) {
+    if (kDefault_GrSurfaceOrigin == origin) {
         surfDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
     } else {
-        surfDesc.fOrigin = desc.fOrigin;
+        surfDesc.fOrigin = origin;
     }
 
     GrGLRenderTarget::IDDesc rtIDDesc;
@@ -650,10 +662,6 @@
                                    GrPixelConfig srcConfig,
                                    DrawPreference* drawPreference,
                                    WritePixelTempDrawInfo* tempDrawInfo) {
-    if (GrPixelConfigIsCompressed(dstSurface->config())) {
-        return false;
-    }
-
     // This subclass only allows writes to textures. If the dst is not a texture we have to draw
     // into it. We could use glDrawPixels on GLs that have it, but we don't today.
     if (!dstSurface->asTexture()) {
@@ -666,6 +674,11 @@
         }
     }
 
+    // If the dst is MSAA, we have to draw, or we'll just be writing to the resolve target.
+    if (dstSurface->asRenderTarget() && dstSurface->asRenderTarget()->numColorSamples() > 0) {
+        ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
+    }
+
     if (GrPixelConfigIsSRGB(dstSurface->config()) != GrPixelConfigIsSRGB(srcConfig)) {
         ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
     }
@@ -748,18 +761,9 @@
     this->setScratchTextureUnit();
     GL_CALL(BindTexture(glTex->target(), glTex->textureID()));
 
-    bool success = false;
-    if (GrPixelConfigIsCompressed(glTex->desc().fConfig)) {
-        // We check that config == desc.fConfig in GrGLGpu::canWriteTexturePixels()
-        SkASSERT(config == glTex->desc().fConfig);
-        success = this->uploadCompressedTexData(glTex->desc(), glTex->target(), texels,
-                                                kWrite_UploadType, left, top, width, height);
-    } else {
-        success = this->uploadTexData(glTex->desc(), glTex->target(), kWrite_UploadType,
-                                      left, top, width, height, config, texels);
-    }
-
-    return success;
+    return this->uploadTexData(glTex->config(), glTex->width(), glTex->height(),
+                               glTex->origin(), glTex->target(), kWrite_UploadType,
+                               left, top, width, height, config, texels);
 }
 
 bool GrGLGpu::onTransferPixels(GrSurface* surface,
@@ -772,11 +776,6 @@
         return false;
     }
 
-    // For the moment, can't transfer compressed data
-    if (GrPixelConfigIsCompressed(glTex->desc().fConfig)) {
-        return false;
-    }
-
     this->setScratchTextureUnit();
     GL_CALL(BindTexture(glTex->target(), glTex->textureID()));
 
@@ -791,14 +790,14 @@
     mipLevel.fRowBytes = rowBytes;
     SkSTArray<1, GrMipLevel> texels;
     texels.push_back(mipLevel);
-    success = this->uploadTexData(glTex->desc(), glTex->target(), kTransfer_UploadType,
-                                  left, top, width, height, config, texels);
+    success = this->uploadTexData(glTex->config(), glTex->width(), glTex->height(), glTex->origin(),
+                                  glTex->target(), kTransfer_UploadType, left, top, width, height,
+                                  config, texels);
     return success;
 }
 
 // For GL_[UN]PACK_ALIGNMENT.
 static inline GrGLint config_alignment(GrPixelConfig config) {
-    SkASSERT(!GrPixelConfigIsCompressed(config));
     switch (config) {
         case kAlpha_8_GrPixelConfig:
         case kGray_8_GrPixelConfig:
@@ -817,26 +816,16 @@
         case kRG_float_GrPixelConfig:
             return 4;
         case kUnknown_GrPixelConfig:
-        case kETC1_GrPixelConfig:
             return 0;
     }
     SkFAIL("Invalid pixel config");
     return 0;
 }
 
-static inline GrGLenum check_alloc_error(const GrSurfaceDesc& desc,
-                                         const GrGLInterface* interface) {
-    if (SkToBool(desc.fFlags & kCheckAllocation_GrSurfaceFlag)) {
-        return GR_GL_GET_ERROR(interface);
-    } else {
-        return CHECK_ALLOC_ERROR(interface);
-    }
-}
-
 /**
  * Creates storage space for the texture and fills it with texels.
  *
- * @param desc           The surface descriptor for the texture being created.
+ * @param config         Pixel config of the texture.
  * @param interface      The GL interface in use.
  * @param caps           The capabilities of the GL device.
  * @param internalFormat The data format used for the internal storage of the texture. May be sized.
@@ -846,37 +835,33 @@
  * @param texels         The texel data of the texture being created.
  * @param baseWidth      The width of the texture's base mipmap level
  * @param baseHeight     The height of the texture's base mipmap level
- * @param succeeded      Set to true if allocating and populating the texture completed
- *                       without error.
  */
-static bool allocate_and_populate_uncompressed_texture(const GrSurfaceDesc& desc,
-                                                       const GrGLInterface& interface,
-                                                       const GrGLCaps& caps,
-                                                       GrGLenum target,
-                                                       GrGLenum internalFormat,
-                                                       GrGLenum internalFormatForTexStorage,
-                                                       GrGLenum externalFormat,
-                                                       GrGLenum externalType,
-                                                       const SkTArray<GrMipLevel>& texels,
-                                                       int baseWidth, int baseHeight) {
+static bool allocate_and_populate_texture(GrPixelConfig config,
+                                          const GrGLInterface& interface,
+                                          const GrGLCaps& caps,
+                                          GrGLenum target,
+                                          GrGLenum internalFormat,
+                                          GrGLenum internalFormatForTexStorage,
+                                          GrGLenum externalFormat,
+                                          GrGLenum externalType,
+                                          const SkTArray<GrMipLevel>& texels,
+                                          int baseWidth, int baseHeight) {
     CLEAR_ERROR_BEFORE_ALLOC(&interface);
 
-    bool useTexStorage = caps.isConfigTexSupportEnabled(desc.fConfig);
+    bool useTexStorage = caps.isConfigTexSupportEnabled(config);
     // We can only use TexStorage if we know we will not later change the storage requirements.
     // This means if we may later want to add mipmaps, we cannot use TexStorage.
     // Right now, we cannot know if we will later add mipmaps or not.
     // The only time we can use TexStorage is when we already have the
     // mipmaps or are using a format incompatible with MIP maps.
-    useTexStorage &= texels.count() > 1 || GrPixelConfigIsSint(desc.fConfig);
+    useTexStorage &= texels.count() > 1 || GrPixelConfigIsSint(config);
 
     if (useTexStorage) {
         // We never resize or change formats of textures.
         GL_ALLOC_CALL(&interface,
-                      TexStorage2D(target,
-                                   SkTMax(texels.count(), 1),
-                                   internalFormatForTexStorage,
-                                   desc.fWidth, desc.fHeight));
-        GrGLenum error = check_alloc_error(desc, &interface);
+                      TexStorage2D(target, SkTMax(texels.count(), 1), internalFormatForTexStorage,
+                                   baseWidth, baseHeight));
+        GrGLenum error = CHECK_ALLOC_ERROR(&interface);
         if (error != GR_GL_NO_ERROR) {
             return  false;
         } else {
@@ -886,8 +871,8 @@
                     continue;
                 }
                 int twoToTheMipLevel = 1 << currentMipLevel;
-                int currentWidth = SkTMax(1, desc.fWidth / twoToTheMipLevel);
-                int currentHeight = SkTMax(1, desc.fHeight / twoToTheMipLevel);
+                int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
+                int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
 
                 GR_GL_CALL(&interface,
                            TexSubImage2D(target,
@@ -912,7 +897,7 @@
                                      0, // border
                                      externalFormat, externalType,
                                      nullptr));
-            GrGLenum error = check_alloc_error(desc, &interface);
+            GrGLenum error = CHECK_ALLOC_ERROR(&interface);
             if (error != GR_GL_NO_ERROR) {
                 return false;
             }
@@ -933,7 +918,7 @@
                                          0, // border
                                          externalFormat, externalType,
                                          currentMipData));
-                GrGLenum error = check_alloc_error(desc, &interface);
+                GrGLenum error = CHECK_ALLOC_ERROR(&interface);
                 if (error != GR_GL_NO_ERROR) {
                     return false;
                 }
@@ -944,97 +929,6 @@
 }
 
 /**
- * Creates storage space for the texture and fills it with texels.
- *
- * @param desc           The surface descriptor for the texture being created.
- * @param interface      The GL interface in use.
- * @param caps           The capabilities of the GL device.
- * @param internalFormat The data format used for the internal storage of the texture.
- * @param texels         The texel data of the texture being created.
- */
-static bool allocate_and_populate_compressed_texture(const GrSurfaceDesc& desc,
-                                                     const GrGLInterface& interface,
-                                                     const GrGLCaps& caps,
-                                                     GrGLenum target, GrGLenum internalFormat,
-                                                     const SkTArray<GrMipLevel>& texels,
-                                                     int baseWidth, int baseHeight) {
-    CLEAR_ERROR_BEFORE_ALLOC(&interface);
-
-    bool useTexStorage = caps.isConfigTexSupportEnabled(desc.fConfig);
-    // We can only use TexStorage if we know we will not later change the storage requirements.
-    // This means if we may later want to add mipmaps, we cannot use TexStorage.
-    // Right now, we cannot know if we will later add mipmaps or not.
-    // The only time we can use TexStorage is when we already have the
-    // mipmaps.
-    useTexStorage &= texels.count() > 1;
-
-    if (useTexStorage) {
-        // We never resize or change formats of textures.
-        GL_ALLOC_CALL(&interface,
-                      TexStorage2D(target,
-                                   texels.count(),
-                                   internalFormat,
-                                   baseWidth, baseHeight));
-        GrGLenum error = check_alloc_error(desc, &interface);
-        if (error != GR_GL_NO_ERROR) {
-            return false;
-        } else {
-            for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
-                const void* currentMipData = texels[currentMipLevel].fPixels;
-                if (currentMipData == nullptr) {
-                    continue;
-                }
-
-                int twoToTheMipLevel = 1 << currentMipLevel;
-                int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
-                int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
-
-                // Make sure that the width and height that we pass to OpenGL
-                // is a multiple of the block size.
-                size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, currentWidth,
-                                                             currentHeight);
-                GR_GL_CALL(&interface, CompressedTexSubImage2D(target,
-                                                               currentMipLevel,
-                                                               0, // left
-                                                               0, // top
-                                                               currentWidth,
-                                                               currentHeight,
-                                                               internalFormat,
-                                                               SkToInt(dataSize),
-                                                               currentMipData));
-            }
-        }
-    } else {
-        for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
-            int twoToTheMipLevel = 1 << currentMipLevel;
-            int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
-            int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
-
-            // Make sure that the width and height that we pass to OpenGL
-            // is a multiple of the block size.
-            size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, baseWidth, baseHeight);
-
-            GL_ALLOC_CALL(&interface,
-                          CompressedTexImage2D(target,
-                                               currentMipLevel,
-                                               internalFormat,
-                                               currentWidth,
-                                               currentHeight,
-                                               0, // border
-                                               SkToInt(dataSize),
-                                               texels[currentMipLevel].fPixels));
-
-            GrGLenum error = check_alloc_error(desc, &interface);
-            if (error != GR_GL_NO_ERROR) {
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-/**
  * After a texture is created, any state which was altered during its creation
  * needs to be restored.
  *
@@ -1054,16 +948,11 @@
     }
 }
 
-bool GrGLGpu::uploadTexData(const GrSurfaceDesc& desc,
-                            GrGLenum target,
-                            UploadType uploadType,
-                            int left, int top, int width, int height,
-                            GrPixelConfig dataConfig,
+bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight,
+                            GrSurfaceOrigin texOrigin, GrGLenum target, UploadType uploadType,
+                            int left, int top, int width, int height, GrPixelConfig dataConfig,
                             const SkTArray<GrMipLevel>& texels) {
-    // If we're uploading compressed data then we should be using uploadCompressedTexData
-    SkASSERT(!GrPixelConfigIsCompressed(dataConfig));
-
-    SkASSERT(this->caps()->isConfigTexturable(desc.fConfig));
+    SkASSERT(this->caps()->isConfigTexturable(texConfig));
 
     // texels is const.
     // But we may need to flip the texture vertically to prepare it.
@@ -1095,11 +984,10 @@
             currentWidth > SK_MaxS32) {
             return false;
         }
-        if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top,
-                                               &currentWidth,
-                                               &currentHeight,
-                                               &texelsShallowCopy[currentMipLevel].fPixels,
-                                               &texelsShallowCopy[currentMipLevel].fRowBytes)) {
+        if (!GrSurfacePriv::AdjustWritePixelParams(texWidth, texHeight, bpp, &left, &top,
+                                                   &currentWidth, &currentHeight,
+                                                   &texelsShallowCopy[currentMipLevel].fPixels,
+                                                   &texelsShallowCopy[currentMipLevel].fRowBytes)) {
             return false;
         }
         if (currentWidth < 0 || currentHeight < 0) {
@@ -1112,12 +1000,12 @@
     // External format and type come from the upload data.
     GrGLenum externalFormat;
     GrGLenum externalType;
-    if (!this->glCaps().getTexImageFormats(desc.fConfig, dataConfig, &internalFormat,
-                                           &externalFormat, &externalType)) {
+    if (!this->glCaps().getTexImageFormats(texConfig, dataConfig, &internalFormat, &externalFormat,
+                                           &externalType)) {
         return false;
     }
     // TexStorage requires a sized format, and internalFormat may or may not be
-    GrGLenum internalFormatForTexStorage = this->glCaps().configSizedInternalFormat(desc.fConfig);
+    GrGLenum internalFormatForTexStorage = this->glCaps().configSizedInternalFormat(texConfig);
 
     /*
      *  Check whether to allocate a temporary buffer for flipping y or
@@ -1129,7 +1017,7 @@
     bool swFlipY = false;
     bool glFlipY = false;
 
-    if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin && !texelsShallowCopy.empty()) {
+    if (kBottomLeft_GrSurfaceOrigin == texOrigin && !texelsShallowCopy.empty()) {
         if (caps.unpackFlipYSupport()) {
             glFlipY = true;
         } else {
@@ -1212,22 +1100,22 @@
         if (glFlipY) {
             GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE));
         }
-        GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ALIGNMENT,
-                                          config_alignment(desc.fConfig)));
+        GR_GL_CALL(interface, PixelStorei(GR_GL_UNPACK_ALIGNMENT, config_alignment(texConfig)));
     }
 
     bool succeeded = true;
-    if (kNewTexture_UploadType == uploadType &&
-        0 == left && 0 == top &&
-        desc.fWidth == width && desc.fHeight == height) {
-        succeeded = allocate_and_populate_uncompressed_texture(desc, *interface, caps, target,
-                                                               internalFormat,
-                                                               internalFormatForTexStorage,
-                                                               externalFormat, externalType,
-                                                               texelsShallowCopy, width, height);
+    if (kNewTexture_UploadType == uploadType) {
+        if (0 == left && 0 == top && texWidth == width && texHeight == height) {
+            succeeded = allocate_and_populate_texture(
+                    texConfig, *interface, caps, target, internalFormat,
+                    internalFormatForTexStorage, externalFormat, externalType, texelsShallowCopy,
+                    width, height);
+        } else {
+            succeeded = false;
+        }
     } else {
         if (swFlipY || glFlipY) {
-            top = desc.fHeight - (top + height);
+            top = texHeight - (top + height);
         }
         for (int currentMipLevel = 0; currentMipLevel < texelsShallowCopy.count();
              currentMipLevel++) {
@@ -1250,77 +1138,6 @@
     return succeeded;
 }
 
-// TODO: This function is using a lot of wonky semantics like, if width == -1
-// then set width = desc.fWdith ... blah. A better way to do it might be to
-// create a CompressedTexData struct that takes a desc/ptr and figures out
-// the proper upload semantics. Then users can construct this function how they
-// see fit if they want to go against the "standard" way to do it.
-bool GrGLGpu::uploadCompressedTexData(const GrSurfaceDesc& desc,
-                                      GrGLenum target,
-                                      const SkTArray<GrMipLevel>& texels,
-                                      UploadType uploadType,
-                                      int left, int top, int width, int height) {
-    SkASSERT(this->caps()->isConfigTexturable(desc.fConfig));
-
-    // No support for software flip y, yet...
-    SkASSERT(kBottomLeft_GrSurfaceOrigin != desc.fOrigin);
-
-    const GrGLInterface* interface = this->glInterface();
-    const GrGLCaps& caps = this->glCaps();
-
-    if (-1 == width) {
-        width = desc.fWidth;
-    }
-#ifdef SK_DEBUG
-    else {
-        SkASSERT(width <= desc.fWidth);
-    }
-#endif
-
-    if (-1 == height) {
-        height = desc.fHeight;
-    }
-#ifdef SK_DEBUG
-    else {
-        SkASSERT(height <= desc.fHeight);
-    }
-#endif
-
-    // We only need the internal format for compressed 2D textures.
-    GrGLenum internalFormat;
-    if (!caps.getCompressedTexImageFormats(desc.fConfig, &internalFormat)) {
-        return false;
-    }
-
-    if (kNewTexture_UploadType == uploadType) {
-        return allocate_and_populate_compressed_texture(desc, *interface, caps, target,
-                                                        internalFormat, texels, width, height);
-    } else {
-        for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
-            SkASSERT(texels[currentMipLevel].fPixels || kTransfer_UploadType == uploadType);
-
-            int twoToTheMipLevel = 1 << currentMipLevel;
-            int currentWidth = SkTMax(1, width / twoToTheMipLevel);
-            int currentHeight = SkTMax(1, height / twoToTheMipLevel);
-
-            // Make sure that the width and height that we pass to OpenGL
-            // is a multiple of the block size.
-            size_t dataSize = GrCompressedFormatDataSize(desc.fConfig, currentWidth,
-                                                         currentHeight);
-            GL_CALL(CompressedTexSubImage2D(target,
-                                            currentMipLevel,
-                                            left, top,
-                                            currentWidth,
-                                            currentHeight,
-                                            internalFormat,
-                                            SkToInt(dataSize),
-                                            texels[currentMipLevel].fPixels));
-        }
-    }
-
-    return true;
-}
-
 static bool renderbuffer_storage_msaa(const GrGLContext& ctx,
                                       int sampleCount,
                                       GrGLenum format,
@@ -1418,8 +1235,7 @@
                                         GR_GL_COLOR_ATTACHMENT0,
                                         GR_GL_RENDERBUFFER,
                                         idDesc->fMSColorRenderbufferID));
-        if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) ||
-            !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) {
+        if (!this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) {
             GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
             if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
                 goto FAILED;
@@ -1441,8 +1257,7 @@
                                      texInfo.fTarget,
                                      texInfo.fID, 0));
     }
-    if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) ||
-        !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) {
+    if (!this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) {
         GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
         if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
             goto FAILED;
@@ -1466,7 +1281,7 @@
 }
 
 // good to set a break-point here to know when createTexture fails
-static GrTexture* return_null_texture() {
+static sk_sp<GrTexture> return_null_texture() {
 //    SkDEBUGFAIL("null texture");
     return nullptr;
 }
@@ -1477,18 +1292,6 @@
 }
 #endif
 
-static GrGLTexture::IDDesc generate_gl_texture(const GrGLInterface* interface) {
-    GrGLTexture::IDDesc idDesc;
-    idDesc.fInfo.fID = 0;
-    GR_GL_CALL(interface, GenTextures(1, &idDesc.fInfo.fID));
-    idDesc.fOwnership = GrBackendObjectOwnership::kOwned;
-    // When we create the texture, we only
-    // create GL_TEXTURE_2D at the moment.
-    // External clients can do something different.
-    idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D;
-    return idDesc;
-}
-
 static void set_initial_texture_params(const GrGLInterface* interface,
                                        const GrGLTextureInfo& info,
                                        GrGLTexture::TexParams* initialTexParams) {
@@ -1515,31 +1318,51 @@
                                         initialTexParams->fWrapT));
 }
 
-GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
-                                    SkBudgeted budgeted,
-                                    const SkTArray<GrMipLevel>& texels) {
+sk_sp<GrTexture> GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
+                                          SkBudgeted budgeted,
+                                          const SkTArray<GrMipLevel>& origTexels) {
     // We fail if the MSAA was requested and is not available.
     if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() && desc.fSampleCnt) {
         //SkDebugf("MSAA RT requested but not supported on this platform.");
         return return_null_texture();
     }
 
-    bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
+    bool performClear = (desc.fFlags & kPerformInitialClear_GrSurfaceFlag);
+    const SkTArray<GrMipLevel>* texels = &origTexels;
+
+    SkTArray<GrMipLevel> zeroLevels;
+    std::unique_ptr<uint8_t[]> zeros;
+    // TODO: remove the GrPixelConfigIsSint test. This is here because we have yet to add support
+    // for glClearBuffer* which must be used instead of glClearColor/glClear for integer FBO
+    // attachments.
+    if (performClear && !this->glCaps().clearTextureSupport() &&
+        (!this->glCaps().canConfigBeFBOColorAttachment(desc.fConfig) ||
+         GrPixelConfigIsSint(desc.fConfig))) {
+        size_t rowSize = GrBytesPerPixel(desc.fConfig) * desc.fWidth;
+        size_t size = rowSize * desc.fHeight;
+        zeros.reset(new uint8_t[size]);
+        memset(zeros.get(), 0, size);
+        zeroLevels.push_back(GrMipLevel{zeros.get(), 0});
+        texels = &zeroLevels;
+        performClear = false;
+    }
+
+    bool isRenderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
 
     GrGLTexture::IDDesc idDesc;
     idDesc.fOwnership = GrBackendObjectOwnership::kOwned;
     GrGLTexture::TexParams initialTexParams;
-    if (!this->createTextureImpl(desc, &idDesc.fInfo, renderTarget, &initialTexParams, texels)) {
+    if (!this->createTextureImpl(desc, &idDesc.fInfo, isRenderTarget, &initialTexParams, *texels)) {
         return return_null_texture();
     }
 
     bool wasMipMapDataProvided = false;
-    if (texels.count() > 1) {
+    if (texels->count() > 1) {
         wasMipMapDataProvided = true;
     }
 
-    GrGLTexture* tex;
-    if (renderTarget) {
+    sk_sp<GrGLTexture> tex;
+    if (isRenderTarget) {
         // unbind the texture from the texture unit before binding it to the frame buffer
         GL_CALL(BindTexture(idDesc.fInfo.fTarget, 0));
         GrGLRenderTarget::IDDesc rtIDDesc;
@@ -1548,50 +1371,36 @@
             GL_CALL(DeleteTextures(1, &idDesc.fInfo.fID));
             return return_null_texture();
         }
-        tex = new GrGLTextureRenderTarget(this, budgeted, desc, idDesc, rtIDDesc,
-                                          wasMipMapDataProvided);
+        tex = sk_make_sp<GrGLTextureRenderTarget>(this, budgeted, desc, idDesc, rtIDDesc,
+                                                  wasMipMapDataProvided);
     } else {
-        tex = new GrGLTexture(this, budgeted, desc, idDesc, wasMipMapDataProvided);
+        tex = sk_make_sp<GrGLTexture>(this, budgeted, desc, idDesc, wasMipMapDataProvided);
     }
     tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
 #ifdef TRACE_TEXTURE_CREATION
     SkDebugf("--- new texture [%d] size=(%d %d) config=%d\n",
              idDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig);
 #endif
-    return tex;
-}
-
-GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc,
-                                              SkBudgeted budgeted,
-                                              const SkTArray<GrMipLevel>& texels) {
-    // Make sure that we're not flipping Y.
-    if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
-        return return_null_texture();
+    if (tex && performClear) {
+        if (this->glCaps().clearTextureSupport()) {
+            GrGLenum format = GrPixelConfigIsSint(tex->config()) ? GR_GL_RGBA_INTEGER : GR_GL_RGBA;
+            static constexpr uint32_t kZero = 0;
+            GL_CALL(ClearTexImage(tex->textureID(), 0, format, GR_GL_UNSIGNED_BYTE, &kZero));
+        } else {
+            SkASSERT(!GrPixelConfigIsSint(desc.fConfig));
+            GrGLIRect viewport;
+            this->bindSurfaceFBOForPixelOps(tex.get(), GR_GL_FRAMEBUFFER, &viewport,
+                                            kDst_TempFBOTarget);
+            this->disableScissor();
+            this->disableWindowRectangles();
+            GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+            fHWWriteToColor = kYes_TriState;
+            GL_CALL(ClearColor(0, 0, 0, 0));
+            GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
+            this->unbindTextureFBOForPixelOps(GR_GL_FRAMEBUFFER, tex.get());
+            fHWBoundRenderTargetUniqueID.makeInvalid();
+        }
     }
-
-    GrGLTexture::IDDesc idDesc = generate_gl_texture(this->glInterface());
-    if (!idDesc.fInfo.fID) {
-        return return_null_texture();
-    }
-
-    this->setScratchTextureUnit();
-    GL_CALL(BindTexture(idDesc.fInfo.fTarget, idDesc.fInfo.fID));
-
-    GrGLTexture::TexParams initialTexParams;
-    set_initial_texture_params(this->glInterface(), idDesc.fInfo, &initialTexParams);
-
-    if (!this->uploadCompressedTexData(desc, idDesc.fInfo.fTarget, texels)) {
-        GL_CALL(DeleteTextures(1, &idDesc.fInfo.fID));
-        return return_null_texture();
-    }
-
-    GrGLTexture* tex;
-    tex = new GrGLTexture(this, budgeted, desc, idDesc);
-    tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
-#ifdef TRACE_TEXTURE_CREATION
-    SkDebugf("--- new compressed texture [%d] size=(%d %d) config=%d\n",
-             idDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig);
-#endif
     return tex;
 }
 
@@ -1756,9 +1565,9 @@
     if (info) {
         set_initial_texture_params(this->glInterface(), *info, initialTexParams);
     }
-    if (!this->uploadTexData(desc, info->fTarget, kNewTexture_UploadType, 0, 0,
-                             desc.fWidth, desc.fHeight,
-                             desc.fConfig, texels)) {
+    if (!this->uploadTexData(desc.fConfig, desc.fWidth, desc.fHeight, desc.fOrigin, info->fTarget,
+                             kNewTexture_UploadType, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                             texels)) {
         GL_CALL(DeleteTextures(1, &(info->fID)));
         return false;
     }
@@ -1766,8 +1575,7 @@
 }
 
 GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
-                                                                     int width,
-                                                                     int height) {
+                                                                     int width, int height) {
     SkASSERT(width >= rt->width());
     SkASSERT(height >= rt->height());
 
@@ -1799,7 +1607,7 @@
         GL_ALLOC_CALL(this->glInterface(), RenderbufferStorage(GR_GL_RENDERBUFFER,
                                                                sFmt.fInternalFormat,
                                                                width, height));
-        SkASSERT(GR_GL_NO_ERROR == check_alloc_error(rt->desc(), this->glInterface()));
+        SkASSERT(GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(this->glInterface()));
     }
     fStats.incStencilAttachmentCreates();
     // After sized formats we attempt an unsized format and take
@@ -1826,6 +1634,11 @@
     return GrGLBuffer::Create(this, size, intendedType, accessPattern, data);
 }
 
+
+std::unique_ptr<OpAllocator> GrGLGpu::onCreateInstancedRenderingAllocator() {
+    return std::unique_ptr<OpAllocator>(new GLOpAllocator(this->caps()));
+}
+
 InstancedRendering* GrGLGpu::onCreateInstancedRendering() {
     return new GLInstancedRendering(this);
 }
@@ -1927,7 +1740,6 @@
     pipeline.getXferProcessor().getBlendInfo(&blendInfo);
 
     this->flushColorWrite(blendInfo.fWriteColor);
-    this->flushDrawFace(pipeline.getDrawFace());
     this->flushMinSampleShading(primProc.getSampleShading());
 
     GrGLuint programID = program->programID();
@@ -1966,51 +1778,48 @@
 }
 
 void GrGLGpu::setupGeometry(const GrPrimitiveProcessor& primProc,
-                            const GrNonInstancedMesh& mesh,
-                            size_t* indexOffsetInBytes) {
-    const GrBuffer* vbuf = mesh.vertexBuffer();
-    SkASSERT(vbuf);
-    SkASSERT(!vbuf->isMapped());
-
+                            const GrBuffer* indexBuffer,
+                            const GrBuffer* vertexBuffer,
+                            int baseVertex,
+                            const GrBuffer* instanceBuffer,
+                            int baseInstance) {
     GrGLAttribArrayState* attribState;
-    if (mesh.isIndexed()) {
-        SkASSERT(indexOffsetInBytes);
-
-        *indexOffsetInBytes = 0;
-        const GrBuffer* ibuf = mesh.indexBuffer();
-        SkASSERT(ibuf);
-        SkASSERT(!ibuf->isMapped());
-        *indexOffsetInBytes += ibuf->baseOffset();
-        attribState = fHWVertexArrayState.bindInternalVertexArray(this, ibuf);
+    if (indexBuffer) {
+        SkASSERT(indexBuffer && !indexBuffer->isMapped());
+        attribState = fHWVertexArrayState.bindInternalVertexArray(this, indexBuffer);
     } else {
         attribState = fHWVertexArrayState.bindInternalVertexArray(this);
     }
 
-    int vaCount = primProc.numAttribs();
-    if (vaCount > 0) {
+    struct {
+        const GrBuffer*   fBuffer;
+        int               fStride;
+        size_t            fBufferOffset;
+    } bindings[2];
 
-        GrGLsizei stride = static_cast<GrGLsizei>(primProc.getVertexStride());
+    if (int vertexStride = primProc.getVertexStride()) {
+        SkASSERT(vertexBuffer && !vertexBuffer->isMapped());
+        bindings[0].fBuffer = vertexBuffer;
+        bindings[0].fStride = vertexStride;
+        bindings[0].fBufferOffset = vertexBuffer->baseOffset() + baseVertex * vertexStride;
+    }
+    if (int instanceStride = primProc.getInstanceStride()) {
+        SkASSERT(instanceBuffer && !instanceBuffer->isMapped());
+        bindings[1].fBuffer = instanceBuffer;
+        bindings[1].fStride = instanceStride;
+        bindings[1].fBufferOffset = instanceBuffer->baseOffset() + baseInstance * instanceStride;
+    }
 
-        size_t vertexOffsetInBytes = stride * mesh.startVertex();
+    int numAttribs = primProc.numAttribs();
+    attribState->enableVertexArrays(this, numAttribs);
 
-        vertexOffsetInBytes += vbuf->baseOffset();
-
-        uint32_t usedAttribArraysMask = 0;
-        size_t offset = 0;
-
-        for (int attribIndex = 0; attribIndex < vaCount; attribIndex++) {
-            const GrGeometryProcessor::Attribute& attrib = primProc.getAttrib(attribIndex);
-            usedAttribArraysMask |= (1 << attribIndex);
-            GrVertexAttribType attribType = attrib.fType;
-            attribState->set(this,
-                             attribIndex,
-                             vbuf,
-                             attribType,
-                             stride,
-                             reinterpret_cast<GrGLvoid*>(vertexOffsetInBytes + offset));
-            offset += attrib.fOffset;
-        }
-        attribState->disableUnusedArrays(this, usedAttribArraysMask);
+    for (int i = 0; i < numAttribs; ++i) {
+        using InputRate = GrPrimitiveProcessor::Attribute::InputRate;
+        const GrGeometryProcessor::Attribute& attrib = primProc.getAttrib(i);
+        const int divisor = InputRate::kPerInstance == attrib.fInputRate ? 1 : 0;
+        const auto& binding = bindings[divisor];
+        attribState->set(this, i, binding.fBuffer, attrib.fType, binding.fStride,
+                         binding.fBufferOffset + attrib.fOffsetInRecord, divisor);
     }
 }
 
@@ -2095,6 +1904,19 @@
 
     GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
     fHWWriteToColor = kYes_TriState;
+
+    if (this->glCaps().clearToBoundaryValuesIsBroken() &&
+        (1 == r || 0 == r) && (1 == g || 0 == g) && (1 == b || 0 == b) && (1 == a || 0 == a)) {
+#ifdef SK_BUILD_FOR_ANDROID
+        // Android doesn't have std::nextafter but has nextafter.
+        static const GrGLfloat safeAlpha1 = nextafter(1.f, 2.f);
+        static const GrGLfloat safeAlpha0 = nextafter(0.f, -1.f);
+#else
+        static const GrGLfloat safeAlpha1 = std::nextafter(1.f, 2.f);
+        static const GrGLfloat safeAlpha0 = std::nextafter(0.f, -1.f);
+#endif
+        a = (1 == a) ? safeAlpha1 : safeAlpha0;
+    }
     GL_CALL(ClearColor(r, g, b, a));
     GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
 }
@@ -2121,6 +1943,11 @@
     SkASSERT(target);
     this->handleDirtyContext();
 
+    if (this->glCaps().useDrawToClearStencilClip()) {
+        this->clearStencilClipAsDraw(clip, insideStencilMask, target);
+        return;
+    }
+
     GrStencilAttachment* sb = target->renderTargetPriv().getStencilAttachment();
     // this should only be called internally when we know we have a
     // stencil buffer.
@@ -2213,7 +2040,7 @@
         desc.fWidth = desc.fHeight = 16;
         if (this->glCaps().isConfigRenderable(rtConfig, false)) {
             desc.fFlags = kRenderTarget_GrSurfaceFlag;
-            temp.reset(this->createTexture(desc, SkBudgeted::kNo));
+            temp = this->createTexture(desc, SkBudgeted::kNo);
             if (!temp) {
                 return false;
             }
@@ -2221,7 +2048,7 @@
             this->flushRenderTarget(glrt, &SkIRect::EmptyIRect());
             return true;
         } else if (this->glCaps().canConfigBeFBOColorAttachment(rtConfig)) {
-            temp.reset(this->createTexture(desc, SkBudgeted::kNo));
+            temp = this->createTexture(desc, SkBudgeted::kNo);
             if (!temp) {
                 return false;
             }
@@ -2278,7 +2105,7 @@
 
     // Depends on why we need/want a temp draw. Start off assuming no change, the surface we read
     // from will be srcConfig and we will read readConfig pixels from it.
-    // Not that if we require a draw and return a non-renderable format for the temp surface the
+    // Note that if we require a draw and return a non-renderable format for the temp surface the
     // base class will fail for us.
     tempDrawInfo->fTempSurfaceDesc.fConfig = srcConfig;
     tempDrawInfo->fReadConfig = readConfig;
@@ -2642,42 +2469,7 @@
             this->xferBarrier(pipeline.getRenderTarget(), barrierType);
         }
 
-        const GrMesh& mesh = meshes[i];
-        GrMesh::Iterator iter;
-        const GrNonInstancedMesh* nonInstMesh = iter.init(mesh);
-        do {
-            size_t indexOffsetInBytes = 0;
-            this->setupGeometry(primProc, *nonInstMesh, &indexOffsetInBytes);
-            if (nonInstMesh->isIndexed()) {
-                GrGLvoid* indices =
-                    reinterpret_cast<GrGLvoid*>(indexOffsetInBytes +
-                                                sizeof(uint16_t) * nonInstMesh->startIndex());
-                // info.startVertex() was accounted for by setupGeometry.
-                if (this->glCaps().drawRangeElementsSupport()) {
-                    // We assume here that the GrMeshDrawOps that generated the mesh used the full
-                    // 0..vertexCount()-1 range.
-                    int start = 0;
-                    int end = nonInstMesh->vertexCount() - 1;
-                    GL_CALL(DrawRangeElements(gPrimitiveType2GLMode[nonInstMesh->primitiveType()],
-                                              start, end,
-                                              nonInstMesh->indexCount(),
-                                              GR_GL_UNSIGNED_SHORT,
-                                              indices));
-                } else {
-                    GL_CALL(DrawElements(gPrimitiveType2GLMode[nonInstMesh->primitiveType()],
-                                         nonInstMesh->indexCount(),
-                                         GR_GL_UNSIGNED_SHORT,
-                                         indices));
-                }
-            } else {
-                // Pass 0 for parameter first. We have to adjust glVertexAttribPointer() to account
-                // for startVertex in the DrawElements case. So we always rely on setupGeometry to
-                // have accounted for startVertex.
-                GL_CALL(DrawArrays(gPrimitiveType2GLMode[nonInstMesh->primitiveType()], 0,
-                                   nonInstMesh->vertexCount()));
-            }
-            fStats.incNumDraws();
-        } while ((nonInstMesh = iter.next()));
+        meshes[i].sendToGpu(primProc, this);
     }
 
 #if SWAP_PER_DRAW
@@ -2694,6 +2486,67 @@
 #endif
 }
 
+void GrGLGpu::sendMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType primitiveType,
+                            const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) {
+    const GrGLenum glPrimType = gPrimitiveType2GLMode[primitiveType];
+
+    if (this->glCaps().drawArraysBaseVertexIsBroken()) {
+        this->setupGeometry(primProc, nullptr, vertexBuffer, baseVertex, nullptr, 0);
+        GL_CALL(DrawArrays(glPrimType, 0, vertexCount));
+    } else {
+        this->setupGeometry(primProc, nullptr, vertexBuffer, 0, nullptr, 0);
+        GL_CALL(DrawArrays(glPrimType, baseVertex, vertexCount));
+    }
+    fStats.incNumDraws();
+}
+
+void GrGLGpu::sendIndexedMeshToGpu(const GrPrimitiveProcessor& primProc,
+                                   GrPrimitiveType primitiveType, const GrBuffer* indexBuffer,
+                                   int indexCount, int baseIndex, uint16_t minIndexValue,
+                                   uint16_t maxIndexValue, const GrBuffer* vertexBuffer,
+                                   int baseVertex) {
+    const GrGLenum glPrimType = gPrimitiveType2GLMode[primitiveType];
+    GrGLvoid* const indices = reinterpret_cast<void*>(indexBuffer->baseOffset() +
+                                                      sizeof(uint16_t) * baseIndex);
+
+    this->setupGeometry(primProc, indexBuffer, vertexBuffer, baseVertex, nullptr, 0);
+
+    if (this->glCaps().drawRangeElementsSupport()) {
+        GL_CALL(DrawRangeElements(glPrimType, minIndexValue, maxIndexValue, indexCount,
+                                  GR_GL_UNSIGNED_SHORT, indices));
+    } else {
+        GL_CALL(DrawElements(glPrimType, indexCount, GR_GL_UNSIGNED_SHORT, indices));
+    }
+    fStats.incNumDraws();
+}
+
+void GrGLGpu::sendInstancedMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType
+                                     primitiveType, const GrBuffer* vertexBuffer,
+                                     int vertexCount, int baseVertex,
+                                     const GrBuffer* instanceBuffer, int instanceCount,
+                                     int baseInstance) {
+    const GrGLenum glPrimType = gPrimitiveType2GLMode[primitiveType];
+    this->setupGeometry(primProc, nullptr, vertexBuffer, 0, instanceBuffer, baseInstance);
+    GL_CALL(DrawArraysInstanced(glPrimType, baseVertex, vertexCount, instanceCount));
+    fStats.incNumDraws();
+}
+
+void GrGLGpu::sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor& primProc,
+                                            GrPrimitiveType primitiveType,
+                                            const GrBuffer* indexBuffer, int indexCount,
+                                            int baseIndex, const GrBuffer* vertexBuffer,
+                                            int baseVertex, const GrBuffer* instanceBuffer,
+                                            int instanceCount, int baseInstance) {
+    const GrGLenum glPrimType = gPrimitiveType2GLMode[primitiveType];
+    GrGLvoid* indices = reinterpret_cast<void*>(indexBuffer->baseOffset() +
+                                                sizeof(uint16_t) * baseIndex);
+    this->setupGeometry(primProc, indexBuffer, vertexBuffer, baseVertex,
+                        instanceBuffer, baseInstance);
+    GL_CALL(DrawElementsInstanced(glPrimType, indexCount, GR_GL_UNSIGNED_SHORT, indices,
+                                  instanceCount));
+    fStats.incNumDraws();
+}
+
 void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target) {
     GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
     if (rt->needsResolve()) {
@@ -2806,7 +2659,6 @@
             fHWStencilTestEnabled = kYes_TriState;
         }
         if (stencilSettings.isTwoSided()) {
-            SkASSERT(this->caps()->twoSidedStencilSupport());
             set_gl_stencil(this->glInterface(),
                            stencilSettings.front(),
                            GR_GL_FRONT);
@@ -2850,7 +2702,7 @@
     }
 
     if (0 != this->caps()->maxRasterSamples()) {
-        if (useHWAA && rt->isMixedSampled() && !stencilEnabled) {
+        if (useHWAA && GrFSAAType::kMixedSamples == rt->fsaaType() && !stencilEnabled) {
             // Since stencil is disabled and we want more samples than are in the color buffer, we
             // need to tell the rasterizer explicitly how many to run.
             if (kYes_TriState != fHWRasterMultisampleEnabled) {
@@ -2869,7 +2721,7 @@
             }
         }
     } else {
-        SkASSERT(!useHWAA || !rt->isMixedSampled() || stencilEnabled);
+        SkASSERT(!useHWAA || GrFSAAType::kMixedSamples != rt->fsaaType() || stencilEnabled);
     }
 }
 
@@ -3028,7 +2880,7 @@
     GrSamplerParams::FilterMode filterMode = params.filterMode();
 
     if (GrSamplerParams::kMipMap_FilterMode == filterMode) {
-        if (!this->caps()->mipMapSupport() || GrPixelConfigIsCompressed(texture->config())) {
+        if (!this->caps()->mipMapSupport()) {
             filterMode = GrSamplerParams::kBilerp_FilterMode;
         }
     }
@@ -3174,7 +3026,7 @@
     GrSamplerParams::FilterMode filterMode = params.filterMode();
 
     if (GrSamplerParams::kMipMap_FilterMode == filterMode) {
-        if (!this->caps()->mipMapSupport() || GrPixelConfigIsCompressed(texture->config())) {
+        if (!this->caps()->mipMapSupport()) {
             filterMode = GrSamplerParams::kBilerp_FilterMode;
         }
     }
@@ -3269,27 +3121,6 @@
     }
 }
 
-void GrGLGpu::flushDrawFace(GrDrawFace face) {
-    if (fHWDrawFace != face) {
-        switch (face) {
-            case GrDrawFace::kCCW:
-                GL_CALL(Enable(GR_GL_CULL_FACE));
-                GL_CALL(CullFace(GR_GL_BACK));
-                break;
-            case GrDrawFace::kCW:
-                GL_CALL(Enable(GR_GL_CULL_FACE));
-                GL_CALL(CullFace(GR_GL_FRONT));
-                break;
-            case GrDrawFace::kBoth:
-                GL_CALL(Disable(GR_GL_CULL_FACE));
-                break;
-            default:
-                SkFAIL("Unknown draw face.");
-        }
-        fHWDrawFace = face;
-    }
-}
-
 void GrGLGpu::setTextureUnit(int unit) {
     SkASSERT(unit >= 0 && unit < fHWBoundTextureUniqueIDs.count());
     if (unit != fHWActiveTextureUnitIdx) {
@@ -3345,8 +3176,13 @@
         }
     }
     if (GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag & blitFramebufferFlags) {
-        if (srcRT && srcRT->numColorSamples() && dstRT && !dstRT->numColorSamples()) {
-            return false;
+        if (srcRT && srcRT->numColorSamples()) {
+            if (dstRT && !dstRT->numColorSamples()) {
+                return false;
+            }
+            if (SkRect::Make(srcRect) != srcRT->getBoundsRect()) {
+                return false;
+            }
         }
     }
     if (GrGLCaps::kNoMSAADst_BlitFramebufferFlag & blitFramebufferFlags) {
@@ -3365,9 +3201,13 @@
         }
     }
     if (GrGLCaps::kRectsMustMatchForMSAASrc_BlitFramebufferFlag & blitFramebufferFlags) {
-        if (srcRT && srcRT->numColorSamples() &&
-            (dstPoint.fX != srcRect.fLeft || dstPoint.fY != srcRect.fTop)) {
-            return false;
+        if (srcRT && srcRT->numColorSamples()) {
+            if (dstPoint.fX != srcRect.fLeft || dstPoint.fY != srcRect.fTop) {
+                return false;
+            }
+            if (dst->origin() != src->origin()) {
+                return false;
+            }
         }
     }
     return true;
@@ -3408,7 +3248,6 @@
     // Check that we could wrap the source in an FBO, that the dst is TEXTURE_2D, that no mirroring
     // is required.
     if (gpu->glCaps().canConfigBeFBOColorAttachment(src->config()) &&
-        !GrPixelConfigIsCompressed(src->config()) &&
         (!srcTex || srcTex->target() == GR_GL_TEXTURE_2D) && dstTex->target() == GR_GL_TEXTURE_2D &&
         dst->origin() == src->origin()) {
         return true;
@@ -3570,7 +3409,7 @@
         fshaderTxt.appendf("#extension %s : require\n",
                            shaderCaps->externalTextureExtensionString());
     }
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *shaderCaps,
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps,
                                                  &fshaderTxt);
     vTexCoord.setTypeModifier(GrShaderVar::kIn_TypeModifier);
     vTexCoord.appendDecl(shaderCaps, &fshaderTxt);
@@ -3708,7 +3547,7 @@
             fshaderTxt.appendf("#extension %s : require\n", extension);
         }
     }
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *shaderCaps,
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps,
                                                  &fshaderTxt);
     for (int i = 0; i < numTaps; ++i) {
         vTexCoords[i].setTypeModifier(GrShaderVar::kIn_TypeModifier);
@@ -3777,64 +3616,43 @@
     return true;
 }
 
-bool GrGLGpu::createWireRectProgram() {
-    if (!fWireRectArrayBuffer) {
-        static const GrGLfloat vdata[] = {
-            0, 0,
-            0, 1,
-            1, 1,
-            1, 0
-        };
-        fWireRectArrayBuffer.reset(GrGLBuffer::Create(this, sizeof(vdata), kVertex_GrBufferType,
-                                                      kStatic_GrAccessPattern, vdata));
-        if (!fWireRectArrayBuffer) {
+bool GrGLGpu::createStencilClipClearProgram() {
+    if (!fStencilClipClearArrayBuffer) {
+        static const GrGLfloat vdata[] = {-1, -1, 1, -1, -1, 1, 1, 1};
+        fStencilClipClearArrayBuffer.reset(GrGLBuffer::Create(
+                this, sizeof(vdata), kVertex_GrBufferType, kStatic_GrAccessPattern, vdata));
+        if (!fStencilClipClearArrayBuffer) {
             return false;
         }
     }
 
-    SkASSERT(!fWireRectProgram.fProgram);
-    GL_CALL_RET(fWireRectProgram.fProgram, CreateProgram());
-    if (!fWireRectProgram.fProgram) {
+    SkASSERT(!fStencilClipClearProgram);
+    GL_CALL_RET(fStencilClipClearProgram, CreateProgram());
+    if (!fStencilClipClearProgram) {
         return false;
     }
 
-    GrShaderVar uColor("u_color", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier);
-    GrShaderVar uRect("u_rect", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier);
     GrShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kIn_TypeModifier);
     const char* version = this->caps()->shaderCaps()->versionDeclString();
 
-    // The rect uniform specifies the rectangle in NDC space as a vec4 (left,top,right,bottom). The
-    // program is used with a vbo containing the unit square. Vertices are computed from the rect
-    // uniform using the 4 vbo vertices.
     SkString vshaderTxt(version);
     aVertex.appendDecl(this->caps()->shaderCaps(), &vshaderTxt);
     vshaderTxt.append(";");
-    uRect.appendDecl(this->caps()->shaderCaps(), &vshaderTxt);
-    vshaderTxt.append(";");
     vshaderTxt.append(
-        "// Wire Rect Program VS\n"
-        "void main() {"
-        "  gl_Position.x = u_rect.x + a_vertex.x * (u_rect.z - u_rect.x);"
-        "  gl_Position.y = u_rect.y + a_vertex.y * (u_rect.w - u_rect.y);"
-        "  gl_Position.zw = vec2(0, 1);"
-        "}"
-    );
-
-    GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
+            "// Stencil Clip Clear Program VS\n"
+            "void main() {"
+            "  gl_Position = vec4(a_vertex.x, a_vertex.y, 0, 1);"
+            "}");
 
     SkString fshaderTxt(version);
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision,
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision,
                                                  *this->caps()->shaderCaps(),
                                                  &fshaderTxt);
-    uColor.appendDecl(this->caps()->shaderCaps(), &fshaderTxt);
-    fshaderTxt.append(";");
     fshaderTxt.appendf(
-        "// Write Rect Program FS\n"
-        "void main() {"
-        "  sk_FragColor = %s;"
-        "}",
-        uColor.c_str()
-    );
+            "// Stencil Clip Clear Program FS\n"
+            "void main() {"
+            "  sk_FragColor = vec4(0);"
+            "}");
 
     const char* str;
     GrGLint length;
@@ -3844,25 +3662,21 @@
     SkSL::Program::Settings settings;
     settings.fCaps = this->caps()->shaderCaps();
     SkSL::Program::Inputs inputs;
-    GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram,
-                                                  GR_GL_VERTEX_SHADER, &str, &length, 1,
-                                                  &fStats, settings, &inputs);
+    GrGLuint vshader =
+            GrGLCompileAndAttachShader(*fGLContext, fStencilClipClearProgram, GR_GL_VERTEX_SHADER,
+                                       &str, &length, 1, &fStats, settings, &inputs);
     SkASSERT(inputs.isEmpty());
 
     str = fshaderTxt.c_str();
     length = SkToInt(fshaderTxt.size());
-    GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram,
-                                                  GR_GL_FRAGMENT_SHADER, &str, &length, 1,
-                                                  &fStats, settings, &inputs);
+    GrGLuint fshader =
+            GrGLCompileAndAttachShader(*fGLContext, fStencilClipClearProgram, GR_GL_FRAGMENT_SHADER,
+                                       &str, &length, 1, &fStats, settings, &inputs);
     SkASSERT(inputs.isEmpty());
 
-    GL_CALL(LinkProgram(fWireRectProgram.fProgram));
+    GL_CALL(LinkProgram(fStencilClipClearProgram));
 
-    GL_CALL_RET(fWireRectProgram.fColorUniform,
-                GetUniformLocation(fWireRectProgram.fProgram, "u_color"));
-    GL_CALL_RET(fWireRectProgram.fRectUniform,
-                GetUniformLocation(fWireRectProgram.fProgram, "u_rect"));
-    GL_CALL(BindAttribLocation(fWireRectProgram.fProgram, 0, "a_vertex"));
+    GL_CALL(BindAttribLocation(fStencilClipClearProgram, 0, "a_vertex"));
 
     GL_CALL(DeleteShader(vshader));
     GL_CALL(DeleteShader(fshader));
@@ -3870,72 +3684,46 @@
     return true;
 }
 
-void GrGLGpu::drawDebugWireRect(GrRenderTarget* rt, const SkIRect& rect, GrColor color) {
+void GrGLGpu::clearStencilClipAsDraw(const GrFixedClip& clip, bool insideStencilMask,
+                                     GrRenderTarget* rt) {
     // TODO: This should swizzle the output to match dst's config, though it is a debugging
     // visualization.
 
     this->handleDirtyContext();
-    if (!fWireRectProgram.fProgram) {
-        if (!this->createWireRectProgram()) {
-            SkDebugf("Failed to create wire rect program.\n");
+    if (!fStencilClipClearProgram) {
+        if (!this->createStencilClipClearProgram()) {
+            SkDebugf("Failed to create stencil clip clear program.\n");
             return;
         }
     }
 
-    int w = rt->width();
-    int h = rt->height();
-
-    // Compute the edges of the rectangle (top,left,right,bottom) in NDC space. Must consider
-    // whether the render target is flipped or not.
-    GrGLfloat edges[4];
-    edges[0] = SkIntToScalar(rect.fLeft) + 0.5f;
-    edges[2] = SkIntToScalar(rect.fRight) - 0.5f;
-    if (kBottomLeft_GrSurfaceOrigin == rt->origin()) {
-        edges[1] = h - (SkIntToScalar(rect.fTop) + 0.5f);
-        edges[3] = h - (SkIntToScalar(rect.fBottom) - 0.5f);
-    } else {
-        edges[1] = SkIntToScalar(rect.fTop) + 0.5f;
-        edges[3] = SkIntToScalar(rect.fBottom) - 0.5f;
-    }
-    edges[0] = 2 * edges[0] / w - 1.0f;
-    edges[1] = 2 * edges[1] / h - 1.0f;
-    edges[2] = 2 * edges[2] / w - 1.0f;
-    edges[3] = 2 * edges[3] / h - 1.0f;
-
-    GrGLfloat channels[4];
-    static const GrGLfloat scale255 = 1.f / 255.f;
-    channels[0] = GrColorUnpackR(color) * scale255;
-    channels[1] = GrColorUnpackG(color) * scale255;
-    channels[2] = GrColorUnpackB(color) * scale255;
-    channels[3] = GrColorUnpackA(color) * scale255;
-
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(rt->asRenderTarget());
-    this->flushRenderTarget(glRT, &rect);
+    this->flushRenderTarget(glRT, nullptr);
 
-    GL_CALL(UseProgram(fWireRectProgram.fProgram));
-    fHWProgramID = fWireRectProgram.fProgram;
+    GL_CALL(UseProgram(fStencilClipClearProgram));
+    fHWProgramID = fStencilClipClearProgram;
 
     fHWVertexArrayState.setVertexArrayID(this, 0);
 
     GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
-    attribs->set(this, 0, fWireRectArrayBuffer.get(), kVec2f_GrVertexAttribType,
+    attribs->enableVertexArrays(this, 1);
+    attribs->set(this, 0, fStencilClipClearArrayBuffer.get(), kVec2f_GrVertexAttribType,
                  2 * sizeof(GrGLfloat), 0);
-    attribs->disableUnusedArrays(this, 0x1);
-
-    GL_CALL(Uniform4fv(fWireRectProgram.fRectUniform, 1, edges));
-    GL_CALL(Uniform4fv(fWireRectProgram.fColorUniform, 1, channels));
 
     GrXferProcessor::BlendInfo blendInfo;
     blendInfo.reset();
     this->flushBlend(blendInfo, GrSwizzle::RGBA());
-    this->flushColorWrite(true);
-    this->flushDrawFace(GrDrawFace::kBoth);
+    this->flushColorWrite(false);
     this->flushHWAAState(glRT, false, false);
-    this->disableScissor();
-    this->disableWindowRectangles();
-    this->disableStencil();
-
-    GL_CALL(DrawArrays(GR_GL_LINE_LOOP, 0, 4));
+    this->flushScissor(clip.scissorState(), glRT->getViewport(), glRT->origin());
+    this->flushWindowRectangles(clip.windowRectsState(), glRT);
+    GrStencilAttachment* sb = rt->renderTargetPriv().getStencilAttachment();
+    // This should only be called internally when we know we have a stencil buffer.
+    SkASSERT(sb);
+    GrStencilSettings settings = GrStencilSettings(
+            *GrStencilSettings::SetClipBitSettings(insideStencilMask), false, sb->bits());
+    this->flushStencil(settings);
+    GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4));
 }
 
 
@@ -3972,9 +3760,9 @@
     fHWVertexArrayState.setVertexArrayID(this, 0);
 
     GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
+    attribs->enableVertexArrays(this, 1);
     attribs->set(this, 0, fCopyProgramArrayBuffer.get(), kVec2f_GrVertexAttribType,
                  2 * sizeof(GrGLfloat), 0);
-    attribs->disableUnusedArrays(this, 0x1);
 
     // dst rect edges in NDC (-1 to 1)
     int dw = dst->width();
@@ -3992,20 +3780,17 @@
     GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w);
     GrGLfloat sy0 = (GrGLfloat)srcRect.fTop;
     GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h);
+    int sw = src->width();
     int sh = src->height();
     if (kBottomLeft_GrSurfaceOrigin == src->origin()) {
         sy0 = sh - sy0;
         sy1 = sh - sy1;
     }
-    // src rect edges in normalized texture space (0 to 1) unless we're using a RECTANGLE texture.
-    GrGLenum srcTarget = srcTex->target();
-    if (GR_GL_TEXTURE_RECTANGLE != srcTarget) {
-        int sw = src->width();
-        sx0 /= sw;
-        sx1 /= sw;
-        sy0 /= sh;
-        sy1 /= sh;
-    }
+    // src rect edges in normalized texture space (0 to 1)
+    sx0 /= sw;
+    sx1 /= sw;
+    sy0 /= sh;
+    sy1 /= sh;
 
     GL_CALL(Uniform4f(fCopyPrograms[progIdx].fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0));
     GL_CALL(Uniform4f(fCopyPrograms[progIdx].fTexCoordXformUniform,
@@ -4016,7 +3801,6 @@
     blendInfo.reset();
     this->flushBlend(blendInfo, GrSwizzle::RGBA());
     this->flushColorWrite(true);
-    this->flushDrawFace(GrDrawFace::kBoth);
     this->flushHWAAState(nullptr, false, false);
     this->disableScissor();
     this->disableWindowRectangles();
@@ -4216,16 +4000,15 @@
     fHWVertexArrayState.setVertexArrayID(this, 0);
 
     GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
+    attribs->enableVertexArrays(this, 1);
     attribs->set(this, 0, fMipmapProgramArrayBuffer.get(), kVec2f_GrVertexAttribType,
                  2 * sizeof(GrGLfloat), 0);
-    attribs->disableUnusedArrays(this, 0x1);
 
     // Set "simple" state once:
     GrXferProcessor::BlendInfo blendInfo;
     blendInfo.reset();
     this->flushBlend(blendInfo, GrSwizzle::RGBA());
     this->flushColorWrite(true);
-    this->flushDrawFace(GrDrawFace::kBoth);
     this->flushHWAAState(nullptr, false, false);
     this->disableScissor();
     this->disableWindowRectangles();
@@ -4280,8 +4063,8 @@
 
 void GrGLGpu::onQueryMultisampleSpecs(GrRenderTarget* rt, const GrStencilSettings& stencil,
                                       int* effectiveSampleCnt, SamplePattern* samplePattern) {
-    SkASSERT(!rt->isMixedSampled() || rt->renderTargetPriv().getStencilAttachment() ||
-             stencil.isDisabled());
+    SkASSERT(GrFSAAType::kMixedSamples != rt->fsaaType() ||
+             rt->renderTargetPriv().getStencilAttachment() || stencil.isDisabled());
 
     this->flushStencil(stencil);
     this->flushHWAAState(rt, true, !stencil.isDisabled());
@@ -4293,7 +4076,7 @@
         GR_GL_GetIntegerv(this->glInterface(), GR_GL_SAMPLES, effectiveSampleCnt);
     }
 
-    SkASSERT(*effectiveSampleCnt >= rt->desc().fSampleCnt);
+    SkASSERT(*effectiveSampleCnt >= rt->numStencilSamples());
 
     if (this->caps()->sampleLocationsSupport()) {
         samplePattern->reset(*effectiveSampleCnt);
@@ -4468,12 +4251,16 @@
     return GrGLSemaphore::Make(this);
 }
 
-void GrGLGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore) {
+void GrGLGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) {
     GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore.get());
 
     GrGLsync sync;
     GL_CALL_RET(sync, FenceSync(GR_GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
     glSem->setSync(sync);
+
+    if (flush) {
+        GL_CALL(Flush());
+    }
 }
 
 void GrGLGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) {
@@ -4486,6 +4273,10 @@
     GL_CALL(DeleteSync(sync));
 }
 
-void GrGLGpu::flush() {
-    GL_CALL(Flush());
+sk_sp<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();
+    this->insertSemaphore(semaphore, true);
+
+    return semaphore;
 }
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 1fc2557..1785eae 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -17,6 +17,7 @@
 #include "GrGLTexture.h"
 #include "GrGLVertexArray.h"
 #include "GrGpu.h"
+#include "GrMesh.h"
 #include "GrTexturePriv.h"
 #include "GrWindowRectsState.h"
 #include "GrXferProcessor.h"
@@ -26,7 +27,6 @@
 
 class GrGLBuffer;
 class GrPipeline;
-class GrNonInstancedMesh;
 class GrSwizzle;
 
 namespace gr_instanced { class GLInstancedRendering; }
@@ -35,7 +35,7 @@
 #define PROGRAM_CACHE_STATS
 #endif
 
-class GrGLGpu final : public GrGpu {
+class GrGLGpu final : public GrGpu, private GrMesh::SendToGpuImpl {
 public:
     static GrGpu* Create(GrBackendContext backendContext, const GrContextOptions& options,
                          GrContext* context);
@@ -104,6 +104,28 @@
               const GrMesh*,
               int meshCount);
 
+
+    // GrMesh::SendToGpuImpl methods. These issue the actual GL draw calls.
+    // Marked final as a hint to the compiler to not use virtual dispatch.
+    void sendMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                       const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) final;
+
+    void sendIndexedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                              const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                              uint16_t minIndexValue, uint16_t maxIndexValue,
+                              const GrBuffer* vertexBuffer, int baseVertex) final;
+
+    void sendInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                const GrBuffer* vertexBuffer, int vertexCount, int baseVertex,
+                                const GrBuffer* instanceBuffer, int instanceCount,
+                                int baseInstance) final;
+
+    void sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                       const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                       const GrBuffer* vertexBuffer, int baseVertex,
+                                       const GrBuffer* instanceBuffer, int instanceCount,
+                                       int baseInstance) final;
+
     // The GrGLGpuCommandBuffer does not buffer up draws before submitting them to the gpu.
     // Thus this is the implementation of the clear call for the corresponding passthrough function
     // on GrGLGpuCommandBuffer.
@@ -140,19 +162,17 @@
 
     void resetShaderCacheForTesting() const override;
 
-    void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override;
-
     GrFence SK_WARN_UNUSED_RESULT insertFence() override;
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
     sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override;
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override;
+    void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override;
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
 
-    void deleteSync(GrGLsync) const;
+    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
-    void flush() override;
+    void deleteSync(GrGLsync) const;
 
 private:
     GrGLGpu(GrGLContext* ctx, GrContext* context);
@@ -162,18 +182,24 @@
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override;
 
-    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                               const SkTArray<GrMipLevel>& texels) override;
-    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc,
-                                         SkBudgeted budgeted,
-                                         const SkTArray<GrMipLevel>& texels) override;
+    sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                     const SkTArray<GrMipLevel>& texels) override;
 
     GrBuffer* onCreateBuffer(size_t size, GrBufferType intendedType, GrAccessPattern,
                              const void* data) override;
-    sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) override;
-    sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) override;
-    sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&) override;
 
+    sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
+                                          GrSurfaceOrigin,
+                                          GrBackendTextureFlags,
+                                          int sampleCnt,
+                                          GrWrapOwnership) override;
+    sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTarget&,
+                                                    GrSurfaceOrigin origin) override;
+    sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTexture&,
+                                                             GrSurfaceOrigin,
+                                                             int sampleCnt) override;
+
+    std::unique_ptr<gr_instanced::OpAllocator> onCreateInstancedRenderingAllocator() override;
     gr_instanced::InstancedRendering* onCreateInstancedRendering() override;
 
     // Given a GrPixelConfig return the index into the stencil format array on GrGLCaps to a
@@ -243,12 +269,13 @@
     // willDrawPoints must be true if point primitives will be rendered after setting the GL state.
     bool flushGLState(const GrPipeline&, const GrPrimitiveProcessor&, bool willDrawPoints);
 
-    // Sets up vertex attribute pointers and strides. On return indexOffsetInBytes gives the offset
-    // an into the index buffer. It does not account for vertices.startIndex() but rather the start
-    // index is relative to the returned offset.
+    // Sets up vertex/instance attribute pointers and strides.
     void setupGeometry(const GrPrimitiveProcessor&,
-                       const GrNonInstancedMesh& mesh,
-                       size_t* indexOffsetInBytes);
+                       const GrBuffer* indexBuffer,
+                       const GrBuffer* vertexBuffer,
+                       int baseVertex,
+                       const GrBuffer* instanceBuffer,
+                       int baseInstance);
 
     void flushBlend(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle&);
 
@@ -267,6 +294,7 @@
                                       const SkIRect& srcRect,
                                       const SkIPoint& dstPoint);
     bool generateMipmap(GrGLTexture* texture, bool gammaCorrect);
+    void clearStencilClipAsDraw(const GrFixedClip&, bool insideStencilMask, GrRenderTarget*);
 
     static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
@@ -307,7 +335,6 @@
     };
 
     void flushColorWrite(bool writeColor);
-    void flushDrawFace(GrDrawFace face);
 
     // flushes the scissor. see the note on flushBoundTextureAndParams about
     // flushing the scissor after that function is called.
@@ -321,11 +348,6 @@
     void flushWindowRectangles(const GrWindowRectsState&, const GrGLRenderTarget*);
     void disableWindowRectangles();
 
-    void initFSAASupport();
-
-    // determines valid stencil formats
-    void initStencilFormats();
-
     // sets a texture unit to use for texture operations other than binding a texture to a program.
     // ensures that such operations don't negatively interact with tracking bound textures.
     void setScratchTextureUnit();
@@ -353,26 +375,11 @@
         kWrite_UploadType,         // we are using TexSubImage2D to copy data to an existing texture
         kTransfer_UploadType,      // we are using a transfer buffer to copy data
     };
-    bool uploadTexData(const GrSurfaceDesc& desc,
-                       GrGLenum target,
-                       UploadType uploadType,
-                       int left, int top, int width, int height,
-                       GrPixelConfig dataConfig,
+    bool uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight,
+                       GrSurfaceOrigin texOrigin, GrGLenum target, UploadType uploadType, int left,
+                       int top, int width, int height, GrPixelConfig dataConfig,
                        const SkTArray<GrMipLevel>& texels);
 
-    // helper for onCreateCompressedTexture. If width and height are
-    // set to -1, then this function will use desc.fWidth and desc.fHeight
-    // for the size of the data. The isNewTexture flag should be set to true
-    // whenever a new texture needs to be created. Otherwise, we assume that
-    // the texture is already in GPU memory and that it's going to be updated
-    // with new data.
-    bool uploadCompressedTexData(const GrSurfaceDesc& desc,
-                                 GrGLenum target,
-                                 const SkTArray<GrMipLevel>& texels,
-                                 UploadType uploadType = kNewTexture_UploadType,
-                                 int left = 0, int top = 0,
-                                 int width = -1, int height = -1);
-
     bool createRenderTargetObjects(const GrSurfaceDesc&, const GrGLTextureInfo& texInfo,
                                    GrGLRenderTarget::IDDesc*);
 
@@ -381,9 +388,9 @@
         kDst_TempFBOTarget
     };
 
-    // Binds a surface as a FBO for copying or reading. If the surface already owns an FBO ID then
-    // that ID is bound. If not the surface is temporarily bound to a FBO and that FBO is bound.
-    // This must be paired with a call to unbindSurfaceFBOForPixelOps().
+    // Binds a surface as a FBO for copying, reading, or clearing. If the surface already owns an
+    // FBO ID then that ID is bound. If not the surface is temporarily bound to a FBO and that FBO
+    // is bound. This must be paired with a call to unbindSurfaceFBOForPixelOps().
     void bindSurfaceFBOForPixelOps(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport,
                                    TempFBOTarget tempFBOTarget);
 
@@ -394,7 +401,7 @@
 
     bool createCopyProgram(GrTexture* srcTexture);
     bool createMipmapProgram(int progIdx);
-    bool createWireRectProgram();
+    bool createStencilClipClearProgram();
 
     // GL program-related state
     ProgramCache*               fProgramCache;
@@ -559,7 +566,6 @@
     TriState                                fHWStencilTestEnabled;
 
 
-    GrDrawFace                              fHWDrawFace;
     TriState                                fHWWriteToColor;
     GrGpuResource::UniqueID                 fHWBoundRenderTargetUniqueID;
     TriState                                fHWSRGBFramebuffer;
@@ -608,12 +614,8 @@
     }                                       fMipmapPrograms[4];
     sk_sp<GrGLBuffer>                       fMipmapProgramArrayBuffer;
 
-    struct {
-        GrGLuint    fProgram;
-        GrGLint     fColorUniform;
-        GrGLint     fRectUniform;
-    }                                       fWireRectProgram;
-    sk_sp<GrGLBuffer>                       fWireRectArrayBuffer;
+    GrGLuint                                fStencilClipClearProgram;
+    sk_sp<GrGLBuffer>                       fStencilClipClearArrayBuffer;
 
     static int TextureToCopyProgramIdx(GrTexture* texture) {
         switch (texture->texturePriv().samplerType()) {
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index a62d353..ee67b21 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -79,6 +79,8 @@
         nullptr == fFunctions.fClearStencil ||
         nullptr == fFunctions.fColorMask ||
         nullptr == fFunctions.fCompileShader ||
+        nullptr == fFunctions.fCompressedTexImage2D ||
+        nullptr == fFunctions.fCompressedTexSubImage2D ||
         nullptr == fFunctions.fCopyTexSubImage2D ||
         nullptr == fFunctions.fCreateProgram ||
         nullptr == fFunctions.fCreateShader ||
@@ -107,9 +109,7 @@
         nullptr == fFunctions.fGetShaderiv ||
         nullptr == fFunctions.fGetString ||
         nullptr == fFunctions.fGetUniformLocation ||
-#if 0 //  Not included in Chrome yet
         nullptr == fFunctions.fIsTexture ||
-#endif
         nullptr == fFunctions.fLinkProgram ||
         nullptr == fFunctions.fLineWidth ||
         nullptr == fFunctions.fPixelStorei ||
@@ -117,8 +117,11 @@
         nullptr == fFunctions.fScissor ||
         nullptr == fFunctions.fShaderSource ||
         nullptr == fFunctions.fStencilFunc ||
+        nullptr == fFunctions.fStencilFuncSeparate ||
         nullptr == fFunctions.fStencilMask ||
+        nullptr == fFunctions.fStencilMaskSeparate ||
         nullptr == fFunctions.fStencilOp ||
+        nullptr == fFunctions.fStencilOpSeparate ||
         nullptr == fFunctions.fTexImage2D ||
         nullptr == fFunctions.fTexParameteri ||
         nullptr == fFunctions.fTexParameteriv ||
@@ -178,41 +181,11 @@
     // these functions are part of ES2, we assume they are available
     // On the desktop we assume they are available if the extension
     // is present or GL version is high enough.
-    if (kGLES_GrGLStandard == fStandard) {
-        if (nullptr == fFunctions.fStencilFuncSeparate ||
-            nullptr == fFunctions.fStencilMaskSeparate ||
-            nullptr == fFunctions.fStencilOpSeparate) {
-            RETURN_FALSE_INTERFACE
-        }
-    } else if (kGL_GrGLStandard == fStandard) {
-
-        if (glVer >= GR_GL_VER(2,0)) {
-            if (nullptr == fFunctions.fStencilFuncSeparate ||
-                nullptr == fFunctions.fStencilMaskSeparate ||
-                nullptr == fFunctions.fStencilOpSeparate) {
-                RETURN_FALSE_INTERFACE
-            }
-        }
+    if (kGL_GrGLStandard == fStandard) {
         if (glVer >= GR_GL_VER(3,0) && nullptr == fFunctions.fBindFragDataLocation) {
             RETURN_FALSE_INTERFACE
         }
-        if (glVer >= GR_GL_VER(2,0) || fExtensions.has("GL_ARB_draw_buffers")) {
-            if (nullptr == fFunctions.fDrawBuffers) {
-                RETURN_FALSE_INTERFACE
-            }
-        }
 
-        if (glVer >= GR_GL_VER(1,5) || fExtensions.has("GL_ARB_occlusion_query")) {
-            if (nullptr == fFunctions.fGenQueries ||
-                nullptr == fFunctions.fDeleteQueries ||
-                nullptr == fFunctions.fBeginQuery ||
-                nullptr == fFunctions.fEndQuery ||
-                nullptr == fFunctions.fGetQueryiv ||
-                nullptr == fFunctions.fGetQueryObjectiv ||
-                nullptr == fFunctions.fGetQueryObjectuiv) {
-                RETURN_FALSE_INTERFACE
-            }
-        }
         if (glVer >= GR_GL_VER(3,3) ||
             fExtensions.has("GL_ARB_timer_query") ||
             fExtensions.has("GL_EXT_timer_query")) {
@@ -228,25 +201,31 @@
         }
     }
 
-    // optional function on desktop before 1.3
-    if (kGL_GrGLStandard != fStandard ||
-        (glVer >= GR_GL_VER(1,3)) ||
-        fExtensions.has("GL_ARB_texture_compression")) {
-        if (nullptr == fFunctions.fCompressedTexImage2D
-#if 0
-            || nullptr == fFunctions.fCompressedTexSubImage2D
-#endif
-            ) {
+    // part of desktop GL, but not ES
+    if (kGL_GrGLStandard == fStandard &&
+        (nullptr == fFunctions.fDrawBuffer ||
+         nullptr == fFunctions.fPolygonMode)) {
+        RETURN_FALSE_INTERFACE
+    }
+
+    // ES 3.0 (or ES 2.0 extended) has glDrawBuffers but not glDrawBuffer
+    if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0)) {
+        if (nullptr == fFunctions.fDrawBuffers) {
             RETURN_FALSE_INTERFACE
         }
     }
 
-    // part of desktop GL, but not ES
-    if (kGL_GrGLStandard == fStandard &&
-        (nullptr == fFunctions.fGetTexLevelParameteriv ||
-         nullptr == fFunctions.fDrawBuffer ||
-         nullptr == fFunctions.fReadBuffer)) {
-        RETURN_FALSE_INTERFACE
+    if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0)) {
+        if (nullptr == fFunctions.fReadBuffer) {
+            RETURN_FALSE_INTERFACE
+        }
+    }
+
+    // glGetTexLevelParameteriv was added to ES in 3.1.
+    if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,1)) {
+        if (nullptr == fFunctions.fGetTexLevelParameteriv) {
+            RETURN_FALSE_INTERFACE
+        }
     }
 
     // GL_EXT_texture_storage is part of desktop 4.2
@@ -293,6 +272,25 @@
         }
     }
 
+    // Required since OpenGL 1.5 and ES 3.0 or with GL_EXT_occlusion_query_boolean
+    if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0) ||
+        fExtensions.has("GL_EXT_occlusion_query_boolean")) {
+#if 0 // Not yet added to chrome's bindings.
+        if (nullptr == fFunctions.fGenQueries ||
+            nullptr == fFunctions.fDeleteQueries ||
+            nullptr == fFunctions.fBeginQuery ||
+            nullptr == fFunctions.fEndQuery ||
+            nullptr == fFunctions.fGetQueryiv ||
+            nullptr == fFunctions.fGetQueryObjectuiv) {
+            RETURN_FALSE_INTERFACE
+        }
+#endif
+    }
+    // glGetQueryObjectiv doesn't exist in ES.
+    if (kGL_GrGLStandard == fStandard && nullptr == fFunctions.fGetQueryObjectiv) {
+        RETURN_FALSE_INTERFACE
+    }
+
     // FBO MSAA
     if (kGL_GrGLStandard == fStandard) {
         // GL 3.0 and the ARB extension have multisample + blit
@@ -622,11 +620,37 @@
             nullptr == fFunctions.fCopyTextureImage2D ||
             nullptr == fFunctions.fCopyTextureSubImage1D ||
             nullptr == fFunctions.fCopyTextureSubImage2D ||
+            nullptr == fFunctions.fGetNamedBufferParameteriv ||
+            nullptr == fFunctions.fGetNamedBufferPointerv ||
+            nullptr == fFunctions.fGetNamedBufferSubData ||
             nullptr == fFunctions.fGetTextureImage ||
             nullptr == fFunctions.fGetTextureParameterfv ||
             nullptr == fFunctions.fGetTextureParameteriv ||
             nullptr == fFunctions.fGetTextureLevelParameterfv ||
-            nullptr == fFunctions.fGetTextureLevelParameteriv) {
+            nullptr == fFunctions.fGetTextureLevelParameteriv ||
+            nullptr == fFunctions.fMapNamedBuffer ||
+            nullptr == fFunctions.fNamedBufferData ||
+            nullptr == fFunctions.fNamedBufferSubData ||
+            nullptr == fFunctions.fProgramUniform1f ||
+            nullptr == fFunctions.fProgramUniform2f ||
+            nullptr == fFunctions.fProgramUniform3f ||
+            nullptr == fFunctions.fProgramUniform4f ||
+            nullptr == fFunctions.fProgramUniform1i ||
+            nullptr == fFunctions.fProgramUniform2i ||
+            nullptr == fFunctions.fProgramUniform3i ||
+            nullptr == fFunctions.fProgramUniform4i ||
+            nullptr == fFunctions.fProgramUniform1fv ||
+            nullptr == fFunctions.fProgramUniform2fv ||
+            nullptr == fFunctions.fProgramUniform3fv ||
+            nullptr == fFunctions.fProgramUniform4fv ||
+            nullptr == fFunctions.fProgramUniform1iv ||
+            nullptr == fFunctions.fProgramUniform2iv ||
+            nullptr == fFunctions.fProgramUniform3iv ||
+            nullptr == fFunctions.fProgramUniform4iv ||
+            nullptr == fFunctions.fProgramUniformMatrix2fv ||
+            nullptr == fFunctions.fProgramUniformMatrix3fv ||
+            nullptr == fFunctions.fProgramUniformMatrix4fv ||
+            nullptr == fFunctions.fUnmapNamedBuffer) {
             RETURN_FALSE_INTERFACE
         }
         if (glVer >= GR_GL_VER(1,2)) {
@@ -643,40 +667,6 @@
                 RETURN_FALSE_INTERFACE
             }
         }
-        if (glVer >= GR_GL_VER(1,5)) {
-            if (nullptr == fFunctions.fNamedBufferData ||
-                nullptr == fFunctions.fNamedBufferSubData ||
-                nullptr == fFunctions.fMapNamedBuffer ||
-                nullptr == fFunctions.fUnmapNamedBuffer ||
-                nullptr == fFunctions.fGetNamedBufferParameteriv ||
-                nullptr == fFunctions.fGetNamedBufferPointerv ||
-                nullptr == fFunctions.fGetNamedBufferSubData) {
-                RETURN_FALSE_INTERFACE
-            }
-        }
-        if (glVer >= GR_GL_VER(2,0)) {
-            if (nullptr == fFunctions.fProgramUniform1f ||
-                nullptr == fFunctions.fProgramUniform2f ||
-                nullptr == fFunctions.fProgramUniform3f ||
-                nullptr == fFunctions.fProgramUniform4f ||
-                nullptr == fFunctions.fProgramUniform1i ||
-                nullptr == fFunctions.fProgramUniform2i ||
-                nullptr == fFunctions.fProgramUniform3i ||
-                nullptr == fFunctions.fProgramUniform4i ||
-                nullptr == fFunctions.fProgramUniform1fv ||
-                nullptr == fFunctions.fProgramUniform2fv ||
-                nullptr == fFunctions.fProgramUniform3fv ||
-                nullptr == fFunctions.fProgramUniform4fv ||
-                nullptr == fFunctions.fProgramUniform1iv ||
-                nullptr == fFunctions.fProgramUniform2iv ||
-                nullptr == fFunctions.fProgramUniform3iv ||
-                nullptr == fFunctions.fProgramUniform4iv ||
-                nullptr == fFunctions.fProgramUniformMatrix2fv ||
-                nullptr == fFunctions.fProgramUniformMatrix3fv ||
-                nullptr == fFunctions.fProgramUniformMatrix4fv) {
-                RETURN_FALSE_INTERFACE
-            }
-        }
         if (glVer >= GR_GL_VER(2,1)) {
             if (nullptr == fFunctions.fProgramUniformMatrix2x3fv ||
                 nullptr == fFunctions.fProgramUniformMatrix3x2fv ||
@@ -791,11 +781,8 @@
         }
     }
 
-    if (kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(2,0)) {
-        if (nullptr == fFunctions.fDrawRangeElements) {
-            RETURN_FALSE_INTERFACE;
-        }
-    } else if (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0)) {
+    // glDrawRangeElements was added to ES in 3.0.
+    if (kGL_GrGLStandard == fStandard || glVer >= GR_GL_VER(3,0)) {
         if (nullptr == fFunctions.fDrawRangeElements) {
             RETURN_FALSE_INTERFACE;
         }
diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp
index cbf2041..a0845e3 100644
--- a/src/gpu/gl/GrGLPathRendering.cpp
+++ b/src/gpu/gl/GrGLPathRendering.cpp
@@ -106,20 +106,19 @@
     fHWPathStencilSettings.invalidate();
 }
 
-GrPath* GrGLPathRendering::createPath(const SkPath& inPath, const GrStyle& style) {
-    return new GrGLPath(this->gpu(), inPath, style);
+sk_sp<GrPath> GrGLPathRendering::createPath(const SkPath& inPath, const GrStyle& style) {
+    return sk_make_sp<GrGLPath>(this->gpu(), inPath, style);
 }
 
-GrPathRange* GrGLPathRendering::createPathRange(GrPathRange::PathGenerator* pathGenerator,
-                                                const GrStyle& style) {
-    return new GrGLPathRange(this->gpu(), pathGenerator, style);
+sk_sp<GrPathRange> GrGLPathRendering::createPathRange(GrPathRange::PathGenerator* pathGenerator,
+                                                      const GrStyle& style) {
+    return sk_make_sp<GrGLPathRange>(this->gpu(), pathGenerator, style);
 }
 
 void GrGLPathRendering::onStencilPath(const StencilPathArgs& args, const GrPath* path) {
     GrGLGpu* gpu = this->gpu();
     SkASSERT(gpu->caps()->shaderCaps()->pathRenderingSupport());
     gpu->flushColorWrite(false);
-    gpu->flushDrawFace(GrDrawFace::kBoth);
 
     GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(args.fRenderTarget);
     SkISize size = SkISize::Make(rt->width(), rt->height());
diff --git a/src/gpu/gl/GrGLPathRendering.h b/src/gpu/gl/GrGLPathRendering.h
index 8c3cd47..ddbd75c 100644
--- a/src/gpu/gl/GrGLPathRendering.h
+++ b/src/gpu/gl/GrGLPathRendering.h
@@ -34,9 +34,9 @@
     ~GrGLPathRendering() override;
 
     // GrPathRendering implementations.
-    GrPath* createPath(const SkPath&, const GrStyle&) override;
-    virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*,
-                                         const GrStyle&) override;
+    sk_sp<GrPath> createPath(const SkPath&, const GrStyle&) override;
+    virtual sk_sp<GrPathRange> createPathRange(GrPathRange::PathGenerator*,
+                                               const GrStyle&) override;
 
     /* Called when the 3D context state is unknown. */
     void resetContext();
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index d9e73f0..f133c1b 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -30,7 +30,8 @@
                          const BuiltinUniformHandles& builtinUniforms,
                          GrGLuint programID,
                          const UniformInfoArray& uniforms,
-                         const UniformInfoArray& samplers,
+                         const UniformInfoArray& textureSamplers,
+                         const UniformInfoArray& texelBuffers,
                          const UniformInfoArray& imageStorages,
                          const VaryingInfoArray& pathProcVaryings,
                          GrGLSLPrimitiveProcessor* geometryProcessor,
@@ -43,10 +44,13 @@
     , fFragmentProcessors(fragmentProcessors)
     , fDesc(desc)
     , fGpu(gpu)
-    , fProgramDataManager(gpu, programID, uniforms, pathProcVaryings) {
+    , fProgramDataManager(gpu, programID, uniforms, pathProcVaryings)
+    , fNumTextureSamplers(textureSamplers.count())
+    , fNumTexelBuffers(texelBuffers.count()) {
     // Assign texture units to sampler uniforms one time up front.
     GL_CALL(UseProgram(fProgramID));
-    fProgramDataManager.setSamplers(samplers);
+    fProgramDataManager.setSamplerUniforms(textureSamplers, 0);
+    fProgramDataManager.setSamplerUniforms(texelBuffers, fNumTextureSamplers);
     fProgramDataManager.setImageStorages(imageStorages);
 }
 
@@ -66,20 +70,37 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline) {
-    this->setRenderTargetState(primProc, pipeline);
+    this->setRenderTargetState(primProc, pipeline.getRenderTarget());
 
     // we set the textures, and uniforms for installed processors in a generic way, but subclasses
     // of GLProgram determine how to set coord transforms
-    int nextSamplerIdx = 0;
+
+    // We must bind to texture units in the same order in which we set the uniforms in
+    // GrGLProgramDataManager. That is first all texture samplers and then texel buffers.
+    // ImageStorages are bound to their own image units so they are tracked separately.
+    // Within each group we will bind them in primProc, fragProcs, XP order.
+    int nextTexSamplerIdx = 0;
+    int nextTexelBufferIdx = fNumTextureSamplers;
+    int nextImageStorageIdx = 0;
     fGeometryProcessor->setData(fProgramDataManager, primProc,
                                 GrFragmentProcessor::CoordTransformIter(pipeline));
-    this->bindTextures(primProc, pipeline.getAllowSRGBInputs(), &nextSamplerIdx);
+    this->bindTextures(primProc, pipeline.getAllowSRGBInputs(), &nextTexSamplerIdx,
+                       &nextTexelBufferIdx, &nextImageStorageIdx);
 
-    this->setFragmentData(primProc, pipeline, &nextSamplerIdx);
+    this->setFragmentData(primProc, pipeline, &nextTexSamplerIdx, &nextTexelBufferIdx,
+                          &nextImageStorageIdx);
 
     const GrXferProcessor& xp = pipeline.getXferProcessor();
-    fXferProcessor->setData(fProgramDataManager, xp);
-    this->bindTextures(xp, pipeline.getAllowSRGBInputs(), &nextSamplerIdx);
+    SkIPoint offset;
+    GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
+
+    fXferProcessor->setData(fProgramDataManager, xp, dstTexture, offset);
+    if (dstTexture) {
+        fGpu->bindTexture(nextTexSamplerIdx++, GrSamplerParams::ClampNoFilter(), true,
+                          static_cast<GrGLTexture*>(dstTexture));
+    }
+    SkASSERT(nextTexSamplerIdx == fNumTextureSamplers);
+    SkASSERT(nextTexelBufferIdx == fNumTextureSamplers + fNumTexelBuffers);
 }
 
 void GrGLProgram::generateMipmaps(const GrPrimitiveProcessor& primProc,
@@ -94,7 +115,9 @@
 
 void GrGLProgram::setFragmentData(const GrPrimitiveProcessor& primProc,
                                   const GrPipeline& pipeline,
-                                  int* nextSamplerIdx) {
+                                  int* nextTexSamplerIdx,
+                                  int* nextTexelBufferIdx,
+                                  int* nextImageStorageIdx) {
     GrFragmentProcessor::Iter iter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.begin(),
                                            fFragmentProcessors.count());
@@ -102,7 +125,8 @@
     GrGLSLFragmentProcessor* glslFP = glslIter.next();
     while (fp && glslFP) {
         glslFP->setData(fProgramDataManager, *fp);
-        this->bindTextures(*fp, pipeline.getAllowSRGBInputs(), nextSamplerIdx);
+        this->bindTextures(*fp, pipeline.getAllowSRGBInputs(), nextTexSamplerIdx,
+                           nextTexelBufferIdx, nextImageStorageIdx);
         fp = iter.next();
         glslFP = glslIter.next();
     }
@@ -111,16 +135,14 @@
 
 
 void GrGLProgram::setRenderTargetState(const GrPrimitiveProcessor& primProc,
-                                       const GrPipeline& pipeline) {
+                                       const GrRenderTarget* rt) {
     // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
     if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
-        fRenderTargetState.fRenderTargetSize.fHeight != pipeline.getRenderTarget()->height()) {
-        fProgramDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni,
-                                   SkIntToScalar(pipeline.getRenderTarget()->height()));
+        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
+        fProgramDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
     }
 
     // set RT adjustment
-    const GrRenderTarget* rt = pipeline.getRenderTarget();
     SkISize size;
     size.set(rt->width(), rt->height());
     if (!primProc.isPathRendering()) {
@@ -141,31 +163,32 @@
     }
 }
 
-void GrGLProgram::bindTextures(const GrProcessor& processor,
+void GrGLProgram::bindTextures(const GrResourceIOProcessor& processor,
                                bool allowSRGBInputs,
-                               int* nextSamplerIdx) {
+                               int* nextTexSamplerIdx,
+                               int* nextTexelBufferIdx,
+                               int* nextImageStorageIdx) {
     for (int i = 0; i < processor.numTextureSamplers(); ++i) {
-        const GrProcessor::TextureSampler& sampler = processor.textureSampler(i);
-        fGpu->bindTexture((*nextSamplerIdx)++, sampler.params(),
-                          allowSRGBInputs, static_cast<GrGLTexture*>(sampler.texture()));
+        const GrResourceIOProcessor::TextureSampler& sampler = processor.textureSampler(i);
+        fGpu->bindTexture((*nextTexSamplerIdx)++, sampler.params(),
+                          allowSRGBInputs, static_cast<GrGLTexture*>(sampler.peekTexture()));
     }
     for (int i = 0; i < processor.numBuffers(); ++i) {
-        const GrProcessor::BufferAccess& access = processor.bufferAccess(i);
-        fGpu->bindTexelBuffer((*nextSamplerIdx)++, access.texelConfig(),
+        const GrResourceIOProcessor::BufferAccess& access = processor.bufferAccess(i);
+        fGpu->bindTexelBuffer((*nextTexelBufferIdx)++, access.texelConfig(),
                               static_cast<GrGLBuffer*>(access.buffer()));
     }
     for (int i = 0; i < processor.numImageStorages(); ++i) {
-        const GrProcessor::ImageStorageAccess& access = processor.imageStorageAccess(i);
-        fGpu->bindImageStorage((*nextSamplerIdx)++, access.ioType(),
-                               static_cast<GrGLTexture *>(access.texture()));
+        const GrResourceIOProcessor::ImageStorageAccess& access = processor.imageStorageAccess(i);
+        fGpu->bindImageStorage((*nextImageStorageIdx)++, access.ioType(),
+                               static_cast<GrGLTexture *>(access.peekTexture()));
     }
 }
 
-void GrGLProgram::generateMipmaps(const GrProcessor& processor,
-                                  bool allowSRGBInputs) {
+void GrGLProgram::generateMipmaps(const GrResourceIOProcessor& processor, bool allowSRGBInputs) {
     for (int i = 0; i < processor.numTextureSamplers(); ++i) {
-        const GrProcessor::TextureSampler& sampler = processor.textureSampler(i);
+        const GrResourceIOProcessor::TextureSampler& sampler = processor.textureSampler(i);
         fGpu->generateMipmaps(sampler.params(), allowSRGBInputs,
-                              static_cast<GrGLTexture*>(sampler.texture()));
+                              static_cast<GrGLTexture*>(sampler.peekTexture()));
     }
 }
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 9c42978..d6bde9a 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -111,7 +111,8 @@
                 const BuiltinUniformHandles&,
                 GrGLuint programID,
                 const UniformInfoArray& uniforms,
-                const UniformInfoArray& samplers,
+                const UniformInfoArray& textureSamplers,
+                const UniformInfoArray& texelBuffers,
                 const UniformInfoArray& imageStorages,
                 const VaryingInfoArray&, // used for NVPR only currently
                 GrGLSLPrimitiveProcessor* geometryProcessor,
@@ -119,16 +120,18 @@
                 const GrGLSLFragProcs& fragmentProcessors);
 
     // A helper to loop over effects, set the transforms (via subclass) and bind textures
-    void setFragmentData(const GrPrimitiveProcessor&, const GrPipeline&, int* nextSamplerIdx);
+    void setFragmentData(const GrPrimitiveProcessor&, const GrPipeline&, int* nextTexSamplerIdx,
+                         int* nextTexelBufferIdx, int* nextImageStorageIdx);
 
     // Helper for setData() that sets the view matrix and loads the render target height uniform
-    void setRenderTargetState(const GrPrimitiveProcessor&, const GrPipeline&);
+    void setRenderTargetState(const GrPrimitiveProcessor&, const GrRenderTarget*);
 
     // Helper for setData() that binds textures and texel buffers to the appropriate texture units
-    void bindTextures(const GrProcessor&, bool allowSRGBInputs, int* nextSamplerIdx);
+    void bindTextures(const GrResourceIOProcessor&, bool allowSRGBInputs, int* nextSamplerIdx,
+                      int* nextTexelBufferIdx, int* nextImageStorageIdx);
 
     // Helper for generateMipmaps() that ensures mipmaps are up to date
-    void generateMipmaps(const GrProcessor&, bool allowSRGBInputs);
+    void generateMipmaps(const GrResourceIOProcessor&, bool allowSRGBInputs);
 
     // these reflect the current values of uniforms (GL uniform values travel with program)
     RenderTargetState fRenderTargetState;
@@ -144,6 +147,10 @@
     GrGLGpu* fGpu;
     GrGLProgramDataManager fProgramDataManager;
 
+    int fNumTextureSamplers;
+    int fNumTexelBuffers;
+    int fNumImageStorages;
+
     friend class GrGLProgramBuilder;
 
     typedef SkRefCnt INHERITED;
diff --git a/src/gpu/gl/GrGLProgramDataManager.cpp b/src/gpu/gl/GrGLProgramDataManager.cpp
index 2a3422e3..3414aba 100644
--- a/src/gpu/gl/GrGLProgramDataManager.cpp
+++ b/src/gpu/gl/GrGLProgramDataManager.cpp
@@ -50,12 +50,13 @@
     }
 }
 
-void GrGLProgramDataManager::setSamplers(const UniformInfoArray& samplers) const {
+void GrGLProgramDataManager::setSamplerUniforms(const UniformInfoArray& samplers,
+                                                int startUnit) const {
     for (int i = 0; i < samplers.count(); ++i) {
         const UniformInfo& sampler = samplers[i];
         SkASSERT(sampler.fVisibility);
         if (kUnusedUniform != sampler.fLocation) {
-            GR_GL_CALL(fGpu->glInterface(), Uniform1i(sampler.fLocation, i));
+            GR_GL_CALL(fGpu->glInterface(), Uniform1i(sampler.fLocation, i + startUnit));
         }
     }
 }
diff --git a/src/gpu/gl/GrGLProgramDataManager.h b/src/gpu/gl/GrGLProgramDataManager.h
index 42e0656..6b4e4d9 100644
--- a/src/gpu/gl/GrGLProgramDataManager.h
+++ b/src/gpu/gl/GrGLProgramDataManager.h
@@ -45,9 +45,8 @@
     GrGLProgramDataManager(GrGLGpu*, GrGLuint programID, const UniformInfoArray&,
                            const VaryingInfoArray&);
 
-
-    void setSamplers(const UniformInfoArray& samplers) const;
-    void setImageStorages(const UniformInfoArray &images) const;
+    void setSamplerUniforms(const UniformInfoArray& samplers, int startUnit) const;
+    void setImageStorages(const UniformInfoArray& images) const;
 
     /** Functions for uploading uniform values. The varities ending in v can be used to upload to an
     *  array of uniforms. arrayCount must be <= the array count of the uniform.
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 45efba2..00a7840 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -7,6 +7,7 @@
 
 #include "GrGLRenderTarget.h"
 
+#include "GrContext.h"
 #include "GrGLGpu.h"
 #include "GrGLUtil.h"
 #include "GrGpuResourcePriv.h"
@@ -82,7 +83,8 @@
 }
 
 size_t GrGLRenderTarget::onGpuMemorySize() const {
-    return GrSurface::ComputeSize(fDesc, fNumSamplesOwnedPerPixel, false);
+    return GrSurface::ComputeSize(this->config(), this->width(), this->height(),
+                                  fNumSamplesOwnedPerPixel, false);
 }
 
 bool GrGLRenderTarget::completeStencilAttachment() {
@@ -170,6 +172,10 @@
 }
 
 bool GrGLRenderTarget::canAttemptStencilAttachment() const {
+    if (this->getGpu()->getContext()->caps()->avoidStencilBuffers()) {
+        return false;
+    }
+
     // Only modify the FBO's attachments if we have created the FBO. Public APIs do not currently
     // allow for borrowed FBO ownership, so we can safely assume that if an object is owned,
     // Skia created it.
@@ -183,7 +189,8 @@
     // Log any renderbuffer's contribution to memory. We only do this if we own the renderbuffer
     // (have a fMSColorRenderbufferID).
     if (fMSColorRenderbufferID) {
-        size_t size = GrSurface::ComputeSize(fDesc, this->msaaSamples(), false);
+        size_t size = GrSurface::ComputeSize(this->config(), this->width(), this->height(),
+                                             this->msaaSamples(), false);
 
         // Due to this resource having both a texture and a renderbuffer component, dump as
         // skia/gpu_resources/resource_#/renderbuffer
@@ -208,7 +215,7 @@
     if (fTexFBOID == kUnresolvableFBOID || fTexFBOID != fRTFBOID) {
         // If the render target's FBO is external (fTexFBOID == kUnresolvableFBOID), or if we own
         // the render target's FBO (fTexFBOID == fRTFBOID) then we use the provided sample count.
-        return SkTMax(1, fDesc.fSampleCnt);
+        return SkTMax(1, this->numStencilSamples());
     }
 
     // When fTexFBOID == fRTFBOID, we either are not using MSAA, or MSAA is auto resolving, so use
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
index e4efc94..795b41e 100644
--- a/src/gpu/gl/GrGLRenderTarget.h
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -48,9 +48,8 @@
 
     // override of GrRenderTarget
     ResolveType getResolveType() const override {
-        if (!this->isUnifiedMultisampled() ||
-            fRTFBOID == fTexFBOID) {
-            // catches FBO 0 and non MSAA case
+        if (GrFSAAType::kUnifiedMSAA != this->fsaaType() || fRTFBOID == fTexFBOID) {
+            // catches FBO 0 and non unified-MSAA case
             return kAutoResolves_ResolveType;
         } else if (kUnresolvableFBOID == fTexFBOID) {
             return kCantResolve_ResolveType;
diff --git a/src/gpu/gl/GrGLTestInterface.cpp b/src/gpu/gl/GrGLTestInterface.cpp
index 83fc663..25d66a9 100644
--- a/src/gpu/gl/GrGLTestInterface.cpp
+++ b/src/gpu/gl/GrGLTestInterface.cpp
@@ -119,6 +119,7 @@
     fFunctions.fMapTexSubImage2D = bind_to_member(this, &GrGLTestInterface::mapTexSubImage2D);
     fFunctions.fMinSampleShading = bind_to_member(this, &GrGLTestInterface::minSampleShading);
     fFunctions.fPixelStorei = bind_to_member(this, &GrGLTestInterface::pixelStorei);
+    fFunctions.fPolygonMode = bind_to_member(this, &GrGLTestInterface::polygonMode);
     fFunctions.fPopGroupMarker = bind_to_member(this, &GrGLTestInterface::popGroupMarker);
     fFunctions.fPushGroupMarker = bind_to_member(this, &GrGLTestInterface::pushGroupMarker);
     fFunctions.fQueryCounter = bind_to_member(this, &GrGLTestInterface::queryCounter);
diff --git a/src/gpu/gl/GrGLTestInterface.h b/src/gpu/gl/GrGLTestInterface.h
index 5918690..636f493 100644
--- a/src/gpu/gl/GrGLTestInterface.h
+++ b/src/gpu/gl/GrGLTestInterface.h
@@ -120,6 +120,7 @@
     virtual GrGLvoid* mapTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLenum access) { return nullptr; }
     virtual GrGLvoid minSampleShading(GrGLfloat value) {}
     virtual GrGLvoid pixelStorei(GrGLenum pname, GrGLint param) {}
+    virtual GrGLvoid polygonMode(GrGLenum face, GrGLenum mode) {}
     virtual GrGLvoid popGroupMarker() {}
     virtual GrGLvoid pushGroupMarker(GrGLsizei length, const char* marker) {}
     virtual GrGLvoid queryCounter(GrGLuint id, GrGLenum target) {}
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 2f75001..c93ff41 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -5,13 +5,11 @@
  * found in the LICENSE file.
  */
 
-#include "GrContext.h"
 #include "GrGLTexture.h"
 #include "GrGLGpu.h"
 #include "GrResourceProvider.h"
 #include "GrSemaphore.h"
 #include "GrShaderCaps.h"
-#include "SkMakeUnique.h"
 #include "SkTraceMemoryDump.h"
 
 #define GPUGL static_cast<GrGLGpu*>(this->getGpu())
@@ -35,6 +33,7 @@
     }
 }
 
+// This method parallels GrTextureProxy::highestFilterMode
 static inline GrSamplerParams::FilterMode highest_filter_mode(const GrGLTexture::IDDesc& idDesc,
                                                               GrPixelConfig config) {
     if (GrPixelConfigIsSint(config)) {
@@ -102,12 +101,14 @@
         }
         fInfo.fID = 0;
     }
+    this->invokeReleaseProc();
     INHERITED::onRelease();
 }
 
 void GrGLTexture::onAbandon() {
     fInfo.fTarget = 0;
     fInfo.fID = 0;
+    this->invokeReleaseProc();
     INHERITED::onAbandon();
 }
 
@@ -115,29 +116,6 @@
     return reinterpret_cast<GrBackendObject>(&fInfo);
 }
 
-std::unique_ptr<GrExternalTextureData> GrGLTexture::detachBackendTexture() {
-    // Flush any pending writes to this texture
-    this->getContext()->prepareSurfaceForExternalIO(this);
-
-    // Set up a semaphore to be signaled once the data is ready, and flush GL
-    sk_sp<GrSemaphore> semaphore = this->getContext()->resourceProvider()->makeSemaphore();
-    this->getGpu()->insertSemaphore(semaphore);
-    this->getGpu()->flush();
-
-    // Make a copy of our GL-specific information
-    auto data = skstd::make_unique<GrGLExternalTextureData>(fInfo, std::move(semaphore),
-                                                            this->getContext());
-
-    // Ensure the cache can't reach this texture anymore
-    this->detachFromCache();
-
-    // Detach from the GL object, so we don't use it (or try to delete it when we're freed)
-    fInfo.fTarget = 0;
-    fInfo.fID = 0;
-
-    return std::move(data);
-}
-
 void GrGLTexture::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
                                    const SkString& dumpName) const {
     SkString texture_id;
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index 16b47f1..7435891 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -36,10 +36,20 @@
     GrGLTexture(GrGLGpu*, SkBudgeted, const GrSurfaceDesc&, const IDDesc&,
                 bool wasMipMapDataProvided);
 
+    ~GrGLTexture() override {
+        // check that invokeReleaseProc has been called (if needed)
+        SkASSERT(!fReleaseProc);
+    }
+
     GrBackendObject getTextureHandle() const override;
 
     void textureParamsModified() override { fTexParams.invalidate(); }
 
+    void setRelease(ReleaseProc proc, ReleaseCtx ctx) override {
+        fReleaseProc = proc;
+        fReleaseCtx = ctx;
+    }
+
     // These functions are used to track the texture parameters associated with the texture.
     const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const {
         *timestamp = fTexParamsTimestamp;
@@ -71,9 +81,15 @@
     void onRelease() override;
     void setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
                           const SkString& dumpName) const override;
-    std::unique_ptr<GrExternalTextureData> detachBackendTexture() override;
 
 private:
+    void invokeReleaseProc() {
+        if (fReleaseProc) {
+            fReleaseProc(fReleaseCtx);
+            fReleaseProc = nullptr;
+        }
+    }
+
     TexParams                       fTexParams;
     GrGpu::ResetTimestamp           fTexParamsTimestamp;
     // Holds the texture target and ID. A pointer to this may be shared to external clients for
@@ -81,6 +97,9 @@
     GrGLTextureInfo                 fInfo;
     GrBackendObjectOwnership        fTextureIDOwnership;
 
+    ReleaseProc                     fReleaseProc = nullptr;
+    ReleaseCtx                      fReleaseCtx = nullptr;
+
     typedef GrTexture INHERITED;
 };
 
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.cpp b/src/gpu/gl/GrGLTextureRenderTarget.cpp
index 0abeb8e..eac29b6 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.cpp
+++ b/src/gpu/gl/GrGLTextureRenderTarget.cpp
@@ -7,6 +7,7 @@
 
 #include "GrGLTextureRenderTarget.h"
 
+#include "GrContext.h"
 #include "SkTraceMemoryDump.h"
 
 // GrGLTextureRenderTarget must dump both of its superclasses.
@@ -40,8 +41,8 @@
 
 bool GrGLTextureRenderTarget::canAttemptStencilAttachment() const {
     // The RT FBO of GrGLTextureRenderTarget is never created from a
-    // wrapped FBO.
-    return true;
+    // wrapped FBO, so we only care about the flag.
+    return !this->getGpu()->getContext()->caps()->avoidStencilBuffers();
 }
 
 sk_sp<GrGLTextureRenderTarget> GrGLTextureRenderTarget::MakeWrapped(
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.h b/src/gpu/gl/GrGLTextureRenderTarget.h
index 7ff8d49..0d3b19d 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.h
+++ b/src/gpu/gl/GrGLTextureRenderTarget.h
@@ -70,7 +70,7 @@
     }
 
     size_t onGpuMemorySize() const override {
-        return GrSurface::ComputeSize(fDesc,
+        return GrSurface::ComputeSize(this->config(), this->width(), this->height(),
                                       this->numSamplesOwnedPerPixel(),
                                       this->texturePriv().hasMipMaps());
     }
diff --git a/src/gpu/gl/GrGLUniformHandler.cpp b/src/gpu/gl/GrGLUniformHandler.cpp
index b00779b..4d718a0 100644
--- a/src/gpu/gl/GrGLUniformHandler.cpp
+++ b/src/gpu/gl/GrGLUniformHandler.cpp
@@ -76,6 +76,26 @@
     return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1);
 }
 
+GrGLSLUniformHandler::TexelBufferHandle GrGLUniformHandler::addTexelBuffer(uint32_t visibility,
+                                                                           GrSLPrecision precision,
+                                                                           const char* name) {
+    SkASSERT(name && strlen(name));
+    SkASSERT(0 != visibility);
+
+    SkString mangleName;
+    char prefix = 'u';
+    fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
+
+    UniformInfo& texelBuffer = fTexelBuffers.push_back();
+    texelBuffer.fVariable.setType(kBufferSampler_GrSLType);
+    texelBuffer.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
+    texelBuffer.fVariable.setPrecision(precision);
+    texelBuffer.fVariable.setName(mangleName);
+    texelBuffer.fLocation = -1;
+    texelBuffer.fVisibility = visibility;
+    return GrGLSLUniformHandler::TexelBufferHandle(fTexelBuffers.count() - 1);
+}
+
 GrGLSLUniformHandler::ImageStorageHandle GrGLUniformHandler::addImageStorage(
         uint32_t visibility, GrSLType type, GrImageStorageFormat format, GrSLMemoryModel model,
         GrSLRestrict restrict, GrIOType ioType, const char* name) {
@@ -114,6 +134,12 @@
             out->append(";\n");
         }
     }
+    for (int i = 0; i < fTexelBuffers.count(); ++i) {
+        if (fTexelBuffers[i].fVisibility & visibility) {
+            fTexelBuffers[i].fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
+            out->append(";\n");
+        }
+    }
     for (int i = 0; i < fImageStorages.count(); ++i) {
         if (fImageStorages[i].fVisibility & visibility) {
             fImageStorages[i].fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
@@ -133,7 +159,12 @@
             GL_CALL(BindUniformLocation(programID, currUniform, fSamplers[i].fVariable.c_str()));
             fSamplers[i].fLocation = currUniform;
         }
-        for (int i = 0; i < fImageStorages.count(); ++i) {
+        for (int i = 0; i < fTexelBuffers.count(); ++i, ++currUniform) {
+            GL_CALL(BindUniformLocation(programID, currUniform,
+                                        fTexelBuffers[i].fVariable.c_str()));
+            fTexelBuffers[i].fLocation = currUniform;
+        }
+        for (int i = 0; i < fImageStorages.count(); ++i, ++currUniform) {
             GL_CALL(BindUniformLocation(programID, currUniform,
                                         fImageStorages[i].fVariable.c_str()));
             fImageStorages[i].fLocation = currUniform;
@@ -154,6 +185,12 @@
             GL_CALL_RET(location, GetUniformLocation(programID, fSamplers[i].fVariable.c_str()));
             fSamplers[i].fLocation = location;
         }
+        for (int i = 0; i < fTexelBuffers.count(); ++i) {
+            GrGLint location;
+            GL_CALL_RET(location, GetUniformLocation(programID,
+                                                     fTexelBuffers[i].fVariable.c_str()));
+            fTexelBuffers[i].fLocation = location;
+        }
         for (int i = 0; i < fImageStorages.count(); ++i) {
             GrGLint location;
             GL_CALL_RET(location, GetUniformLocation(programID,
diff --git a/src/gpu/gl/GrGLUniformHandler.h b/src/gpu/gl/GrGLUniformHandler.h
index da7b13c..d029691 100644
--- a/src/gpu/gl/GrGLUniformHandler.h
+++ b/src/gpu/gl/GrGLUniformHandler.h
@@ -30,6 +30,7 @@
         : INHERITED(program)
         , fUniforms(kUniformsPerBlock)
         , fSamplers(kUniformsPerBlock)
+        , fTexelBuffers(kUniformsPerBlock)
         , fImageStorages(kUniformsPerBlock) {}
 
     UniformHandle internalAddUniformArray(uint32_t visibility,
@@ -47,14 +48,21 @@
         return fSamplers[handle.toIndex()].fVariable;
     }
 
-    ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType, GrImageStorageFormat,
-                                       GrSLMemoryModel, GrSLRestrict, GrIOType,
-                                       const char* name) override;
-
     GrSwizzle samplerSwizzle(SamplerHandle handle) const override {
         return fSamplerSwizzles[handle.toIndex()];
     }
 
+    TexelBufferHandle addTexelBuffer(uint32_t visibility, GrSLPrecision,
+                                     const char* name) override;
+
+    const GrShaderVar& texelBufferVariable(TexelBufferHandle handle) const override {
+        return fTexelBuffers[handle.toIndex()].fVariable;
+    }
+
+    ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType, GrImageStorageFormat,
+                                       GrSLMemoryModel, GrSLRestrict, GrIOType,
+                                       const char* name) override;
+
     const GrShaderVar& imageStorageVariable(ImageStorageHandle handle) const override {
         return fImageStorages[handle.toIndex()].fVariable;
     }
@@ -75,6 +83,7 @@
     UniformInfoArray    fUniforms;
     UniformInfoArray    fSamplers;
     SkTArray<GrSwizzle> fSamplerSwizzles;
+    UniformInfoArray    fTexelBuffers;
     UniformInfoArray    fImageStorages;
 
     friend class GrGLProgramBuilder;
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
index 443d77c..cf4615a 100644
--- a/src/gpu/gl/GrGLUtil.cpp
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -313,9 +313,27 @@
                 }
             }
         }
+        int intelNumber;
+        n = sscanf(rendererString, "Intel(R) Iris(TM) Graphics %d", &intelNumber);
+        if (1 != n) {
+            n = sscanf(rendererString, "Intel(R) HD Graphics %d", &intelNumber);
+        }
+        if (1 == n) {
+            if (intelNumber >= 6000 && intelNumber < 7000) {
+                return kIntel6xxx_GrGLRenderer;
+            }
+        }
         if (0 == strcmp("Mesa Offscreen", rendererString)) {
             return kOSMesa_GrGLRenderer;
         }
+        static const char kMaliTStr[] = "Mali-T";
+        if (0 == strncmp(rendererString, kMaliTStr, SK_ARRAY_COUNT(kMaliTStr) - 1)) {
+            return kMaliT_GrGLRenderer;
+        }
+        static const char kANGLEStr[] = "ANGLE";
+        if (0 == strncmp(rendererString, kANGLEStr, SK_ARRAY_COUNT(kANGLEStr) - 1)) {
+            return kANGLE_GrGLRenderer;
+        }
     }
     return kOther_GrGLRenderer;
 }
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
index 7503371..fc2594a 100644
--- a/src/gpu/gl/GrGLUtil.h
+++ b/src/gpu/gl/GrGLUtil.h
@@ -54,6 +54,11 @@
     kAdreno4xx_GrGLRenderer,
     kAdreno5xx_GrGLRenderer,
     kOSMesa_GrGLRenderer,
+    /** Either HD 6xxx or Iris 6xxx */
+    kIntel6xxx_GrGLRenderer,
+    /** T-6xx, T-7xx, or T-8xx */
+    kMaliT_GrGLRenderer,
+    kANGLE_GrGLRenderer,
     kOther_GrGLRenderer
 };
 
diff --git a/src/gpu/gl/GrGLVertexArray.cpp b/src/gpu/gl/GrGLVertexArray.cpp
index 807b9d0..59f2be9 100644
--- a/src/gpu/gl/GrGLVertexArray.cpp
+++ b/src/gpu/gl/GrGLVertexArray.cpp
@@ -53,27 +53,25 @@
                                const GrBuffer* vertexBuffer,
                                GrVertexAttribType type,
                                GrGLsizei stride,
-                               GrGLvoid* offset) {
+                               size_t offsetInBytes,
+                               int divisor) {
     SkASSERT(index >= 0 && index < fAttribArrayStates.count());
+    SkASSERT(0 == divisor || gpu->caps()->instanceAttribSupport());
     AttribArrayState* array = &fAttribArrayStates[index];
-    if (!array->fEnableIsValid || !array->fEnabled) {
-        GR_GL_CALL(gpu->glInterface(), EnableVertexAttribArray(index));
-        array->fEnableIsValid = true;
-        array->fEnabled = true;
-    }
     if (array->fVertexBufferUniqueID != vertexBuffer->uniqueID() ||
         array->fType != type ||
         array->fStride != stride ||
-        array->fOffset != offset) {
+        array->fOffset != offsetInBytes) {
         gpu->bindBuffer(kVertex_GrBufferType, vertexBuffer);
         const AttribLayout& layout = attrib_layout(type);
+        const GrGLvoid* offsetAsPtr = reinterpret_cast<const GrGLvoid*>(offsetInBytes);
         if (!GrVertexAttribTypeIsIntType(type)) {
             GR_GL_CALL(gpu->glInterface(), VertexAttribPointer(index,
                                                                layout.fCount,
                                                                layout.fType,
                                                                layout.fNormalized,
                                                                stride,
-                                                               offset));
+                                                               offsetAsPtr));
         } else {
             SkASSERT(gpu->caps()->shaderCaps()->integerSupport());
             SkASSERT(!layout.fNormalized);
@@ -81,30 +79,38 @@
                                                                 layout.fCount,
                                                                 layout.fType,
                                                                 stride,
-                                                                offset));
+                                                                offsetAsPtr));
         }
         array->fVertexBufferUniqueID = vertexBuffer->uniqueID();
         array->fType = type;
         array->fStride = stride;
-        array->fOffset = offset;
+        array->fOffset = offsetInBytes;
+    }
+    if (gpu->caps()->instanceAttribSupport() && array->fDivisor != divisor) {
+        SkASSERT(0 == divisor || 1 == divisor); // not necessarily a requirement but what we expect.
+        GR_GL_CALL(gpu->glInterface(), VertexAttribDivisor(index, divisor));
+        array->fDivisor = divisor;
     }
 }
 
-void GrGLAttribArrayState::disableUnusedArrays(const GrGLGpu* gpu, uint64_t usedMask) {
-    int count = fAttribArrayStates.count();
-    for (int i = 0; i < count; ++i) {
-        if (!(usedMask & 0x1)) {
-            if (!fAttribArrayStates[i].fEnableIsValid || fAttribArrayStates[i].fEnabled) {
-                GR_GL_CALL(gpu->glInterface(), DisableVertexAttribArray(i));
-                fAttribArrayStates[i].fEnableIsValid = true;
-                fAttribArrayStates[i].fEnabled = false;
-            }
-        } else {
-            SkASSERT(fAttribArrayStates[i].fEnableIsValid && fAttribArrayStates[i].fEnabled);
-        }
-        // if the count is greater than 64 then this will become 0 and we will disable arrays 64+.
-        usedMask >>= 1;
+void GrGLAttribArrayState::enableVertexArrays(const GrGLGpu* gpu, int enabledCount) {
+    SkASSERT(enabledCount <= fAttribArrayStates.count());
+    if (fEnabledCountIsValid && enabledCount == fNumEnabledArrays) {
+        return;
     }
+
+    int firstIdxToEnable = fEnabledCountIsValid ? fNumEnabledArrays : 0;
+    for (int i = firstIdxToEnable; i < enabledCount; ++i) {
+        GR_GL_CALL(gpu->glInterface(), EnableVertexAttribArray(i));
+    }
+
+    int endIdxToDisable = fEnabledCountIsValid ? fNumEnabledArrays : fAttribArrayStates.count();
+    for (int i = enabledCount; i < endIdxToDisable; ++i) {
+        GR_GL_CALL(gpu->glInterface(), DisableVertexAttribArray(i));
+    }
+
+    fNumEnabledArrays = enabledCount;
+    fEnabledCountIsValid = true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/gl/GrGLVertexArray.h b/src/gpu/gl/GrGLVertexArray.h
index 4c77d2b..553df4f 100644
--- a/src/gpu/gl/GrGLVertexArray.h
+++ b/src/gpu/gl/GrGLVertexArray.h
@@ -29,9 +29,7 @@
 
     void resize(int newCount) {
         fAttribArrayStates.resize_back(newCount);
-        for (int i = 0; i < newCount; ++i) {
-            fAttribArrayStates[i].invalidate();
-        }
+        this->invalidate();
     }
 
     /**
@@ -44,19 +42,20 @@
              const GrBuffer* vertexBuffer,
              GrVertexAttribType type,
              GrGLsizei stride,
-             GrGLvoid* offset);
+             size_t offsetInBytes,
+             int divisor = 0);
 
     /**
-     * This function disables vertex attribs not present in the mask. It is assumed that the
-     * GrGLAttribArrayState is tracking the state of the currently bound vertex array object.
+     * This function enables the first 'enabledCount' vertex arrays and disables the rest.
      */
-    void disableUnusedArrays(const GrGLGpu*, uint64_t usedAttribArrayMask);
+    void enableVertexArrays(const GrGLGpu*, int enabledCount);
 
     void invalidate() {
         int count = fAttribArrayStates.count();
         for (int i = 0; i < count; ++i) {
             fAttribArrayStates[i].invalidate();
         }
+        fEnabledCountIsValid = false;
     }
 
     /**
@@ -65,29 +64,32 @@
     int count() const { return fAttribArrayStates.count(); }
 
 private:
+    static constexpr int kInvalidDivisor = -1;
+
     /**
      * Tracks the state of glVertexAttribArray for an attribute index.
      */
     struct AttribArrayState {
         void invalidate() {
-            fEnableIsValid = false;
             fVertexBufferUniqueID.makeInvalid();
+            fDivisor = kInvalidDivisor;
         }
 
-        bool                            fEnableIsValid;
-        bool                            fEnabled;
-        GrGpuResource::UniqueID         fVertexBufferUniqueID;
-        GrVertexAttribType              fType;
-        GrGLsizei                       fStride;
-        GrGLvoid*                       fOffset;
+        GrGpuResource::UniqueID   fVertexBufferUniqueID;
+        GrVertexAttribType        fType;
+        GrGLsizei                 fStride;
+        size_t                    fOffset;
+        int                       fDivisor;
     };
 
-    SkSTArray<16, AttribArrayState, true> fAttribArrayStates;
+    SkSTArray<16, AttribArrayState, true>   fAttribArrayStates;
+    int                                     fNumEnabledArrays;
+    bool                                    fEnabledCountIsValid;
 };
 
 /**
  * This class represents an OpenGL vertex array object. It manages the lifetime of the vertex array
- * and is used to track the state of the vertex array to avoid redundant GL calls.
+ * and is used to track the state of the vertex array to avoid redundant GL calls.
  */
 class GrGLVertexArray {
 public:
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 9ddfa80..dda2ac1 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -8,6 +8,7 @@
 #include "GrGLProgramBuilder.h"
 
 #include "GrAutoLocaleSetter.h"
+#include "GrContext.h"
 #include "GrCoordTransform.h"
 #include "GrGLProgramBuilder.h"
 #include "GrProgramDesc.h"
@@ -19,7 +20,6 @@
 #include "SkTraceEvent.h"
 #include "gl/GrGLGpu.h"
 #include "gl/GrGLProgram.h"
-#include "gl/GrGLSLPrettyPrint.h"
 #include "gl/builders/GrGLShaderStringBuilder.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
@@ -33,6 +33,8 @@
                                                const GrPrimitiveProcessor& primProc,
                                                GrProgramDesc* desc,
                                                GrGLGpu* gpu) {
+    SkASSERT(!pipeline.isBad() && primProc.instantiate(gpu->getContext()->resourceProvider()));
+
     ATRACE_ANDROID_FRAMEWORK("Shader Compile");
     GrAutoLocaleSetter als("C");
 
@@ -40,12 +42,7 @@
     // uniforms, varyings, textures, etc
     GrGLProgramBuilder builder(gpu, pipeline, primProc, desc);
 
-    // TODO: Once all stages can handle taking a float or vec4 and correctly handling them we can
-    // seed correctly here
-    GrGLSLExpr4 inputColor;
-    GrGLSLExpr4 inputCoverage;
-
-    if (!builder.emitAndInstallProcs(&inputColor, &inputCoverage)) {
+    if (!builder.emitAndInstallProcs()) {
         builder.cleanupFragmentProcessors();
         return nullptr;
     }
@@ -161,7 +158,24 @@
     checkLinked = true;
 #endif
     if (checkLinked) {
-        checkLinkStatus(programID);
+        if (!this->checkLinkStatus(programID)) {
+            SkDebugf("VS:\n");
+            GrGLPrintShader(fGpu->glContext(), GR_GL_VERTEX_SHADER, fVS.fCompilerStrings.begin(),
+                            fVS.fCompilerStringLengths.begin(), fVS.fCompilerStrings.count(),
+                            settings);
+            if (primProc.willUseGeoShader()) {
+                SkDebugf("\nGS:\n");
+                GrGLPrintShader(fGpu->glContext(), GR_GL_GEOMETRY_SHADER,
+                                fGS.fCompilerStrings.begin(), fGS.fCompilerStringLengths.begin(),
+                                fGS.fCompilerStrings.count(), settings);
+            }
+            SkDebugf("\nFS:\n");
+            GrGLPrintShader(fGpu->glContext(), GR_GL_FRAGMENT_SHADER, fFS.fCompilerStrings.begin(),
+                            fFS.fCompilerStringLengths.begin(), fFS.fCompilerStrings.count(),
+                            settings);
+            SkDEBUGFAIL("");
+            return nullptr;
+        }
     }
     this->resolveProgramResourceLocations(programID);
 
@@ -200,6 +214,7 @@
     GrGLint linked = GR_GL_INIT_ZERO;
     GL_CALL(GetProgramiv(programID, GR_GL_LINK_STATUS, &linked));
     if (!linked) {
+        SkDebugf("Program linking failed.\n");
         GrGLint infoLen = GR_GL_INIT_ZERO;
         GL_CALL(GetProgramiv(programID, GR_GL_INFO_LOG_LENGTH, &infoLen));
         SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
@@ -213,7 +228,6 @@
                                       (char*)log.get()));
             SkDebugf("%s", (char*)log.get());
         }
-        SkDEBUGFAIL("Error linking program");
         GL_CALL(DeleteProgram(programID));
         programID = 0;
     }
@@ -257,6 +271,7 @@
                            programID,
                            fUniformHandler.fUniforms,
                            fUniformHandler.fSamplers,
+                           fUniformHandler.fTexelBuffers,
                            fUniformHandler.fImageStorages,
                            fVaryingHandler.fPathProcVaryingInfos,
                            fGeometryProcessor,
diff --git a/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp b/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
deleted file mode 100644
index 0280298..0000000
--- a/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp
+++ /dev/null
@@ -1,204 +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 "gl/GrGLSLPrettyPrint.h"
-
-namespace GrGLSLPrettyPrint {
-
-class GLSLPrettyPrint {
-public:
-    GLSLPrettyPrint() {}
-
-    SkString prettify(const char** strings,
-                      int* lengths,
-                      int count,
-                      bool countlines) {
-        fCountlines = countlines;
-        fTabs = 0;
-        fLinecount = 1;
-        fFreshline = true;
-
-        // If a string breaks while in the middle 'parse until' we need to continue parsing on the
-        // next string
-        fInParseUntilNewline = false;
-        fInParseUntil = false;
-
-        int parensDepth = 0;
-
-        // number 1st line
-        this->lineNumbering();
-        for (int i = 0; i < count; i++) {
-            // setup pretty state
-            fIndex = 0;
-            fLength = lengths[i];
-            fInput = strings[i];
-
-            while (fLength > fIndex) {
-                /* the heart and soul of our prettification algorithm.  The rules should hopefully
-                 * be self explanatory.  For '#' and '//' tokens we parse until we reach a newline.
-                 *
-                 * For long style comments like this one, we search for the ending token.  We also
-                 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
-                 * ourselves.  This allows us to remain in control of line numbers, and matching
-                 * tabs Existing tabs in the input string are copied over too, but this will look
-                 *  funny
-                 *
-                 * '{' and '}' are handled in basically the same way.  We add a newline if we aren't
-                 * on a fresh line, dirty the line, then add a second newline, ie braces are always
-                 * on their own lines indented properly.  The one funkiness here is structs print
-                 * with the semicolon on its own line.  Its not a problem for a glsl compiler though
-                 *
-                 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
-                 * in for loops.
-                 *
-                 * ';' means add a new line
-                 *
-                 * '\t' and '\n' are ignored in general parsing for backwards compatability with
-                 * existing shader code and we also have a special case for handling whitespace
-                 * at the beginning of fresh lines.
-                 *
-                 * Otherwise just add the new character to the pretty string, indenting if necessary.
-                 */
-                if (fInParseUntilNewline) {
-                    this->parseUntilNewline();
-                } else if (fInParseUntil) {
-                    this->parseUntil(fInParseUntilToken);
-                } else if (this->hasToken("#") || this->hasToken("//")) {
-                    this->parseUntilNewline();
-                } else if (this->hasToken("/*")) {
-                    this->parseUntil("*/");
-                } else if ('{' == fInput[fIndex]) {
-                    this->newline();
-                    this->appendChar('{');
-                    fTabs++;
-                    this->newline();
-                } else if ('}' == fInput[fIndex]) {
-                    fTabs--;
-                    this->newline();
-                    this->appendChar('}');
-                    this->newline();
-                } else if (this->hasToken(")")) {
-                    parensDepth--;
-                } else if (this->hasToken("(")) {
-                    parensDepth++;
-                } else if (!parensDepth && this->hasToken(";")) {
-                    this->newline();
-                } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
-                        (fFreshline && ' ' == fInput[fIndex])) {
-                    fIndex++;
-                } else {
-                    this->appendChar(fInput[fIndex]);
-                }
-            }
-        }
-        return fPretty;
-    }
-private:
-    void appendChar(char c) {
-        this->tabString();
-        fPretty.appendf("%c", fInput[fIndex++]);
-        fFreshline = false;
-    }
-
-    // hasToken automatically consumes the next token, if it is a match, and then tabs
-    // if necessary, before inserting the token into the pretty string
-    bool hasToken(const char* token) {
-        size_t i = fIndex;
-        for (size_t j = 0; token[j] && fLength > i; i++, j++) {
-            if (token[j] != fInput[i]) {
-                return false;
-            }
-        }
-        this->tabString();
-        fIndex = i;
-        fPretty.append(token);
-        fFreshline = false;
-        return true;
-    }
-
-    void parseUntilNewline() {
-        while (fLength > fIndex) {
-            if ('\n' == fInput[fIndex]) {
-                fIndex++;
-                this->newline();
-                fInParseUntilNewline = false;
-                break;
-            }
-            fPretty.appendf("%c", fInput[fIndex++]);
-            fInParseUntilNewline = true;
-        }
-    }
-
-    // this code assumes it is not actually searching for a newline.  If you need to search for a
-    // newline, then use the function above.  If you do search for a newline with this function
-    // it will consume the entire string and the output will certainly not be prettified
-    void parseUntil(const char* token) {
-        while (fLength > fIndex) {
-            // For embedded newlines,  this code will make sure to embed the newline in the
-            // pretty string, increase the linecount, and tab out the next line to the appropriate
-            // place
-            if ('\n' == fInput[fIndex]) {
-                this->newline();
-                this->tabString();
-                fIndex++;
-            }
-            if (this->hasToken(token)) {
-                fInParseUntil = false;
-                break;
-            }
-            fFreshline = false;
-            fPretty.appendf("%c", fInput[fIndex++]);
-            fInParseUntil = true;
-            fInParseUntilToken = token;
-        }
-    }
-
-    // We only tab if on a newline, otherwise consider the line tabbed
-    void tabString() {
-        if (fFreshline) {
-            for (int t = 0; t < fTabs; t++) {
-                fPretty.append("\t");
-            }
-        }
-    }
-
-    // newline is really a request to add a newline, if we are on a fresh line there is no reason
-    // to add another newline
-    void newline() {
-        if (!fFreshline) {
-            fFreshline = true;
-            fPretty.append("\n");
-            this->lineNumbering();
-        }
-    }
-
-    void lineNumbering() {
-        if (fCountlines) {
-            fPretty.appendf("%4d\t", fLinecount++);
-        }
-    }
-
-    bool fCountlines, fFreshline;
-    int fTabs, fLinecount;
-    size_t fIndex, fLength;
-    const char* fInput;
-    SkString fPretty;
-
-    // Some helpers for parseUntil when we go over a string length
-    bool fInParseUntilNewline;
-    bool fInParseUntil;
-    const char* fInParseUntilToken;
-};
-
-SkString PrettyPrintGLSL(const char** strings,
-                         int* lengths,
-                         int count,
-                         bool countlines) {
-    GLSLPrettyPrint pp;
-    return pp.prettify(strings, lengths, count, countlines);
-}
-
-} // end namespace
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
index 46530f2..8ea1e3e 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
@@ -6,12 +6,12 @@
  */
 
 #include "GrGLShaderStringBuilder.h"
+#include "GrSKSLPrettyPrint.h"
 #include "SkAutoMalloc.h"
 #include "SkSLCompiler.h"
 #include "SkSLGLSLCodeGenerator.h"
 #include "SkTraceEvent.h"
 #include "gl/GrGLGpu.h"
-#include "gl/GrGLSLPrettyPrint.h"
 #include "ir/SkSLProgram.h"
 
 #define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X)
@@ -20,12 +20,63 @@
 // Print the source code for all shaders generated.
 static const bool c_PrintShaders{false};
 
-static void print_source_with_line_numbers(const SkString&);
+static SkString list_source_with_line_numbers(const char* source) {
+    SkTArray<SkString> lines;
+    SkStrSplit(source, "\n", kStrict_SkStrSplitMode, &lines);
+    SkString result;
+    for (int line = 0; line < lines.count(); ++line) {
+        // Print the shader one line at the time so it doesn't get truncated by the adb log.
+        result.appendf("%4i\t%s\n", line + 1, lines[line].c_str());
+    }
+    return result;
+}
+
+SkString list_shaders(const char** skslStrings, int* lengths, int count, const SkSL::String& glsl) {
+    SkString sksl = GrSKSLPrettyPrint::PrettyPrint(skslStrings, lengths, count, false);
+    SkString result("SKSL:\n");
+    result.append(list_source_with_line_numbers(sksl.c_str()));
+    if (!glsl.isEmpty()) {
+        result.append("GLSL:\n");
+        result.append(list_source_with_line_numbers(glsl.c_str()));
+    }
+    return result;
+}
+
+std::unique_ptr<SkSL::Program> translate_to_glsl(const GrGLContext& context, GrGLenum type,
+                                                 const char** skslStrings, int* lengths, int count,
+                                                 const SkSL::Program::Settings& settings,
+                                                 SkSL::String* glsl) {
+    SkString sksl;
+#ifdef SK_DEBUG
+    sksl = GrSKSLPrettyPrint::PrettyPrint(skslStrings, lengths, count, false);
+#else
+    for (int i = 0; i < count; i++) {
+        sksl.append(skslStrings[i], lengths[i]);
+    }
+#endif
+    SkSL::Compiler* compiler = context.compiler();
+    std::unique_ptr<SkSL::Program> program;
+    SkSL::Program::Kind programKind;
+    switch (type) {
+        case GR_GL_VERTEX_SHADER:   programKind = SkSL::Program::kVertex_Kind;   break;
+        case GR_GL_FRAGMENT_SHADER: programKind = SkSL::Program::kFragment_Kind; break;
+        case GR_GL_GEOMETRY_SHADER: programKind = SkSL::Program::kGeometry_Kind; break;
+    }
+    program = compiler->convertProgram(programKind, sksl, settings);
+    if (!program || !compiler->toGLSL(*program, glsl)) {
+        SkDebugf("SKSL compilation error\n----------------------\n");
+        SkDebugf(list_shaders(skslStrings, lengths, count, *glsl).c_str());
+        SkDebugf("\nErrors:\n%s\n", compiler->errorText().c_str());
+        SkDEBUGFAIL("SKSL compilation failed!\n");
+        return nullptr;
+    }
+    return program;
+}
 
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
-                                    const char** strings,
+                                    const char** skslStrings,
                                     int* lengths,
                                     int count,
                                     GrGpu::Stats* stats,
@@ -33,55 +84,35 @@
                                     SkSL::Program::Inputs* outInputs) {
     const GrGLInterface* gli = glCtx.interface();
 
+    SkSL::String glsl;
+    auto program = translate_to_glsl(glCtx, type, skslStrings, lengths, count, settings, &glsl);
+    if (!program) {
+        return 0;
+    }
+
+    // Specify GLSL source to the driver.
     GrGLuint shaderId;
     GR_GL_CALL_RET(gli, shaderId, CreateShader(type));
     if (0 == shaderId) {
         return 0;
     }
-
-    SkString sksl;
-#ifdef SK_DEBUG
-    sksl = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
-#else
-    for (int i = 0; i < count; i++) {
-        sksl.append(strings[i], lengths[i]);
-    }
-#endif
-
-    SkString glsl;
-    if (type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER) {
-        SkSL::Compiler& compiler = *glCtx.compiler();
-        std::unique_ptr<SkSL::Program> program;
-        program = compiler.convertProgram(
-                                        type == GR_GL_VERTEX_SHADER ? SkSL::Program::kVertex_Kind
-                                                                    : SkSL::Program::kFragment_Kind,
-                                        sksl,
-                                        settings);
-        if (!program || !compiler.toGLSL(*program, &glsl)) {
-            SkDebugf("SKSL compilation error\n----------------------\n");
-            SkDebugf("SKSL:\n");
-            print_source_with_line_numbers(sksl);
-            SkDebugf("\nErrors:\n%s\n", compiler.errorText().c_str());
-            SkDEBUGFAIL("SKSL compilation failed!\n");
-        }
-        *outInputs = program->fInputs;
-    } else {
-        // TODO: geometry shader support in sksl.
-        SkASSERT(type == GR_GL_GEOMETRY_SHADER);
-        glsl = sksl;
-    }
-
     const char* glslChars = glsl.c_str();
     GrGLint glslLength = (GrGLint) glsl.size();
     GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength));
 
-    // If tracing is enabled in chrome then we pretty print
+    // Lazy initialized pretty-printed shaders for dumping.
+    SkString shaderDebugString;
+
+    // Trace event for shader preceding driver compilation
     bool traceShader;
     TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader);
     if (traceShader) {
-        SkString shader = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
+        if (shaderDebugString.isEmpty()) {
+            shaderDebugString = list_shaders(skslStrings, lengths, count, glsl);
+        }
         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "skia_gpu::GLShader",
-                             TRACE_EVENT_SCOPE_THREAD, "shader", TRACE_STR_COPY(shader.c_str()));
+                             TRACE_EVENT_SCOPE_THREAD, "shader",
+                             TRACE_STR_COPY(shaderDebugString.c_str()));
     }
 
     stats->incShaderCompilations();
@@ -97,6 +128,11 @@
         GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_COMPILE_STATUS, &compiled));
 
         if (!compiled) {
+            if (shaderDebugString.isEmpty()) {
+                shaderDebugString = list_shaders(skslStrings, lengths, count, glsl);
+            }
+            SkDebugf("GLSL compilation error\n----------------------\n");
+            SkDebugf(shaderDebugString.c_str());
             GrGLint infoLen = GR_GL_INIT_ZERO;
             GR_GL_CALL(gli, GetShaderiv(shaderId, GR_GL_INFO_LOG_LENGTH, &infoLen));
             SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
@@ -105,11 +141,6 @@
                 // buffer param validation.
                 GrGLsizei length = GR_GL_INIT_ZERO;
                 GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
-                SkDebugf("GLSL compilation error\n----------------------\n");
-                SkDebugf("SKSL:\n");
-                print_source_with_line_numbers(sksl);
-                SkDebugf("GLSL:\n");
-                print_source_with_line_numbers(glsl);
                 SkDebugf("Errors:\n%s\n", (const char*) log.get());
             }
             SkDEBUGFAIL("GLSL compilation failed!");
@@ -126,7 +157,10 @@
             case GR_GL_FRAGMENT_SHADER: typeName = "Fragment"; break;
         }
         SkDebugf("---- %s shader ----------------------------------------------------\n", typeName);
-        print_source_with_line_numbers(sksl);
+        if (shaderDebugString.isEmpty()) {
+            shaderDebugString = list_shaders(skslStrings, lengths, count, glsl);
+        }
+        SkDebugf(shaderDebugString.c_str());
     }
 
     // Attach the shader, but defer deletion until after we have linked the program.
@@ -134,15 +168,14 @@
     // will immediately delete the shader object and free its memory even though it's
     // attached to a program, which then causes glLinkProgram to fail.
     GR_GL_CALL(gli, AttachShader(programId, shaderId));
-
+    *outInputs = program->fInputs;
     return shaderId;
 }
 
-static void print_source_with_line_numbers(const SkString& source) {
-    SkTArray<SkString> lines;
-    SkStrSplit(source.c_str(), "\n", kStrict_SkStrSplitMode, &lines);
-    for (int line = 0; line < lines.count(); ++line) {
-        // Print the shader one line at the time so it doesn't get truncated by the adb log.
-        SkDebugf("%4i\t%s\n", line + 1, lines[line].c_str());
+void GrGLPrintShader(const GrGLContext& context, GrGLenum type, const char** skslStrings,
+                     int* lengths, int count, const SkSL::Program::Settings& settings) {
+    SkSL::String glsl;
+    if (translate_to_glsl(context, type, skslStrings, lengths, count, settings, &glsl)) {
+        SkDebugf(list_shaders(skslStrings, lengths, count, glsl).c_str());
     }
 }
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.h b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
index 242fe61..59dea35 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.h
@@ -17,11 +17,14 @@
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
-                                    const char** strings,
+                                    const char** skslStrings,
                                     int* lengths,
                                     int count,
                                     GrGpu::Stats*,
                                     const SkSL::Program::Settings& settings,
                                     SkSL::Program::Inputs* inputs);
 
+void GrGLPrintShader(const GrGLContext&, GrGLenum type, const char** skslStrings, int* lengths,
+                     int count, const SkSL::Program::Settings&);
+
 #endif
diff --git a/src/gpu/glsl/GrGLSL.cpp b/src/gpu/glsl/GrGLSL.cpp
index d54ddee..76ffb82 100644
--- a/src/gpu/glsl/GrGLSL.cpp
+++ b/src/gpu/glsl/GrGLSL.cpp
@@ -45,15 +45,3 @@
         }
     }
 }
-
-void GrGLSLMulVarBy4f(SkString* outAppend, const char* vec4VarName, const GrGLSLExpr4& mulFactor) {
-    if (mulFactor.isOnes()) {
-        *outAppend = SkString();
-    }
-
-    if (mulFactor.isZeros()) {
-        outAppend->appendf("%s = vec4(0);", vec4VarName);
-    } else {
-        outAppend->appendf("%s *= %s;", vec4VarName, mulFactor.c_str());
-    }
-}
diff --git a/src/gpu/glsl/GrGLSLBlend.cpp b/src/gpu/glsl/GrGLSLBlend.cpp
index 2f41c4e..bee5a83 100644
--- a/src/gpu/glsl/GrGLSLBlend.cpp
+++ b/src/gpu/glsl/GrGLSLBlend.cpp
@@ -7,7 +7,7 @@
 
 #include "GrGLSLBlend.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "SkXfermodePriv.h"
+#include "SkBlendModePriv.h"
 
 //////////////////////////////////////////////////////////////////////////////
 //  Advanced (non-coeff) blend helpers
@@ -370,10 +370,10 @@
 //  Porter-Duff blend helper
 //////////////////////////////////////////////////////////////////////////////
 
-static bool append_porterduff_term(GrGLSLFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff,
+static bool append_porterduff_term(GrGLSLFragmentBuilder* fsBuilder, SkBlendModeCoeff coeff,
                                    const char* colorName, const char* srcColorName,
                                    const char* dstColorName, bool hasPrevious) {
-    if (SkXfermode::kZero_Coeff == coeff) {
+    if (SkBlendModeCoeff::kZero == coeff) {
         return hasPrevious;
     } else {
         if (hasPrevious) {
@@ -381,30 +381,30 @@
         }
         fsBuilder->codeAppendf("%s", colorName);
         switch (coeff) {
-            case SkXfermode::kOne_Coeff:
+            case SkBlendModeCoeff::kOne:
                 break;
-            case SkXfermode::kSC_Coeff:
+            case SkBlendModeCoeff::kSC:
                 fsBuilder->codeAppendf(" * %s", srcColorName);
                 break;
-            case SkXfermode::kISC_Coeff:
+            case SkBlendModeCoeff::kISC:
                 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
                 break;
-            case SkXfermode::kDC_Coeff:
+            case SkBlendModeCoeff::kDC:
                 fsBuilder->codeAppendf(" * %s", dstColorName);
                 break;
-            case SkXfermode::kIDC_Coeff:
+            case SkBlendModeCoeff::kIDC:
                 fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
                 break;
-            case SkXfermode::kSA_Coeff:
+            case SkBlendModeCoeff::kSA:
                 fsBuilder->codeAppendf(" * %s.a", srcColorName);
                 break;
-            case SkXfermode::kISA_Coeff:
+            case SkBlendModeCoeff::kISA:
                 fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
                 break;
-            case SkXfermode::kDA_Coeff:
+            case SkBlendModeCoeff::kDA:
                 fsBuilder->codeAppendf(" * %s.a", dstColorName);
                 break;
-            case SkXfermode::kIDA_Coeff:
+            case SkBlendModeCoeff::kIDA:
                 fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
                 break;
             default:
@@ -420,8 +420,8 @@
                              const char* dstColor, const char* outColor,
                              SkBlendMode mode) {
 
-    SkXfermode::Coeff srcCoeff, dstCoeff;
-    if (SkXfermode::ModeAsCoeff(mode, &srcCoeff, &dstCoeff)) {
+    SkBlendModeCoeff srcCoeff, dstCoeff;
+    if (SkBlendMode_AsCoeff(mode, &srcCoeff, &dstCoeff)) {
         // The only coeff mode that can go out of range is plus.
         bool clamp = mode == SkBlendMode::kPlus;
 
@@ -448,37 +448,37 @@
 void GrGLSLBlend::AppendRegionOp(GrGLSLFragmentBuilder* fsBuilder, const char* srcColor,
                                  const char* dstColor, const char* outColor,
                                  SkRegion::Op regionOp) {
-    SkXfermode::Coeff srcCoeff, dstCoeff;
+    SkBlendModeCoeff srcCoeff, dstCoeff;
     switch (regionOp) {
         case SkRegion::kReplace_Op:
-            srcCoeff = SkXfermode::kOne_Coeff;
-            dstCoeff = SkXfermode::kZero_Coeff;
+            srcCoeff = SkBlendModeCoeff::kOne;
+            dstCoeff = SkBlendModeCoeff::kZero;
             break;
         case SkRegion::kIntersect_Op:
-            srcCoeff = SkXfermode::kDC_Coeff;
-            dstCoeff = SkXfermode::kZero_Coeff;
+            srcCoeff = SkBlendModeCoeff::kDC;
+            dstCoeff = SkBlendModeCoeff::kZero;
             break;
         case SkRegion::kUnion_Op:
-            srcCoeff = SkXfermode::kOne_Coeff;
-            dstCoeff = SkXfermode::kISC_Coeff;
+            srcCoeff = SkBlendModeCoeff::kOne;
+            dstCoeff = SkBlendModeCoeff::kISC;
             break;
         case SkRegion::kXOR_Op:
-            srcCoeff = SkXfermode::kIDC_Coeff;
-            dstCoeff = SkXfermode::kISC_Coeff;
+            srcCoeff = SkBlendModeCoeff::kIDC;
+            dstCoeff = SkBlendModeCoeff::kISC;
             break;
         case SkRegion::kDifference_Op:
-            srcCoeff = SkXfermode::kZero_Coeff;
-            dstCoeff = SkXfermode::kISC_Coeff;
+            srcCoeff = SkBlendModeCoeff::kZero;
+            dstCoeff = SkBlendModeCoeff::kISC;
             break;
         case SkRegion::kReverseDifference_Op:
-            srcCoeff = SkXfermode::kIDC_Coeff;
-            dstCoeff = SkXfermode::kZero_Coeff;
+            srcCoeff = SkBlendModeCoeff::kIDC;
+            dstCoeff = SkBlendModeCoeff::kZero;
             break;
         default:
             SkFAIL("Unsupported Op");
             // We should never get here but to make compiler happy
-            srcCoeff = SkXfermode::kZero_Coeff;
-            dstCoeff = SkXfermode::kZero_Coeff;
+            srcCoeff = SkBlendModeCoeff::kZero;
+            dstCoeff = SkBlendModeCoeff::kZero;
     }
     fsBuilder->codeAppendf("%s = ", outColor);
     // append src blend
diff --git a/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h b/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h
index 1571b06..9653efb 100644
--- a/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h
+++ b/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h
@@ -20,10 +20,11 @@
 public:
     GrGLSLColorSpaceXformHelper() : fValid(false) {}
 
-    void emitCode(GrGLSLUniformHandler* uniformHandler, GrColorSpaceXform* colorSpaceXform) {
+    void emitCode(GrGLSLUniformHandler* uniformHandler, GrColorSpaceXform* colorSpaceXform,
+                  uint32_t visibility = kFragment_GrShaderFlag) {
         SkASSERT(uniformHandler);
         if (colorSpaceXform) {
-            fGamutXformVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
+            fGamutXformVar = uniformHandler->addUniform(visibility, kMat44f_GrSLType,
                                                         kDefault_GrSLPrecision, "ColorXform");
             fValid = true;
         }
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index a779acc..006fe58 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -26,7 +26,6 @@
 
 void GrGLSLFragmentProcessor::emitChild(int childIndex, const char* inputColor,
                                         SkString* outputColor, EmitArgs& args) {
-
     SkASSERT(outputColor);
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
     outputColor->append(fragBuilder->getMangleString());
@@ -36,6 +35,7 @@
 
 void GrGLSLFragmentProcessor::internalEmitChild(int childIndex, const char* inputColor,
                                                 const char* outputColor, EmitArgs& args) {
+    SkASSERT(inputColor);
     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
     fragBuilder->onBeforeChildProcEmitCode();  // call first so mangleString is updated
@@ -48,7 +48,7 @@
                              fragBuilder->getMangleString().c_str(), childProc.name());
     TransformedCoordVars coordVars = args.fTransformedCoords.childInputs(childIndex);
     TextureSamplers textureSamplers = args.fTexSamplers.childInputs(childIndex);
-    BufferSamplers bufferSamplers = args.fBufferSamplers.childInputs(childIndex);
+    TexelBuffers texelBuffers = args.fTexelBuffers.childInputs(childIndex);
     ImageStorages imageStorages = args.fImageStorages.childInputs(childIndex);
     EmitArgs childArgs(fragBuilder,
                        args.fUniformHandler,
@@ -58,9 +58,8 @@
                        inputColor,
                        coordVars,
                        textureSamplers,
-                       bufferSamplers,
-                       imageStorages,
-                       args.fGpImplementsDistanceVector);
+                       texelBuffers,
+                       imageStorages);
     this->childProcessor(childIndex)->emitCode(childArgs);
     fragBuilder->codeAppend("}\n");
 
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.h b/src/gpu/glsl/GrGLSLFragmentProcessor.h
index 7a8d51c..355d490 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.h
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.h
@@ -30,6 +30,7 @@
 
     using UniformHandle      = GrGLSLUniformHandler::UniformHandle;
     using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using TexelBufferHandle  = GrGLSLUniformHandler::TexelBufferHandle;
     using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
 private:
@@ -70,12 +71,12 @@
 public:
     using TransformedCoordVars = BuilderInputProvider<GrShaderVar, GrFragmentProcessor,
                                                       &GrFragmentProcessor::numCoordTransforms>;
-    using TextureSamplers = BuilderInputProvider<SamplerHandle, GrProcessor,
-                                                 &GrProcessor::numTextureSamplers>;
-    using BufferSamplers = BuilderInputProvider<SamplerHandle, GrProcessor,
-                                                &GrProcessor::numBuffers>;
-    using ImageStorages = BuilderInputProvider<ImageStorageHandle, GrProcessor,
-                                               &GrProcessor::numImageStorages>;
+    using TextureSamplers = BuilderInputProvider<SamplerHandle, GrResourceIOProcessor,
+                                                 &GrResourceIOProcessor::numTextureSamplers>;
+    using TexelBuffers = BuilderInputProvider<TexelBufferHandle, GrResourceIOProcessor,
+                                                &GrResourceIOProcessor::numBuffers>;
+    using ImageStorages = BuilderInputProvider<ImageStorageHandle, GrResourceIOProcessor,
+                                               &GrResourceIOProcessor::numImageStorages>;
 
     /** Called when the program stage should insert its code into the shaders. The code in each
         shader will be in its own block ({}) and so locally scoped names will not collide across
@@ -104,9 +105,6 @@
         @param imageStorages     Contains one entry for each ImageStorageAccess of the GrProcessor.
                                  These can be passed to the builder to emit image loads and stores
                                  in the generated code.
-        @param gpImplementsDistanceVector
-                                 Does the GrGeometryProcessor implement the feature where it
-                                 provides a vector to the nearest edge of the shape being rendered.
      */
     struct EmitArgs {
         EmitArgs(GrGLSLFPFragmentBuilder* fragBuilder,
@@ -117,20 +115,18 @@
                  const char* inputColor,
                  const TransformedCoordVars& transformedCoordVars,
                  const TextureSamplers& textureSamplers,
-                 const BufferSamplers& bufferSamplers,
-                 const ImageStorages& imageStorages,
-                 bool gpImplementsDistanceVector)
-            : fFragBuilder(fragBuilder)
-            , fUniformHandler(uniformHandler)
-            , fShaderCaps(caps)
-            , fFp(fp)
-            , fOutputColor(outputColor)
-            , fInputColor(inputColor)
-            , fTransformedCoords(transformedCoordVars)
-            , fTexSamplers(textureSamplers)
-            , fBufferSamplers(bufferSamplers)
-            , fImageStorages(imageStorages)
-            , fGpImplementsDistanceVector(gpImplementsDistanceVector) {}
+                 const TexelBuffers& texelBuffers,
+                 const ImageStorages& imageStorages)
+                : fFragBuilder(fragBuilder)
+                , fUniformHandler(uniformHandler)
+                , fShaderCaps(caps)
+                , fFp(fp)
+                , fOutputColor(outputColor)
+                , fInputColor(inputColor)
+                , fTransformedCoords(transformedCoordVars)
+                , fTexSamplers(textureSamplers)
+                , fTexelBuffers(texelBuffers)
+                , fImageStorages(imageStorages) {}
         GrGLSLFPFragmentBuilder* fFragBuilder;
         GrGLSLUniformHandler* fUniformHandler;
         const GrShaderCaps* fShaderCaps;
@@ -139,9 +135,8 @@
         const char* fInputColor;
         const TransformedCoordVars& fTransformedCoords;
         const TextureSamplers& fTexSamplers;
-        const BufferSamplers& fBufferSamplers;
+        const TexelBuffers& fTexelBuffers;
         const ImageStorages& fImageStorages;
-        bool fGpImplementsDistanceVector;
     };
 
     virtual void emitCode(EmitArgs&) = 0;
@@ -156,6 +151,10 @@
         return fChildProcessors[index];
     }
 
+    inline void emitChild(int childIndex, SkString* outputColor, EmitArgs& parentArgs) {
+        this->emitChild(childIndex, "vec4(1.0)", outputColor, parentArgs);
+    }
+
     /** Will emit the code of a child proc in its own scope. Pass in the parent's EmitArgs and
      *  emitChild will automatically extract the coords and samplers of that child and pass them
      *  on to the child's emitCode(). Also, any uniforms or functions emitted by the child will
@@ -167,6 +166,10 @@
     void emitChild(int childIndex, const char* inputColor, SkString* outputColor,
                    EmitArgs& parentArgs);
 
+    inline void emitChild(int childIndex, EmitArgs& args) {
+        this->emitChild(childIndex, "vec4(1.0)", args);
+    }
+
     /** Variation that uses the parent's output color variable to hold the child's output.*/
     void emitChild(int childIndex, const char* inputColor, EmitArgs& parentArgs);
 
@@ -194,8 +197,7 @@
     uniform variables required by the shaders created in emitCode(). The GrFragmentProcessor
     parameter is guaranteed to be of the same type that created this GrGLSLFragmentProcessor and
     to have an identical processor key as the one that created this GrGLSLFragmentProcessor.  */
-    // TODO update this to pass in GrFragmentProcessor
-    virtual void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) {}
+    virtual void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) {}
 
 private:
     void internalEmitChild(int, const char*, const char*, EmitArgs&);
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
index a1f5173..4fd886b 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
@@ -85,7 +85,8 @@
     , fCustomColorOutputIndex(-1)
     , fHasSecondaryOutput(false)
     , fUsedSampleOffsetArrays(0)
-    , fHasInitializedSampleMask(false) {
+    , fHasInitializedSampleMask(false)
+    , fDefaultPrecision(kMedium_GrSLPrecision) {
     fSubstageIndices.push_back(0);
 #ifdef SK_DEBUG
     fUsedProcessorFeatures = GrProcessor::kNone_RequiredFeatures;
@@ -123,10 +124,6 @@
     return coords2D;
 }
 
-const char* GrGLSLFragmentShaderBuilder::distanceVectorName() const {
-    return "fsDistanceVector";
-}
-
 void GrGLSLFragmentShaderBuilder::appendOffsetToSample(const char* sampleIdx, Coordinates coords) {
     SkASSERT(fProgramBuilder->header().fSamplePatternKey);
     SkDEBUGCODE(fUsedProcessorFeatures |= GrProcessor::kSampleLocations_RequiredFeature);
@@ -178,6 +175,10 @@
     fHasInitializedSampleMask = true;
 }
 
+void GrGLSLFragmentShaderBuilder::elevateDefaultPrecision(GrSLPrecision precision) {
+    fDefaultPrecision = SkTMax(fDefaultPrecision, precision);
+}
+
 const char* GrGLSLFragmentShaderBuilder::dstColor() {
     SkDEBUGCODE(fHasReadDstColor = true;)
 
@@ -279,7 +280,7 @@
 
 void GrGLSLFragmentShaderBuilder::onFinalize() {
     fProgramBuilder->varyingHandler()->getFragDecls(&this->inputs(), &this->outputs());
-    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision,
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(fDefaultPrecision,
                                                  *fProgramBuilder->shaderCaps(),
                                                  &this->precisionQualifier());
     if (fUsedSampleOffsetArrays & (1 << kSkiaDevice_Coordinates)) {
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
index 764f3bd..73fe51f 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
@@ -89,9 +89,12 @@
      */
     virtual void maskSampleCoverage(const char* mask, bool invert = false) = 0;
 
-    /** Returns a variable name that represents a vector to the nearest edge of the shape, in source
-        space coordinates. */
-    virtual const char* distanceVectorName() const = 0;
+    /**
+     * Overrides the default precision for the entire fragment program. Processors that require
+     * high precision input (eg from incoming texture samples) may use this. For calculations that
+     * are limited to a single processor's code, it is better to annotate individual declarations.
+     */
+    virtual void elevateDefaultPrecision(GrSLPrecision) = 0;
 
     /**
      * Fragment procs with child procs should call these functions before/after calling emitCode
@@ -161,12 +164,12 @@
     // Shared GrGLSLFragmentBuilder interface.
     bool enableFeature(GLSLFeature) override;
     virtual SkString ensureCoords2D(const GrShaderVar&) override;
-    const char* distanceVectorName() const override;
 
     // GrGLSLFPFragmentBuilder interface.
     void appendOffsetToSample(const char* sampleIdx, Coordinates) override;
     void maskSampleCoverage(const char* mask, bool invert = false) override;
     void overrideSampleCoverage(const char* mask) override;
+    void elevateDefaultPrecision(GrSLPrecision) override;
     const SkString& getMangleString() const override { return fMangleString; }
     void onBeforeChildProcEmitCode() override;
     void onAfterChildProcEmitCode() override;
@@ -225,13 +228,13 @@
      */
     SkString fMangleString;
 
-    bool       fSetupFragPosition;
-    bool       fHasCustomColorOutput;
-    int        fCustomColorOutputIndex;
-    bool       fHasSecondaryOutput;
-    uint8_t    fUsedSampleOffsetArrays;
-    bool       fHasInitializedSampleMask;
-    SkString   fDistanceVectorOutput;
+    bool          fSetupFragPosition;
+    bool          fHasCustomColorOutput;
+    int           fCustomColorOutputIndex;
+    bool          fHasSecondaryOutput;
+    uint8_t       fUsedSampleOffsetArrays;
+    bool          fHasInitializedSampleMask;
+    GrSLPrecision fDefaultPrecision;
 
 #ifdef SK_DEBUG
     // some state to verify shaders and effects are consistent, this is reset between effects by
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
index f130248..9dd1cba 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -41,7 +41,8 @@
 
         varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kVec3f_GrSLType :
                                                                      kVec2f_GrSLType;
-        GrSLPrecision precision = coordTransform->precision();
+        // Coord transforms are always handled at high precision
+        const GrSLPrecision precision = kHigh_GrSLPrecision;
 
         const char* uniName;
 
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
index f39fff2..7c6b2d4 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
@@ -9,6 +9,7 @@
 
 #include "GrCoordTransform.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
 #include "glsl/GrGLSLUniformHandler.h"
 #include "glsl/GrGLSLVertexShaderBuilder.h"
 
@@ -17,8 +18,8 @@
     SkMatrix combined;
     combined.setConcat(coordTransform.getMatrix(), localMatrix);
     if (coordTransform.normalize()) {
-        SkASSERT(coordTransform.texture());
-        combined.postIDiv(coordTransform.texture()->width(), coordTransform.texture()->height());
+        combined.postIDiv(coordTransform.peekTexture()->width(),
+                          coordTransform.peekTexture()->height());
     }
 
     if (coordTransform.reverseY()) {
@@ -46,6 +47,9 @@
                                                "Color",
                                                &stagedLocalVarName);
     fragBuilder->codeAppendf("%s = %s;", outputName, stagedLocalVarName);
+    if (fragBuilder->getProgramBuilder()->shaderCaps()->mustObfuscateUniformColor()) {
+        fragBuilder->codeAppendf("%s = max(%s, vec4(0, 0, 0, 0));", outputName, outputName);
+    }
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
index 5fbf9bd..c4f3115 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
@@ -29,6 +29,7 @@
 
     using UniformHandle      = GrGLSLProgramDataManager::UniformHandle;
     using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using TexelBufferHandle  = GrGLSLUniformHandler::TexelBufferHandle;
     using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     /**
@@ -74,10 +75,9 @@
                  const GrPrimitiveProcessor& gp,
                  const char* outputColor,
                  const char* outputCoverage,
-                 const char* distanceVectorName,
                  const char* rtAdjustName,
                  const SamplerHandle* texSamplers,
-                 const SamplerHandle* bufferSamplers,
+                 const TexelBufferHandle* texelBuffers,
                  const ImageStorageHandle* imageStorages,
                  FPCoordTransformHandler* transformHandler)
             : fVertBuilder(vertBuilder)
@@ -89,10 +89,9 @@
             , fGP(gp)
             , fOutputColor(outputColor)
             , fOutputCoverage(outputCoverage)
-            , fDistanceVectorName(distanceVectorName)
             , fRTAdjustName(rtAdjustName)
             , fTexSamplers(texSamplers)
-            , fBufferSamplers(bufferSamplers)
+            , fTexelBuffers(texelBuffers)
             , fImageStorages(imageStorages)
             , fFPCoordTransformHandler(transformHandler) {}
         GrGLSLVertexBuilder* fVertBuilder;
@@ -104,10 +103,9 @@
         const GrPrimitiveProcessor& fGP;
         const char* fOutputColor;
         const char* fOutputCoverage;
-        const char* fDistanceVectorName;
         const char* fRTAdjustName;
         const SamplerHandle* fTexSamplers;
-        const SamplerHandle* fBufferSamplers;
+        const TexelBufferHandle* fTexelBuffers;
         const ImageStorageHandle* fImageStorages;
         FPCoordTransformHandler* fFPCoordTransformHandler;
     };
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index 1fcf040..0d87c4f 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -53,40 +53,29 @@
     }
 }
 
-bool GrGLSLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor,
-                                               GrGLSLExpr4* inputCoverage) {
+bool GrGLSLProgramBuilder::emitAndInstallProcs() {
     // First we loop over all of the installed processors and collect coord transforms.  These will
     // be sent to the GrGLSLPrimitiveProcessor in its emitCode function
     const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
 
-    this->emitAndInstallPrimProc(primProc, inputColor, inputCoverage);
-
-    this->emitAndInstallFragProcs(inputColor, inputCoverage);
-    this->emitAndInstallXferProc(this->pipeline().getXferProcessor(), *inputColor, *inputCoverage);
+    SkString inputColor;
+    SkString inputCoverage;
+    this->emitAndInstallPrimProc(primProc, &inputColor, &inputCoverage);
+    this->emitAndInstallFragProcs(&inputColor, &inputCoverage);
+    this->emitAndInstallXferProc(inputColor, inputCoverage);
     this->emitFSOutputSwizzle(this->pipeline().getXferProcessor().hasSecondaryOutput());
 
     return this->checkSamplerCounts() && this->checkImageStorageCounts();
 }
 
 void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& proc,
-                                                  GrGLSLExpr4* outputColor,
-                                                  GrGLSLExpr4* outputCoverage) {
+                                                  SkString* outputColor,
+                                                  SkString* outputCoverage) {
     // Program builders have a bit of state we need to clear with each effect
     AutoStageAdvance adv(this);
     this->nameExpression(outputColor, "outputColor");
     this->nameExpression(outputCoverage, "outputCoverage");
 
-    const char* distanceVectorName = nullptr;
-    if (this->fPipeline.usesDistanceVectorField() && proc.implementsDistanceVector()) {
-        // Each individual user (FP) of the distance vector must be able to handle having this
-        // variable be undeclared. There is no single default value that will yield a reasonable
-        // result for all users.
-        distanceVectorName = fFS.distanceVectorName();
-        fFS.codeAppend( "// Normalized vector to the closest geometric edge (in device space)\n");
-        fFS.codeAppend( "// Distance to the edge encoded in the z-component\n");
-        fFS.codeAppendf("vec4 %s;", distanceVectorName);
-    }
-
     SkASSERT(!fUniformHandles.fRTAdjustmentUni.isValid());
     GrShaderFlags rtAdjustVisibility = kVertex_GrShaderFlag;
     if (proc.willUseGeoShader()) {
@@ -109,9 +98,9 @@
     fGeometryProcessor = proc.createGLSLInstance(*this->shaderCaps());
 
     SkSTArray<4, SamplerHandle>      texSamplers(proc.numTextureSamplers());
-    SkSTArray<2, SamplerHandle>      bufferSamplers(proc.numBuffers());
+    SkSTArray<2, TexelBufferHandle>  texelBuffers(proc.numBuffers());
     SkSTArray<2, ImageStorageHandle> imageStorages(proc.numImageStorages());
-    this->emitSamplersAndImageStorages(proc, &texSamplers, &bufferSamplers, &imageStorages);
+    this->emitSamplersAndImageStorages(proc, &texSamplers, &texelBuffers, &imageStorages);
 
     GrGLSLPrimitiveProcessor::FPCoordTransformHandler transformHandler(fPipeline,
                                                                        &fTransformedCoordVars);
@@ -124,10 +113,9 @@
                                            proc,
                                            outputColor->c_str(),
                                            outputCoverage->c_str(),
-                                           distanceVectorName,
                                            rtAdjustName,
                                            texSamplers.begin(),
-                                           bufferSamplers.begin(),
+                                           texelBuffers.begin(),
                                            imageStorages.begin(),
                                            &transformHandler);
     fGeometryProcessor->emitCode(args);
@@ -139,16 +127,16 @@
     fFS.codeAppend("}");
 }
 
-void GrGLSLProgramBuilder::emitAndInstallFragProcs(GrGLSLExpr4* color, GrGLSLExpr4* coverage) {
+void GrGLSLProgramBuilder::emitAndInstallFragProcs(SkString* color, SkString* coverage) {
     int transformedCoordVarsIdx = 0;
-    GrGLSLExpr4** inOut = &color;
+    SkString** inOut = &color;
     for (int i = 0; i < this->pipeline().numFragmentProcessors(); ++i) {
         if (i == this->pipeline().numColorFragmentProcessors()) {
             inOut = &coverage;
         }
-        GrGLSLExpr4 output;
+        SkString output;
         const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i);
-        this->emitAndInstallFragProc(fp, i, transformedCoordVarsIdx, **inOut, &output);
+        output = this->emitAndInstallFragProc(fp, i, transformedCoordVarsIdx, **inOut, output);
         GrFragmentProcessor::Iter iter(&fp);
         while (const GrFragmentProcessor* fp = iter.next()) {
             transformedCoordVarsIdx += fp->numCoordTransforms();
@@ -158,15 +146,16 @@
 }
 
 // TODO Processors cannot output zeros because an empty string is all 1s
-// the fix is to allow effects to take the GrGLSLExpr4 directly
-void GrGLSLProgramBuilder::emitAndInstallFragProc(const GrFragmentProcessor& fp,
-                                                  int index,
-                                                  int transformedCoordVarsIdx,
-                                                  const GrGLSLExpr4& input,
-                                                  GrGLSLExpr4* output) {
+// the fix is to allow effects to take the SkString directly
+SkString GrGLSLProgramBuilder::emitAndInstallFragProc(const GrFragmentProcessor& fp,
+                                                      int index,
+                                                      int transformedCoordVarsIdx,
+                                                      const SkString& input,
+                                                      SkString output) {
+    SkASSERT(input.size());
     // Program builders have a bit of state we need to clear with each effect
     AutoStageAdvance adv(this);
-    this->nameExpression(output, "output");
+    this->nameExpression(&output, "output");
 
     // Enclose custom code in a block to avoid namespace conflicts
     SkString openBrace;
@@ -176,30 +165,29 @@
     GrGLSLFragmentProcessor* fragProc = fp.createGLSLInstance();
 
     SkSTArray<4, SamplerHandle> textureSamplerArray(fp.numTextureSamplers());
-    SkSTArray<2, SamplerHandle> bufferSamplerArray(fp.numBuffers());
+    SkSTArray<2, TexelBufferHandle> texelBufferArray(fp.numBuffers());
     SkSTArray<2, ImageStorageHandle> imageStorageArray(fp.numImageStorages());
     GrFragmentProcessor::Iter iter(&fp);
     while (const GrFragmentProcessor* subFP = iter.next()) {
-        this->emitSamplersAndImageStorages(*subFP, &textureSamplerArray, &bufferSamplerArray,
+        this->emitSamplersAndImageStorages(*subFP, &textureSamplerArray, &texelBufferArray,
                                            &imageStorageArray);
     }
 
     const GrShaderVar* coordVars = fTransformedCoordVars.begin() + transformedCoordVarsIdx;
     GrGLSLFragmentProcessor::TransformedCoordVars coords(&fp, coordVars);
     GrGLSLFragmentProcessor::TextureSamplers textureSamplers(&fp, textureSamplerArray.begin());
-    GrGLSLFragmentProcessor::BufferSamplers bufferSamplers(&fp, bufferSamplerArray.begin());
+    GrGLSLFragmentProcessor::TexelBuffers texelBuffers(&fp, texelBufferArray.begin());
     GrGLSLFragmentProcessor::ImageStorages imageStorages(&fp, imageStorageArray.begin());
     GrGLSLFragmentProcessor::EmitArgs args(&fFS,
                                            this->uniformHandler(),
                                            this->shaderCaps(),
                                            fp,
-                                           output->c_str(),
-                                           input.isOnes() ? nullptr : input.c_str(),
+                                           output.c_str(),
+                                           input.c_str(),
                                            coords,
                                            textureSamplers,
-                                           bufferSamplers,
-                                           imageStorages,
-                                           this->primitiveProcessor().implementsDistanceVector());
+                                           texelBuffers,
+                                           imageStorages);
 
     fragProc->emitCode(args);
 
@@ -209,15 +197,16 @@
     fFragmentProcessors.push_back(fragProc);
 
     fFS.codeAppend("}");
+    return output;
 }
 
-void GrGLSLProgramBuilder::emitAndInstallXferProc(const GrXferProcessor& xp,
-                                                  const GrGLSLExpr4& colorIn,
-                                                  const GrGLSLExpr4& coverageIn) {
+void GrGLSLProgramBuilder::emitAndInstallXferProc(const SkString& colorIn,
+                                                  const SkString& coverageIn) {
     // Program builders have a bit of state we need to clear with each effect
     AutoStageAdvance adv(this);
 
     SkASSERT(!fXferProcessor);
+    const GrXferProcessor& xp = fPipeline.getXferProcessor();
     fXferProcessor = xp.createGLSLInstance();
 
     // Enable dual source secondary output if we have one
@@ -233,21 +222,29 @@
     openBrace.printf("{ // Xfer Processor: %s\n", xp.name());
     fFS.codeAppend(openBrace.c_str());
 
-    SkSTArray<4, SamplerHandle>      texSamplers(xp.numTextureSamplers());
-    SkSTArray<2, SamplerHandle>      bufferSamplers(xp.numBuffers());
-    SkSTArray<2, ImageStorageHandle> imageStorageArray(xp.numImageStorages());
-    this->emitSamplersAndImageStorages(xp, &texSamplers, &bufferSamplers, &imageStorageArray);
+    SamplerHandle dstTextureSamplerHandle;
+    GrSurfaceOrigin dstTextureOrigin = kTopLeft_GrSurfaceOrigin;
+
+    if (GrTexture* dstTexture = fPipeline.peekDstTexture()) {
+        // GrProcessor::TextureSampler sampler(dstTexture);
+        SkString name("DstTextureSampler");
+        dstTextureSamplerHandle =
+                this->emitSampler(dstTexture->texturePriv().samplerType(), dstTexture->config(),
+                                  "DstTextureSampler", kFragment_GrShaderFlag);
+        dstTextureOrigin = dstTexture->origin();
+        SkASSERT(kTextureExternalSampler_GrSLType != dstTexture->texturePriv().samplerType());
+    }
 
     GrGLSLXferProcessor::EmitArgs args(&fFS,
                                        this->uniformHandler(),
                                        this->shaderCaps(),
-                                       xp, colorIn.c_str(),
-                                       coverageIn.c_str(),
+                                       xp,
+                                       colorIn.size() ? colorIn.c_str() : "vec4(1)",
+                                       coverageIn.size() ? coverageIn.c_str() : "vec4(1)",
                                        fFS.getPrimaryColorOutputName(),
                                        fFS.getSecondaryColorOutputName(),
-                                       texSamplers.begin(),
-                                       bufferSamplers.begin(),
-                                       imageStorageArray.begin());
+                                       dstTextureSamplerHandle,
+                                       dstTextureOrigin);
     fXferProcessor->emitCode(args);
 
     // We have to check that effects and the code they emit are consistent, ie if an effect
@@ -257,16 +254,16 @@
 }
 
 void GrGLSLProgramBuilder::emitSamplersAndImageStorages(
-        const GrProcessor& processor,
+        const GrResourceIOProcessor& processor,
         SkTArray<SamplerHandle>* outTexSamplerHandles,
-        SkTArray<SamplerHandle>* outBufferSamplerHandles,
+        SkTArray<TexelBufferHandle>* outTexelBufferHandles,
         SkTArray<ImageStorageHandle>* outImageStorageHandles) {
     SkString name;
     int numTextureSamplers = processor.numTextureSamplers();
     for (int t = 0; t < numTextureSamplers; ++t) {
-        const GrProcessor::TextureSampler& sampler = processor.textureSampler(t);
+        const GrResourceIOProcessor::TextureSampler& sampler = processor.textureSampler(t);
         name.printf("TextureSampler_%d", outTexSamplerHandles->count());
-        GrSLType samplerType = sampler.texture()->texturePriv().samplerType();
+        GrSLType samplerType = sampler.peekTexture()->texturePriv().samplerType();
         if (kTextureExternalSampler_GrSLType == samplerType) {
             const char* externalFeatureString =
                     this->shaderCaps()->externalTextureExtensionString();
@@ -276,20 +273,18 @@
                              1 << GrGLSLShaderBuilder::kExternalTexture_GLSLPrivateFeature,
                              externalFeatureString);
         }
-        this->emitSampler(samplerType, sampler.texture()->config(), name.c_str(),
-                          sampler.visibility(), outTexSamplerHandles);
-
+        outTexSamplerHandles->emplace_back(this->emitSampler(
+                samplerType, sampler.peekTexture()->config(), name.c_str(), sampler.visibility()));
     }
-
     if (int numBuffers = processor.numBuffers()) {
         SkASSERT(this->shaderCaps()->texelBufferSupport());
         GrShaderFlags texelBufferVisibility = kNone_GrShaderFlags;
 
         for (int b = 0; b < numBuffers; ++b) {
-            const GrProcessor::BufferAccess& access = processor.bufferAccess(b);
-            name.printf("BufferSampler_%d", outBufferSamplerHandles->count());
-            this->emitSampler(kBufferSampler_GrSLType, access.texelConfig(), name.c_str(),
-                              access.visibility(), outBufferSamplerHandles);
+            const GrResourceIOProcessor::BufferAccess& access = processor.bufferAccess(b);
+            name.printf("TexelBuffer_%d", outTexelBufferHandles->count());
+            outTexelBufferHandles->emplace_back(
+                    this->emitTexelBuffer(access.texelConfig(), name.c_str(), access.visibility()));
             texelBufferVisibility |= access.visibility();
         }
 
@@ -301,17 +296,15 @@
     }
     int numImageStorages = processor.numImageStorages();
     for (int i = 0; i < numImageStorages; ++i) {
-        const GrProcessor::ImageStorageAccess& imageStorageAccess = processor.imageStorageAccess(i);
+        const GrResourceIOProcessor::ImageStorageAccess& imageStorageAccess =
+                processor.imageStorageAccess(i);
         name.printf("Image_%d", outImageStorageHandles->count());
-        this->emitImageStorage(imageStorageAccess, name.c_str(), outImageStorageHandles);
+        outImageStorageHandles->emplace_back(
+                this->emitImageStorage(imageStorageAccess, name.c_str()));
     }
 }
 
-void GrGLSLProgramBuilder::emitSampler(GrSLType samplerType,
-                                       GrPixelConfig config,
-                                       const char* name,
-                                       GrShaderFlags visibility,
-                                       SkTArray<SamplerHandle>* outSamplerHandles) {
+void GrGLSLProgramBuilder::updateSamplerCounts(GrShaderFlags visibility) {
     if (visibility & kVertex_GrShaderFlag) {
         ++fNumVertexSamplers;
     }
@@ -322,18 +315,27 @@
     if (visibility & kFragment_GrShaderFlag) {
         ++fNumFragmentSamplers;
     }
-    GrSLPrecision precision = this->shaderCaps()->samplerPrecision(config, visibility);
-    GrSwizzle swizzle = this->shaderCaps()->configTextureSwizzle(config);
-    outSamplerHandles->emplace_back(this->uniformHandler()->addSampler(visibility,
-                                                                       swizzle,
-                                                                       samplerType,
-                                                                       precision,
-                                                                       name));
 }
 
-void GrGLSLProgramBuilder::emitImageStorage(const GrProcessor::ImageStorageAccess& access,
-                                            const char* name,
-                                            SkTArray<ImageStorageHandle>* outImageStorageHandles) {
+GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(GrSLType samplerType,
+                                                                      GrPixelConfig config,
+                                                                      const char* name,
+                                                                      GrShaderFlags visibility) {
+    this->updateSamplerCounts(visibility);
+    GrSLPrecision precision = this->shaderCaps()->samplerPrecision(config, visibility);
+    GrSwizzle swizzle = this->shaderCaps()->configTextureSwizzle(config);
+    return this->uniformHandler()->addSampler(visibility, swizzle, samplerType, precision, name);
+}
+
+GrGLSLProgramBuilder::TexelBufferHandle GrGLSLProgramBuilder::emitTexelBuffer(
+        GrPixelConfig config, const char* name, GrShaderFlags visibility) {
+    this->updateSamplerCounts(visibility);
+    GrSLPrecision precision = this->shaderCaps()->samplerPrecision(config, visibility);
+    return this->uniformHandler()->addTexelBuffer(visibility, precision, name);
+}
+
+GrGLSLProgramBuilder::ImageStorageHandle GrGLSLProgramBuilder::emitImageStorage(
+        const GrResourceIOProcessor::ImageStorageAccess& access, const char* name) {
     if (access.visibility() & kVertex_GrShaderFlag) {
         ++fNumVertexImageStorages;
     }
@@ -344,11 +346,10 @@
     if (access.visibility() & kFragment_GrShaderFlag) {
         ++fNumFragmentImageStorages;
     }
-    GrSLType uniformType = access.texture()->texturePriv().imageStorageType();
-    ImageStorageHandle handle = this->uniformHandler()->addImageStorage(access.visibility(),
-         uniformType, access.format(), access.memoryModel(), access.restrict(), access.ioType(),
-         name);
-    outImageStorageHandles->emplace_back(handle);
+    GrSLType uniformType = access.proxy()->imageStorageType();
+    return this->uniformHandler()->addImageStorage(access.visibility(), uniformType,
+                                                   access.format(), access.memoryModel(),
+                                                   access.restrict(), access.ioType(), name);
 }
 
 void GrGLSLProgramBuilder::emitFSOutputSwizzle(bool hasSecondaryOutput) {
@@ -444,12 +445,12 @@
     }
 }
 
-void GrGLSLProgramBuilder::nameExpression(GrGLSLExpr4* output, const char* baseName) {
+void GrGLSLProgramBuilder::nameExpression(SkString* output, const char* baseName) {
     // create var to hold stage result.  If we already have a valid output name, just use that
     // otherwise create a new mangled one.  This name is only valid if we are reordering stages
     // and have to tell stage exactly where to put its output.
     SkString outName;
-    if (output->isValid()) {
+    if (output->size()) {
         outName = output->c_str();
     } else {
         this->nameVariable(&outName, '\0', baseName);
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 7486646..933d422 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -22,7 +22,7 @@
 
 class GrShaderVar;
 class GrGLSLVaryingHandler;
-class GrGLSLExpr4;
+class SkString;
 class GrShaderCaps;
 
 typedef SkSTArray<8, GrGLSLFragmentProcessor*, true> GrGLSLFragProcs;
@@ -31,6 +31,7 @@
 public:
     using UniformHandle      = GrGLSLUniformHandler::UniformHandle;
     using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using TexelBufferHandle  = GrGLSLUniformHandler::TexelBufferHandle;
     using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     virtual ~GrGLSLProgramBuilder() {}
@@ -53,6 +54,10 @@
         return this->uniformHandler()->samplerSwizzle(handle);
     }
 
+    const GrShaderVar& texelBufferVariable(TexelBufferHandle handle) const {
+        return this->uniformHandler()->texelBufferVariable(handle);
+    }
+
     const GrShaderVar& imageStorageVariable(ImageStorageHandle handle) const {
         return this->uniformHandler()->imageStorageVariable(handle);
     }
@@ -110,7 +115,7 @@
 
     void addFeature(GrShaderFlags shaders, uint32_t featureBit, const char* extensionName);
 
-    bool emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage);
+    bool emitAndInstallProcs();
 
     void cleanupFragmentProcessors();
 
@@ -140,31 +145,29 @@
     };
 
     // Generates a possibly mangled name for a stage variable and writes it to the fragment shader.
-    // If GrGLSLExpr4 has a valid name then it will use that instead
-    void nameExpression(GrGLSLExpr4*, const char* baseName);
+    void nameExpression(SkString*, const char* baseName);
 
     void emitAndInstallPrimProc(const GrPrimitiveProcessor&,
-                                GrGLSLExpr4* outputColor,
-                                GrGLSLExpr4* outputCoverage);
-    void emitAndInstallFragProcs(GrGLSLExpr4* colorInOut, GrGLSLExpr4* coverageInOut);
-    void emitAndInstallFragProc(const GrFragmentProcessor&,
-                                int index,
-                                int transformedCoordVarsIdx,
-                                const GrGLSLExpr4& input,
-                                GrGLSLExpr4* output);
-    void emitAndInstallXferProc(const GrXferProcessor&,
-                                const GrGLSLExpr4& colorIn,
-                                const GrGLSLExpr4& coverageIn);
-    void emitSamplersAndImageStorages(const GrProcessor& processor,
+                                SkString* outputColor,
+                                SkString* outputCoverage);
+    void emitAndInstallFragProcs(SkString* colorInOut, SkString* coverageInOut);
+    SkString emitAndInstallFragProc(const GrFragmentProcessor&,
+                                    int index,
+                                    int transformedCoordVarsIdx,
+                                    const SkString& input,
+                                    SkString output);
+    void emitAndInstallXferProc(const SkString& colorIn, const SkString& coverageIn);
+    void emitSamplersAndImageStorages(const GrResourceIOProcessor& processor,
                                       SkTArray<SamplerHandle>* outTexSamplerHandles,
-                                      SkTArray<SamplerHandle>* outBufferSamplerHandles,
+                                      SkTArray<TexelBufferHandle>* outTexelBufferHandles,
                                       SkTArray<ImageStorageHandle>* outImageStorageHandles);
-    void emitSampler(GrSLType samplerType, GrPixelConfig, const char* name,
-                     GrShaderFlags visibility, SkTArray<SamplerHandle >* outSamplerHandles);
-    void emitImageStorage(const GrProcessor::ImageStorageAccess&,
-                          const char* name,
-                          SkTArray<ImageStorageHandle>* outImageStorageHandles);
+    SamplerHandle emitSampler(GrSLType samplerType, GrPixelConfig, const char* name,
+                              GrShaderFlags visibility);
+    TexelBufferHandle emitTexelBuffer(GrPixelConfig, const char* name, GrShaderFlags visibility);
+    ImageStorageHandle emitImageStorage(const GrResourceIOProcessor::ImageStorageAccess&,
+                                        const char* name);
     void emitFSOutputSwizzle(bool hasSecondaryOutput);
+    void updateSamplerCounts(GrShaderFlags visibility);
     bool checkSamplerCounts();
     bool checkImageStorageCounts();
 
@@ -174,6 +177,8 @@
     void verify(const GrFragmentProcessor&);
 #endif
 
+    // These are used to check that we don't excede the allowable number of resources in a shader.
+    // The sampler counts include both normal texure samplers as well as texel buffers.
     int                         fNumVertexSamplers;
     int                         fNumGeometrySamplers;
     int                         fNumFragmentSamplers;
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index 8658766..542cee6 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -72,18 +72,7 @@
                                               const char* coordName,
                                               GrSLType varyingType) const {
     const GrShaderVar& sampler = fProgramBuilder->samplerVariable(samplerHandle);
-    GrSLType samplerType = sampler.getType();
-    if (samplerType == kTexture2DRectSampler_GrSLType) {
-        if (varyingType == kVec2f_GrSLType) {
-            out->appendf("texture(%s, textureSize(%s) * %s)",
-                         sampler.c_str(), sampler.c_str(), coordName);
-        } else {
-            out->appendf("texture(%s, vec3(textureSize(%s) * %s.xy, %s.z))",
-                         sampler.c_str(), sampler.c_str(), coordName, coordName);
-        }
-    } else {
-        out->appendf("texture(%s, %s)", sampler.c_str(), coordName);
-    }
+    out->appendf("texture(%s, %s)", sampler.c_str(), coordName);
     append_texture_swizzle(out, fProgramBuilder->samplerSwizzle(samplerHandle));
 }
 
@@ -112,9 +101,17 @@
     if (colorXformHelper && colorXformHelper->isValid()) {
         SkString xform;
         this->appendColorGamutXform(&xform, lookup.c_str(), colorXformHelper);
-        this->codeAppend((GrGLSLExpr4(modulation) * GrGLSLExpr4(xform)).c_str());
+        if (modulation) {
+            this->codeAppendf("%s * %s", modulation, xform.c_str());
+        } else {
+            this->codeAppendf("%s", xform.c_str());
+        }
     } else {
-        this->codeAppend((GrGLSLExpr4(modulation) * GrGLSLExpr4(lookup)).c_str());
+        if (modulation) {
+            this->codeAppendf("%s * %s", modulation, lookup.c_str());
+        } else {
+            this->codeAppendf("%s", lookup.c_str());
+        }
     }
 }
 
@@ -154,19 +151,17 @@
 }
 
 void GrGLSLShaderBuilder::appendTexelFetch(SkString* out,
-                                           SamplerHandle samplerHandle,
+                                           TexelBufferHandle texelBufferHandle,
                                            const char* coordExpr) const {
-    const GrShaderVar& sampler = fProgramBuilder->samplerVariable(samplerHandle);
+    const GrShaderVar& texelBuffer = fProgramBuilder->texelBufferVariable(texelBufferHandle);
     SkASSERT(fProgramBuilder->shaderCaps()->texelFetchSupport());
-    SkASSERT(GrSLTypeIsCombinedSamplerType(sampler.getType()));
 
-    out->appendf("texelFetch(%s, %s)", sampler.c_str(), coordExpr);
-
-    append_texture_swizzle(out, fProgramBuilder->samplerSwizzle(samplerHandle));
+    out->appendf("texelFetch(%s, %s)", texelBuffer.c_str(), coordExpr);
 }
 
-void GrGLSLShaderBuilder::appendTexelFetch(SamplerHandle samplerHandle, const char* coordExpr) {
-    this->appendTexelFetch(&this->code(), samplerHandle, coordExpr);
+void GrGLSLShaderBuilder::appendTexelFetch(TexelBufferHandle texelBufferHandle,
+                                           const char* coordExpr) {
+    this->appendTexelFetch(&this->code(), texelBufferHandle, coordExpr);
 }
 
 void GrGLSLShaderBuilder::appendImageStorageLoad(SkString* out, ImageStorageHandle handle,
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index 2d6aaf0..16e5b86 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -26,6 +26,7 @@
     virtual ~GrGLSLShaderBuilder() {}
 
     using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
+    using TexelBufferHandle  = GrGLSLUniformHandler::TexelBufferHandle;
     using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     /** Appends a 2D texture sample with projection if necessary. coordType must either be Vec2f or
@@ -68,10 +69,10 @@
     /** Fetches an unfiltered texel from a sampler at integer coordinates. coordExpr must match the
         dimensionality of the sampler and must be within the sampler's range. coordExpr is emitted
         exactly once, so expressions like "idx++" are acceptable. */
-    void appendTexelFetch(SkString* out, SamplerHandle, const char* coordExpr) const;
+    void appendTexelFetch(SkString* out, TexelBufferHandle, const char* coordExpr) const;
 
     /** Version of above that appends the result to the shader code instead.*/
-    void appendTexelFetch(SamplerHandle, const char* coordExpr);
+    void appendTexelFetch(TexelBufferHandle, const char* coordExpr);
 
     /** Creates a string of shader code that performs an image load. */
     void appendImageStorageLoad(SkString* out, ImageStorageHandle, const char* coordExpr);
diff --git a/src/gpu/glsl/GrGLSLUniformHandler.h b/src/gpu/glsl/GrGLSLUniformHandler.h
index 3d21c1c..cf80c3f 100644
--- a/src/gpu/glsl/GrGLSLUniformHandler.h
+++ b/src/gpu/glsl/GrGLSLUniformHandler.h
@@ -20,6 +20,7 @@
 
     using UniformHandle = GrGLSLProgramDataManager::UniformHandle;
     GR_DEFINE_RESOURCE_HANDLE_CLASS(SamplerHandle);
+    GR_DEFINE_RESOURCE_HANDLE_CLASS(TexelBufferHandle);
     GR_DEFINE_RESOURCE_HANDLE_CLASS(ImageStorageHandle);
 
     /** Add a uniform variable to the current program, that has visibility in one or more shaders.
@@ -68,6 +69,10 @@
     virtual SamplerHandle addSampler(uint32_t visibility, GrSwizzle, GrSLType, GrSLPrecision,
                                      const char* name) = 0;
 
+    virtual const GrShaderVar& texelBufferVariable(TexelBufferHandle) const = 0;
+    virtual TexelBufferHandle addTexelBuffer(uint32_t visibility, GrSLPrecision,
+                                             const char* name) = 0;
+
     virtual const GrShaderVar& imageStorageVariable(ImageStorageHandle) const = 0;
     virtual ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType type,
                                                GrImageStorageFormat, GrSLMemoryModel, GrSLRestrict,
diff --git a/src/gpu/glsl/GrGLSLVarying.cpp b/src/gpu/glsl/GrGLSLVarying.cpp
index 7d841f5..ef3fe8a 100644
--- a/src/gpu/glsl/GrGLSLVarying.cpp
+++ b/src/gpu/glsl/GrGLSLVarying.cpp
@@ -42,7 +42,7 @@
 
     SkASSERT(varying);
     v.fType = varying->fType;
-    v.fPrecision = precision;
+    v.fPrecision = (kDefault_GrSLPrecision == precision) ? kMedium_GrSLPrecision : precision;
     v.fIsFlat = flat;
     fProgramBuilder->nameVariable(&v.fVsOut, 'v', name);
     v.fVisibility = kNone_GrShaderFlags;
diff --git a/src/gpu/glsl/GrGLSLXferProcessor.cpp b/src/gpu/glsl/GrGLSLXferProcessor.cpp
index 4101080..41a4677 100644
--- a/src/gpu/glsl/GrGLSLXferProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLXferProcessor.cpp
@@ -13,8 +13,22 @@
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 
+// This is only called for cases where we are doing LCD coverage and not using in shader blending.
+// For these cases we assume the the src alpha is 1, thus we can just use the max for the alpha
+// coverage since src alpha will always be greater than or equal to dst alpha.
+static void adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder* fragBuilder,
+                                    const char* srcCoverage,
+                                    const GrXferProcessor& proc) {
+    if (srcCoverage && proc.isLCD()) {
+        fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);",
+                                 srcCoverage, srcCoverage, srcCoverage, srcCoverage);
+    }
+}
+
+
 void GrGLSLXferProcessor::emitCode(const EmitArgs& args) {
     if (!args.fXP.willReadDstColor()) {
+        adjust_for_lcd_coverage(args.fXPFragBuilder, args.fInputCoverage, args.fXP);
         this->emitOutputsForBlendState(args);
         return;
     }
@@ -25,13 +39,19 @@
 
     bool needsLocalOutColor = false;
 
-    if (args.fXP.getDstTexture()) {
-        bool topDown = kTopLeft_GrSurfaceOrigin == args.fXP.getDstTexture()->origin();
+    if (args.fDstTextureSamplerHandle.isValid()) {
+        bool flipY = kBottomLeft_GrSurfaceOrigin == args.fDstTextureOrigin;
 
         if (args.fInputCoverage) {
             // We don't think any shaders actually output negative coverage, but just as a safety
-            // check for floating point precision errors we compare with <= here
-            fragBuilder->codeAppendf("if (all(lessThanEqual(%s, vec4(0)))) {"
+            // check for floating point precision errors we compare with <= here. We just check the
+            // rgb values of the coverage since the alpha may not have been set when using lcd. If
+            // we are using single channel coverage alpha will equal to rgb anyways.
+            //
+            // The discard here also helps for batching text draws together which need to read from
+            // a dst copy for blends. Though this only helps the case where the outer bounding boxes
+            // of each letter overlap and not two actually parts of the text.
+            fragBuilder->codeAppendf("if (all(lessThanEqual(%s.rgb, vec3(0)))) {"
                                      "    discard;"
                                      "}", args.fInputCoverage);
         }
@@ -54,12 +74,13 @@
         fragBuilder->codeAppendf("vec2 _dstTexCoord = (sk_FragCoord.xy - %s) * %s;",
                                  dstTopLeftName, dstCoordScaleName);
 
-        if (!topDown) {
+        if (flipY) {
             fragBuilder->codeAppend("_dstTexCoord.y = 1.0 - _dstTexCoord.y;");
         }
 
         fragBuilder->codeAppendf("vec4 %s = ", dstColor);
-        fragBuilder->appendTextureLookup(args.fTexSamplers[0], "_dstTexCoord", kVec2f_GrSLType);
+        fragBuilder->appendTextureLookup(args.fDstTextureSamplerHandle, "_dstTexCoord",
+                                         kVec2f_GrSLType);
         fragBuilder->codeAppend(";");
     } else {
         needsLocalOutColor = args.fShaderCaps->requiresLocalOutputColorForFBFetch();
@@ -85,13 +106,13 @@
     }
 }
 
-void GrGLSLXferProcessor::setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp) {
-    if (xp.getDstTexture()) {
+void GrGLSLXferProcessor::setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp,
+                                  const GrTexture* dstTexture, const SkIPoint& dstTextureOffset) {
+    if (dstTexture) {
         if (fDstTopLeftUni.isValid()) {
-            pdm.set2f(fDstTopLeftUni, static_cast<float>(xp.dstTextureOffset().fX),
-                      static_cast<float>(xp.dstTextureOffset().fY));
-            pdm.set2f(fDstScaleUni, 1.f / xp.getDstTexture()->width(),
-                      1.f / xp.getDstTexture()->height());
+            pdm.set2f(fDstTopLeftUni, static_cast<float>(dstTextureOffset.fX),
+                      static_cast<float>(dstTextureOffset.fY));
+            pdm.set2f(fDstScaleUni, 1.f / dstTexture->width(), 1.f / dstTexture->height());
         } else {
             SkASSERT(!fDstScaleUni.isValid());
         }
@@ -110,13 +131,29 @@
                                                     const GrXferProcessor& proc) {
     if (proc.dstReadUsesMixedSamples()) {
         if (srcCoverage) {
+            // TODO: Once we are no longer using legacy mesh ops, it will not be possible to even
+            // create a mixed sample with lcd so we can uncomment the below assert. In practice
+            // today this never happens except for GLPrograms test which can make one. skia:6661
+            // SkASSERT(!proc.isLCD());
             fragBuilder->codeAppendf("%s *= %s;", outColor, srcCoverage);
             fragBuilder->codeAppendf("%s = %s;", outColorSecondary, srcCoverage);
         } else {
             fragBuilder->codeAppendf("%s = vec4(1.0);", outColorSecondary);
         }
     } else if (srcCoverage) {
+        if (proc.isLCD()) {
+            fragBuilder->codeAppendf("float lerpRed = mix(%s.a, %s.a, %s.r);",
+                                     dstColor, outColor, srcCoverage);
+            fragBuilder->codeAppendf("float lerpBlue = mix(%s.a, %s.a, %s.g);",
+                                     dstColor, outColor, srcCoverage);
+            fragBuilder->codeAppendf("float lerpGreen = mix(%s.a, %s.a, %s.b);",
+                                     dstColor, outColor, srcCoverage);
+        }
         fragBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
                                  outColor, srcCoverage, outColor, srcCoverage, dstColor);
+        if (proc.isLCD()) {
+            fragBuilder->codeAppendf("%s.a = max(max(lerpRed, lerpBlue), lerpGreen);", outColor);
+        }
     }
 }
+
diff --git a/src/gpu/glsl/GrGLSLXferProcessor.h b/src/gpu/glsl/GrGLSLXferProcessor.h
index b4bde37..791bb06 100644
--- a/src/gpu/glsl/GrGLSLXferProcessor.h
+++ b/src/gpu/glsl/GrGLSLXferProcessor.h
@@ -8,6 +8,7 @@
 #ifndef GrGLSLXferProcessor_DEFINED
 #define GrGLSLXferProcessor_DEFINED
 
+#include "SkPoint.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 
@@ -15,14 +16,15 @@
 class GrGLSLXPBuilder;
 class GrGLSLXPFragmentBuilder;
 class GrShaderCaps;
+class GrTexture;
 
 class GrGLSLXferProcessor {
 public:
     GrGLSLXferProcessor() {}
     virtual ~GrGLSLXferProcessor() {}
 
-    using SamplerHandle        = GrGLSLUniformHandler::SamplerHandle;
-    using ImageStorageHandle   = GrGLSLUniformHandler::ImageStorageHandle;
+    using SamplerHandle = GrGLSLUniformHandler::SamplerHandle;
+    using ImageStorageHandle = GrGLSLUniformHandler::ImageStorageHandle;
 
     struct EmitArgs {
         EmitArgs(GrGLSLXPFragmentBuilder* fragBuilder,
@@ -33,9 +35,8 @@
                  const char* inputCoverage,
                  const char* outputPrimary,
                  const char* outputSecondary,
-                 const SamplerHandle* texSamplers,
-                 const SamplerHandle* bufferSamplers,
-                 const ImageStorageHandle* imageStorages)
+                 const SamplerHandle dstTextureSamplerHandle,
+                 GrSurfaceOrigin dstTextureOrigin)
                 : fXPFragBuilder(fragBuilder)
                 , fUniformHandler(uniformHandler)
                 , fShaderCaps(caps)
@@ -44,10 +45,8 @@
                 , fInputCoverage(inputCoverage)
                 , fOutputPrimary(outputPrimary)
                 , fOutputSecondary(outputSecondary)
-                , fTexSamplers(texSamplers)
-                , fBufferSamplers(bufferSamplers)
-                , fImageStorages(imageStorages) {}
-
+                , fDstTextureSamplerHandle(dstTextureSamplerHandle)
+                , fDstTextureOrigin(dstTextureOrigin) {}
         GrGLSLXPFragmentBuilder* fXPFragBuilder;
         GrGLSLUniformHandler* fUniformHandler;
         const GrShaderCaps* fShaderCaps;
@@ -56,9 +55,8 @@
         const char* fInputCoverage;
         const char* fOutputPrimary;
         const char* fOutputSecondary;
-        const SamplerHandle* fTexSamplers;
-        const SamplerHandle* fBufferSamplers;
-        const ImageStorageHandle* fImageStorages;
+        const SamplerHandle fDstTextureSamplerHandle;
+        GrSurfaceOrigin fDstTextureOrigin;
     };
     /**
      * This is similar to emitCode() in the base class, except it takes a full shader builder.
@@ -73,7 +71,8 @@
         to have an identical processor key as the one that created this GrGLSLXferProcessor. This
         function calls onSetData on the subclass of GrGLSLXferProcessor
      */
-    void setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp);
+    void setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp,
+                 const GrTexture* dstTexture, const SkIPoint& dstTextureOffset);
 
 protected:
     static void DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fragBuilder,
diff --git a/src/gpu/instanced/GLInstancedRendering.cpp b/src/gpu/instanced/GLInstancedRendering.cpp
index bfdb960..b2eb9eb 100644
--- a/src/gpu/instanced/GLInstancedRendering.cpp
+++ b/src/gpu/instanced/GLInstancedRendering.cpp
@@ -15,12 +15,13 @@
 
 namespace gr_instanced {
 
-class GLInstancedRendering::GLOp final : public InstancedRendering::Op {
+class GLInstancedOp final : public InstancedOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    GLOp(GLInstancedRendering* instRendering, GrPaint&& paint)
-            : INHERITED(ClassID(), std::move(paint), instRendering) {}
+    GLInstancedOp(GLOpAllocator* alloc, GrPaint&& paint)
+        : INHERITED(ClassID(), std::move(paint), alloc) {
+    }
     int numGLCommands() const { return 1 + fNumChangesInGeometry; }
 
 private:
@@ -29,7 +30,7 @@
 
     friend class GLInstancedRendering;
 
-    typedef Op INHERITED;
+    typedef InstancedOp INHERITED;
 };
 
 GrCaps::InstancedSupport GLInstancedRendering::CheckSupport(const GrGLCaps& glCaps) {
@@ -43,10 +44,10 @@
 }
 
 GLInstancedRendering::GLInstancedRendering(GrGLGpu* gpu)
-    : INHERITED(gpu),
-      fVertexArrayID(0),
-      fGLDrawCmdsInfo(0),
-      fInstanceAttribsBufferUniqueId(SK_InvalidUniqueID) {
+    : INHERITED(gpu)
+    , fVertexArrayID(0)
+    , fGLDrawCmdsInfo(0)
+    , fInstanceAttribsBufferUniqueId(SK_InvalidUniqueID) {
     SkASSERT(GrCaps::InstancedSupport::kNone != this->gpu()->caps()->instancedSupport());
 }
 
@@ -61,8 +62,8 @@
     return static_cast<GrGLGpu*>(this->gpu());
 }
 
-std::unique_ptr<InstancedRendering::Op> GLInstancedRendering::makeOp(GrPaint&& paint) {
-    return std::unique_ptr<Op>(new GLOp(this, std::move(paint)));
+std::unique_ptr<InstancedOp> GLOpAllocator::makeOp(GrPaint&& paint) {
+    return std::unique_ptr<InstancedOp>(new GLInstancedOp(this, std::move(paint)));
 }
 
 void GLInstancedRendering::onBeginFlush(GrResourceProvider* rp) {
@@ -71,8 +72,8 @@
     iter.init(this->trackedOps(), OpList::Iter::kHead_IterStart);
     int numGLInstances = 0;
     int numGLDrawCmds = 0;
-    while (Op* o = iter.get()) {
-        GLOp* op = static_cast<GLOp*>(o);
+    while (InstancedOp* o = iter.get()) {
+        GLInstancedOp* op = (GLInstancedOp*) o;
         iter.next();
 
         numGLInstances += op->fNumDraws;
@@ -152,14 +153,14 @@
 
     // Generate the instance and draw-indirect buffer contents based on the tracked ops.
     iter.init(this->trackedOps(), OpList::Iter::kHead_IterStart);
-    while (Op* o = iter.get()) {
-        GLOp* op = static_cast<GLOp*>(o);
+    while (InstancedOp* o = iter.get()) {
+        GLInstancedOp* op = static_cast<GLInstancedOp*>(o);
         iter.next();
 
         op->fEmulatedBaseInstance = baseInstanceSupport ? 0 : glInstancesIdx;
         op->fGLDrawCmdsIdx = glDrawCmdsIdx;
 
-        const Op::Draw* draw = op->fHeadDraw;
+        const InstancedOp::Draw* draw = op->fHeadDraw;
         SkASSERT(draw);
         do {
             int instanceCount = 0;
@@ -201,7 +202,7 @@
 }
 
 void GLInstancedRendering::onDraw(const GrPipeline& pipeline, const InstanceProcessor& instProc,
-                                  const Op* baseOp) {
+                                  const InstancedOp* baseOp) {
     if (!fDrawIndirectBuffer && !fGLDrawCmdsInfo) {
         return; // beginFlush was not successful.
     }
@@ -214,7 +215,7 @@
     }
 
     const GrGLCaps& glCaps = this->glGpu()->glCaps();
-    const GLOp* op = static_cast<const GLOp*>(baseOp);
+    const GLInstancedOp* op = static_cast<const GLInstancedOp*>(baseOp);
     int numCommands = op->numGLCommands();
 
 #if GR_GL_LOG_INSTANCED_OPS
diff --git a/src/gpu/instanced/GLInstancedRendering.h b/src/gpu/instanced/GLInstancedRendering.h
index d1affba..d68bc86 100644
--- a/src/gpu/instanced/GLInstancedRendering.h
+++ b/src/gpu/instanced/GLInstancedRendering.h
@@ -10,6 +10,7 @@
 
 #include "GrCaps.h"
 #include "gl/GrGLBuffer.h"
+#include "instanced/InstancedOp.h"
 #include "instanced/InstancedRendering.h"
 
 class GrGLCaps;
@@ -19,6 +20,16 @@
 
 namespace gr_instanced {
 
+class GLOpAllocator final : public OpAllocator {
+public:
+    GLOpAllocator(const GrCaps* caps) : INHERITED(caps) {}
+
+private:
+    std::unique_ptr<InstancedOp> makeOp(GrPaint&& paint) override;
+
+    typedef OpAllocator INHERITED;
+};
+
 class GLInstancedRendering final : public InstancedRendering {
 public:
     GLInstancedRendering(GrGLGpu*);
@@ -33,10 +44,8 @@
 
     GrGLGpu* glGpu() const;
 
-    std::unique_ptr<Op> makeOp(GrPaint&& paint) override;
-
     void onBeginFlush(GrResourceProvider*) override;
-    void onDraw(const GrPipeline&, const InstanceProcessor&, const Op*) override;
+    void onDraw(const GrPipeline&, const InstanceProcessor&, const InstancedOp*) override;
     void onEndFlush() override;
     void onResetGpuResources(ResetType) override;
 
@@ -54,8 +63,6 @@
     GrGpuResource::UniqueID               fInstanceAttribsBufferUniqueId;
     int                                   fInstanceAttribsBaseInstance;
 
-    class GLOp;
-
     friend class ::GrGLCaps; // For CheckSupport.
 
     typedef InstancedRendering INHERITED;
diff --git a/src/gpu/instanced/InstanceProcessor.cpp b/src/gpu/instanced/InstanceProcessor.cpp
index efbf825..dd36705 100644
--- a/src/gpu/instanced/InstanceProcessor.cpp
+++ b/src/gpu/instanced/InstanceProcessor.cpp
@@ -104,7 +104,7 @@
           fVertexBuilder(vertexBuilder) {
     }
 
-    void initParams(const SamplerHandle paramsBuffer) {
+    void initParams(const TexelBufferHandle paramsBuffer) {
         fParamsBuffer = paramsBuffer;
         fVertexBuilder->codeAppendf("highp int paramsIdx = int(%s & 0x%x);",
                                     this->attr(Attrib::kInstanceInfo),
@@ -146,7 +146,7 @@
 private:
     const InstanceProcessor&     fInstProc;
     GrGLSLVertexBuilder*         fVertexBuilder;
-    SamplerHandle                fParamsBuffer;
+    TexelBufferHandle            fParamsBuffer;
 };
 
 class GLSLInstanceProcessor::Backend {
@@ -229,7 +229,7 @@
     VertexInputs inputs(ip, v);
     if (ip.opInfo().fHasParams) {
         SkASSERT(1 == ip.numBuffers());
-        inputs.initParams(args.fBufferSamplers[0]);
+        inputs.initParams(args.fTexelBuffers[0]);
     }
 
     if (!ip.opInfo().fHasPerspective) {
@@ -489,7 +489,7 @@
 void GLSLInstanceProcessor::Backend::setupNinePatchRadii(GrGLSLVertexBuilder* v) {
     v->codeAppend("radii = vec2(p[0][corner.x], p[1][corner.y]);");
     if (fNeedsNeighborRadii) {
-        v->codeAppend("neighborRadii = vec2(p[0][1u - corner.x], p[1][1u - corner.y]);");
+        v->codeAppend("neighborRadii = vec2(p[0][1 - corner.x], p[1][1 - corner.y]);");
     }
 }
 
@@ -513,8 +513,8 @@
     v->codeAppend(";");
     v->codeAppend("radii = vec2(p[corner.x][corner.y], p2[corner.y][corner.x]);");
     if (fNeedsNeighborRadii) {
-        v->codeAppend("neighborRadii = vec2(p[1u - corner.x][corner.y], "
-                                           "p2[1u - corner.y][corner.x]);");
+        v->codeAppend("neighborRadii = vec2(p[1 - corner.x][corner.y], "
+                                           "p2[1 - corner.y][corner.x]);");
     }
 }
 
diff --git a/src/gpu/instanced/InstanceProcessor.h b/src/gpu/instanced/InstanceProcessor.h
index 8cde30f..84d75a9 100644
--- a/src/gpu/instanced/InstanceProcessor.h
+++ b/src/gpu/instanced/InstanceProcessor.h
@@ -56,7 +56,7 @@
      */
     static GrCaps::InstancedSupport CheckSupport(const GrShaderCaps&, const GrCaps&);
 
-    OpInfo fOpInfo;
+    const OpInfo fOpInfo;
     BufferAccess fParamsAccess;
 
     friend class GLInstancedRendering; // For CheckSupport.
diff --git a/src/gpu/instanced/InstancedOp.cpp b/src/gpu/instanced/InstancedOp.cpp
new file mode 100644
index 0000000..f0f1cc0
--- /dev/null
+++ b/src/gpu/instanced/InstancedOp.cpp
@@ -0,0 +1,469 @@
+/*
+ * 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 "InstancedOp.h"
+#include "InstanceProcessor.h"
+#include "InstancedRendering.h"
+
+#include "GrOpFlushState.h"
+#include "GrRenderTargetOpList.h"
+
+
+namespace gr_instanced {
+
+InstancedOp::InstancedOp(uint32_t classID, GrPaint&& paint, OpAllocator* alloc)
+        : INHERITED(classID)
+        , fIsTracked(false)
+        , fRequiresBarrierOnOverlap(false)
+        , fAllowsSRGBInputs(paint.getAllowSRGBInputs())
+        , fDisableSRGBOutputConversion(paint.getDisableOutputConversionToSRGB())
+        , fNumDraws(1)
+        , fNumChangesInGeometry(0)
+        , fAllocator(alloc)
+        , fInstancedRendering(nullptr)
+        , fProcessors(std::move(paint)) {
+    fHeadDraw = fTailDraw = alloc->allocateDraw();
+#ifdef SK_DEBUG
+    fHeadDraw->fGeometry = {-1, 0};
+#endif
+    fHeadDraw->fNext = nullptr;
+}
+
+InstancedOp::~InstancedOp() {
+    if (fIsTracked) {
+        fInstancedRendering->removeOp(this);
+    }
+
+    Draw* draw = fHeadDraw;
+    while (draw) {
+        Draw* next = draw->fNext;
+        fAllocator->releaseDraw(draw);
+        draw = next;
+    }
+}
+
+void InstancedOp::appendRRectParams(const SkRRect& rrect) {
+    SkASSERT(!fIsTracked);
+    switch (rrect.getType()) {
+        case SkRRect::kSimple_Type: {
+            const SkVector& radii = rrect.getSimpleRadii();
+            this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height());
+            return;
+        }
+        case SkRRect::kNinePatch_Type: {
+            float twoOverW = 2 / rrect.width();
+            float twoOverH = 2 / rrect.height();
+            const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
+            const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
+            this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBR.x() * twoOverW,
+                                    radiiTL.y() * twoOverH, radiiBR.y() * twoOverH);
+            return;
+        }
+        case SkRRect::kComplex_Type: {
+            /**
+             * The x and y radii of each arc are stored in separate vectors,
+             * in the following order:
+             *
+             *        __x1 _ _ _ x3__
+             *    y1 |               | y2
+             *
+             *       |               |
+             *
+             *    y3 |__   _ _ _   __| y4
+             *          x2       x4
+             *
+             */
+            float twoOverW = 2 / rrect.width();
+            float twoOverH = 2 / rrect.height();
+            const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
+            const SkVector& radiiTR = rrect.radii(SkRRect::kUpperRight_Corner);
+            const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
+            const SkVector& radiiBL = rrect.radii(SkRRect::kLowerLeft_Corner);
+            this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBL.x() * twoOverW,
+                                    radiiTR.x() * twoOverW, radiiBR.x() * twoOverW);
+            this->appendParamsTexel(radiiTL.y() * twoOverH, radiiTR.y() * twoOverH,
+                                    radiiBL.y() * twoOverH, radiiBR.y() * twoOverH);
+            return;
+        }
+        default: return;
+    }
+}
+
+void InstancedOp::appendParamsTexel(const SkScalar* vals, int count) {
+    SkASSERT(!fIsTracked);
+    SkASSERT(count <= 4 && count >= 0);
+    const float* valsAsFloats = vals; // Ensure SkScalar == float.
+    memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float));
+    fInfo.fHasParams = true;
+}
+
+void InstancedOp::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w) {
+    SkASSERT(!fIsTracked);
+    ParamsTexel& texel = fParams.push_back();
+    texel.fX = SkScalarToFloat(x);
+    texel.fY = SkScalarToFloat(y);
+    texel.fZ = SkScalarToFloat(z);
+    texel.fW = SkScalarToFloat(w);
+    fInfo.fHasParams = true;
+}
+
+void InstancedOp::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z) {
+    SkASSERT(!fIsTracked);
+    ParamsTexel& texel = fParams.push_back();
+    texel.fX = SkScalarToFloat(x);
+    texel.fY = SkScalarToFloat(y);
+    texel.fZ = SkScalarToFloat(z);
+    fInfo.fHasParams = true;
+}
+
+bool InstancedOp::xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) {
+    GrProcessorAnalysisCoverage coverageInput;
+    bool isMixedSamples = false;
+    if (GrAAType::kCoverage == fInfo.aaType() ||
+        (GrAAType::kNone == fInfo.aaType() && !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
+        coverageInput = GrProcessorAnalysisCoverage::kSingleChannel;
+    } else {
+        coverageInput = GrProcessorAnalysisCoverage::kNone;
+        isMixedSamples = GrAAType::kMixedSamples == fInfo.aaType();
+    }
+    GrProcessorSet::Analysis analysis =
+            fProcessors.finalize(this->getSingleInstance().fColor, coverageInput, clip,
+                                 isMixedSamples, caps, &this->getSingleDraw().fInstance.fColor);
+
+    Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
+    SkASSERT(draw.fGeometry.isEmpty());
+    SkASSERT(SkIsPow2(fInfo.fShapeTypes));
+    SkASSERT(!fIsTracked);
+
+    if (kRect_ShapeFlag == fInfo.fShapeTypes) {
+        draw.fGeometry = InstanceProcessor::GetIndexRangeForRect(fInfo.aaType());
+    } else if (kOval_ShapeFlag == fInfo.fShapeTypes) {
+        draw.fGeometry = InstanceProcessor::GetIndexRangeForOval(fInfo.aaType(), this->bounds());
+    } else {
+        draw.fGeometry = InstanceProcessor::GetIndexRangeForRRect(fInfo.aaType());
+    }
+
+    fInfo.fCannotTweakAlphaForCoverage = !analysis.isCompatibleWithCoverageAsAlpha();
+
+    fInfo.fUsesLocalCoords = analysis.usesLocalCoords();
+    fRequiresBarrierOnOverlap = analysis.requiresBarrierBetweenOverlappingDraws();
+    return analysis.requiresDstTexture();
+}
+
+void InstancedOp::wasRecorded(GrRenderTargetOpList* opList) {
+    SkASSERT(!fInstancedRendering);
+    SkASSERT(!fIsTracked);
+
+    fInstancedRendering = opList->instancedRendering();
+
+    this->getSingleInstance().fInfo |= fInstancedRendering->addOpParams(this);
+    fInstancedRendering->addOp(this);
+    fIsTracked = true;
+}
+
+bool InstancedOp::onCombineIfPossible(GrOp* other, const GrCaps&) {
+    InstancedOp* that = static_cast<InstancedOp*>(other);
+    SkASSERT(!that->fInstancedRendering || (fInstancedRendering == that->fInstancedRendering));
+    SkASSERT(fTailDraw);
+    SkASSERT(that->fTailDraw);
+
+    if (!OpInfo::CanCombine(fInfo, that->fInfo) || fProcessors != that->fProcessors) {
+        return false;
+    }
+
+    if (fAllowsSRGBInputs != that->fAllowsSRGBInputs ||
+        fDisableSRGBOutputConversion != that->fDisableSRGBOutputConversion) {
+        return false;
+    }
+    SkASSERT(fRequiresBarrierOnOverlap == that->fRequiresBarrierOnOverlap);
+    if (fRequiresBarrierOnOverlap && this->bounds().intersects(that->bounds())) {
+        return false;
+    }
+    OpInfo combinedInfo = fInfo | that->fInfo;
+    if (!combinedInfo.isSimpleRects()) {
+        // This threshold was chosen with the "shapes_mixed" bench on a MacBook with Intel graphics.
+        // There seems to be a wide range where it doesn't matter if we combine or not. What matters
+        // is that the itty bitty rects combine with other shapes and the giant ones don't.
+        constexpr SkScalar kMaxPixelsToGeneralizeRects = 256 * 256;
+        if (fInfo.isSimpleRects() && fPixelLoad > kMaxPixelsToGeneralizeRects) {
+            return false;
+        }
+        if (that->fInfo.isSimpleRects() && that->fPixelLoad > kMaxPixelsToGeneralizeRects) {
+            return false;
+        }
+    }
+
+    if (!that->fInstancedRendering) {
+        that->fInstancedRendering = fInstancedRendering;
+        that->getSingleInstance().fInfo |= fInstancedRendering->addOpParams(that);
+    }
+
+    this->joinBounds(*that);
+    fInfo = combinedInfo;
+    fPixelLoad += that->fPixelLoad;
+    // Adopt the other op's draws.
+    fNumDraws += that->fNumDraws;
+    fNumChangesInGeometry += that->fNumChangesInGeometry;
+    if (fTailDraw->fGeometry != that->fHeadDraw->fGeometry) {
+        ++fNumChangesInGeometry;
+    }
+    fTailDraw->fNext = that->fHeadDraw;
+    fTailDraw = that->fTailDraw;
+
+    that->fHeadDraw = that->fTailDraw = nullptr;
+
+    return true;
+}
+
+void InstancedOp::onExecute(GrOpFlushState* state) {
+    SkASSERT(fInstancedRendering->isFlushing());
+    SkASSERT(state->gpu() == fInstancedRendering->gpu());
+
+    state->gpu()->handleDirtyContext();
+
+    GrPipeline pipeline;
+    GrPipeline::InitArgs args;
+    args.fAppliedClip = state->drawOpArgs().fAppliedClip;
+    args.fCaps = &state->caps();
+    args.fResourceProvider = state->resourceProvider();
+    args.fProcessors = &fProcessors;
+    args.fFlags = GrAATypeIsHW(fInfo.aaType()) ? GrPipeline::kHWAntialias_Flag : 0;
+    if (fAllowsSRGBInputs) {
+        args.fFlags |= GrPipeline::kAllowSRGBInputs_Flag;
+    }
+    if (fDisableSRGBOutputConversion) {
+        args.fFlags |= GrPipeline::kDisableOutputConversionToSRGB_Flag;
+    }
+    args.fRenderTarget = state->drawOpArgs().fRenderTarget;
+    args.fDstProxy = state->drawOpArgs().fDstProxy;
+    pipeline.init(args);
+
+    if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*state->gpu()->caps())) {
+        state->gpu()->xferBarrier(pipeline.getRenderTarget(), barrierType);
+    }
+    fInstancedRendering->draw(pipeline, fInfo, this);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+OpAllocator::OpAllocator(const GrCaps* caps)
+    : fDrawPool(1024, 1024)
+    , fCaps(sk_ref_sp(caps)) {
+}
+
+OpAllocator::~OpAllocator() {}
+
+std::unique_ptr<GrDrawOp> OpAllocator::recordRect(const SkRect& rect,
+                                                  const SkMatrix& viewMatrix,
+                                                  GrPaint&& paint, GrAA aa,
+                                                  const GrInstancedPipelineInfo& info) {
+    return this->recordShape(ShapeType::kRect, rect, viewMatrix, std::move(paint), rect, aa, info);
+}
+
+std::unique_ptr<GrDrawOp> OpAllocator::recordRect(const SkRect& rect,
+                                                  const SkMatrix& viewMatrix,
+                                                  GrPaint&& paint, const SkRect& localRect,
+                                                  GrAA aa,
+                                                  const GrInstancedPipelineInfo& info) {
+    return this->recordShape(ShapeType::kRect, rect, viewMatrix, std::move(paint), localRect, aa,
+                             info);
+}
+
+std::unique_ptr<GrDrawOp> OpAllocator::recordRect(const SkRect& rect,
+                                                  const SkMatrix& viewMatrix,
+                                                  GrPaint&& paint,
+                                                  const SkMatrix& localMatrix, GrAA aa,
+                                                  const GrInstancedPipelineInfo& info) {
+    if (localMatrix.hasPerspective()) {
+        return nullptr; // Perspective is not yet supported in the local matrix.
+    }
+    if (std::unique_ptr<InstancedOp> op = this->recordShape(ShapeType::kRect, rect, viewMatrix,
+                                                            std::move(paint), rect, aa, info)) {
+        op->getSingleInstance().fInfo |= kLocalMatrix_InfoFlag;
+        op->appendParamsTexel(localMatrix.getScaleX(), localMatrix.getSkewX(),
+                              localMatrix.getTranslateX());
+        op->appendParamsTexel(localMatrix.getSkewY(), localMatrix.getScaleY(),
+                              localMatrix.getTranslateY());
+        op->fInfo.fHasLocalMatrix = true;
+        return std::move(op);
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GrDrawOp> OpAllocator::recordOval(const SkRect& oval,
+                                                  const SkMatrix& viewMatrix,
+                                                  GrPaint&& paint, GrAA aa,
+                                                  const GrInstancedPipelineInfo& info) {
+    return this->recordShape(ShapeType::kOval, oval, viewMatrix, std::move(paint), oval, aa, info);
+}
+
+std::unique_ptr<GrDrawOp> OpAllocator::recordRRect(const SkRRect& rrect,
+                                                   const SkMatrix& viewMatrix,
+                                                   GrPaint&& paint, GrAA aa,
+                                                   const GrInstancedPipelineInfo& info) {
+    if (std::unique_ptr<InstancedOp> op =
+                this->recordShape(GetRRectShapeType(rrect), rrect.rect(), viewMatrix,
+                                  std::move(paint), rrect.rect(), aa, info)) {
+        op->appendRRectParams(rrect);
+        return std::move(op);
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GrDrawOp> OpAllocator::recordDRRect(const SkRRect& outer,
+                                                    const SkRRect& inner,
+                                                    const SkMatrix& viewMatrix,
+                                                    GrPaint&& paint, GrAA aa,
+                                                    const GrInstancedPipelineInfo& info) {
+    if (inner.getType() > SkRRect::kSimple_Type) {
+       return nullptr; // Complex inner round rects are not yet supported.
+    }
+    if (SkRRect::kEmpty_Type == inner.getType()) {
+        return this->recordRRect(outer, viewMatrix, std::move(paint), aa, info);
+    }
+    if (std::unique_ptr<InstancedOp> op =
+                this->recordShape(GetRRectShapeType(outer), outer.rect(), viewMatrix,
+                                  std::move(paint), outer.rect(), aa, info)) {
+        op->appendRRectParams(outer);
+        ShapeType innerShapeType = GetRRectShapeType(inner);
+        op->fInfo.fInnerShapeTypes |= GetShapeFlag(innerShapeType);
+        op->getSingleInstance().fInfo |= ((int)innerShapeType << kInnerShapeType_InfoBit);
+        op->appendParamsTexel(inner.rect().asScalars(), 4);
+        op->appendRRectParams(inner);
+        return std::move(op);
+    }
+    return nullptr;
+}
+
+std::unique_ptr<InstancedOp> OpAllocator::recordShape(
+        ShapeType type, const SkRect& bounds, const SkMatrix& viewMatrix, GrPaint&& paint,
+        const SkRect& localRect, GrAA aa, const GrInstancedPipelineInfo& info) {
+
+    if (info.fIsRenderingToFloat && fCaps->avoidInstancedDrawsToFPTargets()) {
+        return nullptr;
+    }
+
+    GrAAType aaType;
+    if (!this->selectAntialiasMode(viewMatrix, aa, info, &aaType)) {
+        return nullptr;
+    }
+
+    GrColor color = paint.getColor();
+    std::unique_ptr<InstancedOp> op = this->makeOp(std::move(paint));
+    op->fInfo.setAAType(aaType);
+    op->fInfo.fShapeTypes = GetShapeFlag(type);
+    op->fInfo.fCannotDiscard = true;
+    Instance& instance = op->getSingleInstance();
+    instance.fInfo = (int)type << kShapeType_InfoBit;
+
+    InstancedOp::HasAABloat aaBloat =
+            (aaType == GrAAType::kCoverage) ? InstancedOp::HasAABloat::kYes
+                                            : InstancedOp::HasAABloat::kNo;
+    InstancedOp::IsZeroArea zeroArea = bounds.isEmpty() ? InstancedOp::IsZeroArea::kYes
+                                                        : InstancedOp::IsZeroArea::kNo;
+
+    // The instanced shape renderer draws rectangles of [-1, -1, +1, +1], so we find the matrix that
+    // will map this rectangle to the same device coordinates as "viewMatrix * bounds".
+    float sx = 0.5f * bounds.width();
+    float sy = 0.5f * bounds.height();
+    float tx = sx + bounds.fLeft;
+    float ty = sy + bounds.fTop;
+    if (!viewMatrix.hasPerspective()) {
+        float* m = instance.fShapeMatrix2x3;
+        m[0] = viewMatrix.getScaleX() * sx;
+        m[1] = viewMatrix.getSkewX() * sy;
+        m[2] = viewMatrix.getTranslateX() +
+               viewMatrix.getScaleX() * tx + viewMatrix.getSkewX() * ty;
+
+        m[3] = viewMatrix.getSkewY() * sx;
+        m[4] = viewMatrix.getScaleY() * sy;
+        m[5] = viewMatrix.getTranslateY() +
+               viewMatrix.getSkewY() * tx + viewMatrix.getScaleY() * ty;
+
+        // Since 'm' is a 2x3 matrix that maps the rect [-1, +1] into the shape's device-space quad,
+        // it's quite simple to find the bounding rectangle:
+        float devBoundsHalfWidth = fabsf(m[0]) + fabsf(m[1]);
+        float devBoundsHalfHeight = fabsf(m[3]) + fabsf(m[4]);
+        SkRect opBounds;
+        opBounds.fLeft = m[2] - devBoundsHalfWidth;
+        opBounds.fRight = m[2] + devBoundsHalfWidth;
+        opBounds.fTop = m[5] - devBoundsHalfHeight;
+        opBounds.fBottom = m[5] + devBoundsHalfHeight;
+        op->setBounds(opBounds, aaBloat, zeroArea);
+
+        // TODO: Is this worth the CPU overhead?
+        op->fInfo.fNonSquare =
+                fabsf(devBoundsHalfHeight - devBoundsHalfWidth) > 0.5f ||  // Early out.
+                fabs(m[0] * m[3] + m[1] * m[4]) > 1e-3f ||                 // Skew?
+                fabs(m[0] * m[0] + m[1] * m[1] - m[3] * m[3] - m[4] * m[4]) >
+                        1e-2f;  // Diff. lengths?
+    } else {
+        SkMatrix shapeMatrix(viewMatrix);
+        shapeMatrix.preTranslate(tx, ty);
+        shapeMatrix.preScale(sx, sy);
+        instance.fInfo |= kPerspective_InfoFlag;
+
+        float* m = instance.fShapeMatrix2x3;
+        m[0] = SkScalarToFloat(shapeMatrix.getScaleX());
+        m[1] = SkScalarToFloat(shapeMatrix.getSkewX());
+        m[2] = SkScalarToFloat(shapeMatrix.getTranslateX());
+        m[3] = SkScalarToFloat(shapeMatrix.getSkewY());
+        m[4] = SkScalarToFloat(shapeMatrix.getScaleY());
+        m[5] = SkScalarToFloat(shapeMatrix.getTranslateY());
+
+        // Send the perspective column as a param.
+        op->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1],
+                              shapeMatrix[SkMatrix::kMPersp2]);
+        op->fInfo.fHasPerspective = true;
+
+        op->setBounds(bounds, aaBloat, zeroArea);
+        op->fInfo.fNonSquare = true;
+    }
+
+    instance.fColor = color;
+
+    const float* rectAsFloats = localRect.asScalars(); // Ensure SkScalar == float.
+    memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float));
+
+    op->fPixelLoad = op->bounds().height() * op->bounds().width();
+    return op;
+}
+
+inline bool OpAllocator::selectAntialiasMode(const SkMatrix& viewMatrix, GrAA aa,
+                                             const GrInstancedPipelineInfo& info,
+                                             GrAAType* aaType) {
+    SkASSERT(!info.fIsMixedSampled || info.fIsMultisampled);
+    SkASSERT(GrCaps::InstancedSupport::kNone != fCaps->instancedSupport());
+
+    if (!info.fIsMultisampled || fCaps->multisampleDisableSupport()) {
+        if (GrAA::kNo == aa) {
+            *aaType = GrAAType::kNone;
+            return true;
+        }
+
+        if (info.canUseCoverageAA() && viewMatrix.preservesRightAngles()) {
+            *aaType = GrAAType::kCoverage;
+            return true;
+        }
+    }
+
+    if (info.fIsMultisampled &&
+        fCaps->instancedSupport() >= GrCaps::InstancedSupport::kMultisampled) {
+        if (!info.fIsMixedSampled) {
+            *aaType = GrAAType::kMSAA;
+            return true;
+        }
+        if (fCaps->instancedSupport() >= GrCaps::InstancedSupport::kMixedSampled) {
+            *aaType = GrAAType::kMixedSamples;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+}
diff --git a/src/gpu/instanced/InstancedOp.h b/src/gpu/instanced/InstancedOp.h
new file mode 100644
index 0000000..7848eff
--- /dev/null
+++ b/src/gpu/instanced/InstancedOp.h
@@ -0,0 +1,162 @@
+/*
+ * 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 gr_instanced_InstancedOp_DEFINED
+#define gr_instanced_InstancedOp_DEFINED
+
+
+#include "../private/GrInstancedPipelineInfo.h"
+#include "GrMemoryPool.h"
+#include "ops/GrDrawOp.h"
+#include "instanced/InstancedRenderingTypes.h"
+
+#include "SkTInternalLList.h"
+
+namespace gr_instanced {
+
+class InstancedRendering;
+class OpAllocator;
+
+class InstancedOp : public GrDrawOp {
+public:
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(InstancedOp);
+
+    ~InstancedOp() override;
+    const char* name() const override { return "InstancedOp"; }
+
+    SkString dumpInfo() const override {
+        SkString string;
+        string.printf(
+                "AA: %d, ShapeTypes: 0x%02x, IShapeTypes: 0x%02x, Persp %d, "
+                "NonSquare: %d, PLoad: %0.2f, Tracked: %d, NumDraws: %d, "
+                "GeomChanges: %d\n",
+                (unsigned)fInfo.fAAType,
+                fInfo.fShapeTypes,
+                fInfo.fInnerShapeTypes,
+                fInfo.fHasPerspective,
+                fInfo.fNonSquare,
+                fPixelLoad,
+                fIsTracked,
+                fNumDraws,
+                fNumChangesInGeometry);
+        string.append(INHERITED::dumpInfo());
+        return string;
+    }
+
+    struct Draw {
+        Instance     fInstance;
+        IndexRange   fGeometry;
+        Draw*        fNext;
+    };
+
+    Draw& getSingleDraw() const { SkASSERT(fHeadDraw && !fHeadDraw->fNext); return *fHeadDraw; }
+    Instance& getSingleInstance() const { return this->getSingleDraw().fInstance; }
+
+    void appendRRectParams(const SkRRect&);
+    void appendParamsTexel(const SkScalar* vals, int count);
+    void appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w);
+    void appendParamsTexel(SkScalar x, SkScalar y, SkScalar z);
+    FixedFunctionFlags fixedFunctionFlags() const override {
+        return GrAATypeIsHW(fInfo.aaType()) ? FixedFunctionFlags::kUsesHWAA
+                                            : FixedFunctionFlags::kNone;
+    }
+    bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override;
+
+    // Registers the op with the InstancedRendering list of tracked ops.
+    void wasRecorded(GrRenderTargetOpList*) override;
+
+protected:
+    InstancedOp(uint32_t classID, GrPaint&&, OpAllocator*);
+
+    bool fIsTracked : 1;
+    bool fRequiresBarrierOnOverlap : 1;
+    bool fAllowsSRGBInputs : 1;
+    bool fDisableSRGBOutputConversion : 1;
+    int fNumDraws;
+    int fNumChangesInGeometry;
+    Draw* fHeadDraw;
+    Draw* fTailDraw;
+    OpAllocator* fAllocator;
+    InstancedRendering* fInstancedRendering;
+    OpInfo fInfo;
+    SkScalar fPixelLoad;
+    GrProcessorSet fProcessors;
+    SkSTArray<5, ParamsTexel, true> fParams;
+
+private:
+    bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override;
+    void onPrepare(GrOpFlushState*) override {}
+    void onExecute(GrOpFlushState*) override;
+
+    typedef GrDrawOp INHERITED;
+
+    friend class InstancedRendering;
+    friend class OpAllocator;
+};
+
+class OpAllocator : public SkNoncopyable {
+public:
+    virtual ~OpAllocator();
+
+    /**
+     * These methods make a new record internally for an instanced draw, and return an op that is
+     * effectively just an index to that record. The returned op is not self-contained, but
+     * rather relies on this class to handle the rendering. The client must call beginFlush() on
+     * this class before attempting to flush ops returned by it. It is invalid to record new
+     * draws between beginFlush() and endFlush().
+     */
+    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRect(const SkRect&, const SkMatrix&,
+                                                               GrPaint&&, GrAA,
+                                                               const GrInstancedPipelineInfo&);
+
+    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRect(const SkRect&, const SkMatrix&,
+                                                               GrPaint&&, const SkRect& localRect,
+                                                               GrAA,
+                                                               const GrInstancedPipelineInfo&);
+
+    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRect(const SkRect&, const SkMatrix&,
+                                                               GrPaint&&,
+                                                               const SkMatrix& localMatrix, GrAA,
+                                                               const GrInstancedPipelineInfo&);
+
+    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordOval(const SkRect&, const SkMatrix&,
+                                                               GrPaint&&, GrAA,
+                                                               const GrInstancedPipelineInfo&);
+
+    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRRect(const SkRRect&, const SkMatrix&,
+                                                                GrPaint&&, GrAA,
+                                                                const GrInstancedPipelineInfo&);
+
+    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordDRRect(const SkRRect& outer,
+                                                                 const SkRRect& inner,
+                                                                 const SkMatrix&, GrPaint&&, GrAA,
+                                                                 const GrInstancedPipelineInfo&);
+
+    InstancedOp::Draw* allocateDraw() { return fDrawPool.allocate(); }
+    void releaseDraw(InstancedOp::Draw* draw) { fDrawPool.release(draw); }
+
+protected:
+    OpAllocator(const GrCaps*);
+
+private:
+    bool selectAntialiasMode(const SkMatrix& viewMatrix, GrAA aa, const GrInstancedPipelineInfo&,
+                             GrAAType*);
+    virtual std::unique_ptr<InstancedOp> makeOp(GrPaint&&) = 0;
+
+    std::unique_ptr<InstancedOp> SK_WARN_UNUSED_RESULT recordShape(
+                                                          ShapeType, const SkRect& bounds,
+                                                          const SkMatrix& viewMatrix, GrPaint&&,
+                                                          const SkRect& localRect, GrAA aa,
+                                                          const GrInstancedPipelineInfo&);
+
+    GrObjectMemoryPool<InstancedOp::Draw> fDrawPool;
+    sk_sp<const GrCaps>                   fCaps;
+};
+
+}
+
+#endif
diff --git a/src/gpu/instanced/InstancedRendering.cpp b/src/gpu/instanced/InstancedRendering.cpp
index e67a8be..7458437 100644
--- a/src/gpu/instanced/InstancedRendering.cpp
+++ b/src/gpu/instanced/InstancedRendering.cpp
@@ -7,429 +7,28 @@
 
 #include "InstancedRendering.h"
 
+#include "InstancedOp.h"
+#include "GrAppliedClip.h"
 #include "GrCaps.h"
-#include "GrOpFlushState.h"
 #include "GrPipeline.h"
 #include "GrResourceProvider.h"
+
 #include "instanced/InstanceProcessor.h"
 
 namespace gr_instanced {
 
+
 InstancedRendering::InstancedRendering(GrGpu* gpu)
-    : fGpu(SkRef(gpu)),
-      fState(State::kRecordingDraws),
-      fDrawPool(1024, 1024) {
+    : fGpu(SkRef(gpu))
+    SkDEBUGCODE(, fState(State::kRecordingDraws)) {
 }
 
-std::unique_ptr<GrDrawOp> InstancedRendering::recordRect(const SkRect& rect,
-                                                         const SkMatrix& viewMatrix,
-                                                         GrPaint&& paint, GrAA aa,
-                                                         const GrInstancedPipelineInfo& info) {
-    return this->recordShape(ShapeType::kRect, rect, viewMatrix, std::move(paint), rect, aa, info);
-}
-
-std::unique_ptr<GrDrawOp> InstancedRendering::recordRect(const SkRect& rect,
-                                                         const SkMatrix& viewMatrix,
-                                                         GrPaint&& paint, const SkRect& localRect,
-                                                         GrAA aa,
-                                                         const GrInstancedPipelineInfo& info) {
-    return this->recordShape(ShapeType::kRect, rect, viewMatrix, std::move(paint), localRect, aa,
-                             info);
-}
-
-std::unique_ptr<GrDrawOp> InstancedRendering::recordRect(const SkRect& rect,
-                                                         const SkMatrix& viewMatrix,
-                                                         GrPaint&& paint,
-                                                         const SkMatrix& localMatrix, GrAA aa,
-                                                         const GrInstancedPipelineInfo& info) {
-    if (localMatrix.hasPerspective()) {
-        return nullptr; // Perspective is not yet supported in the local matrix.
-    }
-    if (std::unique_ptr<Op> op = this->recordShape(ShapeType::kRect, rect, viewMatrix,
-                                                   std::move(paint), rect, aa, info)) {
-        op->getSingleInstance().fInfo |= kLocalMatrix_InfoFlag;
-        op->appendParamsTexel(localMatrix.getScaleX(), localMatrix.getSkewX(),
-                              localMatrix.getTranslateX());
-        op->appendParamsTexel(localMatrix.getSkewY(), localMatrix.getScaleY(),
-                              localMatrix.getTranslateY());
-        op->fInfo.fHasLocalMatrix = true;
-        return std::move(op);
-    }
-    return nullptr;
-}
-
-std::unique_ptr<GrDrawOp> InstancedRendering::recordOval(const SkRect& oval,
-                                                         const SkMatrix& viewMatrix,
-                                                         GrPaint&& paint, GrAA aa,
-                                                         const GrInstancedPipelineInfo& info) {
-    return this->recordShape(ShapeType::kOval, oval, viewMatrix, std::move(paint), oval, aa, info);
-}
-
-std::unique_ptr<GrDrawOp> InstancedRendering::recordRRect(const SkRRect& rrect,
-                                                          const SkMatrix& viewMatrix,
-                                                          GrPaint&& paint, GrAA aa,
-                                                          const GrInstancedPipelineInfo& info) {
-    if (std::unique_ptr<Op> op =
-                this->recordShape(GetRRectShapeType(rrect), rrect.rect(), viewMatrix,
-                                  std::move(paint), rrect.rect(), aa, info)) {
-        op->appendRRectParams(rrect);
-        return std::move(op);
-    }
-    return nullptr;
-}
-
-std::unique_ptr<GrDrawOp> InstancedRendering::recordDRRect(const SkRRect& outer,
-                                                           const SkRRect& inner,
-                                                           const SkMatrix& viewMatrix,
-                                                           GrPaint&& paint, GrAA aa,
-                                                           const GrInstancedPipelineInfo& info) {
-    if (inner.getType() > SkRRect::kSimple_Type) {
-       return nullptr; // Complex inner round rects are not yet supported.
-    }
-    if (SkRRect::kEmpty_Type == inner.getType()) {
-        return this->recordRRect(outer, viewMatrix, std::move(paint), aa, info);
-    }
-    if (std::unique_ptr<Op> op =
-                this->recordShape(GetRRectShapeType(outer), outer.rect(), viewMatrix,
-                                  std::move(paint), outer.rect(), aa, info)) {
-        op->appendRRectParams(outer);
-        ShapeType innerShapeType = GetRRectShapeType(inner);
-        op->fInfo.fInnerShapeTypes |= GetShapeFlag(innerShapeType);
-        op->getSingleInstance().fInfo |= ((int)innerShapeType << kInnerShapeType_InfoBit);
-        op->appendParamsTexel(inner.rect().asScalars(), 4);
-        op->appendRRectParams(inner);
-        return std::move(op);
-    }
-    return nullptr;
-}
-
-std::unique_ptr<InstancedRendering::Op> InstancedRendering::recordShape(
-        ShapeType type, const SkRect& bounds, const SkMatrix& viewMatrix, GrPaint&& paint,
-        const SkRect& localRect, GrAA aa, const GrInstancedPipelineInfo& info) {
-    SkASSERT(State::kRecordingDraws == fState);
-
-    if (info.fIsRenderingToFloat && fGpu->caps()->avoidInstancedDrawsToFPTargets()) {
-        return nullptr;
-    }
-
-    GrAAType aaType;
-    if (!this->selectAntialiasMode(viewMatrix, aa, info, &aaType)) {
-        return nullptr;
-    }
-
-    GrColor color = paint.getColor();
-    std::unique_ptr<Op> op = this->makeOp(std::move(paint));
-    op->fInfo.setAAType(aaType);
-    op->fInfo.fShapeTypes = GetShapeFlag(type);
-    op->fInfo.fCannotDiscard = true;
-    op->fDrawColorsAreOpaque = GrColorIsOpaque(color);
-    op->fDrawColorsAreSame = true;
-    Instance& instance = op->getSingleInstance();
-    instance.fInfo = (int)type << kShapeType_InfoBit;
-
-    Op::HasAABloat aaBloat =
-            (aaType == GrAAType::kCoverage) ? Op::HasAABloat::kYes : Op::HasAABloat::kNo;
-    Op::IsZeroArea zeroArea = (bounds.isEmpty()) ? Op::IsZeroArea::kYes : Op::IsZeroArea::kNo;
-
-    // The instanced shape renderer draws rectangles of [-1, -1, +1, +1], so we find the matrix that
-    // will map this rectangle to the same device coordinates as "viewMatrix * bounds".
-    float sx = 0.5f * bounds.width();
-    float sy = 0.5f * bounds.height();
-    float tx = sx + bounds.fLeft;
-    float ty = sy + bounds.fTop;
-    if (!viewMatrix.hasPerspective()) {
-        float* m = instance.fShapeMatrix2x3;
-        m[0] = viewMatrix.getScaleX() * sx;
-        m[1] = viewMatrix.getSkewX() * sy;
-        m[2] = viewMatrix.getTranslateX() +
-               viewMatrix.getScaleX() * tx + viewMatrix.getSkewX() * ty;
-
-        m[3] = viewMatrix.getSkewY() * sx;
-        m[4] = viewMatrix.getScaleY() * sy;
-        m[5] = viewMatrix.getTranslateY() +
-               viewMatrix.getSkewY() * tx + viewMatrix.getScaleY() * ty;
-
-        // Since 'm' is a 2x3 matrix that maps the rect [-1, +1] into the shape's device-space quad,
-        // it's quite simple to find the bounding rectangle:
-        float devBoundsHalfWidth = fabsf(m[0]) + fabsf(m[1]);
-        float devBoundsHalfHeight = fabsf(m[3]) + fabsf(m[4]);
-        SkRect opBounds;
-        opBounds.fLeft = m[2] - devBoundsHalfWidth;
-        opBounds.fRight = m[2] + devBoundsHalfWidth;
-        opBounds.fTop = m[5] - devBoundsHalfHeight;
-        opBounds.fBottom = m[5] + devBoundsHalfHeight;
-        op->setBounds(opBounds, aaBloat, zeroArea);
-
-        // TODO: Is this worth the CPU overhead?
-        op->fInfo.fNonSquare =
-                fabsf(devBoundsHalfHeight - devBoundsHalfWidth) > 0.5f ||  // Early out.
-                fabs(m[0] * m[3] + m[1] * m[4]) > 1e-3f ||                 // Skew?
-                fabs(m[0] * m[0] + m[1] * m[1] - m[3] * m[3] - m[4] * m[4]) >
-                        1e-2f;  // Diff. lengths?
-    } else {
-        SkMatrix shapeMatrix(viewMatrix);
-        shapeMatrix.preTranslate(tx, ty);
-        shapeMatrix.preScale(sx, sy);
-        instance.fInfo |= kPerspective_InfoFlag;
-
-        float* m = instance.fShapeMatrix2x3;
-        m[0] = SkScalarToFloat(shapeMatrix.getScaleX());
-        m[1] = SkScalarToFloat(shapeMatrix.getSkewX());
-        m[2] = SkScalarToFloat(shapeMatrix.getTranslateX());
-        m[3] = SkScalarToFloat(shapeMatrix.getSkewY());
-        m[4] = SkScalarToFloat(shapeMatrix.getScaleY());
-        m[5] = SkScalarToFloat(shapeMatrix.getTranslateY());
-
-        // Send the perspective column as a param.
-        op->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1],
-                              shapeMatrix[SkMatrix::kMPersp2]);
-        op->fInfo.fHasPerspective = true;
-
-        op->setBounds(bounds, aaBloat, zeroArea);
-        op->fInfo.fNonSquare = true;
-    }
-
-    instance.fColor = color;
-
-    const float* rectAsFloats = localRect.asScalars(); // Ensure SkScalar == float.
-    memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float));
-
-    op->fPixelLoad = op->bounds().height() * op->bounds().width();
-    return op;
-}
-
-inline bool InstancedRendering::selectAntialiasMode(const SkMatrix& viewMatrix, GrAA aa,
-                                                    const GrInstancedPipelineInfo& info,
-                                                    GrAAType* aaType) {
-    SkASSERT(!info.fIsMixedSampled || info.fIsMultisampled);
-    SkASSERT(GrCaps::InstancedSupport::kNone != fGpu->caps()->instancedSupport());
-
-    if (!info.fIsMultisampled || fGpu->caps()->multisampleDisableSupport()) {
-        if (GrAA::kNo == aa) {
-            *aaType = GrAAType::kNone;
-            return true;
-        }
-
-        if (info.canUseCoverageAA() && viewMatrix.preservesRightAngles()) {
-            *aaType = GrAAType::kCoverage;
-            return true;
-        }
-    }
-
-    if (info.fIsMultisampled &&
-        fGpu->caps()->instancedSupport() >= GrCaps::InstancedSupport::kMultisampled) {
-        if (!info.fIsMixedSampled) {
-            *aaType = GrAAType::kMSAA;
-            return true;
-        }
-        if (fGpu->caps()->instancedSupport() >= GrCaps::InstancedSupport::kMixedSampled) {
-            *aaType = GrAAType::kMixedSamples;
-            return true;
-        }
-    }
-
-    return false;
-}
-
-InstancedRendering::Op::Op(uint32_t classID, GrPaint&& paint, InstancedRendering* ir)
-        : INHERITED(classID)
-        , fInstancedRendering(ir)
-        , fProcessors(std::move(paint))
-        , fIsTracked(false)
-        , fNumDraws(1)
-        , fNumChangesInGeometry(0) {
-    fHeadDraw = fTailDraw = fInstancedRendering->fDrawPool.allocate();
-#ifdef SK_DEBUG
-    fHeadDraw->fGeometry = {-1, 0};
-#endif
-    fHeadDraw->fNext = nullptr;
-}
-
-InstancedRendering::Op::~Op() {
-    if (fIsTracked) {
-        fInstancedRendering->fTrackedOps.remove(this);
-    }
-
-    Draw* draw = fHeadDraw;
-    while (draw) {
-        Draw* next = draw->fNext;
-        fInstancedRendering->fDrawPool.release(draw);
-        draw = next;
-    }
-}
-
-void InstancedRendering::Op::appendRRectParams(const SkRRect& rrect) {
-    SkASSERT(!fIsTracked);
-    switch (rrect.getType()) {
-        case SkRRect::kSimple_Type: {
-            const SkVector& radii = rrect.getSimpleRadii();
-            this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height());
-            return;
-        }
-        case SkRRect::kNinePatch_Type: {
-            float twoOverW = 2 / rrect.width();
-            float twoOverH = 2 / rrect.height();
-            const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
-            const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
-            this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBR.x() * twoOverW,
-                                    radiiTL.y() * twoOverH, radiiBR.y() * twoOverH);
-            return;
-        }
-        case SkRRect::kComplex_Type: {
-            /**
-             * The x and y radii of each arc are stored in separate vectors,
-             * in the following order:
-             *
-             *        __x1 _ _ _ x3__
-             *    y1 |               | y2
-             *
-             *       |               |
-             *
-             *    y3 |__   _ _ _   __| y4
-             *          x2       x4
-             *
-             */
-            float twoOverW = 2 / rrect.width();
-            float twoOverH = 2 / rrect.height();
-            const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
-            const SkVector& radiiTR = rrect.radii(SkRRect::kUpperRight_Corner);
-            const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
-            const SkVector& radiiBL = rrect.radii(SkRRect::kLowerLeft_Corner);
-            this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBL.x() * twoOverW,
-                                    radiiTR.x() * twoOverW, radiiBR.x() * twoOverW);
-            this->appendParamsTexel(radiiTL.y() * twoOverH, radiiTR.y() * twoOverH,
-                                    radiiBL.y() * twoOverH, radiiBR.y() * twoOverH);
-            return;
-        }
-        default: return;
-    }
-}
-
-void InstancedRendering::Op::appendParamsTexel(const SkScalar* vals, int count) {
-    SkASSERT(!fIsTracked);
-    SkASSERT(count <= 4 && count >= 0);
-    const float* valsAsFloats = vals; // Ensure SkScalar == float.
-    memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float));
-    fInfo.fHasParams = true;
-}
-
-void InstancedRendering::Op::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w) {
-    SkASSERT(!fIsTracked);
-    ParamsTexel& texel = fParams.push_back();
-    texel.fX = SkScalarToFloat(x);
-    texel.fY = SkScalarToFloat(y);
-    texel.fZ = SkScalarToFloat(z);
-    texel.fW = SkScalarToFloat(w);
-    fInfo.fHasParams = true;
-}
-
-void InstancedRendering::Op::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z) {
-    SkASSERT(!fIsTracked);
-    ParamsTexel& texel = fParams.push_back();
-    texel.fX = SkScalarToFloat(x);
-    texel.fY = SkScalarToFloat(y);
-    texel.fZ = SkScalarToFloat(z);
-    fInfo.fHasParams = true;
-}
-
-bool InstancedRendering::Op::xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) {
-    GrProcessorSet::FragmentProcessorAnalysis analysis;
-    GrPipelineAnalysisCoverage coverageInput;
-    if (GrAAType::kCoverage == fInfo.aaType() ||
-        (GrAAType::kNone == fInfo.aaType() && !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
-        coverageInput = GrPipelineAnalysisCoverage::kSingleChannel;
-    } else {
-        coverageInput = GrPipelineAnalysisCoverage::kNone;
-    }
-    fProcessors.analyzeAndEliminateFragmentProcessors(&analysis, this->getSingleInstance().fColor,
-                                                      coverageInput, clip, caps);
-    Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
-    SkASSERT(draw.fGeometry.isEmpty());
-    SkASSERT(SkIsPow2(fInfo.fShapeTypes));
-    SkASSERT(!fIsTracked);
-
-    if (kRect_ShapeFlag == fInfo.fShapeTypes) {
-        draw.fGeometry = InstanceProcessor::GetIndexRangeForRect(fInfo.aaType());
-    } else if (kOval_ShapeFlag == fInfo.fShapeTypes) {
-        draw.fGeometry = InstanceProcessor::GetIndexRangeForOval(fInfo.aaType(), this->bounds());
-    } else {
-        draw.fGeometry = InstanceProcessor::GetIndexRangeForRRect(fInfo.aaType());
-    }
-
-    if (!fParams.empty()) {
-        SkASSERT(fInstancedRendering->fParams.count() < (int)kParamsIdx_InfoMask); // TODO: cleaner.
-        this->getSingleInstance().fInfo |= fInstancedRendering->fParams.count();
-        fInstancedRendering->fParams.push_back_n(fParams.count(), fParams.begin());
-    }
-
-    GrColor overrideColor;
-    if (analysis.getInputColorOverrideAndColorProcessorEliminationCount(&overrideColor) >= 0) {
-        SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
-        this->getSingleDraw().fInstance.fColor = overrideColor;
-    }
-    fInfo.fCannotTweakAlphaForCoverage =
-            !analysis.isCompatibleWithCoverageAsAlpha() ||
-            !GrXPFactory::CompatibleWithCoverageAsAlpha(fProcessors.xpFactory(),
-                                                        analysis.isOutputColorOpaque());
-
-    fInfo.fUsesLocalCoords = analysis.usesLocalCoords();
-    return GrXPFactory::WillNeedDstTexture(fProcessors.xpFactory(), caps, analysis);
-}
-
-void InstancedRendering::Op::wasRecorded() {
-    SkASSERT(!fIsTracked);
-    fInstancedRendering->fTrackedOps.addToTail(this);
-    fProcessors.makePendingExecution();
-    fIsTracked = true;
-}
-
-bool InstancedRendering::Op::onCombineIfPossible(GrOp* other, const GrCaps& caps) {
-    Op* that = static_cast<Op*>(other);
-    SkASSERT(fInstancedRendering == that->fInstancedRendering);
-    SkASSERT(fTailDraw);
-    SkASSERT(that->fTailDraw);
-
-    if (!OpInfo::CanCombine(fInfo, that->fInfo) || fProcessors != that->fProcessors) {
-        return false;
-    }
-
-    OpInfo combinedInfo = fInfo | that->fInfo;
-    if (!combinedInfo.isSimpleRects()) {
-        // This threshold was chosen with the "shapes_mixed" bench on a MacBook with Intel graphics.
-        // There seems to be a wide range where it doesn't matter if we combine or not. What matters
-        // is that the itty bitty rects combine with other shapes and the giant ones don't.
-        constexpr SkScalar kMaxPixelsToGeneralizeRects = 256 * 256;
-        if (fInfo.isSimpleRects() && fPixelLoad > kMaxPixelsToGeneralizeRects) {
-            return false;
-        }
-        if (that->fInfo.isSimpleRects() && that->fPixelLoad > kMaxPixelsToGeneralizeRects) {
-            return false;
-        }
-    }
-
-    this->joinBounds(*that);
-    fInfo = combinedInfo;
-    fPixelLoad += that->fPixelLoad;
-    fDrawColorsAreOpaque = fDrawColorsAreOpaque && that->fDrawColorsAreOpaque;
-    fDrawColorsAreSame = fDrawColorsAreSame && that->fDrawColorsAreSame &&
-                         fHeadDraw->fInstance.fColor == that->fHeadDraw->fInstance.fColor;
-    // Adopt the other op's draws.
-    fNumDraws += that->fNumDraws;
-    fNumChangesInGeometry += that->fNumChangesInGeometry;
-    if (fTailDraw->fGeometry != that->fHeadDraw->fGeometry) {
-        ++fNumChangesInGeometry;
-    }
-    fTailDraw->fNext = that->fHeadDraw;
-    fTailDraw = that->fTailDraw;
-
-    that->fHeadDraw = that->fTailDraw = nullptr;
-
-    return true;
-}
 
 void InstancedRendering::beginFlush(GrResourceProvider* rp) {
+#ifdef SK_DEBUG
     SkASSERT(State::kRecordingDraws == fState);
     fState = State::kFlushing;
+#endif
 
     if (fTrackedOps.isEmpty()) {
         return;
@@ -463,45 +62,12 @@
     this->onBeginFlush(rp);
 }
 
-void InstancedRendering::Op::onExecute(GrOpFlushState* state) {
-    SkASSERT(State::kFlushing == fInstancedRendering->fState);
-    SkASSERT(state->gpu() == fInstancedRendering->gpu());
+void InstancedRendering::draw(const GrPipeline& pipeline,
+                              OpInfo info,
+                              const InstancedOp* baseOp) {
+    InstanceProcessor instProc(info, fParamsBuffer.get());
 
-    state->gpu()->handleDirtyContext();
-
-    GrProcessorSet::FragmentProcessorAnalysis analysis;
-    GrPipelineAnalysisCoverage coverageInput;
-    if (GrAAType::kCoverage == fInfo.aaType() ||
-        (GrAAType::kNone == fInfo.aaType() && !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
-        coverageInput = GrPipelineAnalysisCoverage::kSingleChannel;
-    } else {
-        coverageInput = GrPipelineAnalysisCoverage::kNone;
-    }
-    GrPipelineAnalysisColor colorInput;
-    if (fDrawColorsAreSame) {
-        colorInput = fHeadDraw->fInstance.fColor;
-    } else if (fDrawColorsAreOpaque) {
-        colorInput = GrPipelineAnalysisColor::Opaque::kYes;
-    }
-    const GrAppliedClip* clip = state->drawOpArgs().fAppliedClip;
-    analysis.init(colorInput, coverageInput, fProcessors, clip, state->caps());
-
-    GrPipeline pipeline;
-    GrPipeline::InitArgs args;
-    args.fAnalysis = &analysis;
-    args.fAppliedClip = clip;
-    args.fCaps = &state->caps();
-    args.fProcessors = &fProcessors;
-    args.fFlags = GrAATypeIsHW(fInfo.aaType()) ? GrPipeline::kHWAntialias_Flag : 0;
-    args.fRenderTarget = state->drawOpArgs().fRenderTarget;
-    args.fDstTexture = state->drawOpArgs().fDstTexture;
-    pipeline.init(args);
-
-    if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*state->gpu()->caps())) {
-        state->gpu()->xferBarrier(pipeline.getRenderTarget(), barrierType);
-    }
-    InstanceProcessor instProc(fInfo, fInstancedRendering->fParamsBuffer.get());
-    fInstancedRendering->onDraw(pipeline, instProc, this);
+    this->onDraw(pipeline, instProc, baseOp);
 }
 
 void InstancedRendering::endFlush() {
@@ -511,7 +77,7 @@
     fParams.reset();
     fParamsBuffer.reset();
     this->onEndFlush();
-    fState = State::kRecordingDraws;
+    SkDEBUGCODE(fState = State::kRecordingDraws;)
     // Hold on to the shape coords and index buffers.
 }
 
@@ -522,4 +88,14 @@
     this->onResetGpuResources(resetType);
 }
 
+int InstancedRendering::addOpParams(InstancedOp* op) {
+    if (op->fParams.empty()) {
+        return 0;
+    }
+
+    SkASSERT(fParams.count() < (int)kParamsIdx_InfoMask); // TODO: cleaner.
+    int count = fParams.count();
+    fParams.push_back_n(op->fParams.count(), op->fParams.begin());
+    return count;
+}
 }
diff --git a/src/gpu/instanced/InstancedRendering.h b/src/gpu/instanced/InstancedRendering.h
index c2db768..3c5976d 100644
--- a/src/gpu/instanced/InstancedRendering.h
+++ b/src/gpu/instanced/InstancedRendering.h
@@ -8,29 +8,41 @@
 #ifndef gr_instanced_InstancedRendering_DEFINED
 #define gr_instanced_InstancedRendering_DEFINED
 
-#include "../private/GrInstancedPipelineInfo.h"
 #include "GrGpu.h"
-#include "GrMemoryPool.h"
-#include "SkTInternalLList.h"
+#include "instanced/InstancedOp.h"
 #include "instanced/InstancedRenderingTypes.h"
-#include "ops/GrDrawOp.h"
+
+#include "SkTInternalLList.h"
 
 class GrResourceProvider;
 
 namespace gr_instanced {
 
+class InstancedOp;
 class InstanceProcessor;
 
+
 /**
- * This class serves as a centralized clearinghouse for instanced rendering. It accumulates data for
- * instanced draws into one location, and creates special ops that pull from this data. The
- * nature of instanced rendering allows these ops to combine well and render efficiently.
+ * Instanced Rendering occurs through the interaction of four class:
+ *   InstancedOp
+ *   OpAllocator
+ *   InstancedRendering
+ *   InstanceProcessor
  *
- * During a flush, this class assembles the accumulated draw data into a single vertex and texel
- * buffer, and its subclass draws the ops using backend-specific instanced rendering APIs.
+ * The InstancedOp is a GrDrawOp but is more of a proxy than normal operations. It accumulates a
+ * LL of Draw objects that are allocated all together by the OpAllocator.
  *
- * This class is responsible for the CPU side of instanced rendering. Shaders are implemented by
- * InstanceProcessor.
+ * There is only one OpAllocator which encapsulates the creation of InstancedOps and the pool
+ * of memory used for their Draw objects.
+ *
+ * The InstancedRendering class tracks a list of InstancedOps that will all be drawn during 
+ * the same flush. There is currently one per opList. The nature of instanced
+ * rendering allows these ops to combine well and render efficiently.
+ * During a flush, it assembles the accumulated draw data into a single vertex and texel
+ * buffer per opList, and its subclasses draw the ops using backend-specific instanced
+ * rendering APIs.
+ *
+ * InstanceProcessors implement the shaders required for instance rendering.
  */
 class InstancedRendering : public SkNoncopyable {
 public:
@@ -39,45 +51,13 @@
     GrGpu* gpu() const { return fGpu.get(); }
 
     /**
-     * These methods make a new record internally for an instanced draw, and return an op that is
-     * effectively just an index to that record. The returned op is not self-contained, but
-     * rather relies on this class to handle the rendering. The client must call beginFlush() on
-     * this class before attempting to flush ops returned by it. It is invalid to record new
-     * draws between beginFlush() and endFlush().
-     */
-    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRect(const SkRect&, const SkMatrix&,
-                                                               GrPaint&&, GrAA,
-                                                               const GrInstancedPipelineInfo&);
-
-    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRect(const SkRect&, const SkMatrix&,
-                                                               GrPaint&&, const SkRect& localRect,
-                                                               GrAA,
-                                                               const GrInstancedPipelineInfo&);
-
-    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRect(const SkRect&, const SkMatrix&,
-                                                               GrPaint&&,
-                                                               const SkMatrix& localMatrix, GrAA,
-                                                               const GrInstancedPipelineInfo&);
-
-    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordOval(const SkRect&, const SkMatrix&,
-                                                               GrPaint&&, GrAA,
-                                                               const GrInstancedPipelineInfo&);
-
-    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordRRect(const SkRRect&, const SkMatrix&,
-                                                                GrPaint&&, GrAA,
-                                                                const GrInstancedPipelineInfo&);
-
-    std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT recordDRRect(const SkRRect& outer,
-                                                                 const SkRRect& inner,
-                                                                 const SkMatrix&, GrPaint&&, GrAA,
-                                                                 const GrInstancedPipelineInfo&);
-
-    /**
      * Compiles all recorded draws into GPU buffers and allows the client to begin flushing the
      * ops created by this class.
      */
     void beginFlush(GrResourceProvider*);
 
+    void draw(const GrPipeline& pipeline, OpInfo info, const InstancedOp* baseOp);
+
     /**
      * Called once the ops created previously by this class have all been released. Allows the
      * client to begin recording draws again.
@@ -95,82 +75,16 @@
      */
     void resetGpuResources(ResetType);
 
+    void addOp(InstancedOp* op) { fTrackedOps.addToTail(op); }
+    void removeOp(InstancedOp* op) { fTrackedOps.remove(op); }
+    int addOpParams(InstancedOp* op);
+
+#ifdef SK_DEBUG
+    bool isFlushing() const { return InstancedRendering::State::kFlushing == fState; }
+#endif
+
 protected:
-    class Op : public GrDrawOp {
-    public:
-        SK_DECLARE_INTERNAL_LLIST_INTERFACE(Op);
-
-        ~Op() override;
-        const char* name() const override { return "InstancedRendering::Op"; }
-
-        SkString dumpInfo() const override {
-            SkString string;
-            string.printf(
-                    "AA: %d, ShapeTypes: 0x%02x, IShapeTypes: 0x%02x, Persp %d, "
-                    "NonSquare: %d, PLoad: %0.2f, Tracked: %d, NumDraws: %d, "
-                    "GeomChanges: %d\n",
-                    (unsigned)fInfo.fAAType,
-                    fInfo.fShapeTypes,
-                    fInfo.fInnerShapeTypes,
-                    fInfo.fHasPerspective,
-                    fInfo.fNonSquare,
-                    fPixelLoad,
-                    fIsTracked,
-                    fNumDraws,
-                    fNumChangesInGeometry);
-            string.append(INHERITED::dumpInfo());
-            return string;
-        }
-
-        struct Draw {
-            Instance     fInstance;
-            IndexRange   fGeometry;
-            Draw*        fNext;
-        };
-
-        Draw& getSingleDraw() const { SkASSERT(fHeadDraw && !fHeadDraw->fNext); return *fHeadDraw; }
-        Instance& getSingleInstance() const { return this->getSingleDraw().fInstance; }
-
-        void appendRRectParams(const SkRRect&);
-        void appendParamsTexel(const SkScalar* vals, int count);
-        void appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w);
-        void appendParamsTexel(SkScalar x, SkScalar y, SkScalar z);
-        FixedFunctionFlags fixedFunctionFlags() const override {
-            return GrAATypeIsHW(fInfo.aaType()) ? FixedFunctionFlags::kUsesHWAA
-                                                : FixedFunctionFlags::kNone;
-        }
-        bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override;
-
-        // Registers the op with the InstancedRendering list of tracked ops.
-        void wasRecorded() override;
-
-    protected:
-        Op(uint32_t classID, GrPaint&&, InstancedRendering*);
-
-        InstancedRendering* const fInstancedRendering;
-        OpInfo fInfo;
-        SkScalar fPixelLoad;
-        GrProcessorSet fProcessors;
-        SkSTArray<5, ParamsTexel, true> fParams;
-        bool fIsTracked : 1;
-        bool fDrawColorsAreOpaque : 1;
-        bool fDrawColorsAreSame : 1;
-        int fNumDraws;
-        int fNumChangesInGeometry;
-        Draw* fHeadDraw;
-        Draw* fTailDraw;
-
-    private:
-        bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override;
-        void onPrepare(GrOpFlushState*) override {}
-        void onExecute(GrOpFlushState*) override;
-
-        typedef GrDrawOp INHERITED;
-
-        friend class InstancedRendering;
-    };
-
-    typedef SkTInternalLList<Op> OpList;
+    typedef SkTInternalLList<InstancedOp> OpList;
 
     InstancedRendering(GrGpu* gpu);
 
@@ -179,29 +93,20 @@
     const GrBuffer* indexBuffer() const { SkASSERT(fIndexBuffer); return fIndexBuffer.get(); }
 
     virtual void onBeginFlush(GrResourceProvider*) = 0;
-    virtual void onDraw(const GrPipeline&, const InstanceProcessor&, const Op*) = 0;
+    virtual void onDraw(const GrPipeline&, const InstanceProcessor&, const InstancedOp*) = 0;
     virtual void onEndFlush() = 0;
     virtual void onResetGpuResources(ResetType) = 0;
 
 private:
+#ifdef SK_DEBUG
     enum class State : bool {
         kRecordingDraws,
         kFlushing
     };
-
-    std::unique_ptr<Op> SK_WARN_UNUSED_RESULT recordShape(ShapeType, const SkRect& bounds,
-                                                          const SkMatrix& viewMatrix, GrPaint&&,
-                                                          const SkRect& localRect, GrAA aa,
-                                                          const GrInstancedPipelineInfo&);
-
-    bool selectAntialiasMode(const SkMatrix& viewMatrix, GrAA aa, const GrInstancedPipelineInfo&,
-                             GrAAType*);
-
-    virtual std::unique_ptr<Op> makeOp(GrPaint&&) = 0;
+#endif
 
     const sk_sp<GrGpu> fGpu;
-    State fState;
-    GrObjectMemoryPool<Op::Draw> fDrawPool;
+    SkDEBUGCODE(State fState;)
     SkSTArray<1024, ParamsTexel, true> fParams;
     OpList fTrackedOps;
     sk_sp<const GrBuffer> fVertexBuffer;
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 79259b4..cd69c6a 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -667,9 +667,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
-    return (args.fShaderCaps->shaderDerivativeSupport() && (GrAAType::kCoverage == args.fAAType) &&
-            args.fShape->style().isSimpleFill() && !args.fShape->inverseFilled() &&
-            args.fShape->knownToBeConvex());
+    return (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
+            (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
+            !args.fShape->inverseFilled() && args.fShape->knownToBeConvex());
 }
 
 // extract the result vertices and indices from the GrAAConvexTessellator
@@ -722,12 +722,12 @@
                               viewMatrix);
 }
 
-class AAConvexPathOp final : public GrMeshDrawOp {
+class AAConvexPathOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              const SkPath& path) {
-        return std::unique_ptr<GrMeshDrawOp>(new AAConvexPathOp(color, viewMatrix, path));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
+                                                    const SkPath& path) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new AAConvexPathOp(color, viewMatrix, path));
     }
 
     const char* name() const override { return "AAConvexPathOp"; }
@@ -747,13 +747,13 @@
         this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
 
         fUsesLocalCoords = optimizations.readsLocalCoords();
@@ -812,12 +812,10 @@
 
             extract_verts(tess, verts, vertexStride, fColor, idxs, canTweakAlphaForCoverage);
 
-            GrMesh mesh;
-            mesh.initIndexed(kTriangles_GrPrimitiveType,
-                             vertexBuffer, indexBuffer,
-                             firstVertex, firstIndex,
-                             tess.numPts(), tess.numIndices());
-            target->draw(gp.get(), mesh);
+            GrMesh mesh(kTriangles_GrPrimitiveType);
+            mesh.setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1);
+            mesh.setVertexData(vertexBuffer, firstVertex);
+            target->draw(gp.get(), this->pipeline(), mesh);
         }
     }
 
@@ -899,15 +897,15 @@
             SkSTArray<kPreallocDrawCnt, Draw, true> draws;
             create_vertices(segments, fanPt, &draws, verts, idxs);
 
-            GrMesh mesh;
+            GrMesh mesh(kTriangles_GrPrimitiveType);
 
             for (int j = 0; j < draws.count(); ++j) {
                 const Draw& draw = draws[j];
-                mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
-                                 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
-                target->draw(quadProcessor.get(), mesh);
-                firstVertex += draw.fVertexCnt;
+                mesh.setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0, draw.fVertexCnt - 1);
+                mesh.setVertexData(vertexBuffer, firstVertex);
+                target->draw(quadProcessor.get(), this->pipeline(), mesh);
                 firstIndex += draw.fIndexCnt;
+                firstVertex += draw.fVertexCnt;
             }
         }
     }
@@ -961,25 +959,26 @@
 
     SkSTArray<1, PathData, true> fPaths;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
                               "GrAAConvexPathRenderer::onDrawPath");
-    SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
+    SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
     SkASSERT(!args.fShape->isEmpty());
 
     SkPath path;
     args.fShape->asPath(&path);
 
-    std::unique_ptr<GrMeshDrawOp> op =
+    std::unique_ptr<GrLegacyMeshDrawOp> op =
             AAConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path);
 
     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
 
-    args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
+    args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
+                                                   std::move(op));
 
     return true;
 
@@ -989,7 +988,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkPath path = GrTest::TestPathConvex(random);
diff --git a/src/gpu/ops/GrAAConvexTessellator.cpp b/src/gpu/ops/GrAAConvexTessellator.cpp
index 5fa07fb..aea7ce7 100644
--- a/src/gpu/ops/GrAAConvexTessellator.cpp
+++ b/src/gpu/ops/GrAAConvexTessellator.cpp
@@ -1049,7 +1049,7 @@
 
         SkString num;
         num.printf("%d", this->origEdgeID(cur));
-        canvas->drawText(num.c_str(), num.size(), mid.fX, mid.fY, paint);
+        canvas->drawString(num, mid.fX, mid.fY, paint);
 
         if (fPts.count()) {
             draw_arrow(canvas, tess.point(fPts[cur].fIndex), fPts[cur].fBisector,
@@ -1094,7 +1094,7 @@
 
         SkString num;
         num.printf("%d", i);
-        canvas->drawText(num.c_str(), num.size(),
+        canvas->drawString(num,
                          this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f),
                          paint);
     }
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index c6aadb9..364c221 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -41,7 +41,7 @@
     // clang-format on
 
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
-    return resourceProvider->findOrCreateInstancedIndexBuffer(
+    return resourceProvider->findOrCreatePatternedIndexBuffer(
             gFillAARectIdx, kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer,
             kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
 }
@@ -153,7 +153,7 @@
     }
 }
 
-class AAFillRectOp final: public GrMeshDrawOp {
+class AAFillRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -180,6 +180,7 @@
 
     SkString dumpInfo() const override {
         SkString str;
+        str.append(INHERITED::dumpInfo());
         str.appendf("# combined: %d\n", fRectCnt);
         const RectInfo* info = this->first();
         for (int i = 0; i < fRectCnt; ++i) {
@@ -189,11 +190,10 @@
             info = this->next(info);
         }
         str.append(DumpPipelineInfo(*this->pipeline()));
-        str.append(INHERITED::dumpInfo());
         return str;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         GrColor color;
         if (optimizations.getOverrideColorIfSet(&color)) {
             this->first()->setColor(color);
@@ -203,10 +203,10 @@
     }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(this->first()->color());
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
     void onPrepareDraws(Target* target) const override {
@@ -231,10 +231,10 @@
         size_t vertexStride = gp->getVertexStride();
 
         sk_sp<const GrBuffer> indexBuffer(get_index_buffer(target->resourceProvider()));
-        InstancedHelper helper;
+        PatternHelper helper(kTriangles_GrPrimitiveType);
         void* vertices =
-                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
-                            kVertsPerAAFillRect, kIndicesPerAAFillRect, fRectCnt);
+                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerAAFillRect,
+                            kIndicesPerAAFillRect, fRectCnt);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -257,7 +257,7 @@
                                            localMatrix);
             info = this->next(info);
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -338,41 +338,41 @@
     SkSTArray<4 * sizeof(RectWithLocalMatrixInfo), uint8_t, true> fRectData;
     int fRectCnt;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 namespace GrAAFillRectOp {
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkRect& devRect) {
-    return std::unique_ptr<GrMeshDrawOp>(
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRect& rect,
+                                         const SkRect& devRect) {
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
             new AAFillRectOp(color, viewMatrix, rect, devRect, nullptr));
 }
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkMatrix& localMatrix,
-                                   const SkRect& rect,
-                                   const SkRect& devRect) {
-    return std::unique_ptr<GrMeshDrawOp>(
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkMatrix& localMatrix,
+                                         const SkRect& rect,
+                                         const SkRect& devRect) {
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
             new AAFillRectOp(color, viewMatrix, rect, devRect, &localMatrix));
 }
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkMatrix& localMatrix,
-                                   const SkRect& rect) {
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkMatrix& localMatrix,
+                                         const SkRect& rect) {
     SkRect devRect;
     viewMatrix.mapRect(&devRect, rect);
     return Make(color, viewMatrix, localMatrix, rect, devRect);
 }
 
-std::unique_ptr<GrMeshDrawOp> MakeWithLocalRect(GrColor color,
-                                                const SkMatrix& viewMatrix,
-                                                const SkRect& rect,
-                                                const SkRect& localRect) {
+std::unique_ptr<GrLegacyMeshDrawOp> MakeWithLocalRect(GrColor color,
+                                                      const SkMatrix& viewMatrix,
+                                                      const SkRect& rect,
+                                                      const SkRect& localRect) {
     SkRect devRect;
     viewMatrix.mapRect(&devRect, rect);
     SkMatrix localMatrix;
@@ -389,7 +389,7 @@
 
 #include "GrDrawOpTest.h"
 
-DRAW_OP_TEST_DEFINE(AAFillRectOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAFillRectOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkRect rect = GrTest::TestRect(random);
@@ -397,7 +397,7 @@
     return GrAAFillRectOp::Make(color, viewMatrix, rect, devRect);
 }
 
-DRAW_OP_TEST_DEFINE(AAFillRectOpLocalMatrix) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAFillRectOpLocalMatrix) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkMatrix localMatrix = GrTest::TestMatrix(random);
diff --git a/src/gpu/ops/GrAAFillRectOp.h b/src/gpu/ops/GrAAFillRectOp.h
index c047eed..7d97716 100644
--- a/src/gpu/ops/GrAAFillRectOp.h
+++ b/src/gpu/ops/GrAAFillRectOp.h
@@ -11,31 +11,31 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class SkMatrix;
 struct SkRect;
 
 namespace GrAAFillRectOp {
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkRect& devRect);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRect& rect,
+                                         const SkRect& devRect);
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkMatrix& localMatrix,
-                                   const SkRect& rect);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkMatrix& localMatrix,
+                                         const SkRect& rect);
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkMatrix& localMatrix,
-                                   const SkRect& rect,
-                                   const SkRect& devRect);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkMatrix& localMatrix,
+                                         const SkRect& rect,
+                                         const SkRect& devRect);
 
-std::unique_ptr<GrMeshDrawOp> MakeWithLocalRect(GrColor color,
-                                                const SkMatrix& viewMatrix,
-                                                const SkRect& rect,
-                                                const SkRect& localRect);
+std::unique_ptr<GrLegacyMeshDrawOp> MakeWithLocalRect(GrColor color,
+                                                      const SkMatrix& viewMatrix,
+                                                      const SkRect& rect,
+                                                      const SkRect& localRect);
 };
 
 #endif
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index 494138d..26d58f4 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -64,7 +64,7 @@
 
 static const GrBuffer* ref_quads_index_buffer(GrResourceProvider* resourceProvider) {
     GR_DEFINE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
-    return resourceProvider->findOrCreateInstancedIndexBuffer(
+    return resourceProvider->findOrCreatePatternedIndexBuffer(
         kQuadIdxBufPattern, kIdxsPerQuad, kQuadsNumInIdxBuffer, kQuadNumVertices,
         gQuadsIndexBufferKey);
 }
@@ -98,7 +98,7 @@
 
 static const GrBuffer* ref_lines_index_buffer(GrResourceProvider* resourceProvider) {
     GR_DEFINE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
-    return resourceProvider->findOrCreateInstancedIndexBuffer(
+    return resourceProvider->findOrCreatePatternedIndexBuffer(
         kLineSegIdxBufPattern, kIdxsPerLineSeg,  kLineSegsNumInIdxBuffer, kLineSegNumVertices,
         gLinesIndexBufferKey);
 }
@@ -624,7 +624,7 @@
     }
 
     if (SkPath::kLine_SegmentMask == args.fShape->segmentMask() ||
-        args.fShaderCaps->shaderDerivativeSupport()) {
+        args.fCaps->shaderCaps()->shaderDerivativeSupport()) {
         return true;
     }
 
@@ -668,22 +668,22 @@
     return true;
 }
 
-class AAHairlineOp final : public GrMeshDrawOp {
+class AAHairlineOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                              const SkMatrix& viewMatrix,
-                                              const SkPath& path,
-                                              const GrStyle& style,
-                                              const SkIRect& devClipBounds) {
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkPath& path,
+                                                    const GrStyle& style,
+                                                    const SkIRect& devClipBounds) {
         SkScalar hairlineCoverage;
         uint8_t newCoverage = 0xff;
         if (GrPathRenderer::IsStrokeHairlineOrEquivalent(style, viewMatrix, &hairlineCoverage)) {
             newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
         }
 
-        return std::unique_ptr<GrMeshDrawOp>(
+        return std::unique_ptr<GrLegacyMeshDrawOp>(
                 new AAHairlineOp(color, newCoverage, viewMatrix, path, devClipBounds));
     }
 
@@ -710,13 +710,13 @@
                                    IsZeroArea::kYes);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
         fUsesLocalCoords = optimizations.readsLocalCoords();
     }
@@ -783,7 +783,7 @@
 
     SkSTArray<1, PathData, true> fPaths;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 void AAHairlineOp::onPrepareDraws(Target* target) const {
@@ -860,11 +860,11 @@
             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
         }
 
-        GrMesh mesh;
-        mesh.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, linesIndexBuffer.get(),
-                           firstVertex, kLineSegNumVertices, kIdxsPerLineSeg, lineCount,
-                           kLineSegsNumInIdxBuffer);
-        target->draw(lineGP.get(), mesh);
+        GrMesh mesh(kTriangles_GrPrimitiveType);
+        mesh.setIndexedPatterned(linesIndexBuffer.get(), kIdxsPerLineSeg, kLineSegNumVertices,
+                                 lineCount, kLineSegsNumInIdxBuffer);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(lineGP.get(), this->pipeline(), mesh);
     }
 
     if (quadCount || conicCount) {
@@ -917,20 +917,20 @@
         }
 
         if (quadCount > 0) {
-            GrMesh mesh;
-            mesh.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer.get(),
-                               firstVertex, kQuadNumVertices, kIdxsPerQuad, quadCount,
-                               kQuadsNumInIdxBuffer);
-            target->draw(quadGP.get(), mesh);
+            GrMesh mesh(kTriangles_GrPrimitiveType);
+            mesh.setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
+                                     quadCount, kQuadsNumInIdxBuffer);
+            mesh.setVertexData(vertexBuffer, firstVertex);
+            target->draw(quadGP.get(), this->pipeline(), mesh);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
         if (conicCount > 0) {
-            GrMesh mesh;
-            mesh.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer.get(),
-                               firstVertex, kQuadNumVertices, kIdxsPerQuad, conicCount,
-                               kQuadsNumInIdxBuffer);
-            target->draw(conicGP.get(), mesh);
+            GrMesh mesh(kTriangles_GrPrimitiveType);
+            mesh.setIndexedPatterned(quadsIndexBuffer.get(), kIdxsPerQuad, kQuadNumVertices,
+                                     conicCount, kQuadsNumInIdxBuffer);
+            mesh.setVertexData(vertexBuffer, firstVertex);
+            target->draw(conicGP.get(), this->pipeline(), mesh);
         }
     }
 }
@@ -938,7 +938,7 @@
 bool GrAAHairLinePathRenderer::onDrawPath(const DrawPathArgs& args) {
     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
                               "GrAAHairlinePathRenderer::onDrawPath");
-    SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
+    SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
 
     SkIRect devClipBounds;
     args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
@@ -946,11 +946,12 @@
                                       &devClipBounds);
     SkPath path;
     args.fShape->asPath(&path);
-    std::unique_ptr<GrMeshDrawOp> op = AAHairlineOp::Make(
+    std::unique_ptr<GrLegacyMeshDrawOp> op = AAHairlineOp::Make(
             args.fPaint.getColor(), *args.fViewMatrix, path, args.fShape->style(), devClipBounds);
     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
-    args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
+    args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
+                                                   std::move(op));
     return true;
 }
 
@@ -958,7 +959,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(AAHairlineOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAHairlineOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
     SkPath path = GrTest::TestPath(random);
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index c9add9d..5cfe5cb 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -117,17 +117,17 @@
                               viewMatrix);
 }
 
-class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
+class AAFlatteningConvexPathOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                              const SkMatrix& viewMatrix,
-                                              const SkPath& path,
-                                              SkScalar strokeWidth,
-                                              SkStrokeRec::Style style,
-                                              SkPaint::Join join,
-                                              SkScalar miterLimit) {
-        return std::unique_ptr<GrMeshDrawOp>(new AAFlatteningConvexPathOp(
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkPath& path,
+                                                    SkScalar strokeWidth,
+                                                    SkStrokeRec::Style style,
+                                                    SkPaint::Join join,
+                                                    SkScalar miterLimit) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new AAFlatteningConvexPathOp(
                 color, viewMatrix, path, strokeWidth, style, join, miterLimit));
     }
 
@@ -172,25 +172,25 @@
         this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fPaths[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fPaths[0].fColor);
         fUsesLocalCoords = optimizations.readsLocalCoords();
         fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
     }
 
-    void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount,
+    void draw(GrLegacyMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount,
               size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
         if (vertexCount == 0 || indexCount == 0) {
             return;
         }
         const GrBuffer* vertexBuffer;
-        GrMesh mesh;
+        GrMesh mesh(kTriangles_GrPrimitiveType);
         int firstVertex;
         void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
                                               &firstVertex);
@@ -208,9 +208,9 @@
             return;
         }
         memcpy(idxs, indices, indexCount * sizeof(uint16_t));
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, vertexCount, indexCount);
-        target->draw(gp, mesh);
+        mesh.setIndexed(indexBuffer, indexCount, firstIndex, 0, vertexCount - 1);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(gp, this->pipeline(), mesh);
     }
 
     void onPrepareDraws(Target* target) const override {
@@ -318,13 +318,13 @@
     bool fCanTweakAlphaForCoverage;
     SkSTArray<1, PathData, true> fPaths;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
                               "GrAALinearizingConvexPathRenderer::onDrawPath");
-    SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
+    SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
     SkASSERT(!args.fShape->isEmpty());
     SkASSERT(!args.fShape->style().pathEffect());
 
@@ -336,14 +336,15 @@
     SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
     SkScalar miterLimit = stroke.getMiter();
 
-    std::unique_ptr<GrMeshDrawOp> op =
+    std::unique_ptr<GrLegacyMeshDrawOp> op =
             AAFlatteningConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path,
                                            strokeWidth, stroke.getStyle(), join, miterLimit);
 
     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
 
-    args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
+    args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
+                                                   std::move(op));
 
     return true;
 }
@@ -352,7 +353,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
     SkPath path = GrTest::TestPathConvex(random);
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index 89739e6..7ec8b86 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -110,7 +110,7 @@
                               viewMatrix);
 }
 
-class AAStrokeRectOp final : public GrMeshDrawOp {
+class AAStrokeRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -125,8 +125,8 @@
         fMiterStroke = true;
     }
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              const SkRect& rect, const SkStrokeRec& stroke) {
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
+                                                    const SkRect& rect, const SkStrokeRec& stroke) {
         bool isMiter;
         if (!allowed_stroke(stroke, &isMiter)) {
             return nullptr;
@@ -148,7 +148,7 @@
             op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
         }
         op->fViewMatrix = viewMatrix;
-        return std::unique_ptr<GrMeshDrawOp>(op);
+        return std::unique_ptr<GrLegacyMeshDrawOp>(op);
     }
 
     const char* name() const override { return "AAStrokeRect"; }
@@ -174,12 +174,12 @@
 private:
     AAStrokeRectOp() : INHERITED(ClassID()) {}
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fRects[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
-    void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
+    void applyPipelineOptimizations(const PipelineOptimizations&) override;
     void onPrepareDraws(Target*) const override;
 
     static const int kMiterIndexCnt = 3 * 24;
@@ -227,10 +227,10 @@
     SkMatrix fViewMatrix;
     bool fMiterStroke;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
-void AAStrokeRectOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
+void AAStrokeRectOp::applyPipelineOptimizations(const PipelineOptimizations& optimizations) {
     optimizations.getOverrideColorIfSet(&fRects[0].fColor);
 
     fUsesLocalCoords = optimizations.readsLocalCoords();
@@ -262,9 +262,9 @@
 
     const sk_sp<const GrBuffer> indexBuffer(
             GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
-    InstancedHelper helper;
+    PatternHelper helper(kTriangles_GrPrimitiveType);
     void* vertices =
-            helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
+            helper.init(target, vertexStride, indexBuffer.get(),
                         verticesPerInstance, indicesPerInstance, instanceCount);
     if (!vertices || !indexBuffer) {
         SkDebugf("Could not allocate vertices\n");
@@ -286,7 +286,7 @@
                                            info.fDegenerate,
                                            canTweakAlphaForCoverage);
     }
-    helper.recordDraw(target, gp.get());
+    helper.recordDraw(target, gp.get(), this->pipeline());
 }
 
 const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
@@ -312,7 +312,7 @@
         // clang-format on
         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
         GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
-        return resourceProvider->findOrCreateInstancedIndexBuffer(
+        return resourceProvider->findOrCreatePatternedIndexBuffer(
                 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
                 gMiterIndexBufferKey);
     } else {
@@ -377,7 +377,7 @@
         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
 
         GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
-        return resourceProvider->findOrCreateInstancedIndexBuffer(
+        return resourceProvider->findOrCreatePatternedIndexBuffer(
                 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
                 gBevelIndexBufferKey);
     }
@@ -571,18 +571,18 @@
 
 namespace GrAAStrokeRectOp {
 
-std::unique_ptr<GrMeshDrawOp> MakeFillBetweenRects(GrColor color,
-                                                   const SkMatrix& viewMatrix,
-                                                   const SkRect& devOutside,
-                                                   const SkRect& devInside) {
-    return std::unique_ptr<GrMeshDrawOp>(
+std::unique_ptr<GrLegacyMeshDrawOp> MakeFillBetweenRects(GrColor color,
+                                                         const SkMatrix& viewMatrix,
+                                                         const SkRect& devOutside,
+                                                         const SkRect& devInside) {
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
             new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
 }
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkStrokeRec& stroke) {
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRect& rect,
+                                         const SkStrokeRec& stroke) {
     return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
 }
 }
@@ -593,7 +593,7 @@
 
 #include "GrDrawOpTest.h"
 
-DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
     bool miterStroke = random->nextBool();
 
     // Create either a empty rect or a non-empty rect.
diff --git a/src/gpu/ops/GrAAStrokeRectOp.h b/src/gpu/ops/GrAAStrokeRectOp.h
index 4dfbe64..074f8e0 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.h
+++ b/src/gpu/ops/GrAAStrokeRectOp.h
@@ -11,22 +11,22 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class SkMatrix;
 struct SkRect;
 class SkStrokeRec;
 
 namespace GrAAStrokeRectOp {
 
-std::unique_ptr<GrMeshDrawOp> MakeFillBetweenRects(GrColor color,
-                                                   const SkMatrix& viewMatrix,
-                                                   const SkRect& devOutside,
-                                                   const SkRect& devInside);
+std::unique_ptr<GrLegacyMeshDrawOp> MakeFillBetweenRects(GrColor color,
+                                                         const SkMatrix& viewMatrix,
+                                                         const SkRect& devOutside,
+                                                         const SkRect& devInside);
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkStrokeRec& stroke);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRect& rect,
+                                         const SkStrokeRec& stroke);
 }
 
 #endif
diff --git a/src/gpu/ops/GrAnalyticRectOp.cpp b/src/gpu/ops/GrAnalyticRectOp.cpp
index 87922cc..f62f0c6 100644
--- a/src/gpu/ops/GrAnalyticRectOp.cpp
+++ b/src/gpu/ops/GrAnalyticRectOp.cpp
@@ -60,8 +60,6 @@
         fInWidthHeight = &this->addVertexAttrib("inWidthHeight", kVec2f_GrVertexAttribType);
     }
 
-    bool implementsDistanceVector() const override { return true; }
-
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inColor() const { return fInColor; }
     const Attribute* inRectEdge() const { return fInRectEdge; }
@@ -141,46 +139,17 @@
             fragBuilder->codeAppendf("float perpDot = abs(offset.x * %s.w - offset.y * %s.z);",
                                      rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
 
-            if (args.fDistanceVectorName) {
-                fragBuilder->codeAppendf("float widthDistance = %s.x - perpDot;",
-                                         widthHeightVary.fsIn());
-            }
-
             fragBuilder->codeAppendf(
                     "float coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);",
                     widthHeightVary.fsIn());
             // Compute the coverage for the rect's height and merge with the width
             fragBuilder->codeAppendf("perpDot = abs(dot(offset, %s.zw));", rectEdgeVary.fsIn());
 
-            if (args.fDistanceVectorName) {
-                fragBuilder->codeAppendf("float heightDistance = %s.y - perpDot;",
-                                         widthHeightVary.fsIn());
-            }
-
             fragBuilder->codeAppendf(
                     "coverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);",
                     widthHeightVary.fsIn());
 
             fragBuilder->codeAppendf("%s = vec4(coverage);", args.fOutputCoverage);
-
-            if (args.fDistanceVectorName) {
-                fragBuilder->codeAppend("// Calculating distance vector\n");
-                fragBuilder->codeAppend("vec2 dvAxis;");
-                fragBuilder->codeAppend("float dvLength;");
-
-                fragBuilder->codeAppend("if (heightDistance < widthDistance) {");
-                fragBuilder->codeAppendf("    dvAxis = %s.zw;", rectEdgeVary.fsIn());
-                fragBuilder->codeAppend("     dvLength = heightDistance;");
-                fragBuilder->codeAppend("} else {");
-                fragBuilder->codeAppendf("    dvAxis = vec2(-%s.w, %s.z);", rectEdgeVary.fsIn(),
-                                         rectEdgeVary.fsIn());
-                fragBuilder->codeAppend("     dvLength = widthDistance;");
-                fragBuilder->codeAppend("}");
-
-                fragBuilder->codeAppend("float dvSign = sign(dot(offset, dvAxis));");
-                fragBuilder->codeAppendf("%s = vec4(dvSign * dvAxis, dvLength, 0.0);",
-                                         args.fDistanceVectorName);
-            }
         }
 
         static void GenKey(const GrGeometryProcessor& gp,
@@ -230,7 +199,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class AnalyticRectOp final : public GrMeshDrawOp {
+class AnalyticRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -269,13 +238,13 @@
     }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
         if (!optimizations.readsLocalCoords()) {
             fViewMatrixIfUsingLocalCoords.reset();
@@ -344,7 +313,7 @@
 
             verts += kVerticesPerQuad;
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -375,27 +344,27 @@
     SkMatrix fViewMatrixIfUsingLocalCoords;
     SkSTArray<1, Geometry, true> fGeoData;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
-std::unique_ptr<GrMeshDrawOp> GrAnalyticRectOp::Make(GrColor color,
-                                                     const SkMatrix& viewMatrix,
-                                                     const SkRect& rect,
-                                                     const SkRect& croppedRect,
-                                                     const SkRect& bounds) {
-    return std::unique_ptr<GrMeshDrawOp>(
+std::unique_ptr<GrLegacyMeshDrawOp> GrAnalyticRectOp::Make(GrColor color,
+                                                           const SkMatrix& viewMatrix,
+                                                           const SkRect& rect,
+                                                           const SkRect& croppedRect,
+                                                           const SkRect& bounds) {
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
             new AnalyticRectOp(color, viewMatrix, rect, croppedRect, bounds));
 }
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(AnalyticRectOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AnalyticRectOp) {
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
     GrColor color = GrRandomColor(random);
     SkRect rect = GrTest::TestSquare(random);
     SkRect croppedRect = GrTest::TestSquare(random);
     SkRect bounds = GrTest::TestSquare(random);
-    return std::unique_ptr<GrMeshDrawOp>(
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
             new AnalyticRectOp(color, viewMatrix, rect, croppedRect, bounds));
 }
 
diff --git a/src/gpu/ops/GrAnalyticRectOp.h b/src/gpu/ops/GrAnalyticRectOp.h
index 4331794..9907de0 100644
--- a/src/gpu/ops/GrAnalyticRectOp.h
+++ b/src/gpu/ops/GrAnalyticRectOp.h
@@ -11,7 +11,7 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class SkMatrix;
 struct SkRect;
 
@@ -27,11 +27,11 @@
  */
 class GrAnalyticRectOp {
 public:
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                              const SkMatrix& viewMatrix,
-                                              const SkRect& rect,
-                                              const SkRect& croppedRect,
-                                              const SkRect& bounds);
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkRect& rect,
+                                                    const SkRect& croppedRect,
+                                                    const SkRect& bounds);
 };
 
 #endif  // GrAnalyticRectOp_DEFINED
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index f8c5443..69e8b4e 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -20,12 +20,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
-    unsigned r = SkColorGetR(c);
-    unsigned g = SkColorGetG(c);
-    unsigned b = SkColorGetB(c);
-    return GrColorPackRGBA(r, g, b, 0xff);
+#ifdef SK_GAMMA_APPLY_TO_A8
+static inline SkColor unpremultiplied_grcolor_to_skcolor(GrColor c) {
+    unsigned r = GrColorUnpackR(c);
+    unsigned g = GrColorUnpackG(c);
+    unsigned b = GrColorUnpackB(c);
+    return SkColorSetRGB(r, g, b);
 }
+#endif
 
 static const int kDistanceAdjustLumShift = 5;
 
@@ -46,8 +48,8 @@
     return str;
 }
 
-void GrAtlasTextOp::getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                                       GrPipelineAnalysisCoverage* coverage) const {
+void GrAtlasTextOp::getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                               GrProcessorAnalysisCoverage* coverage) const {
     if (kColorBitmapMask_MaskType == fMaskType) {
         color->setToUnknown();
     } else {
@@ -56,19 +58,19 @@
     switch (fMaskType) {
         case kGrayscaleDistanceField_MaskType:
         case kGrayscaleCoverageMask_MaskType:
-            *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+            *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
             break;
         case kLCDCoverageMask_MaskType:
         case kLCDDistanceField_MaskType:
-            *coverage = GrPipelineAnalysisCoverage::kLCD;
+            *coverage = GrProcessorAnalysisCoverage::kLCD;
             break;
         case kColorBitmapMask_MaskType:
-            *coverage = GrPipelineAnalysisCoverage::kNone;
+            *coverage = GrProcessorAnalysisCoverage::kNone;
             break;
     }
 }
 
-void GrAtlasTextOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
+void GrAtlasTextOp::applyPipelineOptimizations(const PipelineOptimizations& optimizations) {
     optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
 
     fColor = fGeoData[0].fColor;
@@ -145,14 +147,14 @@
     this->flush(target, &flushInfo);
 }
 
-void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
-    GrMesh mesh;
+void GrAtlasTextOp::flush(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
+    GrMesh mesh(kTriangles_GrPrimitiveType);
     int maxGlyphsPerDraw =
             static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
-    mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
-                       flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerGlyph,
-                       kIndicesPerGlyph, flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
-    target->draw(flushInfo->fGeometryProcessor.get(), mesh);
+    mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph,
+                             flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
+    mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
+    target->draw(flushInfo->fGeometryProcessor.get(), this->pipeline(), mesh);
     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
     flushInfo->fGlyphsToFlush = 0;
 }
@@ -222,7 +224,7 @@
 // TODO trying to figure out why lcd is so whack
 sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(GrResourceProvider* resourceProvider,
                                                            const SkMatrix& viewMatrix,
-                                                           SkColor filteredColor,
+                                                           GrColor filteredColor,
                                                            GrColor color,
                                                            sk_sp<GrTextureProxy> proxy) const {
     GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
@@ -237,16 +239,14 @@
         flags |= kUseLCD_DistanceFieldEffectFlag;
         flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
 
-        GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
-
         float redCorrection = fDistanceAdjustTable->getAdjustment(
-                GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift,
+                GrColorUnpackR(filteredColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         float greenCorrection = fDistanceAdjustTable->getAdjustment(
-                GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift,
+                GrColorUnpackG(filteredColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         float blueCorrection = fDistanceAdjustTable->getAdjustment(
-                GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift,
+                GrColorUnpackB(filteredColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
@@ -258,7 +258,9 @@
                                                    this->usesLocalCoords());
     } else {
 #ifdef SK_GAMMA_APPLY_TO_A8
-        U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
+        SkColor filteredSkColor = unpremultiplied_grcolor_to_skcolor(filteredColor);
+
+        U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredSkColor);
         float correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
                                                                fUseGammaCorrectDistanceTable);
         return GrDistanceFieldA8TextGeoProc::Make(resourceProvider, color,
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 8c08544..ac59d4c 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -13,7 +13,7 @@
 #include "text/GrAtlasTextContext.h"
 #include "text/GrDistanceFieldAdjustTable.h"
 
-class GrAtlasTextOp final : public GrMeshDrawOp {
+class GrAtlasTextOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -64,7 +64,7 @@
     static std::unique_ptr<GrAtlasTextOp> MakeDistanceField(
             int glyphCount, GrAtlasGlyphCache* fontCache,
             const GrDistanceFieldAdjustTable* distanceAdjustTable,
-            bool useGammaCorrectDistanceTable, SkColor filteredColor, bool isLCD, bool useBGR) {
+            bool useGammaCorrectDistanceTable, GrColor filteredColor, bool isLCD, bool useBGR) {
         std::unique_ptr<GrAtlasTextOp> op(new GrAtlasTextOp);
 
         op->fFontCache = fontCache;
@@ -99,9 +99,9 @@
     SkString dumpInfo() const override;
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor*,
-                                            GrPipelineAnalysisCoverage*) const override;
-    void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor*,
+                                    GrProcessorAnalysisCoverage*) const override;
+    void applyPipelineOptimizations(const PipelineOptimizations&) override;
 
     struct FlushInfo {
         sk_sp<const GrBuffer> fVertexBuffer;
@@ -138,7 +138,7 @@
         return kLCDCoverageMask_MaskType == fMaskType || kLCDDistanceField_MaskType == fMaskType;
     }
 
-    inline void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const;
+    inline void flush(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo) const;
 
     GrColor color() const { return fColor; }
     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
@@ -150,7 +150,7 @@
     // TODO just use class params
     // TODO trying to figure out why lcd is so whack
     sk_sp<GrGeometryProcessor> setupDfProcessor(GrResourceProvider*,
-                                                const SkMatrix& viewMatrix, SkColor filteredColor,
+                                                const SkMatrix& viewMatrix, GrColor filteredColor,
                                                 GrColor color, sk_sp<GrTextureProxy> proxy) const;
 
     GrColor fColor;
@@ -175,12 +175,12 @@
 
     // Distance field properties
     sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
-    SkColor fFilteredColor;
+    GrColor fFilteredColor;
     bool fUseGammaCorrectDistanceTable;
 
     friend class GrBlobRegenHelper;  // Needs to trigger flushes
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 /*
@@ -189,7 +189,7 @@
  */
 class GrBlobRegenHelper {
 public:
-    GrBlobRegenHelper(const GrAtlasTextOp* op, GrMeshDrawOp::Target* target,
+    GrBlobRegenHelper(const GrAtlasTextOp* op, GrLegacyMeshDrawOp::Target* target,
                       GrAtlasTextOp::FlushInfo* flushInfo)
             : fOp(op), fTarget(target), fFlushInfo(flushInfo) {}
 
@@ -199,7 +199,7 @@
 
 private:
     const GrAtlasTextOp* fOp;
-    GrMeshDrawOp::Target* fTarget;
+    GrLegacyMeshDrawOp::Target* fTarget;
     GrAtlasTextOp::FlushInfo* fFlushInfo;
 };
 
diff --git a/src/gpu/ops/GrClearOp.h b/src/gpu/ops/GrClearOp.h
index 455c684..6d0cf28 100644
--- a/src/gpu/ops/GrClearOp.h
+++ b/src/gpu/ops/GrClearOp.h
@@ -9,71 +9,78 @@
 #define GrClearOp_DEFINED
 
 #include "GrFixedClip.h"
-#include "GrGpu.h"
 #include "GrGpuCommandBuffer.h"
 #include "GrOp.h"
 #include "GrOpFlushState.h"
-#include "GrRenderTarget.h"
+#include "GrResourceProvider.h"
 
 class GrClearOp final : public GrOp {
 public:
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrClearOp> Make(const GrFixedClip& clip, GrColor color,
-                                           GrRenderTarget* rt) {
-        std::unique_ptr<GrClearOp> op(new GrClearOp(clip, color, rt));
-        if (!op->fRenderTarget) {
-            return nullptr; // The clip did not contain any pixels within the render target.
+                                           GrSurfaceProxy* dstProxy) {
+        const SkIRect rect = SkIRect::MakeWH(dstProxy->width(), dstProxy->height());
+        if (clip.scissorEnabled() && !SkIRect::Intersects(clip.scissorRect(), rect)) {
+            return nullptr;
         }
-        return op;
+
+        return std::unique_ptr<GrClearOp>(new GrClearOp(clip, color, dstProxy));
     }
 
-    static std::unique_ptr<GrClearOp> Make(const SkIRect& rect, GrColor color, GrRenderTarget* rt,
+    static std::unique_ptr<GrClearOp> Make(const SkIRect& rect, GrColor color,
                                            bool fullScreen) {
-        return std::unique_ptr<GrClearOp>(new GrClearOp(rect, color, rt, fullScreen));
+        SkASSERT(fullScreen || !rect.isEmpty());
+
+        return std::unique_ptr<GrClearOp>(new GrClearOp(rect, color, fullScreen));
     }
 
     const char* name() const override { return "Clear"; }
 
     SkString dumpInfo() const override {
-        SkString string("Scissor [");
+        SkString string;
+        string.append(INHERITED::dumpInfo());
+        string.appendf("Scissor [ ");
         if (fClip.scissorEnabled()) {
             const SkIRect& r = fClip.scissorRect();
             string.appendf("L: %d, T: %d, R: %d, B: %d", r.fLeft, r.fTop, r.fRight, r.fBottom);
+        } else {
+            string.append("disabled");
         }
-        string.appendf("], Color: 0x%08x, RT: %d", fColor,
-                                                   fRenderTarget.get()->uniqueID().asUInt());
-        string.append(INHERITED::dumpInfo());
+        string.appendf("], Color: 0x%08x\n", fColor);
         return string;
     }
 
+    GrColor color() const { return fColor; }
     void setColor(GrColor color) { fColor = color; }
 
 private:
-    GrClearOp(const GrFixedClip& clip, GrColor color, GrRenderTarget* rt)
+    GrClearOp(const GrFixedClip& clip, GrColor color, GrSurfaceProxy* proxy)
         : INHERITED(ClassID())
         , fClip(clip)
         , fColor(color) {
-        SkIRect rtRect = SkIRect::MakeWH(rt->width(), rt->height());
+
+        const SkIRect rtRect = SkIRect::MakeWH(proxy->width(), proxy->height());
         if (fClip.scissorEnabled()) {
             // Don't let scissors extend outside the RT. This may improve op combining.
             if (!fClip.intersect(rtRect)) {
-                return;
+                SkASSERT(0);  // should be caught upstream
+                fClip = GrFixedClip(SkIRect::MakeEmpty());
             }
-            if (fClip.scissorRect() == rtRect) {
+
+            if (GrResourceProvider::IsFunctionallyExact(proxy) && fClip.scissorRect() == rtRect) {
                 fClip.disableScissor();
             }
         }
         this->setBounds(SkRect::Make(fClip.scissorEnabled() ? fClip.scissorRect() : rtRect),
                         HasAABloat::kNo, IsZeroArea::kNo);
-        fRenderTarget.reset(rt);
     }
 
-    GrClearOp(const SkIRect& rect, GrColor color, GrRenderTarget* rt, bool fullScreen)
+    GrClearOp(const SkIRect& rect, GrColor color, bool fullScreen)
         : INHERITED(ClassID())
         , fClip(GrFixedClip(rect))
-        , fColor(color)
-        , fRenderTarget(rt) {
+        , fColor(color) {
+
         if (fullScreen) {
             fClip.disableScissor();
         }
@@ -85,7 +92,6 @@
         // contains the old clear, or when the new clear is a subset of the old clear and is the
         // same color.
         GrClearOp* cb = t->cast<GrClearOp>();
-        SkASSERT(cb->fRenderTarget == fRenderTarget);
         if (fClip.windowRectsState() != cb->fClip.windowRectsState()) {
             return false;
         }
@@ -110,12 +116,13 @@
     void onPrepare(GrOpFlushState*) override {}
 
     void onExecute(GrOpFlushState* state) override {
-        state->commandBuffer()->clear(fRenderTarget.get(), fClip, fColor);
+        SkASSERT(state->drawOpArgs().fRenderTarget);
+
+        state->commandBuffer()->clear(state->drawOpArgs().fRenderTarget, fClip, fColor);
     }
 
-    GrFixedClip                                             fClip;
-    GrColor                                                 fColor;
-    GrPendingIOResource<GrRenderTarget, kWrite_GrIOType>    fRenderTarget;
+    GrFixedClip fClip;
+    GrColor     fColor;
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/ops/GrClearStencilClipOp.h b/src/gpu/ops/GrClearStencilClipOp.h
index ef46a4c..4218146 100644
--- a/src/gpu/ops/GrClearStencilClipOp.h
+++ b/src/gpu/ops/GrClearStencilClipOp.h
@@ -9,19 +9,17 @@
 #define GrClearStencilClipOp_DEFINED
 
 #include "GrFixedClip.h"
-#include "GrGpu.h"
 #include "GrGpuCommandBuffer.h"
 #include "GrOp.h"
 #include "GrOpFlushState.h"
-#include "GrRenderTarget.h"
 
 class GrClearStencilClipOp final : public GrOp {
 public:
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrOp> Make(const GrFixedClip& clip, bool insideStencilMask,
-                                      GrRenderTarget* rt) {
-        return std::unique_ptr<GrOp>(new GrClearStencilClipOp(clip, insideStencilMask, rt));
+                                      GrRenderTargetProxy* proxy) {
+        return std::unique_ptr<GrOp>(new GrClearStencilClipOp(clip, insideStencilMask, proxy));
     }
 
     const char* name() const override { return "ClearStencilClip"; }
@@ -31,21 +29,23 @@
         if (fClip.scissorEnabled()) {
             const SkIRect& r = fClip.scissorRect();
             string.appendf("L: %d, T: %d, R: %d, B: %d", r.fLeft, r.fTop, r.fRight, r.fBottom);
+        } else {
+            string.append("disabled");
         }
-        string.appendf("], IC: %d, RT: %d", fInsideStencilMask,
-                       fRenderTarget.get()->uniqueID().asUInt());
+        string.appendf("], insideMask: %s\n", fInsideStencilMask ? "true" : "false");
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
 private:
-    GrClearStencilClipOp(const GrFixedClip& clip, bool insideStencilMask, GrRenderTarget* rt)
+    GrClearStencilClipOp(const GrFixedClip& clip, bool insideStencilMask,
+                         GrRenderTargetProxy* proxy)
             : INHERITED(ClassID())
             , fClip(clip)
-            , fInsideStencilMask(insideStencilMask)
-            , fRenderTarget(rt) {
-        const SkRect& bounds = fClip.scissorEnabled() ? SkRect::Make(fClip.scissorRect())
-                                                      : SkRect::MakeIWH(rt->width(), rt->height());
+            , fInsideStencilMask(insideStencilMask) {
+        const SkRect& bounds = fClip.scissorEnabled()
+                                            ? SkRect::Make(fClip.scissorRect())
+                                            : SkRect::MakeIWH(proxy->width(), proxy->height());
         this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
     }
 
@@ -54,12 +54,14 @@
     void onPrepare(GrOpFlushState*) override {}
 
     void onExecute(GrOpFlushState* state) override {
-        state->commandBuffer()->clearStencilClip(fRenderTarget.get(), fClip, fInsideStencilMask);
+        SkASSERT(state->drawOpArgs().fRenderTarget);
+
+        state->commandBuffer()->clearStencilClip(state->drawOpArgs().fRenderTarget,
+                                                 fClip, fInsideStencilMask);
     }
 
     const GrFixedClip fClip;
-    const bool fInsideStencilMask;
-    GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
+    const bool        fInsideStencilMask;
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/ops/GrCopySurfaceOp.cpp b/src/gpu/ops/GrCopySurfaceOp.cpp
index 87c490b..1a9a5e0 100644
--- a/src/gpu/ops/GrCopySurfaceOp.cpp
+++ b/src/gpu/ops/GrCopySurfaceOp.cpp
@@ -8,12 +8,12 @@
 #include "GrCopySurfaceOp.h"
 
 // returns true if the read/written rect intersects the src/dst and false if not.
-bool GrCopySurfaceOp::ClipSrcRectAndDstPoint(const GrSurface* dst,
-                                             const GrSurface* src,
-                                             const SkIRect& srcRect,
-                                             const SkIPoint& dstPoint,
-                                             SkIRect* clippedSrcRect,
-                                             SkIPoint* clippedDstPoint) {
+static bool clip_src_rect_and_dst_point(const GrSurfaceProxy* dst,
+                                        const GrSurfaceProxy* src,
+                                        const SkIRect& srcRect,
+                                        const SkIPoint& dstPoint,
+                                        SkIRect* clippedSrcRect,
+                                        SkIPoint* clippedDstPoint) {
     *clippedSrcRect = srcRect;
     *clippedDstPoint = dstPoint;
 
@@ -58,21 +58,22 @@
     return !clippedSrcRect->isEmpty();
 }
 
-std::unique_ptr<GrOp> GrCopySurfaceOp::Make(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+std::unique_ptr<GrOp> GrCopySurfaceOp::Make(GrSurfaceProxy* dstProxy, GrSurfaceProxy* srcProxy,
+                                            const SkIRect& srcRect,
                                             const SkIPoint& dstPoint) {
-    SkASSERT(dst);
-    SkASSERT(src);
-    if (GrPixelConfigIsSint(dst->config()) != GrPixelConfigIsSint(src->config())) {
-        return nullptr;
-    }
-    if (GrPixelConfigIsCompressed(dst->config())) {
+    SkASSERT(dstProxy);
+    SkASSERT(srcProxy);
+    if (GrPixelConfigIsSint(dstProxy->config()) != GrPixelConfigIsSint(srcProxy->config())) {
         return nullptr;
     }
     SkIRect clippedSrcRect;
     SkIPoint clippedDstPoint;
-    // If the rect is outside the src or dst then we've already succeeded.
-    if (!ClipSrcRectAndDstPoint(dst, src, srcRect, dstPoint, &clippedSrcRect, &clippedDstPoint)) {
+    // If the rect is outside the srcProxy or dstProxy then we've already succeeded.
+    if (!clip_src_rect_and_dst_point(dstProxy, srcProxy, srcRect, dstPoint,
+                                     &clippedSrcRect, &clippedDstPoint)) {
         return nullptr;
     }
-    return std::unique_ptr<GrOp>(new GrCopySurfaceOp(dst, src, clippedSrcRect, clippedDstPoint));
+
+    return std::unique_ptr<GrOp>(new GrCopySurfaceOp(dstProxy, srcProxy,
+                                                     clippedSrcRect, clippedDstPoint));
 }
diff --git a/src/gpu/ops/GrCopySurfaceOp.h b/src/gpu/ops/GrCopySurfaceOp.h
index 4c4500b..6c80408 100644
--- a/src/gpu/ops/GrCopySurfaceOp.h
+++ b/src/gpu/ops/GrCopySurfaceOp.h
@@ -8,45 +8,41 @@
 #ifndef GrCopySurfaceOp_DEFINED
 #define GrCopySurfaceOp_DEFINED
 
-#include "GrGpu.h"
 #include "GrOp.h"
 #include "GrOpFlushState.h"
-#include "GrRenderTarget.h"
 
 class GrCopySurfaceOp final : public GrOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    /** This should not really be exposed as Create() will apply this clipping, but there is
-     *  currently a workaround in GrContext::copySurface() for non-render target dsts that relies
-     *  on it. */
-    static bool ClipSrcRectAndDstPoint(const GrSurface* dst,
-                                       const GrSurface* src,
-                                       const SkIRect& srcRect,
-                                       const SkIPoint& dstPoint,
-                                       SkIRect* clippedSrcRect,
-                                       SkIPoint* clippedDstPoint);
-
-    static std::unique_ptr<GrOp> Make(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
+    static std::unique_ptr<GrOp> Make(GrSurfaceProxy* dst, GrSurfaceProxy* src,
+                                      const SkIRect& srcRect,
                                       const SkIPoint& dstPoint);
 
     const char* name() const override { return "CopySurface"; }
 
     SkString dumpInfo() const override {
         SkString string;
-        string.printf(
-                "SRC: 0x%p, DST: 0x%p, SRECT: [L: %d, T: %d, R: %d, B: %d], "
-                "DPT:[X: %d, Y: %d]",
-                fDst.get(), fSrc.get(), fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight,
-                fSrcRect.fBottom, fDstPoint.fX, fDstPoint.fY);
         string.append(INHERITED::dumpInfo());
+        string.printf("srcProxyID: %d, dstProxyID: %d,\n"
+                      "srcRect: [ L: %d, T: %d, R: %d, B: %d ], dstPt: [ X: %d, Y: %d ]\n",
+                      fSrc.get()->uniqueID().asUInt(),
+                      fDst.get()->uniqueID().asUInt(),
+                      fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom,
+                      fDstPoint.fX, fDstPoint.fY);
         return string;
     }
 
+    bool needsCommandBufferIsolation() const override { return true; }
+
 private:
-    GrCopySurfaceOp(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
-                    const SkIPoint& dstPoint)
-            : INHERITED(ClassID()), fDst(dst), fSrc(src), fSrcRect(srcRect), fDstPoint(dstPoint) {
+    GrCopySurfaceOp(GrSurfaceProxy* dst, GrSurfaceProxy* src,
+                    const SkIRect& srcRect, const SkIPoint& dstPoint)
+            : INHERITED(ClassID())
+            , fDst(dst)
+            , fSrc(src)
+            , fSrcRect(srcRect)
+            , fDstPoint(dstPoint) {
         SkRect bounds =
                 SkRect::MakeXYWH(SkIntToScalar(dstPoint.fX), SkIntToScalar(dstPoint.fY),
                                  SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
@@ -58,19 +54,23 @@
     void onPrepare(GrOpFlushState*) override {}
 
     void onExecute(GrOpFlushState* state) override {
-        if (!state->commandBuffer()) {
-            state->gpu()->copySurface(fDst.get(), fSrc.get(), fSrcRect, fDstPoint);
-        } else {
-            // Currently we are not sending copies through the GrGpuCommandBuffer. See comment in
-            // renderTargetUniqueID().
-            SkASSERT(false);
+        SkASSERT(!state->commandBuffer());
+
+        if (!fDst.get()->instantiate(state->resourceProvider()) ||
+            !fSrc.get()->instantiate(state->resourceProvider())) {
+            return;
         }
+
+        state->gpu()->copySurface(fDst.get()->priv().peekSurface(),
+                                  fSrc.get()->priv().peekSurface(), fSrcRect, fDstPoint);
     }
 
-    GrPendingIOResource<GrSurface, kWrite_GrIOType> fDst;
-    GrPendingIOResource<GrSurface, kRead_GrIOType> fSrc;
-    SkIRect fSrcRect;
-    SkIPoint fDstPoint;
+    // For RenderTargetContexts 'fDst' is redundant with the RenderTarget that will be passed
+    // into onExecute in the drawOpArgs.
+    GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fDst;
+    GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType>  fSrc;
+    SkIRect                                              fSrcRect;
+    SkIPoint                                             fDstPoint;
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/ops/GrDashLinePathRenderer.cpp b/src/gpu/ops/GrDashLinePathRenderer.cpp
index 9f170ff..cafe247 100644
--- a/src/gpu/ops/GrDashLinePathRenderer.cpp
+++ b/src/gpu/ops/GrDashLinePathRenderer.cpp
@@ -46,7 +46,7 @@
     }
     SkPoint pts[2];
     SkAssertResult(args.fShape->asLine(pts, nullptr));
-    std::unique_ptr<GrMeshDrawOp> op = GrDashOp::MakeDashLineOp(
+    std::unique_ptr<GrLegacyMeshDrawOp> op = GrDashOp::MakeDashLineOp(
             args.fPaint.getColor(), *args.fViewMatrix, pts, aaMode, args.fShape->style());
     if (!op) {
         return false;
@@ -55,6 +55,7 @@
     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
 
-    args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
+    args.fRenderTargetContext->addLegacyMeshDrawOp(
+            std::move(pipelineBuilder), *args.fClip, std::move(op));
     return true;
 }
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index 9dfacde..dda9fa2 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -237,7 +237,7 @@
                                                const SkMatrix& localMatrix,
                                                bool usesLocalCoords);
 
-class DashOp final : public GrMeshDrawOp {
+class DashOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
     struct LineData {
@@ -251,9 +251,11 @@
         SkScalar fPerpendicularScale;
     };
 
-    static std::unique_ptr<GrMeshDrawOp> Make(const LineData& geometry, GrColor color,
-                                              SkPaint::Cap cap, AAMode aaMode, bool fullDash) {
-        return std::unique_ptr<GrMeshDrawOp>(new DashOp(geometry, color, cap, aaMode, fullDash));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(const LineData& geometry, GrColor color,
+                                                    SkPaint::Cap cap, AAMode aaMode,
+                                                    bool fullDash) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(
+                new DashOp(geometry, color, cap, aaMode, fullDash));
     }
 
     const char* name() const override { return "DashOp"; }
@@ -296,13 +298,13 @@
         this->setTransformedBounds(bounds, combinedMatrix, aaBloat, zeroArea);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
 
         fUsesLocalCoords = optimizations.readsLocalCoords();
@@ -625,7 +627,7 @@
             rectIndex++;
         }
         SkASSERT(0 == (curVIdx % 4) && (curVIdx / 4) == totalRectCount);
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -679,14 +681,14 @@
     bool fFullDash;
     SkSTArray<1, LineData, true> fLines;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
-std::unique_ptr<GrMeshDrawOp> GrDashOp::MakeDashLineOp(GrColor color,
-                                                       const SkMatrix& viewMatrix,
-                                                       const SkPoint pts[2],
-                                                       AAMode aaMode,
-                                                       const GrStyle& style) {
+std::unique_ptr<GrLegacyMeshDrawOp> GrDashOp::MakeDashLineOp(GrColor color,
+                                                             const SkMatrix& viewMatrix,
+                                                             const SkPoint pts[2],
+                                                             AAMode aaMode,
+                                                             const GrStyle& style) {
     SkASSERT(GrDashOp::CanDrawDashLine(pts, style, viewMatrix));
     const SkScalar* intervals = style.dashIntervals();
     SkScalar phase = style.dashPhase();
@@ -1193,7 +1195,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(DashOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(DashOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
     AAMode aaMode = static_cast<AAMode>(random->nextULessThan(GrDashOp::kAAModeCnt));
diff --git a/src/gpu/ops/GrDashOp.h b/src/gpu/ops/GrDashOp.h
index 6ffd326..d3e0b6f 100644
--- a/src/gpu/ops/GrDashOp.h
+++ b/src/gpu/ops/GrDashOp.h
@@ -12,7 +12,7 @@
 #include "GrTypesPriv.h"
 #include "SkPathEffect.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class GrStyle;
 
 namespace GrDashOp {
@@ -23,8 +23,9 @@
 };
 static const int kAAModeCnt = static_cast<int>(AAMode::kCoverageWithMSAA) + 1;
 
-std::unique_ptr<GrMeshDrawOp> MakeDashLineOp(GrColor, const SkMatrix& viewMatrix,
-                                             const SkPoint pts[2], AAMode, const GrStyle& style);
+std::unique_ptr<GrLegacyMeshDrawOp> MakeDashLineOp(GrColor, const SkMatrix& viewMatrix,
+                                                   const SkPoint pts[2], AAMode,
+                                                   const GrStyle& style);
 bool CanDrawDashLine(const SkPoint pts[2], const GrStyle& style, const SkMatrix& viewMatrix);
 }
 
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 62c9d45..c282036 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -22,12 +22,9 @@
 #include "SkTraceEvent.h"
 
 #include "ops/GrMeshDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
-GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
-                                             bool stencilWrapOpsSupport)
-    : fSeparateStencil(separateStencilSupport)
-    , fStencilWrapOps(stencilWrapOpsSupport) {
+GrDefaultPathRenderer::GrDefaultPathRenderer() {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -94,15 +91,16 @@
     }
 }
 
-class DefaultPathOp final : public GrMeshDrawOp {
+class DefaultPathOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkPath& path, SkScalar tolerance,
-                                              uint8_t coverage, const SkMatrix& viewMatrix,
-                                              bool isHairline, const SkRect& devBounds) {
-        return std::unique_ptr<GrMeshDrawOp>(new DefaultPathOp(color, path, tolerance, coverage,
-                                                               viewMatrix, isHairline, devBounds));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkPath& path,
+                                                    SkScalar tolerance, uint8_t coverage,
+                                                    const SkMatrix& viewMatrix, bool isHairline,
+                                                    const SkRect& devBounds) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new DefaultPathOp(
+                color, path, tolerance, coverage, viewMatrix, isHairline, devBounds));
     }
 
     const char* name() const override { return "DefaultPathOp"; }
@@ -132,14 +130,14 @@
                         isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = this->coverage() == 0xff ? GrPipelineAnalysisCoverage::kNone
-                                             : GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = this->coverage() == 0xff ? GrProcessorAnalysisCoverage::kNone
+                                             : GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
         fUsesLocalCoords = optimizations.readsLocalCoords();
     }
@@ -249,14 +247,14 @@
             SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
         }
 
-        GrMesh mesh;
-        if (isIndexed) {
-            mesh.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
-                             vertexOffset, indexOffset);
+        GrMesh mesh(primitiveType);
+        if (!isIndexed) {
+            mesh.setNonIndexedNonInstanced(vertexOffset);
         } else {
-            mesh.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
+            mesh.setIndexed(indexBuffer, indexOffset, firstIndex, 0, vertexOffset - 1);
         }
-        target->draw(gp.get(), mesh);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(gp.get(), this->pipeline(), mesh);
 
         // put back reserves
         target->putBackIndices((size_t)(maxIndices - indexOffset));
@@ -343,9 +341,7 @@
                     case SkPath::kConic_Verb: {
                         SkScalar weight = iter.conicWeight();
                         SkAutoConicToQuads converter;
-                        // Converting in src-space, hance the finer tolerance (0.25)
-                        // TODO: find a way to do this in dev-space so the tolerance means something
-                        const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
+                        const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
                         for (int i = 0; i < converter.countQuads(); ++i) {
                             add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
                                      isIndexed, this->isHairline(), subpathIdxStart,
@@ -403,7 +399,7 @@
     bool fIsHairline;
     SkSTArray<1, PathData, true> fPaths;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
@@ -429,8 +425,7 @@
     }
 
     int                          passCount = 0;
-    const GrUserStencilSettings* passes[3];
-    GrDrawFace                   drawFace[3];
+    const GrUserStencilSettings* passes[2];
     bool                         reverse = false;
     bool                         lastPassIsBounds;
 
@@ -442,7 +437,6 @@
             passes[0] = &userStencilSettings;
         }
         lastPassIsBounds = false;
-        drawFace[0] = GrDrawFace::kBoth;
     } else {
         if (single_pass_shape(shape)) {
             passCount = 1;
@@ -451,7 +445,6 @@
             } else {
                 passes[0] = &userStencilSettings;
             }
-            drawFace[0] = GrDrawFace::kBoth;
             lastPassIsBounds = false;
         } else {
             switch (path.getFillType()) {
@@ -472,40 +465,19 @@
                             passes[1] = &gEOColorPass;
                         }
                     }
-                    drawFace[0] = drawFace[1] = GrDrawFace::kBoth;
                     break;
 
                 case SkPath::kInverseWinding_FillType:
                     reverse = true;
                     // fallthrough
                 case SkPath::kWinding_FillType:
-                    if (fSeparateStencil) {
-                        if (fStencilWrapOps) {
-                            passes[0] = &gWindStencilSeparateWithWrap;
-                        } else {
-                            passes[0] = &gWindStencilSeparateNoWrap;
-                        }
-                        passCount = 2;
-                        drawFace[0] = GrDrawFace::kBoth;
-                    } else {
-                        if (fStencilWrapOps) {
-                            passes[0] = &gWindSingleStencilWithWrapInc;
-                            passes[1] = &gWindSingleStencilWithWrapDec;
-                        } else {
-                            passes[0] = &gWindSingleStencilNoWrapInc;
-                            passes[1] = &gWindSingleStencilNoWrapDec;
-                        }
-                        // which is cw and which is ccw is arbitrary.
-                        drawFace[0] = GrDrawFace::kCW;
-                        drawFace[1] = GrDrawFace::kCCW;
-                        passCount = 3;
-                    }
+                    passes[0] = &gWindStencilPass;
+                    passCount = 2;
                     if (stencilOnly) {
                         lastPassIsBounds = false;
                         --passCount;
                     } else {
                         lastPassIsBounds = true;
-                        drawFace[passCount-1] = GrDrawFace::kBoth;
                         if (reverse) {
                             passes[passCount-1] = &gInvWindColorPass;
                         } else {
@@ -548,16 +520,12 @@
             }
             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
                                                                                viewMatrix;
-            std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                    paint.getColor(), viewM, bounds, nullptr, &localMatrix));
-
-            SkASSERT(GrDrawFace::kBoth == drawFace[p]);
-            GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-            pipelineBuilder.setDrawFace(drawFace[p]);
-            pipelineBuilder.setUserStencil(passes[p]);
-            renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+            renderTargetContext->addDrawOp(
+                    clip,
+                    GrNonAAFillRectOp::Make(std::move(paint), viewM, bounds, nullptr, &localMatrix,
+                                            aaType, passes[p]));
         } else {
-            std::unique_ptr<GrMeshDrawOp> op =
+            std::unique_ptr<GrLegacyMeshDrawOp> op =
                     DefaultPathOp::Make(paint.getColor(), path, srcSpaceTol, newCoverage,
                                         viewMatrix, isHairline, devBounds);
             bool stencilPass = stencilOnly || passCount > 1;
@@ -566,19 +534,23 @@
                 passPaint.paint().setXPFactory(GrDisableColorXPFactory::Get());
             }
             GrPipelineBuilder pipelineBuilder(std::move(passPaint), aaType);
-            pipelineBuilder.setDrawFace(drawFace[p]);
             pipelineBuilder.setUserStencil(passes[p]);
-            renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+            renderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip,
+                                                     std::move(op));
         }
     }
     return true;
 }
 
 bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
+    bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
+    // If we aren't a single_pass_shape or hairline, we require stencil buffers.
+    if (!(single_pass_shape(*args.fShape) || isHairline) && args.fCaps->avoidStencilBuffers()) {
+        return false;
+    }
     // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
     return GrAAType::kCoverage != args.fAAType &&
-           (args.fShape->style().isSimpleFill() ||
-            IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr));
+           (args.fShape->style().isSimpleFill() || isHairline);
 }
 
 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
@@ -611,7 +583,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(DefaultPathOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
 
diff --git a/src/gpu/ops/GrDefaultPathRenderer.h b/src/gpu/ops/GrDefaultPathRenderer.h
index 0a84eb9..7f7ab2a 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.h
+++ b/src/gpu/ops/GrDefaultPathRenderer.h
@@ -18,10 +18,9 @@
  */
 class SK_API GrDefaultPathRenderer : public GrPathRenderer {
 public:
-    GrDefaultPathRenderer(bool separateStencilSupport, bool stencilWrapOpsSupport);
+    GrDefaultPathRenderer();
 
 private:
-
     StencilSupport onGetStencilSupport(const GrShape&) const override;
 
     bool onCanDrawPath(const CanDrawPathArgs&) const override;
@@ -39,9 +38,6 @@
                           const GrShape&,
                           bool stencilOnly);
 
-    bool    fSeparateStencil;
-    bool    fStencilWrapOps;
-
     typedef GrPathRenderer INHERITED;
 };
 
diff --git a/src/gpu/ops/GrDiscardOp.h b/src/gpu/ops/GrDiscardOp.h
index 098df63..693a089 100644
--- a/src/gpu/ops/GrDiscardOp.h
+++ b/src/gpu/ops/GrDiscardOp.h
@@ -8,44 +8,42 @@
 #ifndef GrDiscardOp_DEFINED
 #define GrDiscardOp_DEFINED
 
-#include "GrGpu.h"
 #include "GrOp.h"
 #include "GrOpFlushState.h"
 #include "GrRenderTarget.h"
+#include "GrRenderTargetProxy.h"
 
 class GrDiscardOp final : public GrOp {
 public:
     DEFINE_OP_CLASS_ID
-    static std::unique_ptr<GrOp> Make(GrRenderTarget* rt) {
-        return std::unique_ptr<GrOp>(new GrDiscardOp(rt));
+
+    static std::unique_ptr<GrOp> Make(GrRenderTargetProxy* proxy) {
+        return std::unique_ptr<GrOp>(new GrDiscardOp(proxy));
     }
 
     const char* name() const override { return "Discard"; }
 
     SkString dumpInfo() const override {
         SkString string;
-        string.printf("RT: %d", fRenderTarget.get()->uniqueID().asUInt());
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
 private:
-    GrDiscardOp(GrRenderTarget* rt) : INHERITED(ClassID()), fRenderTarget(rt) {
-        this->setBounds(SkRect::MakeIWH(rt->width(), rt->height()), HasAABloat::kNo,
-                        IsZeroArea::kNo);
+    GrDiscardOp(GrRenderTargetProxy* proxy) : INHERITED(ClassID()) {
+        this->setBounds(SkRect::MakeIWH(proxy->width(), proxy->height()),
+                        HasAABloat::kNo, IsZeroArea::kNo);
     }
 
-    bool onCombineIfPossible(GrOp* that, const GrCaps& caps) override {
-        return fRenderTarget.get() == that->cast<GrDiscardOp>()->fRenderTarget.get();
-    }
+    bool onCombineIfPossible(GrOp* that, const GrCaps& caps) override { return false; }
 
     void onPrepare(GrOpFlushState*) override {}
 
     void onExecute(GrOpFlushState* state) override {
-        state->commandBuffer()->discard(fRenderTarget.get());
-    }
+        SkASSERT(state->drawOpArgs().fRenderTarget);
 
-    GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
+        state->commandBuffer()->discard(state->drawOpArgs().fRenderTarget);
+    }
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index 7ef7e06..1c7251a 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -12,7 +12,7 @@
 #include "SkRSXform.h"
 #include "SkRandom.h"
 
-void GrDrawAtlasOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
+void GrDrawAtlasOp::applyPipelineOptimizations(const PipelineOptimizations& optimizations) {
     SkASSERT(fGeoData.count() == 1);
     if (optimizations.getOverrideColorIfSet(&fGeoData[0].fColor) && fHasColors) {
         size_t vertexStride =
@@ -67,7 +67,7 @@
         memcpy(vertPtr, args.fVerts.begin(), allocSize);
         vertPtr += allocSize;
     }
-    helper.recordDraw(target, gp.get());
+    helper.recordDraw(target, gp.get(), this->pipeline());
 }
 
 GrDrawAtlasOp::GrDrawAtlasOp(GrColor color, const SkMatrix& viewMatrix, int spriteCount,
@@ -222,7 +222,7 @@
     }
 }
 
-DRAW_OP_TEST_DEFINE(GrDrawAtlasOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(GrDrawAtlasOp) {
     uint32_t spriteCount = random->nextRangeU(1, 100);
 
     SkTArray<SkRSXform> xforms(spriteCount);
diff --git a/src/gpu/ops/GrDrawAtlasOp.h b/src/gpu/ops/GrDrawAtlasOp.h
index a39a7a9..79a6098 100644
--- a/src/gpu/ops/GrDrawAtlasOp.h
+++ b/src/gpu/ops/GrDrawAtlasOp.h
@@ -12,14 +12,14 @@
 #include "GrDefaultGeoProcFactory.h"
 #include "GrMeshDrawOp.h"
 
-class GrDrawAtlasOp final : public GrMeshDrawOp {
+class GrDrawAtlasOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              int spriteCount, const SkRSXform* xforms,
-                                              const SkRect* rects, const SkColor* colors) {
-        return std::unique_ptr<GrMeshDrawOp>(
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
+                                                    int spriteCount, const SkRSXform* xforms,
+                                                    const SkRect* rects, const SkColor* colors) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(
                 new GrDrawAtlasOp(color, viewMatrix, spriteCount, xforms, rects, colors));
     }
 
@@ -39,19 +39,19 @@
     GrDrawAtlasOp(GrColor color, const SkMatrix& viewMatrix, int spriteCount,
                   const SkRSXform* xforms, const SkRect* rects, const SkColor* colors);
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         if (this->hasColors()) {
             color->setToUnknown();
         } else {
             color->setToConstant(fGeoData[0].fColor);
         }
-        *coverage = GrPipelineAnalysisCoverage::kNone;
+        *coverage = GrProcessorAnalysisCoverage::kNone;
     }
 
     void onPrepareDraws(Target*) const override;
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
+    void applyPipelineOptimizations(const PipelineOptimizations&) override;
 
     GrColor color() const { return fColor; }
     const SkMatrix& viewMatrix() const { return fViewMatrix; }
@@ -72,7 +72,7 @@
     int fQuadCount;
     bool fHasColors;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 #endif
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index 0458370..dbdd9d8 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -76,7 +76,8 @@
      * This is called after the GrAppliedClip has been computed and just prior to recording the op
      * or combining it with a previously recorded op. It is used to determine whether a copy of the
      * destination (or destination texture itself) needs to be provided to the xp when this op
-     * executes.
+     * executes. This is guaranteed to be called before an op is recorded. However, this is also
+     * called on ops that are not recorded because they combine with a previously recorded op.
      */
     virtual bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) = 0;
 
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index fa08ae6..c33af39 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -12,13 +12,14 @@
 #include "SkTemplates.h"
 
 GrDrawPathOpBase::GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
-                                   GrPathRendering::FillType fill, GrAA aa)
+                                   GrPathRendering::FillType fill, GrAAType aaType)
         : INHERITED(classID)
         , fViewMatrix(viewMatrix)
-        , fProcessorSet(std::move(paint))
-        , fAnalysis(paint.getColor())
+        , fInputColor(paint.getColor())
         , fFillType(fill)
-        , fAA(aa) {}
+        , fAAType(aaType)
+        , fPipelineSRGBFlags(GrPipeline::SRGBFlagsFromPaint(paint))
+        , fProcessorSet(std::move(paint)) {}
 
 SkString GrDrawPathOp::dumpInfo() const {
     SkString string;
@@ -27,8 +28,7 @@
     return string;
 }
 
-GrPipelineOptimizations GrDrawPathOpBase::initPipeline(const GrOpFlushState& state,
-                                                       GrPipeline* pipeline) {
+void GrDrawPathOpBase::initPipeline(const GrOpFlushState& state, GrPipeline* pipeline) {
     static constexpr GrUserStencilSettings kCoverPass{
             GrUserStencilSettings::StaticInit<
                     0x0000,
@@ -40,14 +40,16 @@
     };
     GrPipeline::InitArgs args;
     args.fProcessors = &this->processors();
-    args.fFlags = GrAA::kYes == fAA ? GrPipeline::kHWAntialias_Flag : 0;
+    args.fFlags = fPipelineSRGBFlags;
+    if (GrAATypeIsHW(fAAType)) {
+        args.fFlags |= GrPipeline::kHWAntialias_Flag;
+    }
     args.fUserStencil = &kCoverPass;
     args.fAppliedClip = state.drawOpArgs().fAppliedClip;
     args.fRenderTarget = state.drawOpArgs().fRenderTarget;
     args.fCaps = &state.caps();
-    args.fDstTexture = state.drawOpArgs().fDstTexture;
-    args.fAnalysis =
-            &this->doFragmentProcessorAnalysis(state.caps(), state.drawOpArgs().fAppliedClip);
+    args.fResourceProvider = state.resourceProvider();
+    args.fDstProxy = state.drawOpArgs().fDstProxy;
 
     return pipeline->init(args);
 }
@@ -65,11 +67,9 @@
 //////////////////////////////////////////////////////////////////////////////
 
 void GrDrawPathOp::onExecute(GrOpFlushState* state) {
-    GrColor color = this->color();
     GrPipeline pipeline;
-    GrPipelineOptimizations optimizations = this->initPipeline(*state, &pipeline);
-    optimizations.getOverrideColorIfSet(&color);
-    sk_sp<GrPathProcessor> pathProc(GrPathProcessor::Create(color, this->viewMatrix()));
+    this->initPipeline(*state, &pipeline);
+    sk_sp<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(), this->viewMatrix()));
 
     GrStencilSettings stencil;
     init_stencil_pass_settings(*state, this->fillType(), &stencil);
@@ -92,9 +92,9 @@
 
 GrDrawPathRangeOp::GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
                                      SkScalar y, GrPaint&& paint, GrPathRendering::FillType fill,
-                                     GrAA aa, GrPathRange* range, const InstanceData* instanceData,
-                                     const SkRect& bounds)
-        : INHERITED(ClassID(), viewMatrix, std::move(paint), fill, aa)
+                                     GrAAType aaType, GrPathRange* range,
+                                     const InstanceData* instanceData, const SkRect& bounds)
+        : INHERITED(ClassID(), viewMatrix, std::move(paint), fill, aaType)
         , fPathRange(range)
         , fTotalPathCount(instanceData->count())
         , fScale(scale) {
@@ -116,6 +116,9 @@
     if (this->processors() != that->processors()) {
         return false;
     }
+    if (this->pipelineSRGBFlags() != that->pipelineSRGBFlags()) {
+        return false;
+    }
     switch (fDraws.head()->fInstanceData->transformType()) {
         case GrPathRendering::kNone_PathTransformType:
             if (this->fDraws.head()->fX != that->fDraws.head()->fX ||
@@ -148,13 +151,7 @@
         GrPathRendering::kWinding_FillType != that->fillType()) {
         return false;
     }
-    // If we have non-clipping coverage processors we don't try to merge as its unclear whether it
-    // will be correct. We don't expect this to happen in practice.
-    if (this->processors().numCoverageFragmentProcessors()) {
-        return false;
-    }
-    bool opaque = this->fragmentProcessorAnalysis().isOutputColorOpaque();
-    if (!GrXPFactory::CanCombineOverlappedStencilAndCover(this->processors().xpFactory(), opaque)) {
+    if (!this->processorAnalysis().canCombineOverlappedStencilAndCover()) {
         return false;
     }
     fTotalPathCount += that->fTotalPathCount;
diff --git a/src/gpu/ops/GrDrawPathOp.h b/src/gpu/ops/GrDrawPathOp.h
index b1ce2df..40db6ef 100644
--- a/src/gpu/ops/GrDrawPathOp.h
+++ b/src/gpu/ops/GrDrawPathOp.h
@@ -23,32 +23,34 @@
 
 class GrDrawPathOpBase : public GrDrawOp {
 protected:
-    GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
-                     GrPathRendering::FillType fill, GrAA aa);
+    GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&&,
+                     GrPathRendering::FillType, GrAAType);
     FixedFunctionFlags fixedFunctionFlags() const override {
-        return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
+        if (GrAATypeIsHW(fAAType)) {
+            return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
+        }
+        return FixedFunctionFlags::kUsesStencil;
     }
     bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
-        return GrXPFactory::WillNeedDstTexture(fProcessorSet.xpFactory(), caps,
-                                               this->doFragmentProcessorAnalysis(caps, clip));
+        return this->doProcessorAnalysis(caps, clip).requiresDstTexture();
     }
 
-    void wasRecorded() override { fProcessorSet.makePendingExecution(); }
-
 protected:
     const SkMatrix& viewMatrix() const { return fViewMatrix; }
-    GrColor color() const { return fAnalysis.inputColor(); }
+    GrColor color() const { return fInputColor; }
     GrPathRendering::FillType fillType() const { return fFillType; }
     const GrProcessorSet& processors() const { return fProcessorSet; }
-    GrPipelineOptimizations initPipeline(const GrOpFlushState&, GrPipeline*);
-    const GrProcessorSet::FragmentProcessorAnalysis& doFragmentProcessorAnalysis(
-            const GrCaps& caps, const GrAppliedClip* clip) {
-        fProcessorSet.analyzeAndEliminateFragmentProcessors(
-                &fAnalysis, fAnalysis.inputColor(), GrPipelineAnalysisCoverage::kNone, clip, caps);
+    uint32_t pipelineSRGBFlags() const { return fPipelineSRGBFlags; }
+    void initPipeline(const GrOpFlushState&, GrPipeline*);
+    const GrProcessorSet::Analysis& doProcessorAnalysis(const GrCaps& caps,
+                                                        const GrAppliedClip* clip) {
+        bool isMixedSamples = GrAAType::kMixedSamples == fAAType;
+        fAnalysis = fProcessorSet.finalize(fInputColor, GrProcessorAnalysisCoverage::kNone, clip,
+                                           isMixedSamples, caps, &fInputColor);
         return fAnalysis;
     }
-    const GrProcessorSet::FragmentProcessorAnalysis& fragmentProcessorAnalysis() const {
-        SkASSERT(fAnalysis.isInitializedWithProcessorSet());
+    const GrProcessorSet::Analysis& processorAnalysis() const {
+        SkASSERT(fAnalysis.isInitialized());
         return fAnalysis;
     }
 
@@ -56,10 +58,12 @@
     void onPrepare(GrOpFlushState*) final {}
 
     SkMatrix fViewMatrix;
-    GrProcessorSet fProcessorSet;
-    GrProcessorSet::FragmentProcessorAnalysis fAnalysis;
+    GrColor fInputColor;
+    GrProcessorSet::Analysis fAnalysis;
     GrPathRendering::FillType fFillType;
-    GrAA fAA;
+    GrAAType fAAType;
+    uint32_t fPipelineSRGBFlags;
+    GrProcessorSet fProcessorSet;
 
     typedef GrDrawOp INHERITED;
 };
@@ -68,9 +72,10 @@
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, GrPaint&& paint, GrAA aa,
-                                          GrPath* path) {
-        return std::unique_ptr<GrDrawOp>(new GrDrawPathOp(viewMatrix, std::move(paint), aa, path));
+    static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, GrPaint&& paint,
+                                          GrAAType aaType, GrPath* path) {
+        return std::unique_ptr<GrDrawOp>(
+                new GrDrawPathOp(viewMatrix, std::move(paint), aaType, path));
     }
 
     const char* name() const override { return "DrawPath"; }
@@ -78,8 +83,8 @@
     SkString dumpInfo() const override;
 
 private:
-    GrDrawPathOp(const SkMatrix& viewMatrix, GrPaint&& paint, GrAA aa, const GrPath* path)
-            : GrDrawPathOpBase(ClassID(), viewMatrix, std::move(paint), path->getFillType(), aa)
+    GrDrawPathOp(const SkMatrix& viewMatrix, GrPaint&& paint, GrAAType aaType, const GrPath* path)
+            : GrDrawPathOpBase(ClassID(), viewMatrix, std::move(paint), path->getFillType(), aaType)
             , fPath(path) {
         this->setTransformedBounds(path->getBounds(), viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
     }
@@ -161,11 +166,12 @@
 
     static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
                                           SkScalar y, GrPaint&& paint,
-                                          GrPathRendering::FillType fill, GrAA aa,
+                                          GrPathRendering::FillType fill, GrAAType aaType,
                                           GrPathRange* range, const InstanceData* instanceData,
                                           const SkRect& bounds) {
-        return std::unique_ptr<GrDrawOp>(new GrDrawPathRangeOp(
-                viewMatrix, scale, x, y, std::move(paint), fill, aa, range, instanceData, bounds));
+        return std::unique_ptr<GrDrawOp>(new GrDrawPathRangeOp(viewMatrix, scale, x, y,
+                                                               std::move(paint), fill, aaType,
+                                                               range, instanceData, bounds));
     }
 
     const char* name() const override { return "DrawPathRange"; }
@@ -174,8 +180,8 @@
 
 private:
     GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, SkScalar y,
-                      GrPaint&& paint, GrPathRendering::FillType fill, GrAA aa, GrPathRange* range,
-                      const InstanceData* instanceData, const SkRect& bounds);
+                      GrPaint&& paint, GrPathRendering::FillType fill, GrAAType aaType,
+                      GrPathRange* range, const InstanceData* instanceData, const SkRect& bounds);
 
     TransformType transformType() const { return fDraws.head()->fInstanceData->transformType(); }
 
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index d8f91fe..a83b3e0 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -10,52 +10,44 @@
 #include "GrOpFlushState.h"
 #include "SkGr.h"
 
-std::unique_ptr<GrMeshDrawOp> GrDrawVerticesOp::Make(
-        GrColor color, GrPrimitiveType primitiveType, const SkMatrix& viewMatrix,
-        const SkPoint* positions, int vertexCount, const uint16_t* indices, int indexCount,
-        const uint32_t* colors, const SkPoint* localCoords, const SkRect& bounds,
-        GrRenderTargetContext::ColorArrayType colorArrayType) {
-    static constexpr SkCanvas::VertexMode kIgnoredMode = SkCanvas::kTriangles_VertexMode;
-    SkASSERT(positions);
-    if (!colors) {
-        // When we tessellate we will fill a color array with the GrColor value passed above as
-        // 'color'.
-        colorArrayType = GrRenderTargetContext::ColorArrayType::kPremulGrColor;
-    }
-    sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions,
-                                                      localCoords, colors, indexCount, indices);
-    if (!vertices) {
-        return nullptr;
-    }
-    return std::unique_ptr<GrMeshDrawOp>(new GrDrawVerticesOp(std::move(vertices), primitiveType,
-                                                              color, colorArrayType, viewMatrix));
-}
-
-std::unique_ptr<GrMeshDrawOp> GrDrawVerticesOp::Make(GrColor color, sk_sp<SkVertices> vertices,
-                                                     const SkMatrix& viewMatrix) {
+std::unique_ptr<GrLegacyMeshDrawOp> GrDrawVerticesOp::Make(GrColor color,
+                                                           sk_sp<SkVertices> vertices,
+                                                           const SkMatrix& viewMatrix,
+                                                           bool gammaCorrect,
+                                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                                           GrPrimitiveType* overridePrimType) {
     SkASSERT(vertices);
-    GrPrimitiveType primType = SkVertexModeToGrPrimitiveType(vertices->mode());
-    return std::unique_ptr<GrMeshDrawOp>(new GrDrawVerticesOp(
-            std::move(vertices), primType, color, GrRenderTargetContext::ColorArrayType::kSkColor,
-            viewMatrix));
+    GrPrimitiveType primType = overridePrimType ? *overridePrimType
+                                                : SkVertexModeToGrPrimitiveType(vertices->mode());
+    return std::unique_ptr<GrLegacyMeshDrawOp>(new GrDrawVerticesOp(std::move(vertices), primType,
+                                                                    color, gammaCorrect,
+                                                                    std::move(colorSpaceXform),
+                                                                    viewMatrix));
 }
 
 GrDrawVerticesOp::GrDrawVerticesOp(sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType,
-                                   GrColor color,
-                                   GrRenderTargetContext::ColorArrayType colorArrayType,
-                                   const SkMatrix& viewMatrix, uint32_t flags)
-        : INHERITED(ClassID()), fColorArrayType(colorArrayType) {
+                                   GrColor color, bool gammaCorrect,
+                                   sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                   const SkMatrix& viewMatrix)
+        : INHERITED(ClassID())
+        , fPrimitiveType(primitiveType)
+        , fColorSpaceXform(std::move(colorSpaceXform)) {
     SkASSERT(vertices);
 
     fVertexCount = vertices->vertexCount();
     fIndexCount = vertices->indexCount();
-    fPrimitiveType = primitiveType;
+    fColorArrayType = vertices->hasColors() ? ColorArrayType::kSkColor
+                                            : ColorArrayType::kPremulGrColor;
+    // GrColor is linearized (and gamut converted) during paint conversion, but SkColors need to be
+    // handled in the shader
+    fLinearizeColors = gammaCorrect && vertices->hasColors();
 
     Mesh& mesh = fMeshes.push_back();
     mesh.fColor = color;
     mesh.fViewMatrix = viewMatrix;
     mesh.fVertices = std::move(vertices);
-    mesh.fFlags = flags;
+    mesh.fIgnoreTexCoords = false;
+    mesh.fIgnoreColors = false;
 
     fFlags = 0;
     if (mesh.hasPerVertexColors()) {
@@ -74,29 +66,30 @@
     this->setTransformedBounds(mesh.fVertices->bounds(), viewMatrix, HasAABloat::kNo, zeroArea);
 }
 
-void GrDrawVerticesOp::getFragmentProcessorAnalysisInputs(
-        GrPipelineAnalysisColor* color, GrPipelineAnalysisCoverage* coverage) const {
+void GrDrawVerticesOp::getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                                  GrProcessorAnalysisCoverage* coverage) const {
     if (this->requiresPerVertexColors()) {
         color->setToUnknown();
     } else {
         color->setToConstant(fMeshes[0].fColor);
     }
-    *coverage = GrPipelineAnalysisCoverage::kNone;
+    *coverage = GrProcessorAnalysisCoverage::kNone;
 }
 
-void GrDrawVerticesOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
+void GrDrawVerticesOp::applyPipelineOptimizations(const PipelineOptimizations& optimizations) {
     SkASSERT(fMeshes.count() == 1);
     GrColor overrideColor;
     if (optimizations.getOverrideColorIfSet(&overrideColor)) {
         fMeshes[0].fColor = overrideColor;
-        fMeshes[0].fFlags |= kIgnoreColors_VerticesFlag;
+        fMeshes[0].fIgnoreColors = true;
         fFlags &= ~kRequiresPerVertexColors_Flag;
-        fColorArrayType = GrRenderTargetContext::ColorArrayType::kPremulGrColor;
+        fColorArrayType = ColorArrayType::kPremulGrColor;
+        fLinearizeColors = false;
     }
     if (optimizations.readsLocalCoords()) {
         fFlags |= kPipelineRequiresLocalCoords_Flag;
     } else {
-        fFlags |= kIgnoreTexCoords_VerticesFlag;
+        fMeshes[0].fIgnoreTexCoords = true;
         fFlags &= ~kAnyMeshHasExplicitLocalCoords;
     }
 }
@@ -122,9 +115,11 @@
 
     Color color(fMeshes[0].fColor);
     if (this->requiresPerVertexColors()) {
-        color.fType = (fColorArrayType == GrRenderTargetContext::ColorArrayType::kPremulGrColor)
+        color.fType = (fColorArrayType == ColorArrayType::kPremulGrColor)
                               ? Color::kPremulGrColorAttribute_Type
                               : Color::kUnpremulSkColorAttribute_Type;
+        color.fLinearize = fLinearizeColors;
+        color.fColorSpaceXform = fColorSpaceXform;
         *hasColorAttribute = true;
     } else {
         *hasColorAttribute = false;
@@ -234,15 +229,14 @@
         vertexOffset += vertexCount;
     }
 
-    GrMesh mesh;
-    if (indices) {
-        mesh.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex, firstIndex,
-                         fVertexCount, fIndexCount);
-
+    GrMesh mesh(this->primitiveType());
+    if (!indices) {
+        mesh.setNonIndexedNonInstanced(fVertexCount);
     } else {
-        mesh.init(this->primitiveType(), vertexBuffer, firstVertex, fVertexCount);
+        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertexCount - 1);
     }
-    target->draw(gp.get(), mesh);
+    mesh.setVertexData(vertexBuffer, firstVertex);
+    target->draw(gp.get(), this->pipeline(), mesh);
 }
 
 bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
@@ -265,10 +259,18 @@
         return false;
     }
 
+    if (fLinearizeColors != that->fLinearizeColors) {
+        return false;
+    }
+
     if (fVertexCount + that->fVertexCount > SK_MaxU16) {
         return false;
     }
 
+    // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination
+    // gamut is determined by the render target context. A mis-match should be impossible.
+    SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
+
     // If either op required explicit local coords or per-vertex colors the combined mesh does. Same
     // with multiple view matrices.
     fFlags |= that->fFlags;
@@ -355,7 +357,7 @@
     }
 }
 
-DRAW_OP_TEST_DEFINE(VerticesOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(VerticesOp) {
     GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1));
     uint32_t primitiveCount = random->nextRangeU(1, 100);
 
@@ -368,6 +370,7 @@
     bool hasTexCoords = random->nextBool();
     bool hasIndices = random->nextBool();
     bool hasColors = random->nextBool();
+    bool linearizeColors = random->nextBool();
 
     uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
 
@@ -383,18 +386,19 @@
                          hasIndices);
     }
 
-    GrRenderTargetContext::ColorArrayType colorArrayType =
-            random->nextBool() ? GrRenderTargetContext::ColorArrayType::kPremulGrColor
-                               : GrRenderTargetContext::ColorArrayType::kSkColor;
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
-    SkRect bounds;
-    SkDEBUGCODE(bool result =) bounds.setBoundsCheck(positions.begin(), vertexCount);
-    SkASSERT(result);
 
     GrColor color = GrRandomColor(random);
-    return GrDrawVerticesOp::Make(color, type, viewMatrix, positions.begin(), vertexCount,
-                                  indices.begin(), hasIndices ? indices.count() : 0, colors.begin(),
-                                  texCoords.begin(), bounds, colorArrayType);
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random);
+
+    static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
+    sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(),
+                                                      texCoords.begin(), colors.begin(),
+                                                      hasIndices ? indices.count() : 0,
+                                                      indices.begin());
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
+            new GrDrawVerticesOp(std::move(vertices), type, color, linearizeColors,
+                                 std::move(colorSpaceXform), viewMatrix));
 }
 
 #endif
diff --git a/src/gpu/ops/GrDrawVerticesOp.h b/src/gpu/ops/GrDrawVerticesOp.h
index 176b552..1d788ab 100644
--- a/src/gpu/ops/GrDrawVerticesOp.h
+++ b/src/gpu/ops/GrDrawVerticesOp.h
@@ -17,42 +17,29 @@
 #include "SkTDArray.h"
 #include "SkVertices.h"
 
+#if GR_TEST_UTILS
+#include "GrDrawOpTest.h"
+#endif
+
 class GrOpFlushState;
 class SkVertices;
 struct GrInitInvariantOutput;
 
-class GrDrawVerticesOp final : public GrMeshDrawOp {
+class GrDrawVerticesOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    enum {
-        kIgnoreTexCoords_VerticesFlag   = 1 << 0,
-        kIgnoreColors_VerticesFlag      = 1 << 1,
-    };
-
     /**
-     * The 'color' param is used if the 'colors' array is null. 'bounds' is the bounds of the
-     * 'positions' array (in local space prior to application of 'viewMatrix'). If 'indices' is null
-     * then 'indexCnt' must be zero and vice versa. In this case the vertices are indexed as 0, 1,
-     * ..., 'vertexCount' - 1. 'localCoords' are optional and if null the vertex positions are used
-     * as local coords. 'colorArrayType' specifies whether the colors are premul GrColors or
-     * unpremul SkColors.
+     * Draw a SkVertices. The GrColor param is used if the vertices lack per-vertex color. If the
+     * vertices lack local coords then the vertex positions are used as local coords. The primitive
+     * type drawn is derived from the SkVertices object, unless overridePrimType is specified.
+     * If gammaCorrect is true, the vertex colors will be linearized in the shader to get correct
+     * rendering.
      */
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, GrPrimitiveType primitiveType,
-                                              const SkMatrix& viewMatrix, const SkPoint* positions,
-                                              int vertexCount, const uint16_t* indices,
-                                              int indexCount, const uint32_t* colors,
-                                              const SkPoint* localCoords, const SkRect& bounds,
-                                              GrRenderTargetContext::ColorArrayType colorArrayType);
-
-    /**
-     * Draw a SkVertices. The GrColor param is used if the vertices lack per-vertex color or 'flags'
-     * indicates that the per-vertex color should be ignored.  The 'flags' options are those
-     * specified by SkCanvas::VerticesFlags. If the vertices lack local coords or 'flags' indicates
-     * that they should be ignored then the vertex positions are used as local coords.
-     */
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, sk_sp<SkVertices>,
-                                              const SkMatrix& viewMatrix);
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, sk_sp<SkVertices>,
+                                                    const SkMatrix& viewMatrix, bool gammaCorrect,
+                                                    sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                                    GrPrimitiveType* overridePrimType = nullptr);
 
     const char* name() const override { return "DrawVerticesOp"; }
 
@@ -66,13 +53,17 @@
     }
 
 private:
-    GrDrawVerticesOp(sk_sp<SkVertices>, GrPrimitiveType, GrColor,
-                     GrRenderTargetContext::ColorArrayType, const SkMatrix& viewMatrix,
-                     uint32_t flags = 0);
+    enum class ColorArrayType {
+        kPremulGrColor,
+        kSkColor,
+    };
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override;
-    void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
+    GrDrawVerticesOp(sk_sp<SkVertices>, GrPrimitiveType, GrColor, bool gammaCorrect,
+                     sk_sp<GrColorSpaceXform>, const SkMatrix& viewMatrix);
+
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override;
+    void applyPipelineOptimizations(const PipelineOptimizations&) override;
     void onPrepareDraws(Target*) const override;
 
     sk_sp<GrGeometryProcessor> makeGP(bool* hasColorAttribute, bool* hasLocalCoordAttribute) const;
@@ -90,14 +81,15 @@
         GrColor fColor;  // Used if this->hasPerVertexColors() is false.
         sk_sp<SkVertices> fVertices;
         SkMatrix fViewMatrix;
-        uint32_t fFlags;
+        bool fIgnoreTexCoords;
+        bool fIgnoreColors;
 
         bool hasExplicitLocalCoords() const {
-            return fVertices->hasTexCoords() && !(kIgnoreTexCoords_VerticesFlag & fFlags);
+            return fVertices->hasTexCoords() && !fIgnoreTexCoords;
         }
 
         bool hasPerVertexColors() const {
-            return fVertices->hasColors() && !(kIgnoreColors_VerticesFlag & fFlags);
+            return fVertices->hasColors() && !fIgnoreColors;
         }
     };
 
@@ -136,10 +128,16 @@
     uint32_t fFlags;
     int fVertexCount;
     int fIndexCount;
-    GrRenderTargetContext::ColorArrayType fColorArrayType;
+    ColorArrayType fColorArrayType;
+    bool fLinearizeColors;
+    sk_sp<GrColorSpaceXform> fColorSpaceXform;
     SkSTArray<1, Mesh, true> fMeshes;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
+
+#if GR_TEST_UTILS
+    GR_LEGACY_MESH_DRAW_OP_TEST_FRIEND(VerticesOp);
+#endif
 };
 
 #endif
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 598a839..effab8c 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -21,7 +21,7 @@
                                          LocalCoords::kHasExplicit_Type, SkMatrix::I());
 }
 
-class NonAALatticeOp final : public GrMeshDrawOp {
+class NonAALatticeOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -61,13 +61,13 @@
     }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToUnknown();
-        *coverage = GrPipelineAnalysisCoverage::kNone;
+        *coverage = GrProcessorAnalysisCoverage::kNone;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& analysioptimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& analysioptimizations) override {
         analysioptimizations.getOverrideColorIfSet(&fPatches[0].fColor);
     }
 
@@ -86,9 +86,9 @@
         }
 
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
-        void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
-                                     indexBuffer.get(), kVertsPerRect, kIndicesPerRect, numRects);
+        PatternHelper helper(kTriangles_GrPrimitiveType);
+        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
+                                     kIndicesPerRect, numRects);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -133,7 +133,7 @@
                         positions, vertexStride, kVertsPerRect * patch.fIter->numRectsToDraw());
             }
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -162,14 +162,15 @@
     int fImageHeight;
     SkSTArray<1, Patch, true> fPatches;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 namespace GrLatticeOp {
-std::unique_ptr<GrMeshDrawOp> MakeNonAA(GrColor color, const SkMatrix& viewMatrix, int imageWidth,
-                                        int imageHeight, std::unique_ptr<SkLatticeIter> iter,
-                                        const SkRect& dst) {
-    return std::unique_ptr<GrMeshDrawOp>(
+std::unique_ptr<GrLegacyMeshDrawOp> MakeNonAA(GrColor color, const SkMatrix& viewMatrix,
+                                              int imageWidth, int imageHeight,
+                                              std::unique_ptr<SkLatticeIter> iter,
+                                              const SkRect& dst) {
+    return std::unique_ptr<GrLegacyMeshDrawOp>(
             new NonAALatticeOp(color, viewMatrix, imageWidth, imageHeight, std::move(iter), dst));
 }
 };
diff --git a/src/gpu/ops/GrLatticeOp.h b/src/gpu/ops/GrLatticeOp.h
index ef0f58a..65aa622 100644
--- a/src/gpu/ops/GrLatticeOp.h
+++ b/src/gpu/ops/GrLatticeOp.h
@@ -11,15 +11,16 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class SkLatticeIter;
 class SkMatrix;
 struct SkRect;
 
 namespace GrLatticeOp {
-std::unique_ptr<GrMeshDrawOp> MakeNonAA(GrColor color, const SkMatrix& viewMatrix, int imageWidth,
-                                        int imageHeight, std::unique_ptr<SkLatticeIter> iter,
-                                        const SkRect& dst);
+std::unique_ptr<GrLegacyMeshDrawOp> MakeNonAA(GrColor color, const SkMatrix& viewMatrix,
+                                              int imageWidth, int imageHeight,
+                                              std::unique_ptr<SkLatticeIter> iter,
+                                              const SkRect& dst);
 };
 
 #endif
diff --git a/src/gpu/ops/GrMSAAPathRenderer.cpp b/src/gpu/ops/GrMSAAPathRenderer.cpp
index 4e7f036..50a839d7b 100644
--- a/src/gpu/ops/GrMSAAPathRenderer.cpp
+++ b/src/gpu/ops/GrMSAAPathRenderer.cpp
@@ -26,7 +26,7 @@
 #include "glsl/GrGLSLUtil.h"
 #include "glsl/GrGLSLVertexShaderBuilder.h"
 #include "ops/GrMeshDrawOp.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 static const float kTolerance = 0.5f;
 
@@ -215,22 +215,24 @@
     typedef GrGeometryProcessor INHERITED;
 };
 
-class MSAAPathOp final : public GrMeshDrawOp {
+class MSAAPathOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkPath& path,
-                                              const SkMatrix& viewMatrix, const SkRect& devBounds) {
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkPath& path,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkRect& devBounds) {
         int contourCount;
         int maxLineVertices;
         int maxQuadVertices;
-        ComputeWorstCasePointCount(path, &contourCount, &maxLineVertices, &maxQuadVertices);
+        ComputeWorstCasePointCount(path, viewMatrix, &contourCount, &maxLineVertices,
+                                   &maxQuadVertices);
         bool isIndexed = contourCount > 1;
         if (isIndexed &&
             (maxLineVertices > kMaxIndexedVertexCnt || maxQuadVertices > kMaxIndexedVertexCnt)) {
             return nullptr;
         }
 
-        return std::unique_ptr<GrMeshDrawOp>(new MSAAPathOp(
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new MSAAPathOp(
                 color, path, viewMatrix, devBounds, maxLineVertices, maxQuadVertices, isIndexed));
     }
 
@@ -259,18 +261,19 @@
         this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fPaths[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kNone;
+        *coverage = GrProcessorAnalysisCoverage::kNone;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fPaths[0].fColor);
     }
 
-    static void ComputeWorstCasePointCount(const SkPath& path, int* subpaths,
+    static void ComputeWorstCasePointCount(const SkPath& path, const SkMatrix& m, int* subpaths,
                                            int* outLinePointCount, int* outQuadPointCount) {
+        SkScalar tolerance = GrPathUtils::scaleToleranceToSrc(kTolerance, m, path.getBounds());
         int linePointCount = 0;
         int quadPointCount = 0;
         *subpaths = 1;
@@ -289,7 +292,7 @@
                 case SkPath::kConic_Verb: {
                     SkScalar weight = iter.conicWeight();
                     SkAutoConicToQuads converter;
-                    converter.computeQuads(pts, weight, kTolerance);
+                    converter.computeQuads(pts, weight, tolerance);
                     int quadPts = converter.countQuads();
                     linePointCount += quadPts;
                     quadPointCount += 3 * quadPts;
@@ -300,7 +303,7 @@
                     break;
                 case SkPath::kCubic_Verb: {
                     SkSTArray<15, SkPoint, true> quadPts;
-                    GrPathUtils::convertCubicToQuads(pts, kTolerance, &quadPts);
+                    GrPathUtils::convertCubicToQuads(pts, tolerance, &quadPts);
                     int count = quadPts.count();
                     linePointCount += count / 3;
                     quadPointCount += count;
@@ -334,7 +337,7 @@
         const GrBuffer* lineVertexBuffer;
         int firstLineVertex;
         MSAALineVertices lines;
-        size_t lineVertexStride = sizeof(MSAALineVertices::Vertex);
+        int lineVertexStride = sizeof(MSAALineVertices::Vertex);
         lines.vertices = (MSAALineVertices::Vertex*) target->makeVertexSpace(lineVertexStride,
                                                                              fMaxLineVertices,
                                                                              &lineVertexBuffer,
@@ -347,7 +350,7 @@
         SkDEBUGCODE(lines.verticesEnd = lines.vertices + fMaxLineVertices;)
 
         MSAAQuadVertices quads;
-        size_t quadVertexStride = sizeof(MSAAQuadVertices::Vertex);
+        int quadVertexStride = sizeof(MSAAQuadVertices::Vertex);
         SkAutoMalloc quadVertexPtr(fMaxQuadVertices * quadVertexStride);
         quads.vertices = (MSAAQuadVertices::Vertex*) quadVertexPtr.get();
         quads.nextVertex = quads.vertices;
@@ -377,11 +380,9 @@
             quads.indices = nullptr;
             quads.nextIndex = nullptr;
         }
-
         // fill buffers
         for (int i = 0; i < fPaths.count(); i++) {
             const PathInfo& pathInfo = fPaths[i];
-
             if (!this->createGeom(lines,
                                   quads,
                                   pathInfo.fPath,
@@ -409,16 +410,21 @@
             }
             SkASSERT(lineVertexStride == lineGP->getVertexStride());
 
-            GrMesh lineMeshes;
-            if (fIsIndexed) {
-                lineMeshes.initIndexed(primitiveType, lineVertexBuffer, lineIndexBuffer,
-                                         firstLineVertex, firstLineIndex, lineVertexOffset,
-                                         lineIndexOffset);
+            GrMesh lineMeshes(primitiveType);
+            if (!fIsIndexed) {
+                lineMeshes.setNonIndexedNonInstanced(lineVertexOffset);
             } else {
-                lineMeshes.init(primitiveType, lineVertexBuffer, firstLineVertex,
-                                  lineVertexOffset);
+                lineMeshes.setIndexed(lineIndexBuffer, lineIndexOffset, firstLineIndex,
+                                      0, lineVertexOffset - 1);
             }
-            target->draw(lineGP.get(), lineMeshes);
+            lineMeshes.setVertexData(lineVertexBuffer, firstLineVertex);
+
+            // We can get line vertices from path moveTos with no actual segments and thus no index
+            // count. We assert that indexed draws contain a positive index count, so bail here in
+            // that case.
+            if (!fIsIndexed || lineIndexOffset) {
+                target->draw(lineGP.get(), this->pipeline(), lineMeshes);
+            }
         }
 
         if (quadVertexOffset) {
@@ -431,22 +437,21 @@
                     target->makeVertexSpace(quadVertexStride, quadVertexOffset, &quadVertexBuffer,
                                             &firstQuadVertex);
             memcpy(quadVertices, quads.vertices, quadVertexStride * quadVertexOffset);
-            GrMesh quadMeshes;
-            if (fIsIndexed) {
+            GrMesh quadMeshes(kTriangles_GrPrimitiveType);
+            if (!fIsIndexed) {
+                quadMeshes.setNonIndexedNonInstanced(quadVertexOffset);
+            } else {
                 const GrBuffer* quadIndexBuffer;
                 int firstQuadIndex;
                 uint16_t* quadIndices = (uint16_t*) target->makeIndexSpace(quadIndexOffset,
                                                                            &quadIndexBuffer,
                                                                            &firstQuadIndex);
                 memcpy(quadIndices, quads.indices, sizeof(uint16_t) * quadIndexOffset);
-                quadMeshes.initIndexed(kTriangles_GrPrimitiveType, quadVertexBuffer,
-                                       quadIndexBuffer, firstQuadVertex, firstQuadIndex,
-                                       quadVertexOffset, quadIndexOffset);
-            } else {
-                quadMeshes.init(kTriangles_GrPrimitiveType, quadVertexBuffer, firstQuadVertex,
-                                quadVertexOffset);
+                quadMeshes.setIndexed(quadIndexBuffer, quadIndexOffset, firstQuadIndex,
+                                      0, quadVertexOffset - 1);
             }
-            target->draw(quadGP.get(), quadMeshes);
+            quadMeshes.setVertexData(quadVertexBuffer, firstQuadVertex);
+            target->draw(quadGP.get(), this->pipeline(), quadMeshes);
         }
     }
 
@@ -486,6 +491,8 @@
                     SkColor color,
                     bool isIndexed) const {
         {
+            const SkScalar tolerance = GrPathUtils::scaleToleranceToSrc(kTolerance, m,
+                                                                        path.getBounds());
             uint16_t subpathIdxStart = (uint16_t) (lines.nextVertex - lines.vertices);
 
             SkPoint pts[4];
@@ -518,7 +525,7 @@
                     case SkPath::kConic_Verb: {
                         SkScalar weight = iter.conicWeight();
                         SkAutoConicToQuads converter;
-                        const SkPoint* quadPts = converter.computeQuads(pts, weight, kTolerance);
+                        const SkPoint* quadPts = converter.computeQuads(pts, weight, tolerance);
                         for (int i = 0; i < converter.countQuads(); ++i) {
                             add_quad(lines, quads, quadPts + i * 2, color, isIndexed,
                                      subpathIdxStart);
@@ -531,7 +538,7 @@
                     }
                     case SkPath::kCubic_Verb: {
                         SkSTArray<15, SkPoint, true> quadPts;
-                        GrPathUtils::convertCubicToQuads(pts, kTolerance, &quadPts);
+                        GrPathUtils::convertCubicToQuads(pts, tolerance, &quadPts);
                         int count = quadPts.count();
                         for (int i = 0; i < count; i += 3) {
                             add_quad(lines, quads, &quadPts[i], color, isIndexed, subpathIdxStart);
@@ -564,7 +571,7 @@
     int fMaxQuadVertices;
     bool fIsIndexed;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 bool GrMSAAPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
@@ -604,7 +611,7 @@
                 reverse = true;
                 // fallthrough
             case SkPath::kWinding_FillType:
-                passes[0] = &gWindStencilSeparateWithWrap;
+                passes[0] = &gWindStencilPass;
                 if (!stencilOnly) {
                     passes[1] = reverse ? &gInvWindColorPass : &gWindColorPass;
                 }
@@ -621,7 +628,7 @@
 
     SkASSERT(passes[0]);
     {  // First pass
-        std::unique_ptr<GrMeshDrawOp> op =
+        std::unique_ptr<GrLegacyMeshDrawOp> op =
                 MSAAPathOp::Make(paint.getColor(), path, viewMatrix, devBounds);
         if (!op) {
             return false;
@@ -635,7 +642,7 @@
         }
         GrPipelineBuilder pipelineBuilder(std::move(firstPassPaint), aaType);
         pipelineBuilder.setUserStencil(passes[0]);
-        renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+        renderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
     }
 
     if (passes[1]) {
@@ -658,18 +665,19 @@
         }
         const SkMatrix& viewM =
                 (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix;
-        std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
-                paint.getColor(), viewM, bounds, nullptr, &localMatrix));
-
-        GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-        pipelineBuilder.setUserStencil(passes[1]);
-
-        renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+        renderTargetContext->addDrawOp(
+                clip,
+                GrNonAAFillRectOp::Make(std::move(paint), viewM, bounds, nullptr, &localMatrix,
+                                        aaType, passes[1]));
     }
     return true;
 }
 
 bool GrMSAAPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
+    // If we aren't a single_pass_shape, we require stencil buffers.
+    if (!single_pass_shape(*args.fShape) && args.fCaps->avoidStencilBuffers()) {
+        return false;
+    }
     // This path renderer only fills and relies on MSAA for antialiasing. Stroked shapes are
     // handled by passing on the original shape and letting the caller compute the stroked shape
     // which will have a fill style.
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index c3a191d..d6c2ced 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -17,17 +17,16 @@
     this->onPrepareDraws(&target);
 }
 
-void* GrMeshDrawOp::InstancedHelper::init(Target* target, GrPrimitiveType primType,
-                                          size_t vertexStride, const GrBuffer* indexBuffer,
-                                          int verticesPerInstance, int indicesPerInstance,
-                                          int instancesToDraw) {
+void* GrMeshDrawOp::PatternHelper::init(Target* target, size_t vertexStride,
+                                        const GrBuffer* indexBuffer, int verticesPerRepetition,
+                                        int indicesPerRepetition, int repeatCount) {
     SkASSERT(target);
     if (!indexBuffer) {
         return nullptr;
     }
     const GrBuffer* vertexBuffer;
     int firstVertex;
-    int vertexCount = verticesPerInstance * instancesToDraw;
+    int vertexCount = verticesPerRepetition * repeatCount;
     void* vertices =
             target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
     if (!vertices) {
@@ -36,16 +35,17 @@
     }
     SkASSERT(vertexBuffer);
     size_t ibSize = indexBuffer->gpuMemorySize();
-    int maxInstancesPerDraw = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerInstance));
+    int maxRepetitions = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerRepetition));
 
-    fMesh.initInstanced(primType, vertexBuffer, indexBuffer, firstVertex, verticesPerInstance,
-                        indicesPerInstance, instancesToDraw, maxInstancesPerDraw);
+    fMesh.setIndexedPatterned(indexBuffer, indicesPerRepetition, verticesPerRepetition,
+                              repeatCount, maxRepetitions);
+    fMesh.setVertexData(vertexBuffer, firstVertex);
     return vertices;
 }
 
-void GrMeshDrawOp::InstancedHelper::recordDraw(Target* target, const GrGeometryProcessor* gp) {
-    SkASSERT(fMesh.instanceCount());
-    target->draw(gp, fMesh);
+void GrMeshDrawOp::PatternHelper::recordDraw(Target* target, const GrGeometryProcessor* gp,
+                                             const GrPipeline* pipeline) {
+    target->draw(gp, pipeline, fMesh);
 }
 
 void* GrMeshDrawOp::QuadHelper::init(Target* target, size_t vertexStride, int quadsToDraw) {
@@ -54,15 +54,11 @@
         SkDebugf("Could not get quad index buffer.");
         return nullptr;
     }
-    return this->INHERITED::init(target, kTriangles_GrPrimitiveType, vertexStride,
-                                 quadIndexBuffer.get(), kVerticesPerQuad, kIndicesPerQuad,
-                                 quadsToDraw);
+    return this->INHERITED::init(target, vertexStride, quadIndexBuffer.get(), kVerticesPerQuad,
+                                 kIndicesPerQuad, quadsToDraw);
 }
 
 void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
-    SkASSERT(!state->drawOpArgs().fAppliedClip);
-    SkASSERT(!state->drawOpArgs().fDstTexture.texture());
-    SkASSERT(state->drawOpArgs().fRenderTarget == this->pipeline()->getRenderTarget());
     int currUploadIdx = 0;
     int currMeshIdx = 0;
 
@@ -73,10 +69,11 @@
         while (currUploadIdx < fInlineUploads.count() &&
                fInlineUploads[currUploadIdx].fUploadBeforeToken == drawToken) {
             state->commandBuffer()->inlineUpload(state, fInlineUploads[currUploadIdx++].fUpload,
-                                                 this->pipeline()->getRenderTarget());
+                                                 state->drawOpArgs().fRenderTarget);
         }
         const QueuedDraw& draw = fQueuedDraws[currDrawIdx];
-        state->commandBuffer()->draw(*this->pipeline(), *draw.fGeometryProcessor.get(),
+        SkASSERT(draw.fPipeline->getRenderTarget() == state->drawOpArgs().fRenderTarget);
+        state->commandBuffer()->draw(*draw.fPipeline, *draw.fGeometryProcessor.get(),
                                      fMeshes.begin() + currMeshIdx, draw.fMeshCnt, this->bounds());
         currMeshIdx += draw.fMeshCnt;
         state->flushToken();
@@ -89,23 +86,25 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-void GrMeshDrawOp::Target::draw(const GrGeometryProcessor* gp, const GrMesh& mesh) {
+void GrMeshDrawOp::Target::draw(const GrGeometryProcessor* gp, const GrPipeline* pipeline,
+                                const GrMesh& mesh) {
     GrMeshDrawOp* op = this->meshDrawOp();
     op->fMeshes.push_back(mesh);
     if (!op->fQueuedDraws.empty()) {
-        // If the last draw shares a geometry processor and there are no intervening uploads,
-        // add this mesh to it.
-        GrMeshDrawOp::QueuedDraw& lastDraw = op->fQueuedDraws.back();
-        if (lastDraw.fGeometryProcessor == gp &&
+        // If the last draw shares a geometry processor and pipeline and there are no intervening
+        // uploads, add this mesh to it.
+        GrLegacyMeshDrawOp::QueuedDraw& lastDraw = op->fQueuedDraws.back();
+        if (lastDraw.fGeometryProcessor == gp && lastDraw.fPipeline == pipeline &&
             (op->fInlineUploads.empty() ||
              op->fInlineUploads.back().fUploadBeforeToken != this->nextDrawToken())) {
             ++lastDraw.fMeshCnt;
             return;
         }
     }
-    GrMeshDrawOp::QueuedDraw& draw = op->fQueuedDraws.push_back();
+    GrLegacyMeshDrawOp::QueuedDraw& draw = op->fQueuedDraws.push_back();
     GrDrawOpUploadToken token = this->state()->issueDrawToken();
     draw.fGeometryProcessor.reset(gp);
+    draw.fPipeline = pipeline;
     draw.fMeshCnt = 1;
     if (op->fQueuedDraws.count() == 1) {
         op->fBaseDrawToken = token;
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index cbf1f62..178ca29 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -12,6 +12,7 @@
 #include "GrGeometryProcessor.h"
 #include "GrMesh.h"
 #include "GrPendingProgramElement.h"
+#include "GrPipelineBuilder.h"
 
 #include "SkTLList.h"
 
@@ -25,52 +26,21 @@
 public:
     class Target;
 
-    /**
-     * Performs analysis of the fragment processors in GrProcessorSet and GrAppliedClip using the
-     * initial color and coverage from this op's geometry processor.
-     */
-    void analyzeProcessors(GrProcessorSet::FragmentProcessorAnalysis* analysis,
-                           const GrProcessorSet& processors,
-                           const GrAppliedClip* appliedClip,
-                           const GrCaps& caps) const {
-        GrPipelineAnalysisColor inputColor;
-        GrPipelineAnalysisCoverage inputCoverage;
-        this->getFragmentProcessorAnalysisInputs(&inputColor, &inputCoverage);
-        analysis->init(inputColor, inputCoverage, processors, appliedClip, caps);
-    }
-
-    void initPipeline(const GrPipeline::InitArgs& args) {
-        this->applyPipelineOptimizations(fPipeline.init(args));
-    }
-
-    /**
-     * Mesh draw ops use a legacy system in GrRenderTargetContext where the pipeline is created when
-     * the op is recorded. These methods are unnecessary as this information is in the pipeline.
-     */
-    FixedFunctionFlags fixedFunctionFlags() const override {
-        SkFAIL("This should never be called for mesh draw ops.");
-        return FixedFunctionFlags::kNone;
-    }
-    bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override {
-        SkFAIL("Should never be called for mesh draw ops.");
-        return false;
-    }
-
 protected:
     GrMeshDrawOp(uint32_t classID);
 
-    /** Helper for rendering instances using an instanced index index buffer. This class creates the
+    /** Helper for rendering repeating meshes using a patterned index buffer. This class creates the
         space for the vertices and flushes the draws to the GrMeshDrawOp::Target. */
-    class InstancedHelper {
+    class PatternHelper {
     public:
-        InstancedHelper() {}
+        PatternHelper(GrPrimitiveType primitiveType) : fMesh(primitiveType) {}
         /** Returns the allocated storage for the vertices. The caller should populate the vertices
             before calling recordDraws(). */
-        void* init(Target*, GrPrimitiveType, size_t vertexStride, const GrBuffer*,
-                   int verticesPerInstance, int indicesPerInstance, int instancesToDraw);
+        void* init(Target*, size_t vertexStride, const GrBuffer*, int verticesPerRepetition,
+                   int indicesPerRepetition, int repeatCount);
 
         /** Call after init() to issue draws to the GrMeshDrawOp::Target.*/
-        void recordDraw(Target*, const GrGeometryProcessor*);
+        void recordDraw(Target*, const GrGeometryProcessor*, const GrPipeline*);
 
     private:
         GrMesh fMesh;
@@ -80,39 +50,21 @@
     static const int kIndicesPerQuad = 6;
 
     /** A specialization of InstanceHelper for quad rendering. */
-    class QuadHelper : private InstancedHelper {
+    class QuadHelper : private PatternHelper {
     public:
-        QuadHelper() : INHERITED() {}
+        QuadHelper() : INHERITED(kTriangles_GrPrimitiveType) {}
         /** Finds the cached quad index buffer and reserves vertex space. Returns nullptr on failure
             and on success a pointer to the vertex data that the caller should populate before
             calling recordDraws(). */
         void* init(Target*, size_t vertexStride, int quadsToDraw);
 
-        using InstancedHelper::recordDraw;
+        using PatternHelper::recordDraw;
 
     private:
-        typedef InstancedHelper INHERITED;
+        typedef PatternHelper INHERITED;
     };
 
-    const GrPipeline* pipeline() const {
-        SkASSERT(fPipeline.isInitialized());
-        return &fPipeline;
-    }
-
 private:
-    /**
-     * Provides information about the GrPrimitiveProccesor color and coverage outputs which become
-     * inputs to the first color and coverage fragment processors.
-     */
-    virtual void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor*,
-                                                    GrPipelineAnalysisCoverage*) const = 0;
-
-    /**
-     * After GrPipeline analysis is complete this is called so that the op can use the analysis
-     * results when constructing its GrPrimitiveProcessor.
-     */
-    virtual void applyPipelineOptimizations(const GrPipelineOptimizations&) = 0;
-
     void onPrepare(GrOpFlushState* state) final;
     void onExecute(GrOpFlushState* state) final;
 
@@ -126,17 +78,145 @@
     struct QueuedDraw {
         int fMeshCnt = 0;
         GrPendingProgramElement<const GrGeometryProcessor> fGeometryProcessor;
+        const GrPipeline* fPipeline;
     };
 
     // All draws in all the GrMeshDrawOps have implicit tokens based on the order they are enqueued
     // globally across all ops. This is the offset of the first entry in fQueuedDraws.
     // fQueuedDraws[i]'s token is fBaseDrawToken + i.
     GrDrawOpUploadToken fBaseDrawToken;
-    GrPipeline fPipeline;
     SkSTArray<4, GrMesh> fMeshes;
     SkSTArray<4, QueuedDraw, true> fQueuedDraws;
 
     typedef GrDrawOp INHERITED;
 };
 
+/**
+ * Many of our ops derive from this class which initializes a GrPipeline just before being recorded.
+ * We are migrating away from use of this class.
+ */
+class GrLegacyMeshDrawOp : public GrMeshDrawOp {
+public:
+    /**
+     * Performs analysis of the fragment processors in GrProcessorSet and GrAppliedClip using the
+     * initial color and coverage from this op's geometry processor.
+     */
+    GrProcessorSet::Analysis analyzeUpdateAndRecordProcessors(GrPipelineBuilder* pipelineBuilder,
+                                                              const GrAppliedClip* appliedClip,
+                                                              bool isMixedSamples,
+                                                              const GrCaps& caps,
+                                                              GrColor* overrideColor) const {
+        GrProcessorAnalysisColor inputColor;
+        GrProcessorAnalysisCoverage inputCoverage;
+        this->getProcessorAnalysisInputs(&inputColor, &inputCoverage);
+        return pipelineBuilder->finalizeProcessors(inputColor, inputCoverage, appliedClip,
+                                                   isMixedSamples, caps, overrideColor);
+    }
+
+    void initPipeline(const GrPipeline::InitArgs& args, const GrProcessorSet::Analysis& analysis,
+                      GrColor overrideColor) {
+        fPipeline.init(args);
+        this->applyPipelineOptimizations(PipelineOptimizations(analysis, overrideColor));
+    }
+
+    void addDependenciesTo(GrRenderTargetProxy* rtp) {
+        fPipeline.addDependenciesTo(rtp);
+    }
+
+    /**
+     * Mesh draw ops use a legacy system in GrRenderTargetContext where the pipeline is created when
+     * the op is recorded. These methods are unnecessary as this information is in the pipeline.
+     */
+    FixedFunctionFlags fixedFunctionFlags() const override {
+        SkFAIL("This should never be called for legacy mesh draw ops.");
+        return FixedFunctionFlags::kNone;
+    }
+    bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override {
+        SkFAIL("Should never be called for legacy mesh draw ops.");
+        return false;
+    }
+
+protected:
+    GrLegacyMeshDrawOp(uint32_t classID) : INHERITED(classID) {}
+    /**
+     * This is a legacy class only used by GrLegacyMeshDrawOp and will be removed. It presents some
+     * aspects of GrProcessorSet::Analysis to GrLegacyMeshDrawOp subclasses.
+     */
+    class PipelineOptimizations {
+    public:
+        PipelineOptimizations(const GrProcessorSet::Analysis& analysis, GrColor overrideColor) {
+            fFlags = 0;
+            if (analysis.inputColorIsOverridden()) {
+                fFlags |= kUseOverrideColor_Flag;
+                fOverrideColor = overrideColor;
+            }
+            if (analysis.usesLocalCoords()) {
+                fFlags |= kReadsLocalCoords_Flag;
+            }
+            if (analysis.isCompatibleWithCoverageAsAlpha()) {
+                fFlags |= kCanTweakAlphaForCoverage_Flag;
+            }
+        }
+
+        /** Does the pipeline require access to (implicit or explicit) local coordinates? */
+        bool readsLocalCoords() const { return SkToBool(kReadsLocalCoords_Flag & fFlags); }
+
+        /** Does the pipeline allow the GrPrimitiveProcessor to combine color and coverage into one
+            color output ? */
+        bool canTweakAlphaForCoverage() const {
+            return SkToBool(kCanTweakAlphaForCoverage_Flag & fFlags);
+        }
+
+        /** Does the pipeline require the GrPrimitiveProcessor to specify a specific color (and if
+            so get the color)? */
+        bool getOverrideColorIfSet(GrColor* overrideColor) const {
+            if (SkToBool(kUseOverrideColor_Flag & fFlags)) {
+                if (overrideColor) {
+                    *overrideColor = fOverrideColor;
+                }
+                return true;
+            }
+            return false;
+        }
+
+    private:
+        enum {
+            // If this is not set the primitive processor need not produce local coordinates
+            kReadsLocalCoords_Flag = 0x1,
+            // If this flag is set then the primitive processor may produce color*coverage as
+            // its color output (and not output a separate coverage).
+            kCanTweakAlphaForCoverage_Flag = 0x2,
+            // If this flag is set the GrPrimitiveProcessor must produce fOverrideColor as its
+            // output color. If not set fOverrideColor is to be ignored.
+            kUseOverrideColor_Flag = 0x4,
+        };
+
+        uint32_t fFlags;
+        GrColor fOverrideColor;
+    };
+
+    const GrPipeline* pipeline() const {
+        SkASSERT(fPipeline.isInitialized());
+        return &fPipeline;
+    }
+
+private:
+    /**
+     * Provides information about the GrPrimitiveProccesor color and coverage outputs which become
+     * inputs to the first color and coverage fragment processors.
+     */
+    virtual void getProcessorAnalysisInputs(GrProcessorAnalysisColor*,
+                                            GrProcessorAnalysisCoverage*) const = 0;
+
+    /**
+     * After processor analysis is complete this is called so that the op can use the analysis
+     * results when constructing its GrPrimitiveProcessor.
+     */
+    virtual void applyPipelineOptimizations(const PipelineOptimizations&) = 0;
+
+    GrPipeline fPipeline;
+
+    typedef GrMeshDrawOp INHERITED;
+};
+
 #endif
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index ce4b21f..32e77da 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -1,24 +1,25 @@
 /*
- * Copyright 2015 Google Inc.
+ * 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 "GrNonAAFillRectOp.h"
-
+#include "GrAppliedClip.h"
 #include "GrColor.h"
 #include "GrDefaultGeoProcFactory.h"
+#include "GrDrawOpTest.h"
 #include "GrMeshDrawOp.h"
 #include "GrOpFlushState.h"
 #include "GrPrimitiveProcessor.h"
 #include "GrQuad.h"
 #include "GrResourceProvider.h"
-
+#include "GrSimpleMeshDrawOpHelper.h"
 #include "SkMatrixPriv.h"
 
-static const int kVertsPerInstance = 4;
-static const int kIndicesPerInstance = 6;
+static const int kVertsPerRect = 4;
+static const int kIndicesPerRect = 6;
 
 /** We always use per-vertex colors so that rects can be combined across color changes. Sometimes
     we  have explicit local coords and sometimes not. We *could* always provide explicit local
@@ -34,6 +35,35 @@
                                          LocalCoords::kHasExplicit_Type, SkMatrix::I());
 }
 
+static sk_sp<GrGeometryProcessor> make_perspective_gp(const SkMatrix& viewMatrix,
+                                                      bool hasExplicitLocalCoords,
+                                                      const SkMatrix* localMatrix) {
+    SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
+
+    using namespace GrDefaultGeoProcFactory;
+
+    // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map
+    // the local rect on the cpu (in case the localMatrix also has perspective).
+    // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect
+    // to generate vertex local coords
+    if (viewMatrix.hasPerspective()) {
+        LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type
+                                                       : LocalCoords::kUsePosition_Type,
+                                localMatrix);
+        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+                                             Coverage::kSolid_Type, localCoords, viewMatrix);
+    } else if (hasExplicitLocalCoords) {
+        LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix);
+        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+                                             Coverage::kSolid_Type, localCoords, SkMatrix::I());
+    } else {
+        LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
+        return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type,
+                                                           Coverage::kSolid_Type, localCoords,
+                                                           viewMatrix);
+    }
+}
+
 static void tesselate(intptr_t vertices,
                       size_t vertexStride,
                       GrColor color,
@@ -45,14 +75,14 @@
     positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
 
     if (viewMatrix) {
-        SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerInstance);
+        SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerRect);
     }
 
     // Setup local coords
     // TODO we should only do this if local coords are being read
     if (localQuad) {
         static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
-        for (int i = 0; i < kVertsPerInstance; i++) {
+        for (int i = 0; i < kVertsPerRect; i++) {
             SkPoint* coords =
                     reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
             *coords = localQuad->point(i);
@@ -67,13 +97,29 @@
     }
 }
 
-class NonAAFillRectOp final : public GrMeshDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
+namespace {
 
-    NonAAFillRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
-                    const SkRect* localRect, const SkMatrix* localMatrix)
-            : INHERITED(ClassID()) {
+class NonAAFillRectOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
+
+public:
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& rect, const SkRect* localRect,
+                                          const SkMatrix* localMatrix, GrAAType aaType,
+                                          const GrUserStencilSettings* stencilSettings) {
+        SkASSERT(GrAAType::kCoverage != aaType);
+        return Helper::FactoryHelper<NonAAFillRectOp>(std::move(paint), viewMatrix, rect, localRect,
+                                                      localMatrix, aaType, stencilSettings);
+    }
+
+    NonAAFillRectOp() = delete;
+
+    NonAAFillRectOp(const Helper::MakeArgs& args, GrColor color, const SkMatrix& viewMatrix,
+                    const SkRect& rect, const SkRect* localRect, const SkMatrix* localMatrix,
+                    GrAAType aaType, const GrUserStencilSettings* stencilSettings)
+            : INHERITED(ClassID()), fHelper(args, aaType, stencilSettings) {
+
         SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective()));
         RectInfo& info = fRects.push_back();
         info.fColor = color;
@@ -95,6 +141,7 @@
 
     SkString dumpInfo() const override {
         SkString str;
+        str.append(GrMeshDrawOp::dumpInfo());
         str.appendf("# combined: %d\n", fRects.count());
         for (int i = 0; i < fRects.count(); ++i) {
             const RectInfo& info = fRects[i];
@@ -102,24 +149,19 @@
                         info.fColor, info.fRect.fLeft, info.fRect.fTop, info.fRect.fRight,
                         info.fRect.fBottom);
         }
-        str.append(DumpPipelineInfo(*this->pipeline()));
-        str.append(INHERITED::dumpInfo());
         return str;
     }
 
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fRects.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
+    DEFINE_OP_CLASS_ID
+
 private:
-    NonAAFillRectOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fRects[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kNone;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fRects[0].fColor);
-    }
-
     void onPrepareDraws(Target* target) const override {
         sk_sp<GrGeometryProcessor> gp = make_gp();
         if (!gp) {
@@ -130,34 +172,31 @@
                  sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
 
         size_t vertexStride = gp->getVertexStride();
-        int instanceCount = fRects.count();
+        int rectCount = fRects.count();
 
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
-        void* vertices =
-                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
-                            kVertsPerInstance, kIndicesPerInstance, instanceCount);
+        PatternHelper helper(kTriangles_GrPrimitiveType);
+        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
+                                     kIndicesPerRect, rectCount);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        for (int i = 0; i < instanceCount; i++) {
+        for (int i = 0; i < rectCount; i++) {
             intptr_t verts =
-                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride;
+                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
             tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
                       fRects[i].fRect, &fRects[i].fLocalQuad);
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         NonAAFillRectOp* that = t->cast<NonAAFillRectOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
-
         fRects.push_back_n(that->fRects.count(), that->fRects.begin());
         this->joinBounds(*that);
         return true;
@@ -170,43 +209,190 @@
         GrQuad fLocalQuad;
     };
 
+    Helper fHelper;
     SkSTArray<1, RectInfo, true> fRects;
+    typedef GrMeshDrawOp INHERITED;
+};
+
+// We handle perspective in the local matrix or viewmatrix with special ops.
+class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
+
+public:
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& rect, const SkRect* localRect,
+                                          const SkMatrix* localMatrix, GrAAType aaType,
+                                          const GrUserStencilSettings* stencilSettings) {
+        SkASSERT(GrAAType::kCoverage != aaType);
+        return Helper::FactoryHelper<NonAAFillRectPerspectiveOp>(std::move(paint), viewMatrix, rect,
+                                                                 localRect, localMatrix, aaType,
+                                                                 stencilSettings);
+    }
+
+    NonAAFillRectPerspectiveOp() = delete;
+
+    NonAAFillRectPerspectiveOp(const Helper::MakeArgs& args, GrColor color,
+                               const SkMatrix& viewMatrix, const SkRect& rect,
+                               const SkRect* localRect, const SkMatrix* localMatrix,
+                               GrAAType aaType, const GrUserStencilSettings* stencilSettings)
+            : INHERITED(ClassID())
+            , fHelper(args, aaType, stencilSettings)
+            , fViewMatrix(viewMatrix) {
+        SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
+        RectInfo& info = fRects.push_back();
+        info.fColor = color;
+        info.fRect = rect;
+        fHasLocalRect = SkToBool(localRect);
+        fHasLocalMatrix = SkToBool(localMatrix);
+        if (fHasLocalMatrix) {
+            fLocalMatrix = *localMatrix;
+        }
+        if (fHasLocalRect) {
+            info.fLocalRect = *localRect;
+        }
+        this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+    const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
+
+    SkString dumpInfo() const override {
+        SkString str;
+        str.appendf("# combined: %d\n", fRects.count());
+        for (int i = 0; i < fRects.count(); ++i) {
+            const RectInfo& geo = fRects[0];
+            str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
+                        geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight,
+                        geo.fRect.fBottom);
+        }
+        str.append(INHERITED::dumpInfo());
+        return str;
+    }
+
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fRects.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
+    DEFINE_OP_CLASS_ID
+
+private:
+    void onPrepareDraws(Target* target) const override {
+        sk_sp<GrGeometryProcessor> gp = make_perspective_gp(
+                fViewMatrix, fHasLocalRect, fHasLocalMatrix ? &fLocalMatrix : nullptr);
+        if (!gp) {
+            SkDebugf("Couldn't create GrGeometryProcessor\n");
+            return;
+        }
+        SkASSERT(fHasLocalRect
+                         ? gp->getVertexStride() ==
+                                   sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
+                         : gp->getVertexStride() ==
+                                   sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
+
+        size_t vertexStride = gp->getVertexStride();
+        int rectCount = fRects.count();
+
+        sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
+        PatternHelper helper(kTriangles_GrPrimitiveType);
+        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
+                                     kIndicesPerRect, rectCount);
+        if (!vertices || !indexBuffer) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        for (int i = 0; i < rectCount; i++) {
+            const RectInfo& info = fRects[i];
+            intptr_t verts =
+                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
+            if (fHasLocalRect) {
+                GrQuad quad(info.fLocalRect);
+                tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad);
+            } else {
+                tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
+            }
+        }
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+    }
+
+    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
+        NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>();
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
+            return false;
+        }
+
+        // We could combine across perspective vm changes if we really wanted to.
+        if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
+            return false;
+        }
+        if (fHasLocalRect != that->fHasLocalRect) {
+            return false;
+        }
+        if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) {
+            return false;
+        }
+
+        fRects.push_back_n(that->fRects.count(), that->fRects.begin());
+        this->joinBounds(*that);
+        return true;
+    }
+
+    struct RectInfo {
+        SkRect fRect;
+        GrColor fColor;
+        SkRect fLocalRect;
+    };
+
+    SkSTArray<1, RectInfo, true> fRects;
+    Helper fHelper;
+    bool fHasLocalMatrix;
+    bool fHasLocalRect;
+    SkMatrix fLocalMatrix;
+    SkMatrix fViewMatrix;
 
     typedef GrMeshDrawOp INHERITED;
 };
 
+}  // anonymous namespace
+
 namespace GrNonAAFillRectOp {
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkRect* localRect,
-                                   const SkMatrix* localMatrix) {
-    return std::unique_ptr<GrMeshDrawOp>(
-            new NonAAFillRectOp(color, viewMatrix, rect, localRect, localMatrix));
+std::unique_ptr<GrDrawOp> Make(GrPaint&& paint,
+                               const SkMatrix& viewMatrix,
+                               const SkRect& rect,
+                               const SkRect* localRect,
+                               const SkMatrix* localMatrix,
+                               GrAAType aaType,
+                               const GrUserStencilSettings* stencilSettings) {
+    if (!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective())) {
+        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, localRect, localMatrix,
+                                     aaType, stencilSettings);
+    } else {
+        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, localRect,
+                                                localMatrix, aaType, stencilSettings);
+    }
 }
-};
+
+};  // namespace GrNonAAFillRectOp
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-#if GR_TEST_UTILS
-
-#include "GrDrawOpTest.h"
-
-DRAW_OP_TEST_DEFINE(NonAAFillRectOp) {
-    GrColor color = GrRandomColor(random);
+GR_DRAW_OP_TEST_DEFINE(NonAAFillRectOp) {
     SkRect rect = GrTest::TestRect(random);
     SkRect localRect = GrTest::TestRect(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkMatrix localMatrix = GrTest::TestMatrix(random);
-
     bool hasLocalRect = random->nextBool();
     bool hasLocalMatrix = random->nextBool();
-    return GrNonAAFillRectOp::Make(color,
-                                   viewMatrix,
-                                   rect,
+    const GrUserStencilSettings* stencil = GrGetRandomStencil(random, context);
+    GrAAType aaType = GrAAType::kNone;
+    if (fsaaType == GrFSAAType::kUnifiedMSAA) {
+        aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
+    }
+    return GrNonAAFillRectOp::Make(std::move(paint), viewMatrix, rect,
                                    hasLocalRect ? &localRect : nullptr,
-                                   hasLocalMatrix ? &localMatrix : nullptr);
+                                   hasLocalMatrix ? &localMatrix : nullptr, aaType, stencil);
 }
-
-#endif
diff --git a/src/gpu/ops/GrNonAAFillRectOp.h b/src/gpu/ops/GrNonAAFillRectOp.h
index 046a955..c936f2e 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.h
+++ b/src/gpu/ops/GrNonAAFillRectOp.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -8,26 +8,24 @@
 #ifndef GrNonAAFillRectOp_DEFINED
 #define GrNonAAFillRectOp_DEFINED
 
+#include <memory>
 #include "GrColor.h"
-#include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrDrawOp;
+class GrPaint;
 class SkMatrix;
 struct SkRect;
+struct GrUserStencilSettings;
+enum class GrAAType : unsigned;
 
 namespace GrNonAAFillRectOp {
-
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkRect* localRect,
-                                   const SkMatrix* localMatrix);
-
-std::unique_ptr<GrMeshDrawOp> MakeWithPerspective(GrColor color,
-                                                  const SkMatrix& viewMatrix,
-                                                  const SkRect& rect,
-                                                  const SkRect* localRect,
-                                                  const SkMatrix* localMatrix);
+std::unique_ptr<GrDrawOp> Make(GrPaint&&,
+                               const SkMatrix& viewMatrix,
+                               const SkRect& rect,
+                               const SkRect* localRect,
+                               const SkMatrix* localMatrix,
+                               GrAAType,
+                               const GrUserStencilSettings* = nullptr);
 };
 
 #endif
diff --git a/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp b/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp
deleted file mode 100644
index 324ea43..0000000
--- a/src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp
+++ /dev/null
@@ -1,258 +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.
- */
-
-#include "GrNonAAFillRectOp.h"
-
-#include "GrColor.h"
-#include "GrDefaultGeoProcFactory.h"
-#include "GrMeshDrawOp.h"
-#include "GrOpFlushState.h"
-#include "GrPrimitiveProcessor.h"
-#include "GrQuad.h"
-#include "GrResourceProvider.h"
-
-static const int kVertsPerInstance = 4;
-static const int kIndicesPerInstance = 6;
-
-/** We always use per-vertex colors so that rects can be combined across color changes. Sometimes
-    we  have explicit local coords and sometimes not. We *could* always provide explicit local
-    coords and just duplicate the positions when the caller hasn't provided a local coord rect,
-    but we haven't seen a use case which frequently switches between local rect and no local
-    rect draws.
-
-    The vertex attrib order is always pos, color, [local coords].
- */
-static sk_sp<GrGeometryProcessor> make_persp_gp(const SkMatrix& viewMatrix,
-                                                bool hasExplicitLocalCoords,
-                                                const SkMatrix* localMatrix) {
-    SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
-
-    using namespace GrDefaultGeoProcFactory;
-
-    // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map
-    // the local rect on the cpu (in case the localMatrix also has perspective).
-    // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect
-    // to generate vertex local coords
-    if (viewMatrix.hasPerspective()) {
-        LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type
-                                                       : LocalCoords::kUsePosition_Type,
-                                localMatrix);
-        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
-                                             Coverage::kSolid_Type, localCoords, viewMatrix);
-    } else if (hasExplicitLocalCoords) {
-        LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix);
-        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
-                                             Coverage::kSolid_Type, localCoords, SkMatrix::I());
-    } else {
-        LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
-        return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type,
-                                                           Coverage::kSolid_Type, localCoords,
-                                                           viewMatrix);
-    }
-}
-
-static void tesselate(intptr_t vertices,
-                      size_t vertexStride,
-                      GrColor color,
-                      const SkMatrix* viewMatrix,
-                      const SkRect& rect,
-                      const GrQuad* localQuad) {
-    SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
-
-    positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
-
-    if (viewMatrix) {
-        viewMatrix->mapPointsWithStride(positions, vertexStride, kVertsPerInstance);
-    }
-
-    // Setup local coords
-    // TODO we should only do this if local coords are being read
-    if (localQuad) {
-        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
-        for (int i = 0; i < kVertsPerInstance; i++) {
-            SkPoint* coords =
-                    reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
-            *coords = localQuad->point(i);
-        }
-    }
-
-    static const int kColorOffset = sizeof(SkPoint);
-    GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
-    for (int j = 0; j < 4; ++j) {
-        *vertColor = color;
-        vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
-    }
-}
-
-// We handle perspective in the local matrix or viewmatrix with special ops.
-class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    NonAAFillRectPerspectiveOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
-                               const SkRect* localRect, const SkMatrix* localMatrix)
-            : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
-        SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
-        RectInfo& info = fRects.push_back();
-        info.fColor = color;
-        info.fRect = rect;
-        fHasLocalRect = SkToBool(localRect);
-        fHasLocalMatrix = SkToBool(localMatrix);
-        if (fHasLocalMatrix) {
-            fLocalMatrix = *localMatrix;
-        }
-        if (fHasLocalRect) {
-            info.fLocalRect = *localRect;
-        }
-        this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
-    }
-
-    const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
-
-    SkString dumpInfo() const override {
-        SkString str;
-        str.appendf("# combined: %d\n", fRects.count());
-        for (int i = 0; i < fRects.count(); ++i) {
-            const RectInfo& geo = fRects[0];
-            str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
-                        geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight,
-                        geo.fRect.fBottom);
-        }
-        str.append(DumpPipelineInfo(*this->pipeline()));
-        str.append(INHERITED::dumpInfo());
-        return str;
-    }
-
-private:
-    NonAAFillRectPerspectiveOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fRects[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kNone;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fRects[0].fColor);
-    }
-
-    void onPrepareDraws(Target* target) const override {
-        sk_sp<GrGeometryProcessor> gp = make_persp_gp(fViewMatrix,
-                                                      fHasLocalRect,
-                                                      fHasLocalMatrix ? &fLocalMatrix : nullptr);
-        if (!gp) {
-            SkDebugf("Couldn't create GrGeometryProcessor\n");
-            return;
-        }
-        SkASSERT(fHasLocalRect
-                         ? gp->getVertexStride() ==
-                                   sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
-                         : gp->getVertexStride() ==
-                                   sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
-
-        size_t vertexStride = gp->getVertexStride();
-        int instanceCount = fRects.count();
-
-        sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
-        void* vertices =
-                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
-                            kVertsPerInstance, kIndicesPerInstance, instanceCount);
-        if (!vertices || !indexBuffer) {
-            SkDebugf("Could not allocate vertices\n");
-            return;
-        }
-
-        for (int i = 0; i < instanceCount; i++) {
-            const RectInfo& info = fRects[i];
-            intptr_t verts =
-                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride;
-            if (fHasLocalRect) {
-                GrQuad quad(info.fLocalRect);
-                tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad);
-            } else {
-                tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
-            }
-        }
-        helper.recordDraw(target, gp.get());
-    }
-
-    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
-        NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
-            return false;
-        }
-
-        // We could combine across perspective vm changes if we really wanted to.
-        if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
-            return false;
-        }
-        if (fHasLocalRect != that->fHasLocalRect) {
-            return false;
-        }
-        if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) {
-            return false;
-        }
-
-        fRects.push_back_n(that->fRects.count(), that->fRects.begin());
-        this->joinBounds(*that);
-        return true;
-    }
-
-    struct RectInfo {
-        SkRect fRect;
-        GrColor fColor;
-        SkRect fLocalRect;
-    };
-
-    SkSTArray<1, RectInfo, true> fRects;
-    bool fHasLocalMatrix;
-    bool fHasLocalRect;
-    SkMatrix fLocalMatrix;
-    SkMatrix fViewMatrix;
-
-    typedef GrMeshDrawOp INHERITED;
-};
-
-namespace GrNonAAFillRectOp {
-
-std::unique_ptr<GrMeshDrawOp> MakeWithPerspective(GrColor color,
-                                                  const SkMatrix& viewMatrix,
-                                                  const SkRect& rect,
-                                                  const SkRect* localRect,
-                                                  const SkMatrix* localMatrix) {
-    return std::unique_ptr<GrMeshDrawOp>(
-            new NonAAFillRectPerspectiveOp(color, viewMatrix, rect, localRect, localMatrix));
-}
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-#if GR_TEST_UTILS
-
-#include "GrDrawOpTest.h"
-
-DRAW_OP_TEST_DEFINE(NonAAFillRectPerspectiveOp) {
-    GrColor color = GrRandomColor(random);
-    SkRect rect = GrTest::TestRect(random);
-    SkRect localRect = GrTest::TestRect(random);
-    SkMatrix viewMatrix = GrTest::TestMatrix(random);
-    bool hasLocalMatrix = random->nextBool();
-    SkMatrix localMatrix;
-    if (!viewMatrix.hasPerspective()) {
-        localMatrix = GrTest::TestMatrixPerspective(random);
-        hasLocalMatrix = true;
-    }
-
-    bool hasLocalRect = random->nextBool();
-    return GrNonAAFillRectOp::MakeWithPerspective(color, viewMatrix, rect,
-                                                  hasLocalRect ? &localRect : nullptr,
-                                                  hasLocalMatrix ? &localMatrix : nullptr);
-}
-
-#endif
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index 2f63d96..eee8120 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -46,7 +46,7 @@
            (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
 }
 
-class NonAAStrokeRectOp final : public GrMeshDrawOp {
+class NonAAStrokeRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -63,9 +63,9 @@
         return string;
     }
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              const SkRect& rect, const SkStrokeRec& stroke,
-                                              bool snapToPixelCenters) {
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
+                                                    const SkRect& rect, const SkStrokeRec& stroke,
+                                                    bool snapToPixelCenters) {
         if (!allowed_stroke(stroke)) {
             return nullptr;
         }
@@ -96,16 +96,16 @@
         } else {
             op->setTransformedBounds(bounds, op->fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
         }
-        return std::unique_ptr<GrMeshDrawOp>(op);
+        return std::unique_ptr<GrLegacyMeshDrawOp>(op);
     }
 
 private:
     NonAAStrokeRectOp() : INHERITED(ClassID()) {}
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = GrPipelineAnalysisCoverage::kNone;
+        *coverage = GrProcessorAnalysisCoverage::kNone;
     }
 
     void onPrepareDraws(Target* target) const override {
@@ -156,12 +156,13 @@
             vertex[4].set(fRect.fLeft, fRect.fTop);
         }
 
-        GrMesh mesh;
-        mesh.init(primType, vertexBuffer, firstVertex, vertexCount);
-        target->draw(gp.get(), mesh);
+        GrMesh mesh(primType);
+        mesh.setNonIndexedNonInstanced(vertexCount);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(gp.get(), this->pipeline(), mesh);
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
         fNeedsLocalCoords = optimizations.readsLocalCoords();
     }
@@ -181,23 +182,23 @@
     const static int kVertsPerHairlineRect = 5;
     const static int kVertsPerStrokeRect = 10;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 namespace GrNonAAStrokeRectOp {
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkStrokeRec& stroke,
-                                   bool snapToPixelCenters) {
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRect& rect,
+                                         const SkStrokeRec& stroke,
+                                         bool snapToPixelCenters) {
     return NonAAStrokeRectOp::Make(color, viewMatrix, rect, stroke, snapToPixelCenters);
 }
 }
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
     GrColor color = GrRandomColor(random);
     SkRect rect = GrTest::TestRect(random);
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.h b/src/gpu/ops/GrNonAAStrokeRectOp.h
index 5c9c98e..4071cfd 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.h
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.h
@@ -11,18 +11,18 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 struct SkRect;
 class SkStrokeRec;
 class SkMatrix;
 
 namespace GrNonAAStrokeRectOp {
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRect& rect,
-                                   const SkStrokeRec&,
-                                   bool snapToPixelCenters);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRect& rect,
+                                         const SkStrokeRec&,
+                                         bool snapToPixelCenters);
 }
 
 #endif
diff --git a/src/gpu/ops/GrOp.h b/src/gpu/ops/GrOp.h
index ef752b5..953a914 100644
--- a/src/gpu/ops/GrOp.h
+++ b/src/gpu/ops/GrOp.h
@@ -21,6 +21,7 @@
 class GrCaps;
 class GrGpuCommandBuffer;
 class GrOpFlushState;
+class GrRenderTargetOpList;
 
 /**
  * GrOp is the base class for all Ganesh deferred GPU operations. To facilitate reordering and to
@@ -132,7 +133,7 @@
      * combined into another op or have another op combined into it via combineIfPossible() after
      * this call is made.
      */
-    virtual void wasRecorded() {}
+    virtual void wasRecorded(GrRenderTargetOpList*) {}
 
     /**
      * Called prior to executing. The op should perform any resource creation or data transfers
@@ -151,6 +152,8 @@
         return string;
     }
 
+    virtual bool needsCommandBufferIsolation() const { return false; }
+
 protected:
     /**
      * Indicates that the op will produce geometry that extends beyond its bounds for the
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 83e783f..2b9d1c8 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrOvalOpFactory.h"
-
 #include "GrDrawOpTest.h"
 #include "GrGeometryProcessor.h"
 #include "GrOpFlushState.h"
@@ -24,8 +23,7 @@
 #include "glsl/GrGLSLVarying.h"
 #include "glsl/GrGLSLVertexShaderBuilder.h"
 #include "ops/GrMeshDrawOp.h"
-
-// TODO(joshualitt) - Break this file up during GrOp post implementation cleanup
+#include "ops/GrSimpleMeshDrawOpHelper.h"
 
 namespace {
 
@@ -59,12 +57,6 @@
  *             p is the position in the normalized space.
  *             outerRad is the outerRadius in device space.
  *             innerRad is the innerRadius in normalized space (ignored if not stroking).
- * If fUsesDistanceVectorField is set in fragment processors in the same program, then
- * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
- *    vec4f : (v.xy, outerDistance, innerDistance)
- *             v is a normalized vector pointing to the outer edge
- *             outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
- *             if stroking, innerDistance is the distance to the inner edge, < 0 if outside
  * Additional clip planes are supported for rendering circular arcs. The additional planes are
  * either intersected or unioned together. Up to three planes are supported (an initial plane,
  * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
@@ -101,8 +93,6 @@
         fStroke = stroke;
     }
 
-    bool implementsDistanceVector() const override { return !fInClipPlane; }
-
     ~CircleGeometryProcessor() override {}
 
     const char* name() const override { return "CircleEdge"; }
@@ -172,21 +162,6 @@
                 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
             }
 
-            if (args.fDistanceVectorName) {
-                const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
-                fragBuilder->codeAppendf(
-                        "if (d == 0.0) {"  // if on the center of the circle
-                        "    %s = vec4(1.0, 0.0, distanceToOuterEdge, "
-                        "              %s);",  // no normalize
-                        args.fDistanceVectorName,
-                        innerEdgeDistance);
-                fragBuilder->codeAppendf(
-                        "} else {"
-                        "    %s = vec4(normalize(circleEdge.xy),"
-                        "              distanceToOuterEdge, %s);"
-                        "}",
-                        args.fDistanceVectorName, innerEdgeDistance);
-            }
             if (cgp.fInClipPlane) {
                 fragBuilder->codeAppend(
                         "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
@@ -609,6 +584,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 class CircleOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
 public:
     DEFINE_OP_CLASS_ID
 
@@ -618,14 +596,15 @@
         SkScalar fSweepAngleRadians;
         bool fUseCenter;
     };
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              SkPoint center, SkScalar radius, const GrStyle& style,
-                                              const ArcParams* arcParams = nullptr) {
+
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          SkPoint center, SkScalar radius, const GrStyle& style,
+                                          const ArcParams* arcParams = nullptr) {
         SkASSERT(circle_stays_circle(viewMatrix));
-        const SkStrokeRec& stroke = style.strokeRec();
         if (style.hasPathEffect()) {
             return nullptr;
         }
+        const SkStrokeRec& stroke = style.strokeRec();
         SkStrokeRec::Style recStyle = stroke.getStyle();
         if (arcParams) {
             // Arc support depends on the style.
@@ -645,6 +624,15 @@
                     break;
             }
         }
+        return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
+                                               arcParams);
+    }
+
+    CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
+             SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
+            : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
+        const SkStrokeRec& stroke = style.strokeRec();
+        SkStrokeRec::Style recStyle = stroke.getStyle();
 
         viewMatrix.mapPoints(&center, 1);
         radius = viewMatrix.mapRadius(radius);
@@ -677,8 +665,7 @@
         outerRadius += SK_ScalarHalf;
         innerRadius -= SK_ScalarHalf;
         bool stroked = isStrokeOnly && innerRadius > 0.0f;
-        std::unique_ptr<CircleOp> op(new CircleOp());
-        op->fViewMatrixIfUsingLocalCoords = viewMatrix;
+        fViewMatrixIfUsingLocalCoords = viewMatrix;
 
         // This makes every point fully inside the intersection plane.
         static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
@@ -693,6 +680,18 @@
             startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
             SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
             stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
+
+            // Adjust the start and end points based on the view matrix (to handle rotated arcs)
+            startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
+            stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
+            startPoint.normalize();
+            stopPoint.normalize();
+
+            // If the matrix included scale (on one axis) we need to swap our start and end points
+            if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
+                SkTSwap(startPoint, stopPoint);
+            }
+
             // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
             // radial lines. However, in both cases we have to be careful about the half-circle.
             // case. In that case the two radial lines are equal and so that edge gets clipped
@@ -709,9 +708,9 @@
                 } else {
                     norm1.negate();
                 }
-                op->fClipPlane = true;
+                fClipPlane = true;
                 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
-                    op->fGeoData.emplace_back(Geometry{
+                    fCircles.emplace_back(Circle{
                             color,
                             innerRadius,
                             outerRadius,
@@ -720,10 +719,10 @@
                             {norm1.fX, norm1.fY, 0.5f},
                             devBounds,
                             stroked});
-                    op->fClipPlaneIsect = false;
-                    op->fClipPlaneUnion = true;
+                    fClipPlaneIsect = false;
+                    fClipPlaneUnion = true;
                 } else {
-                    op->fGeoData.emplace_back(Geometry{
+                    fCircles.emplace_back(Circle{
                             color,
                             innerRadius,
                             outerRadius,
@@ -732,8 +731,8 @@
                             {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
                             devBounds,
                             stroked});
-                    op->fClipPlaneIsect = true;
-                    op->fClipPlaneUnion = false;
+                    fClipPlaneIsect = true;
+                    fClipPlaneUnion = false;
                 }
             } else {
                 // We clip to a secant of the original circle.
@@ -746,78 +745,69 @@
                 }
                 SkScalar d = -norm.dot(startPoint) + 0.5f;
 
-                op->fGeoData.emplace_back(
-                        Geometry{color,
-                                 innerRadius,
-                                 outerRadius,
-                                 {norm.fX, norm.fY, d},
-                                 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
-                                 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
-                                 devBounds,
-                                 stroked});
-                op->fClipPlane = true;
-                op->fClipPlaneIsect = false;
-                op->fClipPlaneUnion = false;
+                fCircles.emplace_back(
+                        Circle{color,
+                               innerRadius,
+                               outerRadius,
+                               {norm.fX, norm.fY, d},
+                               {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
+                               {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
+                               devBounds,
+                               stroked});
+                fClipPlane = true;
+                fClipPlaneIsect = false;
+                fClipPlaneUnion = false;
             }
         } else {
-            op->fGeoData.emplace_back(
-                    Geometry{color,
-                             innerRadius,
-                             outerRadius,
-                             {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
-                             {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
-                             {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
-                             devBounds,
-                             stroked});
-            op->fClipPlane = false;
-            op->fClipPlaneIsect = false;
-            op->fClipPlaneUnion = false;
+            fCircles.emplace_back(
+                    Circle{color,
+                           innerRadius,
+                           outerRadius,
+                           {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
+                           {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
+                           {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
+                           devBounds,
+                           stroked});
+            fClipPlane = false;
+            fClipPlaneIsect = false;
+            fClipPlaneUnion = false;
         }
         // Use the original radius and stroke radius for the bounds so that it does not include the
         // AA bloat.
         radius += halfWidth;
-        op->setBounds(
+        this->setBounds(
                 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
                 HasAABloat::kYes, IsZeroArea::kNo);
-        op->fVertCount = circle_type_to_vert_count(stroked);
-        op->fIndexCount = circle_type_to_index_count(stroked);
-        op->fAllFill = !stroked;
-        return std::move(op);
+        fVertCount = circle_type_to_vert_count(stroked);
+        fIndexCount = circle_type_to_index_count(stroked);
+        fAllFill = !stroked;
     }
 
     const char* name() const override { return "CircleOp"; }
 
     SkString dumpInfo() const override {
         SkString string;
-        for (int i = 0; i < fGeoData.count(); ++i) {
+        for (int i = 0; i < fCircles.count(); ++i) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
                     "InnerRad: %.2f, OuterRad: %.2f\n",
-                    fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
-                    fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
-                    fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
+                    fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
+                    fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
+                    fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
         }
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fCircles.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
+                                            color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
 private:
-    CircleOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
-        if (!optimizations.readsLocalCoords()) {
-            fViewMatrixIfUsingLocalCoords.reset();
-        }
-    }
-
     void onPrepareDraws(Target* target) const override {
         SkMatrix localMatrix;
         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
@@ -838,7 +828,6 @@
             SkScalar fHalfPlanes[3][3];
         };
 
-        int instanceCount = fGeoData.count();
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride ==
                  sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
@@ -863,14 +852,12 @@
         }
 
         int currStartVertex = 0;
-        for (int i = 0; i < instanceCount; i++) {
-            const Geometry& geom = fGeoData[i];
+        for (const auto& circle : fCircles) {
+            SkScalar innerRadius = circle.fInnerRadius;
+            SkScalar outerRadius = circle.fOuterRadius;
+            GrColor color = circle.fColor;
+            const SkRect& bounds = circle.fDevBounds;
 
-            GrColor color = geom.fColor;
-            SkScalar innerRadius = geom.fInnerRadius;
-            SkScalar outerRadius = geom.fOuterRadius;
-
-            const SkRect& bounds = geom.fDevBounds;
             CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
             CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
             CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
@@ -936,39 +923,39 @@
             v7->fInnerRadius = innerRadius;
 
             if (fClipPlane) {
-                memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
             }
             int unionIdx = 1;
             if (fClipPlaneIsect) {
-                memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
                 unionIdx = 2;
             }
             if (fClipPlaneUnion) {
-                memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
             }
 
-            if (geom.fStroked) {
+            if (circle.fStroked) {
                 // compute the inner ring
                 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
                 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
@@ -982,7 +969,7 @@
                 // cosine and sine of pi/8
                 SkScalar c = 0.923579533f;
                 SkScalar s = 0.382683432f;
-                SkScalar r = geom.fInnerRadius;
+                SkScalar r = circle.fInnerRadius;
 
                 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
                 v0->fColor = color;
@@ -1033,36 +1020,36 @@
                 v7->fInnerRadius = innerRadius;
 
                 if (fClipPlane) {
-                    memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
-                    memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
                 }
                 int unionIdx = 1;
                 if (fClipPlaneIsect) {
-                    memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
-                    memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
                     unionIdx = 2;
                 }
                 if (fClipPlaneUnion) {
-                    memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
-                    memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
                 }
             } else {
                 // filled
@@ -1073,32 +1060,32 @@
                 v8->fOuterRadius = outerRadius;
                 v8->fInnerRadius = innerRadius;
                 if (fClipPlane) {
-                    memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
+                    memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
                 }
                 int unionIdx = 1;
                 if (fClipPlaneIsect) {
-                    memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
+                    memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
                     unionIdx = 2;
                 }
                 if (fClipPlaneUnion) {
-                    memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
+                    memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
                 }
             }
 
-            const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
-            const int primIndexCount = circle_type_to_index_count(geom.fStroked);
+            const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
+            const int primIndexCount = circle_type_to_index_count(circle.fStroked);
             for (int i = 0; i < primIndexCount; ++i) {
                 *indices++ = primIndices[i] + currStartVertex;
             }
 
-            currStartVertex += circle_type_to_vert_count(geom.fStroked);
-            vertices += circle_type_to_vert_count(geom.fStroked) * vertexStride;
+            currStartVertex += circle_type_to_vert_count(circle.fStroked);
+            vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
         }
 
-        GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
-        target->draw(gp.get(), mesh);
+        GrMesh mesh(kTriangles_GrPrimitiveType);
+        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(gp.get(),  fHelper.makePipeline(target), mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -1109,12 +1096,12 @@
             return false;
         }
 
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
-        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
+        if (fHelper.usesLocalCoords() &&
+            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
             return false;
         }
 
@@ -1124,7 +1111,7 @@
         fClipPlaneIsect |= that->fClipPlaneIsect;
         fClipPlaneUnion |= that->fClipPlaneUnion;
 
-        fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
+        fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
         this->joinBounds(*that);
         fVertCount += that->fVertCount;
         fIndexCount += that->fIndexCount;
@@ -1132,7 +1119,7 @@
         return true;
     }
 
-    struct Geometry {
+    struct Circle {
         GrColor fColor;
         SkScalar fInnerRadius;
         SkScalar fOuterRadius;
@@ -1143,8 +1130,9 @@
         bool fStroked;
     };
 
-    SkSTArray<1, Geometry, true> fGeoData;
     SkMatrix fViewMatrixIfUsingLocalCoords;
+    Helper fHelper;
+    SkSTArray<1, Circle, true> fCircles;
     int fVertCount;
     int fIndexCount;
     bool fAllFill;
@@ -1158,21 +1146,31 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 class EllipseOp : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
+    struct DeviceSpaceParams {
+        SkPoint fCenter;
+        SkScalar fXRadius;
+        SkScalar fYRadius;
+        SkScalar fInnerXRadius;
+        SkScalar fInnerYRadius;
+    };
+
 public:
     DEFINE_OP_CLASS_ID
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              const SkRect& ellipse, const SkStrokeRec& stroke) {
-        SkASSERT(viewMatrix.rectStaysRect());
-
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& ellipse, const SkStrokeRec& stroke) {
+        DeviceSpaceParams params;
         // do any matrix crunching before we reset the draw state for device coords
-        SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
-        viewMatrix.mapPoints(&center, 1);
+        params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
+        viewMatrix.mapPoints(&params.fCenter, 1);
         SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
         SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
-        SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
-                                       viewMatrix[SkMatrix::kMSkewY] * ellipseYRadius);
-        SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * ellipseXRadius +
-                                       viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
+        params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
+                                      viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
+        params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
+                                      viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
 
         // do (potentially) anisotropic mapping of stroke
         SkVector scaledStroke;
@@ -1187,8 +1185,8 @@
                 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
         bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
 
-        SkScalar innerXRadius = 0;
-        SkScalar innerYRadius = 0;
+        params.fInnerXRadius = 0;
+        params.fInnerYRadius = 0;
         if (hasStroke) {
             if (SkScalarNearlyZero(scaledStroke.length())) {
                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
@@ -1198,42 +1196,52 @@
 
             // we only handle thick strokes for near-circular ellipses
             if (scaledStroke.length() > SK_ScalarHalf &&
-                (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
+                (0.5f * params.fXRadius > params.fYRadius ||
+                 0.5f * params.fYRadius > params.fXRadius)) {
                 return nullptr;
             }
 
             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
-            if (scaledStroke.fX * (yRadius * yRadius) <
-                        (scaledStroke.fY * scaledStroke.fY) * xRadius ||
-                scaledStroke.fY * (xRadius * xRadius) <
-                        (scaledStroke.fX * scaledStroke.fX) * yRadius) {
+            if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
+                        (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
+                scaledStroke.fY * (params.fXRadius * params.fXRadius) <
+                        (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
                 return nullptr;
             }
 
             // this is legit only if scale & translation (which should be the case at the moment)
             if (isStrokeOnly) {
-                innerXRadius = xRadius - scaledStroke.fX;
-                innerYRadius = yRadius - scaledStroke.fY;
+                params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
+                params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
             }
 
-            xRadius += scaledStroke.fX;
-            yRadius += scaledStroke.fY;
+            params.fXRadius += scaledStroke.fX;
+            params.fYRadius += scaledStroke.fY;
         }
+        return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
+    }
 
-        std::unique_ptr<EllipseOp> op(new EllipseOp());
-        op->fGeoData.emplace_back(
-                Geometry{color, xRadius, yRadius, innerXRadius, innerYRadius,
-                         SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
-                                          center.fX + xRadius, center.fY + yRadius)});
+    EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
+              const DeviceSpaceParams& params, const SkStrokeRec& stroke)
+            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
+        SkStrokeRec::Style style = stroke.getStyle();
+        bool isStrokeOnly =
+                SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
 
-        op->setBounds(op->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
+        fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
+                                       params.fInnerXRadius, params.fInnerYRadius,
+                                       SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
+                                                        params.fCenter.fY - params.fYRadius,
+                                                        params.fCenter.fX + params.fXRadius,
+                                                        params.fCenter.fY + params.fYRadius)});
+
+        this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
 
         // Outset bounds to include half-pixel width antialiasing.
-        op->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+        fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
 
-        op->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
-        op->fViewMatrixIfUsingLocalCoords = viewMatrix;
-        return std::move(op);
+        fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
+        fViewMatrixIfUsingLocalCoords = viewMatrix;
     }
 
     const char* name() const override { return "EllipseOp"; }
@@ -1241,7 +1249,7 @@
     SkString dumpInfo() const override {
         SkString string;
         string.appendf("Stroked: %d\n", fStroked);
-        for (const auto& geo : fGeoData) {
+        for (const auto& geo : fEllipses) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
                     "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
@@ -1249,26 +1257,19 @@
                     geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
                     geo.fInnerYRadius);
         }
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fEllipses.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
+                                            color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
 private:
-    EllipseOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        if (!optimizations.readsLocalCoords()) {
-            fViewMatrixIfUsingLocalCoords.reset();
-        }
-    }
-
     void onPrepareDraws(Target* target) const override {
         SkMatrix localMatrix;
         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
@@ -1278,55 +1279,50 @@
         // Setup geometry processor
         sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
 
-        int instanceCount = fGeoData.count();
         QuadHelper helper;
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(EllipseVertex));
-        EllipseVertex* verts =
-                reinterpret_cast<EllipseVertex*>(helper.init(target, vertexStride, instanceCount));
+        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
+                helper.init(target, vertexStride, fEllipses.count()));
         if (!verts) {
             return;
         }
 
-        for (int i = 0; i < instanceCount; i++) {
-            const Geometry& geom = fGeoData[i];
-
-            GrColor color = geom.fColor;
-            SkScalar xRadius = geom.fXRadius;
-            SkScalar yRadius = geom.fYRadius;
+        for (const auto& ellipse : fEllipses) {
+            GrColor color = ellipse.fColor;
+            SkScalar xRadius = ellipse.fXRadius;
+            SkScalar yRadius = ellipse.fYRadius;
 
             // Compute the reciprocals of the radii here to save time in the shader
             SkScalar xRadRecip = SkScalarInvert(xRadius);
             SkScalar yRadRecip = SkScalarInvert(yRadius);
-            SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
-            SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
-
-            const SkRect& bounds = geom.fDevBounds;
+            SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
+            SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
 
             // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
             SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
             SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
 
             // The inner radius in the vertex data must be specified in normalized space.
-            verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
+            verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
             verts[0].fColor = color;
             verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
             verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
-            verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
+            verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
             verts[1].fColor = color;
             verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
             verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
-            verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
+            verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
             verts[2].fColor = color;
             verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
             verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
-            verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
+            verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
             verts[3].fColor = color;
             verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
             verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
@@ -1334,14 +1330,13 @@
 
             verts += kVerticesPerQuad;
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         EllipseOp* that = t->cast<EllipseOp>();
 
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
@@ -1349,16 +1344,17 @@
             return false;
         }
 
-        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
+        if (fHelper.usesLocalCoords() &&
+            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
             return false;
         }
 
-        fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
+        fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
         this->joinBounds(*that);
         return true;
     }
 
-    struct Geometry {
+    struct Ellipse {
         GrColor fColor;
         SkScalar fXRadius;
         SkScalar fYRadius;
@@ -1367,9 +1363,10 @@
         SkRect fDevBounds;
     };
 
-    bool fStroked;
     SkMatrix fViewMatrixIfUsingLocalCoords;
-    SkSTArray<1, Geometry, true> fGeoData;
+    Helper fHelper;
+    bool fStroked;
+    SkSTArray<1, Ellipse, true> fEllipses;
 
     typedef GrMeshDrawOp INHERITED;
 };
@@ -1377,26 +1374,37 @@
 /////////////////////////////////////////////////////////////////////////////////////////////////
 
 class DIEllipseOp : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
+    struct DeviceSpaceParams {
+        SkPoint fCenter;
+        SkScalar fXRadius;
+        SkScalar fYRadius;
+        SkScalar fInnerXRadius;
+        SkScalar fInnerYRadius;
+        DIEllipseStyle fStyle;
+    };
+
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                              const SkMatrix& viewMatrix,
-                                              const SkRect& ellipse,
-                                              const SkStrokeRec& stroke) {
-        SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
-        SkScalar xRadius = SkScalarHalf(ellipse.width());
-        SkScalar yRadius = SkScalarHalf(ellipse.height());
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& ellipse, const SkStrokeRec& stroke) {
+        DeviceSpaceParams params;
+        params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
+        params.fXRadius = SkScalarHalf(ellipse.width());
+        params.fYRadius = SkScalarHalf(ellipse.height());
 
         SkStrokeRec::Style style = stroke.getStyle();
-        DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style)
-                                          ? DIEllipseStyle::kStroke
-                                          : (SkStrokeRec::kHairline_Style == style)
-                                                    ? DIEllipseStyle::kHairline
-                                                    : DIEllipseStyle::kFill;
+        params.fStyle = (SkStrokeRec::kStroke_Style == style)
+                                ? DIEllipseStyle::kStroke
+                                : (SkStrokeRec::kHairline_Style == style)
+                                          ? DIEllipseStyle::kHairline
+                                          : DIEllipseStyle::kFill;
 
-        SkScalar innerXRadius = 0;
-        SkScalar innerYRadius = 0;
+        params.fInnerXRadius = 0;
+        params.fInnerYRadius = 0;
         if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
             SkScalar strokeWidth = stroke.getWidth();
 
@@ -1408,30 +1416,40 @@
 
             // we only handle thick strokes for near-circular ellipses
             if (strokeWidth > SK_ScalarHalf &&
-                (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
+                (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
+                 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
                 return nullptr;
             }
 
             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
-            if (strokeWidth * (yRadius * yRadius) < (strokeWidth * strokeWidth) * xRadius ||
-                strokeWidth * (xRadius * xRadius) < (strokeWidth * strokeWidth) * yRadius) {
+            if (strokeWidth * (params.fYRadius * params.fYRadius) <
+                (strokeWidth * strokeWidth) * params.fXRadius) {
+                return nullptr;
+            }
+            if (strokeWidth * (params.fXRadius * params.fXRadius) <
+                (strokeWidth * strokeWidth) * params.fYRadius) {
                 return nullptr;
             }
 
             // set inner radius (if needed)
             if (SkStrokeRec::kStroke_Style == style) {
-                innerXRadius = xRadius - strokeWidth;
-                innerYRadius = yRadius - strokeWidth;
+                params.fInnerXRadius = params.fXRadius - strokeWidth;
+                params.fInnerYRadius = params.fYRadius - strokeWidth;
             }
 
-            xRadius += strokeWidth;
-            yRadius += strokeWidth;
+            params.fXRadius += strokeWidth;
+            params.fYRadius += strokeWidth;
         }
-        if (DIEllipseStyle::kStroke == dieStyle) {
-            dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle::kStroke
-                                                              : DIEllipseStyle::kFill;
+        if (DIEllipseStyle::kStroke == params.fStyle &&
+            (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
+            params.fStyle = DIEllipseStyle::kFill;
         }
+        return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
+    }
 
+    DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
+                const SkMatrix& viewMatrix)
+            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
         // This expands the outer rect so that after CTM we end up with a half-pixel border
         SkScalar a = viewMatrix[SkMatrix::kMScaleX];
         SkScalar b = viewMatrix[SkMatrix::kMSkewX];
@@ -1440,22 +1458,22 @@
         SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
         SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
 
-        std::unique_ptr<DIEllipseOp> op(new DIEllipseOp());
-        op->fGeoData.emplace_back(Geometry{
-                viewMatrix, color, xRadius, yRadius, innerXRadius, innerYRadius, geoDx, geoDy,
-                dieStyle,
-                SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
-                                 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)});
-        op->setTransformedBounds(op->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
-                                 IsZeroArea::kNo);
-        return std::move(op);
+        fEllipses.emplace_back(
+                Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
+                        params.fInnerYRadius, geoDx, geoDy, params.fStyle,
+                        SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
+                                         params.fCenter.fY - params.fYRadius - geoDy,
+                                         params.fCenter.fX + params.fXRadius + geoDx,
+                                         params.fCenter.fY + params.fYRadius + geoDy)});
+        this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
+                                   IsZeroArea::kNo);
     }
 
     const char* name() const override { return "DIEllipseOp"; }
 
     SkString dumpInfo() const override {
         SkString string;
-        for (const auto& geo : fGeoData) {
+        for (const auto& geo : fEllipses) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
                     "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
@@ -1464,55 +1482,46 @@
                     geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
                     geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
         }
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fEllipses.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
+                                            color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
 private:
-    DIEllipseOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
-        fUsesLocalCoords = optimizations.readsLocalCoords();
-    }
-
     void onPrepareDraws(Target* target) const override {
         // Setup geometry processor
         sk_sp<GrGeometryProcessor> gp(
                 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
 
-        int instanceCount = fGeoData.count();
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(DIEllipseVertex));
         QuadHelper helper;
         DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
-                helper.init(target, vertexStride, instanceCount));
+                helper.init(target, vertexStride, fEllipses.count()));
         if (!verts) {
             return;
         }
 
-        for (int i = 0; i < instanceCount; i++) {
-            const Geometry& geom = fGeoData[i];
+        for (const auto& ellipse : fEllipses) {
+            GrColor color = ellipse.fColor;
+            SkScalar xRadius = ellipse.fXRadius;
+            SkScalar yRadius = ellipse.fYRadius;
 
-            GrColor color = geom.fColor;
-            SkScalar xRadius = geom.fXRadius;
-            SkScalar yRadius = geom.fYRadius;
-
-            const SkRect& bounds = geom.fBounds;
+            const SkRect& bounds = ellipse.fBounds;
 
             // This adjusts the "radius" to include the half-pixel border
-            SkScalar offsetDx = geom.fGeoDx / xRadius;
-            SkScalar offsetDy = geom.fGeoDy / yRadius;
+            SkScalar offsetDx = ellipse.fGeoDx / xRadius;
+            SkScalar offsetDy = ellipse.fGeoDy / yRadius;
 
-            SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
-            SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
+            SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
+            SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
 
             verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
             verts[0].fColor = color;
@@ -1536,13 +1545,12 @@
 
             verts += kVerticesPerQuad;
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         DIEllipseOp* that = t->cast<DIEllipseOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
@@ -1555,15 +1563,15 @@
             return false;
         }
 
-        fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
+        fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
         this->joinBounds(*that);
         return true;
     }
 
-    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
-    DIEllipseStyle style() const { return fGeoData[0].fStyle; }
+    const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
+    DIEllipseStyle style() const { return fEllipses[0].fStyle; }
 
-    struct Geometry {
+    struct Ellipse {
         SkMatrix fViewMatrix;
         GrColor fColor;
         SkScalar fXRadius;
@@ -1576,8 +1584,8 @@
         SkRect fBounds;
     };
 
-    bool fUsesLocalCoords;
-    SkSTArray<1, Geometry, true> fGeoData;
+    Helper fHelper;
+    SkSTArray<1, Ellipse, true> fEllipses;
 
     typedef GrMeshDrawOp INHERITED;
 };
@@ -1658,7 +1666,6 @@
     kFill_RRectType,
     kStroke_RRectType,
     kOverstroke_RRectType,
-    kFillWithDist_RRectType
 };
 
 static int rrect_type_to_vert_count(RRectType type) {
@@ -1667,7 +1674,6 @@
         case kStroke_RRectType:
             return kVertsPerStandardRRect;
         case kOverstroke_RRectType:
-        case kFillWithDist_RRectType:
             return kVertsPerOverstrokeRRect;
     }
     SkFAIL("Invalid type");
@@ -1681,7 +1687,6 @@
         case kStroke_RRectType:
             return kIndicesPerStrokeRRect;
         case kOverstroke_RRectType:
-        case kFillWithDist_RRectType:
             return kIndicesPerOverstrokeRRect;
     }
     SkFAIL("Invalid type");
@@ -1694,7 +1699,6 @@
         case kStroke_RRectType:
             return gStandardRRectIndices;
         case kOverstroke_RRectType:
-        case kFillWithDist_RRectType:
             return gOverstrokeRRectIndices;
     }
     SkFAIL("Invalid type");
@@ -1713,14 +1717,25 @@
 //      the GP takes the min of those depths +1 to get the normalized distance to the outer edge
 
 class CircularRRectOp : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
 public:
     DEFINE_OP_CLASS_ID
 
     // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
     // whether the rrect is only stroked or stroked and filled.
-    CircularRRectOp(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& devRect, float devRadius,
+                                          float devStrokeWidth, bool strokeOnly) {
+        return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
+                                                      devRadius, devStrokeWidth, strokeOnly);
+    }
+    CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
                     const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
-            : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
+            : INHERITED(ClassID())
+            , fViewMatrixIfUsingLocalCoords(viewMatrix)
+            , fHelper(helperArgs, GrAAType::kCoverage) {
         SkRect bounds = devRect;
         SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
         SkScalar innerRadius = 0.0f;
@@ -1747,9 +1762,6 @@
             outerRadius += halfWidth;
             bounds.outset(halfWidth, halfWidth);
         }
-        if (kFill_RRectType == type && needsDistance) {
-            type = kFillWithDist_RRectType;
-        }
 
         // The radii are outset for two reasons. First, it allows the shader to simply perform
         // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
@@ -1764,7 +1776,7 @@
         // Expand the rect for aa to generate correct vertices.
         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
 
-        fGeoData.emplace_back(Geometry{color, innerRadius, outerRadius, bounds, type});
+        fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
         fVertCount = rrect_type_to_vert_count(type);
         fIndexCount = rrect_type_to_index_count(type);
         fAllFill = (kFill_RRectType == type);
@@ -1774,33 +1786,27 @@
 
     SkString dumpInfo() const override {
         SkString string;
-        for (int i = 0; i < fGeoData.count(); ++i) {
+        for (int i = 0; i < fRRects.count(); ++i) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
                     "InnerRad: %.2f, OuterRad: %.2f\n",
-                    fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
-                    fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
-                    fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
+                    fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
+                    fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
+                    fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
         }
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fRRects.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
+                                            color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
-        if (!optimizations.readsLocalCoords()) {
-            fViewMatrixIfUsingLocalCoords.reset();
-        }
-    }
-
     struct CircleVertex {
         SkPoint fPos;
         GrColor fColor;
@@ -1887,7 +1893,6 @@
         sk_sp<GrGeometryProcessor> gp(
                 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
 
-        int instanceCount = fGeoData.count();
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(sizeof(CircleVertex) == vertexStride);
 
@@ -1910,13 +1915,10 @@
         }
 
         int currStartVertex = 0;
-        for (int i = 0; i < instanceCount; i++) {
-            const Geometry& args = fGeoData[i];
-
-            GrColor color = args.fColor;
-            SkScalar outerRadius = args.fOuterRadius;
-
-            const SkRect& bounds = args.fDevBounds;
+        for (const auto& rrect : fRRects) {
+            GrColor color = rrect.fColor;
+            SkScalar outerRadius = rrect.fOuterRadius;
+            const SkRect& bounds = rrect.fDevBounds;
 
             SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
                                    bounds.fBottom - outerRadius, bounds.fBottom};
@@ -1924,10 +1926,9 @@
             SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
             // The inner radius in the vertex data must be specified in normalized space.
             // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
-            SkScalar innerRadius =
-                    args.fType != kFill_RRectType && args.fType != kFillWithDist_RRectType
-                            ? args.fInnerRadius / args.fOuterRadius
-                            : -1.0f / args.fOuterRadius;
+            SkScalar innerRadius = rrect.fType != kFill_RRectType
+                                           ? rrect.fInnerRadius / rrect.fOuterRadius
+                                           : -1.0f / rrect.fOuterRadius;
             for (int i = 0; i < 4; ++i) {
                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
                 verts->fColor = color;
@@ -1965,40 +1966,31 @@
             //
             // Also, the outer offset is a constant vector pointing to the right, which
             // guarantees that the distance value along the outer rectangle is constant.
-            if (kOverstroke_RRectType == args.fType) {
-                SkASSERT(args.fInnerRadius <= 0.0f);
+            if (kOverstroke_RRectType == rrect.fType) {
+                SkASSERT(rrect.fInnerRadius <= 0.0f);
 
-                SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
+                SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
                 // this is the normalized distance from the outer rectangle of this
                 // geometry to the outer edge
-                SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
+                SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
 
                 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
-                                      overstrokeOuterRadius, 0.0f, color);
+                                      overstrokeOuterRadius, 0.0f, rrect.fColor);
             }
 
-            if (kFillWithDist_RRectType == args.fType) {
-                SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
-
-                SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
-
-                FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
-                                      -1.0f, color);
-            }
-
-            const uint16_t* primIndices = rrect_type_to_indices(args.fType);
-            const int primIndexCount = rrect_type_to_index_count(args.fType);
+            const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
+            const int primIndexCount = rrect_type_to_index_count(rrect.fType);
             for (int i = 0; i < primIndexCount; ++i) {
                 *indices++ = primIndices[i] + currStartVertex;
             }
 
-            currStartVertex += rrect_type_to_vert_count(args.fType);
+            currStartVertex += rrect_type_to_vert_count(rrect.fType);
         }
 
-        GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
-        target->draw(gp.get(), mesh);
+        GrMesh mesh(kTriangles_GrPrimitiveType);
+        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -2009,16 +2001,16 @@
             return false;
         }
 
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
-        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
+        if (fHelper.usesLocalCoords() &&
+            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
             return false;
         }
 
-        fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
+        fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
         this->joinBounds(*that);
         fVertCount += that->fVertCount;
         fIndexCount += that->fIndexCount;
@@ -2026,7 +2018,7 @@
         return true;
     }
 
-    struct Geometry {
+    struct RRect {
         GrColor fColor;
         SkScalar fInnerRadius;
         SkScalar fOuterRadius;
@@ -2034,11 +2026,12 @@
         RRectType fType;
     };
 
-    SkSTArray<1, Geometry, true> fGeoData;
     SkMatrix fViewMatrixIfUsingLocalCoords;
+    Helper fHelper;
     int fVertCount;
     int fIndexCount;
     bool fAllFill;
+    SkSTArray<1, RRect, true> fRRects;
 
     typedef GrMeshDrawOp INHERITED;
 };
@@ -2053,11 +2046,11 @@
     GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
     switch (type) {
         case kFill_RRectType:
-            return resourceProvider->findOrCreateInstancedIndexBuffer(
+            return resourceProvider->findOrCreatePatternedIndexBuffer(
                     gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
                     kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
         case kStroke_RRectType:
-            return resourceProvider->findOrCreateInstancedIndexBuffer(
+            return resourceProvider->findOrCreatePatternedIndexBuffer(
                     gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
                     kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
         default:
@@ -2067,23 +2060,21 @@
 }
 
 class EllipticalRRectOp : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
 public:
     DEFINE_OP_CLASS_ID
 
     // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
     // whether the rrect is only stroked or stroked and filled.
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              const SkRect& devRect, float devXRadius,
-                                              float devYRadius, SkVector devStrokeWidths,
-                                              bool strokeOnly) {
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& devRect, float devXRadius, float devYRadius,
+                                          SkVector devStrokeWidths, bool strokeOnly) {
         SkASSERT(devXRadius > 0.5);
         SkASSERT(devYRadius > 0.5);
         SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
         SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
-        SkScalar innerXRadius = 0.0f;
-        SkScalar innerYRadius = 0.0f;
-        SkRect bounds = devRect;
-        bool stroked = false;
         if (devStrokeWidths.fX > 0) {
             if (SkScalarNearlyZero(devStrokeWidths.length())) {
                 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
@@ -2107,28 +2098,40 @@
                 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
                 return nullptr;
             }
+        }
+        return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
+                                                        devXRadius, devYRadius, devStrokeWidths,
+                                                        strokeOnly);
+    }
 
+    EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
+                      const SkRect& devRect, float devXRadius, float devYRadius,
+                      SkVector devStrokeHalfWidths, bool strokeOnly)
+            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
+        SkScalar innerXRadius = 0.0f;
+        SkScalar innerYRadius = 0.0f;
+        SkRect bounds = devRect;
+        bool stroked = false;
+        if (devStrokeHalfWidths.fX > 0) {
             // this is legit only if scale & translation (which should be the case at the moment)
             if (strokeOnly) {
-                innerXRadius = devXRadius - devStrokeWidths.fX;
-                innerYRadius = devYRadius - devStrokeWidths.fY;
+                innerXRadius = devXRadius - devStrokeHalfWidths.fX;
+                innerYRadius = devYRadius - devStrokeHalfWidths.fY;
                 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
             }
 
-            devXRadius += devStrokeWidths.fX;
-            devYRadius += devStrokeWidths.fY;
-            bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
+            devXRadius += devStrokeHalfWidths.fX;
+            devYRadius += devStrokeHalfWidths.fY;
+            bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
         }
 
-        std::unique_ptr<EllipticalRRectOp> op(new EllipticalRRectOp());
-        op->fStroked = stroked;
-        op->fViewMatrixIfUsingLocalCoords = viewMatrix;
-        op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
+        fStroked = stroked;
+        fViewMatrixIfUsingLocalCoords = viewMatrix;
+        this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
         // Expand the rect for aa in order to generate the correct vertices.
         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
-        op->fGeoData.emplace_back(
-                Geometry{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
-        return std::move(op);
+        fRRects.emplace_back(
+                RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
     }
 
     const char* name() const override { return "EllipticalRRectOp"; }
@@ -2136,7 +2139,7 @@
     SkString dumpInfo() const override {
         SkString string;
         string.appendf("Stroked: %d\n", fStroked);
-        for (const auto& geo : fGeoData) {
+        for (const auto& geo : fRRects) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
                     "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
@@ -2144,27 +2147,19 @@
                     geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
                     geo.fInnerYRadius);
         }
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fRRects.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
+                                            color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
 private:
-    EllipticalRRectOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
-        if (!optimizations.readsLocalCoords()) {
-            fViewMatrixIfUsingLocalCoords.reset();
-        }
-    }
-
     void onPrepareDraws(Target* target) const override {
         SkMatrix localMatrix;
         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
@@ -2174,7 +2169,6 @@
         // Setup geometry processor
         sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
 
-        int instanceCount = fGeoData.count();
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(EllipseVertex));
 
@@ -2183,31 +2177,28 @@
         sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
                 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
 
-        InstancedHelper helper;
+        PatternHelper helper(kTriangles_GrPrimitiveType);
         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
-                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
-                            kVertsPerStandardRRect, indicesPerInstance, instanceCount));
+                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
+                            indicesPerInstance, fRRects.count()));
         if (!verts || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        for (int i = 0; i < instanceCount; i++) {
-            const Geometry& args = fGeoData[i];
-
-            GrColor color = args.fColor;
-
+        for (const auto& rrect : fRRects) {
+            GrColor color = rrect.fColor;
             // Compute the reciprocals of the radii here to save time in the shader
-            SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
-            SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
-            SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
-            SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
+            SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
+            SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
+            SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
+            SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
 
             // Extend the radii out half a pixel to antialias.
-            SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
-            SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
+            SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
+            SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
 
-            const SkRect& bounds = args.fDevBounds;
+            const SkRect& bounds = rrect.fDevBounds;
 
             SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
                                    bounds.fBottom - yOuterRadius, bounds.fBottom};
@@ -2246,14 +2237,13 @@
                 verts++;
             }
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
 
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
@@ -2261,16 +2251,17 @@
             return false;
         }
 
-        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
+        if (fHelper.usesLocalCoords() &&
+            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
             return false;
         }
 
-        fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
+        fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
         this->joinBounds(*that);
         return true;
     }
 
-    struct Geometry {
+    struct RRect {
         GrColor fColor;
         SkScalar fXRadius;
         SkScalar fYRadius;
@@ -2279,18 +2270,18 @@
         SkRect fDevBounds;
     };
 
-    bool fStroked;
     SkMatrix fViewMatrixIfUsingLocalCoords;
-    SkSTArray<1, Geometry, true> fGeoData;
+    Helper fHelper;
+    bool fStroked;
+    SkSTArray<1, RRect, true> fRRects;
 
     typedef GrMeshDrawOp INHERITED;
 };
 
-static std::unique_ptr<GrMeshDrawOp> make_rrect_op(GrColor color,
-                                                   bool needsDistance,
-                                                   const SkMatrix& viewMatrix,
-                                                   const SkRRect& rrect,
-                                                   const SkStrokeRec& stroke) {
+static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
+                                               const SkMatrix& viewMatrix,
+                                               const SkRRect& rrect,
+                                               const SkStrokeRec& stroke) {
     SkASSERT(viewMatrix.rectStaysRect());
     SkASSERT(rrect.isSimple());
     SkASSERT(!rrect.isOval());
@@ -2348,54 +2339,55 @@
 
     // if the corners are circles, use the circle renderer
     if (isCircular) {
-        return std::unique_ptr<GrMeshDrawOp>(new CircularRRectOp(
-                color, needsDistance, viewMatrix, bounds, xRadius, scaledStroke.fX, isStrokeOnly));
+        return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
+                                     isStrokeOnly);
         // otherwise we use the ellipse renderer
     } else {
-        return EllipticalRRectOp::Make(color, viewMatrix, bounds, xRadius, yRadius, scaledStroke,
-                                       isStrokeOnly);
+        return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
+                                       scaledStroke, isStrokeOnly);
     }
 }
 
-std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeRRectOp(GrColor color,
-                                                           bool needsDistance,
-                                                           const SkMatrix& viewMatrix,
-                                                           const SkRRect& rrect,
-                                                           const SkStrokeRec& stroke,
-                                                           const GrShaderCaps* shaderCaps) {
+std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
+                                                       const SkMatrix& viewMatrix,
+                                                       const SkRRect& rrect,
+                                                       const SkStrokeRec& stroke,
+                                                       const GrShaderCaps* shaderCaps) {
     if (rrect.isOval()) {
-        return MakeOvalOp(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
+        return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
     }
 
     if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
         return nullptr;
     }
 
-    return make_rrect_op(color, needsDistance, viewMatrix, rrect, stroke);
+    return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeOvalOp(GrColor color,
-                                                          const SkMatrix& viewMatrix,
-                                                          const SkRect& oval,
-                                                          const SkStrokeRec& stroke,
-                                                          const GrShaderCaps* shaderCaps) {
+std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
+                                                      const SkMatrix& viewMatrix,
+                                                      const SkRect& oval,
+                                                      const SkStrokeRec& stroke,
+                                                      const GrShaderCaps* shaderCaps) {
     // we can draw circles
     SkScalar width = oval.width();
-    if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
+    if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
+        circle_stays_circle(viewMatrix)) {
         SkPoint center = {oval.centerX(), oval.centerY()};
-        return CircleOp::Make(color, viewMatrix, center, width / 2.f, GrStyle(stroke, nullptr));
+        return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
+                              GrStyle(stroke, nullptr));
     }
 
     // prefer the device space ellipse op for batchability
     if (viewMatrix.rectStaysRect()) {
-        return EllipseOp::Make(color, viewMatrix, oval, stroke);
+        return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
     }
 
     // Otherwise, if we have shader derivative support, render as device-independent
     if (shaderCaps->shaderDerivativeSupport()) {
-        return DIEllipseOp::Make(color, viewMatrix, oval, stroke);
+        return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
     }
 
     return nullptr;
@@ -2403,11 +2395,11 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeArcOp(GrColor color, const SkMatrix& viewMatrix,
-                                                         const SkRect& oval, SkScalar startAngle,
-                                                         SkScalar sweepAngle, bool useCenter,
-                                                         const GrStyle& style,
-                                                         const GrShaderCaps* shaderCaps) {
+std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                                     const SkRect& oval, SkScalar startAngle,
+                                                     SkScalar sweepAngle, bool useCenter,
+                                                     const GrStyle& style,
+                                                     const GrShaderCaps* shaderCaps) {
     SkASSERT(!oval.isEmpty());
     SkASSERT(sweepAngle);
     SkScalar width = oval.width();
@@ -2420,14 +2412,14 @@
     SkPoint center = {oval.centerX(), oval.centerY()};
     CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
                                      useCenter};
-    return CircleOp::Make(color, viewMatrix, center, width / 2.f, style, &arcParams);
+    return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(CircleOp) {
+GR_DRAW_OP_TEST_DEFINE(CircleOp) {
     do {
         SkScalar rotate = random->nextSScalar1() * 360.f;
         SkScalar translateX = random->nextSScalar1() * 1000.f;
@@ -2437,7 +2429,6 @@
         viewMatrix.setRotate(rotate);
         viewMatrix.postTranslate(translateX, translateY);
         viewMatrix.postScale(scale, scale);
-        GrColor color = GrRandomColor(random);
         SkRect circle = GrTest::TestSquare(random);
         SkPoint center = {circle.centerX(), circle.centerY()};
         SkScalar radius = circle.width() / 2.f;
@@ -2450,34 +2441,30 @@
             arcParamsTmp.fUseCenter = random->nextBool();
             arcParams = &arcParamsTmp;
         }
-        std::unique_ptr<GrMeshDrawOp> op = CircleOp::Make(color, viewMatrix, center, radius,
-                                                          GrStyle(stroke, nullptr), arcParams);
+        std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
+                                                      GrStyle(stroke, nullptr), arcParams);
         if (op) {
             return op;
         }
     } while (true);
 }
 
-DRAW_OP_TEST_DEFINE(EllipseOp) {
+GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
-    GrColor color = GrRandomColor(random);
     SkRect ellipse = GrTest::TestSquare(random);
-    return EllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
+    return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
 }
 
-DRAW_OP_TEST_DEFINE(DIEllipseOp) {
+GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
-    GrColor color = GrRandomColor(random);
     SkRect ellipse = GrTest::TestSquare(random);
-    return DIEllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
+    return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
 }
 
-DRAW_OP_TEST_DEFINE(RRectOp) {
+GR_DRAW_OP_TEST_DEFINE(RRectOp) {
     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
-    GrColor color = GrRandomColor(random);
     const SkRRect& rrect = GrTest::TestRRectSimple(random);
-    bool needsDistance = random->nextBool();
-    return make_rrect_op(color, needsDistance, viewMatrix, rrect, GrTest::TestStrokeRec(random));
+    return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
 }
 
 #endif
diff --git a/src/gpu/ops/GrOvalOpFactory.h b/src/gpu/ops/GrOvalOpFactory.h
index e6a160d..4c85ee8 100644
--- a/src/gpu/ops/GrOvalOpFactory.h
+++ b/src/gpu/ops/GrOvalOpFactory.h
@@ -11,7 +11,9 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrDrawOp;
+class GrLegacyMeshDrawOp;
+class GrPaint;
 class GrShaderCaps;
 class GrStyle;
 class SkMatrix;
@@ -24,26 +26,26 @@
  */
 class GrOvalOpFactory {
 public:
-    static std::unique_ptr<GrMeshDrawOp> MakeOvalOp(GrColor,
-                                                    const SkMatrix& viewMatrix,
-                                                    const SkRect& oval,
-                                                    const SkStrokeRec& stroke,
-                                                    const GrShaderCaps* shaderCaps);
-    static std::unique_ptr<GrMeshDrawOp> MakeRRectOp(GrColor,
-                                                     bool needsDistance,
-                                                     const SkMatrix& viewMatrix,
-                                                     const SkRRect& rrect,
-                                                     const SkStrokeRec& stroke,
-                                                     const GrShaderCaps* shaderCaps);
+    static std::unique_ptr<GrDrawOp> MakeOvalOp(GrPaint&&,
+                                                const SkMatrix&,
+                                                const SkRect& oval,
+                                                const SkStrokeRec&,
+                                                const GrShaderCaps*);
 
-    static std::unique_ptr<GrMeshDrawOp> MakeArcOp(GrColor,
-                                                   const SkMatrix& viewMatrix,
-                                                   const SkRect& oval,
-                                                   SkScalar startAngle,
-                                                   SkScalar sweepAngle,
-                                                   bool useCenter,
-                                                   const GrStyle&,
-                                                   const GrShaderCaps* shaderCaps);
+    static std::unique_ptr<GrDrawOp> MakeRRectOp(GrPaint&&,
+                                                 const SkMatrix&,
+                                                 const SkRRect&,
+                                                 const SkStrokeRec&,
+                                                 const GrShaderCaps*);
+
+    static std::unique_ptr<GrDrawOp> MakeArcOp(GrPaint&&,
+                                               const SkMatrix&,
+                                               const SkRect& oval,
+                                               SkScalar startAngle,
+                                               SkScalar sweepAngle,
+                                               bool useCenter,
+                                               const GrStyle&,
+                                               const GrShaderCaps*);
 };
 
 #endif  // GrOvalOpFactory_DEFINED
diff --git a/src/gpu/ops/GrPathStencilSettings.h b/src/gpu/ops/GrPathStencilSettings.h
index f37d1b2..10e6ddd 100644
--- a/src/gpu/ops/GrPathStencilSettings.h
+++ b/src/gpu/ops/GrPathStencilSettings.h
@@ -49,11 +49,7 @@
 
 ////// Winding
 
-// when we have separate stencil we increment front faces / decrement back faces
-// when we don't have wrap incr and decr we use the stencil test to simulate
-// them.
-
-static constexpr GrUserStencilSettings gWindStencilSeparateWithWrap(
+static constexpr GrUserStencilSettings gWindStencilPass (
     GrUserStencilSettings::StaticInitSeparate<
         0xffff,                                0xffff,
         GrUserStencilTest::kAlwaysIfInClip,    GrUserStencilTest::kAlwaysIfInClip,
@@ -63,66 +59,6 @@
         0xffff,                                0xffff>()
 );
 
-// if inc'ing the max value, invert to make 0
-// if dec'ing zero invert to make all ones.
-// we can't avoid touching the stencil on both passing and
-// failing, so we can't resctrict ourselves to the clip.
-static constexpr GrUserStencilSettings gWindStencilSeparateNoWrap(
-    GrUserStencilSettings::StaticInitSeparate<
-        0xffff,                                0x0000,
-        GrUserStencilTest::kEqual,             GrUserStencilTest::kEqual,
-        0xffff,                                0xffff,
-        GrUserStencilOp::kInvert,              GrUserStencilOp::kInvert,
-        GrUserStencilOp::kIncMaybeClamp,       GrUserStencilOp::kDecMaybeClamp,
-        0xffff,                                0xffff>()
-);
-
-// When there are no separate faces we do two passes to setup the winding rule
-// stencil. First we draw the front faces and inc, then we draw the back faces
-// and dec. These are same as the above two split into the incrementing and
-// decrementing passes.
-static constexpr GrUserStencilSettings gWindSingleStencilWithWrapInc(
-    GrUserStencilSettings::StaticInit<
-        0xffff,
-        GrUserStencilTest::kAlwaysIfInClip,
-        0xffff,
-        GrUserStencilOp::kIncWrap,
-        GrUserStencilOp::kKeep,
-        0xffff>()
-);
-
-static constexpr GrUserStencilSettings gWindSingleStencilWithWrapDec(
-    GrUserStencilSettings::StaticInit<
-        0xffff,
-        GrUserStencilTest::kAlwaysIfInClip,
-        0xffff,
-        GrUserStencilOp::kDecWrap,
-        GrUserStencilOp::kKeep,
-        0xffff>()
-);
-
-static constexpr GrUserStencilSettings gWindSingleStencilNoWrapInc(
-    GrUserStencilSettings::StaticInit<
-        0xffff,
-        GrUserStencilTest::kEqual,
-        0xffff,
-        GrUserStencilOp::kInvert,
-        GrUserStencilOp::kIncMaybeClamp,
-        0xffff>()
-);
-
-static constexpr GrUserStencilSettings gWindSingleStencilNoWrapDec(
-    GrUserStencilSettings::StaticInit<
-        0x0000,
-        GrUserStencilTest::kEqual,
-        0xffff,
-        GrUserStencilOp::kInvert,
-        GrUserStencilOp::kDecMaybeClamp,
-        0xffff>()
-);
-
-// Color passes are the same whether we use the two-sided stencil or two passes
-
 static constexpr GrUserStencilSettings gWindColorPass(
     GrUserStencilSettings::StaticInit<
         0x0000,
diff --git a/src/gpu/ops/GrRectOpFactory.cpp b/src/gpu/ops/GrRectOpFactory.cpp
index e6d0c1f..45e2053 100644
--- a/src/gpu/ops/GrRectOpFactory.cpp
+++ b/src/gpu/ops/GrRectOpFactory.cpp
@@ -13,9 +13,9 @@
 
 namespace GrRectOpFactory {
 
-std::unique_ptr<GrMeshDrawOp> MakeAAFillNestedRects(GrColor color,
-                                                    const SkMatrix& viewMatrix,
-                                                    const SkRect rects[2]) {
+std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFillNestedRects(GrColor color,
+                                                          const SkMatrix& viewMatrix,
+                                                          const SkRect rects[2]) {
     SkASSERT(viewMatrix.rectStaysRect());
     SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
 
diff --git a/src/gpu/ops/GrRectOpFactory.h b/src/gpu/ops/GrRectOpFactory.h
index 19bce7f..58927ef 100644
--- a/src/gpu/ops/GrRectOpFactory.h
+++ b/src/gpu/ops/GrRectOpFactory.h
@@ -13,7 +13,6 @@
 #include "GrAnalyticRectOp.h"
 #include "GrColor.h"
 #include "GrMeshDrawOp.h"
-#include "GrNonAAFillRectOp.h"
 #include "GrNonAAStrokeRectOp.h"
 #include "GrPaint.h"
 #include "SkMatrix.h"
@@ -27,57 +26,40 @@
  */
 namespace GrRectOpFactory {
 
-inline std::unique_ptr<GrMeshDrawOp> MakeNonAAFill(GrColor color,
-                                                   const SkMatrix& viewMatrix,
-                                                   const SkRect& rect,
-                                                   const SkRect* localRect,
-                                                   const SkMatrix* localMatrix) {
-    if (viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())) {
-        return GrNonAAFillRectOp::MakeWithPerspective(color, viewMatrix, rect, localRect,
-                                                      localMatrix);
-    } else {
-        return GrNonAAFillRectOp::Make(color, viewMatrix, rect, localRect, localMatrix);
-    }
+inline std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFill(const GrPaint& paint,
+                                                      const SkMatrix& viewMatrix,
+                                                      const SkRect& rect,
+                                                      const SkRect& croppedRect,
+                                                      const SkRect& devRect) {
+    return GrAAFillRectOp::Make(paint.getColor(), viewMatrix, croppedRect, devRect);
 }
 
-inline std::unique_ptr<GrMeshDrawOp> MakeAAFill(const GrPaint& paint,
-                                                const SkMatrix& viewMatrix,
-                                                const SkRect& rect,
-                                                const SkRect& croppedRect,
-                                                const SkRect& devRect) {
-    if (!paint.usesDistanceVectorField()) {
-        return GrAAFillRectOp::Make(paint.getColor(), viewMatrix, croppedRect, devRect);
-    } else {
-        return GrAnalyticRectOp::Make(paint.getColor(), viewMatrix, rect, croppedRect, devRect);
-    }
-}
-
-inline std::unique_ptr<GrMeshDrawOp> MakeAAFill(GrColor color,
-                                                const SkMatrix& viewMatrix,
-                                                const SkMatrix& localMatrix,
-                                                const SkRect& rect,
-                                                const SkRect& devRect) {
+inline std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFill(GrColor color,
+                                                      const SkMatrix& viewMatrix,
+                                                      const SkMatrix& localMatrix,
+                                                      const SkRect& rect,
+                                                      const SkRect& devRect) {
     return GrAAFillRectOp::Make(color, viewMatrix, localMatrix, rect, devRect);
 }
 
-inline std::unique_ptr<GrMeshDrawOp> MakeNonAAStroke(GrColor color,
-                                                     const SkMatrix& viewMatrix,
-                                                     const SkRect& rect,
-                                                     const SkStrokeRec& strokeRec,
-                                                     bool snapToPixelCenters) {
+inline std::unique_ptr<GrLegacyMeshDrawOp> MakeNonAAStroke(GrColor color,
+                                                           const SkMatrix& viewMatrix,
+                                                           const SkRect& rect,
+                                                           const SkStrokeRec& strokeRec,
+                                                           bool snapToPixelCenters) {
     return GrNonAAStrokeRectOp::Make(color, viewMatrix, rect, strokeRec, snapToPixelCenters);
 }
 
-inline std::unique_ptr<GrMeshDrawOp> MakeAAStroke(GrColor color,
-                                                  const SkMatrix& viewMatrix,
-                                                  const SkRect& rect,
-                                                  const SkStrokeRec& stroke) {
+inline std::unique_ptr<GrLegacyMeshDrawOp> MakeAAStroke(GrColor color,
+                                                        const SkMatrix& viewMatrix,
+                                                        const SkRect& rect,
+                                                        const SkStrokeRec& stroke) {
     return GrAAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
 }
 
 // First rect is outer; second rect is inner
-std::unique_ptr<GrMeshDrawOp> MakeAAFillNestedRects(GrColor, const SkMatrix& viewMatrix,
-                                                    const SkRect rects[2]);
+std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFillNestedRects(GrColor, const SkMatrix& viewMatrix,
+                                                          const SkRect rects[2]);
 };
 
 #endif
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 0ebec98..62d7362 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -47,7 +47,7 @@
     }
 }
 
-class RegionOp final : public GrMeshDrawOp {
+class RegionOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -77,13 +77,13 @@
     }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fRegions[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kNone;
+        *coverage = GrProcessorAnalysisCoverage::kNone;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fRegions[0].fColor);
     }
 
@@ -103,10 +103,10 @@
 
         size_t vertexStride = gp->getVertexStride();
         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
-        InstancedHelper helper;
+        PatternHelper helper(kTriangles_GrPrimitiveType);
         void* vertices =
-                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
-                            kVertsPerInstance, kIndicesPerInstance, numRects);
+                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerInstance,
+                            kIndicesPerInstance, numRects);
         if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -118,7 +118,7 @@
             int numRectsInRegion = fRegions[i].fRegion.computeRegionComplexity();
             verts += numRectsInRegion * kVertsPerInstance * vertexStride;
         }
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -145,13 +145,13 @@
     SkMatrix fViewMatrix;
     SkSTArray<1, RegionInfo, true> fRegions;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 namespace GrRegionOp {
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                   const SkRegion& region) {
-    return std::unique_ptr<GrMeshDrawOp>(new RegionOp(color, viewMatrix, region));
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
+                                         const SkRegion& region) {
+    return std::unique_ptr<GrLegacyMeshDrawOp>(new RegionOp(color, viewMatrix, region));
 }
 }
diff --git a/src/gpu/ops/GrRegionOp.h b/src/gpu/ops/GrRegionOp.h
index 5ee6f35..2170667 100644
--- a/src/gpu/ops/GrRegionOp.h
+++ b/src/gpu/ops/GrRegionOp.h
@@ -11,13 +11,13 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class SkMatrix;
 class SkRegion;
 
 namespace GrRegionOp {
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                   const SkRegion& region);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
+                                         const SkRegion& region);
 }
 
 #endif
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index bc1a4cc..af41bd4 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -15,7 +15,8 @@
 #include "effects/GrShadowGeoProc.h"
 
 ///////////////////////////////////////////////////////////////////////////////
-
+// Circle Data
+//
 // We have two possible cases for geometry for a circle:
 
 // In the case of a normal fill, we draw geometry for the circle as an octagon.
@@ -62,340 +63,9 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-
-class ShadowCircleOp final : public GrMeshDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                              SkPoint center, SkScalar radius, SkScalar blurRadius,
-                                              const GrStyle& style) {
-        SkASSERT(viewMatrix.isSimilarity());
-        const SkStrokeRec& stroke = style.strokeRec();
-        if (style.hasPathEffect()) {
-            return nullptr;
-        }
-        SkStrokeRec::Style recStyle = stroke.getStyle();
-
-        viewMatrix.mapPoints(&center, 1);
-        radius = viewMatrix.mapRadius(radius);
-        SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
-
-        bool isStrokeOnly =
-                SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
-        bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
-
-        SkScalar innerRadius = -SK_ScalarHalf;
-        SkScalar outerRadius = radius;
-        SkScalar halfWidth = 0;
-        if (hasStroke) {
-            if (SkScalarNearlyZero(strokeWidth)) {
-                halfWidth = SK_ScalarHalf;
-            } else {
-                halfWidth = SkScalarHalf(strokeWidth);
-            }
-
-            outerRadius += halfWidth;
-            if (isStrokeOnly) {
-                innerRadius = radius - halfWidth;
-            }
-        }
-
-        bool stroked = isStrokeOnly && innerRadius > 0.0f;
-        std::unique_ptr<ShadowCircleOp> op(new ShadowCircleOp());
-        op->fViewMatrixIfUsingLocalCoords = viewMatrix;
-
-        SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
-                                            center.fX + outerRadius, center.fY + outerRadius);
-
-        op->fCircles.emplace_back(
-                Circle{color, outerRadius, innerRadius, blurRadius, devBounds, stroked});
-
-        // Use the original radius and stroke radius for the bounds so that it does not include the
-        // AA bloat.
-        radius += halfWidth;
-        op->setBounds(
-                {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
-                HasAABloat::kNo, IsZeroArea::kNo);
-        op->fVertCount = circle_type_to_vert_count(stroked);
-        op->fIndexCount = circle_type_to_index_count(stroked);
-        return std::move(op);
-    }
-
-    const char* name() const override { return "ShadowCircleOp"; }
-
-    SkString dumpInfo() const override {
-        SkString string;
-        for (int i = 0; i < fCircles.count(); ++i) {
-            string.appendf(
-                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
-                    "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
-                    fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
-                    fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
-                    fCircles[i].fOuterRadius, fCircles[i].fInnerRadius, fCircles[i].fBlurRadius);
-        }
-        string.append(DumpPipelineInfo(*this->pipeline()));
-        string.append(INHERITED::dumpInfo());
-        return string;
-    }
-
-private:
-    ShadowCircleOp() : INHERITED(ClassID()) {}
-
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToConstant(fCircles[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fCircles[0].fColor);
-        if (!optimizations.readsLocalCoords()) {
-            fViewMatrixIfUsingLocalCoords.reset();
-        }
-    }
-
-    void onPrepareDraws(Target* target) const override {
-        SkMatrix localMatrix;
-        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
-            return;
-        }
-
-        // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
-
-        struct CircleVertex {
-            SkPoint fPos;
-            GrColor fColor;
-            SkPoint fOffset;
-            SkScalar fOuterRadius;
-            SkScalar fBlurRadius;
-        };
-
-        int instanceCount = fCircles.count();
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(CircleVertex));
-
-        const GrBuffer* vertexBuffer;
-        int firstVertex;
-        char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
-                                                        &firstVertex);
-        if (!vertices) {
-            SkDebugf("Could not allocate vertices\n");
-            return;
-        }
-
-        const GrBuffer* indexBuffer = nullptr;
-        int firstIndex = 0;
-        uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
-        if (!indices) {
-            SkDebugf("Could not allocate indices\n");
-            return;
-        }
-
-        int currStartVertex = 0;
-        for (int i = 0; i < instanceCount; i++) {
-            const Circle& circle = fCircles[i];
-
-            GrColor color = circle.fColor;
-            SkScalar outerRadius = circle.fOuterRadius;
-            SkScalar innerRadius = circle.fInnerRadius;
-            SkScalar blurRadius = circle.fBlurRadius;
-
-            const SkRect& bounds = circle.fDevBounds;
-            CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
-            CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
-            CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
-            CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
-            CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
-            CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
-            CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
-            CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
-
-            // The inner radius in the vertex data must be specified in normalized space.
-            innerRadius = innerRadius / outerRadius;
-
-            SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
-            SkScalar halfWidth = 0.5f * bounds.width();
-            SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
-
-            ov0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
-            ov0->fColor = color;
-            ov0->fOffset = SkPoint::Make(-octOffset, -1);
-            ov0->fOuterRadius = outerRadius;
-            ov0->fBlurRadius = blurRadius;
-
-            ov1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
-            ov1->fColor = color;
-            ov1->fOffset = SkPoint::Make(octOffset, -1);
-            ov1->fOuterRadius = outerRadius;
-            ov1->fBlurRadius = blurRadius;
-
-            ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
-            ov2->fColor = color;
-            ov2->fOffset = SkPoint::Make(1, -octOffset);
-            ov2->fOuterRadius = outerRadius;
-            ov2->fBlurRadius = blurRadius;
-
-            ov3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
-            ov3->fColor = color;
-            ov3->fOffset = SkPoint::Make(1, octOffset);
-            ov3->fOuterRadius = outerRadius;
-            ov3->fBlurRadius = blurRadius;
-
-            ov4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
-            ov4->fColor = color;
-            ov4->fOffset = SkPoint::Make(octOffset, 1);
-            ov4->fOuterRadius = outerRadius;
-            ov4->fBlurRadius = blurRadius;
-
-            ov5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
-            ov5->fColor = color;
-            ov5->fOffset = SkPoint::Make(-octOffset, 1);
-            ov5->fOuterRadius = outerRadius;
-            ov5->fBlurRadius = blurRadius;
-
-            ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
-            ov6->fColor = color;
-            ov6->fOffset = SkPoint::Make(-1, octOffset);
-            ov6->fOuterRadius = outerRadius;
-            ov6->fBlurRadius = blurRadius;
-
-            ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
-            ov7->fColor = color;
-            ov7->fOffset = SkPoint::Make(-1, -octOffset);
-            ov7->fOuterRadius = outerRadius;
-            ov7->fBlurRadius = blurRadius;
-
-            if (circle.fStroked) {
-                // compute the inner ring
-                CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
-                CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
-                CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
-                CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
-                CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
-                CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
-                CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
-                CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
-
-                // cosine and sine of pi/8
-                SkScalar c = 0.923579533f;
-                SkScalar s = 0.382683432f;
-                SkScalar r = circle.fInnerRadius;
-
-                iv0->fPos = center + SkPoint::Make(-s * r, -c * r);
-                iv0->fColor = color;
-                iv0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
-                iv0->fOuterRadius = outerRadius;
-                iv0->fBlurRadius = blurRadius;
-
-                iv1->fPos = center + SkPoint::Make(s * r, -c * r);
-                iv1->fColor = color;
-                iv1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
-                iv1->fOuterRadius = outerRadius;
-                iv1->fBlurRadius = blurRadius;
-
-                iv2->fPos = center + SkPoint::Make(c * r, -s * r);
-                iv2->fColor = color;
-                iv2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
-                iv2->fOuterRadius = outerRadius;
-                iv2->fBlurRadius = blurRadius;
-
-                iv3->fPos = center + SkPoint::Make(c * r, s * r);
-                iv3->fColor = color;
-                iv3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
-                iv3->fOuterRadius = outerRadius;
-                iv3->fBlurRadius = blurRadius;
-
-                iv4->fPos = center + SkPoint::Make(s * r, c * r);
-                iv4->fColor = color;
-                iv4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
-                iv4->fOuterRadius = outerRadius;
-                iv4->fBlurRadius = blurRadius;
-
-                iv5->fPos = center + SkPoint::Make(-s * r, c * r);
-                iv5->fColor = color;
-                iv5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
-                iv5->fOuterRadius = outerRadius;
-                iv5->fBlurRadius = blurRadius;
-
-                iv6->fPos = center + SkPoint::Make(-c * r, s * r);
-                iv6->fColor = color;
-                iv6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
-                iv6->fOuterRadius = outerRadius;
-                iv6->fBlurRadius = blurRadius;
-
-                iv7->fPos = center + SkPoint::Make(-c * r, -s * r);
-                iv7->fColor = color;
-                iv7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
-                iv7->fOuterRadius = outerRadius;
-                iv7->fBlurRadius = blurRadius;
-            } else {
-                // filled
-                CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
-                iv->fPos = center;
-                iv->fColor = color;
-                iv->fOffset = SkPoint::Make(0, 0);
-                iv->fOuterRadius = outerRadius;
-                iv->fBlurRadius = blurRadius;
-            }
-
-            const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
-            const int primIndexCount = circle_type_to_index_count(circle.fStroked);
-            for (int i = 0; i < primIndexCount; ++i) {
-                *indices++ = primIndices[i] + currStartVertex;
-            }
-
-            currStartVertex += circle_type_to_vert_count(circle.fStroked);
-            vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
-        }
-
-        GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
-        target->draw(gp.get(), mesh);
-    }
-
-    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
-        ShadowCircleOp* that = t->cast<ShadowCircleOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
-            return false;
-        }
-
-        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
-            return false;
-        }
-
-        fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
-        this->joinBounds(*that);
-        fVertCount += that->fVertCount;
-        fIndexCount += that->fIndexCount;
-        return true;
-    }
-
-    struct Circle {
-        GrColor fColor;
-        SkScalar fOuterRadius;
-        SkScalar fInnerRadius;
-        SkScalar fBlurRadius;
-        SkRect fDevBounds;
-        bool fStroked;
-    };
-
-    SkSTArray<1, Circle, true> fCircles;
-    SkMatrix fViewMatrixIfUsingLocalCoords;
-    int fVertCount;
-    int fIndexCount;
-
-    typedef GrMeshDrawOp INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-// We have two possible cases for geometry for a shadow roundrect.
+// RoundRect Data
 //
-// In the case of a normal stroke, we draw the roundrect as a 9-patch without the center quad.
+// The geometry for a shadow roundrect is similar to a 9-patch:
 //    ____________
 //   |_|________|_|
 //   | |        | |
@@ -404,9 +74,23 @@
 //   |_|________|_|
 //   |_|________|_|
 //
-// In the case where the stroke width is greater than twice the corner radius (overstroke),
-// we add additional geometry to mark out the rectangle in the center. The shared vertices
-// are duplicated so we can set a different outer radius for the fill calculation.
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+//    ________
+//   |\  \   |
+//   |  \ \  |
+//   |    \\ |
+//   |      \|
+//   --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
 //    ____________
 //   |_|________|_|
 //   | |\ ____ /| |
@@ -415,40 +99,44 @@
 //   |_|/______\|_|
 //   |_|________|_|
 //
-// For filled rrects we reuse the overstroke geometry but make the inner rect degenerate
-// (either a point or a horizontal or vertical line).
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
 
-static const uint16_t gOverstrokeRRectIndices[] = {
-        // clang-format off
-        // corners
-        0, 1, 5, 0, 5, 4,
-        2, 3, 7, 2, 7, 6,
-        8, 9, 13, 8, 13, 12,
-        10, 11, 15, 10, 15, 14,
+static const uint16_t gRRectIndices[] = {
+    // clang-format off
+    // overstroke quads
+    // we place this at the beginning so that we can skip these indices when rendering as filled
+    0, 6, 25, 0, 25, 24,
+    6, 18, 27, 6, 27, 25,
+    18, 12, 26, 18, 26, 27,
+    12, 0, 24, 12, 24, 26,
 
-        // edges
-        1, 2, 6, 1, 6, 5,
-        4, 5, 9, 4, 9, 8,
-        6, 7, 11, 6, 11, 10,
-        9, 10, 14, 9, 14, 13,
+    // corners
+    0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+    6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+    12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+    18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
 
-        // overstroke quads
-        // we place this at the end so that we can skip these indices when rendering as stroked
-        16, 17, 19, 16, 19, 18,
-        19, 17, 23, 19, 23, 21,
-        21, 23, 22, 21, 22, 20,
-        22, 16, 18, 22, 18, 20,
-        // clang-format on
+    // edges
+    0, 5, 11, 0, 11, 6,
+    6, 7, 19, 6, 19, 18,
+    18, 23, 17, 18, 17, 12,
+    12, 13, 1, 12, 1, 0,
+
+    // fill quad
+    // we place this at the end so that we can skip these indices when rendering as stroked
+    0, 6, 18, 0, 18, 12,
+    // clang-format on
 };
-// standard stroke indices start at the same place, but will skip the overstroke "ring"
-static const uint16_t* gStrokeRRectIndices = gOverstrokeRRectIndices;
 
 // overstroke count
-static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices);
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
 // simple stroke count skips overstroke indices
-static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
-static const int kVertsPerStrokeRRect = 16;
-static const int kVertsPerOverstrokeRRect = 24;
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
 
 enum RRectType {
     kFill_RRectType,
@@ -459,7 +147,7 @@
 static int rrect_type_to_vert_count(RRectType type) {
     switch (type) {
         case kFill_RRectType:
-            return kVertsPerOverstrokeRRect;
+            return kVertsPerFillRRect;
         case kStroke_RRectType:
             return kVertsPerStrokeRRect;
         case kOverstroke_RRectType:
@@ -472,7 +160,7 @@
 static int rrect_type_to_index_count(RRectType type) {
     switch (type) {
         case kFill_RRectType:
-            return kIndicesPerOverstrokeRRect;
+            return kIndicesPerFillRRect;
         case kStroke_RRectType:
             return kIndicesPerStrokeRRect;
         case kOverstroke_RRectType:
@@ -485,64 +173,68 @@
 static const uint16_t* rrect_type_to_indices(RRectType type) {
     switch (type) {
         case kFill_RRectType:
-            return gOverstrokeRRectIndices;
         case kStroke_RRectType:
-            return gStrokeRRectIndices;
+            return gRRectIndices + 6*4;
         case kOverstroke_RRectType:
-            return gOverstrokeRRectIndices;
+            return gRRectIndices;
     }
     SkFAIL("Invalid type");
     return nullptr;
 }
 
-// For distance computations in the interior of filled rrects we:
-//
-//   add a interior degenerate (point or line) rect
-//   each vertex of that rect gets -outerRad as its radius
-//      this makes the computation of the distance to the outer edge be negative
-//      negative values are caught and then handled differently in the GP's onEmitCode
-//   each vertex is also given the normalized x & y distance from the interior rect's edge
-//      the GP takes the min of those depths +1 to get the normalized distance to the outer edge
+///////////////////////////////////////////////////////////////////////////////
 
-class ShadowCircularRRectOp final : public GrMeshDrawOp {
+class ShadowCircularRRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
-    // whether the rrect is only stroked or stroked and filled.
+    // An insetWidth > 1/2 rect width or height indicates a simple fill.
     ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
-                          float devRadius, float blurRadius, float devStrokeWidth, bool strokeOnly)
+                          float devRadius, bool isCircle, float blurRadius, float insetWidth,
+                          float blurClamp)
             : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
         SkRect bounds = devRect;
-        SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
+        SkASSERT(insetWidth > 0);
         SkScalar innerRadius = 0.0f;
         SkScalar outerRadius = devRadius;
-        SkScalar halfWidth = 0;
-        RRectType type = kFill_RRectType;
-        if (devStrokeWidth > 0) {
-            if (SkScalarNearlyZero(devStrokeWidth)) {
-                halfWidth = SK_ScalarHalf;
-            } else {
-                halfWidth = SkScalarHalf(devStrokeWidth);
-            }
+        SkScalar umbraInset;
 
-            if (strokeOnly) {
-                // If stroke is greater than width or height, this is still a fill
-                // Otherwise we compute stroke params
-                if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
-                    innerRadius = devRadius - halfWidth;
-                    type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
-                }
+        RRectType type = kFill_RRectType;
+        if (isCircle) {
+            umbraInset = 0;
+        } else if (insetWidth > 0 && insetWidth <= outerRadius) {
+            // If the client has requested a stroke smaller than the outer radius,
+            // we will assume they want no special umbra inset (this is for ambient shadows).
+            umbraInset = outerRadius;
+        } else {
+            umbraInset = SkTMax(outerRadius, blurRadius);
+        }
+
+        // If stroke is greater than width or height, this is still a fill,
+        // otherwise we compute stroke params.
+        if (isCircle) {
+            innerRadius = devRadius - insetWidth;
+            type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+        } else {
+            if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) {
+                // We don't worry about a real inner radius, we just need to know if we
+                // need to create overstroke vertices.
+                innerRadius = SkTMax(insetWidth - umbraInset, 0.0f);
+                type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
             }
-            outerRadius += halfWidth;
-            bounds.outset(halfWidth, halfWidth);
         }
 
         this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
 
-        fGeoData.emplace_back(Geometry{color, outerRadius, innerRadius, blurRadius, bounds, type});
-        fVertCount = rrect_type_to_vert_count(type);
-        fIndexCount = rrect_type_to_index_count(type);
+        fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
+                                       blurRadius, blurClamp, bounds, type, isCircle});
+        if (isCircle) {
+            fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
+            fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
+        } else {
+            fVertCount = rrect_type_to_vert_count(type);
+            fIndexCount = rrect_type_to_index_count(type);
+        }
     }
 
     const char* name() const override { return "ShadowCircularRRectOp"; }
@@ -552,10 +244,11 @@
         for (int i = 0; i < fGeoData.count(); ++i) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
-                    "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
+                    "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
                     fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
                     fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
-                    fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
+                    fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
+                    fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
         }
         string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
@@ -563,91 +256,316 @@
     }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fGeoData[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
         if (!optimizations.readsLocalCoords()) {
             fViewMatrixIfUsingLocalCoords.reset();
         }
     }
 
+    struct Geometry {
+        GrColor   fColor;
+        SkScalar  fOuterRadius;
+        SkScalar  fUmbraInset;
+        SkScalar  fInnerRadius;
+        SkScalar  fBlurRadius;
+        SkScalar  fClampValue;
+        SkRect    fDevBounds;
+        RRectType fType;
+        bool      fIsCircle;
+    };
+
     struct CircleVertex {
         SkPoint fPos;
         GrColor fColor;
         SkPoint fOffset;
-        SkScalar fOuterRadius;
-        SkScalar fBlurRadius;
+        SkScalar fDistanceCorrection;
+        SkScalar fClampValue;
     };
 
-    static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
-                                      SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
-                                      GrColor color, SkScalar blurRadius) {
-        SkASSERT(smInset < bigInset);
+    void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
 
-        // TL
-        (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
+        GrColor color = args.fColor;
+        SkScalar outerRadius = args.fOuterRadius;
+        SkScalar innerRadius = args.fInnerRadius;
+        SkScalar blurRadius = args.fBlurRadius;
+        SkScalar distanceCorrection = outerRadius / blurRadius;
+        SkScalar clampValue = args.fClampValue;
+
+        const SkRect& bounds = args.fDevBounds;
+
+        // The inner radius in the vertex data must be specified in normalized space.
+        innerRadius = innerRadius / outerRadius;
+
+        SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
+        SkScalar halfWidth = 0.5f * bounds.width();
+        SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
+
+        (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        // TR
-        (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
+        (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(octOffset, -1);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
+        (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(1, -octOffset);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
+        (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(1, octOffset);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
+        (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(octOffset, 1);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
+        (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        // BL
-        (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
+        (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(-1, octOffset);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
 
-        // BR
-        (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
+        (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
-        (*verts)->fOuterRadius = outerRadius;
-        (*verts)->fBlurRadius = blurRadius;
+        (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)->fClampValue = clampValue;
         (*verts)++;
+
+        if (isStroked) {
+            // compute the inner ring
+
+            // cosine and sine of pi/8
+            SkScalar c = 0.923579533f;
+            SkScalar s = 0.382683432f;
+            SkScalar r = args.fInnerRadius;
+
+            (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+        } else {
+            // filled
+            (*verts)->fPos = center;
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+        }
+    }
+
+    void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
+        GrColor color = args.fColor;
+        SkScalar outerRadius = args.fOuterRadius;
+
+        const SkRect& bounds = args.fDevBounds;
+
+        SkScalar umbraInset = args.fUmbraInset;
+        SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
+        if (umbraInset > minDim) {
+            umbraInset = minDim;
+        }
+
+        SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
+            bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
+        SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
+            bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
+        SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
+            bounds.fLeft, bounds.fRight };
+        SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
+            bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
+        SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
+            bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
+        SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
+            bounds.fBottom, bounds.fBottom };
+
+        SkScalar blurRadius = args.fBlurRadius;
+
+        // In the case where we have to inset more for the umbra, our two triangles in the
+        // corner get skewed to a diamond rather than a square. To correct for that,
+        // we also skew the vectors we send to the shader that help define the circle.
+        // By doing so, we end up with a quarter circle in the corner rather than the
+        // elliptical curve.
+        SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset);
+        outerVec.normalize();
+        SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY,
+                                          outerVec.fX + outerVec.fY);
+        diagVec *= umbraInset / (2 * umbraInset - outerRadius);
+        SkScalar distanceCorrection = umbraInset / blurRadius;
+        SkScalar clampValue = args.fClampValue;
+
+        // build corner by corner
+        for (int i = 0; i < 4; ++i) {
+            // inner point
+            (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkVector::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            // outer points
+            (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkVector::Make(0, -1);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = outerVec;
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = diagVec;
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = outerVec;
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkVector::Make(0, -1);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+        }
+
+        // Add the additional vertices for overstroked rrects.
+        // Effectively this is an additional stroked rrect, with its
+        // parameters equal to those in the center of the 9-patch. This will
+        // give constant values across this inner ring.
+        if (kOverstroke_RRectType == args.fType) {
+            SkASSERT(args.fInnerRadius > 0.0f);
+
+            SkScalar inset =  umbraInset + args.fInnerRadius;
+
+            // TL
+            (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            // TR
+            (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            // BL
+            (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+
+            // BR
+            (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)->fClampValue = clampValue;
+            (*verts)++;
+        }
+
     }
 
     void onPrepareDraws(Target* target) const override {
@@ -666,7 +584,6 @@
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
-
         CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
                                                                      &vertexBuffer, &firstVertex);
         if (!verts) {
@@ -686,89 +603,35 @@
         for (int i = 0; i < instanceCount; i++) {
             const Geometry& args = fGeoData[i];
 
-            GrColor color = args.fColor;
-            SkScalar outerRadius = args.fOuterRadius;
+            if (args.fIsCircle) {
+                bool isStroked = SkToBool(kStroke_RRectType == args.fType);
+                this->fillInCircleVerts(args, isStroked, &verts);
 
-            const SkRect& bounds = args.fDevBounds;
+                const uint16_t* primIndices = circle_type_to_indices(isStroked);
+                const int primIndexCount = circle_type_to_index_count(isStroked);
+                for (int i = 0; i < primIndexCount; ++i) {
+                    *indices++ = primIndices[i] + currStartVertex;
+                }
 
-            SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
-                                   bounds.fBottom - outerRadius, bounds.fBottom};
+                currStartVertex += circle_type_to_vert_count(isStroked);
 
-            SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
-            // The inner radius in the vertex data must be specified in normalized space.
-            // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
-            SkScalar blurRadius = args.fBlurRadius;
-            for (int i = 0; i < 4; ++i) {
-                verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
-                verts->fColor = color;
-                verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
-                verts->fOuterRadius = outerRadius;
-                verts->fBlurRadius = blurRadius;
-                verts++;
+            } else {
+                this->fillInRRectVerts(args, &verts);
 
-                verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
-                verts->fColor = color;
-                verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
-                verts->fOuterRadius = outerRadius;
-                verts->fBlurRadius = blurRadius;
-                verts++;
+                const uint16_t* primIndices = rrect_type_to_indices(args.fType);
+                const int primIndexCount = rrect_type_to_index_count(args.fType);
+                for (int i = 0; i < primIndexCount; ++i) {
+                    *indices++ = primIndices[i] + currStartVertex;
+                }
 
-                verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
-                verts->fColor = color;
-                verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
-                verts->fOuterRadius = outerRadius;
-                verts->fBlurRadius = blurRadius;
-                verts++;
-
-                verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
-                verts->fColor = color;
-                verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
-                verts->fOuterRadius = outerRadius;
-                verts->fBlurRadius = blurRadius;
-                verts++;
+                currStartVertex += rrect_type_to_vert_count(args.fType);
             }
-            // Add the additional vertices for overstroked rrects.
-            // Effectively this is an additional stroked rrect, with its
-            // outer radius = outerRadius - innerRadius, and inner radius = 0.
-            // This will give us correct AA in the center and the correct
-            // distance to the outer edge.
-            //
-            // Also, the outer offset is a constant vector pointing to the right, which
-            // guarantees that the distance value along the outer rectangle is constant.
-            if (kOverstroke_RRectType == args.fType) {
-                SkASSERT(args.fInnerRadius <= 0.0f);
-
-                SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
-                // this is the normalized distance from the outer rectangle of this
-                // geometry to the outer edge
-                SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
-
-                FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
-                                      overstrokeOuterRadius, color, blurRadius);
-            }
-
-            if (kFill_RRectType == args.fType) {
-                SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
-
-                SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
-
-                FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
-                                      color, blurRadius);
-            }
-
-            const uint16_t* primIndices = rrect_type_to_indices(args.fType);
-            const int primIndexCount = rrect_type_to_index_count(args.fType);
-            for (int i = 0; i < primIndexCount; ++i) {
-                *indices++ = primIndices[i] + currStartVertex;
-            }
-
-            currStartVertex += rrect_type_to_vert_count(args.fType);
         }
 
-        GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
-        target->draw(gp.get(), mesh);
+        GrMesh mesh(kTriangles_GrPrimitiveType);
+        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+        target->draw(gp.get(), this->pipeline(), mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -789,154 +652,74 @@
         return true;
     }
 
-    struct Geometry {
-        GrColor fColor;
-        SkScalar fOuterRadius;
-        SkScalar fInnerRadius;
-        SkScalar fBlurRadius;
-        SkRect fDevBounds;
-        RRectType fType;
-    };
-
     SkSTArray<1, Geometry, true> fGeoData;
     SkMatrix fViewMatrixIfUsingLocalCoords;
     int fVertCount;
     int fIndexCount;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-std::unique_ptr<GrMeshDrawOp> make_shadow_circle_op(GrColor color,
-                                                    const SkMatrix& viewMatrix,
-                                                    const SkRect& oval,
-                                                    SkScalar blurRadius,
-                                                    const SkStrokeRec& stroke,
-                                                    const GrShaderCaps* shaderCaps) {
-    // we can only draw circles
-    SkScalar width = oval.width();
-    SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity());
-    SkPoint center = {oval.centerX(), oval.centerY()};
-    return ShadowCircleOp::Make(color, viewMatrix, center, width / 2.f, blurRadius,
-                                GrStyle(stroke, nullptr));
-}
-
-static std::unique_ptr<GrMeshDrawOp> make_shadow_rrect_op(GrColor color,
-                                                          const SkMatrix& viewMatrix,
-                                                          const SkRRect& rrect,
-                                                          SkScalar blurRadius,
-                                                          const SkStrokeRec& stroke) {
-    SkASSERT(viewMatrix.rectStaysRect());
-    SkASSERT(rrect.isSimple());
-    SkASSERT(!rrect.isOval());
-
+namespace GrShadowRRectOp {
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRRect& rrect,
+                                         SkScalar blurWidth,
+                                         SkScalar insetWidth,
+                                         SkScalar blurClamp) {
     // Shadow rrect ops only handle simple circular rrects.
+    SkASSERT(viewMatrix.isSimilarity() &&
+             (rrect.isSimpleCircular() || rrect.isRect() || rrect.isCircle()));
+
     // Do any matrix crunching before we reset the draw state for device coords.
     const SkRect& rrectBounds = rrect.getBounds();
     SkRect bounds;
     viewMatrix.mapRect(&bounds, rrectBounds);
 
-    SkVector radii = rrect.getSimpleRadii();
-    SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
-                                   viewMatrix[SkMatrix::kMSkewY] * radii.fY);
-    SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
-                                   viewMatrix[SkMatrix::kMScaleY] * radii.fY);
-    SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
+    // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
+    SkScalar radius = rrect.getSimpleRadii().fX;
+    SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
+    SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
+    SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
 
-    SkStrokeRec::Style style = stroke.getStyle();
-
-    // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
-    SkVector scaledStroke = {-1, -1};
-    SkScalar strokeWidth = stroke.getWidth();
-
-    bool isStrokeOnly =
-            SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
-    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
-
-    if (hasStroke) {
-        if (SkStrokeRec::kHairline_Style == style) {
-            scaledStroke.set(1, 1);
-        } else {
-            scaledStroke.fX = SkScalarAbs(
-                    strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
-            scaledStroke.fY = SkScalarAbs(
-                    strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
-        }
-
-        // we don't handle anisotropic strokes
-        if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) {
-            return nullptr;
-        }
-    }
-
-    // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
-    // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
-    // patch will have fractional coverage. This only matters when the interior is actually filled.
-    // We could consider falling back to rect rendering here, since a tiny radius is
-    // indistinguishable from a square corner.
-    if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
-        return nullptr;
-    }
-
-    return std::unique_ptr<GrMeshDrawOp>(new ShadowCircularRRectOp(
-            color, viewMatrix, bounds, xRadius, blurRadius, scaledStroke.fX, isStrokeOnly));
-}
-
-namespace GrShadowRRectOp {
-std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   const SkRRect& rrect,
-                                   const SkScalar blurRadius,
-                                   const SkStrokeRec& stroke,
-                                   const GrShaderCaps* shaderCaps) {
-    if (rrect.isOval()) {
-        return make_shadow_circle_op(color, viewMatrix, rrect.getBounds(), blurRadius, stroke,
-                                     shaderCaps);
-    }
-
-    if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
-        return nullptr;
-    }
-
-    return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius, stroke);
+    return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(color, viewMatrix, bounds,
+                                                                         scaledRadius,
+                                                                         rrect.isOval(),
+                                                                         blurWidth,
+                                                                         scaledInsetWidth,
+                                                                         blurClamp));
 }
 }
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(ShadowCircleOp) {
-    do {
-        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);
-        GrColor color = GrRandomColor(random);
-        SkRect circle = GrTest::TestSquare(random);
-        SkPoint center = {circle.centerX(), circle.centerY()};
-        SkScalar radius = circle.width() / 2.f;
-        SkStrokeRec stroke = GrTest::TestStrokeRec(random);
-        SkScalar blurRadius = random->nextSScalar1() * 72.f;
-        std::unique_ptr<GrMeshDrawOp> op = ShadowCircleOp::Make(
-                color, viewMatrix, center, radius, blurRadius, GrStyle(stroke, nullptr));
-        if (op) {
-            return op;
-        }
-    } while (true);
-}
-
-DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
-    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
+GR_LEGACY_MESH_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 = random->nextSScalar1() * 100.f;
+    SkMatrix viewMatrix;
+    viewMatrix.setRotate(rotate);
+    viewMatrix.postTranslate(translateX, translateY);
+    viewMatrix.postScale(scale, scale);
     GrColor color = GrRandomColor(random);
-    const SkRRect& rrect = GrTest::TestRRectSimple(random);
-    SkScalar blurRadius = random->nextSScalar1() * 72.f;
-    return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius,
-                                GrTest::TestStrokeRec(random));
+    SkScalar insetWidth = random->nextSScalar1() * 72.f;
+    SkScalar blurWidth = random->nextSScalar1() * 72.f;
+    SkScalar blurClamp = random->nextSScalar1();
+    bool isCircle = random->nextBool();
+    if (isCircle) {
+        SkRect circle = GrTest::TestSquare(random);
+        SkRRect rrect = SkRRect::MakeOval(circle);
+        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp);
+    } else {
+        const SkRRect& rrect = GrTest::TestRRectSimple(random);
+        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp);
+    }
 }
 
 #endif
diff --git a/src/gpu/ops/GrShadowRRectOp.h b/src/gpu/ops/GrShadowRRectOp.h
index 081b217..1260095 100644
--- a/src/gpu/ops/GrShadowRRectOp.h
+++ b/src/gpu/ops/GrShadowRRectOp.h
@@ -11,7 +11,7 @@
 #include "GrColor.h"
 #include "SkRefCnt.h"
 
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class GrShaderCaps;
 class SkMatrix;
 class SkRRect;
@@ -19,9 +19,9 @@
 
 namespace GrShadowRRectOp {
 
-std::unique_ptr<GrMeshDrawOp> Make(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect,
-                                   const SkScalar blurRadius, const SkStrokeRec& stroke,
-                                   const GrShaderCaps* shaderCaps);
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect,
+                                         SkScalar blurWidth, SkScalar insetWidth,
+                                         SkScalar blurClamp = 1);
 }
 
 #endif
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
new file mode 100644
index 0000000..dcd8dd8
--- /dev/null
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -0,0 +1,230 @@
+/*
+ * 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 GrSimpleMeshDrawOpHelper_DEFINED
+#define GrSimpleMeshDrawOpHelper_DEFINED
+
+#include "GrAppliedClip.h"
+#include "GrOpFlushState.h"
+#include "GrPipeline.h"
+#include "GrProcessorSet.h"
+#include "GrRect.h"
+#include "GrUserStencilSettings.h"
+
+/**
+ * This class can be used to help implement simple mesh draw ops. It reduces the amount of
+ * boilerplate code to type and also provides a mechanism for optionally allocating space for a
+ * GrProcessorSet based on a GrPaint. It is intended to be used by ops that construct a single
+ * GrPipeline for a uniform primitive color and a GrPaint.
+ */
+class GrSimpleMeshDrawOpHelper {
+public:
+    struct MakeArgs;
+
+    /**
+     * This can be used by a Op class to perform allocation and initialization such that a
+     * GrProcessorSet (if required) is allocated at the same time as the Op instance. It requires
+     * that Op implements a constructor of the form:
+     *      Op(MakeArgs, GrColor, OpArgs...)
+     * which is public or made accessible via 'friend'.
+     */
+    template <typename Op, typename... OpArgs>
+    static std::unique_ptr<GrDrawOp> FactoryHelper(GrPaint&& paint, OpArgs... opArgs);
+
+    GrSimpleMeshDrawOpHelper(const MakeArgs& args, GrAAType aaType,
+                             GrUserStencilSettings* stencilSettings = nullptr)
+            : fProcessors(args.fProcessorSet)
+            , fPipelineFlags(args.fSRGBFlags)
+            , fAAType((int)aaType)
+            , fRequiresDstTexture(false)
+            , fUsesLocalCoords(false)
+            , fCompatibleWithAlphaAsCoveage(false) {
+        SkASSERT(!stencilSettings);
+        SkDEBUGCODE(fDidAnalysis = false);
+        if (GrAATypeIsHW(aaType)) {
+            fPipelineFlags |= GrPipeline::kHWAntialias_Flag;
+        }
+    }
+
+    ~GrSimpleMeshDrawOpHelper() {
+        if (fProcessors) {
+            fProcessors->~GrProcessorSet();
+        }
+    }
+
+    GrSimpleMeshDrawOpHelper() = delete;
+    GrSimpleMeshDrawOpHelper(const GrSimpleMeshDrawOpHelper&) = delete;
+    GrSimpleMeshDrawOpHelper& operator=(const GrSimpleMeshDrawOpHelper&) = delete;
+
+    GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const {
+        return GrAATypeIsHW((this->aaType())) ? GrDrawOp::FixedFunctionFlags::kUsesHWAA
+                                              : GrDrawOp::FixedFunctionFlags::kNone;
+    }
+
+    bool isCompatible(const GrSimpleMeshDrawOpHelper& that, const GrCaps& caps,
+                      const SkRect& aBounds, const SkRect& bBounds) const {
+        if (SkToBool(fProcessors) != SkToBool(that.fProcessors)) {
+            return false;
+        }
+        if (fProcessors) {
+            if (*fProcessors != *that.fProcessors) {
+                return false;
+            }
+            if (fRequiresDstTexture || (fProcessors->xferProcessor() &&
+                                        fProcessors->xferProcessor()->xferBarrierType(caps))) {
+                if (GrRectsTouchOrOverlap(aBounds, bBounds)) {
+                    return false;
+                }
+            }
+        }
+        bool result = fPipelineFlags == that.fPipelineFlags && fAAType == that.fAAType;
+        SkASSERT(!result || fCompatibleWithAlphaAsCoveage == that.fCompatibleWithAlphaAsCoveage);
+        SkASSERT(!result || fUsesLocalCoords == that.fUsesLocalCoords);
+        return result;
+    }
+
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip,
+                              GrProcessorAnalysisCoverage geometryCoverage, GrColor* color) {
+        SkDEBUGCODE(fDidAnalysis = true);
+        GrProcessorSet::Analysis analysis;
+        if (fProcessors) {
+            GrProcessorAnalysisCoverage coverage = geometryCoverage;
+            if (GrProcessorAnalysisCoverage::kNone == coverage) {
+                coverage = clip->clipCoverageFragmentProcessor()
+                                   ? GrProcessorAnalysisCoverage::kSingleChannel
+                                   : GrProcessorAnalysisCoverage::kNone;
+            }
+            bool isMixedSamples = this->aaType() == GrAAType::kMixedSamples;
+            analysis = fProcessors->finalize(*color, coverage, clip, isMixedSamples, caps, color);
+        } else {
+            analysis = GrProcessorSet::EmptySetAnalysis();
+        }
+        fRequiresDstTexture = analysis.requiresDstTexture();
+        fUsesLocalCoords = analysis.usesLocalCoords();
+        fCompatibleWithAlphaAsCoveage = analysis.isCompatibleWithCoverageAsAlpha();
+        return analysis.requiresDstTexture();
+    }
+
+    bool usesLocalCoords() const {
+        SkASSERT(fDidAnalysis);
+        return fUsesLocalCoords;
+    }
+
+    bool compatibleWithAlphaAsCoverage() const { return fCompatibleWithAlphaAsCoveage; }
+
+    GrPipeline* makePipeline(GrMeshDrawOp::Target* target) const {
+        return target->allocPipeline(this->pipelineInitArgs(target));
+    }
+
+    struct MakeArgs {
+    private:
+        MakeArgs() = default;
+
+        GrProcessorSet* fProcessorSet;
+        uint32_t fSRGBFlags;
+
+        friend class GrSimpleMeshDrawOpHelper;
+    };
+
+protected:
+    GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
+    uint32_t pipelineFlags() const { return fPipelineFlags; }
+    const GrProcessorSet& processors() const {
+        return fProcessors ? *fProcessors : GrProcessorSet::EmptySet();
+    }
+
+    GrPipeline::InitArgs pipelineInitArgs(GrMeshDrawOp::Target* target) const {
+        GrPipeline::InitArgs args;
+        args.fFlags = this->pipelineFlags();
+        args.fProcessors = &this->processors();
+        args.fRenderTarget = target->renderTarget();
+        args.fAppliedClip = target->clip();
+        args.fDstProxy = target->dstProxy();
+        args.fCaps = &target->caps();
+        args.fResourceProvider = target->resourceProvider();
+        return args;
+    }
+
+private:
+    GrProcessorSet* fProcessors;
+    unsigned fPipelineFlags : 8;
+    unsigned fAAType : 2;
+    unsigned fRequiresDstTexture : 1;
+    unsigned fUsesLocalCoords : 1;
+    unsigned fCompatibleWithAlphaAsCoveage : 1;
+    SkDEBUGCODE(unsigned fDidAnalysis : 1;)
+};
+
+/**
+ * This class extends GrSimpleMeshDrawOpHelper to support an optional GrUserStencilSettings. This
+ * uses private inheritance because it non-virtually overrides methods in the base class and should
+ * never be used with a GrSimpleMeshDrawOpHelper pointer or reference.
+ */
+class GrSimpleMeshDrawOpHelperWithStencil : private GrSimpleMeshDrawOpHelper {
+public:
+    using MakeArgs = GrSimpleMeshDrawOpHelper::MakeArgs;
+
+    // using declarations can't be templated, so this is a pass through function instead.
+    template <typename Op, typename... OpArgs>
+    static std::unique_ptr<GrDrawOp> FactoryHelper(GrPaint&& paint, OpArgs... opArgs) {
+        return GrSimpleMeshDrawOpHelper::FactoryHelper<Op, OpArgs...>(
+                std::move(paint), std::forward<OpArgs>(opArgs)...);
+    }
+
+    GrSimpleMeshDrawOpHelperWithStencil(const MakeArgs& args, GrAAType aaType,
+                                         const GrUserStencilSettings* stencilSettings)
+            : INHERITED(args, aaType)
+            , fStencilSettings(stencilSettings ? stencilSettings
+                                               : &GrUserStencilSettings::kUnused) {}
+
+    GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const {
+        GrDrawOp::FixedFunctionFlags flags = INHERITED::fixedFunctionFlags();
+        if (fStencilSettings != &GrUserStencilSettings::kUnused) {
+            flags |= GrDrawOp::FixedFunctionFlags::kUsesStencil;
+        }
+        return flags;
+    }
+
+    using GrSimpleMeshDrawOpHelper::xpRequiresDstTexture;
+    using GrSimpleMeshDrawOpHelper::usesLocalCoords;
+
+    bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps& caps,
+                      const SkRect& aBounds, const SkRect& bBounds) const {
+        return INHERITED::isCompatible(that, caps, aBounds, bBounds) &&
+               fStencilSettings == that.fStencilSettings;
+    }
+
+    GrPipeline* makePipeline(GrMeshDrawOp::Target* target) const {
+        auto args = INHERITED::pipelineInitArgs(target);
+        args.fUserStencil = fStencilSettings;
+        return target->allocPipeline(args);
+    }
+
+private:
+    const GrUserStencilSettings* fStencilSettings;
+    typedef GrSimpleMeshDrawOpHelper INHERITED;
+};
+
+template <typename Op, typename... OpArgs>
+std::unique_ptr<GrDrawOp> GrSimpleMeshDrawOpHelper::FactoryHelper(GrPaint&& paint,
+                                                                  OpArgs... opArgs) {
+    MakeArgs makeArgs;
+    makeArgs.fSRGBFlags = GrPipeline::SRGBFlagsFromPaint(paint);
+    GrColor color = paint.getColor();
+    if (paint.isTrivial()) {
+        makeArgs.fProcessorSet = nullptr;
+        return std::unique_ptr<GrDrawOp>(new Op(makeArgs, color, std::forward<OpArgs>(opArgs)...));
+    } else {
+        char* mem = (char*)GrOp::operator new(sizeof(Op) + sizeof(GrProcessorSet));
+        char* setMem = mem + sizeof(Op);
+        makeArgs.fProcessorSet = new (setMem) GrProcessorSet(std::move(paint));
+        return std::unique_ptr<GrDrawOp>(
+                new (mem) Op(makeArgs, color, std::forward<OpArgs>(opArgs)...));
+    }
+}
+
+#endif
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index 8d3f7b7..048351b 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -87,7 +87,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 bool GrSmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
-    if (!args.fShaderCaps->shaderDerivativeSupport()) {
+    if (!args.fCaps->shaderCaps()->shaderDerivativeSupport()) {
         return false;
     }
     // If the shape has no key then we won't get any reuse.
@@ -133,7 +133,7 @@
 // padding around path bounds to allow for antialiased pixels
 static const SkScalar kAntiAliasPad = 1.0f;
 
-class SmallPathOp final : public GrMeshDrawOp {
+class SmallPathOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
@@ -141,12 +141,12 @@
     using ShapeCache = SkTDynamicHash<ShapeData, ShapeData::Key>;
     using ShapeDataList = GrSmallPathRenderer::ShapeDataList;
 
-    static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const GrShape& shape,
-                                              const SkMatrix& viewMatrix, GrDrawOpAtlas* atlas,
-                                              ShapeCache* shapeCache, ShapeDataList* shapeList,
-                                              bool gammaCorrect) {
-        return std::unique_ptr<GrMeshDrawOp>(new SmallPathOp(color, shape, viewMatrix, atlas,
-                                                             shapeCache, shapeList, gammaCorrect));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const GrShape& shape,
+                                                    const SkMatrix& viewMatrix,
+                                                    GrDrawOpAtlas* atlas, ShapeCache* shapeCache,
+                                                    ShapeDataList* shapeList, bool gammaCorrect) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new SmallPathOp(
+                color, shape, viewMatrix, atlas, shapeCache, shapeList, gammaCorrect));
     }
 
     const char* name() const override { return "SmallPathOp"; }
@@ -199,13 +199,13 @@
 
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fShapes[0].fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fShapes[0].fColor);
         fUsesLocalCoords = optimizations.readsLocalCoords();
     }
@@ -388,9 +388,9 @@
         this->flush(target, &flushInfo);
     }
 
-    bool addDFPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo, GrDrawOpAtlas* atlas,
-                          ShapeData* shapeData, const GrShape& shape, uint32_t dimension,
-                          SkScalar scale) const {
+    bool addDFPathToAtlas(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo,
+                          GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
+                          uint32_t dimension, SkScalar scale) const {
         const SkRect& bounds = shape.bounds();
 
         // generate bounding rect for bitmap draw
@@ -505,9 +505,9 @@
         return true;
     }
 
-    bool addBMPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo, 
-                          GrDrawOpAtlas* atlas, ShapeData* shapeData,
-                          const GrShape& shape, const SkMatrix& ctm) const {
+    bool addBMPathToAtlas(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo,
+                          GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
+                          const SkMatrix& ctm) const {
         const SkRect& bounds = shape.bounds();
         if (bounds.isEmpty()) {
             return false;
@@ -677,15 +677,16 @@
         textureCoords[1] = t;
     }
 
-    void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
+    void flush(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
         if (flushInfo->fInstancesToFlush) {
-            GrMesh mesh;
+            GrMesh mesh(kTriangles_GrPrimitiveType);
             int maxInstancesPerDraw =
                 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
-            mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
-                flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerQuad,
-                kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
-            target->draw(flushInfo->fGeometryProcessor.get(), mesh);
+            mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerQuad,
+                                     kVerticesPerQuad, flushInfo->fInstancesToFlush,
+                                     maxInstancesPerDraw);
+            mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
+            target->draw(flushInfo->fGeometryProcessor.get(), this->pipeline(), mesh);
             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
             flushInfo->fInstancesToFlush = 0;
         }
@@ -738,7 +739,7 @@
     ShapeDataList* fShapeList;
     bool fGammaCorrect;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 bool GrSmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
@@ -760,13 +761,14 @@
         }
     }
 
-    std::unique_ptr<GrMeshDrawOp> op = SmallPathOp::Make(
-            args.fPaint.getColor(), *args.fShape, *args.fViewMatrix, fAtlas.get(), &fShapeCache,
-            &fShapeList, args.fGammaCorrect);
+    std::unique_ptr<GrLegacyMeshDrawOp> op =
+            SmallPathOp::Make(args.fPaint.getColor(), *args.fShape, *args.fViewMatrix, fAtlas.get(),
+                              &fShapeCache, &fShapeList, args.fGammaCorrect);
     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
 
-    args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
+    args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
+                                                   std::move(op));
 
     return true;
 }
@@ -817,7 +819,7 @@
     ShapeDataList fShapeList;
 };
 
-DRAW_OP_TEST_DEFINE(SmallPathOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(SmallPathOp) {
     static PathTestStruct gTestStruct;
 
     if (context->uniqueID() != gTestStruct.fContextID) {
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
index 4c2edf4..a301564 100644
--- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
@@ -18,11 +18,11 @@
 #include "GrResourceProvider.h"
 #include "GrStencilPathOp.h"
 #include "GrStyle.h"
-#include "ops/GrRectOpFactory.h"
+#include "ops/GrNonAAFillRectOp.h"
 
 GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider,
                                                       const GrCaps& caps) {
-    if (caps.shaderCaps()->pathRenderingSupport()) {
+    if (caps.shaderCaps()->pathRenderingSupport() && !caps.avoidStencilBuffers()) {
         return new GrStencilAndCoverPathRenderer(resourceProvider);
     } else {
         return nullptr;
@@ -59,7 +59,7 @@
     if (!path) {
         SkPath skPath;
         shape.asPath(&skPath);
-        path.reset(resourceProvider->createPath(skPath, shape.style()));
+        path = resourceProvider->createPath(skPath, shape.style());
         if (!isVolatile) {
             resourceProvider->assignUniqueKeyToResource(key, path.get());
         }
@@ -112,9 +112,6 @@
         }
         const SkMatrix& viewM = viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix;
 
-        std::unique_ptr<GrMeshDrawOp> coverOp(GrRectOpFactory::MakeNonAAFill(
-                args.fPaint.getColor(), viewM, bounds, nullptr, &invert));
-
         // fake inverse with a stencil and cover
         args.fRenderTargetContext->priv().stencilPath(*args.fClip, args.fAAType, viewMatrix,
                                                       path.get());
@@ -138,16 +135,14 @@
             if (GrAAType::kMixedSamples == coverAAType) {
                 coverAAType = GrAAType::kNone;
             }
-            GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), coverAAType);
-            pipelineBuilder.setUserStencil(&kInvertedCoverPass);
-
-            args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip,
-                                                     std::move(coverOp));
+            args.fRenderTargetContext->addDrawOp(
+                    *args.fClip,
+                    GrNonAAFillRectOp::Make(std::move(args.fPaint), viewM, bounds, nullptr, &invert,
+                                            coverAAType, &kInvertedCoverPass));
         }
     } else {
-        GrAA aa = GrBoolToAA(GrAATypeIsHW(args.fAAType));
         std::unique_ptr<GrDrawOp> op =
-                GrDrawPathOp::Make(viewMatrix, std::move(args.fPaint), aa, path.get());
+                GrDrawPathOp::Make(viewMatrix, std::move(args.fPaint), args.fAAType, path.get());
         args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
     }
 
diff --git a/src/gpu/ops/GrStencilPathOp.h b/src/gpu/ops/GrStencilPathOp.h
index ac19b5a..5b308f0 100644
--- a/src/gpu/ops/GrStencilPathOp.h
+++ b/src/gpu/ops/GrStencilPathOp.h
@@ -8,12 +8,10 @@
 #ifndef GrStencilPathOp_DEFINED
 #define GrStencilPathOp_DEFINED
 
-#include "GrGpu.h"
 #include "GrOp.h"
 #include "GrOpFlushState.h"
 #include "GrPath.h"
 #include "GrPathRendering.h"
-#include "GrRenderTarget.h"
 #include "GrStencilSettings.h"
 
 class GrStencilPathOp final : public GrOp {
@@ -26,18 +24,18 @@
                                       bool hasStencilClip,
                                       int numStencilBits,
                                       const GrScissorState& scissor,
-                                      GrRenderTarget* renderTarget,
                                       const GrPath* path) {
+
         return std::unique_ptr<GrOp>(new GrStencilPathOp(viewMatrix, useHWAA, fillType,
                                                          hasStencilClip, numStencilBits, scissor,
-                                                         renderTarget, path));
+                                                         path));
     }
 
     const char* name() const override { return "StencilPathOp"; }
 
     SkString dumpInfo() const override {
         SkString string;
-        string.printf("PATH: 0x%p, AA:%d", fPath.get(), fUseHWAA);
+        string.printf("Path: 0x%p, AA: %d", fPath.get(), fUseHWAA);
         string.append(INHERITED::dumpInfo());
         return string;
     }
@@ -49,7 +47,6 @@
                     bool hasStencilClip,
                     int numStencilBits,
                     const GrScissorState& scissor,
-                    GrRenderTarget* renderTarget,
                     const GrPath* path)
             : INHERITED(ClassID())
             , fViewMatrix(viewMatrix)
@@ -57,7 +54,6 @@
             , fStencil(GrPathRendering::GetStencilPassSettings(fillType), hasStencilClip,
                        numStencilBits)
             , fScissor(scissor)
-            , fRenderTarget(renderTarget)
             , fPath(path) {
         this->setBounds(path->getBounds(), HasAABloat::kNo, IsZeroArea::kNo);
     }
@@ -67,16 +63,17 @@
     void onPrepare(GrOpFlushState*) override {}
 
     void onExecute(GrOpFlushState* state) override {
-        GrPathRendering::StencilPathArgs args(fUseHWAA, fRenderTarget.get(), &fViewMatrix,
-                                              &fScissor, &fStencil);
+        SkASSERT(state->drawOpArgs().fRenderTarget);
+
+        GrPathRendering::StencilPathArgs args(fUseHWAA, state->drawOpArgs().fRenderTarget,
+                                              &fViewMatrix, &fScissor, &fStencil);
         state->gpu()->pathRendering()->stencilPath(args, fPath.get());
     }
 
-    SkMatrix fViewMatrix;
-    bool fUseHWAA;
-    GrStencilSettings fStencil;
-    GrScissorState fScissor;
-    GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
+    SkMatrix                                          fViewMatrix;
+    bool                                              fUseHWAA;
+    GrStencilSettings                                 fStencil;
+    GrScissorState                                    fScissor;
     GrPendingIOResource<const GrPath, kRead_GrIOType> fPath;
 
     typedef GrOp INHERITED;
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 9149988..27de8f2 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -103,8 +103,11 @@
 
 class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
 public:
-    DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
-        : VertexAllocator(stride), fTarget(target), fVertexBuffer(nullptr), fVertices(nullptr) {}
+    DynamicVertexAllocator(size_t stride, GrLegacyMeshDrawOp::Target* target)
+            : VertexAllocator(stride)
+            , fTarget(target)
+            , fVertexBuffer(nullptr)
+            , fVertices(nullptr) {}
     void* lock(int vertexCount) override {
         fVertexCount = vertexCount;
         fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
@@ -117,7 +120,7 @@
     const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
     int firstVertex() const { return fFirstVertex; }
 private:
-    GrMeshDrawOp::Target* fTarget;
+    GrLegacyMeshDrawOp::Target* fTarget;
     const GrBuffer* fVertexBuffer;
     int fVertexCount;
     int fFirstVertex;
@@ -156,16 +159,16 @@
     return true;
 }
 
-class TessellatingPathOp final : public GrMeshDrawOp {
+class TessellatingPathOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrMeshDrawOp> Make(const GrColor& color,
-                                              const GrShape& shape,
-                                              const SkMatrix& viewMatrix,
-                                              SkIRect devClipBounds,
-                                              bool antiAlias) {
-        return std::unique_ptr<GrMeshDrawOp>(
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(const GrColor& color,
+                                                    const GrShape& shape,
+                                                    const SkMatrix& viewMatrix,
+                                                    SkIRect devClipBounds,
+                                                    bool antiAlias) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(
                 new TessellatingPathOp(color, shape, viewMatrix, devClipBounds, antiAlias));
     }
 
@@ -180,13 +183,13 @@
     }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
         fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
         fNeedsLocalCoords = optimizations.readsLocalCoords();
@@ -310,11 +313,10 @@
 
     void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
                       int firstVertex, int count) const {
-        GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
-                                                              : kTriangles_GrPrimitiveType;
-        GrMesh mesh;
-        mesh.init(primitiveType, vb, firstVertex, count);
-        target->draw(gp, mesh);
+        GrMesh mesh(TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType : kTriangles_GrPrimitiveType);
+        mesh.setNonIndexedNonInstanced(count);
+        mesh.setVertexData(vb, firstVertex);
+        target->draw(gp, this->pipeline(), mesh);
     }
 
     bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
@@ -348,7 +350,7 @@
     bool                    fCanTweakAlphaForCoverage;
     bool                    fNeedsLocalCoords;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
@@ -358,7 +360,7 @@
     args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
                                       args.fRenderTargetContext->height(),
                                       &clipBoundsI);
-    std::unique_ptr<GrMeshDrawOp> op =
+    std::unique_ptr<GrLegacyMeshDrawOp> op =
             TessellatingPathOp::Make(args.fPaint.getColor(),
                                      *args.fShape,
                                      *args.fViewMatrix,
@@ -366,7 +368,8 @@
                                      GrAAType::kCoverage == args.fAAType);
     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
-    args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
+    args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
+                                                   std::move(op));
     return true;
 }
 
@@ -374,7 +377,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkPath path = GrTest::TestPath(random);
diff --git a/src/gpu/ops/GrTestMeshDrawOp.h b/src/gpu/ops/GrTestMeshDrawOp.h
index a4bf066..aad6774 100644
--- a/src/gpu/ops/GrTestMeshDrawOp.h
+++ b/src/gpu/ops/GrTestMeshDrawOp.h
@@ -14,10 +14,10 @@
 #include "ops/GrMeshDrawOp.h"
 
 /*
- * A simple solid color GrMeshDrawOp for testing purposes which doesn't ever combine. Subclassing
- * this in tests saves having to fill out some boiler plate methods.
+ * A simple solid color GrLegacyMeshDrawOp for testing purposes which doesn't ever combine.
+ * Subclassing this in tests saves having to fill out some boiler plate methods.
  */
-class GrTestMeshDrawOp : public GrMeshDrawOp {
+class GrTestMeshDrawOp : public GrLegacyMeshDrawOp {
 public:
     const char* name() const override = 0;
 
@@ -33,13 +33,13 @@
     bool usesLocalCoords() const { return fUsesLocalCoords; }
 
 private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToConstant(fColor);
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
         optimizations.getOverrideColorIfSet(&fColor);
         fUsesLocalCoords = optimizations.readsLocalCoords();
     }
@@ -49,7 +49,7 @@
     GrColor fColor;
     bool fUsesLocalCoords = false;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 
 #endif
diff --git a/src/gpu/text/GrAtlasGlyphCache.cpp b/src/gpu/text/GrAtlasGlyphCache.cpp
index 213ecfc..39c3e10 100644
--- a/src/gpu/text/GrAtlasGlyphCache.cpp
+++ b/src/gpu/text/GrAtlasGlyphCache.cpp
@@ -36,31 +36,48 @@
     return true;
 }
 
-GrAtlasGlyphCache::GrAtlasGlyphCache(GrContext* context)
-    : fContext(context)
-    , fPreserveStrike(nullptr) {
+GrAtlasGlyphCache::GrAtlasGlyphCache(GrContext* context, float maxTextureBytes)
+        : fContext(context), fPreserveStrike(nullptr) {
+    // Calculate RGBA size. Must be between 1024 x 512 and MaxTextureSize x MaxTextureSize / 2
+    int log2MaxTextureSize = log2(context->caps()->maxTextureSize());
+    int log2MaxDim = 10;
+    for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) {
+        int maxDim = 1 << log2MaxDim;
+        int minDim = 1 << (log2MaxDim - 1);
 
-    // setup default atlas configs
-    fAtlasConfigs[kA8_GrMaskFormat].fWidth = 2048;
-    fAtlasConfigs[kA8_GrMaskFormat].fHeight = 2048;
-    fAtlasConfigs[kA8_GrMaskFormat].fLog2Width = 11;
-    fAtlasConfigs[kA8_GrMaskFormat].fLog2Height = 11;
-    fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = 512;
-    fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = 256;
+        if (maxDim * minDim * 4 >= maxTextureBytes) break;
+    }
 
-    fAtlasConfigs[kA565_GrMaskFormat].fWidth = 1024;
-    fAtlasConfigs[kA565_GrMaskFormat].fHeight = 2048;
-    fAtlasConfigs[kA565_GrMaskFormat].fLog2Width = 10;
-    fAtlasConfigs[kA565_GrMaskFormat].fLog2Height = 11;
-    fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = 256;
-    fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = 256;
+    int log2MinDim = log2MaxDim - 1;
+    int maxDim = 1 << log2MaxDim;
+    int minDim = 1 << log2MinDim;
+    // Plots are either 256 or 512.
+    int maxPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 2)));
+    int minPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 3)));
 
-    fAtlasConfigs[kARGB_GrMaskFormat].fWidth = 1024;
-    fAtlasConfigs[kARGB_GrMaskFormat].fHeight = 2048;
-    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Width = 10;
-    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Height = 11;
-    fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = 256;
-    fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = 256;
+    // Setup default atlas configs. The A8 atlas uses maxDim for both width and height, as the A8
+    // format is already very compact.
+    fAtlasConfigs[kA8_GrMaskFormat].fWidth = maxDim;
+    fAtlasConfigs[kA8_GrMaskFormat].fHeight = maxDim;
+    fAtlasConfigs[kA8_GrMaskFormat].fLog2Width = log2MaxDim;
+    fAtlasConfigs[kA8_GrMaskFormat].fLog2Height = log2MaxDim;
+    fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = maxPlot;
+    fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = minPlot;
+
+    // A565 and ARGB use maxDim x minDim.
+    fAtlasConfigs[kA565_GrMaskFormat].fWidth = minDim;
+    fAtlasConfigs[kA565_GrMaskFormat].fHeight = maxDim;
+    fAtlasConfigs[kA565_GrMaskFormat].fLog2Width = log2MinDim;
+    fAtlasConfigs[kA565_GrMaskFormat].fLog2Height = log2MaxDim;
+    fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = minPlot;
+    fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = minPlot;
+
+    fAtlasConfigs[kARGB_GrMaskFormat].fWidth = minDim;
+    fAtlasConfigs[kARGB_GrMaskFormat].fHeight = maxDim;
+    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Width = log2MinDim;
+    fAtlasConfigs[kARGB_GrMaskFormat].fLog2Height = log2MaxDim;
+    fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = minPlot;
+    fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = minPlot;
 }
 
 GrAtlasGlyphCache::~GrAtlasGlyphCache() {
@@ -413,7 +430,7 @@
     }
     GrMaskFormat format = get_packed_glyph_mask_format(skGlyph);
 
-    GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph));
+    GrGlyph* glyph = fPool.make<GrGlyph>();
     glyph->init(packed, bounds, format);
     fCache.add(glyph);
     return glyph;
diff --git a/src/gpu/text/GrAtlasGlyphCache.h b/src/gpu/text/GrAtlasGlyphCache.h
index 9e97966..3ebea2b 100644
--- a/src/gpu/text/GrAtlasGlyphCache.h
+++ b/src/gpu/text/GrAtlasGlyphCache.h
@@ -11,9 +11,9 @@
 #include "GrCaps.h"
 #include "GrDrawOpAtlas.h"
 #include "GrGlyph.h"
+#include "SkArenaAlloc.h"
 #include "SkGlyphCache.h"
 #include "SkTDynamicHash.h"
-#include "SkVarAlloc.h"
 
 class GrAtlasGlyphCache;
 class GrGpu;
@@ -85,7 +85,7 @@
 private:
     SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
     SkAutoDescriptor fFontScalerKey;
-    SkVarAlloc fPool;
+    SkArenaAlloc fPool{512};
 
     GrAtlasGlyphCache* fAtlasGlyphCache;
     int fAtlasedGlyphs;
@@ -110,7 +110,7 @@
  */
 class GrAtlasGlyphCache {
 public:
-    GrAtlasGlyphCache(GrContext*);
+    GrAtlasGlyphCache(GrContext*, float maxTextureBytes);
     ~GrAtlasGlyphCache();
     // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
     // another client of the cache may cause the strike to be purged while it is still reffed.
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 5427855..be9bb27 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -179,7 +179,7 @@
     // to regenerate the blob on any color change
     // We use the grPaint to get any color filter effects
     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
-        fFilteredPaintColor != paint.filteredSkColor()) {
+        fFilteredPaintColor != paint.filteredUnpremulColor()) {
         return true;
     }
 
@@ -257,7 +257,7 @@
     return false;
 }
 
-inline std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::makeOp(
+inline std::unique_ptr<GrLegacyMeshDrawOp> GrAtlasTextBlob::makeOp(
         const Run::SubRunInfo& info, int glyphCount, int run, int subRun,
         const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint,
         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
@@ -266,7 +266,7 @@
 
     std::unique_ptr<GrAtlasTextOp> op;
     if (info.drawAsDistanceFields()) {
-        SkColor filteredColor = paint.filteredSkColor();
+        GrColor filteredColor = paint.filteredUnpremulColor();
         bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
         op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable,
                                               useGammaCorrectDistanceTable, filteredColor,
@@ -280,7 +280,7 @@
     geometry.fRun = run;
     geometry.fSubRun = subRun;
     geometry.fColor =
-            info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor();
+            info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulColor();
     geometry.fX = x;
     geometry.fY = y;
     op->init();
@@ -305,12 +305,12 @@
             continue;
         }
 
-        std::unique_ptr<GrMeshDrawOp> op(this->makeOp(info, glyphCount, run, subRun, viewMatrix, x,
-                                                      y, paint, props, distanceAdjustTable,
-                                                      rtc->isGammaCorrect(), cache));
+        std::unique_ptr<GrLegacyMeshDrawOp> op(
+                this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
+                             distanceAdjustTable, rtc->isGammaCorrect(), cache));
         GrPipelineBuilder pipelineBuilder(std::move(grPaint), GrAAType::kNone);
 
-        rtc->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
+        rtc->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
     }
 }
 
@@ -425,7 +425,7 @@
     this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
 }
 
-std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::test_makeOp(
+std::unique_ptr<GrLegacyMeshDrawOp> GrAtlasTextBlob::test_makeOp(
         int glyphCount, int run, int subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
         const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache) {
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index 47dd1db..4cc1028 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -24,7 +24,7 @@
 class GrBlobRegenHelper;
 struct GrDistanceFieldAdjustTable;
 class GrMemoryPool;
-class GrMeshDrawOp;
+class GrLegacyMeshDrawOp;
 class SkDrawFilter;
 class SkTextBlob;
 class SkTextBlobRunIterator;
@@ -240,7 +240,7 @@
     // The color here is the GrPaint color, and it is used to determine whether we
     // have to regenerate LCD text blobs.
     // We use this color vs the SkPaint color because it has the colorfilter applied.
-    void initReusableBlob(SkColor filteredColor, const SkMatrix& viewMatrix, SkScalar x,
+    void initReusableBlob(GrColor filteredColor, const SkMatrix& viewMatrix, SkScalar x,
                           SkScalar y) {
         fFilteredPaintColor = filteredColor;
         this->setupViewMatrix(viewMatrix, x, y);
@@ -270,12 +270,10 @@
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
     // Internal test methods
-    std::unique_ptr<GrMeshDrawOp> test_makeOp(int glyphCount, int run, int subRun,
-                                              const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
-                                              const GrTextUtils::Paint& paint,
-                                              const SkSurfaceProps& props,
-                                              const GrDistanceFieldAdjustTable* distanceAdjustTable,
-                                              GrAtlasGlyphCache* cache);
+    std::unique_ptr<GrLegacyMeshDrawOp> test_makeOp(
+            int glyphCount, int run, int subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
+            const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
+            const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache);
 
 private:
     GrAtlasTextBlob()
@@ -489,7 +487,7 @@
                    Run* run, Run::SubRunInfo* info, SkAutoGlyphCache*, int glyphCount,
                    size_t vertexStride, GrColor color, SkScalar transX, SkScalar transY) const;
 
-    inline std::unique_ptr<GrMeshDrawOp> makeOp(
+    inline std::unique_ptr<GrLegacyMeshDrawOp> makeOp(
             const Run::SubRunInfo& info, int glyphCount, int run, int subRun,
             const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint,
             const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
@@ -532,7 +530,7 @@
     SkMatrix fInitialViewMatrix;
     SkMatrix fInitialViewMatrixInverse;
     size_t fSize;
-    SkColor fFilteredPaintColor;
+    GrColor fFilteredPaintColor;
     SkScalar fInitialX;
     SkScalar fInitialY;
 
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index 4e377c9..9efe466 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -118,7 +118,7 @@
         cacheBlob = cache->find(key);
     }
 
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     if (cacheBlob) {
         if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
             // We have to remake the blob because changes may invalidate our masks.
@@ -166,7 +166,7 @@
                                             uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
                                             const SkSurfaceProps& props, const SkTextBlob* blob,
                                             SkScalar x, SkScalar y, SkDrawFilter* drawFilter) {
-    cacheBlob->initReusableBlob(paint.filteredSkColor(), viewMatrix, x, y);
+    cacheBlob->initReusableBlob(paint.filteredUnpremulColor(), viewMatrix, x, y);
 
     // Regenerate textblob
     SkTextBlobRunIterator it(blob);
@@ -289,7 +289,7 @@
     if (context->abandoned()) {
         return;
     }
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
         sk_sp<GrAtlasTextBlob> blob(
             MakeDrawTextBlob(context->getTextBlobCache(), context->getAtlasGlyphCache(),
@@ -313,7 +313,7 @@
                                      const char text[], size_t byteLength, const SkScalar pos[],
                                      int scalarsPerPosition, const SkPoint& offset,
                                      const SkIRect& regionClipBounds) {
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     if (context->abandoned()) {
         return;
     } else if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
@@ -339,7 +339,7 @@
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(TextBlobOp) {
+GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(TextBlobOp) {
     static uint32_t gContextID = SK_InvalidGenID;
     static GrAtlasTextContext* gTextContext = nullptr;
     static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
@@ -352,7 +352,7 @@
     }
 
     // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
-    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
         SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
 
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
@@ -374,7 +374,7 @@
     SkScalar x = SkIntToScalar(xInt);
     SkScalar y = SkIntToScalar(yInt);
 
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
     // test the text op with this unit test, that is okay.
     sk_sp<GrAtlasTextBlob> blob(GrAtlasTextContext::MakeDrawTextBlob(
diff --git a/src/gpu/text/GrAtlasTextContext.h b/src/gpu/text/GrAtlasTextContext.h
index 7438b64..e375195 100644
--- a/src/gpu/text/GrAtlasTextContext.h
+++ b/src/gpu/text/GrAtlasTextContext.h
@@ -89,7 +89,7 @@
 #if GR_TEST_UTILS
     static const uint32_t kTextBlobOpScalerContextFlags =
             SkPaint::kFakeGammaAndBoostContrast_ScalerContextFlags;
-    DRAW_OP_TEST_FRIEND(TextBlobOp);
+    GR_LEGACY_MESH_DRAW_OP_TEST_FRIEND(TextBlobOp);
 #endif
 };
 
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.cpp b/src/gpu/text/GrStencilAndCoverTextContext.cpp
index e572e5d..6108e7a 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/text/GrStencilAndCoverTextContext.cpp
@@ -134,7 +134,7 @@
                                                         SkScalar x, SkScalar y,
                                                         SkDrawFilter* drawFilter,
                                                         const SkIRect& clipBounds) {
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
     SkTextBlobRunIterator it(blob);
     for (;!it.done(); it.next()) {
@@ -527,11 +527,13 @@
     fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
 }
 
-GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(
+sk_sp<GrPathRange> GrStencilAndCoverTextContext::TextRun::createGlyphs(
                                                     GrResourceProvider* resourceProvider) const {
-    GrPathRange* glyphs = static_cast<GrPathRange*>(
-            resourceProvider->findAndRefResourceByUniqueKey(fGlyphPathsKey));
-    if (nullptr == glyphs) {
+    sk_sp<GrPathRange> glyphs;
+
+    glyphs.reset(static_cast<GrPathRange*>(
+            resourceProvider->findAndRefResourceByUniqueKey(fGlyphPathsKey)));
+    if (!glyphs) {
         if (fUsingRawGlyphPaths) {
             SkScalerContextEffects noeffects;
             glyphs = resourceProvider->createGlyphs(fFont.getTypeface(), noeffects,
@@ -543,7 +545,7 @@
                                                     &cache->getDescriptor(),
                                                     fStyle);
         }
-        resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
+        resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs.get());
     }
     return glyphs;
 }
@@ -570,9 +572,7 @@
                                                  SkScalar y, const SkIRect& clipBounds,
                                                  GrAtlasTextContext* fallbackTextContext,
                                                  const SkPaint& originalSkPaint) const {
-    GrAA runAA = this->isAntiAlias();
     SkASSERT(fInstanceData);
-    SkASSERT(renderTargetContext->isStencilBufferMultisampled() || GrAA::kNo == runAA);
 
     if (fInstanceData->count()) {
         static constexpr GrUserStencilSettings kCoverPass(
@@ -606,9 +606,12 @@
                                               renderTargetContext->height());
 
         // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
+        GrAAType aaType = GrChooseAAType(this->aa(), renderTargetContext->fsaaType(),
+                                         GrAllowMixedSamples::kYes);
+
         std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
                 viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
-                std::move(grPaint), GrPathRendering::kWinding_FillType, runAA, glyphs.get(),
+                std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
                 fInstanceData.get(), bounds);
 
         renderTargetContext->addDrawOp(clip, std::move(op));
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.h b/src/gpu/text/GrStencilAndCoverTextContext.h
index f06442b..49bba4f 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.h
+++ b/src/gpu/text/GrStencilAndCoverTextContext.h
@@ -83,13 +83,13 @@
 
         size_t computeSizeInCache() const;
 
-        GrAA isAntiAlias() const { return fFont.isAntiAlias() ? GrAA::kYes : GrAA::kNo; }
+        GrAA aa() const { return fFont.isAntiAlias() ? GrAA::kYes : GrAA::kNo; }
 
     private:
         typedef GrDrawPathRangeOp::InstanceData InstanceData;
 
         SkGlyphCache* getGlyphCache() const;
-        GrPathRange* createGlyphs(GrResourceProvider*) const;
+        sk_sp<GrPathRange> createGlyphs(GrResourceProvider*) const;
         void appendGlyph(const SkGlyph&, const SkPoint&, FallbackBlobBuilder*);
 
         GrStyle                         fStyle;
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index 31d0291..9baf341 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -50,6 +50,27 @@
     }
 }
 
+void GrTextUtils::Paint::initFilteredColor() {
+    // This mirrors the logic in skpaint_to_grpaint_impl for handling paint colors
+    if (fDstColorSpace) {
+        GrColor4f filteredColor = SkColorToUnpremulGrColor4f(fPaint->getColor(), fDstColorSpace,
+                                                             fColorXformFromSRGB);
+        if (fPaint->getColorFilter()) {
+            filteredColor = GrColor4f::FromSkColor4f(
+                fPaint->getColorFilter()->filterColor4f(filteredColor.toSkColor4f()));
+        }
+        fFilteredPremulColor = filteredColor.premul().toGrColor();
+        fFilteredUnpremulColor = filteredColor.toGrColor();
+    } else {
+        SkColor filteredSkColor = fPaint->getColor();
+        if (fPaint->getColorFilter()) {
+            filteredSkColor = fPaint->getColorFilter()->filterColor(filteredSkColor);
+        }
+        fFilteredPremulColor = SkColorToPremulGrColor(filteredSkColor);
+        fFilteredUnpremulColor = SkColorToUnpremulGrColor(filteredSkColor);
+    }
+}
+
 bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
     if (!fModifiedPaint.isValid()) {
         fModifiedPaint.init(fOriginalPaint->skPaint());
@@ -99,7 +120,7 @@
              BmpAppendGlyph(
                  blob, runIndex, fontCache, &currStrike, glyph,
                  SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
-                 paint.filteredPremulGrColor(), cache);
+                 paint.filteredPremulColor(), cache);
         }
     );
 
@@ -135,7 +156,7 @@
             BmpAppendGlyph(
                 blob, runIndex, fontCache, &currStrike, glyph,
                 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
-                paint.filteredPremulGrColor(), cache);
+                paint.filteredPremulColor(), cache);
         }
     );
 
@@ -390,7 +411,7 @@
                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
 
                 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
-                                   paint.filteredPremulGrColor(), cache, textRatio, viewMatrix)) {
+                                   paint.filteredPremulColor(), cache, textRatio, viewMatrix)) {
                     // couldn't append, send to fallback
                     fallbackTxt.append(SkToInt(text-lastText), lastText);
                     *fallbackPos.append() = pos[0];
@@ -417,7 +438,7 @@
                 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
 
                 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
-                                   y - advanceY, paint.filteredPremulGrColor(), cache, textRatio,
+                                   y - advanceY, paint.filteredPremulColor(), cache, textRatio,
                                    viewMatrix)) {
                     // couldn't append, send to fallback
                     fallbackTxt.append(SkToInt(text-lastText), lastText);
diff --git a/src/gpu/text/GrTextUtils.h b/src/gpu/text/GrTextUtils.h
index c5daa42..9f38699 100644
--- a/src/gpu/text/GrTextUtils.h
+++ b/src/gpu/text/GrTextUtils.h
@@ -19,10 +19,12 @@
 class GrAtlasTextBlob;
 class GrAtlasTextStrike;
 class GrClip;
+class GrColorSpaceXform;
 class GrContext;
 class GrPaint;
 class GrRenderTargetContext;
 class GrShaderCaps;
+class SkColorSpace;
 class SkDrawFilter;
 class SkGlyph;
 class SkMatrix;
@@ -45,12 +47,19 @@
      */
     class Paint {
     public:
-        explicit Paint(const SkPaint* paint) : fPaint(paint) { this->initFilteredColor(); }
+        explicit Paint(const SkPaint* paint,
+                       SkColorSpace* dstColorSpace,
+                       GrColorSpaceXform* colorXformFromSRGB)
+                : fPaint(paint)
+                , fDstColorSpace(dstColorSpace)
+                , fColorXformFromSRGB(colorXformFromSRGB) {
+            this->initFilteredColor();
+        }
 
         // These expose the paint's color run through its color filter (if any). This is only valid
         // when drawing grayscale/lcd glyph masks and not when drawing color glyphs.
-        SkColor filteredSkColor() const { return fFilteredSkColor; }
-        GrColor filteredPremulGrColor() const { return fFilteredGrColor; }
+        GrColor filteredPremulColor() const { return fFilteredPremulColor; }
+        GrColor filteredUnpremulColor() const { return fFilteredUnpremulColor; }
 
         const SkPaint& skPaint() const { return *fPaint; }
         operator const SkPaint&() const { return this->skPaint(); }
@@ -58,21 +67,21 @@
         bool toGrPaint(GrMaskFormat, GrRenderTargetContext*, const SkMatrix& viewMatrix,
                        GrPaint*) const;
 
+        // Just for RunPaint's constructor
+        SkColorSpace* dstColorSpace() const { return fDstColorSpace; }
+        GrColorSpaceXform* colorXformFromSRGB() const { return fColorXformFromSRGB; }
+
     protected:
-        void initFilteredColor() {
-            fFilteredSkColor = fPaint->getColor();
-            if (fPaint->getColorFilter()) {
-                fFilteredSkColor = fPaint->getColorFilter()->filterColor(fFilteredSkColor);
-            }
-            fFilteredGrColor = SkColorToPremulGrColor(fFilteredSkColor);
-        }
+        void initFilteredColor();
         Paint() = default;
         const SkPaint* fPaint;
+        SkColorSpace* fDstColorSpace;
+        GrColorSpaceXform* fColorXformFromSRGB;
         // This is the paint's color run through its color filter, if present. This color should
         // be used except when rendering bitmap text, in which case the bitmap must be filtered in
         // the fragment shader.
-        SkColor fFilteredSkColor;
-        SkColor fFilteredGrColor;
+        GrColor fFilteredUnpremulColor;
+        GrColor fFilteredPremulColor;
     };
 
     /**
@@ -86,8 +95,10 @@
                 : fOriginalPaint(paint), fFilter(filter), fProps(props) {
             // Initially we represent the original paint.
             fPaint = &fOriginalPaint->skPaint();
-            fFilteredSkColor = fOriginalPaint->filteredSkColor();
-            fFilteredGrColor = fOriginalPaint->filteredPremulGrColor();
+            fDstColorSpace = fOriginalPaint->dstColorSpace();
+            fColorXformFromSRGB = fOriginalPaint->colorXformFromSRGB();
+            fFilteredPremulColor = fOriginalPaint->filteredPremulColor();
+            fFilteredUnpremulColor = fOriginalPaint->filteredUnpremulColor();
         }
 
         bool modifyForRun(const SkTextBlobRunIterator&);
diff --git a/src/gpu/vk/GrVkBackendContext.cpp b/src/gpu/vk/GrVkBackendContext.cpp
index 5a9de04..e473178 100644
--- a/src/gpu/vk/GrVkBackendContext.cpp
+++ b/src/gpu/vk/GrVkBackendContext.cpp
@@ -38,9 +38,23 @@
 const uint32_t kGrVkMinimumVersion = VK_MAKE_VERSION(1, 0, 8);
 #endif
 
+#define ACQUIRE_VK_PROC(name, instance, device)                                \
+    PFN_vk##name grVk##name =                                                  \
+        reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \
+    if (grVk##name == nullptr) {                                               \
+        SkDebugf("Function ptr for vk%s could not be acquired\n", #name);      \
+        return nullptr;                                                        \
+    }
+
 // Create the base Vulkan objects needed by the GrVkGpu object
 const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndexPtr,
-                                                     CanPresentFn canPresent) {
+                                                     CanPresentFn canPresent,
+                                                     GrVkInterface::GetProc getProc) {
+    if (!getProc) {
+        return nullptr;
+    }
+    SkASSERT(getProc);
+
     VkPhysicalDevice physDev;
     VkDevice device;
     VkInstance inst;
@@ -56,7 +70,7 @@
         kGrVkMinimumVersion,                // apiVersion
     };
 
-    GrVkExtensions extensions;
+    GrVkExtensions extensions(getProc);
     extensions.initInstance(kGrVkMinimumVersion);
 
     SkTArray<const char*> instanceLayerNames;
@@ -92,7 +106,7 @@
         instanceExtensionNames.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
         extensionFlags |= kKHR_android_surface_GrVkExtensionFlag;
     }
-#elif defined(SK_BUILD_FOR_UNIX)
+#elif defined(SK_BUILD_FOR_UNIX) && !defined(__Fuchsia__)
     if (extensions.hasInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) {
         instanceExtensionNames.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
         extensionFlags |= kKHR_xcb_surface_GrVkExtensionFlag;
@@ -110,40 +124,58 @@
         instanceExtensionNames.begin(),            // ppEnabledExtensionNames
     };
 
-    err = vkCreateInstance(&instance_create, nullptr, &inst);
+    ACQUIRE_VK_PROC(CreateInstance, VK_NULL_HANDLE, VK_NULL_HANDLE);
+    err = grVkCreateInstance(&instance_create, nullptr, &inst);
     if (err < 0) {
         SkDebugf("vkCreateInstance failed: %d\n", err);
         return nullptr;
     }
 
+    ACQUIRE_VK_PROC(DestroyInstance, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(EnumeratePhysicalDevices, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(GetPhysicalDeviceFeatures, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(CreateDevice, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(GetDeviceQueue, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(DeviceWaitIdle, inst, VK_NULL_HANDLE);
+    ACQUIRE_VK_PROC(DestroyDevice, inst, VK_NULL_HANDLE);
+
     uint32_t gpuCount;
-    err = vkEnumeratePhysicalDevices(inst, &gpuCount, nullptr);
+    err = grVkEnumeratePhysicalDevices(inst, &gpuCount, nullptr);
     if (err) {
         SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err);
-        vkDestroyInstance(inst, nullptr);
+        grVkDestroyInstance(inst, nullptr);
         return nullptr;
     }
-    SkASSERT(gpuCount > 0);
+    if (!gpuCount) {
+        SkDebugf("vkEnumeratePhysicalDevices returned no supported devices.\n");
+        grVkDestroyInstance(inst, nullptr);
+        return nullptr;
+    }
     // Just returning the first physical device instead of getting the whole array.
     // TODO: find best match for our needs
     gpuCount = 1;
-    err = vkEnumeratePhysicalDevices(inst, &gpuCount, &physDev);
+    err = grVkEnumeratePhysicalDevices(inst, &gpuCount, &physDev);
     if (err) {
         SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err);
-        vkDestroyInstance(inst, nullptr);
+        grVkDestroyInstance(inst, nullptr);
         return nullptr;
     }
 
     // query to get the initial queue props size
     uint32_t queueCount;
-    vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
-    SkASSERT(queueCount >= 1);
+    grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
+    if (!queueCount) {
+        SkDebugf("vkGetPhysicalDeviceQueueFamilyProperties returned no queues.\n");
+        grVkDestroyInstance(inst, nullptr);
+        return nullptr;
+    }
 
     SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties));
     // now get the actual queue props
     VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get();
 
-    vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueProps);
+    grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueProps);
 
     // iterate to find the graphics queue
     uint32_t graphicsQueueIndex = queueCount;
@@ -153,10 +185,14 @@
             break;
         }
     }
-    SkASSERT(graphicsQueueIndex < queueCount);
+    if (graphicsQueueIndex == queueCount) {
+        SkDebugf("Could not find any supported graphics queues.\n");
+        grVkDestroyInstance(inst, nullptr);
+        return nullptr;
+    }
 
     // iterate to find the present queue, if needed
-    uint32_t presentQueueIndex = graphicsQueueIndex;
+    uint32_t presentQueueIndex = queueCount;
     if (presentQueueIndexPtr && canPresent) {
         for (uint32_t i = 0; i < queueCount; i++) {
             if (canPresent(inst, physDev, i)) {
@@ -164,8 +200,16 @@
                 break;
             }
         }
-        SkASSERT(presentQueueIndex < queueCount);
+        if (presentQueueIndex == queueCount) {
+            SkDebugf("Could not find any supported present queues.\n");
+            grVkDestroyInstance(inst, nullptr);
+            return nullptr;
+        }
         *presentQueueIndexPtr = presentQueueIndex;
+    } else {
+        // Just setting this so we end up make a single queue for graphics since there was no
+        // request for a present queue.
+        presentQueueIndex = graphicsQueueIndex;
     }
 
     extensions.initDevice(kGrVkMinimumVersion, inst, physDev);
@@ -190,7 +234,7 @@
 
     // query to get the physical device properties
     VkPhysicalDeviceFeatures deviceFeatures;
-    vkGetPhysicalDeviceFeatures(physDev, &deviceFeatures);
+    grVkGetPhysicalDeviceFeatures(physDev, &deviceFeatures);
     // this looks like it would slow things down,
     // and we can't depend on it on all platforms
     deviceFeatures.robustBufferAccess = VK_FALSE;
@@ -242,15 +286,25 @@
         &deviceFeatures                          // ppEnabledFeatures
     };
 
-    err = vkCreateDevice(physDev, &deviceInfo, nullptr, &device);
+    err = grVkCreateDevice(physDev, &deviceInfo, nullptr, &device);
     if (err) {
         SkDebugf("CreateDevice failed: %d\n", err);
-        vkDestroyInstance(inst, nullptr);
+        grVkDestroyInstance(inst, nullptr);
+        return nullptr;
+    }
+
+    auto interface =
+        sk_make_sp<GrVkInterface>(getProc, inst, device, extensionFlags);
+    if (!interface->validate(extensionFlags)) {
+        SkDebugf("Vulkan interface validation failed\n");
+        grVkDeviceWaitIdle(device);
+        grVkDestroyDevice(device, nullptr);
+        grVkDestroyInstance(inst, nullptr);
         return nullptr;
     }
 
     VkQueue queue;
-    vkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
+    grVkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
 
     GrVkBackendContext* ctx = new GrVkBackendContext();
     ctx->fInstance = inst;
@@ -261,15 +315,20 @@
     ctx->fMinAPIVersion = kGrVkMinimumVersion;
     ctx->fExtensions = extensionFlags;
     ctx->fFeatures = featureFlags;
-    ctx->fInterface.reset(GrVkCreateInterface(inst, device, extensionFlags));
+    ctx->fInterface.reset(interface.release());
+    ctx->fOwnsInstanceAndDevice = true;
 
     return ctx;
 }
 
 GrVkBackendContext::~GrVkBackendContext() {
-    vkDeviceWaitIdle(fDevice);
-    vkDestroyDevice(fDevice, nullptr);
+    if (fInterface == nullptr || !fOwnsInstanceAndDevice) {
+        return;
+    }
+
+    fInterface->fFunctions.fDeviceWaitIdle(fDevice);
+    fInterface->fFunctions.fDestroyDevice(fDevice, nullptr);
     fDevice = VK_NULL_HANDLE;
-    vkDestroyInstance(fInstance, nullptr);
+    fInterface->fFunctions.fDestroyInstance(fInstance, nullptr);
     fInstance = VK_NULL_HANDLE;
 }
diff --git a/src/gpu/vk/GrVkBuffer.cpp b/src/gpu/vk/GrVkBuffer.cpp
index a0faa21..d0332b9 100644
--- a/src/gpu/vk/GrVkBuffer.cpp
+++ b/src/gpu/vk/GrVkBuffer.cpp
@@ -44,6 +44,8 @@
         case kCopyWrite_Type:
             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
             break;
+        case kTexel_Type:
+            bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
     }
     if (!desc.fDynamic) {
         bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
diff --git a/src/gpu/vk/GrVkBuffer.h b/src/gpu/vk/GrVkBuffer.h
index e58d5e4..6d0c1fd 100644
--- a/src/gpu/vk/GrVkBuffer.h
+++ b/src/gpu/vk/GrVkBuffer.h
@@ -43,6 +43,7 @@
         kVertex_Type,
         kIndex_Type,
         kUniform_Type,
+        kTexel_Type,
         kCopyRead_Type,
         kCopyWrite_Type,
     };
diff --git a/src/gpu/vk/GrVkBufferView.cpp b/src/gpu/vk/GrVkBufferView.cpp
new file mode 100644
index 0000000..b7e35c7
--- /dev/null
+++ b/src/gpu/vk/GrVkBufferView.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "GrVkBufferView.h"
+#include "GrVkGpu.h"
+#include "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(const 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
new file mode 100644
index 0000000..5f01925
--- /dev/null
+++ b/src/gpu/vk/GrVkBufferView.h
@@ -0,0 +1,40 @@
+/*
+ * 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 "GrTypes.h"
+
+#include "GrVkResource.h"
+
+#include "vk/GrVkDefines.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(const 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 bf343a9..e8a3978 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -6,7 +6,7 @@
  */
 
 #include "GrVkCaps.h"
-#include "GrRenderTarget.h"
+#include "GrRenderTargetProxy.h"
 #include "GrShaderCaps.h"
 #include "GrVkUtil.h"
 #include "vk/GrVkBackendContext.h"
@@ -20,7 +20,7 @@
     fSupportsCopiesAsDraws = false;
     fMustSubmitCommandsBeforeCopyOp = false;
     fMustSleepOnTearDown  = false;
-    fNewSecondaryCBOnPipelineChange = false;
+    fNewCBOnPipelineChange = false;
 
     /**************************************************************************
     * GrDrawTargetCaps fields
@@ -28,17 +28,15 @@
     fMipMapSupport = true;   // always available in Vulkan
     fSRGBSupport = true;   // always available in Vulkan
     fNPOTTextureTileSupport = true;  // always available in Vulkan
-    fTwoSidedStencilSupport = true;  // always available in Vulkan
-    fStencilWrapOpsSupport = true; // always available in Vulkan
     fDiscardRenderTargetSupport = true;
     fReuseScratchTextures = true; //TODO: figure this out
     fGpuTracingSupport = false; //TODO: figure this out
-    fCompressedTexSubImageSupport = false; //TODO: figure this out
     fOversizedStencilSupport = false; //TODO: figure this out
+    fInstanceAttribSupport = true;
 
     fUseDrawInsteadOfClear = false;
     fFenceSyncSupport = true;   // always available in Vulkan
-    fCrossContextTextureSupport = false; // TODO: Add thread-safe memory pools so we can enable this
+    fCrossContextTextureSupport = false;
 
     fMapBufferFlags = kNone_MapFlags; //TODO: figure this out
     fBufferMapThreshold = SK_MaxS32;  //TODO: figure this out
@@ -53,13 +51,18 @@
     this->init(contextOptions, vkInterface, physDev, featureFlags, extensionFlags);
 }
 
-bool GrVkCaps::initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const {
+bool GrVkCaps::initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                                  bool* rectsMustMatch, bool* disallowSubrect) const {
+    // Vk doesn't use rectsMustMatch or disallowSubrect. Always return false.
+    *rectsMustMatch = false;
+    *disallowSubrect = false;
+
     // We can always succeed here with either a CopyImage (none msaa src) or ResolveImage (msaa).
     // For CopyImage we can make a simple texture, for ResolveImage we require the dst to be a
     // render target as well.
     desc->fOrigin = src->origin();
     desc->fConfig = src->config();
-    if (src->numColorSamples() > 1 || (src->asTexture() && this->supportsCopiesAsDraws())) {
+    if (src->numColorSamples() > 1 || (src->asTextureProxy() && this->supportsCopiesAsDraws())) {
         desc->fFlags = kRenderTarget_GrSurfaceFlag;
     } else {
         // Just going to use CopyImage here
@@ -94,10 +97,17 @@
     }
 
     if (kNvidia_VkVendor == properties.vendorID) {
-        fSupportsCopiesAsDraws = true;
         fMustSubmitCommandsBeforeCopyOp = true;
     }
 
+    if (kQualcomm_VkVendor != properties.vendorID) {
+        fSupportsCopiesAsDraws = true;
+    }
+
+    if (fSupportsCopiesAsDraws) {
+        fCrossContextTextureSupport = true;
+    }
+
 #if defined(SK_BUILD_FOR_WIN)
     if (kNvidia_VkVendor == properties.vendorID) {
         fMustSleepOnTearDown = true;
@@ -140,6 +150,9 @@
     VkSampleCountFlags stencilSamples = properties.limits.framebufferStencilSampleCounts;
 
     fMaxColorSampleCount = get_max_sample_count(colorSamples);
+    if (kImagination_VkVendor == properties.vendorID) {
+        fMaxColorSampleCount = 0;
+    }
     fMaxStencilSampleCount = get_max_sample_count(stencilSamples);
 }
 
@@ -170,15 +183,20 @@
 
     fMapBufferFlags = kCanMap_MapFlag | kSubset_MapFlag;
 
-    fStencilWrapOpsSupport = true;
     fOversizedStencilSupport = true;
     fSampleShadingSupport = SkToBool(featureFlags & kSampleRateShading_GrVkFeatureFlag);
 
     // AMD seems to have issues binding new VkPipelines inside a secondary command buffer.
     // Current workaround is to use a different secondary command buffer for each new VkPipeline.
     if (kAMD_VkVendor == properties.vendorID) {
-        fNewSecondaryCBOnPipelineChange = true;
+        fNewCBOnPipelineChange = true;
     }
+
+#if defined(SK_CPU_X86)
+    if (kImagination_VkVendor == properties.vendorID) {
+        fSRGBSupport = false;
+    }
+#endif
 }
 
 void GrVkCaps::initShaderCaps(const VkPhysicalDeviceProperties& properties, uint32_t featureFlags) {
@@ -230,6 +248,7 @@
     }
 
     shaderCaps->fIntegerSupport = true;
+    shaderCaps->fVertexIDSupport = true;
 
     // Assume the minimum precisions mandated by the SPIR-V spec.
     shaderCaps->fShaderPrecisionVaries = true;
@@ -292,24 +311,22 @@
     for (int i = 0; i < kGrPixelConfigCnt; ++i) {
         VkFormat format;
         if (GrPixelConfigToVkFormat(static_cast<GrPixelConfig>(i), &format)) {
-            fConfigTable[i].init(interface, physDev, format);
+            if (!GrPixelConfigIsSRGB(static_cast<GrPixelConfig>(i)) || fSRGBSupport) {
+                fConfigTable[i].init(interface, physDev, format);
+            }
         }
     }
-
-    // We currently do not support compressed textures in Vulkan
-    const uint16_t kFlagsToRemove = ConfigInfo::kTextureable_Flag|ConfigInfo::kRenderable_Flag;
-    fConfigTable[kETC1_GrPixelConfig].fOptimalFlags &= ~kFlagsToRemove;
-    fConfigTable[kETC1_GrPixelConfig].fLinearFlags &= ~kFlagsToRemove;
 }
 
 void GrVkCaps::ConfigInfo::InitConfigFlags(VkFormatFeatureFlags vkFlags, uint16_t* flags) {
     if (SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & vkFlags) &&
         SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT & vkFlags)) {
         *flags = *flags | kTextureable_Flag;
-    }
 
-    if (SkToBool(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT & vkFlags)) {
-        *flags = *flags | kRenderable_Flag;
+        // Ganesh assumes that all renderable surfaces are also texturable
+        if (SkToBool(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT & vkFlags)) {
+            *flags = *flags | kRenderable_Flag;
+        }
     }
 
     if (SkToBool(VK_FORMAT_FEATURE_BLIT_SRC_BIT & vkFlags)) {
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 73ed367..f599d22 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -90,11 +90,11 @@
         return fMustSleepOnTearDown;
     }
 
-    // Returns true if while adding commands to secondary command buffers, we must make a new
-    // secondary command buffer everytime we want to bind a new VkPipeline. This is to work around a
-    // driver bug specifically on AMD.
-    bool newSecondaryCBOnPipelineChange() const {
-        return fNewSecondaryCBOnPipelineChange;
+    // Returns true if while adding commands to command buffers, we must make a new command buffer
+    // everytime we want to bind a new VkPipeline. This is true for both primary and secondary
+    // command buffers. This is to work around a driver bug specifically on AMD.
+    bool newCBOnPipelineChange() const {
+        return fNewCBOnPipelineChange;
     }
 
     /**
@@ -104,7 +104,8 @@
         return fPreferedStencilFormat;
     }
 
-    bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const override;
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                            bool* rectsMustMatch, bool* disallowSubrect) const override;
 
 private:
     enum VkVendor {
@@ -156,7 +157,7 @@
 
     bool fMustSleepOnTearDown;
 
-    bool fNewSecondaryCBOnPipelineChange;
+    bool fNewCBOnPipelineChange;
 
     typedef GrCaps INHERITED;
 };
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 64c1c88..ea0a02c 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -18,10 +18,10 @@
 #include "SkRect.h"
 
 void GrVkCommandBuffer::invalidateState() {
-    fBoundVertexBuffer = VK_NULL_HANDLE;
-    fBoundVertexBufferIsValid = false;
+    for (auto& boundInputBuffer : fBoundInputBuffers) {
+        boundInputBuffer = VK_NULL_HANDLE;
+    }
     fBoundIndexBuffer = VK_NULL_HANDLE;
-    fBoundIndexBufferIsValid = false;
 
     memset(&fCachedViewport, 0, sizeof(VkViewport));
     fCachedViewport.width = - 1.0f; // Viewport must have a width greater than 0
@@ -50,7 +50,7 @@
     this->onFreeGPUData(gpu);
 }
 
-void GrVkCommandBuffer::abandonSubResources() const {
+void GrVkCommandBuffer::abandonGPUData() const {
     for (int i = 0; i < fTrackedResources.count(); ++i) {
         fTrackedResources[i]->unrefAndAbandon();
     }
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index e156861..1f3c4a5 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -40,33 +40,36 @@
                          BarrierType barrierType,
                          void* barrier) const;
 
-    void bindVertexBuffer(GrVkGpu* gpu, GrVkVertexBuffer* vbuffer) {
+    static constexpr uint32_t kMaxInputBuffers = 2;
+
+    void bindInputBuffer(GrVkGpu* gpu, uint32_t binding, const GrVkVertexBuffer* vbuffer) {
         VkBuffer vkBuffer = vbuffer->buffer();
+        SkASSERT(VK_NULL_HANDLE != vkBuffer);
+        SkASSERT(binding < kMaxInputBuffers);
         // TODO: once vbuffer->offset() no longer always returns 0, we will need to track the offset
         // to know if we can skip binding or not.
-        if (!fBoundVertexBufferIsValid || vkBuffer != fBoundVertexBuffer) {
+        if (vkBuffer != fBoundInputBuffers[binding]) {
             VkDeviceSize offset = vbuffer->offset();
             GR_VK_CALL(gpu->vkInterface(), CmdBindVertexBuffers(fCmdBuffer,
-                                                                0,
+                                                                binding,
                                                                 1,
                                                                 &vkBuffer,
                                                                 &offset));
-            fBoundVertexBufferIsValid = true;
-            fBoundVertexBuffer = vkBuffer;
+            fBoundInputBuffers[binding] = vkBuffer;
             addResource(vbuffer->resource());
         }
     }
 
-    void bindIndexBuffer(GrVkGpu* gpu, GrVkIndexBuffer* ibuffer) {
+    void bindIndexBuffer(GrVkGpu* gpu, const GrVkIndexBuffer* ibuffer) {
         VkBuffer vkBuffer = ibuffer->buffer();
+        SkASSERT(VK_NULL_HANDLE != vkBuffer);
         // TODO: once ibuffer->offset() no longer always returns 0, we will need to track the offset
         // to know if we can skip binding or not.
-        if (!fBoundIndexBufferIsValid || vkBuffer != fBoundIndexBuffer) {
+        if (vkBuffer != fBoundIndexBuffer) {
             GR_VK_CALL(gpu->vkInterface(), CmdBindIndexBuffer(fCmdBuffer,
                                                               vkBuffer,
                                                               ibuffer->offset(),
                                                               VK_INDEX_TYPE_UINT16));
-            fBoundIndexBufferIsValid = true;
             fBoundIndexBuffer = vkBuffer;
             addResource(ibuffer->resource());
         }
@@ -146,8 +149,6 @@
             : fIsActive(false)
             , fActiveRenderPass(rp)
             , fCmdBuffer(cmdBuffer)
-            , fBoundVertexBufferIsValid(false)
-            , fBoundIndexBufferIsValid(false)
             , fNumResets(0) {
             fTrackedResources.setReserve(kInitialTrackedResourcesCount);
             fTrackedRecycledResources.setReserve(kInitialTrackedResourcesCount);
@@ -173,15 +174,12 @@
 
     void freeGPUData(const GrVkGpu* gpu) const override;
     virtual void onFreeGPUData(const GrVkGpu* gpu) const = 0;
-    void abandonSubResources() const override;
+    void abandonGPUData() const override;
 
     virtual void onReset(GrVkGpu* gpu) {}
 
-    VkBuffer                                fBoundVertexBuffer;
-    bool                                    fBoundVertexBufferIsValid;
-
-    VkBuffer                                fBoundIndexBuffer;
-    bool                                    fBoundIndexBufferIsValid;
+    VkBuffer fBoundInputBuffers[kMaxInputBuffers];
+    VkBuffer fBoundIndexBuffer;
 
     // When resetting the command buffer, we remove the tracked resources from their arrays, and
     // we prefer to not free all the memory every time so usually we just rewind. However, to avoid
diff --git a/src/gpu/vk/GrVkCopyManager.cpp b/src/gpu/vk/GrVkCopyManager.cpp
index 91c9b1a..cd5dba4 100644
--- a/src/gpu/vk/GrVkCopyManager.cpp
+++ b/src/gpu/vk/GrVkCopyManager.cpp
@@ -97,7 +97,8 @@
     uint32_t samplerVisibility = kFragment_GrShaderFlag;
     SkTArray<uint32_t> visibilityArray(&samplerVisibility, 1);
 
-    resourceProvider.getSamplerDescriptorSetHandle(visibilityArray, &fSamplerDSHandle);
+    resourceProvider.getSamplerDescriptorSetHandle(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+                                                   visibilityArray, &fSamplerDSHandle);
     dsLayout[GrVkUniformHandler::kSamplerDescSet] =
         resourceProvider.getSamplerDSLayout(fSamplerDSHandle);
 
@@ -143,10 +144,22 @@
                                         GrSurface* src,
                                         const SkIRect& srcRect,
                                         const SkIPoint& dstPoint) {
+    // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the
+    // swizzle.
+    if (gpu->caps()->shaderCaps()->configOutputSwizzle(src->config()) !=
+        gpu->caps()->shaderCaps()->configOutputSwizzle(dst->config())) {
+        return false;
+    }
+
     if (!gpu->vkCaps().supportsCopiesAsDraws()) {
         return false;
     }
 
+    if (gpu->vkCaps().newCBOnPipelineChange()) {
+        // We bind a new pipeline here for the copy so we must start a new command buffer.
+        gpu->finishFlush();
+    }
+
     GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(dst->asRenderTarget());
     if (!rt) {
         return false;
@@ -227,7 +240,7 @@
     descriptorWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
     descriptorWrites.pNext = nullptr;
     descriptorWrites.dstSet = uniformDS->descriptorSet();
-    descriptorWrites.dstBinding = GrVkUniformHandler::kVertexBinding;
+    descriptorWrites.dstBinding = GrVkUniformHandler::kGeometryBinding;
     descriptorWrites.dstArrayElement = 0;
     descriptorWrites.descriptorCount = 1;
     descriptorWrites.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@@ -364,7 +377,7 @@
     scissor.offset.y = 0;
     cmdBuffer->setScissor(gpu, 0, 1, &scissor);
 
-    cmdBuffer->bindVertexBuffer(gpu, fVertexBuffer.get());
+    cmdBuffer->bindInputBuffer(gpu, 0, fVertexBuffer.get());
     cmdBuffer->draw(gpu, 4, 1, 0, 0);
     cmdBuffer->endRenderPass(gpu);
 
diff --git a/src/gpu/vk/GrVkDescriptorSet.cpp b/src/gpu/vk/GrVkDescriptorSet.cpp
index 47a997f..3cb1035 100644
--- a/src/gpu/vk/GrVkDescriptorSet.cpp
+++ b/src/gpu/vk/GrVkDescriptorSet.cpp
@@ -28,7 +28,7 @@
     gpu->resourceProvider().recycleDescriptorSet(this, fHandle);
 }
 
-void GrVkDescriptorSet::abandonSubResources() const {
+void GrVkDescriptorSet::abandonGPUData() const {
     fPool->unrefAndAbandon();
 }
 
diff --git a/src/gpu/vk/GrVkDescriptorSet.h b/src/gpu/vk/GrVkDescriptorSet.h
index 69e2d44..cbeb98e 100644
--- a/src/gpu/vk/GrVkDescriptorSet.h
+++ b/src/gpu/vk/GrVkDescriptorSet.h
@@ -33,7 +33,7 @@
 
 private:
     void freeGPUData(const GrVkGpu* gpu) const override;
-    void abandonSubResources() const override;
+    void abandonGPUData() const override;
     void onRecycle(GrVkGpu* gpu) const override;
 
     VkDescriptorSet                          fDescSet;
diff --git a/src/gpu/vk/GrVkDescriptorSetManager.cpp b/src/gpu/vk/GrVkDescriptorSetManager.cpp
index f523cee..7d70b33 100644
--- a/src/gpu/vk/GrVkDescriptorSetManager.cpp
+++ b/src/gpu/vk/GrVkDescriptorSetManager.cpp
@@ -12,41 +12,47 @@
 #include "GrVkGpu.h"
 #include "GrVkUniformHandler.h"
 
-GrVkDescriptorSetManager::GrVkDescriptorSetManager(GrVkGpu* gpu,
-                                                   VkDescriptorType type,
-                                                   const GrVkUniformHandler* uniformHandler)
-    : fPoolManager(type, gpu, uniformHandler) {
-    if (type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
-        SkASSERT(uniformHandler);
-        for (int i = 0; i < uniformHandler->numSamplers(); ++i) {
-            fBindingVisibilities.push_back(uniformHandler->samplerVisibility(i));
+GrVkDescriptorSetManager* GrVkDescriptorSetManager::CreateUniformManager(GrVkGpu* gpu) {
+    SkSTArray<2, uint32_t> visibilities;
+    // We set the visibility of the first binding to all supported geometry processing shader
+    // stages (vertex, tesselation, geometry, etc.) and the second binding to the fragment
+    // shader.
+    uint32_t geomStages = kVertex_GrShaderFlag;
+    if (gpu->vkCaps().shaderCaps()->geometryShaderSupport()) {
+        geomStages |= kGeometry_GrShaderFlag;
+    }
+    visibilities.push_back(geomStages);
+    visibilities.push_back(kFragment_GrShaderFlag);
+    return new GrVkDescriptorSetManager(gpu, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, visibilities);
+}
+
+GrVkDescriptorSetManager* GrVkDescriptorSetManager::CreateSamplerManager(
+        GrVkGpu* gpu, VkDescriptorType type, const GrVkUniformHandler& uniformHandler) {
+    SkSTArray<4, uint32_t> visibilities;
+    if (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type) {
+        for (int i = 0 ; i < uniformHandler.numSamplers(); ++i) {
+            visibilities.push_back(uniformHandler.samplerVisibility(i));
         }
     } else {
-        SkASSERT(type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
-        // We set the visibility of the first binding to the vertex shader and the second to the
-        // fragment shader.
-        fBindingVisibilities.push_back(kVertex_GrShaderFlag);
-        fBindingVisibilities.push_back(kFragment_GrShaderFlag);
+        SkASSERT(type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
+        for (int i = 0 ; i < uniformHandler.numTexelBuffers(); ++i) {
+            visibilities.push_back(uniformHandler.texelBufferVisibility(i));
+        }
     }
+    return CreateSamplerManager(gpu, type, visibilities);
+}
+
+GrVkDescriptorSetManager* GrVkDescriptorSetManager::CreateSamplerManager(
+        GrVkGpu* gpu, VkDescriptorType type, const SkTArray<uint32_t>& visibilities) {
+    return new GrVkDescriptorSetManager(gpu, type, visibilities);
 }
 
 GrVkDescriptorSetManager::GrVkDescriptorSetManager(GrVkGpu* gpu,
                                                    VkDescriptorType type,
                                                    const SkTArray<uint32_t>& visibilities)
     : fPoolManager(type, gpu, visibilities) {
-    if (type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
-        for (int i = 0; i < visibilities.count(); ++i) {
-            fBindingVisibilities.push_back(visibilities[i]);
-        }
-    } else {
-        SkASSERT(type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
-        SkASSERT(2 == visibilities.count() &&
-                 kVertex_GrShaderFlag == visibilities[0] &&
-                 kFragment_GrShaderFlag == visibilities[1]);
-        // We set the visibility of the first binding to the vertex shader and the second to the
-        // fragment shader.
-        fBindingVisibilities.push_back(kVertex_GrShaderFlag);
-        fBindingVisibilities.push_back(kFragment_GrShaderFlag);
+    for (int i = 0; i < visibilities.count(); ++i) {
+        fBindingVisibilities.push_back(visibilities[i]);
     }
 }
 
@@ -106,6 +112,15 @@
                 return false;
             }
         }
+    } else if (VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == type) {
+        if (fBindingVisibilities.count() != uniHandler->numTexelBuffers()) {
+            return false;
+        }
+        for (int i = 0; i < uniHandler->numTexelBuffers(); ++i) {
+            if (uniHandler->texelBufferVisibility(i) != fBindingVisibilities[i]) {
+                return false;
+            }
+        }
     }
     return true;
 }
@@ -116,7 +131,8 @@
         return false;
     }
 
-    if (type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
+    if (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type ||
+        VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == type) {
         if (fBindingVisibilities.count() != visibilities.count()) {
             return false;
         }
@@ -149,47 +165,21 @@
 GrVkDescriptorSetManager::DescriptorPoolManager::DescriptorPoolManager(
         VkDescriptorType type,
         GrVkGpu* gpu,
-        const GrVkUniformHandler* uniformHandler)
-    : fDescType(type)
-    , fCurrentDescriptorCount(0)
-    , fPool(nullptr) {
-    this->init(gpu, type, uniformHandler, nullptr);
-}
-
-GrVkDescriptorSetManager::DescriptorPoolManager::DescriptorPoolManager(
-        VkDescriptorType type,
-        GrVkGpu* gpu,
         const SkTArray<uint32_t>& visibilities)
     : fDescType(type)
     , fCurrentDescriptorCount(0)
     , fPool(nullptr) {
-    this->init(gpu, type, nullptr, &visibilities);
-}
 
-void GrVkDescriptorSetManager::DescriptorPoolManager::init(GrVkGpu* gpu,
-                                                           VkDescriptorType type,
-                                                           const GrVkUniformHandler* uniformHandler,
-                                                           const SkTArray<uint32_t>* visibilities) {
-    if (type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
-        SkASSERT(SkToBool(uniformHandler) != SkToBool(visibilities));
-        uint32_t numSamplers;
-        if (uniformHandler) {
-            numSamplers = (uint32_t)uniformHandler->numSamplers();
-        } else {
-            numSamplers = (uint32_t)visibilities->count();
-        }
 
+    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[numSamplers]);
-        for (uint32_t i = 0; i < numSamplers; ++i) {
-            uint32_t visibility;
-            if (uniformHandler) {
-                visibility = uniformHandler->samplerVisibility(i);
-            } else {
-                visibility = (*visibilities)[i];
-            }
+                new VkDescriptorSetLayoutBinding[numBindings]);
+        for (uint32_t i = 0; i < numBindings; ++i) {
+            uint32_t visibility = visibilities[i];
             dsSamplerBindings[i].binding = i;
-            dsSamplerBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+            dsSamplerBindings[i].descriptorType = type;
             dsSamplerBindings[i].descriptorCount = 1;
             dsSamplerBindings[i].stageFlags = visibility_to_vk_stage_flags(visibility);
             dsSamplerBindings[i].pImmutableSamplers = nullptr;
@@ -200,34 +190,34 @@
         dsSamplerLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
         dsSamplerLayoutCreateInfo.pNext = nullptr;
         dsSamplerLayoutCreateInfo.flags = 0;
-        dsSamplerLayoutCreateInfo.bindingCount = numSamplers;
+        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 = numSamplers ? dsSamplerBindings.get() : nullptr;
+        dsSamplerLayoutCreateInfo.pBindings = numBindings ? dsSamplerBindings.get() : nullptr;
 
         GR_VK_CALL_ERRCHECK(gpu->vkInterface(),
                             CreateDescriptorSetLayout(gpu->device(),
                                                       &dsSamplerLayoutCreateInfo,
                                                       nullptr,
                                                       &fDescLayout));
-        fDescCountPerSet = numSamplers;
+        fDescCountPerSet = visibilities.count();
     } else {
-        SkASSERT(type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+        SkASSERT(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER == type);
+        GR_STATIC_ASSERT(2 == kUniformDescPerSet);
+        SkASSERT(kUniformDescPerSet == visibilities.count());
         // Create Uniform Buffer Descriptor
-        // The vertex uniform buffer will have binding 0 and the fragment binding 1.
+        static const uint32_t bindings[kUniformDescPerSet] =
+                { GrVkUniformHandler::kGeometryBinding, GrVkUniformHandler::kFragBinding };
         VkDescriptorSetLayoutBinding dsUniBindings[kUniformDescPerSet];
-        memset(&dsUniBindings, 0, 2 * sizeof(VkDescriptorSetLayoutBinding));
-        dsUniBindings[0].binding = GrVkUniformHandler::kVertexBinding;
-        dsUniBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-        dsUniBindings[0].descriptorCount = 1;
-        dsUniBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
-        dsUniBindings[0].pImmutableSamplers = nullptr;
-        dsUniBindings[1].binding = GrVkUniformHandler::kFragBinding;
-        dsUniBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-        dsUniBindings[1].descriptorCount = 1;
-        dsUniBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
-        dsUniBindings[1].pImmutableSamplers = nullptr;
+        memset(&dsUniBindings, 0, kUniformDescPerSet * sizeof(VkDescriptorSetLayoutBinding));
+        for (int i = 0; i < kUniformDescPerSet; ++i) {
+            dsUniBindings[i].binding = bindings[i];
+            dsUniBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+            dsUniBindings[i].descriptorCount = 1;
+            dsUniBindings[i].stageFlags = visibility_to_vk_stage_flags(visibilities[i]);
+            dsUniBindings[i].pImmutableSamplers = nullptr;
+        }
 
         VkDescriptorSetLayoutCreateInfo uniformLayoutCreateInfo;
         memset(&uniformLayoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
diff --git a/src/gpu/vk/GrVkDescriptorSetManager.h b/src/gpu/vk/GrVkDescriptorSetManager.h
index 84dd29e..6c545cc 100644
--- a/src/gpu/vk/GrVkDescriptorSetManager.h
+++ b/src/gpu/vk/GrVkDescriptorSetManager.h
@@ -26,13 +26,11 @@
 public:
     GR_DEFINE_RESOURCE_HANDLE_CLASS(Handle);
 
-    GrVkDescriptorSetManager(GrVkGpu* gpu,
-                             VkDescriptorType,
-                             const GrVkUniformHandler* handler = nullptr);
-
-    GrVkDescriptorSetManager(GrVkGpu* gpu,
-                             VkDescriptorType,
-                             const SkTArray<uint32_t>& visibilities);
+    static GrVkDescriptorSetManager* CreateUniformManager(GrVkGpu* gpu);
+    static GrVkDescriptorSetManager* CreateSamplerManager(GrVkGpu* gpu, VkDescriptorType type,
+                                                          const GrVkUniformHandler&);
+    static GrVkDescriptorSetManager* CreateSamplerManager(GrVkGpu* gpu, VkDescriptorType type,
+                                                          const SkTArray<uint32_t>& visibilities);
 
     ~GrVkDescriptorSetManager() {}
 
@@ -52,8 +50,6 @@
 private:
     struct DescriptorPoolManager {
         DescriptorPoolManager(VkDescriptorType type, GrVkGpu* gpu,
-                              const GrVkUniformHandler* handler = nullptr);
-        DescriptorPoolManager(VkDescriptorType type, GrVkGpu* gpu,
                               const SkTArray<uint32_t>& visibilities);
 
 
@@ -81,12 +77,14 @@
             kStartNumDescriptors = 16, // must be less than kMaxUniformDescriptors
         };
 
-        void init(GrVkGpu* gpu, VkDescriptorType type, const GrVkUniformHandler* uniformHandler,
-                  const SkTArray<uint32_t>* visibilities);
-
         void getNewPool(GrVkGpu* gpu);
     };
 
+    GrVkDescriptorSetManager(GrVkGpu* gpu,
+                             VkDescriptorType,
+                             const SkTArray<uint32_t>& visibilities);
+
+
     DescriptorPoolManager                    fPoolManager;
     SkTArray<const GrVkDescriptorSet*, true> fFreeSets;
     SkSTArray<4, uint32_t>                   fBindingVisibilities;
diff --git a/src/gpu/vk/GrVkExtensions.cpp b/src/gpu/vk/GrVkExtensions.cpp
index 03b4530..17737f1 100644
--- a/src/gpu/vk/GrVkExtensions.cpp
+++ b/src/gpu/vk/GrVkExtensions.cpp
@@ -30,17 +30,21 @@
     return idx;
 }
 
-#define GET_PROC_LOCAL(inst, F) PFN_vk ## F F = (PFN_vk ## F) vkGetInstanceProcAddr(inst, "vk" #F)
+#define GET_PROC_LOCAL(F, inst, device) PFN_vk ## F F = (PFN_vk ## F) fGetProc("vk" #F, inst, device)
 
 static uint32_t remove_patch_version(uint32_t specVersion) {
     return (specVersion >> 12) << 12;
 }
 
 bool GrVkExtensions::initInstance(uint32_t specVersion) {
+    if (fGetProc == nullptr) {
+        return false;
+    }
+
     uint32_t nonPatchVersion = remove_patch_version(specVersion);
 
-    GET_PROC_LOCAL(nullptr, EnumerateInstanceExtensionProperties);
-    GET_PROC_LOCAL(nullptr, EnumerateInstanceLayerProperties);
+    GET_PROC_LOCAL(EnumerateInstanceExtensionProperties, VK_NULL_HANDLE, VK_NULL_HANDLE);
+    GET_PROC_LOCAL(EnumerateInstanceLayerProperties, VK_NULL_HANDLE, VK_NULL_HANDLE);
 
     SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
 
@@ -126,10 +130,14 @@
 }
 
 bool GrVkExtensions::initDevice(uint32_t specVersion, VkInstance inst, VkPhysicalDevice physDev) {
+    if (fGetProc == nullptr) {
+        return false;
+    }
+
     uint32_t nonPatchVersion = remove_patch_version(specVersion);
 
-    GET_PROC_LOCAL(inst, EnumerateDeviceExtensionProperties);
-    GET_PROC_LOCAL(inst, EnumerateDeviceLayerProperties);
+    GET_PROC_LOCAL(EnumerateDeviceExtensionProperties, inst, VK_NULL_HANDLE);
+    GET_PROC_LOCAL(EnumerateDeviceLayerProperties, inst, VK_NULL_HANDLE);
 
     SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
 
diff --git a/src/gpu/vk/GrVkExtensions.h b/src/gpu/vk/GrVkExtensions.h
index 2decd15..8de28f4 100644
--- a/src/gpu/vk/GrVkExtensions.h
+++ b/src/gpu/vk/GrVkExtensions.h
@@ -11,6 +11,7 @@
 #include "../private/SkTArray.h"
 #include "SkString.h"
 #include "vk/GrVkDefines.h"
+#include "vk/GrVkInterface.h"
 
 /**
  * This helper queries the Vulkan driver for available extensions and layers, remembers them,
@@ -18,7 +19,9 @@
  */
 class SK_API GrVkExtensions {
 public:
-    GrVkExtensions() : fInstanceExtensionStrings(new SkTArray<SkString>)
+    GrVkExtensions(GrVkInterface::GetProc getProc)
+                     : fGetProc(getProc)
+                     , fInstanceExtensionStrings(new SkTArray<SkString>)
                      , fDeviceExtensionStrings(new SkTArray<SkString>)
                      , fInstanceLayerStrings(new SkTArray<SkString>)
                      , fDeviceLayerStrings(new SkTArray<SkString>) {}
@@ -37,6 +40,7 @@
     void print(const char* sep = "\n") const;
 
 private:
+    GrVkInterface::GetProc fGetProc;
     std::unique_ptr<SkTArray<SkString>>  fInstanceExtensionStrings;
     std::unique_ptr<SkTArray<SkString>>  fDeviceExtensionStrings;
     std::unique_ptr<SkTArray<SkString>>  fInstanceLayerStrings;
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index b634116..f76f06c 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkGpu.h"
 
+#include "GrBackendSurface.h"
 #include "GrContextOptions.h"
 #include "GrGeometryProcessor.h"
 #include "GrGpuResourceCacheAccess.h"
@@ -26,6 +27,7 @@
 #include "GrVkRenderPass.h"
 #include "GrVkResourceProvider.h"
 #include "GrVkSemaphore.h"
+#include "GrVkTexelBuffer.h"
 #include "GrVkTexture.h"
 #include "GrVkTextureRenderTarget.h"
 #include "GrVkTransferBuffer.h"
@@ -39,6 +41,10 @@
 
 #include "SkSLCompiler.h"
 
+#if !defined(SK_BUILD_FOR_WIN)
+#include <unistd.h>
+#endif // !defined(SK_BUILD_FOR_WIN)
+
 #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)
@@ -72,10 +78,7 @@
     const GrVkBackendContext* vkBackendContext =
         reinterpret_cast<const GrVkBackendContext*>(backendContext);
     if (!vkBackendContext) {
-        vkBackendContext = GrVkBackendContext::Create();
-        if (!vkBackendContext) {
-            return nullptr;
-        }
+        return nullptr;
     } else {
         vkBackendContext->ref();
     }
@@ -94,7 +97,8 @@
     : INHERITED(context)
     , fDevice(backendCtx->fDevice)
     , fQueue(backendCtx->fQueue)
-    , fResourceProvider(this) {
+    , fResourceProvider(this)
+    , fDisconnected(false) {
     fBackendContext.reset(backendCtx);
 
 #ifdef SK_ENABLE_VK_LAYERS
@@ -144,21 +148,21 @@
 
     // set up our heaps
     fHeaps[kLinearImage_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 16*1024*1024));
-    // We want the OptimalImage_Heap to use a SubAlloc_strategy but it occasionally causes the
-    // device to run out of memory. Most likely this is caused by fragmentation in the device heap
-    // and we can't allocate more. Until we get a fix moving this to SingleAlloc.
-    fHeaps[kOptimalImage_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 64*1024*1024));
+    fHeaps[kOptimalImage_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 64*1024*1024));
     fHeaps[kSmallOptimalImage_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 2*1024*1024));
     fHeaps[kVertexBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
     fHeaps[kIndexBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
     fHeaps[kUniformBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 256*1024));
+    fHeaps[kTexelBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
     fHeaps[kCopyReadBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSingleAlloc_Strategy, 0));
     fHeaps[kCopyWriteBuffer_Heap].reset(new GrVkHeap(this, GrVkHeap::kSubAlloc_Strategy, 16*1024*1024));
 }
 
-GrVkGpu::~GrVkGpu() {
-    fCurrentCmdBuffer->end(this);
-    fCurrentCmdBuffer->unref(this);
+void GrVkGpu::destroyResources() {
+    if (fCurrentCmdBuffer) {
+        fCurrentCmdBuffer->end(this);
+        fCurrentCmdBuffer->unref(this);
+    }
 
     // wait for all commands to finish
     fResourceProvider.checkCommandBuffers();
@@ -192,16 +196,49 @@
     // must call this just before we destroy the command pool and VkDevice
     fResourceProvider.destroyResources(VK_ERROR_DEVICE_LOST == res);
 
-    VK_CALL(DestroyCommandPool(fDevice, fCmdPool, nullptr));
-
-    delete fCompiler;
+    if (fCmdPool != VK_NULL_HANDLE) {
+        VK_CALL(DestroyCommandPool(fDevice, fCmdPool, nullptr));
+    }
 
 #ifdef SK_ENABLE_VK_LAYERS
     if (fCallback) {
         VK_CALL(DestroyDebugReportCallbackEXT(fBackendContext->fInstance, fCallback, nullptr));
-        fCallback = VK_NULL_HANDLE;
     }
 #endif
+
+}
+
+GrVkGpu::~GrVkGpu() {
+    if (!fDisconnected) {
+        this->destroyResources();
+    }
+    delete fCompiler;
+}
+
+
+void GrVkGpu::disconnect(DisconnectType type) {
+    INHERITED::disconnect(type);
+    if (!fDisconnected) {
+        if (DisconnectType::kCleanup == type) {
+            this->destroyResources();
+        } else {
+            fCurrentCmdBuffer->unrefAndAbandon();
+            for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) {
+                fSemaphoresToWaitOn[i]->unrefAndAbandon();
+            }
+            fCopyManager.abandonResources();
+
+            // must call this just before we destroy the command pool and VkDevice
+            fResourceProvider.abandonResources();
+        }
+        fSemaphoresToWaitOn.reset();
+#ifdef SK_ENABLE_VK_LAYERS
+        fCallback = VK_NULL_HANDLE;
+#endif
+        fCurrentCmdBuffer = nullptr;
+        fCmdPool = VK_NULL_HANDLE;
+        fDisconnected = true;
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -257,6 +294,13 @@
             SkASSERT(kStream_GrAccessPattern == accessPattern);
             buff = GrVkTransferBuffer::Create(this, size, GrVkBuffer::kCopyWrite_Type);
             break;
+        case kTexel_GrBufferType:
+            SkASSERT(kDynamic_GrAccessPattern == accessPattern);
+            buff = GrVkTexelBuffer::Create(this, size);
+            break;
+        case kDrawIndirect_GrBufferType:
+            SkFAIL("DrawIndirect Buffers not supported  in vulkan backend.");
+            return nullptr;
         default:
             SkFAIL("Unknown buffer type.");
             return nullptr;
@@ -271,10 +315,6 @@
 bool GrVkGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height,
                                    GrPixelConfig srcConfig, DrawPreference* drawPreference,
                                    WritePixelTempDrawInfo* tempDrawInfo) {
-    if (GrPixelConfigIsCompressed(dstSurface->config())) {
-        return false;
-    }
-
     GrRenderTarget* renderTarget = dstSurface->asRenderTarget();
 
     // Start off assuming no swizzling
@@ -291,27 +331,28 @@
     tempDrawInfo->fTempSurfaceDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
 
     if (dstSurface->config() == srcConfig) {
-        return true;
-    }
-
-    if (renderTarget && this->vkCaps().isConfigRenderable(renderTarget->config(),
-                                                          renderTarget->numColorSamples() > 1)) {
-        ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
-
-        bool configsAreRBSwaps = GrPixelConfigSwapRAndB(srcConfig) == dstSurface->config();
-
-        if (!this->vkCaps().isConfigTexturable(srcConfig) && configsAreRBSwaps) {
-            if (!this->vkCaps().isConfigTexturable(dstSurface->config())) {
-                return false;
-            }
-            tempDrawInfo->fTempSurfaceDesc.fConfig = dstSurface->config();
-            tempDrawInfo->fSwizzle = GrSwizzle::BGRA();
-            tempDrawInfo->fWriteConfig = dstSurface->config();
+        // We only support writing pixels to textures. Forcing a draw lets us write to pure RTs.
+        if (!dstSurface->asTexture()) {
+            ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
+        }
+        // If the dst is MSAA, we have to draw, or we'll just be writing to the resolve target.
+        if (renderTarget && renderTarget->numColorSamples() > 1) {
+            ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
         }
         return true;
     }
 
-    return false;
+    // Any config change requires a draw
+    ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
+
+    bool configsAreRBSwaps = GrPixelConfigSwapRAndB(srcConfig) == dstSurface->config();
+
+    if (!this->vkCaps().isConfigTexturable(srcConfig) && configsAreRBSwaps) {
+        tempDrawInfo->fTempSurfaceDesc.fConfig = dstSurface->config();
+        tempDrawInfo->fSwizzle = GrSwizzle::BGRA();
+        tempDrawInfo->fWriteConfig = dstSurface->config();
+    }
+    return true;
 }
 
 bool GrVkGpu::onWritePixels(GrSurface* surface,
@@ -334,49 +375,38 @@
     }
 
     bool success = false;
-    if (GrPixelConfigIsCompressed(vkTex->desc().fConfig)) {
-        // We check that config == desc.fConfig in GrGpu::getWritePixelsInfo()
-        SkASSERT(config == vkTex->desc().fConfig);
-        // TODO: add compressed texture support
-        // delete the following two lines and uncomment the two after that when ready
-        vkTex->unref();
-        return false;
-        //success = this->uploadCompressedTexData(vkTex->desc(), buffer, false, left, top, width,
-        //                                       height);
+    bool linearTiling = vkTex->isLinearTiled();
+    if (linearTiling) {
+        if (texels.count() > 1) {
+            SkDebugf("Can't upload mipmap data to linear tiled texture");
+            return false;
+        }
+        if (VK_IMAGE_LAYOUT_PREINITIALIZED != vkTex->currentLayout()) {
+            // Need to change the layout to general in order to perform a host write
+            vkTex->setImageLayout(this,
+                                  VK_IMAGE_LAYOUT_GENERAL,
+                                  VK_ACCESS_HOST_WRITE_BIT,
+                                  VK_PIPELINE_STAGE_HOST_BIT,
+                                  false);
+            this->submitCommandBuffer(kForce_SyncQueue);
+        }
+        success = this->uploadTexDataLinear(vkTex, left, top, width, height, config,
+                                            texels.begin()->fPixels, texels.begin()->fRowBytes);
     } else {
-        bool linearTiling = vkTex->isLinearTiled();
-        if (linearTiling) {
-            if (texels.count() > 1) {
-                SkDebugf("Can't upload mipmap data to linear tiled texture");
+        int newMipLevels = texels.count();
+        int currentMipLevels = vkTex->texturePriv().maxMipMapLevel() + 1;
+        if (newMipLevels > currentMipLevels) {
+            if (!vkTex->reallocForMipmap(this, newMipLevels)) {
                 return false;
             }
-            if (VK_IMAGE_LAYOUT_PREINITIALIZED != vkTex->currentLayout()) {
-                // Need to change the layout to general in order to perform a host write
-                vkTex->setImageLayout(this,
-                                      VK_IMAGE_LAYOUT_GENERAL,
-                                      VK_ACCESS_HOST_WRITE_BIT,
-                                      VK_PIPELINE_STAGE_HOST_BIT,
-                                      false);
-                this->submitCommandBuffer(kForce_SyncQueue);
-            }
-            success = this->uploadTexDataLinear(vkTex, left, top, width, height, config,
-                                                texels.begin()->fPixels, texels.begin()->fRowBytes);
-        } else {
-            int newMipLevels = texels.count();
-            int currentMipLevels = vkTex->texturePriv().maxMipMapLevel() + 1;
-            if (newMipLevels != currentMipLevels) {
-                if (!vkTex->reallocForMipmap(this, newMipLevels)) {
-                    return false;
-                }
-            }
-            success = this->uploadTexDataOptimal(vkTex, left, top, width, height, config, texels);
         }
+        success = this->uploadTexDataOptimal(vkTex, left, top, width, height, config, texels);
     }
 
     return success;
 }
 
-void GrVkGpu::resolveImage(GrVkRenderTarget* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
+void GrVkGpu::resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
                            const SkIPoint& dstPoint) {
     SkASSERT(dst);
     SkASSERT(src && src->numColorSamples() > 1 && src->msaaImage());
@@ -403,11 +433,20 @@
     resolveInfo.dstOffset = { dstPoint.fX, dstY, 0 };
     resolveInfo.extent = { (uint32_t)srcVkRect.width(), (uint32_t)srcVkRect.height(), 1 };
 
-    dst->setImageLayout(this,
-                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                        VK_ACCESS_TRANSFER_WRITE_BIT,
-                        VK_PIPELINE_STAGE_TRANSFER_BIT,
-                        false);
+    GrVkImage* dstImage;
+    GrRenderTarget* dstRT = dst->asRenderTarget();
+    if (dstRT) {
+        GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(dstRT);
+        dstImage = vkRT;
+    } else {
+        SkASSERT(dst->asTexture());
+        dstImage = static_cast<GrVkTexture*>(dst->asTexture());
+    }
+    dstImage->setImageLayout(this,
+                             VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                             VK_ACCESS_TRANSFER_WRITE_BIT,
+                             VK_PIPELINE_STAGE_TRANSFER_BIT,
+                             false);
 
     src->msaaImage()->setImageLayout(this,
                                      VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@@ -415,7 +454,7 @@
                                      VK_PIPELINE_STAGE_TRANSFER_BIT,
                                      false);
 
-    fCurrentCmdBuffer->resolveImage(this, *src->msaaImage(), *dst, 1, &resolveInfo);
+    fCurrentCmdBuffer->resolveImage(this, *src->msaaImage(), *dstImage, 1, &resolveInfo);
 }
 
 void GrVkGpu::internalResolveRenderTarget(GrRenderTarget* target, bool requiresSubmit) {
@@ -426,7 +465,7 @@
 
         const SkIRect& srcRect = rt->getResolveRect();
 
-        this->resolveImage(rt, rt, srcRect, SkIPoint::Make(srcRect.fLeft, srcRect.fTop));
+        this->resolveImage(target, rt, srcRect, SkIPoint::Make(srcRect.fLeft, srcRect.fTop));
 
         rt->flagAsResolved();
 
@@ -444,14 +483,9 @@
     SkASSERT(data);
     SkASSERT(tex->isLinearTiled());
 
-    // If we're uploading compressed data then we should be using uploadCompressedTexData
-    SkASSERT(!GrPixelConfigIsCompressed(dataConfig));
-
     size_t bpp = GrBytesPerPixel(dataConfig);
 
-    const GrSurfaceDesc& desc = tex->desc();
-
-    if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top,
+    if (!GrSurfacePriv::AdjustWritePixelParams(tex->width(), tex->height(), bpp, &left, &top,
                                                &width, &height, &data, &rowBytes)) {
         return false;
     }
@@ -474,7 +508,7 @@
                                                     &subres,
                                                     &layout));
 
-    int texTop = kBottomLeft_GrSurfaceOrigin == desc.fOrigin ? tex->height() - top - height : top;
+    int texTop = kBottomLeft_GrSurfaceOrigin == tex->origin() ? tex->height() - top - height : top;
     const GrVkAlloc& alloc = tex->alloc();
     VkDeviceSize offset = alloc.fOffset + texTop*layout.rowPitch + left*bpp;
     VkDeviceSize size = height*layout.rowPitch;
@@ -484,7 +518,7 @@
         return false;
     }
 
-    if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
+    if (kBottomLeft_GrSurfaceOrigin == tex->origin()) {
         // copy into buffer by rows
         const char* srcRow = reinterpret_cast<const char*>(data);
         char* dstRow = reinterpret_cast<char*>(mapPtr)+(height - 1)*layout.rowPitch;
@@ -513,15 +547,15 @@
     SkASSERT(1 == texels.count() ||
              (0 == left && 0 == top && width == tex->width() && height == tex->height()));
 
-    // If we're uploading compressed data then we should be using uploadCompressedTexData
-    SkASSERT(!GrPixelConfigIsCompressed(dataConfig));
+    // We assume that if the texture has mip levels, we either upload to all the levels or just the
+    // first.
+    SkASSERT(1 == texels.count() || texels.count() == (tex->texturePriv().maxMipMapLevel() + 1));
 
     if (width == 0 || height == 0) {
         return false;
     }
 
-    const GrSurfaceDesc& desc = tex->desc();
-    SkASSERT(this->caps()->isConfigTexturable(desc.fConfig));
+    SkASSERT(this->caps()->isConfigTexturable(tex->config()));
     size_t bpp = GrBytesPerPixel(dataConfig);
 
     // texels is const.
@@ -535,17 +569,15 @@
     }
 
     // Determine whether we need to flip when we copy into the buffer
-    bool flipY = (kBottomLeft_GrSurfaceOrigin == desc.fOrigin && !texelsShallowCopy.empty());
+    bool flipY = (kBottomLeft_GrSurfaceOrigin == tex->origin() && !texelsShallowCopy.empty());
 
     // adjust any params (left, top, currentWidth, currentHeight
     // find the combined size of all the mip levels and the relative offset of
     // each into the collective buffer
     // Do the first level separately because we may need to adjust width and height
     // (for the non-mipped case).
-    if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top,
-                                               &width,
-                                               &height,
-                                               &texelsShallowCopy[0].fPixels,
+    if (!GrSurfacePriv::AdjustWritePixelParams(tex->width(), tex->height(), bpp, &left, &top,
+                                               &width, &height, &texelsShallowCopy[0].fPixels,
                                                &texelsShallowCopy[0].fRowBytes)) {
         return false;
     }
@@ -561,9 +593,8 @@
     for (int currentMipLevel = 1; currentMipLevel < texelsShallowCopy.count(); currentMipLevel++) {
         currentWidth = SkTMax(1, currentWidth/2);
         currentHeight = SkTMax(1, currentHeight/2);
-        if (!GrSurfacePriv::AdjustWritePixelParams(desc.fWidth, desc.fHeight, bpp, &left, &top,
-                                                   &currentWidth,
-                                                   &currentHeight,
+        if (!GrSurfacePriv::AdjustWritePixelParams(tex->width(), tex->height(), bpp, &left, &top,
+                                                   &currentWidth, &currentHeight,
                                                    &texelsShallowCopy[currentMipLevel].fPixels,
                                                    &texelsShallowCopy[currentMipLevel].fRowBytes)) {
             return false;
@@ -580,13 +611,17 @@
     // allocate buffer to hold our mip data
     GrVkTransferBuffer* transferBuffer =
                    GrVkTransferBuffer::Create(this, combinedBufferSize, GrVkBuffer::kCopyRead_Type);
+    if(!transferBuffer)
+        return false;
 
     char* buffer = (char*) transferBuffer->map();
     SkTArray<VkBufferImageCopy> regions(texelsShallowCopy.count());
 
     currentWidth = width;
     currentHeight = height;
+    int layerHeight = tex->height();
     for (int currentMipLevel = 0; currentMipLevel < texelsShallowCopy.count(); currentMipLevel++) {
+        SkASSERT(1 == texelsShallowCopy.count() || currentHeight == layerHeight);
         const size_t trimRowBytes = currentWidth * bpp;
         const size_t rowBytes = texelsShallowCopy[currentMipLevel].fRowBytes;
 
@@ -610,11 +645,12 @@
         region.bufferRowLength = currentWidth;
         region.bufferImageHeight = currentHeight;
         region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, SkToU32(currentMipLevel), 0, 1 };
-        region.imageOffset = { left, flipY ? tex->height() - top - currentHeight : top, 0 };
+        region.imageOffset = { left, flipY ? layerHeight - top - currentHeight : top, 0 };
         region.imageExtent = { (uint32_t)currentWidth, (uint32_t)currentHeight, 1 };
 
         currentWidth = SkTMax(1, currentWidth/2);
         currentHeight = SkTMax(1, currentHeight/2);
+        layerHeight = currentHeight;
     }
 
     // no need to flush non-coherent memory, unmap will do that for us
@@ -635,13 +671,16 @@
                                          regions.count(),
                                          regions.begin());
     transferBuffer->unref();
+    if (1 == texelsShallowCopy.count()) {
+       tex->texturePriv().dirtyMipMaps(true);
+    }
 
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-GrTexture* GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                                    const SkTArray<GrMipLevel>& texels) {
+sk_sp<GrTexture> GrVkGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                          const SkTArray<GrMipLevel>& texels) {
     bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
 
     VkFormat pixelFormat;
@@ -657,21 +696,6 @@
         return nullptr;
     }
 
-    bool linearTiling = false;
-    if (SkToBool(desc.fFlags & kZeroCopy_GrSurfaceFlag)) {
-        // we can't have a linear texture with a mipmap
-        if (texels.count() > 1) {
-            SkDebugf("Trying to create linear tiled texture with mipmap");
-            return nullptr;
-        }
-        if (fVkCaps->isConfigTexturableLinearly(desc.fConfig) &&
-            (!renderTarget || fVkCaps->isConfigRenderableLinearly(desc.fConfig, false))) {
-            linearTiling = true;
-        } else {
-            return nullptr;
-        }
-    }
-
     VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
     if (renderTarget) {
         usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
@@ -685,9 +709,6 @@
     // texture.
     usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
 
-    VkFlags memProps = (!texels.empty() && linearTiling) ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT :
-                                                           VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
-
     // This ImageDesc refers to the texture that will be read by the client. Thus even if msaa is
     // requested, this ImageDesc describes the resolved texture. Therefore we always have samples set
     // to 1.
@@ -697,13 +718,13 @@
     imageDesc.fFormat = pixelFormat;
     imageDesc.fWidth = desc.fWidth;
     imageDesc.fHeight = desc.fHeight;
-    imageDesc.fLevels = linearTiling ? 1 : mipLevels;
+    imageDesc.fLevels = mipLevels;
     imageDesc.fSamples = 1;
-    imageDesc.fImageTiling = linearTiling ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
+    imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
     imageDesc.fUsageFlags = usageFlags;
-    imageDesc.fMemProps = memProps;
+    imageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 
-    GrVkTexture* tex;
+    sk_sp<GrVkTexture> tex;
     if (renderTarget) {
         tex = GrVkTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted, desc,
                                                                     imageDesc);
@@ -717,20 +738,26 @@
 
     if (!texels.empty()) {
         SkASSERT(texels.begin()->fPixels);
-        bool success;
-        if (linearTiling) {
-            success = this->uploadTexDataLinear(tex, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                                                texels.begin()->fPixels, texels.begin()->fRowBytes);
-        } else {
-            success = this->uploadTexDataOptimal(tex, 0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                                                 texels);
-        }
-        if (!success) {
+        if (!this->uploadTexDataOptimal(tex.get(), 0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                                        texels)) {
             tex->unref();
             return nullptr;
         }
     }
 
+    if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
+        VkClearColorValue zeroClearColor;
+        memset(&zeroClearColor, 0, sizeof(zeroClearColor));
+        VkImageSubresourceRange range;
+        range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+        range.baseArrayLayer = 0;
+        range.baseMipLevel = 0;
+        range.layerCount = 1;
+        range.levelCount = 1;
+        tex->setImageLayout(this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+                            VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, false);
+        this->currentCommandBuffer()->clearColorImage(this, tex.get(), &zeroClearColor, 1, &range);
+    }
     return tex;
 }
 
@@ -756,41 +783,38 @@
     }
 }
 
-sk_sp<GrTexture> GrVkGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc,
+sk_sp<GrTexture> GrVkGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
+                                               GrSurfaceOrigin origin,
+                                               GrBackendTextureFlags flags,
+                                               int sampleCnt,
                                                GrWrapOwnership ownership) {
-    if (0 == desc.fTextureHandle) {
+    const GrVkImageInfo* info = backendTex.getVkImageInfo();
+    if (!info) {
         return nullptr;
     }
 
     int maxSize = this->caps()->maxTextureSize();
-    if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
+    if (backendTex.width() > maxSize || backendTex.height() > maxSize) {
         return nullptr;
     }
 
-    const GrVkImageInfo* info = reinterpret_cast<const GrVkImageInfo*>(desc.fTextureHandle);
     if (VK_NULL_HANDLE == info->fImage || VK_NULL_HANDLE == info->fAlloc.fMemory) {
         return nullptr;
     }
-#ifdef SK_DEBUG
-    VkFormat format;
-    if (!GrPixelConfigToVkFormat(desc.fConfig, &format)) {
-        return nullptr;
-    }
-    SkASSERT(format == info->fFormat);
-#endif
+
+    SkASSERT(backendTex.config() == GrVkFormatToPixelConfig(info->fFormat));
 
     GrSurfaceDesc surfDesc;
-    // next line relies on GrBackendTextureDesc's flags matching GrTexture's
-    surfDesc.fFlags = (GrSurfaceFlags)desc.fFlags;
-    surfDesc.fWidth = desc.fWidth;
-    surfDesc.fHeight = desc.fHeight;
-    surfDesc.fConfig = desc.fConfig;
-    surfDesc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
-    bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
-    SkASSERT(!renderTarget || kAdoptAndCache_GrWrapOwnership != ownership);  // Not supported
+    // next line relies on GrBackendTextureFlags matching GrTexture's
+    surfDesc.fFlags = (GrSurfaceFlags)flags;
+    surfDesc.fWidth = backendTex.width();
+    surfDesc.fHeight = backendTex.height();
+    surfDesc.fConfig = backendTex.config();
+    surfDesc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
+    bool renderTarget = SkToBool(flags & kRenderTarget_GrBackendTextureFlag);
     // In GL, Chrome assumes all textures are BottomLeft
     // In VK, we don't have this restriction
-    surfDesc.fOrigin = resolve_origin(desc.fOrigin);
+    surfDesc.fOrigin = resolve_origin(origin);
 
     if (!renderTarget) {
         return GrVkTexture::MakeWrappedTexture(this, surfDesc, ownership, info);
@@ -798,25 +822,36 @@
     return GrVkTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, ownership, info);
 }
 
-sk_sp<GrRenderTarget> GrVkGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDesc& wrapDesc){
+sk_sp<GrRenderTarget> GrVkGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT,
+                                                         GrSurfaceOrigin origin){
+    // Currently the Vulkan backend does not support wrapping of msaa render targets directly. In
+    // general this is not an issue since swapchain images in vulkan are never multisampled. Thus if
+    // you want a multisampled RT it is best to wrap the swapchain images and then let Skia handle
+    // creating and owning the MSAA images.
+    if (backendRT.sampleCnt()) {
+        return nullptr;
+    }
 
-    const GrVkImageInfo* info =
-        reinterpret_cast<const GrVkImageInfo*>(wrapDesc.fRenderTargetHandle);
+    const GrVkImageInfo* info = backendRT.getVkImageInfo();
+    if (!info) {
+        return nullptr;
+    }
     if (VK_NULL_HANDLE == info->fImage) {
         return nullptr;
     }
 
     GrSurfaceDesc desc;
-    desc.fConfig = wrapDesc.fConfig;
-    desc.fFlags = kCheckAllocation_GrSurfaceFlag | kRenderTarget_GrSurfaceFlag;
-    desc.fWidth = wrapDesc.fWidth;
-    desc.fHeight = wrapDesc.fHeight;
-    desc.fSampleCnt = SkTMin(wrapDesc.fSampleCnt, this->caps()->maxSampleCount());
+    desc.fConfig = backendRT.config();
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = backendRT.width();
+    desc.fHeight = backendRT.height();
+    desc.fSampleCnt = 0;
 
-    desc.fOrigin = resolve_origin(wrapDesc.fOrigin);
+    SkASSERT(kDefault_GrSurfaceOrigin != origin);
+    desc.fOrigin = origin;
 
     sk_sp<GrVkRenderTarget> tgt = GrVkRenderTarget::MakeWrappedRenderTarget(this, desc, info);
-    if (tgt && wrapDesc.fStencilBits) {
+    if (tgt && backendRT.stencilBits()) {
         if (!createStencilAttachmentForRenderTarget(tgt.get(), desc.fWidth, desc.fHeight)) {
             return nullptr;
         }
@@ -824,6 +859,31 @@
     return tgt;
 }
 
+sk_sp<GrRenderTarget> GrVkGpu::onWrapBackendTextureAsRenderTarget(const GrBackendTexture& tex,
+                                                                  GrSurfaceOrigin origin,
+                                                                  int sampleCnt) {
+
+    const GrVkImageInfo* info = tex.getVkImageInfo();
+    if (!info) {
+        return nullptr;
+    }
+    if (VK_NULL_HANDLE == info->fImage) {
+        return nullptr;
+    }
+
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fConfig = tex.config();
+    desc.fWidth = tex.width();
+    desc.fHeight = tex.height();
+    desc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
+
+    desc.fOrigin = resolve_origin(origin);
+
+    sk_sp<GrVkRenderTarget> tgt = GrVkRenderTarget::MakeWrappedRenderTarget(this, desc, info);
+    return tgt;
+}
+
 void GrVkGpu::generateMipmap(GrVkTexture* tex) {
     // don't do anything for linearly tiled textures (can't have mipmaps)
     if (tex->isLinearTiled()) {
@@ -901,7 +961,7 @@
     }
 
     // setup memory barrier
-    SkASSERT(GrVkFormatToPixelConfig(tex->imageFormat(), nullptr));
+    SkASSERT(kUnknown_GrPixelConfig != GrVkFormatToPixelConfig(tex->imageFormat()));
     VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
     VkImageMemoryBarrier imageMemoryBarrier = {
         VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,          // sType
@@ -980,9 +1040,23 @@
         return false;
     }
 
-    // If there is no padding on dst we can do a single memcopy.
-    // This assumes the srcData comes in with no padding.
-    SkRectMemcpy(mapPtr, static_cast<size_t>(dstRowBytes), srcData, srcRowBytes, srcRowBytes, h);
+    if (srcData) {
+        // If there is no padding on dst we can do a single memcopy.
+        // This assumes the srcData comes in with no padding.
+        SkRectMemcpy(mapPtr, static_cast<size_t>(dstRowBytes),
+                     srcData, srcRowBytes, srcRowBytes, h);
+    } else {
+        // If there is no srcdata we always copy 0's into the textures so that it is initialized
+        // with some data.
+        if (srcRowBytes == static_cast<size_t>(dstRowBytes)) {
+            memset(mapPtr, 0, srcRowBytes * h);
+        } else {
+            for (int i = 0; i < h; ++i) {
+                memset(mapPtr, 0, srcRowBytes);
+                mapPtr = SkTAddOffset<void>(mapPtr, static_cast<size_t>(dstRowBytes));
+            }
+        }
+    }
     GrVkMemory::FlushMappedAlloc(gpu, alloc);
     GR_VK_CALL(gpu->vkInterface(), UnmapMemory(gpu->device(), alloc.fMemory));
     return true;
@@ -1057,172 +1131,170 @@
         return 0;
     }
 
-    if (srcData) {
-        size_t bpp = GrBytesPerPixel(config);
-        size_t rowCopyBytes = bpp * w;
-        if (linearTiling) {
-            const VkImageSubresource subres = {
-                VK_IMAGE_ASPECT_COLOR_BIT,
-                0,  // mipLevel
-                0,  // arraySlice
-            };
-            VkSubresourceLayout layout;
+    size_t bpp = GrBytesPerPixel(config);
+    size_t rowCopyBytes = bpp * w;
+    if (linearTiling) {
+        const VkImageSubresource subres = {
+            VK_IMAGE_ASPECT_COLOR_BIT,
+            0,  // mipLevel
+            0,  // arraySlice
+        };
+        VkSubresourceLayout layout;
 
-            VK_CALL(GetImageSubresourceLayout(fDevice, image, &subres, &layout));
+        VK_CALL(GetImageSubresourceLayout(fDevice, image, &subres, &layout));
 
-            if (!copy_testing_data(this, srcData, alloc, rowCopyBytes,
-                                   static_cast<size_t>(layout.rowPitch), h)) {
-                GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
-                VK_CALL(DestroyImage(fDevice, image, nullptr));
-                return 0;
-            }
-        } else {
-            SkASSERT(w && h);
+        if (!copy_testing_data(this, srcData, alloc, rowCopyBytes,
+                               static_cast<size_t>(layout.rowPitch), h)) {
+            GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
+            VK_CALL(DestroyImage(fDevice, image, nullptr));
+            return 0;
+        }
+    } else {
+        SkASSERT(w && h);
 
-            VkBuffer buffer;
-            VkBufferCreateInfo bufInfo;
-            memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
-            bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
-            bufInfo.flags = 0;
-            bufInfo.size = rowCopyBytes * h;
-            bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
-            bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-            bufInfo.queueFamilyIndexCount = 0;
-            bufInfo.pQueueFamilyIndices = nullptr;
-            VkResult err;
-            err = VK_CALL(CreateBuffer(fDevice, &bufInfo, nullptr, &buffer));
+        VkBuffer buffer;
+        VkBufferCreateInfo bufInfo;
+        memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
+        bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+        bufInfo.flags = 0;
+        bufInfo.size = rowCopyBytes * h;
+        bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+        bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+        bufInfo.queueFamilyIndexCount = 0;
+        bufInfo.pQueueFamilyIndices = nullptr;
+        VkResult err;
+        err = VK_CALL(CreateBuffer(fDevice, &bufInfo, nullptr, &buffer));
 
-            if (err) {
-                GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
-                VK_CALL(DestroyImage(fDevice, image, nullptr));
-                return 0;
-            }
+        if (err) {
+            GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
+            VK_CALL(DestroyImage(fDevice, image, nullptr));
+            return 0;
+        }
 
-            GrVkAlloc bufferAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
-            if (!GrVkMemory::AllocAndBindBufferMemory(this, buffer, GrVkBuffer::kCopyRead_Type,
-                                                      true, &bufferAlloc)) {
-                GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
-                VK_CALL(DestroyImage(fDevice, image, nullptr));
-                VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
-                return 0;
-            }
+        GrVkAlloc bufferAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
+        if (!GrVkMemory::AllocAndBindBufferMemory(this, buffer, GrVkBuffer::kCopyRead_Type,
+                                                  true, &bufferAlloc)) {
+            GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
+            VK_CALL(DestroyImage(fDevice, image, nullptr));
+            VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
+            return 0;
+        }
 
-            if (!copy_testing_data(this, srcData, bufferAlloc, rowCopyBytes, rowCopyBytes, h)) {
-                GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
-                VK_CALL(DestroyImage(fDevice, image, nullptr));
-                GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
-                VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
-                return 0;
-            }
+        if (!copy_testing_data(this, srcData, bufferAlloc, rowCopyBytes, rowCopyBytes, h)) {
+            GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
+            VK_CALL(DestroyImage(fDevice, image, nullptr));
+            GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
+            VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
+            return 0;
+        }
 
-            const VkCommandBufferAllocateInfo cmdInfo = {
-                VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
-                NULL,                                             // pNext
-                fCmdPool,                                         // commandPool
-                VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
-                1                                                 // bufferCount
-            };
+        const VkCommandBufferAllocateInfo cmdInfo = {
+            VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
+            NULL,                                             // pNext
+            fCmdPool,                                         // commandPool
+            VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
+            1                                                 // bufferCount
+        };
 
-            VkCommandBuffer cmdBuffer;
-            err = VK_CALL(AllocateCommandBuffers(fDevice, &cmdInfo, &cmdBuffer));
-            if (err) {
-                GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
-                VK_CALL(DestroyImage(fDevice, image, nullptr));
-                GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
-                VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
-                return 0;
-            }
+        VkCommandBuffer cmdBuffer;
+        err = VK_CALL(AllocateCommandBuffers(fDevice, &cmdInfo, &cmdBuffer));
+        if (err) {
+            GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
+            VK_CALL(DestroyImage(fDevice, image, nullptr));
+            GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
+            VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
+            return 0;
+        }
 
-            VkCommandBufferBeginInfo cmdBufferBeginInfo;
-            memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
-            cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-            cmdBufferBeginInfo.pNext = nullptr;
-            cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-            cmdBufferBeginInfo.pInheritanceInfo = nullptr;
+        VkCommandBufferBeginInfo cmdBufferBeginInfo;
+        memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
+        cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        cmdBufferBeginInfo.pNext = nullptr;
+        cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+        cmdBufferBeginInfo.pInheritanceInfo = nullptr;
 
-            err = VK_CALL(BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
-            SkASSERT(!err);
+        err = VK_CALL(BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
+        SkASSERT(!err);
 
-            // Set image layout and add barrier
-            VkImageMemoryBarrier barrier;
-            memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
-            barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-            barrier.pNext = nullptr;
-            barrier.srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(initialLayout);
-            barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-            barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
-            barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-            barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-            barrier.image = image;
-            barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0 , 1};
+        // Set image layout and add barrier
+        VkImageMemoryBarrier barrier;
+        memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
+        barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+        barrier.pNext = nullptr;
+        barrier.srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(initialLayout);
+        barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+        barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+        barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        barrier.image = image;
+        barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0 , 1};
 
-            VK_CALL(CmdPipelineBarrier(cmdBuffer,
-                                       GrVkMemory::LayoutToPipelineStageFlags(initialLayout),
-                                       VK_PIPELINE_STAGE_TRANSFER_BIT,
-                                       0,
-                                       0, nullptr,
-                                       0, nullptr,
-                                       1, &barrier));
-            initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+        VK_CALL(CmdPipelineBarrier(cmdBuffer,
+                                   GrVkMemory::LayoutToPipelineStageFlags(initialLayout),
+                                   VK_PIPELINE_STAGE_TRANSFER_BIT,
+                                   0,
+                                   0, nullptr,
+                                   0, nullptr,
+                                   1, &barrier));
+        initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
 
-            // Submit copy command
-            VkBufferImageCopy region;
-            memset(&region, 0, sizeof(VkBufferImageCopy));
-            region.bufferOffset = 0;
-            region.bufferRowLength = w;
-            region.bufferImageHeight = h;
-            region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
-            region.imageOffset = { 0, 0, 0 };
-            region.imageExtent = { (uint32_t)w, (uint32_t)h, 1 };
+        // Submit copy command
+        VkBufferImageCopy region;
+        memset(&region, 0, sizeof(VkBufferImageCopy));
+        region.bufferOffset = 0;
+        region.bufferRowLength = w;
+        region.bufferImageHeight = h;
+        region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
+        region.imageOffset = { 0, 0, 0 };
+        region.imageExtent = { (uint32_t)w, (uint32_t)h, 1 };
 
-            VK_CALL(CmdCopyBufferToImage(cmdBuffer, buffer, image, initialLayout, 1, &region));
+        VK_CALL(CmdCopyBufferToImage(cmdBuffer, buffer, image, initialLayout, 1, &region));
 
-            // End CommandBuffer
-            err = VK_CALL(EndCommandBuffer(cmdBuffer));
-            SkASSERT(!err);
+        // End CommandBuffer
+        err = VK_CALL(EndCommandBuffer(cmdBuffer));
+        SkASSERT(!err);
 
-            // Create Fence for queue
-            VkFence fence;
-            VkFenceCreateInfo fenceInfo;
-            memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
-            fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+        // Create Fence for queue
+        VkFence fence;
+        VkFenceCreateInfo fenceInfo;
+        memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
+        fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
 
-            err = VK_CALL(CreateFence(fDevice, &fenceInfo, nullptr, &fence));
-            SkASSERT(!err);
+        err = VK_CALL(CreateFence(fDevice, &fenceInfo, nullptr, &fence));
+        SkASSERT(!err);
 
-            VkSubmitInfo submitInfo;
-            memset(&submitInfo, 0, sizeof(VkSubmitInfo));
-            submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-            submitInfo.pNext = nullptr;
-            submitInfo.waitSemaphoreCount = 0;
-            submitInfo.pWaitSemaphores = nullptr;
-            submitInfo.pWaitDstStageMask = 0;
-            submitInfo.commandBufferCount = 1;
-            submitInfo.pCommandBuffers = &cmdBuffer;
-            submitInfo.signalSemaphoreCount = 0;
-            submitInfo.pSignalSemaphores = nullptr;
-            err = VK_CALL(QueueSubmit(this->queue(), 1, &submitInfo, fence));
-            SkASSERT(!err);
+        VkSubmitInfo submitInfo;
+        memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+        submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        submitInfo.pNext = nullptr;
+        submitInfo.waitSemaphoreCount = 0;
+        submitInfo.pWaitSemaphores = nullptr;
+        submitInfo.pWaitDstStageMask = 0;
+        submitInfo.commandBufferCount = 1;
+        submitInfo.pCommandBuffers = &cmdBuffer;
+        submitInfo.signalSemaphoreCount = 0;
+        submitInfo.pSignalSemaphores = nullptr;
+        err = VK_CALL(QueueSubmit(this->queue(), 1, &submitInfo, fence));
+        SkASSERT(!err);
 
-            err = VK_CALL(WaitForFences(fDevice, 1, &fence, true, UINT64_MAX));
-            if (VK_TIMEOUT == err) {
-                GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
-                VK_CALL(DestroyImage(fDevice, image, nullptr));
-                GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
-                VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
-                VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
-                VK_CALL(DestroyFence(fDevice, fence, nullptr));
-                SkDebugf("Fence failed to signal: %d\n", err);
-                SkFAIL("failing");
-            }
-            SkASSERT(!err);
-
-            // Clean up transfer resources
+        err = VK_CALL(WaitForFences(fDevice, 1, &fence, true, UINT64_MAX));
+        if (VK_TIMEOUT == err) {
+            GrVkMemory::FreeImageMemory(this, linearTiling, alloc);
+            VK_CALL(DestroyImage(fDevice, image, nullptr));
             GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
             VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
             VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
             VK_CALL(DestroyFence(fDevice, fence, nullptr));
+            SkDebugf("Fence failed to signal: %d\n", err);
+            SkFAIL("failing");
         }
+        SkASSERT(!err);
+
+        // Clean up transfer resources
+        GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
+        VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
+        VK_CALL(DestroyFence(fDevice, fence, nullptr));
     }
 
     GrVkImageInfo* info = new GrVkImageInfo;
@@ -1306,7 +1378,7 @@
                                        barrier);
 }
 
-void GrVkGpu::finishOpList() {
+void GrVkGpu::finishFlush() {
     // Submit the current command buffer to the Queue
     this->submitCommandBuffer(kSkip_SyncQueue);
 }
@@ -1361,7 +1433,7 @@
         }
     }
 
-    // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src 
+    // We require that all vulkan GrSurfaces have been created with transfer_dst and transfer_src
     // as image usage flags.
     if (src->origin() == dst->origin() &&
         GrBytesPerPixel(src->config()) == GrBytesPerPixel(dst->config())) {
@@ -1524,8 +1596,10 @@
         return false;
     }
 
-    // The dst must be a render target but not multisampled
-    if (!dst->asRenderTarget() || dst->asRenderTarget()->numColorSamples() > 1) {
+    // The dst must not be a multisampled render target, expect in the case where the dst is the
+    // resolve texture connected to the msaa src. We check for this in case we are copying a part of
+    // a surface to a different region in the same surface.
+    if (dst->asRenderTarget() && dst->asRenderTarget()->numColorSamples() > 1 && dst != src) {
         return false;
     }
 
@@ -1541,10 +1615,8 @@
                                    GrSurface* src,
                                    const SkIRect& srcRect,
                                    const SkIPoint& dstPoint) {
-    GrVkRenderTarget* dstRT = static_cast<GrVkRenderTarget*>(dst->asRenderTarget());
     GrVkRenderTarget* srcRT = static_cast<GrVkRenderTarget*>(src->asRenderTarget());
-    SkASSERT(dstRT && dstRT->numColorSamples() <= 1);
-    this->resolveImage(dstRT, srcRT, srcRect, dstPoint);
+    this->resolveImage(dst, srcRT, srcRect, dstPoint);
 }
 
 bool GrVkGpu::onCopySurface(GrSurface* dst,
@@ -1583,6 +1655,11 @@
         srcImage = static_cast<GrVkTexture*>(src->asTexture());
     }
 
+    // For borrowed textures, we *only* want to copy using draws (to avoid layout changes)
+    if (srcImage->isBorrowed()) {
+        return false;
+    }
+
     if (can_copy_image(dst, src, this)) {
         this->copySurfaceAsCopyImage(dst, src, dstImage, srcImage, srcRect, dstPoint);
         return true;
@@ -1600,7 +1677,7 @@
                                       int* effectiveSampleCnt, SamplePattern*) {
     // TODO: stub.
     SkASSERT(!this->caps()->sampleLocationsSupport());
-    *effectiveSampleCnt = rt->desc().fSampleCnt;
+    *effectiveSampleCnt = rt->numStencilSamples();
 }
 
 bool GrVkGpu::onGetReadPixelsInfo(GrSurface* srcSurface, int width, int height, size_t rowBytes,
@@ -1619,7 +1696,7 @@
 
     // Depends on why we need/want a temp draw. Start off assuming no change, the surface we read
     // from will be srcConfig and we will read readConfig pixels from it.
-    // Not that if we require a draw and return a non-renderable format for the temp surface the
+    // Note that if we require a draw and return a non-renderable format for the temp surface the
     // base class will fail for us.
     tempDrawInfo->fTempSurfaceDesc.fConfig = srcSurface->config();
     tempDrawInfo->fReadConfig = readConfig;
@@ -1628,14 +1705,12 @@
         return true;
     }
 
-    if (this->vkCaps().isConfigRenderable(readConfig, srcSurface->desc().fSampleCnt > 1)) {
-        ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
-        tempDrawInfo->fTempSurfaceDesc.fConfig = readConfig;
-        tempDrawInfo->fReadConfig = readConfig;
-        return true;
-    }
+    // Any config change requires a draw
+    ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
+    tempDrawInfo->fTempSurfaceDesc.fConfig = readConfig;
+    tempDrawInfo->fReadConfig = readConfig;
 
-    return false;
+    return true;
 }
 
 bool GrVkGpu::onReadPixels(GrSurface* surface,
@@ -1866,9 +1941,10 @@
     return GrVkSemaphore::Make(this);
 }
 
-void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore) {
+void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool /*flush*/) {
     GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore.get());
 
+    // We *always* flush, so ignore that parameter
     this->submitCommandBuffer(kSkip_SyncQueue, vkSem->getResource());
 }
 
@@ -1880,6 +1956,16 @@
     fSemaphoresToWaitOn.push_back(resource);
 }
 
-void GrVkGpu::flush() {
-    // We submit the command buffer to the queue whenever Ganesh is flushed, so nothing is needed
+sk_sp<GrSemaphore> GrVkGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
+    SkASSERT(texture);
+    GrVkTexture* vkTexture = static_cast<GrVkTexture*>(texture);
+    vkTexture->setImageLayout(this,
+                              VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+                              VK_ACCESS_SHADER_READ_BIT,
+                              VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+                              false);
+    this->submitCommandBuffer(kSkip_SyncQueue);
+
+    // The image layout change serves as a barrier, so no semaphore is needed
+    return nullptr;
 }
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index db77443..f81daea 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -22,7 +22,6 @@
 #include "vk/GrVkDefines.h"
 
 class GrPipeline;
-class GrNonInstancedMesh;
 
 class GrVkBufferImpl;
 class GrVkPipeline;
@@ -44,6 +43,8 @@
 
     ~GrVkGpu() override;
 
+    void disconnect(DisconnectType) override;
+
     const GrVkInterface* vkInterface() const { return fBackendContext->fInterface.get(); }
     const GrVkCaps& vkCaps() const { return *fVkCaps; }
 
@@ -97,8 +98,6 @@
             const GrGpuCommandBuffer::LoadAndStoreInfo& colorInfo,
             const GrGpuCommandBuffer::LoadAndStoreInfo& stencilInfo) override;
 
-    void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {}
-
     void addMemoryBarrier(VkPipelineStageFlags srcStageMask,
                           VkPipelineStageFlags dstStageMask,
                           bool byRegion,
@@ -126,17 +125,17 @@
                                       GrVkRenderTarget*,
                                       const SkIRect& bounds);
 
-    void finishOpList() override;
+    void finishFlush() override;
 
     GrFence SK_WARN_UNUSED_RESULT insertFence() override;
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
     sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override;
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override;
+    void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override;
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
 
-    void flush() override;
+    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
     void generateMipmap(GrVkTexture* tex);
 
@@ -154,6 +153,7 @@
         kVertexBuffer_Heap,
         kIndexBuffer_Heap,
         kUniformBuffer_Heap,
+        kTexelBuffer_Heap,
         kCopyReadBuffer_Heap,
         kCopyWriteBuffer_Heap,
 
@@ -169,18 +169,22 @@
 
     void onResetContext(uint32_t resetBits) override {}
 
-    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                               const SkTArray<GrMipLevel>&) override;
+    void destroyResources();
 
-    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, SkBudgeted,
-                                         const SkTArray<GrMipLevel>&) override { return NULL; }
+    sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                     const SkTArray<GrMipLevel>&) override;
 
-    sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) override;
+    sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
+                                          GrSurfaceOrigin,
+                                          GrBackendTextureFlags,
+                                          int sampleCnt,
+                                          GrWrapOwnership) override;
+    sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTarget&,
+                                                    GrSurfaceOrigin) override;
 
-    sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) override;
-    sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&) override {
-        return nullptr;
-    }
+    sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTexture&,
+                                                             GrSurfaceOrigin,
+                                                             int sampleCnt) override;
 
     GrBuffer* onCreateBuffer(size_t size, GrBufferType type, GrAccessPattern,
                              const void* data) override;
@@ -243,7 +247,7 @@
                               GrPixelConfig dataConfig,
                               const SkTArray<GrMipLevel>&);
 
-    void resolveImage(GrVkRenderTarget* dst,
+    void resolveImage(GrSurface* dst,
                       GrVkRenderTarget* src,
                       const SkIRect& srcRect,
                       const SkIPoint& dstPoint);
@@ -279,6 +283,10 @@
     // there is significant overhead to the first compile of any compiler.
     SkSL::Compiler* fCompiler;
 
+    // We need a bool to track whether or not we've already disconnected all the gpu resources from
+    // vulkan context.
+    bool fDisconnected;
+
     typedef GrGpu INHERITED;
 };
 
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 6b2cd8e..6f39cfa 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -425,27 +425,43 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrVkGpuCommandBuffer::bindGeometry(const GrPrimitiveProcessor& primProc,
-                                        const GrNonInstancedMesh& mesh) {
-    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+                                        const GrBuffer* indexBuffer,
+                                        const GrBuffer* vertexBuffer,
+                                        const GrBuffer* instanceBuffer) {
+    GrVkSecondaryCommandBuffer* currCmdBuf = fCommandBufferInfos[fCurrentCmdInfo].currentCmdBuf();
     // There is no need to put any memory barriers to make sure host writes have finished here.
     // When a command buffer is submitted to a queue, there is an implicit memory barrier that
     // occurs for all host writes. Additionally, BufferMemoryBarriers are not allowed inside of
     // an active RenderPass.
-    SkASSERT(!mesh.vertexBuffer()->isCPUBacked());
-    GrVkVertexBuffer* vbuf;
-    vbuf = (GrVkVertexBuffer*)mesh.vertexBuffer();
-    SkASSERT(vbuf);
-    SkASSERT(!vbuf->isMapped());
 
-    cbInfo.currentCmdBuf()->bindVertexBuffer(fGpu, vbuf);
+    // Here our vertex and instance inputs need to match the same 0-based bindings they were
+    // assigned in GrVkPipeline. That is, vertex first (if any) followed by instance.
+    uint32_t binding = 0;
 
-    if (mesh.isIndexed()) {
-        SkASSERT(!mesh.indexBuffer()->isCPUBacked());
-        GrVkIndexBuffer* ibuf = (GrVkIndexBuffer*)mesh.indexBuffer();
-        SkASSERT(ibuf);
-        SkASSERT(!ibuf->isMapped());
+    if (primProc.hasVertexAttribs()) {
+        SkASSERT(vertexBuffer);
+        SkASSERT(!vertexBuffer->isCPUBacked());
+        SkASSERT(!vertexBuffer->isMapped());
 
-        cbInfo.currentCmdBuf()->bindIndexBuffer(fGpu, ibuf);
+        currCmdBuf->bindInputBuffer(fGpu, binding++,
+                                    static_cast<const GrVkVertexBuffer*>(vertexBuffer));
+    }
+
+    if (primProc.hasInstanceAttribs()) {
+        SkASSERT(instanceBuffer);
+        SkASSERT(!instanceBuffer->isCPUBacked());
+        SkASSERT(!instanceBuffer->isMapped());
+
+        currCmdBuf->bindInputBuffer(fGpu, binding++,
+                                    static_cast<const GrVkVertexBuffer*>(instanceBuffer));
+    }
+
+    if (indexBuffer) {
+        SkASSERT(indexBuffer);
+        SkASSERT(!indexBuffer->isMapped());
+        SkASSERT(!indexBuffer->isCPUBacked());
+
+        currCmdBuf->bindIndexBuffer(fGpu, static_cast<const GrVkIndexBuffer*>(indexBuffer));
     }
 }
 
@@ -467,7 +483,7 @@
 
     if (!cbInfo.fIsEmpty &&
         fLastPipelineState && fLastPipelineState != pipelineState.get() &&
-        fGpu->vkCaps().newSecondaryCBOnPipelineChange()) {
+        fGpu->vkCaps().newCBOnPipelineChange()) {
         this->addAdditionalCommandBuffer();
     }
     fLastPipelineState = pipelineState.get();
@@ -481,11 +497,21 @@
     return pipelineState;
 }
 
-static void prepare_sampled_images(const GrProcessor& processor, GrVkGpu* gpu) {
+static void set_texture_layout(GrVkTexture* vkTexture, GrVkGpu* gpu) {
+    // TODO: If we ever decide to create the secondary command buffers ahead of time before we
+    // are actually going to submit them, we will need to track the sampled images and delay
+    // adding the layout change/barrier until we are ready to submit.
+    vkTexture->setImageLayout(gpu,
+                              VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+                              VK_ACCESS_SHADER_READ_BIT,
+                              VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+                              false);
+}
+
+static void prepare_sampled_images(const GrResourceIOProcessor& processor, GrVkGpu* gpu) {
     for (int i = 0; i < processor.numTextureSamplers(); ++i) {
-        const GrProcessor::TextureSampler& sampler = processor.textureSampler(i);
-        GrVkTexture* vkTexture = static_cast<GrVkTexture*>(sampler.texture());
-        SkASSERT(vkTexture);
+        const GrResourceIOProcessor::TextureSampler& sampler = processor.textureSampler(i);
+        GrVkTexture* vkTexture = static_cast<GrVkTexture*>(sampler.peekTexture());
 
         // We may need to resolve the texture first if it is also a render target
         GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(vkTexture->asRenderTarget());
@@ -501,15 +527,7 @@
                 vkTexture->texturePriv().dirtyMipMaps(false);
             }
         }
-
-        // TODO: If we ever decide to create the secondary command buffers ahead of time before we
-        // are actually going to submit them, we will need to track the sampled images and delay
-        // adding the layout change/barrier until we are ready to submit.
-        vkTexture->setImageLayout(gpu,
-                                  VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                                  VK_ACCESS_SHADER_READ_BIT,
-                                  VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
-                                  false);
+        set_texture_layout(vkTexture, gpu);
     }
 }
 
@@ -532,7 +550,9 @@
     while (const GrFragmentProcessor* fp = iter.next()) {
         prepare_sampled_images(*fp, fGpu);
     }
-    prepare_sampled_images(pipeline.getXferProcessor(), fGpu);
+    if (GrTexture* dstTexture = pipeline.peekDstTexture()) {
+        set_texture_layout(static_cast<GrVkTexture*>(dstTexture), fGpu);
+    }
 
     GrPrimitiveType primitiveType = meshes[0].primitiveType();
     sk_sp<GrVkPipelineState> pipelineState = this->prepareDrawState(pipeline,
@@ -542,52 +562,30 @@
         return;
     }
 
-    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
-
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
-        GrMesh::Iterator iter;
-        const GrNonInstancedMesh* nonIdxMesh = iter.init(mesh);
-        do {
-            if (nonIdxMesh->primitiveType() != primitiveType) {
-                // Technically we don't have to call this here (since there is a safety check in
-                // pipelineState:setData but this will allow for quicker freeing of resources if the
-                // pipelineState sits in a cache for a while.
-                pipelineState->freeTempResources(fGpu);
-                SkDEBUGCODE(pipelineState = nullptr);
-                primitiveType = nonIdxMesh->primitiveType();
-                pipelineState = this->prepareDrawState(pipeline,
-                                                       primProc,
-                                                       primitiveType);
-                if (!pipelineState) {
-                    return;
-                }
+        if (mesh.primitiveType() != primitiveType) {
+            // Technically we don't have to call this here (since there is a safety check in
+            // pipelineState:setData but this will allow for quicker freeing of resources if the
+            // pipelineState sits in a cache for a while.
+            pipelineState->freeTempResources(fGpu);
+            SkDEBUGCODE(pipelineState = nullptr);
+            primitiveType = mesh.primitiveType();
+            pipelineState = this->prepareDrawState(pipeline,
+                                                   primProc,
+                                                   primitiveType);
+            if (!pipelineState) {
+                return;
             }
-            SkASSERT(pipelineState);
-            this->bindGeometry(primProc, *nonIdxMesh);
+        }
 
-            if (nonIdxMesh->isIndexed()) {
-                cbInfo.currentCmdBuf()->drawIndexed(fGpu,
-                                                   nonIdxMesh->indexCount(),
-                                                   1,
-                                                   nonIdxMesh->startIndex(),
-                                                   nonIdxMesh->startVertex(),
-                                                   0);
-            } else {
-                cbInfo.currentCmdBuf()->draw(fGpu,
-                                            nonIdxMesh->vertexCount(),
-                                            1,
-                                            nonIdxMesh->startVertex(),
-                                            0);
-            }
-            cbInfo.fIsEmpty = false;
-
-            fGpu->stats()->incNumDraws();
-        } while ((nonIdxMesh = iter.next()));
+        SkASSERT(pipelineState);
+        mesh.sendToGpu(primProc, this);
     }
 
-    // Update command buffer bounds
+    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
     cbInfo.fBounds.join(bounds);
+    cbInfo.fIsEmpty = false;
 
     // Technically we don't have to call this here (since there is a safety check in
     // pipelineState:setData but this will allow for quicker freeing of resources if the
@@ -595,3 +593,34 @@
     pipelineState->freeTempResources(fGpu);
 }
 
+void GrVkGpuCommandBuffer::sendInstancedMeshToGpu(const GrPrimitiveProcessor& primProc,
+                                                  GrPrimitiveType,
+                                                  const GrBuffer* vertexBuffer,
+                                                  int vertexCount,
+                                                  int baseVertex,
+                                                  const GrBuffer* instanceBuffer,
+                                                  int instanceCount,
+                                                  int baseInstance) {
+    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+    this->bindGeometry(primProc, nullptr, vertexBuffer, instanceBuffer);
+    cbInfo.currentCmdBuf()->draw(fGpu, vertexCount, instanceCount, baseVertex, baseInstance);
+    fGpu->stats()->incNumDraws();
+}
+
+void GrVkGpuCommandBuffer::sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor& primProc,
+                                                         GrPrimitiveType,
+                                                         const GrBuffer* indexBuffer,
+                                                         int indexCount,
+                                                         int baseIndex,
+                                                         const GrBuffer* vertexBuffer,
+                                                         int baseVertex,
+                                                         const GrBuffer* instanceBuffer,
+                                                         int instanceCount,
+                                                         int baseInstance) {
+    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+    this->bindGeometry(primProc, indexBuffer, vertexBuffer, instanceBuffer);
+    cbInfo.currentCmdBuf()->drawIndexed(fGpu, indexCount, instanceCount,
+                                        baseIndex, baseVertex, baseInstance);
+    fGpu->stats()->incNumDraws();
+}
+
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index 519edb3..6836fac 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -11,17 +11,17 @@
 #include "GrGpuCommandBuffer.h"
 
 #include "GrColor.h"
+#include "GrMesh.h"
 #include "GrTypes.h"
 #include "GrVkPipelineState.h"
 
-class GrNonInstancedMesh;
 class GrVkGpu;
 class GrVkImage;
 class GrVkRenderPass;
 class GrVkRenderTarget;
 class GrVkSecondaryCommandBuffer;
 
-class GrVkGpuCommandBuffer : public GrGpuCommandBuffer {
+class GrVkGpuCommandBuffer : public GrGpuCommandBuffer, private GrMesh::SendToGpuImpl {
 public:
     GrVkGpuCommandBuffer(GrVkGpu* gpu,
                          const LoadAndStoreInfo& colorInfo,
@@ -46,7 +46,10 @@
     void onSubmit() override;
 
     // Bind vertex and index buffers
-    void bindGeometry(const GrPrimitiveProcessor&, const GrNonInstancedMesh&);
+    void bindGeometry(const GrPrimitiveProcessor&,
+                      const GrBuffer* indexBuffer,
+                      const GrBuffer* vertexBuffer,
+                      const GrBuffer* instanceBuffer);
 
     sk_sp<GrVkPipelineState> prepareDrawState(const GrPipeline&,
                                               const GrPrimitiveProcessor&,
@@ -58,6 +61,33 @@
                 int meshCount,
                 const SkRect& bounds) override;
 
+    // GrMesh::SendToGpuImpl methods. These issue the actual Vulkan draw commands.
+    // Marked final as a hint to the compiler to not use virtual dispatch.
+    void sendMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType primType,
+                       const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) final {
+        this->sendInstancedMeshToGpu(primProc, primType, vertexBuffer, vertexCount, baseVertex,
+                                     nullptr, 1, 0);
+    }
+
+    void sendIndexedMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType primType,
+                              const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                              uint16_t /*minIndexValue*/, uint16_t /*maxIndexValue*/,
+                              const GrBuffer* vertexBuffer, int baseVertex) final {
+        this->sendIndexedInstancedMeshToGpu(primProc, primType, indexBuffer, indexCount, baseIndex,
+                                            vertexBuffer, baseVertex, nullptr, 1, 0);
+    }
+
+    void sendInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                const GrBuffer* vertexBuffer, int vertexCount, int baseVertex,
+                                const GrBuffer* instanceBuffer, int instanceCount,
+                                int baseInstance) final;
+
+    void sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+                                       const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+                                       const GrBuffer* vertexBuffer, int baseVertex,
+                                       const GrBuffer* instanceBuffer, int instanceCount,
+                                       int baseInstance) final;
+
     void onClear(GrRenderTarget*, const GrFixedClip&, GrColor color) override;
 
     void onClearStencilClip(GrRenderTarget*, const GrFixedClip&, bool insideStencilMask) override;
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index c3302a2..5a9f69d 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -20,7 +20,7 @@
         case VK_FORMAT_D32_SFLOAT_S8_UINT:
             return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
         default:
-            SkASSERT(GrVkFormatToPixelConfig(format, nullptr));
+            SkASSERT(kUnknown_GrPixelConfig != GrVkFormatToPixelConfig(format));
             return VK_IMAGE_ASPECT_COLOR_BIT;
     }
 }
@@ -152,11 +152,23 @@
     }
 }
 
+void GrVkImage::setResourceRelease(ReleaseProc proc, ReleaseCtx ctx) {
+    // Forward the release proc on to GrVkImage::Resource
+    fResource->setRelease(proc, ctx);
+}
+
 void GrVkImage::Resource::freeGPUData(const GrVkGpu* gpu) const {
+    SkASSERT(!fReleaseProc);
     VK_CALL(gpu, DestroyImage(gpu->device(), fImage, nullptr));
     bool isLinear = (VK_IMAGE_TILING_LINEAR == fImageTiling);
     GrVkMemory::FreeImageMemory(gpu, isLinear, fAlloc);
 }
 
 void GrVkImage::BorrowedResource::freeGPUData(const GrVkGpu* gpu) const {
+    this->invokeReleaseProc();
 }
+
+void GrVkImage::BorrowedResource::abandonGPUData() const {
+    this->invokeReleaseProc();
+}
+
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 21728c0..57ab18a 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -48,6 +48,7 @@
     bool isLinearTiled() const {
         return SkToBool(VK_IMAGE_TILING_LINEAR == fInfo.fImageTiling);
     }
+    bool isBorrowed() const { return fIsBorrowed; }
 
     VkImageLayout currentLayout() const { return fInfo.fImageLayout; }
 
@@ -84,6 +85,12 @@
     // Destroys the internal VkImage and VkDeviceMemory in the GrVkImageInfo
     static void DestroyImageInfo(const GrVkGpu* gpu, GrVkImageInfo*);
 
+    // These match the definitions in SkImage, for whence they came
+    typedef void* ReleaseCtx;
+    typedef void (*ReleaseProc)(ReleaseCtx);
+
+    void setResourceRelease(ReleaseProc proc, ReleaseCtx ctx);
+
 protected:
     void releaseImage(const GrVkGpu* gpu);
     void abandonImage();
@@ -98,23 +105,42 @@
     public:
         Resource()
             : INHERITED()
+            , fReleaseProc(nullptr)
+            , fReleaseCtx(nullptr)
             , fImage(VK_NULL_HANDLE) {
             fAlloc.fMemory = VK_NULL_HANDLE;
             fAlloc.fOffset = 0;
         }
 
         Resource(VkImage image, const GrVkAlloc& alloc, VkImageTiling tiling)
-            : fImage(image), fAlloc(alloc), fImageTiling(tiling) {}
+            : fReleaseProc(nullptr)
+            , fReleaseCtx(nullptr)
+            , fImage(image)
+            , fAlloc(alloc)
+            , fImageTiling(tiling) {}
 
-        ~Resource() override {}
+        ~Resource() override {
+            SkASSERT(!fReleaseProc);
+        }
 
 #ifdef SK_TRACE_VK_RESOURCES
         void dumpInfo() const override {
             SkDebugf("GrVkImage: %d (%d refs)\n", fImage, this->getRefCnt());
         }
 #endif
+        void setRelease(ReleaseProc proc, ReleaseCtx ctx) const {
+            fReleaseProc = proc;
+            fReleaseCtx = ctx;
+        }
+    protected:
+        mutable ReleaseProc fReleaseProc;
+        mutable ReleaseCtx  fReleaseCtx;
+
     private:
         void freeGPUData(const GrVkGpu* gpu) const override;
+        void abandonGPUData() const override {
+            SkASSERT(!fReleaseProc);
+        }
 
         VkImage        fImage;
         GrVkAlloc      fAlloc;
@@ -130,7 +156,15 @@
             : Resource(image, alloc, tiling) {
         }
     private:
+        void invokeReleaseProc() const {
+            if (fReleaseProc) {
+                fReleaseProc(fReleaseCtx);
+                fReleaseProc = nullptr;
+            }
+        }
+
         void freeGPUData(const GrVkGpu* gpu) const override;
+        void abandonGPUData() const override;
     };
 
     const Resource* fResource;
diff --git a/src/gpu/vk/GrVkImageView.h b/src/gpu/vk/GrVkImageView.h
index 1398987..cd9f831 100644
--- a/src/gpu/vk/GrVkImageView.h
+++ b/src/gpu/vk/GrVkImageView.h
@@ -21,7 +21,7 @@
         kStencil_Type
     };
 
-    static const GrVkImageView* Create(const GrVkGpu* gpu, VkImage image, VkFormat format, 
+    static const GrVkImageView* Create(const GrVkGpu* gpu, VkImage image, VkFormat format,
                                        Type viewType, uint32_t miplevels);
 
     VkImageView imageView() const { return fImageView; }
diff --git a/src/gpu/vk/GrVkInterface.cpp b/src/gpu/vk/GrVkInterface.cpp
index 34f12b4..4188943 100644
--- a/src/gpu/vk/GrVkInterface.cpp
+++ b/src/gpu/vk/GrVkInterface.cpp
@@ -9,163 +9,183 @@
 #include "vk/GrVkBackendContext.h"
 #include "vk/GrVkUtil.h"
 
-GrVkInterface::GrVkInterface() {
+#define ACQUIRE_PROC(name, instance, device) fFunctions.f##name = \
+    reinterpret_cast<PFN_vk##name>(getProc("vk"#name, instance, device));
+
+GrVkInterface::GetProc make_unified_getter(const GrVkInterface::GetInstanceProc& iproc,
+                                           const GrVkInterface::GetDeviceProc& dproc) {
+    return [&iproc, &dproc](const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return dproc(device, proc_name);
+        }
+        return iproc(instance, proc_name);
+    };
 }
 
-#define GET_PROC_GLOBAL(F) functions->f ## F = (PFN_vk ## F) vkGetInstanceProcAddr(NULL, "vk" #F)
-#define GET_PROC(F) functions->f ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F)
-#define GET_PROC_LOCAL(inst, F) PFN_vk ## F F = (PFN_vk ## F) vkGetInstanceProcAddr(inst, "vk" #F)
-#define GET_DEV_PROC(F) functions->f ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F)
+GrVkInterface::GrVkInterface(const GetInstanceProc& getInstanceProc,
+                             const GetDeviceProc& getDeviceProc,
+                             VkInstance instance,
+                             VkDevice device,
+                             uint32_t extensionFlags)
+        : GrVkInterface(make_unified_getter(getInstanceProc, getDeviceProc),
+                        instance,
+                        device,
+                        extensionFlags) {}
 
-const GrVkInterface* GrVkCreateInterface(VkInstance instance, VkDevice device,
-                                         uint32_t extensionFlags) {
+GrVkInterface::GrVkInterface(GetProc getProc,
+                             VkInstance instance,
+                             VkDevice device,
+                             uint32_t extensionFlags) {
+    if (getProc == nullptr) {
+        return;
+    }
+    // Global/Loader Procs.
+    ACQUIRE_PROC(CreateInstance, VK_NULL_HANDLE, VK_NULL_HANDLE);
+    ACQUIRE_PROC(EnumerateInstanceExtensionProperties, VK_NULL_HANDLE, VK_NULL_HANDLE);
+    ACQUIRE_PROC(EnumerateInstanceLayerProperties, VK_NULL_HANDLE, VK_NULL_HANDLE);
 
-    GrVkInterface* interface = new GrVkInterface();
-    GrVkInterface::Functions* functions = &interface->fFunctions;
-
-    GET_PROC_GLOBAL(CreateInstance);
-    GET_PROC_GLOBAL(EnumerateInstanceExtensionProperties);
-    GET_PROC_GLOBAL(EnumerateInstanceLayerProperties);
-    GET_PROC(DestroyInstance);
-    GET_PROC(EnumeratePhysicalDevices);
-    GET_PROC(GetPhysicalDeviceFeatures);
-    GET_PROC(GetPhysicalDeviceFormatProperties);
-    GET_PROC(GetPhysicalDeviceImageFormatProperties);
-    GET_PROC(GetPhysicalDeviceProperties);
-    GET_PROC(GetPhysicalDeviceQueueFamilyProperties);
-    GET_PROC(GetPhysicalDeviceMemoryProperties);
-    GET_PROC(CreateDevice);
-    GET_PROC(DestroyDevice);
-    GET_PROC(EnumerateDeviceExtensionProperties);
-    GET_PROC(EnumerateDeviceLayerProperties);
-    GET_DEV_PROC(GetDeviceQueue);
-    GET_DEV_PROC(QueueSubmit);
-    GET_DEV_PROC(QueueWaitIdle);
-    GET_DEV_PROC(DeviceWaitIdle);
-    GET_DEV_PROC(AllocateMemory);
-    GET_DEV_PROC(FreeMemory);
-    GET_DEV_PROC(MapMemory);
-    GET_DEV_PROC(UnmapMemory);
-    GET_DEV_PROC(FlushMappedMemoryRanges);
-    GET_DEV_PROC(InvalidateMappedMemoryRanges);
-    GET_DEV_PROC(GetDeviceMemoryCommitment);
-    GET_DEV_PROC(BindBufferMemory);
-    GET_DEV_PROC(BindImageMemory);
-    GET_DEV_PROC(GetBufferMemoryRequirements);
-    GET_DEV_PROC(GetImageMemoryRequirements);
-    GET_DEV_PROC(GetImageSparseMemoryRequirements);
-    GET_PROC(GetPhysicalDeviceSparseImageFormatProperties);
-    GET_DEV_PROC(QueueBindSparse);
-    GET_DEV_PROC(CreateFence);
-    GET_DEV_PROC(DestroyFence);
-    GET_DEV_PROC(ResetFences);
-    GET_DEV_PROC(GetFenceStatus);
-    GET_DEV_PROC(WaitForFences);
-    GET_DEV_PROC(CreateSemaphore);
-    GET_DEV_PROC(DestroySemaphore);
-    GET_DEV_PROC(CreateEvent);
-    GET_DEV_PROC(DestroyEvent);
-    GET_DEV_PROC(GetEventStatus);
-    GET_DEV_PROC(SetEvent);
-    GET_DEV_PROC(ResetEvent);
-    GET_DEV_PROC(CreateQueryPool);
-    GET_DEV_PROC(DestroyQueryPool);
-    GET_DEV_PROC(GetQueryPoolResults);
-    GET_DEV_PROC(CreateBuffer);
-    GET_DEV_PROC(DestroyBuffer);
-    GET_DEV_PROC(CreateBufferView);
-    GET_DEV_PROC(DestroyBufferView);
-    GET_DEV_PROC(CreateImage);
-    GET_DEV_PROC(DestroyImage);
-    GET_DEV_PROC(GetImageSubresourceLayout);
-    GET_DEV_PROC(CreateImageView);
-    GET_DEV_PROC(DestroyImageView);
-    GET_DEV_PROC(CreateShaderModule);
-    GET_DEV_PROC(DestroyShaderModule);
-    GET_DEV_PROC(CreatePipelineCache);
-    GET_DEV_PROC(DestroyPipelineCache);
-    GET_DEV_PROC(GetPipelineCacheData);
-    GET_DEV_PROC(MergePipelineCaches);
-    GET_DEV_PROC(CreateGraphicsPipelines);
-    GET_DEV_PROC(CreateComputePipelines);
-    GET_DEV_PROC(DestroyPipeline);
-    GET_DEV_PROC(CreatePipelineLayout);
-    GET_DEV_PROC(DestroyPipelineLayout);
-    GET_DEV_PROC(CreateSampler);
-    GET_DEV_PROC(DestroySampler);
-    GET_DEV_PROC(CreateDescriptorSetLayout);
-    GET_DEV_PROC(DestroyDescriptorSetLayout);
-    GET_DEV_PROC(CreateDescriptorPool);
-    GET_DEV_PROC(DestroyDescriptorPool);
-    GET_DEV_PROC(ResetDescriptorPool);
-    GET_DEV_PROC(AllocateDescriptorSets);
-    GET_DEV_PROC(FreeDescriptorSets);
-    GET_DEV_PROC(UpdateDescriptorSets);
-    GET_DEV_PROC(CreateFramebuffer);
-    GET_DEV_PROC(DestroyFramebuffer);
-    GET_DEV_PROC(CreateRenderPass);
-    GET_DEV_PROC(DestroyRenderPass);
-    GET_DEV_PROC(GetRenderAreaGranularity);
-    GET_DEV_PROC(CreateCommandPool);
-    GET_DEV_PROC(DestroyCommandPool);
-    GET_DEV_PROC(ResetCommandPool);
-    GET_DEV_PROC(AllocateCommandBuffers);
-    GET_DEV_PROC(FreeCommandBuffers);
-    GET_DEV_PROC(BeginCommandBuffer);
-    GET_DEV_PROC(EndCommandBuffer);
-    GET_DEV_PROC(ResetCommandBuffer);
-    GET_DEV_PROC(CmdBindPipeline);
-    GET_DEV_PROC(CmdSetViewport);
-    GET_DEV_PROC(CmdSetScissor);
-    GET_DEV_PROC(CmdSetLineWidth);
-    GET_DEV_PROC(CmdSetDepthBias);
-    GET_DEV_PROC(CmdSetBlendConstants);
-    GET_DEV_PROC(CmdSetDepthBounds);
-    GET_DEV_PROC(CmdSetStencilCompareMask);
-    GET_DEV_PROC(CmdSetStencilWriteMask);
-    GET_DEV_PROC(CmdSetStencilReference);
-    GET_DEV_PROC(CmdBindDescriptorSets);
-    GET_DEV_PROC(CmdBindIndexBuffer);
-    GET_DEV_PROC(CmdBindVertexBuffers);
-    GET_DEV_PROC(CmdDraw);
-    GET_DEV_PROC(CmdDrawIndexed);
-    GET_DEV_PROC(CmdDrawIndirect);
-    GET_DEV_PROC(CmdDrawIndexedIndirect);
-    GET_DEV_PROC(CmdDispatch);
-    GET_DEV_PROC(CmdDispatchIndirect);
-    GET_DEV_PROC(CmdCopyBuffer);
-    GET_DEV_PROC(CmdCopyImage);
-    GET_DEV_PROC(CmdBlitImage);
-    GET_DEV_PROC(CmdCopyBufferToImage);
-    GET_DEV_PROC(CmdCopyImageToBuffer);
-    GET_DEV_PROC(CmdUpdateBuffer);
-    GET_DEV_PROC(CmdFillBuffer);
-    GET_DEV_PROC(CmdClearColorImage);
-    GET_DEV_PROC(CmdClearDepthStencilImage);
-    GET_DEV_PROC(CmdClearAttachments);
-    GET_DEV_PROC(CmdResolveImage);
-    GET_DEV_PROC(CmdSetEvent);
-    GET_DEV_PROC(CmdResetEvent);
-    GET_DEV_PROC(CmdWaitEvents);
-    GET_DEV_PROC(CmdPipelineBarrier);
-    GET_DEV_PROC(CmdBeginQuery);
-    GET_DEV_PROC(CmdEndQuery);
-    GET_DEV_PROC(CmdResetQueryPool);
-    GET_DEV_PROC(CmdWriteTimestamp);
-    GET_DEV_PROC(CmdCopyQueryPoolResults);
-    GET_DEV_PROC(CmdPushConstants);
-    GET_DEV_PROC(CmdBeginRenderPass);
-    GET_DEV_PROC(CmdNextSubpass);
-    GET_DEV_PROC(CmdEndRenderPass);
-    GET_DEV_PROC(CmdExecuteCommands);
+    // Instance Procs.
+    ACQUIRE_PROC(EnumeratePhysicalDevices, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceFeatures, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceFormatProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceImageFormatProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceQueueFamilyProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceMemoryProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(GetPhysicalDeviceSparseImageFormatProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(DestroyInstance, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(CreateDevice, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(DestroyDevice, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(EnumerateDeviceExtensionProperties, instance, VK_NULL_HANDLE);
+    ACQUIRE_PROC(EnumerateDeviceLayerProperties, instance, VK_NULL_HANDLE);
 
     if (extensionFlags & kEXT_debug_report_GrVkExtensionFlag) {
-        GET_PROC(CreateDebugReportCallbackEXT);
-        GET_PROC(DebugReportMessageEXT);
-        GET_PROC(DestroyDebugReportCallbackEXT);
+        // Also instance Procs.
+        ACQUIRE_PROC(CreateDebugReportCallbackEXT, instance, VK_NULL_HANDLE);
+        ACQUIRE_PROC(DebugReportMessageEXT, instance, VK_NULL_HANDLE);
+        ACQUIRE_PROC(DestroyDebugReportCallbackEXT, instance, VK_NULL_HANDLE);
     }
 
-    return interface;
+    // Device Procs.
+    ACQUIRE_PROC(GetDeviceQueue, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(QueueSubmit, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(QueueWaitIdle, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DeviceWaitIdle, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(AllocateMemory, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(FreeMemory, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(MapMemory, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(UnmapMemory, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(FlushMappedMemoryRanges, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(InvalidateMappedMemoryRanges, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetDeviceMemoryCommitment, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(BindBufferMemory, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(BindImageMemory, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetBufferMemoryRequirements, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetImageMemoryRequirements, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetImageSparseMemoryRequirements, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(QueueBindSparse, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateFence, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyFence, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(ResetFences, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetFenceStatus, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(WaitForFences, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateSemaphore, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroySemaphore, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateEvent, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyEvent, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetEventStatus, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(SetEvent, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(ResetEvent, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateQueryPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyQueryPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetQueryPoolResults, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateBufferView, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyBufferView, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetImageSubresourceLayout, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateImageView, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyImageView, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateShaderModule, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyShaderModule, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreatePipelineCache, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyPipelineCache, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetPipelineCacheData, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(MergePipelineCaches, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateGraphicsPipelines, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateComputePipelines, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyPipeline, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreatePipelineLayout, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyPipelineLayout, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateSampler, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroySampler, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateDescriptorSetLayout, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyDescriptorSetLayout, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateDescriptorPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyDescriptorPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(ResetDescriptorPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(AllocateDescriptorSets, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(FreeDescriptorSets, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(UpdateDescriptorSets, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateFramebuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyFramebuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateRenderPass, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyRenderPass, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(GetRenderAreaGranularity, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CreateCommandPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(DestroyCommandPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(ResetCommandPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(AllocateCommandBuffers, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(FreeCommandBuffers, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(BeginCommandBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(EndCommandBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(ResetCommandBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBindPipeline, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetViewport, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetScissor, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetLineWidth, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetDepthBias, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetBlendConstants, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetDepthBounds, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetStencilCompareMask, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetStencilWriteMask, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetStencilReference, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBindDescriptorSets, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBindIndexBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBindVertexBuffers, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdDraw, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdDrawIndexed, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdDrawIndirect, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdDrawIndexedIndirect, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdDispatch, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdDispatchIndirect, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdCopyBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdCopyImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBlitImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdCopyBufferToImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdCopyImageToBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdUpdateBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdFillBuffer, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdClearColorImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdClearDepthStencilImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdClearAttachments, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdResolveImage, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdSetEvent, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdResetEvent, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdWaitEvents, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdPipelineBarrier, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBeginQuery, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdEndQuery, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdResetQueryPool, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdWriteTimestamp, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdCopyQueryPoolResults, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdPushConstants, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdBeginRenderPass, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdNextSubpass, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdEndRenderPass, VK_NULL_HANDLE, device);
+    ACQUIRE_PROC(CmdExecuteCommands, VK_NULL_HANDLE, device);
 }
 
 #ifdef SK_DEBUG
diff --git a/src/gpu/vk/GrVkMemory.cpp b/src/gpu/vk/GrVkMemory.cpp
index 2764102..31dea10 100644
--- a/src/gpu/vk/GrVkMemory.cpp
+++ b/src/gpu/vk/GrVkMemory.cpp
@@ -40,14 +40,16 @@
         GrVkGpu::kVertexBuffer_Heap,
         GrVkGpu::kIndexBuffer_Heap,
         GrVkGpu::kUniformBuffer_Heap,
+        GrVkGpu::kTexelBuffer_Heap,
         GrVkGpu::kCopyReadBuffer_Heap,
         GrVkGpu::kCopyWriteBuffer_Heap,
     };
     GR_STATIC_ASSERT(0 == GrVkBuffer::kVertex_Type);
     GR_STATIC_ASSERT(1 == GrVkBuffer::kIndex_Type);
     GR_STATIC_ASSERT(2 == GrVkBuffer::kUniform_Type);
-    GR_STATIC_ASSERT(3 == GrVkBuffer::kCopyRead_Type);
-    GR_STATIC_ASSERT(4 == GrVkBuffer::kCopyWrite_Type);
+    GR_STATIC_ASSERT(3 == GrVkBuffer::kTexel_Type);
+    GR_STATIC_ASSERT(4 == GrVkBuffer::kCopyRead_Type);
+    GR_STATIC_ASSERT(5 == GrVkBuffer::kCopyWrite_Type);
 
     return kBufferToHeap[type];
 }
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 7c0aeb4..2732c6f 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -46,30 +46,41 @@
 }
 
 static void setup_vertex_input_state(const GrPrimitiveProcessor& primProc,
-                                     VkPipelineVertexInputStateCreateInfo* vertexInputInfo,
-                                     VkVertexInputBindingDescription* bindingDesc,
-                                     int maxBindingDescCount,
-                                     VkVertexInputAttributeDescription* attributeDesc) {
-    // for now we have only one vertex buffer and one binding
-    memset(bindingDesc, 0, sizeof(VkVertexInputBindingDescription));
-    bindingDesc->binding = 0;
-    bindingDesc->stride = (uint32_t)primProc.getVertexStride();
-    bindingDesc->inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+                                  VkPipelineVertexInputStateCreateInfo* vertexInputInfo,
+                                  SkSTArray<2, VkVertexInputBindingDescription, true>* bindingDescs,
+                                  VkVertexInputAttributeDescription* attributeDesc) {
+    uint32_t vertexBinding, instanceBinding;
+
+    if (primProc.hasVertexAttribs()) {
+        vertexBinding = bindingDescs->count();
+        bindingDescs->push_back() = {
+            vertexBinding,
+            (uint32_t) primProc.getVertexStride(),
+            VK_VERTEX_INPUT_RATE_VERTEX
+        };
+    }
+
+    if (primProc.hasInstanceAttribs()) {
+        instanceBinding = bindingDescs->count();
+        bindingDescs->push_back() = {
+            instanceBinding,
+            (uint32_t) primProc.getInstanceStride(),
+            VK_VERTEX_INPUT_RATE_INSTANCE
+        };
+    }
 
     // setup attribute descriptions
     int vaCount = primProc.numAttribs();
     if (vaCount > 0) {
-        size_t offset = 0;
         for (int attribIndex = 0; attribIndex < vaCount; attribIndex++) {
+            using InputRate = GrPrimitiveProcessor::Attribute::InputRate;
             const GrGeometryProcessor::Attribute& attrib = primProc.getAttrib(attribIndex);
-            GrVertexAttribType attribType = attrib.fType;
-
             VkVertexInputAttributeDescription& vkAttrib = attributeDesc[attribIndex];
             vkAttrib.location = attribIndex; // for now assume location = attribIndex
-            vkAttrib.binding = 0; // for now only one vertex buffer & binding
-            vkAttrib.format = attrib_type_to_vkformat(attribType);
-            vkAttrib.offset = static_cast<uint32_t>(offset);
-            offset += attrib.fOffset;
+            vkAttrib.binding = InputRate::kPerInstance == attrib.fInputRate ? instanceBinding
+                                                                            : vertexBinding;
+            vkAttrib.format = attrib_type_to_vkformat(attrib.fType);
+            vkAttrib.offset = attrib.fOffsetInRecord;
         }
     }
 
@@ -77,8 +88,8 @@
     vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
     vertexInputInfo->pNext = nullptr;
     vertexInputInfo->flags = 0;
-    vertexInputInfo->vertexBindingDescriptionCount = 1;
-    vertexInputInfo->pVertexBindingDescriptions = bindingDesc;
+    vertexInputInfo->vertexBindingDescriptionCount = bindingDescs->count();
+    vertexInputInfo->pVertexBindingDescriptions = bindingDescs->begin();
     vertexInputInfo->vertexAttributeDescriptionCount = vaCount;
     vertexInputInfo->pVertexAttributeDescriptions = attributeDesc;
 }
@@ -359,22 +370,8 @@
     // colorBlendInfo->blendConstants is set dynamically
 }
 
-static VkCullModeFlags draw_face_to_vk_cull_mode(GrDrawFace drawFace) {
-    // Assumes that we've set the front face to be ccw
-    static const VkCullModeFlags gTable[] = {
-        VK_CULL_MODE_NONE,              // kBoth_DrawFace
-        VK_CULL_MODE_BACK_BIT,          // kCCW_DrawFace, cull back face
-        VK_CULL_MODE_FRONT_BIT,         // kCW_DrawFace, cull front face
-    };
-    GR_STATIC_ASSERT(0 == (int)GrDrawFace::kBoth);
-    GR_STATIC_ASSERT(1 == (int)GrDrawFace::kCCW);
-    GR_STATIC_ASSERT(2 == (int)GrDrawFace::kCW);
-    SkASSERT(-1 < (int)drawFace && (int)drawFace <= 2);
-
-    return gTable[(int)drawFace];
-}
-
 static void setup_raster_state(const GrPipeline& pipeline,
+                               const GrCaps* caps,
                                VkPipelineRasterizationStateCreateInfo* rasterInfo) {
     memset(rasterInfo, 0, sizeof(VkPipelineRasterizationStateCreateInfo));
     rasterInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
@@ -382,8 +379,9 @@
     rasterInfo->flags = 0;
     rasterInfo->depthClampEnable = VK_FALSE;
     rasterInfo->rasterizerDiscardEnable = VK_FALSE;
-    rasterInfo->polygonMode = VK_POLYGON_MODE_FILL;
-    rasterInfo->cullMode = draw_face_to_vk_cull_mode(pipeline.getDrawFace());
+    rasterInfo->polygonMode = caps->wireframeMode() ? VK_POLYGON_MODE_LINE
+                                                    : VK_POLYGON_MODE_FILL;
+    rasterInfo->cullMode = VK_CULL_MODE_NONE;
     rasterInfo->frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
     rasterInfo->depthBiasEnable = VK_FALSE;
     rasterInfo->depthBiasConstantFactor = 0.0f;
@@ -415,11 +413,11 @@
                                    VkPipelineLayout layout,
                                    VkPipelineCache cache) {
     VkPipelineVertexInputStateCreateInfo vertexInputInfo;
-    VkVertexInputBindingDescription bindingDesc;
+    SkSTArray<2, VkVertexInputBindingDescription, true> bindingDescs;
     SkSTArray<16, VkVertexInputAttributeDescription> attributeDesc;
     SkASSERT(primProc.numAttribs() <= gpu->vkCaps().maxVertexAttributes());
     VkVertexInputAttributeDescription* pAttribs = attributeDesc.push_back_n(primProc.numAttribs());
-    setup_vertex_input_state(primProc, &vertexInputInfo, &bindingDesc, 1, pAttribs);
+    setup_vertex_input_state(primProc, &vertexInputInfo, &bindingDescs, pAttribs);
 
     VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
     setup_input_assembly_state(primitiveType, &inputAssemblyInfo);
@@ -439,7 +437,7 @@
     setup_color_blend_state(pipeline, &colorBlendInfo, attachmentStates);
 
     VkPipelineRasterizationStateCreateInfo rasterInfo;
-    setup_raster_state(pipeline, &rasterInfo);
+    setup_raster_state(pipeline, gpu->caps(), &rasterInfo);
 
     VkDynamicState dynamicStates[3];
     VkPipelineDynamicStateCreateInfo dynamicInfo;
@@ -539,7 +537,11 @@
     GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
     float floatColors[4];
     if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
-        GrColorToRGBAFloat(blendInfo.fBlendConstant, floatColors);
+        // Swizzle the blend to match what the shader will output.
+        const GrSwizzle& swizzle = gpu->caps()->shaderCaps()->configOutputSwizzle(
+                pipeline.getRenderTarget()->config());
+        GrColor blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
+        GrColorToRGBAFloat(blendConst, floatColors);
     } else {
         memset(floatColors, 0, 4 * sizeof(float));
     }
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index b4788de..585014e 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -14,7 +14,6 @@
 
 #include "vk/GrVkDefines.h"
 
-class GrNonInstancedVertices;
 class GrPipeline;
 class GrPrimitiveProcessor;
 class GrStencilSettings;
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 257763a..525d75f 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -7,8 +7,10 @@
 
 #include "GrVkPipelineState.h"
 
+#include "GrContext.h"
 #include "GrPipeline.h"
 #include "GrTexturePriv.h"
+#include "GrVkBufferView.h"
 #include "GrVkCommandBuffer.h"
 #include "GrVkDescriptorPool.h"
 #include "GrVkDescriptorSet.h"
@@ -18,6 +20,7 @@
 #include "GrVkPipeline.h"
 #include "GrVkRenderTarget.h"
 #include "GrVkSampler.h"
+#include "GrVkTexelBuffer.h"
 #include "GrVkTexture.h"
 #include "GrVkUniformBuffer.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
@@ -30,11 +33,13 @@
                                      GrVkPipeline* pipeline,
                                      VkPipelineLayout layout,
                                      const GrVkDescriptorSetManager::Handle& samplerDSHandle,
+                                     const GrVkDescriptorSetManager::Handle& texelBufferDSHandle,
                                      const BuiltinUniformHandles& builtinUniformHandles,
                                      const UniformInfoArray& uniforms,
-                                     uint32_t vertexUniformSize,
+                                     uint32_t geometryUniformSize,
                                      uint32_t fragmentUniformSize,
                                      uint32_t numSamplers,
+                                     uint32_t numTexelBuffers,
                                      GrGLSLPrimitiveProcessor* geometryProcessor,
                                      GrGLSLXferProcessor* xferProcessor,
                                      const GrGLSLFragProcs& fragmentProcessors)
@@ -42,36 +47,30 @@
     , fPipelineLayout(layout)
     , fUniformDescriptorSet(nullptr)
     , fSamplerDescriptorSet(nullptr)
+    , fTexelBufferDescriptorSet(nullptr)
     , fSamplerDSHandle(samplerDSHandle)
-    , fStartDS(SK_MaxS32)
-    , fDSCount(0)
+    , fTexelBufferDSHandle(texelBufferDSHandle)
     , fBuiltinUniformHandles(builtinUniformHandles)
     , fGeometryProcessor(geometryProcessor)
     , fXferProcessor(xferProcessor)
     , fFragmentProcessors(fragmentProcessors)
     , fDesc(desc)
-    , fDataManager(uniforms, vertexUniformSize, fragmentUniformSize) {
+    , fDataManager(uniforms, geometryUniformSize, fragmentUniformSize) {
     fSamplers.setReserve(numSamplers);
     fTextureViews.setReserve(numSamplers);
     fTextures.setReserve(numSamplers);
+    fBufferViews.setReserve(numTexelBuffers);
+    fTexelBuffers.setReserve(numTexelBuffers);
 
     fDescriptorSets[0] = VK_NULL_HANDLE;
     fDescriptorSets[1] = VK_NULL_HANDLE;
+    fDescriptorSets[2] = VK_NULL_HANDLE;
 
-    // Currently we are always binding a descriptor set for uniform buffers.
-    if (vertexUniformSize || fragmentUniformSize) {
-        fDSCount++;
-        fStartDS = GrVkUniformHandler::kUniformBufferDescSet;
-    }
-    if (numSamplers) {
-        fDSCount++;
-        fStartDS = SkTMin(fStartDS, (int)GrVkUniformHandler::kSamplerDescSet);
-    }
-
-    fVertexUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, vertexUniformSize));
+    fGeometryUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, geometryUniformSize));
     fFragmentUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, fragmentUniformSize));
 
     fNumSamplers = numSamplers;
+    fNumTexelBuffers = numTexelBuffers;
 }
 
 GrVkPipelineState::~GrVkPipelineState() {
@@ -81,6 +80,9 @@
     SkASSERT(!fSamplers.count());
     SkASSERT(!fTextureViews.count());
     SkASSERT(!fTextures.count());
+    SkASSERT(!fBufferViews.count());
+    SkASSERT(!fTexelBuffers.count());
+
     for (int i = 0; i < fFragmentProcessors.count(); ++i) {
         delete fFragmentProcessors[i];
     }
@@ -93,14 +95,24 @@
     fSamplers.rewind();
 
     for (int i = 0; i < fTextureViews.count(); ++i) {
-            fTextureViews[i]->unref(gpu);
+        fTextureViews[i]->unref(gpu);
     }
     fTextureViews.rewind();
 
     for (int i = 0; i < fTextures.count(); ++i) {
-            fTextures[i]->unref(gpu);
+        fTextures[i]->unref(gpu);
     }
     fTextures.rewind();
+
+    for (int i = 0; i < fBufferViews.count(); ++i) {
+        fBufferViews[i]->unref(gpu);
+    }
+    fBufferViews.rewind();
+
+    for (int i = 0; i < fTexelBuffers.count(); ++i) {
+        fTexelBuffers[i]->unref(gpu);
+    }
+    fTexelBuffers.rewind();
 }
 
 void GrVkPipelineState::freeGPUResources(const GrVkGpu* gpu) {
@@ -116,8 +128,8 @@
         fPipelineLayout = VK_NULL_HANDLE;
     }
 
-    if (fVertexUniformBuffer) {
-        fVertexUniformBuffer->release(gpu);
+    if (fGeometryUniformBuffer) {
+        fGeometryUniformBuffer->release(gpu);
     }
 
     if (fFragmentUniformBuffer) {
@@ -134,6 +146,12 @@
         fSamplerDescriptorSet = nullptr;
     }
 
+    if (fTexelBufferDescriptorSet) {
+        fTexelBufferDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
+        fTexelBufferDescriptorSet = nullptr;
+    }
+
+
     this->freeTempResources(gpu);
 }
 
@@ -143,7 +161,7 @@
 
     fPipelineLayout = VK_NULL_HANDLE;
 
-    fVertexUniformBuffer->abandon();
+    fGeometryUniformBuffer->abandon();
     fFragmentUniformBuffer->abandon();
 
     for (int i = 0; i < fSamplers.count(); ++i) {
@@ -161,6 +179,16 @@
     }
     fTextures.rewind();
 
+    for (int i = 0; i < fBufferViews.count(); ++i) {
+        fBufferViews[i]->unrefAndAbandon();
+    }
+    fBufferViews.rewind();
+
+    for (int i = 0; i < fTexelBuffers.count(); ++i) {
+        fTexelBuffers[i]->unrefAndAbandon();
+    }
+
+    fTexelBuffers.rewind();
     if (fUniformDescriptorSet) {
         fUniformDescriptorSet->unrefAndAbandon();
         fUniformDescriptorSet = nullptr;
@@ -170,20 +198,35 @@
         fSamplerDescriptorSet->unrefAndAbandon();
         fSamplerDescriptorSet = nullptr;
     }
+
+    if (fTexelBufferDescriptorSet) {
+        fTexelBufferDescriptorSet->unrefAndAbandon();
+        fTexelBufferDescriptorSet = nullptr;
+    }
 }
 
-static void append_texture_bindings(const GrProcessor& processor,
-                                    SkTArray<const GrProcessor::TextureSampler*>* textureBindings) {
+static void append_texture_bindings(
+        const GrResourceIOProcessor& processor,
+        SkTArray<const GrResourceIOProcessor::TextureSampler*>* textureBindings,
+        SkTArray<const GrResourceIOProcessor::BufferAccess*>* bufferAccesses) {
     // We don't support image storages in VK.
     SkASSERT(!processor.numImageStorages());
     if (int numTextureSamplers = processor.numTextureSamplers()) {
-        const GrProcessor::TextureSampler** bindings =
+        const GrResourceIOProcessor::TextureSampler** bindings =
                 textureBindings->push_back_n(numTextureSamplers);
         int i = 0;
         do {
             bindings[i] = &processor.textureSampler(i);
         } while (++i < numTextureSamplers);
     }
+    if (int numTexelBuffers = processor.numBuffers()) {
+        const GrResourceIOProcessor::BufferAccess** accesses  =
+                bufferAccesses->push_back_n(numTexelBuffers);
+        int i = 0;
+        do {
+            accesses[i] = &processor.bufferAccess(i);
+        } while (++i < numTexelBuffers);
+    }
 }
 
 void GrVkPipelineState::setData(GrVkGpu* gpu,
@@ -193,13 +236,14 @@
     // freeing the tempData between calls.
     this->freeTempResources(gpu);
 
-    this->setRenderTargetState(pipeline);
+    this->setRenderTargetState(pipeline.getRenderTarget());
 
-    SkSTArray<8, const GrProcessor::TextureSampler*> textureBindings;
+    SkSTArray<8, const GrResourceIOProcessor::TextureSampler*> textureBindings;
+    SkSTArray<8, const GrResourceIOProcessor::BufferAccess*> bufferAccesses;
 
     fGeometryProcessor->setData(fDataManager, primProc,
                                 GrFragmentProcessor::CoordTransformIter(pipeline));
-    append_texture_bindings(primProc, &textureBindings);
+    append_texture_bindings(primProc, &textureBindings, &bufferAccesses);
 
     GrFragmentProcessor::Iter iter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.begin(),
@@ -208,14 +252,25 @@
     GrGLSLFragmentProcessor* glslFP = glslIter.next();
     while (fp && glslFP) {
         glslFP->setData(fDataManager, *fp);
-        append_texture_bindings(*fp, &textureBindings);
+        append_texture_bindings(*fp, &textureBindings, &bufferAccesses);
         fp = iter.next();
         glslFP = glslIter.next();
     }
     SkASSERT(!fp && !glslFP);
 
-    fXferProcessor->setData(fDataManager, pipeline.getXferProcessor());
-    append_texture_bindings(pipeline.getXferProcessor(), &textureBindings);
+    {
+        SkIPoint offset;
+        GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
+
+        fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
+    }
+
+    GrResourceIOProcessor::TextureSampler dstTextureSampler;
+    if (GrTextureProxy* dstTextureProxy = pipeline.dstTextureProxy()) {
+        dstTextureSampler.reset(gpu->getContext()->resourceProvider(), sk_ref_sp(dstTextureProxy));
+        SkAssertResult(dstTextureSampler.instantiate(gpu->getContext()->resourceProvider()));
+        textureBindings.push_back(&dstTextureSampler);
+    }
 
     // Get new descriptor sets
     if (fNumSamplers) {
@@ -228,9 +283,20 @@
         this->writeSamplers(gpu, textureBindings, pipeline.getAllowSRGBInputs());
     }
 
-    if (fVertexUniformBuffer.get() || fFragmentUniformBuffer.get()) {
+    if (fNumTexelBuffers) {
+        if (fTexelBufferDescriptorSet) {
+            fTexelBufferDescriptorSet->recycle(gpu);
+        }
+        fTexelBufferDescriptorSet =
+                gpu->resourceProvider().getSamplerDescriptorSet(fTexelBufferDSHandle);
+        int texelBufferDSIdx = GrVkUniformHandler::kTexelBufferDescSet;
+        fDescriptorSets[texelBufferDSIdx] = fTexelBufferDescriptorSet->descriptorSet();
+        this->writeTexelBuffers(gpu, bufferAccesses);
+    }
+
+    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
         if (fDataManager.uploadUniformBuffers(gpu,
-                                              fVertexUniformBuffer.get(),
+                                              fGeometryUniformBuffer.get(),
                                               fFragmentUniformBuffer.get())
             || !fUniformDescriptorSet)
         {
@@ -245,76 +311,74 @@
     }
 }
 
+void set_uniform_descriptor_writes(VkWriteDescriptorSet* descriptorWrite,
+                                   VkDescriptorBufferInfo* bufferInfo,
+                                   const GrVkUniformBuffer* buffer,
+                                   VkDescriptorSet descriptorSet,
+                                   uint32_t binding) {
+
+    memset(bufferInfo, 0, sizeof(VkDescriptorBufferInfo));
+    bufferInfo->buffer = buffer->buffer();
+    bufferInfo->offset = buffer->offset();
+    bufferInfo->range = buffer->size();
+
+    memset(descriptorWrite, 0, sizeof(VkWriteDescriptorSet));
+    descriptorWrite->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    descriptorWrite->pNext = nullptr;
+    descriptorWrite->dstSet = descriptorSet;
+    descriptorWrite->dstBinding = binding;
+    descriptorWrite->dstArrayElement = 0;
+    descriptorWrite->descriptorCount = 1;
+    descriptorWrite->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    descriptorWrite->pImageInfo = nullptr;
+    descriptorWrite->pBufferInfo = bufferInfo;
+    descriptorWrite->pTexelBufferView = nullptr;
+}
+
 void GrVkPipelineState::writeUniformBuffers(const GrVkGpu* gpu) {
-    VkWriteDescriptorSet descriptorWrites[2];
-    memset(descriptorWrites, 0, 2 * sizeof(VkWriteDescriptorSet));
+    VkWriteDescriptorSet descriptorWrites[3];
+    VkDescriptorBufferInfo bufferInfos[3];
 
-    uint32_t firstUniformWrite = 0;
-    uint32_t uniformBindingUpdateCount = 0;
+    uint32_t writeCount = 0;
 
-    VkDescriptorBufferInfo vertBufferInfo;
-    // Vertex Uniform Buffer
-    if (fVertexUniformBuffer.get()) {
-        ++uniformBindingUpdateCount;
-        memset(&vertBufferInfo, 0, sizeof(VkDescriptorBufferInfo));
-        vertBufferInfo.buffer = fVertexUniformBuffer->buffer();
-        vertBufferInfo.offset = fVertexUniformBuffer->offset();
-        vertBufferInfo.range = fVertexUniformBuffer->size();
-
-        descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-        descriptorWrites[0].pNext = nullptr;
-        descriptorWrites[0].dstSet = fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet];
-        descriptorWrites[0].dstBinding = GrVkUniformHandler::kVertexBinding;
-        descriptorWrites[0].dstArrayElement = 0;
-        descriptorWrites[0].descriptorCount = 1;
-        descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-        descriptorWrites[0].pImageInfo = nullptr;
-        descriptorWrites[0].pBufferInfo = &vertBufferInfo;
-        descriptorWrites[0].pTexelBufferView = nullptr;
+    // Geometry Uniform Buffer
+    if (fGeometryUniformBuffer.get()) {
+        set_uniform_descriptor_writes(&descriptorWrites[writeCount],
+                                      &bufferInfos[writeCount],
+                                      fGeometryUniformBuffer.get(),
+                                      fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet],
+                                      GrVkUniformHandler::kGeometryBinding);
+        ++writeCount;
     }
 
-    VkDescriptorBufferInfo fragBufferInfo;
     // Fragment Uniform Buffer
     if (fFragmentUniformBuffer.get()) {
-        if (0 == uniformBindingUpdateCount) {
-            firstUniformWrite = 1;
-        }
-        ++uniformBindingUpdateCount;
-        memset(&fragBufferInfo, 0, sizeof(VkDescriptorBufferInfo));
-        fragBufferInfo.buffer = fFragmentUniformBuffer->buffer();
-        fragBufferInfo.offset = fFragmentUniformBuffer->offset();
-        fragBufferInfo.range = fFragmentUniformBuffer->size();
-
-        descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-        descriptorWrites[1].pNext = nullptr;
-        descriptorWrites[1].dstSet = fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet];
-        descriptorWrites[1].dstBinding = GrVkUniformHandler::kFragBinding;;
-        descriptorWrites[1].dstArrayElement = 0;
-        descriptorWrites[1].descriptorCount = 1;
-        descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-        descriptorWrites[1].pImageInfo = nullptr;
-        descriptorWrites[1].pBufferInfo = &fragBufferInfo;
-        descriptorWrites[1].pTexelBufferView = nullptr;
+        set_uniform_descriptor_writes(&descriptorWrites[writeCount],
+                                      &bufferInfos[writeCount],
+                                      fFragmentUniformBuffer.get(),
+                                      fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet],
+                                      GrVkUniformHandler::kFragBinding);
+        ++writeCount;
     }
 
-    if (uniformBindingUpdateCount) {
+    if (writeCount) {
         GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
-                                                            uniformBindingUpdateCount,
-                                                            &descriptorWrites[firstUniformWrite],
+                                                            writeCount,
+                                                            descriptorWrites,
                                                             0, nullptr));
     }
 }
 
 void GrVkPipelineState::writeSamplers(
         GrVkGpu* gpu,
-        const SkTArray<const GrProcessor::TextureSampler*>& textureBindings,
+        const SkTArray<const GrResourceIOProcessor::TextureSampler*>& textureBindings,
         bool allowSRGBInputs) {
     SkASSERT(fNumSamplers == textureBindings.count());
 
     for (int i = 0; i < textureBindings.count(); ++i) {
         const GrSamplerParams& params = textureBindings[i]->params();
 
-        GrVkTexture* texture = static_cast<GrVkTexture*>(textureBindings[i]->texture());
+        GrVkTexture* texture = static_cast<GrVkTexture*>(textureBindings[i]->peekTexture());
 
         fSamplers.push(gpu->resourceProvider().findOrCreateCompatibleSampler(params,
                                                           texture->texturePriv().maxMipMapLevel()));
@@ -354,16 +418,57 @@
     }
 }
 
-void GrVkPipelineState::setRenderTargetState(const GrPipeline& pipeline) {
+void GrVkPipelineState::writeTexelBuffers(
+        GrVkGpu* gpu,
+        const SkTArray<const GrResourceIOProcessor::BufferAccess*>& bufferAccesses) {
+    SkASSERT(fNumTexelBuffers == bufferAccesses.count());
+
+    for (int i = 0; i < bufferAccesses.count(); ++i) {
+        GrPixelConfig config = bufferAccesses[i]->texelConfig();
+        VkFormat format;
+        SkAssertResult(GrPixelConfigToVkFormat(config, &format));
+
+        GrVkTexelBuffer* buffer = static_cast<GrVkTexelBuffer*>(bufferAccesses[i]->buffer());
+
+        const GrVkBufferView* bufferView = GrVkBufferView::Create(gpu, buffer->buffer(),
+                                                                  format, buffer->offset(),
+                                                                  buffer->size());
+        fBufferViews.push(bufferView);
+
+        const GrVkResource* bufferResource = buffer->resource();
+        bufferResource->ref();
+        fTexelBuffers.push(bufferResource);
+
+        VkWriteDescriptorSet writeInfo;
+        memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
+        writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+        writeInfo.pNext = nullptr;
+        writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kTexelBufferDescSet];
+        writeInfo.dstBinding = i;
+        writeInfo.dstArrayElement = 0;
+        writeInfo.descriptorCount = 1;
+        writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+        writeInfo.pImageInfo = nullptr;
+        writeInfo.pBufferInfo = nullptr;
+        VkBufferView vkBufferView = bufferView->bufferView();
+        writeInfo.pTexelBufferView = &vkBufferView;
+
+        GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
+                                                            1,
+                                                            &writeInfo,
+                                                            0,
+                                                            nullptr));
+    }
+}
+
+void GrVkPipelineState::setRenderTargetState(const GrRenderTarget* rt) {
     // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
     if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
-        fRenderTargetState.fRenderTargetSize.fHeight != pipeline.getRenderTarget()->height()) {
-        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni,
-                                  SkIntToScalar(pipeline.getRenderTarget()->height()));
+        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
+        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
     }
 
     // set RT adjustment
-    const GrRenderTarget* rt = pipeline.getRenderTarget();
     SkISize size;
     size.set(rt->width(), rt->height());
     SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
@@ -381,9 +486,23 @@
 void GrVkPipelineState::bind(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
     commandBuffer->bindPipeline(gpu, fPipeline);
 
-    if (fDSCount) {
-        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout, fStartDS, fDSCount,
-                                          &fDescriptorSets[fStartDS], 0, nullptr);
+    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
+        int dsIndex = GrVkUniformHandler::kUniformBufferDescSet;
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
+                                          dsIndex, 1,
+                                          &fDescriptorSets[dsIndex], 0, nullptr);
+    }
+    if (fNumSamplers) {
+        int dsIndex = GrVkUniformHandler::kSamplerDescSet;
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
+                                          dsIndex, 1,
+                                          &fDescriptorSets[dsIndex], 0, nullptr);
+    }
+    if (fNumTexelBuffers) {
+        int dsIndex = GrVkUniformHandler::kTexelBufferDescSet;
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
+                                          dsIndex, 1,
+                                          &fDescriptorSets[dsIndex], 0, nullptr);
     }
 }
 
@@ -394,9 +513,12 @@
     if (fSamplerDescriptorSet) {
         commandBuffer.addRecycledResource(fSamplerDescriptorSet);
     }
+    if (fTexelBufferDescriptorSet) {
+        commandBuffer.addRecycledResource(fTexelBufferDescriptorSet);
+    }
 
-    if (fVertexUniformBuffer.get()) {
-        commandBuffer.addRecycledResource(fVertexUniformBuffer->resource());
+    if (fGeometryUniformBuffer.get()) {
+        commandBuffer.addRecycledResource(fGeometryUniformBuffer->resource());
     }
     if (fFragmentUniformBuffer.get()) {
         commandBuffer.addRecycledResource(fFragmentUniformBuffer->resource());
@@ -413,72 +535,18 @@
     for (int i = 0; i < fTextures.count(); ++i) {
         commandBuffer.addResource(fTextures[i]);
     }
+
+    for (int i = 0; i < fBufferViews.count(); ++i) {
+        commandBuffer.addResource(fBufferViews[i]);
+    }
+
+    for (int i = 0; i < fTexelBuffers.count(); ++i) {
+        commandBuffer.addResource(fTexelBuffers[i]);
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrVkPipelineState::DescriptorPoolManager::getNewPool(GrVkGpu* gpu) {
-    if (fPool) {
-        fPool->unref(gpu);
-        uint32_t newPoolSize = fMaxDescriptors + ((fMaxDescriptors + 1) >> 1);
-        if (newPoolSize < kMaxDescLimit) {
-            fMaxDescriptors = newPoolSize;
-        } else {
-            fMaxDescriptors = kMaxDescLimit;
-        }
-
-    }
-    if (fMaxDescriptors) {
-        fPool = gpu->resourceProvider().findOrCreateCompatibleDescriptorPool(fDescType,
-                                                                             fMaxDescriptors);
-    }
-    SkASSERT(fPool || !fMaxDescriptors);
-}
-
-void GrVkPipelineState::DescriptorPoolManager::getNewDescriptorSet(GrVkGpu* gpu,
-                                                                   VkDescriptorSet* ds) {
-    if (!fMaxDescriptors) {
-        return;
-    }
-    fCurrentDescriptorCount += fDescCountPerSet;
-    if (fCurrentDescriptorCount > fMaxDescriptors) {
-        this->getNewPool(gpu);
-        fCurrentDescriptorCount = fDescCountPerSet;
-    }
-
-    VkDescriptorSetAllocateInfo dsAllocateInfo;
-    memset(&dsAllocateInfo, 0, sizeof(VkDescriptorSetAllocateInfo));
-    dsAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
-    dsAllocateInfo.pNext = nullptr;
-    dsAllocateInfo.descriptorPool = fPool->descPool();
-    dsAllocateInfo.descriptorSetCount = 1;
-    dsAllocateInfo.pSetLayouts = &fDescLayout;
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), AllocateDescriptorSets(gpu->device(),
-                                                                   &dsAllocateInfo,
-                                                                   ds));
-}
-
-void GrVkPipelineState::DescriptorPoolManager::freeGPUResources(const GrVkGpu* gpu) {
-    if (fDescLayout) {
-        GR_VK_CALL(gpu->vkInterface(), DestroyDescriptorSetLayout(gpu->device(), fDescLayout,
-                                                                  nullptr));
-        fDescLayout = VK_NULL_HANDLE;
-    }
-
-    if (fPool) {
-        fPool->unref(gpu);
-        fPool = nullptr;
-    }
-}
-
-void GrVkPipelineState::DescriptorPoolManager::abandonGPUResources() {
-    fDescLayout = VK_NULL_HANDLE;
-    if (fPool) {
-        fPool->unrefAndAbandon();
-        fPool = nullptr;
-    }
-}
-
 uint32_t get_blend_info_key(const GrPipeline& pipeline) {
     GrXferProcessor::BlendInfo blendInfo;
     pipeline.getXferProcessor().getBlendInfo(&blendInfo);
@@ -513,9 +581,6 @@
 
     stencil.genKey(&b);
 
-    SkASSERT(sizeof(GrDrawFace) <= sizeof(uint32_t));
-    b.add32((int32_t)pipeline.getDrawFace());
-
     b.add32(get_blend_info_key(pipeline));
 
     b.add32(primitiveType);
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index 49a6f9f..190ad06 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -19,6 +19,7 @@
 #include "vk/GrVkDefines.h"
 
 class GrPipeline;
+class GrVkBufferView;
 class GrVkCommandBuffer;
 class GrVkDescriptorPool;
 class GrVkDescriptorSet;
@@ -92,58 +93,27 @@
                       GrVkPipeline* pipeline,
                       VkPipelineLayout layout,
                       const GrVkDescriptorSetManager::Handle& samplerDSHandle,
+                      const GrVkDescriptorSetManager::Handle& texelBufferDSHandle,
                       const BuiltinUniformHandles& builtinUniformHandles,
                       const UniformInfoArray& uniforms,
-                      uint32_t vertexUniformSize,
+                      uint32_t geometryUniformSize,
                       uint32_t fragmentUniformSize,
                       uint32_t numSamplers,
+                      uint32_t numTexelBuffers,
                       GrGLSLPrimitiveProcessor* geometryProcessor,
                       GrGLSLXferProcessor* xferProcessor,
                       const GrGLSLFragProcs& fragmentProcessors);
 
-    // Each pool will manage one type of descriptor. Thus each descriptor set we use will all be of
-    // one VkDescriptorType.
-    struct DescriptorPoolManager {
-        DescriptorPoolManager(VkDescriptorSetLayout layout, VkDescriptorType type,
-                              uint32_t descCount, GrVkGpu* gpu)
-            : fDescLayout(layout)
-            , fDescType(type)
-            , fDescCountPerSet(descCount)
-            , fCurrentDescriptorCount(0)
-            , fPool(nullptr) {
-            SkASSERT(descCount < kMaxDescLimit >> 2);
-            fMaxDescriptors = fDescCountPerSet << 2;
-            this->getNewPool(gpu);
-        }
-
-        ~DescriptorPoolManager() {
-            SkASSERT(!fDescLayout);
-            SkASSERT(!fPool);
-        }
-
-        void getNewDescriptorSet(GrVkGpu* gpu, VkDescriptorSet* ds);
-
-        void freeGPUResources(const GrVkGpu* gpu);
-        void abandonGPUResources();
-
-        VkDescriptorSetLayout  fDescLayout;
-        VkDescriptorType       fDescType;
-        uint32_t               fDescCountPerSet;
-        uint32_t               fMaxDescriptors;
-        uint32_t               fCurrentDescriptorCount;
-        GrVkDescriptorPool*    fPool;
-
-    private:
-        static const uint32_t kMaxDescLimit = 1 << 10;
-
-        void getNewPool(GrVkGpu* gpu);
-    };
-
     void writeUniformBuffers(const GrVkGpu* gpu);
 
-    void writeSamplers(GrVkGpu* gpu,
-                       const SkTArray<const GrProcessor::TextureSampler*>& textureBindings,
-                       bool allowSRGBInputs);
+    void writeSamplers(
+            GrVkGpu* gpu,
+            const SkTArray<const GrResourceIOProcessor::TextureSampler*>& textureBindings,
+            bool allowSRGBInputs);
+
+    void writeTexelBuffers(
+            GrVkGpu* gpu,
+            const SkTArray<const GrResourceIOProcessor::BufferAccess*>& bufferAccesses);
 
     /**
     * We use the RT's size and origin to adjust from Skia device space to vulkan normalized device
@@ -182,7 +152,7 @@
     };
 
     // Helper for setData() that sets the view matrix and loads the render target height uniform
-    void setRenderTargetState(const GrPipeline&);
+    void setRenderTargetState(const GrRenderTarget*);
 
     // GrVkResources
     GrVkPipeline* fPipeline;
@@ -196,20 +166,16 @@
     // 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[2];
+    VkDescriptorSet fDescriptorSets[3];
 
-    // Once we move samplers over to use the resource provider for descriptor sets we will not need
-    // the above array and instead just use GrVkDescriptorSet like the uniform one here.
     const GrVkDescriptorSet* fUniformDescriptorSet;
     const GrVkDescriptorSet* fSamplerDescriptorSet;
+    const GrVkDescriptorSet* fTexelBufferDescriptorSet;
 
     const GrVkDescriptorSetManager::Handle fSamplerDSHandle;
+    const GrVkDescriptorSetManager::Handle fTexelBufferDSHandle;
 
-    // Meta data so we know which descriptor sets we are using and need to bind.
-    int fStartDS;
-    int fDSCount;
-
-    std::unique_ptr<GrVkUniformBuffer> fVertexUniformBuffer;
+    std::unique_ptr<GrVkUniformBuffer> fGeometryUniformBuffer;
     std::unique_ptr<GrVkUniformBuffer> fFragmentUniformBuffer;
 
     // GrVkResources used for sampling textures
@@ -217,6 +183,10 @@
     SkTDArray<const GrVkImageView*> fTextureViews;
     SkTDArray<const GrVkResource*> fTextures;
 
+    // GrVkResource used for TexelBuffers
+    SkTDArray<const GrVkBufferView*> fBufferViews;
+    SkTDArray<const GrVkResource*> fTexelBuffers;
+
     // Tracks the current render target uniforms stored in the vertex buffer.
     RenderTargetState fRenderTargetState;
     BuiltinUniformHandles fBuiltinUniformHandles;
@@ -231,6 +201,7 @@
     GrVkPipelineStateDataManager fDataManager;
 
     int fNumSamplers;
+    int fNumTexelBuffers;
 
     friend class GrVkPipelineStateBuilder;
 };
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index 1363045..2ea7234 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -25,10 +25,7 @@
     // uniforms, varyings, textures, etc
     GrVkPipelineStateBuilder builder(gpu, pipeline, primProc, desc);
 
-    GrGLSLExpr4 inputColor;
-    GrGLSLExpr4 inputCoverage;
-
-    if (!builder.emitAndInstallProcs(&inputColor, &inputCoverage)) {
+    if (!builder.emitAndInstallProcs()) {
         builder.cleanupFragmentProcessors();
         return nullptr;
     }
@@ -93,27 +90,35 @@
                                                       GrPrimitiveType primitiveType,
                                                       const GrVkRenderPass& renderPass,
                                                       GrVkPipelineState::Desc* desc) {
-    VkDescriptorSetLayout dsLayout[2];
+    VkDescriptorSetLayout dsLayout[3];
     VkPipelineLayout pipelineLayout;
-    VkShaderModule vertShaderModule;
-    VkShaderModule fragShaderModule;
+    VkShaderModule vertShaderModule = VK_NULL_HANDLE;
+    VkShaderModule geomShaderModule = VK_NULL_HANDLE;
+    VkShaderModule fragShaderModule = VK_NULL_HANDLE;
 
     GrVkResourceProvider& resourceProvider = fGpu->resourceProvider();
     // These layouts are not owned by the PipelineStateBuilder and thus should not be destroyed
     dsLayout[GrVkUniformHandler::kUniformBufferDescSet] = resourceProvider.getUniformDSLayout();
 
     GrVkDescriptorSetManager::Handle samplerDSHandle;
-    resourceProvider.getSamplerDescriptorSetHandle(fUniformHandler, &samplerDSHandle);
+    resourceProvider.getSamplerDescriptorSetHandle(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+                                                   fUniformHandler, &samplerDSHandle);
     dsLayout[GrVkUniformHandler::kSamplerDescSet] =
             resourceProvider.getSamplerDSLayout(samplerDSHandle);
 
+    GrVkDescriptorSetManager::Handle texelBufferDSHandle;
+    resourceProvider.getSamplerDescriptorSetHandle(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
+                                                   fUniformHandler, &texelBufferDSHandle);
+    dsLayout[GrVkUniformHandler::kTexelBufferDescSet] =
+            resourceProvider.getSamplerDSLayout(texelBufferDSHandle);
+
     // Create the VkPipelineLayout
     VkPipelineLayoutCreateInfo layoutCreateInfo;
     memset(&layoutCreateInfo, 0, sizeof(VkPipelineLayoutCreateFlags));
     layoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
     layoutCreateInfo.pNext = 0;
     layoutCreateInfo.flags = 0;
-    layoutCreateInfo.setLayoutCount = 2;
+    layoutCreateInfo.setLayoutCount = 3;
     layoutCreateInfo.pSetLayouts = dsLayout;
     layoutCreateInfo.pushConstantRangeCount = 0;
     layoutCreateInfo.pPushConstantRanges = nullptr;
@@ -132,7 +137,7 @@
 
     this->finalizeShaders();
 
-    VkPipelineShaderStageCreateInfo shaderStageInfo[2];
+    VkPipelineShaderStageCreateInfo shaderStageInfo[3];
     SkSL::Program::Settings settings;
     settings.fFlipY = this->pipeline().getRenderTarget()->origin() != kTopLeft_GrSurfaceOrigin;
     SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT,
@@ -142,9 +147,6 @@
                                               settings,
                                               desc));
 
-    // TODO: geometry shader support.
-    SkASSERT(!this->primitiveProcessor().willUseGeoShader());
-
     SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_FRAGMENT_BIT,
                                               fFS,
                                               &fragShaderModule,
@@ -152,11 +154,22 @@
                                               settings,
                                               desc));
 
+    int numShaderStages = 2; // We always have at least vertex and fragment stages.
+    if (this->primitiveProcessor().willUseGeoShader()) {
+        SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_GEOMETRY_BIT,
+                                                  fGS,
+                                                  &geomShaderModule,
+                                                  &shaderStageInfo[2],
+                                                  settings,
+                                                  desc));
+        ++numShaderStages;
+    }
+
     GrVkPipeline* pipeline = resourceProvider.createPipeline(fPipeline,
                                                              stencil,
                                                              fPrimProc,
                                                              shaderStageInfo,
-                                                             2,
+                                                             numShaderStages,
                                                              primitiveType,
                                                              renderPass,
                                                              pipelineLayout);
@@ -164,6 +177,12 @@
                                                         nullptr));
     GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), fragShaderModule,
                                                         nullptr));
+    // 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).
+    if (this->primitiveProcessor().willUseGeoShader()) {
+        GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), geomShaderModule,
+                                                            nullptr));
+    }
 
     if (!pipeline) {
         GR_VK_CALL(fGpu->vkInterface(), DestroyPipelineLayout(fGpu->device(), pipelineLayout,
@@ -177,11 +196,13 @@
                                  pipeline,
                                  pipelineLayout,
                                  samplerDSHandle,
+                                 texelBufferDSHandle,
                                  fUniformHandles,
                                  fUniformHandler.fUniforms,
-                                 fUniformHandler.fCurrentVertexUBOOffset,
+                                 fUniformHandler.fCurrentGeometryUBOOffset,
                                  fUniformHandler.fCurrentFragmentUBOOffset,
                                  (uint32_t)fUniformHandler.numSamplers(),
+                                 (uint32_t)fUniformHandler.numTexelBuffers(),
                                  fGeometryProcessor,
                                  fXferProcessor,
                                  fFragmentProcessors);
diff --git a/src/gpu/vk/GrVkPipelineStateDataManager.cpp b/src/gpu/vk/GrVkPipelineStateDataManager.cpp
index 88176da..5d96bd7 100644
--- a/src/gpu/vk/GrVkPipelineStateDataManager.cpp
+++ b/src/gpu/vk/GrVkPipelineStateDataManager.cpp
@@ -11,13 +11,13 @@
 #include "GrVkUniformBuffer.h"
 
 GrVkPipelineStateDataManager::GrVkPipelineStateDataManager(const UniformInfoArray& uniforms,
-                                                           uint32_t vertexUniformSize,
+                                                           uint32_t geometryUniformSize,
                                                            uint32_t fragmentUniformSize)
-    : fVertexUniformSize(vertexUniformSize)
+    : fGeometryUniformSize(geometryUniformSize)
     , fFragmentUniformSize(fragmentUniformSize)
-    , fVertexUniformsDirty(false)
+    , fGeometryUniformsDirty(false)
     , fFragmentUniformsDirty(false) {
-    fVertexUniformData.reset(vertexUniformSize);
+    fGeometryUniformData.reset(geometryUniformSize);
     fFragmentUniformData.reset(fragmentUniformSize);
     int count = uniforms.count();
     fUniforms.push_back_n(count);
@@ -32,20 +32,24 @@
             uniform.fArrayCount = uniformInfo.fVariable.getArrayCount();
             uniform.fType = uniformInfo.fVariable.getType();
         );
-        uniform.fBinding =
-            (kVertex_GrShaderFlag == uniformInfo.fVisibility) ? GrVkUniformHandler::kVertexBinding
-                                                              : GrVkUniformHandler::kFragBinding;
+
+        if (kVertex_GrShaderFlag == uniformInfo.fVisibility ||
+            kGeometry_GrShaderFlag == uniformInfo.fVisibility) {
+            uniform.fBinding = GrVkUniformHandler::kGeometryBinding;
+        } else {
+            SkASSERT(kFragment_GrShaderFlag == uniformInfo.fVisibility);
+            uniform.fBinding = GrVkUniformHandler::kFragBinding;
+        }
         uniform.fOffset = uniformInfo.fUBOffset;
     }
 }
 
 void* GrVkPipelineStateDataManager::getBufferPtrAndMarkDirty(const Uniform& uni) const {
     void* buffer;
-    if (GrVkUniformHandler::kVertexBinding == uni.fBinding) {
-        buffer = fVertexUniformData.get();
-        fVertexUniformsDirty = true;
-    }
-    else {
+    if (GrVkUniformHandler::kGeometryBinding == uni.fBinding) {
+        buffer = fGeometryUniformData.get();
+        fGeometryUniformsDirty = true;
+    } else {
         SkASSERT(GrVkUniformHandler::kFragBinding == uni.fBinding);
         buffer = fFragmentUniformData.get();
         fFragmentUniformsDirty = true;
@@ -233,9 +237,9 @@
              (1 == arrayCount && GrShaderVar::kNonArray == uni.fArrayCount));
 
     void* buffer;
-    if (GrVkUniformHandler::kVertexBinding == uni.fBinding) {
-        buffer = fVertexUniformData.get();
-        fVertexUniformsDirty = true;
+    if (GrVkUniformHandler::kGeometryBinding == uni.fBinding) {
+        buffer = fGeometryUniformData.get();
+        fGeometryUniformsDirty = true;
     } else {
         SkASSERT(GrVkUniformHandler::kFragBinding == uni.fBinding);
         buffer = fFragmentUniformData.get();
@@ -268,19 +272,19 @@
 };
 
 bool GrVkPipelineStateDataManager::uploadUniformBuffers(GrVkGpu* gpu,
-                                                        GrVkUniformBuffer* vertexBuffer,
+                                                        GrVkUniformBuffer* geometryBuffer,
                                                         GrVkUniformBuffer* fragmentBuffer) const {
     bool updatedBuffer = false;
-    if (vertexBuffer && fVertexUniformsDirty) {
-        SkAssertResult(vertexBuffer->updateData(gpu, fVertexUniformData.get(), fVertexUniformSize,
-                                                &updatedBuffer));
-        fVertexUniformsDirty = false;
+    if (geometryBuffer && fGeometryUniformsDirty) {
+        SkAssertResult(geometryBuffer->updateData(gpu, fGeometryUniformData.get(),
+                                                  fGeometryUniformSize, &updatedBuffer));
+        fGeometryUniformsDirty = false;
     }
-
     if (fragmentBuffer && fFragmentUniformsDirty) {
         SkAssertResult(fragmentBuffer->updateData(gpu, fFragmentUniformData.get(),
                                                   fFragmentUniformSize, &updatedBuffer));
         fFragmentUniformsDirty = false;
     }
+
     return updatedBuffer;
 }
diff --git a/src/gpu/vk/GrVkPipelineStateDataManager.h b/src/gpu/vk/GrVkPipelineStateDataManager.h
index 4a061a6..25c7a6e 100644
--- a/src/gpu/vk/GrVkPipelineStateDataManager.h
+++ b/src/gpu/vk/GrVkPipelineStateDataManager.h
@@ -21,7 +21,7 @@
     typedef GrVkUniformHandler::UniformInfoArray UniformInfoArray;
 
     GrVkPipelineStateDataManager(const UniformInfoArray&,
-                                 uint32_t vertexUniformSize,
+                                 uint32_t geometryUniformSize,
                                  uint32_t fragmentUniformSize);
 
     void set1i(UniformHandle, int32_t) const override;
@@ -49,11 +49,11 @@
         SkFAIL("Only supported in NVPR, which is not in vulkan");
     }
 
-    // Returns true if either the vertex or fragment buffer needed to generate a new underlying
+    // Returns true if either the geometry or fragment buffers needed to generate a new underlying
     // VkBuffer object in order upload data. If true is returned, this is a signal to the caller
     // that they will need to update the descriptor set that is using these buffers.
     bool uploadUniformBuffers(GrVkGpu* gpu,
-                              GrVkUniformBuffer* vertexBuffer,
+                              GrVkUniformBuffer* geometryBuffer,
                               GrVkUniformBuffer* fragmentBuffer) const;
 private:
     struct Uniform {
@@ -70,14 +70,14 @@
 
     void* getBufferPtrAndMarkDirty(const Uniform& uni) const;
 
-    uint32_t fVertexUniformSize;
+    uint32_t fGeometryUniformSize;
     uint32_t fFragmentUniformSize;
 
     SkTArray<Uniform, true> fUniforms;
 
-    mutable SkAutoMalloc fVertexUniformData;
+    mutable SkAutoMalloc fGeometryUniformData;
     mutable SkAutoMalloc fFragmentUniformData;
-    mutable bool         fVertexUniformsDirty;
+    mutable bool         fGeometryUniformsDirty;
     mutable bool         fFragmentUniformsDirty;
 };
 
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index bb2b066..d5719fd 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -281,7 +281,7 @@
 
     if (fMSAAImage) {
         fMSAAImage->releaseImage(gpu);
-        fMSAAImage = nullptr;
+        fMSAAImage.reset();
     }
 
     if (fResolveAttachmentView) {
@@ -305,7 +305,7 @@
 void GrVkRenderTarget::abandonInternalObjects() {
     if (fMSAAImage) {
         fMSAAImage->abandonImage();
-        fMSAAImage = nullptr;
+        fMSAAImage.reset();
     }
 
     if (fResolveAttachmentView) {
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index bfecdc9..272998e 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -47,7 +47,7 @@
         }
         return nullptr;
     }
-    GrVkImage* msaaImage() { return fMSAAImage; }
+    GrVkImage* msaaImage() { return fMSAAImage.get(); }
     const GrVkImageView* resolveAttachmentView() const { return fResolveAttachmentView; }
     const GrVkResource* stencilImageResource() const;
     const GrVkImageView* stencilAttachmentView() const;
@@ -100,13 +100,15 @@
     size_t onGpuMemorySize() const override {
         // The plus 1 is to account for the resolve texture.
         // TODO: is this still correct?
-        return GrSurface::ComputeSize(fDesc, fDesc.fSampleCnt+1, false);
+        int numColorSamples = this->numColorSamples() + 1;
+        return GrSurface::ComputeSize(this->config(), this->width(), this->height(),
+                                      numColorSamples, false);
     }
 
     void createFramebuffer(GrVkGpu* gpu);
 
     const GrVkImageView*       fColorAttachmentView;
-    GrVkImage*                 fMSAAImage;
+    std::unique_ptr<GrVkImage> fMSAAImage;
     const GrVkImageView*       fResolveAttachmentView;
 
 private:
diff --git a/src/gpu/vk/GrVkResource.h b/src/gpu/vk/GrVkResource.h
index 9d7212e..9ddde47 100644
--- a/src/gpu/vk/GrVkResource.h
+++ b/src/gpu/vk/GrVkResource.h
@@ -31,7 +31,7 @@
 
   This is nearly identical to SkRefCntBase. The exceptions are that unref()
   takes a GrVkGpu, and any derived classes must implement freeGPUData() and
-  possibly abandonSubResources().
+  possibly abandonGPUData().
 */
 
 class GrVkResource : SkNoncopyable {
@@ -153,10 +153,12 @@
      */
     virtual void freeGPUData(const GrVkGpu* gpu) const = 0;
 
-    /** Must be overridden by subclasses that themselves store GrVkResources.
-     *  Will unrefAndAbandon those resources without deleting the underlying Vk data
+    /**
+     * Called from unrefAndAbandon. Resources should do any necessary cleanup without freeing
+     * underlying Vk objects. This must be overridden by subclasses that themselves store
+     * GrVkResources since those resource will need to be unrefed.
      */
-    virtual void abandonSubResources() const {}
+    virtual void abandonGPUData() const {}
 
     /**
      *  Called when the ref count goes to 0. Will free Vk resources.
@@ -175,7 +177,7 @@
      *  Internal_dispose without freeing Vk resources. Used when we've lost context.
      */
     void internal_dispose() const {
-        this->abandonSubResources();
+        this->abandonGPUData();
 #ifdef SK_TRACE_VK_RESOURCES
         fTrace.remove(this);
 #endif
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 487633d..9b94c08 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -50,7 +50,8 @@
     }
 
     // Init uniform descriptor objects
-    fDescriptorSetManagers.emplace_back(fGpu, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+    GrVkDescriptorSetManager* dsm = GrVkDescriptorSetManager::CreateUniformManager(fGpu);
+    fDescriptorSetManagers.emplace_back(dsm);
     SkASSERT(1 == fDescriptorSetManagers.count());
     fUniformDSHandle = GrVkDescriptorSetManager::Handle(0);
 }
@@ -183,59 +184,65 @@
     return fPipelineStateCache->refPipelineState(pipeline, proc, primitiveType, renderPass);
 }
 
-void GrVkResourceProvider::getSamplerDescriptorSetHandle(const GrVkUniformHandler& uniformHandler,
+void GrVkResourceProvider::getSamplerDescriptorSetHandle(VkDescriptorType type,
+                                                         const GrVkUniformHandler& uniformHandler,
                                                          GrVkDescriptorSetManager::Handle* handle) {
     SkASSERT(handle);
+    SkASSERT(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type ||
+             VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == type);
     for (int i = 0; i < fDescriptorSetManagers.count(); ++i) {
-        if (fDescriptorSetManagers[i].isCompatible(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                                   &uniformHandler)) {
+        if (fDescriptorSetManagers[i]->isCompatible(type, &uniformHandler)) {
            *handle = GrVkDescriptorSetManager::Handle(i);
            return;
         }
     }
 
-    fDescriptorSetManagers.emplace_back(fGpu, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                        &uniformHandler);
+    GrVkDescriptorSetManager* dsm = GrVkDescriptorSetManager::CreateSamplerManager(fGpu, type,
+                                                                                   uniformHandler);
+    fDescriptorSetManagers.emplace_back(dsm);
     *handle = GrVkDescriptorSetManager::Handle(fDescriptorSetManagers.count() - 1);
 }
 
-void GrVkResourceProvider::getSamplerDescriptorSetHandle(const SkTArray<uint32_t>& visibilities,
+void GrVkResourceProvider::getSamplerDescriptorSetHandle(VkDescriptorType type,
+                                                         const SkTArray<uint32_t>& visibilities,
                                                          GrVkDescriptorSetManager::Handle* handle) {
     SkASSERT(handle);
+    SkASSERT(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type ||
+             VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == type);
     for (int i = 0; i < fDescriptorSetManagers.count(); ++i) {
-        if (fDescriptorSetManagers[i].isCompatible(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                                   visibilities)) {
+        if (fDescriptorSetManagers[i]->isCompatible(type, visibilities)) {
             *handle = GrVkDescriptorSetManager::Handle(i);
             return;
         }
     }
 
-    fDescriptorSetManagers.emplace_back(fGpu, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-                                        visibilities);
+    GrVkDescriptorSetManager* dsm = GrVkDescriptorSetManager::CreateSamplerManager(fGpu, type,
+                                                                                   visibilities);
+    fDescriptorSetManagers.emplace_back(dsm);
     *handle = GrVkDescriptorSetManager::Handle(fDescriptorSetManagers.count() - 1);
 }
 
 VkDescriptorSetLayout GrVkResourceProvider::getUniformDSLayout() const {
     SkASSERT(fUniformDSHandle.isValid());
-    return fDescriptorSetManagers[fUniformDSHandle.toIndex()].layout();
+    return fDescriptorSetManagers[fUniformDSHandle.toIndex()]->layout();
 }
 
 VkDescriptorSetLayout GrVkResourceProvider::getSamplerDSLayout(
         const GrVkDescriptorSetManager::Handle& handle) const {
     SkASSERT(handle.isValid());
-    return fDescriptorSetManagers[handle.toIndex()].layout();
+    return fDescriptorSetManagers[handle.toIndex()]->layout();
 }
 
 const GrVkDescriptorSet* GrVkResourceProvider::getUniformDescriptorSet() {
     SkASSERT(fUniformDSHandle.isValid());
-    return fDescriptorSetManagers[fUniformDSHandle.toIndex()].getDescriptorSet(fGpu,
-                                                                               fUniformDSHandle);
+    return fDescriptorSetManagers[fUniformDSHandle.toIndex()]->getDescriptorSet(fGpu,
+                                                                                fUniformDSHandle);
 }
 
 const GrVkDescriptorSet* GrVkResourceProvider::getSamplerDescriptorSet(
         const GrVkDescriptorSetManager::Handle& handle) {
     SkASSERT(handle.isValid());
-    return fDescriptorSetManagers[handle.toIndex()].getDescriptorSet(fGpu, handle);
+    return fDescriptorSetManagers[handle.toIndex()]->getDescriptorSet(fGpu, handle);
 }
 
 void GrVkResourceProvider::recycleDescriptorSet(const GrVkDescriptorSet* descSet,
@@ -244,7 +251,7 @@
     SkASSERT(handle.isValid());
     int managerIdx = handle.toIndex();
     SkASSERT(managerIdx < fDescriptorSetManagers.count());
-    fDescriptorSetManagers[managerIdx].recycleDescriptorSet(descSet);
+    fDescriptorSetManagers[managerIdx]->recycleDescriptorSet(descSet);
 }
 
 GrVkPrimaryCommandBuffer* GrVkResourceProvider::findOrCreatePrimaryCommandBuffer() {
@@ -356,7 +363,7 @@
     // We must release/destroy all command buffers and pipeline states before releasing the
     // GrVkDescriptorSetManagers
     for (int i = 0; i < fDescriptorSetManagers.count(); ++i) {
-        fDescriptorSetManagers[i].release(fGpu);
+        fDescriptorSetManagers[i]->release(fGpu);
     }
     fDescriptorSetManagers.reset();
 
@@ -416,7 +423,7 @@
     // We must abandon all command buffers and pipeline states before abandoning the
     // GrVkDescriptorSetManagers
     for (int i = 0; i < fDescriptorSetManagers.count(); ++i) {
-        fDescriptorSetManagers[i].abandon();
+        fDescriptorSetManagers[i]->abandon();
     }
     fDescriptorSetManagers.reset();
 
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 903223a..902dece 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -106,9 +106,11 @@
                                                                  GrPrimitiveType,
                                                                  const GrVkRenderPass& renderPass);
 
-    void getSamplerDescriptorSetHandle(const GrVkUniformHandler&,
+    void getSamplerDescriptorSetHandle(VkDescriptorType type,
+                                       const GrVkUniformHandler&,
                                        GrVkDescriptorSetManager::Handle* handle);
-    void getSamplerDescriptorSetHandle(const SkTArray<uint32_t>& visibilities,
+    void getSamplerDescriptorSetHandle(VkDescriptorType type,
+                                       const SkTArray<uint32_t>& visibilities,
                                        GrVkDescriptorSetManager::Handle* handle);
 
     // Returns the compatible VkDescriptorSetLayout to use for uniform buffers. The caller does not
@@ -255,7 +257,7 @@
     // Cache of GrVkPipelineStates
     PipelineStateCache* fPipelineStateCache;
 
-    SkSTArray<4, GrVkDescriptorSetManager, true> fDescriptorSetManagers;
+    SkSTArray<4, std::unique_ptr<GrVkDescriptorSetManager>> fDescriptorSetManagers;
 
     GrVkDescriptorSetManager::Handle fUniformDSHandle;
 };
diff --git a/src/gpu/vk/GrVkTexelBuffer.cpp b/src/gpu/vk/GrVkTexelBuffer.cpp
new file mode 100644
index 0000000..b579ea0
--- /dev/null
+++ b/src/gpu/vk/GrVkTexelBuffer.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "GrVkTexelBuffer.h"
+
+#include "GrVkGpu.h"
+
+GrVkTexelBuffer::GrVkTexelBuffer(GrVkGpu* gpu, const GrVkBuffer::Desc& desc,
+                                   const GrVkBuffer::Resource* bufferResource)
+    : INHERITED(gpu, desc.fSizeInBytes, kTexel_GrBufferType,
+                kDynamic_GrAccessPattern)
+    , GrVkBuffer(desc, bufferResource) {
+    this->registerWithCache(SkBudgeted::kYes);
+}
+
+GrVkTexelBuffer* GrVkTexelBuffer::Create(GrVkGpu* gpu, size_t size) {
+    GrVkBuffer::Desc desc;
+    desc.fDynamic = true;
+    desc.fType = GrVkBuffer::kTexel_Type;
+    desc.fSizeInBytes = size;
+
+    const GrVkBuffer::Resource* bufferResource = GrVkBuffer::Create(gpu, desc);
+    if (!bufferResource) {
+        return nullptr;
+    }
+    GrVkTexelBuffer* buffer = new GrVkTexelBuffer(gpu, desc, bufferResource);
+    if (!buffer) {
+        bufferResource->unref(gpu);
+    }
+    return buffer;
+}
+
+void GrVkTexelBuffer::onRelease() {
+    if (!this->wasDestroyed()) {
+        this->vkRelease(this->getVkGpu());
+    }
+
+    INHERITED::onRelease();
+}
+
+void GrVkTexelBuffer::onAbandon() {
+    this->vkAbandon();
+    INHERITED::onAbandon();
+}
+
+void GrVkTexelBuffer::onMap() {
+    if (!this->wasDestroyed()) {
+        this->GrBuffer::fMapPtr = this->vkMap(this->getVkGpu());
+    }
+}
+
+void GrVkTexelBuffer::onUnmap() {
+    if (!this->wasDestroyed()) {
+        this->vkUnmap(this->getVkGpu());
+    }
+}
+
+bool GrVkTexelBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
+    if (!this->wasDestroyed()) {
+        return this->vkUpdateData(this->getVkGpu(), src, srcSizeInBytes);
+    } else {
+        return false;
+    }
+}
+
+GrVkGpu* GrVkTexelBuffer::getVkGpu() const {
+    SkASSERT(!this->wasDestroyed());
+    return static_cast<GrVkGpu*>(this->getGpu());
+}
diff --git a/src/gpu/vk/GrVkTexelBuffer.h b/src/gpu/vk/GrVkTexelBuffer.h
new file mode 100644
index 0000000..449e3fc
--- /dev/null
+++ b/src/gpu/vk/GrVkTexelBuffer.h
@@ -0,0 +1,37 @@
+/*
+ * 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 GrVkTexelBuffer_DEFINED
+#define GrVkTexelBuffer_DEFINED
+
+#include "GrBuffer.h"
+#include "GrVkBuffer.h"
+
+class GrVkGpu;
+
+class GrVkTexelBuffer : public GrBuffer, public GrVkBuffer {
+public:
+    static GrVkTexelBuffer* Create(GrVkGpu* gpu, size_t size);
+
+protected:
+    void onAbandon() override;
+    void onRelease() override;
+
+private:
+    GrVkTexelBuffer(GrVkGpu* gpu, const GrVkBuffer::Desc& desc,
+                    const GrVkBuffer::Resource* resource);
+
+    void onMap() override;
+    void onUnmap() override;
+    bool onUpdateData(const void* src, size_t srcSizeInBytes) override;
+
+    GrVkGpu* getVkGpu() const;
+
+    typedef GrBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index fb6b94f..2b1655f 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -16,6 +16,15 @@
 
 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
 
+// This method parallels GrTextureProxy::highestFilterMode
+static inline GrSamplerParams::FilterMode highest_filter_mode(GrPixelConfig config) {
+    if (GrPixelConfigIsSint(config)) {
+        // We only ever want to nearest-neighbor sample signed int textures.
+        return GrSamplerParams::kNone_FilterMode;
+    }
+    return GrSamplerParams::kMipMap_FilterMode;
+}
+
 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
 GrVkTexture::GrVkTexture(GrVkGpu* gpu,
                          SkBudgeted budgeted,
@@ -24,8 +33,8 @@
                          const GrVkImageView* view)
     : GrSurface(gpu, desc)
     , GrVkImage(info, GrVkImage::kNot_Wrapped)
-    , INHERITED(gpu, desc, kTexture2DSampler_GrSLType, GrSamplerParams::kMipMap_FilterMode,
-                desc.fIsMipMapped)
+    , INHERITED(gpu, desc, kTexture2DSampler_GrSLType, highest_filter_mode(desc.fConfig),
+                info.fLevelCount > 1)
     , fTextureView(view)
     , fLinearTextureView(nullptr) {
     this->registerWithCache(budgeted);
@@ -39,8 +48,8 @@
                          GrVkImage::Wrapped wrapped)
     : GrSurface(gpu, desc)
     , GrVkImage(info, wrapped)
-    , INHERITED(gpu, desc, kTexture2DSampler_GrSLType, GrSamplerParams::kMipMap_FilterMode,
-                desc.fIsMipMapped)
+    , INHERITED(gpu, desc, kTexture2DSampler_GrSLType, highest_filter_mode(desc.fConfig),
+                info.fLevelCount > 1)
     , fTextureView(view)
     , fLinearTextureView(nullptr) {
     this->registerWithCacheWrapped();
@@ -54,15 +63,15 @@
                          GrVkImage::Wrapped wrapped)
     : GrSurface(gpu, desc)
     , GrVkImage(info, wrapped)
-    , INHERITED(gpu, desc, kTexture2DSampler_GrSLType, GrSamplerParams::kMipMap_FilterMode,
-                desc.fIsMipMapped)
+    , INHERITED(gpu, desc, kTexture2DSampler_GrSLType, highest_filter_mode(desc.fConfig),
+                info.fLevelCount > 1)
     , fTextureView(view)
     , fLinearTextureView(nullptr) {
 }
 
-GrVkTexture* GrVkTexture::CreateNewTexture(GrVkGpu* gpu, SkBudgeted budgeted,
-                                           const GrSurfaceDesc& desc,
-                                           const GrVkImage::ImageDesc& imageDesc) {
+sk_sp<GrVkTexture> GrVkTexture::CreateNewTexture(GrVkGpu* gpu, SkBudgeted budgeted,
+                                                 const GrSurfaceDesc& desc,
+                                                 const GrVkImage::ImageDesc& imageDesc) {
     SkASSERT(imageDesc.fUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT);
 
     GrVkImageInfo info;
@@ -78,7 +87,7 @@
         return nullptr;
     }
 
-    return new GrVkTexture(gpu, budgeted, desc, info, imageView);
+    return sk_sp<GrVkTexture>(new GrVkTexture(gpu, budgeted, desc, info, imageView));
 }
 
 sk_sp<GrVkTexture> GrVkTexture::MakeWrappedTexture(GrVkGpu* gpu,
@@ -96,13 +105,9 @@
         return nullptr;
     }
 
-    if (kAdoptAndCache_GrWrapOwnership == ownership) {
-        return sk_sp<GrVkTexture>(new GrVkTexture(gpu, SkBudgeted::kYes, desc, *info, imageView));
-    } else {
-        GrVkImage::Wrapped wrapped = kBorrow_GrWrapOwnership == ownership
-                ? GrVkImage::kBorrowed_Wrapped : GrVkImage::kAdopted_Wrapped;
-        return sk_sp<GrVkTexture>(new GrVkTexture(gpu, kWrapped, desc, *info, imageView, wrapped));
-    }
+    GrVkImage::Wrapped wrapped = kBorrow_GrWrapOwnership == ownership
+            ? GrVkImage::kBorrowed_Wrapped : GrVkImage::kAdopted_Wrapped;
+    return sk_sp<GrVkTexture>(new GrVkTexture(gpu, kWrapped, desc, *info, imageView, wrapped));
 }
 
 GrVkTexture::~GrVkTexture() {
@@ -147,12 +152,6 @@
     return (GrBackendObject)&fInfo;
 }
 
-std::unique_ptr<GrExternalTextureData> GrVkTexture::detachBackendTexture() {
-    // Not supported on Vulkan yet
-    // TODO: Add thread-safe memory pools, and implement this.
-    return nullptr;
-}
-
 GrVkGpu* GrVkTexture::getVkGpu() const {
     SkASSERT(!this->wasDestroyed());
     return static_cast<GrVkGpu*>(this->getGpu());
@@ -187,7 +186,7 @@
         return false;
     }
 
-    bool renderTarget = SkToBool(fDesc.fFlags & kRenderTarget_GrSurfaceFlag);
+    bool renderTarget = SkToBool(this->asRenderTarget());
 
     VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
     if (renderTarget) {
@@ -198,8 +197,8 @@
     GrVkImage::ImageDesc imageDesc;
     imageDesc.fImageType = VK_IMAGE_TYPE_2D;
     imageDesc.fFormat = fInfo.fFormat;
-    imageDesc.fWidth = fDesc.fWidth;
-    imageDesc.fHeight = fDesc.fHeight;
+    imageDesc.fWidth = this->width();
+    imageDesc.fHeight = this->height();
     imageDesc.fLevels = mipLevels;
     imageDesc.fSamples = 1;
     imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index db7124e..fc3ffdf 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -18,8 +18,8 @@
 
 class GrVkTexture : public GrTexture, public virtual GrVkImage {
 public:
-    static GrVkTexture* CreateNewTexture(GrVkGpu*, SkBudgeted budgeted, const GrSurfaceDesc&,
-                                         const GrVkImage::ImageDesc&);
+    static sk_sp<GrVkTexture> CreateNewTexture(GrVkGpu*, SkBudgeted budgeted, const GrSurfaceDesc&,
+                                               const GrVkImage::ImageDesc&);
 
     static sk_sp<GrVkTexture> MakeWrappedTexture(GrVkGpu*, const GrSurfaceDesc&,
                                                  GrWrapOwnership, const GrVkImageInfo*);
@@ -34,6 +34,13 @@
 
     bool reallocForMipmap(GrVkGpu* gpu, uint32_t mipLevels);
 
+    // In Vulkan we call the release proc after we are finished with the underlying
+    // GrVkImage::Resource object (which occurs after the GPU has finsihed all work on it).
+    void setRelease(GrTexture::ReleaseProc proc, GrTexture::ReleaseCtx ctx) override {
+        // Forward the release proc on to GrVkImage
+        this->setResourceRelease(proc, ctx);
+    }
+
 protected:
     GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, const GrVkImageView*,
                 GrVkImage::Wrapped wrapped);
@@ -42,7 +49,6 @@
 
     void onAbandon() override;
     void onRelease() override;
-    std::unique_ptr<GrExternalTextureData> detachBackendTexture() override;
 
 private:
     enum Wrapped { kWrapped };
diff --git a/src/gpu/vk/GrVkTextureRenderTarget.cpp b/src/gpu/vk/GrVkTextureRenderTarget.cpp
index 1d7d756..1b72c1f 100644
--- a/src/gpu/vk/GrVkTextureRenderTarget.cpp
+++ b/src/gpu/vk/GrVkTextureRenderTarget.cpp
@@ -18,11 +18,11 @@
 
 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
 
-GrVkTextureRenderTarget* GrVkTextureRenderTarget::Create(GrVkGpu* gpu,
-                                                         const GrSurfaceDesc& desc,
-                                                         const GrVkImageInfo& info,
-                                                         SkBudgeted budgeted,
-                                                         GrVkImage::Wrapped wrapped) {
+sk_sp<GrVkTextureRenderTarget> GrVkTextureRenderTarget::Make(GrVkGpu* gpu,
+                                                             const GrSurfaceDesc& desc,
+                                                             const GrVkImageInfo& info,
+                                                             SkBudgeted budgeted,
+                                                             GrVkImage::Wrapped wrapped) {
     VkImage image = info.fImage;
     // Create the texture ImageView
     const GrVkImageView* imageView = GrVkImageView::Create(gpu, image, info.fFormat,
@@ -87,34 +87,38 @@
         return nullptr;
     }
 
-    GrVkTextureRenderTarget* texRT;
+    sk_sp<GrVkTextureRenderTarget> texRT;
     if (desc.fSampleCnt) {
         if (GrVkImage::kNot_Wrapped == wrapped) {
-            texRT = new GrVkTextureRenderTarget(gpu, budgeted, desc,
-                                                info, imageView, msInfo,
-                                                colorAttachmentView,
-                                                resolveAttachmentView);
+            texRT = sk_sp<GrVkTextureRenderTarget>(new GrVkTextureRenderTarget(
+                                                      gpu, budgeted, desc,
+                                                      info, imageView, msInfo,
+                                                      colorAttachmentView,
+                                                      resolveAttachmentView));
         } else {
-            texRT = new GrVkTextureRenderTarget(gpu, desc,
-                                                info, imageView, msInfo,
-                                                colorAttachmentView,
-                                                resolveAttachmentView, wrapped);
+            texRT = sk_sp<GrVkTextureRenderTarget>(new GrVkTextureRenderTarget(
+                                                        gpu, desc,
+                                                        info, imageView, msInfo,
+                                                        colorAttachmentView,
+                                                        resolveAttachmentView, wrapped));
         }
     } else {
         if (GrVkImage::kNot_Wrapped == wrapped) {
-            texRT = new GrVkTextureRenderTarget(gpu, budgeted, desc,
-                                                info, imageView,
-                                                colorAttachmentView);
+            texRT = sk_sp<GrVkTextureRenderTarget>(new GrVkTextureRenderTarget(
+                                                        gpu, budgeted, desc,
+                                                        info, imageView,
+                                                        colorAttachmentView));
         } else {
-            texRT = new GrVkTextureRenderTarget(gpu, desc,
-                                                info, imageView,
-                                                colorAttachmentView, wrapped);
+            texRT = sk_sp<GrVkTextureRenderTarget>(new GrVkTextureRenderTarget(
+                                                        gpu, desc,
+                                                        info, imageView,
+                                                        colorAttachmentView, wrapped));
         }
     }
     return texRT;
 }
 
-GrVkTextureRenderTarget*
+sk_sp<GrVkTextureRenderTarget>
 GrVkTextureRenderTarget::CreateNewTextureRenderTarget(GrVkGpu* gpu,
                                                       SkBudgeted budgeted,
                                                       const GrSurfaceDesc& desc,
@@ -127,7 +131,7 @@
         return nullptr;
     }
 
-    GrVkTextureRenderTarget* trt = Create(gpu, desc, info, budgeted, GrVkImage::kNot_Wrapped);
+    sk_sp<GrVkTextureRenderTarget> trt = Make(gpu, desc, info, budgeted, GrVkImage::kNot_Wrapped);
     if (!trt) {
         GrVkImage::DestroyImageInfo(gpu, &info);
     }
@@ -143,18 +147,17 @@
     SkASSERT(info);
     // Wrapped textures require both image and allocation (because they can be mapped)
     SkASSERT(VK_NULL_HANDLE != info->fImage && VK_NULL_HANDLE != info->fAlloc.fMemory);
-    SkASSERT(kAdoptAndCache_GrWrapOwnership != ownership);  // Not supported
 
     GrVkImage::Wrapped wrapped = kBorrow_GrWrapOwnership == ownership ? GrVkImage::kBorrowed_Wrapped
                                                                       : GrVkImage::kAdopted_Wrapped;
 
-    return sk_sp<GrVkTextureRenderTarget>(Create(gpu, desc, *info, SkBudgeted::kNo, wrapped));
+    return Make(gpu, desc, *info, SkBudgeted::kNo, wrapped);
 }
 
 bool GrVkTextureRenderTarget::updateForMipmap(GrVkGpu* gpu, const GrVkImageInfo& newInfo) {
     VkFormat pixelFormat;
-    GrPixelConfigToVkFormat(fDesc.fConfig, &pixelFormat);
-    if (fDesc.fSampleCnt) {
+    GrPixelConfigToVkFormat(this->config(), &pixelFormat);
+    if (this->numStencilSamples()) {
         const GrVkImageView* resolveAttachmentView =
                 GrVkImageView::Create(gpu,
                                       newInfo.fImage,
diff --git a/src/gpu/vk/GrVkTextureRenderTarget.h b/src/gpu/vk/GrVkTextureRenderTarget.h
index 2877a36..7639dc0 100644
--- a/src/gpu/vk/GrVkTextureRenderTarget.h
+++ b/src/gpu/vk/GrVkTextureRenderTarget.h
@@ -26,9 +26,9 @@
 
 class GrVkTextureRenderTarget: public GrVkTexture, public GrVkRenderTarget {
 public:
-    static GrVkTextureRenderTarget* CreateNewTextureRenderTarget(GrVkGpu*, SkBudgeted,
-                                                                 const GrSurfaceDesc&,
-                                                                 const GrVkImage::ImageDesc&);
+    static sk_sp<GrVkTextureRenderTarget> CreateNewTextureRenderTarget(GrVkGpu*, SkBudgeted,
+                                                                       const GrSurfaceDesc&,
+                                                                       const GrVkImage::ImageDesc&);
 
     static sk_sp<GrVkTextureRenderTarget> MakeWrappedTextureRenderTarget(GrVkGpu*,
                                                                          const GrSurfaceDesc&,
@@ -106,16 +106,18 @@
         this->registerWithCacheWrapped();
     }
 
-    static GrVkTextureRenderTarget* Create(GrVkGpu*,
-                                           const GrSurfaceDesc&,
-                                           const GrVkImageInfo&,
-                                           SkBudgeted budgeted,
-                                           GrVkImage::Wrapped wrapped);
+    static sk_sp<GrVkTextureRenderTarget> Make(GrVkGpu*,
+                                               const GrSurfaceDesc&,
+                                               const GrVkImageInfo&,
+                                               SkBudgeted budgeted,
+                                               GrVkImage::Wrapped wrapped);
 
     // GrGLRenderTarget accounts for the texture's memory and any MSAA renderbuffer's memory.
     size_t onGpuMemorySize() const override {
         // The plus 1 is to account for the resolve texture.
-        return GrSurface::ComputeSize(fDesc, fDesc.fSampleCnt+1,      // TODO: this still correct?
+        int numColorSamples = this->numColorSamples() + 1;
+        return GrSurface::ComputeSize(this->config(), this->width(), this->height(),
+                                      numColorSamples,  // TODO: this still correct?
                                       this->texturePriv().hasMipMaps());
     }
 };
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index f69c9d1..74f12e4 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -145,9 +145,12 @@
                                                                             int arrayCount,
                                                                             const char** outName) {
     SkASSERT(name && strlen(name));
-    SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_GrShaderFlag|kFragment_GrShaderFlag);
-    SkASSERT(0 == (~kVisibilityMask & visibility));
-    SkASSERT(0 != visibility);
+    // For now asserting the the visibility is either geometry types (vertex, tesselation, geometry,
+    // etc.) or only fragment.
+    SkASSERT(kVertex_GrShaderFlag == visibility ||
+             kGeometry_GrShaderFlag == visibility ||
+             (kVertex_GrShaderFlag | kGeometry_GrShaderFlag) == visibility ||
+             kFragment_GrShaderFlag == visibility);
     SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type));
     GrSLTypeIsFloatType(type);
 
@@ -165,16 +168,20 @@
     }
     fProgramBuilder->nameVariable(uni.fVariable.accessName(), prefix, name, mangleName);
     uni.fVariable.setArrayCount(arrayCount);
-    // For now asserting the the visibility is either only vertex or only fragment
-    SkASSERT(kVertex_GrShaderFlag == visibility || kFragment_GrShaderFlag == visibility);
     uni.fVisibility = visibility;
     uni.fVariable.setPrecision(precision);
     // When outputing the GLSL, only the outer uniform block will get the Uniform modifier. Thus
     // we set the modifier to none for all uniforms declared inside the block.
     uni.fVariable.setTypeModifier(GrShaderVar::kNone_TypeModifier);
 
-    uint32_t* currentOffset = kVertex_GrShaderFlag == visibility ? &fCurrentVertexUBOOffset
-                                                                 : &fCurrentFragmentUBOOffset;
+    uint32_t* currentOffset;
+    uint32_t geomStages = kVertex_GrShaderFlag | kGeometry_GrShaderFlag;
+    if (geomStages & visibility) {
+        currentOffset = &fCurrentGeometryUBOOffset;
+    } else {
+        SkASSERT(kFragment_GrShaderFlag == visibility);
+        currentOffset = &fCurrentFragmentUBOOffset;
+    }
     get_ubo_aligned_offset(&uni.fUBOffset, currentOffset, type, arrayCount);
 
     SkString layoutQualifier;
@@ -194,9 +201,10 @@
                                                                    GrSLPrecision precision,
                                                                    const char* name) {
     SkASSERT(name && strlen(name));
-    SkDEBUGCODE(static const uint32_t kVisMask = kVertex_GrShaderFlag | kFragment_GrShaderFlag);
-    SkASSERT(0 == (~kVisMask & visibility));
-    SkASSERT(0 != visibility);
+    // For now asserting the the visibility is either only vertex, geometry, or fragment
+    SkASSERT(kVertex_GrShaderFlag == visibility ||
+             kFragment_GrShaderFlag == visibility ||
+             kGeometry_GrShaderFlag == visibility);
     SkString mangleName;
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
@@ -217,8 +225,34 @@
     return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1);
 }
 
+GrGLSLUniformHandler::TexelBufferHandle GrVkUniformHandler::addTexelBuffer(uint32_t visibility,
+                                                                           GrSLPrecision precision,
+                                                                           const char* name) {
+    SkASSERT(name && strlen(name));
+    SkDEBUGCODE(static const uint32_t kVisMask = kVertex_GrShaderFlag | kFragment_GrShaderFlag);
+    SkASSERT(0 == (~kVisMask & visibility));
+    SkASSERT(0 != visibility);
+    SkString mangleName;
+    char prefix = 'u';
+    fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
+
+    UniformInfo& info = fTexelBuffers.push_back();
+    info.fVariable.setType(kBufferSampler_GrSLType);
+    info.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
+    info.fVariable.setPrecision(precision);
+    info.fVariable.setName(mangleName);
+    SkString layoutQualifier;
+    layoutQualifier.appendf("set=%d, binding=%d", kTexelBufferDescSet, fTexelBuffers.count()- 1);
+    info.fVariable.addLayoutQualifier(layoutQualifier.c_str());
+    info.fVisibility = visibility;
+    info.fUBOffset = 0;
+    return GrGLSLUniformHandler::TexelBufferHandle(fTexelBuffers.count() - 1);
+}
+
 void GrVkUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
-    SkASSERT(kVertex_GrShaderFlag == visibility || kFragment_GrShaderFlag == visibility);
+    SkASSERT(kVertex_GrShaderFlag == visibility ||
+             kGeometry_GrShaderFlag == visibility ||
+             kFragment_GrShaderFlag == visibility);
 
     for (int i = 0; i < fSamplers.count(); ++i) {
         const UniformInfo& sampler = fSamplers[i];
@@ -229,11 +263,19 @@
         }
     }
 
+    for (int i = 0; i < fTexelBuffers.count(); ++i) {
+        const UniformInfo& texelBuffer = fTexelBuffers[i];
+        if (visibility == texelBuffer.fVisibility) {
+            texelBuffer.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
+            out->append(";\n");
+        }
+    }
+
     SkDEBUGCODE(bool firstOffsetCheck = false);
     SkString uniformsString;
     for (int i = 0; i < fUniforms.count(); ++i) {
         const UniformInfo& localUniform = fUniforms[i];
-        if (visibility == localUniform.fVisibility) {
+        if (visibility & localUniform.fVisibility) {
             if (GrSLTypeIsFloatType(localUniform.fVariable.getType())) {
 #ifdef SK_DEBUG
                 if (!firstOffsetCheck) {
@@ -249,9 +291,19 @@
         }
     }
     if (!uniformsString.isEmpty()) {
-        uint32_t uniformBinding = (visibility == kVertex_GrShaderFlag) ? kVertexBinding
-                                                                       : kFragBinding;
-        const char* stage = (visibility == kVertex_GrShaderFlag) ? "vertex" : "fragment";
+        uint32_t uniformBinding;
+        const char* stage;
+        if (kVertex_GrShaderFlag == visibility) {
+            uniformBinding = kGeometryBinding;
+            stage = "vertex";
+        } else if (kGeometry_GrShaderFlag == visibility) {
+            uniformBinding = kGeometryBinding;
+            stage = "geometry";
+        } else {
+            SkASSERT(kFragment_GrShaderFlag == visibility);
+            uniformBinding = kFragBinding;
+            stage = "fragment";
+        }
         out->appendf("layout (set=%d, binding=%d) uniform %sUniformBuffer\n{\n",
                      kUniformBufferDescSet, uniformBinding, stage);
         out->appendf("%s\n};\n", uniformsString.c_str());
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index 808eed7..862fbfc 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -19,9 +19,10 @@
     enum {
         kUniformBufferDescSet = 0,
         kSamplerDescSet = 1,
+        kTexelBufferDescSet = 2,
     };
     enum {
-        kVertexBinding = 0,
+        kGeometryBinding = 0,
         kFragBinding = 1,
     };
 
@@ -46,9 +47,9 @@
         : INHERITED(program)
         , fUniforms(kUniformsPerBlock)
         , fSamplers(kUniformsPerBlock)
-        , fCurrentVertexUBOOffset(0)
-        , fCurrentFragmentUBOOffset(0)
-        , fCurrentSamplerBinding(0) {
+        , fTexelBuffers(kUniformsPerBlock)
+        , fCurrentGeometryUBOOffset(0)
+        , fCurrentFragmentUBOOffset(0) {
     }
 
     UniformHandle internalAddUniformArray(uint32_t visibility,
@@ -76,6 +77,17 @@
         return fSamplers[handle.toIndex()].fVisibility;
     }
 
+    TexelBufferHandle addTexelBuffer(uint32_t visibility, GrSLPrecision,
+                                     const char* name) override;
+
+    int numTexelBuffers() const { return fTexelBuffers.count(); }
+    const GrShaderVar& texelBufferVariable(TexelBufferHandle handle) const override {
+        return fTexelBuffers[handle.toIndex()].fVariable;
+    }
+    uint32_t texelBufferVisibility(TexelBufferHandle handle) const {
+        return fTexelBuffers[handle.toIndex()].fVisibility;
+    }
+
     ImageStorageHandle addImageStorage(uint32_t visibility, GrSLType,  GrImageStorageFormat,
                                        GrSLMemoryModel, GrSLRestrict, GrIOType,
                                        const char* name) override {
@@ -91,7 +103,7 @@
 
     void appendUniformDecls(GrShaderFlags, SkString*) const override;
 
-    bool hasVertexUniforms() const { return fCurrentVertexUBOOffset > 0; }
+    bool hasGeometryUniforms() const { return fCurrentGeometryUBOOffset > 0; }
     bool hasFragmentUniforms() const { return fCurrentFragmentUBOOffset > 0; }
 
 
@@ -103,10 +115,10 @@
     UniformInfoArray    fUniforms;
     UniformInfoArray    fSamplers;
     SkTArray<GrSwizzle> fSamplerSwizzles;
+    UniformInfoArray    fTexelBuffers;
 
-    uint32_t            fCurrentVertexUBOOffset;
+    uint32_t            fCurrentGeometryUBOOffset;
     uint32_t            fCurrentFragmentUBOOffset;
-    uint32_t            fCurrentSamplerBinding;
 
     friend class GrVkPipelineStateBuilder;
     friend class GrVkDescriptorSetManager;
diff --git a/src/gpu/vk/GrVkUtil.cpp b/src/gpu/vk/GrVkUtil.cpp
index 1a73f9a..c56a4fb 100644
--- a/src/gpu/vk/GrVkUtil.cpp
+++ b/src/gpu/vk/GrVkUtil.cpp
@@ -48,10 +48,6 @@
         case kGray_8_GrPixelConfig:
             *format = VK_FORMAT_R8_UNORM;
             return true;
-        case kETC1_GrPixelConfig:
-            // converting to ETC2 which is a superset of ETC1
-            *format = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
-            return true;
         case kRGBA_float_GrPixelConfig:
             *format = VK_FORMAT_R32G32B32A32_SFLOAT;
             return true;
@@ -69,58 +65,38 @@
     return false;
 }
 
-bool GrVkFormatToPixelConfig(VkFormat format, GrPixelConfig* config) {
-    GrPixelConfig dontCare;
-    if (!config) {
-        config = &dontCare;
-    }
-
+GrPixelConfig GrVkFormatToPixelConfig(VkFormat format) {
     switch (format) {
         case VK_FORMAT_R8G8B8A8_UNORM:
-            *config = kRGBA_8888_GrPixelConfig;
-            break;
+            return kRGBA_8888_GrPixelConfig;
         case VK_FORMAT_B8G8R8A8_UNORM:
-            *config = kBGRA_8888_GrPixelConfig;
-            break;
+            return kBGRA_8888_GrPixelConfig;
         case VK_FORMAT_R8G8B8A8_SRGB:
-            *config = kSRGBA_8888_GrPixelConfig;
-            break;
+            return kSRGBA_8888_GrPixelConfig;
         case VK_FORMAT_B8G8R8A8_SRGB:
-            *config = kSBGRA_8888_GrPixelConfig;
-            break;
+            return kSBGRA_8888_GrPixelConfig;
         case VK_FORMAT_R8G8B8A8_SINT:
-            *config = kRGBA_8888_sint_GrPixelConfig;
-            break;
+            return kRGBA_8888_sint_GrPixelConfig;
         case VK_FORMAT_R5G6B5_UNORM_PACK16:
-            *config = kRGB_565_GrPixelConfig;
+            return kRGB_565_GrPixelConfig;
             break;
         case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
             // R4G4B4A4 is not required to be supported so we actually
             // store RGBA_4444 data as B4G4R4A4.
-            *config = kRGBA_4444_GrPixelConfig;
-            break;
+            return kRGBA_4444_GrPixelConfig;
         case VK_FORMAT_R8_UNORM:
-            *config = kAlpha_8_GrPixelConfig;
-            break;
-        case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
-            *config = kETC1_GrPixelConfig;      // this conversion seems a bit sketchy
-            break;
+            return kAlpha_8_GrPixelConfig;
         case VK_FORMAT_R32G32B32A32_SFLOAT:
-            *config = kRGBA_float_GrPixelConfig;
-            break;
+            return kRGBA_float_GrPixelConfig;
         case VK_FORMAT_R32G32_SFLOAT:
-            *config = kRG_float_GrPixelConfig;
-            break;
+            return kRG_float_GrPixelConfig;
         case VK_FORMAT_R16G16B16A16_SFLOAT:
-            *config = kRGBA_half_GrPixelConfig;
-            break;
+            return kRGBA_half_GrPixelConfig;
         case VK_FORMAT_R16_SFLOAT:
-            *config = kAlpha_half_GrPixelConfig;
-            break;
+            return kAlpha_half_GrPixelConfig;
         default:
-            return false;
+            return kUnknown_GrPixelConfig;
     }
-    return true;
 }
 
 bool GrVkFormatIsSRGB(VkFormat format, VkFormat* linearFormat) {
@@ -283,7 +259,7 @@
         SkASSERT(false);
     }
     *outInputs = program->fInputs;
-    SkString code;
+    SkSL::String code;
     if (!gpu->shaderCompiler()->toSPIRV(*program, &code)) {
         SkDebugf("%s\n", gpu->shaderCompiler()->errorText().c_str());
         return false;
diff --git a/src/gpu/vk/GrVkUtil.h b/src/gpu/vk/GrVkUtil.h
index ba07bca..72ed483 100644
--- a/src/gpu/vk/GrVkUtil.h
+++ b/src/gpu/vk/GrVkUtil.h
@@ -35,7 +35,7 @@
 /**
 * Returns the GrPixelConfig for the given vulkan texture format
 */
-bool GrVkFormatToPixelConfig(VkFormat format, GrPixelConfig* config);
+GrPixelConfig GrVkFormatToPixelConfig(VkFormat format);
 
 /**
  * Returns true if the given vulkan texture format is sRGB encoded.
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index e45bbd7..858aa9b 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -9,7 +9,6 @@
 #include "SkBitmapCache.h"
 #include "SkCanvas.h"
 #include "SkColorSpace_Base.h"
-#include "SkCrossContextImageData.h"
 #include "SkData.h"
 #include "SkImageEncoder.h"
 #include "SkImageFilter.h"
@@ -22,7 +21,6 @@
 #include "SkPicture.h"
 #include "SkPixelRef.h"
 #include "SkPixelSerializer.h"
-#include "SkRGBAToYUV.h"
 #include "SkReadPixelsRec.h"
 #include "SkSpecialImage.h"
 #include "SkStream.h"
@@ -67,7 +65,6 @@
     //
     SkBitmap bm;
     if (as_IB(this)->getROPixels(&bm, dst.info().colorSpace(), chint)) {
-        bm.lockPixels();
         SkPixmap pmap;
         // Note: By calling the pixmap scaler, we never cache the final result, so the chint
         //       is (currently) only being applied to the getROPixels. If we get a request to
@@ -115,15 +112,14 @@
     }
 
     SkBitmap bm;
-    SkAutoPixmapUnlock apu;
+    SkPixmap pmap;
     SkColorSpace* legacyColorSpace = nullptr;
-    if (as_IB(this)->getROPixels(&bm, legacyColorSpace) &&
-        bm.requestLock(&apu)) {
+    if (as_IB(this)->getROPixels(&bm, legacyColorSpace) && bm.peekPixels(&pmap)) {
         if (serializer) {
-            return serializer->encode(apu.pixmap());
+            return serializer->encode(pmap);
         } else {
             SkDynamicMemoryWStream buf;
-            return SkEncodeImage(&buf, apu.pixmap(), SkEncodedImageFormat::kPNG, 100)
+            return SkEncodeImage(&buf, pmap, SkEncodedImageFormat::kPNG, 100)
                    ? buf.detachAsData().release() : nullptr;
         }
     }
@@ -132,8 +128,7 @@
 }
 
 SkData* SkImage::refEncoded() const {
-    GrContext* ctx = nullptr;   // should we allow the caller to pass in a ctx?
-    return as_IB(this)->onRefEncoded(ctx);
+    return as_IB(this)->onRefEncoded();
 }
 
 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) {
@@ -179,6 +174,13 @@
     return as_IB(this)->onGetTextureHandle(flushPendingGrContextIO, origin);
 }
 
+bool SkImage::isValid(GrContext* context) const {
+    if (context && context->abandoned()) {
+        return false;
+    }
+    return as_IB(this)->onIsValid(context);
+}
+
 #else
 
 GrTexture* SkImage::getTexture() const { return nullptr; }
@@ -187,6 +189,13 @@
 
 GrBackendObject SkImage::getTextureHandle(bool, GrSurfaceOrigin*) const { return 0; }
 
+bool SkImage::isValid(GrContext* context) const {
+    if (context) {
+        return false;
+    }
+    return as_IB(this)->onIsValid(context);
+}
+
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -202,22 +211,10 @@
     }
 }
 
-bool SkImage_Base::onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
-                                    const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
-    return SkRGBAToYUV(this, sizes, planes, rowBytes, colorSpace);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
     return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
 }
 
-bool SkImage::readYUV8Planes(const SkISize sizes[3], void* const planes[3],
-                             const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
-    return as_IB(this)->onReadYUV8Planes(sizes, planes, rowBytes, colorSpace);
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
@@ -302,22 +299,28 @@
     return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType;
 }
 
-sk_sp<SkImage> SkImage_Base::makeColorSpace(sk_sp<SkColorSpace> target) const {
+sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target,
+                                       SkTransferFunctionBehavior premulBehavior) const {
     SkColorSpaceTransferFn fn;
     if (!target || !target->isNumericalTransferFn(&fn)) {
         return nullptr;
     }
 
     // No need to create a new image if:
-    // (1) The color spaces are equal (nullptr is considered to be sRGB).
+    // (1) The color spaces are equal.
     // (2) The color type is kAlpha8.
-    if ((!this->colorSpace() && target->isSRGB()) ||
-            SkColorSpace::Equals(this->colorSpace(), target.get()) ||
-            kAlpha_8_SkColorType == this->onImageInfo().colorType()) {
-        return sk_ref_sp(const_cast<SkImage_Base*>(this));
+    if (SkColorSpace::Equals(this->colorSpace(), target.get()) ||
+            kAlpha_8_SkColorType == as_IB(this)->onImageInfo().colorType()) {
+        return sk_ref_sp(const_cast<SkImage*>(this));
     }
 
-    return this->onMakeColorSpace(std::move(target));
+    SkColorType targetColorType = kN32_SkColorType;
+    if (SkTransferFunctionBehavior::kRespect == premulBehavior && target->gammaIsLinear()) {
+        targetColorType = kRGBA_F16_SkColorType;
+    }
+
+    // TODO: We might consider making this a deferred conversion?
+    return as_IB(this)->onMakeColorSpace(std::move(target), targetColorType, premulBehavior);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////
@@ -334,6 +337,13 @@
     return nullptr;
 }
 
+sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
+                                        const GrBackendTexture& tex, GrSurfaceOrigin origin,
+                                        SkAlphaType at, sk_sp<SkColorSpace> cs,
+                                        TextureReleaseProc releaseP, ReleaseContext releaseC) {
+    return nullptr;
+}
+
 size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
                                             const DeferredTextureImageUsageParams[],
                                             int paramCnt, void* buffer,
@@ -351,6 +361,12 @@
     return nullptr;
 }
 
+sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
+                                               const GrBackendTexture& tex, GrSurfaceOrigin origin,
+                                               SkAlphaType at, sk_sp<SkColorSpace> cs) {
+    return nullptr;
+}
+
 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space,
                                                 const GrBackendObject yuvTextureHandles[3],
                                                 const SkISize yuvSizes[3],
@@ -363,21 +379,6 @@
     return nullptr;
 }
 
-std::unique_ptr<SkCrossContextImageData> SkCrossContextImageData::MakeFromEncoded(
-        GrContext*, sk_sp<SkData> encoded, SkColorSpace* dstColorSpace) {
-    sk_sp<SkImage> image = SkImage::MakeFromEncoded(std::move(encoded));
-    if (!image) {
-        return nullptr;
-    }
-    // TODO: Force decode to raster here?
-    return std::unique_ptr<SkCrossContextImageData>(new SkCrossContextImageData(std::move(image)));
-}
-
-sk_sp<SkImage> SkImage::MakeFromCrossContextImageData(
-        GrContext*, std::unique_ptr<SkCrossContextImageData> ccid) {
-    return ccid->fImage;
-}
-
 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
     return sk_ref_sp(const_cast<SkImage*>(this));
 }
diff --git a/src/image/SkImageShader.cpp b/src/image/SkImageShader.cpp
deleted file mode 100644
index c8d6a8c..0000000
--- a/src/image/SkImageShader.cpp
+++ /dev/null
@@ -1,366 +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.
- */
-
-#include "SkArenaAlloc.h"
-#include "SkBitmapController.h"
-#include "SkBitmapProcShader.h"
-#include "SkBitmapProvider.h"
-#include "SkColorTable.h"
-#include "SkEmptyShader.h"
-#include "SkImage_Base.h"
-#include "SkImageShader.h"
-#include "SkImageShaderContext.h"
-#include "SkPM4fPriv.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-
-SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
-    : INHERITED(matrix)
-    , fImage(std::move(img))
-    , fTileModeX(tmx)
-    , fTileModeY(tmy)
-{}
-
-sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
-    const TileMode tx = (TileMode)buffer.readUInt();
-    const TileMode ty = (TileMode)buffer.readUInt();
-    SkMatrix matrix;
-    buffer.readMatrix(&matrix);
-    sk_sp<SkImage> img = buffer.readImage();
-    if (!img) {
-        return nullptr;
-    }
-    return SkImageShader::Make(std::move(img), tx, ty, &matrix);
-}
-
-void SkImageShader::flatten(SkWriteBuffer& buffer) const {
-    buffer.writeUInt(fTileModeX);
-    buffer.writeUInt(fTileModeY);
-    buffer.writeMatrix(this->getLocalMatrix());
-    buffer.writeImage(fImage.get());
-}
-
-bool SkImageShader::isOpaque() const {
-    return fImage->isOpaque();
-}
-
-SkShader::Context* SkImageShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
-    return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
-                                                 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
-                                                 rec, alloc);
-}
-
-SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
-    if (texM) {
-        *texM = this->getLocalMatrix();
-    }
-    if (xy) {
-        xy[0] = (TileMode)fTileModeX;
-        xy[1] = (TileMode)fTileModeY;
-    }
-    return const_cast<SkImage*>(fImage.get());
-}
-
-#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
-bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
-    const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
-    if (!bm) {
-        return false;
-    }
-
-    if (texture) {
-        *texture = *bm;
-    }
-    if (texM) {
-        *texM = this->getLocalMatrix();
-    }
-    if (xy) {
-        xy[0] = (TileMode)fTileModeX;
-        xy[1] = (TileMode)fTileModeY;
-    }
-    return true;
-}
-#endif
-
-static bool bitmap_is_too_big(int w, int h) {
-    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
-    // communicates between its matrix-proc and its sampler-proc. Until we can
-    // widen that, we have to reject bitmaps that are larger.
-    //
-    static const int kMaxSize = 65535;
-
-    return w > kMaxSize || h > kMaxSize;
-}
-
-sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
-                                    const SkMatrix* localMatrix) {
-    if (!image || bitmap_is_too_big(image->width(), image->height())) {
-        return sk_make_sp<SkEmptyShader>();
-    } else {
-        return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix);
-    }
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void SkImageShader::toString(SkString* str) const {
-    const char* gTileModeName[SkShader::kTileModeCount] = {
-        "clamp", "repeat", "mirror"
-    };
-
-    str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
-    fImage->toString(str);
-    this->INHERITED::toString(str);
-    str->append(")");
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "GrContext.h"
-#include "effects/GrSimpleTextureEffect.h"
-#include "effects/GrBicubicEffect.h"
-#include "effects/GrSimpleTextureEffect.h"
-
-sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const {
-
-    SkMatrix lmInverse;
-    if (!this->getLocalMatrix().invert(&lmInverse)) {
-        return nullptr;
-    }
-    if (args.fLocalMatrix) {
-        SkMatrix inv;
-        if (!args.fLocalMatrix->invert(&inv)) {
-            return nullptr;
-        }
-        lmInverse.postConcat(inv);
-    }
-
-    SkShader::TileMode tm[] = { fTileModeX, fTileModeY };
-
-    // Must set wrap and filter on the sampler before requesting a texture. In two places below
-    // we check the matrix scale factors to determine how to interpret the filter quality setting.
-    // This completely ignores the complexity of the drawVertices case where explicit local coords
-    // are provided by the caller.
-    bool doBicubic;
-    GrSamplerParams::FilterMode textureFilterMode =
-    GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(),
-                                    &doBicubic);
-    GrSamplerParams params(tm, textureFilterMode);
-    sk_sp<SkColorSpace> texColorSpace;
-    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
-    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params,
-                                                                 args.fDstColorSpace,
-                                                                 &texColorSpace, scaleAdjust));
-    if (!proxy) {
-        return nullptr;
-    }
-
-    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
-
-    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
-
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
-                                                                       args.fDstColorSpace);
-    sk_sp<GrFragmentProcessor> inner;
-    if (doBicubic) {
-        inner = GrBicubicEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
-                                      std::move(colorSpaceXform), lmInverse, tm);
-    } else {
-        inner = GrSimpleTextureEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
-                                            std::move(colorSpaceXform), lmInverse, params);
-    }
-
-    if (isAlphaOnly) {
-        return inner;
-    }
-    return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)));
-}
-
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-#include "SkImagePriv.h"
-
-sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
-                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
-                                   SkCopyPixelsMode cpm) {
-    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
-                               tmx, tmy, localMatrix);
-}
-
-static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
-    SkMatrix lm;
-    buffer.readMatrix(&lm);
-    sk_sp<SkImage> image = buffer.readBitmapAsImage();
-    SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
-    SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
-    return image ? image->makeShader(mx, my, &lm) : nullptr;
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShader)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
-SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShader_Type);
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-
-bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkArenaAlloc* scratch,
-                                   const SkMatrix& ctm, const SkPaint& paint,
-                                   const SkMatrix* localM) const {
-    auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix());
-    if (localM) {
-        matrix.preConcat(*localM);
-    }
-
-    if (!matrix.invert(&matrix)) {
-        return false;
-    }
-    auto quality = paint.getFilterQuality();
-
-    SkBitmapProvider provider(fImage.get(), dst);
-    SkDefaultBitmapController controller(SkDefaultBitmapController::CanShadeHQ::kYes);
-    std::unique_ptr<SkBitmapController::State> state {
-        controller.requestBitmap(provider, matrix, quality)
-    };
-    if (!state) {
-        return false;
-    }
-
-    const SkPixmap& pm = state->pixmap();
-    matrix  = state->invMatrix();
-    quality = state->quality();
-    auto info = pm.info();
-
-    // When the matrix is just an integer translate, bilerp == nearest neighbor.
-    if (quality == kLow_SkFilterQuality &&
-        matrix.getType() <= SkMatrix::kTranslate_Mask &&
-        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
-        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
-        quality = kNone_SkFilterQuality;
-    }
-
-    // See skia:4649 and the GM image_scale_aligned.
-    if (quality == kNone_SkFilterQuality) {
-        if (matrix.getScaleX() >= 0) {
-            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
-                                            floorf(matrix.getTranslateX())));
-        }
-        if (matrix.getScaleY() >= 0) {
-            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
-                                            floorf(matrix.getTranslateY())));
-        }
-    }
-
-    auto ctx = scratch->make<SkImageShaderContext>();
-    ctx->state   = std::move(state);  // Extend lifetime to match the pipeline's.
-    ctx->pixels  = pm.addr();
-    ctx->ctable  = pm.ctable();
-    ctx->color4f = SkColor4f_from_SkColor(paint.getColor(), dst);
-    ctx->stride  = pm.rowBytesAsPixels();
-    ctx->width   = (float)pm.width();
-    ctx->height  = (float)pm.height();
-    if (matrix.asAffine(ctx->matrix)) {
-        p->append(SkRasterPipeline::matrix_2x3, ctx->matrix);
-    } else {
-        matrix.get9(ctx->matrix);
-        p->append(SkRasterPipeline::matrix_perspective, ctx->matrix);
-    }
-
-    auto append_tiling_and_gather = [&] {
-        switch (fTileModeX) {
-            case kClamp_TileMode:  p->append(SkRasterPipeline::clamp_x,  &ctx->width); break;
-            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, &ctx->width); break;
-            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, &ctx->width); break;
-        }
-        switch (fTileModeY) {
-            case kClamp_TileMode:  p->append(SkRasterPipeline::clamp_y,  &ctx->height); break;
-            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, &ctx->height); break;
-            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, &ctx->height); break;
-        }
-        switch (info.colorType()) {
-            case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::gather_a8,   ctx); break;
-            case kIndex_8_SkColorType:   p->append(SkRasterPipeline::gather_i8,   ctx); break;
-            case kGray_8_SkColorType:    p->append(SkRasterPipeline::gather_g8,   ctx); break;
-            case kRGB_565_SkColorType:   p->append(SkRasterPipeline::gather_565,  ctx); break;
-            case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
-            case kRGBA_8888_SkColorType:
-            case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
-            case kRGBA_F16_SkColorType:  p->append(SkRasterPipeline::gather_f16,  ctx); break;
-            default: SkASSERT(false);
-        }
-        if (info.gammaCloseToSRGB() && dst != nullptr) {
-            p->append_from_srgb(info.alphaType());
-        }
-    };
-
-    auto sample = [&](SkRasterPipeline::StockStage setup_x,
-                      SkRasterPipeline::StockStage setup_y) {
-        p->append(setup_x, ctx);
-        p->append(setup_y, ctx);
-        append_tiling_and_gather();
-        p->append(SkRasterPipeline::accumulate, ctx);
-    };
-
-    if (quality == kNone_SkFilterQuality) {
-        append_tiling_and_gather();
-    } else if (quality == kLow_SkFilterQuality) {
-        p->append(SkRasterPipeline::save_xy, ctx);
-
-        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
-        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
-        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
-        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
-
-        p->append(SkRasterPipeline::move_dst_src);
-    } else {
-        p->append(SkRasterPipeline::save_xy, ctx);
-
-        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
-        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
-        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
-        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
-
-        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
-        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
-        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
-        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
-
-        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
-        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
-        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
-        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
-
-        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
-        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
-        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
-        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
-
-        p->append(SkRasterPipeline::move_dst_src);
-    }
-
-    auto effective_color_type = [](SkColorType ct) {
-        return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct;
-    };
-
-    if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) {
-        p->append(SkRasterPipeline::swap_rb);
-    }
-    if (info.colorType() == kAlpha_8_SkColorType) {
-        p->append(SkRasterPipeline::set_rgb, &ctx->color4f);
-    }
-    if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
-        p->append(SkRasterPipeline::premul);
-    }
-    if (quality > kLow_SkFilterQuality) {
-        // Bicubic filtering naturally produces out of range values on both sides.
-        p->append(SkRasterPipeline::clamp_0);
-        p->append(SkRasterPipeline::clamp_a);
-    }
-    return append_gamut_transform(p, scratch, info.colorSpace(), dst);
-}
diff --git a/src/image/SkImageShader.h b/src/image/SkImageShader.h
deleted file mode 100644
index 8960812..0000000
--- a/src/image/SkImageShader.h
+++ /dev/null
@@ -1,52 +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 SkImageShader_DEFINED
-#define SkImageShader_DEFINED
-
-#include "SkImage.h"
-#include "SkShader.h"
-#include "SkBitmapProcShader.h"
-
-class SkImageShader : public SkShader {
-public:
-    static sk_sp<SkShader> Make(sk_sp<SkImage>, TileMode tx, TileMode ty,
-                                const SkMatrix* localMatrix);
-
-    bool isOpaque() const override;
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageShader)
-
-#if SK_SUPPORT_GPU
-    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
-#endif
-
-    SkImageShader(sk_sp<SkImage>, TileMode tx, TileMode ty, const SkMatrix* localMatrix);
-
-protected:
-    void flatten(SkWriteBuffer&) const override;
-    Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override;
-#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
-    bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override;
-#endif
-    SkImage* onIsAImage(SkMatrix*, TileMode*) const override;
-
-    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
-                        const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
-
-    sk_sp<SkImage>  fImage;
-    const TileMode  fTileModeX;
-    const TileMode  fTileModeY;
-
-private:
-    friend class SkShader;
-
-    typedef SkShader INHERITED;
-};
-
-#endif
diff --git a/src/image/SkImageShaderContext.h b/src/image/SkImageShaderContext.h
deleted file mode 100644
index 24f17db..0000000
--- a/src/image/SkImageShaderContext.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkImageShaderContext_DEFINED
-#define SkImageShaderContext_DEFINED
-
-#include "SkBitmapController.h"
-#include "SkColor.h"
-#include "SkColorTable.h"
-#include <memory>
-
-// Definition used by SkImageShader.cpp and SkRasterPipeline_opts.h.
-// Otherwise, completely uninteresting.
-
-struct SkImageShaderContext {
-    std::unique_ptr<SkBitmapController::State> state;
-
-    const void*   pixels;
-    SkColorTable* ctable;
-    SkColor4f     color4f;
-    int           stride;
-    float         width;
-    float         height;
-    float         matrix[9];
-    float         x[8];
-    float         y[8];
-    float         fx[8];
-    float         fy[8];
-    float         scalex[8];
-    float         scaley[8];
-};
-
-#endif//SkImageShaderContext_DEFINED
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 5bdf940..f3e2793 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -28,7 +28,6 @@
 
 class SkImage_Base : public SkImage {
 public:
-    SkImage_Base(int width, int height, uint32_t uniqueID);
     virtual ~SkImage_Base();
 
     // User: returns image info for this SkImage.
@@ -41,9 +40,6 @@
 
     virtual const SkBitmap* onPeekBitmap() const { return nullptr; }
 
-    virtual bool onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
-                                  const size_t rowBytes[3], SkYUVColorSpace colorSpace) const;
-
     virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
                               int srcX, int srcY, CachingHint) const = 0;
 
@@ -73,30 +69,31 @@
 
     virtual sk_sp<SkImage> onMakeSubset(const SkIRect&) const = 0;
 
-    // If a ctx is specified, then only gpu-specific formats are requested.
-    virtual SkData* onRefEncoded(GrContext*) const { return nullptr; }
+    virtual SkData* onRefEncoded() const { return nullptr; }
 
     virtual bool onAsLegacyBitmap(SkBitmap*, LegacyBitmapMode) const;
 
+    // True for picture-backed and codec-backed
     virtual bool onIsLazyGenerated() const { return false; }
 
+    // True only for generators that operate directly on gpu (e.g. picture-generators)
+    virtual bool onCanLazyGenerateOnGPU() const { return false; }
+
     // Call when this image is part of the key to a resourcecache entry. This allows the cache
     // to know automatically those entries can be purged when this SkImage deleted.
     void notifyAddedToCache() const {
         fAddedToCache.store(true);
     }
 
-    // Transforms image into the input color space.
-    sk_sp<SkImage> makeColorSpace(sk_sp<SkColorSpace> target) const;
+    virtual bool onIsValid(GrContext*) const = 0;
 
     virtual bool onPinAsTexture(GrContext*) const { return false; }
     virtual void onUnpinAsTexture(GrContext*) const {}
 
+    virtual sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType,
+                                            SkTransferFunctionBehavior) const = 0;
 protected:
-    virtual sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const {
-        // TODO: Make this pure virtual.
-        return sk_ref_sp(const_cast<SkImage_Base*>(this));
-    }
+    SkImage_Base(int width, int height, uint32_t uniqueID);
 
 private:
     // Set true by caches when they cache content that's derived from the current pixels.
diff --git a/src/image/SkImage_Generator.cpp b/src/image/SkImage_Generator.cpp
deleted file mode 100644
index 72ef877..0000000
--- a/src/image/SkImage_Generator.cpp
+++ /dev/null
@@ -1,128 +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.
- */
-
-#include "SkImage_Base.h"
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkImageCacherator.h"
-#include "SkImagePriv.h"
-#include "SkPixelRef.h"
-
-class SkImage_Generator : public SkImage_Base {
-public:
-    SkImage_Generator(SkImageCacherator::Validator* validator)
-        : INHERITED(validator->fInfo.width(), validator->fInfo.height(), validator->fUniqueID)
-        , fCache(validator)
-    {}
-
-    virtual SkImageInfo onImageInfo() const override {
-        return fCache.info();
-    }
-    SkAlphaType onAlphaType() const override {
-        return fCache.info().alphaType();
-    }
-
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY,
-                      CachingHint) const override;
-#if SK_SUPPORT_GPU
-    sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&,
-                                            SkColorSpace*, sk_sp<SkColorSpace>*,
-                                            SkScalar scaleAdjust[2]) const override;
-#endif
-    SkImageCacherator* peekCacherator() const override { return &fCache; }
-    SkData* onRefEncoded(GrContext*) const override;
-    sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
-    bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
-    bool onIsLazyGenerated() const override { return true; }
-    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const override;
-
-private:
-    mutable SkImageCacherator fCache;
-
-    typedef SkImage_Base INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool SkImage_Generator::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
-                                     int srcX, int srcY, CachingHint chint) const {
-    SkColorSpace* dstColorSpace = dstInfo.colorSpace();
-    SkBitmap bm;
-    if (kDisallow_CachingHint == chint) {
-        SkImageCacherator::CachedFormat cacheFormat = fCache.chooseCacheFormat(dstColorSpace);
-        if (fCache.lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) {
-            return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
-        } else {
-            // Try passing the caller's buffer directly down to the generator. If this fails we
-            // may still succeed in the general case, as the generator may prefer some other
-            // config, which we could then convert via SkBitmap::readPixels.
-            if (fCache.directGeneratePixels(dstInfo, dstPixels, dstRB, srcX, srcY)) {
-                return true;
-            }
-            // else fall through
-        }
-    }
-
-    if (this->getROPixels(&bm, dstColorSpace, chint)) {
-        return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
-    }
-    return false;
-}
-
-SkData* SkImage_Generator::onRefEncoded(GrContext* ctx) const {
-    return fCache.refEncoded(ctx);
-}
-
-bool SkImage_Generator::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace,
-                                    CachingHint chint) const {
-    return fCache.lockAsBitmap(nullptr, bitmap, this, dstColorSpace, chint);
-}
-
-#if SK_SUPPORT_GPU
-sk_sp<GrTextureProxy> SkImage_Generator::asTextureProxyRef(GrContext* context,
-                                                           const GrSamplerParams& params,
-                                                           SkColorSpace* dstColorSpace,
-                                                           sk_sp<SkColorSpace>* texColorSpace,
-                                                           SkScalar scaleAdjust[2]) const {
-    return fCache.lockAsTextureProxy(context, params, dstColorSpace,
-                                     texColorSpace, this, scaleAdjust);
-}
-#endif
-
-sk_sp<SkImage> SkImage_Generator::onMakeSubset(const SkIRect& subset) const {
-    SkASSERT(fCache.info().bounds().contains(subset));
-    SkASSERT(fCache.info().bounds() != subset);
-
-    const SkIRect generatorSubset = subset.makeOffset(fCache.fOrigin.x(), fCache.fOrigin.y());
-    SkImageCacherator::Validator validator(fCache.fSharedGenerator, &generatorSubset);
-    return validator ? sk_sp<SkImage>(new SkImage_Generator(&validator)) : nullptr;
-}
-
-sk_sp<SkImage> SkImage_Generator::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
-    SkBitmap dst;
-    SkImageInfo dstInfo = fCache.info().makeColorSpace(target);
-    if (kIndex_8_SkColorType == dstInfo.colorType() ||
-        kGray_8_SkColorType == dstInfo.colorType()) {
-        dstInfo = dstInfo.makeColorType(kN32_SkColorType);
-    }
-    dst.allocPixels(dstInfo);
-
-    if (!fCache.directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0)) {
-        return nullptr;
-    }
-
-    dst.setImmutable();
-    return SkImage::MakeFromBitmap(dst);
-}
-
-sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,
-                                          const SkIRect* subset) {
-    SkImageCacherator::Validator validator(
-                       SkImageCacherator::SharedGenerator::Make(std::move(generator)), subset);
-
-    return validator ? sk_make_sp<SkImage_Generator>(&validator) : nullptr;
-}
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 1dcc69c..55ceba0 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -10,6 +10,11 @@
 #include <type_traits>
 
 #include "SkAutoPixmapStorage.h"
+#include "GrBackendSurface.h"
+#include "GrBackendTextureImageGenerator.h"
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+#include "GrAHardwareBufferImageGenerator.h"
+#endif
 #include "GrBitmapTextureMaker.h"
 #include "GrCaps.h"
 #include "GrContext.h"
@@ -22,11 +27,9 @@
 #include "GrTextureAdjuster.h"
 #include "GrTexturePriv.h"
 #include "GrTextureProxy.h"
-#include "GrTextureToYUVPlanes.h"
 #include "effects/GrNonlinearColorSpaceXformEffect.h"
 #include "effects/GrYUVEffect.h"
 #include "SkCanvas.h"
-#include "SkCrossContextImageData.h"
 #include "SkBitmapCache.h"
 #include "SkGr.h"
 #include "SkImage_Gpu.h"
@@ -62,11 +65,13 @@
     return SkImageInfo::Make(fProxy->width(), fProxy->height(), ct, fAlphaType, fColorSpace);
 }
 
-static SkImageInfo make_info(int w, int h, SkAlphaType at, sk_sp<SkColorSpace> colorSpace) {
-    return SkImageInfo::MakeN32(w, h, at, std::move(colorSpace));
-}
-
-bool SkImage_Gpu::getROPixels(SkBitmap* dst, SkColorSpace* dstColorSpace, CachingHint chint) const {
+bool SkImage_Gpu::getROPixels(SkBitmap* dst, SkColorSpace*, CachingHint chint) const {
+    // The SkColorSpace parameter "dstColorSpace" is really just a hint about how/where the bitmap
+    // will be used. The client doesn't expect that we convert to that color space, it's intended
+    // for codec-backed images, to drive our decoding heuristic. In theory we *could* read directly
+    // into that color space (to save the client some effort in whatever they're about to do), but
+    // that would make our use of the bitmap cache incorrect (or much less efficient, assuming we
+    // rolled the dstColorSpace into the key).
     const auto desc = SkBitmapCacheDesc::Make(this);
     if (SkBitmapCache::Find(desc, dst)) {
         SkASSERT(dst->getGenerationID() == this->uniqueID());
@@ -75,10 +80,17 @@
         return true;
     }
 
-    SkImageInfo ii = make_info(this->width(), this->height(), this->alphaType(),
-                               sk_ref_sp(dstColorSpace));
-    if (!dst->tryAllocPixels(ii)) {
-        return false;
+    SkBitmapCache::RecPtr rec = nullptr;
+    SkPixmap pmap;
+    if (kAllow_CachingHint == chint) {
+        rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
+        if (!rec) {
+            return false;
+        }
+    } else {
+        if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
+            return false;
+        }
     }
 
     sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
@@ -88,13 +100,12 @@
         return false;
     }
 
-    if (!sContext->readPixels(dst->info(), dst->getPixels(), dst->rowBytes(), 0, 0)) {
+    if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
         return false;
     }
 
-    dst->pixelRef()->setImmutableWithID(this->uniqueID());
-    if (kAllow_CachingHint == chint) {
-        SkBitmapCache::Add(desc, *dst);
+    if (rec) {
+        SkBitmapCache::Add(std::move(rec), dst);
         fAddedRasterVersionToCache.store(true);
     }
     return true;
@@ -142,18 +153,22 @@
 
 GrBackendObject SkImage_Gpu::onGetTextureHandle(bool flushPendingGrContextIO,
                                                 GrSurfaceOrigin* origin) const {
-    GrTextureProxy* proxy = this->peekProxy();
-    SkASSERT(proxy);
+    SkASSERT(fProxy);
 
-    GrSurface* surface = proxy->instantiate(fContext->resourceProvider());
-    if (surface && surface->asTexture()) {
+    if (!fProxy->instantiate(fContext->resourceProvider())) {
+        return 0;
+    }
+
+    GrTexture* texture = fProxy->priv().peekTexture();
+
+    if (texture) {
         if (flushPendingGrContextIO) {
-            fContext->prepareSurfaceForExternalIO(surface);
+            fContext->contextPriv().prepareSurfaceForExternalIO(fProxy.get());
         }
         if (origin) {
-            *origin = surface->origin();
+            *origin = fProxy->origin();
         }
-        return surface->asTexture()->getTextureHandle();
+        return texture->getTextureHandle();
     }
     return 0;
 }
@@ -164,16 +179,11 @@
         return nullptr;
     }
 
-    return proxy->instantiate(fContext->resourceProvider());
-}
-
-bool SkImage_Gpu::onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
-                                   const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
-    if (GrTextureToYUVPlanes(fContext, fProxy, sizes, planes, rowBytes, colorSpace)) {
-        return true;
+    if (!proxy->instantiate(fContext->resourceProvider())) {
+        return nullptr;
     }
 
-    return INHERITED::onReadYUV8Planes(sizes, planes, rowBytes, colorSpace);
+    return proxy->priv().peekTexture();
 }
 
 bool SkImage_Gpu::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
@@ -192,7 +202,7 @@
     uint32_t flags = 0;
     if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
         // let the GPU perform this transformation for us
-        flags = GrContext::kUnpremul_PixelOpsFlag;
+        flags = GrContextPriv::kUnpremul_PixelOpsFlag;
     }
 
     sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
@@ -221,9 +231,11 @@
 }
 
 sk_sp<SkImage> SkImage_Gpu::onMakeSubset(const SkIRect& subset) const {
-    GrSurfaceDesc desc = fProxy->desc();
+    GrSurfaceDesc desc;
+    desc.fConfig = fProxy->config();
     desc.fWidth = subset.width();
     desc.fHeight = subset.height();
+    desc.fOrigin = fProxy->origin();
 
     sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
                                                                         desc,
@@ -245,16 +257,23 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-static sk_sp<SkImage> new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc,
+static sk_sp<SkImage> new_wrapped_texture_common(GrContext* ctx,
+                                                 const GrBackendTexture& backendTex,
+                                                 GrSurfaceOrigin origin,
                                                  SkAlphaType at, sk_sp<SkColorSpace> colorSpace,
                                                  GrWrapOwnership ownership,
                                                  SkImage::TextureReleaseProc releaseProc,
                                                  SkImage::ReleaseContext releaseCtx) {
-    if (desc.fWidth <= 0 || desc.fHeight <= 0) {
+    if (backendTex.width() <= 0 || backendTex.height() <= 0) {
         return nullptr;
     }
 
-    sk_sp<GrTexture> tex = ctx->resourceProvider()->wrapBackendTexture(desc, ownership);
+    GrBackendTextureFlags flags = kNone_GrBackendTextureFlag;
+    sk_sp<GrTexture> tex = ctx->resourceProvider()->wrapBackendTexture(backendTex,
+                                                                       origin,
+                                                                       flags,
+                                                                       0,
+                                                                       ownership);
     if (!tex) {
         return nullptr;
     }
@@ -262,8 +281,7 @@
         tex->setRelease(releaseProc, releaseCtx);
     }
 
-    const SkBudgeted budgeted = (kAdoptAndCache_GrWrapOwnership == ownership)
-            ? SkBudgeted::kYes : SkBudgeted::kNo;
+    const SkBudgeted budgeted = SkBudgeted::kNo;
     sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex)));
     return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
                                    at, std::move(proxy), std::move(colorSpace), budgeted);
@@ -272,16 +290,52 @@
 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
                                         SkAlphaType at, sk_sp<SkColorSpace> cs,
                                         TextureReleaseProc releaseP, ReleaseContext releaseC) {
-    return new_wrapped_texture_common(ctx, desc, at, std::move(cs), kBorrow_GrWrapOwnership,
+    SkASSERT(!(kRenderTarget_GrBackendTextureFlag & desc.fFlags));
+    GrBackendTexture tex(desc, ctx->contextPriv().getBackend());
+    return new_wrapped_texture_common(ctx, tex, desc.fOrigin, at, std::move(cs),
+                                      kBorrow_GrWrapOwnership,
                                       releaseP, releaseC);
 }
 
 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
                                                SkAlphaType at, sk_sp<SkColorSpace> cs) {
-    return new_wrapped_texture_common(ctx, desc, at, std::move(cs), kAdopt_GrWrapOwnership,
+    SkASSERT(!(kRenderTarget_GrBackendTextureFlag & desc.fFlags));
+    GrBackendTexture tex(desc, ctx->contextPriv().getBackend());
+    return new_wrapped_texture_common(ctx, tex, desc.fOrigin, at, std::move(cs),
+                                      kAdopt_GrWrapOwnership,
                                       nullptr, nullptr);
 }
 
+sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
+                                        const GrBackendTexture& tex, GrSurfaceOrigin origin,
+                                        SkAlphaType at, sk_sp<SkColorSpace> cs,
+                                        TextureReleaseProc releaseP, ReleaseContext releaseC) {
+    return new_wrapped_texture_common(ctx, tex, origin, at, std::move(cs), kBorrow_GrWrapOwnership,
+                                      releaseP, releaseC);
+}
+
+sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
+                                               const GrBackendTexture& tex, GrSurfaceOrigin origin,
+                                               SkAlphaType at, sk_sp<SkColorSpace> cs) {
+    return new_wrapped_texture_common(ctx, tex, origin, at, std::move(cs), kAdopt_GrWrapOwnership,
+                                      nullptr, nullptr);
+}
+
+static GrBackendTexture make_backend_texture_from_handle(GrBackend backend,
+                                                         int width, int height,
+                                                         GrPixelConfig config,
+                                                         GrBackendObject handle) {
+
+    if (kOpenGL_GrBackend == backend) {
+        GrGLTextureInfo* glInfo = (GrGLTextureInfo*)(handle);
+        return GrBackendTexture(width, height, config, *glInfo);
+    } else {
+        SkASSERT(kVulkan_GrBackend == backend);
+        GrVkImageInfo* vkInfo = (GrVkImageInfo*)(handle);
+        return GrBackendTexture(width, height, *vkInfo);
+    }
+}
+
 static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace,
                                                   bool nv12,
                                                   const GrBackendObject yuvTextureHandles[],
@@ -300,38 +354,31 @@
 
     const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig;
 
-    GrBackendTextureDesc yDesc;
-    yDesc.fConfig = kConfig;
-    yDesc.fOrigin = origin;
-    yDesc.fSampleCnt = 0;
-    yDesc.fTextureHandle = yuvTextureHandles[0];
-    yDesc.fWidth = yuvSizes[0].fWidth;
-    yDesc.fHeight = yuvSizes[0].fHeight;
+    GrBackend backend = ctx->contextPriv().getBackend();
+    GrBackendTexture yTex = make_backend_texture_from_handle(backend,
+                                                             yuvSizes[0].fWidth,
+                                                             yuvSizes[0].fHeight,
+                                                             kConfig,
+                                                             yuvTextureHandles[0]);
+    GrBackendTexture uTex = make_backend_texture_from_handle(backend,
+                                                             yuvSizes[1].fWidth,
+                                                             yuvSizes[1].fHeight,
+                                                             kConfig,
+                                                             yuvTextureHandles[1]);
 
-    GrBackendTextureDesc uDesc;
-    uDesc.fConfig = kConfig;
-    uDesc.fOrigin = origin;
-    uDesc.fSampleCnt = 0;
-    uDesc.fTextureHandle = yuvTextureHandles[1];
-    uDesc.fWidth = yuvSizes[1].fWidth;
-    uDesc.fHeight = yuvSizes[1].fHeight;
-
-    sk_sp<GrSurfaceProxy> yProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, yDesc);
-    sk_sp<GrSurfaceProxy> uProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, uDesc);
-    sk_sp<GrSurfaceProxy> vProxy;
+    sk_sp<GrTextureProxy> yProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, yTex, origin);
+    sk_sp<GrTextureProxy> uProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, uTex, origin);
+    sk_sp<GrTextureProxy> vProxy;
 
     if (nv12) {
         vProxy = uProxy;
     } else {
-        GrBackendTextureDesc vDesc;
-        vDesc.fConfig = kConfig;
-        vDesc.fOrigin = origin;
-        vDesc.fSampleCnt = 0;
-        vDesc.fTextureHandle = yuvTextureHandles[2];
-        vDesc.fWidth = yuvSizes[2].fWidth;
-        vDesc.fHeight = yuvSizes[2].fHeight;
-
-        vProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, vDesc);
+        GrBackendTexture vTex = make_backend_texture_from_handle(backend,
+                                                                 yuvSizes[2].fWidth,
+                                                                 yuvSizes[2].fHeight,
+                                                                 kConfig,
+                                                                 yuvTextureHandles[2]);
+        vProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, vTex, origin);
     }
     if (!yProxy || !uProxy || !vProxy) {
         return nullptr;
@@ -341,7 +388,7 @@
     const int height = yuvSizes[0].fHeight;
 
     // Needs to be a render target in order to draw to it for the yuv->rgb conversion.
-    sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext(
                                                                          SkBackingFit::kExact,
                                                                          width, height,
                                                                          kRGBA_8888_GrPixelConfig,
@@ -356,18 +403,17 @@
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     paint.addColorFragmentProcessor(
         GrYUVEffect::MakeYUVToRGB(ctx->resourceProvider(),
-                                  sk_ref_sp(yProxy->asTextureProxy()),
-                                  sk_ref_sp(uProxy->asTextureProxy()),
-                                  sk_ref_sp(vProxy->asTextureProxy()), yuvSizes, colorSpace, nv12));
+                                  yProxy, uProxy, vProxy,
+                                  yuvSizes, colorSpace, nv12));
 
     const SkRect rect = SkRect::MakeIWH(width, height);
 
     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
 
-    if (!renderTargetContext->accessRenderTarget()) {
+    if (!renderTargetContext->asSurfaceProxy()) {
         return nullptr;
     }
-    ctx->flushSurfaceWrites(renderTargetContext->accessRenderTarget());
+    ctx->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy());
 
     // MDB: this call is okay bc we know 'renderTargetContext' was exact
     return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
@@ -414,8 +460,8 @@
         return peek->getContext() == context ? sk_ref_sp(const_cast<SkImage*>(this)) : nullptr;
     }
 
-    if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
-        GrImageTextureMaker maker(context, cacher, this, kDisallow_CachingHint);
+    if (this->isLazyGenerated()) {
+        GrImageTextureMaker maker(context, this, kDisallow_CachingHint);
         return create_image_from_maker(context, &maker, this->alphaType(),
                                        this->uniqueID(), dstColorSpace);
     }
@@ -428,64 +474,53 @@
     return nullptr;
 }
 
-std::unique_ptr<SkCrossContextImageData> SkCrossContextImageData::MakeFromEncoded(
-        GrContext* context, sk_sp<SkData> encoded, SkColorSpace* dstColorSpace) {
+sk_sp<SkImage> SkImage::MakeCrossContextFromEncoded(GrContext* context, sk_sp<SkData> encoded,
+                                                    bool buildMips, SkColorSpace* dstColorSpace) {
     sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(std::move(encoded));
     if (!codecImage) {
         return nullptr;
     }
 
     // Some backends or drivers don't support (safely) moving resources between contexts
-    if (!context->caps()->crossContextTextureSupport()) {
-        return std::unique_ptr<SkCrossContextImageData>(
-            new SkCrossContextImageData(std::move(codecImage)));
+    if (!context || !context->caps()->crossContextTextureSupport()) {
+        return codecImage;
     }
 
-    sk_sp<SkImage> textureImage = codecImage->makeTextureImage(context, dstColorSpace);
-    if (!textureImage) {
-        // TODO: Force decode to raster here? Do mip-mapping, like getDeferredTextureImageData?
-        return std::unique_ptr<SkCrossContextImageData>(
-            new SkCrossContextImageData(std::move(codecImage)));
+    // Turn the codec image into a GrTextureProxy
+    GrImageTextureMaker maker(context, codecImage.get(), kDisallow_CachingHint);
+    sk_sp<SkColorSpace> texColorSpace;
+    GrSamplerParams params(SkShader::kClamp_TileMode,
+                           buildMips ? GrSamplerParams::kMipMap_FilterMode
+                                     : GrSamplerParams::kBilerp_FilterMode);
+    sk_sp<GrTextureProxy> proxy(maker.refTextureProxyForParams(params, dstColorSpace,
+                                                               &texColorSpace, nullptr));
+    if (!proxy) {
+        return codecImage;
     }
 
-    // Crack open the gpu image, extract the backend data, stick it in the SkCCID
-    GrTexture* texture = as_IB(textureImage)->peekTexture();
-    SkASSERT(texture);
+    if (!proxy->instantiate(context->resourceProvider())) {
+        return codecImage;
+    }
+    sk_sp<GrTexture> texture = sk_ref_sp(proxy->priv().peekTexture());
 
-    GrBackendTextureDesc desc;
-    desc.fFlags = kNone_GrBackendTextureFlag;
-    desc.fOrigin = texture->origin();
-    desc.fWidth = texture->width();
-    desc.fHeight = texture->height();
-    desc.fConfig = texture->config();
-    desc.fSampleCnt = 0;
+    // Flush any writes or uploads
+    context->contextPriv().prepareSurfaceForExternalIO(proxy.get());
 
-    auto textureData = texture->texturePriv().detachBackendTexture();
-    SkASSERT(textureData);
+    sk_sp<GrSemaphore> sema = context->getGpu()->prepareTextureForCrossContextUsage(texture.get());
 
-    SkImageInfo info = as_IB(textureImage)->onImageInfo();
-    return std::unique_ptr<SkCrossContextImageData>(new SkCrossContextImageData(
-        desc, std::move(textureData), info.alphaType(), info.refColorSpace()));
+    auto gen = GrBackendTextureImageGenerator::Make(std::move(texture), std::move(sema),
+                                                    codecImage->alphaType(),
+                                                    std::move(texColorSpace));
+    return SkImage::MakeFromGenerator(std::move(gen));
 }
 
-sk_sp<SkImage> SkImage::MakeFromCrossContextImageData(
-        GrContext* context, std::unique_ptr<SkCrossContextImageData> ccid) {
-    if (ccid->fImage) {
-        // No pre-existing GPU resource. We could upload it now (with makeTextureImage),
-        // but we'd need a dstColorSpace.
-        return ccid->fImage;
-    }
-
-    if (ccid->fTextureData) {
-        ccid->fTextureData->attachToContext(context);
-    }
-
-    // This texture was created by Ganesh on another thread (see MakeFromEncoded, above).
-    // Thus, we can import it back into our cache and treat it as our own (again).
-    GrWrapOwnership ownership = kAdoptAndCache_GrWrapOwnership;
-    return new_wrapped_texture_common(context, ccid->fDesc, ccid->fAlphaType,
-                                      std::move(ccid->fColorSpace), ownership, nullptr, nullptr);
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+sk_sp<SkImage> SkImage::MakeFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at,
+                                               sk_sp<SkColorSpace> cs) {
+    auto gen = GrAHardwareBufferImageGenerator::Make(graphicBuffer, at, cs);
+    return SkImage::MakeFromGenerator(std::move(gen));
 }
+#endif
 
 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
     if (!this->isTextureBacked()) {
@@ -566,6 +601,18 @@
                                             const DeferredTextureImageUsageParams params[],
                                             int paramCnt, void* buffer,
                                             SkColorSpace* dstColorSpace) const {
+    // Some quick-rejects where is makes no sense to return CPU data
+    //  e.g.
+    //      - texture backed
+    //      - picture backed
+    //
+    if (this->isTextureBacked()) {
+        return 0;
+    }
+    if (as_IB(this)->onCanLazyGenerateOnGPU()) {
+        return 0;
+    }
+
     // Extract relevant min/max values from the params array.
     int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
     SkFilterQuality highestFilterQuality = params[0].fQuality;
@@ -613,12 +660,12 @@
     if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable()) {
         info = pixmap.info();
         pixelSize = SkAlign8(pixmap.getSafeSize());
+        if (!dstColorSpace) {
+            pixmap.setColorSpace(nullptr);
+            info = info.makeColorSpace(nullptr);
+        }
     } else {
-        // Here we're just using presence of data to know whether there is a codec behind the image.
-        // In the future we will access the cacherator and get the exact data that we want to (e.g.
-        // yuv planes) upload.
-        sk_sp<SkData> data(this->refEncoded());
-        if (!data && !this->peekPixels(nullptr)) {
+        if (!this->isLazyGenerated() && !this->peekPixels(nullptr)) {
             return 0;
         }
         if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
@@ -629,6 +676,9 @@
                                                               scaledSize.height());
         } else {
             info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
+            if (!dstColorSpace) {
+                info = info.makeColorSpace(nullptr);
+            }
         }
         if (kIndex_8_SkColorType == info.colorType()) {
             // Force Index8 to be N32 instead. Index8 is unsupported in Ganesh.
@@ -682,10 +732,19 @@
     size += pixelSize;
     size_t colorSpaceOffset = 0;
     size_t colorSpaceSize = 0;
+    SkColorSpaceTransferFn fn;
     if (info.colorSpace()) {
+        SkASSERT(dstColorSpace);
         colorSpaceOffset = size;
         colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
         size += colorSpaceSize;
+    } else if (this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
+        // In legacy mode, preserve the color space tag on the SkImage.  This is only
+        // supported if the color space has a parametric transfer function.
+        SkASSERT(!dstColorSpace);
+        colorSpaceOffset = size;
+        colorSpaceSize = this->colorSpace()->writeToMemory(nullptr);
+        size += colorSpaceSize;
     }
     if (!fillMode) {
         return size;
@@ -730,7 +789,13 @@
         void* colorSpace = bufferAsCharPtr + colorSpaceOffset;
         FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace);
         FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize);
-        info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
+        if (info.colorSpace()) {
+            info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
+        } else {
+            SkASSERT(this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn));
+            SkASSERT(!dstColorSpace);
+            this->colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
+        }
     } else {
         memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace),
                0, sizeof(DeferredTextureImage::fColorSpace));
@@ -803,8 +868,12 @@
     if (mipLevelCount == 1) {
         SkPixmap pixmap;
         pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, dti->fMipMapLevelData[0].fRowBytes);
-        sk_sp<GrTextureProxy> proxy(GrUploadPixmapToTextureProxy(context->resourceProvider(),
-                                                                 pixmap, budgeted));
+
+        // Pass nullptr for the |dstColorSpace|.  This opts in to more lenient color space
+        // verification.  This is ok because we've already verified the color space in
+        // getDeferredTextureImageData().
+        sk_sp<GrTextureProxy> proxy(GrUploadPixmapToTextureProxy(
+                context->resourceProvider(), pixmap, budgeted, nullptr));
         if (!proxy) {
             return nullptr;
         }
@@ -829,6 +898,7 @@
                                               const GrMipLevel* texels, int mipLevelCount,
                                               SkBudgeted budgeted,
                                               SkDestinationSurfaceColorMode colorMode) {
+    SkASSERT(mipLevelCount >= 1);
     if (!ctx) {
         return nullptr;
     }
@@ -844,14 +914,28 @@
                                    info.refColorSpace(), budgeted);
 }
 
-sk_sp<SkImage> SkImage_Gpu::onMakeColorSpace(sk_sp<SkColorSpace> colorSpace) const {
-    sk_sp<SkColorSpace> srcSpace = fColorSpace ? fColorSpace : SkColorSpace::MakeSRGB();
-    auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), colorSpace.get());
+sk_sp<SkImage> SkImage_Gpu::onMakeColorSpace(sk_sp<SkColorSpace> target, SkColorType,
+                                             SkTransferFunctionBehavior premulBehavior) const {
+    if (SkTransferFunctionBehavior::kRespect == premulBehavior) {
+        // TODO: Implement this.
+        return nullptr;
+    }
+
+    sk_sp<SkColorSpace> srcSpace = fColorSpace;
+    if (!fColorSpace) {
+        if (target->isSRGB()) {
+            return sk_ref_sp(const_cast<SkImage*>((SkImage*)this));
+        }
+
+        srcSpace = SkColorSpace::MakeSRGB();
+    }
+
+    auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), target.get());
     if (!xform) {
         return sk_ref_sp(const_cast<SkImage_Gpu*>(this));
     }
 
-    sk_sp<GrRenderTargetContext> renderTargetContext(fContext->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(fContext->makeDeferredRenderTargetContext(
         SkBackingFit::kExact, this->width(), this->height(), kRGBA_8888_GrPixelConfig, nullptr));
     if (!renderTargetContext) {
         return nullptr;
@@ -866,13 +950,26 @@
 
     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
 
-    if (!renderTargetContext->accessRenderTarget()) {
+    if (!renderTargetContext->asTextureProxy()) {
         return nullptr;
     }
 
     // MDB: this call is okay bc we know 'renderTargetContext' was exact
     return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
                                    fAlphaType, renderTargetContext->asTextureProxyRef(),
-                                   std::move(colorSpace), fBudgeted);
+                                   std::move(target), fBudgeted);
 
 }
+
+bool SkImage_Gpu::onIsValid(GrContext* context) const {
+    // The base class has already checked that context isn't abandoned (if it's not nullptr)
+    if (fContext->abandoned()) {
+        return false;
+    }
+
+    if (context && context != fContext) {
+        return false;
+    }
+
+    return true;
+}
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 8c3df39..fbe29d8 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -36,7 +36,11 @@
         return fProxy.get();
     }
     GrTexture* peekTexture() const override {
-        return fProxy->instantiate(fContext->resourceProvider());
+        if (!fProxy->instantiate(fContext->resourceProvider())) {
+            return nullptr;
+        }
+
+        return fProxy->priv().peekTexture();
     }
     sk_sp<GrTextureProxy> asTextureProxyRef() const override {
         return fProxy;
@@ -53,16 +57,16 @@
                                        GrSurfaceOrigin* origin) const override;
     GrTexture* onGetTexture() const override;
 
-    bool onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
-                          const size_t rowBytes[3], SkYUVColorSpace colorSpace) const override;
-
     bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
                       int srcX, int srcY, CachingHint) const override;
 
     GrContext* context() { return fContext; }
     sk_sp<SkColorSpace> refColorSpace() { return fColorSpace; }
 
-    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const override;
+    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType,
+                                    SkTransferFunctionBehavior) const override;
+
+    bool onIsValid(GrContext*) const override;
 
 private:
     GrContext*             fContext;
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
new file mode 100644
index 0000000..980db1d
--- /dev/null
+++ b/src/image/SkImage_Lazy.cpp
@@ -0,0 +1,774 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkImage_Base.h"
+#include "SkImageCacherator.h"
+
+#include "SkBitmap.h"
+#include "SkBitmapCache.h"
+#include "SkColorSpace_Base.h"
+#include "SkData.h"
+#include "SkImageGenerator.h"
+#include "SkImagePriv.h"
+#include "SkNextID.h"
+#include "SkPixelRef.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrGpuResourcePriv.h"
+#include "GrImageTextureMaker.h"
+#include "GrResourceKey.h"
+#include "GrResourceProvider.h"
+#include "GrSamplerParams.h"
+#include "GrYUVProvider.h"
+#include "SkGr.h"
+#endif
+
+// Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing one generator among N images
+class SharedGenerator final : public SkNVRefCnt<SharedGenerator> {
+public:
+    static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) {
+        return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr;
+    }
+
+    // This is thread safe.  It is a const field set in the constructor.
+    const SkImageInfo& getInfo() { return fGenerator->getInfo(); }
+
+private:
+    explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen)
+            : fGenerator(std::move(gen)) {
+        SkASSERT(fGenerator);
+    }
+
+    friend class ScopedGenerator;
+    friend class SkImage_Lazy;
+
+    std::unique_ptr<SkImageGenerator> fGenerator;
+    SkMutex                           fMutex;
+};
+
+class SkImage_Lazy : public SkImage_Base, public SkImageCacherator {
+public:
+    struct Validator {
+        Validator(sk_sp<SharedGenerator>, const SkIRect* subset);
+
+        operator bool() const { return fSharedGenerator.get(); }
+
+        sk_sp<SharedGenerator> fSharedGenerator;
+        SkImageInfo            fInfo;
+        SkIPoint               fOrigin;
+        uint32_t               fUniqueID;
+    };
+
+    SkImage_Lazy(Validator* validator);
+
+    SkImageInfo onImageInfo() const override {
+        return fInfo;
+    }
+    SkAlphaType onAlphaType() const override {
+        return fInfo.alphaType();
+    }
+
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY,
+                      CachingHint) const override;
+#if SK_SUPPORT_GPU
+    sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&,
+                                            SkColorSpace*, sk_sp<SkColorSpace>*,
+                                            SkScalar scaleAdjust[2]) const override;
+#endif
+    SkData* onRefEncoded() const override;
+    sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
+    bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
+    bool onIsLazyGenerated() const override { return true; }
+    bool onCanLazyGenerateOnGPU() const override;
+    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType,
+                                    SkTransferFunctionBehavior) const override;
+
+    bool onIsValid(GrContext*) const override;
+
+    SkImageCacherator* peekCacherator() const override {
+        return const_cast<SkImage_Lazy*>(this);
+    }
+
+    // Only return true if the generate has already been cached.
+    bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat) const;
+    // Call the underlying generator directly
+    bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
+                              int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
+
+    // SkImageCacherator interface
+#if SK_SUPPORT_GPU
+    // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it,
+    // it should use the passed in key (if the key is valid).
+    sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
+                                           const GrUniqueKey& key,
+                                           SkImage::CachingHint,
+                                           bool willBeMipped,
+                                           SkColorSpace* dstColorSpace) override;
+
+    // Returns the color space of the texture that would be returned if you called lockTexture.
+    // Separate code path to allow querying of the color space for textures that cached (even
+    // externally).
+    sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace) override;
+    void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat,
+                                 GrUniqueKey* cacheKey) override;
+#endif
+
+    CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace,
+                                   const GrCaps* = nullptr) const override;
+    SkImageInfo buildCacheInfo(CachedFormat) const override;
+
+private:
+    class ScopedGenerator;
+
+    /**
+     *  On success (true), bitmap will point to the pixels for this generator. If this returns
+     *  false, the bitmap will be reset to empty.
+     */
+    bool lockAsBitmap(SkBitmap*, SkImage::CachingHint, CachedFormat, const SkImageInfo&) const;
+
+    sk_sp<SharedGenerator> fSharedGenerator;
+    const SkImageInfo      fInfo;
+    const SkIPoint         fOrigin;
+
+    struct IDRec {
+        SkOnce      fOnce;
+        uint32_t    fUniqueID;
+    };
+    mutable IDRec fIDRecs[kNumCachedFormats];
+
+    uint32_t getUniqueID(CachedFormat) const;
+
+    typedef SkImage_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
+        : fSharedGenerator(std::move(gen)) {
+
+    if (!fSharedGenerator) {
+        return;
+    }
+
+    // The following generator accessors are safe without acquiring the mutex (const getters).
+    // TODO: refactor to use a ScopedGenerator instead, for clarity.
+    const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
+    if (info.isEmpty()) {
+        fSharedGenerator.reset();
+        return;
+    }
+
+    fUniqueID = fSharedGenerator->fGenerator->uniqueID();
+    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
+    if (subset) {
+        if (!bounds.contains(*subset)) {
+            fSharedGenerator.reset();
+            return;
+        }
+        if (*subset != bounds) {
+            // we need a different uniqueID since we really are a subset of the raw generator
+            fUniqueID = SkNextID::ImageID();
+        }
+    } else {
+        subset = &bounds;
+    }
+
+    fInfo   = info.makeWH(subset->width(), subset->height());
+    fOrigin = SkIPoint::Make(subset->x(), subset->y());
+
+    // colortables are poorly to not-at-all supported in our resourcecache, so we
+    // bully them into N32 (the generator will perform the up-sample)
+    if (fInfo.colorType() == kIndex_8_SkColorType) {
+        fInfo = fInfo.makeColorType(kN32_SkColorType);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Helper for exclusive access to a shared generator.
+class SkImage_Lazy::ScopedGenerator {
+public:
+    ScopedGenerator(const sk_sp<SharedGenerator>& gen)
+      : fSharedGenerator(gen)
+      , fAutoAquire(gen->fMutex) {}
+
+    SkImageGenerator* operator->() const {
+        fSharedGenerator->fMutex.assertHeld();
+        return fSharedGenerator->fGenerator.get();
+    }
+
+    operator SkImageGenerator*() const {
+        fSharedGenerator->fMutex.assertHeld();
+        return fSharedGenerator->fGenerator.get();
+    }
+
+private:
+    const sk_sp<SharedGenerator>& fSharedGenerator;
+    SkAutoExclusive               fAutoAquire;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Lazy::SkImage_Lazy(Validator* validator)
+        : INHERITED(validator->fInfo.width(), validator->fInfo.height(), validator->fUniqueID)
+        , fSharedGenerator(std::move(validator->fSharedGenerator))
+        , fInfo(validator->fInfo)
+        , fOrigin(validator->fOrigin) {
+    SkASSERT(fSharedGenerator);
+    SkASSERT(kIndex_8_SkColorType != fInfo.colorType());
+    // We explicit set the legacy format slot, but leave the others uninitialized (via SkOnce)
+    // and only resolove them to IDs as needed (by calling getUniqueID()).
+    fIDRecs[kLegacy_CachedFormat].fOnce([this, validator] {
+        fIDRecs[kLegacy_CachedFormat].fUniqueID = validator->fUniqueID;
+    });
+}
+
+uint32_t SkImage_Lazy::getUniqueID(CachedFormat format) const {
+    IDRec* rec = &fIDRecs[format];
+    rec->fOnce([rec] {
+        rec->fUniqueID = SkNextID::ImageID();
+    });
+    return rec->fUniqueID;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
+// we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
+// chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
+// won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
+// works, so we require that the formats we choose are renderable (as a proxy for being readable).
+struct CacheCaps {
+    CacheCaps(const GrCaps* caps) : fCaps(caps) {}
+
+#if SK_SUPPORT_GPU
+    bool supportsHalfFloat() const {
+        return !fCaps ||
+            (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
+             fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
+    }
+
+    bool supportsSRGB() const {
+        return !fCaps ||
+            (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
+    }
+
+    bool supportsSBGR() const {
+        return !fCaps || fCaps->srgbSupport();
+    }
+#else
+    bool supportsHalfFloat() const { return true; }
+    bool supportsSRGB() const { return true; }
+    bool supportsSBGR() const { return true; }
+#endif
+
+    const GrCaps* fCaps;
+};
+
+SkImageCacherator::CachedFormat SkImage_Lazy::chooseCacheFormat(SkColorSpace* dstColorSpace,
+                                                                const GrCaps* grCaps) const {
+    SkColorSpace* cs = fInfo.colorSpace();
+    if (!cs || !dstColorSpace) {
+        return kLegacy_CachedFormat;
+    }
+
+    CacheCaps caps(grCaps);
+    switch (fInfo.colorType()) {
+        case kUnknown_SkColorType:
+        case kAlpha_8_SkColorType:
+        case kRGB_565_SkColorType:
+        case kARGB_4444_SkColorType:
+            // We don't support color space on these formats, so always decode in legacy mode:
+            // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
+            return kLegacy_CachedFormat;
+
+        case kIndex_8_SkColorType:
+            SkDEBUGFAIL("Index_8 should have been remapped at construction time.");
+            return kLegacy_CachedFormat;
+
+        case kGray_8_SkColorType:
+            // TODO: What do we do with grayscale sources that have strange color spaces attached?
+            // The codecs and color space xform don't handle this correctly (yet), so drop it on
+            // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
+            // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
+            // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
+            if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
+                return kSRGB8888_CachedFormat;
+            } else {
+                return kLegacy_CachedFormat;
+            }
+
+        case kRGBA_8888_SkColorType:
+            if (cs->gammaCloseToSRGB()) {
+                if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else if (caps.supportsHalfFloat()) {
+                    return kLinearF16_CachedFormat;
+                } else {
+                    return kLegacy_CachedFormat;
+                }
+            } else {
+                if (caps.supportsHalfFloat()) {
+                    return kLinearF16_CachedFormat;
+                } else if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else {
+                    return kLegacy_CachedFormat;
+                }
+            }
+
+        case kBGRA_8888_SkColorType:
+            // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
+            if (caps.supportsSBGR()) {
+                if (cs->gammaCloseToSRGB()) {
+                    return kSBGR8888_CachedFormat;
+                } else if (caps.supportsHalfFloat()) {
+                    return kLinearF16_CachedFormat;
+                } else if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else {
+                    // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
+                    return kLegacy_CachedFormat;
+                }
+            } else {
+                if (cs->gammaCloseToSRGB()) {
+                    if (caps.supportsSRGB()) {
+                        return kSRGB8888_CachedFormat;
+                    } else if (caps.supportsHalfFloat()) {
+                        return kLinearF16_CachedFormat;
+                    } else {
+                        return kLegacy_CachedFormat;
+                    }
+                } else {
+                    if (caps.supportsHalfFloat()) {
+                        return kLinearF16_CachedFormat;
+                    } else if (caps.supportsSRGB()) {
+                        return kSRGB8888_CachedFormat;
+                    } else {
+                        return kLegacy_CachedFormat;
+                    }
+                }
+            }
+
+        case kRGBA_F16_SkColorType:
+            if (caps.supportsHalfFloat()) {
+                return kLinearF16_CachedFormat;
+            } else if (caps.supportsSRGB()) {
+                return kSRGB8888_CachedFormat;
+            } else {
+                return kLegacy_CachedFormat;
+            }
+    }
+    SkDEBUGFAIL("Unreachable");
+    return kLegacy_CachedFormat;
+}
+
+SkImageInfo SkImage_Lazy::buildCacheInfo(CachedFormat format) const {
+    switch (format) {
+        case kLegacy_CachedFormat:
+            return fInfo.makeColorSpace(nullptr);
+        case kLinearF16_CachedFormat:
+            return fInfo.makeColorType(kRGBA_F16_SkColorType)
+                        .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
+        case kSRGB8888_CachedFormat:
+            // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec
+            // to bother trans-coding. It would be slow, and do more harm than good visually,
+            // so we make sure to leave the colorspace as-is.
+            if (fInfo.colorSpace()->gammaCloseToSRGB()) {
+                return fInfo.makeColorType(kRGBA_8888_SkColorType);
+            } else {
+                return fInfo.makeColorType(kRGBA_8888_SkColorType)
+                            .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
+            }
+        case kSBGR8888_CachedFormat:
+            // See note above about not-quite-sRGB transfer functions.
+            if (fInfo.colorSpace()->gammaCloseToSRGB()) {
+                return fInfo.makeColorType(kBGRA_8888_SkColorType);
+            } else {
+                return fInfo.makeColorType(kBGRA_8888_SkColorType)
+                            .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
+            }
+        default:
+            SkDEBUGFAIL("Invalid cached format");
+            return fInfo;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
+    SkASSERT(bitmap.getGenerationID() == expectedID);
+    SkASSERT(bitmap.isImmutable());
+    SkASSERT(bitmap.getPixels());
+    return true;
+}
+
+bool SkImage_Lazy::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
+                                        int srcX, int srcY,
+                                        SkTransferFunctionBehavior behavior) const {
+    ScopedGenerator generator(fSharedGenerator);
+    const SkImageInfo& genInfo = generator->getInfo();
+    // Currently generators do not natively handle subsets, so check that first.
+    if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
+        return false;
+    }
+
+    SkImageGenerator::Options opts;
+    opts.fBehavior = behavior;
+    return generator->getPixels(info, pixels, rb, &opts);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImage_Lazy::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) const {
+    uint32_t uniqueID = this->getUniqueID(format);
+    return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID,
+                                                       fInfo.width(), fInfo.height()), bitmap) &&
+           check_output_bitmap(*bitmap, uniqueID);
+}
+
+static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY) {
+    const int genW = gen->getInfo().width();
+    const int genH = gen->getInfo().height();
+    const SkIRect srcR = SkIRect::MakeWH(genW, genH);
+    const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height());
+    if (!srcR.contains(dstR)) {
+        return false;
+    }
+
+    // If they are requesting a subset, we have to have a temp allocation for full image, and
+    // then copy the subset into their allocation
+    SkBitmap full;
+    SkPixmap fullPM;
+    const SkPixmap* dstPM = &pmap;
+    if (srcR != dstR) {
+        if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) {
+            return false;
+        }
+        if (!full.peekPixels(&fullPM)) {
+            return false;
+        }
+        dstPM = &fullPM;
+    }
+
+    if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes())) {
+        return false;
+    }
+
+    if (srcR != dstR) {
+        if (!full.readPixels(pmap, originX, originY)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint,
+                                CachedFormat format, const SkImageInfo& info) const {
+    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
+        return true;
+    }
+
+    uint32_t uniqueID = this->getUniqueID(format);
+
+    SkBitmap tmpBitmap;
+    SkBitmapCache::RecPtr cacheRec;
+    SkPixmap pmap;
+    if (SkImage::kAllow_CachingHint == chint) {
+        auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height());
+        cacheRec = SkBitmapCache::Alloc(desc, info, &pmap);
+        if (!cacheRec) {
+            return false;
+        }
+    } else {
+        if (!tmpBitmap.tryAllocPixels(info)) {
+            return false;
+        }
+        if (!tmpBitmap.peekPixels(&pmap)) {
+            return false;
+        }
+    }
+
+    ScopedGenerator generator(fSharedGenerator);
+    if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y())) {
+        return false;
+    }
+
+    if (cacheRec) {
+        SkBitmapCache::Add(std::move(cacheRec), bitmap);
+        SkASSERT(bitmap->getPixels());  // we're locked
+        SkASSERT(bitmap->isImmutable());
+        SkASSERT(bitmap->getGenerationID() == uniqueID);
+        this->notifyAddedToCache();
+    } else {
+        *bitmap = tmpBitmap;
+        bitmap->pixelRef()->setImmutableWithID(uniqueID);
+    }
+
+    check_output_bitmap(*bitmap, uniqueID);
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImage_Lazy::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
+                                int srcX, int srcY, CachingHint chint) const {
+    SkColorSpace* dstColorSpace = dstInfo.colorSpace();
+    SkBitmap bm;
+    if (kDisallow_CachingHint == chint) {
+        CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
+        if (this->lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) {
+            return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
+        } else {
+            // Try passing the caller's buffer directly down to the generator. If this fails we
+            // may still succeed in the general case, as the generator may prefer some other
+            // config, which we could then convert via SkBitmap::readPixels.
+            if (this->directGeneratePixels(dstInfo, dstPixels, dstRB, srcX, srcY,
+                                           SkTransferFunctionBehavior::kRespect)) {
+                return true;
+            }
+            // else fall through
+        }
+    }
+
+    if (this->getROPixels(&bm, dstColorSpace, chint)) {
+        return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
+    }
+    return false;
+}
+
+SkData* SkImage_Lazy::onRefEncoded() const {
+    ScopedGenerator generator(fSharedGenerator);
+    return generator->refEncodedData();
+}
+
+bool SkImage_Lazy::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace,
+                               CachingHint chint) const {
+    CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
+    SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat);
+    return this->lockAsBitmap(bitmap, chint, cacheFormat, cacheInfo);
+}
+
+bool SkImage_Lazy::onIsValid(GrContext* context) const {
+    ScopedGenerator generator(fSharedGenerator);
+    return generator->isValid(context);
+}
+
+bool SkImage_Lazy::onCanLazyGenerateOnGPU() const {
+#if SK_SUPPORT_GPU
+    ScopedGenerator generator(fSharedGenerator);
+    return generator->onCanGenerateTexture();
+#else
+    return false;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+sk_sp<GrTextureProxy> SkImage_Lazy::asTextureProxyRef(GrContext* context,
+                                                      const GrSamplerParams& params,
+                                                      SkColorSpace* dstColorSpace,
+                                                      sk_sp<SkColorSpace>* texColorSpace,
+                                                      SkScalar scaleAdjust[2]) const {
+    if (!context) {
+        return nullptr;
+    }
+
+    GrImageTextureMaker textureMaker(context, this, kAllow_CachingHint);
+    return textureMaker.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
+}
+#endif
+
+sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const {
+    SkASSERT(fInfo.bounds().contains(subset));
+    SkASSERT(fInfo.bounds() != subset);
+
+    const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y());
+    Validator validator(fSharedGenerator, &generatorSubset);
+    return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
+}
+
+sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target,
+                                              SkColorType targetColorType,
+                                              SkTransferFunctionBehavior premulBehavior) const {
+    SkBitmap dst;
+    const SkImageInfo& genInfo = fSharedGenerator->getInfo();
+    SkImageInfo dstInfo = genInfo.makeColorType(targetColorType).makeColorSpace(target);
+    dst.allocPixels(dstInfo);
+    if (!this->directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0,
+                                    premulBehavior)) {
+        return nullptr;
+    }
+
+    dst.setImmutable();
+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(dst);
+
+    if (genInfo.dimensions() != fInfo.dimensions()) {
+        // This image must be a subset.
+        image = image->makeSubset(SkIRect::MakeXYWH(fOrigin.fX, fOrigin.fY,
+                                                    fInfo.width(), fInfo.height()));
+    }
+
+    return image;
+}
+
+sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,
+                                          const SkIRect* subset) {
+    SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset);
+
+    return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Implementation of SkImageCacherator interface, as needed by GrImageTextureMaker
+ */
+
+#if SK_SUPPORT_GPU
+
+void SkImage_Lazy::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
+                                           GrUniqueKey* cacheKey) {
+    SkASSERT(!cacheKey->isValid());
+    if (origKey.isValid()) {
+        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+        GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
+        builder[0] = format;
+    }
+}
+
+class Generator_GrYUVProvider : public GrYUVProvider {
+    SkImageGenerator* fGen;
+
+public:
+    Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
+
+    uint32_t onGetID() override { return fGen->uniqueID(); }
+    bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
+        return fGen->queryYUV8(sizeInfo, colorSpace);
+    }
+    bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
+        return fGen->getYUV8Planes(sizeInfo, planes);
+    }
+};
+
+static void set_key_on_proxy(GrResourceProvider* resourceProvider,
+                             GrTextureProxy* proxy, const GrUniqueKey& key) {
+    if (key.isValid()) {
+        resourceProvider->assignUniqueKeyToProxy(key, proxy);
+    }
+}
+
+sk_sp<SkColorSpace> SkImage_Lazy::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
+    // TODO: This isn't always correct. Picture generator currently produces textures in N32,
+    // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
+    // information in/on the key so we can return the correct space in case #1 of lockTexture.
+    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
+    SkImageInfo cacheInfo = this->buildCacheInfo(format);
+    return sk_ref_sp(cacheInfo.colorSpace());
+}
+
+/*
+ *  We have 4 ways to try to return a texture (in sorted order)
+ *
+ *  1. Check the cache for a pre-existing one
+ *  2. Ask the generator to natively create one
+ *  3. Ask the generator to return YUV planes, which the GPU can convert
+ *  4. Ask the generator to return RGB(A) data, which the GPU can convert
+ */
+sk_sp<GrTextureProxy> SkImage_Lazy::lockTextureProxy(GrContext* ctx,
+                                                     const GrUniqueKey& origKey,
+                                                     SkImage::CachingHint chint,
+                                                     bool willBeMipped,
+                                                     SkColorSpace* dstColorSpace) {
+    // Values representing the various texture lock paths we can take. Used for logging the path
+    // taken to a histogram.
+    enum LockTexturePath {
+        kFailure_LockTexturePath,
+        kPreExisting_LockTexturePath,
+        kNative_LockTexturePath,
+        kCompressed_LockTexturePath, // Deprecated
+        kYUV_LockTexturePath,
+        kRGBA_LockTexturePath,
+    };
+
+    enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
+
+    // Determine which cached format we're going to use (which may involve decoding to a different
+    // info than the generator provides).
+    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
+
+    // Fold the cache format into our texture key
+    GrUniqueKey key;
+    this->makeCacheKeyFromOrigKey(origKey, format, &key);
+
+    // 1. Check the cache for a pre-existing one
+    if (key.isValid()) {
+        if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
+                                     kLockTexturePathCount);
+            return proxy;
+        }
+    }
+
+    // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
+    // decoded variant of the encoded data, and also a recipe for how to transform the original
+    // info to get the one that we're going to decode to.
+    SkImageInfo cacheInfo = this->buildCacheInfo(format);
+
+    // 2. Ask the generator to natively create one
+    {
+        ScopedGenerator generator(fSharedGenerator);
+        if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
+                                     kLockTexturePathCount);
+            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
+            return proxy;
+        }
+    }
+
+    // 3. Ask the generator to return YUV planes, which the GPU can convert
+    if (!ctx->contextPriv().disableGpuYUVConversion()) {
+        const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
+        ScopedGenerator generator(fSharedGenerator);
+        Generator_GrYUVProvider provider(generator);
+        if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
+                                     kLockTexturePathCount);
+            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
+            return proxy;
+        }
+    }
+
+    // 4. Ask the generator to return RGB(A) data, which the GPU can convert
+    SkBitmap bitmap;
+    if (this->lockAsBitmap(&bitmap, chint, format, cacheInfo)) {
+        sk_sp<GrTextureProxy> proxy;
+        if (willBeMipped) {
+            proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
+        }
+        if (!proxy) {
+            proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap, dstColorSpace);
+        }
+        if (proxy) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
+                                     kLockTexturePathCount);
+            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
+            return proxy;
+        }
+    }
+    SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
+                             kLockTexturePathCount);
+    return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index e6905aa..f4f1211 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -28,7 +28,7 @@
 // fixes https://bug.skia.org/5096
 static bool is_not_subset(const SkBitmap& bm) {
     SkASSERT(bm.pixelRef());
-    SkISize dim = bm.pixelRef()->info().dimensions();
+    SkISize dim = SkISize::Make(bm.pixelRef()->width(), bm.pixelRef()->height());
     SkASSERT(dim != bm.dimensions() || bm.pixelRefOrigin().isZero());
     return dim == bm.dimensions();
 }
@@ -109,19 +109,13 @@
                                       : (uint32_t)kNeedNewImageUniqueID)
         , fBitmap(bm)
     {
-        if (bm.pixelRef()->isPreLocked()) {
-            // we only preemptively lock if there is no chance of triggering something expensive
-            // like a lazy decode or imagegenerator. PreLocked means it is flat pixels already.
-            fBitmap.lockPixels();
-        }
         SkASSERT(bitmapMayBeMutable || fBitmap.isImmutable());
     }
 
-    bool onIsLazyGenerated() const override {
-        return fBitmap.pixelRef() && fBitmap.pixelRef()->isLazyGenerated();
-    }
+    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType,
+                                    SkTransferFunctionBehavior) const override;
 
-    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const override;
+    bool onIsValid(GrContext* context) const override { return true; }
 
 #if SK_SUPPORT_GPU
     sk_sp<GrTextureProxy> refPinnedTextureProxy(uint32_t* uniqueID) const override;
@@ -156,7 +150,6 @@
 
     fBitmap.installPixels(info, addr, rowBytes, ctable, release_data, data.release());
     fBitmap.setImmutable();
-    fBitmap.lockPixels();
 }
 
 SkImage_Raster::~SkImage_Raster() {
@@ -311,7 +304,6 @@
 sk_sp<SkImage> SkMakeImageFromRasterBitmap(const SkBitmap& bm, SkCopyPixelsMode cpm) {
     bool hasColorTable = false;
     if (kIndex_8_SkColorType == bm.colorType()) {
-        SkAutoLockPixels autoLockPixels(bm);
         hasColorTable = bm.getColorTable() != nullptr;
     }
 
@@ -320,10 +312,8 @@
     }
 
     if (kAlways_SkCopyPixelsMode == cpm || (!bm.isImmutable() && kNever_SkCopyPixelsMode != cpm)) {
-        SkBitmap tmp(bm);
-        tmp.lockPixels();
         SkPixmap pmap;
-        if (tmp.getPixels() && tmp.peekPixels(&pmap)) {
+        if (bm.getPixels() && bm.peekPixels(&pmap)) {
             return SkImage::MakeRasterCopy(pmap);
         }
     } else {
@@ -355,70 +345,26 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static inline void do_color_xform_non_linear_blending(SkBitmap* dst, const SkPixmap& src) {
-    SkDEBUGCODE(SkColorSpaceTransferFn fn;);
-    SkASSERT(dst->colorSpace()->isNumericalTransferFn(&fn) &&
-             src.colorSpace()->isNumericalTransferFn(&fn));
-
-    void* dstPixels = dst->getPixels();
-    const void* srcPixels = src.addr();
-    size_t dstRowBytes = dst->rowBytes();
-    size_t srcRowBytes = src.rowBytes();
-    if (kN32_SkColorType != src.colorType()) {
-        SkAssertResult(src.readPixels(src.info().makeColorType(kN32_SkColorType), dstPixels,
-                                      dstRowBytes, 0, 0));
-
-        srcPixels = dstPixels;
-        srcRowBytes = dstRowBytes;
-    }
-
-    std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform_Base::New(
-            src.colorSpace(), dst->colorSpace(), SkTransferFunctionBehavior::kIgnore);
-
-    void* dstRow = dstPixels;
-    const void* srcRow = srcPixels;
-    for (int y = 0; y < dst->height(); y++) {
-        // This function assumes non-linear blending.  Which means that we must start by
-        // unpremultiplying in the gamma encoded space.
-        const void* tmpRow = srcRow;
-        if (kPremul_SkAlphaType == src.alphaType()) {
-            SkUnpremultiplyRow<false>((uint32_t*) dstRow, (const uint32_t*) srcRow, dst->width());
-            tmpRow = dstRow;
-        }
-
-        SkColorSpaceXform::ColorFormat fmt = select_xform_format(kN32_SkColorType);
-        SkAssertResult(xform->apply(fmt, dstRow, fmt, tmpRow, dst->width(), dst->alphaType()));
-
-        dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
-        srcRow = SkTAddOffset<const void>(srcRow, srcRowBytes);
-    }
-}
-
-sk_sp<SkImage> SkImage_Raster::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
-    // Force the color type of the new image to be kN32_SkColorType.
-    // (1) This means we lose precision on F16 images.  This is necessary while this function is
-    //     used to pre-transform inputs to a legacy canvas.  Legacy canvases do not handle F16.
-    // (2) kIndex8 and kGray8 must be expanded in order perform a color space transformation.
-    // (3) Seems reasonable to expand k565 and k4444.  It's nice to avoid these color types for
-    //     clients who opt into color space support.
-    SkImageInfo dstInfo = fBitmap.info().makeColorType(kN32_SkColorType).makeColorSpace(target);
-    SkBitmap dst;
-    dst.allocPixels(dstInfo);
-
+sk_sp<SkImage> SkImage_Raster::onMakeColorSpace(sk_sp<SkColorSpace> target,
+                                                SkColorType targetColorType,
+                                                SkTransferFunctionBehavior premulBehavior) const {
     SkPixmap src;
-    SkTLazy<SkBitmap> tmp;
-    if (!fBitmap.peekPixels(&src)) {
-        tmp.init(fBitmap);
-        tmp.get()->lockPixels();
-        SkAssertResult(tmp.get()->peekPixels(&src));
-    }
+    SkAssertResult(fBitmap.peekPixels(&src));
 
     // Treat nullptr srcs as sRGB.
     if (!src.colorSpace()) {
+        if (target->isSRGB()) {
+            return sk_ref_sp(const_cast<SkImage*>((SkImage*)this));
+        }
+
         src.setColorSpace(SkColorSpace::MakeSRGB());
     }
 
-    do_color_xform_non_linear_blending(&dst, src);
+    SkImageInfo dstInfo = fBitmap.info().makeColorType(targetColorType).makeColorSpace(target);
+    SkBitmap dst;
+    dst.allocPixels(dstInfo);
+
+    SkAssertResult(dst.writePixels(src, 0, 0, premulBehavior));
     dst.setImmutable();
     return SkImage::MakeFromBitmap(dst);
 }
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 55aab3e..cd2a5f1 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -201,6 +201,12 @@
     return nullptr;
 }
 
+sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext*, const GrBackendTexture&,
+                                                   GrSurfaceOrigin origin, int sampleCnt,
+                                                   sk_sp<SkColorSpace>, const SkSurfaceProps*) {
+    return nullptr;
+}
+
 sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext*,
                                                         const GrBackendRenderTargetDesc&,
                                                         sk_sp<SkColorSpace>,
@@ -208,8 +214,27 @@
     return nullptr;
 }
 
-sk_sp<SkSurface> MakeFromBackendTextureAsRenderTarget(GrContext*, const GrBackendTextureDesc&,
-                                                      sk_sp<SkColorSpace>, const SkSurfaceProps*) {
+sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext*,
+                                                        const GrBackendRenderTarget&,
+                                                        GrSurfaceOrigin origin,
+                                                        sk_sp<SkColorSpace>,
+                                                        const SkSurfaceProps*) {
+    return nullptr;
+}
+
+sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext*,
+                                                                 const GrBackendTextureDesc&,
+                                                                 sk_sp<SkColorSpace>,
+                                                                 const SkSurfaceProps*) {
+    return nullptr;
+}
+
+sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext*,
+                                                                 const GrBackendTexture&,
+                                                                 GrSurfaceOrigin origin,
+                                                                 int sampleCnt,
+                                                                 sk_sp<SkColorSpace>,
+                                                                 const SkSurfaceProps*) {
     return nullptr;
 }
 
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index cef355e..992e4c9 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -7,6 +7,7 @@
 
 #include "SkSurface_Gpu.h"
 
+#include "GrBackendSurface.h"
 #include "GrContextPriv.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrResourceProvider.h"
@@ -105,8 +106,11 @@
     // copy-on-write.
     if (!srcProxy || rtc->priv().refsWrappedObjects()) {
         // MDB TODO: replace this with GrSurfaceProxy::Copy?
-        GrSurfaceDesc desc = rtc->desc();
-        desc.fFlags = desc.fFlags & ~kRenderTarget_GrSurfaceFlag;
+        GrSurfaceDesc desc;
+        desc.fConfig = rtc->config();
+        desc.fWidth = rtc->width();
+        desc.fHeight = rtc->height();
+        desc.fOrigin = rtc->origin();
 
         sk_sp<GrSurfaceContext> copyCtx = ctx->contextPriv().makeDeferredSurfaceContext(
                                                                 desc,
@@ -220,22 +224,32 @@
     if (!context) {
         return nullptr;
     }
-    if (!SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag)) {
+    GrBackendTexture tex(desc, context->contextPriv().getBackend());
+    return MakeFromBackendTexture(context, tex, desc.fOrigin, desc.fSampleCnt, colorSpace, props);
+}
+
+sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex,
+                                                   GrSurfaceOrigin origin, int sampleCnt,
+                                                   sk_sp<SkColorSpace> colorSpace,
+                                                   const SkSurfaceProps* props) {
+    if (!context) {
         return nullptr;
     }
-    if (!SkSurface_Gpu::Valid(context, desc.fConfig, colorSpace.get())) {
+    if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
         return nullptr;
     }
 
     sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeBackendTextureRenderTargetContext(
-                                                                    desc,
+                                                                    tex,
+                                                                    origin,
+                                                                    sampleCnt,
                                                                     std::move(colorSpace),
                                                                     props));
     if (!rtc) {
         return nullptr;
     }
 
-    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), desc.fWidth, desc.fHeight,
+    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
                                                 SkGpuDevice::kUninit_InitContents));
     if (!device) {
         return nullptr;
@@ -250,19 +264,36 @@
     if (!context) {
         return nullptr;
     }
-    if (!SkSurface_Gpu::Valid(context, desc.fConfig, colorSpace.get())) {
+
+    GrBackendRenderTarget backendRT(desc, context->contextPriv().getBackend());
+    return MakeFromBackendRenderTarget(context, backendRT, desc.fOrigin,
+                                       std::move(colorSpace), props);
+
+}
+
+sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
+                                                        const GrBackendRenderTarget& backendRT,
+                                                        GrSurfaceOrigin origin,
+                                                        sk_sp<SkColorSpace> colorSpace,
+                                                        const SkSurfaceProps* props) {
+    if (!context) {
+        return nullptr;
+    }
+    if (!SkSurface_Gpu::Valid(context, backendRT.config(), colorSpace.get())) {
         return nullptr;
     }
 
     sk_sp<GrRenderTargetContext> rtc(
-        context->contextPriv().makeBackendRenderTargetRenderTargetContext(desc,
+        context->contextPriv().makeBackendRenderTargetRenderTargetContext(backendRT,
+                                                                          origin,
                                                                           std::move(colorSpace),
                                                                           props));
     if (!rtc) {
         return nullptr;
     }
 
-    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), desc.fWidth, desc.fHeight,
+    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc),
+                                                backendRT.width(), backendRT.height(),
                                                 SkGpuDevice::kUninit_InitContents));
     if (!device) {
         return nullptr;
@@ -278,20 +309,36 @@
     if (!context) {
         return nullptr;
     }
-    if (!SkSurface_Gpu::Valid(context, desc.fConfig, colorSpace.get())) {
+    GrBackendTexture tex(desc, context->contextPriv().getBackend());
+    return MakeFromBackendTextureAsRenderTarget(context, tex, desc.fOrigin, desc.fSampleCnt,
+                                                std::move(colorSpace), props);
+}
+
+sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
+                                                                 const GrBackendTexture& tex,
+                                                                 GrSurfaceOrigin origin,
+                                                                 int sampleCnt,
+                                                                 sk_sp<SkColorSpace> colorSpace,
+                                                                 const SkSurfaceProps* props) {
+    if (!context) {
+        return nullptr;
+    }
+    if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
         return nullptr;
     }
 
     sk_sp<GrRenderTargetContext> rtc(
         context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext(
-                                                                              desc,
+                                                                              tex,
+                                                                              origin,
+                                                                              sampleCnt,
                                                                               std::move(colorSpace),
                                                                               props));
     if (!rtc) {
         return nullptr;
     }
 
-    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), desc.fWidth, desc.fHeight,
+    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
                                                 SkGpuDevice::kUninit_InitContents));
     if (!device) {
         return nullptr;
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index 4fae0f0..1ee3d7e 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -11,16 +11,12 @@
 #include "SkDevice.h"
 #include "SkMallocPixelRef.h"
 
-static const size_t kIgnoreRowBytesValue = (size_t)~0;
-
 class SkSurface_Raster : public SkSurface_Base {
 public:
-    static bool Valid(const SkImageInfo&, size_t rb = kIgnoreRowBytesValue);
-
     SkSurface_Raster(const SkImageInfo&, void*, size_t rb,
                      void (*releaseProc)(void* pixels, void* context), void* context,
                      const SkSurfaceProps*);
-    SkSurface_Raster(sk_sp<SkPixelRef>, const SkSurfaceProps*);
+    SkSurface_Raster(const SkImageInfo& info, sk_sp<SkPixelRef>, const SkSurfaceProps*);
 
     SkCanvas* onNewCanvas() override;
     sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override;
@@ -39,7 +35,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkSurface_Raster::Valid(const SkImageInfo& info, size_t rowBytes) {
+bool SkSurfaceValidateRasterInfo(const SkImageInfo& info, size_t rowBytes) {
     if (info.isEmpty()) {
         return false;
     }
@@ -108,11 +104,10 @@
     fWeOwnThePixels = false;    // We are "Direct"
 }
 
-SkSurface_Raster::SkSurface_Raster(sk_sp<SkPixelRef> pr, const SkSurfaceProps* props)
-    : INHERITED(pr->info().width(), pr->info().height(), props)
+SkSurface_Raster::SkSurface_Raster(const SkImageInfo& info, sk_sp<SkPixelRef> pr,
+                                   const SkSurfaceProps* props)
+    : INHERITED(pr->width(), pr->height(), props)
 {
-    const SkImageInfo& info = pr->info();
-
     fBitmap.setInfo(info, pr->rowBytes());
     fRowBytes = pr->rowBytes(); // we track this, so that subsequent re-allocs will match
     fBitmap.setPixelRef(std::move(pr), 0, 0);
@@ -165,7 +160,6 @@
         } else {
             SkBitmap prev(fBitmap);
             fBitmap.allocPixels();
-            prev.lockPixels();
             SkASSERT(prev.info() == fBitmap.info());
             SkASSERT(prev.rowBytes() == fBitmap.rowBytes());
             memcpy(fBitmap.getPixels(), prev.getPixels(), fBitmap.getSafeSize());
@@ -188,7 +182,7 @@
     if (nullptr == releaseProc) {
         context = nullptr;
     }
-    if (!SkSurface_Raster::Valid(info, rb)) {
+    if (!SkSurfaceValidateRasterInfo(info, rb)) {
         return nullptr;
     }
     if (nullptr == pixels) {
@@ -205,16 +199,16 @@
 
 sk_sp<SkSurface> SkSurface::MakeRaster(const SkImageInfo& info, size_t rowBytes,
                                        const SkSurfaceProps* props) {
-    if (!SkSurface_Raster::Valid(info)) {
+    if (!SkSurfaceValidateRasterInfo(info)) {
         return nullptr;
     }
 
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewZeroed(info, rowBytes, nullptr));
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeZeroed(info, rowBytes, nullptr);
     if (!pr) {
         return nullptr;
     }
     if (rowBytes) {
         SkASSERT(pr->rowBytes() == rowBytes);
     }
-    return sk_make_sp<SkSurface_Raster>(std::move(pr), props);
+    return sk_make_sp<SkSurface_Raster>(info, std::move(pr), props);
 }
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
index fecadbf..d79741d 100644
--- a/src/images/SkImageEncoder.cpp
+++ b/src/images/SkImageEncoder.cpp
@@ -6,6 +6,27 @@
  */
 
 #include "SkImageEncoderPriv.h"
+#include "SkJpegEncoder.h"
+#include "SkPngEncoder.h"
+#include "SkWebpEncoder.h"
+
+#ifndef SK_HAS_JPEG_LIBRARY
+bool SkJpegEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
+std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream*, const SkPixmap&, const Options&) {
+    return nullptr;
+}
+#endif
+
+#ifndef SK_HAS_PNG_LIBRARY
+bool SkPngEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
+std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream*, const SkPixmap&, const Options&) {
+    return nullptr;
+}
+#endif
+
+#ifndef SK_HAS_WEBP_LIBRARY
+bool SkWebpEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
+#endif
 
 bool SkEncodeImage(SkWStream* dst, const SkPixmap& src,
                    SkEncodedImageFormat format, int quality) {
@@ -16,14 +37,44 @@
         return SkEncodeImageWithWIC(dst, src, format, quality);
     #else
         switch(format) {
-            case SkEncodedImageFormat::kJPEG:
-                return SkEncodeImageAsJPEG(dst, src, quality);
-            case SkEncodedImageFormat::kPNG:
-                return SkEncodeImageAsPNG(dst, src, SkEncodeOptions());
-            case SkEncodedImageFormat::kWEBP:
-                return SkEncodeImageAsWEBP(dst, src, quality);
+            case SkEncodedImageFormat::kJPEG: {
+                SkJpegEncoder::Options opts;
+                opts.fQuality = quality;
+                return SkJpegEncoder::Encode(dst, src, opts);
+            }
+            case SkEncodedImageFormat::kPNG: {
+                SkPngEncoder::Options opts;
+                opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+                return SkPngEncoder::Encode(dst, src, opts);
+            }
+            case SkEncodedImageFormat::kWEBP: {
+                SkWebpEncoder::Options opts;
+                opts.fCompression = SkWebpEncoder::Compression::kLossy;
+                opts.fQuality = quality;
+                opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+                return SkWebpEncoder::Encode(dst, src, opts);
+            }
             default:
                 return false;
         }
     #endif
 }
+
+bool SkEncoder::encodeRows(int numRows) {
+    SkASSERT(numRows > 0 && fCurrRow < fSrc.height());
+    if (numRows <= 0 || fCurrRow >= fSrc.height()) {
+        return false;
+    }
+
+    if (fCurrRow + numRows > fSrc.height()) {
+        numRows = fSrc.height() - fCurrRow;
+    }
+
+    if (!this->onEncodeRows(numRows)) {
+        // If we fail, short circuit any future calls.
+        fCurrRow = fSrc.height();
+        return false;
+    }
+
+    return true;
+}
diff --git a/src/images/SkImageEncoderFns.h b/src/images/SkImageEncoderFns.h
index cfac46e..6012a45 100644
--- a/src/images/SkImageEncoderFns.h
+++ b/src/images/SkImageEncoderFns.h
@@ -17,6 +17,7 @@
 #include "SkColorPriv.h"
 #include "SkColorSpace_Base.h"
 #include "SkICC.h"
+#include "SkOpts.h"
 #include "SkPreConfig.h"
 #include "SkRasterPipeline.h"
 #include "SkUnPreMultiply.h"
@@ -152,7 +153,7 @@
 
 template <bool kIsRGBA>
 static inline void transform_scanline_unpremultiply_sRGB(void* dst, const void* src, int width) {
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::load_8888, &src);
     if (!kIsRGBA) {
         p.append(SkRasterPipeline::swap_rb);
@@ -162,7 +163,31 @@
     p.append(SkRasterPipeline::unpremul);
     p.append(SkRasterPipeline::to_srgb);
     p.append(SkRasterPipeline::store_8888, &dst);
-    p.run(0, width);
+    p.run(0,0, width);
+}
+
+/**
+ * Premultiply RGBA to rgbA.
+ */
+static inline void transform_scanline_to_premul_legacy(char* SK_RESTRICT dst,
+                                                       const char* SK_RESTRICT src,
+                                                       int width, int, const SkPMColor*) {
+    SkOpts::RGBA_to_rgbA((uint32_t*)dst, (const uint32_t*)src, width);
+}
+
+/**
+ * Premultiply RGBA to rgbA linearly.
+ */
+static inline void transform_scanline_to_premul_linear(char* SK_RESTRICT dst,
+                                                       const char* SK_RESTRICT src,
+                                                       int width, int, const SkPMColor*) {
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_8888, (const void**) &src);
+    p.append_from_srgb(kUnpremul_SkAlphaType);
+    p.append(SkRasterPipeline::premul);
+    p.append(SkRasterPipeline::to_srgb);
+    p.append(SkRasterPipeline::store_8888, (void**) &dst);
+    p.run(0,0, width);
 }
 
 /**
@@ -230,11 +255,11 @@
  */
 static inline void transform_scanline_F16(char* SK_RESTRICT dst, const char* SK_RESTRICT src,
                                           int width, int, const SkPMColor*) {
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::load_f16, (const void**) &src);
     p.append(SkRasterPipeline::to_srgb);
     p.append(SkRasterPipeline::store_u16_be, (void**) &dst);
-    p.run(0, width);
+    p.run(0,0, width);
 }
 
 /**
@@ -242,12 +267,12 @@
  */
 static inline void transform_scanline_F16_premul(char* SK_RESTRICT dst, const char* SK_RESTRICT src,
                                                  int width, int, const SkPMColor*) {
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::load_f16, (const void**) &src);
     p.append(SkRasterPipeline::unpremul);
     p.append(SkRasterPipeline::to_srgb);
     p.append(SkRasterPipeline::store_u16_be, (void**) &dst);
-    p.run(0, width);
+    p.run(0,0, width);
 }
 
 /**
@@ -256,11 +281,11 @@
 static inline void transform_scanline_F16_to_8888(char* SK_RESTRICT dst,
                                                   const char* SK_RESTRICT src, int width, int,
                                                   const SkPMColor*) {
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::load_f16, (const void**) &src);
     p.append(SkRasterPipeline::to_srgb);
     p.append(SkRasterPipeline::store_8888, (void**) &dst);
-    p.run(0, width);
+    p.run(0,0, width);
 }
 
 /**
@@ -269,12 +294,25 @@
 static inline void transform_scanline_F16_premul_to_8888(char* SK_RESTRICT dst,
                                                          const char* SK_RESTRICT src, int width,
                                                          int, const SkPMColor*) {
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::load_f16, (const void**) &src);
     p.append(SkRasterPipeline::unpremul);
     p.append(SkRasterPipeline::to_srgb);
     p.append(SkRasterPipeline::store_8888, (void**) &dst);
-    p.run(0, width);
+    p.run(0,0, width);
+}
+
+/**
+ * Transform from kUnpremul, kRGBA_F16 to premultiplied rgbA 8888.
+ */
+static inline void transform_scanline_F16_to_premul_8888(char* SK_RESTRICT dst,
+        const char* SK_RESTRICT src, int width, int, const SkPMColor*) {
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_f16, (const void**) &src);
+    p.append(SkRasterPipeline::premul);
+    p.append(SkRasterPipeline::to_srgb);
+    p.append(SkRasterPipeline::store_8888, (void**) &dst);
+    p.run(0,0, width);
 }
 
 static inline sk_sp<SkData> icc_from_color_space(const SkImageInfo& info) {
diff --git a/src/images/SkImageEncoderPriv.h b/src/images/SkImageEncoderPriv.h
index 540d930..e6903bc 100644
--- a/src/images/SkImageEncoderPriv.h
+++ b/src/images/SkImageEncoderPriv.h
@@ -9,30 +9,27 @@
 #define SkImageEncoderPriv_DEFINED
 
 #include "SkImageEncoder.h"
+#include "SkImageInfoPriv.h"
 
-struct SkEncodeOptions {
-    SkTransferFunctionBehavior fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
-};
+static inline bool SkPixmapIsValid(const SkPixmap& src,
+                                   SkTransferFunctionBehavior unpremulBehavior)
+{
+    if (SkTransferFunctionBehavior::kRespect == unpremulBehavior) {
+        if (!SkImageInfoIsValidRenderingCS(src.info())) {
+            return false;
+        }
+    } else {
+        if (!SkImageInfoIsValidAllowNumericalCS(src.info())) {
+            return false;
+        }
+    }
 
-#ifdef SK_HAS_JPEG_LIBRARY
-    bool SkEncodeImageAsJPEG(SkWStream*, const SkPixmap&, const SkEncodeOptions&);
-    bool SkEncodeImageAsJPEG(SkWStream*, const SkPixmap&, int quality);
-#else
-    #define SkEncodeImageAsJPEG(...) false
-#endif
+    if (!src.addr() || src.rowBytes() < src.info().minRowBytes()) {
+        return false;
+    }
 
-#ifdef SK_HAS_PNG_LIBRARY
-    bool SkEncodeImageAsPNG(SkWStream*, const SkPixmap&, const SkEncodeOptions&);
-#else
-    #define SkEncodeImageAsPNG(...) false
-#endif
-
-#ifdef SK_HAS_WEBP_LIBRARY
-    bool SkEncodeImageAsWEBP(SkWStream*, const SkPixmap&, const SkEncodeOptions&);
-    bool SkEncodeImageAsWEBP(SkWStream*, const SkPixmap&, int quality);
-#else
-    #define SkEncodeImageAsWEBP(...) false
-#endif
+    return true;
+}
 
 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
     bool SkEncodeImageWithCG(SkWStream*, const SkPixmap&, SkEncodedImageFormat);
diff --git a/src/images/SkJPEGImageEncoder.cpp b/src/images/SkJPEGImageEncoder.cpp
deleted file mode 100644
index 1524de3..0000000
--- a/src/images/SkJPEGImageEncoder.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright 2007 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkImageEncoderPriv.h"
-
-#ifdef SK_HAS_JPEG_LIBRARY
-
-#include "SkColorPriv.h"
-#include "SkImageEncoderFns.h"
-#include "SkJPEGWriteUtility.h"
-#include "SkStream.h"
-#include "SkTemplates.h"
-
-#include <stdio.h>
-
-extern "C" {
-    #include "jpeglib.h"
-    #include "jerror.h"
-}
-
-/**
- *  Returns true if |info| is supported by the jpeg encoder and false otherwise.
- *  |jpegColorType| will be set to the proper libjpeg-turbo type for input to the library.
- *  |numComponents| will be set to the number of components in the |jpegColorType|.
- *  |proc|          will be set if we need to pre-convert the input before passing to
- *                  libjpeg-turbo.  Otherwise will be set to nullptr.
- */
-// TODO (skbug.com/1501):
-// Should we fail on non-opaque encodes?
-// Or should we change alpha behavior (ex: unpremultiply when the input is premul)?
-// Or is ignoring the alpha type and alpha channel ok here?
-static bool set_encode_config(J_COLOR_SPACE* jpegColorType, int* numComponents,
-                              transform_scanline_proc* proc, const SkImageInfo& info) {
-    *proc = nullptr;
-    switch (info.colorType()) {
-        case kRGBA_8888_SkColorType:
-            *jpegColorType = JCS_EXT_RGBA;
-            *numComponents = 4;
-            return true;
-        case kBGRA_8888_SkColorType:
-            *jpegColorType = JCS_EXT_BGRA;
-            *numComponents = 4;
-            return true;
-        case kRGB_565_SkColorType:
-            *proc = transform_scanline_565;
-            *jpegColorType = JCS_RGB;
-            *numComponents = 3;
-            return true;
-        case kARGB_4444_SkColorType:
-            *proc = transform_scanline_444;
-            *jpegColorType = JCS_RGB;
-            *numComponents = 3;
-            return true;
-        case kIndex_8_SkColorType:
-            *proc = transform_scanline_index8_opaque;
-            *jpegColorType = JCS_RGB;
-            *numComponents = 3;
-            return true;
-        case kGray_8_SkColorType:
-            SkASSERT(info.isOpaque());
-            *jpegColorType = JCS_GRAYSCALE;
-            *numComponents = 1;
-            return true;
-        case kRGBA_F16_SkColorType:
-            if (!info.colorSpace() || !info.colorSpace()->gammaIsLinear()) {
-                return false;
-            }
-
-            *proc = transform_scanline_F16_to_8888;
-            *jpegColorType = JCS_EXT_RGBA;
-            *numComponents = 4;
-            return true;
-        default:
-            return false;
-    }
-
-
-}
-
-bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts) {
-    if (SkTransferFunctionBehavior::kRespect == opts.fUnpremulBehavior) {
-        // Respecting the transfer function requries a color space.  It's not actually critical
-        // in the jpeg case (since jpegs are opaque), but Skia color correct behavior generally
-        // requires pixels to be tagged with color spaces.
-        if (!pixmap.colorSpace() || (!pixmap.colorSpace()->gammaCloseToSRGB() &&
-                                     !pixmap.colorSpace()->gammaIsLinear())) {
-            return false;
-        }
-    }
-
-    return SkEncodeImageAsJPEG(stream, pixmap, 100);
-}
-
-bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality) {
-    if (!pixmap.addr()) {
-        return false;
-    }
-    jpeg_compress_struct    cinfo;
-    skjpeg_error_mgr        sk_err;
-    skjpeg_destination_mgr  sk_wstream(stream);
-
-    // Declare before calling setjmp.
-    SkAutoTMalloc<uint8_t>  storage;
-
-    cinfo.err = jpeg_std_error(&sk_err);
-    sk_err.error_exit = skjpeg_error_exit;
-    if (setjmp(sk_err.fJmpBuf)) {
-        return false;
-    }
-
-    J_COLOR_SPACE jpegColorSpace;
-    int numComponents;
-    transform_scanline_proc proc;
-    if (!set_encode_config(&jpegColorSpace, &numComponents, &proc, pixmap.info())) {
-        return false;
-    }
-
-    jpeg_create_compress(&cinfo);
-    cinfo.dest = &sk_wstream;
-    cinfo.image_width = pixmap.width();
-    cinfo.image_height = pixmap.height();
-    cinfo.input_components = numComponents;
-    cinfo.in_color_space = jpegColorSpace;
-
-    jpeg_set_defaults(&cinfo);
-
-    // Tells libjpeg-turbo to compute optimal Huffman coding tables
-    // for the image.  This improves compression at the cost of
-    // slower encode performance.
-    cinfo.optimize_coding = TRUE;
-    jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
-
-    jpeg_start_compress(&cinfo, TRUE);
-
-    sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
-    if (icc) {
-        // Create a contiguous block of memory with the icc signature followed by the profile.
-        sk_sp<SkData> markerData =
-                SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
-        uint8_t* ptr = (uint8_t*) markerData->writable_data();
-        memcpy(ptr, kICCSig, sizeof(kICCSig));
-        ptr += sizeof(kICCSig);
-        *ptr++ = 1; // This is the first marker.
-        *ptr++ = 1; // Out of one total markers.
-        memcpy(ptr, icc->data(), icc->size());
-
-        jpeg_write_marker(&cinfo, kICCMarker, markerData->bytes(), markerData->size());
-    }
-
-    if (proc) {
-        storage.reset(numComponents * pixmap.width());
-    }
-
-    const void* srcRow = pixmap.addr();
-    const SkPMColor* colors = pixmap.ctable() ? pixmap.ctable()->readColors() : nullptr;
-    while (cinfo.next_scanline < cinfo.image_height) {
-        JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
-        if (proc) {
-            proc((char*)storage.get(), (const char*)srcRow, pixmap.width(), numComponents, colors);
-            jpegSrcRow = storage.get();
-        }
-
-        (void) jpeg_write_scanlines(&cinfo, &jpegSrcRow, 1);
-        srcRow = SkTAddOffset<const void>(srcRow, pixmap.rowBytes());
-    }
-
-    jpeg_finish_compress(&cinfo);
-    jpeg_destroy_compress(&cinfo);
-
-    return true;
-}
-#endif
diff --git a/src/images/SkJpegEncoder.cpp b/src/images/SkJpegEncoder.cpp
new file mode 100644
index 0000000..a0ee398
--- /dev/null
+++ b/src/images/SkJpegEncoder.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkImageEncoderPriv.h"
+
+#ifdef SK_HAS_JPEG_LIBRARY
+
+#include "SkColorPriv.h"
+#include "SkColorSpace_Base.h"
+#include "SkImageEncoderFns.h"
+#include "SkImageInfoPriv.h"
+#include "SkJpegEncoder.h"
+#include "SkJPEGWriteUtility.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+#include <stdio.h>
+
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+class SkJpegEncoderMgr final : SkNoncopyable {
+public:
+
+    /*
+     * Create the decode manager
+     * Does not take ownership of stream
+     */
+    static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
+        return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
+    }
+
+    bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
+
+    jpeg_compress_struct* cinfo() { return &fCInfo; }
+
+    jmp_buf& jmpBuf() { return fErrMgr.fJmpBuf; }
+
+    transform_scanline_proc proc() const { return fProc; }
+
+    ~SkJpegEncoderMgr() {
+        jpeg_destroy_compress(&fCInfo);
+    }
+
+private:
+
+    SkJpegEncoderMgr(SkWStream* stream)
+        : fDstMgr(stream)
+        , fProc(nullptr)
+    {
+        fCInfo.err = jpeg_std_error(&fErrMgr);
+        fErrMgr.error_exit = skjpeg_error_exit;
+        jpeg_create_compress(&fCInfo);
+        fCInfo.dest = &fDstMgr;
+    }
+
+    jpeg_compress_struct    fCInfo;
+    skjpeg_error_mgr        fErrMgr;
+    skjpeg_destination_mgr  fDstMgr;
+    transform_scanline_proc fProc;
+};
+
+bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options)
+{
+    auto chooseProc8888 = [&]() {
+        if (kUnpremul_SkAlphaType != srcInfo.alphaType() ||
+            SkJpegEncoder::AlphaOption::kIgnore == options.fAlphaOption)
+        {
+            return (transform_scanline_proc) nullptr;
+        }
+
+        // Note that kRespect mode is only supported with sRGB or linear transfer functions.
+        // The legacy code path is incidentally correct when the transfer function is linear.
+        const bool isSRGBTransferFn = srcInfo.gammaCloseToSRGB() &&
+                (SkTransferFunctionBehavior::kRespect == options.fBlendBehavior);
+        if (isSRGBTransferFn) {
+            return transform_scanline_to_premul_linear;
+        } else {
+            return transform_scanline_to_premul_legacy;
+        }
+    };
+
+    J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
+    int numComponents = 0;
+    switch (srcInfo.colorType()) {
+        case kRGBA_8888_SkColorType:
+            fProc = chooseProc8888();
+            jpegColorType = JCS_EXT_RGBA;
+            numComponents = 4;
+            break;
+        case kBGRA_8888_SkColorType:
+            fProc = chooseProc8888();
+            jpegColorType = JCS_EXT_BGRA;
+            numComponents = 4;
+            break;
+        case kRGB_565_SkColorType:
+            fProc = transform_scanline_565;
+            jpegColorType = JCS_RGB;
+            numComponents = 3;
+            break;
+        case kARGB_4444_SkColorType:
+            if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
+                return false;
+            }
+
+            fProc = transform_scanline_444;
+            jpegColorType = JCS_RGB;
+            numComponents = 3;
+            break;
+        case kIndex_8_SkColorType:
+            if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
+                return false;
+            }
+
+            fProc = transform_scanline_index8_opaque;
+            jpegColorType = JCS_RGB;
+            numComponents = 3;
+            break;
+        case kGray_8_SkColorType:
+            SkASSERT(srcInfo.isOpaque());
+            jpegColorType = JCS_GRAYSCALE;
+            numComponents = 1;
+            break;
+        case kRGBA_F16_SkColorType:
+            if (!srcInfo.colorSpace() || !srcInfo.colorSpace()->gammaIsLinear() ||
+                    SkTransferFunctionBehavior::kRespect != options.fBlendBehavior) {
+                return false;
+            }
+
+            if (kUnpremul_SkAlphaType != srcInfo.alphaType() ||
+                SkJpegEncoder::AlphaOption::kIgnore == options.fAlphaOption)
+            {
+                fProc = transform_scanline_F16_to_8888;
+            } else {
+                fProc = transform_scanline_F16_to_premul_8888;
+            }
+            jpegColorType = JCS_EXT_RGBA;
+            numComponents = 4;
+            break;
+        default:
+            return false;
+    }
+
+    fCInfo.image_width = srcInfo.width();
+    fCInfo.image_height = srcInfo.height();
+    fCInfo.in_color_space = jpegColorType;
+    fCInfo.input_components = numComponents;
+    jpeg_set_defaults(&fCInfo);
+
+    if (kGray_8_SkColorType != srcInfo.colorType()) {
+        switch (options.fDownsample) {
+            case SkJpegEncoder::Downsample::k420:
+                SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
+                SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
+                SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
+                SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
+                SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
+                SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
+                break;
+            case SkJpegEncoder::Downsample::k422:
+                fCInfo.comp_info[0].h_samp_factor = 2;
+                fCInfo.comp_info[0].v_samp_factor = 1;
+                fCInfo.comp_info[1].h_samp_factor = 1;
+                fCInfo.comp_info[1].v_samp_factor = 1;
+                fCInfo.comp_info[2].h_samp_factor = 1;
+                fCInfo.comp_info[2].v_samp_factor = 1;
+                break;
+            case SkJpegEncoder::Downsample::k444:
+                fCInfo.comp_info[0].h_samp_factor = 1;
+                fCInfo.comp_info[0].v_samp_factor = 1;
+                fCInfo.comp_info[1].h_samp_factor = 1;
+                fCInfo.comp_info[1].v_samp_factor = 1;
+                fCInfo.comp_info[2].h_samp_factor = 1;
+                fCInfo.comp_info[2].v_samp_factor = 1;
+                break;
+        }
+    }
+
+    // Tells libjpeg-turbo to compute optimal Huffman coding tables
+    // for the image.  This improves compression at the cost of
+    // slower encode performance.
+    fCInfo.optimize_coding = TRUE;
+    return true;
+}
+
+std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
+                                               const Options& options) {
+    if (!SkPixmapIsValid(src, options.fBlendBehavior)) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
+    if (setjmp(encoderMgr->jmpBuf())) {
+        return nullptr;
+    }
+
+    if (!encoderMgr->setParams(src.info(), options)) {
+        return nullptr;
+    }
+
+    jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
+    jpeg_start_compress(encoderMgr->cinfo(), TRUE);
+
+    sk_sp<SkData> icc = icc_from_color_space(src.info());
+    if (icc) {
+        // Create a contiguous block of memory with the icc signature followed by the profile.
+        sk_sp<SkData> markerData =
+                SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
+        uint8_t* ptr = (uint8_t*) markerData->writable_data();
+        memcpy(ptr, kICCSig, sizeof(kICCSig));
+        ptr += sizeof(kICCSig);
+        *ptr++ = 1; // This is the first marker.
+        *ptr++ = 1; // Out of one total markers.
+        memcpy(ptr, icc->data(), icc->size());
+
+        jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
+    }
+
+    return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
+}
+
+SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
+    : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
+    , fEncoderMgr(std::move(encoderMgr))
+{}
+
+SkJpegEncoder::~SkJpegEncoder() {}
+
+bool SkJpegEncoder::onEncodeRows(int numRows) {
+    if (setjmp(fEncoderMgr->jmpBuf())) {
+        return false;
+    }
+
+    const void* srcRow = fSrc.addr(0, fCurrRow);
+    const SkPMColor* colors = fSrc.ctable() ? fSrc.ctable()->readColors() : nullptr;
+    for (int i = 0; i < numRows; i++) {
+        JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
+        if (fEncoderMgr->proc()) {
+            fEncoderMgr->proc()((char*)fStorage.get(), (const char*)srcRow, fSrc.width(),
+                                fEncoderMgr->cinfo()->input_components, colors);
+            jpegSrcRow = fStorage.get();
+        }
+
+        jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
+        srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
+    }
+
+    fCurrRow += numRows;
+    if (fCurrRow == fSrc.height()) {
+        jpeg_finish_compress(fEncoderMgr->cinfo());
+    }
+
+    return true;
+}
+
+bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
+    auto encoder = SkJpegEncoder::Make(dst, src, options);
+    return encoder.get() && encoder->encodeRows(src.height());
+}
+
+#endif
diff --git a/src/images/SkPNGImageEncoder.cpp b/src/images/SkPNGImageEncoder.cpp
deleted file mode 100644
index eb08fa4..0000000
--- a/src/images/SkPNGImageEncoder.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkImageEncoderPriv.h"
-
-#ifdef SK_HAS_PNG_LIBRARY
-
-#include "SkColor.h"
-#include "SkColorPriv.h"
-#include "SkDither.h"
-#include "SkImageEncoderFns.h"
-#include "SkMath.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "SkUnPreMultiply.h"
-#include "SkUtils.h"
-
-#include "png.h"
-
-// Suppress most PNG warnings when calling image decode functions.
-static const bool c_suppressPNGImageDecoderWarnings = true;
-
-static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
-    if (!c_suppressPNGImageDecoderWarnings) {
-        SkDEBUGF(("------ png error %s\n", msg));
-    }
-    longjmp(png_jmpbuf(png_ptr), 1);
-}
-
-static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
-    SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
-    if (!sk_stream->write(data, len)) {
-        png_error(png_ptr, "sk_write_fn Error!");
-    }
-}
-
-static void set_icc(png_structp png_ptr, png_infop info_ptr, sk_sp<SkData> icc) {
-#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
-    const char* name = "Skia";
-    png_const_bytep iccPtr = icc->bytes();
-#else
-    SkString str("Skia");
-    char* name = str.writable_str();
-    png_charp iccPtr = (png_charp) icc->writable_data();
-#endif
-    png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
-}
-
-static transform_scanline_proc choose_proc(const SkImageInfo& info,
-                                           SkTransferFunctionBehavior unpremulBehavior) {
-    const bool isSRGBTransferFn =
-            (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
-    switch (info.colorType()) {
-        case kRGBA_8888_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_RGBX;
-                case kUnpremul_SkAlphaType:
-                    return transform_scanline_memcpy;
-                case kPremul_SkAlphaType:
-                    return isSRGBTransferFn ? transform_scanline_srgbA :
-                                              transform_scanline_rgbA;
-                default:
-                    SkASSERT(false);
-                    return nullptr;
-            }
-        case kBGRA_8888_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_BGRX;
-                case kUnpremul_SkAlphaType:
-                    return transform_scanline_BGRA;
-                case kPremul_SkAlphaType:
-                    return isSRGBTransferFn ? transform_scanline_sbgrA :
-                                              transform_scanline_bgrA;
-                default:
-                    SkASSERT(false);
-                    return nullptr;
-            }
-        case kRGB_565_SkColorType:
-            return transform_scanline_565;
-        case kARGB_4444_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_444;
-                case kPremul_SkAlphaType:
-                    // 4444 is assumed to be legacy premul.
-                    return transform_scanline_4444;
-                default:
-                    SkASSERT(false);
-                    return nullptr;
-            }
-        case kIndex_8_SkColorType:
-        case kGray_8_SkColorType:
-            return transform_scanline_memcpy;
-        case kRGBA_F16_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                case kUnpremul_SkAlphaType:
-                    return transform_scanline_F16;
-                case kPremul_SkAlphaType:
-                    return transform_scanline_F16_premul;
-                default:
-                    SkASSERT(false);
-                    return nullptr;
-            }
-        default:
-            SkASSERT(false);
-            return nullptr;
-    }
-}
-
-/*  Pack palette[] with the corresponding colors, and if the image has alpha, also
-    pack trans[] and return the number of alphas[] entries written. If the image is
-    opaque, the return value will always be 0.
-*/
-static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
-                               png_byte* SK_RESTRICT alphas, const SkImageInfo& info,
-                               SkTransferFunctionBehavior unpremulBehavior) {
-    const SkPMColor* colors = ctable->readColors();
-    const int count = ctable->count();
-    SkPMColor storage[256];
-    if (kPremul_SkAlphaType == info.alphaType()) {
-        // Unpremultiply the colors.
-        const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType);
-        transform_scanline_proc proc = choose_proc(rgbaInfo, unpremulBehavior);
-        proc((char*) storage, (const char*) colors, ctable->count(), 4, nullptr);
-        colors = storage;
-    }
-
-    int numWithAlpha = 0;
-    if (kOpaque_SkAlphaType != info.alphaType()) {
-        // PNG requires that all non-opaque colors come first in the palette.  Write these first.
-        for (int i = 0; i < count; i++) {
-            uint8_t alpha = SkGetPackedA32(colors[i]);
-            if (0xFF != alpha) {
-                alphas[numWithAlpha] = alpha;
-                palette[numWithAlpha].red   = SkGetPackedR32(colors[i]);
-                palette[numWithAlpha].green = SkGetPackedG32(colors[i]);
-                palette[numWithAlpha].blue  = SkGetPackedB32(colors[i]);
-                numWithAlpha++;
-            }
-        }
-
-    }
-
-    if (0 == numWithAlpha) {
-        // All of the entries are opaque.
-        for (int i = 0; i < count; i++) {
-            SkPMColor c = *colors++;
-            palette[i].red   = SkGetPackedR32(c);
-            palette[i].green = SkGetPackedG32(c);
-            palette[i].blue  = SkGetPackedB32(c);
-        }
-    } else {
-        // We have already written the non-opaque colors.  Now just write the opaque colors.
-        int currIndex = numWithAlpha;
-        int i = 0;
-        while (currIndex != count) {
-            uint8_t alpha = SkGetPackedA32(colors[i]);
-            if (0xFF == alpha) {
-                palette[currIndex].red   = SkGetPackedR32(colors[i]);
-                palette[currIndex].green = SkGetPackedG32(colors[i]);
-                palette[currIndex].blue  = SkGetPackedB32(colors[i]);
-                currIndex++;
-            }
-
-            i++;
-        }
-    }
-
-    return numWithAlpha;
-}
-
-static bool do_encode(SkWStream*, const SkPixmap&, int, int, png_color_8&,
-                      SkTransferFunctionBehavior unpremulBehavior);
-
-bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts) {
-    if (SkTransferFunctionBehavior::kRespect == opts.fUnpremulBehavior) {
-        if (!pixmap.colorSpace() || (!pixmap.colorSpace()->gammaCloseToSRGB() &&
-                                     !pixmap.colorSpace()->gammaIsLinear())) {
-            return false;
-        }
-    }
-
-    if (!pixmap.addr() || pixmap.info().isEmpty()) {
-        return false;
-    }
-
-    const SkColorType colorType = pixmap.colorType();
-    const SkAlphaType alphaType = pixmap.alphaType();
-    switch (alphaType) {
-        case kUnpremul_SkAlphaType:
-            if (kARGB_4444_SkColorType == colorType) {
-                return false;
-            }
-
-            break;
-        case kOpaque_SkAlphaType:
-        case kPremul_SkAlphaType:
-            break;
-        default:
-            return false;
-    }
-
-    const bool isOpaque = (kOpaque_SkAlphaType == alphaType);
-    int bitDepth = 8;
-    png_color_8 sig_bit;
-    sk_bzero(&sig_bit, sizeof(png_color_8));
-    int pngColorType;
-    switch (colorType) {
-        case kRGBA_F16_SkColorType:
-            if (!pixmap.colorSpace() || !pixmap.colorSpace()->gammaIsLinear()) {
-                return false;
-            }
-
-            sig_bit.red = 16;
-            sig_bit.green = 16;
-            sig_bit.blue = 16;
-            sig_bit.alpha = 16;
-            bitDepth = 16;
-            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
-            break;
-        case kIndex_8_SkColorType:
-            sig_bit.red = 8;
-            sig_bit.green = 8;
-            sig_bit.blue = 8;
-            sig_bit.alpha = 8;
-            pngColorType = PNG_COLOR_TYPE_PALETTE;
-            break;
-        case kGray_8_SkColorType:
-            sig_bit.gray = 8;
-            pngColorType = PNG_COLOR_TYPE_GRAY;
-            SkASSERT(isOpaque);
-            break;
-        case kRGBA_8888_SkColorType:
-        case kBGRA_8888_SkColorType:
-            sig_bit.red = 8;
-            sig_bit.green = 8;
-            sig_bit.blue = 8;
-            sig_bit.alpha = 8;
-            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
-            break;
-        case kARGB_4444_SkColorType:
-            sig_bit.red = 4;
-            sig_bit.green = 4;
-            sig_bit.blue = 4;
-            sig_bit.alpha = 4;
-            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
-            break;
-        case kRGB_565_SkColorType:
-            sig_bit.red = 5;
-            sig_bit.green = 6;
-            sig_bit.blue = 5;
-            pngColorType = PNG_COLOR_TYPE_RGB;
-            SkASSERT(isOpaque);
-            break;
-        default:
-            return false;
-    }
-
-    if (kIndex_8_SkColorType == colorType) {
-        SkColorTable* ctable = pixmap.ctable();
-        if (!ctable || ctable->count() == 0) {
-            return false;
-        }
-
-        // Currently, we always use 8-bit indices for paletted pngs.
-        // When ctable->count() <= 16, we could potentially use 1, 2,
-        // or 4 bit indices.
-    }
-
-    return do_encode(stream, pixmap, pngColorType, bitDepth, sig_bit, opts.fUnpremulBehavior);
-}
-
-static int num_components(int pngColorType) {
-    switch (pngColorType) {
-        case PNG_COLOR_TYPE_PALETTE:
-        case PNG_COLOR_TYPE_GRAY:
-            return 1;
-        case PNG_COLOR_TYPE_RGB:
-            return 3;
-        case PNG_COLOR_TYPE_RGBA:
-            return 4;
-        default:
-            SkASSERT(false);
-            return 0;
-    }
-}
-
-static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, int pngColorType, int bitDepth,
-                      png_color_8& sig_bit, SkTransferFunctionBehavior unpremulBehavior) {
-    png_structp png_ptr;
-    png_infop info_ptr;
-
-    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
-    if (nullptr == png_ptr) {
-        return false;
-    }
-
-    info_ptr = png_create_info_struct(png_ptr);
-    if (nullptr == info_ptr) {
-        png_destroy_write_struct(&png_ptr,  nullptr);
-        return false;
-    }
-
-    /* Set error handling.  REQUIRED if you aren't supplying your own
-    * error handling functions in the png_create_write_struct() call.
-    */
-    if (setjmp(png_jmpbuf(png_ptr))) {
-        png_destroy_write_struct(&png_ptr, &info_ptr);
-        return false;
-    }
-
-    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, nullptr);
-
-    /* Set the image information here.  Width and height are up to 2^31,
-    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
-    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
-    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
-    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
-    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
-    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
-    */
-
-    png_set_IHDR(png_ptr, info_ptr, pixmap.width(), pixmap.height(),
-                 bitDepth, pngColorType,
-                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
-                 PNG_FILTER_TYPE_BASE);
-
-    // set our colortable/trans arrays if needed
-    png_color paletteColors[256];
-    png_byte trans[256];
-    if (kIndex_8_SkColorType == pixmap.colorType()) {
-        SkColorTable* colorTable = pixmap.ctable();
-        SkASSERT(colorTable);
-        int numTrans = pack_palette(colorTable, paletteColors, trans, pixmap.info(),
-                                    unpremulBehavior);
-        png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count());
-        if (numTrans > 0) {
-            png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr);
-        }
-    }
-
-    if (pixmap.colorSpace() && pixmap.colorSpace()->isSRGB()) {
-        png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
-    } else {
-        sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
-        if (icc) {
-            set_icc(png_ptr, info_ptr, std::move(icc));
-        }
-    }
-
-    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
-    png_write_info(png_ptr, info_ptr);
-    int pngBytesPerPixel = num_components(pngColorType) * (bitDepth / 8);
-    if (kRGBA_F16_SkColorType == pixmap.colorType() && kOpaque_SkAlphaType == pixmap.alphaType()) {
-        // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
-        // to skip the alpha channel.
-        png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
-        pngBytesPerPixel = 8;
-    }
-
-    SkAutoSTMalloc<1024, char> rowStorage(pixmap.width() * pngBytesPerPixel);
-    char* storage = rowStorage.get();
-    const char* srcImage = (const char*)pixmap.addr();
-    transform_scanline_proc proc = choose_proc(pixmap.info(), unpremulBehavior);
-    for (int y = 0; y < pixmap.height(); y++) {
-        png_bytep row_ptr = (png_bytep)storage;
-        proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(pixmap.colorType()),
-             nullptr);
-        png_write_rows(png_ptr, &row_ptr, 1);
-        srcImage += pixmap.rowBytes();
-    }
-
-    png_write_end(png_ptr, info_ptr);
-
-    /* clean up after the write, and free any memory allocated */
-    png_destroy_write_struct(&png_ptr, &info_ptr);
-    return true;
-}
-
-#endif
diff --git a/src/images/SkPngEncoder.cpp b/src/images/SkPngEncoder.cpp
new file mode 100644
index 0000000..7a1bfcf
--- /dev/null
+++ b/src/images/SkPngEncoder.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkImageEncoderPriv.h"
+
+#ifdef SK_HAS_PNG_LIBRARY
+
+#include "SkImageEncoderFns.h"
+#include "SkImageInfoPriv.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkPngEncoder.h"
+
+#include "png.h"
+
+static_assert(PNG_FILTER_NONE  == (int)SkPngEncoder::FilterFlag::kNone,  "Skia libpng filter err.");
+static_assert(PNG_FILTER_SUB   == (int)SkPngEncoder::FilterFlag::kSub,   "Skia libpng filter err.");
+static_assert(PNG_FILTER_UP    == (int)SkPngEncoder::FilterFlag::kUp,    "Skia libpng filter err.");
+static_assert(PNG_FILTER_AVG   == (int)SkPngEncoder::FilterFlag::kAvg,   "Skia libpng filter err.");
+static_assert(PNG_FILTER_PAETH == (int)SkPngEncoder::FilterFlag::kPaeth, "Skia libpng filter err.");
+static_assert(PNG_ALL_FILTERS  == (int)SkPngEncoder::FilterFlag::kAll,   "Skia libpng filter err.");
+
+static constexpr bool kSuppressPngEncodeWarnings = true;
+
+static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
+    if (!kSuppressPngEncodeWarnings) {
+        SkDebugf("libpng encode error: %s\n", msg);
+    }
+
+    longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
+    SkWStream* stream = (SkWStream*)png_get_io_ptr(png_ptr);
+    if (!stream->write(data, len)) {
+        png_error(png_ptr, "sk_write_fn cannot write to stream");
+    }
+}
+
+class SkPngEncoderMgr final : SkNoncopyable {
+public:
+
+    /*
+     * Create the decode manager
+     * Does not take ownership of stream
+     */
+    static std::unique_ptr<SkPngEncoderMgr> Make(SkWStream* stream);
+
+    bool setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options);
+    bool setPalette(const SkImageInfo& srcInfo, SkColorTable* colorTable,
+                    SkTransferFunctionBehavior);
+    bool setColorSpace(const SkImageInfo& info);
+    bool writeInfo(const SkImageInfo& srcInfo);
+    void chooseProc(const SkImageInfo& srcInfo, SkTransferFunctionBehavior unpremulBehavior);
+
+    png_structp pngPtr() { return fPngPtr; }
+    png_infop infoPtr() { return fInfoPtr; }
+    int pngBytesPerPixel() const { return fPngBytesPerPixel; }
+    transform_scanline_proc proc() const { return fProc; }
+
+    ~SkPngEncoderMgr() {
+        png_destroy_write_struct(&fPngPtr, &fInfoPtr);
+    }
+
+private:
+
+    SkPngEncoderMgr(png_structp pngPtr, png_infop infoPtr)
+        : fPngPtr(pngPtr)
+        , fInfoPtr(infoPtr)
+    {}
+
+    png_structp             fPngPtr;
+    png_infop               fInfoPtr;
+    int                     fPngBytesPerPixel;
+    transform_scanline_proc fProc;
+};
+
+std::unique_ptr<SkPngEncoderMgr> SkPngEncoderMgr::Make(SkWStream* stream) {
+    png_structp pngPtr =
+            png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
+    if (!pngPtr) {
+        return nullptr;
+    }
+
+    png_infop infoPtr = png_create_info_struct(pngPtr);
+    if (!infoPtr) {
+        png_destroy_write_struct(&pngPtr, nullptr);
+        return nullptr;
+    }
+
+    png_set_write_fn(pngPtr, (void*)stream, sk_write_fn, nullptr);
+    return std::unique_ptr<SkPngEncoderMgr>(new SkPngEncoderMgr(pngPtr, infoPtr));
+}
+
+bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) {
+    if (setjmp(png_jmpbuf(fPngPtr))) {
+        return false;
+    }
+
+    int pngColorType;
+    png_color_8 sigBit;
+    int bitDepth = 8;
+    switch (srcInfo.colorType()) {
+        case kRGBA_F16_SkColorType:
+            SkASSERT(srcInfo.colorSpace() && srcInfo.colorSpace()->gammaIsLinear());
+            sigBit.red = 16;
+            sigBit.green = 16;
+            sigBit.blue = 16;
+            sigBit.alpha = 16;
+            bitDepth = 16;
+            pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+            fPngBytesPerPixel = 8;
+            break;
+        case kIndex_8_SkColorType:
+            sigBit.red = 8;
+            sigBit.green = 8;
+            sigBit.blue = 8;
+            sigBit.alpha = 8;
+            pngColorType = PNG_COLOR_TYPE_PALETTE;
+            fPngBytesPerPixel = 1;
+            break;
+        case kGray_8_SkColorType:
+            sigBit.gray = 8;
+            pngColorType = PNG_COLOR_TYPE_GRAY;
+            fPngBytesPerPixel = 1;
+            SkASSERT(srcInfo.isOpaque());
+            break;
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            sigBit.red = 8;
+            sigBit.green = 8;
+            sigBit.blue = 8;
+            sigBit.alpha = 8;
+            pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+            fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
+            break;
+        case kARGB_4444_SkColorType:
+            if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
+                return false;
+            }
+
+            sigBit.red = 4;
+            sigBit.green = 4;
+            sigBit.blue = 4;
+            sigBit.alpha = 4;
+            pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+            fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
+            break;
+        case kRGB_565_SkColorType:
+            sigBit.red = 5;
+            sigBit.green = 6;
+            sigBit.blue = 5;
+            pngColorType = PNG_COLOR_TYPE_RGB;
+            fPngBytesPerPixel = 3;
+            SkASSERT(srcInfo.isOpaque());
+            break;
+        default:
+            return false;
+    }
+
+    png_set_IHDR(fPngPtr, fInfoPtr, srcInfo.width(), srcInfo.height(),
+                 bitDepth, pngColorType,
+                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+                 PNG_FILTER_TYPE_BASE);
+    png_set_sBIT(fPngPtr, fInfoPtr, &sigBit);
+
+    int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll;
+    SkASSERT(filters == (int)options.fFilterFlags);
+    png_set_filter(fPngPtr, PNG_FILTER_TYPE_BASE, filters);
+
+    int zlibLevel = SkTMin(SkTMax(0, options.fZLibLevel), 9);
+    SkASSERT(zlibLevel == options.fZLibLevel);
+    png_set_compression_level(fPngPtr, zlibLevel);
+    return true;
+}
+
+static transform_scanline_proc choose_proc(const SkImageInfo& info,
+                                           SkTransferFunctionBehavior unpremulBehavior) {
+    const bool isSRGBTransferFn =
+            (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
+    switch (info.colorType()) {
+        case kRGBA_8888_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_RGBX;
+                case kUnpremul_SkAlphaType:
+                    return transform_scanline_memcpy;
+                case kPremul_SkAlphaType:
+                    return isSRGBTransferFn ? transform_scanline_srgbA :
+                                              transform_scanline_rgbA;
+                default:
+                    SkASSERT(false);
+                    return nullptr;
+            }
+        case kBGRA_8888_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_BGRX;
+                case kUnpremul_SkAlphaType:
+                    return transform_scanline_BGRA;
+                case kPremul_SkAlphaType:
+                    return isSRGBTransferFn ? transform_scanline_sbgrA :
+                                              transform_scanline_bgrA;
+                default:
+                    SkASSERT(false);
+                    return nullptr;
+            }
+        case kRGB_565_SkColorType:
+            return transform_scanline_565;
+        case kARGB_4444_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_444;
+                case kPremul_SkAlphaType:
+                    // 4444 is assumed to be legacy premul.
+                    return transform_scanline_4444;
+                default:
+                    SkASSERT(false);
+                    return nullptr;
+            }
+        case kIndex_8_SkColorType:
+        case kGray_8_SkColorType:
+            return transform_scanline_memcpy;
+        case kRGBA_F16_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                case kUnpremul_SkAlphaType:
+                    return transform_scanline_F16;
+                case kPremul_SkAlphaType:
+                    return transform_scanline_F16_premul;
+                default:
+                    SkASSERT(false);
+                    return nullptr;
+            }
+        default:
+            SkASSERT(false);
+            return nullptr;
+    }
+}
+
+/*
+ * Pack palette[] with the corresponding colors, and if the image has alpha, also
+ * pack trans[] and return the number of alphas[] entries written. If the image is
+ * opaque, the return value will always be 0.
+ */
+static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
+                               png_byte* SK_RESTRICT alphas, const SkImageInfo& info,
+                               SkTransferFunctionBehavior unpremulBehavior) {
+    const SkPMColor* colors = ctable->readColors();
+    const int count = ctable->count();
+    SkPMColor storage[256];
+    if (kPremul_SkAlphaType == info.alphaType()) {
+        // Unpremultiply the colors.
+        const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType);
+        transform_scanline_proc proc = choose_proc(rgbaInfo, unpremulBehavior);
+        proc((char*) storage, (const char*) colors, ctable->count(), 4, nullptr);
+        colors = storage;
+    }
+
+    int numWithAlpha = 0;
+    if (kOpaque_SkAlphaType != info.alphaType()) {
+        // PNG requires that all non-opaque colors come first in the palette.  Write these first.
+        for (int i = 0; i < count; i++) {
+            uint8_t alpha = SkGetPackedA32(colors[i]);
+            if (0xFF != alpha) {
+                alphas[numWithAlpha] = alpha;
+                palette[numWithAlpha].red   = SkGetPackedR32(colors[i]);
+                palette[numWithAlpha].green = SkGetPackedG32(colors[i]);
+                palette[numWithAlpha].blue  = SkGetPackedB32(colors[i]);
+                numWithAlpha++;
+            }
+        }
+    }
+
+    if (0 == numWithAlpha) {
+        // All of the entries are opaque.
+        for (int i = 0; i < count; i++) {
+            SkPMColor c = *colors++;
+            palette[i].red   = SkGetPackedR32(c);
+            palette[i].green = SkGetPackedG32(c);
+            palette[i].blue  = SkGetPackedB32(c);
+        }
+    } else {
+        // We have already written the non-opaque colors.  Now just write the opaque colors.
+        int currIndex = numWithAlpha;
+        int i = 0;
+        while (currIndex != count) {
+            uint8_t alpha = SkGetPackedA32(colors[i]);
+            if (0xFF == alpha) {
+                palette[currIndex].red   = SkGetPackedR32(colors[i]);
+                palette[currIndex].green = SkGetPackedG32(colors[i]);
+                palette[currIndex].blue  = SkGetPackedB32(colors[i]);
+                currIndex++;
+            }
+
+            i++;
+        }
+    }
+
+    return numWithAlpha;
+}
+
+bool SkPngEncoderMgr::setPalette(const SkImageInfo& srcInfo, SkColorTable* colorTable,
+                                 SkTransferFunctionBehavior unpremulBehavior) {
+    if (setjmp(png_jmpbuf(fPngPtr))) {
+        return false;
+    }
+
+    png_color paletteColors[256];
+    png_byte trans[256];
+    if (kIndex_8_SkColorType == srcInfo.colorType()) {
+        if (!colorTable || colorTable->count() <= 0) {
+            return false;
+        }
+
+        int numTrans = pack_palette(colorTable, paletteColors, trans, srcInfo, unpremulBehavior);
+        png_set_PLTE(fPngPtr, fInfoPtr, paletteColors, colorTable->count());
+        if (numTrans > 0) {
+            png_set_tRNS(fPngPtr, fInfoPtr, trans, numTrans, nullptr);
+        }
+    }
+
+    return true;
+}
+
+static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) {
+    sk_sp<SkData> icc = icc_from_color_space(info);
+    if (!icc) {
+        return;
+    }
+
+#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
+    const char* name = "Skia";
+    png_const_bytep iccPtr = icc->bytes();
+#else
+    SkString str("Skia");
+    char* name = str.writable_str();
+    png_charp iccPtr = (png_charp) icc->writable_data();
+#endif
+    png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
+}
+
+bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info) {
+    if (setjmp(png_jmpbuf(fPngPtr))) {
+        return false;
+    }
+
+    if (info.colorSpace() && info.colorSpace()->isSRGB()) {
+        png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
+    } else {
+        set_icc(fPngPtr, fInfoPtr, info);
+    }
+
+    return true;
+}
+
+bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) {
+    if (setjmp(png_jmpbuf(fPngPtr))) {
+        return false;
+    }
+
+    png_write_info(fPngPtr, fInfoPtr);
+    if (kRGBA_F16_SkColorType == srcInfo.colorType() &&
+        kOpaque_SkAlphaType == srcInfo.alphaType())
+    {
+        // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
+        // to skip the alpha channel.
+        png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER);
+    }
+
+    return true;
+}
+
+void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo,
+                                 SkTransferFunctionBehavior unpremulBehavior) {
+    fProc = choose_proc(srcInfo, unpremulBehavior);
+}
+
+std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream* dst, const SkPixmap& src,
+                                              const Options& options) {
+    if (!SkPixmapIsValid(src, options.fUnpremulBehavior)) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SkPngEncoderMgr> encoderMgr = SkPngEncoderMgr::Make(dst);
+    if (!encoderMgr) {
+        return nullptr;
+    }
+
+    if (!encoderMgr->setHeader(src.info(), options)) {
+        return nullptr;
+    }
+
+    if (!encoderMgr->setPalette(src.info(), src.ctable(), options.fUnpremulBehavior)) {
+        return nullptr;
+    }
+
+    if (!encoderMgr->setColorSpace(src.info())) {
+        return nullptr;
+    }
+
+    if (!encoderMgr->writeInfo(src.info())) {
+        return nullptr;
+    }
+
+    encoderMgr->chooseProc(src.info(), options.fUnpremulBehavior);
+
+    return std::unique_ptr<SkPngEncoder>(new SkPngEncoder(std::move(encoderMgr), src));
+}
+
+SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
+    : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
+    , fEncoderMgr(std::move(encoderMgr))
+{}
+
+SkPngEncoder::~SkPngEncoder() {}
+
+bool SkPngEncoder::onEncodeRows(int numRows) {
+    if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) {
+        return false;
+    }
+
+    const void* srcRow = fSrc.addr(0, fCurrRow);
+    for (int y = 0; y < numRows; y++) {
+        fEncoderMgr->proc()((char*) fStorage.get(), (const char*) srcRow, fSrc.width(),
+                            SkColorTypeBytesPerPixel(fSrc.colorType()), nullptr);
+
+        png_bytep rowPtr = (png_bytep) fStorage.get();
+        png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1);
+        srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
+    }
+
+    fCurrRow += numRows;
+    if (fCurrRow == fSrc.height()) {
+        png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr());
+    }
+
+    return true;
+}
+
+bool SkPngEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
+    auto encoder = SkPngEncoder::Make(dst, src, options);
+    return encoder.get() && encoder->encodeRows(src.height());
+}
+
+#endif
diff --git a/src/images/SkWEBPImageEncoder.cpp b/src/images/SkWEBPImageEncoder.cpp
deleted file mode 100644
index f77e17f..0000000
--- a/src/images/SkWEBPImageEncoder.cpp
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SkImageEncoderPriv.h"
-
-#ifdef SK_HAS_WEBP_LIBRARY
-
-#include "SkBitmap.h"
-#include "SkColorPriv.h"
-#include "SkImageEncoderFns.h"
-#include "SkStream.h"
-#include "SkTemplates.h"
-#include "SkUnPreMultiply.h"
-#include "SkUtils.h"
-
-// A WebP encoder only, on top of (subset of) libwebp
-// For more information on WebP image format, and libwebp library, see:
-//   http://code.google.com/speed/webp/
-//   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
-//   http://review.webmproject.org/gitweb?p=libwebp.git
-
-#include <stdio.h>
-extern "C" {
-// If moving libwebp out of skia source tree, path for webp headers must be
-// updated accordingly. Here, we enforce using local copy in webp sub-directory.
-#include "webp/encode.h"
-#include "webp/mux.h"
-}
-
-static transform_scanline_proc choose_proc(const SkImageInfo& info,
-                                           SkTransferFunctionBehavior unpremulBehavior) {
-    const bool isSRGBTransferFn =
-            (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
-    switch (info.colorType()) {
-        case kRGBA_8888_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_RGBX;
-                case kUnpremul_SkAlphaType:
-                    return transform_scanline_memcpy;
-                case kPremul_SkAlphaType:
-                    return isSRGBTransferFn ? transform_scanline_srgbA :
-                                              transform_scanline_rgbA;
-                default:
-                    return nullptr;
-            }
-        case kBGRA_8888_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_BGRX;
-                case kUnpremul_SkAlphaType:
-                    return transform_scanline_BGRA;
-                case kPremul_SkAlphaType:
-                    return isSRGBTransferFn ? transform_scanline_sbgrA :
-                                              transform_scanline_bgrA;
-                default:
-                    return nullptr;
-            }
-        case kRGB_565_SkColorType:
-            if (!info.isOpaque()) {
-                return nullptr;
-            }
-
-            return transform_scanline_565;
-        case kARGB_4444_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_444;
-                case kPremul_SkAlphaType:
-                    return transform_scanline_4444;
-                default:
-                    return nullptr;
-            }
-        case kIndex_8_SkColorType:
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                    return transform_scanline_index8_opaque;
-                case kUnpremul_SkAlphaType:
-                case kPremul_SkAlphaType:
-                    // If the color table is premultiplied, we'll fix it before calling the
-                    // scanline proc.
-                    return transform_scanline_index8_unpremul;
-                default:
-                    return nullptr;
-            }
-        case kGray_8_SkColorType:
-            return transform_scanline_gray;
-        case kRGBA_F16_SkColorType:
-            if (!info.colorSpace() || !info.colorSpace()->gammaIsLinear()) {
-                return nullptr;
-            }
-
-            switch (info.alphaType()) {
-                case kOpaque_SkAlphaType:
-                case kUnpremul_SkAlphaType:
-                    return transform_scanline_F16_to_8888;
-                case kPremul_SkAlphaType:
-                    return transform_scanline_F16_premul_to_8888;
-                default:
-                    return nullptr;
-            }
-        default:
-            return nullptr;
-    }
-}
-
-static int stream_writer(const uint8_t* data, size_t data_size,
-                         const WebPPicture* const picture) {
-  SkWStream* const stream = (SkWStream*)picture->custom_ptr;
-  return stream->write(data, data_size) ? 1 : 0;
-}
-
-static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts,
-                      int quality) {
-    if (SkTransferFunctionBehavior::kRespect == opts.fUnpremulBehavior) {
-        if (!pixmap.colorSpace() || (!pixmap.colorSpace()->gammaCloseToSRGB() &&
-                                     !pixmap.colorSpace()->gammaIsLinear())) {
-            return false;
-        }
-    }
-
-    const transform_scanline_proc proc = choose_proc(pixmap.info(), opts.fUnpremulBehavior);
-    if (!proc) {
-        return false;
-    }
-
-    int bpp;
-    if (kRGBA_F16_SkColorType == pixmap.colorType()) {
-        bpp = 4;
-    } else {
-        bpp = pixmap.isOpaque() ? 3 : 4;
-    }
-
-    if (nullptr == pixmap.addr()) {
-        return false;
-    }
-
-    const SkPMColor* colors = nullptr;
-    SkPMColor storage[256];
-    if (kIndex_8_SkColorType == pixmap.colorType()) {
-        if (!pixmap.ctable()) {
-            return false;
-        }
-
-        colors = pixmap.ctable()->readColors();
-        if (kPremul_SkAlphaType == pixmap.alphaType()) {
-            // Unpremultiply the colors.
-            const SkImageInfo rgbaInfo = pixmap.info().makeColorType(kRGBA_8888_SkColorType);
-            transform_scanline_proc proc = choose_proc(rgbaInfo, opts.fUnpremulBehavior);
-            proc((char*) storage, (const char*) colors, pixmap.ctable()->count(), 4, nullptr);
-            colors = storage;
-        }
-    }
-
-    WebPConfig webp_config;
-    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) {
-        return false;
-    }
-
-    WebPPicture pic;
-    WebPPictureInit(&pic);
-    SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
-    pic.width = pixmap.width();
-    pic.height = pixmap.height();
-    pic.writer = stream_writer;
-
-    // If there is no need to embed an ICC profile, we write directly to the input stream.
-    // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk.  libwebp
-    // forces us to have an encoded image before we can add a profile.
-    sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
-    SkDynamicMemoryWStream tmp;
-    pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
-
-    const uint8_t* src = (uint8_t*)pixmap.addr();
-    const int rgbStride = pic.width * bpp;
-    const size_t rowBytes = pixmap.rowBytes();
-
-    // Import (for each scanline) the bit-map image (in appropriate color-space)
-    // to RGB color space.
-    std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]);
-    for (int y = 0; y < pic.height; ++y) {
-        proc((char*) &rgb[y * rgbStride], (const char*) &src[y * rowBytes], pic.width, bpp, colors);
-    }
-
-    auto importProc = WebPPictureImportRGB;
-    if (3 != bpp) {
-        if (pixmap.isOpaque()) {
-            importProc = WebPPictureImportRGBX;
-        } else {
-            importProc = WebPPictureImportRGBA;
-        }
-    }
-
-    if (!importProc(&pic, &rgb[0], rgbStride)) {
-        return false;
-    }
-
-    if (!WebPEncode(&webp_config, &pic)) {
-        return false;
-    }
-
-    if (icc) {
-        sk_sp<SkData> encodedData = tmp.detachAsData();
-        WebPData encoded = { encodedData->bytes(), encodedData->size() };
-        WebPData iccChunk = { icc->bytes(), icc->size() };
-
-        SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
-        if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
-            return false;
-        }
-
-        if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
-            return false;
-        }
-
-        WebPData assembled;
-        if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
-            return false;
-        }
-
-        stream->write(assembled.bytes, assembled.size);
-        WebPDataClear(&assembled);
-    }
-
-    return true;
-}
-
-bool SkEncodeImageAsWEBP(SkWStream* stream, const SkPixmap& src, int quality) {
-    return do_encode(stream, src, SkEncodeOptions(), quality);
-}
-
-bool SkEncodeImageAsWEBP(SkWStream* stream, const SkPixmap& src, const SkEncodeOptions& opts) {
-    return do_encode(stream, src, opts, 100);
-}
-
-#endif
diff --git a/src/images/SkWebpEncoder.cpp b/src/images/SkWebpEncoder.cpp
new file mode 100644
index 0000000..296d4f4
--- /dev/null
+++ b/src/images/SkWebpEncoder.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkImageEncoderPriv.h"
+
+#ifdef SK_HAS_WEBP_LIBRARY
+
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkColorSpace_Base.h"
+#include "SkImageEncoderFns.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkWebpEncoder.h"
+
+// A WebP encoder only, on top of (subset of) libwebp
+// For more information on WebP image format, and libwebp library, see:
+//   http://code.google.com/speed/webp/
+//   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
+//   http://review.webmproject.org/gitweb?p=libwebp.git
+
+#include <stdio.h>
+extern "C" {
+// If moving libwebp out of skia source tree, path for webp headers must be
+// updated accordingly. Here, we enforce using local copy in webp sub-directory.
+#include "webp/encode.h"
+#include "webp/mux.h"
+}
+
+static transform_scanline_proc choose_proc(const SkImageInfo& info,
+                                           SkTransferFunctionBehavior unpremulBehavior) {
+    const bool isSRGBTransferFn =
+            (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
+    switch (info.colorType()) {
+        case kRGBA_8888_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_RGBX;
+                case kUnpremul_SkAlphaType:
+                    return transform_scanline_memcpy;
+                case kPremul_SkAlphaType:
+                    return isSRGBTransferFn ? transform_scanline_srgbA :
+                                              transform_scanline_rgbA;
+                default:
+                    return nullptr;
+            }
+        case kBGRA_8888_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_BGRX;
+                case kUnpremul_SkAlphaType:
+                    return transform_scanline_BGRA;
+                case kPremul_SkAlphaType:
+                    return isSRGBTransferFn ? transform_scanline_sbgrA :
+                                              transform_scanline_bgrA;
+                default:
+                    return nullptr;
+            }
+        case kRGB_565_SkColorType:
+            if (!info.isOpaque()) {
+                return nullptr;
+            }
+
+            return transform_scanline_565;
+        case kARGB_4444_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_444;
+                case kPremul_SkAlphaType:
+                    return transform_scanline_4444;
+                default:
+                    return nullptr;
+            }
+        case kIndex_8_SkColorType:
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return transform_scanline_index8_opaque;
+                case kUnpremul_SkAlphaType:
+                case kPremul_SkAlphaType:
+                    // If the color table is premultiplied, we'll fix it before calling the
+                    // scanline proc.
+                    return transform_scanline_index8_unpremul;
+                default:
+                    return nullptr;
+            }
+        case kGray_8_SkColorType:
+            return transform_scanline_gray;
+        case kRGBA_F16_SkColorType:
+            if (!info.colorSpace() || !info.colorSpace()->gammaIsLinear()) {
+                return nullptr;
+            }
+
+            switch (info.alphaType()) {
+                case kOpaque_SkAlphaType:
+                case kUnpremul_SkAlphaType:
+                    return transform_scanline_F16_to_8888;
+                case kPremul_SkAlphaType:
+                    return transform_scanline_F16_premul_to_8888;
+                default:
+                    return nullptr;
+            }
+        default:
+            return nullptr;
+    }
+}
+
+static int stream_writer(const uint8_t* data, size_t data_size,
+                         const WebPPicture* const picture) {
+  SkWStream* const stream = (SkWStream*)picture->custom_ptr;
+  return stream->write(data, data_size) ? 1 : 0;
+}
+
+bool SkWebpEncoder::Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) {
+    if (!SkPixmapIsValid(pixmap, opts.fUnpremulBehavior)) {
+        return false;
+    }
+
+    const transform_scanline_proc proc = choose_proc(pixmap.info(), opts.fUnpremulBehavior);
+    if (!proc) {
+        return false;
+    }
+
+    int bpp;
+    if (kRGBA_F16_SkColorType == pixmap.colorType()) {
+        bpp = 4;
+    } else {
+        bpp = pixmap.isOpaque() ? 3 : 4;
+    }
+
+    if (nullptr == pixmap.addr()) {
+        return false;
+    }
+
+    const SkPMColor* colors = nullptr;
+    SkPMColor storage[256];
+    if (kIndex_8_SkColorType == pixmap.colorType()) {
+        if (!pixmap.ctable()) {
+            return false;
+        }
+
+        colors = pixmap.ctable()->readColors();
+        if (kPremul_SkAlphaType == pixmap.alphaType()) {
+            // Unpremultiply the colors.
+            const SkImageInfo rgbaInfo = pixmap.info().makeColorType(kRGBA_8888_SkColorType);
+            transform_scanline_proc proc = choose_proc(rgbaInfo, opts.fUnpremulBehavior);
+            proc((char*) storage, (const char*) colors, pixmap.ctable()->count(), 4, nullptr);
+            colors = storage;
+        }
+    }
+
+    WebPConfig webp_config;
+    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
+        return false;
+    }
+
+    WebPPicture pic;
+    WebPPictureInit(&pic);
+    SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
+    pic.width = pixmap.width();
+    pic.height = pixmap.height();
+    pic.writer = stream_writer;
+
+    // Set compression, method, and pixel format.
+    // libwebp recommends using BGRA for lossless and YUV for lossy.
+    // The choices of |webp_config.method| currently just match Chrome's defaults.  We
+    // could potentially expose this decision to the client.
+    if (Compression::kLossy == opts.fCompression) {
+        webp_config.lossless = 0;
+#ifndef SK_WEBP_ENCODER_USE_DEFAULT_METHOD
+        webp_config.method = 3;
+#endif
+        pic.use_argb = 0;
+    } else {
+        webp_config.lossless = 1;
+        webp_config.method = 0;
+        pic.use_argb = 1;
+    }
+
+    // If there is no need to embed an ICC profile, we write directly to the input stream.
+    // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk.  libwebp
+    // forces us to have an encoded image before we can add a profile.
+    sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
+    SkDynamicMemoryWStream tmp;
+    pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
+
+    const uint8_t* src = (uint8_t*)pixmap.addr();
+    const int rgbStride = pic.width * bpp;
+    const size_t rowBytes = pixmap.rowBytes();
+
+    // Import (for each scanline) the bit-map image (in appropriate color-space)
+    // to RGB color space.
+    std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]);
+    for (int y = 0; y < pic.height; ++y) {
+        proc((char*) &rgb[y * rgbStride], (const char*) &src[y * rowBytes], pic.width, bpp, colors);
+    }
+
+    auto importProc = WebPPictureImportRGB;
+    if (3 != bpp) {
+        if (pixmap.isOpaque()) {
+            importProc = WebPPictureImportRGBX;
+        } else {
+            importProc = WebPPictureImportRGBA;
+        }
+    }
+
+    if (!importProc(&pic, &rgb[0], rgbStride)) {
+        return false;
+    }
+
+    if (!WebPEncode(&webp_config, &pic)) {
+        return false;
+    }
+
+    if (icc) {
+        sk_sp<SkData> encodedData = tmp.detachAsData();
+        WebPData encoded = { encodedData->bytes(), encodedData->size() };
+        WebPData iccChunk = { icc->bytes(), icc->size() };
+
+        SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
+        if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
+            return false;
+        }
+
+        if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
+            return false;
+        }
+
+        WebPData assembled;
+        if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
+            return false;
+        }
+
+        stream->write(assembled.bytes, assembled.size);
+        WebPDataClear(&assembled);
+    }
+
+    return true;
+}
+
+#endif
diff --git a/src/jumper/SkJumper.cpp b/src/jumper/SkJumper.cpp
index 85f1231..8224800 100644
--- a/src/jumper/SkJumper.cpp
+++ b/src/jumper/SkJumper.cpp
@@ -8,25 +8,10 @@
 #include "SkColorPriv.h"
 #include "SkCpu.h"
 #include "SkJumper.h"
+#include "SkOnce.h"
 #include "SkRasterPipeline.h"
 #include "SkTemplates.h"
 
-// A debugging mode that helps prioritize porting stages to SkJumper.
-#if 0
-    #include "SkOnce.h"
-    #include <atomic>
-
-    #define M(st) {0},
-    static std::atomic<int> gMissing[] = { SK_RASTER_PIPELINE_STAGES(M) };
-    #undef M
-
-    #define M(st) #st,
-    static const char* gNames[] = { SK_RASTER_PIPELINE_STAGES(M) };
-    #undef M
-
-    #define WHATS_NEXT
-#endif
-
 // We'll use __has_feature(memory_sanitizer) to detect MSAN.
 // SkJumper_generated.S is not compiled with MSAN, so MSAN would yell really loud.
 #if !defined(__has_feature)
@@ -37,62 +22,86 @@
 // It's fine to rearrange and add new ones if you update SkJumper_constants.
 using K = const SkJumper_constants;
 static K kConstants = {
-    {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f},
+    {0,1,2,3,4,5,6,7},
+    {0,1,2,3,4,5,6,7},
 };
 
-#define STAGES(M)         \
-    M(seed_shader)        \
-    M(constant_color)     \
-    M(clear)              \
-    M(plus_)              \
-    M(srcover)            \
-    M(dstover)            \
-    M(clamp_0)            \
-    M(clamp_1)            \
-    M(clamp_a)            \
-    M(set_rgb)            \
-    M(swap_rb)            \
-    M(swap)               \
-    M(move_src_dst)       \
-    M(move_dst_src)       \
-    M(premul)             \
-    M(unpremul)           \
-    M(from_srgb)          \
-    M(to_srgb)            \
-    M(scale_1_float)      \
-    M(scale_u8)           \
-    M(lerp_1_float)       \
-    M(lerp_u8)            \
-    M(lerp_565)           \
-    M(load_tables)        \
-    M(load_a8)            \
-    M(store_a8)           \
-    M(load_565)           \
-    M(store_565)          \
-    M(load_8888)          \
-    M(store_8888)         \
-    M(load_f16)           \
-    M(store_f16)          \
-    M(store_f32)          \
-    M(luminance_to_alpha) \
-    M(matrix_2x3)         \
-    M(matrix_3x4)         \
-    M(matrix_4x5)         \
-    M(matrix_perspective) \
-    M(clamp_x)            \
-    M(clamp_y)            \
-    M(repeat_x)           \
-    M(repeat_y)           \
-    M(mirror_x)           \
-    M(mirror_y)           \
-    M(linear_gradient_2stops)
+#define M(st) +1
+static const int kNumStages = SK_RASTER_PIPELINE_STAGES(M);
+#undef M
+
+#if !__has_feature(memory_sanitizer) && (defined(__x86_64__) || defined(_M_X64))
+    #if 0
+        #include <atomic>
+
+        #define M(st) #st,
+        static const char* kStageNames[] = { SK_RASTER_PIPELINE_STAGES(M) };
+        #undef M
+
+        static std::atomic<int> gMissingStageCounters[kNumStages];
+
+        static void log_missing(SkRasterPipeline::StockStage st) {
+            static SkOnce once;
+            once([] { atexit([] {
+                for (int i = 0; i < kNumStages; i++) {
+                    if (int count = gMissingStageCounters[i].load()) {
+                        SkDebugf("%7d\t%s\n", count, kStageNames[i]);
+                    }
+                }
+            }); });
+
+            gMissingStageCounters[st]++;
+        }
+    #else
+        static void log_missing(SkRasterPipeline::StockStage) {}
+    #endif
+#endif
 
 // We can't express the real types of most stage functions portably, so we use a stand-in.
 // We'll only ever call start_pipeline(), which then chains into the rest for us.
 using StageFn = void(void);
+using StartPipelineFn = void(size_t,size_t,size_t,void**,K*);
 
-// TODO: maybe don't need this wrapper anymore.
-#define ASM(name, suffix) sk_##name##_##suffix
+// Some platforms expect C "name" maps to asm "_name", others to "name".
+#if defined(__APPLE__)
+    #define ASM(name, suffix)  sk_##name##_##suffix
+#else
+    #define ASM(name, suffix) _sk_##name##_##suffix
+#endif
+
+// Some stages have low-precision (~15 bit) versions from SkJumper_stages_lowp.cpp.
+#define LOWP_STAGES(M)   \
+    M(constant_color)    \
+    M(set_rgb)           \
+    M(premul)            \
+    M(load_8888)         \
+    M(store_8888)        \
+    M(load_a8)           \
+    M(store_a8)          \
+    M(load_g8)           \
+    M(srcover_rgba_8888) \
+    M(lerp_1_float)      \
+    M(lerp_u8)           \
+    M(scale_1_float)     \
+    M(scale_u8)          \
+    M(swap_rb)           \
+    M(swap)              \
+    M(move_src_dst)      \
+    M(move_dst_src)      \
+    M(clear)             \
+    M(srcatop)           \
+    M(dstatop)           \
+    M(srcin)             \
+    M(dstin)             \
+    M(srcout)            \
+    M(dstout)            \
+    M(srcover)           \
+    M(dstover)           \
+    M(modulate)          \
+    M(multiply)          \
+    M(plus_)             \
+    M(screen)            \
+    M(xor_)
 
 extern "C" {
 
@@ -100,210 +109,202 @@
     // We'll just run portable code.
 
 #elif defined(__aarch64__)
-    size_t ASM(start_pipeline,aarch64)(size_t, void**, K*, size_t);
+    StartPipelineFn ASM(start_pipeline,aarch64);
     StageFn ASM(just_return,aarch64);
     #define M(st) StageFn ASM(st,aarch64);
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
     #undef M
 
 #elif defined(__arm__)
-    size_t ASM(start_pipeline,vfp4)(size_t, void**, K*, size_t);
+    StartPipelineFn ASM(start_pipeline,vfp4);
     StageFn ASM(just_return,vfp4);
     #define M(st) StageFn ASM(st,vfp4);
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
     #undef M
 
 #elif defined(__x86_64__) || defined(_M_X64)
-    size_t ASM(start_pipeline,hsw  )(size_t, void**, K*, size_t);
-    size_t ASM(start_pipeline,avx  )(size_t, void**, K*, size_t);
-    size_t ASM(start_pipeline,sse41)(size_t, void**, K*, size_t);
-    size_t ASM(start_pipeline,sse2 )(size_t, void**, K*, size_t);
+    StartPipelineFn ASM(start_pipeline,hsw       ),
+                    ASM(start_pipeline,avx       ),
+                    ASM(start_pipeline,sse41     ),
+                    ASM(start_pipeline,sse2      ),
+                    ASM(start_pipeline,ssse3_lowp);
 
     StageFn ASM(just_return,hsw),
             ASM(just_return,avx),
             ASM(just_return,sse41),
-            ASM(just_return,sse2);
+            ASM(just_return,sse2),
+            ASM(just_return,ssse3_lowp);
 
     #define M(st) StageFn ASM(st,hsw);
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
     #undef M
     #define M(st) StageFn ASM(st,avx);
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
     #undef M
     #define M(st) StageFn ASM(st,sse41);
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
     #undef M
     #define M(st) StageFn ASM(st,sse2);
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
+    #undef M
+
+    #define M(st) StageFn ASM(st,ssse3_lowp);
+        LOWP_STAGES(M)
     #undef M
 #endif
 
     // Portable, single-pixel stages.
-    size_t sk_start_pipeline(size_t, void**, K*, size_t);
+    StartPipelineFn sk_start_pipeline;
     StageFn sk_just_return;
     #define M(st) StageFn sk_##st;
-        STAGES(M)
+        SK_RASTER_PIPELINE_STAGES(M)
     #undef M
 }
 
-// Translate SkRasterPipeline's StockStage enum to StageFn function pointers.
+// Engines comprise everything we need to run SkRasterPipelines.
+struct SkJumper_Engine {
+    StageFn*         stages[kNumStages];
+    StartPipelineFn* start_pipeline;
+    StageFn*         just_return;
+};
 
+// We'll default to this portable engine, but try to choose a better one at runtime.
+static const SkJumper_Engine kPortable = {
+#define M(stage) sk_##stage,
+    { SK_RASTER_PIPELINE_STAGES(M) },
+#undef M
+    sk_start_pipeline,
+    sk_just_return,
+};
+static SkJumper_Engine gEngine = kPortable;
+static SkOnce gChooseEngineOnce;
+
+static SkJumper_Engine choose_engine() {
 #if __has_feature(memory_sanitizer)
     // We'll just run portable code.
 
 #elif defined(__aarch64__)
-    static StageFn* lookup_aarch64(SkRasterPipeline::StockStage st) {
-        switch (st) {
-            default: return nullptr;
-        #define M(st) case SkRasterPipeline::st: return ASM(st,aarch64);
-            STAGES(M)
-        #undef M
-        }
-    }
-
-#elif defined(__arm__)
-    static StageFn* lookup_vfp4(SkRasterPipeline::StockStage st) {
-        switch (st) {
-            default: return nullptr;
-        #define M(st) case SkRasterPipeline::st: return ASM(st,vfp4);
-            STAGES(M)
-        #undef M
-        }
-    }
-
-#elif defined(__x86_64__) || defined(_M_X64)
-    static StageFn* lookup_hsw(SkRasterPipeline::StockStage st) {
-        switch (st) {
-            default:
-        #ifdef WHATS_NEXT
-                gMissing[st]++;
-        #endif
-                return nullptr;
-        #define M(st) case SkRasterPipeline::st: return ASM(st,hsw);
-            STAGES(M)
-        #undef M
-        }
-    }
-    static StageFn* lookup_avx(SkRasterPipeline::StockStage st) {
-        switch (st) {
-            default:
-        #ifdef WHATS_NEXT
-                gMissing[st]++;
-        #endif
-                return nullptr;
-        #define M(st) case SkRasterPipeline::st: return ASM(st,avx);
-            STAGES(M)
-        #undef M
-        }
-    }
-    static StageFn* lookup_sse41(SkRasterPipeline::StockStage st) {
-        switch (st) {
-            default:
-        #ifdef WHATS_NEXT
-                gMissing[st]++;
-        #endif
-                return nullptr;
-        #define M(st) case SkRasterPipeline::st: return ASM(st,sse41);
-            STAGES(M)
-        #undef M
-        }
-    }
-    static StageFn* lookup_sse2(SkRasterPipeline::StockStage st) {
-        switch (st) {
-            default: return nullptr;
-        #define M(st) case SkRasterPipeline::st: return ASM(st,sse2);
-            STAGES(M)
-        #undef M
-        }
-    }
-#endif
-
-static StageFn* lookup_portable(SkRasterPipeline::StockStage st) {
-    switch (st) {
-        default: return nullptr;
-    #define M(st) case SkRasterPipeline::st: return sk_##st;
-        STAGES(M)
+    return {
+    #define M(stage) ASM(stage, aarch64),
+        { SK_RASTER_PIPELINE_STAGES(M) },
+        M(start_pipeline) M(just_return)
     #undef M
-    }
-}
-
-bool SkRasterPipeline::run_with_jumper(size_t x, size_t n) const {
-#ifdef WHATS_NEXT
-    static SkOnce once;
-    once([] {
-        atexit([] {
-            for (int i = 0; i < (int)SK_ARRAY_COUNT(gMissing); i++) {
-                SkDebugf("%10d %s\n", gMissing[i].load(), gNames[i]);
-            }
-        });
-    });
-#endif
-
-    SkAutoSTMalloc<64, void*> program(2*fStages.size() + 1);
-    const size_t limit = x+n;
-
-    auto build_and_run = [&](size_t   min_stride,
-                             StageFn* (*lookup)(SkRasterPipeline::StockStage),
-                             StageFn* just_return,
-                             size_t   (*start_pipeline)(size_t, void**, K*, size_t)) {
-        if (x + min_stride <= limit) {
-            void** ip = program.get();
-            for (auto&& st : fStages) {
-                auto fn = lookup(st.stage);
-                if (!fn) {
-                    return false;
-                }
-                *ip++ = (void*)fn;
-                if (st.ctx) {
-                    *ip++ = st.ctx;
-                }
-            }
-            *ip = (void*)just_return;
-
-            x = start_pipeline(x, program.get(), &kConstants, limit);
-        }
-        return true;
     };
 
-    // While possible, build and run at full vector stride.
-#if __has_feature(memory_sanitizer)
-    // We'll just run portable code.
-
-#elif defined(__aarch64__)
-    if (!build_and_run(4, lookup_aarch64, ASM(just_return,aarch64), ASM(start_pipeline,aarch64))) {
-        return false;
-    }
-
 #elif defined(__arm__)
     if (1 && SkCpu::Supports(SkCpu::NEON|SkCpu::NEON_FMA|SkCpu::VFP_FP16)) {
-        if (!build_and_run(2, lookup_vfp4, ASM(just_return,vfp4), ASM(start_pipeline,vfp4))) {
-            return false;
-        }
+        return {
+        #define M(stage) ASM(stage, vfp4),
+            { SK_RASTER_PIPELINE_STAGES(M) },
+            M(start_pipeline) M(just_return)
+        #undef M
+        };
     }
 
 #elif defined(__x86_64__) || defined(_M_X64)
     if (1 && SkCpu::Supports(SkCpu::HSW)) {
-        if (!build_and_run(1, lookup_hsw, ASM(just_return,hsw), ASM(start_pipeline,hsw))) {
-            return false;
-        }
+        return {
+        #define M(stage) ASM(stage, hsw),
+            { SK_RASTER_PIPELINE_STAGES(M) },
+            M(start_pipeline) M(just_return)
+        #undef M
+        };
     }
     if (1 && SkCpu::Supports(SkCpu::AVX)) {
-        if (!build_and_run(1, lookup_avx, ASM(just_return,avx), ASM(start_pipeline,avx))) {
-            return false;
-        }
+        return {
+        #define M(stage) ASM(stage, avx),
+            { SK_RASTER_PIPELINE_STAGES(M) },
+            M(start_pipeline) M(just_return)
+        #undef M
+        };
     }
     if (1 && SkCpu::Supports(SkCpu::SSE41)) {
-        if (!build_and_run(4, lookup_sse41, ASM(just_return,sse41), ASM(start_pipeline,sse41))) {
-            return false;
-        }
+        return {
+        #define M(stage) ASM(stage, sse41),
+            { SK_RASTER_PIPELINE_STAGES(M) },
+            M(start_pipeline) M(just_return)
+        #undef M
+        };
     }
     if (1 && SkCpu::Supports(SkCpu::SSE2)) {
-        if (!build_and_run(4, lookup_sse2, ASM(just_return,sse2), ASM(start_pipeline,sse2))) {
-            return false;
+        return {
+        #define M(stage) ASM(stage, sse2),
+            { SK_RASTER_PIPELINE_STAGES(M) },
+            M(start_pipeline) M(just_return)
+        #undef M
+        };
+    }
+#endif
+    return kPortable;
+}
+
+StartPipelineFn* SkRasterPipeline::build_pipeline(void** ip) const {
+#if !__has_feature(memory_sanitizer) && (defined(__x86_64__) || defined(_M_X64))
+    if (SkCpu::Supports(SkCpu::SSSE3)) {
+        void** reset_point = ip;
+
+        *--ip = (void*)ASM(just_return,ssse3_lowp);
+        for (const StageList* st = fStages; st; st = st->prev) {
+            StageFn* fn = nullptr;
+            switch (st->stage) {
+            #define M(st) case SkRasterPipeline::st: fn = ASM(st, ssse3_lowp); break;
+                LOWP_STAGES(M)
+            #undef M
+                case SkRasterPipeline::clamp_0: continue;  // clamp_0 is a no-op in lowp.
+                default:
+                    log_missing(st->stage);
+                    ip = reset_point;
+            }
+            if (ip == reset_point) {
+                break;
+            }
+            if (st->ctx) {
+                *--ip = st->ctx;
+            }
+            *--ip = (void*)fn;
+        }
+
+        if (ip != reset_point) {
+            return ASM(start_pipeline,ssse3_lowp);
         }
     }
 #endif
+    gChooseEngineOnce([]{ gEngine = choose_engine(); });
 
-    // Finish up any leftover with portable code one pixel at a time.
-    return build_and_run(1, lookup_portable, sk_just_return, sk_start_pipeline);
+    // We're building the pipeline backwards, so we start with the final stage just_return.
+    *--ip = (void*)gEngine.just_return;
+
+    // Still going backwards, each stage's context pointer then its StageFn.
+    for (const StageList* st = fStages; st; st = st->prev) {
+        if (st->ctx) {
+            *--ip = st->ctx;
+        }
+        *--ip = (void*)gEngine.stages[st->stage];
+    }
+    return gEngine.start_pipeline;
+}
+
+void SkRasterPipeline::run(size_t x, size_t y, size_t n) const {
+    if (this->empty()) {
+        return;
+    }
+
+    // Best to not use fAlloc here... we can't bound how often run() will be called.
+    SkAutoSTMalloc<64, void*> program(fSlotsNeeded);
+
+    auto start_pipeline = this->build_pipeline(program.get() + fSlotsNeeded);
+    start_pipeline(x,y,x+n, program.get(), &kConstants);
+}
+
+std::function<void(size_t, size_t, size_t)> SkRasterPipeline::compile() const {
+    if (this->empty()) {
+        return [](size_t, size_t, size_t) {};
+    }
+
+    void** program = fAlloc->makeArray<void*>(fSlotsNeeded);
+    auto start_pipeline = this->build_pipeline(program + fSlotsNeeded);
+
+    return [=](size_t x, size_t y, size_t n) {
+        start_pipeline(x,y,x+n, program, &kConstants);
+    };
 }
diff --git a/src/jumper/SkJumper.h b/src/jumper/SkJumper.h
index 712417a..8e1dd35 100644
--- a/src/jumper/SkJumper.h
+++ b/src/jumper/SkJumper.h
@@ -12,21 +12,94 @@
 // and SkJumper_stages.cpp (compiled into Skia _and_ offline into SkJumper_generated.h).
 // Keep it simple!
 
-#include <stdint.h>
+// Sometimes we need to make sure externally facing functions are called with MS' ABI, not System V.
+#if defined(JUMPER) && defined(WIN)
+    #define MAYBE_MSABI __attribute__((ms_abi))
+#else
+    #define MAYBE_MSABI
+#endif
 
-// SkJumper_stages.cpp has some unusual constraints on what constants it can use.
-//
-// If the constant is baked into the instruction, that's ok.
-// If the constant is synthesized through code, that's ok.
-// If the constant is loaded from memory, that's no good.
-//
-// We offer a couple facilities to get at any other constants you need:
-//   - the C() function usually constrains constants to be directly baked into an instruction; or
-//   - the _i and _f user-defined literal operators call C() for you in a prettier way; or
-//   - you can load values from this struct.
+#if defined(JUMPER) && (defined(__aarch64__) || defined(__arm__))
+    // To reduce SkJumper's dependency on the Android NDK,
+    // we provide what we need from <string.h>, <stdint.h>, and <stddef.h> ourselves.
+    #define memcpy __builtin_memcpy
+
+    using  int8_t  =   signed char;
+    using uint8_t  = unsigned char;
+    using  int16_t =   signed short;
+    using uint16_t = unsigned short;
+    using  int32_t =   signed int;
+    using uint32_t = unsigned int;
+    #if defined(__aarch64__)
+        using  int64_t =   signed long;
+        using uint64_t = unsigned long;
+        using size_t = uint64_t;
+    #else
+        using  int64_t =   signed long long;
+        using uint64_t = unsigned long long;
+        using size_t = uint32_t;
+    #endif
+
+    // Now pretend we've included <stdint.h> (or it'll be included again by <arm_neon.h>).
+    #define __CLANG_STDINT_H
+    #define _STDINT_H_
+#else
+    #include <string.h>
+    #include <stdint.h>
+#endif
+
+static const int SkJumper_kMaxStride = 8;
 
 struct SkJumper_constants {
-    float iota[8];      //  0,1,2,3,4,5,6,7
+    float    iota_F  [SkJumper_kMaxStride];   //  0,1,2,3,4,...
+    uint32_t iota_U32[SkJumper_kMaxStride];   //  0,1,2,3,4,...
+};
+
+struct SkJumper_GatherCtx {
+    const void*     pixels;
+    const uint32_t* ctable;
+    int             stride;
+};
+
+// State shared by save_xy, accumulate, and bilinear_* / bicubic_*.
+struct SkJumper_SamplerCtx {
+    float      x[SkJumper_kMaxStride];
+    float      y[SkJumper_kMaxStride];
+    float     fx[SkJumper_kMaxStride];
+    float     fy[SkJumper_kMaxStride];
+    float scalex[SkJumper_kMaxStride];
+    float scaley[SkJumper_kMaxStride];
+};
+
+struct SkJumper_CallbackCtx {
+    MAYBE_MSABI void (*fn)(SkJumper_CallbackCtx* self, int active_pixels/*<= SkJumper_kMaxStride*/);
+
+    // When called, fn() will have our active pixels available in rgba.
+    // When fn() returns, the pipeline will read back those active pixels from read_from.
+    float rgba[4*SkJumper_kMaxStride];
+    float* read_from = rgba;
+};
+
+struct SkJumper_LoadTablesCtx {
+    const void* src;
+    const float *r, *g, *b;
+};
+
+struct SkJumper_TableCtx {
+    const float* table;
+    int          size;
+};
+
+// This should line up with the memory layout of SkColorSpaceTransferFn.
+struct SkJumper_ParametricTransferFunction {
+    float G, A,B,C,D,E,F;
+};
+
+struct SkJumper_GradientCtx {
+    size_t stopCount;
+    float* fs[4];
+    float* bs[4];
+    float* ts;
 };
 
 #endif//SkJumper_DEFINED
diff --git a/src/jumper/SkJumper_generated.S b/src/jumper/SkJumper_generated.S
new file mode 100644
index 0000000..e568ed9
--- /dev/null
+++ b/src/jumper/SkJumper_generated.S
@@ -0,0 +1,38032 @@
+# Copyright 2017 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This file is generated semi-automatically with this command:
+#   $ src/jumper/build_stages.py
+
+#if defined(__MACH__)
+    #define HIDDEN .private_extern
+    #define FUNCTION(name)
+    #define BALIGN4  .align 2
+    #define BALIGN16 .align 4
+    #define BALIGN32 .align 5
+#else
+    .section .note.GNU-stack,"",%progbits
+    #define HIDDEN .hidden
+    #define FUNCTION(name) .type name,%function
+    #define BALIGN4  .balign 4
+    #define BALIGN16 .balign 16
+    #define BALIGN32 .balign 32
+#endif
+.text
+#if defined(__aarch64__)
+BALIGN4
+
+HIDDEN _sk_start_pipeline_aarch64
+.globl _sk_start_pipeline_aarch64
+FUNCTION(_sk_start_pipeline_aarch64)
+_sk_start_pipeline_aarch64:
+  .long  0xf81c0ff7                          // str           x23, [sp, #-64]!
+  .long  0xa90157f6                          // stp           x22, x21, [sp, #16]
+  .long  0xa9024ff4                          // stp           x20, x19, [sp, #32]
+  .long  0xa9037bfd                          // stp           x29, x30, [sp, #48]
+  .long  0xaa0303f4                          // mov           x20, x3
+  .long  0xf8408685                          // ldr           x5, [x20], #8
+  .long  0xaa0003f7                          // mov           x23, x0
+  .long  0xaa0203f6                          // mov           x22, x2
+  .long  0x910012e8                          // add           x8, x23, #0x4
+  .long  0xaa0403f3                          // mov           x19, x4
+  .long  0xeb16011f                          // cmp           x8, x22
+  .long  0xaa0103f5                          // mov           x21, x1
+  .long  0x9100c3fd                          // add           x29, sp, #0x30
+  .long  0x54000069                          // b.ls          40 <sk_start_pipeline_aarch64+0x40>  // b.plast
+  .long  0xaa1703e2                          // mov           x2, x23
+  .long  0x14000017                          // b             98 <sk_start_pipeline_aarch64+0x98>
+  .long  0xf90007e5                          // str           x5, [sp, #8]
+  .long  0xf94007e5                          // ldr           x5, [sp, #8]
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0x6f00e403                          // movi          v3.2d, #0x0
+  .long  0x6f00e404                          // movi          v4.2d, #0x0
+  .long  0x6f00e405                          // movi          v5.2d, #0x0
+  .long  0x6f00e406                          // movi          v6.2d, #0x0
+  .long  0x6f00e407                          // movi          v7.2d, #0x0
+  .long  0xaa1303e0                          // mov           x0, x19
+  .long  0xaa1403e1                          // mov           x1, x20
+  .long  0xaa1703e2                          // mov           x2, x23
+  .long  0xaa1503e3                          // mov           x3, x21
+  .long  0xaa1f03e4                          // mov           x4, xzr
+  .long  0xd63f00a0                          // blr           x5
+  .long  0xf94007e5                          // ldr           x5, [sp, #8]
+  .long  0x910012e2                          // add           x2, x23, #0x4
+  .long  0x910022e8                          // add           x8, x23, #0x8
+  .long  0xeb16011f                          // cmp           x8, x22
+  .long  0xaa0203f7                          // mov           x23, x2
+  .long  0x54fffd89                          // b.ls          44 <sk_start_pipeline_aarch64+0x44>  // b.plast
+  .long  0xcb0202c4                          // sub           x4, x22, x2
+  .long  0xb4000224                          // cbz           x4, e0 <sk_start_pipeline_aarch64+0xe0>
+  .long  0xaa1303e0                          // mov           x0, x19
+  .long  0xaa1403e1                          // mov           x1, x20
+  .long  0xaa1503e3                          // mov           x3, x21
+  .long  0xa9437bfd                          // ldp           x29, x30, [sp, #48]
+  .long  0xa9424ff4                          // ldp           x20, x19, [sp, #32]
+  .long  0xa94157f6                          // ldp           x22, x21, [sp, #16]
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0x6f00e403                          // movi          v3.2d, #0x0
+  .long  0x6f00e404                          // movi          v4.2d, #0x0
+  .long  0x6f00e405                          // movi          v5.2d, #0x0
+  .long  0x6f00e406                          // movi          v6.2d, #0x0
+  .long  0x6f00e407                          // movi          v7.2d, #0x0
+  .long  0xf84407f7                          // ldr           x23, [sp], #64
+  .long  0xd61f00a0                          // br            x5
+  .long  0xa9437bfd                          // ldp           x29, x30, [sp, #48]
+  .long  0xa9424ff4                          // ldp           x20, x19, [sp, #32]
+  .long  0xa94157f6                          // ldp           x22, x21, [sp, #16]
+  .long  0xf84407f7                          // ldr           x23, [sp], #64
+  .long  0xd65f03c0                          // ret
+
+HIDDEN _sk_just_return_aarch64
+.globl _sk_just_return_aarch64
+FUNCTION(_sk_just_return_aarch64)
+_sk_just_return_aarch64:
+  .long  0xd65f03c0                          // ret
+
+HIDDEN _sk_seed_shader_aarch64
+.globl _sk_seed_shader_aarch64
+FUNCTION(_sk_seed_shader_aarch64)
+_sk_seed_shader_aarch64:
+  .long  0x3dc00007                          // ldr           q7, [x0]
+  .long  0x4e040c40                          // dup           v0.4s, w2
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4f0167e1                          // movi          v1.4s, #0x3f, lsl #24
+  .long  0x4e040c66                          // dup           v6.4s, w3
+  .long  0x4e21d800                          // scvtf         v0.4s, v0.4s
+  .long  0x4e21d8c6                          // scvtf         v6.4s, v6.4s
+  .long  0x4e21d400                          // fadd          v0.4s, v0.4s, v1.4s
+  .long  0x4f03f602                          // fmov          v2.4s, #1.000000000000000000e+00
+  .long  0x6f00e403                          // movi          v3.2d, #0x0
+  .long  0x6f00e404                          // movi          v4.2d, #0x0
+  .long  0x6f00e405                          // movi          v5.2d, #0x0
+  .long  0x4e21d4c1                          // fadd          v1.4s, v6.4s, v1.4s
+  .long  0x6f00e406                          // movi          v6.2d, #0x0
+  .long  0x4e27d400                          // fadd          v0.4s, v0.4s, v7.4s
+  .long  0x6f00e407                          // movi          v7.2d, #0x0
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_dither_aarch64
+.globl _sk_dither_aarch64
+FUNCTION(_sk_dither_aarch64)
+_sk_dither_aarch64:
+  .long  0x3dc00811                          // ldr           q17, [x0, #32]
+  .long  0x4e040c50                          // dup           v16.4s, w2
+  .long  0x4e040c72                          // dup           v18.4s, w3
+  .long  0x4f000433                          // movi          v19.4s, #0x1
+  .long  0x4f000455                          // movi          v21.4s, #0x2
+  .long  0x4eb08630                          // add           v16.4s, v17.4s, v16.4s
+  .long  0x4f000494                          // movi          v20.4s, #0x4
+  .long  0x52a79008                          // mov           w8, #0x3c800000
+  .long  0x6e301e51                          // eor           v17.16b, v18.16b, v16.16b
+  .long  0x4e331e12                          // and           v18.16b, v16.16b, v19.16b
+  .long  0x4e351e17                          // and           v23.16b, v16.16b, v21.16b
+  .long  0x4e040d16                          // dup           v22.4s, w8
+  .long  0x4e341e10                          // and           v16.16b, v16.16b, v20.16b
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4f245652                          // shl           v18.4s, v18.4s, #4
+  .long  0x4f2156f7                          // shl           v23.4s, v23.4s, #1
+  .long  0x4e331e33                          // and           v19.16b, v17.16b, v19.16b
+  .long  0x4eb21ef2                          // orr           v18.16b, v23.16b, v18.16b
+  .long  0x6f3e0610                          // ushr          v16.4s, v16.4s, #2
+  .long  0x4e351e35                          // and           v21.16b, v17.16b, v21.16b
+  .long  0x4eb01e50                          // orr           v16.16b, v18.16b, v16.16b
+  .long  0x4f255673                          // shl           v19.4s, v19.4s, #5
+  .long  0x4e341e31                          // and           v17.16b, v17.16b, v20.16b
+  .long  0x4f2256b5                          // shl           v21.4s, v21.4s, #2
+  .long  0x4eb31e10                          // orr           v16.16b, v16.16b, v19.16b
+  .long  0xbd400117                          // ldr           s23, [x8]
+  .long  0x6f3f0631                          // ushr          v17.4s, v17.4s, #1
+  .long  0x4eb51e10                          // orr           v16.16b, v16.16b, v21.16b
+  .long  0x52b7df89                          // mov           w9, #0xbefc0000
+  .long  0x4eb11e10                          // orr           v16.16b, v16.16b, v17.16b
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x4e30ced4                          // fmla          v20.4s, v22.4s, v16.4s
+  .long  0x4f979290                          // fmul          v16.4s, v20.4s, v23.s[0]
+  .long  0x4e20d600                          // fadd          v0.4s, v16.4s, v0.4s
+  .long  0x4e21d601                          // fadd          v1.4s, v16.4s, v1.4s
+  .long  0x4e22d602                          // fadd          v2.4s, v16.4s, v2.4s
+  .long  0x6f00e412                          // movi          v18.2d, #0x0
+  .long  0x4ea3f400                          // fmin          v0.4s, v0.4s, v3.4s
+  .long  0x4ea3f421                          // fmin          v1.4s, v1.4s, v3.4s
+  .long  0x4ea3f442                          // fmin          v2.4s, v2.4s, v3.4s
+  .long  0x4e20f640                          // fmax          v0.4s, v18.4s, v0.4s
+  .long  0x4e21f641                          // fmax          v1.4s, v18.4s, v1.4s
+  .long  0x4e22f642                          // fmax          v2.4s, v18.4s, v2.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_constant_color_aarch64
+.globl _sk_constant_color_aarch64
+FUNCTION(_sk_constant_color_aarch64)
+_sk_constant_color_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xaa0803ea                          // mov           x10, x8
+  .long  0x4ddfc940                          // ld1r          {v0.4s}, [x10], #4
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x91003108                          // add           x8, x8, #0xc
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0x4d40c903                          // ld1r          {v3.4s}, [x8]
+  .long  0x4d40c941                          // ld1r          {v1.4s}, [x10]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_load_rgba_aarch64
+.globl _sk_load_rgba_aarch64
+FUNCTION(_sk_load_rgba_aarch64)
+_sk_load_rgba_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xad400500                          // ldp           q0, q1, [x8]
+  .long  0xad410d02                          // ldp           q2, q3, [x8, #32]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_store_rgba_aarch64
+.globl _sk_store_rgba_aarch64
+FUNCTION(_sk_store_rgba_aarch64)
+_sk_store_rgba_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xad000500                          // stp           q0, q1, [x8]
+  .long  0xad010d02                          // stp           q2, q3, [x8, #32]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_clear_aarch64
+.globl _sk_clear_aarch64
+FUNCTION(_sk_clear_aarch64)
+_sk_clear_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0x6f00e403                          // movi          v3.2d, #0x0
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_srcatop_aarch64
+.globl _sk_srcatop_aarch64
+FUNCTION(_sk_srcatop_aarch64)
+_sk_srcatop_aarch64:
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x6e27dc00                          // fmul          v0.4s, v0.4s, v7.4s
+  .long  0x6e27dc21                          // fmul          v1.4s, v1.4s, v7.4s
+  .long  0x6e27dc42                          // fmul          v2.4s, v2.4s, v7.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e30cc80                          // fmla          v0.4s, v4.4s, v16.4s
+  .long  0x4e30cca1                          // fmla          v1.4s, v5.4s, v16.4s
+  .long  0x4e30ccc2                          // fmla          v2.4s, v6.4s, v16.4s
+  .long  0x6e27de10                          // fmul          v16.4s, v16.4s, v7.4s
+  .long  0x4e23ccf0                          // fmla          v16.4s, v7.4s, v3.4s
+  .long  0x4eb01e03                          // mov           v3.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_dstatop_aarch64
+.globl _sk_dstatop_aarch64
+FUNCTION(_sk_dstatop_aarch64)
+_sk_dstatop_aarch64:
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4ea7d610                          // fsub          v16.4s, v16.4s, v7.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e20de00                          // fmul          v0.4s, v16.4s, v0.4s
+  .long  0x6e21de01                          // fmul          v1.4s, v16.4s, v1.4s
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0x6e23de10                          // fmul          v16.4s, v16.4s, v3.4s
+  .long  0x4e23ccf0                          // fmla          v16.4s, v7.4s, v3.4s
+  .long  0x4e23cc80                          // fmla          v0.4s, v4.4s, v3.4s
+  .long  0x4e23cca1                          // fmla          v1.4s, v5.4s, v3.4s
+  .long  0x4e23ccc2                          // fmla          v2.4s, v6.4s, v3.4s
+  .long  0x4eb01e03                          // mov           v3.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_srcin_aarch64
+.globl _sk_srcin_aarch64
+FUNCTION(_sk_srcin_aarch64)
+_sk_srcin_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e27dc00                          // fmul          v0.4s, v0.4s, v7.4s
+  .long  0x6e27dc21                          // fmul          v1.4s, v1.4s, v7.4s
+  .long  0x6e27dc42                          // fmul          v2.4s, v2.4s, v7.4s
+  .long  0x6e27dc63                          // fmul          v3.4s, v3.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_dstin_aarch64
+.globl _sk_dstin_aarch64
+FUNCTION(_sk_dstin_aarch64)
+_sk_dstin_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e24dc60                          // fmul          v0.4s, v3.4s, v4.4s
+  .long  0x6e25dc61                          // fmul          v1.4s, v3.4s, v5.4s
+  .long  0x6e26dc62                          // fmul          v2.4s, v3.4s, v6.4s
+  .long  0x6e27dc63                          // fmul          v3.4s, v3.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_srcout_aarch64
+.globl _sk_srcout_aarch64
+FUNCTION(_sk_srcout_aarch64)
+_sk_srcout_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4ea7d610                          // fsub          v16.4s, v16.4s, v7.4s
+  .long  0x6e20de00                          // fmul          v0.4s, v16.4s, v0.4s
+  .long  0x6e21de01                          // fmul          v1.4s, v16.4s, v1.4s
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0x6e23de03                          // fmul          v3.4s, v16.4s, v3.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_dstout_aarch64
+.globl _sk_dstout_aarch64
+FUNCTION(_sk_dstout_aarch64)
+_sk_dstout_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4f03f600                          // fmov          v0.4s, #1.000000000000000000e+00
+  .long  0x4ea3d403                          // fsub          v3.4s, v0.4s, v3.4s
+  .long  0x6e24dc60                          // fmul          v0.4s, v3.4s, v4.4s
+  .long  0x6e25dc61                          // fmul          v1.4s, v3.4s, v5.4s
+  .long  0x6e26dc62                          // fmul          v2.4s, v3.4s, v6.4s
+  .long  0x6e27dc63                          // fmul          v3.4s, v3.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_srcover_aarch64
+.globl _sk_srcover_aarch64
+FUNCTION(_sk_srcover_aarch64)
+_sk_srcover_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x4e24ce00                          // fmla          v0.4s, v16.4s, v4.4s
+  .long  0x4e25ce01                          // fmla          v1.4s, v16.4s, v5.4s
+  .long  0x4e26ce02                          // fmla          v2.4s, v16.4s, v6.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_dstover_aarch64
+.globl _sk_dstover_aarch64
+FUNCTION(_sk_dstover_aarch64)
+_sk_dstover_aarch64:
+  .long  0x4f03f611                          // fmov          v17.4s, #1.000000000000000000e+00
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea41c90                          // mov           v16.16b, v4.16b
+  .long  0x4ea7d634                          // fsub          v20.4s, v17.4s, v7.4s
+  .long  0x4ea51cb1                          // mov           v17.16b, v5.16b
+  .long  0x4ea61cd2                          // mov           v18.16b, v6.16b
+  .long  0x4ea71cf3                          // mov           v19.16b, v7.16b
+  .long  0x4e20ce90                          // fmla          v16.4s, v20.4s, v0.4s
+  .long  0x4e21ce91                          // fmla          v17.4s, v20.4s, v1.4s
+  .long  0x4e22ce92                          // fmla          v18.4s, v20.4s, v2.4s
+  .long  0x4e23ce93                          // fmla          v19.4s, v20.4s, v3.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0x4eb21e42                          // mov           v2.16b, v18.16b
+  .long  0x4eb31e63                          // mov           v3.16b, v19.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_modulate_aarch64
+.globl _sk_modulate_aarch64
+FUNCTION(_sk_modulate_aarch64)
+_sk_modulate_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e24dc00                          // fmul          v0.4s, v0.4s, v4.4s
+  .long  0x6e25dc21                          // fmul          v1.4s, v1.4s, v5.4s
+  .long  0x6e26dc42                          // fmul          v2.4s, v2.4s, v6.4s
+  .long  0x6e27dc63                          // fmul          v3.4s, v3.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_multiply_aarch64
+.globl _sk_multiply_aarch64
+FUNCTION(_sk_multiply_aarch64)
+_sk_multiply_aarch64:
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4ea7d613                          // fsub          v19.4s, v16.4s, v7.4s
+  .long  0x4ea3d614                          // fsub          v20.4s, v16.4s, v3.4s
+  .long  0x6e20de70                          // fmul          v16.4s, v19.4s, v0.4s
+  .long  0x6e21de71                          // fmul          v17.4s, v19.4s, v1.4s
+  .long  0x6e22de72                          // fmul          v18.4s, v19.4s, v2.4s
+  .long  0x6e23de73                          // fmul          v19.4s, v19.4s, v3.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e34cc90                          // fmla          v16.4s, v4.4s, v20.4s
+  .long  0x4e34ccb1                          // fmla          v17.4s, v5.4s, v20.4s
+  .long  0x4e34ccd2                          // fmla          v18.4s, v6.4s, v20.4s
+  .long  0x4e34ccf3                          // fmla          v19.4s, v7.4s, v20.4s
+  .long  0x4e20cc90                          // fmla          v16.4s, v4.4s, v0.4s
+  .long  0x4e21ccb1                          // fmla          v17.4s, v5.4s, v1.4s
+  .long  0x4e22ccd2                          // fmla          v18.4s, v6.4s, v2.4s
+  .long  0x4e23ccf3                          // fmla          v19.4s, v7.4s, v3.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0x4eb21e42                          // mov           v2.16b, v18.16b
+  .long  0x4eb31e63                          // mov           v3.16b, v19.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_plus__aarch64
+.globl _sk_plus__aarch64
+FUNCTION(_sk_plus__aarch64)
+_sk_plus__aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e24d400                          // fadd          v0.4s, v0.4s, v4.4s
+  .long  0x4e25d421                          // fadd          v1.4s, v1.4s, v5.4s
+  .long  0x4e26d442                          // fadd          v2.4s, v2.4s, v6.4s
+  .long  0x4e27d463                          // fadd          v3.4s, v3.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_screen_aarch64
+.globl _sk_screen_aarch64
+FUNCTION(_sk_screen_aarch64)
+_sk_screen_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e24d410                          // fadd          v16.4s, v0.4s, v4.4s
+  .long  0x4e25d431                          // fadd          v17.4s, v1.4s, v5.4s
+  .long  0x4e26d452                          // fadd          v18.4s, v2.4s, v6.4s
+  .long  0x4e27d473                          // fadd          v19.4s, v3.4s, v7.4s
+  .long  0x4ea4cc10                          // fmls          v16.4s, v0.4s, v4.4s
+  .long  0x4ea5cc31                          // fmls          v17.4s, v1.4s, v5.4s
+  .long  0x4ea6cc52                          // fmls          v18.4s, v2.4s, v6.4s
+  .long  0x4ea7cc73                          // fmls          v19.4s, v3.4s, v7.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0x4eb21e42                          // mov           v2.16b, v18.16b
+  .long  0x4eb31e63                          // mov           v3.16b, v19.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_xor__aarch64
+.globl _sk_xor__aarch64
+FUNCTION(_sk_xor__aarch64)
+_sk_xor__aarch64:
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea7d611                          // fsub          v17.4s, v16.4s, v7.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x6e20de20                          // fmul          v0.4s, v17.4s, v0.4s
+  .long  0x6e21de21                          // fmul          v1.4s, v17.4s, v1.4s
+  .long  0x6e22de22                          // fmul          v2.4s, v17.4s, v2.4s
+  .long  0x6e23de23                          // fmul          v3.4s, v17.4s, v3.4s
+  .long  0x4e30cc80                          // fmla          v0.4s, v4.4s, v16.4s
+  .long  0x4e30cca1                          // fmla          v1.4s, v5.4s, v16.4s
+  .long  0x4e30ccc2                          // fmla          v2.4s, v6.4s, v16.4s
+  .long  0x4e30cce3                          // fmla          v3.4s, v7.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_darken_aarch64
+.globl _sk_darken_aarch64
+FUNCTION(_sk_darken_aarch64)
+_sk_darken_aarch64:
+  .long  0x6e27dc10                          // fmul          v16.4s, v0.4s, v7.4s
+  .long  0x6e24dc71                          // fmul          v17.4s, v3.4s, v4.4s
+  .long  0x6e27dc32                          // fmul          v18.4s, v1.4s, v7.4s
+  .long  0x6e25dc73                          // fmul          v19.4s, v3.4s, v5.4s
+  .long  0x4e31f610                          // fmax          v16.4s, v16.4s, v17.4s
+  .long  0x4e24d400                          // fadd          v0.4s, v0.4s, v4.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e27dc51                          // fmul          v17.4s, v2.4s, v7.4s
+  .long  0x4e33f652                          // fmax          v18.4s, v18.4s, v19.4s
+  .long  0x6e26dc73                          // fmul          v19.4s, v3.4s, v6.4s
+  .long  0x4eb0d400                          // fsub          v0.4s, v0.4s, v16.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4e33f631                          // fmax          v17.4s, v17.4s, v19.4s
+  .long  0x4e25d421                          // fadd          v1.4s, v1.4s, v5.4s
+  .long  0x4e26d442                          // fadd          v2.4s, v2.4s, v6.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x4eb2d421                          // fsub          v1.4s, v1.4s, v18.4s
+  .long  0x4eb1d442                          // fsub          v2.4s, v2.4s, v17.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_lighten_aarch64
+.globl _sk_lighten_aarch64
+FUNCTION(_sk_lighten_aarch64)
+_sk_lighten_aarch64:
+  .long  0x6e27dc10                          // fmul          v16.4s, v0.4s, v7.4s
+  .long  0x6e24dc71                          // fmul          v17.4s, v3.4s, v4.4s
+  .long  0x6e27dc32                          // fmul          v18.4s, v1.4s, v7.4s
+  .long  0x6e25dc73                          // fmul          v19.4s, v3.4s, v5.4s
+  .long  0x4eb1f610                          // fmin          v16.4s, v16.4s, v17.4s
+  .long  0x4e24d400                          // fadd          v0.4s, v0.4s, v4.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e27dc51                          // fmul          v17.4s, v2.4s, v7.4s
+  .long  0x4eb3f652                          // fmin          v18.4s, v18.4s, v19.4s
+  .long  0x6e26dc73                          // fmul          v19.4s, v3.4s, v6.4s
+  .long  0x4eb0d400                          // fsub          v0.4s, v0.4s, v16.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4eb3f631                          // fmin          v17.4s, v17.4s, v19.4s
+  .long  0x4e25d421                          // fadd          v1.4s, v1.4s, v5.4s
+  .long  0x4e26d442                          // fadd          v2.4s, v2.4s, v6.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x4eb2d421                          // fsub          v1.4s, v1.4s, v18.4s
+  .long  0x4eb1d442                          // fsub          v2.4s, v2.4s, v17.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_difference_aarch64
+.globl _sk_difference_aarch64
+FUNCTION(_sk_difference_aarch64)
+_sk_difference_aarch64:
+  .long  0x6e27dc10                          // fmul          v16.4s, v0.4s, v7.4s
+  .long  0x6e24dc71                          // fmul          v17.4s, v3.4s, v4.4s
+  .long  0x6e27dc32                          // fmul          v18.4s, v1.4s, v7.4s
+  .long  0x6e25dc73                          // fmul          v19.4s, v3.4s, v5.4s
+  .long  0x4eb1f610                          // fmin          v16.4s, v16.4s, v17.4s
+  .long  0x4eb3f652                          // fmin          v18.4s, v18.4s, v19.4s
+  .long  0x4e24d400                          // fadd          v0.4s, v0.4s, v4.4s
+  .long  0x4e30d610                          // fadd          v16.4s, v16.4s, v16.4s
+  .long  0x6e27dc51                          // fmul          v17.4s, v2.4s, v7.4s
+  .long  0x6e26dc73                          // fmul          v19.4s, v3.4s, v6.4s
+  .long  0x4eb0d400                          // fsub          v0.4s, v0.4s, v16.4s
+  .long  0x4e25d421                          // fadd          v1.4s, v1.4s, v5.4s
+  .long  0x4e32d650                          // fadd          v16.4s, v18.4s, v18.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb3f631                          // fmin          v17.4s, v17.4s, v19.4s
+  .long  0x4eb0d421                          // fsub          v1.4s, v1.4s, v16.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4e26d442                          // fadd          v2.4s, v2.4s, v6.4s
+  .long  0x4e31d631                          // fadd          v17.4s, v17.4s, v17.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x4eb1d442                          // fsub          v2.4s, v2.4s, v17.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_exclusion_aarch64
+.globl _sk_exclusion_aarch64
+FUNCTION(_sk_exclusion_aarch64)
+_sk_exclusion_aarch64:
+  .long  0x4e24d410                          // fadd          v16.4s, v0.4s, v4.4s
+  .long  0x6e24dc00                          // fmul          v0.4s, v0.4s, v4.4s
+  .long  0x4e20d400                          // fadd          v0.4s, v0.4s, v0.4s
+  .long  0x4ea0d600                          // fsub          v0.4s, v16.4s, v0.4s
+  .long  0x4e25d430                          // fadd          v16.4s, v1.4s, v5.4s
+  .long  0x6e25dc21                          // fmul          v1.4s, v1.4s, v5.4s
+  .long  0x4e21d421                          // fadd          v1.4s, v1.4s, v1.4s
+  .long  0x4ea1d601                          // fsub          v1.4s, v16.4s, v1.4s
+  .long  0x4e26d450                          // fadd          v16.4s, v2.4s, v6.4s
+  .long  0x6e26dc42                          // fmul          v2.4s, v2.4s, v6.4s
+  .long  0x4e22d442                          // fadd          v2.4s, v2.4s, v2.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea2d602                          // fsub          v2.4s, v16.4s, v2.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_colorburn_aarch64
+.globl _sk_colorburn_aarch64
+FUNCTION(_sk_colorburn_aarch64)
+_sk_colorburn_aarch64:
+  .long  0x4ea4d4f3                          // fsub          v19.4s, v7.4s, v4.4s
+  .long  0x6e23de73                          // fmul          v19.4s, v19.4s, v3.4s
+  .long  0x4f03f611                          // fmov          v17.4s, #1.000000000000000000e+00
+  .long  0x6e20fe73                          // fdiv          v19.4s, v19.4s, v0.4s
+  .long  0x4ea7d634                          // fsub          v20.4s, v17.4s, v7.4s
+  .long  0x4eb3f4f3                          // fmin          v19.4s, v7.4s, v19.4s
+  .long  0x6e20de95                          // fmul          v21.4s, v20.4s, v0.4s
+  .long  0x4eb3d4f3                          // fsub          v19.4s, v7.4s, v19.4s
+  .long  0x4e24d6b6                          // fadd          v22.4s, v21.4s, v4.4s
+  .long  0x4e33cc75                          // fmla          v21.4s, v3.4s, v19.4s
+  .long  0x4ea5d4f3                          // fsub          v19.4s, v7.4s, v5.4s
+  .long  0x6e23de73                          // fmul          v19.4s, v19.4s, v3.4s
+  .long  0x6e21fe73                          // fdiv          v19.4s, v19.4s, v1.4s
+  .long  0x4ea0d812                          // fcmeq         v18.4s, v0.4s, #0.0
+  .long  0x4eb3f4f3                          // fmin          v19.4s, v7.4s, v19.4s
+  .long  0x6e751c12                          // bsl           v18.16b, v0.16b, v21.16b
+  .long  0x6e21de80                          // fmul          v0.4s, v20.4s, v1.4s
+  .long  0x4eb3d4f3                          // fsub          v19.4s, v7.4s, v19.4s
+  .long  0x4e25d415                          // fadd          v21.4s, v0.4s, v5.4s
+  .long  0x4e33cc60                          // fmla          v0.4s, v3.4s, v19.4s
+  .long  0x4ea0d833                          // fcmeq         v19.4s, v1.4s, #0.0
+  .long  0x6e601c33                          // bsl           v19.16b, v1.16b, v0.16b
+  .long  0x4ea6d4e0                          // fsub          v0.4s, v7.4s, v6.4s
+  .long  0x6e23dc00                          // fmul          v0.4s, v0.4s, v3.4s
+  .long  0x6e22fc00                          // fdiv          v0.4s, v0.4s, v2.4s
+  .long  0x4ea0f4e0                          // fmin          v0.4s, v7.4s, v0.4s
+  .long  0x6e22de81                          // fmul          v1.4s, v20.4s, v2.4s
+  .long  0x4ea0d4e0                          // fsub          v0.4s, v7.4s, v0.4s
+  .long  0x4e26d434                          // fadd          v20.4s, v1.4s, v6.4s
+  .long  0x4e20cc61                          // fmla          v1.4s, v3.4s, v0.4s
+  .long  0x4ea0d840                          // fcmeq         v0.4s, v2.4s, #0.0
+  .long  0x4ea3d631                          // fsub          v17.4s, v17.4s, v3.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e27e490                          // fcmeq         v16.4s, v4.4s, v7.4s
+  .long  0x6e611c40                          // bsl           v0.16b, v2.16b, v1.16b
+  .long  0x4e31cc92                          // fmla          v18.4s, v4.4s, v17.4s
+  .long  0x4e27e4a1                          // fcmeq         v1.4s, v5.4s, v7.4s
+  .long  0x4e27e4c2                          // fcmeq         v2.4s, v6.4s, v7.4s
+  .long  0x4e31ccb3                          // fmla          v19.4s, v5.4s, v17.4s
+  .long  0x4e31ccc0                          // fmla          v0.4s, v6.4s, v17.4s
+  .long  0x6e721ed0                          // bsl           v16.16b, v22.16b, v18.16b
+  .long  0x6e731ea1                          // bsl           v1.16b, v21.16b, v19.16b
+  .long  0x6e601e82                          // bsl           v2.16b, v20.16b, v0.16b
+  .long  0x4e27ce23                          // fmla          v3.4s, v17.4s, v7.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_colordodge_aarch64
+.globl _sk_colordodge_aarch64
+FUNCTION(_sk_colordodge_aarch64)
+_sk_colordodge_aarch64:
+  .long  0x4f03f612                          // fmov          v18.4s, #1.000000000000000000e+00
+  .long  0x6e24dc71                          // fmul          v17.4s, v3.4s, v4.4s
+  .long  0x4ea0d474                          // fsub          v20.4s, v3.4s, v0.4s
+  .long  0x6e25dc75                          // fmul          v21.4s, v3.4s, v5.4s
+  .long  0x4ea1d476                          // fsub          v22.4s, v3.4s, v1.4s
+  .long  0x4ea7d657                          // fsub          v23.4s, v18.4s, v7.4s
+  .long  0x6e34fe31                          // fdiv          v17.4s, v17.4s, v20.4s
+  .long  0x6e36feb4                          // fdiv          v20.4s, v21.4s, v22.4s
+  .long  0x6e20def5                          // fmul          v21.4s, v23.4s, v0.4s
+  .long  0x4eb1f4f1                          // fmin          v17.4s, v7.4s, v17.4s
+  .long  0x4e23e413                          // fcmeq         v19.4s, v0.4s, v3.4s
+  .long  0x4e24d6b6                          // fadd          v22.4s, v21.4s, v4.4s
+  .long  0x4e31cc75                          // fmla          v21.4s, v3.4s, v17.4s
+  .long  0x6e751c13                          // bsl           v19.16b, v0.16b, v21.16b
+  .long  0x6e21dee0                          // fmul          v0.4s, v23.4s, v1.4s
+  .long  0x4eb4f4f4                          // fmin          v20.4s, v7.4s, v20.4s
+  .long  0x4e25d415                          // fadd          v21.4s, v0.4s, v5.4s
+  .long  0x4e34cc60                          // fmla          v0.4s, v3.4s, v20.4s
+  .long  0x4e23e434                          // fcmeq         v20.4s, v1.4s, v3.4s
+  .long  0x6e601c34                          // bsl           v20.16b, v1.16b, v0.16b
+  .long  0x6e26dc60                          // fmul          v0.4s, v3.4s, v6.4s
+  .long  0x4ea2d461                          // fsub          v1.4s, v3.4s, v2.4s
+  .long  0x6e21fc00                          // fdiv          v0.4s, v0.4s, v1.4s
+  .long  0x6e22dee1                          // fmul          v1.4s, v23.4s, v2.4s
+  .long  0x4ea0f4e0                          // fmin          v0.4s, v7.4s, v0.4s
+  .long  0x4e26d437                          // fadd          v23.4s, v1.4s, v6.4s
+  .long  0x4e20cc61                          // fmla          v1.4s, v3.4s, v0.4s
+  .long  0x4e23e440                          // fcmeq         v0.4s, v2.4s, v3.4s
+  .long  0x6e611c40                          // bsl           v0.16b, v2.16b, v1.16b
+  .long  0x4ea3d641                          // fsub          v1.4s, v18.4s, v3.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea0d890                          // fcmeq         v16.4s, v4.4s, #0.0
+  .long  0x4ea0d8b1                          // fcmeq         v17.4s, v5.4s, #0.0
+  .long  0x4e21cc93                          // fmla          v19.4s, v4.4s, v1.4s
+  .long  0x4e21ccb4                          // fmla          v20.4s, v5.4s, v1.4s
+  .long  0x4ea0d8c2                          // fcmeq         v2.4s, v6.4s, #0.0
+  .long  0x4e21ccc0                          // fmla          v0.4s, v6.4s, v1.4s
+  .long  0x6e731ed0                          // bsl           v16.16b, v22.16b, v19.16b
+  .long  0x6e741eb1                          // bsl           v17.16b, v21.16b, v20.16b
+  .long  0x6e601ee2                          // bsl           v2.16b, v23.16b, v0.16b
+  .long  0x4e27cc23                          // fmla          v3.4s, v1.4s, v7.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_hardlight_aarch64
+.globl _sk_hardlight_aarch64
+FUNCTION(_sk_hardlight_aarch64)
+_sk_hardlight_aarch64:
+  .long  0x4ea4d4f4                          // fsub          v20.4s, v7.4s, v4.4s
+  .long  0x4ea0d475                          // fsub          v21.4s, v3.4s, v0.4s
+  .long  0x6e34deb4                          // fmul          v20.4s, v21.4s, v20.4s
+  .long  0x4e20d411                          // fadd          v17.4s, v0.4s, v0.4s
+  .long  0x6e24dc12                          // fmul          v18.4s, v0.4s, v4.4s
+  .long  0x6e27dc73                          // fmul          v19.4s, v3.4s, v7.4s
+  .long  0x4e34d694                          // fadd          v20.4s, v20.4s, v20.4s
+  .long  0x6e31e471                          // fcmge         v17.4s, v3.4s, v17.4s
+  .long  0x4e32d652                          // fadd          v18.4s, v18.4s, v18.4s
+  .long  0x4eb4d674                          // fsub          v20.4s, v19.4s, v20.4s
+  .long  0x6e741e51                          // bsl           v17.16b, v18.16b, v20.16b
+  .long  0x4ea5d4f2                          // fsub          v18.4s, v7.4s, v5.4s
+  .long  0x4ea1d474                          // fsub          v20.4s, v3.4s, v1.4s
+  .long  0x6e32de92                          // fmul          v18.4s, v20.4s, v18.4s
+  .long  0x4e21d436                          // fadd          v22.4s, v1.4s, v1.4s
+  .long  0x6e25dc35                          // fmul          v21.4s, v1.4s, v5.4s
+  .long  0x4e32d652                          // fadd          v18.4s, v18.4s, v18.4s
+  .long  0x6e36e476                          // fcmge         v22.4s, v3.4s, v22.4s
+  .long  0x4e35d6b5                          // fadd          v21.4s, v21.4s, v21.4s
+  .long  0x4eb2d672                          // fsub          v18.4s, v19.4s, v18.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x6e721eb6                          // bsl           v22.16b, v21.16b, v18.16b
+  .long  0x4ea6d4f2                          // fsub          v18.4s, v7.4s, v6.4s
+  .long  0x4ea2d475                          // fsub          v21.4s, v3.4s, v2.4s
+  .long  0x6e32deb2                          // fmul          v18.4s, v21.4s, v18.4s
+  .long  0x4ea7d615                          // fsub          v21.4s, v16.4s, v7.4s
+  .long  0x4e22d454                          // fadd          v20.4s, v2.4s, v2.4s
+  .long  0x6e20dea0                          // fmul          v0.4s, v21.4s, v0.4s
+  .long  0x6e21dea1                          // fmul          v1.4s, v21.4s, v1.4s
+  .long  0x6e22deb5                          // fmul          v21.4s, v21.4s, v2.4s
+  .long  0x6e26dc42                          // fmul          v2.4s, v2.4s, v6.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e32d652                          // fadd          v18.4s, v18.4s, v18.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x6e34e474                          // fcmge         v20.4s, v3.4s, v20.4s
+  .long  0x4e22d442                          // fadd          v2.4s, v2.4s, v2.4s
+  .long  0x4eb2d672                          // fsub          v18.4s, v19.4s, v18.4s
+  .long  0x4e30cc80                          // fmla          v0.4s, v4.4s, v16.4s
+  .long  0x4e30cca1                          // fmla          v1.4s, v5.4s, v16.4s
+  .long  0x4e30ccd5                          // fmla          v21.4s, v6.4s, v16.4s
+  .long  0x6e721c54                          // bsl           v20.16b, v2.16b, v18.16b
+  .long  0x4e31d400                          // fadd          v0.4s, v0.4s, v17.4s
+  .long  0x4e36d421                          // fadd          v1.4s, v1.4s, v22.4s
+  .long  0x4e34d6a2                          // fadd          v2.4s, v21.4s, v20.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_overlay_aarch64
+.globl _sk_overlay_aarch64
+FUNCTION(_sk_overlay_aarch64)
+_sk_overlay_aarch64:
+  .long  0x4ea4d4f4                          // fsub          v20.4s, v7.4s, v4.4s
+  .long  0x4ea0d475                          // fsub          v21.4s, v3.4s, v0.4s
+  .long  0x6e34deb4                          // fmul          v20.4s, v21.4s, v20.4s
+  .long  0x4e24d491                          // fadd          v17.4s, v4.4s, v4.4s
+  .long  0x6e24dc12                          // fmul          v18.4s, v0.4s, v4.4s
+  .long  0x6e27dc73                          // fmul          v19.4s, v3.4s, v7.4s
+  .long  0x4e34d694                          // fadd          v20.4s, v20.4s, v20.4s
+  .long  0x6e31e4f1                          // fcmge         v17.4s, v7.4s, v17.4s
+  .long  0x4e32d652                          // fadd          v18.4s, v18.4s, v18.4s
+  .long  0x4eb4d674                          // fsub          v20.4s, v19.4s, v20.4s
+  .long  0x6e741e51                          // bsl           v17.16b, v18.16b, v20.16b
+  .long  0x4ea5d4f2                          // fsub          v18.4s, v7.4s, v5.4s
+  .long  0x4ea1d474                          // fsub          v20.4s, v3.4s, v1.4s
+  .long  0x6e32de92                          // fmul          v18.4s, v20.4s, v18.4s
+  .long  0x4e25d4b6                          // fadd          v22.4s, v5.4s, v5.4s
+  .long  0x6e25dc35                          // fmul          v21.4s, v1.4s, v5.4s
+  .long  0x4e32d652                          // fadd          v18.4s, v18.4s, v18.4s
+  .long  0x6e36e4f6                          // fcmge         v22.4s, v7.4s, v22.4s
+  .long  0x4e35d6b5                          // fadd          v21.4s, v21.4s, v21.4s
+  .long  0x4eb2d672                          // fsub          v18.4s, v19.4s, v18.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x6e721eb6                          // bsl           v22.16b, v21.16b, v18.16b
+  .long  0x4ea6d4f2                          // fsub          v18.4s, v7.4s, v6.4s
+  .long  0x4ea2d475                          // fsub          v21.4s, v3.4s, v2.4s
+  .long  0x6e32deb2                          // fmul          v18.4s, v21.4s, v18.4s
+  .long  0x4ea7d615                          // fsub          v21.4s, v16.4s, v7.4s
+  .long  0x4e26d4d4                          // fadd          v20.4s, v6.4s, v6.4s
+  .long  0x6e20dea0                          // fmul          v0.4s, v21.4s, v0.4s
+  .long  0x6e21dea1                          // fmul          v1.4s, v21.4s, v1.4s
+  .long  0x6e22deb5                          // fmul          v21.4s, v21.4s, v2.4s
+  .long  0x6e26dc42                          // fmul          v2.4s, v2.4s, v6.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e32d652                          // fadd          v18.4s, v18.4s, v18.4s
+  .long  0x4ea3d610                          // fsub          v16.4s, v16.4s, v3.4s
+  .long  0x6e34e4f4                          // fcmge         v20.4s, v7.4s, v20.4s
+  .long  0x4e22d442                          // fadd          v2.4s, v2.4s, v2.4s
+  .long  0x4eb2d672                          // fsub          v18.4s, v19.4s, v18.4s
+  .long  0x4e30cc80                          // fmla          v0.4s, v4.4s, v16.4s
+  .long  0x4e30cca1                          // fmla          v1.4s, v5.4s, v16.4s
+  .long  0x4e30ccd5                          // fmla          v21.4s, v6.4s, v16.4s
+  .long  0x6e721c54                          // bsl           v20.16b, v2.16b, v18.16b
+  .long  0x4e31d400                          // fadd          v0.4s, v0.4s, v17.4s
+  .long  0x4e36d421                          // fadd          v1.4s, v1.4s, v22.4s
+  .long  0x4e34d6a2                          // fadd          v2.4s, v21.4s, v20.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_softlight_aarch64
+.globl _sk_softlight_aarch64
+FUNCTION(_sk_softlight_aarch64)
+_sk_softlight_aarch64:
+  .long  0x4ea0c8f5                          // fcmgt         v21.4s, v7.4s, #0.0
+  .long  0x6e27fc96                          // fdiv          v22.4s, v4.4s, v7.4s
+  .long  0x6e27fcb8                          // fdiv          v24.4s, v5.4s, v7.4s
+  .long  0x6e27fcd9                          // fdiv          v25.4s, v6.4s, v7.4s
+  .long  0x4e351ed6                          // and           v22.16b, v22.16b, v21.16b
+  .long  0x4e351f18                          // and           v24.16b, v24.16b, v21.16b
+  .long  0x4e351f35                          // and           v21.16b, v25.16b, v21.16b
+  .long  0x6ea1dad9                          // frsqrte       v25.4s, v22.4s
+  .long  0x6e39df3d                          // fmul          v29.4s, v25.4s, v25.4s
+  .long  0x4ebdfedd                          // frsqrts       v29.4s, v22.4s, v29.4s
+  .long  0x6e3ddf39                          // fmul          v25.4s, v25.4s, v29.4s
+  .long  0x4ea1db3d                          // frecpe        v29.4s, v25.4s
+  .long  0x6ea0fada                          // fneg          v26.4s, v22.4s
+  .long  0x6ea1db1b                          // frsqrte       v27.4s, v24.4s
+  .long  0x4e3dff39                          // frecps        v25.4s, v25.4s, v29.4s
+  .long  0x4e3dcf3a                          // fmla          v26.4s, v25.4s, v29.4s
+  .long  0x6e3bdf7d                          // fmul          v29.4s, v27.4s, v27.4s
+  .long  0x4ebdff1d                          // frsqrts       v29.4s, v24.4s, v29.4s
+  .long  0x6e3ddf7b                          // fmul          v27.4s, v27.4s, v29.4s
+  .long  0x4ea1db7d                          // frecpe        v29.4s, v27.4s
+  .long  0x6ea0fb1c                          // fneg          v28.4s, v24.4s
+  .long  0x6ea1dab9                          // frsqrte       v25.4s, v21.4s
+  .long  0x4e3dff7b                          // frecps        v27.4s, v27.4s, v29.4s
+  .long  0x4e3dcf7c                          // fmla          v28.4s, v27.4s, v29.4s
+  .long  0x6e39df3d                          // fmul          v29.4s, v25.4s, v25.4s
+  .long  0x4ebdfebd                          // frsqrts       v29.4s, v21.4s, v29.4s
+  .long  0x6e3ddf39                          // fmul          v25.4s, v25.4s, v29.4s
+  .long  0x4ea1db3d                          // frecpe        v29.4s, v25.4s
+  .long  0x6ea0fabb                          // fneg          v27.4s, v21.4s
+  .long  0x4e3dff39                          // frecps        v25.4s, v25.4s, v29.4s
+  .long  0x4e3dcf3b                          // fmla          v27.4s, v25.4s, v29.4s
+  .long  0x4e36d6d9                          // fadd          v25.4s, v22.4s, v22.4s
+  .long  0x4f07f613                          // fmov          v19.4s, #-1.000000000000000000e+00
+  .long  0x4e39d739                          // fadd          v25.4s, v25.4s, v25.4s
+  .long  0x4e24d497                          // fadd          v23.4s, v4.4s, v4.4s
+  .long  0x4e33d6dd                          // fadd          v29.4s, v22.4s, v19.4s
+  .long  0x4e39cf39                          // fmla          v25.4s, v25.4s, v25.4s
+  .long  0x4f00f794                          // fmov          v20.4s, #7.000000000000000000e+00
+  .long  0x6e39dfb9                          // fmul          v25.4s, v29.4s, v25.4s
+  .long  0x4e37d6f7                          // fadd          v23.4s, v23.4s, v23.4s
+  .long  0x6e37e4f7                          // fcmge         v23.4s, v7.4s, v23.4s
+  .long  0x4e36ce99                          // fmla          v25.4s, v20.4s, v22.4s
+  .long  0x6e7a1f37                          // bsl           v23.16b, v25.16b, v26.16b
+  .long  0x4e38d719                          // fadd          v25.4s, v24.4s, v24.4s
+  .long  0x4e39d739                          // fadd          v25.4s, v25.4s, v25.4s
+  .long  0x4e33d71a                          // fadd          v26.4s, v24.4s, v19.4s
+  .long  0x4e39cf39                          // fmla          v25.4s, v25.4s, v25.4s
+  .long  0x6e39df59                          // fmul          v25.4s, v26.4s, v25.4s
+  .long  0x4e25d4ba                          // fadd          v26.4s, v5.4s, v5.4s
+  .long  0x4e3ad75a                          // fadd          v26.4s, v26.4s, v26.4s
+  .long  0x6e3ae4fa                          // fcmge         v26.4s, v7.4s, v26.4s
+  .long  0x4e38ce99                          // fmla          v25.4s, v20.4s, v24.4s
+  .long  0x6e7c1f3a                          // bsl           v26.16b, v25.16b, v28.16b
+  .long  0x4e35d6bc                          // fadd          v28.4s, v21.4s, v21.4s
+  .long  0x4e3cd79c                          // fadd          v28.4s, v28.4s, v28.4s
+  .long  0x4e33d6b3                          // fadd          v19.4s, v21.4s, v19.4s
+  .long  0x4e3ccf9c                          // fmla          v28.4s, v28.4s, v28.4s
+  .long  0x6e3cde73                          // fmul          v19.4s, v19.4s, v28.4s
+  .long  0x4e35ce93                          // fmla          v19.4s, v20.4s, v21.4s
+  .long  0x4e26d4d4                          // fadd          v20.4s, v6.4s, v6.4s
+  .long  0x4e34d694                          // fadd          v20.4s, v20.4s, v20.4s
+  .long  0x4f03f612                          // fmov          v18.4s, #1.000000000000000000e+00
+  .long  0x6e34e4f4                          // fcmge         v20.4s, v7.4s, v20.4s
+  .long  0x4e20d411                          // fadd          v17.4s, v0.4s, v0.4s
+  .long  0x6e7b1e74                          // bsl           v20.16b, v19.16b, v27.16b
+  .long  0x4ea7d65b                          // fsub          v27.4s, v18.4s, v7.4s
+  .long  0x4ea31c70                          // mov           v16.16b, v3.16b
+  .long  0x4e21d43d                          // fadd          v29.4s, v1.4s, v1.4s
+  .long  0x4e22d45c                          // fadd          v28.4s, v2.4s, v2.4s
+  .long  0x6e20df60                          // fmul          v0.4s, v27.4s, v0.4s
+  .long  0x6e21df61                          // fmul          v1.4s, v27.4s, v1.4s
+  .long  0x6e22df62                          // fmul          v2.4s, v27.4s, v2.4s
+  .long  0x4ea3d63b                          // fsub          v27.4s, v17.4s, v3.4s
+  .long  0x4eb6d656                          // fsub          v22.4s, v18.4s, v22.4s
+  .long  0x4ea31c79                          // mov           v25.16b, v3.16b
+  .long  0x4e3bced0                          // fmla          v16.4s, v22.4s, v27.4s
+  .long  0x4ea3d7b6                          // fsub          v22.4s, v29.4s, v3.4s
+  .long  0x4eb8d658                          // fsub          v24.4s, v18.4s, v24.4s
+  .long  0x4ea31c73                          // mov           v19.16b, v3.16b
+  .long  0x4e36cf19                          // fmla          v25.4s, v24.4s, v22.4s
+  .long  0x4ea3d798                          // fsub          v24.4s, v28.4s, v3.4s
+  .long  0x4eb5d655                          // fsub          v21.4s, v18.4s, v21.4s
+  .long  0x4e38ceb3                          // fmla          v19.4s, v21.4s, v24.4s
+  .long  0x6e27df7b                          // fmul          v27.4s, v27.4s, v7.4s
+  .long  0x6e27ded6                          // fmul          v22.4s, v22.4s, v7.4s
+  .long  0x6e27df18                          // fmul          v24.4s, v24.4s, v7.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e37df77                          // fmul          v23.4s, v27.4s, v23.4s
+  .long  0x6e3aded6                          // fmul          v22.4s, v22.4s, v26.4s
+  .long  0x6e34df14                          // fmul          v20.4s, v24.4s, v20.4s
+  .long  0x4ea3d652                          // fsub          v18.4s, v18.4s, v3.4s
+  .long  0x6e31e471                          // fcmge         v17.4s, v3.4s, v17.4s
+  .long  0x6e3de475                          // fcmge         v21.4s, v3.4s, v29.4s
+  .long  0x6e3ce47c                          // fcmge         v28.4s, v3.4s, v28.4s
+  .long  0x6e24de10                          // fmul          v16.4s, v16.4s, v4.4s
+  .long  0x6e25df39                          // fmul          v25.4s, v25.4s, v5.4s
+  .long  0x6e26de73                          // fmul          v19.4s, v19.4s, v6.4s
+  .long  0x4e23cc97                          // fmla          v23.4s, v4.4s, v3.4s
+  .long  0x4e23ccb6                          // fmla          v22.4s, v5.4s, v3.4s
+  .long  0x4e23ccd4                          // fmla          v20.4s, v6.4s, v3.4s
+  .long  0x4e32cc80                          // fmla          v0.4s, v4.4s, v18.4s
+  .long  0x4e32cca1                          // fmla          v1.4s, v5.4s, v18.4s
+  .long  0x4e32ccc2                          // fmla          v2.4s, v6.4s, v18.4s
+  .long  0x6e771e11                          // bsl           v17.16b, v16.16b, v23.16b
+  .long  0x6e761f35                          // bsl           v21.16b, v25.16b, v22.16b
+  .long  0x6e741e7c                          // bsl           v28.16b, v19.16b, v20.16b
+  .long  0x4e31d400                          // fadd          v0.4s, v0.4s, v17.4s
+  .long  0x4e35d421                          // fadd          v1.4s, v1.4s, v21.4s
+  .long  0x4e3cd442                          // fadd          v2.4s, v2.4s, v28.4s
+  .long  0x4e27ce43                          // fmla          v3.4s, v18.4s, v7.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_hue_aarch64
+.globl _sk_hue_aarch64
+FUNCTION(_sk_hue_aarch64)
+_sk_hue_aarch64:
+  .long  0x6e23dc32                          // fmul          v18.4s, v1.4s, v3.4s
+  .long  0x6e23dc53                          // fmul          v19.4s, v2.4s, v3.4s
+  .long  0x4e26f4b5                          // fmax          v21.4s, v5.4s, v6.4s
+  .long  0x4ea6f4b7                          // fmin          v23.4s, v5.4s, v6.4s
+  .long  0x6e23dc11                          // fmul          v17.4s, v0.4s, v3.4s
+  .long  0x4e35f495                          // fmax          v21.4s, v4.4s, v21.4s
+  .long  0x4eb7f497                          // fmin          v23.4s, v4.4s, v23.4s
+  .long  0x4eb3f65b                          // fmin          v27.4s, v18.4s, v19.4s
+  .long  0x52a7d328                          // mov           w8, #0x3e990000
+  .long  0x4f03f619                          // fmov          v25.4s, #1.000000000000000000e+00
+  .long  0x4e33f65c                          // fmax          v28.4s, v18.4s, v19.4s
+  .long  0x4eb7d6b5                          // fsub          v21.4s, v21.4s, v23.4s
+  .long  0x4ebbf63b                          // fmin          v27.4s, v17.4s, v27.4s
+  .long  0x72933348                          // movk          w8, #0x999a
+  .long  0x4ea7d737                          // fsub          v23.4s, v25.4s, v7.4s
+  .long  0x4e3cf63c                          // fmax          v28.4s, v17.4s, v28.4s
+  .long  0x4ebbd652                          // fsub          v18.4s, v18.4s, v27.4s
+  .long  0x6e23deb5                          // fmul          v21.4s, v21.4s, v3.4s
+  .long  0x4e040d16                          // dup           v22.4s, w8
+  .long  0x52a7e2e8                          // mov           w8, #0x3f170000
+  .long  0x6e20dee0                          // fmul          v0.4s, v23.4s, v0.4s
+  .long  0x6e21dee1                          // fmul          v1.4s, v23.4s, v1.4s
+  .long  0x6e22dee2                          // fmul          v2.4s, v23.4s, v2.4s
+  .long  0x4ea3d739                          // fsub          v25.4s, v25.4s, v3.4s
+  .long  0x4ebbd79c                          // fsub          v28.4s, v28.4s, v27.4s
+  .long  0x4ebbd631                          // fsub          v17.4s, v17.4s, v27.4s
+  .long  0x6e32deb2                          // fmul          v18.4s, v21.4s, v18.4s
+  .long  0x728147a8                          // movk          w8, #0xa3d
+  .long  0x4ebbd673                          // fsub          v19.4s, v19.4s, v27.4s
+  .long  0x4e39cc80                          // fmla          v0.4s, v4.4s, v25.4s
+  .long  0x4e39cca1                          // fmla          v1.4s, v5.4s, v25.4s
+  .long  0x4e39ccc2                          // fmla          v2.4s, v6.4s, v25.4s
+  .long  0x4ea0db99                          // fcmeq         v25.4s, v28.4s, #0.0
+  .long  0x6e31deb1                          // fmul          v17.4s, v21.4s, v17.4s
+  .long  0x6e3cfe52                          // fdiv          v18.4s, v18.4s, v28.4s
+  .long  0x4e040d18                          // dup           v24.4s, w8
+  .long  0x52a7bc28                          // mov           w8, #0x3de10000
+  .long  0x6e33deb3                          // fmul          v19.4s, v21.4s, v19.4s
+  .long  0x6e3cfe31                          // fdiv          v17.4s, v17.4s, v28.4s
+  .long  0x4e791e52                          // bic           v18.16b, v18.16b, v25.16b
+  .long  0x7288f5c8                          // movk          w8, #0x47ae
+  .long  0x6e3cfe73                          // fdiv          v19.4s, v19.4s, v28.4s
+  .long  0x4e791e31                          // bic           v17.16b, v17.16b, v25.16b
+  .long  0x6e38de55                          // fmul          v21.4s, v18.4s, v24.4s
+  .long  0x4e040d17                          // dup           v23.4s, w8
+  .long  0x6e38dcbb                          // fmul          v27.4s, v5.4s, v24.4s
+  .long  0x4e791e73                          // bic           v19.16b, v19.16b, v25.16b
+  .long  0x4e31ced5                          // fmla          v21.4s, v22.4s, v17.4s
+  .long  0x4e24cedb                          // fmla          v27.4s, v22.4s, v4.4s
+  .long  0x4e33cef5                          // fmla          v21.4s, v23.4s, v19.4s
+  .long  0x4e26cefb                          // fmla          v27.4s, v23.4s, v6.4s
+  .long  0x6ea0fab5                          // fneg          v21.4s, v21.4s
+  .long  0x4e3bcc75                          // fmla          v21.4s, v3.4s, v27.4s
+  .long  0x6e27dc74                          // fmul          v20.4s, v3.4s, v7.4s
+  .long  0x4e27d47a                          // fadd          v26.4s, v3.4s, v7.4s
+  .long  0x4e35d623                          // fadd          v3.4s, v17.4s, v21.4s
+  .long  0x4e35d651                          // fadd          v17.4s, v18.4s, v21.4s
+  .long  0x6e38de38                          // fmul          v24.4s, v17.4s, v24.4s
+  .long  0x4e35d672                          // fadd          v18.4s, v19.4s, v21.4s
+  .long  0x4e23ced8                          // fmla          v24.4s, v22.4s, v3.4s
+  .long  0x4eb2f633                          // fmin          v19.4s, v17.4s, v18.4s
+  .long  0x4e32cef8                          // fmla          v24.4s, v23.4s, v18.4s
+  .long  0x4eb3f473                          // fmin          v19.4s, v3.4s, v19.4s
+  .long  0x4eb8d479                          // fsub          v25.4s, v3.4s, v24.4s
+  .long  0x6ea0ca76                          // fcmge         v22.4s, v19.4s, #0.0
+  .long  0x4eb3d713                          // fsub          v19.4s, v24.4s, v19.4s
+  .long  0x6e39df19                          // fmul          v25.4s, v24.4s, v25.4s
+  .long  0x6e33ff39                          // fdiv          v25.4s, v25.4s, v19.4s
+  .long  0x4e32f635                          // fmax          v21.4s, v17.4s, v18.4s
+  .long  0x4eb61edb                          // mov           v27.16b, v22.16b
+  .long  0x4e39d719                          // fadd          v25.4s, v24.4s, v25.4s
+  .long  0x4e35f475                          // fmax          v21.4s, v3.4s, v21.4s
+  .long  0x6e791c7b                          // bsl           v27.16b, v3.16b, v25.16b
+  .long  0x4eb8d623                          // fsub          v3.4s, v17.4s, v24.4s
+  .long  0x6e23df03                          // fmul          v3.4s, v24.4s, v3.4s
+  .long  0x6e33fc63                          // fdiv          v3.4s, v3.4s, v19.4s
+  .long  0x4eb61ed9                          // mov           v25.16b, v22.16b
+  .long  0x4e23d703                          // fadd          v3.4s, v24.4s, v3.4s
+  .long  0x6e631e39                          // bsl           v25.16b, v17.16b, v3.16b
+  .long  0x4eb8d651                          // fsub          v17.4s, v18.4s, v24.4s
+  .long  0x6e31df11                          // fmul          v17.4s, v24.4s, v17.4s
+  .long  0x6e33fe31                          // fdiv          v17.4s, v17.4s, v19.4s
+  .long  0x4e31d711                          // fadd          v17.4s, v24.4s, v17.4s
+  .long  0x6e711e56                          // bsl           v22.16b, v18.16b, v17.16b
+  .long  0x4eb8d69c                          // fsub          v28.4s, v20.4s, v24.4s
+  .long  0x4eb8d771                          // fsub          v17.4s, v27.4s, v24.4s
+  .long  0x4eb8d732                          // fsub          v18.4s, v25.4s, v24.4s
+  .long  0x4eb8d6d3                          // fsub          v19.4s, v22.4s, v24.4s
+  .long  0x6eb4e6b7                          // fcmgt         v23.4s, v21.4s, v20.4s
+  .long  0x4eb8d6b5                          // fsub          v21.4s, v21.4s, v24.4s
+  .long  0x6e31df91                          // fmul          v17.4s, v28.4s, v17.4s
+  .long  0x6e32df92                          // fmul          v18.4s, v28.4s, v18.4s
+  .long  0x6e33df93                          // fmul          v19.4s, v28.4s, v19.4s
+  .long  0x6e35fe31                          // fdiv          v17.4s, v17.4s, v21.4s
+  .long  0x6e35fe52                          // fdiv          v18.4s, v18.4s, v21.4s
+  .long  0x6e35fe73                          // fdiv          v19.4s, v19.4s, v21.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb71ee3                          // mov           v3.16b, v23.16b
+  .long  0x4eb71efc                          // mov           v28.16b, v23.16b
+  .long  0x4e31d711                          // fadd          v17.4s, v24.4s, v17.4s
+  .long  0x4e32d712                          // fadd          v18.4s, v24.4s, v18.4s
+  .long  0x4e33d713                          // fadd          v19.4s, v24.4s, v19.4s
+  .long  0x6f00e410                          // movi          v16.2d, #0x0
+  .long  0x6e7b1e23                          // bsl           v3.16b, v17.16b, v27.16b
+  .long  0x6e791e5c                          // bsl           v28.16b, v18.16b, v25.16b
+  .long  0x6e761e77                          // bsl           v23.16b, v19.16b, v22.16b
+  .long  0x4e30f463                          // fmax          v3.4s, v3.4s, v16.4s
+  .long  0x4e30f791                          // fmax          v17.4s, v28.4s, v16.4s
+  .long  0x4e30f6f0                          // fmax          v16.4s, v23.4s, v16.4s
+  .long  0x4e23d400                          // fadd          v0.4s, v0.4s, v3.4s
+  .long  0x4e31d421                          // fadd          v1.4s, v1.4s, v17.4s
+  .long  0x4e30d442                          // fadd          v2.4s, v2.4s, v16.4s
+  .long  0x4eb4d743                          // fsub          v3.4s, v26.4s, v20.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_saturation_aarch64
+.globl _sk_saturation_aarch64
+FUNCTION(_sk_saturation_aarch64)
+_sk_saturation_aarch64:
+  .long  0x6e25dc72                          // fmul          v18.4s, v3.4s, v5.4s
+  .long  0x6e26dc73                          // fmul          v19.4s, v3.4s, v6.4s
+  .long  0x4e22f435                          // fmax          v21.4s, v1.4s, v2.4s
+  .long  0x4ea2f437                          // fmin          v23.4s, v1.4s, v2.4s
+  .long  0x6e24dc71                          // fmul          v17.4s, v3.4s, v4.4s
+  .long  0x4e35f415                          // fmax          v21.4s, v0.4s, v21.4s
+  .long  0x4eb7f417                          // fmin          v23.4s, v0.4s, v23.4s
+  .long  0x4eb3f65b                          // fmin          v27.4s, v18.4s, v19.4s
+  .long  0x52a7d328                          // mov           w8, #0x3e990000
+  .long  0x4f03f619                          // fmov          v25.4s, #1.000000000000000000e+00
+  .long  0x4e33f65c                          // fmax          v28.4s, v18.4s, v19.4s
+  .long  0x4eb7d6b5                          // fsub          v21.4s, v21.4s, v23.4s
+  .long  0x4ebbf63b                          // fmin          v27.4s, v17.4s, v27.4s
+  .long  0x72933348                          // movk          w8, #0x999a
+  .long  0x4ea7d737                          // fsub          v23.4s, v25.4s, v7.4s
+  .long  0x4e3cf63c                          // fmax          v28.4s, v17.4s, v28.4s
+  .long  0x4ebbd652                          // fsub          v18.4s, v18.4s, v27.4s
+  .long  0x6e27deb5                          // fmul          v21.4s, v21.4s, v7.4s
+  .long  0x4e040d16                          // dup           v22.4s, w8
+  .long  0x52a7e2e8                          // mov           w8, #0x3f170000
+  .long  0x6e20dee0                          // fmul          v0.4s, v23.4s, v0.4s
+  .long  0x6e21dee1                          // fmul          v1.4s, v23.4s, v1.4s
+  .long  0x6e22dee2                          // fmul          v2.4s, v23.4s, v2.4s
+  .long  0x4ea3d739                          // fsub          v25.4s, v25.4s, v3.4s
+  .long  0x4ebbd79c                          // fsub          v28.4s, v28.4s, v27.4s
+  .long  0x4ebbd631                          // fsub          v17.4s, v17.4s, v27.4s
+  .long  0x6e32deb2                          // fmul          v18.4s, v21.4s, v18.4s
+  .long  0x728147a8                          // movk          w8, #0xa3d
+  .long  0x4ebbd673                          // fsub          v19.4s, v19.4s, v27.4s
+  .long  0x4e39cc80                          // fmla          v0.4s, v4.4s, v25.4s
+  .long  0x4e39cca1                          // fmla          v1.4s, v5.4s, v25.4s
+  .long  0x4e39ccc2                          // fmla          v2.4s, v6.4s, v25.4s
+  .long  0x4ea0db99                          // fcmeq         v25.4s, v28.4s, #0.0
+  .long  0x6e31deb1                          // fmul          v17.4s, v21.4s, v17.4s
+  .long  0x6e3cfe52                          // fdiv          v18.4s, v18.4s, v28.4s
+  .long  0x4e040d18                          // dup           v24.4s, w8
+  .long  0x52a7bc28                          // mov           w8, #0x3de10000
+  .long  0x6e33deb3                          // fmul          v19.4s, v21.4s, v19.4s
+  .long  0x6e3cfe31                          // fdiv          v17.4s, v17.4s, v28.4s
+  .long  0x4e791e52                          // bic           v18.16b, v18.16b, v25.16b
+  .long  0x7288f5c8                          // movk          w8, #0x47ae
+  .long  0x6e3cfe73                          // fdiv          v19.4s, v19.4s, v28.4s
+  .long  0x4e791e31                          // bic           v17.16b, v17.16b, v25.16b
+  .long  0x6e38de55                          // fmul          v21.4s, v18.4s, v24.4s
+  .long  0x4e040d17                          // dup           v23.4s, w8
+  .long  0x6e38dcbb                          // fmul          v27.4s, v5.4s, v24.4s
+  .long  0x4e791e73                          // bic           v19.16b, v19.16b, v25.16b
+  .long  0x4e31ced5                          // fmla          v21.4s, v22.4s, v17.4s
+  .long  0x4e24cedb                          // fmla          v27.4s, v22.4s, v4.4s
+  .long  0x4e33cef5                          // fmla          v21.4s, v23.4s, v19.4s
+  .long  0x4e26cefb                          // fmla          v27.4s, v23.4s, v6.4s
+  .long  0x6ea0fab5                          // fneg          v21.4s, v21.4s
+  .long  0x4e3bcc75                          // fmla          v21.4s, v3.4s, v27.4s
+  .long  0x6e27dc74                          // fmul          v20.4s, v3.4s, v7.4s
+  .long  0x4e27d47a                          // fadd          v26.4s, v3.4s, v7.4s
+  .long  0x4e35d623                          // fadd          v3.4s, v17.4s, v21.4s
+  .long  0x4e35d651                          // fadd          v17.4s, v18.4s, v21.4s
+  .long  0x6e38de38                          // fmul          v24.4s, v17.4s, v24.4s
+  .long  0x4e35d672                          // fadd          v18.4s, v19.4s, v21.4s
+  .long  0x4e23ced8                          // fmla          v24.4s, v22.4s, v3.4s
+  .long  0x4eb2f633                          // fmin          v19.4s, v17.4s, v18.4s
+  .long  0x4e32cef8                          // fmla          v24.4s, v23.4s, v18.4s
+  .long  0x4eb3f473                          // fmin          v19.4s, v3.4s, v19.4s
+  .long  0x4eb8d479                          // fsub          v25.4s, v3.4s, v24.4s
+  .long  0x6ea0ca76                          // fcmge         v22.4s, v19.4s, #0.0
+  .long  0x4eb3d713                          // fsub          v19.4s, v24.4s, v19.4s
+  .long  0x6e39df19                          // fmul          v25.4s, v24.4s, v25.4s
+  .long  0x6e33ff39                          // fdiv          v25.4s, v25.4s, v19.4s
+  .long  0x4e32f635                          // fmax          v21.4s, v17.4s, v18.4s
+  .long  0x4eb61edb                          // mov           v27.16b, v22.16b
+  .long  0x4e39d719                          // fadd          v25.4s, v24.4s, v25.4s
+  .long  0x4e35f475                          // fmax          v21.4s, v3.4s, v21.4s
+  .long  0x6e791c7b                          // bsl           v27.16b, v3.16b, v25.16b
+  .long  0x4eb8d623                          // fsub          v3.4s, v17.4s, v24.4s
+  .long  0x6e23df03                          // fmul          v3.4s, v24.4s, v3.4s
+  .long  0x6e33fc63                          // fdiv          v3.4s, v3.4s, v19.4s
+  .long  0x4eb61ed9                          // mov           v25.16b, v22.16b
+  .long  0x4e23d703                          // fadd          v3.4s, v24.4s, v3.4s
+  .long  0x6e631e39                          // bsl           v25.16b, v17.16b, v3.16b
+  .long  0x4eb8d651                          // fsub          v17.4s, v18.4s, v24.4s
+  .long  0x6e31df11                          // fmul          v17.4s, v24.4s, v17.4s
+  .long  0x6e33fe31                          // fdiv          v17.4s, v17.4s, v19.4s
+  .long  0x4e31d711                          // fadd          v17.4s, v24.4s, v17.4s
+  .long  0x6e711e56                          // bsl           v22.16b, v18.16b, v17.16b
+  .long  0x4eb8d69c                          // fsub          v28.4s, v20.4s, v24.4s
+  .long  0x4eb8d771                          // fsub          v17.4s, v27.4s, v24.4s
+  .long  0x4eb8d732                          // fsub          v18.4s, v25.4s, v24.4s
+  .long  0x4eb8d6d3                          // fsub          v19.4s, v22.4s, v24.4s
+  .long  0x6eb4e6b7                          // fcmgt         v23.4s, v21.4s, v20.4s
+  .long  0x4eb8d6b5                          // fsub          v21.4s, v21.4s, v24.4s
+  .long  0x6e31df91                          // fmul          v17.4s, v28.4s, v17.4s
+  .long  0x6e32df92                          // fmul          v18.4s, v28.4s, v18.4s
+  .long  0x6e33df93                          // fmul          v19.4s, v28.4s, v19.4s
+  .long  0x6e35fe31                          // fdiv          v17.4s, v17.4s, v21.4s
+  .long  0x6e35fe52                          // fdiv          v18.4s, v18.4s, v21.4s
+  .long  0x6e35fe73                          // fdiv          v19.4s, v19.4s, v21.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb71ee3                          // mov           v3.16b, v23.16b
+  .long  0x4eb71efc                          // mov           v28.16b, v23.16b
+  .long  0x4e31d711                          // fadd          v17.4s, v24.4s, v17.4s
+  .long  0x4e32d712                          // fadd          v18.4s, v24.4s, v18.4s
+  .long  0x4e33d713                          // fadd          v19.4s, v24.4s, v19.4s
+  .long  0x6f00e410                          // movi          v16.2d, #0x0
+  .long  0x6e7b1e23                          // bsl           v3.16b, v17.16b, v27.16b
+  .long  0x6e791e5c                          // bsl           v28.16b, v18.16b, v25.16b
+  .long  0x6e761e77                          // bsl           v23.16b, v19.16b, v22.16b
+  .long  0x4e30f463                          // fmax          v3.4s, v3.4s, v16.4s
+  .long  0x4e30f791                          // fmax          v17.4s, v28.4s, v16.4s
+  .long  0x4e30f6f0                          // fmax          v16.4s, v23.4s, v16.4s
+  .long  0x4e23d400                          // fadd          v0.4s, v0.4s, v3.4s
+  .long  0x4e31d421                          // fadd          v1.4s, v1.4s, v17.4s
+  .long  0x4e30d442                          // fadd          v2.4s, v2.4s, v16.4s
+  .long  0x4eb4d743                          // fsub          v3.4s, v26.4s, v20.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_color_aarch64
+.globl _sk_color_aarch64
+FUNCTION(_sk_color_aarch64)
+_sk_color_aarch64:
+  .long  0x52a7d328                          // mov           w8, #0x3e990000
+  .long  0x72933348                          // movk          w8, #0x999a
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a7e2e8                          // mov           w8, #0x3f170000
+  .long  0x728147a8                          // movk          w8, #0xa3d
+  .long  0x6e27dc33                          // fmul          v19.4s, v1.4s, v7.4s
+  .long  0x4e040d15                          // dup           v21.4s, w8
+  .long  0x52a7bc28                          // mov           w8, #0x3de10000
+  .long  0x6e27dc11                          // fmul          v17.4s, v0.4s, v7.4s
+  .long  0x7288f5c8                          // movk          w8, #0x47ae
+  .long  0x4f03f617                          // fmov          v23.4s, #1.000000000000000000e+00
+  .long  0x6e35de7c                          // fmul          v28.4s, v19.4s, v21.4s
+  .long  0x6e27dc54                          // fmul          v20.4s, v2.4s, v7.4s
+  .long  0x4e040d19                          // dup           v25.4s, w8
+  .long  0x4ea7d6fa                          // fsub          v26.4s, v23.4s, v7.4s
+  .long  0x6e35dcbb                          // fmul          v27.4s, v5.4s, v21.4s
+  .long  0x4e31ce5c                          // fmla          v28.4s, v18.4s, v17.4s
+  .long  0x4ea3d6f7                          // fsub          v23.4s, v23.4s, v3.4s
+  .long  0x6e20df40                          // fmul          v0.4s, v26.4s, v0.4s
+  .long  0x6e21df41                          // fmul          v1.4s, v26.4s, v1.4s
+  .long  0x6e22df42                          // fmul          v2.4s, v26.4s, v2.4s
+  .long  0x4e24ce5b                          // fmla          v27.4s, v18.4s, v4.4s
+  .long  0x4e34cf3c                          // fmla          v28.4s, v25.4s, v20.4s
+  .long  0x4e37cc80                          // fmla          v0.4s, v4.4s, v23.4s
+  .long  0x4e37cca1                          // fmla          v1.4s, v5.4s, v23.4s
+  .long  0x4e37ccc2                          // fmla          v2.4s, v6.4s, v23.4s
+  .long  0x4e26cf3b                          // fmla          v27.4s, v25.4s, v6.4s
+  .long  0x6ea0fb97                          // fneg          v23.4s, v28.4s
+  .long  0x4e3bcc77                          // fmla          v23.4s, v3.4s, v27.4s
+  .long  0x6e27dc70                          // fmul          v16.4s, v3.4s, v7.4s
+  .long  0x4e27d478                          // fadd          v24.4s, v3.4s, v7.4s
+  .long  0x4e37d623                          // fadd          v3.4s, v17.4s, v23.4s
+  .long  0x4e37d671                          // fadd          v17.4s, v19.4s, v23.4s
+  .long  0x6e35de35                          // fmul          v21.4s, v17.4s, v21.4s
+  .long  0x4e37d693                          // fadd          v19.4s, v20.4s, v23.4s
+  .long  0x4e23ce55                          // fmla          v21.4s, v18.4s, v3.4s
+  .long  0x4eb3f634                          // fmin          v20.4s, v17.4s, v19.4s
+  .long  0x4e33cf35                          // fmla          v21.4s, v25.4s, v19.4s
+  .long  0x4eb4f474                          // fmin          v20.4s, v3.4s, v20.4s
+  .long  0x4eb5d47a                          // fsub          v26.4s, v3.4s, v21.4s
+  .long  0x6ea0ca92                          // fcmge         v18.4s, v20.4s, #0.0
+  .long  0x4eb4d6b4                          // fsub          v20.4s, v21.4s, v20.4s
+  .long  0x6e3adeba                          // fmul          v26.4s, v21.4s, v26.4s
+  .long  0x6e34ff5a                          // fdiv          v26.4s, v26.4s, v20.4s
+  .long  0x4e33f637                          // fmax          v23.4s, v17.4s, v19.4s
+  .long  0x4eb21e5b                          // mov           v27.16b, v18.16b
+  .long  0x4e3ad6ba                          // fadd          v26.4s, v21.4s, v26.4s
+  .long  0x4e37f477                          // fmax          v23.4s, v3.4s, v23.4s
+  .long  0x6e7a1c7b                          // bsl           v27.16b, v3.16b, v26.16b
+  .long  0x4eb5d623                          // fsub          v3.4s, v17.4s, v21.4s
+  .long  0x6e23dea3                          // fmul          v3.4s, v21.4s, v3.4s
+  .long  0x6e34fc63                          // fdiv          v3.4s, v3.4s, v20.4s
+  .long  0x4eb21e5a                          // mov           v26.16b, v18.16b
+  .long  0x4e23d6a3                          // fadd          v3.4s, v21.4s, v3.4s
+  .long  0x6e631e3a                          // bsl           v26.16b, v17.16b, v3.16b
+  .long  0x4eb5d671                          // fsub          v17.4s, v19.4s, v21.4s
+  .long  0x6e31deb1                          // fmul          v17.4s, v21.4s, v17.4s
+  .long  0x6e34fe31                          // fdiv          v17.4s, v17.4s, v20.4s
+  .long  0x4e31d6b1                          // fadd          v17.4s, v21.4s, v17.4s
+  .long  0x6e711e72                          // bsl           v18.16b, v19.16b, v17.16b
+  .long  0x4eb5d61c                          // fsub          v28.4s, v16.4s, v21.4s
+  .long  0x4eb5d771                          // fsub          v17.4s, v27.4s, v21.4s
+  .long  0x4eb5d753                          // fsub          v19.4s, v26.4s, v21.4s
+  .long  0x4eb5d654                          // fsub          v20.4s, v18.4s, v21.4s
+  .long  0x6eb0e6f9                          // fcmgt         v25.4s, v23.4s, v16.4s
+  .long  0x4eb5d6f7                          // fsub          v23.4s, v23.4s, v21.4s
+  .long  0x6e31df91                          // fmul          v17.4s, v28.4s, v17.4s
+  .long  0x6e33df93                          // fmul          v19.4s, v28.4s, v19.4s
+  .long  0x6e34df94                          // fmul          v20.4s, v28.4s, v20.4s
+  .long  0x6e37fe31                          // fdiv          v17.4s, v17.4s, v23.4s
+  .long  0x6e37fe73                          // fdiv          v19.4s, v19.4s, v23.4s
+  .long  0x6e37fe94                          // fdiv          v20.4s, v20.4s, v23.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb91f23                          // mov           v3.16b, v25.16b
+  .long  0x4eb91f3c                          // mov           v28.16b, v25.16b
+  .long  0x4e31d6b1                          // fadd          v17.4s, v21.4s, v17.4s
+  .long  0x4e33d6b3                          // fadd          v19.4s, v21.4s, v19.4s
+  .long  0x4e34d6b4                          // fadd          v20.4s, v21.4s, v20.4s
+  .long  0x6f00e416                          // movi          v22.2d, #0x0
+  .long  0x6e7b1e23                          // bsl           v3.16b, v17.16b, v27.16b
+  .long  0x6e7a1e7c                          // bsl           v28.16b, v19.16b, v26.16b
+  .long  0x6e721e99                          // bsl           v25.16b, v20.16b, v18.16b
+  .long  0x4e36f463                          // fmax          v3.4s, v3.4s, v22.4s
+  .long  0x4e36f791                          // fmax          v17.4s, v28.4s, v22.4s
+  .long  0x4e36f732                          // fmax          v18.4s, v25.4s, v22.4s
+  .long  0x4e23d400                          // fadd          v0.4s, v0.4s, v3.4s
+  .long  0x4e31d421                          // fadd          v1.4s, v1.4s, v17.4s
+  .long  0x4e32d442                          // fadd          v2.4s, v2.4s, v18.4s
+  .long  0x4eb0d703                          // fsub          v3.4s, v24.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_luminosity_aarch64
+.globl _sk_luminosity_aarch64
+FUNCTION(_sk_luminosity_aarch64)
+_sk_luminosity_aarch64:
+  .long  0x52a7d328                          // mov           w8, #0x3e990000
+  .long  0x72933348                          // movk          w8, #0x999a
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a7e2e8                          // mov           w8, #0x3f170000
+  .long  0x728147a8                          // movk          w8, #0xa3d
+  .long  0x6e25dc73                          // fmul          v19.4s, v3.4s, v5.4s
+  .long  0x4e040d15                          // dup           v21.4s, w8
+  .long  0x52a7bc28                          // mov           w8, #0x3de10000
+  .long  0x6e24dc71                          // fmul          v17.4s, v3.4s, v4.4s
+  .long  0x7288f5c8                          // movk          w8, #0x47ae
+  .long  0x4f03f617                          // fmov          v23.4s, #1.000000000000000000e+00
+  .long  0x6e35de7b                          // fmul          v27.4s, v19.4s, v21.4s
+  .long  0x6e26dc74                          // fmul          v20.4s, v3.4s, v6.4s
+  .long  0x6e27dc70                          // fmul          v16.4s, v3.4s, v7.4s
+  .long  0x4e27d478                          // fadd          v24.4s, v3.4s, v7.4s
+  .long  0x4e040d19                          // dup           v25.4s, w8
+  .long  0x4ea7d6fa                          // fsub          v26.4s, v23.4s, v7.4s
+  .long  0x4ea3d6e3                          // fsub          v3.4s, v23.4s, v3.4s
+  .long  0x6e35dc37                          // fmul          v23.4s, v1.4s, v21.4s
+  .long  0x4e31ce5b                          // fmla          v27.4s, v18.4s, v17.4s
+  .long  0x4e20ce57                          // fmla          v23.4s, v18.4s, v0.4s
+  .long  0x4e34cf3b                          // fmla          v27.4s, v25.4s, v20.4s
+  .long  0x6e20df5c                          // fmul          v28.4s, v26.4s, v0.4s
+  .long  0x4e22cf37                          // fmla          v23.4s, v25.4s, v2.4s
+  .long  0x6ea0fb60                          // fneg          v0.4s, v27.4s
+  .long  0x6e21df41                          // fmul          v1.4s, v26.4s, v1.4s
+  .long  0x6e22df5a                          // fmul          v26.4s, v26.4s, v2.4s
+  .long  0x4e37cce0                          // fmla          v0.4s, v7.4s, v23.4s
+  .long  0x4e23cc9c                          // fmla          v28.4s, v4.4s, v3.4s
+  .long  0x4e23cca1                          // fmla          v1.4s, v5.4s, v3.4s
+  .long  0x4e23ccda                          // fmla          v26.4s, v6.4s, v3.4s
+  .long  0x4e20d663                          // fadd          v3.4s, v19.4s, v0.4s
+  .long  0x4e20d622                          // fadd          v2.4s, v17.4s, v0.4s
+  .long  0x4e20d680                          // fadd          v0.4s, v20.4s, v0.4s
+  .long  0x6e35dc74                          // fmul          v20.4s, v3.4s, v21.4s
+  .long  0x4e22ce54                          // fmla          v20.4s, v18.4s, v2.4s
+  .long  0x4ea0f471                          // fmin          v17.4s, v3.4s, v0.4s
+  .long  0x4e20cf34                          // fmla          v20.4s, v25.4s, v0.4s
+  .long  0x4eb1f451                          // fmin          v17.4s, v2.4s, v17.4s
+  .long  0x4eb4d457                          // fsub          v23.4s, v2.4s, v20.4s
+  .long  0x6ea0ca32                          // fcmge         v18.4s, v17.4s, #0.0
+  .long  0x4eb1d691                          // fsub          v17.4s, v20.4s, v17.4s
+  .long  0x6e37de97                          // fmul          v23.4s, v20.4s, v23.4s
+  .long  0x6e31fef7                          // fdiv          v23.4s, v23.4s, v17.4s
+  .long  0x4e20f473                          // fmax          v19.4s, v3.4s, v0.4s
+  .long  0x4eb21e59                          // mov           v25.16b, v18.16b
+  .long  0x4e37d697                          // fadd          v23.4s, v20.4s, v23.4s
+  .long  0x4e33f453                          // fmax          v19.4s, v2.4s, v19.4s
+  .long  0x6e771c59                          // bsl           v25.16b, v2.16b, v23.16b
+  .long  0x4eb4d462                          // fsub          v2.4s, v3.4s, v20.4s
+  .long  0x6e22de82                          // fmul          v2.4s, v20.4s, v2.4s
+  .long  0x6e31fc42                          // fdiv          v2.4s, v2.4s, v17.4s
+  .long  0x4eb21e57                          // mov           v23.16b, v18.16b
+  .long  0x4e22d682                          // fadd          v2.4s, v20.4s, v2.4s
+  .long  0x6e621c77                          // bsl           v23.16b, v3.16b, v2.16b
+  .long  0x4eb4d403                          // fsub          v3.4s, v0.4s, v20.4s
+  .long  0x6e23de83                          // fmul          v3.4s, v20.4s, v3.4s
+  .long  0x6e31fc63                          // fdiv          v3.4s, v3.4s, v17.4s
+  .long  0x4e23d683                          // fadd          v3.4s, v20.4s, v3.4s
+  .long  0x6e631c12                          // bsl           v18.16b, v0.16b, v3.16b
+  .long  0x4eb4d61b                          // fsub          v27.4s, v16.4s, v20.4s
+  .long  0x4eb4d720                          // fsub          v0.4s, v25.4s, v20.4s
+  .long  0x4eb4d6e3                          // fsub          v3.4s, v23.4s, v20.4s
+  .long  0x4eb4d651                          // fsub          v17.4s, v18.4s, v20.4s
+  .long  0x6eb0e675                          // fcmgt         v21.4s, v19.4s, v16.4s
+  .long  0x4eb4d673                          // fsub          v19.4s, v19.4s, v20.4s
+  .long  0x6e20df60                          // fmul          v0.4s, v27.4s, v0.4s
+  .long  0x6e23df63                          // fmul          v3.4s, v27.4s, v3.4s
+  .long  0x6e31df71                          // fmul          v17.4s, v27.4s, v17.4s
+  .long  0x6e33fc00                          // fdiv          v0.4s, v0.4s, v19.4s
+  .long  0x6e33fc63                          // fdiv          v3.4s, v3.4s, v19.4s
+  .long  0x6e33fe31                          // fdiv          v17.4s, v17.4s, v19.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb51ea2                          // mov           v2.16b, v21.16b
+  .long  0x4eb51ebb                          // mov           v27.16b, v21.16b
+  .long  0x4e20d680                          // fadd          v0.4s, v20.4s, v0.4s
+  .long  0x4e23d683                          // fadd          v3.4s, v20.4s, v3.4s
+  .long  0x4e31d691                          // fadd          v17.4s, v20.4s, v17.4s
+  .long  0x6f00e416                          // movi          v22.2d, #0x0
+  .long  0x6e791c02                          // bsl           v2.16b, v0.16b, v25.16b
+  .long  0x6e771c7b                          // bsl           v27.16b, v3.16b, v23.16b
+  .long  0x6e721e35                          // bsl           v21.16b, v17.16b, v18.16b
+  .long  0x4e36f440                          // fmax          v0.4s, v2.4s, v22.4s
+  .long  0x4e36f762                          // fmax          v2.4s, v27.4s, v22.4s
+  .long  0x4e36f6a3                          // fmax          v3.4s, v21.4s, v22.4s
+  .long  0x4e20d780                          // fadd          v0.4s, v28.4s, v0.4s
+  .long  0x4e22d421                          // fadd          v1.4s, v1.4s, v2.4s
+  .long  0x4e23d742                          // fadd          v2.4s, v26.4s, v3.4s
+  .long  0x4eb0d703                          // fsub          v3.4s, v24.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_srcover_rgba_8888_aarch64
+.globl _sk_srcover_rgba_8888_aarch64
+FUNCTION(_sk_srcover_rgba_8888_aarch64)
+_sk_srcover_rgba_8888_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020908                          // add           x8, x8, x2, lsl #2
+  .long  0xb5000504                          // cbnz          x4, 115c <sk_srcover_rgba_8888_aarch64+0xac>
+  .long  0x3dc00104                          // ldr           q4, [x8]
+  .long  0x6f00e625                          // movi          v5.2d, #0xff000000ff
+  .long  0x6f380486                          // ushr          v6.4s, v4.4s, #8
+  .long  0x6f300487                          // ushr          v7.4s, v4.4s, #16
+  .long  0x6f280490                          // ushr          v16.4s, v4.4s, #24
+  .long  0x4f03f611                          // fmov          v17.4s, #1.000000000000000000e+00
+  .long  0x52a86fe9                          // mov           w9, #0x437f0000
+  .long  0x4e251cc6                          // and           v6.16b, v6.16b, v5.16b
+  .long  0x4e251cf2                          // and           v18.16b, v7.16b, v5.16b
+  .long  0x4e21da07                          // scvtf         v7.4s, v16.4s
+  .long  0x4ea3d630                          // fsub          v16.4s, v17.4s, v3.4s
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x4e251c84                          // and           v4.16b, v4.16b, v5.16b
+  .long  0x4e21d8c5                          // scvtf         v5.4s, v6.4s
+  .long  0x6e31dc21                          // fmul          v1.4s, v1.4s, v17.4s
+  .long  0x4e21d884                          // scvtf         v4.4s, v4.4s
+  .long  0x6e31dc00                          // fmul          v0.4s, v0.4s, v17.4s
+  .long  0x4e25ce01                          // fmla          v1.4s, v16.4s, v5.4s
+  .long  0x4e21da46                          // scvtf         v6.4s, v18.4s
+  .long  0x6e31dc42                          // fmul          v2.4s, v2.4s, v17.4s
+  .long  0x6e31dc63                          // fmul          v3.4s, v3.4s, v17.4s
+  .long  0x4e24ce00                          // fmla          v0.4s, v16.4s, v4.4s
+  .long  0x6e21a831                          // fcvtnu        v17.4s, v1.4s
+  .long  0x4e26ce02                          // fmla          v2.4s, v16.4s, v6.4s
+  .long  0x4e27ce03                          // fmla          v3.4s, v16.4s, v7.4s
+  .long  0x6e21a810                          // fcvtnu        v16.4s, v0.4s
+  .long  0x4f285631                          // shl           v17.4s, v17.4s, #8
+  .long  0x4eb01e30                          // orr           v16.16b, v17.16b, v16.16b
+  .long  0x6e21a851                          // fcvtnu        v17.4s, v2.4s
+  .long  0x4f305631                          // shl           v17.4s, v17.4s, #16
+  .long  0x4eb11e10                          // orr           v16.16b, v16.16b, v17.16b
+  .long  0x6e21a871                          // fcvtnu        v17.4s, v3.4s
+  .long  0x4f385631                          // shl           v17.4s, v17.4s, #24
+  .long  0x4eb11e10                          // orr           v16.16b, v16.16b, v17.16b
+  .long  0xb5000284                          // cbnz          x4, 1198 <sk_srcover_rgba_8888_aarch64+0xe8>
+  .long  0x3d800110                          // str           q16, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x6f00e404                          // movi          v4.2d, #0x0
+  .long  0x54000140                          // b.eq          1190 <sk_srcover_rgba_8888_aarch64+0xe0>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          1188 <sk_srcover_rgba_8888_aarch64+0xd8>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffa61                          // b.ne          10c4 <sk_srcover_rgba_8888_aarch64+0x14>  // b.any
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x4e040fe4                          // dup           v4.4s, wzr
+  .long  0x4d408124                          // ld1           {v4.s}[2], [x9]
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0d409124                          // ld1           {v4.s}[1], [x9]
+  .long  0x0d408104                          // ld1           {v4.s}[0], [x8]
+  .long  0x17ffffcc                          // b             10c4 <sk_srcover_rgba_8888_aarch64+0x14>
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x54000120                          // b.eq          11c4 <sk_srcover_rgba_8888_aarch64+0x114>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000a0                          // b.eq          11bc <sk_srcover_rgba_8888_aarch64+0x10c>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffd01                          // b.ne          1150 <sk_srcover_rgba_8888_aarch64+0xa0>  // b.any
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x4d008130                          // st1           {v16.s}[2], [x9]
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0d009130                          // st1           {v16.s}[1], [x9]
+  .long  0x0d008110                          // st1           {v16.s}[0], [x8]
+  .long  0x17ffffe2                          // b             1150 <sk_srcover_rgba_8888_aarch64+0xa0>
+
+HIDDEN _sk_clamp_0_aarch64
+.globl _sk_clamp_0_aarch64
+FUNCTION(_sk_clamp_0_aarch64)
+_sk_clamp_0_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6f00e410                          // movi          v16.2d, #0x0
+  .long  0x4e30f400                          // fmax          v0.4s, v0.4s, v16.4s
+  .long  0x4e30f421                          // fmax          v1.4s, v1.4s, v16.4s
+  .long  0x4e30f442                          // fmax          v2.4s, v2.4s, v16.4s
+  .long  0x4e30f463                          // fmax          v3.4s, v3.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_clamp_1_aarch64
+.globl _sk_clamp_1_aarch64
+FUNCTION(_sk_clamp_1_aarch64)
+_sk_clamp_1_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4eb0f400                          // fmin          v0.4s, v0.4s, v16.4s
+  .long  0x4eb0f421                          // fmin          v1.4s, v1.4s, v16.4s
+  .long  0x4eb0f442                          // fmin          v2.4s, v2.4s, v16.4s
+  .long  0x4eb0f463                          // fmin          v3.4s, v3.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_clamp_a_aarch64
+.globl _sk_clamp_a_aarch64
+FUNCTION(_sk_clamp_a_aarch64)
+_sk_clamp_a_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4eb0f463                          // fmin          v3.4s, v3.4s, v16.4s
+  .long  0x4ea3f400                          // fmin          v0.4s, v0.4s, v3.4s
+  .long  0x4ea3f421                          // fmin          v1.4s, v1.4s, v3.4s
+  .long  0x4ea3f442                          // fmin          v2.4s, v2.4s, v3.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_set_rgb_aarch64
+.globl _sk_set_rgb_aarch64
+FUNCTION(_sk_set_rgb_aarch64)
+_sk_set_rgb_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0x4ddfc920                          // ld1r          {v0.4s}, [x9], #4
+  .long  0x91002108                          // add           x8, x8, #0x8
+  .long  0x4d40c902                          // ld1r          {v2.4s}, [x8]
+  .long  0x4d40c921                          // ld1r          {v1.4s}, [x9]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_swap_rb_aarch64
+.globl _sk_swap_rb_aarch64
+FUNCTION(_sk_swap_rb_aarch64)
+_sk_swap_rb_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea01c10                          // mov           v16.16b, v0.16b
+  .long  0x4ea21c40                          // mov           v0.16b, v2.16b
+  .long  0x4eb01e02                          // mov           v2.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_swap_aarch64
+.globl _sk_swap_aarch64
+FUNCTION(_sk_swap_aarch64)
+_sk_swap_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea31c70                          // mov           v16.16b, v3.16b
+  .long  0x4ea21c51                          // mov           v17.16b, v2.16b
+  .long  0x4ea11c32                          // mov           v18.16b, v1.16b
+  .long  0x4ea01c13                          // mov           v19.16b, v0.16b
+  .long  0x4ea41c80                          // mov           v0.16b, v4.16b
+  .long  0x4ea51ca1                          // mov           v1.16b, v5.16b
+  .long  0x4ea61cc2                          // mov           v2.16b, v6.16b
+  .long  0x4ea71ce3                          // mov           v3.16b, v7.16b
+  .long  0x4eb31e64                          // mov           v4.16b, v19.16b
+  .long  0x4eb21e45                          // mov           v5.16b, v18.16b
+  .long  0x4eb11e26                          // mov           v6.16b, v17.16b
+  .long  0x4eb01e07                          // mov           v7.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_move_src_dst_aarch64
+.globl _sk_move_src_dst_aarch64
+FUNCTION(_sk_move_src_dst_aarch64)
+_sk_move_src_dst_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea01c04                          // mov           v4.16b, v0.16b
+  .long  0x4ea11c25                          // mov           v5.16b, v1.16b
+  .long  0x4ea21c46                          // mov           v6.16b, v2.16b
+  .long  0x4ea31c67                          // mov           v7.16b, v3.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_move_dst_src_aarch64
+.globl _sk_move_dst_src_aarch64
+FUNCTION(_sk_move_dst_src_aarch64)
+_sk_move_dst_src_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea41c80                          // mov           v0.16b, v4.16b
+  .long  0x4ea51ca1                          // mov           v1.16b, v5.16b
+  .long  0x4ea61cc2                          // mov           v2.16b, v6.16b
+  .long  0x4ea71ce3                          // mov           v3.16b, v7.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_premul_aarch64
+.globl _sk_premul_aarch64
+FUNCTION(_sk_premul_aarch64)
+_sk_premul_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e23dc00                          // fmul          v0.4s, v0.4s, v3.4s
+  .long  0x6e23dc21                          // fmul          v1.4s, v1.4s, v3.4s
+  .long  0x6e23dc42                          // fmul          v2.4s, v2.4s, v3.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_unpremul_aarch64
+.globl _sk_unpremul_aarch64
+FUNCTION(_sk_unpremul_aarch64)
+_sk_unpremul_aarch64:
+  .long  0x4f03f611                          // fmov          v17.4s, #1.000000000000000000e+00
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4ea0d870                          // fcmeq         v16.4s, v3.4s, #0.0
+  .long  0x6e23fe31                          // fdiv          v17.4s, v17.4s, v3.4s
+  .long  0x4e701e30                          // bic           v16.16b, v17.16b, v16.16b
+  .long  0x6e20de00                          // fmul          v0.4s, v16.4s, v0.4s
+  .long  0x6e21de01                          // fmul          v1.4s, v16.4s, v1.4s
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_from_srgb_aarch64
+.globl _sk_from_srgb_aarch64
+FUNCTION(_sk_from_srgb_aarch64)
+_sk_from_srgb_aarch64:
+  .long  0x52a7d328                          // mov           w8, #0x3e990000
+  .long  0x72933348                          // movk          w8, #0x999a
+  .long  0x4e040d10                          // dup           v16.4s, w8
+  .long  0x52a7e648                          // mov           w8, #0x3f320000
+  .long  0x7291eb88                          // movk          w8, #0x8f5c
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x52a76468                          // mov           w8, #0x3b230000
+  .long  0x729ae148                          // movk          w8, #0xd70a
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a7b3c8                          // mov           w8, #0x3d9e0000
+  .long  0x72907228                          // movk          w8, #0x8391
+  .long  0x6e22dc54                          // fmul          v20.4s, v2.4s, v2.4s
+  .long  0x4eb11e35                          // mov           v21.16b, v17.16b
+  .long  0x4eb11e37                          // mov           v23.16b, v17.16b
+  .long  0x4e22ce11                          // fmla          v17.4s, v16.4s, v2.4s
+  .long  0x4eb21e56                          // mov           v22.16b, v18.16b
+  .long  0x4eb21e58                          // mov           v24.16b, v18.16b
+  .long  0x4e34ce32                          // fmla          v18.4s, v17.4s, v20.4s
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x52a7ac28                          // mov           w8, #0x3d610000
+  .long  0x6e20dc13                          // fmul          v19.4s, v0.4s, v0.4s
+  .long  0x7288f5c8                          // movk          w8, #0x47ae
+  .long  0x4e20ce15                          // fmla          v21.4s, v16.4s, v0.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e21dc34                          // fmul          v20.4s, v1.4s, v1.4s
+  .long  0x4e33ceb6                          // fmla          v22.4s, v21.4s, v19.4s
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x4e21ce17                          // fmla          v23.4s, v16.4s, v1.4s
+  .long  0x6e31dc15                          // fmul          v21.4s, v0.4s, v17.4s
+  .long  0x6ea0e660                          // fcmgt         v0.4s, v19.4s, v0.4s
+  .long  0x6e31dc30                          // fmul          v16.4s, v1.4s, v17.4s
+  .long  0x6ea1e661                          // fcmgt         v1.4s, v19.4s, v1.4s
+  .long  0x6e31dc51                          // fmul          v17.4s, v2.4s, v17.4s
+  .long  0x6ea2e662                          // fcmgt         v2.4s, v19.4s, v2.4s
+  .long  0x4e34cef8                          // fmla          v24.4s, v23.4s, v20.4s
+  .long  0x6e761ea0                          // bsl           v0.16b, v21.16b, v22.16b
+  .long  0x6e781e01                          // bsl           v1.16b, v16.16b, v24.16b
+  .long  0x6e721e22                          // bsl           v2.16b, v17.16b, v18.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_to_srgb_aarch64
+.globl _sk_to_srgb_aarch64
+FUNCTION(_sk_to_srgb_aarch64)
+_sk_to_srgb_aarch64:
+  .long  0x52a829c8                          // mov           w8, #0x414e0000
+  .long  0x72970a48                          // movk          w8, #0xb852
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x52b76408                          // mov           w8, #0xbb200000
+  .long  0x729ae728                          // movk          w8, #0xd739
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a77308                          // mov           w8, #0x3b980000
+  .long  0x72963508                          // movk          w8, #0xb1a8
+  .long  0x6ea1d810                          // frsqrte       v16.4s, v0.4s
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x52a78c48                          // mov           w8, #0x3c620000
+  .long  0x6ea1d834                          // frsqrte       v20.4s, v1.4s
+  .long  0x6ea1d855                          // frsqrte       v21.4s, v2.4s
+  .long  0x7293f748                          // movk          w8, #0x9fba
+  .long  0x6e30de16                          // fmul          v22.4s, v16.4s, v16.4s
+  .long  0x6e34de97                          // fmul          v23.4s, v20.4s, v20.4s
+  .long  0x6e35deb8                          // fmul          v24.4s, v21.4s, v21.4s
+  .long  0x4eb6fc16                          // frsqrts       v22.4s, v0.4s, v22.4s
+  .long  0x6e31dc19                          // fmul          v25.4s, v0.4s, v17.4s
+  .long  0x6ea0e660                          // fcmgt         v0.4s, v19.4s, v0.4s
+  .long  0x4eb7fc37                          // frsqrts       v23.4s, v1.4s, v23.4s
+  .long  0x6e31dc3a                          // fmul          v26.4s, v1.4s, v17.4s
+  .long  0x6ea1e661                          // fcmgt         v1.4s, v19.4s, v1.4s
+  .long  0x4eb8fc58                          // frsqrts       v24.4s, v2.4s, v24.4s
+  .long  0x6e31dc51                          // fmul          v17.4s, v2.4s, v17.4s
+  .long  0x6ea2e662                          // fcmgt         v2.4s, v19.4s, v2.4s
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x52a7f208                          // mov           w8, #0x3f900000
+  .long  0x72947ae8                          // movk          w8, #0xa3d7
+  .long  0x6e36de10                          // fmul          v16.4s, v16.4s, v22.4s
+  .long  0x4e040d16                          // dup           v22.4s, w8
+  .long  0x6e37de94                          // fmul          v20.4s, v20.4s, v23.4s
+  .long  0x4eb31e77                          // mov           v23.16b, v19.16b
+  .long  0x6e38deb5                          // fmul          v21.4s, v21.4s, v24.4s
+  .long  0x4eb31e78                          // mov           v24.16b, v19.16b
+  .long  0x52a7c208                          // mov           w8, #0x3e100000
+  .long  0x4e30ce57                          // fmla          v23.4s, v18.4s, v16.4s
+  .long  0x4e34ce58                          // fmla          v24.4s, v18.4s, v20.4s
+  .long  0x4e35ce53                          // fmla          v19.4s, v18.4s, v21.4s
+  .long  0x4eb61ed2                          // mov           v18.16b, v22.16b
+  .long  0x7298c988                          // movk          w8, #0xc64c
+  .long  0x4e30cef2                          // fmla          v18.4s, v23.4s, v16.4s
+  .long  0x4eb61ed7                          // mov           v23.16b, v22.16b
+  .long  0x4e35ce76                          // fmla          v22.4s, v19.4s, v21.4s
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x4e33d610                          // fadd          v16.4s, v16.4s, v19.4s
+  .long  0x4e34cf17                          // fmla          v23.4s, v24.4s, v20.4s
+  .long  0x4e33d694                          // fadd          v20.4s, v20.4s, v19.4s
+  .long  0x4e33d6b3                          // fadd          v19.4s, v21.4s, v19.4s
+  .long  0x4ea1da15                          // frecpe        v21.4s, v16.4s
+  .long  0x4e35fe10                          // frecps        v16.4s, v16.4s, v21.4s
+  .long  0x6e30deb0                          // fmul          v16.4s, v21.4s, v16.4s
+  .long  0x4ea1da95                          // frecpe        v21.4s, v20.4s
+  .long  0x4e35fe94                          // frecps        v20.4s, v20.4s, v21.4s
+  .long  0x6e34deb4                          // fmul          v20.4s, v21.4s, v20.4s
+  .long  0x4ea1da75                          // frecpe        v21.4s, v19.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e35fe73                          // frecps        v19.4s, v19.4s, v21.4s
+  .long  0x6e33deb3                          // fmul          v19.4s, v21.4s, v19.4s
+  .long  0x6e30de50                          // fmul          v16.4s, v18.4s, v16.4s
+  .long  0x6e34def2                          // fmul          v18.4s, v23.4s, v20.4s
+  .long  0x6e33ded3                          // fmul          v19.4s, v22.4s, v19.4s
+  .long  0x6e701f20                          // bsl           v0.16b, v25.16b, v16.16b
+  .long  0x6e721f41                          // bsl           v1.16b, v26.16b, v18.16b
+  .long  0x6e731e22                          // bsl           v2.16b, v17.16b, v19.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_rgb_to_hsl_aarch64
+.globl _sk_rgb_to_hsl_aarch64
+FUNCTION(_sk_rgb_to_hsl_aarch64)
+_sk_rgb_to_hsl_aarch64:
+  .long  0x4e21f410                          // fmax          v16.4s, v0.4s, v1.4s
+  .long  0x4ea1f411                          // fmin          v17.4s, v0.4s, v1.4s
+  .long  0x6ea1e454                          // fcmgt         v20.4s, v2.4s, v1.4s
+  .long  0x4f00f715                          // fmov          v21.4s, #6.000000000000000000e+00
+  .long  0x4e22f610                          // fmax          v16.4s, v16.4s, v2.4s
+  .long  0x4ea2f631                          // fmin          v17.4s, v17.4s, v2.4s
+  .long  0x4f03f612                          // fmov          v18.4s, #1.000000000000000000e+00
+  .long  0x4e341eb4                          // and           v20.16b, v21.16b, v20.16b
+  .long  0x4eb1d615                          // fsub          v21.4s, v16.4s, v17.4s
+  .long  0x4ea2d433                          // fsub          v19.4s, v1.4s, v2.4s
+  .long  0x4ea0d456                          // fsub          v22.4s, v2.4s, v0.4s
+  .long  0x4f026417                          // movi          v23.4s, #0x40, lsl #24
+  .long  0x6e35fe42                          // fdiv          v2.4s, v18.4s, v21.4s
+  .long  0x4ea1d418                          // fsub          v24.4s, v0.4s, v1.4s
+  .long  0x4f00f619                          // fmov          v25.4s, #4.000000000000000000e+00
+  .long  0x4f0167fa                          // movi          v26.4s, #0x3f, lsl #24
+  .long  0x4eb0d6f2                          // fsub          v18.4s, v23.4s, v16.4s
+  .long  0x4e36cc57                          // fmla          v23.4s, v2.4s, v22.4s
+  .long  0x4e31e616                          // fcmeq         v22.4s, v16.4s, v17.4s
+  .long  0x4e20e600                          // fcmeq         v0.4s, v16.4s, v0.4s
+  .long  0x4e21e601                          // fcmeq         v1.4s, v16.4s, v1.4s
+  .long  0x4e31d610                          // fadd          v16.4s, v16.4s, v17.4s
+  .long  0x52a7c548                          // mov           w8, #0x3e2a0000
+  .long  0x4e33cc54                          // fmla          v20.4s, v2.4s, v19.4s
+  .long  0x4e38cc59                          // fmla          v25.4s, v2.4s, v24.4s
+  .long  0x6e3ade02                          // fmul          v2.4s, v16.4s, v26.4s
+  .long  0x72955568                          // movk          w8, #0xaaab
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb1d651                          // fsub          v17.4s, v18.4s, v17.4s
+  .long  0x6ebae452                          // fcmgt         v18.4s, v2.4s, v26.4s
+  .long  0x6e791ee1                          // bsl           v1.16b, v23.16b, v25.16b
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x6e701e32                          // bsl           v18.16b, v17.16b, v16.16b
+  .long  0x6e611e80                          // bsl           v0.16b, v20.16b, v1.16b
+  .long  0x6e32fea1                          // fdiv          v1.4s, v21.4s, v18.4s
+  .long  0x6e33dc00                          // fmul          v0.4s, v0.4s, v19.4s
+  .long  0x4e761c00                          // bic           v0.16b, v0.16b, v22.16b
+  .long  0x4e761c21                          // bic           v1.16b, v1.16b, v22.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_hsl_to_rgb_aarch64
+.globl _sk_hsl_to_rgb_aarch64
+FUNCTION(_sk_hsl_to_rgb_aarch64)
+_sk_hsl_to_rgb_aarch64:
+  .long  0x52a7d548                          // mov           w8, #0x3eaa0000
+  .long  0x72955568                          // movk          w8, #0xaaab
+  .long  0x4e040d14                          // dup           v20.4s, w8
+  .long  0x52a7e548                          // mov           w8, #0x3f2a0000
+  .long  0x72955568                          // movk          w8, #0xaaab
+  .long  0x4f0167f1                          // movi          v17.4s, #0x3f, lsl #24
+  .long  0x6e22dc32                          // fmul          v18.4s, v1.4s, v2.4s
+  .long  0x4e040d17                          // dup           v23.4s, w8
+  .long  0x52b7d548                          // mov           w8, #0xbeaa0000
+  .long  0x4ea0d830                          // fcmeq         v16.4s, v1.4s, #0.0
+  .long  0x72955568                          // movk          w8, #0xaaab
+  .long  0x6e31e45a                          // fcmge         v26.4s, v2.4s, v17.4s
+  .long  0x4eb2d421                          // fsub          v1.4s, v1.4s, v18.4s
+  .long  0x4e040d18                          // dup           v24.4s, w8
+  .long  0x4e219819                          // frintm        v25.4s, v0.4s
+  .long  0x6e721c3a                          // bsl           v26.16b, v1.16b, v18.16b
+  .long  0x4e34d401                          // fadd          v1.4s, v0.4s, v20.4s
+  .long  0x4eb9d419                          // fsub          v25.4s, v0.4s, v25.4s
+  .long  0x4e38d400                          // fadd          v0.4s, v0.4s, v24.4s
+  .long  0x4e22d754                          // fadd          v20.4s, v26.4s, v2.4s
+  .long  0x4e219838                          // frintm        v24.4s, v1.4s
+  .long  0x4f026413                          // movi          v19.4s, #0x40, lsl #24
+  .long  0x4f00f715                          // fmov          v21.4s, #6.000000000000000000e+00
+  .long  0x4e21981b                          // frintm        v27.4s, v0.4s
+  .long  0x6ea0fa9c                          // fneg          v28.4s, v20.4s
+  .long  0x4eb8d421                          // fsub          v1.4s, v1.4s, v24.4s
+  .long  0x4f00f616                          // fmov          v22.4s, #4.000000000000000000e+00
+  .long  0x4ebbd418                          // fsub          v24.4s, v0.4s, v27.4s
+  .long  0x4e22ce7c                          // fmla          v28.4s, v19.4s, v2.4s
+  .long  0x6e35dc20                          // fmul          v0.4s, v1.4s, v21.4s
+  .long  0x6e35df32                          // fmul          v18.4s, v25.4s, v21.4s
+  .long  0x6e35df13                          // fmul          v19.4s, v24.4s, v21.4s
+  .long  0x4ebcd695                          // fsub          v21.4s, v20.4s, v28.4s
+  .long  0x4ea0d6db                          // fsub          v27.4s, v22.4s, v0.4s
+  .long  0x4ebc1f9d                          // mov           v29.16b, v28.16b
+  .long  0x4e3bcebd                          // fmla          v29.4s, v21.4s, v27.4s
+  .long  0x4ebc1f9b                          // mov           v27.16b, v28.16b
+  .long  0x4eb2d6da                          // fsub          v26.4s, v22.4s, v18.4s
+  .long  0x4e20cebb                          // fmla          v27.4s, v21.4s, v0.4s
+  .long  0x4ebc1f80                          // mov           v0.16b, v28.16b
+  .long  0x4e3acea0                          // fmla          v0.4s, v21.4s, v26.4s
+  .long  0x4ebc1f9a                          // mov           v26.16b, v28.16b
+  .long  0x4eb3d6d6                          // fsub          v22.4s, v22.4s, v19.4s
+  .long  0x4e32ceba                          // fmla          v26.4s, v21.4s, v18.4s
+  .long  0x4ebc1f92                          // mov           v18.16b, v28.16b
+  .long  0x52a7c548                          // mov           w8, #0x3e2a0000
+  .long  0x4e36ceb2                          // fmla          v18.4s, v21.4s, v22.4s
+  .long  0x6e37e436                          // fcmge         v22.4s, v1.4s, v23.4s
+  .long  0x72955568                          // movk          w8, #0xaaab
+  .long  0x6e7d1f96                          // bsl           v22.16b, v28.16b, v29.16b
+  .long  0x6e37e73d                          // fcmge         v29.4s, v25.4s, v23.4s
+  .long  0x6e37e717                          // fcmge         v23.4s, v24.4s, v23.4s
+  .long  0x6e601f9d                          // bsl           v29.16b, v28.16b, v0.16b
+  .long  0x4e040d1e                          // dup           v30.4s, w8
+  .long  0x6e721f97                          // bsl           v23.16b, v28.16b, v18.16b
+  .long  0x4e33cebc                          // fmla          v28.4s, v21.4s, v19.4s
+  .long  0x6e31e732                          // fcmge         v18.4s, v25.4s, v17.4s
+  .long  0x6e31e435                          // fcmge         v21.4s, v1.4s, v17.4s
+  .long  0x6e31e711                          // fcmge         v17.4s, v24.4s, v17.4s
+  .long  0x6e3ee718                          // fcmge         v24.4s, v24.4s, v30.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e741ef1                          // bsl           v17.16b, v23.16b, v20.16b
+  .long  0x6e3ee733                          // fcmge         v19.4s, v25.4s, v30.4s
+  .long  0x6e3ee439                          // fcmge         v25.4s, v1.4s, v30.4s
+  .long  0x6e741ed5                          // bsl           v21.16b, v22.16b, v20.16b
+  .long  0x6e741fb2                          // bsl           v18.16b, v29.16b, v20.16b
+  .long  0x6e7c1e38                          // bsl           v24.16b, v17.16b, v28.16b
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb01e01                          // mov           v1.16b, v16.16b
+  .long  0x6e7b1eb9                          // bsl           v25.16b, v21.16b, v27.16b
+  .long  0x6e7a1e53                          // bsl           v19.16b, v18.16b, v26.16b
+  .long  0x6e781c50                          // bsl           v16.16b, v2.16b, v24.16b
+  .long  0x6e791c40                          // bsl           v0.16b, v2.16b, v25.16b
+  .long  0x6e731c41                          // bsl           v1.16b, v2.16b, v19.16b
+  .long  0x4eb01e02                          // mov           v2.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_scale_1_float_aarch64
+.globl _sk_scale_1_float_aarch64
+FUNCTION(_sk_scale_1_float_aarch64)
+_sk_scale_1_float_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xbd400110                          // ldr           s16, [x8]
+  .long  0x4f909000                          // fmul          v0.4s, v0.4s, v16.s[0]
+  .long  0x4f909021                          // fmul          v1.4s, v1.4s, v16.s[0]
+  .long  0x4f909042                          // fmul          v2.4s, v2.4s, v16.s[0]
+  .long  0x4f909063                          // fmul          v3.4s, v3.4s, v16.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_scale_u8_aarch64
+.globl _sk_scale_u8_aarch64
+FUNCTION(_sk_scale_u8_aarch64)
+_sk_scale_u8_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020108                          // add           x8, x8, x2
+  .long  0xb50002e4                          // cbnz          x4, 16e4 <sk_scale_u8_aarch64+0x68>
+  .long  0x39400109                          // ldrb          w9, [x8]
+  .long  0x3940050a                          // ldrb          w10, [x8, #1]
+  .long  0x3940090b                          // ldrb          w11, [x8, #2]
+  .long  0x39400d08                          // ldrb          w8, [x8, #3]
+  .long  0x4e021d30                          // mov           v16.h[0], w9
+  .long  0x4e061d50                          // mov           v16.h[1], w10
+  .long  0x4e0a1d70                          // mov           v16.h[2], w11
+  .long  0x4e0e1d10                          // mov           v16.h[3], w8
+  .long  0x2f07b7f0                          // bic           v16.4h, #0xff, lsl #8
+  .long  0x52a77008                          // mov           w8, #0x3b800000
+  .long  0x72901028                          // movk          w8, #0x8081
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x2f10a610                          // uxtl          v16.4s, v16.4h
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x6e21da10                          // ucvtf         v16.4s, v16.4s
+  .long  0x6e31de10                          // fmul          v16.4s, v16.4s, v17.4s
+  .long  0x6e20de00                          // fmul          v0.4s, v16.4s, v0.4s
+  .long  0x6e21de01                          // fmul          v1.4s, v16.4s, v1.4s
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0x6e23de03                          // fmul          v3.4s, v16.4s, v3.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e410                          // movi          d16, #0x0
+  .long  0x54000140                          // b.eq          1718 <sk_scale_u8_aarch64+0x9c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          1710 <sk_scale_u8_aarch64+0x94>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffd61                          // b.ne          16ac <sk_scale_u8_aarch64+0x30>  // b.any
+  .long  0x39400909                          // ldrb          w9, [x8, #2]
+  .long  0x0e020ff0                          // dup           v16.4h, wzr
+  .long  0x4e0a1d30                          // mov           v16.h[2], w9
+  .long  0x39400509                          // ldrb          w9, [x8, #1]
+  .long  0x4e061d30                          // mov           v16.h[1], w9
+  .long  0x39400108                          // ldrb          w8, [x8]
+  .long  0x4e021d10                          // mov           v16.h[0], w8
+  .long  0x17ffffe3                          // b             16ac <sk_scale_u8_aarch64+0x30>
+
+HIDDEN _sk_lerp_1_float_aarch64
+.globl _sk_lerp_1_float_aarch64
+FUNCTION(_sk_lerp_1_float_aarch64)
+_sk_lerp_1_float_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4ea4d411                          // fsub          v17.4s, v0.4s, v4.4s
+  .long  0x4ea41c80                          // mov           v0.16b, v4.16b
+  .long  0x4ea5d432                          // fsub          v18.4s, v1.4s, v5.4s
+  .long  0xbd400110                          // ldr           s16, [x8]
+  .long  0x4ea51ca1                          // mov           v1.16b, v5.16b
+  .long  0x4f901220                          // fmla          v0.4s, v17.4s, v16.s[0]
+  .long  0x4ea6d451                          // fsub          v17.4s, v2.4s, v6.4s
+  .long  0x4f901241                          // fmla          v1.4s, v18.4s, v16.s[0]
+  .long  0x4ea61cc2                          // mov           v2.16b, v6.16b
+  .long  0x4ea7d472                          // fsub          v18.4s, v3.4s, v7.4s
+  .long  0x4ea71ce3                          // mov           v3.16b, v7.16b
+  .long  0x4f901222                          // fmla          v2.4s, v17.4s, v16.s[0]
+  .long  0x4f901243                          // fmla          v3.4s, v18.4s, v16.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_lerp_u8_aarch64
+.globl _sk_lerp_u8_aarch64
+FUNCTION(_sk_lerp_u8_aarch64)
+_sk_lerp_u8_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020108                          // add           x8, x8, x2
+  .long  0xb50003e4                          // cbnz          x4, 17e8 <sk_lerp_u8_aarch64+0x88>
+  .long  0x39400109                          // ldrb          w9, [x8]
+  .long  0x3940050a                          // ldrb          w10, [x8, #1]
+  .long  0x3940090b                          // ldrb          w11, [x8, #2]
+  .long  0x39400d08                          // ldrb          w8, [x8, #3]
+  .long  0x4e021d30                          // mov           v16.h[0], w9
+  .long  0x4e061d50                          // mov           v16.h[1], w10
+  .long  0x4e0a1d70                          // mov           v16.h[2], w11
+  .long  0x4e0e1d10                          // mov           v16.h[3], w8
+  .long  0x2f07b7f0                          // bic           v16.4h, #0xff, lsl #8
+  .long  0x52a77008                          // mov           w8, #0x3b800000
+  .long  0x72901028                          // movk          w8, #0x8081
+  .long  0x4ea4d411                          // fsub          v17.4s, v0.4s, v4.4s
+  .long  0x2f10a600                          // uxtl          v0.4s, v16.4h
+  .long  0x4e040d10                          // dup           v16.4s, w8
+  .long  0x6e21d800                          // ucvtf         v0.4s, v0.4s
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x6e30dc10                          // fmul          v16.4s, v0.4s, v16.4s
+  .long  0x4ea41c80                          // mov           v0.16b, v4.16b
+  .long  0x4ea5d432                          // fsub          v18.4s, v1.4s, v5.4s
+  .long  0x4ea51ca1                          // mov           v1.16b, v5.16b
+  .long  0x4e31ce00                          // fmla          v0.4s, v16.4s, v17.4s
+  .long  0x4ea6d451                          // fsub          v17.4s, v2.4s, v6.4s
+  .long  0x4e32ce01                          // fmla          v1.4s, v16.4s, v18.4s
+  .long  0x4ea61cc2                          // mov           v2.16b, v6.16b
+  .long  0x4ea7d472                          // fsub          v18.4s, v3.4s, v7.4s
+  .long  0x4ea71ce3                          // mov           v3.16b, v7.16b
+  .long  0x4e31ce02                          // fmla          v2.4s, v16.4s, v17.4s
+  .long  0x4e32ce03                          // fmla          v3.4s, v16.4s, v18.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e410                          // movi          d16, #0x0
+  .long  0x54000140                          // b.eq          181c <sk_lerp_u8_aarch64+0xbc>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          1814 <sk_lerp_u8_aarch64+0xb4>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffc61                          // b.ne          1790 <sk_lerp_u8_aarch64+0x30>  // b.any
+  .long  0x39400909                          // ldrb          w9, [x8, #2]
+  .long  0x0e020ff0                          // dup           v16.4h, wzr
+  .long  0x4e0a1d30                          // mov           v16.h[2], w9
+  .long  0x39400509                          // ldrb          w9, [x8, #1]
+  .long  0x4e061d30                          // mov           v16.h[1], w9
+  .long  0x39400108                          // ldrb          w8, [x8]
+  .long  0x4e021d10                          // mov           v16.h[0], w8
+  .long  0x17ffffdb                          // b             1790 <sk_lerp_u8_aarch64+0x30>
+
+HIDDEN _sk_lerp_565_aarch64
+.globl _sk_lerp_565_aarch64
+FUNCTION(_sk_lerp_565_aarch64)
+_sk_lerp_565_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020508                          // add           x8, x8, x2, lsl #1
+  .long  0xb50005c4                          // cbnz          x4, 18ec <sk_lerp_565_aarch64+0xc4>
+  .long  0xfd400110                          // ldr           d16, [x8]
+  .long  0x321b17e8                          // orr           w8, wzr, #0x7e0
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a6f088                          // mov           w8, #0x37840000
+  .long  0x2f10a610                          // uxtl          v16.4s, v16.4h
+  .long  0x4f072711                          // movi          v17.4s, #0xf8, lsl #8
+  .long  0x72842108                          // movk          w8, #0x2108
+  .long  0x4f0007f3                          // movi          v19.4s, #0x1f
+  .long  0x4e311e11                          // and           v17.16b, v16.16b, v17.16b
+  .long  0x4e331e13                          // and           v19.16b, v16.16b, v19.16b
+  .long  0x4e321e10                          // and           v16.16b, v16.16b, v18.16b
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a7a088                          // mov           w8, #0x3d040000
+  .long  0x72842108                          // movk          w8, #0x2108
+  .long  0x4e21da31                          // scvtf         v17.4s, v17.4s
+  .long  0x6e32de31                          // fmul          v17.4s, v17.4s, v18.4s
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a74048                          // mov           w8, #0x3a020000
+  .long  0x72810428                          // movk          w8, #0x821
+  .long  0x4ea4d414                          // fsub          v20.4s, v0.4s, v4.4s
+  .long  0x4e21da60                          // scvtf         v0.4s, v19.4s
+  .long  0x6e32dc12                          // fmul          v18.4s, v0.4s, v18.4s
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e20de10                          // fmul          v16.4s, v16.4s, v0.4s
+  .long  0x4ea41c80                          // mov           v0.16b, v4.16b
+  .long  0x4e34ce20                          // fmla          v0.4s, v17.4s, v20.4s
+  .long  0x4ea7d463                          // fsub          v3.4s, v3.4s, v7.4s
+  .long  0x4ea71cf4                          // mov           v20.16b, v7.16b
+  .long  0x4ea5d433                          // fsub          v19.4s, v1.4s, v5.4s
+  .long  0x4ea6d441                          // fsub          v1.4s, v2.4s, v6.4s
+  .long  0x4ea61cc2                          // mov           v2.16b, v6.16b
+  .long  0x4e23ce34                          // fmla          v20.4s, v17.4s, v3.4s
+  .long  0x4ea71cf1                          // mov           v17.16b, v7.16b
+  .long  0x4e21ce42                          // fmla          v2.4s, v18.4s, v1.4s
+  .long  0x4e23ce51                          // fmla          v17.4s, v18.4s, v3.4s
+  .long  0x4ea71cf2                          // mov           v18.16b, v7.16b
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4e23ce12                          // fmla          v18.4s, v16.4s, v3.4s
+  .long  0x4ea51ca1                          // mov           v1.16b, v5.16b
+  .long  0x4e31f643                          // fmax          v3.4s, v18.4s, v17.4s
+  .long  0x4e33ce01                          // fmla          v1.4s, v16.4s, v19.4s
+  .long  0x4e23f683                          // fmax          v3.4s, v20.4s, v3.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e410                          // movi          d16, #0x0
+  .long  0x54000140                          // b.eq          1920 <sk_lerp_565_aarch64+0xf8>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          1918 <sk_lerp_565_aarch64+0xf0>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fff9a1                          // b.ne          183c <sk_lerp_565_aarch64+0x14>  // b.any
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0e020ff0                          // dup           v16.4h, wzr
+  .long  0x0d405130                          // ld1           {v16.h}[2], [x9]
+  .long  0x91000909                          // add           x9, x8, #0x2
+  .long  0x0d404930                          // ld1           {v16.h}[1], [x9]
+  .long  0x0d404110                          // ld1           {v16.h}[0], [x8]
+  .long  0x17ffffc6                          // b             183c <sk_lerp_565_aarch64+0x14>
+
+HIDDEN _sk_load_tables_aarch64
+.globl _sk_load_tables_aarch64
+FUNCTION(_sk_load_tables_aarch64)
+_sk_load_tables_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400109                          // ldr           x9, [x8]
+  .long  0x8b020929                          // add           x9, x9, x2, lsl #2
+  .long  0xb50006e4                          // cbnz          x4, 1a10 <sk_load_tables_aarch64+0xe8>
+  .long  0x3dc00122                          // ldr           q2, [x9]
+  .long  0xa940a909                          // ldp           x9, x10, [x8, #8]
+  .long  0x6f00e620                          // movi          v0.2d, #0xff000000ff
+  .long  0x6f380441                          // ushr          v1.4s, v2.4s, #8
+  .long  0x4e201c50                          // and           v16.16b, v2.16b, v0.16b
+  .long  0x4e201c21                          // and           v1.16b, v1.16b, v0.16b
+  .long  0x1e26020e                          // fmov          w14, s16
+  .long  0xf9400d08                          // ldr           x8, [x8, #24]
+  .long  0x6f300443                          // ushr          v3.4s, v2.4s, #16
+  .long  0x1e260032                          // fmov          w18, s1
+  .long  0x8b2e492e                          // add           x14, x9, w14, uxtw #2
+  .long  0x52a7700b                          // mov           w11, #0x3b800000
+  .long  0x4e201c63                          // and           v3.16b, v3.16b, v0.16b
+  .long  0x0d4081c0                          // ld1           {v0.s}[0], [x14]
+  .long  0x8b324952                          // add           x18, x10, w18, uxtw #2
+  .long  0x7290102b                          // movk          w11, #0x8081
+  .long  0x0e0c3c2f                          // mov           w15, v1.s[1]
+  .long  0x0e143c30                          // mov           w16, v1.s[2]
+  .long  0x0e1c3c31                          // mov           w17, v1.s[3]
+  .long  0x0d408241                          // ld1           {v1.s}[0], [x18]
+  .long  0x4e040d71                          // dup           v17.4s, w11
+  .long  0x0e0c3e0b                          // mov           w11, v16.s[1]
+  .long  0x1e26006e                          // fmov          w14, s3
+  .long  0x6f280442                          // ushr          v2.4s, v2.4s, #24
+  .long  0x0e143e0c                          // mov           w12, v16.s[2]
+  .long  0xbc705952                          // ldr           s18, [x10, w16, uxtw #2]
+  .long  0x0e143c70                          // mov           w16, v3.s[2]
+  .long  0x4e21d842                          // scvtf         v2.4s, v2.4s
+  .long  0x8b2e490e                          // add           x14, x8, w14, uxtw #2
+  .long  0x8b2b492b                          // add           x11, x9, w11, uxtw #2
+  .long  0x0e1c3e0d                          // mov           w13, v16.s[3]
+  .long  0xbc6c5930                          // ldr           s16, [x9, w12, uxtw #2]
+  .long  0x0e0c3c6c                          // mov           w12, v3.s[1]
+  .long  0xbc705913                          // ldr           s19, [x8, w16, uxtw #2]
+  .long  0x0e1c3c70                          // mov           w16, v3.s[3]
+  .long  0x6e31dc43                          // fmul          v3.4s, v2.4s, v17.4s
+  .long  0x0d4081c2                          // ld1           {v2.s}[0], [x14]
+  .long  0x0d409160                          // ld1           {v0.s}[1], [x11]
+  .long  0x8b2f494b                          // add           x11, x10, w15, uxtw #2
+  .long  0x0d409161                          // ld1           {v1.s}[1], [x11]
+  .long  0x8b2c490b                          // add           x11, x8, w12, uxtw #2
+  .long  0x0d409162                          // ld1           {v2.s}[1], [x11]
+  .long  0x6e140600                          // mov           v0.s[2], v16.s[0]
+  .long  0xbc6d5930                          // ldr           s16, [x9, w13, uxtw #2]
+  .long  0x6e140641                          // mov           v1.s[2], v18.s[0]
+  .long  0xbc715951                          // ldr           s17, [x10, w17, uxtw #2]
+  .long  0xbc705912                          // ldr           s18, [x8, w16, uxtw #2]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x6e140662                          // mov           v2.s[2], v19.s[0]
+  .long  0x6e1c0600                          // mov           v0.s[3], v16.s[0]
+  .long  0x6e1c0621                          // mov           v1.s[3], v17.s[0]
+  .long  0x6e1c0642                          // mov           v2.s[3], v18.s[0]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x1200048a                          // and           w10, w4, #0x3
+  .long  0x7100055f                          // cmp           w10, #0x1
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0x54000140                          // b.eq          1a44 <sk_load_tables_aarch64+0x11c>  // b.none
+  .long  0x7100095f                          // cmp           w10, #0x2
+  .long  0x540000c0                          // b.eq          1a3c <sk_load_tables_aarch64+0x114>  // b.none
+  .long  0x71000d5f                          // cmp           w10, #0x3
+  .long  0x54fff881                          // b.ne          193c <sk_load_tables_aarch64+0x14>  // b.any
+  .long  0x9100212a                          // add           x10, x9, #0x8
+  .long  0x4e040fe2                          // dup           v2.4s, wzr
+  .long  0x4d408142                          // ld1           {v2.s}[2], [x10]
+  .long  0x9100112a                          // add           x10, x9, #0x4
+  .long  0x0d409142                          // ld1           {v2.s}[1], [x10]
+  .long  0x0d408122                          // ld1           {v2.s}[0], [x9]
+  .long  0x17ffffbd                          // b             193c <sk_load_tables_aarch64+0x14>
+
+HIDDEN _sk_load_tables_u16_be_aarch64
+.globl _sk_load_tables_u16_be_aarch64
+FUNCTION(_sk_load_tables_u16_be_aarch64)
+_sk_load_tables_u16_be_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400109                          // ldr           x9, [x8]
+  .long  0x8b020d29                          // add           x9, x9, x2, lsl #3
+  .long  0xb5000744                          // cbnz          x4, 1b40 <sk_load_tables_u16_be_aarch64+0xf4>
+  .long  0x0c400520                          // ld4           {v0.4h-v3.4h}, [x9]
+  .long  0xa940a909                          // ldp           x9, x10, [x8, #8]
+  .long  0x2f07b7e0                          // bic           v0.4h, #0xff, lsl #8
+  .long  0x52a6f00b                          // mov           w11, #0x37800000
+  .long  0x7280100b                          // movk          w11, #0x80
+  .long  0x2f10a410                          // uxtl          v16.4s, v0.4h
+  .long  0x2f07b7e1                          // bic           v1.4h, #0xff, lsl #8
+  .long  0xf9400d08                          // ldr           x8, [x8, #24]
+  .long  0x4e040d71                          // dup           v17.4s, w11
+  .long  0x0e0c3e0b                          // mov           w11, v16.s[1]
+  .long  0x0e143e0c                          // mov           w12, v16.s[2]
+  .long  0x0e1c3e0d                          // mov           w13, v16.s[3]
+  .long  0x1e26020e                          // fmov          w14, s16
+  .long  0x2f10a430                          // uxtl          v16.4s, v1.4h
+  .long  0x2f07b7e2                          // bic           v2.4h, #0xff, lsl #8
+  .long  0x0e0c3e0f                          // mov           w15, v16.s[1]
+  .long  0xbc6c5932                          // ldr           s18, [x9, w12, uxtw #2]
+  .long  0x0e143e0c                          // mov           w12, v16.s[2]
+  .long  0xbc6d5933                          // ldr           s19, [x9, w13, uxtw #2]
+  .long  0x0e1c3e0d                          // mov           w13, v16.s[3]
+  .long  0x8b2e492e                          // add           x14, x9, w14, uxtw #2
+  .long  0x8b2b4929                          // add           x9, x9, w11, uxtw #2
+  .long  0x1e26020b                          // fmov          w11, s16
+  .long  0x2f10a450                          // uxtl          v16.4s, v2.4h
+  .long  0x0f185474                          // shl           v20.4h, v3.4h, #8
+  .long  0x2f180462                          // ushr          v2.4h, v3.4h, #8
+  .long  0x0ea21e82                          // orr           v2.8b, v20.8b, v2.8b
+  .long  0x8b2b494b                          // add           x11, x10, w11, uxtw #2
+  .long  0x0d4081c0                          // ld1           {v0.s}[0], [x14]
+  .long  0x1e26020e                          // fmov          w14, s16
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x0d408161                          // ld1           {v1.s}[0], [x11]
+  .long  0x8b2e490b                          // add           x11, x8, w14, uxtw #2
+  .long  0x6e21d842                          // ucvtf         v2.4s, v2.4s
+  .long  0x6e31dc43                          // fmul          v3.4s, v2.4s, v17.4s
+  .long  0x0d408162                          // ld1           {v2.s}[0], [x11]
+  .long  0x8b2f494f                          // add           x15, x10, w15, uxtw #2
+  .long  0xbc6c5955                          // ldr           s21, [x10, w12, uxtw #2]
+  .long  0xbc6d5956                          // ldr           s22, [x10, w13, uxtw #2]
+  .long  0x0e0c3e0a                          // mov           w10, v16.s[1]
+  .long  0x0e143e0c                          // mov           w12, v16.s[2]
+  .long  0x0d409120                          // ld1           {v0.s}[1], [x9]
+  .long  0x8b2a4909                          // add           x9, x8, w10, uxtw #2
+  .long  0x0e1c3e0d                          // mov           w13, v16.s[3]
+  .long  0xbc6c5910                          // ldr           s16, [x8, w12, uxtw #2]
+  .long  0x0d4091e1                          // ld1           {v1.s}[1], [x15]
+  .long  0x0d409122                          // ld1           {v2.s}[1], [x9]
+  .long  0xbc6d5911                          // ldr           s17, [x8, w13, uxtw #2]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x6e140640                          // mov           v0.s[2], v18.s[0]
+  .long  0x6e1406a1                          // mov           v1.s[2], v21.s[0]
+  .long  0x6e140602                          // mov           v2.s[2], v16.s[0]
+  .long  0x6e1c0660                          // mov           v0.s[3], v19.s[0]
+  .long  0x6e1c06c1                          // mov           v1.s[3], v22.s[0]
+  .long  0x6e1c0622                          // mov           v2.s[3], v17.s[0]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x0d606120                          // ld4           {v0.h-v3.h}[0], [x9]
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x54fff8c0                          // b.eq          1a60 <sk_load_tables_u16_be_aarch64+0x14>  // b.none
+  .long  0x9100212a                          // add           x10, x9, #0x8
+  .long  0x0d606940                          // ld4           {v0.h-v3.h}[1], [x10]
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x54fff843                          // b.cc          1a60 <sk_load_tables_u16_be_aarch64+0x14>  // b.lo, b.ul, b.last
+  .long  0x91004129                          // add           x9, x9, #0x10
+  .long  0x0d607120                          // ld4           {v0.h-v3.h}[2], [x9]
+  .long  0x17ffffbf                          // b             1a60 <sk_load_tables_u16_be_aarch64+0x14>
+
+HIDDEN _sk_load_tables_rgb_u16_be_aarch64
+.globl _sk_load_tables_rgb_u16_be_aarch64
+FUNCTION(_sk_load_tables_rgb_u16_be_aarch64)
+_sk_load_tables_rgb_u16_be_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x321f07ea                          // orr           w10, wzr, #0x6
+  .long  0xf9400109                          // ldr           x9, [x8]
+  .long  0x9b0a2449                          // madd          x9, x2, x10, x9
+  .long  0xb5000664                          // cbnz          x4, 1c44 <sk_load_tables_rgb_u16_be_aarch64+0xdc>
+  .long  0x0c404520                          // ld3           {v0.4h-v2.4h}, [x9]
+  .long  0xa940a909                          // ldp           x9, x10, [x8, #8]
+  .long  0x2f07b7e0                          // bic           v0.4h, #0xff, lsl #8
+  .long  0x2f10a403                          // uxtl          v3.4s, v0.4h
+  .long  0x2f07b7e1                          // bic           v1.4h, #0xff, lsl #8
+  .long  0xf9400d0b                          // ldr           x11, [x8, #24]
+  .long  0x0e0c3c68                          // mov           w8, v3.s[1]
+  .long  0x0e143c6c                          // mov           w12, v3.s[2]
+  .long  0x0e1c3c6d                          // mov           w13, v3.s[3]
+  .long  0x1e26006e                          // fmov          w14, s3
+  .long  0x2f10a423                          // uxtl          v3.4s, v1.4h
+  .long  0x2f07b7e2                          // bic           v2.4h, #0xff, lsl #8
+  .long  0xbc6c5930                          // ldr           s16, [x9, w12, uxtw #2]
+  .long  0xbc6d5931                          // ldr           s17, [x9, w13, uxtw #2]
+  .long  0x8b2e492e                          // add           x14, x9, w14, uxtw #2
+  .long  0x8b284928                          // add           x8, x9, w8, uxtw #2
+  .long  0x1e260069                          // fmov          w9, s3
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x8b294949                          // add           x9, x10, w9, uxtw #2
+  .long  0x0d4081c0                          // ld1           {v0.s}[0], [x14]
+  .long  0x0e143c4e                          // mov           w14, v2.s[2]
+  .long  0x0d408121                          // ld1           {v1.s}[0], [x9]
+  .long  0xbc6e5972                          // ldr           s18, [x11, w14, uxtw #2]
+  .long  0x1e26004e                          // fmov          w14, s2
+  .long  0x0e0c3c6f                          // mov           w15, v3.s[1]
+  .long  0x0e143c6c                          // mov           w12, v3.s[2]
+  .long  0x8b2e496e                          // add           x14, x11, w14, uxtw #2
+  .long  0x0e1c3c6d                          // mov           w13, v3.s[3]
+  .long  0xbc6c5943                          // ldr           s3, [x10, w12, uxtw #2]
+  .long  0x0e0c3c4c                          // mov           w12, v2.s[1]
+  .long  0x0e1c3c49                          // mov           w9, v2.s[3]
+  .long  0x0d4081c2                          // ld1           {v2.s}[0], [x14]
+  .long  0x0d409100                          // ld1           {v0.s}[1], [x8]
+  .long  0x8b2f4948                          // add           x8, x10, w15, uxtw #2
+  .long  0x0d409101                          // ld1           {v1.s}[1], [x8]
+  .long  0x8b2c4968                          // add           x8, x11, w12, uxtw #2
+  .long  0x0d409102                          // ld1           {v2.s}[1], [x8]
+  .long  0x6e140600                          // mov           v0.s[2], v16.s[0]
+  .long  0xbc6d5950                          // ldr           s16, [x10, w13, uxtw #2]
+  .long  0x6e140461                          // mov           v1.s[2], v3.s[0]
+  .long  0xbc695963                          // ldr           s3, [x11, w9, uxtw #2]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004028                          // add           x8, x1, #0x10
+  .long  0x6e140642                          // mov           v2.s[2], v18.s[0]
+  .long  0x6e1c0620                          // mov           v0.s[3], v17.s[0]
+  .long  0x6e1c0601                          // mov           v1.s[3], v16.s[0]
+  .long  0x6e1c0462                          // mov           v2.s[3], v3.s[0]
+  .long  0x4f03f603                          // fmov          v3.4s, #1.000000000000000000e+00
+  .long  0xaa0803e1                          // mov           x1, x8
+  .long  0xd61f00a0                          // br            x5
+  .long  0x0d406120                          // ld3           {v0.h-v2.h}[0], [x9]
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x54fff9a0                          // b.eq          1b80 <sk_load_tables_rgb_u16_be_aarch64+0x18>  // b.none
+  .long  0x9100192a                          // add           x10, x9, #0x6
+  .long  0x0d406940                          // ld3           {v0.h-v2.h}[1], [x10]
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x54fff923                          // b.cc          1b80 <sk_load_tables_rgb_u16_be_aarch64+0x18>  // b.lo, b.ul, b.last
+  .long  0x91003129                          // add           x9, x9, #0xc
+  .long  0x0d407120                          // ld3           {v0.h-v2.h}[2], [x9]
+  .long  0x17ffffc6                          // b             1b80 <sk_load_tables_rgb_u16_be_aarch64+0x18>
+
+HIDDEN _sk_byte_tables_aarch64
+.globl _sk_byte_tables_aarch64
+FUNCTION(_sk_byte_tables_aarch64)
+_sk_byte_tables_aarch64:
+  .long  0xd100c3ff                          // sub           sp, sp, #0x30
+  .long  0xaa0103e8                          // mov           x8, x1
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0xa90157f6                          // stp           x22, x21, [sp, #16]
+  .long  0xa9024ff4                          // stp           x20, x19, [sp, #32]
+  .long  0xf90007e9                          // str           x9, [sp, #8]
+  .long  0xf8410429                          // ldr           x9, [x1], #16
+  .long  0x52a86fea                          // mov           w10, #0x437f0000
+  .long  0x4e040d51                          // dup           v17.4s, w10
+  .long  0x52a7700b                          // mov           w11, #0x3b800000
+  .long  0xa9405933                          // ldp           x19, x22, [x9]
+  .long  0x6e31dc00                          // fmul          v0.4s, v0.4s, v17.4s
+  .long  0x7290102b                          // movk          w11, #0x8081
+  .long  0x6e21a800                          // fcvtnu        v0.4s, v0.4s
+  .long  0x4e040d70                          // dup           v16.4s, w11
+  .long  0x0e0c3c0a                          // mov           w10, v0.s[1]
+  .long  0x0e143c0b                          // mov           w11, v0.s[2]
+  .long  0x0e1c3c0c                          // mov           w12, v0.s[3]
+  .long  0x1e26000d                          // fmov          w13, s0
+  .long  0x386d4a6d                          // ldrb          w13, [x19, w13, uxtw]
+  .long  0x386a4a6a                          // ldrb          w10, [x19, w10, uxtw]
+  .long  0x386b4a6b                          // ldrb          w11, [x19, w11, uxtw]
+  .long  0x386c4a6c                          // ldrb          w12, [x19, w12, uxtw]
+  .long  0xa9412533                          // ldp           x19, x9, [x9, #16]
+  .long  0x6e31dc42                          // fmul          v2.4s, v2.4s, v17.4s
+  .long  0x6e31dc21                          // fmul          v1.4s, v1.4s, v17.4s
+  .long  0x6e31dc63                          // fmul          v3.4s, v3.4s, v17.4s
+  .long  0x6e21a842                          // fcvtnu        v2.4s, v2.4s
+  .long  0x6e21a821                          // fcvtnu        v1.4s, v1.4s
+  .long  0x6e21a863                          // fcvtnu        v3.4s, v3.4s
+  .long  0x0e0c3c52                          // mov           w18, v2.s[1]
+  .long  0x0e143c45                          // mov           w5, v2.s[2]
+  .long  0x0e1c3c46                          // mov           w6, v2.s[3]
+  .long  0x1e260047                          // fmov          w7, s2
+  .long  0x1e260031                          // fmov          w17, s1
+  .long  0x38674a67                          // ldrb          w7, [x19, w7, uxtw]
+  .long  0x38724a72                          // ldrb          w18, [x19, w18, uxtw]
+  .long  0x38654a65                          // ldrb          w5, [x19, w5, uxtw]
+  .long  0x38664a66                          // ldrb          w6, [x19, w6, uxtw]
+  .long  0x1e260073                          // fmov          w19, s3
+  .long  0x0e0c3c2e                          // mov           w14, v1.s[1]
+  .long  0x0e0c3c74                          // mov           w20, v3.s[1]
+  .long  0x38714ad1                          // ldrb          w17, [x22, w17, uxtw]
+  .long  0x38734933                          // ldrb          w19, [x9, w19, uxtw]
+  .long  0x0e143c2f                          // mov           w15, v1.s[2]
+  .long  0x0e1c3c30                          // mov           w16, v1.s[3]
+  .long  0x0e143c75                          // mov           w21, v3.s[2]
+  .long  0x386e4ace                          // ldrb          w14, [x22, w14, uxtw]
+  .long  0x38744934                          // ldrb          w20, [x9, w20, uxtw]
+  .long  0x386f4acf                          // ldrb          w15, [x22, w15, uxtw]
+  .long  0x38704ad0                          // ldrb          w16, [x22, w16, uxtw]
+  .long  0x0e1c3c76                          // mov           w22, v3.s[3]
+  .long  0x38754935                          // ldrb          w21, [x9, w21, uxtw]
+  .long  0x38764929                          // ldrb          w9, [x9, w22, uxtw]
+  .long  0x4e021da0                          // mov           v0.h[0], w13
+  .long  0x4e021e21                          // mov           v1.h[0], w17
+  .long  0x4e021ce2                          // mov           v2.h[0], w7
+  .long  0x4e021e63                          // mov           v3.h[0], w19
+  .long  0x4e061d40                          // mov           v0.h[1], w10
+  .long  0x4e061dc1                          // mov           v1.h[1], w14
+  .long  0x4e061e42                          // mov           v2.h[1], w18
+  .long  0x4e061e83                          // mov           v3.h[1], w20
+  .long  0x4e0a1d60                          // mov           v0.h[2], w11
+  .long  0x4e0a1de1                          // mov           v1.h[2], w15
+  .long  0x4e0a1ca2                          // mov           v2.h[2], w5
+  .long  0x4e0a1ea3                          // mov           v3.h[2], w21
+  .long  0x4e0e1d80                          // mov           v0.h[3], w12
+  .long  0x4e0e1e01                          // mov           v1.h[3], w16
+  .long  0x4e0e1cc2                          // mov           v2.h[3], w6
+  .long  0x4e0e1d23                          // mov           v3.h[3], w9
+  .long  0xf9400507                          // ldr           x7, [x8, #8]
+  .long  0x2f07b7e0                          // bic           v0.4h, #0xff, lsl #8
+  .long  0x2f07b7e1                          // bic           v1.4h, #0xff, lsl #8
+  .long  0x2f07b7e2                          // bic           v2.4h, #0xff, lsl #8
+  .long  0x2f07b7e3                          // bic           v3.4h, #0xff, lsl #8
+  .long  0xa9424ff4                          // ldp           x20, x19, [sp, #32]
+  .long  0xa94157f6                          // ldp           x22, x21, [sp, #16]
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x2f10a421                          // uxtl          v1.4s, v1.4h
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x2f10a463                          // uxtl          v3.4s, v3.4h
+  .long  0x6e21d800                          // ucvtf         v0.4s, v0.4s
+  .long  0x6e21d821                          // ucvtf         v1.4s, v1.4s
+  .long  0x6e21d842                          // ucvtf         v2.4s, v2.4s
+  .long  0x6e21d863                          // ucvtf         v3.4s, v3.4s
+  .long  0x6e30dc00                          // fmul          v0.4s, v0.4s, v16.4s
+  .long  0x6e30dc21                          // fmul          v1.4s, v1.4s, v16.4s
+  .long  0x6e30dc42                          // fmul          v2.4s, v2.4s, v16.4s
+  .long  0x6e30dc63                          // fmul          v3.4s, v3.4s, v16.4s
+  .long  0x9100c3ff                          // add           sp, sp, #0x30
+  .long  0xd61f00e0                          // br            x7
+
+HIDDEN _sk_byte_tables_rgb_aarch64
+.globl _sk_byte_tables_rgb_aarch64
+FUNCTION(_sk_byte_tables_rgb_aarch64)
+_sk_byte_tables_rgb_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x52a77009                          // mov           w9, #0x3b800000
+  .long  0x72901029                          // movk          w9, #0x8081
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0xb9401909                          // ldr           w9, [x8, #24]
+  .long  0xa9402d0a                          // ldp           x10, x11, [x8]
+  .long  0xf9400908                          // ldr           x8, [x8, #16]
+  .long  0x51000529                          // sub           w9, w9, #0x1
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x4e21da31                          // scvtf         v17.4s, v17.4s
+  .long  0x6e21de21                          // fmul          v1.4s, v17.4s, v1.4s
+  .long  0x6e20de20                          // fmul          v0.4s, v17.4s, v0.4s
+  .long  0x6e22de22                          // fmul          v2.4s, v17.4s, v2.4s
+  .long  0x6e21a821                          // fcvtnu        v1.4s, v1.4s
+  .long  0x6e21a800                          // fcvtnu        v0.4s, v0.4s
+  .long  0x6e21a842                          // fcvtnu        v2.4s, v2.4s
+  .long  0x0e0c3c2f                          // mov           w15, v1.s[1]
+  .long  0x0e143c30                          // mov           w16, v1.s[2]
+  .long  0x0e1c3c31                          // mov           w17, v1.s[3]
+  .long  0x1e260032                          // fmov          w18, s1
+  .long  0x1e26000e                          // fmov          w14, s0
+  .long  0x38724972                          // ldrb          w18, [x11, w18, uxtw]
+  .long  0x386f496f                          // ldrb          w15, [x11, w15, uxtw]
+  .long  0x38704970                          // ldrb          w16, [x11, w16, uxtw]
+  .long  0x3871496b                          // ldrb          w11, [x11, w17, uxtw]
+  .long  0x1e260051                          // fmov          w17, s2
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x386e494e                          // ldrb          w14, [x10, w14, uxtw]
+  .long  0x0e0c3c46                          // mov           w6, v2.s[1]
+  .long  0x38714911                          // ldrb          w17, [x8, w17, uxtw]
+  .long  0x0e143c0c                          // mov           w12, v0.s[2]
+  .long  0x0e1c3c0d                          // mov           w13, v0.s[3]
+  .long  0x0e143c47                          // mov           w7, v2.s[2]
+  .long  0x38694949                          // ldrb          w9, [x10, w9, uxtw]
+  .long  0x38664906                          // ldrb          w6, [x8, w6, uxtw]
+  .long  0x386c494c                          // ldrb          w12, [x10, w12, uxtw]
+  .long  0x386d494a                          // ldrb          w10, [x10, w13, uxtw]
+  .long  0x0e1c3c4d                          // mov           w13, v2.s[3]
+  .long  0x38674907                          // ldrb          w7, [x8, w7, uxtw]
+  .long  0x386d4908                          // ldrb          w8, [x8, w13, uxtw]
+  .long  0x4e021dc0                          // mov           v0.h[0], w14
+  .long  0x4e021e41                          // mov           v1.h[0], w18
+  .long  0x4e021e22                          // mov           v2.h[0], w17
+  .long  0x4e061d20                          // mov           v0.h[1], w9
+  .long  0x4e061de1                          // mov           v1.h[1], w15
+  .long  0x4e061cc2                          // mov           v2.h[1], w6
+  .long  0x4e0a1d80                          // mov           v0.h[2], w12
+  .long  0x4e0a1e01                          // mov           v1.h[2], w16
+  .long  0x4e0a1ce2                          // mov           v2.h[2], w7
+  .long  0x4e0e1d40                          // mov           v0.h[3], w10
+  .long  0x4e0e1d61                          // mov           v1.h[3], w11
+  .long  0x4e0e1d02                          // mov           v2.h[3], w8
+  .long  0x2f07b7e0                          // bic           v0.4h, #0xff, lsl #8
+  .long  0x2f07b7e1                          // bic           v1.4h, #0xff, lsl #8
+  .long  0x2f07b7e2                          // bic           v2.4h, #0xff, lsl #8
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x2f10a421                          // uxtl          v1.4s, v1.4h
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x6e21d800                          // ucvtf         v0.4s, v0.4s
+  .long  0x6e21d821                          // ucvtf         v1.4s, v1.4s
+  .long  0x6e21d842                          // ucvtf         v2.4s, v2.4s
+  .long  0x6e30dc00                          // fmul          v0.4s, v0.4s, v16.4s
+  .long  0x6e30dc21                          // fmul          v1.4s, v1.4s, v16.4s
+  .long  0x6e30dc42                          // fmul          v2.4s, v2.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_table_r_aarch64
+.globl _sk_table_r_aarch64
+FUNCTION(_sk_table_r_aarch64)
+_sk_table_r_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xb9400909                          // ldr           w9, [x8, #8]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x51000529                          // sub           w9, w9, #0x1
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e20de00                          // fmul          v0.4s, v16.4s, v0.4s
+  .long  0x6e21a810                          // fcvtnu        v16.4s, v0.4s
+  .long  0x1e26020b                          // fmov          w11, s16
+  .long  0x8b2b490b                          // add           x11, x8, w11, uxtw #2
+  .long  0x0d408160                          // ld1           {v0.s}[0], [x11]
+  .long  0x0e0c3e09                          // mov           w9, v16.s[1]
+  .long  0x0e143e0a                          // mov           w10, v16.s[2]
+  .long  0x8b294909                          // add           x9, x8, w9, uxtw #2
+  .long  0x0e1c3e0b                          // mov           w11, v16.s[3]
+  .long  0xbc6a5910                          // ldr           s16, [x8, w10, uxtw #2]
+  .long  0x0d409120                          // ld1           {v0.s}[1], [x9]
+  .long  0xbc6b5911                          // ldr           s17, [x8, w11, uxtw #2]
+  .long  0x6e140600                          // mov           v0.s[2], v16.s[0]
+  .long  0x6e1c0620                          // mov           v0.s[3], v17.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_table_g_aarch64
+.globl _sk_table_g_aarch64
+FUNCTION(_sk_table_g_aarch64)
+_sk_table_g_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xb9400909                          // ldr           w9, [x8, #8]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x51000529                          // sub           w9, w9, #0x1
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e21de01                          // fmul          v1.4s, v16.4s, v1.4s
+  .long  0x6e21a830                          // fcvtnu        v16.4s, v1.4s
+  .long  0x1e26020b                          // fmov          w11, s16
+  .long  0x8b2b490b                          // add           x11, x8, w11, uxtw #2
+  .long  0x0d408161                          // ld1           {v1.s}[0], [x11]
+  .long  0x0e0c3e09                          // mov           w9, v16.s[1]
+  .long  0x0e143e0a                          // mov           w10, v16.s[2]
+  .long  0x8b294909                          // add           x9, x8, w9, uxtw #2
+  .long  0x0e1c3e0b                          // mov           w11, v16.s[3]
+  .long  0xbc6a5910                          // ldr           s16, [x8, w10, uxtw #2]
+  .long  0x0d409121                          // ld1           {v1.s}[1], [x9]
+  .long  0xbc6b5911                          // ldr           s17, [x8, w11, uxtw #2]
+  .long  0x6e140601                          // mov           v1.s[2], v16.s[0]
+  .long  0x6e1c0621                          // mov           v1.s[3], v17.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_table_b_aarch64
+.globl _sk_table_b_aarch64
+FUNCTION(_sk_table_b_aarch64)
+_sk_table_b_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xb9400909                          // ldr           w9, [x8, #8]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x51000529                          // sub           w9, w9, #0x1
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0x6e21a850                          // fcvtnu        v16.4s, v2.4s
+  .long  0x1e26020b                          // fmov          w11, s16
+  .long  0x8b2b490b                          // add           x11, x8, w11, uxtw #2
+  .long  0x0d408162                          // ld1           {v2.s}[0], [x11]
+  .long  0x0e0c3e09                          // mov           w9, v16.s[1]
+  .long  0x0e143e0a                          // mov           w10, v16.s[2]
+  .long  0x8b294909                          // add           x9, x8, w9, uxtw #2
+  .long  0x0e1c3e0b                          // mov           w11, v16.s[3]
+  .long  0xbc6a5910                          // ldr           s16, [x8, w10, uxtw #2]
+  .long  0x0d409122                          // ld1           {v2.s}[1], [x9]
+  .long  0xbc6b5911                          // ldr           s17, [x8, w11, uxtw #2]
+  .long  0x6e140602                          // mov           v2.s[2], v16.s[0]
+  .long  0x6e1c0622                          // mov           v2.s[3], v17.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_table_a_aarch64
+.globl _sk_table_a_aarch64
+FUNCTION(_sk_table_a_aarch64)
+_sk_table_a_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xb9400909                          // ldr           w9, [x8, #8]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x51000529                          // sub           w9, w9, #0x1
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e23de03                          // fmul          v3.4s, v16.4s, v3.4s
+  .long  0x6e21a870                          // fcvtnu        v16.4s, v3.4s
+  .long  0x1e26020b                          // fmov          w11, s16
+  .long  0x8b2b490b                          // add           x11, x8, w11, uxtw #2
+  .long  0x0d408163                          // ld1           {v3.s}[0], [x11]
+  .long  0x0e0c3e09                          // mov           w9, v16.s[1]
+  .long  0x0e143e0a                          // mov           w10, v16.s[2]
+  .long  0x8b294909                          // add           x9, x8, w9, uxtw #2
+  .long  0x0e1c3e0b                          // mov           w11, v16.s[3]
+  .long  0xbc6a5910                          // ldr           s16, [x8, w10, uxtw #2]
+  .long  0x0d409123                          // ld1           {v3.s}[1], [x9]
+  .long  0xbc6b5911                          // ldr           s17, [x8, w11, uxtw #2]
+  .long  0x6e140603                          // mov           v3.s[2], v16.s[0]
+  .long  0x6e1c0623                          // mov           v3.s[3], v17.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_parametric_r_aarch64
+.globl _sk_parametric_r_aarch64
+FUNCTION(_sk_parametric_r_aarch64)
+_sk_parametric_r_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4f016696                          // movi          v22.4s, #0x34, lsl #24
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x9100610a                          // add           x10, x8, #0x18
+  .long  0x4d40c932                          // ld1r          {v18.4s}, [x9]
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0xbd400d11                          // ldr           s17, [x8, #12]
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x4ddfc933                          // ld1r          {v19.4s}, [x9], #4
+  .long  0x9100210a                          // add           x10, x8, #0x8
+  .long  0x4d40c954                          // ld1r          {v20.4s}, [x10]
+  .long  0x4f911010                          // fmla          v16.4s, v0.4s, v17.s[0]
+  .long  0xbd400135                          // ldr           s21, [x9]
+  .long  0x52b85f09                          // mov           w9, #0xc2f80000
+  .long  0x728e6ee9                          // movk          w9, #0x7377
+  .long  0x4e040d37                          // dup           v23.4s, w9
+  .long  0x52a7f7e9                          // mov           w9, #0x3fbf0000
+  .long  0x7297eea9                          // movk          w9, #0xbf75
+  .long  0x4f951014                          // fmla          v20.4s, v0.4s, v21.s[0]
+  .long  0x6e20e640                          // fcmge         v0.4s, v18.4s, v0.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a7d689                          // mov           w9, #0x3eb40000
+  .long  0x4f03d7f1                          // movi          v17.4s, #0x7f, msl #16
+  .long  0x72889f29                          // movk          w9, #0x44f9
+  .long  0x4e21da95                          // scvtf         v21.4s, v20.4s
+  .long  0x4e311e91                          // and           v17.16b, v20.16b, v17.16b
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7fb89                          // mov           w9, #0x3fdc0000
+  .long  0x4e35ced7                          // fmla          v23.4s, v22.4s, v21.4s
+  .long  0x729d3469                          // movk          w9, #0xe9a3
+  .long  0x4f0177f1                          // orr           v17.4s, #0x3f, lsl #24
+  .long  0x4eb2ce37                          // fmls          v23.4s, v17.4s, v18.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a85e49                          // mov           w9, #0x42f20000
+  .long  0x72918a29                          // movk          w9, #0x8c51
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7f7c9                          // mov           w9, #0x3fbe0000
+  .long  0x729791a9                          // movk          w9, #0xbc8d
+  .long  0x6e31fe51                          // fdiv          v17.4s, v18.4s, v17.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a81349                          // mov           w9, #0x409a0000
+  .long  0x4eb1d6f1                          // fsub          v17.4s, v23.4s, v17.4s
+  .long  0x729ebf09                          // movk          w9, #0xf5f8
+  .long  0x6e31de71                          // fmul          v17.4s, v19.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x52a83ba9                          // mov           w9, #0x41dd0000
+  .long  0x4e219a33                          // frintm        v19.4s, v17.4s
+  .long  0x729a5fc9                          // movk          w9, #0xd2fe
+  .long  0x4e34d634                          // fadd          v20.4s, v17.4s, v20.4s
+  .long  0x4eb3d631                          // fsub          v17.4s, v17.4s, v19.4s
+  .long  0x4eb2ce34                          // fmls          v20.4s, v17.4s, v18.4s
+  .long  0x4eb1d6b1                          // fsub          v17.4s, v21.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x91005108                          // add           x8, x8, #0x14
+  .long  0x6e31feb1                          // fdiv          v17.4s, v21.4s, v17.4s
+  .long  0x4e31d691                          // fadd          v17.4s, v20.4s, v17.4s
+  .long  0x4d40c914                          // ld1r          {v20.4s}, [x8]
+  .long  0x4f026573                          // movi          v19.4s, #0x4b, lsl #24
+  .long  0x6e33de31                          // fmul          v17.4s, v17.4s, v19.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x6f00e412                          // movi          v18.2d, #0x0
+  .long  0x6e711e00                          // bsl           v0.16b, v16.16b, v17.16b
+  .long  0x4f03f615                          // fmov          v21.4s, #1.000000000000000000e+00
+  .long  0x4e32f400                          // fmax          v0.4s, v0.4s, v18.4s
+  .long  0x4eb5f400                          // fmin          v0.4s, v0.4s, v21.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_parametric_g_aarch64
+.globl _sk_parametric_g_aarch64
+FUNCTION(_sk_parametric_g_aarch64)
+_sk_parametric_g_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4f016696                          // movi          v22.4s, #0x34, lsl #24
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x9100610a                          // add           x10, x8, #0x18
+  .long  0x4d40c932                          // ld1r          {v18.4s}, [x9]
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0xbd400d11                          // ldr           s17, [x8, #12]
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x4ddfc933                          // ld1r          {v19.4s}, [x9], #4
+  .long  0x9100210a                          // add           x10, x8, #0x8
+  .long  0x4d40c954                          // ld1r          {v20.4s}, [x10]
+  .long  0x4f911030                          // fmla          v16.4s, v1.4s, v17.s[0]
+  .long  0xbd400135                          // ldr           s21, [x9]
+  .long  0x52b85f09                          // mov           w9, #0xc2f80000
+  .long  0x728e6ee9                          // movk          w9, #0x7377
+  .long  0x4e040d37                          // dup           v23.4s, w9
+  .long  0x52a7f7e9                          // mov           w9, #0x3fbf0000
+  .long  0x7297eea9                          // movk          w9, #0xbf75
+  .long  0x4f951034                          // fmla          v20.4s, v1.4s, v21.s[0]
+  .long  0x6e21e641                          // fcmge         v1.4s, v18.4s, v1.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a7d689                          // mov           w9, #0x3eb40000
+  .long  0x4f03d7f1                          // movi          v17.4s, #0x7f, msl #16
+  .long  0x72889f29                          // movk          w9, #0x44f9
+  .long  0x4e21da95                          // scvtf         v21.4s, v20.4s
+  .long  0x4e311e91                          // and           v17.16b, v20.16b, v17.16b
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7fb89                          // mov           w9, #0x3fdc0000
+  .long  0x4e35ced7                          // fmla          v23.4s, v22.4s, v21.4s
+  .long  0x729d3469                          // movk          w9, #0xe9a3
+  .long  0x4f0177f1                          // orr           v17.4s, #0x3f, lsl #24
+  .long  0x4eb2ce37                          // fmls          v23.4s, v17.4s, v18.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a85e49                          // mov           w9, #0x42f20000
+  .long  0x72918a29                          // movk          w9, #0x8c51
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7f7c9                          // mov           w9, #0x3fbe0000
+  .long  0x729791a9                          // movk          w9, #0xbc8d
+  .long  0x6e31fe51                          // fdiv          v17.4s, v18.4s, v17.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a81349                          // mov           w9, #0x409a0000
+  .long  0x4eb1d6f1                          // fsub          v17.4s, v23.4s, v17.4s
+  .long  0x729ebf09                          // movk          w9, #0xf5f8
+  .long  0x6e31de71                          // fmul          v17.4s, v19.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x52a83ba9                          // mov           w9, #0x41dd0000
+  .long  0x4e219a33                          // frintm        v19.4s, v17.4s
+  .long  0x729a5fc9                          // movk          w9, #0xd2fe
+  .long  0x4e34d634                          // fadd          v20.4s, v17.4s, v20.4s
+  .long  0x4eb3d631                          // fsub          v17.4s, v17.4s, v19.4s
+  .long  0x4eb2ce34                          // fmls          v20.4s, v17.4s, v18.4s
+  .long  0x4eb1d6b1                          // fsub          v17.4s, v21.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x91005108                          // add           x8, x8, #0x14
+  .long  0x6e31feb1                          // fdiv          v17.4s, v21.4s, v17.4s
+  .long  0x4e31d691                          // fadd          v17.4s, v20.4s, v17.4s
+  .long  0x4d40c914                          // ld1r          {v20.4s}, [x8]
+  .long  0x4f026573                          // movi          v19.4s, #0x4b, lsl #24
+  .long  0x6e33de31                          // fmul          v17.4s, v17.4s, v19.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x6f00e412                          // movi          v18.2d, #0x0
+  .long  0x6e711e01                          // bsl           v1.16b, v16.16b, v17.16b
+  .long  0x4f03f615                          // fmov          v21.4s, #1.000000000000000000e+00
+  .long  0x4e32f421                          // fmax          v1.4s, v1.4s, v18.4s
+  .long  0x4eb5f421                          // fmin          v1.4s, v1.4s, v21.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_parametric_b_aarch64
+.globl _sk_parametric_b_aarch64
+FUNCTION(_sk_parametric_b_aarch64)
+_sk_parametric_b_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4f016696                          // movi          v22.4s, #0x34, lsl #24
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x9100610a                          // add           x10, x8, #0x18
+  .long  0x4d40c932                          // ld1r          {v18.4s}, [x9]
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0xbd400d11                          // ldr           s17, [x8, #12]
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x4ddfc933                          // ld1r          {v19.4s}, [x9], #4
+  .long  0x9100210a                          // add           x10, x8, #0x8
+  .long  0x4d40c954                          // ld1r          {v20.4s}, [x10]
+  .long  0x4f911050                          // fmla          v16.4s, v2.4s, v17.s[0]
+  .long  0xbd400135                          // ldr           s21, [x9]
+  .long  0x52b85f09                          // mov           w9, #0xc2f80000
+  .long  0x728e6ee9                          // movk          w9, #0x7377
+  .long  0x4e040d37                          // dup           v23.4s, w9
+  .long  0x52a7f7e9                          // mov           w9, #0x3fbf0000
+  .long  0x7297eea9                          // movk          w9, #0xbf75
+  .long  0x4f951054                          // fmla          v20.4s, v2.4s, v21.s[0]
+  .long  0x6e22e642                          // fcmge         v2.4s, v18.4s, v2.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a7d689                          // mov           w9, #0x3eb40000
+  .long  0x4f03d7f1                          // movi          v17.4s, #0x7f, msl #16
+  .long  0x72889f29                          // movk          w9, #0x44f9
+  .long  0x4e21da95                          // scvtf         v21.4s, v20.4s
+  .long  0x4e311e91                          // and           v17.16b, v20.16b, v17.16b
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7fb89                          // mov           w9, #0x3fdc0000
+  .long  0x4e35ced7                          // fmla          v23.4s, v22.4s, v21.4s
+  .long  0x729d3469                          // movk          w9, #0xe9a3
+  .long  0x4f0177f1                          // orr           v17.4s, #0x3f, lsl #24
+  .long  0x4eb2ce37                          // fmls          v23.4s, v17.4s, v18.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a85e49                          // mov           w9, #0x42f20000
+  .long  0x72918a29                          // movk          w9, #0x8c51
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7f7c9                          // mov           w9, #0x3fbe0000
+  .long  0x729791a9                          // movk          w9, #0xbc8d
+  .long  0x6e31fe51                          // fdiv          v17.4s, v18.4s, v17.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a81349                          // mov           w9, #0x409a0000
+  .long  0x4eb1d6f1                          // fsub          v17.4s, v23.4s, v17.4s
+  .long  0x729ebf09                          // movk          w9, #0xf5f8
+  .long  0x6e31de71                          // fmul          v17.4s, v19.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x52a83ba9                          // mov           w9, #0x41dd0000
+  .long  0x4e219a33                          // frintm        v19.4s, v17.4s
+  .long  0x729a5fc9                          // movk          w9, #0xd2fe
+  .long  0x4e34d634                          // fadd          v20.4s, v17.4s, v20.4s
+  .long  0x4eb3d631                          // fsub          v17.4s, v17.4s, v19.4s
+  .long  0x4eb2ce34                          // fmls          v20.4s, v17.4s, v18.4s
+  .long  0x4eb1d6b1                          // fsub          v17.4s, v21.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x91005108                          // add           x8, x8, #0x14
+  .long  0x6e31feb1                          // fdiv          v17.4s, v21.4s, v17.4s
+  .long  0x4e31d691                          // fadd          v17.4s, v20.4s, v17.4s
+  .long  0x4d40c914                          // ld1r          {v20.4s}, [x8]
+  .long  0x4f026573                          // movi          v19.4s, #0x4b, lsl #24
+  .long  0x6e33de31                          // fmul          v17.4s, v17.4s, v19.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x6f00e412                          // movi          v18.2d, #0x0
+  .long  0x6e711e02                          // bsl           v2.16b, v16.16b, v17.16b
+  .long  0x4f03f615                          // fmov          v21.4s, #1.000000000000000000e+00
+  .long  0x4e32f442                          // fmax          v2.4s, v2.4s, v18.4s
+  .long  0x4eb5f442                          // fmin          v2.4s, v2.4s, v21.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_parametric_a_aarch64
+.globl _sk_parametric_a_aarch64
+FUNCTION(_sk_parametric_a_aarch64)
+_sk_parametric_a_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4f016696                          // movi          v22.4s, #0x34, lsl #24
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x9100610a                          // add           x10, x8, #0x18
+  .long  0x4d40c932                          // ld1r          {v18.4s}, [x9]
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0xbd400d11                          // ldr           s17, [x8, #12]
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x4ddfc933                          // ld1r          {v19.4s}, [x9], #4
+  .long  0x9100210a                          // add           x10, x8, #0x8
+  .long  0x4d40c954                          // ld1r          {v20.4s}, [x10]
+  .long  0x4f911070                          // fmla          v16.4s, v3.4s, v17.s[0]
+  .long  0xbd400135                          // ldr           s21, [x9]
+  .long  0x52b85f09                          // mov           w9, #0xc2f80000
+  .long  0x728e6ee9                          // movk          w9, #0x7377
+  .long  0x4e040d37                          // dup           v23.4s, w9
+  .long  0x52a7f7e9                          // mov           w9, #0x3fbf0000
+  .long  0x7297eea9                          // movk          w9, #0xbf75
+  .long  0x4f951074                          // fmla          v20.4s, v3.4s, v21.s[0]
+  .long  0x6e23e643                          // fcmge         v3.4s, v18.4s, v3.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a7d689                          // mov           w9, #0x3eb40000
+  .long  0x4f03d7f1                          // movi          v17.4s, #0x7f, msl #16
+  .long  0x72889f29                          // movk          w9, #0x44f9
+  .long  0x4e21da95                          // scvtf         v21.4s, v20.4s
+  .long  0x4e311e91                          // and           v17.16b, v20.16b, v17.16b
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7fb89                          // mov           w9, #0x3fdc0000
+  .long  0x4e35ced7                          // fmla          v23.4s, v22.4s, v21.4s
+  .long  0x729d3469                          // movk          w9, #0xe9a3
+  .long  0x4f0177f1                          // orr           v17.4s, #0x3f, lsl #24
+  .long  0x4eb2ce37                          // fmls          v23.4s, v17.4s, v18.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a85e49                          // mov           w9, #0x42f20000
+  .long  0x72918a29                          // movk          w9, #0x8c51
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x52a7f7c9                          // mov           w9, #0x3fbe0000
+  .long  0x729791a9                          // movk          w9, #0xbc8d
+  .long  0x6e31fe51                          // fdiv          v17.4s, v18.4s, v17.4s
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x52a81349                          // mov           w9, #0x409a0000
+  .long  0x4eb1d6f1                          // fsub          v17.4s, v23.4s, v17.4s
+  .long  0x729ebf09                          // movk          w9, #0xf5f8
+  .long  0x6e31de71                          // fmul          v17.4s, v19.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x52a83ba9                          // mov           w9, #0x41dd0000
+  .long  0x4e219a33                          // frintm        v19.4s, v17.4s
+  .long  0x729a5fc9                          // movk          w9, #0xd2fe
+  .long  0x4e34d634                          // fadd          v20.4s, v17.4s, v20.4s
+  .long  0x4eb3d631                          // fsub          v17.4s, v17.4s, v19.4s
+  .long  0x4eb2ce34                          // fmls          v20.4s, v17.4s, v18.4s
+  .long  0x4eb1d6b1                          // fsub          v17.4s, v21.4s, v17.4s
+  .long  0x4e040d35                          // dup           v21.4s, w9
+  .long  0x91005108                          // add           x8, x8, #0x14
+  .long  0x6e31feb1                          // fdiv          v17.4s, v21.4s, v17.4s
+  .long  0x4e31d691                          // fadd          v17.4s, v20.4s, v17.4s
+  .long  0x4d40c914                          // ld1r          {v20.4s}, [x8]
+  .long  0x4f026573                          // movi          v19.4s, #0x4b, lsl #24
+  .long  0x6e33de31                          // fmul          v17.4s, v17.4s, v19.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4e34d631                          // fadd          v17.4s, v17.4s, v20.4s
+  .long  0x6f00e412                          // movi          v18.2d, #0x0
+  .long  0x6e711e03                          // bsl           v3.16b, v16.16b, v17.16b
+  .long  0x4f03f615                          // fmov          v21.4s, #1.000000000000000000e+00
+  .long  0x4e32f463                          // fmax          v3.4s, v3.4s, v18.4s
+  .long  0x4eb5f463                          // fmin          v3.4s, v3.4s, v21.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_lab_to_xyz_aarch64
+.globl _sk_lab_to_xyz_aarch64
+FUNCTION(_sk_lab_to_xyz_aarch64)
+_sk_lab_to_xyz_aarch64:
+  .long  0x52a85908                          // mov           w8, #0x42c80000
+  .long  0x4e040d10                          // dup           v16.4s, w8
+  .long  0x52a86fe8                          // mov           w8, #0x437f0000
+  .long  0x4f066471                          // movi          v17.4s, #0xc3, lsl #24
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x52a781a8                          // mov           w8, #0x3c0d0000
+  .long  0x7287b968                          // movk          w8, #0x3dcb
+  .long  0x4eb11e34                          // mov           v20.16b, v17.16b
+  .long  0x4e21ce74                          // fmla          v20.4s, v19.4s, v1.4s
+  .long  0x4e040d01                          // dup           v1.4s, w8
+  .long  0x52a76068                          // mov           w8, #0x3b030000
+  .long  0x72824de8                          // movk          w8, #0x126f
+  .long  0x4e22ce71                          // fmla          v17.4s, v19.4s, v2.4s
+  .long  0x4e040d02                          // dup           v2.4s, w8
+  .long  0x52a77468                          // mov           w8, #0x3ba30000
+  .long  0x729ae148                          // movk          w8, #0xd70a
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x52a78228                          // mov           w8, #0x3c110000
+  .long  0x4f01f612                          // fmov          v18.4s, #1.600000000000000000e+01
+  .long  0x72831848                          // movk          w8, #0x18c2
+  .long  0x4e20ce12                          // fmla          v18.4s, v16.4s, v0.4s
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x52b7c1a8                          // mov           w8, #0xbe0d0000
+  .long  0x7287b968                          // movk          w8, #0x3dcb
+  .long  0x6e21de41                          // fmul          v1.4s, v18.4s, v1.4s
+  .long  0x4e040d10                          // dup           v16.4s, w8
+  .long  0x52a7c068                          // mov           w8, #0x3e030000
+  .long  0x4ea11c32                          // mov           v18.16b, v1.16b
+  .long  0x72900a08                          // movk          w8, #0x8050
+  .long  0x4eb3ce32                          // fmls          v18.4s, v17.4s, v19.4s
+  .long  0x6e21dc31                          // fmul          v17.4s, v1.4s, v1.4s
+  .long  0x4ea11c35                          // mov           v21.16b, v1.16b
+  .long  0x4e30d433                          // fadd          v19.4s, v1.4s, v16.4s
+  .long  0x6e31dc31                          // fmul          v17.4s, v1.4s, v17.4s
+  .long  0x4e34cc55                          // fmla          v21.4s, v2.4s, v20.4s
+  .long  0x4e040d02                          // dup           v2.4s, w8
+  .long  0x6e22de73                          // fmul          v19.4s, v19.4s, v2.4s
+  .long  0x6ea0e621                          // fcmgt         v1.4s, v17.4s, v0.4s
+  .long  0x6e731e21                          // bsl           v1.16b, v17.16b, v19.16b
+  .long  0x6e32de51                          // fmul          v17.4s, v18.4s, v18.4s
+  .long  0x4e30d653                          // fadd          v19.4s, v18.4s, v16.4s
+  .long  0x6e31de51                          // fmul          v17.4s, v18.4s, v17.4s
+  .long  0x52a7eec8                          // mov           w8, #0x3f760000
+  .long  0x6e22de72                          // fmul          v18.4s, v19.4s, v2.4s
+  .long  0x6ea0e633                          // fcmgt         v19.4s, v17.4s, v0.4s
+  .long  0x729ae3e8                          // movk          w8, #0xd71f
+  .long  0x6e721e33                          // bsl           v19.16b, v17.16b, v18.16b
+  .long  0x6e35deb2                          // fmul          v18.4s, v21.4s, v21.4s
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x52a7ea68                          // mov           w8, #0x3f530000
+  .long  0x4e30d6b0                          // fadd          v16.4s, v21.4s, v16.4s
+  .long  0x6e32deb2                          // fmul          v18.4s, v21.4s, v18.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x72881ec8                          // movk          w8, #0x40f6
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0x6ea0e640                          // fcmgt         v0.4s, v18.4s, v0.4s
+  .long  0x4e040d14                          // dup           v20.4s, w8
+  .long  0x6e621e40                          // bsl           v0.16b, v18.16b, v2.16b
+  .long  0x6e31dc00                          // fmul          v0.4s, v0.4s, v17.4s
+  .long  0x6e34de62                          // fmul          v2.4s, v19.4s, v20.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_load_a8_aarch64
+.globl _sk_load_a8_aarch64
+FUNCTION(_sk_load_a8_aarch64)
+_sk_load_a8_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020108                          // add           x8, x8, x2
+  .long  0xb50002e4                          // cbnz          x4, 25e8 <sk_load_a8_aarch64+0x68>
+  .long  0x39400109                          // ldrb          w9, [x8]
+  .long  0x3940050a                          // ldrb          w10, [x8, #1]
+  .long  0x3940090b                          // ldrb          w11, [x8, #2]
+  .long  0x39400d08                          // ldrb          w8, [x8, #3]
+  .long  0x4e021d22                          // mov           v2.h[0], w9
+  .long  0x4e061d42                          // mov           v2.h[1], w10
+  .long  0x4e0a1d62                          // mov           v2.h[2], w11
+  .long  0x4e0e1d02                          // mov           v2.h[3], w8
+  .long  0x2f07b7e2                          // bic           v2.4h, #0xff, lsl #8
+  .long  0x52a77008                          // mov           w8, #0x3b800000
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x72901028                          // movk          w8, #0x8081
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x4e040d03                          // dup           v3.4s, w8
+  .long  0x91004028                          // add           x8, x1, #0x10
+  .long  0x6e21d842                          // ucvtf         v2.4s, v2.4s
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x6e23dc43                          // fmul          v3.4s, v2.4s, v3.4s
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0xaa0803e1                          // mov           x1, x8
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e402                          // movi          d2, #0x0
+  .long  0x54000140                          // b.eq          261c <sk_load_a8_aarch64+0x9c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          2614 <sk_load_a8_aarch64+0x94>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffd61                          // b.ne          25b0 <sk_load_a8_aarch64+0x30>  // b.any
+  .long  0x39400909                          // ldrb          w9, [x8, #2]
+  .long  0x0e020fe2                          // dup           v2.4h, wzr
+  .long  0x4e0a1d22                          // mov           v2.h[2], w9
+  .long  0x39400509                          // ldrb          w9, [x8, #1]
+  .long  0x4e061d22                          // mov           v2.h[1], w9
+  .long  0x39400108                          // ldrb          w8, [x8]
+  .long  0x4e021d02                          // mov           v2.h[0], w8
+  .long  0x17ffffe3                          // b             25b0 <sk_load_a8_aarch64+0x30>
+
+HIDDEN _sk_gather_a8_aarch64
+.globl _sk_gather_a8_aarch64
+FUNCTION(_sk_gather_a8_aarch64)
+_sk_gather_a8_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x52a77009                          // mov           w9, #0x3b800000
+  .long  0x72901029                          // movk          w9, #0x8081
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x4e040d23                          // dup           v3.4s, w9
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x386c490c                          // ldrb          w12, [x8, w12, uxtw]
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x38694909                          // ldrb          w9, [x8, w9, uxtw]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x386a490a                          // ldrb          w10, [x8, w10, uxtw]
+  .long  0x386b4908                          // ldrb          w8, [x8, w11, uxtw]
+  .long  0x4e021d82                          // mov           v2.h[0], w12
+  .long  0x4e061d22                          // mov           v2.h[1], w9
+  .long  0x4e0a1d42                          // mov           v2.h[2], w10
+  .long  0x4e0e1d02                          // mov           v2.h[3], w8
+  .long  0x2f07b7e2                          // bic           v2.4h, #0xff, lsl #8
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x6e21d842                          // ucvtf         v2.4s, v2.4s
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x6e23dc43                          // fmul          v3.4s, v2.4s, v3.4s
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_store_a8_aarch64
+.globl _sk_store_a8_aarch64
+FUNCTION(_sk_store_a8_aarch64)
+_sk_store_a8_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52a86fe9                          // mov           w9, #0x437f0000
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x6e30dc70                          // fmul          v16.4s, v3.4s, v16.4s
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x6e21aa10                          // fcvtnu        v16.4s, v16.4s
+  .long  0x0e612a10                          // xtn           v16.4h, v16.4s
+  .long  0x8b020108                          // add           x8, x8, x2
+  .long  0xb5000184                          // cbnz          x4, 26f0 <sk_store_a8_aarch64+0x50>
+  .long  0x0e0e3e09                          // umov          w9, v16.h[3]
+  .long  0x0e0a3e0a                          // umov          w10, v16.h[2]
+  .long  0x0e063e0b                          // umov          w11, v16.h[1]
+  .long  0x0e023e0c                          // umov          w12, v16.h[0]
+  .long  0x39000d09                          // strb          w9, [x8, #3]
+  .long  0x3900090a                          // strb          w10, [x8, #2]
+  .long  0x3900050b                          // strb          w11, [x8, #1]
+  .long  0x3900010c                          // strb          w12, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x54000120                          // b.eq          271c <sk_store_a8_aarch64+0x7c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000a0                          // b.eq          2714 <sk_store_a8_aarch64+0x74>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffee1                          // b.ne          26e4 <sk_store_a8_aarch64+0x44>  // b.any
+  .long  0x0e0a3e09                          // umov          w9, v16.h[2]
+  .long  0x39000909                          // strb          w9, [x8, #2]
+  .long  0x0e063e09                          // umov          w9, v16.h[1]
+  .long  0x39000509                          // strb          w9, [x8, #1]
+  .long  0x0e023e09                          // umov          w9, v16.h[0]
+  .long  0x39000109                          // strb          w9, [x8]
+  .long  0x17fffff0                          // b             26e4 <sk_store_a8_aarch64+0x44>
+
+HIDDEN _sk_load_g8_aarch64
+.globl _sk_load_g8_aarch64
+FUNCTION(_sk_load_g8_aarch64)
+_sk_load_g8_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020108                          // add           x8, x8, x2
+  .long  0xb50002e4                          // cbnz          x4, 2790 <sk_load_g8_aarch64+0x68>
+  .long  0x39400109                          // ldrb          w9, [x8]
+  .long  0x3940050a                          // ldrb          w10, [x8, #1]
+  .long  0x3940090b                          // ldrb          w11, [x8, #2]
+  .long  0x39400d08                          // ldrb          w8, [x8, #3]
+  .long  0x4e021d20                          // mov           v0.h[0], w9
+  .long  0x4e061d40                          // mov           v0.h[1], w10
+  .long  0x4e0a1d60                          // mov           v0.h[2], w11
+  .long  0x4e0e1d00                          // mov           v0.h[3], w8
+  .long  0x2f07b7e0                          // bic           v0.4h, #0xff, lsl #8
+  .long  0x52a77008                          // mov           w8, #0x3b800000
+  .long  0x72901028                          // movk          w8, #0x8081
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x4e040d01                          // dup           v1.4s, w8
+  .long  0x6e21d800                          // ucvtf         v0.4s, v0.4s
+  .long  0x91004028                          // add           x8, x1, #0x10
+  .long  0x6e21dc00                          // fmul          v0.4s, v0.4s, v1.4s
+  .long  0x4f03f603                          // fmov          v3.4s, #1.000000000000000000e+00
+  .long  0xaa0803e1                          // mov           x1, x8
+  .long  0x4ea01c01                          // mov           v1.16b, v0.16b
+  .long  0x4ea01c02                          // mov           v2.16b, v0.16b
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e400                          // movi          d0, #0x0
+  .long  0x54000140                          // b.eq          27c4 <sk_load_g8_aarch64+0x9c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          27bc <sk_load_g8_aarch64+0x94>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffd61                          // b.ne          2758 <sk_load_g8_aarch64+0x30>  // b.any
+  .long  0x39400909                          // ldrb          w9, [x8, #2]
+  .long  0x0e020fe0                          // dup           v0.4h, wzr
+  .long  0x4e0a1d20                          // mov           v0.h[2], w9
+  .long  0x39400509                          // ldrb          w9, [x8, #1]
+  .long  0x4e061d20                          // mov           v0.h[1], w9
+  .long  0x39400108                          // ldrb          w8, [x8]
+  .long  0x4e021d00                          // mov           v0.h[0], w8
+  .long  0x17ffffe3                          // b             2758 <sk_load_g8_aarch64+0x30>
+
+HIDDEN _sk_gather_g8_aarch64
+.globl _sk_gather_g8_aarch64
+FUNCTION(_sk_gather_g8_aarch64)
+_sk_gather_g8_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x52a77009                          // mov           w9, #0x3b800000
+  .long  0x72901029                          // movk          w9, #0x8081
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x4e040d23                          // dup           v3.4s, w9
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x386c490c                          // ldrb          w12, [x8, w12, uxtw]
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x38694909                          // ldrb          w9, [x8, w9, uxtw]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x386a490a                          // ldrb          w10, [x8, w10, uxtw]
+  .long  0x386b4908                          // ldrb          w8, [x8, w11, uxtw]
+  .long  0x4e021d80                          // mov           v0.h[0], w12
+  .long  0x4e061d20                          // mov           v0.h[1], w9
+  .long  0x4e0a1d40                          // mov           v0.h[2], w10
+  .long  0x4e0e1d00                          // mov           v0.h[3], w8
+  .long  0x2f07b7e0                          // bic           v0.4h, #0xff, lsl #8
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x6e21d800                          // ucvtf         v0.4s, v0.4s
+  .long  0x6e23dc00                          // fmul          v0.4s, v0.4s, v3.4s
+  .long  0x4f03f603                          // fmov          v3.4s, #1.000000000000000000e+00
+  .long  0x4ea01c01                          // mov           v1.16b, v0.16b
+  .long  0x4ea01c02                          // mov           v2.16b, v0.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_gather_i8_aarch64
+.globl _sk_gather_i8_aarch64
+FUNCTION(_sk_gather_i8_aarch64)
+_sk_gather_i8_aarch64:
+  .long  0xaa0103e8                          // mov           x8, x1
+  .long  0xf8408429                          // ldr           x9, [x1], #8
+  .long  0xb4000069                          // cbz           x9, 285c <sk_gather_i8_aarch64+0x14>
+  .long  0xaa0903ea                          // mov           x10, x9
+  .long  0x14000003                          // b             2864 <sk_gather_i8_aarch64+0x1c>
+  .long  0xf940050a                          // ldr           x10, [x8, #8]
+  .long  0x91004101                          // add           x1, x8, #0x10
+  .long  0xf8410548                          // ldr           x8, [x10], #16
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0xf9400529                          // ldr           x9, [x9, #8]
+  .long  0x4d40c942                          // ld1r          {v2.4s}, [x10]
+  .long  0x6f00e623                          // movi          v3.2d, #0xff000000ff
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x1e26000d                          // fmov          w13, s0
+  .long  0x0e0c3c0a                          // mov           w10, v0.s[1]
+  .long  0x386d490d                          // ldrb          w13, [x8, w13, uxtw]
+  .long  0x0e143c0b                          // mov           w11, v0.s[2]
+  .long  0x386a490a                          // ldrb          w10, [x8, w10, uxtw]
+  .long  0x0e1c3c0c                          // mov           w12, v0.s[3]
+  .long  0x386b490b                          // ldrb          w11, [x8, w11, uxtw]
+  .long  0x386c4908                          // ldrb          w8, [x8, w12, uxtw]
+  .long  0x4e021da0                          // mov           v0.h[0], w13
+  .long  0x4e061d40                          // mov           v0.h[1], w10
+  .long  0x4e0a1d60                          // mov           v0.h[2], w11
+  .long  0x4e0e1d00                          // mov           v0.h[3], w8
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x4e231c00                          // and           v0.16b, v0.16b, v3.16b
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x8b2c492c                          // add           x12, x9, w12, uxtw #2
+  .long  0x0e0c3c08                          // mov           w8, v0.s[1]
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x0d408180                          // ld1           {v0.s}[0], [x12]
+  .long  0x8b284928                          // add           x8, x9, w8, uxtw #2
+  .long  0xb86a592a                          // ldr           w10, [x9, w10, uxtw #2]
+  .long  0x52a7700c                          // mov           w12, #0x3b800000
+  .long  0x0d409100                          // ld1           {v0.s}[1], [x8]
+  .long  0xb86b5928                          // ldr           w8, [x9, w11, uxtw #2]
+  .long  0x7290102c                          // movk          w12, #0x8081
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e141d40                          // mov           v0.s[2], w10
+  .long  0x4e1c1d00                          // mov           v0.s[3], w8
+  .long  0x4e231c01                          // and           v1.16b, v0.16b, v3.16b
+  .long  0x6f380402                          // ushr          v2.4s, v0.4s, #8
+  .long  0x6f300411                          // ushr          v17.4s, v0.4s, #16
+  .long  0x4e040d90                          // dup           v16.4s, w12
+  .long  0x6f280400                          // ushr          v0.4s, v0.4s, #24
+  .long  0x4e21d821                          // scvtf         v1.4s, v1.4s
+  .long  0x4e231c42                          // and           v2.16b, v2.16b, v3.16b
+  .long  0x4e231e23                          // and           v3.16b, v17.16b, v3.16b
+  .long  0x4e21d811                          // scvtf         v17.4s, v0.4s
+  .long  0x6e30dc20                          // fmul          v0.4s, v1.4s, v16.4s
+  .long  0x4e21d841                          // scvtf         v1.4s, v2.4s
+  .long  0x4e21d862                          // scvtf         v2.4s, v3.4s
+  .long  0x6e30dc21                          // fmul          v1.4s, v1.4s, v16.4s
+  .long  0x6e30dc42                          // fmul          v2.4s, v2.4s, v16.4s
+  .long  0x6e30de23                          // fmul          v3.4s, v17.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_load_565_aarch64
+.globl _sk_load_565_aarch64
+FUNCTION(_sk_load_565_aarch64)
+_sk_load_565_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020508                          // add           x8, x8, x2, lsl #1
+  .long  0xb50003c4                          // cbnz          x4, 29b8 <sk_load_565_aarch64+0x84>
+  .long  0xfd400100                          // ldr           d0, [x8]
+  .long  0x321b17e8                          // orr           w8, wzr, #0x7e0
+  .long  0x4e040d02                          // dup           v2.4s, w8
+  .long  0x52a6f088                          // mov           w8, #0x37840000
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x4f072701                          // movi          v1.4s, #0xf8, lsl #8
+  .long  0x72842108                          // movk          w8, #0x2108
+  .long  0x4f0007e3                          // movi          v3.4s, #0x1f
+  .long  0x4e211c01                          // and           v1.16b, v0.16b, v1.16b
+  .long  0x4e231c03                          // and           v3.16b, v0.16b, v3.16b
+  .long  0x4e221c10                          // and           v16.16b, v0.16b, v2.16b
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x52a7a088                          // mov           w8, #0x3d040000
+  .long  0x72842108                          // movk          w8, #0x2108
+  .long  0x4e21d821                          // scvtf         v1.4s, v1.4s
+  .long  0x6e20dc20                          // fmul          v0.4s, v1.4s, v0.4s
+  .long  0x4e040d01                          // dup           v1.4s, w8
+  .long  0x52a74048                          // mov           w8, #0x3a020000
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x72810428                          // movk          w8, #0x821
+  .long  0x4e21d862                          // scvtf         v2.4s, v3.4s
+  .long  0x6e21dc42                          // fmul          v2.4s, v2.4s, v1.4s
+  .long  0x4e040d01                          // dup           v1.4s, w8
+  .long  0x91004028                          // add           x8, x1, #0x10
+  .long  0x4e21da03                          // scvtf         v3.4s, v16.4s
+  .long  0x6e21dc61                          // fmul          v1.4s, v3.4s, v1.4s
+  .long  0x4f03f603                          // fmov          v3.4s, #1.000000000000000000e+00
+  .long  0xaa0803e1                          // mov           x1, x8
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e400                          // movi          d0, #0x0
+  .long  0x54000140                          // b.eq          29ec <sk_load_565_aarch64+0xb8>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          29e4 <sk_load_565_aarch64+0xb0>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffba1                          // b.ne          2948 <sk_load_565_aarch64+0x14>  // b.any
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0e020fe0                          // dup           v0.4h, wzr
+  .long  0x0d405120                          // ld1           {v0.h}[2], [x9]
+  .long  0x91000909                          // add           x9, x8, #0x2
+  .long  0x0d404920                          // ld1           {v0.h}[1], [x9]
+  .long  0x0d404100                          // ld1           {v0.h}[0], [x8]
+  .long  0x17ffffd6                          // b             2948 <sk_load_565_aarch64+0x14>
+
+HIDDEN _sk_gather_565_aarch64
+.globl _sk_gather_565_aarch64
+FUNCTION(_sk_gather_565_aarch64)
+_sk_gather_565_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x321b17e9                          // orr           w9, wzr, #0x7e0
+  .long  0x4e040d23                          // dup           v3.4s, w9
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x8b2c450c                          // add           x12, x8, w12, uxtw #1
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x0d404180                          // ld1           {v0.h}[0], [x12]
+  .long  0x78695909                          // ldrh          w9, [x8, w9, uxtw #1]
+  .long  0x786a590a                          // ldrh          w10, [x8, w10, uxtw #1]
+  .long  0x786b5908                          // ldrh          w8, [x8, w11, uxtw #1]
+  .long  0x4f072701                          // movi          v1.4s, #0xf8, lsl #8
+  .long  0x4e061d20                          // mov           v0.h[1], w9
+  .long  0x4e0a1d40                          // mov           v0.h[2], w10
+  .long  0x4e0e1d00                          // mov           v0.h[3], w8
+  .long  0x52a6f08b                          // mov           w11, #0x37840000
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x7284210b                          // movk          w11, #0x2108
+  .long  0x52a74049                          // mov           w9, #0x3a020000
+  .long  0x4f0007e2                          // movi          v2.4s, #0x1f
+  .long  0x4e211c01                          // and           v1.16b, v0.16b, v1.16b
+  .long  0x72810429                          // movk          w9, #0x821
+  .long  0x52a7a08a                          // mov           w10, #0x3d040000
+  .long  0x4e231c03                          // and           v3.16b, v0.16b, v3.16b
+  .long  0x4e221c02                          // and           v2.16b, v0.16b, v2.16b
+  .long  0x4e040d60                          // dup           v0.4s, w11
+  .long  0x4e21d821                          // scvtf         v1.4s, v1.4s
+  .long  0x7284210a                          // movk          w10, #0x2108
+  .long  0x6e20dc20                          // fmul          v0.4s, v1.4s, v0.4s
+  .long  0x4e040d21                          // dup           v1.4s, w9
+  .long  0x4e21d863                          // scvtf         v3.4s, v3.4s
+  .long  0x6e21dc61                          // fmul          v1.4s, v3.4s, v1.4s
+  .long  0x4e040d43                          // dup           v3.4s, w10
+  .long  0x4e21d842                          // scvtf         v2.4s, v2.4s
+  .long  0x6e23dc42                          // fmul          v2.4s, v2.4s, v3.4s
+  .long  0x4f03f603                          // fmov          v3.4s, #1.000000000000000000e+00
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_store_565_aarch64
+.globl _sk_store_565_aarch64
+FUNCTION(_sk_store_565_aarch64)
+_sk_store_565_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52a84f89                          // mov           w9, #0x427c0000
+  .long  0x4f01f7f0                          // fmov          v16.4s, #3.100000000000000000e+01
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x6e30dc11                          // fmul          v17.4s, v0.4s, v16.4s
+  .long  0x6e32dc32                          // fmul          v18.4s, v1.4s, v18.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0x6e21aa52                          // fcvtnu        v18.4s, v18.4s
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x6e30dc50                          // fmul          v16.4s, v2.4s, v16.4s
+  .long  0x4f2b5631                          // shl           v17.4s, v17.4s, #11
+  .long  0x4f255652                          // shl           v18.4s, v18.4s, #5
+  .long  0x6e21aa10                          // fcvtnu        v16.4s, v16.4s
+  .long  0x4eb11e51                          // orr           v17.16b, v18.16b, v17.16b
+  .long  0x4eb01e30                          // orr           v16.16b, v17.16b, v16.16b
+  .long  0x8b020508                          // add           x8, x8, x2, lsl #1
+  .long  0x0e612a10                          // xtn           v16.4h, v16.4s
+  .long  0xb50000a4                          // cbnz          x4, 2afc <sk_store_565_aarch64+0x58>
+  .long  0xfd000110                          // str           d16, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x54000120                          // b.eq          2b28 <sk_store_565_aarch64+0x84>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000a0                          // b.eq          2b20 <sk_store_565_aarch64+0x7c>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffee1                          // b.ne          2af0 <sk_store_565_aarch64+0x4c>  // b.any
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0d005130                          // st1           {v16.h}[2], [x9]
+  .long  0x91000909                          // add           x9, x8, #0x2
+  .long  0x0d004930                          // st1           {v16.h}[1], [x9]
+  .long  0x0d004110                          // st1           {v16.h}[0], [x8]
+  .long  0x17fffff1                          // b             2af0 <sk_store_565_aarch64+0x4c>
+
+HIDDEN _sk_load_4444_aarch64
+.globl _sk_load_4444_aarch64
+FUNCTION(_sk_load_4444_aarch64)
+_sk_load_4444_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020508                          // add           x8, x8, x2, lsl #1
+  .long  0xb5000444                          // cbnz          x4, 2bc4 <sk_load_4444_aarch64+0x94>
+  .long  0xfd400100                          // ldr           d0, [x8]
+  .long  0x52a6f108                          // mov           w8, #0x37880000
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x4f072601                          // movi          v1.4s, #0xf0, lsl #8
+  .long  0x72911128                          // movk          w8, #0x8889
+  .long  0x4f0025e2                          // movi          v2.4s, #0xf, lsl #8
+  .long  0x4f070603                          // movi          v3.4s, #0xf0
+  .long  0x4f0005f0                          // movi          v16.4s, #0xf
+  .long  0x4e211c01                          // and           v1.16b, v0.16b, v1.16b
+  .long  0x4e221c02                          // and           v2.16b, v0.16b, v2.16b
+  .long  0x4e231c03                          // and           v3.16b, v0.16b, v3.16b
+  .long  0x4e301c10                          // and           v16.16b, v0.16b, v16.16b
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x52a73108                          // mov           w8, #0x39880000
+  .long  0x72911128                          // movk          w8, #0x8889
+  .long  0x4e21d821                          // scvtf         v1.4s, v1.4s
+  .long  0x6e20dc20                          // fmul          v0.4s, v1.4s, v0.4s
+  .long  0x4e040d01                          // dup           v1.4s, w8
+  .long  0x52a77108                          // mov           w8, #0x3b880000
+  .long  0x72911128                          // movk          w8, #0x8889
+  .long  0x4e21d842                          // scvtf         v2.4s, v2.4s
+  .long  0x6e21dc41                          // fmul          v1.4s, v2.4s, v1.4s
+  .long  0x4e040d02                          // dup           v2.4s, w8
+  .long  0x52a7b108                          // mov           w8, #0x3d880000
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x72911128                          // movk          w8, #0x8889
+  .long  0x4e21d863                          // scvtf         v3.4s, v3.4s
+  .long  0x6e22dc62                          // fmul          v2.4s, v3.4s, v2.4s
+  .long  0x4e040d03                          // dup           v3.4s, w8
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e23de03                          // fmul          v3.4s, v16.4s, v3.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x2f00e400                          // movi          d0, #0x0
+  .long  0x54000140                          // b.eq          2bf8 <sk_load_4444_aarch64+0xc8>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          2bf0 <sk_load_4444_aarch64+0xc0>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffb21                          // b.ne          2b44 <sk_load_4444_aarch64+0x14>  // b.any
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0e020fe0                          // dup           v0.4h, wzr
+  .long  0x0d405120                          // ld1           {v0.h}[2], [x9]
+  .long  0x91000909                          // add           x9, x8, #0x2
+  .long  0x0d404920                          // ld1           {v0.h}[1], [x9]
+  .long  0x0d404100                          // ld1           {v0.h}[0], [x8]
+  .long  0x17ffffd2                          // b             2b44 <sk_load_4444_aarch64+0x14>
+
+HIDDEN _sk_gather_4444_aarch64
+.globl _sk_gather_4444_aarch64
+FUNCTION(_sk_gather_4444_aarch64)
+_sk_gather_4444_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0x4f070603                          // movi          v3.4s, #0xf0
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x4f0005f0                          // movi          v16.4s, #0xf
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x8b2c450c                          // add           x12, x8, w12, uxtw #1
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x0d404180                          // ld1           {v0.h}[0], [x12]
+  .long  0x78695909                          // ldrh          w9, [x8, w9, uxtw #1]
+  .long  0x786a590a                          // ldrh          w10, [x8, w10, uxtw #1]
+  .long  0x786b5908                          // ldrh          w8, [x8, w11, uxtw #1]
+  .long  0x4f072601                          // movi          v1.4s, #0xf0, lsl #8
+  .long  0x4e061d20                          // mov           v0.h[1], w9
+  .long  0x4e0a1d40                          // mov           v0.h[2], w10
+  .long  0x4e0e1d00                          // mov           v0.h[3], w8
+  .long  0x52a6f10b                          // mov           w11, #0x37880000
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x7291112b                          // movk          w11, #0x8889
+  .long  0x4f0025e2                          // movi          v2.4s, #0xf, lsl #8
+  .long  0x52a73109                          // mov           w9, #0x39880000
+  .long  0x4e211c01                          // and           v1.16b, v0.16b, v1.16b
+  .long  0x72911129                          // movk          w9, #0x8889
+  .long  0x52a7710a                          // mov           w10, #0x3b880000
+  .long  0x4e221c02                          // and           v2.16b, v0.16b, v2.16b
+  .long  0x4e231c03                          // and           v3.16b, v0.16b, v3.16b
+  .long  0x4e301c10                          // and           v16.16b, v0.16b, v16.16b
+  .long  0x4e040d60                          // dup           v0.4s, w11
+  .long  0x4e21d821                          // scvtf         v1.4s, v1.4s
+  .long  0x7291112a                          // movk          w10, #0x8889
+  .long  0x52a7b108                          // mov           w8, #0x3d880000
+  .long  0x6e20dc20                          // fmul          v0.4s, v1.4s, v0.4s
+  .long  0x4e040d21                          // dup           v1.4s, w9
+  .long  0x4e21d842                          // scvtf         v2.4s, v2.4s
+  .long  0x72911128                          // movk          w8, #0x8889
+  .long  0x6e21dc41                          // fmul          v1.4s, v2.4s, v1.4s
+  .long  0x4e040d42                          // dup           v2.4s, w10
+  .long  0x4e21d863                          // scvtf         v3.4s, v3.4s
+  .long  0x6e22dc62                          // fmul          v2.4s, v3.4s, v2.4s
+  .long  0x4e040d03                          // dup           v3.4s, w8
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x6e23de03                          // fmul          v3.4s, v16.4s, v3.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_store_4444_aarch64
+.globl _sk_store_4444_aarch64
+FUNCTION(_sk_store_4444_aarch64)
+_sk_store_4444_aarch64:
+  .long  0x4f01f5d0                          // fmov          v16.4s, #1.500000000000000000e+01
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x6e30dc11                          // fmul          v17.4s, v0.4s, v16.4s
+  .long  0x6e30dc32                          // fmul          v18.4s, v1.4s, v16.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0x6e21aa52                          // fcvtnu        v18.4s, v18.4s
+  .long  0x4f2c5631                          // shl           v17.4s, v17.4s, #12
+  .long  0x4f285652                          // shl           v18.4s, v18.4s, #8
+  .long  0x4eb11e51                          // orr           v17.16b, v18.16b, v17.16b
+  .long  0x6e30dc52                          // fmul          v18.4s, v2.4s, v16.4s
+  .long  0x6e21aa52                          // fcvtnu        v18.4s, v18.4s
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x6e30dc70                          // fmul          v16.4s, v3.4s, v16.4s
+  .long  0x4f245652                          // shl           v18.4s, v18.4s, #4
+  .long  0x6e21aa10                          // fcvtnu        v16.4s, v16.4s
+  .long  0x4eb21e31                          // orr           v17.16b, v17.16b, v18.16b
+  .long  0x4eb01e30                          // orr           v16.16b, v17.16b, v16.16b
+  .long  0x8b020508                          // add           x8, x8, x2, lsl #1
+  .long  0x0e612a10                          // xtn           v16.4h, v16.4s
+  .long  0xb50000a4                          // cbnz          x4, 2d24 <sk_store_4444_aarch64+0x60>
+  .long  0xfd000110                          // str           d16, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x54000120                          // b.eq          2d50 <sk_store_4444_aarch64+0x8c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000a0                          // b.eq          2d48 <sk_store_4444_aarch64+0x84>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffee1                          // b.ne          2d18 <sk_store_4444_aarch64+0x54>  // b.any
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0d005130                          // st1           {v16.h}[2], [x9]
+  .long  0x91000909                          // add           x9, x8, #0x2
+  .long  0x0d004930                          // st1           {v16.h}[1], [x9]
+  .long  0x0d004110                          // st1           {v16.h}[0], [x8]
+  .long  0x17fffff1                          // b             2d18 <sk_store_4444_aarch64+0x54>
+
+HIDDEN _sk_load_8888_aarch64
+.globl _sk_load_8888_aarch64
+FUNCTION(_sk_load_8888_aarch64)
+_sk_load_8888_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020908                          // add           x8, x8, x2, lsl #2
+  .long  0xb50002e4                          // cbnz          x4, 2dc0 <sk_load_8888_aarch64+0x68>
+  .long  0x3dc00100                          // ldr           q0, [x8]
+  .long  0x6f00e621                          // movi          v1.2d, #0xff000000ff
+  .long  0x52a77008                          // mov           w8, #0x3b800000
+  .long  0x6f380402                          // ushr          v2.4s, v0.4s, #8
+  .long  0x6f300403                          // ushr          v3.4s, v0.4s, #16
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x72901028                          // movk          w8, #0x8081
+  .long  0x6f280410                          // ushr          v16.4s, v0.4s, #24
+  .long  0x4e211c00                          // and           v0.16b, v0.16b, v1.16b
+  .long  0x4e211c42                          // and           v2.16b, v2.16b, v1.16b
+  .long  0x4e211c61                          // and           v1.16b, v3.16b, v1.16b
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x4e21da03                          // scvtf         v3.4s, v16.4s
+  .long  0x4e21d800                          // scvtf         v0.4s, v0.4s
+  .long  0x4e21d842                          // scvtf         v2.4s, v2.4s
+  .long  0x4e21d830                          // scvtf         v16.4s, v1.4s
+  .long  0x6e31dc63                          // fmul          v3.4s, v3.4s, v17.4s
+  .long  0x6e31dc00                          // fmul          v0.4s, v0.4s, v17.4s
+  .long  0x6e31dc41                          // fmul          v1.4s, v2.4s, v17.4s
+  .long  0x6e31de02                          // fmul          v2.4s, v16.4s, v17.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x54000140                          // b.eq          2df4 <sk_load_8888_aarch64+0x9c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000c0                          // b.eq          2dec <sk_load_8888_aarch64+0x94>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffc81                          // b.ne          2d6c <sk_load_8888_aarch64+0x14>  // b.any
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x4e040fe0                          // dup           v0.4s, wzr
+  .long  0x4d408120                          // ld1           {v0.s}[2], [x9]
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0d409120                          // ld1           {v0.s}[1], [x9]
+  .long  0x0d408100                          // ld1           {v0.s}[0], [x8]
+  .long  0x17ffffdd                          // b             2d6c <sk_load_8888_aarch64+0x14>
+
+HIDDEN _sk_gather_8888_aarch64
+.globl _sk_gather_8888_aarch64
+FUNCTION(_sk_gather_8888_aarch64)
+_sk_gather_8888_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x8b2c490c                          // add           x12, x8, w12, uxtw #2
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x0d408180                          // ld1           {v0.s}[0], [x12]
+  .long  0x8b294909                          // add           x9, x8, w9, uxtw #2
+  .long  0xb86a590a                          // ldr           w10, [x8, w10, uxtw #2]
+  .long  0xb86b5908                          // ldr           w8, [x8, w11, uxtw #2]
+  .long  0x0d409120                          // ld1           {v0.s}[1], [x9]
+  .long  0x6f00e621                          // movi          v1.2d, #0xff000000ff
+  .long  0x52a77009                          // mov           w9, #0x3b800000
+  .long  0x72901029                          // movk          w9, #0x8081
+  .long  0x4e141d40                          // mov           v0.s[2], w10
+  .long  0x4e1c1d00                          // mov           v0.s[3], w8
+  .long  0x6f380410                          // ushr          v16.4s, v0.4s, #8
+  .long  0x6f300411                          // ushr          v17.4s, v0.4s, #16
+  .long  0x4e211c03                          // and           v3.16b, v0.16b, v1.16b
+  .long  0x6f280400                          // ushr          v0.4s, v0.4s, #24
+  .long  0x4e211e10                          // and           v16.16b, v16.16b, v1.16b
+  .long  0x4e211e21                          // and           v1.16b, v17.16b, v1.16b
+  .long  0x4e040d22                          // dup           v2.4s, w9
+  .long  0x4e21d863                          // scvtf         v3.4s, v3.4s
+  .long  0x4e21d811                          // scvtf         v17.4s, v0.4s
+  .long  0x4e21da10                          // scvtf         v16.4s, v16.4s
+  .long  0x4e21d832                          // scvtf         v18.4s, v1.4s
+  .long  0x6e22dc60                          // fmul          v0.4s, v3.4s, v2.4s
+  .long  0x6e22de23                          // fmul          v3.4s, v17.4s, v2.4s
+  .long  0x6e22de01                          // fmul          v1.4s, v16.4s, v2.4s
+  .long  0x6e22de42                          // fmul          v2.4s, v18.4s, v2.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_store_8888_aarch64
+.globl _sk_store_8888_aarch64
+FUNCTION(_sk_store_8888_aarch64)
+_sk_store_8888_aarch64:
+  .long  0x52a86fe9                          // mov           w9, #0x437f0000
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x6e30dc32                          // fmul          v18.4s, v1.4s, v16.4s
+  .long  0x6e30dc11                          // fmul          v17.4s, v0.4s, v16.4s
+  .long  0x6e21aa52                          // fcvtnu        v18.4s, v18.4s
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0x4f285652                          // shl           v18.4s, v18.4s, #8
+  .long  0x4eb11e51                          // orr           v17.16b, v18.16b, v17.16b
+  .long  0x6e30dc52                          // fmul          v18.4s, v2.4s, v16.4s
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x6e30dc70                          // fmul          v16.4s, v3.4s, v16.4s
+  .long  0x6e21aa52                          // fcvtnu        v18.4s, v18.4s
+  .long  0x6e21aa10                          // fcvtnu        v16.4s, v16.4s
+  .long  0x4f305652                          // shl           v18.4s, v18.4s, #16
+  .long  0x4f385610                          // shl           v16.4s, v16.4s, #24
+  .long  0x4eb21e31                          // orr           v17.16b, v17.16b, v18.16b
+  .long  0x8b020908                          // add           x8, x8, x2, lsl #2
+  .long  0x4eb01e30                          // orr           v16.16b, v17.16b, v16.16b
+  .long  0xb50000a4                          // cbnz          x4, 2ef4 <sk_store_8888_aarch64+0x60>
+  .long  0x3d800110                          // str           q16, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x12000489                          // and           w9, w4, #0x3
+  .long  0x7100053f                          // cmp           w9, #0x1
+  .long  0x54000120                          // b.eq          2f20 <sk_store_8888_aarch64+0x8c>  // b.none
+  .long  0x7100093f                          // cmp           w9, #0x2
+  .long  0x540000a0                          // b.eq          2f18 <sk_store_8888_aarch64+0x84>  // b.none
+  .long  0x71000d3f                          // cmp           w9, #0x3
+  .long  0x54fffee1                          // b.ne          2ee8 <sk_store_8888_aarch64+0x54>  // b.any
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x4d008130                          // st1           {v16.s}[2], [x9]
+  .long  0x91001109                          // add           x9, x8, #0x4
+  .long  0x0d009130                          // st1           {v16.s}[1], [x9]
+  .long  0x0d008110                          // st1           {v16.s}[0], [x8]
+  .long  0x17fffff1                          // b             2ee8 <sk_store_8888_aarch64+0x54>
+
+HIDDEN _sk_load_f16_aarch64
+.globl _sk_load_f16_aarch64
+FUNCTION(_sk_load_f16_aarch64)
+_sk_load_f16_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020d08                          // add           x8, x8, x2, lsl #3
+  .long  0xb5000124                          // cbnz          x4, 2f58 <sk_load_f16_aarch64+0x30>
+  .long  0x0c400510                          // ld4           {v16.4h-v19.4h}, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x0e217a00                          // fcvtl         v0.4s, v16.4h
+  .long  0x0e217a21                          // fcvtl         v1.4s, v17.4h
+  .long  0x0e217a42                          // fcvtl         v2.4s, v18.4h
+  .long  0x0e217a63                          // fcvtl         v3.4s, v19.4h
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x0d606110                          // ld4           {v16.h-v19.h}[0], [x8]
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x54fffee0                          // b.eq          2f3c <sk_load_f16_aarch64+0x14>  // b.none
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x0d606930                          // ld4           {v16.h-v19.h}[1], [x9]
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x54fffe63                          // b.cc          2f3c <sk_load_f16_aarch64+0x14>  // b.lo, b.ul, b.last
+  .long  0x91004108                          // add           x8, x8, #0x10
+  .long  0x0d607110                          // ld4           {v16.h-v19.h}[2], [x8]
+  .long  0x17fffff0                          // b             2f3c <sk_load_f16_aarch64+0x14>
+
+HIDDEN _sk_gather_f16_aarch64
+.globl _sk_gather_f16_aarch64
+FUNCTION(_sk_gather_f16_aarch64)
+_sk_gather_f16_aarch64:
+  .long  0xa9bf7bfd                          // stp           x29, x30, [sp, #-16]!
+  .long  0xd100c3e9                          // sub           x9, sp, #0x30
+  .long  0x910003fd                          // mov           x29, sp
+  .long  0x927be93f                          // and           sp, x9, #0xffffffffffffffe0
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x4ea1b800                          // fcvtzs        v0.4s, v0.4s
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x4d40c922                          // ld1r          {v2.4s}, [x9]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x4ea19440                          // mla           v0.4s, v2.4s, v1.4s
+  .long  0x0e143c0a                          // mov           w10, v0.s[2]
+  .long  0x1e26000c                          // fmov          w12, s0
+  .long  0x8b2c4d0c                          // add           x12, x8, w12, uxtw #3
+  .long  0x8b2a4d0a                          // add           x10, x8, w10, uxtw #3
+  .long  0x0e0c3c09                          // mov           w9, v0.s[1]
+  .long  0x0e1c3c0b                          // mov           w11, v0.s[3]
+  .long  0x0d408540                          // ld1           {v0.d}[0], [x10]
+  .long  0x0d408581                          // ld1           {v1.d}[0], [x12]
+  .long  0x8b294d09                          // add           x9, x8, w9, uxtw #3
+  .long  0x8b2b4d08                          // add           x8, x8, w11, uxtw #3
+  .long  0x4d408500                          // ld1           {v0.d}[1], [x8]
+  .long  0x4d408521                          // ld1           {v1.d}[1], [x9]
+  .long  0x910003e8                          // mov           x8, sp
+  .long  0xad0003e1                          // stp           q1, q0, [sp]
+  .long  0x0c400510                          // ld4           {v16.4h-v19.4h}, [x8]
+  .long  0xf9400428                          // ldr           x8, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0x0e217a00                          // fcvtl         v0.4s, v16.4h
+  .long  0x0e217a21                          // fcvtl         v1.4s, v17.4h
+  .long  0x0e217a42                          // fcvtl         v2.4s, v18.4h
+  .long  0x0e217a63                          // fcvtl         v3.4s, v19.4h
+  .long  0xd63f0100                          // blr           x8
+  .long  0x910003bf                          // mov           sp, x29
+  .long  0xa8c17bfd                          // ldp           x29, x30, [sp], #16
+  .long  0xd65f03c0                          // ret
+
+HIDDEN _sk_store_f16_aarch64
+.globl _sk_store_f16_aarch64
+FUNCTION(_sk_store_f16_aarch64)
+_sk_store_f16_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x0e216810                          // fcvtn         v16.4h, v0.4s
+  .long  0x0e216831                          // fcvtn         v17.4h, v1.4s
+  .long  0x0e216852                          // fcvtn         v18.4h, v2.4s
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x0e216873                          // fcvtn         v19.4h, v3.4s
+  .long  0x8b020d08                          // add           x8, x8, x2, lsl #3
+  .long  0xb50000a4                          // cbnz          x4, 3040 <sk_store_f16_aarch64+0x30>
+  .long  0x0c000510                          // st4           {v16.4h-v19.4h}, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x0d206110                          // st4           {v16.h-v19.h}[0], [x8]
+  .long  0x54ffff60                          // b.eq          3034 <sk_store_f16_aarch64+0x24>  // b.none
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x0d206930                          // st4           {v16.h-v19.h}[1], [x9]
+  .long  0x54fffee3                          // b.cc          3034 <sk_store_f16_aarch64+0x24>  // b.lo, b.ul, b.last
+  .long  0x91004108                          // add           x8, x8, #0x10
+  .long  0x0d207110                          // st4           {v16.h-v19.h}[2], [x8]
+  .long  0x17fffff4                          // b             3034 <sk_store_f16_aarch64+0x24>
+
+HIDDEN _sk_load_u16_be_aarch64
+.globl _sk_load_u16_be_aarch64
+FUNCTION(_sk_load_u16_be_aarch64)
+_sk_load_u16_be_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b020d08                          // add           x8, x8, x2, lsl #3
+  .long  0xb5000404                          // cbnz          x4, 30f4 <sk_load_u16_be_aarch64+0x8c>
+  .long  0x0c400500                          // ld4           {v0.4h-v3.4h}, [x8]
+  .long  0x0f185410                          // shl           v16.4h, v0.4h, #8
+  .long  0x2f180411                          // ushr          v17.4h, v0.4h, #8
+  .long  0x0f185432                          // shl           v18.4h, v1.4h, #8
+  .long  0x2f180433                          // ushr          v19.4h, v1.4h, #8
+  .long  0x0f185454                          // shl           v20.4h, v2.4h, #8
+  .long  0x2f180455                          // ushr          v21.4h, v2.4h, #8
+  .long  0x0f185476                          // shl           v22.4h, v3.4h, #8
+  .long  0x2f180460                          // ushr          v0.4h, v3.4h, #8
+  .long  0x52a6f008                          // mov           w8, #0x37800000
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x0eb11e01                          // orr           v1.8b, v16.8b, v17.8b
+  .long  0x0eb31e42                          // orr           v2.8b, v18.8b, v19.8b
+  .long  0x0eb51e90                          // orr           v16.8b, v20.8b, v21.8b
+  .long  0x0ea01ec0                          // orr           v0.8b, v22.8b, v0.8b
+  .long  0x72801008                          // movk          w8, #0x80
+  .long  0x2f10a421                          // uxtl          v1.4s, v1.4h
+  .long  0x2f10a442                          // uxtl          v2.4s, v2.4h
+  .long  0x2f10a610                          // uxtl          v16.4s, v16.4h
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x4e040d03                          // dup           v3.4s, w8
+  .long  0x6e21d821                          // ucvtf         v1.4s, v1.4s
+  .long  0x6e21d842                          // ucvtf         v2.4s, v2.4s
+  .long  0x6e21da10                          // ucvtf         v16.4s, v16.4s
+  .long  0x6e21d811                          // ucvtf         v17.4s, v0.4s
+  .long  0x6e23dc20                          // fmul          v0.4s, v1.4s, v3.4s
+  .long  0x6e23dc41                          // fmul          v1.4s, v2.4s, v3.4s
+  .long  0x6e23de02                          // fmul          v2.4s, v16.4s, v3.4s
+  .long  0x6e23de23                          // fmul          v3.4s, v17.4s, v3.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x0d606100                          // ld4           {v0.h-v3.h}[0], [x8]
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x54fffc00                          // b.eq          307c <sk_load_u16_be_aarch64+0x14>  // b.none
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0x0d606920                          // ld4           {v0.h-v3.h}[1], [x9]
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x54fffb83                          // b.cc          307c <sk_load_u16_be_aarch64+0x14>  // b.lo, b.ul, b.last
+  .long  0x91004108                          // add           x8, x8, #0x10
+  .long  0x0d607100                          // ld4           {v0.h-v3.h}[2], [x8]
+  .long  0x17ffffd9                          // b             307c <sk_load_u16_be_aarch64+0x14>
+
+HIDDEN _sk_load_rgb_u16_be_aarch64
+.globl _sk_load_rgb_u16_be_aarch64
+FUNCTION(_sk_load_rgb_u16_be_aarch64)
+_sk_load_rgb_u16_be_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x321f07e9                          // orr           w9, wzr, #0x6
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x9b092048                          // madd          x8, x2, x9, x8
+  .long  0xb5000384                          // cbnz          x4, 319c <sk_load_rgb_u16_be_aarch64+0x80>
+  .long  0x0c404500                          // ld3           {v0.4h-v2.4h}, [x8]
+  .long  0x0f185403                          // shl           v3.4h, v0.4h, #8
+  .long  0x2f180410                          // ushr          v16.4h, v0.4h, #8
+  .long  0x0f185431                          // shl           v17.4h, v1.4h, #8
+  .long  0x2f180432                          // ushr          v18.4h, v1.4h, #8
+  .long  0x0f185453                          // shl           v19.4h, v2.4h, #8
+  .long  0x2f180440                          // ushr          v0.4h, v2.4h, #8
+  .long  0x52a6f008                          // mov           w8, #0x37800000
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x0eb01c61                          // orr           v1.8b, v3.8b, v16.8b
+  .long  0x0eb21e23                          // orr           v3.8b, v17.8b, v18.8b
+  .long  0x0ea01e60                          // orr           v0.8b, v19.8b, v0.8b
+  .long  0x72801008                          // movk          w8, #0x80
+  .long  0x2f10a421                          // uxtl          v1.4s, v1.4h
+  .long  0x2f10a463                          // uxtl          v3.4s, v3.4h
+  .long  0x2f10a400                          // uxtl          v0.4s, v0.4h
+  .long  0x4e040d02                          // dup           v2.4s, w8
+  .long  0x91004028                          // add           x8, x1, #0x10
+  .long  0x6e21d821                          // ucvtf         v1.4s, v1.4s
+  .long  0x6e21d863                          // ucvtf         v3.4s, v3.4s
+  .long  0x6e21d810                          // ucvtf         v16.4s, v0.4s
+  .long  0x6e22dc20                          // fmul          v0.4s, v1.4s, v2.4s
+  .long  0x6e22dc61                          // fmul          v1.4s, v3.4s, v2.4s
+  .long  0x6e22de02                          // fmul          v2.4s, v16.4s, v2.4s
+  .long  0x4f03f603                          // fmov          v3.4s, #1.000000000000000000e+00
+  .long  0xaa0803e1                          // mov           x1, x8
+  .long  0xd61f00a0                          // br            x5
+  .long  0x0d406100                          // ld3           {v0.h-v2.h}[0], [x8]
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x54fffc80                          // b.eq          3134 <sk_load_rgb_u16_be_aarch64+0x18>  // b.none
+  .long  0x91001909                          // add           x9, x8, #0x6
+  .long  0x0d406920                          // ld3           {v0.h-v2.h}[1], [x9]
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x54fffc03                          // b.cc          3134 <sk_load_rgb_u16_be_aarch64+0x18>  // b.lo, b.ul, b.last
+  .long  0x91003108                          // add           x8, x8, #0xc
+  .long  0x0d407100                          // ld3           {v0.h-v2.h}[2], [x8]
+  .long  0x17ffffdd                          // b             3134 <sk_load_rgb_u16_be_aarch64+0x18>
+
+HIDDEN _sk_store_u16_be_aarch64
+.globl _sk_store_u16_be_aarch64
+FUNCTION(_sk_store_u16_be_aarch64)
+_sk_store_u16_be_aarch64:
+  .long  0x52a8efe9                          // mov           w9, #0x477f0000
+  .long  0x729fe009                          // movk          w9, #0xff00
+  .long  0x4e040d34                          // dup           v20.4s, w9
+  .long  0x6e34dc10                          // fmul          v16.4s, v0.4s, v20.4s
+  .long  0x6e34dc31                          // fmul          v17.4s, v1.4s, v20.4s
+  .long  0x6e21aa10                          // fcvtnu        v16.4s, v16.4s
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x6e21aa31                          // fcvtnu        v17.4s, v17.4s
+  .long  0x0e612a10                          // xtn           v16.4h, v16.4s
+  .long  0x0e612a31                          // xtn           v17.4h, v17.4s
+  .long  0x0f185612                          // shl           v18.4h, v16.4h, #8
+  .long  0x2f180610                          // ushr          v16.4h, v16.4h, #8
+  .long  0x0f185635                          // shl           v21.4h, v17.4h, #8
+  .long  0x2f180636                          // ushr          v22.4h, v17.4h, #8
+  .long  0x0eb01e50                          // orr           v16.8b, v18.8b, v16.8b
+  .long  0x0eb61eb1                          // orr           v17.8b, v21.8b, v22.8b
+  .long  0x6e34dc55                          // fmul          v21.4s, v2.4s, v20.4s
+  .long  0x6e34dc74                          // fmul          v20.4s, v3.4s, v20.4s
+  .long  0x6e21aab5                          // fcvtnu        v21.4s, v21.4s
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x6e21aa94                          // fcvtnu        v20.4s, v20.4s
+  .long  0x0e612ab5                          // xtn           v21.4h, v21.4s
+  .long  0x0e612a94                          // xtn           v20.4h, v20.4s
+  .long  0x0f1856b6                          // shl           v22.4h, v21.4h, #8
+  .long  0x2f1806b5                          // ushr          v21.4h, v21.4h, #8
+  .long  0x0eb51ed2                          // orr           v18.8b, v22.8b, v21.8b
+  .long  0x0f185695                          // shl           v21.4h, v20.4h, #8
+  .long  0x2f180694                          // ushr          v20.4h, v20.4h, #8
+  .long  0x8b020d08                          // add           x8, x8, x2, lsl #3
+  .long  0x0eb41eb3                          // orr           v19.8b, v21.8b, v20.8b
+  .long  0xb50000a4                          // cbnz          x4, 3250 <sk_store_u16_be_aarch64+0x8c>
+  .long  0x0c000510                          // st4           {v16.4h-v19.4h}, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x0d206110                          // st4           {v16.h-v19.h}[0], [x8]
+  .long  0x54ffff60                          // b.eq          3244 <sk_store_u16_be_aarch64+0x80>  // b.none
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x0d206930                          // st4           {v16.h-v19.h}[1], [x9]
+  .long  0x54fffee3                          // b.cc          3244 <sk_store_u16_be_aarch64+0x80>  // b.lo, b.ul, b.last
+  .long  0x91004108                          // add           x8, x8, #0x10
+  .long  0x0d207110                          // st4           {v16.h-v19.h}[2], [x8]
+  .long  0x17fffff4                          // b             3244 <sk_store_u16_be_aarch64+0x80>
+
+HIDDEN _sk_load_f32_aarch64
+.globl _sk_load_f32_aarch64
+FUNCTION(_sk_load_f32_aarch64)
+_sk_load_f32_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b021108                          // add           x8, x8, x2, lsl #4
+  .long  0xb50000a4                          // cbnz          x4, 3298 <sk_load_f32_aarch64+0x20>
+  .long  0x4c400900                          // ld4           {v0.4s-v3.4s}, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0x0d60a100                          // ld4           {v0.s-v3.s}[0], [x8]
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x54ffff60                          // b.eq          328c <sk_load_f32_aarch64+0x14>  // b.none
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0x0d60b120                          // ld4           {v0.s-v3.s}[1], [x9]
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x54fffee3                          // b.cc          328c <sk_load_f32_aarch64+0x14>  // b.lo, b.ul, b.last
+  .long  0x91008108                          // add           x8, x8, #0x20
+  .long  0x4d60a100                          // ld4           {v0.s-v3.s}[2], [x8]
+  .long  0x17fffff4                          // b             328c <sk_load_f32_aarch64+0x14>
+
+HIDDEN _sk_store_f32_aarch64
+.globl _sk_store_f32_aarch64
+FUNCTION(_sk_store_f32_aarch64)
+_sk_store_f32_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xf9400108                          // ldr           x8, [x8]
+  .long  0x8b021108                          // add           x8, x8, x2, lsl #4
+  .long  0xb50000a4                          // cbnz          x4, 32e0 <sk_store_f32_aarch64+0x20>
+  .long  0x4c000900                          // st4           {v0.4s-v3.4s}, [x8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+  .long  0xf100049f                          // cmp           x4, #0x1
+  .long  0x0d20a100                          // st4           {v0.s-v3.s}[0], [x8]
+  .long  0x54ffff60                          // b.eq          32d4 <sk_store_f32_aarch64+0x14>  // b.none
+  .long  0x91004109                          // add           x9, x8, #0x10
+  .long  0xf1000c9f                          // cmp           x4, #0x3
+  .long  0x0d20b120                          // st4           {v0.s-v3.s}[1], [x9]
+  .long  0x54fffee3                          // b.cc          32d4 <sk_store_f32_aarch64+0x14>  // b.lo, b.ul, b.last
+  .long  0x91008108                          // add           x8, x8, #0x20
+  .long  0x4d20a100                          // st4           {v0.s-v3.s}[2], [x8]
+  .long  0x17fffff4                          // b             32d4 <sk_store_f32_aarch64+0x14>
+
+HIDDEN _sk_clamp_x_aarch64
+.globl _sk_clamp_x_aarch64
+FUNCTION(_sk_clamp_x_aarch64)
+_sk_clamp_x_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x6f00e411                          // movi          v17.2d, #0x0
+  .long  0x4e20f620                          // fmax          v0.4s, v17.4s, v0.4s
+  .long  0x4d40c910                          // ld1r          {v16.4s}, [x8]
+  .long  0x4eb0f400                          // fmin          v0.4s, v0.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_clamp_y_aarch64
+.globl _sk_clamp_y_aarch64
+FUNCTION(_sk_clamp_y_aarch64)
+_sk_clamp_y_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x6f00e411                          // movi          v17.2d, #0x0
+  .long  0x4e21f621                          // fmax          v1.4s, v17.4s, v1.4s
+  .long  0x4d40c910                          // ld1r          {v16.4s}, [x8]
+  .long  0x4eb0f421                          // fmin          v1.4s, v1.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_repeat_x_aarch64
+.globl _sk_repeat_x_aarch64
+FUNCTION(_sk_repeat_x_aarch64)
+_sk_repeat_x_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xbd400110                          // ldr           s16, [x8]
+  .long  0x4e040611                          // dup           v17.4s, v16.s[0]
+  .long  0x6e31fc11                          // fdiv          v17.4s, v0.4s, v17.4s
+  .long  0x4e219a31                          // frintm        v17.4s, v17.4s
+  .long  0x4f905220                          // fmls          v0.4s, v17.4s, v16.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_repeat_y_aarch64
+.globl _sk_repeat_y_aarch64
+FUNCTION(_sk_repeat_y_aarch64)
+_sk_repeat_y_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xbd400110                          // ldr           s16, [x8]
+  .long  0x4e040611                          // dup           v17.4s, v16.s[0]
+  .long  0x6e31fc31                          // fdiv          v17.4s, v1.4s, v17.4s
+  .long  0x4e219a31                          // frintm        v17.4s, v17.4s
+  .long  0x4f905221                          // fmls          v1.4s, v17.4s, v16.s[0]
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_mirror_x_aarch64
+.globl _sk_mirror_x_aarch64
+FUNCTION(_sk_mirror_x_aarch64)
+_sk_mirror_x_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xbd400110                          // ldr           s16, [x8]
+  .long  0x4e040611                          // dup           v17.4s, v16.s[0]
+  .long  0x1e302a10                          // fadd          s16, s16, s16
+  .long  0x4eb1d400                          // fsub          v0.4s, v0.4s, v17.4s
+  .long  0x4e040612                          // dup           v18.4s, v16.s[0]
+  .long  0x6e32fc12                          // fdiv          v18.4s, v0.4s, v18.4s
+  .long  0x4e219a52                          // frintm        v18.4s, v18.4s
+  .long  0x4f905240                          // fmls          v0.4s, v18.4s, v16.s[0]
+  .long  0x4eb1d400                          // fsub          v0.4s, v0.4s, v17.4s
+  .long  0x4ea0f800                          // fabs          v0.4s, v0.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_mirror_y_aarch64
+.globl _sk_mirror_y_aarch64
+FUNCTION(_sk_mirror_y_aarch64)
+_sk_mirror_y_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xbd400110                          // ldr           s16, [x8]
+  .long  0x4e040611                          // dup           v17.4s, v16.s[0]
+  .long  0x1e302a10                          // fadd          s16, s16, s16
+  .long  0x4eb1d421                          // fsub          v1.4s, v1.4s, v17.4s
+  .long  0x4e040612                          // dup           v18.4s, v16.s[0]
+  .long  0x6e32fc32                          // fdiv          v18.4s, v1.4s, v18.4s
+  .long  0x4e219a52                          // frintm        v18.4s, v18.4s
+  .long  0x4f905241                          // fmls          v1.4s, v18.4s, v16.s[0]
+  .long  0x4eb1d421                          // fsub          v1.4s, v1.4s, v17.4s
+  .long  0x4ea0f821                          // fabs          v1.4s, v1.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_clamp_x_1_aarch64
+.globl _sk_clamp_x_1_aarch64
+FUNCTION(_sk_clamp_x_1_aarch64)
+_sk_clamp_x_1_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6f00e410                          // movi          v16.2d, #0x0
+  .long  0x4e20f600                          // fmax          v0.4s, v16.4s, v0.4s
+  .long  0x4f03f610                          // fmov          v16.4s, #1.000000000000000000e+00
+  .long  0x4eb0f400                          // fmin          v0.4s, v0.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_repeat_x_1_aarch64
+.globl _sk_repeat_x_1_aarch64
+FUNCTION(_sk_repeat_x_1_aarch64)
+_sk_repeat_x_1_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e219810                          // frintm        v16.4s, v0.4s
+  .long  0x4eb0d400                          // fsub          v0.4s, v0.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_mirror_x_1_aarch64
+.globl _sk_mirror_x_1_aarch64
+FUNCTION(_sk_mirror_x_1_aarch64)
+_sk_mirror_x_1_aarch64:
+  .long  0x4f07f610                          // fmov          v16.4s, #-1.000000000000000000e+00
+  .long  0x4f0167f1                          // movi          v17.4s, #0x3f, lsl #24
+  .long  0x4e30d400                          // fadd          v0.4s, v0.4s, v16.4s
+  .long  0x6e31dc11                          // fmul          v17.4s, v0.4s, v17.4s
+  .long  0x4e219a31                          // frintm        v17.4s, v17.4s
+  .long  0x4e31d631                          // fadd          v17.4s, v17.4s, v17.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4eb1d400                          // fsub          v0.4s, v0.4s, v17.4s
+  .long  0x4e30d400                          // fadd          v0.4s, v0.4s, v16.4s
+  .long  0x4ea0f800                          // fabs          v0.4s, v0.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_luminance_to_alpha_aarch64
+.globl _sk_luminance_to_alpha_aarch64
+FUNCTION(_sk_luminance_to_alpha_aarch64)
+_sk_luminance_to_alpha_aarch64:
+  .long  0x52a7cb28                          // mov           w8, #0x3e590000
+  .long  0x72967a08                          // movk          w8, #0xb3d0
+  .long  0x4e040d11                          // dup           v17.4s, w8
+  .long  0x52a7e6e8                          // mov           w8, #0x3f370000
+  .long  0x7282eb28                          // movk          w8, #0x1759
+  .long  0x4ea01c10                          // mov           v16.16b, v0.16b
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x52a7b268                          // mov           w8, #0x3d930000
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x729bb308                          // movk          w8, #0xdd98
+  .long  0x6e20dc23                          // fmul          v3.4s, v1.4s, v0.4s
+  .long  0x4e30ce23                          // fmla          v3.4s, v17.4s, v16.4s
+  .long  0x4e040d10                          // dup           v16.4s, w8
+  .long  0x6f00e400                          // movi          v0.2d, #0x0
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x4e22ce03                          // fmla          v3.4s, v16.4s, v2.4s
+  .long  0x6f00e402                          // movi          v2.2d, #0x0
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_matrix_2x3_aarch64
+.globl _sk_matrix_2x3_aarch64
+FUNCTION(_sk_matrix_2x3_aarch64)
+_sk_matrix_2x3_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0x9100410a                          // add           x10, x8, #0x10
+  .long  0x4ddfc932                          // ld1r          {v18.4s}, [x9], #4
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x2d415113                          // ldp           s19, s20, [x8, #8]
+  .long  0x9100510a                          // add           x10, x8, #0x14
+  .long  0x4d40c951                          // ld1r          {v17.4s}, [x10]
+  .long  0x4f931030                          // fmla          v16.4s, v1.4s, v19.s[0]
+  .long  0xbd400133                          // ldr           s19, [x9]
+  .long  0x4f941031                          // fmla          v17.4s, v1.4s, v20.s[0]
+  .long  0x4e20ce50                          // fmla          v16.4s, v18.4s, v0.4s
+  .long  0x4f931011                          // fmla          v17.4s, v0.4s, v19.s[0]
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_matrix_3x4_aarch64
+.globl _sk_matrix_3x4_aarch64
+FUNCTION(_sk_matrix_3x4_aarch64)
+_sk_matrix_3x4_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0x9100910a                          // add           x10, x8, #0x24
+  .long  0x4ddfc933                          // ld1r          {v19.4s}, [x9], #4
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x9100a10a                          // add           x10, x8, #0x28
+  .long  0x4d40c951                          // ld1r          {v17.4s}, [x10]
+  .long  0x9100b10a                          // add           x10, x8, #0x2c
+  .long  0x2d435514                          // ldp           s20, s21, [x8, #24]
+  .long  0xbd402116                          // ldr           s22, [x8, #32]
+  .long  0x4d40c952                          // ld1r          {v18.4s}, [x10]
+  .long  0x4f941050                          // fmla          v16.4s, v2.4s, v20.s[0]
+  .long  0x4f951051                          // fmla          v17.4s, v2.4s, v21.s[0]
+  .long  0x4f961052                          // fmla          v18.4s, v2.4s, v22.s[0]
+  .long  0x2d425502                          // ldp           s2, s21, [x8, #16]
+  .long  0x2d415d14                          // ldp           s20, s23, [x8, #8]
+  .long  0x4f821031                          // fmla          v17.4s, v1.4s, v2.s[0]
+  .long  0xbd400122                          // ldr           s2, [x9]
+  .long  0x4f971030                          // fmla          v16.4s, v1.4s, v23.s[0]
+  .long  0x4f951032                          // fmla          v18.4s, v1.4s, v21.s[0]
+  .long  0x4e20ce70                          // fmla          v16.4s, v19.4s, v0.4s
+  .long  0x4f941012                          // fmla          v18.4s, v0.4s, v20.s[0]
+  .long  0x4f821011                          // fmla          v17.4s, v0.4s, v2.s[0]
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0x4eb21e42                          // mov           v2.16b, v18.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_matrix_4x5_aarch64
+.globl _sk_matrix_4x5_aarch64
+FUNCTION(_sk_matrix_4x5_aarch64)
+_sk_matrix_4x5_aarch64:
+  .long  0xf9400029                          // ldr           x9, [x1]
+  .long  0xaa0903e8                          // mov           x8, x9
+  .long  0x9101012a                          // add           x10, x9, #0x40
+  .long  0x4ddfc914                          // ld1r          {v20.4s}, [x8], #4
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x9101112a                          // add           x10, x9, #0x44
+  .long  0x4d40c951                          // ld1r          {v17.4s}, [x10]
+  .long  0x9101212a                          // add           x10, x9, #0x48
+  .long  0x4d40c952                          // ld1r          {v18.4s}, [x10]
+  .long  0x2d465533                          // ldp           s19, s21, [x9, #48]
+  .long  0x2d475d36                          // ldp           s22, s23, [x9, #56]
+  .long  0x9101312a                          // add           x10, x9, #0x4c
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f931070                          // fmla          v16.4s, v3.4s, v19.s[0]
+  .long  0x4d40c953                          // ld1r          {v19.4s}, [x10]
+  .long  0x4f951071                          // fmla          v17.4s, v3.4s, v21.s[0]
+  .long  0x4f961072                          // fmla          v18.4s, v3.4s, v22.s[0]
+  .long  0x2d445935                          // ldp           s21, s22, [x9, #32]
+  .long  0x4f971073                          // fmla          v19.4s, v3.4s, v23.s[0]
+  .long  0x2d455d23                          // ldp           s3, s23, [x9, #40]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0x4f951050                          // fmla          v16.4s, v2.4s, v21.s[0]
+  .long  0x4f961051                          // fmla          v17.4s, v2.4s, v22.s[0]
+  .long  0x2d425935                          // ldp           s21, s22, [x9, #16]
+  .long  0x4f971053                          // fmla          v19.4s, v2.4s, v23.s[0]
+  .long  0x4f831052                          // fmla          v18.4s, v2.4s, v3.s[0]
+  .long  0x2d410d22                          // ldp           s2, s3, [x9, #8]
+  .long  0x4f951030                          // fmla          v16.4s, v1.4s, v21.s[0]
+  .long  0x2d435d35                          // ldp           s21, s23, [x9, #24]
+  .long  0x4f961031                          // fmla          v17.4s, v1.4s, v22.s[0]
+  .long  0xbd400116                          // ldr           s22, [x8]
+  .long  0x4e20ce90                          // fmla          v16.4s, v20.4s, v0.4s
+  .long  0x4f951032                          // fmla          v18.4s, v1.4s, v21.s[0]
+  .long  0x4f971033                          // fmla          v19.4s, v1.4s, v23.s[0]
+  .long  0x4f821012                          // fmla          v18.4s, v0.4s, v2.s[0]
+  .long  0x4f831013                          // fmla          v19.4s, v0.4s, v3.s[0]
+  .long  0x4f961011                          // fmla          v17.4s, v0.4s, v22.s[0]
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0x4eb21e42                          // mov           v2.16b, v18.16b
+  .long  0x4eb31e63                          // mov           v3.16b, v19.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_matrix_4x3_aarch64
+.globl _sk_matrix_4x3_aarch64
+FUNCTION(_sk_matrix_4x3_aarch64)
+_sk_matrix_4x3_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0x9100810a                          // add           x10, x8, #0x20
+  .long  0x4ddfc932                          // ld1r          {v18.4s}, [x9], #4
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x9100910a                          // add           x10, x8, #0x24
+  .long  0x4d40c951                          // ld1r          {v17.4s}, [x10]
+  .long  0x9100a10a                          // add           x10, x8, #0x28
+  .long  0x2d425113                          // ldp           s19, s20, [x8, #16]
+  .long  0x4d40c942                          // ld1r          {v2.4s}, [x10]
+  .long  0x9100b10a                          // add           x10, x8, #0x2c
+  .long  0x2d435915                          // ldp           s21, s22, [x8, #24]
+  .long  0x4d40c943                          // ld1r          {v3.4s}, [x10]
+  .long  0x4f931030                          // fmla          v16.4s, v1.4s, v19.s[0]
+  .long  0x4e20ce50                          // fmla          v16.4s, v18.4s, v0.4s
+  .long  0xbd400132                          // ldr           s18, [x9]
+  .long  0x4f941031                          // fmla          v17.4s, v1.4s, v20.s[0]
+  .long  0x4f951022                          // fmla          v2.4s, v1.4s, v21.s[0]
+  .long  0x4f961023                          // fmla          v3.4s, v1.4s, v22.s[0]
+  .long  0x2d414d01                          // ldp           s1, s19, [x8, #8]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f921011                          // fmla          v17.4s, v0.4s, v18.s[0]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0x4f811002                          // fmla          v2.4s, v0.4s, v1.s[0]
+  .long  0x4f931003                          // fmla          v3.4s, v0.4s, v19.s[0]
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x4eb11e21                          // mov           v1.16b, v17.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_matrix_perspective_aarch64
+.globl _sk_matrix_perspective_aarch64
+FUNCTION(_sk_matrix_perspective_aarch64)
+_sk_matrix_perspective_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0x9100510a                          // add           x10, x8, #0x14
+  .long  0x4ddfc930                          // ld1r          {v16.4s}, [x9], #4
+  .long  0x4d40c951                          // ld1r          {v17.4s}, [x10]
+  .long  0x9100810a                          // add           x10, x8, #0x20
+  .long  0x4d40c952                          // ld1r          {v18.4s}, [x10]
+  .long  0x2d41d113                          // ldp           s19, s20, [x8, #12]
+  .long  0x2d435915                          // ldp           s21, s22, [x8, #24]
+  .long  0x91002108                          // add           x8, x8, #0x8
+  .long  0x4f941031                          // fmla          v17.4s, v1.4s, v20.s[0]
+  .long  0x4d40c914                          // ld1r          {v20.4s}, [x8]
+  .long  0x4f961032                          // fmla          v18.4s, v1.4s, v22.s[0]
+  .long  0xbd400136                          // ldr           s22, [x9]
+  .long  0x4f951012                          // fmla          v18.4s, v0.4s, v21.s[0]
+  .long  0x4f931011                          // fmla          v17.4s, v0.4s, v19.s[0]
+  .long  0x4f961034                          // fmla          v20.4s, v1.4s, v22.s[0]
+  .long  0x4ea1da41                          // frecpe        v1.4s, v18.4s
+  .long  0x4e21fe52                          // frecps        v18.4s, v18.4s, v1.4s
+  .long  0x6e32dc32                          // fmul          v18.4s, v1.4s, v18.4s
+  .long  0x4e20ce14                          // fmla          v20.4s, v16.4s, v0.4s
+  .long  0x6e32de21                          // fmul          v1.4s, v17.4s, v18.4s
+  .long  0x6e32de80                          // fmul          v0.4s, v20.4s, v18.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_evenly_spaced_gradient_aarch64
+.globl _sk_evenly_spaced_gradient_aarch64
+FUNCTION(_sk_evenly_spaced_gradient_aarch64)
+_sk_evenly_spaced_gradient_aarch64:
+  .long  0xd10043ff                          // sub           sp, sp, #0x10
+  .long  0xaa0103e8                          // mov           x8, x1
+  .long  0x91002109                          // add           x9, x8, #0x8
+  .long  0xf90007e9                          // str           x9, [sp, #8]
+  .long  0xf841042a                          // ldr           x10, [x1], #16
+  .long  0xa940254b                          // ldp           x11, x9, [x10]
+  .long  0xa942354c                          // ldp           x12, x13, [x10, #32]
+  .long  0xa9413d4e                          // ldp           x14, x15, [x10, #16]
+  .long  0xa9434550                          // ldp           x16, x17, [x10, #48]
+  .long  0xd100056b                          // sub           x11, x11, #0x1
+  .long  0x9e230161                          // ucvtf         s1, x11
+  .long  0xf940214a                          // ldr           x10, [x10, #64]
+  .long  0x4f819001                          // fmul          v1.4s, v0.4s, v1.s[0]
+  .long  0x4ea1b821                          // fcvtzs        v1.4s, v1.4s
+  .long  0x6f20a422                          // uxtl2         v2.2d, v1.4s
+  .long  0x2f20a421                          // uxtl          v1.2d, v1.2s
+  .long  0x9e660032                          // fmov          x18, d1
+  .long  0x9e660046                          // fmov          x6, d2
+  .long  0x4e183c2b                          // mov           x11, v1.d[1]
+  .long  0x4e183c45                          // mov           x5, v2.d[1]
+  .long  0xbc667921                          // ldr           s1, [x9, x6, lsl #2]
+  .long  0xbc6679a2                          // ldr           s2, [x13, x6, lsl #2]
+  .long  0xbc6679c3                          // ldr           s3, [x14, x6, lsl #2]
+  .long  0xbc667a11                          // ldr           s17, [x16, x6, lsl #2]
+  .long  0xbc6679f2                          // ldr           s18, [x15, x6, lsl #2]
+  .long  0xbc667a33                          // ldr           s19, [x17, x6, lsl #2]
+  .long  0xbc667994                          // ldr           s20, [x12, x6, lsl #2]
+  .long  0xbc667955                          // ldr           s21, [x10, x6, lsl #2]
+  .long  0x8b120926                          // add           x6, x9, x18, lsl #2
+  .long  0x0d4080d6                          // ld1           {v22.s}[0], [x6]
+  .long  0x8b1209a6                          // add           x6, x13, x18, lsl #2
+  .long  0x0d4080d0                          // ld1           {v16.s}[0], [x6]
+  .long  0x8b0b0926                          // add           x6, x9, x11, lsl #2
+  .long  0x0d4090d6                          // ld1           {v22.s}[1], [x6]
+  .long  0x8b1209c6                          // add           x6, x14, x18, lsl #2
+  .long  0x0d4080d7                          // ld1           {v23.s}[0], [x6]
+  .long  0x8b120a06                          // add           x6, x16, x18, lsl #2
+  .long  0x6e140436                          // mov           v22.s[2], v1.s[0]
+  .long  0x0d4080c1                          // ld1           {v1.s}[0], [x6]
+  .long  0x8b0b09a6                          // add           x6, x13, x11, lsl #2
+  .long  0x0d4090d0                          // ld1           {v16.s}[1], [x6]
+  .long  0x8b0b09c6                          // add           x6, x14, x11, lsl #2
+  .long  0x0d4090d7                          // ld1           {v23.s}[1], [x6]
+  .long  0x8b1209e6                          // add           x6, x15, x18, lsl #2
+  .long  0x0d4080d8                          // ld1           {v24.s}[0], [x6]
+  .long  0x8b120a26                          // add           x6, x17, x18, lsl #2
+  .long  0x6e140450                          // mov           v16.s[2], v2.s[0]
+  .long  0x0d4080c2                          // ld1           {v2.s}[0], [x6]
+  .long  0x8b0b0a06                          // add           x6, x16, x11, lsl #2
+  .long  0x0d4090c1                          // ld1           {v1.s}[1], [x6]
+  .long  0x8b0b09e6                          // add           x6, x15, x11, lsl #2
+  .long  0x0d4090d8                          // ld1           {v24.s}[1], [x6]
+  .long  0x8b120986                          // add           x6, x12, x18, lsl #2
+  .long  0x8b120952                          // add           x18, x10, x18, lsl #2
+  .long  0x6e140477                          // mov           v23.s[2], v3.s[0]
+  .long  0x0d408243                          // ld1           {v3.s}[0], [x18]
+  .long  0x8b0b0a32                          // add           x18, x17, x11, lsl #2
+  .long  0x6e140621                          // mov           v1.s[2], v17.s[0]
+  .long  0x0d4080d1                          // ld1           {v17.s}[0], [x6]
+  .long  0x0d409242                          // ld1           {v2.s}[1], [x18]
+  .long  0x8b0b0992                          // add           x18, x12, x11, lsl #2
+  .long  0x6e140658                          // mov           v24.s[2], v18.s[0]
+  .long  0x0d409251                          // ld1           {v17.s}[1], [x18]
+  .long  0x6e140662                          // mov           v2.s[2], v19.s[0]
+  .long  0xbc657932                          // ldr           s18, [x9, x5, lsl #2]
+  .long  0xbc6579b3                          // ldr           s19, [x13, x5, lsl #2]
+  .long  0x6e140691                          // mov           v17.s[2], v20.s[0]
+  .long  0xbc6579d4                          // ldr           s20, [x14, x5, lsl #2]
+  .long  0x6e1c0656                          // mov           v22.s[3], v18.s[0]
+  .long  0xbc657a12                          // ldr           s18, [x16, x5, lsl #2]
+  .long  0x6e1c0670                          // mov           v16.s[3], v19.s[0]
+  .long  0xbc6579f3                          // ldr           s19, [x15, x5, lsl #2]
+  .long  0x8b0b094b                          // add           x11, x10, x11, lsl #2
+  .long  0x0d409163                          // ld1           {v3.s}[1], [x11]
+  .long  0x6e1c0697                          // mov           v23.s[3], v20.s[0]
+  .long  0xbc657a34                          // ldr           s20, [x17, x5, lsl #2]
+  .long  0x6e1c0641                          // mov           v1.s[3], v18.s[0]
+  .long  0xbc657992                          // ldr           s18, [x12, x5, lsl #2]
+  .long  0x6e1c0678                          // mov           v24.s[3], v19.s[0]
+  .long  0xbc657953                          // ldr           s19, [x10, x5, lsl #2]
+  .long  0xf9400505                          // ldr           x5, [x8, #8]
+  .long  0x6e1406a3                          // mov           v3.s[2], v21.s[0]
+  .long  0x6e1c0682                          // mov           v2.s[3], v20.s[0]
+  .long  0x6e1c0651                          // mov           v17.s[3], v18.s[0]
+  .long  0x6e1c0663                          // mov           v3.s[3], v19.s[0]
+  .long  0x4e20ced0                          // fmla          v16.4s, v22.4s, v0.4s
+  .long  0x4e20cee1                          // fmla          v1.4s, v23.4s, v0.4s
+  .long  0x4e20cf02                          // fmla          v2.4s, v24.4s, v0.4s
+  .long  0x4e20ce23                          // fmla          v3.4s, v17.4s, v0.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x910043ff                          // add           sp, sp, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_gauss_a_to_rgba_aarch64
+.globl _sk_gauss_a_to_rgba_aarch64
+FUNCTION(_sk_gauss_a_to_rgba_aarch64)
+_sk_gauss_a_to_rgba_aarch64:
+  .long  0x52b80228                          // mov           w8, #0xc0110000
+  .long  0x728205a8                          // movk          w8, #0x102d
+  .long  0x52a80729                          // mov           w9, #0x40390000
+  .long  0x728f0249                          // movk          w9, #0x7812
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x52a7cb48                          // mov           w8, #0x3e5a0000
+  .long  0x72928408                          // movk          w8, #0x9420
+  .long  0x4e040d21                          // dup           v1.4s, w9
+  .long  0x52a7c3c9                          // mov           w9, #0x3e1e0000
+  .long  0x7293a089                          // movk          w9, #0x9d04
+  .long  0x4e040d02                          // dup           v2.4s, w8
+  .long  0x52a73428                          // mov           w8, #0x39a10000
+  .long  0x4e23cc01                          // fmla          v1.4s, v0.4s, v3.4s
+  .long  0x72830008                          // movk          w8, #0x1800
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x4e23cc22                          // fmla          v2.4s, v1.4s, v3.4s
+  .long  0x4e040d00                          // dup           v0.4s, w8
+  .long  0x4e23cc50                          // fmla          v16.4s, v2.4s, v3.4s
+  .long  0x4e23ce00                          // fmla          v0.4s, v16.4s, v3.4s
+  .long  0x4ea01c01                          // mov           v1.16b, v0.16b
+  .long  0x4ea01c02                          // mov           v2.16b, v0.16b
+  .long  0x4ea01c03                          // mov           v3.16b, v0.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_gradient_aarch64
+.globl _sk_gradient_aarch64
+FUNCTION(_sk_gradient_aarch64)
+_sk_gradient_aarch64:
+  .long  0xd10043ff                          // sub           sp, sp, #0x10
+  .long  0x91002028                          // add           x8, x1, #0x8
+  .long  0xf90007e8                          // str           x8, [sp, #8]
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x6f00e411                          // movi          v17.2d, #0x0
+  .long  0xf9400109                          // ldr           x9, [x8]
+  .long  0xf100093f                          // cmp           x9, #0x2
+  .long  0x540001c3                          // b.cc          38b8 <sk_gradient_aarch64+0x58>  // b.lo, b.ul, b.last
+  .long  0xf940250a                          // ldr           x10, [x8, #72]
+  .long  0xd1000529                          // sub           x9, x9, #0x1
+  .long  0x6f00e401                          // movi          v1.2d, #0x0
+  .long  0x4f000422                          // movi          v2.4s, #0x1
+  .long  0x9100114a                          // add           x10, x10, #0x4
+  .long  0x4ddfc943                          // ld1r          {v3.4s}, [x10], #4
+  .long  0xd1000529                          // sub           x9, x9, #0x1
+  .long  0x6e23e403                          // fcmge         v3.4s, v0.4s, v3.4s
+  .long  0x4e221c63                          // and           v3.16b, v3.16b, v2.16b
+  .long  0x4ea18461                          // add           v1.4s, v3.4s, v1.4s
+  .long  0xb5ffff69                          // cbnz          x9, 3898 <sk_gradient_aarch64+0x38>
+  .long  0x6f20a431                          // uxtl2         v17.2d, v1.4s
+  .long  0x2f20a421                          // uxtl          v1.2d, v1.2s
+  .long  0xa940b10a                          // ldp           x10, x12, [x8, #8]
+  .long  0xa942b90d                          // ldp           x13, x14, [x8, #40]
+  .long  0x9e66002b                          // fmov          x11, d1
+  .long  0xa941c10f                          // ldp           x15, x16, [x8, #24]
+  .long  0x8b0b0952                          // add           x18, x10, x11, lsl #2
+  .long  0xa943a111                          // ldp           x17, x8, [x8, #56]
+  .long  0x0d408252                          // ld1           {v18.s}[0], [x18]
+  .long  0x8b0b09b2                          // add           x18, x13, x11, lsl #2
+  .long  0x0d408250                          // ld1           {v16.s}[0], [x18]
+  .long  0x8b0b0992                          // add           x18, x12, x11, lsl #2
+  .long  0x0d408253                          // ld1           {v19.s}[0], [x18]
+  .long  0x8b0b09d2                          // add           x18, x14, x11, lsl #2
+  .long  0x4e183c29                          // mov           x9, v1.d[1]
+  .long  0x0d408241                          // ld1           {v1.s}[0], [x18]
+  .long  0x8b0b09f2                          // add           x18, x15, x11, lsl #2
+  .long  0x0d408254                          // ld1           {v20.s}[0], [x18]
+  .long  0x8b0b0a32                          // add           x18, x17, x11, lsl #2
+  .long  0x0d408242                          // ld1           {v2.s}[0], [x18]
+  .long  0x8b0b0a12                          // add           x18, x16, x11, lsl #2
+  .long  0x8b0b090b                          // add           x11, x8, x11, lsl #2
+  .long  0x0d408163                          // ld1           {v3.s}[0], [x11]
+  .long  0x8b09094b                          // add           x11, x10, x9, lsl #2
+  .long  0x0d409172                          // ld1           {v18.s}[1], [x11]
+  .long  0x8b0909ab                          // add           x11, x13, x9, lsl #2
+  .long  0x0d409170                          // ld1           {v16.s}[1], [x11]
+  .long  0x8b09098b                          // add           x11, x12, x9, lsl #2
+  .long  0x0d409173                          // ld1           {v19.s}[1], [x11]
+  .long  0x8b0909cb                          // add           x11, x14, x9, lsl #2
+  .long  0x0d409161                          // ld1           {v1.s}[1], [x11]
+  .long  0x8b0909eb                          // add           x11, x15, x9, lsl #2
+  .long  0x0d408255                          // ld1           {v21.s}[0], [x18]
+  .long  0x9e660232                          // fmov          x18, d17
+  .long  0x0d409174                          // ld1           {v20.s}[1], [x11]
+  .long  0x4e183e2b                          // mov           x11, v17.d[1]
+  .long  0xbc6b7951                          // ldr           s17, [x10, x11, lsl #2]
+  .long  0x8b12094a                          // add           x10, x10, x18, lsl #2
+  .long  0x4d408152                          // ld1           {v18.s}[2], [x10]
+  .long  0x8b1209aa                          // add           x10, x13, x18, lsl #2
+  .long  0xbc6b79b6                          // ldr           s22, [x13, x11, lsl #2]
+  .long  0x4d408150                          // ld1           {v16.s}[2], [x10]
+  .long  0x8b12098a                          // add           x10, x12, x18, lsl #2
+  .long  0x4d408153                          // ld1           {v19.s}[2], [x10]
+  .long  0x8b1209ca                          // add           x10, x14, x18, lsl #2
+  .long  0x4d408141                          // ld1           {v1.s}[2], [x10]
+  .long  0x8b090a2a                          // add           x10, x17, x9, lsl #2
+  .long  0xbc6b7997                          // ldr           s23, [x12, x11, lsl #2]
+  .long  0x8b1209ec                          // add           x12, x15, x18, lsl #2
+  .long  0x0d409142                          // ld1           {v2.s}[1], [x10]
+  .long  0x8b090a0a                          // add           x10, x16, x9, lsl #2
+  .long  0x8b090909                          // add           x9, x8, x9, lsl #2
+  .long  0x6e1c0632                          // mov           v18.s[3], v17.s[0]
+  .long  0xbc6b79d1                          // ldr           s17, [x14, x11, lsl #2]
+  .long  0x6e1c06d0                          // mov           v16.s[3], v22.s[0]
+  .long  0xbc6b79f6                          // ldr           s22, [x15, x11, lsl #2]
+  .long  0x0d409155                          // ld1           {v21.s}[1], [x10]
+  .long  0x4d408194                          // ld1           {v20.s}[2], [x12]
+  .long  0x0d409123                          // ld1           {v3.s}[1], [x9]
+  .long  0xf94007e1                          // ldr           x1, [sp, #8]
+  .long  0x8b120a2d                          // add           x13, x17, x18, lsl #2
+  .long  0x8b120a0e                          // add           x14, x16, x18, lsl #2
+  .long  0x8b12090f                          // add           x15, x8, x18, lsl #2
+  .long  0x6e1c06f3                          // mov           v19.s[3], v23.s[0]
+  .long  0xbc6b7a37                          // ldr           s23, [x17, x11, lsl #2]
+  .long  0x6e1c0621                          // mov           v1.s[3], v17.s[0]
+  .long  0xbc6b7a11                          // ldr           s17, [x16, x11, lsl #2]
+  .long  0x4d4081a2                          // ld1           {v2.s}[2], [x13]
+  .long  0x4d4081d5                          // ld1           {v21.s}[2], [x14]
+  .long  0x6e1c06d4                          // mov           v20.s[3], v22.s[0]
+  .long  0xbc6b7916                          // ldr           s22, [x8, x11, lsl #2]
+  .long  0x4d4081e3                          // ld1           {v3.s}[2], [x15]
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e1c06e2                          // mov           v2.s[3], v23.s[0]
+  .long  0x6e1c0635                          // mov           v21.s[3], v17.s[0]
+  .long  0x6e1c06c3                          // mov           v3.s[3], v22.s[0]
+  .long  0x4e20ce50                          // fmla          v16.4s, v18.4s, v0.4s
+  .long  0x4e20ce61                          // fmla          v1.4s, v19.4s, v0.4s
+  .long  0x4e20ce82                          // fmla          v2.4s, v20.4s, v0.4s
+  .long  0x4e20cea3                          // fmla          v3.4s, v21.4s, v0.4s
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0x910043ff                          // add           sp, sp, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_evenly_spaced_2_stop_gradient_aarch64
+.globl _sk_evenly_spaced_2_stop_gradient_aarch64
+FUNCTION(_sk_evenly_spaced_2_stop_gradient_aarch64)
+_sk_evenly_spaced_2_stop_gradient_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0xaa0803e9                          // mov           x9, x8
+  .long  0x9100410a                          // add           x10, x8, #0x10
+  .long  0x4ddfc931                          // ld1r          {v17.4s}, [x9], #4
+  .long  0x4d40c950                          // ld1r          {v16.4s}, [x10]
+  .long  0x9100510a                          // add           x10, x8, #0x14
+  .long  0x4d40c941                          // ld1r          {v1.4s}, [x10]
+  .long  0x9100610a                          // add           x10, x8, #0x18
+  .long  0x4d40c942                          // ld1r          {v2.4s}, [x10]
+  .long  0x9100710a                          // add           x10, x8, #0x1c
+  .long  0x2d414d12                          // ldp           s18, s19, [x8, #8]
+  .long  0x4d40c943                          // ld1r          {v3.4s}, [x10]
+  .long  0x4e20ce30                          // fmla          v16.4s, v17.4s, v0.4s
+  .long  0xbd400131                          // ldr           s17, [x9]
+  .long  0x4f921002                          // fmla          v2.4s, v0.4s, v18.s[0]
+  .long  0x4f931003                          // fmla          v3.4s, v0.4s, v19.s[0]
+  .long  0x4f911001                          // fmla          v1.4s, v0.4s, v17.s[0]
+  .long  0x4eb01e00                          // mov           v0.16b, v16.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_xy_to_unit_angle_aarch64
+.globl _sk_xy_to_unit_angle_aarch64
+FUNCTION(_sk_xy_to_unit_angle_aarch64)
+_sk_xy_to_unit_angle_aarch64:
+  .long  0x52b77ce8                          // mov           w8, #0xbbe70000
+  .long  0x72856de8                          // movk          w8, #0x2b6f
+  .long  0x4ea0f810                          // fabs          v16.4s, v0.4s
+  .long  0x4ea0f831                          // fabs          v17.4s, v1.4s
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a79948                          // mov           w8, #0x3cca0000
+  .long  0x729af3e8                          // movk          w8, #0xd79f
+  .long  0x4eb1f614                          // fmin          v20.4s, v16.4s, v17.4s
+  .long  0x4e31f615                          // fmax          v21.4s, v16.4s, v17.4s
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x52b7aa88                          // mov           w8, #0xbd540000
+  .long  0x6e35fe94                          // fdiv          v20.4s, v20.4s, v21.4s
+  .long  0x728c9a88                          // movk          w8, #0x64d4
+  .long  0x6e34de95                          // fmul          v21.4s, v20.4s, v20.4s
+  .long  0x4e35ce53                          // fmla          v19.4s, v18.4s, v21.4s
+  .long  0x4e040d12                          // dup           v18.4s, w8
+  .long  0x52a7c448                          // mov           w8, #0x3e220000
+  .long  0x729e1528                          // movk          w8, #0xf0a9
+  .long  0x4e35ce72                          // fmla          v18.4s, v19.4s, v21.4s
+  .long  0x4e040d13                          // dup           v19.4s, w8
+  .long  0x4e35ce53                          // fmla          v19.4s, v18.4s, v21.4s
+  .long  0x4f02f612                          // fmov          v18.4s, #2.500000000000000000e-01
+  .long  0x6e33de93                          // fmul          v19.4s, v20.4s, v19.4s
+  .long  0x6eb0e630                          // fcmgt         v16.4s, v17.4s, v16.4s
+  .long  0x4eb3d652                          // fsub          v18.4s, v18.4s, v19.4s
+  .long  0x4f0167f5                          // movi          v21.4s, #0x3f, lsl #24
+  .long  0x6e731e50                          // bsl           v16.16b, v18.16b, v19.16b
+  .long  0x4ea0e800                          // fcmlt         v0.4s, v0.4s, #0.0
+  .long  0x4eb0d6b2                          // fsub          v18.4s, v21.4s, v16.4s
+  .long  0x4f03f614                          // fmov          v20.4s, #1.000000000000000000e+00
+  .long  0x6e701e40                          // bsl           v0.16b, v18.16b, v16.16b
+  .long  0x4ea0e831                          // fcmlt         v17.4s, v1.4s, #0.0
+  .long  0x4ea0d690                          // fsub          v16.4s, v20.4s, v0.4s
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e601e11                          // bsl           v17.16b, v16.16b, v0.16b
+  .long  0x6ea0ca20                          // fcmge         v0.4s, v17.4s, #0.0
+  .long  0x4ea0ea30                          // fcmlt         v16.4s, v17.4s, #0.0
+  .long  0x4ea01e00                          // orr           v0.16b, v16.16b, v0.16b
+  .long  0x4e201e20                          // and           v0.16b, v17.16b, v0.16b
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_xy_to_radius_aarch64
+.globl _sk_xy_to_radius_aarch64
+FUNCTION(_sk_xy_to_radius_aarch64)
+_sk_xy_to_radius_aarch64:
+  .long  0xf8408425                          // ldr           x5, [x1], #8
+  .long  0x6e21dc30                          // fmul          v16.4s, v1.4s, v1.4s
+  .long  0x4e20cc10                          // fmla          v16.4s, v0.4s, v0.4s
+  .long  0x6ea1fa00                          // fsqrt         v0.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_save_xy_aarch64
+.globl _sk_save_xy_aarch64
+FUNCTION(_sk_save_xy_aarch64)
+_sk_save_xy_aarch64:
+  .long  0x4f0167f0                          // movi          v16.4s, #0x3f, lsl #24
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4e30d411                          // fadd          v17.4s, v0.4s, v16.4s
+  .long  0x4e30d430                          // fadd          v16.4s, v1.4s, v16.4s
+  .long  0x4e219a32                          // frintm        v18.4s, v17.4s
+  .long  0x4eb2d631                          // fsub          v17.4s, v17.4s, v18.4s
+  .long  0x4e219a12                          // frintm        v18.4s, v16.4s
+  .long  0x4eb2d610                          // fsub          v16.4s, v16.4s, v18.4s
+  .long  0x3d800100                          // str           q0, [x8]
+  .long  0x3d800901                          // str           q1, [x8, #32]
+  .long  0x3d801111                          // str           q17, [x8, #64]
+  .long  0x3d801910                          // str           q16, [x8, #96]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_accumulate_aarch64
+.globl _sk_accumulate_aarch64
+FUNCTION(_sk_accumulate_aarch64)
+_sk_accumulate_aarch64:
+  .long  0xa8c11428                          // ldp           x8, x5, [x1], #16
+  .long  0x3dc02110                          // ldr           q16, [x8, #128]
+  .long  0x3dc02911                          // ldr           q17, [x8, #160]
+  .long  0x6e31de10                          // fmul          v16.4s, v16.4s, v17.4s
+  .long  0x4e30cc04                          // fmla          v4.4s, v0.4s, v16.4s
+  .long  0x4e30cc25                          // fmla          v5.4s, v1.4s, v16.4s
+  .long  0x4e30cc46                          // fmla          v6.4s, v2.4s, v16.4s
+  .long  0x4e30cc67                          // fmla          v7.4s, v3.4s, v16.4s
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bilinear_nx_aarch64
+.globl _sk_bilinear_nx_aarch64
+FUNCTION(_sk_bilinear_nx_aarch64)
+_sk_bilinear_nx_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4f03f611                          // fmov          v17.4s, #1.000000000000000000e+00
+  .long  0x3dc01100                          // ldr           q0, [x8, #64]
+  .long  0x3dc00110                          // ldr           q16, [x8]
+  .long  0x4ea0d620                          // fsub          v0.4s, v17.4s, v0.4s
+  .long  0x3d802100                          // str           q0, [x8, #128]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f0567e0                          // movi          v0.4s, #0xbf, lsl #24
+  .long  0x4e20d600                          // fadd          v0.4s, v16.4s, v0.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bilinear_px_aarch64
+.globl _sk_bilinear_px_aarch64
+FUNCTION(_sk_bilinear_px_aarch64)
+_sk_bilinear_px_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x3dc01100                          // ldr           q0, [x8, #64]
+  .long  0x3dc00110                          // ldr           q16, [x8]
+  .long  0x3d802100                          // str           q0, [x8, #128]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f0167e0                          // movi          v0.4s, #0x3f, lsl #24
+  .long  0x4e20d600                          // fadd          v0.4s, v16.4s, v0.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bilinear_ny_aarch64
+.globl _sk_bilinear_ny_aarch64
+FUNCTION(_sk_bilinear_ny_aarch64)
+_sk_bilinear_ny_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x4f03f611                          // fmov          v17.4s, #1.000000000000000000e+00
+  .long  0x3dc01901                          // ldr           q1, [x8, #96]
+  .long  0x3dc00910                          // ldr           q16, [x8, #32]
+  .long  0x4ea1d621                          // fsub          v1.4s, v17.4s, v1.4s
+  .long  0x3d802901                          // str           q1, [x8, #160]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f0567e1                          // movi          v1.4s, #0xbf, lsl #24
+  .long  0x4e21d601                          // fadd          v1.4s, v16.4s, v1.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bilinear_py_aarch64
+.globl _sk_bilinear_py_aarch64
+FUNCTION(_sk_bilinear_py_aarch64)
+_sk_bilinear_py_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x3dc01901                          // ldr           q1, [x8, #96]
+  .long  0x3dc00910                          // ldr           q16, [x8, #32]
+  .long  0x3d802901                          // str           q1, [x8, #160]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f0167e1                          // movi          v1.4s, #0x3f, lsl #24
+  .long  0x4e21d601                          // fadd          v1.4s, v16.4s, v1.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_n3x_aarch64
+.globl _sk_bicubic_n3x_aarch64
+FUNCTION(_sk_bicubic_n3x_aarch64)
+_sk_bicubic_n3x_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52a7d8e9                          // mov           w9, #0x3ec70000
+  .long  0x72838e49                          // movk          w9, #0x1c72
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x3dc01111                          // ldr           q17, [x8, #64]
+  .long  0x52b7d549                          // mov           w9, #0xbeaa0000
+  .long  0x4f03f600                          // fmov          v0.4s, #1.000000000000000000e+00
+  .long  0x72955569                          // movk          w9, #0xaaab
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x4eb1d400                          // fsub          v0.4s, v0.4s, v17.4s
+  .long  0x6e20dc11                          // fmul          v17.4s, v0.4s, v0.4s
+  .long  0x4e20ce12                          // fmla          v18.4s, v16.4s, v0.4s
+  .long  0x6e32de20                          // fmul          v0.4s, v17.4s, v18.4s
+  .long  0x3dc00113                          // ldr           q19, [x8]
+  .long  0x3d802100                          // str           q0, [x8, #128]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f07f700                          // fmov          v0.4s, #-1.500000000000000000e+00
+  .long  0x4e20d660                          // fadd          v0.4s, v19.4s, v0.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_n1x_aarch64
+.globl _sk_bicubic_n1x_aarch64
+FUNCTION(_sk_bicubic_n1x_aarch64)
+_sk_bicubic_n1x_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52b7f2a9                          // mov           w9, #0xbf950000
+  .long  0x4f03f600                          // fmov          v0.4s, #1.000000000000000000e+00
+  .long  0x728aaaa9                          // movk          w9, #0x5555
+  .long  0x3dc01110                          // ldr           q16, [x8, #64]
+  .long  0x4f03f711                          // fmov          v17.4s, #1.500000000000000000e+00
+  .long  0x4f0167f2                          // movi          v18.4s, #0x3f, lsl #24
+  .long  0x4eb0d400                          // fsub          v0.4s, v0.4s, v16.4s
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x52a7ac69                          // mov           w9, #0x3d630000
+  .long  0x7291c729                          // movk          w9, #0x8e39
+  .long  0x4e20ce11                          // fmla          v17.4s, v16.4s, v0.4s
+  .long  0x4e20ce32                          // fmla          v18.4s, v17.4s, v0.4s
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x4e20ce51                          // fmla          v17.4s, v18.4s, v0.4s
+  .long  0x3dc00110                          // ldr           q16, [x8]
+  .long  0x3d802111                          // str           q17, [x8, #128]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f0567e0                          // movi          v0.4s, #0xbf, lsl #24
+  .long  0x4e20d600                          // fadd          v0.4s, v16.4s, v0.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_p1x_aarch64
+.globl _sk_bicubic_p1x_aarch64
+FUNCTION(_sk_bicubic_p1x_aarch64)
+_sk_bicubic_p1x_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52b7f2a9                          // mov           w9, #0xbf950000
+  .long  0x728aaaa9                          // movk          w9, #0x5555
+  .long  0x4f03f711                          // fmov          v17.4s, #1.500000000000000000e+00
+  .long  0x3dc01112                          // ldr           q18, [x8, #64]
+  .long  0x3dc00100                          // ldr           q0, [x8]
+  .long  0x4e040d33                          // dup           v19.4s, w9
+  .long  0x52a7ac69                          // mov           w9, #0x3d630000
+  .long  0x4f0167f0                          // movi          v16.4s, #0x3f, lsl #24
+  .long  0x7291c729                          // movk          w9, #0x8e39
+  .long  0x4e32ce71                          // fmla          v17.4s, v19.4s, v18.4s
+  .long  0x4e30d400                          // fadd          v0.4s, v0.4s, v16.4s
+  .long  0x4e32ce30                          // fmla          v16.4s, v17.4s, v18.4s
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x4e32ce11                          // fmla          v17.4s, v16.4s, v18.4s
+  .long  0x3d802111                          // str           q17, [x8, #128]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_p3x_aarch64
+.globl _sk_bicubic_p3x_aarch64
+FUNCTION(_sk_bicubic_p3x_aarch64)
+_sk_bicubic_p3x_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52a7d8e9                          // mov           w9, #0x3ec70000
+  .long  0x72838e49                          // movk          w9, #0x1c72
+  .long  0x4e040d20                          // dup           v0.4s, w9
+  .long  0x3dc01110                          // ldr           q16, [x8, #64]
+  .long  0x52b7d549                          // mov           w9, #0xbeaa0000
+  .long  0x72955569                          // movk          w9, #0xaaab
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x6e30de13                          // fmul          v19.4s, v16.4s, v16.4s
+  .long  0x4e30cc11                          // fmla          v17.4s, v0.4s, v16.4s
+  .long  0x6e31de60                          // fmul          v0.4s, v19.4s, v17.4s
+  .long  0x3dc00112                          // ldr           q18, [x8]
+  .long  0x3d802100                          // str           q0, [x8, #128]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f03f700                          // fmov          v0.4s, #1.500000000000000000e+00
+  .long  0x4e20d640                          // fadd          v0.4s, v18.4s, v0.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_n3y_aarch64
+.globl _sk_bicubic_n3y_aarch64
+FUNCTION(_sk_bicubic_n3y_aarch64)
+_sk_bicubic_n3y_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52a7d8e9                          // mov           w9, #0x3ec70000
+  .long  0x72838e49                          // movk          w9, #0x1c72
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x3dc01911                          // ldr           q17, [x8, #96]
+  .long  0x52b7d549                          // mov           w9, #0xbeaa0000
+  .long  0x4f03f601                          // fmov          v1.4s, #1.000000000000000000e+00
+  .long  0x72955569                          // movk          w9, #0xaaab
+  .long  0x4e040d32                          // dup           v18.4s, w9
+  .long  0x4eb1d421                          // fsub          v1.4s, v1.4s, v17.4s
+  .long  0x6e21dc31                          // fmul          v17.4s, v1.4s, v1.4s
+  .long  0x4e21ce12                          // fmla          v18.4s, v16.4s, v1.4s
+  .long  0x6e32de21                          // fmul          v1.4s, v17.4s, v18.4s
+  .long  0x3dc00913                          // ldr           q19, [x8, #32]
+  .long  0x3d802901                          // str           q1, [x8, #160]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f07f701                          // fmov          v1.4s, #-1.500000000000000000e+00
+  .long  0x4e21d661                          // fadd          v1.4s, v19.4s, v1.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_n1y_aarch64
+.globl _sk_bicubic_n1y_aarch64
+FUNCTION(_sk_bicubic_n1y_aarch64)
+_sk_bicubic_n1y_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52b7f2a9                          // mov           w9, #0xbf950000
+  .long  0x4f03f601                          // fmov          v1.4s, #1.000000000000000000e+00
+  .long  0x728aaaa9                          // movk          w9, #0x5555
+  .long  0x3dc01910                          // ldr           q16, [x8, #96]
+  .long  0x4f03f711                          // fmov          v17.4s, #1.500000000000000000e+00
+  .long  0x4f0167f2                          // movi          v18.4s, #0x3f, lsl #24
+  .long  0x4eb0d421                          // fsub          v1.4s, v1.4s, v16.4s
+  .long  0x4e040d30                          // dup           v16.4s, w9
+  .long  0x52a7ac69                          // mov           w9, #0x3d630000
+  .long  0x7291c729                          // movk          w9, #0x8e39
+  .long  0x4e21ce11                          // fmla          v17.4s, v16.4s, v1.4s
+  .long  0x4e21ce32                          // fmla          v18.4s, v17.4s, v1.4s
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x4e21ce51                          // fmla          v17.4s, v18.4s, v1.4s
+  .long  0x3dc00910                          // ldr           q16, [x8, #32]
+  .long  0x3d802911                          // str           q17, [x8, #160]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f0567e1                          // movi          v1.4s, #0xbf, lsl #24
+  .long  0x4e21d601                          // fadd          v1.4s, v16.4s, v1.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_p1y_aarch64
+.globl _sk_bicubic_p1y_aarch64
+FUNCTION(_sk_bicubic_p1y_aarch64)
+_sk_bicubic_p1y_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52b7f2a9                          // mov           w9, #0xbf950000
+  .long  0x728aaaa9                          // movk          w9, #0x5555
+  .long  0x4f03f711                          // fmov          v17.4s, #1.500000000000000000e+00
+  .long  0x3dc01912                          // ldr           q18, [x8, #96]
+  .long  0x3dc00901                          // ldr           q1, [x8, #32]
+  .long  0x4e040d33                          // dup           v19.4s, w9
+  .long  0x52a7ac69                          // mov           w9, #0x3d630000
+  .long  0x4f0167f0                          // movi          v16.4s, #0x3f, lsl #24
+  .long  0x7291c729                          // movk          w9, #0x8e39
+  .long  0x4e32ce71                          // fmla          v17.4s, v19.4s, v18.4s
+  .long  0x4e30d421                          // fadd          v1.4s, v1.4s, v16.4s
+  .long  0x4e32ce30                          // fmla          v16.4s, v17.4s, v18.4s
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x4e32ce11                          // fmla          v17.4s, v16.4s, v18.4s
+  .long  0x3d802911                          // str           q17, [x8, #160]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_bicubic_p3y_aarch64
+.globl _sk_bicubic_p3y_aarch64
+FUNCTION(_sk_bicubic_p3y_aarch64)
+_sk_bicubic_p3y_aarch64:
+  .long  0xf9400028                          // ldr           x8, [x1]
+  .long  0x52a7d8e9                          // mov           w9, #0x3ec70000
+  .long  0x72838e49                          // movk          w9, #0x1c72
+  .long  0x4e040d21                          // dup           v1.4s, w9
+  .long  0x3dc01910                          // ldr           q16, [x8, #96]
+  .long  0x52b7d549                          // mov           w9, #0xbeaa0000
+  .long  0x72955569                          // movk          w9, #0xaaab
+  .long  0x4e040d31                          // dup           v17.4s, w9
+  .long  0x6e30de13                          // fmul          v19.4s, v16.4s, v16.4s
+  .long  0x4e30cc31                          // fmla          v17.4s, v1.4s, v16.4s
+  .long  0x6e31de61                          // fmul          v1.4s, v19.4s, v17.4s
+  .long  0x3dc00912                          // ldr           q18, [x8, #32]
+  .long  0x3d802901                          // str           q1, [x8, #160]
+  .long  0xf9400425                          // ldr           x5, [x1, #8]
+  .long  0x4f03f701                          // fmov          v1.4s, #1.500000000000000000e+00
+  .long  0x4e21d641                          // fadd          v1.4s, v18.4s, v1.4s
+  .long  0x91004021                          // add           x1, x1, #0x10
+  .long  0xd61f00a0                          // br            x5
+
+HIDDEN _sk_callback_aarch64
+.globl _sk_callback_aarch64
+FUNCTION(_sk_callback_aarch64)
+_sk_callback_aarch64:
+  .long  0xd10203ff                          // sub           sp, sp, #0x80
+  .long  0xa9045ff8                          // stp           x24, x23, [sp, #64]
+  .long  0xa90557f6                          // stp           x22, x21, [sp, #80]
+  .long  0xa9064ff4                          // stp           x20, x19, [sp, #96]
+  .long  0xa9077bfd                          // stp           x29, x30, [sp, #112]
+  .long  0xad011fe6                          // stp           q6, q7, [sp, #32]
+  .long  0xad0017e4                          // stp           q4, q5, [sp]
+  .long  0xaa0103f6                          // mov           x22, x1
+  .long  0xf94002d8                          // ldr           x24, [x22]
+  .long  0xaa0403f3                          // mov           x19, x4
+  .long  0xf100027f                          // cmp           x19, #0x0
+  .long  0x321e03e9                          // orr           w9, wzr, #0x4
+  .long  0x91002308                          // add           x8, x24, #0x8
+  .long  0x4c000900                          // st4           {v0.4s-v3.4s}, [x8]
+  .long  0xf9400308                          // ldr           x8, [x24]
+  .long  0xaa0003f7                          // mov           x23, x0
+  .long  0x1a891261                          // csel          w1, w19, w9, ne  // ne = any
+  .long  0xaa1803e0                          // mov           x0, x24
+  .long  0x9101c3fd                          // add           x29, sp, #0x70
+  .long  0xaa0303f4                          // mov           x20, x3
+  .long  0xaa0203f5                          // mov           x21, x2
+  .long  0xd63f0100                          // blr           x8
+  .long  0xf9404708                          // ldr           x8, [x24, #136]
+  .long  0xf94006c5                          // ldr           x5, [x22, #8]
+  .long  0x910042c1                          // add           x1, x22, #0x10
+  .long  0xaa1703e0                          // mov           x0, x23
+  .long  0x4c400900                          // ld4           {v0.4s-v3.4s}, [x8]
+  .long  0xaa1503e2                          // mov           x2, x21
+  .long  0xaa1403e3                          // mov           x3, x20
+  .long  0xaa1303e4                          // mov           x4, x19
+  .long  0xad4017e4                          // ldp           q4, q5, [sp]
+  .long  0xad411fe6                          // ldp           q6, q7, [sp, #32]
+  .long  0xa9477bfd                          // ldp           x29, x30, [sp, #112]
+  .long  0xa9464ff4                          // ldp           x20, x19, [sp, #96]
+  .long  0xa94557f6                          // ldp           x22, x21, [sp, #80]
+  .long  0xa9445ff8                          // ldp           x24, x23, [sp, #64]
+  .long  0x910203ff                          // add           sp, sp, #0x80
+  .long  0xd61f00a0                          // br            x5
+#elif defined(__arm__)
+BALIGN4
+
+HIDDEN _sk_start_pipeline_vfp4
+.globl _sk_start_pipeline_vfp4
+FUNCTION(_sk_start_pipeline_vfp4)
+_sk_start_pipeline_vfp4:
+  .long  0xe92d4ff0                          // push          {r4, r5, r6, r7, r8, r9, sl, fp, lr}
+  .long  0xe28db01c                          // add           fp, sp, #28
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xe1a04003                          // mov           r4, r3
+  .long  0xe59b8008                          // ldr           r8, [fp, #8]
+  .long  0xe4949004                          // ldr           r9, [r4], #4
+  .long  0xe1a06000                          // mov           r6, r0
+  .long  0xe2860002                          // add           r0, r6, #2
+  .long  0xe1a07002                          // mov           r7, r2
+  .long  0xe1a05001                          // mov           r5, r1
+  .long  0xe1500007                          // cmp           r0, r7
+  .long  0x8a000012                          // bhi           7c <sk_start_pipeline_vfp4+0x7c>
+  .long  0xe3a0a000                          // mov           sl, #0
+  .long  0xf2800010                          // vmov.i32      d0, #0
+  .long  0xe1a00008                          // mov           r0, r8
+  .long  0xf2801010                          // vmov.i32      d1, #0
+  .long  0xe1a01004                          // mov           r1, r4
+  .long  0xf2802010                          // vmov.i32      d2, #0
+  .long  0xe1a02006                          // mov           r2, r6
+  .long  0xf2803010                          // vmov.i32      d3, #0
+  .long  0xe1a03005                          // mov           r3, r5
+  .long  0xf2804010                          // vmov.i32      d4, #0
+  .long  0xe58da000                          // str           sl, [sp]
+  .long  0xf2805010                          // vmov.i32      d5, #0
+  .long  0xf2806010                          // vmov.i32      d6, #0
+  .long  0xf2807010                          // vmov.i32      d7, #0
+  .long  0xe12fff39                          // blx           r9
+  .long  0xe2860004                          // add           r0, r6, #4
+  .long  0xe2866002                          // add           r6, r6, #2
+  .long  0xe1500007                          // cmp           r0, r7
+  .long  0x9affffed                          // bls           34 <sk_start_pipeline_vfp4+0x34>
+  .long  0xe0570006                          // subs          r0, r7, r6
+  .long  0x0a00000d                          // beq           bc <sk_start_pipeline_vfp4+0xbc>
+  .long  0xf2800010                          // vmov.i32      d0, #0
+  .long  0xe58d0000                          // str           r0, [sp]
+  .long  0xf2801010                          // vmov.i32      d1, #0
+  .long  0xe1a00008                          // mov           r0, r8
+  .long  0xf2802010                          // vmov.i32      d2, #0
+  .long  0xe1a01004                          // mov           r1, r4
+  .long  0xf2803010                          // vmov.i32      d3, #0
+  .long  0xe1a02006                          // mov           r2, r6
+  .long  0xf2804010                          // vmov.i32      d4, #0
+  .long  0xe1a03005                          // mov           r3, r5
+  .long  0xf2805010                          // vmov.i32      d5, #0
+  .long  0xf2806010                          // vmov.i32      d6, #0
+  .long  0xf2807010                          // vmov.i32      d7, #0
+  .long  0xe12fff39                          // blx           r9
+  .long  0xe24bd01c                          // sub           sp, fp, #28
+  .long  0xe8bd8ff0                          // pop           {r4, r5, r6, r7, r8, r9, sl, fp, pc}
+
+HIDDEN _sk_just_return_vfp4
+.globl _sk_just_return_vfp4
+FUNCTION(_sk_just_return_vfp4)
+_sk_just_return_vfp4:
+  .long  0xe12fff1e                          // bx            lr
+
+HIDDEN _sk_seed_shader_vfp4
+.globl _sk_seed_shader_vfp4
+FUNCTION(_sk_seed_shader_vfp4)
+_sk_seed_shader_vfp4:
+  .long  0xee802b90                          // vdup.32       d16, r2
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xee823b90                          // vdup.32       d18, r3
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xedd03b00                          // vldr          d19, [r0]
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2872f10                          // vmov.f32      d2, #1
+  .long  0xf2803010                          // vmov.i32      d3, #0
+  .long  0xf2400da1                          // vadd.f32      d16, d16, d17
+  .long  0xf2021da1                          // vadd.f32      d1, d18, d17
+  .long  0xf2804010                          // vmov.i32      d4, #0
+  .long  0xf2805010                          // vmov.i32      d5, #0
+  .long  0xf2000da3                          // vadd.f32      d0, d16, d19
+  .long  0xf2806010                          // vmov.i32      d6, #0
+  .long  0xf2807010                          // vmov.i32      d7, #0
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_dither_vfp4
+.globl _sk_dither_vfp4
+FUNCTION(_sk_dither_vfp4)
+_sk_dither_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xee802b90                          // vdup.32       d16, r2
+  .long  0xf2c02011                          // vmov.i32      d18, #1
+  .long  0xedd01b08                          // vldr          d17, [r0, #32]
+  .long  0xf2c03014                          // vmov.i32      d19, #4
+  .long  0xf26108a0                          // vadd.i32      d16, d17, d16
+  .long  0xee853b90                          // vdup.32       d21, r3
+  .long  0xf2c01012                          // vmov.i32      d17, #2
+  .long  0xe3a0c5f2                          // mov           ip, #1015021568
+  .long  0xf24041b2                          // vand          d20, d16, d18
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf24061b1                          // vand          d22, d16, d17
+  .long  0xf34551b0                          // veor          d21, d21, d16
+  .long  0xf24001b3                          // vand          d16, d16, d19
+  .long  0xf2e44534                          // vshl.s32      d20, d20, #4
+  .long  0xf2e16536                          // vshl.s32      d22, d22, #1
+  .long  0xf24521b2                          // vand          d18, d21, d18
+  .long  0xf3fe0030                          // vshr.u32      d16, d16, #2
+  .long  0xf26641b4                          // vorr          d20, d22, d20
+  .long  0xf24511b1                          // vand          d17, d21, d17
+  .long  0xf2e52532                          // vshl.s32      d18, d18, #5
+  .long  0xf26401b0                          // vorr          d16, d20, d16
+  .long  0xf24531b3                          // vand          d19, d21, d19
+  .long  0xf26001b2                          // vorr          d16, d16, d18
+  .long  0xf2e21531                          // vshl.s32      d17, d17, #2
+  .long  0xf3ff2033                          // vshr.u32      d18, d19, #1
+  .long  0xf26001b1                          // vorr          d16, d16, d17
+  .long  0xee81cb90                          // vdup.32       d17, ip
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf26001b2                          // vorr          d16, d16, d18
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xeddf1b0e                          // vldr          d17, [pc, #56]
+  .long  0xf2400da1                          // vadd.f32      d16, d16, d17
+  .long  0xf4ee1c9f                          // vld1.32       {d17[]}, [lr :32]
+  .long  0xf3410db0                          // vmul.f32      d16, d17, d16
+  .long  0xf2401d80                          // vadd.f32      d17, d16, d0
+  .long  0xf2402d81                          // vadd.f32      d18, d16, d1
+  .long  0xf2400d82                          // vadd.f32      d16, d16, d2
+  .long  0xf2611f83                          // vmin.f32      d17, d17, d3
+  .long  0xf2622f83                          // vmin.f32      d18, d18, d3
+  .long  0xf2600f83                          // vmin.f32      d16, d16, d3
+  .long  0xf2030fa1                          // vmax.f32      d0, d19, d17
+  .long  0xf2031fa2                          // vmax.f32      d1, d19, d18
+  .long  0xf2032fa0                          // vmax.f32      d2, d19, d16
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0xbefc0000                          // .word         0xbefc0000
+  .long  0xbefc0000                          // .word         0xbefc0000
+
+HIDDEN _sk_constant_color_vfp4
+.globl _sk_constant_color_vfp4
+FUNCTION(_sk_constant_color_vfp4)
+_sk_constant_color_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e500c                          // add           r5, lr, #12
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xf4a40c9d                          // vld1.32       {d0[]}, [r4 :32]!
+  .long  0xf4a53c9f                          // vld1.32       {d3[]}, [r5 :32]
+  .long  0xe28e5008                          // add           r5, lr, #8
+  .long  0xf4a52c9f                          // vld1.32       {d2[]}, [r5 :32]
+  .long  0xf4a41c9f                          // vld1.32       {d1[]}, [r4 :32]
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_load_rgba_vfp4
+.globl _sk_load_rgba_vfp4
+FUNCTION(_sk_load_rgba_vfp4)
+_sk_load_rgba_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xed9e2b04                          // vldr          d2, [lr, #16]
+  .long  0xf424078d                          // vld1.32       {d0}, [r4]!
+  .long  0xed9e3b06                          // vldr          d3, [lr, #24]
+  .long  0xed941b00                          // vldr          d1, [r4]
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_store_rgba_vfp4
+.globl _sk_store_rgba_vfp4
+FUNCTION(_sk_store_rgba_vfp4)
+_sk_store_rgba_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe1a0e00c                          // mov           lr, ip
+  .long  0xf40e078d                          // vst1.32       {d0}, [lr]!
+  .long  0xed8e1b00                          // vstr          d1, [lr]
+  .long  0xed8c2b04                          // vstr          d2, [ip, #16]
+  .long  0xed8c3b06                          // vstr          d3, [ip, #24]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_clear_vfp4
+.globl _sk_clear_vfp4
+FUNCTION(_sk_clear_vfp4)
+_sk_clear_vfp4:
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2800010                          // vmov.i32      d0, #0
+  .long  0xf2801010                          // vmov.i32      d1, #0
+  .long  0xf2802010                          // vmov.i32      d2, #0
+  .long  0xf2803010                          // vmov.i32      d3, #0
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_srcatop_vfp4
+.globl _sk_srcatop_vfp4
+FUNCTION(_sk_srcatop_vfp4)
+_sk_srcatop_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2603d83                          // vsub.f32      d19, d16, d3
+  .long  0xf3033d17                          // vmul.f32      d3, d3, d7
+  .long  0xf3430d94                          // vmul.f32      d16, d19, d4
+  .long  0xf3431d95                          // vmul.f32      d17, d19, d5
+  .long  0xf3432d96                          // vmul.f32      d18, d19, d6
+  .long  0xf2400c17                          // vfma.f32      d16, d0, d7
+  .long  0xf2411c17                          // vfma.f32      d17, d1, d7
+  .long  0xf2422c17                          // vfma.f32      d18, d2, d7
+  .long  0xf2033c97                          // vfma.f32      d3, d19, d7
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_dstatop_vfp4
+.globl _sk_dstatop_vfp4
+FUNCTION(_sk_dstatop_vfp4)
+_sk_dstatop_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3431d15                          // vmul.f32      d17, d3, d5
+  .long  0xf2604d87                          // vsub.f32      d20, d16, d7
+  .long  0xf3430d14                          // vmul.f32      d16, d3, d4
+  .long  0xf3432d16                          // vmul.f32      d18, d3, d6
+  .long  0xf3433d17                          // vmul.f32      d19, d3, d7
+  .long  0xf2440c90                          // vfma.f32      d16, d20, d0
+  .long  0xf2441c91                          // vfma.f32      d17, d20, d1
+  .long  0xf2442c92                          // vfma.f32      d18, d20, d2
+  .long  0xf2443c93                          // vfma.f32      d19, d20, d3
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xf22331b3                          // vorr          d3, d19, d19
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_srcin_vfp4
+.globl _sk_srcin_vfp4
+FUNCTION(_sk_srcin_vfp4)
+_sk_srcin_vfp4:
+  .long  0xf3000d17                          // vmul.f32      d0, d0, d7
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3011d17                          // vmul.f32      d1, d1, d7
+  .long  0xf3022d17                          // vmul.f32      d2, d2, d7
+  .long  0xf3033d17                          // vmul.f32      d3, d3, d7
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_dstin_vfp4
+.globl _sk_dstin_vfp4
+FUNCTION(_sk_dstin_vfp4)
+_sk_dstin_vfp4:
+  .long  0xf3030d14                          // vmul.f32      d0, d3, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3031d15                          // vmul.f32      d1, d3, d5
+  .long  0xf3032d16                          // vmul.f32      d2, d3, d6
+  .long  0xf3033d17                          // vmul.f32      d3, d3, d7
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_srcout_vfp4
+.globl _sk_srcout_vfp4
+FUNCTION(_sk_srcout_vfp4)
+_sk_srcout_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2600d87                          // vsub.f32      d16, d16, d7
+  .long  0xf3000d90                          // vmul.f32      d0, d16, d0
+  .long  0xf3001d91                          // vmul.f32      d1, d16, d1
+  .long  0xf3002d92                          // vmul.f32      d2, d16, d2
+  .long  0xf3003d93                          // vmul.f32      d3, d16, d3
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_dstout_vfp4
+.globl _sk_dstout_vfp4
+FUNCTION(_sk_dstout_vfp4)
+_sk_dstout_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf3000d94                          // vmul.f32      d0, d16, d4
+  .long  0xf3001d95                          // vmul.f32      d1, d16, d5
+  .long  0xf3002d96                          // vmul.f32      d2, d16, d6
+  .long  0xf3003d97                          // vmul.f32      d3, d16, d7
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_srcover_vfp4
+.globl _sk_srcover_vfp4
+FUNCTION(_sk_srcover_vfp4)
+_sk_srcover_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf2040c30                          // vfma.f32      d0, d4, d16
+  .long  0xf2051c30                          // vfma.f32      d1, d5, d16
+  .long  0xf2062c30                          // vfma.f32      d2, d6, d16
+  .long  0xf2073c30                          // vfma.f32      d3, d7, d16
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_dstover_vfp4
+.globl _sk_dstover_vfp4
+FUNCTION(_sk_dstover_vfp4)
+_sk_dstover_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2651115                          // vorr          d17, d5, d5
+  .long  0xf2604d87                          // vsub.f32      d20, d16, d7
+  .long  0xf2640114                          // vorr          d16, d4, d4
+  .long  0xf2662116                          // vorr          d18, d6, d6
+  .long  0xf2673117                          // vorr          d19, d7, d7
+  .long  0xf2400c34                          // vfma.f32      d16, d0, d20
+  .long  0xf2411c34                          // vfma.f32      d17, d1, d20
+  .long  0xf2422c34                          // vfma.f32      d18, d2, d20
+  .long  0xf2433c34                          // vfma.f32      d19, d3, d20
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xf22331b3                          // vorr          d3, d19, d19
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_modulate_vfp4
+.globl _sk_modulate_vfp4
+FUNCTION(_sk_modulate_vfp4)
+_sk_modulate_vfp4:
+  .long  0xf3000d14                          // vmul.f32      d0, d0, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3011d15                          // vmul.f32      d1, d1, d5
+  .long  0xf3022d16                          // vmul.f32      d2, d2, d6
+  .long  0xf3033d17                          // vmul.f32      d3, d3, d7
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_multiply_vfp4
+.globl _sk_multiply_vfp4
+FUNCTION(_sk_multiply_vfp4)
+_sk_multiply_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2601d87                          // vsub.f32      d17, d16, d7
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf3412d90                          // vmul.f32      d18, d17, d0
+  .long  0xf3403d94                          // vmul.f32      d19, d16, d4
+  .long  0xf3414d91                          // vmul.f32      d20, d17, d1
+  .long  0xf3405d95                          // vmul.f32      d21, d16, d5
+  .long  0xf3416d92                          // vmul.f32      d22, d17, d2
+  .long  0xf3418d93                          // vmul.f32      d24, d17, d3
+  .long  0xf3407d96                          // vmul.f32      d23, d16, d6
+  .long  0xf3409d97                          // vmul.f32      d25, d16, d7
+  .long  0xf2430da2                          // vadd.f32      d16, d19, d18
+  .long  0xf2451da4                          // vadd.f32      d17, d21, d20
+  .long  0xf2472da6                          // vadd.f32      d18, d23, d22
+  .long  0xf2493da8                          // vadd.f32      d19, d25, d24
+  .long  0xf2400c14                          // vfma.f32      d16, d0, d4
+  .long  0xf2411c15                          // vfma.f32      d17, d1, d5
+  .long  0xf2422c16                          // vfma.f32      d18, d2, d6
+  .long  0xf2433c17                          // vfma.f32      d19, d3, d7
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xf22331b3                          // vorr          d3, d19, d19
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_plus__vfp4
+.globl _sk_plus__vfp4
+FUNCTION(_sk_plus__vfp4)
+_sk_plus__vfp4:
+  .long  0xf2000d04                          // vadd.f32      d0, d0, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2011d05                          // vadd.f32      d1, d1, d5
+  .long  0xf2022d06                          // vadd.f32      d2, d2, d6
+  .long  0xf2033d07                          // vadd.f32      d3, d3, d7
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_screen_vfp4
+.globl _sk_screen_vfp4
+FUNCTION(_sk_screen_vfp4)
+_sk_screen_vfp4:
+  .long  0xf2400d04                          // vadd.f32      d16, d0, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2411d05                          // vadd.f32      d17, d1, d5
+  .long  0xf2422d06                          // vadd.f32      d18, d2, d6
+  .long  0xf2433d07                          // vadd.f32      d19, d3, d7
+  .long  0xf2600c14                          // vfms.f32      d16, d0, d4
+  .long  0xf2611c15                          // vfms.f32      d17, d1, d5
+  .long  0xf2622c16                          // vfms.f32      d18, d2, d6
+  .long  0xf2633c17                          // vfms.f32      d19, d3, d7
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xf22331b3                          // vorr          d3, d19, d19
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_xor__vfp4
+.globl _sk_xor__vfp4
+FUNCTION(_sk_xor__vfp4)
+_sk_xor__vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2603d83                          // vsub.f32      d19, d16, d3
+  .long  0xf2604d87                          // vsub.f32      d20, d16, d7
+  .long  0xf3430d94                          // vmul.f32      d16, d19, d4
+  .long  0xf3431d95                          // vmul.f32      d17, d19, d5
+  .long  0xf3432d96                          // vmul.f32      d18, d19, d6
+  .long  0xf3433d97                          // vmul.f32      d19, d19, d7
+  .long  0xf2440c90                          // vfma.f32      d16, d20, d0
+  .long  0xf2441c91                          // vfma.f32      d17, d20, d1
+  .long  0xf2442c92                          // vfma.f32      d18, d20, d2
+  .long  0xf2443c93                          // vfma.f32      d19, d20, d3
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xf22331b3                          // vorr          d3, d19, d19
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_darken_vfp4
+.globl _sk_darken_vfp4
+FUNCTION(_sk_darken_vfp4)
+_sk_darken_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3431d14                          // vmul.f32      d17, d3, d4
+  .long  0xf3402d17                          // vmul.f32      d18, d0, d7
+  .long  0xf3433d15                          // vmul.f32      d19, d3, d5
+  .long  0xf3414d17                          // vmul.f32      d20, d1, d7
+  .long  0xf3435d16                          // vmul.f32      d21, d3, d6
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf3426d17                          // vmul.f32      d22, d2, d7
+  .long  0xf2421fa1                          // vmax.f32      d17, d18, d17
+  .long  0xf2407d04                          // vadd.f32      d23, d0, d4
+  .long  0xf2443fa3                          // vmax.f32      d19, d20, d19
+  .long  0xf2412d05                          // vadd.f32      d18, d1, d5
+  .long  0xf2424d06                          // vadd.f32      d20, d2, d6
+  .long  0xf2465fa5                          // vmax.f32      d21, d22, d21
+  .long  0xf2073c30                          // vfma.f32      d3, d7, d16
+  .long  0xf2270da1                          // vsub.f32      d0, d23, d17
+  .long  0xf2221da3                          // vsub.f32      d1, d18, d19
+  .long  0xf2242da5                          // vsub.f32      d2, d20, d21
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_lighten_vfp4
+.globl _sk_lighten_vfp4
+FUNCTION(_sk_lighten_vfp4)
+_sk_lighten_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3431d14                          // vmul.f32      d17, d3, d4
+  .long  0xf3402d17                          // vmul.f32      d18, d0, d7
+  .long  0xf3433d15                          // vmul.f32      d19, d3, d5
+  .long  0xf3414d17                          // vmul.f32      d20, d1, d7
+  .long  0xf3435d16                          // vmul.f32      d21, d3, d6
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf3426d17                          // vmul.f32      d22, d2, d7
+  .long  0xf2621fa1                          // vmin.f32      d17, d18, d17
+  .long  0xf2407d04                          // vadd.f32      d23, d0, d4
+  .long  0xf2643fa3                          // vmin.f32      d19, d20, d19
+  .long  0xf2412d05                          // vadd.f32      d18, d1, d5
+  .long  0xf2424d06                          // vadd.f32      d20, d2, d6
+  .long  0xf2665fa5                          // vmin.f32      d21, d22, d21
+  .long  0xf2073c30                          // vfma.f32      d3, d7, d16
+  .long  0xf2270da1                          // vsub.f32      d0, d23, d17
+  .long  0xf2221da3                          // vsub.f32      d1, d18, d19
+  .long  0xf2242da5                          // vsub.f32      d2, d20, d21
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_difference_vfp4
+.globl _sk_difference_vfp4
+FUNCTION(_sk_difference_vfp4)
+_sk_difference_vfp4:
+  .long  0xf3430d14                          // vmul.f32      d16, d3, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3401d17                          // vmul.f32      d17, d0, d7
+  .long  0xf3432d15                          // vmul.f32      d18, d3, d5
+  .long  0xf3413d17                          // vmul.f32      d19, d1, d7
+  .long  0xf3434d16                          // vmul.f32      d20, d3, d6
+  .long  0xf3425d17                          // vmul.f32      d21, d2, d7
+  .long  0xf2c76f10                          // vmov.f32      d22, #1
+  .long  0xf2610fa0                          // vmin.f32      d16, d17, d16
+  .long  0xf2631fa2                          // vmin.f32      d17, d19, d18
+  .long  0xf2662d83                          // vsub.f32      d18, d22, d3
+  .long  0xf2653fa4                          // vmin.f32      d19, d21, d20
+  .long  0xf2404d04                          // vadd.f32      d20, d0, d4
+  .long  0xf2400da0                          // vadd.f32      d16, d16, d16
+  .long  0xf2073c32                          // vfma.f32      d3, d7, d18
+  .long  0xf2415d05                          // vadd.f32      d21, d1, d5
+  .long  0xf2411da1                          // vadd.f32      d17, d17, d17
+  .long  0xf2426d06                          // vadd.f32      d22, d2, d6
+  .long  0xf2432da3                          // vadd.f32      d18, d19, d19
+  .long  0xf2240da0                          // vsub.f32      d0, d20, d16
+  .long  0xf2251da1                          // vsub.f32      d1, d21, d17
+  .long  0xf2262da2                          // vsub.f32      d2, d22, d18
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_exclusion_vfp4
+.globl _sk_exclusion_vfp4
+FUNCTION(_sk_exclusion_vfp4)
+_sk_exclusion_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3401d14                          // vmul.f32      d17, d0, d4
+  .long  0xf3412d15                          // vmul.f32      d18, d1, d5
+  .long  0xf3423d16                          // vmul.f32      d19, d2, d6
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf2404d04                          // vadd.f32      d20, d0, d4
+  .long  0xf2411da1                          // vadd.f32      d17, d17, d17
+  .long  0xf2415d05                          // vadd.f32      d21, d1, d5
+  .long  0xf2422da2                          // vadd.f32      d18, d18, d18
+  .long  0xf2426d06                          // vadd.f32      d22, d2, d6
+  .long  0xf2433da3                          // vadd.f32      d19, d19, d19
+  .long  0xf2073c30                          // vfma.f32      d3, d7, d16
+  .long  0xf2240da1                          // vsub.f32      d0, d20, d17
+  .long  0xf2251da2                          // vsub.f32      d1, d21, d18
+  .long  0xf2262da3                          // vsub.f32      d2, d22, d19
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_colorburn_vfp4
+.globl _sk_colorburn_vfp4
+FUNCTION(_sk_colorburn_vfp4)
+_sk_colorburn_vfp4:
+  .long  0xed2d8b08                          // vpush         {d8-d11}
+  .long  0xf2670d04                          // vsub.f32      d16, d7, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2671d06                          // vsub.f32      d17, d7, d6
+  .long  0xf2672d05                          // vsub.f32      d18, d7, d5
+  .long  0xf3008d93                          // vmul.f32      d8, d16, d3
+  .long  0xf3019d93                          // vmul.f32      d9, d17, d3
+  .long  0xf302ad93                          // vmul.f32      d10, d18, d3
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xeec8baa0                          // vdiv.f32      s23, s17, s1
+  .long  0xee88ba00                          // vdiv.f32      s22, s16, s0
+  .long  0xeec98aa2                          // vdiv.f32      s17, s19, s5
+  .long  0xee898a02                          // vdiv.f32      s16, s18, s4
+  .long  0xeeca9aa1                          // vdiv.f32      s19, s21, s3
+  .long  0xee8a9a01                          // vdiv.f32      s18, s20, s2
+  .long  0xf2672f08                          // vmin.f32      d18, d7, d8
+  .long  0xf2673f09                          // vmin.f32      d19, d7, d9
+  .long  0xf2670f0b                          // vmin.f32      d16, d7, d11
+  .long  0xf2614d87                          // vsub.f32      d20, d17, d7
+  .long  0xf2672d22                          // vsub.f32      d18, d7, d18
+  .long  0xf2673d23                          // vsub.f32      d19, d7, d19
+  .long  0xf2611d83                          // vsub.f32      d17, d17, d3
+  .long  0xf2670d20                          // vsub.f32      d16, d7, d16
+  .long  0xf3445d90                          // vmul.f32      d21, d20, d0
+  .long  0xf3446d92                          // vmul.f32      d22, d20, d2
+  .long  0xf3422d93                          // vmul.f32      d18, d18, d3
+  .long  0xf3444d91                          // vmul.f32      d20, d20, d1
+  .long  0xf3433d93                          // vmul.f32      d19, d19, d3
+  .long  0xf3400d93                          // vmul.f32      d16, d16, d3
+  .long  0xf3417d95                          // vmul.f32      d23, d17, d5
+  .long  0xf3418d94                          // vmul.f32      d24, d17, d4
+  .long  0xf3419d96                          // vmul.f32      d25, d17, d6
+  .long  0xf2443da3                          // vadd.f32      d19, d20, d19
+  .long  0xf2462da2                          // vadd.f32      d18, d22, d18
+  .long  0xf245ada0                          // vadd.f32      d26, d21, d16
+  .long  0xf247bd81                          // vadd.f32      d27, d23, d1
+  .long  0xf248cd80                          // vadd.f32      d28, d24, d0
+  .long  0xf249dd82                          // vadd.f32      d29, d25, d2
+  .long  0xf2073c31                          // vfma.f32      d3, d7, d17
+  .long  0xf2499da2                          // vadd.f32      d25, d25, d18
+  .long  0xf2473da3                          // vadd.f32      d19, d23, d19
+  .long  0xf3f97501                          // vceq.f32      d23, d1, #0
+  .long  0xf2455d84                          // vadd.f32      d21, d21, d4
+  .long  0xf2444d85                          // vadd.f32      d20, d20, d5
+  .long  0xf2440e07                          // vceq.f32      d16, d4, d7
+  .long  0xf2466d86                          // vadd.f32      d22, d22, d6
+  .long  0xf2451e07                          // vceq.f32      d17, d5, d7
+  .long  0xf2462e07                          // vceq.f32      d18, d6, d7
+  .long  0xf35b71b3                          // vbsl          d23, d27, d19
+  .long  0xf3f93500                          // vceq.f32      d19, d0, #0
+  .long  0xf2488daa                          // vadd.f32      d24, d24, d26
+  .long  0xf35c31b8                          // vbsl          d19, d28, d24
+  .long  0xf3f98502                          // vceq.f32      d24, d2, #0
+  .long  0xf35d81b9                          // vbsl          d24, d29, d25
+  .long  0xf35501b3                          // vbsl          d16, d21, d19
+  .long  0xf35411b7                          // vbsl          d17, d20, d23
+  .long  0xf35621b8                          // vbsl          d18, d22, d24
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xecbd8b08                          // vpop          {d8-d11}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_colordodge_vfp4
+.globl _sk_colordodge_vfp4
+FUNCTION(_sk_colordodge_vfp4)
+_sk_colordodge_vfp4:
+  .long  0xed2d8b0e                          // vpush         {d8-d14}
+  .long  0xf2238d02                          // vsub.f32      d8, d3, d2
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3039d16                          // vmul.f32      d9, d3, d6
+  .long  0xf223ad01                          // vsub.f32      d10, d3, d1
+  .long  0xf303bd15                          // vmul.f32      d11, d3, d5
+  .long  0xf223cd00                          // vsub.f32      d12, d3, d0
+  .long  0xf303dd14                          // vmul.f32      d13, d3, d4
+  .long  0xeec9eaa8                          // vdiv.f32      s29, s19, s17
+  .long  0xee89ea08                          // vdiv.f32      s28, s18, s16
+  .long  0xeecb8aaa                          // vdiv.f32      s17, s23, s21
+  .long  0xeecd9aac                          // vdiv.f32      s19, s27, s25
+  .long  0xee8b8a0a                          // vdiv.f32      s16, s22, s20
+  .long  0xee8d9a0c                          // vdiv.f32      s18, s26, s24
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xf2672f0e                          // vmin.f32      d18, d7, d14
+  .long  0xf2601d87                          // vsub.f32      d17, d16, d7
+  .long  0xf2673f08                          // vmin.f32      d19, d7, d8
+  .long  0xf2674f09                          // vmin.f32      d20, d7, d9
+  .long  0xf2600d83                          // vsub.f32      d16, d16, d3
+  .long  0xf3415d92                          // vmul.f32      d21, d17, d2
+  .long  0xf3422d93                          // vmul.f32      d18, d18, d3
+  .long  0xf3416d91                          // vmul.f32      d22, d17, d1
+  .long  0xf3433d93                          // vmul.f32      d19, d19, d3
+  .long  0xf3411d90                          // vmul.f32      d17, d17, d0
+  .long  0xf3444d93                          // vmul.f32      d20, d20, d3
+  .long  0xf3407d95                          // vmul.f32      d23, d16, d5
+  .long  0xf3408d94                          // vmul.f32      d24, d16, d4
+  .long  0xf3409d96                          // vmul.f32      d25, d16, d6
+  .long  0xf2452da2                          // vadd.f32      d18, d21, d18
+  .long  0xf2463da3                          // vadd.f32      d19, d22, d19
+  .long  0xf2414da4                          // vadd.f32      d20, d17, d20
+  .long  0xf241ae03                          // vceq.f32      d26, d1, d3
+  .long  0xf247bd81                          // vadd.f32      d27, d23, d1
+  .long  0xf3b91505                          // vceq.f32      d1, d5, #0
+  .long  0xf240ce03                          // vceq.f32      d28, d0, d3
+  .long  0xf248dd80                          // vadd.f32      d29, d24, d0
+  .long  0xf3b90504                          // vceq.f32      d0, d4, #0
+  .long  0xf242ee03                          // vceq.f32      d30, d2, d3
+  .long  0xf249fd82                          // vadd.f32      d31, d25, d2
+  .long  0xf3b92506                          // vceq.f32      d2, d6, #0
+  .long  0xf2073c30                          // vfma.f32      d3, d7, d16
+  .long  0xf2410d84                          // vadd.f32      d16, d17, d4
+  .long  0xf2491da2                          // vadd.f32      d17, d25, d18
+  .long  0xf2462d85                          // vadd.f32      d18, d22, d5
+  .long  0xf2455d86                          // vadd.f32      d21, d21, d6
+  .long  0xf2473da3                          // vadd.f32      d19, d23, d19
+  .long  0xf2484da4                          // vadd.f32      d20, d24, d20
+  .long  0xf35fe1b1                          // vbsl          d30, d31, d17
+  .long  0xf35ba1b3                          // vbsl          d26, d27, d19
+  .long  0xf35dc1b4                          // vbsl          d28, d29, d20
+  .long  0xf31001bc                          // vbsl          d0, d16, d28
+  .long  0xf31211ba                          // vbsl          d1, d18, d26
+  .long  0xf31521be                          // vbsl          d2, d21, d30
+  .long  0xecbd8b0e                          // vpop          {d8-d14}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_hardlight_vfp4
+.globl _sk_hardlight_vfp4
+FUNCTION(_sk_hardlight_vfp4)
+_sk_hardlight_vfp4:
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2670d04                          // vsub.f32      d16, d7, d4
+  .long  0xf2617d87                          // vsub.f32      d23, d17, d7
+  .long  0xf2611d83                          // vsub.f32      d17, d17, d3
+  .long  0xf2672d05                          // vsub.f32      d18, d7, d5
+  .long  0xf2674d06                          // vsub.f32      d20, d7, d6
+  .long  0xf2633d00                          // vsub.f32      d19, d3, d0
+  .long  0xf2635d01                          // vsub.f32      d21, d3, d1
+  .long  0xf2636d02                          // vsub.f32      d22, d3, d2
+  .long  0xf347bd90                          // vmul.f32      d27, d23, d0
+  .long  0xf341cd94                          // vmul.f32      d28, d17, d4
+  .long  0xf3430db0                          // vmul.f32      d16, d19, d16
+  .long  0xf3463db4                          // vmul.f32      d19, d22, d20
+  .long  0xf3452db2                          // vmul.f32      d18, d21, d18
+  .long  0xf2404d00                          // vadd.f32      d20, d0, d0
+  .long  0xf3405d14                          // vmul.f32      d21, d0, d4
+  .long  0xf2416d01                          // vadd.f32      d22, d1, d1
+  .long  0xf3418d15                          // vmul.f32      d24, d1, d5
+  .long  0xf2429d02                          // vadd.f32      d25, d2, d2
+  .long  0xf342ad16                          // vmul.f32      d26, d2, d6
+  .long  0xf347dd91                          // vmul.f32      d29, d23, d1
+  .long  0xf341fd95                          // vmul.f32      d31, d17, d5
+  .long  0xf24cbdab                          // vadd.f32      d27, d28, d27
+  .long  0xf3477d92                          // vmul.f32      d23, d23, d2
+  .long  0xf341cd96                          // vmul.f32      d28, d17, d6
+  .long  0xf2400da0                          // vadd.f32      d16, d16, d16
+  .long  0xf343ed17                          // vmul.f32      d30, d3, d7
+  .long  0xf2422da2                          // vadd.f32      d18, d18, d18
+  .long  0xf2433da3                          // vadd.f32      d19, d19, d19
+  .long  0xf3434e24                          // vcge.f32      d20, d3, d20
+  .long  0xf2455da5                          // vadd.f32      d21, d21, d21
+  .long  0xf3436e26                          // vcge.f32      d22, d3, d22
+  .long  0xf3439e29                          // vcge.f32      d25, d3, d25
+  .long  0xf2488da8                          // vadd.f32      d24, d24, d24
+  .long  0xf24aadaa                          // vadd.f32      d26, d26, d26
+  .long  0xf2073c31                          // vfma.f32      d3, d7, d17
+  .long  0xf24fddad                          // vadd.f32      d29, d31, d29
+  .long  0xf24c1da7                          // vadd.f32      d17, d28, d23
+  .long  0xf26e0da0                          // vsub.f32      d16, d30, d16
+  .long  0xf26e2da2                          // vsub.f32      d18, d30, d18
+  .long  0xf26e3da3                          // vsub.f32      d19, d30, d19
+  .long  0xf35541b0                          // vbsl          d20, d21, d16
+  .long  0xf35861b2                          // vbsl          d22, d24, d18
+  .long  0xf35a91b3                          // vbsl          d25, d26, d19
+  .long  0xf20b0da4                          // vadd.f32      d0, d27, d20
+  .long  0xf20d1da6                          // vadd.f32      d1, d29, d22
+  .long  0xf2012da9                          // vadd.f32      d2, d17, d25
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_overlay_vfp4
+.globl _sk_overlay_vfp4
+FUNCTION(_sk_overlay_vfp4)
+_sk_overlay_vfp4:
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2670d04                          // vsub.f32      d16, d7, d4
+  .long  0xf2617d87                          // vsub.f32      d23, d17, d7
+  .long  0xf2611d83                          // vsub.f32      d17, d17, d3
+  .long  0xf2672d05                          // vsub.f32      d18, d7, d5
+  .long  0xf2674d06                          // vsub.f32      d20, d7, d6
+  .long  0xf2633d00                          // vsub.f32      d19, d3, d0
+  .long  0xf2635d01                          // vsub.f32      d21, d3, d1
+  .long  0xf2636d02                          // vsub.f32      d22, d3, d2
+  .long  0xf347bd90                          // vmul.f32      d27, d23, d0
+  .long  0xf341cd94                          // vmul.f32      d28, d17, d4
+  .long  0xf3430db0                          // vmul.f32      d16, d19, d16
+  .long  0xf3463db4                          // vmul.f32      d19, d22, d20
+  .long  0xf3452db2                          // vmul.f32      d18, d21, d18
+  .long  0xf2444d04                          // vadd.f32      d20, d4, d4
+  .long  0xf3405d14                          // vmul.f32      d21, d0, d4
+  .long  0xf2456d05                          // vadd.f32      d22, d5, d5
+  .long  0xf3418d15                          // vmul.f32      d24, d1, d5
+  .long  0xf2469d06                          // vadd.f32      d25, d6, d6
+  .long  0xf342ad16                          // vmul.f32      d26, d2, d6
+  .long  0xf347dd91                          // vmul.f32      d29, d23, d1
+  .long  0xf341fd95                          // vmul.f32      d31, d17, d5
+  .long  0xf24cbdab                          // vadd.f32      d27, d28, d27
+  .long  0xf3477d92                          // vmul.f32      d23, d23, d2
+  .long  0xf341cd96                          // vmul.f32      d28, d17, d6
+  .long  0xf343ed17                          // vmul.f32      d30, d3, d7
+  .long  0xf2400da0                          // vadd.f32      d16, d16, d16
+  .long  0xf2422da2                          // vadd.f32      d18, d18, d18
+  .long  0xf2433da3                          // vadd.f32      d19, d19, d19
+  .long  0xf3474e24                          // vcge.f32      d20, d7, d20
+  .long  0xf2455da5                          // vadd.f32      d21, d21, d21
+  .long  0xf3476e26                          // vcge.f32      d22, d7, d22
+  .long  0xf2488da8                          // vadd.f32      d24, d24, d24
+  .long  0xf3479e29                          // vcge.f32      d25, d7, d25
+  .long  0xf24aadaa                          // vadd.f32      d26, d26, d26
+  .long  0xf2073c31                          // vfma.f32      d3, d7, d17
+  .long  0xf24fddad                          // vadd.f32      d29, d31, d29
+  .long  0xf24c1da7                          // vadd.f32      d17, d28, d23
+  .long  0xf26e0da0                          // vsub.f32      d16, d30, d16
+  .long  0xf26e2da2                          // vsub.f32      d18, d30, d18
+  .long  0xf26e3da3                          // vsub.f32      d19, d30, d19
+  .long  0xf35541b0                          // vbsl          d20, d21, d16
+  .long  0xf35861b2                          // vbsl          d22, d24, d18
+  .long  0xf35a91b3                          // vbsl          d25, d26, d19
+  .long  0xf20b0da4                          // vadd.f32      d0, d27, d20
+  .long  0xf20d1da6                          // vadd.f32      d1, d29, d22
+  .long  0xf2012da9                          // vadd.f32      d2, d17, d25
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_softlight_vfp4
+.globl _sk_softlight_vfp4
+FUNCTION(_sk_softlight_vfp4)
+_sk_softlight_vfp4:
+  .long  0xed2d8b06                          // vpush         {d8-d10}
+  .long  0xeec58aa7                          // vdiv.f32      s17, s11, s15
+  .long  0xf3f90407                          // vcgt.f32      d16, d7, #0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeec49aa7                          // vdiv.f32      s19, s9, s15
+  .long  0xeec6aaa7                          // vdiv.f32      s21, s13, s15
+  .long  0xee858a07                          // vdiv.f32      s16, s10, s14
+  .long  0xee849a07                          // vdiv.f32      s18, s8, s14
+  .long  0xee86aa07                          // vdiv.f32      s20, s12, s14
+  .long  0xf26021b0                          // vorr          d18, d16, d16
+  .long  0xf2c01010                          // vmov.i32      d17, #0
+  .long  0xf3582131                          // vbsl          d18, d8, d17
+  .long  0xf26031b0                          // vorr          d19, d16, d16
+  .long  0xf3fb45a2                          // vrsqrte.f32   d20, d18
+  .long  0xf3593131                          // vbsl          d19, d9, d17
+  .long  0xf35a0131                          // vbsl          d16, d10, d17
+  .long  0xf3fb15a3                          // vrsqrte.f32   d17, d19
+  .long  0xf3fb55a0                          // vrsqrte.f32   d21, d16
+  .long  0xf3446db4                          // vmul.f32      d22, d20, d20
+  .long  0xf243ada3                          // vadd.f32      d26, d19, d19
+  .long  0xf240bda0                          // vadd.f32      d27, d16, d16
+  .long  0xf3417db1                          // vmul.f32      d23, d17, d17
+  .long  0xf3458db5                          // vmul.f32      d24, d21, d21
+  .long  0xf2626fb6                          // vrsqrts.f32   d22, d18, d22
+  .long  0xf2429da2                          // vadd.f32      d25, d18, d18
+  .long  0xf2637fb7                          // vrsqrts.f32   d23, d19, d23
+  .long  0xf2608fb8                          // vrsqrts.f32   d24, d16, d24
+  .long  0xf2818f1c                          // vmov.f32      d8, #7
+  .long  0xf2499da9                          // vadd.f32      d25, d25, d25
+  .long  0xf3444db6                          // vmul.f32      d20, d20, d22
+  .long  0xf24a6daa                          // vadd.f32      d22, d26, d26
+  .long  0xf24badab                          // vadd.f32      d26, d27, d27
+  .long  0xf3411db7                          // vmul.f32      d17, d17, d23
+  .long  0xf3455db8                          // vmul.f32      d21, d21, d24
+  .long  0xf3fb7524                          // vrecpe.f32    d23, d20
+  .long  0xf3498db9                          // vmul.f32      d24, d25, d25
+  .long  0xf3fbd521                          // vrecpe.f32    d29, d17
+  .long  0xf34aedba                          // vmul.f32      d30, d26, d26
+  .long  0xf3fbf525                          // vrecpe.f32    d31, d21
+  .long  0xf2444fb7                          // vrecps.f32    d20, d20, d23
+  .long  0xf346cdb6                          // vmul.f32      d28, d22, d22
+  .long  0xf2411fbd                          // vrecps.f32    d17, d17, d29
+  .long  0xf3c7bf10                          // vmov.f32      d27, #-1
+  .long  0xf2455fbf                          // vrecps.f32    d21, d21, d31
+  .long  0xf24aadae                          // vadd.f32      d26, d26, d30
+  .long  0xf2498da8                          // vadd.f32      d24, d25, d24
+  .long  0xf2429dab                          // vadd.f32      d25, d18, d27
+  .long  0xf2466dac                          // vadd.f32      d22, d22, d28
+  .long  0xf243cdab                          // vadd.f32      d28, d19, d27
+  .long  0xf240bdab                          // vadd.f32      d27, d16, d27
+  .long  0xf3474db4                          // vmul.f32      d20, d23, d20
+  .long  0xf2c7ef10                          // vmov.f32      d30, #1
+  .long  0xf34d1db1                          // vmul.f32      d17, d29, d17
+  .long  0xf34badba                          // vmul.f32      d26, d27, d26
+  .long  0xf242bd02                          // vadd.f32      d27, d2, d2
+  .long  0xf26edda0                          // vsub.f32      d29, d30, d16
+  .long  0xf3498db8                          // vmul.f32      d24, d25, d24
+  .long  0xf3429d98                          // vmul.f32      d25, d18, d8
+  .long  0xf34f5db5                          // vmul.f32      d21, d31, d21
+  .long  0xf26efda2                          // vsub.f32      d31, d30, d18
+  .long  0xf2642da2                          // vsub.f32      d18, d20, d18
+  .long  0xf26b4d83                          // vsub.f32      d20, d27, d3
+  .long  0xf2498da8                          // vadd.f32      d24, d25, d24
+  .long  0xf34c6db6                          // vmul.f32      d22, d28, d22
+  .long  0xf3437d98                          // vmul.f32      d23, d19, d8
+  .long  0xf3449dbd                          // vmul.f32      d25, d20, d29
+  .long  0xf245dd05                          // vadd.f32      d29, d5, d5
+  .long  0xf340cd98                          // vmul.f32      d28, d16, d8
+  .long  0xf2476da6                          // vadd.f32      d22, d23, d22
+  .long  0xf2611da3                          // vsub.f32      d17, d17, d19
+  .long  0xf24dddad                          // vadd.f32      d29, d29, d29
+  .long  0xf24c7daa                          // vadd.f32      d23, d28, d26
+  .long  0xf2650da0                          // vsub.f32      d16, d21, d16
+  .long  0xf26e3da3                          // vsub.f32      d19, d30, d19
+  .long  0xf347de2d                          // vcge.f32      d29, d7, d29
+  .long  0xf241ad01                          // vadd.f32      d26, d1, d1
+  .long  0xf3444d97                          // vmul.f32      d20, d20, d7
+  .long  0xf358d1b2                          // vbsl          d29, d24, d18
+  .long  0xf2448d04                          // vadd.f32      d24, d4, d4
+  .long  0xf2462d06                          // vadd.f32      d18, d6, d6
+  .long  0xf26a5d83                          // vsub.f32      d21, d26, d3
+  .long  0xf2488da8                          // vadd.f32      d24, d24, d24
+  .long  0xf2422da2                          // vadd.f32      d18, d18, d18
+  .long  0xf345cdbf                          // vmul.f32      d28, d21, d31
+  .long  0xf3455d97                          // vmul.f32      d21, d21, d7
+  .long  0xf3478e28                          // vcge.f32      d24, d7, d24
+  .long  0xf3472e22                          // vcge.f32      d18, d7, d18
+  .long  0xf343fd14                          // vmul.f32      d31, d3, d4
+  .long  0xf3455dbd                          // vmul.f32      d21, d21, d29
+  .long  0xf35681b1                          // vbsl          d24, d22, d17
+  .long  0xf2401d00                          // vadd.f32      d17, d0, d0
+  .long  0xf35721b0                          // vbsl          d18, d23, d16
+  .long  0xf24c0d83                          // vadd.f32      d16, d28, d3
+  .long  0xf2496d83                          // vadd.f32      d22, d25, d3
+  .long  0xf2617d83                          // vsub.f32      d23, d17, d3
+  .long  0xf3442db2                          // vmul.f32      d18, d20, d18
+  .long  0xf3434e2a                          // vcge.f32      d20, d3, d26
+  .long  0xf343ae2b                          // vcge.f32      d26, d3, d27
+  .long  0xf3473db3                          // vmul.f32      d19, d23, d19
+  .long  0xf3477d97                          // vmul.f32      d23, d23, d7
+  .long  0xf3431e21                          // vcge.f32      d17, d3, d17
+  .long  0xf3400d95                          // vmul.f32      d16, d16, d5
+  .long  0xf2433d83                          // vadd.f32      d19, d19, d3
+  .long  0xf3477db8                          // vmul.f32      d23, d23, d24
+  .long  0xf26e8d87                          // vsub.f32      d24, d30, d7
+  .long  0xf26eed83                          // vsub.f32      d30, d30, d3
+  .long  0xf3433d94                          // vmul.f32      d19, d19, d4
+  .long  0xf24f7da7                          // vadd.f32      d23, d31, d23
+  .long  0xf3489d91                          // vmul.f32      d25, d24, d1
+  .long  0xf348cd90                          // vmul.f32      d28, d24, d0
+  .long  0xf34edd94                          // vmul.f32      d29, d30, d4
+  .long  0xf34ebd95                          // vmul.f32      d27, d30, d5
+  .long  0xf3488d92                          // vmul.f32      d24, d24, d2
+  .long  0xf34efd96                          // vmul.f32      d31, d30, d6
+  .long  0xf24dcdac                          // vadd.f32      d28, d29, d28
+  .long  0xf343dd15                          // vmul.f32      d29, d3, d5
+  .long  0xf24b9da9                          // vadd.f32      d25, d27, d25
+  .long  0xf343bd16                          // vmul.f32      d27, d3, d6
+  .long  0xf3466d96                          // vmul.f32      d22, d22, d6
+  .long  0xf24f8da8                          // vadd.f32      d24, d31, d24
+  .long  0xf24d5da5                          // vadd.f32      d21, d29, d21
+  .long  0xf24b2da2                          // vadd.f32      d18, d27, d18
+  .long  0xf35311b7                          // vbsl          d17, d19, d23
+  .long  0xf35041b5                          // vbsl          d20, d16, d21
+  .long  0xf356a1b2                          // vbsl          d26, d22, d18
+  .long  0xf2073c3e                          // vfma.f32      d3, d7, d30
+  .long  0xf20c0da1                          // vadd.f32      d0, d28, d17
+  .long  0xf2091da4                          // vadd.f32      d1, d25, d20
+  .long  0xf2082daa                          // vadd.f32      d2, d24, d26
+  .long  0xecbd8b06                          // vpop          {d8-d10}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_hue_vfp4
+.globl _sk_hue_vfp4
+FUNCTION(_sk_hue_vfp4)
+_sk_hue_vfp4:
+  .long  0xed2d8b0c                          // vpush         {d8-d13}
+  .long  0xf3420d13                          // vmul.f32      d16, d2, d3
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3411d13                          // vmul.f32      d17, d1, d3
+  .long  0xf2652f06                          // vmin.f32      d18, d5, d6
+  .long  0xf2453f06                          // vmax.f32      d19, d5, d6
+  .long  0xf3404d13                          // vmul.f32      d20, d0, d3
+  .long  0xf2615fa0                          // vmin.f32      d21, d17, d16
+  .long  0xf2642f22                          // vmin.f32      d18, d4, d18
+  .long  0xf2443f23                          // vmax.f32      d19, d4, d19
+  .long  0xf2416fa0                          // vmax.f32      d22, d17, d16
+  .long  0xf2645fa5                          // vmin.f32      d21, d20, d21
+  .long  0xf2632da2                          // vsub.f32      d18, d19, d18
+  .long  0xf2443fa6                          // vmax.f32      d19, d20, d22
+  .long  0xeddf6b76                          // vldr          d22, [pc, #472]
+  .long  0xf2611da5                          // vsub.f32      d17, d17, d21
+  .long  0xf2644da5                          // vsub.f32      d20, d20, d21
+  .long  0xf3422d93                          // vmul.f32      d18, d18, d3
+  .long  0xf2238da5                          // vsub.f32      d8, d19, d21
+  .long  0xf2600da5                          // vsub.f32      d16, d16, d21
+  .long  0xf3f97508                          // vceq.f32      d23, d8, #0
+  .long  0xf26751b7                          // vorr          d21, d23, d23
+  .long  0xf3029db1                          // vmul.f32      d9, d18, d17
+  .long  0xeddf1b69                          // vldr          d17, [pc, #420]
+  .long  0xf302adb4                          // vmul.f32      d10, d18, d20
+  .long  0xf3453d31                          // vmul.f32      d19, d5, d17
+  .long  0xf302bdb0                          // vmul.f32      d11, d18, d16
+  .long  0xeddf2b67                          // vldr          d18, [pc, #412]
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xeec9caa8                          // vdiv.f32      s25, s19, s17
+  .long  0xee89ca08                          // vdiv.f32      s24, s18, s16
+  .long  0xeeca9aa8                          // vdiv.f32      s19, s21, s17
+  .long  0xee8a9a08                          // vdiv.f32      s18, s20, s16
+  .long  0xeecbaaa8                          // vdiv.f32      s21, s23, s17
+  .long  0xee8baa08                          // vdiv.f32      s20, s22, s16
+  .long  0xf3444d32                          // vmul.f32      d20, d4, d18
+  .long  0xf350519c                          // vbsl          d21, d16, d12
+  .long  0xf3468d36                          // vmul.f32      d24, d6, d22
+  .long  0xf3459db1                          // vmul.f32      d25, d21, d17
+  .long  0xf2443da3                          // vadd.f32      d19, d20, d19
+  .long  0xf26741b7                          // vorr          d20, d23, d23
+  .long  0xf350719a                          // vbsl          d23, d16, d10
+  .long  0xf3504199                          // vbsl          d20, d16, d9
+  .long  0xf2433da8                          // vadd.f32      d19, d19, d24
+  .long  0xf344adb2                          // vmul.f32      d26, d20, d18
+  .long  0xf3478db6                          // vmul.f32      d24, d23, d22
+  .long  0xf3433d93                          // vmul.f32      d19, d19, d3
+  .long  0xf24a9da9                          // vadd.f32      d25, d26, d25
+  .long  0xf2498da8                          // vadd.f32      d24, d25, d24
+  .long  0xf2633da8                          // vsub.f32      d19, d19, d24
+  .long  0xf2458da3                          // vadd.f32      d24, d21, d19
+  .long  0xf2445da3                          // vadd.f32      d21, d20, d19
+  .long  0xf2477da3                          // vadd.f32      d23, d23, d19
+  .long  0xf3481db1                          // vmul.f32      d17, d24, d17
+  .long  0xf3452db2                          // vmul.f32      d18, d21, d18
+  .long  0xf3473db6                          // vmul.f32      d19, d23, d22
+  .long  0xf2684fa7                          // vmin.f32      d20, d24, d23
+  .long  0xf2486fa7                          // vmax.f32      d22, d24, d23
+  .long  0xf2421da1                          // vadd.f32      d17, d18, d17
+  .long  0xf2456fa6                          // vmax.f32      d22, d21, d22
+  .long  0xf2432da1                          // vadd.f32      d18, d19, d17
+  .long  0xf2653fa4                          // vmin.f32      d19, d21, d20
+  .long  0xf3f944a3                          // vcge.f32      d20, d19, #0
+  .long  0xf264a1b4                          // vorr          d26, d20, d20
+  .long  0xf2671da2                          // vsub.f32      d17, d23, d18
+  .long  0xf2228da3                          // vsub.f32      d8, d18, d19
+  .long  0xf26431b4                          // vorr          d19, d20, d20
+  .long  0xf3029db1                          // vmul.f32      d9, d18, d17
+  .long  0xf3431d17                          // vmul.f32      d17, d3, d7
+  .long  0xeec9aaa8                          // vdiv.f32      s21, s19, s17
+  .long  0xee89aa08                          // vdiv.f32      s20, s18, s16
+  .long  0xf2429d8a                          // vadd.f32      d25, d18, d10
+  .long  0xf35731b9                          // vbsl          d19, d23, d25
+  .long  0xf2619da2                          // vsub.f32      d25, d17, d18
+  .long  0xf2637da2                          // vsub.f32      d23, d19, d18
+  .long  0xf226ada2                          // vsub.f32      d10, d22, d18
+  .long  0xf309bdb7                          // vmul.f32      d11, d25, d23
+  .long  0xf2687da2                          // vsub.f32      d23, d24, d18
+  .long  0xeecb9aaa                          // vdiv.f32      s19, s23, s21
+  .long  0xee8b9a0a                          // vdiv.f32      s18, s22, s20
+  .long  0xf302bdb7                          // vmul.f32      d11, d18, d23
+  .long  0xeecbcaa8                          // vdiv.f32      s25, s23, s17
+  .long  0xee8bca08                          // vdiv.f32      s24, s22, s16
+  .long  0xf2427d8c                          // vadd.f32      d23, d18, d12
+  .long  0xf358a1b7                          // vbsl          d26, d24, d23
+  .long  0xf26a7da2                          // vsub.f32      d23, d26, d18
+  .long  0xf309bdb7                          // vmul.f32      d11, d25, d23
+  .long  0xf2657da2                          // vsub.f32      d23, d21, d18
+  .long  0xeecbcaaa                          // vdiv.f32      s25, s23, s21
+  .long  0xee8bca0a                          // vdiv.f32      s24, s22, s20
+  .long  0xf302bdb7                          // vmul.f32      d11, d18, d23
+  .long  0xf2428d8c                          // vadd.f32      d24, d18, d12
+  .long  0xeecbdaa8                          // vdiv.f32      s27, s23, s17
+  .long  0xee8bda08                          // vdiv.f32      s26, s22, s16
+  .long  0xf2427d8d                          // vadd.f32      d23, d18, d13
+  .long  0xf35541b7                          // vbsl          d20, d21, d23
+  .long  0xf2c77f10                          // vmov.f32      d23, #1
+  .long  0xf2645da2                          // vsub.f32      d21, d20, d18
+  .long  0xf3098db5                          // vmul.f32      d8, d25, d21
+  .long  0xf3665ea1                          // vcgt.f32      d21, d22, d17
+  .long  0xf2679d87                          // vsub.f32      d25, d23, d7
+  .long  0xf2677d83                          // vsub.f32      d23, d23, d3
+  .long  0xeec8baaa                          // vdiv.f32      s23, s17, s21
+  .long  0xee88ba0a                          // vdiv.f32      s22, s16, s20
+  .long  0xf2426d8b                          // vadd.f32      d22, d18, d11
+  .long  0xf265f1b5                          // vorr          d31, d21, d21
+  .long  0xf2422d89                          // vadd.f32      d18, d18, d9
+  .long  0xf349bd90                          // vmul.f32      d27, d25, d0
+  .long  0xf356f1b4                          // vbsl          d31, d22, d20
+  .long  0xf347cd94                          // vmul.f32      d28, d23, d4
+  .long  0xf349dd91                          // vmul.f32      d29, d25, d1
+  .long  0xf3494d92                          // vmul.f32      d20, d25, d2
+  .long  0xf3476d96                          // vmul.f32      d22, d23, d6
+  .long  0xf347ed95                          // vmul.f32      d30, d23, d5
+  .long  0xf26571b5                          // vorr          d23, d21, d21
+  .long  0xf35251b3                          // vbsl          d21, d18, d19
+  .long  0xf35871ba                          // vbsl          d23, d24, d26
+  .long  0xf2438d07                          // vadd.f32      d24, d3, d7
+  .long  0xf24c9dab                          // vadd.f32      d25, d28, d27
+  .long  0xf24f2fa0                          // vmax.f32      d18, d31, d16
+  .long  0xf2477fa0                          // vmax.f32      d23, d23, d16
+  .long  0xf24e3dad                          // vadd.f32      d19, d30, d29
+  .long  0xf2464da4                          // vadd.f32      d20, d22, d20
+  .long  0xf2450fa0                          // vmax.f32      d16, d21, d16
+  .long  0xf2283da1                          // vsub.f32      d3, d24, d17
+  .long  0xf2090da2                          // vadd.f32      d0, d25, d18
+  .long  0xf2031da7                          // vadd.f32      d1, d19, d23
+  .long  0xf2042da0                          // vadd.f32      d2, d20, d16
+  .long  0xecbd8b0c                          // vpop          {d8-d13}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3de147ae                          // .word         0x3de147ae
+  .long  0x3de147ae                          // .word         0x3de147ae
+
+HIDDEN _sk_saturation_vfp4
+.globl _sk_saturation_vfp4
+FUNCTION(_sk_saturation_vfp4)
+_sk_saturation_vfp4:
+  .long  0xed2d8b0c                          // vpush         {d8-d13}
+  .long  0xf3430d16                          // vmul.f32      d16, d3, d6
+  .long  0xeddf7b82                          // vldr          d23, [pc, #520]
+  .long  0xf3431d15                          // vmul.f32      d17, d3, d5
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2612f02                          // vmin.f32      d18, d1, d2
+  .long  0xf2413f02                          // vmax.f32      d19, d1, d2
+  .long  0xf3434d14                          // vmul.f32      d20, d3, d4
+  .long  0xf2615fa0                          // vmin.f32      d21, d17, d16
+  .long  0xf2602f22                          // vmin.f32      d18, d0, d18
+  .long  0xf2403f23                          // vmax.f32      d19, d0, d19
+  .long  0xf2416fa0                          // vmax.f32      d22, d17, d16
+  .long  0xf2645fa5                          // vmin.f32      d21, d20, d21
+  .long  0xf3468d37                          // vmul.f32      d24, d6, d23
+  .long  0xf2632da2                          // vsub.f32      d18, d19, d18
+  .long  0xf2443fa6                          // vmax.f32      d19, d20, d22
+  .long  0xf2611da5                          // vsub.f32      d17, d17, d21
+  .long  0xf2644da5                          // vsub.f32      d20, d20, d21
+  .long  0xf3422d97                          // vmul.f32      d18, d18, d7
+  .long  0xf2238da5                          // vsub.f32      d8, d19, d21
+  .long  0xf2600da5                          // vsub.f32      d16, d16, d21
+  .long  0xf3f96508                          // vceq.f32      d22, d8, #0
+  .long  0xf26651b6                          // vorr          d21, d22, d22
+  .long  0xf3029db1                          // vmul.f32      d9, d18, d17
+  .long  0xeddf1b68                          // vldr          d17, [pc, #416]
+  .long  0xf302adb4                          // vmul.f32      d10, d18, d20
+  .long  0xf3453d31                          // vmul.f32      d19, d5, d17
+  .long  0xf302bdb0                          // vmul.f32      d11, d18, d16
+  .long  0xeddf2b66                          // vldr          d18, [pc, #408]
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xeec9caa8                          // vdiv.f32      s25, s19, s17
+  .long  0xee89ca08                          // vdiv.f32      s24, s18, s16
+  .long  0xeeca9aa8                          // vdiv.f32      s19, s21, s17
+  .long  0xee8a9a08                          // vdiv.f32      s18, s20, s16
+  .long  0xeecbaaa8                          // vdiv.f32      s21, s23, s17
+  .long  0xee8baa08                          // vdiv.f32      s20, s22, s16
+  .long  0xf3444d32                          // vmul.f32      d20, d4, d18
+  .long  0xf350519c                          // vbsl          d21, d16, d12
+  .long  0xf3459db1                          // vmul.f32      d25, d21, d17
+  .long  0xf2443da3                          // vadd.f32      d19, d20, d19
+  .long  0xf26641b6                          // vorr          d20, d22, d22
+  .long  0xf350619a                          // vbsl          d22, d16, d10
+  .long  0xf3504199                          // vbsl          d20, d16, d9
+  .long  0xf2433da8                          // vadd.f32      d19, d19, d24
+  .long  0xf344adb2                          // vmul.f32      d26, d20, d18
+  .long  0xf3468db7                          // vmul.f32      d24, d22, d23
+  .long  0xf3433d93                          // vmul.f32      d19, d19, d3
+  .long  0xf24a9da9                          // vadd.f32      d25, d26, d25
+  .long  0xf2498da8                          // vadd.f32      d24, d25, d24
+  .long  0xf2633da8                          // vsub.f32      d19, d19, d24
+  .long  0xf2458da3                          // vadd.f32      d24, d21, d19
+  .long  0xf2445da3                          // vadd.f32      d21, d20, d19
+  .long  0xf2466da3                          // vadd.f32      d22, d22, d19
+  .long  0xf3481db1                          // vmul.f32      d17, d24, d17
+  .long  0xf3452db2                          // vmul.f32      d18, d21, d18
+  .long  0xf3463db7                          // vmul.f32      d19, d22, d23
+  .long  0xf2684fa6                          // vmin.f32      d20, d24, d22
+  .long  0xf2487fa6                          // vmax.f32      d23, d24, d22
+  .long  0xf2421da1                          // vadd.f32      d17, d18, d17
+  .long  0xf2457fa7                          // vmax.f32      d23, d21, d23
+  .long  0xf2432da1                          // vadd.f32      d18, d19, d17
+  .long  0xf2653fa4                          // vmin.f32      d19, d21, d20
+  .long  0xf3f944a3                          // vcge.f32      d20, d19, #0
+  .long  0xf264a1b4                          // vorr          d26, d20, d20
+  .long  0xf2661da2                          // vsub.f32      d17, d22, d18
+  .long  0xf2228da3                          // vsub.f32      d8, d18, d19
+  .long  0xf26431b4                          // vorr          d19, d20, d20
+  .long  0xf3029db1                          // vmul.f32      d9, d18, d17
+  .long  0xf3431d17                          // vmul.f32      d17, d3, d7
+  .long  0xeec9aaa8                          // vdiv.f32      s21, s19, s17
+  .long  0xee89aa08                          // vdiv.f32      s20, s18, s16
+  .long  0xf2429d8a                          // vadd.f32      d25, d18, d10
+  .long  0xf35631b9                          // vbsl          d19, d22, d25
+  .long  0xf2619da2                          // vsub.f32      d25, d17, d18
+  .long  0xf2636da2                          // vsub.f32      d22, d19, d18
+  .long  0xf227ada2                          // vsub.f32      d10, d23, d18
+  .long  0xf309bdb6                          // vmul.f32      d11, d25, d22
+  .long  0xf2686da2                          // vsub.f32      d22, d24, d18
+  .long  0xeecb9aaa                          // vdiv.f32      s19, s23, s21
+  .long  0xee8b9a0a                          // vdiv.f32      s18, s22, s20
+  .long  0xf302bdb6                          // vmul.f32      d11, d18, d22
+  .long  0xeecbcaa8                          // vdiv.f32      s25, s23, s17
+  .long  0xee8bca08                          // vdiv.f32      s24, s22, s16
+  .long  0xf2426d8c                          // vadd.f32      d22, d18, d12
+  .long  0xf358a1b6                          // vbsl          d26, d24, d22
+  .long  0xf26a6da2                          // vsub.f32      d22, d26, d18
+  .long  0xf309bdb6                          // vmul.f32      d11, d25, d22
+  .long  0xf2656da2                          // vsub.f32      d22, d21, d18
+  .long  0xeecbcaaa                          // vdiv.f32      s25, s23, s21
+  .long  0xee8bca0a                          // vdiv.f32      s24, s22, s20
+  .long  0xf302bdb6                          // vmul.f32      d11, d18, d22
+  .long  0xf2428d8c                          // vadd.f32      d24, d18, d12
+  .long  0xeecbdaa8                          // vdiv.f32      s27, s23, s17
+  .long  0xee8bda08                          // vdiv.f32      s26, s22, s16
+  .long  0xf2426d8d                          // vadd.f32      d22, d18, d13
+  .long  0xf35541b6                          // vbsl          d20, d21, d22
+  .long  0xf2645da2                          // vsub.f32      d21, d20, d18
+  .long  0xf3098db5                          // vmul.f32      d8, d25, d21
+  .long  0xf3675ea1                          // vcgt.f32      d21, d23, d17
+  .long  0xf2c77f10                          // vmov.f32      d23, #1
+  .long  0xf2679d87                          // vsub.f32      d25, d23, d7
+  .long  0xeec8baaa                          // vdiv.f32      s23, s17, s21
+  .long  0xee88ba0a                          // vdiv.f32      s22, s16, s20
+  .long  0xf2677d83                          // vsub.f32      d23, d23, d3
+  .long  0xf2426d8b                          // vadd.f32      d22, d18, d11
+  .long  0xf265f1b5                          // vorr          d31, d21, d21
+  .long  0xf2422d89                          // vadd.f32      d18, d18, d9
+  .long  0xf349bd90                          // vmul.f32      d27, d25, d0
+  .long  0xf356f1b4                          // vbsl          d31, d22, d20
+  .long  0xf347cd94                          // vmul.f32      d28, d23, d4
+  .long  0xf349dd91                          // vmul.f32      d29, d25, d1
+  .long  0xf3494d92                          // vmul.f32      d20, d25, d2
+  .long  0xf3476d96                          // vmul.f32      d22, d23, d6
+  .long  0xf347ed95                          // vmul.f32      d30, d23, d5
+  .long  0xf26571b5                          // vorr          d23, d21, d21
+  .long  0xf35251b3                          // vbsl          d21, d18, d19
+  .long  0xf35871ba                          // vbsl          d23, d24, d26
+  .long  0xf2438d07                          // vadd.f32      d24, d3, d7
+  .long  0xf24c9dab                          // vadd.f32      d25, d28, d27
+  .long  0xf24f2fa0                          // vmax.f32      d18, d31, d16
+  .long  0xf2477fa0                          // vmax.f32      d23, d23, d16
+  .long  0xf24e3dad                          // vadd.f32      d19, d30, d29
+  .long  0xf2464da4                          // vadd.f32      d20, d22, d20
+  .long  0xf2450fa0                          // vmax.f32      d16, d21, d16
+  .long  0xf2283da1                          // vsub.f32      d3, d24, d17
+  .long  0xf2090da2                          // vadd.f32      d0, d25, d18
+  .long  0xf2031da7                          // vadd.f32      d1, d19, d23
+  .long  0xf2042da0                          // vadd.f32      d2, d20, d16
+  .long  0xecbd8b0c                          // vpop          {d8-d13}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3de147ae                          // .word         0x3de147ae
+  .long  0x3de147ae                          // .word         0x3de147ae
+
+HIDDEN _sk_color_vfp4
+.globl _sk_color_vfp4
+FUNCTION(_sk_color_vfp4)
+_sk_color_vfp4:
+  .long  0xed2d8b0e                          // vpush         {d8-d14}
+  .long  0xeddf0b63                          // vldr          d16, [pc, #396]
+  .long  0xf3412d17                          // vmul.f32      d18, d1, d7
+  .long  0xeddf3b63                          // vldr          d19, [pc, #396]
+  .long  0xf3401d17                          // vmul.f32      d17, d0, d7
+  .long  0xf3454d30                          // vmul.f32      d20, d5, d16
+  .long  0xeddf7b62                          // vldr          d23, [pc, #392]
+  .long  0xf3446d33                          // vmul.f32      d22, d4, d19
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3425d17                          // vmul.f32      d21, d2, d7
+  .long  0xf3428db0                          // vmul.f32      d24, d18, d16
+  .long  0xf3419db3                          // vmul.f32      d25, d17, d19
+  .long  0xf2464da4                          // vadd.f32      d20, d22, d20
+  .long  0xf346ad37                          // vmul.f32      d26, d6, d23
+  .long  0xf3456db7                          // vmul.f32      d22, d21, d23
+  .long  0xf2498da8                          // vadd.f32      d24, d25, d24
+  .long  0xf2c0e010                          // vmov.i32      d30, #0
+  .long  0xf2444daa                          // vadd.f32      d20, d20, d26
+  .long  0xf2466da8                          // vadd.f32      d22, d22, d24
+  .long  0xf3444d93                          // vmul.f32      d20, d20, d3
+  .long  0xf2646da6                          // vsub.f32      d22, d20, d22
+  .long  0xf2414da6                          // vadd.f32      d20, d17, d22
+  .long  0xf2428da6                          // vadd.f32      d24, d18, d22
+  .long  0xf2455da6                          // vadd.f32      d21, d21, d22
+  .long  0xf3441db3                          // vmul.f32      d17, d20, d19
+  .long  0xf3480db0                          // vmul.f32      d16, d24, d16
+  .long  0xf3452db7                          // vmul.f32      d18, d21, d23
+  .long  0xf2683fa5                          // vmin.f32      d19, d24, d21
+  .long  0xf2486fa5                          // vmax.f32      d22, d24, d21
+  .long  0xf2410da0                          // vadd.f32      d16, d17, d16
+  .long  0xf2421da0                          // vadd.f32      d17, d18, d16
+  .long  0xf2642fa3                          // vmin.f32      d18, d20, d19
+  .long  0xf3f934a2                          // vcge.f32      d19, d18, #0
+  .long  0xf26391b3                          // vorr          d25, d19, d19
+  .long  0xf2650da1                          // vsub.f32      d16, d21, d17
+  .long  0xf2219da2                          // vsub.f32      d9, d17, d18
+  .long  0xf26321b3                          // vorr          d18, d19, d19
+  .long  0xf3018db0                          // vmul.f32      d8, d17, d16
+  .long  0xf3430d17                          // vmul.f32      d16, d3, d7
+  .long  0xeec8aaa9                          // vdiv.f32      s21, s17, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf2417d8a                          // vadd.f32      d23, d17, d10
+  .long  0xf35521b7                          // vbsl          d18, d21, d23
+  .long  0xf2445fa6                          // vmax.f32      d21, d20, d22
+  .long  0xf2626da1                          // vsub.f32      d22, d18, d17
+  .long  0xf2607da1                          // vsub.f32      d23, d16, d17
+  .long  0xf225ada1                          // vsub.f32      d10, d21, d17
+  .long  0xf307bdb6                          // vmul.f32      d11, d23, d22
+  .long  0xf2686da1                          // vsub.f32      d22, d24, d17
+  .long  0xeecb8aaa                          // vdiv.f32      s17, s23, s21
+  .long  0xf301cdb6                          // vmul.f32      d12, d17, d22
+  .long  0xee8b8a0a                          // vdiv.f32      s16, s22, s20
+  .long  0xeeccdaa9                          // vdiv.f32      s27, s25, s19
+  .long  0xee8cda09                          // vdiv.f32      s26, s24, s18
+  .long  0xf2416d8d                          // vadd.f32      d22, d17, d13
+  .long  0xf35891b6                          // vbsl          d25, d24, d22
+  .long  0xf2696da1                          // vsub.f32      d22, d25, d17
+  .long  0xf307ddb6                          // vmul.f32      d13, d23, d22
+  .long  0xf2646da1                          // vsub.f32      d22, d20, d17
+  .long  0xeecdcaaa                          // vdiv.f32      s25, s27, s21
+  .long  0xee8dca0a                          // vdiv.f32      s24, s26, s20
+  .long  0xf301ddb6                          // vmul.f32      d13, d17, d22
+  .long  0xf2418d8c                          // vadd.f32      d24, d17, d12
+  .long  0xeecdeaa9                          // vdiv.f32      s29, s27, s19
+  .long  0xee8dea09                          // vdiv.f32      s28, s26, s18
+  .long  0xf2416d8e                          // vadd.f32      d22, d17, d14
+  .long  0xf35431b6                          // vbsl          d19, d20, d22
+  .long  0xf2634da1                          // vsub.f32      d20, d19, d17
+  .long  0xf3079db4                          // vmul.f32      d9, d23, d20
+  .long  0xf3654ea0                          // vcgt.f32      d20, d21, d16
+  .long  0xf2c75f10                          // vmov.f32      d21, #1
+  .long  0xf2657d83                          // vsub.f32      d23, d21, d3
+  .long  0xeec9daaa                          // vdiv.f32      s27, s19, s21
+  .long  0xee89da0a                          // vdiv.f32      s26, s18, s20
+  .long  0xf2655d87                          // vsub.f32      d21, d21, d7
+  .long  0xf2416d8d                          // vadd.f32      d22, d17, d13
+  .long  0xf264f1b4                          // vorr          d31, d20, d20
+  .long  0xf2411d88                          // vadd.f32      d17, d17, d8
+  .long  0xf345ad90                          // vmul.f32      d26, d21, d0
+  .long  0xf356f1b3                          // vbsl          d31, d22, d19
+  .long  0xf26461b4                          // vorr          d22, d20, d20
+  .long  0xf347bd94                          // vmul.f32      d27, d23, d4
+  .long  0xf345cd91                          // vmul.f32      d28, d21, d1
+  .long  0xf3453d92                          // vmul.f32      d19, d21, d2
+  .long  0xf3475d96                          // vmul.f32      d21, d23, d6
+  .long  0xf347dd95                          // vmul.f32      d29, d23, d5
+  .long  0xf35141b2                          // vbsl          d20, d17, d18
+  .long  0xf35861b9                          // vbsl          d22, d24, d25
+  .long  0xf2437d07                          // vadd.f32      d23, d3, d7
+  .long  0xf24b8daa                          // vadd.f32      d24, d27, d26
+  .long  0xf24f1fae                          // vmax.f32      d17, d31, d30
+  .long  0xf24d2dac                          // vadd.f32      d18, d29, d28
+  .long  0xf2466fae                          // vmax.f32      d22, d22, d30
+  .long  0xf2453da3                          // vadd.f32      d19, d21, d19
+  .long  0xf2444fae                          // vmax.f32      d20, d20, d30
+  .long  0xf2273da0                          // vsub.f32      d3, d23, d16
+  .long  0xf2080da1                          // vadd.f32      d0, d24, d17
+  .long  0xf2021da6                          // vadd.f32      d1, d18, d22
+  .long  0xf2032da4                          // vadd.f32      d2, d19, d20
+  .long  0xecbd8b0e                          // vpop          {d8-d14}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3de147ae                          // .word         0x3de147ae
+  .long  0x3de147ae                          // .word         0x3de147ae
+
+HIDDEN _sk_luminosity_vfp4
+.globl _sk_luminosity_vfp4
+FUNCTION(_sk_luminosity_vfp4)
+_sk_luminosity_vfp4:
+  .long  0xed2d8b0e                          // vpush         {d8-d14}
+  .long  0xeddf0b63                          // vldr          d16, [pc, #396]
+  .long  0xf3432d15                          // vmul.f32      d18, d3, d5
+  .long  0xeddf3b63                          // vldr          d19, [pc, #396]
+  .long  0xf3431d14                          // vmul.f32      d17, d3, d4
+  .long  0xf3414d30                          // vmul.f32      d20, d1, d16
+  .long  0xeddf7b62                          // vldr          d23, [pc, #392]
+  .long  0xf3406d33                          // vmul.f32      d22, d0, d19
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3435d16                          // vmul.f32      d21, d3, d6
+  .long  0xf3428db0                          // vmul.f32      d24, d18, d16
+  .long  0xf3419db3                          // vmul.f32      d25, d17, d19
+  .long  0xf2464da4                          // vadd.f32      d20, d22, d20
+  .long  0xf342ad37                          // vmul.f32      d26, d2, d23
+  .long  0xf3456db7                          // vmul.f32      d22, d21, d23
+  .long  0xf2498da8                          // vadd.f32      d24, d25, d24
+  .long  0xf2c0e010                          // vmov.i32      d30, #0
+  .long  0xf2444daa                          // vadd.f32      d20, d20, d26
+  .long  0xf2486da6                          // vadd.f32      d22, d24, d22
+  .long  0xf3444d97                          // vmul.f32      d20, d20, d7
+  .long  0xf2646da6                          // vsub.f32      d22, d20, d22
+  .long  0xf2414da6                          // vadd.f32      d20, d17, d22
+  .long  0xf2428da6                          // vadd.f32      d24, d18, d22
+  .long  0xf2455da6                          // vadd.f32      d21, d21, d22
+  .long  0xf3441db3                          // vmul.f32      d17, d20, d19
+  .long  0xf3480db0                          // vmul.f32      d16, d24, d16
+  .long  0xf3452db7                          // vmul.f32      d18, d21, d23
+  .long  0xf2683fa5                          // vmin.f32      d19, d24, d21
+  .long  0xf2486fa5                          // vmax.f32      d22, d24, d21
+  .long  0xf2410da0                          // vadd.f32      d16, d17, d16
+  .long  0xf2421da0                          // vadd.f32      d17, d18, d16
+  .long  0xf2642fa3                          // vmin.f32      d18, d20, d19
+  .long  0xf3f934a2                          // vcge.f32      d19, d18, #0
+  .long  0xf26391b3                          // vorr          d25, d19, d19
+  .long  0xf2650da1                          // vsub.f32      d16, d21, d17
+  .long  0xf2219da2                          // vsub.f32      d9, d17, d18
+  .long  0xf26321b3                          // vorr          d18, d19, d19
+  .long  0xf3018db0                          // vmul.f32      d8, d17, d16
+  .long  0xf3430d17                          // vmul.f32      d16, d3, d7
+  .long  0xeec8aaa9                          // vdiv.f32      s21, s17, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf2417d8a                          // vadd.f32      d23, d17, d10
+  .long  0xf35521b7                          // vbsl          d18, d21, d23
+  .long  0xf2445fa6                          // vmax.f32      d21, d20, d22
+  .long  0xf2626da1                          // vsub.f32      d22, d18, d17
+  .long  0xf2607da1                          // vsub.f32      d23, d16, d17
+  .long  0xf225ada1                          // vsub.f32      d10, d21, d17
+  .long  0xf307bdb6                          // vmul.f32      d11, d23, d22
+  .long  0xf2686da1                          // vsub.f32      d22, d24, d17
+  .long  0xeecb8aaa                          // vdiv.f32      s17, s23, s21
+  .long  0xf301cdb6                          // vmul.f32      d12, d17, d22
+  .long  0xee8b8a0a                          // vdiv.f32      s16, s22, s20
+  .long  0xeeccdaa9                          // vdiv.f32      s27, s25, s19
+  .long  0xee8cda09                          // vdiv.f32      s26, s24, s18
+  .long  0xf2416d8d                          // vadd.f32      d22, d17, d13
+  .long  0xf35891b6                          // vbsl          d25, d24, d22
+  .long  0xf2696da1                          // vsub.f32      d22, d25, d17
+  .long  0xf307ddb6                          // vmul.f32      d13, d23, d22
+  .long  0xf2646da1                          // vsub.f32      d22, d20, d17
+  .long  0xeecdcaaa                          // vdiv.f32      s25, s27, s21
+  .long  0xee8dca0a                          // vdiv.f32      s24, s26, s20
+  .long  0xf301ddb6                          // vmul.f32      d13, d17, d22
+  .long  0xf2418d8c                          // vadd.f32      d24, d17, d12
+  .long  0xeecdeaa9                          // vdiv.f32      s29, s27, s19
+  .long  0xee8dea09                          // vdiv.f32      s28, s26, s18
+  .long  0xf2416d8e                          // vadd.f32      d22, d17, d14
+  .long  0xf35431b6                          // vbsl          d19, d20, d22
+  .long  0xf2634da1                          // vsub.f32      d20, d19, d17
+  .long  0xf3079db4                          // vmul.f32      d9, d23, d20
+  .long  0xf3654ea0                          // vcgt.f32      d20, d21, d16
+  .long  0xf2c75f10                          // vmov.f32      d21, #1
+  .long  0xf2657d83                          // vsub.f32      d23, d21, d3
+  .long  0xeec9daaa                          // vdiv.f32      s27, s19, s21
+  .long  0xee89da0a                          // vdiv.f32      s26, s18, s20
+  .long  0xf2655d87                          // vsub.f32      d21, d21, d7
+  .long  0xf2416d8d                          // vadd.f32      d22, d17, d13
+  .long  0xf264f1b4                          // vorr          d31, d20, d20
+  .long  0xf2411d88                          // vadd.f32      d17, d17, d8
+  .long  0xf345ad90                          // vmul.f32      d26, d21, d0
+  .long  0xf356f1b3                          // vbsl          d31, d22, d19
+  .long  0xf26461b4                          // vorr          d22, d20, d20
+  .long  0xf347bd94                          // vmul.f32      d27, d23, d4
+  .long  0xf345cd91                          // vmul.f32      d28, d21, d1
+  .long  0xf3453d92                          // vmul.f32      d19, d21, d2
+  .long  0xf3475d96                          // vmul.f32      d21, d23, d6
+  .long  0xf347dd95                          // vmul.f32      d29, d23, d5
+  .long  0xf35141b2                          // vbsl          d20, d17, d18
+  .long  0xf35861b9                          // vbsl          d22, d24, d25
+  .long  0xf2437d07                          // vadd.f32      d23, d3, d7
+  .long  0xf24b8daa                          // vadd.f32      d24, d27, d26
+  .long  0xf24f1fae                          // vmax.f32      d17, d31, d30
+  .long  0xf24d2dac                          // vadd.f32      d18, d29, d28
+  .long  0xf2466fae                          // vmax.f32      d22, d22, d30
+  .long  0xf2453da3                          // vadd.f32      d19, d21, d19
+  .long  0xf2444fae                          // vmax.f32      d20, d20, d30
+  .long  0xf2273da0                          // vsub.f32      d3, d23, d16
+  .long  0xf2080da1                          // vadd.f32      d0, d24, d17
+  .long  0xf2021da6                          // vadd.f32      d1, d18, d22
+  .long  0xf2032da4                          // vadd.f32      d2, d19, d20
+  .long  0xecbd8b0e                          // vpop          {d8-d14}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3f170a3d                          // .word         0x3f170a3d
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3de147ae                          // .word         0x3de147ae
+  .long  0x3de147ae                          // .word         0x3de147ae
+
+HIDDEN _sk_srcover_rgba_8888_vfp4
+.globl _sk_srcover_rgba_8888_vfp4
+FUNCTION(_sk_srcover_rgba_8888_vfp4)
+_sk_srcover_rgba_8888_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc102                          // add           ip, ip, r2, lsl #2
+  .long  0x0a00002c                          // beq           1408 <sk_srcover_rgba_8888_vfp4+0xd0>
+  .long  0xed9c4b00                          // vldr          d4, [ip]
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xeddf6b2f                          // vldr          d22, [pc, #188]
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xf3f82014                          // vshr.u32      d18, d4, #8
+  .long  0xf3e84014                          // vshr.u32      d20, d4, #24
+  .long  0xf2611d83                          // vsub.f32      d17, d17, d3
+  .long  0xf24221b0                          // vand          d18, d18, d16
+  .long  0xf3f03014                          // vshr.u32      d19, d4, #16
+  .long  0xf3bb7624                          // vcvt.f32.s32  d7, d20
+  .long  0xf2445130                          // vand          d21, d4, d16
+  .long  0xf3033d36                          // vmul.f32      d3, d3, d22
+  .long  0xf24301b0                          // vand          d16, d19, d16
+  .long  0xf3bb5622                          // vcvt.f32.s32  d5, d18
+  .long  0xf3011d36                          // vmul.f32      d1, d1, d22
+  .long  0xf3bb4625                          // vcvt.f32.s32  d4, d21
+  .long  0xf3000d36                          // vmul.f32      d0, d0, d22
+  .long  0xf3bb6620                          // vcvt.f32.s32  d6, d16
+  .long  0xf2073c31                          // vfma.f32      d3, d7, d17
+  .long  0xf3022d36                          // vmul.f32      d2, d2, d22
+  .long  0xf2051c31                          // vfma.f32      d1, d5, d17
+  .long  0xf2040c31                          // vfma.f32      d0, d4, d17
+  .long  0xf2062c31                          // vfma.f32      d2, d6, d17
+  .long  0xf2c3061f                          // vmov.i32      d16, #1056964608
+  .long  0xf2431d20                          // vadd.f32      d17, d3, d16
+  .long  0xf2413d20                          // vadd.f32      d19, d1, d16
+  .long  0xf2402d20                          // vadd.f32      d18, d0, d16
+  .long  0xf2420d20                          // vadd.f32      d16, d2, d16
+  .long  0xf3fb17a1                          // vcvt.u32.f32  d17, d17
+  .long  0xf3fb37a3                          // vcvt.u32.f32  d19, d19
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf3fb07a0                          // vcvt.u32.f32  d16, d16
+  .long  0xf2f81531                          // vshl.s32      d17, d17, #24
+  .long  0xf26111b2                          // vorr          d17, d17, d18
+  .long  0xf2e82533                          // vshl.s32      d18, d19, #8
+  .long  0xf2f00530                          // vshl.s32      d16, d16, #16
+  .long  0xf26111b2                          // vorr          d17, d17, d18
+  .long  0xf26101b0                          // vorr          d16, d17, d16
+  .long  0x0a000007                          // beq           1414 <sk_srcover_rgba_8888_vfp4+0xdc>
+  .long  0xedcc0b00                          // vstr          d16, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xeddf4a06                          // vldr          s9, [pc, #24]
+  .long  0xed9c4a00                          // vldr          s8, [ip]
+  .long  0xeaffffd0                          // b             1358 <sk_srcover_rgba_8888_vfp4+0x20>
+  .long  0xf4cc083f                          // vst1.32       {d16[0]}, [ip :32]
+  .long  0xeafffff6                          // b             13f8 <sk_srcover_rgba_8888_vfp4+0xc0>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x00000000                          // .word         0x00000000
+
+HIDDEN _sk_clamp_0_vfp4
+.globl _sk_clamp_0_vfp4
+FUNCTION(_sk_clamp_0_vfp4)
+_sk_clamp_0_vfp4:
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2000f20                          // vmax.f32      d0, d0, d16
+  .long  0xf2011f20                          // vmax.f32      d1, d1, d16
+  .long  0xf2022f20                          // vmax.f32      d2, d2, d16
+  .long  0xf2033f20                          // vmax.f32      d3, d3, d16
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_clamp_1_vfp4
+.globl _sk_clamp_1_vfp4
+FUNCTION(_sk_clamp_1_vfp4)
+_sk_clamp_1_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2200f20                          // vmin.f32      d0, d0, d16
+  .long  0xf2211f20                          // vmin.f32      d1, d1, d16
+  .long  0xf2222f20                          // vmin.f32      d2, d2, d16
+  .long  0xf2233f20                          // vmin.f32      d3, d3, d16
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_clamp_a_vfp4
+.globl _sk_clamp_a_vfp4
+FUNCTION(_sk_clamp_a_vfp4)
+_sk_clamp_a_vfp4:
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2233f20                          // vmin.f32      d3, d3, d16
+  .long  0xf2200f03                          // vmin.f32      d0, d0, d3
+  .long  0xf2211f03                          // vmin.f32      d1, d1, d3
+  .long  0xf2222f03                          // vmin.f32      d2, d2, d3
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_set_rgb_vfp4
+.globl _sk_set_rgb_vfp4
+FUNCTION(_sk_set_rgb_vfp4)
+_sk_set_rgb_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4ae0c9d                          // vld1.32       {d0[]}, [lr :32]!
+  .long  0xf4a42c9f                          // vld1.32       {d2[]}, [r4 :32]
+  .long  0xf4ae1c9f                          // vld1.32       {d1[]}, [lr :32]
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_swap_rb_vfp4
+.globl _sk_swap_rb_vfp4
+FUNCTION(_sk_swap_rb_vfp4)
+_sk_swap_rb_vfp4:
+  .long  0xeef00b40                          // vmov.f64      d16, d0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeeb00b42                          // vmov.f64      d0, d2
+  .long  0xeeb02b60                          // vmov.f64      d2, d16
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_swap_vfp4
+.globl _sk_swap_vfp4
+FUNCTION(_sk_swap_vfp4)
+_sk_swap_vfp4:
+  .long  0xeef00b43                          // vmov.f64      d16, d3
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeef01b42                          // vmov.f64      d17, d2
+  .long  0xeef02b41                          // vmov.f64      d18, d1
+  .long  0xeef03b40                          // vmov.f64      d19, d0
+  .long  0xeeb00b44                          // vmov.f64      d0, d4
+  .long  0xeeb01b45                          // vmov.f64      d1, d5
+  .long  0xeeb02b46                          // vmov.f64      d2, d6
+  .long  0xeeb03b47                          // vmov.f64      d3, d7
+  .long  0xeeb04b63                          // vmov.f64      d4, d19
+  .long  0xeeb05b62                          // vmov.f64      d5, d18
+  .long  0xeeb06b61                          // vmov.f64      d6, d17
+  .long  0xeeb07b60                          // vmov.f64      d7, d16
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_move_src_dst_vfp4
+.globl _sk_move_src_dst_vfp4
+FUNCTION(_sk_move_src_dst_vfp4)
+_sk_move_src_dst_vfp4:
+  .long  0xeeb04b40                          // vmov.f64      d4, d0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeeb05b41                          // vmov.f64      d5, d1
+  .long  0xeeb06b42                          // vmov.f64      d6, d2
+  .long  0xeeb07b43                          // vmov.f64      d7, d3
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_move_dst_src_vfp4
+.globl _sk_move_dst_src_vfp4
+FUNCTION(_sk_move_dst_src_vfp4)
+_sk_move_dst_src_vfp4:
+  .long  0xeeb00b44                          // vmov.f64      d0, d4
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeeb01b45                          // vmov.f64      d1, d5
+  .long  0xeeb02b46                          // vmov.f64      d2, d6
+  .long  0xeeb03b47                          // vmov.f64      d3, d7
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_premul_vfp4
+.globl _sk_premul_vfp4
+FUNCTION(_sk_premul_vfp4)
+_sk_premul_vfp4:
+  .long  0xf3000d13                          // vmul.f32      d0, d0, d3
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3011d13                          // vmul.f32      d1, d1, d3
+  .long  0xf3022d13                          // vmul.f32      d2, d2, d3
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_unpremul_vfp4
+.globl _sk_unpremul_vfp4
+FUNCTION(_sk_unpremul_vfp4)
+_sk_unpremul_vfp4:
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xeeb78a00                          // vmov.f32      s16, #112
+  .long  0xf3f91503                          // vceq.f32      d17, d3, #0
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeec89a23                          // vdiv.f32      s19, s16, s7
+  .long  0xee889a03                          // vdiv.f32      s18, s16, s6
+  .long  0xf3501199                          // vbsl          d17, d16, d9
+  .long  0xf3010d90                          // vmul.f32      d0, d17, d0
+  .long  0xf3011d91                          // vmul.f32      d1, d17, d1
+  .long  0xf3012d92                          // vmul.f32      d2, d17, d2
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_from_srgb_vfp4
+.globl _sk_from_srgb_vfp4
+FUNCTION(_sk_from_srgb_vfp4)
+_sk_from_srgb_vfp4:
+  .long  0xeddf3b20                          // vldr          d19, [pc, #128]
+  .long  0xf3408d10                          // vmul.f32      d24, d0, d0
+  .long  0xeddf0b1c                          // vldr          d16, [pc, #112]
+  .long  0xf26341b3                          // vorr          d20, d19, d19
+  .long  0xf26351b3                          // vorr          d21, d19, d19
+  .long  0xeddf9b1f                          // vldr          d25, [pc, #124]
+  .long  0xf2404c30                          // vfma.f32      d20, d0, d16
+  .long  0xeddf2b1b                          // vldr          d18, [pc, #108]
+  .long  0xf2415c30                          // vfma.f32      d21, d1, d16
+  .long  0xeddfcb1d                          // vldr          d28, [pc, #116]
+  .long  0xf2423c30                          // vfma.f32      d19, d2, d16
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3426d12                          // vmul.f32      d22, d2, d2
+  .long  0xf3417d11                          // vmul.f32      d23, d1, d1
+  .long  0xf3620e80                          // vcgt.f32      d16, d18, d0
+  .long  0xf3621e81                          // vcgt.f32      d17, d18, d1
+  .long  0xf341ad39                          // vmul.f32      d26, d1, d25
+  .long  0xf342bd39                          // vmul.f32      d27, d2, d25
+  .long  0xf3622e82                          // vcgt.f32      d18, d18, d2
+  .long  0xf3409d39                          // vmul.f32      d25, d0, d25
+  .long  0xf26cd1bc                          // vorr          d29, d28, d28
+  .long  0xf248dcb4                          // vfma.f32      d29, d24, d20
+  .long  0xf26c41bc                          // vorr          d20, d28, d28
+  .long  0xf2474cb5                          // vfma.f32      d20, d23, d21
+  .long  0xf246ccb3                          // vfma.f32      d28, d22, d19
+  .long  0xf35901bd                          // vbsl          d16, d25, d29
+  .long  0xf35a11b4                          // vbsl          d17, d26, d20
+  .long  0xf35b21bc                          // vbsl          d18, d27, d28
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3e99999a                          // .word         0x3e99999a
+  .long  0x3f328f5c                          // .word         0x3f328f5c
+  .long  0x3f328f5c                          // .word         0x3f328f5c
+  .long  0x3d6147ae                          // .word         0x3d6147ae
+  .long  0x3d6147ae                          // .word         0x3d6147ae
+  .long  0x3d9e8391                          // .word         0x3d9e8391
+  .long  0x3d9e8391                          // .word         0x3d9e8391
+  .long  0x3b23d70a                          // .word         0x3b23d70a
+  .long  0x3b23d70a                          // .word         0x3b23d70a
+
+HIDDEN _sk_to_srgb_vfp4
+.globl _sk_to_srgb_vfp4
+FUNCTION(_sk_to_srgb_vfp4)
+_sk_to_srgb_vfp4:
+  .long  0xf3fb0580                          // vrsqrte.f32   d16, d0
+  .long  0xeddf9b3b                          // vldr          d25, [pc, #236]
+  .long  0xf3fb1581                          // vrsqrte.f32   d17, d1
+  .long  0xeddf7b37                          // vldr          d23, [pc, #220]
+  .long  0xf3fb2582                          // vrsqrte.f32   d18, d2
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf269a1b9                          // vorr          d26, d25, d25
+  .long  0xf269c1b9                          // vorr          d28, d25, d25
+  .long  0xf3403db0                          // vmul.f32      d19, d16, d16
+  .long  0xf3414db1                          // vmul.f32      d20, d17, d17
+  .long  0xf3425db2                          // vmul.f32      d21, d18, d18
+  .long  0xf2603f33                          // vrsqrts.f32   d19, d0, d19
+  .long  0xf2614f34                          // vrsqrts.f32   d20, d1, d20
+  .long  0xf2625f35                          // vrsqrts.f32   d21, d2, d21
+  .long  0xf3400db3                          // vmul.f32      d16, d16, d19
+  .long  0xeddf3b29                          // vldr          d19, [pc, #164]
+  .long  0xf3411db4                          // vmul.f32      d17, d17, d20
+  .long  0xf3422db5                          // vmul.f32      d18, d18, d21
+  .long  0xf2404da3                          // vadd.f32      d20, d16, d19
+  .long  0xf2415da3                          // vadd.f32      d21, d17, d19
+  .long  0xf2423da3                          // vadd.f32      d19, d18, d19
+  .long  0xf240acb7                          // vfma.f32      d26, d16, d23
+  .long  0xf3fb6524                          // vrecpe.f32    d22, d20
+  .long  0xf3fb8525                          // vrecpe.f32    d24, d21
+  .long  0xf3fbb523                          // vrecpe.f32    d27, d19
+  .long  0xf241ccb7                          // vfma.f32      d28, d17, d23
+  .long  0xf2429cb7                          // vfma.f32      d25, d18, d23
+  .long  0xeddf7b23                          // vldr          d23, [pc, #140]
+  .long  0xf2455fb8                          // vrecps.f32    d21, d21, d24
+  .long  0xf2444fb6                          // vrecps.f32    d20, d20, d22
+  .long  0xf2433fbb                          // vrecps.f32    d19, d19, d27
+  .long  0xf267d1b7                          // vorr          d29, d23, d23
+  .long  0xf240dcba                          // vfma.f32      d29, d16, d26
+  .long  0xf267a1b7                          // vorr          d26, d23, d23
+  .long  0xf241acbc                          // vfma.f32      d26, d17, d28
+  .long  0xf2427cb9                          // vfma.f32      d23, d18, d25
+  .long  0xeddf2b1e                          // vldr          d18, [pc, #120]
+  .long  0xf3620e80                          // vcgt.f32      d16, d18, d0
+  .long  0xf3485db5                          // vmul.f32      d21, d24, d21
+  .long  0xeddf8b19                          // vldr          d24, [pc, #100]
+  .long  0xf3464db4                          // vmul.f32      d20, d22, d20
+  .long  0xf34b3db3                          // vmul.f32      d19, d27, d19
+  .long  0xf3621e81                          // vcgt.f32      d17, d18, d1
+  .long  0xf3406d38                          // vmul.f32      d22, d0, d24
+  .long  0xf3419d38                          // vmul.f32      d25, d1, d24
+  .long  0xf3622e82                          // vcgt.f32      d18, d18, d2
+  .long  0xf3428d38                          // vmul.f32      d24, d2, d24
+  .long  0xf34d4db4                          // vmul.f32      d20, d29, d20
+  .long  0xf34a5db5                          // vmul.f32      d21, d26, d21
+  .long  0xf3473db3                          // vmul.f32      d19, d23, d19
+  .long  0xf35601b4                          // vbsl          d16, d22, d20
+  .long  0xf35911b5                          // vbsl          d17, d25, d21
+  .long  0xf35821b3                          // vbsl          d18, d24, d19
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22221b2                          // vorr          d2, d18, d18
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3e10c64c                          // .word         0x3e10c64c
+  .long  0x3e10c64c                          // .word         0x3e10c64c
+  .long  0xbb20d739                          // .word         0xbb20d739
+  .long  0xbb20d739                          // .word         0xbb20d739
+  .long  0x3c629fba                          // .word         0x3c629fba
+  .long  0x3c629fba                          // .word         0x3c629fba
+  .long  0x3f90a3d7                          // .word         0x3f90a3d7
+  .long  0x3f90a3d7                          // .word         0x3f90a3d7
+  .long  0x414eb852                          // .word         0x414eb852
+  .long  0x414eb852                          // .word         0x414eb852
+  .long  0x3b98b1a8                          // .word         0x3b98b1a8
+  .long  0x3b98b1a8                          // .word         0x3b98b1a8
+
+HIDDEN _sk_rgb_to_hsl_vfp4
+.globl _sk_rgb_to_hsl_vfp4
+FUNCTION(_sk_rgb_to_hsl_vfp4)
+_sk_rgb_to_hsl_vfp4:
+  .long  0xed2d8b08                          // vpush         {d8-d11}
+  .long  0xf2401f01                          // vmax.f32      d17, d0, d1
+  .long  0xeddf9b2c                          // vldr          d25, [pc, #176]
+  .long  0xf2600f01                          // vmin.f32      d16, d0, d1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xeeb78a00                          // vmov.f32      s16, #112
+  .long  0xf2c3461f                          // vmov.i32      d20, #1056964608
+  .long  0xf2411f82                          // vmax.f32      d17, d17, d2
+  .long  0xf2602f82                          // vmin.f32      d18, d16, d2
+  .long  0xf2c45610                          // vmov.i32      d21, #1073741824
+  .long  0xf2607d01                          // vsub.f32      d23, d0, d1
+  .long  0xf2656da1                          // vsub.f32      d22, d21, d17
+  .long  0xf221ada2                          // vsub.f32      d10, d17, d18
+  .long  0xf2413da2                          // vadd.f32      d19, d17, d18
+  .long  0xf2c08010                          // vmov.i32      d24, #0
+  .long  0xf2666da2                          // vsub.f32      d22, d22, d18
+  .long  0xf241ae80                          // vceq.f32      d26, d17, d0
+  .long  0xeec8ba2a                          // vdiv.f32      s23, s16, s21
+  .long  0xf3430db4                          // vmul.f32      d16, d19, d20
+  .long  0xee88ba0a                          // vdiv.f32      s22, s16, s20
+  .long  0xf3209ea4                          // vcgt.f32      d9, d16, d20
+  .long  0xf2614d02                          // vsub.f32      d20, d1, d2
+  .long  0xf3477d9b                          // vmul.f32      d23, d23, d11
+  .long  0xf31691b3                          // vbsl          d9, d22, d19
+  .long  0xf2623d00                          // vsub.f32      d19, d2, d0
+  .long  0xf3626e01                          // vcgt.f32      d22, d2, d1
+  .long  0xeeca8aa9                          // vdiv.f32      s17, s21, s19
+  .long  0xee8a8a09                          // vdiv.f32      s16, s20, s18
+  .long  0xf3433d9b                          // vmul.f32      d19, d19, d11
+  .long  0xf3444d9b                          // vmul.f32      d20, d20, d11
+  .long  0xf35961b8                          // vbsl          d22, d25, d24
+  .long  0xf2419e81                          // vceq.f32      d25, d17, d1
+  .long  0xf2011ea2                          // vceq.f32      d1, d17, d18
+  .long  0xf2433da5                          // vadd.f32      d19, d19, d21
+  .long  0xf2c15f10                          // vmov.f32      d21, #4
+  .long  0xf2464da4                          // vadd.f32      d20, d22, d20
+  .long  0xf2471da5                          // vadd.f32      d17, d23, d21
+  .long  0xf35391b1                          // vbsl          d25, d19, d17
+  .long  0xeddf1b0a                          // vldr          d17, [pc, #40]
+  .long  0xf2612111                          // vorr          d18, d1, d1
+  .long  0xf354a1b9                          // vbsl          d26, d20, d25
+  .long  0xf35821ba                          // vbsl          d18, d24, d26
+  .long  0xf3181198                          // vbsl          d1, d24, d8
+  .long  0xf3020db1                          // vmul.f32      d0, d18, d17
+  .long  0xf22021b0                          // vorr          d2, d16, d16
+  .long  0xecbd8b08                          // vpop          {d8-d11}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x40c00000                          // .word         0x40c00000
+  .long  0x40c00000                          // .word         0x40c00000
+  .long  0x3e2aaaab                          // .word         0x3e2aaaab
+  .long  0x3e2aaaab                          // .word         0x3e2aaaab
+
+HIDDEN _sk_hsl_to_rgb_vfp4
+.globl _sk_hsl_to_rgb_vfp4
+FUNCTION(_sk_hsl_to_rgb_vfp4)
+_sk_hsl_to_rgb_vfp4:
+  .long  0xed2d8b02                          // vpush         {d8}
+  .long  0xeddf0b51                          // vldr          d16, [pc, #324]
+  .long  0xf3fb2700                          // vcvt.s32.f32  d18, d0
+  .long  0xeddf1b51                          // vldr          d17, [pc, #324]
+  .long  0xf2400d20                          // vadd.f32      d16, d0, d16
+  .long  0xf2401d21                          // vadd.f32      d17, d0, d17
+  .long  0xeddfab50                          // vldr          d26, [pc, #320]
+  .long  0xf3416d12                          // vmul.f32      d22, d1, d2
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb3720                          // vcvt.s32.f32  d19, d16
+  .long  0xf3fb4721                          // vcvt.s32.f32  d20, d17
+  .long  0xf2c3761f                          // vmov.i32      d23, #1056964608
+  .long  0xf3625e80                          // vcgt.f32      d21, d18, d0
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3fb4624                          // vcvt.f32.s32  d20, d20
+  .long  0xf2c08010                          // vmov.i32      d24, #0
+  .long  0xf35a51b8                          // vbsl          d21, d26, d24
+  .long  0xf3639ea0                          // vcgt.f32      d25, d19, d16
+  .long  0xf364bea1                          // vcgt.f32      d27, d20, d17
+  .long  0xf342ce27                          // vcge.f32      d28, d2, d23
+  .long  0xf261dd26                          // vsub.f32      d29, d1, d22
+  .long  0xf35a91b8                          // vbsl          d25, d26, d24
+  .long  0xf35ab1b8                          // vbsl          d27, d26, d24
+  .long  0xf2622da5                          // vsub.f32      d18, d18, d21
+  .long  0xf2633da9                          // vsub.f32      d19, d19, d25
+  .long  0xf2644dab                          // vsub.f32      d20, d20, d27
+  .long  0xf35dc1b6                          // vbsl          d28, d29, d22
+  .long  0xeddfdb3c                          // vldr          d29, [pc, #240]
+  .long  0xf2602d22                          // vsub.f32      d18, d0, d18
+  .long  0xf2600da3                          // vsub.f32      d16, d16, d19
+  .long  0xf2c15f18                          // vmov.f32      d21, #6
+  .long  0xf2426d02                          // vadd.f32      d22, d2, d2
+  .long  0xf24c8d82                          // vadd.f32      d24, d28, d2
+  .long  0xf2611da4                          // vsub.f32      d17, d17, d20
+  .long  0xf3423db5                          // vmul.f32      d19, d18, d21
+  .long  0xf3409db5                          // vmul.f32      d25, d16, d21
+  .long  0xf2664da8                          // vsub.f32      d20, d22, d24
+  .long  0xf3415db5                          // vmul.f32      d21, d17, d21
+  .long  0xf2c16f10                          // vmov.f32      d22, #4
+  .long  0xf342eead                          // vcge.f32      d30, d18, d29
+  .long  0xf266ada3                          // vsub.f32      d26, d22, d19
+  .long  0xf268bda4                          // vsub.f32      d27, d24, d20
+  .long  0xf266cda9                          // vsub.f32      d28, d22, d25
+  .long  0xf2666da5                          // vsub.f32      d22, d22, d21
+  .long  0xf340fead                          // vcge.f32      d31, d16, d29
+  .long  0xf34badba                          // vmul.f32      d26, d27, d26
+  .long  0xf341dead                          // vcge.f32      d29, d17, d29
+  .long  0xf34bcdbc                          // vmul.f32      d28, d27, d28
+  .long  0xf34b6db6                          // vmul.f32      d22, d27, d22
+  .long  0xf244adaa                          // vadd.f32      d26, d20, d26
+  .long  0xf3020ea7                          // vcge.f32      d0, d18, d23
+  .long  0xf3008ea7                          // vcge.f32      d8, d16, d23
+  .long  0xf354e1ba                          // vbsl          d30, d20, d26
+  .long  0xeddfab24                          // vldr          d26, [pc, #144]
+  .long  0xf244cdac                          // vadd.f32      d28, d20, d28
+  .long  0xf2446da6                          // vadd.f32      d22, d20, d22
+  .long  0xf34b3db3                          // vmul.f32      d19, d27, d19
+  .long  0xf34b9db9                          // vmul.f32      d25, d27, d25
+  .long  0xf3417ea7                          // vcge.f32      d23, d17, d23
+  .long  0xf354d1b6                          // vbsl          d29, d20, d22
+  .long  0xf354f1bc                          // vbsl          d31, d20, d28
+  .long  0xf3406eaa                          // vcge.f32      d22, d16, d26
+  .long  0xf3422eaa                          // vcge.f32      d18, d18, d26
+  .long  0xf34b5db5                          // vmul.f32      d21, d27, d21
+  .long  0xf3411eaa                          // vcge.f32      d17, d17, d26
+  .long  0xf31e01b8                          // vbsl          d0, d30, d24
+  .long  0xf31f81b8                          // vbsl          d8, d31, d24
+  .long  0xf2440da3                          // vadd.f32      d16, d20, d19
+  .long  0xf2443da9                          // vadd.f32      d19, d20, d25
+  .long  0xf3502130                          // vbsl          d18, d0, d16
+  .long  0xf3f90501                          // vceq.f32      d16, d1, #0
+  .long  0xf35d71b8                          // vbsl          d23, d29, d24
+  .long  0xf3586133                          // vbsl          d22, d8, d19
+  .long  0xf22011b0                          // vorr          d1, d16, d16
+  .long  0xf2444da5                          // vadd.f32      d20, d20, d21
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf3520136                          // vbsl          d16, d2, d22
+  .long  0xf35711b4                          // vbsl          d17, d23, d20
+  .long  0xf3121132                          // vbsl          d1, d2, d18
+  .long  0xf3120131                          // vbsl          d0, d2, d17
+  .long  0xf22021b0                          // vorr          d2, d16, d16
+  .long  0xecbd8b02                          // vpop          {d8}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+  .long  0x3eaaaaab                          // .word         0x3eaaaaab
+  .long  0x3eaaaaab                          // .word         0x3eaaaaab
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f2aaaab                          // .word         0x3f2aaaab
+  .long  0x3f2aaaab                          // .word         0x3f2aaaab
+  .long  0x3e2aaaab                          // .word         0x3e2aaaab
+  .long  0x3e2aaaab                          // .word         0x3e2aaaab
+
+HIDDEN _sk_scale_1_float_vfp4
+.globl _sk_scale_1_float_vfp4
+FUNCTION(_sk_scale_1_float_vfp4)
+_sk_scale_1_float_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4ee0c9f                          // vld1.32       {d16[]}, [lr :32]
+  .long  0xf3000d90                          // vmul.f32      d0, d16, d0
+  .long  0xf3001d91                          // vmul.f32      d1, d16, d1
+  .long  0xf3002d92                          // vmul.f32      d2, d16, d2
+  .long  0xf3003d93                          // vmul.f32      d3, d16, d3
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_scale_u8_vfp4
+.globl _sk_scale_u8_vfp4
+FUNCTION(_sk_scale_u8_vfp4)
+_sk_scale_u8_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe24dd008                          // sub           sp, sp, #8
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de020                          // ldr           lr, [sp, #32]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc002                          // add           ip, ip, r2
+  .long  0x0a000014                          // beq           1a20 <sk_scale_u8_vfp4+0x78>
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xe1cdc0b4                          // strh          ip, [sp, #4]
+  .long  0xe28dc004                          // add           ip, sp, #4
+  .long  0xf4ec041f                          // vld1.16       {d16[0]}, [ip :16]
+  .long  0xf3c80a30                          // vmovl.u8      q8, d16
+  .long  0xf3908a30                          // vmovl.u16     q4, d16
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xeddf1b10                          // vldr          d17, [pc, #64]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2480130                          // vand          d16, d8, d16
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf3000d90                          // vmul.f32      d0, d16, d0
+  .long  0xf3001d91                          // vmul.f32      d1, d16, d1
+  .long  0xf3002d92                          // vmul.f32      d2, d16, d2
+  .long  0xf3003d93                          // vmul.f32      d3, d16, d3
+  .long  0xe28dd008                          // add           sp, sp, #8
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe5dcc000                          // ldrb          ip, [ip]
+  .long  0xeddf8a03                          // vldr          s17, [pc, #12]
+  .long  0xee08ca10                          // vmov          s16, ip
+  .long  0xeaffffec                          // b             19e4 <sk_scale_u8_vfp4+0x3c>
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x00000000                          // .word         0x00000000
+
+HIDDEN _sk_lerp_1_float_vfp4
+.globl _sk_lerp_1_float_vfp4
+FUNCTION(_sk_lerp_1_float_vfp4)
+_sk_lerp_1_float_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2600d04                          // vsub.f32      d16, d0, d4
+  .long  0xf2611d05                          // vsub.f32      d17, d1, d5
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf2622d06                          // vsub.f32      d18, d2, d6
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2633d07                          // vsub.f32      d19, d3, d7
+  .long  0xf4ee4c9f                          // vld1.32       {d20[]}, [lr :32]
+  .long  0xf2240114                          // vorr          d0, d4, d4
+  .long  0xf2251115                          // vorr          d1, d5, d5
+  .long  0xf2262116                          // vorr          d2, d6, d6
+  .long  0xf2273117                          // vorr          d3, d7, d7
+  .long  0xf2000cb4                          // vfma.f32      d0, d16, d20
+  .long  0xf2011cb4                          // vfma.f32      d1, d17, d20
+  .long  0xf2022cb4                          // vfma.f32      d2, d18, d20
+  .long  0xf2033cb4                          // vfma.f32      d3, d19, d20
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_lerp_u8_vfp4
+.globl _sk_lerp_u8_vfp4
+FUNCTION(_sk_lerp_u8_vfp4)
+_sk_lerp_u8_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe24dd008                          // sub           sp, sp, #8
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de020                          // ldr           lr, [sp, #32]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc002                          // add           ip, ip, r2
+  .long  0x0a00001e                          // beq           1b28 <sk_lerp_u8_vfp4+0xa0>
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xe1cdc0b4                          // strh          ip, [sp, #4]
+  .long  0xe28dc004                          // add           ip, sp, #4
+  .long  0xf4ec041f                          // vld1.16       {d16[0]}, [ip :16]
+  .long  0xf3c80a30                          // vmovl.u8      q8, d16
+  .long  0xf3908a30                          // vmovl.u16     q4, d16
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xeddf1b1a                          // vldr          d17, [pc, #104]
+  .long  0xf2602d04                          // vsub.f32      d18, d0, d4
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xf2480130                          // vand          d16, d8, d16
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xf2623d06                          // vsub.f32      d19, d2, d6
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xf2634d07                          // vsub.f32      d20, d3, d7
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xf2240114                          // vorr          d0, d4, d4
+  .long  0xf2262116                          // vorr          d2, d6, d6
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf2611d05                          // vsub.f32      d17, d1, d5
+  .long  0xf2251115                          // vorr          d1, d5, d5
+  .long  0xf2273117                          // vorr          d3, d7, d7
+  .long  0xf2020cb0                          // vfma.f32      d0, d18, d16
+  .long  0xf2011cb0                          // vfma.f32      d1, d17, d16
+  .long  0xf2032cb0                          // vfma.f32      d2, d19, d16
+  .long  0xf2043cb0                          // vfma.f32      d3, d20, d16
+  .long  0xe28dd008                          // add           sp, sp, #8
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe5dcc000                          // ldrb          ip, [ip]
+  .long  0xeddf8a03                          // vldr          s17, [pc, #12]
+  .long  0xee08ca10                          // vmov          s16, ip
+  .long  0xeaffffe2                          // b             1ac4 <sk_lerp_u8_vfp4+0x3c>
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_lerp_565_vfp4
+.globl _sk_lerp_565_vfp4
+FUNCTION(_sk_lerp_565_vfp4)
+_sk_lerp_565_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe24dd008                          // sub           sp, sp, #8
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de020                          // ldr           lr, [sp, #32]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc082                          // add           ip, ip, r2, lsl #1
+  .long  0x0a00002e                          // beq           1c28 <sk_lerp_565_vfp4+0xe0>
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe58dc004                          // str           ip, [sp, #4]
+  .long  0xe28dc004                          // add           ip, sp, #4
+  .long  0xf4ec083f                          // vld1.32       {d16[0]}, [ip :32]
+  .long  0xf3908a30                          // vmovl.u16     q4, d16
+  .long  0xf2c1001f                          // vmov.i32      d16, #31
+  .long  0xe3a0ce7e                          // mov           ip, #2016
+  .long  0xee82cb90                          // vdup.32       d18, ip
+  .long  0xf3c71218                          // vmov.i32      d17, #63488
+  .long  0xf2480130                          // vand          d16, d8, d16
+  .long  0xeddf3b27                          // vldr          d19, [pc, #156]
+  .long  0xf2482132                          // vand          d18, d8, d18
+  .long  0xeddf4b27                          // vldr          d20, [pc, #156]
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xf2481131                          // vand          d17, d8, d17
+  .long  0xf2635d07                          // vsub.f32      d21, d3, d7
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xf3400db3                          // vmul.f32      d16, d16, d19
+  .long  0xeddf3b1e                          // vldr          d19, [pc, #120]
+  .long  0xf3422db4                          // vmul.f32      d18, d18, d20
+  .long  0xf2674117                          // vorr          d20, d7, d7
+  .long  0xf3411db3                          // vmul.f32      d17, d17, d19
+  .long  0xf2673117                          // vorr          d19, d7, d7
+  .long  0xf2453cb0                          // vfma.f32      d19, d21, d16
+  .long  0xf2454cb2                          // vfma.f32      d20, d21, d18
+  .long  0xf2679117                          // vorr          d25, d7, d7
+  .long  0xf2628d06                          // vsub.f32      d24, d2, d6
+  .long  0xf2459cb1                          // vfma.f32      d25, d21, d17
+  .long  0xf2262116                          // vorr          d2, d6, d6
+  .long  0xf2606d04                          // vsub.f32      d22, d0, d4
+  .long  0xf2617d05                          // vsub.f32      d23, d1, d5
+  .long  0xf2082cb0                          // vfma.f32      d2, d24, d16
+  .long  0xf2440fa3                          // vmax.f32      d16, d20, d19
+  .long  0xf2240114                          // vorr          d0, d4, d4
+  .long  0xf2251115                          // vorr          d1, d5, d5
+  .long  0xf2060cb1                          // vfma.f32      d0, d22, d17
+  .long  0xf2071cb2                          // vfma.f32      d1, d23, d18
+  .long  0xf2093fa0                          // vmax.f32      d3, d25, d16
+  .long  0xe28dd008                          // add           sp, sp, #8
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xeddf8a07                          // vldr          s17, [pc, #28]
+  .long  0xee08ca10                          // vmov          s16, ip
+  .long  0xeaffffd1                          // b             1b80 <sk_lerp_565_vfp4+0x38>
+  .long  0x3d042108                          // .word         0x3d042108
+  .long  0x3d042108                          // .word         0x3d042108
+  .long  0x3a020821                          // .word         0x3a020821
+  .long  0x3a020821                          // .word         0x3a020821
+  .long  0x37842108                          // .word         0x37842108
+  .long  0x37842108                          // .word         0x37842108
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_load_tables_vfp4
+.globl _sk_load_tables_vfp4
+FUNCTION(_sk_load_tables_vfp4)
+_sk_load_tables_vfp4:
+  .long  0xe92d47f0                          // push          {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59d4020                          // ldr           r4, [sp, #32]
+  .long  0xe59ce000                          // ldr           lr, [ip]
+  .long  0xe3540001                          // cmp           r4, #1
+  .long  0xe08ee102                          // add           lr, lr, r2, lsl #2
+  .long  0x0a000023                          // beq           1d04 <sk_load_tables_vfp4+0xac>
+  .long  0xed9e0b00                          // vldr          d0, [lr]
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xe59c7004                          // ldr           r7, [ip, #4]
+  .long  0xf3f01010                          // vshr.u32      d17, d0, #16
+  .long  0xe59c6008                          // ldr           r6, [ip, #8]
+  .long  0xf3f82010                          // vshr.u32      d18, d0, #8
+  .long  0xe59c400c                          // ldr           r4, [ip, #12]
+  .long  0xf24111b0                          // vand          d17, d17, d16
+  .long  0xf24221b0                          // vand          d18, d18, d16
+  .long  0xf2400130                          // vand          d16, d0, d16
+  .long  0xee319b90                          // vmov.32       r9, d17[1]
+  .long  0xee32eb90                          // vmov.32       lr, d18[1]
+  .long  0xee305b90                          // vmov.32       r5, d16[1]
+  .long  0xee108b90                          // vmov.32       r8, d16[0]
+  .long  0xf3e80010                          // vshr.u32      d16, d0, #24
+  .long  0xee12cb90                          // vmov.32       ip, d18[0]
+  .long  0xee11ab90                          // vmov.32       sl, d17[0]
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xeddf1b13                          // vldr          d17, [pc, #76]
+  .long  0xf3003db1                          // vmul.f32      d3, d16, d17
+  .long  0xe0849109                          // add           r9, r4, r9, lsl #2
+  .long  0xe086e10e                          // add           lr, r6, lr, lsl #2
+  .long  0xe0875105                          // add           r5, r7, r5, lsl #2
+  .long  0xedd92a00                          // vldr          s5, [r9]
+  .long  0xedde1a00                          // vldr          s3, [lr]
+  .long  0xedd50a00                          // vldr          s1, [r5]
+  .long  0xe0875108                          // add           r5, r7, r8, lsl #2
+  .long  0xe086710c                          // add           r7, r6, ip, lsl #2
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xed950a00                          // vldr          s0, [r5]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed971a00                          // vldr          s2, [r7]
+  .long  0xe084710a                          // add           r7, r4, sl, lsl #2
+  .long  0xed972a00                          // vldr          s4, [r7]
+  .long  0xe8bd47f0                          // pop           {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xeddf0a03                          // vldr          s1, [pc, #12]
+  .long  0xed9e0a00                          // vldr          s0, [lr]
+  .long  0xeaffffd9                          // b             1c78 <sk_load_tables_vfp4+0x20>
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_load_tables_u16_be_vfp4
+.globl _sk_load_tables_u16_be_vfp4
+FUNCTION(_sk_load_tables_u16_be_vfp4)
+_sk_load_tables_u16_be_vfp4:
+  .long  0xe92d47f0                          // push          {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59d4020                          // ldr           r4, [sp, #32]
+  .long  0xe59ce000                          // ldr           lr, [ip]
+  .long  0xe3540000                          // cmp           r4, #0
+  .long  0xe08ee182                          // add           lr, lr, r2, lsl #3
+  .long  0xf4ee070f                          // vld4.16       {d16[0],d17[0],d18[0],d19[0]}, [lr]
+  .long  0x1a000001                          // bne           1d48 <sk_load_tables_u16_be_vfp4+0x28>
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4e4074f                          // vld4.16       {d16[1],d17[1],d18[1],d19[1]}, [r4]
+  .long  0xee924bb0                          // vmov.u16      r4, d18[0]
+  .long  0xf3c7701f                          // vmov.i32      d23, #255
+  .long  0xee905bb0                          // vmov.u16      r5, d16[0]
+  .long  0xee91ebb0                          // vmov.u16      lr, d17[0]
+  .long  0xee926bf0                          // vmov.u16      r6, d18[1]
+  .long  0xee908bf0                          // vmov.u16      r8, d16[1]
+  .long  0xee917bf0                          // vmov.u16      r7, d17[1]
+  .long  0xee044b90                          // vmov.32       d20[0], r4
+  .long  0xe59c400c                          // ldr           r4, [ip, #12]
+  .long  0xee065b90                          // vmov.32       d22[0], r5
+  .long  0xee05eb90                          // vmov.32       d21[0], lr
+  .long  0xee246b90                          // vmov.32       d20[1], r6
+  .long  0xee936bb0                          // vmov.u16      r6, d19[0]
+  .long  0xee268b90                          // vmov.32       d22[1], r8
+  .long  0xee257b90                          // vmov.32       d21[1], r7
+  .long  0xee937bf0                          // vmov.u16      r7, d19[1]
+  .long  0xf24621b7                          // vand          d18, d22, d23
+  .long  0xf24401b7                          // vand          d16, d20, d23
+  .long  0xf24511b7                          // vand          d17, d21, d23
+  .long  0xee32eb90                          // vmov.32       lr, d18[1]
+  .long  0xee305b90                          // vmov.32       r5, d16[1]
+  .long  0xee319b90                          // vmov.32       r9, d17[1]
+  .long  0xee128b90                          // vmov.32       r8, d18[0]
+  .long  0xf3c72c1f                          // vmov.i32      d18, #65535
+  .long  0xee036b90                          // vmov.32       d19[0], r6
+  .long  0xe59c6008                          // ldr           r6, [ip, #8]
+  .long  0xee237b90                          // vmov.32       d19[1], r7
+  .long  0xe59c7004                          // ldr           r7, [ip, #4]
+  .long  0xee11cb90                          // vmov.32       ip, d17[0]
+  .long  0xf24311b2                          // vand          d17, d19, d18
+  .long  0xe084a105                          // add           sl, r4, r5, lsl #2
+  .long  0xe087510e                          // add           r5, r7, lr, lsl #2
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xf2e80533                          // vshl.s32      d16, d19, #8
+  .long  0xf3f81031                          // vshr.u32      d17, d17, #8
+  .long  0xe0869109                          // add           r9, r6, r9, lsl #2
+  .long  0xedd50a00                          // vldr          s1, [r5]
+  .long  0xe0875108                          // add           r5, r7, r8, lsl #2
+  .long  0xf26001b1                          // vorr          d16, d16, d17
+  .long  0xedd91a00                          // vldr          s3, [r9]
+  .long  0xeddf1b0c                          // vldr          d17, [pc, #48]
+  .long  0xf24001b2                          // vand          d16, d16, d18
+  .long  0xedda2a00                          // vldr          s5, [sl]
+  .long  0xed950a00                          // vldr          s0, [r5]
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3003db1                          // vmul.f32      d3, d16, d17
+  .long  0xe086710c                          // add           r7, r6, ip, lsl #2
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed971a00                          // vldr          s2, [r7]
+  .long  0xe084710e                          // add           r7, r4, lr, lsl #2
+  .long  0xed972a00                          // vldr          s4, [r7]
+  .long  0xe8bd47f0                          // pop           {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x37800080                          // .word         0x37800080
+  .long  0x37800080                          // .word         0x37800080
+
+HIDDEN _sk_load_tables_rgb_u16_be_vfp4
+.globl _sk_load_tables_rgb_u16_be_vfp4
+FUNCTION(_sk_load_tables_rgb_u16_be_vfp4)
+_sk_load_tables_rgb_u16_be_vfp4:
+  .long  0xe92d47f0                          // push          {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe0824082                          // add           r4, r2, r2, lsl #1
+  .long  0xe59ce000                          // ldr           lr, [ip]
+  .long  0xe08ee084                          // add           lr, lr, r4, lsl #1
+  .long  0xe59d4020                          // ldr           r4, [sp, #32]
+  .long  0xf4ee060f                          // vld3.16       {d16[0],d17[0],d18[0]}, [lr]
+  .long  0xe3540000                          // cmp           r4, #0
+  .long  0x1a000001                          // bne           1e54 <sk_load_tables_rgb_u16_be_vfp4+0x2c>
+  .long  0xe28e4006                          // add           r4, lr, #6
+  .long  0xf4e4064f                          // vld3.16       {d16[1],d17[1],d18[1]}, [r4]
+  .long  0xee924bb0                          // vmov.u16      r4, d18[0]
+  .long  0xf2873f10                          // vmov.f32      d3, #1
+  .long  0xee905bb0                          // vmov.u16      r5, d16[0]
+  .long  0xee908bf0                          // vmov.u16      r8, d16[1]
+  .long  0xf3c7301f                          // vmov.i32      d19, #255
+  .long  0xee926bf0                          // vmov.u16      r6, d18[1]
+  .long  0xee91ebb0                          // vmov.u16      lr, d17[0]
+  .long  0xee917bf0                          // vmov.u16      r7, d17[1]
+  .long  0xee004b90                          // vmov.32       d16[0], r4
+  .long  0xee025b90                          // vmov.32       d18[0], r5
+  .long  0xee206b90                          // vmov.32       d16[1], r6
+  .long  0xe99c0070                          // ldmib         ip, {r4, r5, r6}
+  .long  0xee228b90                          // vmov.32       d18[1], r8
+  .long  0xf24001b3                          // vand          d16, d16, d19
+  .long  0xee01eb90                          // vmov.32       d17[0], lr
+  .long  0xf24221b3                          // vand          d18, d18, d19
+  .long  0xee217b90                          // vmov.32       d17[1], r7
+  .long  0xee307b90                          // vmov.32       r7, d16[1]
+  .long  0xf24111b3                          // vand          d17, d17, d19
+  .long  0xee328b90                          // vmov.32       r8, d18[1]
+  .long  0xee12eb90                          // vmov.32       lr, d18[0]
+  .long  0xee319b90                          // vmov.32       r9, d17[1]
+  .long  0xee11cb90                          // vmov.32       ip, d17[0]
+  .long  0xe086a107                          // add           sl, r6, r7, lsl #2
+  .long  0xe0847108                          // add           r7, r4, r8, lsl #2
+  .long  0xee108b90                          // vmov.32       r8, d16[0]
+  .long  0xedda2a00                          // vldr          s5, [sl]
+  .long  0xe0859109                          // add           r9, r5, r9, lsl #2
+  .long  0xedd70a00                          // vldr          s1, [r7]
+  .long  0xe084710e                          // add           r7, r4, lr, lsl #2
+  .long  0xedd91a00                          // vldr          s3, [r9]
+  .long  0xed970a00                          // vldr          s0, [r7]
+  .long  0xe085710c                          // add           r7, r5, ip, lsl #2
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed971a00                          // vldr          s2, [r7]
+  .long  0xe0867108                          // add           r7, r6, r8, lsl #2
+  .long  0xed972a00                          // vldr          s4, [r7]
+  .long  0xe8bd47f0                          // pop           {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_byte_tables_vfp4
+.globl _sk_byte_tables_vfp4
+FUNCTION(_sk_byte_tables_vfp4)
+_sk_byte_tables_vfp4:
+  .long  0xe92d47f0                          // push          {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xeddf0b37                          // vldr          d16, [pc, #220]
+  .long  0xf2c3361f                          // vmov.i32      d19, #1056964608
+  .long  0xf2413c30                          // vfma.f32      d19, d1, d16
+  .long  0xe8911020                          // ldm           r1, {r5, ip}
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xf2422c30                          // vfma.f32      d18, d2, d16
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2c3461f                          // vmov.i32      d20, #1056964608
+  .long  0xe89504c0                          // ldm           r5, {r6, r7, sl}
+  .long  0xf2404c30                          // vfma.f32      d20, d0, d16
+  .long  0xe595900c                          // ldr           r9, [r5, #12]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf2431c30                          // vfma.f32      d17, d3, d16
+  .long  0xf3fb37a3                          // vcvt.u32.f32  d19, d19
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf3fb47a4                          // vcvt.u32.f32  d20, d20
+  .long  0xee138b90                          // vmov.32       r8, d19[0]
+  .long  0xf3fb07a1                          // vcvt.u32.f32  d16, d17
+  .long  0xee12eb90                          // vmov.32       lr, d18[0]
+  .long  0xee144b90                          // vmov.32       r4, d20[0]
+  .long  0xe7d75008                          // ldrb          r5, [r7, r8]
+  .long  0xe7dae00e                          // ldrb          lr, [sl, lr]
+  .long  0xee055b90                          // vmov.32       d21[0], r5
+  .long  0xe7d64004                          // ldrb          r4, [r6, r4]
+  .long  0xee345b90                          // vmov.32       r5, d20[1]
+  .long  0xee04eb90                          // vmov.32       d20[0], lr
+  .long  0xee014b90                          // vmov.32       d17[0], r4
+  .long  0xee104b90                          // vmov.32       r4, d16[0]
+  .long  0xe7d6e005                          // ldrb          lr, [r6, r5]
+  .long  0xee335b90                          // vmov.32       r5, d19[1]
+  .long  0xee326b90                          // vmov.32       r6, d18[1]
+  .long  0xf3c7201f                          // vmov.i32      d18, #255
+  .long  0xee21eb90                          // vmov.32       d17[1], lr
+  .long  0xe7d94004                          // ldrb          r4, [r9, r4]
+  .long  0xf24111b2                          // vand          d17, d17, d18
+  .long  0xf3fb16a1                          // vcvt.f32.u32  d17, d17
+  .long  0xe7d75005                          // ldrb          r5, [r7, r5]
+  .long  0xee307b90                          // vmov.32       r7, d16[1]
+  .long  0xee004b90                          // vmov.32       d16[0], r4
+  .long  0xee255b90                          // vmov.32       d21[1], r5
+  .long  0xe7da5006                          // ldrb          r5, [sl, r6]
+  .long  0xf24531b2                          // vand          d19, d21, d18
+  .long  0xee245b90                          // vmov.32       d20[1], r5
+  .long  0xf24441b2                          // vand          d20, d20, d18
+  .long  0xf3fb46a4                          // vcvt.f32.u32  d20, d20
+  .long  0xe7d94007                          // ldrb          r4, [r9, r7]
+  .long  0xee204b90                          // vmov.32       d16[1], r4
+  .long  0xf24001b2                          // vand          d16, d16, d18
+  .long  0xf3fb26a3                          // vcvt.f32.u32  d18, d19
+  .long  0xeddf3b08                          // vldr          d19, [pc, #32]
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3010db3                          // vmul.f32      d0, d17, d19
+  .long  0xf3042db3                          // vmul.f32      d2, d20, d19
+  .long  0xf3021db3                          // vmul.f32      d1, d18, d19
+  .long  0xf3003db3                          // vmul.f32      d3, d16, d19
+  .long  0xe8bd47f0                          // pop           {r4, r5, r6, r7, r8, r9, sl, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+
+HIDDEN _sk_byte_tables_rgb_vfp4
+.globl _sk_byte_tables_rgb_vfp4
+FUNCTION(_sk_byte_tables_rgb_vfp4)
+_sk_byte_tables_rgb_vfp4:
+  .long  0xe92d4bf0                          // push          {r4, r5, r6, r7, r8, r9, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf3c7301f                          // vmov.i32      d19, #255
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe89e0210                          // ldm           lr, {r4, r9}
+  .long  0xe59e600c                          // ldr           r6, [lr, #12]
+  .long  0xe59e8008                          // ldr           r8, [lr, #8]
+  .long  0xe2466001                          // sub           r6, r6, #1
+  .long  0xee806b90                          // vdup.32       d16, r6
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf2402c30                          // vfma.f32      d18, d0, d16
+  .long  0xf2411c30                          // vfma.f32      d17, d1, d16
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf3fb17a1                          // vcvt.u32.f32  d17, d17
+  .long  0xee126b90                          // vmov.32       r6, d18[0]
+  .long  0xee327b90                          // vmov.32       r7, d18[1]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xf2422c30                          // vfma.f32      d18, d2, d16
+  .long  0xf3fb07a2                          // vcvt.u32.f32  d16, d18
+  .long  0xee105b90                          // vmov.32       r5, d16[0]
+  .long  0xe7d46006                          // ldrb          r6, [r4, r6]
+  .long  0xe7d4e007                          // ldrb          lr, [r4, r7]
+  .long  0xee314b90                          // vmov.32       r4, d17[1]
+  .long  0xee026b90                          // vmov.32       d18[0], r6
+  .long  0xee116b90                          // vmov.32       r6, d17[0]
+  .long  0xee307b90                          // vmov.32       r7, d16[1]
+  .long  0xee22eb90                          // vmov.32       d18[1], lr
+  .long  0xf24221b3                          // vand          d18, d18, d19
+  .long  0xf3fb26a2                          // vcvt.f32.u32  d18, d18
+  .long  0xe7d85005                          // ldrb          r5, [r8, r5]
+  .long  0xee015b90                          // vmov.32       d17[0], r5
+  .long  0xe7d94004                          // ldrb          r4, [r9, r4]
+  .long  0xe7d96006                          // ldrb          r6, [r9, r6]
+  .long  0xe7d85007                          // ldrb          r5, [r8, r7]
+  .long  0xee006b90                          // vmov.32       d16[0], r6
+  .long  0xee215b90                          // vmov.32       d17[1], r5
+  .long  0xee204b90                          // vmov.32       d16[1], r4
+  .long  0xf24111b3                          // vand          d17, d17, d19
+  .long  0xf24001b3                          // vand          d16, d16, d19
+  .long  0xeddf3b06                          // vldr          d19, [pc, #24]
+  .long  0xf3fb16a1                          // vcvt.f32.u32  d17, d17
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3020db3                          // vmul.f32      d0, d18, d19
+  .long  0xf3012db3                          // vmul.f32      d2, d17, d19
+  .long  0xf3001db3                          // vmul.f32      d1, d16, d19
+  .long  0xe8bd4bf0                          // pop           {r4, r5, r6, r7, r8, r9, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+
+HIDDEN _sk_table_r_vfp4
+.globl _sk_table_r_vfp4
+FUNCTION(_sk_table_r_vfp4)
+_sk_table_r_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe59e4004                          // ldr           r4, [lr, #4]
+  .long  0xe59e5000                          // ldr           r5, [lr]
+  .long  0xe2444001                          // sub           r4, r4, #1
+  .long  0xee804b90                          // vdup.32       d16, r4
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf2401c30                          // vfma.f32      d17, d0, d16
+  .long  0xf3fb07a1                          // vcvt.u32.f32  d16, d17
+  .long  0xee304b90                          // vmov.32       r4, d16[1]
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe0854104                          // add           r4, r5, r4, lsl #2
+  .long  0xe085510e                          // add           r5, r5, lr, lsl #2
+  .long  0xedd40a00                          // vldr          s1, [r4]
+  .long  0xed950a00                          // vldr          s0, [r5]
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_table_g_vfp4
+.globl _sk_table_g_vfp4
+FUNCTION(_sk_table_g_vfp4)
+_sk_table_g_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe59e4004                          // ldr           r4, [lr, #4]
+  .long  0xe59e5000                          // ldr           r5, [lr]
+  .long  0xe2444001                          // sub           r4, r4, #1
+  .long  0xee804b90                          // vdup.32       d16, r4
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf2411c30                          // vfma.f32      d17, d1, d16
+  .long  0xf3fb07a1                          // vcvt.u32.f32  d16, d17
+  .long  0xee304b90                          // vmov.32       r4, d16[1]
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe0854104                          // add           r4, r5, r4, lsl #2
+  .long  0xe085510e                          // add           r5, r5, lr, lsl #2
+  .long  0xedd41a00                          // vldr          s3, [r4]
+  .long  0xed951a00                          // vldr          s2, [r5]
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_table_b_vfp4
+.globl _sk_table_b_vfp4
+FUNCTION(_sk_table_b_vfp4)
+_sk_table_b_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe59e4004                          // ldr           r4, [lr, #4]
+  .long  0xe59e5000                          // ldr           r5, [lr]
+  .long  0xe2444001                          // sub           r4, r4, #1
+  .long  0xee804b90                          // vdup.32       d16, r4
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf2421c30                          // vfma.f32      d17, d2, d16
+  .long  0xf3fb07a1                          // vcvt.u32.f32  d16, d17
+  .long  0xee304b90                          // vmov.32       r4, d16[1]
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe0854104                          // add           r4, r5, r4, lsl #2
+  .long  0xe085510e                          // add           r5, r5, lr, lsl #2
+  .long  0xedd42a00                          // vldr          s5, [r4]
+  .long  0xed952a00                          // vldr          s4, [r5]
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_table_a_vfp4
+.globl _sk_table_a_vfp4
+FUNCTION(_sk_table_a_vfp4)
+_sk_table_a_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe59e4004                          // ldr           r4, [lr, #4]
+  .long  0xe59e5000                          // ldr           r5, [lr]
+  .long  0xe2444001                          // sub           r4, r4, #1
+  .long  0xee804b90                          // vdup.32       d16, r4
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf2431c30                          // vfma.f32      d17, d3, d16
+  .long  0xf3fb07a1                          // vcvt.u32.f32  d16, d17
+  .long  0xee304b90                          // vmov.32       r4, d16[1]
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe0854104                          // add           r4, r5, r4, lsl #2
+  .long  0xe085510e                          // add           r5, r5, lr, lsl #2
+  .long  0xedd43a00                          // vldr          s7, [r4]
+  .long  0xed953a00                          // vldr          s6, [r5]
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_parametric_r_vfp4
+.globl _sk_parametric_r_vfp4
+FUNCTION(_sk_parametric_r_vfp4)
+_sk_parametric_r_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xed2d8b06                          // vpush         {d8-d10}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xeddf3b41                          // vldr          d19, [pc, #260]
+  .long  0xed9f8a4e                          // vldr          s16, [pc, #312]
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xeddf4b42                          // vldr          d20, [pc, #264]
+  .long  0xf4e40c9d                          // vld1.32       {d16[]}, [r4 :32]!
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e400c                          // add           r4, lr, #12
+  .long  0xf2412c90                          // vfma.f32      d18, d17, d0
+  .long  0xf2c71d1f                          // vmov.i32      d17, #8388607
+  .long  0xf24211b1                          // vand          d17, d18, d17
+  .long  0xf2c3171f                          // vorr.i32      d17, #1056964608
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf2019da3                          // vadd.f32      d9, d17, d19
+  .long  0xf2c33614                          // vmov.i32      d19, #872415232
+  .long  0xf3422db3                          // vmul.f32      d18, d18, d19
+  .long  0xeddf3b30                          // vldr          d19, [pc, #192]
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf3411db3                          // vmul.f32      d17, d17, d19
+  .long  0xed9f8a39                          // vldr          s16, [pc, #228]
+  .long  0xf2422da4                          // vadd.f32      d18, d18, d20
+  .long  0xeddf4b2e                          // vldr          d20, [pc, #184]
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xf2621da1                          // vsub.f32      d17, d18, d17
+  .long  0xf2611d8a                          // vsub.f32      d17, d17, d10
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf3fb1720                          // vcvt.s32.f32  d17, d16
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3612ea0                          // vcgt.f32      d18, d17, d16
+  .long  0xf35421b3                          // vbsl          d18, d20, d19
+  .long  0xeddf4b2b                          // vldr          d20, [pc, #172]
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xeddf2b25                          // vldr          d18, [pc, #148]
+  .long  0xf2601da1                          // vsub.f32      d17, d16, d17
+  .long  0xf2400da4                          // vadd.f32      d16, d16, d20
+  .long  0xf2229da1                          // vsub.f32      d9, d18, d17
+  .long  0xeddf2b23                          // vldr          d18, [pc, #140]
+  .long  0xf3411db2                          // vmul.f32      d17, d17, d18
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf2c4161b                          // vmov.i32      d17, #1258291200
+  .long  0xf2400d8a                          // vadd.f32      d16, d16, d10
+  .long  0xf2402cb1                          // vfma.f32      d18, d16, d17
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4018                          // add           r4, lr, #24
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4010                          // add           r4, lr, #16
+  .long  0xf2401c90                          // vfma.f32      d17, d16, d0
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf3400e80                          // vcge.f32      d16, d16, d0
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf2442da2                          // vadd.f32      d18, d20, d18
+  .long  0xf35101b2                          // vbsl          d16, d17, d18
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xf2400fa3                          // vmax.f32      d16, d16, d19
+  .long  0xf2200fa1                          // vmin.f32      d0, d16, d17
+  .long  0xecbd8b06                          // vpop          {d8-d10}
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x3fdce9a3                          // .word         0x3fdce9a3
+  .long  0x41ddd2fe                          // .word         0x41ddd2fe
+
+HIDDEN _sk_parametric_g_vfp4
+.globl _sk_parametric_g_vfp4
+FUNCTION(_sk_parametric_g_vfp4)
+_sk_parametric_g_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xed2d8b06                          // vpush         {d8-d10}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xeddf3b41                          // vldr          d19, [pc, #260]
+  .long  0xed9f8a4e                          // vldr          s16, [pc, #312]
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xeddf4b42                          // vldr          d20, [pc, #264]
+  .long  0xf4e40c9d                          // vld1.32       {d16[]}, [r4 :32]!
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e400c                          // add           r4, lr, #12
+  .long  0xf2412c91                          // vfma.f32      d18, d17, d1
+  .long  0xf2c71d1f                          // vmov.i32      d17, #8388607
+  .long  0xf24211b1                          // vand          d17, d18, d17
+  .long  0xf2c3171f                          // vorr.i32      d17, #1056964608
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf2019da3                          // vadd.f32      d9, d17, d19
+  .long  0xf2c33614                          // vmov.i32      d19, #872415232
+  .long  0xf3422db3                          // vmul.f32      d18, d18, d19
+  .long  0xeddf3b30                          // vldr          d19, [pc, #192]
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf3411db3                          // vmul.f32      d17, d17, d19
+  .long  0xed9f8a39                          // vldr          s16, [pc, #228]
+  .long  0xf2422da4                          // vadd.f32      d18, d18, d20
+  .long  0xeddf4b2e                          // vldr          d20, [pc, #184]
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xf2621da1                          // vsub.f32      d17, d18, d17
+  .long  0xf2611d8a                          // vsub.f32      d17, d17, d10
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf3fb1720                          // vcvt.s32.f32  d17, d16
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3612ea0                          // vcgt.f32      d18, d17, d16
+  .long  0xf35421b3                          // vbsl          d18, d20, d19
+  .long  0xeddf4b2b                          // vldr          d20, [pc, #172]
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xeddf2b25                          // vldr          d18, [pc, #148]
+  .long  0xf2601da1                          // vsub.f32      d17, d16, d17
+  .long  0xf2400da4                          // vadd.f32      d16, d16, d20
+  .long  0xf2229da1                          // vsub.f32      d9, d18, d17
+  .long  0xeddf2b23                          // vldr          d18, [pc, #140]
+  .long  0xf3411db2                          // vmul.f32      d17, d17, d18
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf2c4161b                          // vmov.i32      d17, #1258291200
+  .long  0xf2400d8a                          // vadd.f32      d16, d16, d10
+  .long  0xf2402cb1                          // vfma.f32      d18, d16, d17
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4018                          // add           r4, lr, #24
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4010                          // add           r4, lr, #16
+  .long  0xf2401c91                          // vfma.f32      d17, d16, d1
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf3400e81                          // vcge.f32      d16, d16, d1
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf2442da2                          // vadd.f32      d18, d20, d18
+  .long  0xf35101b2                          // vbsl          d16, d17, d18
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xf2400fa3                          // vmax.f32      d16, d16, d19
+  .long  0xf2201fa1                          // vmin.f32      d1, d16, d17
+  .long  0xecbd8b06                          // vpop          {d8-d10}
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x3fdce9a3                          // .word         0x3fdce9a3
+  .long  0x41ddd2fe                          // .word         0x41ddd2fe
+
+HIDDEN _sk_parametric_b_vfp4
+.globl _sk_parametric_b_vfp4
+FUNCTION(_sk_parametric_b_vfp4)
+_sk_parametric_b_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xed2d8b06                          // vpush         {d8-d10}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xeddf3b41                          // vldr          d19, [pc, #260]
+  .long  0xed9f8a4e                          // vldr          s16, [pc, #312]
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xeddf4b42                          // vldr          d20, [pc, #264]
+  .long  0xf4e40c9d                          // vld1.32       {d16[]}, [r4 :32]!
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e400c                          // add           r4, lr, #12
+  .long  0xf2412c92                          // vfma.f32      d18, d17, d2
+  .long  0xf2c71d1f                          // vmov.i32      d17, #8388607
+  .long  0xf24211b1                          // vand          d17, d18, d17
+  .long  0xf2c3171f                          // vorr.i32      d17, #1056964608
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf2019da3                          // vadd.f32      d9, d17, d19
+  .long  0xf2c33614                          // vmov.i32      d19, #872415232
+  .long  0xf3422db3                          // vmul.f32      d18, d18, d19
+  .long  0xeddf3b30                          // vldr          d19, [pc, #192]
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf3411db3                          // vmul.f32      d17, d17, d19
+  .long  0xed9f8a39                          // vldr          s16, [pc, #228]
+  .long  0xf2422da4                          // vadd.f32      d18, d18, d20
+  .long  0xeddf4b2e                          // vldr          d20, [pc, #184]
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xf2621da1                          // vsub.f32      d17, d18, d17
+  .long  0xf2611d8a                          // vsub.f32      d17, d17, d10
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf3fb1720                          // vcvt.s32.f32  d17, d16
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3612ea0                          // vcgt.f32      d18, d17, d16
+  .long  0xf35421b3                          // vbsl          d18, d20, d19
+  .long  0xeddf4b2b                          // vldr          d20, [pc, #172]
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xeddf2b25                          // vldr          d18, [pc, #148]
+  .long  0xf2601da1                          // vsub.f32      d17, d16, d17
+  .long  0xf2400da4                          // vadd.f32      d16, d16, d20
+  .long  0xf2229da1                          // vsub.f32      d9, d18, d17
+  .long  0xeddf2b23                          // vldr          d18, [pc, #140]
+  .long  0xf3411db2                          // vmul.f32      d17, d17, d18
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf2c4161b                          // vmov.i32      d17, #1258291200
+  .long  0xf2400d8a                          // vadd.f32      d16, d16, d10
+  .long  0xf2402cb1                          // vfma.f32      d18, d16, d17
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4018                          // add           r4, lr, #24
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4010                          // add           r4, lr, #16
+  .long  0xf2401c92                          // vfma.f32      d17, d16, d2
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf3400e82                          // vcge.f32      d16, d16, d2
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf2442da2                          // vadd.f32      d18, d20, d18
+  .long  0xf35101b2                          // vbsl          d16, d17, d18
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xf2400fa3                          // vmax.f32      d16, d16, d19
+  .long  0xf2202fa1                          // vmin.f32      d2, d16, d17
+  .long  0xecbd8b06                          // vpop          {d8-d10}
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x3fdce9a3                          // .word         0x3fdce9a3
+  .long  0x41ddd2fe                          // .word         0x41ddd2fe
+
+HIDDEN _sk_parametric_a_vfp4
+.globl _sk_parametric_a_vfp4
+FUNCTION(_sk_parametric_a_vfp4)
+_sk_parametric_a_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xed2d8b06                          // vpush         {d8-d10}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xeddf3b41                          // vldr          d19, [pc, #260]
+  .long  0xed9f8a4e                          // vldr          s16, [pc, #312]
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xeddf4b42                          // vldr          d20, [pc, #264]
+  .long  0xf4e40c9d                          // vld1.32       {d16[]}, [r4 :32]!
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e400c                          // add           r4, lr, #12
+  .long  0xf2412c93                          // vfma.f32      d18, d17, d3
+  .long  0xf2c71d1f                          // vmov.i32      d17, #8388607
+  .long  0xf24211b1                          // vand          d17, d18, d17
+  .long  0xf2c3171f                          // vorr.i32      d17, #1056964608
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf2019da3                          // vadd.f32      d9, d17, d19
+  .long  0xf2c33614                          // vmov.i32      d19, #872415232
+  .long  0xf3422db3                          // vmul.f32      d18, d18, d19
+  .long  0xeddf3b30                          // vldr          d19, [pc, #192]
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf3411db3                          // vmul.f32      d17, d17, d19
+  .long  0xed9f8a39                          // vldr          s16, [pc, #228]
+  .long  0xf2422da4                          // vadd.f32      d18, d18, d20
+  .long  0xeddf4b2e                          // vldr          d20, [pc, #184]
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xf2621da1                          // vsub.f32      d17, d18, d17
+  .long  0xf2611d8a                          // vsub.f32      d17, d17, d10
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf3fb1720                          // vcvt.s32.f32  d17, d16
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3612ea0                          // vcgt.f32      d18, d17, d16
+  .long  0xf35421b3                          // vbsl          d18, d20, d19
+  .long  0xeddf4b2b                          // vldr          d20, [pc, #172]
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xeddf2b25                          // vldr          d18, [pc, #148]
+  .long  0xf2601da1                          // vsub.f32      d17, d16, d17
+  .long  0xf2400da4                          // vadd.f32      d16, d16, d20
+  .long  0xf2229da1                          // vsub.f32      d9, d18, d17
+  .long  0xeddf2b23                          // vldr          d18, [pc, #140]
+  .long  0xf3411db2                          // vmul.f32      d17, d17, d18
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xeec8aa29                          // vdiv.f32      s21, s16, s19
+  .long  0xee88aa09                          // vdiv.f32      s20, s16, s18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf2c4161b                          // vmov.i32      d17, #1258291200
+  .long  0xf2400d8a                          // vadd.f32      d16, d16, d10
+  .long  0xf2402cb1                          // vfma.f32      d18, d16, d17
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4018                          // add           r4, lr, #24
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4010                          // add           r4, lr, #16
+  .long  0xf2401c93                          // vfma.f32      d17, d16, d3
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf3400e83                          // vcge.f32      d16, d16, d3
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf2442da2                          // vadd.f32      d18, d20, d18
+  .long  0xf35101b2                          // vbsl          d16, d17, d18
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xf2400fa3                          // vmax.f32      d16, d16, d19
+  .long  0xf2203fa1                          // vmin.f32      d3, d16, d17
+  .long  0xecbd8b06                          // vpop          {d8-d10}
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3eb444f9                          // .word         0x3eb444f9
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0x3fbfbf75                          // .word         0x3fbfbf75
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0xc2f87377                          // .word         0xc2f87377
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x409af5f8                          // .word         0x409af5f8
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x3fbebc8d                          // .word         0x3fbebc8d
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x42f28c51                          // .word         0x42f28c51
+  .long  0x3fdce9a3                          // .word         0x3fdce9a3
+  .long  0x41ddd2fe                          // .word         0x41ddd2fe
+
+HIDDEN _sk_lab_to_xyz_vfp4
+.globl _sk_lab_to_xyz_vfp4
+FUNCTION(_sk_lab_to_xyz_vfp4)
+_sk_lab_to_xyz_vfp4:
+  .long  0xeddf1b2e                          // vldr          d17, [pc, #184]
+  .long  0xf3c43613                          // vmov.i32      d19, #-1023410176
+  .long  0xeddf0b2a                          // vldr          d16, [pc, #168]
+  .long  0xf2c34f10                          // vmov.f32      d20, #16
+  .long  0xf3401d31                          // vmul.f32      d17, d0, d17
+  .long  0xeddf5b2f                          // vldr          d21, [pc, #188]
+  .long  0xf3412d30                          // vmul.f32      d18, d1, d16
+  .long  0xeddf6b2f                          // vldr          d22, [pc, #188]
+  .long  0xf3420d30                          // vmul.f32      d16, d2, d16
+  .long  0xeddf7b2f                          // vldr          d23, [pc, #188]
+  .long  0xeddf8b30                          // vldr          d24, [pc, #192]
+  .long  0xf2411da4                          // vadd.f32      d17, d17, d20
+  .long  0xeddf4b24                          // vldr          d20, [pc, #144]
+  .long  0xf2422da3                          // vadd.f32      d18, d18, d19
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2400da3                          // vadd.f32      d16, d16, d19
+  .long  0xeddf3b22                          // vldr          d19, [pc, #136]
+  .long  0xf3411db3                          // vmul.f32      d17, d17, d19
+  .long  0xf3422db4                          // vmul.f32      d18, d18, d20
+  .long  0xf3400db5                          // vmul.f32      d16, d16, d21
+  .long  0xf2412da2                          // vadd.f32      d18, d17, d18
+  .long  0xf2610da0                          // vsub.f32      d16, d17, d16
+  .long  0xf3415db1                          // vmul.f32      d21, d17, d17
+  .long  0xf3423db2                          // vmul.f32      d19, d18, d18
+  .long  0xf3404db0                          // vmul.f32      d20, d16, d16
+  .long  0xf3415db5                          // vmul.f32      d21, d17, d21
+  .long  0xf2411da6                          // vadd.f32      d17, d17, d22
+  .long  0xf3423db3                          // vmul.f32      d19, d18, d19
+  .long  0xf3404db4                          // vmul.f32      d20, d16, d20
+  .long  0xf2400da6                          // vadd.f32      d16, d16, d22
+  .long  0xf2422da6                          // vadd.f32      d18, d18, d22
+  .long  0xeddf6b1d                          // vldr          d22, [pc, #116]
+  .long  0xf3639ea7                          // vcgt.f32      d25, d19, d23
+  .long  0xf364aea7                          // vcgt.f32      d26, d20, d23
+  .long  0xf3400db8                          // vmul.f32      d16, d16, d24
+  .long  0xf3422db8                          // vmul.f32      d18, d18, d24
+  .long  0xf35391b2                          // vbsl          d25, d19, d18
+  .long  0xeddf2b19                          // vldr          d18, [pc, #100]
+  .long  0xf354a1b0                          // vbsl          d26, d20, d16
+  .long  0xf3251ea7                          // vcgt.f32      d1, d21, d23
+  .long  0xf3090db6                          // vmul.f32      d0, d25, d22
+  .long  0xf30a2db2                          // vmul.f32      d2, d26, d18
+  .long  0xf3410db8                          // vmul.f32      d16, d17, d24
+  .long  0xf31511b0                          // vbsl          d1, d21, d16
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x42c80000                          // .word         0x42c80000
+  .long  0x42c80000                          // .word         0x42c80000
+  .long  0x3b03126f                          // .word         0x3b03126f
+  .long  0x3b03126f                          // .word         0x3b03126f
+  .long  0x3c0d3dcb                          // .word         0x3c0d3dcb
+  .long  0x3c0d3dcb                          // .word         0x3c0d3dcb
+  .long  0x3ba3d70a                          // .word         0x3ba3d70a
+  .long  0x3ba3d70a                          // .word         0x3ba3d70a
+  .long  0xbe0d3dcb                          // .word         0xbe0d3dcb
+  .long  0xbe0d3dcb                          // .word         0xbe0d3dcb
+  .long  0x3c1118c2                          // .word         0x3c1118c2
+  .long  0x3c1118c2                          // .word         0x3c1118c2
+  .long  0x3e038050                          // .word         0x3e038050
+  .long  0x3e038050                          // .word         0x3e038050
+  .long  0x3f76d71f                          // .word         0x3f76d71f
+  .long  0x3f76d71f                          // .word         0x3f76d71f
+  .long  0x3f5340f6                          // .word         0x3f5340f6
+  .long  0x3f5340f6                          // .word         0x3f5340f6
+
+HIDDEN _sk_load_a8_vfp4
+.globl _sk_load_a8_vfp4
+FUNCTION(_sk_load_a8_vfp4)
+_sk_load_a8_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de00c                          // ldr           lr, [sp, #12]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc002                          // add           ip, ip, r2
+  .long  0x0a000012                          // beq           28d4 <sk_load_a8_vfp4+0x6c>
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xe1cdc0b0                          // strh          ip, [sp]
+  .long  0xe1a0c00d                          // mov           ip, sp
+  .long  0xf4ec041f                          // vld1.16       {d16[0]}, [ip :16]
+  .long  0xf3c80a30                          // vmovl.u8      q8, d16
+  .long  0xf3900a30                          // vmovl.u16     q0, d16
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xeddf1b0f                          // vldr          d17, [pc, #60]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2400130                          // vand          d16, d0, d16
+  .long  0xf2800010                          // vmov.i32      d0, #0
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf2801010                          // vmov.i32      d1, #0
+  .long  0xf2802010                          // vmov.i32      d2, #0
+  .long  0xf3003db1                          // vmul.f32      d3, d16, d17
+  .long  0xe28dd004                          // add           sp, sp, #4
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe5dcc000                          // ldrb          ip, [ip]
+  .long  0xeddf0a04                          // vldr          s1, [pc, #16]
+  .long  0xee00ca10                          // vmov          s0, ip
+  .long  0xeaffffee                          // b             28a0 <sk_load_a8_vfp4+0x38>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_gather_a8_vfp4
+.globl _sk_gather_a8_vfp4
+FUNCTION(_sk_gather_a8_vfp4)
+_sk_gather_a8_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2800010                          // vmov.i32      d0, #0
+  .long  0xe49e4008                          // ldr           r4, [lr], #8
+  .long  0xf2801010                          // vmov.i32      d1, #0
+  .long  0xf2802010                          // vmov.i32      d2, #0
+  .long  0xf4ee2c9f                          // vld1.32       {d18[]}, [lr :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xee11eb90                          // vmov.32       lr, d17[0]
+  .long  0xee315b90                          // vmov.32       r5, d17[1]
+  .long  0xf3c7101f                          // vmov.i32      d17, #255
+  .long  0xe7d4e00e                          // ldrb          lr, [r4, lr]
+  .long  0xe7d44005                          // ldrb          r4, [r4, r5]
+  .long  0xee00eb90                          // vmov.32       d16[0], lr
+  .long  0xee204b90                          // vmov.32       d16[1], r4
+  .long  0xf24001b1                          // vand          d16, d16, d17
+  .long  0xeddf1b04                          // vldr          d17, [pc, #16]
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3003db1                          // vmul.f32      d3, d16, d17
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+
+HIDDEN _sk_store_a8_vfp4
+.globl _sk_store_a8_vfp4
+FUNCTION(_sk_store_a8_vfp4)
+_sk_store_a8_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xeddf0b13                          // vldr          d16, [pc, #76]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf2431c30                          // vfma.f32      d17, d3, d16
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc002                          // add           ip, ip, r2
+  .long  0xf3fb07a1                          // vcvt.u32.f32  d16, d17
+  .long  0x0a000007                          // beq           29b4 <sk_store_a8_vfp4+0x4c>
+  .long  0xee30eb90                          // vmov.32       lr, d16[1]
+  .long  0xee104b90                          // vmov.32       r4, d16[0]
+  .long  0xe5cce001                          // strb          lr, [ip, #1]
+  .long  0xe5cc4000                          // strb          r4, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe5cce000                          // strb          lr, [ip]
+  .long  0xeafffff8                          // b             29a4 <sk_store_a8_vfp4+0x3c>
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x437f0000                          // .word         0x437f0000
+
+HIDDEN _sk_load_g8_vfp4
+.globl _sk_load_g8_vfp4
+FUNCTION(_sk_load_g8_vfp4)
+_sk_load_g8_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de00c                          // ldr           lr, [sp, #12]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc002                          // add           ip, ip, r2
+  .long  0x0a000012                          // beq           2a34 <sk_load_g8_vfp4+0x6c>
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xe1cdc0b0                          // strh          ip, [sp]
+  .long  0xe1a0c00d                          // mov           ip, sp
+  .long  0xf4ec041f                          // vld1.16       {d16[0]}, [ip :16]
+  .long  0xf3c80a30                          // vmovl.u8      q8, d16
+  .long  0xf3900a30                          // vmovl.u16     q0, d16
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xeddf1b0f                          // vldr          d17, [pc, #60]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2400130                          // vand          d16, d0, d16
+  .long  0xf2873f10                          // vmov.f32      d3, #1
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3000db1                          // vmul.f32      d0, d16, d17
+  .long  0xf2201110                          // vorr          d1, d0, d0
+  .long  0xf2202110                          // vorr          d2, d0, d0
+  .long  0xe28dd004                          // add           sp, sp, #4
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe5dcc000                          // ldrb          ip, [ip]
+  .long  0xeddf0a04                          // vldr          s1, [pc, #16]
+  .long  0xee00ca10                          // vmov          s0, ip
+  .long  0xeaffffee                          // b             2a00 <sk_load_g8_vfp4+0x38>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_gather_g8_vfp4
+.globl _sk_gather_g8_vfp4
+FUNCTION(_sk_gather_g8_vfp4)
+_sk_gather_g8_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2873f10                          // vmov.f32      d3, #1
+  .long  0xe49e4008                          // ldr           r4, [lr], #8
+  .long  0xf4ee2c9f                          // vld1.32       {d18[]}, [lr :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xee11eb90                          // vmov.32       lr, d17[0]
+  .long  0xee315b90                          // vmov.32       r5, d17[1]
+  .long  0xf3c7101f                          // vmov.i32      d17, #255
+  .long  0xe7d4e00e                          // ldrb          lr, [r4, lr]
+  .long  0xe7d44005                          // ldrb          r4, [r4, r5]
+  .long  0xee00eb90                          // vmov.32       d16[0], lr
+  .long  0xee204b90                          // vmov.32       d16[1], r4
+  .long  0xf24001b1                          // vand          d16, d16, d17
+  .long  0xeddf1b06                          // vldr          d17, [pc, #24]
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3000db1                          // vmul.f32      d0, d16, d17
+  .long  0xf2201110                          // vorr          d1, d0, d0
+  .long  0xf2202110                          // vorr          d2, d0, d0
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+
+HIDDEN _sk_gather_i8_vfp4
+.globl _sk_gather_i8_vfp4
+FUNCTION(_sk_gather_i8_vfp4)
+_sk_gather_i8_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe1a0c001                          // mov           ip, r1
+  .long  0xe491e004                          // ldr           lr, [r1], #4
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0x028c1008                          // addeq         r1, ip, #8
+  .long  0x059c4004                          // ldreq         r4, [ip, #4]
+  .long  0xe494c008                          // ldr           ip, [r4], #8
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xee114b90                          // vmov.32       r4, d17[0]
+  .long  0xee315b90                          // vmov.32       r5, d17[1]
+  .long  0xf3c7101f                          // vmov.i32      d17, #255
+  .long  0xe7dc4004                          // ldrb          r4, [ip, r4]
+  .long  0xe7dc5005                          // ldrb          r5, [ip, r5]
+  .long  0xee004b90                          // vmov.32       d16[0], r4
+  .long  0xe59e4004                          // ldr           r4, [lr, #4]
+  .long  0xee205b90                          // vmov.32       d16[1], r5
+  .long  0xf24001b1                          // vand          d16, d16, d17
+  .long  0xee105b90                          // vmov.32       r5, d16[0]
+  .long  0xee30cb90                          // vmov.32       ip, d16[1]
+  .long  0xe0845105                          // add           r5, r4, r5, lsl #2
+  .long  0xf4e5083f                          // vld1.32       {d16[0]}, [r5 :32]
+  .long  0xe084510c                          // add           r5, r4, ip, lsl #2
+  .long  0xf4e508bf                          // vld1.32       {d16[1]}, [r5 :32]
+  .long  0xf24021b1                          // vand          d18, d16, d17
+  .long  0xf3f83030                          // vshr.u32      d19, d16, #8
+  .long  0xf3e84030                          // vshr.u32      d20, d16, #24
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3f00030                          // vshr.u32      d16, d16, #16
+  .long  0xf24331b1                          // vand          d19, d19, d17
+  .long  0xf24001b1                          // vand          d16, d16, d17
+  .long  0xeddf1b0a                          // vldr          d17, [pc, #40]
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb4624                          // vcvt.f32.s32  d20, d20
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3020db1                          // vmul.f32      d0, d18, d17
+  .long  0xf3043db1                          // vmul.f32      d3, d20, d17
+  .long  0xf3031db1                          // vmul.f32      d1, d19, d17
+  .long  0xf3002db1                          // vmul.f32      d2, d16, d17
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+
+HIDDEN _sk_load_565_vfp4
+.globl _sk_load_565_vfp4
+FUNCTION(_sk_load_565_vfp4)
+_sk_load_565_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de00c                          // ldr           lr, [sp, #12]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc082                          // add           ip, ip, r2, lsl #1
+  .long  0x0a00001a                          // beq           2c14 <sk_load_565_vfp4+0x8c>
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe58dc000                          // str           ip, [sp]
+  .long  0xe1a0c00d                          // mov           ip, sp
+  .long  0xf4ec083f                          // vld1.32       {d16[0]}, [ip :32]
+  .long  0xf3900a30                          // vmovl.u16     q0, d16
+  .long  0xf3c70218                          // vmov.i32      d16, #63488
+  .long  0xe3a0ce7e                          // mov           ip, #2016
+  .long  0xf2c1101f                          // vmov.i32      d17, #31
+  .long  0xee82cb90                          // vdup.32       d18, ip
+  .long  0xf2400130                          // vand          d16, d0, d16
+  .long  0xeddf3b14                          // vldr          d19, [pc, #80]
+  .long  0xf2402132                          // vand          d18, d0, d18
+  .long  0xeddf4b14                          // vldr          d20, [pc, #80]
+  .long  0xf2401131                          // vand          d17, d0, d17
+  .long  0xeddf5b14                          // vldr          d21, [pc, #80]
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf2873f10                          // vmov.f32      d3, #1
+  .long  0xf3000db3                          // vmul.f32      d0, d16, d19
+  .long  0xf3021db4                          // vmul.f32      d1, d18, d20
+  .long  0xf3012db5                          // vmul.f32      d2, d17, d21
+  .long  0xe28dd004                          // add           sp, sp, #4
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xeddf0a08                          // vldr          s1, [pc, #32]
+  .long  0xee00ca10                          // vmov          s0, ip
+  .long  0xeaffffe5                          // b             2bbc <sk_load_565_vfp4+0x34>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x37842108                          // .word         0x37842108
+  .long  0x37842108                          // .word         0x37842108
+  .long  0x3a020821                          // .word         0x3a020821
+  .long  0x3a020821                          // .word         0x3a020821
+  .long  0x3d042108                          // .word         0x3d042108
+  .long  0x3d042108                          // .word         0x3d042108
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_gather_565_vfp4
+.globl _sk_gather_565_vfp4
+FUNCTION(_sk_gather_565_vfp4)
+_sk_gather_565_vfp4:
+  .long  0xe92d4070                          // push          {r4, r5, r6, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xeddf4b20                          // vldr          d20, [pc, #128]
+  .long  0xeddf5b21                          // vldr          d21, [pc, #132]
+  .long  0xf2873f10                          // vmov.f32      d3, #1
+  .long  0xe49e4008                          // ldr           r4, [lr], #8
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4ee2c9f                          // vld1.32       {d18[]}, [lr :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xf2c1201f                          // vmov.i32      d18, #31
+  .long  0xee11eb90                          // vmov.32       lr, d17[0]
+  .long  0xee316b90                          // vmov.32       r6, d17[1]
+  .long  0xf3c71218                          // vmov.i32      d17, #63488
+  .long  0xe084508e                          // add           r5, r4, lr, lsl #1
+  .long  0xe0846086                          // add           r6, r4, r6, lsl #1
+  .long  0xe1d550b0                          // ldrh          r5, [r5]
+  .long  0xe1d660b0                          // ldrh          r6, [r6]
+  .long  0xee005b90                          // vmov.32       d16[0], r5
+  .long  0xee206b90                          // vmov.32       d16[1], r6
+  .long  0xe3a06e7e                          // mov           r6, #2016
+  .long  0xee836b90                          // vdup.32       d19, r6
+  .long  0xf24011b1                          // vand          d17, d16, d17
+  .long  0xf24031b3                          // vand          d19, d16, d19
+  .long  0xf24001b2                          // vand          d16, d16, d18
+  .long  0xf3fb2623                          // vcvt.f32.s32  d18, d19
+  .long  0xeddf3b06                          // vldr          d19, [pc, #24]
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3021db4                          // vmul.f32      d1, d18, d20
+  .long  0xf3010db3                          // vmul.f32      d0, d17, d19
+  .long  0xf3002db5                          // vmul.f32      d2, d16, d21
+  .long  0xe8bd4070                          // pop           {r4, r5, r6, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x37842108                          // .word         0x37842108
+  .long  0x37842108                          // .word         0x37842108
+  .long  0x3a020821                          // .word         0x3a020821
+  .long  0x3a020821                          // .word         0x3a020821
+  .long  0x3d042108                          // .word         0x3d042108
+  .long  0x3d042108                          // .word         0x3d042108
+
+HIDDEN _sk_store_565_vfp4
+.globl _sk_store_565_vfp4
+FUNCTION(_sk_store_565_vfp4)
+_sk_store_565_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xf2c30f1f                          // vmov.f32      d16, #31
+  .long  0xeddf1b1c                          // vldr          d17, [pc, #112]
+  .long  0xf2c3361f                          // vmov.i32      d19, #1056964608
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2413c31                          // vfma.f32      d19, d1, d17
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf2401c30                          // vfma.f32      d17, d0, d16
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xf2422c30                          // vfma.f32      d18, d2, d16
+  .long  0xe08cc082                          // add           ip, ip, r2, lsl #1
+  .long  0xf3fb07a3                          // vcvt.u32.f32  d16, d19
+  .long  0xf3fb17a1                          // vcvt.u32.f32  d17, d17
+  .long  0xf3fb27a2                          // vcvt.u32.f32  d18, d18
+  .long  0xf2e50530                          // vshl.s32      d16, d16, #5
+  .long  0xf2eb1531                          // vshl.s32      d17, d17, #11
+  .long  0xf26001b1                          // vorr          d16, d16, d17
+  .long  0xf26001b2                          // vorr          d16, d16, d18
+  .long  0x0a000005                          // beq           2d60 <sk_store_565_vfp4+0x70>
+  .long  0xf3f60121                          // vuzp.16       d16, d17
+  .long  0xf4cc080f                          // vst1.32       {d16[0]}, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe1cce0b0                          // strh          lr, [ip]
+  .long  0xeafffff8                          // b             2d50 <sk_store_565_vfp4+0x60>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x427c0000                          // .word         0x427c0000
+  .long  0x427c0000                          // .word         0x427c0000
+
+HIDDEN _sk_load_4444_vfp4
+.globl _sk_load_4444_vfp4
+FUNCTION(_sk_load_4444_vfp4)
+_sk_load_4444_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de00c                          // ldr           lr, [sp, #12]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc082                          // add           ip, ip, r2, lsl #1
+  .long  0x0a00001d                          // beq           2e10 <sk_load_4444_vfp4+0x98>
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe58dc000                          // str           ip, [sp]
+  .long  0xe1a0c00d                          // mov           ip, sp
+  .long  0xf4ec083f                          // vld1.32       {d16[0]}, [ip :32]
+  .long  0xf3900a30                          // vmovl.u16     q0, d16
+  .long  0xf3c70210                          // vmov.i32      d16, #61440
+  .long  0xeddf4b1a                          // vldr          d20, [pc, #104]
+  .long  0xf2c0121f                          // vmov.i32      d17, #3840
+  .long  0xeddf5b1a                          // vldr          d21, [pc, #104]
+  .long  0xf3c72010                          // vmov.i32      d18, #240
+  .long  0xeddf6b1a                          // vldr          d22, [pc, #104]
+  .long  0xf2400130                          // vand          d16, d0, d16
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf2c0301f                          // vmov.i32      d19, #15
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2401131                          // vand          d17, d0, d17
+  .long  0xf2402132                          // vand          d18, d0, d18
+  .long  0xf2403133                          // vand          d19, d0, d19
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3000db4                          // vmul.f32      d0, d16, d20
+  .long  0xeddf0b0f                          // vldr          d16, [pc, #60]
+  .long  0xf3011db5                          // vmul.f32      d1, d17, d21
+  .long  0xf3022db6                          // vmul.f32      d2, d18, d22
+  .long  0xf3033db0                          // vmul.f32      d3, d19, d16
+  .long  0xe28dd004                          // add           sp, sp, #4
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe1dcc0b0                          // ldrh          ip, [ip]
+  .long  0xeddf0a09                          // vldr          s1, [pc, #36]
+  .long  0xee00ca10                          // vmov          s0, ip
+  .long  0xeaffffe2                          // b             2dac <sk_load_4444_vfp4+0x34>
+  .long  0x37888889                          // .word         0x37888889
+  .long  0x37888889                          // .word         0x37888889
+  .long  0x39888889                          // .word         0x39888889
+  .long  0x39888889                          // .word         0x39888889
+  .long  0x3b888889                          // .word         0x3b888889
+  .long  0x3b888889                          // .word         0x3b888889
+  .long  0x3d888889                          // .word         0x3d888889
+  .long  0x3d888889                          // .word         0x3d888889
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_gather_4444_vfp4
+.globl _sk_gather_4444_vfp4
+FUNCTION(_sk_gather_4444_vfp4)
+_sk_gather_4444_vfp4:
+  .long  0xe92d4070                          // push          {r4, r5, r6, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xeddf5b24                          // vldr          d21, [pc, #144]
+  .long  0xf3c73010                          // vmov.i32      d19, #240
+  .long  0xeddf6b24                          // vldr          d22, [pc, #144]
+  .long  0xe49e4008                          // ldr           r4, [lr], #8
+  .long  0xf2c0401f                          // vmov.i32      d20, #15
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf4ee2c9f                          // vld1.32       {d18[]}, [lr :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xf2c0221f                          // vmov.i32      d18, #3840
+  .long  0xee11eb90                          // vmov.32       lr, d17[0]
+  .long  0xee316b90                          // vmov.32       r6, d17[1]
+  .long  0xf3c71210                          // vmov.i32      d17, #61440
+  .long  0xe084508e                          // add           r5, r4, lr, lsl #1
+  .long  0xe0846086                          // add           r6, r4, r6, lsl #1
+  .long  0xe1d550b0                          // ldrh          r5, [r5]
+  .long  0xe1d660b0                          // ldrh          r6, [r6]
+  .long  0xee005b90                          // vmov.32       d16[0], r5
+  .long  0xee206b90                          // vmov.32       d16[1], r6
+  .long  0xf24011b1                          // vand          d17, d16, d17
+  .long  0xf24021b2                          // vand          d18, d16, d18
+  .long  0xf24031b3                          // vand          d19, d16, d19
+  .long  0xf24001b4                          // vand          d16, d16, d20
+  .long  0xeddf4b0b                          // vldr          d20, [pc, #44]
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3010db4                          // vmul.f32      d0, d17, d20
+  .long  0xeddf1b0b                          // vldr          d17, [pc, #44]
+  .long  0xf3021db5                          // vmul.f32      d1, d18, d21
+  .long  0xf3032db6                          // vmul.f32      d2, d19, d22
+  .long  0xf3003db1                          // vmul.f32      d3, d16, d17
+  .long  0xe8bd4070                          // pop           {r4, r5, r6, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x37888889                          // .word         0x37888889
+  .long  0x37888889                          // .word         0x37888889
+  .long  0x39888889                          // .word         0x39888889
+  .long  0x39888889                          // .word         0x39888889
+  .long  0x3b888889                          // .word         0x3b888889
+  .long  0x3b888889                          // .word         0x3b888889
+  .long  0x3d888889                          // .word         0x3d888889
+  .long  0x3d888889                          // .word         0x3d888889
+
+HIDDEN _sk_store_4444_vfp4
+.globl _sk_store_4444_vfp4
+FUNCTION(_sk_store_4444_vfp4)
+_sk_store_4444_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xf2c20f1e                          // vmov.f32      d16, #15
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xf2c3361f                          // vmov.i32      d19, #1056964608
+  .long  0xf2402c30                          // vfma.f32      d18, d0, d16
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xf2413c30                          // vfma.f32      d19, d1, d16
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xf2c3461f                          // vmov.i32      d20, #1056964608
+  .long  0xe08cc082                          // add           ip, ip, r2, lsl #1
+  .long  0xf2424c30                          // vfma.f32      d20, d2, d16
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf2431c30                          // vfma.f32      d17, d3, d16
+  .long  0xf3fb07a2                          // vcvt.u32.f32  d16, d18
+  .long  0xf3fb27a3                          // vcvt.u32.f32  d18, d19
+  .long  0xf3fb37a4                          // vcvt.u32.f32  d19, d20
+  .long  0xf2ec0530                          // vshl.s32      d16, d16, #12
+  .long  0xf2e82532                          // vshl.s32      d18, d18, #8
+  .long  0xf3fb17a1                          // vcvt.u32.f32  d17, d17
+  .long  0xf2e43533                          // vshl.s32      d19, d19, #4
+  .long  0xf26201b0                          // vorr          d16, d18, d16
+  .long  0xf26001b3                          // vorr          d16, d16, d19
+  .long  0xf26001b1                          // vorr          d16, d16, d17
+  .long  0x0a000005                          // beq           2f88 <sk_store_4444_vfp4+0x80>
+  .long  0xf3f60121                          // vuzp.16       d16, d17
+  .long  0xf4cc080f                          // vst1.32       {d16[0]}, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe1cce0b0                          // strh          lr, [ip]
+  .long  0xeafffff8                          // b             2f78 <sk_store_4444_vfp4+0x70>
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_load_8888_vfp4
+.globl _sk_load_8888_vfp4
+FUNCTION(_sk_load_8888_vfp4)
+_sk_load_8888_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xe08cc102                          // add           ip, ip, r2, lsl #2
+  .long  0x0a000014                          // beq           3008 <sk_load_8888_vfp4+0x70>
+  .long  0xed9c0b00                          // vldr          d0, [ip]
+  .long  0xf3c7001f                          // vmov.i32      d16, #255
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf3f81010                          // vshr.u32      d17, d0, #8
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf3f03010                          // vshr.u32      d19, d0, #16
+  .long  0xf3e82010                          // vshr.u32      d18, d0, #24
+  .long  0xf24111b0                          // vand          d17, d17, d16
+  .long  0xf2404130                          // vand          d20, d0, d16
+  .long  0xf24301b0                          // vand          d16, d19, d16
+  .long  0xf3fb3624                          // vcvt.f32.s32  d19, d20
+  .long  0xeddf4b0c                          // vldr          d20, [pc, #48]
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3030db4                          // vmul.f32      d0, d19, d20
+  .long  0xf3023db4                          // vmul.f32      d3, d18, d20
+  .long  0xf3011db4                          // vmul.f32      d1, d17, d20
+  .long  0xf3002db4                          // vmul.f32      d2, d16, d20
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xeddf0a04                          // vldr          s1, [pc, #16]
+  .long  0xed9c0a00                          // vldr          s0, [ip]
+  .long  0xeaffffe8                          // b             2fb8 <sk_load_8888_vfp4+0x20>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x00000000                          // .word         0x00000000
+  .long  0xe320f000                          // .word         0xe320f000
+
+HIDDEN _sk_gather_8888_vfp4
+.globl _sk_gather_8888_vfp4
+FUNCTION(_sk_gather_8888_vfp4)
+_sk_gather_8888_vfp4:
+  .long  0xe92d4070                          // push          {r4, r5, r6, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe49e4008                          // ldr           r4, [lr], #8
+  .long  0xf4ee2c9f                          // vld1.32       {d18[]}, [lr :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xee11eb90                          // vmov.32       lr, d17[0]
+  .long  0xee316b90                          // vmov.32       r6, d17[1]
+  .long  0xf3c7101f                          // vmov.i32      d17, #255
+  .long  0xe084510e                          // add           r5, r4, lr, lsl #2
+  .long  0xe0846106                          // add           r6, r4, r6, lsl #2
+  .long  0xf4e5083f                          // vld1.32       {d16[0]}, [r5 :32]
+  .long  0xf4e608bf                          // vld1.32       {d16[1]}, [r6 :32]
+  .long  0xf24021b1                          // vand          d18, d16, d17
+  .long  0xf3f83030                          // vshr.u32      d19, d16, #8
+  .long  0xf3e84030                          // vshr.u32      d20, d16, #24
+  .long  0xf3f00030                          // vshr.u32      d16, d16, #16
+  .long  0xf24331b1                          // vand          d19, d19, d17
+  .long  0xf24001b1                          // vand          d16, d16, d17
+  .long  0xeddf1b0a                          // vldr          d17, [pc, #40]
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb4624                          // vcvt.f32.s32  d20, d20
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3020db1                          // vmul.f32      d0, d18, d17
+  .long  0xf3043db1                          // vmul.f32      d3, d20, d17
+  .long  0xf3031db1                          // vmul.f32      d1, d19, d17
+  .long  0xf3002db1                          // vmul.f32      d2, d16, d17
+  .long  0xe8bd4070                          // pop           {r4, r5, r6, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3b808081                          // .word         0x3b808081
+  .long  0x3b808081                          // .word         0x3b808081
+
+HIDDEN _sk_store_8888_vfp4
+.globl _sk_store_8888_vfp4
+FUNCTION(_sk_store_8888_vfp4)
+_sk_store_8888_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xeddf0b1f                          // vldr          d16, [pc, #124]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xf2412c30                          // vfma.f32      d18, d1, d16
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c3361f                          // vmov.i32      d19, #1056964608
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf2423c30                          // vfma.f32      d19, d2, d16
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xf2c3461f                          // vmov.i32      d20, #1056964608
+  .long  0xe35e0001                          // cmp           lr, #1
+  .long  0xf2401c30                          // vfma.f32      d17, d0, d16
+  .long  0xe08cc102                          // add           ip, ip, r2, lsl #2
+  .long  0xf2434c30                          // vfma.f32      d20, d3, d16
+  .long  0xf3fb07a2                          // vcvt.u32.f32  d16, d18
+  .long  0xf3fb27a3                          // vcvt.u32.f32  d18, d19
+  .long  0xf3fb17a1                          // vcvt.u32.f32  d17, d17
+  .long  0xf3fb37a4                          // vcvt.u32.f32  d19, d20
+  .long  0xf2e80530                          // vshl.s32      d16, d16, #8
+  .long  0xf2f02532                          // vshl.s32      d18, d18, #16
+  .long  0xf26001b1                          // vorr          d16, d16, d17
+  .long  0xf2f81533                          // vshl.s32      d17, d19, #24
+  .long  0xf26001b2                          // vorr          d16, d16, d18
+  .long  0xf26001b1                          // vorr          d16, d16, d17
+  .long  0x0a000004                          // beq           3134 <sk_store_8888_vfp4+0x7c>
+  .long  0xedcc0b00                          // vstr          d16, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xf4cc083f                          // vst1.32       {d16[0]}, [ip :32]
+  .long  0xeafffff9                          // b             3124 <sk_store_8888_vfp4+0x6c>
+  .long  0xe320f000                          // nop           {0}
+  .long  0x437f0000                          // .word         0x437f0000
+  .long  0x437f0000                          // .word         0x437f0000
+
+HIDDEN _sk_load_f16_vfp4
+.globl _sk_load_f16_vfp4
+FUNCTION(_sk_load_f16_vfp4)
+_sk_load_f16_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xe08cc182                          // add           ip, ip, r2, lsl #3
+  .long  0xf4ec070f                          // vld4.16       {d16[0],d17[0],d18[0],d19[0]}, [ip]
+  .long  0x1a000001                          // bne           3170 <sk_load_f16_vfp4+0x28>
+  .long  0xe28cc008                          // add           ip, ip, #8
+  .long  0xf4ec074f                          // vld4.16       {d16[1],d17[1],d18[1],d19[1]}, [ip]
+  .long  0xf3b60720                          // vcvt.f32.f16  q0, d16
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf3b62722                          // vcvt.f32.f16  q1, d18
+  .long  0xf3f64721                          // vcvt.f32.f16  q10, d17
+  .long  0xf3f60723                          // vcvt.f32.f16  q8, d19
+  .long  0xf22411b4                          // vorr          d1, d20, d20
+  .long  0xf22031b0                          // vorr          d3, d16, d16
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_gather_f16_vfp4
+.globl _sk_gather_f16_vfp4
+FUNCTION(_sk_gather_f16_vfp4)
+_sk_gather_f16_vfp4:
+  .long  0xe92d4c10                          // push          {r4, sl, fp, lr}
+  .long  0xe28db008                          // add           fp, sp, #8
+  .long  0xe24dd020                          // sub           sp, sp, #32
+  .long  0xe7c3d01f                          // bfc           sp, #0, #4
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf3fb0701                          // vcvt.s32.f32  d16, d1
+  .long  0xf3fb1700                          // vcvt.s32.f32  d17, d0
+  .long  0xe49ce008                          // ldr           lr, [ip], #8
+  .long  0xf4ec2c9f                          // vld1.32       {d18[]}, [ip :32]
+  .long  0xf26219a0                          // vmla.i32      d17, d18, d16
+  .long  0xee314b90                          // vmov.32       r4, d17[1]
+  .long  0xee11cb90                          // vmov.32       ip, d17[0]
+  .long  0xe08e4184                          // add           r4, lr, r4, lsl #3
+  .long  0xe08ec18c                          // add           ip, lr, ip, lsl #3
+  .long  0xedd41b00                          // vldr          d17, [r4]
+  .long  0xe28d4010                          // add           r4, sp, #16
+  .long  0xeddc0b00                          // vldr          d16, [ip]
+  .long  0xe59bc008                          // ldr           ip, [fp, #8]
+  .long  0xf4440aef                          // vst1.64       {d16-d17}, [r4 :128]
+  .long  0xf4e4071f                          // vld4.16       {d16[0],d17[0],d18[0],d19[0]}, [r4 :64]
+  .long  0xe3844008                          // orr           r4, r4, #8
+  .long  0xf4e4075f                          // vld4.16       {d16[1],d17[1],d18[1],d19[1]}, [r4 :64]
+  .long  0xf3b60720                          // vcvt.f32.f16  q0, d16
+  .long  0xf3b62722                          // vcvt.f32.f16  q1, d18
+  .long  0xe5914004                          // ldr           r4, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe58dc000                          // str           ip, [sp]
+  .long  0xf3f64721                          // vcvt.f32.f16  q10, d17
+  .long  0xf3f60723                          // vcvt.f32.f16  q8, d19
+  .long  0xf22411b4                          // vorr          d1, d20, d20
+  .long  0xf22031b0                          // vorr          d3, d16, d16
+  .long  0xe12fff34                          // blx           r4
+  .long  0xe24bd008                          // sub           sp, fp, #8
+  .long  0xe8bd8c10                          // pop           {r4, sl, fp, pc}
+
+HIDDEN _sk_store_f16_vfp4
+.globl _sk_store_f16_vfp4
+FUNCTION(_sk_store_f16_vfp4)
+_sk_store_f16_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xf2630113                          // vorr          d16, d3, d3
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2612111                          // vorr          d18, d1, d1
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xf3f67620                          // vcvt.f16.f32  d23, q8
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xf3f66602                          // vcvt.f16.f32  d22, q1
+  .long  0xe08cc182                          // add           ip, ip, r2, lsl #3
+  .long  0xf3f65622                          // vcvt.f16.f32  d21, q9
+  .long  0xf3f64600                          // vcvt.f16.f32  d20, q0
+  .long  0xf4cc470f                          // vst4.16       {d20[0],d21[0],d22[0],d23[0]}, [ip]
+  .long  0x1a000001                          // bne           3260 <sk_store_f16_vfp4+0x40>
+  .long  0xe28cc008                          // add           ip, ip, #8
+  .long  0xf4cc474f                          // vst4.16       {d20[1],d21[1],d22[1],d23[1]}, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf22211b2                          // vorr          d1, d18, d18
+  .long  0xf22031b0                          // vorr          d3, d16, d16
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_load_u16_be_vfp4
+.globl _sk_load_u16_be_vfp4
+FUNCTION(_sk_load_u16_be_vfp4)
+_sk_load_u16_be_vfp4:
+  .long  0xe92d4bf0                          // push          {r4, r5, r6, r7, r8, r9, fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de020                          // ldr           lr, [sp, #32]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xe08cc182                          // add           ip, ip, r2, lsl #3
+  .long  0xf4ec070f                          // vld4.16       {d16[0],d17[0],d18[0],d19[0]}, [ip]
+  .long  0x1a000001                          // bne           32a0 <sk_load_u16_be_vfp4+0x28>
+  .long  0xe28cc008                          // add           ip, ip, #8
+  .long  0xf4ec074f                          // vld4.16       {d16[1],d17[1],d18[1],d19[1]}, [ip]
+  .long  0xee90cbb0                          // vmov.u16      ip, d16[0]
+  .long  0xee937bb0                          // vmov.u16      r7, d19[0]
+  .long  0xee92ebb0                          // vmov.u16      lr, d18[0]
+  .long  0xee914bb0                          // vmov.u16      r4, d17[0]
+  .long  0xee905bf0                          // vmov.u16      r5, d16[1]
+  .long  0xee939bf0                          // vmov.u16      r9, d19[1]
+  .long  0xf3c73c1f                          // vmov.i32      d19, #65535
+  .long  0xee926bf0                          // vmov.u16      r6, d18[1]
+  .long  0xee918bf0                          // vmov.u16      r8, d17[1]
+  .long  0xee00cb90                          // vmov.32       d16[0], ip
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xee01eb90                          // vmov.32       d17[0], lr
+  .long  0xee024b90                          // vmov.32       d18[0], r4
+  .long  0xee047b90                          // vmov.32       d20[0], r7
+  .long  0xee205b90                          // vmov.32       d16[1], r5
+  .long  0xf24051b3                          // vand          d21, d16, d19
+  .long  0xee216b90                          // vmov.32       d17[1], r6
+  .long  0xf2e80530                          // vshl.s32      d16, d16, #8
+  .long  0xee228b90                          // vmov.32       d18[1], r8
+  .long  0xf24161b3                          // vand          d22, d17, d19
+  .long  0xf3f85035                          // vshr.u32      d21, d21, #8
+  .long  0xee249b90                          // vmov.32       d20[1], r9
+  .long  0xf24271b3                          // vand          d23, d18, d19
+  .long  0xf26001b5                          // vorr          d16, d16, d21
+  .long  0xf24481b3                          // vand          d24, d20, d19
+  .long  0xf2e82532                          // vshl.s32      d18, d18, #8
+  .long  0xf3f87037                          // vshr.u32      d23, d23, #8
+  .long  0xf2e81531                          // vshl.s32      d17, d17, #8
+  .long  0xf3f86036                          // vshr.u32      d22, d22, #8
+  .long  0xf2e84534                          // vshl.s32      d20, d20, #8
+  .long  0xf3f85038                          // vshr.u32      d21, d24, #8
+  .long  0xf26221b7                          // vorr          d18, d18, d23
+  .long  0xf26111b6                          // vorr          d17, d17, d22
+  .long  0xf26441b5                          // vorr          d20, d20, d21
+  .long  0xf24001b3                          // vand          d16, d16, d19
+  .long  0xf24221b3                          // vand          d18, d18, d19
+  .long  0xf24111b3                          // vand          d17, d17, d19
+  .long  0xf24431b3                          // vand          d19, d20, d19
+  .long  0xeddf4b09                          // vldr          d20, [pc, #36]
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3fb26a2                          // vcvt.f32.u32  d18, d18
+  .long  0xf3fb16a1                          // vcvt.f32.u32  d17, d17
+  .long  0xf3fb36a3                          // vcvt.f32.u32  d19, d19
+  .long  0xf3000db4                          // vmul.f32      d0, d16, d20
+  .long  0xf3021db4                          // vmul.f32      d1, d18, d20
+  .long  0xf3012db4                          // vmul.f32      d2, d17, d20
+  .long  0xf3033db4                          // vmul.f32      d3, d19, d20
+  .long  0xe8bd4bf0                          // pop           {r4, r5, r6, r7, r8, r9, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x37800080                          // .word         0x37800080
+  .long  0x37800080                          // .word         0x37800080
+
+HIDDEN _sk_load_rgb_u16_be_vfp4
+.globl _sk_load_rgb_u16_be_vfp4
+FUNCTION(_sk_load_rgb_u16_be_vfp4)
+_sk_load_rgb_u16_be_vfp4:
+  .long  0xe92d48f0                          // push          {r4, r5, r6, r7, fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe082e082                          // add           lr, r2, r2, lsl #1
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe08cc08e                          // add           ip, ip, lr, lsl #1
+  .long  0xe59de018                          // ldr           lr, [sp, #24]
+  .long  0xf4ec060f                          // vld3.16       {d16[0],d17[0],d18[0]}, [ip]
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0x1a000001                          // bne           339c <sk_load_rgb_u16_be_vfp4+0x2c>
+  .long  0xe28cc006                          // add           ip, ip, #6
+  .long  0xf4ec064f                          // vld3.16       {d16[1],d17[1],d18[1]}, [ip]
+  .long  0xee90cbb0                          // vmov.u16      ip, d16[0]
+  .long  0xf2873f10                          // vmov.f32      d3, #1
+  .long  0xee91ebb0                          // vmov.u16      lr, d17[0]
+  .long  0xee924bb0                          // vmov.u16      r4, d18[0]
+  .long  0xee927bf0                          // vmov.u16      r7, d18[1]
+  .long  0xf3c73c1f                          // vmov.i32      d19, #65535
+  .long  0xee905bf0                          // vmov.u16      r5, d16[1]
+  .long  0xee916bf0                          // vmov.u16      r6, d17[1]
+  .long  0xee00cb90                          // vmov.32       d16[0], ip
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xee01eb90                          // vmov.32       d17[0], lr
+  .long  0xee024b90                          // vmov.32       d18[0], r4
+  .long  0xee205b90                          // vmov.32       d16[1], r5
+  .long  0xee216b90                          // vmov.32       d17[1], r6
+  .long  0xf24041b3                          // vand          d20, d16, d19
+  .long  0xf2e80530                          // vshl.s32      d16, d16, #8
+  .long  0xee227b90                          // vmov.32       d18[1], r7
+  .long  0xf24151b3                          // vand          d21, d17, d19
+  .long  0xf3f84034                          // vshr.u32      d20, d20, #8
+  .long  0xf24261b3                          // vand          d22, d18, d19
+  .long  0xf2e81531                          // vshl.s32      d17, d17, #8
+  .long  0xf3f85035                          // vshr.u32      d21, d21, #8
+  .long  0xf2e82532                          // vshl.s32      d18, d18, #8
+  .long  0xf3f86036                          // vshr.u32      d22, d22, #8
+  .long  0xf26001b4                          // vorr          d16, d16, d20
+  .long  0xf26111b5                          // vorr          d17, d17, d21
+  .long  0xf26221b6                          // vorr          d18, d18, d22
+  .long  0xf24001b3                          // vand          d16, d16, d19
+  .long  0xf24111b3                          // vand          d17, d17, d19
+  .long  0xf24221b3                          // vand          d18, d18, d19
+  .long  0xeddf3b08                          // vldr          d19, [pc, #32]
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3fb16a1                          // vcvt.f32.u32  d17, d17
+  .long  0xf3fb26a2                          // vcvt.f32.u32  d18, d18
+  .long  0xf3000db3                          // vmul.f32      d0, d16, d19
+  .long  0xf3011db3                          // vmul.f32      d1, d17, d19
+  .long  0xf3022db3                          // vmul.f32      d2, d18, d19
+  .long  0xe8bd48f0                          // pop           {r4, r5, r6, r7, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x37800080                          // .word         0x37800080
+  .long  0x37800080                          // .word         0x37800080
+
+HIDDEN _sk_store_u16_be_vfp4
+.globl _sk_store_u16_be_vfp4
+FUNCTION(_sk_store_u16_be_vfp4)
+_sk_store_u16_be_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xeddf0b2d                          // vldr          d16, [pc, #180]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xf2c3361f                          // vmov.i32      d19, #1056964608
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2432c30                          // vfma.f32      d18, d3, d16
+  .long  0xf2c3161f                          // vmov.i32      d17, #1056964608
+  .long  0xf2c3461f                          // vmov.i32      d20, #1056964608
+  .long  0xf2423c30                          // vfma.f32      d19, d2, d16
+  .long  0xf2414c30                          // vfma.f32      d20, d1, d16
+  .long  0xf2401c30                          // vfma.f32      d17, d0, d16
+  .long  0xf3fb07a2                          // vcvt.u32.f32  d16, d18
+  .long  0xf3fb27a3                          // vcvt.u32.f32  d18, d19
+  .long  0xf3c73c1f                          // vmov.i32      d19, #65535
+  .long  0xf3fb47a4                          // vcvt.u32.f32  d20, d20
+  .long  0xf3fb17a1                          // vcvt.u32.f32  d17, d17
+  .long  0xf24051b3                          // vand          d21, d16, d19
+  .long  0xf24261b3                          // vand          d22, d18, d19
+  .long  0xf24471b3                          // vand          d23, d20, d19
+  .long  0xf24131b3                          // vand          d19, d17, d19
+  .long  0xf2e80530                          // vshl.s32      d16, d16, #8
+  .long  0xf3f85035                          // vshr.u32      d21, d21, #8
+  .long  0xf2e88532                          // vshl.s32      d24, d18, #8
+  .long  0xf3f86036                          // vshr.u32      d22, d22, #8
+  .long  0xf2e84534                          // vshl.s32      d20, d20, #8
+  .long  0xf3f87037                          // vshr.u32      d23, d23, #8
+  .long  0xf2e89531                          // vshl.s32      d25, d17, #8
+  .long  0xf3f8a033                          // vshr.u32      d26, d19, #8
+  .long  0xf26031b5                          // vorr          d19, d16, d21
+  .long  0xf26821b6                          // vorr          d18, d24, d22
+  .long  0xf26411b7                          // vorr          d17, d20, d23
+  .long  0xf26901ba                          // vorr          d16, d25, d26
+  .long  0xf3f63124                          // vuzp.16       d19, d20
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xf3f62124                          // vuzp.16       d18, d20
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe08cc182                          // add           ip, ip, r2, lsl #3
+  .long  0xf3f61124                          // vuzp.16       d17, d20
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xf3f60124                          // vuzp.16       d16, d20
+  .long  0xf4cc070f                          // vst4.16       {d16[0],d17[0],d18[0],d19[0]}, [ip]
+  .long  0x1a000001                          // bne           34f8 <sk_store_u16_be_vfp4+0xb0>
+  .long  0xe28cc008                          // add           ip, ip, #8
+  .long  0xf4cc074f                          // vst4.16       {d16[1],d17[1],d18[1],d19[1]}, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x477fff00                          // .word         0x477fff00
+  .long  0x477fff00                          // .word         0x477fff00
+
+HIDDEN _sk_load_f32_vfp4
+.globl _sk_load_f32_vfp4
+FUNCTION(_sk_load_f32_vfp4)
+_sk_load_f32_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xe08cc202                          // add           ip, ip, r2, lsl #4
+  .long  0x1a000004                          // bne           3540 <sk_load_f32_vfp4+0x30>
+  .long  0xf42c008f                          // vld4.32       {d0-d3}, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xf4ac0f8f                          // vld4.32       {d0[]-d3[]}, [ip]
+  .long  0xeafffff9                          // b             3530 <sk_load_f32_vfp4+0x20>
+
+HIDDEN _sk_store_f32_vfp4
+.globl _sk_store_f32_vfp4
+FUNCTION(_sk_store_f32_vfp4)
+_sk_store_f32_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59de008                          // ldr           lr, [sp, #8]
+  .long  0xe59cc000                          // ldr           ip, [ip]
+  .long  0xe35e0000                          // cmp           lr, #0
+  .long  0xe08cc202                          // add           ip, ip, r2, lsl #4
+  .long  0x1a000004                          // bne           3578 <sk_store_f32_vfp4+0x30>
+  .long  0xf40c008f                          // vst4.32       {d0-d3}, [ip]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xf48c0b0f                          // vst4.32       {d0[0],d1[0],d2[0],d3[0]}, [ip]
+  .long  0xeafffff9                          // b             3568 <sk_store_f32_vfp4+0x20>
+
+HIDDEN _sk_clamp_x_vfp4
+.globl _sk_clamp_x_vfp4
+FUNCTION(_sk_clamp_x_vfp4)
+_sk_clamp_x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2400f80                          // vmax.f32      d16, d16, d0
+  .long  0xf4ee1c9f                          // vld1.32       {d17[]}, [lr :32]
+  .long  0xf2200fa1                          // vmin.f32      d0, d16, d17
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_clamp_y_vfp4
+.globl _sk_clamp_y_vfp4
+FUNCTION(_sk_clamp_y_vfp4)
+_sk_clamp_y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xf2400f81                          // vmax.f32      d16, d16, d1
+  .long  0xf4ee1c9f                          // vld1.32       {d17[]}, [lr :32]
+  .long  0xf2201fa1                          // vmin.f32      d1, d16, d17
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_repeat_x_vfp4
+.globl _sk_repeat_x_vfp4
+FUNCTION(_sk_repeat_x_vfp4)
+_sk_repeat_x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c02010                          // vmov.i32      d18, #0
+  .long  0xeddf3b0e                          // vldr          d19, [pc, #56]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed9e8a00                          // vldr          s16, [lr]
+  .long  0xeec09a88                          // vdiv.f32      s19, s1, s16
+  .long  0xee809a08                          // vdiv.f32      s18, s0, s16
+  .long  0xf3fb0709                          // vcvt.s32.f32  d16, d9
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3601e89                          // vcgt.f32      d17, d16, d9
+  .long  0xf35311b2                          // vbsl          d17, d19, d18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf3f41c08                          // vdup.32       d17, d8[0]
+  .long  0xf2210cb0                          // vfms.f32      d0, d17, d16
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_repeat_y_vfp4
+.globl _sk_repeat_y_vfp4
+FUNCTION(_sk_repeat_y_vfp4)
+_sk_repeat_y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c02010                          // vmov.i32      d18, #0
+  .long  0xeddf3b0e                          // vldr          d19, [pc, #56]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed9e8a00                          // vldr          s16, [lr]
+  .long  0xeec19a88                          // vdiv.f32      s19, s3, s16
+  .long  0xee819a08                          // vdiv.f32      s18, s2, s16
+  .long  0xf3fb0709                          // vcvt.s32.f32  d16, d9
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3601e89                          // vcgt.f32      d17, d16, d9
+  .long  0xf35311b2                          // vbsl          d17, d19, d18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf3f41c08                          // vdup.32       d17, d8[0]
+  .long  0xf2211cb0                          // vfms.f32      d1, d17, d16
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_mirror_x_vfp4
+.globl _sk_mirror_x_vfp4
+FUNCTION(_sk_mirror_x_vfp4)
+_sk_mirror_x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xeddf4b14                          // vldr          d20, [pc, #80]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed9e8a00                          // vldr          s16, [lr]
+  .long  0xee389a08                          // vadd.f32      s18, s16, s16
+  .long  0xf3f40c08                          // vdup.32       d16, d8[0]
+  .long  0xf2200d20                          // vsub.f32      d0, d0, d16
+  .long  0xeec08a89                          // vdiv.f32      s17, s1, s18
+  .long  0xee808a09                          // vdiv.f32      s16, s0, s18
+  .long  0xf3fb1708                          // vcvt.s32.f32  d17, d8
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3612e88                          // vcgt.f32      d18, d17, d8
+  .long  0xf35421b3                          // vbsl          d18, d20, d19
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xf2e119c9                          // vmul.f32      d17, d17, d9[0]
+  .long  0xf2601d21                          // vsub.f32      d17, d0, d17
+  .long  0xf2610da0                          // vsub.f32      d16, d17, d16
+  .long  0xf3b90720                          // vabs.f32      d0, d16
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_mirror_y_vfp4
+.globl _sk_mirror_y_vfp4
+FUNCTION(_sk_mirror_y_vfp4)
+_sk_mirror_y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xed2d8b04                          // vpush         {d8-d9}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2c03010                          // vmov.i32      d19, #0
+  .long  0xeddf4b14                          // vldr          d20, [pc, #80]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed9e8a00                          // vldr          s16, [lr]
+  .long  0xee389a08                          // vadd.f32      s18, s16, s16
+  .long  0xf3f40c08                          // vdup.32       d16, d8[0]
+  .long  0xf2211d20                          // vsub.f32      d1, d1, d16
+  .long  0xeec18a89                          // vdiv.f32      s17, s3, s18
+  .long  0xee818a09                          // vdiv.f32      s16, s2, s18
+  .long  0xf3fb1708                          // vcvt.s32.f32  d17, d8
+  .long  0xf3fb1621                          // vcvt.f32.s32  d17, d17
+  .long  0xf3612e88                          // vcgt.f32      d18, d17, d8
+  .long  0xf35421b3                          // vbsl          d18, d20, d19
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xf2e119c9                          // vmul.f32      d17, d17, d9[0]
+  .long  0xf2611d21                          // vsub.f32      d17, d1, d17
+  .long  0xf2610da0                          // vsub.f32      d16, d17, d16
+  .long  0xf3b91720                          // vabs.f32      d1, d16
+  .long  0xecbd8b04                          // vpop          {d8-d9}
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_clamp_x_1_vfp4
+.globl _sk_clamp_x_1_vfp4
+FUNCTION(_sk_clamp_x_1_vfp4)
+_sk_clamp_x_1_vfp4:
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2c71f10                          // vmov.f32      d17, #1
+  .long  0xf2400f80                          // vmax.f32      d16, d16, d0
+  .long  0xf2200fa1                          // vmin.f32      d0, d16, d17
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_repeat_x_1_vfp4
+.globl _sk_repeat_x_1_vfp4
+FUNCTION(_sk_repeat_x_1_vfp4)
+_sk_repeat_x_1_vfp4:
+  .long  0xf3fb0700                          // vcvt.s32.f32  d16, d0
+  .long  0xeddf3b07                          // vldr          d19, [pc, #28]
+  .long  0xf2c02010                          // vmov.i32      d18, #0
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3fb0620                          // vcvt.f32.s32  d16, d16
+  .long  0xf3601e80                          // vcgt.f32      d17, d16, d0
+  .long  0xf35311b2                          // vbsl          d17, d19, d18
+  .long  0xf2600da1                          // vsub.f32      d16, d16, d17
+  .long  0xf2200d20                          // vsub.f32      d0, d0, d16
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_mirror_x_1_vfp4
+.globl _sk_mirror_x_1_vfp4
+FUNCTION(_sk_mirror_x_1_vfp4)
+_sk_mirror_x_1_vfp4:
+  .long  0xf3c70f10                          // vmov.f32      d16, #-1
+  .long  0xeddf5b0f                          // vldr          d21, [pc, #60]
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2401d20                          // vadd.f32      d17, d0, d16
+  .long  0xf2c04010                          // vmov.i32      d20, #0
+  .long  0xf3412db2                          // vmul.f32      d18, d17, d18
+  .long  0xf3fb3722                          // vcvt.s32.f32  d19, d18
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3632ea2                          // vcgt.f32      d18, d19, d18
+  .long  0xf35521b4                          // vbsl          d18, d21, d20
+  .long  0xf2632da2                          // vsub.f32      d18, d19, d18
+  .long  0xf2422da2                          // vadd.f32      d18, d18, d18
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xf2410da0                          // vadd.f32      d16, d17, d16
+  .long  0xf3b90720                          // vabs.f32      d0, d16
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_luminance_to_alpha_vfp4
+.globl _sk_luminance_to_alpha_vfp4
+FUNCTION(_sk_luminance_to_alpha_vfp4)
+_sk_luminance_to_alpha_vfp4:
+  .long  0xeddf0b0a                          // vldr          d16, [pc, #40]
+  .long  0xeddf1b0b                          // vldr          d17, [pc, #44]
+  .long  0xf3410d30                          // vmul.f32      d16, d1, d16
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3401d31                          // vmul.f32      d17, d0, d17
+  .long  0xf2800010                          // vmov.i32      d0, #0
+  .long  0xf2801010                          // vmov.i32      d1, #0
+  .long  0xf2013da0                          // vadd.f32      d3, d17, d16
+  .long  0xeddf0b06                          // vldr          d16, [pc, #24]
+  .long  0xf2023c30                          // vfma.f32      d3, d2, d16
+  .long  0xf2802010                          // vmov.i32      d2, #0
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3f371759                          // .word         0x3f371759
+  .long  0x3f371759                          // .word         0x3f371759
+  .long  0x3e59b3d0                          // .word         0x3e59b3d0
+  .long  0x3e59b3d0                          // .word         0x3e59b3d0
+  .long  0x3d93dd98                          // .word         0x3d93dd98
+  .long  0x3d93dd98                          // .word         0x3d93dd98
+
+HIDDEN _sk_matrix_2x3_vfp4
+.globl _sk_matrix_2x3_vfp4
+FUNCTION(_sk_matrix_2x3_vfp4)
+_sk_matrix_2x3_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e400c                          // add           r4, lr, #12
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4010                          // add           r4, lr, #16
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf2410c31                          // vfma.f32      d16, d1, d17
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xf2411c32                          // vfma.f32      d17, d1, d18
+  .long  0xf4ee2c9d                          // vld1.32       {d18[]}, [lr :32]!
+  .long  0xf4ee3c9f                          // vld1.32       {d19[]}, [lr :32]
+  .long  0xf2400c32                          // vfma.f32      d16, d0, d18
+  .long  0xf2401c33                          // vfma.f32      d17, d0, d19
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_matrix_3x4_vfp4
+.globl _sk_matrix_3x4_vfp4
+FUNCTION(_sk_matrix_3x4_vfp4)
+_sk_matrix_3x4_vfp4:
+  .long  0xe92d4010                          // push          {r4, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e4020                          // add           r4, lr, #32
+  .long  0xf4e43c9f                          // vld1.32       {d19[]}, [r4 :32]
+  .long  0xe28e402c                          // add           r4, lr, #44
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e401c                          // add           r4, lr, #28
+  .long  0xf2420c33                          // vfma.f32      d16, d2, d19
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xe28e4018                          // add           r4, lr, #24
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e4024                          // add           r4, lr, #36
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4028                          // add           r4, lr, #40
+  .long  0xf2421c32                          // vfma.f32      d17, d2, d18
+  .long  0xf4e42c9f                          // vld1.32       {d18[]}, [r4 :32]
+  .long  0xe28e4010                          // add           r4, lr, #16
+  .long  0xf2422c34                          // vfma.f32      d18, d2, d20
+  .long  0xf4e43c9f                          // vld1.32       {d19[]}, [r4 :32]
+  .long  0xe28e400c                          // add           r4, lr, #12
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf2411c34                          // vfma.f32      d17, d1, d20
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xf2410c34                          // vfma.f32      d16, d1, d20
+  .long  0xe28e4008                          // add           r4, lr, #8
+  .long  0xf2412c33                          // vfma.f32      d18, d1, d19
+  .long  0xf4ee3c9d                          // vld1.32       {d19[]}, [lr :32]!
+  .long  0xf4ee4c9f                          // vld1.32       {d20[]}, [lr :32]
+  .long  0xf2401c33                          // vfma.f32      d17, d0, d19
+  .long  0xf4e43c9f                          // vld1.32       {d19[]}, [r4 :32]
+  .long  0xf2400c33                          // vfma.f32      d16, d0, d19
+  .long  0xf2402c34                          // vfma.f32      d18, d0, d20
+  .long  0xf22101b1                          // vorr          d0, d17, d17
+  .long  0xf22021b0                          // vorr          d2, d16, d16
+  .long  0xf22211b2                          // vorr          d1, d18, d18
+  .long  0xe8bd4010                          // pop           {r4, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_matrix_4x5_vfp4
+.globl _sk_matrix_4x5_vfp4
+FUNCTION(_sk_matrix_4x5_vfp4)
+_sk_matrix_4x5_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xf2620112                          // vorr          d16, d2, d2
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e5014                          // add           r5, lr, #20
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xf4e55c9f                          // vld1.32       {d21[]}, [r5 :32]
+  .long  0xe28e5028                          // add           r5, lr, #40
+  .long  0xf4e56c9f                          // vld1.32       {d22[]}, [r5 :32]
+  .long  0xe28e5038                          // add           r5, lr, #56
+  .long  0xf4e57c9f                          // vld1.32       {d23[]}, [r5 :32]
+  .long  0xe28e5048                          // add           r5, lr, #72
+  .long  0xf4a52c9f                          // vld1.32       {d2[]}, [r5 :32]
+  .long  0xe28e5034                          // add           r5, lr, #52
+  .long  0xf2032c37                          // vfma.f32      d2, d3, d23
+  .long  0xf4e58c9f                          // vld1.32       {d24[]}, [r5 :32]
+  .long  0xe28e5044                          // add           r5, lr, #68
+  .long  0xf4e51c9f                          // vld1.32       {d17[]}, [r5 :32]
+  .long  0xe28e5030                          // add           r5, lr, #48
+  .long  0xf2431c38                          // vfma.f32      d17, d3, d24
+  .long  0xf4e59c9f                          // vld1.32       {d25[]}, [r5 :32]
+  .long  0xe28e503c                          // add           r5, lr, #60
+  .long  0xf4e53c9f                          // vld1.32       {d19[]}, [r5 :32]
+  .long  0xe28e504c                          // add           r5, lr, #76
+  .long  0xf2002cb6                          // vfma.f32      d2, d16, d22
+  .long  0xf4e52c9f                          // vld1.32       {d18[]}, [r5 :32]
+  .long  0xe28e5040                          // add           r5, lr, #64
+  .long  0xf2432c33                          // vfma.f32      d18, d3, d19
+  .long  0xf4e53c9f                          // vld1.32       {d19[]}, [r5 :32]
+  .long  0xe28e5020                          // add           r5, lr, #32
+  .long  0xf2433c39                          // vfma.f32      d19, d3, d25
+  .long  0xf4e57c9f                          // vld1.32       {d23[]}, [r5 :32]
+  .long  0xe28e502c                          // add           r5, lr, #44
+  .long  0xf4e58c9f                          // vld1.32       {d24[]}, [r5 :32]
+  .long  0xe28e5024                          // add           r5, lr, #36
+  .long  0xf2402cb8                          // vfma.f32      d18, d16, d24
+  .long  0xf4e58c9f                          // vld1.32       {d24[]}, [r5 :32]
+  .long  0xf2401cb8                          // vfma.f32      d17, d16, d24
+  .long  0xe28e5010                          // add           r5, lr, #16
+  .long  0xf2403cb7                          // vfma.f32      d19, d16, d23
+  .long  0xf4e44c9d                          // vld1.32       {d20[]}, [r4 :32]!
+  .long  0xf4e50c9f                          // vld1.32       {d16[]}, [r5 :32]
+  .long  0xe28e501c                          // add           r5, lr, #28
+  .long  0xf4e56c9f                          // vld1.32       {d22[]}, [r5 :32]
+  .long  0xe28e5018                          // add           r5, lr, #24
+  .long  0xf2412c36                          // vfma.f32      d18, d1, d22
+  .long  0xf2411c35                          // vfma.f32      d17, d1, d21
+  .long  0xf4e45c9f                          // vld1.32       {d21[]}, [r4 :32]
+  .long  0xf2413c30                          // vfma.f32      d19, d1, d16
+  .long  0xf4e50c9f                          // vld1.32       {d16[]}, [r5 :32]
+  .long  0xe28e5008                          // add           r5, lr, #8
+  .long  0xf2012c30                          // vfma.f32      d2, d1, d16
+  .long  0xf4e50c9f                          // vld1.32       {d16[]}, [r5 :32]
+  .long  0xe28e500c                          // add           r5, lr, #12
+  .long  0xf2401c35                          // vfma.f32      d17, d0, d21
+  .long  0xf2403c34                          // vfma.f32      d19, d0, d20
+  .long  0xf4e54c9f                          // vld1.32       {d20[]}, [r5 :32]
+  .long  0xf2402c34                          // vfma.f32      d18, d0, d20
+  .long  0xf2002c30                          // vfma.f32      d2, d0, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xf22301b3                          // vorr          d0, d19, d19
+  .long  0xf22231b2                          // vorr          d3, d18, d18
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_matrix_4x3_vfp4
+.globl _sk_matrix_4x3_vfp4
+FUNCTION(_sk_matrix_4x3_vfp4)
+_sk_matrix_4x3_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e5018                          // add           r5, lr, #24
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xf4e53c9f                          // vld1.32       {d19[]}, [r5 :32]
+  .long  0xe28e5028                          // add           r5, lr, #40
+  .long  0xf4a52c9f                          // vld1.32       {d2[]}, [r5 :32]
+  .long  0xe28e5014                          // add           r5, lr, #20
+  .long  0xf2012c33                          // vfma.f32      d2, d1, d19
+  .long  0xf4e54c9f                          // vld1.32       {d20[]}, [r5 :32]
+  .long  0xe28e5010                          // add           r5, lr, #16
+  .long  0xf4e51c9f                          // vld1.32       {d17[]}, [r5 :32]
+  .long  0xe28e5020                          // add           r5, lr, #32
+  .long  0xf4e50c9f                          // vld1.32       {d16[]}, [r5 :32]
+  .long  0xe28e501c                          // add           r5, lr, #28
+  .long  0xf2410c31                          // vfma.f32      d16, d1, d17
+  .long  0xf4e55c9f                          // vld1.32       {d21[]}, [r5 :32]
+  .long  0xe28e502c                          // add           r5, lr, #44
+  .long  0xf4a53c9f                          // vld1.32       {d3[]}, [r5 :32]
+  .long  0xe28e5024                          // add           r5, lr, #36
+  .long  0xf2013c35                          // vfma.f32      d3, d1, d21
+  .long  0xf4e51c9f                          // vld1.32       {d17[]}, [r5 :32]
+  .long  0xe28e5008                          // add           r5, lr, #8
+  .long  0xf2411c34                          // vfma.f32      d17, d1, d20
+  .long  0xf4e42c9d                          // vld1.32       {d18[]}, [r4 :32]!
+  .long  0xf2400c32                          // vfma.f32      d16, d0, d18
+  .long  0xf4e44c9f                          // vld1.32       {d20[]}, [r4 :32]
+  .long  0xf4e53c9f                          // vld1.32       {d19[]}, [r5 :32]
+  .long  0xe28e500c                          // add           r5, lr, #12
+  .long  0xf2002c33                          // vfma.f32      d2, d0, d19
+  .long  0xf2401c34                          // vfma.f32      d17, d0, d20
+  .long  0xf4e52c9f                          // vld1.32       {d18[]}, [r5 :32]
+  .long  0xf2003c32                          // vfma.f32      d3, d0, d18
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xf22111b1                          // vorr          d1, d17, d17
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_matrix_perspective_vfp4
+.globl _sk_matrix_perspective_vfp4
+FUNCTION(_sk_matrix_perspective_vfp4)
+_sk_matrix_perspective_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e401c                          // add           r4, lr, #28
+  .long  0xe28e5010                          // add           r5, lr, #16
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe28e4020                          // add           r4, lr, #32
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xe28e4018                          // add           r4, lr, #24
+  .long  0xf2411c30                          // vfma.f32      d17, d1, d16
+  .long  0xf4e40c9f                          // vld1.32       {d16[]}, [r4 :32]
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xf4e52c9f                          // vld1.32       {d18[]}, [r5 :32]
+  .long  0xe28e5008                          // add           r5, lr, #8
+  .long  0xf4e53c9f                          // vld1.32       {d19[]}, [r5 :32]
+  .long  0xe28e500c                          // add           r5, lr, #12
+  .long  0xf2401c30                          // vfma.f32      d17, d0, d16
+  .long  0xf4e40c9d                          // vld1.32       {d16[]}, [r4 :32]!
+  .long  0xf4e45c9f                          // vld1.32       {d21[]}, [r4 :32]
+  .long  0xe28e4014                          // add           r4, lr, #20
+  .long  0xf2413c35                          // vfma.f32      d19, d1, d21
+  .long  0xf4e45c9f                          // vld1.32       {d21[]}, [r4 :32]
+  .long  0xf2415c32                          // vfma.f32      d21, d1, d18
+  .long  0xf4e52c9f                          // vld1.32       {d18[]}, [r5 :32]
+  .long  0xf3fb4521                          // vrecpe.f32    d20, d17
+  .long  0xf2403c30                          // vfma.f32      d19, d0, d16
+  .long  0xf2411fb4                          // vrecps.f32    d17, d17, d20
+  .long  0xf2405c32                          // vfma.f32      d21, d0, d18
+  .long  0xf3440db1                          // vmul.f32      d16, d20, d17
+  .long  0xf3030db0                          // vmul.f32      d0, d19, d16
+  .long  0xf3051db0                          // vmul.f32      d1, d21, d16
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_evenly_spaced_gradient_vfp4
+.globl _sk_evenly_spaced_gradient_vfp4
+FUNCTION(_sk_evenly_spaced_gradient_vfp4)
+_sk_evenly_spaced_gradient_vfp4:
+  .long  0xe92d4ff0                          // push          {r4, r5, r6, r7, r8, r9, sl, fp, lr}
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xed2d8b0a                          // vpush         {d8-d12}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe89e0720                          // ldm           lr, {r5, r8, r9, sl}
+  .long  0xe2455001                          // sub           r5, r5, #1
+  .long  0xe59e4010                          // ldr           r4, [lr, #16]
+  .long  0xe59eb020                          // ldr           fp, [lr, #32]
+  .long  0xee805b90                          // vdup.32       d16, r5
+  .long  0xf3fb06a0                          // vcvt.f32.u32  d16, d16
+  .long  0xf3400d90                          // vmul.f32      d16, d16, d0
+  .long  0xf3fb0720                          // vcvt.s32.f32  d16, d16
+  .long  0xee305b90                          // vmov.32       r5, d16[1]
+  .long  0xe0846105                          // add           r6, r4, r5, lsl #2
+  .long  0xe08b7105                          // add           r7, fp, r5, lsl #2
+  .long  0xedd69a00                          // vldr          s19, [r6]
+  .long  0xee106b90                          // vmov.32       r6, d16[0]
+  .long  0xedd73a00                          // vldr          s7, [r7]
+  .long  0xe08a7105                          // add           r7, sl, r5, lsl #2
+  .long  0xedd7aa00                          // vldr          s21, [r7]
+  .long  0xe08a7106                          // add           r7, sl, r6, lsl #2
+  .long  0xe0844106                          // add           r4, r4, r6, lsl #2
+  .long  0xe59ea018                          // ldr           sl, [lr, #24]
+  .long  0xed97aa00                          // vldr          s20, [r7]
+  .long  0xe59e701c                          // ldr           r7, [lr, #28]
+  .long  0xed949a00                          // vldr          s18, [r4]
+  .long  0xe0874105                          // add           r4, r7, r5, lsl #2
+  .long  0xedd42a00                          // vldr          s5, [r4]
+  .long  0xe0894105                          // add           r4, r9, r5, lsl #2
+  .long  0xedd4ba00                          // vldr          s23, [r4]
+  .long  0xe0894106                          // add           r4, r9, r6, lsl #2
+  .long  0xe08b9106                          // add           r9, fp, r6, lsl #2
+  .long  0xe087b106                          // add           fp, r7, r6, lsl #2
+  .long  0xed94ba00                          // vldr          s22, [r4]
+  .long  0xe08a4105                          // add           r4, sl, r5, lsl #2
+  .long  0xe0887105                          // add           r7, r8, r5, lsl #2
+  .long  0xed9b2a00                          // vldr          s4, [fp]
+  .long  0xedd41a00                          // vldr          s3, [r4]
+  .long  0xe59e4014                          // ldr           r4, [lr, #20]
+  .long  0xf2002c1a                          // vfma.f32      d2, d0, d10
+  .long  0xedd7ca00                          // vldr          s25, [r7]
+  .long  0xe0887106                          // add           r7, r8, r6, lsl #2
+  .long  0xe0845105                          // add           r5, r4, r5, lsl #2
+  .long  0xed993a00                          // vldr          s6, [r9]
+  .long  0xed97ca00                          // vldr          s24, [r7]
+  .long  0xe0847106                          // add           r7, r4, r6, lsl #2
+  .long  0xedd58a00                          // vldr          s17, [r5]
+  .long  0xf2003c19                          // vfma.f32      d3, d0, d9
+  .long  0xed978a00                          // vldr          s16, [r7]
+  .long  0xe08a7106                          // add           r7, sl, r6, lsl #2
+  .long  0xf2008c1c                          // vfma.f32      d8, d0, d12
+  .long  0xed971a00                          // vldr          s2, [r7]
+  .long  0xf2001c1b                          // vfma.f32      d1, d0, d11
+  .long  0xf2280118                          // vorr          d0, d8, d8
+  .long  0xecbd8b0a                          // vpop          {d8-d12}
+  .long  0xe28dd004                          // add           sp, sp, #4
+  .long  0xe8bd4ff0                          // pop           {r4, r5, r6, r7, r8, r9, sl, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_gauss_a_to_rgba_vfp4
+.globl _sk_gauss_a_to_rgba_vfp4
+FUNCTION(_sk_gauss_a_to_rgba_vfp4)
+_sk_gauss_a_to_rgba_vfp4:
+  .long  0xeddf0b0c                          // vldr          d16, [pc, #48]
+  .long  0xeddf1b0d                          // vldr          d17, [pc, #52]
+  .long  0xf2431c30                          // vfma.f32      d17, d3, d16
+  .long  0xeddf0b0d                          // vldr          d16, [pc, #52]
+  .long  0xed9f0b10                          // vldr          d0, [pc, #64]
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2430c31                          // vfma.f32      d16, d3, d17
+  .long  0xeddf1b0b                          // vldr          d17, [pc, #44]
+  .long  0xf2431c30                          // vfma.f32      d17, d3, d16
+  .long  0xf2030c31                          // vfma.f32      d0, d3, d17
+  .long  0xf2201110                          // vorr          d1, d0, d0
+  .long  0xf2202110                          // vorr          d2, d0, d0
+  .long  0xf2203110                          // vorr          d3, d0, d0
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xc011102d                          // .word         0xc011102d
+  .long  0xc011102d                          // .word         0xc011102d
+  .long  0x40397812                          // .word         0x40397812
+  .long  0x40397812                          // .word         0x40397812
+  .long  0x3e5a9420                          // .word         0x3e5a9420
+  .long  0x3e5a9420                          // .word         0x3e5a9420
+  .long  0x3e1e9d04                          // .word         0x3e1e9d04
+  .long  0x3e1e9d04                          // .word         0x3e1e9d04
+  .long  0x39a11800                          // .word         0x39a11800
+  .long  0x39a11800                          // .word         0x39a11800
+
+HIDDEN _sk_gradient_vfp4
+.globl _sk_gradient_vfp4
+FUNCTION(_sk_gradient_vfp4)
+_sk_gradient_vfp4:
+  .long  0xe92d4ff0                          // push          {r4, r5, r6, r7, r8, r9, sl, fp, lr}
+  .long  0xe24dd004                          // sub           sp, sp, #4
+  .long  0xed2d8b0a                          // vpush         {d8-d12}
+  .long  0xe24dd008                          // sub           sp, sp, #8
+  .long  0xe58d3004                          // str           r3, [sp, #4]
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xe59c4000                          // ldr           r4, [ip]
+  .long  0xe3540002                          // cmp           r4, #2
+  .long  0x3a00000b                          // bcc           3d08 <sk_gradient_vfp4+0x58>
+  .long  0xe59c5024                          // ldr           r5, [ip, #36]
+  .long  0xf2c01010                          // vmov.i32      d17, #0
+  .long  0xf2c02011                          // vmov.i32      d18, #1
+  .long  0xe244e001                          // sub           lr, r4, #1
+  .long  0xf2c00010                          // vmov.i32      d16, #0
+  .long  0xe2854004                          // add           r4, r5, #4
+  .long  0xf4e43c9d                          // vld1.32       {d19[]}, [r4 :32]!
+  .long  0xe25ee001                          // subs          lr, lr, #1
+  .long  0xf3403e23                          // vcge.f32      d19, d0, d19
+  .long  0xf35231b1                          // vbsl          d19, d18, d17
+  .long  0xf26308a0                          // vadd.i32      d16, d19, d16
+  .long  0x1afffff9                          // bne           3cf0 <sk_gradient_vfp4+0x40>
+  .long  0xee304b90                          // vmov.32       r4, d16[1]
+  .long  0xe59c6010                          // ldr           r6, [ip, #16]
+  .long  0xee10eb90                          // vmov.32       lr, d16[0]
+  .long  0xe59c700c                          // ldr           r7, [ip, #12]
+  .long  0xe59ca020                          // ldr           sl, [ip, #32]
+  .long  0xe59c9004                          // ldr           r9, [ip, #4]
+  .long  0xe59c8008                          // ldr           r8, [ip, #8]
+  .long  0xe0865104                          // add           r5, r6, r4, lsl #2
+  .long  0xe089310e                          // add           r3, r9, lr, lsl #2
+  .long  0xe0899104                          // add           r9, r9, r4, lsl #2
+  .long  0xedd59a00                          // vldr          s19, [r5]
+  .long  0xe086510e                          // add           r5, r6, lr, lsl #2
+  .long  0xedd9ca00                          // vldr          s25, [r9]
+  .long  0xed959a00                          // vldr          s18, [r5]
+  .long  0xe0875104                          // add           r5, r7, r4, lsl #2
+  .long  0xed93ca00                          // vldr          s24, [r3]
+  .long  0xedd5aa00                          // vldr          s21, [r5]
+  .long  0xe087510e                          // add           r5, r7, lr, lsl #2
+  .long  0xed95aa00                          // vldr          s20, [r5]
+  .long  0xe08a5104                          // add           r5, sl, r4, lsl #2
+  .long  0xe08aa10e                          // add           sl, sl, lr, lsl #2
+  .long  0xedd53a00                          // vldr          s7, [r5]
+  .long  0xe59c501c                          // ldr           r5, [ip, #28]
+  .long  0xed9a3a00                          // vldr          s6, [sl]
+  .long  0xe0856104                          // add           r6, r5, r4, lsl #2
+  .long  0xe085b10e                          // add           fp, r5, lr, lsl #2
+  .long  0xe0885104                          // add           r5, r8, r4, lsl #2
+  .long  0xe088810e                          // add           r8, r8, lr, lsl #2
+  .long  0xedd62a00                          // vldr          s5, [r6]
+  .long  0xf2003c19                          // vfma.f32      d3, d0, d9
+  .long  0xedd5ba00                          // vldr          s23, [r5]
+  .long  0xe59c6014                          // ldr           r6, [ip, #20]
+  .long  0xe59c5018                          // ldr           r5, [ip, #24]
+  .long  0xe0863104                          // add           r3, r6, r4, lsl #2
+  .long  0xed9b2a00                          // vldr          s4, [fp]
+  .long  0xe0857104                          // add           r7, r5, r4, lsl #2
+  .long  0xed98ba00                          // vldr          s22, [r8]
+  .long  0xedd38a00                          // vldr          s17, [r3]
+  .long  0xe085310e                          // add           r3, r5, lr, lsl #2
+  .long  0xedd71a00                          // vldr          s3, [r7]
+  .long  0xe086710e                          // add           r7, r6, lr, lsl #2
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xf2002c1a                          // vfma.f32      d2, d0, d10
+  .long  0xed978a00                          // vldr          s16, [r7]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xed931a00                          // vldr          s2, [r3]
+  .long  0xf2008c1c                          // vfma.f32      d8, d0, d12
+  .long  0xe59d3004                          // ldr           r3, [sp, #4]
+  .long  0xf2001c1b                          // vfma.f32      d1, d0, d11
+  .long  0xf2280118                          // vorr          d0, d8, d8
+  .long  0xe28dd008                          // add           sp, sp, #8
+  .long  0xecbd8b0a                          // vpop          {d8-d12}
+  .long  0xe28dd004                          // add           sp, sp, #4
+  .long  0xe8bd4ff0                          // pop           {r4, r5, r6, r7, r8, r9, sl, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_evenly_spaced_2_stop_gradient_vfp4
+.globl _sk_evenly_spaced_2_stop_gradient_vfp4
+FUNCTION(_sk_evenly_spaced_2_stop_gradient_vfp4)
+_sk_evenly_spaced_2_stop_gradient_vfp4:
+  .long  0xe92d4830                          // push          {r4, r5, fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xe28e500c                          // add           r5, lr, #12
+  .long  0xe1a0400e                          // mov           r4, lr
+  .long  0xf4e52c9f                          // vld1.32       {d18[]}, [r5 :32]
+  .long  0xe28e5008                          // add           r5, lr, #8
+  .long  0xf4e53c9f                          // vld1.32       {d19[]}, [r5 :32]
+  .long  0xe28e5018                          // add           r5, lr, #24
+  .long  0xf4a52c9f                          // vld1.32       {d2[]}, [r5 :32]
+  .long  0xe28e5010                          // add           r5, lr, #16
+  .long  0xf2002c33                          // vfma.f32      d2, d0, d19
+  .long  0xf4e50c9f                          // vld1.32       {d16[]}, [r5 :32]
+  .long  0xe28e501c                          // add           r5, lr, #28
+  .long  0xf4e41c9d                          // vld1.32       {d17[]}, [r4 :32]!
+  .long  0xf2400c31                          // vfma.f32      d16, d0, d17
+  .long  0xf4a53c9f                          // vld1.32       {d3[]}, [r5 :32]
+  .long  0xe28e5014                          // add           r5, lr, #20
+  .long  0xf2003c32                          // vfma.f32      d3, d0, d18
+  .long  0xf4e41c9f                          // vld1.32       {d17[]}, [r4 :32]
+  .long  0xf4a51c9f                          // vld1.32       {d1[]}, [r5 :32]
+  .long  0xf2001c31                          // vfma.f32      d1, d0, d17
+  .long  0xf22001b0                          // vorr          d0, d16, d16
+  .long  0xe8bd4830                          // pop           {r4, r5, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_xy_to_unit_angle_vfp4
+.globl _sk_xy_to_unit_angle_vfp4
+FUNCTION(_sk_xy_to_unit_angle_vfp4)
+_sk_xy_to_unit_angle_vfp4:
+  .long  0xed2d8b06                          // vpush         {d8-d10}
+  .long  0xf3f90701                          // vabs.f32      d16, d1
+  .long  0xeddf3b24                          // vldr          d19, [pc, #144]
+  .long  0xf3f91700                          // vabs.f32      d17, d0
+  .long  0xeddf4b24                          // vldr          d20, [pc, #144]
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf2018fa0                          // vmax.f32      d8, d17, d16
+  .long  0xf2219fa0                          // vmin.f32      d9, d17, d16
+  .long  0xf3600ea1                          // vcgt.f32      d16, d16, d17
+  .long  0xeec9aaa8                          // vdiv.f32      s21, s19, s17
+  .long  0xee89aa08                          // vdiv.f32      s20, s18, s16
+  .long  0xf34a2d1a                          // vmul.f32      d18, d10, d10
+  .long  0xf3423db3                          // vmul.f32      d19, d18, d19
+  .long  0xf2433da4                          // vadd.f32      d19, d19, d20
+  .long  0xeddf4b1c                          // vldr          d20, [pc, #112]
+  .long  0xf3423db3                          // vmul.f32      d19, d18, d19
+  .long  0xf2433da4                          // vadd.f32      d19, d19, d20
+  .long  0xf3422db3                          // vmul.f32      d18, d18, d19
+  .long  0xeddf3b1a                          // vldr          d19, [pc, #104]
+  .long  0xf2422da3                          // vadd.f32      d18, d18, d19
+  .long  0xf2c53f10                          // vmov.f32      d19, #0.25
+  .long  0xf34a2d32                          // vmul.f32      d18, d10, d18
+  .long  0xf2631da2                          // vsub.f32      d17, d19, d18
+  .long  0xf2c3361f                          // vmov.i32      d19, #1056964608
+  .long  0xf35101b2                          // vbsl          d16, d17, d18
+  .long  0xf2c72f10                          // vmov.f32      d18, #1
+  .long  0xf2631da0                          // vsub.f32      d17, d19, d16
+  .long  0xf3f93600                          // vclt.f32      d19, d0, #0
+  .long  0xf35131b0                          // vbsl          d19, d17, d16
+  .long  0xf2c01010                          // vmov.i32      d17, #0
+  .long  0xf2620da3                          // vsub.f32      d16, d18, d19
+  .long  0xf3f92601                          // vclt.f32      d18, d1, #0
+  .long  0xf35021b3                          // vbsl          d18, d16, d19
+  .long  0xf3420ea1                          // vcge.f32      d16, d18, d17
+  .long  0xf3613ea2                          // vcgt.f32      d19, d17, d18
+  .long  0xf26301b0                          // vorr          d16, d19, d16
+  .long  0xf3b005a0                          // vmvn          d0, d16
+  .long  0xf31101b2                          // vbsl          d0, d17, d18
+  .long  0xecbd8b06                          // vpop          {d8-d10}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xbbe72b6f                          // .word         0xbbe72b6f
+  .long  0xbbe72b6f                          // .word         0xbbe72b6f
+  .long  0x3ccad79f                          // .word         0x3ccad79f
+  .long  0x3ccad79f                          // .word         0x3ccad79f
+  .long  0xbd5464d4                          // .word         0xbd5464d4
+  .long  0xbd5464d4                          // .word         0xbd5464d4
+  .long  0x3e22f0a9                          // .word         0x3e22f0a9
+  .long  0x3e22f0a9                          // .word         0x3e22f0a9
+
+HIDDEN _sk_xy_to_radius_vfp4
+.globl _sk_xy_to_radius_vfp4
+FUNCTION(_sk_xy_to_radius_vfp4)
+_sk_xy_to_radius_vfp4:
+  .long  0xf3410d11                          // vmul.f32      d16, d1, d1
+  .long  0xe491c004                          // ldr           ip, [r1], #4
+  .long  0xf3401d10                          // vmul.f32      d17, d0, d0
+  .long  0xf2410da0                          // vadd.f32      d16, d17, d16
+  .long  0xf3fb15a0                          // vrsqrte.f32   d17, d16
+  .long  0xf3412db1                          // vmul.f32      d18, d17, d17
+  .long  0xf2602fb2                          // vrsqrts.f32   d18, d16, d18
+  .long  0xf3411db2                          // vmul.f32      d17, d17, d18
+  .long  0xf3412db1                          // vmul.f32      d18, d17, d17
+  .long  0xf2602fb2                          // vrsqrts.f32   d18, d16, d18
+  .long  0xf3411db2                          // vmul.f32      d17, d17, d18
+  .long  0xf3000db1                          // vmul.f32      d0, d16, d17
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_save_xy_vfp4
+.globl _sk_save_xy_vfp4
+FUNCTION(_sk_save_xy_vfp4)
+_sk_save_xy_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xf2c3061f                          // vmov.i32      d16, #1056964608
+  .long  0xeddf7b1a                          // vldr          d23, [pc, #104]
+  .long  0xf2c06010                          // vmov.i32      d22, #0
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2401d20                          // vadd.f32      d17, d0, d16
+  .long  0xf2410d20                          // vadd.f32      d16, d1, d16
+  .long  0xed8c0b00                          // vstr          d0, [ip]
+  .long  0xed8c1b08                          // vstr          d1, [ip, #32]
+  .long  0xf3fb2721                          // vcvt.s32.f32  d18, d17
+  .long  0xf3fb3720                          // vcvt.s32.f32  d19, d16
+  .long  0xf3fb2622                          // vcvt.f32.s32  d18, d18
+  .long  0xf3fb3623                          // vcvt.f32.s32  d19, d19
+  .long  0xf3624ea1                          // vcgt.f32      d20, d18, d17
+  .long  0xf3635ea0                          // vcgt.f32      d21, d19, d16
+  .long  0xf35741b6                          // vbsl          d20, d23, d22
+  .long  0xf35751b6                          // vbsl          d21, d23, d22
+  .long  0xf2622da4                          // vsub.f32      d18, d18, d20
+  .long  0xf2633da5                          // vsub.f32      d19, d19, d21
+  .long  0xf2611da2                          // vsub.f32      d17, d17, d18
+  .long  0xf2600da3                          // vsub.f32      d16, d16, d19
+  .long  0xedcc1b10                          // vstr          d17, [ip, #64]
+  .long  0xedcc0b18                          // vstr          d16, [ip, #96]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0x3f800000                          // .word         0x3f800000
+  .long  0x3f800000                          // .word         0x3f800000
+
+HIDDEN _sk_accumulate_vfp4
+.globl _sk_accumulate_vfp4
+FUNCTION(_sk_accumulate_vfp4)
+_sk_accumulate_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591e000                          // ldr           lr, [r1]
+  .long  0xe591c004                          // ldr           ip, [r1, #4]
+  .long  0xe2811008                          // add           r1, r1, #8
+  .long  0xedde1b28                          // vldr          d17, [lr, #160]
+  .long  0xedde0b20                          // vldr          d16, [lr, #128]
+  .long  0xf3400db1                          // vmul.f32      d16, d16, d17
+  .long  0xf2004c90                          // vfma.f32      d4, d16, d0
+  .long  0xf2005c91                          // vfma.f32      d5, d16, d1
+  .long  0xf2006c92                          // vfma.f32      d6, d16, d2
+  .long  0xf2007c93                          // vfma.f32      d7, d16, d3
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_bilinear_nx_vfp4
+.globl _sk_bilinear_nx_vfp4
+FUNCTION(_sk_bilinear_nx_vfp4)
+_sk_bilinear_nx_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xeddc2b10                          // vldr          d18, [ip, #64]
+  .long  0xf2600da2                          // vsub.f32      d16, d16, d18
+  .long  0xeddc1b00                          // vldr          d17, [ip]
+  .long  0xf3c3261f                          // vmov.i32      d18, #-1090519040
+  .long  0xf2010da2                          // vadd.f32      d0, d17, d18
+  .long  0xedcc0b20                          // vstr          d16, [ip, #128]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_bilinear_px_vfp4
+.globl _sk_bilinear_px_vfp4
+FUNCTION(_sk_bilinear_px_vfp4)
+_sk_bilinear_px_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c3061f                          // vmov.i32      d16, #1056964608
+  .long  0xeddc2b10                          // vldr          d18, [ip, #64]
+  .long  0xeddc1b00                          // vldr          d17, [ip]
+  .long  0xedcc2b20                          // vstr          d18, [ip, #128]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xf2010da0                          // vadd.f32      d0, d17, d16
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_bilinear_ny_vfp4
+.globl _sk_bilinear_ny_vfp4
+FUNCTION(_sk_bilinear_ny_vfp4)
+_sk_bilinear_ny_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xeddc2b18                          // vldr          d18, [ip, #96]
+  .long  0xf2600da2                          // vsub.f32      d16, d16, d18
+  .long  0xeddc1b08                          // vldr          d17, [ip, #32]
+  .long  0xf3c3261f                          // vmov.i32      d18, #-1090519040
+  .long  0xf2011da2                          // vadd.f32      d1, d17, d18
+  .long  0xedcc0b28                          // vstr          d16, [ip, #160]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+
+HIDDEN _sk_bilinear_py_vfp4
+.globl _sk_bilinear_py_vfp4
+FUNCTION(_sk_bilinear_py_vfp4)
+_sk_bilinear_py_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c3061f                          // vmov.i32      d16, #1056964608
+  .long  0xeddc2b18                          // vldr          d18, [ip, #96]
+  .long  0xeddc1b08                          // vldr          d17, [ip, #32]
+  .long  0xedcc2b28                          // vstr          d18, [ip, #160]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xf2011da0                          // vadd.f32      d1, d17, d16
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+
+HIDDEN _sk_bicubic_n3x_vfp4
+.globl _sk_bicubic_n3x_vfp4
+FUNCTION(_sk_bicubic_n3x_vfp4)
+_sk_bicubic_n3x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xeddf3b11                          // vldr          d19, [pc, #68]
+  .long  0xeddc2b10                          // vldr          d18, [ip, #64]
+  .long  0xf2600da2                          // vsub.f32      d16, d16, d18
+  .long  0xeddf2b0c                          // vldr          d18, [pc, #48]
+  .long  0xeddc1b00                          // vldr          d17, [ip]
+  .long  0xf2403cb2                          // vfma.f32      d19, d16, d18
+  .long  0xf3400db0                          // vmul.f32      d16, d16, d16
+  .long  0xf3c72f18                          // vmov.f32      d18, #-1.5
+  .long  0xf2010da2                          // vadd.f32      d0, d17, d18
+  .long  0xf3400db3                          // vmul.f32      d16, d16, d19
+  .long  0xedcc0b20                          // vstr          d16, [ip, #128]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+
+HIDDEN _sk_bicubic_n1x_vfp4
+.globl _sk_bicubic_n1x_vfp4
+FUNCTION(_sk_bicubic_n1x_vfp4)
+_sk_bicubic_n1x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xf2c73f18                          // vmov.f32      d19, #1.5
+  .long  0xeddc2b10                          // vldr          d18, [ip, #64]
+  .long  0xf2600da2                          // vsub.f32      d16, d16, d18
+  .long  0xeddf2b0e                          // vldr          d18, [pc, #56]
+  .long  0xeddc1b00                          // vldr          d17, [ip]
+  .long  0xf2403cb2                          // vfma.f32      d19, d16, d18
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xf2402cb3                          // vfma.f32      d18, d16, d19
+  .long  0xeddf3b0b                          // vldr          d19, [pc, #44]
+  .long  0xf2403cb2                          // vfma.f32      d19, d16, d18
+  .long  0xf3c3061f                          // vmov.i32      d16, #-1090519040
+  .long  0xf2010da0                          // vadd.f32      d0, d17, d16
+  .long  0xedcc3b20                          // vstr          d19, [ip, #128]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0x3d638e39                          // .word         0x3d638e39
+  .long  0x3d638e39                          // .word         0x3d638e39
+
+HIDDEN _sk_bicubic_p1x_vfp4
+.globl _sk_bicubic_p1x_vfp4
+FUNCTION(_sk_bicubic_p1x_vfp4)
+_sk_bicubic_p1x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c71f18                          // vmov.f32      d17, #1.5
+  .long  0xeddf0b0f                          // vldr          d16, [pc, #60]
+  .long  0xeddc3b10                          // vldr          d19, [ip, #64]
+  .long  0xf2431cb0                          // vfma.f32      d17, d19, d16
+  .long  0xeddc2b00                          // vldr          d18, [ip]
+  .long  0xf2c3061f                          // vmov.i32      d16, #1056964608
+  .long  0xf2020da0                          // vadd.f32      d0, d18, d16
+  .long  0xf2430cb1                          // vfma.f32      d16, d19, d17
+  .long  0xeddf1b0a                          // vldr          d17, [pc, #40]
+  .long  0xf2431cb0                          // vfma.f32      d17, d19, d16
+  .long  0xedcc1b20                          // vstr          d17, [ip, #128]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0x3d638e39                          // .word         0x3d638e39
+  .long  0x3d638e39                          // .word         0x3d638e39
+
+HIDDEN _sk_bicubic_p3x_vfp4
+.globl _sk_bicubic_p3x_vfp4
+FUNCTION(_sk_bicubic_p3x_vfp4)
+_sk_bicubic_p3x_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xeddf0b0e                          // vldr          d16, [pc, #56]
+  .long  0xeddf3b0f                          // vldr          d19, [pc, #60]
+  .long  0xeddc2b10                          // vldr          d18, [ip, #64]
+  .long  0xf2423cb0                          // vfma.f32      d19, d18, d16
+  .long  0xeddc1b00                          // vldr          d17, [ip]
+  .long  0xf3420db2                          // vmul.f32      d16, d18, d18
+  .long  0xf2c72f18                          // vmov.f32      d18, #1.5
+  .long  0xf2010da2                          // vadd.f32      d0, d17, d18
+  .long  0xf3400db3                          // vmul.f32      d16, d16, d19
+  .long  0xedcc0b20                          // vstr          d16, [ip, #128]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+
+HIDDEN _sk_bicubic_n3y_vfp4
+.globl _sk_bicubic_n3y_vfp4
+FUNCTION(_sk_bicubic_n3y_vfp4)
+_sk_bicubic_n3y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xeddf3b11                          // vldr          d19, [pc, #68]
+  .long  0xeddc2b18                          // vldr          d18, [ip, #96]
+  .long  0xf2600da2                          // vsub.f32      d16, d16, d18
+  .long  0xeddf2b0c                          // vldr          d18, [pc, #48]
+  .long  0xeddc1b08                          // vldr          d17, [ip, #32]
+  .long  0xf2403cb2                          // vfma.f32      d19, d16, d18
+  .long  0xf3400db0                          // vmul.f32      d16, d16, d16
+  .long  0xf3c72f18                          // vmov.f32      d18, #-1.5
+  .long  0xf2011da2                          // vadd.f32      d1, d17, d18
+  .long  0xf3400db3                          // vmul.f32      d16, d16, d19
+  .long  0xedcc0b28                          // vstr          d16, [ip, #160]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+
+HIDDEN _sk_bicubic_n1y_vfp4
+.globl _sk_bicubic_n1y_vfp4
+FUNCTION(_sk_bicubic_n1y_vfp4)
+_sk_bicubic_n1y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c70f10                          // vmov.f32      d16, #1
+  .long  0xf2c73f18                          // vmov.f32      d19, #1.5
+  .long  0xeddc2b18                          // vldr          d18, [ip, #96]
+  .long  0xf2600da2                          // vsub.f32      d16, d16, d18
+  .long  0xeddf2b0e                          // vldr          d18, [pc, #56]
+  .long  0xeddc1b08                          // vldr          d17, [ip, #32]
+  .long  0xf2403cb2                          // vfma.f32      d19, d16, d18
+  .long  0xf2c3261f                          // vmov.i32      d18, #1056964608
+  .long  0xf2402cb3                          // vfma.f32      d18, d16, d19
+  .long  0xeddf3b0b                          // vldr          d19, [pc, #44]
+  .long  0xf2403cb2                          // vfma.f32      d19, d16, d18
+  .long  0xf3c3061f                          // vmov.i32      d16, #-1090519040
+  .long  0xf2011da0                          // vadd.f32      d1, d17, d16
+  .long  0xedcc3b28                          // vstr          d19, [ip, #160]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0x3d638e39                          // .word         0x3d638e39
+  .long  0x3d638e39                          // .word         0x3d638e39
+
+HIDDEN _sk_bicubic_p1y_vfp4
+.globl _sk_bicubic_p1y_vfp4
+FUNCTION(_sk_bicubic_p1y_vfp4)
+_sk_bicubic_p1y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xf2c71f18                          // vmov.f32      d17, #1.5
+  .long  0xeddf0b0f                          // vldr          d16, [pc, #60]
+  .long  0xeddc3b18                          // vldr          d19, [ip, #96]
+  .long  0xf2431cb0                          // vfma.f32      d17, d19, d16
+  .long  0xeddc2b08                          // vldr          d18, [ip, #32]
+  .long  0xf2c3061f                          // vmov.i32      d16, #1056964608
+  .long  0xf2021da0                          // vadd.f32      d1, d18, d16
+  .long  0xf2430cb1                          // vfma.f32      d16, d19, d17
+  .long  0xeddf1b0a                          // vldr          d17, [pc, #40]
+  .long  0xf2431cb0                          // vfma.f32      d17, d19, d16
+  .long  0xedcc1b28                          // vstr          d17, [ip, #160]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0xe320f000                          // nop           {0}
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0xbf955555                          // .word         0xbf955555
+  .long  0x3d638e39                          // .word         0x3d638e39
+  .long  0x3d638e39                          // .word         0x3d638e39
+
+HIDDEN _sk_bicubic_p3y_vfp4
+.globl _sk_bicubic_p3y_vfp4
+FUNCTION(_sk_bicubic_p3y_vfp4)
+_sk_bicubic_p3y_vfp4:
+  .long  0xe92d4800                          // push          {fp, lr}
+  .long  0xe591c000                          // ldr           ip, [r1]
+  .long  0xeddf0b0e                          // vldr          d16, [pc, #56]
+  .long  0xeddf3b0f                          // vldr          d19, [pc, #60]
+  .long  0xeddc2b18                          // vldr          d18, [ip, #96]
+  .long  0xf2423cb0                          // vfma.f32      d19, d18, d16
+  .long  0xeddc1b08                          // vldr          d17, [ip, #32]
+  .long  0xf3420db2                          // vmul.f32      d16, d18, d18
+  .long  0xf2c72f18                          // vmov.f32      d18, #1.5
+  .long  0xf2011da2                          // vadd.f32      d1, d17, d18
+  .long  0xf3400db3                          // vmul.f32      d16, d16, d19
+  .long  0xedcc0b28                          // vstr          d16, [ip, #160]
+  .long  0xe281c008                          // add           ip, r1, #8
+  .long  0xe591e004                          // ldr           lr, [r1, #4]
+  .long  0xe1a0100c                          // mov           r1, ip
+  .long  0xe1a0c00e                          // mov           ip, lr
+  .long  0xe8bd4800                          // pop           {fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0x3ec71c72                          // .word         0x3ec71c72
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+  .long  0xbeaaaaab                          // .word         0xbeaaaaab
+
+HIDDEN _sk_callback_vfp4
+.globl _sk_callback_vfp4
+FUNCTION(_sk_callback_vfp4)
+_sk_callback_vfp4:
+  .long  0xe92d4df0                          // push          {r4, r5, r6, r7, r8, sl, fp, lr}
+  .long  0xe28db018                          // add           fp, sp, #24
+  .long  0xed2d8b08                          // vpush         {d8-d11}
+  .long  0xe1a06001                          // mov           r6, r1
+  .long  0xe1a07000                          // mov           r7, r0
+  .long  0xe5964000                          // ldr           r4, [r6]
+  .long  0xe1a05002                          // mov           r5, r2
+  .long  0xe59b1008                          // ldr           r1, [fp, #8]
+  .long  0xeeb08b47                          // vmov.f64      d8, d7
+  .long  0xe2840004                          // add           r0, r4, #4
+  .long  0xe1a08003                          // mov           r8, r3
+  .long  0xe3510000                          // cmp           r1, #0
+  .long  0x03a01002                          // moveq         r1, #2
+  .long  0xf400008f                          // vst4.32       {d0-d3}, [r0]
+  .long  0xe1a00004                          // mov           r0, r4
+  .long  0xe5942000                          // ldr           r2, [r4]
+  .long  0xeeb09b46                          // vmov.f64      d9, d6
+  .long  0xeeb0ab45                          // vmov.f64      d10, d5
+  .long  0xeeb0bb44                          // vmov.f64      d11, d4
+  .long  0xe12fff32                          // blx           r2
+  .long  0xe5940084                          // ldr           r0, [r4, #132]
+  .long  0xe2861008                          // add           r1, r6, #8
+  .long  0xe596c004                          // ldr           ip, [r6, #4]
+  .long  0xe1a02005                          // mov           r2, r5
+  .long  0xe1a03008                          // mov           r3, r8
+  .long  0xeeb04b4b                          // vmov.f64      d4, d11
+  .long  0xf420008f                          // vld4.32       {d0-d3}, [r0]
+  .long  0xe1a00007                          // mov           r0, r7
+  .long  0xeeb05b4a                          // vmov.f64      d5, d10
+  .long  0xeeb06b49                          // vmov.f64      d6, d9
+  .long  0xeeb07b48                          // vmov.f64      d7, d8
+  .long  0xecbd8b08                          // vpop          {d8-d11}
+  .long  0xe8bd4df0                          // pop           {r4, r5, r6, r7, r8, sl, fp, lr}
+  .long  0xe12fff1c                          // bx            ip
+#elif defined(__x86_64__)
+BALIGN32
+
+HIDDEN _sk_start_pipeline_hsw
+.globl _sk_start_pipeline_hsw
+FUNCTION(_sk_start_pipeline_hsw)
+_sk_start_pipeline_hsw:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  80                                  // push          %rax
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  73,137,208                          // mov           %rdx,%r8
+  .byte  73,137,244                          // mov           %rsi,%r12
+  .byte  73,137,254                          // mov           %rdi,%r14
+  .byte  72,137,206                          // mov           %rcx,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,197                          // mov           %rax,%r13
+  .byte  73,137,247                          // mov           %rsi,%r15
+  .byte  73,141,78,8                         // lea           0x8(%r14),%rcx
+  .byte  76,57,193                           // cmp           %r8,%rcx
+  .byte  118,5                               // jbe           33 <_sk_start_pipeline_hsw+0x33>
+  .byte  76,137,242                          // mov           %r14,%rdx
+  .byte  235,77                              // jmp           80 <_sk_start_pipeline_hsw+0x80>
+  .byte  76,137,69,208                       // mov           %r8,-0x30(%rbp)
+  .byte  65,184,0,0,0,0                      // mov           $0x0,%r8d
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  197,220,87,228                      // vxorps        %ymm4,%ymm4,%ymm4
+  .byte  197,212,87,237                      // vxorps        %ymm5,%ymm5,%ymm5
+  .byte  197,204,87,246                      // vxorps        %ymm6,%ymm6,%ymm6
+  .byte  197,196,87,255                      // vxorps        %ymm7,%ymm7,%ymm7
+  .byte  72,137,223                          // mov           %rbx,%rdi
+  .byte  76,137,254                          // mov           %r15,%rsi
+  .byte  76,137,242                          // mov           %r14,%rdx
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  65,255,213                          // callq         *%r13
+  .byte  76,139,69,208                       // mov           -0x30(%rbp),%r8
+  .byte  73,141,86,8                         // lea           0x8(%r14),%rdx
+  .byte  73,131,198,16                       // add           $0x10,%r14
+  .byte  77,57,198                           // cmp           %r8,%r14
+  .byte  73,137,214                          // mov           %rdx,%r14
+  .byte  118,183                             // jbe           37 <_sk_start_pipeline_hsw+0x37>
+  .byte  73,41,208                           // sub           %rdx,%r8
+  .byte  116,44                              // je            b1 <_sk_start_pipeline_hsw+0xb1>
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  197,220,87,228                      // vxorps        %ymm4,%ymm4,%ymm4
+  .byte  197,212,87,237                      // vxorps        %ymm5,%ymm5,%ymm5
+  .byte  197,204,87,246                      // vxorps        %ymm6,%ymm6,%ymm6
+  .byte  197,196,87,255                      // vxorps        %ymm7,%ymm7,%ymm7
+  .byte  72,137,223                          // mov           %rbx,%rdi
+  .byte  76,137,254                          // mov           %r15,%rsi
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  65,255,213                          // callq         *%r13
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  197,248,119                         // vzeroupper
+  .byte  195                                 // retq
+
+HIDDEN _sk_just_return_hsw
+.globl _sk_just_return_hsw
+FUNCTION(_sk_just_return_hsw)
+_sk_just_return_hsw:
+  .byte  195                                 // retq
+
+HIDDEN _sk_seed_shader_hsw
+.globl _sk_seed_shader_hsw
+FUNCTION(_sk_seed_shader_hsw)
+_sk_seed_shader_hsw:
+  .byte  197,249,110,194                     // vmovd         %edx,%xmm0
+  .byte  196,226,125,88,192                  // vpbroadcastd  %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,194,72,0,0        // vbroadcastss  0x48c2(%rip),%ymm1        # 499c <_sk_callback_hsw+0x144>
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,252,88,7                        // vaddps        (%rdi),%ymm0,%ymm0
+  .byte  197,249,110,209                     // vmovd         %ecx,%xmm2
+  .byte  196,226,125,88,210                  // vpbroadcastd  %xmm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  197,236,88,201                      // vaddps        %ymm1,%ymm2,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,21,162,72,0,0        // vbroadcastss  0x48a2(%rip),%ymm2        # 49a0 <_sk_callback_hsw+0x148>
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  197,220,87,228                      // vxorps        %ymm4,%ymm4,%ymm4
+  .byte  197,212,87,237                      // vxorps        %ymm5,%ymm5,%ymm5
+  .byte  197,204,87,246                      // vxorps        %ymm6,%ymm6,%ymm6
+  .byte  197,196,87,255                      // vxorps        %ymm7,%ymm7,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dither_hsw
+.globl _sk_dither_hsw
+FUNCTION(_sk_dither_hsw)
+_sk_dither_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,121,110,194                     // vmovd         %edx,%xmm8
+  .byte  196,66,125,88,192                   // vpbroadcastd  %xmm8,%ymm8
+  .byte  197,61,254,71,32                    // vpaddd        0x20(%rdi),%ymm8,%ymm8
+  .byte  197,121,110,201                     // vmovd         %ecx,%xmm9
+  .byte  196,66,125,88,201                   // vpbroadcastd  %xmm9,%ymm9
+  .byte  196,65,53,239,200                   // vpxor         %ymm8,%ymm9,%ymm9
+  .byte  196,98,125,88,21,105,72,0,0         // vpbroadcastd  0x4869(%rip),%ymm10        # 49a4 <_sk_callback_hsw+0x14c>
+  .byte  196,65,53,219,218                   // vpand         %ymm10,%ymm9,%ymm11
+  .byte  196,193,37,114,243,5                // vpslld        $0x5,%ymm11,%ymm11
+  .byte  196,65,61,219,210                   // vpand         %ymm10,%ymm8,%ymm10
+  .byte  196,193,45,114,242,4                // vpslld        $0x4,%ymm10,%ymm10
+  .byte  196,98,125,88,37,78,72,0,0          // vpbroadcastd  0x484e(%rip),%ymm12        # 49a8 <_sk_callback_hsw+0x150>
+  .byte  196,98,125,88,45,73,72,0,0          // vpbroadcastd  0x4849(%rip),%ymm13        # 49ac <_sk_callback_hsw+0x154>
+  .byte  196,65,53,219,245                   // vpand         %ymm13,%ymm9,%ymm14
+  .byte  196,193,13,114,246,2                // vpslld        $0x2,%ymm14,%ymm14
+  .byte  196,65,61,219,237                   // vpand         %ymm13,%ymm8,%ymm13
+  .byte  196,65,21,254,237                   // vpaddd        %ymm13,%ymm13,%ymm13
+  .byte  196,65,53,219,204                   // vpand         %ymm12,%ymm9,%ymm9
+  .byte  196,193,53,114,209,1                // vpsrld        $0x1,%ymm9,%ymm9
+  .byte  196,65,61,219,196                   // vpand         %ymm12,%ymm8,%ymm8
+  .byte  196,193,61,114,208,2                // vpsrld        $0x2,%ymm8,%ymm8
+  .byte  196,65,21,235,210                   // vpor          %ymm10,%ymm13,%ymm10
+  .byte  196,65,45,235,192                   // vpor          %ymm8,%ymm10,%ymm8
+  .byte  196,65,37,235,214                   // vpor          %ymm14,%ymm11,%ymm10
+  .byte  196,65,61,235,194                   // vpor          %ymm10,%ymm8,%ymm8
+  .byte  196,65,61,235,193                   // vpor          %ymm9,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,13,251,71,0,0         // vbroadcastss  0x47fb(%rip),%ymm9        # 49b0 <_sk_callback_hsw+0x158>
+  .byte  196,98,125,24,21,246,71,0,0         // vbroadcastss  0x47f6(%rip),%ymm10        # 49b4 <_sk_callback_hsw+0x15c>
+  .byte  196,66,61,184,209                   // vfmadd231ps   %ymm9,%ymm8,%ymm10
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,65,60,89,194                    // vmulps        %ymm10,%ymm8,%ymm8
+  .byte  197,188,88,192                      // vaddps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,88,201                      // vaddps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,88,210                      // vaddps        %ymm2,%ymm8,%ymm2
+  .byte  197,252,93,195                      // vminps        %ymm3,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,192                      // vmaxps        %ymm0,%ymm8,%ymm0
+  .byte  197,244,93,203                      // vminps        %ymm3,%ymm1,%ymm1
+  .byte  197,188,95,201                      // vmaxps        %ymm1,%ymm8,%ymm1
+  .byte  197,236,93,211                      // vminps        %ymm3,%ymm2,%ymm2
+  .byte  197,188,95,210                      // vmaxps        %ymm2,%ymm8,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_constant_color_hsw
+.globl _sk_constant_color_hsw
+FUNCTION(_sk_constant_color_hsw)
+_sk_constant_color_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,0                    // vbroadcastss  (%rax),%ymm0
+  .byte  196,226,125,24,72,4                 // vbroadcastss  0x4(%rax),%ymm1
+  .byte  196,226,125,24,80,8                 // vbroadcastss  0x8(%rax),%ymm2
+  .byte  196,226,125,24,88,12                // vbroadcastss  0xc(%rax),%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_rgba_hsw
+.globl _sk_load_rgba_hsw
+FUNCTION(_sk_load_rgba_hsw)
+_sk_load_rgba_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,0                        // vmovups       (%rax),%ymm0
+  .byte  197,252,16,72,32                    // vmovups       0x20(%rax),%ymm1
+  .byte  197,252,16,80,64                    // vmovups       0x40(%rax),%ymm2
+  .byte  197,252,16,88,96                    // vmovups       0x60(%rax),%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_rgba_hsw
+.globl _sk_store_rgba_hsw
+FUNCTION(_sk_store_rgba_hsw)
+_sk_store_rgba_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,17,0                        // vmovups       %ymm0,(%rax)
+  .byte  197,252,17,72,32                    // vmovups       %ymm1,0x20(%rax)
+  .byte  197,252,17,80,64                    // vmovups       %ymm2,0x40(%rax)
+  .byte  197,252,17,88,96                    // vmovups       %ymm3,0x60(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clear_hsw
+.globl _sk_clear_hsw
+FUNCTION(_sk_clear_hsw)
+_sk_clear_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcatop_hsw
+.globl _sk_srcatop_hsw
+FUNCTION(_sk_srcatop_hsw)
+_sk_srcatop_hsw:
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  196,98,125,24,5,78,71,0,0           // vbroadcastss  0x474e(%rip),%ymm8        # 49b8 <_sk_callback_hsw+0x160>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,226,61,184,196                  // vfmadd231ps   %ymm4,%ymm8,%ymm0
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  196,226,61,184,205                  // vfmadd231ps   %ymm5,%ymm8,%ymm1
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  196,226,61,184,214                  // vfmadd231ps   %ymm6,%ymm8,%ymm2
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  196,194,69,168,216                  // vfmadd213ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstatop_hsw
+.globl _sk_dstatop_hsw
+FUNCTION(_sk_dstatop_hsw)
+_sk_dstatop_hsw:
+  .byte  196,98,125,24,5,33,71,0,0           // vbroadcastss  0x4721(%rip),%ymm8        # 49bc <_sk_callback_hsw+0x164>
+  .byte  197,60,92,199                       // vsubps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  196,226,101,184,196                 // vfmadd231ps   %ymm4,%ymm3,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  196,226,101,184,205                 // vfmadd231ps   %ymm5,%ymm3,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  196,226,101,184,214                 // vfmadd231ps   %ymm6,%ymm3,%ymm2
+  .byte  197,60,89,195                       // vmulps        %ymm3,%ymm8,%ymm8
+  .byte  196,194,69,168,216                  // vfmadd213ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcin_hsw
+.globl _sk_srcin_hsw
+FUNCTION(_sk_srcin_hsw)
+_sk_srcin_hsw:
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstin_hsw
+.globl _sk_dstin_hsw
+FUNCTION(_sk_dstin_hsw)
+_sk_dstin_hsw:
+  .byte  197,228,89,196                      // vmulps        %ymm4,%ymm3,%ymm0
+  .byte  197,228,89,205                      // vmulps        %ymm5,%ymm3,%ymm1
+  .byte  197,228,89,214                      // vmulps        %ymm6,%ymm3,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcout_hsw
+.globl _sk_srcout_hsw
+FUNCTION(_sk_srcout_hsw)
+_sk_srcout_hsw:
+  .byte  196,98,125,24,5,200,70,0,0          // vbroadcastss  0x46c8(%rip),%ymm8        # 49c0 <_sk_callback_hsw+0x168>
+  .byte  197,60,92,199                       // vsubps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstout_hsw
+.globl _sk_dstout_hsw
+FUNCTION(_sk_dstout_hsw)
+_sk_dstout_hsw:
+  .byte  196,226,125,24,5,171,70,0,0         // vbroadcastss  0x46ab(%rip),%ymm0        # 49c4 <_sk_callback_hsw+0x16c>
+  .byte  197,252,92,219                      // vsubps        %ymm3,%ymm0,%ymm3
+  .byte  197,228,89,196                      // vmulps        %ymm4,%ymm3,%ymm0
+  .byte  197,228,89,205                      // vmulps        %ymm5,%ymm3,%ymm1
+  .byte  197,228,89,214                      // vmulps        %ymm6,%ymm3,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_hsw
+.globl _sk_srcover_hsw
+FUNCTION(_sk_srcover_hsw)
+_sk_srcover_hsw:
+  .byte  196,98,125,24,5,142,70,0,0          // vbroadcastss  0x468e(%rip),%ymm8        # 49c8 <_sk_callback_hsw+0x170>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,194,93,184,192                  // vfmadd231ps   %ymm8,%ymm4,%ymm0
+  .byte  196,194,85,184,200                  // vfmadd231ps   %ymm8,%ymm5,%ymm1
+  .byte  196,194,77,184,208                  // vfmadd231ps   %ymm8,%ymm6,%ymm2
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstover_hsw
+.globl _sk_dstover_hsw
+FUNCTION(_sk_dstover_hsw)
+_sk_dstover_hsw:
+  .byte  196,98,125,24,5,109,70,0,0          // vbroadcastss  0x466d(%rip),%ymm8        # 49cc <_sk_callback_hsw+0x174>
+  .byte  197,60,92,199                       // vsubps        %ymm7,%ymm8,%ymm8
+  .byte  196,226,61,168,196                  // vfmadd213ps   %ymm4,%ymm8,%ymm0
+  .byte  196,226,61,168,205                  // vfmadd213ps   %ymm5,%ymm8,%ymm1
+  .byte  196,226,61,168,214                  // vfmadd213ps   %ymm6,%ymm8,%ymm2
+  .byte  196,226,61,168,223                  // vfmadd213ps   %ymm7,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_modulate_hsw
+.globl _sk_modulate_hsw
+FUNCTION(_sk_modulate_hsw)
+_sk_modulate_hsw:
+  .byte  197,252,89,196                      // vmulps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,89,205                      // vmulps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,89,214                      // vmulps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_multiply_hsw
+.globl _sk_multiply_hsw
+FUNCTION(_sk_multiply_hsw)
+_sk_multiply_hsw:
+  .byte  196,98,125,24,5,56,70,0,0           // vbroadcastss  0x4638(%rip),%ymm8        # 49d0 <_sk_callback_hsw+0x178>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,52,89,208                       // vmulps        %ymm0,%ymm9,%ymm10
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,98,61,184,212                   // vfmadd231ps   %ymm4,%ymm8,%ymm10
+  .byte  196,194,93,168,194                  // vfmadd213ps   %ymm10,%ymm4,%ymm0
+  .byte  197,52,89,209                       // vmulps        %ymm1,%ymm9,%ymm10
+  .byte  196,98,61,184,213                   // vfmadd231ps   %ymm5,%ymm8,%ymm10
+  .byte  196,194,85,168,202                  // vfmadd213ps   %ymm10,%ymm5,%ymm1
+  .byte  197,52,89,210                       // vmulps        %ymm2,%ymm9,%ymm10
+  .byte  196,98,61,184,214                   // vfmadd231ps   %ymm6,%ymm8,%ymm10
+  .byte  196,194,77,168,210                  // vfmadd213ps   %ymm10,%ymm6,%ymm2
+  .byte  197,52,89,203                       // vmulps        %ymm3,%ymm9,%ymm9
+  .byte  196,66,69,168,193                   // vfmadd213ps   %ymm9,%ymm7,%ymm8
+  .byte  196,194,69,168,216                  // vfmadd213ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_plus__hsw
+.globl _sk_plus__hsw
+FUNCTION(_sk_plus__hsw)
+_sk_plus__hsw:
+  .byte  197,252,88,196                      // vaddps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,88,205                      // vaddps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,88,214                      // vaddps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_screen_hsw
+.globl _sk_screen_hsw
+FUNCTION(_sk_screen_hsw)
+_sk_screen_hsw:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  196,194,93,172,192                  // vfnmadd213ps  %ymm8,%ymm4,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  196,194,85,172,200                  // vfnmadd213ps  %ymm8,%ymm5,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  196,194,77,172,208                  // vfnmadd213ps  %ymm8,%ymm6,%ymm2
+  .byte  197,100,88,199                      // vaddps        %ymm7,%ymm3,%ymm8
+  .byte  196,194,69,172,216                  // vfnmadd213ps  %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xor__hsw
+.globl _sk_xor__hsw
+FUNCTION(_sk_xor__hsw)
+_sk_xor__hsw:
+  .byte  196,98,125,24,5,179,69,0,0          // vbroadcastss  0x45b3(%rip),%ymm8        # 49d4 <_sk_callback_hsw+0x17c>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,226,61,184,196                  // vfmadd231ps   %ymm4,%ymm8,%ymm0
+  .byte  197,180,89,201                      // vmulps        %ymm1,%ymm9,%ymm1
+  .byte  196,226,61,184,205                  // vfmadd231ps   %ymm5,%ymm8,%ymm1
+  .byte  197,180,89,210                      // vmulps        %ymm2,%ymm9,%ymm2
+  .byte  196,226,61,184,214                  // vfmadd231ps   %ymm6,%ymm8,%ymm2
+  .byte  197,180,89,219                      // vmulps        %ymm3,%ymm9,%ymm3
+  .byte  196,98,69,168,195                   // vfmadd213ps   %ymm3,%ymm7,%ymm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,195                      // vmovaps       %ymm8,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_darken_hsw
+.globl _sk_darken_hsw
+FUNCTION(_sk_darken_hsw)
+_sk_darken_hsw:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  196,193,124,95,193                  // vmaxps        %ymm9,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,193,116,95,201                  // vmaxps        %ymm9,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,100,89,206                      // vmulps        %ymm6,%ymm3,%ymm9
+  .byte  196,193,108,95,209                  // vmaxps        %ymm9,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,59,69,0,0           // vbroadcastss  0x453b(%rip),%ymm8        # 49d8 <_sk_callback_hsw+0x180>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lighten_hsw
+.globl _sk_lighten_hsw
+FUNCTION(_sk_lighten_hsw)
+_sk_lighten_hsw:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  196,193,124,93,193                  // vminps        %ymm9,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,193,116,93,201                  // vminps        %ymm9,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,100,89,206                      // vmulps        %ymm6,%ymm3,%ymm9
+  .byte  196,193,108,93,209                  // vminps        %ymm9,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,234,68,0,0          // vbroadcastss  0x44ea(%rip),%ymm8        # 49dc <_sk_callback_hsw+0x184>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_difference_hsw
+.globl _sk_difference_hsw
+FUNCTION(_sk_difference_hsw)
+_sk_difference_hsw:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  196,193,124,93,193                  // vminps        %ymm9,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,193,116,93,201                  // vminps        %ymm9,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,100,89,206                      // vmulps        %ymm6,%ymm3,%ymm9
+  .byte  196,193,108,93,209                  // vminps        %ymm9,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,141,68,0,0          // vbroadcastss  0x448d(%rip),%ymm8        # 49e0 <_sk_callback_hsw+0x188>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_exclusion_hsw
+.globl _sk_exclusion_hsw
+FUNCTION(_sk_exclusion_hsw)
+_sk_exclusion_hsw:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,196                      // vmulps        %ymm4,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,205                      // vmulps        %ymm5,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,214                      // vmulps        %ymm6,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,75,68,0,0           // vbroadcastss  0x444b(%rip),%ymm8        # 49e4 <_sk_callback_hsw+0x18c>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colorburn_hsw
+.globl _sk_colorburn_hsw
+FUNCTION(_sk_colorburn_hsw)
+_sk_colorburn_hsw:
+  .byte  196,98,125,24,5,57,68,0,0           // vbroadcastss  0x4439(%rip),%ymm8        # 49e8 <_sk_callback_hsw+0x190>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,52,89,216                       // vmulps        %ymm0,%ymm9,%ymm11
+  .byte  196,65,44,87,210                    // vxorps        %ymm10,%ymm10,%ymm10
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,228                       // vmulps        %ymm4,%ymm8,%ymm12
+  .byte  197,68,92,236                       // vsubps        %ymm4,%ymm7,%ymm13
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  197,20,94,232                       // vdivps        %ymm0,%ymm13,%ymm13
+  .byte  196,65,68,93,237                    // vminps        %ymm13,%ymm7,%ymm13
+  .byte  196,65,68,92,237                    // vsubps        %ymm13,%ymm7,%ymm13
+  .byte  196,66,101,168,235                  // vfmadd213ps   %ymm11,%ymm3,%ymm13
+  .byte  196,65,28,88,237                    // vaddps        %ymm13,%ymm12,%ymm13
+  .byte  197,28,88,224                       // vaddps        %ymm0,%ymm12,%ymm12
+  .byte  196,193,124,194,194,0               // vcmpeqps      %ymm10,%ymm0,%ymm0
+  .byte  196,195,21,74,196,0                 // vblendvps     %ymm0,%ymm12,%ymm13,%ymm0
+  .byte  197,92,194,231,0                    // vcmpeqps      %ymm7,%ymm4,%ymm12
+  .byte  197,36,88,220                       // vaddps        %ymm4,%ymm11,%ymm11
+  .byte  196,195,125,74,195,192              // vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  .byte  197,52,89,217                       // vmulps        %ymm1,%ymm9,%ymm11
+  .byte  197,60,89,229                       // vmulps        %ymm5,%ymm8,%ymm12
+  .byte  197,68,92,237                       // vsubps        %ymm5,%ymm7,%ymm13
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  197,20,94,233                       // vdivps        %ymm1,%ymm13,%ymm13
+  .byte  196,65,68,93,237                    // vminps        %ymm13,%ymm7,%ymm13
+  .byte  196,65,68,92,237                    // vsubps        %ymm13,%ymm7,%ymm13
+  .byte  196,66,101,168,235                  // vfmadd213ps   %ymm11,%ymm3,%ymm13
+  .byte  196,65,28,88,237                    // vaddps        %ymm13,%ymm12,%ymm13
+  .byte  197,28,88,225                       // vaddps        %ymm1,%ymm12,%ymm12
+  .byte  196,193,116,194,202,0               // vcmpeqps      %ymm10,%ymm1,%ymm1
+  .byte  196,195,21,74,204,16                // vblendvps     %ymm1,%ymm12,%ymm13,%ymm1
+  .byte  197,84,194,231,0                    // vcmpeqps      %ymm7,%ymm5,%ymm12
+  .byte  197,36,88,221                       // vaddps        %ymm5,%ymm11,%ymm11
+  .byte  196,195,117,74,203,192              // vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  .byte  197,52,89,202                       // vmulps        %ymm2,%ymm9,%ymm9
+  .byte  196,65,108,194,210,0                // vcmpeqps      %ymm10,%ymm2,%ymm10
+  .byte  197,60,89,222                       // vmulps        %ymm6,%ymm8,%ymm11
+  .byte  197,68,92,230                       // vsubps        %ymm6,%ymm7,%ymm12
+  .byte  197,28,89,227                       // vmulps        %ymm3,%ymm12,%ymm12
+  .byte  197,28,94,226                       // vdivps        %ymm2,%ymm12,%ymm12
+  .byte  197,164,88,210                      // vaddps        %ymm2,%ymm11,%ymm2
+  .byte  196,65,68,93,228                    // vminps        %ymm12,%ymm7,%ymm12
+  .byte  196,65,68,92,228                    // vsubps        %ymm12,%ymm7,%ymm12
+  .byte  196,66,101,168,225                  // vfmadd213ps   %ymm9,%ymm3,%ymm12
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,227,37,74,210,160               // vblendvps     %ymm10,%ymm2,%ymm11,%ymm2
+  .byte  197,76,194,215,0                    // vcmpeqps      %ymm7,%ymm6,%ymm10
+  .byte  197,52,88,206                       // vaddps        %ymm6,%ymm9,%ymm9
+  .byte  196,195,109,74,209,160              // vblendvps     %ymm10,%ymm9,%ymm2,%ymm2
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colordodge_hsw
+.globl _sk_colordodge_hsw
+FUNCTION(_sk_colordodge_hsw)
+_sk_colordodge_hsw:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,98,125,24,13,68,67,0,0          // vbroadcastss  0x4344(%rip),%ymm9        # 49ec <_sk_callback_hsw+0x194>
+  .byte  197,52,92,215                       // vsubps        %ymm7,%ymm9,%ymm10
+  .byte  197,44,89,216                       // vmulps        %ymm0,%ymm10,%ymm11
+  .byte  197,52,92,203                       // vsubps        %ymm3,%ymm9,%ymm9
+  .byte  197,100,89,228                      // vmulps        %ymm4,%ymm3,%ymm12
+  .byte  197,100,92,232                      // vsubps        %ymm0,%ymm3,%ymm13
+  .byte  196,65,28,94,229                    // vdivps        %ymm13,%ymm12,%ymm12
+  .byte  197,52,89,236                       // vmulps        %ymm4,%ymm9,%ymm13
+  .byte  196,65,68,93,228                    // vminps        %ymm12,%ymm7,%ymm12
+  .byte  196,66,101,168,227                  // vfmadd213ps   %ymm11,%ymm3,%ymm12
+  .byte  196,65,20,88,228                    // vaddps        %ymm12,%ymm13,%ymm12
+  .byte  197,20,88,232                       // vaddps        %ymm0,%ymm13,%ymm13
+  .byte  197,252,194,195,0                   // vcmpeqps      %ymm3,%ymm0,%ymm0
+  .byte  196,195,29,74,197,0                 // vblendvps     %ymm0,%ymm13,%ymm12,%ymm0
+  .byte  196,65,92,194,224,0                 // vcmpeqps      %ymm8,%ymm4,%ymm12
+  .byte  197,36,88,220                       // vaddps        %ymm4,%ymm11,%ymm11
+  .byte  196,195,125,74,195,192              // vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  .byte  197,44,89,217                       // vmulps        %ymm1,%ymm10,%ymm11
+  .byte  197,100,89,229                      // vmulps        %ymm5,%ymm3,%ymm12
+  .byte  197,100,92,233                      // vsubps        %ymm1,%ymm3,%ymm13
+  .byte  196,65,28,94,229                    // vdivps        %ymm13,%ymm12,%ymm12
+  .byte  197,52,89,237                       // vmulps        %ymm5,%ymm9,%ymm13
+  .byte  196,65,68,93,228                    // vminps        %ymm12,%ymm7,%ymm12
+  .byte  196,66,101,168,227                  // vfmadd213ps   %ymm11,%ymm3,%ymm12
+  .byte  196,65,20,88,228                    // vaddps        %ymm12,%ymm13,%ymm12
+  .byte  197,20,88,233                       // vaddps        %ymm1,%ymm13,%ymm13
+  .byte  197,244,194,203,0                   // vcmpeqps      %ymm3,%ymm1,%ymm1
+  .byte  196,195,29,74,205,16                // vblendvps     %ymm1,%ymm13,%ymm12,%ymm1
+  .byte  196,65,84,194,224,0                 // vcmpeqps      %ymm8,%ymm5,%ymm12
+  .byte  197,36,88,221                       // vaddps        %ymm5,%ymm11,%ymm11
+  .byte  196,195,117,74,203,192              // vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  197,100,89,222                      // vmulps        %ymm6,%ymm3,%ymm11
+  .byte  197,100,92,226                      // vsubps        %ymm2,%ymm3,%ymm12
+  .byte  196,65,36,94,220                    // vdivps        %ymm12,%ymm11,%ymm11
+  .byte  197,52,89,230                       // vmulps        %ymm6,%ymm9,%ymm12
+  .byte  196,65,68,93,219                    // vminps        %ymm11,%ymm7,%ymm11
+  .byte  196,66,101,168,218                  // vfmadd213ps   %ymm10,%ymm3,%ymm11
+  .byte  196,65,28,88,219                    // vaddps        %ymm11,%ymm12,%ymm11
+  .byte  197,28,88,226                       // vaddps        %ymm2,%ymm12,%ymm12
+  .byte  197,236,194,211,0                   // vcmpeqps      %ymm3,%ymm2,%ymm2
+  .byte  196,195,37,74,212,32                // vblendvps     %ymm2,%ymm12,%ymm11,%ymm2
+  .byte  196,65,76,194,192,0                 // vcmpeqps      %ymm8,%ymm6,%ymm8
+  .byte  197,44,88,214                       // vaddps        %ymm6,%ymm10,%ymm10
+  .byte  196,195,109,74,210,128              // vblendvps     %ymm8,%ymm10,%ymm2,%ymm2
+  .byte  196,194,69,184,217                  // vfmadd231ps   %ymm9,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hardlight_hsw
+.globl _sk_hardlight_hsw
+FUNCTION(_sk_hardlight_hsw)
+_sk_hardlight_hsw:
+  .byte  196,98,125,24,5,101,66,0,0          // vbroadcastss  0x4265(%rip),%ymm8        # 49f0 <_sk_callback_hsw+0x198>
+  .byte  197,60,92,215                       // vsubps        %ymm7,%ymm8,%ymm10
+  .byte  197,44,89,216                       // vmulps        %ymm0,%ymm10,%ymm11
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,98,61,184,220                   // vfmadd231ps   %ymm4,%ymm8,%ymm11
+  .byte  197,124,88,200                      // vaddps        %ymm0,%ymm0,%ymm9
+  .byte  197,52,194,227,2                    // vcmpleps      %ymm3,%ymm9,%ymm12
+  .byte  197,124,89,204                      // vmulps        %ymm4,%ymm0,%ymm9
+  .byte  196,65,52,88,233                    // vaddps        %ymm9,%ymm9,%ymm13
+  .byte  197,100,89,207                      // vmulps        %ymm7,%ymm3,%ymm9
+  .byte  197,68,92,244                       // vsubps        %ymm4,%ymm7,%ymm14
+  .byte  197,228,92,192                      // vsubps        %ymm0,%ymm3,%ymm0
+  .byte  196,193,124,89,198                  // vmulps        %ymm14,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,180,92,192                      // vsubps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,197,192              // vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  .byte  197,164,88,192                      // vaddps        %ymm0,%ymm11,%ymm0
+  .byte  197,44,89,217                       // vmulps        %ymm1,%ymm10,%ymm11
+  .byte  196,98,61,184,221                   // vfmadd231ps   %ymm5,%ymm8,%ymm11
+  .byte  197,116,88,225                      // vaddps        %ymm1,%ymm1,%ymm12
+  .byte  197,28,194,227,2                    // vcmpleps      %ymm3,%ymm12,%ymm12
+  .byte  197,116,89,237                      // vmulps        %ymm5,%ymm1,%ymm13
+  .byte  196,65,20,88,237                    // vaddps        %ymm13,%ymm13,%ymm13
+  .byte  197,68,92,245                       // vsubps        %ymm5,%ymm7,%ymm14
+  .byte  197,228,92,201                      // vsubps        %ymm1,%ymm3,%ymm1
+  .byte  196,193,116,89,206                  // vmulps        %ymm14,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,180,92,201                      // vsubps        %ymm1,%ymm9,%ymm1
+  .byte  196,195,117,74,205,192              // vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  196,98,61,184,214                   // vfmadd231ps   %ymm6,%ymm8,%ymm10
+  .byte  197,108,88,218                      // vaddps        %ymm2,%ymm2,%ymm11
+  .byte  197,36,194,219,2                    // vcmpleps      %ymm3,%ymm11,%ymm11
+  .byte  197,108,89,230                      // vmulps        %ymm6,%ymm2,%ymm12
+  .byte  196,65,28,88,228                    // vaddps        %ymm12,%ymm12,%ymm12
+  .byte  197,68,92,238                       // vsubps        %ymm6,%ymm7,%ymm13
+  .byte  197,228,92,210                      // vsubps        %ymm2,%ymm3,%ymm2
+  .byte  196,193,108,89,213                  // vmulps        %ymm13,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,180,92,210                      // vsubps        %ymm2,%ymm9,%ymm2
+  .byte  196,195,109,74,212,176              // vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_overlay_hsw
+.globl _sk_overlay_hsw
+FUNCTION(_sk_overlay_hsw)
+_sk_overlay_hsw:
+  .byte  196,98,125,24,5,157,65,0,0          // vbroadcastss  0x419d(%rip),%ymm8        # 49f4 <_sk_callback_hsw+0x19c>
+  .byte  197,60,92,215                       // vsubps        %ymm7,%ymm8,%ymm10
+  .byte  197,44,89,216                       // vmulps        %ymm0,%ymm10,%ymm11
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,98,61,184,220                   // vfmadd231ps   %ymm4,%ymm8,%ymm11
+  .byte  197,92,88,204                       // vaddps        %ymm4,%ymm4,%ymm9
+  .byte  197,52,194,231,2                    // vcmpleps      %ymm7,%ymm9,%ymm12
+  .byte  197,124,89,204                      // vmulps        %ymm4,%ymm0,%ymm9
+  .byte  196,65,52,88,233                    // vaddps        %ymm9,%ymm9,%ymm13
+  .byte  197,100,89,207                      // vmulps        %ymm7,%ymm3,%ymm9
+  .byte  197,68,92,244                       // vsubps        %ymm4,%ymm7,%ymm14
+  .byte  197,228,92,192                      // vsubps        %ymm0,%ymm3,%ymm0
+  .byte  196,193,124,89,198                  // vmulps        %ymm14,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,180,92,192                      // vsubps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,197,192              // vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  .byte  197,164,88,192                      // vaddps        %ymm0,%ymm11,%ymm0
+  .byte  197,44,89,217                       // vmulps        %ymm1,%ymm10,%ymm11
+  .byte  196,98,61,184,221                   // vfmadd231ps   %ymm5,%ymm8,%ymm11
+  .byte  197,84,88,229                       // vaddps        %ymm5,%ymm5,%ymm12
+  .byte  197,28,194,231,2                    // vcmpleps      %ymm7,%ymm12,%ymm12
+  .byte  197,116,89,237                      // vmulps        %ymm5,%ymm1,%ymm13
+  .byte  196,65,20,88,237                    // vaddps        %ymm13,%ymm13,%ymm13
+  .byte  197,68,92,245                       // vsubps        %ymm5,%ymm7,%ymm14
+  .byte  197,228,92,201                      // vsubps        %ymm1,%ymm3,%ymm1
+  .byte  196,193,116,89,206                  // vmulps        %ymm14,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,180,92,201                      // vsubps        %ymm1,%ymm9,%ymm1
+  .byte  196,195,117,74,205,192              // vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  196,98,61,184,214                   // vfmadd231ps   %ymm6,%ymm8,%ymm10
+  .byte  197,76,88,222                       // vaddps        %ymm6,%ymm6,%ymm11
+  .byte  197,36,194,223,2                    // vcmpleps      %ymm7,%ymm11,%ymm11
+  .byte  197,108,89,230                      // vmulps        %ymm6,%ymm2,%ymm12
+  .byte  196,65,28,88,228                    // vaddps        %ymm12,%ymm12,%ymm12
+  .byte  197,68,92,238                       // vsubps        %ymm6,%ymm7,%ymm13
+  .byte  197,228,92,210                      // vsubps        %ymm2,%ymm3,%ymm2
+  .byte  196,193,108,89,213                  // vmulps        %ymm13,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,180,92,210                      // vsubps        %ymm2,%ymm9,%ymm2
+  .byte  196,195,109,74,212,176              // vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_softlight_hsw
+.globl _sk_softlight_hsw
+FUNCTION(_sk_softlight_hsw)
+_sk_softlight_hsw:
+  .byte  197,252,17,84,36,200                // vmovups       %ymm2,-0x38(%rsp)
+  .byte  197,252,40,209                      // vmovaps       %ymm1,%ymm2
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  196,65,52,87,201                    // vxorps        %ymm9,%ymm9,%ymm9
+  .byte  197,52,194,215,1                    // vcmpltps      %ymm7,%ymm9,%ymm10
+  .byte  197,92,94,199                       // vdivps        %ymm7,%ymm4,%ymm8
+  .byte  196,67,53,74,232,160                // vblendvps     %ymm10,%ymm8,%ymm9,%ymm13
+  .byte  196,65,20,88,197                    // vaddps        %ymm13,%ymm13,%ymm8
+  .byte  196,65,60,88,192                    // vaddps        %ymm8,%ymm8,%ymm8
+  .byte  196,66,61,168,192                   // vfmadd213ps   %ymm8,%ymm8,%ymm8
+  .byte  196,98,125,24,29,168,64,0,0         // vbroadcastss  0x40a8(%rip),%ymm11        # 49fc <_sk_callback_hsw+0x1a4>
+  .byte  196,65,20,88,227                    // vaddps        %ymm11,%ymm13,%ymm12
+  .byte  196,65,28,89,192                    // vmulps        %ymm8,%ymm12,%ymm8
+  .byte  196,98,125,24,37,153,64,0,0         // vbroadcastss  0x4099(%rip),%ymm12        # 4a00 <_sk_callback_hsw+0x1a8>
+  .byte  196,66,21,184,196                   // vfmadd231ps   %ymm12,%ymm13,%ymm8
+  .byte  196,65,124,82,245                   // vrsqrtps      %ymm13,%ymm14
+  .byte  196,65,124,83,246                   // vrcpps        %ymm14,%ymm14
+  .byte  196,65,12,92,245                    // vsubps        %ymm13,%ymm14,%ymm14
+  .byte  197,92,88,252                       // vaddps        %ymm4,%ymm4,%ymm15
+  .byte  196,65,4,88,255                     // vaddps        %ymm15,%ymm15,%ymm15
+  .byte  197,4,194,255,2                     // vcmpleps      %ymm7,%ymm15,%ymm15
+  .byte  196,67,13,74,240,240                // vblendvps     %ymm15,%ymm8,%ymm14,%ymm14
+  .byte  197,116,88,249                      // vaddps        %ymm1,%ymm1,%ymm15
+  .byte  196,98,125,24,5,92,64,0,0           // vbroadcastss  0x405c(%rip),%ymm8        # 49f8 <_sk_callback_hsw+0x1a0>
+  .byte  196,65,60,92,237                    // vsubps        %ymm13,%ymm8,%ymm13
+  .byte  197,132,92,195                      // vsubps        %ymm3,%ymm15,%ymm0
+  .byte  196,98,125,168,235                  // vfmadd213ps   %ymm3,%ymm0,%ymm13
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  196,193,124,89,198                  // vmulps        %ymm14,%ymm0,%ymm0
+  .byte  197,20,89,236                       // vmulps        %ymm4,%ymm13,%ymm13
+  .byte  196,226,101,184,196                 // vfmadd231ps   %ymm4,%ymm3,%ymm0
+  .byte  197,4,194,243,2                     // vcmpleps      %ymm3,%ymm15,%ymm14
+  .byte  196,195,125,74,197,224              // vblendvps     %ymm14,%ymm13,%ymm0,%ymm0
+  .byte  197,252,17,68,36,168                // vmovups       %ymm0,-0x58(%rsp)
+  .byte  197,212,94,199                      // vdivps        %ymm7,%ymm5,%ymm0
+  .byte  196,227,53,74,192,160               // vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  .byte  197,124,88,240                      // vaddps        %ymm0,%ymm0,%ymm14
+  .byte  196,65,12,88,246                    // vaddps        %ymm14,%ymm14,%ymm14
+  .byte  196,66,13,168,246                   // vfmadd213ps   %ymm14,%ymm14,%ymm14
+  .byte  196,65,124,88,251                   // vaddps        %ymm11,%ymm0,%ymm15
+  .byte  196,65,4,89,246                     // vmulps        %ymm14,%ymm15,%ymm14
+  .byte  196,66,125,184,244                  // vfmadd231ps   %ymm12,%ymm0,%ymm14
+  .byte  197,124,82,248                      // vrsqrtps      %ymm0,%ymm15
+  .byte  196,65,124,83,255                   // vrcpps        %ymm15,%ymm15
+  .byte  197,4,92,248                        // vsubps        %ymm0,%ymm15,%ymm15
+  .byte  197,84,88,237                       // vaddps        %ymm5,%ymm5,%ymm13
+  .byte  196,65,20,88,237                    // vaddps        %ymm13,%ymm13,%ymm13
+  .byte  197,20,194,239,2                    // vcmpleps      %ymm7,%ymm13,%ymm13
+  .byte  196,67,5,74,238,208                 // vblendvps     %ymm13,%ymm14,%ymm15,%ymm13
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,108,88,242                      // vaddps        %ymm2,%ymm2,%ymm14
+  .byte  197,12,92,251                       // vsubps        %ymm3,%ymm14,%ymm15
+  .byte  196,226,5,168,195                   // vfmadd213ps   %ymm3,%ymm15,%ymm0
+  .byte  197,4,89,255                        // vmulps        %ymm7,%ymm15,%ymm15
+  .byte  196,65,4,89,237                     // vmulps        %ymm13,%ymm15,%ymm13
+  .byte  197,252,89,197                      // vmulps        %ymm5,%ymm0,%ymm0
+  .byte  196,98,101,184,237                  // vfmadd231ps   %ymm5,%ymm3,%ymm13
+  .byte  197,12,194,243,2                    // vcmpleps      %ymm3,%ymm14,%ymm14
+  .byte  196,99,21,74,240,224                // vblendvps     %ymm14,%ymm0,%ymm13,%ymm14
+  .byte  197,204,94,199                      // vdivps        %ymm7,%ymm6,%ymm0
+  .byte  196,227,53,74,192,160               // vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  .byte  197,124,88,200                      // vaddps        %ymm0,%ymm0,%ymm9
+  .byte  196,65,52,88,201                    // vaddps        %ymm9,%ymm9,%ymm9
+  .byte  196,66,53,168,201                   // vfmadd213ps   %ymm9,%ymm9,%ymm9
+  .byte  196,65,124,88,211                   // vaddps        %ymm11,%ymm0,%ymm10
+  .byte  196,65,44,89,201                    // vmulps        %ymm9,%ymm10,%ymm9
+  .byte  196,66,125,184,204                  // vfmadd231ps   %ymm12,%ymm0,%ymm9
+  .byte  197,124,82,208                      // vrsqrtps      %ymm0,%ymm10
+  .byte  196,65,124,83,210                   // vrcpps        %ymm10,%ymm10
+  .byte  197,44,92,208                       // vsubps        %ymm0,%ymm10,%ymm10
+  .byte  197,76,88,222                       // vaddps        %ymm6,%ymm6,%ymm11
+  .byte  196,65,36,88,219                    // vaddps        %ymm11,%ymm11,%ymm11
+  .byte  197,36,194,223,2                    // vcmpleps      %ymm7,%ymm11,%ymm11
+  .byte  196,67,45,74,201,176                // vblendvps     %ymm11,%ymm9,%ymm10,%ymm9
+  .byte  197,124,16,100,36,200               // vmovups       -0x38(%rsp),%ymm12
+  .byte  196,65,28,88,212                    // vaddps        %ymm12,%ymm12,%ymm10
+  .byte  197,44,92,219                       // vsubps        %ymm3,%ymm10,%ymm11
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  196,226,37,168,195                  // vfmadd213ps   %ymm3,%ymm11,%ymm0
+  .byte  197,36,89,223                       // vmulps        %ymm7,%ymm11,%ymm11
+  .byte  196,65,36,89,201                    // vmulps        %ymm9,%ymm11,%ymm9
+  .byte  197,252,89,198                      // vmulps        %ymm6,%ymm0,%ymm0
+  .byte  196,98,101,184,206                  // vfmadd231ps   %ymm6,%ymm3,%ymm9
+  .byte  197,44,194,211,2                    // vcmpleps      %ymm3,%ymm10,%ymm10
+  .byte  196,99,53,74,200,160                // vblendvps     %ymm10,%ymm0,%ymm9,%ymm9
+  .byte  197,60,92,215                       // vsubps        %ymm7,%ymm8,%ymm10
+  .byte  197,172,89,193                      // vmulps        %ymm1,%ymm10,%ymm0
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,226,61,184,196                  // vfmadd231ps   %ymm4,%ymm8,%ymm0
+  .byte  197,252,88,68,36,168                // vaddps        -0x58(%rsp),%ymm0,%ymm0
+  .byte  197,172,89,202                      // vmulps        %ymm2,%ymm10,%ymm1
+  .byte  196,226,61,184,205                  // vfmadd231ps   %ymm5,%ymm8,%ymm1
+  .byte  196,193,116,88,206                  // vaddps        %ymm14,%ymm1,%ymm1
+  .byte  196,193,44,89,212                   // vmulps        %ymm12,%ymm10,%ymm2
+  .byte  196,226,61,184,214                  // vfmadd231ps   %ymm6,%ymm8,%ymm2
+  .byte  196,193,108,88,209                  // vaddps        %ymm9,%ymm2,%ymm2
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hue_hsw
+.globl _sk_hue_hsw
+FUNCTION(_sk_hue_hsw)
+_sk_hue_hsw:
+  .byte  197,124,40,194                      // vmovaps       %ymm2,%ymm8
+  .byte  197,124,17,68,36,200                // vmovups       %ymm8,-0x38(%rsp)
+  .byte  197,252,17,76,36,168                // vmovups       %ymm1,-0x58(%rsp)
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,108,89,203                      // vmulps        %ymm3,%ymm2,%ymm9
+  .byte  197,116,89,211                      // vmulps        %ymm3,%ymm1,%ymm10
+  .byte  197,60,89,219                       // vmulps        %ymm3,%ymm8,%ymm11
+  .byte  197,84,95,198                       // vmaxps        %ymm6,%ymm5,%ymm8
+  .byte  196,65,92,95,192                    // vmaxps        %ymm8,%ymm4,%ymm8
+  .byte  197,84,93,230                       // vminps        %ymm6,%ymm5,%ymm12
+  .byte  196,65,92,93,228                    // vminps        %ymm12,%ymm4,%ymm12
+  .byte  196,65,60,92,196                    // vsubps        %ymm12,%ymm8,%ymm8
+  .byte  197,60,89,227                       // vmulps        %ymm3,%ymm8,%ymm12
+  .byte  196,65,44,93,195                    // vminps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,93,232                    // vminps        %ymm8,%ymm9,%ymm13
+  .byte  196,65,44,95,195                    // vmaxps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,95,192                    // vmaxps        %ymm8,%ymm9,%ymm8
+  .byte  196,65,60,92,245                    // vsubps        %ymm13,%ymm8,%ymm14
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,12,194,248,0                 // vcmpeqps      %ymm8,%ymm14,%ymm15
+  .byte  196,65,52,92,205                    // vsubps        %ymm13,%ymm9,%ymm9
+  .byte  196,65,28,89,201                    // vmulps        %ymm9,%ymm12,%ymm9
+  .byte  196,65,52,94,206                    // vdivps        %ymm14,%ymm9,%ymm9
+  .byte  196,67,53,74,200,240                // vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  .byte  196,65,44,92,213                    // vsubps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,28,89,210                    // vmulps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,44,94,214                    // vdivps        %ymm14,%ymm10,%ymm10
+  .byte  196,195,45,74,200,240               // vblendvps     %ymm15,%ymm8,%ymm10,%ymm1
+  .byte  196,65,36,92,213                    // vsubps        %ymm13,%ymm11,%ymm10
+  .byte  196,65,28,89,210                    // vmulps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,44,94,214                    // vdivps        %ymm14,%ymm10,%ymm10
+  .byte  196,67,45,74,224,240                // vblendvps     %ymm15,%ymm8,%ymm10,%ymm12
+  .byte  196,98,125,24,53,96,62,0,0          // vbroadcastss  0x3e60(%rip),%ymm14        # 4a04 <_sk_callback_hsw+0x1ac>
+  .byte  196,98,125,24,61,91,62,0,0          // vbroadcastss  0x3e5b(%rip),%ymm15        # 4a08 <_sk_callback_hsw+0x1b0>
+  .byte  196,65,84,89,239                    // vmulps        %ymm15,%ymm5,%ymm13
+  .byte  196,66,93,184,238                   // vfmadd231ps   %ymm14,%ymm4,%ymm13
+  .byte  196,226,125,24,5,76,62,0,0          // vbroadcastss  0x3e4c(%rip),%ymm0        # 4a0c <_sk_callback_hsw+0x1b4>
+  .byte  196,98,77,184,232                   // vfmadd231ps   %ymm0,%ymm6,%ymm13
+  .byte  196,65,116,89,215                   // vmulps        %ymm15,%ymm1,%ymm10
+  .byte  196,66,53,184,214                   // vfmadd231ps   %ymm14,%ymm9,%ymm10
+  .byte  196,98,29,184,208                   // vfmadd231ps   %ymm0,%ymm12,%ymm10
+  .byte  196,66,101,170,234                  // vfmsub213ps   %ymm10,%ymm3,%ymm13
+  .byte  196,65,52,88,213                    // vaddps        %ymm13,%ymm9,%ymm10
+  .byte  196,65,116,88,221                   // vaddps        %ymm13,%ymm1,%ymm11
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  196,193,36,93,204                   // vminps        %ymm12,%ymm11,%ymm1
+  .byte  197,44,93,233                       // vminps        %ymm1,%ymm10,%ymm13
+  .byte  196,65,36,89,207                    // vmulps        %ymm15,%ymm11,%ymm9
+  .byte  196,66,45,184,206                   // vfmadd231ps   %ymm14,%ymm10,%ymm9
+  .byte  196,98,29,184,200                   // vfmadd231ps   %ymm0,%ymm12,%ymm9
+  .byte  196,193,44,92,193                   // vsubps        %ymm9,%ymm10,%ymm0
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  196,193,52,92,205                   // vsubps        %ymm13,%ymm9,%ymm1
+  .byte  197,252,94,193                      // vdivps        %ymm1,%ymm0,%ymm0
+  .byte  196,65,36,92,241                    // vsubps        %ymm9,%ymm11,%ymm14
+  .byte  196,65,52,89,246                    // vmulps        %ymm14,%ymm9,%ymm14
+  .byte  197,12,94,241                       // vdivps        %ymm1,%ymm14,%ymm14
+  .byte  196,65,28,92,249                    // vsubps        %ymm9,%ymm12,%ymm15
+  .byte  196,65,52,89,255                    // vmulps        %ymm15,%ymm9,%ymm15
+  .byte  197,132,94,201                      // vdivps        %ymm1,%ymm15,%ymm1
+  .byte  196,65,60,194,237,2                 // vcmpleps      %ymm13,%ymm8,%ymm13
+  .byte  196,65,52,88,246                    // vaddps        %ymm14,%ymm9,%ymm14
+  .byte  196,67,13,74,243,208                // vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  .byte  196,65,36,95,220                    // vmaxps        %ymm12,%ymm11,%ymm11
+  .byte  197,180,88,201                      // vaddps        %ymm1,%ymm9,%ymm1
+  .byte  196,195,117,74,204,208              // vblendvps     %ymm13,%ymm12,%ymm1,%ymm1
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,194,208              // vblendvps     %ymm13,%ymm10,%ymm0,%ymm0
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,44,95,211                    // vmaxps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,124,92,217                   // vsubps        %ymm9,%ymm0,%ymm11
+  .byte  196,65,28,92,233                    // vsubps        %ymm9,%ymm12,%ymm13
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,28,194,250,1                 // vcmpltps      %ymm10,%ymm12,%ymm15
+  .byte  196,65,44,92,209                    // vsubps        %ymm9,%ymm10,%ymm10
+  .byte  196,65,36,94,218                    // vdivps        %ymm10,%ymm11,%ymm11
+  .byte  196,65,52,88,219                    // vaddps        %ymm11,%ymm9,%ymm11
+  .byte  196,195,125,74,195,240              // vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  .byte  196,65,12,92,217                    // vsubps        %ymm9,%ymm14,%ymm11
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,36,94,218                    // vdivps        %ymm10,%ymm11,%ymm11
+  .byte  196,65,52,88,219                    // vaddps        %ymm11,%ymm9,%ymm11
+  .byte  196,67,13,74,219,240                // vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  .byte  196,65,116,92,241                   // vsubps        %ymm9,%ymm1,%ymm14
+  .byte  196,65,20,89,238                    // vmulps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,20,94,210                    // vdivps        %ymm10,%ymm13,%ymm10
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,195,117,74,201,240              // vblendvps     %ymm15,%ymm9,%ymm1,%ymm1
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,65,36,95,200                    // vmaxps        %ymm8,%ymm11,%ymm9
+  .byte  196,65,116,95,192                   // vmaxps        %ymm8,%ymm1,%ymm8
+  .byte  196,226,125,24,13,57,61,0,0         // vbroadcastss  0x3d39(%rip),%ymm1        # 4a10 <_sk_callback_hsw+0x1b8>
+  .byte  197,116,92,215                      // vsubps        %ymm7,%ymm1,%ymm10
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  197,116,92,219                      // vsubps        %ymm3,%ymm1,%ymm11
+  .byte  196,226,37,184,212                  // vfmadd231ps   %ymm4,%ymm11,%ymm2
+  .byte  197,236,88,192                      // vaddps        %ymm0,%ymm2,%ymm0
+  .byte  197,172,89,76,36,168                // vmulps        -0x58(%rsp),%ymm10,%ymm1
+  .byte  196,226,37,184,205                  // vfmadd231ps   %ymm5,%ymm11,%ymm1
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  197,172,89,84,36,200                // vmulps        -0x38(%rsp),%ymm10,%ymm2
+  .byte  196,98,77,168,218                   // vfmadd213ps   %ymm2,%ymm6,%ymm11
+  .byte  196,193,36,88,208                   // vaddps        %ymm8,%ymm11,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_saturation_hsw
+.globl _sk_saturation_hsw
+FUNCTION(_sk_saturation_hsw)
+_sk_saturation_hsw:
+  .byte  197,124,40,194                      // vmovaps       %ymm2,%ymm8
+  .byte  197,252,17,76,36,168                // vmovups       %ymm1,-0x58(%rsp)
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  197,100,89,213                      // vmulps        %ymm5,%ymm3,%ymm10
+  .byte  197,100,89,222                      // vmulps        %ymm6,%ymm3,%ymm11
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,252,17,68,36,200                // vmovups       %ymm0,-0x38(%rsp)
+  .byte  197,116,95,192                      // vmaxps        %ymm0,%ymm1,%ymm8
+  .byte  196,65,108,95,192                   // vmaxps        %ymm8,%ymm2,%ymm8
+  .byte  197,116,93,224                      // vminps        %ymm0,%ymm1,%ymm12
+  .byte  196,65,108,93,228                   // vminps        %ymm12,%ymm2,%ymm12
+  .byte  196,65,60,92,196                    // vsubps        %ymm12,%ymm8,%ymm8
+  .byte  197,60,89,231                       // vmulps        %ymm7,%ymm8,%ymm12
+  .byte  196,65,44,93,195                    // vminps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,93,232                    // vminps        %ymm8,%ymm9,%ymm13
+  .byte  196,65,44,95,195                    // vmaxps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,95,192                    // vmaxps        %ymm8,%ymm9,%ymm8
+  .byte  196,65,60,92,245                    // vsubps        %ymm13,%ymm8,%ymm14
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,12,194,248,0                 // vcmpeqps      %ymm8,%ymm14,%ymm15
+  .byte  196,65,52,92,205                    // vsubps        %ymm13,%ymm9,%ymm9
+  .byte  196,65,28,89,201                    // vmulps        %ymm9,%ymm12,%ymm9
+  .byte  196,65,52,94,206                    // vdivps        %ymm14,%ymm9,%ymm9
+  .byte  196,67,53,74,200,240                // vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  .byte  196,65,44,92,213                    // vsubps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,28,89,210                    // vmulps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,44,94,214                    // vdivps        %ymm14,%ymm10,%ymm10
+  .byte  196,195,45,74,200,240               // vblendvps     %ymm15,%ymm8,%ymm10,%ymm1
+  .byte  196,65,36,92,213                    // vsubps        %ymm13,%ymm11,%ymm10
+  .byte  196,65,28,89,210                    // vmulps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,44,94,214                    // vdivps        %ymm14,%ymm10,%ymm10
+  .byte  196,67,45,74,224,240                // vblendvps     %ymm15,%ymm8,%ymm10,%ymm12
+  .byte  196,98,125,24,53,80,60,0,0          // vbroadcastss  0x3c50(%rip),%ymm14        # 4a14 <_sk_callback_hsw+0x1bc>
+  .byte  196,98,125,24,61,75,60,0,0          // vbroadcastss  0x3c4b(%rip),%ymm15        # 4a18 <_sk_callback_hsw+0x1c0>
+  .byte  196,65,84,89,239                    // vmulps        %ymm15,%ymm5,%ymm13
+  .byte  196,66,93,184,238                   // vfmadd231ps   %ymm14,%ymm4,%ymm13
+  .byte  196,226,125,24,5,60,60,0,0          // vbroadcastss  0x3c3c(%rip),%ymm0        # 4a1c <_sk_callback_hsw+0x1c4>
+  .byte  196,98,77,184,232                   // vfmadd231ps   %ymm0,%ymm6,%ymm13
+  .byte  196,65,116,89,215                   // vmulps        %ymm15,%ymm1,%ymm10
+  .byte  196,66,53,184,214                   // vfmadd231ps   %ymm14,%ymm9,%ymm10
+  .byte  196,98,29,184,208                   // vfmadd231ps   %ymm0,%ymm12,%ymm10
+  .byte  196,66,101,170,234                  // vfmsub213ps   %ymm10,%ymm3,%ymm13
+  .byte  196,65,52,88,213                    // vaddps        %ymm13,%ymm9,%ymm10
+  .byte  196,65,116,88,221                   // vaddps        %ymm13,%ymm1,%ymm11
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  196,193,36,93,204                   // vminps        %ymm12,%ymm11,%ymm1
+  .byte  197,44,93,233                       // vminps        %ymm1,%ymm10,%ymm13
+  .byte  196,65,36,89,207                    // vmulps        %ymm15,%ymm11,%ymm9
+  .byte  196,66,45,184,206                   // vfmadd231ps   %ymm14,%ymm10,%ymm9
+  .byte  196,98,29,184,200                   // vfmadd231ps   %ymm0,%ymm12,%ymm9
+  .byte  196,193,44,92,193                   // vsubps        %ymm9,%ymm10,%ymm0
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  196,193,52,92,205                   // vsubps        %ymm13,%ymm9,%ymm1
+  .byte  197,252,94,193                      // vdivps        %ymm1,%ymm0,%ymm0
+  .byte  196,65,36,92,241                    // vsubps        %ymm9,%ymm11,%ymm14
+  .byte  196,65,52,89,246                    // vmulps        %ymm14,%ymm9,%ymm14
+  .byte  197,12,94,241                       // vdivps        %ymm1,%ymm14,%ymm14
+  .byte  196,65,28,92,249                    // vsubps        %ymm9,%ymm12,%ymm15
+  .byte  196,65,52,89,255                    // vmulps        %ymm15,%ymm9,%ymm15
+  .byte  197,132,94,201                      // vdivps        %ymm1,%ymm15,%ymm1
+  .byte  196,65,60,194,237,2                 // vcmpleps      %ymm13,%ymm8,%ymm13
+  .byte  196,65,52,88,246                    // vaddps        %ymm14,%ymm9,%ymm14
+  .byte  196,67,13,74,243,208                // vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  .byte  196,65,36,95,220                    // vmaxps        %ymm12,%ymm11,%ymm11
+  .byte  197,180,88,201                      // vaddps        %ymm1,%ymm9,%ymm1
+  .byte  196,195,117,74,204,208              // vblendvps     %ymm13,%ymm12,%ymm1,%ymm1
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,194,208              // vblendvps     %ymm13,%ymm10,%ymm0,%ymm0
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,44,95,211                    // vmaxps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,124,92,217                   // vsubps        %ymm9,%ymm0,%ymm11
+  .byte  196,65,28,92,233                    // vsubps        %ymm9,%ymm12,%ymm13
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,28,194,250,1                 // vcmpltps      %ymm10,%ymm12,%ymm15
+  .byte  196,65,44,92,209                    // vsubps        %ymm9,%ymm10,%ymm10
+  .byte  196,65,36,94,218                    // vdivps        %ymm10,%ymm11,%ymm11
+  .byte  196,65,52,88,219                    // vaddps        %ymm11,%ymm9,%ymm11
+  .byte  196,195,125,74,195,240              // vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  .byte  196,65,12,92,217                    // vsubps        %ymm9,%ymm14,%ymm11
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,36,94,218                    // vdivps        %ymm10,%ymm11,%ymm11
+  .byte  196,65,52,88,219                    // vaddps        %ymm11,%ymm9,%ymm11
+  .byte  196,67,13,74,219,240                // vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  .byte  196,65,116,92,241                   // vsubps        %ymm9,%ymm1,%ymm14
+  .byte  196,65,20,89,238                    // vmulps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,20,94,210                    // vdivps        %ymm10,%ymm13,%ymm10
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,195,117,74,201,240              // vblendvps     %ymm15,%ymm9,%ymm1,%ymm1
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,65,36,95,200                    // vmaxps        %ymm8,%ymm11,%ymm9
+  .byte  196,65,116,95,192                   // vmaxps        %ymm8,%ymm1,%ymm8
+  .byte  196,226,125,24,13,41,59,0,0         // vbroadcastss  0x3b29(%rip),%ymm1        # 4a20 <_sk_callback_hsw+0x1c8>
+  .byte  197,116,92,215                      // vsubps        %ymm7,%ymm1,%ymm10
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  197,116,92,219                      // vsubps        %ymm3,%ymm1,%ymm11
+  .byte  196,226,37,184,212                  // vfmadd231ps   %ymm4,%ymm11,%ymm2
+  .byte  197,236,88,192                      // vaddps        %ymm0,%ymm2,%ymm0
+  .byte  197,172,89,76,36,168                // vmulps        -0x58(%rsp),%ymm10,%ymm1
+  .byte  196,226,37,184,205                  // vfmadd231ps   %ymm5,%ymm11,%ymm1
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  197,172,89,84,36,200                // vmulps        -0x38(%rsp),%ymm10,%ymm2
+  .byte  196,98,77,168,218                   // vfmadd213ps   %ymm2,%ymm6,%ymm11
+  .byte  196,193,36,88,208                   // vaddps        %ymm8,%ymm11,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_color_hsw
+.globl _sk_color_hsw
+FUNCTION(_sk_color_hsw)
+_sk_color_hsw:
+  .byte  197,124,40,202                      // vmovaps       %ymm2,%ymm9
+  .byte  197,124,17,76,36,200                // vmovups       %ymm9,-0x38(%rsp)
+  .byte  197,252,17,76,36,168                // vmovups       %ymm1,-0x58(%rsp)
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,108,89,199                      // vmulps        %ymm7,%ymm2,%ymm8
+  .byte  197,116,89,215                      // vmulps        %ymm7,%ymm1,%ymm10
+  .byte  197,52,89,223                       // vmulps        %ymm7,%ymm9,%ymm11
+  .byte  196,98,125,24,45,194,58,0,0         // vbroadcastss  0x3ac2(%rip),%ymm13        # 4a24 <_sk_callback_hsw+0x1cc>
+  .byte  196,98,125,24,53,189,58,0,0         // vbroadcastss  0x3abd(%rip),%ymm14        # 4a28 <_sk_callback_hsw+0x1d0>
+  .byte  196,65,84,89,230                    // vmulps        %ymm14,%ymm5,%ymm12
+  .byte  196,66,93,184,229                   // vfmadd231ps   %ymm13,%ymm4,%ymm12
+  .byte  196,98,125,24,61,174,58,0,0         // vbroadcastss  0x3aae(%rip),%ymm15        # 4a2c <_sk_callback_hsw+0x1d4>
+  .byte  196,66,77,184,231                   // vfmadd231ps   %ymm15,%ymm6,%ymm12
+  .byte  196,65,44,89,206                    // vmulps        %ymm14,%ymm10,%ymm9
+  .byte  196,66,61,184,205                   // vfmadd231ps   %ymm13,%ymm8,%ymm9
+  .byte  196,66,37,184,207                   // vfmadd231ps   %ymm15,%ymm11,%ymm9
+  .byte  196,66,101,170,225                  // vfmsub213ps   %ymm9,%ymm3,%ymm12
+  .byte  196,65,60,88,204                    // vaddps        %ymm12,%ymm8,%ymm9
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,65,44,93,195                    // vminps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,93,224                    // vminps        %ymm8,%ymm9,%ymm12
+  .byte  196,65,44,89,198                    // vmulps        %ymm14,%ymm10,%ymm8
+  .byte  196,66,53,184,197                   // vfmadd231ps   %ymm13,%ymm9,%ymm8
+  .byte  196,66,37,184,199                   // vfmadd231ps   %ymm15,%ymm11,%ymm8
+  .byte  196,65,52,92,232                    // vsubps        %ymm8,%ymm9,%ymm13
+  .byte  196,65,60,89,237                    // vmulps        %ymm13,%ymm8,%ymm13
+  .byte  196,65,60,92,244                    // vsubps        %ymm12,%ymm8,%ymm14
+  .byte  196,193,20,94,198                   // vdivps        %ymm14,%ymm13,%ymm0
+  .byte  196,65,44,92,248                    // vsubps        %ymm8,%ymm10,%ymm15
+  .byte  196,65,60,89,255                    // vmulps        %ymm15,%ymm8,%ymm15
+  .byte  196,65,4,94,254                     // vdivps        %ymm14,%ymm15,%ymm15
+  .byte  196,65,36,92,232                    // vsubps        %ymm8,%ymm11,%ymm13
+  .byte  196,65,60,89,237                    // vmulps        %ymm13,%ymm8,%ymm13
+  .byte  196,65,20,94,238                    // vdivps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,12,87,246                    // vxorps        %ymm14,%ymm14,%ymm14
+  .byte  196,65,12,194,228,2                 // vcmpleps      %ymm12,%ymm14,%ymm12
+  .byte  196,65,60,88,255                    // vaddps        %ymm15,%ymm8,%ymm15
+  .byte  196,67,5,74,250,192                 // vblendvps     %ymm12,%ymm10,%ymm15,%ymm15
+  .byte  196,65,44,95,211                    // vmaxps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,60,88,237                    // vaddps        %ymm13,%ymm8,%ymm13
+  .byte  196,67,21,74,219,192                // vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  .byte  197,188,88,192                      // vaddps        %ymm0,%ymm8,%ymm0
+  .byte  196,195,125,74,201,192              // vblendvps     %ymm12,%ymm9,%ymm0,%ymm1
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,52,95,202                    // vmaxps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,116,92,208                   // vsubps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,28,92,232                    // vsubps        %ymm8,%ymm12,%ymm13
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,193,28,194,193,1                // vcmpltps      %ymm9,%ymm12,%ymm0
+  .byte  196,65,52,92,200                    // vsubps        %ymm8,%ymm9,%ymm9
+  .byte  196,65,44,94,209                    // vdivps        %ymm9,%ymm10,%ymm10
+  .byte  196,65,60,88,210                    // vaddps        %ymm10,%ymm8,%ymm10
+  .byte  196,195,117,74,202,0                // vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  .byte  196,65,4,92,208                     // vsubps        %ymm8,%ymm15,%ymm10
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,65,44,94,209                    // vdivps        %ymm9,%ymm10,%ymm10
+  .byte  196,65,60,88,210                    // vaddps        %ymm10,%ymm8,%ymm10
+  .byte  196,67,5,74,210,0                   // vblendvps     %ymm0,%ymm10,%ymm15,%ymm10
+  .byte  196,65,36,92,248                    // vsubps        %ymm8,%ymm11,%ymm15
+  .byte  196,65,20,89,239                    // vmulps        %ymm15,%ymm13,%ymm13
+  .byte  196,65,20,94,201                    // vdivps        %ymm9,%ymm13,%ymm9
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,195,37,74,192,0                 // vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  .byte  196,193,116,95,206                  // vmaxps        %ymm14,%ymm1,%ymm1
+  .byte  196,65,44,95,198                    // vmaxps        %ymm14,%ymm10,%ymm8
+  .byte  196,65,124,95,206                   // vmaxps        %ymm14,%ymm0,%ymm9
+  .byte  196,226,125,24,5,144,57,0,0         // vbroadcastss  0x3990(%rip),%ymm0        # 4a30 <_sk_callback_hsw+0x1d8>
+  .byte  197,124,92,215                      // vsubps        %ymm7,%ymm0,%ymm10
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  197,124,92,219                      // vsubps        %ymm3,%ymm0,%ymm11
+  .byte  196,226,37,184,212                  // vfmadd231ps   %ymm4,%ymm11,%ymm2
+  .byte  197,236,88,193                      // vaddps        %ymm1,%ymm2,%ymm0
+  .byte  197,172,89,76,36,168                // vmulps        -0x58(%rsp),%ymm10,%ymm1
+  .byte  196,226,37,184,205                  // vfmadd231ps   %ymm5,%ymm11,%ymm1
+  .byte  196,193,116,88,200                  // vaddps        %ymm8,%ymm1,%ymm1
+  .byte  197,172,89,84,36,200                // vmulps        -0x38(%rsp),%ymm10,%ymm2
+  .byte  196,98,77,168,218                   // vfmadd213ps   %ymm2,%ymm6,%ymm11
+  .byte  196,193,36,88,209                   // vaddps        %ymm9,%ymm11,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminosity_hsw
+.globl _sk_luminosity_hsw
+FUNCTION(_sk_luminosity_hsw)
+_sk_luminosity_hsw:
+  .byte  197,124,40,202                      // vmovaps       %ymm2,%ymm9
+  .byte  197,124,17,76,36,168                // vmovups       %ymm9,-0x58(%rsp)
+  .byte  197,252,17,76,36,200                // vmovups       %ymm1,-0x38(%rsp)
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,100,89,196                      // vmulps        %ymm4,%ymm3,%ymm8
+  .byte  197,100,89,213                      // vmulps        %ymm5,%ymm3,%ymm10
+  .byte  197,100,89,222                      // vmulps        %ymm6,%ymm3,%ymm11
+  .byte  196,98,125,24,45,41,57,0,0          // vbroadcastss  0x3929(%rip),%ymm13        # 4a34 <_sk_callback_hsw+0x1dc>
+  .byte  196,98,125,24,53,36,57,0,0          // vbroadcastss  0x3924(%rip),%ymm14        # 4a38 <_sk_callback_hsw+0x1e0>
+  .byte  196,65,116,89,230                   // vmulps        %ymm14,%ymm1,%ymm12
+  .byte  196,66,109,184,229                  // vfmadd231ps   %ymm13,%ymm2,%ymm12
+  .byte  196,98,125,24,61,21,57,0,0          // vbroadcastss  0x3915(%rip),%ymm15        # 4a3c <_sk_callback_hsw+0x1e4>
+  .byte  196,66,53,184,231                   // vfmadd231ps   %ymm15,%ymm9,%ymm12
+  .byte  196,65,44,89,206                    // vmulps        %ymm14,%ymm10,%ymm9
+  .byte  196,66,61,184,205                   // vfmadd231ps   %ymm13,%ymm8,%ymm9
+  .byte  196,66,37,184,207                   // vfmadd231ps   %ymm15,%ymm11,%ymm9
+  .byte  196,66,69,170,225                   // vfmsub213ps   %ymm9,%ymm7,%ymm12
+  .byte  196,65,60,88,204                    // vaddps        %ymm12,%ymm8,%ymm9
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,65,44,93,195                    // vminps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,93,224                    // vminps        %ymm8,%ymm9,%ymm12
+  .byte  196,65,44,89,198                    // vmulps        %ymm14,%ymm10,%ymm8
+  .byte  196,66,53,184,197                   // vfmadd231ps   %ymm13,%ymm9,%ymm8
+  .byte  196,66,37,184,199                   // vfmadd231ps   %ymm15,%ymm11,%ymm8
+  .byte  196,65,52,92,232                    // vsubps        %ymm8,%ymm9,%ymm13
+  .byte  196,65,60,89,237                    // vmulps        %ymm13,%ymm8,%ymm13
+  .byte  196,65,60,92,244                    // vsubps        %ymm12,%ymm8,%ymm14
+  .byte  196,193,20,94,198                   // vdivps        %ymm14,%ymm13,%ymm0
+  .byte  196,65,44,92,248                    // vsubps        %ymm8,%ymm10,%ymm15
+  .byte  196,65,60,89,255                    // vmulps        %ymm15,%ymm8,%ymm15
+  .byte  196,65,4,94,254                     // vdivps        %ymm14,%ymm15,%ymm15
+  .byte  196,65,36,92,232                    // vsubps        %ymm8,%ymm11,%ymm13
+  .byte  196,65,60,89,237                    // vmulps        %ymm13,%ymm8,%ymm13
+  .byte  196,65,20,94,238                    // vdivps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,12,87,246                    // vxorps        %ymm14,%ymm14,%ymm14
+  .byte  196,65,12,194,228,2                 // vcmpleps      %ymm12,%ymm14,%ymm12
+  .byte  196,65,60,88,255                    // vaddps        %ymm15,%ymm8,%ymm15
+  .byte  196,67,5,74,250,192                 // vblendvps     %ymm12,%ymm10,%ymm15,%ymm15
+  .byte  196,65,44,95,211                    // vmaxps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,60,88,237                    // vaddps        %ymm13,%ymm8,%ymm13
+  .byte  196,67,21,74,219,192                // vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  .byte  197,188,88,192                      // vaddps        %ymm0,%ymm8,%ymm0
+  .byte  196,195,125,74,201,192              // vblendvps     %ymm12,%ymm9,%ymm0,%ymm1
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,52,95,202                    // vmaxps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,116,92,208                   // vsubps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,28,92,232                    // vsubps        %ymm8,%ymm12,%ymm13
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,193,28,194,193,1                // vcmpltps      %ymm9,%ymm12,%ymm0
+  .byte  196,65,52,92,200                    // vsubps        %ymm8,%ymm9,%ymm9
+  .byte  196,65,44,94,209                    // vdivps        %ymm9,%ymm10,%ymm10
+  .byte  196,65,60,88,210                    // vaddps        %ymm10,%ymm8,%ymm10
+  .byte  196,195,117,74,202,0                // vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  .byte  196,65,4,92,208                     // vsubps        %ymm8,%ymm15,%ymm10
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,65,44,94,209                    // vdivps        %ymm9,%ymm10,%ymm10
+  .byte  196,65,60,88,210                    // vaddps        %ymm10,%ymm8,%ymm10
+  .byte  196,67,5,74,210,0                   // vblendvps     %ymm0,%ymm10,%ymm15,%ymm10
+  .byte  196,65,36,92,248                    // vsubps        %ymm8,%ymm11,%ymm15
+  .byte  196,65,20,89,239                    // vmulps        %ymm15,%ymm13,%ymm13
+  .byte  196,65,20,94,201                    // vdivps        %ymm9,%ymm13,%ymm9
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,195,37,74,192,0                 // vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  .byte  196,193,116,95,206                  // vmaxps        %ymm14,%ymm1,%ymm1
+  .byte  196,65,44,95,198                    // vmaxps        %ymm14,%ymm10,%ymm8
+  .byte  196,65,124,95,206                   // vmaxps        %ymm14,%ymm0,%ymm9
+  .byte  196,226,125,24,5,247,55,0,0         // vbroadcastss  0x37f7(%rip),%ymm0        # 4a40 <_sk_callback_hsw+0x1e8>
+  .byte  197,124,92,215                      // vsubps        %ymm7,%ymm0,%ymm10
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  197,124,92,219                      // vsubps        %ymm3,%ymm0,%ymm11
+  .byte  196,226,37,184,212                  // vfmadd231ps   %ymm4,%ymm11,%ymm2
+  .byte  197,236,88,193                      // vaddps        %ymm1,%ymm2,%ymm0
+  .byte  197,172,89,76,36,200                // vmulps        -0x38(%rsp),%ymm10,%ymm1
+  .byte  196,226,37,184,205                  // vfmadd231ps   %ymm5,%ymm11,%ymm1
+  .byte  196,193,116,88,200                  // vaddps        %ymm8,%ymm1,%ymm1
+  .byte  197,172,89,84,36,168                // vmulps        -0x58(%rsp),%ymm10,%ymm2
+  .byte  196,98,77,168,218                   // vfmadd213ps   %ymm2,%ymm6,%ymm11
+  .byte  196,193,36,88,209                   // vaddps        %ymm9,%ymm11,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_rgba_8888_hsw
+.globl _sk_srcover_rgba_8888_hsw
+FUNCTION(_sk_srcover_rgba_8888_hsw)
+_sk_srcover_rgba_8888_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,180,0,0,0                    // jne           1358 <_sk_srcover_rgba_8888_hsw+0xcd>
+  .byte  196,193,124,16,58                   // vmovups       (%r10),%ymm7
+  .byte  197,196,84,37,15,59,0,0             // vandps        0x3b0f(%rip),%ymm7,%ymm4        # 4dc0 <_sk_callback_hsw+0x568>
+  .byte  197,252,91,228                      // vcvtdq2ps     %ymm4,%ymm4
+  .byte  196,226,69,0,45,34,59,0,0           // vpshufb       0x3b22(%rip),%ymm7,%ymm5        # 4de0 <_sk_callback_hsw+0x588>
+  .byte  197,252,91,237                      // vcvtdq2ps     %ymm5,%ymm5
+  .byte  196,226,69,0,53,53,59,0,0           // vpshufb       0x3b35(%rip),%ymm7,%ymm6        # 4e00 <_sk_callback_hsw+0x5a8>
+  .byte  197,252,91,246                      // vcvtdq2ps     %ymm6,%ymm6
+  .byte  197,197,114,215,24                  // vpsrld        $0x18,%ymm7,%ymm7
+  .byte  197,252,91,255                      // vcvtdq2ps     %ymm7,%ymm7
+  .byte  196,98,125,24,5,99,55,0,0           // vbroadcastss  0x3763(%rip),%ymm8        # 4a44 <_sk_callback_hsw+0x1ec>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,98,125,24,13,90,55,0,0          // vbroadcastss  0x375a(%rip),%ymm9        # 4a48 <_sk_callback_hsw+0x1f0>
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  196,194,93,184,192                  // vfmadd231ps   %ymm8,%ymm4,%ymm0
+  .byte  196,193,116,89,201                  // vmulps        %ymm9,%ymm1,%ymm1
+  .byte  196,194,85,184,200                  // vfmadd231ps   %ymm8,%ymm5,%ymm1
+  .byte  196,193,108,89,209                  // vmulps        %ymm9,%ymm2,%ymm2
+  .byte  196,194,77,184,208                  // vfmadd231ps   %ymm8,%ymm6,%ymm2
+  .byte  196,193,100,89,217                  // vmulps        %ymm9,%ymm3,%ymm3
+  .byte  196,194,69,184,216                  // vfmadd231ps   %ymm8,%ymm7,%ymm3
+  .byte  197,125,91,192                      // vcvtps2dq     %ymm0,%ymm8
+  .byte  197,125,91,201                      // vcvtps2dq     %ymm1,%ymm9
+  .byte  196,193,53,114,241,8                // vpslld        $0x8,%ymm9,%ymm9
+  .byte  196,65,53,235,192                   // vpor          %ymm8,%ymm9,%ymm8
+  .byte  197,125,91,202                      // vcvtps2dq     %ymm2,%ymm9
+  .byte  196,193,53,114,241,16               // vpslld        $0x10,%ymm9,%ymm9
+  .byte  197,125,91,211                      // vcvtps2dq     %ymm3,%ymm10
+  .byte  196,193,45,114,242,24               // vpslld        $0x18,%ymm10,%ymm10
+  .byte  196,65,53,235,202                   // vpor          %ymm10,%ymm9,%ymm9
+  .byte  196,65,61,235,193                   // vpor          %ymm9,%ymm8,%ymm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,53                              // jne           1381 <_sk_srcover_rgba_8888_hsw+0xf6>
+  .byte  196,65,124,17,2                     // vmovups       %ymm8,(%r10)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,225,249,110,224                 // vmovq         %rax,%xmm4
+  .byte  196,226,125,33,228                  // vpmovsxbd     %xmm4,%ymm4
+  .byte  196,194,93,44,58                    // vmaskmovps    (%r10),%ymm4,%ymm7
+  .byte  233,40,255,255,255                  // jmpq          12a9 <_sk_srcover_rgba_8888_hsw+0x1e>
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,97,249,110,200                  // vmovq         %rax,%xmm9
+  .byte  196,66,125,33,201                   // vpmovsxbd     %xmm9,%ymm9
+  .byte  196,66,53,46,2                      // vmaskmovps    %ymm8,%ymm9,(%r10)
+  .byte  235,170                             // jmp           1351 <_sk_srcover_rgba_8888_hsw+0xc6>
+
+HIDDEN _sk_clamp_0_hsw
+.globl _sk_clamp_0_hsw
+FUNCTION(_sk_clamp_0_hsw)
+_sk_clamp_0_hsw:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,193,116,95,200                  // vmaxps        %ymm8,%ymm1,%ymm1
+  .byte  196,193,108,95,208                  // vmaxps        %ymm8,%ymm2,%ymm2
+  .byte  196,193,100,95,216                  // vmaxps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_1_hsw
+.globl _sk_clamp_1_hsw
+FUNCTION(_sk_clamp_1_hsw)
+_sk_clamp_1_hsw:
+  .byte  196,98,125,24,5,127,54,0,0          // vbroadcastss  0x367f(%rip),%ymm8        # 4a4c <_sk_callback_hsw+0x1f4>
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  196,193,116,93,200                  // vminps        %ymm8,%ymm1,%ymm1
+  .byte  196,193,108,93,208                  // vminps        %ymm8,%ymm2,%ymm2
+  .byte  196,193,100,93,216                  // vminps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_a_hsw
+.globl _sk_clamp_a_hsw
+FUNCTION(_sk_clamp_a_hsw)
+_sk_clamp_a_hsw:
+  .byte  196,98,125,24,5,98,54,0,0           // vbroadcastss  0x3662(%rip),%ymm8        # 4a50 <_sk_callback_hsw+0x1f8>
+  .byte  196,193,100,93,216                  // vminps        %ymm8,%ymm3,%ymm3
+  .byte  197,252,93,195                      // vminps        %ymm3,%ymm0,%ymm0
+  .byte  197,244,93,203                      // vminps        %ymm3,%ymm1,%ymm1
+  .byte  197,236,93,211                      // vminps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_set_rgb_hsw
+.globl _sk_set_rgb_hsw
+FUNCTION(_sk_set_rgb_hsw)
+_sk_set_rgb_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,0                    // vbroadcastss  (%rax),%ymm0
+  .byte  196,226,125,24,72,4                 // vbroadcastss  0x4(%rax),%ymm1
+  .byte  196,226,125,24,80,8                 // vbroadcastss  0x8(%rax),%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_rb_hsw
+.globl _sk_swap_rb_hsw
+FUNCTION(_sk_swap_rb_hsw)
+_sk_swap_rb_hsw:
+  .byte  197,124,40,192                      // vmovaps       %ymm0,%ymm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,194                      // vmovaps       %ymm2,%ymm0
+  .byte  197,124,41,194                      // vmovaps       %ymm8,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_hsw
+.globl _sk_swap_hsw
+FUNCTION(_sk_swap_hsw)
+_sk_swap_hsw:
+  .byte  197,124,40,195                      // vmovaps       %ymm3,%ymm8
+  .byte  197,124,40,202                      // vmovaps       %ymm2,%ymm9
+  .byte  197,124,40,209                      // vmovaps       %ymm1,%ymm10
+  .byte  197,124,40,216                      // vmovaps       %ymm0,%ymm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,196                      // vmovaps       %ymm4,%ymm0
+  .byte  197,252,40,205                      // vmovaps       %ymm5,%ymm1
+  .byte  197,252,40,214                      // vmovaps       %ymm6,%ymm2
+  .byte  197,252,40,223                      // vmovaps       %ymm7,%ymm3
+  .byte  197,124,41,220                      // vmovaps       %ymm11,%ymm4
+  .byte  197,124,41,213                      // vmovaps       %ymm10,%ymm5
+  .byte  197,124,41,206                      // vmovaps       %ymm9,%ymm6
+  .byte  197,124,41,199                      // vmovaps       %ymm8,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_src_dst_hsw
+.globl _sk_move_src_dst_hsw
+FUNCTION(_sk_move_src_dst_hsw)
+_sk_move_src_dst_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,224                      // vmovaps       %ymm0,%ymm4
+  .byte  197,252,40,233                      // vmovaps       %ymm1,%ymm5
+  .byte  197,252,40,242                      // vmovaps       %ymm2,%ymm6
+  .byte  197,252,40,251                      // vmovaps       %ymm3,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_dst_src_hsw
+.globl _sk_move_dst_src_hsw
+FUNCTION(_sk_move_dst_src_hsw)
+_sk_move_dst_src_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,196                      // vmovaps       %ymm4,%ymm0
+  .byte  197,252,40,205                      // vmovaps       %ymm5,%ymm1
+  .byte  197,252,40,214                      // vmovaps       %ymm6,%ymm2
+  .byte  197,252,40,223                      // vmovaps       %ymm7,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_premul_hsw
+.globl _sk_premul_hsw
+FUNCTION(_sk_premul_hsw)
+_sk_premul_hsw:
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  197,236,89,211                      // vmulps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_unpremul_hsw
+.globl _sk_unpremul_hsw
+FUNCTION(_sk_unpremul_hsw)
+_sk_unpremul_hsw:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,100,194,200,0                // vcmpeqps      %ymm8,%ymm3,%ymm9
+  .byte  196,98,125,24,21,170,53,0,0         // vbroadcastss  0x35aa(%rip),%ymm10        # 4a54 <_sk_callback_hsw+0x1fc>
+  .byte  197,44,94,211                       // vdivps        %ymm3,%ymm10,%ymm10
+  .byte  196,67,45,74,192,144                // vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_from_srgb_hsw
+.globl _sk_from_srgb_hsw
+FUNCTION(_sk_from_srgb_hsw)
+_sk_from_srgb_hsw:
+  .byte  196,98,125,24,5,139,53,0,0          // vbroadcastss  0x358b(%rip),%ymm8        # 4a58 <_sk_callback_hsw+0x200>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  197,124,89,208                      // vmulps        %ymm0,%ymm0,%ymm10
+  .byte  196,98,125,24,29,125,53,0,0         // vbroadcastss  0x357d(%rip),%ymm11        # 4a5c <_sk_callback_hsw+0x204>
+  .byte  196,98,125,24,37,120,53,0,0         // vbroadcastss  0x3578(%rip),%ymm12        # 4a60 <_sk_callback_hsw+0x208>
+  .byte  196,65,124,40,236                   // vmovaps       %ymm12,%ymm13
+  .byte  196,66,125,168,235                  // vfmadd213ps   %ymm11,%ymm0,%ymm13
+  .byte  196,98,125,24,53,105,53,0,0         // vbroadcastss  0x3569(%rip),%ymm14        # 4a64 <_sk_callback_hsw+0x20c>
+  .byte  196,66,45,168,238                   // vfmadd213ps   %ymm14,%ymm10,%ymm13
+  .byte  196,98,125,24,21,95,53,0,0          // vbroadcastss  0x355f(%rip),%ymm10        # 4a68 <_sk_callback_hsw+0x210>
+  .byte  196,193,124,194,194,1               // vcmpltps      %ymm10,%ymm0,%ymm0
+  .byte  196,195,21,74,193,0                 // vblendvps     %ymm0,%ymm9,%ymm13,%ymm0
+  .byte  196,65,116,89,200                   // vmulps        %ymm8,%ymm1,%ymm9
+  .byte  197,116,89,233                      // vmulps        %ymm1,%ymm1,%ymm13
+  .byte  196,65,124,40,252                   // vmovaps       %ymm12,%ymm15
+  .byte  196,66,117,168,251                  // vfmadd213ps   %ymm11,%ymm1,%ymm15
+  .byte  196,66,21,168,254                   // vfmadd213ps   %ymm14,%ymm13,%ymm15
+  .byte  196,193,116,194,202,1               // vcmpltps      %ymm10,%ymm1,%ymm1
+  .byte  196,195,5,74,201,16                 // vblendvps     %ymm1,%ymm9,%ymm15,%ymm1
+  .byte  196,65,108,89,192                   // vmulps        %ymm8,%ymm2,%ymm8
+  .byte  197,108,89,202                      // vmulps        %ymm2,%ymm2,%ymm9
+  .byte  196,66,109,168,227                  // vfmadd213ps   %ymm11,%ymm2,%ymm12
+  .byte  196,66,53,168,230                   // vfmadd213ps   %ymm14,%ymm9,%ymm12
+  .byte  196,193,108,194,210,1               // vcmpltps      %ymm10,%ymm2,%ymm2
+  .byte  196,195,29,74,208,32                // vblendvps     %ymm2,%ymm8,%ymm12,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_to_srgb_hsw
+.globl _sk_to_srgb_hsw
+FUNCTION(_sk_to_srgb_hsw)
+_sk_to_srgb_hsw:
+  .byte  197,124,82,200                      // vrsqrtps      %ymm0,%ymm9
+  .byte  196,98,125,24,5,3,53,0,0            // vbroadcastss  0x3503(%rip),%ymm8        # 4a6c <_sk_callback_hsw+0x214>
+  .byte  196,65,124,89,208                   // vmulps        %ymm8,%ymm0,%ymm10
+  .byte  196,98,125,24,29,249,52,0,0         // vbroadcastss  0x34f9(%rip),%ymm11        # 4a70 <_sk_callback_hsw+0x218>
+  .byte  196,98,125,24,37,244,52,0,0         // vbroadcastss  0x34f4(%rip),%ymm12        # 4a74 <_sk_callback_hsw+0x21c>
+  .byte  196,65,124,40,236                   // vmovaps       %ymm12,%ymm13
+  .byte  196,66,53,168,235                   // vfmadd213ps   %ymm11,%ymm9,%ymm13
+  .byte  196,98,125,24,53,229,52,0,0         // vbroadcastss  0x34e5(%rip),%ymm14        # 4a78 <_sk_callback_hsw+0x220>
+  .byte  196,66,53,168,238                   // vfmadd213ps   %ymm14,%ymm9,%ymm13
+  .byte  196,98,125,24,61,219,52,0,0         // vbroadcastss  0x34db(%rip),%ymm15        # 4a7c <_sk_callback_hsw+0x224>
+  .byte  196,65,52,88,207                    // vaddps        %ymm15,%ymm9,%ymm9
+  .byte  196,65,124,83,201                   // vrcpps        %ymm9,%ymm9
+  .byte  196,65,20,89,201                    // vmulps        %ymm9,%ymm13,%ymm9
+  .byte  196,98,125,24,45,199,52,0,0         // vbroadcastss  0x34c7(%rip),%ymm13        # 4a80 <_sk_callback_hsw+0x228>
+  .byte  196,193,124,194,197,1               // vcmpltps      %ymm13,%ymm0,%ymm0
+  .byte  196,195,53,74,194,0                 // vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  .byte  197,124,82,201                      // vrsqrtps      %ymm1,%ymm9
+  .byte  196,65,124,40,212                   // vmovaps       %ymm12,%ymm10
+  .byte  196,66,53,168,211                   // vfmadd213ps   %ymm11,%ymm9,%ymm10
+  .byte  196,66,53,168,214                   // vfmadd213ps   %ymm14,%ymm9,%ymm10
+  .byte  196,65,52,88,207                    // vaddps        %ymm15,%ymm9,%ymm9
+  .byte  196,65,124,83,201                   // vrcpps        %ymm9,%ymm9
+  .byte  196,65,44,89,201                    // vmulps        %ymm9,%ymm10,%ymm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,193,116,194,205,1               // vcmpltps      %ymm13,%ymm1,%ymm1
+  .byte  196,195,53,74,202,16                // vblendvps     %ymm1,%ymm10,%ymm9,%ymm1
+  .byte  197,124,82,202                      // vrsqrtps      %ymm2,%ymm9
+  .byte  196,66,53,168,227                   // vfmadd213ps   %ymm11,%ymm9,%ymm12
+  .byte  196,66,53,168,230                   // vfmadd213ps   %ymm14,%ymm9,%ymm12
+  .byte  196,65,52,88,207                    // vaddps        %ymm15,%ymm9,%ymm9
+  .byte  196,65,124,83,201                   // vrcpps        %ymm9,%ymm9
+  .byte  196,65,28,89,201                    // vmulps        %ymm9,%ymm12,%ymm9
+  .byte  196,65,108,89,192                   // vmulps        %ymm8,%ymm2,%ymm8
+  .byte  196,193,108,194,213,1               // vcmpltps      %ymm13,%ymm2,%ymm2
+  .byte  196,195,53,74,208,32                // vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_rgb_to_hsl_hsw
+.globl _sk_rgb_to_hsl_hsw
+FUNCTION(_sk_rgb_to_hsl_hsw)
+_sk_rgb_to_hsl_hsw:
+  .byte  197,124,95,193                      // vmaxps        %ymm1,%ymm0,%ymm8
+  .byte  197,60,95,194                       // vmaxps        %ymm2,%ymm8,%ymm8
+  .byte  197,124,93,201                      // vminps        %ymm1,%ymm0,%ymm9
+  .byte  197,52,93,202                       // vminps        %ymm2,%ymm9,%ymm9
+  .byte  196,65,60,92,209                    // vsubps        %ymm9,%ymm8,%ymm10
+  .byte  196,98,125,24,29,60,52,0,0          // vbroadcastss  0x343c(%rip),%ymm11        # 4a84 <_sk_callback_hsw+0x22c>
+  .byte  196,65,36,94,218                    // vdivps        %ymm10,%ymm11,%ymm11
+  .byte  197,116,92,226                      // vsubps        %ymm2,%ymm1,%ymm12
+  .byte  197,116,194,234,1                   // vcmpltps      %ymm2,%ymm1,%ymm13
+  .byte  196,98,125,24,53,41,52,0,0          // vbroadcastss  0x3429(%rip),%ymm14        # 4a88 <_sk_callback_hsw+0x230>
+  .byte  196,65,4,87,255                     // vxorps        %ymm15,%ymm15,%ymm15
+  .byte  196,67,5,74,238,208                 // vblendvps     %ymm13,%ymm14,%ymm15,%ymm13
+  .byte  196,66,37,168,229                   // vfmadd213ps   %ymm13,%ymm11,%ymm12
+  .byte  197,236,92,208                      // vsubps        %ymm0,%ymm2,%ymm2
+  .byte  197,124,92,233                      // vsubps        %ymm1,%ymm0,%ymm13
+  .byte  196,98,125,24,53,16,52,0,0          // vbroadcastss  0x3410(%rip),%ymm14        # 4a90 <_sk_callback_hsw+0x238>
+  .byte  196,66,37,168,238                   // vfmadd213ps   %ymm14,%ymm11,%ymm13
+  .byte  196,98,125,24,53,254,51,0,0         // vbroadcastss  0x33fe(%rip),%ymm14        # 4a8c <_sk_callback_hsw+0x234>
+  .byte  196,194,37,168,214                  // vfmadd213ps   %ymm14,%ymm11,%ymm2
+  .byte  197,188,194,201,0                   // vcmpeqps      %ymm1,%ymm8,%ymm1
+  .byte  196,227,21,74,202,16                // vblendvps     %ymm1,%ymm2,%ymm13,%ymm1
+  .byte  197,188,194,192,0                   // vcmpeqps      %ymm0,%ymm8,%ymm0
+  .byte  196,195,117,74,196,0                // vblendvps     %ymm0,%ymm12,%ymm1,%ymm0
+  .byte  196,193,60,88,201                   // vaddps        %ymm9,%ymm8,%ymm1
+  .byte  196,98,125,24,29,225,51,0,0         // vbroadcastss  0x33e1(%rip),%ymm11        # 4a98 <_sk_callback_hsw+0x240>
+  .byte  196,193,116,89,211                  // vmulps        %ymm11,%ymm1,%ymm2
+  .byte  197,36,194,218,1                    // vcmpltps      %ymm2,%ymm11,%ymm11
+  .byte  196,65,12,92,224                    // vsubps        %ymm8,%ymm14,%ymm12
+  .byte  196,65,28,92,225                    // vsubps        %ymm9,%ymm12,%ymm12
+  .byte  196,195,117,74,204,176              // vblendvps     %ymm11,%ymm12,%ymm1,%ymm1
+  .byte  196,65,60,194,193,0                 // vcmpeqps      %ymm9,%ymm8,%ymm8
+  .byte  197,172,94,201                      // vdivps        %ymm1,%ymm10,%ymm1
+  .byte  196,195,125,74,199,128              // vblendvps     %ymm8,%ymm15,%ymm0,%ymm0
+  .byte  196,195,117,74,207,128              // vblendvps     %ymm8,%ymm15,%ymm1,%ymm1
+  .byte  196,98,125,24,5,164,51,0,0          // vbroadcastss  0x33a4(%rip),%ymm8        # 4a94 <_sk_callback_hsw+0x23c>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hsl_to_rgb_hsw
+.globl _sk_hsl_to_rgb_hsw
+FUNCTION(_sk_hsl_to_rgb_hsw)
+_sk_hsl_to_rgb_hsw:
+  .byte  72,131,236,56                       // sub           $0x38,%rsp
+  .byte  197,252,17,60,36                    // vmovups       %ymm7,(%rsp)
+  .byte  197,252,17,116,36,224               // vmovups       %ymm6,-0x20(%rsp)
+  .byte  197,252,17,108,36,192               // vmovups       %ymm5,-0x40(%rsp)
+  .byte  197,252,17,100,36,160               // vmovups       %ymm4,-0x60(%rsp)
+  .byte  197,252,17,92,36,128                // vmovups       %ymm3,-0x80(%rsp)
+  .byte  197,252,40,233                      // vmovaps       %ymm1,%ymm5
+  .byte  197,252,40,224                      // vmovaps       %ymm0,%ymm4
+  .byte  196,98,125,24,5,113,51,0,0          // vbroadcastss  0x3371(%rip),%ymm8        # 4a9c <_sk_callback_hsw+0x244>
+  .byte  197,60,194,202,2                    // vcmpleps      %ymm2,%ymm8,%ymm9
+  .byte  197,84,89,210                       // vmulps        %ymm2,%ymm5,%ymm10
+  .byte  196,65,84,92,218                    // vsubps        %ymm10,%ymm5,%ymm11
+  .byte  196,67,45,74,203,144                // vblendvps     %ymm9,%ymm11,%ymm10,%ymm9
+  .byte  197,52,88,210                       // vaddps        %ymm2,%ymm9,%ymm10
+  .byte  196,98,125,24,13,84,51,0,0          // vbroadcastss  0x3354(%rip),%ymm9        # 4aa0 <_sk_callback_hsw+0x248>
+  .byte  196,66,109,170,202                  // vfmsub213ps   %ymm10,%ymm2,%ymm9
+  .byte  196,98,125,24,29,74,51,0,0          // vbroadcastss  0x334a(%rip),%ymm11        # 4aa4 <_sk_callback_hsw+0x24c>
+  .byte  196,65,92,88,219                    // vaddps        %ymm11,%ymm4,%ymm11
+  .byte  196,67,125,8,227,1                  // vroundps      $0x1,%ymm11,%ymm12
+  .byte  196,65,36,92,252                    // vsubps        %ymm12,%ymm11,%ymm15
+  .byte  196,65,44,92,217                    // vsubps        %ymm9,%ymm10,%ymm11
+  .byte  196,98,125,24,45,52,51,0,0          // vbroadcastss  0x3334(%rip),%ymm13        # 4aac <_sk_callback_hsw+0x254>
+  .byte  196,193,4,89,197                    // vmulps        %ymm13,%ymm15,%ymm0
+  .byte  196,98,125,24,53,42,51,0,0          // vbroadcastss  0x332a(%rip),%ymm14        # 4ab0 <_sk_callback_hsw+0x258>
+  .byte  197,12,92,224                       // vsubps        %ymm0,%ymm14,%ymm12
+  .byte  196,66,37,168,225                   // vfmadd213ps   %ymm9,%ymm11,%ymm12
+  .byte  196,226,125,24,29,16,51,0,0         // vbroadcastss  0x3310(%rip),%ymm3        # 4aa8 <_sk_callback_hsw+0x250>
+  .byte  196,193,100,194,255,2               // vcmpleps      %ymm15,%ymm3,%ymm7
+  .byte  196,195,29,74,249,112               // vblendvps     %ymm7,%ymm9,%ymm12,%ymm7
+  .byte  196,65,60,194,231,2                 // vcmpleps      %ymm15,%ymm8,%ymm12
+  .byte  196,227,45,74,255,192               // vblendvps     %ymm12,%ymm7,%ymm10,%ymm7
+  .byte  196,98,125,24,37,251,50,0,0         // vbroadcastss  0x32fb(%rip),%ymm12        # 4ab4 <_sk_callback_hsw+0x25c>
+  .byte  196,65,28,194,255,2                 // vcmpleps      %ymm15,%ymm12,%ymm15
+  .byte  196,194,37,168,193                  // vfmadd213ps   %ymm9,%ymm11,%ymm0
+  .byte  196,99,125,74,255,240               // vblendvps     %ymm15,%ymm7,%ymm0,%ymm15
+  .byte  196,227,125,8,196,1                 // vroundps      $0x1,%ymm4,%ymm0
+  .byte  197,220,92,192                      // vsubps        %ymm0,%ymm4,%ymm0
+  .byte  196,193,124,89,253                  // vmulps        %ymm13,%ymm0,%ymm7
+  .byte  197,140,92,207                      // vsubps        %ymm7,%ymm14,%ymm1
+  .byte  196,194,37,168,201                  // vfmadd213ps   %ymm9,%ymm11,%ymm1
+  .byte  197,228,194,240,2                   // vcmpleps      %ymm0,%ymm3,%ymm6
+  .byte  196,195,117,74,201,96               // vblendvps     %ymm6,%ymm9,%ymm1,%ymm1
+  .byte  197,188,194,240,2                   // vcmpleps      %ymm0,%ymm8,%ymm6
+  .byte  196,227,45,74,201,96                // vblendvps     %ymm6,%ymm1,%ymm10,%ymm1
+  .byte  197,156,194,192,2                   // vcmpleps      %ymm0,%ymm12,%ymm0
+  .byte  196,194,37,168,249                  // vfmadd213ps   %ymm9,%ymm11,%ymm7
+  .byte  196,227,69,74,201,0                 // vblendvps     %ymm0,%ymm1,%ymm7,%ymm1
+  .byte  196,226,125,24,5,167,50,0,0         // vbroadcastss  0x32a7(%rip),%ymm0        # 4ab8 <_sk_callback_hsw+0x260>
+  .byte  197,220,88,192                      // vaddps        %ymm0,%ymm4,%ymm0
+  .byte  196,227,125,8,224,1                 // vroundps      $0x1,%ymm0,%ymm4
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  197,228,194,216,2                   // vcmpleps      %ymm0,%ymm3,%ymm3
+  .byte  196,193,124,89,229                  // vmulps        %ymm13,%ymm0,%ymm4
+  .byte  197,140,92,244                      // vsubps        %ymm4,%ymm14,%ymm6
+  .byte  196,194,37,168,241                  // vfmadd213ps   %ymm9,%ymm11,%ymm6
+  .byte  196,195,77,74,217,48                // vblendvps     %ymm3,%ymm9,%ymm6,%ymm3
+  .byte  197,188,194,240,2                   // vcmpleps      %ymm0,%ymm8,%ymm6
+  .byte  196,227,45,74,219,96                // vblendvps     %ymm6,%ymm3,%ymm10,%ymm3
+  .byte  196,98,37,184,204                   // vfmadd231ps   %ymm4,%ymm11,%ymm9
+  .byte  197,156,194,192,2                   // vcmpleps      %ymm0,%ymm12,%ymm0
+  .byte  196,227,53,74,219,0                 // vblendvps     %ymm0,%ymm3,%ymm9,%ymm3
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,212,194,224,0                   // vcmpeqps      %ymm0,%ymm5,%ymm4
+  .byte  196,227,5,74,194,64                 // vblendvps     %ymm4,%ymm2,%ymm15,%ymm0
+  .byte  196,227,117,74,202,64               // vblendvps     %ymm4,%ymm2,%ymm1,%ymm1
+  .byte  196,227,101,74,210,64               // vblendvps     %ymm4,%ymm2,%ymm3,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,92,36,128                // vmovups       -0x80(%rsp),%ymm3
+  .byte  197,252,16,100,36,160               // vmovups       -0x60(%rsp),%ymm4
+  .byte  197,252,16,108,36,192               // vmovups       -0x40(%rsp),%ymm5
+  .byte  197,252,16,116,36,224               // vmovups       -0x20(%rsp),%ymm6
+  .byte  197,252,16,60,36                    // vmovups       (%rsp),%ymm7
+  .byte  72,131,196,56                       // add           $0x38,%rsp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_1_float_hsw
+.globl _sk_scale_1_float_hsw
+FUNCTION(_sk_scale_1_float_hsw)
+_sk_scale_1_float_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_u8_hsw
+.globl _sk_scale_u8_hsw
+FUNCTION(_sk_scale_u8_hsw)
+_sk_scale_u8_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,51                              // jne           18f1 <_sk_scale_u8_hsw+0x43>
+  .byte  197,122,126,0                       // vmovq         (%rax),%xmm8
+  .byte  196,66,125,49,192                   // vpmovzxbd     %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,13,231,49,0,0         // vbroadcastss  0x31e7(%rip),%ymm9        # 4abc <_sk_callback_hsw+0x264>
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           18fa <_sk_scale_u8_hsw+0x4c>
+  .byte  196,65,249,110,194                  // vmovq         %r10,%xmm8
+  .byte  91                                  // pop           %rbx
+  .byte  235,171                             // jmp           18c2 <_sk_scale_u8_hsw+0x14>
+
+HIDDEN _sk_lerp_1_float_hsw
+.globl _sk_lerp_1_float_hsw
+FUNCTION(_sk_lerp_1_float_hsw)
+_sk_lerp_1_float_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  196,226,61,168,196                  // vfmadd213ps   %ymm4,%ymm8,%ymm0
+  .byte  197,244,92,205                      // vsubps        %ymm5,%ymm1,%ymm1
+  .byte  196,226,61,168,205                  // vfmadd213ps   %ymm5,%ymm8,%ymm1
+  .byte  197,236,92,214                      // vsubps        %ymm6,%ymm2,%ymm2
+  .byte  196,226,61,168,214                  // vfmadd213ps   %ymm6,%ymm8,%ymm2
+  .byte  197,228,92,223                      // vsubps        %ymm7,%ymm3,%ymm3
+  .byte  196,226,61,168,223                  // vfmadd213ps   %ymm7,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lerp_u8_hsw
+.globl _sk_lerp_u8_hsw
+FUNCTION(_sk_lerp_u8_hsw)
+_sk_lerp_u8_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,71                              // jne           199d <_sk_lerp_u8_hsw+0x57>
+  .byte  197,122,126,0                       // vmovq         (%rax),%xmm8
+  .byte  196,66,125,49,192                   // vpmovzxbd     %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,13,83,49,0,0          // vbroadcastss  0x3153(%rip),%ymm9        # 4ac0 <_sk_callback_hsw+0x268>
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  196,226,61,168,196                  // vfmadd213ps   %ymm4,%ymm8,%ymm0
+  .byte  197,244,92,205                      // vsubps        %ymm5,%ymm1,%ymm1
+  .byte  196,226,61,168,205                  // vfmadd213ps   %ymm5,%ymm8,%ymm1
+  .byte  197,236,92,214                      // vsubps        %ymm6,%ymm2,%ymm2
+  .byte  196,226,61,168,214                  // vfmadd213ps   %ymm6,%ymm8,%ymm2
+  .byte  197,228,92,223                      // vsubps        %ymm7,%ymm3,%ymm3
+  .byte  196,226,61,168,223                  // vfmadd213ps   %ymm7,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           19a6 <_sk_lerp_u8_hsw+0x60>
+  .byte  196,65,249,110,194                  // vmovq         %r10,%xmm8
+  .byte  91                                  // pop           %rbx
+  .byte  235,151                             // jmp           195a <_sk_lerp_u8_hsw+0x14>
+
+HIDDEN _sk_lerp_565_hsw
+.globl _sk_lerp_565_hsw
+FUNCTION(_sk_lerp_565_hsw)
+_sk_lerp_565_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,169,0,0,0                    // jne           1a7a <_sk_lerp_565_hsw+0xb7>
+  .byte  196,65,122,111,4,83                 // vmovdqu       (%r11,%rdx,2),%xmm8
+  .byte  196,66,125,51,192                   // vpmovzxwd     %xmm8,%ymm8
+  .byte  196,98,125,88,13,223,48,0,0         // vpbroadcastd  0x30df(%rip),%ymm9        # 4ac4 <_sk_callback_hsw+0x26c>
+  .byte  196,65,61,219,201                   // vpand         %ymm9,%ymm8,%ymm9
+  .byte  196,65,124,91,201                   // vcvtdq2ps     %ymm9,%ymm9
+  .byte  196,98,125,24,21,208,48,0,0         // vbroadcastss  0x30d0(%rip),%ymm10        # 4ac8 <_sk_callback_hsw+0x270>
+  .byte  196,65,52,89,202                    // vmulps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,88,21,198,48,0,0         // vpbroadcastd  0x30c6(%rip),%ymm10        # 4acc <_sk_callback_hsw+0x274>
+  .byte  196,65,61,219,210                   // vpand         %ymm10,%ymm8,%ymm10
+  .byte  196,65,124,91,210                   // vcvtdq2ps     %ymm10,%ymm10
+  .byte  196,98,125,24,29,183,48,0,0         // vbroadcastss  0x30b7(%rip),%ymm11        # 4ad0 <_sk_callback_hsw+0x278>
+  .byte  196,65,44,89,211                    // vmulps        %ymm11,%ymm10,%ymm10
+  .byte  196,98,125,88,29,173,48,0,0         // vpbroadcastd  0x30ad(%rip),%ymm11        # 4ad4 <_sk_callback_hsw+0x27c>
+  .byte  196,65,61,219,195                   // vpand         %ymm11,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,29,158,48,0,0         // vbroadcastss  0x309e(%rip),%ymm11        # 4ad8 <_sk_callback_hsw+0x280>
+  .byte  196,65,60,89,195                    // vmulps        %ymm11,%ymm8,%ymm8
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  196,226,53,168,196                  // vfmadd213ps   %ymm4,%ymm9,%ymm0
+  .byte  197,244,92,205                      // vsubps        %ymm5,%ymm1,%ymm1
+  .byte  196,226,45,168,205                  // vfmadd213ps   %ymm5,%ymm10,%ymm1
+  .byte  197,236,92,214                      // vsubps        %ymm6,%ymm2,%ymm2
+  .byte  196,226,61,168,214                  // vfmadd213ps   %ymm6,%ymm8,%ymm2
+  .byte  197,228,92,223                      // vsubps        %ymm7,%ymm3,%ymm3
+  .byte  196,98,101,168,207                  // vfmadd213ps   %ymm7,%ymm3,%ymm9
+  .byte  196,98,101,168,215                  // vfmadd213ps   %ymm7,%ymm3,%ymm10
+  .byte  196,98,101,168,199                  // vfmadd213ps   %ymm7,%ymm3,%ymm8
+  .byte  196,193,44,95,216                   // vmaxps        %ymm8,%ymm10,%ymm3
+  .byte  197,180,95,219                      // vmaxps        %ymm3,%ymm9,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  196,65,57,239,192                   // vpxor         %xmm8,%xmm8,%xmm8
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,68,255,255,255               // ja            19d7 <_sk_lerp_565_hsw+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,74,0,0,0                  // lea           0x4a(%rip),%r10        # 1ae8 <_sk_lerp_565_hsw+0x125>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,57,239,192                   // vpxor         %xmm8,%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,12,6            // vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,10,5            // vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,8,4             // vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,6,3             // vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,4,2             // vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,2,1             // vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,4,83,0                // vpinsrw       $0x0,(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  233,239,254,255,255                 // jmpq          19d7 <_sk_lerp_565_hsw+0x14>
+  .byte  244                                 // hlt
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  236                                 // in            (%dx),%al
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,228                             // jmpq          *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  220,255                             // fdivr         %st,%st(7)
+  .byte  255                                 // (bad)
+  .byte  255,212                             // callq         *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,204                             // dec           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  191                                 // .byte         0xbf
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_tables_hsw
+.globl _sk_load_tables_hsw
+FUNCTION(_sk_load_tables_hsw)
+_sk_load_tables_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,105                             // jne           1b82 <_sk_load_tables_hsw+0x7e>
+  .byte  196,193,124,16,26                   // vmovups       (%r10),%ymm3
+  .byte  197,228,84,13,250,50,0,0            // vandps        0x32fa(%rip),%ymm3,%ymm1        # 4e20 <_sk_callback_hsw+0x5c8>
+  .byte  196,65,61,118,192                   // vpcmpeqd      %ymm8,%ymm8,%ymm8
+  .byte  72,139,72,8                         // mov           0x8(%rax),%rcx
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  197,237,118,210                     // vpcmpeqd      %ymm2,%ymm2,%ymm2
+  .byte  196,226,109,146,4,137               // vgatherdps    %ymm2,(%rcx,%ymm1,4),%ymm0
+  .byte  196,226,101,0,21,250,50,0,0         // vpshufb       0x32fa(%rip),%ymm3,%ymm2        # 4e40 <_sk_callback_hsw+0x5e8>
+  .byte  196,65,53,118,201                   // vpcmpeqd      %ymm9,%ymm9,%ymm9
+  .byte  196,194,53,146,12,146               // vgatherdps    %ymm9,(%r10,%ymm2,4),%ymm1
+  .byte  72,139,64,24                        // mov           0x18(%rax),%rax
+  .byte  196,98,101,0,13,2,51,0,0            // vpshufb       0x3302(%rip),%ymm3,%ymm9        # 4e60 <_sk_callback_hsw+0x608>
+  .byte  196,162,61,146,20,136               // vgatherdps    %ymm8,(%rax,%ymm9,4),%ymm2
+  .byte  197,229,114,211,24                  // vpsrld        $0x18,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,98,125,24,5,102,47,0,0          // vbroadcastss  0x2f66(%rip),%ymm8        # 4adc <_sk_callback_hsw+0x284>
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  73,199,195,255,255,255,255          // mov           $0xffffffffffffffff,%r11
+  .byte  73,211,235                          // shr           %cl,%r11
+  .byte  196,193,249,110,195                 // vmovq         %r11,%xmm0
+  .byte  196,226,125,33,192                  // vpmovsxbd     %xmm0,%ymm0
+  .byte  196,194,125,44,26                   // vmaskmovps    (%r10),%ymm0,%ymm3
+  .byte  233,115,255,255,255                 // jmpq          1b1e <_sk_load_tables_hsw+0x1a>
+
+HIDDEN _sk_load_tables_u16_be_hsw
+.globl _sk_load_tables_u16_be_hsw
+FUNCTION(_sk_load_tables_u16_be_hsw)
+_sk_load_tables_u16_be_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,201,0,0,0                    // jne           1c8a <_sk_load_tables_u16_be_hsw+0xdf>
+  .byte  196,1,121,16,4,81                   // vmovupd       (%r9,%r10,2),%xmm8
+  .byte  196,129,121,16,84,81,16             // vmovupd       0x10(%r9,%r10,2),%xmm2
+  .byte  196,129,121,16,92,81,32             // vmovupd       0x20(%r9,%r10,2),%xmm3
+  .byte  196,1,122,111,76,81,48              // vmovdqu       0x30(%r9,%r10,2),%xmm9
+  .byte  197,185,97,194                      // vpunpcklwd    %xmm2,%xmm8,%xmm0
+  .byte  197,185,105,210                     // vpunpckhwd    %xmm2,%xmm8,%xmm2
+  .byte  196,193,97,97,201                   // vpunpcklwd    %xmm9,%xmm3,%xmm1
+  .byte  196,193,97,105,217                  // vpunpckhwd    %xmm9,%xmm3,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,121,105,202                     // vpunpckhwd    %xmm2,%xmm0,%xmm9
+  .byte  197,241,97,195                      // vpunpcklwd    %xmm3,%xmm1,%xmm0
+  .byte  197,241,105,219                     // vpunpckhwd    %xmm3,%xmm1,%xmm3
+  .byte  197,185,108,200                     // vpunpcklqdq   %xmm0,%xmm8,%xmm1
+  .byte  197,185,109,208                     // vpunpckhqdq   %xmm0,%xmm8,%xmm2
+  .byte  197,49,108,195                      // vpunpcklqdq   %xmm3,%xmm9,%xmm8
+  .byte  197,121,111,21,142,51,0,0           // vmovdqa       0x338e(%rip),%xmm10        # 4fa0 <_sk_callback_hsw+0x748>
+  .byte  196,193,113,219,194                 // vpand         %xmm10,%xmm1,%xmm0
+  .byte  196,226,125,51,200                  // vpmovzxwd     %xmm0,%ymm1
+  .byte  196,65,37,118,219                   // vpcmpeqd      %ymm11,%ymm11,%ymm11
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  196,65,29,118,228                   // vpcmpeqd      %ymm12,%ymm12,%ymm12
+  .byte  196,194,29,146,4,137                // vgatherdps    %ymm12,(%r9,%ymm1,4),%ymm0
+  .byte  196,193,105,219,202                 // vpand         %xmm10,%xmm2,%xmm1
+  .byte  196,226,125,51,209                  // vpmovzxwd     %xmm1,%ymm2
+  .byte  196,65,29,118,228                   // vpcmpeqd      %ymm12,%ymm12,%ymm12
+  .byte  196,194,29,146,12,146               // vgatherdps    %ymm12,(%r10,%ymm2,4),%ymm1
+  .byte  72,139,64,24                        // mov           0x18(%rax),%rax
+  .byte  196,193,57,219,210                  // vpand         %xmm10,%xmm8,%xmm2
+  .byte  196,98,125,51,194                   // vpmovzxwd     %xmm2,%ymm8
+  .byte  196,162,37,146,20,128               // vgatherdps    %ymm11,(%rax,%ymm8,4),%ymm2
+  .byte  197,177,109,219                     // vpunpckhqdq   %xmm3,%xmm9,%xmm3
+  .byte  197,185,113,243,8                   // vpsllw        $0x8,%xmm3,%xmm8
+  .byte  197,225,113,211,8                   // vpsrlw        $0x8,%xmm3,%xmm3
+  .byte  197,185,235,219                     // vpor          %xmm3,%xmm8,%xmm3
+  .byte  196,226,125,51,219                  // vpmovzxwd     %xmm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,98,125,24,5,95,46,0,0           // vbroadcastss  0x2e5f(%rip),%ymm8        # 4ae0 <_sk_callback_hsw+0x288>
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,1,123,16,4,81                   // vmovsd        (%r9,%r10,2),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,85                              // je            1cf0 <_sk_load_tables_u16_be_hsw+0x145>
+  .byte  196,1,57,22,68,81,8                 // vmovhpd       0x8(%r9,%r10,2),%xmm8,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,72                              // jb            1cf0 <_sk_load_tables_u16_be_hsw+0x145>
+  .byte  196,129,123,16,84,81,16             // vmovsd        0x10(%r9,%r10,2),%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  116,72                              // je            1cfd <_sk_load_tables_u16_be_hsw+0x152>
+  .byte  196,129,105,22,84,81,24             // vmovhpd       0x18(%r9,%r10,2),%xmm2,%xmm2
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,59                              // jb            1cfd <_sk_load_tables_u16_be_hsw+0x152>
+  .byte  196,129,123,16,92,81,32             // vmovsd        0x20(%r9,%r10,2),%xmm3
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  15,132,9,255,255,255                // je            1bdc <_sk_load_tables_u16_be_hsw+0x31>
+  .byte  196,129,97,22,92,81,40              // vmovhpd       0x28(%r9,%r10,2),%xmm3,%xmm3
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  15,130,248,254,255,255              // jb            1bdc <_sk_load_tables_u16_be_hsw+0x31>
+  .byte  196,1,122,126,76,81,48              // vmovq         0x30(%r9,%r10,2),%xmm9
+  .byte  233,236,254,255,255                 // jmpq          1bdc <_sk_load_tables_u16_be_hsw+0x31>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  197,233,87,210                      // vxorpd        %xmm2,%xmm2,%xmm2
+  .byte  233,223,254,255,255                 // jmpq          1bdc <_sk_load_tables_u16_be_hsw+0x31>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  233,214,254,255,255                 // jmpq          1bdc <_sk_load_tables_u16_be_hsw+0x31>
+
+HIDDEN _sk_load_tables_rgb_u16_be_hsw
+.globl _sk_load_tables_rgb_u16_be_hsw
+FUNCTION(_sk_load_tables_rgb_u16_be_hsw)
+_sk_load_tables_rgb_u16_be_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,82                        // lea           (%rdx,%rdx,2),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,193,0,0,0                    // jne           1dd9 <_sk_load_tables_rgb_u16_be_hsw+0xd3>
+  .byte  196,129,122,111,4,81                // vmovdqu       (%r9,%r10,2),%xmm0
+  .byte  196,129,122,111,84,81,12            // vmovdqu       0xc(%r9,%r10,2),%xmm2
+  .byte  196,129,122,111,76,81,24            // vmovdqu       0x18(%r9,%r10,2),%xmm1
+  .byte  196,129,122,111,92,81,32            // vmovdqu       0x20(%r9,%r10,2),%xmm3
+  .byte  197,225,115,219,4                   // vpsrldq       $0x4,%xmm3,%xmm3
+  .byte  197,185,115,216,6                   // vpsrldq       $0x6,%xmm0,%xmm8
+  .byte  197,177,115,218,6                   // vpsrldq       $0x6,%xmm2,%xmm9
+  .byte  197,161,115,217,6                   // vpsrldq       $0x6,%xmm1,%xmm11
+  .byte  197,169,115,219,6                   // vpsrldq       $0x6,%xmm3,%xmm10
+  .byte  197,249,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm0
+  .byte  196,193,57,97,209                   // vpunpcklwd    %xmm9,%xmm8,%xmm2
+  .byte  197,241,97,203                      // vpunpcklwd    %xmm3,%xmm1,%xmm1
+  .byte  196,193,33,97,218                   // vpunpcklwd    %xmm10,%xmm11,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,249,105,194                     // vpunpckhwd    %xmm2,%xmm0,%xmm0
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,241,105,203                     // vpunpckhwd    %xmm3,%xmm1,%xmm1
+  .byte  197,185,108,218                     // vpunpcklqdq   %xmm2,%xmm8,%xmm3
+  .byte  197,185,109,210                     // vpunpckhqdq   %xmm2,%xmm8,%xmm2
+  .byte  197,121,108,193                     // vpunpcklqdq   %xmm1,%xmm0,%xmm8
+  .byte  197,121,111,13,46,50,0,0            // vmovdqa       0x322e(%rip),%xmm9        # 4fb0 <_sk_callback_hsw+0x758>
+  .byte  196,193,97,219,193                  // vpand         %xmm9,%xmm3,%xmm0
+  .byte  196,226,125,51,200                  // vpmovzxwd     %xmm0,%ymm1
+  .byte  197,229,118,219                     // vpcmpeqd      %ymm3,%ymm3,%ymm3
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  196,65,45,118,210                   // vpcmpeqd      %ymm10,%ymm10,%ymm10
+  .byte  196,194,45,146,4,137                // vgatherdps    %ymm10,(%r9,%ymm1,4),%ymm0
+  .byte  196,193,105,219,201                 // vpand         %xmm9,%xmm2,%xmm1
+  .byte  196,226,125,51,209                  // vpmovzxwd     %xmm1,%ymm2
+  .byte  196,65,45,118,210                   // vpcmpeqd      %ymm10,%ymm10,%ymm10
+  .byte  196,194,45,146,12,146               // vgatherdps    %ymm10,(%r10,%ymm2,4),%ymm1
+  .byte  72,139,64,24                        // mov           0x18(%rax),%rax
+  .byte  196,193,57,219,209                  // vpand         %xmm9,%xmm8,%xmm2
+  .byte  196,98,125,51,194                   // vpmovzxwd     %xmm2,%ymm8
+  .byte  196,162,101,146,20,128              // vgatherdps    %ymm3,(%rax,%ymm8,4),%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,13,45,0,0         // vbroadcastss  0x2d0d(%rip),%ymm3        # 4ae4 <_sk_callback_hsw+0x28c>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,129,121,110,4,81                // vmovd         (%r9,%r10,2),%xmm0
+  .byte  196,129,121,196,68,81,4,2           // vpinsrw       $0x2,0x4(%r9,%r10,2),%xmm0,%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,5                               // jne           1df2 <_sk_load_tables_rgb_u16_be_hsw+0xec>
+  .byte  233,90,255,255,255                  // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  .byte  196,129,121,110,76,81,6             // vmovd         0x6(%r9,%r10,2),%xmm1
+  .byte  196,1,113,196,68,81,10,2            // vpinsrw       $0x2,0xa(%r9,%r10,2),%xmm1,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,26                              // jb            1e21 <_sk_load_tables_rgb_u16_be_hsw+0x11b>
+  .byte  196,129,121,110,76,81,12            // vmovd         0xc(%r9,%r10,2),%xmm1
+  .byte  196,129,113,196,84,81,16,2          // vpinsrw       $0x2,0x10(%r9,%r10,2),%xmm1,%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  117,10                              // jne           1e26 <_sk_load_tables_rgb_u16_be_hsw+0x120>
+  .byte  233,43,255,255,255                  // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  .byte  233,38,255,255,255                  // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  .byte  196,129,121,110,76,81,18            // vmovd         0x12(%r9,%r10,2),%xmm1
+  .byte  196,1,113,196,76,81,22,2            // vpinsrw       $0x2,0x16(%r9,%r10,2),%xmm1,%xmm9
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,26                              // jb            1e55 <_sk_load_tables_rgb_u16_be_hsw+0x14f>
+  .byte  196,129,121,110,76,81,24            // vmovd         0x18(%r9,%r10,2),%xmm1
+  .byte  196,129,113,196,76,81,28,2          // vpinsrw       $0x2,0x1c(%r9,%r10,2),%xmm1,%xmm1
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  117,10                              // jne           1e5a <_sk_load_tables_rgb_u16_be_hsw+0x154>
+  .byte  233,247,254,255,255                 // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  .byte  233,242,254,255,255                 // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  .byte  196,129,121,110,92,81,30            // vmovd         0x1e(%r9,%r10,2),%xmm3
+  .byte  196,1,97,196,92,81,34,2             // vpinsrw       $0x2,0x22(%r9,%r10,2),%xmm3,%xmm11
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,20                              // jb            1e83 <_sk_load_tables_rgb_u16_be_hsw+0x17d>
+  .byte  196,129,121,110,92,81,36            // vmovd         0x24(%r9,%r10,2),%xmm3
+  .byte  196,129,97,196,92,81,40,2           // vpinsrw       $0x2,0x28(%r9,%r10,2),%xmm3,%xmm3
+  .byte  233,201,254,255,255                 // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  .byte  233,196,254,255,255                 // jmpq          1d4c <_sk_load_tables_rgb_u16_be_hsw+0x46>
+
+HIDDEN _sk_byte_tables_hsw
+.globl _sk_byte_tables_hsw
+FUNCTION(_sk_byte_tables_hsw)
+_sk_byte_tables_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,85,44,0,0           // vbroadcastss  0x2c55(%rip),%ymm8        # 4ae8 <_sk_callback_hsw+0x290>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  197,125,91,200                      // vcvtps2dq     %ymm0,%ymm9
+  .byte  196,65,249,126,201                  // vmovq         %xmm9,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,131,121,32,4,19,0               // vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  .byte  196,67,249,22,202,1                 // vpextrq       $0x1,%xmm9,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,121,32,20,11,1                // vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,99,125,57,200,1                 // vextracti128  $0x1,%ymm9,%xmm0
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,67,41,32,201,2                  // vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,49,32,202,3                  // vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,49,32,202,4                  // vpinsrb       $0x4,%r10d,%xmm9,%xmm9
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,195,49,32,193,5                 // vpinsrb       $0x5,%r9d,%xmm9,%xmm0
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,195,121,32,193,6                // vpinsrb       $0x6,%r9d,%xmm0,%xmm0
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,121,32,202,7                 // vpinsrb       $0x7,%r10d,%xmm0,%xmm9
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,4,25,0               // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,121,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,57,201,1                // vextracti128  $0x1,%ymm1,%xmm1
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,2                // vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,195,121,32,195,3                // vpinsrb       $0x3,%r11d,%xmm0,%xmm0
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,195,121,32,195,4                // vpinsrb       $0x4,%r11d,%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,5                // vpinsrb       $0x5,%r10d,%xmm0,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,6                // vpinsrb       $0x6,%r10d,%xmm0,%xmm0
+  .byte  71,15,182,12,25                     // movzbl        (%r9,%r11,1),%r9d
+  .byte  196,195,121,32,201,7                // vpinsrb       $0x7,%r9d,%xmm0,%xmm1
+  .byte  76,139,72,16                        // mov           0x10(%rax),%r9
+  .byte  196,193,108,89,192                  // vmulps        %ymm8,%ymm2,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,20,25,0              // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm2
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,20,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm2
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,105,32,210,2                // vpinsrb       $0x2,%r10d,%xmm2,%xmm2
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,195,105,32,211,3                // vpinsrb       $0x3,%r11d,%xmm2,%xmm2
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,195,105,32,211,4                // vpinsrb       $0x4,%r11d,%xmm2,%xmm2
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,105,32,194,5                // vpinsrb       $0x5,%r10d,%xmm2,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,6                // vpinsrb       $0x6,%r10d,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  71,15,182,12,25                     // movzbl        (%r9,%r11,1),%r9d
+  .byte  196,195,121,32,209,7                // vpinsrb       $0x7,%r9d,%xmm0,%xmm2
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  196,193,100,89,192                  // vmulps        %ymm8,%ymm3,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,195,121,32,28,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm3
+  .byte  196,227,249,22,192,1                // vpextrq       $0x1,%xmm0,%rax
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,97,32,28,10,1               // vpinsrb       $0x1,(%r10,%r9,1),%xmm3,%xmm3
+  .byte  65,137,193                          // mov           %eax,%r9d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,12,10                     // movzbl        (%r10,%r9,1),%r9d
+  .byte  196,195,97,32,217,2                 // vpinsrb       $0x2,%r9d,%xmm3,%xmm3
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,227,97,32,216,3                 // vpinsrb       $0x3,%eax,%xmm3,%xmm3
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,227,97,32,216,4                 // vpinsrb       $0x4,%eax,%xmm3,%xmm3
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  67,15,182,4,10                      // movzbl        (%r10,%r9,1),%eax
+  .byte  196,227,97,32,192,5                 // vpinsrb       $0x5,%eax,%xmm3,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,227,121,32,216,6                // vpinsrb       $0x6,%eax,%xmm0,%xmm3
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,26                      // movzbl        (%r10,%r11,1),%eax
+  .byte  196,194,125,49,193                  // vpmovzxbd     %xmm9,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,5,250,41,0,0          // vbroadcastss  0x29fa(%rip),%ymm8        # 4aec <_sk_callback_hsw+0x294>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,226,125,49,201                  // vpmovzxbd     %xmm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,226,125,49,210                  // vpmovzxbd     %xmm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  196,227,97,32,216,7                 // vpinsrb       $0x7,%eax,%xmm3,%xmm3
+  .byte  196,226,125,49,219                  // vpmovzxbd     %xmm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_byte_tables_rgb_hsw
+.globl _sk_byte_tables_rgb_hsw
+FUNCTION(_sk_byte_tables_rgb_hsw)
+_sk_byte_tables_rgb_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,139,72,24                        // mov           0x18(%rax),%r9d
+  .byte  65,255,201                          // dec           %r9d
+  .byte  196,65,121,110,193                  // vmovd         %r9d,%xmm8
+  .byte  196,66,125,88,192                   // vpbroadcastd  %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,125,91,200                      // vcvtps2dq     %ymm0,%ymm9
+  .byte  196,65,249,126,201                  // vmovq         %xmm9,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,131,121,32,4,19,0               // vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  .byte  196,67,249,22,202,1                 // vpextrq       $0x1,%xmm9,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,121,32,20,11,1                // vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,99,125,57,200,1                 // vextracti128  $0x1,%ymm9,%xmm0
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,67,41,32,201,2                  // vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,49,32,202,3                  // vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,49,32,202,4                  // vpinsrb       $0x4,%r10d,%xmm9,%xmm9
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,195,49,32,193,5                 // vpinsrb       $0x5,%r9d,%xmm9,%xmm0
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,195,121,32,193,6                // vpinsrb       $0x6,%r9d,%xmm0,%xmm0
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,121,32,202,7                 // vpinsrb       $0x7,%r10d,%xmm0,%xmm9
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,4,25,0               // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,121,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,57,201,1                // vextracti128  $0x1,%ymm1,%xmm1
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,2                // vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,195,121,32,195,3                // vpinsrb       $0x3,%r11d,%xmm0,%xmm0
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,195,121,32,195,4                // vpinsrb       $0x4,%r11d,%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,5                // vpinsrb       $0x5,%r10d,%xmm0,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,6                // vpinsrb       $0x6,%r10d,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  71,15,182,12,25                     // movzbl        (%r9,%r11,1),%r9d
+  .byte  196,195,121,32,201,7                // vpinsrb       $0x7,%r9d,%xmm0,%xmm1
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  197,188,89,194                      // vmulps        %ymm2,%ymm8,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,195,121,32,20,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm2
+  .byte  196,227,249,22,192,1                // vpextrq       $0x1,%xmm0,%rax
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,105,32,20,10,1              // vpinsrb       $0x1,(%r10,%r9,1),%xmm2,%xmm2
+  .byte  65,137,193                          // mov           %eax,%r9d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,12,10                     // movzbl        (%r10,%r9,1),%r9d
+  .byte  196,195,105,32,209,2                // vpinsrb       $0x2,%r9d,%xmm2,%xmm2
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,227,105,32,208,3                // vpinsrb       $0x3,%eax,%xmm2,%xmm2
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,227,105,32,208,4                // vpinsrb       $0x4,%eax,%xmm2,%xmm2
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  67,15,182,4,10                      // movzbl        (%r10,%r9,1),%eax
+  .byte  196,227,105,32,192,5                // vpinsrb       $0x5,%eax,%xmm2,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,227,121,32,208,6                // vpinsrb       $0x6,%eax,%xmm0,%xmm2
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,26                      // movzbl        (%r10,%r11,1),%eax
+  .byte  196,194,125,49,193                  // vpmovzxbd     %xmm9,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,5,230,39,0,0          // vbroadcastss  0x27e6(%rip),%ymm8        # 4af0 <_sk_callback_hsw+0x298>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,226,125,49,201                  // vpmovzxbd     %xmm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,227,105,32,208,7                // vpinsrb       $0x7,%eax,%xmm2,%xmm2
+  .byte  196,226,125,49,210                  // vpmovzxbd     %xmm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_r_hsw
+.globl _sk_table_r_hsw
+FUNCTION(_sk_table_r_hsw)
+_sk_table_r_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,66,125,88,192                   // vpbroadcastd  %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,125,91,192                      // vcvtps2dq     %ymm0,%ymm8
+  .byte  196,65,53,118,201                   // vpcmpeqd      %ymm9,%ymm9,%ymm9
+  .byte  196,130,53,146,4,129                // vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_g_hsw
+.globl _sk_table_g_hsw
+FUNCTION(_sk_table_g_hsw)
+_sk_table_g_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,66,125,88,192                   // vpbroadcastd  %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,125,91,193                      // vcvtps2dq     %ymm1,%ymm8
+  .byte  196,65,53,118,201                   // vpcmpeqd      %ymm9,%ymm9,%ymm9
+  .byte  196,130,53,146,12,129               // vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_b_hsw
+.globl _sk_table_b_hsw
+FUNCTION(_sk_table_b_hsw)
+_sk_table_b_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,66,125,88,192                   // vpbroadcastd  %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,125,91,194                      // vcvtps2dq     %ymm2,%ymm8
+  .byte  196,65,53,118,201                   // vpcmpeqd      %ymm9,%ymm9,%ymm9
+  .byte  196,130,53,146,20,129               // vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_a_hsw
+.globl _sk_table_a_hsw
+FUNCTION(_sk_table_a_hsw)
+_sk_table_a_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,66,125,88,192                   // vpbroadcastd  %xmm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  197,125,91,195                      // vcvtps2dq     %ymm3,%ymm8
+  .byte  196,65,53,118,201                   // vpcmpeqd      %ymm9,%ymm9,%ymm9
+  .byte  196,130,53,146,28,129               // vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_r_hsw
+.globl _sk_parametric_r_hsw
+FUNCTION(_sk_parametric_r_hsw)
+_sk_parametric_r_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,124,194,192,2                // vcmpleps      %ymm8,%ymm0,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,66,125,168,202                  // vfmadd213ps   %ymm10,%ymm0,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  196,66,125,168,211                  // vfmadd213ps   %ymm11,%ymm0,%ymm10
+  .byte  196,226,125,24,0                    // vbroadcastss  (%rax),%ymm0
+  .byte  196,65,124,91,218                   // vcvtdq2ps     %ymm10,%ymm11
+  .byte  196,98,125,24,37,192,38,0,0         // vbroadcastss  0x26c0(%rip),%ymm12        # 4af4 <_sk_callback_hsw+0x29c>
+  .byte  196,98,125,24,45,187,38,0,0         // vbroadcastss  0x26bb(%rip),%ymm13        # 4af8 <_sk_callback_hsw+0x2a0>
+  .byte  196,65,44,84,213                    // vandps        %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,177,38,0,0         // vbroadcastss  0x26b1(%rip),%ymm13        # 4afc <_sk_callback_hsw+0x2a4>
+  .byte  196,65,44,86,213                    // vorps         %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,167,38,0,0         // vbroadcastss  0x26a7(%rip),%ymm13        # 4b00 <_sk_callback_hsw+0x2a8>
+  .byte  196,66,37,184,236                   // vfmadd231ps   %ymm12,%ymm11,%ymm13
+  .byte  196,98,125,24,29,157,38,0,0         // vbroadcastss  0x269d(%rip),%ymm11        # 4b04 <_sk_callback_hsw+0x2ac>
+  .byte  196,66,45,172,221                   // vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  .byte  196,98,125,24,37,147,38,0,0         // vbroadcastss  0x2693(%rip),%ymm12        # 4b08 <_sk_callback_hsw+0x2b0>
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,98,125,24,37,137,38,0,0         // vbroadcastss  0x2689(%rip),%ymm12        # 4b0c <_sk_callback_hsw+0x2b4>
+  .byte  196,65,28,94,210                    // vdivps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,124,89,194                  // vmulps        %ymm10,%ymm0,%ymm0
+  .byte  196,99,125,8,208,1                  // vroundps      $0x1,%ymm0,%ymm10
+  .byte  196,65,124,92,210                   // vsubps        %ymm10,%ymm0,%ymm10
+  .byte  196,98,125,24,29,106,38,0,0         // vbroadcastss  0x266a(%rip),%ymm11        # 4b10 <_sk_callback_hsw+0x2b8>
+  .byte  196,193,124,88,195                  // vaddps        %ymm11,%ymm0,%ymm0
+  .byte  196,98,125,24,29,96,38,0,0          // vbroadcastss  0x2660(%rip),%ymm11        # 4b14 <_sk_callback_hsw+0x2bc>
+  .byte  196,98,45,172,216                   // vfnmadd213ps  %ymm0,%ymm10,%ymm11
+  .byte  196,226,125,24,5,86,38,0,0          // vbroadcastss  0x2656(%rip),%ymm0        # 4b18 <_sk_callback_hsw+0x2c0>
+  .byte  196,193,124,92,194                  // vsubps        %ymm10,%ymm0,%ymm0
+  .byte  196,98,125,24,21,76,38,0,0          // vbroadcastss  0x264c(%rip),%ymm10        # 4b1c <_sk_callback_hsw+0x2c4>
+  .byte  197,172,94,192                      // vdivps        %ymm0,%ymm10,%ymm0
+  .byte  197,164,88,192                      // vaddps        %ymm0,%ymm11,%ymm0
+  .byte  196,98,125,24,21,63,38,0,0          // vbroadcastss  0x263f(%rip),%ymm10        # 4b20 <_sk_callback_hsw+0x2c8>
+  .byte  196,193,124,89,194                  // vmulps        %ymm10,%ymm0,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,124,88,194                  // vaddps        %ymm10,%ymm0,%ymm0
+  .byte  196,195,125,74,193,128              // vblendvps     %ymm8,%ymm9,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,5,22,38,0,0           // vbroadcastss  0x2616(%rip),%ymm8        # 4b24 <_sk_callback_hsw+0x2cc>
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_g_hsw
+.globl _sk_parametric_g_hsw
+FUNCTION(_sk_parametric_g_hsw)
+_sk_parametric_g_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,116,194,192,2                // vcmpleps      %ymm8,%ymm1,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,66,117,168,202                  // vfmadd213ps   %ymm10,%ymm1,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  196,66,117,168,211                  // vfmadd213ps   %ymm11,%ymm1,%ymm10
+  .byte  196,226,125,24,8                    // vbroadcastss  (%rax),%ymm1
+  .byte  196,65,124,91,218                   // vcvtdq2ps     %ymm10,%ymm11
+  .byte  196,98,125,24,37,206,37,0,0         // vbroadcastss  0x25ce(%rip),%ymm12        # 4b28 <_sk_callback_hsw+0x2d0>
+  .byte  196,98,125,24,45,201,37,0,0         // vbroadcastss  0x25c9(%rip),%ymm13        # 4b2c <_sk_callback_hsw+0x2d4>
+  .byte  196,65,44,84,213                    // vandps        %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,191,37,0,0         // vbroadcastss  0x25bf(%rip),%ymm13        # 4b30 <_sk_callback_hsw+0x2d8>
+  .byte  196,65,44,86,213                    // vorps         %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,181,37,0,0         // vbroadcastss  0x25b5(%rip),%ymm13        # 4b34 <_sk_callback_hsw+0x2dc>
+  .byte  196,66,37,184,236                   // vfmadd231ps   %ymm12,%ymm11,%ymm13
+  .byte  196,98,125,24,29,171,37,0,0         // vbroadcastss  0x25ab(%rip),%ymm11        # 4b38 <_sk_callback_hsw+0x2e0>
+  .byte  196,66,45,172,221                   // vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  .byte  196,98,125,24,37,161,37,0,0         // vbroadcastss  0x25a1(%rip),%ymm12        # 4b3c <_sk_callback_hsw+0x2e4>
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,98,125,24,37,151,37,0,0         // vbroadcastss  0x2597(%rip),%ymm12        # 4b40 <_sk_callback_hsw+0x2e8>
+  .byte  196,65,28,94,210                    // vdivps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,116,89,202                  // vmulps        %ymm10,%ymm1,%ymm1
+  .byte  196,99,125,8,209,1                  // vroundps      $0x1,%ymm1,%ymm10
+  .byte  196,65,116,92,210                   // vsubps        %ymm10,%ymm1,%ymm10
+  .byte  196,98,125,24,29,120,37,0,0         // vbroadcastss  0x2578(%rip),%ymm11        # 4b44 <_sk_callback_hsw+0x2ec>
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,29,110,37,0,0         // vbroadcastss  0x256e(%rip),%ymm11        # 4b48 <_sk_callback_hsw+0x2f0>
+  .byte  196,98,45,172,217                   // vfnmadd213ps  %ymm1,%ymm10,%ymm11
+  .byte  196,226,125,24,13,100,37,0,0        // vbroadcastss  0x2564(%rip),%ymm1        # 4b4c <_sk_callback_hsw+0x2f4>
+  .byte  196,193,116,92,202                  // vsubps        %ymm10,%ymm1,%ymm1
+  .byte  196,98,125,24,21,90,37,0,0          // vbroadcastss  0x255a(%rip),%ymm10        # 4b50 <_sk_callback_hsw+0x2f8>
+  .byte  197,172,94,201                      // vdivps        %ymm1,%ymm10,%ymm1
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  196,98,125,24,21,77,37,0,0          // vbroadcastss  0x254d(%rip),%ymm10        # 4b54 <_sk_callback_hsw+0x2fc>
+  .byte  196,193,116,89,202                  // vmulps        %ymm10,%ymm1,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  196,195,117,74,201,128              // vblendvps     %ymm8,%ymm9,%ymm1,%ymm1
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,116,95,200                  // vmaxps        %ymm8,%ymm1,%ymm1
+  .byte  196,98,125,24,5,36,37,0,0           // vbroadcastss  0x2524(%rip),%ymm8        # 4b58 <_sk_callback_hsw+0x300>
+  .byte  196,193,116,93,200                  // vminps        %ymm8,%ymm1,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_b_hsw
+.globl _sk_parametric_b_hsw
+FUNCTION(_sk_parametric_b_hsw)
+_sk_parametric_b_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,108,194,192,2                // vcmpleps      %ymm8,%ymm2,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,66,109,168,202                  // vfmadd213ps   %ymm10,%ymm2,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  196,66,109,168,211                  // vfmadd213ps   %ymm11,%ymm2,%ymm10
+  .byte  196,226,125,24,16                   // vbroadcastss  (%rax),%ymm2
+  .byte  196,65,124,91,218                   // vcvtdq2ps     %ymm10,%ymm11
+  .byte  196,98,125,24,37,220,36,0,0         // vbroadcastss  0x24dc(%rip),%ymm12        # 4b5c <_sk_callback_hsw+0x304>
+  .byte  196,98,125,24,45,215,36,0,0         // vbroadcastss  0x24d7(%rip),%ymm13        # 4b60 <_sk_callback_hsw+0x308>
+  .byte  196,65,44,84,213                    // vandps        %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,205,36,0,0         // vbroadcastss  0x24cd(%rip),%ymm13        # 4b64 <_sk_callback_hsw+0x30c>
+  .byte  196,65,44,86,213                    // vorps         %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,195,36,0,0         // vbroadcastss  0x24c3(%rip),%ymm13        # 4b68 <_sk_callback_hsw+0x310>
+  .byte  196,66,37,184,236                   // vfmadd231ps   %ymm12,%ymm11,%ymm13
+  .byte  196,98,125,24,29,185,36,0,0         // vbroadcastss  0x24b9(%rip),%ymm11        # 4b6c <_sk_callback_hsw+0x314>
+  .byte  196,66,45,172,221                   // vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  .byte  196,98,125,24,37,175,36,0,0         // vbroadcastss  0x24af(%rip),%ymm12        # 4b70 <_sk_callback_hsw+0x318>
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,98,125,24,37,165,36,0,0         // vbroadcastss  0x24a5(%rip),%ymm12        # 4b74 <_sk_callback_hsw+0x31c>
+  .byte  196,65,28,94,210                    // vdivps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,108,89,210                  // vmulps        %ymm10,%ymm2,%ymm2
+  .byte  196,99,125,8,210,1                  // vroundps      $0x1,%ymm2,%ymm10
+  .byte  196,65,108,92,210                   // vsubps        %ymm10,%ymm2,%ymm10
+  .byte  196,98,125,24,29,134,36,0,0         // vbroadcastss  0x2486(%rip),%ymm11        # 4b78 <_sk_callback_hsw+0x320>
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  196,98,125,24,29,124,36,0,0         // vbroadcastss  0x247c(%rip),%ymm11        # 4b7c <_sk_callback_hsw+0x324>
+  .byte  196,98,45,172,218                   // vfnmadd213ps  %ymm2,%ymm10,%ymm11
+  .byte  196,226,125,24,21,114,36,0,0        // vbroadcastss  0x2472(%rip),%ymm2        # 4b80 <_sk_callback_hsw+0x328>
+  .byte  196,193,108,92,210                  // vsubps        %ymm10,%ymm2,%ymm2
+  .byte  196,98,125,24,21,104,36,0,0         // vbroadcastss  0x2468(%rip),%ymm10        # 4b84 <_sk_callback_hsw+0x32c>
+  .byte  197,172,94,210                      // vdivps        %ymm2,%ymm10,%ymm2
+  .byte  197,164,88,210                      // vaddps        %ymm2,%ymm11,%ymm2
+  .byte  196,98,125,24,21,91,36,0,0          // vbroadcastss  0x245b(%rip),%ymm10        # 4b88 <_sk_callback_hsw+0x330>
+  .byte  196,193,108,89,210                  // vmulps        %ymm10,%ymm2,%ymm2
+  .byte  197,253,91,210                      // vcvtps2dq     %ymm2,%ymm2
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  196,195,109,74,209,128              // vblendvps     %ymm8,%ymm9,%ymm2,%ymm2
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,108,95,208                  // vmaxps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,24,5,50,36,0,0           // vbroadcastss  0x2432(%rip),%ymm8        # 4b8c <_sk_callback_hsw+0x334>
+  .byte  196,193,108,93,208                  // vminps        %ymm8,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_a_hsw
+.globl _sk_parametric_a_hsw
+FUNCTION(_sk_parametric_a_hsw)
+_sk_parametric_a_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,100,194,192,2                // vcmpleps      %ymm8,%ymm3,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,66,101,168,202                  // vfmadd213ps   %ymm10,%ymm3,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  196,66,101,168,211                  // vfmadd213ps   %ymm11,%ymm3,%ymm10
+  .byte  196,226,125,24,24                   // vbroadcastss  (%rax),%ymm3
+  .byte  196,65,124,91,218                   // vcvtdq2ps     %ymm10,%ymm11
+  .byte  196,98,125,24,37,234,35,0,0         // vbroadcastss  0x23ea(%rip),%ymm12        # 4b90 <_sk_callback_hsw+0x338>
+  .byte  196,98,125,24,45,229,35,0,0         // vbroadcastss  0x23e5(%rip),%ymm13        # 4b94 <_sk_callback_hsw+0x33c>
+  .byte  196,65,44,84,213                    // vandps        %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,219,35,0,0         // vbroadcastss  0x23db(%rip),%ymm13        # 4b98 <_sk_callback_hsw+0x340>
+  .byte  196,65,44,86,213                    // vorps         %ymm13,%ymm10,%ymm10
+  .byte  196,98,125,24,45,209,35,0,0         // vbroadcastss  0x23d1(%rip),%ymm13        # 4b9c <_sk_callback_hsw+0x344>
+  .byte  196,66,37,184,236                   // vfmadd231ps   %ymm12,%ymm11,%ymm13
+  .byte  196,98,125,24,29,199,35,0,0         // vbroadcastss  0x23c7(%rip),%ymm11        # 4ba0 <_sk_callback_hsw+0x348>
+  .byte  196,66,45,172,221                   // vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  .byte  196,98,125,24,37,189,35,0,0         // vbroadcastss  0x23bd(%rip),%ymm12        # 4ba4 <_sk_callback_hsw+0x34c>
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,98,125,24,37,179,35,0,0         // vbroadcastss  0x23b3(%rip),%ymm12        # 4ba8 <_sk_callback_hsw+0x350>
+  .byte  196,65,28,94,210                    // vdivps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,100,89,218                  // vmulps        %ymm10,%ymm3,%ymm3
+  .byte  196,99,125,8,211,1                  // vroundps      $0x1,%ymm3,%ymm10
+  .byte  196,65,100,92,210                   // vsubps        %ymm10,%ymm3,%ymm10
+  .byte  196,98,125,24,29,148,35,0,0         // vbroadcastss  0x2394(%rip),%ymm11        # 4bac <_sk_callback_hsw+0x354>
+  .byte  196,193,100,88,219                  // vaddps        %ymm11,%ymm3,%ymm3
+  .byte  196,98,125,24,29,138,35,0,0         // vbroadcastss  0x238a(%rip),%ymm11        # 4bb0 <_sk_callback_hsw+0x358>
+  .byte  196,98,45,172,219                   // vfnmadd213ps  %ymm3,%ymm10,%ymm11
+  .byte  196,226,125,24,29,128,35,0,0        // vbroadcastss  0x2380(%rip),%ymm3        # 4bb4 <_sk_callback_hsw+0x35c>
+  .byte  196,193,100,92,218                  // vsubps        %ymm10,%ymm3,%ymm3
+  .byte  196,98,125,24,21,118,35,0,0         // vbroadcastss  0x2376(%rip),%ymm10        # 4bb8 <_sk_callback_hsw+0x360>
+  .byte  197,172,94,219                      // vdivps        %ymm3,%ymm10,%ymm3
+  .byte  197,164,88,219                      // vaddps        %ymm3,%ymm11,%ymm3
+  .byte  196,98,125,24,21,105,35,0,0         // vbroadcastss  0x2369(%rip),%ymm10        # 4bbc <_sk_callback_hsw+0x364>
+  .byte  196,193,100,89,218                  // vmulps        %ymm10,%ymm3,%ymm3
+  .byte  197,253,91,219                      // vcvtps2dq     %ymm3,%ymm3
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,100,88,218                  // vaddps        %ymm10,%ymm3,%ymm3
+  .byte  196,195,101,74,217,128              // vblendvps     %ymm8,%ymm9,%ymm3,%ymm3
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,100,95,216                  // vmaxps        %ymm8,%ymm3,%ymm3
+  .byte  196,98,125,24,5,64,35,0,0           // vbroadcastss  0x2340(%rip),%ymm8        # 4bc0 <_sk_callback_hsw+0x368>
+  .byte  196,193,100,93,216                  // vminps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lab_to_xyz_hsw
+.globl _sk_lab_to_xyz_hsw
+FUNCTION(_sk_lab_to_xyz_hsw)
+_sk_lab_to_xyz_hsw:
+  .byte  196,98,125,24,5,50,35,0,0           // vbroadcastss  0x2332(%rip),%ymm8        # 4bc4 <_sk_callback_hsw+0x36c>
+  .byte  196,98,125,24,13,45,35,0,0          // vbroadcastss  0x232d(%rip),%ymm9        # 4bc8 <_sk_callback_hsw+0x370>
+  .byte  196,98,125,24,21,40,35,0,0          // vbroadcastss  0x2328(%rip),%ymm10        # 4bcc <_sk_callback_hsw+0x374>
+  .byte  196,194,53,168,202                  // vfmadd213ps   %ymm10,%ymm9,%ymm1
+  .byte  196,194,53,168,210                  // vfmadd213ps   %ymm10,%ymm9,%ymm2
+  .byte  196,98,125,24,13,25,35,0,0          // vbroadcastss  0x2319(%rip),%ymm9        # 4bd0 <_sk_callback_hsw+0x378>
+  .byte  196,66,125,184,200                  // vfmadd231ps   %ymm8,%ymm0,%ymm9
+  .byte  196,226,125,24,5,15,35,0,0          // vbroadcastss  0x230f(%rip),%ymm0        # 4bd4 <_sk_callback_hsw+0x37c>
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  196,98,125,24,5,6,35,0,0            // vbroadcastss  0x2306(%rip),%ymm8        # 4bd8 <_sk_callback_hsw+0x380>
+  .byte  196,98,117,168,192                  // vfmadd213ps   %ymm0,%ymm1,%ymm8
+  .byte  196,98,125,24,13,252,34,0,0         // vbroadcastss  0x22fc(%rip),%ymm9        # 4bdc <_sk_callback_hsw+0x384>
+  .byte  196,98,109,172,200                  // vfnmadd213ps  %ymm0,%ymm2,%ymm9
+  .byte  196,193,60,89,200                   // vmulps        %ymm8,%ymm8,%ymm1
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  196,226,125,24,21,233,34,0,0        // vbroadcastss  0x22e9(%rip),%ymm2        # 4be0 <_sk_callback_hsw+0x388>
+  .byte  197,108,194,209,1                   // vcmpltps      %ymm1,%ymm2,%ymm10
+  .byte  196,98,125,24,29,223,34,0,0         // vbroadcastss  0x22df(%rip),%ymm11        # 4be4 <_sk_callback_hsw+0x38c>
+  .byte  196,65,60,88,195                    // vaddps        %ymm11,%ymm8,%ymm8
+  .byte  196,98,125,24,37,213,34,0,0         // vbroadcastss  0x22d5(%rip),%ymm12        # 4be8 <_sk_callback_hsw+0x390>
+  .byte  196,65,60,89,196                    // vmulps        %ymm12,%ymm8,%ymm8
+  .byte  196,99,61,74,193,160                // vblendvps     %ymm10,%ymm1,%ymm8,%ymm8
+  .byte  197,252,89,200                      // vmulps        %ymm0,%ymm0,%ymm1
+  .byte  197,252,89,201                      // vmulps        %ymm1,%ymm0,%ymm1
+  .byte  197,108,194,209,1                   // vcmpltps      %ymm1,%ymm2,%ymm10
+  .byte  196,193,124,88,195                  // vaddps        %ymm11,%ymm0,%ymm0
+  .byte  196,193,124,89,196                  // vmulps        %ymm12,%ymm0,%ymm0
+  .byte  196,227,125,74,201,160              // vblendvps     %ymm10,%ymm1,%ymm0,%ymm1
+  .byte  196,193,52,89,193                   // vmulps        %ymm9,%ymm9,%ymm0
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  197,236,194,208,1                   // vcmpltps      %ymm0,%ymm2,%ymm2
+  .byte  196,65,52,88,203                    // vaddps        %ymm11,%ymm9,%ymm9
+  .byte  196,65,52,89,204                    // vmulps        %ymm12,%ymm9,%ymm9
+  .byte  196,227,53,74,208,32                // vblendvps     %ymm2,%ymm0,%ymm9,%ymm2
+  .byte  196,226,125,24,5,138,34,0,0         // vbroadcastss  0x228a(%rip),%ymm0        # 4bec <_sk_callback_hsw+0x394>
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  196,98,125,24,5,129,34,0,0          // vbroadcastss  0x2281(%rip),%ymm8        # 4bf0 <_sk_callback_hsw+0x398>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_a8_hsw
+.globl _sk_load_a8_hsw
+FUNCTION(_sk_load_a8_hsw)
+_sk_load_a8_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,45                              // jne           29b5 <_sk_load_a8_hsw+0x3d>
+  .byte  197,250,126,0                       // vmovq         (%rax),%xmm0
+  .byte  196,226,125,49,192                  // vpmovzxbd     %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,86,34,0,0         // vbroadcastss  0x2256(%rip),%ymm1        # 4bf4 <_sk_callback_hsw+0x39c>
+  .byte  197,252,89,217                      // vmulps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           29be <_sk_load_a8_hsw+0x46>
+  .byte  196,193,249,110,194                 // vmovq         %r10,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  235,177                             // jmp           298c <_sk_load_a8_hsw+0x14>
+
+HIDDEN _sk_gather_a8_hsw
+.globl _sk_gather_a8_hsw
+FUNCTION(_sk_gather_a8_hsw)
+_sk_gather_a8_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,32,12,1,0               // vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm1
+  .byte  196,227,249,22,192,1                // vpextrq       $0x1,%xmm0,%rax
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,113,32,12,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm1
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,113,32,202,2                // vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,113,32,200,3                // vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,113,32,200,4                // vpinsrb       $0x4,%eax,%xmm1,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,182,4,17                      // movzbl        (%r9,%r10,1),%eax
+  .byte  196,227,113,32,192,5                // vpinsrb       $0x5,%eax,%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,121,32,192,6                // vpinsrb       $0x6,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,25                      // movzbl        (%r9,%r11,1),%eax
+  .byte  196,227,121,32,192,7                // vpinsrb       $0x7,%eax,%xmm0,%xmm0
+  .byte  196,226,125,49,192                  // vpmovzxbd     %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,103,33,0,0        // vbroadcastss  0x2167(%rip),%ymm1        # 4bf8 <_sk_callback_hsw+0x3a0>
+  .byte  197,252,89,217                      // vmulps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,237,239,210                     // vpxor         %ymm2,%ymm2,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_a8_hsw
+.globl _sk_store_a8_hsw
+FUNCTION(_sk_store_a8_hsw)
+_sk_store_a8_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,98,125,24,5,73,33,0,0           // vbroadcastss  0x2149(%rip),%ymm8        # 4bfc <_sk_callback_hsw+0x3a4>
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,67,125,25,193,1                 // vextractf128  $0x1,%ymm8,%xmm9
+  .byte  196,66,57,43,193                    // vpackusdw     %xmm9,%xmm8,%xmm8
+  .byte  196,65,57,103,192                   // vpackuswb     %xmm8,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           2adc <_sk_store_a8_hsw+0x37>
+  .byte  196,65,123,17,4,19                  // vmovsd        %xmm8,(%r11,%rdx,1)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            2ad8 <_sk_store_a8_hsw+0x33>
+  .byte  196,66,121,48,192                   // vpmovzxbw     %xmm8,%xmm8
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,68,0,0,0                  // lea           0x44(%rip),%r10        # 2b40 <_sk_store_a8_hsw+0x9b>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,67,121,20,68,19,6,12            // vpextrb       $0xc,%xmm8,0x6(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,5,10            // vpextrb       $0xa,%xmm8,0x5(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,4,8             // vpextrb       $0x8,%xmm8,0x4(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,3,6             // vpextrb       $0x6,%xmm8,0x3(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,2,4             // vpextrb       $0x4,%xmm8,0x2(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,1,2             // vpextrb       $0x2,%xmm8,0x1(%r11,%rdx,1)
+  .byte  196,67,121,20,4,19,0                // vpextrb       $0x0,%xmm8,(%r11,%rdx,1)
+  .byte  235,154                             // jmp           2ad8 <_sk_store_a8_hsw+0x33>
+  .byte  102,144                             // xchg          %ax,%ax
+  .byte  245                                 // cmc
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  237                                 // in            (%dx),%eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,229                             // jmpq          *%rbp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  221,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,213                             // callq         *%rbp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,205                             // dec           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,197                             // inc           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_g8_hsw
+.globl _sk_load_g8_hsw
+FUNCTION(_sk_load_g8_hsw)
+_sk_load_g8_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,50                              // jne           2b9e <_sk_load_g8_hsw+0x42>
+  .byte  197,250,126,0                       // vmovq         (%rax),%xmm0
+  .byte  196,226,125,49,192                  // vpmovzxbd     %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,126,32,0,0        // vbroadcastss  0x207e(%rip),%ymm1        # 4c00 <_sk_callback_hsw+0x3a8>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,115,32,0,0        // vbroadcastss  0x2073(%rip),%ymm3        # 4c04 <_sk_callback_hsw+0x3ac>
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           2ba7 <_sk_load_g8_hsw+0x4b>
+  .byte  196,193,249,110,194                 // vmovq         %r10,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  235,172                             // jmp           2b70 <_sk_load_g8_hsw+0x14>
+
+HIDDEN _sk_gather_g8_hsw
+.globl _sk_gather_g8_hsw
+FUNCTION(_sk_gather_g8_hsw)
+_sk_gather_g8_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,32,12,1,0               // vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm1
+  .byte  196,227,249,22,192,1                // vpextrq       $0x1,%xmm0,%rax
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,113,32,12,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm1
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,113,32,202,2                // vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,113,32,200,3                // vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,113,32,200,4                // vpinsrb       $0x4,%eax,%xmm1,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,182,4,17                      // movzbl        (%r9,%r10,1),%eax
+  .byte  196,227,113,32,192,5                // vpinsrb       $0x5,%eax,%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,121,32,192,6                // vpinsrb       $0x6,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,25                      // movzbl        (%r9,%r11,1),%eax
+  .byte  196,227,121,32,192,7                // vpinsrb       $0x7,%eax,%xmm0,%xmm0
+  .byte  196,226,125,49,192                  // vpmovzxbd     %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,142,31,0,0        // vbroadcastss  0x1f8e(%rip),%ymm1        # 4c08 <_sk_callback_hsw+0x3b0>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,131,31,0,0        // vbroadcastss  0x1f83(%rip),%ymm3        # 4c0c <_sk_callback_hsw+0x3b4>
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gather_i8_hsw
+.globl _sk_gather_i8_hsw
+FUNCTION(_sk_gather_i8_hsw)
+_sk_gather_i8_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,193                          // mov           %rax,%r9
+  .byte  77,133,201                          // test          %r9,%r9
+  .byte  116,5                               // je            2ca2 <_sk_gather_i8_hsw+0xf>
+  .byte  76,137,200                          // mov           %r9,%rax
+  .byte  235,2                               // jmp           2ca4 <_sk_gather_i8_hsw+0x11>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  83                                  // push          %rbx
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  196,193,249,126,195                 // vmovq         %xmm0,%r11
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,195,121,32,12,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm1
+  .byte  196,227,249,22,192,1                // vpextrq       $0x1,%xmm0,%rax
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,131,113,32,12,26,1              // vpinsrb       $0x1,(%r10,%r11,1),%xmm1,%xmm1
+  .byte  65,137,195                          // mov           %eax,%r11d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  196,131,113,32,12,26,2              // vpinsrb       $0x2,(%r10,%r11,1),%xmm1,%xmm1
+  .byte  196,193,249,126,195                 // vmovq         %xmm0,%r11
+  .byte  196,195,113,32,12,2,3               // vpinsrb       $0x3,(%r10,%rax,1),%xmm1,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,195,113,32,12,2,4               // vpinsrb       $0x4,(%r10,%rax,1),%xmm1,%xmm1
+  .byte  196,227,249,22,195,1                // vpextrq       $0x1,%xmm0,%rbx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,131,113,32,4,26,5               // vpinsrb       $0x5,(%r10,%r11,1),%xmm1,%xmm0
+  .byte  137,216                             // mov           %ebx,%eax
+  .byte  196,195,121,32,4,2,6                // vpinsrb       $0x6,(%r10,%rax,1),%xmm0,%xmm0
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  196,195,121,32,4,26,7               // vpinsrb       $0x7,(%r10,%rbx,1),%xmm0,%xmm0
+  .byte  196,226,125,49,192                  // vpmovzxbd     %xmm0,%ymm0
+  .byte  73,139,65,8                         // mov           0x8(%r9),%rax
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  196,226,117,144,28,128              // vpgatherdd    %ymm1,(%rax,%ymm0,4),%ymm3
+  .byte  197,229,219,5,55,33,0,0             // vpand         0x2137(%rip),%ymm3,%ymm0        # 4e80 <_sk_callback_hsw+0x628>
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,5,186,30,0,0          // vbroadcastss  0x1eba(%rip),%ymm8        # 4c10 <_sk_callback_hsw+0x3b8>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,226,101,0,13,60,33,0,0          // vpshufb       0x213c(%rip),%ymm3,%ymm1        # 4ea0 <_sk_callback_hsw+0x648>
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,226,101,0,21,74,33,0,0          // vpshufb       0x214a(%rip),%ymm3,%ymm2        # 4ec0 <_sk_callback_hsw+0x668>
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,229,114,211,24                  // vpsrld        $0x18,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_565_hsw
+.globl _sk_load_565_hsw
+FUNCTION(_sk_load_565_hsw)
+_sk_load_565_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,114                             // jne           2e0e <_sk_load_565_hsw+0x7c>
+  .byte  196,193,122,111,4,83                // vmovdqu       (%r11,%rdx,2),%xmm0
+  .byte  196,226,125,51,208                  // vpmovzxwd     %xmm0,%ymm2
+  .byte  196,226,125,88,5,100,30,0,0         // vpbroadcastd  0x1e64(%rip),%ymm0        # 4c14 <_sk_callback_hsw+0x3bc>
+  .byte  197,237,219,192                     // vpand         %ymm0,%ymm2,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,87,30,0,0         // vbroadcastss  0x1e57(%rip),%ymm1        # 4c18 <_sk_callback_hsw+0x3c0>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,88,13,78,30,0,0         // vpbroadcastd  0x1e4e(%rip),%ymm1        # 4c1c <_sk_callback_hsw+0x3c4>
+  .byte  197,237,219,201                     // vpand         %ymm1,%ymm2,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,29,65,30,0,0         // vbroadcastss  0x1e41(%rip),%ymm3        # 4c20 <_sk_callback_hsw+0x3c8>
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  196,226,125,88,29,56,30,0,0         // vpbroadcastd  0x1e38(%rip),%ymm3        # 4c24 <_sk_callback_hsw+0x3cc>
+  .byte  197,237,219,211                     // vpand         %ymm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,226,125,24,29,43,30,0,0         // vbroadcastss  0x1e2b(%rip),%ymm3        # 4c28 <_sk_callback_hsw+0x3d0>
+  .byte  197,236,89,211                      // vmulps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,32,30,0,0         // vbroadcastss  0x1e20(%rip),%ymm3        # 4c2c <_sk_callback_hsw+0x3d4>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,128                             // ja            2da2 <_sk_load_565_hsw+0x10>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,75,0,0,0                  // lea           0x4b(%rip),%r10        # 2e78 <_sk_load_565_hsw+0xe6>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,12,6          // vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,10,5          // vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,8,4           // vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,6,3           // vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,4,2           // vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,2,1           // vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,4,83,0              // vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  233,44,255,255,255                  // jmpq          2da2 <_sk_load_565_hsw+0x10>
+  .byte  102,144                             // xchg          %ax,%ax
+  .byte  242,255                             // repnz         (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  234                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,226                             // jmpq          *%rdx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  218,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,210                             // callq         *%rdx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,202                             // dec           %edx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  190                                 // .byte         0xbe
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_gather_565_hsw
+.globl _sk_gather_565_hsw
+FUNCTION(_sk_gather_565_hsw)
+_sk_gather_565_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,110,200                     // vmovd         %eax,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,241,196,200,1                   // vpinsrw       $0x1,%eax,%xmm1,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,2                   // vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,241,196,200,3                   // vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,4                   // vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,241,196,192,5                   // vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,196,192,6                   // vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,249,196,192,7                   // vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  .byte  196,226,125,51,208                  // vpmovzxwd     %xmm0,%ymm2
+  .byte  196,226,125,88,5,235,28,0,0         // vpbroadcastd  0x1ceb(%rip),%ymm0        # 4c30 <_sk_callback_hsw+0x3d8>
+  .byte  197,237,219,192                     // vpand         %ymm0,%ymm2,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,222,28,0,0        // vbroadcastss  0x1cde(%rip),%ymm1        # 4c34 <_sk_callback_hsw+0x3dc>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,88,13,213,28,0,0        // vpbroadcastd  0x1cd5(%rip),%ymm1        # 4c38 <_sk_callback_hsw+0x3e0>
+  .byte  197,237,219,201                     // vpand         %ymm1,%ymm2,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,29,200,28,0,0        // vbroadcastss  0x1cc8(%rip),%ymm3        # 4c3c <_sk_callback_hsw+0x3e4>
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  196,226,125,88,29,191,28,0,0        // vpbroadcastd  0x1cbf(%rip),%ymm3        # 4c40 <_sk_callback_hsw+0x3e8>
+  .byte  197,237,219,211                     // vpand         %ymm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,226,125,24,29,178,28,0,0        // vbroadcastss  0x1cb2(%rip),%ymm3        # 4c44 <_sk_callback_hsw+0x3ec>
+  .byte  197,236,89,211                      // vmulps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,167,28,0,0        // vbroadcastss  0x1ca7(%rip),%ymm3        # 4c48 <_sk_callback_hsw+0x3f0>
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_565_hsw
+.globl _sk_store_565_hsw
+FUNCTION(_sk_store_565_hsw)
+_sk_store_565_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,98,125,24,5,155,28,0,0          // vbroadcastss  0x1c9b(%rip),%ymm8        # 4c4c <_sk_callback_hsw+0x3f4>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,193,53,114,241,11               // vpslld        $0xb,%ymm9,%ymm9
+  .byte  196,98,125,24,21,134,28,0,0         // vbroadcastss  0x1c86(%rip),%ymm10        # 4c50 <_sk_callback_hsw+0x3f8>
+  .byte  196,65,116,89,210                   // vmulps        %ymm10,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,45,114,242,5                // vpslld        $0x5,%ymm10,%ymm10
+  .byte  196,65,45,235,201                   // vpor          %ymm9,%ymm10,%ymm9
+  .byte  196,65,108,89,192                   // vmulps        %ymm8,%ymm2,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,65,53,235,192                   // vpor          %ymm8,%ymm9,%ymm8
+  .byte  196,67,125,57,193,1                 // vextracti128  $0x1,%ymm8,%xmm9
+  .byte  196,66,57,43,193                    // vpackusdw     %xmm9,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3008 <_sk_store_565_hsw+0x65>
+  .byte  196,65,122,127,4,83                 // vmovdqu       %xmm8,(%r11,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            3004 <_sk_store_565_hsw+0x61>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,69,0,0,0                  // lea           0x45(%rip),%r10        # 3068 <_sk_store_565_hsw+0xc5>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,67,121,21,68,83,12,6            // vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,10,5            // vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,8,4             // vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,6,3             // vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,4,2             // vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,2,1             // vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  .byte  196,67,121,21,4,83,0                // vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  .byte  235,159                             // jmp           3004 <_sk_store_565_hsw+0x61>
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  244                                 // hlt
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  236                                 // in            (%dx),%al
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,228                             // jmpq          *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  220,255                             // fdivr         %st,%st(7)
+  .byte  255                                 // (bad)
+  .byte  255,212                             // callq         *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,204                             // dec           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,196                             // inc           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_4444_hsw
+.globl _sk_load_4444_hsw
+FUNCTION(_sk_load_4444_hsw)
+_sk_load_4444_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,138,0,0,0                    // jne           311c <_sk_load_4444_hsw+0x98>
+  .byte  196,193,122,111,4,83                // vmovdqu       (%r11,%rdx,2),%xmm0
+  .byte  196,226,125,51,216                  // vpmovzxwd     %xmm0,%ymm3
+  .byte  196,226,125,88,5,174,27,0,0         // vpbroadcastd  0x1bae(%rip),%ymm0        # 4c54 <_sk_callback_hsw+0x3fc>
+  .byte  197,229,219,192                     // vpand         %ymm0,%ymm3,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,161,27,0,0        // vbroadcastss  0x1ba1(%rip),%ymm1        # 4c58 <_sk_callback_hsw+0x400>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,88,13,152,27,0,0        // vpbroadcastd  0x1b98(%rip),%ymm1        # 4c5c <_sk_callback_hsw+0x404>
+  .byte  197,229,219,201                     // vpand         %ymm1,%ymm3,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,21,139,27,0,0        // vbroadcastss  0x1b8b(%rip),%ymm2        # 4c60 <_sk_callback_hsw+0x408>
+  .byte  197,244,89,202                      // vmulps        %ymm2,%ymm1,%ymm1
+  .byte  196,226,125,88,21,130,27,0,0        // vpbroadcastd  0x1b82(%rip),%ymm2        # 4c64 <_sk_callback_hsw+0x40c>
+  .byte  197,229,219,210                     // vpand         %ymm2,%ymm3,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,98,125,24,5,117,27,0,0          // vbroadcastss  0x1b75(%rip),%ymm8        # 4c68 <_sk_callback_hsw+0x410>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,88,5,107,27,0,0          // vpbroadcastd  0x1b6b(%rip),%ymm8        # 4c6c <_sk_callback_hsw+0x414>
+  .byte  196,193,101,219,216                 // vpand         %ymm8,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,98,125,24,5,93,27,0,0           // vbroadcastss  0x1b5d(%rip),%ymm8        # 4c70 <_sk_callback_hsw+0x418>
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,100,255,255,255              // ja            3098 <_sk_load_4444_hsw+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,73,0,0,0                  // lea           0x49(%rip),%r10        # 3188 <_sk_load_4444_hsw+0x104>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,12,6          // vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,10,5          // vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,8,4           // vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,6,3           // vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,4,2           // vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,2,1           // vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,4,83,0              // vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  233,16,255,255,255                  // jmpq          3098 <_sk_load_4444_hsw+0x14>
+  .byte  244                                 // hlt
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  236                                 // in            (%dx),%al
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,228                             // jmpq          *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  220,255                             // fdivr         %st,%st(7)
+  .byte  255                                 // (bad)
+  .byte  255,212                             // callq         *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,204                             // dec           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,192                             // inc           %eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_gather_4444_hsw
+.globl _sk_gather_4444_hsw
+FUNCTION(_sk_gather_4444_hsw)
+_sk_gather_4444_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,110,200                     // vmovd         %eax,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,241,196,200,1                   // vpinsrw       $0x1,%eax,%xmm1,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,2                   // vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,241,196,200,3                   // vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,4                   // vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,241,196,192,5                   // vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,196,192,6                   // vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,249,196,192,7                   // vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  .byte  196,226,125,51,216                  // vpmovzxwd     %xmm0,%ymm3
+  .byte  196,226,125,88,5,31,26,0,0          // vpbroadcastd  0x1a1f(%rip),%ymm0        # 4c74 <_sk_callback_hsw+0x41c>
+  .byte  197,229,219,192                     // vpand         %ymm0,%ymm3,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,18,26,0,0         // vbroadcastss  0x1a12(%rip),%ymm1        # 4c78 <_sk_callback_hsw+0x420>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,88,13,9,26,0,0          // vpbroadcastd  0x1a09(%rip),%ymm1        # 4c7c <_sk_callback_hsw+0x424>
+  .byte  197,229,219,201                     // vpand         %ymm1,%ymm3,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,21,252,25,0,0        // vbroadcastss  0x19fc(%rip),%ymm2        # 4c80 <_sk_callback_hsw+0x428>
+  .byte  197,244,89,202                      // vmulps        %ymm2,%ymm1,%ymm1
+  .byte  196,226,125,88,21,243,25,0,0        // vpbroadcastd  0x19f3(%rip),%ymm2        # 4c84 <_sk_callback_hsw+0x42c>
+  .byte  197,229,219,210                     // vpand         %ymm2,%ymm3,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,98,125,24,5,230,25,0,0          // vbroadcastss  0x19e6(%rip),%ymm8        # 4c88 <_sk_callback_hsw+0x430>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,88,5,220,25,0,0          // vpbroadcastd  0x19dc(%rip),%ymm8        # 4c8c <_sk_callback_hsw+0x434>
+  .byte  196,193,101,219,216                 // vpand         %ymm8,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,98,125,24,5,206,25,0,0          // vbroadcastss  0x19ce(%rip),%ymm8        # 4c90 <_sk_callback_hsw+0x438>
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_4444_hsw
+.globl _sk_store_4444_hsw
+FUNCTION(_sk_store_4444_hsw)
+_sk_store_4444_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,98,125,24,5,187,25,0,0          // vbroadcastss  0x19bb(%rip),%ymm8        # 4c94 <_sk_callback_hsw+0x43c>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,193,53,114,241,12               // vpslld        $0xc,%ymm9,%ymm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,45,114,242,8                // vpslld        $0x8,%ymm10,%ymm10
+  .byte  196,65,45,235,201                   // vpor          %ymm9,%ymm10,%ymm9
+  .byte  196,65,108,89,208                   // vmulps        %ymm8,%ymm2,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,45,114,242,4                // vpslld        $0x4,%ymm10,%ymm10
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,65,45,235,192                   // vpor          %ymm8,%ymm10,%ymm8
+  .byte  196,65,53,235,192                   // vpor          %ymm8,%ymm9,%ymm8
+  .byte  196,67,125,57,193,1                 // vextracti128  $0x1,%ymm8,%xmm9
+  .byte  196,66,57,43,193                    // vpackusdw     %xmm9,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           333c <_sk_store_4444_hsw+0x71>
+  .byte  196,65,122,127,4,83                 // vmovdqu       %xmm8,(%r11,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            3338 <_sk_store_4444_hsw+0x6d>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,69,0,0,0                  // lea           0x45(%rip),%r10        # 339c <_sk_store_4444_hsw+0xd1>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,67,121,21,68,83,12,6            // vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,10,5            // vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,8,4             // vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,6,3             // vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,4,2             // vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,2,1             // vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  .byte  196,67,121,21,4,83,0                // vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  .byte  235,159                             // jmp           3338 <_sk_store_4444_hsw+0x6d>
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  244                                 // hlt
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  236                                 // in            (%dx),%al
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,228                             // jmpq          *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  220,255                             // fdivr         %st,%st(7)
+  .byte  255                                 // (bad)
+  .byte  255,212                             // callq         *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,204                             // dec           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,196                             // inc           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_8888_hsw
+.globl _sk_load_8888_hsw
+FUNCTION(_sk_load_8888_hsw)
+_sk_load_8888_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,88                              // jne           3425 <_sk_load_8888_hsw+0x6d>
+  .byte  196,193,124,16,26                   // vmovups       (%r10),%ymm3
+  .byte  197,228,84,5,6,27,0,0               // vandps        0x1b06(%rip),%ymm3,%ymm0        # 4ee0 <_sk_callback_hsw+0x688>
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,5,177,24,0,0          // vbroadcastss  0x18b1(%rip),%ymm8        # 4c98 <_sk_callback_hsw+0x440>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,226,101,0,13,11,27,0,0          // vpshufb       0x1b0b(%rip),%ymm3,%ymm1        # 4f00 <_sk_callback_hsw+0x6a8>
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,226,101,0,21,25,27,0,0          // vpshufb       0x1b19(%rip),%ymm3,%ymm2        # 4f20 <_sk_callback_hsw+0x6c8>
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,229,114,211,24                  // vpsrld        $0x18,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,225,249,110,192                 // vmovq         %rax,%xmm0
+  .byte  196,226,125,33,192                  // vpmovsxbd     %xmm0,%ymm0
+  .byte  196,194,125,44,26                   // vmaskmovps    (%r10),%ymm0,%ymm3
+  .byte  235,135                             // jmp           33d2 <_sk_load_8888_hsw+0x1a>
+
+HIDDEN _sk_gather_8888_hsw
+.globl _sk_gather_8888_hsw
+FUNCTION(_sk_gather_8888_hsw)
+_sk_gather_8888_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  196,194,117,144,28,129              // vpgatherdd    %ymm1,(%r9,%ymm0,4),%ymm3
+  .byte  197,229,219,5,199,26,0,0            // vpand         0x1ac7(%rip),%ymm3,%ymm0        # 4f40 <_sk_callback_hsw+0x6e8>
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,5,22,24,0,0           // vbroadcastss  0x1816(%rip),%ymm8        # 4c9c <_sk_callback_hsw+0x444>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,226,101,0,13,204,26,0,0         // vpshufb       0x1acc(%rip),%ymm3,%ymm1        # 4f60 <_sk_callback_hsw+0x708>
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,226,101,0,21,218,26,0,0         // vpshufb       0x1ada(%rip),%ymm3,%ymm2        # 4f80 <_sk_callback_hsw+0x728>
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,229,114,211,24                  // vpsrld        $0x18,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_8888_hsw
+.globl _sk_store_8888_hsw
+FUNCTION(_sk_store_8888_hsw)
+_sk_store_8888_hsw:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  196,98,125,24,5,198,23,0,0          // vbroadcastss  0x17c6(%rip),%ymm8        # 4ca0 <_sk_callback_hsw+0x448>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,45,114,242,8                // vpslld        $0x8,%ymm10,%ymm10
+  .byte  196,65,45,235,201                   // vpor          %ymm9,%ymm10,%ymm9
+  .byte  196,65,108,89,208                   // vmulps        %ymm8,%ymm2,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,45,114,242,16               // vpslld        $0x10,%ymm10,%ymm10
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,193,61,114,240,24               // vpslld        $0x18,%ymm8,%ymm8
+  .byte  196,65,45,235,192                   // vpor          %ymm8,%ymm10,%ymm8
+  .byte  196,65,53,235,192                   // vpor          %ymm8,%ymm9,%ymm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,12                              // jne           3534 <_sk_store_8888_hsw+0x73>
+  .byte  196,65,124,17,2                     // vmovups       %ymm8,(%r10)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,97,249,110,200                  // vmovq         %rax,%xmm9
+  .byte  196,66,125,33,201                   // vpmovsxbd     %xmm9,%ymm9
+  .byte  196,66,53,46,2                      // vmaskmovps    %ymm8,%ymm9,(%r10)
+  .byte  235,211                             // jmp           352d <_sk_store_8888_hsw+0x6c>
+
+HIDDEN _sk_load_f16_hsw
+.globl _sk_load_f16_hsw
+FUNCTION(_sk_load_f16_hsw)
+_sk_load_f16_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,97                              // jne           35c5 <_sk_load_f16_hsw+0x6b>
+  .byte  197,121,16,4,208                    // vmovupd       (%rax,%rdx,8),%xmm8
+  .byte  197,249,16,84,208,16                // vmovupd       0x10(%rax,%rdx,8),%xmm2
+  .byte  197,249,16,92,208,32                // vmovupd       0x20(%rax,%rdx,8),%xmm3
+  .byte  197,122,111,76,208,48               // vmovdqu       0x30(%rax,%rdx,8),%xmm9
+  .byte  197,185,97,194                      // vpunpcklwd    %xmm2,%xmm8,%xmm0
+  .byte  197,185,105,210                     // vpunpckhwd    %xmm2,%xmm8,%xmm2
+  .byte  196,193,97,97,201                   // vpunpcklwd    %xmm9,%xmm3,%xmm1
+  .byte  196,193,97,105,217                  // vpunpckhwd    %xmm9,%xmm3,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,121,105,202                     // vpunpckhwd    %xmm2,%xmm0,%xmm9
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,241,105,219                     // vpunpckhwd    %xmm3,%xmm1,%xmm3
+  .byte  197,185,108,194                     // vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  .byte  196,226,125,19,192                  // vcvtph2ps     %xmm0,%ymm0
+  .byte  197,185,109,202                     // vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  .byte  196,226,125,19,201                  // vcvtph2ps     %xmm1,%ymm1
+  .byte  197,177,108,211                     // vpunpcklqdq   %xmm3,%xmm9,%xmm2
+  .byte  196,226,125,19,210                  // vcvtph2ps     %xmm2,%ymm2
+  .byte  197,177,109,219                     // vpunpckhqdq   %xmm3,%xmm9,%xmm3
+  .byte  196,226,125,19,219                  // vcvtph2ps     %xmm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,123,16,4,208                    // vmovsd        (%rax,%rdx,8),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,79                              // je            3624 <_sk_load_f16_hsw+0xca>
+  .byte  197,57,22,68,208,8                  // vmovhpd       0x8(%rax,%rdx,8),%xmm8,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,67                              // jb            3624 <_sk_load_f16_hsw+0xca>
+  .byte  197,251,16,84,208,16                // vmovsd        0x10(%rax,%rdx,8),%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  116,68                              // je            3631 <_sk_load_f16_hsw+0xd7>
+  .byte  197,233,22,84,208,24                // vmovhpd       0x18(%rax,%rdx,8),%xmm2,%xmm2
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,56                              // jb            3631 <_sk_load_f16_hsw+0xd7>
+  .byte  197,251,16,92,208,32                // vmovsd        0x20(%rax,%rdx,8),%xmm3
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  15,132,114,255,255,255              // je            357b <_sk_load_f16_hsw+0x21>
+  .byte  197,225,22,92,208,40                // vmovhpd       0x28(%rax,%rdx,8),%xmm3,%xmm3
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  15,130,98,255,255,255               // jb            357b <_sk_load_f16_hsw+0x21>
+  .byte  197,122,126,76,208,48               // vmovq         0x30(%rax,%rdx,8),%xmm9
+  .byte  233,87,255,255,255                  // jmpq          357b <_sk_load_f16_hsw+0x21>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  197,233,87,210                      // vxorpd        %xmm2,%xmm2,%xmm2
+  .byte  233,74,255,255,255                  // jmpq          357b <_sk_load_f16_hsw+0x21>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  233,65,255,255,255                  // jmpq          357b <_sk_load_f16_hsw+0x21>
+
+HIDDEN _sk_gather_f16_hsw
+.globl _sk_gather_f16_hsw
+FUNCTION(_sk_gather_f16_hsw)
+_sk_gather_f16_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,226,125,88,80,16                // vpbroadcastd  0x10(%rax),%ymm2
+  .byte  196,226,109,64,201                  // vpmulld       %ymm1,%ymm2,%ymm1
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  197,245,254,192                     // vpaddd        %ymm0,%ymm1,%ymm0
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  197,237,118,210                     // vpcmpeqd      %ymm2,%ymm2,%ymm2
+  .byte  196,194,237,144,28,193              // vpgatherdq    %ymm2,(%r9,%xmm0,8),%ymm3
+  .byte  196,227,125,57,192,1                // vextracti128  $0x1,%ymm0,%xmm0
+  .byte  196,194,245,144,20,193              // vpgatherdq    %ymm1,(%r9,%xmm0,8),%ymm2
+  .byte  196,227,125,57,216,1                // vextracti128  $0x1,%ymm3,%xmm0
+  .byte  196,227,125,57,209,1                // vextracti128  $0x1,%ymm2,%xmm1
+  .byte  197,97,97,192                       // vpunpcklwd    %xmm0,%xmm3,%xmm8
+  .byte  197,225,105,192                     // vpunpckhwd    %xmm0,%xmm3,%xmm0
+  .byte  197,233,97,217                      // vpunpcklwd    %xmm1,%xmm2,%xmm3
+  .byte  197,233,105,201                     // vpunpckhwd    %xmm1,%xmm2,%xmm1
+  .byte  197,57,97,200                       // vpunpcklwd    %xmm0,%xmm8,%xmm9
+  .byte  197,57,105,192                      // vpunpckhwd    %xmm0,%xmm8,%xmm8
+  .byte  197,225,97,209                      // vpunpcklwd    %xmm1,%xmm3,%xmm2
+  .byte  197,225,105,217                     // vpunpckhwd    %xmm1,%xmm3,%xmm3
+  .byte  197,177,108,194                     // vpunpcklqdq   %xmm2,%xmm9,%xmm0
+  .byte  196,226,125,19,192                  // vcvtph2ps     %xmm0,%ymm0
+  .byte  197,177,109,202                     // vpunpckhqdq   %xmm2,%xmm9,%xmm1
+  .byte  196,226,125,19,201                  // vcvtph2ps     %xmm1,%ymm1
+  .byte  197,185,108,211                     // vpunpcklqdq   %xmm3,%xmm8,%xmm2
+  .byte  196,226,125,19,210                  // vcvtph2ps     %xmm2,%ymm2
+  .byte  197,185,109,219                     // vpunpckhqdq   %xmm3,%xmm8,%xmm3
+  .byte  196,226,125,19,219                  // vcvtph2ps     %xmm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_f16_hsw
+.globl _sk_store_f16_hsw
+FUNCTION(_sk_store_f16_hsw)
+_sk_store_f16_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  196,195,125,29,192,4                // vcvtps2ph     $0x4,%ymm0,%xmm8
+  .byte  196,195,125,29,201,4                // vcvtps2ph     $0x4,%ymm1,%xmm9
+  .byte  196,195,125,29,210,4                // vcvtps2ph     $0x4,%ymm2,%xmm10
+  .byte  196,195,125,29,219,4                // vcvtps2ph     $0x4,%ymm3,%xmm11
+  .byte  196,65,57,97,225                    // vpunpcklwd    %xmm9,%xmm8,%xmm12
+  .byte  196,65,57,105,193                   // vpunpckhwd    %xmm9,%xmm8,%xmm8
+  .byte  196,65,41,97,203                    // vpunpcklwd    %xmm11,%xmm10,%xmm9
+  .byte  196,65,41,105,235                   // vpunpckhwd    %xmm11,%xmm10,%xmm13
+  .byte  196,65,25,98,217                    // vpunpckldq    %xmm9,%xmm12,%xmm11
+  .byte  196,65,25,106,209                   // vpunpckhdq    %xmm9,%xmm12,%xmm10
+  .byte  196,65,57,98,205                    // vpunpckldq    %xmm13,%xmm8,%xmm9
+  .byte  196,65,57,106,197                   // vpunpckhdq    %xmm13,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,27                              // jne           3729 <_sk_store_f16_hsw+0x65>
+  .byte  197,120,17,28,208                   // vmovups       %xmm11,(%rax,%rdx,8)
+  .byte  197,120,17,84,208,16                // vmovups       %xmm10,0x10(%rax,%rdx,8)
+  .byte  197,120,17,76,208,32                // vmovups       %xmm9,0x20(%rax,%rdx,8)
+  .byte  197,122,127,68,208,48               // vmovdqu       %xmm8,0x30(%rax,%rdx,8)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,121,214,28,208                  // vmovq         %xmm11,(%rax,%rdx,8)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,241                             // je            3725 <_sk_store_f16_hsw+0x61>
+  .byte  197,121,23,92,208,8                 // vmovhpd       %xmm11,0x8(%rax,%rdx,8)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,229                             // jb            3725 <_sk_store_f16_hsw+0x61>
+  .byte  197,121,214,84,208,16               // vmovq         %xmm10,0x10(%rax,%rdx,8)
+  .byte  116,221                             // je            3725 <_sk_store_f16_hsw+0x61>
+  .byte  197,121,23,84,208,24                // vmovhpd       %xmm10,0x18(%rax,%rdx,8)
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,209                             // jb            3725 <_sk_store_f16_hsw+0x61>
+  .byte  197,121,214,76,208,32               // vmovq         %xmm9,0x20(%rax,%rdx,8)
+  .byte  116,201                             // je            3725 <_sk_store_f16_hsw+0x61>
+  .byte  197,121,23,76,208,40                // vmovhpd       %xmm9,0x28(%rax,%rdx,8)
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,189                             // jb            3725 <_sk_store_f16_hsw+0x61>
+  .byte  197,121,214,68,208,48               // vmovq         %xmm8,0x30(%rax,%rdx,8)
+  .byte  235,181                             // jmp           3725 <_sk_store_f16_hsw+0x61>
+
+HIDDEN _sk_load_u16_be_hsw
+.globl _sk_load_u16_be_hsw
+FUNCTION(_sk_load_u16_be_hsw)
+_sk_load_u16_be_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,204,0,0,0                    // jne           3852 <_sk_load_u16_be_hsw+0xe2>
+  .byte  196,65,121,16,4,65                  // vmovupd       (%r9,%rax,2),%xmm8
+  .byte  196,193,121,16,84,65,16             // vmovupd       0x10(%r9,%rax,2),%xmm2
+  .byte  196,193,121,16,92,65,32             // vmovupd       0x20(%r9,%rax,2),%xmm3
+  .byte  196,65,122,111,76,65,48             // vmovdqu       0x30(%r9,%rax,2),%xmm9
+  .byte  197,185,97,194                      // vpunpcklwd    %xmm2,%xmm8,%xmm0
+  .byte  197,185,105,210                     // vpunpckhwd    %xmm2,%xmm8,%xmm2
+  .byte  196,193,97,97,201                   // vpunpcklwd    %xmm9,%xmm3,%xmm1
+  .byte  196,193,97,105,217                  // vpunpckhwd    %xmm9,%xmm3,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,121,105,202                     // vpunpckhwd    %xmm2,%xmm0,%xmm9
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,113,105,219                     // vpunpckhwd    %xmm3,%xmm1,%xmm11
+  .byte  197,185,108,194                     // vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  .byte  197,241,113,240,8                   // vpsllw        $0x8,%xmm0,%xmm1
+  .byte  197,249,113,208,8                   // vpsrlw        $0x8,%xmm0,%xmm0
+  .byte  197,241,235,192                     // vpor          %xmm0,%xmm1,%xmm0
+  .byte  196,226,125,51,192                  // vpmovzxwd     %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,21,189,20,0,0         // vbroadcastss  0x14bd(%rip),%ymm10        # 4ca4 <_sk_callback_hsw+0x44c>
+  .byte  196,193,124,89,194                  // vmulps        %ymm10,%ymm0,%ymm0
+  .byte  197,185,109,202                     // vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  .byte  197,233,113,241,8                   // vpsllw        $0x8,%xmm1,%xmm2
+  .byte  197,241,113,209,8                   // vpsrlw        $0x8,%xmm1,%xmm1
+  .byte  197,233,235,201                     // vpor          %xmm1,%xmm2,%xmm1
+  .byte  196,226,125,51,201                  // vpmovzxwd     %xmm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,202                  // vmulps        %ymm10,%ymm1,%ymm1
+  .byte  196,193,49,108,211                  // vpunpcklqdq   %xmm11,%xmm9,%xmm2
+  .byte  197,225,113,242,8                   // vpsllw        $0x8,%xmm2,%xmm3
+  .byte  197,233,113,210,8                   // vpsrlw        $0x8,%xmm2,%xmm2
+  .byte  197,225,235,210                     // vpor          %xmm2,%xmm3,%xmm2
+  .byte  196,226,125,51,210                  // vpmovzxwd     %xmm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,210                  // vmulps        %ymm10,%ymm2,%ymm2
+  .byte  196,193,49,109,219                  // vpunpckhqdq   %xmm11,%xmm9,%xmm3
+  .byte  197,185,113,243,8                   // vpsllw        $0x8,%xmm3,%xmm8
+  .byte  197,225,113,211,8                   // vpsrlw        $0x8,%xmm3,%xmm3
+  .byte  197,185,235,219                     // vpor          %xmm3,%xmm8,%xmm3
+  .byte  196,226,125,51,219                  // vpmovzxwd     %xmm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,218                  // vmulps        %ymm10,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,123,16,4,65                  // vmovsd        (%r9,%rax,2),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,85                              // je            38b8 <_sk_load_u16_be_hsw+0x148>
+  .byte  196,65,57,22,68,65,8                // vmovhpd       0x8(%r9,%rax,2),%xmm8,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,72                              // jb            38b8 <_sk_load_u16_be_hsw+0x148>
+  .byte  196,193,123,16,84,65,16             // vmovsd        0x10(%r9,%rax,2),%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  116,72                              // je            38c5 <_sk_load_u16_be_hsw+0x155>
+  .byte  196,193,105,22,84,65,24             // vmovhpd       0x18(%r9,%rax,2),%xmm2,%xmm2
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,59                              // jb            38c5 <_sk_load_u16_be_hsw+0x155>
+  .byte  196,193,123,16,92,65,32             // vmovsd        0x20(%r9,%rax,2),%xmm3
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  15,132,6,255,255,255                // je            37a1 <_sk_load_u16_be_hsw+0x31>
+  .byte  196,193,97,22,92,65,40              // vmovhpd       0x28(%r9,%rax,2),%xmm3,%xmm3
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  15,130,245,254,255,255              // jb            37a1 <_sk_load_u16_be_hsw+0x31>
+  .byte  196,65,122,126,76,65,48             // vmovq         0x30(%r9,%rax,2),%xmm9
+  .byte  233,233,254,255,255                 // jmpq          37a1 <_sk_load_u16_be_hsw+0x31>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  197,233,87,210                      // vxorpd        %xmm2,%xmm2,%xmm2
+  .byte  233,220,254,255,255                 // jmpq          37a1 <_sk_load_u16_be_hsw+0x31>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  233,211,254,255,255                 // jmpq          37a1 <_sk_load_u16_be_hsw+0x31>
+
+HIDDEN _sk_load_rgb_u16_be_hsw
+.globl _sk_load_rgb_u16_be_hsw
+FUNCTION(_sk_load_rgb_u16_be_hsw)
+_sk_load_rgb_u16_be_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,82                         // lea           (%rdx,%rdx,2),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,204,0,0,0                    // jne           39ac <_sk_load_rgb_u16_be_hsw+0xde>
+  .byte  196,193,122,111,4,65                // vmovdqu       (%r9,%rax,2),%xmm0
+  .byte  196,193,122,111,84,65,12            // vmovdqu       0xc(%r9,%rax,2),%xmm2
+  .byte  196,193,122,111,76,65,24            // vmovdqu       0x18(%r9,%rax,2),%xmm1
+  .byte  196,193,122,111,92,65,32            // vmovdqu       0x20(%r9,%rax,2),%xmm3
+  .byte  197,225,115,219,4                   // vpsrldq       $0x4,%xmm3,%xmm3
+  .byte  197,185,115,216,6                   // vpsrldq       $0x6,%xmm0,%xmm8
+  .byte  197,177,115,218,6                   // vpsrldq       $0x6,%xmm2,%xmm9
+  .byte  197,161,115,217,6                   // vpsrldq       $0x6,%xmm1,%xmm11
+  .byte  197,169,115,219,6                   // vpsrldq       $0x6,%xmm3,%xmm10
+  .byte  197,249,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm0
+  .byte  196,193,57,97,209                   // vpunpcklwd    %xmm9,%xmm8,%xmm2
+  .byte  197,241,97,203                      // vpunpcklwd    %xmm3,%xmm1,%xmm1
+  .byte  196,193,33,97,218                   // vpunpcklwd    %xmm10,%xmm11,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,121,105,202                     // vpunpckhwd    %xmm2,%xmm0,%xmm9
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,241,105,219                     // vpunpckhwd    %xmm3,%xmm1,%xmm3
+  .byte  197,185,108,194                     // vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  .byte  197,241,113,240,8                   // vpsllw        $0x8,%xmm0,%xmm1
+  .byte  197,249,113,208,8                   // vpsrlw        $0x8,%xmm0,%xmm0
+  .byte  197,241,235,192                     // vpor          %xmm0,%xmm1,%xmm0
+  .byte  196,226,125,51,192                  // vpmovzxwd     %xmm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,21,78,19,0,0          // vbroadcastss  0x134e(%rip),%ymm10        # 4ca8 <_sk_callback_hsw+0x450>
+  .byte  196,193,124,89,194                  // vmulps        %ymm10,%ymm0,%ymm0
+  .byte  197,185,109,202                     // vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  .byte  197,233,113,241,8                   // vpsllw        $0x8,%xmm1,%xmm2
+  .byte  197,241,113,209,8                   // vpsrlw        $0x8,%xmm1,%xmm1
+  .byte  197,233,235,201                     // vpor          %xmm1,%xmm2,%xmm1
+  .byte  196,226,125,51,201                  // vpmovzxwd     %xmm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,202                  // vmulps        %ymm10,%ymm1,%ymm1
+  .byte  197,177,108,211                     // vpunpcklqdq   %xmm3,%xmm9,%xmm2
+  .byte  197,225,113,242,8                   // vpsllw        $0x8,%xmm2,%xmm3
+  .byte  197,233,113,210,8                   // vpsrlw        $0x8,%xmm2,%xmm2
+  .byte  197,225,235,210                     // vpor          %xmm2,%xmm3,%xmm2
+  .byte  196,226,125,51,210                  // vpmovzxwd     %xmm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,210                  // vmulps        %ymm10,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,2,19,0,0          // vbroadcastss  0x1302(%rip),%ymm3        # 4cac <_sk_callback_hsw+0x454>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,193,121,110,4,65                // vmovd         (%r9,%rax,2),%xmm0
+  .byte  196,193,121,196,68,65,4,2           // vpinsrw       $0x2,0x4(%r9,%rax,2),%xmm0,%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,5                               // jne           39c5 <_sk_load_rgb_u16_be_hsw+0xf7>
+  .byte  233,79,255,255,255                  // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+  .byte  196,193,121,110,76,65,6             // vmovd         0x6(%r9,%rax,2),%xmm1
+  .byte  196,65,113,196,68,65,10,2           // vpinsrw       $0x2,0xa(%r9,%rax,2),%xmm1,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,26                              // jb            39f4 <_sk_load_rgb_u16_be_hsw+0x126>
+  .byte  196,193,121,110,76,65,12            // vmovd         0xc(%r9,%rax,2),%xmm1
+  .byte  196,193,113,196,84,65,16,2          // vpinsrw       $0x2,0x10(%r9,%rax,2),%xmm1,%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  117,10                              // jne           39f9 <_sk_load_rgb_u16_be_hsw+0x12b>
+  .byte  233,32,255,255,255                  // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+  .byte  233,27,255,255,255                  // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+  .byte  196,193,121,110,76,65,18            // vmovd         0x12(%r9,%rax,2),%xmm1
+  .byte  196,65,113,196,76,65,22,2           // vpinsrw       $0x2,0x16(%r9,%rax,2),%xmm1,%xmm9
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,26                              // jb            3a28 <_sk_load_rgb_u16_be_hsw+0x15a>
+  .byte  196,193,121,110,76,65,24            // vmovd         0x18(%r9,%rax,2),%xmm1
+  .byte  196,193,113,196,76,65,28,2          // vpinsrw       $0x2,0x1c(%r9,%rax,2),%xmm1,%xmm1
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  117,10                              // jne           3a2d <_sk_load_rgb_u16_be_hsw+0x15f>
+  .byte  233,236,254,255,255                 // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+  .byte  233,231,254,255,255                 // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+  .byte  196,193,121,110,92,65,30            // vmovd         0x1e(%r9,%rax,2),%xmm3
+  .byte  196,65,97,196,92,65,34,2            // vpinsrw       $0x2,0x22(%r9,%rax,2),%xmm3,%xmm11
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,20                              // jb            3a56 <_sk_load_rgb_u16_be_hsw+0x188>
+  .byte  196,193,121,110,92,65,36            // vmovd         0x24(%r9,%rax,2),%xmm3
+  .byte  196,193,97,196,92,65,40,2           // vpinsrw       $0x2,0x28(%r9,%rax,2),%xmm3,%xmm3
+  .byte  233,190,254,255,255                 // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+  .byte  233,185,254,255,255                 // jmpq          3914 <_sk_load_rgb_u16_be_hsw+0x46>
+
+HIDDEN _sk_store_u16_be_hsw
+.globl _sk_store_u16_be_hsw
+FUNCTION(_sk_store_u16_be_hsw)
+_sk_store_u16_be_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  196,98,125,24,5,63,18,0,0           // vbroadcastss  0x123f(%rip),%ymm8        # 4cb0 <_sk_callback_hsw+0x458>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,67,125,25,202,1                 // vextractf128  $0x1,%ymm9,%xmm10
+  .byte  196,66,49,43,202                    // vpackusdw     %xmm10,%xmm9,%xmm9
+  .byte  196,193,41,113,241,8                // vpsllw        $0x8,%xmm9,%xmm10
+  .byte  196,193,49,113,209,8                // vpsrlw        $0x8,%xmm9,%xmm9
+  .byte  196,65,41,235,201                   // vpor          %xmm9,%xmm10,%xmm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,67,125,25,211,1                 // vextractf128  $0x1,%ymm10,%xmm11
+  .byte  196,66,41,43,211                    // vpackusdw     %xmm11,%xmm10,%xmm10
+  .byte  196,193,33,113,242,8                // vpsllw        $0x8,%xmm10,%xmm11
+  .byte  196,193,41,113,210,8                // vpsrlw        $0x8,%xmm10,%xmm10
+  .byte  196,65,33,235,210                   // vpor          %xmm10,%xmm11,%xmm10
+  .byte  196,65,108,89,216                   // vmulps        %ymm8,%ymm2,%ymm11
+  .byte  196,65,125,91,219                   // vcvtps2dq     %ymm11,%ymm11
+  .byte  196,67,125,25,220,1                 // vextractf128  $0x1,%ymm11,%xmm12
+  .byte  196,66,33,43,220                    // vpackusdw     %xmm12,%xmm11,%xmm11
+  .byte  196,193,25,113,243,8                // vpsllw        $0x8,%xmm11,%xmm12
+  .byte  196,193,33,113,211,8                // vpsrlw        $0x8,%xmm11,%xmm11
+  .byte  196,65,25,235,219                   // vpor          %xmm11,%xmm12,%xmm11
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,67,125,25,196,1                 // vextractf128  $0x1,%ymm8,%xmm12
+  .byte  196,66,57,43,196                    // vpackusdw     %xmm12,%xmm8,%xmm8
+  .byte  196,193,25,113,240,8                // vpsllw        $0x8,%xmm8,%xmm12
+  .byte  196,193,57,113,208,8                // vpsrlw        $0x8,%xmm8,%xmm8
+  .byte  196,65,25,235,192                   // vpor          %xmm8,%xmm12,%xmm8
+  .byte  196,65,49,97,226                    // vpunpcklwd    %xmm10,%xmm9,%xmm12
+  .byte  196,65,49,105,234                   // vpunpckhwd    %xmm10,%xmm9,%xmm13
+  .byte  196,65,33,97,200                    // vpunpcklwd    %xmm8,%xmm11,%xmm9
+  .byte  196,65,33,105,192                   // vpunpckhwd    %xmm8,%xmm11,%xmm8
+  .byte  196,65,25,98,217                    // vpunpckldq    %xmm9,%xmm12,%xmm11
+  .byte  196,65,25,106,209                   // vpunpckhdq    %xmm9,%xmm12,%xmm10
+  .byte  196,65,17,98,200                    // vpunpckldq    %xmm8,%xmm13,%xmm9
+  .byte  196,65,17,106,192                   // vpunpckhdq    %xmm8,%xmm13,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,31                              // jne           3b55 <_sk_store_u16_be_hsw+0xfa>
+  .byte  196,65,120,17,28,65                 // vmovups       %xmm11,(%r9,%rax,2)
+  .byte  196,65,120,17,84,65,16              // vmovups       %xmm10,0x10(%r9,%rax,2)
+  .byte  196,65,120,17,76,65,32              // vmovups       %xmm9,0x20(%r9,%rax,2)
+  .byte  196,65,122,127,68,65,48             // vmovdqu       %xmm8,0x30(%r9,%rax,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,121,214,28,65                // vmovq         %xmm11,(%r9,%rax,2)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            3b51 <_sk_store_u16_be_hsw+0xf6>
+  .byte  196,65,121,23,92,65,8               // vmovhpd       %xmm11,0x8(%r9,%rax,2)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            3b51 <_sk_store_u16_be_hsw+0xf6>
+  .byte  196,65,121,214,84,65,16             // vmovq         %xmm10,0x10(%r9,%rax,2)
+  .byte  116,218                             // je            3b51 <_sk_store_u16_be_hsw+0xf6>
+  .byte  196,65,121,23,84,65,24              // vmovhpd       %xmm10,0x18(%r9,%rax,2)
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,205                             // jb            3b51 <_sk_store_u16_be_hsw+0xf6>
+  .byte  196,65,121,214,76,65,32             // vmovq         %xmm9,0x20(%r9,%rax,2)
+  .byte  116,196                             // je            3b51 <_sk_store_u16_be_hsw+0xf6>
+  .byte  196,65,121,23,76,65,40              // vmovhpd       %xmm9,0x28(%r9,%rax,2)
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,183                             // jb            3b51 <_sk_store_u16_be_hsw+0xf6>
+  .byte  196,65,121,214,68,65,48             // vmovq         %xmm8,0x30(%r9,%rax,2)
+  .byte  235,174                             // jmp           3b51 <_sk_store_u16_be_hsw+0xf6>
+
+HIDDEN _sk_load_f32_hsw
+.globl _sk_load_f32_hsw
+FUNCTION(_sk_load_f32_hsw)
+_sk_load_f32_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  119,110                             // ja            3c19 <_sk_load_f32_hsw+0x76>
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,141,29,135,0,0,0                 // lea           0x87(%rip),%r11        # 3c44 <_sk_load_f32_hsw+0xa1>
+  .byte  75,99,4,131                         // movslq        (%r11,%r8,4),%rax
+  .byte  76,1,216                            // add           %r11,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,3,125,24,68,145,112,1           // vinsertf128   $0x1,0x70(%r9,%r10,4),%ymm0,%ymm8
+  .byte  196,131,125,24,92,145,96,1          // vinsertf128   $0x1,0x60(%r9,%r10,4),%ymm0,%ymm3
+  .byte  196,131,125,24,76,145,80,1          // vinsertf128   $0x1,0x50(%r9,%r10,4),%ymm0,%ymm1
+  .byte  196,131,125,24,84,145,64,1          // vinsertf128   $0x1,0x40(%r9,%r10,4),%ymm0,%ymm2
+  .byte  196,129,121,16,68,145,48            // vmovupd       0x30(%r9,%r10,4),%xmm0
+  .byte  196,195,125,13,192,12               // vblendpd      $0xc,%ymm8,%ymm0,%ymm0
+  .byte  196,1,121,16,68,145,32              // vmovupd       0x20(%r9,%r10,4),%xmm8
+  .byte  196,99,61,13,203,12                 // vblendpd      $0xc,%ymm3,%ymm8,%ymm9
+  .byte  196,129,121,16,92,145,16            // vmovupd       0x10(%r9,%r10,4),%xmm3
+  .byte  196,99,101,13,209,12                // vblendpd      $0xc,%ymm1,%ymm3,%ymm10
+  .byte  196,129,121,16,12,145               // vmovupd       (%r9,%r10,4),%xmm1
+  .byte  196,227,117,13,202,12               // vblendpd      $0xc,%ymm2,%ymm1,%ymm1
+  .byte  196,193,116,20,210                  // vunpcklps     %ymm10,%ymm1,%ymm2
+  .byte  196,193,116,21,218                  // vunpckhps     %ymm10,%ymm1,%ymm3
+  .byte  197,180,20,200                      // vunpcklps     %ymm0,%ymm9,%ymm1
+  .byte  197,52,21,192                       // vunpckhps     %ymm0,%ymm9,%ymm8
+  .byte  197,237,20,193                      // vunpcklpd     %ymm1,%ymm2,%ymm0
+  .byte  197,237,21,201                      // vunpckhpd     %ymm1,%ymm2,%ymm1
+  .byte  196,193,101,20,208                  // vunpcklpd     %ymm8,%ymm3,%ymm2
+  .byte  196,193,101,21,216                  // vunpckhpd     %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  130                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,201                             // dec           %ecx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  188,255,255,255,175                 // mov           $0xafffffff,%esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,162,255,255,255,154             // jmpq          *-0x65000001(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,146,255,255,255,138             // callq         *-0x75000001(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_store_f32_hsw
+.globl _sk_store_f32_hsw
+FUNCTION(_sk_store_f32_hsw)
+_sk_store_f32_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  197,124,20,193                      // vunpcklps     %ymm1,%ymm0,%ymm8
+  .byte  197,124,21,217                      // vunpckhps     %ymm1,%ymm0,%ymm11
+  .byte  197,108,20,203                      // vunpcklps     %ymm3,%ymm2,%ymm9
+  .byte  197,108,21,227                      // vunpckhps     %ymm3,%ymm2,%ymm12
+  .byte  196,65,61,20,209                    // vunpcklpd     %ymm9,%ymm8,%ymm10
+  .byte  196,65,61,21,201                    // vunpckhpd     %ymm9,%ymm8,%ymm9
+  .byte  196,65,37,20,196                    // vunpcklpd     %ymm12,%ymm11,%ymm8
+  .byte  196,65,37,21,220                    // vunpckhpd     %ymm12,%ymm11,%ymm11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,55                              // jne           3cd1 <_sk_store_f32_hsw+0x6d>
+  .byte  196,67,45,24,225,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
+  .byte  196,67,61,24,235,1                  // vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
+  .byte  196,67,45,6,201,49                  // vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
+  .byte  196,67,61,6,195,49                  // vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
+  .byte  196,65,125,17,36,129                // vmovupd       %ymm12,(%r9,%rax,4)
+  .byte  196,65,125,17,108,129,32            // vmovupd       %ymm13,0x20(%r9,%rax,4)
+  .byte  196,65,125,17,76,129,64             // vmovupd       %ymm9,0x40(%r9,%rax,4)
+  .byte  196,65,125,17,68,129,96             // vmovupd       %ymm8,0x60(%r9,%rax,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,121,17,20,129                // vmovupd       %xmm10,(%r9,%rax,4)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            3ccd <_sk_store_f32_hsw+0x69>
+  .byte  196,65,121,17,76,129,16             // vmovupd       %xmm9,0x10(%r9,%rax,4)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            3ccd <_sk_store_f32_hsw+0x69>
+  .byte  196,65,121,17,68,129,32             // vmovupd       %xmm8,0x20(%r9,%rax,4)
+  .byte  116,218                             // je            3ccd <_sk_store_f32_hsw+0x69>
+  .byte  196,65,121,17,92,129,48             // vmovupd       %xmm11,0x30(%r9,%rax,4)
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,205                             // jb            3ccd <_sk_store_f32_hsw+0x69>
+  .byte  196,67,125,25,84,129,64,1           // vextractf128  $0x1,%ymm10,0x40(%r9,%rax,4)
+  .byte  116,195                             // je            3ccd <_sk_store_f32_hsw+0x69>
+  .byte  196,67,125,25,76,129,80,1           // vextractf128  $0x1,%ymm9,0x50(%r9,%rax,4)
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,181                             // jb            3ccd <_sk_store_f32_hsw+0x69>
+  .byte  196,67,125,25,68,129,96,1           // vextractf128  $0x1,%ymm8,0x60(%r9,%rax,4)
+  .byte  235,171                             // jmp           3ccd <_sk_store_f32_hsw+0x69>
+
+HIDDEN _sk_clamp_x_hsw
+.globl _sk_clamp_x_hsw
+FUNCTION(_sk_clamp_x_hsw)
+_sk_clamp_x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,192                      // vmaxps        %ymm0,%ymm8,%ymm0
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_y_hsw
+.globl _sk_clamp_y_hsw
+FUNCTION(_sk_clamp_y_hsw)
+_sk_clamp_y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,201                      // vmaxps        %ymm1,%ymm8,%ymm1
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,193,116,93,200                  // vminps        %ymm8,%ymm1,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_hsw
+.globl _sk_repeat_x_hsw
+FUNCTION(_sk_repeat_x_hsw)
+_sk_repeat_x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,8                     // vbroadcastss  (%rax),%ymm9
+  .byte  196,65,124,94,193                   // vdivps        %ymm9,%ymm0,%ymm8
+  .byte  196,67,125,8,192,1                  // vroundps      $0x1,%ymm8,%ymm8
+  .byte  196,98,53,172,192                   // vfnmadd213ps  %ymm0,%ymm9,%ymm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_y_hsw
+.globl _sk_repeat_y_hsw
+FUNCTION(_sk_repeat_y_hsw)
+_sk_repeat_y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,8                     // vbroadcastss  (%rax),%ymm9
+  .byte  196,65,116,94,193                   // vdivps        %ymm9,%ymm1,%ymm8
+  .byte  196,67,125,8,192,1                  // vroundps      $0x1,%ymm8,%ymm8
+  .byte  196,98,53,172,193                   // vfnmadd213ps  %ymm1,%ymm9,%ymm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,193                      // vmovaps       %ymm8,%ymm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_hsw
+.globl _sk_mirror_x_hsw
+FUNCTION(_sk_mirror_x_hsw)
+_sk_mirror_x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,122,16,0                        // vmovss        (%rax),%xmm8
+  .byte  196,66,125,24,200                   // vbroadcastss  %xmm8,%ymm9
+  .byte  196,65,124,92,209                   // vsubps        %ymm9,%ymm0,%ymm10
+  .byte  196,193,58,88,192                   // vaddss        %xmm8,%xmm8,%xmm0
+  .byte  196,226,125,24,192                  // vbroadcastss  %xmm0,%ymm0
+  .byte  197,44,94,192                       // vdivps        %ymm0,%ymm10,%ymm8
+  .byte  196,67,125,8,192,1                  // vroundps      $0x1,%ymm8,%ymm8
+  .byte  196,66,125,172,194                  // vfnmadd213ps  %ymm10,%ymm0,%ymm8
+  .byte  196,193,60,92,193                   // vsubps        %ymm9,%ymm8,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,192                       // vsubps        %ymm0,%ymm8,%ymm8
+  .byte  197,188,84,192                      // vandps        %ymm0,%ymm8,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_y_hsw
+.globl _sk_mirror_y_hsw
+FUNCTION(_sk_mirror_y_hsw)
+_sk_mirror_y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,122,16,0                        // vmovss        (%rax),%xmm8
+  .byte  196,66,125,24,200                   // vbroadcastss  %xmm8,%ymm9
+  .byte  196,65,116,92,209                   // vsubps        %ymm9,%ymm1,%ymm10
+  .byte  196,193,58,88,200                   // vaddss        %xmm8,%xmm8,%xmm1
+  .byte  196,226,125,24,201                  // vbroadcastss  %xmm1,%ymm1
+  .byte  197,44,94,193                       // vdivps        %ymm1,%ymm10,%ymm8
+  .byte  196,67,125,8,192,1                  // vroundps      $0x1,%ymm8,%ymm8
+  .byte  196,66,117,172,194                  // vfnmadd213ps  %ymm10,%ymm1,%ymm8
+  .byte  196,193,60,92,201                   // vsubps        %ymm9,%ymm8,%ymm1
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,193                       // vsubps        %ymm1,%ymm8,%ymm8
+  .byte  197,188,84,201                      // vandps        %ymm1,%ymm8,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_x_1_hsw
+.globl _sk_clamp_x_1_hsw
+FUNCTION(_sk_clamp_x_1_hsw)
+_sk_clamp_x_1_hsw:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,192                      // vmaxps        %ymm0,%ymm8,%ymm0
+  .byte  196,98,125,24,5,146,14,0,0          // vbroadcastss  0xe92(%rip),%ymm8        # 4cb4 <_sk_callback_hsw+0x45c>
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_1_hsw
+.globl _sk_repeat_x_1_hsw
+FUNCTION(_sk_repeat_x_1_hsw)
+_sk_repeat_x_1_hsw:
+  .byte  196,99,125,8,192,1                  // vroundps      $0x1,%ymm0,%ymm8
+  .byte  196,193,124,92,192                  // vsubps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_1_hsw
+.globl _sk_mirror_x_1_hsw
+FUNCTION(_sk_mirror_x_1_hsw)
+_sk_mirror_x_1_hsw:
+  .byte  196,98,125,24,5,117,14,0,0          // vbroadcastss  0xe75(%rip),%ymm8        # 4cb8 <_sk_callback_hsw+0x460>
+  .byte  196,193,124,88,192                  // vaddps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,13,107,14,0,0         // vbroadcastss  0xe6b(%rip),%ymm9        # 4cbc <_sk_callback_hsw+0x464>
+  .byte  196,65,124,89,201                   // vmulps        %ymm9,%ymm0,%ymm9
+  .byte  196,67,125,8,201,1                  // vroundps      $0x1,%ymm9,%ymm9
+  .byte  196,65,52,88,201                    // vaddps        %ymm9,%ymm9,%ymm9
+  .byte  196,193,124,92,193                  // vsubps        %ymm9,%ymm0,%ymm0
+  .byte  196,193,124,88,192                  // vaddps        %ymm8,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,192                       // vsubps        %ymm0,%ymm8,%ymm8
+  .byte  197,188,84,192                      // vandps        %ymm0,%ymm8,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminance_to_alpha_hsw
+.globl _sk_luminance_to_alpha_hsw
+FUNCTION(_sk_luminance_to_alpha_hsw)
+_sk_luminance_to_alpha_hsw:
+  .byte  196,226,125,24,29,59,14,0,0         // vbroadcastss  0xe3b(%rip),%ymm3        # 4cc0 <_sk_callback_hsw+0x468>
+  .byte  196,98,125,24,5,54,14,0,0           // vbroadcastss  0xe36(%rip),%ymm8        # 4cc4 <_sk_callback_hsw+0x46c>
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,226,125,184,203                 // vfmadd231ps   %ymm3,%ymm0,%ymm1
+  .byte  196,226,125,24,29,39,14,0,0         // vbroadcastss  0xe27(%rip),%ymm3        # 4cc8 <_sk_callback_hsw+0x470>
+  .byte  196,226,109,168,217                 // vfmadd213ps   %ymm1,%ymm2,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_2x3_hsw
+.globl _sk_matrix_2x3_hsw
+FUNCTION(_sk_matrix_2x3_hsw)
+_sk_matrix_2x3_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,8                     // vbroadcastss  (%rax),%ymm9
+  .byte  196,98,125,24,80,8                  // vbroadcastss  0x8(%rax),%ymm10
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,66,117,184,194                  // vfmadd231ps   %ymm10,%ymm1,%ymm8
+  .byte  196,66,125,184,193                  // vfmadd231ps   %ymm9,%ymm0,%ymm8
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,12                 // vbroadcastss  0xc(%rax),%ymm11
+  .byte  196,98,125,24,72,20                 // vbroadcastss  0x14(%rax),%ymm9
+  .byte  196,66,117,184,203                  // vfmadd231ps   %ymm11,%ymm1,%ymm9
+  .byte  196,66,125,184,202                  // vfmadd231ps   %ymm10,%ymm0,%ymm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_3x4_hsw
+.globl _sk_matrix_3x4_hsw
+FUNCTION(_sk_matrix_3x4_hsw)
+_sk_matrix_3x4_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,8                     // vbroadcastss  (%rax),%ymm9
+  .byte  196,98,125,24,80,12                 // vbroadcastss  0xc(%rax),%ymm10
+  .byte  196,98,125,24,88,24                 // vbroadcastss  0x18(%rax),%ymm11
+  .byte  196,98,125,24,64,36                 // vbroadcastss  0x24(%rax),%ymm8
+  .byte  196,66,109,184,195                  // vfmadd231ps   %ymm11,%ymm2,%ymm8
+  .byte  196,66,117,184,194                  // vfmadd231ps   %ymm10,%ymm1,%ymm8
+  .byte  196,66,125,184,193                  // vfmadd231ps   %ymm9,%ymm0,%ymm8
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,16                 // vbroadcastss  0x10(%rax),%ymm11
+  .byte  196,98,125,24,96,28                 // vbroadcastss  0x1c(%rax),%ymm12
+  .byte  196,98,125,24,72,40                 // vbroadcastss  0x28(%rax),%ymm9
+  .byte  196,66,109,184,204                  // vfmadd231ps   %ymm12,%ymm2,%ymm9
+  .byte  196,66,117,184,203                  // vfmadd231ps   %ymm11,%ymm1,%ymm9
+  .byte  196,66,125,184,202                  // vfmadd231ps   %ymm10,%ymm0,%ymm9
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  196,98,125,24,96,20                 // vbroadcastss  0x14(%rax),%ymm12
+  .byte  196,98,125,24,104,32                // vbroadcastss  0x20(%rax),%ymm13
+  .byte  196,98,125,24,80,44                 // vbroadcastss  0x2c(%rax),%ymm10
+  .byte  196,66,109,184,213                  // vfmadd231ps   %ymm13,%ymm2,%ymm10
+  .byte  196,66,117,184,212                  // vfmadd231ps   %ymm12,%ymm1,%ymm10
+  .byte  196,66,125,184,211                  // vfmadd231ps   %ymm11,%ymm0,%ymm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  197,124,41,210                      // vmovaps       %ymm10,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x5_hsw
+.globl _sk_matrix_4x5_hsw
+FUNCTION(_sk_matrix_4x5_hsw)
+_sk_matrix_4x5_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,8                     // vbroadcastss  (%rax),%ymm9
+  .byte  196,98,125,24,80,16                 // vbroadcastss  0x10(%rax),%ymm10
+  .byte  196,98,125,24,88,32                 // vbroadcastss  0x20(%rax),%ymm11
+  .byte  196,98,125,24,96,48                 // vbroadcastss  0x30(%rax),%ymm12
+  .byte  196,98,125,24,64,64                 // vbroadcastss  0x40(%rax),%ymm8
+  .byte  196,66,101,184,196                  // vfmadd231ps   %ymm12,%ymm3,%ymm8
+  .byte  196,66,109,184,195                  // vfmadd231ps   %ymm11,%ymm2,%ymm8
+  .byte  196,66,117,184,194                  // vfmadd231ps   %ymm10,%ymm1,%ymm8
+  .byte  196,66,125,184,193                  // vfmadd231ps   %ymm9,%ymm0,%ymm8
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,20                 // vbroadcastss  0x14(%rax),%ymm11
+  .byte  196,98,125,24,96,36                 // vbroadcastss  0x24(%rax),%ymm12
+  .byte  196,98,125,24,104,52                // vbroadcastss  0x34(%rax),%ymm13
+  .byte  196,98,125,24,72,68                 // vbroadcastss  0x44(%rax),%ymm9
+  .byte  196,66,101,184,205                  // vfmadd231ps   %ymm13,%ymm3,%ymm9
+  .byte  196,66,109,184,204                  // vfmadd231ps   %ymm12,%ymm2,%ymm9
+  .byte  196,66,117,184,203                  // vfmadd231ps   %ymm11,%ymm1,%ymm9
+  .byte  196,66,125,184,202                  // vfmadd231ps   %ymm10,%ymm0,%ymm9
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  196,98,125,24,96,24                 // vbroadcastss  0x18(%rax),%ymm12
+  .byte  196,98,125,24,104,40                // vbroadcastss  0x28(%rax),%ymm13
+  .byte  196,98,125,24,112,56                // vbroadcastss  0x38(%rax),%ymm14
+  .byte  196,98,125,24,80,72                 // vbroadcastss  0x48(%rax),%ymm10
+  .byte  196,66,101,184,214                  // vfmadd231ps   %ymm14,%ymm3,%ymm10
+  .byte  196,66,109,184,213                  // vfmadd231ps   %ymm13,%ymm2,%ymm10
+  .byte  196,66,117,184,212                  // vfmadd231ps   %ymm12,%ymm1,%ymm10
+  .byte  196,66,125,184,211                  // vfmadd231ps   %ymm11,%ymm0,%ymm10
+  .byte  196,98,125,24,96,12                 // vbroadcastss  0xc(%rax),%ymm12
+  .byte  196,98,125,24,104,28                // vbroadcastss  0x1c(%rax),%ymm13
+  .byte  196,98,125,24,112,44                // vbroadcastss  0x2c(%rax),%ymm14
+  .byte  196,98,125,24,120,60                // vbroadcastss  0x3c(%rax),%ymm15
+  .byte  196,98,125,24,88,76                 // vbroadcastss  0x4c(%rax),%ymm11
+  .byte  196,66,101,184,223                  // vfmadd231ps   %ymm15,%ymm3,%ymm11
+  .byte  196,66,109,184,222                  // vfmadd231ps   %ymm14,%ymm2,%ymm11
+  .byte  196,66,117,184,221                  // vfmadd231ps   %ymm13,%ymm1,%ymm11
+  .byte  196,66,125,184,220                  // vfmadd231ps   %ymm12,%ymm0,%ymm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  197,124,41,210                      // vmovaps       %ymm10,%ymm2
+  .byte  197,124,41,219                      // vmovaps       %ymm11,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x3_hsw
+.globl _sk_matrix_4x3_hsw
+FUNCTION(_sk_matrix_4x3_hsw)
+_sk_matrix_4x3_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,16                   // vbroadcastss  (%rax),%ymm2
+  .byte  196,226,125,24,88,16                // vbroadcastss  0x10(%rax),%ymm3
+  .byte  196,98,125,24,64,32                 // vbroadcastss  0x20(%rax),%ymm8
+  .byte  196,98,117,184,195                  // vfmadd231ps   %ymm3,%ymm1,%ymm8
+  .byte  196,98,125,184,194                  // vfmadd231ps   %ymm2,%ymm0,%ymm8
+  .byte  196,226,125,24,80,4                 // vbroadcastss  0x4(%rax),%ymm2
+  .byte  196,226,125,24,88,20                // vbroadcastss  0x14(%rax),%ymm3
+  .byte  196,98,125,24,72,36                 // vbroadcastss  0x24(%rax),%ymm9
+  .byte  196,98,117,184,203                  // vfmadd231ps   %ymm3,%ymm1,%ymm9
+  .byte  196,98,125,184,202                  // vfmadd231ps   %ymm2,%ymm0,%ymm9
+  .byte  196,226,125,24,88,8                 // vbroadcastss  0x8(%rax),%ymm3
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,226,125,24,80,40                // vbroadcastss  0x28(%rax),%ymm2
+  .byte  196,194,117,184,210                 // vfmadd231ps   %ymm10,%ymm1,%ymm2
+  .byte  196,226,125,184,211                 // vfmadd231ps   %ymm3,%ymm0,%ymm2
+  .byte  196,98,125,24,80,12                 // vbroadcastss  0xc(%rax),%ymm10
+  .byte  196,98,125,24,88,28                 // vbroadcastss  0x1c(%rax),%ymm11
+  .byte  196,226,125,24,88,44                // vbroadcastss  0x2c(%rax),%ymm3
+  .byte  196,194,117,184,219                 // vfmadd231ps   %ymm11,%ymm1,%ymm3
+  .byte  196,194,125,184,218                 // vfmadd231ps   %ymm10,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_perspective_hsw
+.globl _sk_matrix_perspective_hsw
+FUNCTION(_sk_matrix_perspective_hsw)
+_sk_matrix_perspective_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,98,125,24,72,4                  // vbroadcastss  0x4(%rax),%ymm9
+  .byte  196,98,125,24,80,8                  // vbroadcastss  0x8(%rax),%ymm10
+  .byte  196,66,117,184,209                  // vfmadd231ps   %ymm9,%ymm1,%ymm10
+  .byte  196,66,125,184,208                  // vfmadd231ps   %ymm8,%ymm0,%ymm10
+  .byte  196,98,125,24,64,12                 // vbroadcastss  0xc(%rax),%ymm8
+  .byte  196,98,125,24,72,16                 // vbroadcastss  0x10(%rax),%ymm9
+  .byte  196,98,125,24,88,20                 // vbroadcastss  0x14(%rax),%ymm11
+  .byte  196,66,117,184,217                  // vfmadd231ps   %ymm9,%ymm1,%ymm11
+  .byte  196,66,125,184,216                  // vfmadd231ps   %ymm8,%ymm0,%ymm11
+  .byte  196,98,125,24,64,24                 // vbroadcastss  0x18(%rax),%ymm8
+  .byte  196,98,125,24,72,28                 // vbroadcastss  0x1c(%rax),%ymm9
+  .byte  196,98,125,24,96,32                 // vbroadcastss  0x20(%rax),%ymm12
+  .byte  196,66,117,184,225                  // vfmadd231ps   %ymm9,%ymm1,%ymm12
+  .byte  196,66,125,184,224                  // vfmadd231ps   %ymm8,%ymm0,%ymm12
+  .byte  196,193,124,83,204                  // vrcpps        %ymm12,%ymm1
+  .byte  197,172,89,193                      // vmulps        %ymm1,%ymm10,%ymm0
+  .byte  197,164,89,201                      // vmulps        %ymm1,%ymm11,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_gradient_hsw
+.globl _sk_evenly_spaced_gradient_hsw
+FUNCTION(_sk_evenly_spaced_gradient_hsw)
+_sk_evenly_spaced_gradient_hsw:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  77,137,211                          // mov           %r10,%r11
+  .byte  73,255,203                          // dec           %r11
+  .byte  120,7                               // js            415a <_sk_evenly_spaced_gradient_hsw+0x19>
+  .byte  196,193,242,42,203                  // vcvtsi2ss     %r11,%xmm1,%xmm1
+  .byte  235,22                              // jmp           4170 <_sk_evenly_spaced_gradient_hsw+0x2f>
+  .byte  76,137,219                          // mov           %r11,%rbx
+  .byte  72,209,235                          // shr           %rbx
+  .byte  65,131,227,1                        // and           $0x1,%r11d
+  .byte  73,9,219                            // or            %rbx,%r11
+  .byte  196,193,242,42,203                  // vcvtsi2ss     %r11,%xmm1,%xmm1
+  .byte  197,242,88,201                      // vaddss        %xmm1,%xmm1,%xmm1
+  .byte  196,226,125,24,201                  // vbroadcastss  %xmm1,%ymm1
+  .byte  197,244,89,200                      // vmulps        %ymm0,%ymm1,%ymm1
+  .byte  197,126,91,217                      // vcvttps2dq    %ymm1,%ymm11
+  .byte  73,131,250,8                        // cmp           $0x8,%r10
+  .byte  119,70                              // ja            41c9 <_sk_evenly_spaced_gradient_hsw+0x88>
+  .byte  196,66,37,22,1                      // vpermps       (%r9),%ymm11,%ymm8
+  .byte  72,139,88,40                        // mov           0x28(%rax),%rbx
+  .byte  196,98,37,22,11                     // vpermps       (%rbx),%ymm11,%ymm9
+  .byte  72,139,88,16                        // mov           0x10(%rax),%rbx
+  .byte  76,139,72,24                        // mov           0x18(%rax),%r9
+  .byte  196,226,37,22,11                    // vpermps       (%rbx),%ymm11,%ymm1
+  .byte  72,139,88,48                        // mov           0x30(%rax),%rbx
+  .byte  196,98,37,22,19                     // vpermps       (%rbx),%ymm11,%ymm10
+  .byte  196,194,37,22,17                    // vpermps       (%r9),%ymm11,%ymm2
+  .byte  72,139,88,56                        // mov           0x38(%rax),%rbx
+  .byte  196,98,37,22,35                     // vpermps       (%rbx),%ymm11,%ymm12
+  .byte  72,139,88,32                        // mov           0x20(%rax),%rbx
+  .byte  196,226,37,22,27                    // vpermps       (%rbx),%ymm11,%ymm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  196,98,37,22,40                     // vpermps       (%rax),%ymm11,%ymm13
+  .byte  235,110                             // jmp           4237 <_sk_evenly_spaced_gradient_hsw+0xf6>
+  .byte  196,65,13,118,246                   // vpcmpeqd      %ymm14,%ymm14,%ymm14
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  196,2,117,146,4,153                 // vgatherdps    %ymm1,(%r9,%ymm11,4),%ymm8
+  .byte  72,139,88,40                        // mov           0x28(%rax),%rbx
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  196,34,117,146,12,155               // vgatherdps    %ymm1,(%rbx,%ymm11,4),%ymm9
+  .byte  72,139,88,16                        // mov           0x10(%rax),%rbx
+  .byte  76,139,72,24                        // mov           0x18(%rax),%r9
+  .byte  197,237,118,210                     // vpcmpeqd      %ymm2,%ymm2,%ymm2
+  .byte  196,162,109,146,12,155              // vgatherdps    %ymm2,(%rbx,%ymm11,4),%ymm1
+  .byte  72,139,88,48                        // mov           0x30(%rax),%rbx
+  .byte  197,237,118,210                     // vpcmpeqd      %ymm2,%ymm2,%ymm2
+  .byte  196,34,109,146,20,155               // vgatherdps    %ymm2,(%rbx,%ymm11,4),%ymm10
+  .byte  197,229,118,219                     // vpcmpeqd      %ymm3,%ymm3,%ymm3
+  .byte  196,130,101,146,20,153              // vgatherdps    %ymm3,(%r9,%ymm11,4),%ymm2
+  .byte  72,139,88,56                        // mov           0x38(%rax),%rbx
+  .byte  197,229,118,219                     // vpcmpeqd      %ymm3,%ymm3,%ymm3
+  .byte  196,34,101,146,36,155               // vgatherdps    %ymm3,(%rbx,%ymm11,4),%ymm12
+  .byte  72,139,88,32                        // mov           0x20(%rax),%rbx
+  .byte  196,65,21,118,237                   // vpcmpeqd      %ymm13,%ymm13,%ymm13
+  .byte  196,162,21,146,28,155               // vgatherdps    %ymm13,(%rbx,%ymm11,4),%ymm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  196,34,13,146,44,152                // vgatherdps    %ymm14,(%rax,%ymm11,4),%ymm13
+  .byte  196,66,125,168,193                  // vfmadd213ps   %ymm9,%ymm0,%ymm8
+  .byte  196,194,125,168,202                 // vfmadd213ps   %ymm10,%ymm0,%ymm1
+  .byte  196,194,125,168,212                 // vfmadd213ps   %ymm12,%ymm0,%ymm2
+  .byte  196,194,125,168,221                 // vfmadd213ps   %ymm13,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gauss_a_to_rgba_hsw
+.globl _sk_gauss_a_to_rgba_hsw
+FUNCTION(_sk_gauss_a_to_rgba_hsw)
+_sk_gauss_a_to_rgba_hsw:
+  .byte  196,226,125,24,5,111,10,0,0         // vbroadcastss  0xa6f(%rip),%ymm0        # 4ccc <_sk_callback_hsw+0x474>
+  .byte  196,226,125,24,13,106,10,0,0        // vbroadcastss  0xa6a(%rip),%ymm1        # 4cd0 <_sk_callback_hsw+0x478>
+  .byte  196,226,101,168,200                 // vfmadd213ps   %ymm0,%ymm3,%ymm1
+  .byte  196,226,125,24,5,96,10,0,0          // vbroadcastss  0xa60(%rip),%ymm0        # 4cd4 <_sk_callback_hsw+0x47c>
+  .byte  196,226,101,184,193                 // vfmadd231ps   %ymm1,%ymm3,%ymm0
+  .byte  196,226,125,24,13,86,10,0,0         // vbroadcastss  0xa56(%rip),%ymm1        # 4cd8 <_sk_callback_hsw+0x480>
+  .byte  196,226,101,184,200                 // vfmadd231ps   %ymm0,%ymm3,%ymm1
+  .byte  196,226,125,24,5,76,10,0,0          // vbroadcastss  0xa4c(%rip),%ymm0        # 4cdc <_sk_callback_hsw+0x484>
+  .byte  196,226,101,184,193                 // vfmadd231ps   %ymm1,%ymm3,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,252,40,216                      // vmovaps       %ymm0,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gradient_hsw
+.globl _sk_gradient_hsw
+FUNCTION(_sk_gradient_hsw)
+_sk_gradient_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  73,131,249,1                        // cmp           $0x1,%r9
+  .byte  15,134,180,0,0,0                    // jbe           4368 <_sk_gradient_hsw+0xc3>
+  .byte  76,139,80,72                        // mov           0x48(%rax),%r10
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  65,187,1,0,0,0                      // mov           $0x1,%r11d
+  .byte  196,226,125,24,21,21,10,0,0         // vbroadcastss  0xa15(%rip),%ymm2        # 4ce0 <_sk_callback_hsw+0x488>
+  .byte  196,65,53,239,201                   // vpxor         %ymm9,%ymm9,%ymm9
+  .byte  196,130,125,24,28,154               // vbroadcastss  (%r10,%r11,4),%ymm3
+  .byte  197,228,194,216,2                   // vcmpleps      %ymm0,%ymm3,%ymm3
+  .byte  196,227,117,74,218,48               // vblendvps     %ymm3,%ymm2,%ymm1,%ymm3
+  .byte  196,65,101,254,201                  // vpaddd        %ymm9,%ymm3,%ymm9
+  .byte  73,255,195                          // inc           %r11
+  .byte  77,57,217                           // cmp           %r11,%r9
+  .byte  117,226                             // jne           42d0 <_sk_gradient_hsw+0x2b>
+  .byte  76,139,80,8                         // mov           0x8(%rax),%r10
+  .byte  73,131,249,8                        // cmp           $0x8,%r9
+  .byte  118,121                             // jbe           4371 <_sk_gradient_hsw+0xcc>
+  .byte  196,65,13,118,246                   // vpcmpeqd      %ymm14,%ymm14,%ymm14
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  196,2,117,146,4,138                 // vgatherdps    %ymm1,(%r10,%ymm9,4),%ymm8
+  .byte  76,139,72,40                        // mov           0x28(%rax),%r9
+  .byte  197,245,118,201                     // vpcmpeqd      %ymm1,%ymm1,%ymm1
+  .byte  196,2,117,146,20,137                // vgatherdps    %ymm1,(%r9,%ymm9,4),%ymm10
+  .byte  76,139,72,16                        // mov           0x10(%rax),%r9
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  197,237,118,210                     // vpcmpeqd      %ymm2,%ymm2,%ymm2
+  .byte  196,130,109,146,12,137              // vgatherdps    %ymm2,(%r9,%ymm9,4),%ymm1
+  .byte  76,139,72,48                        // mov           0x30(%rax),%r9
+  .byte  197,237,118,210                     // vpcmpeqd      %ymm2,%ymm2,%ymm2
+  .byte  196,2,109,146,28,137                // vgatherdps    %ymm2,(%r9,%ymm9,4),%ymm11
+  .byte  197,229,118,219                     // vpcmpeqd      %ymm3,%ymm3,%ymm3
+  .byte  196,130,101,146,20,138              // vgatherdps    %ymm3,(%r10,%ymm9,4),%ymm2
+  .byte  76,139,72,56                        // mov           0x38(%rax),%r9
+  .byte  197,229,118,219                     // vpcmpeqd      %ymm3,%ymm3,%ymm3
+  .byte  196,2,101,146,36,137                // vgatherdps    %ymm3,(%r9,%ymm9,4),%ymm12
+  .byte  76,139,72,32                        // mov           0x20(%rax),%r9
+  .byte  196,65,21,118,237                   // vpcmpeqd      %ymm13,%ymm13,%ymm13
+  .byte  196,130,21,146,28,137               // vgatherdps    %ymm13,(%r9,%ymm9,4),%ymm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  196,34,13,146,44,136                // vgatherdps    %ymm14,(%rax,%ymm9,4),%ymm13
+  .byte  235,77                              // jmp           43b5 <_sk_gradient_hsw+0x110>
+  .byte  76,139,80,8                         // mov           0x8(%rax),%r10
+  .byte  196,65,52,87,201                    // vxorps        %ymm9,%ymm9,%ymm9
+  .byte  196,66,53,22,2                      // vpermps       (%r10),%ymm9,%ymm8
+  .byte  76,139,72,40                        // mov           0x28(%rax),%r9
+  .byte  196,66,53,22,17                     // vpermps       (%r9),%ymm9,%ymm10
+  .byte  76,139,72,16                        // mov           0x10(%rax),%r9
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  196,194,53,22,9                     // vpermps       (%r9),%ymm9,%ymm1
+  .byte  76,139,72,48                        // mov           0x30(%rax),%r9
+  .byte  196,66,53,22,25                     // vpermps       (%r9),%ymm9,%ymm11
+  .byte  196,194,53,22,18                    // vpermps       (%r10),%ymm9,%ymm2
+  .byte  76,139,72,56                        // mov           0x38(%rax),%r9
+  .byte  196,66,53,22,33                     // vpermps       (%r9),%ymm9,%ymm12
+  .byte  76,139,72,32                        // mov           0x20(%rax),%r9
+  .byte  196,194,53,22,25                    // vpermps       (%r9),%ymm9,%ymm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  196,98,53,22,40                     // vpermps       (%rax),%ymm9,%ymm13
+  .byte  196,66,125,168,194                  // vfmadd213ps   %ymm10,%ymm0,%ymm8
+  .byte  196,194,125,168,203                 // vfmadd213ps   %ymm11,%ymm0,%ymm1
+  .byte  196,194,125,168,212                 // vfmadd213ps   %ymm12,%ymm0,%ymm2
+  .byte  196,194,125,168,221                 // vfmadd213ps   %ymm13,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_2_stop_gradient_hsw
+.globl _sk_evenly_spaced_2_stop_gradient_hsw
+FUNCTION(_sk_evenly_spaced_2_stop_gradient_hsw)
+_sk_evenly_spaced_2_stop_gradient_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,8                    // vbroadcastss  (%rax),%ymm1
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,98,125,184,193                  // vfmadd231ps   %ymm1,%ymm0,%ymm8
+  .byte  196,226,125,24,80,4                 // vbroadcastss  0x4(%rax),%ymm2
+  .byte  196,226,125,24,72,20                // vbroadcastss  0x14(%rax),%ymm1
+  .byte  196,226,125,184,202                 // vfmadd231ps   %ymm2,%ymm0,%ymm1
+  .byte  196,226,125,24,88,8                 // vbroadcastss  0x8(%rax),%ymm3
+  .byte  196,226,125,24,80,24                // vbroadcastss  0x18(%rax),%ymm2
+  .byte  196,226,125,184,211                 // vfmadd231ps   %ymm3,%ymm0,%ymm2
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,226,125,24,88,28                // vbroadcastss  0x1c(%rax),%ymm3
+  .byte  196,194,125,184,217                 // vfmadd231ps   %ymm9,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_unit_angle_hsw
+.globl _sk_xy_to_unit_angle_hsw
+FUNCTION(_sk_xy_to_unit_angle_hsw)
+_sk_xy_to_unit_angle_hsw:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,200                       // vsubps        %ymm0,%ymm8,%ymm9
+  .byte  197,52,84,200                       // vandps        %ymm0,%ymm9,%ymm9
+  .byte  197,60,92,209                       // vsubps        %ymm1,%ymm8,%ymm10
+  .byte  197,44,84,209                       // vandps        %ymm1,%ymm10,%ymm10
+  .byte  196,65,52,93,218                    // vminps        %ymm10,%ymm9,%ymm11
+  .byte  196,65,52,95,226                    // vmaxps        %ymm10,%ymm9,%ymm12
+  .byte  196,65,36,94,220                    // vdivps        %ymm12,%ymm11,%ymm11
+  .byte  196,65,36,89,227                    // vmulps        %ymm11,%ymm11,%ymm12
+  .byte  196,98,125,24,45,148,8,0,0          // vbroadcastss  0x894(%rip),%ymm13        # 4ce4 <_sk_callback_hsw+0x48c>
+  .byte  196,98,125,24,53,143,8,0,0          // vbroadcastss  0x88f(%rip),%ymm14        # 4ce8 <_sk_callback_hsw+0x490>
+  .byte  196,66,29,184,245                   // vfmadd231ps   %ymm13,%ymm12,%ymm14
+  .byte  196,98,125,24,45,133,8,0,0          // vbroadcastss  0x885(%rip),%ymm13        # 4cec <_sk_callback_hsw+0x494>
+  .byte  196,66,29,184,238                   // vfmadd231ps   %ymm14,%ymm12,%ymm13
+  .byte  196,98,125,24,53,123,8,0,0          // vbroadcastss  0x87b(%rip),%ymm14        # 4cf0 <_sk_callback_hsw+0x498>
+  .byte  196,66,29,184,245                   // vfmadd231ps   %ymm13,%ymm12,%ymm14
+  .byte  196,65,36,89,222                    // vmulps        %ymm14,%ymm11,%ymm11
+  .byte  196,65,52,194,202,1                 // vcmpltps      %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,21,102,8,0,0          // vbroadcastss  0x866(%rip),%ymm10        # 4cf4 <_sk_callback_hsw+0x49c>
+  .byte  196,65,44,92,211                    // vsubps        %ymm11,%ymm10,%ymm10
+  .byte  196,67,37,74,202,144                // vblendvps     %ymm9,%ymm10,%ymm11,%ymm9
+  .byte  196,193,124,194,192,1               // vcmpltps      %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,21,80,8,0,0           // vbroadcastss  0x850(%rip),%ymm10        # 4cf8 <_sk_callback_hsw+0x4a0>
+  .byte  196,65,44,92,209                    // vsubps        %ymm9,%ymm10,%ymm10
+  .byte  196,195,53,74,194,0                 // vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  .byte  196,65,116,194,200,1                // vcmpltps      %ymm8,%ymm1,%ymm9
+  .byte  196,98,125,24,21,58,8,0,0           // vbroadcastss  0x83a(%rip),%ymm10        # 4cfc <_sk_callback_hsw+0x4a4>
+  .byte  197,44,92,208                       // vsubps        %ymm0,%ymm10,%ymm10
+  .byte  196,195,125,74,194,144              // vblendvps     %ymm9,%ymm10,%ymm0,%ymm0
+  .byte  196,65,124,194,200,3                // vcmpunordps   %ymm8,%ymm0,%ymm9
+  .byte  196,195,125,74,192,144              // vblendvps     %ymm9,%ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_radius_hsw
+.globl _sk_xy_to_radius_hsw
+FUNCTION(_sk_xy_to_radius_hsw)
+_sk_xy_to_radius_hsw:
+  .byte  197,116,89,193                      // vmulps        %ymm1,%ymm1,%ymm8
+  .byte  196,98,125,184,192                  // vfmadd231ps   %ymm0,%ymm0,%ymm8
+  .byte  196,193,124,81,192                  // vsqrtps       %ymm8,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_save_xy_hsw
+.globl _sk_save_xy_hsw
+FUNCTION(_sk_save_xy_hsw)
+_sk_save_xy_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,7,8,0,0             // vbroadcastss  0x807(%rip),%ymm8        # 4d00 <_sk_callback_hsw+0x4a8>
+  .byte  196,65,124,88,200                   // vaddps        %ymm8,%ymm0,%ymm9
+  .byte  196,67,125,8,209,1                  // vroundps      $0x1,%ymm9,%ymm10
+  .byte  196,65,52,92,202                    // vsubps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,116,88,192                   // vaddps        %ymm8,%ymm1,%ymm8
+  .byte  196,67,125,8,208,1                  // vroundps      $0x1,%ymm8,%ymm10
+  .byte  196,65,60,92,194                    // vsubps        %ymm10,%ymm8,%ymm8
+  .byte  197,252,17,0                        // vmovups       %ymm0,(%rax)
+  .byte  197,252,17,72,32                    // vmovups       %ymm1,0x20(%rax)
+  .byte  197,124,17,72,64                    // vmovups       %ymm9,0x40(%rax)
+  .byte  197,124,17,64,96                    // vmovups       %ymm8,0x60(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_accumulate_hsw
+.globl _sk_accumulate_hsw
+FUNCTION(_sk_accumulate_hsw)
+_sk_accumulate_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,16,128,128,0,0,0            // vmovups       0x80(%rax),%ymm8
+  .byte  197,60,89,128,160,0,0,0             // vmulps        0xa0(%rax),%ymm8,%ymm8
+  .byte  196,226,61,184,224                  // vfmadd231ps   %ymm0,%ymm8,%ymm4
+  .byte  196,226,61,184,233                  // vfmadd231ps   %ymm1,%ymm8,%ymm5
+  .byte  196,226,61,184,242                  // vfmadd231ps   %ymm2,%ymm8,%ymm6
+  .byte  196,98,101,168,199                  // vfmadd213ps   %ymm7,%ymm3,%ymm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,199                      // vmovaps       %ymm8,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_nx_hsw
+.globl _sk_bilinear_nx_hsw
+FUNCTION(_sk_bilinear_nx_hsw)
+_sk_bilinear_nx_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,155,7,0,0          // vbroadcastss  0x79b(%rip),%ymm0        # 4d04 <_sk_callback_hsw+0x4ac>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  196,98,125,24,5,146,7,0,0           // vbroadcastss  0x792(%rip),%ymm8        # 4d08 <_sk_callback_hsw+0x4b0>
+  .byte  197,60,92,64,64                     // vsubps        0x40(%rax),%ymm8,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_px_hsw
+.globl _sk_bilinear_px_hsw
+FUNCTION(_sk_bilinear_px_hsw)
+_sk_bilinear_px_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,122,7,0,0          // vbroadcastss  0x77a(%rip),%ymm0        # 4d0c <_sk_callback_hsw+0x4b4>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  197,124,16,64,64                    // vmovups       0x40(%rax),%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_ny_hsw
+.globl _sk_bilinear_ny_hsw
+FUNCTION(_sk_bilinear_ny_hsw)
+_sk_bilinear_ny_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,94,7,0,0          // vbroadcastss  0x75e(%rip),%ymm1        # 4d10 <_sk_callback_hsw+0x4b8>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  196,98,125,24,5,84,7,0,0            // vbroadcastss  0x754(%rip),%ymm8        # 4d14 <_sk_callback_hsw+0x4bc>
+  .byte  197,60,92,64,96                     // vsubps        0x60(%rax),%ymm8,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_py_hsw
+.globl _sk_bilinear_py_hsw
+FUNCTION(_sk_bilinear_py_hsw)
+_sk_bilinear_py_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,60,7,0,0          // vbroadcastss  0x73c(%rip),%ymm1        # 4d18 <_sk_callback_hsw+0x4c0>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  197,124,16,64,96                    // vmovups       0x60(%rax),%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3x_hsw
+.globl _sk_bicubic_n3x_hsw
+FUNCTION(_sk_bicubic_n3x_hsw)
+_sk_bicubic_n3x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,31,7,0,0           // vbroadcastss  0x71f(%rip),%ymm0        # 4d1c <_sk_callback_hsw+0x4c4>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  196,98,125,24,5,22,7,0,0            // vbroadcastss  0x716(%rip),%ymm8        # 4d20 <_sk_callback_hsw+0x4c8>
+  .byte  197,60,92,64,64                     // vsubps        0x40(%rax),%ymm8,%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,7,7,0,0            // vbroadcastss  0x707(%rip),%ymm10        # 4d24 <_sk_callback_hsw+0x4cc>
+  .byte  196,98,125,24,29,2,7,0,0            // vbroadcastss  0x702(%rip),%ymm11        # 4d28 <_sk_callback_hsw+0x4d0>
+  .byte  196,66,61,168,218                   // vfmadd213ps   %ymm10,%ymm8,%ymm11
+  .byte  196,65,36,89,193                    // vmulps        %ymm9,%ymm11,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1x_hsw
+.globl _sk_bicubic_n1x_hsw
+FUNCTION(_sk_bicubic_n1x_hsw)
+_sk_bicubic_n1x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,229,6,0,0          // vbroadcastss  0x6e5(%rip),%ymm0        # 4d2c <_sk_callback_hsw+0x4d4>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  196,98,125,24,5,220,6,0,0           // vbroadcastss  0x6dc(%rip),%ymm8        # 4d30 <_sk_callback_hsw+0x4d8>
+  .byte  197,60,92,64,64                     // vsubps        0x40(%rax),%ymm8,%ymm8
+  .byte  196,98,125,24,13,210,6,0,0          // vbroadcastss  0x6d2(%rip),%ymm9        # 4d34 <_sk_callback_hsw+0x4dc>
+  .byte  196,98,125,24,21,205,6,0,0          // vbroadcastss  0x6cd(%rip),%ymm10        # 4d38 <_sk_callback_hsw+0x4e0>
+  .byte  196,66,61,168,209                   // vfmadd213ps   %ymm9,%ymm8,%ymm10
+  .byte  196,98,125,24,13,195,6,0,0          // vbroadcastss  0x6c3(%rip),%ymm9        # 4d3c <_sk_callback_hsw+0x4e4>
+  .byte  196,66,61,184,202                   // vfmadd231ps   %ymm10,%ymm8,%ymm9
+  .byte  196,98,125,24,21,185,6,0,0          // vbroadcastss  0x6b9(%rip),%ymm10        # 4d40 <_sk_callback_hsw+0x4e8>
+  .byte  196,66,61,184,209                   // vfmadd231ps   %ymm9,%ymm8,%ymm10
+  .byte  197,124,17,144,128,0,0,0            // vmovups       %ymm10,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1x_hsw
+.globl _sk_bicubic_p1x_hsw
+FUNCTION(_sk_bicubic_p1x_hsw)
+_sk_bicubic_p1x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,161,6,0,0           // vbroadcastss  0x6a1(%rip),%ymm8        # 4d44 <_sk_callback_hsw+0x4ec>
+  .byte  197,188,88,0                        // vaddps        (%rax),%ymm8,%ymm0
+  .byte  197,124,16,72,64                    // vmovups       0x40(%rax),%ymm9
+  .byte  196,98,125,24,21,147,6,0,0          // vbroadcastss  0x693(%rip),%ymm10        # 4d48 <_sk_callback_hsw+0x4f0>
+  .byte  196,98,125,24,29,142,6,0,0          // vbroadcastss  0x68e(%rip),%ymm11        # 4d4c <_sk_callback_hsw+0x4f4>
+  .byte  196,66,53,168,218                   // vfmadd213ps   %ymm10,%ymm9,%ymm11
+  .byte  196,66,53,168,216                   // vfmadd213ps   %ymm8,%ymm9,%ymm11
+  .byte  196,98,125,24,5,127,6,0,0           // vbroadcastss  0x67f(%rip),%ymm8        # 4d50 <_sk_callback_hsw+0x4f8>
+  .byte  196,66,53,184,195                   // vfmadd231ps   %ymm11,%ymm9,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3x_hsw
+.globl _sk_bicubic_p3x_hsw
+FUNCTION(_sk_bicubic_p3x_hsw)
+_sk_bicubic_p3x_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,103,6,0,0          // vbroadcastss  0x667(%rip),%ymm0        # 4d54 <_sk_callback_hsw+0x4fc>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  197,124,16,64,64                    // vmovups       0x40(%rax),%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,84,6,0,0           // vbroadcastss  0x654(%rip),%ymm10        # 4d58 <_sk_callback_hsw+0x500>
+  .byte  196,98,125,24,29,79,6,0,0           // vbroadcastss  0x64f(%rip),%ymm11        # 4d5c <_sk_callback_hsw+0x504>
+  .byte  196,66,61,168,218                   // vfmadd213ps   %ymm10,%ymm8,%ymm11
+  .byte  196,65,52,89,195                    // vmulps        %ymm11,%ymm9,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3y_hsw
+.globl _sk_bicubic_n3y_hsw
+FUNCTION(_sk_bicubic_n3y_hsw)
+_sk_bicubic_n3y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,50,6,0,0          // vbroadcastss  0x632(%rip),%ymm1        # 4d60 <_sk_callback_hsw+0x508>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  196,98,125,24,5,40,6,0,0            // vbroadcastss  0x628(%rip),%ymm8        # 4d64 <_sk_callback_hsw+0x50c>
+  .byte  197,60,92,64,96                     // vsubps        0x60(%rax),%ymm8,%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,25,6,0,0           // vbroadcastss  0x619(%rip),%ymm10        # 4d68 <_sk_callback_hsw+0x510>
+  .byte  196,98,125,24,29,20,6,0,0           // vbroadcastss  0x614(%rip),%ymm11        # 4d6c <_sk_callback_hsw+0x514>
+  .byte  196,66,61,168,218                   // vfmadd213ps   %ymm10,%ymm8,%ymm11
+  .byte  196,65,36,89,193                    // vmulps        %ymm9,%ymm11,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1y_hsw
+.globl _sk_bicubic_n1y_hsw
+FUNCTION(_sk_bicubic_n1y_hsw)
+_sk_bicubic_n1y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,247,5,0,0         // vbroadcastss  0x5f7(%rip),%ymm1        # 4d70 <_sk_callback_hsw+0x518>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  196,98,125,24,5,237,5,0,0           // vbroadcastss  0x5ed(%rip),%ymm8        # 4d74 <_sk_callback_hsw+0x51c>
+  .byte  197,60,92,64,96                     // vsubps        0x60(%rax),%ymm8,%ymm8
+  .byte  196,98,125,24,13,227,5,0,0          // vbroadcastss  0x5e3(%rip),%ymm9        # 4d78 <_sk_callback_hsw+0x520>
+  .byte  196,98,125,24,21,222,5,0,0          // vbroadcastss  0x5de(%rip),%ymm10        # 4d7c <_sk_callback_hsw+0x524>
+  .byte  196,66,61,168,209                   // vfmadd213ps   %ymm9,%ymm8,%ymm10
+  .byte  196,98,125,24,13,212,5,0,0          // vbroadcastss  0x5d4(%rip),%ymm9        # 4d80 <_sk_callback_hsw+0x528>
+  .byte  196,66,61,184,202                   // vfmadd231ps   %ymm10,%ymm8,%ymm9
+  .byte  196,98,125,24,21,202,5,0,0          // vbroadcastss  0x5ca(%rip),%ymm10        # 4d84 <_sk_callback_hsw+0x52c>
+  .byte  196,66,61,184,209                   // vfmadd231ps   %ymm9,%ymm8,%ymm10
+  .byte  197,124,17,144,160,0,0,0            // vmovups       %ymm10,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1y_hsw
+.globl _sk_bicubic_p1y_hsw
+FUNCTION(_sk_bicubic_p1y_hsw)
+_sk_bicubic_p1y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,178,5,0,0           // vbroadcastss  0x5b2(%rip),%ymm8        # 4d88 <_sk_callback_hsw+0x530>
+  .byte  197,188,88,72,32                    // vaddps        0x20(%rax),%ymm8,%ymm1
+  .byte  197,124,16,72,96                    // vmovups       0x60(%rax),%ymm9
+  .byte  196,98,125,24,21,163,5,0,0          // vbroadcastss  0x5a3(%rip),%ymm10        # 4d8c <_sk_callback_hsw+0x534>
+  .byte  196,98,125,24,29,158,5,0,0          // vbroadcastss  0x59e(%rip),%ymm11        # 4d90 <_sk_callback_hsw+0x538>
+  .byte  196,66,53,168,218                   // vfmadd213ps   %ymm10,%ymm9,%ymm11
+  .byte  196,66,53,168,216                   // vfmadd213ps   %ymm8,%ymm9,%ymm11
+  .byte  196,98,125,24,5,143,5,0,0           // vbroadcastss  0x58f(%rip),%ymm8        # 4d94 <_sk_callback_hsw+0x53c>
+  .byte  196,66,53,184,195                   // vfmadd231ps   %ymm11,%ymm9,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3y_hsw
+.globl _sk_bicubic_p3y_hsw
+FUNCTION(_sk_bicubic_p3y_hsw)
+_sk_bicubic_p3y_hsw:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,119,5,0,0         // vbroadcastss  0x577(%rip),%ymm1        # 4d98 <_sk_callback_hsw+0x540>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  197,124,16,64,96                    // vmovups       0x60(%rax),%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,99,5,0,0           // vbroadcastss  0x563(%rip),%ymm10        # 4d9c <_sk_callback_hsw+0x544>
+  .byte  196,98,125,24,29,94,5,0,0           // vbroadcastss  0x55e(%rip),%ymm11        # 4da0 <_sk_callback_hsw+0x548>
+  .byte  196,66,61,168,218                   // vfmadd213ps   %ymm10,%ymm8,%ymm11
+  .byte  196,65,52,89,195                    // vmulps        %ymm11,%ymm9,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_callback_hsw
+.globl _sk_callback_hsw
+FUNCTION(_sk_callback_hsw)
+_sk_callback_hsw:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  72,131,228,224                      // and           $0xffffffffffffffe0,%rsp
+  .byte  72,129,236,192,0,0,0                // sub           $0xc0,%rsp
+  .byte  197,252,41,188,36,128,0,0,0         // vmovaps       %ymm7,0x80(%rsp)
+  .byte  197,252,41,116,36,96                // vmovaps       %ymm6,0x60(%rsp)
+  .byte  197,252,41,108,36,64                // vmovaps       %ymm5,0x40(%rsp)
+  .byte  197,252,41,100,36,32                // vmovaps       %ymm4,0x20(%rsp)
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  72,137,76,36,24                     // mov           %rcx,0x18(%rsp)
+  .byte  73,137,215                          // mov           %rdx,%r15
+  .byte  73,137,252                          // mov           %rdi,%r12
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,198                          // mov           %rax,%r14
+  .byte  73,137,245                          // mov           %rsi,%r13
+  .byte  197,252,20,225                      // vunpcklps     %ymm1,%ymm0,%ymm4
+  .byte  197,252,21,193                      // vunpckhps     %ymm1,%ymm0,%ymm0
+  .byte  197,236,20,203                      // vunpcklps     %ymm3,%ymm2,%ymm1
+  .byte  197,236,21,211                      // vunpckhps     %ymm3,%ymm2,%ymm2
+  .byte  197,221,20,217                      // vunpcklpd     %ymm1,%ymm4,%ymm3
+  .byte  197,221,21,201                      // vunpckhpd     %ymm1,%ymm4,%ymm1
+  .byte  197,253,20,226                      // vunpcklpd     %ymm2,%ymm0,%ymm4
+  .byte  197,253,21,194                      // vunpckhpd     %ymm2,%ymm0,%ymm0
+  .byte  196,227,101,24,209,1                // vinsertf128   $0x1,%xmm1,%ymm3,%ymm2
+  .byte  196,227,93,24,232,1                 // vinsertf128   $0x1,%xmm0,%ymm4,%ymm5
+  .byte  196,227,101,6,201,49                // vperm2f128    $0x31,%ymm1,%ymm3,%ymm1
+  .byte  196,227,93,6,192,49                 // vperm2f128    $0x31,%ymm0,%ymm4,%ymm0
+  .byte  196,193,125,17,86,8                 // vmovupd       %ymm2,0x8(%r14)
+  .byte  196,193,125,17,110,40               // vmovupd       %ymm5,0x28(%r14)
+  .byte  196,193,125,17,78,72                // vmovupd       %ymm1,0x48(%r14)
+  .byte  196,193,125,17,70,104               // vmovupd       %ymm0,0x68(%r14)
+  .byte  72,133,219                          // test          %rbx,%rbx
+  .byte  190,8,0,0,0                         // mov           $0x8,%esi
+  .byte  15,69,243                           // cmovne        %ebx,%esi
+  .byte  76,137,247                          // mov           %r14,%rdi
+  .byte  197,248,119                         // vzeroupper
+  .byte  65,255,22                           // callq         *(%r14)
+  .byte  73,139,134,136,0,0,0                // mov           0x88(%r14),%rax
+  .byte  197,248,16,0                        // vmovups       (%rax),%xmm0
+  .byte  197,248,16,72,16                    // vmovups       0x10(%rax),%xmm1
+  .byte  197,248,16,80,32                    // vmovups       0x20(%rax),%xmm2
+  .byte  197,248,16,88,48                    // vmovups       0x30(%rax),%xmm3
+  .byte  196,227,101,24,88,112,1             // vinsertf128   $0x1,0x70(%rax),%ymm3,%ymm3
+  .byte  196,227,109,24,80,96,1              // vinsertf128   $0x1,0x60(%rax),%ymm2,%ymm2
+  .byte  196,227,117,24,72,80,1              // vinsertf128   $0x1,0x50(%rax),%ymm1,%ymm1
+  .byte  196,227,125,24,64,64,1              // vinsertf128   $0x1,0x40(%rax),%ymm0,%ymm0
+  .byte  197,252,20,225                      // vunpcklps     %ymm1,%ymm0,%ymm4
+  .byte  197,252,21,233                      // vunpckhps     %ymm1,%ymm0,%ymm5
+  .byte  197,236,20,203                      // vunpcklps     %ymm3,%ymm2,%ymm1
+  .byte  197,236,21,219                      // vunpckhps     %ymm3,%ymm2,%ymm3
+  .byte  197,221,20,193                      // vunpcklpd     %ymm1,%ymm4,%ymm0
+  .byte  197,221,21,201                      // vunpckhpd     %ymm1,%ymm4,%ymm1
+  .byte  197,213,20,211                      // vunpcklpd     %ymm3,%ymm5,%ymm2
+  .byte  197,213,21,219                      // vunpckhpd     %ymm3,%ymm5,%ymm3
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,231                          // mov           %r12,%rdi
+  .byte  76,137,250                          // mov           %r15,%rdx
+  .byte  72,139,76,36,24                     // mov           0x18(%rsp),%rcx
+  .byte  73,137,216                          // mov           %rbx,%r8
+  .byte  197,252,40,100,36,32                // vmovaps       0x20(%rsp),%ymm4
+  .byte  197,252,40,108,36,64                // vmovaps       0x40(%rsp),%ymm5
+  .byte  197,252,40,116,36,96                // vmovaps       0x60(%rsp),%ymm6
+  .byte  197,252,40,188,36,128,0,0,0         // vmovaps       0x80(%rsp),%ymm7
+  .byte  72,141,101,216                      // lea           -0x28(%rbp),%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+BALIGN4
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,1                            // cmpb          $0x1,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  252                                 // cld
+  .byte  190,0,0,128,63                      // mov           $0x3f800000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,191,0,0,224                   // add           %al,-0x1fffff41(%rax)
+  .byte  64,154                              // rex           (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 4a4d <.literal4+0xb1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 4a5d <.literal4+0xc1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 4a6d <.literal4+0xd1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 4a7d <.literal4+0xe1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,127                    // add           %al,0x7f00003f(%rax)
+  .byte  67,0,0                              // rex.XB        add %al,(%r8)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  145                                 // xchg          %eax,%ecx
+  .byte  131,158,61,92,143,50,63             // sbbl          $0x3f,0x328f5c3d(%rsi)
+  .byte  154                                 // (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,10,215                           // ds            or  %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,97                               // rex.RXB       (bad)
+  .byte  61,82,184,78,65                     // cmp           $0x414eb852,%eax
+  .byte  186,159,98,60,57                    // mov           $0x393c629f,%edx
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  32,187,109,165,144,63               // and           %bh,0x3f90a56d(%rbx)
+  .byte  252                                 // cld
+  .byte  191,16,62,168,177                   // mov           $0xb1a83e10,%edi
+  .byte  152                                 // cwtl
+  .byte  59,0                                // cmp           (%rax),%eax
+  .byte  0,128,63,0,0,192                    // add           %al,-0x3fffffc1(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  0,64,0                              // add           %al,0x0(%rax)
+  .byte  0,128,64,171,170,42                 // add           %al,0x2aaaab40(%rax)
+  .byte  62,0,0                              // add           %al,%ds:(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,64,171                            // add           %al,-0x55(%rax)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  128,64,171,170                      // addb          $0xaa,-0x55(%rax)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,129,128,128,59                  // mov           $0x3b808081,%esi
+  .byte  129,128,128,59,0,248,0,0,8,33       // addl          $0x21080000,-0x7ffc480(%rax)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        4ad5 <.literal4+0x139>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,129,128,128,59              // and           %eax,0x3b808081(,%rdi,1)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  129,128,128,59,129,128,128,59,0,0   // addl          $0x3b80,-0x7f7ec480(%rax)
+  .byte  0,52,255                            // add           %dh,(%rdi,%rdi,8)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            4afc <.literal4+0x160>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            4b75 <.literal4+0x1d9>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,0                      // add           %al,0x3f(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            4b30 <.literal4+0x194>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            4ba9 <.literal4+0x20d>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,0                      // add           %al,0x3f(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            4b64 <.literal4+0x1c8>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            4bdd <.literal4+0x241>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,0                      // add           %al,0x3f(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            4b98 <.literal4+0x1fc>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            4c11 <.literal4+0x275>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,200                    // add           %al,-0x37ffffc1(%rax)
+  .byte  66,0,0                              // rex.X         add %al,(%rax)
+  .byte  127,67                              // jg            4c0f <.literal4+0x273>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,65,203,61                       // addb          $0x3d,-0x35(%rcx)
+  .byte  13,60,111,18,3                      // or            $0x3126f3c,%eax
+  .byte  59,10                               // cmp           (%rdx),%ecx
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  163,59,194,24,17,60,203,61,13       // movabs        %eax,0xd3dcb3c1118c23b
+  .byte  190,80,128,3,62                     // mov           $0x3e038050,%esi
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           4c2f <.literal4+0x293>
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  129,128,128,59,129,128,128,59,0,0   // addl          $0x3b80,-0x7f7ec480(%rax)
+  .byte  127,67                              // jg            4c43 <.literal4+0x2a7>
+  .byte  129,128,128,59,0,0,128,63,129,128   // addl          $0x80813f80,0x3b80(%rax)
+  .byte  128,59,0                            // cmpb          $0x0,(%rbx)
+  .byte  0,128,63,129,128,128                // add           %al,-0x7f7f7ec1(%rax)
+  .byte  59,0                                // cmp           (%rax),%eax
+  .byte  248                                 // clc
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        4c25 <.literal4+0x289>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,0,0,128,63                  // and           %eax,0x3f800000(,%rdi,1)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        4c41 <.literal4+0x2a5>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,0,0,128,63                  // and           %eax,0x3f800000(,%rdi,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  124,66                              // jl            4c96 <.literal4+0x2fa>
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,0,15                 // mov           %ecx,0xf003788(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,240,0                // mov           %ecx,0xf03988(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,59,15,0                 // mov           %ecx,0xf3b88(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,61,0,240                // mov           %ecx,-0xfffc278(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,0,15                 // mov           %ecx,0xf003788(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,240,0                // mov           %ecx,0xf03988(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,59,15,0                 // mov           %ecx,0xf3b88(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,61,0,0                  // mov           %ecx,0x3d88(%rax)
+  .byte  112,65                              // jo            4cd9 <.literal4+0x33d>
+  .byte  129,128,128,59,129,128,128,59,0,0   // addl          $0x3b80,-0x7f7ec480(%rax)
+  .byte  127,67                              // jg            4ce7 <.literal4+0x34b>
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  255                                 // (bad)
+  .byte  127,71                              // jg            4cfb <.literal4+0x35f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,191,0,0,0                     // add           %al,0xbf(%rax)
+  .byte  63                                  // (bad)
+  .byte  208                                 // (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,89                               // ds            pop %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  152                                 // cwtl
+  .byte  221,147,61,18,120,57                // fstl          0x3978123d(%rbx)
+  .byte  64,45,16,17,192,32                  // rex           sub $0x20c01110,%eax
+  .byte  148                                 // xchg          %eax,%esp
+  .byte  90                                  // pop           %rdx
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,0,24                             // add           %bl,%ds:(%rax)
+  .byte  161,57,1,0,0,0,111,43,231           // movabs        0xe72b6f0000000139,%eax
+  .byte  187,159,215,202,60                  // mov           $0x3ccad79f,%ebx
+  .byte  212                                 // (bad)
+  .byte  100,84                              // fs            push %rsp
+  .byte  189,169,240,34,62                   // mov           $0x3e22f0a9,%ebp
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,62,0                            // cmpb          $0x0,(%rsi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,128,63                    // add           %bh,0x3f800000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,128,63                    // add           %bh,0x3f800000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,128,63,171              // sarb          $0xab,0x3f800000(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,114,28,199,62                   // mov           $0x3ec71c72,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,128,63                    // add           %bh,0x3f800000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,85                           // sarb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,0,0,0,63                        // mov           $0x3f000000,%edi
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,85                           // sarb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,57,142,99,61                    // mov           $0x3d638e39,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,171                          // sarb          $0xab,(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,114,28,199,62                   // mov           $0x3ec71c72,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,128,63,171              // sarb          $0xab,0x3f800000(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,114,28,199,62                   // mov           $0x3ec71c72,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,128,63                    // add           %bh,0x3f800000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,85                           // sarb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,0,0,0,63                        // mov           $0x3f000000,%edi
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,85                           // sarb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,57,142,99,61                    // mov           $0x3d638e39,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,171                          // sarb          $0xab,(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,114,28,199,62                   // mov           $0x3ec71c72,%esi
+
+BALIGN32
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  1,255                               // add           %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a004de8 <_sk_callback_hsw+0xa000590>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,17               // decl          0x11ffffff(%rip)        # 12004df0 <_sk_callback_hsw+0x12000598>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,21,255,255,255,25               // callq         *0x19ffffff(%rip)        # 1a004df8 <_sk_callback_hsw+0x1a0005a0>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,29,255,255,255,2                // lcall         *0x2ffffff(%rip)        # 3004e00 <_sk_callback_hsw+0x30005a8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,18                              // callq         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,22                              // callq         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,26                              // lcall         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,30                              // lcall         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  1,255                               // add           %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a004e48 <_sk_callback_hsw+0xa0005f0>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,17               // decl          0x11ffffff(%rip)        # 12004e50 <_sk_callback_hsw+0x120005f8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,21,255,255,255,25               // callq         *0x19ffffff(%rip)        # 1a004e58 <_sk_callback_hsw+0x1a000600>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,29,255,255,255,2                // lcall         *0x2ffffff(%rip)        # 3004e60 <_sk_callback_hsw+0x3000608>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,18                              // callq         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,22                              // callq         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,26                              // lcall         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,30                              // lcall         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  1,255                               // add           %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a004ea8 <_sk_callback_hsw+0xa000650>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,17               // decl          0x11ffffff(%rip)        # 12004eb0 <_sk_callback_hsw+0x12000658>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,21,255,255,255,25               // callq         *0x19ffffff(%rip)        # 1a004eb8 <_sk_callback_hsw+0x1a000660>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,29,255,255,255,2                // lcall         *0x2ffffff(%rip)        # 3004ec0 <_sk_callback_hsw+0x3000668>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,18                              // callq         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,22                              // callq         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,26                              // lcall         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,30                              // lcall         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  1,255                               // add           %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a004f08 <_sk_callback_hsw+0xa0006b0>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,17               // decl          0x11ffffff(%rip)        # 12004f10 <_sk_callback_hsw+0x120006b8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,21,255,255,255,25               // callq         *0x19ffffff(%rip)        # 1a004f18 <_sk_callback_hsw+0x1a0006c0>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,29,255,255,255,2                // lcall         *0x2ffffff(%rip)        # 3004f20 <_sk_callback_hsw+0x30006c8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,18                              // callq         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,22                              // callq         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,26                              // lcall         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,30                              // lcall         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  1,255                               // add           %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a004f68 <_sk_callback_hsw+0xa000710>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,17               // decl          0x11ffffff(%rip)        # 12004f70 <_sk_callback_hsw+0x12000718>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,21,255,255,255,25               // callq         *0x19ffffff(%rip)        # 1a004f78 <_sk_callback_hsw+0x1a000720>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,29,255,255,255,2                // lcall         *0x2ffffff(%rip)        # 3004f80 <_sk_callback_hsw+0x3000728>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,18                              // callq         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,22                              // callq         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,26                              // lcall         *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,30                              // lcall         *(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+BALIGN16
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+BALIGN32
+
+HIDDEN _sk_start_pipeline_avx
+.globl _sk_start_pipeline_avx
+FUNCTION(_sk_start_pipeline_avx)
+_sk_start_pipeline_avx:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  80                                  // push          %rax
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  73,137,208                          // mov           %rdx,%r8
+  .byte  73,137,244                          // mov           %rsi,%r12
+  .byte  73,137,254                          // mov           %rdi,%r14
+  .byte  72,137,206                          // mov           %rcx,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,197                          // mov           %rax,%r13
+  .byte  73,137,247                          // mov           %rsi,%r15
+  .byte  73,141,78,8                         // lea           0x8(%r14),%rcx
+  .byte  76,57,193                           // cmp           %r8,%rcx
+  .byte  118,5                               // jbe           33 <_sk_start_pipeline_avx+0x33>
+  .byte  76,137,242                          // mov           %r14,%rdx
+  .byte  235,77                              // jmp           80 <_sk_start_pipeline_avx+0x80>
+  .byte  76,137,69,208                       // mov           %r8,-0x30(%rbp)
+  .byte  65,184,0,0,0,0                      // mov           $0x0,%r8d
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  197,220,87,228                      // vxorps        %ymm4,%ymm4,%ymm4
+  .byte  197,212,87,237                      // vxorps        %ymm5,%ymm5,%ymm5
+  .byte  197,204,87,246                      // vxorps        %ymm6,%ymm6,%ymm6
+  .byte  197,196,87,255                      // vxorps        %ymm7,%ymm7,%ymm7
+  .byte  72,137,223                          // mov           %rbx,%rdi
+  .byte  76,137,254                          // mov           %r15,%rsi
+  .byte  76,137,242                          // mov           %r14,%rdx
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  65,255,213                          // callq         *%r13
+  .byte  76,139,69,208                       // mov           -0x30(%rbp),%r8
+  .byte  73,141,86,8                         // lea           0x8(%r14),%rdx
+  .byte  73,131,198,16                       // add           $0x10,%r14
+  .byte  77,57,198                           // cmp           %r8,%r14
+  .byte  73,137,214                          // mov           %rdx,%r14
+  .byte  118,183                             // jbe           37 <_sk_start_pipeline_avx+0x37>
+  .byte  73,41,208                           // sub           %rdx,%r8
+  .byte  116,44                              // je            b1 <_sk_start_pipeline_avx+0xb1>
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  197,220,87,228                      // vxorps        %ymm4,%ymm4,%ymm4
+  .byte  197,212,87,237                      // vxorps        %ymm5,%ymm5,%ymm5
+  .byte  197,204,87,246                      // vxorps        %ymm6,%ymm6,%ymm6
+  .byte  197,196,87,255                      // vxorps        %ymm7,%ymm7,%ymm7
+  .byte  72,137,223                          // mov           %rbx,%rdi
+  .byte  76,137,254                          // mov           %r15,%rsi
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  65,255,213                          // callq         *%r13
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  197,248,119                         // vzeroupper
+  .byte  195                                 // retq
+
+HIDDEN _sk_just_return_avx
+.globl _sk_just_return_avx
+FUNCTION(_sk_just_return_avx)
+_sk_just_return_avx:
+  .byte  195                                 // retq
+
+HIDDEN _sk_seed_shader_avx
+.globl _sk_seed_shader_avx
+FUNCTION(_sk_seed_shader_avx)
+_sk_seed_shader_avx:
+  .byte  197,249,110,194                     // vmovd         %edx,%xmm0
+  .byte  197,249,112,192,0                   // vpshufd       $0x0,%xmm0,%xmm0
+  .byte  196,227,125,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,96,100,0,0        // vbroadcastss  0x6460(%rip),%ymm1        # 6540 <_sk_callback_avx+0x144>
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,252,88,7                        // vaddps        (%rdi),%ymm0,%ymm0
+  .byte  197,249,110,209                     // vmovd         %ecx,%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,227,109,24,210,1                // vinsertf128   $0x1,%xmm2,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  197,236,88,201                      // vaddps        %ymm1,%ymm2,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,21,58,100,0,0        // vbroadcastss  0x643a(%rip),%ymm2        # 6544 <_sk_callback_avx+0x148>
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  197,220,87,228                      // vxorps        %ymm4,%ymm4,%ymm4
+  .byte  197,212,87,237                      // vxorps        %ymm5,%ymm5,%ymm5
+  .byte  197,204,87,246                      // vxorps        %ymm6,%ymm6,%ymm6
+  .byte  197,196,87,255                      // vxorps        %ymm7,%ymm7,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dither_avx
+.globl _sk_dither_avx
+FUNCTION(_sk_dither_avx)
+_sk_dither_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,16,71,32                    // vmovups       0x20(%rdi),%ymm8
+  .byte  196,67,125,25,193,1                 // vextractf128  $0x1,%ymm8,%xmm9
+  .byte  197,121,110,210                     // vmovd         %edx,%xmm10
+  .byte  196,65,121,112,210,0                // vpshufd       $0x0,%xmm10,%xmm10
+  .byte  196,65,49,254,202                   // vpaddd        %xmm10,%xmm9,%xmm9
+  .byte  196,65,57,254,194                   // vpaddd        %xmm10,%xmm8,%xmm8
+  .byte  196,67,61,24,193,1                  // vinsertf128   $0x1,%xmm9,%ymm8,%ymm8
+  .byte  197,121,110,201                     // vmovd         %ecx,%xmm9
+  .byte  196,65,121,112,201,0                // vpshufd       $0x0,%xmm9,%xmm9
+  .byte  196,67,53,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
+  .byte  196,65,52,87,208                    // vxorps        %ymm8,%ymm9,%ymm10
+  .byte  196,98,125,24,29,227,99,0,0         // vbroadcastss  0x63e3(%rip),%ymm11        # 6548 <_sk_callback_avx+0x14c>
+  .byte  196,65,44,84,203                    // vandps        %ymm11,%ymm10,%ymm9
+  .byte  196,193,25,114,241,5                // vpslld        $0x5,%xmm9,%xmm12
+  .byte  196,67,125,25,201,1                 // vextractf128  $0x1,%ymm9,%xmm9
+  .byte  196,193,49,114,241,5                // vpslld        $0x5,%xmm9,%xmm9
+  .byte  196,67,29,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm12,%ymm9
+  .byte  196,65,60,84,219                    // vandps        %ymm11,%ymm8,%ymm11
+  .byte  196,193,25,114,243,4                // vpslld        $0x4,%xmm11,%xmm12
+  .byte  196,67,125,25,219,1                 // vextractf128  $0x1,%ymm11,%xmm11
+  .byte  196,193,33,114,243,4                // vpslld        $0x4,%xmm11,%xmm11
+  .byte  196,67,29,24,219,1                  // vinsertf128   $0x1,%xmm11,%ymm12,%ymm11
+  .byte  196,98,125,24,37,164,99,0,0         // vbroadcastss  0x63a4(%rip),%ymm12        # 654c <_sk_callback_avx+0x150>
+  .byte  196,98,125,24,45,159,99,0,0         // vbroadcastss  0x639f(%rip),%ymm13        # 6550 <_sk_callback_avx+0x154>
+  .byte  196,65,44,84,245                    // vandps        %ymm13,%ymm10,%ymm14
+  .byte  196,193,1,114,246,2                 // vpslld        $0x2,%xmm14,%xmm15
+  .byte  196,67,125,25,246,1                 // vextractf128  $0x1,%ymm14,%xmm14
+  .byte  196,193,9,114,246,2                 // vpslld        $0x2,%xmm14,%xmm14
+  .byte  196,67,5,24,246,1                   // vinsertf128   $0x1,%xmm14,%ymm15,%ymm14
+  .byte  196,65,60,84,237                    // vandps        %ymm13,%ymm8,%ymm13
+  .byte  196,65,17,254,253                   // vpaddd        %xmm13,%xmm13,%xmm15
+  .byte  196,67,125,25,237,1                 // vextractf128  $0x1,%ymm13,%xmm13
+  .byte  196,65,17,254,237                   // vpaddd        %xmm13,%xmm13,%xmm13
+  .byte  196,67,5,24,237,1                   // vinsertf128   $0x1,%xmm13,%ymm15,%ymm13
+  .byte  196,65,44,84,212                    // vandps        %ymm12,%ymm10,%ymm10
+  .byte  196,193,1,114,210,1                 // vpsrld        $0x1,%xmm10,%xmm15
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,210,1                // vpsrld        $0x1,%xmm10,%xmm10
+  .byte  196,67,5,24,210,1                   // vinsertf128   $0x1,%xmm10,%ymm15,%ymm10
+  .byte  196,65,60,84,196                    // vandps        %ymm12,%ymm8,%ymm8
+  .byte  196,193,25,114,208,2                // vpsrld        $0x2,%xmm8,%xmm12
+  .byte  196,67,125,25,192,1                 // vextractf128  $0x1,%ymm8,%xmm8
+  .byte  196,193,57,114,208,2                // vpsrld        $0x2,%xmm8,%xmm8
+  .byte  196,67,29,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm12,%ymm8
+  .byte  196,65,20,86,219                    // vorps         %ymm11,%ymm13,%ymm11
+  .byte  196,65,36,86,192                    // vorps         %ymm8,%ymm11,%ymm8
+  .byte  196,65,52,86,206                    // vorps         %ymm14,%ymm9,%ymm9
+  .byte  196,65,60,86,193                    // vorps         %ymm9,%ymm8,%ymm8
+  .byte  196,65,60,86,194                    // vorps         %ymm10,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,13,10,99,0,0          // vbroadcastss  0x630a(%rip),%ymm9        # 6554 <_sk_callback_avx+0x158>
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,13,0,99,0,0           // vbroadcastss  0x6300(%rip),%ymm9        # 6558 <_sk_callback_avx+0x15c>
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,8                     // vbroadcastss  (%rax),%ymm9
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  197,188,88,192                      // vaddps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,88,201                      // vaddps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,88,210                      // vaddps        %ymm2,%ymm8,%ymm2
+  .byte  197,252,93,195                      // vminps        %ymm3,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,192                      // vmaxps        %ymm0,%ymm8,%ymm0
+  .byte  197,244,93,203                      // vminps        %ymm3,%ymm1,%ymm1
+  .byte  197,188,95,201                      // vmaxps        %ymm1,%ymm8,%ymm1
+  .byte  197,236,93,211                      // vminps        %ymm3,%ymm2,%ymm2
+  .byte  197,188,95,210                      // vmaxps        %ymm2,%ymm8,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_constant_color_avx
+.globl _sk_constant_color_avx
+FUNCTION(_sk_constant_color_avx)
+_sk_constant_color_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,0                    // vbroadcastss  (%rax),%ymm0
+  .byte  196,226,125,24,72,4                 // vbroadcastss  0x4(%rax),%ymm1
+  .byte  196,226,125,24,80,8                 // vbroadcastss  0x8(%rax),%ymm2
+  .byte  196,226,125,24,88,12                // vbroadcastss  0xc(%rax),%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_rgba_avx
+.globl _sk_load_rgba_avx
+FUNCTION(_sk_load_rgba_avx)
+_sk_load_rgba_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,0                        // vmovups       (%rax),%ymm0
+  .byte  197,252,16,72,32                    // vmovups       0x20(%rax),%ymm1
+  .byte  197,252,16,80,64                    // vmovups       0x40(%rax),%ymm2
+  .byte  197,252,16,88,96                    // vmovups       0x60(%rax),%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_rgba_avx
+.globl _sk_store_rgba_avx
+FUNCTION(_sk_store_rgba_avx)
+_sk_store_rgba_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,17,0                        // vmovups       %ymm0,(%rax)
+  .byte  197,252,17,72,32                    // vmovups       %ymm1,0x20(%rax)
+  .byte  197,252,17,80,64                    // vmovups       %ymm2,0x40(%rax)
+  .byte  197,252,17,88,96                    // vmovups       %ymm3,0x60(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clear_avx
+.globl _sk_clear_avx
+FUNCTION(_sk_clear_avx)
+_sk_clear_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  197,228,87,219                      // vxorps        %ymm3,%ymm3,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcatop_avx
+.globl _sk_srcatop_avx
+FUNCTION(_sk_srcatop_avx)
+_sk_srcatop_avx:
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  196,98,125,24,5,88,98,0,0           // vbroadcastss  0x6258(%rip),%ymm8        # 655c <_sk_callback_avx+0x160>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,204                       // vmulps        %ymm4,%ymm8,%ymm9
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,60,89,205                       // vmulps        %ymm5,%ymm8,%ymm9
+  .byte  197,180,88,201                      // vaddps        %ymm1,%ymm9,%ymm1
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,60,89,206                       // vmulps        %ymm6,%ymm8,%ymm9
+  .byte  197,180,88,210                      // vaddps        %ymm2,%ymm9,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  196,193,100,88,216                  // vaddps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstatop_avx
+.globl _sk_dstatop_avx
+FUNCTION(_sk_dstatop_avx)
+_sk_dstatop_avx:
+  .byte  197,100,89,196                      // vmulps        %ymm4,%ymm3,%ymm8
+  .byte  196,98,125,24,13,26,98,0,0          // vbroadcastss  0x621a(%rip),%ymm9        # 6560 <_sk_callback_avx+0x164>
+  .byte  197,52,92,207                       // vsubps        %ymm7,%ymm9,%ymm9
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  197,188,88,192                      // vaddps        %ymm0,%ymm8,%ymm0
+  .byte  197,100,89,197                      // vmulps        %ymm5,%ymm3,%ymm8
+  .byte  197,180,89,201                      // vmulps        %ymm1,%ymm9,%ymm1
+  .byte  197,188,88,201                      // vaddps        %ymm1,%ymm8,%ymm1
+  .byte  197,100,89,198                      // vmulps        %ymm6,%ymm3,%ymm8
+  .byte  197,180,89,210                      // vmulps        %ymm2,%ymm9,%ymm2
+  .byte  197,188,88,210                      // vaddps        %ymm2,%ymm8,%ymm2
+  .byte  197,100,89,199                      // vmulps        %ymm7,%ymm3,%ymm8
+  .byte  197,180,89,219                      // vmulps        %ymm3,%ymm9,%ymm3
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcin_avx
+.globl _sk_srcin_avx
+FUNCTION(_sk_srcin_avx)
+_sk_srcin_avx:
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstin_avx
+.globl _sk_dstin_avx
+FUNCTION(_sk_dstin_avx)
+_sk_dstin_avx:
+  .byte  197,228,89,196                      // vmulps        %ymm4,%ymm3,%ymm0
+  .byte  197,228,89,205                      // vmulps        %ymm5,%ymm3,%ymm1
+  .byte  197,228,89,214                      // vmulps        %ymm6,%ymm3,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcout_avx
+.globl _sk_srcout_avx
+FUNCTION(_sk_srcout_avx)
+_sk_srcout_avx:
+  .byte  196,98,125,24,5,185,97,0,0          // vbroadcastss  0x61b9(%rip),%ymm8        # 6564 <_sk_callback_avx+0x168>
+  .byte  197,60,92,199                       // vsubps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstout_avx
+.globl _sk_dstout_avx
+FUNCTION(_sk_dstout_avx)
+_sk_dstout_avx:
+  .byte  196,226,125,24,5,156,97,0,0         // vbroadcastss  0x619c(%rip),%ymm0        # 6568 <_sk_callback_avx+0x16c>
+  .byte  197,252,92,219                      // vsubps        %ymm3,%ymm0,%ymm3
+  .byte  197,228,89,196                      // vmulps        %ymm4,%ymm3,%ymm0
+  .byte  197,228,89,205                      // vmulps        %ymm5,%ymm3,%ymm1
+  .byte  197,228,89,214                      // vmulps        %ymm6,%ymm3,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_avx
+.globl _sk_srcover_avx
+FUNCTION(_sk_srcover_avx)
+_sk_srcover_avx:
+  .byte  196,98,125,24,5,127,97,0,0          // vbroadcastss  0x617f(%rip),%ymm8        # 656c <_sk_callback_avx+0x170>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,204                       // vmulps        %ymm4,%ymm8,%ymm9
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  197,60,89,205                       // vmulps        %ymm5,%ymm8,%ymm9
+  .byte  197,180,88,201                      // vaddps        %ymm1,%ymm9,%ymm1
+  .byte  197,60,89,206                       // vmulps        %ymm6,%ymm8,%ymm9
+  .byte  197,180,88,210                      // vaddps        %ymm2,%ymm9,%ymm2
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstover_avx
+.globl _sk_dstover_avx
+FUNCTION(_sk_dstover_avx)
+_sk_dstover_avx:
+  .byte  196,98,125,24,5,82,97,0,0           // vbroadcastss  0x6152(%rip),%ymm8        # 6570 <_sk_callback_avx+0x174>
+  .byte  197,60,92,199                       // vsubps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,252,88,196                      // vaddps        %ymm4,%ymm0,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,244,88,205                      // vaddps        %ymm5,%ymm1,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,236,88,214                      // vaddps        %ymm6,%ymm2,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_modulate_avx
+.globl _sk_modulate_avx
+FUNCTION(_sk_modulate_avx)
+_sk_modulate_avx:
+  .byte  197,252,89,196                      // vmulps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,89,205                      // vmulps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,89,214                      // vmulps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_multiply_avx
+.globl _sk_multiply_avx
+FUNCTION(_sk_multiply_avx)
+_sk_multiply_avx:
+  .byte  196,98,125,24,5,17,97,0,0           // vbroadcastss  0x6111(%rip),%ymm8        # 6574 <_sk_callback_avx+0x178>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,52,89,208                       // vmulps        %ymm0,%ymm9,%ymm10
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,220                       // vmulps        %ymm4,%ymm8,%ymm11
+  .byte  196,65,36,88,210                    // vaddps        %ymm10,%ymm11,%ymm10
+  .byte  197,252,89,196                      // vmulps        %ymm4,%ymm0,%ymm0
+  .byte  196,193,124,88,194                  // vaddps        %ymm10,%ymm0,%ymm0
+  .byte  197,52,89,209                       // vmulps        %ymm1,%ymm9,%ymm10
+  .byte  197,60,89,221                       // vmulps        %ymm5,%ymm8,%ymm11
+  .byte  196,65,36,88,210                    // vaddps        %ymm10,%ymm11,%ymm10
+  .byte  197,244,89,205                      // vmulps        %ymm5,%ymm1,%ymm1
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  197,52,89,210                       // vmulps        %ymm2,%ymm9,%ymm10
+  .byte  197,60,89,222                       // vmulps        %ymm6,%ymm8,%ymm11
+  .byte  196,65,36,88,210                    // vaddps        %ymm10,%ymm11,%ymm10
+  .byte  197,236,89,214                      // vmulps        %ymm6,%ymm2,%ymm2
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  197,52,89,203                       // vmulps        %ymm3,%ymm9,%ymm9
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,88,216                  // vaddps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_plus__avx
+.globl _sk_plus__avx
+FUNCTION(_sk_plus__avx)
+_sk_plus__avx:
+  .byte  197,252,88,196                      // vaddps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,88,205                      // vaddps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,88,214                      // vaddps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_screen_avx
+.globl _sk_screen_avx
+FUNCTION(_sk_screen_avx)
+_sk_screen_avx:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,196                      // vmulps        %ymm4,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,205                      // vmulps        %ymm5,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,214                      // vmulps        %ymm6,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  197,100,88,199                      // vaddps        %ymm7,%ymm3,%ymm8
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  197,188,92,219                      // vsubps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xor__avx
+.globl _sk_xor__avx
+FUNCTION(_sk_xor__avx)
+_sk_xor__avx:
+  .byte  196,98,125,24,5,96,96,0,0           // vbroadcastss  0x6060(%rip),%ymm8        # 6578 <_sk_callback_avx+0x17c>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,212                       // vmulps        %ymm4,%ymm8,%ymm10
+  .byte  197,172,88,192                      // vaddps        %ymm0,%ymm10,%ymm0
+  .byte  197,180,89,201                      // vmulps        %ymm1,%ymm9,%ymm1
+  .byte  197,60,89,213                       // vmulps        %ymm5,%ymm8,%ymm10
+  .byte  197,172,88,201                      // vaddps        %ymm1,%ymm10,%ymm1
+  .byte  197,180,89,210                      // vmulps        %ymm2,%ymm9,%ymm2
+  .byte  197,60,89,214                       // vmulps        %ymm6,%ymm8,%ymm10
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  197,180,89,219                      // vmulps        %ymm3,%ymm9,%ymm3
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_darken_avx
+.globl _sk_darken_avx
+FUNCTION(_sk_darken_avx)
+_sk_darken_avx:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  196,193,124,95,193                  // vmaxps        %ymm9,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,193,116,95,201                  // vmaxps        %ymm9,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,100,89,206                      // vmulps        %ymm6,%ymm3,%ymm9
+  .byte  196,193,108,95,209                  // vmaxps        %ymm9,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,224,95,0,0          // vbroadcastss  0x5fe0(%rip),%ymm8        # 657c <_sk_callback_avx+0x180>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lighten_avx
+.globl _sk_lighten_avx
+FUNCTION(_sk_lighten_avx)
+_sk_lighten_avx:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  196,193,124,93,193                  // vminps        %ymm9,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,193,116,93,201                  // vminps        %ymm9,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,100,89,206                      // vmulps        %ymm6,%ymm3,%ymm9
+  .byte  196,193,108,93,209                  // vminps        %ymm9,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,140,95,0,0          // vbroadcastss  0x5f8c(%rip),%ymm8        # 6580 <_sk_callback_avx+0x184>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_difference_avx
+.globl _sk_difference_avx
+FUNCTION(_sk_difference_avx)
+_sk_difference_avx:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  196,193,124,93,193                  // vminps        %ymm9,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,207                      // vmulps        %ymm7,%ymm1,%ymm1
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,193,116,93,201                  // vminps        %ymm9,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,215                      // vmulps        %ymm7,%ymm2,%ymm2
+  .byte  197,100,89,206                      // vmulps        %ymm6,%ymm3,%ymm9
+  .byte  196,193,108,93,209                  // vminps        %ymm9,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,44,95,0,0           // vbroadcastss  0x5f2c(%rip),%ymm8        # 6584 <_sk_callback_avx+0x188>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_exclusion_avx
+.globl _sk_exclusion_avx
+FUNCTION(_sk_exclusion_avx)
+_sk_exclusion_avx:
+  .byte  197,124,88,196                      // vaddps        %ymm4,%ymm0,%ymm8
+  .byte  197,252,89,196                      // vmulps        %ymm4,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,116,88,197                      // vaddps        %ymm5,%ymm1,%ymm8
+  .byte  197,244,89,205                      // vmulps        %ymm5,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,188,92,201                      // vsubps        %ymm1,%ymm8,%ymm1
+  .byte  197,108,88,198                      // vaddps        %ymm6,%ymm2,%ymm8
+  .byte  197,236,89,214                      // vmulps        %ymm6,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,188,92,210                      // vsubps        %ymm2,%ymm8,%ymm2
+  .byte  196,98,125,24,5,231,94,0,0          // vbroadcastss  0x5ee7(%rip),%ymm8        # 6588 <_sk_callback_avx+0x18c>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colorburn_avx
+.globl _sk_colorburn_avx
+FUNCTION(_sk_colorburn_avx)
+_sk_colorburn_avx:
+  .byte  196,98,125,24,5,210,94,0,0          // vbroadcastss  0x5ed2(%rip),%ymm8        # 658c <_sk_callback_avx+0x190>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,52,89,216                       // vmulps        %ymm0,%ymm9,%ymm11
+  .byte  196,65,44,87,210                    // vxorps        %ymm10,%ymm10,%ymm10
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,228                       // vmulps        %ymm4,%ymm8,%ymm12
+  .byte  197,68,92,236                       // vsubps        %ymm4,%ymm7,%ymm13
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  197,20,94,232                       // vdivps        %ymm0,%ymm13,%ymm13
+  .byte  196,65,68,93,237                    // vminps        %ymm13,%ymm7,%ymm13
+  .byte  196,65,68,92,237                    // vsubps        %ymm13,%ymm7,%ymm13
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  196,65,36,88,237                    // vaddps        %ymm13,%ymm11,%ymm13
+  .byte  196,65,28,88,237                    // vaddps        %ymm13,%ymm12,%ymm13
+  .byte  197,28,88,224                       // vaddps        %ymm0,%ymm12,%ymm12
+  .byte  196,193,124,194,194,0               // vcmpeqps      %ymm10,%ymm0,%ymm0
+  .byte  196,195,21,74,196,0                 // vblendvps     %ymm0,%ymm12,%ymm13,%ymm0
+  .byte  197,92,194,231,0                    // vcmpeqps      %ymm7,%ymm4,%ymm12
+  .byte  197,36,88,220                       // vaddps        %ymm4,%ymm11,%ymm11
+  .byte  196,195,125,74,195,192              // vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  .byte  197,52,89,217                       // vmulps        %ymm1,%ymm9,%ymm11
+  .byte  197,60,89,229                       // vmulps        %ymm5,%ymm8,%ymm12
+  .byte  197,68,92,237                       // vsubps        %ymm5,%ymm7,%ymm13
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  197,20,94,233                       // vdivps        %ymm1,%ymm13,%ymm13
+  .byte  196,65,68,93,237                    // vminps        %ymm13,%ymm7,%ymm13
+  .byte  196,65,68,92,237                    // vsubps        %ymm13,%ymm7,%ymm13
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  196,65,36,88,237                    // vaddps        %ymm13,%ymm11,%ymm13
+  .byte  196,65,28,88,237                    // vaddps        %ymm13,%ymm12,%ymm13
+  .byte  197,28,88,225                       // vaddps        %ymm1,%ymm12,%ymm12
+  .byte  196,193,116,194,202,0               // vcmpeqps      %ymm10,%ymm1,%ymm1
+  .byte  196,195,21,74,204,16                // vblendvps     %ymm1,%ymm12,%ymm13,%ymm1
+  .byte  197,84,194,231,0                    // vcmpeqps      %ymm7,%ymm5,%ymm12
+  .byte  197,36,88,221                       // vaddps        %ymm5,%ymm11,%ymm11
+  .byte  196,195,117,74,203,192              // vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  .byte  197,52,89,202                       // vmulps        %ymm2,%ymm9,%ymm9
+  .byte  196,65,108,194,210,0                // vcmpeqps      %ymm10,%ymm2,%ymm10
+  .byte  197,60,89,222                       // vmulps        %ymm6,%ymm8,%ymm11
+  .byte  197,68,92,230                       // vsubps        %ymm6,%ymm7,%ymm12
+  .byte  197,28,89,227                       // vmulps        %ymm3,%ymm12,%ymm12
+  .byte  197,28,94,226                       // vdivps        %ymm2,%ymm12,%ymm12
+  .byte  197,164,88,210                      // vaddps        %ymm2,%ymm11,%ymm2
+  .byte  196,65,68,93,228                    // vminps        %ymm12,%ymm7,%ymm12
+  .byte  196,65,68,92,228                    // vsubps        %ymm12,%ymm7,%ymm12
+  .byte  197,28,89,227                       // vmulps        %ymm3,%ymm12,%ymm12
+  .byte  196,65,52,88,228                    // vaddps        %ymm12,%ymm9,%ymm12
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,227,37,74,210,160               // vblendvps     %ymm10,%ymm2,%ymm11,%ymm2
+  .byte  197,76,194,215,0                    // vcmpeqps      %ymm7,%ymm6,%ymm10
+  .byte  197,52,88,206                       // vaddps        %ymm6,%ymm9,%ymm9
+  .byte  196,195,109,74,209,160              // vblendvps     %ymm10,%ymm9,%ymm2,%ymm2
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colordodge_avx
+.globl _sk_colordodge_avx
+FUNCTION(_sk_colordodge_avx)
+_sk_colordodge_avx:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,98,125,24,13,206,93,0,0         // vbroadcastss  0x5dce(%rip),%ymm9        # 6590 <_sk_callback_avx+0x194>
+  .byte  197,52,92,215                       // vsubps        %ymm7,%ymm9,%ymm10
+  .byte  197,44,89,216                       // vmulps        %ymm0,%ymm10,%ymm11
+  .byte  197,52,92,203                       // vsubps        %ymm3,%ymm9,%ymm9
+  .byte  197,100,89,228                      // vmulps        %ymm4,%ymm3,%ymm12
+  .byte  197,100,92,232                      // vsubps        %ymm0,%ymm3,%ymm13
+  .byte  196,65,28,94,229                    // vdivps        %ymm13,%ymm12,%ymm12
+  .byte  197,52,89,236                       // vmulps        %ymm4,%ymm9,%ymm13
+  .byte  196,65,68,93,228                    // vminps        %ymm12,%ymm7,%ymm12
+  .byte  197,28,89,227                       // vmulps        %ymm3,%ymm12,%ymm12
+  .byte  196,65,36,88,228                    // vaddps        %ymm12,%ymm11,%ymm12
+  .byte  196,65,20,88,228                    // vaddps        %ymm12,%ymm13,%ymm12
+  .byte  197,20,88,232                       // vaddps        %ymm0,%ymm13,%ymm13
+  .byte  197,252,194,195,0                   // vcmpeqps      %ymm3,%ymm0,%ymm0
+  .byte  196,195,29,74,197,0                 // vblendvps     %ymm0,%ymm13,%ymm12,%ymm0
+  .byte  196,65,92,194,224,0                 // vcmpeqps      %ymm8,%ymm4,%ymm12
+  .byte  197,36,88,220                       // vaddps        %ymm4,%ymm11,%ymm11
+  .byte  196,195,125,74,195,192              // vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  .byte  197,44,89,217                       // vmulps        %ymm1,%ymm10,%ymm11
+  .byte  197,100,89,229                      // vmulps        %ymm5,%ymm3,%ymm12
+  .byte  197,100,92,233                      // vsubps        %ymm1,%ymm3,%ymm13
+  .byte  196,65,28,94,229                    // vdivps        %ymm13,%ymm12,%ymm12
+  .byte  197,52,89,237                       // vmulps        %ymm5,%ymm9,%ymm13
+  .byte  196,65,68,93,228                    // vminps        %ymm12,%ymm7,%ymm12
+  .byte  197,28,89,227                       // vmulps        %ymm3,%ymm12,%ymm12
+  .byte  196,65,36,88,228                    // vaddps        %ymm12,%ymm11,%ymm12
+  .byte  196,65,20,88,228                    // vaddps        %ymm12,%ymm13,%ymm12
+  .byte  197,20,88,233                       // vaddps        %ymm1,%ymm13,%ymm13
+  .byte  197,244,194,203,0                   // vcmpeqps      %ymm3,%ymm1,%ymm1
+  .byte  196,195,29,74,205,16                // vblendvps     %ymm1,%ymm13,%ymm12,%ymm1
+  .byte  196,65,84,194,224,0                 // vcmpeqps      %ymm8,%ymm5,%ymm12
+  .byte  197,36,88,221                       // vaddps        %ymm5,%ymm11,%ymm11
+  .byte  196,195,117,74,203,192              // vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  197,100,89,222                      // vmulps        %ymm6,%ymm3,%ymm11
+  .byte  197,100,92,226                      // vsubps        %ymm2,%ymm3,%ymm12
+  .byte  196,65,36,94,220                    // vdivps        %ymm12,%ymm11,%ymm11
+  .byte  197,52,89,230                       // vmulps        %ymm6,%ymm9,%ymm12
+  .byte  196,65,68,93,219                    // vminps        %ymm11,%ymm7,%ymm11
+  .byte  197,36,89,219                       // vmulps        %ymm3,%ymm11,%ymm11
+  .byte  196,65,44,88,219                    // vaddps        %ymm11,%ymm10,%ymm11
+  .byte  196,65,28,88,219                    // vaddps        %ymm11,%ymm12,%ymm11
+  .byte  197,28,88,226                       // vaddps        %ymm2,%ymm12,%ymm12
+  .byte  197,236,194,211,0                   // vcmpeqps      %ymm3,%ymm2,%ymm2
+  .byte  196,195,37,74,212,32                // vblendvps     %ymm2,%ymm12,%ymm11,%ymm2
+  .byte  196,65,76,194,192,0                 // vcmpeqps      %ymm8,%ymm6,%ymm8
+  .byte  197,44,88,214                       // vaddps        %ymm6,%ymm10,%ymm10
+  .byte  196,195,109,74,210,128              // vblendvps     %ymm8,%ymm10,%ymm2,%ymm2
+  .byte  197,52,89,199                       // vmulps        %ymm7,%ymm9,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hardlight_avx
+.globl _sk_hardlight_avx
+FUNCTION(_sk_hardlight_avx)
+_sk_hardlight_avx:
+  .byte  196,98,125,24,5,224,92,0,0          // vbroadcastss  0x5ce0(%rip),%ymm8        # 6594 <_sk_callback_avx+0x198>
+  .byte  197,60,92,215                       // vsubps        %ymm7,%ymm8,%ymm10
+  .byte  197,44,89,200                       // vmulps        %ymm0,%ymm10,%ymm9
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,220                       // vmulps        %ymm4,%ymm8,%ymm11
+  .byte  196,65,36,88,217                    // vaddps        %ymm9,%ymm11,%ymm11
+  .byte  197,124,88,200                      // vaddps        %ymm0,%ymm0,%ymm9
+  .byte  197,52,194,227,2                    // vcmpleps      %ymm3,%ymm9,%ymm12
+  .byte  197,124,89,204                      // vmulps        %ymm4,%ymm0,%ymm9
+  .byte  196,65,52,88,233                    // vaddps        %ymm9,%ymm9,%ymm13
+  .byte  197,100,89,207                      // vmulps        %ymm7,%ymm3,%ymm9
+  .byte  197,68,92,244                       // vsubps        %ymm4,%ymm7,%ymm14
+  .byte  197,228,92,192                      // vsubps        %ymm0,%ymm3,%ymm0
+  .byte  196,193,124,89,198                  // vmulps        %ymm14,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,180,92,192                      // vsubps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,197,192              // vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  .byte  197,164,88,192                      // vaddps        %ymm0,%ymm11,%ymm0
+  .byte  197,44,89,217                       // vmulps        %ymm1,%ymm10,%ymm11
+  .byte  197,60,89,229                       // vmulps        %ymm5,%ymm8,%ymm12
+  .byte  196,65,28,88,219                    // vaddps        %ymm11,%ymm12,%ymm11
+  .byte  197,116,88,225                      // vaddps        %ymm1,%ymm1,%ymm12
+  .byte  197,28,194,227,2                    // vcmpleps      %ymm3,%ymm12,%ymm12
+  .byte  197,116,89,237                      // vmulps        %ymm5,%ymm1,%ymm13
+  .byte  196,65,20,88,237                    // vaddps        %ymm13,%ymm13,%ymm13
+  .byte  197,68,92,245                       // vsubps        %ymm5,%ymm7,%ymm14
+  .byte  197,228,92,201                      // vsubps        %ymm1,%ymm3,%ymm1
+  .byte  196,193,116,89,206                  // vmulps        %ymm14,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,180,92,201                      // vsubps        %ymm1,%ymm9,%ymm1
+  .byte  196,195,117,74,205,192              // vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  197,60,89,222                       // vmulps        %ymm6,%ymm8,%ymm11
+  .byte  196,65,36,88,210                    // vaddps        %ymm10,%ymm11,%ymm10
+  .byte  197,108,88,218                      // vaddps        %ymm2,%ymm2,%ymm11
+  .byte  197,36,194,219,2                    // vcmpleps      %ymm3,%ymm11,%ymm11
+  .byte  197,108,89,230                      // vmulps        %ymm6,%ymm2,%ymm12
+  .byte  196,65,28,88,228                    // vaddps        %ymm12,%ymm12,%ymm12
+  .byte  197,68,92,238                       // vsubps        %ymm6,%ymm7,%ymm13
+  .byte  197,228,92,210                      // vsubps        %ymm2,%ymm3,%ymm2
+  .byte  196,193,108,89,213                  // vmulps        %ymm13,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,180,92,210                      // vsubps        %ymm2,%ymm9,%ymm2
+  .byte  196,195,109,74,212,176              // vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_overlay_avx
+.globl _sk_overlay_avx
+FUNCTION(_sk_overlay_avx)
+_sk_overlay_avx:
+  .byte  196,98,125,24,5,9,92,0,0            // vbroadcastss  0x5c09(%rip),%ymm8        # 6598 <_sk_callback_avx+0x19c>
+  .byte  197,60,92,215                       // vsubps        %ymm7,%ymm8,%ymm10
+  .byte  197,44,89,200                       // vmulps        %ymm0,%ymm10,%ymm9
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,220                       // vmulps        %ymm4,%ymm8,%ymm11
+  .byte  196,65,36,88,217                    // vaddps        %ymm9,%ymm11,%ymm11
+  .byte  197,92,88,204                       // vaddps        %ymm4,%ymm4,%ymm9
+  .byte  197,52,194,231,2                    // vcmpleps      %ymm7,%ymm9,%ymm12
+  .byte  197,124,89,204                      // vmulps        %ymm4,%ymm0,%ymm9
+  .byte  196,65,52,88,233                    // vaddps        %ymm9,%ymm9,%ymm13
+  .byte  197,100,89,207                      // vmulps        %ymm7,%ymm3,%ymm9
+  .byte  197,68,92,244                       // vsubps        %ymm4,%ymm7,%ymm14
+  .byte  197,228,92,192                      // vsubps        %ymm0,%ymm3,%ymm0
+  .byte  196,193,124,89,198                  // vmulps        %ymm14,%ymm0,%ymm0
+  .byte  197,252,88,192                      // vaddps        %ymm0,%ymm0,%ymm0
+  .byte  197,180,92,192                      // vsubps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,197,192              // vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  .byte  197,164,88,192                      // vaddps        %ymm0,%ymm11,%ymm0
+  .byte  197,44,89,217                       // vmulps        %ymm1,%ymm10,%ymm11
+  .byte  197,60,89,229                       // vmulps        %ymm5,%ymm8,%ymm12
+  .byte  196,65,28,88,219                    // vaddps        %ymm11,%ymm12,%ymm11
+  .byte  197,84,88,229                       // vaddps        %ymm5,%ymm5,%ymm12
+  .byte  197,28,194,231,2                    // vcmpleps      %ymm7,%ymm12,%ymm12
+  .byte  197,116,89,237                      // vmulps        %ymm5,%ymm1,%ymm13
+  .byte  196,65,20,88,237                    // vaddps        %ymm13,%ymm13,%ymm13
+  .byte  197,68,92,245                       // vsubps        %ymm5,%ymm7,%ymm14
+  .byte  197,228,92,201                      // vsubps        %ymm1,%ymm3,%ymm1
+  .byte  196,193,116,89,206                  // vmulps        %ymm14,%ymm1,%ymm1
+  .byte  197,244,88,201                      // vaddps        %ymm1,%ymm1,%ymm1
+  .byte  197,180,92,201                      // vsubps        %ymm1,%ymm9,%ymm1
+  .byte  196,195,117,74,205,192              // vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  197,60,89,222                       // vmulps        %ymm6,%ymm8,%ymm11
+  .byte  196,65,36,88,210                    // vaddps        %ymm10,%ymm11,%ymm10
+  .byte  197,76,88,222                       // vaddps        %ymm6,%ymm6,%ymm11
+  .byte  197,36,194,223,2                    // vcmpleps      %ymm7,%ymm11,%ymm11
+  .byte  197,108,89,230                      // vmulps        %ymm6,%ymm2,%ymm12
+  .byte  196,65,28,88,228                    // vaddps        %ymm12,%ymm12,%ymm12
+  .byte  197,68,92,238                       // vsubps        %ymm6,%ymm7,%ymm13
+  .byte  197,228,92,210                      // vsubps        %ymm2,%ymm3,%ymm2
+  .byte  196,193,108,89,213                  // vmulps        %ymm13,%ymm2,%ymm2
+  .byte  197,236,88,210                      // vaddps        %ymm2,%ymm2,%ymm2
+  .byte  197,180,92,210                      // vsubps        %ymm2,%ymm9,%ymm2
+  .byte  196,195,109,74,212,176              // vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_softlight_avx
+.globl _sk_softlight_avx
+FUNCTION(_sk_softlight_avx)
+_sk_softlight_avx:
+  .byte  197,252,17,84,36,200                // vmovups       %ymm2,-0x38(%rsp)
+  .byte  197,252,40,209                      // vmovaps       %ymm1,%ymm2
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  196,65,52,87,201                    // vxorps        %ymm9,%ymm9,%ymm9
+  .byte  197,52,194,215,1                    // vcmpltps      %ymm7,%ymm9,%ymm10
+  .byte  197,92,94,199                       // vdivps        %ymm7,%ymm4,%ymm8
+  .byte  196,67,53,74,224,160                // vblendvps     %ymm10,%ymm8,%ymm9,%ymm12
+  .byte  196,65,28,88,196                    // vaddps        %ymm12,%ymm12,%ymm8
+  .byte  196,65,60,88,192                    // vaddps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,60,89,216                    // vmulps        %ymm8,%ymm8,%ymm11
+  .byte  196,65,60,88,195                    // vaddps        %ymm11,%ymm8,%ymm8
+  .byte  196,98,125,24,29,0,91,0,0           // vbroadcastss  0x5b00(%rip),%ymm11        # 65a0 <_sk_callback_avx+0x1a4>
+  .byte  196,65,28,88,235                    // vaddps        %ymm11,%ymm12,%ymm13
+  .byte  196,65,20,89,192                    // vmulps        %ymm8,%ymm13,%ymm8
+  .byte  196,98,125,24,45,241,90,0,0         // vbroadcastss  0x5af1(%rip),%ymm13        # 65a4 <_sk_callback_avx+0x1a8>
+  .byte  196,65,28,89,245                    // vmulps        %ymm13,%ymm12,%ymm14
+  .byte  196,65,12,88,192                    // vaddps        %ymm8,%ymm14,%ymm8
+  .byte  196,65,124,82,244                   // vrsqrtps      %ymm12,%ymm14
+  .byte  196,65,124,83,246                   // vrcpps        %ymm14,%ymm14
+  .byte  196,65,12,92,244                    // vsubps        %ymm12,%ymm14,%ymm14
+  .byte  197,92,88,252                       // vaddps        %ymm4,%ymm4,%ymm15
+  .byte  196,65,4,88,255                     // vaddps        %ymm15,%ymm15,%ymm15
+  .byte  197,4,194,255,2                     // vcmpleps      %ymm7,%ymm15,%ymm15
+  .byte  196,67,13,74,240,240                // vblendvps     %ymm15,%ymm8,%ymm14,%ymm14
+  .byte  197,116,88,249                      // vaddps        %ymm1,%ymm1,%ymm15
+  .byte  196,98,125,24,5,175,90,0,0          // vbroadcastss  0x5aaf(%rip),%ymm8        # 659c <_sk_callback_avx+0x1a0>
+  .byte  196,65,60,92,228                    // vsubps        %ymm12,%ymm8,%ymm12
+  .byte  197,132,92,195                      // vsubps        %ymm3,%ymm15,%ymm0
+  .byte  196,65,124,89,228                   // vmulps        %ymm12,%ymm0,%ymm12
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  196,193,124,89,198                  // vmulps        %ymm14,%ymm0,%ymm0
+  .byte  197,100,89,244                      // vmulps        %ymm4,%ymm3,%ymm14
+  .byte  197,140,88,192                      // vaddps        %ymm0,%ymm14,%ymm0
+  .byte  197,28,88,227                       // vaddps        %ymm3,%ymm12,%ymm12
+  .byte  197,28,89,228                       // vmulps        %ymm4,%ymm12,%ymm12
+  .byte  197,4,194,243,2                     // vcmpleps      %ymm3,%ymm15,%ymm14
+  .byte  196,195,125,74,196,224              // vblendvps     %ymm14,%ymm12,%ymm0,%ymm0
+  .byte  197,252,17,68,36,168                // vmovups       %ymm0,-0x58(%rsp)
+  .byte  197,212,94,199                      // vdivps        %ymm7,%ymm5,%ymm0
+  .byte  196,227,53,74,192,160               // vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  .byte  197,124,88,240                      // vaddps        %ymm0,%ymm0,%ymm14
+  .byte  196,65,12,88,246                    // vaddps        %ymm14,%ymm14,%ymm14
+  .byte  196,65,12,89,254                    // vmulps        %ymm14,%ymm14,%ymm15
+  .byte  196,65,12,88,247                    // vaddps        %ymm15,%ymm14,%ymm14
+  .byte  196,65,124,88,251                   // vaddps        %ymm11,%ymm0,%ymm15
+  .byte  196,65,4,89,246                     // vmulps        %ymm14,%ymm15,%ymm14
+  .byte  196,65,124,89,253                   // vmulps        %ymm13,%ymm0,%ymm15
+  .byte  196,65,4,88,246                     // vaddps        %ymm14,%ymm15,%ymm14
+  .byte  197,124,82,248                      // vrsqrtps      %ymm0,%ymm15
+  .byte  196,65,124,83,255                   // vrcpps        %ymm15,%ymm15
+  .byte  197,4,92,248                        // vsubps        %ymm0,%ymm15,%ymm15
+  .byte  197,84,88,229                       // vaddps        %ymm5,%ymm5,%ymm12
+  .byte  196,65,28,88,228                    // vaddps        %ymm12,%ymm12,%ymm12
+  .byte  197,28,194,231,2                    // vcmpleps      %ymm7,%ymm12,%ymm12
+  .byte  196,67,5,74,230,192                 // vblendvps     %ymm12,%ymm14,%ymm15,%ymm12
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,108,88,242                      // vaddps        %ymm2,%ymm2,%ymm14
+  .byte  197,12,92,251                       // vsubps        %ymm3,%ymm14,%ymm15
+  .byte  197,132,89,192                      // vmulps        %ymm0,%ymm15,%ymm0
+  .byte  197,4,89,255                        // vmulps        %ymm7,%ymm15,%ymm15
+  .byte  196,65,4,89,228                     // vmulps        %ymm12,%ymm15,%ymm12
+  .byte  197,100,89,253                      // vmulps        %ymm5,%ymm3,%ymm15
+  .byte  196,65,4,88,228                     // vaddps        %ymm12,%ymm15,%ymm12
+  .byte  197,252,88,195                      // vaddps        %ymm3,%ymm0,%ymm0
+  .byte  197,252,89,197                      // vmulps        %ymm5,%ymm0,%ymm0
+  .byte  197,12,194,243,2                    // vcmpleps      %ymm3,%ymm14,%ymm14
+  .byte  196,99,29,74,240,224                // vblendvps     %ymm14,%ymm0,%ymm12,%ymm14
+  .byte  197,204,94,199                      // vdivps        %ymm7,%ymm6,%ymm0
+  .byte  196,227,53,74,192,160               // vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  .byte  197,124,88,200                      // vaddps        %ymm0,%ymm0,%ymm9
+  .byte  196,65,52,88,201                    // vaddps        %ymm9,%ymm9,%ymm9
+  .byte  196,65,52,89,209                    // vmulps        %ymm9,%ymm9,%ymm10
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,124,88,211                   // vaddps        %ymm11,%ymm0,%ymm10
+  .byte  196,65,44,89,201                    // vmulps        %ymm9,%ymm10,%ymm9
+  .byte  196,65,124,89,213                   // vmulps        %ymm13,%ymm0,%ymm10
+  .byte  196,65,44,88,201                    // vaddps        %ymm9,%ymm10,%ymm9
+  .byte  197,124,82,208                      // vrsqrtps      %ymm0,%ymm10
+  .byte  196,65,124,83,210                   // vrcpps        %ymm10,%ymm10
+  .byte  197,44,92,208                       // vsubps        %ymm0,%ymm10,%ymm10
+  .byte  197,76,88,222                       // vaddps        %ymm6,%ymm6,%ymm11
+  .byte  196,65,36,88,219                    // vaddps        %ymm11,%ymm11,%ymm11
+  .byte  197,36,194,223,2                    // vcmpleps      %ymm7,%ymm11,%ymm11
+  .byte  196,67,45,74,201,176                // vblendvps     %ymm11,%ymm9,%ymm10,%ymm9
+  .byte  197,124,16,100,36,200               // vmovups       -0x38(%rsp),%ymm12
+  .byte  196,65,28,88,212                    // vaddps        %ymm12,%ymm12,%ymm10
+  .byte  197,44,92,219                       // vsubps        %ymm3,%ymm10,%ymm11
+  .byte  197,188,92,192                      // vsubps        %ymm0,%ymm8,%ymm0
+  .byte  197,164,89,192                      // vmulps        %ymm0,%ymm11,%ymm0
+  .byte  197,36,89,223                       // vmulps        %ymm7,%ymm11,%ymm11
+  .byte  196,65,36,89,201                    // vmulps        %ymm9,%ymm11,%ymm9
+  .byte  197,100,89,222                      // vmulps        %ymm6,%ymm3,%ymm11
+  .byte  196,65,36,88,201                    // vaddps        %ymm9,%ymm11,%ymm9
+  .byte  197,252,88,195                      // vaddps        %ymm3,%ymm0,%ymm0
+  .byte  197,252,89,198                      // vmulps        %ymm6,%ymm0,%ymm0
+  .byte  197,44,194,211,2                    // vcmpleps      %ymm3,%ymm10,%ymm10
+  .byte  196,99,53,74,200,160                // vblendvps     %ymm10,%ymm0,%ymm9,%ymm9
+  .byte  197,60,92,215                       // vsubps        %ymm7,%ymm8,%ymm10
+  .byte  197,172,89,193                      // vmulps        %ymm1,%ymm10,%ymm0
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,188,89,204                      // vmulps        %ymm4,%ymm8,%ymm1
+  .byte  197,244,88,192                      // vaddps        %ymm0,%ymm1,%ymm0
+  .byte  197,252,88,68,36,168                // vaddps        -0x58(%rsp),%ymm0,%ymm0
+  .byte  197,172,89,202                      // vmulps        %ymm2,%ymm10,%ymm1
+  .byte  197,188,89,213                      // vmulps        %ymm5,%ymm8,%ymm2
+  .byte  197,236,88,201                      // vaddps        %ymm1,%ymm2,%ymm1
+  .byte  196,193,116,88,206                  // vaddps        %ymm14,%ymm1,%ymm1
+  .byte  196,193,44,89,212                   // vmulps        %ymm12,%ymm10,%ymm2
+  .byte  197,60,89,214                       // vmulps        %ymm6,%ymm8,%ymm10
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  196,193,108,88,209                  // vaddps        %ymm9,%ymm2,%ymm2
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  197,188,88,219                      // vaddps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hue_avx
+.globl _sk_hue_avx
+FUNCTION(_sk_hue_avx)
+_sk_hue_avx:
+  .byte  197,252,17,84,36,200                // vmovups       %ymm2,-0x38(%rsp)
+  .byte  197,124,40,193                      // vmovaps       %ymm1,%ymm8
+  .byte  197,124,17,68,36,168                // vmovups       %ymm8,-0x58(%rsp)
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,116,89,203                      // vmulps        %ymm3,%ymm1,%ymm9
+  .byte  197,60,89,211                       // vmulps        %ymm3,%ymm8,%ymm10
+  .byte  197,108,89,219                      // vmulps        %ymm3,%ymm2,%ymm11
+  .byte  197,84,95,198                       // vmaxps        %ymm6,%ymm5,%ymm8
+  .byte  196,65,92,95,192                    // vmaxps        %ymm8,%ymm4,%ymm8
+  .byte  197,84,93,230                       // vminps        %ymm6,%ymm5,%ymm12
+  .byte  196,65,92,93,228                    // vminps        %ymm12,%ymm4,%ymm12
+  .byte  196,65,60,92,196                    // vsubps        %ymm12,%ymm8,%ymm8
+  .byte  197,60,89,227                       // vmulps        %ymm3,%ymm8,%ymm12
+  .byte  196,65,44,93,195                    // vminps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,93,232                    // vminps        %ymm8,%ymm9,%ymm13
+  .byte  196,65,44,95,195                    // vmaxps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,95,192                    // vmaxps        %ymm8,%ymm9,%ymm8
+  .byte  196,65,60,92,245                    // vsubps        %ymm13,%ymm8,%ymm14
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,12,194,248,0                 // vcmpeqps      %ymm8,%ymm14,%ymm15
+  .byte  196,65,52,92,205                    // vsubps        %ymm13,%ymm9,%ymm9
+  .byte  196,65,28,89,201                    // vmulps        %ymm9,%ymm12,%ymm9
+  .byte  196,65,52,94,206                    // vdivps        %ymm14,%ymm9,%ymm9
+  .byte  196,67,53,74,200,240                // vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  .byte  196,65,44,92,213                    // vsubps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,28,89,210                    // vmulps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,44,94,214                    // vdivps        %ymm14,%ymm10,%ymm10
+  .byte  196,67,45,74,208,240                // vblendvps     %ymm15,%ymm8,%ymm10,%ymm10
+  .byte  196,65,36,92,221                    // vsubps        %ymm13,%ymm11,%ymm11
+  .byte  196,65,28,89,219                    // vmulps        %ymm11,%ymm12,%ymm11
+  .byte  196,65,36,94,222                    // vdivps        %ymm14,%ymm11,%ymm11
+  .byte  196,67,37,74,224,240                // vblendvps     %ymm15,%ymm8,%ymm11,%ymm12
+  .byte  196,98,125,24,53,126,88,0,0         // vbroadcastss  0x587e(%rip),%ymm14        # 65a8 <_sk_callback_avx+0x1ac>
+  .byte  196,65,92,89,222                    // vmulps        %ymm14,%ymm4,%ymm11
+  .byte  196,98,125,24,61,116,88,0,0         // vbroadcastss  0x5874(%rip),%ymm15        # 65ac <_sk_callback_avx+0x1b0>
+  .byte  196,65,84,89,239                    // vmulps        %ymm15,%ymm5,%ymm13
+  .byte  196,65,36,88,221                    // vaddps        %ymm13,%ymm11,%ymm11
+  .byte  196,226,125,24,5,101,88,0,0         // vbroadcastss  0x5865(%rip),%ymm0        # 65b0 <_sk_callback_avx+0x1b4>
+  .byte  197,76,89,232                       // vmulps        %ymm0,%ymm6,%ymm13
+  .byte  196,65,36,88,221                    // vaddps        %ymm13,%ymm11,%ymm11
+  .byte  196,65,52,89,238                    // vmulps        %ymm14,%ymm9,%ymm13
+  .byte  196,193,44,89,215                   // vmulps        %ymm15,%ymm10,%ymm2
+  .byte  197,148,88,210                      // vaddps        %ymm2,%ymm13,%ymm2
+  .byte  197,28,89,232                       // vmulps        %ymm0,%ymm12,%ymm13
+  .byte  196,193,108,88,213                  // vaddps        %ymm13,%ymm2,%ymm2
+  .byte  197,36,89,219                       // vmulps        %ymm3,%ymm11,%ymm11
+  .byte  197,164,92,210                      // vsubps        %ymm2,%ymm11,%ymm2
+  .byte  197,52,88,202                       // vaddps        %ymm2,%ymm9,%ymm9
+  .byte  197,44,88,218                       // vaddps        %ymm2,%ymm10,%ymm11
+  .byte  197,28,88,226                       // vaddps        %ymm2,%ymm12,%ymm12
+  .byte  196,193,36,93,212                   // vminps        %ymm12,%ymm11,%ymm2
+  .byte  197,52,93,234                       // vminps        %ymm2,%ymm9,%ymm13
+  .byte  196,193,52,89,214                   // vmulps        %ymm14,%ymm9,%ymm2
+  .byte  196,65,36,89,215                    // vmulps        %ymm15,%ymm11,%ymm10
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  197,156,89,192                      // vmulps        %ymm0,%ymm12,%ymm0
+  .byte  197,124,88,210                      // vaddps        %ymm2,%ymm0,%ymm10
+  .byte  196,193,52,92,194                   // vsubps        %ymm10,%ymm9,%ymm0
+  .byte  197,172,89,192                      // vmulps        %ymm0,%ymm10,%ymm0
+  .byte  196,193,44,92,213                   // vsubps        %ymm13,%ymm10,%ymm2
+  .byte  197,252,94,194                      // vdivps        %ymm2,%ymm0,%ymm0
+  .byte  196,65,36,92,242                    // vsubps        %ymm10,%ymm11,%ymm14
+  .byte  196,65,44,89,246                    // vmulps        %ymm14,%ymm10,%ymm14
+  .byte  197,12,94,242                       // vdivps        %ymm2,%ymm14,%ymm14
+  .byte  196,65,28,92,250                    // vsubps        %ymm10,%ymm12,%ymm15
+  .byte  196,65,44,89,255                    // vmulps        %ymm15,%ymm10,%ymm15
+  .byte  197,132,94,210                      // vdivps        %ymm2,%ymm15,%ymm2
+  .byte  196,65,60,194,237,2                 // vcmpleps      %ymm13,%ymm8,%ymm13
+  .byte  196,65,44,88,246                    // vaddps        %ymm14,%ymm10,%ymm14
+  .byte  196,67,13,74,243,208                // vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  .byte  196,65,36,95,220                    // vmaxps        %ymm12,%ymm11,%ymm11
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  196,195,109,74,212,208              // vblendvps     %ymm13,%ymm12,%ymm2,%ymm2
+  .byte  197,172,88,192                      // vaddps        %ymm0,%ymm10,%ymm0
+  .byte  196,195,125,74,193,208              // vblendvps     %ymm13,%ymm9,%ymm0,%ymm0
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,52,95,203                    // vmaxps        %ymm11,%ymm9,%ymm9
+  .byte  196,65,124,92,218                   // vsubps        %ymm10,%ymm0,%ymm11
+  .byte  196,65,28,92,234                    // vsubps        %ymm10,%ymm12,%ymm13
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,28,194,249,1                 // vcmpltps      %ymm9,%ymm12,%ymm15
+  .byte  196,65,52,92,202                    // vsubps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,36,94,217                    // vdivps        %ymm9,%ymm11,%ymm11
+  .byte  196,65,44,88,219                    // vaddps        %ymm11,%ymm10,%ymm11
+  .byte  196,195,125,74,195,240              // vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  .byte  196,65,12,92,218                    // vsubps        %ymm10,%ymm14,%ymm11
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,36,94,217                    // vdivps        %ymm9,%ymm11,%ymm11
+  .byte  196,65,44,88,219                    // vaddps        %ymm11,%ymm10,%ymm11
+  .byte  196,67,13,74,219,240                // vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  .byte  196,65,108,92,242                   // vsubps        %ymm10,%ymm2,%ymm14
+  .byte  196,65,20,89,238                    // vmulps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,20,94,201                    // vdivps        %ymm9,%ymm13,%ymm9
+  .byte  196,65,44,88,201                    // vaddps        %ymm9,%ymm10,%ymm9
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,65,36,95,208                    // vmaxps        %ymm8,%ymm11,%ymm10
+  .byte  196,195,109,74,209,240              // vblendvps     %ymm15,%ymm9,%ymm2,%ymm2
+  .byte  196,193,108,95,208                  // vmaxps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,24,5,62,87,0,0           // vbroadcastss  0x573e(%rip),%ymm8        # 65b4 <_sk_callback_avx+0x1b8>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,180,89,201                      // vmulps        %ymm1,%ymm9,%ymm1
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,220                       // vmulps        %ymm4,%ymm8,%ymm11
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  197,244,88,192                      // vaddps        %ymm0,%ymm1,%ymm0
+  .byte  197,180,89,76,36,168                // vmulps        -0x58(%rsp),%ymm9,%ymm1
+  .byte  197,60,89,221                       // vmulps        %ymm5,%ymm8,%ymm11
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  197,52,89,76,36,200                 // vmulps        -0x38(%rsp),%ymm9,%ymm9
+  .byte  197,60,89,198                       // vmulps        %ymm6,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,188,88,210                      // vaddps        %ymm2,%ymm8,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_saturation_avx
+.globl _sk_saturation_avx
+FUNCTION(_sk_saturation_avx)
+_sk_saturation_avx:
+  .byte  197,124,40,193                      // vmovaps       %ymm1,%ymm8
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,100,89,204                      // vmulps        %ymm4,%ymm3,%ymm9
+  .byte  197,100,89,213                      // vmulps        %ymm5,%ymm3,%ymm10
+  .byte  197,100,89,222                      // vmulps        %ymm6,%ymm3,%ymm11
+  .byte  197,252,17,84,36,200                // vmovups       %ymm2,-0x38(%rsp)
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,252,17,68,36,168                // vmovups       %ymm0,-0x58(%rsp)
+  .byte  197,124,95,194                      // vmaxps        %ymm2,%ymm0,%ymm8
+  .byte  196,65,116,95,192                   // vmaxps        %ymm8,%ymm1,%ymm8
+  .byte  197,124,93,226                      // vminps        %ymm2,%ymm0,%ymm12
+  .byte  196,65,116,93,228                   // vminps        %ymm12,%ymm1,%ymm12
+  .byte  196,65,60,92,196                    // vsubps        %ymm12,%ymm8,%ymm8
+  .byte  197,60,89,231                       // vmulps        %ymm7,%ymm8,%ymm12
+  .byte  196,65,44,93,195                    // vminps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,93,232                    // vminps        %ymm8,%ymm9,%ymm13
+  .byte  196,65,44,95,195                    // vmaxps        %ymm11,%ymm10,%ymm8
+  .byte  196,65,52,95,192                    // vmaxps        %ymm8,%ymm9,%ymm8
+  .byte  196,65,60,92,245                    // vsubps        %ymm13,%ymm8,%ymm14
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,12,194,248,0                 // vcmpeqps      %ymm8,%ymm14,%ymm15
+  .byte  196,65,52,92,205                    // vsubps        %ymm13,%ymm9,%ymm9
+  .byte  196,65,28,89,201                    // vmulps        %ymm9,%ymm12,%ymm9
+  .byte  196,65,52,94,206                    // vdivps        %ymm14,%ymm9,%ymm9
+  .byte  196,67,53,74,200,240                // vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  .byte  196,65,44,92,213                    // vsubps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,28,89,210                    // vmulps        %ymm10,%ymm12,%ymm10
+  .byte  196,65,44,94,214                    // vdivps        %ymm14,%ymm10,%ymm10
+  .byte  196,67,45,74,208,240                // vblendvps     %ymm15,%ymm8,%ymm10,%ymm10
+  .byte  196,65,36,92,221                    // vsubps        %ymm13,%ymm11,%ymm11
+  .byte  196,65,28,89,219                    // vmulps        %ymm11,%ymm12,%ymm11
+  .byte  196,65,36,94,222                    // vdivps        %ymm14,%ymm11,%ymm11
+  .byte  196,67,37,74,224,240                // vblendvps     %ymm15,%ymm8,%ymm11,%ymm12
+  .byte  196,98,125,24,53,76,86,0,0          // vbroadcastss  0x564c(%rip),%ymm14        # 65b8 <_sk_callback_avx+0x1bc>
+  .byte  196,65,92,89,222                    // vmulps        %ymm14,%ymm4,%ymm11
+  .byte  196,98,125,24,61,66,86,0,0          // vbroadcastss  0x5642(%rip),%ymm15        # 65bc <_sk_callback_avx+0x1c0>
+  .byte  196,65,84,89,239                    // vmulps        %ymm15,%ymm5,%ymm13
+  .byte  196,65,36,88,221                    // vaddps        %ymm13,%ymm11,%ymm11
+  .byte  196,226,125,24,5,51,86,0,0          // vbroadcastss  0x5633(%rip),%ymm0        # 65c0 <_sk_callback_avx+0x1c4>
+  .byte  197,76,89,232                       // vmulps        %ymm0,%ymm6,%ymm13
+  .byte  196,65,36,88,221                    // vaddps        %ymm13,%ymm11,%ymm11
+  .byte  196,65,52,89,238                    // vmulps        %ymm14,%ymm9,%ymm13
+  .byte  196,193,44,89,215                   // vmulps        %ymm15,%ymm10,%ymm2
+  .byte  197,148,88,210                      // vaddps        %ymm2,%ymm13,%ymm2
+  .byte  197,28,89,232                       // vmulps        %ymm0,%ymm12,%ymm13
+  .byte  196,193,108,88,213                  // vaddps        %ymm13,%ymm2,%ymm2
+  .byte  197,36,89,219                       // vmulps        %ymm3,%ymm11,%ymm11
+  .byte  197,164,92,210                      // vsubps        %ymm2,%ymm11,%ymm2
+  .byte  197,52,88,202                       // vaddps        %ymm2,%ymm9,%ymm9
+  .byte  197,44,88,218                       // vaddps        %ymm2,%ymm10,%ymm11
+  .byte  197,28,88,226                       // vaddps        %ymm2,%ymm12,%ymm12
+  .byte  196,193,36,93,212                   // vminps        %ymm12,%ymm11,%ymm2
+  .byte  197,52,93,234                       // vminps        %ymm2,%ymm9,%ymm13
+  .byte  196,193,52,89,214                   // vmulps        %ymm14,%ymm9,%ymm2
+  .byte  196,65,36,89,215                    // vmulps        %ymm15,%ymm11,%ymm10
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  197,156,89,192                      // vmulps        %ymm0,%ymm12,%ymm0
+  .byte  197,124,88,210                      // vaddps        %ymm2,%ymm0,%ymm10
+  .byte  196,193,52,92,194                   // vsubps        %ymm10,%ymm9,%ymm0
+  .byte  197,172,89,192                      // vmulps        %ymm0,%ymm10,%ymm0
+  .byte  196,193,44,92,213                   // vsubps        %ymm13,%ymm10,%ymm2
+  .byte  197,252,94,194                      // vdivps        %ymm2,%ymm0,%ymm0
+  .byte  196,65,36,92,242                    // vsubps        %ymm10,%ymm11,%ymm14
+  .byte  196,65,44,89,246                    // vmulps        %ymm14,%ymm10,%ymm14
+  .byte  197,12,94,242                       // vdivps        %ymm2,%ymm14,%ymm14
+  .byte  196,65,28,92,250                    // vsubps        %ymm10,%ymm12,%ymm15
+  .byte  196,65,44,89,255                    // vmulps        %ymm15,%ymm10,%ymm15
+  .byte  197,132,94,210                      // vdivps        %ymm2,%ymm15,%ymm2
+  .byte  196,65,60,194,237,2                 // vcmpleps      %ymm13,%ymm8,%ymm13
+  .byte  196,65,44,88,246                    // vaddps        %ymm14,%ymm10,%ymm14
+  .byte  196,67,13,74,243,208                // vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  .byte  196,65,36,95,220                    // vmaxps        %ymm12,%ymm11,%ymm11
+  .byte  197,172,88,210                      // vaddps        %ymm2,%ymm10,%ymm2
+  .byte  196,195,109,74,212,208              // vblendvps     %ymm13,%ymm12,%ymm2,%ymm2
+  .byte  197,172,88,192                      // vaddps        %ymm0,%ymm10,%ymm0
+  .byte  196,195,125,74,193,208              // vblendvps     %ymm13,%ymm9,%ymm0,%ymm0
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,52,95,203                    // vmaxps        %ymm11,%ymm9,%ymm9
+  .byte  196,65,124,92,218                   // vsubps        %ymm10,%ymm0,%ymm11
+  .byte  196,65,28,92,234                    // vsubps        %ymm10,%ymm12,%ymm13
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,28,194,249,1                 // vcmpltps      %ymm9,%ymm12,%ymm15
+  .byte  196,65,52,92,202                    // vsubps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,36,94,217                    // vdivps        %ymm9,%ymm11,%ymm11
+  .byte  196,65,44,88,219                    // vaddps        %ymm11,%ymm10,%ymm11
+  .byte  196,195,125,74,195,240              // vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  .byte  196,65,12,92,218                    // vsubps        %ymm10,%ymm14,%ymm11
+  .byte  196,65,20,89,219                    // vmulps        %ymm11,%ymm13,%ymm11
+  .byte  196,65,36,94,217                    // vdivps        %ymm9,%ymm11,%ymm11
+  .byte  196,65,44,88,219                    // vaddps        %ymm11,%ymm10,%ymm11
+  .byte  196,67,13,74,219,240                // vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  .byte  196,65,108,92,242                   // vsubps        %ymm10,%ymm2,%ymm14
+  .byte  196,65,20,89,238                    // vmulps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,20,94,201                    // vdivps        %ymm9,%ymm13,%ymm9
+  .byte  196,65,44,88,201                    // vaddps        %ymm9,%ymm10,%ymm9
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,65,36,95,208                    // vmaxps        %ymm8,%ymm11,%ymm10
+  .byte  196,195,109,74,209,240              // vblendvps     %ymm15,%ymm9,%ymm2,%ymm2
+  .byte  196,193,108,95,208                  // vmaxps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,24,5,12,85,0,0           // vbroadcastss  0x550c(%rip),%ymm8        # 65c4 <_sk_callback_avx+0x1c8>
+  .byte  197,60,92,207                       // vsubps        %ymm7,%ymm8,%ymm9
+  .byte  197,180,89,201                      // vmulps        %ymm1,%ymm9,%ymm1
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  197,60,89,220                       // vmulps        %ymm4,%ymm8,%ymm11
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  197,244,88,192                      // vaddps        %ymm0,%ymm1,%ymm0
+  .byte  197,180,89,76,36,168                // vmulps        -0x58(%rsp),%ymm9,%ymm1
+  .byte  197,60,89,221                       // vmulps        %ymm5,%ymm8,%ymm11
+  .byte  197,164,88,201                      // vaddps        %ymm1,%ymm11,%ymm1
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  197,52,89,76,36,200                 // vmulps        -0x38(%rsp),%ymm9,%ymm9
+  .byte  197,60,89,198                       // vmulps        %ymm6,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,188,88,210                      // vaddps        %ymm2,%ymm8,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_color_avx
+.globl _sk_color_avx
+FUNCTION(_sk_color_avx)
+_sk_color_avx:
+  .byte  197,252,17,84,36,136                // vmovups       %ymm2,-0x78(%rsp)
+  .byte  197,252,17,76,36,200                // vmovups       %ymm1,-0x38(%rsp)
+  .byte  197,252,17,68,36,168                // vmovups       %ymm0,-0x58(%rsp)
+  .byte  197,124,89,199                      // vmulps        %ymm7,%ymm0,%ymm8
+  .byte  197,116,89,207                      // vmulps        %ymm7,%ymm1,%ymm9
+  .byte  196,98,125,24,45,162,84,0,0         // vbroadcastss  0x54a2(%rip),%ymm13        # 65c8 <_sk_callback_avx+0x1cc>
+  .byte  196,65,92,89,213                    // vmulps        %ymm13,%ymm4,%ymm10
+  .byte  196,98,125,24,53,152,84,0,0         // vbroadcastss  0x5498(%rip),%ymm14        # 65cc <_sk_callback_avx+0x1d0>
+  .byte  196,65,84,89,222                    // vmulps        %ymm14,%ymm5,%ymm11
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  196,98,125,24,61,137,84,0,0         // vbroadcastss  0x5489(%rip),%ymm15        # 65d0 <_sk_callback_avx+0x1d4>
+  .byte  196,65,76,89,223                    // vmulps        %ymm15,%ymm6,%ymm11
+  .byte  196,193,44,88,195                   // vaddps        %ymm11,%ymm10,%ymm0
+  .byte  196,65,60,89,221                    // vmulps        %ymm13,%ymm8,%ymm11
+  .byte  196,65,52,89,230                    // vmulps        %ymm14,%ymm9,%ymm12
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  197,108,89,231                      // vmulps        %ymm7,%ymm2,%ymm12
+  .byte  196,65,28,89,215                    // vmulps        %ymm15,%ymm12,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  196,193,124,92,194                  // vsubps        %ymm10,%ymm0,%ymm0
+  .byte  197,60,88,192                       // vaddps        %ymm0,%ymm8,%ymm8
+  .byte  197,52,88,208                       // vaddps        %ymm0,%ymm9,%ymm10
+  .byte  197,28,88,216                       // vaddps        %ymm0,%ymm12,%ymm11
+  .byte  196,193,44,93,195                   // vminps        %ymm11,%ymm10,%ymm0
+  .byte  197,60,93,224                       // vminps        %ymm0,%ymm8,%ymm12
+  .byte  196,193,60,89,197                   // vmulps        %ymm13,%ymm8,%ymm0
+  .byte  196,65,44,89,206                    // vmulps        %ymm14,%ymm10,%ymm9
+  .byte  196,193,124,88,193                  // vaddps        %ymm9,%ymm0,%ymm0
+  .byte  196,65,36,89,207                    // vmulps        %ymm15,%ymm11,%ymm9
+  .byte  197,52,88,200                       // vaddps        %ymm0,%ymm9,%ymm9
+  .byte  196,193,60,92,193                   // vsubps        %ymm9,%ymm8,%ymm0
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  196,65,52,92,236                    // vsubps        %ymm12,%ymm9,%ymm13
+  .byte  196,193,124,94,197                  // vdivps        %ymm13,%ymm0,%ymm0
+  .byte  196,65,44,92,241                    // vsubps        %ymm9,%ymm10,%ymm14
+  .byte  196,65,52,89,246                    // vmulps        %ymm14,%ymm9,%ymm14
+  .byte  196,65,12,94,245                    // vdivps        %ymm13,%ymm14,%ymm14
+  .byte  196,65,36,92,249                    // vsubps        %ymm9,%ymm11,%ymm15
+  .byte  196,65,52,89,255                    // vmulps        %ymm15,%ymm9,%ymm15
+  .byte  196,65,4,94,237                     // vdivps        %ymm13,%ymm15,%ymm13
+  .byte  196,65,4,87,255                     // vxorps        %ymm15,%ymm15,%ymm15
+  .byte  196,65,4,194,228,2                  // vcmpleps      %ymm12,%ymm15,%ymm12
+  .byte  196,65,52,88,246                    // vaddps        %ymm14,%ymm9,%ymm14
+  .byte  196,67,13,74,242,192                // vblendvps     %ymm12,%ymm10,%ymm14,%ymm14
+  .byte  196,65,44,95,211                    // vmaxps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,52,88,237                    // vaddps        %ymm13,%ymm9,%ymm13
+  .byte  196,67,21,74,219,192                // vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,200,192              // vblendvps     %ymm12,%ymm8,%ymm0,%ymm1
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,60,95,194                    // vmaxps        %ymm10,%ymm8,%ymm8
+  .byte  196,65,116,92,209                   // vsubps        %ymm9,%ymm1,%ymm10
+  .byte  196,65,28,92,233                    // vsubps        %ymm9,%ymm12,%ymm13
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,193,28,194,192,1                // vcmpltps      %ymm8,%ymm12,%ymm0
+  .byte  196,65,60,92,193                    // vsubps        %ymm9,%ymm8,%ymm8
+  .byte  196,65,44,94,208                    // vdivps        %ymm8,%ymm10,%ymm10
+  .byte  196,65,52,88,210                    // vaddps        %ymm10,%ymm9,%ymm10
+  .byte  196,195,117,74,202,0                // vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  .byte  196,65,12,92,209                    // vsubps        %ymm9,%ymm14,%ymm10
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,65,44,94,208                    // vdivps        %ymm8,%ymm10,%ymm10
+  .byte  196,65,52,88,210                    // vaddps        %ymm10,%ymm9,%ymm10
+  .byte  196,67,13,74,210,0                  // vblendvps     %ymm0,%ymm10,%ymm14,%ymm10
+  .byte  196,65,36,92,241                    // vsubps        %ymm9,%ymm11,%ymm14
+  .byte  196,65,20,89,238                    // vmulps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,20,94,192                    // vdivps        %ymm8,%ymm13,%ymm8
+  .byte  196,65,52,88,192                    // vaddps        %ymm8,%ymm9,%ymm8
+  .byte  196,193,116,95,207                  // vmaxps        %ymm15,%ymm1,%ymm1
+  .byte  196,65,44,95,207                    // vmaxps        %ymm15,%ymm10,%ymm9
+  .byte  196,195,37,74,192,0                 // vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  .byte  196,65,124,95,199                   // vmaxps        %ymm15,%ymm0,%ymm8
+  .byte  196,226,125,24,5,80,83,0,0          // vbroadcastss  0x5350(%rip),%ymm0        # 65d4 <_sk_callback_avx+0x1d8>
+  .byte  197,124,92,215                      // vsubps        %ymm7,%ymm0,%ymm10
+  .byte  197,172,89,84,36,168                // vmulps        -0x58(%rsp),%ymm10,%ymm2
+  .byte  197,124,92,219                      // vsubps        %ymm3,%ymm0,%ymm11
+  .byte  197,164,89,196                      // vmulps        %ymm4,%ymm11,%ymm0
+  .byte  197,252,88,194                      // vaddps        %ymm2,%ymm0,%ymm0
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,172,89,76,36,200                // vmulps        -0x38(%rsp),%ymm10,%ymm1
+  .byte  197,164,89,213                      // vmulps        %ymm5,%ymm11,%ymm2
+  .byte  197,236,88,201                      // vaddps        %ymm1,%ymm2,%ymm1
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  197,172,89,84,36,136                // vmulps        -0x78(%rsp),%ymm10,%ymm2
+  .byte  197,36,89,206                       // vmulps        %ymm6,%ymm11,%ymm9
+  .byte  197,180,88,210                      // vaddps        %ymm2,%ymm9,%ymm2
+  .byte  196,193,108,88,208                  // vaddps        %ymm8,%ymm2,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminosity_avx
+.globl _sk_luminosity_avx
+FUNCTION(_sk_luminosity_avx)
+_sk_luminosity_avx:
+  .byte  197,124,40,226                      // vmovaps       %ymm2,%ymm12
+  .byte  197,124,17,100,36,168               // vmovups       %ymm12,-0x58(%rsp)
+  .byte  197,252,17,76,36,200                // vmovups       %ymm1,-0x38(%rsp)
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,100,89,196                      // vmulps        %ymm4,%ymm3,%ymm8
+  .byte  197,100,89,205                      // vmulps        %ymm5,%ymm3,%ymm9
+  .byte  196,98,125,24,45,226,82,0,0         // vbroadcastss  0x52e2(%rip),%ymm13        # 65d8 <_sk_callback_avx+0x1dc>
+  .byte  196,65,108,89,213                   // vmulps        %ymm13,%ymm2,%ymm10
+  .byte  196,98,125,24,53,216,82,0,0         // vbroadcastss  0x52d8(%rip),%ymm14        # 65dc <_sk_callback_avx+0x1e0>
+  .byte  196,65,116,89,222                   // vmulps        %ymm14,%ymm1,%ymm11
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  196,98,125,24,61,201,82,0,0         // vbroadcastss  0x52c9(%rip),%ymm15        # 65e0 <_sk_callback_avx+0x1e4>
+  .byte  196,65,28,89,223                    // vmulps        %ymm15,%ymm12,%ymm11
+  .byte  196,193,44,88,195                   // vaddps        %ymm11,%ymm10,%ymm0
+  .byte  196,65,60,89,221                    // vmulps        %ymm13,%ymm8,%ymm11
+  .byte  196,65,52,89,230                    // vmulps        %ymm14,%ymm9,%ymm12
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  197,100,89,230                      // vmulps        %ymm6,%ymm3,%ymm12
+  .byte  196,65,28,89,215                    // vmulps        %ymm15,%ymm12,%ymm10
+  .byte  196,65,36,88,210                    // vaddps        %ymm10,%ymm11,%ymm10
+  .byte  197,252,89,199                      // vmulps        %ymm7,%ymm0,%ymm0
+  .byte  196,193,124,92,194                  // vsubps        %ymm10,%ymm0,%ymm0
+  .byte  197,60,88,192                       // vaddps        %ymm0,%ymm8,%ymm8
+  .byte  197,52,88,208                       // vaddps        %ymm0,%ymm9,%ymm10
+  .byte  197,28,88,216                       // vaddps        %ymm0,%ymm12,%ymm11
+  .byte  196,193,44,93,195                   // vminps        %ymm11,%ymm10,%ymm0
+  .byte  197,60,93,224                       // vminps        %ymm0,%ymm8,%ymm12
+  .byte  196,193,60,89,197                   // vmulps        %ymm13,%ymm8,%ymm0
+  .byte  196,65,44,89,206                    // vmulps        %ymm14,%ymm10,%ymm9
+  .byte  196,193,124,88,193                  // vaddps        %ymm9,%ymm0,%ymm0
+  .byte  196,65,36,89,207                    // vmulps        %ymm15,%ymm11,%ymm9
+  .byte  197,52,88,200                       // vaddps        %ymm0,%ymm9,%ymm9
+  .byte  196,193,60,92,193                   // vsubps        %ymm9,%ymm8,%ymm0
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  196,65,52,92,236                    // vsubps        %ymm12,%ymm9,%ymm13
+  .byte  196,193,124,94,197                  // vdivps        %ymm13,%ymm0,%ymm0
+  .byte  196,65,44,92,241                    // vsubps        %ymm9,%ymm10,%ymm14
+  .byte  196,65,52,89,246                    // vmulps        %ymm14,%ymm9,%ymm14
+  .byte  196,65,12,94,245                    // vdivps        %ymm13,%ymm14,%ymm14
+  .byte  196,65,36,92,249                    // vsubps        %ymm9,%ymm11,%ymm15
+  .byte  196,65,52,89,255                    // vmulps        %ymm15,%ymm9,%ymm15
+  .byte  196,65,4,94,237                     // vdivps        %ymm13,%ymm15,%ymm13
+  .byte  196,65,4,87,255                     // vxorps        %ymm15,%ymm15,%ymm15
+  .byte  196,65,4,194,228,2                  // vcmpleps      %ymm12,%ymm15,%ymm12
+  .byte  196,65,52,88,246                    // vaddps        %ymm14,%ymm9,%ymm14
+  .byte  196,67,13,74,242,192                // vblendvps     %ymm12,%ymm10,%ymm14,%ymm14
+  .byte  196,65,44,95,211                    // vmaxps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,52,88,237                    // vaddps        %ymm13,%ymm9,%ymm13
+  .byte  196,67,21,74,219,192                // vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  196,195,125,74,200,192              // vblendvps     %ymm12,%ymm8,%ymm0,%ymm1
+  .byte  197,100,89,231                      // vmulps        %ymm7,%ymm3,%ymm12
+  .byte  196,65,60,95,194                    // vmaxps        %ymm10,%ymm8,%ymm8
+  .byte  196,65,116,92,209                   // vsubps        %ymm9,%ymm1,%ymm10
+  .byte  196,65,28,92,233                    // vsubps        %ymm9,%ymm12,%ymm13
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,193,28,194,192,1                // vcmpltps      %ymm8,%ymm12,%ymm0
+  .byte  196,65,60,92,193                    // vsubps        %ymm9,%ymm8,%ymm8
+  .byte  196,65,44,94,208                    // vdivps        %ymm8,%ymm10,%ymm10
+  .byte  196,65,52,88,210                    // vaddps        %ymm10,%ymm9,%ymm10
+  .byte  196,195,117,74,202,0                // vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  .byte  196,65,12,92,209                    // vsubps        %ymm9,%ymm14,%ymm10
+  .byte  196,65,20,89,210                    // vmulps        %ymm10,%ymm13,%ymm10
+  .byte  196,65,44,94,208                    // vdivps        %ymm8,%ymm10,%ymm10
+  .byte  196,65,52,88,210                    // vaddps        %ymm10,%ymm9,%ymm10
+  .byte  196,67,13,74,210,0                  // vblendvps     %ymm0,%ymm10,%ymm14,%ymm10
+  .byte  196,65,36,92,241                    // vsubps        %ymm9,%ymm11,%ymm14
+  .byte  196,65,20,89,238                    // vmulps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,20,94,192                    // vdivps        %ymm8,%ymm13,%ymm8
+  .byte  196,65,52,88,192                    // vaddps        %ymm8,%ymm9,%ymm8
+  .byte  196,193,116,95,207                  // vmaxps        %ymm15,%ymm1,%ymm1
+  .byte  196,65,44,95,207                    // vmaxps        %ymm15,%ymm10,%ymm9
+  .byte  196,195,37,74,192,0                 // vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  .byte  196,65,124,95,199                   // vmaxps        %ymm15,%ymm0,%ymm8
+  .byte  196,226,125,24,5,144,81,0,0         // vbroadcastss  0x5190(%rip),%ymm0        # 65e4 <_sk_callback_avx+0x1e8>
+  .byte  197,124,92,215                      // vsubps        %ymm7,%ymm0,%ymm10
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  197,124,92,219                      // vsubps        %ymm3,%ymm0,%ymm11
+  .byte  197,164,89,196                      // vmulps        %ymm4,%ymm11,%ymm0
+  .byte  197,252,88,194                      // vaddps        %ymm2,%ymm0,%ymm0
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,172,89,76,36,200                // vmulps        -0x38(%rsp),%ymm10,%ymm1
+  .byte  197,164,89,213                      // vmulps        %ymm5,%ymm11,%ymm2
+  .byte  197,236,88,201                      // vaddps        %ymm1,%ymm2,%ymm1
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  197,172,89,84,36,168                // vmulps        -0x58(%rsp),%ymm10,%ymm2
+  .byte  197,36,89,206                       // vmulps        %ymm6,%ymm11,%ymm9
+  .byte  197,180,88,210                      // vaddps        %ymm2,%ymm9,%ymm2
+  .byte  196,193,108,88,208                  // vaddps        %ymm8,%ymm2,%ymm2
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,92,220                  // vsubps        %ymm12,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_rgba_8888_avx
+.globl _sk_srcover_rgba_8888_avx
+FUNCTION(_sk_srcover_rgba_8888_avx)
+_sk_srcover_rgba_8888_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,43,1,0,0                     // jne           15e3 <_sk_srcover_rgba_8888_avx+0x144>
+  .byte  196,193,124,16,58                   // vmovups       (%r10),%ymm7
+  .byte  197,124,40,13,91,85,0,0             // vmovaps       0x555b(%rip),%ymm9        # 6a20 <_sk_callback_avx+0x624>
+  .byte  196,193,68,84,225                   // vandps        %ymm9,%ymm7,%ymm4
+  .byte  197,252,91,228                      // vcvtdq2ps     %ymm4,%ymm4
+  .byte  197,209,114,215,8                   // vpsrld        $0x8,%xmm7,%xmm5
+  .byte  196,195,125,25,248,1                // vextractf128  $0x1,%ymm7,%xmm8
+  .byte  196,193,73,114,208,8                // vpsrld        $0x8,%xmm8,%xmm6
+  .byte  196,227,85,24,238,1                 // vinsertf128   $0x1,%xmm6,%ymm5,%ymm5
+  .byte  196,193,84,84,233                   // vandps        %ymm9,%ymm5,%ymm5
+  .byte  197,252,91,237                      // vcvtdq2ps     %ymm5,%ymm5
+  .byte  197,169,114,215,16                  // vpsrld        $0x10,%xmm7,%xmm10
+  .byte  196,193,73,114,208,16               // vpsrld        $0x10,%xmm8,%xmm6
+  .byte  196,227,45,24,246,1                 // vinsertf128   $0x1,%xmm6,%ymm10,%ymm6
+  .byte  196,193,76,84,241                   // vandps        %ymm9,%ymm6,%ymm6
+  .byte  197,252,91,246                      // vcvtdq2ps     %ymm6,%ymm6
+  .byte  197,177,114,215,24                  // vpsrld        $0x18,%xmm7,%xmm9
+  .byte  196,193,65,114,208,24               // vpsrld        $0x18,%xmm8,%xmm7
+  .byte  196,227,53,24,255,1                 // vinsertf128   $0x1,%xmm7,%ymm9,%ymm7
+  .byte  197,252,91,255                      // vcvtdq2ps     %ymm7,%ymm7
+  .byte  196,98,125,24,5,194,80,0,0          // vbroadcastss  0x50c2(%rip),%ymm8        # 65e8 <_sk_callback_avx+0x1ec>
+  .byte  197,60,92,195                       // vsubps        %ymm3,%ymm8,%ymm8
+  .byte  196,98,125,24,13,185,80,0,0         // vbroadcastss  0x50b9(%rip),%ymm9        # 65ec <_sk_callback_avx+0x1f0>
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  197,60,89,212                       // vmulps        %ymm4,%ymm8,%ymm10
+  .byte  196,193,124,88,194                  // vaddps        %ymm10,%ymm0,%ymm0
+  .byte  196,193,116,89,201                  // vmulps        %ymm9,%ymm1,%ymm1
+  .byte  197,60,89,213                       // vmulps        %ymm5,%ymm8,%ymm10
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  196,193,108,89,209                  // vmulps        %ymm9,%ymm2,%ymm2
+  .byte  197,60,89,214                       // vmulps        %ymm6,%ymm8,%ymm10
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  196,193,100,89,217                  // vmulps        %ymm9,%ymm3,%ymm3
+  .byte  197,60,89,199                       // vmulps        %ymm7,%ymm8,%ymm8
+  .byte  196,193,100,88,216                  // vaddps        %ymm8,%ymm3,%ymm3
+  .byte  197,125,91,192                      // vcvtps2dq     %ymm0,%ymm8
+  .byte  197,125,91,201                      // vcvtps2dq     %ymm1,%ymm9
+  .byte  196,193,41,114,241,8                // vpslld        $0x8,%xmm9,%xmm10
+  .byte  196,67,125,25,201,1                 // vextractf128  $0x1,%ymm9,%xmm9
+  .byte  196,193,49,114,241,8                // vpslld        $0x8,%xmm9,%xmm9
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,65,53,86,192                    // vorpd         %ymm8,%ymm9,%ymm8
+  .byte  197,125,91,202                      // vcvtps2dq     %ymm2,%ymm9
+  .byte  196,193,41,114,241,16               // vpslld        $0x10,%xmm9,%xmm10
+  .byte  196,67,125,25,201,1                 // vextractf128  $0x1,%ymm9,%xmm9
+  .byte  196,193,49,114,241,16               // vpslld        $0x10,%xmm9,%xmm9
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  197,125,91,211                      // vcvtps2dq     %ymm3,%ymm10
+  .byte  196,193,33,114,242,24               // vpslld        $0x18,%xmm10,%xmm11
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,242,24               // vpslld        $0x18,%xmm10,%xmm10
+  .byte  196,67,37,24,210,1                  // vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  .byte  196,65,53,86,202                    // vorpd         %ymm10,%ymm9,%ymm9
+  .byte  196,65,61,86,193                    // vorpd         %ymm9,%ymm8,%ymm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,87                              // jne           162e <_sk_srcover_rgba_8888_avx+0x18f>
+  .byte  196,65,124,17,2                     // vmovups       %ymm8,(%r10)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,225,249,110,224                 // vmovq         %rax,%xmm4
+  .byte  196,226,121,48,228                  // vpmovzxbw     %xmm4,%xmm4
+  .byte  196,226,89,0,45,101,83,0,0          // vpshufb       0x5365(%rip),%xmm4,%xmm5        # 6970 <_sk_callback_avx+0x574>
+  .byte  196,226,121,33,237                  // vpmovsxbd     %xmm5,%xmm5
+  .byte  196,226,89,0,37,103,83,0,0          // vpshufb       0x5367(%rip),%xmm4,%xmm4        # 6980 <_sk_callback_avx+0x584>
+  .byte  196,226,121,33,228                  // vpmovsxbd     %xmm4,%xmm4
+  .byte  196,227,85,24,228,1                 // vinsertf128   $0x1,%xmm4,%ymm5,%ymm4
+  .byte  196,194,93,44,58                    // vmaskmovps    (%r10),%ymm4,%ymm7
+  .byte  233,143,254,255,255                 // jmpq          14bd <_sk_srcover_rgba_8888_avx+0x1e>
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,97,249,110,200                  // vmovq         %rax,%xmm9
+  .byte  196,66,121,48,201                   // vpmovzxbw     %xmm9,%xmm9
+  .byte  196,98,49,0,21,26,83,0,0            // vpshufb       0x531a(%rip),%xmm9,%xmm10        # 6970 <_sk_callback_avx+0x574>
+  .byte  196,66,121,33,210                   // vpmovsxbd     %xmm10,%xmm10
+  .byte  196,98,49,0,13,28,83,0,0            // vpshufb       0x531c(%rip),%xmm9,%xmm9        # 6980 <_sk_callback_avx+0x584>
+  .byte  196,66,121,33,201                   // vpmovsxbd     %xmm9,%xmm9
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,66,53,46,2                      // vmaskmovps    %ymm8,%ymm9,(%r10)
+  .byte  233,99,255,255,255                  // jmpq          15dc <_sk_srcover_rgba_8888_avx+0x13d>
+
+HIDDEN _sk_clamp_0_avx
+.globl _sk_clamp_0_avx
+FUNCTION(_sk_clamp_0_avx)
+_sk_clamp_0_avx:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,193,116,95,200                  // vmaxps        %ymm8,%ymm1,%ymm1
+  .byte  196,193,108,95,208                  // vmaxps        %ymm8,%ymm2,%ymm2
+  .byte  196,193,100,95,216                  // vmaxps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_1_avx
+.globl _sk_clamp_1_avx
+FUNCTION(_sk_clamp_1_avx)
+_sk_clamp_1_avx:
+  .byte  196,98,125,24,5,81,79,0,0           // vbroadcastss  0x4f51(%rip),%ymm8        # 65f0 <_sk_callback_avx+0x1f4>
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  196,193,116,93,200                  // vminps        %ymm8,%ymm1,%ymm1
+  .byte  196,193,108,93,208                  // vminps        %ymm8,%ymm2,%ymm2
+  .byte  196,193,100,93,216                  // vminps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_a_avx
+.globl _sk_clamp_a_avx
+FUNCTION(_sk_clamp_a_avx)
+_sk_clamp_a_avx:
+  .byte  196,98,125,24,5,52,79,0,0           // vbroadcastss  0x4f34(%rip),%ymm8        # 65f4 <_sk_callback_avx+0x1f8>
+  .byte  196,193,100,93,216                  // vminps        %ymm8,%ymm3,%ymm3
+  .byte  197,252,93,195                      // vminps        %ymm3,%ymm0,%ymm0
+  .byte  197,244,93,203                      // vminps        %ymm3,%ymm1,%ymm1
+  .byte  197,236,93,211                      // vminps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_set_rgb_avx
+.globl _sk_set_rgb_avx
+FUNCTION(_sk_set_rgb_avx)
+_sk_set_rgb_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,0                    // vbroadcastss  (%rax),%ymm0
+  .byte  196,226,125,24,72,4                 // vbroadcastss  0x4(%rax),%ymm1
+  .byte  196,226,125,24,80,8                 // vbroadcastss  0x8(%rax),%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_rb_avx
+.globl _sk_swap_rb_avx
+FUNCTION(_sk_swap_rb_avx)
+_sk_swap_rb_avx:
+  .byte  197,124,40,192                      // vmovaps       %ymm0,%ymm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,194                      // vmovaps       %ymm2,%ymm0
+  .byte  197,124,41,194                      // vmovaps       %ymm8,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_avx
+.globl _sk_swap_avx
+FUNCTION(_sk_swap_avx)
+_sk_swap_avx:
+  .byte  197,124,40,195                      // vmovaps       %ymm3,%ymm8
+  .byte  197,124,40,202                      // vmovaps       %ymm2,%ymm9
+  .byte  197,124,40,209                      // vmovaps       %ymm1,%ymm10
+  .byte  197,124,40,216                      // vmovaps       %ymm0,%ymm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,196                      // vmovaps       %ymm4,%ymm0
+  .byte  197,252,40,205                      // vmovaps       %ymm5,%ymm1
+  .byte  197,252,40,214                      // vmovaps       %ymm6,%ymm2
+  .byte  197,252,40,223                      // vmovaps       %ymm7,%ymm3
+  .byte  197,124,41,220                      // vmovaps       %ymm11,%ymm4
+  .byte  197,124,41,213                      // vmovaps       %ymm10,%ymm5
+  .byte  197,124,41,206                      // vmovaps       %ymm9,%ymm6
+  .byte  197,124,41,199                      // vmovaps       %ymm8,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_src_dst_avx
+.globl _sk_move_src_dst_avx
+FUNCTION(_sk_move_src_dst_avx)
+_sk_move_src_dst_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,224                      // vmovaps       %ymm0,%ymm4
+  .byte  197,252,40,233                      // vmovaps       %ymm1,%ymm5
+  .byte  197,252,40,242                      // vmovaps       %ymm2,%ymm6
+  .byte  197,252,40,251                      // vmovaps       %ymm3,%ymm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_dst_src_avx
+.globl _sk_move_dst_src_avx
+FUNCTION(_sk_move_dst_src_avx)
+_sk_move_dst_src_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,196                      // vmovaps       %ymm4,%ymm0
+  .byte  197,252,40,205                      // vmovaps       %ymm5,%ymm1
+  .byte  197,252,40,214                      // vmovaps       %ymm6,%ymm2
+  .byte  197,252,40,223                      // vmovaps       %ymm7,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_premul_avx
+.globl _sk_premul_avx
+FUNCTION(_sk_premul_avx)
+_sk_premul_avx:
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  197,236,89,211                      // vmulps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_unpremul_avx
+.globl _sk_unpremul_avx
+FUNCTION(_sk_unpremul_avx)
+_sk_unpremul_avx:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,65,100,194,200,0                // vcmpeqps      %ymm8,%ymm3,%ymm9
+  .byte  196,98,125,24,21,124,78,0,0         // vbroadcastss  0x4e7c(%rip),%ymm10        # 65f8 <_sk_callback_avx+0x1fc>
+  .byte  197,44,94,211                       // vdivps        %ymm3,%ymm10,%ymm10
+  .byte  196,67,45,74,192,144                // vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_from_srgb_avx
+.globl _sk_from_srgb_avx
+FUNCTION(_sk_from_srgb_avx)
+_sk_from_srgb_avx:
+  .byte  196,98,125,24,5,93,78,0,0           // vbroadcastss  0x4e5d(%rip),%ymm8        # 65fc <_sk_callback_avx+0x200>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  197,124,89,208                      // vmulps        %ymm0,%ymm0,%ymm10
+  .byte  196,98,125,24,29,79,78,0,0          // vbroadcastss  0x4e4f(%rip),%ymm11        # 6600 <_sk_callback_avx+0x204>
+  .byte  196,65,124,89,227                   // vmulps        %ymm11,%ymm0,%ymm12
+  .byte  196,98,125,24,45,69,78,0,0          // vbroadcastss  0x4e45(%rip),%ymm13        # 6604 <_sk_callback_avx+0x208>
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  196,65,44,89,212                    // vmulps        %ymm12,%ymm10,%ymm10
+  .byte  196,98,125,24,37,54,78,0,0          // vbroadcastss  0x4e36(%rip),%ymm12        # 6608 <_sk_callback_avx+0x20c>
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,98,125,24,53,44,78,0,0          // vbroadcastss  0x4e2c(%rip),%ymm14        # 660c <_sk_callback_avx+0x210>
+  .byte  196,193,124,194,198,1               // vcmpltps      %ymm14,%ymm0,%ymm0
+  .byte  196,195,45,74,193,0                 // vblendvps     %ymm0,%ymm9,%ymm10,%ymm0
+  .byte  196,65,116,89,200                   // vmulps        %ymm8,%ymm1,%ymm9
+  .byte  197,116,89,209                      // vmulps        %ymm1,%ymm1,%ymm10
+  .byte  196,65,116,89,251                   // vmulps        %ymm11,%ymm1,%ymm15
+  .byte  196,65,4,88,253                     // vaddps        %ymm13,%ymm15,%ymm15
+  .byte  196,65,44,89,215                    // vmulps        %ymm15,%ymm10,%ymm10
+  .byte  196,65,44,88,212                    // vaddps        %ymm12,%ymm10,%ymm10
+  .byte  196,193,116,194,206,1               // vcmpltps      %ymm14,%ymm1,%ymm1
+  .byte  196,195,45,74,201,16                // vblendvps     %ymm1,%ymm9,%ymm10,%ymm1
+  .byte  196,65,108,89,192                   // vmulps        %ymm8,%ymm2,%ymm8
+  .byte  197,108,89,202                      // vmulps        %ymm2,%ymm2,%ymm9
+  .byte  196,65,108,89,211                   // vmulps        %ymm11,%ymm2,%ymm10
+  .byte  196,65,44,88,213                    // vaddps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,52,89,202                    // vmulps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,52,88,204                    // vaddps        %ymm12,%ymm9,%ymm9
+  .byte  196,193,108,194,214,1               // vcmpltps      %ymm14,%ymm2,%ymm2
+  .byte  196,195,53,74,208,32                // vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_to_srgb_avx
+.globl _sk_to_srgb_avx
+FUNCTION(_sk_to_srgb_avx)
+_sk_to_srgb_avx:
+  .byte  197,124,82,200                      // vrsqrtps      %ymm0,%ymm9
+  .byte  196,98,125,24,5,193,77,0,0          // vbroadcastss  0x4dc1(%rip),%ymm8        # 6610 <_sk_callback_avx+0x214>
+  .byte  196,65,124,89,208                   // vmulps        %ymm8,%ymm0,%ymm10
+  .byte  196,98,125,24,29,183,77,0,0         // vbroadcastss  0x4db7(%rip),%ymm11        # 6614 <_sk_callback_avx+0x218>
+  .byte  196,65,52,89,227                    // vmulps        %ymm11,%ymm9,%ymm12
+  .byte  196,98,125,24,45,173,77,0,0         // vbroadcastss  0x4dad(%rip),%ymm13        # 6618 <_sk_callback_avx+0x21c>
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  196,65,52,89,228                    // vmulps        %ymm12,%ymm9,%ymm12
+  .byte  196,98,125,24,53,158,77,0,0         // vbroadcastss  0x4d9e(%rip),%ymm14        # 661c <_sk_callback_avx+0x220>
+  .byte  196,65,28,88,230                    // vaddps        %ymm14,%ymm12,%ymm12
+  .byte  196,98,125,24,61,148,77,0,0         // vbroadcastss  0x4d94(%rip),%ymm15        # 6620 <_sk_callback_avx+0x224>
+  .byte  196,65,52,88,207                    // vaddps        %ymm15,%ymm9,%ymm9
+  .byte  196,65,124,83,201                   // vrcpps        %ymm9,%ymm9
+  .byte  196,65,52,89,204                    // vmulps        %ymm12,%ymm9,%ymm9
+  .byte  196,98,125,24,37,128,77,0,0         // vbroadcastss  0x4d80(%rip),%ymm12        # 6624 <_sk_callback_avx+0x228>
+  .byte  196,193,124,194,196,1               // vcmpltps      %ymm12,%ymm0,%ymm0
+  .byte  196,195,53,74,194,0                 // vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  .byte  197,124,82,201                      // vrsqrtps      %ymm1,%ymm9
+  .byte  196,65,52,89,211                    // vmulps        %ymm11,%ymm9,%ymm10
+  .byte  196,65,44,88,213                    // vaddps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,52,89,210                    // vmulps        %ymm10,%ymm9,%ymm10
+  .byte  196,65,44,88,214                    // vaddps        %ymm14,%ymm10,%ymm10
+  .byte  196,65,52,88,207                    // vaddps        %ymm15,%ymm9,%ymm9
+  .byte  196,65,124,83,201                   // vrcpps        %ymm9,%ymm9
+  .byte  196,65,52,89,202                    // vmulps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,193,116,194,204,1               // vcmpltps      %ymm12,%ymm1,%ymm1
+  .byte  196,195,53,74,202,16                // vblendvps     %ymm1,%ymm10,%ymm9,%ymm1
+  .byte  197,124,82,202                      // vrsqrtps      %ymm2,%ymm9
+  .byte  196,65,52,89,211                    // vmulps        %ymm11,%ymm9,%ymm10
+  .byte  196,65,44,88,213                    // vaddps        %ymm13,%ymm10,%ymm10
+  .byte  196,65,52,89,210                    // vmulps        %ymm10,%ymm9,%ymm10
+  .byte  196,65,44,88,214                    // vaddps        %ymm14,%ymm10,%ymm10
+  .byte  196,65,52,88,207                    // vaddps        %ymm15,%ymm9,%ymm9
+  .byte  196,65,124,83,201                   // vrcpps        %ymm9,%ymm9
+  .byte  196,65,52,89,202                    // vmulps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,108,89,192                   // vmulps        %ymm8,%ymm2,%ymm8
+  .byte  196,193,108,194,212,1               // vcmpltps      %ymm12,%ymm2,%ymm2
+  .byte  196,195,53,74,208,32                // vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_rgb_to_hsl_avx
+.globl _sk_rgb_to_hsl_avx
+FUNCTION(_sk_rgb_to_hsl_avx)
+_sk_rgb_to_hsl_avx:
+  .byte  197,124,95,193                      // vmaxps        %ymm1,%ymm0,%ymm8
+  .byte  197,60,95,194                       // vmaxps        %ymm2,%ymm8,%ymm8
+  .byte  197,124,93,201                      // vminps        %ymm1,%ymm0,%ymm9
+  .byte  197,52,93,202                       // vminps        %ymm2,%ymm9,%ymm9
+  .byte  196,65,60,92,209                    // vsubps        %ymm9,%ymm8,%ymm10
+  .byte  196,98,125,24,29,230,76,0,0         // vbroadcastss  0x4ce6(%rip),%ymm11        # 6628 <_sk_callback_avx+0x22c>
+  .byte  196,65,36,94,218                    // vdivps        %ymm10,%ymm11,%ymm11
+  .byte  197,116,92,226                      // vsubps        %ymm2,%ymm1,%ymm12
+  .byte  196,65,28,89,227                    // vmulps        %ymm11,%ymm12,%ymm12
+  .byte  197,116,194,234,1                   // vcmpltps      %ymm2,%ymm1,%ymm13
+  .byte  197,60,194,241,0                    // vcmpeqps      %ymm1,%ymm8,%ymm14
+  .byte  197,236,92,208                      // vsubps        %ymm0,%ymm2,%ymm2
+  .byte  196,193,108,89,211                  // vmulps        %ymm11,%ymm2,%ymm2
+  .byte  197,252,92,201                      // vsubps        %ymm1,%ymm0,%ymm1
+  .byte  196,193,116,89,203                  // vmulps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,29,191,76,0,0         // vbroadcastss  0x4cbf(%rip),%ymm11        # 6634 <_sk_callback_avx+0x238>
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,29,173,76,0,0         // vbroadcastss  0x4cad(%rip),%ymm11        # 6630 <_sk_callback_avx+0x234>
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  196,227,117,74,202,224              // vblendvps     %ymm14,%ymm2,%ymm1,%ymm1
+  .byte  196,226,125,24,21,149,76,0,0        // vbroadcastss  0x4c95(%rip),%ymm2        # 662c <_sk_callback_avx+0x230>
+  .byte  196,65,12,87,246                    // vxorps        %ymm14,%ymm14,%ymm14
+  .byte  196,227,13,74,210,208               // vblendvps     %ymm13,%ymm2,%ymm14,%ymm2
+  .byte  197,188,194,192,0                   // vcmpeqps      %ymm0,%ymm8,%ymm0
+  .byte  196,193,108,88,212                  // vaddps        %ymm12,%ymm2,%ymm2
+  .byte  196,227,117,74,194,0                // vblendvps     %ymm0,%ymm2,%ymm1,%ymm0
+  .byte  196,193,60,88,201                   // vaddps        %ymm9,%ymm8,%ymm1
+  .byte  196,98,125,24,37,124,76,0,0         // vbroadcastss  0x4c7c(%rip),%ymm12        # 663c <_sk_callback_avx+0x240>
+  .byte  196,193,116,89,212                  // vmulps        %ymm12,%ymm1,%ymm2
+  .byte  197,28,194,226,1                    // vcmpltps      %ymm2,%ymm12,%ymm12
+  .byte  196,65,36,92,216                    // vsubps        %ymm8,%ymm11,%ymm11
+  .byte  196,65,36,92,217                    // vsubps        %ymm9,%ymm11,%ymm11
+  .byte  196,195,117,74,203,192              // vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  .byte  196,65,60,194,193,0                 // vcmpeqps      %ymm9,%ymm8,%ymm8
+  .byte  197,172,94,201                      // vdivps        %ymm1,%ymm10,%ymm1
+  .byte  196,195,125,74,198,128              // vblendvps     %ymm8,%ymm14,%ymm0,%ymm0
+  .byte  196,195,117,74,206,128              // vblendvps     %ymm8,%ymm14,%ymm1,%ymm1
+  .byte  196,98,125,24,5,63,76,0,0           // vbroadcastss  0x4c3f(%rip),%ymm8        # 6638 <_sk_callback_avx+0x23c>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hsl_to_rgb_avx
+.globl _sk_hsl_to_rgb_avx
+FUNCTION(_sk_hsl_to_rgb_avx)
+_sk_hsl_to_rgb_avx:
+  .byte  72,131,236,56                       // sub           $0x38,%rsp
+  .byte  197,252,17,60,36                    // vmovups       %ymm7,(%rsp)
+  .byte  197,252,17,116,36,224               // vmovups       %ymm6,-0x20(%rsp)
+  .byte  197,252,17,108,36,192               // vmovups       %ymm5,-0x40(%rsp)
+  .byte  197,252,17,100,36,160               // vmovups       %ymm4,-0x60(%rsp)
+  .byte  197,252,17,92,36,128                // vmovups       %ymm3,-0x80(%rsp)
+  .byte  197,252,40,225                      // vmovaps       %ymm1,%ymm4
+  .byte  197,252,40,216                      // vmovaps       %ymm0,%ymm3
+  .byte  196,98,125,24,5,12,76,0,0           // vbroadcastss  0x4c0c(%rip),%ymm8        # 6640 <_sk_callback_avx+0x244>
+  .byte  197,60,194,202,2                    // vcmpleps      %ymm2,%ymm8,%ymm9
+  .byte  197,92,89,210                       // vmulps        %ymm2,%ymm4,%ymm10
+  .byte  196,65,92,92,218                    // vsubps        %ymm10,%ymm4,%ymm11
+  .byte  196,67,45,74,203,144                // vblendvps     %ymm9,%ymm11,%ymm10,%ymm9
+  .byte  197,52,88,210                       // vaddps        %ymm2,%ymm9,%ymm10
+  .byte  197,108,88,202                      // vaddps        %ymm2,%ymm2,%ymm9
+  .byte  196,65,52,92,202                    // vsubps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,29,230,75,0,0         // vbroadcastss  0x4be6(%rip),%ymm11        # 6644 <_sk_callback_avx+0x248>
+  .byte  196,65,100,88,219                   // vaddps        %ymm11,%ymm3,%ymm11
+  .byte  196,67,125,8,227,1                  // vroundps      $0x1,%ymm11,%ymm12
+  .byte  196,65,36,92,252                    // vsubps        %ymm12,%ymm11,%ymm15
+  .byte  196,65,44,92,217                    // vsubps        %ymm9,%ymm10,%ymm11
+  .byte  196,98,125,24,37,208,75,0,0         // vbroadcastss  0x4bd0(%rip),%ymm12        # 664c <_sk_callback_avx+0x250>
+  .byte  196,193,4,89,196                    // vmulps        %ymm12,%ymm15,%ymm0
+  .byte  196,98,125,24,45,198,75,0,0         // vbroadcastss  0x4bc6(%rip),%ymm13        # 6650 <_sk_callback_avx+0x254>
+  .byte  197,20,92,240                       // vsubps        %ymm0,%ymm13,%ymm14
+  .byte  196,65,36,89,246                    // vmulps        %ymm14,%ymm11,%ymm14
+  .byte  196,65,52,88,246                    // vaddps        %ymm14,%ymm9,%ymm14
+  .byte  196,226,125,24,13,167,75,0,0        // vbroadcastss  0x4ba7(%rip),%ymm1        # 6648 <_sk_callback_avx+0x24c>
+  .byte  196,193,116,194,255,2               // vcmpleps      %ymm15,%ymm1,%ymm7
+  .byte  196,195,13,74,249,112               // vblendvps     %ymm7,%ymm9,%ymm14,%ymm7
+  .byte  196,65,60,194,247,2                 // vcmpleps      %ymm15,%ymm8,%ymm14
+  .byte  196,227,45,74,255,224               // vblendvps     %ymm14,%ymm7,%ymm10,%ymm7
+  .byte  196,98,125,24,53,146,75,0,0         // vbroadcastss  0x4b92(%rip),%ymm14        # 6654 <_sk_callback_avx+0x258>
+  .byte  196,65,12,194,255,2                 // vcmpleps      %ymm15,%ymm14,%ymm15
+  .byte  196,193,124,89,195                  // vmulps        %ymm11,%ymm0,%ymm0
+  .byte  197,180,88,192                      // vaddps        %ymm0,%ymm9,%ymm0
+  .byte  196,99,125,74,255,240               // vblendvps     %ymm15,%ymm7,%ymm0,%ymm15
+  .byte  196,227,125,8,195,1                 // vroundps      $0x1,%ymm3,%ymm0
+  .byte  197,228,92,192                      // vsubps        %ymm0,%ymm3,%ymm0
+  .byte  196,193,124,89,252                  // vmulps        %ymm12,%ymm0,%ymm7
+  .byte  197,148,92,247                      // vsubps        %ymm7,%ymm13,%ymm6
+  .byte  197,164,89,246                      // vmulps        %ymm6,%ymm11,%ymm6
+  .byte  197,180,88,246                      // vaddps        %ymm6,%ymm9,%ymm6
+  .byte  197,244,194,232,2                   // vcmpleps      %ymm0,%ymm1,%ymm5
+  .byte  196,195,77,74,233,80                // vblendvps     %ymm5,%ymm9,%ymm6,%ymm5
+  .byte  197,188,194,240,2                   // vcmpleps      %ymm0,%ymm8,%ymm6
+  .byte  196,227,45,74,237,96                // vblendvps     %ymm6,%ymm5,%ymm10,%ymm5
+  .byte  197,140,194,192,2                   // vcmpleps      %ymm0,%ymm14,%ymm0
+  .byte  197,164,89,247                      // vmulps        %ymm7,%ymm11,%ymm6
+  .byte  197,180,88,246                      // vaddps        %ymm6,%ymm9,%ymm6
+  .byte  196,227,77,74,237,0                 // vblendvps     %ymm0,%ymm5,%ymm6,%ymm5
+  .byte  196,226,125,24,5,52,75,0,0          // vbroadcastss  0x4b34(%rip),%ymm0        # 6658 <_sk_callback_avx+0x25c>
+  .byte  197,228,88,192                      // vaddps        %ymm0,%ymm3,%ymm0
+  .byte  196,227,125,8,216,1                 // vroundps      $0x1,%ymm0,%ymm3
+  .byte  197,252,92,195                      // vsubps        %ymm3,%ymm0,%ymm0
+  .byte  197,244,194,200,2                   // vcmpleps      %ymm0,%ymm1,%ymm1
+  .byte  196,193,124,89,220                  // vmulps        %ymm12,%ymm0,%ymm3
+  .byte  197,148,92,243                      // vsubps        %ymm3,%ymm13,%ymm6
+  .byte  197,164,89,246                      // vmulps        %ymm6,%ymm11,%ymm6
+  .byte  197,180,88,246                      // vaddps        %ymm6,%ymm9,%ymm6
+  .byte  196,195,77,74,201,16                // vblendvps     %ymm1,%ymm9,%ymm6,%ymm1
+  .byte  197,188,194,240,2                   // vcmpleps      %ymm0,%ymm8,%ymm6
+  .byte  196,227,45,74,201,96                // vblendvps     %ymm6,%ymm1,%ymm10,%ymm1
+  .byte  197,140,194,192,2                   // vcmpleps      %ymm0,%ymm14,%ymm0
+  .byte  197,164,89,219                      // vmulps        %ymm3,%ymm11,%ymm3
+  .byte  197,180,88,219                      // vaddps        %ymm3,%ymm9,%ymm3
+  .byte  196,227,101,74,217,0                // vblendvps     %ymm0,%ymm1,%ymm3,%ymm3
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,220,194,224,0                   // vcmpeqps      %ymm0,%ymm4,%ymm4
+  .byte  196,227,5,74,194,64                 // vblendvps     %ymm4,%ymm2,%ymm15,%ymm0
+  .byte  196,227,85,74,202,64                // vblendvps     %ymm4,%ymm2,%ymm5,%ymm1
+  .byte  196,227,101,74,210,64               // vblendvps     %ymm4,%ymm2,%ymm3,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,92,36,128                // vmovups       -0x80(%rsp),%ymm3
+  .byte  197,252,16,100,36,160               // vmovups       -0x60(%rsp),%ymm4
+  .byte  197,252,16,108,36,192               // vmovups       -0x40(%rsp),%ymm5
+  .byte  197,252,16,116,36,224               // vmovups       -0x20(%rsp),%ymm6
+  .byte  197,252,16,60,36                    // vmovups       (%rsp),%ymm7
+  .byte  72,131,196,56                       // add           $0x38,%rsp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_1_float_avx
+.globl _sk_scale_1_float_avx
+FUNCTION(_sk_scale_1_float_avx)
+_sk_scale_1_float_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_u8_avx
+.globl _sk_scale_u8_avx
+FUNCTION(_sk_scale_u8_avx)
+_sk_scale_u8_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,68                              // jne           1c1b <_sk_scale_u8_avx+0x54>
+  .byte  197,122,126,0                       // vmovq         (%rax),%xmm8
+  .byte  196,66,121,49,200                   // vpmovzxbd     %xmm8,%xmm9
+  .byte  196,67,121,4,192,229                // vpermilps     $0xe5,%xmm8,%xmm8
+  .byte  196,66,121,49,192                   // vpmovzxbd     %xmm8,%xmm8
+  .byte  196,67,53,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,13,93,74,0,0          // vbroadcastss  0x4a5d(%rip),%ymm9        # 665c <_sk_callback_avx+0x260>
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           1c24 <_sk_scale_u8_avx+0x5d>
+  .byte  196,65,249,110,194                  // vmovq         %r10,%xmm8
+  .byte  91                                  // pop           %rbx
+  .byte  235,154                             // jmp           1bdb <_sk_scale_u8_avx+0x14>
+
+HIDDEN _sk_lerp_1_float_avx
+.globl _sk_lerp_1_float_avx
+FUNCTION(_sk_lerp_1_float_avx)
+_sk_lerp_1_float_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  197,252,88,196                      // vaddps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,92,205                      // vsubps        %ymm5,%ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  197,244,88,205                      // vaddps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,92,214                      // vsubps        %ymm6,%ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,236,88,214                      // vaddps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,92,223                      // vsubps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lerp_u8_avx
+.globl _sk_lerp_u8_avx
+FUNCTION(_sk_lerp_u8_avx)
+_sk_lerp_u8_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,104                             // jne           1cf8 <_sk_lerp_u8_avx+0x78>
+  .byte  197,122,126,0                       // vmovq         (%rax),%xmm8
+  .byte  196,66,121,49,200                   // vpmovzxbd     %xmm8,%xmm9
+  .byte  196,67,121,4,192,229                // vpermilps     $0xe5,%xmm8,%xmm8
+  .byte  196,66,121,49,192                   // vpmovzxbd     %xmm8,%xmm8
+  .byte  196,67,53,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,13,168,73,0,0         // vbroadcastss  0x49a8(%rip),%ymm9        # 6660 <_sk_callback_avx+0x264>
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  197,252,88,196                      // vaddps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,92,205                      // vsubps        %ymm5,%ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  197,244,88,205                      // vaddps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,92,214                      // vsubps        %ymm6,%ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,236,88,214                      // vaddps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,92,223                      // vsubps        %ymm7,%ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           1d01 <_sk_lerp_u8_avx+0x81>
+  .byte  196,65,249,110,194                  // vmovq         %r10,%xmm8
+  .byte  91                                  // pop           %rbx
+  .byte  233,115,255,255,255                 // jmpq          1c94 <_sk_lerp_u8_avx+0x14>
+
+HIDDEN _sk_lerp_565_avx
+.globl _sk_lerp_565_avx
+FUNCTION(_sk_lerp_565_avx)
+_sk_lerp_565_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,208,0,0,0                    // jne           1dff <_sk_lerp_565_avx+0xde>
+  .byte  196,65,122,111,4,83                 // vmovdqu       (%r11,%rdx,2),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  196,65,57,105,201                   // vpunpckhwd    %xmm9,%xmm8,%xmm9
+  .byte  196,66,121,51,192                   // vpmovzxwd     %xmm8,%xmm8
+  .byte  196,67,61,24,193,1                  // vinsertf128   $0x1,%xmm9,%ymm8,%ymm8
+  .byte  196,98,125,24,13,17,73,0,0          // vbroadcastss  0x4911(%rip),%ymm9        # 6664 <_sk_callback_avx+0x268>
+  .byte  196,65,60,84,201                    // vandps        %ymm9,%ymm8,%ymm9
+  .byte  196,65,124,91,201                   // vcvtdq2ps     %ymm9,%ymm9
+  .byte  196,98,125,24,21,2,73,0,0           // vbroadcastss  0x4902(%rip),%ymm10        # 6668 <_sk_callback_avx+0x26c>
+  .byte  196,65,52,89,202                    // vmulps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,21,248,72,0,0         // vbroadcastss  0x48f8(%rip),%ymm10        # 666c <_sk_callback_avx+0x270>
+  .byte  196,65,60,84,210                    // vandps        %ymm10,%ymm8,%ymm10
+  .byte  196,65,124,91,210                   // vcvtdq2ps     %ymm10,%ymm10
+  .byte  196,98,125,24,29,233,72,0,0         // vbroadcastss  0x48e9(%rip),%ymm11        # 6670 <_sk_callback_avx+0x274>
+  .byte  196,65,44,89,211                    // vmulps        %ymm11,%ymm10,%ymm10
+  .byte  196,98,125,24,29,223,72,0,0         // vbroadcastss  0x48df(%rip),%ymm11        # 6674 <_sk_callback_avx+0x278>
+  .byte  196,65,60,84,195                    // vandps        %ymm11,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  196,98,125,24,29,208,72,0,0         // vbroadcastss  0x48d0(%rip),%ymm11        # 6678 <_sk_callback_avx+0x27c>
+  .byte  196,65,60,89,195                    // vmulps        %ymm11,%ymm8,%ymm8
+  .byte  197,252,92,196                      // vsubps        %ymm4,%ymm0,%ymm0
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  197,252,88,196                      // vaddps        %ymm4,%ymm0,%ymm0
+  .byte  197,244,92,205                      // vsubps        %ymm5,%ymm1,%ymm1
+  .byte  196,193,116,89,202                  // vmulps        %ymm10,%ymm1,%ymm1
+  .byte  197,244,88,205                      // vaddps        %ymm5,%ymm1,%ymm1
+  .byte  197,236,92,214                      // vsubps        %ymm6,%ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,236,88,214                      // vaddps        %ymm6,%ymm2,%ymm2
+  .byte  197,228,92,223                      // vsubps        %ymm7,%ymm3,%ymm3
+  .byte  196,65,100,89,201                   // vmulps        %ymm9,%ymm3,%ymm9
+  .byte  197,52,88,207                       // vaddps        %ymm7,%ymm9,%ymm9
+  .byte  196,65,100,89,210                   // vmulps        %ymm10,%ymm3,%ymm10
+  .byte  197,44,88,215                       // vaddps        %ymm7,%ymm10,%ymm10
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  197,228,88,223                      // vaddps        %ymm7,%ymm3,%ymm3
+  .byte  197,172,95,219                      // vmaxps        %ymm3,%ymm10,%ymm3
+  .byte  197,180,95,219                      // vmaxps        %ymm3,%ymm9,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  196,65,57,239,192                   // vpxor         %xmm8,%xmm8,%xmm8
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,29,255,255,255               // ja            1d35 <_sk_lerp_565_avx+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,77,0,0,0                  // lea           0x4d(%rip),%r10        # 1e70 <_sk_lerp_565_avx+0x14f>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,57,239,192                   // vpxor         %xmm8,%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,12,6            // vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,10,5            // vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,8,4             // vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,6,3             // vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,4,2             // vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,68,83,2,1             // vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  196,65,57,196,4,83,0                // vpinsrw       $0x0,(%r11,%rdx,2),%xmm8,%xmm8
+  .byte  233,200,254,255,255                 // jmpq          1d35 <_sk_lerp_565_avx+0x14>
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  241                                 // icebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  233,255,255,255,225                 // jmpq          ffffffffe2001e78 <_sk_callback_avx+0xffffffffe1ffba7c>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  217,255                             // fcos
+  .byte  255                                 // (bad)
+  .byte  255,209                             // callq         *%rcx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,201                             // dec           %ecx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  188                                 // .byte         0xbc
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_tables_avx
+.globl _sk_load_tables_avx
+FUNCTION(_sk_load_tables_avx)
+_sk_load_tables_avx:
+  .byte  83                                  // push          %rbx
+  .byte  197,252,17,124,36,208               // vmovups       %ymm7,-0x30(%rsp)
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,248,1,0,0                    // jne           20a4 <_sk_load_tables_avx+0x218>
+  .byte  196,65,124,16,18                    // vmovups       (%r10),%ymm10
+  .byte  197,124,40,13,135,75,0,0            // vmovaps       0x4b87(%rip),%ymm9        # 6a40 <_sk_callback_avx+0x644>
+  .byte  196,193,44,84,201                   // vandps        %ymm9,%ymm10,%ymm1
+  .byte  196,227,125,25,200,1                // vextractf128  $0x1,%ymm1,%xmm0
+  .byte  196,193,249,126,195                 // vmovq         %xmm0,%r11
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  196,161,122,16,20,147               // vmovss        (%rbx,%r10,4),%xmm2
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,163,105,33,4,155,16             // vinsertps     $0x10,(%rbx,%r11,4),%xmm2,%xmm0
+  .byte  68,137,209                          // mov           %r10d,%ecx
+  .byte  196,227,121,33,4,139,32             // vinsertps     $0x20,(%rbx,%rcx,4),%xmm0,%xmm0
+  .byte  196,193,249,126,203                 // vmovq         %xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,35,121,33,44,147,48             // vinsertps     $0x30,(%rbx,%r10,4),%xmm0,%xmm13
+  .byte  68,137,217                          // mov           %r11d,%ecx
+  .byte  197,250,16,20,139                   // vmovss        (%rbx,%rcx,4),%xmm2
+  .byte  196,227,249,22,201,1                // vpextrq       $0x1,%xmm1,%rcx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,163,105,33,12,155,16            // vinsertps     $0x10,(%rbx,%r11,4),%xmm2,%xmm1
+  .byte  65,137,202                          // mov           %ecx,%r10d
+  .byte  72,193,233,32                       // shr           $0x20,%rcx
+  .byte  196,163,113,33,12,147,32            // vinsertps     $0x20,(%rbx,%r10,4),%xmm1,%xmm1
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  196,99,113,33,36,139,48             // vinsertps     $0x30,(%rbx,%rcx,4),%xmm1,%xmm12
+  .byte  196,193,105,114,210,8               // vpsrld        $0x8,%xmm10,%xmm2
+  .byte  196,67,125,25,208,1                 // vextractf128  $0x1,%ymm10,%xmm8
+  .byte  196,193,121,114,208,8               // vpsrld        $0x8,%xmm8,%xmm0
+  .byte  196,227,109,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm2,%ymm0
+  .byte  196,193,124,84,209                  // vandps        %ymm9,%ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  196,225,249,126,193                 // vmovq         %xmm0,%rcx
+  .byte  137,203                             // mov           %ecx,%ebx
+  .byte  196,193,122,16,12,154               // vmovss        (%r10,%rbx,4),%xmm1
+  .byte  196,227,249,22,195,1                // vpextrq       $0x1,%xmm0,%rbx
+  .byte  72,193,233,32                       // shr           $0x20,%rcx
+  .byte  196,67,113,33,52,138,16             // vinsertps     $0x10,(%r10,%rcx,4),%xmm1,%xmm14
+  .byte  137,217                             // mov           %ebx,%ecx
+  .byte  196,193,122,16,28,138               // vmovss        (%r10,%rcx,4),%xmm3
+  .byte  196,225,249,126,209                 // vmovq         %xmm2,%rcx
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  196,193,122,16,12,154               // vmovss        (%r10,%rbx,4),%xmm1
+  .byte  137,203                             // mov           %ecx,%ebx
+  .byte  196,193,122,16,4,154                // vmovss        (%r10,%rbx,4),%xmm0
+  .byte  196,227,249,22,211,1                // vpextrq       $0x1,%xmm2,%rbx
+  .byte  72,193,233,32                       // shr           $0x20,%rcx
+  .byte  196,67,121,33,28,138,16             // vinsertps     $0x10,(%r10,%rcx,4),%xmm0,%xmm11
+  .byte  137,217                             // mov           %ebx,%ecx
+  .byte  196,65,122,16,60,138                // vmovss        (%r10,%rcx,4),%xmm15
+  .byte  196,195,29,24,197,1                 // vinsertf128   $0x1,%xmm13,%ymm12,%ymm0
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  196,227,9,33,219,32                 // vinsertps     $0x20,%xmm3,%xmm14,%xmm3
+  .byte  196,227,97,33,249,48                // vinsertps     $0x30,%xmm1,%xmm3,%xmm7
+  .byte  196,65,122,16,52,154                // vmovss        (%r10,%rbx,4),%xmm14
+  .byte  72,139,64,24                        // mov           0x18(%rax),%rax
+  .byte  196,193,97,114,210,16               // vpsrld        $0x10,%xmm10,%xmm3
+  .byte  196,193,105,114,208,16              // vpsrld        $0x10,%xmm8,%xmm2
+  .byte  196,227,101,24,210,1                // vinsertf128   $0x1,%xmm2,%ymm3,%ymm2
+  .byte  196,65,108,84,201                   // vandps        %ymm9,%ymm2,%ymm9
+  .byte  196,99,125,25,202,1                 // vextractf128  $0x1,%ymm9,%xmm2
+  .byte  196,225,249,126,209                 // vmovq         %xmm2,%rcx
+  .byte  137,203                             // mov           %ecx,%ebx
+  .byte  197,250,16,28,152                   // vmovss        (%rax,%rbx,4),%xmm3
+  .byte  196,227,249,22,211,1                // vpextrq       $0x1,%xmm2,%rbx
+  .byte  72,193,233,32                       // shr           $0x20,%rcx
+  .byte  196,99,97,33,36,136,16              // vinsertps     $0x10,(%rax,%rcx,4),%xmm3,%xmm12
+  .byte  137,217                             // mov           %ebx,%ecx
+  .byte  197,250,16,28,136                   // vmovss        (%rax,%rcx,4),%xmm3
+  .byte  196,97,249,126,201                  // vmovq         %xmm9,%rcx
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  197,250,16,20,152                   // vmovss        (%rax,%rbx,4),%xmm2
+  .byte  137,203                             // mov           %ecx,%ebx
+  .byte  197,250,16,12,152                   // vmovss        (%rax,%rbx,4),%xmm1
+  .byte  196,99,249,22,203,1                 // vpextrq       $0x1,%xmm9,%rbx
+  .byte  72,193,233,32                       // shr           $0x20,%rcx
+  .byte  196,99,113,33,12,136,16             // vinsertps     $0x10,(%rax,%rcx,4),%xmm1,%xmm9
+  .byte  137,217                             // mov           %ebx,%ecx
+  .byte  197,122,16,44,136                   // vmovss        (%rax,%rcx,4),%xmm13
+  .byte  196,195,33,33,207,32                // vinsertps     $0x20,%xmm15,%xmm11,%xmm1
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  197,122,16,28,152                   // vmovss        (%rax,%rbx,4),%xmm11
+  .byte  196,195,113,33,206,48               // vinsertps     $0x30,%xmm14,%xmm1,%xmm1
+  .byte  196,227,117,24,207,1                // vinsertf128   $0x1,%xmm7,%ymm1,%ymm1
+  .byte  196,227,25,33,219,32                // vinsertps     $0x20,%xmm3,%xmm12,%xmm3
+  .byte  196,227,97,33,210,48                // vinsertps     $0x30,%xmm2,%xmm3,%xmm2
+  .byte  196,195,49,33,221,32                // vinsertps     $0x20,%xmm13,%xmm9,%xmm3
+  .byte  196,195,97,33,219,48                // vinsertps     $0x30,%xmm11,%xmm3,%xmm3
+  .byte  196,227,101,24,210,1                // vinsertf128   $0x1,%xmm2,%ymm3,%ymm2
+  .byte  196,193,97,114,210,24               // vpsrld        $0x18,%xmm10,%xmm3
+  .byte  196,193,65,114,208,24               // vpsrld        $0x18,%xmm8,%xmm7
+  .byte  196,227,101,24,223,1                // vinsertf128   $0x1,%xmm7,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,226,125,24,61,234,69,0,0        // vbroadcastss  0x45ea(%rip),%ymm7        # 667c <_sk_callback_avx+0x280>
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  197,252,16,124,36,208               // vmovups       -0x30(%rsp),%ymm7
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  73,199,195,255,255,255,255          // mov           $0xffffffffffffffff,%r11
+  .byte  73,211,235                          // shr           %cl,%r11
+  .byte  196,193,249,110,195                 // vmovq         %r11,%xmm0
+  .byte  196,226,121,48,192                  // vpmovzxbw     %xmm0,%xmm0
+  .byte  196,226,121,0,13,196,72,0,0         // vpshufb       0x48c4(%rip),%xmm0,%xmm1        # 6990 <_sk_callback_avx+0x594>
+  .byte  196,226,121,33,201                  // vpmovsxbd     %xmm1,%xmm1
+  .byte  196,226,121,0,5,198,72,0,0          // vpshufb       0x48c6(%rip),%xmm0,%xmm0        # 69a0 <_sk_callback_avx+0x5a4>
+  .byte  196,226,121,33,192                  // vpmovsxbd     %xmm0,%xmm0
+  .byte  196,227,117,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  .byte  196,66,125,44,18                    // vmaskmovps    (%r10),%ymm0,%ymm10
+  .byte  233,194,253,255,255                 // jmpq          1eb1 <_sk_load_tables_avx+0x25>
+
+HIDDEN _sk_load_tables_u16_be_avx
+.globl _sk_load_tables_u16_be_avx
+FUNCTION(_sk_load_tables_u16_be_avx)
+_sk_load_tables_u16_be_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  197,252,17,124,36,200               // vmovups       %ymm7,-0x38(%rsp)
+  .byte  15,133,84,2,0,0                     // jne           235f <_sk_load_tables_u16_be_avx+0x270>
+  .byte  196,1,121,16,4,81                   // vmovupd       (%r9,%r10,2),%xmm8
+  .byte  196,129,121,16,84,81,16             // vmovupd       0x10(%r9,%r10,2),%xmm2
+  .byte  196,129,121,16,92,81,32             // vmovupd       0x20(%r9,%r10,2),%xmm3
+  .byte  196,1,122,111,76,81,48              // vmovdqu       0x30(%r9,%r10,2),%xmm9
+  .byte  197,185,97,194                      // vpunpcklwd    %xmm2,%xmm8,%xmm0
+  .byte  197,185,105,210                     // vpunpckhwd    %xmm2,%xmm8,%xmm2
+  .byte  196,193,97,97,201                   // vpunpcklwd    %xmm9,%xmm3,%xmm1
+  .byte  196,193,97,105,217                  // vpunpckhwd    %xmm9,%xmm3,%xmm3
+  .byte  197,121,97,202                      // vpunpcklwd    %xmm2,%xmm0,%xmm9
+  .byte  197,121,105,194                     // vpunpckhwd    %xmm2,%xmm0,%xmm8
+  .byte  197,241,97,195                      // vpunpcklwd    %xmm3,%xmm1,%xmm0
+  .byte  197,113,105,219                     // vpunpckhwd    %xmm3,%xmm1,%xmm11
+  .byte  197,177,108,200                     // vpunpcklqdq   %xmm0,%xmm9,%xmm1
+  .byte  197,49,109,224                      // vpunpckhqdq   %xmm0,%xmm9,%xmm12
+  .byte  197,121,111,21,88,72,0,0            // vmovdqa       0x4858(%rip),%xmm10        # 69b0 <_sk_callback_avx+0x5b4>
+  .byte  196,193,113,219,202                 // vpand         %xmm10,%xmm1,%xmm1
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  196,193,113,105,209                 // vpunpckhwd    %xmm9,%xmm1,%xmm2
+  .byte  196,193,249,126,209                 // vmovq         %xmm2,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  76,139,88,8                         // mov           0x8(%rax),%r11
+  .byte  196,129,122,16,28,147               // vmovss        (%r11,%r10,4),%xmm3
+  .byte  196,195,249,22,210,1                // vpextrq       $0x1,%xmm2,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,97,33,44,139,16               // vinsertps     $0x10,(%r11,%r9,4),%xmm3,%xmm13
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,226,121,51,201                  // vpmovzxwd     %xmm1,%xmm1
+  .byte  196,129,122,16,28,139               // vmovss        (%r11,%r9,4),%xmm3
+  .byte  196,193,249,126,201                 // vmovq         %xmm1,%r9
+  .byte  196,129,122,16,4,147                // vmovss        (%r11,%r10,4),%xmm0
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  196,129,122,16,20,147               // vmovss        (%r11,%r10,4),%xmm2
+  .byte  196,195,249,22,202,1                // vpextrq       $0x1,%xmm1,%r10
+  .byte  196,131,105,33,12,11,16             // vinsertps     $0x10,(%r11,%r9,1),%xmm2,%xmm1
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  196,129,122,16,20,139               // vmovss        (%r11,%r9,4),%xmm2
+  .byte  76,139,72,16                        // mov           0x10(%rax),%r9
+  .byte  196,227,17,33,219,32                // vinsertps     $0x20,%xmm3,%xmm13,%xmm3
+  .byte  196,99,97,33,232,48                 // vinsertps     $0x30,%xmm0,%xmm3,%xmm13
+  .byte  196,99,113,33,242,32                // vinsertps     $0x20,%xmm2,%xmm1,%xmm14
+  .byte  196,1,122,16,60,19                  // vmovss        (%r11,%r10,1),%xmm15
+  .byte  196,193,25,219,210                  // vpand         %xmm10,%xmm12,%xmm2
+  .byte  196,193,105,105,193                 // vpunpckhwd    %xmm9,%xmm2,%xmm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,129,122,16,12,153               // vmovss        (%r9,%r11,4),%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,113,33,36,145,16              // vinsertps     $0x10,(%r9,%r10,4),%xmm1,%xmm12
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,226,121,51,194                  // vpmovzxwd     %xmm2,%xmm0
+  .byte  196,129,122,16,20,145               // vmovss        (%r9,%r10,4),%xmm2
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  196,129,122,16,28,153               // vmovss        (%r9,%r11,4),%xmm3
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  196,129,122,16,12,153               // vmovss        (%r9,%r11,4),%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  196,131,113,33,12,17,16             // vinsertps     $0x10,(%r9,%r10,1),%xmm1,%xmm1
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  196,129,122,16,60,145               // vmovss        (%r9,%r10,4),%xmm7
+  .byte  196,195,9,33,199,48                 // vinsertps     $0x30,%xmm15,%xmm14,%xmm0
+  .byte  196,65,57,108,243                   // vpunpcklqdq   %xmm11,%xmm8,%xmm14
+  .byte  196,195,125,24,197,1                // vinsertf128   $0x1,%xmm13,%ymm0,%ymm0
+  .byte  73,193,235,30                       // shr           $0x1e,%r11
+  .byte  196,227,25,33,210,32                // vinsertps     $0x20,%xmm2,%xmm12,%xmm2
+  .byte  196,227,105,33,219,48               // vinsertps     $0x30,%xmm3,%xmm2,%xmm3
+  .byte  196,99,113,33,239,32                // vinsertps     $0x20,%xmm7,%xmm1,%xmm13
+  .byte  196,1,122,16,60,25                  // vmovss        (%r9,%r11,1),%xmm15
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  196,193,9,219,250                   // vpand         %xmm10,%xmm14,%xmm7
+  .byte  196,193,65,105,209                  // vpunpckhwd    %xmm9,%xmm7,%xmm2
+  .byte  196,193,249,126,209                 // vmovq         %xmm2,%r9
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,193,122,16,12,130               // vmovss        (%r10,%rax,4),%xmm1
+  .byte  196,227,249,22,208,1                // vpextrq       $0x1,%xmm2,%rax
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,113,33,20,138,16              // vinsertps     $0x10,(%r10,%r9,4),%xmm1,%xmm10
+  .byte  65,137,193                          // mov           %eax,%r9d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,226,121,51,207                  // vpmovzxwd     %xmm7,%xmm1
+  .byte  196,1,122,16,52,138                 // vmovss        (%r10,%r9,4),%xmm14
+  .byte  196,193,249,126,201                 // vmovq         %xmm1,%r9
+  .byte  196,65,122,16,36,130                // vmovss        (%r10,%rax,4),%xmm12
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  196,193,122,16,20,130               // vmovss        (%r10,%rax,4),%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  196,131,105,33,20,10,16             // vinsertps     $0x10,(%r10,%r9,1),%xmm2,%xmm2
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,193,122,16,60,130               // vmovss        (%r10,%rax,4),%xmm7
+  .byte  196,195,17,33,207,48                // vinsertps     $0x30,%xmm15,%xmm13,%xmm1
+  .byte  73,193,235,30                       // shr           $0x1e,%r11
+  .byte  196,1,122,16,44,26                  // vmovss        (%r10,%r11,1),%xmm13
+  .byte  196,227,117,24,203,1                // vinsertf128   $0x1,%xmm3,%ymm1,%ymm1
+  .byte  196,195,41,33,222,32                // vinsertps     $0x20,%xmm14,%xmm10,%xmm3
+  .byte  196,195,97,33,220,48                // vinsertps     $0x30,%xmm12,%xmm3,%xmm3
+  .byte  196,227,105,33,215,32               // vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  .byte  196,195,105,33,213,48               // vinsertps     $0x30,%xmm13,%xmm2,%xmm2
+  .byte  196,227,109,24,211,1                // vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  .byte  196,193,57,109,219                  // vpunpckhqdq   %xmm11,%xmm8,%xmm3
+  .byte  197,193,113,243,8                   // vpsllw        $0x8,%xmm3,%xmm7
+  .byte  197,225,113,211,8                   // vpsrlw        $0x8,%xmm3,%xmm3
+  .byte  197,193,235,219                     // vpor          %xmm3,%xmm7,%xmm3
+  .byte  196,193,97,105,249                  // vpunpckhwd    %xmm9,%xmm3,%xmm7
+  .byte  196,226,121,51,219                  // vpmovzxwd     %xmm3,%xmm3
+  .byte  196,227,101,24,223,1                // vinsertf128   $0x1,%xmm7,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,226,125,24,61,47,67,0,0         // vbroadcastss  0x432f(%rip),%ymm7        # 6680 <_sk_callback_avx+0x284>
+  .byte  197,228,89,223                      // vmulps        %ymm7,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,124,36,200               // vmovups       -0x38(%rsp),%ymm7
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,1,123,16,4,81                   // vmovsd        (%r9,%r10,2),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,85                              // je            23c5 <_sk_load_tables_u16_be_avx+0x2d6>
+  .byte  196,1,57,22,68,81,8                 // vmovhpd       0x8(%r9,%r10,2),%xmm8,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,72                              // jb            23c5 <_sk_load_tables_u16_be_avx+0x2d6>
+  .byte  196,129,123,16,84,81,16             // vmovsd        0x10(%r9,%r10,2),%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  116,72                              // je            23d2 <_sk_load_tables_u16_be_avx+0x2e3>
+  .byte  196,129,105,22,84,81,24             // vmovhpd       0x18(%r9,%r10,2),%xmm2,%xmm2
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,59                              // jb            23d2 <_sk_load_tables_u16_be_avx+0x2e3>
+  .byte  196,129,123,16,92,81,32             // vmovsd        0x20(%r9,%r10,2),%xmm3
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  15,132,126,253,255,255              // je            2126 <_sk_load_tables_u16_be_avx+0x37>
+  .byte  196,129,97,22,92,81,40              // vmovhpd       0x28(%r9,%r10,2),%xmm3,%xmm3
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  15,130,109,253,255,255              // jb            2126 <_sk_load_tables_u16_be_avx+0x37>
+  .byte  196,1,122,126,76,81,48              // vmovq         0x30(%r9,%r10,2),%xmm9
+  .byte  233,97,253,255,255                  // jmpq          2126 <_sk_load_tables_u16_be_avx+0x37>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  197,233,87,210                      // vxorpd        %xmm2,%xmm2,%xmm2
+  .byte  233,84,253,255,255                  // jmpq          2126 <_sk_load_tables_u16_be_avx+0x37>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  233,75,253,255,255                  // jmpq          2126 <_sk_load_tables_u16_be_avx+0x37>
+
+HIDDEN _sk_load_tables_rgb_u16_be_avx
+.globl _sk_load_tables_rgb_u16_be_avx
+FUNCTION(_sk_load_tables_rgb_u16_be_avx)
+_sk_load_tables_rgb_u16_be_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,82                        // lea           (%rdx,%rdx,2),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  197,252,17,124,36,200               // vmovups       %ymm7,-0x38(%rsp)
+  .byte  197,252,17,116,36,168               // vmovups       %ymm6,-0x58(%rsp)
+  .byte  15,133,71,2,0,0                     // jne           2640 <_sk_load_tables_rgb_u16_be_avx+0x265>
+  .byte  196,129,122,111,4,81                // vmovdqu       (%r9,%r10,2),%xmm0
+  .byte  196,129,122,111,84,81,12            // vmovdqu       0xc(%r9,%r10,2),%xmm2
+  .byte  196,129,122,111,76,81,24            // vmovdqu       0x18(%r9,%r10,2),%xmm1
+  .byte  196,129,122,111,92,81,32            // vmovdqu       0x20(%r9,%r10,2),%xmm3
+  .byte  197,225,115,219,4                   // vpsrldq       $0x4,%xmm3,%xmm3
+  .byte  197,185,115,216,6                   // vpsrldq       $0x6,%xmm0,%xmm8
+  .byte  197,177,115,218,6                   // vpsrldq       $0x6,%xmm2,%xmm9
+  .byte  197,161,115,217,6                   // vpsrldq       $0x6,%xmm1,%xmm11
+  .byte  197,169,115,219,6                   // vpsrldq       $0x6,%xmm3,%xmm10
+  .byte  197,249,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm0
+  .byte  196,193,57,97,209                   // vpunpcklwd    %xmm9,%xmm8,%xmm2
+  .byte  197,241,97,203                      // vpunpcklwd    %xmm3,%xmm1,%xmm1
+  .byte  196,193,33,97,218                   // vpunpcklwd    %xmm10,%xmm11,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,249,105,194                     // vpunpckhwd    %xmm2,%xmm0,%xmm0
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,241,105,203                     // vpunpckhwd    %xmm3,%xmm1,%xmm1
+  .byte  197,185,108,218                     // vpunpcklqdq   %xmm2,%xmm8,%xmm3
+  .byte  197,57,109,218                      // vpunpckhqdq   %xmm2,%xmm8,%xmm11
+  .byte  197,121,108,193                     // vpunpcklqdq   %xmm1,%xmm0,%xmm8
+  .byte  197,121,111,13,93,69,0,0            // vmovdqa       0x455d(%rip),%xmm9        # 69c0 <_sk_callback_avx+0x5c4>
+  .byte  196,193,97,219,193                  // vpand         %xmm9,%xmm3,%xmm0
+  .byte  196,65,41,239,210                   // vpxor         %xmm10,%xmm10,%xmm10
+  .byte  196,193,121,105,202                 // vpunpckhwd    %xmm10,%xmm0,%xmm1
+  .byte  196,193,249,126,201                 // vmovq         %xmm1,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  76,139,88,8                         // mov           0x8(%rax),%r11
+  .byte  196,129,122,16,20,147               // vmovss        (%r11,%r10,4),%xmm2
+  .byte  196,195,249,22,202,1                // vpextrq       $0x1,%xmm1,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,105,33,36,139,16              // vinsertps     $0x10,(%r11,%r9,4),%xmm2,%xmm12
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,129,122,16,20,139               // vmovss        (%r11,%r9,4),%xmm2
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  196,129,122,16,12,147               // vmovss        (%r11,%r10,4),%xmm1
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  196,129,122,16,28,147               // vmovss        (%r11,%r10,4),%xmm3
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  196,131,97,33,28,11,16              // vinsertps     $0x10,(%r11,%r9,1),%xmm3,%xmm3
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  196,129,122,16,4,139                // vmovss        (%r11,%r9,4),%xmm0
+  .byte  76,139,72,16                        // mov           0x10(%rax),%r9
+  .byte  196,227,25,33,210,32                // vinsertps     $0x20,%xmm2,%xmm12,%xmm2
+  .byte  196,227,105,33,201,48               // vinsertps     $0x30,%xmm1,%xmm2,%xmm1
+  .byte  196,129,122,16,20,19                // vmovss        (%r11,%r10,1),%xmm2
+  .byte  196,65,33,219,225                   // vpand         %xmm9,%xmm11,%xmm12
+  .byte  196,65,25,105,218                   // vpunpckhwd    %xmm10,%xmm12,%xmm11
+  .byte  196,65,249,126,218                  // vmovq         %xmm11,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,1,122,16,44,153                 // vmovss        (%r9,%r11,4),%xmm13
+  .byte  196,67,249,22,219,1                 // vpextrq       $0x1,%xmm11,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,17,33,28,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm13,%xmm11
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,66,121,51,244                   // vpmovzxwd     %xmm12,%xmm14
+  .byte  196,1,122,16,44,145                 // vmovss        (%r9,%r10,4),%xmm13
+  .byte  196,65,249,126,242                  // vmovq         %xmm14,%r10
+  .byte  196,1,122,16,36,153                 // vmovss        (%r9,%r11,4),%xmm12
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  196,1,122,16,60,153                 // vmovss        (%r9,%r11,4),%xmm15
+  .byte  196,67,249,22,243,1                 // vpextrq       $0x1,%xmm14,%r11
+  .byte  196,3,1,33,52,17,16                 // vinsertps     $0x10,(%r9,%r10,1),%xmm15,%xmm14
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  196,1,122,16,60,145                 // vmovss        (%r9,%r10,4),%xmm15
+  .byte  196,227,97,33,192,32                // vinsertps     $0x20,%xmm0,%xmm3,%xmm0
+  .byte  196,227,121,33,194,48               // vinsertps     $0x30,%xmm2,%xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  73,193,235,30                       // shr           $0x1e,%r11
+  .byte  196,129,122,16,52,25                // vmovss        (%r9,%r11,1),%xmm6
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  196,65,57,219,193                   // vpand         %xmm9,%xmm8,%xmm8
+  .byte  196,193,57,105,210                  // vpunpckhwd    %xmm10,%xmm8,%xmm2
+  .byte  196,193,249,126,209                 // vmovq         %xmm2,%r9
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,193,122,16,12,130               // vmovss        (%r10,%rax,4),%xmm1
+  .byte  196,227,249,22,208,1                // vpextrq       $0x1,%xmm2,%rax
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,113,33,12,138,16              // vinsertps     $0x10,(%r10,%r9,4),%xmm1,%xmm9
+  .byte  65,137,193                          // mov           %eax,%r9d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,194,121,51,200                  // vpmovzxwd     %xmm8,%xmm1
+  .byte  196,1,122,16,4,138                  // vmovss        (%r10,%r9,4),%xmm8
+  .byte  196,193,249,126,201                 // vmovq         %xmm1,%r9
+  .byte  196,65,122,16,20,130                // vmovss        (%r10,%rax,4),%xmm10
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  196,193,122,16,20,130               // vmovss        (%r10,%rax,4),%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  196,131,105,33,20,10,16             // vinsertps     $0x10,(%r10,%r9,1),%xmm2,%xmm2
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,193,122,16,60,130               // vmovss        (%r10,%rax,4),%xmm7
+  .byte  196,195,33,33,205,32                // vinsertps     $0x20,%xmm13,%xmm11,%xmm1
+  .byte  73,193,235,30                       // shr           $0x1e,%r11
+  .byte  196,1,122,16,28,26                  // vmovss        (%r10,%r11,1),%xmm11
+  .byte  196,195,113,33,204,48               // vinsertps     $0x30,%xmm12,%xmm1,%xmm1
+  .byte  196,195,9,33,223,32                 // vinsertps     $0x20,%xmm15,%xmm14,%xmm3
+  .byte  196,227,97,33,222,48                // vinsertps     $0x30,%xmm6,%xmm3,%xmm3
+  .byte  196,227,101,24,201,1                // vinsertf128   $0x1,%xmm1,%ymm3,%ymm1
+  .byte  196,195,49,33,216,32                // vinsertps     $0x20,%xmm8,%xmm9,%xmm3
+  .byte  196,195,97,33,218,48                // vinsertps     $0x30,%xmm10,%xmm3,%xmm3
+  .byte  196,227,105,33,215,32               // vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  .byte  196,195,105,33,211,48               // vinsertps     $0x30,%xmm11,%xmm2,%xmm2
+  .byte  196,227,109,24,211,1                // vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,82,64,0,0         // vbroadcastss  0x4052(%rip),%ymm3        # 6684 <_sk_callback_avx+0x288>
+  .byte  197,252,16,116,36,168               // vmovups       -0x58(%rsp),%ymm6
+  .byte  197,252,16,124,36,200               // vmovups       -0x38(%rsp),%ymm7
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,129,121,110,4,81                // vmovd         (%r9,%r10,2),%xmm0
+  .byte  196,129,121,196,68,81,4,2           // vpinsrw       $0x2,0x4(%r9,%r10,2),%xmm0,%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,5                               // jne           2659 <_sk_load_tables_rgb_u16_be_avx+0x27e>
+  .byte  233,212,253,255,255                 // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+  .byte  196,129,121,110,76,81,6             // vmovd         0x6(%r9,%r10,2),%xmm1
+  .byte  196,1,113,196,68,81,10,2            // vpinsrw       $0x2,0xa(%r9,%r10,2),%xmm1,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,26                              // jb            2688 <_sk_load_tables_rgb_u16_be_avx+0x2ad>
+  .byte  196,129,121,110,76,81,12            // vmovd         0xc(%r9,%r10,2),%xmm1
+  .byte  196,129,113,196,84,81,16,2          // vpinsrw       $0x2,0x10(%r9,%r10,2),%xmm1,%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  117,10                              // jne           268d <_sk_load_tables_rgb_u16_be_avx+0x2b2>
+  .byte  233,165,253,255,255                 // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+  .byte  233,160,253,255,255                 // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+  .byte  196,129,121,110,76,81,18            // vmovd         0x12(%r9,%r10,2),%xmm1
+  .byte  196,1,113,196,76,81,22,2            // vpinsrw       $0x2,0x16(%r9,%r10,2),%xmm1,%xmm9
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,26                              // jb            26bc <_sk_load_tables_rgb_u16_be_avx+0x2e1>
+  .byte  196,129,121,110,76,81,24            // vmovd         0x18(%r9,%r10,2),%xmm1
+  .byte  196,129,113,196,76,81,28,2          // vpinsrw       $0x2,0x1c(%r9,%r10,2),%xmm1,%xmm1
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  117,10                              // jne           26c1 <_sk_load_tables_rgb_u16_be_avx+0x2e6>
+  .byte  233,113,253,255,255                 // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+  .byte  233,108,253,255,255                 // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+  .byte  196,129,121,110,92,81,30            // vmovd         0x1e(%r9,%r10,2),%xmm3
+  .byte  196,1,97,196,92,81,34,2             // vpinsrw       $0x2,0x22(%r9,%r10,2),%xmm3,%xmm11
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,20                              // jb            26ea <_sk_load_tables_rgb_u16_be_avx+0x30f>
+  .byte  196,129,121,110,92,81,36            // vmovd         0x24(%r9,%r10,2),%xmm3
+  .byte  196,129,97,196,92,81,40,2           // vpinsrw       $0x2,0x28(%r9,%r10,2),%xmm3,%xmm3
+  .byte  233,67,253,255,255                  // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+  .byte  233,62,253,255,255                  // jmpq          242d <_sk_load_tables_rgb_u16_be_avx+0x52>
+
+HIDDEN _sk_byte_tables_avx
+.globl _sk_byte_tables_avx
+FUNCTION(_sk_byte_tables_avx)
+_sk_byte_tables_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,142,63,0,0          // vbroadcastss  0x3f8e(%rip),%ymm8        # 6688 <_sk_callback_avx+0x28c>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  197,125,91,200                      // vcvtps2dq     %ymm0,%ymm9
+  .byte  196,65,249,126,201                  // vmovq         %xmm9,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,131,121,32,4,19,0               // vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  .byte  196,67,249,22,202,1                 // vpextrq       $0x1,%xmm9,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,121,32,20,11,1                // vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,99,125,25,200,1                 // vextractf128  $0x1,%ymm9,%xmm0
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,67,41,32,201,2                  // vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,49,32,202,3                  // vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  196,3,121,32,20,19,0                // vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm10
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,41,32,4,11,1                // vpinsrb       $0x1,(%r11,%r9,1),%xmm10,%xmm0
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,195,121,32,193,2                // vpinsrb       $0x2,%r9d,%xmm0,%xmm0
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,121,32,210,3                 // vpinsrb       $0x3,%r10d,%xmm0,%xmm10
+  .byte  196,193,116,89,192                  // vmulps        %ymm8,%ymm1,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,12,25,0              // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,113,32,12,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm1
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,25,192,1                // vextractf128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,113,32,202,2                // vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,67,113,32,227,3                 // vpinsrb       $0x3,%r11d,%xmm1,%xmm12
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,12,25,0              // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,113,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,2                // vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  .byte  71,15,182,12,25                     // movzbl        (%r9,%r11,1),%r9d
+  .byte  196,67,121,32,233,3                 // vpinsrb       $0x3,%r9d,%xmm0,%xmm13
+  .byte  76,139,72,16                        // mov           0x10(%rax),%r9
+  .byte  196,193,108,89,200                  // vmulps        %ymm8,%ymm2,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,20,25,0              // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,20,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm2
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,105,32,210,2                // vpinsrb       $0x2,%r10d,%xmm2,%xmm2
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,67,105,32,219,3                 // vpinsrb       $0x3,%r11d,%xmm2,%xmm11
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,20,25,0              // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,12,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm1
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,113,32,202,2                // vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  71,15,182,12,25                     // movzbl        (%r9,%r11,1),%r9d
+  .byte  196,67,113,32,241,3                 // vpinsrb       $0x3,%r9d,%xmm1,%xmm14
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  196,193,100,89,200                  // vmulps        %ymm8,%ymm3,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,193,249,126,201                 // vmovq         %xmm1,%r9
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,195,121,32,28,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm3
+  .byte  196,227,249,22,200,1                // vpextrq       $0x1,%xmm1,%rax
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,97,32,28,10,1               // vpinsrb       $0x1,(%r10,%r9,1),%xmm3,%xmm3
+  .byte  65,137,193                          // mov           %eax,%r9d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  71,15,182,12,10                     // movzbl        (%r10,%r9,1),%r9d
+  .byte  196,195,97,32,217,2                 // vpinsrb       $0x2,%r9d,%xmm3,%xmm3
+  .byte  196,193,249,126,201                 // vmovq         %xmm1,%r9
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,99,97,32,192,3                  // vpinsrb       $0x3,%eax,%xmm3,%xmm8
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,195,121,32,4,2,0                // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,121,32,4,10,1               // vpinsrb       $0x1,(%r10,%r9,1),%xmm0,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,99,121,32,248,2                 // vpinsrb       $0x2,%eax,%xmm0,%xmm15
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,26                      // movzbl        (%r10,%r11,1),%eax
+  .byte  196,194,121,49,193                  // vpmovzxbd     %xmm9,%xmm0
+  .byte  196,194,121,49,202                  // vpmovzxbd     %xmm10,%xmm1
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,194,121,49,204                  // vpmovzxbd     %xmm12,%xmm1
+  .byte  196,194,121,49,213                  // vpmovzxbd     %xmm13,%xmm2
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,98,125,24,13,56,61,0,0          // vbroadcastss  0x3d38(%rip),%ymm9        # 668c <_sk_callback_avx+0x290>
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,201                  // vmulps        %ymm9,%ymm1,%ymm1
+  .byte  196,194,121,49,211                  // vpmovzxbd     %xmm11,%xmm2
+  .byte  196,194,121,49,222                  // vpmovzxbd     %xmm14,%xmm3
+  .byte  196,227,109,24,211,1                // vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,209                  // vmulps        %ymm9,%ymm2,%ymm2
+  .byte  196,66,121,49,192                   // vpmovzxbd     %xmm8,%xmm8
+  .byte  196,227,1,32,216,3                  // vpinsrb       $0x3,%eax,%xmm15,%xmm3
+  .byte  196,226,121,49,219                  // vpmovzxbd     %xmm3,%xmm3
+  .byte  196,227,61,24,219,1                 // vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,217                  // vmulps        %ymm9,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_byte_tables_rgb_avx
+.globl _sk_byte_tables_rgb_avx
+FUNCTION(_sk_byte_tables_rgb_avx)
+_sk_byte_tables_rgb_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,139,72,24                        // mov           0x18(%rax),%r9d
+  .byte  65,255,201                          // dec           %r9d
+  .byte  196,65,121,110,193                  // vmovd         %r9d,%xmm8
+  .byte  196,65,121,112,192,0                // vpshufd       $0x0,%xmm8,%xmm8
+  .byte  196,67,61,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,125,91,200                      // vcvtps2dq     %ymm0,%ymm9
+  .byte  196,65,249,126,201                  // vmovq         %xmm9,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,131,121,32,4,19,0               // vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  .byte  196,67,249,22,202,1                 // vpextrq       $0x1,%xmm9,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,3,121,32,20,11,1                // vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,99,125,25,200,1                 // vextractf128  $0x1,%ymm9,%xmm0
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,67,41,32,201,2                  // vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,49,32,202,3                  // vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  196,3,121,32,20,19,0                // vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm10
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,41,32,4,11,1                // vpinsrb       $0x1,(%r11,%r9,1),%xmm10,%xmm0
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  71,15,182,12,11                     // movzbl        (%r11,%r9,1),%r9d
+  .byte  196,195,121,32,193,2                // vpinsrb       $0x2,%r9d,%xmm0,%xmm0
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  71,15,182,20,19                     // movzbl        (%r11,%r10,1),%r10d
+  .byte  196,67,121,32,218,3                 // vpinsrb       $0x3,%r10d,%xmm0,%xmm11
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,4,25,0               // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,121,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,2                // vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  196,67,121,32,211,3                 // vpinsrb       $0x3,%r11d,%xmm0,%xmm10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  196,131,121,32,4,25,0               // vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,121,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  .byte  69,137,218                          // mov           %r11d,%r10d
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  196,195,121,32,194,2                // vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  71,15,182,12,25                     // movzbl        (%r9,%r11,1),%r9d
+  .byte  196,67,121,32,225,3                 // vpinsrb       $0x3,%r9d,%xmm0,%xmm12
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  197,188,89,194                      // vmulps        %ymm2,%ymm8,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,195,121,32,20,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm2
+  .byte  196,227,249,22,192,1                // vpextrq       $0x1,%xmm0,%rax
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,105,32,20,10,1              // vpinsrb       $0x1,(%r10,%r9,1),%xmm2,%xmm2
+  .byte  65,137,193                          // mov           %eax,%r9d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,227,125,25,192,1                // vextractf128  $0x1,%ymm0,%xmm0
+  .byte  71,15,182,12,10                     // movzbl        (%r10,%r9,1),%r9d
+  .byte  196,195,105,32,209,2                // vpinsrb       $0x2,%r9d,%xmm2,%xmm2
+  .byte  196,193,249,126,193                 // vmovq         %xmm0,%r9
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,99,105,32,192,3                 // vpinsrb       $0x3,%eax,%xmm2,%xmm8
+  .byte  68,137,200                          // mov           %r9d,%eax
+  .byte  196,195,121,32,12,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,131,113,32,4,10,1               // vpinsrb       $0x1,(%r10,%r9,1),%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  196,99,121,32,232,2                 // vpinsrb       $0x2,%eax,%xmm0,%xmm13
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,26                      // movzbl        (%r10,%r11,1),%eax
+  .byte  196,194,121,49,193                  // vpmovzxbd     %xmm9,%xmm0
+  .byte  196,194,121,49,203                  // vpmovzxbd     %xmm11,%xmm1
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,13,25,59,0,0          // vbroadcastss  0x3b19(%rip),%ymm9        # 6690 <_sk_callback_avx+0x294>
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  196,194,121,49,202                  // vpmovzxbd     %xmm10,%xmm1
+  .byte  196,194,121,49,212                  // vpmovzxbd     %xmm12,%xmm2
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,201                  // vmulps        %ymm9,%ymm1,%ymm1
+  .byte  196,66,121,49,192                   // vpmovzxbd     %xmm8,%xmm8
+  .byte  196,227,17,32,208,3                 // vpinsrb       $0x3,%eax,%xmm13,%xmm2
+  .byte  196,226,121,49,210                  // vpmovzxbd     %xmm2,%xmm2
+  .byte  196,227,61,24,210,1                 // vinsertf128   $0x1,%xmm2,%ymm8,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,209                  // vmulps        %ymm9,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_r_avx
+.globl _sk_table_r_avx
+FUNCTION(_sk_table_r_avx)
+_sk_table_r_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,65,121,112,192,0                // vpshufd       $0x0,%xmm8,%xmm8
+  .byte  196,67,61,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,125,91,192                      // vcvtps2dq     %ymm0,%ymm8
+  .byte  196,99,125,25,192,1                 // vextractf128  $0x1,%ymm8,%xmm0
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,65,122,16,12,129                // vmovss        (%r9,%rax,4),%xmm9
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,49,33,12,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,20,129                // vmovss        (%r9,%rax,4),%xmm10
+  .byte  196,65,249,126,194                  // vmovq         %xmm8,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,28,153                 // vmovss        (%r9,%r11,4),%xmm11
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,193,122,16,4,129                // vmovss        (%r9,%rax,4),%xmm0
+  .byte  196,67,249,22,195,1                 // vpextrq       $0x1,%xmm8,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,121,33,4,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm0,%xmm8
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,36,129                // vmovss        (%r9,%rax,4),%xmm12
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,44,153                 // vmovss        (%r9,%r11,4),%xmm13
+  .byte  196,195,49,33,194,32                // vinsertps     $0x20,%xmm10,%xmm9,%xmm0
+  .byte  196,67,121,33,203,48                // vinsertps     $0x30,%xmm11,%xmm0,%xmm9
+  .byte  196,195,57,33,196,32                // vinsertps     $0x20,%xmm12,%xmm8,%xmm0
+  .byte  196,195,121,33,197,48               // vinsertps     $0x30,%xmm13,%xmm0,%xmm0
+  .byte  196,195,125,24,193,1                // vinsertf128   $0x1,%xmm9,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_g_avx
+.globl _sk_table_g_avx
+FUNCTION(_sk_table_g_avx)
+_sk_table_g_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,65,121,112,192,0                // vpshufd       $0x0,%xmm8,%xmm8
+  .byte  196,67,61,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,125,91,193                      // vcvtps2dq     %ymm1,%ymm8
+  .byte  196,99,125,25,193,1                 // vextractf128  $0x1,%ymm8,%xmm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,65,122,16,12,129                // vmovss        (%r9,%rax,4),%xmm9
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,49,33,12,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,20,129                // vmovss        (%r9,%rax,4),%xmm10
+  .byte  196,65,249,126,194                  // vmovq         %xmm8,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,28,153                 // vmovss        (%r9,%r11,4),%xmm11
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,193,122,16,12,129               // vmovss        (%r9,%rax,4),%xmm1
+  .byte  196,67,249,22,195,1                 // vpextrq       $0x1,%xmm8,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,113,33,4,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm1,%xmm8
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,36,129                // vmovss        (%r9,%rax,4),%xmm12
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,44,153                 // vmovss        (%r9,%r11,4),%xmm13
+  .byte  196,195,49,33,202,32                // vinsertps     $0x20,%xmm10,%xmm9,%xmm1
+  .byte  196,67,113,33,203,48                // vinsertps     $0x30,%xmm11,%xmm1,%xmm9
+  .byte  196,195,57,33,204,32                // vinsertps     $0x20,%xmm12,%xmm8,%xmm1
+  .byte  196,195,113,33,205,48               // vinsertps     $0x30,%xmm13,%xmm1,%xmm1
+  .byte  196,195,117,24,201,1                // vinsertf128   $0x1,%xmm9,%ymm1,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_b_avx
+.globl _sk_table_b_avx
+FUNCTION(_sk_table_b_avx)
+_sk_table_b_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,65,121,112,192,0                // vpshufd       $0x0,%xmm8,%xmm8
+  .byte  196,67,61,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,210                      // vmulps        %ymm2,%ymm8,%ymm2
+  .byte  197,125,91,194                      // vcvtps2dq     %ymm2,%ymm8
+  .byte  196,99,125,25,194,1                 // vextractf128  $0x1,%ymm8,%xmm2
+  .byte  196,193,249,126,210                 // vmovq         %xmm2,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,65,122,16,12,129                // vmovss        (%r9,%rax,4),%xmm9
+  .byte  196,195,249,22,211,1                // vpextrq       $0x1,%xmm2,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,49,33,12,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,20,129                // vmovss        (%r9,%rax,4),%xmm10
+  .byte  196,65,249,126,194                  // vmovq         %xmm8,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,28,153                 // vmovss        (%r9,%r11,4),%xmm11
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,193,122,16,20,129               // vmovss        (%r9,%rax,4),%xmm2
+  .byte  196,67,249,22,195,1                 // vpextrq       $0x1,%xmm8,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,105,33,4,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm2,%xmm8
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,36,129                // vmovss        (%r9,%rax,4),%xmm12
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,44,153                 // vmovss        (%r9,%r11,4),%xmm13
+  .byte  196,195,49,33,210,32                // vinsertps     $0x20,%xmm10,%xmm9,%xmm2
+  .byte  196,67,105,33,203,48                // vinsertps     $0x30,%xmm11,%xmm2,%xmm9
+  .byte  196,195,57,33,212,32                // vinsertps     $0x20,%xmm12,%xmm8,%xmm2
+  .byte  196,195,105,33,213,48               // vinsertps     $0x30,%xmm13,%xmm2,%xmm2
+  .byte  196,195,109,24,209,1                // vinsertf128   $0x1,%xmm9,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_a_avx
+.globl _sk_table_a_avx
+FUNCTION(_sk_table_a_avx)
+_sk_table_a_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  197,121,110,192                     // vmovd         %eax,%xmm8
+  .byte  196,65,121,112,192,0                // vpshufd       $0x0,%xmm8,%xmm8
+  .byte  196,67,61,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  .byte  196,65,124,91,192                   // vcvtdq2ps     %ymm8,%ymm8
+  .byte  197,188,89,219                      // vmulps        %ymm3,%ymm8,%ymm3
+  .byte  197,125,91,195                      // vcvtps2dq     %ymm3,%ymm8
+  .byte  196,99,125,25,195,1                 // vextractf128  $0x1,%ymm8,%xmm3
+  .byte  196,193,249,126,218                 // vmovq         %xmm3,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,65,122,16,12,129                // vmovss        (%r9,%rax,4),%xmm9
+  .byte  196,195,249,22,219,1                // vpextrq       $0x1,%xmm3,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,49,33,12,145,16               // vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,20,129                // vmovss        (%r9,%rax,4),%xmm10
+  .byte  196,65,249,126,194                  // vmovq         %xmm8,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,28,153                 // vmovss        (%r9,%r11,4),%xmm11
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,193,122,16,28,129               // vmovss        (%r9,%rax,4),%xmm3
+  .byte  196,67,249,22,195,1                 // vpextrq       $0x1,%xmm8,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,3,97,33,4,145,16                // vinsertps     $0x10,(%r9,%r10,4),%xmm3,%xmm8
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,65,122,16,36,129                // vmovss        (%r9,%rax,4),%xmm12
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,1,122,16,44,153                 // vmovss        (%r9,%r11,4),%xmm13
+  .byte  196,195,49,33,218,32                // vinsertps     $0x20,%xmm10,%xmm9,%xmm3
+  .byte  196,67,97,33,203,48                 // vinsertps     $0x30,%xmm11,%xmm3,%xmm9
+  .byte  196,195,57,33,220,32                // vinsertps     $0x20,%xmm12,%xmm8,%xmm3
+  .byte  196,195,97,33,221,48                // vinsertps     $0x30,%xmm13,%xmm3,%xmm3
+  .byte  196,195,101,24,217,1                // vinsertf128   $0x1,%xmm9,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_r_avx
+.globl _sk_parametric_r_avx
+FUNCTION(_sk_parametric_r_avx)
+_sk_parametric_r_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,124,194,192,2                // vcmpleps      %ymm8,%ymm0,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  197,52,89,200                       // vmulps        %ymm0,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  197,172,89,192                      // vmulps        %ymm0,%ymm10,%ymm0
+  .byte  196,193,124,88,195                  // vaddps        %ymm11,%ymm0,%ymm0
+  .byte  196,98,125,24,16                    // vbroadcastss  (%rax),%ymm10
+  .byte  197,124,91,216                      // vcvtdq2ps     %ymm0,%ymm11
+  .byte  196,98,125,24,37,198,55,0,0         // vbroadcastss  0x37c6(%rip),%ymm12        # 6694 <_sk_callback_avx+0x298>
+  .byte  196,65,36,89,220                    // vmulps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,188,55,0,0         // vbroadcastss  0x37bc(%rip),%ymm12        # 6698 <_sk_callback_avx+0x29c>
+  .byte  196,193,124,84,196                  // vandps        %ymm12,%ymm0,%ymm0
+  .byte  196,98,125,24,37,178,55,0,0         // vbroadcastss  0x37b2(%rip),%ymm12        # 669c <_sk_callback_avx+0x2a0>
+  .byte  196,193,124,86,196                  // vorps         %ymm12,%ymm0,%ymm0
+  .byte  196,98,125,24,37,168,55,0,0         // vbroadcastss  0x37a8(%rip),%ymm12        # 66a0 <_sk_callback_avx+0x2a4>
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,158,55,0,0         // vbroadcastss  0x379e(%rip),%ymm12        # 66a4 <_sk_callback_avx+0x2a8>
+  .byte  196,65,124,89,228                   // vmulps        %ymm12,%ymm0,%ymm12
+  .byte  196,65,36,92,220                    // vsubps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,143,55,0,0         // vbroadcastss  0x378f(%rip),%ymm12        # 66a8 <_sk_callback_avx+0x2ac>
+  .byte  196,193,124,88,196                  // vaddps        %ymm12,%ymm0,%ymm0
+  .byte  196,98,125,24,37,133,55,0,0         // vbroadcastss  0x3785(%rip),%ymm12        # 66ac <_sk_callback_avx+0x2b0>
+  .byte  197,156,94,192                      // vdivps        %ymm0,%ymm12,%ymm0
+  .byte  197,164,92,192                      // vsubps        %ymm0,%ymm11,%ymm0
+  .byte  197,172,89,192                      // vmulps        %ymm0,%ymm10,%ymm0
+  .byte  196,99,125,8,208,1                  // vroundps      $0x1,%ymm0,%ymm10
+  .byte  196,65,124,92,210                   // vsubps        %ymm10,%ymm0,%ymm10
+  .byte  196,98,125,24,29,105,55,0,0         // vbroadcastss  0x3769(%rip),%ymm11        # 66b0 <_sk_callback_avx+0x2b4>
+  .byte  196,193,124,88,195                  // vaddps        %ymm11,%ymm0,%ymm0
+  .byte  196,98,125,24,29,95,55,0,0          // vbroadcastss  0x375f(%rip),%ymm11        # 66b4 <_sk_callback_avx+0x2b8>
+  .byte  196,65,44,89,219                    // vmulps        %ymm11,%ymm10,%ymm11
+  .byte  196,193,124,92,195                  // vsubps        %ymm11,%ymm0,%ymm0
+  .byte  196,98,125,24,29,80,55,0,0          // vbroadcastss  0x3750(%rip),%ymm11        # 66b8 <_sk_callback_avx+0x2bc>
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,98,125,24,29,70,55,0,0          // vbroadcastss  0x3746(%rip),%ymm11        # 66bc <_sk_callback_avx+0x2c0>
+  .byte  196,65,36,94,210                    // vdivps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,124,88,194                  // vaddps        %ymm10,%ymm0,%ymm0
+  .byte  196,98,125,24,21,55,55,0,0          // vbroadcastss  0x3737(%rip),%ymm10        # 66c0 <_sk_callback_avx+0x2c4>
+  .byte  196,193,124,89,194                  // vmulps        %ymm10,%ymm0,%ymm0
+  .byte  197,253,91,192                      // vcvtps2dq     %ymm0,%ymm0
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,124,88,194                  // vaddps        %ymm10,%ymm0,%ymm0
+  .byte  196,195,125,74,193,128              // vblendvps     %ymm8,%ymm9,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,124,95,192                  // vmaxps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,5,14,55,0,0           // vbroadcastss  0x370e(%rip),%ymm8        # 66c4 <_sk_callback_avx+0x2c8>
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_g_avx
+.globl _sk_parametric_g_avx
+FUNCTION(_sk_parametric_g_avx)
+_sk_parametric_g_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,116,194,192,2                // vcmpleps      %ymm8,%ymm1,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  197,52,89,201                       // vmulps        %ymm1,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  197,172,89,201                      // vmulps        %ymm1,%ymm10,%ymm1
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,16                    // vbroadcastss  (%rax),%ymm10
+  .byte  197,124,91,217                      // vcvtdq2ps     %ymm1,%ymm11
+  .byte  196,98,125,24,37,191,54,0,0         // vbroadcastss  0x36bf(%rip),%ymm12        # 66c8 <_sk_callback_avx+0x2cc>
+  .byte  196,65,36,89,220                    // vmulps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,181,54,0,0         // vbroadcastss  0x36b5(%rip),%ymm12        # 66cc <_sk_callback_avx+0x2d0>
+  .byte  196,193,116,84,204                  // vandps        %ymm12,%ymm1,%ymm1
+  .byte  196,98,125,24,37,171,54,0,0         // vbroadcastss  0x36ab(%rip),%ymm12        # 66d0 <_sk_callback_avx+0x2d4>
+  .byte  196,193,116,86,204                  // vorps         %ymm12,%ymm1,%ymm1
+  .byte  196,98,125,24,37,161,54,0,0         // vbroadcastss  0x36a1(%rip),%ymm12        # 66d4 <_sk_callback_avx+0x2d8>
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,151,54,0,0         // vbroadcastss  0x3697(%rip),%ymm12        # 66d8 <_sk_callback_avx+0x2dc>
+  .byte  196,65,116,89,228                   // vmulps        %ymm12,%ymm1,%ymm12
+  .byte  196,65,36,92,220                    // vsubps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,136,54,0,0         // vbroadcastss  0x3688(%rip),%ymm12        # 66dc <_sk_callback_avx+0x2e0>
+  .byte  196,193,116,88,204                  // vaddps        %ymm12,%ymm1,%ymm1
+  .byte  196,98,125,24,37,126,54,0,0         // vbroadcastss  0x367e(%rip),%ymm12        # 66e0 <_sk_callback_avx+0x2e4>
+  .byte  197,156,94,201                      // vdivps        %ymm1,%ymm12,%ymm1
+  .byte  197,164,92,201                      // vsubps        %ymm1,%ymm11,%ymm1
+  .byte  197,172,89,201                      // vmulps        %ymm1,%ymm10,%ymm1
+  .byte  196,99,125,8,209,1                  // vroundps      $0x1,%ymm1,%ymm10
+  .byte  196,65,116,92,210                   // vsubps        %ymm10,%ymm1,%ymm10
+  .byte  196,98,125,24,29,98,54,0,0          // vbroadcastss  0x3662(%rip),%ymm11        # 66e4 <_sk_callback_avx+0x2e8>
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,29,88,54,0,0          // vbroadcastss  0x3658(%rip),%ymm11        # 66e8 <_sk_callback_avx+0x2ec>
+  .byte  196,65,44,89,219                    // vmulps        %ymm11,%ymm10,%ymm11
+  .byte  196,193,116,92,203                  // vsubps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,29,73,54,0,0          // vbroadcastss  0x3649(%rip),%ymm11        # 66ec <_sk_callback_avx+0x2f0>
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,98,125,24,29,63,54,0,0          // vbroadcastss  0x363f(%rip),%ymm11        # 66f0 <_sk_callback_avx+0x2f4>
+  .byte  196,65,36,94,210                    // vdivps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  196,98,125,24,21,48,54,0,0          // vbroadcastss  0x3630(%rip),%ymm10        # 66f4 <_sk_callback_avx+0x2f8>
+  .byte  196,193,116,89,202                  // vmulps        %ymm10,%ymm1,%ymm1
+  .byte  197,253,91,201                      // vcvtps2dq     %ymm1,%ymm1
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,116,88,202                  // vaddps        %ymm10,%ymm1,%ymm1
+  .byte  196,195,117,74,201,128              // vblendvps     %ymm8,%ymm9,%ymm1,%ymm1
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,116,95,200                  // vmaxps        %ymm8,%ymm1,%ymm1
+  .byte  196,98,125,24,5,7,54,0,0            // vbroadcastss  0x3607(%rip),%ymm8        # 66f8 <_sk_callback_avx+0x2fc>
+  .byte  196,193,116,93,200                  // vminps        %ymm8,%ymm1,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_b_avx
+.globl _sk_parametric_b_avx
+FUNCTION(_sk_parametric_b_avx)
+_sk_parametric_b_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,108,194,192,2                // vcmpleps      %ymm8,%ymm2,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  197,52,89,202                       // vmulps        %ymm2,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  196,98,125,24,16                    // vbroadcastss  (%rax),%ymm10
+  .byte  197,124,91,218                      // vcvtdq2ps     %ymm2,%ymm11
+  .byte  196,98,125,24,37,184,53,0,0         // vbroadcastss  0x35b8(%rip),%ymm12        # 66fc <_sk_callback_avx+0x300>
+  .byte  196,65,36,89,220                    // vmulps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,174,53,0,0         // vbroadcastss  0x35ae(%rip),%ymm12        # 6700 <_sk_callback_avx+0x304>
+  .byte  196,193,108,84,212                  // vandps        %ymm12,%ymm2,%ymm2
+  .byte  196,98,125,24,37,164,53,0,0         // vbroadcastss  0x35a4(%rip),%ymm12        # 6704 <_sk_callback_avx+0x308>
+  .byte  196,193,108,86,212                  // vorps         %ymm12,%ymm2,%ymm2
+  .byte  196,98,125,24,37,154,53,0,0         // vbroadcastss  0x359a(%rip),%ymm12        # 6708 <_sk_callback_avx+0x30c>
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,144,53,0,0         // vbroadcastss  0x3590(%rip),%ymm12        # 670c <_sk_callback_avx+0x310>
+  .byte  196,65,108,89,228                   // vmulps        %ymm12,%ymm2,%ymm12
+  .byte  196,65,36,92,220                    // vsubps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,129,53,0,0         // vbroadcastss  0x3581(%rip),%ymm12        # 6710 <_sk_callback_avx+0x314>
+  .byte  196,193,108,88,212                  // vaddps        %ymm12,%ymm2,%ymm2
+  .byte  196,98,125,24,37,119,53,0,0         // vbroadcastss  0x3577(%rip),%ymm12        # 6714 <_sk_callback_avx+0x318>
+  .byte  197,156,94,210                      // vdivps        %ymm2,%ymm12,%ymm2
+  .byte  197,164,92,210                      // vsubps        %ymm2,%ymm11,%ymm2
+  .byte  197,172,89,210                      // vmulps        %ymm2,%ymm10,%ymm2
+  .byte  196,99,125,8,210,1                  // vroundps      $0x1,%ymm2,%ymm10
+  .byte  196,65,108,92,210                   // vsubps        %ymm10,%ymm2,%ymm10
+  .byte  196,98,125,24,29,91,53,0,0          // vbroadcastss  0x355b(%rip),%ymm11        # 6718 <_sk_callback_avx+0x31c>
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  196,98,125,24,29,81,53,0,0          // vbroadcastss  0x3551(%rip),%ymm11        # 671c <_sk_callback_avx+0x320>
+  .byte  196,65,44,89,219                    // vmulps        %ymm11,%ymm10,%ymm11
+  .byte  196,193,108,92,211                  // vsubps        %ymm11,%ymm2,%ymm2
+  .byte  196,98,125,24,29,66,53,0,0          // vbroadcastss  0x3542(%rip),%ymm11        # 6720 <_sk_callback_avx+0x324>
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,98,125,24,29,56,53,0,0          // vbroadcastss  0x3538(%rip),%ymm11        # 6724 <_sk_callback_avx+0x328>
+  .byte  196,65,36,94,210                    // vdivps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  196,98,125,24,21,41,53,0,0          // vbroadcastss  0x3529(%rip),%ymm10        # 6728 <_sk_callback_avx+0x32c>
+  .byte  196,193,108,89,210                  // vmulps        %ymm10,%ymm2,%ymm2
+  .byte  197,253,91,210                      // vcvtps2dq     %ymm2,%ymm2
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,108,88,210                  // vaddps        %ymm10,%ymm2,%ymm2
+  .byte  196,195,109,74,209,128              // vblendvps     %ymm8,%ymm9,%ymm2,%ymm2
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,108,95,208                  // vmaxps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,24,5,0,53,0,0            // vbroadcastss  0x3500(%rip),%ymm8        # 672c <_sk_callback_avx+0x330>
+  .byte  196,193,108,93,208                  // vminps        %ymm8,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_a_avx
+.globl _sk_parametric_a_avx
+FUNCTION(_sk_parametric_a_avx)
+_sk_parametric_a_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,64,16                 // vbroadcastss  0x10(%rax),%ymm8
+  .byte  196,65,100,194,192,2                // vcmpleps      %ymm8,%ymm3,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  197,52,89,203                       // vmulps        %ymm3,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,4                  // vbroadcastss  0x4(%rax),%ymm10
+  .byte  196,98,125,24,88,8                  // vbroadcastss  0x8(%rax),%ymm11
+  .byte  197,172,89,219                      // vmulps        %ymm3,%ymm10,%ymm3
+  .byte  196,193,100,88,219                  // vaddps        %ymm11,%ymm3,%ymm3
+  .byte  196,98,125,24,16                    // vbroadcastss  (%rax),%ymm10
+  .byte  197,124,91,219                      // vcvtdq2ps     %ymm3,%ymm11
+  .byte  196,98,125,24,37,177,52,0,0         // vbroadcastss  0x34b1(%rip),%ymm12        # 6730 <_sk_callback_avx+0x334>
+  .byte  196,65,36,89,220                    // vmulps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,167,52,0,0         // vbroadcastss  0x34a7(%rip),%ymm12        # 6734 <_sk_callback_avx+0x338>
+  .byte  196,193,100,84,220                  // vandps        %ymm12,%ymm3,%ymm3
+  .byte  196,98,125,24,37,157,52,0,0         // vbroadcastss  0x349d(%rip),%ymm12        # 6738 <_sk_callback_avx+0x33c>
+  .byte  196,193,100,86,220                  // vorps         %ymm12,%ymm3,%ymm3
+  .byte  196,98,125,24,37,147,52,0,0         // vbroadcastss  0x3493(%rip),%ymm12        # 673c <_sk_callback_avx+0x340>
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,137,52,0,0         // vbroadcastss  0x3489(%rip),%ymm12        # 6740 <_sk_callback_avx+0x344>
+  .byte  196,65,100,89,228                   // vmulps        %ymm12,%ymm3,%ymm12
+  .byte  196,65,36,92,220                    // vsubps        %ymm12,%ymm11,%ymm11
+  .byte  196,98,125,24,37,122,52,0,0         // vbroadcastss  0x347a(%rip),%ymm12        # 6744 <_sk_callback_avx+0x348>
+  .byte  196,193,100,88,220                  // vaddps        %ymm12,%ymm3,%ymm3
+  .byte  196,98,125,24,37,112,52,0,0         // vbroadcastss  0x3470(%rip),%ymm12        # 6748 <_sk_callback_avx+0x34c>
+  .byte  197,156,94,219                      // vdivps        %ymm3,%ymm12,%ymm3
+  .byte  197,164,92,219                      // vsubps        %ymm3,%ymm11,%ymm3
+  .byte  197,172,89,219                      // vmulps        %ymm3,%ymm10,%ymm3
+  .byte  196,99,125,8,211,1                  // vroundps      $0x1,%ymm3,%ymm10
+  .byte  196,65,100,92,210                   // vsubps        %ymm10,%ymm3,%ymm10
+  .byte  196,98,125,24,29,84,52,0,0          // vbroadcastss  0x3454(%rip),%ymm11        # 674c <_sk_callback_avx+0x350>
+  .byte  196,193,100,88,219                  // vaddps        %ymm11,%ymm3,%ymm3
+  .byte  196,98,125,24,29,74,52,0,0          // vbroadcastss  0x344a(%rip),%ymm11        # 6750 <_sk_callback_avx+0x354>
+  .byte  196,65,44,89,219                    // vmulps        %ymm11,%ymm10,%ymm11
+  .byte  196,193,100,92,219                  // vsubps        %ymm11,%ymm3,%ymm3
+  .byte  196,98,125,24,29,59,52,0,0          // vbroadcastss  0x343b(%rip),%ymm11        # 6754 <_sk_callback_avx+0x358>
+  .byte  196,65,36,92,210                    // vsubps        %ymm10,%ymm11,%ymm10
+  .byte  196,98,125,24,29,49,52,0,0          // vbroadcastss  0x3431(%rip),%ymm11        # 6758 <_sk_callback_avx+0x35c>
+  .byte  196,65,36,94,210                    // vdivps        %ymm10,%ymm11,%ymm10
+  .byte  196,193,100,88,218                  // vaddps        %ymm10,%ymm3,%ymm3
+  .byte  196,98,125,24,21,34,52,0,0          // vbroadcastss  0x3422(%rip),%ymm10        # 675c <_sk_callback_avx+0x360>
+  .byte  196,193,100,89,218                  // vmulps        %ymm10,%ymm3,%ymm3
+  .byte  197,253,91,219                      // vcvtps2dq     %ymm3,%ymm3
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,193,100,88,218                  // vaddps        %ymm10,%ymm3,%ymm3
+  .byte  196,195,101,74,217,128              // vblendvps     %ymm8,%ymm9,%ymm3,%ymm3
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  196,193,100,95,216                  // vmaxps        %ymm8,%ymm3,%ymm3
+  .byte  196,98,125,24,5,249,51,0,0          // vbroadcastss  0x33f9(%rip),%ymm8        # 6760 <_sk_callback_avx+0x364>
+  .byte  196,193,100,93,216                  // vminps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lab_to_xyz_avx
+.globl _sk_lab_to_xyz_avx
+FUNCTION(_sk_lab_to_xyz_avx)
+_sk_lab_to_xyz_avx:
+  .byte  196,98,125,24,5,235,51,0,0          // vbroadcastss  0x33eb(%rip),%ymm8        # 6764 <_sk_callback_avx+0x368>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,5,225,51,0,0          // vbroadcastss  0x33e1(%rip),%ymm8        # 6768 <_sk_callback_avx+0x36c>
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  196,98,125,24,13,215,51,0,0         // vbroadcastss  0x33d7(%rip),%ymm9        # 676c <_sk_callback_avx+0x370>
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  196,193,108,88,209                  // vaddps        %ymm9,%ymm2,%ymm2
+  .byte  196,98,125,24,5,195,51,0,0          // vbroadcastss  0x33c3(%rip),%ymm8        # 6770 <_sk_callback_avx+0x374>
+  .byte  196,193,124,88,192                  // vaddps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,5,185,51,0,0          // vbroadcastss  0x33b9(%rip),%ymm8        # 6774 <_sk_callback_avx+0x378>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,5,175,51,0,0          // vbroadcastss  0x33af(%rip),%ymm8        # 6778 <_sk_callback_avx+0x37c>
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  197,252,88,201                      // vaddps        %ymm1,%ymm0,%ymm1
+  .byte  196,98,125,24,5,161,51,0,0          // vbroadcastss  0x33a1(%rip),%ymm8        # 677c <_sk_callback_avx+0x380>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,252,92,210                      // vsubps        %ymm2,%ymm0,%ymm2
+  .byte  197,116,89,193                      // vmulps        %ymm1,%ymm1,%ymm8
+  .byte  196,65,116,89,192                   // vmulps        %ymm8,%ymm1,%ymm8
+  .byte  196,98,125,24,13,138,51,0,0         // vbroadcastss  0x338a(%rip),%ymm9        # 6780 <_sk_callback_avx+0x384>
+  .byte  196,65,52,194,208,1                 // vcmpltps      %ymm8,%ymm9,%ymm10
+  .byte  196,98,125,24,29,127,51,0,0         // vbroadcastss  0x337f(%rip),%ymm11        # 6784 <_sk_callback_avx+0x388>
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  196,98,125,24,37,117,51,0,0         // vbroadcastss  0x3375(%rip),%ymm12        # 6788 <_sk_callback_avx+0x38c>
+  .byte  196,193,116,89,204                  // vmulps        %ymm12,%ymm1,%ymm1
+  .byte  196,67,117,74,192,160               // vblendvps     %ymm10,%ymm8,%ymm1,%ymm8
+  .byte  197,252,89,200                      // vmulps        %ymm0,%ymm0,%ymm1
+  .byte  197,252,89,201                      // vmulps        %ymm1,%ymm0,%ymm1
+  .byte  197,52,194,209,1                    // vcmpltps      %ymm1,%ymm9,%ymm10
+  .byte  196,193,124,88,195                  // vaddps        %ymm11,%ymm0,%ymm0
+  .byte  196,193,124,89,196                  // vmulps        %ymm12,%ymm0,%ymm0
+  .byte  196,227,125,74,201,160              // vblendvps     %ymm10,%ymm1,%ymm0,%ymm1
+  .byte  197,236,89,194                      // vmulps        %ymm2,%ymm2,%ymm0
+  .byte  197,236,89,192                      // vmulps        %ymm0,%ymm2,%ymm0
+  .byte  197,52,194,200,1                    // vcmpltps      %ymm0,%ymm9,%ymm9
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  196,193,108,89,212                  // vmulps        %ymm12,%ymm2,%ymm2
+  .byte  196,227,109,74,208,144              // vblendvps     %ymm9,%ymm0,%ymm2,%ymm2
+  .byte  196,226,125,24,5,43,51,0,0          // vbroadcastss  0x332b(%rip),%ymm0        # 678c <_sk_callback_avx+0x390>
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  196,98,125,24,5,34,51,0,0           // vbroadcastss  0x3322(%rip),%ymm8        # 6790 <_sk_callback_avx+0x394>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_a8_avx
+.globl _sk_load_a8_avx
+FUNCTION(_sk_load_a8_avx)
+_sk_load_a8_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,62                              // jne           34c5 <_sk_load_a8_avx+0x4e>
+  .byte  197,250,126,0                       // vmovq         (%rax),%xmm0
+  .byte  196,226,121,49,200                  // vpmovzxbd     %xmm0,%xmm1
+  .byte  196,227,121,4,192,229               // vpermilps     $0xe5,%xmm0,%xmm0
+  .byte  196,226,121,49,192                  // vpmovzxbd     %xmm0,%xmm0
+  .byte  196,227,117,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,230,50,0,0        // vbroadcastss  0x32e6(%rip),%ymm1        # 6794 <_sk_callback_avx+0x398>
+  .byte  197,252,89,217                      // vmulps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           34ce <_sk_load_a8_avx+0x57>
+  .byte  196,193,249,110,194                 // vmovq         %r10,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  235,160                             // jmp           348b <_sk_load_a8_avx+0x14>
+
+HIDDEN _sk_gather_a8_avx
+.globl _sk_gather_a8_avx
+FUNCTION(_sk_gather_a8_avx)
+_sk_gather_a8_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  197,249,110,80,16                   // vmovd         0x10(%rax),%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,226,105,64,217                  // vpmulld       %xmm1,%xmm2,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,226,105,64,201                  // vpmulld       %xmm1,%xmm2,%xmm1
+  .byte  197,254,91,208                      // vcvttps2dq    %ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  197,241,254,192                     // vpaddd        %xmm0,%xmm1,%xmm0
+  .byte  197,225,254,202                     // vpaddd        %xmm2,%xmm3,%xmm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,32,20,1,0               // vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,12,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,113,32,200,2                // vpinsrb       $0x2,%eax,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,25                      // movzbl        (%r9,%r11,1),%eax
+  .byte  196,227,113,32,200,3                // vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,32,20,1,0               // vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,121,32,192,2                // vpinsrb       $0x2,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,25                      // movzbl        (%r9,%r11,1),%eax
+  .byte  196,226,121,49,201                  // vpmovzxbd     %xmm1,%xmm1
+  .byte  196,227,121,32,192,3                // vpinsrb       $0x3,%eax,%xmm0,%xmm0
+  .byte  196,226,121,49,192                  // vpmovzxbd     %xmm0,%xmm0
+  .byte  196,227,117,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,225,49,0,0        // vbroadcastss  0x31e1(%rip),%ymm1        # 6798 <_sk_callback_avx+0x39c>
+  .byte  197,252,89,217                      // vmulps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_a8_avx
+.globl _sk_store_a8_avx
+FUNCTION(_sk_store_a8_avx)
+_sk_store_a8_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,98,125,24,5,195,49,0,0          // vbroadcastss  0x31c3(%rip),%ymm8        # 679c <_sk_callback_avx+0x3a0>
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,67,125,25,193,1                 // vextractf128  $0x1,%ymm8,%xmm9
+  .byte  196,66,57,43,193                    // vpackusdw     %xmm9,%xmm8,%xmm8
+  .byte  196,65,57,103,192                   // vpackuswb     %xmm8,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3602 <_sk_store_a8_avx+0x37>
+  .byte  196,65,123,17,4,19                  // vmovsd        %xmm8,(%r11,%rdx,1)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            35fe <_sk_store_a8_avx+0x33>
+  .byte  196,66,121,48,192                   // vpmovzxbw     %xmm8,%xmm8
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,66,0,0,0                  // lea           0x42(%rip),%r10        # 3664 <_sk_store_a8_avx+0x99>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,67,121,20,68,19,6,12            // vpextrb       $0xc,%xmm8,0x6(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,5,10            // vpextrb       $0xa,%xmm8,0x5(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,4,8             // vpextrb       $0x8,%xmm8,0x4(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,3,6             // vpextrb       $0x6,%xmm8,0x3(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,2,4             // vpextrb       $0x4,%xmm8,0x2(%r11,%rdx,1)
+  .byte  196,67,121,20,68,19,1,2             // vpextrb       $0x2,%xmm8,0x1(%r11,%rdx,1)
+  .byte  196,67,121,20,4,19,0                // vpextrb       $0x0,%xmm8,(%r11,%rdx,1)
+  .byte  235,154                             // jmp           35fe <_sk_store_a8_avx+0x33>
+  .byte  247,255                             // idiv          %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  239                                 // out           %eax,(%dx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,231                             // jmpq          *%rdi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  223,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,215                             // callq         *%rdi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,207                             // dec           %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,199                             // inc           %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_g8_avx
+.globl _sk_load_g8_avx
+FUNCTION(_sk_load_g8_avx)
+_sk_load_g8_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  72,1,208                            // add           %rdx,%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,67                              // jne           36d3 <_sk_load_g8_avx+0x53>
+  .byte  197,250,126,0                       // vmovq         (%rax),%xmm0
+  .byte  196,226,121,49,200                  // vpmovzxbd     %xmm0,%xmm1
+  .byte  196,227,121,4,192,229               // vpermilps     $0xe5,%xmm0,%xmm0
+  .byte  196,226,121,49,192                  // vpmovzxbd     %xmm0,%xmm0
+  .byte  196,227,117,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,233,48,0,0        // vbroadcastss  0x30e9(%rip),%ymm1        # 67a0 <_sk_callback_avx+0x3a4>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,222,48,0,0        // vbroadcastss  0x30de(%rip),%ymm3        # 67a4 <_sk_callback_avx+0x3a8>
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  83                                  // push          %rbx
+  .byte  49,201                              // xor           %ecx,%ecx
+  .byte  77,137,195                          // mov           %r8,%r11
+  .byte  69,49,210                           // xor           %r10d,%r10d
+  .byte  15,182,24                           // movzbl        (%rax),%ebx
+  .byte  72,255,192                          // inc           %rax
+  .byte  72,211,227                          // shl           %cl,%rbx
+  .byte  73,9,218                            // or            %rbx,%r10
+  .byte  72,131,193,8                        // add           $0x8,%rcx
+  .byte  73,255,203                          // dec           %r11
+  .byte  117,235                             // jne           36dc <_sk_load_g8_avx+0x5c>
+  .byte  196,193,249,110,194                 // vmovq         %r10,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  235,155                             // jmp           3694 <_sk_load_g8_avx+0x14>
+
+HIDDEN _sk_gather_g8_avx
+.globl _sk_gather_g8_avx
+FUNCTION(_sk_gather_g8_avx)
+_sk_gather_g8_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  197,249,110,80,16                   // vmovd         0x10(%rax),%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,226,105,64,217                  // vpmulld       %xmm1,%xmm2,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,226,105,64,201                  // vpmulld       %xmm1,%xmm2,%xmm1
+  .byte  197,254,91,208                      // vcvttps2dq    %ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  197,241,254,192                     // vpaddd        %xmm0,%xmm1,%xmm0
+  .byte  197,225,254,202                     // vpaddd        %xmm2,%xmm3,%xmm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,32,20,1,0               // vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,12,17,1              // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,113,32,200,2                // vpinsrb       $0x2,%eax,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,25                      // movzbl        (%r9,%r11,1),%eax
+  .byte  196,227,113,32,200,3                // vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,32,20,1,0               // vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,32,4,17,1               // vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  196,227,121,32,192,2                // vpinsrb       $0x2,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,182,4,25                      // movzbl        (%r9,%r11,1),%eax
+  .byte  196,226,121,49,201                  // vpmovzxbd     %xmm1,%xmm1
+  .byte  196,227,121,32,192,3                // vpinsrb       $0x3,%eax,%xmm0,%xmm0
+  .byte  196,226,121,49,192                  // vpmovzxbd     %xmm0,%xmm0
+  .byte  196,227,117,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,227,47,0,0        // vbroadcastss  0x2fe3(%rip),%ymm1        # 67a8 <_sk_callback_avx+0x3ac>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,216,47,0,0        // vbroadcastss  0x2fd8(%rip),%ymm3        # 67ac <_sk_callback_avx+0x3b0>
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gather_i8_avx
+.globl _sk_gather_i8_avx
+FUNCTION(_sk_gather_i8_avx)
+_sk_gather_i8_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,193                          // mov           %rax,%r9
+  .byte  77,133,201                          // test          %r9,%r9
+  .byte  116,5                               // je            37ed <_sk_gather_i8_avx+0xf>
+  .byte  76,137,200                          // mov           %r9,%rax
+  .byte  235,2                               // jmp           37ef <_sk_gather_i8_avx+0x11>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  83                                  // push          %rbx
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  197,254,91,209                      // vcvttps2dq    %ymm1,%ymm2
+  .byte  197,249,110,72,16                   // vmovd         0x10(%rax),%xmm1
+  .byte  197,249,112,217,0                   // vpshufd       $0x0,%xmm1,%xmm3
+  .byte  196,226,97,64,202                   // vpmulld       %xmm2,%xmm3,%xmm1
+  .byte  196,227,125,25,210,1                // vextractf128  $0x1,%ymm2,%xmm2
+  .byte  196,226,97,64,210                   // vpmulld       %xmm2,%xmm3,%xmm2
+  .byte  197,254,91,192                      // vcvttps2dq    %ymm0,%ymm0
+  .byte  196,227,125,25,195,1                // vextractf128  $0x1,%ymm0,%xmm3
+  .byte  197,233,254,211                     // vpaddd        %xmm3,%xmm2,%xmm2
+  .byte  196,193,249,126,211                 // vmovq         %xmm2,%r11
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,195,121,32,28,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm3
+  .byte  196,227,249,22,208,1                // vpextrq       $0x1,%xmm2,%rax
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,131,97,32,20,26,1               // vpinsrb       $0x1,(%r10,%r11,1),%xmm3,%xmm2
+  .byte  65,137,195                          // mov           %eax,%r11d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  197,241,254,192                     // vpaddd        %xmm0,%xmm1,%xmm0
+  .byte  196,131,105,32,12,26,2              // vpinsrb       $0x2,(%r10,%r11,1),%xmm2,%xmm1
+  .byte  196,193,249,126,195                 // vmovq         %xmm0,%r11
+  .byte  196,195,113,32,12,2,3               // vpinsrb       $0x3,(%r10,%rax,1),%xmm1,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,195,121,32,20,2,0               // vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm2
+  .byte  196,227,249,22,195,1                // vpextrq       $0x1,%xmm0,%rbx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,131,105,32,4,26,1               // vpinsrb       $0x1,(%r10,%r11,1),%xmm2,%xmm0
+  .byte  137,216                             // mov           %ebx,%eax
+  .byte  196,195,121,32,4,2,2                // vpinsrb       $0x2,(%r10,%rax,1),%xmm0,%xmm0
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  196,226,121,49,201                  // vpmovzxbd     %xmm1,%xmm1
+  .byte  196,195,121,32,4,26,3               // vpinsrb       $0x3,(%r10,%rbx,1),%xmm0,%xmm0
+  .byte  196,226,121,49,192                  // vpmovzxbd     %xmm0,%xmm0
+  .byte  77,139,89,8                         // mov           0x8(%r9),%r11
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  69,137,209                          // mov           %r10d,%r9d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  196,129,121,110,20,139              // vmovd         (%r11,%r9,4),%xmm2
+  .byte  196,227,249,22,195,1                // vpextrq       $0x1,%xmm0,%rbx
+  .byte  196,131,105,34,4,19,1               // vpinsrd       $0x1,(%r11,%r10,1),%xmm2,%xmm0
+  .byte  137,216                             // mov           %ebx,%eax
+  .byte  196,195,121,34,4,131,2              // vpinsrd       $0x2,(%r11,%rax,4),%xmm0,%xmm0
+  .byte  196,225,249,126,200                 // vmovq         %xmm1,%rax
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  196,67,121,34,4,27,3                // vpinsrd       $0x3,(%r11,%rbx,1),%xmm0,%xmm8
+  .byte  137,195                             // mov           %eax,%ebx
+  .byte  196,193,121,110,4,155               // vmovd         (%r11,%rbx,4),%xmm0
+  .byte  196,227,249,22,203,1                // vpextrq       $0x1,%xmm1,%rbx
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  196,195,121,34,4,3,1                // vpinsrd       $0x1,(%r11,%rax,1),%xmm0,%xmm0
+  .byte  137,216                             // mov           %ebx,%eax
+  .byte  196,195,121,34,4,131,2              // vpinsrd       $0x2,(%r11,%rax,4),%xmm0,%xmm0
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  196,195,121,34,28,27,3              // vpinsrd       $0x3,(%r11,%rbx,1),%xmm0,%xmm3
+  .byte  196,227,61,24,195,1                 // vinsertf128   $0x1,%xmm3,%ymm8,%ymm0
+  .byte  197,124,40,21,83,49,0,0             // vmovaps       0x3153(%rip),%ymm10        # 6a60 <_sk_callback_avx+0x664>
+  .byte  196,193,124,84,194                  // vandps        %ymm10,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,13,145,46,0,0         // vbroadcastss  0x2e91(%rip),%ymm9        # 67b0 <_sk_callback_avx+0x3b4>
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  196,193,113,114,208,8               // vpsrld        $0x8,%xmm8,%xmm1
+  .byte  197,233,114,211,8                   // vpsrld        $0x8,%xmm3,%xmm2
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,193,116,84,202                  // vandps        %ymm10,%ymm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,201                  // vmulps        %ymm9,%ymm1,%ymm1
+  .byte  196,193,33,114,208,16               // vpsrld        $0x10,%xmm8,%xmm11
+  .byte  197,233,114,211,16                  // vpsrld        $0x10,%xmm3,%xmm2
+  .byte  196,227,37,24,210,1                 // vinsertf128   $0x1,%xmm2,%ymm11,%ymm2
+  .byte  196,193,108,84,210                  // vandps        %ymm10,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,209                  // vmulps        %ymm9,%ymm2,%ymm2
+  .byte  196,193,57,114,208,24               // vpsrld        $0x18,%xmm8,%xmm8
+  .byte  197,225,114,211,24                  // vpsrld        $0x18,%xmm3,%xmm3
+  .byte  196,227,61,24,219,1                 // vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,217                  // vmulps        %ymm9,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_565_avx
+.globl _sk_load_565_avx
+FUNCTION(_sk_load_565_avx)
+_sk_load_565_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,128,0,0,0                    // jne           3a0f <_sk_load_565_avx+0x8e>
+  .byte  196,193,122,111,4,83                // vmovdqu       (%r11,%rdx,2),%xmm0
+  .byte  197,241,239,201                     // vpxor         %xmm1,%xmm1,%xmm1
+  .byte  197,249,105,201                     // vpunpckhwd    %xmm1,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,209,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm2
+  .byte  196,226,125,24,5,3,46,0,0           // vbroadcastss  0x2e03(%rip),%ymm0        # 67b4 <_sk_callback_avx+0x3b8>
+  .byte  197,236,84,192                      // vandps        %ymm0,%ymm2,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,246,45,0,0        // vbroadcastss  0x2df6(%rip),%ymm1        # 67b8 <_sk_callback_avx+0x3bc>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,24,13,237,45,0,0        // vbroadcastss  0x2ded(%rip),%ymm1        # 67bc <_sk_callback_avx+0x3c0>
+  .byte  197,236,84,201                      // vandps        %ymm1,%ymm2,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,29,224,45,0,0        // vbroadcastss  0x2de0(%rip),%ymm3        # 67c0 <_sk_callback_avx+0x3c4>
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  196,226,125,24,29,215,45,0,0        // vbroadcastss  0x2dd7(%rip),%ymm3        # 67c4 <_sk_callback_avx+0x3c8>
+  .byte  197,236,84,211                      // vandps        %ymm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,226,125,24,29,202,45,0,0        // vbroadcastss  0x2dca(%rip),%ymm3        # 67c8 <_sk_callback_avx+0x3cc>
+  .byte  197,236,89,211                      // vmulps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,191,45,0,0        // vbroadcastss  0x2dbf(%rip),%ymm3        # 67cc <_sk_callback_avx+0x3d0>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,110,255,255,255              // ja            3995 <_sk_load_565_avx+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,74,0,0,0                  // lea           0x4a(%rip),%r10        # 3a7c <_sk_load_565_avx+0xfb>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,12,6          // vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,10,5          // vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,8,4           // vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,6,3           // vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,4,2           // vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,2,1           // vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,4,83,0              // vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  233,26,255,255,255                  // jmpq          3995 <_sk_load_565_avx+0x14>
+  .byte  144                                 // nop
+  .byte  243,255                             // repz          (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  235,255                             // jmp           3a81 <_sk_load_565_avx+0x100>
+  .byte  255                                 // (bad)
+  .byte  255,227                             // jmpq          *%rbx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  219,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,211                             // callq         *%rbx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,203                             // dec           %ebx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  191                                 // .byte         0xbf
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_gather_565_avx
+.globl _sk_gather_565_avx
+FUNCTION(_sk_gather_565_avx)
+_sk_gather_565_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  197,249,110,80,16                   // vmovd         0x10(%rax),%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,226,105,64,217                  // vpmulld       %xmm1,%xmm2,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,226,105,64,201                  // vpmulld       %xmm1,%xmm2,%xmm1
+  .byte  197,254,91,208                      // vcvttps2dq    %ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  197,241,254,192                     // vpaddd        %xmm0,%xmm1,%xmm0
+  .byte  197,225,254,202                     // vpaddd        %xmm2,%xmm3,%xmm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,110,208                     // vmovd         %eax,%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,233,196,200,1                   // vpinsrw       $0x1,%eax,%xmm2,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,2                   // vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,241,196,200,3                   // vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,4                   // vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,241,196,192,5                   // vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,196,192,6                   // vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,249,196,192,7                   // vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  .byte  197,241,239,201                     // vpxor         %xmm1,%xmm1,%xmm1
+  .byte  197,249,105,201                     // vpunpckhwd    %xmm1,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,209,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm2
+  .byte  196,226,125,24,5,102,44,0,0         // vbroadcastss  0x2c66(%rip),%ymm0        # 67d0 <_sk_callback_avx+0x3d4>
+  .byte  197,236,84,192                      // vandps        %ymm0,%ymm2,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,89,44,0,0         // vbroadcastss  0x2c59(%rip),%ymm1        # 67d4 <_sk_callback_avx+0x3d8>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,24,13,80,44,0,0         // vbroadcastss  0x2c50(%rip),%ymm1        # 67d8 <_sk_callback_avx+0x3dc>
+  .byte  197,236,84,201                      // vandps        %ymm1,%ymm2,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,29,67,44,0,0         // vbroadcastss  0x2c43(%rip),%ymm3        # 67dc <_sk_callback_avx+0x3e0>
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  196,226,125,24,29,58,44,0,0         // vbroadcastss  0x2c3a(%rip),%ymm3        # 67e0 <_sk_callback_avx+0x3e4>
+  .byte  197,236,84,211                      // vandps        %ymm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,226,125,24,29,45,44,0,0         // vbroadcastss  0x2c2d(%rip),%ymm3        # 67e4 <_sk_callback_avx+0x3e8>
+  .byte  197,236,89,211                      // vmulps        %ymm3,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,34,44,0,0         // vbroadcastss  0x2c22(%rip),%ymm3        # 67e8 <_sk_callback_avx+0x3ec>
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_565_avx
+.globl _sk_store_565_avx
+FUNCTION(_sk_store_565_avx)
+_sk_store_565_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,98,125,24,5,22,44,0,0           // vbroadcastss  0x2c16(%rip),%ymm8        # 67ec <_sk_callback_avx+0x3f0>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,193,41,114,241,11               // vpslld        $0xb,%xmm9,%xmm10
+  .byte  196,67,125,25,201,1                 // vextractf128  $0x1,%ymm9,%xmm9
+  .byte  196,193,49,114,241,11               // vpslld        $0xb,%xmm9,%xmm9
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,98,125,24,21,239,43,0,0         // vbroadcastss  0x2bef(%rip),%ymm10        # 67f0 <_sk_callback_avx+0x3f4>
+  .byte  196,65,116,89,210                   // vmulps        %ymm10,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,33,114,242,5                // vpslld        $0x5,%xmm10,%xmm11
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,242,5                // vpslld        $0x5,%xmm10,%xmm10
+  .byte  196,67,37,24,210,1                  // vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  .byte  196,65,45,86,201                    // vorpd         %ymm9,%ymm10,%ymm9
+  .byte  196,65,108,89,192                   // vmulps        %ymm8,%ymm2,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,65,53,86,192                    // vorpd         %ymm8,%ymm9,%ymm8
+  .byte  196,67,125,25,193,1                 // vextractf128  $0x1,%ymm8,%xmm9
+  .byte  196,66,57,43,193                    // vpackusdw     %xmm9,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3c51 <_sk_store_565_avx+0x89>
+  .byte  196,65,122,127,4,83                 // vmovdqu       %xmm8,(%r11,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            3c4d <_sk_store_565_avx+0x85>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,68,0,0,0                  // lea           0x44(%rip),%r10        # 3cb0 <_sk_store_565_avx+0xe8>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,67,121,21,68,83,12,6            // vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,10,5            // vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,8,4             // vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,6,3             // vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,4,2             // vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,2,1             // vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  .byte  196,67,121,21,4,83,0                // vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  .byte  235,159                             // jmp           3c4d <_sk_store_565_avx+0x85>
+  .byte  102,144                             // xchg          %ax,%ax
+  .byte  245                                 // cmc
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  237                                 // in            (%dx),%eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,229                             // jmpq          *%rbp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  221,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,213                             // callq         *%rbp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,205                             // dec           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,197                             // inc           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_4444_avx
+.globl _sk_load_4444_avx
+FUNCTION(_sk_load_4444_avx)
+_sk_load_4444_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,152,0,0,0                    // jne           3d72 <_sk_load_4444_avx+0xa6>
+  .byte  196,193,122,111,4,83                // vmovdqu       (%r11,%rdx,2),%xmm0
+  .byte  197,241,239,201                     // vpxor         %xmm1,%xmm1,%xmm1
+  .byte  197,249,105,201                     // vpunpckhwd    %xmm1,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,217,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm3
+  .byte  196,226,125,24,5,248,42,0,0         // vbroadcastss  0x2af8(%rip),%ymm0        # 67f4 <_sk_callback_avx+0x3f8>
+  .byte  197,228,84,192                      // vandps        %ymm0,%ymm3,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,235,42,0,0        // vbroadcastss  0x2aeb(%rip),%ymm1        # 67f8 <_sk_callback_avx+0x3fc>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,24,13,226,42,0,0        // vbroadcastss  0x2ae2(%rip),%ymm1        # 67fc <_sk_callback_avx+0x400>
+  .byte  197,228,84,201                      // vandps        %ymm1,%ymm3,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,21,213,42,0,0        // vbroadcastss  0x2ad5(%rip),%ymm2        # 6800 <_sk_callback_avx+0x404>
+  .byte  197,244,89,202                      // vmulps        %ymm2,%ymm1,%ymm1
+  .byte  196,226,125,24,21,204,42,0,0        // vbroadcastss  0x2acc(%rip),%ymm2        # 6804 <_sk_callback_avx+0x408>
+  .byte  197,228,84,210                      // vandps        %ymm2,%ymm3,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,98,125,24,5,191,42,0,0          // vbroadcastss  0x2abf(%rip),%ymm8        # 6808 <_sk_callback_avx+0x40c>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,24,5,181,42,0,0          // vbroadcastss  0x2ab5(%rip),%ymm8        # 680c <_sk_callback_avx+0x410>
+  .byte  196,193,100,84,216                  // vandps        %ymm8,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,98,125,24,5,167,42,0,0          // vbroadcastss  0x2aa7(%rip),%ymm8        # 6810 <_sk_callback_avx+0x414>
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,86,255,255,255               // ja            3ce0 <_sk_load_4444_avx+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,75,0,0,0                  // lea           0x4b(%rip),%r10        # 3de0 <_sk_load_4444_avx+0x114>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,249,239,192                     // vpxor         %xmm0,%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,12,6          // vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,10,5          // vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,8,4           // vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,6,3           // vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,4,2           // vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,68,83,2,1           // vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  196,193,121,196,4,83,0              // vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  .byte  233,2,255,255,255                   // jmpq          3ce0 <_sk_load_4444_avx+0x14>
+  .byte  102,144                             // xchg          %ax,%ax
+  .byte  242,255                             // repnz         (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  234                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,226                             // jmpq          *%rdx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  218,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,210                             // callq         *%rdx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,202                             // dec           %edx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  190                                 // .byte         0xbe
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_gather_4444_avx
+.globl _sk_gather_4444_avx
+FUNCTION(_sk_gather_4444_avx)
+_sk_gather_4444_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  197,249,110,80,16                   // vmovd         0x10(%rax),%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,226,105,64,217                  // vpmulld       %xmm1,%xmm2,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,226,105,64,201                  // vpmulld       %xmm1,%xmm2,%xmm1
+  .byte  197,254,91,208                      // vcvttps2dq    %ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  197,241,254,192                     // vpaddd        %xmm0,%xmm1,%xmm0
+  .byte  197,225,254,202                     // vpaddd        %xmm2,%xmm3,%xmm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,110,208                     // vmovd         %eax,%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,233,196,200,1                   // vpinsrw       $0x1,%eax,%xmm2,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,2                   // vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  .byte  196,193,249,126,194                 // vmovq         %xmm0,%r10
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,241,196,200,3                   // vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,241,196,200,4                   // vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  .byte  196,195,249,22,195,1                // vpextrq       $0x1,%xmm0,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  67,15,183,4,81                      // movzwl        (%r9,%r10,2),%eax
+  .byte  197,241,196,192,5                   // vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  197,249,196,192,6                   // vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  67,15,183,4,89                      // movzwl        (%r9,%r11,2),%eax
+  .byte  197,249,196,192,7                   // vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  .byte  197,241,239,201                     // vpxor         %xmm1,%xmm1,%xmm1
+  .byte  197,249,105,201                     // vpunpckhwd    %xmm1,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,217,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm3
+  .byte  196,226,125,24,5,70,41,0,0          // vbroadcastss  0x2946(%rip),%ymm0        # 6814 <_sk_callback_avx+0x418>
+  .byte  197,228,84,192                      // vandps        %ymm0,%ymm3,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,226,125,24,13,57,41,0,0         // vbroadcastss  0x2939(%rip),%ymm1        # 6818 <_sk_callback_avx+0x41c>
+  .byte  197,252,89,193                      // vmulps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,24,13,48,41,0,0         // vbroadcastss  0x2930(%rip),%ymm1        # 681c <_sk_callback_avx+0x420>
+  .byte  197,228,84,201                      // vandps        %ymm1,%ymm3,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,226,125,24,21,35,41,0,0         // vbroadcastss  0x2923(%rip),%ymm2        # 6820 <_sk_callback_avx+0x424>
+  .byte  197,244,89,202                      // vmulps        %ymm2,%ymm1,%ymm1
+  .byte  196,226,125,24,21,26,41,0,0         // vbroadcastss  0x291a(%rip),%ymm2        # 6824 <_sk_callback_avx+0x428>
+  .byte  197,228,84,210                      // vandps        %ymm2,%ymm3,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,98,125,24,5,13,41,0,0           // vbroadcastss  0x290d(%rip),%ymm8        # 6828 <_sk_callback_avx+0x42c>
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  196,98,125,24,5,3,41,0,0            // vbroadcastss  0x2903(%rip),%ymm8        # 682c <_sk_callback_avx+0x430>
+  .byte  196,193,100,84,216                  // vandps        %ymm8,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,98,125,24,5,245,40,0,0          // vbroadcastss  0x28f5(%rip),%ymm8        # 6830 <_sk_callback_avx+0x434>
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_4444_avx
+.globl _sk_store_4444_avx
+FUNCTION(_sk_store_4444_avx)
+_sk_store_4444_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  196,98,125,24,5,226,40,0,0          // vbroadcastss  0x28e2(%rip),%ymm8        # 6834 <_sk_callback_avx+0x438>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,193,41,114,241,12               // vpslld        $0xc,%xmm9,%xmm10
+  .byte  196,67,125,25,201,1                 // vextractf128  $0x1,%ymm9,%xmm9
+  .byte  196,193,49,114,241,12               // vpslld        $0xc,%xmm9,%xmm9
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,33,114,242,8                // vpslld        $0x8,%xmm10,%xmm11
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,242,8                // vpslld        $0x8,%xmm10,%xmm10
+  .byte  196,67,37,24,210,1                  // vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  .byte  196,65,45,86,201                    // vorpd         %ymm9,%ymm10,%ymm9
+  .byte  196,65,108,89,208                   // vmulps        %ymm8,%ymm2,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,33,114,242,4                // vpslld        $0x4,%xmm10,%xmm11
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,242,4                // vpslld        $0x4,%xmm10,%xmm10
+  .byte  196,67,37,24,210,1                  // vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,65,45,86,192                    // vorpd         %ymm8,%ymm10,%ymm8
+  .byte  196,65,53,86,192                    // vorpd         %ymm8,%ymm9,%ymm8
+  .byte  196,67,125,25,193,1                 // vextractf128  $0x1,%ymm8,%xmm9
+  .byte  196,66,57,43,193                    // vpackusdw     %xmm9,%xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3feb <_sk_store_4444_avx+0xa7>
+  .byte  196,65,122,127,4,83                 // vmovdqu       %xmm8,(%r11,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            3fe7 <_sk_store_4444_avx+0xa3>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,66,0,0,0                  // lea           0x42(%rip),%r10        # 4048 <_sk_store_4444_avx+0x104>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,67,121,21,68,83,12,6            // vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,10,5            // vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,8,4             // vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,6,3             // vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,4,2             // vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  .byte  196,67,121,21,68,83,2,1             // vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  .byte  196,67,121,21,4,83,0                // vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  .byte  235,159                             // jmp           3fe7 <_sk_store_4444_avx+0xa3>
+  .byte  247,255                             // idiv          %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  239                                 // out           %eax,(%dx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,231                             // jmpq          *%rdi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  223,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,215                             // callq         *%rdi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,207                             // dec           %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,199                             // inc           %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_8888_avx
+.globl _sk_load_8888_avx
+FUNCTION(_sk_load_8888_avx)
+_sk_load_8888_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,137,0,0,0                    // jne           4106 <_sk_load_8888_avx+0xa2>
+  .byte  196,193,124,16,26                   // vmovups       (%r10),%ymm3
+  .byte  197,124,40,21,246,41,0,0            // vmovaps       0x29f6(%rip),%ymm10        # 6a80 <_sk_callback_avx+0x684>
+  .byte  196,193,100,84,194                  // vandps        %ymm10,%ymm3,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,5,156,39,0,0          // vbroadcastss  0x279c(%rip),%ymm8        # 6838 <_sk_callback_avx+0x43c>
+  .byte  196,193,124,89,192                  // vmulps        %ymm8,%ymm0,%ymm0
+  .byte  197,241,114,211,8                   // vpsrld        $0x8,%xmm3,%xmm1
+  .byte  196,195,125,25,217,1                // vextractf128  $0x1,%ymm3,%xmm9
+  .byte  196,193,105,114,209,8               // vpsrld        $0x8,%xmm9,%xmm2
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,193,116,84,202                  // vandps        %ymm10,%ymm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,200                  // vmulps        %ymm8,%ymm1,%ymm1
+  .byte  197,161,114,211,16                  // vpsrld        $0x10,%xmm3,%xmm11
+  .byte  196,193,105,114,209,16              // vpsrld        $0x10,%xmm9,%xmm2
+  .byte  196,227,37,24,210,1                 // vinsertf128   $0x1,%xmm2,%ymm11,%ymm2
+  .byte  196,193,108,84,210                  // vandps        %ymm10,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,208                  // vmulps        %ymm8,%ymm2,%ymm2
+  .byte  197,169,114,211,24                  // vpsrld        $0x18,%xmm3,%xmm10
+  .byte  196,193,97,114,209,24               // vpsrld        $0x18,%xmm9,%xmm3
+  .byte  196,227,45,24,219,1                 // vinsertf128   $0x1,%xmm3,%ymm10,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,216                  // vmulps        %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,225,249,110,192                 // vmovq         %rax,%xmm0
+  .byte  196,226,121,48,192                  // vpmovzxbw     %xmm0,%xmm0
+  .byte  196,226,121,0,13,162,40,0,0         // vpshufb       0x28a2(%rip),%xmm0,%xmm1        # 69d0 <_sk_callback_avx+0x5d4>
+  .byte  196,226,121,33,201                  // vpmovsxbd     %xmm1,%xmm1
+  .byte  196,226,121,0,5,164,40,0,0          // vpshufb       0x28a4(%rip),%xmm0,%xmm0        # 69e0 <_sk_callback_avx+0x5e4>
+  .byte  196,226,121,33,192                  // vpmovsxbd     %xmm0,%xmm0
+  .byte  196,227,117,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  .byte  196,194,125,44,26                   // vmaskmovps    (%r10),%ymm0,%ymm3
+  .byte  233,49,255,255,255                  // jmpq          4082 <_sk_load_8888_avx+0x1e>
+
+HIDDEN _sk_gather_8888_avx
+.globl _sk_gather_8888_avx
+FUNCTION(_sk_gather_8888_avx)
+_sk_gather_8888_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  197,249,110,80,16                   // vmovd         0x10(%rax),%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,226,105,64,217                  // vpmulld       %xmm1,%xmm2,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,226,105,64,201                  // vpmulld       %xmm1,%xmm2,%xmm1
+  .byte  197,254,91,208                      // vcvttps2dq    %ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  197,241,254,192                     // vpaddd        %xmm0,%xmm1,%xmm0
+  .byte  197,225,254,202                     // vpaddd        %xmm2,%xmm3,%xmm1
+  .byte  196,193,249,126,202                 // vmovq         %xmm1,%r10
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,193,121,110,20,129              // vmovd         (%r9,%rax,4),%xmm2
+  .byte  196,195,249,22,203,1                // vpextrq       $0x1,%xmm1,%r11
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,105,34,12,145,1             // vpinsrd       $0x1,(%r9,%r10,4),%xmm2,%xmm1
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,195,113,34,12,129,2             // vpinsrd       $0x2,(%r9,%rax,4),%xmm1,%xmm1
+  .byte  196,225,249,126,192                 // vmovq         %xmm0,%rax
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,3,113,34,4,153,3                // vpinsrd       $0x3,(%r9,%r11,4),%xmm1,%xmm8
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,129,121,110,12,145              // vmovd         (%r9,%r10,4),%xmm1
+  .byte  196,195,249,22,194,1                // vpextrq       $0x1,%xmm0,%r10
+  .byte  196,195,113,34,4,129,1              // vpinsrd       $0x1,(%r9,%rax,4),%xmm1,%xmm0
+  .byte  68,137,208                          // mov           %r10d,%eax
+  .byte  196,195,121,34,4,129,2              // vpinsrd       $0x2,(%r9,%rax,4),%xmm0,%xmm0
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  196,131,121,34,28,145,3             // vpinsrd       $0x3,(%r9,%r10,4),%xmm0,%xmm3
+  .byte  196,227,61,24,195,1                 // vinsertf128   $0x1,%xmm3,%ymm8,%ymm0
+  .byte  197,124,40,21,164,40,0,0            // vmovaps       0x28a4(%rip),%ymm10        # 6aa0 <_sk_callback_avx+0x6a4>
+  .byte  196,193,124,84,194                  // vandps        %ymm10,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,13,46,38,0,0          // vbroadcastss  0x262e(%rip),%ymm9        # 683c <_sk_callback_avx+0x440>
+  .byte  196,193,124,89,193                  // vmulps        %ymm9,%ymm0,%ymm0
+  .byte  196,193,113,114,208,8               // vpsrld        $0x8,%xmm8,%xmm1
+  .byte  197,233,114,211,8                   // vpsrld        $0x8,%xmm3,%xmm2
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,193,116,84,202                  // vandps        %ymm10,%ymm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,201                  // vmulps        %ymm9,%ymm1,%ymm1
+  .byte  196,193,33,114,208,16               // vpsrld        $0x10,%xmm8,%xmm11
+  .byte  197,233,114,211,16                  // vpsrld        $0x10,%xmm3,%xmm2
+  .byte  196,227,37,24,210,1                 // vinsertf128   $0x1,%xmm2,%ymm11,%ymm2
+  .byte  196,193,108,84,210                  // vandps        %ymm10,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,209                  // vmulps        %ymm9,%ymm2,%ymm2
+  .byte  196,193,57,114,208,24               // vpsrld        $0x18,%xmm8,%xmm8
+  .byte  197,225,114,211,24                  // vpsrld        $0x18,%xmm3,%xmm3
+  .byte  196,227,61,24,219,1                 // vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,217                  // vmulps        %ymm9,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_8888_avx
+.globl _sk_store_8888_avx
+FUNCTION(_sk_store_8888_avx)
+_sk_store_8888_avx:
+  .byte  73,137,201                          // mov           %rcx,%r9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,3,16                             // add           (%rax),%r10
+  .byte  196,98,125,24,5,184,37,0,0          // vbroadcastss  0x25b8(%rip),%ymm8        # 6840 <_sk_callback_avx+0x444>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,33,114,242,8                // vpslld        $0x8,%xmm10,%xmm11
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,242,8                // vpslld        $0x8,%xmm10,%xmm10
+  .byte  196,67,37,24,210,1                  // vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  .byte  196,65,45,86,201                    // vorpd         %ymm9,%ymm10,%ymm9
+  .byte  196,65,108,89,208                   // vmulps        %ymm8,%ymm2,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,193,33,114,242,16               // vpslld        $0x10,%xmm10,%xmm11
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,242,16               // vpslld        $0x10,%xmm10,%xmm10
+  .byte  196,67,37,24,210,1                  // vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,193,33,114,240,24               // vpslld        $0x18,%xmm8,%xmm11
+  .byte  196,67,125,25,192,1                 // vextractf128  $0x1,%ymm8,%xmm8
+  .byte  196,193,57,114,240,24               // vpslld        $0x18,%xmm8,%xmm8
+  .byte  196,67,37,24,192,1                  // vinsertf128   $0x1,%xmm8,%ymm11,%ymm8
+  .byte  196,65,45,86,192                    // vorpd         %ymm8,%ymm10,%ymm8
+  .byte  196,65,53,86,192                    // vorpd         %ymm8,%ymm9,%ymm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,12                              // jne           4318 <_sk_store_8888_avx+0xa9>
+  .byte  196,65,124,17,2                     // vmovups       %ymm8,(%r10)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,201                          // mov           %r9,%rcx
+  .byte  255,224                             // jmpq          *%rax
+  .byte  185,8,0,0,0                         // mov           $0x8,%ecx
+  .byte  68,41,193                           // sub           %r8d,%ecx
+  .byte  192,225,3                           // shl           $0x3,%cl
+  .byte  72,199,192,255,255,255,255          // mov           $0xffffffffffffffff,%rax
+  .byte  72,211,232                          // shr           %cl,%rax
+  .byte  196,97,249,110,200                  // vmovq         %rax,%xmm9
+  .byte  196,66,121,48,201                   // vpmovzxbw     %xmm9,%xmm9
+  .byte  196,98,49,0,21,176,38,0,0           // vpshufb       0x26b0(%rip),%xmm9,%xmm10        # 69f0 <_sk_callback_avx+0x5f4>
+  .byte  196,66,121,33,210                   // vpmovsxbd     %xmm10,%xmm10
+  .byte  196,98,49,0,13,178,38,0,0           // vpshufb       0x26b2(%rip),%xmm9,%xmm9        # 6a00 <_sk_callback_avx+0x604>
+  .byte  196,66,121,33,201                   // vpmovsxbd     %xmm9,%xmm9
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,66,53,46,2                      // vmaskmovps    %ymm8,%ymm9,(%r10)
+  .byte  235,177                             // jmp           4311 <_sk_store_8888_avx+0xa2>
+
+HIDDEN _sk_load_f16_avx
+.globl _sk_load_f16_avx
+FUNCTION(_sk_load_f16_avx)
+_sk_load_f16_avx:
+  .byte  72,131,236,24                       // sub           $0x18,%rsp
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  197,252,17,124,36,224               // vmovups       %ymm7,-0x20(%rsp)
+  .byte  197,252,17,116,36,192               // vmovups       %ymm6,-0x40(%rsp)
+  .byte  197,252,17,108,36,160               // vmovups       %ymm5,-0x60(%rsp)
+  .byte  197,254,127,100,36,128              // vmovdqu       %ymm4,-0x80(%rsp)
+  .byte  15,133,141,2,0,0                    // jne           4617 <_sk_load_f16_avx+0x2b7>
+  .byte  197,121,16,4,208                    // vmovupd       (%rax,%rdx,8),%xmm8
+  .byte  197,249,16,84,208,16                // vmovupd       0x10(%rax,%rdx,8),%xmm2
+  .byte  197,249,16,76,208,32                // vmovupd       0x20(%rax,%rdx,8),%xmm1
+  .byte  197,122,111,76,208,48               // vmovdqu       0x30(%rax,%rdx,8),%xmm9
+  .byte  197,185,97,194                      // vpunpcklwd    %xmm2,%xmm8,%xmm0
+  .byte  197,185,105,210                     // vpunpckhwd    %xmm2,%xmm8,%xmm2
+  .byte  196,193,113,97,217                  // vpunpcklwd    %xmm9,%xmm1,%xmm3
+  .byte  196,193,113,105,201                 // vpunpckhwd    %xmm9,%xmm1,%xmm1
+  .byte  197,121,97,250                      // vpunpcklwd    %xmm2,%xmm0,%xmm15
+  .byte  197,121,105,194                     // vpunpckhwd    %xmm2,%xmm0,%xmm8
+  .byte  197,225,97,209                      // vpunpcklwd    %xmm1,%xmm3,%xmm2
+  .byte  197,97,105,201                      // vpunpckhwd    %xmm1,%xmm3,%xmm9
+  .byte  197,129,108,194                     // vpunpcklqdq   %xmm2,%xmm15,%xmm0
+  .byte  197,241,239,201                     // vpxor         %xmm1,%xmm1,%xmm1
+  .byte  197,249,105,201                     // vpunpckhwd    %xmm1,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  196,98,125,24,37,97,36,0,0          // vbroadcastss  0x2461(%rip),%ymm12        # 6844 <_sk_callback_avx+0x448>
+  .byte  196,193,124,84,204                  // vandps        %ymm12,%ymm0,%ymm1
+  .byte  197,252,87,193                      // vxorps        %ymm1,%ymm0,%ymm0
+  .byte  196,195,125,25,198,1                // vextractf128  $0x1,%ymm0,%xmm14
+  .byte  196,98,121,24,29,77,36,0,0          // vbroadcastss  0x244d(%rip),%xmm11        # 6848 <_sk_callback_avx+0x44c>
+  .byte  196,193,8,87,219                    // vxorps        %xmm11,%xmm14,%xmm3
+  .byte  196,98,121,24,45,67,36,0,0          // vbroadcastss  0x2443(%rip),%xmm13        # 684c <_sk_callback_avx+0x450>
+  .byte  197,145,102,219                     // vpcmpgtd      %xmm3,%xmm13,%xmm3
+  .byte  196,65,120,87,211                   // vxorps        %xmm11,%xmm0,%xmm10
+  .byte  196,65,17,102,210                   // vpcmpgtd      %xmm10,%xmm13,%xmm10
+  .byte  196,99,45,24,211,1                  // vinsertf128   $0x1,%xmm3,%ymm10,%ymm10
+  .byte  197,225,114,241,16                  // vpslld        $0x10,%xmm1,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  197,241,114,241,16                  // vpslld        $0x10,%xmm1,%xmm1
+  .byte  196,227,101,24,201,1                // vinsertf128   $0x1,%xmm1,%ymm3,%ymm1
+  .byte  197,249,114,240,13                  // vpslld        $0xd,%xmm0,%xmm0
+  .byte  196,193,97,114,246,13               // vpslld        $0xd,%xmm14,%xmm3
+  .byte  196,227,125,24,195,1                // vinsertf128   $0x1,%xmm3,%ymm0,%ymm0
+  .byte  197,252,86,193                      // vorps         %ymm1,%ymm0,%ymm0
+  .byte  196,227,125,25,193,1                // vextractf128  $0x1,%ymm0,%xmm1
+  .byte  196,226,121,24,29,249,35,0,0        // vbroadcastss  0x23f9(%rip),%xmm3        # 6850 <_sk_callback_avx+0x454>
+  .byte  197,241,254,203                     // vpaddd        %xmm3,%xmm1,%xmm1
+  .byte  197,249,254,195                     // vpaddd        %xmm3,%xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  196,65,12,87,246                    // vxorps        %ymm14,%ymm14,%ymm14
+  .byte  196,195,125,74,198,160              // vblendvps     %ymm10,%ymm14,%ymm0,%ymm0
+  .byte  197,129,109,202                     // vpunpckhqdq   %xmm2,%xmm15,%xmm1
+  .byte  197,217,239,228                     // vpxor         %xmm4,%xmm4,%xmm4
+  .byte  197,241,105,212                     // vpunpckhwd    %xmm4,%xmm1,%xmm2
+  .byte  196,226,121,51,201                  // vpmovzxwd     %xmm1,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,193,116,84,212                  // vandps        %ymm12,%ymm1,%ymm2
+  .byte  197,244,87,202                      // vxorps        %ymm2,%ymm1,%ymm1
+  .byte  196,195,125,25,202,1                // vextractf128  $0x1,%ymm1,%xmm10
+  .byte  196,193,40,87,251                   // vxorps        %xmm11,%xmm10,%xmm7
+  .byte  197,145,102,255                     // vpcmpgtd      %xmm7,%xmm13,%xmm7
+  .byte  196,193,112,87,243                  // vxorps        %xmm11,%xmm1,%xmm6
+  .byte  197,145,102,246                     // vpcmpgtd      %xmm6,%xmm13,%xmm6
+  .byte  196,227,77,24,247,1                 // vinsertf128   $0x1,%xmm7,%ymm6,%ymm6
+  .byte  197,193,114,242,16                  // vpslld        $0x10,%xmm2,%xmm7
+  .byte  196,227,125,25,210,1                // vextractf128  $0x1,%ymm2,%xmm2
+  .byte  197,233,114,242,16                  // vpslld        $0x10,%xmm2,%xmm2
+  .byte  196,227,69,24,210,1                 // vinsertf128   $0x1,%xmm2,%ymm7,%ymm2
+  .byte  197,241,114,241,13                  // vpslld        $0xd,%xmm1,%xmm1
+  .byte  196,193,65,114,242,13               // vpslld        $0xd,%xmm10,%xmm7
+  .byte  196,227,117,24,207,1                // vinsertf128   $0x1,%xmm7,%ymm1,%ymm1
+  .byte  197,244,86,202                      // vorps         %ymm2,%ymm1,%ymm1
+  .byte  196,227,125,25,202,1                // vextractf128  $0x1,%ymm1,%xmm2
+  .byte  197,233,254,211                     // vpaddd        %xmm3,%xmm2,%xmm2
+  .byte  197,241,254,203                     // vpaddd        %xmm3,%xmm1,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,195,117,74,206,96               // vblendvps     %ymm6,%ymm14,%ymm1,%ymm1
+  .byte  196,193,57,108,209                  // vpunpcklqdq   %xmm9,%xmm8,%xmm2
+  .byte  197,233,105,244                     // vpunpckhwd    %xmm4,%xmm2,%xmm6
+  .byte  196,65,41,239,210                   // vpxor         %xmm10,%xmm10,%xmm10
+  .byte  196,226,121,51,210                  // vpmovzxwd     %xmm2,%xmm2
+  .byte  196,227,109,24,214,1                // vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  .byte  196,193,108,84,244                  // vandps        %ymm12,%ymm2,%ymm6
+  .byte  197,236,87,214                      // vxorps        %ymm6,%ymm2,%ymm2
+  .byte  196,227,125,25,215,1                // vextractf128  $0x1,%ymm2,%xmm7
+  .byte  196,193,64,87,235                   // vxorps        %xmm11,%xmm7,%xmm5
+  .byte  197,145,102,237                     // vpcmpgtd      %xmm5,%xmm13,%xmm5
+  .byte  196,193,104,87,227                  // vxorps        %xmm11,%xmm2,%xmm4
+  .byte  197,145,102,228                     // vpcmpgtd      %xmm4,%xmm13,%xmm4
+  .byte  196,227,93,24,229,1                 // vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  .byte  197,209,114,246,16                  // vpslld        $0x10,%xmm6,%xmm5
+  .byte  196,227,125,25,246,1                // vextractf128  $0x1,%ymm6,%xmm6
+  .byte  197,201,114,246,16                  // vpslld        $0x10,%xmm6,%xmm6
+  .byte  196,227,85,24,238,1                 // vinsertf128   $0x1,%xmm6,%ymm5,%ymm5
+  .byte  197,233,114,242,13                  // vpslld        $0xd,%xmm2,%xmm2
+  .byte  197,201,114,247,13                  // vpslld        $0xd,%xmm7,%xmm6
+  .byte  196,227,109,24,214,1                // vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  .byte  197,236,86,213                      // vorps         %ymm5,%ymm2,%ymm2
+  .byte  196,227,125,25,213,1                // vextractf128  $0x1,%ymm2,%xmm5
+  .byte  197,209,254,235                     // vpaddd        %xmm3,%xmm5,%xmm5
+  .byte  197,233,254,211                     // vpaddd        %xmm3,%xmm2,%xmm2
+  .byte  196,227,109,24,213,1                // vinsertf128   $0x1,%xmm5,%ymm2,%ymm2
+  .byte  196,195,109,74,214,64               // vblendvps     %ymm4,%ymm14,%ymm2,%ymm2
+  .byte  196,193,57,109,225                  // vpunpckhqdq   %xmm9,%xmm8,%xmm4
+  .byte  196,193,89,105,234                  // vpunpckhwd    %xmm10,%xmm4,%xmm5
+  .byte  196,226,121,51,228                  // vpmovzxwd     %xmm4,%xmm4
+  .byte  196,227,93,24,229,1                 // vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  .byte  196,193,92,84,236                   // vandps        %ymm12,%ymm4,%ymm5
+  .byte  197,220,87,229                      // vxorps        %ymm5,%ymm4,%ymm4
+  .byte  196,227,125,25,230,1                // vextractf128  $0x1,%ymm4,%xmm6
+  .byte  196,193,72,87,251                   // vxorps        %xmm11,%xmm6,%xmm7
+  .byte  197,17,102,199                      // vpcmpgtd      %xmm7,%xmm13,%xmm8
+  .byte  196,193,88,87,251                   // vxorps        %xmm11,%xmm4,%xmm7
+  .byte  197,145,102,255                     // vpcmpgtd      %xmm7,%xmm13,%xmm7
+  .byte  196,195,69,24,248,1                 // vinsertf128   $0x1,%xmm8,%ymm7,%ymm7
+  .byte  197,185,114,245,16                  // vpslld        $0x10,%xmm5,%xmm8
+  .byte  196,227,125,25,237,1                // vextractf128  $0x1,%ymm5,%xmm5
+  .byte  197,209,114,245,16                  // vpslld        $0x10,%xmm5,%xmm5
+  .byte  196,227,61,24,237,1                 // vinsertf128   $0x1,%xmm5,%ymm8,%ymm5
+  .byte  197,217,114,244,13                  // vpslld        $0xd,%xmm4,%xmm4
+  .byte  197,201,114,246,13                  // vpslld        $0xd,%xmm6,%xmm6
+  .byte  196,227,93,24,230,1                 // vinsertf128   $0x1,%xmm6,%ymm4,%ymm4
+  .byte  197,220,86,229                      // vorps         %ymm5,%ymm4,%ymm4
+  .byte  196,227,125,25,229,1                // vextractf128  $0x1,%ymm4,%xmm5
+  .byte  197,209,254,235                     // vpaddd        %xmm3,%xmm5,%xmm5
+  .byte  197,217,254,219                     // vpaddd        %xmm3,%xmm4,%xmm3
+  .byte  196,227,101,24,221,1                // vinsertf128   $0x1,%xmm5,%ymm3,%ymm3
+  .byte  196,195,101,74,222,112              // vblendvps     %ymm7,%ymm14,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,100,36,128               // vmovups       -0x80(%rsp),%ymm4
+  .byte  197,252,16,108,36,160               // vmovups       -0x60(%rsp),%ymm5
+  .byte  197,252,16,116,36,192               // vmovups       -0x40(%rsp),%ymm6
+  .byte  197,252,16,124,36,224               // vmovups       -0x20(%rsp),%ymm7
+  .byte  72,131,196,24                       // add           $0x18,%rsp
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,123,16,4,208                    // vmovsd        (%rax,%rdx,8),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,79                              // je            4676 <_sk_load_f16_avx+0x316>
+  .byte  197,57,22,68,208,8                  // vmovhpd       0x8(%rax,%rdx,8),%xmm8,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,67                              // jb            4676 <_sk_load_f16_avx+0x316>
+  .byte  197,251,16,84,208,16                // vmovsd        0x10(%rax,%rdx,8),%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  116,68                              // je            4683 <_sk_load_f16_avx+0x323>
+  .byte  197,233,22,84,208,24                // vmovhpd       0x18(%rax,%rdx,8),%xmm2,%xmm2
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,56                              // jb            4683 <_sk_load_f16_avx+0x323>
+  .byte  197,251,16,76,208,32                // vmovsd        0x20(%rax,%rdx,8),%xmm1
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  15,132,70,253,255,255               // je            43a1 <_sk_load_f16_avx+0x41>
+  .byte  197,241,22,76,208,40                // vmovhpd       0x28(%rax,%rdx,8),%xmm1,%xmm1
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  15,130,54,253,255,255               // jb            43a1 <_sk_load_f16_avx+0x41>
+  .byte  197,122,126,76,208,48               // vmovq         0x30(%rax,%rdx,8),%xmm9
+  .byte  233,43,253,255,255                  // jmpq          43a1 <_sk_load_f16_avx+0x41>
+  .byte  197,241,87,201                      // vxorpd        %xmm1,%xmm1,%xmm1
+  .byte  197,233,87,210                      // vxorpd        %xmm2,%xmm2,%xmm2
+  .byte  233,30,253,255,255                  // jmpq          43a1 <_sk_load_f16_avx+0x41>
+  .byte  197,241,87,201                      // vxorpd        %xmm1,%xmm1,%xmm1
+  .byte  233,21,253,255,255                  // jmpq          43a1 <_sk_load_f16_avx+0x41>
+
+HIDDEN _sk_gather_f16_avx
+.globl _sk_gather_f16_avx
+FUNCTION(_sk_gather_f16_avx)
+_sk_gather_f16_avx:
+  .byte  72,131,236,24                       // sub           $0x18,%rsp
+  .byte  197,252,17,124,36,224               // vmovups       %ymm7,-0x20(%rsp)
+  .byte  197,252,17,116,36,192               // vmovups       %ymm6,-0x40(%rsp)
+  .byte  197,252,17,108,36,160               // vmovups       %ymm5,-0x60(%rsp)
+  .byte  197,254,127,100,36,128              // vmovdqu       %ymm4,-0x80(%rsp)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  197,249,110,80,16                   // vmovd         0x10(%rax),%xmm2
+  .byte  197,249,112,210,0                   // vpshufd       $0x0,%xmm2,%xmm2
+  .byte  196,226,105,64,217                  // vpmulld       %xmm1,%xmm2,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,226,105,64,201                  // vpmulld       %xmm1,%xmm2,%xmm1
+  .byte  197,254,91,208                      // vcvttps2dq    %ymm0,%ymm2
+  .byte  196,227,125,25,208,1                // vextractf128  $0x1,%ymm2,%xmm0
+  .byte  197,113,254,192                     // vpaddd        %xmm0,%xmm1,%xmm8
+  .byte  197,225,254,202                     // vpaddd        %xmm2,%xmm3,%xmm1
+  .byte  196,225,249,126,200                 // vmovq         %xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,65,122,126,12,193               // vmovq         (%r9,%rax,8),%xmm9
+  .byte  196,227,249,22,200,1                // vpextrq       $0x1,%xmm1,%rax
+  .byte  196,1,122,126,20,209                // vmovq         (%r9,%r10,8),%xmm10
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,65,122,126,28,193               // vmovq         (%r9,%rax,8),%xmm11
+  .byte  196,97,249,126,192                  // vmovq         %xmm8,%rax
+  .byte  196,1,122,126,36,209                // vmovq         (%r9,%r10,8),%xmm12
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,65,122,126,44,193               // vmovq         (%r9,%rax,8),%xmm13
+  .byte  196,67,249,22,195,1                 // vpextrq       $0x1,%xmm8,%r11
+  .byte  196,1,122,126,4,209                 // vmovq         (%r9,%r10,8),%xmm8
+  .byte  76,137,216                          // mov           %r11,%rax
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  196,65,122,126,52,193               // vmovq         (%r9,%rax,8),%xmm14
+  .byte  68,137,216                          // mov           %r11d,%eax
+  .byte  196,193,122,126,4,193               // vmovq         (%r9,%rax,8),%xmm0
+  .byte  196,193,41,108,209                  // vpunpcklqdq   %xmm9,%xmm10,%xmm2
+  .byte  196,193,25,108,203                  // vpunpcklqdq   %xmm11,%xmm12,%xmm1
+  .byte  196,193,57,108,221                  // vpunpcklqdq   %xmm13,%xmm8,%xmm3
+  .byte  196,193,121,108,198                 // vpunpcklqdq   %xmm14,%xmm0,%xmm0
+  .byte  197,105,97,193                      // vpunpcklwd    %xmm1,%xmm2,%xmm8
+  .byte  197,233,105,209                     // vpunpckhwd    %xmm1,%xmm2,%xmm2
+  .byte  197,225,97,200                      // vpunpcklwd    %xmm0,%xmm3,%xmm1
+  .byte  197,225,105,192                     // vpunpckhwd    %xmm0,%xmm3,%xmm0
+  .byte  197,57,97,250                       // vpunpcklwd    %xmm2,%xmm8,%xmm15
+  .byte  197,57,105,194                      // vpunpckhwd    %xmm2,%xmm8,%xmm8
+  .byte  197,241,97,208                      // vpunpcklwd    %xmm0,%xmm1,%xmm2
+  .byte  197,113,105,200                     // vpunpckhwd    %xmm0,%xmm1,%xmm9
+  .byte  197,129,108,194                     // vpunpcklqdq   %xmm2,%xmm15,%xmm0
+  .byte  197,241,239,201                     // vpxor         %xmm1,%xmm1,%xmm1
+  .byte  197,249,105,201                     // vpunpckhwd    %xmm1,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  196,98,125,24,37,190,32,0,0         // vbroadcastss  0x20be(%rip),%ymm12        # 6854 <_sk_callback_avx+0x458>
+  .byte  196,193,124,84,204                  // vandps        %ymm12,%ymm0,%ymm1
+  .byte  197,252,87,193                      // vxorps        %ymm1,%ymm0,%ymm0
+  .byte  196,195,125,25,198,1                // vextractf128  $0x1,%ymm0,%xmm14
+  .byte  196,98,121,24,29,170,32,0,0         // vbroadcastss  0x20aa(%rip),%xmm11        # 6858 <_sk_callback_avx+0x45c>
+  .byte  196,193,8,87,219                    // vxorps        %xmm11,%xmm14,%xmm3
+  .byte  196,98,121,24,45,160,32,0,0         // vbroadcastss  0x20a0(%rip),%xmm13        # 685c <_sk_callback_avx+0x460>
+  .byte  197,145,102,219                     // vpcmpgtd      %xmm3,%xmm13,%xmm3
+  .byte  196,65,120,87,211                   // vxorps        %xmm11,%xmm0,%xmm10
+  .byte  196,65,17,102,210                   // vpcmpgtd      %xmm10,%xmm13,%xmm10
+  .byte  196,99,45,24,211,1                  // vinsertf128   $0x1,%xmm3,%ymm10,%ymm10
+  .byte  197,225,114,241,16                  // vpslld        $0x10,%xmm1,%xmm3
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  197,241,114,241,16                  // vpslld        $0x10,%xmm1,%xmm1
+  .byte  196,227,101,24,201,1                // vinsertf128   $0x1,%xmm1,%ymm3,%ymm1
+  .byte  197,249,114,240,13                  // vpslld        $0xd,%xmm0,%xmm0
+  .byte  196,193,97,114,246,13               // vpslld        $0xd,%xmm14,%xmm3
+  .byte  196,227,125,24,195,1                // vinsertf128   $0x1,%xmm3,%ymm0,%ymm0
+  .byte  197,252,86,193                      // vorps         %ymm1,%ymm0,%ymm0
+  .byte  196,227,125,25,193,1                // vextractf128  $0x1,%ymm0,%xmm1
+  .byte  196,226,121,24,29,86,32,0,0         // vbroadcastss  0x2056(%rip),%xmm3        # 6860 <_sk_callback_avx+0x464>
+  .byte  197,241,254,203                     // vpaddd        %xmm3,%xmm1,%xmm1
+  .byte  197,249,254,195                     // vpaddd        %xmm3,%xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  196,65,12,87,246                    // vxorps        %ymm14,%ymm14,%ymm14
+  .byte  196,195,125,74,198,160              // vblendvps     %ymm10,%ymm14,%ymm0,%ymm0
+  .byte  197,129,109,202                     // vpunpckhqdq   %xmm2,%xmm15,%xmm1
+  .byte  197,217,239,228                     // vpxor         %xmm4,%xmm4,%xmm4
+  .byte  197,241,105,212                     // vpunpckhwd    %xmm4,%xmm1,%xmm2
+  .byte  196,226,121,51,201                  // vpmovzxwd     %xmm1,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,193,116,84,212                  // vandps        %ymm12,%ymm1,%ymm2
+  .byte  197,244,87,202                      // vxorps        %ymm2,%ymm1,%ymm1
+  .byte  196,195,125,25,202,1                // vextractf128  $0x1,%ymm1,%xmm10
+  .byte  196,193,40,87,251                   // vxorps        %xmm11,%xmm10,%xmm7
+  .byte  197,145,102,255                     // vpcmpgtd      %xmm7,%xmm13,%xmm7
+  .byte  196,193,112,87,243                  // vxorps        %xmm11,%xmm1,%xmm6
+  .byte  197,145,102,246                     // vpcmpgtd      %xmm6,%xmm13,%xmm6
+  .byte  196,227,77,24,247,1                 // vinsertf128   $0x1,%xmm7,%ymm6,%ymm6
+  .byte  197,193,114,242,16                  // vpslld        $0x10,%xmm2,%xmm7
+  .byte  196,227,125,25,210,1                // vextractf128  $0x1,%ymm2,%xmm2
+  .byte  197,233,114,242,16                  // vpslld        $0x10,%xmm2,%xmm2
+  .byte  196,227,69,24,210,1                 // vinsertf128   $0x1,%xmm2,%ymm7,%ymm2
+  .byte  197,241,114,241,13                  // vpslld        $0xd,%xmm1,%xmm1
+  .byte  196,193,65,114,242,13               // vpslld        $0xd,%xmm10,%xmm7
+  .byte  196,227,117,24,207,1                // vinsertf128   $0x1,%xmm7,%ymm1,%ymm1
+  .byte  197,244,86,202                      // vorps         %ymm2,%ymm1,%ymm1
+  .byte  196,227,125,25,202,1                // vextractf128  $0x1,%ymm1,%xmm2
+  .byte  197,233,254,211                     // vpaddd        %xmm3,%xmm2,%xmm2
+  .byte  197,241,254,203                     // vpaddd        %xmm3,%xmm1,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  196,195,117,74,206,96               // vblendvps     %ymm6,%ymm14,%ymm1,%ymm1
+  .byte  196,193,57,108,209                  // vpunpcklqdq   %xmm9,%xmm8,%xmm2
+  .byte  197,233,105,244                     // vpunpckhwd    %xmm4,%xmm2,%xmm6
+  .byte  196,65,41,239,210                   // vpxor         %xmm10,%xmm10,%xmm10
+  .byte  196,226,121,51,210                  // vpmovzxwd     %xmm2,%xmm2
+  .byte  196,227,109,24,214,1                // vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  .byte  196,193,108,84,244                  // vandps        %ymm12,%ymm2,%ymm6
+  .byte  197,236,87,214                      // vxorps        %ymm6,%ymm2,%ymm2
+  .byte  196,227,125,25,215,1                // vextractf128  $0x1,%ymm2,%xmm7
+  .byte  196,193,64,87,235                   // vxorps        %xmm11,%xmm7,%xmm5
+  .byte  197,145,102,237                     // vpcmpgtd      %xmm5,%xmm13,%xmm5
+  .byte  196,193,104,87,227                  // vxorps        %xmm11,%xmm2,%xmm4
+  .byte  197,145,102,228                     // vpcmpgtd      %xmm4,%xmm13,%xmm4
+  .byte  196,227,93,24,229,1                 // vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  .byte  197,209,114,246,16                  // vpslld        $0x10,%xmm6,%xmm5
+  .byte  196,227,125,25,246,1                // vextractf128  $0x1,%ymm6,%xmm6
+  .byte  197,201,114,246,16                  // vpslld        $0x10,%xmm6,%xmm6
+  .byte  196,227,85,24,238,1                 // vinsertf128   $0x1,%xmm6,%ymm5,%ymm5
+  .byte  197,233,114,242,13                  // vpslld        $0xd,%xmm2,%xmm2
+  .byte  197,201,114,247,13                  // vpslld        $0xd,%xmm7,%xmm6
+  .byte  196,227,109,24,214,1                // vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  .byte  197,236,86,213                      // vorps         %ymm5,%ymm2,%ymm2
+  .byte  196,227,125,25,213,1                // vextractf128  $0x1,%ymm2,%xmm5
+  .byte  197,209,254,235                     // vpaddd        %xmm3,%xmm5,%xmm5
+  .byte  197,233,254,211                     // vpaddd        %xmm3,%xmm2,%xmm2
+  .byte  196,227,109,24,213,1                // vinsertf128   $0x1,%xmm5,%ymm2,%ymm2
+  .byte  196,195,109,74,214,64               // vblendvps     %ymm4,%ymm14,%ymm2,%ymm2
+  .byte  196,193,57,109,225                  // vpunpckhqdq   %xmm9,%xmm8,%xmm4
+  .byte  196,193,89,105,234                  // vpunpckhwd    %xmm10,%xmm4,%xmm5
+  .byte  196,226,121,51,228                  // vpmovzxwd     %xmm4,%xmm4
+  .byte  196,227,93,24,229,1                 // vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  .byte  196,193,92,84,236                   // vandps        %ymm12,%ymm4,%ymm5
+  .byte  197,220,87,229                      // vxorps        %ymm5,%ymm4,%ymm4
+  .byte  196,227,125,25,230,1                // vextractf128  $0x1,%ymm4,%xmm6
+  .byte  196,193,72,87,251                   // vxorps        %xmm11,%xmm6,%xmm7
+  .byte  197,17,102,199                      // vpcmpgtd      %xmm7,%xmm13,%xmm8
+  .byte  196,193,88,87,251                   // vxorps        %xmm11,%xmm4,%xmm7
+  .byte  197,145,102,255                     // vpcmpgtd      %xmm7,%xmm13,%xmm7
+  .byte  196,195,69,24,248,1                 // vinsertf128   $0x1,%xmm8,%ymm7,%ymm7
+  .byte  197,185,114,245,16                  // vpslld        $0x10,%xmm5,%xmm8
+  .byte  196,227,125,25,237,1                // vextractf128  $0x1,%ymm5,%xmm5
+  .byte  197,209,114,245,16                  // vpslld        $0x10,%xmm5,%xmm5
+  .byte  196,227,61,24,237,1                 // vinsertf128   $0x1,%xmm5,%ymm8,%ymm5
+  .byte  197,217,114,244,13                  // vpslld        $0xd,%xmm4,%xmm4
+  .byte  197,201,114,246,13                  // vpslld        $0xd,%xmm6,%xmm6
+  .byte  196,227,93,24,230,1                 // vinsertf128   $0x1,%xmm6,%ymm4,%ymm4
+  .byte  197,220,86,229                      // vorps         %ymm5,%ymm4,%ymm4
+  .byte  196,227,125,25,229,1                // vextractf128  $0x1,%ymm4,%xmm5
+  .byte  197,209,254,235                     // vpaddd        %xmm3,%xmm5,%xmm5
+  .byte  197,217,254,219                     // vpaddd        %xmm3,%xmm4,%xmm3
+  .byte  196,227,101,24,221,1                // vinsertf128   $0x1,%xmm5,%ymm3,%ymm3
+  .byte  196,195,101,74,222,112              // vblendvps     %ymm7,%ymm14,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,100,36,128               // vmovups       -0x80(%rsp),%ymm4
+  .byte  197,252,16,108,36,160               // vmovups       -0x60(%rsp),%ymm5
+  .byte  197,252,16,116,36,192               // vmovups       -0x40(%rsp),%ymm6
+  .byte  197,252,16,124,36,224               // vmovups       -0x20(%rsp),%ymm7
+  .byte  72,131,196,24                       // add           $0x18,%rsp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_f16_avx
+.globl _sk_store_f16_avx
+FUNCTION(_sk_store_f16_avx)
+_sk_store_f16_avx:
+  .byte  72,131,236,88                       // sub           $0x58,%rsp
+  .byte  197,252,17,124,36,32                // vmovups       %ymm7,0x20(%rsp)
+  .byte  197,252,17,52,36                    // vmovups       %ymm6,(%rsp)
+  .byte  197,252,17,108,36,224               // vmovups       %ymm5,-0x20(%rsp)
+  .byte  197,252,17,100,36,192               // vmovups       %ymm4,-0x40(%rsp)
+  .byte  196,98,125,24,13,118,30,0,0         // vbroadcastss  0x1e76(%rip),%ymm9        # 6864 <_sk_callback_avx+0x468>
+  .byte  196,65,124,84,209                   // vandps        %ymm9,%ymm0,%ymm10
+  .byte  197,252,17,68,36,128                // vmovups       %ymm0,-0x80(%rsp)
+  .byte  196,65,124,87,218                   // vxorps        %ymm10,%ymm0,%ymm11
+  .byte  196,67,125,25,220,1                 // vextractf128  $0x1,%ymm11,%xmm12
+  .byte  196,98,121,24,5,91,30,0,0           // vbroadcastss  0x1e5b(%rip),%xmm8        # 6868 <_sk_callback_avx+0x46c>
+  .byte  196,65,57,102,236                   // vpcmpgtd      %xmm12,%xmm8,%xmm13
+  .byte  196,65,57,102,243                   // vpcmpgtd      %xmm11,%xmm8,%xmm14
+  .byte  196,67,13,24,237,1                  // vinsertf128   $0x1,%xmm13,%ymm14,%ymm13
+  .byte  196,193,9,114,210,16                // vpsrld        $0x10,%xmm10,%xmm14
+  .byte  196,67,125,25,210,1                 // vextractf128  $0x1,%ymm10,%xmm10
+  .byte  196,193,41,114,210,16               // vpsrld        $0x10,%xmm10,%xmm10
+  .byte  196,67,13,24,242,1                  // vinsertf128   $0x1,%xmm10,%ymm14,%ymm14
+  .byte  196,193,33,114,211,13               // vpsrld        $0xd,%xmm11,%xmm11
+  .byte  196,193,25,114,212,13               // vpsrld        $0xd,%xmm12,%xmm12
+  .byte  196,98,125,24,21,34,30,0,0          // vbroadcastss  0x1e22(%rip),%ymm10        # 686c <_sk_callback_avx+0x470>
+  .byte  196,65,12,86,242                    // vorps         %ymm10,%ymm14,%ymm14
+  .byte  196,67,125,25,247,1                 // vextractf128  $0x1,%ymm14,%xmm15
+  .byte  196,65,1,254,228                    // vpaddd        %xmm12,%xmm15,%xmm12
+  .byte  196,65,9,254,219                    // vpaddd        %xmm11,%xmm14,%xmm11
+  .byte  196,67,37,24,228,1                  // vinsertf128   $0x1,%xmm12,%ymm11,%ymm12
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  196,99,29,74,224,208                // vblendvps     %ymm13,%ymm0,%ymm12,%ymm12
+  .byte  196,65,116,84,233                   // vandps        %ymm9,%ymm1,%ymm13
+  .byte  197,252,17,76,36,160                // vmovups       %ymm1,-0x60(%rsp)
+  .byte  196,65,116,87,245                   // vxorps        %ymm13,%ymm1,%ymm14
+  .byte  196,67,125,25,247,1                 // vextractf128  $0x1,%ymm14,%xmm15
+  .byte  196,193,57,102,255                  // vpcmpgtd      %xmm15,%xmm8,%xmm7
+  .byte  196,65,57,102,222                   // vpcmpgtd      %xmm14,%xmm8,%xmm11
+  .byte  196,227,37,24,255,1                 // vinsertf128   $0x1,%xmm7,%ymm11,%ymm7
+  .byte  196,193,33,114,213,16               // vpsrld        $0x10,%xmm13,%xmm11
+  .byte  196,99,125,25,238,1                 // vextractf128  $0x1,%ymm13,%xmm6
+  .byte  197,201,114,214,16                  // vpsrld        $0x10,%xmm6,%xmm6
+  .byte  196,227,37,24,246,1                 // vinsertf128   $0x1,%xmm6,%ymm11,%ymm6
+  .byte  196,193,33,114,215,13               // vpsrld        $0xd,%xmm15,%xmm11
+  .byte  196,193,76,86,242                   // vorps         %ymm10,%ymm6,%ymm6
+  .byte  196,227,125,25,245,1                // vextractf128  $0x1,%ymm6,%xmm5
+  .byte  196,193,81,254,235                  // vpaddd        %xmm11,%xmm5,%xmm5
+  .byte  196,193,89,114,214,13               // vpsrld        $0xd,%xmm14,%xmm4
+  .byte  197,201,254,228                     // vpaddd        %xmm4,%xmm6,%xmm4
+  .byte  196,227,93,24,229,1                 // vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  .byte  196,99,93,74,232,112                // vblendvps     %ymm7,%ymm0,%ymm4,%ymm13
+  .byte  196,193,108,84,225                  // vandps        %ymm9,%ymm2,%ymm4
+  .byte  197,236,87,236                      // vxorps        %ymm4,%ymm2,%ymm5
+  .byte  196,227,125,25,238,1                // vextractf128  $0x1,%ymm5,%xmm6
+  .byte  197,185,102,254                     // vpcmpgtd      %xmm6,%xmm8,%xmm7
+  .byte  197,57,102,221                      // vpcmpgtd      %xmm5,%xmm8,%xmm11
+  .byte  196,227,37,24,255,1                 // vinsertf128   $0x1,%xmm7,%ymm11,%ymm7
+  .byte  197,161,114,212,16                  // vpsrld        $0x10,%xmm4,%xmm11
+  .byte  196,227,125,25,228,1                // vextractf128  $0x1,%ymm4,%xmm4
+  .byte  197,217,114,212,16                  // vpsrld        $0x10,%xmm4,%xmm4
+  .byte  196,227,37,24,228,1                 // vinsertf128   $0x1,%xmm4,%ymm11,%ymm4
+  .byte  197,201,114,214,13                  // vpsrld        $0xd,%xmm6,%xmm6
+  .byte  196,193,92,86,226                   // vorps         %ymm10,%ymm4,%ymm4
+  .byte  196,227,125,25,225,1                // vextractf128  $0x1,%ymm4,%xmm1
+  .byte  197,241,254,206                     // vpaddd        %xmm6,%xmm1,%xmm1
+  .byte  197,209,114,213,13                  // vpsrld        $0xd,%xmm5,%xmm5
+  .byte  197,217,254,229                     // vpaddd        %xmm5,%xmm4,%xmm4
+  .byte  196,227,93,24,201,1                 // vinsertf128   $0x1,%xmm1,%ymm4,%ymm1
+  .byte  196,99,117,74,216,112               // vblendvps     %ymm7,%ymm0,%ymm1,%ymm11
+  .byte  196,193,100,84,225                  // vandps        %ymm9,%ymm3,%ymm4
+  .byte  197,228,87,236                      // vxorps        %ymm4,%ymm3,%ymm5
+  .byte  196,227,125,25,238,1                // vextractf128  $0x1,%ymm5,%xmm6
+  .byte  197,185,102,254                     // vpcmpgtd      %xmm6,%xmm8,%xmm7
+  .byte  197,57,102,197                      // vpcmpgtd      %xmm5,%xmm8,%xmm8
+  .byte  196,227,61,24,255,1                 // vinsertf128   $0x1,%xmm7,%ymm8,%ymm7
+  .byte  197,185,114,212,16                  // vpsrld        $0x10,%xmm4,%xmm8
+  .byte  196,227,125,25,228,1                // vextractf128  $0x1,%ymm4,%xmm4
+  .byte  197,217,114,212,16                  // vpsrld        $0x10,%xmm4,%xmm4
+  .byte  196,227,61,24,228,1                 // vinsertf128   $0x1,%xmm4,%ymm8,%ymm4
+  .byte  196,193,92,86,226                   // vorps         %ymm10,%ymm4,%ymm4
+  .byte  197,201,114,214,13                  // vpsrld        $0xd,%xmm6,%xmm6
+  .byte  196,227,125,25,225,1                // vextractf128  $0x1,%ymm4,%xmm1
+  .byte  197,241,254,206                     // vpaddd        %xmm6,%xmm1,%xmm1
+  .byte  197,209,114,213,13                  // vpsrld        $0xd,%xmm5,%xmm5
+  .byte  197,217,254,229                     // vpaddd        %xmm5,%xmm4,%xmm4
+  .byte  196,227,93,24,201,1                 // vinsertf128   $0x1,%xmm1,%ymm4,%ymm1
+  .byte  196,227,117,74,200,112              // vblendvps     %ymm7,%ymm0,%ymm1,%ymm1
+  .byte  196,99,125,25,224,1                 // vextractf128  $0x1,%ymm12,%xmm0
+  .byte  196,226,25,43,192                   // vpackusdw     %xmm0,%xmm12,%xmm0
+  .byte  196,99,125,25,236,1                 // vextractf128  $0x1,%ymm13,%xmm4
+  .byte  196,226,17,43,228                   // vpackusdw     %xmm4,%xmm13,%xmm4
+  .byte  196,99,125,25,221,1                 // vextractf128  $0x1,%ymm11,%xmm5
+  .byte  196,226,33,43,245                   // vpackusdw     %xmm5,%xmm11,%xmm6
+  .byte  196,227,125,25,205,1                // vextractf128  $0x1,%ymm1,%xmm5
+  .byte  196,226,113,43,205                  // vpackusdw     %xmm5,%xmm1,%xmm1
+  .byte  197,249,97,236                      // vpunpcklwd    %xmm4,%xmm0,%xmm5
+  .byte  197,249,105,196                     // vpunpckhwd    %xmm4,%xmm0,%xmm0
+  .byte  197,201,97,225                      // vpunpcklwd    %xmm1,%xmm6,%xmm4
+  .byte  197,201,105,201                     // vpunpckhwd    %xmm1,%xmm6,%xmm1
+  .byte  197,81,98,220                       // vpunpckldq    %xmm4,%xmm5,%xmm11
+  .byte  197,81,106,212                      // vpunpckhdq    %xmm4,%xmm5,%xmm10
+  .byte  197,121,98,201                      // vpunpckldq    %xmm1,%xmm0,%xmm9
+  .byte  197,121,106,193                     // vpunpckhdq    %xmm1,%xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,66                              // jne           4c28 <_sk_store_f16_avx+0x25e>
+  .byte  197,120,17,28,208                   // vmovups       %xmm11,(%rax,%rdx,8)
+  .byte  197,120,17,84,208,16                // vmovups       %xmm10,0x10(%rax,%rdx,8)
+  .byte  197,120,17,76,208,32                // vmovups       %xmm9,0x20(%rax,%rdx,8)
+  .byte  197,122,127,68,208,48               // vmovdqu       %xmm8,0x30(%rax,%rdx,8)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,16,68,36,128                // vmovups       -0x80(%rsp),%ymm0
+  .byte  197,252,16,76,36,160                // vmovups       -0x60(%rsp),%ymm1
+  .byte  197,252,16,100,36,192               // vmovups       -0x40(%rsp),%ymm4
+  .byte  197,252,16,108,36,224               // vmovups       -0x20(%rsp),%ymm5
+  .byte  197,252,16,52,36                    // vmovups       (%rsp),%ymm6
+  .byte  197,252,16,124,36,32                // vmovups       0x20(%rsp),%ymm7
+  .byte  72,131,196,88                       // add           $0x58,%rsp
+  .byte  255,224                             // jmpq          *%rax
+  .byte  197,121,214,28,208                  // vmovq         %xmm11,(%rax,%rdx,8)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,202                             // je            4bfd <_sk_store_f16_avx+0x233>
+  .byte  197,121,23,92,208,8                 // vmovhpd       %xmm11,0x8(%rax,%rdx,8)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,190                             // jb            4bfd <_sk_store_f16_avx+0x233>
+  .byte  197,121,214,84,208,16               // vmovq         %xmm10,0x10(%rax,%rdx,8)
+  .byte  116,182                             // je            4bfd <_sk_store_f16_avx+0x233>
+  .byte  197,121,23,84,208,24                // vmovhpd       %xmm10,0x18(%rax,%rdx,8)
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,170                             // jb            4bfd <_sk_store_f16_avx+0x233>
+  .byte  197,121,214,76,208,32               // vmovq         %xmm9,0x20(%rax,%rdx,8)
+  .byte  116,162                             // je            4bfd <_sk_store_f16_avx+0x233>
+  .byte  197,121,23,76,208,40                // vmovhpd       %xmm9,0x28(%rax,%rdx,8)
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,150                             // jb            4bfd <_sk_store_f16_avx+0x233>
+  .byte  197,121,214,68,208,48               // vmovq         %xmm8,0x30(%rax,%rdx,8)
+  .byte  235,142                             // jmp           4bfd <_sk_store_f16_avx+0x233>
+
+HIDDEN _sk_load_u16_be_avx
+.globl _sk_load_u16_be_avx
+FUNCTION(_sk_load_u16_be_avx)
+_sk_load_u16_be_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,253,0,0,0                    // jne           4d82 <_sk_load_u16_be_avx+0x113>
+  .byte  196,65,121,16,4,65                  // vmovupd       (%r9,%rax,2),%xmm8
+  .byte  196,193,121,16,84,65,16             // vmovupd       0x10(%r9,%rax,2),%xmm2
+  .byte  196,193,121,16,92,65,32             // vmovupd       0x20(%r9,%rax,2),%xmm3
+  .byte  196,65,122,111,76,65,48             // vmovdqu       0x30(%r9,%rax,2),%xmm9
+  .byte  197,185,97,194                      // vpunpcklwd    %xmm2,%xmm8,%xmm0
+  .byte  197,185,105,210                     // vpunpckhwd    %xmm2,%xmm8,%xmm2
+  .byte  196,193,97,97,201                   // vpunpcklwd    %xmm9,%xmm3,%xmm1
+  .byte  196,193,97,105,217                  // vpunpckhwd    %xmm9,%xmm3,%xmm3
+  .byte  197,121,97,202                      // vpunpcklwd    %xmm2,%xmm0,%xmm9
+  .byte  197,121,105,194                     // vpunpckhwd    %xmm2,%xmm0,%xmm8
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,113,105,227                     // vpunpckhwd    %xmm3,%xmm1,%xmm12
+  .byte  197,177,108,194                     // vpunpcklqdq   %xmm2,%xmm9,%xmm0
+  .byte  197,241,113,240,8                   // vpsllw        $0x8,%xmm0,%xmm1
+  .byte  197,249,113,208,8                   // vpsrlw        $0x8,%xmm0,%xmm0
+  .byte  197,241,235,192                     // vpor          %xmm0,%xmm1,%xmm0
+  .byte  196,65,41,239,210                   // vpxor         %xmm10,%xmm10,%xmm10
+  .byte  196,193,121,105,202                 // vpunpckhwd    %xmm10,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,29,122,27,0,0         // vbroadcastss  0x1b7a(%rip),%ymm11        # 6870 <_sk_callback_avx+0x474>
+  .byte  196,193,124,89,195                  // vmulps        %ymm11,%ymm0,%ymm0
+  .byte  197,177,109,202                     // vpunpckhqdq   %xmm2,%xmm9,%xmm1
+  .byte  197,233,113,241,8                   // vpsllw        $0x8,%xmm1,%xmm2
+  .byte  197,241,113,209,8                   // vpsrlw        $0x8,%xmm1,%xmm1
+  .byte  197,233,235,201                     // vpor          %xmm1,%xmm2,%xmm1
+  .byte  196,193,113,105,210                 // vpunpckhwd    %xmm10,%xmm1,%xmm2
+  .byte  196,226,121,51,201                  // vpmovzxwd     %xmm1,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,203                  // vmulps        %ymm11,%ymm1,%ymm1
+  .byte  196,193,57,108,212                  // vpunpcklqdq   %xmm12,%xmm8,%xmm2
+  .byte  197,225,113,242,8                   // vpsllw        $0x8,%xmm2,%xmm3
+  .byte  197,233,113,210,8                   // vpsrlw        $0x8,%xmm2,%xmm2
+  .byte  197,225,235,210                     // vpor          %xmm2,%xmm3,%xmm2
+  .byte  196,193,105,105,218                 // vpunpckhwd    %xmm10,%xmm2,%xmm3
+  .byte  196,226,121,51,210                  // vpmovzxwd     %xmm2,%xmm2
+  .byte  196,227,109,24,211,1                // vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,211                  // vmulps        %ymm11,%ymm2,%ymm2
+  .byte  196,193,57,109,220                  // vpunpckhqdq   %xmm12,%xmm8,%xmm3
+  .byte  197,185,113,243,8                   // vpsllw        $0x8,%xmm3,%xmm8
+  .byte  197,225,113,211,8                   // vpsrlw        $0x8,%xmm3,%xmm3
+  .byte  197,185,235,219                     // vpor          %xmm3,%xmm8,%xmm3
+  .byte  196,65,97,105,194                   // vpunpckhwd    %xmm10,%xmm3,%xmm8
+  .byte  196,226,121,51,219                  // vpmovzxwd     %xmm3,%xmm3
+  .byte  196,195,101,24,216,1                // vinsertf128   $0x1,%xmm8,%ymm3,%ymm3
+  .byte  197,252,91,219                      // vcvtdq2ps     %ymm3,%ymm3
+  .byte  196,193,100,89,219                  // vmulps        %ymm11,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,123,16,4,65                  // vmovsd        (%r9,%rax,2),%xmm8
+  .byte  196,65,49,239,201                   // vpxor         %xmm9,%xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,85                              // je            4de8 <_sk_load_u16_be_avx+0x179>
+  .byte  196,65,57,22,68,65,8                // vmovhpd       0x8(%r9,%rax,2),%xmm8,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,72                              // jb            4de8 <_sk_load_u16_be_avx+0x179>
+  .byte  196,193,123,16,84,65,16             // vmovsd        0x10(%r9,%rax,2),%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  116,72                              // je            4df5 <_sk_load_u16_be_avx+0x186>
+  .byte  196,193,105,22,84,65,24             // vmovhpd       0x18(%r9,%rax,2),%xmm2,%xmm2
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,59                              // jb            4df5 <_sk_load_u16_be_avx+0x186>
+  .byte  196,193,123,16,92,65,32             // vmovsd        0x20(%r9,%rax,2),%xmm3
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  15,132,213,254,255,255              // je            4ca0 <_sk_load_u16_be_avx+0x31>
+  .byte  196,193,97,22,92,65,40              // vmovhpd       0x28(%r9,%rax,2),%xmm3,%xmm3
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  15,130,196,254,255,255              // jb            4ca0 <_sk_load_u16_be_avx+0x31>
+  .byte  196,65,122,126,76,65,48             // vmovq         0x30(%r9,%rax,2),%xmm9
+  .byte  233,184,254,255,255                 // jmpq          4ca0 <_sk_load_u16_be_avx+0x31>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  197,233,87,210                      // vxorpd        %xmm2,%xmm2,%xmm2
+  .byte  233,171,254,255,255                 // jmpq          4ca0 <_sk_load_u16_be_avx+0x31>
+  .byte  197,225,87,219                      // vxorpd        %xmm3,%xmm3,%xmm3
+  .byte  233,162,254,255,255                 // jmpq          4ca0 <_sk_load_u16_be_avx+0x31>
+
+HIDDEN _sk_load_rgb_u16_be_avx
+.globl _sk_load_rgb_u16_be_avx
+FUNCTION(_sk_load_rgb_u16_be_avx)
+_sk_load_rgb_u16_be_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,82                         // lea           (%rdx,%rdx,2),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,243,0,0,0                    // jne           4f03 <_sk_load_rgb_u16_be_avx+0x105>
+  .byte  196,193,122,111,4,65                // vmovdqu       (%r9,%rax,2),%xmm0
+  .byte  196,193,122,111,84,65,12            // vmovdqu       0xc(%r9,%rax,2),%xmm2
+  .byte  196,193,122,111,76,65,24            // vmovdqu       0x18(%r9,%rax,2),%xmm1
+  .byte  196,193,122,111,92,65,32            // vmovdqu       0x20(%r9,%rax,2),%xmm3
+  .byte  197,225,115,219,4                   // vpsrldq       $0x4,%xmm3,%xmm3
+  .byte  197,185,115,216,6                   // vpsrldq       $0x6,%xmm0,%xmm8
+  .byte  197,177,115,218,6                   // vpsrldq       $0x6,%xmm2,%xmm9
+  .byte  197,161,115,217,6                   // vpsrldq       $0x6,%xmm1,%xmm11
+  .byte  197,169,115,219,6                   // vpsrldq       $0x6,%xmm3,%xmm10
+  .byte  197,249,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm0
+  .byte  196,193,57,97,209                   // vpunpcklwd    %xmm9,%xmm8,%xmm2
+  .byte  197,241,97,203                      // vpunpcklwd    %xmm3,%xmm1,%xmm1
+  .byte  196,193,33,97,218                   // vpunpcklwd    %xmm10,%xmm11,%xmm3
+  .byte  197,121,97,194                      // vpunpcklwd    %xmm2,%xmm0,%xmm8
+  .byte  197,121,105,202                     // vpunpckhwd    %xmm2,%xmm0,%xmm9
+  .byte  197,241,97,211                      // vpunpcklwd    %xmm3,%xmm1,%xmm2
+  .byte  197,113,105,211                     // vpunpckhwd    %xmm3,%xmm1,%xmm10
+  .byte  197,185,108,194                     // vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  .byte  197,241,113,240,8                   // vpsllw        $0x8,%xmm0,%xmm1
+  .byte  197,249,113,208,8                   // vpsrlw        $0x8,%xmm0,%xmm0
+  .byte  197,241,235,192                     // vpor          %xmm0,%xmm1,%xmm0
+  .byte  196,65,25,239,228                   // vpxor         %xmm12,%xmm12,%xmm12
+  .byte  196,193,121,105,204                 // vpunpckhwd    %xmm12,%xmm0,%xmm1
+  .byte  196,226,121,51,192                  // vpmovzxwd     %xmm0,%xmm0
+  .byte  196,227,125,24,193,1                // vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  .byte  197,252,91,192                      // vcvtdq2ps     %ymm0,%ymm0
+  .byte  196,98,125,24,29,218,25,0,0         // vbroadcastss  0x19da(%rip),%ymm11        # 6874 <_sk_callback_avx+0x478>
+  .byte  196,193,124,89,195                  // vmulps        %ymm11,%ymm0,%ymm0
+  .byte  197,185,109,202                     // vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  .byte  197,233,113,241,8                   // vpsllw        $0x8,%xmm1,%xmm2
+  .byte  197,241,113,209,8                   // vpsrlw        $0x8,%xmm1,%xmm1
+  .byte  197,233,235,201                     // vpor          %xmm1,%xmm2,%xmm1
+  .byte  196,193,113,105,212                 // vpunpckhwd    %xmm12,%xmm1,%xmm2
+  .byte  196,226,121,51,201                  // vpmovzxwd     %xmm1,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  197,252,91,201                      // vcvtdq2ps     %ymm1,%ymm1
+  .byte  196,193,116,89,203                  // vmulps        %ymm11,%ymm1,%ymm1
+  .byte  196,193,49,108,210                  // vpunpcklqdq   %xmm10,%xmm9,%xmm2
+  .byte  197,225,113,242,8                   // vpsllw        $0x8,%xmm2,%xmm3
+  .byte  197,233,113,210,8                   // vpsrlw        $0x8,%xmm2,%xmm2
+  .byte  197,225,235,210                     // vpor          %xmm2,%xmm3,%xmm2
+  .byte  196,193,105,105,220                 // vpunpckhwd    %xmm12,%xmm2,%xmm3
+  .byte  196,226,121,51,210                  // vpmovzxwd     %xmm2,%xmm2
+  .byte  196,227,109,24,211,1                // vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  .byte  197,252,91,210                      // vcvtdq2ps     %ymm2,%ymm2
+  .byte  196,193,108,89,211                  // vmulps        %ymm11,%ymm2,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,29,119,25,0,0        // vbroadcastss  0x1977(%rip),%ymm3        # 6878 <_sk_callback_avx+0x47c>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,193,121,110,4,65                // vmovd         (%r9,%rax,2),%xmm0
+  .byte  196,193,121,196,68,65,4,2           // vpinsrw       $0x2,0x4(%r9,%rax,2),%xmm0,%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,5                               // jne           4f1c <_sk_load_rgb_u16_be_avx+0x11e>
+  .byte  233,40,255,255,255                  // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+  .byte  196,193,121,110,76,65,6             // vmovd         0x6(%r9,%rax,2),%xmm1
+  .byte  196,65,113,196,68,65,10,2           // vpinsrw       $0x2,0xa(%r9,%rax,2),%xmm1,%xmm8
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,26                              // jb            4f4b <_sk_load_rgb_u16_be_avx+0x14d>
+  .byte  196,193,121,110,76,65,12            // vmovd         0xc(%r9,%rax,2),%xmm1
+  .byte  196,193,113,196,84,65,16,2          // vpinsrw       $0x2,0x10(%r9,%rax,2),%xmm1,%xmm2
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  117,10                              // jne           4f50 <_sk_load_rgb_u16_be_avx+0x152>
+  .byte  233,249,254,255,255                 // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+  .byte  233,244,254,255,255                 // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+  .byte  196,193,121,110,76,65,18            // vmovd         0x12(%r9,%rax,2),%xmm1
+  .byte  196,65,113,196,76,65,22,2           // vpinsrw       $0x2,0x16(%r9,%rax,2),%xmm1,%xmm9
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,26                              // jb            4f7f <_sk_load_rgb_u16_be_avx+0x181>
+  .byte  196,193,121,110,76,65,24            // vmovd         0x18(%r9,%rax,2),%xmm1
+  .byte  196,193,113,196,76,65,28,2          // vpinsrw       $0x2,0x1c(%r9,%rax,2),%xmm1,%xmm1
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  117,10                              // jne           4f84 <_sk_load_rgb_u16_be_avx+0x186>
+  .byte  233,197,254,255,255                 // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+  .byte  233,192,254,255,255                 // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+  .byte  196,193,121,110,92,65,30            // vmovd         0x1e(%r9,%rax,2),%xmm3
+  .byte  196,65,97,196,92,65,34,2            // vpinsrw       $0x2,0x22(%r9,%rax,2),%xmm3,%xmm11
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,20                              // jb            4fad <_sk_load_rgb_u16_be_avx+0x1af>
+  .byte  196,193,121,110,92,65,36            // vmovd         0x24(%r9,%rax,2),%xmm3
+  .byte  196,193,97,196,92,65,40,2           // vpinsrw       $0x2,0x28(%r9,%rax,2),%xmm3,%xmm3
+  .byte  233,151,254,255,255                 // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+  .byte  233,146,254,255,255                 // jmpq          4e44 <_sk_load_rgb_u16_be_avx+0x46>
+
+HIDDEN _sk_store_u16_be_avx
+.globl _sk_store_u16_be_avx
+FUNCTION(_sk_store_u16_be_avx)
+_sk_store_u16_be_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  196,98,125,24,5,180,24,0,0          // vbroadcastss  0x18b4(%rip),%ymm8        # 687c <_sk_callback_avx+0x480>
+  .byte  196,65,124,89,200                   // vmulps        %ymm8,%ymm0,%ymm9
+  .byte  196,65,125,91,201                   // vcvtps2dq     %ymm9,%ymm9
+  .byte  196,67,125,25,202,1                 // vextractf128  $0x1,%ymm9,%xmm10
+  .byte  196,66,49,43,202                    // vpackusdw     %xmm10,%xmm9,%xmm9
+  .byte  196,193,41,113,241,8                // vpsllw        $0x8,%xmm9,%xmm10
+  .byte  196,193,49,113,209,8                // vpsrlw        $0x8,%xmm9,%xmm9
+  .byte  196,65,41,235,201                   // vpor          %xmm9,%xmm10,%xmm9
+  .byte  196,65,116,89,208                   // vmulps        %ymm8,%ymm1,%ymm10
+  .byte  196,65,125,91,210                   // vcvtps2dq     %ymm10,%ymm10
+  .byte  196,67,125,25,211,1                 // vextractf128  $0x1,%ymm10,%xmm11
+  .byte  196,66,41,43,211                    // vpackusdw     %xmm11,%xmm10,%xmm10
+  .byte  196,193,33,113,242,8                // vpsllw        $0x8,%xmm10,%xmm11
+  .byte  196,193,41,113,210,8                // vpsrlw        $0x8,%xmm10,%xmm10
+  .byte  196,65,33,235,210                   // vpor          %xmm10,%xmm11,%xmm10
+  .byte  196,65,108,89,216                   // vmulps        %ymm8,%ymm2,%ymm11
+  .byte  196,65,125,91,219                   // vcvtps2dq     %ymm11,%ymm11
+  .byte  196,67,125,25,220,1                 // vextractf128  $0x1,%ymm11,%xmm12
+  .byte  196,66,33,43,220                    // vpackusdw     %xmm12,%xmm11,%xmm11
+  .byte  196,193,25,113,243,8                // vpsllw        $0x8,%xmm11,%xmm12
+  .byte  196,193,33,113,211,8                // vpsrlw        $0x8,%xmm11,%xmm11
+  .byte  196,65,25,235,219                   // vpor          %xmm11,%xmm12,%xmm11
+  .byte  196,65,100,89,192                   // vmulps        %ymm8,%ymm3,%ymm8
+  .byte  196,65,125,91,192                   // vcvtps2dq     %ymm8,%ymm8
+  .byte  196,67,125,25,196,1                 // vextractf128  $0x1,%ymm8,%xmm12
+  .byte  196,66,57,43,196                    // vpackusdw     %xmm12,%xmm8,%xmm8
+  .byte  196,193,25,113,240,8                // vpsllw        $0x8,%xmm8,%xmm12
+  .byte  196,193,57,113,208,8                // vpsrlw        $0x8,%xmm8,%xmm8
+  .byte  196,65,25,235,192                   // vpor          %xmm8,%xmm12,%xmm8
+  .byte  196,65,49,97,226                    // vpunpcklwd    %xmm10,%xmm9,%xmm12
+  .byte  196,65,49,105,234                   // vpunpckhwd    %xmm10,%xmm9,%xmm13
+  .byte  196,65,33,97,200                    // vpunpcklwd    %xmm8,%xmm11,%xmm9
+  .byte  196,65,33,105,192                   // vpunpckhwd    %xmm8,%xmm11,%xmm8
+  .byte  196,65,25,98,217                    // vpunpckldq    %xmm9,%xmm12,%xmm11
+  .byte  196,65,25,106,209                   // vpunpckhdq    %xmm9,%xmm12,%xmm10
+  .byte  196,65,17,98,200                    // vpunpckldq    %xmm8,%xmm13,%xmm9
+  .byte  196,65,17,106,192                   // vpunpckhdq    %xmm8,%xmm13,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,31                              // jne           50ac <_sk_store_u16_be_avx+0xfa>
+  .byte  196,65,120,17,28,65                 // vmovups       %xmm11,(%r9,%rax,2)
+  .byte  196,65,120,17,84,65,16              // vmovups       %xmm10,0x10(%r9,%rax,2)
+  .byte  196,65,120,17,76,65,32              // vmovups       %xmm9,0x20(%r9,%rax,2)
+  .byte  196,65,122,127,68,65,48             // vmovdqu       %xmm8,0x30(%r9,%rax,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,121,214,28,65                // vmovq         %xmm11,(%r9,%rax,2)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            50a8 <_sk_store_u16_be_avx+0xf6>
+  .byte  196,65,121,23,92,65,8               // vmovhpd       %xmm11,0x8(%r9,%rax,2)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            50a8 <_sk_store_u16_be_avx+0xf6>
+  .byte  196,65,121,214,84,65,16             // vmovq         %xmm10,0x10(%r9,%rax,2)
+  .byte  116,218                             // je            50a8 <_sk_store_u16_be_avx+0xf6>
+  .byte  196,65,121,23,84,65,24              // vmovhpd       %xmm10,0x18(%r9,%rax,2)
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,205                             // jb            50a8 <_sk_store_u16_be_avx+0xf6>
+  .byte  196,65,121,214,76,65,32             // vmovq         %xmm9,0x20(%r9,%rax,2)
+  .byte  116,196                             // je            50a8 <_sk_store_u16_be_avx+0xf6>
+  .byte  196,65,121,23,76,65,40              // vmovhpd       %xmm9,0x28(%r9,%rax,2)
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,183                             // jb            50a8 <_sk_store_u16_be_avx+0xf6>
+  .byte  196,65,121,214,68,65,48             // vmovq         %xmm8,0x30(%r9,%rax,2)
+  .byte  235,174                             // jmp           50a8 <_sk_store_u16_be_avx+0xf6>
+
+HIDDEN _sk_load_f32_avx
+.globl _sk_load_f32_avx
+FUNCTION(_sk_load_f32_avx)
+_sk_load_f32_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  119,110                             // ja            5170 <_sk_load_f32_avx+0x76>
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  76,141,29,132,0,0,0                 // lea           0x84(%rip),%r11        # 5198 <_sk_load_f32_avx+0x9e>
+  .byte  75,99,4,131                         // movslq        (%r11,%r8,4),%rax
+  .byte  76,1,216                            // add           %r11,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,3,125,24,68,145,112,1           // vinsertf128   $0x1,0x70(%r9,%r10,4),%ymm0,%ymm8
+  .byte  196,131,125,24,92,145,96,1          // vinsertf128   $0x1,0x60(%r9,%r10,4),%ymm0,%ymm3
+  .byte  196,131,125,24,76,145,80,1          // vinsertf128   $0x1,0x50(%r9,%r10,4),%ymm0,%ymm1
+  .byte  196,131,125,24,84,145,64,1          // vinsertf128   $0x1,0x40(%r9,%r10,4),%ymm0,%ymm2
+  .byte  196,129,121,16,68,145,48            // vmovupd       0x30(%r9,%r10,4),%xmm0
+  .byte  196,195,125,13,192,12               // vblendpd      $0xc,%ymm8,%ymm0,%ymm0
+  .byte  196,1,121,16,68,145,32              // vmovupd       0x20(%r9,%r10,4),%xmm8
+  .byte  196,99,61,13,203,12                 // vblendpd      $0xc,%ymm3,%ymm8,%ymm9
+  .byte  196,129,121,16,92,145,16            // vmovupd       0x10(%r9,%r10,4),%xmm3
+  .byte  196,99,101,13,209,12                // vblendpd      $0xc,%ymm1,%ymm3,%ymm10
+  .byte  196,129,121,16,12,145               // vmovupd       (%r9,%r10,4),%xmm1
+  .byte  196,227,117,13,202,12               // vblendpd      $0xc,%ymm2,%ymm1,%ymm1
+  .byte  196,193,116,20,210                  // vunpcklps     %ymm10,%ymm1,%ymm2
+  .byte  196,193,116,21,218                  // vunpckhps     %ymm10,%ymm1,%ymm3
+  .byte  197,180,20,200                      // vunpcklps     %ymm0,%ymm9,%ymm1
+  .byte  197,52,21,192                       // vunpckhps     %ymm0,%ymm9,%ymm8
+  .byte  197,237,20,193                      // vunpcklpd     %ymm1,%ymm2,%ymm0
+  .byte  197,237,21,201                      // vunpckhpd     %ymm1,%ymm2,%ymm1
+  .byte  196,193,101,20,208                  // vunpcklpd     %ymm8,%ymm3,%ymm2
+  .byte  196,193,101,21,216                  // vunpckhpd     %ymm8,%ymm3,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  133,255                             // test          %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,204                             // dec           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  191,255,255,255,178                 // mov           $0xb2ffffff,%edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,165,255,255,255,157             // jmpq          *-0x62000001(%rbp)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,149,255,255,255,141             // callq         *-0x72000001(%rbp)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_store_f32_avx
+.globl _sk_store_f32_avx
+FUNCTION(_sk_store_f32_avx)
+_sk_store_f32_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  197,124,20,193                      // vunpcklps     %ymm1,%ymm0,%ymm8
+  .byte  197,124,21,217                      // vunpckhps     %ymm1,%ymm0,%ymm11
+  .byte  197,108,20,203                      // vunpcklps     %ymm3,%ymm2,%ymm9
+  .byte  197,108,21,227                      // vunpckhps     %ymm3,%ymm2,%ymm12
+  .byte  196,65,61,20,209                    // vunpcklpd     %ymm9,%ymm8,%ymm10
+  .byte  196,65,61,21,201                    // vunpckhpd     %ymm9,%ymm8,%ymm9
+  .byte  196,65,37,20,196                    // vunpcklpd     %ymm12,%ymm11,%ymm8
+  .byte  196,65,37,21,220                    // vunpckhpd     %ymm12,%ymm11,%ymm11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,55                              // jne           5225 <_sk_store_f32_avx+0x6d>
+  .byte  196,67,45,24,225,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
+  .byte  196,67,61,24,235,1                  // vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
+  .byte  196,67,45,6,201,49                  // vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
+  .byte  196,67,61,6,195,49                  // vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
+  .byte  196,65,125,17,36,129                // vmovupd       %ymm12,(%r9,%rax,4)
+  .byte  196,65,125,17,108,129,32            // vmovupd       %ymm13,0x20(%r9,%rax,4)
+  .byte  196,65,125,17,76,129,64             // vmovupd       %ymm9,0x40(%r9,%rax,4)
+  .byte  196,65,125,17,68,129,96             // vmovupd       %ymm8,0x60(%r9,%rax,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  196,65,121,17,20,129                // vmovupd       %xmm10,(%r9,%rax,4)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            5221 <_sk_store_f32_avx+0x69>
+  .byte  196,65,121,17,76,129,16             // vmovupd       %xmm9,0x10(%r9,%rax,4)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            5221 <_sk_store_f32_avx+0x69>
+  .byte  196,65,121,17,68,129,32             // vmovupd       %xmm8,0x20(%r9,%rax,4)
+  .byte  116,218                             // je            5221 <_sk_store_f32_avx+0x69>
+  .byte  196,65,121,17,92,129,48             // vmovupd       %xmm11,0x30(%r9,%rax,4)
+  .byte  73,131,248,5                        // cmp           $0x5,%r8
+  .byte  114,205                             // jb            5221 <_sk_store_f32_avx+0x69>
+  .byte  196,67,125,25,84,129,64,1           // vextractf128  $0x1,%ymm10,0x40(%r9,%rax,4)
+  .byte  116,195                             // je            5221 <_sk_store_f32_avx+0x69>
+  .byte  196,67,125,25,76,129,80,1           // vextractf128  $0x1,%ymm9,0x50(%r9,%rax,4)
+  .byte  73,131,248,7                        // cmp           $0x7,%r8
+  .byte  114,181                             // jb            5221 <_sk_store_f32_avx+0x69>
+  .byte  196,67,125,25,68,129,96,1           // vextractf128  $0x1,%ymm8,0x60(%r9,%rax,4)
+  .byte  235,171                             // jmp           5221 <_sk_store_f32_avx+0x69>
+
+HIDDEN _sk_clamp_x_avx
+.globl _sk_clamp_x_avx
+FUNCTION(_sk_clamp_x_avx)
+_sk_clamp_x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,192                      // vmaxps        %ymm0,%ymm8,%ymm0
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_y_avx
+.globl _sk_clamp_y_avx
+FUNCTION(_sk_clamp_y_avx)
+_sk_clamp_y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,201                      // vmaxps        %ymm1,%ymm8,%ymm1
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,193,116,93,200                  // vminps        %ymm8,%ymm1,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_avx
+.globl _sk_repeat_x_avx
+FUNCTION(_sk_repeat_x_avx)
+_sk_repeat_x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,65,124,94,200                   // vdivps        %ymm8,%ymm0,%ymm9
+  .byte  196,67,125,8,201,1                  // vroundps      $0x1,%ymm9,%ymm9
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  196,193,124,92,192                  // vsubps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_y_avx
+.globl _sk_repeat_y_avx
+FUNCTION(_sk_repeat_y_avx)
+_sk_repeat_y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,65,116,94,200                   // vdivps        %ymm8,%ymm1,%ymm9
+  .byte  196,67,125,8,201,1                  // vroundps      $0x1,%ymm9,%ymm9
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  196,193,116,92,200                  // vsubps        %ymm8,%ymm1,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_avx
+.globl _sk_mirror_x_avx
+FUNCTION(_sk_mirror_x_avx)
+_sk_mirror_x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,121,110,0                       // vmovd         (%rax),%xmm8
+  .byte  196,65,121,112,200,0                // vpshufd       $0x0,%xmm8,%xmm9
+  .byte  196,67,53,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
+  .byte  196,65,124,92,209                   // vsubps        %ymm9,%ymm0,%ymm10
+  .byte  196,193,58,88,192                   // vaddss        %xmm8,%xmm8,%xmm0
+  .byte  196,227,121,4,192,0                 // vpermilps     $0x0,%xmm0,%xmm0
+  .byte  196,227,125,24,192,1                // vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
+  .byte  197,44,94,192                       // vdivps        %ymm0,%ymm10,%ymm8
+  .byte  196,67,125,8,192,1                  // vroundps      $0x1,%ymm8,%ymm8
+  .byte  197,188,89,192                      // vmulps        %ymm0,%ymm8,%ymm0
+  .byte  197,172,92,192                      // vsubps        %ymm0,%ymm10,%ymm0
+  .byte  196,193,124,92,193                  // vsubps        %ymm9,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,192                       // vsubps        %ymm0,%ymm8,%ymm8
+  .byte  197,188,84,192                      // vandps        %ymm0,%ymm8,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_y_avx
+.globl _sk_mirror_y_avx
+FUNCTION(_sk_mirror_y_avx)
+_sk_mirror_y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,121,110,0                       // vmovd         (%rax),%xmm8
+  .byte  196,65,121,112,200,0                // vpshufd       $0x0,%xmm8,%xmm9
+  .byte  196,67,53,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
+  .byte  196,65,116,92,209                   // vsubps        %ymm9,%ymm1,%ymm10
+  .byte  196,193,58,88,200                   // vaddss        %xmm8,%xmm8,%xmm1
+  .byte  196,227,121,4,201,0                 // vpermilps     $0x0,%xmm1,%xmm1
+  .byte  196,227,117,24,201,1                // vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
+  .byte  197,44,94,193                       // vdivps        %ymm1,%ymm10,%ymm8
+  .byte  196,67,125,8,192,1                  // vroundps      $0x1,%ymm8,%ymm8
+  .byte  197,188,89,201                      // vmulps        %ymm1,%ymm8,%ymm1
+  .byte  197,172,92,201                      // vsubps        %ymm1,%ymm10,%ymm1
+  .byte  196,193,116,92,201                  // vsubps        %ymm9,%ymm1,%ymm1
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,193                       // vsubps        %ymm1,%ymm8,%ymm8
+  .byte  197,188,84,201                      // vandps        %ymm1,%ymm8,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_x_1_avx
+.globl _sk_clamp_x_1_avx
+FUNCTION(_sk_clamp_x_1_avx)
+_sk_clamp_x_1_avx:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,188,95,192                      // vmaxps        %ymm0,%ymm8,%ymm0
+  .byte  196,98,125,24,5,230,20,0,0          // vbroadcastss  0x14e6(%rip),%ymm8        # 6880 <_sk_callback_avx+0x484>
+  .byte  196,193,124,93,192                  // vminps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_1_avx
+.globl _sk_repeat_x_1_avx
+FUNCTION(_sk_repeat_x_1_avx)
+_sk_repeat_x_1_avx:
+  .byte  196,99,125,8,192,1                  // vroundps      $0x1,%ymm0,%ymm8
+  .byte  196,193,124,92,192                  // vsubps        %ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_1_avx
+.globl _sk_mirror_x_1_avx
+FUNCTION(_sk_mirror_x_1_avx)
+_sk_mirror_x_1_avx:
+  .byte  196,98,125,24,5,201,20,0,0          // vbroadcastss  0x14c9(%rip),%ymm8        # 6884 <_sk_callback_avx+0x488>
+  .byte  196,193,124,88,192                  // vaddps        %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,13,191,20,0,0         // vbroadcastss  0x14bf(%rip),%ymm9        # 6888 <_sk_callback_avx+0x48c>
+  .byte  196,65,124,89,201                   // vmulps        %ymm9,%ymm0,%ymm9
+  .byte  196,67,125,8,201,1                  // vroundps      $0x1,%ymm9,%ymm9
+  .byte  196,65,52,88,201                    // vaddps        %ymm9,%ymm9,%ymm9
+  .byte  196,193,124,92,193                  // vsubps        %ymm9,%ymm0,%ymm0
+  .byte  196,193,124,88,192                  // vaddps        %ymm8,%ymm0,%ymm0
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,192                       // vsubps        %ymm0,%ymm8,%ymm8
+  .byte  197,188,84,192                      // vandps        %ymm0,%ymm8,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminance_to_alpha_avx
+.globl _sk_luminance_to_alpha_avx
+FUNCTION(_sk_luminance_to_alpha_avx)
+_sk_luminance_to_alpha_avx:
+  .byte  196,226,125,24,29,143,20,0,0        // vbroadcastss  0x148f(%rip),%ymm3        # 688c <_sk_callback_avx+0x490>
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  196,226,125,24,29,134,20,0,0        // vbroadcastss  0x1486(%rip),%ymm3        # 6890 <_sk_callback_avx+0x494>
+  .byte  197,244,89,203                      // vmulps        %ymm3,%ymm1,%ymm1
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  196,226,125,24,13,121,20,0,0        // vbroadcastss  0x1479(%rip),%ymm1        # 6894 <_sk_callback_avx+0x498>
+  .byte  197,236,89,201                      // vmulps        %ymm1,%ymm2,%ymm1
+  .byte  197,252,88,217                      // vaddps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,87,192                      // vxorps        %ymm0,%ymm0,%ymm0
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  197,236,87,210                      // vxorps        %ymm2,%ymm2,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_2x3_avx
+.globl _sk_matrix_2x3_avx
+FUNCTION(_sk_matrix_2x3_avx)
+_sk_matrix_2x3_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,98,125,24,72,8                  // vbroadcastss  0x8(%rax),%ymm9
+  .byte  196,98,125,24,80,16                 // vbroadcastss  0x10(%rax),%ymm10
+  .byte  197,52,89,201                       // vmulps        %ymm1,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  197,60,89,192                       // vmulps        %ymm0,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,72,4                  // vbroadcastss  0x4(%rax),%ymm9
+  .byte  196,98,125,24,80,12                 // vbroadcastss  0xc(%rax),%ymm10
+  .byte  196,98,125,24,88,20                 // vbroadcastss  0x14(%rax),%ymm11
+  .byte  197,172,89,201                      // vmulps        %ymm1,%ymm10,%ymm1
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  197,180,89,192                      // vmulps        %ymm0,%ymm9,%ymm0
+  .byte  197,252,88,201                      // vaddps        %ymm1,%ymm0,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_3x4_avx
+.globl _sk_matrix_3x4_avx
+FUNCTION(_sk_matrix_3x4_avx)
+_sk_matrix_3x4_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,98,125,24,88,36                 // vbroadcastss  0x24(%rax),%ymm11
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  197,52,89,201                       // vmulps        %ymm1,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  197,60,89,192                       // vmulps        %ymm0,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,72,4                  // vbroadcastss  0x4(%rax),%ymm9
+  .byte  196,98,125,24,80,16                 // vbroadcastss  0x10(%rax),%ymm10
+  .byte  196,98,125,24,88,28                 // vbroadcastss  0x1c(%rax),%ymm11
+  .byte  196,98,125,24,96,40                 // vbroadcastss  0x28(%rax),%ymm12
+  .byte  197,36,89,218                       // vmulps        %ymm2,%ymm11,%ymm11
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  197,44,89,209                       // vmulps        %ymm1,%ymm10,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  197,52,89,200                       // vmulps        %ymm0,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,8                  // vbroadcastss  0x8(%rax),%ymm10
+  .byte  196,98,125,24,88,20                 // vbroadcastss  0x14(%rax),%ymm11
+  .byte  196,98,125,24,96,32                 // vbroadcastss  0x20(%rax),%ymm12
+  .byte  196,98,125,24,104,44                // vbroadcastss  0x2c(%rax),%ymm13
+  .byte  197,156,89,210                      // vmulps        %ymm2,%ymm12,%ymm2
+  .byte  196,193,108,88,213                  // vaddps        %ymm13,%ymm2,%ymm2
+  .byte  197,164,89,201                      // vmulps        %ymm1,%ymm11,%ymm1
+  .byte  197,244,88,202                      // vaddps        %ymm2,%ymm1,%ymm1
+  .byte  197,172,89,192                      // vmulps        %ymm0,%ymm10,%ymm0
+  .byte  197,252,88,209                      // vaddps        %ymm1,%ymm0,%ymm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x5_avx
+.globl _sk_matrix_4x5_avx
+FUNCTION(_sk_matrix_4x5_avx)
+_sk_matrix_4x5_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,98,125,24,72,16                 // vbroadcastss  0x10(%rax),%ymm9
+  .byte  196,98,125,24,80,32                 // vbroadcastss  0x20(%rax),%ymm10
+  .byte  196,98,125,24,88,48                 // vbroadcastss  0x30(%rax),%ymm11
+  .byte  196,98,125,24,96,64                 // vbroadcastss  0x40(%rax),%ymm12
+  .byte  197,36,89,219                       // vmulps        %ymm3,%ymm11,%ymm11
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  197,44,89,210                       // vmulps        %ymm2,%ymm10,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  197,52,89,201                       // vmulps        %ymm1,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  197,60,89,192                       // vmulps        %ymm0,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,72,4                  // vbroadcastss  0x4(%rax),%ymm9
+  .byte  196,98,125,24,80,20                 // vbroadcastss  0x14(%rax),%ymm10
+  .byte  196,98,125,24,88,36                 // vbroadcastss  0x24(%rax),%ymm11
+  .byte  196,98,125,24,96,52                 // vbroadcastss  0x34(%rax),%ymm12
+  .byte  196,98,125,24,104,68                // vbroadcastss  0x44(%rax),%ymm13
+  .byte  197,28,89,227                       // vmulps        %ymm3,%ymm12,%ymm12
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  197,36,89,218                       // vmulps        %ymm2,%ymm11,%ymm11
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  197,44,89,209                       // vmulps        %ymm1,%ymm10,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  197,52,89,200                       // vmulps        %ymm0,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,8                  // vbroadcastss  0x8(%rax),%ymm10
+  .byte  196,98,125,24,88,24                 // vbroadcastss  0x18(%rax),%ymm11
+  .byte  196,98,125,24,96,40                 // vbroadcastss  0x28(%rax),%ymm12
+  .byte  196,98,125,24,104,56                // vbroadcastss  0x38(%rax),%ymm13
+  .byte  196,98,125,24,112,72                // vbroadcastss  0x48(%rax),%ymm14
+  .byte  197,20,89,235                       // vmulps        %ymm3,%ymm13,%ymm13
+  .byte  196,65,20,88,238                    // vaddps        %ymm14,%ymm13,%ymm13
+  .byte  197,28,89,226                       // vmulps        %ymm2,%ymm12,%ymm12
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  197,36,89,217                       // vmulps        %ymm1,%ymm11,%ymm11
+  .byte  196,65,36,88,220                    // vaddps        %ymm12,%ymm11,%ymm11
+  .byte  197,44,89,208                       // vmulps        %ymm0,%ymm10,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  196,98,125,24,88,12                 // vbroadcastss  0xc(%rax),%ymm11
+  .byte  196,98,125,24,96,28                 // vbroadcastss  0x1c(%rax),%ymm12
+  .byte  196,98,125,24,104,44                // vbroadcastss  0x2c(%rax),%ymm13
+  .byte  196,98,125,24,112,60                // vbroadcastss  0x3c(%rax),%ymm14
+  .byte  196,98,125,24,120,76                // vbroadcastss  0x4c(%rax),%ymm15
+  .byte  197,140,89,219                      // vmulps        %ymm3,%ymm14,%ymm3
+  .byte  196,193,100,88,223                  // vaddps        %ymm15,%ymm3,%ymm3
+  .byte  197,148,89,210                      // vmulps        %ymm2,%ymm13,%ymm2
+  .byte  197,236,88,211                      // vaddps        %ymm3,%ymm2,%ymm2
+  .byte  197,156,89,201                      // vmulps        %ymm1,%ymm12,%ymm1
+  .byte  197,244,88,202                      // vaddps        %ymm2,%ymm1,%ymm1
+  .byte  197,164,89,192                      // vmulps        %ymm0,%ymm11,%ymm0
+  .byte  197,252,88,217                      // vaddps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  197,124,41,210                      // vmovaps       %ymm10,%ymm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x3_avx
+.globl _sk_matrix_4x3_avx
+FUNCTION(_sk_matrix_4x3_avx)
+_sk_matrix_4x3_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,16                   // vbroadcastss  (%rax),%ymm2
+  .byte  196,226,125,24,88,16                // vbroadcastss  0x10(%rax),%ymm3
+  .byte  196,98,125,24,64,32                 // vbroadcastss  0x20(%rax),%ymm8
+  .byte  197,228,89,217                      // vmulps        %ymm1,%ymm3,%ymm3
+  .byte  196,193,100,88,216                  // vaddps        %ymm8,%ymm3,%ymm3
+  .byte  197,236,89,208                      // vmulps        %ymm0,%ymm2,%ymm2
+  .byte  197,108,88,195                      // vaddps        %ymm3,%ymm2,%ymm8
+  .byte  196,226,125,24,80,4                 // vbroadcastss  0x4(%rax),%ymm2
+  .byte  196,226,125,24,88,20                // vbroadcastss  0x14(%rax),%ymm3
+  .byte  196,98,125,24,72,36                 // vbroadcastss  0x24(%rax),%ymm9
+  .byte  197,228,89,217                      // vmulps        %ymm1,%ymm3,%ymm3
+  .byte  196,193,100,88,217                  // vaddps        %ymm9,%ymm3,%ymm3
+  .byte  197,236,89,208                      // vmulps        %ymm0,%ymm2,%ymm2
+  .byte  197,108,88,203                      // vaddps        %ymm3,%ymm2,%ymm9
+  .byte  196,226,125,24,80,8                 // vbroadcastss  0x8(%rax),%ymm2
+  .byte  196,226,125,24,88,24                // vbroadcastss  0x18(%rax),%ymm3
+  .byte  196,98,125,24,80,40                 // vbroadcastss  0x28(%rax),%ymm10
+  .byte  197,228,89,217                      // vmulps        %ymm1,%ymm3,%ymm3
+  .byte  196,193,100,88,218                  // vaddps        %ymm10,%ymm3,%ymm3
+  .byte  197,236,89,208                      // vmulps        %ymm0,%ymm2,%ymm2
+  .byte  197,236,88,211                      // vaddps        %ymm3,%ymm2,%ymm2
+  .byte  196,226,125,24,88,12                // vbroadcastss  0xc(%rax),%ymm3
+  .byte  196,98,125,24,80,28                 // vbroadcastss  0x1c(%rax),%ymm10
+  .byte  196,98,125,24,88,44                 // vbroadcastss  0x2c(%rax),%ymm11
+  .byte  197,172,89,201                      // vmulps        %ymm1,%ymm10,%ymm1
+  .byte  196,193,116,88,203                  // vaddps        %ymm11,%ymm1,%ymm1
+  .byte  197,228,89,192                      // vmulps        %ymm0,%ymm3,%ymm0
+  .byte  197,252,88,217                      // vaddps        %ymm1,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,124,41,201                      // vmovaps       %ymm9,%ymm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_perspective_avx
+.globl _sk_matrix_perspective_avx
+FUNCTION(_sk_matrix_perspective_avx)
+_sk_matrix_perspective_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,0                     // vbroadcastss  (%rax),%ymm8
+  .byte  196,98,125,24,72,4                  // vbroadcastss  0x4(%rax),%ymm9
+  .byte  196,98,125,24,80,8                  // vbroadcastss  0x8(%rax),%ymm10
+  .byte  197,52,89,201                       // vmulps        %ymm1,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  197,60,89,192                       // vmulps        %ymm0,%ymm8,%ymm8
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,72,12                 // vbroadcastss  0xc(%rax),%ymm9
+  .byte  196,98,125,24,80,16                 // vbroadcastss  0x10(%rax),%ymm10
+  .byte  196,98,125,24,88,20                 // vbroadcastss  0x14(%rax),%ymm11
+  .byte  197,44,89,209                       // vmulps        %ymm1,%ymm10,%ymm10
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  197,52,89,200                       // vmulps        %ymm0,%ymm9,%ymm9
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,80,24                 // vbroadcastss  0x18(%rax),%ymm10
+  .byte  196,98,125,24,88,28                 // vbroadcastss  0x1c(%rax),%ymm11
+  .byte  196,98,125,24,96,32                 // vbroadcastss  0x20(%rax),%ymm12
+  .byte  197,164,89,201                      // vmulps        %ymm1,%ymm11,%ymm1
+  .byte  196,193,116,88,204                  // vaddps        %ymm12,%ymm1,%ymm1
+  .byte  197,172,89,192                      // vmulps        %ymm0,%ymm10,%ymm0
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,252,83,200                      // vrcpps        %ymm0,%ymm1
+  .byte  197,188,89,193                      // vmulps        %ymm1,%ymm8,%ymm0
+  .byte  197,180,89,201                      // vmulps        %ymm1,%ymm9,%ymm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_gradient_avx
+.globl _sk_evenly_spaced_gradient_avx
+FUNCTION(_sk_evenly_spaced_gradient_avx)
+_sk_evenly_spaced_gradient_avx:
+  .byte  85                                  // push          %rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  197,252,17,124,36,216               // vmovups       %ymm7,-0x28(%rsp)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,24                           // mov           (%rax),%rbx
+  .byte  72,139,104,8                        // mov           0x8(%rax),%rbp
+  .byte  72,255,203                          // dec           %rbx
+  .byte  120,7                               // js            5777 <_sk_evenly_spaced_gradient_avx+0x25>
+  .byte  196,225,242,42,203                  // vcvtsi2ss     %rbx,%xmm1,%xmm1
+  .byte  235,21                              // jmp           578c <_sk_evenly_spaced_gradient_avx+0x3a>
+  .byte  73,137,217                          // mov           %rbx,%r9
+  .byte  73,209,233                          // shr           %r9
+  .byte  131,227,1                           // and           $0x1,%ebx
+  .byte  76,9,203                            // or            %r9,%rbx
+  .byte  196,225,242,42,203                  // vcvtsi2ss     %rbx,%xmm1,%xmm1
+  .byte  197,242,88,201                      // vaddss        %xmm1,%xmm1,%xmm1
+  .byte  196,227,121,4,201,0                 // vpermilps     $0x0,%xmm1,%xmm1
+  .byte  196,227,117,24,201,1                // vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
+  .byte  197,244,89,200                      // vmulps        %ymm0,%ymm1,%ymm1
+  .byte  197,254,91,201                      // vcvttps2dq    %ymm1,%ymm1
+  .byte  196,195,249,22,201,1                // vpextrq       $0x1,%xmm1,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,193,249,126,203                 // vmovq         %xmm1,%r11
+  .byte  69,137,222                          // mov           %r11d,%r14d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,195,249,22,204,1                // vpextrq       $0x1,%xmm1,%r12
+  .byte  69,137,231                          // mov           %r12d,%r15d
+  .byte  73,193,236,32                       // shr           $0x20,%r12
+  .byte  196,225,249,126,203                 // vmovq         %xmm1,%rbx
+  .byte  65,137,221                          // mov           %ebx,%r13d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  196,161,122,16,76,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm1
+  .byte  196,227,113,33,76,157,0,16          // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  .byte  196,161,122,16,84,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm2
+  .byte  196,33,122,16,68,165,0              // vmovss        0x0(%rbp,%r12,4),%xmm8
+  .byte  196,161,122,16,92,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm3
+  .byte  196,35,97,33,76,157,0,16            // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm9
+  .byte  196,161,122,16,124,149,0            // vmovss        0x0(%rbp,%r10,4),%xmm7
+  .byte  196,33,122,16,92,141,0              // vmovss        0x0(%rbp,%r9,4),%xmm11
+  .byte  196,99,113,33,226,32                // vinsertps     $0x20,%xmm2,%xmm1,%xmm12
+  .byte  72,139,104,40                       // mov           0x28(%rax),%rbp
+  .byte  196,161,122,16,84,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm2
+  .byte  196,99,105,33,108,157,0,16          // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm13
+  .byte  196,161,122,16,92,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm3
+  .byte  196,161,122,16,76,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm1
+  .byte  196,161,122,16,84,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm2
+  .byte  196,35,105,33,116,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm14
+  .byte  196,33,122,16,124,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm15
+  .byte  196,33,122,16,84,141,0              // vmovss        0x0(%rbp,%r9,4),%xmm10
+  .byte  196,67,25,33,192,48                 // vinsertps     $0x30,%xmm8,%xmm12,%xmm8
+  .byte  196,227,49,33,215,32                // vinsertps     $0x20,%xmm7,%xmm9,%xmm2
+  .byte  196,195,105,33,211,48               // vinsertps     $0x30,%xmm11,%xmm2,%xmm2
+  .byte  196,67,109,24,192,1                 // vinsertf128   $0x1,%xmm8,%ymm2,%ymm8
+  .byte  196,227,17,33,211,32                // vinsertps     $0x20,%xmm3,%xmm13,%xmm2
+  .byte  196,99,105,33,201,48                // vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  .byte  72,139,104,16                       // mov           0x10(%rax),%rbp
+  .byte  196,161,122,16,84,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm2
+  .byte  196,99,105,33,92,157,0,16           // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm11
+  .byte  196,33,122,16,100,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm12
+  .byte  196,161,122,16,76,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm1
+  .byte  196,161,122,16,124,181,0            // vmovss        0x0(%rbp,%r14,4),%xmm7
+  .byte  196,163,65,33,124,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  .byte  196,161,122,16,92,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm3
+  .byte  196,33,122,16,108,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm13
+  .byte  196,195,9,33,215,32                 // vinsertps     $0x20,%xmm15,%xmm14,%xmm2
+  .byte  196,195,105,33,210,48               // vinsertps     $0x30,%xmm10,%xmm2,%xmm2
+  .byte  196,67,109,24,241,1                 // vinsertf128   $0x1,%xmm9,%ymm2,%ymm14
+  .byte  196,195,33,33,212,32                // vinsertps     $0x20,%xmm12,%xmm11,%xmm2
+  .byte  196,99,105,33,201,48                // vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  .byte  196,99,65,33,211,32                 // vinsertps     $0x20,%xmm3,%xmm7,%xmm10
+  .byte  72,139,104,48                       // mov           0x30(%rax),%rbp
+  .byte  196,161,122,16,92,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm3
+  .byte  196,99,97,33,92,157,0,16            // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  .byte  196,33,122,16,124,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm15
+  .byte  196,33,122,16,100,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm12
+  .byte  196,161,122,16,84,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm2
+  .byte  196,163,105,33,84,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  .byte  196,161,122,16,124,149,0            // vmovss        0x0(%rbp,%r10,4),%xmm7
+  .byte  196,161,122,16,92,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm3
+  .byte  196,67,41,33,213,48                 // vinsertps     $0x30,%xmm13,%xmm10,%xmm10
+  .byte  196,67,45,24,233,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm13
+  .byte  196,195,33,33,207,32                // vinsertps     $0x20,%xmm15,%xmm11,%xmm1
+  .byte  196,67,113,33,204,48                // vinsertps     $0x30,%xmm12,%xmm1,%xmm9
+  .byte  196,227,105,33,215,32               // vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  .byte  196,99,105,33,211,48                // vinsertps     $0x30,%xmm3,%xmm2,%xmm10
+  .byte  72,139,104,24                       // mov           0x18(%rax),%rbp
+  .byte  196,161,122,16,92,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm3
+  .byte  196,99,97,33,92,157,0,16            // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  .byte  196,33,122,16,100,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm12
+  .byte  196,33,122,16,124,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm15
+  .byte  196,161,122,16,84,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm2
+  .byte  196,163,105,33,84,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  .byte  196,161,122,16,92,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm3
+  .byte  196,161,122,16,124,141,0            // vmovss        0x0(%rbp,%r9,4),%xmm7
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,195,33,33,204,32                // vinsertps     $0x20,%xmm12,%xmm11,%xmm1
+  .byte  196,195,113,33,207,48               // vinsertps     $0x30,%xmm15,%xmm1,%xmm1
+  .byte  196,227,105,33,211,32               // vinsertps     $0x20,%xmm3,%xmm2,%xmm2
+  .byte  196,227,105,33,215,48               // vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  .byte  196,99,109,24,209,1                 // vinsertf128   $0x1,%xmm1,%ymm2,%ymm10
+  .byte  72,139,104,56                       // mov           0x38(%rax),%rbp
+  .byte  196,161,122,16,76,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm1
+  .byte  196,99,113,33,92,157,0,16           // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm11
+  .byte  196,33,122,16,100,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm12
+  .byte  196,33,122,16,124,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm15
+  .byte  196,161,122,16,124,181,0            // vmovss        0x0(%rbp,%r14,4),%xmm7
+  .byte  196,163,65,33,124,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  .byte  196,161,122,16,76,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm1
+  .byte  196,161,122,16,84,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm2
+  .byte  196,195,33,33,220,32                // vinsertps     $0x20,%xmm12,%xmm11,%xmm3
+  .byte  196,195,97,33,223,48                // vinsertps     $0x30,%xmm15,%xmm3,%xmm3
+  .byte  196,227,65,33,201,32                // vinsertps     $0x20,%xmm1,%xmm7,%xmm1
+  .byte  196,227,113,33,202,48               // vinsertps     $0x30,%xmm2,%xmm1,%xmm1
+  .byte  196,99,117,24,219,1                 // vinsertf128   $0x1,%xmm3,%ymm1,%ymm11
+  .byte  72,139,104,32                       // mov           0x20(%rax),%rbp
+  .byte  196,161,122,16,76,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm1
+  .byte  196,227,113,33,76,157,0,16          // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  .byte  196,161,122,16,84,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm2
+  .byte  196,227,113,33,202,32               // vinsertps     $0x20,%xmm2,%xmm1,%xmm1
+  .byte  196,161,122,16,84,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm2
+  .byte  196,161,122,16,92,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm3
+  .byte  196,35,97,33,100,157,0,16           // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm12
+  .byte  196,161,122,16,124,149,0            // vmovss        0x0(%rbp,%r10,4),%xmm7
+  .byte  196,161,122,16,92,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm3
+  .byte  196,99,113,33,250,48                // vinsertps     $0x30,%xmm2,%xmm1,%xmm15
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  196,161,122,16,20,168               // vmovss        (%rax,%r13,4),%xmm2
+  .byte  196,227,105,33,20,152,16            // vinsertps     $0x10,(%rax,%rbx,4),%xmm2,%xmm2
+  .byte  196,227,25,33,255,32                // vinsertps     $0x20,%xmm7,%xmm12,%xmm7
+  .byte  196,161,122,16,12,184               // vmovss        (%rax,%r15,4),%xmm1
+  .byte  196,227,65,33,219,48                // vinsertps     $0x30,%xmm3,%xmm7,%xmm3
+  .byte  196,161,122,16,60,160               // vmovss        (%rax,%r12,4),%xmm7
+  .byte  196,67,101,24,231,1                 // vinsertf128   $0x1,%xmm15,%ymm3,%ymm12
+  .byte  196,161,122,16,28,176               // vmovss        (%rax,%r14,4),%xmm3
+  .byte  196,163,97,33,28,152,16             // vinsertps     $0x10,(%rax,%r11,4),%xmm3,%xmm3
+  .byte  196,227,105,33,201,32               // vinsertps     $0x20,%xmm1,%xmm2,%xmm1
+  .byte  196,161,122,16,20,144               // vmovss        (%rax,%r10,4),%xmm2
+  .byte  196,227,113,33,207,48               // vinsertps     $0x30,%xmm7,%xmm1,%xmm1
+  .byte  196,161,122,16,60,136               // vmovss        (%rax,%r9,4),%xmm7
+  .byte  196,227,97,33,210,32                // vinsertps     $0x20,%xmm2,%xmm3,%xmm2
+  .byte  196,227,105,33,215,48               // vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  .byte  196,227,109,24,217,1                // vinsertf128   $0x1,%xmm1,%ymm2,%ymm3
+  .byte  197,188,89,200                      // vmulps        %ymm0,%ymm8,%ymm1
+  .byte  196,65,116,88,198                   // vaddps        %ymm14,%ymm1,%ymm8
+  .byte  197,148,89,200                      // vmulps        %ymm0,%ymm13,%ymm1
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  197,172,89,208                      // vmulps        %ymm0,%ymm10,%ymm2
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  197,156,89,192                      // vmulps        %ymm0,%ymm12,%ymm0
+  .byte  197,252,88,219                      // vaddps        %ymm3,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,252,16,124,36,216               // vmovups       -0x28(%rsp),%ymm7
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gauss_a_to_rgba_avx
+.globl _sk_gauss_a_to_rgba_avx
+FUNCTION(_sk_gauss_a_to_rgba_avx)
+_sk_gauss_a_to_rgba_avx:
+  .byte  196,226,125,24,5,168,13,0,0         // vbroadcastss  0xda8(%rip),%ymm0        # 6898 <_sk_callback_avx+0x49c>
+  .byte  197,228,89,192                      // vmulps        %ymm0,%ymm3,%ymm0
+  .byte  196,226,125,24,13,159,13,0,0        // vbroadcastss  0xd9f(%rip),%ymm1        # 689c <_sk_callback_avx+0x4a0>
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  196,226,125,24,13,146,13,0,0        // vbroadcastss  0xd92(%rip),%ymm1        # 68a0 <_sk_callback_avx+0x4a4>
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  196,226,125,24,13,133,13,0,0        // vbroadcastss  0xd85(%rip),%ymm1        # 68a4 <_sk_callback_avx+0x4a8>
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  197,252,89,195                      // vmulps        %ymm3,%ymm0,%ymm0
+  .byte  196,226,125,24,13,120,13,0,0        // vbroadcastss  0xd78(%rip),%ymm1        # 68a8 <_sk_callback_avx+0x4ac>
+  .byte  197,252,88,193                      // vaddps        %ymm1,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,252,40,200                      // vmovaps       %ymm0,%ymm1
+  .byte  197,252,40,208                      // vmovaps       %ymm0,%ymm2
+  .byte  197,252,40,216                      // vmovaps       %ymm0,%ymm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gradient_avx
+.globl _sk_gradient_avx
+FUNCTION(_sk_gradient_avx)
+_sk_gradient_avx:
+  .byte  85                                  // push          %rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  197,252,17,124,36,216               // vmovups       %ymm7,-0x28(%rsp)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  73,131,249,2                        // cmp           $0x2,%r9
+  .byte  114,80                              // jb            5bb3 <_sk_gradient_avx+0x6f>
+  .byte  72,139,88,72                        // mov           0x48(%rax),%rbx
+  .byte  73,255,201                          // dec           %r9
+  .byte  72,131,195,4                        // add           $0x4,%rbx
+  .byte  196,65,52,87,201                    // vxorps        %ymm9,%ymm9,%ymm9
+  .byte  196,98,125,24,21,48,13,0,0          // vbroadcastss  0xd30(%rip),%ymm10        # 68ac <_sk_callback_avx+0x4b0>
+  .byte  197,244,87,201                      // vxorps        %ymm1,%ymm1,%ymm1
+  .byte  196,98,125,24,3                     // vbroadcastss  (%rbx),%ymm8
+  .byte  197,60,194,192,2                    // vcmpleps      %ymm0,%ymm8,%ymm8
+  .byte  196,67,53,74,194,128                // vblendvps     %ymm8,%ymm10,%ymm9,%ymm8
+  .byte  196,99,125,25,194,1                 // vextractf128  $0x1,%ymm8,%xmm2
+  .byte  196,227,125,25,203,1                // vextractf128  $0x1,%ymm1,%xmm3
+  .byte  197,233,254,211                     // vpaddd        %xmm3,%xmm2,%xmm2
+  .byte  197,185,254,201                     // vpaddd        %xmm1,%xmm8,%xmm1
+  .byte  196,227,117,24,202,1                // vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  .byte  72,131,195,4                        // add           $0x4,%rbx
+  .byte  73,255,201                          // dec           %r9
+  .byte  117,205                             // jne           5b80 <_sk_gradient_avx+0x3c>
+  .byte  196,195,249,22,201,1                // vpextrq       $0x1,%xmm1,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  196,193,249,126,203                 // vmovq         %xmm1,%r11
+  .byte  69,137,222                          // mov           %r11d,%r14d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  196,227,125,25,201,1                // vextractf128  $0x1,%ymm1,%xmm1
+  .byte  196,195,249,22,204,1                // vpextrq       $0x1,%xmm1,%r12
+  .byte  69,137,231                          // mov           %r12d,%r15d
+  .byte  73,193,236,32                       // shr           $0x20,%r12
+  .byte  196,225,249,126,203                 // vmovq         %xmm1,%rbx
+  .byte  65,137,221                          // mov           %ebx,%r13d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  72,139,104,8                        // mov           0x8(%rax),%rbp
+  .byte  196,161,122,16,76,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm1
+  .byte  196,227,113,33,76,157,0,16          // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  .byte  196,161,122,16,84,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm2
+  .byte  196,33,122,16,68,165,0              // vmovss        0x0(%rbp,%r12,4),%xmm8
+  .byte  196,161,122,16,92,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm3
+  .byte  196,35,97,33,76,157,0,16            // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm9
+  .byte  196,161,122,16,124,149,0            // vmovss        0x0(%rbp,%r10,4),%xmm7
+  .byte  196,33,122,16,92,141,0              // vmovss        0x0(%rbp,%r9,4),%xmm11
+  .byte  196,99,113,33,226,32                // vinsertps     $0x20,%xmm2,%xmm1,%xmm12
+  .byte  72,139,104,40                       // mov           0x28(%rax),%rbp
+  .byte  196,161,122,16,84,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm2
+  .byte  196,99,105,33,108,157,0,16          // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm13
+  .byte  196,161,122,16,92,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm3
+  .byte  196,161,122,16,76,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm1
+  .byte  196,161,122,16,84,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm2
+  .byte  196,35,105,33,116,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm14
+  .byte  196,33,122,16,124,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm15
+  .byte  196,33,122,16,84,141,0              // vmovss        0x0(%rbp,%r9,4),%xmm10
+  .byte  196,67,25,33,192,48                 // vinsertps     $0x30,%xmm8,%xmm12,%xmm8
+  .byte  196,227,49,33,215,32                // vinsertps     $0x20,%xmm7,%xmm9,%xmm2
+  .byte  196,195,105,33,211,48               // vinsertps     $0x30,%xmm11,%xmm2,%xmm2
+  .byte  196,67,109,24,192,1                 // vinsertf128   $0x1,%xmm8,%ymm2,%ymm8
+  .byte  196,227,17,33,211,32                // vinsertps     $0x20,%xmm3,%xmm13,%xmm2
+  .byte  196,99,105,33,201,48                // vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  .byte  72,139,104,16                       // mov           0x10(%rax),%rbp
+  .byte  196,161,122,16,84,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm2
+  .byte  196,99,105,33,92,157,0,16           // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm11
+  .byte  196,33,122,16,100,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm12
+  .byte  196,161,122,16,76,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm1
+  .byte  196,161,122,16,124,181,0            // vmovss        0x0(%rbp,%r14,4),%xmm7
+  .byte  196,163,65,33,124,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  .byte  196,161,122,16,92,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm3
+  .byte  196,33,122,16,108,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm13
+  .byte  196,195,9,33,215,32                 // vinsertps     $0x20,%xmm15,%xmm14,%xmm2
+  .byte  196,195,105,33,210,48               // vinsertps     $0x30,%xmm10,%xmm2,%xmm2
+  .byte  196,67,109,24,241,1                 // vinsertf128   $0x1,%xmm9,%ymm2,%ymm14
+  .byte  196,195,33,33,212,32                // vinsertps     $0x20,%xmm12,%xmm11,%xmm2
+  .byte  196,99,105,33,201,48                // vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  .byte  196,99,65,33,211,32                 // vinsertps     $0x20,%xmm3,%xmm7,%xmm10
+  .byte  72,139,104,48                       // mov           0x30(%rax),%rbp
+  .byte  196,161,122,16,92,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm3
+  .byte  196,99,97,33,92,157,0,16            // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  .byte  196,33,122,16,124,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm15
+  .byte  196,33,122,16,100,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm12
+  .byte  196,161,122,16,84,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm2
+  .byte  196,163,105,33,84,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  .byte  196,161,122,16,124,149,0            // vmovss        0x0(%rbp,%r10,4),%xmm7
+  .byte  196,161,122,16,92,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm3
+  .byte  196,67,41,33,213,48                 // vinsertps     $0x30,%xmm13,%xmm10,%xmm10
+  .byte  196,67,45,24,233,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm13
+  .byte  196,195,33,33,207,32                // vinsertps     $0x20,%xmm15,%xmm11,%xmm1
+  .byte  196,67,113,33,204,48                // vinsertps     $0x30,%xmm12,%xmm1,%xmm9
+  .byte  196,227,105,33,215,32               // vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  .byte  196,99,105,33,211,48                // vinsertps     $0x30,%xmm3,%xmm2,%xmm10
+  .byte  72,139,104,24                       // mov           0x18(%rax),%rbp
+  .byte  196,161,122,16,92,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm3
+  .byte  196,99,97,33,92,157,0,16            // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  .byte  196,33,122,16,100,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm12
+  .byte  196,33,122,16,124,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm15
+  .byte  196,161,122,16,84,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm2
+  .byte  196,163,105,33,84,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  .byte  196,161,122,16,92,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm3
+  .byte  196,161,122,16,124,141,0            // vmovss        0x0(%rbp,%r9,4),%xmm7
+  .byte  196,67,45,24,201,1                  // vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  .byte  196,195,33,33,204,32                // vinsertps     $0x20,%xmm12,%xmm11,%xmm1
+  .byte  196,195,113,33,207,48               // vinsertps     $0x30,%xmm15,%xmm1,%xmm1
+  .byte  196,227,105,33,211,32               // vinsertps     $0x20,%xmm3,%xmm2,%xmm2
+  .byte  196,227,105,33,215,48               // vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  .byte  196,99,109,24,209,1                 // vinsertf128   $0x1,%xmm1,%ymm2,%ymm10
+  .byte  72,139,104,56                       // mov           0x38(%rax),%rbp
+  .byte  196,161,122,16,76,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm1
+  .byte  196,99,113,33,92,157,0,16           // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm11
+  .byte  196,33,122,16,100,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm12
+  .byte  196,33,122,16,124,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm15
+  .byte  196,161,122,16,124,181,0            // vmovss        0x0(%rbp,%r14,4),%xmm7
+  .byte  196,163,65,33,124,157,0,16          // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  .byte  196,161,122,16,76,149,0             // vmovss        0x0(%rbp,%r10,4),%xmm1
+  .byte  196,161,122,16,84,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm2
+  .byte  196,195,33,33,220,32                // vinsertps     $0x20,%xmm12,%xmm11,%xmm3
+  .byte  196,195,97,33,223,48                // vinsertps     $0x30,%xmm15,%xmm3,%xmm3
+  .byte  196,227,65,33,201,32                // vinsertps     $0x20,%xmm1,%xmm7,%xmm1
+  .byte  196,227,113,33,202,48               // vinsertps     $0x30,%xmm2,%xmm1,%xmm1
+  .byte  196,99,117,24,219,1                 // vinsertf128   $0x1,%xmm3,%ymm1,%ymm11
+  .byte  72,139,104,32                       // mov           0x20(%rax),%rbp
+  .byte  196,161,122,16,76,173,0             // vmovss        0x0(%rbp,%r13,4),%xmm1
+  .byte  196,227,113,33,76,157,0,16          // vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  .byte  196,161,122,16,84,189,0             // vmovss        0x0(%rbp,%r15,4),%xmm2
+  .byte  196,227,113,33,202,32               // vinsertps     $0x20,%xmm2,%xmm1,%xmm1
+  .byte  196,161,122,16,84,165,0             // vmovss        0x0(%rbp,%r12,4),%xmm2
+  .byte  196,161,122,16,92,181,0             // vmovss        0x0(%rbp,%r14,4),%xmm3
+  .byte  196,35,97,33,100,157,0,16           // vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm12
+  .byte  196,161,122,16,124,149,0            // vmovss        0x0(%rbp,%r10,4),%xmm7
+  .byte  196,161,122,16,92,141,0             // vmovss        0x0(%rbp,%r9,4),%xmm3
+  .byte  196,99,113,33,250,48                // vinsertps     $0x30,%xmm2,%xmm1,%xmm15
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  196,161,122,16,20,168               // vmovss        (%rax,%r13,4),%xmm2
+  .byte  196,227,105,33,20,152,16            // vinsertps     $0x10,(%rax,%rbx,4),%xmm2,%xmm2
+  .byte  196,227,25,33,255,32                // vinsertps     $0x20,%xmm7,%xmm12,%xmm7
+  .byte  196,161,122,16,12,184               // vmovss        (%rax,%r15,4),%xmm1
+  .byte  196,227,65,33,219,48                // vinsertps     $0x30,%xmm3,%xmm7,%xmm3
+  .byte  196,161,122,16,60,160               // vmovss        (%rax,%r12,4),%xmm7
+  .byte  196,67,101,24,231,1                 // vinsertf128   $0x1,%xmm15,%ymm3,%ymm12
+  .byte  196,161,122,16,28,176               // vmovss        (%rax,%r14,4),%xmm3
+  .byte  196,163,97,33,28,152,16             // vinsertps     $0x10,(%rax,%r11,4),%xmm3,%xmm3
+  .byte  196,227,105,33,201,32               // vinsertps     $0x20,%xmm1,%xmm2,%xmm1
+  .byte  196,161,122,16,20,144               // vmovss        (%rax,%r10,4),%xmm2
+  .byte  196,227,113,33,207,48               // vinsertps     $0x30,%xmm7,%xmm1,%xmm1
+  .byte  196,161,122,16,60,136               // vmovss        (%rax,%r9,4),%xmm7
+  .byte  196,227,97,33,210,32                // vinsertps     $0x20,%xmm2,%xmm3,%xmm2
+  .byte  196,227,105,33,215,48               // vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  .byte  196,227,109,24,217,1                // vinsertf128   $0x1,%xmm1,%ymm2,%ymm3
+  .byte  197,188,89,200                      // vmulps        %ymm0,%ymm8,%ymm1
+  .byte  196,65,116,88,198                   // vaddps        %ymm14,%ymm1,%ymm8
+  .byte  197,148,89,200                      // vmulps        %ymm0,%ymm13,%ymm1
+  .byte  196,193,116,88,201                  // vaddps        %ymm9,%ymm1,%ymm1
+  .byte  197,172,89,208                      // vmulps        %ymm0,%ymm10,%ymm2
+  .byte  196,193,108,88,211                  // vaddps        %ymm11,%ymm2,%ymm2
+  .byte  197,156,89,192                      // vmulps        %ymm0,%ymm12,%ymm0
+  .byte  197,252,88,219                      // vaddps        %ymm3,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  197,252,16,124,36,216               // vmovups       -0x28(%rsp),%ymm7
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_2_stop_gradient_avx
+.globl _sk_evenly_spaced_2_stop_gradient_avx
+FUNCTION(_sk_evenly_spaced_2_stop_gradient_avx)
+_sk_evenly_spaced_2_stop_gradient_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,8                    // vbroadcastss  (%rax),%ymm1
+  .byte  196,226,125,24,80,16                // vbroadcastss  0x10(%rax),%ymm2
+  .byte  197,244,89,200                      // vmulps        %ymm0,%ymm1,%ymm1
+  .byte  197,116,88,194                      // vaddps        %ymm2,%ymm1,%ymm8
+  .byte  196,226,125,24,72,4                 // vbroadcastss  0x4(%rax),%ymm1
+  .byte  196,226,125,24,80,20                // vbroadcastss  0x14(%rax),%ymm2
+  .byte  197,244,89,200                      // vmulps        %ymm0,%ymm1,%ymm1
+  .byte  197,244,88,202                      // vaddps        %ymm2,%ymm1,%ymm1
+  .byte  196,226,125,24,80,8                 // vbroadcastss  0x8(%rax),%ymm2
+  .byte  196,226,125,24,88,24                // vbroadcastss  0x18(%rax),%ymm3
+  .byte  197,236,89,208                      // vmulps        %ymm0,%ymm2,%ymm2
+  .byte  197,236,88,211                      // vaddps        %ymm3,%ymm2,%ymm2
+  .byte  196,226,125,24,88,12                // vbroadcastss  0xc(%rax),%ymm3
+  .byte  196,98,125,24,72,28                 // vbroadcastss  0x1c(%rax),%ymm9
+  .byte  197,228,89,192                      // vmulps        %ymm0,%ymm3,%ymm0
+  .byte  196,193,124,88,217                  // vaddps        %ymm9,%ymm0,%ymm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,41,192                      // vmovaps       %ymm8,%ymm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_unit_angle_avx
+.globl _sk_xy_to_unit_angle_avx
+FUNCTION(_sk_xy_to_unit_angle_avx)
+_sk_xy_to_unit_angle_avx:
+  .byte  196,65,60,87,192                    // vxorps        %ymm8,%ymm8,%ymm8
+  .byte  197,60,92,200                       // vsubps        %ymm0,%ymm8,%ymm9
+  .byte  197,52,84,200                       // vandps        %ymm0,%ymm9,%ymm9
+  .byte  197,60,92,209                       // vsubps        %ymm1,%ymm8,%ymm10
+  .byte  197,44,84,209                       // vandps        %ymm1,%ymm10,%ymm10
+  .byte  196,65,52,93,218                    // vminps        %ymm10,%ymm9,%ymm11
+  .byte  196,65,52,95,226                    // vmaxps        %ymm10,%ymm9,%ymm12
+  .byte  196,65,36,94,220                    // vdivps        %ymm12,%ymm11,%ymm11
+  .byte  196,65,36,89,227                    // vmulps        %ymm11,%ymm11,%ymm12
+  .byte  196,98,125,24,45,38,9,0,0           // vbroadcastss  0x926(%rip),%ymm13        # 68b0 <_sk_callback_avx+0x4b4>
+  .byte  196,65,28,89,237                    // vmulps        %ymm13,%ymm12,%ymm13
+  .byte  196,98,125,24,53,28,9,0,0           // vbroadcastss  0x91c(%rip),%ymm14        # 68b4 <_sk_callback_avx+0x4b8>
+  .byte  196,65,20,88,238                    // vaddps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,28,89,237                    // vmulps        %ymm13,%ymm12,%ymm13
+  .byte  196,98,125,24,53,13,9,0,0           // vbroadcastss  0x90d(%rip),%ymm14        # 68b8 <_sk_callback_avx+0x4bc>
+  .byte  196,65,20,88,238                    // vaddps        %ymm14,%ymm13,%ymm13
+  .byte  196,65,28,89,229                    // vmulps        %ymm13,%ymm12,%ymm12
+  .byte  196,98,125,24,45,254,8,0,0          // vbroadcastss  0x8fe(%rip),%ymm13        # 68bc <_sk_callback_avx+0x4c0>
+  .byte  196,65,28,88,229                    // vaddps        %ymm13,%ymm12,%ymm12
+  .byte  196,65,36,89,220                    // vmulps        %ymm12,%ymm11,%ymm11
+  .byte  196,65,52,194,202,1                 // vcmpltps      %ymm10,%ymm9,%ymm9
+  .byte  196,98,125,24,21,233,8,0,0          // vbroadcastss  0x8e9(%rip),%ymm10        # 68c0 <_sk_callback_avx+0x4c4>
+  .byte  196,65,44,92,211                    // vsubps        %ymm11,%ymm10,%ymm10
+  .byte  196,67,37,74,202,144                // vblendvps     %ymm9,%ymm10,%ymm11,%ymm9
+  .byte  196,193,124,194,192,1               // vcmpltps      %ymm8,%ymm0,%ymm0
+  .byte  196,98,125,24,21,211,8,0,0          // vbroadcastss  0x8d3(%rip),%ymm10        # 68c4 <_sk_callback_avx+0x4c8>
+  .byte  196,65,44,92,209                    // vsubps        %ymm9,%ymm10,%ymm10
+  .byte  196,195,53,74,194,0                 // vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  .byte  196,65,116,194,200,1                // vcmpltps      %ymm8,%ymm1,%ymm9
+  .byte  196,98,125,24,21,189,8,0,0          // vbroadcastss  0x8bd(%rip),%ymm10        # 68c8 <_sk_callback_avx+0x4cc>
+  .byte  197,44,92,208                       // vsubps        %ymm0,%ymm10,%ymm10
+  .byte  196,195,125,74,194,144              // vblendvps     %ymm9,%ymm10,%ymm0,%ymm0
+  .byte  196,65,124,194,200,3                // vcmpunordps   %ymm8,%ymm0,%ymm9
+  .byte  196,195,125,74,192,144              // vblendvps     %ymm9,%ymm8,%ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_radius_avx
+.globl _sk_xy_to_radius_avx
+FUNCTION(_sk_xy_to_radius_avx)
+_sk_xy_to_radius_avx:
+  .byte  197,252,89,192                      // vmulps        %ymm0,%ymm0,%ymm0
+  .byte  197,116,89,193                      // vmulps        %ymm1,%ymm1,%ymm8
+  .byte  196,193,124,88,192                  // vaddps        %ymm8,%ymm0,%ymm0
+  .byte  197,252,81,192                      // vsqrtps       %ymm0,%ymm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_save_xy_avx
+.globl _sk_save_xy_avx
+FUNCTION(_sk_save_xy_avx)
+_sk_save_xy_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,135,8,0,0           // vbroadcastss  0x887(%rip),%ymm8        # 68cc <_sk_callback_avx+0x4d0>
+  .byte  196,65,124,88,200                   // vaddps        %ymm8,%ymm0,%ymm9
+  .byte  196,67,125,8,209,1                  // vroundps      $0x1,%ymm9,%ymm10
+  .byte  196,65,52,92,202                    // vsubps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,116,88,192                   // vaddps        %ymm8,%ymm1,%ymm8
+  .byte  196,67,125,8,208,1                  // vroundps      $0x1,%ymm8,%ymm10
+  .byte  196,65,60,92,194                    // vsubps        %ymm10,%ymm8,%ymm8
+  .byte  197,252,17,0                        // vmovups       %ymm0,(%rax)
+  .byte  197,252,17,72,32                    // vmovups       %ymm1,0x20(%rax)
+  .byte  197,124,17,72,64                    // vmovups       %ymm9,0x40(%rax)
+  .byte  197,124,17,64,96                    // vmovups       %ymm8,0x60(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_accumulate_avx
+.globl _sk_accumulate_avx
+FUNCTION(_sk_accumulate_avx)
+_sk_accumulate_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  197,124,16,128,128,0,0,0            // vmovups       0x80(%rax),%ymm8
+  .byte  197,60,89,128,160,0,0,0             // vmulps        0xa0(%rax),%ymm8,%ymm8
+  .byte  197,60,89,200                       // vmulps        %ymm0,%ymm8,%ymm9
+  .byte  197,180,88,228                      // vaddps        %ymm4,%ymm9,%ymm4
+  .byte  197,60,89,201                       // vmulps        %ymm1,%ymm8,%ymm9
+  .byte  197,180,88,237                      // vaddps        %ymm5,%ymm9,%ymm5
+  .byte  197,60,89,202                       // vmulps        %ymm2,%ymm8,%ymm9
+  .byte  197,180,88,246                      // vaddps        %ymm6,%ymm9,%ymm6
+  .byte  197,60,89,195                       // vmulps        %ymm3,%ymm8,%ymm8
+  .byte  197,188,88,255                      // vaddps        %ymm7,%ymm8,%ymm7
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_nx_avx
+.globl _sk_bilinear_nx_avx
+FUNCTION(_sk_bilinear_nx_avx)
+_sk_bilinear_nx_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,19,8,0,0           // vbroadcastss  0x813(%rip),%ymm0        # 68d0 <_sk_callback_avx+0x4d4>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  196,98,125,24,5,10,8,0,0            // vbroadcastss  0x80a(%rip),%ymm8        # 68d4 <_sk_callback_avx+0x4d8>
+  .byte  197,60,92,64,64                     // vsubps        0x40(%rax),%ymm8,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_px_avx
+.globl _sk_bilinear_px_avx
+FUNCTION(_sk_bilinear_px_avx)
+_sk_bilinear_px_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,242,7,0,0          // vbroadcastss  0x7f2(%rip),%ymm0        # 68d8 <_sk_callback_avx+0x4dc>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  197,124,16,64,64                    // vmovups       0x40(%rax),%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_ny_avx
+.globl _sk_bilinear_ny_avx
+FUNCTION(_sk_bilinear_ny_avx)
+_sk_bilinear_ny_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,214,7,0,0         // vbroadcastss  0x7d6(%rip),%ymm1        # 68dc <_sk_callback_avx+0x4e0>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  196,98,125,24,5,204,7,0,0           // vbroadcastss  0x7cc(%rip),%ymm8        # 68e0 <_sk_callback_avx+0x4e4>
+  .byte  197,60,92,64,96                     // vsubps        0x60(%rax),%ymm8,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_py_avx
+.globl _sk_bilinear_py_avx
+FUNCTION(_sk_bilinear_py_avx)
+_sk_bilinear_py_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,180,7,0,0         // vbroadcastss  0x7b4(%rip),%ymm1        # 68e4 <_sk_callback_avx+0x4e8>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  197,124,16,64,96                    // vmovups       0x60(%rax),%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3x_avx
+.globl _sk_bicubic_n3x_avx
+FUNCTION(_sk_bicubic_n3x_avx)
+_sk_bicubic_n3x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,151,7,0,0          // vbroadcastss  0x797(%rip),%ymm0        # 68e8 <_sk_callback_avx+0x4ec>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  196,98,125,24,5,142,7,0,0           // vbroadcastss  0x78e(%rip),%ymm8        # 68ec <_sk_callback_avx+0x4f0>
+  .byte  197,60,92,64,64                     // vsubps        0x40(%rax),%ymm8,%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,127,7,0,0          // vbroadcastss  0x77f(%rip),%ymm10        # 68f0 <_sk_callback_avx+0x4f4>
+  .byte  196,65,60,89,194                    // vmulps        %ymm10,%ymm8,%ymm8
+  .byte  196,98,125,24,21,117,7,0,0          // vbroadcastss  0x775(%rip),%ymm10        # 68f4 <_sk_callback_avx+0x4f8>
+  .byte  196,65,60,88,194                    // vaddps        %ymm10,%ymm8,%ymm8
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1x_avx
+.globl _sk_bicubic_n1x_avx
+FUNCTION(_sk_bicubic_n1x_avx)
+_sk_bicubic_n1x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,88,7,0,0           // vbroadcastss  0x758(%rip),%ymm0        # 68f8 <_sk_callback_avx+0x4fc>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  196,98,125,24,5,79,7,0,0            // vbroadcastss  0x74f(%rip),%ymm8        # 68fc <_sk_callback_avx+0x500>
+  .byte  197,60,92,64,64                     // vsubps        0x40(%rax),%ymm8,%ymm8
+  .byte  196,98,125,24,13,69,7,0,0           // vbroadcastss  0x745(%rip),%ymm9        # 6900 <_sk_callback_avx+0x504>
+  .byte  196,65,60,89,201                    // vmulps        %ymm9,%ymm8,%ymm9
+  .byte  196,98,125,24,21,59,7,0,0           // vbroadcastss  0x73b(%rip),%ymm10        # 6904 <_sk_callback_avx+0x508>
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,60,89,201                    // vmulps        %ymm9,%ymm8,%ymm9
+  .byte  196,98,125,24,21,44,7,0,0           // vbroadcastss  0x72c(%rip),%ymm10        # 6908 <_sk_callback_avx+0x50c>
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,13,29,7,0,0           // vbroadcastss  0x71d(%rip),%ymm9        # 690c <_sk_callback_avx+0x510>
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1x_avx
+.globl _sk_bicubic_p1x_avx
+FUNCTION(_sk_bicubic_p1x_avx)
+_sk_bicubic_p1x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,5,7,0,0             // vbroadcastss  0x705(%rip),%ymm8        # 6910 <_sk_callback_avx+0x514>
+  .byte  197,188,88,0                        // vaddps        (%rax),%ymm8,%ymm0
+  .byte  197,124,16,72,64                    // vmovups       0x40(%rax),%ymm9
+  .byte  196,98,125,24,21,247,6,0,0          // vbroadcastss  0x6f7(%rip),%ymm10        # 6914 <_sk_callback_avx+0x518>
+  .byte  196,65,52,89,210                    // vmulps        %ymm10,%ymm9,%ymm10
+  .byte  196,98,125,24,29,237,6,0,0          // vbroadcastss  0x6ed(%rip),%ymm11        # 6918 <_sk_callback_avx+0x51c>
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,52,89,210                    // vmulps        %ymm10,%ymm9,%ymm10
+  .byte  196,65,44,88,192                    // vaddps        %ymm8,%ymm10,%ymm8
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  196,98,125,24,13,212,6,0,0          // vbroadcastss  0x6d4(%rip),%ymm9        # 691c <_sk_callback_avx+0x520>
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3x_avx
+.globl _sk_bicubic_p3x_avx
+FUNCTION(_sk_bicubic_p3x_avx)
+_sk_bicubic_p3x_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,5,188,6,0,0          // vbroadcastss  0x6bc(%rip),%ymm0        # 6920 <_sk_callback_avx+0x524>
+  .byte  197,252,88,0                        // vaddps        (%rax),%ymm0,%ymm0
+  .byte  197,124,16,64,64                    // vmovups       0x40(%rax),%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,169,6,0,0          // vbroadcastss  0x6a9(%rip),%ymm10        # 6924 <_sk_callback_avx+0x528>
+  .byte  196,65,60,89,194                    // vmulps        %ymm10,%ymm8,%ymm8
+  .byte  196,98,125,24,21,159,6,0,0          // vbroadcastss  0x69f(%rip),%ymm10        # 6928 <_sk_callback_avx+0x52c>
+  .byte  196,65,60,88,194                    // vaddps        %ymm10,%ymm8,%ymm8
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  197,124,17,128,128,0,0,0            // vmovups       %ymm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3y_avx
+.globl _sk_bicubic_n3y_avx
+FUNCTION(_sk_bicubic_n3y_avx)
+_sk_bicubic_n3y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,130,6,0,0         // vbroadcastss  0x682(%rip),%ymm1        # 692c <_sk_callback_avx+0x530>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  196,98,125,24,5,120,6,0,0           // vbroadcastss  0x678(%rip),%ymm8        # 6930 <_sk_callback_avx+0x534>
+  .byte  197,60,92,64,96                     // vsubps        0x60(%rax),%ymm8,%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,105,6,0,0          // vbroadcastss  0x669(%rip),%ymm10        # 6934 <_sk_callback_avx+0x538>
+  .byte  196,65,60,89,194                    // vmulps        %ymm10,%ymm8,%ymm8
+  .byte  196,98,125,24,21,95,6,0,0           // vbroadcastss  0x65f(%rip),%ymm10        # 6938 <_sk_callback_avx+0x53c>
+  .byte  196,65,60,88,194                    // vaddps        %ymm10,%ymm8,%ymm8
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1y_avx
+.globl _sk_bicubic_n1y_avx
+FUNCTION(_sk_bicubic_n1y_avx)
+_sk_bicubic_n1y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,66,6,0,0          // vbroadcastss  0x642(%rip),%ymm1        # 693c <_sk_callback_avx+0x540>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  196,98,125,24,5,56,6,0,0            // vbroadcastss  0x638(%rip),%ymm8        # 6940 <_sk_callback_avx+0x544>
+  .byte  197,60,92,64,96                     // vsubps        0x60(%rax),%ymm8,%ymm8
+  .byte  196,98,125,24,13,46,6,0,0           // vbroadcastss  0x62e(%rip),%ymm9        # 6944 <_sk_callback_avx+0x548>
+  .byte  196,65,60,89,201                    // vmulps        %ymm9,%ymm8,%ymm9
+  .byte  196,98,125,24,21,36,6,0,0           // vbroadcastss  0x624(%rip),%ymm10        # 6948 <_sk_callback_avx+0x54c>
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,60,89,201                    // vmulps        %ymm9,%ymm8,%ymm9
+  .byte  196,98,125,24,21,21,6,0,0           // vbroadcastss  0x615(%rip),%ymm10        # 694c <_sk_callback_avx+0x550>
+  .byte  196,65,52,88,202                    // vaddps        %ymm10,%ymm9,%ymm9
+  .byte  196,65,60,89,193                    // vmulps        %ymm9,%ymm8,%ymm8
+  .byte  196,98,125,24,13,6,6,0,0            // vbroadcastss  0x606(%rip),%ymm9        # 6950 <_sk_callback_avx+0x554>
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1y_avx
+.globl _sk_bicubic_p1y_avx
+FUNCTION(_sk_bicubic_p1y_avx)
+_sk_bicubic_p1y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,98,125,24,5,238,5,0,0           // vbroadcastss  0x5ee(%rip),%ymm8        # 6954 <_sk_callback_avx+0x558>
+  .byte  197,188,88,72,32                    // vaddps        0x20(%rax),%ymm8,%ymm1
+  .byte  197,124,16,72,96                    // vmovups       0x60(%rax),%ymm9
+  .byte  196,98,125,24,21,223,5,0,0          // vbroadcastss  0x5df(%rip),%ymm10        # 6958 <_sk_callback_avx+0x55c>
+  .byte  196,65,52,89,210                    // vmulps        %ymm10,%ymm9,%ymm10
+  .byte  196,98,125,24,29,213,5,0,0          // vbroadcastss  0x5d5(%rip),%ymm11        # 695c <_sk_callback_avx+0x560>
+  .byte  196,65,44,88,211                    // vaddps        %ymm11,%ymm10,%ymm10
+  .byte  196,65,52,89,210                    // vmulps        %ymm10,%ymm9,%ymm10
+  .byte  196,65,44,88,192                    // vaddps        %ymm8,%ymm10,%ymm8
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  196,98,125,24,13,188,5,0,0          // vbroadcastss  0x5bc(%rip),%ymm9        # 6960 <_sk_callback_avx+0x564>
+  .byte  196,65,60,88,193                    // vaddps        %ymm9,%ymm8,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3y_avx
+.globl _sk_bicubic_p3y_avx
+FUNCTION(_sk_bicubic_p3y_avx)
+_sk_bicubic_p3y_avx:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  196,226,125,24,13,164,5,0,0         // vbroadcastss  0x5a4(%rip),%ymm1        # 6964 <_sk_callback_avx+0x568>
+  .byte  197,244,88,72,32                    // vaddps        0x20(%rax),%ymm1,%ymm1
+  .byte  197,124,16,64,96                    // vmovups       0x60(%rax),%ymm8
+  .byte  196,65,60,89,200                    // vmulps        %ymm8,%ymm8,%ymm9
+  .byte  196,98,125,24,21,144,5,0,0          // vbroadcastss  0x590(%rip),%ymm10        # 6968 <_sk_callback_avx+0x56c>
+  .byte  196,65,60,89,194                    // vmulps        %ymm10,%ymm8,%ymm8
+  .byte  196,98,125,24,21,134,5,0,0          // vbroadcastss  0x586(%rip),%ymm10        # 696c <_sk_callback_avx+0x570>
+  .byte  196,65,60,88,194                    // vaddps        %ymm10,%ymm8,%ymm8
+  .byte  196,65,52,89,192                    // vmulps        %ymm8,%ymm9,%ymm8
+  .byte  197,124,17,128,160,0,0,0            // vmovups       %ymm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_callback_avx
+.globl _sk_callback_avx
+FUNCTION(_sk_callback_avx)
+_sk_callback_avx:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  72,131,228,224                      // and           $0xffffffffffffffe0,%rsp
+  .byte  72,129,236,192,0,0,0                // sub           $0xc0,%rsp
+  .byte  197,252,41,188,36,128,0,0,0         // vmovaps       %ymm7,0x80(%rsp)
+  .byte  197,252,41,116,36,96                // vmovaps       %ymm6,0x60(%rsp)
+  .byte  197,252,41,108,36,64                // vmovaps       %ymm5,0x40(%rsp)
+  .byte  197,252,41,100,36,32                // vmovaps       %ymm4,0x20(%rsp)
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  72,137,76,36,24                     // mov           %rcx,0x18(%rsp)
+  .byte  73,137,215                          // mov           %rdx,%r15
+  .byte  73,137,252                          // mov           %rdi,%r12
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,198                          // mov           %rax,%r14
+  .byte  73,137,245                          // mov           %rsi,%r13
+  .byte  197,252,20,225                      // vunpcklps     %ymm1,%ymm0,%ymm4
+  .byte  197,252,21,193                      // vunpckhps     %ymm1,%ymm0,%ymm0
+  .byte  197,236,20,203                      // vunpcklps     %ymm3,%ymm2,%ymm1
+  .byte  197,236,21,211                      // vunpckhps     %ymm3,%ymm2,%ymm2
+  .byte  197,221,20,217                      // vunpcklpd     %ymm1,%ymm4,%ymm3
+  .byte  197,221,21,201                      // vunpckhpd     %ymm1,%ymm4,%ymm1
+  .byte  197,253,20,226                      // vunpcklpd     %ymm2,%ymm0,%ymm4
+  .byte  197,253,21,194                      // vunpckhpd     %ymm2,%ymm0,%ymm0
+  .byte  196,227,101,24,209,1                // vinsertf128   $0x1,%xmm1,%ymm3,%ymm2
+  .byte  196,227,93,24,232,1                 // vinsertf128   $0x1,%xmm0,%ymm4,%ymm5
+  .byte  196,227,101,6,201,49                // vperm2f128    $0x31,%ymm1,%ymm3,%ymm1
+  .byte  196,227,93,6,192,49                 // vperm2f128    $0x31,%ymm0,%ymm4,%ymm0
+  .byte  196,193,125,17,86,8                 // vmovupd       %ymm2,0x8(%r14)
+  .byte  196,193,125,17,110,40               // vmovupd       %ymm5,0x28(%r14)
+  .byte  196,193,125,17,78,72                // vmovupd       %ymm1,0x48(%r14)
+  .byte  196,193,125,17,70,104               // vmovupd       %ymm0,0x68(%r14)
+  .byte  72,133,219                          // test          %rbx,%rbx
+  .byte  190,8,0,0,0                         // mov           $0x8,%esi
+  .byte  15,69,243                           // cmovne        %ebx,%esi
+  .byte  76,137,247                          // mov           %r14,%rdi
+  .byte  197,248,119                         // vzeroupper
+  .byte  65,255,22                           // callq         *(%r14)
+  .byte  73,139,134,136,0,0,0                // mov           0x88(%r14),%rax
+  .byte  197,248,16,0                        // vmovups       (%rax),%xmm0
+  .byte  197,248,16,72,16                    // vmovups       0x10(%rax),%xmm1
+  .byte  197,248,16,80,32                    // vmovups       0x20(%rax),%xmm2
+  .byte  197,248,16,88,48                    // vmovups       0x30(%rax),%xmm3
+  .byte  196,227,101,24,88,112,1             // vinsertf128   $0x1,0x70(%rax),%ymm3,%ymm3
+  .byte  196,227,109,24,80,96,1              // vinsertf128   $0x1,0x60(%rax),%ymm2,%ymm2
+  .byte  196,227,117,24,72,80,1              // vinsertf128   $0x1,0x50(%rax),%ymm1,%ymm1
+  .byte  196,227,125,24,64,64,1              // vinsertf128   $0x1,0x40(%rax),%ymm0,%ymm0
+  .byte  197,252,20,225                      // vunpcklps     %ymm1,%ymm0,%ymm4
+  .byte  197,252,21,233                      // vunpckhps     %ymm1,%ymm0,%ymm5
+  .byte  197,236,20,203                      // vunpcklps     %ymm3,%ymm2,%ymm1
+  .byte  197,236,21,219                      // vunpckhps     %ymm3,%ymm2,%ymm3
+  .byte  197,221,20,193                      // vunpcklpd     %ymm1,%ymm4,%ymm0
+  .byte  197,221,21,201                      // vunpckhpd     %ymm1,%ymm4,%ymm1
+  .byte  197,213,20,211                      // vunpcklpd     %ymm3,%ymm5,%ymm2
+  .byte  197,213,21,219                      // vunpckhpd     %ymm3,%ymm5,%ymm3
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,231                          // mov           %r12,%rdi
+  .byte  76,137,250                          // mov           %r15,%rdx
+  .byte  72,139,76,36,24                     // mov           0x18(%rsp),%rcx
+  .byte  73,137,216                          // mov           %rbx,%r8
+  .byte  197,252,40,100,36,32                // vmovaps       0x20(%rsp),%ymm4
+  .byte  197,252,40,108,36,64                // vmovaps       0x40(%rsp),%ymm5
+  .byte  197,252,40,116,36,96                // vmovaps       0x60(%rsp),%ymm6
+  .byte  197,252,40,188,36,128,0,0,0         // vmovaps       0x80(%rsp),%ymm7
+  .byte  72,141,101,216                      // lea           -0x28(%rbp),%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+BALIGN4
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,1                            // cmpb          $0x1,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  252                                 // cld
+  .byte  190,0,0,128,63                      // mov           $0x3f800000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,191,0,0,224                   // add           %al,-0x1fffff41(%rax)
+  .byte  64,154                              // rex           (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 65f1 <.literal4+0xb1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 6601 <.literal4+0xc1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 6611 <.literal4+0xd1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,174                  // ds            cmp $0xae3f170a,%eax
+  .byte  71,225,61                           // rex.RXB       loope 6621 <.literal4+0xe1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,127                    // add           %al,0x7f00003f(%rax)
+  .byte  67,0,0                              // rex.XB        add %al,(%r8)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  145                                 // xchg          %eax,%ecx
+  .byte  131,158,61,154,153,153,62           // sbbl          $0x3e,-0x666665c3(%rsi)
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,97                               // rex.RXB       (bad)
+  .byte  61,82,184,78,65                     // cmp           $0x414eb852,%eax
+  .byte  57,215                              // cmp           %edx,%edi
+  .byte  32,187,186,159,98,60                // and           %bh,0x3c629fba(%rbx)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  252                                 // cld
+  .byte  191,16,62,168,177                   // mov           $0xb1a83e10,%edi
+  .byte  152                                 // cwtl
+  .byte  59,0                                // cmp           (%rax),%eax
+  .byte  0,128,63,0,0,192                    // add           %al,-0x3fffffc1(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  0,64,0                              // add           %al,0x0(%rax)
+  .byte  0,128,64,171,170,42                 // add           %al,0x2aaaab40(%rax)
+  .byte  62,0,0                              // add           %al,%ds:(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  128,64,171,170                      // addb          $0xaa,-0x55(%rax)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,129,128,128,59                  // mov           $0x3b808081,%esi
+  .byte  129,128,128,59,0,248,0,0,8,33       // addl          $0x21080000,-0x7ffc480(%rax)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        6675 <.literal4+0x135>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,129,128,128,59              // and           %eax,0x3b808081(,%rdi,1)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  129,128,128,59,129,128,128,59,0,0   // addl          $0x3b80,-0x7f7ec480(%rax)
+  .byte  0,52,255                            // add           %dh,(%rdi,%rdi,8)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            669c <.literal4+0x15c>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            6715 <.literal4+0x1d5>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,0                      // add           %al,0x3f(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            66d0 <.literal4+0x190>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            6749 <.literal4+0x209>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,0                      // add           %al,0x3f(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            6704 <.literal4+0x1c4>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            677d <.literal4+0x23d>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,0                      // add           %al,0x3f(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            6738 <.literal4+0x1f8>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            67b1 <.literal4+0x271>
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,249,68,180                   // mov           $0xb444f93f,%edi
+  .byte  62,163,233,220,63,81,140,242,66,141 // movabs        %eax,%ds:0x8d42f28c513fdce9
+  .byte  188,190,63,248,245                  // mov           $0xf5f83fbe,%esp
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,128,63,0,0,200                    // add           %al,-0x37ffffc1(%rax)
+  .byte  66,0,0                              // rex.X         add %al,(%rax)
+  .byte  127,67                              // jg            67af <.literal4+0x26f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,65,203,61                       // addb          $0x3d,-0x35(%rcx)
+  .byte  13,60,111,18,3                      // or            $0x3126f3c,%eax
+  .byte  59,10                               // cmp           (%rdx),%ecx
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  163,59,194,24,17,60,203,61,13       // movabs        %eax,0xd3dcb3c1118c23b
+  .byte  190,80,128,3,62                     // mov           $0x3e038050,%esi
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           67cf <.literal4+0x28f>
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  129,128,128,59,129,128,128,59,0,0   // addl          $0x3b80,-0x7f7ec480(%rax)
+  .byte  127,67                              // jg            67e3 <.literal4+0x2a3>
+  .byte  129,128,128,59,0,0,128,63,129,128   // addl          $0x80813f80,0x3b80(%rax)
+  .byte  128,59,0                            // cmpb          $0x0,(%rbx)
+  .byte  0,128,63,129,128,128                // add           %al,-0x7f7f7ec1(%rax)
+  .byte  59,0                                // cmp           (%rax),%eax
+  .byte  248                                 // clc
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        67c5 <.literal4+0x285>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,0,0,128,63                  // and           %eax,0x3f800000(,%rdi,1)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        67e1 <.literal4+0x2a1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,0,0,128,63                  // and           %eax,0x3f800000(,%rdi,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  124,66                              // jl            6836 <.literal4+0x2f6>
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,0,15                 // mov           %ecx,0xf003788(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,240,0                // mov           %ecx,0xf03988(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,59,15,0                 // mov           %ecx,0xf3b88(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,61,0,240                // mov           %ecx,-0xfffc278(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,0,15                 // mov           %ecx,0xf003788(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,240,0                // mov           %ecx,0xf03988(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,59,15,0                 // mov           %ecx,0xf3b88(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,61,0,0                  // mov           %ecx,0x3d88(%rax)
+  .byte  112,65                              // jo            6879 <.literal4+0x339>
+  .byte  129,128,128,59,129,128,128,59,0,0   // addl          $0x3b80,-0x7f7ec480(%rax)
+  .byte  127,67                              // jg            6887 <.literal4+0x347>
+  .byte  0,128,0,0,0,0                       // add           %al,0x0(%rax)
+  .byte  0,128,0,4,0,128                     // add           %al,-0x7ffffc00(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,128,0,0,0,0                       // add           %al,0x0(%rax)
+  .byte  0,128,0,4,0,128                     // add           %al,-0x7ffffc00(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,128,56                    // add           %al,0x38800000(%rax)
+  .byte  0,64,254                            // add           %al,-0x2(%rax)
+  .byte  255,128,0,128,55,128                // incl          -0x7fc88000(%rax)
+  .byte  0,128,55,0,0,128                    // add           %al,-0x7fffffc9(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            68c7 <.literal4+0x387>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,191,0,0,0                     // add           %al,0xbf(%rax)
+  .byte  63                                  // (bad)
+  .byte  208                                 // (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,89                               // ds            pop %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  152                                 // cwtl
+  .byte  221,147,61,45,16,17                 // fstl          0x11102d3d(%rbx)
+  .byte  192,18,120                          // rclb          $0x78,(%rdx)
+  .byte  57,64,32                            // cmp           %eax,0x20(%rax)
+  .byte  148                                 // xchg          %eax,%esp
+  .byte  90                                  // pop           %rdx
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,0,24                             // add           %bl,%ds:(%rax)
+  .byte  161,57,1,0,0,0,111,43,231           // movabs        0xe72b6f0000000139,%eax
+  .byte  187,159,215,202,60                  // mov           $0x3ccad79f,%ebx
+  .byte  212                                 // (bad)
+  .byte  100,84                              // fs            push %rsp
+  .byte  189,169,240,34,62                   // mov           $0x3e22f0a9,%ebp
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,62,0                            // cmpb          $0x0,(%rsi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,128,63                    // add           %bh,0x3f800000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,128,63                    // add           %bh,0x3f800000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,128,63,114              // sarb          $0x72,0x3f800000(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,0,0,0,191                       // mov           $0xbf000000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,85                           // cmpb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,0,0,192,63                      // mov           $0x3fc00000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,0,0,192,63                      // mov           $0x3fc00000,%edi
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  192,63,114                          // sarb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,0,0,192,191                     // mov           $0xbfc00000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,114                          // cmpb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,0,0,0,191                       // mov           $0xbf000000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,85                           // cmpb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,0,0,192,63                      // mov           $0x3fc00000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,0,0,192,63                      // mov           $0x3fc00000,%edi
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  192,63,114                          // sarb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190                                 // .byte         0xbe
+
+BALIGN16
+  .byte  0,2                                 // add           %al,(%rdx)
+  .byte  4,6                                 // add           $0x6,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,10                                // or            %cl,(%rdx)
+  .byte  12,14                               // or            $0xe,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,2                                 // add           %al,(%rdx)
+  .byte  4,6                                 // add           $0x6,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,10                                // or            %cl,(%rdx)
+  .byte  12,14                               // or            $0xe,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,2                                 // add           %al,(%rdx)
+  .byte  4,6                                 // add           $0x6,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,10                                // or            %cl,(%rdx)
+  .byte  12,14                               // or            $0xe,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,2                                 // add           %al,(%rdx)
+  .byte  4,6                                 // add           $0x6,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,10                                // or            %cl,(%rdx)
+  .byte  12,14                               // or            $0xe,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+
+BALIGN32
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+BALIGN32
+
+HIDDEN _sk_start_pipeline_sse41
+.globl _sk_start_pipeline_sse41
+FUNCTION(_sk_start_pipeline_sse41)
+_sk_start_pipeline_sse41:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  80                                  // push          %rax
+  .byte  77,137,199                          // mov           %r8,%r15
+  .byte  73,137,208                          // mov           %rdx,%r8
+  .byte  73,137,244                          // mov           %rsi,%r12
+  .byte  72,137,251                          // mov           %rdi,%rbx
+  .byte  72,137,206                          // mov           %rcx,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,245                          // mov           %rsi,%r13
+  .byte  72,141,75,4                         // lea           0x4(%rbx),%rcx
+  .byte  76,57,193                           // cmp           %r8,%rcx
+  .byte  118,5                               // jbe           30 <_sk_start_pipeline_sse41+0x30>
+  .byte  72,137,218                          // mov           %rbx,%rdx
+  .byte  235,75                              // jmp           7b <_sk_start_pipeline_sse41+0x7b>
+  .byte  76,137,69,208                       // mov           %r8,-0x30(%rbp)
+  .byte  65,184,0,0,0,0                      // mov           $0x0,%r8d
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  76,137,255                          // mov           %r15,%rdi
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  72,137,218                          // mov           %rbx,%rdx
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  73,137,198                          // mov           %rax,%r14
+  .byte  65,255,214                          // callq         *%r14
+  .byte  76,139,69,208                       // mov           -0x30(%rbp),%r8
+  .byte  76,137,240                          // mov           %r14,%rax
+  .byte  72,141,83,4                         // lea           0x4(%rbx),%rdx
+  .byte  72,131,195,8                        // add           $0x8,%rbx
+  .byte  76,57,195                           // cmp           %r8,%rbx
+  .byte  72,137,211                          // mov           %rdx,%rbx
+  .byte  118,185                             // jbe           34 <_sk_start_pipeline_sse41+0x34>
+  .byte  73,41,208                           // sub           %rdx,%r8
+  .byte  116,49                              // je            b1 <_sk_start_pipeline_sse41+0xb1>
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  76,137,255                          // mov           %r15,%rdi
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  195                                 // retq
+
+HIDDEN _sk_just_return_sse41
+.globl _sk_just_return_sse41
+FUNCTION(_sk_just_return_sse41)
+_sk_just_return_sse41:
+  .byte  195                                 // retq
+
+HIDDEN _sk_seed_shader_sse41
+.globl _sk_seed_shader_sse41
+FUNCTION(_sk_seed_shader_sse41)
+_sk_seed_shader_sse41:
+  .byte  102,15,110,194                      // movd          %edx,%xmm0
+  .byte  102,15,112,192,0                    // pshufd        $0x0,%xmm0,%xmm0
+  .byte  15,91,200                           // cvtdq2ps      %xmm0,%xmm1
+  .byte  15,40,21,252,79,0,0                 // movaps        0x4ffc(%rip),%xmm2        # 50d0 <_sk_callback_sse41+0xf3>
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  15,16,7                             // movups        (%rdi),%xmm0
+  .byte  15,88,193                           // addps         %xmm1,%xmm0
+  .byte  102,15,110,201                      // movd          %ecx,%xmm1
+  .byte  102,15,112,201,0                    // pshufd        $0x0,%xmm1,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,21,235,79,0,0                 // movaps        0x4feb(%rip),%xmm2        # 50e0 <_sk_callback_sse41+0x103>
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dither_sse41
+.globl _sk_dither_sse41
+FUNCTION(_sk_dither_sse41)
+_sk_dither_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  102,68,15,110,194                   // movd          %edx,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  243,68,15,111,79,32                 // movdqu        0x20(%rdi),%xmm9
+  .byte  102,69,15,254,200                   // paddd         %xmm8,%xmm9
+  .byte  102,68,15,110,193                   // movd          %ecx,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  102,69,15,239,193                   // pxor          %xmm9,%xmm8
+  .byte  102,68,15,111,21,185,79,0,0         // movdqa        0x4fb9(%rip),%xmm10        # 50f0 <_sk_callback_sse41+0x113>
+  .byte  102,69,15,111,216                   // movdqa        %xmm8,%xmm11
+  .byte  102,69,15,219,218                   // pand          %xmm10,%xmm11
+  .byte  102,65,15,114,243,5                 // pslld         $0x5,%xmm11
+  .byte  102,69,15,219,209                   // pand          %xmm9,%xmm10
+  .byte  102,65,15,114,242,4                 // pslld         $0x4,%xmm10
+  .byte  102,68,15,111,37,165,79,0,0         // movdqa        0x4fa5(%rip),%xmm12        # 5100 <_sk_callback_sse41+0x123>
+  .byte  102,68,15,111,45,172,79,0,0         // movdqa        0x4fac(%rip),%xmm13        # 5110 <_sk_callback_sse41+0x133>
+  .byte  102,69,15,111,240                   // movdqa        %xmm8,%xmm14
+  .byte  102,69,15,219,245                   // pand          %xmm13,%xmm14
+  .byte  102,65,15,114,246,2                 // pslld         $0x2,%xmm14
+  .byte  102,69,15,219,233                   // pand          %xmm9,%xmm13
+  .byte  102,69,15,254,237                   // paddd         %xmm13,%xmm13
+  .byte  102,69,15,219,196                   // pand          %xmm12,%xmm8
+  .byte  102,65,15,114,208,1                 // psrld         $0x1,%xmm8
+  .byte  102,69,15,219,204                   // pand          %xmm12,%xmm9
+  .byte  102,65,15,114,209,2                 // psrld         $0x2,%xmm9
+  .byte  102,69,15,235,234                   // por           %xmm10,%xmm13
+  .byte  102,69,15,235,233                   // por           %xmm9,%xmm13
+  .byte  102,69,15,235,243                   // por           %xmm11,%xmm14
+  .byte  102,69,15,235,245                   // por           %xmm13,%xmm14
+  .byte  102,69,15,235,240                   // por           %xmm8,%xmm14
+  .byte  69,15,91,198                        // cvtdq2ps      %xmm14,%xmm8
+  .byte  68,15,89,5,103,79,0,0               // mulps         0x4f67(%rip),%xmm8        # 5120 <_sk_callback_sse41+0x143>
+  .byte  68,15,88,5,111,79,0,0               // addps         0x4f6f(%rip),%xmm8        # 5130 <_sk_callback_sse41+0x153>
+  .byte  243,68,15,16,16                     // movss         (%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  68,15,88,210                        // addps         %xmm2,%xmm10
+  .byte  15,93,195                           // minps         %xmm3,%xmm0
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  15,93,203                           // minps         %xmm3,%xmm1
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  68,15,95,201                        // maxps         %xmm1,%xmm9
+  .byte  68,15,93,211                        // minps         %xmm3,%xmm10
+  .byte  65,15,95,210                        // maxps         %xmm10,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_constant_color_sse41
+.globl _sk_constant_color_sse41
+FUNCTION(_sk_constant_color_sse41)
+_sk_constant_color_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,12                     // movss         0xc(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_rgba_sse41
+.globl _sk_load_rgba_sse41
+FUNCTION(_sk_load_rgba_sse41)
+_sk_load_rgba_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  15,16,72,16                         // movups        0x10(%rax),%xmm1
+  .byte  15,16,80,32                         // movups        0x20(%rax),%xmm2
+  .byte  15,16,88,48                         // movups        0x30(%rax),%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_rgba_sse41
+.globl _sk_store_rgba_sse41
+FUNCTION(_sk_store_rgba_sse41)
+_sk_store_rgba_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,17,0                             // movups        %xmm0,(%rax)
+  .byte  15,17,72,16                         // movups        %xmm1,0x10(%rax)
+  .byte  15,17,80,32                         // movups        %xmm2,0x20(%rax)
+  .byte  15,17,88,48                         // movups        %xmm3,0x30(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clear_sse41
+.globl _sk_clear_sse41
+FUNCTION(_sk_clear_sse41)
+_sk_clear_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcatop_sse41
+.globl _sk_srcatop_sse41
+FUNCTION(_sk_srcatop_sse41)
+_sk_srcatop_sse41:
+  .byte  15,89,199                           // mulps         %xmm7,%xmm0
+  .byte  68,15,40,5,201,78,0,0               // movaps        0x4ec9(%rip),%xmm8        # 5140 <_sk_callback_sse41+0x163>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  65,15,88,193                        // addps         %xmm9,%xmm0
+  .byte  15,89,207                           // mulps         %xmm7,%xmm1
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,88,209                        // addps         %xmm9,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstatop_sse41
+.globl _sk_dstatop_sse41
+FUNCTION(_sk_dstatop_sse41)
+_sk_dstatop_sse41:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  68,15,40,13,140,78,0,0              // movaps        0x4e8c(%rip),%xmm9        # 5150 <_sk_callback_sse41+0x173>
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,197                        // mulps         %xmm5,%xmm8
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,198                        // mulps         %xmm6,%xmm8
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  65,15,88,208                        // addps         %xmm8,%xmm2
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcin_sse41
+.globl _sk_srcin_sse41
+FUNCTION(_sk_srcin_sse41)
+_sk_srcin_sse41:
+  .byte  15,89,199                           // mulps         %xmm7,%xmm0
+  .byte  15,89,207                           // mulps         %xmm7,%xmm1
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstin_sse41
+.globl _sk_dstin_sse41
+FUNCTION(_sk_dstin_sse41)
+_sk_dstin_sse41:
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcout_sse41
+.globl _sk_srcout_sse41
+FUNCTION(_sk_srcout_sse41)
+_sk_srcout_sse41:
+  .byte  68,15,40,5,48,78,0,0                // movaps        0x4e30(%rip),%xmm8        # 5160 <_sk_callback_sse41+0x183>
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstout_sse41
+.globl _sk_dstout_sse41
+FUNCTION(_sk_dstout_sse41)
+_sk_dstout_sse41:
+  .byte  68,15,40,5,32,78,0,0                // movaps        0x4e20(%rip),%xmm8        # 5170 <_sk_callback_sse41+0x193>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_sse41
+.globl _sk_srcover_sse41
+FUNCTION(_sk_srcover_sse41)
+_sk_srcover_sse41:
+  .byte  68,15,40,5,3,78,0,0                 // movaps        0x4e03(%rip),%xmm8        # 5180 <_sk_callback_sse41+0x1a3>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  65,15,88,193                        // addps         %xmm9,%xmm0
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,88,209                        // addps         %xmm9,%xmm2
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstover_sse41
+.globl _sk_dstover_sse41
+FUNCTION(_sk_dstover_sse41)
+_sk_dstover_sse41:
+  .byte  68,15,40,5,215,77,0,0               // movaps        0x4dd7(%rip),%xmm8        # 5190 <_sk_callback_sse41+0x1b3>
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_modulate_sse41
+.globl _sk_modulate_sse41
+FUNCTION(_sk_modulate_sse41)
+_sk_modulate_sse41:
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_multiply_sse41
+.globl _sk_multiply_sse41
+FUNCTION(_sk_multiply_sse41)
+_sk_multiply_sse41:
+  .byte  68,15,40,5,171,77,0,0               // movaps        0x4dab(%rip),%xmm8        # 51a0 <_sk_callback_sse41+0x1c3>
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,89,208                        // mulps         %xmm0,%xmm10
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,220                        // mulps         %xmm4,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,221                        // mulps         %xmm5,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,222                        // mulps         %xmm6,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  65,15,88,211                        // addps         %xmm11,%xmm2
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_plus__sse41
+.globl _sk_plus__sse41
+FUNCTION(_sk_plus__sse41)
+_sk_plus__sse41:
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_screen_sse41
+.globl _sk_screen_sse41
+FUNCTION(_sk_screen_sse41)
+_sk_screen_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  65,15,92,192                        // subps         %xmm8,%xmm0
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,88,197                        // addps         %xmm5,%xmm8
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  68,15,92,193                        // subps         %xmm1,%xmm8
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  68,15,88,206                        // addps         %xmm6,%xmm9
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,92,202                        // subps         %xmm2,%xmm9
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,88,215                        // addps         %xmm7,%xmm10
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xor__sse41
+.globl _sk_xor__sse41
+FUNCTION(_sk_xor__sse41)
+_sk_xor__sse41:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  15,40,29,224,76,0,0                 // movaps        0x4ce0(%rip),%xmm3        # 51b0 <_sk_callback_sse41+0x1d3>
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,92,216                        // subps         %xmm8,%xmm3
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,89,212                        // mulps         %xmm4,%xmm10
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,89,213                        // mulps         %xmm5,%xmm10
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,89,214                        // mulps         %xmm6,%xmm10
+  .byte  65,15,88,210                        // addps         %xmm10,%xmm2
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_darken_sse41
+.globl _sk_darken_sse41
+FUNCTION(_sk_darken_sse41)
+_sk_darken_sse41:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,95,201                        // maxps         %xmm1,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,95,193                        // maxps         %xmm9,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,95,209                        // maxps         %xmm9,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,75,76,0,0                  // movaps        0x4c4b(%rip),%xmm2        # 51c0 <_sk_callback_sse41+0x1e3>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lighten_sse41
+.globl _sk_lighten_sse41
+FUNCTION(_sk_lighten_sse41)
+_sk_lighten_sse41:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,93,201                        // minps         %xmm1,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,93,193                        // minps         %xmm9,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,93,209                        // minps         %xmm9,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,240,75,0,0                 // movaps        0x4bf0(%rip),%xmm2        # 51d0 <_sk_callback_sse41+0x1f3>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_difference_sse41
+.globl _sk_difference_sse41
+FUNCTION(_sk_difference_sse41)
+_sk_difference_sse41:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,93,201                        // minps         %xmm1,%xmm9
+  .byte  69,15,88,201                        // addps         %xmm9,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,93,193                        // minps         %xmm9,%xmm8
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,93,209                        // minps         %xmm9,%xmm2
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,138,75,0,0                 // movaps        0x4b8a(%rip),%xmm2        # 51e0 <_sk_callback_sse41+0x203>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_exclusion_sse41
+.globl _sk_exclusion_sse41
+FUNCTION(_sk_exclusion_sse41)
+_sk_exclusion_sse41:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,197                        // mulps         %xmm5,%xmm8
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,74,75,0,0                  // movaps        0x4b4a(%rip),%xmm2        # 51f0 <_sk_callback_sse41+0x213>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colorburn_sse41
+.globl _sk_colorburn_sse41
+FUNCTION(_sk_colorburn_sse41)
+_sk_colorburn_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  68,15,40,21,61,75,0,0               // movaps        0x4b3d(%rip),%xmm10        # 5200 <_sk_callback_sse41+0x223>
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  68,15,92,223                        // subps         %xmm7,%xmm11
+  .byte  69,15,40,203                        // movaps        %xmm11,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  65,15,94,192                        // divps         %xmm8,%xmm0
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,93,224                        // minps         %xmm0,%xmm12
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,233                        // addps         %xmm9,%xmm13
+  .byte  69,15,40,225                        // movaps        %xmm9,%xmm12
+  .byte  68,15,88,228                        // addps         %xmm4,%xmm12
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,194,193,0                     // cmpeqps       %xmm9,%xmm0
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  102,69,15,56,20,232                 // blendvps      %xmm0,%xmm8,%xmm13
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,194,199,0                        // cmpeqps       %xmm7,%xmm0
+  .byte  69,15,88,197                        // addps         %xmm13,%xmm8
+  .byte  102,69,15,56,20,196                 // blendvps      %xmm0,%xmm12,%xmm8
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  68,15,89,225                        // mulps         %xmm1,%xmm12
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  15,92,197                           // subps         %xmm5,%xmm0
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,94,193                           // divps         %xmm1,%xmm0
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,93,232                        // minps         %xmm0,%xmm13
+  .byte  68,15,40,247                        // movaps        %xmm7,%xmm14
+  .byte  69,15,92,245                        // subps         %xmm13,%xmm14
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,194,193,0                     // cmpeqps       %xmm9,%xmm0
+  .byte  68,15,89,243                        // mulps         %xmm3,%xmm14
+  .byte  69,15,88,244                        // addps         %xmm12,%xmm14
+  .byte  102,68,15,56,20,241                 // blendvps      %xmm0,%xmm1,%xmm14
+  .byte  68,15,88,229                        // addps         %xmm5,%xmm12
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  15,194,199,0                        // cmpeqps       %xmm7,%xmm0
+  .byte  102,65,15,56,20,204                 // blendvps      %xmm0,%xmm12,%xmm1
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  15,92,198                           // subps         %xmm6,%xmm0
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,94,194                           // divps         %xmm2,%xmm0
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,93,224                        // minps         %xmm0,%xmm12
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  68,15,194,202,0                     // cmpeqps       %xmm2,%xmm9
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,235                        // addps         %xmm11,%xmm13
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,68,15,56,20,234                 // blendvps      %xmm0,%xmm2,%xmm13
+  .byte  68,15,88,222                        // addps         %xmm6,%xmm11
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  65,15,88,213                        // addps         %xmm13,%xmm2
+  .byte  15,40,198                           // movaps        %xmm6,%xmm0
+  .byte  15,194,199,0                        // cmpeqps       %xmm7,%xmm0
+  .byte  102,65,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm2
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colordodge_sse41
+.globl _sk_colordodge_sse41
+FUNCTION(_sk_colordodge_sse41)
+_sk_colordodge_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  68,15,40,21,27,74,0,0               // movaps        0x4a1b(%rip),%xmm10        # 5210 <_sk_callback_sse41+0x233>
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  68,15,92,223                        // subps         %xmm7,%xmm11
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,89,224                        // mulps         %xmm8,%xmm12
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  65,15,92,192                        // subps         %xmm8,%xmm0
+  .byte  68,15,94,200                        // divps         %xmm0,%xmm9
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,40,247                        // movaps        %xmm7,%xmm14
+  .byte  69,15,93,241                        // minps         %xmm9,%xmm14
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,194,195,0                        // cmpeqps       %xmm3,%xmm0
+  .byte  68,15,89,243                        // mulps         %xmm3,%xmm14
+  .byte  69,15,88,244                        // addps         %xmm12,%xmm14
+  .byte  102,69,15,56,20,240                 // blendvps      %xmm0,%xmm8,%xmm14
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  68,15,88,228                        // addps         %xmm4,%xmm12
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  69,15,88,198                        // addps         %xmm14,%xmm8
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  65,15,194,193,0                     // cmpeqps       %xmm9,%xmm0
+  .byte  102,69,15,56,20,196                 // blendvps      %xmm0,%xmm12,%xmm8
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  68,15,89,229                        // mulps         %xmm5,%xmm12
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  68,15,94,224                        // divps         %xmm0,%xmm12
+  .byte  69,15,40,243                        // movaps        %xmm11,%xmm14
+  .byte  68,15,89,241                        // mulps         %xmm1,%xmm14
+  .byte  69,15,93,236                        // minps         %xmm12,%xmm13
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  15,194,195,0                        // cmpeqps       %xmm3,%xmm0
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,238                        // addps         %xmm14,%xmm13
+  .byte  102,68,15,56,20,233                 // blendvps      %xmm0,%xmm1,%xmm13
+  .byte  68,15,88,245                        // addps         %xmm5,%xmm14
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,88,205                        // addps         %xmm13,%xmm1
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  65,15,194,193,0                     // cmpeqps       %xmm9,%xmm0
+  .byte  102,65,15,56,20,206                 // blendvps      %xmm0,%xmm14,%xmm1
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  68,15,89,230                        // mulps         %xmm6,%xmm12
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,92,194                           // subps         %xmm2,%xmm0
+  .byte  68,15,94,224                        // divps         %xmm0,%xmm12
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,93,236                        // minps         %xmm12,%xmm13
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  15,194,195,0                        // cmpeqps       %xmm3,%xmm0
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,235                        // addps         %xmm11,%xmm13
+  .byte  102,68,15,56,20,234                 // blendvps      %xmm0,%xmm2,%xmm13
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  65,15,88,213                        // addps         %xmm13,%xmm2
+  .byte  68,15,194,206,0                     // cmpeqps       %xmm6,%xmm9
+  .byte  68,15,88,222                        // addps         %xmm6,%xmm11
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,65,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm2
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hardlight_sse41
+.globl _sk_hardlight_sse41
+FUNCTION(_sk_hardlight_sse41)
+_sk_hardlight_sse41:
+  .byte  15,41,116,36,232                    // movaps        %xmm6,-0x18(%rsp)
+  .byte  68,15,40,229                        // movaps        %xmm5,%xmm12
+  .byte  15,40,244                           // movaps        %xmm4,%xmm6
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  68,15,40,21,244,72,0,0              // movaps        0x48f4(%rip),%xmm10        # 5220 <_sk_callback_sse41+0x243>
+  .byte  65,15,40,234                        // movaps        %xmm10,%xmm5
+  .byte  15,92,239                           // subps         %xmm7,%xmm5
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  68,15,92,212                        // subps         %xmm4,%xmm10
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  68,15,89,198                        // mulps         %xmm6,%xmm8
+  .byte  68,15,88,192                        // addps         %xmm0,%xmm8
+  .byte  68,15,40,252                        // movaps        %xmm4,%xmm15
+  .byte  69,15,92,249                        // subps         %xmm9,%xmm15
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,40,247                        // movaps        %xmm7,%xmm14
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  15,92,198                           // subps         %xmm6,%xmm0
+  .byte  65,15,89,199                        // mulps         %xmm15,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  68,15,40,251                        // movaps        %xmm3,%xmm15
+  .byte  68,15,92,248                        // subps         %xmm0,%xmm15
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,196,2                        // cmpleps       %xmm4,%xmm0
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  69,15,88,201                        // addps         %xmm9,%xmm9
+  .byte  102,69,15,56,20,249                 // blendvps      %xmm0,%xmm9,%xmm15
+  .byte  68,15,40,221                        // movaps        %xmm5,%xmm11
+  .byte  68,15,89,217                        // mulps         %xmm1,%xmm11
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  69,15,40,204                        // movaps        %xmm12,%xmm9
+  .byte  69,15,92,233                        // subps         %xmm9,%xmm13
+  .byte  68,15,89,232                        // mulps         %xmm0,%xmm13
+  .byte  69,15,88,237                        // addps         %xmm13,%xmm13
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,196,2                        // cmpleps       %xmm4,%xmm0
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  102,68,15,56,20,225                 // blendvps      %xmm0,%xmm1,%xmm12
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  69,15,40,202                        // movaps        %xmm10,%xmm9
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  69,15,88,199                        // addps         %xmm15,%xmm8
+  .byte  65,15,89,205                        // mulps         %xmm13,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  65,15,88,204                        // addps         %xmm12,%xmm1
+  .byte  15,89,234                           // mulps         %xmm2,%xmm5
+  .byte  68,15,40,92,36,232                  // movaps        -0x18(%rsp),%xmm11
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  68,15,88,205                        // addps         %xmm5,%xmm9
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,196,2                        // cmpleps       %xmm4,%xmm0
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,92,234                           // subps         %xmm2,%xmm5
+  .byte  65,15,89,211                        // mulps         %xmm11,%xmm2
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  69,15,92,243                        // subps         %xmm11,%xmm14
+  .byte  68,15,89,245                        // mulps         %xmm5,%xmm14
+  .byte  69,15,88,246                        // addps         %xmm14,%xmm14
+  .byte  65,15,92,222                        // subps         %xmm14,%xmm3
+  .byte  102,15,56,20,218                    // blendvps      %xmm0,%xmm2,%xmm3
+  .byte  68,15,88,203                        // addps         %xmm3,%xmm9
+  .byte  65,15,88,226                        // addps         %xmm10,%xmm4
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,40,220                           // movaps        %xmm4,%xmm3
+  .byte  15,40,230                           // movaps        %xmm6,%xmm4
+  .byte  65,15,40,237                        // movaps        %xmm13,%xmm5
+  .byte  65,15,40,243                        // movaps        %xmm11,%xmm6
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_overlay_sse41
+.globl _sk_overlay_sse41
+FUNCTION(_sk_overlay_sse41)
+_sk_overlay_sse41:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,240                        // movaps        %xmm0,%xmm14
+  .byte  68,15,40,21,217,71,0,0              // movaps        0x47d9(%rip),%xmm10        # 5230 <_sk_callback_sse41+0x253>
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  68,15,92,223                        // subps         %xmm7,%xmm11
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,89,198                        // mulps         %xmm14,%xmm0
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  68,15,88,192                        // addps         %xmm0,%xmm8
+  .byte  68,15,40,235                        // movaps        %xmm3,%xmm13
+  .byte  69,15,92,238                        // subps         %xmm14,%xmm13
+  .byte  68,15,89,244                        // mulps         %xmm4,%xmm14
+  .byte  15,40,207                           // movaps        %xmm7,%xmm1
+  .byte  15,92,204                           // subps         %xmm4,%xmm1
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,199,2                        // cmpleps       %xmm7,%xmm0
+  .byte  69,15,88,246                        // addps         %xmm14,%xmm14
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  68,15,89,231                        // mulps         %xmm7,%xmm12
+  .byte  65,15,89,205                        // mulps         %xmm13,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  69,15,40,236                        // movaps        %xmm12,%xmm13
+  .byte  68,15,92,233                        // subps         %xmm1,%xmm13
+  .byte  102,69,15,56,20,238                 // blendvps      %xmm0,%xmm14,%xmm13
+  .byte  69,15,88,197                        // addps         %xmm13,%xmm8
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  68,15,40,235                        // movaps        %xmm3,%xmm13
+  .byte  69,15,92,233                        // subps         %xmm9,%xmm13
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  68,15,40,247                        // movaps        %xmm7,%xmm14
+  .byte  68,15,92,245                        // subps         %xmm5,%xmm14
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,199,2                        // cmpleps       %xmm7,%xmm0
+  .byte  69,15,88,201                        // addps         %xmm9,%xmm9
+  .byte  69,15,89,245                        // mulps         %xmm13,%xmm14
+  .byte  69,15,88,246                        // addps         %xmm14,%xmm14
+  .byte  69,15,40,236                        // movaps        %xmm12,%xmm13
+  .byte  69,15,92,238                        // subps         %xmm14,%xmm13
+  .byte  102,69,15,56,20,233                 // blendvps      %xmm0,%xmm9,%xmm13
+  .byte  65,15,88,205                        // addps         %xmm13,%xmm1
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,40,202                        // movaps        %xmm10,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  69,15,88,203                        // addps         %xmm11,%xmm9
+  .byte  68,15,40,219                        // movaps        %xmm3,%xmm11
+  .byte  68,15,92,218                        // subps         %xmm2,%xmm11
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,92,238                        // subps         %xmm6,%xmm13
+  .byte  15,40,198                           // movaps        %xmm6,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,199,2                        // cmpleps       %xmm7,%xmm0
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  69,15,89,235                        // mulps         %xmm11,%xmm13
+  .byte  69,15,88,237                        // addps         %xmm13,%xmm13
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  102,68,15,56,20,226                 // blendvps      %xmm0,%xmm2,%xmm12
+  .byte  69,15,88,204                        // addps         %xmm12,%xmm9
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_softlight_sse41
+.globl _sk_softlight_sse41
+FUNCTION(_sk_softlight_sse41)
+_sk_softlight_sse41:
+  .byte  15,41,116,36,216                    // movaps        %xmm6,-0x28(%rsp)
+  .byte  15,40,244                           // movaps        %xmm4,%xmm6
+  .byte  15,41,84,36,232                     // movaps        %xmm2,-0x18(%rsp)
+  .byte  15,41,76,36,200                     // movaps        %xmm1,-0x38(%rsp)
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  68,15,194,207,1                     // cmpltps       %xmm7,%xmm9
+  .byte  15,40,198                           // movaps        %xmm6,%xmm0
+  .byte  15,94,199                           // divps         %xmm7,%xmm0
+  .byte  65,15,84,193                        // andps         %xmm9,%xmm0
+  .byte  15,40,13,176,70,0,0                 // movaps        0x46b0(%rip),%xmm1        # 5240 <_sk_callback_sse41+0x263>
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  68,15,92,208                        // subps         %xmm0,%xmm10
+  .byte  68,15,40,240                        // movaps        %xmm0,%xmm14
+  .byte  68,15,40,248                        // movaps        %xmm0,%xmm15
+  .byte  15,82,208                           // rsqrtps       %xmm0,%xmm2
+  .byte  68,15,83,218                        // rcpps         %xmm2,%xmm11
+  .byte  68,15,92,216                        // subps         %xmm0,%xmm11
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,89,210                           // mulps         %xmm2,%xmm2
+  .byte  15,88,208                           // addps         %xmm0,%xmm2
+  .byte  68,15,40,45,142,70,0,0              // movaps        0x468e(%rip),%xmm13        # 5250 <_sk_callback_sse41+0x273>
+  .byte  69,15,88,245                        // addps         %xmm13,%xmm14
+  .byte  68,15,89,242                        // mulps         %xmm2,%xmm14
+  .byte  68,15,40,37,142,70,0,0              // movaps        0x468e(%rip),%xmm12        # 5260 <_sk_callback_sse41+0x283>
+  .byte  69,15,89,252                        // mulps         %xmm12,%xmm15
+  .byte  69,15,88,254                        // addps         %xmm14,%xmm15
+  .byte  15,40,198                           // movaps        %xmm6,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,199,2                        // cmpleps       %xmm7,%xmm0
+  .byte  102,69,15,56,20,223                 // blendvps      %xmm0,%xmm15,%xmm11
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  68,15,88,211                        // addps         %xmm3,%xmm10
+  .byte  68,15,89,214                        // mulps         %xmm6,%xmm10
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,88,218                        // addps         %xmm2,%xmm11
+  .byte  15,194,195,2                        // cmpleps       %xmm3,%xmm0
+  .byte  102,69,15,56,20,218                 // blendvps      %xmm0,%xmm10,%xmm11
+  .byte  68,15,40,213                        // movaps        %xmm5,%xmm10
+  .byte  68,15,94,215                        // divps         %xmm7,%xmm10
+  .byte  69,15,84,209                        // andps         %xmm9,%xmm10
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,89,210                           // mulps         %xmm2,%xmm2
+  .byte  15,88,208                           // addps         %xmm0,%xmm2
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  65,15,88,197                        // addps         %xmm13,%xmm0
+  .byte  15,89,194                           // mulps         %xmm2,%xmm0
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,89,212                        // mulps         %xmm12,%xmm2
+  .byte  15,88,208                           // addps         %xmm0,%xmm2
+  .byte  65,15,82,194                        // rsqrtps       %xmm10,%xmm0
+  .byte  68,15,83,240                        // rcpps         %xmm0,%xmm14
+  .byte  69,15,92,242                        // subps         %xmm10,%xmm14
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,199,2                        // cmpleps       %xmm7,%xmm0
+  .byte  102,68,15,56,20,242                 // blendvps      %xmm0,%xmm2,%xmm14
+  .byte  68,15,40,249                        // movaps        %xmm1,%xmm15
+  .byte  69,15,92,250                        // subps         %xmm10,%xmm15
+  .byte  15,40,108,36,200                    // movaps        -0x38(%rsp),%xmm5
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  68,15,89,250                        // mulps         %xmm2,%xmm15
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,89,242                        // mulps         %xmm2,%xmm14
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,41,100,36,184                    // movaps        %xmm4,-0x48(%rsp)
+  .byte  15,89,212                           // mulps         %xmm4,%xmm2
+  .byte  68,15,88,242                        // addps         %xmm2,%xmm14
+  .byte  68,15,88,251                        // addps         %xmm3,%xmm15
+  .byte  68,15,89,252                        // mulps         %xmm4,%xmm15
+  .byte  15,194,195,2                        // cmpleps       %xmm3,%xmm0
+  .byte  102,69,15,56,20,247                 // blendvps      %xmm0,%xmm15,%xmm14
+  .byte  68,15,40,249                        // movaps        %xmm1,%xmm15
+  .byte  15,40,100,36,216                    // movaps        -0x28(%rsp),%xmm4
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,94,199                           // divps         %xmm7,%xmm0
+  .byte  65,15,84,193                        // andps         %xmm9,%xmm0
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  15,92,200                           // subps         %xmm0,%xmm1
+  .byte  68,15,88,232                        // addps         %xmm0,%xmm13
+  .byte  68,15,89,224                        // mulps         %xmm0,%xmm12
+  .byte  15,82,208                           // rsqrtps       %xmm0,%xmm2
+  .byte  68,15,83,202                        // rcpps         %xmm2,%xmm9
+  .byte  68,15,92,200                        // subps         %xmm0,%xmm9
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,89,210                           // mulps         %xmm2,%xmm2
+  .byte  15,88,208                           // addps         %xmm0,%xmm2
+  .byte  68,15,89,234                        // mulps         %xmm2,%xmm13
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,194,199,2                        // cmpleps       %xmm7,%xmm0
+  .byte  102,69,15,56,20,204                 // blendvps      %xmm0,%xmm12,%xmm9
+  .byte  68,15,40,100,36,232                 // movaps        -0x18(%rsp),%xmm12
+  .byte  65,15,40,196                        // movaps        %xmm12,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,202                           // mulps         %xmm2,%xmm1
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,89,202                        // mulps         %xmm2,%xmm9
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,89,212                           // mulps         %xmm4,%xmm2
+  .byte  68,15,88,202                        // addps         %xmm2,%xmm9
+  .byte  15,88,203                           // addps         %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  15,194,195,2                        // cmpleps       %xmm3,%xmm0
+  .byte  102,68,15,56,20,201                 // blendvps      %xmm0,%xmm1,%xmm9
+  .byte  68,15,92,255                        // subps         %xmm7,%xmm15
+  .byte  69,15,89,199                        // mulps         %xmm15,%xmm8
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  65,15,89,207                        // mulps         %xmm15,%xmm1
+  .byte  69,15,89,252                        // mulps         %xmm12,%xmm15
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,89,198                           // mulps         %xmm6,%xmm0
+  .byte  68,15,88,192                        // addps         %xmm0,%xmm8
+  .byte  69,15,88,195                        // addps         %xmm11,%xmm8
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,40,108,36,184                    // movaps        -0x48(%rsp),%xmm5
+  .byte  15,89,197                           // mulps         %xmm5,%xmm0
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,40,212                           // movaps        %xmm4,%xmm2
+  .byte  65,15,88,199                        // addps         %xmm15,%xmm0
+  .byte  68,15,88,200                        // addps         %xmm0,%xmm9
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,230                           // movaps        %xmm6,%xmm4
+  .byte  15,40,242                           // movaps        %xmm2,%xmm6
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hue_sse41
+.globl _sk_hue_sse41
+FUNCTION(_sk_hue_sse41)
+_sk_hue_sse41:
+  .byte  15,41,124,36,152                    // movaps        %xmm7,-0x68(%rsp)
+  .byte  68,15,40,246                        // movaps        %xmm6,%xmm14
+  .byte  15,40,244                           // movaps        %xmm4,%xmm6
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  15,41,84,36,232                     // movaps        %xmm2,-0x18(%rsp)
+  .byte  15,41,76,36,216                     // movaps        %xmm1,-0x28(%rsp)
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  68,15,41,84,36,200                  // movaps        %xmm10,-0x38(%rsp)
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  68,15,40,234                        // movaps        %xmm2,%xmm13
+  .byte  69,15,89,232                        // mulps         %xmm8,%xmm13
+  .byte  68,15,40,205                        // movaps        %xmm5,%xmm9
+  .byte  68,15,40,221                        // movaps        %xmm5,%xmm11
+  .byte  15,41,108,36,184                    // movaps        %xmm5,-0x48(%rsp)
+  .byte  69,15,95,222                        // maxps         %xmm14,%xmm11
+  .byte  15,40,254                           // movaps        %xmm6,%xmm7
+  .byte  68,15,40,230                        // movaps        %xmm6,%xmm12
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  65,15,95,211                        // maxps         %xmm11,%xmm2
+  .byte  65,15,40,230                        // movaps        %xmm14,%xmm4
+  .byte  15,41,100,36,168                    // movaps        %xmm4,-0x58(%rsp)
+  .byte  68,15,93,204                        // minps         %xmm4,%xmm9
+  .byte  65,15,93,249                        // minps         %xmm9,%xmm7
+  .byte  15,92,215                           // subps         %xmm7,%xmm2
+  .byte  15,40,249                           // movaps        %xmm1,%xmm7
+  .byte  65,15,93,253                        // minps         %xmm13,%xmm7
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,93,223                           // minps         %xmm7,%xmm3
+  .byte  15,40,249                           // movaps        %xmm1,%xmm7
+  .byte  65,15,95,253                        // maxps         %xmm13,%xmm7
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,95,199                           // maxps         %xmm7,%xmm0
+  .byte  15,40,253                           // movaps        %xmm5,%xmm7
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,92,195                           // subps         %xmm3,%xmm0
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  15,92,203                           // subps         %xmm3,%xmm1
+  .byte  68,15,92,235                        // subps         %xmm3,%xmm13
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  68,15,94,208                        // divps         %xmm0,%xmm10
+  .byte  15,89,202                           // mulps         %xmm2,%xmm1
+  .byte  15,94,200                           // divps         %xmm0,%xmm1
+  .byte  68,15,89,234                        // mulps         %xmm2,%xmm13
+  .byte  68,15,94,232                        // divps         %xmm0,%xmm13
+  .byte  15,194,195,4                        // cmpneqps      %xmm3,%xmm0
+  .byte  68,15,84,208                        // andps         %xmm0,%xmm10
+  .byte  15,84,200                           // andps         %xmm0,%xmm1
+  .byte  68,15,84,232                        // andps         %xmm0,%xmm13
+  .byte  15,40,5,249,67,0,0                  // movaps        0x43f9(%rip),%xmm0        # 5270 <_sk_callback_sse41+0x293>
+  .byte  68,15,89,224                        // mulps         %xmm0,%xmm12
+  .byte  15,40,21,254,67,0,0                 // movaps        0x43fe(%rip),%xmm2        # 5280 <_sk_callback_sse41+0x2a3>
+  .byte  15,89,250                           // mulps         %xmm2,%xmm7
+  .byte  65,15,88,252                        // addps         %xmm12,%xmm7
+  .byte  68,15,40,53,255,67,0,0              // movaps        0x43ff(%rip),%xmm14        # 5290 <_sk_callback_sse41+0x2b3>
+  .byte  68,15,40,252                        // movaps        %xmm4,%xmm15
+  .byte  69,15,89,254                        // mulps         %xmm14,%xmm15
+  .byte  68,15,88,255                        // addps         %xmm7,%xmm15
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,89,216                           // mulps         %xmm0,%xmm3
+  .byte  15,40,249                           // movaps        %xmm1,%xmm7
+  .byte  15,89,250                           // mulps         %xmm2,%xmm7
+  .byte  15,88,251                           // addps         %xmm3,%xmm7
+  .byte  65,15,40,221                        // movaps        %xmm13,%xmm3
+  .byte  65,15,89,222                        // mulps         %xmm14,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  69,15,89,248                        // mulps         %xmm8,%xmm15
+  .byte  68,15,92,251                        // subps         %xmm3,%xmm15
+  .byte  69,15,88,215                        // addps         %xmm15,%xmm10
+  .byte  65,15,88,207                        // addps         %xmm15,%xmm1
+  .byte  69,15,88,253                        // addps         %xmm13,%xmm15
+  .byte  15,40,217                           // movaps        %xmm1,%xmm3
+  .byte  65,15,93,223                        // minps         %xmm15,%xmm3
+  .byte  65,15,40,250                        // movaps        %xmm10,%xmm7
+  .byte  15,93,251                           // minps         %xmm3,%xmm7
+  .byte  65,15,89,194                        // mulps         %xmm10,%xmm0
+  .byte  15,89,209                           // mulps         %xmm1,%xmm2
+  .byte  15,88,208                           // addps         %xmm0,%xmm2
+  .byte  69,15,89,247                        // mulps         %xmm15,%xmm14
+  .byte  68,15,88,242                        // addps         %xmm2,%xmm14
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  68,15,194,207,2                     // cmpleps       %xmm7,%xmm9
+  .byte  65,15,40,222                        // movaps        %xmm14,%xmm3
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  69,15,40,234                        // movaps        %xmm10,%xmm13
+  .byte  69,15,92,238                        // subps         %xmm14,%xmm13
+  .byte  69,15,89,238                        // mulps         %xmm14,%xmm13
+  .byte  68,15,94,235                        // divps         %xmm3,%xmm13
+  .byte  69,15,88,238                        // addps         %xmm14,%xmm13
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,69,15,56,20,234                 // blendvps      %xmm0,%xmm10,%xmm13
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,95,199                        // maxps         %xmm15,%xmm0
+  .byte  68,15,95,208                        // maxps         %xmm0,%xmm10
+  .byte  65,15,40,248                        // movaps        %xmm8,%xmm7
+  .byte  15,40,108,36,152                    // movaps        -0x68(%rsp),%xmm5
+  .byte  15,89,253                           // mulps         %xmm5,%xmm7
+  .byte  15,40,231                           // movaps        %xmm7,%xmm4
+  .byte  65,15,194,226,1                     // cmpltps       %xmm10,%xmm4
+  .byte  65,15,40,213                        // movaps        %xmm13,%xmm2
+  .byte  65,15,92,214                        // subps         %xmm14,%xmm2
+  .byte  68,15,40,223                        // movaps        %xmm7,%xmm11
+  .byte  69,15,92,222                        // subps         %xmm14,%xmm11
+  .byte  65,15,89,211                        // mulps         %xmm11,%xmm2
+  .byte  69,15,92,214                        // subps         %xmm14,%xmm10
+  .byte  65,15,94,210                        // divps         %xmm10,%xmm2
+  .byte  65,15,88,214                        // addps         %xmm14,%xmm2
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  102,68,15,56,20,234                 // blendvps      %xmm0,%xmm2,%xmm13
+  .byte  68,15,40,225                        // movaps        %xmm1,%xmm12
+  .byte  69,15,92,230                        // subps         %xmm14,%xmm12
+  .byte  69,15,89,230                        // mulps         %xmm14,%xmm12
+  .byte  68,15,94,227                        // divps         %xmm3,%xmm12
+  .byte  69,15,88,230                        // addps         %xmm14,%xmm12
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,68,15,56,20,225                 // blendvps      %xmm0,%xmm1,%xmm12
+  .byte  65,15,40,204                        // movaps        %xmm12,%xmm1
+  .byte  65,15,92,206                        // subps         %xmm14,%xmm1
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  65,15,94,202                        // divps         %xmm10,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  102,68,15,56,20,225                 // blendvps      %xmm0,%xmm1,%xmm12
+  .byte  65,15,40,207                        // movaps        %xmm15,%xmm1
+  .byte  65,15,92,206                        // subps         %xmm14,%xmm1
+  .byte  65,15,89,206                        // mulps         %xmm14,%xmm1
+  .byte  15,94,203                           // divps         %xmm3,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,65,15,56,20,207                 // blendvps      %xmm0,%xmm15,%xmm1
+  .byte  15,40,209                           // movaps        %xmm1,%xmm2
+  .byte  65,15,92,214                        // subps         %xmm14,%xmm2
+  .byte  65,15,89,211                        // mulps         %xmm11,%xmm2
+  .byte  65,15,94,210                        // divps         %xmm10,%xmm2
+  .byte  65,15,88,214                        // addps         %xmm14,%xmm2
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  102,15,56,20,202                    // blendvps      %xmm0,%xmm2,%xmm1
+  .byte  68,15,40,13,195,66,0,0              // movaps        0x42c3(%rip),%xmm9        # 52a0 <_sk_callback_sse41+0x2c3>
+  .byte  65,15,40,225                        // movaps        %xmm9,%xmm4
+  .byte  15,92,229                           // subps         %xmm5,%xmm4
+  .byte  15,40,68,36,200                     // movaps        -0x38(%rsp),%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,40,92,36,216                     // movaps        -0x28(%rsp),%xmm3
+  .byte  15,89,220                           // mulps         %xmm4,%xmm3
+  .byte  15,89,100,36,232                    // mulps         -0x18(%rsp),%xmm4
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,88,197                        // addps         %xmm5,%xmm8
+  .byte  68,15,40,213                        // movaps        %xmm5,%xmm10
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  68,15,95,234                        // maxps         %xmm2,%xmm13
+  .byte  68,15,95,226                        // maxps         %xmm2,%xmm12
+  .byte  15,95,202                           // maxps         %xmm2,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,88,194                           // addps         %xmm2,%xmm0
+  .byte  65,15,88,197                        // addps         %xmm13,%xmm0
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,40,108,36,184                    // movaps        -0x48(%rsp),%xmm5
+  .byte  15,89,213                           // mulps         %xmm5,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  65,15,88,220                        // addps         %xmm12,%xmm3
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,40,92,36,168                     // movaps        -0x58(%rsp),%xmm3
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  68,15,88,204                        // addps         %xmm4,%xmm9
+  .byte  68,15,88,201                        // addps         %xmm1,%xmm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,230                           // movaps        %xmm6,%xmm4
+  .byte  15,40,243                           // movaps        %xmm3,%xmm6
+  .byte  15,40,202                           // movaps        %xmm2,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  65,15,40,250                        // movaps        %xmm10,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_saturation_sse41
+.globl _sk_saturation_sse41
+FUNCTION(_sk_saturation_sse41)
+_sk_saturation_sse41:
+  .byte  68,15,40,206                        // movaps        %xmm6,%xmm9
+  .byte  15,40,245                           // movaps        %xmm5,%xmm6
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  15,41,76,36,216                     // movaps        %xmm1,-0x28(%rsp)
+  .byte  15,41,68,36,200                     // movaps        %xmm0,-0x38(%rsp)
+  .byte  68,15,40,212                        // movaps        %xmm4,%xmm10
+  .byte  68,15,89,213                        // mulps         %xmm5,%xmm10
+  .byte  68,15,40,220                        // movaps        %xmm4,%xmm11
+  .byte  68,15,89,222                        // mulps         %xmm6,%xmm11
+  .byte  68,15,40,196                        // movaps        %xmm4,%xmm8
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  69,15,40,241                        // movaps        %xmm9,%xmm14
+  .byte  68,15,41,116,36,184                 // movaps        %xmm14,-0x48(%rsp)
+  .byte  15,40,217                           // movaps        %xmm1,%xmm3
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  68,15,41,76,36,232                  // movaps        %xmm9,-0x18(%rsp)
+  .byte  65,15,95,217                        // maxps         %xmm9,%xmm3
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,95,211                           // maxps         %xmm3,%xmm2
+  .byte  68,15,40,225                        // movaps        %xmm1,%xmm12
+  .byte  69,15,93,225                        // minps         %xmm9,%xmm12
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  65,15,93,220                        // minps         %xmm12,%xmm3
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,41,100,36,168                 // movaps        %xmm12,-0x58(%rsp)
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,93,192                        // minps         %xmm8,%xmm0
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,93,216                           // minps         %xmm0,%xmm3
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,95,192                        // maxps         %xmm8,%xmm0
+  .byte  65,15,40,250                        // movaps        %xmm10,%xmm7
+  .byte  15,95,248                           // maxps         %xmm0,%xmm7
+  .byte  15,92,251                           // subps         %xmm3,%xmm7
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  68,15,94,215                        // divps         %xmm7,%xmm10
+  .byte  68,15,92,219                        // subps         %xmm3,%xmm11
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  68,15,94,223                        // divps         %xmm7,%xmm11
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  68,15,94,199                        // divps         %xmm7,%xmm8
+  .byte  15,194,248,4                        // cmpneqps      %xmm0,%xmm7
+  .byte  68,15,84,215                        // andps         %xmm7,%xmm10
+  .byte  68,15,84,223                        // andps         %xmm7,%xmm11
+  .byte  68,15,84,199                        // andps         %xmm7,%xmm8
+  .byte  15,40,21,125,65,0,0                 // movaps        0x417d(%rip),%xmm2        # 52b0 <_sk_callback_sse41+0x2d3>
+  .byte  15,40,221                           // movaps        %xmm5,%xmm3
+  .byte  15,89,218                           // mulps         %xmm2,%xmm3
+  .byte  15,40,13,128,65,0,0                 // movaps        0x4180(%rip),%xmm1        # 52c0 <_sk_callback_sse41+0x2e3>
+  .byte  15,40,254                           // movaps        %xmm6,%xmm7
+  .byte  15,89,249                           // mulps         %xmm1,%xmm7
+  .byte  15,88,251                           // addps         %xmm3,%xmm7
+  .byte  68,15,40,45,127,65,0,0              // movaps        0x417f(%rip),%xmm13        # 52d0 <_sk_callback_sse41+0x2f3>
+  .byte  69,15,89,245                        // mulps         %xmm13,%xmm14
+  .byte  68,15,88,247                        // addps         %xmm7,%xmm14
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,89,218                           // mulps         %xmm2,%xmm3
+  .byte  65,15,40,251                        // movaps        %xmm11,%xmm7
+  .byte  15,89,249                           // mulps         %xmm1,%xmm7
+  .byte  15,88,251                           // addps         %xmm3,%xmm7
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  65,15,89,221                        // mulps         %xmm13,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  68,15,89,244                        // mulps         %xmm4,%xmm14
+  .byte  68,15,92,243                        // subps         %xmm3,%xmm14
+  .byte  69,15,88,214                        // addps         %xmm14,%xmm10
+  .byte  69,15,88,222                        // addps         %xmm14,%xmm11
+  .byte  69,15,88,240                        // addps         %xmm8,%xmm14
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,93,198                        // minps         %xmm14,%xmm0
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,93,216                           // minps         %xmm0,%xmm3
+  .byte  65,15,89,210                        // mulps         %xmm10,%xmm2
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  69,15,89,238                        // mulps         %xmm14,%xmm13
+  .byte  68,15,88,233                        // addps         %xmm1,%xmm13
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  68,15,194,203,2                     // cmpleps       %xmm3,%xmm9
+  .byte  65,15,40,253                        // movaps        %xmm13,%xmm7
+  .byte  15,92,251                           // subps         %xmm3,%xmm7
+  .byte  69,15,40,250                        // movaps        %xmm10,%xmm15
+  .byte  69,15,92,253                        // subps         %xmm13,%xmm15
+  .byte  69,15,89,253                        // mulps         %xmm13,%xmm15
+  .byte  68,15,94,255                        // divps         %xmm7,%xmm15
+  .byte  69,15,88,253                        // addps         %xmm13,%xmm15
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,69,15,56,20,250                 // blendvps      %xmm0,%xmm10,%xmm15
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,95,198                        // maxps         %xmm14,%xmm0
+  .byte  68,15,95,208                        // maxps         %xmm0,%xmm10
+  .byte  15,40,212                           // movaps        %xmm4,%xmm2
+  .byte  65,15,89,212                        // mulps         %xmm12,%xmm2
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  69,15,194,194,1                     // cmpltps       %xmm10,%xmm8
+  .byte  65,15,40,223                        // movaps        %xmm15,%xmm3
+  .byte  65,15,92,221                        // subps         %xmm13,%xmm3
+  .byte  15,40,202                           // movaps        %xmm2,%xmm1
+  .byte  65,15,92,205                        // subps         %xmm13,%xmm1
+  .byte  15,89,217                           // mulps         %xmm1,%xmm3
+  .byte  69,15,92,213                        // subps         %xmm13,%xmm10
+  .byte  65,15,94,218                        // divps         %xmm10,%xmm3
+  .byte  65,15,88,221                        // addps         %xmm13,%xmm3
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  102,68,15,56,20,251                 // blendvps      %xmm0,%xmm3,%xmm15
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  68,15,94,231                        // divps         %xmm7,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,69,15,56,20,227                 // blendvps      %xmm0,%xmm11,%xmm12
+  .byte  65,15,40,220                        // movaps        %xmm12,%xmm3
+  .byte  65,15,92,221                        // subps         %xmm13,%xmm3
+  .byte  15,89,217                           // mulps         %xmm1,%xmm3
+  .byte  65,15,94,218                        // divps         %xmm10,%xmm3
+  .byte  65,15,88,221                        // addps         %xmm13,%xmm3
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  102,68,15,56,20,227                 // blendvps      %xmm0,%xmm3,%xmm12
+  .byte  69,15,40,222                        // movaps        %xmm14,%xmm11
+  .byte  69,15,92,221                        // subps         %xmm13,%xmm11
+  .byte  69,15,89,221                        // mulps         %xmm13,%xmm11
+  .byte  68,15,94,223                        // divps         %xmm7,%xmm11
+  .byte  69,15,88,221                        // addps         %xmm13,%xmm11
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,69,15,56,20,222                 // blendvps      %xmm0,%xmm14,%xmm11
+  .byte  65,15,40,251                        // movaps        %xmm11,%xmm7
+  .byte  65,15,92,253                        // subps         %xmm13,%xmm7
+  .byte  15,89,249                           // mulps         %xmm1,%xmm7
+  .byte  65,15,94,250                        // divps         %xmm10,%xmm7
+  .byte  65,15,88,253                        // addps         %xmm13,%xmm7
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  102,68,15,56,20,223                 // blendvps      %xmm0,%xmm7,%xmm11
+  .byte  68,15,40,13,69,64,0,0               // movaps        0x4045(%rip),%xmm9        # 52e0 <_sk_callback_sse41+0x303>
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,92,204                        // subps         %xmm4,%xmm9
+  .byte  15,40,124,36,168                    // movaps        -0x58(%rsp),%xmm7
+  .byte  15,88,231                           // addps         %xmm7,%xmm4
+  .byte  15,92,226                           // subps         %xmm2,%xmm4
+  .byte  15,40,220                           // movaps        %xmm4,%xmm3
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  68,15,95,248                        // maxps         %xmm0,%xmm15
+  .byte  68,15,95,224                        // maxps         %xmm0,%xmm12
+  .byte  68,15,95,216                        // maxps         %xmm0,%xmm11
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  15,40,68,36,200                     // movaps        -0x38(%rsp),%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,193                           // addps         %xmm1,%xmm0
+  .byte  65,15,88,199                        // addps         %xmm15,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  15,89,206                           // mulps         %xmm6,%xmm1
+  .byte  15,40,84,36,216                     // movaps        -0x28(%rsp),%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,209                           // addps         %xmm1,%xmm2
+  .byte  65,15,88,212                        // addps         %xmm12,%xmm2
+  .byte  15,40,202                           // movaps        %xmm2,%xmm1
+  .byte  68,15,89,68,36,232                  // mulps         -0x18(%rsp),%xmm8
+  .byte  15,40,84,36,184                     // movaps        -0x48(%rsp),%xmm2
+  .byte  68,15,89,202                        // mulps         %xmm2,%xmm9
+  .byte  69,15,88,200                        // addps         %xmm8,%xmm9
+  .byte  69,15,88,203                        // addps         %xmm11,%xmm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,40,238                           // movaps        %xmm6,%xmm5
+  .byte  15,40,242                           // movaps        %xmm2,%xmm6
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_color_sse41
+.globl _sk_color_sse41
+FUNCTION(_sk_color_sse41)
+_sk_color_sse41:
+  .byte  68,15,40,230                        // movaps        %xmm6,%xmm12
+  .byte  68,15,41,100,36,200                 // movaps        %xmm12,-0x38(%rsp)
+  .byte  68,15,40,221                        // movaps        %xmm5,%xmm11
+  .byte  68,15,41,92,36,216                  // movaps        %xmm11,-0x28(%rsp)
+  .byte  68,15,40,212                        // movaps        %xmm4,%xmm10
+  .byte  68,15,41,84,36,232                  // movaps        %xmm10,-0x18(%rsp)
+  .byte  15,40,243                           // movaps        %xmm3,%xmm6
+  .byte  15,41,84,36,184                     // movaps        %xmm2,-0x48(%rsp)
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,40,240                        // movaps        %xmm8,%xmm14
+  .byte  15,40,231                           // movaps        %xmm7,%xmm4
+  .byte  68,15,89,244                        // mulps         %xmm4,%xmm14
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,40,13,144,63,0,0              // movaps        0x3f90(%rip),%xmm9        # 52f0 <_sk_callback_sse41+0x313>
+  .byte  65,15,40,250                        // movaps        %xmm10,%xmm7
+  .byte  65,15,89,249                        // mulps         %xmm9,%xmm7
+  .byte  68,15,40,21,144,63,0,0              // movaps        0x3f90(%rip),%xmm10        # 5300 <_sk_callback_sse41+0x323>
+  .byte  65,15,40,219                        // movaps        %xmm11,%xmm3
+  .byte  65,15,89,218                        // mulps         %xmm10,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  68,15,40,29,141,63,0,0              // movaps        0x3f8d(%rip),%xmm11        # 5310 <_sk_callback_sse41+0x333>
+  .byte  69,15,40,236                        // movaps        %xmm12,%xmm13
+  .byte  69,15,89,235                        // mulps         %xmm11,%xmm13
+  .byte  68,15,88,235                        // addps         %xmm3,%xmm13
+  .byte  65,15,40,222                        // movaps        %xmm14,%xmm3
+  .byte  65,15,89,217                        // mulps         %xmm9,%xmm3
+  .byte  15,40,249                           // movaps        %xmm1,%xmm7
+  .byte  65,15,89,250                        // mulps         %xmm10,%xmm7
+  .byte  15,88,251                           // addps         %xmm3,%xmm7
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  65,15,89,219                        // mulps         %xmm11,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  68,15,89,238                        // mulps         %xmm6,%xmm13
+  .byte  68,15,92,235                        // subps         %xmm3,%xmm13
+  .byte  69,15,88,245                        // addps         %xmm13,%xmm14
+  .byte  65,15,88,205                        // addps         %xmm13,%xmm1
+  .byte  68,15,88,232                        // addps         %xmm0,%xmm13
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,93,197                        // minps         %xmm13,%xmm0
+  .byte  65,15,40,222                        // movaps        %xmm14,%xmm3
+  .byte  15,93,216                           // minps         %xmm0,%xmm3
+  .byte  69,15,89,206                        // mulps         %xmm14,%xmm9
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  69,15,88,209                        // addps         %xmm9,%xmm10
+  .byte  69,15,89,221                        // mulps         %xmm13,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  68,15,194,203,2                     // cmpleps       %xmm3,%xmm9
+  .byte  69,15,40,230                        // movaps        %xmm14,%xmm12
+  .byte  69,15,92,227                        // subps         %xmm11,%xmm12
+  .byte  69,15,89,227                        // mulps         %xmm11,%xmm12
+  .byte  65,15,40,251                        // movaps        %xmm11,%xmm7
+  .byte  15,92,251                           // subps         %xmm3,%xmm7
+  .byte  68,15,94,231                        // divps         %xmm7,%xmm12
+  .byte  69,15,88,227                        // addps         %xmm11,%xmm12
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,69,15,56,20,230                 // blendvps      %xmm0,%xmm14,%xmm12
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,95,197                        // maxps         %xmm13,%xmm0
+  .byte  65,15,40,214                        // movaps        %xmm14,%xmm2
+  .byte  15,95,208                           // maxps         %xmm0,%xmm2
+  .byte  15,40,222                           // movaps        %xmm6,%xmm3
+  .byte  15,89,220                           // mulps         %xmm4,%xmm3
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,194,210,1                     // cmpltps       %xmm2,%xmm10
+  .byte  69,15,40,244                        // movaps        %xmm12,%xmm14
+  .byte  69,15,92,243                        // subps         %xmm11,%xmm14
+  .byte  68,15,40,251                        // movaps        %xmm3,%xmm15
+  .byte  69,15,92,251                        // subps         %xmm11,%xmm15
+  .byte  69,15,89,247                        // mulps         %xmm15,%xmm14
+  .byte  65,15,92,211                        // subps         %xmm11,%xmm2
+  .byte  68,15,94,242                        // divps         %xmm2,%xmm14
+  .byte  69,15,88,243                        // addps         %xmm11,%xmm14
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,69,15,56,20,230                 // blendvps      %xmm0,%xmm14,%xmm12
+  .byte  68,15,40,241                        // movaps        %xmm1,%xmm14
+  .byte  69,15,92,243                        // subps         %xmm11,%xmm14
+  .byte  69,15,89,243                        // mulps         %xmm11,%xmm14
+  .byte  68,15,94,247                        // divps         %xmm7,%xmm14
+  .byte  69,15,88,243                        // addps         %xmm11,%xmm14
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,68,15,56,20,241                 // blendvps      %xmm0,%xmm1,%xmm14
+  .byte  65,15,40,206                        // movaps        %xmm14,%xmm1
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  65,15,89,207                        // mulps         %xmm15,%xmm1
+  .byte  15,94,202                           // divps         %xmm2,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,68,15,56,20,241                 // blendvps      %xmm0,%xmm1,%xmm14
+  .byte  65,15,40,205                        // movaps        %xmm13,%xmm1
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  15,94,207                           // divps         %xmm7,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,65,15,56,20,205                 // blendvps      %xmm0,%xmm13,%xmm1
+  .byte  15,40,249                           // movaps        %xmm1,%xmm7
+  .byte  65,15,92,251                        // subps         %xmm11,%xmm7
+  .byte  65,15,89,255                        // mulps         %xmm15,%xmm7
+  .byte  15,94,250                           // divps         %xmm2,%xmm7
+  .byte  65,15,88,251                        // addps         %xmm11,%xmm7
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,15,56,20,207                    // blendvps      %xmm0,%xmm7,%xmm1
+  .byte  68,15,40,13,73,62,0,0               // movaps        0x3e49(%rip),%xmm9        # 5320 <_sk_callback_sse41+0x343>
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  15,89,232                           // mulps         %xmm0,%xmm5
+  .byte  15,89,68,36,184                     // mulps         -0x48(%rsp),%xmm0
+  .byte  68,15,92,206                        // subps         %xmm6,%xmm9
+  .byte  15,88,244                           // addps         %xmm4,%xmm6
+  .byte  15,40,252                           // movaps        %xmm4,%xmm7
+  .byte  15,92,243                           // subps         %xmm3,%xmm6
+  .byte  15,40,222                           // movaps        %xmm6,%xmm3
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  68,15,95,226                        // maxps         %xmm2,%xmm12
+  .byte  68,15,95,242                        // maxps         %xmm2,%xmm14
+  .byte  15,95,202                           // maxps         %xmm2,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,40,100,36,232                    // movaps        -0x18(%rsp),%xmm4
+  .byte  15,89,212                           // mulps         %xmm4,%xmm2
+  .byte  68,15,88,194                        // addps         %xmm2,%xmm8
+  .byte  69,15,88,196                        // addps         %xmm12,%xmm8
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,40,116,36,216                    // movaps        -0x28(%rsp),%xmm6
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,40,213                        // movaps        %xmm5,%xmm10
+  .byte  68,15,88,210                        // addps         %xmm2,%xmm10
+  .byte  69,15,88,214                        // addps         %xmm14,%xmm10
+  .byte  15,40,84,36,200                     // movaps        -0x38(%rsp),%xmm2
+  .byte  68,15,89,202                        // mulps         %xmm2,%xmm9
+  .byte  68,15,88,200                        // addps         %xmm0,%xmm9
+  .byte  68,15,88,201                        // addps         %xmm1,%xmm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,238                           // movaps        %xmm6,%xmm5
+  .byte  15,40,242                           // movaps        %xmm2,%xmm6
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminosity_sse41
+.globl _sk_luminosity_sse41
+FUNCTION(_sk_luminosity_sse41)
+_sk_luminosity_sse41:
+  .byte  15,41,116,36,200                    // movaps        %xmm6,-0x38(%rsp)
+  .byte  15,41,108,36,232                    // movaps        %xmm5,-0x18(%rsp)
+  .byte  68,15,40,196                        // movaps        %xmm4,%xmm8
+  .byte  68,15,41,68,36,216                  // movaps        %xmm8,-0x28(%rsp)
+  .byte  15,41,84,36,184                     // movaps        %xmm2,-0x48(%rsp)
+  .byte  15,40,224                           // movaps        %xmm0,%xmm4
+  .byte  68,15,40,219                        // movaps        %xmm3,%xmm11
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  68,15,40,5,161,61,0,0               // movaps        0x3da1(%rip),%xmm8        # 5330 <_sk_callback_sse41+0x353>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  68,15,40,21,165,61,0,0              // movaps        0x3da5(%rip),%xmm10        # 5340 <_sk_callback_sse41+0x363>
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  65,15,89,234                        // mulps         %xmm10,%xmm5
+  .byte  15,88,232                           // addps         %xmm0,%xmm5
+  .byte  68,15,40,37,163,61,0,0              // movaps        0x3da3(%rip),%xmm12        # 5350 <_sk_callback_sse41+0x373>
+  .byte  68,15,40,242                        // movaps        %xmm2,%xmm14
+  .byte  69,15,89,244                        // mulps         %xmm12,%xmm14
+  .byte  68,15,88,245                        // addps         %xmm5,%xmm14
+  .byte  65,15,40,235                        // movaps        %xmm11,%xmm5
+  .byte  65,15,89,232                        // mulps         %xmm8,%xmm5
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  65,15,89,210                        // mulps         %xmm10,%xmm2
+  .byte  15,88,213                           // addps         %xmm5,%xmm2
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,89,198                           // mulps         %xmm6,%xmm0
+  .byte  15,40,232                           // movaps        %xmm0,%xmm5
+  .byte  65,15,89,236                        // mulps         %xmm12,%xmm5
+  .byte  15,88,234                           // addps         %xmm2,%xmm5
+  .byte  68,15,89,247                        // mulps         %xmm7,%xmm14
+  .byte  68,15,92,245                        // subps         %xmm5,%xmm14
+  .byte  69,15,88,222                        // addps         %xmm14,%xmm11
+  .byte  69,15,88,206                        // addps         %xmm14,%xmm9
+  .byte  68,15,88,240                        // addps         %xmm0,%xmm14
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,93,198                        // minps         %xmm14,%xmm0
+  .byte  65,15,40,235                        // movaps        %xmm11,%xmm5
+  .byte  15,93,232                           // minps         %xmm0,%xmm5
+  .byte  69,15,89,195                        // mulps         %xmm11,%xmm8
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,208                        // addps         %xmm8,%xmm10
+  .byte  69,15,89,230                        // mulps         %xmm14,%xmm12
+  .byte  69,15,88,226                        // addps         %xmm10,%xmm12
+  .byte  69,15,87,210                        // xorps         %xmm10,%xmm10
+  .byte  68,15,194,213,2                     // cmpleps       %xmm5,%xmm10
+  .byte  69,15,40,235                        // movaps        %xmm11,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  69,15,89,236                        // mulps         %xmm12,%xmm13
+  .byte  65,15,40,244                        // movaps        %xmm12,%xmm6
+  .byte  15,92,245                           // subps         %xmm5,%xmm6
+  .byte  68,15,94,238                        // divps         %xmm6,%xmm13
+  .byte  69,15,88,236                        // addps         %xmm12,%xmm13
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,69,15,56,20,235                 // blendvps      %xmm0,%xmm11,%xmm13
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,95,198                        // maxps         %xmm14,%xmm0
+  .byte  65,15,40,211                        // movaps        %xmm11,%xmm2
+  .byte  15,95,208                           // maxps         %xmm0,%xmm2
+  .byte  15,40,235                           // movaps        %xmm3,%xmm5
+  .byte  15,89,239                           // mulps         %xmm7,%xmm5
+  .byte  68,15,40,221                        // movaps        %xmm5,%xmm11
+  .byte  68,15,194,218,1                     // cmpltps       %xmm2,%xmm11
+  .byte  69,15,40,197                        // movaps        %xmm13,%xmm8
+  .byte  69,15,92,196                        // subps         %xmm12,%xmm8
+  .byte  68,15,40,253                        // movaps        %xmm5,%xmm15
+  .byte  69,15,92,252                        // subps         %xmm12,%xmm15
+  .byte  69,15,89,199                        // mulps         %xmm15,%xmm8
+  .byte  65,15,92,212                        // subps         %xmm12,%xmm2
+  .byte  68,15,94,194                        // divps         %xmm2,%xmm8
+  .byte  69,15,88,196                        // addps         %xmm12,%xmm8
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  102,69,15,56,20,232                 // blendvps      %xmm0,%xmm8,%xmm13
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  69,15,92,196                        // subps         %xmm12,%xmm8
+  .byte  69,15,89,196                        // mulps         %xmm12,%xmm8
+  .byte  68,15,94,198                        // divps         %xmm6,%xmm8
+  .byte  69,15,88,196                        // addps         %xmm12,%xmm8
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,69,15,56,20,193                 // blendvps      %xmm0,%xmm9,%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  69,15,89,207                        // mulps         %xmm15,%xmm9
+  .byte  68,15,94,202                        // divps         %xmm2,%xmm9
+  .byte  69,15,88,204                        // addps         %xmm12,%xmm9
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  102,69,15,56,20,193                 // blendvps      %xmm0,%xmm9,%xmm8
+  .byte  69,15,40,206                        // movaps        %xmm14,%xmm9
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  69,15,89,204                        // mulps         %xmm12,%xmm9
+  .byte  68,15,94,206                        // divps         %xmm6,%xmm9
+  .byte  69,15,88,204                        // addps         %xmm12,%xmm9
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,69,15,56,20,206                 // blendvps      %xmm0,%xmm14,%xmm9
+  .byte  65,15,40,241                        // movaps        %xmm9,%xmm6
+  .byte  65,15,92,244                        // subps         %xmm12,%xmm6
+  .byte  65,15,89,247                        // mulps         %xmm15,%xmm6
+  .byte  15,94,242                           // divps         %xmm2,%xmm6
+  .byte  65,15,88,244                        // addps         %xmm12,%xmm6
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  102,68,15,56,20,206                 // blendvps      %xmm0,%xmm6,%xmm9
+  .byte  15,40,5,89,60,0,0                   // movaps        0x3c59(%rip),%xmm0        # 5360 <_sk_callback_sse41+0x383>
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,92,215                           // subps         %xmm7,%xmm2
+  .byte  15,89,226                           // mulps         %xmm2,%xmm4
+  .byte  15,89,202                           // mulps         %xmm2,%xmm1
+  .byte  15,89,84,36,184                     // mulps         -0x48(%rsp),%xmm2
+  .byte  15,92,195                           // subps         %xmm3,%xmm0
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  15,92,221                           // subps         %xmm5,%xmm3
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  68,15,95,237                        // maxps         %xmm5,%xmm13
+  .byte  68,15,95,197                        // maxps         %xmm5,%xmm8
+  .byte  68,15,95,205                        // maxps         %xmm5,%xmm9
+  .byte  15,40,232                           // movaps        %xmm0,%xmm5
+  .byte  68,15,40,84,36,216                  // movaps        -0x28(%rsp),%xmm10
+  .byte  65,15,89,234                        // mulps         %xmm10,%xmm5
+  .byte  15,88,229                           // addps         %xmm5,%xmm4
+  .byte  65,15,88,229                        // addps         %xmm13,%xmm4
+  .byte  15,40,240                           // movaps        %xmm0,%xmm6
+  .byte  15,40,108,36,232                    // movaps        -0x18(%rsp),%xmm5
+  .byte  15,89,245                           // mulps         %xmm5,%xmm6
+  .byte  15,88,206                           // addps         %xmm6,%xmm1
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  15,40,116,36,200                    // movaps        -0x38(%rsp),%xmm6
+  .byte  15,89,198                           // mulps         %xmm6,%xmm0
+  .byte  15,88,194                           // addps         %xmm2,%xmm0
+  .byte  68,15,88,200                        // addps         %xmm0,%xmm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  65,15,40,226                        // movaps        %xmm10,%xmm4
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_rgba_8888_sse41
+.globl _sk_srcover_rgba_8888_sse41
+FUNCTION(_sk_srcover_rgba_8888_sse41)
+_sk_srcover_rgba_8888_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,217,0,0,0                    // jne           185b <_sk_srcover_rgba_8888_sse41+0xe7>
+  .byte  243,15,111,60,144                   // movdqu        (%rax,%rdx,4),%xmm7
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  102,15,111,37,222,59,0,0            // movdqa        0x3bde(%rip),%xmm4        # 5370 <_sk_callback_sse41+0x393>
+  .byte  102,15,219,231                      // pand          %xmm7,%xmm4
+  .byte  15,91,228                           // cvtdq2ps      %xmm4,%xmm4
+  .byte  102,15,111,239                      // movdqa        %xmm7,%xmm5
+  .byte  102,15,56,0,45,218,59,0,0           // pshufb        0x3bda(%rip),%xmm5        # 5380 <_sk_callback_sse41+0x3a3>
+  .byte  15,91,237                           // cvtdq2ps      %xmm5,%xmm5
+  .byte  102,15,111,247                      // movdqa        %xmm7,%xmm6
+  .byte  102,15,56,0,53,218,59,0,0           // pshufb        0x3bda(%rip),%xmm6        # 5390 <_sk_callback_sse41+0x3b3>
+  .byte  15,91,246                           // cvtdq2ps      %xmm6,%xmm6
+  .byte  102,15,114,215,24                   // psrld         $0x18,%xmm7
+  .byte  15,91,255                           // cvtdq2ps      %xmm7,%xmm7
+  .byte  68,15,40,5,215,59,0,0               // movaps        0x3bd7(%rip),%xmm8        # 53a0 <_sk_callback_sse41+0x3c3>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  68,15,40,37,219,59,0,0              // movaps        0x3bdb(%rip),%xmm12        # 53b0 <_sk_callback_sse41+0x3d3>
+  .byte  65,15,89,196                        // mulps         %xmm12,%xmm0
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  68,15,88,200                        // addps         %xmm0,%xmm9
+  .byte  65,15,89,204                        // mulps         %xmm12,%xmm1
+  .byte  69,15,40,208                        // movaps        %xmm8,%xmm10
+  .byte  68,15,89,213                        // mulps         %xmm5,%xmm10
+  .byte  68,15,88,209                        // addps         %xmm1,%xmm10
+  .byte  65,15,89,212                        // mulps         %xmm12,%xmm2
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,222                        // mulps         %xmm6,%xmm11
+  .byte  68,15,88,218                        // addps         %xmm2,%xmm11
+  .byte  65,15,89,220                        // mulps         %xmm12,%xmm3
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,88,195                        // addps         %xmm3,%xmm8
+  .byte  102,65,15,91,193                    // cvtps2dq      %xmm9,%xmm0
+  .byte  102,65,15,91,202                    // cvtps2dq      %xmm10,%xmm1
+  .byte  102,15,114,241,8                    // pslld         $0x8,%xmm1
+  .byte  102,15,235,200                      // por           %xmm0,%xmm1
+  .byte  102,65,15,91,211                    // cvtps2dq      %xmm11,%xmm2
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,65,15,91,192                    // cvtps2dq      %xmm8,%xmm0
+  .byte  102,15,114,240,24                   // pslld         $0x18,%xmm0
+  .byte  102,15,235,194                      // por           %xmm2,%xmm0
+  .byte  102,15,235,193                      // por           %xmm1,%xmm0
+  .byte  117,89                              // jne           189b <_sk_srcover_rgba_8888_sse41+0x127>
+  .byte  243,15,127,4,144                    // movdqu        %xmm0,(%rax,%rdx,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  65,15,40,211                        // movaps        %xmm11,%xmm2
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,255                      // pxor          %xmm7,%xmm7
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,35                              // je            188f <_sk_srcover_rgba_8888_sse41+0x11b>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            1887 <_sk_srcover_rgba_8888_sse41+0x113>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  15,133,11,255,255,255               // jne           1787 <_sk_srcover_rgba_8888_sse41+0x13>
+  .byte  102,15,110,100,144,8                // movd          0x8(%rax,%rdx,4),%xmm4
+  .byte  102,15,112,252,69                   // pshufd        $0x45,%xmm4,%xmm7
+  .byte  102,15,58,34,124,144,4,1            // pinsrd        $0x1,0x4(%rax,%rdx,4),%xmm7
+  .byte  102,15,58,34,60,144,0               // pinsrd        $0x0,(%rax,%rdx,4),%xmm7
+  .byte  233,236,254,255,255                 // jmpq          1787 <_sk_srcover_rgba_8888_sse41+0x13>
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,28                              // je            18c4 <_sk_srcover_rgba_8888_sse41+0x150>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,14                              // je            18bc <_sk_srcover_rgba_8888_sse41+0x148>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,147                             // jne           1847 <_sk_srcover_rgba_8888_sse41+0xd3>
+  .byte  102,15,58,22,68,144,8,2             // pextrd        $0x2,%xmm0,0x8(%rax,%rdx,4)
+  .byte  102,15,58,22,68,144,4,1             // pextrd        $0x1,%xmm0,0x4(%rax,%rdx,4)
+  .byte  102,15,126,4,144                    // movd          %xmm0,(%rax,%rdx,4)
+  .byte  233,121,255,255,255                 // jmpq          1847 <_sk_srcover_rgba_8888_sse41+0xd3>
+
+HIDDEN _sk_clamp_0_sse41
+.globl _sk_clamp_0_sse41
+FUNCTION(_sk_clamp_0_sse41)
+_sk_clamp_0_sse41:
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  65,15,95,192                        // maxps         %xmm8,%xmm0
+  .byte  65,15,95,200                        // maxps         %xmm8,%xmm1
+  .byte  65,15,95,208                        // maxps         %xmm8,%xmm2
+  .byte  65,15,95,216                        // maxps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_1_sse41
+.globl _sk_clamp_1_sse41
+FUNCTION(_sk_clamp_1_sse41)
+_sk_clamp_1_sse41:
+  .byte  68,15,40,5,210,58,0,0               // movaps        0x3ad2(%rip),%xmm8        # 53c0 <_sk_callback_sse41+0x3e3>
+  .byte  65,15,93,192                        // minps         %xmm8,%xmm0
+  .byte  65,15,93,200                        // minps         %xmm8,%xmm1
+  .byte  65,15,93,208                        // minps         %xmm8,%xmm2
+  .byte  65,15,93,216                        // minps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_a_sse41
+.globl _sk_clamp_a_sse41
+FUNCTION(_sk_clamp_a_sse41)
+_sk_clamp_a_sse41:
+  .byte  15,93,29,199,58,0,0                 // minps         0x3ac7(%rip),%xmm3        # 53d0 <_sk_callback_sse41+0x3f3>
+  .byte  15,93,195                           // minps         %xmm3,%xmm0
+  .byte  15,93,203                           // minps         %xmm3,%xmm1
+  .byte  15,93,211                           // minps         %xmm3,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_set_rgb_sse41
+.globl _sk_set_rgb_sse41
+FUNCTION(_sk_set_rgb_sse41)
+_sk_set_rgb_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_rb_sse41
+.globl _sk_swap_rb_sse41
+FUNCTION(_sk_swap_rb_sse41)
+_sk_swap_rb_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_sse41
+.globl _sk_swap_sse41
+FUNCTION(_sk_swap_sse41)
+_sk_swap_sse41:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  68,15,40,216                        // movaps        %xmm0,%xmm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  65,15,40,227                        // movaps        %xmm11,%xmm4
+  .byte  65,15,40,234                        // movaps        %xmm10,%xmm5
+  .byte  65,15,40,241                        // movaps        %xmm9,%xmm6
+  .byte  65,15,40,248                        // movaps        %xmm8,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_src_dst_sse41
+.globl _sk_move_src_dst_sse41
+FUNCTION(_sk_move_src_dst_sse41)
+_sk_move_src_dst_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,224                           // movaps        %xmm0,%xmm4
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  15,40,242                           // movaps        %xmm2,%xmm6
+  .byte  15,40,251                           // movaps        %xmm3,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_dst_src_sse41
+.globl _sk_move_dst_src_sse41
+FUNCTION(_sk_move_dst_src_sse41)
+_sk_move_dst_src_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_premul_sse41
+.globl _sk_premul_sse41
+FUNCTION(_sk_premul_sse41)
+_sk_premul_sse41:
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,89,203                           // mulps         %xmm3,%xmm1
+  .byte  15,89,211                           // mulps         %xmm3,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_unpremul_sse41
+.globl _sk_unpremul_sse41
+FUNCTION(_sk_unpremul_sse41)
+_sk_unpremul_sse41:
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,40,13,50,58,0,0               // movaps        0x3a32(%rip),%xmm9        # 53e0 <_sk_callback_sse41+0x403>
+  .byte  68,15,94,203                        // divps         %xmm3,%xmm9
+  .byte  68,15,194,195,4                     // cmpneqps      %xmm3,%xmm8
+  .byte  69,15,84,193                        // andps         %xmm9,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_from_srgb_sse41
+.globl _sk_from_srgb_sse41
+FUNCTION(_sk_from_srgb_sse41)
+_sk_from_srgb_sse41:
+  .byte  68,15,40,29,29,58,0,0               // movaps        0x3a1d(%rip),%xmm11        # 53f0 <_sk_callback_sse41+0x413>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  69,15,89,210                        // mulps         %xmm10,%xmm10
+  .byte  68,15,40,37,21,58,0,0               // movaps        0x3a15(%rip),%xmm12        # 5400 <_sk_callback_sse41+0x423>
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,89,196                        // mulps         %xmm12,%xmm8
+  .byte  68,15,40,45,21,58,0,0               // movaps        0x3a15(%rip),%xmm13        # 5410 <_sk_callback_sse41+0x433>
+  .byte  69,15,88,197                        // addps         %xmm13,%xmm8
+  .byte  69,15,89,194                        // mulps         %xmm10,%xmm8
+  .byte  68,15,40,53,21,58,0,0               // movaps        0x3a15(%rip),%xmm14        # 5420 <_sk_callback_sse41+0x443>
+  .byte  69,15,88,198                        // addps         %xmm14,%xmm8
+  .byte  68,15,40,61,25,58,0,0               // movaps        0x3a19(%rip),%xmm15        # 5430 <_sk_callback_sse41+0x453>
+  .byte  65,15,194,199,1                     // cmpltps       %xmm15,%xmm0
+  .byte  102,69,15,56,20,193                 // blendvps      %xmm0,%xmm9,%xmm8
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,89,211                        // mulps         %xmm11,%xmm10
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  15,89,192                           // mulps         %xmm0,%xmm0
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  69,15,89,204                        // mulps         %xmm12,%xmm9
+  .byte  69,15,88,205                        // addps         %xmm13,%xmm9
+  .byte  68,15,89,200                        // mulps         %xmm0,%xmm9
+  .byte  69,15,88,206                        // addps         %xmm14,%xmm9
+  .byte  65,15,194,207,1                     // cmpltps       %xmm15,%xmm1
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  102,69,15,56,20,202                 // blendvps      %xmm0,%xmm10,%xmm9
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  68,15,40,210                        // movaps        %xmm2,%xmm10
+  .byte  69,15,89,210                        // mulps         %xmm10,%xmm10
+  .byte  68,15,89,226                        // mulps         %xmm2,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,212                        // mulps         %xmm12,%xmm10
+  .byte  69,15,88,214                        // addps         %xmm14,%xmm10
+  .byte  65,15,194,215,1                     // cmpltps       %xmm15,%xmm2
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  102,69,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_to_srgb_sse41
+.globl _sk_to_srgb_sse41
+FUNCTION(_sk_to_srgb_sse41)
+_sk_to_srgb_sse41:
+  .byte  15,41,124,36,232                    // movaps        %xmm7,-0x18(%rsp)
+  .byte  15,40,254                           // movaps        %xmm6,%xmm7
+  .byte  15,40,245                           // movaps        %xmm5,%xmm6
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  15,40,218                           // movaps        %xmm2,%xmm3
+  .byte  15,40,209                           // movaps        %xmm1,%xmm2
+  .byte  68,15,82,192                        // rsqrtps       %xmm0,%xmm8
+  .byte  68,15,40,29,145,57,0,0              // movaps        0x3991(%rip),%xmm11        # 5440 <_sk_callback_sse41+0x463>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  68,15,40,37,145,57,0,0              // movaps        0x3991(%rip),%xmm12        # 5450 <_sk_callback_sse41+0x473>
+  .byte  69,15,40,248                        // movaps        %xmm8,%xmm15
+  .byte  69,15,89,252                        // mulps         %xmm12,%xmm15
+  .byte  68,15,40,21,145,57,0,0              // movaps        0x3991(%rip),%xmm10        # 5460 <_sk_callback_sse41+0x483>
+  .byte  69,15,88,250                        // addps         %xmm10,%xmm15
+  .byte  69,15,89,248                        // mulps         %xmm8,%xmm15
+  .byte  68,15,40,45,145,57,0,0              // movaps        0x3991(%rip),%xmm13        # 5470 <_sk_callback_sse41+0x493>
+  .byte  69,15,88,253                        // addps         %xmm13,%xmm15
+  .byte  68,15,40,53,149,57,0,0              // movaps        0x3995(%rip),%xmm14        # 5480 <_sk_callback_sse41+0x4a3>
+  .byte  69,15,88,198                        // addps         %xmm14,%xmm8
+  .byte  69,15,83,192                        // rcpps         %xmm8,%xmm8
+  .byte  69,15,89,199                        // mulps         %xmm15,%xmm8
+  .byte  68,15,40,61,145,57,0,0              // movaps        0x3991(%rip),%xmm15        # 5490 <_sk_callback_sse41+0x4b3>
+  .byte  65,15,194,199,1                     // cmpltps       %xmm15,%xmm0
+  .byte  102,69,15,56,20,193                 // blendvps      %xmm0,%xmm9,%xmm8
+  .byte  68,15,82,202                        // rsqrtps       %xmm2,%xmm9
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,89,196                        // mulps         %xmm12,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,88,197                        // addps         %xmm13,%xmm0
+  .byte  69,15,88,206                        // addps         %xmm14,%xmm9
+  .byte  69,15,83,201                        // rcpps         %xmm9,%xmm9
+  .byte  68,15,89,200                        // mulps         %xmm0,%xmm9
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  65,15,194,215,1                     // cmpltps       %xmm15,%xmm2
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  102,68,15,56,20,201                 // blendvps      %xmm0,%xmm1,%xmm9
+  .byte  15,82,195                           // rsqrtps       %xmm3,%xmm0
+  .byte  68,15,89,224                        // mulps         %xmm0,%xmm12
+  .byte  69,15,88,226                        // addps         %xmm10,%xmm12
+  .byte  68,15,89,224                        // mulps         %xmm0,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  65,15,88,198                        // addps         %xmm14,%xmm0
+  .byte  68,15,83,208                        // rcpps         %xmm0,%xmm10
+  .byte  69,15,89,212                        // mulps         %xmm12,%xmm10
+  .byte  68,15,89,219                        // mulps         %xmm3,%xmm11
+  .byte  65,15,194,223,1                     // cmpltps       %xmm15,%xmm3
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  102,69,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  15,40,220                           // movaps        %xmm4,%xmm3
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,40,238                           // movaps        %xmm6,%xmm5
+  .byte  15,40,247                           // movaps        %xmm7,%xmm6
+  .byte  15,40,124,36,232                    // movaps        -0x18(%rsp),%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_rgb_to_hsl_sse41
+.globl _sk_rgb_to_hsl_sse41
+FUNCTION(_sk_rgb_to_hsl_sse41)
+_sk_rgb_to_hsl_sse41:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  69,15,95,217                        // maxps         %xmm9,%xmm11
+  .byte  68,15,95,218                        // maxps         %xmm2,%xmm11
+  .byte  69,15,40,224                        // movaps        %xmm8,%xmm12
+  .byte  69,15,93,225                        // minps         %xmm9,%xmm12
+  .byte  68,15,93,226                        // minps         %xmm2,%xmm12
+  .byte  65,15,40,203                        // movaps        %xmm11,%xmm1
+  .byte  65,15,92,204                        // subps         %xmm12,%xmm1
+  .byte  68,15,40,53,222,56,0,0              // movaps        0x38de(%rip),%xmm14        # 54a0 <_sk_callback_sse41+0x4c3>
+  .byte  68,15,94,241                        // divps         %xmm1,%xmm14
+  .byte  69,15,40,211                        // movaps        %xmm11,%xmm10
+  .byte  69,15,194,208,0                     // cmpeqps       %xmm8,%xmm10
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  15,92,194                           // subps         %xmm2,%xmm0
+  .byte  65,15,89,198                        // mulps         %xmm14,%xmm0
+  .byte  69,15,40,249                        // movaps        %xmm9,%xmm15
+  .byte  68,15,194,250,1                     // cmpltps       %xmm2,%xmm15
+  .byte  68,15,84,61,197,56,0,0              // andps         0x38c5(%rip),%xmm15        # 54b0 <_sk_callback_sse41+0x4d3>
+  .byte  68,15,88,248                        // addps         %xmm0,%xmm15
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,194,193,0                     // cmpeqps       %xmm9,%xmm0
+  .byte  65,15,92,208                        // subps         %xmm8,%xmm2
+  .byte  65,15,89,214                        // mulps         %xmm14,%xmm2
+  .byte  68,15,40,45,184,56,0,0              // movaps        0x38b8(%rip),%xmm13        # 54c0 <_sk_callback_sse41+0x4e3>
+  .byte  65,15,88,213                        // addps         %xmm13,%xmm2
+  .byte  69,15,92,193                        // subps         %xmm9,%xmm8
+  .byte  69,15,89,198                        // mulps         %xmm14,%xmm8
+  .byte  68,15,88,5,180,56,0,0               // addps         0x38b4(%rip),%xmm8        # 54d0 <_sk_callback_sse41+0x4f3>
+  .byte  102,68,15,56,20,194                 // blendvps      %xmm0,%xmm2,%xmm8
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  102,69,15,56,20,199                 // blendvps      %xmm0,%xmm15,%xmm8
+  .byte  68,15,89,5,172,56,0,0               // mulps         0x38ac(%rip),%xmm8        # 54e0 <_sk_callback_sse41+0x503>
+  .byte  69,15,40,203                        // movaps        %xmm11,%xmm9
+  .byte  69,15,194,204,4                     // cmpneqps      %xmm12,%xmm9
+  .byte  69,15,84,193                        // andps         %xmm9,%xmm8
+  .byte  69,15,92,235                        // subps         %xmm11,%xmm13
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  15,40,5,160,56,0,0                  // movaps        0x38a0(%rip),%xmm0        # 54f0 <_sk_callback_sse41+0x513>
+  .byte  65,15,40,211                        // movaps        %xmm11,%xmm2
+  .byte  15,89,208                           // mulps         %xmm0,%xmm2
+  .byte  15,194,194,1                        // cmpltps       %xmm2,%xmm0
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  102,69,15,56,20,221                 // blendvps      %xmm0,%xmm13,%xmm11
+  .byte  65,15,94,203                        // divps         %xmm11,%xmm1
+  .byte  65,15,84,201                        // andps         %xmm9,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hsl_to_rgb_sse41
+.globl _sk_hsl_to_rgb_sse41
+FUNCTION(_sk_hsl_to_rgb_sse41)
+_sk_hsl_to_rgb_sse41:
+  .byte  15,41,124,36,232                    // movaps        %xmm7,-0x18(%rsp)
+  .byte  15,41,116,36,216                    // movaps        %xmm6,-0x28(%rsp)
+  .byte  15,41,108,36,200                    // movaps        %xmm5,-0x38(%rsp)
+  .byte  15,41,100,36,184                    // movaps        %xmm4,-0x48(%rsp)
+  .byte  15,41,92,36,168                     // movaps        %xmm3,-0x58(%rsp)
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  68,15,40,13,102,56,0,0              // movaps        0x3866(%rip),%xmm9        # 5500 <_sk_callback_sse41+0x523>
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  15,194,194,2                        // cmpleps       %xmm2,%xmm0
+  .byte  15,40,217                           // movaps        %xmm1,%xmm3
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  15,89,234                           // mulps         %xmm2,%xmm5
+  .byte  15,92,221                           // subps         %xmm5,%xmm3
+  .byte  102,15,56,20,235                    // blendvps      %xmm0,%xmm3,%xmm5
+  .byte  15,88,234                           // addps         %xmm2,%xmm5
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  15,41,84,36,152                     // movaps        %xmm2,-0x68(%rsp)
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  68,15,92,197                        // subps         %xmm5,%xmm8
+  .byte  68,15,40,53,65,56,0,0               // movaps        0x3841(%rip),%xmm14        # 5510 <_sk_callback_sse41+0x533>
+  .byte  69,15,88,242                        // addps         %xmm10,%xmm14
+  .byte  102,65,15,58,8,198,1                // roundps       $0x1,%xmm14,%xmm0
+  .byte  68,15,92,240                        // subps         %xmm0,%xmm14
+  .byte  68,15,40,29,58,56,0,0               // movaps        0x383a(%rip),%xmm11        # 5520 <_sk_callback_sse41+0x543>
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,194,198,2                     // cmpleps       %xmm14,%xmm0
+  .byte  15,40,245                           // movaps        %xmm5,%xmm6
+  .byte  65,15,92,240                        // subps         %xmm8,%xmm6
+  .byte  15,40,61,51,56,0,0                  // movaps        0x3833(%rip),%xmm7        # 5530 <_sk_callback_sse41+0x553>
+  .byte  69,15,40,238                        // movaps        %xmm14,%xmm13
+  .byte  68,15,89,239                        // mulps         %xmm7,%xmm13
+  .byte  15,40,29,52,56,0,0                  // movaps        0x3834(%rip),%xmm3        # 5540 <_sk_callback_sse41+0x563>
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,89,230                        // mulps         %xmm6,%xmm12
+  .byte  69,15,88,224                        // addps         %xmm8,%xmm12
+  .byte  102,69,15,56,20,224                 // blendvps      %xmm0,%xmm8,%xmm12
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,194,198,2                     // cmpleps       %xmm14,%xmm0
+  .byte  68,15,40,253                        // movaps        %xmm5,%xmm15
+  .byte  102,69,15,56,20,252                 // blendvps      %xmm0,%xmm12,%xmm15
+  .byte  68,15,40,37,19,56,0,0               // movaps        0x3813(%rip),%xmm12        # 5550 <_sk_callback_sse41+0x573>
+  .byte  65,15,40,196                        // movaps        %xmm12,%xmm0
+  .byte  65,15,194,198,2                     // cmpleps       %xmm14,%xmm0
+  .byte  68,15,89,238                        // mulps         %xmm6,%xmm13
+  .byte  69,15,88,232                        // addps         %xmm8,%xmm13
+  .byte  102,69,15,56,20,239                 // blendvps      %xmm0,%xmm15,%xmm13
+  .byte  69,15,87,246                        // xorps         %xmm14,%xmm14
+  .byte  68,15,194,241,0                     // cmpeqps       %xmm1,%xmm14
+  .byte  65,15,40,198                        // movaps        %xmm14,%xmm0
+  .byte  102,68,15,56,20,234                 // blendvps      %xmm0,%xmm2,%xmm13
+  .byte  102,65,15,58,8,194,1                // roundps       $0x1,%xmm10,%xmm0
+  .byte  69,15,40,250                        // movaps        %xmm10,%xmm15
+  .byte  68,15,92,248                        // subps         %xmm0,%xmm15
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,194,199,2                     // cmpleps       %xmm15,%xmm0
+  .byte  65,15,40,207                        // movaps        %xmm15,%xmm1
+  .byte  15,89,207                           // mulps         %xmm7,%xmm1
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,92,209                           // subps         %xmm1,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  65,15,88,208                        // addps         %xmm8,%xmm2
+  .byte  102,65,15,56,20,208                 // blendvps      %xmm0,%xmm8,%xmm2
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,194,199,2                     // cmpleps       %xmm15,%xmm0
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  102,15,56,20,226                    // blendvps      %xmm0,%xmm2,%xmm4
+  .byte  65,15,40,196                        // movaps        %xmm12,%xmm0
+  .byte  65,15,194,199,2                     // cmpleps       %xmm15,%xmm0
+  .byte  15,89,206                           // mulps         %xmm6,%xmm1
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  102,15,56,20,204                    // blendvps      %xmm0,%xmm4,%xmm1
+  .byte  65,15,40,198                        // movaps        %xmm14,%xmm0
+  .byte  15,40,84,36,152                     // movaps        -0x68(%rsp),%xmm2
+  .byte  102,15,56,20,202                    // blendvps      %xmm0,%xmm2,%xmm1
+  .byte  68,15,88,21,139,55,0,0              // addps         0x378b(%rip),%xmm10        # 5560 <_sk_callback_sse41+0x583>
+  .byte  102,65,15,58,8,194,1                // roundps       $0x1,%xmm10,%xmm0
+  .byte  68,15,92,208                        // subps         %xmm0,%xmm10
+  .byte  69,15,194,218,2                     // cmpleps       %xmm10,%xmm11
+  .byte  65,15,89,250                        // mulps         %xmm10,%xmm7
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  15,89,222                           // mulps         %xmm6,%xmm3
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  102,65,15,56,20,216                 // blendvps      %xmm0,%xmm8,%xmm3
+  .byte  69,15,194,202,2                     // cmpleps       %xmm10,%xmm9
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,15,56,20,235                    // blendvps      %xmm0,%xmm3,%xmm5
+  .byte  69,15,194,226,2                     // cmpleps       %xmm10,%xmm12
+  .byte  15,89,254                           // mulps         %xmm6,%xmm7
+  .byte  68,15,88,199                        // addps         %xmm7,%xmm8
+  .byte  65,15,40,196                        // movaps        %xmm12,%xmm0
+  .byte  102,68,15,56,20,197                 // blendvps      %xmm0,%xmm5,%xmm8
+  .byte  65,15,40,198                        // movaps        %xmm14,%xmm0
+  .byte  102,68,15,56,20,194                 // blendvps      %xmm0,%xmm2,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,197                        // movaps        %xmm13,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,40,92,36,168                     // movaps        -0x58(%rsp),%xmm3
+  .byte  15,40,100,36,184                    // movaps        -0x48(%rsp),%xmm4
+  .byte  15,40,108,36,200                    // movaps        -0x38(%rsp),%xmm5
+  .byte  15,40,116,36,216                    // movaps        -0x28(%rsp),%xmm6
+  .byte  15,40,124,36,232                    // movaps        -0x18(%rsp),%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_1_float_sse41
+.globl _sk_scale_1_float_sse41
+FUNCTION(_sk_scale_1_float_sse41)
+_sk_scale_1_float_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_u8_sse41
+.globl _sk_scale_u8_sse41
+FUNCTION(_sk_scale_u8_sse41)
+_sk_scale_u8_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,52                              // jne           1eae <_sk_scale_u8_sse41+0x3e>
+  .byte  102,69,15,56,49,4,18                // pmovzxbd      (%r10,%rdx,1),%xmm8
+  .byte  102,68,15,219,5,230,54,0,0          // pand          0x36e6(%rip),%xmm8        # 5570 <_sk_callback_sse41+0x593>
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,5,234,54,0,0               // mulps         0x36ea(%rip),%xmm8        # 5580 <_sk_callback_sse41+0x5a3>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,42                              // je            1eea <_sk_scale_u8_sse41+0x7a>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,23                              // je            1edd <_sk_scale_u8_sse41+0x6d>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,181                             // jne           1e81 <_sk_scale_u8_sse41+0x11>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,69                // pshufd        $0x45,%xmm8,%xmm8
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,68,15,58,34,192,1               // pinsrd        $0x1,%eax,%xmm8
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,68,15,58,34,192,0               // pinsrd        $0x0,%eax,%xmm8
+  .byte  235,137                             // jmp           1e81 <_sk_scale_u8_sse41+0x11>
+
+HIDDEN _sk_lerp_1_float_sse41
+.globl _sk_lerp_1_float_sse41
+FUNCTION(_sk_lerp_1_float_sse41)
+_sk_lerp_1_float_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,92,205                           // subps         %xmm5,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,92,214                           // subps         %xmm6,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lerp_u8_sse41
+.globl _sk_lerp_u8_sse41
+FUNCTION(_sk_lerp_u8_sse41)
+_sk_lerp_u8_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,72                              // jne           1f82 <_sk_lerp_u8_sse41+0x52>
+  .byte  102,69,15,56,49,4,18                // pmovzxbd      (%r10,%rdx,1),%xmm8
+  .byte  102,68,15,219,5,70,54,0,0           // pand          0x3646(%rip),%xmm8        # 5590 <_sk_callback_sse41+0x5b3>
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,5,74,54,0,0                // mulps         0x364a(%rip),%xmm8        # 55a0 <_sk_callback_sse41+0x5c3>
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,92,205                           // subps         %xmm5,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,92,214                           // subps         %xmm6,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,42                              // je            1fbe <_sk_lerp_u8_sse41+0x8e>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,23                              // je            1fb1 <_sk_lerp_u8_sse41+0x81>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,161                             // jne           1f41 <_sk_lerp_u8_sse41+0x11>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,69                // pshufd        $0x45,%xmm8,%xmm8
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,68,15,58,34,192,1               // pinsrd        $0x1,%eax,%xmm8
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,68,15,58,34,192,0               // pinsrd        $0x0,%eax,%xmm8
+  .byte  233,114,255,255,255                 // jmpq          1f41 <_sk_lerp_u8_sse41+0x11>
+
+HIDDEN _sk_lerp_565_sse41
+.globl _sk_lerp_565_sse41
+FUNCTION(_sk_lerp_565_sse41)
+_sk_lerp_565_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,152,0,0,0                    // jne           2075 <_sk_lerp_565_sse41+0xa6>
+  .byte  102,69,15,56,51,12,82               // pmovzxwd      (%r10,%rdx,2),%xmm9
+  .byte  102,68,15,111,5,195,53,0,0          // movdqa        0x35c3(%rip),%xmm8        # 55b0 <_sk_callback_sse41+0x5d3>
+  .byte  102,69,15,219,193                   // pand          %xmm9,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,5,194,53,0,0               // mulps         0x35c2(%rip),%xmm8        # 55c0 <_sk_callback_sse41+0x5e3>
+  .byte  102,68,15,111,21,201,53,0,0         // movdqa        0x35c9(%rip),%xmm10        # 55d0 <_sk_callback_sse41+0x5f3>
+  .byte  102,69,15,219,209                   // pand          %xmm9,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  68,15,89,21,200,53,0,0              // mulps         0x35c8(%rip),%xmm10        # 55e0 <_sk_callback_sse41+0x603>
+  .byte  102,68,15,219,13,207,53,0,0         // pand          0x35cf(%rip),%xmm9        # 55f0 <_sk_callback_sse41+0x613>
+  .byte  69,15,91,201                        // cvtdq2ps      %xmm9,%xmm9
+  .byte  68,15,89,13,211,53,0,0              // mulps         0x35d3(%rip),%xmm9        # 5600 <_sk_callback_sse41+0x623>
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,92,205                           // subps         %xmm5,%xmm1
+  .byte  65,15,89,202                        // mulps         %xmm10,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,92,214                           // subps         %xmm6,%xmm2
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  68,15,88,199                        // addps         %xmm7,%xmm8
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  68,15,88,215                        // addps         %xmm7,%xmm10
+  .byte  65,15,89,217                        // mulps         %xmm9,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  68,15,95,211                        // maxps         %xmm3,%xmm10
+  .byte  69,15,95,194                        // maxps         %xmm10,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,46                              // je            20b5 <_sk_lerp_565_sse41+0xe6>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,27                              // je            20a8 <_sk_lerp_565_sse41+0xd9>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  15,133,77,255,255,255               // jne           1fe4 <_sk_lerp_565_sse41+0x15>
+  .byte  65,15,183,68,82,4                   // movzwl        0x4(%r10,%rdx,2),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,200,69                // pshufd        $0x45,%xmm8,%xmm9
+  .byte  65,15,183,68,82,2                   // movzwl        0x2(%r10,%rdx,2),%eax
+  .byte  102,68,15,58,34,200,1               // pinsrd        $0x1,%eax,%xmm9
+  .byte  65,15,183,4,82                      // movzwl        (%r10,%rdx,2),%eax
+  .byte  102,68,15,58,34,200,0               // pinsrd        $0x0,%eax,%xmm9
+  .byte  233,30,255,255,255                  // jmpq          1fe4 <_sk_lerp_565_sse41+0x15>
+
+HIDDEN _sk_load_tables_sse41
+.globl _sk_load_tables_sse41
+FUNCTION(_sk_load_tables_sse41)
+_sk_load_tables_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,24,1,0,0                     // jne           21ec <_sk_load_tables_sse41+0x126>
+  .byte  243,69,15,111,4,145                 // movdqu        (%r9,%rdx,4),%xmm8
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  102,15,111,5,41,53,0,0              // movdqa        0x3529(%rip),%xmm0        # 5610 <_sk_callback_sse41+0x633>
+  .byte  102,65,15,219,192                   // pand          %xmm8,%xmm0
+  .byte  102,73,15,58,22,193,1               // pextrq        $0x1,%xmm0,%r9
+  .byte  102,73,15,126,194                   // movq          %xmm0,%r10
+  .byte  69,15,182,218                       // movzbl        %r10b,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  69,15,182,241                       // movzbl        %r9b,%r14d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,66,15,16,4,155                  // movss         (%rbx,%r11,4),%xmm0
+  .byte  102,66,15,58,33,4,19,16             // insertps      $0x10,(%rbx,%r10,1),%xmm0
+  .byte  102,66,15,58,33,4,179,32            // insertps      $0x20,(%rbx,%r14,4),%xmm0
+  .byte  102,66,15,58,33,4,11,48             // insertps      $0x30,(%rbx,%r9,1),%xmm0
+  .byte  102,65,15,111,200                   // movdqa        %xmm8,%xmm1
+  .byte  102,15,56,0,13,228,52,0,0           // pshufb        0x34e4(%rip),%xmm1        # 5620 <_sk_callback_sse41+0x643>
+  .byte  102,73,15,58,22,201,1               // pextrq        $0x1,%xmm1,%r9
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  68,15,182,211                       // movzbl        %bl,%r10d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  69,15,182,217                       // movzbl        %r9b,%r11d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  243,67,15,16,12,151                 // movss         (%r15,%r10,4),%xmm1
+  .byte  102,65,15,58,33,12,31,16            // insertps      $0x10,(%r15,%rbx,1),%xmm1
+  .byte  243,67,15,16,20,159                 // movss         (%r15,%r11,4),%xmm2
+  .byte  102,15,58,33,202,32                 // insertps      $0x20,%xmm2,%xmm1
+  .byte  243,67,15,16,20,15                  // movss         (%r15,%r9,1),%xmm2
+  .byte  102,15,58,33,202,48                 // insertps      $0x30,%xmm2,%xmm1
+  .byte  76,139,72,24                        // mov           0x18(%rax),%r9
+  .byte  102,65,15,111,208                   // movdqa        %xmm8,%xmm2
+  .byte  102,15,56,0,21,160,52,0,0           // pshufb        0x34a0(%rip),%xmm2        # 5630 <_sk_callback_sse41+0x653>
+  .byte  102,72,15,58,22,211,1               // pextrq        $0x1,%xmm2,%rbx
+  .byte  102,72,15,126,208                   // movq          %xmm2,%rax
+  .byte  68,15,182,208                       // movzbl        %al,%r10d
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  68,15,182,219                       // movzbl        %bl,%r11d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  243,67,15,16,20,145                 // movss         (%r9,%r10,4),%xmm2
+  .byte  102,65,15,58,33,20,1,16             // insertps      $0x10,(%r9,%rax,1),%xmm2
+  .byte  243,67,15,16,28,153                 // movss         (%r9,%r11,4),%xmm3
+  .byte  102,15,58,33,211,32                 // insertps      $0x20,%xmm3,%xmm2
+  .byte  243,65,15,16,28,25                  // movss         (%r9,%rbx,1),%xmm3
+  .byte  102,15,58,33,211,48                 // insertps      $0x30,%xmm3,%xmm2
+  .byte  102,65,15,114,208,24                // psrld         $0x18,%xmm8
+  .byte  65,15,91,216                        // cvtdq2ps      %xmm8,%xmm3
+  .byte  15,89,29,93,52,0,0                  // mulps         0x345d(%rip),%xmm3        # 5640 <_sk_callback_sse41+0x663>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,194                          // mov           %r8d,%r10d
+  .byte  65,128,226,3                        // and           $0x3,%r10b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,128,250,1                        // cmp           $0x1,%r10b
+  .byte  116,38                              // je            2224 <_sk_load_tables_sse41+0x15e>
+  .byte  65,128,250,2                        // cmp           $0x2,%r10b
+  .byte  116,23                              // je            221b <_sk_load_tables_sse41+0x155>
+  .byte  65,128,250,3                        // cmp           $0x3,%r10b
+  .byte  15,133,204,254,255,255              // jne           20da <_sk_load_tables_sse41+0x14>
+  .byte  102,65,15,110,68,145,8              // movd          0x8(%r9,%rdx,4),%xmm0
+  .byte  102,68,15,112,192,69                // pshufd        $0x45,%xmm0,%xmm8
+  .byte  102,69,15,58,34,68,145,4,1          // pinsrd        $0x1,0x4(%r9,%rdx,4),%xmm8
+  .byte  102,69,15,58,34,4,145,0             // pinsrd        $0x0,(%r9,%rdx,4),%xmm8
+  .byte  233,169,254,255,255                 // jmpq          20da <_sk_load_tables_sse41+0x14>
+
+HIDDEN _sk_load_tables_u16_be_sse41
+.globl _sk_load_tables_u16_be_sse41
+FUNCTION(_sk_load_tables_u16_be_sse41)
+_sk_load_tables_u16_be_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,99,1,0,0                     // jne           23aa <_sk_load_tables_u16_be_sse41+0x179>
+  .byte  102,67,15,16,4,81                   // movupd        (%r9,%r10,2),%xmm0
+  .byte  243,67,15,111,76,81,16              // movdqu        0x10(%r9,%r10,2),%xmm1
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  102,68,15,40,200                    // movapd        %xmm0,%xmm9
+  .byte  102,68,15,97,201                    // punpcklwd     %xmm1,%xmm9
+  .byte  102,15,105,193                      // punpckhwd     %xmm1,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,15,97,200                       // punpcklwd     %xmm0,%xmm1
+  .byte  102,68,15,105,200                   // punpckhwd     %xmm0,%xmm9
+  .byte  102,68,15,111,5,210,51,0,0          // movdqa        0x33d2(%rip),%xmm8        # 5650 <_sk_callback_sse41+0x673>
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,65,15,219,192                   // pand          %xmm8,%xmm0
+  .byte  102,15,56,51,192                    // pmovzxwd      %xmm0,%xmm0
+  .byte  102,73,15,58,22,193,1               // pextrq        $0x1,%xmm0,%r9
+  .byte  102,73,15,126,194                   // movq          %xmm0,%r10
+  .byte  69,15,182,218                       // movzbl        %r10b,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  69,15,182,241                       // movzbl        %r9b,%r14d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,66,15,16,4,155                  // movss         (%rbx,%r11,4),%xmm0
+  .byte  102,66,15,58,33,4,19,16             // insertps      $0x10,(%rbx,%r10,1),%xmm0
+  .byte  243,66,15,16,20,179                 // movss         (%rbx,%r14,4),%xmm2
+  .byte  102,15,58,33,194,32                 // insertps      $0x20,%xmm2,%xmm0
+  .byte  243,66,15,16,20,11                  // movss         (%rbx,%r9,1),%xmm2
+  .byte  102,15,58,33,194,48                 // insertps      $0x30,%xmm2,%xmm0
+  .byte  102,15,56,0,13,129,51,0,0           // pshufb        0x3381(%rip),%xmm1        # 5660 <_sk_callback_sse41+0x683>
+  .byte  102,15,56,51,201                    // pmovzxwd      %xmm1,%xmm1
+  .byte  102,73,15,58,22,201,1               // pextrq        $0x1,%xmm1,%r9
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  68,15,182,211                       // movzbl        %bl,%r10d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  69,15,182,217                       // movzbl        %r9b,%r11d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  243,67,15,16,12,151                 // movss         (%r15,%r10,4),%xmm1
+  .byte  102,65,15,58,33,12,31,16            // insertps      $0x10,(%r15,%rbx,1),%xmm1
+  .byte  243,67,15,16,20,159                 // movss         (%r15,%r11,4),%xmm2
+  .byte  102,15,58,33,202,32                 // insertps      $0x20,%xmm2,%xmm1
+  .byte  243,67,15,16,20,15                  // movss         (%r15,%r9,1),%xmm2
+  .byte  102,15,58,33,202,48                 // insertps      $0x30,%xmm2,%xmm1
+  .byte  76,139,72,24                        // mov           0x18(%rax),%r9
+  .byte  102,69,15,219,193                   // pand          %xmm9,%xmm8
+  .byte  102,65,15,56,51,208                 // pmovzxwd      %xmm8,%xmm2
+  .byte  102,72,15,58,22,211,1               // pextrq        $0x1,%xmm2,%rbx
+  .byte  102,72,15,126,208                   // movq          %xmm2,%rax
+  .byte  68,15,182,208                       // movzbl        %al,%r10d
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  68,15,182,219                       // movzbl        %bl,%r11d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  243,67,15,16,20,145                 // movss         (%r9,%r10,4),%xmm2
+  .byte  102,65,15,58,33,20,1,16             // insertps      $0x10,(%r9,%rax,1),%xmm2
+  .byte  243,67,15,16,28,153                 // movss         (%r9,%r11,4),%xmm3
+  .byte  102,15,58,33,211,32                 // insertps      $0x20,%xmm3,%xmm2
+  .byte  243,65,15,16,28,25                  // movss         (%r9,%rbx,1),%xmm3
+  .byte  102,15,58,33,211,48                 // insertps      $0x30,%xmm3,%xmm2
+  .byte  102,65,15,112,217,78                // pshufd        $0x4e,%xmm9,%xmm3
+  .byte  102,68,15,111,195                   // movdqa        %xmm3,%xmm8
+  .byte  102,65,15,113,240,8                 // psllw         $0x8,%xmm8
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,65,15,235,216                   // por           %xmm8,%xmm3
+  .byte  102,15,56,51,219                    // pmovzxwd      %xmm3,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  15,89,29,207,50,0,0                 // mulps         0x32cf(%rip),%xmm3        # 5670 <_sk_callback_sse41+0x693>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+  .byte  242,67,15,16,4,81                   // movsd         (%r9,%r10,2),%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,13                              // jne           23c3 <_sk_load_tables_u16_be_sse41+0x192>
+  .byte  243,15,126,192                      // movq          %xmm0,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,145,254,255,255                 // jmpq          2254 <_sk_load_tables_u16_be_sse41+0x23>
+  .byte  102,67,15,22,68,81,8                // movhpd        0x8(%r9,%r10,2),%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  15,130,124,254,255,255              // jb            2254 <_sk_load_tables_u16_be_sse41+0x23>
+  .byte  243,67,15,126,76,81,16              // movq          0x10(%r9,%r10,2),%xmm1
+  .byte  233,112,254,255,255                 // jmpq          2254 <_sk_load_tables_u16_be_sse41+0x23>
+
+HIDDEN _sk_load_tables_rgb_u16_be_sse41
+.globl _sk_load_tables_rgb_u16_be_sse41
+FUNCTION(_sk_load_tables_rgb_u16_be_sse41)
+_sk_load_tables_rgb_u16_be_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,82                        // lea           (%rdx,%rdx,2),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,83,1,0,0                     // jne           2549 <_sk_load_tables_rgb_u16_be_sse41+0x165>
+  .byte  243,67,15,111,20,81                 // movdqu        (%r9,%r10,2),%xmm2
+  .byte  243,67,15,111,76,81,8               // movdqu        0x8(%r9,%r10,2),%xmm1
+  .byte  102,15,115,217,4                    // psrldq        $0x4,%xmm1
+  .byte  102,68,15,111,202                   // movdqa        %xmm2,%xmm9
+  .byte  102,65,15,115,217,6                 // psrldq        $0x6,%xmm9
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,115,216,6                    // psrldq        $0x6,%xmm0
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  102,15,97,209                       // punpcklwd     %xmm1,%xmm2
+  .byte  102,68,15,97,200                    // punpcklwd     %xmm0,%xmm9
+  .byte  102,15,111,202                      // movdqa        %xmm2,%xmm1
+  .byte  102,65,15,97,201                    // punpcklwd     %xmm9,%xmm1
+  .byte  102,68,15,111,5,68,50,0,0           // movdqa        0x3244(%rip),%xmm8        # 5680 <_sk_callback_sse41+0x6a3>
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,65,15,219,192                   // pand          %xmm8,%xmm0
+  .byte  102,15,56,51,192                    // pmovzxwd      %xmm0,%xmm0
+  .byte  102,73,15,58,22,193,1               // pextrq        $0x1,%xmm0,%r9
+  .byte  102,73,15,126,194                   // movq          %xmm0,%r10
+  .byte  69,15,182,218                       // movzbl        %r10b,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  69,15,182,241                       // movzbl        %r9b,%r14d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,66,15,16,4,155                  // movss         (%rbx,%r11,4),%xmm0
+  .byte  102,66,15,58,33,4,19,16             // insertps      $0x10,(%rbx,%r10,1),%xmm0
+  .byte  243,66,15,16,28,179                 // movss         (%rbx,%r14,4),%xmm3
+  .byte  102,15,58,33,195,32                 // insertps      $0x20,%xmm3,%xmm0
+  .byte  243,66,15,16,28,11                  // movss         (%rbx,%r9,1),%xmm3
+  .byte  102,15,58,33,195,48                 // insertps      $0x30,%xmm3,%xmm0
+  .byte  102,15,56,0,13,243,49,0,0           // pshufb        0x31f3(%rip),%xmm1        # 5690 <_sk_callback_sse41+0x6b3>
+  .byte  102,15,56,51,201                    // pmovzxwd      %xmm1,%xmm1
+  .byte  102,73,15,58,22,201,1               // pextrq        $0x1,%xmm1,%r9
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  68,15,182,211                       // movzbl        %bl,%r10d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  69,15,182,217                       // movzbl        %r9b,%r11d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  243,67,15,16,12,151                 // movss         (%r15,%r10,4),%xmm1
+  .byte  102,65,15,58,33,12,31,16            // insertps      $0x10,(%r15,%rbx,1),%xmm1
+  .byte  243,67,15,16,28,159                 // movss         (%r15,%r11,4),%xmm3
+  .byte  102,15,58,33,203,32                 // insertps      $0x20,%xmm3,%xmm1
+  .byte  243,67,15,16,28,15                  // movss         (%r15,%r9,1),%xmm3
+  .byte  102,15,58,33,203,48                 // insertps      $0x30,%xmm3,%xmm1
+  .byte  76,139,72,24                        // mov           0x18(%rax),%r9
+  .byte  102,65,15,105,209                   // punpckhwd     %xmm9,%xmm2
+  .byte  102,65,15,219,208                   // pand          %xmm8,%xmm2
+  .byte  102,15,56,51,210                    // pmovzxwd      %xmm2,%xmm2
+  .byte  102,72,15,58,22,211,1               // pextrq        $0x1,%xmm2,%rbx
+  .byte  102,72,15,126,208                   // movq          %xmm2,%rax
+  .byte  68,15,182,208                       // movzbl        %al,%r10d
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  68,15,182,219                       // movzbl        %bl,%r11d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  243,67,15,16,20,145                 // movss         (%r9,%r10,4),%xmm2
+  .byte  102,65,15,58,33,20,1,16             // insertps      $0x10,(%r9,%rax,1),%xmm2
+  .byte  243,67,15,16,28,153                 // movss         (%r9,%r11,4),%xmm3
+  .byte  102,15,58,33,211,32                 // insertps      $0x20,%xmm3,%xmm2
+  .byte  243,65,15,16,28,25                  // movss         (%r9,%rbx,1),%xmm3
+  .byte  102,15,58,33,211,48                 // insertps      $0x30,%xmm3,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,94,49,0,0                  // movaps        0x315e(%rip),%xmm3        # 56a0 <_sk_callback_sse41+0x6c3>
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,67,15,110,20,81                 // movd          (%r9,%r10,2),%xmm2
+  .byte  102,67,15,196,84,81,4,2             // pinsrw        $0x2,0x4(%r9,%r10,2),%xmm2
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,14                              // jne           256f <_sk_load_tables_rgb_u16_be_sse41+0x18b>
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  233,173,254,255,255                 // jmpq          241c <_sk_load_tables_rgb_u16_be_sse41+0x38>
+  .byte  102,71,15,110,76,81,6               // movd          0x6(%r9,%r10,2),%xmm9
+  .byte  102,71,15,196,76,81,10,2            // pinsrw        $0x2,0xa(%r9,%r10,2),%xmm9
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,24                              // jb            25a0 <_sk_load_tables_rgb_u16_be_sse41+0x1bc>
+  .byte  102,67,15,110,76,81,12              // movd          0xc(%r9,%r10,2),%xmm1
+  .byte  102,67,15,196,76,81,16,2            // pinsrw        $0x2,0x10(%r9,%r10,2),%xmm1
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  233,124,254,255,255                 // jmpq          241c <_sk_load_tables_rgb_u16_be_sse41+0x38>
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,115,254,255,255                 // jmpq          241c <_sk_load_tables_rgb_u16_be_sse41+0x38>
+
+HIDDEN _sk_byte_tables_sse41
+.globl _sk_byte_tables_sse41
+FUNCTION(_sk_byte_tables_sse41)
+_sk_byte_tables_sse41:
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,246,48,0,0               // movaps        0x30f6(%rip),%xmm8        # 56b0 <_sk_callback_sse41+0x6d3>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,91,192                       // cvtps2dq      %xmm0,%xmm0
+  .byte  102,73,15,58,22,193,1               // pextrq        $0x1,%xmm0,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  69,137,222                          // mov           %r11d,%r14d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  76,139,32                           // mov           (%rax),%r12
+  .byte  76,139,120,8                        // mov           0x8(%rax),%r15
+  .byte  102,67,15,58,32,4,52,0              // pinsrb        $0x0,(%r12,%r14,1),%xmm0
+  .byte  102,67,15,58,32,4,28,1              // pinsrb        $0x1,(%r12,%r11,1),%xmm0
+  .byte  67,15,182,28,20                     // movzbl        (%r12,%r10,1),%ebx
+  .byte  102,15,58,32,195,2                  // pinsrb        $0x2,%ebx,%xmm0
+  .byte  67,15,182,28,12                     // movzbl        (%r12,%r9,1),%ebx
+  .byte  102,15,58,32,195,3                  // pinsrb        $0x3,%ebx,%xmm0
+  .byte  102,15,56,49,192                    // pmovzxbd      %xmm0,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,13,167,48,0,0              // movaps        0x30a7(%rip),%xmm9        # 56c0 <_sk_callback_sse41+0x6e3>
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,91,201                       // cvtps2dq      %xmm1,%xmm1
+  .byte  102,72,15,58,22,203,1               // pextrq        $0x1,%xmm1,%rbx
+  .byte  65,137,217                          // mov           %ebx,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,73,15,126,202                   // movq          %xmm1,%r10
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  73,193,234,32                       // shr           $0x20,%r10
+  .byte  102,67,15,58,32,12,31,0             // pinsrb        $0x0,(%r15,%r11,1),%xmm1
+  .byte  102,67,15,58,32,12,23,1             // pinsrb        $0x1,(%r15,%r10,1),%xmm1
+  .byte  71,15,182,12,15                     // movzbl        (%r15,%r9,1),%r9d
+  .byte  102,65,15,58,32,201,2               // pinsrb        $0x2,%r9d,%xmm1
+  .byte  65,15,182,28,31                     // movzbl        (%r15,%rbx,1),%ebx
+  .byte  102,15,58,32,203,3                  // pinsrb        $0x3,%ebx,%xmm1
+  .byte  102,15,56,49,201                    // pmovzxbd      %xmm1,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  76,139,88,16                        // mov           0x10(%rax),%r11
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,91,210                       // cvtps2dq      %xmm2,%xmm2
+  .byte  102,73,15,58,22,209,1               // pextrq        $0x1,%xmm2,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  65,137,222                          // mov           %ebx,%r14d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,67,15,58,32,20,51,0             // pinsrb        $0x0,(%r11,%r14,1),%xmm2
+  .byte  102,65,15,58,32,20,27,1             // pinsrb        $0x1,(%r11,%rbx,1),%xmm2
+  .byte  67,15,182,28,19                     // movzbl        (%r11,%r10,1),%ebx
+  .byte  102,15,58,32,211,2                  // pinsrb        $0x2,%ebx,%xmm2
+  .byte  67,15,182,28,11                     // movzbl        (%r11,%r9,1),%ebx
+  .byte  102,15,58,32,211,3                  // pinsrb        $0x3,%ebx,%xmm2
+  .byte  102,15,56,49,210                    // pmovzxbd      %xmm2,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  102,15,91,219                       // cvtps2dq      %xmm3,%xmm3
+  .byte  102,72,15,58,22,219,1               // pextrq        $0x1,%xmm3,%rbx
+  .byte  65,137,217                          // mov           %ebx,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,72,15,126,216                   // movq          %xmm3,%rax
+  .byte  65,137,195                          // mov           %eax,%r11d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,67,15,58,32,28,26,0             // pinsrb        $0x0,(%r10,%r11,1),%xmm3
+  .byte  102,65,15,58,32,28,2,1              // pinsrb        $0x1,(%r10,%rax,1),%xmm3
+  .byte  67,15,182,4,10                      // movzbl        (%r10,%r9,1),%eax
+  .byte  102,15,58,32,216,2                  // pinsrb        $0x2,%eax,%xmm3
+  .byte  65,15,182,4,26                      // movzbl        (%r10,%rbx,1),%eax
+  .byte  102,15,58,32,216,3                  // pinsrb        $0x3,%eax,%xmm3
+  .byte  102,15,56,49,219                    // pmovzxbd      %xmm3,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,217                        // mulps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_byte_tables_rgb_sse41
+.globl _sk_byte_tables_rgb_sse41
+FUNCTION(_sk_byte_tables_rgb_sse41)
+_sk_byte_tables_rgb_sse41:
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,139,72,24                        // mov           0x18(%rax),%r9d
+  .byte  65,255,201                          // dec           %r9d
+  .byte  102,69,15,110,193                   // movd          %r9d,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,91,192                       // cvtps2dq      %xmm0,%xmm0
+  .byte  102,73,15,58,22,193,1               // pextrq        $0x1,%xmm0,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  77,137,203                          // mov           %r9,%r11
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,73,15,126,193                   // movq          %xmm0,%r9
+  .byte  69,137,206                          // mov           %r9d,%r14d
+  .byte  77,137,207                          // mov           %r9,%r15
+  .byte  73,193,239,32                       // shr           $0x20,%r15
+  .byte  76,139,32                           // mov           (%rax),%r12
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  102,67,15,58,32,4,52,0              // pinsrb        $0x0,(%r12,%r14,1),%xmm0
+  .byte  102,67,15,58,32,4,60,1              // pinsrb        $0x1,(%r12,%r15,1),%xmm0
+  .byte  67,15,182,28,20                     // movzbl        (%r12,%r10,1),%ebx
+  .byte  102,15,58,32,195,2                  // pinsrb        $0x2,%ebx,%xmm0
+  .byte  67,15,182,28,28                     // movzbl        (%r12,%r11,1),%ebx
+  .byte  102,15,58,32,195,3                  // pinsrb        $0x3,%ebx,%xmm0
+  .byte  102,15,56,49,192                    // pmovzxbd      %xmm0,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,13,31,47,0,0               // movaps        0x2f1f(%rip),%xmm9        # 56d0 <_sk_callback_sse41+0x6f3>
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,91,201                       // cvtps2dq      %xmm1,%xmm1
+  .byte  102,72,15,58,22,203,1               // pextrq        $0x1,%xmm1,%rbx
+  .byte  65,137,218                          // mov           %ebx,%r10d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,73,15,126,203                   // movq          %xmm1,%r11
+  .byte  69,137,222                          // mov           %r11d,%r14d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,67,15,58,32,12,49,0             // pinsrb        $0x0,(%r9,%r14,1),%xmm1
+  .byte  102,67,15,58,32,12,25,1             // pinsrb        $0x1,(%r9,%r11,1),%xmm1
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  102,65,15,58,32,202,2               // pinsrb        $0x2,%r10d,%xmm1
+  .byte  65,15,182,28,25                     // movzbl        (%r9,%rbx,1),%ebx
+  .byte  102,15,58,32,203,3                  // pinsrb        $0x3,%ebx,%xmm1
+  .byte  102,15,56,49,201                    // pmovzxbd      %xmm1,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,91,210                       // cvtps2dq      %xmm2,%xmm2
+  .byte  102,72,15,58,22,211,1               // pextrq        $0x1,%xmm2,%rbx
+  .byte  65,137,217                          // mov           %ebx,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,72,15,126,208                   // movq          %xmm2,%rax
+  .byte  65,137,195                          // mov           %eax,%r11d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,67,15,58,32,20,26,0             // pinsrb        $0x0,(%r10,%r11,1),%xmm2
+  .byte  102,65,15,58,32,20,2,1              // pinsrb        $0x1,(%r10,%rax,1),%xmm2
+  .byte  67,15,182,4,10                      // movzbl        (%r10,%r9,1),%eax
+  .byte  102,15,58,32,208,2                  // pinsrb        $0x2,%eax,%xmm2
+  .byte  65,15,182,4,26                      // movzbl        (%r10,%rbx,1),%eax
+  .byte  102,15,58,32,208,3                  // pinsrb        $0x3,%eax,%xmm2
+  .byte  102,15,56,49,210                    // pmovzxbd      %xmm2,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_r_sse41
+.globl _sk_table_r_sse41
+FUNCTION(_sk_table_r_sse41)
+_sk_table_r_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  102,65,15,91,192                    // cvtps2dq      %xmm8,%xmm0
+  .byte  102,72,15,58,22,192,1               // pextrq        $0x1,%xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,65,15,16,4,153                  // movss         (%r9,%rbx,4),%xmm0
+  .byte  102,67,15,58,33,4,153,16            // insertps      $0x10,(%r9,%r11,4),%xmm0
+  .byte  243,71,15,16,4,145                  // movss         (%r9,%r10,4),%xmm8
+  .byte  102,65,15,58,33,192,32              // insertps      $0x20,%xmm8,%xmm0
+  .byte  243,69,15,16,4,129                  // movss         (%r9,%rax,4),%xmm8
+  .byte  102,65,15,58,33,192,48              // insertps      $0x30,%xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_g_sse41
+.globl _sk_table_g_sse41
+FUNCTION(_sk_table_g_sse41)
+_sk_table_g_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,193                        // mulps         %xmm1,%xmm8
+  .byte  102,65,15,91,200                    // cvtps2dq      %xmm8,%xmm1
+  .byte  102,72,15,58,22,200,1               // pextrq        $0x1,%xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,203                   // movq          %xmm1,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,65,15,16,12,153                 // movss         (%r9,%rbx,4),%xmm1
+  .byte  102,67,15,58,33,12,153,16           // insertps      $0x10,(%r9,%r11,4),%xmm1
+  .byte  243,71,15,16,4,145                  // movss         (%r9,%r10,4),%xmm8
+  .byte  102,65,15,58,33,200,32              // insertps      $0x20,%xmm8,%xmm1
+  .byte  243,69,15,16,4,129                  // movss         (%r9,%rax,4),%xmm8
+  .byte  102,65,15,58,33,200,48              // insertps      $0x30,%xmm8,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_b_sse41
+.globl _sk_table_b_sse41
+FUNCTION(_sk_table_b_sse41)
+_sk_table_b_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  102,65,15,91,208                    // cvtps2dq      %xmm8,%xmm2
+  .byte  102,72,15,58,22,208,1               // pextrq        $0x1,%xmm2,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,211                   // movq          %xmm2,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,65,15,16,20,153                 // movss         (%r9,%rbx,4),%xmm2
+  .byte  102,67,15,58,33,20,153,16           // insertps      $0x10,(%r9,%r11,4),%xmm2
+  .byte  243,71,15,16,4,145                  // movss         (%r9,%r10,4),%xmm8
+  .byte  102,65,15,58,33,208,32              // insertps      $0x20,%xmm8,%xmm2
+  .byte  243,69,15,16,4,129                  // movss         (%r9,%rax,4),%xmm8
+  .byte  102,65,15,58,33,208,48              // insertps      $0x30,%xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_a_sse41
+.globl _sk_table_a_sse41
+FUNCTION(_sk_table_a_sse41)
+_sk_table_a_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,65,15,91,216                    // cvtps2dq      %xmm8,%xmm3
+  .byte  102,72,15,58,22,216,1               // pextrq        $0x1,%xmm3,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,219                   // movq          %xmm3,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,65,15,16,28,153                 // movss         (%r9,%rbx,4),%xmm3
+  .byte  102,67,15,58,33,28,153,16           // insertps      $0x10,(%r9,%r11,4),%xmm3
+  .byte  243,71,15,16,4,145                  // movss         (%r9,%r10,4),%xmm8
+  .byte  102,65,15,58,33,216,32              // insertps      $0x20,%xmm8,%xmm3
+  .byte  243,69,15,16,4,129                  // movss         (%r9,%rax,4),%xmm8
+  .byte  102,65,15,58,33,216,48              // insertps      $0x30,%xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_r_sse41
+.globl _sk_parametric_r_sse41
+FUNCTION(_sk_parametric_r_sse41)
+_sk_parametric_r_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,64,16                  // movss         0x10(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  243,68,15,16,72,12                  // movss         0xc(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  68,15,89,200                        // mulps         %xmm0,%xmm9
+  .byte  243,68,15,16,80,4                   // movss         0x4(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,208                        // mulps         %xmm0,%xmm10
+  .byte  65,15,194,192,2                     // cmpleps       %xmm8,%xmm0
+  .byte  243,68,15,16,64,24                  // movss         0x18(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  69,15,88,200                        // addps         %xmm8,%xmm9
+  .byte  243,68,15,16,24                     // movss         (%rax),%xmm11
+  .byte  243,68,15,16,64,8                   // movss         0x8(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  69,15,88,208                        // addps         %xmm8,%xmm10
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,91,194                        // cvtdq2ps      %xmm10,%xmm8
+  .byte  68,15,89,5,103,44,0,0               // mulps         0x2c67(%rip),%xmm8        # 56e0 <_sk_callback_sse41+0x703>
+  .byte  68,15,84,21,111,44,0,0              // andps         0x2c6f(%rip),%xmm10        # 56f0 <_sk_callback_sse41+0x713>
+  .byte  68,15,86,21,119,44,0,0              // orps          0x2c77(%rip),%xmm10        # 5700 <_sk_callback_sse41+0x723>
+  .byte  68,15,88,5,127,44,0,0               // addps         0x2c7f(%rip),%xmm8        # 5710 <_sk_callback_sse41+0x733>
+  .byte  68,15,40,37,135,44,0,0              // movaps        0x2c87(%rip),%xmm12        # 5720 <_sk_callback_sse41+0x743>
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  69,15,92,196                        // subps         %xmm12,%xmm8
+  .byte  68,15,88,21,135,44,0,0              // addps         0x2c87(%rip),%xmm10        # 5730 <_sk_callback_sse41+0x753>
+  .byte  68,15,40,37,143,44,0,0              // movaps        0x2c8f(%rip),%xmm12        # 5740 <_sk_callback_sse41+0x763>
+  .byte  69,15,94,226                        // divps         %xmm10,%xmm12
+  .byte  69,15,92,196                        // subps         %xmm12,%xmm8
+  .byte  69,15,89,195                        // mulps         %xmm11,%xmm8
+  .byte  102,69,15,58,8,208,1                // roundps       $0x1,%xmm8,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  68,15,88,5,124,44,0,0               // addps         0x2c7c(%rip),%xmm8        # 5750 <_sk_callback_sse41+0x773>
+  .byte  68,15,40,21,132,44,0,0              // movaps        0x2c84(%rip),%xmm10        # 5760 <_sk_callback_sse41+0x783>
+  .byte  69,15,89,211                        // mulps         %xmm11,%xmm10
+  .byte  69,15,92,194                        // subps         %xmm10,%xmm8
+  .byte  68,15,40,21,132,44,0,0              // movaps        0x2c84(%rip),%xmm10        # 5770 <_sk_callback_sse41+0x793>
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  68,15,40,29,136,44,0,0              // movaps        0x2c88(%rip),%xmm11        # 5780 <_sk_callback_sse41+0x7a3>
+  .byte  69,15,94,218                        // divps         %xmm10,%xmm11
+  .byte  69,15,88,216                        // addps         %xmm8,%xmm11
+  .byte  68,15,89,29,136,44,0,0              // mulps         0x2c88(%rip),%xmm11        # 5790 <_sk_callback_sse41+0x7b3>
+  .byte  102,69,15,91,211                    // cvtps2dq      %xmm11,%xmm10
+  .byte  243,68,15,16,64,20                  // movss         0x14(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  69,15,88,194                        // addps         %xmm10,%xmm8
+  .byte  102,69,15,56,20,193                 // blendvps      %xmm0,%xmm9,%xmm8
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  68,15,93,5,111,44,0,0               // minps         0x2c6f(%rip),%xmm8        # 57a0 <_sk_callback_sse41+0x7c3>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_g_sse41
+.globl _sk_parametric_g_sse41
+FUNCTION(_sk_parametric_g_sse41)
+_sk_parametric_g_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,72,12                  // movss         0xc(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  243,68,15,16,88,4                   // movss         0x4(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  68,15,89,217                        // mulps         %xmm1,%xmm11
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,194,194,2                     // cmpleps       %xmm10,%xmm0
+  .byte  243,15,16,72,24                     // movss         0x18(%rax),%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  68,15,88,201                        // addps         %xmm1,%xmm9
+  .byte  243,68,15,16,16                     // movss         (%rax),%xmm10
+  .byte  243,15,16,72,8                      // movss         0x8(%rax),%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  68,15,88,217                        // addps         %xmm1,%xmm11
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  69,15,91,227                        // cvtdq2ps      %xmm11,%xmm12
+  .byte  68,15,89,37,16,44,0,0               // mulps         0x2c10(%rip),%xmm12        # 57b0 <_sk_callback_sse41+0x7d3>
+  .byte  68,15,84,29,24,44,0,0               // andps         0x2c18(%rip),%xmm11        # 57c0 <_sk_callback_sse41+0x7e3>
+  .byte  68,15,86,29,32,44,0,0               // orps          0x2c20(%rip),%xmm11        # 57d0 <_sk_callback_sse41+0x7f3>
+  .byte  68,15,88,37,40,44,0,0               // addps         0x2c28(%rip),%xmm12        # 57e0 <_sk_callback_sse41+0x803>
+  .byte  15,40,13,49,44,0,0                  // movaps        0x2c31(%rip),%xmm1        # 57f0 <_sk_callback_sse41+0x813>
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  68,15,92,225                        // subps         %xmm1,%xmm12
+  .byte  68,15,88,29,49,44,0,0               // addps         0x2c31(%rip),%xmm11        # 5800 <_sk_callback_sse41+0x823>
+  .byte  15,40,13,58,44,0,0                  // movaps        0x2c3a(%rip),%xmm1        # 5810 <_sk_callback_sse41+0x833>
+  .byte  65,15,94,203                        // divps         %xmm11,%xmm1
+  .byte  68,15,92,225                        // subps         %xmm1,%xmm12
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  102,69,15,58,8,212,1                // roundps       $0x1,%xmm12,%xmm10
+  .byte  69,15,40,220                        // movaps        %xmm12,%xmm11
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  68,15,88,37,39,44,0,0               // addps         0x2c27(%rip),%xmm12        # 5820 <_sk_callback_sse41+0x843>
+  .byte  15,40,13,48,44,0,0                  // movaps        0x2c30(%rip),%xmm1        # 5830 <_sk_callback_sse41+0x853>
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  68,15,92,225                        // subps         %xmm1,%xmm12
+  .byte  68,15,40,21,48,44,0,0               // movaps        0x2c30(%rip),%xmm10        # 5840 <_sk_callback_sse41+0x863>
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  15,40,13,53,44,0,0                  // movaps        0x2c35(%rip),%xmm1        # 5850 <_sk_callback_sse41+0x873>
+  .byte  65,15,94,202                        // divps         %xmm10,%xmm1
+  .byte  65,15,88,204                        // addps         %xmm12,%xmm1
+  .byte  15,89,13,54,44,0,0                  // mulps         0x2c36(%rip),%xmm1        # 5860 <_sk_callback_sse41+0x883>
+  .byte  102,68,15,91,209                    // cvtps2dq      %xmm1,%xmm10
+  .byte  243,15,16,72,20                     // movss         0x14(%rax),%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  102,65,15,56,20,201                 // blendvps      %xmm0,%xmm9,%xmm1
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,95,200                           // maxps         %xmm0,%xmm1
+  .byte  15,93,13,33,44,0,0                  // minps         0x2c21(%rip),%xmm1        # 5870 <_sk_callback_sse41+0x893>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_b_sse41
+.globl _sk_parametric_b_sse41
+FUNCTION(_sk_parametric_b_sse41)
+_sk_parametric_b_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,72,12                  // movss         0xc(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  68,15,89,202                        // mulps         %xmm2,%xmm9
+  .byte  243,68,15,16,88,4                   // movss         0x4(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  65,15,194,194,2                     // cmpleps       %xmm10,%xmm0
+  .byte  243,15,16,80,24                     // movss         0x18(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  68,15,88,202                        // addps         %xmm2,%xmm9
+  .byte  243,68,15,16,16                     // movss         (%rax),%xmm10
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  68,15,88,218                        // addps         %xmm2,%xmm11
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  69,15,91,227                        // cvtdq2ps      %xmm11,%xmm12
+  .byte  68,15,89,37,194,43,0,0              // mulps         0x2bc2(%rip),%xmm12        # 5880 <_sk_callback_sse41+0x8a3>
+  .byte  68,15,84,29,202,43,0,0              // andps         0x2bca(%rip),%xmm11        # 5890 <_sk_callback_sse41+0x8b3>
+  .byte  68,15,86,29,210,43,0,0              // orps          0x2bd2(%rip),%xmm11        # 58a0 <_sk_callback_sse41+0x8c3>
+  .byte  68,15,88,37,218,43,0,0              // addps         0x2bda(%rip),%xmm12        # 58b0 <_sk_callback_sse41+0x8d3>
+  .byte  15,40,21,227,43,0,0                 // movaps        0x2be3(%rip),%xmm2        # 58c0 <_sk_callback_sse41+0x8e3>
+  .byte  65,15,89,211                        // mulps         %xmm11,%xmm2
+  .byte  68,15,92,226                        // subps         %xmm2,%xmm12
+  .byte  68,15,88,29,227,43,0,0              // addps         0x2be3(%rip),%xmm11        # 58d0 <_sk_callback_sse41+0x8f3>
+  .byte  15,40,21,236,43,0,0                 // movaps        0x2bec(%rip),%xmm2        # 58e0 <_sk_callback_sse41+0x903>
+  .byte  65,15,94,211                        // divps         %xmm11,%xmm2
+  .byte  68,15,92,226                        // subps         %xmm2,%xmm12
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  102,69,15,58,8,212,1                // roundps       $0x1,%xmm12,%xmm10
+  .byte  69,15,40,220                        // movaps        %xmm12,%xmm11
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  68,15,88,37,217,43,0,0              // addps         0x2bd9(%rip),%xmm12        # 58f0 <_sk_callback_sse41+0x913>
+  .byte  15,40,21,226,43,0,0                 // movaps        0x2be2(%rip),%xmm2        # 5900 <_sk_callback_sse41+0x923>
+  .byte  65,15,89,211                        // mulps         %xmm11,%xmm2
+  .byte  68,15,92,226                        // subps         %xmm2,%xmm12
+  .byte  68,15,40,21,226,43,0,0              // movaps        0x2be2(%rip),%xmm10        # 5910 <_sk_callback_sse41+0x933>
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  15,40,21,231,43,0,0                 // movaps        0x2be7(%rip),%xmm2        # 5920 <_sk_callback_sse41+0x943>
+  .byte  65,15,94,210                        // divps         %xmm10,%xmm2
+  .byte  65,15,88,212                        // addps         %xmm12,%xmm2
+  .byte  15,89,21,232,43,0,0                 // mulps         0x2be8(%rip),%xmm2        # 5930 <_sk_callback_sse41+0x953>
+  .byte  102,68,15,91,210                    // cvtps2dq      %xmm2,%xmm10
+  .byte  243,15,16,80,20                     // movss         0x14(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  65,15,88,210                        // addps         %xmm10,%xmm2
+  .byte  102,65,15,56,20,209                 // blendvps      %xmm0,%xmm9,%xmm2
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,95,208                           // maxps         %xmm0,%xmm2
+  .byte  15,93,21,211,43,0,0                 // minps         0x2bd3(%rip),%xmm2        # 5940 <_sk_callback_sse41+0x963>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_a_sse41
+.globl _sk_parametric_a_sse41
+FUNCTION(_sk_parametric_a_sse41)
+_sk_parametric_a_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,72,12                  // movss         0xc(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  243,68,15,16,88,4                   // movss         0x4(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  68,15,89,219                        // mulps         %xmm3,%xmm11
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  65,15,194,194,2                     // cmpleps       %xmm10,%xmm0
+  .byte  243,15,16,88,24                     // movss         0x18(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  68,15,88,203                        // addps         %xmm3,%xmm9
+  .byte  243,68,15,16,16                     // movss         (%rax),%xmm10
+  .byte  243,15,16,88,8                      // movss         0x8(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  68,15,88,219                        // addps         %xmm3,%xmm11
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  69,15,91,227                        // cvtdq2ps      %xmm11,%xmm12
+  .byte  68,15,89,37,116,43,0,0              // mulps         0x2b74(%rip),%xmm12        # 5950 <_sk_callback_sse41+0x973>
+  .byte  68,15,84,29,124,43,0,0              // andps         0x2b7c(%rip),%xmm11        # 5960 <_sk_callback_sse41+0x983>
+  .byte  68,15,86,29,132,43,0,0              // orps          0x2b84(%rip),%xmm11        # 5970 <_sk_callback_sse41+0x993>
+  .byte  68,15,88,37,140,43,0,0              // addps         0x2b8c(%rip),%xmm12        # 5980 <_sk_callback_sse41+0x9a3>
+  .byte  15,40,29,149,43,0,0                 // movaps        0x2b95(%rip),%xmm3        # 5990 <_sk_callback_sse41+0x9b3>
+  .byte  65,15,89,219                        // mulps         %xmm11,%xmm3
+  .byte  68,15,92,227                        // subps         %xmm3,%xmm12
+  .byte  68,15,88,29,149,43,0,0              // addps         0x2b95(%rip),%xmm11        # 59a0 <_sk_callback_sse41+0x9c3>
+  .byte  15,40,29,158,43,0,0                 // movaps        0x2b9e(%rip),%xmm3        # 59b0 <_sk_callback_sse41+0x9d3>
+  .byte  65,15,94,219                        // divps         %xmm11,%xmm3
+  .byte  68,15,92,227                        // subps         %xmm3,%xmm12
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  102,69,15,58,8,212,1                // roundps       $0x1,%xmm12,%xmm10
+  .byte  69,15,40,220                        // movaps        %xmm12,%xmm11
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  68,15,88,37,139,43,0,0              // addps         0x2b8b(%rip),%xmm12        # 59c0 <_sk_callback_sse41+0x9e3>
+  .byte  15,40,29,148,43,0,0                 // movaps        0x2b94(%rip),%xmm3        # 59d0 <_sk_callback_sse41+0x9f3>
+  .byte  65,15,89,219                        // mulps         %xmm11,%xmm3
+  .byte  68,15,92,227                        // subps         %xmm3,%xmm12
+  .byte  68,15,40,21,148,43,0,0              // movaps        0x2b94(%rip),%xmm10        # 59e0 <_sk_callback_sse41+0xa03>
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  15,40,29,153,43,0,0                 // movaps        0x2b99(%rip),%xmm3        # 59f0 <_sk_callback_sse41+0xa13>
+  .byte  65,15,94,218                        // divps         %xmm10,%xmm3
+  .byte  65,15,88,220                        // addps         %xmm12,%xmm3
+  .byte  15,89,29,154,43,0,0                 // mulps         0x2b9a(%rip),%xmm3        # 5a00 <_sk_callback_sse41+0xa23>
+  .byte  102,68,15,91,211                    // cvtps2dq      %xmm3,%xmm10
+  .byte  243,15,16,88,20                     // movss         0x14(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  102,65,15,56,20,217                 // blendvps      %xmm0,%xmm9,%xmm3
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,95,216                           // maxps         %xmm0,%xmm3
+  .byte  15,93,29,133,43,0,0                 // minps         0x2b85(%rip),%xmm3        # 5a10 <_sk_callback_sse41+0xa33>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lab_to_xyz_sse41
+.globl _sk_lab_to_xyz_sse41
+FUNCTION(_sk_lab_to_xyz_sse41)
+_sk_lab_to_xyz_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  68,15,89,5,129,43,0,0               // mulps         0x2b81(%rip),%xmm8        # 5a20 <_sk_callback_sse41+0xa43>
+  .byte  68,15,40,13,137,43,0,0              // movaps        0x2b89(%rip),%xmm9        # 5a30 <_sk_callback_sse41+0xa53>
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  15,40,5,142,43,0,0                  // movaps        0x2b8e(%rip),%xmm0        # 5a40 <_sk_callback_sse41+0xa63>
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,208                           // addps         %xmm0,%xmm2
+  .byte  68,15,88,5,140,43,0,0               // addps         0x2b8c(%rip),%xmm8        # 5a50 <_sk_callback_sse41+0xa73>
+  .byte  68,15,89,5,148,43,0,0               // mulps         0x2b94(%rip),%xmm8        # 5a60 <_sk_callback_sse41+0xa83>
+  .byte  15,89,13,157,43,0,0                 // mulps         0x2b9d(%rip),%xmm1        # 5a70 <_sk_callback_sse41+0xa93>
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  15,89,21,162,43,0,0                 // mulps         0x2ba2(%rip),%xmm2        # 5a80 <_sk_callback_sse41+0xaa3>
+  .byte  69,15,40,208                        // movaps        %xmm8,%xmm10
+  .byte  68,15,92,210                        // subps         %xmm2,%xmm10
+  .byte  68,15,40,217                        // movaps        %xmm1,%xmm11
+  .byte  69,15,89,219                        // mulps         %xmm11,%xmm11
+  .byte  68,15,89,217                        // mulps         %xmm1,%xmm11
+  .byte  68,15,40,13,150,43,0,0              // movaps        0x2b96(%rip),%xmm9        # 5a90 <_sk_callback_sse41+0xab3>
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,194,195,1                     // cmpltps       %xmm11,%xmm0
+  .byte  15,40,21,150,43,0,0                 // movaps        0x2b96(%rip),%xmm2        # 5aa0 <_sk_callback_sse41+0xac3>
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  68,15,40,37,155,43,0,0              // movaps        0x2b9b(%rip),%xmm12        # 5ab0 <_sk_callback_sse41+0xad3>
+  .byte  65,15,89,204                        // mulps         %xmm12,%xmm1
+  .byte  102,65,15,56,20,203                 // blendvps      %xmm0,%xmm11,%xmm1
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  69,15,89,219                        // mulps         %xmm11,%xmm11
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,194,195,1                     // cmpltps       %xmm11,%xmm0
+  .byte  68,15,88,194                        // addps         %xmm2,%xmm8
+  .byte  69,15,89,196                        // mulps         %xmm12,%xmm8
+  .byte  102,69,15,56,20,195                 // blendvps      %xmm0,%xmm11,%xmm8
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  69,15,89,219                        // mulps         %xmm11,%xmm11
+  .byte  69,15,89,218                        // mulps         %xmm10,%xmm11
+  .byte  69,15,194,203,1                     // cmpltps       %xmm11,%xmm9
+  .byte  65,15,88,210                        // addps         %xmm10,%xmm2
+  .byte  65,15,89,212                        // mulps         %xmm12,%xmm2
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,65,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm2
+  .byte  15,89,13,84,43,0,0                  // mulps         0x2b54(%rip),%xmm1        # 5ac0 <_sk_callback_sse41+0xae3>
+  .byte  15,89,21,93,43,0,0                  // mulps         0x2b5d(%rip),%xmm2        # 5ad0 <_sk_callback_sse41+0xaf3>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_a8_sse41
+.globl _sk_load_a8_sse41
+FUNCTION(_sk_load_a8_sse41)
+_sk_load_a8_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,38                              // jne           2fae <_sk_load_a8_sse41+0x30>
+  .byte  102,65,15,56,49,4,18                // pmovzxbd      (%r10,%rdx,1),%xmm0
+  .byte  102,15,219,5,73,43,0,0              // pand          0x2b49(%rip),%xmm0        # 5ae0 <_sk_callback_sse41+0xb03>
+  .byte  15,91,216                           // cvtdq2ps      %xmm0,%xmm3
+  .byte  15,89,29,79,43,0,0                  // mulps         0x2b4f(%rip),%xmm3        # 5af0 <_sk_callback_sse41+0xb13>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,39                              // je            2fe6 <_sk_load_a8_sse41+0x68>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            2fda <_sk_load_a8_sse41+0x5c>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,196                             // jne           2f8f <_sk_load_a8_sse41+0x11>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,192,69                   // pshufd        $0x45,%xmm0,%xmm0
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,15,58,34,192,1                  // pinsrd        $0x1,%eax,%xmm0
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,15,58,34,192,0                  // pinsrd        $0x0,%eax,%xmm0
+  .byte  235,156                             // jmp           2f8f <_sk_load_a8_sse41+0x11>
+
+HIDDEN _sk_gather_a8_sse41
+.globl _sk_gather_a8_sse41
+FUNCTION(_sk_gather_a8_sse41)
+_sk_gather_a8_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,58,22,192,1               // pextrq        $0x1,%xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,65,15,58,32,4,25,0              // pinsrb        $0x0,(%r9,%rbx,1),%xmm0
+  .byte  102,67,15,58,32,4,25,1              // pinsrb        $0x1,(%r9,%r11,1),%xmm0
+  .byte  67,15,182,28,17                     // movzbl        (%r9,%r10,1),%ebx
+  .byte  102,15,58,32,195,2                  // pinsrb        $0x2,%ebx,%xmm0
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  102,15,58,32,192,3                  // pinsrb        $0x3,%eax,%xmm0
+  .byte  102,15,56,49,192                    // pmovzxbd      %xmm0,%xmm0
+  .byte  15,91,216                           // cvtdq2ps      %xmm0,%xmm3
+  .byte  15,89,29,157,42,0,0                 // mulps         0x2a9d(%rip),%xmm3        # 5b00 <_sk_callback_sse41+0xb23>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,15,239,210                      // pxor          %xmm2,%xmm2
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_a8_sse41
+.globl _sk_store_a8_sse41
+FUNCTION(_sk_store_a8_sse41)
+_sk_store_a8_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  68,15,40,5,144,42,0,0               // movaps        0x2a90(%rip),%xmm8        # 5b10 <_sk_callback_sse41+0xb33>
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,69,15,56,43,192                 // packusdw      %xmm8,%xmm8
+  .byte  102,69,15,103,192                   // packuswb      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,13                              // jne           30a6 <_sk_store_a8_sse41+0x33>
+  .byte  102,68,15,126,192                   // movd          %xmm8,%eax
+  .byte  65,137,4,18                         // mov           %eax,(%r10,%rdx,1)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,69,15,56,49,192                 // pmovzxbd      %xmm8,%xmm8
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,30                              // je            30d7 <_sk_store_a8_sse41+0x64>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,15                              // je            30ce <_sk_store_a8_sse41+0x5b>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,221                             // jne           30a2 <_sk_store_a8_sse41+0x2f>
+  .byte  102,69,15,58,20,68,18,2,8           // pextrb        $0x8,%xmm8,0x2(%r10,%rdx,1)
+  .byte  102,69,15,58,20,68,18,1,4           // pextrb        $0x4,%xmm8,0x1(%r10,%rdx,1)
+  .byte  102,69,15,58,20,4,18,0              // pextrb        $0x0,%xmm8,(%r10,%rdx,1)
+  .byte  235,193                             // jmp           30a2 <_sk_store_a8_sse41+0x2f>
+
+HIDDEN _sk_load_g8_sse41
+.globl _sk_load_g8_sse41
+FUNCTION(_sk_load_g8_sse41)
+_sk_load_g8_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,42                              // jne           3115 <_sk_load_g8_sse41+0x34>
+  .byte  102,65,15,56,49,4,18                // pmovzxbd      (%r10,%rdx,1),%xmm0
+  .byte  102,15,219,5,38,42,0,0              // pand          0x2a26(%rip),%xmm0        # 5b20 <_sk_callback_sse41+0xb43>
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,44,42,0,0                   // mulps         0x2a2c(%rip),%xmm0        # 5b30 <_sk_callback_sse41+0xb53>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,51,42,0,0                  // movaps        0x2a33(%rip),%xmm3        # 5b40 <_sk_callback_sse41+0xb63>
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,39                              // je            314d <_sk_load_g8_sse41+0x6c>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            3141 <_sk_load_g8_sse41+0x60>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,192                             // jne           30f2 <_sk_load_g8_sse41+0x11>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,192,69                   // pshufd        $0x45,%xmm0,%xmm0
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,15,58,34,192,1                  // pinsrd        $0x1,%eax,%xmm0
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,15,58,34,192,0                  // pinsrd        $0x0,%eax,%xmm0
+  .byte  235,152                             // jmp           30f2 <_sk_load_g8_sse41+0x11>
+
+HIDDEN _sk_gather_g8_sse41
+.globl _sk_gather_g8_sse41
+FUNCTION(_sk_gather_g8_sse41)
+_sk_gather_g8_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,58,22,192,1               // pextrq        $0x1,%xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,65,15,58,32,4,25,0              // pinsrb        $0x0,(%r9,%rbx,1),%xmm0
+  .byte  102,67,15,58,32,4,25,1              // pinsrb        $0x1,(%r9,%r11,1),%xmm0
+  .byte  67,15,182,28,17                     // movzbl        (%r9,%r10,1),%ebx
+  .byte  102,15,58,32,195,2                  // pinsrb        $0x2,%ebx,%xmm0
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  102,15,58,32,192,3                  // pinsrb        $0x3,%eax,%xmm0
+  .byte  102,15,56,49,192                    // pmovzxbd      %xmm0,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,134,41,0,0                  // mulps         0x2986(%rip),%xmm0        # 5b50 <_sk_callback_sse41+0xb73>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,141,41,0,0                 // movaps        0x298d(%rip),%xmm3        # 5b60 <_sk_callback_sse41+0xb83>
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gather_i8_sse41
+.globl _sk_gather_i8_sse41
+FUNCTION(_sk_gather_i8_sse41)
+_sk_gather_i8_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,193                          // mov           %rax,%r9
+  .byte  77,133,201                          // test          %r9,%r9
+  .byte  116,5                               // je            31eb <_sk_gather_i8_sse41+0xf>
+  .byte  76,137,200                          // mov           %r9,%rax
+  .byte  235,2                               // jmp           31ed <_sk_gather_i8_sse41+0x11>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,58,22,192,1               // pextrq        $0x1,%xmm0,%rax
+  .byte  65,137,195                          // mov           %eax,%r11d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,72,15,126,195                   // movq          %xmm0,%rbx
+  .byte  65,137,222                          // mov           %ebx,%r14d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,67,15,58,32,4,50,0              // pinsrb        $0x0,(%r10,%r14,1),%xmm0
+  .byte  102,65,15,58,32,4,26,1              // pinsrb        $0x1,(%r10,%rbx,1),%xmm0
+  .byte  102,67,15,58,32,4,26,2              // pinsrb        $0x2,(%r10,%r11,1),%xmm0
+  .byte  102,65,15,58,32,4,2,3               // pinsrb        $0x3,(%r10,%rax,1),%xmm0
+  .byte  102,15,56,49,192                    // pmovzxbd      %xmm0,%xmm0
+  .byte  102,73,15,58,22,194,1               // pextrq        $0x1,%xmm0,%r10
+  .byte  102,72,15,126,195                   // movq          %xmm0,%rbx
+  .byte  73,139,65,8                         // mov           0x8(%r9),%rax
+  .byte  65,137,217                          // mov           %ebx,%r9d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  69,137,211                          // mov           %r10d,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  102,66,15,110,28,136                // movd          (%rax,%r9,4),%xmm3
+  .byte  102,15,58,34,28,24,1                // pinsrd        $0x1,(%rax,%rbx,1),%xmm3
+  .byte  102,66,15,58,34,28,152,2            // pinsrd        $0x2,(%rax,%r11,4),%xmm3
+  .byte  102,66,15,58,34,28,16,3             // pinsrd        $0x3,(%rax,%r10,1),%xmm3
+  .byte  102,15,111,5,224,40,0,0             // movdqa        0x28e0(%rip),%xmm0        # 5b70 <_sk_callback_sse41+0xb93>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,225,40,0,0               // movaps        0x28e1(%rip),%xmm8        # 5b80 <_sk_callback_sse41+0xba3>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,56,0,13,224,40,0,0           // pshufb        0x28e0(%rip),%xmm1        # 5b90 <_sk_callback_sse41+0xbb3>
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,111,211                      // movdqa        %xmm3,%xmm2
+  .byte  102,15,56,0,21,220,40,0,0           // pshufb        0x28dc(%rip),%xmm2        # 5ba0 <_sk_callback_sse41+0xbc3>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,114,211,24                   // psrld         $0x18,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_565_sse41
+.globl _sk_load_565_sse41
+FUNCTION(_sk_load_565_sse41)
+_sk_load_565_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,80                              // jne           3338 <_sk_load_565_sse41+0x5a>
+  .byte  102,65,15,56,51,20,82               // pmovzxwd      (%r10,%rdx,2),%xmm2
+  .byte  102,15,111,5,185,40,0,0             // movdqa        0x28b9(%rip),%xmm0        # 5bb0 <_sk_callback_sse41+0xbd3>
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,187,40,0,0                  // mulps         0x28bb(%rip),%xmm0        # 5bc0 <_sk_callback_sse41+0xbe3>
+  .byte  102,15,111,13,195,40,0,0            // movdqa        0x28c3(%rip),%xmm1        # 5bd0 <_sk_callback_sse41+0xbf3>
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,197,40,0,0                 // mulps         0x28c5(%rip),%xmm1        # 5be0 <_sk_callback_sse41+0xc03>
+  .byte  102,15,219,21,205,40,0,0            // pand          0x28cd(%rip),%xmm2        # 5bf0 <_sk_callback_sse41+0xc13>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,211,40,0,0                 // mulps         0x28d3(%rip),%xmm2        # 5c00 <_sk_callback_sse41+0xc23>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,218,40,0,0                 // movaps        0x28da(%rip),%xmm3        # 5c10 <_sk_callback_sse41+0xc33>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,210                      // pxor          %xmm2,%xmm2
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,39                              // je            3370 <_sk_load_565_sse41+0x92>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            3364 <_sk_load_565_sse41+0x86>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,154                             // jne           32ef <_sk_load_565_sse41+0x11>
+  .byte  65,15,183,68,82,4                   // movzwl        0x4(%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,208,69                   // pshufd        $0x45,%xmm0,%xmm2
+  .byte  65,15,183,68,82,2                   // movzwl        0x2(%r10,%rdx,2),%eax
+  .byte  102,15,58,34,208,1                  // pinsrd        $0x1,%eax,%xmm2
+  .byte  65,15,183,4,82                      // movzwl        (%r10,%rdx,2),%eax
+  .byte  102,15,58,34,208,0                  // pinsrd        $0x0,%eax,%xmm2
+  .byte  233,111,255,255,255                 // jmpq          32ef <_sk_load_565_sse41+0x11>
+
+HIDDEN _sk_gather_565_sse41
+.globl _sk_gather_565_sse41
+FUNCTION(_sk_gather_565_sse41)
+_sk_gather_565_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,58,22,192,1               // pextrq        $0x1,%xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,65,15,196,4,89,0                // pinsrw        $0x0,(%r9,%rbx,2),%xmm0
+  .byte  102,67,15,196,4,89,1                // pinsrw        $0x1,(%r9,%r11,2),%xmm0
+  .byte  67,15,183,28,81                     // movzwl        (%r9,%r10,2),%ebx
+  .byte  102,15,196,195,2                    // pinsrw        $0x2,%ebx,%xmm0
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  102,15,196,192,3                    // pinsrw        $0x3,%eax,%xmm0
+  .byte  102,15,56,51,208                    // pmovzxwd      %xmm0,%xmm2
+  .byte  102,15,111,5,54,40,0,0              // movdqa        0x2836(%rip),%xmm0        # 5c20 <_sk_callback_sse41+0xc43>
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,56,40,0,0                   // mulps         0x2838(%rip),%xmm0        # 5c30 <_sk_callback_sse41+0xc53>
+  .byte  102,15,111,13,64,40,0,0             // movdqa        0x2840(%rip),%xmm1        # 5c40 <_sk_callback_sse41+0xc63>
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,66,40,0,0                  // mulps         0x2842(%rip),%xmm1        # 5c50 <_sk_callback_sse41+0xc73>
+  .byte  102,15,219,21,74,40,0,0             // pand          0x284a(%rip),%xmm2        # 5c60 <_sk_callback_sse41+0xc83>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,80,40,0,0                  // mulps         0x2850(%rip),%xmm2        # 5c70 <_sk_callback_sse41+0xc93>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,87,40,0,0                  // movaps        0x2857(%rip),%xmm3        # 5c80 <_sk_callback_sse41+0xca3>
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_565_sse41
+.globl _sk_store_565_sse41
+FUNCTION(_sk_store_565_sse41)
+_sk_store_565_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  68,15,40,5,87,40,0,0                // movaps        0x2857(%rip),%xmm8        # 5c90 <_sk_callback_sse41+0xcb3>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,11                // pslld         $0xb,%xmm9
+  .byte  68,15,40,21,76,40,0,0               // movaps        0x284c(%rip),%xmm10        # 5ca0 <_sk_callback_sse41+0xcc3>
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,5                 // pslld         $0x5,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,69,15,86,194                    // orpd          %xmm10,%xmm8
+  .byte  102,69,15,56,43,192                 // packusdw      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           348b <_sk_store_565_sse41+0x5f>
+  .byte  242,68,15,17,4,80                   // movsd         %xmm8,(%rax,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,69,15,56,51,192                 // pmovzxwd      %xmm8,%xmm8
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,30                              // je            34bc <_sk_store_565_sse41+0x90>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,15                              // je            34b3 <_sk_store_565_sse41+0x87>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,221                             // jne           3487 <_sk_store_565_sse41+0x5b>
+  .byte  102,68,15,58,21,68,80,4,4           // pextrw        $0x4,%xmm8,0x4(%rax,%rdx,2)
+  .byte  102,68,15,58,21,68,80,2,2           // pextrw        $0x2,%xmm8,0x2(%rax,%rdx,2)
+  .byte  102,68,15,58,21,4,80,0              // pextrw        $0x0,%xmm8,(%rax,%rdx,2)
+  .byte  235,193                             // jmp           3487 <_sk_store_565_sse41+0x5b>
+
+HIDDEN _sk_load_4444_sse41
+.globl _sk_load_4444_sse41
+FUNCTION(_sk_load_4444_sse41)
+_sk_load_4444_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,95                              // jne           352f <_sk_load_4444_sse41+0x69>
+  .byte  102,65,15,56,51,28,82               // pmovzxwd      (%r10,%rdx,2),%xmm3
+  .byte  102,15,111,5,209,39,0,0             // movdqa        0x27d1(%rip),%xmm0        # 5cb0 <_sk_callback_sse41+0xcd3>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,211,39,0,0                  // mulps         0x27d3(%rip),%xmm0        # 5cc0 <_sk_callback_sse41+0xce3>
+  .byte  102,15,111,13,219,39,0,0            // movdqa        0x27db(%rip),%xmm1        # 5cd0 <_sk_callback_sse41+0xcf3>
+  .byte  102,15,219,203                      // pand          %xmm3,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,221,39,0,0                 // mulps         0x27dd(%rip),%xmm1        # 5ce0 <_sk_callback_sse41+0xd03>
+  .byte  102,15,111,21,229,39,0,0            // movdqa        0x27e5(%rip),%xmm2        # 5cf0 <_sk_callback_sse41+0xd13>
+  .byte  102,15,219,211                      // pand          %xmm3,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,231,39,0,0                 // mulps         0x27e7(%rip),%xmm2        # 5d00 <_sk_callback_sse41+0xd23>
+  .byte  102,15,219,29,239,39,0,0            // pand          0x27ef(%rip),%xmm3        # 5d10 <_sk_callback_sse41+0xd33>
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  15,89,29,245,39,0,0                 // mulps         0x27f5(%rip),%xmm3        # 5d20 <_sk_callback_sse41+0xd43>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,39                              // je            3567 <_sk_load_4444_sse41+0xa1>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            355b <_sk_load_4444_sse41+0x95>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,139                             // jne           34d7 <_sk_load_4444_sse41+0x11>
+  .byte  65,15,183,68,82,4                   // movzwl        0x4(%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,216,69                   // pshufd        $0x45,%xmm0,%xmm3
+  .byte  65,15,183,68,82,2                   // movzwl        0x2(%r10,%rdx,2),%eax
+  .byte  102,15,58,34,216,1                  // pinsrd        $0x1,%eax,%xmm3
+  .byte  65,15,183,4,82                      // movzwl        (%r10,%rdx,2),%eax
+  .byte  102,15,58,34,216,0                  // pinsrd        $0x0,%eax,%xmm3
+  .byte  233,96,255,255,255                  // jmpq          34d7 <_sk_load_4444_sse41+0x11>
+
+HIDDEN _sk_gather_4444_sse41
+.globl _sk_gather_4444_sse41
+FUNCTION(_sk_gather_4444_sse41)
+_sk_gather_4444_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,58,22,192,1               // pextrq        $0x1,%xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,65,15,196,4,89,0                // pinsrw        $0x0,(%r9,%rbx,2),%xmm0
+  .byte  102,67,15,196,4,89,1                // pinsrw        $0x1,(%r9,%r11,2),%xmm0
+  .byte  67,15,183,28,81                     // movzwl        (%r9,%r10,2),%ebx
+  .byte  102,15,196,195,2                    // pinsrw        $0x2,%ebx,%xmm0
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  102,15,196,192,3                    // pinsrw        $0x3,%eax,%xmm0
+  .byte  102,15,56,51,216                    // pmovzxwd      %xmm0,%xmm3
+  .byte  102,15,111,5,79,39,0,0              // movdqa        0x274f(%rip),%xmm0        # 5d30 <_sk_callback_sse41+0xd53>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,81,39,0,0                   // mulps         0x2751(%rip),%xmm0        # 5d40 <_sk_callback_sse41+0xd63>
+  .byte  102,15,111,13,89,39,0,0             // movdqa        0x2759(%rip),%xmm1        # 5d50 <_sk_callback_sse41+0xd73>
+  .byte  102,15,219,203                      // pand          %xmm3,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,91,39,0,0                  // mulps         0x275b(%rip),%xmm1        # 5d60 <_sk_callback_sse41+0xd83>
+  .byte  102,15,111,21,99,39,0,0             // movdqa        0x2763(%rip),%xmm2        # 5d70 <_sk_callback_sse41+0xd93>
+  .byte  102,15,219,211                      // pand          %xmm3,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,101,39,0,0                 // mulps         0x2765(%rip),%xmm2        # 5d80 <_sk_callback_sse41+0xda3>
+  .byte  102,15,219,29,109,39,0,0            // pand          0x276d(%rip),%xmm3        # 5d90 <_sk_callback_sse41+0xdb3>
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  15,89,29,115,39,0,0                 // mulps         0x2773(%rip),%xmm3        # 5da0 <_sk_callback_sse41+0xdc3>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_4444_sse41
+.globl _sk_store_4444_sse41
+FUNCTION(_sk_store_4444_sse41)
+_sk_store_4444_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  68,15,40,5,113,39,0,0               // movaps        0x2771(%rip),%xmm8        # 5db0 <_sk_callback_sse41+0xdd3>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,12                // pslld         $0xc,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,8                 // pslld         $0x8,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,4                 // pslld         $0x4,%xmm9
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,69,15,86,193                    // orpd          %xmm9,%xmm8
+  .byte  102,69,15,86,194                    // orpd          %xmm10,%xmm8
+  .byte  102,69,15,56,43,192                 // packusdw      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           36a5 <_sk_store_4444_sse41+0x73>
+  .byte  242,68,15,17,4,80                   // movsd         %xmm8,(%rax,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,69,15,56,51,192                 // pmovzxwd      %xmm8,%xmm8
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,30                              // je            36d6 <_sk_store_4444_sse41+0xa4>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,15                              // je            36cd <_sk_store_4444_sse41+0x9b>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,221                             // jne           36a1 <_sk_store_4444_sse41+0x6f>
+  .byte  102,68,15,58,21,68,80,4,4           // pextrw        $0x4,%xmm8,0x4(%rax,%rdx,2)
+  .byte  102,68,15,58,21,68,80,2,2           // pextrw        $0x2,%xmm8,0x2(%rax,%rdx,2)
+  .byte  102,68,15,58,21,4,80,0              // pextrw        $0x0,%xmm8,(%rax,%rdx,2)
+  .byte  235,193                             // jmp           36a1 <_sk_store_4444_sse41+0x6f>
+
+HIDDEN _sk_load_8888_sse41
+.globl _sk_load_8888_sse41
+FUNCTION(_sk_load_8888_sse41)
+_sk_load_8888_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,88                              // jne           3742 <_sk_load_8888_sse41+0x62>
+  .byte  243,15,111,28,144                   // movdqu        (%rax,%rdx,4),%xmm3
+  .byte  102,15,111,5,201,38,0,0             // movdqa        0x26c9(%rip),%xmm0        # 5dc0 <_sk_callback_sse41+0xde3>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,202,38,0,0               // movaps        0x26ca(%rip),%xmm8        # 5dd0 <_sk_callback_sse41+0xdf3>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,56,0,13,201,38,0,0           // pshufb        0x26c9(%rip),%xmm1        # 5de0 <_sk_callback_sse41+0xe03>
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,111,211                      // movdqa        %xmm3,%xmm2
+  .byte  102,15,56,0,21,197,38,0,0           // pshufb        0x26c5(%rip),%xmm2        # 5df0 <_sk_callback_sse41+0xe13>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,114,211,24                   // psrld         $0x18,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,31                              // je            3772 <_sk_load_8888_sse41+0x92>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,17                              // je            376a <_sk_load_8888_sse41+0x8a>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,144                             // jne           36ef <_sk_load_8888_sse41+0xf>
+  .byte  102,15,110,68,144,8                 // movd          0x8(%rax,%rdx,4),%xmm0
+  .byte  102,15,112,216,69                   // pshufd        $0x45,%xmm0,%xmm3
+  .byte  102,15,58,34,92,144,4,1             // pinsrd        $0x1,0x4(%rax,%rdx,4),%xmm3
+  .byte  102,15,58,34,28,144,0               // pinsrd        $0x0,(%rax,%rdx,4),%xmm3
+  .byte  233,113,255,255,255                 // jmpq          36ef <_sk_load_8888_sse41+0xf>
+
+HIDDEN _sk_gather_8888_sse41
+.globl _sk_gather_8888_sse41
+FUNCTION(_sk_gather_8888_sse41)
+_sk_gather_8888_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,126,192                   // movq          %xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,58,22,195,1               // pextrq        $0x1,%xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,67,15,110,28,145                // movd          (%r9,%r10,4),%xmm3
+  .byte  102,65,15,58,34,28,129,1            // pinsrd        $0x1,(%r9,%rax,4),%xmm3
+  .byte  102,65,15,58,34,28,153,2            // pinsrd        $0x2,(%r9,%rbx,4),%xmm3
+  .byte  102,67,15,58,34,28,153,3            // pinsrd        $0x3,(%r9,%r11,4),%xmm3
+  .byte  102,15,111,5,33,38,0,0              // movdqa        0x2621(%rip),%xmm0        # 5e00 <_sk_callback_sse41+0xe23>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,34,38,0,0                // movaps        0x2622(%rip),%xmm8        # 5e10 <_sk_callback_sse41+0xe33>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,56,0,13,33,38,0,0            // pshufb        0x2621(%rip),%xmm1        # 5e20 <_sk_callback_sse41+0xe43>
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,111,211                      // movdqa        %xmm3,%xmm2
+  .byte  102,15,56,0,21,29,38,0,0            // pshufb        0x261d(%rip),%xmm2        # 5e30 <_sk_callback_sse41+0xe53>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,114,211,24                   // psrld         $0x18,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_8888_sse41
+.globl _sk_store_8888_sse41
+FUNCTION(_sk_store_8888_sse41)
+_sk_store_8888_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  68,15,40,5,8,38,0,0                 // movaps        0x2608(%rip),%xmm8        # 5e40 <_sk_callback_sse41+0xe63>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,8                 // pslld         $0x8,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,16                // pslld         $0x10,%xmm9
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,114,240,24                // pslld         $0x18,%xmm8
+  .byte  102,69,15,235,193                   // por           %xmm9,%xmm8
+  .byte  102,69,15,235,194                   // por           %xmm10,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3898 <_sk_store_8888_sse41+0x6d>
+  .byte  243,68,15,127,4,144                 // movdqu        %xmm8,(%rax,%rdx,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,30                              // je            38c3 <_sk_store_8888_sse41+0x98>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,15                              // je            38ba <_sk_store_8888_sse41+0x8f>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,227                             // jne           3894 <_sk_store_8888_sse41+0x69>
+  .byte  102,68,15,58,22,68,144,8,2          // pextrd        $0x2,%xmm8,0x8(%rax,%rdx,4)
+  .byte  102,68,15,58,22,68,144,4,1          // pextrd        $0x1,%xmm8,0x4(%rax,%rdx,4)
+  .byte  102,68,15,126,4,144                 // movd          %xmm8,(%rax,%rdx,4)
+  .byte  235,201                             // jmp           3894 <_sk_store_8888_sse41+0x69>
+
+HIDDEN _sk_load_f16_sse41
+.globl _sk_load_f16_sse41
+FUNCTION(_sk_load_f16_sse41)
+_sk_load_f16_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,60,1,0,0                     // jne           3a15 <_sk_load_f16_sse41+0x14a>
+  .byte  102,15,16,4,208                     // movupd        (%rax,%rdx,8),%xmm0
+  .byte  243,15,111,76,208,16                // movdqu        0x10(%rax,%rdx,8),%xmm1
+  .byte  102,68,15,40,200                    // movapd        %xmm0,%xmm9
+  .byte  102,68,15,97,201                    // punpcklwd     %xmm1,%xmm9
+  .byte  102,15,105,193                      // punpckhwd     %xmm1,%xmm0
+  .byte  102,69,15,111,217                   // movdqa        %xmm9,%xmm11
+  .byte  102,68,15,97,216                    // punpcklwd     %xmm0,%xmm11
+  .byte  102,68,15,105,200                   // punpckhwd     %xmm0,%xmm9
+  .byte  102,65,15,56,51,203                 // pmovzxwd      %xmm11,%xmm1
+  .byte  102,68,15,111,5,64,37,0,0           // movdqa        0x2540(%rip),%xmm8        # 5e50 <_sk_callback_sse41+0xe73>
+  .byte  102,15,111,209                      // movdqa        %xmm1,%xmm2
+  .byte  102,65,15,219,208                   // pand          %xmm8,%xmm2
+  .byte  102,15,239,202                      // pxor          %xmm2,%xmm1
+  .byte  102,15,111,29,59,37,0,0             // movdqa        0x253b(%rip),%xmm3        # 5e60 <_sk_callback_sse41+0xe83>
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,56,63,195                    // pmaxud        %xmm3,%xmm0
+  .byte  102,15,118,193                      // pcmpeqd       %xmm1,%xmm0
+  .byte  102,15,114,241,13                   // pslld         $0xd,%xmm1
+  .byte  102,15,235,202                      // por           %xmm2,%xmm1
+  .byte  102,68,15,111,21,39,37,0,0          // movdqa        0x2527(%rip),%xmm10        # 5e70 <_sk_callback_sse41+0xe93>
+  .byte  102,65,15,254,202                   // paddd         %xmm10,%xmm1
+  .byte  102,15,219,193                      // pand          %xmm1,%xmm0
+  .byte  102,65,15,115,219,8                 // psrldq        $0x8,%xmm11
+  .byte  102,69,15,56,51,219                 // pmovzxwd      %xmm11,%xmm11
+  .byte  102,65,15,111,211                   // movdqa        %xmm11,%xmm2
+  .byte  102,65,15,219,208                   // pand          %xmm8,%xmm2
+  .byte  102,68,15,239,218                   // pxor          %xmm2,%xmm11
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,65,15,111,203                   // movdqa        %xmm11,%xmm1
+  .byte  102,15,56,63,203                    // pmaxud        %xmm3,%xmm1
+  .byte  102,65,15,118,203                   // pcmpeqd       %xmm11,%xmm1
+  .byte  102,65,15,114,243,13                // pslld         $0xd,%xmm11
+  .byte  102,68,15,235,218                   // por           %xmm2,%xmm11
+  .byte  102,69,15,254,218                   // paddd         %xmm10,%xmm11
+  .byte  102,65,15,219,203                   // pand          %xmm11,%xmm1
+  .byte  102,69,15,56,51,217                 // pmovzxwd      %xmm9,%xmm11
+  .byte  102,69,15,111,227                   // movdqa        %xmm11,%xmm12
+  .byte  102,69,15,219,224                   // pand          %xmm8,%xmm12
+  .byte  102,69,15,239,220                   // pxor          %xmm12,%xmm11
+  .byte  102,65,15,114,244,16                // pslld         $0x10,%xmm12
+  .byte  102,65,15,111,211                   // movdqa        %xmm11,%xmm2
+  .byte  102,15,56,63,211                    // pmaxud        %xmm3,%xmm2
+  .byte  102,65,15,118,211                   // pcmpeqd       %xmm11,%xmm2
+  .byte  102,65,15,114,243,13                // pslld         $0xd,%xmm11
+  .byte  102,69,15,235,220                   // por           %xmm12,%xmm11
+  .byte  102,69,15,254,218                   // paddd         %xmm10,%xmm11
+  .byte  102,65,15,219,211                   // pand          %xmm11,%xmm2
+  .byte  102,65,15,115,217,8                 // psrldq        $0x8,%xmm9
+  .byte  102,69,15,56,51,201                 // pmovzxwd      %xmm9,%xmm9
+  .byte  102,69,15,219,193                   // pand          %xmm9,%xmm8
+  .byte  102,69,15,239,200                   // pxor          %xmm8,%xmm9
+  .byte  102,65,15,114,240,16                // pslld         $0x10,%xmm8
+  .byte  102,65,15,56,63,217                 // pmaxud        %xmm9,%xmm3
+  .byte  102,65,15,118,217                   // pcmpeqd       %xmm9,%xmm3
+  .byte  102,65,15,114,241,13                // pslld         $0xd,%xmm9
+  .byte  102,69,15,235,200                   // por           %xmm8,%xmm9
+  .byte  102,69,15,254,202                   // paddd         %xmm10,%xmm9
+  .byte  102,65,15,219,217                   // pand          %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  242,15,16,4,208                     // movsd         (%rax,%rdx,8),%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,13                              // jne           3a2d <_sk_load_f16_sse41+0x162>
+  .byte  243,15,126,192                      // movq          %xmm0,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,183,254,255,255                 // jmpq          38e4 <_sk_load_f16_sse41+0x19>
+  .byte  102,15,22,68,208,8                  // movhpd        0x8(%rax,%rdx,8),%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  15,130,163,254,255,255              // jb            38e4 <_sk_load_f16_sse41+0x19>
+  .byte  243,15,126,76,208,16                // movq          0x10(%rax,%rdx,8),%xmm1
+  .byte  233,152,254,255,255                 // jmpq          38e4 <_sk_load_f16_sse41+0x19>
+
+HIDDEN _sk_gather_f16_sse41
+.globl _sk_gather_f16_sse41
+FUNCTION(_sk_gather_f16_sse41)
+_sk_gather_f16_sse41:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,56,64,209                    // pmulld        %xmm1,%xmm2
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,194                      // paddd         %xmm2,%xmm0
+  .byte  102,72,15,126,192                   // movq          %xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,58,22,195,1               // pextrq        $0x1,%xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,67,15,126,4,217                 // movq          (%r9,%r11,8),%xmm0
+  .byte  243,65,15,126,12,217                // movq          (%r9,%rbx,8),%xmm1
+  .byte  102,15,108,200                      // punpcklqdq    %xmm0,%xmm1
+  .byte  243,65,15,126,4,193                 // movq          (%r9,%rax,8),%xmm0
+  .byte  243,67,15,126,20,209                // movq          (%r9,%r10,8),%xmm2
+  .byte  102,15,108,208                      // punpcklqdq    %xmm0,%xmm2
+  .byte  102,68,15,111,202                   // movdqa        %xmm2,%xmm9
+  .byte  102,68,15,97,201                    // punpcklwd     %xmm1,%xmm9
+  .byte  102,15,105,209                      // punpckhwd     %xmm1,%xmm2
+  .byte  102,69,15,111,217                   // movdqa        %xmm9,%xmm11
+  .byte  102,68,15,97,218                    // punpcklwd     %xmm2,%xmm11
+  .byte  102,68,15,105,202                   // punpckhwd     %xmm2,%xmm9
+  .byte  102,65,15,56,51,203                 // pmovzxwd      %xmm11,%xmm1
+  .byte  102,68,15,111,5,173,35,0,0          // movdqa        0x23ad(%rip),%xmm8        # 5e80 <_sk_callback_sse41+0xea3>
+  .byte  102,15,111,209                      // movdqa        %xmm1,%xmm2
+  .byte  102,65,15,219,208                   // pand          %xmm8,%xmm2
+  .byte  102,15,239,202                      // pxor          %xmm2,%xmm1
+  .byte  102,15,111,29,168,35,0,0            // movdqa        0x23a8(%rip),%xmm3        # 5e90 <_sk_callback_sse41+0xeb3>
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,56,63,195                    // pmaxud        %xmm3,%xmm0
+  .byte  102,15,118,193                      // pcmpeqd       %xmm1,%xmm0
+  .byte  102,15,114,241,13                   // pslld         $0xd,%xmm1
+  .byte  102,15,235,202                      // por           %xmm2,%xmm1
+  .byte  102,68,15,111,21,148,35,0,0         // movdqa        0x2394(%rip),%xmm10        # 5ea0 <_sk_callback_sse41+0xec3>
+  .byte  102,65,15,254,202                   // paddd         %xmm10,%xmm1
+  .byte  102,15,219,193                      // pand          %xmm1,%xmm0
+  .byte  102,65,15,115,219,8                 // psrldq        $0x8,%xmm11
+  .byte  102,69,15,56,51,219                 // pmovzxwd      %xmm11,%xmm11
+  .byte  102,65,15,111,211                   // movdqa        %xmm11,%xmm2
+  .byte  102,65,15,219,208                   // pand          %xmm8,%xmm2
+  .byte  102,68,15,239,218                   // pxor          %xmm2,%xmm11
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,65,15,111,203                   // movdqa        %xmm11,%xmm1
+  .byte  102,15,56,63,203                    // pmaxud        %xmm3,%xmm1
+  .byte  102,65,15,118,203                   // pcmpeqd       %xmm11,%xmm1
+  .byte  102,65,15,114,243,13                // pslld         $0xd,%xmm11
+  .byte  102,68,15,235,218                   // por           %xmm2,%xmm11
+  .byte  102,69,15,254,218                   // paddd         %xmm10,%xmm11
+  .byte  102,65,15,219,203                   // pand          %xmm11,%xmm1
+  .byte  102,69,15,56,51,217                 // pmovzxwd      %xmm9,%xmm11
+  .byte  102,69,15,111,227                   // movdqa        %xmm11,%xmm12
+  .byte  102,69,15,219,224                   // pand          %xmm8,%xmm12
+  .byte  102,69,15,239,220                   // pxor          %xmm12,%xmm11
+  .byte  102,65,15,114,244,16                // pslld         $0x10,%xmm12
+  .byte  102,65,15,111,211                   // movdqa        %xmm11,%xmm2
+  .byte  102,15,56,63,211                    // pmaxud        %xmm3,%xmm2
+  .byte  102,65,15,118,211                   // pcmpeqd       %xmm11,%xmm2
+  .byte  102,65,15,114,243,13                // pslld         $0xd,%xmm11
+  .byte  102,69,15,235,220                   // por           %xmm12,%xmm11
+  .byte  102,69,15,254,218                   // paddd         %xmm10,%xmm11
+  .byte  102,65,15,219,211                   // pand          %xmm11,%xmm2
+  .byte  102,65,15,115,217,8                 // psrldq        $0x8,%xmm9
+  .byte  102,69,15,56,51,201                 // pmovzxwd      %xmm9,%xmm9
+  .byte  102,69,15,219,193                   // pand          %xmm9,%xmm8
+  .byte  102,69,15,239,200                   // pxor          %xmm8,%xmm9
+  .byte  102,65,15,114,240,16                // pslld         $0x10,%xmm8
+  .byte  102,65,15,56,63,217                 // pmaxud        %xmm9,%xmm3
+  .byte  102,65,15,118,217                   // pcmpeqd       %xmm9,%xmm3
+  .byte  102,65,15,114,241,13                // pslld         $0xd,%xmm9
+  .byte  102,69,15,235,200                   // por           %xmm8,%xmm9
+  .byte  102,69,15,254,202                   // paddd         %xmm10,%xmm9
+  .byte  102,65,15,219,217                   // pand          %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_f16_sse41
+.globl _sk_store_f16_sse41
+FUNCTION(_sk_store_f16_sse41)
+_sk_store_f16_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  102,68,15,111,21,201,34,0,0         // movdqa        0x22c9(%rip),%xmm10        # 5eb0 <_sk_callback_sse41+0xed3>
+  .byte  102,68,15,111,216                   // movdqa        %xmm0,%xmm11
+  .byte  102,69,15,219,218                   // pand          %xmm10,%xmm11
+  .byte  102,68,15,111,232                   // movdqa        %xmm0,%xmm13
+  .byte  102,69,15,239,235                   // pxor          %xmm11,%xmm13
+  .byte  102,68,15,111,13,188,34,0,0         // movdqa        0x22bc(%rip),%xmm9        # 5ec0 <_sk_callback_sse41+0xee3>
+  .byte  102,65,15,114,211,16                // psrld         $0x10,%xmm11
+  .byte  102,69,15,111,193                   // movdqa        %xmm9,%xmm8
+  .byte  102,69,15,102,197                   // pcmpgtd       %xmm13,%xmm8
+  .byte  102,65,15,114,213,13                // psrld         $0xd,%xmm13
+  .byte  102,68,15,111,37,173,34,0,0         // movdqa        0x22ad(%rip),%xmm12        # 5ed0 <_sk_callback_sse41+0xef3>
+  .byte  102,69,15,235,220                   // por           %xmm12,%xmm11
+  .byte  102,69,15,254,221                   // paddd         %xmm13,%xmm11
+  .byte  102,69,15,223,195                   // pandn         %xmm11,%xmm8
+  .byte  102,69,15,56,43,192                 // packusdw      %xmm8,%xmm8
+  .byte  102,68,15,111,217                   // movdqa        %xmm1,%xmm11
+  .byte  102,69,15,219,218                   // pand          %xmm10,%xmm11
+  .byte  102,68,15,111,241                   // movdqa        %xmm1,%xmm14
+  .byte  102,69,15,239,243                   // pxor          %xmm11,%xmm14
+  .byte  102,65,15,114,211,16                // psrld         $0x10,%xmm11
+  .byte  102,69,15,111,233                   // movdqa        %xmm9,%xmm13
+  .byte  102,69,15,102,238                   // pcmpgtd       %xmm14,%xmm13
+  .byte  102,65,15,114,214,13                // psrld         $0xd,%xmm14
+  .byte  102,69,15,235,220                   // por           %xmm12,%xmm11
+  .byte  102,69,15,254,222                   // paddd         %xmm14,%xmm11
+  .byte  102,69,15,223,235                   // pandn         %xmm11,%xmm13
+  .byte  102,69,15,56,43,237                 // packusdw      %xmm13,%xmm13
+  .byte  102,68,15,111,242                   // movdqa        %xmm2,%xmm14
+  .byte  102,69,15,219,242                   // pand          %xmm10,%xmm14
+  .byte  102,68,15,111,250                   // movdqa        %xmm2,%xmm15
+  .byte  102,69,15,239,254                   // pxor          %xmm14,%xmm15
+  .byte  102,65,15,114,214,16                // psrld         $0x10,%xmm14
+  .byte  102,69,15,111,217                   // movdqa        %xmm9,%xmm11
+  .byte  102,69,15,102,223                   // pcmpgtd       %xmm15,%xmm11
+  .byte  102,65,15,114,215,13                // psrld         $0xd,%xmm15
+  .byte  102,69,15,235,244                   // por           %xmm12,%xmm14
+  .byte  102,69,15,254,247                   // paddd         %xmm15,%xmm14
+  .byte  102,69,15,223,222                   // pandn         %xmm14,%xmm11
+  .byte  102,69,15,56,43,219                 // packusdw      %xmm11,%xmm11
+  .byte  102,68,15,219,211                   // pand          %xmm3,%xmm10
+  .byte  102,68,15,111,243                   // movdqa        %xmm3,%xmm14
+  .byte  102,69,15,239,242                   // pxor          %xmm10,%xmm14
+  .byte  102,65,15,114,210,16                // psrld         $0x10,%xmm10
+  .byte  102,69,15,102,206                   // pcmpgtd       %xmm14,%xmm9
+  .byte  102,65,15,114,214,13                // psrld         $0xd,%xmm14
+  .byte  102,69,15,235,212                   // por           %xmm12,%xmm10
+  .byte  102,69,15,254,214                   // paddd         %xmm14,%xmm10
+  .byte  102,69,15,223,202                   // pandn         %xmm10,%xmm9
+  .byte  102,69,15,56,43,201                 // packusdw      %xmm9,%xmm9
+  .byte  102,69,15,97,197                    // punpcklwd     %xmm13,%xmm8
+  .byte  102,69,15,97,217                    // punpcklwd     %xmm9,%xmm11
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,69,15,98,203                    // punpckldq     %xmm11,%xmm9
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,21                              // jne           3d19 <_sk_store_f16_sse41+0x140>
+  .byte  68,15,17,12,208                     // movups        %xmm9,(%rax,%rdx,8)
+  .byte  102,69,15,106,195                   // punpckhdq     %xmm11,%xmm8
+  .byte  243,68,15,127,68,208,16             // movdqu        %xmm8,0x10(%rax,%rdx,8)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,68,15,214,12,208                // movq          %xmm9,(%rax,%rdx,8)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            3d15 <_sk_store_f16_sse41+0x13c>
+  .byte  102,68,15,23,76,208,8               // movhpd        %xmm9,0x8(%rax,%rdx,8)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            3d15 <_sk_store_f16_sse41+0x13c>
+  .byte  102,69,15,106,195                   // punpckhdq     %xmm11,%xmm8
+  .byte  102,68,15,214,68,208,16             // movq          %xmm8,0x10(%rax,%rdx,8)
+  .byte  235,213                             // jmp           3d15 <_sk_store_f16_sse41+0x13c>
+
+HIDDEN _sk_load_u16_be_sse41
+.globl _sk_load_u16_be_sse41
+FUNCTION(_sk_load_u16_be_sse41)
+_sk_load_u16_be_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,185,0,0,0                    // jne           3e0f <_sk_load_u16_be_sse41+0xcf>
+  .byte  102,65,15,16,4,65                   // movupd        (%r9,%rax,2),%xmm0
+  .byte  243,65,15,111,76,65,16              // movdqu        0x10(%r9,%rax,2),%xmm1
+  .byte  102,15,40,208                       // movapd        %xmm0,%xmm2
+  .byte  102,15,97,209                       // punpcklwd     %xmm1,%xmm2
+  .byte  102,15,105,193                      // punpckhwd     %xmm1,%xmm0
+  .byte  102,15,111,202                      // movdqa        %xmm2,%xmm1
+  .byte  102,15,97,200                       // punpcklwd     %xmm0,%xmm1
+  .byte  102,15,105,208                      // punpckhwd     %xmm0,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,113,240,8                    // psllw         $0x8,%xmm0
+  .byte  102,15,112,217,78                   // pshufd        $0x4e,%xmm1,%xmm3
+  .byte  102,15,113,209,8                    // psrlw         $0x8,%xmm1
+  .byte  102,15,235,200                      // por           %xmm0,%xmm1
+  .byte  102,15,56,51,193                    // pmovzxwd      %xmm1,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,62,33,0,0                // movaps        0x213e(%rip),%xmm8        # 5ee0 <_sk_callback_sse41+0xf03>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,113,241,8                    // psllw         $0x8,%xmm1
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,15,235,217                      // por           %xmm1,%xmm3
+  .byte  102,15,56,51,203                    // pmovzxwd      %xmm3,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,68,15,111,202                   // movdqa        %xmm2,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,15,112,218,78                   // pshufd        $0x4e,%xmm2,%xmm3
+  .byte  102,15,113,210,8                    // psrlw         $0x8,%xmm2
+  .byte  102,65,15,235,209                   // por           %xmm9,%xmm2
+  .byte  102,15,56,51,210                    // pmovzxwd      %xmm2,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,68,15,111,203                   // movdqa        %xmm3,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,65,15,235,217                   // por           %xmm9,%xmm3
+  .byte  102,15,56,51,219                    // pmovzxwd      %xmm3,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  242,65,15,16,4,65                   // movsd         (%r9,%rax,2),%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,13                              // jne           3e28 <_sk_load_u16_be_sse41+0xe8>
+  .byte  243,15,126,192                      // movq          %xmm0,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,59,255,255,255                  // jmpq          3d63 <_sk_load_u16_be_sse41+0x23>
+  .byte  102,65,15,22,68,65,8                // movhpd        0x8(%r9,%rax,2),%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  15,130,38,255,255,255               // jb            3d63 <_sk_load_u16_be_sse41+0x23>
+  .byte  243,65,15,126,76,65,16              // movq          0x10(%r9,%rax,2),%xmm1
+  .byte  233,26,255,255,255                  // jmpq          3d63 <_sk_load_u16_be_sse41+0x23>
+
+HIDDEN _sk_load_rgb_u16_be_sse41
+.globl _sk_load_rgb_u16_be_sse41
+FUNCTION(_sk_load_rgb_u16_be_sse41)
+_sk_load_rgb_u16_be_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,82                         // lea           (%rdx,%rdx,2),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,170,0,0,0                    // jne           3f05 <_sk_load_rgb_u16_be_sse41+0xbc>
+  .byte  243,65,15,111,20,65                 // movdqu        (%r9,%rax,2),%xmm2
+  .byte  243,65,15,111,92,65,8               // movdqu        0x8(%r9,%rax,2),%xmm3
+  .byte  102,15,115,219,4                    // psrldq        $0x4,%xmm3
+  .byte  102,15,111,194                      // movdqa        %xmm2,%xmm0
+  .byte  102,15,115,216,6                    // psrldq        $0x6,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,115,217,6                    // psrldq        $0x6,%xmm1
+  .byte  102,15,97,211                       // punpcklwd     %xmm3,%xmm2
+  .byte  102,15,97,193                       // punpcklwd     %xmm1,%xmm0
+  .byte  102,15,111,202                      // movdqa        %xmm2,%xmm1
+  .byte  102,15,97,200                       // punpcklwd     %xmm0,%xmm1
+  .byte  102,15,112,217,78                   // pshufd        $0x4e,%xmm1,%xmm3
+  .byte  102,15,105,208                      // punpckhwd     %xmm0,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,113,240,8                    // psllw         $0x8,%xmm0
+  .byte  102,15,113,209,8                    // psrlw         $0x8,%xmm1
+  .byte  102,15,235,200                      // por           %xmm0,%xmm1
+  .byte  102,15,56,51,193                    // pmovzxwd      %xmm1,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,54,32,0,0                // movaps        0x2036(%rip),%xmm8        # 5ef0 <_sk_callback_sse41+0xf13>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,113,241,8                    // psllw         $0x8,%xmm1
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,15,235,217                      // por           %xmm1,%xmm3
+  .byte  102,15,56,51,203                    // pmovzxwd      %xmm3,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,111,218                      // movdqa        %xmm2,%xmm3
+  .byte  102,15,113,243,8                    // psllw         $0x8,%xmm3
+  .byte  102,15,113,210,8                    // psrlw         $0x8,%xmm2
+  .byte  102,15,235,211                      // por           %xmm3,%xmm2
+  .byte  102,15,56,51,210                    // pmovzxwd      %xmm2,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,253,31,0,0                 // movaps        0x1ffd(%rip),%xmm3        # 5f00 <_sk_callback_sse41+0xf23>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,65,15,110,20,65                 // movd          (%r9,%rax,2),%xmm2
+  .byte  102,65,15,196,84,65,4,2             // pinsrw        $0x2,0x4(%r9,%rax,2),%xmm2
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,13                              // jne           3f2a <_sk_load_rgb_u16_be_sse41+0xe1>
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  233,85,255,255,255                  // jmpq          3e7f <_sk_load_rgb_u16_be_sse41+0x36>
+  .byte  102,65,15,110,68,65,6               // movd          0x6(%r9,%rax,2),%xmm0
+  .byte  102,65,15,196,68,65,10,2            // pinsrw        $0x2,0xa(%r9,%rax,2),%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,24                              // jb            3f5b <_sk_load_rgb_u16_be_sse41+0x112>
+  .byte  102,65,15,110,92,65,12              // movd          0xc(%r9,%rax,2),%xmm3
+  .byte  102,65,15,196,92,65,16,2            // pinsrw        $0x2,0x10(%r9,%rax,2),%xmm3
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,36,255,255,255                  // jmpq          3e7f <_sk_load_rgb_u16_be_sse41+0x36>
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  233,27,255,255,255                  // jmpq          3e7f <_sk_load_rgb_u16_be_sse41+0x36>
+
+HIDDEN _sk_store_u16_be_sse41
+.globl _sk_store_u16_be_sse41
+FUNCTION(_sk_store_u16_be_sse41)
+_sk_store_u16_be_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  68,15,40,21,151,31,0,0              // movaps        0x1f97(%rip),%xmm10        # 5f10 <_sk_callback_sse41+0xf33>
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,89,194                        // mulps         %xmm10,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,69,15,56,43,192                 // packusdw      %xmm8,%xmm8
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,65,15,113,208,8                 // psrlw         $0x8,%xmm8
+  .byte  102,69,15,235,193                   // por           %xmm9,%xmm8
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  69,15,89,202                        // mulps         %xmm10,%xmm9
+  .byte  102,69,15,91,217                    // cvtps2dq      %xmm9,%xmm11
+  .byte  102,69,15,56,43,219                 // packusdw      %xmm11,%xmm11
+  .byte  102,69,15,111,203                   // movdqa        %xmm11,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,65,15,113,211,8                 // psrlw         $0x8,%xmm11
+  .byte  102,69,15,235,217                   // por           %xmm9,%xmm11
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  69,15,89,202                        // mulps         %xmm10,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,69,15,56,43,201                 // packusdw      %xmm9,%xmm9
+  .byte  102,69,15,111,225                   // movdqa        %xmm9,%xmm12
+  .byte  102,65,15,113,244,8                 // psllw         $0x8,%xmm12
+  .byte  102,65,15,113,209,8                 // psrlw         $0x8,%xmm9
+  .byte  102,69,15,235,204                   // por           %xmm12,%xmm9
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,69,15,56,43,210                 // packusdw      %xmm10,%xmm10
+  .byte  102,69,15,111,226                   // movdqa        %xmm10,%xmm12
+  .byte  102,65,15,113,244,8                 // psllw         $0x8,%xmm12
+  .byte  102,65,15,113,210,8                 // psrlw         $0x8,%xmm10
+  .byte  102,69,15,235,212                   // por           %xmm12,%xmm10
+  .byte  102,69,15,97,195                    // punpcklwd     %xmm11,%xmm8
+  .byte  102,69,15,97,202                    // punpcklwd     %xmm10,%xmm9
+  .byte  102,69,15,111,208                   // movdqa        %xmm8,%xmm10
+  .byte  102,69,15,98,209                    // punpckldq     %xmm9,%xmm10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,21                              // jne           4047 <_sk_store_u16_be_sse41+0xe3>
+  .byte  69,15,17,20,65                      // movups        %xmm10,(%r9,%rax,2)
+  .byte  102,69,15,106,193                   // punpckhdq     %xmm9,%xmm8
+  .byte  243,69,15,127,68,65,16              // movdqu        %xmm8,0x10(%r9,%rax,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,69,15,214,20,65                 // movq          %xmm10,(%r9,%rax,2)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            4043 <_sk_store_u16_be_sse41+0xdf>
+  .byte  102,69,15,23,84,65,8                // movhpd        %xmm10,0x8(%r9,%rax,2)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            4043 <_sk_store_u16_be_sse41+0xdf>
+  .byte  102,69,15,106,193                   // punpckhdq     %xmm9,%xmm8
+  .byte  102,69,15,214,68,65,16              // movq          %xmm8,0x10(%r9,%rax,2)
+  .byte  235,213                             // jmp           4043 <_sk_store_u16_be_sse41+0xdf>
+
+HIDDEN _sk_load_f32_sse41
+.globl _sk_load_f32_sse41
+FUNCTION(_sk_load_f32_sse41)
+_sk_load_f32_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  76,141,12,149,0,0,0,0               // lea           0x0(,%rdx,4),%r9
+  .byte  72,137,208                          // mov           %rdx,%rax
+  .byte  72,193,224,4                        // shl           $0x4,%rax
+  .byte  69,15,16,4,2                        // movups        (%r10,%rax,1),%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,66                              // jne           40ce <_sk_load_f32_sse41+0x60>
+  .byte  67,15,16,68,138,16                  // movups        0x10(%r10,%r9,4),%xmm0
+  .byte  67,15,16,92,138,32                  // movups        0x20(%r10,%r9,4),%xmm3
+  .byte  71,15,16,76,138,48                  // movups        0x30(%r10,%r9,4),%xmm9
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,20,208                           // unpcklps      %xmm0,%xmm2
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  65,15,20,201                        // unpcklps      %xmm9,%xmm1
+  .byte  68,15,21,192                        // unpckhps      %xmm0,%xmm8
+  .byte  65,15,21,217                        // unpckhps      %xmm9,%xmm3
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  15,18,202                           // movhlps       %xmm2,%xmm1
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  102,15,20,211                       // unpcklpd      %xmm3,%xmm2
+  .byte  65,15,18,216                        // movhlps       %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,8                               // jne           40e0 <_sk_load_f32_sse41+0x72>
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  235,190                             // jmp           409e <_sk_load_f32_sse41+0x30>
+  .byte  67,15,16,68,138,16                  // movups        0x10(%r10,%r9,4),%xmm0
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,8                               // jb            40f4 <_sk_load_f32_sse41+0x86>
+  .byte  67,15,16,92,138,32                  // movups        0x20(%r10,%r9,4),%xmm3
+  .byte  235,170                             // jmp           409e <_sk_load_f32_sse41+0x30>
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  235,165                             // jmp           409e <_sk_load_f32_sse41+0x30>
+
+HIDDEN _sk_store_f32_sse41
+.globl _sk_store_f32_sse41
+FUNCTION(_sk_store_f32_sse41)
+_sk_store_f32_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  76,141,12,149,0,0,0,0               // lea           0x0(,%rdx,4),%r9
+  .byte  72,137,208                          // mov           %rdx,%rax
+  .byte  72,193,224,4                        // shl           $0x4,%rax
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  68,15,20,201                        // unpcklps      %xmm1,%xmm9
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,20,195                        // unpcklps      %xmm3,%xmm8
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  68,15,21,209                        // unpckhps      %xmm1,%xmm10
+  .byte  68,15,40,218                        // movaps        %xmm2,%xmm11
+  .byte  68,15,21,219                        // unpckhps      %xmm3,%xmm11
+  .byte  69,15,40,225                        // movaps        %xmm9,%xmm12
+  .byte  102,69,15,20,224                    // unpcklpd      %xmm8,%xmm12
+  .byte  69,15,18,193                        // movhlps       %xmm9,%xmm8
+  .byte  69,15,40,202                        // movaps        %xmm10,%xmm9
+  .byte  102,69,15,20,203                    // unpcklpd      %xmm11,%xmm9
+  .byte  102,69,15,17,36,2                   // movupd        %xmm12,(%r10,%rax,1)
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,29                              // jne           416b <_sk_store_f32_sse41+0x72>
+  .byte  102,69,15,21,211                    // unpckhpd      %xmm11,%xmm10
+  .byte  71,15,17,68,138,16                  // movups        %xmm8,0x10(%r10,%r9,4)
+  .byte  102,71,15,17,76,138,32              // movupd        %xmm9,0x20(%r10,%r9,4)
+  .byte  102,71,15,17,84,138,48              // movupd        %xmm10,0x30(%r10,%r9,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,246                             // je            4167 <_sk_store_f32_sse41+0x6e>
+  .byte  71,15,17,68,138,16                  // movups        %xmm8,0x10(%r10,%r9,4)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,234                             // jb            4167 <_sk_store_f32_sse41+0x6e>
+  .byte  102,71,15,17,76,138,32              // movupd        %xmm9,0x20(%r10,%r9,4)
+  .byte  235,225                             // jmp           4167 <_sk_store_f32_sse41+0x6e>
+
+HIDDEN _sk_clamp_x_sse41
+.globl _sk_clamp_x_sse41
+FUNCTION(_sk_clamp_x_sse41)
+_sk_clamp_x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  68,15,93,192                        // minps         %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_y_sse41
+.globl _sk_clamp_y_sse41
+FUNCTION(_sk_clamp_y_sse41)
+_sk_clamp_y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,193                        // maxps         %xmm1,%xmm8
+  .byte  243,15,16,8                         // movss         (%rax),%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  68,15,93,193                        // minps         %xmm1,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_sse41
+.globl _sk_repeat_x_sse41
+FUNCTION(_sk_repeat_x_sse41)
+_sk_repeat_x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,94,200                        // divps         %xmm8,%xmm9
+  .byte  102,69,15,58,8,201,1                // roundps       $0x1,%xmm9,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_y_sse41
+.globl _sk_repeat_y_sse41
+FUNCTION(_sk_repeat_y_sse41)
+_sk_repeat_y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  69,15,94,200                        // divps         %xmm8,%xmm9
+  .byte  102,69,15,58,8,201,1                // roundps       $0x1,%xmm9,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  65,15,92,201                        // subps         %xmm9,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_sse41
+.globl _sk_mirror_x_sse41
+FUNCTION(_sk_mirror_x_sse41)
+_sk_mirror_x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  243,69,15,88,192                    // addss         %xmm8,%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  69,15,94,208                        // divps         %xmm8,%xmm10
+  .byte  102,69,15,58,8,210,1                // roundps       $0x1,%xmm10,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,92,194                        // subps         %xmm10,%xmm0
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,92,192                        // subps         %xmm0,%xmm8
+  .byte  65,15,84,192                        // andps         %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_y_sse41
+.globl _sk_mirror_y_sse41
+FUNCTION(_sk_mirror_y_sse41)
+_sk_mirror_y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  65,15,92,201                        // subps         %xmm9,%xmm1
+  .byte  243,69,15,88,192                    // addss         %xmm8,%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,94,208                        // divps         %xmm8,%xmm10
+  .byte  102,69,15,58,8,210,1                // roundps       $0x1,%xmm10,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,92,202                        // subps         %xmm10,%xmm1
+  .byte  65,15,92,201                        // subps         %xmm9,%xmm1
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,92,193                        // subps         %xmm1,%xmm8
+  .byte  65,15,84,200                        // andps         %xmm8,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_x_1_sse41
+.globl _sk_clamp_x_1_sse41
+FUNCTION(_sk_clamp_x_1_sse41)
+_sk_clamp_x_1_sse41:
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  68,15,93,5,110,28,0,0               // minps         0x1c6e(%rip),%xmm8        # 5f20 <_sk_callback_sse41+0xf43>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_1_sse41
+.globl _sk_repeat_x_1_sse41
+FUNCTION(_sk_repeat_x_1_sse41)
+_sk_repeat_x_1_sse41:
+  .byte  102,68,15,58,8,192,1                // roundps       $0x1,%xmm0,%xmm8
+  .byte  65,15,92,192                        // subps         %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_1_sse41
+.globl _sk_mirror_x_1_sse41
+FUNCTION(_sk_mirror_x_1_sse41)
+_sk_mirror_x_1_sse41:
+  .byte  68,15,40,5,95,28,0,0                // movaps        0x1c5f(%rip),%xmm8        # 5f30 <_sk_callback_sse41+0xf53>
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,13,99,28,0,0               // movaps        0x1c63(%rip),%xmm9        # 5f40 <_sk_callback_sse41+0xf63>
+  .byte  68,15,89,200                        // mulps         %xmm0,%xmm9
+  .byte  102,69,15,58,8,201,1                // roundps       $0x1,%xmm9,%xmm9
+  .byte  69,15,88,201                        // addps         %xmm9,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,92,192                        // subps         %xmm0,%xmm8
+  .byte  65,15,84,192                        // andps         %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminance_to_alpha_sse41
+.globl _sk_luminance_to_alpha_sse41
+FUNCTION(_sk_luminance_to_alpha_sse41)
+_sk_luminance_to_alpha_sse41:
+  .byte  15,40,218                           // movaps        %xmm2,%xmm3
+  .byte  15,89,5,66,28,0,0                   // mulps         0x1c42(%rip),%xmm0        # 5f50 <_sk_callback_sse41+0xf73>
+  .byte  15,89,13,75,28,0,0                  // mulps         0x1c4b(%rip),%xmm1        # 5f60 <_sk_callback_sse41+0xf83>
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  15,89,29,81,28,0,0                  // mulps         0x1c51(%rip),%xmm3        # 5f70 <_sk_callback_sse41+0xf93>
+  .byte  15,88,217                           // addps         %xmm1,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_2x3_sse41
+.globl _sk_matrix_2x3_sse41
+FUNCTION(_sk_matrix_2x3_sse41)
+_sk_matrix_2x3_sse41:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,16                  // movss         0x10(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,68,15,16,80,12                  // movss         0xc(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,20                  // movss         0x14(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_3x4_sse41
+.globl _sk_matrix_3x4_sse41
+FUNCTION(_sk_matrix_3x4_sse41)
+_sk_matrix_3x4_sse41:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,68,15,16,80,12                  // movss         0xc(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,24                  // movss         0x18(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,36                  // movss         0x24(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,28                  // movss         0x1c(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,40                  // movss         0x28(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,20                  // movss         0x14(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,32                  // movss         0x20(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,44                 // movss         0x2c(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  68,15,89,226                        // mulps         %xmm2,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,217                        // mulps         %xmm9,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x5_sse41
+.globl _sk_matrix_4x5_sse41
+FUNCTION(_sk_matrix_4x5_sse41)
+_sk_matrix_4x5_sse41:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,32                  // movss         0x20(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,48                  // movss         0x30(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,64                 // movss         0x40(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,68,15,16,80,20                  // movss         0x14(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,36                  // movss         0x24(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,52                  // movss         0x34(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,68                 // movss         0x44(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,24                  // movss         0x18(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,40                  // movss         0x28(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,56                 // movss         0x38(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  243,68,15,16,112,72                 // movss         0x48(%rax),%xmm14
+  .byte  69,15,198,246,0                     // shufps        $0x0,%xmm14,%xmm14
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,238                        // addps         %xmm14,%xmm13
+  .byte  68,15,89,226                        // mulps         %xmm2,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,217                        // mulps         %xmm9,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  243,68,15,16,88,12                  // movss         0xc(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,28                  // movss         0x1c(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,44                 // movss         0x2c(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  243,68,15,16,112,60                 // movss         0x3c(%rax),%xmm14
+  .byte  69,15,198,246,0                     // shufps        $0x0,%xmm14,%xmm14
+  .byte  243,68,15,16,120,76                 // movss         0x4c(%rax),%xmm15
+  .byte  69,15,198,255,0                     // shufps        $0x0,%xmm15,%xmm15
+  .byte  68,15,89,243                        // mulps         %xmm3,%xmm14
+  .byte  69,15,88,247                        // addps         %xmm15,%xmm14
+  .byte  68,15,89,234                        // mulps         %xmm2,%xmm13
+  .byte  69,15,88,238                        // addps         %xmm14,%xmm13
+  .byte  69,15,89,225                        // mulps         %xmm9,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,40,219                        // movaps        %xmm11,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x3_sse41
+.globl _sk_matrix_4x3_sse41
+FUNCTION(_sk_matrix_4x3_sse41)
+_sk_matrix_4x3_sse41:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,15,16,80,16                     // movss         0x10(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,32                     // movss         0x20(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,194                           // addps         %xmm2,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,20                     // movss         0x14(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,36                     // movss         0x24(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,24                     // movss         0x18(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  243,68,15,16,80,40                  // movss         0x28(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  65,15,89,217                        // mulps         %xmm9,%xmm3
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  243,15,16,88,12                     // movss         0xc(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  243,68,15,16,80,28                  // movss         0x1c(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,44                  // movss         0x2c(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_perspective_sse41
+.globl _sk_matrix_perspective_sse41
+FUNCTION(_sk_matrix_perspective_sse41)
+_sk_matrix_perspective_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,68,15,16,72,4                   // movss         0x4(%rax),%xmm9
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,193                        // addps         %xmm9,%xmm0
+  .byte  243,68,15,16,72,12                  // movss         0xc(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,20                  // movss         0x14(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  243,68,15,16,80,24                  // movss         0x18(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,28                  // movss         0x1c(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,32                  // movss         0x20(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  68,15,89,217                        // mulps         %xmm1,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,83,202                        // rcpps         %xmm10,%xmm1
+  .byte  15,89,193                           // mulps         %xmm1,%xmm0
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_gradient_sse41
+.globl _sk_evenly_spaced_gradient_sse41
+FUNCTION(_sk_evenly_spaced_gradient_sse41)
+_sk_evenly_spaced_gradient_sse41:
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,24                           // mov           (%rax),%rbx
+  .byte  76,139,112,8                        // mov           0x8(%rax),%r14
+  .byte  72,255,203                          // dec           %rbx
+  .byte  120,7                               // js            475d <_sk_evenly_spaced_gradient_sse41+0x1a>
+  .byte  243,72,15,42,203                    // cvtsi2ss      %rbx,%xmm1
+  .byte  235,21                              // jmp           4772 <_sk_evenly_spaced_gradient_sse41+0x2f>
+  .byte  73,137,217                          // mov           %rbx,%r9
+  .byte  73,209,233                          // shr           %r9
+  .byte  131,227,1                           // and           $0x1,%ebx
+  .byte  76,9,203                            // or            %r9,%rbx
+  .byte  243,72,15,42,203                    // cvtsi2ss      %rbx,%xmm1
+  .byte  243,15,88,201                       // addss         %xmm1,%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  15,89,200                           // mulps         %xmm0,%xmm1
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,73,15,58,22,201,1               // pextrq        $0x1,%xmm1,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  102,73,15,126,203                   // movq          %xmm1,%r11
+  .byte  69,137,223                          // mov           %r11d,%r15d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,71,15,16,4,190                  // movss         (%r14,%r15,4),%xmm8
+  .byte  102,71,15,58,33,4,158,16            // insertps      $0x10,(%r14,%r11,4),%xmm8
+  .byte  243,67,15,16,12,150                 // movss         (%r14,%r10,4),%xmm1
+  .byte  102,68,15,58,33,193,32              // insertps      $0x20,%xmm1,%xmm8
+  .byte  243,67,15,16,12,142                 // movss         (%r14,%r9,4),%xmm1
+  .byte  102,68,15,58,33,193,48              // insertps      $0x30,%xmm1,%xmm8
+  .byte  72,139,88,40                        // mov           0x28(%rax),%rbx
+  .byte  243,70,15,16,12,187                 // movss         (%rbx,%r15,4),%xmm9
+  .byte  102,70,15,58,33,12,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm9
+  .byte  243,66,15,16,12,147                 // movss         (%rbx,%r10,4),%xmm1
+  .byte  102,68,15,58,33,201,32              // insertps      $0x20,%xmm1,%xmm9
+  .byte  243,66,15,16,12,139                 // movss         (%rbx,%r9,4),%xmm1
+  .byte  102,68,15,58,33,201,48              // insertps      $0x30,%xmm1,%xmm9
+  .byte  72,139,88,16                        // mov           0x10(%rax),%rbx
+  .byte  243,66,15,16,12,187                 // movss         (%rbx,%r15,4),%xmm1
+  .byte  102,66,15,58,33,12,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm1
+  .byte  243,66,15,16,20,147                 // movss         (%rbx,%r10,4),%xmm2
+  .byte  102,15,58,33,202,32                 // insertps      $0x20,%xmm2,%xmm1
+  .byte  243,66,15,16,20,139                 // movss         (%rbx,%r9,4),%xmm2
+  .byte  102,15,58,33,202,48                 // insertps      $0x30,%xmm2,%xmm1
+  .byte  72,139,88,48                        // mov           0x30(%rax),%rbx
+  .byte  243,70,15,16,20,187                 // movss         (%rbx,%r15,4),%xmm10
+  .byte  102,70,15,58,33,20,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm10
+  .byte  243,66,15,16,20,147                 // movss         (%rbx,%r10,4),%xmm2
+  .byte  102,68,15,58,33,210,32              // insertps      $0x20,%xmm2,%xmm10
+  .byte  243,66,15,16,20,139                 // movss         (%rbx,%r9,4),%xmm2
+  .byte  102,68,15,58,33,210,48              // insertps      $0x30,%xmm2,%xmm10
+  .byte  72,139,88,24                        // mov           0x18(%rax),%rbx
+  .byte  243,66,15,16,20,187                 // movss         (%rbx,%r15,4),%xmm2
+  .byte  102,66,15,58,33,20,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm2
+  .byte  243,66,15,16,28,147                 // movss         (%rbx,%r10,4),%xmm3
+  .byte  102,15,58,33,211,32                 // insertps      $0x20,%xmm3,%xmm2
+  .byte  243,66,15,16,28,139                 // movss         (%rbx,%r9,4),%xmm3
+  .byte  102,15,58,33,211,48                 // insertps      $0x30,%xmm3,%xmm2
+  .byte  72,139,88,56                        // mov           0x38(%rax),%rbx
+  .byte  243,70,15,16,28,187                 // movss         (%rbx,%r15,4),%xmm11
+  .byte  102,70,15,58,33,28,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm11
+  .byte  243,66,15,16,28,147                 // movss         (%rbx,%r10,4),%xmm3
+  .byte  102,68,15,58,33,219,32              // insertps      $0x20,%xmm3,%xmm11
+  .byte  243,66,15,16,28,139                 // movss         (%rbx,%r9,4),%xmm3
+  .byte  102,68,15,58,33,219,48              // insertps      $0x30,%xmm3,%xmm11
+  .byte  72,139,88,32                        // mov           0x20(%rax),%rbx
+  .byte  243,66,15,16,28,187                 // movss         (%rbx,%r15,4),%xmm3
+  .byte  102,66,15,58,33,28,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm3
+  .byte  243,70,15,16,36,147                 // movss         (%rbx,%r10,4),%xmm12
+  .byte  102,65,15,58,33,220,32              // insertps      $0x20,%xmm12,%xmm3
+  .byte  243,70,15,16,36,139                 // movss         (%rbx,%r9,4),%xmm12
+  .byte  102,65,15,58,33,220,48              // insertps      $0x30,%xmm12,%xmm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  243,70,15,16,36,184                 // movss         (%rax,%r15,4),%xmm12
+  .byte  102,70,15,58,33,36,152,16           // insertps      $0x10,(%rax,%r11,4),%xmm12
+  .byte  243,70,15,16,44,144                 // movss         (%rax,%r10,4),%xmm13
+  .byte  102,69,15,58,33,229,32              // insertps      $0x20,%xmm13,%xmm12
+  .byte  243,70,15,16,44,136                 // movss         (%rax,%r9,4),%xmm13
+  .byte  102,69,15,58,33,229,48              // insertps      $0x30,%xmm13,%xmm12
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  15,89,200                           // mulps         %xmm0,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  15,89,208                           // mulps         %xmm0,%xmm2
+  .byte  65,15,88,211                        // addps         %xmm11,%xmm2
+  .byte  15,89,216                           // mulps         %xmm0,%xmm3
+  .byte  65,15,88,220                        // addps         %xmm12,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gauss_a_to_rgba_sse41
+.globl _sk_gauss_a_to_rgba_sse41
+FUNCTION(_sk_gauss_a_to_rgba_sse41)
+_sk_gauss_a_to_rgba_sse41:
+  .byte  15,40,5,96,22,0,0                   // movaps        0x1660(%rip),%xmm0        # 5f80 <_sk_callback_sse41+0xfa3>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,102,22,0,0                  // addps         0x1666(%rip),%xmm0        # 5f90 <_sk_callback_sse41+0xfb3>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,108,22,0,0                  // addps         0x166c(%rip),%xmm0        # 5fa0 <_sk_callback_sse41+0xfc3>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,114,22,0,0                  // addps         0x1672(%rip),%xmm0        # 5fb0 <_sk_callback_sse41+0xfd3>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,120,22,0,0                  // addps         0x1678(%rip),%xmm0        # 5fc0 <_sk_callback_sse41+0xfe3>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gradient_sse41
+.globl _sk_gradient_sse41
+FUNCTION(_sk_gradient_sse41)
+_sk_gradient_sse41:
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,249,2                        // cmp           $0x2,%r9
+  .byte  114,50                              // jb            499b <_sk_gradient_sse41+0x46>
+  .byte  72,139,88,72                        // mov           0x48(%rax),%rbx
+  .byte  73,255,201                          // dec           %r9
+  .byte  72,131,195,4                        // add           $0x4,%rbx
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  15,40,21,81,22,0,0                  // movaps        0x1651(%rip),%xmm2        # 5fd0 <_sk_callback_sse41+0xff3>
+  .byte  243,15,16,27                        // movss         (%rbx),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  15,194,216,2                        // cmpleps       %xmm0,%xmm3
+  .byte  15,84,218                           // andps         %xmm2,%xmm3
+  .byte  102,15,254,203                      // paddd         %xmm3,%xmm1
+  .byte  72,131,195,4                        // add           $0x4,%rbx
+  .byte  73,255,201                          // dec           %r9
+  .byte  117,228                             // jne           497f <_sk_gradient_sse41+0x2a>
+  .byte  102,73,15,58,22,201,1               // pextrq        $0x1,%xmm1,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  102,73,15,126,203                   // movq          %xmm1,%r11
+  .byte  69,137,222                          // mov           %r11d,%r14d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,70,15,16,4,179                  // movss         (%rbx,%r14,4),%xmm8
+  .byte  102,70,15,58,33,4,155,16            // insertps      $0x10,(%rbx,%r11,4),%xmm8
+  .byte  243,66,15,16,12,147                 // movss         (%rbx,%r10,4),%xmm1
+  .byte  102,68,15,58,33,193,32              // insertps      $0x20,%xmm1,%xmm8
+  .byte  243,66,15,16,12,139                 // movss         (%rbx,%r9,4),%xmm1
+  .byte  102,68,15,58,33,193,48              // insertps      $0x30,%xmm1,%xmm8
+  .byte  72,139,88,40                        // mov           0x28(%rax),%rbx
+  .byte  243,70,15,16,12,179                 // movss         (%rbx,%r14,4),%xmm9
+  .byte  102,70,15,58,33,12,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm9
+  .byte  243,66,15,16,12,147                 // movss         (%rbx,%r10,4),%xmm1
+  .byte  102,68,15,58,33,201,32              // insertps      $0x20,%xmm1,%xmm9
+  .byte  243,66,15,16,12,139                 // movss         (%rbx,%r9,4),%xmm1
+  .byte  102,68,15,58,33,201,48              // insertps      $0x30,%xmm1,%xmm9
+  .byte  243,67,15,16,12,183                 // movss         (%r15,%r14,4),%xmm1
+  .byte  102,67,15,58,33,12,159,16           // insertps      $0x10,(%r15,%r11,4),%xmm1
+  .byte  243,67,15,16,20,151                 // movss         (%r15,%r10,4),%xmm2
+  .byte  102,15,58,33,202,32                 // insertps      $0x20,%xmm2,%xmm1
+  .byte  243,67,15,16,20,143                 // movss         (%r15,%r9,4),%xmm2
+  .byte  102,15,58,33,202,48                 // insertps      $0x30,%xmm2,%xmm1
+  .byte  72,139,88,48                        // mov           0x30(%rax),%rbx
+  .byte  243,70,15,16,20,179                 // movss         (%rbx,%r14,4),%xmm10
+  .byte  102,70,15,58,33,20,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm10
+  .byte  243,66,15,16,20,147                 // movss         (%rbx,%r10,4),%xmm2
+  .byte  102,68,15,58,33,210,32              // insertps      $0x20,%xmm2,%xmm10
+  .byte  243,66,15,16,20,139                 // movss         (%rbx,%r9,4),%xmm2
+  .byte  102,68,15,58,33,210,48              // insertps      $0x30,%xmm2,%xmm10
+  .byte  72,139,88,24                        // mov           0x18(%rax),%rbx
+  .byte  243,66,15,16,20,179                 // movss         (%rbx,%r14,4),%xmm2
+  .byte  102,66,15,58,33,20,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm2
+  .byte  243,66,15,16,28,147                 // movss         (%rbx,%r10,4),%xmm3
+  .byte  102,15,58,33,211,32                 // insertps      $0x20,%xmm3,%xmm2
+  .byte  243,66,15,16,28,139                 // movss         (%rbx,%r9,4),%xmm3
+  .byte  102,15,58,33,211,48                 // insertps      $0x30,%xmm3,%xmm2
+  .byte  72,139,88,56                        // mov           0x38(%rax),%rbx
+  .byte  243,70,15,16,28,179                 // movss         (%rbx,%r14,4),%xmm11
+  .byte  102,70,15,58,33,28,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm11
+  .byte  243,66,15,16,28,147                 // movss         (%rbx,%r10,4),%xmm3
+  .byte  102,68,15,58,33,219,32              // insertps      $0x20,%xmm3,%xmm11
+  .byte  243,66,15,16,28,139                 // movss         (%rbx,%r9,4),%xmm3
+  .byte  102,68,15,58,33,219,48              // insertps      $0x30,%xmm3,%xmm11
+  .byte  72,139,88,32                        // mov           0x20(%rax),%rbx
+  .byte  243,66,15,16,28,179                 // movss         (%rbx,%r14,4),%xmm3
+  .byte  102,66,15,58,33,28,155,16           // insertps      $0x10,(%rbx,%r11,4),%xmm3
+  .byte  243,70,15,16,36,147                 // movss         (%rbx,%r10,4),%xmm12
+  .byte  102,65,15,58,33,220,32              // insertps      $0x20,%xmm12,%xmm3
+  .byte  243,70,15,16,36,139                 // movss         (%rbx,%r9,4),%xmm12
+  .byte  102,65,15,58,33,220,48              // insertps      $0x30,%xmm12,%xmm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  243,70,15,16,36,176                 // movss         (%rax,%r14,4),%xmm12
+  .byte  102,70,15,58,33,36,152,16           // insertps      $0x10,(%rax,%r11,4),%xmm12
+  .byte  243,70,15,16,44,144                 // movss         (%rax,%r10,4),%xmm13
+  .byte  102,69,15,58,33,229,32              // insertps      $0x20,%xmm13,%xmm12
+  .byte  243,70,15,16,44,136                 // movss         (%rax,%r9,4),%xmm13
+  .byte  102,69,15,58,33,229,48              // insertps      $0x30,%xmm13,%xmm12
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  15,89,200                           // mulps         %xmm0,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  15,89,208                           // mulps         %xmm0,%xmm2
+  .byte  65,15,88,211                        // addps         %xmm11,%xmm2
+  .byte  15,89,216                           // mulps         %xmm0,%xmm3
+  .byte  65,15,88,220                        // addps         %xmm12,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_2_stop_gradient_sse41
+.globl _sk_evenly_spaced_2_stop_gradient_sse41
+FUNCTION(_sk_evenly_spaced_2_stop_gradient_sse41)
+_sk_evenly_spaced_2_stop_gradient_sse41:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,15,16,80,16                     // movss         0x10(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,194                           // addps         %xmm2,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,20                     // movss         0x14(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,24                     // movss         0x18(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  243,15,16,88,12                     // movss         0xc(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  243,68,15,16,72,28                  // movss         0x1c(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_unit_angle_sse41
+.globl _sk_xy_to_unit_angle_sse41
+FUNCTION(_sk_xy_to_unit_angle_sse41)
+_sk_xy_to_unit_angle_sse41:
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,84,193                        // andps         %xmm9,%xmm0
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  68,15,92,217                        // subps         %xmm1,%xmm11
+  .byte  68,15,84,217                        // andps         %xmm1,%xmm11
+  .byte  68,15,40,224                        // movaps        %xmm0,%xmm12
+  .byte  69,15,93,227                        // minps         %xmm11,%xmm12
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  69,15,95,211                        // maxps         %xmm11,%xmm10
+  .byte  69,15,94,226                        // divps         %xmm10,%xmm12
+  .byte  69,15,40,236                        // movaps        %xmm12,%xmm13
+  .byte  69,15,89,237                        // mulps         %xmm13,%xmm13
+  .byte  68,15,40,21,242,19,0,0              // movaps        0x13f2(%rip),%xmm10        # 5fe0 <_sk_callback_sse41+0x1003>
+  .byte  69,15,89,213                        // mulps         %xmm13,%xmm10
+  .byte  68,15,88,21,246,19,0,0              // addps         0x13f6(%rip),%xmm10        # 5ff0 <_sk_callback_sse41+0x1013>
+  .byte  69,15,89,213                        // mulps         %xmm13,%xmm10
+  .byte  68,15,88,21,250,19,0,0              // addps         0x13fa(%rip),%xmm10        # 6000 <_sk_callback_sse41+0x1023>
+  .byte  69,15,89,213                        // mulps         %xmm13,%xmm10
+  .byte  68,15,88,21,254,19,0,0              // addps         0x13fe(%rip),%xmm10        # 6010 <_sk_callback_sse41+0x1033>
+  .byte  69,15,89,212                        // mulps         %xmm12,%xmm10
+  .byte  65,15,194,195,1                     // cmpltps       %xmm11,%xmm0
+  .byte  68,15,40,29,253,19,0,0              // movaps        0x13fd(%rip),%xmm11        # 6020 <_sk_callback_sse41+0x1043>
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  102,69,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm10
+  .byte  69,15,194,200,1                     // cmpltps       %xmm8,%xmm9
+  .byte  68,15,40,29,246,19,0,0              // movaps        0x13f6(%rip),%xmm11        # 6030 <_sk_callback_sse41+0x1053>
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  102,69,15,56,20,211                 // blendvps      %xmm0,%xmm11,%xmm10
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,194,192,1                     // cmpltps       %xmm8,%xmm0
+  .byte  68,15,40,13,232,19,0,0              // movaps        0x13e8(%rip),%xmm9        # 6040 <_sk_callback_sse41+0x1063>
+  .byte  69,15,92,202                        // subps         %xmm10,%xmm9
+  .byte  102,69,15,56,20,209                 // blendvps      %xmm0,%xmm9,%xmm10
+  .byte  69,15,194,194,7                     // cmpordps      %xmm10,%xmm8
+  .byte  69,15,84,194                        // andps         %xmm10,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_radius_sse41
+.globl _sk_xy_to_radius_sse41
+FUNCTION(_sk_xy_to_radius_sse41)
+_sk_xy_to_radius_sse41:
+  .byte  15,89,192                           // mulps         %xmm0,%xmm0
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  69,15,89,192                        // mulps         %xmm8,%xmm8
+  .byte  68,15,88,192                        // addps         %xmm0,%xmm8
+  .byte  65,15,81,192                        // sqrtps        %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_save_xy_sse41
+.globl _sk_save_xy_sse41
+FUNCTION(_sk_save_xy_sse41)
+_sk_save_xy_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,188,19,0,0               // movaps        0x13bc(%rip),%xmm8        # 6050 <_sk_callback_sse41+0x1073>
+  .byte  15,17,0                             // movups        %xmm0,(%rax)
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,88,200                        // addps         %xmm8,%xmm9
+  .byte  102,69,15,58,8,209,1                // roundps       $0x1,%xmm9,%xmm10
+  .byte  69,15,92,202                        // subps         %xmm10,%xmm9
+  .byte  68,15,88,193                        // addps         %xmm1,%xmm8
+  .byte  102,69,15,58,8,208,1                // roundps       $0x1,%xmm8,%xmm10
+  .byte  69,15,92,194                        // subps         %xmm10,%xmm8
+  .byte  15,17,72,32                         // movups        %xmm1,0x20(%rax)
+  .byte  68,15,17,72,64                      // movups        %xmm9,0x40(%rax)
+  .byte  68,15,17,64,96                      // movups        %xmm8,0x60(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_accumulate_sse41
+.globl _sk_accumulate_sse41
+FUNCTION(_sk_accumulate_sse41)
+_sk_accumulate_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,16,128,128,0,0,0              // movups        0x80(%rax),%xmm8
+  .byte  68,15,16,136,160,0,0,0              // movups        0xa0(%rax),%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  65,15,88,224                        // addps         %xmm8,%xmm4
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,193                        // mulps         %xmm1,%xmm8
+  .byte  65,15,88,232                        // addps         %xmm8,%xmm5
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  65,15,88,240                        // addps         %xmm8,%xmm6
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  65,15,88,249                        // addps         %xmm9,%xmm7
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_nx_sse41
+.globl _sk_bilinear_nx_sse41
+FUNCTION(_sk_bilinear_nx_sse41)
+_sk_bilinear_nx_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,62,19,0,0                   // addps         0x133e(%rip),%xmm0        # 6060 <_sk_callback_sse41+0x1083>
+  .byte  68,15,40,13,70,19,0,0               // movaps        0x1346(%rip),%xmm9        # 6070 <_sk_callback_sse41+0x1093>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,17,136,128,0,0,0              // movups        %xmm9,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_px_sse41
+.globl _sk_bilinear_px_sse41
+FUNCTION(_sk_bilinear_px_sse41)
+_sk_bilinear_px_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,53,19,0,0                   // addps         0x1335(%rip),%xmm0        # 6080 <_sk_callback_sse41+0x10a3>
+  .byte  68,15,17,128,128,0,0,0              // movups        %xmm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_ny_sse41
+.globl _sk_bilinear_ny_sse41
+FUNCTION(_sk_bilinear_ny_sse41)
+_sk_bilinear_ny_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,39,19,0,0                  // addps         0x1327(%rip),%xmm1        # 6090 <_sk_callback_sse41+0x10b3>
+  .byte  68,15,40,13,47,19,0,0               // movaps        0x132f(%rip),%xmm9        # 60a0 <_sk_callback_sse41+0x10c3>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,17,136,160,0,0,0              // movups        %xmm9,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_py_sse41
+.globl _sk_bilinear_py_sse41
+FUNCTION(_sk_bilinear_py_sse41)
+_sk_bilinear_py_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,29,19,0,0                  // addps         0x131d(%rip),%xmm1        # 60b0 <_sk_callback_sse41+0x10d3>
+  .byte  68,15,17,128,160,0,0,0              // movups        %xmm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3x_sse41
+.globl _sk_bicubic_n3x_sse41
+FUNCTION(_sk_bicubic_n3x_sse41)
+_sk_bicubic_n3x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,16,19,0,0                   // addps         0x1310(%rip),%xmm0        # 60c0 <_sk_callback_sse41+0x10e3>
+  .byte  68,15,40,13,24,19,0,0               // movaps        0x1318(%rip),%xmm9        # 60d0 <_sk_callback_sse41+0x10f3>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  69,15,89,192                        // mulps         %xmm8,%xmm8
+  .byte  68,15,89,13,20,19,0,0               // mulps         0x1314(%rip),%xmm9        # 60e0 <_sk_callback_sse41+0x1103>
+  .byte  68,15,88,13,28,19,0,0               // addps         0x131c(%rip),%xmm9        # 60f0 <_sk_callback_sse41+0x1113>
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  68,15,17,136,128,0,0,0              // movups        %xmm9,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1x_sse41
+.globl _sk_bicubic_n1x_sse41
+FUNCTION(_sk_bicubic_n1x_sse41)
+_sk_bicubic_n1x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,11,19,0,0                   // addps         0x130b(%rip),%xmm0        # 6100 <_sk_callback_sse41+0x1123>
+  .byte  68,15,40,13,19,19,0,0               // movaps        0x1313(%rip),%xmm9        # 6110 <_sk_callback_sse41+0x1133>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,40,5,23,19,0,0                // movaps        0x1317(%rip),%xmm8        # 6120 <_sk_callback_sse41+0x1143>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,27,19,0,0                // addps         0x131b(%rip),%xmm8        # 6130 <_sk_callback_sse41+0x1153>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,31,19,0,0                // addps         0x131f(%rip),%xmm8        # 6140 <_sk_callback_sse41+0x1163>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,35,19,0,0                // addps         0x1323(%rip),%xmm8        # 6150 <_sk_callback_sse41+0x1173>
+  .byte  68,15,17,128,128,0,0,0              // movups        %xmm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1x_sse41
+.globl _sk_bicubic_p1x_sse41
+FUNCTION(_sk_bicubic_p1x_sse41)
+_sk_bicubic_p1x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,29,19,0,0                // movaps        0x131d(%rip),%xmm8        # 6160 <_sk_callback_sse41+0x1183>
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,72,64                      // movups        0x40(%rax),%xmm9
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,21,25,19,0,0               // movaps        0x1319(%rip),%xmm10        # 6170 <_sk_callback_sse41+0x1193>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,29,19,0,0               // addps         0x131d(%rip),%xmm10        # 6180 <_sk_callback_sse41+0x11a3>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,208                        // addps         %xmm8,%xmm10
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,25,19,0,0               // addps         0x1319(%rip),%xmm10        # 6190 <_sk_callback_sse41+0x11b3>
+  .byte  68,15,17,144,128,0,0,0              // movups        %xmm10,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3x_sse41
+.globl _sk_bicubic_p3x_sse41
+FUNCTION(_sk_bicubic_p3x_sse41)
+_sk_bicubic_p3x_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,12,19,0,0                   // addps         0x130c(%rip),%xmm0        # 61a0 <_sk_callback_sse41+0x11c3>
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,89,201                        // mulps         %xmm9,%xmm9
+  .byte  68,15,89,5,12,19,0,0                // mulps         0x130c(%rip),%xmm8        # 61b0 <_sk_callback_sse41+0x11d3>
+  .byte  68,15,88,5,20,19,0,0                // addps         0x1314(%rip),%xmm8        # 61c0 <_sk_callback_sse41+0x11e3>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,17,128,128,0,0,0              // movups        %xmm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3y_sse41
+.globl _sk_bicubic_n3y_sse41
+FUNCTION(_sk_bicubic_n3y_sse41)
+_sk_bicubic_n3y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,2,19,0,0                   // addps         0x1302(%rip),%xmm1        # 61d0 <_sk_callback_sse41+0x11f3>
+  .byte  68,15,40,13,10,19,0,0               // movaps        0x130a(%rip),%xmm9        # 61e0 <_sk_callback_sse41+0x1203>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  69,15,89,192                        // mulps         %xmm8,%xmm8
+  .byte  68,15,89,13,6,19,0,0                // mulps         0x1306(%rip),%xmm9        # 61f0 <_sk_callback_sse41+0x1213>
+  .byte  68,15,88,13,14,19,0,0               // addps         0x130e(%rip),%xmm9        # 6200 <_sk_callback_sse41+0x1223>
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  68,15,17,136,160,0,0,0              // movups        %xmm9,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1y_sse41
+.globl _sk_bicubic_n1y_sse41
+FUNCTION(_sk_bicubic_n1y_sse41)
+_sk_bicubic_n1y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,252,18,0,0                 // addps         0x12fc(%rip),%xmm1        # 6210 <_sk_callback_sse41+0x1233>
+  .byte  68,15,40,13,4,19,0,0                // movaps        0x1304(%rip),%xmm9        # 6220 <_sk_callback_sse41+0x1243>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,40,5,8,19,0,0                 // movaps        0x1308(%rip),%xmm8        # 6230 <_sk_callback_sse41+0x1253>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,12,19,0,0                // addps         0x130c(%rip),%xmm8        # 6240 <_sk_callback_sse41+0x1263>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,16,19,0,0                // addps         0x1310(%rip),%xmm8        # 6250 <_sk_callback_sse41+0x1273>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,20,19,0,0                // addps         0x1314(%rip),%xmm8        # 6260 <_sk_callback_sse41+0x1283>
+  .byte  68,15,17,128,160,0,0,0              // movups        %xmm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1y_sse41
+.globl _sk_bicubic_p1y_sse41
+FUNCTION(_sk_bicubic_p1y_sse41)
+_sk_bicubic_p1y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,14,19,0,0                // movaps        0x130e(%rip),%xmm8        # 6270 <_sk_callback_sse41+0x1293>
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,72,96                      // movups        0x60(%rax),%xmm9
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  68,15,40,21,9,19,0,0                // movaps        0x1309(%rip),%xmm10        # 6280 <_sk_callback_sse41+0x12a3>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,13,19,0,0               // addps         0x130d(%rip),%xmm10        # 6290 <_sk_callback_sse41+0x12b3>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,208                        // addps         %xmm8,%xmm10
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,9,19,0,0                // addps         0x1309(%rip),%xmm10        # 62a0 <_sk_callback_sse41+0x12c3>
+  .byte  68,15,17,144,160,0,0,0              // movups        %xmm10,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3y_sse41
+.globl _sk_bicubic_p3y_sse41
+FUNCTION(_sk_bicubic_p3y_sse41)
+_sk_bicubic_p3y_sse41:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,251,18,0,0                 // addps         0x12fb(%rip),%xmm1        # 62b0 <_sk_callback_sse41+0x12d3>
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,89,201                        // mulps         %xmm9,%xmm9
+  .byte  68,15,89,5,251,18,0,0               // mulps         0x12fb(%rip),%xmm8        # 62c0 <_sk_callback_sse41+0x12e3>
+  .byte  68,15,88,5,3,19,0,0                 // addps         0x1303(%rip),%xmm8        # 62d0 <_sk_callback_sse41+0x12f3>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,17,128,160,0,0,0              // movups        %xmm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_callback_sse41
+.globl _sk_callback_sse41
+FUNCTION(_sk_callback_sse41)
+_sk_callback_sse41:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  72,131,236,72                       // sub           $0x48,%rsp
+  .byte  15,41,125,144                       // movaps        %xmm7,-0x70(%rbp)
+  .byte  15,41,117,160                       // movaps        %xmm6,-0x60(%rbp)
+  .byte  15,41,109,176                       // movaps        %xmm5,-0x50(%rbp)
+  .byte  15,41,101,192                       // movaps        %xmm4,-0x40(%rbp)
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  72,137,77,208                       // mov           %rcx,-0x30(%rbp)
+  .byte  73,137,215                          // mov           %rdx,%r15
+  .byte  73,137,252                          // mov           %rdi,%r12
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,198                          // mov           %rax,%r14
+  .byte  73,137,245                          // mov           %rsi,%r13
+  .byte  15,40,224                           // movaps        %xmm0,%xmm4
+  .byte  15,20,225                           // unpcklps      %xmm1,%xmm4
+  .byte  15,40,234                           // movaps        %xmm2,%xmm5
+  .byte  15,20,235                           // unpcklps      %xmm3,%xmm5
+  .byte  15,21,193                           // unpckhps      %xmm1,%xmm0
+  .byte  15,21,211                           // unpckhps      %xmm3,%xmm2
+  .byte  15,40,204                           // movaps        %xmm4,%xmm1
+  .byte  102,15,20,205                       // unpcklpd      %xmm5,%xmm1
+  .byte  15,18,236                           // movhlps       %xmm4,%xmm5
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  102,15,20,218                       // unpcklpd      %xmm2,%xmm3
+  .byte  102,65,15,17,78,8                   // movupd        %xmm1,0x8(%r14)
+  .byte  15,18,208                           // movhlps       %xmm0,%xmm2
+  .byte  65,15,17,110,24                     // movups        %xmm5,0x18(%r14)
+  .byte  102,65,15,17,94,40                  // movupd        %xmm3,0x28(%r14)
+  .byte  65,15,17,86,56                      // movups        %xmm2,0x38(%r14)
+  .byte  72,133,219                          // test          %rbx,%rbx
+  .byte  190,4,0,0,0                         // mov           $0x4,%esi
+  .byte  15,69,243                           // cmovne        %ebx,%esi
+  .byte  76,137,247                          // mov           %r14,%rdi
+  .byte  65,255,22                           // callq         *(%r14)
+  .byte  73,139,134,136,0,0,0                // mov           0x88(%r14),%rax
+  .byte  15,16,32                            // movups        (%rax),%xmm4
+  .byte  15,16,64,16                         // movups        0x10(%rax),%xmm0
+  .byte  15,16,88,32                         // movups        0x20(%rax),%xmm3
+  .byte  15,16,80,48                         // movups        0x30(%rax),%xmm2
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,20,232                           // unpcklps      %xmm0,%xmm5
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  15,21,224                           // unpckhps      %xmm0,%xmm4
+  .byte  15,21,218                           // unpckhps      %xmm2,%xmm3
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  15,18,205                           // movhlps       %xmm5,%xmm1
+  .byte  15,40,212                           // movaps        %xmm4,%xmm2
+  .byte  102,15,20,211                       // unpcklpd      %xmm3,%xmm2
+  .byte  15,18,220                           // movhlps       %xmm4,%xmm3
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,231                          // mov           %r12,%rdi
+  .byte  76,137,250                          // mov           %r15,%rdx
+  .byte  72,139,77,208                       // mov           -0x30(%rbp),%rcx
+  .byte  73,137,216                          // mov           %rbx,%r8
+  .byte  15,40,101,192                       // movaps        -0x40(%rbp),%xmm4
+  .byte  15,40,109,176                       // movaps        -0x50(%rbp),%xmm5
+  .byte  15,40,117,160                       // movaps        -0x60(%rbp),%xmm6
+  .byte  15,40,125,144                       // movaps        -0x70(%rbp),%xmm7
+  .byte  72,131,196,72                       // add           $0x48,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+BALIGN16
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,1                            // cmpb          $0x1,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  252                                 // cld
+  .byte  190,0,0,252,190                     // mov           $0xbefc0000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  252                                 // cld
+  .byte  190,0,0,252,190                     // mov           $0xbefc0000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  191,0,0,128,191                     // mov           $0xbf800000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,191,0,0,128,191,0               // cmpb          $0x0,-0x40800000(%rdi)
+  .byte  0,224                               // add           %ah,%al
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  224,64                              // loopne        52a8 <.literal16+0x1d8>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,64                              // loopne        52ac <.literal16+0x1dc>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,64                              // loopne        52b0 <.literal16+0x1e0>
+  .byte  154                                 // (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 52d1 <.literal16+0x201>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 52d5 <.literal16+0x205>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 52d9 <.literal16+0x209>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 52dd <.literal16+0x20d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5311 <.literal16+0x241>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5315 <.literal16+0x245>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5319 <.literal16+0x249>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 531d <.literal16+0x24d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5351 <.literal16+0x281>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5355 <.literal16+0x285>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5359 <.literal16+0x289>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 535d <.literal16+0x28d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5391 <.literal16+0x2c1>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5395 <.literal16+0x2c5>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5399 <.literal16+0x2c9>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 539d <.literal16+0x2cd>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,255                          // cmpb          $0xff,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a005388 <_sk_callback_sse41+0xa0003ab>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,2                // decl          0x2ffffff(%rip)        # 3005390 <_sk_callback_sse41+0x30003b3>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,127                    // add           %al,0x7f00003f(%rax)
+  .byte  67,0,0                              // rex.XB        add %al,(%r8)
+  .byte  127,67                              // jg            53fb <.literal16+0x32b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            53ff <.literal16+0x32f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5403 <.literal16+0x333>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  145                                 // xchg          %eax,%ecx
+  .byte  131,158,61,145,131,158,61           // sbbl          $0x3d,-0x617c6ec3(%rsi)
+  .byte  145                                 // xchg          %eax,%ecx
+  .byte  131,158,61,145,131,158,61           // sbbl          $0x3d,-0x617c6ec3(%rsi)
+  .byte  154                                 // (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,92                               // ds            pop %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,97                               // rex.RXB       (bad)
+  .byte  61,174,71,97,61                     // cmp           $0x3d6147ae,%eax
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,97                               // rex.RXB       (bad)
+  .byte  61,174,71,97,61                     // cmp           $0x3d6147ae,%eax
+  .byte  82                                  // push          %rdx
+  .byte  184,78,65,82,184                    // mov           $0xb852414e,%eax
+  .byte  78                                  // rex.WRX
+  .byte  65,82                               // push          %r10
+  .byte  184,78,65,82,184                    // mov           $0xb852414e,%eax
+  .byte  78                                  // rex.WRX
+  .byte  65,57,215                           // cmp           %edx,%r15d
+  .byte  32,187,57,215,32,187                // and           %bh,-0x44df28c7(%rbx)
+  .byte  57,215                              // cmp           %edx,%edi
+  .byte  32,187,57,215,32,187                // and           %bh,-0x44df28c7(%rbx)
+  .byte  186,159,98,60,186                   // mov           $0xba3c629f,%edx
+  .byte  159                                 // lahf
+  .byte  98                                  // (bad)
+  .byte  60,186                              // cmp           $0xba,%al
+  .byte  159                                 // lahf
+  .byte  98                                  // (bad)
+  .byte  60,186                              // cmp           $0xba,%al
+  .byte  159                                 // lahf
+  .byte  98                                  // (bad)
+  .byte  60,109                              // cmp           $0x6d,%al
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  252                                 // cld
+  .byte  191,16,62,252,191                   // mov           $0xbffc3e10,%edi
+  .byte  16,62                               // adc           %bh,(%rsi)
+  .byte  252                                 // cld
+  .byte  191,16,62,252,191                   // mov           $0xbffc3e10,%edi
+  .byte  16,62                               // adc           %bh,(%rsi)
+  .byte  168,177                             // test          $0xb1,%al
+  .byte  152                                 // cwtl
+  .byte  59,168,177,152,59,168               // cmp           -0x57c4674f(%rax),%ebp
+  .byte  177,152                             // mov           $0x98,%cl
+  .byte  59,168,177,152,59,0                 // cmp           0x3b98b1(%rax),%ebp
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,192                    // add           %al,-0x3fffffc1(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  0,64,0                              // add           %al,0x0(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  0,64,0                              // add           %al,0x0(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,171,170                      // addb          $0xaa,-0x55(%rax)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,171,170                      // addb          $0xaa,-0x55(%rax)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,255,0,0,0                // addb          $0x0,0xff3b(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,248,0,0                // addb          $0x0,0xf8003b(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        55d9 <.literal16+0x509>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        55dd <.literal16+0x50d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        55e1 <.literal16+0x511>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        55e5 <.literal16+0x515>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,8,33,4,61                   // and           %eax,0x3d042108(,%rdi,1)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  1,255                               // add           %edi,%edi
+  .byte  255                                 // (bad)
+  .byte  255,5,255,255,255,9                 // incl          0x9ffffff(%rip)        # a005628 <_sk_callback_sse41+0xa00064b>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,2                // decl          0x2ffffff(%rip)        # 3005630 <_sk_callback_sse41+0x3000653>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,129,128,128,59,129              // incl          -0x7ec47f80(%rcx)
+  .byte  128,128,59,129,128,128,59           // addb          $0x3b,-0x7f7f7ec5(%rax)
+  .byte  129,128,128,59,255,0,255,0,255,0    // addl          $0xff00ff,0xff3b80(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,255                               // or            %bh,%bh
+  .byte  10,255                              // or            %bh,%bh
+  .byte  12,255                              // or            $0xff,%al
+  .byte  14                                  // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,128,55,128                  // add           %al,-0x7fc88000(%rax)
+  .byte  0,128,55,128,0,128                  // add           %al,-0x7fff7fc9(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,128,10,128,12,128                 // or            %al,-0x7ff37ff6(%rax)
+  .byte  14                                  // (bad)
+  .byte  128,0,0                             // addb          $0x0,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            56fb <.literal16+0x62b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            56ff <.literal16+0x62f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5703 <.literal16+0x633>
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,129,128,128,59           // addb          $0x3b,-0x7f7f7ec5(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,0                            // cmpb          $0x0,(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            56f4 <.literal16+0x624>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            56f8 <.literal16+0x628>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            56fc <.literal16+0x62c>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5700 <.literal16+0x630>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5785 <.literal16+0x6b5>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           56e9 <.literal16+0x619>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a3972a <_sk_callback_sse41+0xffffffffe9a3474d>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  81                                  // push          %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            57c4 <.literal16+0x6f4>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            57c8 <.literal16+0x6f8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            57cc <.literal16+0x6fc>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            57d0 <.literal16+0x700>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5855 <.literal16+0x785>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           57b9 <.literal16+0x6e9>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a397fa <_sk_callback_sse41+0xffffffffe9a3481d>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  81                                  // push          %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5894 <.literal16+0x7c4>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5898 <.literal16+0x7c8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            589c <.literal16+0x7cc>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            58a0 <.literal16+0x7d0>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5925 <.literal16+0x855>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           5889 <.literal16+0x7b9>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a398ca <_sk_callback_sse41+0xffffffffe9a348ed>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  81                                  // push          %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5964 <.literal16+0x894>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5968 <.literal16+0x898>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            596c <.literal16+0x89c>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5970 <.literal16+0x8a0>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            59f5 <.literal16+0x925>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           5959 <.literal16+0x889>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a3999a <_sk_callback_sse41+0xffffffffe9a349bd>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  81                                  // push          %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,200                               // add           %cl,%al
+  .byte  66,0,0                              // rex.X         add %al,(%rax)
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  127,67                              // jg            5a77 <.literal16+0x9a7>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5a7b <.literal16+0x9ab>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5a7f <.literal16+0x9af>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5a83 <.literal16+0x9b3>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,65,0,0                          // addb          $0x0,0x0(%rcx)
+  .byte  128,65,0,0                          // addb          $0x0,0x0(%rcx)
+  .byte  128,65,0,0                          // addb          $0x0,0x0(%rcx)
+  .byte  128,65,203,61                       // addb          $0x3d,-0x35(%rcx)
+  .byte  13,60,203,61,13                     // or            $0xd3dcb3c,%eax
+  .byte  60,203                              // cmp           $0xcb,%al
+  .byte  61,13,60,203,61                     // cmp           $0x3dcb3c0d,%eax
+  .byte  13,60,111,18,3                      // or            $0x3126f3c,%eax
+  .byte  59,111,18                           // cmp           0x12(%rdi),%ebp
+  .byte  3,59                                // add           (%rbx),%edi
+  .byte  111                                 // outsl         %ds:(%rsi),(%dx)
+  .byte  18,3                                // adc           (%rbx),%al
+  .byte  59,111,18                           // cmp           0x12(%rdi),%ebp
+  .byte  3,59                                // add           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  163,59,10,215,163,59,10,215,163     // movabs        %eax,0xa3d70a3ba3d70a3b
+  .byte  59,10                               // cmp           (%rdx),%ecx
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  163,59,194,24,17,60,194,24,17       // movabs        %eax,0x1118c23c1118c23b
+  .byte  60,194                              // cmp           $0xc2,%al
+  .byte  24,17                               // sbb           %dl,(%rcx)
+  .byte  60,194                              // cmp           $0xc2,%al
+  .byte  24,17                               // sbb           %dl,(%rcx)
+  .byte  60,203                              // cmp           $0xcb,%al
+  .byte  61,13,190,203,61                    // cmp           $0x3dcbbe0d,%eax
+  .byte  13,190,203,61,13                    // or            $0xd3dcbbe,%eax
+  .byte  190,203,61,13,190                   // mov           $0xbe0d3dcb,%esi
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           5b03 <.literal16+0xa33>
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           5b07 <.literal16+0xa37>
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           5b0b <.literal16+0xa3b>
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           5b0f <.literal16+0xa3f>
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,129,128,128,59           // addb          $0x3b,-0x7f7f7ec5(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,0                            // cmpb          $0x0,(%rbx)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5b5b <.literal16+0xa8b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5b5f <.literal16+0xa8f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5b63 <.literal16+0xa93>
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,0,128,63               // addb          $0x3f,-0x7fffffc5(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,0,128,63               // addb          $0x3f,-0x7fffffc5(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,1,255,255,255            // addb          $0xff,-0xfec5(%rax)
+  .byte  5,255,255,255,9                     // add           $0x9ffffff,%eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,2                // decl          0x2ffffff(%rip)        # 3005ba0 <_sk_callback_sse41+0x3000bc3>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  248                                 // clc
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        5bd9 <.literal16+0xb09>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5bdd <.literal16+0xb0d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5be1 <.literal16+0xb11>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5be5 <.literal16+0xb15>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,8,33,4,61                   // and           %eax,0x3d042108(,%rdi,1)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  248                                 // clc
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        5c49 <.literal16+0xb79>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5c4d <.literal16+0xb7d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5c51 <.literal16+0xb81>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5c55 <.literal16+0xb85>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,8,33,4,61                   // and           %eax,0x3d042108(,%rdi,1)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,248                               // add           %bh,%al
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  124,66                              // jl            5ce6 <.literal16+0xc16>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  124,66                              // jl            5cea <.literal16+0xc1a>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  124,66                              // jl            5cee <.literal16+0xc1e>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  124,66                              // jl            5cf2 <.literal16+0xc22>
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  240,0,0                             // lock          add %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,59,137                // add           %cl,-0x76c47778(%rcx)
+  .byte  136,136,59,137,136,136              // mov           %cl,-0x777776c5(%rax)
+  .byte  59,137,136,136,59,15                // cmp           0xf3b8888(%rcx),%ecx
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,61,137                // add           %cl,-0x76c27778(%rcx)
+  .byte  136,136,61,137,136,136              // mov           %cl,-0x777776c3(%rax)
+  .byte  61,137,136,136,61                   // cmp           $0x3d888889,%eax
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  240,0,0                             // lock          add %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,59,137                // add           %cl,-0x76c47778(%rcx)
+  .byte  136,136,59,137,136,136              // mov           %cl,-0x777776c5(%rax)
+  .byte  59,137,136,136,59,15                // cmp           0xf3b8888(%rcx),%ecx
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,61,137                // add           %cl,-0x76c27778(%rcx)
+  .byte  136,136,61,137,136,136              // mov           %cl,-0x777776c3(%rax)
+  .byte  61,137,136,136,61                   // cmp           $0x3d888889,%eax
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            5df5 <.literal16+0xd25>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            5df9 <.literal16+0xd29>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            5dfd <.literal16+0xd2d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            5e01 <.literal16+0xd31>
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,1,255,255,255            // addb          $0xff,-0xfec5(%rax)
+  .byte  5,255,255,255,9                     // add           $0x9ffffff,%eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,2                // decl          0x2ffffff(%rip)        # 3005df0 <_sk_callback_sse41+0x3000e13>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,1,255,255,255            // addb          $0xff,-0xfec5(%rax)
+  .byte  5,255,255,255,9                     // add           $0x9ffffff,%eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,13,255,255,255,2                // decl          0x2ffffff(%rip)        # 3005e30 <_sk_callback_sse41+0x3000e53>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,6                               // incl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,10                              // decl          (%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,14                              // decl          (%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5e8b <.literal16+0xdbb>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5e8f <.literal16+0xdbf>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5e93 <.literal16+0xdc3>
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  128,0,0                             // addb          $0x0,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,4                       // add           %al,0x4000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,0,0                             // addb          $0x0,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,128,56                    // add           %al,0x38800000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,56,0                            // cmpb          $0x0,(%rax)
+  .byte  0,128,56,0,0,128                    // add           %al,-0x7fffffc8(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,128,0,128,55,128                // incl          -0x7fc88000(%rax)
+  .byte  0,128,55,128,0,128                  // add           %al,-0x7fff7fc9(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  255                                 // (bad)
+  .byte  127,71                              // jg            5f5b <.literal16+0xe8b>
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            5f5f <.literal16+0xe8f>
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            5f63 <.literal16+0xe93>
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            5f67 <.literal16+0xe97>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,191,0,0,128                   // add           %al,-0x7fffff41(%rax)
+  .byte  191,0,0,128,191                     // mov           $0xbf800000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,191,0,0,0,63,0                  // cmpb          $0x0,0x3f000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  208                                 // (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,208                              // ds            (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,208                              // ds            (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,208                              // ds            (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,89                               // ds            pop %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  89                                  // pop           %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  89                                  // pop           %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  89                                  // pop           %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  152                                 // cwtl
+  .byte  221,147,61,152,221,147              // fstl          -0x6c2267c3(%rbx)
+  .byte  61,152,221,147,61                   // cmp           $0x3d93dd98,%eax
+  .byte  152                                 // cwtl
+  .byte  221,147,61,45,16,17                 // fstl          0x11102d3d(%rbx)
+  .byte  192,45,16,17,192,45,16              // shrb          $0x10,0x2dc01110(%rip)        # 2dc0709a <_sk_callback_sse41+0x2dc020bd>
+  .byte  17,192                              // adc           %eax,%eax
+  .byte  45,16,17,192,18                     // sub           $0x12c01110,%eax
+  .byte  120,57                              // js            5fcc <.literal16+0xefc>
+  .byte  64,18,120,57                        // adc           0x39(%rax),%dil
+  .byte  64,18,120,57                        // adc           0x39(%rax),%dil
+  .byte  64,18,120,57                        // adc           0x39(%rax),%dil
+  .byte  64,32,148,90,62,32,148,90           // and           %dl,0x5a94203e(%rdx,%rbx,2)
+  .byte  62,32,148,90,62,32,148,90           // and           %dl,%ds:0x5a94203e(%rdx,%rbx,2)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,0,24                             // add           %bl,%ds:(%rax)
+  .byte  161,57,0,24,161,57,0,24,161         // movabs        0xa1180039a1180039,%eax
+  .byte  57,0                                // cmp           %eax,(%rax)
+  .byte  24,161,57,1,0,0                     // sbb           %ah,0x139(%rcx)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,111,43                            // add           %ch,0x2b(%rdi)
+  .byte  231,187                             // out           %eax,$0xbb
+  .byte  111                                 // outsl         %ds:(%rsi),(%dx)
+  .byte  43,231                              // sub           %edi,%esp
+  .byte  187,111,43,231,187                  // mov           $0xbbe72b6f,%ebx
+  .byte  111                                 // outsl         %ds:(%rsi),(%dx)
+  .byte  43,231                              // sub           %edi,%esp
+  .byte  187,159,215,202,60                  // mov           $0x3ccad79f,%ebx
+  .byte  159                                 // lahf
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  202,60,159                          // lret          $0x9f3c
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  202,60,159                          // lret          $0x9f3c
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  202,60,212                          // lret          $0xd43c
+  .byte  100,84                              // fs            push %rsp
+  .byte  189,212,100,84,189                  // mov           $0xbd5464d4,%ebp
+  .byte  212                                 // (bad)
+  .byte  100,84                              // fs            push %rsp
+  .byte  189,212,100,84,189                  // mov           $0xbd5464d4,%ebp
+  .byte  169,240,34,62,169                   // test          $0xa93e22f0,%eax
+  .byte  240,34,62                           // lock          and (%rsi),%bh
+  .byte  169,240,34,62,169                   // test          $0xa93e22f0,%eax
+  .byte  240,34,62                           // lock          and (%rsi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,62,0                            // cmpb          $0x0,(%rsi)
+  .byte  0,128,62,0,0,128                    // add           %al,-0x7fffffc2(%rax)
+  .byte  62,0,0                              // add           %al,%ds:(%rax)
+  .byte  128,62,0                            // cmpb          $0x0,(%rsi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,192,191,0               // sarb          $0x0,-0x40400000(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  191,0,0,192,191                     // mov           $0xbfc00000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,114                          // cmpb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,114,28                           // jb,pt         6102 <.literal16+0x1032>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6106 <.literal16+0x1036>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         610a <.literal16+0x103a>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,85                           // cmpb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63ef95 <_sk_callback_sse41+0x3d639fb8>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63efd5 <_sk_callback_sse41+0x3d639ff8>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  114,28                              // jb            61ce <.literal16+0x10fe>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         61d2 <.literal16+0x1102>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         61d6 <.literal16+0x1106>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         61da <.literal16+0x110a>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,192,191,0               // sarb          $0x0,-0x40400000(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  191,0,0,192,191                     // mov           $0xbfc00000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,114                          // cmpb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,114,28                           // jb,pt         6212 <.literal16+0x1142>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6216 <.literal16+0x1146>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         621a <.literal16+0x114a>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,85                           // cmpb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63f0a5 <_sk_callback_sse41+0x3d63a0c8>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63f0e5 <_sk_callback_sse41+0x3d63a108>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  114,28                              // jb            62de <.literal16+0x120e>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         62e2 <_sk_callback_sse41+0x1305>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         62e6 <_sk_callback_sse41+0x1309>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         62ea <_sk_callback_sse41+0x130d>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+BALIGN32
+
+HIDDEN _sk_start_pipeline_sse2
+.globl _sk_start_pipeline_sse2
+FUNCTION(_sk_start_pipeline_sse2)
+_sk_start_pipeline_sse2:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  80                                  // push          %rax
+  .byte  77,137,199                          // mov           %r8,%r15
+  .byte  73,137,208                          // mov           %rdx,%r8
+  .byte  73,137,244                          // mov           %rsi,%r12
+  .byte  72,137,251                          // mov           %rdi,%rbx
+  .byte  72,137,206                          // mov           %rcx,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,245                          // mov           %rsi,%r13
+  .byte  72,141,75,4                         // lea           0x4(%rbx),%rcx
+  .byte  76,57,193                           // cmp           %r8,%rcx
+  .byte  118,5                               // jbe           30 <_sk_start_pipeline_sse2+0x30>
+  .byte  72,137,218                          // mov           %rbx,%rdx
+  .byte  235,75                              // jmp           7b <_sk_start_pipeline_sse2+0x7b>
+  .byte  76,137,69,208                       // mov           %r8,-0x30(%rbp)
+  .byte  65,184,0,0,0,0                      // mov           $0x0,%r8d
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  76,137,255                          // mov           %r15,%rdi
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  72,137,218                          // mov           %rbx,%rdx
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  73,137,198                          // mov           %rax,%r14
+  .byte  65,255,214                          // callq         *%r14
+  .byte  76,139,69,208                       // mov           -0x30(%rbp),%r8
+  .byte  76,137,240                          // mov           %r14,%rax
+  .byte  72,141,83,4                         // lea           0x4(%rbx),%rdx
+  .byte  72,131,195,8                        // add           $0x8,%rbx
+  .byte  76,57,195                           // cmp           %r8,%rbx
+  .byte  72,137,211                          // mov           %rdx,%rbx
+  .byte  118,185                             // jbe           34 <_sk_start_pipeline_sse2+0x34>
+  .byte  73,41,208                           // sub           %rdx,%r8
+  .byte  116,49                              // je            b1 <_sk_start_pipeline_sse2+0xb1>
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  76,137,255                          // mov           %r15,%rdi
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  195                                 // retq
+
+HIDDEN _sk_just_return_sse2
+.globl _sk_just_return_sse2
+FUNCTION(_sk_just_return_sse2)
+_sk_just_return_sse2:
+  .byte  195                                 // retq
+
+HIDDEN _sk_seed_shader_sse2
+.globl _sk_seed_shader_sse2
+FUNCTION(_sk_seed_shader_sse2)
+_sk_seed_shader_sse2:
+  .byte  102,15,110,194                      // movd          %edx,%xmm0
+  .byte  102,15,112,192,0                    // pshufd        $0x0,%xmm0,%xmm0
+  .byte  15,91,200                           // cvtdq2ps      %xmm0,%xmm1
+  .byte  15,40,21,92,85,0,0                  // movaps        0x555c(%rip),%xmm2        # 5630 <_sk_callback_sse2+0xfc>
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  15,16,7                             // movups        (%rdi),%xmm0
+  .byte  15,88,193                           // addps         %xmm1,%xmm0
+  .byte  102,15,110,201                      // movd          %ecx,%xmm1
+  .byte  102,15,112,201,0                    // pshufd        $0x0,%xmm1,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,21,75,85,0,0                  // movaps        0x554b(%rip),%xmm2        # 5640 <_sk_callback_sse2+0x10c>
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dither_sse2
+.globl _sk_dither_sse2
+FUNCTION(_sk_dither_sse2)
+_sk_dither_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  102,68,15,110,194                   // movd          %edx,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  243,68,15,111,79,32                 // movdqu        0x20(%rdi),%xmm9
+  .byte  102,69,15,254,200                   // paddd         %xmm8,%xmm9
+  .byte  102,68,15,110,193                   // movd          %ecx,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  102,69,15,239,193                   // pxor          %xmm9,%xmm8
+  .byte  102,68,15,111,21,25,85,0,0          // movdqa        0x5519(%rip),%xmm10        # 5650 <_sk_callback_sse2+0x11c>
+  .byte  102,69,15,111,216                   // movdqa        %xmm8,%xmm11
+  .byte  102,69,15,219,218                   // pand          %xmm10,%xmm11
+  .byte  102,65,15,114,243,5                 // pslld         $0x5,%xmm11
+  .byte  102,69,15,219,209                   // pand          %xmm9,%xmm10
+  .byte  102,65,15,114,242,4                 // pslld         $0x4,%xmm10
+  .byte  102,68,15,111,37,5,85,0,0           // movdqa        0x5505(%rip),%xmm12        # 5660 <_sk_callback_sse2+0x12c>
+  .byte  102,68,15,111,45,12,85,0,0          // movdqa        0x550c(%rip),%xmm13        # 5670 <_sk_callback_sse2+0x13c>
+  .byte  102,69,15,111,240                   // movdqa        %xmm8,%xmm14
+  .byte  102,69,15,219,245                   // pand          %xmm13,%xmm14
+  .byte  102,65,15,114,246,2                 // pslld         $0x2,%xmm14
+  .byte  102,69,15,219,233                   // pand          %xmm9,%xmm13
+  .byte  102,69,15,254,237                   // paddd         %xmm13,%xmm13
+  .byte  102,69,15,219,196                   // pand          %xmm12,%xmm8
+  .byte  102,65,15,114,208,1                 // psrld         $0x1,%xmm8
+  .byte  102,69,15,219,204                   // pand          %xmm12,%xmm9
+  .byte  102,65,15,114,209,2                 // psrld         $0x2,%xmm9
+  .byte  102,69,15,235,234                   // por           %xmm10,%xmm13
+  .byte  102,69,15,235,233                   // por           %xmm9,%xmm13
+  .byte  102,69,15,235,243                   // por           %xmm11,%xmm14
+  .byte  102,69,15,235,245                   // por           %xmm13,%xmm14
+  .byte  102,69,15,235,240                   // por           %xmm8,%xmm14
+  .byte  69,15,91,198                        // cvtdq2ps      %xmm14,%xmm8
+  .byte  68,15,89,5,199,84,0,0               // mulps         0x54c7(%rip),%xmm8        # 5680 <_sk_callback_sse2+0x14c>
+  .byte  68,15,88,5,207,84,0,0               // addps         0x54cf(%rip),%xmm8        # 5690 <_sk_callback_sse2+0x15c>
+  .byte  243,68,15,16,16                     // movss         (%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  68,15,88,210                        // addps         %xmm2,%xmm10
+  .byte  15,93,195                           // minps         %xmm3,%xmm0
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  15,93,203                           // minps         %xmm3,%xmm1
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  68,15,95,201                        // maxps         %xmm1,%xmm9
+  .byte  68,15,93,211                        // minps         %xmm3,%xmm10
+  .byte  65,15,95,210                        // maxps         %xmm10,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_constant_color_sse2
+.globl _sk_constant_color_sse2
+FUNCTION(_sk_constant_color_sse2)
+_sk_constant_color_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,12                     // movss         0xc(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_rgba_sse2
+.globl _sk_load_rgba_sse2
+FUNCTION(_sk_load_rgba_sse2)
+_sk_load_rgba_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  15,16,72,16                         // movups        0x10(%rax),%xmm1
+  .byte  15,16,80,32                         // movups        0x20(%rax),%xmm2
+  .byte  15,16,88,48                         // movups        0x30(%rax),%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_rgba_sse2
+.globl _sk_store_rgba_sse2
+FUNCTION(_sk_store_rgba_sse2)
+_sk_store_rgba_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,17,0                             // movups        %xmm0,(%rax)
+  .byte  15,17,72,16                         // movups        %xmm1,0x10(%rax)
+  .byte  15,17,80,32                         // movups        %xmm2,0x20(%rax)
+  .byte  15,17,88,48                         // movups        %xmm3,0x30(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clear_sse2
+.globl _sk_clear_sse2
+FUNCTION(_sk_clear_sse2)
+_sk_clear_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcatop_sse2
+.globl _sk_srcatop_sse2
+FUNCTION(_sk_srcatop_sse2)
+_sk_srcatop_sse2:
+  .byte  15,89,199                           // mulps         %xmm7,%xmm0
+  .byte  68,15,40,5,41,84,0,0                // movaps        0x5429(%rip),%xmm8        # 56a0 <_sk_callback_sse2+0x16c>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  65,15,88,193                        // addps         %xmm9,%xmm0
+  .byte  15,89,207                           // mulps         %xmm7,%xmm1
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,88,209                        // addps         %xmm9,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstatop_sse2
+.globl _sk_dstatop_sse2
+FUNCTION(_sk_dstatop_sse2)
+_sk_dstatop_sse2:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  68,15,40,13,236,83,0,0              // movaps        0x53ec(%rip),%xmm9        # 56b0 <_sk_callback_sse2+0x17c>
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,197                        // mulps         %xmm5,%xmm8
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,198                        // mulps         %xmm6,%xmm8
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  65,15,88,208                        // addps         %xmm8,%xmm2
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcin_sse2
+.globl _sk_srcin_sse2
+FUNCTION(_sk_srcin_sse2)
+_sk_srcin_sse2:
+  .byte  15,89,199                           // mulps         %xmm7,%xmm0
+  .byte  15,89,207                           // mulps         %xmm7,%xmm1
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstin_sse2
+.globl _sk_dstin_sse2
+FUNCTION(_sk_dstin_sse2)
+_sk_dstin_sse2:
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  15,40,211                           // movaps        %xmm3,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcout_sse2
+.globl _sk_srcout_sse2
+FUNCTION(_sk_srcout_sse2)
+_sk_srcout_sse2:
+  .byte  68,15,40,5,144,83,0,0               // movaps        0x5390(%rip),%xmm8        # 56c0 <_sk_callback_sse2+0x18c>
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstout_sse2
+.globl _sk_dstout_sse2
+FUNCTION(_sk_dstout_sse2)
+_sk_dstout_sse2:
+  .byte  68,15,40,5,128,83,0,0               // movaps        0x5380(%rip),%xmm8        # 56d0 <_sk_callback_sse2+0x19c>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_sse2
+.globl _sk_srcover_sse2
+FUNCTION(_sk_srcover_sse2)
+_sk_srcover_sse2:
+  .byte  68,15,40,5,99,83,0,0                // movaps        0x5363(%rip),%xmm8        # 56e0 <_sk_callback_sse2+0x1ac>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  65,15,88,193                        // addps         %xmm9,%xmm0
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,88,209                        // addps         %xmm9,%xmm2
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstover_sse2
+.globl _sk_dstover_sse2
+FUNCTION(_sk_dstover_sse2)
+_sk_dstover_sse2:
+  .byte  68,15,40,5,55,83,0,0                // movaps        0x5337(%rip),%xmm8        # 56f0 <_sk_callback_sse2+0x1bc>
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_modulate_sse2
+.globl _sk_modulate_sse2
+FUNCTION(_sk_modulate_sse2)
+_sk_modulate_sse2:
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_multiply_sse2
+.globl _sk_multiply_sse2
+FUNCTION(_sk_multiply_sse2)
+_sk_multiply_sse2:
+  .byte  68,15,40,5,11,83,0,0                // movaps        0x530b(%rip),%xmm8        # 5700 <_sk_callback_sse2+0x1cc>
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,89,208                        // mulps         %xmm0,%xmm10
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,220                        // mulps         %xmm4,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,221                        // mulps         %xmm5,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,222                        // mulps         %xmm6,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  65,15,88,211                        // addps         %xmm11,%xmm2
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  65,15,88,216                        // addps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_plus__sse2
+.globl _sk_plus__sse2
+FUNCTION(_sk_plus__sse2)
+_sk_plus__sse2:
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_screen_sse2
+.globl _sk_screen_sse2
+FUNCTION(_sk_screen_sse2)
+_sk_screen_sse2:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  65,15,92,192                        // subps         %xmm8,%xmm0
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,88,197                        // addps         %xmm5,%xmm8
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  68,15,92,193                        // subps         %xmm1,%xmm8
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  68,15,88,206                        // addps         %xmm6,%xmm9
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,92,202                        // subps         %xmm2,%xmm9
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,88,215                        // addps         %xmm7,%xmm10
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xor__sse2
+.globl _sk_xor__sse2
+FUNCTION(_sk_xor__sse2)
+_sk_xor__sse2:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  15,40,29,64,82,0,0                  // movaps        0x5240(%rip),%xmm3        # 5710 <_sk_callback_sse2+0x1dc>
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  65,15,92,216                        // subps         %xmm8,%xmm3
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,89,212                        // mulps         %xmm4,%xmm10
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,89,213                        // mulps         %xmm5,%xmm10
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,89,214                        // mulps         %xmm6,%xmm10
+  .byte  65,15,88,210                        // addps         %xmm10,%xmm2
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  15,89,223                           // mulps         %xmm7,%xmm3
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_darken_sse2
+.globl _sk_darken_sse2
+FUNCTION(_sk_darken_sse2)
+_sk_darken_sse2:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,95,201                        // maxps         %xmm1,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,95,193                        // maxps         %xmm9,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,95,209                        // maxps         %xmm9,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,171,81,0,0                 // movaps        0x51ab(%rip),%xmm2        # 5720 <_sk_callback_sse2+0x1ec>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lighten_sse2
+.globl _sk_lighten_sse2
+FUNCTION(_sk_lighten_sse2)
+_sk_lighten_sse2:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,93,201                        // minps         %xmm1,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,93,193                        // minps         %xmm9,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,93,209                        // minps         %xmm9,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,80,81,0,0                  // movaps        0x5150(%rip),%xmm2        # 5730 <_sk_callback_sse2+0x1fc>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_difference_sse2
+.globl _sk_difference_sse2
+FUNCTION(_sk_difference_sse2)
+_sk_difference_sse2:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  68,15,93,201                        // minps         %xmm1,%xmm9
+  .byte  69,15,88,201                        // addps         %xmm9,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,93,193                        // minps         %xmm9,%xmm8
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,93,209                        // minps         %xmm9,%xmm2
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,234,80,0,0                 // movaps        0x50ea(%rip),%xmm2        # 5740 <_sk_callback_sse2+0x20c>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_exclusion_sse2
+.globl _sk_exclusion_sse2
+FUNCTION(_sk_exclusion_sse2)
+_sk_exclusion_sse2:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  68,15,89,197                        // mulps         %xmm5,%xmm8
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  65,15,92,200                        // subps         %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,88,198                        // addps         %xmm6,%xmm8
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  68,15,92,194                        // subps         %xmm2,%xmm8
+  .byte  15,40,21,170,80,0,0                 // movaps        0x50aa(%rip),%xmm2        # 5750 <_sk_callback_sse2+0x21c>
+  .byte  15,92,211                           // subps         %xmm3,%xmm2
+  .byte  15,89,215                           // mulps         %xmm7,%xmm2
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colorburn_sse2
+.globl _sk_colorburn_sse2
+FUNCTION(_sk_colorburn_sse2)
+_sk_colorburn_sse2:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  68,15,40,21,157,80,0,0              // movaps        0x509d(%rip),%xmm10        # 5760 <_sk_callback_sse2+0x22c>
+  .byte  69,15,40,202                        // movaps        %xmm10,%xmm9
+  .byte  68,15,92,207                        // subps         %xmm7,%xmm9
+  .byte  69,15,40,217                        // movaps        %xmm9,%xmm11
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  65,15,94,192                        // divps         %xmm8,%xmm0
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,93,224                        // minps         %xmm0,%xmm12
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,235                        // addps         %xmm11,%xmm13
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  69,15,40,224                        // movaps        %xmm8,%xmm12
+  .byte  69,15,194,227,0                     // cmpeqps       %xmm11,%xmm12
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  69,15,84,196                        // andps         %xmm12,%xmm8
+  .byte  69,15,85,229                        // andnps        %xmm13,%xmm12
+  .byte  69,15,40,234                        // movaps        %xmm10,%xmm13
+  .byte  68,15,89,236                        // mulps         %xmm4,%xmm13
+  .byte  69,15,86,224                        // orps          %xmm8,%xmm12
+  .byte  68,15,40,196                        // movaps        %xmm4,%xmm8
+  .byte  68,15,194,199,0                     // cmpeqps       %xmm7,%xmm8
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  65,15,84,192                        // andps         %xmm8,%xmm0
+  .byte  69,15,85,196                        // andnps        %xmm12,%xmm8
+  .byte  65,15,86,192                        // orps          %xmm8,%xmm0
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,193                        // mulps         %xmm1,%xmm8
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,92,229                        // subps         %xmm5,%xmm12
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  68,15,94,225                        // divps         %xmm1,%xmm12
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  69,15,93,236                        // minps         %xmm12,%xmm13
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,40,233                        // movaps        %xmm1,%xmm13
+  .byte  69,15,194,235,0                     // cmpeqps       %xmm11,%xmm13
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  69,15,88,224                        // addps         %xmm8,%xmm12
+  .byte  65,15,84,205                        // andps         %xmm13,%xmm1
+  .byte  69,15,85,236                        // andnps        %xmm12,%xmm13
+  .byte  68,15,88,197                        // addps         %xmm5,%xmm8
+  .byte  68,15,86,233                        // orps          %xmm1,%xmm13
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  68,15,88,233                        // addps         %xmm1,%xmm13
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,194,207,0                        // cmpeqps       %xmm7,%xmm1
+  .byte  68,15,84,193                        // andps         %xmm1,%xmm8
+  .byte  65,15,85,205                        // andnps        %xmm13,%xmm1
+  .byte  68,15,86,193                        // orps          %xmm1,%xmm8
+  .byte  15,40,207                           // movaps        %xmm7,%xmm1
+  .byte  15,92,206                           // subps         %xmm6,%xmm1
+  .byte  15,89,203                           // mulps         %xmm3,%xmm1
+  .byte  15,94,202                           // divps         %xmm2,%xmm1
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,93,225                        // minps         %xmm1,%xmm12
+  .byte  15,40,207                           // movaps        %xmm7,%xmm1
+  .byte  65,15,92,204                        // subps         %xmm12,%xmm1
+  .byte  68,15,89,202                        // mulps         %xmm2,%xmm9
+  .byte  68,15,194,218,0                     // cmpeqps       %xmm2,%xmm11
+  .byte  15,89,203                           // mulps         %xmm3,%xmm1
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  65,15,84,211                        // andps         %xmm11,%xmm2
+  .byte  68,15,85,217                        // andnps        %xmm1,%xmm11
+  .byte  68,15,88,206                        // addps         %xmm6,%xmm9
+  .byte  68,15,86,218                        // orps          %xmm2,%xmm11
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  15,89,206                           // mulps         %xmm6,%xmm1
+  .byte  68,15,88,217                        // addps         %xmm1,%xmm11
+  .byte  15,40,206                           // movaps        %xmm6,%xmm1
+  .byte  15,194,207,0                        // cmpeqps       %xmm7,%xmm1
+  .byte  68,15,84,201                        // andps         %xmm1,%xmm9
+  .byte  65,15,85,203                        // andnps        %xmm11,%xmm1
+  .byte  68,15,86,201                        // orps          %xmm1,%xmm9
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_colordodge_sse2
+.globl _sk_colordodge_sse2
+FUNCTION(_sk_colordodge_sse2)
+_sk_colordodge_sse2:
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  68,15,40,21,83,79,0,0               // movaps        0x4f53(%rip),%xmm10        # 5770 <_sk_callback_sse2+0x23c>
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  68,15,92,223                        // subps         %xmm7,%xmm11
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,89,225                        // mulps         %xmm9,%xmm12
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,89,196                        // mulps         %xmm4,%xmm8
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  68,15,94,192                        // divps         %xmm0,%xmm8
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  65,15,93,192                        // minps         %xmm8,%xmm0
+  .byte  69,15,40,241                        // movaps        %xmm9,%xmm14
+  .byte  68,15,194,243,0                     // cmpeqps       %xmm3,%xmm14
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  65,15,88,196                        // addps         %xmm12,%xmm0
+  .byte  69,15,84,206                        // andps         %xmm14,%xmm9
+  .byte  68,15,85,240                        // andnps        %xmm0,%xmm14
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  65,15,40,196                        // movaps        %xmm12,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  68,15,92,211                        // subps         %xmm3,%xmm10
+  .byte  69,15,86,241                        // orps          %xmm9,%xmm14
+  .byte  69,15,40,202                        // movaps        %xmm10,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  69,15,88,241                        // addps         %xmm9,%xmm14
+  .byte  68,15,40,204                        // movaps        %xmm4,%xmm9
+  .byte  69,15,194,200,0                     // cmpeqps       %xmm8,%xmm9
+  .byte  65,15,84,193                        // andps         %xmm9,%xmm0
+  .byte  69,15,85,206                        // andnps        %xmm14,%xmm9
+  .byte  65,15,86,193                        // orps          %xmm9,%xmm0
+  .byte  68,15,40,203                        // movaps        %xmm3,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  68,15,92,225                        // subps         %xmm1,%xmm12
+  .byte  69,15,94,204                        // divps         %xmm12,%xmm9
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  68,15,89,225                        // mulps         %xmm1,%xmm12
+  .byte  69,15,93,233                        // minps         %xmm9,%xmm13
+  .byte  68,15,40,241                        // movaps        %xmm1,%xmm14
+  .byte  68,15,194,243,0                     // cmpeqps       %xmm3,%xmm14
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,236                        // addps         %xmm12,%xmm13
+  .byte  65,15,84,206                        // andps         %xmm14,%xmm1
+  .byte  69,15,85,245                        // andnps        %xmm13,%xmm14
+  .byte  69,15,40,204                        // movaps        %xmm12,%xmm9
+  .byte  68,15,88,205                        // addps         %xmm5,%xmm9
+  .byte  68,15,86,241                        // orps          %xmm1,%xmm14
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  68,15,88,241                        // addps         %xmm1,%xmm14
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  65,15,194,200,0                     // cmpeqps       %xmm8,%xmm1
+  .byte  68,15,84,201                        // andps         %xmm1,%xmm9
+  .byte  65,15,85,206                        // andnps        %xmm14,%xmm1
+  .byte  68,15,86,201                        // orps          %xmm1,%xmm9
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  68,15,89,230                        // mulps         %xmm6,%xmm12
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,92,202                           // subps         %xmm2,%xmm1
+  .byte  68,15,94,225                        // divps         %xmm1,%xmm12
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,93,236                        // minps         %xmm12,%xmm13
+  .byte  15,40,202                           // movaps        %xmm2,%xmm1
+  .byte  15,194,203,0                        // cmpeqps       %xmm3,%xmm1
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,235                        // addps         %xmm11,%xmm13
+  .byte  15,84,209                           // andps         %xmm1,%xmm2
+  .byte  65,15,85,205                        // andnps        %xmm13,%xmm1
+  .byte  15,86,202                           // orps          %xmm2,%xmm1
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  68,15,194,198,0                     // cmpeqps       %xmm6,%xmm8
+  .byte  68,15,88,222                        // addps         %xmm6,%xmm11
+  .byte  69,15,84,216                        // andps         %xmm8,%xmm11
+  .byte  68,15,85,193                        // andnps        %xmm1,%xmm8
+  .byte  69,15,86,195                        // orps          %xmm11,%xmm8
+  .byte  68,15,89,215                        // mulps         %xmm7,%xmm10
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hardlight_sse2
+.globl _sk_hardlight_sse2
+FUNCTION(_sk_hardlight_sse2)
+_sk_hardlight_sse2:
+  .byte  15,41,116,36,232                    // movaps        %xmm6,-0x18(%rsp)
+  .byte  15,40,245                           // movaps        %xmm5,%xmm6
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  68,15,40,29,8,78,0,0                // movaps        0x4e08(%rip),%xmm11        # 5780 <_sk_callback_sse2+0x24c>
+  .byte  69,15,40,211                        // movaps        %xmm11,%xmm10
+  .byte  68,15,92,215                        // subps         %xmm7,%xmm10
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  68,15,92,219                        // subps         %xmm3,%xmm11
+  .byte  69,15,40,203                        // movaps        %xmm11,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  69,15,88,200                        // addps         %xmm8,%xmm9
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,92,192                        // subps         %xmm0,%xmm8
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  15,89,231                           // mulps         %xmm7,%xmm4
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,40,247                        // movaps        %xmm7,%xmm14
+  .byte  68,15,40,255                        // movaps        %xmm7,%xmm15
+  .byte  68,15,92,253                        // subps         %xmm5,%xmm15
+  .byte  69,15,89,248                        // mulps         %xmm8,%xmm15
+  .byte  69,15,88,255                        // addps         %xmm15,%xmm15
+  .byte  68,15,40,228                        // movaps        %xmm4,%xmm12
+  .byte  69,15,92,231                        // subps         %xmm15,%xmm12
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  68,15,194,195,2                     // cmpleps       %xmm3,%xmm8
+  .byte  15,89,197                           // mulps         %xmm5,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  65,15,84,192                        // andps         %xmm8,%xmm0
+  .byte  69,15,85,196                        // andnps        %xmm12,%xmm8
+  .byte  68,15,86,192                        // orps          %xmm0,%xmm8
+  .byte  69,15,40,251                        // movaps        %xmm11,%xmm15
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  68,15,89,223                        // mulps         %xmm7,%xmm11
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,89,193                           // mulps         %xmm1,%xmm0
+  .byte  68,15,89,254                        // mulps         %xmm6,%xmm15
+  .byte  68,15,88,248                        // addps         %xmm0,%xmm15
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  68,15,92,238                        // subps         %xmm6,%xmm13
+  .byte  68,15,89,232                        // mulps         %xmm0,%xmm13
+  .byte  69,15,88,237                        // addps         %xmm13,%xmm13
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  65,15,92,197                        // subps         %xmm13,%xmm0
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  69,15,88,201                        // addps         %xmm9,%xmm9
+  .byte  68,15,194,203,2                     // cmpleps       %xmm3,%xmm9
+  .byte  15,89,206                           // mulps         %xmm6,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  65,15,84,201                        // andps         %xmm9,%xmm1
+  .byte  68,15,85,200                        // andnps        %xmm0,%xmm9
+  .byte  68,15,86,201                        // orps          %xmm1,%xmm9
+  .byte  69,15,88,207                        // addps         %xmm15,%xmm9
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  68,15,40,108,36,232                 // movaps        -0x18(%rsp),%xmm13
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,88,226                        // addps         %xmm10,%xmm12
+  .byte  68,15,40,210                        // movaps        %xmm2,%xmm10
+  .byte  69,15,88,210                        // addps         %xmm10,%xmm10
+  .byte  68,15,194,211,2                     // cmpleps       %xmm3,%xmm10
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,92,194                           // subps         %xmm2,%xmm0
+  .byte  65,15,89,213                        // mulps         %xmm13,%xmm2
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  69,15,92,245                        // subps         %xmm13,%xmm14
+  .byte  68,15,89,240                        // mulps         %xmm0,%xmm14
+  .byte  69,15,88,246                        // addps         %xmm14,%xmm14
+  .byte  65,15,92,230                        // subps         %xmm14,%xmm4
+  .byte  65,15,84,210                        // andps         %xmm10,%xmm2
+  .byte  68,15,85,212                        // andnps        %xmm4,%xmm10
+  .byte  68,15,86,210                        // orps          %xmm2,%xmm10
+  .byte  69,15,88,212                        // addps         %xmm12,%xmm10
+  .byte  65,15,88,219                        // addps         %xmm11,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,40,238                           // movaps        %xmm6,%xmm5
+  .byte  65,15,40,245                        // movaps        %xmm13,%xmm6
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_overlay_sse2
+.globl _sk_overlay_sse2
+FUNCTION(_sk_overlay_sse2)
+_sk_overlay_sse2:
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  68,15,40,232                        // movaps        %xmm0,%xmm13
+  .byte  68,15,40,13,214,76,0,0              // movaps        0x4cd6(%rip),%xmm9        # 5790 <_sk_callback_sse2+0x25c>
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  68,15,92,215                        // subps         %xmm7,%xmm10
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  69,15,89,221                        // mulps         %xmm13,%xmm11
+  .byte  68,15,92,203                        // subps         %xmm3,%xmm9
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,89,236                        // mulps         %xmm4,%xmm13
+  .byte  68,15,40,247                        // movaps        %xmm7,%xmm14
+  .byte  68,15,92,244                        // subps         %xmm4,%xmm14
+  .byte  15,40,204                           // movaps        %xmm4,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,194,207,2                        // cmpleps       %xmm7,%xmm1
+  .byte  69,15,88,237                        // addps         %xmm13,%xmm13
+  .byte  68,15,40,219                        // movaps        %xmm3,%xmm11
+  .byte  68,15,89,223                        // mulps         %xmm7,%xmm11
+  .byte  69,15,89,244                        // mulps         %xmm12,%xmm14
+  .byte  69,15,88,246                        // addps         %xmm14,%xmm14
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,92,230                        // subps         %xmm14,%xmm12
+  .byte  68,15,84,233                        // andps         %xmm1,%xmm13
+  .byte  65,15,85,204                        // andnps        %xmm12,%xmm1
+  .byte  65,15,86,205                        // orps          %xmm13,%xmm1
+  .byte  15,88,193                           // addps         %xmm1,%xmm0
+  .byte  69,15,40,226                        // movaps        %xmm10,%xmm12
+  .byte  69,15,89,224                        // mulps         %xmm8,%xmm12
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  65,15,88,204                        // addps         %xmm12,%xmm1
+  .byte  68,15,40,227                        // movaps        %xmm3,%xmm12
+  .byte  69,15,92,224                        // subps         %xmm8,%xmm12
+  .byte  68,15,89,197                        // mulps         %xmm5,%xmm8
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  68,15,92,237                        // subps         %xmm5,%xmm13
+  .byte  68,15,40,245                        // movaps        %xmm5,%xmm14
+  .byte  69,15,88,246                        // addps         %xmm14,%xmm14
+  .byte  68,15,194,247,2                     // cmpleps       %xmm7,%xmm14
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  69,15,89,236                        // mulps         %xmm12,%xmm13
+  .byte  69,15,88,237                        // addps         %xmm13,%xmm13
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  69,15,84,198                        // andps         %xmm14,%xmm8
+  .byte  69,15,85,244                        // andnps        %xmm12,%xmm14
+  .byte  69,15,86,240                        // orps          %xmm8,%xmm14
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,198                        // mulps         %xmm6,%xmm8
+  .byte  69,15,88,194                        // addps         %xmm10,%xmm8
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,92,210                        // subps         %xmm2,%xmm10
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,92,230                        // subps         %xmm6,%xmm12
+  .byte  68,15,40,238                        // movaps        %xmm6,%xmm13
+  .byte  69,15,88,237                        // addps         %xmm13,%xmm13
+  .byte  68,15,194,239,2                     // cmpleps       %xmm7,%xmm13
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  69,15,88,228                        // addps         %xmm12,%xmm12
+  .byte  69,15,92,220                        // subps         %xmm12,%xmm11
+  .byte  65,15,84,213                        // andps         %xmm13,%xmm2
+  .byte  69,15,85,235                        // andnps        %xmm11,%xmm13
+  .byte  68,15,86,234                        // orps          %xmm2,%xmm13
+  .byte  69,15,88,197                        // addps         %xmm13,%xmm8
+  .byte  68,15,89,207                        // mulps         %xmm7,%xmm9
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_softlight_sse2
+.globl _sk_softlight_sse2
+FUNCTION(_sk_softlight_sse2)
+_sk_softlight_sse2:
+  .byte  15,41,116,36,216                    // movaps        %xmm6,-0x28(%rsp)
+  .byte  15,40,245                           // movaps        %xmm5,%xmm6
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,41,84,36,232                     // movaps        %xmm2,-0x18(%rsp)
+  .byte  15,40,209                           // movaps        %xmm1,%xmm2
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,87,228                        // xorps         %xmm12,%xmm12
+  .byte  68,15,194,231,1                     // cmpltps       %xmm7,%xmm12
+  .byte  68,15,40,213                        // movaps        %xmm5,%xmm10
+  .byte  68,15,94,215                        // divps         %xmm7,%xmm10
+  .byte  69,15,84,212                        // andps         %xmm12,%xmm10
+  .byte  68,15,40,13,147,75,0,0              // movaps        0x4b93(%rip),%xmm9        # 57a0 <_sk_callback_sse2+0x26c>
+  .byte  69,15,40,249                        // movaps        %xmm9,%xmm15
+  .byte  69,15,92,250                        // subps         %xmm10,%xmm15
+  .byte  69,15,40,218                        // movaps        %xmm10,%xmm11
+  .byte  69,15,40,234                        // movaps        %xmm10,%xmm13
+  .byte  65,15,82,194                        // rsqrtps       %xmm10,%xmm0
+  .byte  15,83,200                           // rcpps         %xmm0,%xmm1
+  .byte  65,15,92,202                        // subps         %xmm10,%xmm1
+  .byte  69,15,88,210                        // addps         %xmm10,%xmm10
+  .byte  69,15,88,210                        // addps         %xmm10,%xmm10
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  15,89,192                           // mulps         %xmm0,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  68,15,40,53,109,75,0,0              // movaps        0x4b6d(%rip),%xmm14        # 57b0 <_sk_callback_sse2+0x27c>
+  .byte  69,15,88,222                        // addps         %xmm14,%xmm11
+  .byte  68,15,89,216                        // mulps         %xmm0,%xmm11
+  .byte  68,15,40,21,109,75,0,0              // movaps        0x4b6d(%rip),%xmm10        # 57c0 <_sk_callback_sse2+0x28c>
+  .byte  69,15,89,234                        // mulps         %xmm10,%xmm13
+  .byte  69,15,88,235                        // addps         %xmm11,%xmm13
+  .byte  15,88,228                           // addps         %xmm4,%xmm4
+  .byte  15,88,228                           // addps         %xmm4,%xmm4
+  .byte  15,194,231,2                        // cmpleps       %xmm7,%xmm4
+  .byte  68,15,84,236                        // andps         %xmm4,%xmm13
+  .byte  15,85,225                           // andnps        %xmm1,%xmm4
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,88,192                           // addps         %xmm0,%xmm0
+  .byte  65,15,86,229                        // orps          %xmm13,%xmm4
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,92,203                           // subps         %xmm3,%xmm1
+  .byte  68,15,89,249                        // mulps         %xmm1,%xmm15
+  .byte  15,89,207                           // mulps         %xmm7,%xmm1
+  .byte  15,89,225                           // mulps         %xmm1,%xmm4
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  15,88,225                           // addps         %xmm1,%xmm4
+  .byte  69,15,40,217                        // movaps        %xmm9,%xmm11
+  .byte  68,15,92,219                        // subps         %xmm3,%xmm11
+  .byte  65,15,40,203                        // movaps        %xmm11,%xmm1
+  .byte  15,89,205                           // mulps         %xmm5,%xmm1
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  68,15,92,239                        // subps         %xmm7,%xmm13
+  .byte  69,15,89,197                        // mulps         %xmm13,%xmm8
+  .byte  68,15,88,193                        // addps         %xmm1,%xmm8
+  .byte  68,15,88,251                        // addps         %xmm3,%xmm15
+  .byte  68,15,89,253                        // mulps         %xmm5,%xmm15
+  .byte  15,194,195,2                        // cmpleps       %xmm3,%xmm0
+  .byte  68,15,84,248                        // andps         %xmm0,%xmm15
+  .byte  15,85,196                           // andnps        %xmm4,%xmm0
+  .byte  65,15,86,199                        // orps          %xmm15,%xmm0
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,198                        // movaps        %xmm6,%xmm8
+  .byte  68,15,94,199                        // divps         %xmm7,%xmm8
+  .byte  69,15,84,196                        // andps         %xmm12,%xmm8
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,40,225                           // movaps        %xmm1,%xmm4
+  .byte  15,89,228                           // mulps         %xmm4,%xmm4
+  .byte  15,88,225                           // addps         %xmm1,%xmm4
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  69,15,40,249                        // movaps        %xmm9,%xmm15
+  .byte  69,15,92,248                        // subps         %xmm8,%xmm15
+  .byte  65,15,82,224                        // rsqrtps       %xmm8,%xmm4
+  .byte  15,83,228                           // rcpps         %xmm4,%xmm4
+  .byte  65,15,92,224                        // subps         %xmm8,%xmm4
+  .byte  69,15,89,194                        // mulps         %xmm10,%xmm8
+  .byte  68,15,88,193                        // addps         %xmm1,%xmm8
+  .byte  15,40,206                           // movaps        %xmm6,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,194,207,2                        // cmpleps       %xmm7,%xmm1
+  .byte  68,15,84,193                        // andps         %xmm1,%xmm8
+  .byte  15,85,204                           // andnps        %xmm4,%xmm1
+  .byte  65,15,86,200                        // orps          %xmm8,%xmm1
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  69,15,88,192                        // addps         %xmm8,%xmm8
+  .byte  65,15,40,224                        // movaps        %xmm8,%xmm4
+  .byte  15,92,227                           // subps         %xmm3,%xmm4
+  .byte  68,15,89,252                        // mulps         %xmm4,%xmm15
+  .byte  15,89,231                           // mulps         %xmm7,%xmm4
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  15,89,230                           // mulps         %xmm6,%xmm4
+  .byte  15,88,204                           // addps         %xmm4,%xmm1
+  .byte  65,15,40,227                        // movaps        %xmm11,%xmm4
+  .byte  15,89,230                           // mulps         %xmm6,%xmm4
+  .byte  65,15,89,213                        // mulps         %xmm13,%xmm2
+  .byte  15,88,212                           // addps         %xmm4,%xmm2
+  .byte  68,15,88,251                        // addps         %xmm3,%xmm15
+  .byte  68,15,89,254                        // mulps         %xmm6,%xmm15
+  .byte  68,15,194,195,2                     // cmpleps       %xmm3,%xmm8
+  .byte  69,15,84,248                        // andps         %xmm8,%xmm15
+  .byte  68,15,85,193                        // andnps        %xmm1,%xmm8
+  .byte  69,15,86,199                        // orps          %xmm15,%xmm8
+  .byte  68,15,88,194                        // addps         %xmm2,%xmm8
+  .byte  68,15,40,124,36,216                 // movaps        -0x28(%rsp),%xmm15
+  .byte  65,15,40,207                        // movaps        %xmm15,%xmm1
+  .byte  15,94,207                           // divps         %xmm7,%xmm1
+  .byte  65,15,84,204                        // andps         %xmm12,%xmm1
+  .byte  68,15,92,201                        // subps         %xmm1,%xmm9
+  .byte  68,15,88,241                        // addps         %xmm1,%xmm14
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  15,82,209                           // rsqrtps       %xmm1,%xmm2
+  .byte  15,83,210                           // rcpps         %xmm2,%xmm2
+  .byte  15,92,209                           // subps         %xmm1,%xmm2
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,40,225                           // movaps        %xmm1,%xmm4
+  .byte  15,89,228                           // mulps         %xmm4,%xmm4
+  .byte  15,88,225                           // addps         %xmm1,%xmm4
+  .byte  68,15,89,244                        // mulps         %xmm4,%xmm14
+  .byte  69,15,88,214                        // addps         %xmm14,%xmm10
+  .byte  65,15,40,207                        // movaps        %xmm15,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,88,201                           // addps         %xmm1,%xmm1
+  .byte  15,194,207,2                        // cmpleps       %xmm7,%xmm1
+  .byte  68,15,84,209                        // andps         %xmm1,%xmm10
+  .byte  15,85,202                           // andnps        %xmm2,%xmm1
+  .byte  15,40,84,36,232                     // movaps        -0x18(%rsp),%xmm2
+  .byte  68,15,89,234                        // mulps         %xmm2,%xmm13
+  .byte  15,88,210                           // addps         %xmm2,%xmm2
+  .byte  65,15,86,202                        // orps          %xmm10,%xmm1
+  .byte  15,40,226                           // movaps        %xmm2,%xmm4
+  .byte  15,92,227                           // subps         %xmm3,%xmm4
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  15,89,231                           // mulps         %xmm7,%xmm4
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  65,15,89,231                        // mulps         %xmm15,%xmm4
+  .byte  15,88,204                           // addps         %xmm4,%xmm1
+  .byte  65,15,40,227                        // movaps        %xmm11,%xmm4
+  .byte  65,15,89,231                        // mulps         %xmm15,%xmm4
+  .byte  65,15,88,229                        // addps         %xmm13,%xmm4
+  .byte  68,15,88,203                        // addps         %xmm3,%xmm9
+  .byte  69,15,89,207                        // mulps         %xmm15,%xmm9
+  .byte  69,15,40,215                        // movaps        %xmm15,%xmm10
+  .byte  15,194,211,2                        // cmpleps       %xmm3,%xmm2
+  .byte  68,15,84,202                        // andps         %xmm2,%xmm9
+  .byte  15,85,209                           // andnps        %xmm1,%xmm2
+  .byte  65,15,86,209                        // orps          %xmm9,%xmm2
+  .byte  15,88,212                           // addps         %xmm4,%xmm2
+  .byte  68,15,89,223                        // mulps         %xmm7,%xmm11
+  .byte  65,15,88,219                        // addps         %xmm11,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,40,238                           // movaps        %xmm6,%xmm5
+  .byte  65,15,40,242                        // movaps        %xmm10,%xmm6
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hue_sse2
+.globl _sk_hue_sse2
+FUNCTION(_sk_hue_sse2)
+_sk_hue_sse2:
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  68,15,40,225                        // movaps        %xmm1,%xmm12
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  68,15,40,5,176,73,0,0               // movaps        0x49b0(%rip),%xmm8        # 5800 <_sk_callback_sse2+0x2cc>
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  15,40,207                           // movaps        %xmm7,%xmm1
+  .byte  68,15,92,217                        // subps         %xmm1,%xmm11
+  .byte  65,15,89,195                        // mulps         %xmm11,%xmm0
+  .byte  15,41,68,36,232                     // movaps        %xmm0,-0x18(%rsp)
+  .byte  69,15,89,227                        // mulps         %xmm11,%xmm12
+  .byte  68,15,41,100,36,216                 // movaps        %xmm12,-0x28(%rsp)
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  15,89,211                           // mulps         %xmm3,%xmm2
+  .byte  68,15,40,229                        // movaps        %xmm5,%xmm12
+  .byte  68,15,40,237                        // movaps        %xmm5,%xmm13
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  15,41,68,36,200                     // movaps        %xmm0,-0x38(%rsp)
+  .byte  15,40,254                           // movaps        %xmm6,%xmm7
+  .byte  68,15,95,239                        // maxps         %xmm7,%xmm13
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  68,15,40,245                        // movaps        %xmm5,%xmm14
+  .byte  68,15,40,253                        // movaps        %xmm5,%xmm15
+  .byte  69,15,95,253                        // maxps         %xmm13,%xmm15
+  .byte  68,15,93,231                        // minps         %xmm7,%xmm12
+  .byte  69,15,93,244                        // minps         %xmm12,%xmm14
+  .byte  69,15,92,254                        // subps         %xmm14,%xmm15
+  .byte  69,15,40,226                        // movaps        %xmm10,%xmm12
+  .byte  68,15,93,226                        // minps         %xmm2,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,93,236                        // minps         %xmm12,%xmm13
+  .byte  69,15,40,226                        // movaps        %xmm10,%xmm12
+  .byte  68,15,95,226                        // maxps         %xmm2,%xmm12
+  .byte  69,15,40,241                        // movaps        %xmm9,%xmm14
+  .byte  69,15,95,244                        // maxps         %xmm12,%xmm14
+  .byte  69,15,92,245                        // subps         %xmm13,%xmm14
+  .byte  69,15,92,205                        // subps         %xmm13,%xmm9
+  .byte  69,15,92,213                        // subps         %xmm13,%xmm10
+  .byte  65,15,92,213                        // subps         %xmm13,%xmm2
+  .byte  15,40,240                           // movaps        %xmm0,%xmm6
+  .byte  68,15,89,251                        // mulps         %xmm3,%xmm15
+  .byte  69,15,89,207                        // mulps         %xmm15,%xmm9
+  .byte  69,15,89,215                        // mulps         %xmm15,%xmm10
+  .byte  65,15,89,215                        // mulps         %xmm15,%xmm2
+  .byte  69,15,87,228                        // xorps         %xmm12,%xmm12
+  .byte  69,15,94,206                        // divps         %xmm14,%xmm9
+  .byte  69,15,94,214                        // divps         %xmm14,%xmm10
+  .byte  65,15,94,214                        // divps         %xmm14,%xmm2
+  .byte  69,15,194,244,4                     // cmpneqps      %xmm12,%xmm14
+  .byte  69,15,84,206                        // andps         %xmm14,%xmm9
+  .byte  69,15,84,214                        // andps         %xmm14,%xmm10
+  .byte  65,15,84,214                        // andps         %xmm14,%xmm2
+  .byte  68,15,40,61,189,72,0,0              // movaps        0x48bd(%rip),%xmm15        # 57d0 <_sk_callback_sse2+0x29c>
+  .byte  65,15,89,231                        // mulps         %xmm15,%xmm4
+  .byte  15,40,5,194,72,0,0                  // movaps        0x48c2(%rip),%xmm0        # 57e0 <_sk_callback_sse2+0x2ac>
+  .byte  15,89,240                           // mulps         %xmm0,%xmm6
+  .byte  15,88,244                           // addps         %xmm4,%xmm6
+  .byte  68,15,40,53,196,72,0,0              // movaps        0x48c4(%rip),%xmm14        # 57f0 <_sk_callback_sse2+0x2bc>
+  .byte  68,15,40,239                        // movaps        %xmm7,%xmm13
+  .byte  69,15,89,238                        // mulps         %xmm14,%xmm13
+  .byte  68,15,88,238                        // addps         %xmm6,%xmm13
+  .byte  65,15,40,225                        // movaps        %xmm9,%xmm4
+  .byte  65,15,89,231                        // mulps         %xmm15,%xmm4
+  .byte  65,15,40,242                        // movaps        %xmm10,%xmm6
+  .byte  15,89,240                           // mulps         %xmm0,%xmm6
+  .byte  15,88,244                           // addps         %xmm4,%xmm6
+  .byte  15,40,226                           // movaps        %xmm2,%xmm4
+  .byte  65,15,89,230                        // mulps         %xmm14,%xmm4
+  .byte  15,88,230                           // addps         %xmm6,%xmm4
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  68,15,92,236                        // subps         %xmm4,%xmm13
+  .byte  69,15,88,205                        // addps         %xmm13,%xmm9
+  .byte  69,15,88,213                        // addps         %xmm13,%xmm10
+  .byte  68,15,88,234                        // addps         %xmm2,%xmm13
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,93,213                        // minps         %xmm13,%xmm2
+  .byte  65,15,40,241                        // movaps        %xmm9,%xmm6
+  .byte  15,93,242                           // minps         %xmm2,%xmm6
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,95,213                        // maxps         %xmm13,%xmm2
+  .byte  65,15,40,225                        // movaps        %xmm9,%xmm4
+  .byte  15,95,226                           // maxps         %xmm2,%xmm4
+  .byte  69,15,89,249                        // mulps         %xmm9,%xmm15
+  .byte  65,15,89,194                        // mulps         %xmm10,%xmm0
+  .byte  65,15,88,199                        // addps         %xmm15,%xmm0
+  .byte  69,15,89,245                        // mulps         %xmm13,%xmm14
+  .byte  68,15,88,240                        // addps         %xmm0,%xmm14
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,194,214,2                        // cmpleps       %xmm6,%xmm2
+  .byte  69,15,40,254                        // movaps        %xmm14,%xmm15
+  .byte  68,15,92,254                        // subps         %xmm6,%xmm15
+  .byte  65,15,40,241                        // movaps        %xmm9,%xmm6
+  .byte  65,15,92,246                        // subps         %xmm14,%xmm6
+  .byte  65,15,89,246                        // mulps         %xmm14,%xmm6
+  .byte  65,15,94,247                        // divps         %xmm15,%xmm6
+  .byte  65,15,88,246                        // addps         %xmm14,%xmm6
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  15,85,198                           // andnps        %xmm6,%xmm0
+  .byte  68,15,84,202                        // andps         %xmm2,%xmm9
+  .byte  68,15,86,200                        // orps          %xmm0,%xmm9
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,41,76,36,184                     // movaps        %xmm1,-0x48(%rsp)
+  .byte  15,89,193                           // mulps         %xmm1,%xmm0
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  15,88,217                           // addps         %xmm1,%xmm3
+  .byte  15,92,216                           // subps         %xmm0,%xmm3
+  .byte  15,41,92,36,168                     // movaps        %xmm3,-0x58(%rsp)
+  .byte  15,40,240                           // movaps        %xmm0,%xmm6
+  .byte  15,194,196,1                        // cmpltps       %xmm4,%xmm0
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  65,15,85,201                        // andnps        %xmm9,%xmm1
+  .byte  69,15,92,206                        // subps         %xmm14,%xmm9
+  .byte  65,15,92,246                        // subps         %xmm14,%xmm6
+  .byte  68,15,89,206                        // mulps         %xmm6,%xmm9
+  .byte  65,15,92,230                        // subps         %xmm14,%xmm4
+  .byte  68,15,94,204                        // divps         %xmm4,%xmm9
+  .byte  69,15,88,206                        // addps         %xmm14,%xmm9
+  .byte  68,15,84,200                        // andps         %xmm0,%xmm9
+  .byte  68,15,86,201                        // orps          %xmm1,%xmm9
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  65,15,92,206                        // subps         %xmm14,%xmm1
+  .byte  65,15,89,206                        // mulps         %xmm14,%xmm1
+  .byte  65,15,94,207                        // divps         %xmm15,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  15,40,218                           // movaps        %xmm2,%xmm3
+  .byte  15,85,217                           // andnps        %xmm1,%xmm3
+  .byte  68,15,84,210                        // andps         %xmm2,%xmm10
+  .byte  68,15,86,211                        // orps          %xmm3,%xmm10
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  65,15,85,202                        // andnps        %xmm10,%xmm1
+  .byte  69,15,92,214                        // subps         %xmm14,%xmm10
+  .byte  68,15,89,214                        // mulps         %xmm6,%xmm10
+  .byte  68,15,94,212                        // divps         %xmm4,%xmm10
+  .byte  69,15,88,214                        // addps         %xmm14,%xmm10
+  .byte  68,15,84,208                        // andps         %xmm0,%xmm10
+  .byte  68,15,86,209                        // orps          %xmm1,%xmm10
+  .byte  65,15,40,205                        // movaps        %xmm13,%xmm1
+  .byte  65,15,92,206                        // subps         %xmm14,%xmm1
+  .byte  65,15,89,206                        // mulps         %xmm14,%xmm1
+  .byte  65,15,94,207                        // divps         %xmm15,%xmm1
+  .byte  65,15,88,206                        // addps         %xmm14,%xmm1
+  .byte  68,15,84,234                        // andps         %xmm2,%xmm13
+  .byte  15,85,209                           // andnps        %xmm1,%xmm2
+  .byte  65,15,86,213                        // orps          %xmm13,%xmm2
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,85,202                           // andnps        %xmm2,%xmm1
+  .byte  65,15,92,214                        // subps         %xmm14,%xmm2
+  .byte  15,89,214                           // mulps         %xmm6,%xmm2
+  .byte  15,94,212                           // divps         %xmm4,%xmm2
+  .byte  65,15,88,214                        // addps         %xmm14,%xmm2
+  .byte  15,84,208                           // andps         %xmm0,%xmm2
+  .byte  15,86,209                           // orps          %xmm1,%xmm2
+  .byte  69,15,95,204                        // maxps         %xmm12,%xmm9
+  .byte  69,15,95,212                        // maxps         %xmm12,%xmm10
+  .byte  65,15,95,212                        // maxps         %xmm12,%xmm2
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,89,197                           // mulps         %xmm5,%xmm0
+  .byte  15,40,76,36,232                     // movaps        -0x18(%rsp),%xmm1
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  15,40,116,36,200                    // movaps        -0x38(%rsp),%xmm6
+  .byte  15,89,222                           // mulps         %xmm6,%xmm3
+  .byte  15,40,76,36,216                     // movaps        -0x28(%rsp),%xmm1
+  .byte  15,88,203                           // addps         %xmm3,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  69,15,88,195                        // addps         %xmm11,%xmm8
+  .byte  68,15,88,194                        // addps         %xmm2,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,40,238                           // movaps        %xmm6,%xmm5
+  .byte  15,40,247                           // movaps        %xmm7,%xmm6
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,40,92,36,168                     // movaps        -0x58(%rsp),%xmm3
+  .byte  15,40,124,36,184                    // movaps        -0x48(%rsp),%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_saturation_sse2
+.globl _sk_saturation_sse2
+FUNCTION(_sk_saturation_sse2)
+_sk_saturation_sse2:
+  .byte  68,15,40,231                        // movaps        %xmm7,%xmm12
+  .byte  68,15,40,198                        // movaps        %xmm6,%xmm8
+  .byte  15,40,251                           // movaps        %xmm3,%xmm7
+  .byte  68,15,40,216                        // movaps        %xmm0,%xmm11
+  .byte  68,15,40,215                        // movaps        %xmm7,%xmm10
+  .byte  68,15,89,212                        // mulps         %xmm4,%xmm10
+  .byte  15,40,244                           // movaps        %xmm4,%xmm6
+  .byte  15,41,116,36,184                    // movaps        %xmm6,-0x48(%rsp)
+  .byte  68,15,40,207                        // movaps        %xmm7,%xmm9
+  .byte  68,15,89,205                        // mulps         %xmm5,%xmm9
+  .byte  15,41,108,36,200                    // movaps        %xmm5,-0x38(%rsp)
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  69,15,40,232                        // movaps        %xmm8,%xmm13
+  .byte  68,15,41,108,36,168                 // movaps        %xmm13,-0x58(%rsp)
+  .byte  15,40,225                           // movaps        %xmm1,%xmm4
+  .byte  15,41,100,36,216                    // movaps        %xmm4,-0x28(%rsp)
+  .byte  15,40,220                           // movaps        %xmm4,%xmm3
+  .byte  15,41,84,36,232                     // movaps        %xmm2,-0x18(%rsp)
+  .byte  15,95,218                           // maxps         %xmm2,%xmm3
+  .byte  65,15,40,203                        // movaps        %xmm11,%xmm1
+  .byte  15,95,203                           // maxps         %xmm3,%xmm1
+  .byte  15,40,220                           // movaps        %xmm4,%xmm3
+  .byte  15,93,218                           // minps         %xmm2,%xmm3
+  .byte  65,15,40,211                        // movaps        %xmm11,%xmm2
+  .byte  15,93,211                           // minps         %xmm3,%xmm2
+  .byte  15,92,202                           // subps         %xmm2,%xmm1
+  .byte  65,15,89,204                        // mulps         %xmm12,%xmm1
+  .byte  65,15,40,228                        // movaps        %xmm12,%xmm4
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,93,208                           // minps         %xmm0,%xmm2
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  68,15,93,194                        // minps         %xmm2,%xmm8
+  .byte  65,15,40,209                        // movaps        %xmm9,%xmm2
+  .byte  15,95,208                           // maxps         %xmm0,%xmm2
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,95,218                           // maxps         %xmm2,%xmm3
+  .byte  65,15,92,216                        // subps         %xmm8,%xmm3
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  69,15,92,208                        // subps         %xmm8,%xmm10
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  68,15,94,211                        // divps         %xmm3,%xmm10
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  68,15,94,203                        // divps         %xmm3,%xmm9
+  .byte  65,15,92,192                        // subps         %xmm8,%xmm0
+  .byte  15,89,193                           // mulps         %xmm1,%xmm0
+  .byte  15,94,195                           // divps         %xmm3,%xmm0
+  .byte  15,194,218,4                        // cmpneqps      %xmm2,%xmm3
+  .byte  68,15,84,211                        // andps         %xmm3,%xmm10
+  .byte  68,15,84,203                        // andps         %xmm3,%xmm9
+  .byte  15,84,195                           // andps         %xmm3,%xmm0
+  .byte  68,15,40,5,86,70,0,0                // movaps        0x4656(%rip),%xmm8        # 5810 <_sk_callback_sse2+0x2dc>
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,40,13,88,70,0,0                  // movaps        0x4658(%rip),%xmm1        # 5820 <_sk_callback_sse2+0x2ec>
+  .byte  15,40,221                           // movaps        %xmm5,%xmm3
+  .byte  15,89,217                           // mulps         %xmm1,%xmm3
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  68,15,40,37,87,70,0,0               // movaps        0x4657(%rip),%xmm12        # 5830 <_sk_callback_sse2+0x2fc>
+  .byte  69,15,89,236                        // mulps         %xmm12,%xmm13
+  .byte  68,15,88,235                        // addps         %xmm3,%xmm13
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  65,15,40,217                        // movaps        %xmm9,%xmm3
+  .byte  15,89,217                           // mulps         %xmm1,%xmm3
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  65,15,89,212                        // mulps         %xmm12,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  68,15,89,239                        // mulps         %xmm7,%xmm13
+  .byte  68,15,92,234                        // subps         %xmm2,%xmm13
+  .byte  69,15,88,213                        // addps         %xmm13,%xmm10
+  .byte  69,15,88,205                        // addps         %xmm13,%xmm9
+  .byte  68,15,88,232                        // addps         %xmm0,%xmm13
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,93,197                        // minps         %xmm13,%xmm0
+  .byte  65,15,40,218                        // movaps        %xmm10,%xmm3
+  .byte  15,93,216                           // minps         %xmm0,%xmm3
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,95,197                        // maxps         %xmm13,%xmm0
+  .byte  69,15,40,242                        // movaps        %xmm10,%xmm14
+  .byte  68,15,95,240                        // maxps         %xmm0,%xmm14
+  .byte  69,15,89,194                        // mulps         %xmm10,%xmm8
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  68,15,88,225                        // addps         %xmm1,%xmm12
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,194,211,2                        // cmpleps       %xmm3,%xmm2
+  .byte  65,15,40,244                        // movaps        %xmm12,%xmm6
+  .byte  15,92,243                           // subps         %xmm3,%xmm6
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  65,15,92,196                        // subps         %xmm12,%xmm0
+  .byte  65,15,89,196                        // mulps         %xmm12,%xmm0
+  .byte  15,94,198                           // divps         %xmm6,%xmm0
+  .byte  65,15,88,196                        // addps         %xmm12,%xmm0
+  .byte  15,40,202                           // movaps        %xmm2,%xmm1
+  .byte  15,85,200                           // andnps        %xmm0,%xmm1
+  .byte  68,15,84,210                        // andps         %xmm2,%xmm10
+  .byte  68,15,86,209                        // orps          %xmm1,%xmm10
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,89,221                           // mulps         %xmm5,%xmm3
+  .byte  68,15,40,5,188,69,0,0               // movaps        0x45bc(%rip),%xmm8        # 5840 <_sk_callback_sse2+0x30c>
+  .byte  65,15,40,224                        // movaps        %xmm8,%xmm4
+  .byte  68,15,92,199                        // subps         %xmm7,%xmm8
+  .byte  15,88,253                           // addps         %xmm5,%xmm7
+  .byte  15,92,251                           // subps         %xmm3,%xmm7
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  65,15,194,222,1                     // cmpltps       %xmm14,%xmm3
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  65,15,85,194                        // andnps        %xmm10,%xmm0
+  .byte  69,15,92,212                        // subps         %xmm12,%xmm10
+  .byte  65,15,92,204                        // subps         %xmm12,%xmm1
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  69,15,92,244                        // subps         %xmm12,%xmm14
+  .byte  69,15,94,214                        // divps         %xmm14,%xmm10
+  .byte  69,15,88,212                        // addps         %xmm12,%xmm10
+  .byte  68,15,84,211                        // andps         %xmm3,%xmm10
+  .byte  68,15,86,208                        // orps          %xmm0,%xmm10
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,92,196                        // subps         %xmm12,%xmm0
+  .byte  65,15,89,196                        // mulps         %xmm12,%xmm0
+  .byte  15,94,198                           // divps         %xmm6,%xmm0
+  .byte  65,15,88,196                        // addps         %xmm12,%xmm0
+  .byte  68,15,40,250                        // movaps        %xmm2,%xmm15
+  .byte  68,15,85,248                        // andnps        %xmm0,%xmm15
+  .byte  68,15,84,202                        // andps         %xmm2,%xmm9
+  .byte  69,15,86,207                        // orps          %xmm15,%xmm9
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  65,15,85,193                        // andnps        %xmm9,%xmm0
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  69,15,94,206                        // divps         %xmm14,%xmm9
+  .byte  69,15,88,204                        // addps         %xmm12,%xmm9
+  .byte  68,15,84,203                        // andps         %xmm3,%xmm9
+  .byte  68,15,86,200                        // orps          %xmm0,%xmm9
+  .byte  65,15,40,197                        // movaps        %xmm13,%xmm0
+  .byte  65,15,92,196                        // subps         %xmm12,%xmm0
+  .byte  65,15,89,196                        // mulps         %xmm12,%xmm0
+  .byte  15,94,198                           // divps         %xmm6,%xmm0
+  .byte  65,15,88,196                        // addps         %xmm12,%xmm0
+  .byte  68,15,84,234                        // andps         %xmm2,%xmm13
+  .byte  15,85,208                           // andnps        %xmm0,%xmm2
+  .byte  65,15,86,213                        // orps          %xmm13,%xmm2
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,85,194                           // andnps        %xmm2,%xmm0
+  .byte  65,15,92,212                        // subps         %xmm12,%xmm2
+  .byte  15,89,209                           // mulps         %xmm1,%xmm2
+  .byte  65,15,94,214                        // divps         %xmm14,%xmm2
+  .byte  65,15,88,212                        // addps         %xmm12,%xmm2
+  .byte  15,84,211                           // andps         %xmm3,%xmm2
+  .byte  15,86,208                           // orps          %xmm0,%xmm2
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  68,15,95,208                        // maxps         %xmm0,%xmm10
+  .byte  68,15,95,200                        // maxps         %xmm0,%xmm9
+  .byte  15,95,208                           // maxps         %xmm0,%xmm2
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,40,92,36,184                     // movaps        -0x48(%rsp),%xmm3
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  68,15,40,229                        // movaps        %xmm5,%xmm12
+  .byte  65,15,92,228                        // subps         %xmm12,%xmm4
+  .byte  68,15,89,220                        // mulps         %xmm4,%xmm11
+  .byte  68,15,88,216                        // addps         %xmm0,%xmm11
+  .byte  69,15,88,218                        // addps         %xmm10,%xmm11
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,40,108,36,200                    // movaps        -0x38(%rsp),%xmm5
+  .byte  15,89,197                           // mulps         %xmm5,%xmm0
+  .byte  15,40,76,36,216                     // movaps        -0x28(%rsp),%xmm1
+  .byte  15,89,204                           // mulps         %xmm4,%xmm1
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  15,89,100,36,232                    // mulps         -0x18(%rsp),%xmm4
+  .byte  15,40,68,36,168                     // movaps        -0x58(%rsp),%xmm0
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  68,15,88,196                        // addps         %xmm4,%xmm8
+  .byte  68,15,88,194                        // addps         %xmm2,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,227                           // movaps        %xmm3,%xmm4
+  .byte  15,40,240                           // movaps        %xmm0,%xmm6
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  65,15,40,252                        // movaps        %xmm12,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_color_sse2
+.globl _sk_color_sse2
+FUNCTION(_sk_color_sse2)
+_sk_color_sse2:
+  .byte  68,15,40,199                        // movaps        %xmm7,%xmm8
+  .byte  68,15,40,230                        // movaps        %xmm6,%xmm12
+  .byte  68,15,41,100,36,216                 // movaps        %xmm12,-0x28(%rsp)
+  .byte  68,15,40,221                        // movaps        %xmm5,%xmm11
+  .byte  68,15,41,92,36,232                  // movaps        %xmm11,-0x18(%rsp)
+  .byte  15,40,244                           // movaps        %xmm4,%xmm6
+  .byte  15,41,84,36,184                     // movaps        %xmm2,-0x48(%rsp)
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  15,40,248                           // movaps        %xmm0,%xmm7
+  .byte  68,15,40,207                        // movaps        %xmm7,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  68,15,40,213                        // movaps        %xmm5,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  68,15,40,45,90,68,0,0               // movaps        0x445a(%rip),%xmm13        # 5850 <_sk_callback_sse2+0x31c>
+  .byte  68,15,40,198                        // movaps        %xmm6,%xmm8
+  .byte  69,15,89,197                        // mulps         %xmm13,%xmm8
+  .byte  68,15,40,53,90,68,0,0               // movaps        0x445a(%rip),%xmm14        # 5860 <_sk_callback_sse2+0x32c>
+  .byte  65,15,40,195                        // movaps        %xmm11,%xmm0
+  .byte  65,15,89,198                        // mulps         %xmm14,%xmm0
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,29,86,68,0,0               // movaps        0x4456(%rip),%xmm11        # 5870 <_sk_callback_sse2+0x33c>
+  .byte  69,15,89,227                        // mulps         %xmm11,%xmm12
+  .byte  68,15,88,224                        // addps         %xmm0,%xmm12
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,89,197                        // mulps         %xmm13,%xmm0
+  .byte  69,15,40,250                        // movaps        %xmm10,%xmm15
+  .byte  69,15,89,254                        // mulps         %xmm14,%xmm15
+  .byte  68,15,88,248                        // addps         %xmm0,%xmm15
+  .byte  68,15,40,5,66,68,0,0                // movaps        0x4442(%rip),%xmm8        # 5880 <_sk_callback_sse2+0x34c>
+  .byte  65,15,40,224                        // movaps        %xmm8,%xmm4
+  .byte  15,92,226                           // subps         %xmm2,%xmm4
+  .byte  15,89,252                           // mulps         %xmm4,%xmm7
+  .byte  15,89,236                           // mulps         %xmm4,%xmm5
+  .byte  15,40,76,36,184                     // movaps        -0x48(%rsp),%xmm1
+  .byte  15,89,225                           // mulps         %xmm1,%xmm4
+  .byte  15,89,202                           // mulps         %xmm2,%xmm1
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,89,195                        // mulps         %xmm11,%xmm0
+  .byte  65,15,88,199                        // addps         %xmm15,%xmm0
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  68,15,92,224                        // subps         %xmm0,%xmm12
+  .byte  69,15,88,204                        // addps         %xmm12,%xmm9
+  .byte  69,15,88,212                        // addps         %xmm12,%xmm10
+  .byte  68,15,88,225                        // addps         %xmm1,%xmm12
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  65,15,93,196                        // minps         %xmm12,%xmm0
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  15,93,200                           // minps         %xmm0,%xmm1
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  65,15,95,196                        // maxps         %xmm12,%xmm0
+  .byte  69,15,40,249                        // movaps        %xmm9,%xmm15
+  .byte  68,15,95,248                        // maxps         %xmm0,%xmm15
+  .byte  69,15,89,233                        // mulps         %xmm9,%xmm13
+  .byte  69,15,89,242                        // mulps         %xmm10,%xmm14
+  .byte  69,15,88,245                        // addps         %xmm13,%xmm14
+  .byte  69,15,89,220                        // mulps         %xmm12,%xmm11
+  .byte  69,15,88,222                        // addps         %xmm14,%xmm11
+  .byte  69,15,87,237                        // xorps         %xmm13,%xmm13
+  .byte  68,15,194,233,2                     // cmpleps       %xmm1,%xmm13
+  .byte  69,15,40,243                        // movaps        %xmm11,%xmm14
+  .byte  68,15,92,241                        // subps         %xmm1,%xmm14
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  65,15,94,206                        // divps         %xmm14,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  65,15,40,197                        // movaps        %xmm13,%xmm0
+  .byte  15,85,193                           // andnps        %xmm1,%xmm0
+  .byte  69,15,84,205                        // andps         %xmm13,%xmm9
+  .byte  68,15,86,200                        // orps          %xmm0,%xmm9
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  15,41,84,36,200                     // movaps        %xmm2,-0x38(%rsp)
+  .byte  15,89,194                           // mulps         %xmm2,%xmm0
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  15,92,216                           // subps         %xmm0,%xmm3
+  .byte  15,41,92,36,184                     // movaps        %xmm3,-0x48(%rsp)
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  65,15,194,199,1                     // cmpltps       %xmm15,%xmm0
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  65,15,85,201                        // andnps        %xmm9,%xmm1
+  .byte  69,15,92,203                        // subps         %xmm11,%xmm9
+  .byte  65,15,92,219                        // subps         %xmm11,%xmm3
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  69,15,92,251                        // subps         %xmm11,%xmm15
+  .byte  69,15,94,207                        // divps         %xmm15,%xmm9
+  .byte  69,15,88,203                        // addps         %xmm11,%xmm9
+  .byte  68,15,84,200                        // andps         %xmm0,%xmm9
+  .byte  68,15,86,201                        // orps          %xmm1,%xmm9
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  65,15,94,206                        // divps         %xmm14,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  65,15,40,213                        // movaps        %xmm13,%xmm2
+  .byte  15,85,209                           // andnps        %xmm1,%xmm2
+  .byte  69,15,84,213                        // andps         %xmm13,%xmm10
+  .byte  68,15,86,210                        // orps          %xmm2,%xmm10
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  65,15,85,202                        // andnps        %xmm10,%xmm1
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  69,15,94,215                        // divps         %xmm15,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  68,15,84,208                        // andps         %xmm0,%xmm10
+  .byte  68,15,86,209                        // orps          %xmm1,%xmm10
+  .byte  65,15,40,204                        // movaps        %xmm12,%xmm1
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  65,15,94,206                        // divps         %xmm14,%xmm1
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  69,15,84,229                        // andps         %xmm13,%xmm12
+  .byte  68,15,85,233                        // andnps        %xmm1,%xmm13
+  .byte  69,15,86,236                        // orps          %xmm12,%xmm13
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  65,15,85,205                        // andnps        %xmm13,%xmm1
+  .byte  69,15,92,235                        // subps         %xmm11,%xmm13
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,94,239                        // divps         %xmm15,%xmm13
+  .byte  69,15,88,235                        // addps         %xmm11,%xmm13
+  .byte  68,15,84,232                        // andps         %xmm0,%xmm13
+  .byte  68,15,86,233                        // orps          %xmm1,%xmm13
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  68,15,95,200                        // maxps         %xmm0,%xmm9
+  .byte  68,15,95,208                        // maxps         %xmm0,%xmm10
+  .byte  68,15,95,232                        // maxps         %xmm0,%xmm13
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,89,198                           // mulps         %xmm6,%xmm0
+  .byte  15,88,248                           // addps         %xmm0,%xmm7
+  .byte  65,15,88,249                        // addps         %xmm9,%xmm7
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,40,84,36,232                     // movaps        -0x18(%rsp),%xmm2
+  .byte  15,89,194                           // mulps         %xmm2,%xmm0
+  .byte  15,88,232                           // addps         %xmm0,%xmm5
+  .byte  65,15,88,234                        // addps         %xmm10,%xmm5
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,68,36,216                     // movaps        -0x28(%rsp),%xmm0
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  68,15,88,196                        // addps         %xmm4,%xmm8
+  .byte  69,15,88,197                        // addps         %xmm13,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,230                           // movaps        %xmm6,%xmm4
+  .byte  15,40,234                           // movaps        %xmm2,%xmm5
+  .byte  15,40,240                           // movaps        %xmm0,%xmm6
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,40,92,36,184                     // movaps        -0x48(%rsp),%xmm3
+  .byte  15,40,124,36,200                    // movaps        -0x38(%rsp),%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminosity_sse2
+.globl _sk_luminosity_sse2
+FUNCTION(_sk_luminosity_sse2)
+_sk_luminosity_sse2:
+  .byte  68,15,40,215                        // movaps        %xmm7,%xmm10
+  .byte  15,41,116,36,200                    // movaps        %xmm6,-0x38(%rsp)
+  .byte  15,40,245                           // movaps        %xmm5,%xmm6
+  .byte  15,41,116,36,232                    // movaps        %xmm6,-0x18(%rsp)
+  .byte  15,41,100,36,216                    // movaps        %xmm4,-0x28(%rsp)
+  .byte  15,40,235                           // movaps        %xmm3,%xmm5
+  .byte  15,40,248                           // movaps        %xmm0,%xmm7
+  .byte  68,15,40,205                        // movaps        %xmm5,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  15,89,222                           // mulps         %xmm6,%xmm3
+  .byte  68,15,40,37,89,66,0,0               // movaps        0x4259(%rip),%xmm12        # 5890 <_sk_callback_sse2+0x35c>
+  .byte  68,15,40,199                        // movaps        %xmm7,%xmm8
+  .byte  69,15,89,196                        // mulps         %xmm12,%xmm8
+  .byte  68,15,40,45,89,66,0,0               // movaps        0x4259(%rip),%xmm13        # 58a0 <_sk_callback_sse2+0x36c>
+  .byte  68,15,40,241                        // movaps        %xmm1,%xmm14
+  .byte  69,15,89,245                        // mulps         %xmm13,%xmm14
+  .byte  69,15,88,240                        // addps         %xmm8,%xmm14
+  .byte  68,15,40,29,85,66,0,0               // movaps        0x4255(%rip),%xmm11        # 58b0 <_sk_callback_sse2+0x37c>
+  .byte  68,15,40,5,93,66,0,0                // movaps        0x425d(%rip),%xmm8        # 58c0 <_sk_callback_sse2+0x38c>
+  .byte  69,15,40,248                        // movaps        %xmm8,%xmm15
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  68,15,92,248                        // subps         %xmm0,%xmm15
+  .byte  65,15,89,255                        // mulps         %xmm15,%xmm7
+  .byte  65,15,89,207                        // mulps         %xmm15,%xmm1
+  .byte  15,41,76,36,184                     // movaps        %xmm1,-0x48(%rsp)
+  .byte  68,15,89,250                        // mulps         %xmm2,%xmm15
+  .byte  65,15,89,211                        // mulps         %xmm11,%xmm2
+  .byte  65,15,88,214                        // addps         %xmm14,%xmm2
+  .byte  69,15,40,241                        // movaps        %xmm9,%xmm14
+  .byte  69,15,89,244                        // mulps         %xmm12,%xmm14
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  69,15,89,213                        // mulps         %xmm13,%xmm10
+  .byte  69,15,88,214                        // addps         %xmm14,%xmm10
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,40,116,36,200                    // movaps        -0x38(%rsp),%xmm6
+  .byte  15,89,230                           // mulps         %xmm6,%xmm4
+  .byte  68,15,40,244                        // movaps        %xmm4,%xmm14
+  .byte  69,15,89,243                        // mulps         %xmm11,%xmm14
+  .byte  69,15,88,242                        // addps         %xmm10,%xmm14
+  .byte  15,89,208                           // mulps         %xmm0,%xmm2
+  .byte  65,15,92,214                        // subps         %xmm14,%xmm2
+  .byte  68,15,88,202                        // addps         %xmm2,%xmm9
+  .byte  15,88,218                           // addps         %xmm2,%xmm3
+  .byte  15,88,212                           // addps         %xmm4,%xmm2
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,93,210                        // minps         %xmm2,%xmm10
+  .byte  65,15,40,225                        // movaps        %xmm9,%xmm4
+  .byte  65,15,93,226                        // minps         %xmm10,%xmm4
+  .byte  68,15,40,211                        // movaps        %xmm3,%xmm10
+  .byte  68,15,95,210                        // maxps         %xmm2,%xmm10
+  .byte  69,15,40,241                        // movaps        %xmm9,%xmm14
+  .byte  69,15,95,242                        // maxps         %xmm10,%xmm14
+  .byte  69,15,89,225                        // mulps         %xmm9,%xmm12
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,236                        // addps         %xmm12,%xmm13
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,221                        // addps         %xmm13,%xmm11
+  .byte  69,15,87,228                        // xorps         %xmm12,%xmm12
+  .byte  68,15,194,228,2                     // cmpleps       %xmm4,%xmm12
+  .byte  69,15,40,211                        // movaps        %xmm11,%xmm10
+  .byte  68,15,92,212                        // subps         %xmm4,%xmm10
+  .byte  65,15,40,225                        // movaps        %xmm9,%xmm4
+  .byte  65,15,92,227                        // subps         %xmm11,%xmm4
+  .byte  65,15,89,227                        // mulps         %xmm11,%xmm4
+  .byte  65,15,94,226                        // divps         %xmm10,%xmm4
+  .byte  65,15,88,227                        // addps         %xmm11,%xmm4
+  .byte  69,15,40,236                        // movaps        %xmm12,%xmm13
+  .byte  68,15,85,236                        // andnps        %xmm4,%xmm13
+  .byte  69,15,84,204                        // andps         %xmm12,%xmm9
+  .byte  69,15,86,205                        // orps          %xmm13,%xmm9
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,41,68,36,168                     // movaps        %xmm0,-0x58(%rsp)
+  .byte  15,89,224                           // mulps         %xmm0,%xmm4
+  .byte  68,15,92,197                        // subps         %xmm5,%xmm8
+  .byte  15,88,232                           // addps         %xmm0,%xmm5
+  .byte  15,92,236                           // subps         %xmm4,%xmm5
+  .byte  68,15,40,236                        // movaps        %xmm4,%xmm13
+  .byte  65,15,194,230,1                     // cmpltps       %xmm14,%xmm4
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  65,15,85,193                        // andnps        %xmm9,%xmm0
+  .byte  69,15,92,203                        // subps         %xmm11,%xmm9
+  .byte  69,15,92,235                        // subps         %xmm11,%xmm13
+  .byte  69,15,89,205                        // mulps         %xmm13,%xmm9
+  .byte  69,15,92,243                        // subps         %xmm11,%xmm14
+  .byte  69,15,94,206                        // divps         %xmm14,%xmm9
+  .byte  69,15,88,203                        // addps         %xmm11,%xmm9
+  .byte  68,15,84,204                        // andps         %xmm4,%xmm9
+  .byte  68,15,86,200                        // orps          %xmm0,%xmm9
+  .byte  15,40,195                           // movaps        %xmm3,%xmm0
+  .byte  65,15,92,195                        // subps         %xmm11,%xmm0
+  .byte  65,15,89,195                        // mulps         %xmm11,%xmm0
+  .byte  65,15,94,194                        // divps         %xmm10,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  65,15,40,204                        // movaps        %xmm12,%xmm1
+  .byte  15,85,200                           // andnps        %xmm0,%xmm1
+  .byte  65,15,84,220                        // andps         %xmm12,%xmm3
+  .byte  15,86,217                           // orps          %xmm1,%xmm3
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,85,195                           // andnps        %xmm3,%xmm0
+  .byte  65,15,92,219                        // subps         %xmm11,%xmm3
+  .byte  65,15,89,221                        // mulps         %xmm13,%xmm3
+  .byte  65,15,94,222                        // divps         %xmm14,%xmm3
+  .byte  65,15,88,219                        // addps         %xmm11,%xmm3
+  .byte  15,84,220                           // andps         %xmm4,%xmm3
+  .byte  15,86,216                           // orps          %xmm0,%xmm3
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  65,15,92,195                        // subps         %xmm11,%xmm0
+  .byte  65,15,89,195                        // mulps         %xmm11,%xmm0
+  .byte  65,15,94,194                        // divps         %xmm10,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  65,15,84,212                        // andps         %xmm12,%xmm2
+  .byte  68,15,85,224                        // andnps        %xmm0,%xmm12
+  .byte  68,15,86,226                        // orps          %xmm2,%xmm12
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  65,15,85,196                        // andnps        %xmm12,%xmm0
+  .byte  69,15,92,227                        // subps         %xmm11,%xmm12
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,94,230                        // divps         %xmm14,%xmm12
+  .byte  69,15,88,227                        // addps         %xmm11,%xmm12
+  .byte  68,15,84,228                        // andps         %xmm4,%xmm12
+  .byte  68,15,86,224                        // orps          %xmm0,%xmm12
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  68,15,95,200                        // maxps         %xmm0,%xmm9
+  .byte  15,95,216                           // maxps         %xmm0,%xmm3
+  .byte  68,15,95,224                        // maxps         %xmm0,%xmm12
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,40,100,36,216                    // movaps        -0x28(%rsp),%xmm4
+  .byte  15,89,196                           // mulps         %xmm4,%xmm0
+  .byte  15,88,248                           // addps         %xmm0,%xmm7
+  .byte  65,15,88,249                        // addps         %xmm9,%xmm7
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  68,15,40,84,36,232                  // movaps        -0x18(%rsp),%xmm10
+  .byte  65,15,89,194                        // mulps         %xmm10,%xmm0
+  .byte  15,40,76,36,184                     // movaps        -0x48(%rsp),%xmm1
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  15,88,203                           // addps         %xmm3,%xmm1
+  .byte  68,15,89,198                        // mulps         %xmm6,%xmm8
+  .byte  69,15,88,199                        // addps         %xmm15,%xmm8
+  .byte  69,15,88,196                        // addps         %xmm12,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,199                           // movaps        %xmm7,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,40,221                           // movaps        %xmm5,%xmm3
+  .byte  65,15,40,234                        // movaps        %xmm10,%xmm5
+  .byte  15,40,124,36,168                    // movaps        -0x58(%rsp),%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_rgba_8888_sse2
+.globl _sk_srcover_rgba_8888_sse2
+FUNCTION(_sk_srcover_rgba_8888_sse2)
+_sk_srcover_rgba_8888_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,227,0,0,0                    // jne           193b <_sk_srcover_rgba_8888_sse2+0xf1>
+  .byte  243,68,15,111,4,144                 // movdqu        (%rax,%rdx,4),%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  102,15,111,53,103,64,0,0            // movdqa        0x4067(%rip),%xmm6        # 58d0 <_sk_callback_sse2+0x39c>
+  .byte  102,65,15,111,224                   // movdqa        %xmm8,%xmm4
+  .byte  102,15,219,230                      // pand          %xmm6,%xmm4
+  .byte  15,91,228                           // cvtdq2ps      %xmm4,%xmm4
+  .byte  102,65,15,111,232                   // movdqa        %xmm8,%xmm5
+  .byte  102,15,114,213,8                    // psrld         $0x8,%xmm5
+  .byte  102,15,219,238                      // pand          %xmm6,%xmm5
+  .byte  15,91,237                           // cvtdq2ps      %xmm5,%xmm5
+  .byte  102,65,15,111,248                   // movdqa        %xmm8,%xmm7
+  .byte  102,15,114,215,16                   // psrld         $0x10,%xmm7
+  .byte  102,15,219,254                      // pand          %xmm6,%xmm7
+  .byte  15,91,247                           // cvtdq2ps      %xmm7,%xmm6
+  .byte  102,65,15,114,208,24                // psrld         $0x18,%xmm8
+  .byte  65,15,91,248                        // cvtdq2ps      %xmm8,%xmm7
+  .byte  68,15,40,5,55,64,0,0                // movaps        0x4037(%rip),%xmm8        # 58e0 <_sk_callback_sse2+0x3ac>
+  .byte  68,15,92,195                        // subps         %xmm3,%xmm8
+  .byte  68,15,40,37,59,64,0,0               // movaps        0x403b(%rip),%xmm12        # 58f0 <_sk_callback_sse2+0x3bc>
+  .byte  65,15,89,196                        // mulps         %xmm12,%xmm0
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  68,15,89,204                        // mulps         %xmm4,%xmm9
+  .byte  68,15,88,200                        // addps         %xmm0,%xmm9
+  .byte  65,15,89,204                        // mulps         %xmm12,%xmm1
+  .byte  69,15,40,208                        // movaps        %xmm8,%xmm10
+  .byte  68,15,89,213                        // mulps         %xmm5,%xmm10
+  .byte  68,15,88,209                        // addps         %xmm1,%xmm10
+  .byte  65,15,89,212                        // mulps         %xmm12,%xmm2
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  68,15,89,222                        // mulps         %xmm6,%xmm11
+  .byte  68,15,88,218                        // addps         %xmm2,%xmm11
+  .byte  65,15,89,220                        // mulps         %xmm12,%xmm3
+  .byte  68,15,89,199                        // mulps         %xmm7,%xmm8
+  .byte  68,15,88,195                        // addps         %xmm3,%xmm8
+  .byte  102,65,15,91,193                    // cvtps2dq      %xmm9,%xmm0
+  .byte  102,65,15,91,202                    // cvtps2dq      %xmm10,%xmm1
+  .byte  102,15,114,241,8                    // pslld         $0x8,%xmm1
+  .byte  102,15,235,200                      // por           %xmm0,%xmm1
+  .byte  102,65,15,91,211                    // cvtps2dq      %xmm11,%xmm2
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,65,15,91,192                    // cvtps2dq      %xmm8,%xmm0
+  .byte  102,15,114,240,24                   // pslld         $0x18,%xmm0
+  .byte  102,15,235,194                      // por           %xmm2,%xmm0
+  .byte  102,15,235,193                      // por           %xmm1,%xmm0
+  .byte  117,106                             // jne           198c <_sk_srcover_rgba_8888_sse2+0x142>
+  .byte  243,15,127,4,144                    // movdqu        %xmm0,(%rax,%rdx,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  65,15,40,211                        // movaps        %xmm11,%xmm2
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,48                              // je            197d <_sk_srcover_rgba_8888_sse2+0x133>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,22                              // je            1969 <_sk_srcover_rgba_8888_sse2+0x11f>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  15,133,1,255,255,255                // jne           185e <_sk_srcover_rgba_8888_sse2+0x14>
+  .byte  102,15,110,100,144,8                // movd          0x8(%rax,%rdx,4),%xmm4
+  .byte  102,68,15,112,196,69                // pshufd        $0x45,%xmm4,%xmm8
+  .byte  243,15,16,100,144,4                 // movss         0x4(%rax,%rdx,4),%xmm4
+  .byte  65,15,198,224,0                     // shufps        $0x0,%xmm8,%xmm4
+  .byte  65,15,198,224,226                   // shufps        $0xe2,%xmm8,%xmm4
+  .byte  68,15,40,196                        // movaps        %xmm4,%xmm8
+  .byte  243,15,16,36,144                    // movss         (%rax,%rdx,4),%xmm4
+  .byte  243,68,15,16,196                    // movss         %xmm4,%xmm8
+  .byte  233,210,254,255,255                 // jmpq          185e <_sk_srcover_rgba_8888_sse2+0x14>
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,34                              // je            19bb <_sk_srcover_rgba_8888_sse2+0x171>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,17                              // je            19b0 <_sk_srcover_rgba_8888_sse2+0x166>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,130                             // jne           1927 <_sk_srcover_rgba_8888_sse2+0xdd>
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,15,126,76,144,8                 // movd          %xmm1,0x8(%rax,%rdx,4)
+  .byte  102,15,112,200,229                  // pshufd        $0xe5,%xmm0,%xmm1
+  .byte  102,15,126,76,144,4                 // movd          %xmm1,0x4(%rax,%rdx,4)
+  .byte  102,15,126,4,144                    // movd          %xmm0,(%rax,%rdx,4)
+  .byte  233,98,255,255,255                  // jmpq          1927 <_sk_srcover_rgba_8888_sse2+0xdd>
+
+HIDDEN _sk_clamp_0_sse2
+.globl _sk_clamp_0_sse2
+FUNCTION(_sk_clamp_0_sse2)
+_sk_clamp_0_sse2:
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  65,15,95,192                        // maxps         %xmm8,%xmm0
+  .byte  65,15,95,200                        // maxps         %xmm8,%xmm1
+  .byte  65,15,95,208                        // maxps         %xmm8,%xmm2
+  .byte  65,15,95,216                        // maxps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_1_sse2
+.globl _sk_clamp_1_sse2
+FUNCTION(_sk_clamp_1_sse2)
+_sk_clamp_1_sse2:
+  .byte  68,15,40,5,27,63,0,0                // movaps        0x3f1b(%rip),%xmm8        # 5900 <_sk_callback_sse2+0x3cc>
+  .byte  65,15,93,192                        // minps         %xmm8,%xmm0
+  .byte  65,15,93,200                        // minps         %xmm8,%xmm1
+  .byte  65,15,93,208                        // minps         %xmm8,%xmm2
+  .byte  65,15,93,216                        // minps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_a_sse2
+.globl _sk_clamp_a_sse2
+FUNCTION(_sk_clamp_a_sse2)
+_sk_clamp_a_sse2:
+  .byte  15,93,29,16,63,0,0                  // minps         0x3f10(%rip),%xmm3        # 5910 <_sk_callback_sse2+0x3dc>
+  .byte  15,93,195                           // minps         %xmm3,%xmm0
+  .byte  15,93,203                           // minps         %xmm3,%xmm1
+  .byte  15,93,211                           // minps         %xmm3,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_set_rgb_sse2
+.globl _sk_set_rgb_sse2
+FUNCTION(_sk_set_rgb_sse2)
+_sk_set_rgb_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_rb_sse2
+.globl _sk_swap_rb_sse2
+FUNCTION(_sk_swap_rb_sse2)
+_sk_swap_rb_sse2:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_sse2
+.globl _sk_swap_sse2
+FUNCTION(_sk_swap_sse2)
+_sk_swap_sse2:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  68,15,40,216                        // movaps        %xmm0,%xmm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  65,15,40,227                        // movaps        %xmm11,%xmm4
+  .byte  65,15,40,234                        // movaps        %xmm10,%xmm5
+  .byte  65,15,40,241                        // movaps        %xmm9,%xmm6
+  .byte  65,15,40,248                        // movaps        %xmm8,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_src_dst_sse2
+.globl _sk_move_src_dst_sse2
+FUNCTION(_sk_move_src_dst_sse2)
+_sk_move_src_dst_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,224                           // movaps        %xmm0,%xmm4
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  15,40,242                           // movaps        %xmm2,%xmm6
+  .byte  15,40,251                           // movaps        %xmm3,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_dst_src_sse2
+.globl _sk_move_dst_src_sse2
+FUNCTION(_sk_move_dst_src_sse2)
+_sk_move_dst_src_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_premul_sse2
+.globl _sk_premul_sse2
+FUNCTION(_sk_premul_sse2)
+_sk_premul_sse2:
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,89,203                           // mulps         %xmm3,%xmm1
+  .byte  15,89,211                           // mulps         %xmm3,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_unpremul_sse2
+.globl _sk_unpremul_sse2
+FUNCTION(_sk_unpremul_sse2)
+_sk_unpremul_sse2:
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,40,13,123,62,0,0              // movaps        0x3e7b(%rip),%xmm9        # 5920 <_sk_callback_sse2+0x3ec>
+  .byte  68,15,94,203                        // divps         %xmm3,%xmm9
+  .byte  68,15,194,195,4                     // cmpneqps      %xmm3,%xmm8
+  .byte  69,15,84,193                        // andps         %xmm9,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_from_srgb_sse2
+.globl _sk_from_srgb_sse2
+FUNCTION(_sk_from_srgb_sse2)
+_sk_from_srgb_sse2:
+  .byte  68,15,40,5,102,62,0,0               // movaps        0x3e66(%rip),%xmm8        # 5930 <_sk_callback_sse2+0x3fc>
+  .byte  68,15,40,232                        // movaps        %xmm0,%xmm13
+  .byte  69,15,89,232                        // mulps         %xmm8,%xmm13
+  .byte  68,15,40,216                        // movaps        %xmm0,%xmm11
+  .byte  69,15,89,219                        // mulps         %xmm11,%xmm11
+  .byte  68,15,40,13,94,62,0,0               // movaps        0x3e5e(%rip),%xmm9        # 5940 <_sk_callback_sse2+0x40c>
+  .byte  68,15,40,240                        // movaps        %xmm0,%xmm14
+  .byte  69,15,89,241                        // mulps         %xmm9,%xmm14
+  .byte  68,15,40,21,94,62,0,0               // movaps        0x3e5e(%rip),%xmm10        # 5950 <_sk_callback_sse2+0x41c>
+  .byte  69,15,88,242                        // addps         %xmm10,%xmm14
+  .byte  69,15,89,243                        // mulps         %xmm11,%xmm14
+  .byte  68,15,40,29,94,62,0,0               // movaps        0x3e5e(%rip),%xmm11        # 5960 <_sk_callback_sse2+0x42c>
+  .byte  69,15,88,243                        // addps         %xmm11,%xmm14
+  .byte  68,15,40,37,98,62,0,0               // movaps        0x3e62(%rip),%xmm12        # 5970 <_sk_callback_sse2+0x43c>
+  .byte  65,15,194,196,1                     // cmpltps       %xmm12,%xmm0
+  .byte  68,15,84,232                        // andps         %xmm0,%xmm13
+  .byte  65,15,85,198                        // andnps        %xmm14,%xmm0
+  .byte  65,15,86,197                        // orps          %xmm13,%xmm0
+  .byte  68,15,40,233                        // movaps        %xmm1,%xmm13
+  .byte  69,15,89,232                        // mulps         %xmm8,%xmm13
+  .byte  68,15,40,241                        // movaps        %xmm1,%xmm14
+  .byte  69,15,89,246                        // mulps         %xmm14,%xmm14
+  .byte  68,15,40,249                        // movaps        %xmm1,%xmm15
+  .byte  69,15,89,249                        // mulps         %xmm9,%xmm15
+  .byte  69,15,88,250                        // addps         %xmm10,%xmm15
+  .byte  69,15,89,254                        // mulps         %xmm14,%xmm15
+  .byte  69,15,88,251                        // addps         %xmm11,%xmm15
+  .byte  65,15,194,204,1                     // cmpltps       %xmm12,%xmm1
+  .byte  68,15,84,233                        // andps         %xmm1,%xmm13
+  .byte  65,15,85,207                        // andnps        %xmm15,%xmm1
+  .byte  65,15,86,205                        // orps          %xmm13,%xmm1
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  68,15,40,234                        // movaps        %xmm2,%xmm13
+  .byte  69,15,89,237                        // mulps         %xmm13,%xmm13
+  .byte  68,15,89,202                        // mulps         %xmm2,%xmm9
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  69,15,89,205                        // mulps         %xmm13,%xmm9
+  .byte  69,15,88,203                        // addps         %xmm11,%xmm9
+  .byte  65,15,194,212,1                     // cmpltps       %xmm12,%xmm2
+  .byte  68,15,84,194                        // andps         %xmm2,%xmm8
+  .byte  65,15,85,209                        // andnps        %xmm9,%xmm2
+  .byte  65,15,86,208                        // orps          %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_to_srgb_sse2
+.globl _sk_to_srgb_sse2
+FUNCTION(_sk_to_srgb_sse2)
+_sk_to_srgb_sse2:
+  .byte  68,15,82,232                        // rsqrtps       %xmm0,%xmm13
+  .byte  68,15,40,5,239,61,0,0               // movaps        0x3def(%rip),%xmm8        # 5980 <_sk_callback_sse2+0x44c>
+  .byte  68,15,40,240                        // movaps        %xmm0,%xmm14
+  .byte  69,15,89,240                        // mulps         %xmm8,%xmm14
+  .byte  68,15,40,13,239,61,0,0              // movaps        0x3def(%rip),%xmm9        # 5990 <_sk_callback_sse2+0x45c>
+  .byte  69,15,40,253                        // movaps        %xmm13,%xmm15
+  .byte  69,15,89,249                        // mulps         %xmm9,%xmm15
+  .byte  68,15,40,21,239,61,0,0              // movaps        0x3def(%rip),%xmm10        # 59a0 <_sk_callback_sse2+0x46c>
+  .byte  69,15,88,250                        // addps         %xmm10,%xmm15
+  .byte  69,15,89,253                        // mulps         %xmm13,%xmm15
+  .byte  68,15,40,29,239,61,0,0              // movaps        0x3def(%rip),%xmm11        # 59b0 <_sk_callback_sse2+0x47c>
+  .byte  69,15,88,251                        // addps         %xmm11,%xmm15
+  .byte  68,15,40,37,243,61,0,0              // movaps        0x3df3(%rip),%xmm12        # 59c0 <_sk_callback_sse2+0x48c>
+  .byte  69,15,88,236                        // addps         %xmm12,%xmm13
+  .byte  69,15,83,237                        // rcpps         %xmm13,%xmm13
+  .byte  69,15,89,239                        // mulps         %xmm15,%xmm13
+  .byte  68,15,40,61,239,61,0,0              // movaps        0x3def(%rip),%xmm15        # 59d0 <_sk_callback_sse2+0x49c>
+  .byte  65,15,194,199,1                     // cmpltps       %xmm15,%xmm0
+  .byte  68,15,84,240                        // andps         %xmm0,%xmm14
+  .byte  65,15,85,197                        // andnps        %xmm13,%xmm0
+  .byte  65,15,86,198                        // orps          %xmm14,%xmm0
+  .byte  68,15,82,233                        // rsqrtps       %xmm1,%xmm13
+  .byte  69,15,40,245                        // movaps        %xmm13,%xmm14
+  .byte  69,15,89,241                        // mulps         %xmm9,%xmm14
+  .byte  69,15,88,242                        // addps         %xmm10,%xmm14
+  .byte  69,15,89,245                        // mulps         %xmm13,%xmm14
+  .byte  69,15,88,243                        // addps         %xmm11,%xmm14
+  .byte  69,15,88,236                        // addps         %xmm12,%xmm13
+  .byte  69,15,83,237                        // rcpps         %xmm13,%xmm13
+  .byte  69,15,89,238                        // mulps         %xmm14,%xmm13
+  .byte  68,15,40,241                        // movaps        %xmm1,%xmm14
+  .byte  69,15,89,240                        // mulps         %xmm8,%xmm14
+  .byte  65,15,194,207,1                     // cmpltps       %xmm15,%xmm1
+  .byte  68,15,84,241                        // andps         %xmm1,%xmm14
+  .byte  65,15,85,205                        // andnps        %xmm13,%xmm1
+  .byte  65,15,86,206                        // orps          %xmm14,%xmm1
+  .byte  68,15,82,234                        // rsqrtps       %xmm2,%xmm13
+  .byte  69,15,89,205                        // mulps         %xmm13,%xmm9
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  69,15,89,205                        // mulps         %xmm13,%xmm9
+  .byte  69,15,88,203                        // addps         %xmm11,%xmm9
+  .byte  69,15,88,236                        // addps         %xmm12,%xmm13
+  .byte  69,15,83,213                        // rcpps         %xmm13,%xmm10
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  65,15,194,215,1                     // cmpltps       %xmm15,%xmm2
+  .byte  68,15,84,194                        // andps         %xmm2,%xmm8
+  .byte  65,15,85,210                        // andnps        %xmm10,%xmm2
+  .byte  65,15,86,208                        // orps          %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_rgb_to_hsl_sse2
+.globl _sk_rgb_to_hsl_sse2
+FUNCTION(_sk_rgb_to_hsl_sse2)
+_sk_rgb_to_hsl_sse2:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,40,208                        // movaps        %xmm8,%xmm10
+  .byte  69,15,95,209                        // maxps         %xmm9,%xmm10
+  .byte  68,15,95,210                        // maxps         %xmm2,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  69,15,93,217                        // minps         %xmm9,%xmm11
+  .byte  68,15,93,218                        // minps         %xmm2,%xmm11
+  .byte  65,15,40,202                        // movaps        %xmm10,%xmm1
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  68,15,40,45,72,61,0,0               // movaps        0x3d48(%rip),%xmm13        # 59e0 <_sk_callback_sse2+0x4ac>
+  .byte  68,15,94,233                        // divps         %xmm1,%xmm13
+  .byte  65,15,40,194                        // movaps        %xmm10,%xmm0
+  .byte  65,15,194,192,0                     // cmpeqps       %xmm8,%xmm0
+  .byte  69,15,40,225                        // movaps        %xmm9,%xmm12
+  .byte  68,15,92,226                        // subps         %xmm2,%xmm12
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,40,241                        // movaps        %xmm9,%xmm14
+  .byte  68,15,194,242,1                     // cmpltps       %xmm2,%xmm14
+  .byte  68,15,84,53,46,61,0,0               // andps         0x3d2e(%rip),%xmm14        # 59f0 <_sk_callback_sse2+0x4bc>
+  .byte  69,15,88,244                        // addps         %xmm12,%xmm14
+  .byte  69,15,40,250                        // movaps        %xmm10,%xmm15
+  .byte  69,15,194,249,0                     // cmpeqps       %xmm9,%xmm15
+  .byte  65,15,92,208                        // subps         %xmm8,%xmm2
+  .byte  65,15,89,213                        // mulps         %xmm13,%xmm2
+  .byte  68,15,40,37,33,61,0,0               // movaps        0x3d21(%rip),%xmm12        # 5a00 <_sk_callback_sse2+0x4cc>
+  .byte  65,15,88,212                        // addps         %xmm12,%xmm2
+  .byte  69,15,92,193                        // subps         %xmm9,%xmm8
+  .byte  69,15,89,197                        // mulps         %xmm13,%xmm8
+  .byte  68,15,88,5,29,61,0,0                // addps         0x3d1d(%rip),%xmm8        # 5a10 <_sk_callback_sse2+0x4dc>
+  .byte  65,15,84,215                        // andps         %xmm15,%xmm2
+  .byte  69,15,85,248                        // andnps        %xmm8,%xmm15
+  .byte  68,15,86,250                        // orps          %xmm2,%xmm15
+  .byte  68,15,84,240                        // andps         %xmm0,%xmm14
+  .byte  65,15,85,199                        // andnps        %xmm15,%xmm0
+  .byte  65,15,86,198                        // orps          %xmm14,%xmm0
+  .byte  15,89,5,14,61,0,0                   // mulps         0x3d0e(%rip),%xmm0        # 5a20 <_sk_callback_sse2+0x4ec>
+  .byte  69,15,40,194                        // movaps        %xmm10,%xmm8
+  .byte  69,15,194,195,4                     // cmpneqps      %xmm11,%xmm8
+  .byte  65,15,84,192                        // andps         %xmm8,%xmm0
+  .byte  69,15,92,226                        // subps         %xmm10,%xmm12
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  68,15,40,13,1,61,0,0                // movaps        0x3d01(%rip),%xmm9        # 5a30 <_sk_callback_sse2+0x4fc>
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  68,15,194,202,1                     // cmpltps       %xmm2,%xmm9
+  .byte  69,15,92,227                        // subps         %xmm11,%xmm12
+  .byte  69,15,84,225                        // andps         %xmm9,%xmm12
+  .byte  69,15,85,202                        // andnps        %xmm10,%xmm9
+  .byte  69,15,86,204                        // orps          %xmm12,%xmm9
+  .byte  65,15,94,201                        // divps         %xmm9,%xmm1
+  .byte  65,15,84,200                        // andps         %xmm8,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_hsl_to_rgb_sse2
+.globl _sk_hsl_to_rgb_sse2
+FUNCTION(_sk_hsl_to_rgb_sse2)
+_sk_hsl_to_rgb_sse2:
+  .byte  15,41,124,36,232                    // movaps        %xmm7,-0x18(%rsp)
+  .byte  15,41,116,36,216                    // movaps        %xmm6,-0x28(%rsp)
+  .byte  15,41,108,36,200                    // movaps        %xmm5,-0x38(%rsp)
+  .byte  15,41,100,36,184                    // movaps        %xmm4,-0x48(%rsp)
+  .byte  15,41,92,36,168                     // movaps        %xmm3,-0x58(%rsp)
+  .byte  68,15,40,218                        // movaps        %xmm2,%xmm11
+  .byte  15,40,240                           // movaps        %xmm0,%xmm6
+  .byte  68,15,40,13,192,60,0,0              // movaps        0x3cc0(%rip),%xmm9        # 5a40 <_sk_callback_sse2+0x50c>
+  .byte  69,15,40,209                        // movaps        %xmm9,%xmm10
+  .byte  69,15,194,211,2                     // cmpleps       %xmm11,%xmm10
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,194,217,0                        // cmpeqps       %xmm1,%xmm3
+  .byte  15,40,251                           // movaps        %xmm3,%xmm7
+  .byte  15,41,124,36,136                    // movaps        %xmm7,-0x78(%rsp)
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  65,15,84,194                        // andps         %xmm10,%xmm0
+  .byte  68,15,85,209                        // andnps        %xmm1,%xmm10
+  .byte  68,15,86,208                        // orps          %xmm0,%xmm10
+  .byte  68,15,41,92,36,152                  // movaps        %xmm11,-0x68(%rsp)
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  69,15,88,219                        // addps         %xmm11,%xmm11
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  15,40,5,137,60,0,0                  // movaps        0x3c89(%rip),%xmm0        # 5a50 <_sk_callback_sse2+0x51c>
+  .byte  15,88,198                           // addps         %xmm6,%xmm0
+  .byte  243,15,91,200                       // cvttps2dq     %xmm0,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  15,194,217,1                        // cmpltps       %xmm1,%xmm3
+  .byte  15,84,29,129,60,0,0                 // andps         0x3c81(%rip),%xmm3        # 5a60 <_sk_callback_sse2+0x52c>
+  .byte  15,92,203                           // subps         %xmm3,%xmm1
+  .byte  15,92,193                           // subps         %xmm1,%xmm0
+  .byte  68,15,40,45,131,60,0,0              // movaps        0x3c83(%rip),%xmm13        # 5a70 <_sk_callback_sse2+0x53c>
+  .byte  69,15,40,197                        // movaps        %xmm13,%xmm8
+  .byte  68,15,194,192,2                     // cmpleps       %xmm0,%xmm8
+  .byte  69,15,40,242                        // movaps        %xmm10,%xmm14
+  .byte  69,15,92,243                        // subps         %xmm11,%xmm14
+  .byte  65,15,40,217                        // movaps        %xmm9,%xmm3
+  .byte  15,194,216,2                        // cmpleps       %xmm0,%xmm3
+  .byte  15,40,21,147,60,0,0                 // movaps        0x3c93(%rip),%xmm2        # 5aa0 <_sk_callback_sse2+0x56c>
+  .byte  68,15,40,250                        // movaps        %xmm2,%xmm15
+  .byte  68,15,194,248,2                     // cmpleps       %xmm0,%xmm15
+  .byte  15,40,13,99,60,0,0                  // movaps        0x3c63(%rip),%xmm1        # 5a80 <_sk_callback_sse2+0x54c>
+  .byte  15,89,193                           // mulps         %xmm1,%xmm0
+  .byte  15,40,45,105,60,0,0                 // movaps        0x3c69(%rip),%xmm5        # 5a90 <_sk_callback_sse2+0x55c>
+  .byte  15,40,229                           // movaps        %xmm5,%xmm4
+  .byte  15,92,224                           // subps         %xmm0,%xmm4
+  .byte  65,15,89,230                        // mulps         %xmm14,%xmm4
+  .byte  65,15,88,227                        // addps         %xmm11,%xmm4
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,84,224                        // andps         %xmm8,%xmm12
+  .byte  68,15,85,196                        // andnps        %xmm4,%xmm8
+  .byte  69,15,86,196                        // orps          %xmm12,%xmm8
+  .byte  68,15,84,195                        // andps         %xmm3,%xmm8
+  .byte  65,15,85,218                        // andnps        %xmm10,%xmm3
+  .byte  65,15,86,216                        // orps          %xmm8,%xmm3
+  .byte  65,15,89,198                        // mulps         %xmm14,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  65,15,84,223                        // andps         %xmm15,%xmm3
+  .byte  68,15,85,248                        // andnps        %xmm0,%xmm15
+  .byte  68,15,86,251                        // orps          %xmm3,%xmm15
+  .byte  68,15,40,199                        // movaps        %xmm7,%xmm8
+  .byte  69,15,85,199                        // andnps        %xmm15,%xmm8
+  .byte  243,15,91,198                       // cvttps2dq     %xmm6,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,40,222                           // movaps        %xmm6,%xmm3
+  .byte  15,194,216,1                        // cmpltps       %xmm0,%xmm3
+  .byte  15,84,29,222,59,0,0                 // andps         0x3bde(%rip),%xmm3        # 5a60 <_sk_callback_sse2+0x52c>
+  .byte  15,92,195                           // subps         %xmm3,%xmm0
+  .byte  68,15,40,230                        // movaps        %xmm6,%xmm12
+  .byte  68,15,92,224                        // subps         %xmm0,%xmm12
+  .byte  69,15,40,253                        // movaps        %xmm13,%xmm15
+  .byte  69,15,194,252,2                     // cmpleps       %xmm12,%xmm15
+  .byte  65,15,40,225                        // movaps        %xmm9,%xmm4
+  .byte  65,15,194,228,2                     // cmpleps       %xmm12,%xmm4
+  .byte  15,40,218                           // movaps        %xmm2,%xmm3
+  .byte  65,15,194,220,2                     // cmpleps       %xmm12,%xmm3
+  .byte  68,15,89,225                        // mulps         %xmm1,%xmm12
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  65,15,92,196                        // subps         %xmm12,%xmm0
+  .byte  65,15,89,198                        // mulps         %xmm14,%xmm0
+  .byte  65,15,88,195                        // addps         %xmm11,%xmm0
+  .byte  65,15,40,251                        // movaps        %xmm11,%xmm7
+  .byte  65,15,84,255                        // andps         %xmm15,%xmm7
+  .byte  68,15,85,248                        // andnps        %xmm0,%xmm15
+  .byte  68,15,86,255                        // orps          %xmm7,%xmm15
+  .byte  68,15,84,252                        // andps         %xmm4,%xmm15
+  .byte  65,15,85,226                        // andnps        %xmm10,%xmm4
+  .byte  65,15,86,231                        // orps          %xmm15,%xmm4
+  .byte  69,15,89,230                        // mulps         %xmm14,%xmm12
+  .byte  69,15,88,227                        // addps         %xmm11,%xmm12
+  .byte  15,84,227                           // andps         %xmm3,%xmm4
+  .byte  65,15,85,220                        // andnps        %xmm12,%xmm3
+  .byte  15,86,220                           // orps          %xmm4,%xmm3
+  .byte  15,40,124,36,136                    // movaps        -0x78(%rsp),%xmm7
+  .byte  15,40,231                           // movaps        %xmm7,%xmm4
+  .byte  15,85,227                           // andnps        %xmm3,%xmm4
+  .byte  15,88,53,182,59,0,0                 // addps         0x3bb6(%rip),%xmm6        # 5ab0 <_sk_callback_sse2+0x57c>
+  .byte  243,15,91,198                       // cvttps2dq     %xmm6,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,40,222                           // movaps        %xmm6,%xmm3
+  .byte  15,194,216,1                        // cmpltps       %xmm0,%xmm3
+  .byte  15,84,29,81,59,0,0                  // andps         0x3b51(%rip),%xmm3        # 5a60 <_sk_callback_sse2+0x52c>
+  .byte  15,92,195                           // subps         %xmm3,%xmm0
+  .byte  15,92,240                           // subps         %xmm0,%xmm6
+  .byte  15,89,206                           // mulps         %xmm6,%xmm1
+  .byte  15,92,233                           // subps         %xmm1,%xmm5
+  .byte  65,15,89,238                        // mulps         %xmm14,%xmm5
+  .byte  65,15,89,206                        // mulps         %xmm14,%xmm1
+  .byte  65,15,88,235                        // addps         %xmm11,%xmm5
+  .byte  65,15,88,203                        // addps         %xmm11,%xmm1
+  .byte  68,15,194,238,2                     // cmpleps       %xmm6,%xmm13
+  .byte  69,15,84,221                        // andps         %xmm13,%xmm11
+  .byte  68,15,85,237                        // andnps        %xmm5,%xmm13
+  .byte  69,15,86,235                        // orps          %xmm11,%xmm13
+  .byte  68,15,194,206,2                     // cmpleps       %xmm6,%xmm9
+  .byte  69,15,84,233                        // andps         %xmm9,%xmm13
+  .byte  69,15,85,202                        // andnps        %xmm10,%xmm9
+  .byte  69,15,86,205                        // orps          %xmm13,%xmm9
+  .byte  15,194,214,2                        // cmpleps       %xmm6,%xmm2
+  .byte  68,15,84,202                        // andps         %xmm2,%xmm9
+  .byte  15,85,209                           // andnps        %xmm1,%xmm2
+  .byte  65,15,86,209                        // orps          %xmm9,%xmm2
+  .byte  15,40,68,36,152                     // movaps        -0x68(%rsp),%xmm0
+  .byte  15,40,207                           // movaps        %xmm7,%xmm1
+  .byte  15,84,193                           // andps         %xmm1,%xmm0
+  .byte  15,85,202                           // andnps        %xmm2,%xmm1
+  .byte  68,15,86,192                        // orps          %xmm0,%xmm8
+  .byte  15,86,224                           // orps          %xmm0,%xmm4
+  .byte  15,86,193                           // orps          %xmm1,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  15,40,204                           // movaps        %xmm4,%xmm1
+  .byte  15,40,92,36,168                     // movaps        -0x58(%rsp),%xmm3
+  .byte  15,40,100,36,184                    // movaps        -0x48(%rsp),%xmm4
+  .byte  15,40,108,36,200                    // movaps        -0x38(%rsp),%xmm5
+  .byte  15,40,116,36,216                    // movaps        -0x28(%rsp),%xmm6
+  .byte  15,40,124,36,232                    // movaps        -0x18(%rsp),%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_1_float_sse2
+.globl _sk_scale_1_float_sse2
+FUNCTION(_sk_scale_1_float_sse2)
+_sk_scale_1_float_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_u8_sse2
+.globl _sk_scale_u8_sse2
+FUNCTION(_sk_scale_u8_sse2)
+_sk_scale_u8_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,61                              // jne           2002 <_sk_scale_u8_sse2+0x47>
+  .byte  102,69,15,110,4,18                  // movd          (%r10,%rdx,1),%xmm8
+  .byte  102,68,15,96,192                    // punpcklbw     %xmm0,%xmm8
+  .byte  102,68,15,97,192                    // punpcklwd     %xmm0,%xmm8
+  .byte  102,68,15,219,5,226,58,0,0          // pand          0x3ae2(%rip),%xmm8        # 5ac0 <_sk_callback_sse2+0x58c>
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,5,230,58,0,0               // mulps         0x3ae6(%rip),%xmm8        # 5ad0 <_sk_callback_sse2+0x59c>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,54                              // je            204a <_sk_scale_u8_sse2+0x8f>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,23                              // je            2031 <_sk_scale_u8_sse2+0x76>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,181                             // jne           1fd5 <_sk_scale_u8_sse2+0x1a>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,69                // pshufd        $0x45,%xmm8,%xmm8
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,68,15,110,200                   // movd          %eax,%xmm9
+  .byte  69,15,198,200,0                     // shufps        $0x0,%xmm8,%xmm9
+  .byte  69,15,198,200,226                   // shufps        $0xe2,%xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,68,15,110,200                   // movd          %eax,%xmm9
+  .byte  243,69,15,16,193                    // movss         %xmm9,%xmm8
+  .byte  233,119,255,255,255                 // jmpq          1fd5 <_sk_scale_u8_sse2+0x1a>
+
+HIDDEN _sk_lerp_1_float_sse2
+.globl _sk_lerp_1_float_sse2
+FUNCTION(_sk_lerp_1_float_sse2)
+_sk_lerp_1_float_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,92,205                           // subps         %xmm5,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,92,214                           // subps         %xmm6,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lerp_u8_sse2
+.globl _sk_lerp_u8_sse2
+FUNCTION(_sk_lerp_u8_sse2)
+_sk_lerp_u8_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,81                              // jne           20f1 <_sk_lerp_u8_sse2+0x5b>
+  .byte  102,69,15,110,4,18                  // movd          (%r10,%rdx,1),%xmm8
+  .byte  102,68,15,96,192                    // punpcklbw     %xmm0,%xmm8
+  .byte  102,68,15,97,192                    // punpcklwd     %xmm0,%xmm8
+  .byte  102,68,15,219,5,39,58,0,0           // pand          0x3a27(%rip),%xmm8        # 5ae0 <_sk_callback_sse2+0x5ac>
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,5,43,58,0,0                // mulps         0x3a2b(%rip),%xmm8        # 5af0 <_sk_callback_sse2+0x5bc>
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,92,205                           // subps         %xmm5,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,92,214                           // subps         %xmm6,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,54                              // je            2139 <_sk_lerp_u8_sse2+0xa3>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,23                              // je            2120 <_sk_lerp_u8_sse2+0x8a>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,161                             // jne           20b0 <_sk_lerp_u8_sse2+0x1a>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,69                // pshufd        $0x45,%xmm8,%xmm8
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,68,15,110,200                   // movd          %eax,%xmm9
+  .byte  69,15,198,200,0                     // shufps        $0x0,%xmm8,%xmm9
+  .byte  69,15,198,200,226                   // shufps        $0xe2,%xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,68,15,110,200                   // movd          %eax,%xmm9
+  .byte  243,69,15,16,193                    // movss         %xmm9,%xmm8
+  .byte  233,99,255,255,255                  // jmpq          20b0 <_sk_lerp_u8_sse2+0x1a>
+
+HIDDEN _sk_lerp_565_sse2
+.globl _sk_lerp_565_sse2
+FUNCTION(_sk_lerp_565_sse2)
+_sk_lerp_565_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,156,0,0,0                    // jne           21f7 <_sk_lerp_565_sse2+0xaa>
+  .byte  243,69,15,126,12,82                 // movq          (%r10,%rdx,2),%xmm9
+  .byte  102,68,15,97,200                    // punpcklwd     %xmm0,%xmm9
+  .byte  102,68,15,111,5,145,57,0,0          // movdqa        0x3991(%rip),%xmm8        # 5b00 <_sk_callback_sse2+0x5cc>
+  .byte  102,69,15,219,193                   // pand          %xmm9,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,5,144,57,0,0               // mulps         0x3990(%rip),%xmm8        # 5b10 <_sk_callback_sse2+0x5dc>
+  .byte  102,68,15,111,21,151,57,0,0         // movdqa        0x3997(%rip),%xmm10        # 5b20 <_sk_callback_sse2+0x5ec>
+  .byte  102,69,15,219,209                   // pand          %xmm9,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  68,15,89,21,150,57,0,0              // mulps         0x3996(%rip),%xmm10        # 5b30 <_sk_callback_sse2+0x5fc>
+  .byte  102,68,15,219,13,157,57,0,0         // pand          0x399d(%rip),%xmm9        # 5b40 <_sk_callback_sse2+0x60c>
+  .byte  69,15,91,201                        // cvtdq2ps      %xmm9,%xmm9
+  .byte  68,15,89,13,161,57,0,0              // mulps         0x39a1(%rip),%xmm9        # 5b50 <_sk_callback_sse2+0x61c>
+  .byte  15,92,196                           // subps         %xmm4,%xmm0
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,196                           // addps         %xmm4,%xmm0
+  .byte  15,92,205                           // subps         %xmm5,%xmm1
+  .byte  65,15,89,202                        // mulps         %xmm10,%xmm1
+  .byte  15,88,205                           // addps         %xmm5,%xmm1
+  .byte  15,92,214                           // subps         %xmm6,%xmm2
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,214                           // addps         %xmm6,%xmm2
+  .byte  15,92,223                           // subps         %xmm7,%xmm3
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  68,15,88,199                        // addps         %xmm7,%xmm8
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  68,15,88,215                        // addps         %xmm7,%xmm10
+  .byte  65,15,89,217                        // mulps         %xmm9,%xmm3
+  .byte  15,88,223                           // addps         %xmm7,%xmm3
+  .byte  68,15,95,211                        // maxps         %xmm3,%xmm10
+  .byte  69,15,95,194                        // maxps         %xmm10,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,216                        // movaps        %xmm8,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,58                              // je            2243 <_sk_lerp_565_sse2+0xf6>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,27                              // je            222a <_sk_lerp_565_sse2+0xdd>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  15,133,77,255,255,255               // jne           2166 <_sk_lerp_565_sse2+0x19>
+  .byte  65,15,183,68,82,4                   // movzwl        0x4(%r10,%rdx,2),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,200,69                // pshufd        $0x45,%xmm8,%xmm9
+  .byte  65,15,183,68,82,2                   // movzwl        0x2(%r10,%rdx,2),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  69,15,198,193,0                     // shufps        $0x0,%xmm9,%xmm8
+  .byte  69,15,198,193,226                   // shufps        $0xe2,%xmm9,%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  65,15,183,4,82                      // movzwl        (%r10,%rdx,2),%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  243,69,15,16,200                    // movss         %xmm8,%xmm9
+  .byte  233,15,255,255,255                  // jmpq          2166 <_sk_lerp_565_sse2+0x19>
+
+HIDDEN _sk_load_tables_sse2
+.globl _sk_load_tables_sse2
+FUNCTION(_sk_load_tables_sse2)
+_sk_load_tables_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,36,1,0,0                     // jne           2389 <_sk_load_tables_sse2+0x132>
+  .byte  243,69,15,111,12,145                // movdqu        (%r9,%rdx,4),%xmm9
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  102,68,15,111,5,231,56,0,0          // movdqa        0x38e7(%rip),%xmm8        # 5b60 <_sk_callback_sse2+0x62c>
+  .byte  102,65,15,111,193                   // movdqa        %xmm9,%xmm0
+  .byte  102,65,15,219,192                   // pand          %xmm8,%xmm0
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,73,15,126,201                   // movq          %xmm1,%r9
+  .byte  102,73,15,126,194                   // movq          %xmm0,%r10
+  .byte  69,15,182,218                       // movzbl        %r10b,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  69,15,182,241                       // movzbl        %r9b,%r14d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,66,15,16,12,19                  // movss         (%rbx,%r10,1),%xmm1
+  .byte  243,66,15,16,4,11                   // movss         (%rbx,%r9,1),%xmm0
+  .byte  15,20,200                           // unpcklps      %xmm0,%xmm1
+  .byte  243,66,15,16,4,155                  // movss         (%rbx,%r11,4),%xmm0
+  .byte  243,66,15,16,20,179                 // movss         (%rbx,%r14,4),%xmm2
+  .byte  15,20,194                           // unpcklps      %xmm2,%xmm0
+  .byte  15,20,193                           // unpcklps      %xmm1,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,15,114,209,8                    // psrld         $0x8,%xmm1
+  .byte  102,65,15,219,200                   // pand          %xmm8,%xmm1
+  .byte  102,15,112,209,78                   // pshufd        $0x4e,%xmm1,%xmm2
+  .byte  102,73,15,126,209                   // movq          %xmm2,%r9
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  68,15,182,211                       // movzbl        %bl,%r10d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  69,15,182,217                       // movzbl        %r9b,%r11d
+  .byte  73,193,233,30                       // shr           $0x1e,%r9
+  .byte  243,65,15,16,20,31                  // movss         (%r15,%rbx,1),%xmm2
+  .byte  243,67,15,16,12,15                  // movss         (%r15,%r9,1),%xmm1
+  .byte  15,20,209                           // unpcklps      %xmm1,%xmm2
+  .byte  243,67,15,16,12,151                 // movss         (%r15,%r10,4),%xmm1
+  .byte  243,67,15,16,28,159                 // movss         (%r15,%r11,4),%xmm3
+  .byte  15,20,203                           // unpcklps      %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  76,139,72,24                        // mov           0x18(%rax),%r9
+  .byte  102,65,15,111,209                   // movdqa        %xmm9,%xmm2
+  .byte  102,15,114,210,16                   // psrld         $0x10,%xmm2
+  .byte  102,65,15,219,208                   // pand          %xmm8,%xmm2
+  .byte  102,15,112,218,78                   // pshufd        $0x4e,%xmm2,%xmm3
+  .byte  102,72,15,126,219                   // movq          %xmm3,%rbx
+  .byte  102,72,15,126,208                   // movq          %xmm2,%rax
+  .byte  68,15,182,208                       // movzbl        %al,%r10d
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  68,15,182,219                       // movzbl        %bl,%r11d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  243,69,15,16,4,1                    // movss         (%r9,%rax,1),%xmm8
+  .byte  243,65,15,16,20,25                  // movss         (%r9,%rbx,1),%xmm2
+  .byte  68,15,20,194                        // unpcklps      %xmm2,%xmm8
+  .byte  243,67,15,16,20,145                 // movss         (%r9,%r10,4),%xmm2
+  .byte  243,67,15,16,28,153                 // movss         (%r9,%r11,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  65,15,20,208                        // unpcklps      %xmm8,%xmm2
+  .byte  102,65,15,114,209,24                // psrld         $0x18,%xmm9
+  .byte  65,15,91,217                        // cvtdq2ps      %xmm9,%xmm3
+  .byte  15,89,29,240,55,0,0                 // mulps         0x37f0(%rip),%xmm3        # 5b70 <_sk_callback_sse2+0x63c>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,194                          // mov           %r8d,%r10d
+  .byte  65,128,226,3                        // and           $0x3,%r10b
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  65,128,250,1                        // cmp           $0x1,%r10b
+  .byte  116,50                              // je            23cd <_sk_load_tables_sse2+0x176>
+  .byte  65,128,250,2                        // cmp           $0x2,%r10b
+  .byte  116,23                              // je            23b8 <_sk_load_tables_sse2+0x161>
+  .byte  65,128,250,3                        // cmp           $0x3,%r10b
+  .byte  15,133,192,254,255,255              // jne           226b <_sk_load_tables_sse2+0x14>
+  .byte  102,65,15,110,68,145,8              // movd          0x8(%r9,%rdx,4),%xmm0
+  .byte  102,68,15,112,200,69                // pshufd        $0x45,%xmm0,%xmm9
+  .byte  243,65,15,16,68,145,4               // movss         0x4(%r9,%rdx,4),%xmm0
+  .byte  65,15,198,193,0                     // shufps        $0x0,%xmm9,%xmm0
+  .byte  65,15,198,193,226                   // shufps        $0xe2,%xmm9,%xmm0
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  243,65,15,16,4,145                  // movss         (%r9,%rdx,4),%xmm0
+  .byte  243,68,15,16,200                    // movss         %xmm0,%xmm9
+  .byte  233,142,254,255,255                 // jmpq          226b <_sk_load_tables_sse2+0x14>
+
+HIDDEN _sk_load_tables_u16_be_sse2
+.globl _sk_load_tables_u16_be_sse2
+FUNCTION(_sk_load_tables_u16_be_sse2)
+_sk_load_tables_u16_be_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,149,0,0,0,0               // lea           0x0(,%rdx,4),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,101,1,0,0                    // jne           2558 <_sk_load_tables_u16_be_sse2+0x17b>
+  .byte  102,67,15,16,4,81                   // movupd        (%r9,%r10,2),%xmm0
+  .byte  102,67,15,16,76,81,16               // movupd        0x10(%r9,%r10,2),%xmm1
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  102,68,15,40,200                    // movapd        %xmm0,%xmm9
+  .byte  102,68,15,97,201                    // punpcklwd     %xmm1,%xmm9
+  .byte  102,15,105,193                      // punpckhwd     %xmm1,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,15,97,200                       // punpcklwd     %xmm0,%xmm1
+  .byte  102,68,15,105,200                   // punpckhwd     %xmm0,%xmm9
+  .byte  102,68,15,111,21,86,55,0,0          // movdqa        0x3756(%rip),%xmm10        # 5b80 <_sk_callback_sse2+0x64c>
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,65,15,219,194                   // pand          %xmm10,%xmm0
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  102,65,15,97,192                    // punpcklwd     %xmm8,%xmm0
+  .byte  102,15,112,216,78                   // pshufd        $0x4e,%xmm0,%xmm3
+  .byte  102,73,15,126,217                   // movq          %xmm3,%r9
+  .byte  69,15,182,209                       // movzbl        %r9b,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  69,15,182,243                       // movzbl        %r11b,%r14d
+  .byte  73,193,235,30                       // shr           $0x1e,%r11
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,66,15,16,28,27                  // movss         (%rbx,%r11,1),%xmm3
+  .byte  243,66,15,16,4,139                  // movss         (%rbx,%r9,4),%xmm0
+  .byte  15,20,216                           // unpcklps      %xmm0,%xmm3
+  .byte  243,66,15,16,4,179                  // movss         (%rbx,%r14,4),%xmm0
+  .byte  243,66,15,16,20,147                 // movss         (%rbx,%r10,4),%xmm2
+  .byte  15,20,194                           // unpcklps      %xmm2,%xmm0
+  .byte  15,20,195                           // unpcklps      %xmm3,%xmm0
+  .byte  102,15,115,217,8                    // psrldq        $0x8,%xmm1
+  .byte  102,65,15,219,202                   // pand          %xmm10,%xmm1
+  .byte  102,65,15,97,200                    // punpcklwd     %xmm8,%xmm1
+  .byte  102,15,112,209,78                   // pshufd        $0x4e,%xmm1,%xmm2
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  68,15,182,203                       // movzbl        %bl,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,73,15,126,202                   // movq          %xmm1,%r10
+  .byte  69,15,182,218                       // movzbl        %r10b,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  243,67,15,16,20,23                  // movss         (%r15,%r10,1),%xmm2
+  .byte  243,65,15,16,12,159                 // movss         (%r15,%rbx,4),%xmm1
+  .byte  15,20,209                           // unpcklps      %xmm1,%xmm2
+  .byte  243,67,15,16,12,159                 // movss         (%r15,%r11,4),%xmm1
+  .byte  243,67,15,16,28,143                 // movss         (%r15,%r9,4),%xmm3
+  .byte  15,20,203                           // unpcklps      %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  102,69,15,219,209                   // pand          %xmm9,%xmm10
+  .byte  102,69,15,97,208                    // punpcklwd     %xmm8,%xmm10
+  .byte  102,65,15,112,210,78                // pshufd        $0x4e,%xmm10,%xmm2
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  68,15,182,203                       // movzbl        %bl,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,76,15,126,208                   // movq          %xmm10,%rax
+  .byte  68,15,182,216                       // movzbl        %al,%r11d
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  243,69,15,16,20,2                   // movss         (%r10,%rax,1),%xmm10
+  .byte  243,65,15,16,20,154                 // movss         (%r10,%rbx,4),%xmm2
+  .byte  68,15,20,210                        // unpcklps      %xmm2,%xmm10
+  .byte  243,67,15,16,20,154                 // movss         (%r10,%r11,4),%xmm2
+  .byte  243,67,15,16,28,138                 // movss         (%r10,%r9,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  65,15,20,210                        // unpcklps      %xmm10,%xmm2
+  .byte  102,65,15,112,217,78                // pshufd        $0x4e,%xmm9,%xmm3
+  .byte  102,68,15,111,203                   // movdqa        %xmm3,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,65,15,235,217                   // por           %xmm9,%xmm3
+  .byte  102,65,15,97,216                    // punpcklwd     %xmm8,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  15,89,29,65,54,0,0                  // mulps         0x3641(%rip),%xmm3        # 5b90 <_sk_callback_sse2+0x65c>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+  .byte  242,67,15,16,4,81                   // movsd         (%r9,%r10,2),%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,17                              // jne           2575 <_sk_load_tables_u16_be_sse2+0x198>
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  233,139,254,255,255                 // jmpq          2400 <_sk_load_tables_u16_be_sse2+0x23>
+  .byte  102,67,15,22,68,81,8                // movhpd        0x8(%r9,%r10,2),%xmm0
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  15,130,118,254,255,255              // jb            2400 <_sk_load_tables_u16_be_sse2+0x23>
+  .byte  242,67,15,16,76,81,16               // movsd         0x10(%r9,%r10,2),%xmm1
+  .byte  233,106,254,255,255                 // jmpq          2400 <_sk_load_tables_u16_be_sse2+0x23>
+
+HIDDEN _sk_load_tables_rgb_u16_be_sse2
+.globl _sk_load_tables_rgb_u16_be_sse2
+FUNCTION(_sk_load_tables_rgb_u16_be_sse2)
+_sk_load_tables_rgb_u16_be_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  76,141,20,82                        // lea           (%rdx,%rdx,2),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,84,1,0,0                     // jne           26fc <_sk_load_tables_rgb_u16_be_sse2+0x166>
+  .byte  243,71,15,111,28,81                 // movdqu        (%r9,%r10,2),%xmm11
+  .byte  243,67,15,111,76,81,8               // movdqu        0x8(%r9,%r10,2),%xmm1
+  .byte  102,15,115,217,4                    // psrldq        $0x4,%xmm1
+  .byte  102,69,15,111,211                   // movdqa        %xmm11,%xmm10
+  .byte  102,65,15,115,218,6                 // psrldq        $0x6,%xmm10
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,115,216,6                    // psrldq        $0x6,%xmm0
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  102,68,15,97,217                    // punpcklwd     %xmm1,%xmm11
+  .byte  102,68,15,97,208                    // punpcklwd     %xmm0,%xmm10
+  .byte  102,65,15,111,195                   // movdqa        %xmm11,%xmm0
+  .byte  102,65,15,97,194                    // punpcklwd     %xmm10,%xmm0
+  .byte  102,68,15,111,5,176,53,0,0          // movdqa        0x35b0(%rip),%xmm8        # 5ba0 <_sk_callback_sse2+0x66c>
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,65,15,219,192                   // pand          %xmm8,%xmm0
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  102,65,15,97,193                    // punpcklwd     %xmm9,%xmm0
+  .byte  102,15,112,216,78                   // pshufd        $0x4e,%xmm0,%xmm3
+  .byte  102,73,15,126,217                   // movq          %xmm3,%r9
+  .byte  69,15,182,209                       // movzbl        %r9b,%r10d
+  .byte  73,193,233,32                       // shr           $0x20,%r9
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  69,15,182,243                       // movzbl        %r11b,%r14d
+  .byte  73,193,235,30                       // shr           $0x1e,%r11
+  .byte  72,139,88,8                         // mov           0x8(%rax),%rbx
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,66,15,16,28,27                  // movss         (%rbx,%r11,1),%xmm3
+  .byte  243,66,15,16,4,139                  // movss         (%rbx,%r9,4),%xmm0
+  .byte  15,20,216                           // unpcklps      %xmm0,%xmm3
+  .byte  243,66,15,16,4,179                  // movss         (%rbx,%r14,4),%xmm0
+  .byte  243,66,15,16,20,147                 // movss         (%rbx,%r10,4),%xmm2
+  .byte  15,20,194                           // unpcklps      %xmm2,%xmm0
+  .byte  15,20,195                           // unpcklps      %xmm3,%xmm0
+  .byte  102,65,15,219,200                   // pand          %xmm8,%xmm1
+  .byte  102,65,15,97,201                    // punpcklwd     %xmm9,%xmm1
+  .byte  102,15,112,209,78                   // pshufd        $0x4e,%xmm1,%xmm2
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  68,15,182,203                       // movzbl        %bl,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,73,15,126,202                   // movq          %xmm1,%r10
+  .byte  69,15,182,218                       // movzbl        %r10b,%r11d
+  .byte  73,193,234,30                       // shr           $0x1e,%r10
+  .byte  243,67,15,16,20,23                  // movss         (%r15,%r10,1),%xmm2
+  .byte  243,65,15,16,12,159                 // movss         (%r15,%rbx,4),%xmm1
+  .byte  15,20,209                           // unpcklps      %xmm1,%xmm2
+  .byte  243,67,15,16,12,159                 // movss         (%r15,%r11,4),%xmm1
+  .byte  243,67,15,16,28,143                 // movss         (%r15,%r9,4),%xmm3
+  .byte  15,20,203                           // unpcklps      %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  76,139,80,24                        // mov           0x18(%rax),%r10
+  .byte  102,69,15,105,218                   // punpckhwd     %xmm10,%xmm11
+  .byte  102,69,15,219,216                   // pand          %xmm8,%xmm11
+  .byte  102,69,15,97,217                    // punpcklwd     %xmm9,%xmm11
+  .byte  102,65,15,112,211,78                // pshufd        $0x4e,%xmm11,%xmm2
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  68,15,182,203                       // movzbl        %bl,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,76,15,126,216                   // movq          %xmm11,%rax
+  .byte  68,15,182,216                       // movzbl        %al,%r11d
+  .byte  72,193,232,30                       // shr           $0x1e,%rax
+  .byte  243,69,15,16,4,2                    // movss         (%r10,%rax,1),%xmm8
+  .byte  243,65,15,16,20,154                 // movss         (%r10,%rbx,4),%xmm2
+  .byte  68,15,20,194                        // unpcklps      %xmm2,%xmm8
+  .byte  243,67,15,16,20,154                 // movss         (%r10,%r11,4),%xmm2
+  .byte  243,67,15,16,28,138                 // movss         (%r10,%r9,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  65,15,20,208                        // unpcklps      %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,187,52,0,0                 // movaps        0x34bb(%rip),%xmm3        # 5bb0 <_sk_callback_sse2+0x67c>
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,71,15,110,28,81                 // movd          (%r9,%r10,2),%xmm11
+  .byte  102,71,15,196,92,81,4,2             // pinsrw        $0x2,0x4(%r9,%r10,2),%xmm11
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,14                              // jne           2722 <_sk_load_tables_rgb_u16_be_sse2+0x18c>
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,69,15,239,210                   // pxor          %xmm10,%xmm10
+  .byte  233,172,254,255,255                 // jmpq          25ce <_sk_load_tables_rgb_u16_be_sse2+0x38>
+  .byte  102,71,15,110,84,81,6               // movd          0x6(%r9,%r10,2),%xmm10
+  .byte  102,71,15,196,84,81,10,2            // pinsrw        $0x2,0xa(%r9,%r10,2),%xmm10
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,24                              // jb            2753 <_sk_load_tables_rgb_u16_be_sse2+0x1bd>
+  .byte  102,67,15,110,76,81,12              // movd          0xc(%r9,%r10,2),%xmm1
+  .byte  102,67,15,196,76,81,16,2            // pinsrw        $0x2,0x10(%r9,%r10,2),%xmm1
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  233,123,254,255,255                 // jmpq          25ce <_sk_load_tables_rgb_u16_be_sse2+0x38>
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,114,254,255,255                 // jmpq          25ce <_sk_load_tables_rgb_u16_be_sse2+0x38>
+
+HIDDEN _sk_byte_tables_sse2
+.globl _sk_byte_tables_sse2
+FUNCTION(_sk_byte_tables_sse2)
+_sk_byte_tables_sse2:
+  .byte  85                                  // push          %rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,84,52,0,0                // movaps        0x3454(%rip),%xmm8        # 5bc0 <_sk_callback_sse2+0x68c>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,91,192                       // cvtps2dq      %xmm0,%xmm0
+  .byte  102,73,15,126,193                   // movq          %xmm0,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  77,137,203                          // mov           %r9,%r11
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,15,112,192,78                   // pshufd        $0x4e,%xmm0,%xmm0
+  .byte  102,73,15,126,193                   // movq          %xmm0,%r9
+  .byte  69,137,206                          // mov           %r9d,%r14d
+  .byte  77,137,207                          // mov           %r9,%r15
+  .byte  73,193,239,32                       // shr           $0x20,%r15
+  .byte  72,139,24                           // mov           (%rax),%rbx
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  70,15,182,52,51                     // movzbl        (%rbx,%r14,1),%r14d
+  .byte  66,15,182,44,59                     // movzbl        (%rbx,%r15,1),%ebp
+  .byte  193,229,8                           // shl           $0x8,%ebp
+  .byte  68,9,245                            // or            %r14d,%ebp
+  .byte  70,15,182,20,19                     // movzbl        (%rbx,%r10,1),%r10d
+  .byte  66,15,182,28,27                     // movzbl        (%rbx,%r11,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,211                            // or            %r10d,%ebx
+  .byte  102,15,196,195,0                    // pinsrw        $0x0,%ebx,%xmm0
+  .byte  102,15,196,197,1                    // pinsrw        $0x1,%ebp,%xmm0
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  102,65,15,96,193                    // punpcklbw     %xmm9,%xmm0
+  .byte  102,65,15,97,193                    // punpcklwd     %xmm9,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,21,238,51,0,0              // movaps        0x33ee(%rip),%xmm10        # 5bd0 <_sk_callback_sse2+0x69c>
+  .byte  65,15,89,194                        // mulps         %xmm10,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,91,201                       // cvtps2dq      %xmm1,%xmm1
+  .byte  102,72,15,126,205                   // movq          %xmm1,%rbp
+  .byte  65,137,234                          // mov           %ebp,%r10d
+  .byte  72,193,237,32                       // shr           $0x20,%rbp
+  .byte  102,15,112,201,78                   // pshufd        $0x4e,%xmm1,%xmm1
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  65,137,219                          // mov           %ebx,%r11d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  65,15,182,28,25                     // movzbl        (%r9,%rbx,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,219                            // or            %r11d,%ebx
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  65,15,182,44,41                     // movzbl        (%r9,%rbp,1),%ebp
+  .byte  193,229,8                           // shl           $0x8,%ebp
+  .byte  68,9,213                            // or            %r10d,%ebp
+  .byte  102,15,196,205,0                    // pinsrw        $0x0,%ebp,%xmm1
+  .byte  102,15,196,203,1                    // pinsrw        $0x1,%ebx,%xmm1
+  .byte  102,65,15,96,201                    // punpcklbw     %xmm9,%xmm1
+  .byte  102,65,15,97,201                    // punpcklwd     %xmm9,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,202                        // mulps         %xmm10,%xmm1
+  .byte  76,139,80,16                        // mov           0x10(%rax),%r10
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,91,210                       // cvtps2dq      %xmm2,%xmm2
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  65,137,217                          // mov           %ebx,%r9d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  102,15,112,210,78                   // pshufd        $0x4e,%xmm2,%xmm2
+  .byte  102,72,15,126,213                   // movq          %xmm2,%rbp
+  .byte  65,137,235                          // mov           %ebp,%r11d
+  .byte  72,193,237,32                       // shr           $0x20,%rbp
+  .byte  71,15,182,28,26                     // movzbl        (%r10,%r11,1),%r11d
+  .byte  65,15,182,44,42                     // movzbl        (%r10,%rbp,1),%ebp
+  .byte  193,229,8                           // shl           $0x8,%ebp
+  .byte  68,9,221                            // or            %r11d,%ebp
+  .byte  71,15,182,12,10                     // movzbl        (%r10,%r9,1),%r9d
+  .byte  65,15,182,28,26                     // movzbl        (%r10,%rbx,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,203                            // or            %r9d,%ebx
+  .byte  102,15,196,211,0                    // pinsrw        $0x0,%ebx,%xmm2
+  .byte  102,15,196,213,1                    // pinsrw        $0x1,%ebp,%xmm2
+  .byte  102,65,15,96,209                    // punpcklbw     %xmm9,%xmm2
+  .byte  102,65,15,97,209                    // punpcklwd     %xmm9,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,210                        // mulps         %xmm10,%xmm2
+  .byte  72,139,64,24                        // mov           0x18(%rax),%rax
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  102,15,91,219                       // cvtps2dq      %xmm3,%xmm3
+  .byte  102,72,15,126,221                   // movq          %xmm3,%rbp
+  .byte  65,137,233                          // mov           %ebp,%r9d
+  .byte  72,193,237,32                       // shr           $0x20,%rbp
+  .byte  102,15,112,219,78                   // pshufd        $0x4e,%xmm3,%xmm3
+  .byte  102,72,15,126,219                   // movq          %xmm3,%rbx
+  .byte  65,137,218                          // mov           %ebx,%r10d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  70,15,182,20,16                     // movzbl        (%rax,%r10,1),%r10d
+  .byte  15,182,28,24                        // movzbl        (%rax,%rbx,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,211                            // or            %r10d,%ebx
+  .byte  70,15,182,12,8                      // movzbl        (%rax,%r9,1),%r9d
+  .byte  15,182,4,40                         // movzbl        (%rax,%rbp,1),%eax
+  .byte  193,224,8                           // shl           $0x8,%eax
+  .byte  68,9,200                            // or            %r9d,%eax
+  .byte  102,15,196,216,0                    // pinsrw        $0x0,%eax,%xmm3
+  .byte  102,15,196,219,1                    // pinsrw        $0x1,%ebx,%xmm3
+  .byte  102,65,15,96,217                    // punpcklbw     %xmm9,%xmm3
+  .byte  102,65,15,97,217                    // punpcklwd     %xmm9,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,218                        // mulps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_byte_tables_rgb_sse2
+.globl _sk_byte_tables_rgb_sse2
+FUNCTION(_sk_byte_tables_rgb_sse2)
+_sk_byte_tables_rgb_sse2:
+  .byte  85                                  // push          %rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,139,72,24                        // mov           0x18(%rax),%r9d
+  .byte  65,255,201                          // dec           %r9d
+  .byte  102,69,15,110,193                   // movd          %r9d,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,91,192                       // cvtps2dq      %xmm0,%xmm0
+  .byte  102,73,15,126,193                   // movq          %xmm0,%r9
+  .byte  69,137,202                          // mov           %r9d,%r10d
+  .byte  77,137,203                          // mov           %r9,%r11
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,15,112,192,78                   // pshufd        $0x4e,%xmm0,%xmm0
+  .byte  102,73,15,126,193                   // movq          %xmm0,%r9
+  .byte  69,137,206                          // mov           %r9d,%r14d
+  .byte  77,137,207                          // mov           %r9,%r15
+  .byte  73,193,239,32                       // shr           $0x20,%r15
+  .byte  72,139,24                           // mov           (%rax),%rbx
+  .byte  76,139,72,8                         // mov           0x8(%rax),%r9
+  .byte  70,15,182,52,51                     // movzbl        (%rbx,%r14,1),%r14d
+  .byte  66,15,182,44,59                     // movzbl        (%rbx,%r15,1),%ebp
+  .byte  193,229,8                           // shl           $0x8,%ebp
+  .byte  68,9,245                            // or            %r14d,%ebp
+  .byte  70,15,182,20,19                     // movzbl        (%rbx,%r10,1),%r10d
+  .byte  66,15,182,28,27                     // movzbl        (%rbx,%r11,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,211                            // or            %r10d,%ebx
+  .byte  102,15,196,195,0                    // pinsrw        $0x0,%ebx,%xmm0
+  .byte  102,15,196,197,1                    // pinsrw        $0x1,%ebp,%xmm0
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  102,65,15,96,193                    // punpcklbw     %xmm9,%xmm0
+  .byte  102,65,15,97,193                    // punpcklwd     %xmm9,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,21,54,50,0,0               // movaps        0x3236(%rip),%xmm10        # 5be0 <_sk_callback_sse2+0x6ac>
+  .byte  65,15,89,194                        // mulps         %xmm10,%xmm0
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,15,91,201                       // cvtps2dq      %xmm1,%xmm1
+  .byte  102,72,15,126,205                   // movq          %xmm1,%rbp
+  .byte  65,137,234                          // mov           %ebp,%r10d
+  .byte  72,193,237,32                       // shr           $0x20,%rbp
+  .byte  102,15,112,201,78                   // pshufd        $0x4e,%xmm1,%xmm1
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  65,137,219                          // mov           %ebx,%r11d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  71,15,182,28,25                     // movzbl        (%r9,%r11,1),%r11d
+  .byte  65,15,182,28,25                     // movzbl        (%r9,%rbx,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,219                            // or            %r11d,%ebx
+  .byte  71,15,182,20,17                     // movzbl        (%r9,%r10,1),%r10d
+  .byte  65,15,182,44,41                     // movzbl        (%r9,%rbp,1),%ebp
+  .byte  193,229,8                           // shl           $0x8,%ebp
+  .byte  68,9,213                            // or            %r10d,%ebp
+  .byte  102,15,196,205,0                    // pinsrw        $0x0,%ebp,%xmm1
+  .byte  102,15,196,203,1                    // pinsrw        $0x1,%ebx,%xmm1
+  .byte  102,65,15,96,201                    // punpcklbw     %xmm9,%xmm1
+  .byte  102,65,15,97,201                    // punpcklwd     %xmm9,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,202                        // mulps         %xmm10,%xmm1
+  .byte  72,139,64,16                        // mov           0x10(%rax),%rax
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,15,91,210                       // cvtps2dq      %xmm2,%xmm2
+  .byte  102,72,15,126,213                   // movq          %xmm2,%rbp
+  .byte  65,137,233                          // mov           %ebp,%r9d
+  .byte  72,193,237,32                       // shr           $0x20,%rbp
+  .byte  102,15,112,210,78                   // pshufd        $0x4e,%xmm2,%xmm2
+  .byte  102,72,15,126,211                   // movq          %xmm2,%rbx
+  .byte  65,137,218                          // mov           %ebx,%r10d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  70,15,182,20,16                     // movzbl        (%rax,%r10,1),%r10d
+  .byte  15,182,28,24                        // movzbl        (%rax,%rbx,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  68,9,211                            // or            %r10d,%ebx
+  .byte  70,15,182,12,8                      // movzbl        (%rax,%r9,1),%r9d
+  .byte  15,182,4,40                         // movzbl        (%rax,%rbp,1),%eax
+  .byte  193,224,8                           // shl           $0x8,%eax
+  .byte  68,9,200                            // or            %r9d,%eax
+  .byte  102,15,196,208,0                    // pinsrw        $0x0,%eax,%xmm2
+  .byte  102,15,196,211,1                    // pinsrw        $0x1,%ebx,%xmm2
+  .byte  102,65,15,96,209                    // punpcklbw     %xmm9,%xmm2
+  .byte  102,65,15,97,209                    // punpcklwd     %xmm9,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,210                        // mulps         %xmm10,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_r_sse2
+.globl _sk_table_r_sse2
+FUNCTION(_sk_table_r_sse2)
+_sk_table_r_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,112,192,78                // pshufd        $0x4e,%xmm8,%xmm0
+  .byte  102,72,15,126,192                   // movq          %xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,77,15,126,195                   // movq          %xmm8,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,71,15,16,4,153                  // movss         (%r9,%r11,4),%xmm8
+  .byte  243,65,15,16,4,129                  // movss         (%r9,%rax,4),%xmm0
+  .byte  68,15,20,192                        // unpcklps      %xmm0,%xmm8
+  .byte  243,65,15,16,4,153                  // movss         (%r9,%rbx,4),%xmm0
+  .byte  243,71,15,16,12,145                 // movss         (%r9,%r10,4),%xmm9
+  .byte  65,15,20,193                        // unpcklps      %xmm9,%xmm0
+  .byte  65,15,20,192                        // unpcklps      %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_g_sse2
+.globl _sk_table_g_sse2
+FUNCTION(_sk_table_g_sse2)
+_sk_table_g_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,193                        // mulps         %xmm1,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,112,200,78                // pshufd        $0x4e,%xmm8,%xmm1
+  .byte  102,72,15,126,200                   // movq          %xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,77,15,126,195                   // movq          %xmm8,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,71,15,16,4,153                  // movss         (%r9,%r11,4),%xmm8
+  .byte  243,65,15,16,12,129                 // movss         (%r9,%rax,4),%xmm1
+  .byte  68,15,20,193                        // unpcklps      %xmm1,%xmm8
+  .byte  243,65,15,16,12,153                 // movss         (%r9,%rbx,4),%xmm1
+  .byte  243,71,15,16,12,145                 // movss         (%r9,%r10,4),%xmm9
+  .byte  65,15,20,201                        // unpcklps      %xmm9,%xmm1
+  .byte  65,15,20,200                        // unpcklps      %xmm8,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_b_sse2
+.globl _sk_table_b_sse2
+FUNCTION(_sk_table_b_sse2)
+_sk_table_b_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,112,208,78                // pshufd        $0x4e,%xmm8,%xmm2
+  .byte  102,72,15,126,208                   // movq          %xmm2,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,77,15,126,195                   // movq          %xmm8,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,71,15,16,4,153                  // movss         (%r9,%r11,4),%xmm8
+  .byte  243,65,15,16,20,129                 // movss         (%r9,%rax,4),%xmm2
+  .byte  68,15,20,194                        // unpcklps      %xmm2,%xmm8
+  .byte  243,65,15,16,20,153                 // movss         (%r9,%rbx,4),%xmm2
+  .byte  243,71,15,16,12,145                 // movss         (%r9,%r10,4),%xmm9
+  .byte  65,15,20,209                        // unpcklps      %xmm9,%xmm2
+  .byte  65,15,20,208                        // unpcklps      %xmm8,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_table_a_sse2
+.globl _sk_table_a_sse2
+FUNCTION(_sk_table_a_sse2)
+_sk_table_a_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  139,64,8                            // mov           0x8(%rax),%eax
+  .byte  255,200                             // dec           %eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  102,69,15,112,192,0                 // pshufd        $0x0,%xmm8,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,112,216,78                // pshufd        $0x4e,%xmm8,%xmm3
+  .byte  102,72,15,126,216                   // movq          %xmm3,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,77,15,126,195                   // movq          %xmm8,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,71,15,16,4,153                  // movss         (%r9,%r11,4),%xmm8
+  .byte  243,65,15,16,28,129                 // movss         (%r9,%rax,4),%xmm3
+  .byte  68,15,20,195                        // unpcklps      %xmm3,%xmm8
+  .byte  243,65,15,16,28,153                 // movss         (%r9,%rbx,4),%xmm3
+  .byte  243,71,15,16,12,145                 // movss         (%r9,%r10,4),%xmm9
+  .byte  65,15,20,217                        // unpcklps      %xmm9,%xmm3
+  .byte  65,15,20,216                        // unpcklps      %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_r_sse2
+.globl _sk_parametric_r_sse2
+FUNCTION(_sk_parametric_r_sse2)
+_sk_parametric_r_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,72,16                  // movss         0x10(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,64,12                  // movss         0xc(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  243,68,15,16,80,4                   // movss         0x4(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,208                        // mulps         %xmm0,%xmm10
+  .byte  65,15,194,193,2                     // cmpleps       %xmm9,%xmm0
+  .byte  243,68,15,16,72,24                  // movss         0x18(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  243,68,15,16,24                     // movss         (%rax),%xmm11
+  .byte  243,68,15,16,72,8                   // movss         0x8(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,209                        // addps         %xmm9,%xmm10
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,91,202                        // cvtdq2ps      %xmm10,%xmm9
+  .byte  68,15,89,13,106,47,0,0              // mulps         0x2f6a(%rip),%xmm9        # 5bf0 <_sk_callback_sse2+0x6bc>
+  .byte  68,15,84,21,114,47,0,0              // andps         0x2f72(%rip),%xmm10        # 5c00 <_sk_callback_sse2+0x6cc>
+  .byte  68,15,86,21,122,47,0,0              // orps          0x2f7a(%rip),%xmm10        # 5c10 <_sk_callback_sse2+0x6dc>
+  .byte  68,15,88,13,130,47,0,0              // addps         0x2f82(%rip),%xmm9        # 5c20 <_sk_callback_sse2+0x6ec>
+  .byte  68,15,40,37,138,47,0,0              // movaps        0x2f8a(%rip),%xmm12        # 5c30 <_sk_callback_sse2+0x6fc>
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,88,21,138,47,0,0              // addps         0x2f8a(%rip),%xmm10        # 5c40 <_sk_callback_sse2+0x70c>
+  .byte  68,15,40,37,146,47,0,0              // movaps        0x2f92(%rip),%xmm12        # 5c50 <_sk_callback_sse2+0x71c>
+  .byte  69,15,94,226                        // divps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,226                        // cvtdq2ps      %xmm10,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,194,236,1                     // cmpltps       %xmm12,%xmm13
+  .byte  68,15,40,21,124,47,0,0              // movaps        0x2f7c(%rip),%xmm10        # 5c60 <_sk_callback_sse2+0x72c>
+  .byte  69,15,84,234                        // andps         %xmm10,%xmm13
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,88,13,112,47,0,0              // addps         0x2f70(%rip),%xmm9        # 5c70 <_sk_callback_sse2+0x73c>
+  .byte  68,15,40,37,120,47,0,0              // movaps        0x2f78(%rip),%xmm12        # 5c80 <_sk_callback_sse2+0x74c>
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,40,37,120,47,0,0              // movaps        0x2f78(%rip),%xmm12        # 5c90 <_sk_callback_sse2+0x75c>
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,40,45,124,47,0,0              // movaps        0x2f7c(%rip),%xmm13        # 5ca0 <_sk_callback_sse2+0x76c>
+  .byte  69,15,94,236                        // divps         %xmm12,%xmm13
+  .byte  69,15,88,233                        // addps         %xmm9,%xmm13
+  .byte  68,15,89,45,124,47,0,0              // mulps         0x2f7c(%rip),%xmm13        # 5cb0 <_sk_callback_sse2+0x77c>
+  .byte  102,69,15,91,205                    // cvtps2dq      %xmm13,%xmm9
+  .byte  243,68,15,16,96,20                  // movss         0x14(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  69,15,88,225                        // addps         %xmm9,%xmm12
+  .byte  68,15,84,192                        // andps         %xmm0,%xmm8
+  .byte  65,15,85,196                        // andnps        %xmm12,%xmm0
+  .byte  65,15,86,192                        // orps          %xmm8,%xmm0
+  .byte  65,15,95,195                        // maxps         %xmm11,%xmm0
+  .byte  65,15,93,194                        // minps         %xmm10,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_g_sse2
+.globl _sk_parametric_g_sse2
+FUNCTION(_sk_parametric_g_sse2)
+_sk_parametric_g_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,72,16                  // movss         0x10(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,64,12                  // movss         0xc(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,89,193                        // mulps         %xmm1,%xmm8
+  .byte  243,68,15,16,80,4                   // movss         0x4(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  65,15,194,201,2                     // cmpleps       %xmm9,%xmm1
+  .byte  243,68,15,16,72,24                  // movss         0x18(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  243,68,15,16,24                     // movss         (%rax),%xmm11
+  .byte  243,68,15,16,72,8                   // movss         0x8(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,209                        // addps         %xmm9,%xmm10
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,91,202                        // cvtdq2ps      %xmm10,%xmm9
+  .byte  68,15,89,13,252,46,0,0              // mulps         0x2efc(%rip),%xmm9        # 5cc0 <_sk_callback_sse2+0x78c>
+  .byte  68,15,84,21,4,47,0,0                // andps         0x2f04(%rip),%xmm10        # 5cd0 <_sk_callback_sse2+0x79c>
+  .byte  68,15,86,21,12,47,0,0               // orps          0x2f0c(%rip),%xmm10        # 5ce0 <_sk_callback_sse2+0x7ac>
+  .byte  68,15,88,13,20,47,0,0               // addps         0x2f14(%rip),%xmm9        # 5cf0 <_sk_callback_sse2+0x7bc>
+  .byte  68,15,40,37,28,47,0,0               // movaps        0x2f1c(%rip),%xmm12        # 5d00 <_sk_callback_sse2+0x7cc>
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,88,21,28,47,0,0               // addps         0x2f1c(%rip),%xmm10        # 5d10 <_sk_callback_sse2+0x7dc>
+  .byte  68,15,40,37,36,47,0,0               // movaps        0x2f24(%rip),%xmm12        # 5d20 <_sk_callback_sse2+0x7ec>
+  .byte  69,15,94,226                        // divps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,226                        // cvtdq2ps      %xmm10,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,194,236,1                     // cmpltps       %xmm12,%xmm13
+  .byte  68,15,40,21,14,47,0,0               // movaps        0x2f0e(%rip),%xmm10        # 5d30 <_sk_callback_sse2+0x7fc>
+  .byte  69,15,84,234                        // andps         %xmm10,%xmm13
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,88,13,2,47,0,0                // addps         0x2f02(%rip),%xmm9        # 5d40 <_sk_callback_sse2+0x80c>
+  .byte  68,15,40,37,10,47,0,0               // movaps        0x2f0a(%rip),%xmm12        # 5d50 <_sk_callback_sse2+0x81c>
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,40,37,10,47,0,0               // movaps        0x2f0a(%rip),%xmm12        # 5d60 <_sk_callback_sse2+0x82c>
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,40,45,14,47,0,0               // movaps        0x2f0e(%rip),%xmm13        # 5d70 <_sk_callback_sse2+0x83c>
+  .byte  69,15,94,236                        // divps         %xmm12,%xmm13
+  .byte  69,15,88,233                        // addps         %xmm9,%xmm13
+  .byte  68,15,89,45,14,47,0,0               // mulps         0x2f0e(%rip),%xmm13        # 5d80 <_sk_callback_sse2+0x84c>
+  .byte  102,69,15,91,205                    // cvtps2dq      %xmm13,%xmm9
+  .byte  243,68,15,16,96,20                  // movss         0x14(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  69,15,88,225                        // addps         %xmm9,%xmm12
+  .byte  68,15,84,193                        // andps         %xmm1,%xmm8
+  .byte  65,15,85,204                        // andnps        %xmm12,%xmm1
+  .byte  65,15,86,200                        // orps          %xmm8,%xmm1
+  .byte  65,15,95,203                        // maxps         %xmm11,%xmm1
+  .byte  65,15,93,202                        // minps         %xmm10,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_b_sse2
+.globl _sk_parametric_b_sse2
+FUNCTION(_sk_parametric_b_sse2)
+_sk_parametric_b_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,72,16                  // movss         0x10(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,64,12                  // movss         0xc(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  243,68,15,16,80,4                   // movss         0x4(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,210                        // mulps         %xmm2,%xmm10
+  .byte  65,15,194,209,2                     // cmpleps       %xmm9,%xmm2
+  .byte  243,68,15,16,72,24                  // movss         0x18(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  243,68,15,16,24                     // movss         (%rax),%xmm11
+  .byte  243,68,15,16,72,8                   // movss         0x8(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,209                        // addps         %xmm9,%xmm10
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,91,202                        // cvtdq2ps      %xmm10,%xmm9
+  .byte  68,15,89,13,142,46,0,0              // mulps         0x2e8e(%rip),%xmm9        # 5d90 <_sk_callback_sse2+0x85c>
+  .byte  68,15,84,21,150,46,0,0              // andps         0x2e96(%rip),%xmm10        # 5da0 <_sk_callback_sse2+0x86c>
+  .byte  68,15,86,21,158,46,0,0              // orps          0x2e9e(%rip),%xmm10        # 5db0 <_sk_callback_sse2+0x87c>
+  .byte  68,15,88,13,166,46,0,0              // addps         0x2ea6(%rip),%xmm9        # 5dc0 <_sk_callback_sse2+0x88c>
+  .byte  68,15,40,37,174,46,0,0              // movaps        0x2eae(%rip),%xmm12        # 5dd0 <_sk_callback_sse2+0x89c>
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,88,21,174,46,0,0              // addps         0x2eae(%rip),%xmm10        # 5de0 <_sk_callback_sse2+0x8ac>
+  .byte  68,15,40,37,182,46,0,0              // movaps        0x2eb6(%rip),%xmm12        # 5df0 <_sk_callback_sse2+0x8bc>
+  .byte  69,15,94,226                        // divps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,226                        // cvtdq2ps      %xmm10,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,194,236,1                     // cmpltps       %xmm12,%xmm13
+  .byte  68,15,40,21,160,46,0,0              // movaps        0x2ea0(%rip),%xmm10        # 5e00 <_sk_callback_sse2+0x8cc>
+  .byte  69,15,84,234                        // andps         %xmm10,%xmm13
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,88,13,148,46,0,0              // addps         0x2e94(%rip),%xmm9        # 5e10 <_sk_callback_sse2+0x8dc>
+  .byte  68,15,40,37,156,46,0,0              // movaps        0x2e9c(%rip),%xmm12        # 5e20 <_sk_callback_sse2+0x8ec>
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,40,37,156,46,0,0              // movaps        0x2e9c(%rip),%xmm12        # 5e30 <_sk_callback_sse2+0x8fc>
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,40,45,160,46,0,0              // movaps        0x2ea0(%rip),%xmm13        # 5e40 <_sk_callback_sse2+0x90c>
+  .byte  69,15,94,236                        // divps         %xmm12,%xmm13
+  .byte  69,15,88,233                        // addps         %xmm9,%xmm13
+  .byte  68,15,89,45,160,46,0,0              // mulps         0x2ea0(%rip),%xmm13        # 5e50 <_sk_callback_sse2+0x91c>
+  .byte  102,69,15,91,205                    // cvtps2dq      %xmm13,%xmm9
+  .byte  243,68,15,16,96,20                  // movss         0x14(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  69,15,88,225                        // addps         %xmm9,%xmm12
+  .byte  68,15,84,194                        // andps         %xmm2,%xmm8
+  .byte  65,15,85,212                        // andnps        %xmm12,%xmm2
+  .byte  65,15,86,208                        // orps          %xmm8,%xmm2
+  .byte  65,15,95,211                        // maxps         %xmm11,%xmm2
+  .byte  65,15,93,210                        // minps         %xmm10,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_parametric_a_sse2
+.globl _sk_parametric_a_sse2
+FUNCTION(_sk_parametric_a_sse2)
+_sk_parametric_a_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,72,16                  // movss         0x10(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,64,12                  // movss         0xc(%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  243,68,15,16,80,4                   // movss         0x4(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  65,15,194,217,2                     // cmpleps       %xmm9,%xmm3
+  .byte  243,68,15,16,72,24                  // movss         0x18(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  243,68,15,16,24                     // movss         (%rax),%xmm11
+  .byte  243,68,15,16,72,8                   // movss         0x8(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  69,15,88,209                        // addps         %xmm9,%xmm10
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,91,202                        // cvtdq2ps      %xmm10,%xmm9
+  .byte  68,15,89,13,32,46,0,0               // mulps         0x2e20(%rip),%xmm9        # 5e60 <_sk_callback_sse2+0x92c>
+  .byte  68,15,84,21,40,46,0,0               // andps         0x2e28(%rip),%xmm10        # 5e70 <_sk_callback_sse2+0x93c>
+  .byte  68,15,86,21,48,46,0,0               // orps          0x2e30(%rip),%xmm10        # 5e80 <_sk_callback_sse2+0x94c>
+  .byte  68,15,88,13,56,46,0,0               // addps         0x2e38(%rip),%xmm9        # 5e90 <_sk_callback_sse2+0x95c>
+  .byte  68,15,40,37,64,46,0,0               // movaps        0x2e40(%rip),%xmm12        # 5ea0 <_sk_callback_sse2+0x96c>
+  .byte  69,15,89,226                        // mulps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,88,21,64,46,0,0               // addps         0x2e40(%rip),%xmm10        # 5eb0 <_sk_callback_sse2+0x97c>
+  .byte  68,15,40,37,72,46,0,0               // movaps        0x2e48(%rip),%xmm12        # 5ec0 <_sk_callback_sse2+0x98c>
+  .byte  69,15,94,226                        // divps         %xmm10,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,226                        // cvtdq2ps      %xmm10,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,194,236,1                     // cmpltps       %xmm12,%xmm13
+  .byte  68,15,40,21,50,46,0,0               // movaps        0x2e32(%rip),%xmm10        # 5ed0 <_sk_callback_sse2+0x99c>
+  .byte  69,15,84,234                        // andps         %xmm10,%xmm13
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  69,15,40,233                        // movaps        %xmm9,%xmm13
+  .byte  69,15,92,236                        // subps         %xmm12,%xmm13
+  .byte  68,15,88,13,38,46,0,0               // addps         0x2e26(%rip),%xmm9        # 5ee0 <_sk_callback_sse2+0x9ac>
+  .byte  68,15,40,37,46,46,0,0               // movaps        0x2e2e(%rip),%xmm12        # 5ef0 <_sk_callback_sse2+0x9bc>
+  .byte  69,15,89,229                        // mulps         %xmm13,%xmm12
+  .byte  69,15,92,204                        // subps         %xmm12,%xmm9
+  .byte  68,15,40,37,46,46,0,0               // movaps        0x2e2e(%rip),%xmm12        # 5f00 <_sk_callback_sse2+0x9cc>
+  .byte  69,15,92,229                        // subps         %xmm13,%xmm12
+  .byte  68,15,40,45,50,46,0,0               // movaps        0x2e32(%rip),%xmm13        # 5f10 <_sk_callback_sse2+0x9dc>
+  .byte  69,15,94,236                        // divps         %xmm12,%xmm13
+  .byte  69,15,88,233                        // addps         %xmm9,%xmm13
+  .byte  68,15,89,45,50,46,0,0               // mulps         0x2e32(%rip),%xmm13        # 5f20 <_sk_callback_sse2+0x9ec>
+  .byte  102,69,15,91,205                    // cvtps2dq      %xmm13,%xmm9
+  .byte  243,68,15,16,96,20                  // movss         0x14(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  69,15,88,225                        // addps         %xmm9,%xmm12
+  .byte  68,15,84,195                        // andps         %xmm3,%xmm8
+  .byte  65,15,85,220                        // andnps        %xmm12,%xmm3
+  .byte  65,15,86,216                        // orps          %xmm8,%xmm3
+  .byte  65,15,95,219                        // maxps         %xmm11,%xmm3
+  .byte  65,15,93,218                        // minps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lab_to_xyz_sse2
+.globl _sk_lab_to_xyz_sse2
+FUNCTION(_sk_lab_to_xyz_sse2)
+_sk_lab_to_xyz_sse2:
+  .byte  15,89,5,15,46,0,0                   // mulps         0x2e0f(%rip),%xmm0        # 5f30 <_sk_callback_sse2+0x9fc>
+  .byte  68,15,40,5,23,46,0,0                // movaps        0x2e17(%rip),%xmm8        # 5f40 <_sk_callback_sse2+0xa0c>
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  68,15,40,13,27,46,0,0               // movaps        0x2e1b(%rip),%xmm9        # 5f50 <_sk_callback_sse2+0xa1c>
+  .byte  65,15,88,201                        // addps         %xmm9,%xmm1
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  65,15,88,209                        // addps         %xmm9,%xmm2
+  .byte  15,88,5,24,46,0,0                   // addps         0x2e18(%rip),%xmm0        # 5f60 <_sk_callback_sse2+0xa2c>
+  .byte  15,89,5,33,46,0,0                   // mulps         0x2e21(%rip),%xmm0        # 5f70 <_sk_callback_sse2+0xa3c>
+  .byte  15,89,13,42,46,0,0                  // mulps         0x2e2a(%rip),%xmm1        # 5f80 <_sk_callback_sse2+0xa4c>
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  15,89,21,48,46,0,0                  // mulps         0x2e30(%rip),%xmm2        # 5f90 <_sk_callback_sse2+0xa5c>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  68,15,92,202                        // subps         %xmm2,%xmm9
+  .byte  68,15,40,225                        // movaps        %xmm1,%xmm12
+  .byte  69,15,89,228                        // mulps         %xmm12,%xmm12
+  .byte  68,15,89,225                        // mulps         %xmm1,%xmm12
+  .byte  15,40,21,37,46,0,0                  // movaps        0x2e25(%rip),%xmm2        # 5fa0 <_sk_callback_sse2+0xa6c>
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  69,15,194,196,1                     // cmpltps       %xmm12,%xmm8
+  .byte  68,15,40,21,36,46,0,0               // movaps        0x2e24(%rip),%xmm10        # 5fb0 <_sk_callback_sse2+0xa7c>
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  68,15,40,29,40,46,0,0               // movaps        0x2e28(%rip),%xmm11        # 5fc0 <_sk_callback_sse2+0xa8c>
+  .byte  65,15,89,203                        // mulps         %xmm11,%xmm1
+  .byte  69,15,84,224                        // andps         %xmm8,%xmm12
+  .byte  68,15,85,193                        // andnps        %xmm1,%xmm8
+  .byte  69,15,86,196                        // orps          %xmm12,%xmm8
+  .byte  68,15,40,224                        // movaps        %xmm0,%xmm12
+  .byte  69,15,89,228                        // mulps         %xmm12,%xmm12
+  .byte  68,15,89,224                        // mulps         %xmm0,%xmm12
+  .byte  15,40,202                           // movaps        %xmm2,%xmm1
+  .byte  65,15,194,204,1                     // cmpltps       %xmm12,%xmm1
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  65,15,89,195                        // mulps         %xmm11,%xmm0
+  .byte  68,15,84,225                        // andps         %xmm1,%xmm12
+  .byte  15,85,200                           // andnps        %xmm0,%xmm1
+  .byte  65,15,86,204                        // orps          %xmm12,%xmm1
+  .byte  65,15,40,193                        // movaps        %xmm9,%xmm0
+  .byte  15,89,192                           // mulps         %xmm0,%xmm0
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  15,194,208,1                        // cmpltps       %xmm0,%xmm2
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  69,15,89,203                        // mulps         %xmm11,%xmm9
+  .byte  15,84,194                           // andps         %xmm2,%xmm0
+  .byte  65,15,85,209                        // andnps        %xmm9,%xmm2
+  .byte  15,86,208                           // orps          %xmm0,%xmm2
+  .byte  68,15,89,5,216,45,0,0               // mulps         0x2dd8(%rip),%xmm8        # 5fd0 <_sk_callback_sse2+0xa9c>
+  .byte  15,89,21,225,45,0,0                 // mulps         0x2de1(%rip),%xmm2        # 5fe0 <_sk_callback_sse2+0xaac>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_a8_sse2
+.globl _sk_load_a8_sse2
+FUNCTION(_sk_load_a8_sse2)
+_sk_load_a8_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,45                              // jne           323e <_sk_load_a8_sse2+0x37>
+  .byte  102,65,15,110,4,18                  // movd          (%r10,%rdx,1),%xmm0
+  .byte  102,15,96,192                       // punpcklbw     %xmm0,%xmm0
+  .byte  102,15,97,192                       // punpcklwd     %xmm0,%xmm0
+  .byte  102,15,219,5,201,45,0,0             // pand          0x2dc9(%rip),%xmm0        # 5ff0 <_sk_callback_sse2+0xabc>
+  .byte  15,91,216                           // cvtdq2ps      %xmm0,%xmm3
+  .byte  15,89,29,207,45,0,0                 // mulps         0x2dcf(%rip),%xmm3        # 6000 <_sk_callback_sse2+0xacc>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,48                              // je            327f <_sk_load_a8_sse2+0x78>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            326a <_sk_load_a8_sse2+0x63>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,196                             // jne           321f <_sk_load_a8_sse2+0x18>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,192,69                   // pshufd        $0x45,%xmm0,%xmm0
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,15,110,200                      // movd          %eax,%xmm1
+  .byte  15,198,200,0                        // shufps        $0x0,%xmm0,%xmm1
+  .byte  15,198,200,226                      // shufps        $0xe2,%xmm0,%xmm1
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,15,110,200                      // movd          %eax,%xmm1
+  .byte  243,15,16,193                       // movss         %xmm1,%xmm0
+  .byte  235,145                             // jmp           321f <_sk_load_a8_sse2+0x18>
+
+HIDDEN _sk_gather_a8_sse2
+.globl _sk_gather_a8_sse2
+FUNCTION(_sk_gather_a8_sse2)
+_sk_gather_a8_sse2:
+  .byte  85                                  // push          %rbp
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,72,15,126,192                   // movq          %xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,15,112,192,78                   // pshufd        $0x4e,%xmm0,%xmm0
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  65,15,182,44,25                     // movzbl        (%r9,%rbx,1),%ebp
+  .byte  67,15,182,28,25                     // movzbl        (%r9,%r11,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  9,235                               // or            %ebp,%ebx
+  .byte  67,15,182,44,17                     // movzbl        (%r9,%r10,1),%ebp
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  193,224,8                           // shl           $0x8,%eax
+  .byte  9,232                               // or            %ebp,%eax
+  .byte  102,15,196,192,0                    // pinsrw        $0x0,%eax,%xmm0
+  .byte  102,15,196,195,1                    // pinsrw        $0x1,%ebx,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,15,96,193                       // punpcklbw     %xmm1,%xmm0
+  .byte  102,15,97,193                       // punpcklwd     %xmm1,%xmm0
+  .byte  15,91,216                           // cvtdq2ps      %xmm0,%xmm3
+  .byte  15,89,29,239,44,0,0                 // mulps         0x2cef(%rip),%xmm3        # 6010 <_sk_callback_sse2+0xadc>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,15,239,210                      // pxor          %xmm2,%xmm2
+  .byte  91                                  // pop           %rbx
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_a8_sse2
+.globl _sk_store_a8_sse2
+FUNCTION(_sk_store_a8_sse2)
+_sk_store_a8_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  68,15,40,5,225,44,0,0               // movaps        0x2ce1(%rip),%xmm8        # 6020 <_sk_callback_sse2+0xaec>
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,114,240,16                // pslld         $0x10,%xmm8
+  .byte  102,65,15,114,224,16                // psrad         $0x10,%xmm8
+  .byte  102,69,15,107,192                   // packssdw      %xmm8,%xmm8
+  .byte  102,69,15,103,192                   // packuswb      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,13                              // jne           3370 <_sk_store_a8_sse2+0x3e>
+  .byte  102,68,15,126,192                   // movd          %xmm8,%eax
+  .byte  65,137,4,18                         // mov           %eax,(%r10,%rdx,1)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,68,15,96,192                    // punpcklbw     %xmm0,%xmm8
+  .byte  102,68,15,97,192                    // punpcklwd     %xmm0,%xmm8
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,44                              // je            33b3 <_sk_store_a8_sse2+0x81>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,22                              // je            33a3 <_sk_store_a8_sse2+0x71>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,217                             // jne           336c <_sk_store_a8_sse2+0x3a>
+  .byte  102,68,15,127,68,36,232             // movdqa        %xmm8,-0x18(%rsp)
+  .byte  138,68,36,240                       // mov           -0x10(%rsp),%al
+  .byte  65,136,68,18,2                      // mov           %al,0x2(%r10,%rdx,1)
+  .byte  102,68,15,127,68,36,216             // movdqa        %xmm8,-0x28(%rsp)
+  .byte  138,68,36,220                       // mov           -0x24(%rsp),%al
+  .byte  65,136,68,18,1                      // mov           %al,0x1(%r10,%rdx,1)
+  .byte  102,68,15,127,68,36,200             // movdqa        %xmm8,-0x38(%rsp)
+  .byte  138,68,36,200                       // mov           -0x38(%rsp),%al
+  .byte  65,136,4,18                         // mov           %al,(%r10,%rdx,1)
+  .byte  235,168                             // jmp           336c <_sk_store_a8_sse2+0x3a>
+
+HIDDEN _sk_load_g8_sse2
+.globl _sk_load_g8_sse2
+FUNCTION(_sk_load_g8_sse2)
+_sk_load_g8_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,49                              // jne           33ff <_sk_load_g8_sse2+0x3b>
+  .byte  102,65,15,110,4,18                  // movd          (%r10,%rdx,1),%xmm0
+  .byte  102,15,96,192                       // punpcklbw     %xmm0,%xmm0
+  .byte  102,15,97,192                       // punpcklwd     %xmm0,%xmm0
+  .byte  102,15,219,5,76,44,0,0              // pand          0x2c4c(%rip),%xmm0        # 6030 <_sk_callback_sse2+0xafc>
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,82,44,0,0                   // mulps         0x2c52(%rip),%xmm0        # 6040 <_sk_callback_sse2+0xb0c>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,89,44,0,0                  // movaps        0x2c59(%rip),%xmm3        # 6050 <_sk_callback_sse2+0xb1c>
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,48                              // je            3440 <_sk_load_g8_sse2+0x7c>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            342b <_sk_load_g8_sse2+0x67>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,192                             // jne           33dc <_sk_load_g8_sse2+0x18>
+  .byte  65,15,182,68,18,2                   // movzbl        0x2(%r10,%rdx,1),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,192,69                   // pshufd        $0x45,%xmm0,%xmm0
+  .byte  65,15,182,68,18,1                   // movzbl        0x1(%r10,%rdx,1),%eax
+  .byte  102,15,110,200                      // movd          %eax,%xmm1
+  .byte  15,198,200,0                        // shufps        $0x0,%xmm0,%xmm1
+  .byte  15,198,200,226                      // shufps        $0xe2,%xmm0,%xmm1
+  .byte  15,40,193                           // movaps        %xmm1,%xmm0
+  .byte  65,15,182,4,18                      // movzbl        (%r10,%rdx,1),%eax
+  .byte  102,15,110,200                      // movd          %eax,%xmm1
+  .byte  243,15,16,193                       // movss         %xmm1,%xmm0
+  .byte  235,141                             // jmp           33dc <_sk_load_g8_sse2+0x18>
+
+HIDDEN _sk_gather_g8_sse2
+.globl _sk_gather_g8_sse2
+FUNCTION(_sk_gather_g8_sse2)
+_sk_gather_g8_sse2:
+  .byte  85                                  // push          %rbp
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,72,15,126,192                   // movq          %xmm0,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,15,112,192,78                   // pshufd        $0x4e,%xmm0,%xmm0
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  65,15,182,44,25                     // movzbl        (%r9,%rbx,1),%ebp
+  .byte  67,15,182,28,25                     // movzbl        (%r9,%r11,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  9,235                               // or            %ebp,%ebx
+  .byte  67,15,182,44,17                     // movzbl        (%r9,%r10,1),%ebp
+  .byte  65,15,182,4,1                       // movzbl        (%r9,%rax,1),%eax
+  .byte  193,224,8                           // shl           $0x8,%eax
+  .byte  9,232                               // or            %ebp,%eax
+  .byte  102,15,196,192,0                    // pinsrw        $0x0,%eax,%xmm0
+  .byte  102,15,196,195,1                    // pinsrw        $0x1,%ebx,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,15,96,193                       // punpcklbw     %xmm1,%xmm0
+  .byte  102,15,97,193                       // punpcklwd     %xmm1,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,126,43,0,0                  // mulps         0x2b7e(%rip),%xmm0        # 6060 <_sk_callback_sse2+0xb2c>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,133,43,0,0                 // movaps        0x2b85(%rip),%xmm3        # 6070 <_sk_callback_sse2+0xb3c>
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  91                                  // pop           %rbx
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gather_i8_sse2
+.globl _sk_gather_i8_sse2
+FUNCTION(_sk_gather_i8_sse2)
+_sk_gather_i8_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,193                          // mov           %rax,%r9
+  .byte  77,133,201                          // test          %r9,%r9
+  .byte  116,5                               // je            3504 <_sk_gather_i8_sse2+0xf>
+  .byte  76,137,200                          // mov           %r9,%rax
+  .byte  235,2                               // jmp           3506 <_sk_gather_i8_sse2+0x11>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  85                                  // push          %rbp
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,72,15,126,192                   // movq          %xmm0,%rax
+  .byte  65,137,195                          // mov           %eax,%r11d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,15,112,192,78                   // pshufd        $0x4e,%xmm0,%xmm0
+  .byte  102,72,15,126,195                   // movq          %xmm0,%rbx
+  .byte  65,137,222                          // mov           %ebx,%r14d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  67,15,182,44,50                     // movzbl        (%r10,%r14,1),%ebp
+  .byte  65,15,182,28,26                     // movzbl        (%r10,%rbx,1),%ebx
+  .byte  193,227,8                           // shl           $0x8,%ebx
+  .byte  9,235                               // or            %ebp,%ebx
+  .byte  67,15,182,44,26                     // movzbl        (%r10,%r11,1),%ebp
+  .byte  65,15,182,4,2                       // movzbl        (%r10,%rax,1),%eax
+  .byte  193,224,8                           // shl           $0x8,%eax
+  .byte  9,232                               // or            %ebp,%eax
+  .byte  102,15,196,192,0                    // pinsrw        $0x0,%eax,%xmm0
+  .byte  102,15,196,195,1                    // pinsrw        $0x1,%ebx,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,15,96,193                       // punpcklbw     %xmm1,%xmm0
+  .byte  102,15,97,193                       // punpcklwd     %xmm1,%xmm0
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,72,15,126,200                   // movq          %xmm1,%rax
+  .byte  68,15,182,208                       // movzbl        %al,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,72,15,126,195                   // movq          %xmm0,%rbx
+  .byte  73,139,105,8                        // mov           0x8(%r9),%rbp
+  .byte  68,15,182,203                       // movzbl        %bl,%r9d
+  .byte  72,193,235,30                       // shr           $0x1e,%rbx
+  .byte  102,15,110,68,29,0                  // movd          0x0(%rbp,%rbx,1),%xmm0
+  .byte  102,15,110,76,133,0                 // movd          0x0(%rbp,%rax,4),%xmm1
+  .byte  102,15,98,193                       // punpckldq     %xmm1,%xmm0
+  .byte  102,70,15,110,76,141,0              // movd          0x0(%rbp,%r9,4),%xmm9
+  .byte  102,66,15,110,76,149,0              // movd          0x0(%rbp,%r10,4),%xmm1
+  .byte  102,68,15,98,201                    // punpckldq     %xmm1,%xmm9
+  .byte  102,68,15,98,200                    // punpckldq     %xmm0,%xmm9
+  .byte  102,15,111,21,158,42,0,0            // movdqa        0x2a9e(%rip),%xmm2        # 6080 <_sk_callback_sse2+0xb4c>
+  .byte  102,65,15,111,193                   // movdqa        %xmm9,%xmm0
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,154,42,0,0               // movaps        0x2a9a(%rip),%xmm8        # 6090 <_sk_callback_sse2+0xb5c>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,15,114,209,8                    // psrld         $0x8,%xmm1
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,65,15,111,217                   // movdqa        %xmm9,%xmm3
+  .byte  102,15,114,211,16                   // psrld         $0x10,%xmm3
+  .byte  102,15,219,218                      // pand          %xmm2,%xmm3
+  .byte  15,91,211                           // cvtdq2ps      %xmm3,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,65,15,114,209,24                // psrld         $0x18,%xmm9
+  .byte  65,15,91,217                        // cvtdq2ps      %xmm9,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_565_sse2
+.globl _sk_load_565_sse2
+FUNCTION(_sk_load_565_sse2)
+_sk_load_565_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,83                              // jne           3697 <_sk_load_565_sse2+0x5d>
+  .byte  243,65,15,126,20,82                 // movq          (%r10,%rdx,2),%xmm2
+  .byte  102,15,97,208                       // punpcklwd     %xmm0,%xmm2
+  .byte  102,15,111,5,74,42,0,0              // movdqa        0x2a4a(%rip),%xmm0        # 60a0 <_sk_callback_sse2+0xb6c>
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,76,42,0,0                   // mulps         0x2a4c(%rip),%xmm0        # 60b0 <_sk_callback_sse2+0xb7c>
+  .byte  102,15,111,13,84,42,0,0             // movdqa        0x2a54(%rip),%xmm1        # 60c0 <_sk_callback_sse2+0xb8c>
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,86,42,0,0                  // mulps         0x2a56(%rip),%xmm1        # 60d0 <_sk_callback_sse2+0xb9c>
+  .byte  102,15,219,21,94,42,0,0             // pand          0x2a5e(%rip),%xmm2        # 60e0 <_sk_callback_sse2+0xbac>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,100,42,0,0                 // mulps         0x2a64(%rip),%xmm2        # 60f0 <_sk_callback_sse2+0xbbc>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,107,42,0,0                 // movaps        0x2a6b(%rip),%xmm3        # 6100 <_sk_callback_sse2+0xbcc>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,210                      // pxor          %xmm2,%xmm2
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,48                              // je            36d8 <_sk_load_565_sse2+0x9e>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            36c3 <_sk_load_565_sse2+0x89>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,154                             // jne           364e <_sk_load_565_sse2+0x14>
+  .byte  65,15,183,68,82,4                   // movzwl        0x4(%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,208,69                   // pshufd        $0x45,%xmm0,%xmm2
+  .byte  65,15,183,68,82,2                   // movzwl        0x2(%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  15,198,194,0                        // shufps        $0x0,%xmm2,%xmm0
+  .byte  15,198,194,226                      // shufps        $0xe2,%xmm2,%xmm0
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  65,15,183,4,82                      // movzwl        (%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  243,15,16,208                       // movss         %xmm0,%xmm2
+  .byte  233,100,255,255,255                 // jmpq          364e <_sk_load_565_sse2+0x14>
+
+HIDDEN _sk_gather_565_sse2
+.globl _sk_gather_565_sse2
+FUNCTION(_sk_gather_565_sse2)
+_sk_gather_565_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,72,15,126,200                   // movq          %xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,65,15,196,20,89,0               // pinsrw        $0x0,(%r9,%rbx,2),%xmm2
+  .byte  102,67,15,196,20,89,1               // pinsrw        $0x1,(%r9,%r11,2),%xmm2
+  .byte  67,15,183,28,81                     // movzwl        (%r9,%r10,2),%ebx
+  .byte  102,15,196,211,2                    // pinsrw        $0x2,%ebx,%xmm2
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  102,15,196,208,3                    // pinsrw        $0x3,%eax,%xmm2
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  102,15,97,208                       // punpcklwd     %xmm0,%xmm2
+  .byte  102,15,111,5,160,41,0,0             // movdqa        0x29a0(%rip),%xmm0        # 6110 <_sk_callback_sse2+0xbdc>
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,162,41,0,0                  // mulps         0x29a2(%rip),%xmm0        # 6120 <_sk_callback_sse2+0xbec>
+  .byte  102,15,111,13,170,41,0,0            // movdqa        0x29aa(%rip),%xmm1        # 6130 <_sk_callback_sse2+0xbfc>
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,172,41,0,0                 // mulps         0x29ac(%rip),%xmm1        # 6140 <_sk_callback_sse2+0xc0c>
+  .byte  102,15,219,21,180,41,0,0            // pand          0x29b4(%rip),%xmm2        # 6150 <_sk_callback_sse2+0xc1c>
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,186,41,0,0                 // mulps         0x29ba(%rip),%xmm2        # 6160 <_sk_callback_sse2+0xc2c>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,193,41,0,0                 // movaps        0x29c1(%rip),%xmm3        # 6170 <_sk_callback_sse2+0xc3c>
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_565_sse2
+.globl _sk_store_565_sse2
+FUNCTION(_sk_store_565_sse2)
+_sk_store_565_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  68,15,40,5,193,41,0,0               // movaps        0x29c1(%rip),%xmm8        # 6180 <_sk_callback_sse2+0xc4c>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,11                // pslld         $0xb,%xmm9
+  .byte  68,15,40,21,182,41,0,0              // movaps        0x29b6(%rip),%xmm10        # 6190 <_sk_callback_sse2+0xc5c>
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,5                 // pslld         $0x5,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,69,15,86,194                    // orpd          %xmm10,%xmm8
+  .byte  102,65,15,114,240,16                // pslld         $0x10,%xmm8
+  .byte  102,65,15,114,224,16                // psrad         $0x10,%xmm8
+  .byte  102,69,15,107,192                   // packssdw      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           381c <_sk_store_565_sse2+0x6a>
+  .byte  242,69,15,17,4,82                   // movsd         %xmm8,(%r10,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,68,15,97,192                    // punpcklwd     %xmm0,%xmm8
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,36                              // je            3852 <_sk_store_565_sse2+0xa0>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,18                              // je            3846 <_sk_store_565_sse2+0x94>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,222                             // jne           3818 <_sk_store_565_sse2+0x66>
+  .byte  102,65,15,197,192,4                 // pextrw        $0x4,%xmm8,%eax
+  .byte  102,65,137,68,82,4                  // mov           %ax,0x4(%r10,%rdx,2)
+  .byte  102,65,15,197,192,2                 // pextrw        $0x2,%xmm8,%eax
+  .byte  102,65,137,68,82,2                  // mov           %ax,0x2(%r10,%rdx,2)
+  .byte  102,68,15,126,192                   // movd          %xmm8,%eax
+  .byte  102,65,137,4,82                     // mov           %ax,(%r10,%rdx,2)
+  .byte  235,186                             // jmp           3818 <_sk_store_565_sse2+0x66>
+
+HIDDEN _sk_load_4444_sse2
+.globl _sk_load_4444_sse2
+FUNCTION(_sk_load_4444_sse2)
+_sk_load_4444_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,98                              // jne           38ca <_sk_load_4444_sse2+0x6c>
+  .byte  243,65,15,126,28,82                 // movq          (%r10,%rdx,2),%xmm3
+  .byte  102,15,97,216                       // punpcklwd     %xmm0,%xmm3
+  .byte  102,15,111,5,38,41,0,0              // movdqa        0x2926(%rip),%xmm0        # 61a0 <_sk_callback_sse2+0xc6c>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,40,41,0,0                   // mulps         0x2928(%rip),%xmm0        # 61b0 <_sk_callback_sse2+0xc7c>
+  .byte  102,15,111,13,48,41,0,0             // movdqa        0x2930(%rip),%xmm1        # 61c0 <_sk_callback_sse2+0xc8c>
+  .byte  102,15,219,203                      // pand          %xmm3,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,50,41,0,0                  // mulps         0x2932(%rip),%xmm1        # 61d0 <_sk_callback_sse2+0xc9c>
+  .byte  102,15,111,21,58,41,0,0             // movdqa        0x293a(%rip),%xmm2        # 61e0 <_sk_callback_sse2+0xcac>
+  .byte  102,15,219,211                      // pand          %xmm3,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,60,41,0,0                  // mulps         0x293c(%rip),%xmm2        # 61f0 <_sk_callback_sse2+0xcbc>
+  .byte  102,15,219,29,68,41,0,0             // pand          0x2944(%rip),%xmm3        # 6200 <_sk_callback_sse2+0xccc>
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  15,89,29,74,41,0,0                  // mulps         0x294a(%rip),%xmm3        # 6210 <_sk_callback_sse2+0xcdc>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,48                              // je            390b <_sk_load_4444_sse2+0xad>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,21                              // je            38f6 <_sk_load_4444_sse2+0x98>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,139                             // jne           3872 <_sk_load_4444_sse2+0x14>
+  .byte  65,15,183,68,82,4                   // movzwl        0x4(%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  102,15,112,216,69                   // pshufd        $0x45,%xmm0,%xmm3
+  .byte  65,15,183,68,82,2                   // movzwl        0x2(%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  15,198,195,0                        // shufps        $0x0,%xmm3,%xmm0
+  .byte  15,198,195,226                      // shufps        $0xe2,%xmm3,%xmm0
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  65,15,183,4,82                      // movzwl        (%r10,%rdx,2),%eax
+  .byte  102,15,110,192                      // movd          %eax,%xmm0
+  .byte  243,15,16,216                       // movss         %xmm0,%xmm3
+  .byte  233,85,255,255,255                  // jmpq          3872 <_sk_load_4444_sse2+0x14>
+
+HIDDEN _sk_gather_4444_sse2
+.globl _sk_gather_4444_sse2
+FUNCTION(_sk_gather_4444_sse2)
+_sk_gather_4444_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,72,15,126,200                   // movq          %xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,65,15,196,28,89,0               // pinsrw        $0x0,(%r9,%rbx,2),%xmm3
+  .byte  102,67,15,196,28,89,1               // pinsrw        $0x1,(%r9,%r11,2),%xmm3
+  .byte  67,15,183,28,81                     // movzwl        (%r9,%r10,2),%ebx
+  .byte  102,15,196,219,2                    // pinsrw        $0x2,%ebx,%xmm3
+  .byte  65,15,183,4,65                      // movzwl        (%r9,%rax,2),%eax
+  .byte  102,15,196,216,3                    // pinsrw        $0x3,%eax,%xmm3
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  102,15,97,216                       // punpcklwd     %xmm0,%xmm3
+  .byte  102,15,111,5,125,40,0,0             // movdqa        0x287d(%rip),%xmm0        # 6220 <_sk_callback_sse2+0xcec>
+  .byte  102,15,219,195                      // pand          %xmm3,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  15,89,5,127,40,0,0                  // mulps         0x287f(%rip),%xmm0        # 6230 <_sk_callback_sse2+0xcfc>
+  .byte  102,15,111,13,135,40,0,0            // movdqa        0x2887(%rip),%xmm1        # 6240 <_sk_callback_sse2+0xd0c>
+  .byte  102,15,219,203                      // pand          %xmm3,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  15,89,13,137,40,0,0                 // mulps         0x2889(%rip),%xmm1        # 6250 <_sk_callback_sse2+0xd1c>
+  .byte  102,15,111,21,145,40,0,0            // movdqa        0x2891(%rip),%xmm2        # 6260 <_sk_callback_sse2+0xd2c>
+  .byte  102,15,219,211                      // pand          %xmm3,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  15,89,21,147,40,0,0                 // mulps         0x2893(%rip),%xmm2        # 6270 <_sk_callback_sse2+0xd3c>
+  .byte  102,15,219,29,155,40,0,0            // pand          0x289b(%rip),%xmm3        # 6280 <_sk_callback_sse2+0xd4c>
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  15,89,29,161,40,0,0                 // mulps         0x28a1(%rip),%xmm3        # 6290 <_sk_callback_sse2+0xd5c>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_4444_sse2
+.globl _sk_store_4444_sse2
+FUNCTION(_sk_store_4444_sse2)
+_sk_store_4444_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  68,15,40,5,159,40,0,0               // movaps        0x289f(%rip),%xmm8        # 62a0 <_sk_callback_sse2+0xd6c>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,12                // pslld         $0xc,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,8                 // pslld         $0x8,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,4                 // pslld         $0x4,%xmm9
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,69,15,86,193                    // orpd          %xmm9,%xmm8
+  .byte  102,69,15,86,194                    // orpd          %xmm10,%xmm8
+  .byte  102,65,15,114,240,16                // pslld         $0x10,%xmm8
+  .byte  102,65,15,114,224,16                // psrad         $0x10,%xmm8
+  .byte  102,69,15,107,192                   // packssdw      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3a72 <_sk_store_4444_sse2+0x7e>
+  .byte  242,69,15,17,4,82                   // movsd         %xmm8,(%r10,%rdx,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,68,15,97,192                    // punpcklwd     %xmm0,%xmm8
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,36                              // je            3aa8 <_sk_store_4444_sse2+0xb4>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,18                              // je            3a9c <_sk_store_4444_sse2+0xa8>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,222                             // jne           3a6e <_sk_store_4444_sse2+0x7a>
+  .byte  102,65,15,197,192,4                 // pextrw        $0x4,%xmm8,%eax
+  .byte  102,65,137,68,82,4                  // mov           %ax,0x4(%r10,%rdx,2)
+  .byte  102,65,15,197,192,2                 // pextrw        $0x2,%xmm8,%eax
+  .byte  102,65,137,68,82,2                  // mov           %ax,0x2(%r10,%rdx,2)
+  .byte  102,68,15,126,192                   // movd          %xmm8,%eax
+  .byte  102,65,137,4,82                     // mov           %ax,(%r10,%rdx,2)
+  .byte  235,186                             // jmp           3a6e <_sk_store_4444_sse2+0x7a>
+
+HIDDEN _sk_load_8888_sse2
+.globl _sk_load_8888_sse2
+FUNCTION(_sk_load_8888_sse2)
+_sk_load_8888_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,98                              // jne           3b20 <_sk_load_8888_sse2+0x6c>
+  .byte  243,68,15,111,12,144                // movdqu        (%rax,%rdx,4),%xmm9
+  .byte  102,15,111,21,228,39,0,0            // movdqa        0x27e4(%rip),%xmm2        # 62b0 <_sk_callback_sse2+0xd7c>
+  .byte  102,65,15,111,193                   // movdqa        %xmm9,%xmm0
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,224,39,0,0               // movaps        0x27e0(%rip),%xmm8        # 62c0 <_sk_callback_sse2+0xd8c>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,15,114,209,8                    // psrld         $0x8,%xmm1
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,65,15,111,217                   // movdqa        %xmm9,%xmm3
+  .byte  102,15,114,211,16                   // psrld         $0x10,%xmm3
+  .byte  102,15,219,218                      // pand          %xmm2,%xmm3
+  .byte  15,91,211                           // cvtdq2ps      %xmm3,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,65,15,114,209,24                // psrld         $0x18,%xmm9
+  .byte  65,15,91,217                        // cvtdq2ps      %xmm9,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,44                              // je            3b5e <_sk_load_8888_sse2+0xaa>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,18                              // je            3b4a <_sk_load_8888_sse2+0x96>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,134                             // jne           3ac4 <_sk_load_8888_sse2+0x10>
+  .byte  102,15,110,68,144,8                 // movd          0x8(%rax,%rdx,4),%xmm0
+  .byte  102,68,15,112,200,69                // pshufd        $0x45,%xmm0,%xmm9
+  .byte  243,15,16,68,144,4                  // movss         0x4(%rax,%rdx,4),%xmm0
+  .byte  65,15,198,193,0                     // shufps        $0x0,%xmm9,%xmm0
+  .byte  65,15,198,193,226                   // shufps        $0xe2,%xmm9,%xmm0
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  243,15,16,4,144                     // movss         (%rax,%rdx,4),%xmm0
+  .byte  243,68,15,16,200                    // movss         %xmm0,%xmm9
+  .byte  233,87,255,255,255                  // jmpq          3ac4 <_sk_load_8888_sse2+0x10>
+
+HIDDEN _sk_gather_8888_sse2
+.globl _sk_gather_8888_sse2
+FUNCTION(_sk_gather_8888_sse2)
+_sk_gather_8888_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,72,15,126,200                   // movq          %xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,67,15,110,4,153                 // movd          (%r9,%r11,4),%xmm0
+  .byte  102,65,15,110,12,129                // movd          (%r9,%rax,4),%xmm1
+  .byte  102,15,98,193                       // punpckldq     %xmm1,%xmm0
+  .byte  102,69,15,110,12,153                // movd          (%r9,%rbx,4),%xmm9
+  .byte  102,67,15,110,12,145                // movd          (%r9,%r10,4),%xmm1
+  .byte  102,68,15,98,201                    // punpckldq     %xmm1,%xmm9
+  .byte  102,68,15,98,200                    // punpckldq     %xmm0,%xmm9
+  .byte  102,15,111,21,225,38,0,0            // movdqa        0x26e1(%rip),%xmm2        # 62d0 <_sk_callback_sse2+0xd9c>
+  .byte  102,65,15,111,193                   // movdqa        %xmm9,%xmm0
+  .byte  102,15,219,194                      // pand          %xmm2,%xmm0
+  .byte  15,91,192                           // cvtdq2ps      %xmm0,%xmm0
+  .byte  68,15,40,5,221,38,0,0               // movaps        0x26dd(%rip),%xmm8        # 62e0 <_sk_callback_sse2+0xdac>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,15,114,209,8                    // psrld         $0x8,%xmm1
+  .byte  102,15,219,202                      // pand          %xmm2,%xmm1
+  .byte  15,91,201                           // cvtdq2ps      %xmm1,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,65,15,111,217                   // movdqa        %xmm9,%xmm3
+  .byte  102,15,114,211,16                   // psrld         $0x10,%xmm3
+  .byte  102,15,219,218                      // pand          %xmm2,%xmm3
+  .byte  15,91,211                           // cvtdq2ps      %xmm3,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,65,15,114,209,24                // psrld         $0x18,%xmm9
+  .byte  65,15,91,217                        // cvtdq2ps      %xmm9,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_8888_sse2
+.globl _sk_store_8888_sse2
+FUNCTION(_sk_store_8888_sse2)
+_sk_store_8888_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  68,15,40,5,159,38,0,0               // movaps        0x269f(%rip),%xmm8        # 62f0 <_sk_callback_sse2+0xdbc>
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,8                 // pslld         $0x8,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,16                // pslld         $0x10,%xmm9
+  .byte  68,15,89,195                        // mulps         %xmm3,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,114,240,24                // pslld         $0x18,%xmm8
+  .byte  102,69,15,235,193                   // por           %xmm9,%xmm8
+  .byte  102,69,15,235,194                   // por           %xmm10,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           3cb1 <_sk_store_8888_sse2+0x6d>
+  .byte  243,68,15,127,4,144                 // movdqu        %xmm8,(%rax,%rdx,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,3                        // and           $0x3,%r9b
+  .byte  65,128,249,1                        // cmp           $0x1,%r9b
+  .byte  116,38                              // je            3ce4 <_sk_store_8888_sse2+0xa0>
+  .byte  65,128,249,2                        // cmp           $0x2,%r9b
+  .byte  116,19                              // je            3cd7 <_sk_store_8888_sse2+0x93>
+  .byte  65,128,249,3                        // cmp           $0x3,%r9b
+  .byte  117,227                             // jne           3cad <_sk_store_8888_sse2+0x69>
+  .byte  102,69,15,112,200,78                // pshufd        $0x4e,%xmm8,%xmm9
+  .byte  102,68,15,126,76,144,8              // movd          %xmm9,0x8(%rax,%rdx,4)
+  .byte  102,69,15,112,200,229               // pshufd        $0xe5,%xmm8,%xmm9
+  .byte  102,68,15,126,76,144,4              // movd          %xmm9,0x4(%rax,%rdx,4)
+  .byte  102,68,15,126,4,144                 // movd          %xmm8,(%rax,%rdx,4)
+  .byte  235,193                             // jmp           3cad <_sk_store_8888_sse2+0x69>
+
+HIDDEN _sk_load_f16_sse2
+.globl _sk_load_f16_sse2
+FUNCTION(_sk_load_f16_sse2)
+_sk_load_f16_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,96,1,0,0                     // jne           3e5a <_sk_load_f16_sse2+0x16e>
+  .byte  102,15,16,4,208                     // movupd        (%rax,%rdx,8),%xmm0
+  .byte  102,15,16,76,208,16                 // movupd        0x10(%rax,%rdx,8),%xmm1
+  .byte  102,68,15,40,192                    // movapd        %xmm0,%xmm8
+  .byte  102,68,15,97,193                    // punpcklwd     %xmm1,%xmm8
+  .byte  102,15,105,193                      // punpckhwd     %xmm1,%xmm0
+  .byte  102,69,15,111,240                   // movdqa        %xmm8,%xmm14
+  .byte  102,68,15,97,240                    // punpcklwd     %xmm0,%xmm14
+  .byte  102,68,15,105,192                   // punpckhwd     %xmm0,%xmm8
+  .byte  102,69,15,239,210                   // pxor          %xmm10,%xmm10
+  .byte  102,65,15,111,206                   // movdqa        %xmm14,%xmm1
+  .byte  102,65,15,97,202                    // punpcklwd     %xmm10,%xmm1
+  .byte  102,68,15,111,13,198,37,0,0         // movdqa        0x25c6(%rip),%xmm9        # 6300 <_sk_callback_sse2+0xdcc>
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,65,15,219,193                   // pand          %xmm9,%xmm0
+  .byte  102,15,239,200                      // pxor          %xmm0,%xmm1
+  .byte  102,15,114,240,16                   // pslld         $0x10,%xmm0
+  .byte  102,68,15,111,233                   // movdqa        %xmm1,%xmm13
+  .byte  102,65,15,114,245,13                // pslld         $0xd,%xmm13
+  .byte  102,68,15,235,232                   // por           %xmm0,%xmm13
+  .byte  102,68,15,111,29,171,37,0,0         // movdqa        0x25ab(%rip),%xmm11        # 6310 <_sk_callback_sse2+0xddc>
+  .byte  102,69,15,254,235                   // paddd         %xmm11,%xmm13
+  .byte  102,68,15,111,37,173,37,0,0         // movdqa        0x25ad(%rip),%xmm12        # 6320 <_sk_callback_sse2+0xdec>
+  .byte  102,65,15,239,204                   // pxor          %xmm12,%xmm1
+  .byte  102,15,111,29,176,37,0,0            // movdqa        0x25b0(%rip),%xmm3        # 6330 <_sk_callback_sse2+0xdfc>
+  .byte  102,15,111,195                      // movdqa        %xmm3,%xmm0
+  .byte  102,15,102,193                      // pcmpgtd       %xmm1,%xmm0
+  .byte  102,65,15,223,197                   // pandn         %xmm13,%xmm0
+  .byte  102,65,15,115,222,8                 // psrldq        $0x8,%xmm14
+  .byte  102,69,15,97,242                    // punpcklwd     %xmm10,%xmm14
+  .byte  102,65,15,111,206                   // movdqa        %xmm14,%xmm1
+  .byte  102,65,15,219,201                   // pand          %xmm9,%xmm1
+  .byte  102,68,15,239,241                   // pxor          %xmm1,%xmm14
+  .byte  102,15,114,241,16                   // pslld         $0x10,%xmm1
+  .byte  102,65,15,111,214                   // movdqa        %xmm14,%xmm2
+  .byte  102,15,114,242,13                   // pslld         $0xd,%xmm2
+  .byte  102,15,235,209                      // por           %xmm1,%xmm2
+  .byte  102,65,15,254,211                   // paddd         %xmm11,%xmm2
+  .byte  102,69,15,239,244                   // pxor          %xmm12,%xmm14
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,65,15,102,206                   // pcmpgtd       %xmm14,%xmm1
+  .byte  102,15,223,202                      // pandn         %xmm2,%xmm1
+  .byte  102,69,15,111,232                   // movdqa        %xmm8,%xmm13
+  .byte  102,69,15,97,234                    // punpcklwd     %xmm10,%xmm13
+  .byte  102,65,15,111,213                   // movdqa        %xmm13,%xmm2
+  .byte  102,65,15,219,209                   // pand          %xmm9,%xmm2
+  .byte  102,68,15,239,234                   // pxor          %xmm2,%xmm13
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,69,15,111,245                   // movdqa        %xmm13,%xmm14
+  .byte  102,65,15,114,246,13                // pslld         $0xd,%xmm14
+  .byte  102,68,15,235,242                   // por           %xmm2,%xmm14
+  .byte  102,69,15,254,243                   // paddd         %xmm11,%xmm14
+  .byte  102,69,15,239,236                   // pxor          %xmm12,%xmm13
+  .byte  102,15,111,211                      // movdqa        %xmm3,%xmm2
+  .byte  102,65,15,102,213                   // pcmpgtd       %xmm13,%xmm2
+  .byte  102,65,15,223,214                   // pandn         %xmm14,%xmm2
+  .byte  102,65,15,115,216,8                 // psrldq        $0x8,%xmm8
+  .byte  102,69,15,97,194                    // punpcklwd     %xmm10,%xmm8
+  .byte  102,69,15,219,200                   // pand          %xmm8,%xmm9
+  .byte  102,69,15,239,193                   // pxor          %xmm9,%xmm8
+  .byte  102,65,15,114,241,16                // pslld         $0x10,%xmm9
+  .byte  102,69,15,111,208                   // movdqa        %xmm8,%xmm10
+  .byte  102,65,15,114,242,13                // pslld         $0xd,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  102,69,15,254,211                   // paddd         %xmm11,%xmm10
+  .byte  102,69,15,239,196                   // pxor          %xmm12,%xmm8
+  .byte  102,65,15,102,216                   // pcmpgtd       %xmm8,%xmm3
+  .byte  102,65,15,223,218                   // pandn         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  242,15,16,4,208                     // movsd         (%rax,%rdx,8),%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,17                              // jne           3e76 <_sk_load_f16_sse2+0x18a>
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  233,143,254,255,255                 // jmpq          3d05 <_sk_load_f16_sse2+0x19>
+  .byte  102,15,22,68,208,8                  // movhpd        0x8(%rax,%rdx,8),%xmm0
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  15,130,123,254,255,255              // jb            3d05 <_sk_load_f16_sse2+0x19>
+  .byte  242,15,16,76,208,16                 // movsd         0x10(%rax,%rdx,8),%xmm1
+  .byte  233,112,254,255,255                 // jmpq          3d05 <_sk_load_f16_sse2+0x19>
+
+HIDDEN _sk_gather_f16_sse2
+.globl _sk_gather_f16_sse2
+FUNCTION(_sk_gather_f16_sse2)
+_sk_gather_f16_sse2:
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,110,80,16                    // movd          0x10(%rax),%xmm2
+  .byte  102,15,112,210,0                    // pshufd        $0x0,%xmm2,%xmm2
+  .byte  102,15,112,217,245                  // pshufd        $0xf5,%xmm1,%xmm3
+  .byte  102,15,244,218                      // pmuludq       %xmm2,%xmm3
+  .byte  102,15,112,219,232                  // pshufd        $0xe8,%xmm3,%xmm3
+  .byte  102,15,244,209                      // pmuludq       %xmm1,%xmm2
+  .byte  102,15,112,202,232                  // pshufd        $0xe8,%xmm2,%xmm1
+  .byte  102,15,98,203                       // punpckldq     %xmm3,%xmm1
+  .byte  243,15,91,192                       // cvttps2dq     %xmm0,%xmm0
+  .byte  102,15,254,193                      // paddd         %xmm1,%xmm0
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,72,15,126,200                   // movq          %xmm1,%rax
+  .byte  65,137,194                          // mov           %eax,%r10d
+  .byte  72,193,232,32                       // shr           $0x20,%rax
+  .byte  102,73,15,126,195                   // movq          %xmm0,%r11
+  .byte  68,137,219                          // mov           %r11d,%ebx
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  243,67,15,126,4,217                 // movq          (%r9,%r11,8),%xmm0
+  .byte  243,65,15,126,12,217                // movq          (%r9,%rbx,8),%xmm1
+  .byte  102,15,108,200                      // punpcklqdq    %xmm0,%xmm1
+  .byte  243,65,15,126,4,193                 // movq          (%r9,%rax,8),%xmm0
+  .byte  243,67,15,126,20,209                // movq          (%r9,%r10,8),%xmm2
+  .byte  102,15,108,208                      // punpcklqdq    %xmm0,%xmm2
+  .byte  102,68,15,111,193                   // movdqa        %xmm1,%xmm8
+  .byte  102,68,15,97,194                    // punpcklwd     %xmm2,%xmm8
+  .byte  102,15,105,202                      // punpckhwd     %xmm2,%xmm1
+  .byte  102,69,15,111,240                   // movdqa        %xmm8,%xmm14
+  .byte  102,68,15,97,241                    // punpcklwd     %xmm1,%xmm14
+  .byte  102,68,15,105,193                   // punpckhwd     %xmm1,%xmm8
+  .byte  102,69,15,239,210                   // pxor          %xmm10,%xmm10
+  .byte  102,65,15,111,206                   // movdqa        %xmm14,%xmm1
+  .byte  102,65,15,97,202                    // punpcklwd     %xmm10,%xmm1
+  .byte  102,68,15,111,13,2,36,0,0           // movdqa        0x2402(%rip),%xmm9        # 6340 <_sk_callback_sse2+0xe0c>
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,65,15,219,193                   // pand          %xmm9,%xmm0
+  .byte  102,15,239,200                      // pxor          %xmm0,%xmm1
+  .byte  102,15,114,240,16                   // pslld         $0x10,%xmm0
+  .byte  102,68,15,111,233                   // movdqa        %xmm1,%xmm13
+  .byte  102,65,15,114,245,13                // pslld         $0xd,%xmm13
+  .byte  102,68,15,235,232                   // por           %xmm0,%xmm13
+  .byte  102,68,15,111,29,231,35,0,0         // movdqa        0x23e7(%rip),%xmm11        # 6350 <_sk_callback_sse2+0xe1c>
+  .byte  102,69,15,254,235                   // paddd         %xmm11,%xmm13
+  .byte  102,68,15,111,37,233,35,0,0         // movdqa        0x23e9(%rip),%xmm12        # 6360 <_sk_callback_sse2+0xe2c>
+  .byte  102,65,15,239,204                   // pxor          %xmm12,%xmm1
+  .byte  102,15,111,29,236,35,0,0            // movdqa        0x23ec(%rip),%xmm3        # 6370 <_sk_callback_sse2+0xe3c>
+  .byte  102,15,111,195                      // movdqa        %xmm3,%xmm0
+  .byte  102,15,102,193                      // pcmpgtd       %xmm1,%xmm0
+  .byte  102,65,15,223,197                   // pandn         %xmm13,%xmm0
+  .byte  102,65,15,115,222,8                 // psrldq        $0x8,%xmm14
+  .byte  102,69,15,97,242                    // punpcklwd     %xmm10,%xmm14
+  .byte  102,65,15,111,206                   // movdqa        %xmm14,%xmm1
+  .byte  102,65,15,219,201                   // pand          %xmm9,%xmm1
+  .byte  102,68,15,239,241                   // pxor          %xmm1,%xmm14
+  .byte  102,15,114,241,16                   // pslld         $0x10,%xmm1
+  .byte  102,65,15,111,214                   // movdqa        %xmm14,%xmm2
+  .byte  102,15,114,242,13                   // pslld         $0xd,%xmm2
+  .byte  102,15,235,209                      // por           %xmm1,%xmm2
+  .byte  102,65,15,254,211                   // paddd         %xmm11,%xmm2
+  .byte  102,69,15,239,244                   // pxor          %xmm12,%xmm14
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,65,15,102,206                   // pcmpgtd       %xmm14,%xmm1
+  .byte  102,15,223,202                      // pandn         %xmm2,%xmm1
+  .byte  102,69,15,111,232                   // movdqa        %xmm8,%xmm13
+  .byte  102,69,15,97,234                    // punpcklwd     %xmm10,%xmm13
+  .byte  102,65,15,111,213                   // movdqa        %xmm13,%xmm2
+  .byte  102,65,15,219,209                   // pand          %xmm9,%xmm2
+  .byte  102,68,15,239,234                   // pxor          %xmm2,%xmm13
+  .byte  102,15,114,242,16                   // pslld         $0x10,%xmm2
+  .byte  102,69,15,111,245                   // movdqa        %xmm13,%xmm14
+  .byte  102,65,15,114,246,13                // pslld         $0xd,%xmm14
+  .byte  102,68,15,235,242                   // por           %xmm2,%xmm14
+  .byte  102,69,15,254,243                   // paddd         %xmm11,%xmm14
+  .byte  102,69,15,239,236                   // pxor          %xmm12,%xmm13
+  .byte  102,15,111,211                      // movdqa        %xmm3,%xmm2
+  .byte  102,65,15,102,213                   // pcmpgtd       %xmm13,%xmm2
+  .byte  102,65,15,223,214                   // pandn         %xmm14,%xmm2
+  .byte  102,65,15,115,216,8                 // psrldq        $0x8,%xmm8
+  .byte  102,69,15,97,194                    // punpcklwd     %xmm10,%xmm8
+  .byte  102,69,15,219,200                   // pand          %xmm8,%xmm9
+  .byte  102,69,15,239,193                   // pxor          %xmm9,%xmm8
+  .byte  102,65,15,114,241,16                // pslld         $0x10,%xmm9
+  .byte  102,69,15,111,208                   // movdqa        %xmm8,%xmm10
+  .byte  102,65,15,114,242,13                // pslld         $0xd,%xmm10
+  .byte  102,69,15,235,209                   // por           %xmm9,%xmm10
+  .byte  102,69,15,254,211                   // paddd         %xmm11,%xmm10
+  .byte  102,69,15,239,196                   // pxor          %xmm12,%xmm8
+  .byte  102,65,15,102,216                   // pcmpgtd       %xmm8,%xmm3
+  .byte  102,65,15,223,218                   // pandn         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  91                                  // pop           %rbx
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_store_f16_sse2
+.globl _sk_store_f16_sse2
+FUNCTION(_sk_store_f16_sse2)
+_sk_store_f16_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,0                            // mov           (%rax),%rax
+  .byte  102,68,15,111,21,19,35,0,0          // movdqa        0x2313(%rip),%xmm10        # 6380 <_sk_callback_sse2+0xe4c>
+  .byte  102,68,15,111,224                   // movdqa        %xmm0,%xmm12
+  .byte  102,69,15,219,226                   // pand          %xmm10,%xmm12
+  .byte  102,68,15,111,232                   // movdqa        %xmm0,%xmm13
+  .byte  102,69,15,239,236                   // pxor          %xmm12,%xmm13
+  .byte  102,68,15,111,13,6,35,0,0           // movdqa        0x2306(%rip),%xmm9        # 6390 <_sk_callback_sse2+0xe5c>
+  .byte  102,65,15,114,212,16                // psrld         $0x10,%xmm12
+  .byte  102,69,15,111,193                   // movdqa        %xmm9,%xmm8
+  .byte  102,69,15,102,197                   // pcmpgtd       %xmm13,%xmm8
+  .byte  102,65,15,114,213,13                // psrld         $0xd,%xmm13
+  .byte  102,68,15,111,29,247,34,0,0         // movdqa        0x22f7(%rip),%xmm11        # 63a0 <_sk_callback_sse2+0xe6c>
+  .byte  102,69,15,235,227                   // por           %xmm11,%xmm12
+  .byte  102,69,15,254,229                   // paddd         %xmm13,%xmm12
+  .byte  102,65,15,114,244,16                // pslld         $0x10,%xmm12
+  .byte  102,65,15,114,228,16                // psrad         $0x10,%xmm12
+  .byte  102,69,15,223,196                   // pandn         %xmm12,%xmm8
+  .byte  102,69,15,107,192                   // packssdw      %xmm8,%xmm8
+  .byte  102,68,15,111,225                   // movdqa        %xmm1,%xmm12
+  .byte  102,69,15,219,226                   // pand          %xmm10,%xmm12
+  .byte  102,68,15,111,241                   // movdqa        %xmm1,%xmm14
+  .byte  102,69,15,239,244                   // pxor          %xmm12,%xmm14
+  .byte  102,65,15,114,212,16                // psrld         $0x10,%xmm12
+  .byte  102,69,15,111,233                   // movdqa        %xmm9,%xmm13
+  .byte  102,69,15,102,238                   // pcmpgtd       %xmm14,%xmm13
+  .byte  102,65,15,114,214,13                // psrld         $0xd,%xmm14
+  .byte  102,69,15,235,227                   // por           %xmm11,%xmm12
+  .byte  102,69,15,254,230                   // paddd         %xmm14,%xmm12
+  .byte  102,65,15,114,244,16                // pslld         $0x10,%xmm12
+  .byte  102,65,15,114,228,16                // psrad         $0x10,%xmm12
+  .byte  102,69,15,223,236                   // pandn         %xmm12,%xmm13
+  .byte  102,69,15,107,237                   // packssdw      %xmm13,%xmm13
+  .byte  102,68,15,111,242                   // movdqa        %xmm2,%xmm14
+  .byte  102,69,15,219,242                   // pand          %xmm10,%xmm14
+  .byte  102,68,15,111,250                   // movdqa        %xmm2,%xmm15
+  .byte  102,69,15,239,254                   // pxor          %xmm14,%xmm15
+  .byte  102,65,15,114,214,16                // psrld         $0x10,%xmm14
+  .byte  102,69,15,111,225                   // movdqa        %xmm9,%xmm12
+  .byte  102,69,15,102,231                   // pcmpgtd       %xmm15,%xmm12
+  .byte  102,65,15,114,215,13                // psrld         $0xd,%xmm15
+  .byte  102,69,15,235,243                   // por           %xmm11,%xmm14
+  .byte  102,69,15,254,247                   // paddd         %xmm15,%xmm14
+  .byte  102,65,15,114,246,16                // pslld         $0x10,%xmm14
+  .byte  102,65,15,114,230,16                // psrad         $0x10,%xmm14
+  .byte  102,69,15,223,230                   // pandn         %xmm14,%xmm12
+  .byte  102,69,15,107,228                   // packssdw      %xmm12,%xmm12
+  .byte  102,68,15,219,211                   // pand          %xmm3,%xmm10
+  .byte  102,68,15,111,243                   // movdqa        %xmm3,%xmm14
+  .byte  102,69,15,239,242                   // pxor          %xmm10,%xmm14
+  .byte  102,65,15,114,210,16                // psrld         $0x10,%xmm10
+  .byte  102,69,15,102,206                   // pcmpgtd       %xmm14,%xmm9
+  .byte  102,65,15,114,214,13                // psrld         $0xd,%xmm14
+  .byte  102,69,15,235,211                   // por           %xmm11,%xmm10
+  .byte  102,69,15,254,214                   // paddd         %xmm14,%xmm10
+  .byte  102,65,15,114,242,16                // pslld         $0x10,%xmm10
+  .byte  102,65,15,114,226,16                // psrad         $0x10,%xmm10
+  .byte  102,69,15,223,202                   // pandn         %xmm10,%xmm9
+  .byte  102,69,15,107,201                   // packssdw      %xmm9,%xmm9
+  .byte  102,69,15,97,197                    // punpcklwd     %xmm13,%xmm8
+  .byte  102,69,15,97,225                    // punpcklwd     %xmm9,%xmm12
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,69,15,98,204                    // punpckldq     %xmm12,%xmm9
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,21                              // jne           41cb <_sk_store_f16_sse2+0x16c>
+  .byte  68,15,17,12,208                     // movups        %xmm9,(%rax,%rdx,8)
+  .byte  102,69,15,106,196                   // punpckhdq     %xmm12,%xmm8
+  .byte  243,68,15,127,68,208,16             // movdqu        %xmm8,0x10(%rax,%rdx,8)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,68,15,214,12,208                // movq          %xmm9,(%rax,%rdx,8)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            41c7 <_sk_store_f16_sse2+0x168>
+  .byte  102,68,15,23,76,208,8               // movhpd        %xmm9,0x8(%rax,%rdx,8)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            41c7 <_sk_store_f16_sse2+0x168>
+  .byte  102,69,15,106,196                   // punpckhdq     %xmm12,%xmm8
+  .byte  102,68,15,214,68,208,16             // movq          %xmm8,0x10(%rax,%rdx,8)
+  .byte  235,213                             // jmp           41c7 <_sk_store_f16_sse2+0x168>
+
+HIDDEN _sk_load_u16_be_sse2
+.globl _sk_load_u16_be_sse2
+FUNCTION(_sk_load_u16_be_sse2)
+_sk_load_u16_be_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,190,0,0,0                    // jne           42c6 <_sk_load_u16_be_sse2+0xd4>
+  .byte  102,65,15,16,4,65                   // movupd        (%r9,%rax,2),%xmm0
+  .byte  102,65,15,16,76,65,16               // movupd        0x10(%r9,%rax,2),%xmm1
+  .byte  102,15,40,208                       // movapd        %xmm0,%xmm2
+  .byte  102,15,97,209                       // punpcklwd     %xmm1,%xmm2
+  .byte  102,15,105,193                      // punpckhwd     %xmm1,%xmm0
+  .byte  102,15,111,202                      // movdqa        %xmm2,%xmm1
+  .byte  102,15,97,200                       // punpcklwd     %xmm0,%xmm1
+  .byte  102,15,105,208                      // punpckhwd     %xmm0,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,113,240,8                    // psllw         $0x8,%xmm0
+  .byte  102,15,112,217,78                   // pshufd        $0x4e,%xmm1,%xmm3
+  .byte  102,15,113,209,8                    // psrlw         $0x8,%xmm1
+  .byte  102,15,235,200                      // por           %xmm0,%xmm1
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  102,65,15,97,201                    // punpcklwd     %xmm9,%xmm1
+  .byte  15,91,193                           // cvtdq2ps      %xmm1,%xmm0
+  .byte  68,15,40,5,87,33,0,0                // movaps        0x2157(%rip),%xmm8        # 63b0 <_sk_callback_sse2+0xe7c>
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,113,241,8                    // psllw         $0x8,%xmm1
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,15,235,217                      // por           %xmm1,%xmm3
+  .byte  102,65,15,97,217                    // punpcklwd     %xmm9,%xmm3
+  .byte  15,91,203                           // cvtdq2ps      %xmm3,%xmm1
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  102,68,15,111,210                   // movdqa        %xmm2,%xmm10
+  .byte  102,65,15,113,242,8                 // psllw         $0x8,%xmm10
+  .byte  102,15,112,218,78                   // pshufd        $0x4e,%xmm2,%xmm3
+  .byte  102,15,113,210,8                    // psrlw         $0x8,%xmm2
+  .byte  102,65,15,235,210                   // por           %xmm10,%xmm2
+  .byte  102,65,15,97,209                    // punpcklwd     %xmm9,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  102,68,15,111,211                   // movdqa        %xmm3,%xmm10
+  .byte  102,65,15,113,242,8                 // psllw         $0x8,%xmm10
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,65,15,235,218                   // por           %xmm10,%xmm3
+  .byte  102,65,15,97,217                    // punpcklwd     %xmm9,%xmm3
+  .byte  15,91,219                           // cvtdq2ps      %xmm3,%xmm3
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  242,65,15,16,4,65                   // movsd         (%r9,%rax,2),%xmm0
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,17                              // jne           42e3 <_sk_load_u16_be_sse2+0xf1>
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  233,50,255,255,255                  // jmpq          4215 <_sk_load_u16_be_sse2+0x23>
+  .byte  102,65,15,22,68,65,8                // movhpd        0x8(%r9,%rax,2),%xmm0
+  .byte  102,15,87,201                       // xorpd         %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  15,130,29,255,255,255               // jb            4215 <_sk_load_u16_be_sse2+0x23>
+  .byte  242,65,15,16,76,65,16               // movsd         0x10(%r9,%rax,2),%xmm1
+  .byte  233,17,255,255,255                  // jmpq          4215 <_sk_load_u16_be_sse2+0x23>
+
+HIDDEN _sk_load_rgb_u16_be_sse2
+.globl _sk_load_rgb_u16_be_sse2
+FUNCTION(_sk_load_rgb_u16_be_sse2)
+_sk_load_rgb_u16_be_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,82                         // lea           (%rdx,%rdx,2),%rax
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,175,0,0,0                    // jne           43c5 <_sk_load_rgb_u16_be_sse2+0xc1>
+  .byte  243,65,15,111,20,65                 // movdqu        (%r9,%rax,2),%xmm2
+  .byte  243,65,15,111,92,65,8               // movdqu        0x8(%r9,%rax,2),%xmm3
+  .byte  102,15,115,219,4                    // psrldq        $0x4,%xmm3
+  .byte  102,15,111,194                      // movdqa        %xmm2,%xmm0
+  .byte  102,15,115,216,6                    // psrldq        $0x6,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,115,217,6                    // psrldq        $0x6,%xmm1
+  .byte  102,15,97,211                       // punpcklwd     %xmm3,%xmm2
+  .byte  102,15,97,193                       // punpcklwd     %xmm1,%xmm0
+  .byte  102,15,111,202                      // movdqa        %xmm2,%xmm1
+  .byte  102,15,97,200                       // punpcklwd     %xmm0,%xmm1
+  .byte  102,15,112,217,78                   // pshufd        $0x4e,%xmm1,%xmm3
+  .byte  102,15,105,208                      // punpckhwd     %xmm0,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,113,240,8                    // psllw         $0x8,%xmm0
+  .byte  102,15,113,209,8                    // psrlw         $0x8,%xmm1
+  .byte  102,15,235,200                      // por           %xmm0,%xmm1
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  102,65,15,97,200                    // punpcklwd     %xmm8,%xmm1
+  .byte  15,91,193                           // cvtdq2ps      %xmm1,%xmm0
+  .byte  68,15,40,13,70,32,0,0               // movaps        0x2046(%rip),%xmm9        # 63c0 <_sk_callback_sse2+0xe8c>
+  .byte  65,15,89,193                        // mulps         %xmm9,%xmm0
+  .byte  102,15,111,203                      // movdqa        %xmm3,%xmm1
+  .byte  102,15,113,241,8                    // psllw         $0x8,%xmm1
+  .byte  102,15,113,211,8                    // psrlw         $0x8,%xmm3
+  .byte  102,15,235,217                      // por           %xmm1,%xmm3
+  .byte  102,65,15,97,216                    // punpcklwd     %xmm8,%xmm3
+  .byte  15,91,203                           // cvtdq2ps      %xmm3,%xmm1
+  .byte  65,15,89,201                        // mulps         %xmm9,%xmm1
+  .byte  102,15,111,218                      // movdqa        %xmm2,%xmm3
+  .byte  102,15,113,243,8                    // psllw         $0x8,%xmm3
+  .byte  102,15,113,210,8                    // psrlw         $0x8,%xmm2
+  .byte  102,15,235,211                      // por           %xmm3,%xmm2
+  .byte  102,65,15,97,208                    // punpcklwd     %xmm8,%xmm2
+  .byte  15,91,210                           // cvtdq2ps      %xmm2,%xmm2
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,13,32,0,0                  // movaps        0x200d(%rip),%xmm3        # 63d0 <_sk_callback_sse2+0xe9c>
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,65,15,110,20,65                 // movd          (%r9,%rax,2),%xmm2
+  .byte  102,65,15,196,84,65,4,2             // pinsrw        $0x2,0x4(%r9,%rax,2),%xmm2
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,13                              // jne           43ea <_sk_load_rgb_u16_be_sse2+0xe6>
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  233,80,255,255,255                  // jmpq          433a <_sk_load_rgb_u16_be_sse2+0x36>
+  .byte  102,65,15,110,68,65,6               // movd          0x6(%r9,%rax,2),%xmm0
+  .byte  102,65,15,196,68,65,10,2            // pinsrw        $0x2,0xa(%r9,%rax,2),%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,24                              // jb            441b <_sk_load_rgb_u16_be_sse2+0x117>
+  .byte  102,65,15,110,92,65,12              // movd          0xc(%r9,%rax,2),%xmm3
+  .byte  102,65,15,196,92,65,16,2            // pinsrw        $0x2,0x10(%r9,%rax,2),%xmm3
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  233,31,255,255,255                  // jmpq          433a <_sk_load_rgb_u16_be_sse2+0x36>
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  233,22,255,255,255                  // jmpq          433a <_sk_load_rgb_u16_be_sse2+0x36>
+
+HIDDEN _sk_store_u16_be_sse2
+.globl _sk_store_u16_be_sse2
+FUNCTION(_sk_store_u16_be_sse2)
+_sk_store_u16_be_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  72,141,4,149,0,0,0,0                // lea           0x0(,%rdx,4),%rax
+  .byte  68,15,40,21,167,31,0,0              // movaps        0x1fa7(%rip),%xmm10        # 63e0 <_sk_callback_sse2+0xeac>
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  69,15,89,194                        // mulps         %xmm10,%xmm8
+  .byte  102,69,15,91,192                    // cvtps2dq      %xmm8,%xmm8
+  .byte  102,65,15,114,240,16                // pslld         $0x10,%xmm8
+  .byte  102,65,15,114,224,16                // psrad         $0x10,%xmm8
+  .byte  102,69,15,107,192                   // packssdw      %xmm8,%xmm8
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,65,15,113,208,8                 // psrlw         $0x8,%xmm8
+  .byte  102,69,15,235,193                   // por           %xmm9,%xmm8
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  69,15,89,202                        // mulps         %xmm10,%xmm9
+  .byte  102,69,15,91,217                    // cvtps2dq      %xmm9,%xmm11
+  .byte  102,65,15,114,243,16                // pslld         $0x10,%xmm11
+  .byte  102,65,15,114,227,16                // psrad         $0x10,%xmm11
+  .byte  102,69,15,107,219                   // packssdw      %xmm11,%xmm11
+  .byte  102,69,15,111,203                   // movdqa        %xmm11,%xmm9
+  .byte  102,65,15,113,241,8                 // psllw         $0x8,%xmm9
+  .byte  102,65,15,113,211,8                 // psrlw         $0x8,%xmm11
+  .byte  102,69,15,235,217                   // por           %xmm9,%xmm11
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  69,15,89,202                        // mulps         %xmm10,%xmm9
+  .byte  102,69,15,91,201                    // cvtps2dq      %xmm9,%xmm9
+  .byte  102,65,15,114,241,16                // pslld         $0x10,%xmm9
+  .byte  102,65,15,114,225,16                // psrad         $0x10,%xmm9
+  .byte  102,69,15,107,201                   // packssdw      %xmm9,%xmm9
+  .byte  102,69,15,111,225                   // movdqa        %xmm9,%xmm12
+  .byte  102,65,15,113,244,8                 // psllw         $0x8,%xmm12
+  .byte  102,65,15,113,209,8                 // psrlw         $0x8,%xmm9
+  .byte  102,69,15,235,204                   // por           %xmm12,%xmm9
+  .byte  68,15,89,211                        // mulps         %xmm3,%xmm10
+  .byte  102,69,15,91,210                    // cvtps2dq      %xmm10,%xmm10
+  .byte  102,65,15,114,242,16                // pslld         $0x10,%xmm10
+  .byte  102,65,15,114,226,16                // psrad         $0x10,%xmm10
+  .byte  102,69,15,107,210                   // packssdw      %xmm10,%xmm10
+  .byte  102,69,15,111,226                   // movdqa        %xmm10,%xmm12
+  .byte  102,65,15,113,244,8                 // psllw         $0x8,%xmm12
+  .byte  102,65,15,113,210,8                 // psrlw         $0x8,%xmm10
+  .byte  102,69,15,235,212                   // por           %xmm12,%xmm10
+  .byte  102,69,15,97,195                    // punpcklwd     %xmm11,%xmm8
+  .byte  102,69,15,97,202                    // punpcklwd     %xmm10,%xmm9
+  .byte  102,69,15,111,208                   // movdqa        %xmm8,%xmm10
+  .byte  102,69,15,98,209                    // punpckldq     %xmm9,%xmm10
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,21                              // jne           4533 <_sk_store_u16_be_sse2+0x10f>
+  .byte  69,15,17,20,65                      // movups        %xmm10,(%r9,%rax,2)
+  .byte  102,69,15,106,193                   // punpckhdq     %xmm9,%xmm8
+  .byte  243,69,15,127,68,65,16              // movdqu        %xmm8,0x10(%r9,%rax,2)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,69,15,214,20,65                 // movq          %xmm10,(%r9,%rax,2)
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,240                             // je            452f <_sk_store_u16_be_sse2+0x10b>
+  .byte  102,69,15,23,84,65,8                // movhpd        %xmm10,0x8(%r9,%rax,2)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,227                             // jb            452f <_sk_store_u16_be_sse2+0x10b>
+  .byte  102,69,15,106,193                   // punpckhdq     %xmm9,%xmm8
+  .byte  102,69,15,214,68,65,16              // movq          %xmm8,0x10(%r9,%rax,2)
+  .byte  235,213                             // jmp           452f <_sk_store_u16_be_sse2+0x10b>
+
+HIDDEN _sk_load_f32_sse2
+.globl _sk_load_f32_sse2
+FUNCTION(_sk_load_f32_sse2)
+_sk_load_f32_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  76,141,12,149,0,0,0,0               // lea           0x0(,%rdx,4),%r9
+  .byte  72,137,208                          // mov           %rdx,%rax
+  .byte  72,193,224,4                        // shl           $0x4,%rax
+  .byte  69,15,16,4,2                        // movups        (%r10,%rax,1),%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,66                              // jne           45ba <_sk_load_f32_sse2+0x60>
+  .byte  67,15,16,68,138,16                  // movups        0x10(%r10,%r9,4),%xmm0
+  .byte  67,15,16,92,138,32                  // movups        0x20(%r10,%r9,4),%xmm3
+  .byte  71,15,16,76,138,48                  // movups        0x30(%r10,%r9,4),%xmm9
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  15,20,208                           // unpcklps      %xmm0,%xmm2
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  65,15,20,201                        // unpcklps      %xmm9,%xmm1
+  .byte  68,15,21,192                        // unpckhps      %xmm0,%xmm8
+  .byte  65,15,21,217                        // unpckhps      %xmm9,%xmm3
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  15,18,202                           // movhlps       %xmm2,%xmm1
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  102,15,20,211                       // unpcklpd      %xmm3,%xmm2
+  .byte  65,15,18,216                        // movhlps       %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  117,8                               // jne           45cc <_sk_load_f32_sse2+0x72>
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  235,190                             // jmp           458a <_sk_load_f32_sse2+0x30>
+  .byte  67,15,16,68,138,16                  // movups        0x10(%r10,%r9,4),%xmm0
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,8                               // jb            45e0 <_sk_load_f32_sse2+0x86>
+  .byte  67,15,16,92,138,32                  // movups        0x20(%r10,%r9,4),%xmm3
+  .byte  235,170                             // jmp           458a <_sk_load_f32_sse2+0x30>
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  235,165                             // jmp           458a <_sk_load_f32_sse2+0x30>
+
+HIDDEN _sk_store_f32_sse2
+.globl _sk_store_f32_sse2
+FUNCTION(_sk_store_f32_sse2)
+_sk_store_f32_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,16                           // mov           (%rax),%r10
+  .byte  76,141,12,149,0,0,0,0               // lea           0x0(,%rdx,4),%r9
+  .byte  72,137,208                          // mov           %rdx,%rax
+  .byte  72,193,224,4                        // shl           $0x4,%rax
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  68,15,20,201                        // unpcklps      %xmm1,%xmm9
+  .byte  68,15,40,194                        // movaps        %xmm2,%xmm8
+  .byte  68,15,20,195                        // unpcklps      %xmm3,%xmm8
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  68,15,21,209                        // unpckhps      %xmm1,%xmm10
+  .byte  68,15,40,218                        // movaps        %xmm2,%xmm11
+  .byte  68,15,21,219                        // unpckhps      %xmm3,%xmm11
+  .byte  69,15,40,225                        // movaps        %xmm9,%xmm12
+  .byte  102,69,15,20,224                    // unpcklpd      %xmm8,%xmm12
+  .byte  69,15,18,193                        // movhlps       %xmm9,%xmm8
+  .byte  69,15,40,202                        // movaps        %xmm10,%xmm9
+  .byte  102,69,15,20,203                    // unpcklpd      %xmm11,%xmm9
+  .byte  102,69,15,17,36,2                   // movupd        %xmm12,(%r10,%rax,1)
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,29                              // jne           4657 <_sk_store_f32_sse2+0x72>
+  .byte  102,69,15,21,211                    // unpckhpd      %xmm11,%xmm10
+  .byte  71,15,17,68,138,16                  // movups        %xmm8,0x10(%r10,%r9,4)
+  .byte  102,71,15,17,76,138,32              // movupd        %xmm9,0x20(%r10,%r9,4)
+  .byte  102,71,15,17,84,138,48              // movupd        %xmm10,0x30(%r10,%r9,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  73,131,248,1                        // cmp           $0x1,%r8
+  .byte  116,246                             // je            4653 <_sk_store_f32_sse2+0x6e>
+  .byte  71,15,17,68,138,16                  // movups        %xmm8,0x10(%r10,%r9,4)
+  .byte  73,131,248,3                        // cmp           $0x3,%r8
+  .byte  114,234                             // jb            4653 <_sk_store_f32_sse2+0x6e>
+  .byte  102,71,15,17,76,138,32              // movupd        %xmm9,0x20(%r10,%r9,4)
+  .byte  235,225                             // jmp           4653 <_sk_store_f32_sse2+0x6e>
+
+HIDDEN _sk_clamp_x_sse2
+.globl _sk_clamp_x_sse2
+FUNCTION(_sk_clamp_x_sse2)
+_sk_clamp_x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  68,15,93,192                        // minps         %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_y_sse2
+.globl _sk_clamp_y_sse2
+FUNCTION(_sk_clamp_y_sse2)
+_sk_clamp_y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,193                        // maxps         %xmm1,%xmm8
+  .byte  243,15,16,8                         // movss         (%rax),%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  68,15,93,193                        // minps         %xmm1,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,200                        // movaps        %xmm8,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_sse2
+.globl _sk_repeat_x_sse2
+FUNCTION(_sk_repeat_x_sse2)
+_sk_repeat_x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,94,200                        // divps         %xmm8,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  69,15,194,202,1                     // cmpltps       %xmm10,%xmm9
+  .byte  68,15,84,13,24,29,0,0               // andps         0x1d18(%rip),%xmm9        # 63f0 <_sk_callback_sse2+0xebc>
+  .byte  69,15,92,209                        // subps         %xmm9,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,92,194                        // subps         %xmm10,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_y_sse2
+.globl _sk_repeat_y_sse2
+FUNCTION(_sk_repeat_y_sse2)
+_sk_repeat_y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  69,15,94,200                        // divps         %xmm8,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  69,15,194,202,1                     // cmpltps       %xmm10,%xmm9
+  .byte  68,15,84,13,238,28,0,0              // andps         0x1cee(%rip),%xmm9        # 6400 <_sk_callback_sse2+0xecc>
+  .byte  69,15,92,209                        // subps         %xmm9,%xmm10
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  65,15,92,202                        // subps         %xmm10,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_sse2
+.globl _sk_mirror_x_sse2
+FUNCTION(_sk_mirror_x_sse2)
+_sk_mirror_x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  243,69,15,88,192                    // addss         %xmm8,%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,208                        // movaps        %xmm0,%xmm10
+  .byte  69,15,94,208                        // divps         %xmm8,%xmm10
+  .byte  243,69,15,91,218                    // cvttps2dq     %xmm10,%xmm11
+  .byte  69,15,91,219                        // cvtdq2ps      %xmm11,%xmm11
+  .byte  69,15,194,211,1                     // cmpltps       %xmm11,%xmm10
+  .byte  68,15,84,21,178,28,0,0              // andps         0x1cb2(%rip),%xmm10        # 6410 <_sk_callback_sse2+0xedc>
+  .byte  69,15,87,228                        // xorps         %xmm12,%xmm12
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  65,15,92,195                        // subps         %xmm11,%xmm0
+  .byte  65,15,92,193                        // subps         %xmm9,%xmm0
+  .byte  68,15,92,224                        // subps         %xmm0,%xmm12
+  .byte  65,15,84,196                        // andps         %xmm12,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_y_sse2
+.globl _sk_mirror_y_sse2
+FUNCTION(_sk_mirror_y_sse2)
+_sk_mirror_y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  65,15,92,201                        // subps         %xmm9,%xmm1
+  .byte  243,69,15,88,192                    // addss         %xmm8,%xmm8
+  .byte  69,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm8
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  69,15,94,208                        // divps         %xmm8,%xmm10
+  .byte  243,69,15,91,218                    // cvttps2dq     %xmm10,%xmm11
+  .byte  69,15,91,219                        // cvtdq2ps      %xmm11,%xmm11
+  .byte  69,15,194,211,1                     // cmpltps       %xmm11,%xmm10
+  .byte  68,15,84,21,102,28,0,0              // andps         0x1c66(%rip),%xmm10        # 6420 <_sk_callback_sse2+0xeec>
+  .byte  69,15,87,228                        // xorps         %xmm12,%xmm12
+  .byte  69,15,92,218                        // subps         %xmm10,%xmm11
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  65,15,92,203                        // subps         %xmm11,%xmm1
+  .byte  65,15,92,201                        // subps         %xmm9,%xmm1
+  .byte  68,15,92,225                        // subps         %xmm1,%xmm12
+  .byte  65,15,84,204                        // andps         %xmm12,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clamp_x_1_sse2
+.globl _sk_clamp_x_1_sse2
+FUNCTION(_sk_clamp_x_1_sse2)
+_sk_clamp_x_1_sse2:
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  68,15,95,192                        // maxps         %xmm0,%xmm8
+  .byte  68,15,93,5,70,28,0,0                // minps         0x1c46(%rip),%xmm8        # 6430 <_sk_callback_sse2+0xefc>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_repeat_x_1_sse2
+.globl _sk_repeat_x_1_sse2
+FUNCTION(_sk_repeat_x_1_sse2)
+_sk_repeat_x_1_sse2:
+  .byte  243,68,15,91,192                    // cvttps2dq     %xmm0,%xmm8
+  .byte  69,15,91,192                        // cvtdq2ps      %xmm8,%xmm8
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,194,200,1                     // cmpltps       %xmm8,%xmm9
+  .byte  68,15,84,13,52,28,0,0               // andps         0x1c34(%rip),%xmm9        # 6440 <_sk_callback_sse2+0xf0c>
+  .byte  69,15,92,193                        // subps         %xmm9,%xmm8
+  .byte  65,15,92,192                        // subps         %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_mirror_x_1_sse2
+.globl _sk_mirror_x_1_sse2
+FUNCTION(_sk_mirror_x_1_sse2)
+_sk_mirror_x_1_sse2:
+  .byte  68,15,40,5,48,28,0,0                // movaps        0x1c30(%rip),%xmm8        # 6450 <_sk_callback_sse2+0xf1c>
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,13,52,28,0,0               // movaps        0x1c34(%rip),%xmm9        # 6460 <_sk_callback_sse2+0xf2c>
+  .byte  68,15,89,200                        // mulps         %xmm0,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  69,15,194,202,1                     // cmpltps       %xmm10,%xmm9
+  .byte  68,15,84,13,42,28,0,0               // andps         0x1c2a(%rip),%xmm9        # 6470 <_sk_callback_sse2+0xf3c>
+  .byte  69,15,87,219                        // xorps         %xmm11,%xmm11
+  .byte  69,15,92,209                        // subps         %xmm9,%xmm10
+  .byte  69,15,88,210                        // addps         %xmm10,%xmm10
+  .byte  65,15,92,194                        // subps         %xmm10,%xmm0
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,92,216                        // subps         %xmm0,%xmm11
+  .byte  65,15,84,195                        // andps         %xmm11,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_luminance_to_alpha_sse2
+.globl _sk_luminance_to_alpha_sse2
+FUNCTION(_sk_luminance_to_alpha_sse2)
+_sk_luminance_to_alpha_sse2:
+  .byte  15,40,218                           // movaps        %xmm2,%xmm3
+  .byte  15,89,5,16,28,0,0                   // mulps         0x1c10(%rip),%xmm0        # 6480 <_sk_callback_sse2+0xf4c>
+  .byte  15,89,13,25,28,0,0                  // mulps         0x1c19(%rip),%xmm1        # 6490 <_sk_callback_sse2+0xf5c>
+  .byte  15,88,200                           // addps         %xmm0,%xmm1
+  .byte  15,89,29,31,28,0,0                  // mulps         0x1c1f(%rip),%xmm3        # 64a0 <_sk_callback_sse2+0xf6c>
+  .byte  15,88,217                           // addps         %xmm1,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_2x3_sse2
+.globl _sk_matrix_2x3_sse2
+FUNCTION(_sk_matrix_2x3_sse2)
+_sk_matrix_2x3_sse2:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,16                  // movss         0x10(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,68,15,16,80,12                  // movss         0xc(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,20                  // movss         0x14(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_3x4_sse2
+.globl _sk_matrix_3x4_sse2
+FUNCTION(_sk_matrix_3x4_sse2)
+_sk_matrix_3x4_sse2:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,68,15,16,80,12                  // movss         0xc(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,24                  // movss         0x18(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,36                  // movss         0x24(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,28                  // movss         0x1c(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,40                  // movss         0x28(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,20                  // movss         0x14(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,32                  // movss         0x20(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,44                 // movss         0x2c(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  68,15,89,226                        // mulps         %xmm2,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,217                        // mulps         %xmm9,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x5_sse2
+.globl _sk_matrix_4x5_sse2
+FUNCTION(_sk_matrix_4x5_sse2)
+_sk_matrix_4x5_sse2:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,32                  // movss         0x20(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,48                  // movss         0x30(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,64                 // movss         0x40(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,194                        // addps         %xmm10,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,68,15,16,80,20                  // movss         0x14(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,36                  // movss         0x24(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,52                  // movss         0x34(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,68                 // movss         0x44(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  68,15,89,227                        // mulps         %xmm3,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  68,15,89,218                        // mulps         %xmm2,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,24                  // movss         0x18(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,40                  // movss         0x28(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,56                 // movss         0x38(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  243,68,15,16,112,72                 // movss         0x48(%rax),%xmm14
+  .byte  69,15,198,246,0                     // shufps        $0x0,%xmm14,%xmm14
+  .byte  68,15,89,235                        // mulps         %xmm3,%xmm13
+  .byte  69,15,88,238                        // addps         %xmm14,%xmm13
+  .byte  68,15,89,226                        // mulps         %xmm2,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,217                        // mulps         %xmm9,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  243,68,15,16,88,12                  // movss         0xc(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,28                  // movss         0x1c(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  243,68,15,16,104,44                 // movss         0x2c(%rax),%xmm13
+  .byte  69,15,198,237,0                     // shufps        $0x0,%xmm13,%xmm13
+  .byte  243,68,15,16,112,60                 // movss         0x3c(%rax),%xmm14
+  .byte  69,15,198,246,0                     // shufps        $0x0,%xmm14,%xmm14
+  .byte  243,68,15,16,120,76                 // movss         0x4c(%rax),%xmm15
+  .byte  69,15,198,255,0                     // shufps        $0x0,%xmm15,%xmm15
+  .byte  68,15,89,243                        // mulps         %xmm3,%xmm14
+  .byte  69,15,88,247                        // addps         %xmm15,%xmm14
+  .byte  68,15,89,234                        // mulps         %xmm2,%xmm13
+  .byte  69,15,88,238                        // addps         %xmm14,%xmm13
+  .byte  69,15,89,225                        // mulps         %xmm9,%xmm12
+  .byte  69,15,88,229                        // addps         %xmm13,%xmm12
+  .byte  69,15,89,216                        // mulps         %xmm8,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,210                        // movaps        %xmm10,%xmm2
+  .byte  65,15,40,219                        // movaps        %xmm11,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_4x3_sse2
+.globl _sk_matrix_4x3_sse2
+FUNCTION(_sk_matrix_4x3_sse2)
+_sk_matrix_4x3_sse2:
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,15,16,80,16                     // movss         0x10(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,32                     // movss         0x20(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,194                           // addps         %xmm2,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,20                     // movss         0x14(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,36                     // movss         0x24(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,89,209                        // mulps         %xmm9,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,24                     // movss         0x18(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  243,68,15,16,80,40                  // movss         0x28(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  65,15,89,217                        // mulps         %xmm9,%xmm3
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  243,15,16,88,12                     // movss         0xc(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  243,68,15,16,80,28                  // movss         0x1c(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,44                  // movss         0x2c(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  65,15,88,218                        // addps         %xmm10,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_matrix_perspective_sse2
+.globl _sk_matrix_perspective_sse2
+FUNCTION(_sk_matrix_perspective_sse2)
+_sk_matrix_perspective_sse2:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,68,15,16,72,4                   // movss         0x4(%rax),%xmm9
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,80,8                   // movss         0x8(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  65,15,88,193                        // addps         %xmm9,%xmm0
+  .byte  243,68,15,16,72,12                  // movss         0xc(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  243,68,15,16,80,16                  // movss         0x10(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,20                  // movss         0x14(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  68,15,89,209                        // mulps         %xmm1,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  69,15,88,202                        // addps         %xmm10,%xmm9
+  .byte  243,68,15,16,80,24                  // movss         0x18(%rax),%xmm10
+  .byte  69,15,198,210,0                     // shufps        $0x0,%xmm10,%xmm10
+  .byte  243,68,15,16,88,28                  // movss         0x1c(%rax),%xmm11
+  .byte  69,15,198,219,0                     // shufps        $0x0,%xmm11,%xmm11
+  .byte  243,68,15,16,96,32                  // movss         0x20(%rax),%xmm12
+  .byte  69,15,198,228,0                     // shufps        $0x0,%xmm12,%xmm12
+  .byte  68,15,89,217                        // mulps         %xmm1,%xmm11
+  .byte  69,15,88,220                        // addps         %xmm12,%xmm11
+  .byte  69,15,89,208                        // mulps         %xmm8,%xmm10
+  .byte  69,15,88,211                        // addps         %xmm11,%xmm10
+  .byte  65,15,83,202                        // rcpps         %xmm10,%xmm1
+  .byte  15,89,193                           // mulps         %xmm1,%xmm0
+  .byte  68,15,89,201                        // mulps         %xmm1,%xmm9
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,201                        // movaps        %xmm9,%xmm1
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_gradient_sse2
+.globl _sk_evenly_spaced_gradient_sse2
+FUNCTION(_sk_evenly_spaced_gradient_sse2)
+_sk_evenly_spaced_gradient_sse2:
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  72,139,24                           // mov           (%rax),%rbx
+  .byte  76,139,112,8                        // mov           0x8(%rax),%r14
+  .byte  72,255,203                          // dec           %rbx
+  .byte  120,7                               // js            4cbd <_sk_evenly_spaced_gradient_sse2+0x18>
+  .byte  243,72,15,42,203                    // cvtsi2ss      %rbx,%xmm1
+  .byte  235,21                              // jmp           4cd2 <_sk_evenly_spaced_gradient_sse2+0x2d>
+  .byte  73,137,217                          // mov           %rbx,%r9
+  .byte  73,209,233                          // shr           %r9
+  .byte  131,227,1                           // and           $0x1,%ebx
+  .byte  76,9,203                            // or            %r9,%rbx
+  .byte  243,72,15,42,203                    // cvtsi2ss      %rbx,%xmm1
+  .byte  243,15,88,201                       // addss         %xmm1,%xmm1
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  15,89,200                           // mulps         %xmm0,%xmm1
+  .byte  243,15,91,201                       // cvttps2dq     %xmm1,%xmm1
+  .byte  102,15,112,209,78                   // pshufd        $0x4e,%xmm1,%xmm2
+  .byte  102,73,15,126,211                   // movq          %xmm2,%r11
+  .byte  69,137,217                          // mov           %r11d,%r9d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  65,137,218                          // mov           %ebx,%r10d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  243,65,15,16,12,158                 // movss         (%r14,%rbx,4),%xmm1
+  .byte  243,67,15,16,20,158                 // movss         (%r14,%r11,4),%xmm2
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  243,71,15,16,4,150                  // movss         (%r14,%r10,4),%xmm8
+  .byte  243,67,15,16,20,142                 // movss         (%r14,%r9,4),%xmm2
+  .byte  68,15,20,194                        // unpcklps      %xmm2,%xmm8
+  .byte  68,15,20,193                        // unpcklps      %xmm1,%xmm8
+  .byte  76,139,112,40                       // mov           0x28(%rax),%r14
+  .byte  243,65,15,16,12,158                 // movss         (%r14,%rbx,4),%xmm1
+  .byte  243,67,15,16,20,158                 // movss         (%r14,%r11,4),%xmm2
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  243,71,15,16,12,150                 // movss         (%r14,%r10,4),%xmm9
+  .byte  243,67,15,16,20,142                 // movss         (%r14,%r9,4),%xmm2
+  .byte  68,15,20,202                        // unpcklps      %xmm2,%xmm9
+  .byte  68,15,20,201                        // unpcklps      %xmm1,%xmm9
+  .byte  76,139,112,16                       // mov           0x10(%rax),%r14
+  .byte  243,65,15,16,20,158                 // movss         (%r14,%rbx,4),%xmm2
+  .byte  243,67,15,16,12,158                 // movss         (%r14,%r11,4),%xmm1
+  .byte  15,20,209                           // unpcklps      %xmm1,%xmm2
+  .byte  243,67,15,16,12,150                 // movss         (%r14,%r10,4),%xmm1
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  15,20,203                           // unpcklps      %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  76,139,112,48                       // mov           0x30(%rax),%r14
+  .byte  243,65,15,16,20,158                 // movss         (%r14,%rbx,4),%xmm2
+  .byte  243,67,15,16,28,158                 // movss         (%r14,%r11,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  243,71,15,16,20,150                 // movss         (%r14,%r10,4),%xmm10
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  68,15,20,211                        // unpcklps      %xmm3,%xmm10
+  .byte  68,15,20,210                        // unpcklps      %xmm2,%xmm10
+  .byte  76,139,112,24                       // mov           0x18(%rax),%r14
+  .byte  243,69,15,16,28,158                 // movss         (%r14,%rbx,4),%xmm11
+  .byte  243,67,15,16,20,158                 // movss         (%r14,%r11,4),%xmm2
+  .byte  68,15,20,218                        // unpcklps      %xmm2,%xmm11
+  .byte  243,67,15,16,20,150                 // movss         (%r14,%r10,4),%xmm2
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  65,15,20,211                        // unpcklps      %xmm11,%xmm2
+  .byte  76,139,112,56                       // mov           0x38(%rax),%r14
+  .byte  243,69,15,16,36,158                 // movss         (%r14,%rbx,4),%xmm12
+  .byte  243,67,15,16,28,158                 // movss         (%r14,%r11,4),%xmm3
+  .byte  68,15,20,227                        // unpcklps      %xmm3,%xmm12
+  .byte  243,71,15,16,28,150                 // movss         (%r14,%r10,4),%xmm11
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  68,15,20,219                        // unpcklps      %xmm3,%xmm11
+  .byte  69,15,20,220                        // unpcklps      %xmm12,%xmm11
+  .byte  76,139,112,32                       // mov           0x20(%rax),%r14
+  .byte  243,69,15,16,36,158                 // movss         (%r14,%rbx,4),%xmm12
+  .byte  243,67,15,16,28,158                 // movss         (%r14,%r11,4),%xmm3
+  .byte  68,15,20,227                        // unpcklps      %xmm3,%xmm12
+  .byte  243,67,15,16,28,150                 // movss         (%r14,%r10,4),%xmm3
+  .byte  243,71,15,16,44,142                 // movss         (%r14,%r9,4),%xmm13
+  .byte  65,15,20,221                        // unpcklps      %xmm13,%xmm3
+  .byte  65,15,20,220                        // unpcklps      %xmm12,%xmm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  243,68,15,16,36,152                 // movss         (%rax,%rbx,4),%xmm12
+  .byte  243,70,15,16,44,152                 // movss         (%rax,%r11,4),%xmm13
+  .byte  69,15,20,229                        // unpcklps      %xmm13,%xmm12
+  .byte  243,70,15,16,44,144                 // movss         (%rax,%r10,4),%xmm13
+  .byte  243,70,15,16,52,136                 // movss         (%rax,%r9,4),%xmm14
+  .byte  69,15,20,238                        // unpcklps      %xmm14,%xmm13
+  .byte  69,15,20,236                        // unpcklps      %xmm12,%xmm13
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  15,89,200                           // mulps         %xmm0,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  15,89,208                           // mulps         %xmm0,%xmm2
+  .byte  65,15,88,211                        // addps         %xmm11,%xmm2
+  .byte  15,89,216                           // mulps         %xmm0,%xmm3
+  .byte  65,15,88,221                        // addps         %xmm13,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gauss_a_to_rgba_sse2
+.globl _sk_gauss_a_to_rgba_sse2
+FUNCTION(_sk_gauss_a_to_rgba_sse2)
+_sk_gauss_a_to_rgba_sse2:
+  .byte  15,40,5,82,22,0,0                   // movaps        0x1652(%rip),%xmm0        # 64b0 <_sk_callback_sse2+0xf7c>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,88,22,0,0                   // addps         0x1658(%rip),%xmm0        # 64c0 <_sk_callback_sse2+0xf8c>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,94,22,0,0                   // addps         0x165e(%rip),%xmm0        # 64d0 <_sk_callback_sse2+0xf9c>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,100,22,0,0                  // addps         0x1664(%rip),%xmm0        # 64e0 <_sk_callback_sse2+0xfac>
+  .byte  15,89,195                           // mulps         %xmm3,%xmm0
+  .byte  15,88,5,106,22,0,0                  // addps         0x166a(%rip),%xmm0        # 64f0 <_sk_callback_sse2+0xfbc>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,200                           // movaps        %xmm0,%xmm1
+  .byte  15,40,208                           // movaps        %xmm0,%xmm2
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_gradient_sse2
+.globl _sk_gradient_sse2
+FUNCTION(_sk_gradient_sse2)
+_sk_gradient_sse2:
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  83                                  // push          %rbx
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,8                            // mov           (%rax),%r9
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  73,131,249,2                        // cmp           $0x2,%r9
+  .byte  114,50                              // jb            4ed9 <_sk_gradient_sse2+0x46>
+  .byte  72,139,88,72                        // mov           0x48(%rax),%rbx
+  .byte  73,255,201                          // dec           %r9
+  .byte  72,131,195,4                        // add           $0x4,%rbx
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  15,40,21,67,22,0,0                  // movaps        0x1643(%rip),%xmm2        # 6500 <_sk_callback_sse2+0xfcc>
+  .byte  243,15,16,27                        // movss         (%rbx),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  15,194,216,2                        // cmpleps       %xmm0,%xmm3
+  .byte  15,84,218                           // andps         %xmm2,%xmm3
+  .byte  102,15,254,203                      // paddd         %xmm3,%xmm1
+  .byte  72,131,195,4                        // add           $0x4,%rbx
+  .byte  73,255,201                          // dec           %r9
+  .byte  117,228                             // jne           4ebd <_sk_gradient_sse2+0x2a>
+  .byte  102,15,112,209,78                   // pshufd        $0x4e,%xmm1,%xmm2
+  .byte  102,73,15,126,211                   // movq          %xmm2,%r11
+  .byte  69,137,217                          // mov           %r11d,%r9d
+  .byte  73,193,235,32                       // shr           $0x20,%r11
+  .byte  102,72,15,126,203                   // movq          %xmm1,%rbx
+  .byte  65,137,218                          // mov           %ebx,%r10d
+  .byte  72,193,235,32                       // shr           $0x20,%rbx
+  .byte  76,139,112,8                        // mov           0x8(%rax),%r14
+  .byte  76,139,120,16                       // mov           0x10(%rax),%r15
+  .byte  243,65,15,16,12,158                 // movss         (%r14,%rbx,4),%xmm1
+  .byte  243,67,15,16,20,158                 // movss         (%r14,%r11,4),%xmm2
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  243,71,15,16,4,150                  // movss         (%r14,%r10,4),%xmm8
+  .byte  243,67,15,16,20,142                 // movss         (%r14,%r9,4),%xmm2
+  .byte  68,15,20,194                        // unpcklps      %xmm2,%xmm8
+  .byte  68,15,20,193                        // unpcklps      %xmm1,%xmm8
+  .byte  76,139,112,40                       // mov           0x28(%rax),%r14
+  .byte  243,65,15,16,12,158                 // movss         (%r14,%rbx,4),%xmm1
+  .byte  243,67,15,16,20,158                 // movss         (%r14,%r11,4),%xmm2
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  243,71,15,16,12,150                 // movss         (%r14,%r10,4),%xmm9
+  .byte  243,67,15,16,20,142                 // movss         (%r14,%r9,4),%xmm2
+  .byte  68,15,20,202                        // unpcklps      %xmm2,%xmm9
+  .byte  68,15,20,201                        // unpcklps      %xmm1,%xmm9
+  .byte  243,65,15,16,20,159                 // movss         (%r15,%rbx,4),%xmm2
+  .byte  243,67,15,16,12,159                 // movss         (%r15,%r11,4),%xmm1
+  .byte  15,20,209                           // unpcklps      %xmm1,%xmm2
+  .byte  243,67,15,16,12,151                 // movss         (%r15,%r10,4),%xmm1
+  .byte  243,67,15,16,28,143                 // movss         (%r15,%r9,4),%xmm3
+  .byte  15,20,203                           // unpcklps      %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  76,139,112,48                       // mov           0x30(%rax),%r14
+  .byte  243,65,15,16,20,158                 // movss         (%r14,%rbx,4),%xmm2
+  .byte  243,67,15,16,28,158                 // movss         (%r14,%r11,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  243,71,15,16,20,150                 // movss         (%r14,%r10,4),%xmm10
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  68,15,20,211                        // unpcklps      %xmm3,%xmm10
+  .byte  68,15,20,210                        // unpcklps      %xmm2,%xmm10
+  .byte  76,139,112,24                       // mov           0x18(%rax),%r14
+  .byte  243,69,15,16,28,158                 // movss         (%r14,%rbx,4),%xmm11
+  .byte  243,67,15,16,20,158                 // movss         (%r14,%r11,4),%xmm2
+  .byte  68,15,20,218                        // unpcklps      %xmm2,%xmm11
+  .byte  243,67,15,16,20,150                 // movss         (%r14,%r10,4),%xmm2
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  15,20,211                           // unpcklps      %xmm3,%xmm2
+  .byte  65,15,20,211                        // unpcklps      %xmm11,%xmm2
+  .byte  76,139,112,56                       // mov           0x38(%rax),%r14
+  .byte  243,69,15,16,36,158                 // movss         (%r14,%rbx,4),%xmm12
+  .byte  243,67,15,16,28,158                 // movss         (%r14,%r11,4),%xmm3
+  .byte  68,15,20,227                        // unpcklps      %xmm3,%xmm12
+  .byte  243,71,15,16,28,150                 // movss         (%r14,%r10,4),%xmm11
+  .byte  243,67,15,16,28,142                 // movss         (%r14,%r9,4),%xmm3
+  .byte  68,15,20,219                        // unpcklps      %xmm3,%xmm11
+  .byte  69,15,20,220                        // unpcklps      %xmm12,%xmm11
+  .byte  76,139,112,32                       // mov           0x20(%rax),%r14
+  .byte  243,69,15,16,36,158                 // movss         (%r14,%rbx,4),%xmm12
+  .byte  243,67,15,16,28,158                 // movss         (%r14,%r11,4),%xmm3
+  .byte  68,15,20,227                        // unpcklps      %xmm3,%xmm12
+  .byte  243,67,15,16,28,150                 // movss         (%r14,%r10,4),%xmm3
+  .byte  243,71,15,16,44,142                 // movss         (%r14,%r9,4),%xmm13
+  .byte  65,15,20,221                        // unpcklps      %xmm13,%xmm3
+  .byte  65,15,20,220                        // unpcklps      %xmm12,%xmm3
+  .byte  72,139,64,64                        // mov           0x40(%rax),%rax
+  .byte  243,68,15,16,36,152                 // movss         (%rax,%rbx,4),%xmm12
+  .byte  243,70,15,16,44,152                 // movss         (%rax,%r11,4),%xmm13
+  .byte  69,15,20,229                        // unpcklps      %xmm13,%xmm12
+  .byte  243,70,15,16,44,144                 // movss         (%rax,%r10,4),%xmm13
+  .byte  243,70,15,16,52,136                 // movss         (%rax,%r9,4),%xmm14
+  .byte  69,15,20,238                        // unpcklps      %xmm14,%xmm13
+  .byte  69,15,20,236                        // unpcklps      %xmm12,%xmm13
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  69,15,88,193                        // addps         %xmm9,%xmm8
+  .byte  15,89,200                           // mulps         %xmm0,%xmm1
+  .byte  65,15,88,202                        // addps         %xmm10,%xmm1
+  .byte  15,89,208                           // mulps         %xmm0,%xmm2
+  .byte  65,15,88,211                        // addps         %xmm11,%xmm2
+  .byte  15,89,216                           // mulps         %xmm0,%xmm3
+  .byte  65,15,88,221                        // addps         %xmm13,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  65,15,40,192                        // movaps        %xmm8,%xmm0
+  .byte  91                                  // pop           %rbx
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_evenly_spaced_2_stop_gradient_sse2
+.globl _sk_evenly_spaced_2_stop_gradient_sse2
+FUNCTION(_sk_evenly_spaced_2_stop_gradient_sse2)
+_sk_evenly_spaced_2_stop_gradient_sse2:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  15,198,192,0                        // shufps        $0x0,%xmm0,%xmm0
+  .byte  243,15,16,80,16                     // movss         0x10(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  65,15,89,192                        // mulps         %xmm8,%xmm0
+  .byte  15,88,194                           // addps         %xmm2,%xmm0
+  .byte  15,198,201,0                        // shufps        $0x0,%xmm1,%xmm1
+  .byte  243,15,16,80,20                     // movss         0x14(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  65,15,89,200                        // mulps         %xmm8,%xmm1
+  .byte  15,88,202                           // addps         %xmm2,%xmm1
+  .byte  243,15,16,80,8                      // movss         0x8(%rax),%xmm2
+  .byte  15,198,210,0                        // shufps        $0x0,%xmm2,%xmm2
+  .byte  243,15,16,88,24                     // movss         0x18(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  65,15,89,208                        // mulps         %xmm8,%xmm2
+  .byte  15,88,211                           // addps         %xmm3,%xmm2
+  .byte  243,15,16,88,12                     // movss         0xc(%rax),%xmm3
+  .byte  15,198,219,0                        // shufps        $0x0,%xmm3,%xmm3
+  .byte  243,68,15,16,72,28                  // movss         0x1c(%rax),%xmm9
+  .byte  69,15,198,201,0                     // shufps        $0x0,%xmm9,%xmm9
+  .byte  65,15,89,216                        // mulps         %xmm8,%xmm3
+  .byte  65,15,88,217                        // addps         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_unit_angle_sse2
+.globl _sk_xy_to_unit_angle_sse2
+FUNCTION(_sk_xy_to_unit_angle_sse2)
+_sk_xy_to_unit_angle_sse2:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  69,15,87,201                        // xorps         %xmm9,%xmm9
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  69,15,84,200                        // andps         %xmm8,%xmm9
+  .byte  69,15,87,210                        // xorps         %xmm10,%xmm10
+  .byte  68,15,92,209                        // subps         %xmm1,%xmm10
+  .byte  68,15,84,209                        // andps         %xmm1,%xmm10
+  .byte  69,15,40,217                        // movaps        %xmm9,%xmm11
+  .byte  69,15,93,218                        // minps         %xmm10,%xmm11
+  .byte  69,15,40,225                        // movaps        %xmm9,%xmm12
+  .byte  69,15,95,226                        // maxps         %xmm10,%xmm12
+  .byte  69,15,94,220                        // divps         %xmm12,%xmm11
+  .byte  69,15,40,227                        // movaps        %xmm11,%xmm12
+  .byte  69,15,89,228                        // mulps         %xmm12,%xmm12
+  .byte  68,15,40,45,4,20,0,0                // movaps        0x1404(%rip),%xmm13        # 6510 <_sk_callback_sse2+0xfdc>
+  .byte  69,15,89,236                        // mulps         %xmm12,%xmm13
+  .byte  68,15,88,45,8,20,0,0                // addps         0x1408(%rip),%xmm13        # 6520 <_sk_callback_sse2+0xfec>
+  .byte  69,15,89,236                        // mulps         %xmm12,%xmm13
+  .byte  68,15,88,45,12,20,0,0               // addps         0x140c(%rip),%xmm13        # 6530 <_sk_callback_sse2+0xffc>
+  .byte  69,15,89,236                        // mulps         %xmm12,%xmm13
+  .byte  68,15,88,45,16,20,0,0               // addps         0x1410(%rip),%xmm13        # 6540 <_sk_callback_sse2+0x100c>
+  .byte  69,15,89,235                        // mulps         %xmm11,%xmm13
+  .byte  69,15,194,202,1                     // cmpltps       %xmm10,%xmm9
+  .byte  68,15,40,21,15,20,0,0               // movaps        0x140f(%rip),%xmm10        # 6550 <_sk_callback_sse2+0x101c>
+  .byte  69,15,92,213                        // subps         %xmm13,%xmm10
+  .byte  69,15,84,209                        // andps         %xmm9,%xmm10
+  .byte  69,15,85,205                        // andnps        %xmm13,%xmm9
+  .byte  69,15,86,202                        // orps          %xmm10,%xmm9
+  .byte  68,15,194,192,1                     // cmpltps       %xmm0,%xmm8
+  .byte  68,15,40,21,2,20,0,0                // movaps        0x1402(%rip),%xmm10        # 6560 <_sk_callback_sse2+0x102c>
+  .byte  69,15,92,209                        // subps         %xmm9,%xmm10
+  .byte  69,15,84,208                        // andps         %xmm8,%xmm10
+  .byte  69,15,85,193                        // andnps        %xmm9,%xmm8
+  .byte  69,15,86,194                        // orps          %xmm10,%xmm8
+  .byte  68,15,40,201                        // movaps        %xmm1,%xmm9
+  .byte  68,15,194,200,1                     // cmpltps       %xmm0,%xmm9
+  .byte  68,15,40,21,241,19,0,0              // movaps        0x13f1(%rip),%xmm10        # 6570 <_sk_callback_sse2+0x103c>
+  .byte  69,15,92,208                        // subps         %xmm8,%xmm10
+  .byte  69,15,84,209                        // andps         %xmm9,%xmm10
+  .byte  69,15,85,200                        // andnps        %xmm8,%xmm9
+  .byte  69,15,86,202                        // orps          %xmm10,%xmm9
+  .byte  65,15,194,193,7                     // cmpordps      %xmm9,%xmm0
+  .byte  65,15,84,193                        // andps         %xmm9,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xy_to_radius_sse2
+.globl _sk_xy_to_radius_sse2
+FUNCTION(_sk_xy_to_radius_sse2)
+_sk_xy_to_radius_sse2:
+  .byte  15,89,192                           // mulps         %xmm0,%xmm0
+  .byte  68,15,40,193                        // movaps        %xmm1,%xmm8
+  .byte  69,15,89,192                        // mulps         %xmm8,%xmm8
+  .byte  68,15,88,192                        // addps         %xmm0,%xmm8
+  .byte  65,15,81,192                        // sqrtps        %xmm8,%xmm0
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_save_xy_sse2
+.globl _sk_save_xy_sse2
+FUNCTION(_sk_save_xy_sse2)
+_sk_save_xy_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,195,19,0,0               // movaps        0x13c3(%rip),%xmm8        # 6580 <_sk_callback_sse2+0x104c>
+  .byte  15,17,0                             // movups        %xmm0,(%rax)
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  69,15,88,200                        // addps         %xmm8,%xmm9
+  .byte  243,69,15,91,209                    // cvttps2dq     %xmm9,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  69,15,40,217                        // movaps        %xmm9,%xmm11
+  .byte  69,15,194,218,1                     // cmpltps       %xmm10,%xmm11
+  .byte  68,15,40,37,174,19,0,0              // movaps        0x13ae(%rip),%xmm12        # 6590 <_sk_callback_sse2+0x105c>
+  .byte  69,15,84,220                        // andps         %xmm12,%xmm11
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  69,15,92,202                        // subps         %xmm10,%xmm9
+  .byte  68,15,88,193                        // addps         %xmm1,%xmm8
+  .byte  243,69,15,91,208                    // cvttps2dq     %xmm8,%xmm10
+  .byte  69,15,91,210                        // cvtdq2ps      %xmm10,%xmm10
+  .byte  69,15,40,216                        // movaps        %xmm8,%xmm11
+  .byte  69,15,194,218,1                     // cmpltps       %xmm10,%xmm11
+  .byte  69,15,84,220                        // andps         %xmm12,%xmm11
+  .byte  69,15,92,211                        // subps         %xmm11,%xmm10
+  .byte  69,15,92,194                        // subps         %xmm10,%xmm8
+  .byte  15,17,72,32                         // movups        %xmm1,0x20(%rax)
+  .byte  68,15,17,72,64                      // movups        %xmm9,0x40(%rax)
+  .byte  68,15,17,64,96                      // movups        %xmm8,0x60(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_accumulate_sse2
+.globl _sk_accumulate_sse2
+FUNCTION(_sk_accumulate_sse2)
+_sk_accumulate_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,16,128,128,0,0,0              // movups        0x80(%rax),%xmm8
+  .byte  68,15,16,136,160,0,0,0              // movups        0xa0(%rax),%xmm9
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,192                        // mulps         %xmm0,%xmm8
+  .byte  65,15,88,224                        // addps         %xmm8,%xmm4
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,193                        // mulps         %xmm1,%xmm8
+  .byte  65,15,88,232                        // addps         %xmm8,%xmm5
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  68,15,89,194                        // mulps         %xmm2,%xmm8
+  .byte  65,15,88,240                        // addps         %xmm8,%xmm6
+  .byte  68,15,89,203                        // mulps         %xmm3,%xmm9
+  .byte  65,15,88,249                        // addps         %xmm9,%xmm7
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_nx_sse2
+.globl _sk_bilinear_nx_sse2
+FUNCTION(_sk_bilinear_nx_sse2)
+_sk_bilinear_nx_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,39,19,0,0                   // addps         0x1327(%rip),%xmm0        # 65a0 <_sk_callback_sse2+0x106c>
+  .byte  68,15,40,13,47,19,0,0               // movaps        0x132f(%rip),%xmm9        # 65b0 <_sk_callback_sse2+0x107c>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,17,136,128,0,0,0              // movups        %xmm9,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_px_sse2
+.globl _sk_bilinear_px_sse2
+FUNCTION(_sk_bilinear_px_sse2)
+_sk_bilinear_px_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,30,19,0,0                   // addps         0x131e(%rip),%xmm0        # 65c0 <_sk_callback_sse2+0x108c>
+  .byte  68,15,17,128,128,0,0,0              // movups        %xmm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_ny_sse2
+.globl _sk_bilinear_ny_sse2
+FUNCTION(_sk_bilinear_ny_sse2)
+_sk_bilinear_ny_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,16,19,0,0                  // addps         0x1310(%rip),%xmm1        # 65d0 <_sk_callback_sse2+0x109c>
+  .byte  68,15,40,13,24,19,0,0               // movaps        0x1318(%rip),%xmm9        # 65e0 <_sk_callback_sse2+0x10ac>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,17,136,160,0,0,0              // movups        %xmm9,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bilinear_py_sse2
+.globl _sk_bilinear_py_sse2
+FUNCTION(_sk_bilinear_py_sse2)
+_sk_bilinear_py_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,6,19,0,0                   // addps         0x1306(%rip),%xmm1        # 65f0 <_sk_callback_sse2+0x10bc>
+  .byte  68,15,17,128,160,0,0,0              // movups        %xmm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3x_sse2
+.globl _sk_bicubic_n3x_sse2
+FUNCTION(_sk_bicubic_n3x_sse2)
+_sk_bicubic_n3x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,249,18,0,0                  // addps         0x12f9(%rip),%xmm0        # 6600 <_sk_callback_sse2+0x10cc>
+  .byte  68,15,40,13,1,19,0,0                // movaps        0x1301(%rip),%xmm9        # 6610 <_sk_callback_sse2+0x10dc>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  69,15,89,192                        // mulps         %xmm8,%xmm8
+  .byte  68,15,89,13,253,18,0,0              // mulps         0x12fd(%rip),%xmm9        # 6620 <_sk_callback_sse2+0x10ec>
+  .byte  68,15,88,13,5,19,0,0                // addps         0x1305(%rip),%xmm9        # 6630 <_sk_callback_sse2+0x10fc>
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  68,15,17,136,128,0,0,0              // movups        %xmm9,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1x_sse2
+.globl _sk_bicubic_n1x_sse2
+FUNCTION(_sk_bicubic_n1x_sse2)
+_sk_bicubic_n1x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,244,18,0,0                  // addps         0x12f4(%rip),%xmm0        # 6640 <_sk_callback_sse2+0x110c>
+  .byte  68,15,40,13,252,18,0,0              // movaps        0x12fc(%rip),%xmm9        # 6650 <_sk_callback_sse2+0x111c>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,40,5,0,19,0,0                 // movaps        0x1300(%rip),%xmm8        # 6660 <_sk_callback_sse2+0x112c>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,4,19,0,0                 // addps         0x1304(%rip),%xmm8        # 6670 <_sk_callback_sse2+0x113c>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,8,19,0,0                 // addps         0x1308(%rip),%xmm8        # 6680 <_sk_callback_sse2+0x114c>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,12,19,0,0                // addps         0x130c(%rip),%xmm8        # 6690 <_sk_callback_sse2+0x115c>
+  .byte  68,15,17,128,128,0,0,0              // movups        %xmm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1x_sse2
+.globl _sk_bicubic_p1x_sse2
+FUNCTION(_sk_bicubic_p1x_sse2)
+_sk_bicubic_p1x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,6,19,0,0                 // movaps        0x1306(%rip),%xmm8        # 66a0 <_sk_callback_sse2+0x116c>
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,72,64                      // movups        0x40(%rax),%xmm9
+  .byte  65,15,88,192                        // addps         %xmm8,%xmm0
+  .byte  68,15,40,21,2,19,0,0                // movaps        0x1302(%rip),%xmm10        # 66b0 <_sk_callback_sse2+0x117c>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,6,19,0,0                // addps         0x1306(%rip),%xmm10        # 66c0 <_sk_callback_sse2+0x118c>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,208                        // addps         %xmm8,%xmm10
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,2,19,0,0                // addps         0x1302(%rip),%xmm10        # 66d0 <_sk_callback_sse2+0x119c>
+  .byte  68,15,17,144,128,0,0,0              // movups        %xmm10,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3x_sse2
+.globl _sk_bicubic_p3x_sse2
+FUNCTION(_sk_bicubic_p3x_sse2)
+_sk_bicubic_p3x_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,0                             // movups        (%rax),%xmm0
+  .byte  68,15,16,64,64                      // movups        0x40(%rax),%xmm8
+  .byte  15,88,5,245,18,0,0                  // addps         0x12f5(%rip),%xmm0        # 66e0 <_sk_callback_sse2+0x11ac>
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,89,201                        // mulps         %xmm9,%xmm9
+  .byte  68,15,89,5,245,18,0,0               // mulps         0x12f5(%rip),%xmm8        # 66f0 <_sk_callback_sse2+0x11bc>
+  .byte  68,15,88,5,253,18,0,0               // addps         0x12fd(%rip),%xmm8        # 6700 <_sk_callback_sse2+0x11cc>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,17,128,128,0,0,0              // movups        %xmm8,0x80(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n3y_sse2
+.globl _sk_bicubic_n3y_sse2
+FUNCTION(_sk_bicubic_n3y_sse2)
+_sk_bicubic_n3y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,235,18,0,0                 // addps         0x12eb(%rip),%xmm1        # 6710 <_sk_callback_sse2+0x11dc>
+  .byte  68,15,40,13,243,18,0,0              // movaps        0x12f3(%rip),%xmm9        # 6720 <_sk_callback_sse2+0x11ec>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  69,15,40,193                        // movaps        %xmm9,%xmm8
+  .byte  69,15,89,192                        // mulps         %xmm8,%xmm8
+  .byte  68,15,89,13,239,18,0,0              // mulps         0x12ef(%rip),%xmm9        # 6730 <_sk_callback_sse2+0x11fc>
+  .byte  68,15,88,13,247,18,0,0              // addps         0x12f7(%rip),%xmm9        # 6740 <_sk_callback_sse2+0x120c>
+  .byte  69,15,89,200                        // mulps         %xmm8,%xmm9
+  .byte  68,15,17,136,160,0,0,0              // movups        %xmm9,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_n1y_sse2
+.globl _sk_bicubic_n1y_sse2
+FUNCTION(_sk_bicubic_n1y_sse2)
+_sk_bicubic_n1y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,229,18,0,0                 // addps         0x12e5(%rip),%xmm1        # 6750 <_sk_callback_sse2+0x121c>
+  .byte  68,15,40,13,237,18,0,0              // movaps        0x12ed(%rip),%xmm9        # 6760 <_sk_callback_sse2+0x122c>
+  .byte  69,15,92,200                        // subps         %xmm8,%xmm9
+  .byte  68,15,40,5,241,18,0,0               // movaps        0x12f1(%rip),%xmm8        # 6770 <_sk_callback_sse2+0x123c>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,245,18,0,0               // addps         0x12f5(%rip),%xmm8        # 6780 <_sk_callback_sse2+0x124c>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,249,18,0,0               // addps         0x12f9(%rip),%xmm8        # 6790 <_sk_callback_sse2+0x125c>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,88,5,253,18,0,0               // addps         0x12fd(%rip),%xmm8        # 67a0 <_sk_callback_sse2+0x126c>
+  .byte  68,15,17,128,160,0,0,0              // movups        %xmm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p1y_sse2
+.globl _sk_bicubic_p1y_sse2
+FUNCTION(_sk_bicubic_p1y_sse2)
+_sk_bicubic_p1y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  68,15,40,5,247,18,0,0               // movaps        0x12f7(%rip),%xmm8        # 67b0 <_sk_callback_sse2+0x127c>
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,72,96                      // movups        0x60(%rax),%xmm9
+  .byte  65,15,88,200                        // addps         %xmm8,%xmm1
+  .byte  68,15,40,21,242,18,0,0              // movaps        0x12f2(%rip),%xmm10        # 67c0 <_sk_callback_sse2+0x128c>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,246,18,0,0              // addps         0x12f6(%rip),%xmm10        # 67d0 <_sk_callback_sse2+0x129c>
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  69,15,88,208                        // addps         %xmm8,%xmm10
+  .byte  69,15,89,209                        // mulps         %xmm9,%xmm10
+  .byte  68,15,88,21,242,18,0,0              // addps         0x12f2(%rip),%xmm10        # 67e0 <_sk_callback_sse2+0x12ac>
+  .byte  68,15,17,144,160,0,0,0              // movups        %xmm10,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_bicubic_p3y_sse2
+.globl _sk_bicubic_p3y_sse2
+FUNCTION(_sk_bicubic_p3y_sse2)
+_sk_bicubic_p3y_sse2:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,72,32                         // movups        0x20(%rax),%xmm1
+  .byte  68,15,16,64,96                      // movups        0x60(%rax),%xmm8
+  .byte  15,88,13,228,18,0,0                 // addps         0x12e4(%rip),%xmm1        # 67f0 <_sk_callback_sse2+0x12bc>
+  .byte  69,15,40,200                        // movaps        %xmm8,%xmm9
+  .byte  69,15,89,201                        // mulps         %xmm9,%xmm9
+  .byte  68,15,89,5,228,18,0,0               // mulps         0x12e4(%rip),%xmm8        # 6800 <_sk_callback_sse2+0x12cc>
+  .byte  68,15,88,5,236,18,0,0               // addps         0x12ec(%rip),%xmm8        # 6810 <_sk_callback_sse2+0x12dc>
+  .byte  69,15,89,193                        // mulps         %xmm9,%xmm8
+  .byte  68,15,17,128,160,0,0,0              // movups        %xmm8,0xa0(%rax)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_callback_sse2
+.globl _sk_callback_sse2
+FUNCTION(_sk_callback_sse2)
+_sk_callback_sse2:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  72,131,236,72                       // sub           $0x48,%rsp
+  .byte  15,41,125,144                       // movaps        %xmm7,-0x70(%rbp)
+  .byte  15,41,117,160                       // movaps        %xmm6,-0x60(%rbp)
+  .byte  15,41,109,176                       // movaps        %xmm5,-0x50(%rbp)
+  .byte  15,41,101,192                       // movaps        %xmm4,-0x40(%rbp)
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  72,137,77,208                       // mov           %rcx,-0x30(%rbp)
+  .byte  73,137,215                          // mov           %rdx,%r15
+  .byte  73,137,252                          // mov           %rdi,%r12
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,198                          // mov           %rax,%r14
+  .byte  73,137,245                          // mov           %rsi,%r13
+  .byte  15,40,224                           // movaps        %xmm0,%xmm4
+  .byte  15,20,225                           // unpcklps      %xmm1,%xmm4
+  .byte  15,40,234                           // movaps        %xmm2,%xmm5
+  .byte  15,20,235                           // unpcklps      %xmm3,%xmm5
+  .byte  15,21,193                           // unpckhps      %xmm1,%xmm0
+  .byte  15,21,211                           // unpckhps      %xmm3,%xmm2
+  .byte  15,40,204                           // movaps        %xmm4,%xmm1
+  .byte  102,15,20,205                       // unpcklpd      %xmm5,%xmm1
+  .byte  15,18,236                           // movhlps       %xmm4,%xmm5
+  .byte  15,40,216                           // movaps        %xmm0,%xmm3
+  .byte  102,15,20,218                       // unpcklpd      %xmm2,%xmm3
+  .byte  102,65,15,17,78,8                   // movupd        %xmm1,0x8(%r14)
+  .byte  15,18,208                           // movhlps       %xmm0,%xmm2
+  .byte  65,15,17,110,24                     // movups        %xmm5,0x18(%r14)
+  .byte  102,65,15,17,94,40                  // movupd        %xmm3,0x28(%r14)
+  .byte  65,15,17,86,56                      // movups        %xmm2,0x38(%r14)
+  .byte  72,133,219                          // test          %rbx,%rbx
+  .byte  190,4,0,0,0                         // mov           $0x4,%esi
+  .byte  15,69,243                           // cmovne        %ebx,%esi
+  .byte  76,137,247                          // mov           %r14,%rdi
+  .byte  65,255,22                           // callq         *(%r14)
+  .byte  73,139,134,136,0,0,0                // mov           0x88(%r14),%rax
+  .byte  15,16,32                            // movups        (%rax),%xmm4
+  .byte  15,16,64,16                         // movups        0x10(%rax),%xmm0
+  .byte  15,16,88,32                         // movups        0x20(%rax),%xmm3
+  .byte  15,16,80,48                         // movups        0x30(%rax),%xmm2
+  .byte  15,40,236                           // movaps        %xmm4,%xmm5
+  .byte  15,20,232                           // unpcklps      %xmm0,%xmm5
+  .byte  15,40,203                           // movaps        %xmm3,%xmm1
+  .byte  15,20,202                           // unpcklps      %xmm2,%xmm1
+  .byte  15,21,224                           // unpckhps      %xmm0,%xmm4
+  .byte  15,21,218                           // unpckhps      %xmm2,%xmm3
+  .byte  15,40,197                           // movaps        %xmm5,%xmm0
+  .byte  102,15,20,193                       // unpcklpd      %xmm1,%xmm0
+  .byte  15,18,205                           // movhlps       %xmm5,%xmm1
+  .byte  15,40,212                           // movaps        %xmm4,%xmm2
+  .byte  102,15,20,211                       // unpcklpd      %xmm3,%xmm2
+  .byte  15,18,220                           // movhlps       %xmm4,%xmm3
+  .byte  76,137,238                          // mov           %r13,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,137,231                          // mov           %r12,%rdi
+  .byte  76,137,250                          // mov           %r15,%rdx
+  .byte  72,139,77,208                       // mov           -0x30(%rbp),%rcx
+  .byte  73,137,216                          // mov           %rbx,%r8
+  .byte  15,40,101,192                       // movaps        -0x40(%rbp),%xmm4
+  .byte  15,40,109,176                       // movaps        -0x50(%rbp),%xmm5
+  .byte  15,40,117,160                       // movaps        -0x60(%rbp),%xmm6
+  .byte  15,40,125,144                       // movaps        -0x70(%rbp),%xmm7
+  .byte  72,131,196,72                       // add           $0x48,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  255,224                             // jmpq          *%rax
+
+BALIGN16
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,1                            // cmpb          $0x1,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  4,0                                 // add           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  2,0                                 // add           (%rax),%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  128,60,0,0                          // cmpb          $0x0,(%rax,%rax,1)
+  .byte  252                                 // cld
+  .byte  190,0,0,252,190                     // mov           $0xbefc0000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  252                                 // cld
+  .byte  190,0,0,252,190                     // mov           $0xbefc0000,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  191,0,0,128,191                     // mov           $0xbf800000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,191,0,0,128,191,0               // cmpb          $0x0,-0x40800000(%rdi)
+  .byte  0,224                               // add           %ah,%al
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  224,64                              // loopne        5808 <.literal16+0x1d8>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,64                              // loopne        580c <.literal16+0x1dc>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,64                              // loopne        5810 <.literal16+0x1e0>
+  .byte  154                                 // (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5831 <.literal16+0x201>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5835 <.literal16+0x205>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5839 <.literal16+0x209>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 583d <.literal16+0x20d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5871 <.literal16+0x241>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5875 <.literal16+0x245>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 5879 <.literal16+0x249>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 587d <.literal16+0x24d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58b1 <.literal16+0x281>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58b5 <.literal16+0x285>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58b9 <.literal16+0x289>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58bd <.literal16+0x28d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,154                          // cmpb          $0x9a,(%rdi)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,61,10,23,63,61                   // ds            cmp $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  61,10,23,63,61                      // cmp           $0x3d3f170a,%eax
+  .byte  10,23                               // or            (%rdi),%dl
+  .byte  63                                  // (bad)
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58f1 <.literal16+0x2c1>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58f5 <.literal16+0x2c5>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58f9 <.literal16+0x2c9>
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,225,61                           // rex.RXB       loope 58fd <.literal16+0x2cd>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,255                          // cmpb          $0xff,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,255                               // add           %bh,%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,127                    // add           %al,0x7f00003f(%rax)
+  .byte  67,0,0                              // rex.XB        add %al,(%r8)
+  .byte  127,67                              // jg            593b <.literal16+0x30b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            593f <.literal16+0x30f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5943 <.literal16+0x313>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  145                                 // xchg          %eax,%ecx
+  .byte  131,158,61,145,131,158,61           // sbbl          $0x3d,-0x617c6ec3(%rsi)
+  .byte  145                                 // xchg          %eax,%ecx
+  .byte  131,158,61,145,131,158,61           // sbbl          $0x3d,-0x617c6ec3(%rsi)
+  .byte  154                                 // (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,154                              // ds            (bad)
+  .byte  153                                 // cltd
+  .byte  153                                 // cltd
+  .byte  62,92                               // ds            pop %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  92                                  // pop           %rsp
+  .byte  143                                 // (bad)
+  .byte  50,63                               // xor           (%rdi),%bh
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  35,59                               // and           (%rbx),%edi
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,97                               // rex.RXB       (bad)
+  .byte  61,174,71,97,61                     // cmp           $0x3d6147ae,%eax
+  .byte  174                                 // scas          %es:(%rdi),%al
+  .byte  71,97                               // rex.RXB       (bad)
+  .byte  61,174,71,97,61                     // cmp           $0x3d6147ae,%eax
+  .byte  82                                  // push          %rdx
+  .byte  184,78,65,82,184                    // mov           $0xb852414e,%eax
+  .byte  78                                  // rex.WRX
+  .byte  65,82                               // push          %r10
+  .byte  184,78,65,82,184                    // mov           $0xb852414e,%eax
+  .byte  78                                  // rex.WRX
+  .byte  65,57,215                           // cmp           %edx,%r15d
+  .byte  32,187,57,215,32,187                // and           %bh,-0x44df28c7(%rbx)
+  .byte  57,215                              // cmp           %edx,%edi
+  .byte  32,187,57,215,32,187                // and           %bh,-0x44df28c7(%rbx)
+  .byte  186,159,98,60,186                   // mov           $0xba3c629f,%edx
+  .byte  159                                 // lahf
+  .byte  98                                  // (bad)
+  .byte  60,186                              // cmp           $0xba,%al
+  .byte  159                                 // lahf
+  .byte  98                                  // (bad)
+  .byte  60,186                              // cmp           $0xba,%al
+  .byte  159                                 // lahf
+  .byte  98                                  // (bad)
+  .byte  60,109                              // cmp           $0x6d,%al
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  109                                 // insl          (%dx),%es:(%rdi)
+  .byte  165                                 // movsl         %ds:(%rsi),%es:(%rdi)
+  .byte  144                                 // nop
+  .byte  63                                  // (bad)
+  .byte  252                                 // cld
+  .byte  191,16,62,252,191                   // mov           $0xbffc3e10,%edi
+  .byte  16,62                               // adc           %bh,(%rsi)
+  .byte  252                                 // cld
+  .byte  191,16,62,252,191                   // mov           $0xbffc3e10,%edi
+  .byte  16,62                               // adc           %bh,(%rsi)
+  .byte  168,177                             // test          $0xb1,%al
+  .byte  152                                 // cwtl
+  .byte  59,168,177,152,59,168               // cmp           -0x57c4674f(%rax),%ebp
+  .byte  177,152                             // mov           $0x98,%cl
+  .byte  59,168,177,152,59,0                 // cmp           0x3b98b1(%rax),%ebp
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,192                    // add           %al,-0x3fffffc1(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  0,64,0                              // add           %al,0x0(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  0,64,0                              // add           %al,0x0(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  64,0,0                              // add           %al,(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,171,170                      // addb          $0xaa,-0x55(%rax)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  62,0,0                              // add           %al,%ds:(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,171                          // cmpb          $0xab,(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,63                               // sub           (%rdi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  192,64,0,0                          // rolb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,0,0                          // addb          $0x0,0x0(%rax)
+  .byte  128,64,171,170                      // addb          $0xaa,-0x55(%rax)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  42,62                               // sub           (%rsi),%bh
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,255,0,0,0                // addb          $0x0,0xff3b(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,248,0,0                // addb          $0x0,0xf8003b(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        5b29 <.literal16+0x4f9>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5b2d <.literal16+0x4fd>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5b31 <.literal16+0x501>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        5b35 <.literal16+0x505>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,8,33,4,61                   // and           %eax,0x3d042108(,%rdi,1)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,255,0,255,0              // addb          $0x0,-0xff00c5(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5c0b <.literal16+0x5db>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5c0f <.literal16+0x5df>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5c13 <.literal16+0x5e3>
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,129,128,128,59           // addb          $0x3b,-0x7f7f7ec5(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,0                            // cmpb          $0x0,(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5c04 <.literal16+0x5d4>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5c08 <.literal16+0x5d8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5c0c <.literal16+0x5dc>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5c10 <.literal16+0x5e0>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5c95 <.literal16+0x665>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           5bf9 <.literal16+0x5c9>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a39c3a <_sk_callback_sse2+0xffffffffe9a34706>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,81                           // cmpb          $0x51,(%rdi)
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,52,0                              // add           %dh,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5cd4 <.literal16+0x6a4>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5cd8 <.literal16+0x6a8>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5cdc <.literal16+0x6ac>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5ce0 <.literal16+0x6b0>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5d65 <.literal16+0x735>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           5cc9 <.literal16+0x699>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a39d0a <_sk_callback_sse2+0xffffffffe9a347d6>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,81                           // cmpb          $0x51,(%rdi)
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,52,0                              // add           %dh,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5da4 <.literal16+0x774>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5da8 <.literal16+0x778>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5dac <.literal16+0x77c>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5db0 <.literal16+0x780>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5e35 <.literal16+0x805>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           5d99 <.literal16+0x769>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a39dda <_sk_callback_sse2+0xffffffffe9a348a6>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,81                           // cmpb          $0x51,(%rdi)
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,52,0                              // add           %dh,(%rax,%rax,1)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,0                                // xor           $0x0,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  52,255                              // xor           $0xff,%al
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5e74 <.literal16+0x844>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5e78 <.literal16+0x848>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5e7c <.literal16+0x84c>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  127,0                               // jg            5e80 <.literal16+0x850>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  119,115                             // ja            5f05 <.literal16+0x8d5>
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,119,115                         // retq          $0x7377
+  .byte  248                                 // clc
+  .byte  194,117,191                         // retq          $0xbf75
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  117,191                             // jne           5e69 <.literal16+0x839>
+  .byte  191,63,117,191,191                  // mov           $0xbfbf753f,%edi
+  .byte  63                                  // (bad)
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  249                                 // stc
+  .byte  68,180,62                           // rex.R         mov $0x3e,%spl
+  .byte  163,233,220,63,163,233,220,63,163   // movabs        %eax,0xa33fdce9a33fdce9
+  .byte  233,220,63,163,233                  // jmpq          ffffffffe9a39eaa <_sk_callback_sse2+0xffffffffe9a34976>
+  .byte  220,63                              // fdivrl        (%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,81                           // cmpb          $0x51,(%rdi)
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,81                               // rex.X         push %rcx
+  .byte  140,242                             // mov           %?,%edx
+  .byte  66,141,188,190,63,141,188,190       // lea           -0x414372c1(%rsi,%r15,4),%edi
+  .byte  63                                  // (bad)
+  .byte  141,188,190,63,141,188,190          // lea           -0x414372c1(%rsi,%rdi,4),%edi
+  .byte  63                                  // (bad)
+  .byte  248                                 // clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,248                              // rex           clc
+  .byte  245                                 // cmc
+  .byte  154                                 // (bad)
+  .byte  64,254                              // rex           (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,254                              // rex.B         (bad)
+  .byte  210,221                             // rcr           %cl,%ch
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  0,75,0                              // add           %cl,0x0(%rbx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  75,0,0                              // rex.WXB       add %al,(%r8)
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  200,66,0,0                          // enterq        $0x42,$0x0
+  .byte  127,67                              // jg            5f87 <.literal16+0x957>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5f8b <.literal16+0x95b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5f8f <.literal16+0x95f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            5f93 <.literal16+0x963>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,195                               // add           %al,%bl
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,65,0,0                          // addb          $0x0,0x0(%rcx)
+  .byte  128,65,0,0                          // addb          $0x0,0x0(%rcx)
+  .byte  128,65,0,0                          // addb          $0x0,0x0(%rcx)
+  .byte  128,65,203,61                       // addb          $0x3d,-0x35(%rcx)
+  .byte  13,60,203,61,13                     // or            $0xd3dcb3c,%eax
+  .byte  60,203                              // cmp           $0xcb,%al
+  .byte  61,13,60,203,61                     // cmp           $0x3dcb3c0d,%eax
+  .byte  13,60,111,18,3                      // or            $0x3126f3c,%eax
+  .byte  59,111,18                           // cmp           0x12(%rdi),%ebp
+  .byte  3,59                                // add           (%rbx),%edi
+  .byte  111                                 // outsl         %ds:(%rsi),(%dx)
+  .byte  18,3                                // adc           (%rbx),%al
+  .byte  59,111,18                           // cmp           0x12(%rdi),%ebp
+  .byte  3,59                                // add           (%rbx),%edi
+  .byte  10,215                              // or            %bh,%dl
+  .byte  163,59,10,215,163,59,10,215,163     // movabs        %eax,0xa3d70a3ba3d70a3b
+  .byte  59,10                               // cmp           (%rdx),%ecx
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  163,59,194,24,17,60,194,24,17       // movabs        %eax,0x1118c23c1118c23b
+  .byte  60,194                              // cmp           $0xc2,%al
+  .byte  24,17                               // sbb           %dl,(%rcx)
+  .byte  60,194                              // cmp           $0xc2,%al
+  .byte  24,17                               // sbb           %dl,(%rcx)
+  .byte  60,203                              // cmp           $0xcb,%al
+  .byte  61,13,190,203,61                    // cmp           $0x3dcbbe0d,%eax
+  .byte  13,190,203,61,13                    // or            $0xd3dcbbe,%eax
+  .byte  190,203,61,13,190                   // mov           $0xbe0d3dcb,%esi
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  80                                  // push          %rax
+  .byte  128,3,62                            // addb          $0x3e,(%rbx)
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           6013 <.literal16+0x9e3>
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           6017 <.literal16+0x9e7>
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           601b <.literal16+0x9eb>
+  .byte  31                                  // (bad)
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  118,63                              // jbe           601f <.literal16+0x9ef>
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  246,64,83,63                        // testb         $0x3f,0x53(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,129,128,128,59           // addb          $0x3b,-0x7f7f7ec5(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,0                            // cmpb          $0x0,(%rbx)
+  .byte  0,127,67                            // add           %bh,0x43(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            606b <.literal16+0xa3b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            606f <.literal16+0xa3f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            6073 <.literal16+0xa43>
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,0,128,63               // addb          $0x3f,-0x7fffffc5(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,0,128,63               // addb          $0x3f,-0x7fffffc5(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,248,0,0                // addb          $0x0,0xf8003b(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        60c9 <.literal16+0xa99>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        60cd <.literal16+0xa9d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        60d1 <.literal16+0xaa1>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        60d5 <.literal16+0xaa5>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,8,33,4,61                   // and           %eax,0x3d042108(,%rdi,1)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  248                                 // clc
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,248                               // add           %bh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  132,55                              // test          %dh,(%rdi)
+  .byte  224,7                               // loopne        6139 <.literal16+0xb09>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        613d <.literal16+0xb0d>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        6141 <.literal16+0xb11>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  224,7                               // loopne        6145 <.literal16+0xb15>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  33,8                                // and           %ecx,(%rax)
+  .byte  2,58                                // add           (%rdx),%bh
+  .byte  31                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,31                                // add           %bl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,8                                 // add           %cl,(%rax)
+  .byte  33,4,61,8,33,4,61                   // and           %eax,0x3d042108(,%rdi,1)
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  8,33                                // or            %ah,(%rcx)
+  .byte  4,61                                // add           $0x3d,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,248                               // add           %bh,%al
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  248                                 // clc
+  .byte  65,0,0                              // add           %al,(%r8)
+  .byte  124,66                              // jl            61d6 <.literal16+0xba6>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  124,66                              // jl            61da <.literal16+0xbaa>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  124,66                              // jl            61de <.literal16+0xbae>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  124,66                              // jl            61e2 <.literal16+0xbb2>
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  240,0,0                             // lock          add %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,59,137                // add           %cl,-0x76c47778(%rcx)
+  .byte  136,136,59,137,136,136              // mov           %cl,-0x777776c5(%rax)
+  .byte  59,137,136,136,59,15                // cmp           0xf3b8888(%rcx),%ecx
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,61,137                // add           %cl,-0x76c27778(%rcx)
+  .byte  136,136,61,137,136,136              // mov           %cl,-0x777776c3(%rax)
+  .byte  61,137,136,136,61                   // cmp           $0x3d888889,%eax
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  137,136,136,55,137,136              // mov           %ecx,-0x7776c878(%rax)
+  .byte  136,55                              // mov           %dh,(%rdi)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  137,136,136,57,137,136              // mov           %ecx,-0x7776c678(%rax)
+  .byte  136,57                              // mov           %bh,(%rcx)
+  .byte  240,0,0                             // lock          add %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,240                               // add           %dh,%al
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,59,137                // add           %cl,-0x76c47778(%rcx)
+  .byte  136,136,59,137,136,136              // mov           %cl,-0x777776c5(%rax)
+  .byte  59,137,136,136,59,15                // cmp           0xf3b8888(%rcx),%ecx
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,15                                // add           %cl,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,137,136,136,61,137                // add           %cl,-0x76c27778(%rcx)
+  .byte  136,136,61,137,136,136              // mov           %cl,-0x777776c3(%rax)
+  .byte  61,137,136,136,61                   // cmp           $0x3d888889,%eax
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            62e5 <.literal16+0xcb5>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            62e9 <.literal16+0xcb9>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            62ed <.literal16+0xcbd>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  112,65                              // jo            62f1 <.literal16+0xcc1>
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,255,0,0,0                // addb          $0x0,0xff3b(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  129,128,128,59,129,128,128,59,129,128// addl          $0x80813b80,-0x7f7ec480(%rax)
+  .byte  128,59,129                          // cmpb          $0x81,(%rbx)
+  .byte  128,128,59,0,0,127,67               // addb          $0x43,0x7f00003b(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            633b <.literal16+0xd0b>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            633f <.literal16+0xd0f>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  127,67                              // jg            6343 <.literal16+0xd13>
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  128,0,4                             // addb          $0x4,(%rax)
+  .byte  0,128,0,4,0,128                     // add           %al,-0x7ffffc00(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,0                       // add           %al,0x0(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,56                                // add           %bh,(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  128,0,4                             // addb          $0x4,(%rax)
+  .byte  0,128,0,4,0,128                     // add           %al,-0x7ffffc00(%rax)
+  .byte  0,4,0                               // add           %al,(%rax,%rax,1)
+  .byte  128,0,0                             // addb          $0x0,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,128,0,0,0,128                     // add           %al,-0x80000000(%rax)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,56,0                            // cmpb          $0x0,(%rax)
+  .byte  0,128,56,0,0,128                    // add           %al,-0x7fffffc8(%rax)
+  .byte  56,0                                // cmp           %al,(%rax)
+  .byte  0,128,56,0,64,254                   // add           %al,-0x1bfffc8(%rax)
+  .byte  255,0                               // incl          (%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,0                               // incl          (%rax)
+  .byte  64,254                              // rex           (bad)
+  .byte  255,128,0,128,55,128                // incl          -0x7fc88000(%rax)
+  .byte  0,128,55,128,0,128                  // add           %al,-0x7fff7fc9(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  128,0,128                           // addb          $0x80,(%rax)
+  .byte  55                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  255                                 // (bad)
+  .byte  127,71                              // jg            642b <.literal16+0xdfb>
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            642f <.literal16+0xdff>
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            6433 <.literal16+0xe03>
+  .byte  0,255                               // add           %bh,%bh
+  .byte  127,71                              // jg            6437 <.literal16+0xe07>
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,191,0,0,128,191,0               // cmpb          $0x0,-0x40800000(%rdi)
+  .byte  0,128,191,0,0,128                   // add           %al,-0x7fffff41(%rax)
+  .byte  191,0,0,0,63                        // mov           $0x3f000000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,208                          // cmpb          $0xd0,(%rdi)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,208                              // ds            (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,208                              // ds            (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,208                              // ds            (bad)
+  .byte  179,89                              // mov           $0x59,%bl
+  .byte  62,89                               // ds            pop %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  89                                  // pop           %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  89                                  // pop           %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  89                                  // pop           %rcx
+  .byte  23                                  // (bad)
+  .byte  55                                  // (bad)
+  .byte  63                                  // (bad)
+  .byte  152                                 // cwtl
+  .byte  221,147,61,152,221,147              // fstl          -0x6c2267c3(%rbx)
+  .byte  61,152,221,147,61                   // cmp           $0x3d93dd98,%eax
+  .byte  152                                 // cwtl
+  .byte  221,147,61,45,16,17                 // fstl          0x11102d3d(%rbx)
+  .byte  192,45,16,17,192,45,16              // shrb          $0x10,0x2dc01110(%rip)        # 2dc075ca <_sk_callback_sse2+0x2dc02096>
+  .byte  17,192                              // adc           %eax,%eax
+  .byte  45,16,17,192,18                     // sub           $0x12c01110,%eax
+  .byte  120,57                              // js            64fc <.literal16+0xecc>
+  .byte  64,18,120,57                        // adc           0x39(%rax),%dil
+  .byte  64,18,120,57                        // adc           0x39(%rax),%dil
+  .byte  64,18,120,57                        // adc           0x39(%rax),%dil
+  .byte  64,32,148,90,62,32,148,90           // and           %dl,0x5a94203e(%rdx,%rbx,2)
+  .byte  62,32,148,90,62,32,148,90           // and           %dl,%ds:0x5a94203e(%rdx,%rbx,2)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,4,157                            // ds            add $0x9d,%al
+  .byte  30                                  // (bad)
+  .byte  62,0,24                             // add           %bl,%ds:(%rax)
+  .byte  161,57,0,24,161,57,0,24,161         // movabs        0xa1180039a1180039,%eax
+  .byte  57,0                                // cmp           %eax,(%rax)
+  .byte  24,161,57,1,0,0                     // sbb           %ah,0x139(%rcx)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,1                                 // add           %al,(%rcx)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,111,43                            // add           %ch,0x2b(%rdi)
+  .byte  231,187                             // out           %eax,$0xbb
+  .byte  111                                 // outsl         %ds:(%rsi),(%dx)
+  .byte  43,231                              // sub           %edi,%esp
+  .byte  187,111,43,231,187                  // mov           $0xbbe72b6f,%ebx
+  .byte  111                                 // outsl         %ds:(%rsi),(%dx)
+  .byte  43,231                              // sub           %edi,%esp
+  .byte  187,159,215,202,60                  // mov           $0x3ccad79f,%ebx
+  .byte  159                                 // lahf
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  202,60,159                          // lret          $0x9f3c
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  202,60,159                          // lret          $0x9f3c
+  .byte  215                                 // xlat          %ds:(%rbx)
+  .byte  202,60,212                          // lret          $0xd43c
+  .byte  100,84                              // fs            push %rsp
+  .byte  189,212,100,84,189                  // mov           $0xbd5464d4,%ebp
+  .byte  212                                 // (bad)
+  .byte  100,84                              // fs            push %rsp
+  .byte  189,212,100,84,189                  // mov           $0xbd5464d4,%ebp
+  .byte  169,240,34,62,169                   // test          $0xa93e22f0,%eax
+  .byte  240,34,62                           // lock          and (%rsi),%bh
+  .byte  169,240,34,62,169                   // test          $0xa93e22f0,%eax
+  .byte  240,34,62                           // lock          and (%rsi),%bh
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,62,0                            // cmpb          $0x0,(%rsi)
+  .byte  0,128,62,0,0,128                    // add           %al,-0x7fffffc2(%rax)
+  .byte  62,0,0                              // add           %al,%ds:(%rax)
+  .byte  128,62,0                            // cmpb          $0x0,(%rsi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  191,0,0,0,191                       // mov           $0xbf000000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,192,191,0               // sarb          $0x0,-0x40400000(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  191,0,0,192,191                     // mov           $0xbfc00000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,114                          // cmpb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,114,28                           // jb,pt         6642 <.literal16+0x1012>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6646 <.literal16+0x1016>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         664a <.literal16+0x101a>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,85                           // cmpb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63f4d5 <_sk_callback_sse2+0x3d639fa1>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63f515 <_sk_callback_sse2+0x3d639fe1>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  114,28                              // jb            670e <.literal16+0x10de>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6712 <.literal16+0x10e2>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6716 <.literal16+0x10e6>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         671a <.literal16+0x10ea>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,191,0,0,192,191,0               // sarb          $0x0,-0x40400000(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  191,0,0,192,191                     // mov           $0xbfc00000,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,114                          // cmpb          $0x72,(%rdi)
+  .byte  28,199                              // sbb           $0xc7,%al
+  .byte  62,114,28                           // jb,pt         6752 <.literal16+0x1122>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6756 <.literal16+0x1126>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         675a <.literal16+0x112a>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,191,0,0,0,191                     // add           %bh,-0x41000000(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,0                            // cmpb          $0x0,(%rdi)
+  .byte  0,128,63,0,0,128                    // add           %al,-0x7fffffc1(%rax)
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,63,85                           // cmpb          $0x55,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63f5e5 <_sk_callback_sse2+0x3d63a0b1>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  0,63                                // add           %bh,(%rdi)
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  85                                  // push          %rbp
+  .byte  85                                  // push          %rbp
+  .byte  149                                 // xchg          %eax,%ebp
+  .byte  191,85,85,149,191                   // mov           $0xbf955555,%edi
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  57,142,99,61,57,142                 // cmp           %ecx,-0x71c6c29d(%rsi)
+  .byte  99,61,57,142,99,61                  // movslq        0x3d638e39(%rip),%edi        # 3d63f625 <_sk_callback_sse2+0x3d63a0f1>
+  .byte  57,142,99,61,0,0                    // cmp           %ecx,0x3d63(%rsi)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  192,63,0                            // sarb          $0x0,(%rdi)
+  .byte  0,192                               // add           %al,%al
+  .byte  63                                  // (bad)
+  .byte  114,28                              // jb            681e <.literal16+0x11ee>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6822 <_sk_callback_sse2+0x12ee>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         6826 <_sk_callback_sse2+0x12f2>
+  .byte  199                                 // (bad)
+  .byte  62,114,28                           // jb,pt         682a <_sk_callback_sse2+0x12f6>
+  .byte  199                                 // (bad)
+  .byte  62,171                              // ds            stos %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+  .byte  171                                 // stos          %eax,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  170                                 // stos          %al,%es:(%rdi)
+  .byte  190,171,170,170,190                 // mov           $0xbeaaaaab,%esi
+BALIGN32
+
+HIDDEN _sk_start_pipeline_ssse3_lowp
+.globl _sk_start_pipeline_ssse3_lowp
+FUNCTION(_sk_start_pipeline_ssse3_lowp)
+_sk_start_pipeline_ssse3_lowp:
+  .byte  85                                  // push          %rbp
+  .byte  72,137,229                          // mov           %rsp,%rbp
+  .byte  65,87                               // push          %r15
+  .byte  65,86                               // push          %r14
+  .byte  65,85                               // push          %r13
+  .byte  65,84                               // push          %r12
+  .byte  83                                  // push          %rbx
+  .byte  80                                  // push          %rax
+  .byte  76,137,195                          // mov           %r8,%rbx
+  .byte  73,137,215                          // mov           %rdx,%r15
+  .byte  73,137,244                          // mov           %rsi,%r12
+  .byte  73,137,254                          // mov           %rdi,%r14
+  .byte  72,137,206                          // mov           %rcx,%rsi
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  73,137,197                          // mov           %rax,%r13
+  .byte  73,141,78,8                         // lea           0x8(%r14),%rcx
+  .byte  76,57,249                           // cmp           %r15,%rcx
+  .byte  118,5                               // jbe           30 <_sk_start_pipeline_ssse3_lowp+0x30>
+  .byte  76,137,242                          // mov           %r14,%rdx
+  .byte  235,72                              // jmp           78 <_sk_start_pipeline_ssse3_lowp+0x78>
+  .byte  76,137,125,208                      // mov           %r15,-0x30(%rbp)
+  .byte  65,184,0,0,0,0                      // mov           $0x0,%r8d
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  72,137,223                          // mov           %rbx,%rdi
+  .byte  73,137,247                          // mov           %rsi,%r15
+  .byte  76,137,242                          // mov           %r14,%rdx
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  65,255,213                          // callq         *%r13
+  .byte  76,137,254                          // mov           %r15,%rsi
+  .byte  76,139,125,208                      // mov           -0x30(%rbp),%r15
+  .byte  73,141,86,8                         // lea           0x8(%r14),%rdx
+  .byte  73,131,198,16                       // add           $0x10,%r14
+  .byte  77,57,254                           // cmp           %r15,%r14
+  .byte  73,137,214                          // mov           %rdx,%r14
+  .byte  118,188                             // jbe           34 <_sk_start_pipeline_ssse3_lowp+0x34>
+  .byte  77,137,248                          // mov           %r15,%r8
+  .byte  73,41,208                           // sub           %rdx,%r8
+  .byte  116,33                              // je            a1 <_sk_start_pipeline_ssse3_lowp+0xa1>
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  15,87,228                           // xorps         %xmm4,%xmm4
+  .byte  15,87,237                           // xorps         %xmm5,%xmm5
+  .byte  15,87,246                           // xorps         %xmm6,%xmm6
+  .byte  15,87,255                           // xorps         %xmm7,%xmm7
+  .byte  72,137,223                          // mov           %rbx,%rdi
+  .byte  76,137,225                          // mov           %r12,%rcx
+  .byte  65,255,213                          // callq         *%r13
+  .byte  76,137,248                          // mov           %r15,%rax
+  .byte  72,131,196,8                        // add           $0x8,%rsp
+  .byte  91                                  // pop           %rbx
+  .byte  65,92                               // pop           %r12
+  .byte  65,93                               // pop           %r13
+  .byte  65,94                               // pop           %r14
+  .byte  65,95                               // pop           %r15
+  .byte  93                                  // pop           %rbp
+  .byte  195                                 // retq
+
+HIDDEN _sk_just_return_ssse3_lowp
+.globl _sk_just_return_ssse3_lowp
+FUNCTION(_sk_just_return_ssse3_lowp)
+_sk_just_return_ssse3_lowp:
+  .byte  195                                 // retq
+
+HIDDEN _sk_constant_color_ssse3_lowp
+.globl _sk_constant_color_ssse3_lowp
+FUNCTION(_sk_constant_color_ssse3_lowp)
+_sk_constant_color_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,16,24                            // movups        (%rax),%xmm3
+  .byte  15,88,29,224,16,0,0                 // addps         0x10e0(%rip),%xmm3        # 11a0 <_sk_xor__ssse3_lowp+0xa3>
+  .byte  242,15,112,195,0                    // pshuflw       $0x0,%xmm3,%xmm0
+  .byte  102,15,112,192,80                   // pshufd        $0x50,%xmm0,%xmm0
+  .byte  242,15,112,203,170                  // pshuflw       $0xaa,%xmm3,%xmm1
+  .byte  102,15,112,201,80                   // pshufd        $0x50,%xmm1,%xmm1
+  .byte  243,15,112,211,0                    // pshufhw       $0x0,%xmm3,%xmm2
+  .byte  102,15,112,210,250                  // pshufd        $0xfa,%xmm2,%xmm2
+  .byte  243,15,112,219,170                  // pshufhw       $0xaa,%xmm3,%xmm3
+  .byte  102,15,112,219,250                  // pshufd        $0xfa,%xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_set_rgb_ssse3_lowp
+.globl _sk_set_rgb_ssse3_lowp
+FUNCTION(_sk_set_rgb_ssse3_lowp)
+_sk_set_rgb_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,15,16,21,10,18,0,0              // movss         0x120a(%rip),%xmm2        # 1300 <_sk_xor__ssse3_lowp+0x203>
+  .byte  243,15,16,0                         // movss         (%rax),%xmm0
+  .byte  243,15,88,194                       // addss         %xmm2,%xmm0
+  .byte  102,65,15,126,193                   // movd          %xmm0,%r9d
+  .byte  102,65,15,110,193                   // movd          %r9d,%xmm0
+  .byte  242,15,112,192,0                    // pshuflw       $0x0,%xmm0,%xmm0
+  .byte  102,15,112,192,80                   // pshufd        $0x50,%xmm0,%xmm0
+  .byte  243,15,16,72,4                      // movss         0x4(%rax),%xmm1
+  .byte  243,15,88,202                       // addss         %xmm2,%xmm1
+  .byte  102,65,15,126,201                   // movd          %xmm1,%r9d
+  .byte  102,65,15,110,201                   // movd          %r9d,%xmm1
+  .byte  242,15,112,201,0                    // pshuflw       $0x0,%xmm1,%xmm1
+  .byte  102,15,112,201,80                   // pshufd        $0x50,%xmm1,%xmm1
+  .byte  243,15,88,80,8                      // addss         0x8(%rax),%xmm2
+  .byte  102,15,126,208                      // movd          %xmm2,%eax
+  .byte  102,15,110,208                      // movd          %eax,%xmm2
+  .byte  242,15,112,210,0                    // pshuflw       $0x0,%xmm2,%xmm2
+  .byte  102,15,112,210,80                   // pshufd        $0x50,%xmm2,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_premul_ssse3_lowp
+.globl _sk_premul_ssse3_lowp
+FUNCTION(_sk_premul_ssse3_lowp)
+_sk_premul_ssse3_lowp:
+  .byte  102,15,56,11,195                    // pmulhrsw      %xmm3,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,15,56,11,203                    // pmulhrsw      %xmm3,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,15,56,11,211                    // pmulhrsw      %xmm3,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_load_8888_ssse3_lowp
+.globl _sk_load_8888_ssse3_lowp
+FUNCTION(_sk_load_8888_ssse3_lowp)
+_sk_load_8888_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,113                             // jne           1e7 <_sk_load_8888_ssse3_lowp+0x7b>
+  .byte  69,15,16,76,147,16                  // movups        0x10(%r11,%rdx,4),%xmm9
+  .byte  69,15,16,4,147                      // movups        (%r11,%rdx,4),%xmm8
+  .byte  102,15,111,5,39,16,0,0              // movdqa        0x1027(%rip),%xmm0        # 11b0 <_sk_xor__ssse3_lowp+0xb3>
+  .byte  102,68,15,56,0,192                  // pshufb        %xmm0,%xmm8
+  .byte  102,68,15,56,0,200                  // pshufb        %xmm0,%xmm9
+  .byte  102,65,15,111,208                   // movdqa        %xmm8,%xmm2
+  .byte  102,65,15,98,209                    // punpckldq     %xmm9,%xmm2
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  102,15,96,194                       // punpcklbw     %xmm2,%xmm0
+  .byte  102,15,239,201                      // pxor          %xmm1,%xmm1
+  .byte  102,15,104,202                      // punpckhbw     %xmm2,%xmm1
+  .byte  102,69,15,106,193                   // punpckhdq     %xmm9,%xmm8
+  .byte  102,15,239,210                      // pxor          %xmm2,%xmm2
+  .byte  102,65,15,96,208                    // punpcklbw     %xmm8,%xmm2
+  .byte  102,65,15,104,216                   // punpckhbw     %xmm8,%xmm3
+  .byte  102,68,15,111,5,241,15,0,0          // movdqa        0xff1(%rip),%xmm8        # 11c0 <_sk_xor__ssse3_lowp+0xc3>
+  .byte  102,65,15,228,192                   // pmulhuw       %xmm8,%xmm0
+  .byte  102,65,15,228,200                   // pmulhuw       %xmm8,%xmm1
+  .byte  102,65,15,228,208                   // pmulhuw       %xmm8,%xmm2
+  .byte  102,65,15,228,216                   // pmulhuw       %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,129                             // ja            181 <_sk_load_8888_ssse3_lowp+0x15>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,133,0,0,0                 // lea           0x85(%rip),%r10        # 290 <_sk_load_8888_ssse3_lowp+0x124>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,65,15,110,68,147,24             // movd          0x18(%r11,%rdx,4),%xmm0
+  .byte  102,68,15,112,200,69                // pshufd        $0x45,%xmm0,%xmm9
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  243,65,15,16,68,147,20              // movss         0x14(%r11,%rdx,4),%xmm0
+  .byte  65,15,198,193,0                     // shufps        $0x0,%xmm9,%xmm0
+  .byte  65,15,198,193,226                   // shufps        $0xe2,%xmm9,%xmm0
+  .byte  68,15,40,200                        // movaps        %xmm0,%xmm9
+  .byte  243,65,15,16,68,147,16              // movss         0x10(%r11,%rdx,4),%xmm0
+  .byte  243,68,15,16,200                    // movss         %xmm0,%xmm9
+  .byte  243,65,15,16,68,147,12              // movss         0xc(%r11,%rdx,4),%xmm0
+  .byte  65,15,198,192,32                    // shufps        $0x20,%xmm8,%xmm0
+  .byte  68,15,198,192,36                    // shufps        $0x24,%xmm0,%xmm8
+  .byte  243,65,15,16,68,147,8               // movss         0x8(%r11,%rdx,4),%xmm0
+  .byte  65,15,198,192,48                    // shufps        $0x30,%xmm8,%xmm0
+  .byte  68,15,198,192,132                   // shufps        $0x84,%xmm0,%xmm8
+  .byte  243,65,15,16,68,147,4               // movss         0x4(%r11,%rdx,4),%xmm0
+  .byte  65,15,198,192,0                     // shufps        $0x0,%xmm8,%xmm0
+  .byte  65,15,198,192,226                   // shufps        $0xe2,%xmm8,%xmm0
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  243,65,15,16,4,147                  // movss         (%r11,%rdx,4),%xmm0
+  .byte  243,68,15,16,192                    // movss         %xmm0,%xmm8
+  .byte  233,244,254,255,255                 // jmpq          181 <_sk_load_8888_ssse3_lowp+0x15>
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  237                                 // in            (%dx),%eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  216,255                             // fdivr         %st(7),%st
+  .byte  255                                 // (bad)
+  .byte  255,199                             // inc           %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,182,255,255,255,170             // pushq         -0x55000001(%rsi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,149,255,255,255,132             // callq         *-0x7b000001(%rbp)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_store_8888_ssse3_lowp
+.globl _sk_store_8888_ssse3_lowp
+FUNCTION(_sk_store_8888_ssse3_lowp)
+_sk_store_8888_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  102,68,15,111,208                   // movdqa        %xmm0,%xmm10
+  .byte  102,65,15,113,210,7                 // psrlw         $0x7,%xmm10
+  .byte  102,68,15,111,194                   // movdqa        %xmm2,%xmm8
+  .byte  102,65,15,113,208,7                 // psrlw         $0x7,%xmm8
+  .byte  102,69,15,103,208                   // packuswb      %xmm8,%xmm10
+  .byte  102,68,15,111,193                   // movdqa        %xmm1,%xmm8
+  .byte  102,65,15,113,208,7                 // psrlw         $0x7,%xmm8
+  .byte  102,68,15,111,203                   // movdqa        %xmm3,%xmm9
+  .byte  102,65,15,113,209,7                 // psrlw         $0x7,%xmm9
+  .byte  102,69,15,103,193                   // packuswb      %xmm9,%xmm8
+  .byte  102,69,15,111,202                   // movdqa        %xmm10,%xmm9
+  .byte  102,69,15,96,200                    // punpcklbw     %xmm8,%xmm9
+  .byte  102,69,15,104,208                   // punpckhbw     %xmm8,%xmm10
+  .byte  102,69,15,111,193                   // movdqa        %xmm9,%xmm8
+  .byte  102,69,15,97,194                    // punpcklwd     %xmm10,%xmm8
+  .byte  102,69,15,105,202                   // punpckhwd     %xmm10,%xmm9
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,17                              // jne           31b <_sk_store_8888_ssse3_lowp+0x6f>
+  .byte  243,69,15,127,76,147,16             // movdqu        %xmm9,0x10(%r11,%rdx,4)
+  .byte  243,69,15,127,4,147                 // movdqu        %xmm8,(%r11,%rdx,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            317 <_sk_store_8888_ssse3_lowp+0x6b>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,90,0,0,0                  // lea           0x5a(%rip),%r10        # 390 <_sk_store_8888_ssse3_lowp+0xe4>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,69,15,112,209,78                // pshufd        $0x4e,%xmm9,%xmm10
+  .byte  102,69,15,126,84,147,24             // movd          %xmm10,0x18(%r11,%rdx,4)
+  .byte  102,69,15,112,209,229               // pshufd        $0xe5,%xmm9,%xmm10
+  .byte  102,69,15,126,84,147,20             // movd          %xmm10,0x14(%r11,%rdx,4)
+  .byte  102,69,15,126,76,147,16             // movd          %xmm9,0x10(%r11,%rdx,4)
+  .byte  102,69,15,112,200,231               // pshufd        $0xe7,%xmm8,%xmm9
+  .byte  102,69,15,126,76,147,12             // movd          %xmm9,0xc(%r11,%rdx,4)
+  .byte  102,69,15,112,200,78                // pshufd        $0x4e,%xmm8,%xmm9
+  .byte  102,69,15,126,76,147,8              // movd          %xmm9,0x8(%r11,%rdx,4)
+  .byte  102,69,15,112,200,229               // pshufd        $0xe5,%xmm8,%xmm9
+  .byte  102,69,15,126,76,147,4              // movd          %xmm9,0x4(%r11,%rdx,4)
+  .byte  102,69,15,126,4,147                 // movd          %xmm8,(%r11,%rdx,4)
+  .byte  235,136                             // jmp           317 <_sk_store_8888_ssse3_lowp+0x6b>
+  .byte  144                                 // nop
+  .byte  247,255                             // idiv          %edi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  234                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  221,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,208                             // callq         *%rax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,201                             // dec           %ecx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  188,255,255,255,175                 // mov           $0xafffffff,%esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_a8_ssse3_lowp
+.globl _sk_load_a8_ssse3_lowp
+FUNCTION(_sk_load_a8_ssse3_lowp)
+_sk_load_a8_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,37                              // jne           3db <_sk_load_a8_ssse3_lowp+0x2f>
+  .byte  243,65,15,126,28,19                 // movq          (%r11,%rdx,1),%xmm3
+  .byte  102,15,96,216                       // punpcklbw     %xmm0,%xmm3
+  .byte  102,15,113,243,8                    // psllw         $0x8,%xmm3
+  .byte  102,15,228,29,3,14,0,0              // pmulhuw       0xe03(%rip),%xmm3        # 11d0 <_sk_xor__ssse3_lowp+0xd3>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,209                             // ja            3c0 <_sk_load_a8_ssse3_lowp+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,94,0,0,0                  // lea           0x5e(%rip),%r10        # 458 <_sk_load_a8_ssse3_lowp+0xac>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  65,15,182,68,19,6                   // movzbl        0x6(%r11,%rdx,1),%eax
+  .byte  102,15,239,219                      // pxor          %xmm3,%xmm3
+  .byte  102,15,196,216,6                    // pinsrw        $0x6,%eax,%xmm3
+  .byte  65,15,182,68,19,5                   // movzbl        0x5(%r11,%rdx,1),%eax
+  .byte  102,15,196,216,5                    // pinsrw        $0x5,%eax,%xmm3
+  .byte  65,15,182,68,19,4                   // movzbl        0x4(%r11,%rdx,1),%eax
+  .byte  102,15,196,216,4                    // pinsrw        $0x4,%eax,%xmm3
+  .byte  65,15,182,68,19,3                   // movzbl        0x3(%r11,%rdx,1),%eax
+  .byte  102,15,196,216,3                    // pinsrw        $0x3,%eax,%xmm3
+  .byte  65,15,182,68,19,2                   // movzbl        0x2(%r11,%rdx,1),%eax
+  .byte  102,15,196,216,2                    // pinsrw        $0x2,%eax,%xmm3
+  .byte  65,15,182,68,19,1                   // movzbl        0x1(%r11,%rdx,1),%eax
+  .byte  102,15,196,216,1                    // pinsrw        $0x1,%eax,%xmm3
+  .byte  65,15,182,4,19                      // movzbl        (%r11,%rdx,1),%eax
+  .byte  102,15,196,216,0                    // pinsrw        $0x0,%eax,%xmm3
+  .byte  233,104,255,255,255                 // jmpq          3c0 <_sk_load_a8_ssse3_lowp+0x14>
+  .byte  241                                 // icebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,230                             // jmpq          *%rsi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  219,255                             // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,208                             // callq         *%rax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,197                             // inc           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  186,255,255,255,171                 // mov           $0xabffffff,%edx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_store_a8_ssse3_lowp
+.globl _sk_store_a8_ssse3_lowp
+FUNCTION(_sk_store_a8_ssse3_lowp)
+_sk_store_a8_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  102,68,15,111,195                   // movdqa        %xmm3,%xmm8
+  .byte  102,65,15,113,208,7                 // psrlw         $0x7,%xmm8
+  .byte  102,69,15,103,192                   // packuswb      %xmm8,%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,10                              // jne           498 <_sk_store_a8_ssse3_lowp+0x24>
+  .byte  242,69,15,17,4,19                   // movsd         %xmm8,(%r11,%rdx,1)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,236                             // ja            494 <_sk_store_a8_ssse3_lowp+0x20>
+  .byte  102,68,15,96,192                    // punpcklbw     %xmm0,%xmm8
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,128,0,0,0                 // lea           0x80(%rip),%r10        # 538 <_sk_store_a8_ssse3_lowp+0xc4>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,68,15,127,68,36,232             // movdqa        %xmm8,-0x18(%rsp)
+  .byte  138,68,36,244                       // mov           -0xc(%rsp),%al
+  .byte  65,136,68,19,6                      // mov           %al,0x6(%r11,%rdx,1)
+  .byte  102,68,15,127,68,36,216             // movdqa        %xmm8,-0x28(%rsp)
+  .byte  138,68,36,226                       // mov           -0x1e(%rsp),%al
+  .byte  65,136,68,19,5                      // mov           %al,0x5(%r11,%rdx,1)
+  .byte  102,68,15,127,68,36,200             // movdqa        %xmm8,-0x38(%rsp)
+  .byte  138,68,36,208                       // mov           -0x30(%rsp),%al
+  .byte  65,136,68,19,4                      // mov           %al,0x4(%r11,%rdx,1)
+  .byte  102,68,15,127,68,36,184             // movdqa        %xmm8,-0x48(%rsp)
+  .byte  138,68,36,190                       // mov           -0x42(%rsp),%al
+  .byte  65,136,68,19,3                      // mov           %al,0x3(%r11,%rdx,1)
+  .byte  102,68,15,127,68,36,168             // movdqa        %xmm8,-0x58(%rsp)
+  .byte  138,68,36,172                       // mov           -0x54(%rsp),%al
+  .byte  65,136,68,19,2                      // mov           %al,0x2(%r11,%rdx,1)
+  .byte  102,68,15,127,68,36,152             // movdqa        %xmm8,-0x68(%rsp)
+  .byte  138,68,36,154                       // mov           -0x66(%rsp),%al
+  .byte  65,136,68,19,1                      // mov           %al,0x1(%r11,%rdx,1)
+  .byte  102,68,15,127,68,36,136             // movdqa        %xmm8,-0x78(%rsp)
+  .byte  138,68,36,136                       // mov           -0x78(%rsp),%al
+  .byte  65,136,4,19                         // mov           %al,(%r11,%rdx,1)
+  .byte  233,95,255,255,255                  // jmpq          494 <_sk_store_a8_ssse3_lowp+0x20>
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  233,255,255,255,217                 // jmpq          ffffffffda00053c <_sk_xor__ssse3_lowp+0xffffffffd9fff43f>
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,201                             // dec           %ecx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  185,255,255,255,169                 // mov           $0xa9ffffff,%ecx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,153,255,255,255,137             // lcall         *-0x76000001(%rcx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_load_g8_ssse3_lowp
+.globl _sk_load_g8_ssse3_lowp
+FUNCTION(_sk_load_g8_ssse3_lowp)
+_sk_load_g8_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,42                              // jne           588 <_sk_load_g8_ssse3_lowp+0x34>
+  .byte  243,65,15,126,4,19                  // movq          (%r11,%rdx,1),%xmm0
+  .byte  102,15,96,192                       // punpcklbw     %xmm0,%xmm0
+  .byte  102,15,113,240,8                    // psllw         $0x8,%xmm0
+  .byte  102,15,228,5,107,12,0,0             // pmulhuw       0xc6b(%rip),%xmm0        # 11e0 <_sk_xor__ssse3_lowp+0xe3>
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,29,114,12,0,0                 // movaps        0xc72(%rip),%xmm3        # 11f0 <_sk_xor__ssse3_lowp+0xf3>
+  .byte  102,15,111,200                      // movdqa        %xmm0,%xmm1
+  .byte  102,15,111,208                      // movdqa        %xmm0,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,204                             // ja            568 <_sk_load_g8_ssse3_lowp+0x14>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,97,0,0,0                  // lea           0x61(%rip),%r10        # 608 <_sk_load_g8_ssse3_lowp+0xb4>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  65,15,182,68,19,6                   // movzbl        0x6(%r11,%rdx,1),%eax
+  .byte  102,15,239,192                      // pxor          %xmm0,%xmm0
+  .byte  102,15,196,192,6                    // pinsrw        $0x6,%eax,%xmm0
+  .byte  65,15,182,68,19,5                   // movzbl        0x5(%r11,%rdx,1),%eax
+  .byte  102,15,196,192,5                    // pinsrw        $0x5,%eax,%xmm0
+  .byte  65,15,182,68,19,4                   // movzbl        0x4(%r11,%rdx,1),%eax
+  .byte  102,15,196,192,4                    // pinsrw        $0x4,%eax,%xmm0
+  .byte  65,15,182,68,19,3                   // movzbl        0x3(%r11,%rdx,1),%eax
+  .byte  102,15,196,192,3                    // pinsrw        $0x3,%eax,%xmm0
+  .byte  65,15,182,68,19,2                   // movzbl        0x2(%r11,%rdx,1),%eax
+  .byte  102,15,196,192,2                    // pinsrw        $0x2,%eax,%xmm0
+  .byte  65,15,182,68,19,1                   // movzbl        0x1(%r11,%rdx,1),%eax
+  .byte  102,15,196,192,1                    // pinsrw        $0x1,%eax,%xmm0
+  .byte  65,15,182,4,19                      // movzbl        (%r11,%rdx,1),%eax
+  .byte  102,15,196,192,0                    // pinsrw        $0x0,%eax,%xmm0
+  .byte  233,99,255,255,255                  // jmpq          568 <_sk_load_g8_ssse3_lowp+0x14>
+  .byte  15,31,0                             // nopl          (%rax)
+  .byte  238                                 // out           %al,(%dx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,227                             // jmpq          *%rbx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  216,255                             // fdivr         %st(7),%st
+  .byte  255                                 // (bad)
+  .byte  255,205                             // dec           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,194                             // inc           %edx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,183,255,255,255,168             // pushq         -0x57000001(%rdi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_srcover_rgba_8888_ssse3_lowp
+.globl _sk_srcover_rgba_8888_ssse3_lowp
+FUNCTION(_sk_srcover_rgba_8888_ssse3_lowp)
+_sk_srcover_rgba_8888_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,66,1,0,0                     // jne           774 <_sk_srcover_rgba_8888_ssse3_lowp+0x150>
+  .byte  69,15,16,76,147,16                  // movups        0x10(%r11,%rdx,4),%xmm9
+  .byte  69,15,16,4,147                      // movups        (%r11,%rdx,4),%xmm8
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  102,15,111,37,184,11,0,0            // movdqa        0xbb8(%rip),%xmm4        # 1200 <_sk_xor__ssse3_lowp+0x103>
+  .byte  102,68,15,56,0,196                  // pshufb        %xmm4,%xmm8
+  .byte  102,68,15,56,0,204                  // pshufb        %xmm4,%xmm9
+  .byte  102,65,15,111,240                   // movdqa        %xmm8,%xmm6
+  .byte  102,65,15,98,241                    // punpckldq     %xmm9,%xmm6
+  .byte  102,15,239,255                      // pxor          %xmm7,%xmm7
+  .byte  102,15,239,228                      // pxor          %xmm4,%xmm4
+  .byte  102,15,96,230                       // punpcklbw     %xmm6,%xmm4
+  .byte  102,15,239,237                      // pxor          %xmm5,%xmm5
+  .byte  102,15,104,238                      // punpckhbw     %xmm6,%xmm5
+  .byte  102,69,15,106,193                   // punpckhdq     %xmm9,%xmm8
+  .byte  102,15,239,246                      // pxor          %xmm6,%xmm6
+  .byte  102,65,15,96,240                    // punpcklbw     %xmm8,%xmm6
+  .byte  102,65,15,104,248                   // punpckhbw     %xmm8,%xmm7
+  .byte  102,68,15,111,5,130,11,0,0          // movdqa        0xb82(%rip),%xmm8        # 1210 <_sk_xor__ssse3_lowp+0x113>
+  .byte  102,65,15,228,224                   // pmulhuw       %xmm8,%xmm4
+  .byte  102,65,15,228,232                   // pmulhuw       %xmm8,%xmm5
+  .byte  102,65,15,228,240                   // pmulhuw       %xmm8,%xmm6
+  .byte  102,65,15,228,248                   // pmulhuw       %xmm8,%xmm7
+  .byte  102,68,15,111,29,117,11,0,0         // movdqa        0xb75(%rip),%xmm11        # 1220 <_sk_xor__ssse3_lowp+0x123>
+  .byte  102,68,15,249,219                   // psubw         %xmm3,%xmm11
+  .byte  102,68,15,111,196                   // movdqa        %xmm4,%xmm8
+  .byte  102,69,15,56,11,195                 // pmulhrsw      %xmm11,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,68,15,253,192                   // paddw         %xmm0,%xmm8
+  .byte  102,15,111,197                      // movdqa        %xmm5,%xmm0
+  .byte  102,65,15,56,11,195                 // pmulhrsw      %xmm11,%xmm0
+  .byte  102,68,15,56,29,200                 // pabsw         %xmm0,%xmm9
+  .byte  102,68,15,253,201                   // paddw         %xmm1,%xmm9
+  .byte  102,15,111,198                      // movdqa        %xmm6,%xmm0
+  .byte  102,65,15,56,11,195                 // pmulhrsw      %xmm11,%xmm0
+  .byte  102,68,15,56,29,208                 // pabsw         %xmm0,%xmm10
+  .byte  102,68,15,253,210                   // paddw         %xmm2,%xmm10
+  .byte  102,68,15,56,11,223                 // pmulhrsw      %xmm7,%xmm11
+  .byte  102,69,15,56,29,219                 // pabsw         %xmm11,%xmm11
+  .byte  102,68,15,253,219                   // paddw         %xmm3,%xmm11
+  .byte  102,65,15,111,208                   // movdqa        %xmm8,%xmm2
+  .byte  102,15,113,210,7                    // psrlw         $0x7,%xmm2
+  .byte  102,65,15,111,194                   // movdqa        %xmm10,%xmm0
+  .byte  102,15,113,208,7                    // psrlw         $0x7,%xmm0
+  .byte  102,15,103,208                      // packuswb      %xmm0,%xmm2
+  .byte  102,65,15,111,193                   // movdqa        %xmm9,%xmm0
+  .byte  102,15,113,208,7                    // psrlw         $0x7,%xmm0
+  .byte  102,65,15,111,203                   // movdqa        %xmm11,%xmm1
+  .byte  102,15,113,209,7                    // psrlw         $0x7,%xmm1
+  .byte  102,15,103,193                      // packuswb      %xmm1,%xmm0
+  .byte  102,15,111,202                      // movdqa        %xmm2,%xmm1
+  .byte  102,15,96,200                       // punpcklbw     %xmm0,%xmm1
+  .byte  102,15,104,208                      // punpckhbw     %xmm0,%xmm2
+  .byte  102,15,111,193                      // movdqa        %xmm1,%xmm0
+  .byte  102,15,97,194                       // punpcklwd     %xmm2,%xmm0
+  .byte  102,15,105,202                      // punpckhwd     %xmm2,%xmm1
+  .byte  15,133,207,0,0,0                    // jne           81e <_sk_srcover_rgba_8888_ssse3_lowp+0x1fa>
+  .byte  243,65,15,127,76,147,16             // movdqu        %xmm1,0x10(%r11,%rdx,4)
+  .byte  243,65,15,127,4,147                 // movdqu        %xmm0,(%r11,%rdx,4)
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  102,65,15,111,192                   // movdqa        %xmm8,%xmm0
+  .byte  102,65,15,111,201                   // movdqa        %xmm9,%xmm1
+  .byte  102,65,15,111,210                   // movdqa        %xmm10,%xmm2
+  .byte  102,65,15,111,219                   // movdqa        %xmm11,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  102,69,15,239,201                   // pxor          %xmm9,%xmm9
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,172,254,255,255              // ja            63d <_sk_srcover_rgba_8888_ssse3_lowp+0x19>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,248,0,0,0                 // lea           0xf8(%rip),%r10        # 894 <_sk_srcover_rgba_8888_ssse3_lowp+0x270>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,65,15,110,100,147,24            // movd          0x18(%r11,%rdx,4),%xmm4
+  .byte  102,68,15,112,204,69                // pshufd        $0x45,%xmm4,%xmm9
+  .byte  69,15,87,192                        // xorps         %xmm8,%xmm8
+  .byte  243,65,15,16,100,147,20             // movss         0x14(%r11,%rdx,4),%xmm4
+  .byte  65,15,198,225,0                     // shufps        $0x0,%xmm9,%xmm4
+  .byte  65,15,198,225,226                   // shufps        $0xe2,%xmm9,%xmm4
+  .byte  68,15,40,204                        // movaps        %xmm4,%xmm9
+  .byte  243,65,15,16,100,147,16             // movss         0x10(%r11,%rdx,4),%xmm4
+  .byte  243,68,15,16,204                    // movss         %xmm4,%xmm9
+  .byte  243,65,15,16,100,147,12             // movss         0xc(%r11,%rdx,4),%xmm4
+  .byte  65,15,198,224,32                    // shufps        $0x20,%xmm8,%xmm4
+  .byte  68,15,198,196,36                    // shufps        $0x24,%xmm4,%xmm8
+  .byte  243,65,15,16,100,147,8              // movss         0x8(%r11,%rdx,4),%xmm4
+  .byte  65,15,198,224,48                    // shufps        $0x30,%xmm8,%xmm4
+  .byte  68,15,198,196,132                   // shufps        $0x84,%xmm4,%xmm8
+  .byte  243,65,15,16,100,147,4              // movss         0x4(%r11,%rdx,4),%xmm4
+  .byte  65,15,198,224,0                     // shufps        $0x0,%xmm8,%xmm4
+  .byte  65,15,198,224,226                   // shufps        $0xe2,%xmm8,%xmm4
+  .byte  68,15,40,196                        // movaps        %xmm4,%xmm8
+  .byte  243,65,15,16,36,147                 // movss         (%r11,%rdx,4),%xmm4
+  .byte  243,68,15,16,196                    // movss         %xmm4,%xmm8
+  .byte  233,31,254,255,255                  // jmpq          63d <_sk_srcover_rgba_8888_ssse3_lowp+0x19>
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,42,255,255,255               // ja            75c <_sk_srcover_rgba_8888_ssse3_lowp+0x138>
+  .byte  65,15,182,193                       // movzbl        %r9b,%eax
+  .byte  76,141,13,115,0,0,0                 // lea           0x73(%rip),%r9        # 8b0 <_sk_srcover_rgba_8888_ssse3_lowp+0x28c>
+  .byte  73,99,4,129                         // movslq        (%r9,%rax,4),%rax
+  .byte  76,1,200                            // add           %r9,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  102,15,112,209,78                   // pshufd        $0x4e,%xmm1,%xmm2
+  .byte  102,65,15,126,84,147,24             // movd          %xmm2,0x18(%r11,%rdx,4)
+  .byte  102,15,112,209,229                  // pshufd        $0xe5,%xmm1,%xmm2
+  .byte  102,65,15,126,84,147,20             // movd          %xmm2,0x14(%r11,%rdx,4)
+  .byte  102,65,15,126,76,147,16             // movd          %xmm1,0x10(%r11,%rdx,4)
+  .byte  102,15,112,200,231                  // pshufd        $0xe7,%xmm0,%xmm1
+  .byte  102,65,15,126,76,147,12             // movd          %xmm1,0xc(%r11,%rdx,4)
+  .byte  102,15,112,200,78                   // pshufd        $0x4e,%xmm0,%xmm1
+  .byte  102,65,15,126,76,147,8              // movd          %xmm1,0x8(%r11,%rdx,4)
+  .byte  102,15,112,200,229                  // pshufd        $0xe5,%xmm0,%xmm1
+  .byte  102,65,15,126,76,147,4              // movd          %xmm1,0x4(%r11,%rdx,4)
+  .byte  102,65,15,126,4,147                 // movd          %xmm0,(%r11,%rdx,4)
+  .byte  233,200,254,255,255                 // jmpq          75c <_sk_srcover_rgba_8888_ssse3_lowp+0x138>
+  .byte  122,255                             // jp            895 <_sk_srcover_rgba_8888_ssse3_lowp+0x271>
+  .byte  255                                 // (bad)
+  .byte  255,101,255                         // jmpq          *-0x1(%rbp)
+  .byte  255                                 // (bad)
+  .byte  255,84,255,255                      // callq         *-0x1(%rdi,%rdi,8)
+  .byte  255,67,255                          // incl          -0x1(%rbx)
+  .byte  255                                 // (bad)
+  .byte  255,55                              // pushq         (%rdi)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,34                              // jmpq          *(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,17                              // callq         *(%rcx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  217,255                             // fcos
+  .byte  255                                 // (bad)
+  .byte  255,205                             // dec           %ebp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,193                             // inc           %ecx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,181,255,255,255,174             // pushq         -0x51000001(%rbp)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,162,255,255,255,150             // jmpq          *-0x69000001(%rdx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_scale_1_float_ssse3_lowp
+.globl _sk_scale_1_float_ssse3_lowp
+FUNCTION(_sk_scale_1_float_ssse3_lowp)
+_sk_scale_1_float_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  243,68,15,88,5,40,10,0,0            // addss         0xa28(%rip),%xmm8        # 1304 <_sk_xor__ssse3_lowp+0x207>
+  .byte  102,68,15,126,192                   // movd          %xmm8,%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  242,69,15,112,192,0                 // pshuflw       $0x0,%xmm8,%xmm8
+  .byte  102,69,15,112,192,80                // pshufd        $0x50,%xmm8,%xmm8
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,56,11,216                 // pmulhrsw      %xmm8,%xmm3
+  .byte  102,15,56,29,219                    // pabsw         %xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_scale_u8_ssse3_lowp
+.globl _sk_scale_u8_ssse3_lowp
+FUNCTION(_sk_scale_u8_ssse3_lowp)
+_sk_scale_u8_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  117,74                              // jne           976 <_sk_scale_u8_ssse3_lowp+0x54>
+  .byte  243,69,15,126,4,19                  // movq          (%r11,%rdx,1),%xmm8
+  .byte  102,68,15,96,192                    // punpcklbw     %xmm0,%xmm8
+  .byte  102,65,15,113,240,8                 // psllw         $0x8,%xmm8
+  .byte  102,68,15,228,5,234,8,0,0           // pmulhuw       0x8ea(%rip),%xmm8        # 1230 <_sk_xor__ssse3_lowp+0x133>
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,56,11,216                 // pmulhrsw      %xmm8,%xmm3
+  .byte  102,15,56,29,219                    // pabsw         %xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  119,172                             // ja            937 <_sk_scale_u8_ssse3_lowp+0x15>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,102,0,0,0                 // lea           0x66(%rip),%r10        # 9fc <_sk_scale_u8_ssse3_lowp+0xda>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  65,15,182,68,19,6                   // movzbl        0x6(%r11,%rdx,1),%eax
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  102,68,15,196,192,6                 // pinsrw        $0x6,%eax,%xmm8
+  .byte  65,15,182,68,19,5                   // movzbl        0x5(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,5                 // pinsrw        $0x5,%eax,%xmm8
+  .byte  65,15,182,68,19,4                   // movzbl        0x4(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,4                 // pinsrw        $0x4,%eax,%xmm8
+  .byte  65,15,182,68,19,3                   // movzbl        0x3(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,3                 // pinsrw        $0x3,%eax,%xmm8
+  .byte  65,15,182,68,19,2                   // movzbl        0x2(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,2                 // pinsrw        $0x2,%eax,%xmm8
+  .byte  65,15,182,68,19,1                   // movzbl        0x1(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,1                 // pinsrw        $0x1,%eax,%xmm8
+  .byte  65,15,182,4,19                      // movzbl        (%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,0                 // pinsrw        $0x0,%eax,%xmm8
+  .byte  233,59,255,255,255                  // jmpq          937 <_sk_scale_u8_ssse3_lowp+0x15>
+  .byte  240,255                             // lock          (bad)
+  .byte  255                                 // (bad)
+  .byte  255,228                             // jmpq          *%rsp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  216,255                             // fdivr         %st(7),%st
+  .byte  255                                 // (bad)
+  .byte  255,204                             // dec           %esp
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,192                             // inc           %eax
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,180,255,255,255,163,255         // pushq         -0x5c0001(%rdi,%rdi,8)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_lerp_1_float_ssse3_lowp
+.globl _sk_lerp_1_float_ssse3_lowp
+FUNCTION(_sk_lerp_1_float_ssse3_lowp)
+_sk_lerp_1_float_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  243,68,15,16,0                      // movss         (%rax),%xmm8
+  .byte  243,68,15,88,5,224,8,0,0            // addss         0x8e0(%rip),%xmm8        # 1308 <_sk_xor__ssse3_lowp+0x20b>
+  .byte  102,68,15,126,192                   // movd          %xmm8,%eax
+  .byte  102,68,15,110,192                   // movd          %eax,%xmm8
+  .byte  242,69,15,112,192,0                 // pshuflw       $0x0,%xmm8,%xmm8
+  .byte  102,69,15,112,192,80                // pshufd        $0x50,%xmm8,%xmm8
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,68,15,56,29,200                 // pabsw         %xmm0,%xmm9
+  .byte  102,68,15,111,21,237,7,0,0          // movdqa        0x7ed(%rip),%xmm10        # 1240 <_sk_xor__ssse3_lowp+0x143>
+  .byte  102,69,15,249,208                   // psubw         %xmm8,%xmm10
+  .byte  102,15,111,196                      // movdqa        %xmm4,%xmm0
+  .byte  102,65,15,56,11,194                 // pmulhrsw      %xmm10,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,253,193                   // paddw         %xmm9,%xmm0
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,68,15,56,29,201                 // pabsw         %xmm1,%xmm9
+  .byte  102,15,111,205                      // movdqa        %xmm5,%xmm1
+  .byte  102,65,15,56,11,202                 // pmulhrsw      %xmm10,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,253,201                   // paddw         %xmm9,%xmm1
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,68,15,56,29,202                 // pabsw         %xmm2,%xmm9
+  .byte  102,15,111,214                      // movdqa        %xmm6,%xmm2
+  .byte  102,65,15,56,11,210                 // pmulhrsw      %xmm10,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,253,209                   // paddw         %xmm9,%xmm2
+  .byte  102,65,15,56,11,216                 // pmulhrsw      %xmm8,%xmm3
+  .byte  102,68,15,56,29,195                 // pabsw         %xmm3,%xmm8
+  .byte  102,68,15,56,11,215                 // pmulhrsw      %xmm7,%xmm10
+  .byte  102,65,15,56,29,218                 // pabsw         %xmm10,%xmm3
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_lerp_u8_ssse3_lowp
+.globl _sk_lerp_u8_ssse3_lowp
+FUNCTION(_sk_lerp_u8_ssse3_lowp)
+_sk_lerp_u8_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  76,139,24                           // mov           (%rax),%r11
+  .byte  77,133,192                          // test          %r8,%r8
+  .byte  15,133,169,0,0,0                    // jne           b84 <_sk_lerp_u8_ssse3_lowp+0xb7>
+  .byte  243,69,15,126,4,19                  // movq          (%r11,%rdx,1),%xmm8
+  .byte  102,68,15,96,192                    // punpcklbw     %xmm0,%xmm8
+  .byte  102,65,15,113,240,8                 // psllw         $0x8,%xmm8
+  .byte  102,68,15,228,5,91,7,0,0            // pmulhuw       0x75b(%rip),%xmm8        # 1250 <_sk_xor__ssse3_lowp+0x153>
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,68,15,56,29,200                 // pabsw         %xmm0,%xmm9
+  .byte  102,68,15,111,21,86,7,0,0           // movdqa        0x756(%rip),%xmm10        # 1260 <_sk_xor__ssse3_lowp+0x163>
+  .byte  102,69,15,249,208                   // psubw         %xmm8,%xmm10
+  .byte  102,15,111,196                      // movdqa        %xmm4,%xmm0
+  .byte  102,65,15,56,11,194                 // pmulhrsw      %xmm10,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,253,193                   // paddw         %xmm9,%xmm0
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,68,15,56,29,201                 // pabsw         %xmm1,%xmm9
+  .byte  102,15,111,205                      // movdqa        %xmm5,%xmm1
+  .byte  102,65,15,56,11,202                 // pmulhrsw      %xmm10,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,253,201                   // paddw         %xmm9,%xmm1
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,68,15,56,29,202                 // pabsw         %xmm2,%xmm9
+  .byte  102,15,111,214                      // movdqa        %xmm6,%xmm2
+  .byte  102,65,15,56,11,210                 // pmulhrsw      %xmm10,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,253,209                   // paddw         %xmm9,%xmm2
+  .byte  102,65,15,56,11,216                 // pmulhrsw      %xmm8,%xmm3
+  .byte  102,68,15,56,29,195                 // pabsw         %xmm3,%xmm8
+  .byte  102,68,15,56,11,215                 // pmulhrsw      %xmm7,%xmm10
+  .byte  102,65,15,56,29,218                 // pabsw         %xmm10,%xmm3
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  69,137,193                          // mov           %r8d,%r9d
+  .byte  65,128,225,7                        // and           $0x7,%r9b
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  65,254,201                          // dec           %r9b
+  .byte  65,128,249,6                        // cmp           $0x6,%r9b
+  .byte  15,135,73,255,255,255               // ja            ae6 <_sk_lerp_u8_ssse3_lowp+0x19>
+  .byte  69,15,182,201                       // movzbl        %r9b,%r9d
+  .byte  76,141,21,104,0,0,0                 // lea           0x68(%rip),%r10        # c10 <_sk_lerp_u8_ssse3_lowp+0x143>
+  .byte  75,99,4,138                         // movslq        (%r10,%r9,4),%rax
+  .byte  76,1,208                            // add           %r10,%rax
+  .byte  255,224                             // jmpq          *%rax
+  .byte  65,15,182,68,19,6                   // movzbl        0x6(%r11,%rdx,1),%eax
+  .byte  102,69,15,239,192                   // pxor          %xmm8,%xmm8
+  .byte  102,68,15,196,192,6                 // pinsrw        $0x6,%eax,%xmm8
+  .byte  65,15,182,68,19,5                   // movzbl        0x5(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,5                 // pinsrw        $0x5,%eax,%xmm8
+  .byte  65,15,182,68,19,4                   // movzbl        0x4(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,4                 // pinsrw        $0x4,%eax,%xmm8
+  .byte  65,15,182,68,19,3                   // movzbl        0x3(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,3                 // pinsrw        $0x3,%eax,%xmm8
+  .byte  65,15,182,68,19,2                   // movzbl        0x2(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,2                 // pinsrw        $0x2,%eax,%xmm8
+  .byte  65,15,182,68,19,1                   // movzbl        0x1(%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,1                 // pinsrw        $0x1,%eax,%xmm8
+  .byte  65,15,182,4,19                      // movzbl        (%r11,%rdx,1),%eax
+  .byte  102,68,15,196,192,0                 // pinsrw        $0x0,%eax,%xmm8
+  .byte  233,216,254,255,255                 // jmpq          ae6 <_sk_lerp_u8_ssse3_lowp+0x19>
+  .byte  102,144                             // xchg          %ax,%ax
+  .byte  238                                 // out           %al,(%dx)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,226                             // jmpq          *%rdx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,214                             // callq         *%rsi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255,202                             // dec           %edx
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  190,255,255,255,178                 // mov           $0xb2ffffff,%esi
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+  .byte  161                                 // .byte         0xa1
+  .byte  255                                 // (bad)
+  .byte  255                                 // (bad)
+  .byte  255                                 // .byte         0xff
+
+HIDDEN _sk_swap_rb_ssse3_lowp
+.globl _sk_swap_rb_ssse3_lowp
+FUNCTION(_sk_swap_rb_ssse3_lowp)
+_sk_swap_rb_ssse3_lowp:
+  .byte  68,15,40,192                        // movaps        %xmm0,%xmm8
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,194                           // movaps        %xmm2,%xmm0
+  .byte  65,15,40,208                        // movaps        %xmm8,%xmm2
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_swap_ssse3_lowp
+.globl _sk_swap_ssse3_lowp
+FUNCTION(_sk_swap_ssse3_lowp)
+_sk_swap_ssse3_lowp:
+  .byte  68,15,40,195                        // movaps        %xmm3,%xmm8
+  .byte  68,15,40,202                        // movaps        %xmm2,%xmm9
+  .byte  68,15,40,209                        // movaps        %xmm1,%xmm10
+  .byte  68,15,40,216                        // movaps        %xmm0,%xmm11
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  65,15,40,227                        // movaps        %xmm11,%xmm4
+  .byte  65,15,40,234                        // movaps        %xmm10,%xmm5
+  .byte  65,15,40,241                        // movaps        %xmm9,%xmm6
+  .byte  65,15,40,248                        // movaps        %xmm8,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_src_dst_ssse3_lowp
+.globl _sk_move_src_dst_ssse3_lowp
+FUNCTION(_sk_move_src_dst_ssse3_lowp)
+_sk_move_src_dst_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,224                           // movaps        %xmm0,%xmm4
+  .byte  15,40,233                           // movaps        %xmm1,%xmm5
+  .byte  15,40,242                           // movaps        %xmm2,%xmm6
+  .byte  15,40,251                           // movaps        %xmm3,%xmm7
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_move_dst_src_ssse3_lowp
+.globl _sk_move_dst_src_ssse3_lowp
+FUNCTION(_sk_move_dst_src_ssse3_lowp)
+_sk_move_dst_src_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,40,196                           // movaps        %xmm4,%xmm0
+  .byte  15,40,205                           // movaps        %xmm5,%xmm1
+  .byte  15,40,214                           // movaps        %xmm6,%xmm2
+  .byte  15,40,223                           // movaps        %xmm7,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_clear_ssse3_lowp
+.globl _sk_clear_ssse3_lowp
+FUNCTION(_sk_clear_ssse3_lowp)
+_sk_clear_ssse3_lowp:
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  15,87,192                           // xorps         %xmm0,%xmm0
+  .byte  15,87,201                           // xorps         %xmm1,%xmm1
+  .byte  15,87,210                           // xorps         %xmm2,%xmm2
+  .byte  15,87,219                           // xorps         %xmm3,%xmm3
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcatop_ssse3_lowp
+.globl _sk_srcatop_ssse3_lowp
+FUNCTION(_sk_srcatop_ssse3_lowp)
+_sk_srcatop_ssse3_lowp:
+  .byte  102,15,56,11,199                    // pmulhrsw      %xmm7,%xmm0
+  .byte  102,68,15,56,29,192                 // pabsw         %xmm0,%xmm8
+  .byte  102,68,15,111,13,193,5,0,0          // movdqa        0x5c1(%rip),%xmm9        # 1270 <_sk_xor__ssse3_lowp+0x173>
+  .byte  102,68,15,249,203                   // psubw         %xmm3,%xmm9
+  .byte  102,15,111,196                      // movdqa        %xmm4,%xmm0
+  .byte  102,65,15,56,11,193                 // pmulhrsw      %xmm9,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,253,192                   // paddw         %xmm8,%xmm0
+  .byte  102,15,56,11,207                    // pmulhrsw      %xmm7,%xmm1
+  .byte  102,68,15,56,29,193                 // pabsw         %xmm1,%xmm8
+  .byte  102,15,111,205                      // movdqa        %xmm5,%xmm1
+  .byte  102,65,15,56,11,201                 // pmulhrsw      %xmm9,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,253,200                   // paddw         %xmm8,%xmm1
+  .byte  102,15,56,11,215                    // pmulhrsw      %xmm7,%xmm2
+  .byte  102,68,15,56,29,194                 // pabsw         %xmm2,%xmm8
+  .byte  102,15,111,214                      // movdqa        %xmm6,%xmm2
+  .byte  102,65,15,56,11,209                 // pmulhrsw      %xmm9,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,253,208                   // paddw         %xmm8,%xmm2
+  .byte  102,15,56,11,223                    // pmulhrsw      %xmm7,%xmm3
+  .byte  102,68,15,56,29,195                 // pabsw         %xmm3,%xmm8
+  .byte  102,68,15,56,11,207                 // pmulhrsw      %xmm7,%xmm9
+  .byte  102,65,15,56,29,217                 // pabsw         %xmm9,%xmm3
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstatop_ssse3_lowp
+.globl _sk_dstatop_ssse3_lowp
+FUNCTION(_sk_dstatop_ssse3_lowp)
+_sk_dstatop_ssse3_lowp:
+  .byte  102,68,15,111,196                   // movdqa        %xmm4,%xmm8
+  .byte  102,68,15,56,11,195                 // pmulhrsw      %xmm3,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,68,15,111,13,64,5,0,0           // movdqa        0x540(%rip),%xmm9        # 1280 <_sk_xor__ssse3_lowp+0x183>
+  .byte  102,68,15,249,207                   // psubw         %xmm7,%xmm9
+  .byte  102,65,15,56,11,193                 // pmulhrsw      %xmm9,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,253,192                   // paddw         %xmm8,%xmm0
+  .byte  102,68,15,111,197                   // movdqa        %xmm5,%xmm8
+  .byte  102,68,15,56,11,195                 // pmulhrsw      %xmm3,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,65,15,56,11,201                 // pmulhrsw      %xmm9,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,253,200                   // paddw         %xmm8,%xmm1
+  .byte  102,68,15,111,198                   // movdqa        %xmm6,%xmm8
+  .byte  102,68,15,56,11,195                 // pmulhrsw      %xmm3,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,65,15,56,11,209                 // pmulhrsw      %xmm9,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,253,208                   // paddw         %xmm8,%xmm2
+  .byte  102,68,15,111,199                   // movdqa        %xmm7,%xmm8
+  .byte  102,68,15,56,11,195                 // pmulhrsw      %xmm3,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,68,15,56,11,203                 // pmulhrsw      %xmm3,%xmm9
+  .byte  102,65,15,56,29,217                 // pabsw         %xmm9,%xmm3
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcin_ssse3_lowp
+.globl _sk_srcin_ssse3_lowp
+FUNCTION(_sk_srcin_ssse3_lowp)
+_sk_srcin_ssse3_lowp:
+  .byte  102,15,56,11,199                    // pmulhrsw      %xmm7,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,15,56,11,207                    // pmulhrsw      %xmm7,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,15,56,11,215                    // pmulhrsw      %xmm7,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,15,56,11,223                    // pmulhrsw      %xmm7,%xmm3
+  .byte  102,15,56,29,219                    // pabsw         %xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstin_ssse3_lowp
+.globl _sk_dstin_ssse3_lowp
+FUNCTION(_sk_dstin_ssse3_lowp)
+_sk_dstin_ssse3_lowp:
+  .byte  102,15,111,196                      // movdqa        %xmm4,%xmm0
+  .byte  102,15,56,11,195                    // pmulhrsw      %xmm3,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,15,111,205                      // movdqa        %xmm5,%xmm1
+  .byte  102,15,56,11,203                    // pmulhrsw      %xmm3,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,15,111,214                      // movdqa        %xmm6,%xmm2
+  .byte  102,15,56,11,211                    // pmulhrsw      %xmm3,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,15,56,11,223                    // pmulhrsw      %xmm7,%xmm3
+  .byte  102,15,56,29,219                    // pabsw         %xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcout_ssse3_lowp
+.globl _sk_srcout_ssse3_lowp
+FUNCTION(_sk_srcout_ssse3_lowp)
+_sk_srcout_ssse3_lowp:
+  .byte  102,68,15,111,5,102,4,0,0           // movdqa        0x466(%rip),%xmm8        # 1290 <_sk_xor__ssse3_lowp+0x193>
+  .byte  102,68,15,249,199                   // psubw         %xmm7,%xmm8
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,68,15,56,11,195                 // pmulhrsw      %xmm3,%xmm8
+  .byte  102,65,15,56,29,216                 // pabsw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstout_ssse3_lowp
+.globl _sk_dstout_ssse3_lowp
+FUNCTION(_sk_dstout_ssse3_lowp)
+_sk_dstout_ssse3_lowp:
+  .byte  102,68,15,111,5,55,4,0,0            // movdqa        0x437(%rip),%xmm8        # 12a0 <_sk_xor__ssse3_lowp+0x1a3>
+  .byte  102,68,15,249,195                   // psubw         %xmm3,%xmm8
+  .byte  102,15,111,196                      // movdqa        %xmm4,%xmm0
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,15,111,205                      // movdqa        %xmm5,%xmm1
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,15,111,214                      // movdqa        %xmm6,%xmm2
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,68,15,56,11,199                 // pmulhrsw      %xmm7,%xmm8
+  .byte  102,65,15,56,29,216                 // pabsw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_srcover_ssse3_lowp
+.globl _sk_srcover_ssse3_lowp
+FUNCTION(_sk_srcover_ssse3_lowp)
+_sk_srcover_ssse3_lowp:
+  .byte  102,68,15,111,5,252,3,0,0           // movdqa        0x3fc(%rip),%xmm8        # 12b0 <_sk_xor__ssse3_lowp+0x1b3>
+  .byte  102,68,15,249,195                   // psubw         %xmm3,%xmm8
+  .byte  102,68,15,111,204                   // movdqa        %xmm4,%xmm9
+  .byte  102,69,15,56,11,200                 // pmulhrsw      %xmm8,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,65,15,253,193                   // paddw         %xmm9,%xmm0
+  .byte  102,68,15,111,205                   // movdqa        %xmm5,%xmm9
+  .byte  102,69,15,56,11,200                 // pmulhrsw      %xmm8,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,65,15,253,201                   // paddw         %xmm9,%xmm1
+  .byte  102,68,15,111,206                   // movdqa        %xmm6,%xmm9
+  .byte  102,69,15,56,11,200                 // pmulhrsw      %xmm8,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,65,15,253,209                   // paddw         %xmm9,%xmm2
+  .byte  102,68,15,56,11,199                 // pmulhrsw      %xmm7,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_dstover_ssse3_lowp
+.globl _sk_dstover_ssse3_lowp
+FUNCTION(_sk_dstover_ssse3_lowp)
+_sk_dstover_ssse3_lowp:
+  .byte  102,68,15,111,5,167,3,0,0           // movdqa        0x3a7(%rip),%xmm8        # 12c0 <_sk_xor__ssse3_lowp+0x1c3>
+  .byte  102,68,15,249,199                   // psubw         %xmm7,%xmm8
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,15,253,196                      // paddw         %xmm4,%xmm0
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,15,253,205                      // paddw         %xmm5,%xmm1
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,15,253,214                      // paddw         %xmm6,%xmm2
+  .byte  102,68,15,56,11,195                 // pmulhrsw      %xmm3,%xmm8
+  .byte  102,65,15,56,29,216                 // pabsw         %xmm8,%xmm3
+  .byte  102,15,253,223                      // paddw         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_modulate_ssse3_lowp
+.globl _sk_modulate_ssse3_lowp
+FUNCTION(_sk_modulate_ssse3_lowp)
+_sk_modulate_ssse3_lowp:
+  .byte  102,15,56,11,196                    // pmulhrsw      %xmm4,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,15,56,11,205                    // pmulhrsw      %xmm5,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,15,56,11,214                    // pmulhrsw      %xmm6,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,15,56,11,223                    // pmulhrsw      %xmm7,%xmm3
+  .byte  102,15,56,29,219                    // pabsw         %xmm3,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_multiply_ssse3_lowp
+.globl _sk_multiply_ssse3_lowp
+FUNCTION(_sk_multiply_ssse3_lowp)
+_sk_multiply_ssse3_lowp:
+  .byte  102,68,15,111,5,60,3,0,0            // movdqa        0x33c(%rip),%xmm8        # 12d0 <_sk_xor__ssse3_lowp+0x1d3>
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,68,15,249,207                   // psubw         %xmm7,%xmm9
+  .byte  102,68,15,111,208                   // movdqa        %xmm0,%xmm10
+  .byte  102,69,15,56,11,209                 // pmulhrsw      %xmm9,%xmm10
+  .byte  102,69,15,56,29,210                 // pabsw         %xmm10,%xmm10
+  .byte  102,68,15,249,195                   // psubw         %xmm3,%xmm8
+  .byte  102,15,56,11,196                    // pmulhrsw      %xmm4,%xmm0
+  .byte  102,68,15,111,220                   // movdqa        %xmm4,%xmm11
+  .byte  102,69,15,56,11,216                 // pmulhrsw      %xmm8,%xmm11
+  .byte  102,69,15,56,29,219                 // pabsw         %xmm11,%xmm11
+  .byte  102,69,15,253,218                   // paddw         %xmm10,%xmm11
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,253,195                   // paddw         %xmm11,%xmm0
+  .byte  102,68,15,111,209                   // movdqa        %xmm1,%xmm10
+  .byte  102,69,15,56,11,209                 // pmulhrsw      %xmm9,%xmm10
+  .byte  102,69,15,56,29,210                 // pabsw         %xmm10,%xmm10
+  .byte  102,15,56,11,205                    // pmulhrsw      %xmm5,%xmm1
+  .byte  102,68,15,111,221                   // movdqa        %xmm5,%xmm11
+  .byte  102,69,15,56,11,216                 // pmulhrsw      %xmm8,%xmm11
+  .byte  102,69,15,56,29,219                 // pabsw         %xmm11,%xmm11
+  .byte  102,69,15,253,218                   // paddw         %xmm10,%xmm11
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,253,203                   // paddw         %xmm11,%xmm1
+  .byte  102,68,15,111,210                   // movdqa        %xmm2,%xmm10
+  .byte  102,69,15,56,11,209                 // pmulhrsw      %xmm9,%xmm10
+  .byte  102,69,15,56,29,210                 // pabsw         %xmm10,%xmm10
+  .byte  102,15,56,11,214                    // pmulhrsw      %xmm6,%xmm2
+  .byte  102,68,15,111,222                   // movdqa        %xmm6,%xmm11
+  .byte  102,69,15,56,11,216                 // pmulhrsw      %xmm8,%xmm11
+  .byte  102,69,15,56,29,219                 // pabsw         %xmm11,%xmm11
+  .byte  102,69,15,253,218                   // paddw         %xmm10,%xmm11
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,253,211                   // paddw         %xmm11,%xmm2
+  .byte  102,68,15,56,11,203                 // pmulhrsw      %xmm3,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,68,15,56,11,199                 // pmulhrsw      %xmm7,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,69,15,253,193                   // paddw         %xmm9,%xmm8
+  .byte  102,15,56,11,223                    // pmulhrsw      %xmm7,%xmm3
+  .byte  102,15,56,29,219                    // pabsw         %xmm3,%xmm3
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_plus__ssse3_lowp
+.globl _sk_plus__ssse3_lowp
+FUNCTION(_sk_plus__ssse3_lowp)
+_sk_plus__ssse3_lowp:
+  .byte  102,15,253,196                      // paddw         %xmm4,%xmm0
+  .byte  102,15,253,205                      // paddw         %xmm5,%xmm1
+  .byte  102,15,253,214                      // paddw         %xmm6,%xmm2
+  .byte  102,15,253,223                      // paddw         %xmm7,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_screen_ssse3_lowp
+.globl _sk_screen_ssse3_lowp
+FUNCTION(_sk_screen_ssse3_lowp)
+_sk_screen_ssse3_lowp:
+  .byte  102,68,15,111,5,78,2,0,0            // movdqa        0x24e(%rip),%xmm8        # 12e0 <_sk_xor__ssse3_lowp+0x1e3>
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,68,15,249,200                   // psubw         %xmm0,%xmm9
+  .byte  102,68,15,56,11,204                 // pmulhrsw      %xmm4,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,65,15,253,193                   // paddw         %xmm9,%xmm0
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,68,15,249,201                   // psubw         %xmm1,%xmm9
+  .byte  102,68,15,56,11,205                 // pmulhrsw      %xmm5,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,65,15,253,201                   // paddw         %xmm9,%xmm1
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,68,15,249,202                   // psubw         %xmm2,%xmm9
+  .byte  102,68,15,56,11,206                 // pmulhrsw      %xmm6,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,65,15,253,209                   // paddw         %xmm9,%xmm2
+  .byte  102,68,15,249,195                   // psubw         %xmm3,%xmm8
+  .byte  102,68,15,56,11,199                 // pmulhrsw      %xmm7,%xmm8
+  .byte  102,69,15,56,29,192                 // pabsw         %xmm8,%xmm8
+  .byte  102,65,15,253,216                   // paddw         %xmm8,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+HIDDEN _sk_xor__ssse3_lowp
+.globl _sk_xor__ssse3_lowp
+FUNCTION(_sk_xor__ssse3_lowp)
+_sk_xor__ssse3_lowp:
+  .byte  102,68,15,111,5,234,1,0,0           // movdqa        0x1ea(%rip),%xmm8        # 12f0 <_sk_xor__ssse3_lowp+0x1f3>
+  .byte  102,69,15,111,200                   // movdqa        %xmm8,%xmm9
+  .byte  102,68,15,249,207                   // psubw         %xmm7,%xmm9
+  .byte  102,65,15,56,11,193                 // pmulhrsw      %xmm9,%xmm0
+  .byte  102,68,15,56,29,208                 // pabsw         %xmm0,%xmm10
+  .byte  102,68,15,249,195                   // psubw         %xmm3,%xmm8
+  .byte  102,15,111,196                      // movdqa        %xmm4,%xmm0
+  .byte  102,65,15,56,11,192                 // pmulhrsw      %xmm8,%xmm0
+  .byte  102,15,56,29,192                    // pabsw         %xmm0,%xmm0
+  .byte  102,65,15,253,194                   // paddw         %xmm10,%xmm0
+  .byte  102,65,15,56,11,201                 // pmulhrsw      %xmm9,%xmm1
+  .byte  102,68,15,56,29,209                 // pabsw         %xmm1,%xmm10
+  .byte  102,15,111,205                      // movdqa        %xmm5,%xmm1
+  .byte  102,65,15,56,11,200                 // pmulhrsw      %xmm8,%xmm1
+  .byte  102,15,56,29,201                    // pabsw         %xmm1,%xmm1
+  .byte  102,65,15,253,202                   // paddw         %xmm10,%xmm1
+  .byte  102,65,15,56,11,209                 // pmulhrsw      %xmm9,%xmm2
+  .byte  102,68,15,56,29,210                 // pabsw         %xmm2,%xmm10
+  .byte  102,15,111,214                      // movdqa        %xmm6,%xmm2
+  .byte  102,65,15,56,11,208                 // pmulhrsw      %xmm8,%xmm2
+  .byte  102,15,56,29,210                    // pabsw         %xmm2,%xmm2
+  .byte  102,65,15,253,210                   // paddw         %xmm10,%xmm2
+  .byte  102,68,15,56,11,203                 // pmulhrsw      %xmm3,%xmm9
+  .byte  102,69,15,56,29,201                 // pabsw         %xmm9,%xmm9
+  .byte  102,68,15,56,11,199                 // pmulhrsw      %xmm7,%xmm8
+  .byte  102,65,15,56,29,216                 // pabsw         %xmm8,%xmm3
+  .byte  102,65,15,253,217                   // paddw         %xmm9,%xmm3
+  .byte  72,173                              // lods          %ds:(%rsi),%rax
+  .byte  255,224                             // jmpq          *%rax
+
+BALIGN16
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,67,0,0                          // addb          $0x0,0x0(%rbx)
+  .byte  128,67,0,0                          // addb          $0x0,0x0(%rbx)
+  .byte  128,67,0,0                          // addb          $0x0,0x0(%rbx)
+  .byte  128,67,0,4                          // addb          $0x4,0x0(%rbx)
+  .byte  8,12,1                              // or            %cl,(%rcx,%rax,1)
+  .byte  5,9,13,2,6                          // add           $0x6020d09,%eax
+  .byte  10,14                               // or            (%rsi),%cl
+  .byte  3,7                                 // add           (%rdi),%eax
+  .byte  11,15                               // or            (%rdi),%ecx
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,129,128,0,128// addl          $0x80008081,-0x7f7e7f7f(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,4,8,12                      // add           %al,0xc080400(%rax)
+  .byte  1,5,9,13,2,6                        // add           %eax,0x6020d09(%rip)        # 6021f13 <_sk_xor__ssse3_lowp+0x6020e16>
+  .byte  10,14                               // or            (%rsi),%cl
+  .byte  3,7                                 // add           (%rdi),%eax
+  .byte  11,15                               // or            (%rdi),%ecx
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,0,128,0,128 // addl          $0x80008000,-0x7f7e7f7f(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,0,128,0,128 // addl          $0x80008000,-0x7f7e7f7f(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  129,128,129,128,129,128,129,128,129,128// addl          $0x80818081,-0x7f7e7f7f(%rax)
+  .byte  129,128,129,128,129,128,0,128,0,128 // addl          $0x80008000,-0x7f7e7f7f(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+  .byte  0,128,0,128,0,128                   // add           %al,-0x7fff8000(%rax)
+
+BALIGN4
+  .byte  0,0                                 // add           %al,(%rax)
+  .byte  128,67,0,0                          // addb          $0x0,0x0(%rbx)
+  .byte  128,67,0,0                          // addb          $0x0,0x0(%rbx)
+  .byte  128                                 // .byte         0x80
+  .byte  67                                  // rex.XB
+#endif
diff --git a/src/jumper/SkJumper_generated.cpp b/src/jumper/SkJumper_generated.cpp
deleted file mode 100644
index 2494c1e..0000000
--- a/src/jumper/SkJumper_generated.cpp
+++ /dev/null
@@ -1,13052 +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.
- */
-
-// This file is generated semi-automatically with this command:
-//   $ src/jumper/build_stages.py
-
-#include <stdint.h>
-
-#if defined(_MSC_VER)
-    #pragma section("code", read,execute)
-    #define CODE extern "C" __declspec(allocate("code"))
-#elif defined(__MACH__)
-    #define CODE extern "C" __attribute__((section("__TEXT,__text")))
-#else
-    #define CODE extern "C" __attribute__((section(".text")))
-#endif
-
-#if defined(__aarch64__)
-
-CODE const uint32_t sk_start_pipeline_aarch64[] = {
-  0xa9bd5bf7,                             //stp           x23, x22, [sp, #-48]!
-  0xa90153f5,                             //stp           x21, x20, [sp, #16]
-  0xa9027bf3,                             //stp           x19, x30, [sp, #32]
-  0xaa0103f4,                             //mov           x20, x1
-  0xf8408697,                             //ldr           x23, [x20], #8
-  0xaa0003f5,                             //mov           x21, x0
-  0xaa0303f3,                             //mov           x19, x3
-  0x910012a8,                             //add           x8, x21, #0x4
-  0xeb13011f,                             //cmp           x8, x19
-  0xaa0203f6,                             //mov           x22, x2
-  0x54000069,                             //b.ls          34 <sk_start_pipeline_aarch64+0x34>  // b.plast
-  0xaa1503e0,                             //mov           x0, x21
-  0x14000012,                             //b             78 <sk_start_pipeline_aarch64+0x78>
-  0x6f00e400,                             //movi          v0.2d, #0x0
-  0x6f00e401,                             //movi          v1.2d, #0x0
-  0x6f00e402,                             //movi          v2.2d, #0x0
-  0x6f00e403,                             //movi          v3.2d, #0x0
-  0x6f00e404,                             //movi          v4.2d, #0x0
-  0x6f00e405,                             //movi          v5.2d, #0x0
-  0x6f00e406,                             //movi          v6.2d, #0x0
-  0x6f00e407,                             //movi          v7.2d, #0x0
-  0xaa1503e0,                             //mov           x0, x21
-  0xaa1403e1,                             //mov           x1, x20
-  0xaa1603e2,                             //mov           x2, x22
-  0xd63f02e0,                             //blr           x23
-  0x910012a0,                             //add           x0, x21, #0x4
-  0x910022a8,                             //add           x8, x21, #0x8
-  0xeb13011f,                             //cmp           x8, x19
-  0xaa0003f5,                             //mov           x21, x0
-  0x54fffe09,                             //b.ls          34 <sk_start_pipeline_aarch64+0x34>  // b.plast
-  0xa9427bf3,                             //ldp           x19, x30, [sp, #32]
-  0xa94153f5,                             //ldp           x21, x20, [sp, #16]
-  0xa8c35bf7,                             //ldp           x23, x22, [sp], #48
-  0xd65f03c0,                             //ret
-};
-
-CODE const uint32_t sk_just_return_aarch64[] = {
-  0xd65f03c0,                             //ret
-};
-
-CODE const uint32_t sk_seed_shader_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x3dc00046,                             //ldr           q6, [x2]
-  0x4e040c00,                             //dup           v0.4s, w0
-  0x4f0167e7,                             //movi          v7.4s, #0x3f, lsl #24
-  0x4d40c901,                             //ld1r          {v1.4s}, [x8]
-  0x4e21d800,                             //scvtf         v0.4s, v0.4s
-  0x4e27d400,                             //fadd          v0.4s, v0.4s, v7.4s
-  0x4f03f602,                             //fmov          v2.4s, #1.000000000000000000e+00
-  0x4e21d821,                             //scvtf         v1.4s, v1.4s
-  0x6f00e403,                             //movi          v3.2d, #0x0
-  0x6f00e404,                             //movi          v4.2d, #0x0
-  0x6f00e405,                             //movi          v5.2d, #0x0
-  0x4e26d400,                             //fadd          v0.4s, v0.4s, v6.4s
-  0x6f00e406,                             //movi          v6.2d, #0x0
-  0x4e27d421,                             //fadd          v1.4s, v1.4s, v7.4s
-  0x6f00e407,                             //movi          v7.2d, #0x0
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_constant_color_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x3dc00103,                             //ldr           q3, [x8]
-  0x4e040460,                             //dup           v0.4s, v3.s[0]
-  0x4e0c0461,                             //dup           v1.4s, v3.s[1]
-  0x4e140462,                             //dup           v2.4s, v3.s[2]
-  0x4e1c0463,                             //dup           v3.4s, v3.s[3]
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_clear_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x6f00e400,                             //movi          v0.2d, #0x0
-  0x6f00e401,                             //movi          v1.2d, #0x0
-  0x6f00e402,                             //movi          v2.2d, #0x0
-  0x6f00e403,                             //movi          v3.2d, #0x0
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_plus__aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4e24d400,                             //fadd          v0.4s, v0.4s, v4.4s
-  0x4e25d421,                             //fadd          v1.4s, v1.4s, v5.4s
-  0x4e26d442,                             //fadd          v2.4s, v2.4s, v6.4s
-  0x4e27d463,                             //fadd          v3.4s, v3.4s, v7.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_srcover_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4f03f610,                             //fmov          v16.4s, #1.000000000000000000e+00
-  0x4ea3d610,                             //fsub          v16.4s, v16.4s, v3.4s
-  0x4e24ce00,                             //fmla          v0.4s, v16.4s, v4.4s
-  0x4e25ce01,                             //fmla          v1.4s, v16.4s, v5.4s
-  0x4e26ce02,                             //fmla          v2.4s, v16.4s, v6.4s
-  0x4e27ce03,                             //fmla          v3.4s, v16.4s, v7.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_dstover_aarch64[] = {
-  0x4f03f611,                             //fmov          v17.4s, #1.000000000000000000e+00
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4ea41c90,                             //mov           v16.16b, v4.16b
-  0x4ea7d634,                             //fsub          v20.4s, v17.4s, v7.4s
-  0x4ea51cb1,                             //mov           v17.16b, v5.16b
-  0x4ea61cd2,                             //mov           v18.16b, v6.16b
-  0x4ea71cf3,                             //mov           v19.16b, v7.16b
-  0x4e20ce90,                             //fmla          v16.4s, v20.4s, v0.4s
-  0x4e21ce91,                             //fmla          v17.4s, v20.4s, v1.4s
-  0x4e22ce92,                             //fmla          v18.4s, v20.4s, v2.4s
-  0x4e23ce93,                             //fmla          v19.4s, v20.4s, v3.4s
-  0x4eb01e00,                             //mov           v0.16b, v16.16b
-  0x4eb11e21,                             //mov           v1.16b, v17.16b
-  0x4eb21e42,                             //mov           v2.16b, v18.16b
-  0x4eb31e63,                             //mov           v3.16b, v19.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_clamp_0_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x6f00e410,                             //movi          v16.2d, #0x0
-  0x4e30f400,                             //fmax          v0.4s, v0.4s, v16.4s
-  0x4e30f421,                             //fmax          v1.4s, v1.4s, v16.4s
-  0x4e30f442,                             //fmax          v2.4s, v2.4s, v16.4s
-  0x4e30f463,                             //fmax          v3.4s, v3.4s, v16.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_clamp_1_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4f03f610,                             //fmov          v16.4s, #1.000000000000000000e+00
-  0x4eb0f400,                             //fmin          v0.4s, v0.4s, v16.4s
-  0x4eb0f421,                             //fmin          v1.4s, v1.4s, v16.4s
-  0x4eb0f442,                             //fmin          v2.4s, v2.4s, v16.4s
-  0x4eb0f463,                             //fmin          v3.4s, v3.4s, v16.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_clamp_a_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4f03f610,                             //fmov          v16.4s, #1.000000000000000000e+00
-  0x4eb0f463,                             //fmin          v3.4s, v3.4s, v16.4s
-  0x4ea3f400,                             //fmin          v0.4s, v0.4s, v3.4s
-  0x4ea3f421,                             //fmin          v1.4s, v1.4s, v3.4s
-  0x4ea3f442,                             //fmin          v2.4s, v2.4s, v3.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_set_rgb_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xaa0803e9,                             //mov           x9, x8
-  0x4ddfc920,                             //ld1r          {v0.4s}, [x9], #4
-  0x91002108,                             //add           x8, x8, #0x8
-  0x4d40c902,                             //ld1r          {v2.4s}, [x8]
-  0x4d40c921,                             //ld1r          {v1.4s}, [x9]
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_swap_rb_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4ea01c10,                             //mov           v16.16b, v0.16b
-  0x4ea21c40,                             //mov           v0.16b, v2.16b
-  0x4eb01e02,                             //mov           v2.16b, v16.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_swap_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4ea31c70,                             //mov           v16.16b, v3.16b
-  0x4ea21c51,                             //mov           v17.16b, v2.16b
-  0x4ea11c32,                             //mov           v18.16b, v1.16b
-  0x4ea01c13,                             //mov           v19.16b, v0.16b
-  0x4ea41c80,                             //mov           v0.16b, v4.16b
-  0x4ea51ca1,                             //mov           v1.16b, v5.16b
-  0x4ea61cc2,                             //mov           v2.16b, v6.16b
-  0x4ea71ce3,                             //mov           v3.16b, v7.16b
-  0x4eb31e64,                             //mov           v4.16b, v19.16b
-  0x4eb21e45,                             //mov           v5.16b, v18.16b
-  0x4eb11e26,                             //mov           v6.16b, v17.16b
-  0x4eb01e07,                             //mov           v7.16b, v16.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_move_src_dst_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4ea01c04,                             //mov           v4.16b, v0.16b
-  0x4ea11c25,                             //mov           v5.16b, v1.16b
-  0x4ea21c46,                             //mov           v6.16b, v2.16b
-  0x4ea31c67,                             //mov           v7.16b, v3.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_move_dst_src_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4ea41c80,                             //mov           v0.16b, v4.16b
-  0x4ea51ca1,                             //mov           v1.16b, v5.16b
-  0x4ea61cc2,                             //mov           v2.16b, v6.16b
-  0x4ea71ce3,                             //mov           v3.16b, v7.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_premul_aarch64[] = {
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x6e23dc00,                             //fmul          v0.4s, v0.4s, v3.4s
-  0x6e23dc21,                             //fmul          v1.4s, v1.4s, v3.4s
-  0x6e23dc42,                             //fmul          v2.4s, v2.4s, v3.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_unpremul_aarch64[] = {
-  0x4f03f611,                             //fmov          v17.4s, #1.000000000000000000e+00
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4ea0d870,                             //fcmeq         v16.4s, v3.4s, #0.0
-  0x6e23fe31,                             //fdiv          v17.4s, v17.4s, v3.4s
-  0x4e701e30,                             //bic           v16.16b, v17.16b, v16.16b
-  0x6e20de00,                             //fmul          v0.4s, v16.4s, v0.4s
-  0x6e21de01,                             //fmul          v1.4s, v16.4s, v1.4s
-  0x6e22de02,                             //fmul          v2.4s, v16.4s, v2.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_from_srgb_aarch64[] = {
-  0x52a7d328,                             //mov           w8, #0x3e990000
-  0x72933348,                             //movk          w8, #0x999a
-  0x4e040d10,                             //dup           v16.4s, w8
-  0x52a7e648,                             //mov           w8, #0x3f320000
-  0x7291eb88,                             //movk          w8, #0x8f5c
-  0x4e040d11,                             //dup           v17.4s, w8
-  0x52a76468,                             //mov           w8, #0x3b230000
-  0x729ae148,                             //movk          w8, #0xd70a
-  0x4e040d12,                             //dup           v18.4s, w8
-  0x52a7b3c8,                             //mov           w8, #0x3d9e0000
-  0x72907228,                             //movk          w8, #0x8391
-  0x6e22dc54,                             //fmul          v20.4s, v2.4s, v2.4s
-  0x4eb11e35,                             //mov           v21.16b, v17.16b
-  0x4eb11e37,                             //mov           v23.16b, v17.16b
-  0x4e22ce11,                             //fmla          v17.4s, v16.4s, v2.4s
-  0x4eb21e56,                             //mov           v22.16b, v18.16b
-  0x4eb21e58,                             //mov           v24.16b, v18.16b
-  0x4e34ce32,                             //fmla          v18.4s, v17.4s, v20.4s
-  0x4e040d11,                             //dup           v17.4s, w8
-  0x52a7ac28,                             //mov           w8, #0x3d610000
-  0x6e20dc13,                             //fmul          v19.4s, v0.4s, v0.4s
-  0x7288f5c8,                             //movk          w8, #0x47ae
-  0x4e20ce15,                             //fmla          v21.4s, v16.4s, v0.4s
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x6e21dc34,                             //fmul          v20.4s, v1.4s, v1.4s
-  0x4e33ceb6,                             //fmla          v22.4s, v21.4s, v19.4s
-  0x4e040d13,                             //dup           v19.4s, w8
-  0x4e21ce17,                             //fmla          v23.4s, v16.4s, v1.4s
-  0x6e31dc15,                             //fmul          v21.4s, v0.4s, v17.4s
-  0x6ea0e660,                             //fcmgt         v0.4s, v19.4s, v0.4s
-  0x6e31dc30,                             //fmul          v16.4s, v1.4s, v17.4s
-  0x6ea1e661,                             //fcmgt         v1.4s, v19.4s, v1.4s
-  0x6e31dc51,                             //fmul          v17.4s, v2.4s, v17.4s
-  0x6ea2e662,                             //fcmgt         v2.4s, v19.4s, v2.4s
-  0x4e34cef8,                             //fmla          v24.4s, v23.4s, v20.4s
-  0x6e761ea0,                             //bsl           v0.16b, v21.16b, v22.16b
-  0x6e781e01,                             //bsl           v1.16b, v16.16b, v24.16b
-  0x6e721e22,                             //bsl           v2.16b, v17.16b, v18.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_to_srgb_aarch64[] = {
-  0x52a828e8,                             //mov           w8, #0x41470000
-  0x728b8528,                             //movk          w8, #0x5c29
-  0x4e040d12,                             //dup           v18.4s, w8
-  0x52a7e608,                             //mov           w8, #0x3f300000
-  0x728df9c8,                             //movk          w8, #0x6fce
-  0x6ea1d811,                             //frsqrte       v17.4s, v0.4s
-  0x4e040d13,                             //dup           v19.4s, w8
-  0x52b7b948,                             //mov           w8, #0xbdca0000
-  0x728af508,                             //movk          w8, #0x57a8
-  0x6ea1d834,                             //frsqrte       v20.4s, v1.4s
-  0x6e31de36,                             //fmul          v22.4s, v17.4s, v17.4s
-  0x4e040d10,                             //dup           v16.4s, w8
-  0x52a77188,                             //mov           w8, #0x3b8c0000
-  0x6ea1d855,                             //frsqrte       v21.4s, v2.4s
-  0x6e34de98,                             //fmul          v24.4s, v20.4s, v20.4s
-  0x4eb6fc16,                             //frsqrts       v22.4s, v0.4s, v22.4s
-  0x729ce088,                             //movk          w8, #0xe704
-  0x6e35deb9,                             //fmul          v25.4s, v21.4s, v21.4s
-  0x4eb8fc38,                             //frsqrts       v24.4s, v1.4s, v24.4s
-  0x6e36de31,                             //fmul          v17.4s, v17.4s, v22.4s
-  0x4e040d17,                             //dup           v23.4s, w8
-  0x4eb9fc59,                             //frsqrts       v25.4s, v2.4s, v25.4s
-  0x6e38de94,                             //fmul          v20.4s, v20.4s, v24.4s
-  0x4ea1da36,                             //frecpe        v22.4s, v17.4s
-  0x6e32dc1a,                             //fmul          v26.4s, v0.4s, v18.4s
-  0x6ea0e6e0,                             //fcmgt         v0.4s, v23.4s, v0.4s
-  0x6e32dc3c,                             //fmul          v28.4s, v1.4s, v18.4s
-  0x6ea1e6e1,                             //fcmgt         v1.4s, v23.4s, v1.4s
-  0x6e32dc52,                             //fmul          v18.4s, v2.4s, v18.4s
-  0x6ea2e6e2,                             //fcmgt         v2.4s, v23.4s, v2.4s
-  0x6e39deb5,                             //fmul          v21.4s, v21.4s, v25.4s
-  0x4ea1da97,                             //frecpe        v23.4s, v20.4s
-  0x4e36fe39,                             //frecps        v25.4s, v17.4s, v22.4s
-  0x4ea1dab8,                             //frecpe        v24.4s, v21.4s
-  0x6e39ded6,                             //fmul          v22.4s, v22.4s, v25.4s
-  0x4e37fe99,                             //frecps        v25.4s, v20.4s, v23.4s
-  0x4eb01e1b,                             //mov           v27.16b, v16.16b
-  0x6e39def7,                             //fmul          v23.4s, v23.4s, v25.4s
-  0x4e38feb9,                             //frecps        v25.4s, v21.4s, v24.4s
-  0x6e39df18,                             //fmul          v24.4s, v24.4s, v25.4s
-  0x4eb01e19,                             //mov           v25.16b, v16.16b
-  0x4e36ce7b,                             //fmla          v27.4s, v19.4s, v22.4s
-  0x6ea1da36,                             //frsqrte       v22.4s, v17.4s
-  0x4e37ce79,                             //fmla          v25.4s, v19.4s, v23.4s
-  0x6ea1da97,                             //frsqrte       v23.4s, v20.4s
-  0x4e38ce70,                             //fmla          v16.4s, v19.4s, v24.4s
-  0x6e36ded8,                             //fmul          v24.4s, v22.4s, v22.4s
-  0x6ea1dab3,                             //frsqrte       v19.4s, v21.4s
-  0x4eb8fe31,                             //frsqrts       v17.4s, v17.4s, v24.4s
-  0x6e37def8,                             //fmul          v24.4s, v23.4s, v23.4s
-  0x4eb8fe94,                             //frsqrts       v20.4s, v20.4s, v24.4s
-  0x6e33de78,                             //fmul          v24.4s, v19.4s, v19.4s
-  0x52a7da48,                             //mov           w8, #0x3ed20000
-  0x4eb8feb5,                             //frsqrts       v21.4s, v21.4s, v24.4s
-  0x7290f848,                             //movk          w8, #0x87c2
-  0x6e31ded1,                             //fmul          v17.4s, v22.4s, v17.4s
-  0x6e34def4,                             //fmul          v20.4s, v23.4s, v20.4s
-  0x6e35de73,                             //fmul          v19.4s, v19.4s, v21.4s
-  0x4e040d15,                             //dup           v21.4s, w8
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x4e31cebb,                             //fmla          v27.4s, v21.4s, v17.4s
-  0x4f03f611,                             //fmov          v17.4s, #1.000000000000000000e+00
-  0x4e34ceb9,                             //fmla          v25.4s, v21.4s, v20.4s
-  0x4e33ceb0,                             //fmla          v16.4s, v21.4s, v19.4s
-  0x4ebbf633,                             //fmin          v19.4s, v17.4s, v27.4s
-  0x4eb9f634,                             //fmin          v20.4s, v17.4s, v25.4s
-  0x4eb0f630,                             //fmin          v16.4s, v17.4s, v16.4s
-  0x6e731f40,                             //bsl           v0.16b, v26.16b, v19.16b
-  0x6e741f81,                             //bsl           v1.16b, v28.16b, v20.16b
-  0x6e701e42,                             //bsl           v2.16b, v18.16b, v16.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_scale_1_float_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xbd400110,                             //ldr           s16, [x8]
-  0x4f909000,                             //fmul          v0.4s, v0.4s, v16.s[0]
-  0x4f909021,                             //fmul          v1.4s, v1.4s, v16.s[0]
-  0x4f909042,                             //fmul          v2.4s, v2.4s, v16.s[0]
-  0x4f909063,                             //fmul          v3.4s, v3.4s, v16.s[0]
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_scale_u8_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x52a77009,                             //mov           w9, #0x3b800000
-  0x72901029,                             //movk          w9, #0x8081
-  0x4e040d30,                             //dup           v16.4s, w9
-  0xf9400108,                             //ldr           x8, [x8]
-  0x8b000108,                             //add           x8, x8, x0
-  0x39400109,                             //ldrb          w9, [x8]
-  0x3940050a,                             //ldrb          w10, [x8, #1]
-  0x3940090b,                             //ldrb          w11, [x8, #2]
-  0x39400d08,                             //ldrb          w8, [x8, #3]
-  0x4e021d31,                             //mov           v17.h[0], w9
-  0x4e061d51,                             //mov           v17.h[1], w10
-  0x4e0a1d71,                             //mov           v17.h[2], w11
-  0x4e0e1d11,                             //mov           v17.h[3], w8
-  0x2f10a631,                             //uxtl          v17.4s, v17.4h
-  0x6e21da31,                             //ucvtf         v17.4s, v17.4s
-  0x6e30de30,                             //fmul          v16.4s, v17.4s, v16.4s
-  0x6e20de00,                             //fmul          v0.4s, v16.4s, v0.4s
-  0x6e21de01,                             //fmul          v1.4s, v16.4s, v1.4s
-  0x6e22de02,                             //fmul          v2.4s, v16.4s, v2.4s
-  0x6e23de03,                             //fmul          v3.4s, v16.4s, v3.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_lerp_1_float_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x4ea4d411,                             //fsub          v17.4s, v0.4s, v4.4s
-  0x4ea41c80,                             //mov           v0.16b, v4.16b
-  0x4ea5d432,                             //fsub          v18.4s, v1.4s, v5.4s
-  0xbd400110,                             //ldr           s16, [x8]
-  0x4ea51ca1,                             //mov           v1.16b, v5.16b
-  0x4f901220,                             //fmla          v0.4s, v17.4s, v16.s[0]
-  0x4ea6d451,                             //fsub          v17.4s, v2.4s, v6.4s
-  0x4f901241,                             //fmla          v1.4s, v18.4s, v16.s[0]
-  0x4ea61cc2,                             //mov           v2.16b, v6.16b
-  0x4ea7d472,                             //fsub          v18.4s, v3.4s, v7.4s
-  0x4ea71ce3,                             //mov           v3.16b, v7.16b
-  0x4f901222,                             //fmla          v2.4s, v17.4s, v16.s[0]
-  0x4f901243,                             //fmla          v3.4s, v18.4s, v16.s[0]
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_lerp_u8_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x52a77009,                             //mov           w9, #0x3b800000
-  0x72901029,                             //movk          w9, #0x8081
-  0x4e040d30,                             //dup           v16.4s, w9
-  0xf9400108,                             //ldr           x8, [x8]
-  0x4ea4d412,                             //fsub          v18.4s, v0.4s, v4.4s
-  0x8b000108,                             //add           x8, x8, x0
-  0x3940010a,                             //ldrb          w10, [x8]
-  0x39400509,                             //ldrb          w9, [x8, #1]
-  0x3940090b,                             //ldrb          w11, [x8, #2]
-  0x39400d08,                             //ldrb          w8, [x8, #3]
-  0x4e021d51,                             //mov           v17.h[0], w10
-  0x4e061d31,                             //mov           v17.h[1], w9
-  0x4e0a1d71,                             //mov           v17.h[2], w11
-  0x4e0e1d11,                             //mov           v17.h[3], w8
-  0x2f10a620,                             //uxtl          v0.4s, v17.4h
-  0x6e21d800,                             //ucvtf         v0.4s, v0.4s
-  0x6e30dc10,                             //fmul          v16.4s, v0.4s, v16.4s
-  0x4ea41c80,                             //mov           v0.16b, v4.16b
-  0x4ea5d431,                             //fsub          v17.4s, v1.4s, v5.4s
-  0x4ea51ca1,                             //mov           v1.16b, v5.16b
-  0x4e32ce00,                             //fmla          v0.4s, v16.4s, v18.4s
-  0x4ea6d452,                             //fsub          v18.4s, v2.4s, v6.4s
-  0x4e31ce01,                             //fmla          v1.4s, v16.4s, v17.4s
-  0x4ea61cc2,                             //mov           v2.16b, v6.16b
-  0x4ea7d471,                             //fsub          v17.4s, v3.4s, v7.4s
-  0x4ea71ce3,                             //mov           v3.16b, v7.16b
-  0x4e32ce02,                             //fmla          v2.4s, v16.4s, v18.4s
-  0x4e31ce03,                             //fmla          v3.4s, v16.4s, v17.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_lerp_565_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xd37ff809,                             //lsl           x9, x0, #1
-  0x4f072710,                             //movi          v16.4s, #0xf8, lsl #8
-  0x4ea4d413,                             //fsub          v19.4s, v0.4s, v4.4s
-  0xf9400108,                             //ldr           x8, [x8]
-  0xfc696903,                             //ldr           d3, [x8, x9]
-  0x52a6f088,                             //mov           w8, #0x37840000
-  0x72842108,                             //movk          w8, #0x2108
-  0x4e040d11,                             //dup           v17.4s, w8
-  0x2f10a463,                             //uxtl          v3.4s, v3.4h
-  0x321b17e8,                             //orr           w8, wzr, #0x7e0
-  0x4e301c60,                             //and           v0.16b, v3.16b, v16.16b
-  0x4e040d12,                             //dup           v18.4s, w8
-  0x52a74048,                             //mov           w8, #0x3a020000
-  0x4e21d800,                             //scvtf         v0.4s, v0.4s
-  0x72810428,                             //movk          w8, #0x821
-  0x6e31dc10,                             //fmul          v16.4s, v0.4s, v17.4s
-  0x4ea41c80,                             //mov           v0.16b, v4.16b
-  0x4e33ce00,                             //fmla          v0.4s, v16.4s, v19.4s
-  0x4f0007f0,                             //movi          v16.4s, #0x1f
-  0x4e040d11,                             //dup           v17.4s, w8
-  0x52a7a088,                             //mov           w8, #0x3d040000
-  0x4e321c72,                             //and           v18.16b, v3.16b, v18.16b
-  0x72842108,                             //movk          w8, #0x2108
-  0x4e301c63,                             //and           v3.16b, v3.16b, v16.16b
-  0x4ea6d450,                             //fsub          v16.4s, v2.4s, v6.4s
-  0x4e21da42,                             //scvtf         v2.4s, v18.4s
-  0x6e31dc51,                             //fmul          v17.4s, v2.4s, v17.4s
-  0x4e040d02,                             //dup           v2.4s, w8
-  0x4e21d863,                             //scvtf         v3.4s, v3.4s
-  0x4ea5d433,                             //fsub          v19.4s, v1.4s, v5.4s
-  0x4ea51ca1,                             //mov           v1.16b, v5.16b
-  0x6e22dc63,                             //fmul          v3.4s, v3.4s, v2.4s
-  0x4ea61cc2,                             //mov           v2.16b, v6.16b
-  0x4e33ce21,                             //fmla          v1.4s, v17.4s, v19.4s
-  0x4e30cc62,                             //fmla          v2.4s, v3.4s, v16.4s
-  0x4f03f603,                             //fmov          v3.4s, #1.000000000000000000e+00
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_load_tables_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xd37ef409,                             //lsl           x9, x0, #2
-  0x6f00e620,                             //movi          v0.2d, #0xff000000ff
-  0x52a7700b,                             //mov           w11, #0x3b800000
-  0xa940310a,                             //ldp           x10, x12, [x8]
-  0x7290102b,                             //movk          w11, #0x8081
-  0x4e040d63,                             //dup           v3.4s, w11
-  0x3ce96942,                             //ldr           q2, [x10, x9]
-  0xa9412109,                             //ldp           x9, x8, [x8, #16]
-  0x4e201c41,                             //and           v1.16b, v2.16b, v0.16b
-  0x1e26002e,                             //fmov          w14, s1
-  0x6f380450,                             //ushr          v16.4s, v2.4s, #8
-  0x6f300451,                             //ushr          v17.4s, v2.4s, #16
-  0x8b2e498e,                             //add           x14, x12, w14, uxtw #2
-  0x0e0c3c2a,                             //mov           w10, v1.s[1]
-  0x0e143c2b,                             //mov           w11, v1.s[2]
-  0x0e1c3c2d,                             //mov           w13, v1.s[3]
-  0x4e201e01,                             //and           v1.16b, v16.16b, v0.16b
-  0x4e201e30,                             //and           v16.16b, v17.16b, v0.16b
-  0x0d4081c0,                             //ld1           {v0.s}[0], [x14]
-  0x8b2a498a,                             //add           x10, x12, w10, uxtw #2
-  0xbc6b5991,                             //ldr           s17, [x12, w11, uxtw #2]
-  0xbc6d5992,                             //ldr           s18, [x12, w13, uxtw #2]
-  0x0e0c3c2b,                             //mov           w11, v1.s[1]
-  0x0e143c2c,                             //mov           w12, v1.s[2]
-  0x0e1c3c2d,                             //mov           w13, v1.s[3]
-  0x1e26002e,                             //fmov          w14, s1
-  0x8b2e492e,                             //add           x14, x9, w14, uxtw #2
-  0xbc6c5933,                             //ldr           s19, [x9, w12, uxtw #2]
-  0xbc6d5934,                             //ldr           s20, [x9, w13, uxtw #2]
-  0x8b2b4929,                             //add           x9, x9, w11, uxtw #2
-  0x1e26020b,                             //fmov          w11, s16
-  0x6f280442,                             //ushr          v2.4s, v2.4s, #24
-  0x0d409140,                             //ld1           {v0.s}[1], [x10]
-  0x4e21d842,                             //scvtf         v2.4s, v2.4s
-  0x8b2b490a,                             //add           x10, x8, w11, uxtw #2
-  0x0d4081c1,                             //ld1           {v1.s}[0], [x14]
-  0x6e23dc43,                             //fmul          v3.4s, v2.4s, v3.4s
-  0x0d408142,                             //ld1           {v2.s}[0], [x10]
-  0x0e0c3e0f,                             //mov           w15, v16.s[1]
-  0x0e143e0c,                             //mov           w12, v16.s[2]
-  0x8b2f490a,                             //add           x10, x8, w15, uxtw #2
-  0x0e1c3e0d,                             //mov           w13, v16.s[3]
-  0xbc6c5910,                             //ldr           s16, [x8, w12, uxtw #2]
-  0x0d409121,                             //ld1           {v1.s}[1], [x9]
-  0x0d409142,                             //ld1           {v2.s}[1], [x10]
-  0x6e140620,                             //mov           v0.s[2], v17.s[0]
-  0xbc6d5911,                             //ldr           s17, [x8, w13, uxtw #2]
-  0x6e140661,                             //mov           v1.s[2], v19.s[0]
-  0x6e140602,                             //mov           v2.s[2], v16.s[0]
-  0x6e1c0640,                             //mov           v0.s[3], v18.s[0]
-  0x6e1c0681,                             //mov           v1.s[3], v20.s[0]
-  0x6e1c0622,                             //mov           v2.s[3], v17.s[0]
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_load_a8_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x52a77009,                             //mov           w9, #0x3b800000
-  0x72901029,                             //movk          w9, #0x8081
-  0x4e040d22,                             //dup           v2.4s, w9
-  0xf9400108,                             //ldr           x8, [x8]
-  0x6f00e400,                             //movi          v0.2d, #0x0
-  0x6f00e401,                             //movi          v1.2d, #0x0
-  0x8b000108,                             //add           x8, x8, x0
-  0x3940010a,                             //ldrb          w10, [x8]
-  0x3940050b,                             //ldrb          w11, [x8, #1]
-  0x3940090c,                             //ldrb          w12, [x8, #2]
-  0x39400d08,                             //ldrb          w8, [x8, #3]
-  0x4e021d43,                             //mov           v3.h[0], w10
-  0x4e061d63,                             //mov           v3.h[1], w11
-  0x4e0a1d83,                             //mov           v3.h[2], w12
-  0x4e0e1d03,                             //mov           v3.h[3], w8
-  0x2f10a463,                             //uxtl          v3.4s, v3.4h
-  0x6e21d863,                             //ucvtf         v3.4s, v3.4s
-  0x6e22dc63,                             //fmul          v3.4s, v3.4s, v2.4s
-  0x6f00e402,                             //movi          v2.2d, #0x0
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_store_a8_aarch64[] = {
-  0xf9400028,                             //ldr           x8, [x1]
-  0x52a86fe9,                             //mov           w9, #0x437f0000
-  0x4e040d30,                             //dup           v16.4s, w9
-  0x6e30dc70,                             //fmul          v16.4s, v3.4s, v16.4s
-  0xf9400108,                             //ldr           x8, [x8]
-  0x6e21aa10,                             //fcvtnu        v16.4s, v16.4s
-  0x0e612a10,                             //xtn           v16.4h, v16.4s
-  0x0e0e3e09,                             //umov          w9, v16.h[3]
-  0x8b000108,                             //add           x8, x8, x0
-  0x39000d09,                             //strb          w9, [x8, #3]
-  0x0e0a3e09,                             //umov          w9, v16.h[2]
-  0x39000909,                             //strb          w9, [x8, #2]
-  0x0e063e09,                             //umov          w9, v16.h[1]
-  0x39000509,                             //strb          w9, [x8, #1]
-  0x0e023e09,                             //umov          w9, v16.h[0]
-  0x39000109,                             //strb          w9, [x8]
-  0xf9400423,                             //ldr           x3, [x1, #8]
-  0x91004021,                             //add           x1, x1, #0x10
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_load_565_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xd37ff809,                             //lsl           x9, x0, #1
-  0x4f072701,                             //movi          v1.4s, #0xf8, lsl #8
-  0x4f0007e3,                             //movi          v3.4s, #0x1f
-  0xf9400108,                             //ldr           x8, [x8]
-  0xfc696900,                             //ldr           d0, [x8, x9]
-  0x321b17e8,                             //orr           w8, wzr, #0x7e0
-  0x4e040d02,                             //dup           v2.4s, w8
-  0x52a6f088,                             //mov           w8, #0x37840000
-  0x72842108,                             //movk          w8, #0x2108
-  0x2f10a400,                             //uxtl          v0.4s, v0.4h
-  0x4e211c01,                             //and           v1.16b, v0.16b, v1.16b
-  0x4e221c02,                             //and           v2.16b, v0.16b, v2.16b
-  0x4e231c03,                             //and           v3.16b, v0.16b, v3.16b
-  0x4e040d00,                             //dup           v0.4s, w8
-  0x52a74048,                             //mov           w8, #0x3a020000
-  0x72810428,                             //movk          w8, #0x821
-  0x4e21d821,                             //scvtf         v1.4s, v1.4s
-  0x6e20dc20,                             //fmul          v0.4s, v1.4s, v0.4s
-  0x4e040d01,                             //dup           v1.4s, w8
-  0x52a7a088,                             //mov           w8, #0x3d040000
-  0x72842108,                             //movk          w8, #0x2108
-  0x4e21d842,                             //scvtf         v2.4s, v2.4s
-  0x6e21dc41,                             //fmul          v1.4s, v2.4s, v1.4s
-  0x4e040d02,                             //dup           v2.4s, w8
-  0x4e21d863,                             //scvtf         v3.4s, v3.4s
-  0x6e22dc62,                             //fmul          v2.4s, v3.4s, v2.4s
-  0x4f03f603,                             //fmov          v3.4s, #1.000000000000000000e+00
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_store_565_aarch64[] = {
-  0xf9400028,                             //ldr           x8, [x1]
-  0x52a84f8a,                             //mov           w10, #0x427c0000
-  0x4f01f7f0,                             //fmov          v16.4s, #3.100000000000000000e+01
-  0x4e040d52,                             //dup           v18.4s, w10
-  0x6e30dc11,                             //fmul          v17.4s, v0.4s, v16.4s
-  0x6e32dc32,                             //fmul          v18.4s, v1.4s, v18.4s
-  0x6e21aa31,                             //fcvtnu        v17.4s, v17.4s
-  0x6e21aa52,                             //fcvtnu        v18.4s, v18.4s
-  0x6e30dc50,                             //fmul          v16.4s, v2.4s, v16.4s
-  0x4f2b5631,                             //shl           v17.4s, v17.4s, #11
-  0xf9400108,                             //ldr           x8, [x8]
-  0x4f255652,                             //shl           v18.4s, v18.4s, #5
-  0x4eb11e51,                             //orr           v17.16b, v18.16b, v17.16b
-  0x6e21aa10,                             //fcvtnu        v16.4s, v16.4s
-  0x4eb01e30,                             //orr           v16.16b, v17.16b, v16.16b
-  0xd37ff809,                             //lsl           x9, x0, #1
-  0x0e612a10,                             //xtn           v16.4h, v16.4s
-  0xfc296910,                             //str           d16, [x8, x9]
-  0xf9400423,                             //ldr           x3, [x1, #8]
-  0x91004021,                             //add           x1, x1, #0x10
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_load_8888_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xd37ef409,                             //lsl           x9, x0, #2
-  0x6f00e621,                             //movi          v1.2d, #0xff000000ff
-  0xf9400108,                             //ldr           x8, [x8]
-  0x3ce96900,                             //ldr           q0, [x8, x9]
-  0x52a77008,                             //mov           w8, #0x3b800000
-  0x72901028,                             //movk          w8, #0x8081
-  0x4e040d02,                             //dup           v2.4s, w8
-  0x6f380410,                             //ushr          v16.4s, v0.4s, #8
-  0x6f300411,                             //ushr          v17.4s, v0.4s, #16
-  0x4e211c03,                             //and           v3.16b, v0.16b, v1.16b
-  0x6f280400,                             //ushr          v0.4s, v0.4s, #24
-  0x4e211e10,                             //and           v16.16b, v16.16b, v1.16b
-  0x4e211e21,                             //and           v1.16b, v17.16b, v1.16b
-  0x4e21d863,                             //scvtf         v3.4s, v3.4s
-  0x4e21d811,                             //scvtf         v17.4s, v0.4s
-  0x4e21da10,                             //scvtf         v16.4s, v16.4s
-  0x4e21d832,                             //scvtf         v18.4s, v1.4s
-  0x6e22dc60,                             //fmul          v0.4s, v3.4s, v2.4s
-  0x6e22de23,                             //fmul          v3.4s, v17.4s, v2.4s
-  0x6e22de01,                             //fmul          v1.4s, v16.4s, v2.4s
-  0x6e22de42,                             //fmul          v2.4s, v18.4s, v2.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_store_8888_aarch64[] = {
-  0x52a86fea,                             //mov           w10, #0x437f0000
-  0x4e040d50,                             //dup           v16.4s, w10
-  0xf9400028,                             //ldr           x8, [x1]
-  0x6e30dc32,                             //fmul          v18.4s, v1.4s, v16.4s
-  0x6e30dc11,                             //fmul          v17.4s, v0.4s, v16.4s
-  0x6e21aa52,                             //fcvtnu        v18.4s, v18.4s
-  0x6e21aa31,                             //fcvtnu        v17.4s, v17.4s
-  0x4f285652,                             //shl           v18.4s, v18.4s, #8
-  0x4eb11e51,                             //orr           v17.16b, v18.16b, v17.16b
-  0x6e30dc52,                             //fmul          v18.4s, v2.4s, v16.4s
-  0x6e30dc70,                             //fmul          v16.4s, v3.4s, v16.4s
-  0x6e21aa52,                             //fcvtnu        v18.4s, v18.4s
-  0xf9400108,                             //ldr           x8, [x8]
-  0x6e21aa10,                             //fcvtnu        v16.4s, v16.4s
-  0x4f305652,                             //shl           v18.4s, v18.4s, #16
-  0x4eb21e31,                             //orr           v17.16b, v17.16b, v18.16b
-  0x4f385610,                             //shl           v16.4s, v16.4s, #24
-  0xd37ef409,                             //lsl           x9, x0, #2
-  0x4eb01e30,                             //orr           v16.16b, v17.16b, v16.16b
-  0x3ca96910,                             //str           q16, [x8, x9]
-  0xf9400423,                             //ldr           x3, [x1, #8]
-  0x91004021,                             //add           x1, x1, #0x10
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_load_f16_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xf9400108,                             //ldr           x8, [x8]
-  0x8b000d08,                             //add           x8, x8, x0, lsl #3
-  0x0c400510,                             //ld4           {v16.4h-v19.4h}, [x8]
-  0x0e217a00,                             //fcvtl         v0.4s, v16.4h
-  0x0e217a21,                             //fcvtl         v1.4s, v17.4h
-  0x0e217a42,                             //fcvtl         v2.4s, v18.4h
-  0x0e217a63,                             //fcvtl         v3.4s, v19.4h
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_store_f16_aarch64[] = {
-  0xf9400028,                             //ldr           x8, [x1]
-  0x0e216810,                             //fcvtn         v16.4h, v0.4s
-  0x0e216831,                             //fcvtn         v17.4h, v1.4s
-  0x0e216852,                             //fcvtn         v18.4h, v2.4s
-  0xf9400108,                             //ldr           x8, [x8]
-  0x0e216873,                             //fcvtn         v19.4h, v3.4s
-  0x8b000d08,                             //add           x8, x8, x0, lsl #3
-  0x0c000510,                             //st4           {v16.4h-v19.4h}, [x8]
-  0xf9400423,                             //ldr           x3, [x1, #8]
-  0x91004021,                             //add           x1, x1, #0x10
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_store_f32_aarch64[] = {
-  0xf9400028,                             //ldr           x8, [x1]
-  0xf9400108,                             //ldr           x8, [x8]
-  0x8b001108,                             //add           x8, x8, x0, lsl #4
-  0x4c000900,                             //st4           {v0.4s-v3.4s}, [x8]
-  0xf9400423,                             //ldr           x3, [x1, #8]
-  0x91004021,                             //add           x1, x1, #0x10
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_clamp_x_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x6f00e411,                             //movi          v17.2d, #0x0
-  0x4e20f620,                             //fmax          v0.4s, v17.4s, v0.4s
-  0x6f07e7f1,                             //movi          v17.2d, #0xffffffffffffffff
-  0x4d40c910,                             //ld1r          {v16.4s}, [x8]
-  0x4eb18610,                             //add           v16.4s, v16.4s, v17.4s
-  0x4eb0f400,                             //fmin          v0.4s, v0.4s, v16.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_clamp_y_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x6f00e411,                             //movi          v17.2d, #0x0
-  0x4e21f621,                             //fmax          v1.4s, v17.4s, v1.4s
-  0x6f07e7f1,                             //movi          v17.2d, #0xffffffffffffffff
-  0x4d40c910,                             //ld1r          {v16.4s}, [x8]
-  0x4eb18610,                             //add           v16.4s, v16.4s, v17.4s
-  0x4eb0f421,                             //fmin          v1.4s, v1.4s, v16.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_repeat_x_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x6f07e7f1,                             //movi          v17.2d, #0xffffffffffffffff
-  0xbd400110,                             //ldr           s16, [x8]
-  0x4e040612,                             //dup           v18.4s, v16.s[0]
-  0x4eb18651,                             //add           v17.4s, v18.4s, v17.4s
-  0x6e32fc12,                             //fdiv          v18.4s, v0.4s, v18.4s
-  0x4e219a52,                             //frintm        v18.4s, v18.4s
-  0x4f905240,                             //fmls          v0.4s, v18.4s, v16.s[0]
-  0x4eb1f400,                             //fmin          v0.4s, v0.4s, v17.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_repeat_y_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0x6f07e7f1,                             //movi          v17.2d, #0xffffffffffffffff
-  0xbd400110,                             //ldr           s16, [x8]
-  0x4e040612,                             //dup           v18.4s, v16.s[0]
-  0x4eb18651,                             //add           v17.4s, v18.4s, v17.4s
-  0x6e32fc32,                             //fdiv          v18.4s, v1.4s, v18.4s
-  0x4e219a52,                             //frintm        v18.4s, v18.4s
-  0x4f905241,                             //fmls          v1.4s, v18.4s, v16.s[0]
-  0x4eb1f421,                             //fmin          v1.4s, v1.4s, v17.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_mirror_x_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xbd400110,                             //ldr           s16, [x8]
-  0x4e040611,                             //dup           v17.4s, v16.s[0]
-  0x1e302a10,                             //fadd          s16, s16, s16
-  0x4eb1d400,                             //fsub          v0.4s, v0.4s, v17.4s
-  0x4e040612,                             //dup           v18.4s, v16.s[0]
-  0x6e32fc12,                             //fdiv          v18.4s, v0.4s, v18.4s
-  0x4e219a52,                             //frintm        v18.4s, v18.4s
-  0x4f905240,                             //fmls          v0.4s, v18.4s, v16.s[0]
-  0x6f07e7f0,                             //movi          v16.2d, #0xffffffffffffffff
-  0x4eb1d400,                             //fsub          v0.4s, v0.4s, v17.4s
-  0x4eb08630,                             //add           v16.4s, v17.4s, v16.4s
-  0x4ea0f800,                             //fabs          v0.4s, v0.4s
-  0x4eb0f400,                             //fmin          v0.4s, v0.4s, v16.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_mirror_y_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xbd400110,                             //ldr           s16, [x8]
-  0x4e040611,                             //dup           v17.4s, v16.s[0]
-  0x1e302a10,                             //fadd          s16, s16, s16
-  0x4eb1d421,                             //fsub          v1.4s, v1.4s, v17.4s
-  0x4e040612,                             //dup           v18.4s, v16.s[0]
-  0x6e32fc32,                             //fdiv          v18.4s, v1.4s, v18.4s
-  0x4e219a52,                             //frintm        v18.4s, v18.4s
-  0x4f905241,                             //fmls          v1.4s, v18.4s, v16.s[0]
-  0x6f07e7f0,                             //movi          v16.2d, #0xffffffffffffffff
-  0x4eb1d421,                             //fsub          v1.4s, v1.4s, v17.4s
-  0x4eb08630,                             //add           v16.4s, v17.4s, v16.4s
-  0x4ea0f821,                             //fabs          v1.4s, v1.4s
-  0x4eb0f421,                             //fmin          v1.4s, v1.4s, v16.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_luminance_to_alpha_aarch64[] = {
-  0x52a7cb28,                             //mov           w8, #0x3e590000
-  0x72967a08,                             //movk          w8, #0xb3d0
-  0x4e040d11,                             //dup           v17.4s, w8
-  0x52a7e6e8,                             //mov           w8, #0x3f370000
-  0x7282eb28,                             //movk          w8, #0x1759
-  0x4ea01c10,                             //mov           v16.16b, v0.16b
-  0x4e040d00,                             //dup           v0.4s, w8
-  0x52a7b268,                             //mov           w8, #0x3d930000
-  0xf8408423,                             //ldr           x3, [x1], #8
-  0x729bb308,                             //movk          w8, #0xdd98
-  0x6e20dc23,                             //fmul          v3.4s, v1.4s, v0.4s
-  0x4e30ce23,                             //fmla          v3.4s, v17.4s, v16.4s
-  0x4e040d10,                             //dup           v16.4s, w8
-  0x6f00e400,                             //movi          v0.2d, #0x0
-  0x6f00e401,                             //movi          v1.2d, #0x0
-  0x4e22ce03,                             //fmla          v3.4s, v16.4s, v2.4s
-  0x6f00e402,                             //movi          v2.2d, #0x0
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_matrix_2x3_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xaa0803e9,                             //mov           x9, x8
-  0x9100410a,                             //add           x10, x8, #0x10
-  0x4ddfc932,                             //ld1r          {v18.4s}, [x9], #4
-  0x4d40c950,                             //ld1r          {v16.4s}, [x10]
-  0x2d415113,                             //ldp           s19, s20, [x8, #8]
-  0x9100510a,                             //add           x10, x8, #0x14
-  0x4d40c951,                             //ld1r          {v17.4s}, [x10]
-  0x4f931030,                             //fmla          v16.4s, v1.4s, v19.s[0]
-  0xbd400133,                             //ldr           s19, [x9]
-  0x4f941031,                             //fmla          v17.4s, v1.4s, v20.s[0]
-  0x4e20ce50,                             //fmla          v16.4s, v18.4s, v0.4s
-  0x4f931011,                             //fmla          v17.4s, v0.4s, v19.s[0]
-  0x4eb01e00,                             //mov           v0.16b, v16.16b
-  0x4eb11e21,                             //mov           v1.16b, v17.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_matrix_3x4_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xaa0803e9,                             //mov           x9, x8
-  0x9100910a,                             //add           x10, x8, #0x24
-  0x4ddfc933,                             //ld1r          {v19.4s}, [x9], #4
-  0x4d40c950,                             //ld1r          {v16.4s}, [x10]
-  0x9100a10a,                             //add           x10, x8, #0x28
-  0x4d40c951,                             //ld1r          {v17.4s}, [x10]
-  0x9100b10a,                             //add           x10, x8, #0x2c
-  0x2d435514,                             //ldp           s20, s21, [x8, #24]
-  0xbd402116,                             //ldr           s22, [x8, #32]
-  0x4d40c952,                             //ld1r          {v18.4s}, [x10]
-  0x4f941050,                             //fmla          v16.4s, v2.4s, v20.s[0]
-  0x4f951051,                             //fmla          v17.4s, v2.4s, v21.s[0]
-  0x4f961052,                             //fmla          v18.4s, v2.4s, v22.s[0]
-  0x2d425502,                             //ldp           s2, s21, [x8, #16]
-  0x2d415d14,                             //ldp           s20, s23, [x8, #8]
-  0x4f821031,                             //fmla          v17.4s, v1.4s, v2.s[0]
-  0xbd400122,                             //ldr           s2, [x9]
-  0x4f971030,                             //fmla          v16.4s, v1.4s, v23.s[0]
-  0x4f951032,                             //fmla          v18.4s, v1.4s, v21.s[0]
-  0x4e20ce70,                             //fmla          v16.4s, v19.4s, v0.4s
-  0x4f941012,                             //fmla          v18.4s, v0.4s, v20.s[0]
-  0x4f821011,                             //fmla          v17.4s, v0.4s, v2.s[0]
-  0x4eb01e00,                             //mov           v0.16b, v16.16b
-  0x4eb11e21,                             //mov           v1.16b, v17.16b
-  0x4eb21e42,                             //mov           v2.16b, v18.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_matrix_4x5_aarch64[] = {
-  0xf9400029,                             //ldr           x9, [x1]
-  0xaa0903e8,                             //mov           x8, x9
-  0x9101012a,                             //add           x10, x9, #0x40
-  0x4ddfc914,                             //ld1r          {v20.4s}, [x8], #4
-  0x4d40c950,                             //ld1r          {v16.4s}, [x10]
-  0x9101112a,                             //add           x10, x9, #0x44
-  0x4d40c951,                             //ld1r          {v17.4s}, [x10]
-  0x9101212a,                             //add           x10, x9, #0x48
-  0x4d40c952,                             //ld1r          {v18.4s}, [x10]
-  0x2d465533,                             //ldp           s19, s21, [x9, #48]
-  0x2d475d36,                             //ldp           s22, s23, [x9, #56]
-  0x9101312a,                             //add           x10, x9, #0x4c
-  0xf9400423,                             //ldr           x3, [x1, #8]
-  0x4f931070,                             //fmla          v16.4s, v3.4s, v19.s[0]
-  0x4d40c953,                             //ld1r          {v19.4s}, [x10]
-  0x4f951071,                             //fmla          v17.4s, v3.4s, v21.s[0]
-  0x4f961072,                             //fmla          v18.4s, v3.4s, v22.s[0]
-  0x2d445935,                             //ldp           s21, s22, [x9, #32]
-  0x4f971073,                             //fmla          v19.4s, v3.4s, v23.s[0]
-  0x2d455d23,                             //ldp           s3, s23, [x9, #40]
-  0x91004021,                             //add           x1, x1, #0x10
-  0x4f951050,                             //fmla          v16.4s, v2.4s, v21.s[0]
-  0x4f961051,                             //fmla          v17.4s, v2.4s, v22.s[0]
-  0x2d425935,                             //ldp           s21, s22, [x9, #16]
-  0x4f971053,                             //fmla          v19.4s, v2.4s, v23.s[0]
-  0x4f831052,                             //fmla          v18.4s, v2.4s, v3.s[0]
-  0x2d410d22,                             //ldp           s2, s3, [x9, #8]
-  0x4f951030,                             //fmla          v16.4s, v1.4s, v21.s[0]
-  0x2d435d35,                             //ldp           s21, s23, [x9, #24]
-  0x4f961031,                             //fmla          v17.4s, v1.4s, v22.s[0]
-  0xbd400116,                             //ldr           s22, [x8]
-  0x4e20ce90,                             //fmla          v16.4s, v20.4s, v0.4s
-  0x4f951032,                             //fmla          v18.4s, v1.4s, v21.s[0]
-  0x4f971033,                             //fmla          v19.4s, v1.4s, v23.s[0]
-  0x4f821012,                             //fmla          v18.4s, v0.4s, v2.s[0]
-  0x4f831013,                             //fmla          v19.4s, v0.4s, v3.s[0]
-  0x4f961011,                             //fmla          v17.4s, v0.4s, v22.s[0]
-  0x4eb01e00,                             //mov           v0.16b, v16.16b
-  0x4eb11e21,                             //mov           v1.16b, v17.16b
-  0x4eb21e42,                             //mov           v2.16b, v18.16b
-  0x4eb31e63,                             //mov           v3.16b, v19.16b
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_matrix_perspective_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xaa0803e9,                             //mov           x9, x8
-  0x9100510a,                             //add           x10, x8, #0x14
-  0x4ddfc930,                             //ld1r          {v16.4s}, [x9], #4
-  0x4d40c951,                             //ld1r          {v17.4s}, [x10]
-  0x9100810a,                             //add           x10, x8, #0x20
-  0x4d40c952,                             //ld1r          {v18.4s}, [x10]
-  0x2d41d113,                             //ldp           s19, s20, [x8, #12]
-  0x2d435915,                             //ldp           s21, s22, [x8, #24]
-  0x91002108,                             //add           x8, x8, #0x8
-  0x4f941031,                             //fmla          v17.4s, v1.4s, v20.s[0]
-  0x4d40c914,                             //ld1r          {v20.4s}, [x8]
-  0x4f961032,                             //fmla          v18.4s, v1.4s, v22.s[0]
-  0xbd400136,                             //ldr           s22, [x9]
-  0x4f951012,                             //fmla          v18.4s, v0.4s, v21.s[0]
-  0x4f931011,                             //fmla          v17.4s, v0.4s, v19.s[0]
-  0x4f961034,                             //fmla          v20.4s, v1.4s, v22.s[0]
-  0x4ea1da41,                             //frecpe        v1.4s, v18.4s
-  0x4e21fe52,                             //frecps        v18.4s, v18.4s, v1.4s
-  0x6e32dc32,                             //fmul          v18.4s, v1.4s, v18.4s
-  0x4e20ce14,                             //fmla          v20.4s, v16.4s, v0.4s
-  0x6e32de21,                             //fmul          v1.4s, v17.4s, v18.4s
-  0x6e32de80,                             //fmul          v0.4s, v20.4s, v18.4s
-  0xd61f0060,                             //br            x3
-};
-
-CODE const uint32_t sk_linear_gradient_2stops_aarch64[] = {
-  0xa8c10c28,                             //ldp           x8, x3, [x1], #16
-  0xad404503,                             //ldp           q3, q17, [x8]
-  0x4e040470,                             //dup           v16.4s, v3.s[0]
-  0x4e0c0461,                             //dup           v1.4s, v3.s[1]
-  0x4e140462,                             //dup           v2.4s, v3.s[2]
-  0x4e1c0463,                             //dup           v3.4s, v3.s[3]
-  0x4f911010,                             //fmla          v16.4s, v0.4s, v17.s[0]
-  0x4fb11001,                             //fmla          v1.4s, v0.4s, v17.s[1]
-  0x4f911802,                             //fmla          v2.4s, v0.4s, v17.s[2]
-  0x4fb11803,                             //fmla          v3.4s, v0.4s, v17.s[3]
-  0x4eb01e00,                             //mov           v0.16b, v16.16b
-  0xd61f0060,                             //br            x3
-};
-#elif defined(__arm__)
-
-CODE const uint32_t sk_start_pipeline_vfp4[] = {
-  0xe92d41f0,                             //push          {r4, r5, r6, r7, r8, lr}
-  0xe1a04000,                             //mov           r4, r0
-  0xe2840002,                             //add           r0, r4, #2
-  0xe1a05003,                             //mov           r5, r3
-  0xe1a08002,                             //mov           r8, r2
-  0xe1a07001,                             //mov           r7, r1
-  0xe1500005,                             //cmp           r0, r5
-  0x8a000010,                             //bhi           64 <sk_start_pipeline_vfp4+0x64>
-  0xe4976004,                             //ldr           r6, [r7], #4
-  0xf2800010,                             //vmov.i32      d0, #0
-  0xe1a00004,                             //mov           r0, r4
-  0xf2801010,                             //vmov.i32      d1, #0
-  0xe1a01007,                             //mov           r1, r7
-  0xf2802010,                             //vmov.i32      d2, #0
-  0xe1a02008,                             //mov           r2, r8
-  0xf2803010,                             //vmov.i32      d3, #0
-  0xf2804010,                             //vmov.i32      d4, #0
-  0xf2805010,                             //vmov.i32      d5, #0
-  0xf2806010,                             //vmov.i32      d6, #0
-  0xf2807010,                             //vmov.i32      d7, #0
-  0xe12fff36,                             //blx           r6
-  0xe2840004,                             //add           r0, r4, #4
-  0xe2844002,                             //add           r4, r4, #2
-  0xe1500005,                             //cmp           r0, r5
-  0x9affffef,                             //bls           24 <sk_start_pipeline_vfp4+0x24>
-  0xe1a00004,                             //mov           r0, r4
-  0xe8bd81f0,                             //pop           {r4, r5, r6, r7, r8, pc}
-};
-
-CODE const uint32_t sk_just_return_vfp4[] = {
-  0xe12fff1e,                             //bx            lr
-};
-
-CODE const uint32_t sk_seed_shader_vfp4[] = {
-  0xee800b90,                             //vdup.32       d16, r0
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf2c3161f,                             //vmov.i32      d17, #1056964608
-  0xedd23b00,                             //vldr          d19, [r2]
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xf2872f10,                             //vmov.f32      d2, #1
-  0xf3fb2622,                             //vcvt.f32.s32  d18, d18
-  0xe2811008,                             //add           r1, r1, #8
-  0xf2400da1,                             //vadd.f32      d16, d16, d17
-  0xf2803010,                             //vmov.i32      d3, #0
-  0xf2804010,                             //vmov.i32      d4, #0
-  0xf2021da1,                             //vadd.f32      d1, d18, d17
-  0xf2000da3,                             //vadd.f32      d0, d16, d19
-  0xf2805010,                             //vmov.i32      d5, #0
-  0xf2806010,                             //vmov.i32      d6, #0
-  0xf2807010,                             //vmov.i32      d7, #0
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_constant_color_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xf4630a0f,                             //vld1.8        {d16-d17}, [r3]
-  0xf3b40c20,                             //vdup.32       d0, d16[0]
-  0xf3bc1c20,                             //vdup.32       d1, d16[1]
-  0xf3b42c21,                             //vdup.32       d2, d17[0]
-  0xf3bc3c21,                             //vdup.32       d3, d17[1]
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_clear_vfp4[] = {
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2800010,                             //vmov.i32      d0, #0
-  0xf2801010,                             //vmov.i32      d1, #0
-  0xf2802010,                             //vmov.i32      d2, #0
-  0xf2803010,                             //vmov.i32      d3, #0
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_plus__vfp4[] = {
-  0xf2000d04,                             //vadd.f32      d0, d0, d4
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2011d05,                             //vadd.f32      d1, d1, d5
-  0xf2022d06,                             //vadd.f32      d2, d2, d6
-  0xf2033d07,                             //vadd.f32      d3, d3, d7
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_srcover_vfp4[] = {
-  0xf2c70f10,                             //vmov.f32      d16, #1
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2600d83,                             //vsub.f32      d16, d16, d3
-  0xf2040c30,                             //vfma.f32      d0, d4, d16
-  0xf2051c30,                             //vfma.f32      d1, d5, d16
-  0xf2062c30,                             //vfma.f32      d2, d6, d16
-  0xf2073c30,                             //vfma.f32      d3, d7, d16
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_dstover_vfp4[] = {
-  0xf2c70f10,                             //vmov.f32      d16, #1
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2651115,                             //vorr          d17, d5, d5
-  0xf2604d87,                             //vsub.f32      d20, d16, d7
-  0xf2640114,                             //vorr          d16, d4, d4
-  0xf2662116,                             //vorr          d18, d6, d6
-  0xf2673117,                             //vorr          d19, d7, d7
-  0xf2400c34,                             //vfma.f32      d16, d0, d20
-  0xf2411c34,                             //vfma.f32      d17, d1, d20
-  0xf2422c34,                             //vfma.f32      d18, d2, d20
-  0xf2433c34,                             //vfma.f32      d19, d3, d20
-  0xf22001b0,                             //vorr          d0, d16, d16
-  0xf22111b1,                             //vorr          d1, d17, d17
-  0xf22221b2,                             //vorr          d2, d18, d18
-  0xf22331b3,                             //vorr          d3, d19, d19
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_clamp_0_vfp4[] = {
-  0xf2c00010,                             //vmov.i32      d16, #0
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2000f20,                             //vmax.f32      d0, d0, d16
-  0xf2011f20,                             //vmax.f32      d1, d1, d16
-  0xf2022f20,                             //vmax.f32      d2, d2, d16
-  0xf2033f20,                             //vmax.f32      d3, d3, d16
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_clamp_1_vfp4[] = {
-  0xf2c70f10,                             //vmov.f32      d16, #1
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2200f20,                             //vmin.f32      d0, d0, d16
-  0xf2211f20,                             //vmin.f32      d1, d1, d16
-  0xf2222f20,                             //vmin.f32      d2, d2, d16
-  0xf2233f20,                             //vmin.f32      d3, d3, d16
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_clamp_a_vfp4[] = {
-  0xf2c70f10,                             //vmov.f32      d16, #1
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf2233f20,                             //vmin.f32      d3, d3, d16
-  0xf2200f03,                             //vmin.f32      d0, d0, d3
-  0xf2211f03,                             //vmin.f32      d1, d1, d3
-  0xf2222f03,                             //vmin.f32      d2, d2, d3
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_set_rgb_vfp4[] = {
-  0xe92d4800,                             //push          {fp, lr}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xe283e008,                             //add           lr, r3, #8
-  0xf4a30c9d,                             //vld1.32       {d0[]}, [r3 :32]!
-  0xf4ae2c9f,                             //vld1.32       {d2[]}, [lr :32]
-  0xf4a31c9f,                             //vld1.32       {d1[]}, [r3 :32]
-  0xe8bd4800,                             //pop           {fp, lr}
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_swap_rb_vfp4[] = {
-  0xeef00b40,                             //vmov.f64      d16, d0
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xeeb00b42,                             //vmov.f64      d0, d2
-  0xeeb02b60,                             //vmov.f64      d2, d16
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_swap_vfp4[] = {
-  0xeef00b43,                             //vmov.f64      d16, d3
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xeef01b42,                             //vmov.f64      d17, d2
-  0xeef02b41,                             //vmov.f64      d18, d1
-  0xeef03b40,                             //vmov.f64      d19, d0
-  0xeeb00b44,                             //vmov.f64      d0, d4
-  0xeeb01b45,                             //vmov.f64      d1, d5
-  0xeeb02b46,                             //vmov.f64      d2, d6
-  0xeeb03b47,                             //vmov.f64      d3, d7
-  0xeeb04b63,                             //vmov.f64      d4, d19
-  0xeeb05b62,                             //vmov.f64      d5, d18
-  0xeeb06b61,                             //vmov.f64      d6, d17
-  0xeeb07b60,                             //vmov.f64      d7, d16
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_move_src_dst_vfp4[] = {
-  0xeeb04b40,                             //vmov.f64      d4, d0
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xeeb05b41,                             //vmov.f64      d5, d1
-  0xeeb06b42,                             //vmov.f64      d6, d2
-  0xeeb07b43,                             //vmov.f64      d7, d3
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_move_dst_src_vfp4[] = {
-  0xeeb00b44,                             //vmov.f64      d0, d4
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xeeb01b45,                             //vmov.f64      d1, d5
-  0xeeb02b46,                             //vmov.f64      d2, d6
-  0xeeb03b47,                             //vmov.f64      d3, d7
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_premul_vfp4[] = {
-  0xf3000d13,                             //vmul.f32      d0, d0, d3
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf3011d13,                             //vmul.f32      d1, d1, d3
-  0xf3022d13,                             //vmul.f32      d2, d2, d3
-  0xe12fff13,                             //bx            r3
-};
-
-CODE const uint32_t sk_unpremul_vfp4[] = {
-  0xed2d8b04,                             //vpush         {d8-d9}
-  0xeeb78a00,                             //vmov.f32      s16, #112
-  0xf3f91503,                             //vceq.f32      d17, d3, #0
-  0xf2c00010,                             //vmov.i32      d16, #0
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xeec89a23,                             //vdiv.f32      s19, s16, s7
-  0xee889a03,                             //vdiv.f32      s18, s16, s6
-  0xf3501199,                             //vbsl          d17, d16, d9
-  0xf3010d90,                             //vmul.f32      d0, d17, d0
-  0xf3011d91,                             //vmul.f32      d1, d17, d1
-  0xf3012d92,                             //vmul.f32      d2, d17, d2
-  0xecbd8b04,                             //vpop          {d8-d9}
-  0xe12fff13,                             //bx            r3
-  0xe320f000,                             //nop           {0}
-};
-
-CODE const uint32_t sk_from_srgb_vfp4[] = {
-  0xeddf3b20,                             //vldr          d19, [pc, #128]
-  0xf3408d10,                             //vmul.f32      d24, d0, d0
-  0xeddf0b1c,                             //vldr          d16, [pc, #112]
-  0xf26341b3,                             //vorr          d20, d19, d19
-  0xf26351b3,                             //vorr          d21, d19, d19
-  0xeddf9b1f,                             //vldr          d25, [pc, #124]
-  0xf2404c30,                             //vfma.f32      d20, d0, d16
-  0xeddf2b1b,                             //vldr          d18, [pc, #108]
-  0xf2415c30,                             //vfma.f32      d21, d1, d16
-  0xeddfcb1d,                             //vldr          d28, [pc, #116]
-  0xf2423c30,                             //vfma.f32      d19, d2, d16
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf3426d12,                             //vmul.f32      d22, d2, d2
-  0xf3417d11,                             //vmul.f32      d23, d1, d1
-  0xf3620e80,                             //vcgt.f32      d16, d18, d0
-  0xf3621e81,                             //vcgt.f32      d17, d18, d1
-  0xf341ad39,                             //vmul.f32      d26, d1, d25
-  0xf342bd39,                             //vmul.f32      d27, d2, d25
-  0xf3622e82,                             //vcgt.f32      d18, d18, d2
-  0xf3409d39,                             //vmul.f32      d25, d0, d25
-  0xf26cd1bc,                             //vorr          d29, d28, d28
-  0xf248dcb4,                             //vfma.f32      d29, d24, d20
-  0xf26c41bc,                             //vorr          d20, d28, d28
-  0xf2474cb5,                             //vfma.f32      d20, d23, d21
-  0xf246ccb3,                             //vfma.f32      d28, d22, d19
-  0xf35901bd,                             //vbsl          d16, d25, d29
-  0xf35a11b4,                             //vbsl          d17, d26, d20
-  0xf35b21bc,                             //vbsl          d18, d27, d28
-  0xf22001b0,                             //vorr          d0, d16, d16
-  0xf22111b1,                             //vorr          d1, d17, d17
-  0xf22221b2,                             //vorr          d2, d18, d18
-  0xe12fff13,                             //bx            r3
-  0x3e99999a,                             //.word         0x3e99999a
-  0x3e99999a,                             //.word         0x3e99999a
-  0x3f328f5c,                             //.word         0x3f328f5c
-  0x3f328f5c,                             //.word         0x3f328f5c
-  0x3d6147ae,                             //.word         0x3d6147ae
-  0x3d6147ae,                             //.word         0x3d6147ae
-  0x3d9e8391,                             //.word         0x3d9e8391
-  0x3d9e8391,                             //.word         0x3d9e8391
-  0x3b23d70a,                             //.word         0x3b23d70a
-  0x3b23d70a,                             //.word         0x3b23d70a
-};
-
-CODE const uint32_t sk_to_srgb_vfp4[] = {
-  0xf3fb0582,                             //vrsqrte.f32   d16, d2
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf3fb1581,                             //vrsqrte.f32   d17, d1
-  0xf3fb2580,                             //vrsqrte.f32   d18, d0
-  0xf3403db0,                             //vmul.f32      d19, d16, d16
-  0xf3414db1,                             //vmul.f32      d20, d17, d17
-  0xf3425db2,                             //vmul.f32      d21, d18, d18
-  0xf2623f33,                             //vrsqrts.f32   d19, d2, d19
-  0xf2614f34,                             //vrsqrts.f32   d20, d1, d20
-  0xf2605f35,                             //vrsqrts.f32   d21, d0, d21
-  0xf3400db3,                             //vmul.f32      d16, d16, d19
-  0xf3411db4,                             //vmul.f32      d17, d17, d20
-  0xf3422db5,                             //vmul.f32      d18, d18, d21
-  0xf3fb3520,                             //vrecpe.f32    d19, d16
-  0xf3fb4521,                             //vrecpe.f32    d20, d17
-  0xf3fb6522,                             //vrecpe.f32    d22, d18
-  0xf3fb55a0,                             //vrsqrte.f32   d21, d16
-  0xf3fb75a1,                             //vrsqrte.f32   d23, d17
-  0xf3fb85a2,                             //vrsqrte.f32   d24, d18
-  0xf2409fb3,                             //vrecps.f32    d25, d16, d19
-  0xf241afb4,                             //vrecps.f32    d26, d17, d20
-  0xf242bfb6,                             //vrecps.f32    d27, d18, d22
-  0xf345cdb5,                             //vmul.f32      d28, d21, d21
-  0xf347ddb7,                             //vmul.f32      d29, d23, d23
-  0xf348edb8,                             //vmul.f32      d30, d24, d24
-  0xf2600fbc,                             //vrsqrts.f32   d16, d16, d28
-  0xf2611fbd,                             //vrsqrts.f32   d17, d17, d29
-  0xf2622fbe,                             //vrsqrts.f32   d18, d18, d30
-  0xf3433db9,                             //vmul.f32      d19, d19, d25
-  0xeddf9b21,                             //vldr          d25, [pc, #132]
-  0xf3444dba,                             //vmul.f32      d20, d20, d26
-  0xeddfab21,                             //vldr          d26, [pc, #132]
-  0xf3466dbb,                             //vmul.f32      d22, d22, d27
-  0xf26ab1ba,                             //vorr          d27, d26, d26
-  0xf243bcb9,                             //vfma.f32      d27, d19, d25
-  0xf26a31ba,                             //vorr          d19, d26, d26
-  0xf2443cb9,                             //vfma.f32      d19, d20, d25
-  0xeddf4b1d,                             //vldr          d20, [pc, #116]
-  0xf246acb9,                             //vfma.f32      d26, d22, d25
-  0xf3450db0,                             //vmul.f32      d16, d21, d16
-  0xeddf5b1c,                             //vldr          d21, [pc, #112]
-  0xf3471db1,                             //vmul.f32      d17, d23, d17
-  0xf3482db2,                             //vmul.f32      d18, d24, d18
-  0xf3406d35,                             //vmul.f32      d22, d0, d21
-  0xf240bcb4,                             //vfma.f32      d27, d16, d20
-  0xf2413cb4,                             //vfma.f32      d19, d17, d20
-  0xf242acb4,                             //vfma.f32      d26, d18, d20
-  0xeddf2b17,                             //vldr          d18, [pc, #92]
-  0xf3417d35,                             //vmul.f32      d23, d1, d21
-  0xf3620e80,                             //vcgt.f32      d16, d18, d0
-  0xf3621e81,                             //vcgt.f32      d17, d18, d1
-  0xf3622e82,                             //vcgt.f32      d18, d18, d2
-  0xf3425d35,                             //vmul.f32      d21, d2, d21
-  0xf2c74f10,                             //vmov.f32      d20, #1
-  0xf2648faa,                             //vmin.f32      d24, d20, d26
-  0xf2643fa3,                             //vmin.f32      d19, d20, d19
-  0xf2644fab,                             //vmin.f32      d20, d20, d27
-  0xf35601b8,                             //vbsl          d16, d22, d24
-  0xf35711b3,                             //vbsl          d17, d23, d19
-  0xf35521b4,                             //vbsl          d18, d21, d20
-  0xf22001b0,                             //vorr          d0, d16, d16
-  0xf22111b1,                             //vorr          d1, d17, d17
-  0xf22221b2,                             //vorr          d2, d18, d18
-  0xe12fff13,                             //bx            r3
-  0x3f306fce,                             //.word         0x3f306fce
-  0x3f306fce,                             //.word         0x3f306fce
-  0xbdca57a8,                             //.word         0xbdca57a8
-  0xbdca57a8,                             //.word         0xbdca57a8
-  0x3ed287c2,                             //.word         0x3ed287c2
-  0x3ed287c2,                             //.word         0x3ed287c2
-  0x41475c29,                             //.word         0x41475c29
-  0x41475c29,                             //.word         0x41475c29
-  0x3b8ce704,                             //.word         0x3b8ce704
-  0x3b8ce704,                             //.word         0x3b8ce704
-};
-
-CODE const uint32_t sk_scale_1_float_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xf4e30c9f,                             //vld1.32       {d16[]}, [r3 :32]
-  0xf3000d90,                             //vmul.f32      d0, d16, d0
-  0xf3001d91,                             //vmul.f32      d1, d16, d1
-  0xf3002d92,                             //vmul.f32      d2, d16, d2
-  0xf3003d93,                             //vmul.f32      d3, d16, d3
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_scale_u8_vfp4[] = {
-  0xe24dd004,                             //sub           sp, sp, #4
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xe5933000,                             //ldr           r3, [r3]
-  0xe0833000,                             //add           r3, r3, r0
-  0xe1d330b0,                             //ldrh          r3, [r3]
-  0xe1cd30b0,                             //strh          r3, [sp]
-  0xe1a0300d,                             //mov           r3, sp
-  0xf4e3041f,                             //vld1.16       {d16[0]}, [r3 :16]
-  0xf3c80a30,                             //vmovl.u8      q8, d16
-  0xf3d00a30,                             //vmovl.u16     q8, d16
-  0xf3fb06a0,                             //vcvt.f32.u32  d16, d16
-  0xeddf1b06,                             //vldr          d17, [pc, #24]
-  0xf3400db1,                             //vmul.f32      d16, d16, d17
-  0xf3000d90,                             //vmul.f32      d0, d16, d0
-  0xf3001d91,                             //vmul.f32      d1, d16, d1
-  0xf3002d92,                             //vmul.f32      d2, d16, d2
-  0xf3003d93,                             //vmul.f32      d3, d16, d3
-  0xe28dd004,                             //add           sp, sp, #4
-  0xe12fff1c,                             //bx            ip
-  0x3b808081,                             //.word         0x3b808081
-  0x3b808081,                             //.word         0x3b808081
-};
-
-CODE const uint32_t sk_lerp_1_float_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2600d04,                             //vsub.f32      d16, d0, d4
-  0xf2611d05,                             //vsub.f32      d17, d1, d5
-  0xf2622d06,                             //vsub.f32      d18, d2, d6
-  0xe2811008,                             //add           r1, r1, #8
-  0xf2633d07,                             //vsub.f32      d19, d3, d7
-  0xf4e34c9f,                             //vld1.32       {d20[]}, [r3 :32]
-  0xf2240114,                             //vorr          d0, d4, d4
-  0xf2251115,                             //vorr          d1, d5, d5
-  0xf2262116,                             //vorr          d2, d6, d6
-  0xf2273117,                             //vorr          d3, d7, d7
-  0xf2000cb4,                             //vfma.f32      d0, d16, d20
-  0xf2011cb4,                             //vfma.f32      d1, d17, d20
-  0xf2022cb4,                             //vfma.f32      d2, d18, d20
-  0xf2033cb4,                             //vfma.f32      d3, d19, d20
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_lerp_u8_vfp4[] = {
-  0xe24dd004,                             //sub           sp, sp, #4
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2602d04,                             //vsub.f32      d18, d0, d4
-  0xf2623d06,                             //vsub.f32      d19, d2, d6
-  0xf2634d07,                             //vsub.f32      d20, d3, d7
-  0xe2811008,                             //add           r1, r1, #8
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf2240114,                             //vorr          d0, d4, d4
-  0xf2262116,                             //vorr          d2, d6, d6
-  0xe0833000,                             //add           r3, r3, r0
-  0xf2273117,                             //vorr          d3, d7, d7
-  0xe1d330b0,                             //ldrh          r3, [r3]
-  0xe1cd30b0,                             //strh          r3, [sp]
-  0xe1a0300d,                             //mov           r3, sp
-  0xf4e3041f,                             //vld1.16       {d16[0]}, [r3 :16]
-  0xf3c80a30,                             //vmovl.u8      q8, d16
-  0xf3d00a30,                             //vmovl.u16     q8, d16
-  0xf3fb06a0,                             //vcvt.f32.u32  d16, d16
-  0xeddf1b08,                             //vldr          d17, [pc, #32]
-  0xf3400db1,                             //vmul.f32      d16, d16, d17
-  0xf2611d05,                             //vsub.f32      d17, d1, d5
-  0xf2251115,                             //vorr          d1, d5, d5
-  0xf2020cb0,                             //vfma.f32      d0, d18, d16
-  0xf2011cb0,                             //vfma.f32      d1, d17, d16
-  0xf2032cb0,                             //vfma.f32      d2, d19, d16
-  0xf2043cb0,                             //vfma.f32      d3, d20, d16
-  0xe28dd004,                             //add           sp, sp, #4
-  0xe12fff1c,                             //bx            ip
-  0x3b808081,                             //.word         0x3b808081
-  0x3b808081,                             //.word         0x3b808081
-};
-
-CODE const uint32_t sk_lerp_565_vfp4[] = {
-  0xe24dd004,                             //sub           sp, sp, #4
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf3c72218,                             //vmov.i32      d18, #63488
-  0xf2c1101f,                             //vmov.i32      d17, #31
-  0xf2603d04,                             //vsub.f32      d19, d0, d4
-  0xe2811008,                             //add           r1, r1, #8
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf2616d05,                             //vsub.f32      d22, d1, d5
-  0xf2240114,                             //vorr          d0, d4, d4
-  0xf2251115,                             //vorr          d1, d5, d5
-  0xe7933080,                             //ldr           r3, [r3, r0, lsl #1]
-  0xf2873f10,                             //vmov.f32      d3, #1
-  0xe58d3000,                             //str           r3, [sp]
-  0xe1a0300d,                             //mov           r3, sp
-  0xf4e3083f,                             //vld1.32       {d16[0]}, [r3 :32]
-  0xe3a03e7e,                             //mov           r3, #2016
-  0xf3d04a30,                             //vmovl.u16     q10, d16
-  0xee803b90,                             //vdup.32       d16, r3
-  0xf24421b2,                             //vand          d18, d20, d18
-  0xf24411b1,                             //vand          d17, d20, d17
-  0xeddf5b12,                             //vldr          d21, [pc, #72]
-  0xf24401b0,                             //vand          d16, d20, d16
-  0xeddf4b0e,                             //vldr          d20, [pc, #56]
-  0xf3fb2622,                             //vcvt.f32.s32  d18, d18
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf3fb1621,                             //vcvt.f32.s32  d17, d17
-  0xf3422db4,                             //vmul.f32      d18, d18, d20
-  0xeddf4b0d,                             //vldr          d20, [pc, #52]
-  0xf3400db5,                             //vmul.f32      d16, d16, d21
-  0xf2625d06,                             //vsub.f32      d21, d2, d6
-  0xf3411db4,                             //vmul.f32      d17, d17, d20
-  0xf2262116,                             //vorr          d2, d6, d6
-  0xf2030cb2,                             //vfma.f32      d0, d19, d18
-  0xf2061cb0,                             //vfma.f32      d1, d22, d16
-  0xf2052cb1,                             //vfma.f32      d2, d21, d17
-  0xe28dd004,                             //add           sp, sp, #4
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x37842108,                             //.word         0x37842108
-  0x37842108,                             //.word         0x37842108
-  0x3a020821,                             //.word         0x3a020821
-  0x3a020821,                             //.word         0x3a020821
-  0x3d042108,                             //.word         0x3d042108
-  0x3d042108,                             //.word         0x3d042108
-};
-
-CODE const uint32_t sk_load_tables_vfp4[] = {
-  0xe92d48f0,                             //push          {r4, r5, r6, r7, fp, lr}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf3c7001f,                             //vmov.i32      d16, #255
-  0xe2811008,                             //add           r1, r1, #8
-  0xe593e000,                             //ldr           lr, [r3]
-  0xe99300b0,                             //ldmib         r3, {r4, r5, r7}
-  0xe08e3100,                             //add           r3, lr, r0, lsl #2
-  0xedd31b00,                             //vldr          d17, [r3]
-  0xf24121b0,                             //vand          d18, d17, d16
-  0xf3f83031,                             //vshr.u32      d19, d17, #8
-  0xee323b90,                             //vmov.32       r3, d18[1]
-  0xee126b90,                             //vmov.32       r6, d18[0]
-  0xf3f02031,                             //vshr.u32      d18, d17, #16
-  0xf24221b0,                             //vand          d18, d18, d16
-  0xf24301b0,                             //vand          d16, d19, d16
-  0xe0843103,                             //add           r3, r4, r3, lsl #2
-  0xedd30a00,                             //vldr          s1, [r3]
-  0xe0843106,                             //add           r3, r4, r6, lsl #2
-  0xee326b90,                             //vmov.32       r6, d18[1]
-  0xed930a00,                             //vldr          s0, [r3]
-  0xee303b90,                             //vmov.32       r3, d16[1]
-  0xee104b90,                             //vmov.32       r4, d16[0]
-  0xf3e80031,                             //vshr.u32      d16, d17, #24
-  0xeddf1b0d,                             //vldr          d17, [pc, #52]
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf3003db1,                             //vmul.f32      d3, d16, d17
-  0xe087e106,                             //add           lr, r7, r6, lsl #2
-  0xee126b90,                             //vmov.32       r6, d18[0]
-  0xe0853103,                             //add           r3, r5, r3, lsl #2
-  0xedde2a00,                             //vldr          s5, [lr]
-  0xedd31a00,                             //vldr          s3, [r3]
-  0xe0853104,                             //add           r3, r5, r4, lsl #2
-  0xed931a00,                             //vldr          s2, [r3]
-  0xe0873106,                             //add           r3, r7, r6, lsl #2
-  0xed932a00,                             //vldr          s4, [r3]
-  0xe8bd48f0,                             //pop           {r4, r5, r6, r7, fp, lr}
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x3b808081,                             //.word         0x3b808081
-  0x3b808081,                             //.word         0x3b808081
-};
-
-CODE const uint32_t sk_load_a8_vfp4[] = {
-  0xe24dd004,                             //sub           sp, sp, #4
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xf2800010,                             //vmov.i32      d0, #0
-  0xf2801010,                             //vmov.i32      d1, #0
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf2802010,                             //vmov.i32      d2, #0
-  0xe0833000,                             //add           r3, r3, r0
-  0xe1d330b0,                             //ldrh          r3, [r3]
-  0xe1cd30b0,                             //strh          r3, [sp]
-  0xe1a0300d,                             //mov           r3, sp
-  0xf4e3041f,                             //vld1.16       {d16[0]}, [r3 :16]
-  0xf3c80a30,                             //vmovl.u8      q8, d16
-  0xf3d00a30,                             //vmovl.u16     q8, d16
-  0xf3fb06a0,                             //vcvt.f32.u32  d16, d16
-  0xeddf1b03,                             //vldr          d17, [pc, #12]
-  0xf3003db1,                             //vmul.f32      d3, d16, d17
-  0xe28dd004,                             //add           sp, sp, #4
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x3b808081,                             //.word         0x3b808081
-  0x3b808081,                             //.word         0x3b808081
-};
-
-CODE const uint32_t sk_store_a8_vfp4[] = {
-  0xe92d4800,                             //push          {fp, lr}
-  0xeddf0b0d,                             //vldr          d16, [pc, #52]
-  0xf2c3161f,                             //vmov.i32      d17, #1056964608
-  0xf2431c30,                             //vfma.f32      d17, d3, d16
-  0xe5913000,                             //ldr           r3, [r1]
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf3fb07a1,                             //vcvt.u32.f32  d16, d17
-  0xee10eb90,                             //vmov.32       lr, d16[0]
-  0xee30cb90,                             //vmov.32       ip, d16[1]
-  0xe7e3e000,                             //strb          lr, [r3, r0]!
-  0xe5c3c001,                             //strb          ip, [r3, #1]
-  0xe2813008,                             //add           r3, r1, #8
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe1a01003,                             //mov           r1, r3
-  0xe8bd4800,                             //pop           {fp, lr}
-  0xe12fff1c,                             //bx            ip
-  0x437f0000,                             //.word         0x437f0000
-  0x437f0000,                             //.word         0x437f0000
-};
-
-CODE const uint32_t sk_load_565_vfp4[] = {
-  0xe24dd004,                             //sub           sp, sp, #4
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c1101f,                             //vmov.i32      d17, #31
-  0xf3c72218,                             //vmov.i32      d18, #63488
-  0xeddf3b16,                             //vldr          d19, [pc, #88]
-  0xe2811008,                             //add           r1, r1, #8
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf2873f10,                             //vmov.f32      d3, #1
-  0xe7933080,                             //ldr           r3, [r3, r0, lsl #1]
-  0xe58d3000,                             //str           r3, [sp]
-  0xe1a0300d,                             //mov           r3, sp
-  0xf4e3083f,                             //vld1.32       {d16[0]}, [r3 :32]
-  0xe3a03e7e,                             //mov           r3, #2016
-  0xf3d04a30,                             //vmovl.u16     q10, d16
-  0xee803b90,                             //vdup.32       d16, r3
-  0xf24411b1,                             //vand          d17, d20, d17
-  0xeddf5b0e,                             //vldr          d21, [pc, #56]
-  0xf24421b2,                             //vand          d18, d20, d18
-  0xf24401b0,                             //vand          d16, d20, d16
-  0xeddf4b09,                             //vldr          d20, [pc, #36]
-  0xf3fb2622,                             //vcvt.f32.s32  d18, d18
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf3fb1621,                             //vcvt.f32.s32  d17, d17
-  0xf3020db3,                             //vmul.f32      d0, d18, d19
-  0xf3001db4,                             //vmul.f32      d1, d16, d20
-  0xf3012db5,                             //vmul.f32      d2, d17, d21
-  0xe28dd004,                             //add           sp, sp, #4
-  0xe12fff1c,                             //bx            ip
-  0x37842108,                             //.word         0x37842108
-  0x37842108,                             //.word         0x37842108
-  0x3a020821,                             //.word         0x3a020821
-  0x3a020821,                             //.word         0x3a020821
-  0x3d042108,                             //.word         0x3d042108
-  0x3d042108,                             //.word         0x3d042108
-};
-
-CODE const uint32_t sk_store_565_vfp4[] = {
-  0xf2c30f1f,                             //vmov.f32      d16, #31
-  0xeddf1b15,                             //vldr          d17, [pc, #84]
-  0xf2c3361f,                             //vmov.i32      d19, #1056964608
-  0xe5913000,                             //ldr           r3, [r1]
-  0xf2413c31,                             //vfma.f32      d19, d1, d17
-  0xf2c3161f,                             //vmov.i32      d17, #1056964608
-  0xf2401c30,                             //vfma.f32      d17, d0, d16
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf2c3261f,                             //vmov.i32      d18, #1056964608
-  0xf2422c30,                             //vfma.f32      d18, d2, d16
-  0xe0833080,                             //add           r3, r3, r0, lsl #1
-  0xf3fb07a3,                             //vcvt.u32.f32  d16, d19
-  0xf3fb17a1,                             //vcvt.u32.f32  d17, d17
-  0xf3fb27a2,                             //vcvt.u32.f32  d18, d18
-  0xf2e50530,                             //vshl.s32      d16, d16, #5
-  0xf2eb1531,                             //vshl.s32      d17, d17, #11
-  0xf26001b1,                             //vorr          d16, d16, d17
-  0xf26001b2,                             //vorr          d16, d16, d18
-  0xf3f60121,                             //vuzp.16       d16, d17
-  0xf4c3080f,                             //vst1.32       {d16[0]}, [r3]
-  0xe2813008,                             //add           r3, r1, #8
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe1a01003,                             //mov           r1, r3
-  0xe12fff1c,                             //bx            ip
-  0x427c0000,                             //.word         0x427c0000
-  0x427c0000,                             //.word         0x427c0000
-};
-
-CODE const uint32_t sk_load_8888_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf3c7001f,                             //vmov.i32      d16, #255
-  0xe2811008,                             //add           r1, r1, #8
-  0xe5933000,                             //ldr           r3, [r3]
-  0xe0833100,                             //add           r3, r3, r0, lsl #2
-  0xedd31b00,                             //vldr          d17, [r3]
-  0xf24121b0,                             //vand          d18, d17, d16
-  0xf3f83031,                             //vshr.u32      d19, d17, #8
-  0xf3e84031,                             //vshr.u32      d20, d17, #24
-  0xf3f01031,                             //vshr.u32      d17, d17, #16
-  0xf24331b0,                             //vand          d19, d19, d16
-  0xf24101b0,                             //vand          d16, d17, d16
-  0xeddf1b08,                             //vldr          d17, [pc, #32]
-  0xf3fb2622,                             //vcvt.f32.s32  d18, d18
-  0xf3fb4624,                             //vcvt.f32.s32  d20, d20
-  0xf3fb3623,                             //vcvt.f32.s32  d19, d19
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf3020db1,                             //vmul.f32      d0, d18, d17
-  0xf3043db1,                             //vmul.f32      d3, d20, d17
-  0xf3031db1,                             //vmul.f32      d1, d19, d17
-  0xf3002db1,                             //vmul.f32      d2, d16, d17
-  0xe12fff1c,                             //bx            ip
-  0x3b808081,                             //.word         0x3b808081
-  0x3b808081,                             //.word         0x3b808081
-};
-
-CODE const uint32_t sk_store_8888_vfp4[] = {
-  0xeddf0b1a,                             //vldr          d16, [pc, #104]
-  0xf2c3261f,                             //vmov.i32      d18, #1056964608
-  0xf2412c30,                             //vfma.f32      d18, d1, d16
-  0xe5913000,                             //ldr           r3, [r1]
-  0xf2c3361f,                             //vmov.i32      d19, #1056964608
-  0xf2c3161f,                             //vmov.i32      d17, #1056964608
-  0xf2423c30,                             //vfma.f32      d19, d2, d16
-  0xe5933000,                             //ldr           r3, [r3]
-  0xf2c3461f,                             //vmov.i32      d20, #1056964608
-  0xf2401c30,                             //vfma.f32      d17, d0, d16
-  0xe0833100,                             //add           r3, r3, r0, lsl #2
-  0xf2434c30,                             //vfma.f32      d20, d3, d16
-  0xf3fb07a2,                             //vcvt.u32.f32  d16, d18
-  0xf3fb27a3,                             //vcvt.u32.f32  d18, d19
-  0xf3fb17a1,                             //vcvt.u32.f32  d17, d17
-  0xf3fb37a4,                             //vcvt.u32.f32  d19, d20
-  0xf2e80530,                             //vshl.s32      d16, d16, #8
-  0xf2f02532,                             //vshl.s32      d18, d18, #16
-  0xf26001b1,                             //vorr          d16, d16, d17
-  0xf2f81533,                             //vshl.s32      d17, d19, #24
-  0xf26001b2,                             //vorr          d16, d16, d18
-  0xf26001b1,                             //vorr          d16, d16, d17
-  0xedc30b00,                             //vstr          d16, [r3]
-  0xe2813008,                             //add           r3, r1, #8
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe1a01003,                             //mov           r1, r3
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x437f0000,                             //.word         0x437f0000
-  0x437f0000,                             //.word         0x437f0000
-};
-
-CODE const uint32_t sk_load_f16_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xe5933000,                             //ldr           r3, [r3]
-  0xe0833180,                             //add           r3, r3, r0, lsl #3
-  0xf463084f,                             //vld2.16       {d16-d17}, [r3]
-  0xf3f62720,                             //vcvt.f32.f16  q9, d16
-  0xf3f60721,                             //vcvt.f32.f16  q8, d17
-  0xf22201b2,                             //vorr          d0, d18, d18
-  0xf22011b0,                             //vorr          d1, d16, d16
-  0xf3ba00a3,                             //vtrn.32       d0, d19
-  0xf22321b3,                             //vorr          d2, d19, d19
-  0xf3ba10a1,                             //vtrn.32       d1, d17
-  0xf22131b1,                             //vorr          d3, d17, d17
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_store_f16_vfp4[] = {
-  0xeef00b41,                             //vmov.f64      d16, d1
-  0xeef03b42,                             //vmov.f64      d19, d2
-  0xf2631113,                             //vorr          d17, d3, d3
-  0xf2602110,                             //vorr          d18, d0, d0
-  0xf3fa00a1,                             //vtrn.32       d16, d17
-  0xf3f61620,                             //vcvt.f16.f32  d17, q8
-  0xf3fa20a3,                             //vtrn.32       d18, d19
-  0xe5913000,                             //ldr           r3, [r1]
-  0xf3f60622,                             //vcvt.f16.f32  d16, q9
-  0xe5933000,                             //ldr           r3, [r3]
-  0xe0833180,                             //add           r3, r3, r0, lsl #3
-  0xf443084f,                             //vst2.16       {d16-d17}, [r3]
-  0xe2813008,                             //add           r3, r1, #8
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe1a01003,                             //mov           r1, r3
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_store_f32_vfp4[] = {
-  0xe5913000,                             //ldr           r3, [r1]
-  0xe5933000,                             //ldr           r3, [r3]
-  0xe0833200,                             //add           r3, r3, r0, lsl #4
-  0xf403008f,                             //vst4.32       {d0-d3}, [r3]
-  0xe2813008,                             //add           r3, r1, #8
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe1a01003,                             //mov           r1, r3
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_clamp_x_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c00010,                             //vmov.i32      d16, #0
-  0xf3c71e1f,                             //vmov.i8       d17, #255
-  0xf2400f80,                             //vmax.f32      d16, d16, d0
-  0xe2811008,                             //add           r1, r1, #8
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xf26218a1,                             //vadd.i32      d17, d18, d17
-  0xf2200fa1,                             //vmin.f32      d0, d16, d17
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_clamp_y_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c00010,                             //vmov.i32      d16, #0
-  0xf3c71e1f,                             //vmov.i8       d17, #255
-  0xf2400f81,                             //vmax.f32      d16, d16, d1
-  0xe2811008,                             //add           r1, r1, #8
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xf26218a1,                             //vadd.i32      d17, d18, d17
-  0xf2201fa1,                             //vmin.f32      d1, d16, d17
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_repeat_x_vfp4[] = {
-  0xed2d8b04,                             //vpush         {d8-d9}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c02010,                             //vmov.i32      d18, #0
-  0xe2811008,                             //add           r1, r1, #8
-  0xeddf3b10,                             //vldr          d19, [pc, #64]
-  0xed938a00,                             //vldr          s16, [r3]
-  0xeec09a88,                             //vdiv.f32      s19, s1, s16
-  0xee809a08,                             //vdiv.f32      s18, s0, s16
-  0xf3fb0709,                             //vcvt.s32.f32  d16, d9
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf3601e89,                             //vcgt.f32      d17, d16, d9
-  0xf35311b2,                             //vbsl          d17, d19, d18
-  0xf3f42c08,                             //vdup.32       d18, d8[0]
-  0xf2600da1,                             //vsub.f32      d16, d16, d17
-  0xf3c71e1f,                             //vmov.i8       d17, #255
-  0xf26218a1,                             //vadd.i32      d17, d18, d17
-  0xf2e009c8,                             //vmul.f32      d16, d16, d8[0]
-  0xf2600d20,                             //vsub.f32      d16, d0, d16
-  0xf2200fa1,                             //vmin.f32      d0, d16, d17
-  0xecbd8b04,                             //vpop          {d8-d9}
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x3f800000,                             //.word         0x3f800000
-  0x3f800000,                             //.word         0x3f800000
-};
-
-CODE const uint32_t sk_repeat_y_vfp4[] = {
-  0xed2d8b04,                             //vpush         {d8-d9}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c02010,                             //vmov.i32      d18, #0
-  0xe2811008,                             //add           r1, r1, #8
-  0xeddf3b10,                             //vldr          d19, [pc, #64]
-  0xed938a00,                             //vldr          s16, [r3]
-  0xeec19a88,                             //vdiv.f32      s19, s3, s16
-  0xee819a08,                             //vdiv.f32      s18, s2, s16
-  0xf3fb0709,                             //vcvt.s32.f32  d16, d9
-  0xf3fb0620,                             //vcvt.f32.s32  d16, d16
-  0xf3601e89,                             //vcgt.f32      d17, d16, d9
-  0xf35311b2,                             //vbsl          d17, d19, d18
-  0xf3f42c08,                             //vdup.32       d18, d8[0]
-  0xf2600da1,                             //vsub.f32      d16, d16, d17
-  0xf3c71e1f,                             //vmov.i8       d17, #255
-  0xf26218a1,                             //vadd.i32      d17, d18, d17
-  0xf2e009c8,                             //vmul.f32      d16, d16, d8[0]
-  0xf2610d20,                             //vsub.f32      d16, d1, d16
-  0xf2201fa1,                             //vmin.f32      d1, d16, d17
-  0xecbd8b04,                             //vpop          {d8-d9}
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x3f800000,                             //.word         0x3f800000
-  0x3f800000,                             //.word         0x3f800000
-};
-
-CODE const uint32_t sk_mirror_x_vfp4[] = {
-  0xed2d8b04,                             //vpush         {d8-d9}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c03010,                             //vmov.i32      d19, #0
-  0xe2811008,                             //add           r1, r1, #8
-  0xeddf4b14,                             //vldr          d20, [pc, #80]
-  0xed938a00,                             //vldr          s16, [r3]
-  0xee389a08,                             //vadd.f32      s18, s16, s16
-  0xf3f40c08,                             //vdup.32       d16, d8[0]
-  0xf2200d20,                             //vsub.f32      d0, d0, d16
-  0xeec08a89,                             //vdiv.f32      s17, s1, s18
-  0xee808a09,                             //vdiv.f32      s16, s0, s18
-  0xf3fb1708,                             //vcvt.s32.f32  d17, d8
-  0xf3fb1621,                             //vcvt.f32.s32  d17, d17
-  0xf3612e88,                             //vcgt.f32      d18, d17, d8
-  0xf35421b3,                             //vbsl          d18, d20, d19
-  0xf2611da2,                             //vsub.f32      d17, d17, d18
-  0xf3c72e1f,                             //vmov.i8       d18, #255
-  0xf2e119c9,                             //vmul.f32      d17, d17, d9[0]
-  0xf2601d21,                             //vsub.f32      d17, d0, d17
-  0xf2611da0,                             //vsub.f32      d17, d17, d16
-  0xf26008a2,                             //vadd.i32      d16, d16, d18
-  0xf3f91721,                             //vabs.f32      d17, d17
-  0xf2210fa0,                             //vmin.f32      d0, d17, d16
-  0xecbd8b04,                             //vpop          {d8-d9}
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x3f800000,                             //.word         0x3f800000
-  0x3f800000,                             //.word         0x3f800000
-};
-
-CODE const uint32_t sk_mirror_y_vfp4[] = {
-  0xed2d8b04,                             //vpush         {d8-d9}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2c03010,                             //vmov.i32      d19, #0
-  0xe2811008,                             //add           r1, r1, #8
-  0xeddf4b14,                             //vldr          d20, [pc, #80]
-  0xed938a00,                             //vldr          s16, [r3]
-  0xee389a08,                             //vadd.f32      s18, s16, s16
-  0xf3f40c08,                             //vdup.32       d16, d8[0]
-  0xf2211d20,                             //vsub.f32      d1, d1, d16
-  0xeec18a89,                             //vdiv.f32      s17, s3, s18
-  0xee818a09,                             //vdiv.f32      s16, s2, s18
-  0xf3fb1708,                             //vcvt.s32.f32  d17, d8
-  0xf3fb1621,                             //vcvt.f32.s32  d17, d17
-  0xf3612e88,                             //vcgt.f32      d18, d17, d8
-  0xf35421b3,                             //vbsl          d18, d20, d19
-  0xf2611da2,                             //vsub.f32      d17, d17, d18
-  0xf3c72e1f,                             //vmov.i8       d18, #255
-  0xf2e119c9,                             //vmul.f32      d17, d17, d9[0]
-  0xf2611d21,                             //vsub.f32      d17, d1, d17
-  0xf2611da0,                             //vsub.f32      d17, d17, d16
-  0xf26008a2,                             //vadd.i32      d16, d16, d18
-  0xf3f91721,                             //vabs.f32      d17, d17
-  0xf2211fa0,                             //vmin.f32      d1, d17, d16
-  0xecbd8b04,                             //vpop          {d8-d9}
-  0xe12fff1c,                             //bx            ip
-  0xe320f000,                             //nop           {0}
-  0x3f800000,                             //.word         0x3f800000
-  0x3f800000,                             //.word         0x3f800000
-};
-
-CODE const uint32_t sk_luminance_to_alpha_vfp4[] = {
-  0xeddf0b0a,                             //vldr          d16, [pc, #40]
-  0xeddf1b0b,                             //vldr          d17, [pc, #44]
-  0xf3410d30,                             //vmul.f32      d16, d1, d16
-  0xe4913004,                             //ldr           r3, [r1], #4
-  0xf3401d31,                             //vmul.f32      d17, d0, d17
-  0xf2800010,                             //vmov.i32      d0, #0
-  0xf2801010,                             //vmov.i32      d1, #0
-  0xf2013da0,                             //vadd.f32      d3, d17, d16
-  0xeddf0b06,                             //vldr          d16, [pc, #24]
-  0xf2023c30,                             //vfma.f32      d3, d2, d16
-  0xf2802010,                             //vmov.i32      d2, #0
-  0xe12fff13,                             //bx            r3
-  0x3f371759,                             //.word         0x3f371759
-  0x3f371759,                             //.word         0x3f371759
-  0x3e59b3d0,                             //.word         0x3e59b3d0
-  0x3e59b3d0,                             //.word         0x3e59b3d0
-  0x3d93dd98,                             //.word         0x3d93dd98
-  0x3d93dd98,                             //.word         0x3d93dd98
-};
-
-CODE const uint32_t sk_matrix_2x3_vfp4[] = {
-  0xe92d4800,                             //push          {fp, lr}
-  0xe591e000,                             //ldr           lr, [r1]
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe2811008,                             //add           r1, r1, #8
-  0xe28e300c,                             //add           r3, lr, #12
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xe28e3008,                             //add           r3, lr, #8
-  0xf4e31c9f,                             //vld1.32       {d17[]}, [r3 :32]
-  0xe28e3010,                             //add           r3, lr, #16
-  0xf4e30c9f,                             //vld1.32       {d16[]}, [r3 :32]
-  0xe28e3014,                             //add           r3, lr, #20
-  0xf2410c31,                             //vfma.f32      d16, d1, d17
-  0xf4e31c9f,                             //vld1.32       {d17[]}, [r3 :32]
-  0xf2411c32,                             //vfma.f32      d17, d1, d18
-  0xf4ee2c9d,                             //vld1.32       {d18[]}, [lr :32]!
-  0xf4ee3c9f,                             //vld1.32       {d19[]}, [lr :32]
-  0xf2400c32,                             //vfma.f32      d16, d0, d18
-  0xf2401c33,                             //vfma.f32      d17, d0, d19
-  0xf22001b0,                             //vorr          d0, d16, d16
-  0xf22111b1,                             //vorr          d1, d17, d17
-  0xe8bd4800,                             //pop           {fp, lr}
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_matrix_3x4_vfp4[] = {
-  0xe92d4800,                             //push          {fp, lr}
-  0xe591e000,                             //ldr           lr, [r1]
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe2811008,                             //add           r1, r1, #8
-  0xe28e3020,                             //add           r3, lr, #32
-  0xf4e33c9f,                             //vld1.32       {d19[]}, [r3 :32]
-  0xe28e302c,                             //add           r3, lr, #44
-  0xf4e30c9f,                             //vld1.32       {d16[]}, [r3 :32]
-  0xe28e301c,                             //add           r3, lr, #28
-  0xf2420c33,                             //vfma.f32      d16, d2, d19
-  0xf4e34c9f,                             //vld1.32       {d20[]}, [r3 :32]
-  0xe28e3018,                             //add           r3, lr, #24
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xe28e3024,                             //add           r3, lr, #36
-  0xf4e31c9f,                             //vld1.32       {d17[]}, [r3 :32]
-  0xe28e3028,                             //add           r3, lr, #40
-  0xf2421c32,                             //vfma.f32      d17, d2, d18
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xe28e3010,                             //add           r3, lr, #16
-  0xf2422c34,                             //vfma.f32      d18, d2, d20
-  0xf4e33c9f,                             //vld1.32       {d19[]}, [r3 :32]
-  0xe28e300c,                             //add           r3, lr, #12
-  0xf4e34c9f,                             //vld1.32       {d20[]}, [r3 :32]
-  0xe28e3014,                             //add           r3, lr, #20
-  0xf2411c34,                             //vfma.f32      d17, d1, d20
-  0xf4e34c9f,                             //vld1.32       {d20[]}, [r3 :32]
-  0xf2410c34,                             //vfma.f32      d16, d1, d20
-  0xe28e3008,                             //add           r3, lr, #8
-  0xf2412c33,                             //vfma.f32      d18, d1, d19
-  0xf4ee3c9d,                             //vld1.32       {d19[]}, [lr :32]!
-  0xf4ee4c9f,                             //vld1.32       {d20[]}, [lr :32]
-  0xf2401c33,                             //vfma.f32      d17, d0, d19
-  0xf4e33c9f,                             //vld1.32       {d19[]}, [r3 :32]
-  0xf2400c33,                             //vfma.f32      d16, d0, d19
-  0xf2402c34,                             //vfma.f32      d18, d0, d20
-  0xf22101b1,                             //vorr          d0, d17, d17
-  0xf22021b0,                             //vorr          d2, d16, d16
-  0xf22211b2,                             //vorr          d1, d18, d18
-  0xe8bd4800,                             //pop           {fp, lr}
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_matrix_4x5_vfp4[] = {
-  0xe92d4010,                             //push          {r4, lr}
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xf2620112,                             //vorr          d16, d2, d2
-  0xe2811008,                             //add           r1, r1, #8
-  0xe2834014,                             //add           r4, r3, #20
-  0xe1a0e003,                             //mov           lr, r3
-  0xf4e45c9f,                             //vld1.32       {d21[]}, [r4 :32]
-  0xe2834028,                             //add           r4, r3, #40
-  0xf4e46c9f,                             //vld1.32       {d22[]}, [r4 :32]
-  0xe2834038,                             //add           r4, r3, #56
-  0xf4e47c9f,                             //vld1.32       {d23[]}, [r4 :32]
-  0xe2834048,                             //add           r4, r3, #72
-  0xf4a42c9f,                             //vld1.32       {d2[]}, [r4 :32]
-  0xe2834034,                             //add           r4, r3, #52
-  0xf2032c37,                             //vfma.f32      d2, d3, d23
-  0xf4e48c9f,                             //vld1.32       {d24[]}, [r4 :32]
-  0xe2834044,                             //add           r4, r3, #68
-  0xf4e41c9f,                             //vld1.32       {d17[]}, [r4 :32]
-  0xe2834030,                             //add           r4, r3, #48
-  0xf2431c38,                             //vfma.f32      d17, d3, d24
-  0xf4e49c9f,                             //vld1.32       {d25[]}, [r4 :32]
-  0xe283403c,                             //add           r4, r3, #60
-  0xf4e43c9f,                             //vld1.32       {d19[]}, [r4 :32]
-  0xe283404c,                             //add           r4, r3, #76
-  0xf2002cb6,                             //vfma.f32      d2, d16, d22
-  0xf4e42c9f,                             //vld1.32       {d18[]}, [r4 :32]
-  0xe2834040,                             //add           r4, r3, #64
-  0xf2432c33,                             //vfma.f32      d18, d3, d19
-  0xf4e43c9f,                             //vld1.32       {d19[]}, [r4 :32]
-  0xe2834020,                             //add           r4, r3, #32
-  0xf2433c39,                             //vfma.f32      d19, d3, d25
-  0xf4e47c9f,                             //vld1.32       {d23[]}, [r4 :32]
-  0xe283402c,                             //add           r4, r3, #44
-  0xf4e48c9f,                             //vld1.32       {d24[]}, [r4 :32]
-  0xe2834024,                             //add           r4, r3, #36
-  0xf2402cb8,                             //vfma.f32      d18, d16, d24
-  0xf4e48c9f,                             //vld1.32       {d24[]}, [r4 :32]
-  0xf2401cb8,                             //vfma.f32      d17, d16, d24
-  0xe2834010,                             //add           r4, r3, #16
-  0xf2403cb7,                             //vfma.f32      d19, d16, d23
-  0xf4ee4c9d,                             //vld1.32       {d20[]}, [lr :32]!
-  0xf4e40c9f,                             //vld1.32       {d16[]}, [r4 :32]
-  0xe283401c,                             //add           r4, r3, #28
-  0xf4e46c9f,                             //vld1.32       {d22[]}, [r4 :32]
-  0xe2834018,                             //add           r4, r3, #24
-  0xf2412c36,                             //vfma.f32      d18, d1, d22
-  0xf2411c35,                             //vfma.f32      d17, d1, d21
-  0xf4ee5c9f,                             //vld1.32       {d21[]}, [lr :32]
-  0xf2413c30,                             //vfma.f32      d19, d1, d16
-  0xf4e40c9f,                             //vld1.32       {d16[]}, [r4 :32]
-  0xe2834008,                             //add           r4, r3, #8
-  0xe283300c,                             //add           r3, r3, #12
-  0xf2012c30,                             //vfma.f32      d2, d1, d16
-  0xf4e40c9f,                             //vld1.32       {d16[]}, [r4 :32]
-  0xf2401c35,                             //vfma.f32      d17, d0, d21
-  0xf2403c34,                             //vfma.f32      d19, d0, d20
-  0xf4e34c9f,                             //vld1.32       {d20[]}, [r3 :32]
-  0xf2402c34,                             //vfma.f32      d18, d0, d20
-  0xf2002c30,                             //vfma.f32      d2, d0, d16
-  0xf22111b1,                             //vorr          d1, d17, d17
-  0xf22301b3,                             //vorr          d0, d19, d19
-  0xf22231b2,                             //vorr          d3, d18, d18
-  0xe8bd4010,                             //pop           {r4, lr}
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_matrix_perspective_vfp4[] = {
-  0xe92d4010,                             //push          {r4, lr}
-  0xe591e000,                             //ldr           lr, [r1]
-  0xe591c004,                             //ldr           ip, [r1, #4]
-  0xe2811008,                             //add           r1, r1, #8
-  0xe28e301c,                             //add           r3, lr, #28
-  0xe28e4010,                             //add           r4, lr, #16
-  0xf4e30c9f,                             //vld1.32       {d16[]}, [r3 :32]
-  0xe28e3020,                             //add           r3, lr, #32
-  0xf4e31c9f,                             //vld1.32       {d17[]}, [r3 :32]
-  0xe28e3018,                             //add           r3, lr, #24
-  0xf2411c30,                             //vfma.f32      d17, d1, d16
-  0xf4e30c9f,                             //vld1.32       {d16[]}, [r3 :32]
-  0xe1a0300e,                             //mov           r3, lr
-  0xf4e42c9f,                             //vld1.32       {d18[]}, [r4 :32]
-  0xe28e4008,                             //add           r4, lr, #8
-  0xf4e43c9f,                             //vld1.32       {d19[]}, [r4 :32]
-  0xf2401c30,                             //vfma.f32      d17, d0, d16
-  0xf4e30c9d,                             //vld1.32       {d16[]}, [r3 :32]!
-  0xf4e35c9f,                             //vld1.32       {d21[]}, [r3 :32]
-  0xe28e3014,                             //add           r3, lr, #20
-  0xf2413c35,                             //vfma.f32      d19, d1, d21
-  0xf4e35c9f,                             //vld1.32       {d21[]}, [r3 :32]
-  0xe28e300c,                             //add           r3, lr, #12
-  0xf2415c32,                             //vfma.f32      d21, d1, d18
-  0xf4e32c9f,                             //vld1.32       {d18[]}, [r3 :32]
-  0xf3fb4521,                             //vrecpe.f32    d20, d17
-  0xf2403c30,                             //vfma.f32      d19, d0, d16
-  0xf2411fb4,                             //vrecps.f32    d17, d17, d20
-  0xf2405c32,                             //vfma.f32      d21, d0, d18
-  0xf3440db1,                             //vmul.f32      d16, d20, d17
-  0xf3030db0,                             //vmul.f32      d0, d19, d16
-  0xf3051db0,                             //vmul.f32      d1, d21, d16
-  0xe8bd4010,                             //pop           {r4, lr}
-  0xe12fff1c,                             //bx            ip
-};
-
-CODE const uint32_t sk_linear_gradient_2stops_vfp4[] = {
-  0xe8911008,                             //ldm           r1, {r3, ip}
-  0xe2811008,                             //add           r1, r1, #8
-  0xf4632a0d,                             //vld1.8        {d18-d19}, [r3]!
-  0xf4634a0f,                             //vld1.8        {d20-d21}, [r3]
-  0xf3f40c22,                             //vdup.32       d16, d18[0]
-  0xf3f41c24,                             //vdup.32       d17, d20[0]
-  0xf2400c31,                             //vfma.f32      d16, d0, d17
-  0xf3fc6c24,                             //vdup.32       d22, d20[1]
-  0xf3bc1c22,                             //vdup.32       d1, d18[1]
-  0xf3b42c23,                             //vdup.32       d2, d19[0]
-  0xf2001c36,                             //vfma.f32      d1, d0, d22
-  0xf3f41c25,                             //vdup.32       d17, d21[0]
-  0xf3fc4c25,                             //vdup.32       d20, d21[1]
-  0xf2002c31,                             //vfma.f32      d2, d0, d17
-  0xf3bc3c23,                             //vdup.32       d3, d19[1]
-  0xf2003c34,                             //vfma.f32      d3, d0, d20
-  0xf22001b0,                             //vorr          d0, d16, d16
-  0xe12fff1c,                             //bx            ip
-};
-#elif defined(__x86_64__)
-
-CODE const uint8_t sk_start_pipeline_hsw[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  83,                                     //push          %rbx
-  73,137,205,                             //mov           %rcx,%r13
-  73,137,214,                             //mov           %rdx,%r14
-  72,137,251,                             //mov           %rdi,%rbx
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,199,                             //mov           %rax,%r15
-  73,137,244,                             //mov           %rsi,%r12
-  72,141,67,8,                            //lea           0x8(%rbx),%rax
-  76,57,232,                              //cmp           %r13,%rax
-  118,5,                                  //jbe           28 <_sk_start_pipeline_hsw+0x28>
-  72,137,223,                             //mov           %rbx,%rdi
-  235,65,                                 //jmp           69 <_sk_start_pipeline_hsw+0x69>
-  185,0,0,0,0,                            //mov           $0x0,%ecx
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  72,141,123,8,                           //lea           0x8(%rbx),%rdi
-  72,131,195,16,                          //add           $0x10,%rbx
-  76,57,235,                              //cmp           %r13,%rbx
-  72,137,251,                             //mov           %rdi,%rbx
-  118,191,                                //jbe           28 <_sk_start_pipeline_hsw+0x28>
-  76,137,233,                             //mov           %r13,%rcx
-  72,41,249,                              //sub           %rdi,%rcx
-  116,41,                                 //je            9a <_sk_start_pipeline_hsw+0x9a>
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  76,137,232,                             //mov           %r13,%rax
-  91,                                     //pop           %rbx
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  197,248,119,                            //vzeroupper
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_hsw[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,249,110,199,                        //vmovd         %edi,%xmm0
-  196,226,125,88,192,                     //vpbroadcastd  %xmm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  65,184,0,0,0,63,                        //mov           $0x3f000000,%r8d
-  196,193,121,110,200,                    //vmovd         %r8d,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  197,252,88,2,                           //vaddps        (%rdx),%ymm0,%ymm0
-  196,226,125,24,16,                      //vbroadcastss  (%rax),%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  197,236,88,201,                         //vaddps        %ymm1,%ymm2,%ymm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,208,                        //vmovd         %eax,%xmm2
-  196,226,125,88,210,                     //vpbroadcastd  %xmm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  196,226,125,24,88,12,                   //vbroadcastss  0xc(%rax),%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__hsw[] = {
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,92,195,                          //vsubps        %ymm3,%ymm8,%ymm8
-  196,194,93,184,192,                     //vfmadd231ps   %ymm8,%ymm4,%ymm0
-  196,194,85,184,200,                     //vfmadd231ps   %ymm8,%ymm5,%ymm1
-  196,194,77,184,208,                     //vfmadd231ps   %ymm8,%ymm6,%ymm2
-  196,194,69,184,216,                     //vfmadd231ps   %ymm8,%ymm7,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,92,199,                          //vsubps        %ymm7,%ymm8,%ymm8
-  196,226,61,168,196,                     //vfmadd213ps   %ymm4,%ymm8,%ymm0
-  196,226,61,168,205,                     //vfmadd213ps   %ymm5,%ymm8,%ymm1
-  196,226,61,168,214,                     //vfmadd213ps   %ymm6,%ymm8,%ymm2
-  196,226,61,168,223,                     //vfmadd213ps   %ymm7,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_hsw[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,193,124,95,192,                     //vmaxps        %ymm8,%ymm0,%ymm0
-  196,193,116,95,200,                     //vmaxps        %ymm8,%ymm1,%ymm1
-  196,193,108,95,208,                     //vmaxps        %ymm8,%ymm2,%ymm2
-  196,193,100,95,216,                     //vmaxps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  196,193,108,93,208,                     //vminps        %ymm8,%ymm2,%ymm2
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  197,252,93,195,                         //vminps        %ymm3,%ymm0,%ymm0
-  197,244,93,203,                         //vminps        %ymm3,%ymm1,%ymm1
-  197,236,93,211,                         //vminps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_hsw[] = {
-  197,124,40,192,                         //vmovaps       %ymm0,%ymm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,194,                         //vmovaps       %ymm2,%ymm0
-  197,124,41,194,                         //vmovaps       %ymm8,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_hsw[] = {
-  197,124,40,195,                         //vmovaps       %ymm3,%ymm8
-  197,124,40,202,                         //vmovaps       %ymm2,%ymm9
-  197,124,40,209,                         //vmovaps       %ymm1,%ymm10
-  197,124,40,216,                         //vmovaps       %ymm0,%ymm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  197,124,41,220,                         //vmovaps       %ymm11,%ymm4
-  197,124,41,213,                         //vmovaps       %ymm10,%ymm5
-  197,124,41,206,                         //vmovaps       %ymm9,%ymm6
-  197,124,41,199,                         //vmovaps       %ymm8,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,224,                         //vmovaps       %ymm0,%ymm4
-  197,252,40,233,                         //vmovaps       %ymm1,%ymm5
-  197,252,40,242,                         //vmovaps       %ymm2,%ymm6
-  197,252,40,251,                         //vmovaps       %ymm3,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_hsw[] = {
-  197,252,89,195,                         //vmulps        %ymm3,%ymm0,%ymm0
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_hsw[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,65,100,194,200,0,                   //vcmpeqps      %ymm8,%ymm3,%ymm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  197,44,94,211,                          //vdivps        %ymm3,%ymm10,%ymm10
-  196,67,45,74,192,144,                   //vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_hsw[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  197,124,89,208,                         //vmulps        %ymm0,%ymm0,%ymm10
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  197,121,110,216,                        //vmovd         %eax,%xmm11
-  196,66,125,88,219,                      //vpbroadcastd  %xmm11,%ymm11
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  197,121,110,224,                        //vmovd         %eax,%xmm12
-  196,66,125,88,228,                      //vpbroadcastd  %xmm12,%ymm12
-  196,65,125,111,235,                     //vmovdqa       %ymm11,%ymm13
-  196,66,125,168,236,                     //vfmadd213ps   %ymm12,%ymm0,%ymm13
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,66,125,88,246,                      //vpbroadcastd  %xmm14,%ymm14
-  196,66,45,168,238,                      //vfmadd213ps   %ymm14,%ymm10,%ymm13
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  196,193,124,194,194,1,                  //vcmpltps      %ymm10,%ymm0,%ymm0
-  196,195,21,74,193,0,                    //vblendvps     %ymm0,%ymm9,%ymm13,%ymm0
-  197,60,89,201,                          //vmulps        %ymm1,%ymm8,%ymm9
-  197,116,89,233,                         //vmulps        %ymm1,%ymm1,%ymm13
-  196,65,125,111,251,                     //vmovdqa       %ymm11,%ymm15
-  196,66,117,168,252,                     //vfmadd213ps   %ymm12,%ymm1,%ymm15
-  196,66,21,168,254,                      //vfmadd213ps   %ymm14,%ymm13,%ymm15
-  196,193,116,194,202,1,                  //vcmpltps      %ymm10,%ymm1,%ymm1
-  196,195,5,74,201,16,                    //vblendvps     %ymm1,%ymm9,%ymm15,%ymm1
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  197,108,89,202,                         //vmulps        %ymm2,%ymm2,%ymm9
-  196,66,109,168,220,                     //vfmadd213ps   %ymm12,%ymm2,%ymm11
-  196,66,53,168,222,                      //vfmadd213ps   %ymm14,%ymm9,%ymm11
-  196,193,108,194,210,1,                  //vcmpltps      %ymm10,%ymm2,%ymm2
-  196,195,37,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm11,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_hsw[] = {
-  197,124,82,192,                         //vrsqrtps      %ymm0,%ymm8
-  196,65,124,83,216,                      //vrcpps        %ymm8,%ymm11
-  196,65,124,82,224,                      //vrsqrtps      %ymm8,%ymm12
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,232,                          //vmulps        %ymm0,%ymm8,%ymm13
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,66,125,88,201,                      //vpbroadcastd  %xmm9,%ymm9
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,66,125,88,246,                      //vpbroadcastd  %xmm14,%ymm14
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  197,121,110,248,                        //vmovd         %eax,%xmm15
-  196,66,125,88,255,                      //vpbroadcastd  %xmm15,%ymm15
-  196,66,13,168,223,                      //vfmadd213ps   %ymm15,%ymm14,%ymm11
-  196,66,45,184,220,                      //vfmadd231ps   %ymm12,%ymm10,%ymm11
-  196,65,52,93,219,                       //vminps        %ymm11,%ymm9,%ymm11
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  197,121,110,224,                        //vmovd         %eax,%xmm12
-  196,66,125,88,228,                      //vpbroadcastd  %xmm12,%ymm12
-  196,193,124,194,196,1,                  //vcmpltps      %ymm12,%ymm0,%ymm0
-  196,195,37,74,197,0,                    //vblendvps     %ymm0,%ymm13,%ymm11,%ymm0
-  197,124,82,217,                         //vrsqrtps      %ymm1,%ymm11
-  196,65,124,83,235,                      //vrcpps        %ymm11,%ymm13
-  196,65,124,82,219,                      //vrsqrtps      %ymm11,%ymm11
-  196,66,13,168,239,                      //vfmadd213ps   %ymm15,%ymm14,%ymm13
-  196,66,45,184,235,                      //vfmadd231ps   %ymm11,%ymm10,%ymm13
-  197,60,89,217,                          //vmulps        %ymm1,%ymm8,%ymm11
-  196,65,52,93,237,                       //vminps        %ymm13,%ymm9,%ymm13
-  196,193,116,194,204,1,                  //vcmpltps      %ymm12,%ymm1,%ymm1
-  196,195,21,74,203,16,                   //vblendvps     %ymm1,%ymm11,%ymm13,%ymm1
-  197,124,82,218,                         //vrsqrtps      %ymm2,%ymm11
-  196,65,124,83,235,                      //vrcpps        %ymm11,%ymm13
-  196,66,13,168,239,                      //vfmadd213ps   %ymm15,%ymm14,%ymm13
-  196,65,124,82,219,                      //vrsqrtps      %ymm11,%ymm11
-  196,66,45,184,235,                      //vfmadd231ps   %ymm11,%ymm10,%ymm13
-  196,65,52,93,205,                       //vminps        %ymm13,%ymm9,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,193,108,194,212,1,                  //vcmpltps      %ymm12,%ymm2,%ymm2
-  196,195,53,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,56,                                 //jne           4bf <_sk_scale_u8_hsw+0x48>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,125,49,192,                      //vpmovzxbd     %xmm8,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,66,125,88,201,                      //vpbroadcastd  %xmm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           4c7 <_sk_scale_u8_hsw+0x50>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  235,167,                                //jmp           48b <_sk_scale_u8_hsw+0x14>
-};
-
-CODE const uint8_t sk_lerp_1_float_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,226,61,168,196,                     //vfmadd213ps   %ymm4,%ymm8,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,226,61,168,205,                     //vfmadd213ps   %ymm5,%ymm8,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,226,61,168,214,                     //vfmadd213ps   %ymm6,%ymm8,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,226,61,168,223,                     //vfmadd213ps   %ymm7,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,76,                                 //jne           56f <_sk_lerp_u8_hsw+0x5c>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,125,49,192,                      //vpmovzxbd     %xmm8,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,66,125,88,201,                      //vpbroadcastd  %xmm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,226,61,168,196,                     //vfmadd213ps   %ymm4,%ymm8,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,226,61,168,205,                     //vfmadd213ps   %ymm5,%ymm8,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,226,61,168,214,                     //vfmadd213ps   %ymm6,%ymm8,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,226,61,168,223,                     //vfmadd213ps   %ymm7,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           577 <_sk_lerp_u8_hsw+0x64>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  235,147,                                //jmp           527 <_sk_lerp_u8_hsw+0x14>
-};
-
-CODE const uint8_t sk_lerp_565_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,179,0,0,0,                       //jne           655 <_sk_lerp_565_hsw+0xc1>
-  196,193,122,111,28,122,                 //vmovdqu       (%r10,%rdi,2),%xmm3
-  196,98,125,51,195,                      //vpmovzxwd     %xmm3,%ymm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  196,193,101,219,216,                    //vpand         %ymm8,%ymm3,%ymm3
-  197,124,91,203,                         //vcvtdq2ps     %ymm3,%ymm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,52,89,203,                          //vmulps        %ymm3,%ymm9,%ymm9
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  196,193,101,219,216,                    //vpand         %ymm8,%ymm3,%ymm3
-  197,124,91,211,                         //vcvtdq2ps     %ymm3,%ymm10
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,44,89,211,                          //vmulps        %ymm3,%ymm10,%ymm10
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  196,193,101,219,216,                    //vpand         %ymm8,%ymm3,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,226,53,168,196,                     //vfmadd213ps   %ymm4,%ymm9,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,226,45,168,205,                     //vfmadd213ps   %ymm5,%ymm10,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,226,101,168,214,                    //vfmadd213ps   %ymm6,%ymm3,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,59,255,255,255,                  //ja            5a8 <_sk_lerp_565_hsw+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,76,0,0,0,                     //lea           0x4c(%rip),%r9        # 6c4 <_sk_lerp_565_hsw+0x130>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  196,193,97,196,92,122,12,6,             //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,10,5,             //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,8,4,              //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,6,3,              //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,4,2,              //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,2,1,              //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,28,122,0,                //vpinsrw       $0x0,(%r10,%rdi,2),%xmm3,%xmm3
-  233,231,254,255,255,                    //jmpq          5a8 <_sk_lerp_565_hsw+0x14>
-  15,31,0,                                //nopl          (%rax)
-  241,                                    //icebp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  233,255,255,255,225,                    //jmpq          ffffffffe20006cc <_sk_linear_gradient_2stops_hsw+0xffffffffe1fff4f0>
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  217,255,                                //fcos
-  255,                                    //(bad)
-  255,209,                                //callq         *%rcx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,201,                                //dec           %ecx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  189,                                    //.byte         0xbd
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_tables_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,141,12,189,0,0,0,0,                  //lea           0x0(,%rdi,4),%r9
-  76,3,8,                                 //add           (%rax),%r9
-  77,133,192,                             //test          %r8,%r8
-  117,121,                                //jne           76e <_sk_load_tables_hsw+0x8e>
-  196,193,126,111,25,                     //vmovdqu       (%r9),%ymm3
-  185,255,0,0,0,                          //mov           $0xff,%ecx
-  197,249,110,193,                        //vmovd         %ecx,%xmm0
-  196,226,125,88,208,                     //vpbroadcastd  %xmm0,%ymm2
-  197,237,219,203,                        //vpand         %ymm3,%ymm2,%ymm1
-  196,65,61,118,192,                      //vpcmpeqd      %ymm8,%ymm8,%ymm8
-  72,139,72,8,                            //mov           0x8(%rax),%rcx
-  76,139,72,16,                           //mov           0x10(%rax),%r9
-  196,65,53,118,201,                      //vpcmpeqd      %ymm9,%ymm9,%ymm9
-  196,226,53,146,4,137,                   //vgatherdps    %ymm9,(%rcx,%ymm1,4),%ymm0
-  197,245,114,211,8,                      //vpsrld        $0x8,%ymm3,%ymm1
-  197,109,219,201,                        //vpand         %ymm1,%ymm2,%ymm9
-  196,65,45,118,210,                      //vpcmpeqd      %ymm10,%ymm10,%ymm10
-  196,130,45,146,12,137,                  //vgatherdps    %ymm10,(%r9,%ymm9,4),%ymm1
-  72,139,64,24,                           //mov           0x18(%rax),%rax
-  197,181,114,211,16,                     //vpsrld        $0x10,%ymm3,%ymm9
-  196,65,109,219,201,                     //vpand         %ymm9,%ymm2,%ymm9
-  196,162,61,146,20,136,                  //vgatherdps    %ymm8,(%rax,%ymm9,4),%ymm2
-  197,229,114,211,24,                     //vpsrld        $0x18,%ymm3,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  185,8,0,0,0,                            //mov           $0x8,%ecx
-  68,41,193,                              //sub           %r8d,%ecx
-  192,225,3,                              //shl           $0x3,%cl
-  73,199,194,255,255,255,255,             //mov           $0xffffffffffffffff,%r10
-  73,211,234,                             //shr           %cl,%r10
-  196,193,249,110,194,                    //vmovq         %r10,%xmm0
-  196,226,125,33,192,                     //vpmovsxbd     %xmm0,%ymm0
-  196,194,125,140,25,                     //vpmaskmovd    (%r9),%ymm0,%ymm3
-  233,99,255,255,255,                     //jmpq          6fa <_sk_load_tables_hsw+0x1a>
-};
-
-CODE const uint8_t sk_load_a8_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,50,                                 //jne           7d9 <_sk_load_a8_hsw+0x42>
-  197,250,126,0,                          //vmovq         (%rax),%xmm0
-  196,226,125,49,192,                     //vpmovzxbd     %xmm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,252,89,217,                         //vmulps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           7e1 <_sk_load_a8_hsw+0x4a>
-  196,193,249,110,193,                    //vmovq         %r9,%xmm0
-  235,173,                                //jmp           7ab <_sk_load_a8_hsw+0x14>
-};
-
-CODE const uint8_t sk_store_a8_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  196,65,57,103,192,                      //vpackuswb     %xmm8,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           839 <_sk_store_a8_hsw+0x3b>
-  196,65,123,17,4,57,                     //vmovsd        %xmm8,(%r9,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            835 <_sk_store_a8_hsw+0x37>
-  196,66,121,48,192,                      //vpmovzxbw     %xmm8,%xmm8
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,67,0,0,0,                      //lea           0x43(%rip),%r8        # 89c <_sk_store_a8_hsw+0x9e>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,20,68,57,6,12,               //vpextrb       $0xc,%xmm8,0x6(%r9,%rdi,1)
-  196,67,121,20,68,57,5,10,               //vpextrb       $0xa,%xmm8,0x5(%r9,%rdi,1)
-  196,67,121,20,68,57,4,8,                //vpextrb       $0x8,%xmm8,0x4(%r9,%rdi,1)
-  196,67,121,20,68,57,3,6,                //vpextrb       $0x6,%xmm8,0x3(%r9,%rdi,1)
-  196,67,121,20,68,57,2,4,                //vpextrb       $0x4,%xmm8,0x2(%r9,%rdi,1)
-  196,67,121,20,68,57,1,2,                //vpextrb       $0x2,%xmm8,0x1(%r9,%rdi,1)
-  196,67,121,20,4,57,0,                   //vpextrb       $0x0,%xmm8,(%r9,%rdi,1)
-  235,154,                                //jmp           835 <_sk_store_a8_hsw+0x37>
-  144,                                    //nop
-  246,255,                                //idiv          %bh
-  255,                                    //(bad)
-  255,                                    //(bad)
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,230,                                //jmpq          *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  222,255,                                //fdivrp        %st,%st(7)
-  255,                                    //(bad)
-  255,214,                                //callq         *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,206,                                //dec           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,198,                                //inc           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_565_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,149,0,0,0,                       //jne           95b <_sk_load_565_hsw+0xa3>
-  196,193,122,111,4,122,                  //vmovdqu       (%r10,%rdi,2),%xmm0
-  196,226,125,51,208,                     //vpmovzxwd     %xmm0,%ymm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  196,226,125,88,192,                     //vpbroadcastd  %xmm0,%ymm0
-  197,253,219,194,                        //vpand         %ymm2,%ymm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,252,89,193,                         //vmulps        %ymm1,%ymm0,%ymm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,245,219,202,                        //vpand         %ymm2,%ymm1,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,229,219,210,                        //vpand         %ymm2,%ymm3,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,89,255,255,255,                  //ja            8cc <_sk_load_565_hsw+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,74,0,0,0,                     //lea           0x4a(%rip),%r9        # 9c8 <_sk_load_565_hsw+0x110>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  196,193,121,196,68,122,12,6,            //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,10,5,            //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,8,4,             //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,6,3,             //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,4,2,             //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,2,1,             //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,4,122,0,                //vpinsrw       $0x0,(%r10,%rdi,2),%xmm0,%xmm0
-  233,5,255,255,255,                      //jmpq          8cc <_sk_load_565_hsw+0x14>
-  144,                                    //nop
-  243,255,                                //repz          (bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  235,255,                                //jmp           9cd <_sk_load_565_hsw+0x115>
-  255,                                    //(bad)
-  255,227,                                //jmpq          *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  219,255,                                //(bad)
-  255,                                    //(bad)
-  255,211,                                //callq         *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,203,                                //dec           %ebx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  191,                                    //.byte         0xbf
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_store_565_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,248,65,                         //mov           $0x41f80000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  196,193,53,114,241,11,                  //vpslld        $0xb,%ymm9,%ymm9
-  184,0,0,124,66,                         //mov           $0x427c0000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,45,114,242,5,                   //vpslld        $0x5,%ymm10,%ymm10
-  196,65,45,235,201,                      //vpor          %ymm9,%ymm10,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,65,53,235,192,                      //vpor          %ymm8,%ymm9,%ymm8
-  196,67,125,57,193,1,                    //vextracti128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           a50 <_sk_store_565_hsw+0x6c>
-  196,65,122,127,4,121,                   //vmovdqu       %xmm8,(%r9,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            a4c <_sk_store_565_hsw+0x68>
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,69,0,0,0,                      //lea           0x45(%rip),%r8        # ab0 <_sk_store_565_hsw+0xcc>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,21,68,121,12,6,              //vpextrw       $0x6,%xmm8,0xc(%r9,%rdi,2)
-  196,67,121,21,68,121,10,5,              //vpextrw       $0x5,%xmm8,0xa(%r9,%rdi,2)
-  196,67,121,21,68,121,8,4,               //vpextrw       $0x4,%xmm8,0x8(%r9,%rdi,2)
-  196,67,121,21,68,121,6,3,               //vpextrw       $0x3,%xmm8,0x6(%r9,%rdi,2)
-  196,67,121,21,68,121,4,2,               //vpextrw       $0x2,%xmm8,0x4(%r9,%rdi,2)
-  196,67,121,21,68,121,2,1,               //vpextrw       $0x1,%xmm8,0x2(%r9,%rdi,2)
-  196,67,121,21,4,121,0,                  //vpextrw       $0x0,%xmm8,(%r9,%rdi,2)
-  235,159,                                //jmp           a4c <_sk_store_565_hsw+0x68>
-  15,31,0,                                //nopl          (%rax)
-  244,                                    //hlt
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  236,                                    //in            (%dx),%al
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,228,                                //jmpq          *%rsp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  220,255,                                //fdivr         %st,%st(7)
-  255,                                    //(bad)
-  255,212,                                //callq         *%rsp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,204,                                //dec           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,196,                                //inc           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_8888_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,141,12,189,0,0,0,0,                  //lea           0x0(,%rdi,4),%r9
-  76,3,8,                                 //add           (%rax),%r9
-  77,133,192,                             //test          %r8,%r8
-  117,104,                                //jne           b49 <_sk_load_8888_hsw+0x7d>
-  196,193,126,111,25,                     //vmovdqu       (%r9),%ymm3
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  196,226,125,88,208,                     //vpbroadcastd  %xmm0,%ymm2
-  197,237,219,195,                        //vpand         %ymm3,%ymm2,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,98,125,88,193,                      //vpbroadcastd  %xmm1,%ymm8
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  197,245,114,211,8,                      //vpsrld        $0x8,%ymm3,%ymm1
-  197,237,219,201,                        //vpand         %ymm1,%ymm2,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  197,181,114,211,16,                     //vpsrld        $0x10,%ymm3,%ymm9
-  196,193,109,219,209,                    //vpand         %ymm9,%ymm2,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  197,229,114,211,24,                     //vpsrld        $0x18,%ymm3,%ymm3
-  197,252,91,219,                         //vcvtdq2ps     %ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  185,8,0,0,0,                            //mov           $0x8,%ecx
-  68,41,193,                              //sub           %r8d,%ecx
-  192,225,3,                              //shl           $0x3,%cl
-  72,199,192,255,255,255,255,             //mov           $0xffffffffffffffff,%rax
-  72,211,232,                             //shr           %cl,%rax
-  196,225,249,110,192,                    //vmovq         %rax,%xmm0
-  196,226,125,33,192,                     //vpmovsxbd     %xmm0,%ymm0
-  196,194,125,140,25,                     //vpmaskmovd    (%r9),%ymm0,%ymm3
-  233,116,255,255,255,                    //jmpq          ae6 <_sk_load_8888_hsw+0x1a>
-};
-
-CODE const uint8_t sk_store_8888_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,141,12,189,0,0,0,0,                  //lea           0x0(,%rdi,4),%r9
-  76,3,8,                                 //add           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  197,60,89,209,                          //vmulps        %ymm1,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,45,114,242,8,                   //vpslld        $0x8,%ymm10,%ymm10
-  196,65,45,235,201,                      //vpor          %ymm9,%ymm10,%ymm9
-  197,60,89,210,                          //vmulps        %ymm2,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,45,114,242,16,                  //vpslld        $0x10,%ymm10,%ymm10
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,193,61,114,240,24,                  //vpslld        $0x18,%ymm8,%ymm8
-  196,65,45,235,192,                      //vpor          %ymm8,%ymm10,%ymm8
-  196,65,53,235,192,                      //vpor          %ymm8,%ymm9,%ymm8
-  77,133,192,                             //test          %r8,%r8
-  117,12,                                 //jne           be6 <_sk_store_8888_hsw+0x74>
-  196,65,126,127,1,                       //vmovdqu       %ymm8,(%r9)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  185,8,0,0,0,                            //mov           $0x8,%ecx
-  68,41,193,                              //sub           %r8d,%ecx
-  192,225,3,                              //shl           $0x3,%cl
-  72,199,192,255,255,255,255,             //mov           $0xffffffffffffffff,%rax
-  72,211,232,                             //shr           %cl,%rax
-  196,97,249,110,200,                     //vmovq         %rax,%xmm9
-  196,66,125,33,201,                      //vpmovsxbd     %xmm9,%ymm9
-  196,66,53,142,1,                        //vpmaskmovd    %ymm8,%ymm9,(%r9)
-  235,211,                                //jmp           bdf <_sk_store_8888_hsw+0x6d>
-};
-
-CODE const uint8_t sk_load_f16_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,133,201,                             //test          %rcx,%rcx
-  117,97,                                 //jne           c77 <_sk_load_f16_hsw+0x6b>
-  197,121,16,4,248,                       //vmovupd       (%rax,%rdi,8),%xmm8
-  197,249,16,84,248,16,                   //vmovupd       0x10(%rax,%rdi,8),%xmm2
-  197,249,16,92,248,32,                   //vmovupd       0x20(%rax,%rdi,8),%xmm3
-  197,122,111,76,248,48,                  //vmovdqu       0x30(%rax,%rdi,8),%xmm9
-  197,185,97,194,                         //vpunpcklwd    %xmm2,%xmm8,%xmm0
-  197,185,105,210,                        //vpunpckhwd    %xmm2,%xmm8,%xmm2
-  196,193,97,97,201,                      //vpunpcklwd    %xmm9,%xmm3,%xmm1
-  196,193,97,105,217,                     //vpunpckhwd    %xmm9,%xmm3,%xmm3
-  197,121,97,194,                         //vpunpcklwd    %xmm2,%xmm0,%xmm8
-  197,121,105,202,                        //vpunpckhwd    %xmm2,%xmm0,%xmm9
-  197,241,97,211,                         //vpunpcklwd    %xmm3,%xmm1,%xmm2
-  197,241,105,219,                        //vpunpckhwd    %xmm3,%xmm1,%xmm3
-  197,185,108,194,                        //vpunpcklqdq   %xmm2,%xmm8,%xmm0
-  196,226,125,19,192,                     //vcvtph2ps     %xmm0,%ymm0
-  197,185,109,202,                        //vpunpckhqdq   %xmm2,%xmm8,%xmm1
-  196,226,125,19,201,                     //vcvtph2ps     %xmm1,%ymm1
-  197,177,108,211,                        //vpunpcklqdq   %xmm3,%xmm9,%xmm2
-  196,226,125,19,210,                     //vcvtph2ps     %xmm2,%ymm2
-  197,177,109,219,                        //vpunpckhqdq   %xmm3,%xmm9,%xmm3
-  196,226,125,19,219,                     //vcvtph2ps     %xmm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  197,123,16,4,248,                       //vmovsd        (%rax,%rdi,8),%xmm8
-  196,65,49,239,201,                      //vpxor         %xmm9,%xmm9,%xmm9
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,79,                                 //je            cd6 <_sk_load_f16_hsw+0xca>
-  197,57,22,68,248,8,                     //vmovhpd       0x8(%rax,%rdi,8),%xmm8,%xmm8
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,67,                                 //jb            cd6 <_sk_load_f16_hsw+0xca>
-  197,251,16,84,248,16,                   //vmovsd        0x10(%rax,%rdi,8),%xmm2
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  116,68,                                 //je            ce3 <_sk_load_f16_hsw+0xd7>
-  197,233,22,84,248,24,                   //vmovhpd       0x18(%rax,%rdi,8),%xmm2,%xmm2
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,56,                                 //jb            ce3 <_sk_load_f16_hsw+0xd7>
-  197,251,16,92,248,32,                   //vmovsd        0x20(%rax,%rdi,8),%xmm3
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  15,132,114,255,255,255,                 //je            c2d <_sk_load_f16_hsw+0x21>
-  197,225,22,92,248,40,                   //vmovhpd       0x28(%rax,%rdi,8),%xmm3,%xmm3
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  15,130,98,255,255,255,                  //jb            c2d <_sk_load_f16_hsw+0x21>
-  197,122,126,76,248,48,                  //vmovq         0x30(%rax,%rdi,8),%xmm9
-  233,87,255,255,255,                     //jmpq          c2d <_sk_load_f16_hsw+0x21>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  197,233,87,210,                         //vxorpd        %xmm2,%xmm2,%xmm2
-  233,74,255,255,255,                     //jmpq          c2d <_sk_load_f16_hsw+0x21>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  233,65,255,255,255,                     //jmpq          c2d <_sk_load_f16_hsw+0x21>
-};
-
-CODE const uint8_t sk_store_f16_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  196,195,125,29,192,4,                   //vcvtps2ph     $0x4,%ymm0,%xmm8
-  196,195,125,29,201,4,                   //vcvtps2ph     $0x4,%ymm1,%xmm9
-  196,195,125,29,210,4,                   //vcvtps2ph     $0x4,%ymm2,%xmm10
-  196,195,125,29,219,4,                   //vcvtps2ph     $0x4,%ymm3,%xmm11
-  196,65,57,97,225,                       //vpunpcklwd    %xmm9,%xmm8,%xmm12
-  196,65,57,105,193,                      //vpunpckhwd    %xmm9,%xmm8,%xmm8
-  196,65,41,97,203,                       //vpunpcklwd    %xmm11,%xmm10,%xmm9
-  196,65,41,105,235,                      //vpunpckhwd    %xmm11,%xmm10,%xmm13
-  196,65,25,98,217,                       //vpunpckldq    %xmm9,%xmm12,%xmm11
-  196,65,25,106,209,                      //vpunpckhdq    %xmm9,%xmm12,%xmm10
-  196,65,57,98,205,                       //vpunpckldq    %xmm13,%xmm8,%xmm9
-  196,65,57,106,197,                      //vpunpckhdq    %xmm13,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,27,                                 //jne           d51 <_sk_store_f16_hsw+0x65>
-  197,120,17,28,248,                      //vmovups       %xmm11,(%rax,%rdi,8)
-  197,120,17,84,248,16,                   //vmovups       %xmm10,0x10(%rax,%rdi,8)
-  197,120,17,76,248,32,                   //vmovups       %xmm9,0x20(%rax,%rdi,8)
-  197,122,127,68,248,48,                  //vmovdqu       %xmm8,0x30(%rax,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  197,121,214,28,248,                     //vmovq         %xmm11,(%rax,%rdi,8)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,241,                                //je            d4d <_sk_store_f16_hsw+0x61>
-  197,121,23,92,248,8,                    //vmovhpd       %xmm11,0x8(%rax,%rdi,8)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,229,                                //jb            d4d <_sk_store_f16_hsw+0x61>
-  197,121,214,84,248,16,                  //vmovq         %xmm10,0x10(%rax,%rdi,8)
-  116,221,                                //je            d4d <_sk_store_f16_hsw+0x61>
-  197,121,23,84,248,24,                   //vmovhpd       %xmm10,0x18(%rax,%rdi,8)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,209,                                //jb            d4d <_sk_store_f16_hsw+0x61>
-  197,121,214,76,248,32,                  //vmovq         %xmm9,0x20(%rax,%rdi,8)
-  116,201,                                //je            d4d <_sk_store_f16_hsw+0x61>
-  197,121,23,76,248,40,                   //vmovhpd       %xmm9,0x28(%rax,%rdi,8)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,189,                                //jb            d4d <_sk_store_f16_hsw+0x61>
-  197,121,214,68,248,48,                  //vmovq         %xmm8,0x30(%rax,%rdi,8)
-  235,181,                                //jmp           d4d <_sk_store_f16_hsw+0x61>
-};
-
-CODE const uint8_t sk_store_f32_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  72,141,4,189,0,0,0,0,                   //lea           0x0(,%rdi,4),%rax
-  197,124,20,193,                         //vunpcklps     %ymm1,%ymm0,%ymm8
-  197,124,21,217,                         //vunpckhps     %ymm1,%ymm0,%ymm11
-  197,108,20,203,                         //vunpcklps     %ymm3,%ymm2,%ymm9
-  197,108,21,227,                         //vunpckhps     %ymm3,%ymm2,%ymm12
-  196,65,61,20,209,                       //vunpcklpd     %ymm9,%ymm8,%ymm10
-  196,65,61,21,201,                       //vunpckhpd     %ymm9,%ymm8,%ymm9
-  196,65,37,20,196,                       //vunpcklpd     %ymm12,%ymm11,%ymm8
-  196,65,37,21,220,                       //vunpckhpd     %ymm12,%ymm11,%ymm11
-  72,133,201,                             //test          %rcx,%rcx
-  117,55,                                 //jne           e05 <_sk_store_f32_hsw+0x6d>
-  196,67,45,24,225,1,                     //vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
-  196,67,61,24,235,1,                     //vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
-  196,67,45,6,201,49,                     //vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
-  196,67,61,6,195,49,                     //vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
-  196,65,125,17,36,128,                   //vmovupd       %ymm12,(%r8,%rax,4)
-  196,65,125,17,108,128,32,               //vmovupd       %ymm13,0x20(%r8,%rax,4)
-  196,65,125,17,76,128,64,                //vmovupd       %ymm9,0x40(%r8,%rax,4)
-  196,65,125,17,68,128,96,                //vmovupd       %ymm8,0x60(%r8,%rax,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  196,65,121,17,20,128,                   //vmovupd       %xmm10,(%r8,%rax,4)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,240,                                //je            e01 <_sk_store_f32_hsw+0x69>
-  196,65,121,17,76,128,16,                //vmovupd       %xmm9,0x10(%r8,%rax,4)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,227,                                //jb            e01 <_sk_store_f32_hsw+0x69>
-  196,65,121,17,68,128,32,                //vmovupd       %xmm8,0x20(%r8,%rax,4)
-  116,218,                                //je            e01 <_sk_store_f32_hsw+0x69>
-  196,65,121,17,92,128,48,                //vmovupd       %xmm11,0x30(%r8,%rax,4)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,205,                                //jb            e01 <_sk_store_f32_hsw+0x69>
-  196,67,125,25,84,128,64,1,              //vextractf128  $0x1,%ymm10,0x40(%r8,%rax,4)
-  116,195,                                //je            e01 <_sk_store_f32_hsw+0x69>
-  196,67,125,25,76,128,80,1,              //vextractf128  $0x1,%ymm9,0x50(%r8,%rax,4)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,181,                                //jb            e01 <_sk_store_f32_hsw+0x69>
-  196,67,125,25,68,128,96,1,              //vextractf128  $0x1,%ymm8,0x60(%r8,%rax,4)
-  235,171,                                //jmp           e01 <_sk_store_f32_hsw+0x69>
-};
-
-CODE const uint8_t sk_clamp_x_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,188,95,192,                         //vmaxps        %ymm0,%ymm8,%ymm0
-  196,98,125,88,0,                        //vpbroadcastd  (%rax),%ymm8
-  196,65,53,118,201,                      //vpcmpeqd      %ymm9,%ymm9,%ymm9
-  196,65,61,254,193,                      //vpaddd        %ymm9,%ymm8,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,188,95,201,                         //vmaxps        %ymm1,%ymm8,%ymm1
-  196,98,125,88,0,                        //vpbroadcastd  (%rax),%ymm8
-  196,65,53,118,201,                      //vpcmpeqd      %ymm9,%ymm9,%ymm9
-  196,65,61,254,193,                      //vpaddd        %ymm9,%ymm8,%ymm8
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,124,94,200,                      //vdivps        %ymm8,%ymm0,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,98,61,172,200,                      //vfnmadd213ps  %ymm0,%ymm8,%ymm9
-  197,253,118,192,                        //vpcmpeqd      %ymm0,%ymm0,%ymm0
-  197,189,254,192,                        //vpaddd        %ymm0,%ymm8,%ymm0
-  197,180,93,192,                         //vminps        %ymm0,%ymm9,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,116,94,200,                      //vdivps        %ymm8,%ymm1,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,98,61,172,201,                      //vfnmadd213ps  %ymm1,%ymm8,%ymm9
-  197,245,118,201,                        //vpcmpeqd      %ymm1,%ymm1,%ymm1
-  197,189,254,201,                        //vpaddd        %ymm1,%ymm8,%ymm1
-  197,180,93,201,                         //vminps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,122,16,0,                           //vmovss        (%rax),%xmm8
-  196,66,125,24,200,                      //vbroadcastss  %xmm8,%ymm9
-  196,65,124,92,209,                      //vsubps        %ymm9,%ymm0,%ymm10
-  196,193,58,88,192,                      //vaddss        %xmm8,%xmm8,%xmm0
-  196,226,125,24,192,                     //vbroadcastss  %xmm0,%ymm0
-  197,44,94,192,                          //vdivps        %ymm0,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  196,66,125,172,194,                     //vfnmadd213ps  %ymm10,%ymm0,%ymm8
-  196,193,60,92,193,                      //vsubps        %ymm9,%ymm8,%ymm0
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,192,                          //vsubps        %ymm0,%ymm8,%ymm8
-  197,188,84,192,                         //vandps        %ymm0,%ymm8,%ymm0
-  196,65,61,118,192,                      //vpcmpeqd      %ymm8,%ymm8,%ymm8
-  196,65,53,254,192,                      //vpaddd        %ymm8,%ymm9,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,122,16,0,                           //vmovss        (%rax),%xmm8
-  196,66,125,24,200,                      //vbroadcastss  %xmm8,%ymm9
-  196,65,116,92,209,                      //vsubps        %ymm9,%ymm1,%ymm10
-  196,193,58,88,200,                      //vaddss        %xmm8,%xmm8,%xmm1
-  196,226,125,24,201,                     //vbroadcastss  %xmm1,%ymm1
-  197,44,94,193,                          //vdivps        %ymm1,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  196,66,117,172,194,                     //vfnmadd213ps  %ymm10,%ymm1,%ymm8
-  196,193,60,92,201,                      //vsubps        %ymm9,%ymm8,%ymm1
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,193,                          //vsubps        %ymm1,%ymm8,%ymm8
-  197,188,84,201,                         //vandps        %ymm1,%ymm8,%ymm1
-  196,65,61,118,192,                      //vpcmpeqd      %ymm8,%ymm8,%ymm8
-  196,65,53,254,192,                      //vpaddd        %ymm8,%ymm9,%ymm8
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_hsw[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,98,125,88,195,                      //vpbroadcastd  %xmm3,%ymm8
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,228,89,201,                         //vmulps        %ymm1,%ymm3,%ymm1
-  196,98,125,168,193,                     //vfmadd213ps   %ymm1,%ymm0,%ymm8
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  196,226,125,88,216,                     //vpbroadcastd  %xmm0,%ymm3
-  196,194,109,168,216,                    //vfmadd213ps   %ymm8,%ymm2,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,253,239,192,                        //vpxor         %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,8,                        //vbroadcastss  (%rax),%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,98,125,24,64,16,                    //vbroadcastss  0x10(%rax),%ymm8
-  196,66,117,184,194,                     //vfmadd231ps   %ymm10,%ymm1,%ymm8
-  196,66,125,184,193,                     //vfmadd231ps   %ymm9,%ymm0,%ymm8
-  196,98,125,24,80,4,                     //vbroadcastss  0x4(%rax),%ymm10
-  196,98,125,24,88,12,                    //vbroadcastss  0xc(%rax),%ymm11
-  196,98,125,24,72,20,                    //vbroadcastss  0x14(%rax),%ymm9
-  196,66,117,184,203,                     //vfmadd231ps   %ymm11,%ymm1,%ymm9
-  196,66,125,184,202,                     //vfmadd231ps   %ymm10,%ymm0,%ymm9
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,8,                        //vbroadcastss  (%rax),%ymm9
-  196,98,125,24,80,12,                    //vbroadcastss  0xc(%rax),%ymm10
-  196,98,125,24,88,24,                    //vbroadcastss  0x18(%rax),%ymm11
-  196,98,125,24,64,36,                    //vbroadcastss  0x24(%rax),%ymm8
-  196,66,109,184,195,                     //vfmadd231ps   %ymm11,%ymm2,%ymm8
-  196,66,117,184,194,                     //vfmadd231ps   %ymm10,%ymm1,%ymm8
-  196,66,125,184,193,                     //vfmadd231ps   %ymm9,%ymm0,%ymm8
-  196,98,125,24,80,4,                     //vbroadcastss  0x4(%rax),%ymm10
-  196,98,125,24,88,16,                    //vbroadcastss  0x10(%rax),%ymm11
-  196,98,125,24,96,28,                    //vbroadcastss  0x1c(%rax),%ymm12
-  196,98,125,24,72,40,                    //vbroadcastss  0x28(%rax),%ymm9
-  196,66,109,184,204,                     //vfmadd231ps   %ymm12,%ymm2,%ymm9
-  196,66,117,184,203,                     //vfmadd231ps   %ymm11,%ymm1,%ymm9
-  196,66,125,184,202,                     //vfmadd231ps   %ymm10,%ymm0,%ymm9
-  196,98,125,24,88,8,                     //vbroadcastss  0x8(%rax),%ymm11
-  196,98,125,24,96,20,                    //vbroadcastss  0x14(%rax),%ymm12
-  196,98,125,24,104,32,                   //vbroadcastss  0x20(%rax),%ymm13
-  196,98,125,24,80,44,                    //vbroadcastss  0x2c(%rax),%ymm10
-  196,66,109,184,213,                     //vfmadd231ps   %ymm13,%ymm2,%ymm10
-  196,66,117,184,212,                     //vfmadd231ps   %ymm12,%ymm1,%ymm10
-  196,66,125,184,211,                     //vfmadd231ps   %ymm11,%ymm0,%ymm10
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  197,124,41,210,                         //vmovaps       %ymm10,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,8,                        //vbroadcastss  (%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  196,98,125,24,88,32,                    //vbroadcastss  0x20(%rax),%ymm11
-  196,98,125,24,96,48,                    //vbroadcastss  0x30(%rax),%ymm12
-  196,98,125,24,64,64,                    //vbroadcastss  0x40(%rax),%ymm8
-  196,66,101,184,196,                     //vfmadd231ps   %ymm12,%ymm3,%ymm8
-  196,66,109,184,195,                     //vfmadd231ps   %ymm11,%ymm2,%ymm8
-  196,66,117,184,194,                     //vfmadd231ps   %ymm10,%ymm1,%ymm8
-  196,66,125,184,193,                     //vfmadd231ps   %ymm9,%ymm0,%ymm8
-  196,98,125,24,80,4,                     //vbroadcastss  0x4(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  196,98,125,24,96,36,                    //vbroadcastss  0x24(%rax),%ymm12
-  196,98,125,24,104,52,                   //vbroadcastss  0x34(%rax),%ymm13
-  196,98,125,24,72,68,                    //vbroadcastss  0x44(%rax),%ymm9
-  196,66,101,184,205,                     //vfmadd231ps   %ymm13,%ymm3,%ymm9
-  196,66,109,184,204,                     //vfmadd231ps   %ymm12,%ymm2,%ymm9
-  196,66,117,184,203,                     //vfmadd231ps   %ymm11,%ymm1,%ymm9
-  196,66,125,184,202,                     //vfmadd231ps   %ymm10,%ymm0,%ymm9
-  196,98,125,24,88,8,                     //vbroadcastss  0x8(%rax),%ymm11
-  196,98,125,24,96,24,                    //vbroadcastss  0x18(%rax),%ymm12
-  196,98,125,24,104,40,                   //vbroadcastss  0x28(%rax),%ymm13
-  196,98,125,24,112,56,                   //vbroadcastss  0x38(%rax),%ymm14
-  196,98,125,24,80,72,                    //vbroadcastss  0x48(%rax),%ymm10
-  196,66,101,184,214,                     //vfmadd231ps   %ymm14,%ymm3,%ymm10
-  196,66,109,184,213,                     //vfmadd231ps   %ymm13,%ymm2,%ymm10
-  196,66,117,184,212,                     //vfmadd231ps   %ymm12,%ymm1,%ymm10
-  196,66,125,184,211,                     //vfmadd231ps   %ymm11,%ymm0,%ymm10
-  196,98,125,24,96,12,                    //vbroadcastss  0xc(%rax),%ymm12
-  196,98,125,24,104,28,                   //vbroadcastss  0x1c(%rax),%ymm13
-  196,98,125,24,112,44,                   //vbroadcastss  0x2c(%rax),%ymm14
-  196,98,125,24,120,60,                   //vbroadcastss  0x3c(%rax),%ymm15
-  196,98,125,24,88,76,                    //vbroadcastss  0x4c(%rax),%ymm11
-  196,66,101,184,223,                     //vfmadd231ps   %ymm15,%ymm3,%ymm11
-  196,66,109,184,222,                     //vfmadd231ps   %ymm14,%ymm2,%ymm11
-  196,66,117,184,221,                     //vfmadd231ps   %ymm13,%ymm1,%ymm11
-  196,66,125,184,220,                     //vfmadd231ps   %ymm12,%ymm0,%ymm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  197,124,41,210,                         //vmovaps       %ymm10,%ymm2
-  197,124,41,219,                         //vmovaps       %ymm11,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,66,117,184,209,                     //vfmadd231ps   %ymm9,%ymm1,%ymm10
-  196,66,125,184,208,                     //vfmadd231ps   %ymm8,%ymm0,%ymm10
-  196,98,125,24,64,12,                    //vbroadcastss  0xc(%rax),%ymm8
-  196,98,125,24,72,16,                    //vbroadcastss  0x10(%rax),%ymm9
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  196,66,117,184,217,                     //vfmadd231ps   %ymm9,%ymm1,%ymm11
-  196,66,125,184,216,                     //vfmadd231ps   %ymm8,%ymm0,%ymm11
-  196,98,125,24,64,24,                    //vbroadcastss  0x18(%rax),%ymm8
-  196,98,125,24,72,28,                    //vbroadcastss  0x1c(%rax),%ymm9
-  196,98,125,24,96,32,                    //vbroadcastss  0x20(%rax),%ymm12
-  196,66,117,184,225,                     //vfmadd231ps   %ymm9,%ymm1,%ymm12
-  196,66,125,184,224,                     //vfmadd231ps   %ymm8,%ymm0,%ymm12
-  196,193,124,83,204,                     //vrcpps        %ymm12,%ymm1
-  197,172,89,193,                         //vmulps        %ymm1,%ymm10,%ymm0
-  197,164,89,201,                         //vmulps        %ymm1,%ymm11,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,72,16,                   //vbroadcastss  0x10(%rax),%ymm1
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,184,193,                     //vfmadd231ps   %ymm1,%ymm0,%ymm8
-  196,226,125,24,80,20,                   //vbroadcastss  0x14(%rax),%ymm2
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,184,202,                    //vfmadd231ps   %ymm2,%ymm0,%ymm1
-  196,226,125,24,88,24,                   //vbroadcastss  0x18(%rax),%ymm3
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  196,226,125,184,211,                    //vfmadd231ps   %ymm3,%ymm0,%ymm2
-  196,98,125,24,72,28,                    //vbroadcastss  0x1c(%rax),%ymm9
-  196,226,125,24,88,12,                   //vbroadcastss  0xc(%rax),%ymm3
-  196,194,125,184,217,                    //vfmadd231ps   %ymm9,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_start_pipeline_avx[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  83,                                     //push          %rbx
-  73,137,205,                             //mov           %rcx,%r13
-  73,137,214,                             //mov           %rdx,%r14
-  72,137,251,                             //mov           %rdi,%rbx
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,199,                             //mov           %rax,%r15
-  73,137,244,                             //mov           %rsi,%r12
-  72,141,67,8,                            //lea           0x8(%rbx),%rax
-  76,57,232,                              //cmp           %r13,%rax
-  118,5,                                  //jbe           28 <_sk_start_pipeline_avx+0x28>
-  72,137,223,                             //mov           %rbx,%rdi
-  235,65,                                 //jmp           69 <_sk_start_pipeline_avx+0x69>
-  185,0,0,0,0,                            //mov           $0x0,%ecx
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  72,141,123,8,                           //lea           0x8(%rbx),%rdi
-  72,131,195,16,                          //add           $0x10,%rbx
-  76,57,235,                              //cmp           %r13,%rbx
-  72,137,251,                             //mov           %rdi,%rbx
-  118,191,                                //jbe           28 <_sk_start_pipeline_avx+0x28>
-  76,137,233,                             //mov           %r13,%rcx
-  72,41,249,                              //sub           %rdi,%rcx
-  116,41,                                 //je            9a <_sk_start_pipeline_avx+0x9a>
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  76,137,232,                             //mov           %r13,%rax
-  91,                                     //pop           %rbx
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  197,248,119,                            //vzeroupper
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_avx[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,249,110,199,                        //vmovd         %edi,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  65,184,0,0,0,63,                        //mov           $0x3f000000,%r8d
-  196,193,121,110,200,                    //vmovd         %r8d,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  197,252,88,2,                           //vaddps        (%rdx),%ymm0,%ymm0
-  196,226,125,24,16,                      //vbroadcastss  (%rax),%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  197,236,88,201,                         //vaddps        %ymm1,%ymm2,%ymm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,208,                        //vmovd         %eax,%xmm2
-  196,227,121,4,210,0,                    //vpermilps     $0x0,%xmm2,%xmm2
-  196,227,109,24,210,1,                   //vinsertf128   $0x1,%xmm2,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  196,226,125,24,88,12,                   //vbroadcastss  0xc(%rax),%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__avx[] = {
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,92,195,                          //vsubps        %ymm3,%ymm8,%ymm8
-  197,60,89,204,                          //vmulps        %ymm4,%ymm8,%ymm9
-  197,180,88,192,                         //vaddps        %ymm0,%ymm9,%ymm0
-  197,60,89,205,                          //vmulps        %ymm5,%ymm8,%ymm9
-  197,180,88,201,                         //vaddps        %ymm1,%ymm9,%ymm1
-  197,60,89,206,                          //vmulps        %ymm6,%ymm8,%ymm9
-  197,180,88,210,                         //vaddps        %ymm2,%ymm9,%ymm2
-  197,60,89,199,                          //vmulps        %ymm7,%ymm8,%ymm8
-  197,188,88,219,                         //vaddps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,92,199,                          //vsubps        %ymm7,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_avx[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,193,124,95,192,                     //vmaxps        %ymm8,%ymm0,%ymm0
-  196,193,116,95,200,                     //vmaxps        %ymm8,%ymm1,%ymm1
-  196,193,108,95,208,                     //vmaxps        %ymm8,%ymm2,%ymm2
-  196,193,100,95,216,                     //vmaxps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  196,193,108,93,208,                     //vminps        %ymm8,%ymm2,%ymm2
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  197,252,93,195,                         //vminps        %ymm3,%ymm0,%ymm0
-  197,244,93,203,                         //vminps        %ymm3,%ymm1,%ymm1
-  197,236,93,211,                         //vminps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_avx[] = {
-  197,124,40,192,                         //vmovaps       %ymm0,%ymm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,194,                         //vmovaps       %ymm2,%ymm0
-  197,124,41,194,                         //vmovaps       %ymm8,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_avx[] = {
-  197,124,40,195,                         //vmovaps       %ymm3,%ymm8
-  197,124,40,202,                         //vmovaps       %ymm2,%ymm9
-  197,124,40,209,                         //vmovaps       %ymm1,%ymm10
-  197,124,40,216,                         //vmovaps       %ymm0,%ymm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  197,124,41,220,                         //vmovaps       %ymm11,%ymm4
-  197,124,41,213,                         //vmovaps       %ymm10,%ymm5
-  197,124,41,206,                         //vmovaps       %ymm9,%ymm6
-  197,124,41,199,                         //vmovaps       %ymm8,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,224,                         //vmovaps       %ymm0,%ymm4
-  197,252,40,233,                         //vmovaps       %ymm1,%ymm5
-  197,252,40,242,                         //vmovaps       %ymm2,%ymm6
-  197,252,40,251,                         //vmovaps       %ymm3,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_avx[] = {
-  197,252,89,195,                         //vmulps        %ymm3,%ymm0,%ymm0
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_avx[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,65,100,194,200,0,                   //vcmpeqps      %ymm8,%ymm3,%ymm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,67,121,4,210,0,                     //vpermilps     $0x0,%xmm10,%xmm10
-  196,67,45,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm10,%ymm10
-  197,44,94,211,                          //vdivps        %ymm3,%ymm10,%ymm10
-  196,67,45,74,192,144,                   //vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_avx[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  197,124,89,208,                         //vmulps        %ymm0,%ymm0,%ymm10
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  197,121,110,216,                        //vmovd         %eax,%xmm11
-  196,67,121,4,219,0,                     //vpermilps     $0x0,%xmm11,%xmm11
-  196,67,37,24,219,1,                     //vinsertf128   $0x1,%xmm11,%ymm11,%ymm11
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  197,121,110,224,                        //vmovd         %eax,%xmm12
-  196,67,121,4,228,0,                     //vpermilps     $0x0,%xmm12,%xmm12
-  196,67,29,24,228,1,                     //vinsertf128   $0x1,%xmm12,%ymm12,%ymm12
-  197,36,89,232,                          //vmulps        %ymm0,%ymm11,%ymm13
-  196,65,20,88,236,                       //vaddps        %ymm12,%ymm13,%ymm13
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,67,121,4,246,0,                     //vpermilps     $0x0,%xmm14,%xmm14
-  196,67,13,24,246,1,                     //vinsertf128   $0x1,%xmm14,%ymm14,%ymm14
-  196,65,44,89,213,                       //vmulps        %ymm13,%ymm10,%ymm10
-  196,65,12,88,210,                       //vaddps        %ymm10,%ymm14,%ymm10
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  197,121,110,232,                        //vmovd         %eax,%xmm13
-  196,67,121,4,237,0,                     //vpermilps     $0x0,%xmm13,%xmm13
-  196,67,21,24,237,1,                     //vinsertf128   $0x1,%xmm13,%ymm13,%ymm13
-  196,193,124,194,197,1,                  //vcmpltps      %ymm13,%ymm0,%ymm0
-  196,195,45,74,193,0,                    //vblendvps     %ymm0,%ymm9,%ymm10,%ymm0
-  197,60,89,201,                          //vmulps        %ymm1,%ymm8,%ymm9
-  197,116,89,209,                         //vmulps        %ymm1,%ymm1,%ymm10
-  197,36,89,249,                          //vmulps        %ymm1,%ymm11,%ymm15
-  196,65,28,88,255,                       //vaddps        %ymm15,%ymm12,%ymm15
-  196,65,44,89,215,                       //vmulps        %ymm15,%ymm10,%ymm10
-  196,65,12,88,210,                       //vaddps        %ymm10,%ymm14,%ymm10
-  196,193,116,194,205,1,                  //vcmpltps      %ymm13,%ymm1,%ymm1
-  196,195,45,74,201,16,                   //vblendvps     %ymm1,%ymm9,%ymm10,%ymm1
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  197,108,89,202,                         //vmulps        %ymm2,%ymm2,%ymm9
-  197,36,89,210,                          //vmulps        %ymm2,%ymm11,%ymm10
-  196,65,28,88,210,                       //vaddps        %ymm10,%ymm12,%ymm10
-  196,65,52,89,202,                       //vmulps        %ymm10,%ymm9,%ymm9
-  196,65,12,88,201,                       //vaddps        %ymm9,%ymm14,%ymm9
-  196,193,108,194,213,1,                  //vcmpltps      %ymm13,%ymm2,%ymm2
-  196,195,53,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_avx[] = {
-  197,124,82,192,                         //vrsqrtps      %ymm0,%ymm8
-  196,65,124,83,232,                      //vrcpps        %ymm8,%ymm13
-  196,65,124,82,240,                      //vrsqrtps      %ymm8,%ymm14
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,224,                          //vmulps        %ymm0,%ymm8,%ymm12
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,67,121,4,201,0,                     //vpermilps     $0x0,%xmm9,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,67,121,4,210,0,                     //vpermilps     $0x0,%xmm10,%xmm10
-  196,67,45,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm10,%ymm10
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  197,121,110,216,                        //vmovd         %eax,%xmm11
-  196,67,121,4,219,0,                     //vpermilps     $0x0,%xmm11,%xmm11
-  196,67,37,24,219,1,                     //vinsertf128   $0x1,%xmm11,%ymm11,%ymm11
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  197,121,110,248,                        //vmovd         %eax,%xmm15
-  196,67,121,4,255,0,                     //vpermilps     $0x0,%xmm15,%xmm15
-  196,67,5,24,255,1,                      //vinsertf128   $0x1,%xmm15,%ymm15,%ymm15
-  196,65,20,89,235,                       //vmulps        %ymm11,%ymm13,%ymm13
-  196,65,20,88,239,                       //vaddps        %ymm15,%ymm13,%ymm13
-  196,65,12,89,242,                       //vmulps        %ymm10,%ymm14,%ymm14
-  196,65,12,88,237,                       //vaddps        %ymm13,%ymm14,%ymm13
-  196,65,52,93,237,                       //vminps        %ymm13,%ymm9,%ymm13
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,67,121,4,246,0,                     //vpermilps     $0x0,%xmm14,%xmm14
-  196,67,13,24,246,1,                     //vinsertf128   $0x1,%xmm14,%ymm14,%ymm14
-  196,193,124,194,198,1,                  //vcmpltps      %ymm14,%ymm0,%ymm0
-  196,195,21,74,196,0,                    //vblendvps     %ymm0,%ymm12,%ymm13,%ymm0
-  197,124,82,225,                         //vrsqrtps      %ymm1,%ymm12
-  196,65,124,83,236,                      //vrcpps        %ymm12,%ymm13
-  196,65,124,82,228,                      //vrsqrtps      %ymm12,%ymm12
-  196,65,36,89,237,                       //vmulps        %ymm13,%ymm11,%ymm13
-  196,65,4,88,237,                        //vaddps        %ymm13,%ymm15,%ymm13
-  196,65,44,89,228,                       //vmulps        %ymm12,%ymm10,%ymm12
-  196,65,28,88,229,                       //vaddps        %ymm13,%ymm12,%ymm12
-  197,60,89,233,                          //vmulps        %ymm1,%ymm8,%ymm13
-  196,65,52,93,228,                       //vminps        %ymm12,%ymm9,%ymm12
-  196,193,116,194,206,1,                  //vcmpltps      %ymm14,%ymm1,%ymm1
-  196,195,29,74,205,16,                   //vblendvps     %ymm1,%ymm13,%ymm12,%ymm1
-  197,124,82,226,                         //vrsqrtps      %ymm2,%ymm12
-  196,65,124,83,236,                      //vrcpps        %ymm12,%ymm13
-  196,65,36,89,221,                       //vmulps        %ymm13,%ymm11,%ymm11
-  196,65,4,88,219,                        //vaddps        %ymm11,%ymm15,%ymm11
-  196,65,124,82,228,                      //vrsqrtps      %ymm12,%ymm12
-  196,65,44,89,212,                       //vmulps        %ymm12,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  196,65,52,93,202,                       //vminps        %ymm10,%ymm9,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,193,108,194,214,1,                  //vcmpltps      %ymm14,%ymm2,%ymm2
-  196,195,53,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_avx[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,80,                                 //jne           5a2 <_sk_scale_u8_avx+0x60>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,121,49,200,                      //vpmovzxbd     %xmm8,%xmm9
-  196,67,121,4,192,229,                   //vpermilps     $0xe5,%xmm8,%xmm8
-  196,66,121,49,192,                      //vpmovzxbd     %xmm8,%xmm8
-  196,67,53,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,67,121,4,201,0,                     //vpermilps     $0x0,%xmm9,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           5aa <_sk_scale_u8_avx+0x68>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  235,143,                                //jmp           556 <_sk_scale_u8_avx+0x14>
-};
-
-CODE const uint8_t sk_lerp_1_float_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_avx[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,116,                                //jne           68a <_sk_lerp_u8_avx+0x84>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,121,49,200,                      //vpmovzxbd     %xmm8,%xmm9
-  196,67,121,4,192,229,                   //vpermilps     $0xe5,%xmm8,%xmm8
-  196,66,121,49,192,                      //vpmovzxbd     %xmm8,%xmm8
-  196,67,53,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,67,121,4,201,0,                     //vpermilps     $0x0,%xmm9,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           692 <_sk_lerp_u8_avx+0x8c>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  233,104,255,255,255,                    //jmpq          61a <_sk_lerp_u8_avx+0x14>
-};
-
-CODE const uint8_t sk_lerp_565_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,250,0,0,0,                       //jne           7ba <_sk_lerp_565_avx+0x108>
-  196,65,122,111,4,122,                   //vmovdqu       (%r10,%rdi,2),%xmm8
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  197,185,105,219,                        //vpunpckhwd    %xmm3,%xmm8,%xmm3
-  196,66,121,51,192,                      //vpmovzxwd     %xmm8,%xmm8
-  196,99,61,24,195,1,                     //vinsertf128   $0x1,%xmm3,%ymm8,%ymm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  196,193,100,84,216,                     //vandps        %ymm8,%ymm3,%ymm3
-  197,124,91,203,                         //vcvtdq2ps     %ymm3,%ymm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,52,89,203,                          //vmulps        %ymm3,%ymm9,%ymm9
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  196,193,100,84,216,                     //vandps        %ymm8,%ymm3,%ymm3
-  197,124,91,211,                         //vcvtdq2ps     %ymm3,%ymm10
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,44,89,211,                          //vmulps        %ymm3,%ymm10,%ymm10
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  196,193,100,84,216,                     //vandps        %ymm8,%ymm3,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,193,124,89,193,                     //vmulps        %ymm9,%ymm0,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,193,116,89,202,                     //vmulps        %ymm10,%ymm1,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  196,65,57,239,192,                      //vpxor         %xmm8,%xmm8,%xmm8
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,243,254,255,255,                 //ja            6c6 <_sk_lerp_565_avx+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,74,0,0,0,                     //lea           0x4a(%rip),%r9        # 828 <_sk_lerp_565_avx+0x176>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  196,65,97,196,68,122,12,6,              //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm3,%xmm8
-  196,65,57,196,68,122,10,5,              //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,8,4,               //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,6,3,               //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,4,2,               //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,2,1,               //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,4,122,0,                  //vpinsrw       $0x0,(%r10,%rdi,2),%xmm8,%xmm8
-  233,159,254,255,255,                    //jmpq          6c6 <_sk_lerp_565_avx+0x14>
-  144,                                    //nop
-  243,255,                                //repz          (bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  235,255,                                //jmp           82d <_sk_lerp_565_avx+0x17b>
-  255,                                    //(bad)
-  255,227,                                //jmpq          *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  219,255,                                //(bad)
-  255,                                    //(bad)
-  255,211,                                //callq         *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,203,                                //dec           %ebx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  191,                                    //.byte         0xbf
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_tables_avx[] = {
-  85,                                     //push          %rbp
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  83,                                     //push          %rbx
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,56,2,0,0,                        //jne           a94 <_sk_load_tables_avx+0x250>
-  196,65,124,16,4,184,                    //vmovups       (%r8,%rdi,4),%ymm8
-  187,255,0,0,0,                          //mov           $0xff,%ebx
-  197,249,110,195,                        //vmovd         %ebx,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,99,125,24,200,1,                    //vinsertf128   $0x1,%xmm0,%ymm0,%ymm9
-  196,193,52,84,192,                      //vandps        %ymm8,%ymm9,%ymm0
-  196,193,249,126,193,                    //vmovq         %xmm0,%r9
-  69,137,203,                             //mov           %r9d,%r11d
-  196,195,249,22,194,1,                   //vpextrq       $0x1,%xmm0,%r10
-  69,137,214,                             //mov           %r10d,%r14d
-  73,193,234,32,                          //shr           $0x20,%r10
-  73,193,233,32,                          //shr           $0x20,%r9
-  196,227,125,25,192,1,                   //vextractf128  $0x1,%ymm0,%xmm0
-  196,193,249,126,196,                    //vmovq         %xmm0,%r12
-  69,137,231,                             //mov           %r12d,%r15d
-  196,227,249,22,195,1,                   //vpextrq       $0x1,%xmm0,%rbx
-  65,137,221,                             //mov           %ebx,%r13d
-  72,193,235,32,                          //shr           $0x20,%rbx
-  73,193,236,32,                          //shr           $0x20,%r12
-  72,139,104,8,                           //mov           0x8(%rax),%rbp
-  76,139,64,16,                           //mov           0x10(%rax),%r8
-  196,161,122,16,68,189,0,                //vmovss        0x0(%rbp,%r15,4),%xmm0
-  196,163,121,33,68,165,0,16,             //vinsertps     $0x10,0x0(%rbp,%r12,4),%xmm0,%xmm0
-  196,161,122,16,76,173,0,                //vmovss        0x0(%rbp,%r13,4),%xmm1
-  196,227,121,33,193,32,                  //vinsertps     $0x20,%xmm1,%xmm0,%xmm0
-  197,250,16,76,157,0,                    //vmovss        0x0(%rbp,%rbx,4),%xmm1
-  196,227,121,33,193,48,                  //vinsertps     $0x30,%xmm1,%xmm0,%xmm0
-  196,161,122,16,76,157,0,                //vmovss        0x0(%rbp,%r11,4),%xmm1
-  196,163,113,33,76,141,0,16,             //vinsertps     $0x10,0x0(%rbp,%r9,4),%xmm1,%xmm1
-  196,161,122,16,92,181,0,                //vmovss        0x0(%rbp,%r14,4),%xmm3
-  196,227,113,33,203,32,                  //vinsertps     $0x20,%xmm3,%xmm1,%xmm1
-  196,161,122,16,92,149,0,                //vmovss        0x0(%rbp,%r10,4),%xmm3
-  196,227,113,33,203,48,                  //vinsertps     $0x30,%xmm3,%xmm1,%xmm1
-  196,227,117,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
-  196,193,113,114,208,8,                  //vpsrld        $0x8,%xmm8,%xmm1
-  196,67,125,25,194,1,                    //vextractf128  $0x1,%ymm8,%xmm10
-  196,193,105,114,210,8,                  //vpsrld        $0x8,%xmm10,%xmm2
-  196,227,117,24,202,1,                   //vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
-  197,180,84,201,                         //vandps        %ymm1,%ymm9,%ymm1
-  196,193,249,126,201,                    //vmovq         %xmm1,%r9
-  69,137,203,                             //mov           %r9d,%r11d
-  196,195,249,22,202,1,                   //vpextrq       $0x1,%xmm1,%r10
-  69,137,214,                             //mov           %r10d,%r14d
-  73,193,234,32,                          //shr           $0x20,%r10
-  73,193,233,32,                          //shr           $0x20,%r9
-  196,227,125,25,201,1,                   //vextractf128  $0x1,%ymm1,%xmm1
-  196,225,249,126,205,                    //vmovq         %xmm1,%rbp
-  65,137,239,                             //mov           %ebp,%r15d
-  196,227,249,22,203,1,                   //vpextrq       $0x1,%xmm1,%rbx
-  65,137,220,                             //mov           %ebx,%r12d
-  72,193,235,32,                          //shr           $0x20,%rbx
-  72,193,237,32,                          //shr           $0x20,%rbp
-  196,129,122,16,12,184,                  //vmovss        (%r8,%r15,4),%xmm1
-  196,195,113,33,12,168,16,               //vinsertps     $0x10,(%r8,%rbp,4),%xmm1,%xmm1
-  196,129,122,16,20,160,                  //vmovss        (%r8,%r12,4),%xmm2
-  196,227,113,33,202,32,                  //vinsertps     $0x20,%xmm2,%xmm1,%xmm1
-  196,193,122,16,20,152,                  //vmovss        (%r8,%rbx,4),%xmm2
-  196,227,113,33,202,48,                  //vinsertps     $0x30,%xmm2,%xmm1,%xmm1
-  196,129,122,16,20,152,                  //vmovss        (%r8,%r11,4),%xmm2
-  196,131,105,33,20,136,16,               //vinsertps     $0x10,(%r8,%r9,4),%xmm2,%xmm2
-  196,129,122,16,28,176,                  //vmovss        (%r8,%r14,4),%xmm3
-  196,227,105,33,211,32,                  //vinsertps     $0x20,%xmm3,%xmm2,%xmm2
-  196,129,122,16,28,144,                  //vmovss        (%r8,%r10,4),%xmm3
-  196,227,105,33,211,48,                  //vinsertps     $0x30,%xmm3,%xmm2,%xmm2
-  196,227,109,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm2,%ymm1
-  72,139,64,24,                           //mov           0x18(%rax),%rax
-  196,193,105,114,208,16,                 //vpsrld        $0x10,%xmm8,%xmm2
-  196,193,97,114,210,16,                  //vpsrld        $0x10,%xmm10,%xmm3
-  196,227,109,24,211,1,                   //vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
-  197,180,84,210,                         //vandps        %ymm2,%ymm9,%ymm2
-  196,193,249,126,208,                    //vmovq         %xmm2,%r8
-  69,137,194,                             //mov           %r8d,%r10d
-  196,195,249,22,209,1,                   //vpextrq       $0x1,%xmm2,%r9
-  69,137,203,                             //mov           %r9d,%r11d
-  73,193,233,32,                          //shr           $0x20,%r9
-  73,193,232,32,                          //shr           $0x20,%r8
-  196,227,125,25,210,1,                   //vextractf128  $0x1,%ymm2,%xmm2
-  196,225,249,126,213,                    //vmovq         %xmm2,%rbp
-  65,137,238,                             //mov           %ebp,%r14d
-  196,227,249,22,211,1,                   //vpextrq       $0x1,%xmm2,%rbx
-  65,137,223,                             //mov           %ebx,%r15d
-  72,193,235,32,                          //shr           $0x20,%rbx
-  72,193,237,32,                          //shr           $0x20,%rbp
-  196,161,122,16,20,176,                  //vmovss        (%rax,%r14,4),%xmm2
-  196,227,105,33,20,168,16,               //vinsertps     $0x10,(%rax,%rbp,4),%xmm2,%xmm2
-  196,161,122,16,28,184,                  //vmovss        (%rax,%r15,4),%xmm3
-  196,227,105,33,211,32,                  //vinsertps     $0x20,%xmm3,%xmm2,%xmm2
-  197,250,16,28,152,                      //vmovss        (%rax,%rbx,4),%xmm3
-  196,99,105,33,203,48,                   //vinsertps     $0x30,%xmm3,%xmm2,%xmm9
-  196,161,122,16,28,144,                  //vmovss        (%rax,%r10,4),%xmm3
-  196,163,97,33,28,128,16,                //vinsertps     $0x10,(%rax,%r8,4),%xmm3,%xmm3
-  196,161,122,16,20,152,                  //vmovss        (%rax,%r11,4),%xmm2
-  196,227,97,33,210,32,                   //vinsertps     $0x20,%xmm2,%xmm3,%xmm2
-  196,161,122,16,28,136,                  //vmovss        (%rax,%r9,4),%xmm3
-  196,227,105,33,211,48,                  //vinsertps     $0x30,%xmm3,%xmm2,%xmm2
-  196,195,109,24,209,1,                   //vinsertf128   $0x1,%xmm9,%ymm2,%ymm2
-  196,193,57,114,208,24,                  //vpsrld        $0x18,%xmm8,%xmm8
-  196,193,97,114,210,24,                  //vpsrld        $0x18,%xmm10,%xmm3
-  196,227,61,24,219,1,                    //vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  91,                                     //pop           %rbx
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  93,                                     //pop           %rbp
-  255,224,                                //jmpq          *%rax
-  137,203,                                //mov           %ecx,%ebx
-  128,227,7,                              //and           $0x7,%bl
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  254,203,                                //dec           %bl
-  128,251,6,                              //cmp           $0x6,%bl
-  15,135,185,253,255,255,                 //ja            862 <_sk_load_tables_avx+0x1e>
-  15,182,219,                             //movzbl        %bl,%ebx
-  76,141,13,137,0,0,0,                    //lea           0x89(%rip),%r9        # b3c <_sk_load_tables_avx+0x2f8>
-  73,99,28,153,                           //movslq        (%r9,%rbx,4),%rbx
-  76,1,203,                               //add           %r9,%rbx
-  255,227,                                //jmpq          *%rbx
-  196,193,121,110,68,184,24,              //vmovd         0x18(%r8,%rdi,4),%xmm0
-  197,249,112,192,68,                     //vpshufd       $0x44,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  196,99,117,12,192,64,                   //vblendps      $0x40,%ymm0,%ymm1,%ymm8
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,195,121,34,68,184,20,1,             //vpinsrd       $0x1,0x14(%r8,%rdi,4),%xmm0,%xmm0
-  196,99,61,24,192,1,                     //vinsertf128   $0x1,%xmm0,%ymm8,%ymm8
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,195,121,34,68,184,16,0,             //vpinsrd       $0x0,0x10(%r8,%rdi,4),%xmm0,%xmm0
-  196,99,61,24,192,1,                     //vinsertf128   $0x1,%xmm0,%ymm8,%ymm8
-  196,195,57,34,68,184,12,3,              //vpinsrd       $0x3,0xc(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  196,195,57,34,68,184,8,2,               //vpinsrd       $0x2,0x8(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  196,195,57,34,68,184,4,1,               //vpinsrd       $0x1,0x4(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  196,195,57,34,4,184,0,                  //vpinsrd       $0x0,(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  233,38,253,255,255,                     //jmpq          862 <_sk_load_tables_avx+0x1e>
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,224,                                //jmpq          *%rax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,210,                                //callq         *%rdx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,196,                                //inc           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,176,255,255,255,156,                //pushq         -0x63000001(%rax)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-  128,255,255,                            //cmp           $0xff,%bh
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_a8_avx[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,74,                                 //jne           bb2 <_sk_load_a8_avx+0x5a>
-  197,250,126,0,                          //vmovq         (%rax),%xmm0
-  196,226,121,49,200,                     //vpmovzxbd     %xmm0,%xmm1
-  196,227,121,4,192,229,                  //vpermilps     $0xe5,%xmm0,%xmm0
-  196,226,121,49,192,                     //vpmovzxbd     %xmm0,%xmm0
-  196,227,117,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,252,89,217,                         //vmulps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           bba <_sk_load_a8_avx+0x62>
-  196,193,249,110,193,                    //vmovq         %r9,%xmm0
-  235,149,                                //jmp           b6c <_sk_load_a8_avx+0x14>
-};
-
-CODE const uint8_t sk_store_a8_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  196,65,57,103,192,                      //vpackuswb     %xmm8,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           c19 <_sk_store_a8_avx+0x42>
-  196,65,123,17,4,57,                     //vmovsd        %xmm8,(%r9,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            c15 <_sk_store_a8_avx+0x3e>
-  196,66,121,48,192,                      //vpmovzxbw     %xmm8,%xmm8
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,67,0,0,0,                      //lea           0x43(%rip),%r8        # c7c <_sk_store_a8_avx+0xa5>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,20,68,57,6,12,               //vpextrb       $0xc,%xmm8,0x6(%r9,%rdi,1)
-  196,67,121,20,68,57,5,10,               //vpextrb       $0xa,%xmm8,0x5(%r9,%rdi,1)
-  196,67,121,20,68,57,4,8,                //vpextrb       $0x8,%xmm8,0x4(%r9,%rdi,1)
-  196,67,121,20,68,57,3,6,                //vpextrb       $0x6,%xmm8,0x3(%r9,%rdi,1)
-  196,67,121,20,68,57,2,4,                //vpextrb       $0x4,%xmm8,0x2(%r9,%rdi,1)
-  196,67,121,20,68,57,1,2,                //vpextrb       $0x2,%xmm8,0x1(%r9,%rdi,1)
-  196,67,121,20,4,57,0,                   //vpextrb       $0x0,%xmm8,(%r9,%rdi,1)
-  235,154,                                //jmp           c15 <_sk_store_a8_avx+0x3e>
-  144,                                    //nop
-  246,255,                                //idiv          %bh
-  255,                                    //(bad)
-  255,                                    //(bad)
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,230,                                //jmpq          *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  222,255,                                //fdivrp        %st,%st(7)
-  255,                                    //(bad)
-  255,214,                                //callq         *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,206,                                //dec           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,198,                                //inc           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_565_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,209,0,0,0,                       //jne           d77 <_sk_load_565_avx+0xdf>
-  196,193,122,111,4,122,                  //vmovdqu       (%r10,%rdi,2),%xmm0
-  197,241,239,201,                        //vpxor         %xmm1,%xmm1,%xmm1
-  197,249,105,201,                        //vpunpckhwd    %xmm1,%xmm0,%xmm1
-  196,226,121,51,192,                     //vpmovzxwd     %xmm0,%xmm0
-  196,227,125,24,209,1,                   //vinsertf128   $0x1,%xmm1,%ymm0,%ymm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,252,84,194,                         //vandps        %ymm2,%ymm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,252,89,193,                         //vmulps        %ymm1,%ymm0,%ymm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  197,249,112,201,0,                      //vpshufd       $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,244,84,202,                         //vandps        %ymm2,%ymm1,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,228,84,210,                         //vandps        %ymm2,%ymm3,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,29,255,255,255,                  //ja            cac <_sk_load_565_avx+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,74,0,0,0,                     //lea           0x4a(%rip),%r9        # de4 <_sk_load_565_avx+0x14c>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  196,193,121,196,68,122,12,6,            //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,10,5,            //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,8,4,             //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,6,3,             //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,4,2,             //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,2,1,             //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,4,122,0,                //vpinsrw       $0x0,(%r10,%rdi,2),%xmm0,%xmm0
-  233,201,254,255,255,                    //jmpq          cac <_sk_load_565_avx+0x14>
-  144,                                    //nop
-  243,255,                                //repz          (bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  235,255,                                //jmp           de9 <_sk_load_565_avx+0x151>
-  255,                                    //(bad)
-  255,227,                                //jmpq          *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  219,255,                                //(bad)
-  255,                                    //(bad)
-  255,211,                                //callq         *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,203,                                //dec           %ebx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  191,                                    //.byte         0xbf
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_store_565_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,248,65,                         //mov           $0x41f80000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  196,193,41,114,241,11,                  //vpslld        $0xb,%xmm9,%xmm10
-  196,67,125,25,201,1,                    //vextractf128  $0x1,%ymm9,%xmm9
-  196,193,49,114,241,11,                  //vpslld        $0xb,%xmm9,%xmm9
-  196,67,45,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
-  184,0,0,124,66,                         //mov           $0x427c0000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,67,121,4,210,0,                     //vpermilps     $0x0,%xmm10,%xmm10
-  196,67,45,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm10,%ymm10
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,33,114,242,5,                   //vpslld        $0x5,%xmm10,%xmm11
-  196,67,125,25,210,1,                    //vextractf128  $0x1,%ymm10,%xmm10
-  196,193,41,114,242,5,                   //vpslld        $0x5,%xmm10,%xmm10
-  196,67,37,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
-  196,65,45,86,201,                       //vorpd         %ymm9,%ymm10,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,65,53,86,192,                       //vorpd         %ymm8,%ymm9,%ymm8
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           e9e <_sk_store_565_avx+0x9e>
-  196,65,122,127,4,121,                   //vmovdqu       %xmm8,(%r9,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            e9a <_sk_store_565_avx+0x9a>
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,67,0,0,0,                      //lea           0x43(%rip),%r8        # efc <_sk_store_565_avx+0xfc>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,21,68,121,12,6,              //vpextrw       $0x6,%xmm8,0xc(%r9,%rdi,2)
-  196,67,121,21,68,121,10,5,              //vpextrw       $0x5,%xmm8,0xa(%r9,%rdi,2)
-  196,67,121,21,68,121,8,4,               //vpextrw       $0x4,%xmm8,0x8(%r9,%rdi,2)
-  196,67,121,21,68,121,6,3,               //vpextrw       $0x3,%xmm8,0x6(%r9,%rdi,2)
-  196,67,121,21,68,121,4,2,               //vpextrw       $0x2,%xmm8,0x4(%r9,%rdi,2)
-  196,67,121,21,68,121,2,1,               //vpextrw       $0x1,%xmm8,0x2(%r9,%rdi,2)
-  196,67,121,21,4,121,0,                  //vpextrw       $0x0,%xmm8,(%r9,%rdi,2)
-  235,159,                                //jmp           e9a <_sk_store_565_avx+0x9a>
-  144,                                    //nop
-  246,255,                                //idiv          %bh
-  255,                                    //(bad)
-  255,                                    //(bad)
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,230,                                //jmpq          *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  222,255,                                //fdivrp        %st,%st(7)
-  255,                                    //(bad)
-  255,214,                                //callq         *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,206,                                //dec           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,198,                                //inc           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_8888_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,157,0,0,0,                       //jne           fc3 <_sk_load_8888_avx+0xab>
-  196,65,124,16,12,186,                   //vmovups       (%r10,%rdi,4),%ymm9
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,99,125,24,216,1,                    //vinsertf128   $0x1,%xmm0,%ymm0,%ymm11
-  196,193,36,84,193,                      //vandps        %ymm9,%ymm11,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,99,117,24,193,1,                    //vinsertf128   $0x1,%xmm1,%ymm1,%ymm8
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  196,193,41,114,209,8,                   //vpsrld        $0x8,%xmm9,%xmm10
-  196,99,125,25,203,1,                    //vextractf128  $0x1,%ymm9,%xmm3
-  197,241,114,211,8,                      //vpsrld        $0x8,%xmm3,%xmm1
-  196,227,45,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm10,%ymm1
-  197,164,84,201,                         //vandps        %ymm1,%ymm11,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  196,193,41,114,209,16,                  //vpsrld        $0x10,%xmm9,%xmm10
-  197,233,114,211,16,                     //vpsrld        $0x10,%xmm3,%xmm2
-  196,227,45,24,210,1,                    //vinsertf128   $0x1,%xmm2,%ymm10,%ymm2
-  197,164,84,210,                         //vandps        %ymm2,%ymm11,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  196,193,49,114,209,24,                  //vpsrld        $0x18,%xmm9,%xmm9
-  197,225,114,211,24,                     //vpsrld        $0x18,%xmm3,%xmm3
-  196,227,53,24,219,1,                    //vinsertf128   $0x1,%xmm3,%ymm9,%ymm3
-  197,252,91,219,                         //vcvtdq2ps     %ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  196,65,52,87,201,                       //vxorps        %ymm9,%ymm9,%ymm9
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,80,255,255,255,                  //ja            f2c <_sk_load_8888_avx+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,137,0,0,0,                    //lea           0x89(%rip),%r9        # 1070 <_sk_load_8888_avx+0x158>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  196,193,121,110,68,186,24,              //vmovd         0x18(%r10,%rdi,4),%xmm0
-  197,249,112,192,68,                     //vpshufd       $0x44,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  196,99,117,12,200,64,                   //vblendps      $0x40,%ymm0,%ymm1,%ymm9
-  196,99,125,25,200,1,                    //vextractf128  $0x1,%ymm9,%xmm0
-  196,195,121,34,68,186,20,1,             //vpinsrd       $0x1,0x14(%r10,%rdi,4),%xmm0,%xmm0
-  196,99,53,24,200,1,                     //vinsertf128   $0x1,%xmm0,%ymm9,%ymm9
-  196,99,125,25,200,1,                    //vextractf128  $0x1,%ymm9,%xmm0
-  196,195,121,34,68,186,16,0,             //vpinsrd       $0x0,0x10(%r10,%rdi,4),%xmm0,%xmm0
-  196,99,53,24,200,1,                     //vinsertf128   $0x1,%xmm0,%ymm9,%ymm9
-  196,195,49,34,68,186,12,3,              //vpinsrd       $0x3,0xc(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  196,195,49,34,68,186,8,2,               //vpinsrd       $0x2,0x8(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  196,195,49,34,68,186,4,1,               //vpinsrd       $0x1,0x4(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  196,195,49,34,4,186,0,                  //vpinsrd       $0x0,(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  233,188,254,255,255,                    //jmpq          f2c <_sk_load_8888_avx+0x14>
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,224,                                //jmpq          *%rax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,210,                                //callq         *%rdx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,196,                                //inc           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,176,255,255,255,156,                //pushq         -0x63000001(%rax)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-  128,255,255,                            //cmp           $0xff,%bh
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_store_8888_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  197,60,89,209,                          //vmulps        %ymm1,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,33,114,242,8,                   //vpslld        $0x8,%xmm10,%xmm11
-  196,67,125,25,210,1,                    //vextractf128  $0x1,%ymm10,%xmm10
-  196,193,41,114,242,8,                   //vpslld        $0x8,%xmm10,%xmm10
-  196,67,37,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
-  196,65,45,86,201,                       //vorpd         %ymm9,%ymm10,%ymm9
-  197,60,89,210,                          //vmulps        %ymm2,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,33,114,242,16,                  //vpslld        $0x10,%xmm10,%xmm11
-  196,67,125,25,210,1,                    //vextractf128  $0x1,%ymm10,%xmm10
-  196,193,41,114,242,16,                  //vpslld        $0x10,%xmm10,%xmm10
-  196,67,37,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,193,33,114,240,24,                  //vpslld        $0x18,%xmm8,%xmm11
-  196,67,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm8
-  196,193,57,114,240,24,                  //vpslld        $0x18,%xmm8,%xmm8
-  196,67,37,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm11,%ymm8
-  196,65,45,86,192,                       //vorpd         %ymm8,%ymm10,%ymm8
-  196,65,53,86,192,                       //vorpd         %ymm8,%ymm9,%ymm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           1130 <_sk_store_8888_avx+0xa4>
-  196,65,124,17,4,185,                    //vmovups       %ymm8,(%r9,%rdi,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            112c <_sk_store_8888_avx+0xa0>
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,85,0,0,0,                      //lea           0x55(%rip),%r8        # 11a0 <_sk_store_8888_avx+0x114>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,67,121,22,76,185,24,2,              //vpextrd       $0x2,%xmm9,0x18(%r9,%rdi,4)
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,67,121,22,76,185,20,1,              //vpextrd       $0x1,%xmm9,0x14(%r9,%rdi,4)
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,65,122,17,76,185,16,                //vmovss        %xmm9,0x10(%r9,%rdi,4)
-  196,67,121,22,68,185,12,3,              //vpextrd       $0x3,%xmm8,0xc(%r9,%rdi,4)
-  196,67,121,22,68,185,8,2,               //vpextrd       $0x2,%xmm8,0x8(%r9,%rdi,4)
-  196,67,121,22,68,185,4,1,               //vpextrd       $0x1,%xmm8,0x4(%r9,%rdi,4)
-  196,65,121,126,4,185,                   //vmovd         %xmm8,(%r9,%rdi,4)
-  235,143,                                //jmp           112c <_sk_store_8888_avx+0xa0>
-  15,31,0,                                //nopl          (%rax)
-  245,                                    //cmc
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  237,                                    //in            (%dx),%eax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,229,                                //jmpq          *%rbp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  221,255,                                //(bad)
-  255,                                    //(bad)
-  255,208,                                //callq         *%rax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,194,                                //inc           %edx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-  180,255,                                //mov           $0xff,%ah
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_f16_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,2,1,0,0,                         //jne           12cc <_sk_load_f16_avx+0x110>
-  197,121,16,4,248,                       //vmovupd       (%rax,%rdi,8),%xmm8
-  197,249,16,84,248,16,                   //vmovupd       0x10(%rax,%rdi,8),%xmm2
-  197,249,16,92,248,32,                   //vmovupd       0x20(%rax,%rdi,8),%xmm3
-  197,122,111,76,248,48,                  //vmovdqu       0x30(%rax,%rdi,8),%xmm9
-  197,185,97,194,                         //vpunpcklwd    %xmm2,%xmm8,%xmm0
-  197,185,105,210,                        //vpunpckhwd    %xmm2,%xmm8,%xmm2
-  196,193,97,97,201,                      //vpunpcklwd    %xmm9,%xmm3,%xmm1
-  196,193,97,105,217,                     //vpunpckhwd    %xmm9,%xmm3,%xmm3
-  197,121,97,194,                         //vpunpcklwd    %xmm2,%xmm0,%xmm8
-  197,249,105,194,                        //vpunpckhwd    %xmm2,%xmm0,%xmm0
-  197,241,97,211,                         //vpunpcklwd    %xmm3,%xmm1,%xmm2
-  197,113,105,203,                        //vpunpckhwd    %xmm3,%xmm1,%xmm9
-  184,0,4,0,4,                            //mov           $0x4000400,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,193,97,101,200,                     //vpcmpgtw      %xmm8,%xmm3,%xmm1
-  196,65,113,223,192,                     //vpandn        %xmm8,%xmm1,%xmm8
-  197,225,101,200,                        //vpcmpgtw      %xmm0,%xmm3,%xmm1
-  197,241,223,192,                        //vpandn        %xmm0,%xmm1,%xmm0
-  197,225,101,202,                        //vpcmpgtw      %xmm2,%xmm3,%xmm1
-  197,241,223,202,                        //vpandn        %xmm2,%xmm1,%xmm1
-  196,193,97,101,209,                     //vpcmpgtw      %xmm9,%xmm3,%xmm2
-  196,193,105,223,209,                    //vpandn        %xmm9,%xmm2,%xmm2
-  196,66,121,51,208,                      //vpmovzxwd     %xmm8,%xmm10
-  196,98,121,51,201,                      //vpmovzxwd     %xmm1,%xmm9
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  197,57,105,195,                         //vpunpckhwd    %xmm3,%xmm8,%xmm8
-  197,241,105,203,                        //vpunpckhwd    %xmm3,%xmm1,%xmm1
-  196,98,121,51,216,                      //vpmovzxwd     %xmm0,%xmm11
-  196,98,121,51,226,                      //vpmovzxwd     %xmm2,%xmm12
-  197,121,105,235,                        //vpunpckhwd    %xmm3,%xmm0,%xmm13
-  197,105,105,243,                        //vpunpckhwd    %xmm3,%xmm2,%xmm14
-  196,193,121,114,242,13,                 //vpslld        $0xd,%xmm10,%xmm0
-  196,193,105,114,241,13,                 //vpslld        $0xd,%xmm9,%xmm2
-  196,227,125,24,194,1,                   //vinsertf128   $0x1,%xmm2,%ymm0,%ymm0
-  184,0,0,128,119,                        //mov           $0x77800000,%eax
-  197,249,110,208,                        //vmovd         %eax,%xmm2
-  197,249,112,210,0,                      //vpshufd       $0x0,%xmm2,%xmm2
-  196,99,109,24,202,1,                    //vinsertf128   $0x1,%xmm2,%ymm2,%ymm9
-  197,180,89,192,                         //vmulps        %ymm0,%ymm9,%ymm0
-  196,193,105,114,240,13,                 //vpslld        $0xd,%xmm8,%xmm2
-  197,241,114,241,13,                     //vpslld        $0xd,%xmm1,%xmm1
-  196,227,109,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm2,%ymm1
-  197,180,89,201,                         //vmulps        %ymm1,%ymm9,%ymm1
-  196,193,105,114,243,13,                 //vpslld        $0xd,%xmm11,%xmm2
-  196,193,97,114,244,13,                  //vpslld        $0xd,%xmm12,%xmm3
-  196,227,109,24,211,1,                   //vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
-  197,180,89,210,                         //vmulps        %ymm2,%ymm9,%ymm2
-  196,193,57,114,245,13,                  //vpslld        $0xd,%xmm13,%xmm8
-  196,193,97,114,246,13,                  //vpslld        $0xd,%xmm14,%xmm3
-  196,227,61,24,219,1,                    //vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
-  197,180,89,219,                         //vmulps        %ymm3,%ymm9,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  197,123,16,4,248,                       //vmovsd        (%rax,%rdi,8),%xmm8
-  196,65,49,239,201,                      //vpxor         %xmm9,%xmm9,%xmm9
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,79,                                 //je            132b <_sk_load_f16_avx+0x16f>
-  197,57,22,68,248,8,                     //vmovhpd       0x8(%rax,%rdi,8),%xmm8,%xmm8
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,67,                                 //jb            132b <_sk_load_f16_avx+0x16f>
-  197,251,16,84,248,16,                   //vmovsd        0x10(%rax,%rdi,8),%xmm2
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  116,68,                                 //je            1338 <_sk_load_f16_avx+0x17c>
-  197,233,22,84,248,24,                   //vmovhpd       0x18(%rax,%rdi,8),%xmm2,%xmm2
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,56,                                 //jb            1338 <_sk_load_f16_avx+0x17c>
-  197,251,16,92,248,32,                   //vmovsd        0x20(%rax,%rdi,8),%xmm3
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  15,132,209,254,255,255,                 //je            11e1 <_sk_load_f16_avx+0x25>
-  197,225,22,92,248,40,                   //vmovhpd       0x28(%rax,%rdi,8),%xmm3,%xmm3
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  15,130,193,254,255,255,                 //jb            11e1 <_sk_load_f16_avx+0x25>
-  197,122,126,76,248,48,                  //vmovq         0x30(%rax,%rdi,8),%xmm9
-  233,182,254,255,255,                    //jmpq          11e1 <_sk_load_f16_avx+0x25>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  197,233,87,210,                         //vxorpd        %xmm2,%xmm2,%xmm2
-  233,169,254,255,255,                    //jmpq          11e1 <_sk_load_f16_avx+0x25>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  233,160,254,255,255,                    //jmpq          11e1 <_sk_load_f16_avx+0x25>
-};
-
-CODE const uint8_t sk_store_f16_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  184,0,0,128,7,                          //mov           $0x7800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,65,121,112,192,0,                   //vpshufd       $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,67,125,25,202,1,                    //vextractf128  $0x1,%ymm9,%xmm10
-  196,193,41,114,210,13,                  //vpsrld        $0xd,%xmm10,%xmm10
-  196,193,49,114,209,13,                  //vpsrld        $0xd,%xmm9,%xmm9
-  197,60,89,217,                          //vmulps        %ymm1,%ymm8,%ymm11
-  196,67,125,25,220,1,                    //vextractf128  $0x1,%ymm11,%xmm12
-  196,193,25,114,212,13,                  //vpsrld        $0xd,%xmm12,%xmm12
-  196,193,33,114,211,13,                  //vpsrld        $0xd,%xmm11,%xmm11
-  197,60,89,234,                          //vmulps        %ymm2,%ymm8,%ymm13
-  196,67,125,25,238,1,                    //vextractf128  $0x1,%ymm13,%xmm14
-  196,193,9,114,214,13,                   //vpsrld        $0xd,%xmm14,%xmm14
-  196,193,17,114,213,13,                  //vpsrld        $0xd,%xmm13,%xmm13
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,67,125,25,199,1,                    //vextractf128  $0x1,%ymm8,%xmm15
-  196,193,1,114,215,13,                   //vpsrld        $0xd,%xmm15,%xmm15
-  196,193,57,114,208,13,                  //vpsrld        $0xd,%xmm8,%xmm8
-  196,193,33,115,251,2,                   //vpslldq       $0x2,%xmm11,%xmm11
-  196,65,33,235,201,                      //vpor          %xmm9,%xmm11,%xmm9
-  196,193,33,115,252,2,                   //vpslldq       $0x2,%xmm12,%xmm11
-  196,65,33,235,226,                      //vpor          %xmm10,%xmm11,%xmm12
-  196,193,57,115,248,2,                   //vpslldq       $0x2,%xmm8,%xmm8
-  196,65,57,235,197,                      //vpor          %xmm13,%xmm8,%xmm8
-  196,193,41,115,255,2,                   //vpslldq       $0x2,%xmm15,%xmm10
-  196,65,41,235,238,                      //vpor          %xmm14,%xmm10,%xmm13
-  196,65,49,98,216,                       //vpunpckldq    %xmm8,%xmm9,%xmm11
-  196,65,49,106,208,                      //vpunpckhdq    %xmm8,%xmm9,%xmm10
-  196,65,25,98,205,                       //vpunpckldq    %xmm13,%xmm12,%xmm9
-  196,65,25,106,197,                      //vpunpckhdq    %xmm13,%xmm12,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,31,                                 //jne           1417 <_sk_store_f16_avx+0xd6>
-  196,65,120,17,28,248,                   //vmovups       %xmm11,(%r8,%rdi,8)
-  196,65,120,17,84,248,16,                //vmovups       %xmm10,0x10(%r8,%rdi,8)
-  196,65,120,17,76,248,32,                //vmovups       %xmm9,0x20(%r8,%rdi,8)
-  196,65,122,127,68,248,48,               //vmovdqu       %xmm8,0x30(%r8,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  196,65,121,214,28,248,                  //vmovq         %xmm11,(%r8,%rdi,8)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,240,                                //je            1413 <_sk_store_f16_avx+0xd2>
-  196,65,121,23,92,248,8,                 //vmovhpd       %xmm11,0x8(%r8,%rdi,8)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,227,                                //jb            1413 <_sk_store_f16_avx+0xd2>
-  196,65,121,214,84,248,16,               //vmovq         %xmm10,0x10(%r8,%rdi,8)
-  116,218,                                //je            1413 <_sk_store_f16_avx+0xd2>
-  196,65,121,23,84,248,24,                //vmovhpd       %xmm10,0x18(%r8,%rdi,8)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,205,                                //jb            1413 <_sk_store_f16_avx+0xd2>
-  196,65,121,214,76,248,32,               //vmovq         %xmm9,0x20(%r8,%rdi,8)
-  116,196,                                //je            1413 <_sk_store_f16_avx+0xd2>
-  196,65,121,23,76,248,40,                //vmovhpd       %xmm9,0x28(%r8,%rdi,8)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,183,                                //jb            1413 <_sk_store_f16_avx+0xd2>
-  196,65,121,214,68,248,48,               //vmovq         %xmm8,0x30(%r8,%rdi,8)
-  235,174,                                //jmp           1413 <_sk_store_f16_avx+0xd2>
-};
-
-CODE const uint8_t sk_store_f32_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  72,141,4,189,0,0,0,0,                   //lea           0x0(,%rdi,4),%rax
-  197,124,20,193,                         //vunpcklps     %ymm1,%ymm0,%ymm8
-  197,124,21,217,                         //vunpckhps     %ymm1,%ymm0,%ymm11
-  197,108,20,203,                         //vunpcklps     %ymm3,%ymm2,%ymm9
-  197,108,21,227,                         //vunpckhps     %ymm3,%ymm2,%ymm12
-  196,65,61,20,209,                       //vunpcklpd     %ymm9,%ymm8,%ymm10
-  196,65,61,21,201,                       //vunpckhpd     %ymm9,%ymm8,%ymm9
-  196,65,37,20,196,                       //vunpcklpd     %ymm12,%ymm11,%ymm8
-  196,65,37,21,220,                       //vunpckhpd     %ymm12,%ymm11,%ymm11
-  72,133,201,                             //test          %rcx,%rcx
-  117,55,                                 //jne           14d2 <_sk_store_f32_avx+0x6d>
-  196,67,45,24,225,1,                     //vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
-  196,67,61,24,235,1,                     //vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
-  196,67,45,6,201,49,                     //vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
-  196,67,61,6,195,49,                     //vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
-  196,65,125,17,36,128,                   //vmovupd       %ymm12,(%r8,%rax,4)
-  196,65,125,17,108,128,32,               //vmovupd       %ymm13,0x20(%r8,%rax,4)
-  196,65,125,17,76,128,64,                //vmovupd       %ymm9,0x40(%r8,%rax,4)
-  196,65,125,17,68,128,96,                //vmovupd       %ymm8,0x60(%r8,%rax,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  196,65,121,17,20,128,                   //vmovupd       %xmm10,(%r8,%rax,4)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,240,                                //je            14ce <_sk_store_f32_avx+0x69>
-  196,65,121,17,76,128,16,                //vmovupd       %xmm9,0x10(%r8,%rax,4)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,227,                                //jb            14ce <_sk_store_f32_avx+0x69>
-  196,65,121,17,68,128,32,                //vmovupd       %xmm8,0x20(%r8,%rax,4)
-  116,218,                                //je            14ce <_sk_store_f32_avx+0x69>
-  196,65,121,17,92,128,48,                //vmovupd       %xmm11,0x30(%r8,%rax,4)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,205,                                //jb            14ce <_sk_store_f32_avx+0x69>
-  196,67,125,25,84,128,64,1,              //vextractf128  $0x1,%ymm10,0x40(%r8,%rax,4)
-  116,195,                                //je            14ce <_sk_store_f32_avx+0x69>
-  196,67,125,25,76,128,80,1,              //vextractf128  $0x1,%ymm9,0x50(%r8,%rax,4)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,181,                                //jb            14ce <_sk_store_f32_avx+0x69>
-  196,67,125,25,68,128,96,1,              //vextractf128  $0x1,%ymm8,0x60(%r8,%rax,4)
-  235,171,                                //jmp           14ce <_sk_store_f32_avx+0x69>
-};
-
-CODE const uint8_t sk_clamp_x_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,95,200,                          //vmaxps        %ymm0,%ymm8,%ymm9
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,121,254,194,                    //vpaddd        %xmm10,%xmm0,%xmm0
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,192,1,                    //vinsertf128   $0x1,%xmm0,%ymm8,%ymm0
-  197,180,93,192,                         //vminps        %ymm0,%ymm9,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,95,201,                          //vmaxps        %ymm1,%ymm8,%ymm9
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,99,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm1
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,113,254,202,                    //vpaddd        %xmm10,%xmm1,%xmm1
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm8,%ymm1
-  197,180,93,201,                         //vminps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,124,94,200,                      //vdivps        %ymm8,%ymm0,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,65,52,89,200,                       //vmulps        %ymm8,%ymm9,%ymm9
-  196,65,124,92,201,                      //vsubps        %ymm9,%ymm0,%ymm9
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,121,254,194,                    //vpaddd        %xmm10,%xmm0,%xmm0
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,192,1,                    //vinsertf128   $0x1,%xmm0,%ymm8,%ymm0
-  197,180,93,192,                         //vminps        %ymm0,%ymm9,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,116,94,200,                      //vdivps        %ymm8,%ymm1,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,65,52,89,200,                       //vmulps        %ymm8,%ymm9,%ymm9
-  196,65,116,92,201,                      //vsubps        %ymm9,%ymm1,%ymm9
-  196,99,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm1
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,113,254,202,                    //vpaddd        %xmm10,%xmm1,%xmm1
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm8,%ymm1
-  197,180,93,201,                         //vminps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,121,110,0,                          //vmovd         (%rax),%xmm8
-  196,65,121,112,200,0,                   //vpshufd       $0x0,%xmm8,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,124,92,209,                      //vsubps        %ymm9,%ymm0,%ymm10
-  196,193,58,88,192,                      //vaddss        %xmm8,%xmm8,%xmm0
-  196,227,121,4,192,0,                    //vpermilps     $0x0,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,44,94,192,                          //vdivps        %ymm0,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,172,92,192,                         //vsubps        %ymm0,%ymm10,%ymm0
-  196,193,124,92,193,                     //vsubps        %ymm9,%ymm0,%ymm0
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,192,                          //vsubps        %ymm0,%ymm8,%ymm8
-  197,60,84,192,                          //vandps        %ymm0,%ymm8,%ymm8
-  196,99,125,25,200,1,                    //vextractf128  $0x1,%ymm9,%xmm0
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,121,254,194,                    //vpaddd        %xmm10,%xmm0,%xmm0
-  196,65,49,254,202,                      //vpaddd        %xmm10,%xmm9,%xmm9
-  196,227,53,24,192,1,                    //vinsertf128   $0x1,%xmm0,%ymm9,%ymm0
-  197,188,93,192,                         //vminps        %ymm0,%ymm8,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,121,110,0,                          //vmovd         (%rax),%xmm8
-  196,65,121,112,200,0,                   //vpshufd       $0x0,%xmm8,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,116,92,209,                      //vsubps        %ymm9,%ymm1,%ymm10
-  196,193,58,88,200,                      //vaddss        %xmm8,%xmm8,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,44,94,193,                          //vdivps        %ymm1,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,172,92,201,                         //vsubps        %ymm1,%ymm10,%ymm1
-  196,193,116,92,201,                     //vsubps        %ymm9,%ymm1,%ymm1
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,193,                          //vsubps        %ymm1,%ymm8,%ymm8
-  197,60,84,193,                          //vandps        %ymm1,%ymm8,%ymm8
-  196,99,125,25,201,1,                    //vextractf128  $0x1,%ymm9,%xmm1
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,113,254,202,                    //vpaddd        %xmm10,%xmm1,%xmm1
-  196,65,49,254,202,                      //vpaddd        %xmm10,%xmm9,%xmm9
-  196,227,53,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm9,%ymm1
-  197,188,93,201,                         //vminps        %ymm1,%ymm8,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_avx[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,228,89,192,                         //vmulps        %ymm0,%ymm3,%ymm0
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,228,89,201,                         //vmulps        %ymm1,%ymm3,%ymm1
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,244,89,202,                         //vmulps        %ymm2,%ymm1,%ymm1
-  197,252,88,217,                         //vaddps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,8,                     //vbroadcastss  0x8(%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,12,                    //vbroadcastss  0xc(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  197,172,89,201,                         //vmulps        %ymm1,%ymm10,%ymm1
-  196,193,116,88,203,                     //vaddps        %ymm11,%ymm1,%ymm1
-  197,180,89,192,                         //vmulps        %ymm0,%ymm9,%ymm0
-  197,252,88,201,                         //vaddps        %ymm1,%ymm0,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,12,                    //vbroadcastss  0xc(%rax),%ymm9
-  196,98,125,24,80,24,                    //vbroadcastss  0x18(%rax),%ymm10
-  196,98,125,24,88,36,                    //vbroadcastss  0x24(%rax),%ymm11
-  197,44,89,210,                          //vmulps        %ymm2,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  196,98,125,24,88,28,                    //vbroadcastss  0x1c(%rax),%ymm11
-  196,98,125,24,96,40,                    //vbroadcastss  0x28(%rax),%ymm12
-  197,36,89,218,                          //vmulps        %ymm2,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,200,                          //vmulps        %ymm0,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  196,98,125,24,96,32,                    //vbroadcastss  0x20(%rax),%ymm12
-  196,98,125,24,104,44,                   //vbroadcastss  0x2c(%rax),%ymm13
-  197,156,89,210,                         //vmulps        %ymm2,%ymm12,%ymm2
-  196,193,108,88,213,                     //vaddps        %ymm13,%ymm2,%ymm2
-  197,164,89,201,                         //vmulps        %ymm1,%ymm11,%ymm1
-  197,244,88,202,                         //vaddps        %ymm2,%ymm1,%ymm1
-  197,172,89,192,                         //vmulps        %ymm0,%ymm10,%ymm0
-  197,252,88,209,                         //vaddps        %ymm1,%ymm0,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,16,                    //vbroadcastss  0x10(%rax),%ymm9
-  196,98,125,24,80,32,                    //vbroadcastss  0x20(%rax),%ymm10
-  196,98,125,24,88,48,                    //vbroadcastss  0x30(%rax),%ymm11
-  196,98,125,24,96,64,                    //vbroadcastss  0x40(%rax),%ymm12
-  197,36,89,219,                          //vmulps        %ymm3,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,210,                          //vmulps        %ymm2,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,20,                    //vbroadcastss  0x14(%rax),%ymm10
-  196,98,125,24,88,36,                    //vbroadcastss  0x24(%rax),%ymm11
-  196,98,125,24,96,52,                    //vbroadcastss  0x34(%rax),%ymm12
-  196,98,125,24,104,68,                   //vbroadcastss  0x44(%rax),%ymm13
-  197,28,89,227,                          //vmulps        %ymm3,%ymm12,%ymm12
-  196,65,28,88,229,                       //vaddps        %ymm13,%ymm12,%ymm12
-  197,36,89,218,                          //vmulps        %ymm2,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,200,                          //vmulps        %ymm0,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,98,125,24,88,24,                    //vbroadcastss  0x18(%rax),%ymm11
-  196,98,125,24,96,40,                    //vbroadcastss  0x28(%rax),%ymm12
-  196,98,125,24,104,56,                   //vbroadcastss  0x38(%rax),%ymm13
-  196,98,125,24,112,72,                   //vbroadcastss  0x48(%rax),%ymm14
-  197,20,89,235,                          //vmulps        %ymm3,%ymm13,%ymm13
-  196,65,20,88,238,                       //vaddps        %ymm14,%ymm13,%ymm13
-  197,28,89,226,                          //vmulps        %ymm2,%ymm12,%ymm12
-  196,65,28,88,229,                       //vaddps        %ymm13,%ymm12,%ymm12
-  197,36,89,217,                          //vmulps        %ymm1,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,208,                          //vmulps        %ymm0,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  196,98,125,24,88,12,                    //vbroadcastss  0xc(%rax),%ymm11
-  196,98,125,24,96,28,                    //vbroadcastss  0x1c(%rax),%ymm12
-  196,98,125,24,104,44,                   //vbroadcastss  0x2c(%rax),%ymm13
-  196,98,125,24,112,60,                   //vbroadcastss  0x3c(%rax),%ymm14
-  196,98,125,24,120,76,                   //vbroadcastss  0x4c(%rax),%ymm15
-  197,140,89,219,                         //vmulps        %ymm3,%ymm14,%ymm3
-  196,193,100,88,223,                     //vaddps        %ymm15,%ymm3,%ymm3
-  197,148,89,210,                         //vmulps        %ymm2,%ymm13,%ymm2
-  197,236,88,211,                         //vaddps        %ymm3,%ymm2,%ymm2
-  197,156,89,201,                         //vmulps        %ymm1,%ymm12,%ymm1
-  197,244,88,202,                         //vaddps        %ymm2,%ymm1,%ymm1
-  197,164,89,192,                         //vmulps        %ymm0,%ymm11,%ymm0
-  197,252,88,217,                         //vaddps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  197,124,41,210,                         //vmovaps       %ymm10,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,12,                    //vbroadcastss  0xc(%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,200,                          //vmulps        %ymm0,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  196,98,125,24,80,24,                    //vbroadcastss  0x18(%rax),%ymm10
-  196,98,125,24,88,28,                    //vbroadcastss  0x1c(%rax),%ymm11
-  196,98,125,24,96,32,                    //vbroadcastss  0x20(%rax),%ymm12
-  197,164,89,201,                         //vmulps        %ymm1,%ymm11,%ymm1
-  196,193,116,88,204,                     //vaddps        %ymm12,%ymm1,%ymm1
-  197,172,89,192,                         //vmulps        %ymm0,%ymm10,%ymm0
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  197,252,83,200,                         //vrcpps        %ymm0,%ymm1
-  197,188,89,193,                         //vmulps        %ymm1,%ymm8,%ymm0
-  197,180,89,201,                         //vmulps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,72,16,                   //vbroadcastss  0x10(%rax),%ymm1
-  196,226,125,24,16,                      //vbroadcastss  (%rax),%ymm2
-  197,244,89,200,                         //vmulps        %ymm0,%ymm1,%ymm1
-  197,108,88,193,                         //vaddps        %ymm1,%ymm2,%ymm8
-  196,226,125,24,72,20,                   //vbroadcastss  0x14(%rax),%ymm1
-  196,226,125,24,80,4,                    //vbroadcastss  0x4(%rax),%ymm2
-  197,244,89,200,                         //vmulps        %ymm0,%ymm1,%ymm1
-  197,236,88,201,                         //vaddps        %ymm1,%ymm2,%ymm1
-  196,226,125,24,80,24,                   //vbroadcastss  0x18(%rax),%ymm2
-  196,226,125,24,88,8,                    //vbroadcastss  0x8(%rax),%ymm3
-  197,236,89,208,                         //vmulps        %ymm0,%ymm2,%ymm2
-  197,228,88,210,                         //vaddps        %ymm2,%ymm3,%ymm2
-  196,226,125,24,88,28,                   //vbroadcastss  0x1c(%rax),%ymm3
-  196,98,125,24,72,12,                    //vbroadcastss  0xc(%rax),%ymm9
-  197,228,89,192,                         //vmulps        %ymm0,%ymm3,%ymm0
-  197,180,88,216,                         //vaddps        %ymm0,%ymm9,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_start_pipeline_sse41[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  83,                                     //push          %rbx
-  73,137,207,                             //mov           %rcx,%r15
-  73,137,214,                             //mov           %rdx,%r14
-  72,137,251,                             //mov           %rdi,%rbx
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,196,                             //mov           %rax,%r12
-  73,137,245,                             //mov           %rsi,%r13
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  76,57,248,                              //cmp           %r15,%rax
-  118,5,                                  //jbe           28 <_sk_start_pipeline_sse41+0x28>
-  72,137,216,                             //mov           %rbx,%rax
-  235,52,                                 //jmp           5c <_sk_start_pipeline_sse41+0x5c>
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,238,                             //mov           %r13,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,212,                             //callq         *%r12
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  72,131,195,8,                           //add           $0x8,%rbx
-  76,57,251,                              //cmp           %r15,%rbx
-  72,137,195,                             //mov           %rax,%rbx
-  118,204,                                //jbe           28 <_sk_start_pipeline_sse41+0x28>
-  91,                                     //pop           %rbx
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_sse41[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  102,15,110,199,                         //movd          %edi,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  185,0,0,0,63,                           //mov           $0x3f000000,%ecx
-  102,15,110,209,                         //movd          %ecx,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,16,2,                                //movups        (%rdx),%xmm0
-  15,88,193,                              //addps         %xmm1,%xmm0
-  102,15,110,8,                           //movd          (%rax),%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,16,24,                               //movups        (%rax),%xmm3
-  15,40,195,                              //movaps        %xmm3,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__sse41[] = {
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,195,                           //subps         %xmm3,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,204,                           //mulps         %xmm4,%xmm9
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,205,                           //mulps         %xmm5,%xmm9
-  65,15,88,201,                           //addps         %xmm9,%xmm1
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,206,                           //mulps         %xmm6,%xmm9
-  65,15,88,209,                           //addps         %xmm9,%xmm2
-  68,15,89,199,                           //mulps         %xmm7,%xmm8
-  65,15,88,216,                           //addps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,199,                           //subps         %xmm7,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_sse41[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  65,15,95,192,                           //maxps         %xmm8,%xmm0
-  65,15,95,200,                           //maxps         %xmm8,%xmm1
-  65,15,95,208,                           //maxps         %xmm8,%xmm2
-  65,15,95,216,                           //maxps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,192,                           //minps         %xmm8,%xmm0
-  65,15,93,200,                           //minps         %xmm8,%xmm1
-  65,15,93,208,                           //minps         %xmm8,%xmm2
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  15,93,195,                              //minps         %xmm3,%xmm0
-  15,93,203,                              //minps         %xmm3,%xmm1
-  15,93,211,                              //minps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,15,16,80,8,                         //movss         0x8(%rax),%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_sse41[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_sse41[] = {
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  68,15,40,202,                           //movaps        %xmm2,%xmm9
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  68,15,40,216,                           //movaps        %xmm0,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  65,15,40,227,                           //movaps        %xmm11,%xmm4
-  65,15,40,234,                           //movaps        %xmm10,%xmm5
-  65,15,40,241,                           //movaps        %xmm9,%xmm6
-  65,15,40,248,                           //movaps        %xmm8,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,224,                              //movaps        %xmm0,%xmm4
-  15,40,233,                              //movaps        %xmm1,%xmm5
-  15,40,242,                              //movaps        %xmm2,%xmm6
-  15,40,251,                              //movaps        %xmm3,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_sse41[] = {
-  15,89,195,                              //mulps         %xmm3,%xmm0
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_sse41[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,94,203,                           //divps         %xmm3,%xmm9
-  68,15,194,195,4,                        //cmpneqps      %xmm3,%xmm8
-  69,15,84,193,                           //andps         %xmm9,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_sse41[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,40,211,                           //movaps        %xmm11,%xmm10
-  68,15,89,208,                           //mulps         %xmm0,%xmm10
-  68,15,40,240,                           //movaps        %xmm0,%xmm14
-  69,15,89,246,                           //mulps         %xmm14,%xmm14
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  69,15,88,204,                           //addps         %xmm12,%xmm9
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  102,68,15,110,232,                      //movd          %eax,%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  69,15,89,206,                           //mulps         %xmm14,%xmm9
-  69,15,88,205,                           //addps         %xmm13,%xmm9
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  102,68,15,110,240,                      //movd          %eax,%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  65,15,194,198,1,                        //cmpltps       %xmm14,%xmm0
-  102,69,15,56,20,202,                    //blendvps      %xmm0,%xmm10,%xmm9
-  69,15,40,251,                           //movaps        %xmm11,%xmm15
-  68,15,89,249,                           //mulps         %xmm1,%xmm15
-  15,40,193,                              //movaps        %xmm1,%xmm0
-  15,89,192,                              //mulps         %xmm0,%xmm0
-  69,15,40,208,                           //movaps        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  69,15,88,212,                           //addps         %xmm12,%xmm10
-  68,15,89,208,                           //mulps         %xmm0,%xmm10
-  69,15,88,213,                           //addps         %xmm13,%xmm10
-  65,15,194,206,1,                        //cmpltps       %xmm14,%xmm1
-  15,40,193,                              //movaps        %xmm1,%xmm0
-  102,69,15,56,20,215,                    //blendvps      %xmm0,%xmm15,%xmm10
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  15,89,192,                              //mulps         %xmm0,%xmm0
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  69,15,88,196,                           //addps         %xmm12,%xmm8
-  68,15,89,192,                           //mulps         %xmm0,%xmm8
-  69,15,88,197,                           //addps         %xmm13,%xmm8
-  65,15,194,214,1,                        //cmpltps       %xmm14,%xmm2
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  102,69,15,56,20,195,                    //blendvps      %xmm0,%xmm11,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,193,                           //movaps        %xmm9,%xmm0
-  65,15,40,202,                           //movaps        %xmm10,%xmm1
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_sse41[] = {
-  72,131,236,24,                          //sub           $0x18,%rsp
-  15,41,60,36,                            //movaps        %xmm7,(%rsp)
-  15,40,254,                              //movaps        %xmm6,%xmm7
-  15,40,245,                              //movaps        %xmm5,%xmm6
-  15,40,236,                              //movaps        %xmm4,%xmm5
-  15,40,227,                              //movaps        %xmm3,%xmm4
-  15,40,218,                              //movaps        %xmm2,%xmm3
-  15,40,209,                              //movaps        %xmm1,%xmm2
-  68,15,82,192,                           //rsqrtps       %xmm0,%xmm8
-  69,15,83,200,                           //rcpps         %xmm8,%xmm9
-  69,15,82,248,                           //rsqrtps       %xmm8,%xmm15
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,40,211,                           //movaps        %xmm11,%xmm10
-  68,15,89,208,                           //mulps         %xmm0,%xmm10
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  102,68,15,110,232,                      //movd          %eax,%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  102,68,15,110,240,                      //movd          %eax,%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  69,15,89,205,                           //mulps         %xmm13,%xmm9
-  69,15,88,206,                           //addps         %xmm14,%xmm9
-  69,15,89,252,                           //mulps         %xmm12,%xmm15
-  69,15,88,249,                           //addps         %xmm9,%xmm15
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  69,15,93,207,                           //minps         %xmm15,%xmm9
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  102,68,15,110,248,                      //movd          %eax,%xmm15
-  69,15,198,255,0,                        //shufps        $0x0,%xmm15,%xmm15
-  65,15,194,199,1,                        //cmpltps       %xmm15,%xmm0
-  102,69,15,56,20,202,                    //blendvps      %xmm0,%xmm10,%xmm9
-  68,15,82,210,                           //rsqrtps       %xmm2,%xmm10
-  65,15,83,194,                           //rcpps         %xmm10,%xmm0
-  69,15,82,210,                           //rsqrtps       %xmm10,%xmm10
-  65,15,89,197,                           //mulps         %xmm13,%xmm0
-  65,15,88,198,                           //addps         %xmm14,%xmm0
-  69,15,89,212,                           //mulps         %xmm12,%xmm10
-  68,15,88,208,                           //addps         %xmm0,%xmm10
-  65,15,40,200,                           //movaps        %xmm8,%xmm1
-  65,15,93,202,                           //minps         %xmm10,%xmm1
-  69,15,40,211,                           //movaps        %xmm11,%xmm10
-  68,15,89,210,                           //mulps         %xmm2,%xmm10
-  65,15,194,215,1,                        //cmpltps       %xmm15,%xmm2
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  102,65,15,56,20,202,                    //blendvps      %xmm0,%xmm10,%xmm1
-  15,82,195,                              //rsqrtps       %xmm3,%xmm0
-  15,83,208,                              //rcpps         %xmm0,%xmm2
-  65,15,89,213,                           //mulps         %xmm13,%xmm2
-  65,15,88,214,                           //addps         %xmm14,%xmm2
-  15,82,192,                              //rsqrtps       %xmm0,%xmm0
-  65,15,89,196,                           //mulps         %xmm12,%xmm0
-  15,88,194,                              //addps         %xmm2,%xmm0
-  68,15,93,192,                           //minps         %xmm0,%xmm8
-  68,15,89,219,                           //mulps         %xmm3,%xmm11
-  65,15,194,223,1,                        //cmpltps       %xmm15,%xmm3
-  15,40,195,                              //movaps        %xmm3,%xmm0
-  102,69,15,56,20,195,                    //blendvps      %xmm0,%xmm11,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,193,                           //movaps        %xmm9,%xmm0
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  15,40,220,                              //movaps        %xmm4,%xmm3
-  15,40,229,                              //movaps        %xmm5,%xmm4
-  15,40,238,                              //movaps        %xmm6,%xmm5
-  15,40,247,                              //movaps        %xmm7,%xmm6
-  15,40,60,36,                            //movaps        (%rsp),%xmm7
-  72,131,196,24,                          //add           $0x18,%rsp
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,56,49,4,56,                   //pmovzxbd      (%rax,%rdi,1),%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_1_float_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,56,49,4,56,                   //pmovzxbd      (%rax,%rdi,1),%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_565_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,56,51,4,120,                  //pmovzxwd      (%rax,%rdi,2),%xmm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,195,                           //cvtdq2ps      %xmm3,%xmm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,194,                           //mulps         %xmm10,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,203,                           //mulps         %xmm11,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_tables_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,8,                               //mov           (%rax),%rcx
-  76,139,64,8,                            //mov           0x8(%rax),%r8
-  243,68,15,111,4,185,                    //movdqu        (%rcx,%rdi,4),%xmm8
-  185,255,0,0,0,                          //mov           $0xff,%ecx
-  102,15,110,193,                         //movd          %ecx,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,65,15,111,200,                      //movdqa        %xmm8,%xmm1
-  102,15,114,209,8,                       //psrld         $0x8,%xmm1
-  102,15,219,200,                         //pand          %xmm0,%xmm1
-  102,65,15,111,208,                      //movdqa        %xmm8,%xmm2
-  102,15,114,210,16,                      //psrld         $0x10,%xmm2
-  102,15,219,208,                         //pand          %xmm0,%xmm2
-  102,65,15,219,192,                      //pand          %xmm8,%xmm0
-  102,72,15,58,22,193,1,                  //pextrq        $0x1,%xmm0,%rcx
-  65,137,201,                             //mov           %ecx,%r9d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,73,15,126,194,                      //movq          %xmm0,%r10
-  69,137,211,                             //mov           %r10d,%r11d
-  73,193,234,32,                          //shr           $0x20,%r10
-  243,67,15,16,4,152,                     //movss         (%r8,%r11,4),%xmm0
-  102,67,15,58,33,4,144,16,               //insertps      $0x10,(%r8,%r10,4),%xmm0
-  102,67,15,58,33,4,136,32,               //insertps      $0x20,(%r8,%r9,4),%xmm0
-  102,65,15,58,33,4,136,48,               //insertps      $0x30,(%r8,%rcx,4),%xmm0
-  76,139,64,16,                           //mov           0x10(%rax),%r8
-  102,73,15,58,22,202,1,                  //pextrq        $0x1,%xmm1,%r10
-  77,137,209,                             //mov           %r10,%r9
-  73,193,233,32,                          //shr           $0x20,%r9
-  102,72,15,126,201,                      //movq          %xmm1,%rcx
-  65,137,203,                             //mov           %ecx,%r11d
-  65,129,227,255,255,255,0,               //and           $0xffffff,%r11d
-  72,193,233,30,                          //shr           $0x1e,%rcx
-  65,129,226,255,255,255,0,               //and           $0xffffff,%r10d
-  243,67,15,16,12,152,                    //movss         (%r8,%r11,4),%xmm1
-  102,65,15,58,33,12,8,16,                //insertps      $0x10,(%r8,%rcx,1),%xmm1
-  243,67,15,16,28,144,                    //movss         (%r8,%r10,4),%xmm3
-  102,15,58,33,203,32,                    //insertps      $0x20,%xmm3,%xmm1
-  243,67,15,16,28,136,                    //movss         (%r8,%r9,4),%xmm3
-  102,15,58,33,203,48,                    //insertps      $0x30,%xmm3,%xmm1
-  76,139,72,24,                           //mov           0x18(%rax),%r9
-  102,72,15,58,22,209,1,                  //pextrq        $0x1,%xmm2,%rcx
-  68,15,183,193,                          //movzwl        %cx,%r8d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,72,15,126,208,                      //movq          %xmm2,%rax
-  68,15,183,208,                          //movzwl        %ax,%r10d
-  72,193,232,30,                          //shr           $0x1e,%rax
-  243,67,15,16,20,145,                    //movss         (%r9,%r10,4),%xmm2
-  102,65,15,58,33,20,1,16,                //insertps      $0x10,(%r9,%rax,1),%xmm2
-  243,67,15,16,28,129,                    //movss         (%r9,%r8,4),%xmm3
-  102,15,58,33,211,32,                    //insertps      $0x20,%xmm3,%xmm2
-  243,65,15,16,28,137,                    //movss         (%r9,%rcx,4),%xmm3
-  102,15,58,33,211,48,                    //insertps      $0x30,%xmm3,%xmm2
-  102,65,15,114,208,24,                   //psrld         $0x18,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_a8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,15,56,49,4,56,                      //pmovzxbd      (%rax,%rdi,1),%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_a8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,69,15,56,43,192,                    //packusdw      %xmm8,%xmm8
-  102,69,15,103,192,                      //packuswb      %xmm8,%xmm8
-  102,68,15,126,4,56,                     //movd          %xmm8,(%rax,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_565_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,15,56,51,20,120,                    //pmovzxwd      (%rax,%rdi,2),%xmm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,219,194,                         //pand          %xmm2,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  102,15,219,202,                         //pand          %xmm2,%xmm1
-  15,91,217,                              //cvtdq2ps      %xmm1,%xmm3
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,15,219,218,                         //pand          %xmm2,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_565_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,248,65,                         //mov           $0x41f80000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,11,                   //pslld         $0xb,%xmm9
-  185,0,0,124,66,                         //mov           $0x427c0000,%ecx
-  102,68,15,110,209,                      //movd          %ecx,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,5,                    //pslld         $0x5,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,69,15,86,194,                       //orpd          %xmm10,%xmm8
-  102,69,15,56,43,192,                    //packusdw      %xmm8,%xmm8
-  102,68,15,214,4,120,                    //movq          %xmm8,(%rax,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_8888_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,28,184,                      //movdqu        (%rax,%rdi,4),%xmm3
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,15,114,209,8,                       //psrld         $0x8,%xmm1
-  102,15,219,200,                         //pand          %xmm0,%xmm1
-  102,15,111,211,                         //movdqa        %xmm3,%xmm2
-  102,15,114,210,16,                      //psrld         $0x10,%xmm2
-  102,15,219,208,                         //pand          %xmm0,%xmm2
-  102,15,219,195,                         //pand          %xmm3,%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,91,210,                              //cvtdq2ps      %xmm2,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  102,15,114,211,24,                      //psrld         $0x18,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_8888_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  69,15,40,208,                           //movaps        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,8,                    //pslld         $0x8,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,202,                           //mulps         %xmm2,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,16,                   //pslld         $0x10,%xmm9
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,65,15,114,240,24,                   //pslld         $0x18,%xmm8
-  102,69,15,235,193,                      //por           %xmm9,%xmm8
-  102,69,15,235,194,                      //por           %xmm10,%xmm8
-  243,68,15,127,4,184,                    //movdqu        %xmm8,(%rax,%rdi,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_f16_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,4,248,                       //movdqu        (%rax,%rdi,8),%xmm0
-  243,15,111,76,248,16,                   //movdqu        0x10(%rax,%rdi,8),%xmm1
-  102,15,111,208,                         //movdqa        %xmm0,%xmm2
-  102,15,97,209,                          //punpcklwd     %xmm1,%xmm2
-  102,15,105,193,                         //punpckhwd     %xmm1,%xmm0
-  102,68,15,111,194,                      //movdqa        %xmm2,%xmm8
-  102,68,15,97,192,                       //punpcklwd     %xmm0,%xmm8
-  102,15,105,208,                         //punpckhwd     %xmm0,%xmm2
-  184,0,4,0,4,                            //mov           $0x4000400,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,216,0,                       //pshufd        $0x0,%xmm0,%xmm3
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,65,15,101,200,                      //pcmpgtw       %xmm8,%xmm1
-  102,65,15,223,200,                      //pandn         %xmm8,%xmm1
-  102,15,101,218,                         //pcmpgtw       %xmm2,%xmm3
-  102,15,223,218,                         //pandn         %xmm2,%xmm3
-  102,15,56,51,193,                       //pmovzxwd      %xmm1,%xmm0
-  102,15,114,240,13,                      //pslld         $0xd,%xmm0
-  184,0,0,128,119,                        //mov           $0x77800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  102,68,15,112,194,0,                    //pshufd        $0x0,%xmm2,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  102,69,15,239,201,                      //pxor          %xmm9,%xmm9
-  102,65,15,105,201,                      //punpckhwd     %xmm9,%xmm1
-  102,15,114,241,13,                      //pslld         $0xd,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  102,15,56,51,211,                       //pmovzxwd      %xmm3,%xmm2
-  102,15,114,242,13,                      //pslld         $0xd,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  102,65,15,105,217,                      //punpckhwd     %xmm9,%xmm3
-  102,15,114,243,13,                      //pslld         $0xd,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f16_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,128,7,                          //mov           $0x7800000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  102,69,15,112,192,0,                    //pshufd        $0x0,%xmm8,%xmm8
-  102,69,15,111,200,                      //movdqa        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,65,15,114,209,13,                   //psrld         $0xd,%xmm9
-  102,69,15,111,208,                      //movdqa        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,65,15,114,210,13,                   //psrld         $0xd,%xmm10
-  102,69,15,111,216,                      //movdqa        %xmm8,%xmm11
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  102,65,15,114,211,13,                   //psrld         $0xd,%xmm11
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,65,15,114,208,13,                   //psrld         $0xd,%xmm8
-  102,65,15,115,250,2,                    //pslldq        $0x2,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  102,65,15,115,248,2,                    //pslldq        $0x2,%xmm8
-  102,69,15,235,195,                      //por           %xmm11,%xmm8
-  102,69,15,111,202,                      //movdqa        %xmm10,%xmm9
-  102,69,15,98,200,                       //punpckldq     %xmm8,%xmm9
-  243,68,15,127,12,248,                   //movdqu        %xmm9,(%rax,%rdi,8)
-  102,69,15,106,208,                      //punpckhdq     %xmm8,%xmm10
-  243,68,15,127,84,248,16,                //movdqu        %xmm10,0x10(%rax,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f32_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,137,249,                             //mov           %rdi,%rcx
-  72,193,225,4,                           //shl           $0x4,%rcx
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  68,15,20,201,                           //unpcklps      %xmm1,%xmm9
-  68,15,40,210,                           //movaps        %xmm2,%xmm10
-  68,15,40,218,                           //movaps        %xmm2,%xmm11
-  68,15,20,219,                           //unpcklps      %xmm3,%xmm11
-  68,15,21,193,                           //unpckhps      %xmm1,%xmm8
-  68,15,21,211,                           //unpckhps      %xmm3,%xmm10
-  69,15,40,225,                           //movaps        %xmm9,%xmm12
-  102,69,15,20,227,                       //unpcklpd      %xmm11,%xmm12
-  69,15,18,217,                           //movhlps       %xmm9,%xmm11
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  102,69,15,20,202,                       //unpcklpd      %xmm10,%xmm9
-  69,15,18,208,                           //movhlps       %xmm8,%xmm10
-  102,68,15,17,36,8,                      //movupd        %xmm12,(%rax,%rcx,1)
-  68,15,17,92,8,16,                       //movups        %xmm11,0x10(%rax,%rcx,1)
-  102,68,15,17,76,8,32,                   //movupd        %xmm9,0x20(%rax,%rcx,1)
-  68,15,17,84,8,48,                       //movups        %xmm10,0x30(%rax,%rcx,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_x_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,192,                           //maxps         %xmm0,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,192,                         //pcmpeqd       %xmm0,%xmm0
-  102,65,15,254,193,                      //paddd         %xmm9,%xmm0
-  68,15,93,192,                           //minps         %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,193,                           //maxps         %xmm1,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,201,                         //pcmpeqd       %xmm1,%xmm1
-  102,65,15,254,201,                      //paddd         %xmm9,%xmm1
-  68,15,93,193,                           //minps         %xmm1,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,200,                           //movaps        %xmm8,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  102,69,15,58,8,201,1,                   //roundps       $0x1,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,92,193,                           //subps         %xmm9,%xmm0
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,193,                           //minps         %xmm9,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  102,69,15,58,8,201,1,                   //roundps       $0x1,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,92,201,                           //subps         %xmm9,%xmm1
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,201,                           //minps         %xmm9,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  65,15,92,193,                           //subps         %xmm9,%xmm0
-  243,69,15,88,192,                       //addss         %xmm8,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,208,                           //movaps        %xmm0,%xmm10
-  69,15,94,208,                           //divps         %xmm8,%xmm10
-  102,69,15,58,8,210,1,                   //roundps       $0x1,%xmm10,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,194,                           //subps         %xmm10,%xmm0
-  65,15,92,193,                           //subps         %xmm9,%xmm0
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,92,192,                           //subps         %xmm0,%xmm8
-  65,15,84,192,                           //andps         %xmm8,%xmm0
-  102,69,15,118,192,                      //pcmpeqd       %xmm8,%xmm8
-  102,69,15,254,193,                      //paddd         %xmm9,%xmm8
-  65,15,93,192,                           //minps         %xmm8,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  65,15,92,201,                           //subps         %xmm9,%xmm1
-  243,69,15,88,192,                       //addss         %xmm8,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  69,15,94,208,                           //divps         %xmm8,%xmm10
-  102,69,15,58,8,210,1,                   //roundps       $0x1,%xmm10,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,202,                           //subps         %xmm10,%xmm1
-  65,15,92,201,                           //subps         %xmm9,%xmm1
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,92,193,                           //subps         %xmm1,%xmm8
-  65,15,84,200,                           //andps         %xmm8,%xmm1
-  102,69,15,118,192,                      //pcmpeqd       %xmm8,%xmm8
-  102,69,15,254,193,                      //paddd         %xmm9,%xmm8
-  65,15,93,200,                           //minps         %xmm8,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_sse41[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  15,88,195,                              //addps         %xmm3,%xmm0
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,218,                              //mulps         %xmm2,%xmm3
-  15,88,216,                              //addps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_sse41[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,16,                     //movss         0x10(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_sse41[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,36,                     //movss         0x24(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_sse41[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,32,                     //movss         0x20(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,48,                     //movss         0x30(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,64,                    //movss         0x40(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,20,                     //movss         0x14(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,36,                     //movss         0x24(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,52,                     //movss         0x34(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,68,                    //movss         0x44(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,56,                    //movss         0x38(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,72,                    //movss         0x48(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  68,15,89,235,                           //mulps         %xmm3,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  243,68,15,16,88,12,                     //movss         0xc(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,28,                     //movss         0x1c(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,60,                    //movss         0x3c(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  243,68,15,16,120,76,                    //movss         0x4c(%rax),%xmm15
-  69,15,198,255,0,                        //shufps        $0x0,%xmm15,%xmm15
-  68,15,89,243,                           //mulps         %xmm3,%xmm14
-  69,15,88,247,                           //addps         %xmm15,%xmm14
-  68,15,89,234,                           //mulps         %xmm2,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  69,15,89,225,                           //mulps         %xmm9,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,216,                           //mulps         %xmm8,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  65,15,40,219,                           //movaps        %xmm11,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_sse41[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,68,15,16,72,4,                      //movss         0x4(%rax),%xmm9
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  243,68,15,16,72,12,                     //movss         0xc(%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  243,68,15,16,80,24,                     //movss         0x18(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,217,                           //mulps         %xmm1,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,83,202,                           //rcpps         %xmm10,%xmm1
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  68,15,16,8,                             //movups        (%rax),%xmm9
-  15,16,88,16,                            //movups        0x10(%rax),%xmm3
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  68,15,89,192,                           //mulps         %xmm0,%xmm8
-  68,15,88,193,                           //addps         %xmm1,%xmm8
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  65,15,40,209,                           //movaps        %xmm9,%xmm2
-  15,198,210,85,                          //shufps        $0x55,%xmm2,%xmm2
-  15,89,200,                              //mulps         %xmm0,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  69,15,40,209,                           //movaps        %xmm9,%xmm10
-  69,15,198,210,170,                      //shufps        $0xaa,%xmm10,%xmm10
-  15,89,208,                              //mulps         %xmm0,%xmm2
-  65,15,88,210,                           //addps         %xmm10,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  69,15,198,201,255,                      //shufps        $0xff,%xmm9,%xmm9
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  65,15,88,217,                           //addps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_start_pipeline_sse2[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  83,                                     //push          %rbx
-  73,137,207,                             //mov           %rcx,%r15
-  73,137,214,                             //mov           %rdx,%r14
-  72,137,251,                             //mov           %rdi,%rbx
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,196,                             //mov           %rax,%r12
-  73,137,245,                             //mov           %rsi,%r13
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  76,57,248,                              //cmp           %r15,%rax
-  118,5,                                  //jbe           28 <_sk_start_pipeline_sse2+0x28>
-  72,137,216,                             //mov           %rbx,%rax
-  235,52,                                 //jmp           5c <_sk_start_pipeline_sse2+0x5c>
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,238,                             //mov           %r13,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,212,                             //callq         *%r12
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  72,131,195,8,                           //add           $0x8,%rbx
-  76,57,251,                              //cmp           %r15,%rbx
-  72,137,195,                             //mov           %rax,%rbx
-  118,204,                                //jbe           28 <_sk_start_pipeline_sse2+0x28>
-  91,                                     //pop           %rbx
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_sse2[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  102,15,110,199,                         //movd          %edi,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  185,0,0,0,63,                           //mov           $0x3f000000,%ecx
-  102,15,110,209,                         //movd          %ecx,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,16,2,                                //movups        (%rdx),%xmm0
-  15,88,193,                              //addps         %xmm1,%xmm0
-  102,15,110,8,                           //movd          (%rax),%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,16,24,                               //movups        (%rax),%xmm3
-  15,40,195,                              //movaps        %xmm3,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__sse2[] = {
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,195,                           //subps         %xmm3,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,204,                           //mulps         %xmm4,%xmm9
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,205,                           //mulps         %xmm5,%xmm9
-  65,15,88,201,                           //addps         %xmm9,%xmm1
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,206,                           //mulps         %xmm6,%xmm9
-  65,15,88,209,                           //addps         %xmm9,%xmm2
-  68,15,89,199,                           //mulps         %xmm7,%xmm8
-  65,15,88,216,                           //addps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,199,                           //subps         %xmm7,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_sse2[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  65,15,95,192,                           //maxps         %xmm8,%xmm0
-  65,15,95,200,                           //maxps         %xmm8,%xmm1
-  65,15,95,208,                           //maxps         %xmm8,%xmm2
-  65,15,95,216,                           //maxps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,192,                           //minps         %xmm8,%xmm0
-  65,15,93,200,                           //minps         %xmm8,%xmm1
-  65,15,93,208,                           //minps         %xmm8,%xmm2
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  15,93,195,                              //minps         %xmm3,%xmm0
-  15,93,203,                              //minps         %xmm3,%xmm1
-  15,93,211,                              //minps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,15,16,80,8,                         //movss         0x8(%rax),%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_sse2[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_sse2[] = {
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  68,15,40,202,                           //movaps        %xmm2,%xmm9
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  68,15,40,216,                           //movaps        %xmm0,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  65,15,40,227,                           //movaps        %xmm11,%xmm4
-  65,15,40,234,                           //movaps        %xmm10,%xmm5
-  65,15,40,241,                           //movaps        %xmm9,%xmm6
-  65,15,40,248,                           //movaps        %xmm8,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,224,                              //movaps        %xmm0,%xmm4
-  15,40,233,                              //movaps        %xmm1,%xmm5
-  15,40,242,                              //movaps        %xmm2,%xmm6
-  15,40,251,                              //movaps        %xmm3,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_sse2[] = {
-  15,89,195,                              //mulps         %xmm3,%xmm0
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_sse2[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,94,203,                           //divps         %xmm3,%xmm9
-  68,15,194,195,4,                        //cmpneqps      %xmm3,%xmm8
-  69,15,84,193,                           //andps         %xmm9,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_sse2[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,232,                           //movaps        %xmm8,%xmm13
-  68,15,89,232,                           //mulps         %xmm0,%xmm13
-  68,15,40,224,                           //movaps        %xmm0,%xmm12
-  69,15,89,228,                           //mulps         %xmm12,%xmm12
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  69,15,40,241,                           //movaps        %xmm9,%xmm14
-  68,15,89,240,                           //mulps         %xmm0,%xmm14
-  69,15,88,242,                           //addps         %xmm10,%xmm14
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,244,                           //mulps         %xmm12,%xmm14
-  69,15,88,243,                           //addps         %xmm11,%xmm14
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  65,15,194,196,1,                        //cmpltps       %xmm12,%xmm0
-  68,15,84,232,                           //andps         %xmm0,%xmm13
-  65,15,85,198,                           //andnps        %xmm14,%xmm0
-  65,15,86,197,                           //orps          %xmm13,%xmm0
-  69,15,40,232,                           //movaps        %xmm8,%xmm13
-  68,15,89,233,                           //mulps         %xmm1,%xmm13
-  68,15,40,241,                           //movaps        %xmm1,%xmm14
-  69,15,89,246,                           //mulps         %xmm14,%xmm14
-  69,15,40,249,                           //movaps        %xmm9,%xmm15
-  68,15,89,249,                           //mulps         %xmm1,%xmm15
-  69,15,88,250,                           //addps         %xmm10,%xmm15
-  69,15,89,254,                           //mulps         %xmm14,%xmm15
-  69,15,88,251,                           //addps         %xmm11,%xmm15
-  65,15,194,204,1,                        //cmpltps       %xmm12,%xmm1
-  68,15,84,233,                           //andps         %xmm1,%xmm13
-  65,15,85,207,                           //andnps        %xmm15,%xmm1
-  65,15,86,205,                           //orps          %xmm13,%xmm1
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  68,15,40,234,                           //movaps        %xmm2,%xmm13
-  69,15,89,237,                           //mulps         %xmm13,%xmm13
-  68,15,89,202,                           //mulps         %xmm2,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  69,15,89,205,                           //mulps         %xmm13,%xmm9
-  69,15,88,203,                           //addps         %xmm11,%xmm9
-  65,15,194,212,1,                        //cmpltps       %xmm12,%xmm2
-  68,15,84,194,                           //andps         %xmm2,%xmm8
-  65,15,85,209,                           //andnps        %xmm9,%xmm2
-  65,15,86,208,                           //orps          %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_sse2[] = {
-  68,15,82,192,                           //rsqrtps       %xmm0,%xmm8
-  69,15,83,248,                           //rcpps         %xmm8,%xmm15
-  69,15,82,232,                           //rsqrtps       %xmm8,%xmm13
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,240,                           //movaps        %xmm8,%xmm14
-  68,15,89,240,                           //mulps         %xmm0,%xmm14
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,89,251,                           //mulps         %xmm11,%xmm15
-  69,15,88,252,                           //addps         %xmm12,%xmm15
-  69,15,89,234,                           //mulps         %xmm10,%xmm13
-  69,15,88,239,                           //addps         %xmm15,%xmm13
-  69,15,40,249,                           //movaps        %xmm9,%xmm15
-  69,15,93,253,                           //minps         %xmm13,%xmm15
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  102,68,15,110,232,                      //movd          %eax,%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  65,15,194,197,1,                        //cmpltps       %xmm13,%xmm0
-  68,15,84,240,                           //andps         %xmm0,%xmm14
-  65,15,85,199,                           //andnps        %xmm15,%xmm0
-  65,15,86,198,                           //orps          %xmm14,%xmm0
-  68,15,82,241,                           //rsqrtps       %xmm1,%xmm14
-  69,15,83,254,                           //rcpps         %xmm14,%xmm15
-  69,15,82,246,                           //rsqrtps       %xmm14,%xmm14
-  69,15,89,251,                           //mulps         %xmm11,%xmm15
-  69,15,88,252,                           //addps         %xmm12,%xmm15
-  69,15,89,242,                           //mulps         %xmm10,%xmm14
-  69,15,88,247,                           //addps         %xmm15,%xmm14
-  69,15,40,249,                           //movaps        %xmm9,%xmm15
-  69,15,93,254,                           //minps         %xmm14,%xmm15
-  69,15,40,240,                           //movaps        %xmm8,%xmm14
-  68,15,89,241,                           //mulps         %xmm1,%xmm14
-  65,15,194,205,1,                        //cmpltps       %xmm13,%xmm1
-  68,15,84,241,                           //andps         %xmm1,%xmm14
-  65,15,85,207,                           //andnps        %xmm15,%xmm1
-  65,15,86,206,                           //orps          %xmm14,%xmm1
-  68,15,82,242,                           //rsqrtps       %xmm2,%xmm14
-  69,15,83,254,                           //rcpps         %xmm14,%xmm15
-  69,15,89,251,                           //mulps         %xmm11,%xmm15
-  69,15,88,252,                           //addps         %xmm12,%xmm15
-  69,15,82,222,                           //rsqrtps       %xmm14,%xmm11
-  69,15,89,218,                           //mulps         %xmm10,%xmm11
-  69,15,88,223,                           //addps         %xmm15,%xmm11
-  69,15,93,203,                           //minps         %xmm11,%xmm9
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  65,15,194,213,1,                        //cmpltps       %xmm13,%xmm2
-  68,15,84,194,                           //andps         %xmm2,%xmm8
-  65,15,85,209,                           //andnps        %xmm9,%xmm2
-  65,15,86,208,                           //orps          %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,110,4,56,                     //movd          (%rax,%rdi,1),%xmm8
-  102,69,15,239,201,                      //pxor          %xmm9,%xmm9
-  102,69,15,96,193,                       //punpcklbw     %xmm9,%xmm8
-  102,69,15,97,193,                       //punpcklwd     %xmm9,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_1_float_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,110,4,56,                     //movd          (%rax,%rdi,1),%xmm8
-  102,69,15,239,201,                      //pxor          %xmm9,%xmm9
-  102,69,15,96,193,                       //punpcklbw     %xmm9,%xmm8
-  102,69,15,97,193,                       //punpcklwd     %xmm9,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_565_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,68,15,126,4,120,                    //movq          (%rax,%rdi,2),%xmm8
-  102,15,239,219,                         //pxor          %xmm3,%xmm3
-  102,68,15,97,195,                       //punpcklwd     %xmm3,%xmm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,195,                           //cvtdq2ps      %xmm3,%xmm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,194,                           //mulps         %xmm10,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,203,                           //mulps         %xmm11,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_tables_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,8,                               //mov           (%rax),%rcx
-  76,139,64,8,                            //mov           0x8(%rax),%r8
-  243,68,15,111,4,185,                    //movdqu        (%rcx,%rdi,4),%xmm8
-  185,255,0,0,0,                          //mov           $0xff,%ecx
-  102,15,110,193,                         //movd          %ecx,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,69,15,111,200,                      //movdqa        %xmm8,%xmm9
-  102,65,15,114,209,8,                    //psrld         $0x8,%xmm9
-  102,68,15,219,200,                      //pand          %xmm0,%xmm9
-  102,69,15,111,208,                      //movdqa        %xmm8,%xmm10
-  102,65,15,114,210,16,                   //psrld         $0x10,%xmm10
-  102,68,15,219,208,                      //pand          %xmm0,%xmm10
-  102,65,15,219,192,                      //pand          %xmm8,%xmm0
-  102,15,112,216,78,                      //pshufd        $0x4e,%xmm0,%xmm3
-  102,72,15,126,217,                      //movq          %xmm3,%rcx
-  65,137,201,                             //mov           %ecx,%r9d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,73,15,126,194,                      //movq          %xmm0,%r10
-  69,137,211,                             //mov           %r10d,%r11d
-  73,193,234,32,                          //shr           $0x20,%r10
-  243,67,15,16,28,144,                    //movss         (%r8,%r10,4),%xmm3
-  243,65,15,16,4,136,                     //movss         (%r8,%rcx,4),%xmm0
-  15,20,216,                              //unpcklps      %xmm0,%xmm3
-  243,67,15,16,4,152,                     //movss         (%r8,%r11,4),%xmm0
-  243,67,15,16,12,136,                    //movss         (%r8,%r9,4),%xmm1
-  15,20,193,                              //unpcklps      %xmm1,%xmm0
-  15,20,195,                              //unpcklps      %xmm3,%xmm0
-  76,139,64,16,                           //mov           0x10(%rax),%r8
-  102,65,15,112,201,78,                   //pshufd        $0x4e,%xmm9,%xmm1
-  102,73,15,126,202,                      //movq          %xmm1,%r10
-  77,137,209,                             //mov           %r10,%r9
-  73,193,233,32,                          //shr           $0x20,%r9
-  102,76,15,126,201,                      //movq          %xmm9,%rcx
-  65,137,203,                             //mov           %ecx,%r11d
-  65,129,227,255,255,255,0,               //and           $0xffffff,%r11d
-  72,193,233,30,                          //shr           $0x1e,%rcx
-  65,129,226,255,255,255,0,               //and           $0xffffff,%r10d
-  243,65,15,16,28,8,                      //movss         (%r8,%rcx,1),%xmm3
-  243,67,15,16,12,136,                    //movss         (%r8,%r9,4),%xmm1
-  15,20,217,                              //unpcklps      %xmm1,%xmm3
-  243,67,15,16,12,152,                    //movss         (%r8,%r11,4),%xmm1
-  243,67,15,16,20,144,                    //movss         (%r8,%r10,4),%xmm2
-  15,20,202,                              //unpcklps      %xmm2,%xmm1
-  15,20,203,                              //unpcklps      %xmm3,%xmm1
-  76,139,72,24,                           //mov           0x18(%rax),%r9
-  102,65,15,112,210,78,                   //pshufd        $0x4e,%xmm10,%xmm2
-  102,72,15,126,209,                      //movq          %xmm2,%rcx
-  68,15,183,193,                          //movzwl        %cx,%r8d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,76,15,126,208,                      //movq          %xmm10,%rax
-  68,15,183,208,                          //movzwl        %ax,%r10d
-  72,193,232,30,                          //shr           $0x1e,%rax
-  243,69,15,16,12,1,                      //movss         (%r9,%rax,1),%xmm9
-  243,65,15,16,20,137,                    //movss         (%r9,%rcx,4),%xmm2
-  68,15,20,202,                           //unpcklps      %xmm2,%xmm9
-  243,67,15,16,20,145,                    //movss         (%r9,%r10,4),%xmm2
-  243,67,15,16,28,129,                    //movss         (%r9,%r8,4),%xmm3
-  15,20,211,                              //unpcklps      %xmm3,%xmm2
-  65,15,20,209,                           //unpcklps      %xmm9,%xmm2
-  102,65,15,114,208,24,                   //psrld         $0x18,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_a8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,15,110,4,56,                        //movd          (%rax,%rdi,1),%xmm0
-  102,15,239,201,                         //pxor          %xmm1,%xmm1
-  102,15,96,193,                          //punpcklbw     %xmm1,%xmm0
-  102,15,97,193,                          //punpcklwd     %xmm1,%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  102,15,239,201,                         //pxor          %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_a8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,65,15,114,240,16,                   //pslld         $0x10,%xmm8
-  102,65,15,114,224,16,                   //psrad         $0x10,%xmm8
-  102,69,15,107,192,                      //packssdw      %xmm8,%xmm8
-  102,69,15,103,192,                      //packuswb      %xmm8,%xmm8
-  102,68,15,126,4,56,                     //movd          %xmm8,(%rax,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_565_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,126,20,120,                      //movq          (%rax,%rdi,2),%xmm2
-  102,15,239,192,                         //pxor          %xmm0,%xmm0
-  102,15,97,208,                          //punpcklwd     %xmm0,%xmm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,219,194,                         //pand          %xmm2,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  102,15,219,202,                         //pand          %xmm2,%xmm1
-  15,91,217,                              //cvtdq2ps      %xmm1,%xmm3
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,15,219,218,                         //pand          %xmm2,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_565_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,248,65,                         //mov           $0x41f80000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,11,                   //pslld         $0xb,%xmm9
-  185,0,0,124,66,                         //mov           $0x427c0000,%ecx
-  102,68,15,110,209,                      //movd          %ecx,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,5,                    //pslld         $0x5,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,69,15,86,194,                       //orpd          %xmm10,%xmm8
-  102,65,15,114,240,16,                   //pslld         $0x10,%xmm8
-  102,65,15,114,224,16,                   //psrad         $0x10,%xmm8
-  102,69,15,107,192,                      //packssdw      %xmm8,%xmm8
-  102,68,15,214,4,120,                    //movq          %xmm8,(%rax,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_8888_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,28,184,                      //movdqu        (%rax,%rdi,4),%xmm3
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,15,114,209,8,                       //psrld         $0x8,%xmm1
-  102,15,219,200,                         //pand          %xmm0,%xmm1
-  102,15,111,211,                         //movdqa        %xmm3,%xmm2
-  102,15,114,210,16,                      //psrld         $0x10,%xmm2
-  102,15,219,208,                         //pand          %xmm0,%xmm2
-  102,15,219,195,                         //pand          %xmm3,%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,91,210,                              //cvtdq2ps      %xmm2,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  102,15,114,211,24,                      //psrld         $0x18,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_8888_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  69,15,40,208,                           //movaps        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,8,                    //pslld         $0x8,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,202,                           //mulps         %xmm2,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,16,                   //pslld         $0x10,%xmm9
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,65,15,114,240,24,                   //pslld         $0x18,%xmm8
-  102,69,15,235,193,                      //por           %xmm9,%xmm8
-  102,69,15,235,194,                      //por           %xmm10,%xmm8
-  243,68,15,127,4,184,                    //movdqu        %xmm8,(%rax,%rdi,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_f16_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,4,248,                       //movdqu        (%rax,%rdi,8),%xmm0
-  243,15,111,76,248,16,                   //movdqu        0x10(%rax,%rdi,8),%xmm1
-  102,15,111,208,                         //movdqa        %xmm0,%xmm2
-  102,15,97,209,                          //punpcklwd     %xmm1,%xmm2
-  102,15,105,193,                         //punpckhwd     %xmm1,%xmm0
-  102,68,15,111,194,                      //movdqa        %xmm2,%xmm8
-  102,68,15,97,192,                       //punpcklwd     %xmm0,%xmm8
-  102,15,105,208,                         //punpckhwd     %xmm0,%xmm2
-  184,0,4,0,4,                            //mov           $0x4000400,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,216,0,                       //pshufd        $0x0,%xmm0,%xmm3
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,65,15,101,200,                      //pcmpgtw       %xmm8,%xmm1
-  102,65,15,223,200,                      //pandn         %xmm8,%xmm1
-  102,15,101,218,                         //pcmpgtw       %xmm2,%xmm3
-  102,15,223,218,                         //pandn         %xmm2,%xmm3
-  102,69,15,239,192,                      //pxor          %xmm8,%xmm8
-  102,15,111,193,                         //movdqa        %xmm1,%xmm0
-  102,65,15,97,192,                       //punpcklwd     %xmm8,%xmm0
-  102,15,114,240,13,                      //pslld         $0xd,%xmm0
-  184,0,0,128,119,                        //mov           $0x77800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  102,68,15,112,202,0,                    //pshufd        $0x0,%xmm2,%xmm9
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  102,65,15,105,200,                      //punpckhwd     %xmm8,%xmm1
-  102,15,114,241,13,                      //pslld         $0xd,%xmm1
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  102,15,111,211,                         //movdqa        %xmm3,%xmm2
-  102,65,15,97,208,                       //punpcklwd     %xmm8,%xmm2
-  102,15,114,242,13,                      //pslld         $0xd,%xmm2
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  102,65,15,105,216,                      //punpckhwd     %xmm8,%xmm3
-  102,15,114,243,13,                      //pslld         $0xd,%xmm3
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f16_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,128,7,                          //mov           $0x7800000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  102,69,15,112,192,0,                    //pshufd        $0x0,%xmm8,%xmm8
-  102,69,15,111,200,                      //movdqa        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,65,15,114,209,13,                   //psrld         $0xd,%xmm9
-  102,69,15,111,208,                      //movdqa        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,65,15,114,210,13,                   //psrld         $0xd,%xmm10
-  102,69,15,111,216,                      //movdqa        %xmm8,%xmm11
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  102,65,15,114,211,13,                   //psrld         $0xd,%xmm11
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,65,15,114,208,13,                   //psrld         $0xd,%xmm8
-  102,65,15,115,250,2,                    //pslldq        $0x2,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  102,65,15,115,248,2,                    //pslldq        $0x2,%xmm8
-  102,69,15,235,195,                      //por           %xmm11,%xmm8
-  102,69,15,111,202,                      //movdqa        %xmm10,%xmm9
-  102,69,15,98,200,                       //punpckldq     %xmm8,%xmm9
-  243,68,15,127,12,248,                   //movdqu        %xmm9,(%rax,%rdi,8)
-  102,69,15,106,208,                      //punpckhdq     %xmm8,%xmm10
-  243,68,15,127,84,248,16,                //movdqu        %xmm10,0x10(%rax,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f32_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,137,249,                             //mov           %rdi,%rcx
-  72,193,225,4,                           //shl           $0x4,%rcx
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  68,15,20,201,                           //unpcklps      %xmm1,%xmm9
-  68,15,40,210,                           //movaps        %xmm2,%xmm10
-  68,15,40,218,                           //movaps        %xmm2,%xmm11
-  68,15,20,219,                           //unpcklps      %xmm3,%xmm11
-  68,15,21,193,                           //unpckhps      %xmm1,%xmm8
-  68,15,21,211,                           //unpckhps      %xmm3,%xmm10
-  69,15,40,225,                           //movaps        %xmm9,%xmm12
-  102,69,15,20,227,                       //unpcklpd      %xmm11,%xmm12
-  69,15,18,217,                           //movhlps       %xmm9,%xmm11
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  102,69,15,20,202,                       //unpcklpd      %xmm10,%xmm9
-  69,15,18,208,                           //movhlps       %xmm8,%xmm10
-  102,68,15,17,36,8,                      //movupd        %xmm12,(%rax,%rcx,1)
-  68,15,17,92,8,16,                       //movups        %xmm11,0x10(%rax,%rcx,1)
-  102,68,15,17,76,8,32,                   //movupd        %xmm9,0x20(%rax,%rcx,1)
-  68,15,17,84,8,48,                       //movups        %xmm10,0x30(%rax,%rcx,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_x_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,192,                           //maxps         %xmm0,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,192,                         //pcmpeqd       %xmm0,%xmm0
-  102,65,15,254,193,                      //paddd         %xmm9,%xmm0
-  68,15,93,192,                           //minps         %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,193,                           //maxps         %xmm1,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,201,                         //pcmpeqd       %xmm1,%xmm1
-  102,65,15,254,201,                      //paddd         %xmm9,%xmm1
-  68,15,93,193,                           //minps         %xmm1,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,200,                           //movaps        %xmm8,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  243,69,15,91,209,                       //cvttps2dq     %xmm9,%xmm10
-  69,15,91,210,                           //cvtdq2ps      %xmm10,%xmm10
-  69,15,194,202,1,                        //cmpltps       %xmm10,%xmm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,84,217,                           //andps         %xmm9,%xmm11
-  69,15,92,211,                           //subps         %xmm11,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,194,                           //subps         %xmm10,%xmm0
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,193,                           //minps         %xmm9,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  243,69,15,91,209,                       //cvttps2dq     %xmm9,%xmm10
-  69,15,91,210,                           //cvtdq2ps      %xmm10,%xmm10
-  69,15,194,202,1,                        //cmpltps       %xmm10,%xmm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,84,217,                           //andps         %xmm9,%xmm11
-  69,15,92,211,                           //subps         %xmm11,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,202,                           //subps         %xmm10,%xmm1
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,201,                           //minps         %xmm9,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,40,193,                           //movaps        %xmm9,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,92,192,                           //subps         %xmm8,%xmm0
-  243,69,15,88,201,                       //addss         %xmm9,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,40,208,                           //movaps        %xmm0,%xmm10
-  69,15,94,209,                           //divps         %xmm9,%xmm10
-  243,69,15,91,218,                       //cvttps2dq     %xmm10,%xmm11
-  69,15,91,219,                           //cvtdq2ps      %xmm11,%xmm11
-  69,15,194,211,1,                        //cmpltps       %xmm11,%xmm10
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,84,226,                           //andps         %xmm10,%xmm12
-  69,15,87,210,                           //xorps         %xmm10,%xmm10
-  69,15,92,220,                           //subps         %xmm12,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  65,15,92,195,                           //subps         %xmm11,%xmm0
-  65,15,92,192,                           //subps         %xmm8,%xmm0
-  68,15,92,208,                           //subps         %xmm0,%xmm10
-  65,15,84,194,                           //andps         %xmm10,%xmm0
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,193,                           //minps         %xmm9,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,40,193,                           //movaps        %xmm9,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,92,200,                           //subps         %xmm8,%xmm1
-  243,69,15,88,201,                       //addss         %xmm9,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  69,15,94,209,                           //divps         %xmm9,%xmm10
-  243,69,15,91,218,                       //cvttps2dq     %xmm10,%xmm11
-  69,15,91,219,                           //cvtdq2ps      %xmm11,%xmm11
-  69,15,194,211,1,                        //cmpltps       %xmm11,%xmm10
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,84,226,                           //andps         %xmm10,%xmm12
-  69,15,87,210,                           //xorps         %xmm10,%xmm10
-  69,15,92,220,                           //subps         %xmm12,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  65,15,92,203,                           //subps         %xmm11,%xmm1
-  65,15,92,200,                           //subps         %xmm8,%xmm1
-  68,15,92,209,                           //subps         %xmm1,%xmm10
-  65,15,84,202,                           //andps         %xmm10,%xmm1
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,201,                           //minps         %xmm9,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_sse2[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  15,88,195,                              //addps         %xmm3,%xmm0
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,218,                              //mulps         %xmm2,%xmm3
-  15,88,216,                              //addps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_sse2[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,16,                     //movss         0x10(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_sse2[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,36,                     //movss         0x24(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_sse2[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,32,                     //movss         0x20(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,48,                     //movss         0x30(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,64,                    //movss         0x40(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,20,                     //movss         0x14(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,36,                     //movss         0x24(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,52,                     //movss         0x34(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,68,                    //movss         0x44(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,56,                    //movss         0x38(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,72,                    //movss         0x48(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  68,15,89,235,                           //mulps         %xmm3,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  243,68,15,16,88,12,                     //movss         0xc(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,28,                     //movss         0x1c(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,60,                    //movss         0x3c(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  243,68,15,16,120,76,                    //movss         0x4c(%rax),%xmm15
-  69,15,198,255,0,                        //shufps        $0x0,%xmm15,%xmm15
-  68,15,89,243,                           //mulps         %xmm3,%xmm14
-  69,15,88,247,                           //addps         %xmm15,%xmm14
-  68,15,89,234,                           //mulps         %xmm2,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  69,15,89,225,                           //mulps         %xmm9,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,216,                           //mulps         %xmm8,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  65,15,40,219,                           //movaps        %xmm11,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_sse2[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,68,15,16,72,4,                      //movss         0x4(%rax),%xmm9
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  243,68,15,16,72,12,                     //movss         0xc(%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  243,68,15,16,80,24,                     //movss         0x18(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,217,                           //mulps         %xmm1,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,83,202,                           //rcpps         %xmm10,%xmm1
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  68,15,16,8,                             //movups        (%rax),%xmm9
-  15,16,88,16,                            //movups        0x10(%rax),%xmm3
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  68,15,89,192,                           //mulps         %xmm0,%xmm8
-  68,15,88,193,                           //addps         %xmm1,%xmm8
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  65,15,40,209,                           //movaps        %xmm9,%xmm2
-  15,198,210,85,                          //shufps        $0x55,%xmm2,%xmm2
-  15,89,200,                              //mulps         %xmm0,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  69,15,40,209,                           //movaps        %xmm9,%xmm10
-  69,15,198,210,170,                      //shufps        $0xaa,%xmm10,%xmm10
-  15,89,208,                              //mulps         %xmm0,%xmm2
-  65,15,88,210,                           //addps         %xmm10,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  69,15,198,201,255,                      //shufps        $0xff,%xmm9,%xmm9
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  65,15,88,217,                           //addps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-#elif defined(_M_X64)
-
-CODE const uint8_t sk_start_pipeline_hsw[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  86,                                     //push          %rsi
-  87,                                     //push          %rdi
-  83,                                     //push          %rbx
-  72,129,236,160,0,0,0,                   //sub           $0xa0,%rsp
-  197,120,41,188,36,144,0,0,0,            //vmovaps       %xmm15,0x90(%rsp)
-  197,120,41,180,36,128,0,0,0,            //vmovaps       %xmm14,0x80(%rsp)
-  197,120,41,108,36,112,                  //vmovaps       %xmm13,0x70(%rsp)
-  197,120,41,100,36,96,                   //vmovaps       %xmm12,0x60(%rsp)
-  197,120,41,92,36,80,                    //vmovaps       %xmm11,0x50(%rsp)
-  197,120,41,84,36,64,                    //vmovaps       %xmm10,0x40(%rsp)
-  197,120,41,76,36,48,                    //vmovaps       %xmm9,0x30(%rsp)
-  197,120,41,68,36,32,                    //vmovaps       %xmm8,0x20(%rsp)
-  197,248,41,124,36,16,                   //vmovaps       %xmm7,0x10(%rsp)
-  197,248,41,52,36,                       //vmovaps       %xmm6,(%rsp)
-  77,137,205,                             //mov           %r9,%r13
-  77,137,198,                             //mov           %r8,%r14
-  72,137,203,                             //mov           %rcx,%rbx
-  72,137,214,                             //mov           %rdx,%rsi
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,199,                             //mov           %rax,%r15
-  73,137,244,                             //mov           %rsi,%r12
-  72,141,67,8,                            //lea           0x8(%rbx),%rax
-  76,57,232,                              //cmp           %r13,%rax
-  118,5,                                  //jbe           75 <_sk_start_pipeline_hsw+0x75>
-  72,137,223,                             //mov           %rbx,%rdi
-  235,65,                                 //jmp           b6 <_sk_start_pipeline_hsw+0xb6>
-  185,0,0,0,0,                            //mov           $0x0,%ecx
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  72,141,123,8,                           //lea           0x8(%rbx),%rdi
-  72,131,195,16,                          //add           $0x10,%rbx
-  76,57,235,                              //cmp           %r13,%rbx
-  72,137,251,                             //mov           %rdi,%rbx
-  118,191,                                //jbe           75 <_sk_start_pipeline_hsw+0x75>
-  76,137,233,                             //mov           %r13,%rcx
-  72,41,249,                              //sub           %rdi,%rcx
-  116,41,                                 //je            e7 <_sk_start_pipeline_hsw+0xe7>
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  76,137,232,                             //mov           %r13,%rax
-  197,248,40,52,36,                       //vmovaps       (%rsp),%xmm6
-  197,248,40,124,36,16,                   //vmovaps       0x10(%rsp),%xmm7
-  197,120,40,68,36,32,                    //vmovaps       0x20(%rsp),%xmm8
-  197,120,40,76,36,48,                    //vmovaps       0x30(%rsp),%xmm9
-  197,120,40,84,36,64,                    //vmovaps       0x40(%rsp),%xmm10
-  197,120,40,92,36,80,                    //vmovaps       0x50(%rsp),%xmm11
-  197,120,40,100,36,96,                   //vmovaps       0x60(%rsp),%xmm12
-  197,120,40,108,36,112,                  //vmovaps       0x70(%rsp),%xmm13
-  197,120,40,180,36,128,0,0,0,            //vmovaps       0x80(%rsp),%xmm14
-  197,120,40,188,36,144,0,0,0,            //vmovaps       0x90(%rsp),%xmm15
-  72,129,196,160,0,0,0,                   //add           $0xa0,%rsp
-  91,                                     //pop           %rbx
-  95,                                     //pop           %rdi
-  94,                                     //pop           %rsi
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  197,248,119,                            //vzeroupper
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_hsw[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,249,110,199,                        //vmovd         %edi,%xmm0
-  196,226,125,88,192,                     //vpbroadcastd  %xmm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  65,184,0,0,0,63,                        //mov           $0x3f000000,%r8d
-  196,193,121,110,200,                    //vmovd         %r8d,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  197,252,88,2,                           //vaddps        (%rdx),%ymm0,%ymm0
-  196,226,125,24,16,                      //vbroadcastss  (%rax),%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  197,236,88,201,                         //vaddps        %ymm1,%ymm2,%ymm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,208,                        //vmovd         %eax,%xmm2
-  196,226,125,88,210,                     //vpbroadcastd  %xmm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  196,226,125,24,88,12,                   //vbroadcastss  0xc(%rax),%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__hsw[] = {
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,92,195,                          //vsubps        %ymm3,%ymm8,%ymm8
-  196,194,93,184,192,                     //vfmadd231ps   %ymm8,%ymm4,%ymm0
-  196,194,85,184,200,                     //vfmadd231ps   %ymm8,%ymm5,%ymm1
-  196,194,77,184,208,                     //vfmadd231ps   %ymm8,%ymm6,%ymm2
-  196,194,69,184,216,                     //vfmadd231ps   %ymm8,%ymm7,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,92,199,                          //vsubps        %ymm7,%ymm8,%ymm8
-  196,226,61,168,196,                     //vfmadd213ps   %ymm4,%ymm8,%ymm0
-  196,226,61,168,205,                     //vfmadd213ps   %ymm5,%ymm8,%ymm1
-  196,226,61,168,214,                     //vfmadd213ps   %ymm6,%ymm8,%ymm2
-  196,226,61,168,223,                     //vfmadd213ps   %ymm7,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_hsw[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,193,124,95,192,                     //vmaxps        %ymm8,%ymm0,%ymm0
-  196,193,116,95,200,                     //vmaxps        %ymm8,%ymm1,%ymm1
-  196,193,108,95,208,                     //vmaxps        %ymm8,%ymm2,%ymm2
-  196,193,100,95,216,                     //vmaxps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  196,193,108,93,208,                     //vminps        %ymm8,%ymm2,%ymm2
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_hsw[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  197,252,93,195,                         //vminps        %ymm3,%ymm0,%ymm0
-  197,244,93,203,                         //vminps        %ymm3,%ymm1,%ymm1
-  197,236,93,211,                         //vminps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_hsw[] = {
-  197,124,40,192,                         //vmovaps       %ymm0,%ymm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,194,                         //vmovaps       %ymm2,%ymm0
-  197,124,41,194,                         //vmovaps       %ymm8,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_hsw[] = {
-  197,124,40,195,                         //vmovaps       %ymm3,%ymm8
-  197,124,40,202,                         //vmovaps       %ymm2,%ymm9
-  197,124,40,209,                         //vmovaps       %ymm1,%ymm10
-  197,124,40,216,                         //vmovaps       %ymm0,%ymm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  197,124,41,220,                         //vmovaps       %ymm11,%ymm4
-  197,124,41,213,                         //vmovaps       %ymm10,%ymm5
-  197,124,41,206,                         //vmovaps       %ymm9,%ymm6
-  197,124,41,199,                         //vmovaps       %ymm8,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,224,                         //vmovaps       %ymm0,%ymm4
-  197,252,40,233,                         //vmovaps       %ymm1,%ymm5
-  197,252,40,242,                         //vmovaps       %ymm2,%ymm6
-  197,252,40,251,                         //vmovaps       %ymm3,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_hsw[] = {
-  197,252,89,195,                         //vmulps        %ymm3,%ymm0,%ymm0
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_hsw[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,65,100,194,200,0,                   //vcmpeqps      %ymm8,%ymm3,%ymm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  197,44,94,211,                          //vdivps        %ymm3,%ymm10,%ymm10
-  196,67,45,74,192,144,                   //vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_hsw[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  197,124,89,208,                         //vmulps        %ymm0,%ymm0,%ymm10
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  197,121,110,216,                        //vmovd         %eax,%xmm11
-  196,66,125,88,219,                      //vpbroadcastd  %xmm11,%ymm11
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  197,121,110,224,                        //vmovd         %eax,%xmm12
-  196,66,125,88,228,                      //vpbroadcastd  %xmm12,%ymm12
-  196,65,125,111,235,                     //vmovdqa       %ymm11,%ymm13
-  196,66,125,168,236,                     //vfmadd213ps   %ymm12,%ymm0,%ymm13
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,66,125,88,246,                      //vpbroadcastd  %xmm14,%ymm14
-  196,66,45,168,238,                      //vfmadd213ps   %ymm14,%ymm10,%ymm13
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  196,193,124,194,194,1,                  //vcmpltps      %ymm10,%ymm0,%ymm0
-  196,195,21,74,193,0,                    //vblendvps     %ymm0,%ymm9,%ymm13,%ymm0
-  197,60,89,201,                          //vmulps        %ymm1,%ymm8,%ymm9
-  197,116,89,233,                         //vmulps        %ymm1,%ymm1,%ymm13
-  196,65,125,111,251,                     //vmovdqa       %ymm11,%ymm15
-  196,66,117,168,252,                     //vfmadd213ps   %ymm12,%ymm1,%ymm15
-  196,66,21,168,254,                      //vfmadd213ps   %ymm14,%ymm13,%ymm15
-  196,193,116,194,202,1,                  //vcmpltps      %ymm10,%ymm1,%ymm1
-  196,195,5,74,201,16,                    //vblendvps     %ymm1,%ymm9,%ymm15,%ymm1
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  197,108,89,202,                         //vmulps        %ymm2,%ymm2,%ymm9
-  196,66,109,168,220,                     //vfmadd213ps   %ymm12,%ymm2,%ymm11
-  196,66,53,168,222,                      //vfmadd213ps   %ymm14,%ymm9,%ymm11
-  196,193,108,194,210,1,                  //vcmpltps      %ymm10,%ymm2,%ymm2
-  196,195,37,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm11,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_hsw[] = {
-  197,124,82,192,                         //vrsqrtps      %ymm0,%ymm8
-  196,65,124,83,216,                      //vrcpps        %ymm8,%ymm11
-  196,65,124,82,224,                      //vrsqrtps      %ymm8,%ymm12
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,232,                          //vmulps        %ymm0,%ymm8,%ymm13
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,66,125,88,201,                      //vpbroadcastd  %xmm9,%ymm9
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,66,125,88,246,                      //vpbroadcastd  %xmm14,%ymm14
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  197,121,110,248,                        //vmovd         %eax,%xmm15
-  196,66,125,88,255,                      //vpbroadcastd  %xmm15,%ymm15
-  196,66,13,168,223,                      //vfmadd213ps   %ymm15,%ymm14,%ymm11
-  196,66,45,184,220,                      //vfmadd231ps   %ymm12,%ymm10,%ymm11
-  196,65,52,93,219,                       //vminps        %ymm11,%ymm9,%ymm11
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  197,121,110,224,                        //vmovd         %eax,%xmm12
-  196,66,125,88,228,                      //vpbroadcastd  %xmm12,%ymm12
-  196,193,124,194,196,1,                  //vcmpltps      %ymm12,%ymm0,%ymm0
-  196,195,37,74,197,0,                    //vblendvps     %ymm0,%ymm13,%ymm11,%ymm0
-  197,124,82,217,                         //vrsqrtps      %ymm1,%ymm11
-  196,65,124,83,235,                      //vrcpps        %ymm11,%ymm13
-  196,65,124,82,219,                      //vrsqrtps      %ymm11,%ymm11
-  196,66,13,168,239,                      //vfmadd213ps   %ymm15,%ymm14,%ymm13
-  196,66,45,184,235,                      //vfmadd231ps   %ymm11,%ymm10,%ymm13
-  197,60,89,217,                          //vmulps        %ymm1,%ymm8,%ymm11
-  196,65,52,93,237,                       //vminps        %ymm13,%ymm9,%ymm13
-  196,193,116,194,204,1,                  //vcmpltps      %ymm12,%ymm1,%ymm1
-  196,195,21,74,203,16,                   //vblendvps     %ymm1,%ymm11,%ymm13,%ymm1
-  197,124,82,218,                         //vrsqrtps      %ymm2,%ymm11
-  196,65,124,83,235,                      //vrcpps        %ymm11,%ymm13
-  196,66,13,168,239,                      //vfmadd213ps   %ymm15,%ymm14,%ymm13
-  196,65,124,82,219,                      //vrsqrtps      %ymm11,%ymm11
-  196,66,45,184,235,                      //vfmadd231ps   %ymm11,%ymm10,%ymm13
-  196,65,52,93,205,                       //vminps        %ymm13,%ymm9,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,193,108,194,212,1,                  //vcmpltps      %ymm12,%ymm2,%ymm2
-  196,195,53,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,56,                                 //jne           556 <_sk_scale_u8_hsw+0x48>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,125,49,192,                      //vpmovzxbd     %xmm8,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,66,125,88,201,                      //vpbroadcastd  %xmm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           55e <_sk_scale_u8_hsw+0x50>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  235,167,                                //jmp           522 <_sk_scale_u8_hsw+0x14>
-};
-
-CODE const uint8_t sk_lerp_1_float_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,226,61,168,196,                     //vfmadd213ps   %ymm4,%ymm8,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,226,61,168,205,                     //vfmadd213ps   %ymm5,%ymm8,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,226,61,168,214,                     //vfmadd213ps   %ymm6,%ymm8,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,226,61,168,223,                     //vfmadd213ps   %ymm7,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,76,                                 //jne           606 <_sk_lerp_u8_hsw+0x5c>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,125,49,192,                      //vpmovzxbd     %xmm8,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,66,125,88,201,                      //vpbroadcastd  %xmm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,226,61,168,196,                     //vfmadd213ps   %ymm4,%ymm8,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,226,61,168,205,                     //vfmadd213ps   %ymm5,%ymm8,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,226,61,168,214,                     //vfmadd213ps   %ymm6,%ymm8,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,226,61,168,223,                     //vfmadd213ps   %ymm7,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           60e <_sk_lerp_u8_hsw+0x64>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  235,147,                                //jmp           5be <_sk_lerp_u8_hsw+0x14>
-};
-
-CODE const uint8_t sk_lerp_565_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,179,0,0,0,                       //jne           6ec <_sk_lerp_565_hsw+0xc1>
-  196,193,122,111,28,122,                 //vmovdqu       (%r10,%rdi,2),%xmm3
-  196,98,125,51,195,                      //vpmovzxwd     %xmm3,%ymm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  196,193,101,219,216,                    //vpand         %ymm8,%ymm3,%ymm3
-  197,124,91,203,                         //vcvtdq2ps     %ymm3,%ymm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,52,89,203,                          //vmulps        %ymm3,%ymm9,%ymm9
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  196,193,101,219,216,                    //vpand         %ymm8,%ymm3,%ymm3
-  197,124,91,211,                         //vcvtdq2ps     %ymm3,%ymm10
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,44,89,211,                          //vmulps        %ymm3,%ymm10,%ymm10
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  196,193,101,219,216,                    //vpand         %ymm8,%ymm3,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,226,53,168,196,                     //vfmadd213ps   %ymm4,%ymm9,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,226,45,168,205,                     //vfmadd213ps   %ymm5,%ymm10,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,226,101,168,214,                    //vfmadd213ps   %ymm6,%ymm3,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,59,255,255,255,                  //ja            63f <_sk_lerp_565_hsw+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,73,0,0,0,                     //lea           0x49(%rip),%r9        # 758 <_sk_lerp_565_hsw+0x12d>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  196,193,97,196,92,122,12,6,             //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,10,5,             //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,8,4,              //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,6,3,              //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,4,2,              //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,92,122,2,1,              //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm3,%xmm3
-  196,193,97,196,28,122,0,                //vpinsrw       $0x0,(%r10,%rdi,2),%xmm3,%xmm3
-  233,231,254,255,255,                    //jmpq          63f <_sk_lerp_565_hsw+0x14>
-  244,                                    //hlt
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  236,                                    //in            (%dx),%al
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,228,                                //jmpq          *%rsp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  220,255,                                //fdivr         %st,%st(7)
-  255,                                    //(bad)
-  255,212,                                //callq         *%rsp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,204,                                //dec           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,192,                                //inc           %eax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_tables_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,141,12,189,0,0,0,0,                  //lea           0x0(,%rdi,4),%r9
-  76,3,8,                                 //add           (%rax),%r9
-  77,133,192,                             //test          %r8,%r8
-  117,121,                                //jne           802 <_sk_load_tables_hsw+0x8e>
-  196,193,126,111,25,                     //vmovdqu       (%r9),%ymm3
-  185,255,0,0,0,                          //mov           $0xff,%ecx
-  197,249,110,193,                        //vmovd         %ecx,%xmm0
-  196,226,125,88,208,                     //vpbroadcastd  %xmm0,%ymm2
-  197,237,219,203,                        //vpand         %ymm3,%ymm2,%ymm1
-  196,65,61,118,192,                      //vpcmpeqd      %ymm8,%ymm8,%ymm8
-  72,139,72,8,                            //mov           0x8(%rax),%rcx
-  76,139,72,16,                           //mov           0x10(%rax),%r9
-  196,65,53,118,201,                      //vpcmpeqd      %ymm9,%ymm9,%ymm9
-  196,226,53,146,4,137,                   //vgatherdps    %ymm9,(%rcx,%ymm1,4),%ymm0
-  197,245,114,211,8,                      //vpsrld        $0x8,%ymm3,%ymm1
-  197,109,219,201,                        //vpand         %ymm1,%ymm2,%ymm9
-  196,65,45,118,210,                      //vpcmpeqd      %ymm10,%ymm10,%ymm10
-  196,130,45,146,12,137,                  //vgatherdps    %ymm10,(%r9,%ymm9,4),%ymm1
-  72,139,64,24,                           //mov           0x18(%rax),%rax
-  197,181,114,211,16,                     //vpsrld        $0x10,%ymm3,%ymm9
-  196,65,109,219,201,                     //vpand         %ymm9,%ymm2,%ymm9
-  196,162,61,146,20,136,                  //vgatherdps    %ymm8,(%rax,%ymm9,4),%ymm2
-  197,229,114,211,24,                     //vpsrld        $0x18,%ymm3,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  185,8,0,0,0,                            //mov           $0x8,%ecx
-  68,41,193,                              //sub           %r8d,%ecx
-  192,225,3,                              //shl           $0x3,%cl
-  73,199,194,255,255,255,255,             //mov           $0xffffffffffffffff,%r10
-  73,211,234,                             //shr           %cl,%r10
-  196,193,249,110,194,                    //vmovq         %r10,%xmm0
-  196,226,125,33,192,                     //vpmovsxbd     %xmm0,%ymm0
-  196,194,125,140,25,                     //vpmaskmovd    (%r9),%ymm0,%ymm3
-  233,99,255,255,255,                     //jmpq          78e <_sk_load_tables_hsw+0x1a>
-};
-
-CODE const uint8_t sk_load_a8_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,50,                                 //jne           86d <_sk_load_a8_hsw+0x42>
-  197,250,126,0,                          //vmovq         (%rax),%xmm0
-  196,226,125,49,192,                     //vpmovzxbd     %xmm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,252,89,217,                         //vmulps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           875 <_sk_load_a8_hsw+0x4a>
-  196,193,249,110,193,                    //vmovq         %r9,%xmm0
-  235,173,                                //jmp           83f <_sk_load_a8_hsw+0x14>
-};
-
-CODE const uint8_t sk_store_a8_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  196,65,57,103,192,                      //vpackuswb     %xmm8,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           8cd <_sk_store_a8_hsw+0x3b>
-  196,65,123,17,4,57,                     //vmovsd        %xmm8,(%r9,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            8c9 <_sk_store_a8_hsw+0x37>
-  196,66,121,48,192,                      //vpmovzxbw     %xmm8,%xmm8
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,67,0,0,0,                      //lea           0x43(%rip),%r8        # 930 <_sk_store_a8_hsw+0x9e>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,20,68,57,6,12,               //vpextrb       $0xc,%xmm8,0x6(%r9,%rdi,1)
-  196,67,121,20,68,57,5,10,               //vpextrb       $0xa,%xmm8,0x5(%r9,%rdi,1)
-  196,67,121,20,68,57,4,8,                //vpextrb       $0x8,%xmm8,0x4(%r9,%rdi,1)
-  196,67,121,20,68,57,3,6,                //vpextrb       $0x6,%xmm8,0x3(%r9,%rdi,1)
-  196,67,121,20,68,57,2,4,                //vpextrb       $0x4,%xmm8,0x2(%r9,%rdi,1)
-  196,67,121,20,68,57,1,2,                //vpextrb       $0x2,%xmm8,0x1(%r9,%rdi,1)
-  196,67,121,20,4,57,0,                   //vpextrb       $0x0,%xmm8,(%r9,%rdi,1)
-  235,154,                                //jmp           8c9 <_sk_store_a8_hsw+0x37>
-  144,                                    //nop
-  246,255,                                //idiv          %bh
-  255,                                    //(bad)
-  255,                                    //(bad)
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,230,                                //jmpq          *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  222,255,                                //fdivrp        %st,%st(7)
-  255,                                    //(bad)
-  255,214,                                //callq         *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,206,                                //dec           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,198,                                //inc           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_565_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,149,0,0,0,                       //jne           9ef <_sk_load_565_hsw+0xa3>
-  196,193,122,111,4,122,                  //vmovdqu       (%r10,%rdi,2),%xmm0
-  196,226,125,51,208,                     //vpmovzxwd     %xmm0,%ymm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  196,226,125,88,192,                     //vpbroadcastd  %xmm0,%ymm0
-  197,253,219,194,                        //vpand         %ymm2,%ymm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,252,89,193,                         //vmulps        %ymm1,%ymm0,%ymm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,226,125,88,201,                     //vpbroadcastd  %xmm1,%ymm1
-  197,245,219,202,                        //vpand         %ymm2,%ymm1,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,229,219,210,                        //vpand         %ymm2,%ymm3,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,89,255,255,255,                  //ja            960 <_sk_load_565_hsw+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,74,0,0,0,                     //lea           0x4a(%rip),%r9        # a5c <_sk_load_565_hsw+0x110>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  196,193,121,196,68,122,12,6,            //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,10,5,            //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,8,4,             //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,6,3,             //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,4,2,             //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,2,1,             //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,4,122,0,                //vpinsrw       $0x0,(%r10,%rdi,2),%xmm0,%xmm0
-  233,5,255,255,255,                      //jmpq          960 <_sk_load_565_hsw+0x14>
-  144,                                    //nop
-  243,255,                                //repz          (bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  235,255,                                //jmp           a61 <_sk_load_565_hsw+0x115>
-  255,                                    //(bad)
-  255,227,                                //jmpq          *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  219,255,                                //(bad)
-  255,                                    //(bad)
-  255,211,                                //callq         *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,203,                                //dec           %ebx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  191,                                    //.byte         0xbf
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_store_565_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,248,65,                         //mov           $0x41f80000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  196,193,53,114,241,11,                  //vpslld        $0xb,%ymm9,%ymm9
-  184,0,0,124,66,                         //mov           $0x427c0000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,66,125,88,210,                      //vpbroadcastd  %xmm10,%ymm10
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,45,114,242,5,                   //vpslld        $0x5,%ymm10,%ymm10
-  196,65,45,235,201,                      //vpor          %ymm9,%ymm10,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,65,53,235,192,                      //vpor          %ymm8,%ymm9,%ymm8
-  196,67,125,57,193,1,                    //vextracti128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           ae4 <_sk_store_565_hsw+0x6c>
-  196,65,122,127,4,121,                   //vmovdqu       %xmm8,(%r9,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            ae0 <_sk_store_565_hsw+0x68>
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,69,0,0,0,                      //lea           0x45(%rip),%r8        # b44 <_sk_store_565_hsw+0xcc>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,21,68,121,12,6,              //vpextrw       $0x6,%xmm8,0xc(%r9,%rdi,2)
-  196,67,121,21,68,121,10,5,              //vpextrw       $0x5,%xmm8,0xa(%r9,%rdi,2)
-  196,67,121,21,68,121,8,4,               //vpextrw       $0x4,%xmm8,0x8(%r9,%rdi,2)
-  196,67,121,21,68,121,6,3,               //vpextrw       $0x3,%xmm8,0x6(%r9,%rdi,2)
-  196,67,121,21,68,121,4,2,               //vpextrw       $0x2,%xmm8,0x4(%r9,%rdi,2)
-  196,67,121,21,68,121,2,1,               //vpextrw       $0x1,%xmm8,0x2(%r9,%rdi,2)
-  196,67,121,21,4,121,0,                  //vpextrw       $0x0,%xmm8,(%r9,%rdi,2)
-  235,159,                                //jmp           ae0 <_sk_store_565_hsw+0x68>
-  15,31,0,                                //nopl          (%rax)
-  244,                                    //hlt
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  236,                                    //in            (%dx),%al
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,228,                                //jmpq          *%rsp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  220,255,                                //fdivr         %st,%st(7)
-  255,                                    //(bad)
-  255,212,                                //callq         *%rsp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,204,                                //dec           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,196,                                //inc           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_8888_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,141,12,189,0,0,0,0,                  //lea           0x0(,%rdi,4),%r9
-  76,3,8,                                 //add           (%rax),%r9
-  77,133,192,                             //test          %r8,%r8
-  117,104,                                //jne           bdd <_sk_load_8888_hsw+0x7d>
-  196,193,126,111,25,                     //vmovdqu       (%r9),%ymm3
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  196,226,125,88,208,                     //vpbroadcastd  %xmm0,%ymm2
-  197,237,219,195,                        //vpand         %ymm3,%ymm2,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,98,125,88,193,                      //vpbroadcastd  %xmm1,%ymm8
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  197,245,114,211,8,                      //vpsrld        $0x8,%ymm3,%ymm1
-  197,237,219,201,                        //vpand         %ymm1,%ymm2,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  197,181,114,211,16,                     //vpsrld        $0x10,%ymm3,%ymm9
-  196,193,109,219,209,                    //vpand         %ymm9,%ymm2,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  197,229,114,211,24,                     //vpsrld        $0x18,%ymm3,%ymm3
-  197,252,91,219,                         //vcvtdq2ps     %ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  185,8,0,0,0,                            //mov           $0x8,%ecx
-  68,41,193,                              //sub           %r8d,%ecx
-  192,225,3,                              //shl           $0x3,%cl
-  72,199,192,255,255,255,255,             //mov           $0xffffffffffffffff,%rax
-  72,211,232,                             //shr           %cl,%rax
-  196,225,249,110,192,                    //vmovq         %rax,%xmm0
-  196,226,125,33,192,                     //vpmovsxbd     %xmm0,%ymm0
-  196,194,125,140,25,                     //vpmaskmovd    (%r9),%ymm0,%ymm3
-  233,116,255,255,255,                    //jmpq          b7a <_sk_load_8888_hsw+0x1a>
-};
-
-CODE const uint8_t sk_store_8888_hsw[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,141,12,189,0,0,0,0,                  //lea           0x0(,%rdi,4),%r9
-  76,3,8,                                 //add           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,66,125,88,192,                      //vpbroadcastd  %xmm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  197,60,89,209,                          //vmulps        %ymm1,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,45,114,242,8,                   //vpslld        $0x8,%ymm10,%ymm10
-  196,65,45,235,201,                      //vpor          %ymm9,%ymm10,%ymm9
-  197,60,89,210,                          //vmulps        %ymm2,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,45,114,242,16,                  //vpslld        $0x10,%ymm10,%ymm10
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,193,61,114,240,24,                  //vpslld        $0x18,%ymm8,%ymm8
-  196,65,45,235,192,                      //vpor          %ymm8,%ymm10,%ymm8
-  196,65,53,235,192,                      //vpor          %ymm8,%ymm9,%ymm8
-  77,133,192,                             //test          %r8,%r8
-  117,12,                                 //jne           c7a <_sk_store_8888_hsw+0x74>
-  196,65,126,127,1,                       //vmovdqu       %ymm8,(%r9)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  185,8,0,0,0,                            //mov           $0x8,%ecx
-  68,41,193,                              //sub           %r8d,%ecx
-  192,225,3,                              //shl           $0x3,%cl
-  72,199,192,255,255,255,255,             //mov           $0xffffffffffffffff,%rax
-  72,211,232,                             //shr           %cl,%rax
-  196,97,249,110,200,                     //vmovq         %rax,%xmm9
-  196,66,125,33,201,                      //vpmovsxbd     %xmm9,%ymm9
-  196,66,53,142,1,                        //vpmaskmovd    %ymm8,%ymm9,(%r9)
-  235,211,                                //jmp           c73 <_sk_store_8888_hsw+0x6d>
-};
-
-CODE const uint8_t sk_load_f16_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,133,201,                             //test          %rcx,%rcx
-  117,97,                                 //jne           d0b <_sk_load_f16_hsw+0x6b>
-  197,121,16,4,248,                       //vmovupd       (%rax,%rdi,8),%xmm8
-  197,249,16,84,248,16,                   //vmovupd       0x10(%rax,%rdi,8),%xmm2
-  197,249,16,92,248,32,                   //vmovupd       0x20(%rax,%rdi,8),%xmm3
-  197,122,111,76,248,48,                  //vmovdqu       0x30(%rax,%rdi,8),%xmm9
-  197,185,97,194,                         //vpunpcklwd    %xmm2,%xmm8,%xmm0
-  197,185,105,210,                        //vpunpckhwd    %xmm2,%xmm8,%xmm2
-  196,193,97,97,201,                      //vpunpcklwd    %xmm9,%xmm3,%xmm1
-  196,193,97,105,217,                     //vpunpckhwd    %xmm9,%xmm3,%xmm3
-  197,121,97,194,                         //vpunpcklwd    %xmm2,%xmm0,%xmm8
-  197,121,105,202,                        //vpunpckhwd    %xmm2,%xmm0,%xmm9
-  197,241,97,211,                         //vpunpcklwd    %xmm3,%xmm1,%xmm2
-  197,241,105,219,                        //vpunpckhwd    %xmm3,%xmm1,%xmm3
-  197,185,108,194,                        //vpunpcklqdq   %xmm2,%xmm8,%xmm0
-  196,226,125,19,192,                     //vcvtph2ps     %xmm0,%ymm0
-  197,185,109,202,                        //vpunpckhqdq   %xmm2,%xmm8,%xmm1
-  196,226,125,19,201,                     //vcvtph2ps     %xmm1,%ymm1
-  197,177,108,211,                        //vpunpcklqdq   %xmm3,%xmm9,%xmm2
-  196,226,125,19,210,                     //vcvtph2ps     %xmm2,%ymm2
-  197,177,109,219,                        //vpunpckhqdq   %xmm3,%xmm9,%xmm3
-  196,226,125,19,219,                     //vcvtph2ps     %xmm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  197,123,16,4,248,                       //vmovsd        (%rax,%rdi,8),%xmm8
-  196,65,49,239,201,                      //vpxor         %xmm9,%xmm9,%xmm9
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,79,                                 //je            d6a <_sk_load_f16_hsw+0xca>
-  197,57,22,68,248,8,                     //vmovhpd       0x8(%rax,%rdi,8),%xmm8,%xmm8
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,67,                                 //jb            d6a <_sk_load_f16_hsw+0xca>
-  197,251,16,84,248,16,                   //vmovsd        0x10(%rax,%rdi,8),%xmm2
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  116,68,                                 //je            d77 <_sk_load_f16_hsw+0xd7>
-  197,233,22,84,248,24,                   //vmovhpd       0x18(%rax,%rdi,8),%xmm2,%xmm2
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,56,                                 //jb            d77 <_sk_load_f16_hsw+0xd7>
-  197,251,16,92,248,32,                   //vmovsd        0x20(%rax,%rdi,8),%xmm3
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  15,132,114,255,255,255,                 //je            cc1 <_sk_load_f16_hsw+0x21>
-  197,225,22,92,248,40,                   //vmovhpd       0x28(%rax,%rdi,8),%xmm3,%xmm3
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  15,130,98,255,255,255,                  //jb            cc1 <_sk_load_f16_hsw+0x21>
-  197,122,126,76,248,48,                  //vmovq         0x30(%rax,%rdi,8),%xmm9
-  233,87,255,255,255,                     //jmpq          cc1 <_sk_load_f16_hsw+0x21>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  197,233,87,210,                         //vxorpd        %xmm2,%xmm2,%xmm2
-  233,74,255,255,255,                     //jmpq          cc1 <_sk_load_f16_hsw+0x21>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  233,65,255,255,255,                     //jmpq          cc1 <_sk_load_f16_hsw+0x21>
-};
-
-CODE const uint8_t sk_store_f16_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  196,195,125,29,192,4,                   //vcvtps2ph     $0x4,%ymm0,%xmm8
-  196,195,125,29,201,4,                   //vcvtps2ph     $0x4,%ymm1,%xmm9
-  196,195,125,29,210,4,                   //vcvtps2ph     $0x4,%ymm2,%xmm10
-  196,195,125,29,219,4,                   //vcvtps2ph     $0x4,%ymm3,%xmm11
-  196,65,57,97,225,                       //vpunpcklwd    %xmm9,%xmm8,%xmm12
-  196,65,57,105,193,                      //vpunpckhwd    %xmm9,%xmm8,%xmm8
-  196,65,41,97,203,                       //vpunpcklwd    %xmm11,%xmm10,%xmm9
-  196,65,41,105,235,                      //vpunpckhwd    %xmm11,%xmm10,%xmm13
-  196,65,25,98,217,                       //vpunpckldq    %xmm9,%xmm12,%xmm11
-  196,65,25,106,209,                      //vpunpckhdq    %xmm9,%xmm12,%xmm10
-  196,65,57,98,205,                       //vpunpckldq    %xmm13,%xmm8,%xmm9
-  196,65,57,106,197,                      //vpunpckhdq    %xmm13,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,27,                                 //jne           de5 <_sk_store_f16_hsw+0x65>
-  197,120,17,28,248,                      //vmovups       %xmm11,(%rax,%rdi,8)
-  197,120,17,84,248,16,                   //vmovups       %xmm10,0x10(%rax,%rdi,8)
-  197,120,17,76,248,32,                   //vmovups       %xmm9,0x20(%rax,%rdi,8)
-  197,122,127,68,248,48,                  //vmovdqu       %xmm8,0x30(%rax,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  197,121,214,28,248,                     //vmovq         %xmm11,(%rax,%rdi,8)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,241,                                //je            de1 <_sk_store_f16_hsw+0x61>
-  197,121,23,92,248,8,                    //vmovhpd       %xmm11,0x8(%rax,%rdi,8)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,229,                                //jb            de1 <_sk_store_f16_hsw+0x61>
-  197,121,214,84,248,16,                  //vmovq         %xmm10,0x10(%rax,%rdi,8)
-  116,221,                                //je            de1 <_sk_store_f16_hsw+0x61>
-  197,121,23,84,248,24,                   //vmovhpd       %xmm10,0x18(%rax,%rdi,8)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,209,                                //jb            de1 <_sk_store_f16_hsw+0x61>
-  197,121,214,76,248,32,                  //vmovq         %xmm9,0x20(%rax,%rdi,8)
-  116,201,                                //je            de1 <_sk_store_f16_hsw+0x61>
-  197,121,23,76,248,40,                   //vmovhpd       %xmm9,0x28(%rax,%rdi,8)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,189,                                //jb            de1 <_sk_store_f16_hsw+0x61>
-  197,121,214,68,248,48,                  //vmovq         %xmm8,0x30(%rax,%rdi,8)
-  235,181,                                //jmp           de1 <_sk_store_f16_hsw+0x61>
-};
-
-CODE const uint8_t sk_store_f32_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  72,141,4,189,0,0,0,0,                   //lea           0x0(,%rdi,4),%rax
-  197,124,20,193,                         //vunpcklps     %ymm1,%ymm0,%ymm8
-  197,124,21,217,                         //vunpckhps     %ymm1,%ymm0,%ymm11
-  197,108,20,203,                         //vunpcklps     %ymm3,%ymm2,%ymm9
-  197,108,21,227,                         //vunpckhps     %ymm3,%ymm2,%ymm12
-  196,65,61,20,209,                       //vunpcklpd     %ymm9,%ymm8,%ymm10
-  196,65,61,21,201,                       //vunpckhpd     %ymm9,%ymm8,%ymm9
-  196,65,37,20,196,                       //vunpcklpd     %ymm12,%ymm11,%ymm8
-  196,65,37,21,220,                       //vunpckhpd     %ymm12,%ymm11,%ymm11
-  72,133,201,                             //test          %rcx,%rcx
-  117,55,                                 //jne           e99 <_sk_store_f32_hsw+0x6d>
-  196,67,45,24,225,1,                     //vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
-  196,67,61,24,235,1,                     //vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
-  196,67,45,6,201,49,                     //vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
-  196,67,61,6,195,49,                     //vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
-  196,65,125,17,36,128,                   //vmovupd       %ymm12,(%r8,%rax,4)
-  196,65,125,17,108,128,32,               //vmovupd       %ymm13,0x20(%r8,%rax,4)
-  196,65,125,17,76,128,64,                //vmovupd       %ymm9,0x40(%r8,%rax,4)
-  196,65,125,17,68,128,96,                //vmovupd       %ymm8,0x60(%r8,%rax,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  196,65,121,17,20,128,                   //vmovupd       %xmm10,(%r8,%rax,4)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,240,                                //je            e95 <_sk_store_f32_hsw+0x69>
-  196,65,121,17,76,128,16,                //vmovupd       %xmm9,0x10(%r8,%rax,4)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,227,                                //jb            e95 <_sk_store_f32_hsw+0x69>
-  196,65,121,17,68,128,32,                //vmovupd       %xmm8,0x20(%r8,%rax,4)
-  116,218,                                //je            e95 <_sk_store_f32_hsw+0x69>
-  196,65,121,17,92,128,48,                //vmovupd       %xmm11,0x30(%r8,%rax,4)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,205,                                //jb            e95 <_sk_store_f32_hsw+0x69>
-  196,67,125,25,84,128,64,1,              //vextractf128  $0x1,%ymm10,0x40(%r8,%rax,4)
-  116,195,                                //je            e95 <_sk_store_f32_hsw+0x69>
-  196,67,125,25,76,128,80,1,              //vextractf128  $0x1,%ymm9,0x50(%r8,%rax,4)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,181,                                //jb            e95 <_sk_store_f32_hsw+0x69>
-  196,67,125,25,68,128,96,1,              //vextractf128  $0x1,%ymm8,0x60(%r8,%rax,4)
-  235,171,                                //jmp           e95 <_sk_store_f32_hsw+0x69>
-};
-
-CODE const uint8_t sk_clamp_x_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,188,95,192,                         //vmaxps        %ymm0,%ymm8,%ymm0
-  196,98,125,88,0,                        //vpbroadcastd  (%rax),%ymm8
-  196,65,53,118,201,                      //vpcmpeqd      %ymm9,%ymm9,%ymm9
-  196,65,61,254,193,                      //vpaddd        %ymm9,%ymm8,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,188,95,201,                         //vmaxps        %ymm1,%ymm8,%ymm1
-  196,98,125,88,0,                        //vpbroadcastd  (%rax),%ymm8
-  196,65,53,118,201,                      //vpcmpeqd      %ymm9,%ymm9,%ymm9
-  196,65,61,254,193,                      //vpaddd        %ymm9,%ymm8,%ymm8
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,124,94,200,                      //vdivps        %ymm8,%ymm0,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,98,61,172,200,                      //vfnmadd213ps  %ymm0,%ymm8,%ymm9
-  197,253,118,192,                        //vpcmpeqd      %ymm0,%ymm0,%ymm0
-  197,189,254,192,                        //vpaddd        %ymm0,%ymm8,%ymm0
-  197,180,93,192,                         //vminps        %ymm0,%ymm9,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,116,94,200,                      //vdivps        %ymm8,%ymm1,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,98,61,172,201,                      //vfnmadd213ps  %ymm1,%ymm8,%ymm9
-  197,245,118,201,                        //vpcmpeqd      %ymm1,%ymm1,%ymm1
-  197,189,254,201,                        //vpaddd        %ymm1,%ymm8,%ymm1
-  197,180,93,201,                         //vminps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,122,16,0,                           //vmovss        (%rax),%xmm8
-  196,66,125,24,200,                      //vbroadcastss  %xmm8,%ymm9
-  196,65,124,92,209,                      //vsubps        %ymm9,%ymm0,%ymm10
-  196,193,58,88,192,                      //vaddss        %xmm8,%xmm8,%xmm0
-  196,226,125,24,192,                     //vbroadcastss  %xmm0,%ymm0
-  197,44,94,192,                          //vdivps        %ymm0,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  196,66,125,172,194,                     //vfnmadd213ps  %ymm10,%ymm0,%ymm8
-  196,193,60,92,193,                      //vsubps        %ymm9,%ymm8,%ymm0
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,192,                          //vsubps        %ymm0,%ymm8,%ymm8
-  197,188,84,192,                         //vandps        %ymm0,%ymm8,%ymm0
-  196,65,61,118,192,                      //vpcmpeqd      %ymm8,%ymm8,%ymm8
-  196,65,53,254,192,                      //vpaddd        %ymm8,%ymm9,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,122,16,0,                           //vmovss        (%rax),%xmm8
-  196,66,125,24,200,                      //vbroadcastss  %xmm8,%ymm9
-  196,65,116,92,209,                      //vsubps        %ymm9,%ymm1,%ymm10
-  196,193,58,88,200,                      //vaddss        %xmm8,%xmm8,%xmm1
-  196,226,125,24,201,                     //vbroadcastss  %xmm1,%ymm1
-  197,44,94,193,                          //vdivps        %ymm1,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  196,66,117,172,194,                     //vfnmadd213ps  %ymm10,%ymm1,%ymm8
-  196,193,60,92,201,                      //vsubps        %ymm9,%ymm8,%ymm1
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,193,                          //vsubps        %ymm1,%ymm8,%ymm8
-  197,188,84,201,                         //vandps        %ymm1,%ymm8,%ymm1
-  196,65,61,118,192,                      //vpcmpeqd      %ymm8,%ymm8,%ymm8
-  196,65,53,254,192,                      //vpaddd        %ymm8,%ymm9,%ymm8
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_hsw[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,98,125,88,195,                      //vpbroadcastd  %xmm3,%ymm8
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,226,125,88,219,                     //vpbroadcastd  %xmm3,%ymm3
-  197,228,89,201,                         //vmulps        %ymm1,%ymm3,%ymm1
-  196,98,125,168,193,                     //vfmadd213ps   %ymm1,%ymm0,%ymm8
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  196,226,125,88,216,                     //vpbroadcastd  %xmm0,%ymm3
-  196,194,109,168,216,                    //vfmadd213ps   %ymm8,%ymm2,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,253,239,192,                        //vpxor         %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,8,                        //vbroadcastss  (%rax),%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,98,125,24,64,16,                    //vbroadcastss  0x10(%rax),%ymm8
-  196,66,117,184,194,                     //vfmadd231ps   %ymm10,%ymm1,%ymm8
-  196,66,125,184,193,                     //vfmadd231ps   %ymm9,%ymm0,%ymm8
-  196,98,125,24,80,4,                     //vbroadcastss  0x4(%rax),%ymm10
-  196,98,125,24,88,12,                    //vbroadcastss  0xc(%rax),%ymm11
-  196,98,125,24,72,20,                    //vbroadcastss  0x14(%rax),%ymm9
-  196,66,117,184,203,                     //vfmadd231ps   %ymm11,%ymm1,%ymm9
-  196,66,125,184,202,                     //vfmadd231ps   %ymm10,%ymm0,%ymm9
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,8,                        //vbroadcastss  (%rax),%ymm9
-  196,98,125,24,80,12,                    //vbroadcastss  0xc(%rax),%ymm10
-  196,98,125,24,88,24,                    //vbroadcastss  0x18(%rax),%ymm11
-  196,98,125,24,64,36,                    //vbroadcastss  0x24(%rax),%ymm8
-  196,66,109,184,195,                     //vfmadd231ps   %ymm11,%ymm2,%ymm8
-  196,66,117,184,194,                     //vfmadd231ps   %ymm10,%ymm1,%ymm8
-  196,66,125,184,193,                     //vfmadd231ps   %ymm9,%ymm0,%ymm8
-  196,98,125,24,80,4,                     //vbroadcastss  0x4(%rax),%ymm10
-  196,98,125,24,88,16,                    //vbroadcastss  0x10(%rax),%ymm11
-  196,98,125,24,96,28,                    //vbroadcastss  0x1c(%rax),%ymm12
-  196,98,125,24,72,40,                    //vbroadcastss  0x28(%rax),%ymm9
-  196,66,109,184,204,                     //vfmadd231ps   %ymm12,%ymm2,%ymm9
-  196,66,117,184,203,                     //vfmadd231ps   %ymm11,%ymm1,%ymm9
-  196,66,125,184,202,                     //vfmadd231ps   %ymm10,%ymm0,%ymm9
-  196,98,125,24,88,8,                     //vbroadcastss  0x8(%rax),%ymm11
-  196,98,125,24,96,20,                    //vbroadcastss  0x14(%rax),%ymm12
-  196,98,125,24,104,32,                   //vbroadcastss  0x20(%rax),%ymm13
-  196,98,125,24,80,44,                    //vbroadcastss  0x2c(%rax),%ymm10
-  196,66,109,184,213,                     //vfmadd231ps   %ymm13,%ymm2,%ymm10
-  196,66,117,184,212,                     //vfmadd231ps   %ymm12,%ymm1,%ymm10
-  196,66,125,184,211,                     //vfmadd231ps   %ymm11,%ymm0,%ymm10
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  197,124,41,210,                         //vmovaps       %ymm10,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,8,                        //vbroadcastss  (%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  196,98,125,24,88,32,                    //vbroadcastss  0x20(%rax),%ymm11
-  196,98,125,24,96,48,                    //vbroadcastss  0x30(%rax),%ymm12
-  196,98,125,24,64,64,                    //vbroadcastss  0x40(%rax),%ymm8
-  196,66,101,184,196,                     //vfmadd231ps   %ymm12,%ymm3,%ymm8
-  196,66,109,184,195,                     //vfmadd231ps   %ymm11,%ymm2,%ymm8
-  196,66,117,184,194,                     //vfmadd231ps   %ymm10,%ymm1,%ymm8
-  196,66,125,184,193,                     //vfmadd231ps   %ymm9,%ymm0,%ymm8
-  196,98,125,24,80,4,                     //vbroadcastss  0x4(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  196,98,125,24,96,36,                    //vbroadcastss  0x24(%rax),%ymm12
-  196,98,125,24,104,52,                   //vbroadcastss  0x34(%rax),%ymm13
-  196,98,125,24,72,68,                    //vbroadcastss  0x44(%rax),%ymm9
-  196,66,101,184,205,                     //vfmadd231ps   %ymm13,%ymm3,%ymm9
-  196,66,109,184,204,                     //vfmadd231ps   %ymm12,%ymm2,%ymm9
-  196,66,117,184,203,                     //vfmadd231ps   %ymm11,%ymm1,%ymm9
-  196,66,125,184,202,                     //vfmadd231ps   %ymm10,%ymm0,%ymm9
-  196,98,125,24,88,8,                     //vbroadcastss  0x8(%rax),%ymm11
-  196,98,125,24,96,24,                    //vbroadcastss  0x18(%rax),%ymm12
-  196,98,125,24,104,40,                   //vbroadcastss  0x28(%rax),%ymm13
-  196,98,125,24,112,56,                   //vbroadcastss  0x38(%rax),%ymm14
-  196,98,125,24,80,72,                    //vbroadcastss  0x48(%rax),%ymm10
-  196,66,101,184,214,                     //vfmadd231ps   %ymm14,%ymm3,%ymm10
-  196,66,109,184,213,                     //vfmadd231ps   %ymm13,%ymm2,%ymm10
-  196,66,117,184,212,                     //vfmadd231ps   %ymm12,%ymm1,%ymm10
-  196,66,125,184,211,                     //vfmadd231ps   %ymm11,%ymm0,%ymm10
-  196,98,125,24,96,12,                    //vbroadcastss  0xc(%rax),%ymm12
-  196,98,125,24,104,28,                   //vbroadcastss  0x1c(%rax),%ymm13
-  196,98,125,24,112,44,                   //vbroadcastss  0x2c(%rax),%ymm14
-  196,98,125,24,120,60,                   //vbroadcastss  0x3c(%rax),%ymm15
-  196,98,125,24,88,76,                    //vbroadcastss  0x4c(%rax),%ymm11
-  196,66,101,184,223,                     //vfmadd231ps   %ymm15,%ymm3,%ymm11
-  196,66,109,184,222,                     //vfmadd231ps   %ymm14,%ymm2,%ymm11
-  196,66,117,184,221,                     //vfmadd231ps   %ymm13,%ymm1,%ymm11
-  196,66,125,184,220,                     //vfmadd231ps   %ymm12,%ymm0,%ymm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  197,124,41,210,                         //vmovaps       %ymm10,%ymm2
-  197,124,41,219,                         //vmovaps       %ymm11,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,66,117,184,209,                     //vfmadd231ps   %ymm9,%ymm1,%ymm10
-  196,66,125,184,208,                     //vfmadd231ps   %ymm8,%ymm0,%ymm10
-  196,98,125,24,64,12,                    //vbroadcastss  0xc(%rax),%ymm8
-  196,98,125,24,72,16,                    //vbroadcastss  0x10(%rax),%ymm9
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  196,66,117,184,217,                     //vfmadd231ps   %ymm9,%ymm1,%ymm11
-  196,66,125,184,216,                     //vfmadd231ps   %ymm8,%ymm0,%ymm11
-  196,98,125,24,64,24,                    //vbroadcastss  0x18(%rax),%ymm8
-  196,98,125,24,72,28,                    //vbroadcastss  0x1c(%rax),%ymm9
-  196,98,125,24,96,32,                    //vbroadcastss  0x20(%rax),%ymm12
-  196,66,117,184,225,                     //vfmadd231ps   %ymm9,%ymm1,%ymm12
-  196,66,125,184,224,                     //vfmadd231ps   %ymm8,%ymm0,%ymm12
-  196,193,124,83,204,                     //vrcpps        %ymm12,%ymm1
-  197,172,89,193,                         //vmulps        %ymm1,%ymm10,%ymm0
-  197,164,89,201,                         //vmulps        %ymm1,%ymm11,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_hsw[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,72,16,                   //vbroadcastss  0x10(%rax),%ymm1
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,184,193,                     //vfmadd231ps   %ymm1,%ymm0,%ymm8
-  196,226,125,24,80,20,                   //vbroadcastss  0x14(%rax),%ymm2
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,184,202,                    //vfmadd231ps   %ymm2,%ymm0,%ymm1
-  196,226,125,24,88,24,                   //vbroadcastss  0x18(%rax),%ymm3
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  196,226,125,184,211,                    //vfmadd231ps   %ymm3,%ymm0,%ymm2
-  196,98,125,24,72,28,                    //vbroadcastss  0x1c(%rax),%ymm9
-  196,226,125,24,88,12,                   //vbroadcastss  0xc(%rax),%ymm3
-  196,194,125,184,217,                    //vfmadd231ps   %ymm9,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_start_pipeline_avx[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  86,                                     //push          %rsi
-  87,                                     //push          %rdi
-  83,                                     //push          %rbx
-  72,129,236,160,0,0,0,                   //sub           $0xa0,%rsp
-  197,120,41,188,36,144,0,0,0,            //vmovaps       %xmm15,0x90(%rsp)
-  197,120,41,180,36,128,0,0,0,            //vmovaps       %xmm14,0x80(%rsp)
-  197,120,41,108,36,112,                  //vmovaps       %xmm13,0x70(%rsp)
-  197,120,41,100,36,96,                   //vmovaps       %xmm12,0x60(%rsp)
-  197,120,41,92,36,80,                    //vmovaps       %xmm11,0x50(%rsp)
-  197,120,41,84,36,64,                    //vmovaps       %xmm10,0x40(%rsp)
-  197,120,41,76,36,48,                    //vmovaps       %xmm9,0x30(%rsp)
-  197,120,41,68,36,32,                    //vmovaps       %xmm8,0x20(%rsp)
-  197,248,41,124,36,16,                   //vmovaps       %xmm7,0x10(%rsp)
-  197,248,41,52,36,                       //vmovaps       %xmm6,(%rsp)
-  77,137,205,                             //mov           %r9,%r13
-  77,137,198,                             //mov           %r8,%r14
-  72,137,203,                             //mov           %rcx,%rbx
-  72,137,214,                             //mov           %rdx,%rsi
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,199,                             //mov           %rax,%r15
-  73,137,244,                             //mov           %rsi,%r12
-  72,141,67,8,                            //lea           0x8(%rbx),%rax
-  76,57,232,                              //cmp           %r13,%rax
-  118,5,                                  //jbe           75 <_sk_start_pipeline_avx+0x75>
-  72,137,223,                             //mov           %rbx,%rdi
-  235,65,                                 //jmp           b6 <_sk_start_pipeline_avx+0xb6>
-  185,0,0,0,0,                            //mov           $0x0,%ecx
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  72,141,123,8,                           //lea           0x8(%rbx),%rdi
-  72,131,195,16,                          //add           $0x10,%rbx
-  76,57,235,                              //cmp           %r13,%rbx
-  72,137,251,                             //mov           %rdi,%rbx
-  118,191,                                //jbe           75 <_sk_start_pipeline_avx+0x75>
-  76,137,233,                             //mov           %r13,%rcx
-  72,41,249,                              //sub           %rdi,%rcx
-  116,41,                                 //je            e7 <_sk_start_pipeline_avx+0xe7>
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  76,137,230,                             //mov           %r12,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,215,                             //callq         *%r15
-  76,137,232,                             //mov           %r13,%rax
-  197,248,40,52,36,                       //vmovaps       (%rsp),%xmm6
-  197,248,40,124,36,16,                   //vmovaps       0x10(%rsp),%xmm7
-  197,120,40,68,36,32,                    //vmovaps       0x20(%rsp),%xmm8
-  197,120,40,76,36,48,                    //vmovaps       0x30(%rsp),%xmm9
-  197,120,40,84,36,64,                    //vmovaps       0x40(%rsp),%xmm10
-  197,120,40,92,36,80,                    //vmovaps       0x50(%rsp),%xmm11
-  197,120,40,100,36,96,                   //vmovaps       0x60(%rsp),%xmm12
-  197,120,40,108,36,112,                  //vmovaps       0x70(%rsp),%xmm13
-  197,120,40,180,36,128,0,0,0,            //vmovaps       0x80(%rsp),%xmm14
-  197,120,40,188,36,144,0,0,0,            //vmovaps       0x90(%rsp),%xmm15
-  72,129,196,160,0,0,0,                   //add           $0xa0,%rsp
-  91,                                     //pop           %rbx
-  95,                                     //pop           %rdi
-  94,                                     //pop           %rsi
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  197,248,119,                            //vzeroupper
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_avx[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,249,110,199,                        //vmovd         %edi,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  65,184,0,0,0,63,                        //mov           $0x3f000000,%r8d
-  196,193,121,110,200,                    //vmovd         %r8d,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  197,252,88,2,                           //vaddps        (%rdx),%ymm0,%ymm0
-  196,226,125,24,16,                      //vbroadcastss  (%rax),%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  197,236,88,201,                         //vaddps        %ymm1,%ymm2,%ymm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,208,                        //vmovd         %eax,%xmm2
-  196,227,121,4,210,0,                    //vpermilps     $0x0,%xmm2,%xmm2
-  196,227,109,24,210,1,                   //vinsertf128   $0x1,%xmm2,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  197,220,87,228,                         //vxorps        %ymm4,%ymm4,%ymm4
-  197,212,87,237,                         //vxorps        %ymm5,%ymm5,%ymm5
-  197,204,87,246,                         //vxorps        %ymm6,%ymm6,%ymm6
-  197,196,87,255,                         //vxorps        %ymm7,%ymm7,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  196,226,125,24,88,12,                   //vbroadcastss  0xc(%rax),%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  197,228,87,219,                         //vxorps        %ymm3,%ymm3,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__avx[] = {
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,92,195,                          //vsubps        %ymm3,%ymm8,%ymm8
-  197,60,89,204,                          //vmulps        %ymm4,%ymm8,%ymm9
-  197,180,88,192,                         //vaddps        %ymm0,%ymm9,%ymm0
-  197,60,89,205,                          //vmulps        %ymm5,%ymm8,%ymm9
-  197,180,88,201,                         //vaddps        %ymm1,%ymm9,%ymm1
-  197,60,89,206,                          //vmulps        %ymm6,%ymm8,%ymm9
-  197,180,88,210,                         //vaddps        %ymm2,%ymm9,%ymm2
-  197,60,89,199,                          //vmulps        %ymm7,%ymm8,%ymm8
-  197,188,88,219,                         //vaddps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,92,199,                          //vsubps        %ymm7,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_avx[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,193,124,95,192,                     //vmaxps        %ymm8,%ymm0,%ymm0
-  196,193,116,95,200,                     //vmaxps        %ymm8,%ymm1,%ymm1
-  196,193,108,95,208,                     //vmaxps        %ymm8,%ymm2,%ymm2
-  196,193,100,95,216,                     //vmaxps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  196,193,124,93,192,                     //vminps        %ymm8,%ymm0,%ymm0
-  196,193,116,93,200,                     //vminps        %ymm8,%ymm1,%ymm1
-  196,193,108,93,208,                     //vminps        %ymm8,%ymm2,%ymm2
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_avx[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  196,193,100,93,216,                     //vminps        %ymm8,%ymm3,%ymm3
-  197,252,93,195,                         //vminps        %ymm3,%ymm0,%ymm0
-  197,244,93,203,                         //vminps        %ymm3,%ymm1,%ymm1
-  197,236,93,211,                         //vminps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,0,                       //vbroadcastss  (%rax),%ymm0
-  196,226,125,24,72,4,                    //vbroadcastss  0x4(%rax),%ymm1
-  196,226,125,24,80,8,                    //vbroadcastss  0x8(%rax),%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_avx[] = {
-  197,124,40,192,                         //vmovaps       %ymm0,%ymm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,194,                         //vmovaps       %ymm2,%ymm0
-  197,124,41,194,                         //vmovaps       %ymm8,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_avx[] = {
-  197,124,40,195,                         //vmovaps       %ymm3,%ymm8
-  197,124,40,202,                         //vmovaps       %ymm2,%ymm9
-  197,124,40,209,                         //vmovaps       %ymm1,%ymm10
-  197,124,40,216,                         //vmovaps       %ymm0,%ymm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  197,124,41,220,                         //vmovaps       %ymm11,%ymm4
-  197,124,41,213,                         //vmovaps       %ymm10,%ymm5
-  197,124,41,206,                         //vmovaps       %ymm9,%ymm6
-  197,124,41,199,                         //vmovaps       %ymm8,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,224,                         //vmovaps       %ymm0,%ymm4
-  197,252,40,233,                         //vmovaps       %ymm1,%ymm5
-  197,252,40,242,                         //vmovaps       %ymm2,%ymm6
-  197,252,40,251,                         //vmovaps       %ymm3,%ymm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,40,196,                         //vmovaps       %ymm4,%ymm0
-  197,252,40,205,                         //vmovaps       %ymm5,%ymm1
-  197,252,40,214,                         //vmovaps       %ymm6,%ymm2
-  197,252,40,223,                         //vmovaps       %ymm7,%ymm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_avx[] = {
-  197,252,89,195,                         //vmulps        %ymm3,%ymm0,%ymm0
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_avx[] = {
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  196,65,100,194,200,0,                   //vcmpeqps      %ymm8,%ymm3,%ymm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,67,121,4,210,0,                     //vpermilps     $0x0,%xmm10,%xmm10
-  196,67,45,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm10,%ymm10
-  197,44,94,211,                          //vdivps        %ymm3,%ymm10,%ymm10
-  196,67,45,74,192,144,                   //vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_avx[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  197,124,89,208,                         //vmulps        %ymm0,%ymm0,%ymm10
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  197,121,110,216,                        //vmovd         %eax,%xmm11
-  196,67,121,4,219,0,                     //vpermilps     $0x0,%xmm11,%xmm11
-  196,67,37,24,219,1,                     //vinsertf128   $0x1,%xmm11,%ymm11,%ymm11
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  197,121,110,224,                        //vmovd         %eax,%xmm12
-  196,67,121,4,228,0,                     //vpermilps     $0x0,%xmm12,%xmm12
-  196,67,29,24,228,1,                     //vinsertf128   $0x1,%xmm12,%ymm12,%ymm12
-  197,36,89,232,                          //vmulps        %ymm0,%ymm11,%ymm13
-  196,65,20,88,236,                       //vaddps        %ymm12,%ymm13,%ymm13
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,67,121,4,246,0,                     //vpermilps     $0x0,%xmm14,%xmm14
-  196,67,13,24,246,1,                     //vinsertf128   $0x1,%xmm14,%ymm14,%ymm14
-  196,65,44,89,213,                       //vmulps        %ymm13,%ymm10,%ymm10
-  196,65,12,88,210,                       //vaddps        %ymm10,%ymm14,%ymm10
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  197,121,110,232,                        //vmovd         %eax,%xmm13
-  196,67,121,4,237,0,                     //vpermilps     $0x0,%xmm13,%xmm13
-  196,67,21,24,237,1,                     //vinsertf128   $0x1,%xmm13,%ymm13,%ymm13
-  196,193,124,194,197,1,                  //vcmpltps      %ymm13,%ymm0,%ymm0
-  196,195,45,74,193,0,                    //vblendvps     %ymm0,%ymm9,%ymm10,%ymm0
-  197,60,89,201,                          //vmulps        %ymm1,%ymm8,%ymm9
-  197,116,89,209,                         //vmulps        %ymm1,%ymm1,%ymm10
-  197,36,89,249,                          //vmulps        %ymm1,%ymm11,%ymm15
-  196,65,28,88,255,                       //vaddps        %ymm15,%ymm12,%ymm15
-  196,65,44,89,215,                       //vmulps        %ymm15,%ymm10,%ymm10
-  196,65,12,88,210,                       //vaddps        %ymm10,%ymm14,%ymm10
-  196,193,116,194,205,1,                  //vcmpltps      %ymm13,%ymm1,%ymm1
-  196,195,45,74,201,16,                   //vblendvps     %ymm1,%ymm9,%ymm10,%ymm1
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  197,108,89,202,                         //vmulps        %ymm2,%ymm2,%ymm9
-  197,36,89,210,                          //vmulps        %ymm2,%ymm11,%ymm10
-  196,65,28,88,210,                       //vaddps        %ymm10,%ymm12,%ymm10
-  196,65,52,89,202,                       //vmulps        %ymm10,%ymm9,%ymm9
-  196,65,12,88,201,                       //vaddps        %ymm9,%ymm14,%ymm9
-  196,193,108,194,213,1,                  //vcmpltps      %ymm13,%ymm2,%ymm2
-  196,195,53,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_avx[] = {
-  197,124,82,192,                         //vrsqrtps      %ymm0,%ymm8
-  196,65,124,83,232,                      //vrcpps        %ymm8,%ymm13
-  196,65,124,82,240,                      //vrsqrtps      %ymm8,%ymm14
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,224,                          //vmulps        %ymm0,%ymm8,%ymm12
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,67,121,4,201,0,                     //vpermilps     $0x0,%xmm9,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,67,121,4,210,0,                     //vpermilps     $0x0,%xmm10,%xmm10
-  196,67,45,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm10,%ymm10
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  197,121,110,216,                        //vmovd         %eax,%xmm11
-  196,67,121,4,219,0,                     //vpermilps     $0x0,%xmm11,%xmm11
-  196,67,37,24,219,1,                     //vinsertf128   $0x1,%xmm11,%ymm11,%ymm11
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  197,121,110,248,                        //vmovd         %eax,%xmm15
-  196,67,121,4,255,0,                     //vpermilps     $0x0,%xmm15,%xmm15
-  196,67,5,24,255,1,                      //vinsertf128   $0x1,%xmm15,%ymm15,%ymm15
-  196,65,20,89,235,                       //vmulps        %ymm11,%ymm13,%ymm13
-  196,65,20,88,239,                       //vaddps        %ymm15,%ymm13,%ymm13
-  196,65,12,89,242,                       //vmulps        %ymm10,%ymm14,%ymm14
-  196,65,12,88,237,                       //vaddps        %ymm13,%ymm14,%ymm13
-  196,65,52,93,237,                       //vminps        %ymm13,%ymm9,%ymm13
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  197,121,110,240,                        //vmovd         %eax,%xmm14
-  196,67,121,4,246,0,                     //vpermilps     $0x0,%xmm14,%xmm14
-  196,67,13,24,246,1,                     //vinsertf128   $0x1,%xmm14,%ymm14,%ymm14
-  196,193,124,194,198,1,                  //vcmpltps      %ymm14,%ymm0,%ymm0
-  196,195,21,74,196,0,                    //vblendvps     %ymm0,%ymm12,%ymm13,%ymm0
-  197,124,82,225,                         //vrsqrtps      %ymm1,%ymm12
-  196,65,124,83,236,                      //vrcpps        %ymm12,%ymm13
-  196,65,124,82,228,                      //vrsqrtps      %ymm12,%ymm12
-  196,65,36,89,237,                       //vmulps        %ymm13,%ymm11,%ymm13
-  196,65,4,88,237,                        //vaddps        %ymm13,%ymm15,%ymm13
-  196,65,44,89,228,                       //vmulps        %ymm12,%ymm10,%ymm12
-  196,65,28,88,229,                       //vaddps        %ymm13,%ymm12,%ymm12
-  197,60,89,233,                          //vmulps        %ymm1,%ymm8,%ymm13
-  196,65,52,93,228,                       //vminps        %ymm12,%ymm9,%ymm12
-  196,193,116,194,206,1,                  //vcmpltps      %ymm14,%ymm1,%ymm1
-  196,195,29,74,205,16,                   //vblendvps     %ymm1,%ymm13,%ymm12,%ymm1
-  197,124,82,226,                         //vrsqrtps      %ymm2,%ymm12
-  196,65,124,83,236,                      //vrcpps        %ymm12,%ymm13
-  196,65,36,89,221,                       //vmulps        %ymm13,%ymm11,%ymm11
-  196,65,4,88,219,                        //vaddps        %ymm11,%ymm15,%ymm11
-  196,65,124,82,228,                      //vrsqrtps      %ymm12,%ymm12
-  196,65,44,89,212,                       //vmulps        %ymm12,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  196,65,52,93,202,                       //vminps        %ymm10,%ymm9,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,193,108,194,214,1,                  //vcmpltps      %ymm14,%ymm2,%ymm2
-  196,195,53,74,208,32,                   //vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_avx[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,80,                                 //jne           639 <_sk_scale_u8_avx+0x60>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,121,49,200,                      //vpmovzxbd     %xmm8,%xmm9
-  196,67,121,4,192,229,                   //vpermilps     $0xe5,%xmm8,%xmm8
-  196,66,121,49,192,                      //vpmovzxbd     %xmm8,%xmm8
-  196,67,53,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,67,121,4,201,0,                     //vpermilps     $0x0,%xmm9,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,188,89,210,                         //vmulps        %ymm2,%ymm8,%ymm2
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           641 <_sk_scale_u8_avx+0x68>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  235,143,                                //jmp           5ed <_sk_scale_u8_avx+0x14>
-};
-
-CODE const uint8_t sk_lerp_1_float_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_avx[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,116,                                //jne           721 <_sk_lerp_u8_avx+0x84>
-  197,122,126,0,                          //vmovq         (%rax),%xmm8
-  196,66,121,49,200,                      //vpmovzxbd     %xmm8,%xmm9
-  196,67,121,4,192,229,                   //vpermilps     $0xe5,%xmm8,%xmm8
-  196,66,121,49,192,                      //vpmovzxbd     %xmm8,%xmm8
-  196,67,53,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
-  196,65,124,91,192,                      //vcvtdq2ps     %ymm8,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,121,110,200,                        //vmovd         %eax,%xmm9
-  196,67,121,4,201,0,                     //vpermilps     $0x0,%xmm9,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,60,89,193,                       //vmulps        %ymm9,%ymm8,%ymm8
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  197,228,92,223,                         //vsubps        %ymm7,%ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  197,228,88,223,                         //vaddps        %ymm7,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           729 <_sk_lerp_u8_avx+0x8c>
-  196,65,249,110,193,                     //vmovq         %r9,%xmm8
-  233,104,255,255,255,                    //jmpq          6b1 <_sk_lerp_u8_avx+0x14>
-};
-
-CODE const uint8_t sk_lerp_565_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,250,0,0,0,                       //jne           851 <_sk_lerp_565_avx+0x108>
-  196,65,122,111,4,122,                   //vmovdqu       (%r10,%rdi,2),%xmm8
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  197,185,105,219,                        //vpunpckhwd    %xmm3,%xmm8,%xmm3
-  196,66,121,51,192,                      //vpmovzxwd     %xmm8,%xmm8
-  196,99,61,24,195,1,                     //vinsertf128   $0x1,%xmm3,%ymm8,%ymm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  196,193,100,84,216,                     //vandps        %ymm8,%ymm3,%ymm3
-  197,124,91,203,                         //vcvtdq2ps     %ymm3,%ymm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,52,89,203,                          //vmulps        %ymm3,%ymm9,%ymm9
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  196,193,100,84,216,                     //vandps        %ymm8,%ymm3,%ymm3
-  197,124,91,211,                         //vcvtdq2ps     %ymm3,%ymm10
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,44,89,211,                          //vmulps        %ymm3,%ymm10,%ymm10
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  196,193,100,84,216,                     //vandps        %ymm8,%ymm3,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  197,252,92,196,                         //vsubps        %ymm4,%ymm0,%ymm0
-  196,193,124,89,193,                     //vmulps        %ymm9,%ymm0,%ymm0
-  197,252,88,196,                         //vaddps        %ymm4,%ymm0,%ymm0
-  197,244,92,205,                         //vsubps        %ymm5,%ymm1,%ymm1
-  196,193,116,89,202,                     //vmulps        %ymm10,%ymm1,%ymm1
-  197,244,88,205,                         //vaddps        %ymm5,%ymm1,%ymm1
-  197,236,92,214,                         //vsubps        %ymm6,%ymm2,%ymm2
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  197,236,88,214,                         //vaddps        %ymm6,%ymm2,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  196,65,57,239,192,                      //vpxor         %xmm8,%xmm8,%xmm8
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,243,254,255,255,                 //ja            75d <_sk_lerp_565_avx+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,75,0,0,0,                     //lea           0x4b(%rip),%r9        # 8c0 <_sk_lerp_565_avx+0x177>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  196,65,97,196,68,122,12,6,              //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm3,%xmm8
-  196,65,57,196,68,122,10,5,              //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,8,4,               //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,6,3,               //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,4,2,               //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,68,122,2,1,               //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm8,%xmm8
-  196,65,57,196,4,122,0,                  //vpinsrw       $0x0,(%r10,%rdi,2),%xmm8,%xmm8
-  233,159,254,255,255,                    //jmpq          75d <_sk_lerp_565_avx+0x14>
-  102,144,                                //xchg          %ax,%ax
-  242,255,                                //repnz         (bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  234,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,226,                                //jmpq          *%rdx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  218,255,                                //(bad)
-  255,                                    //(bad)
-  255,210,                                //callq         *%rdx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,202,                                //dec           %edx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  190,                                    //.byte         0xbe
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_tables_avx[] = {
-  85,                                     //push          %rbp
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  83,                                     //push          %rbx
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,56,2,0,0,                        //jne           b2c <_sk_load_tables_avx+0x250>
-  196,65,124,16,4,184,                    //vmovups       (%r8,%rdi,4),%ymm8
-  187,255,0,0,0,                          //mov           $0xff,%ebx
-  197,249,110,195,                        //vmovd         %ebx,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,99,125,24,200,1,                    //vinsertf128   $0x1,%xmm0,%ymm0,%ymm9
-  196,193,52,84,192,                      //vandps        %ymm8,%ymm9,%ymm0
-  196,193,249,126,193,                    //vmovq         %xmm0,%r9
-  69,137,203,                             //mov           %r9d,%r11d
-  196,195,249,22,194,1,                   //vpextrq       $0x1,%xmm0,%r10
-  69,137,214,                             //mov           %r10d,%r14d
-  73,193,234,32,                          //shr           $0x20,%r10
-  73,193,233,32,                          //shr           $0x20,%r9
-  196,227,125,25,192,1,                   //vextractf128  $0x1,%ymm0,%xmm0
-  196,193,249,126,196,                    //vmovq         %xmm0,%r12
-  69,137,231,                             //mov           %r12d,%r15d
-  196,227,249,22,195,1,                   //vpextrq       $0x1,%xmm0,%rbx
-  65,137,221,                             //mov           %ebx,%r13d
-  72,193,235,32,                          //shr           $0x20,%rbx
-  73,193,236,32,                          //shr           $0x20,%r12
-  72,139,104,8,                           //mov           0x8(%rax),%rbp
-  76,139,64,16,                           //mov           0x10(%rax),%r8
-  196,161,122,16,68,189,0,                //vmovss        0x0(%rbp,%r15,4),%xmm0
-  196,163,121,33,68,165,0,16,             //vinsertps     $0x10,0x0(%rbp,%r12,4),%xmm0,%xmm0
-  196,161,122,16,76,173,0,                //vmovss        0x0(%rbp,%r13,4),%xmm1
-  196,227,121,33,193,32,                  //vinsertps     $0x20,%xmm1,%xmm0,%xmm0
-  197,250,16,76,157,0,                    //vmovss        0x0(%rbp,%rbx,4),%xmm1
-  196,227,121,33,193,48,                  //vinsertps     $0x30,%xmm1,%xmm0,%xmm0
-  196,161,122,16,76,157,0,                //vmovss        0x0(%rbp,%r11,4),%xmm1
-  196,163,113,33,76,141,0,16,             //vinsertps     $0x10,0x0(%rbp,%r9,4),%xmm1,%xmm1
-  196,161,122,16,92,181,0,                //vmovss        0x0(%rbp,%r14,4),%xmm3
-  196,227,113,33,203,32,                  //vinsertps     $0x20,%xmm3,%xmm1,%xmm1
-  196,161,122,16,92,149,0,                //vmovss        0x0(%rbp,%r10,4),%xmm3
-  196,227,113,33,203,48,                  //vinsertps     $0x30,%xmm3,%xmm1,%xmm1
-  196,227,117,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
-  196,193,113,114,208,8,                  //vpsrld        $0x8,%xmm8,%xmm1
-  196,67,125,25,194,1,                    //vextractf128  $0x1,%ymm8,%xmm10
-  196,193,105,114,210,8,                  //vpsrld        $0x8,%xmm10,%xmm2
-  196,227,117,24,202,1,                   //vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
-  197,180,84,201,                         //vandps        %ymm1,%ymm9,%ymm1
-  196,193,249,126,201,                    //vmovq         %xmm1,%r9
-  69,137,203,                             //mov           %r9d,%r11d
-  196,195,249,22,202,1,                   //vpextrq       $0x1,%xmm1,%r10
-  69,137,214,                             //mov           %r10d,%r14d
-  73,193,234,32,                          //shr           $0x20,%r10
-  73,193,233,32,                          //shr           $0x20,%r9
-  196,227,125,25,201,1,                   //vextractf128  $0x1,%ymm1,%xmm1
-  196,225,249,126,205,                    //vmovq         %xmm1,%rbp
-  65,137,239,                             //mov           %ebp,%r15d
-  196,227,249,22,203,1,                   //vpextrq       $0x1,%xmm1,%rbx
-  65,137,220,                             //mov           %ebx,%r12d
-  72,193,235,32,                          //shr           $0x20,%rbx
-  72,193,237,32,                          //shr           $0x20,%rbp
-  196,129,122,16,12,184,                  //vmovss        (%r8,%r15,4),%xmm1
-  196,195,113,33,12,168,16,               //vinsertps     $0x10,(%r8,%rbp,4),%xmm1,%xmm1
-  196,129,122,16,20,160,                  //vmovss        (%r8,%r12,4),%xmm2
-  196,227,113,33,202,32,                  //vinsertps     $0x20,%xmm2,%xmm1,%xmm1
-  196,193,122,16,20,152,                  //vmovss        (%r8,%rbx,4),%xmm2
-  196,227,113,33,202,48,                  //vinsertps     $0x30,%xmm2,%xmm1,%xmm1
-  196,129,122,16,20,152,                  //vmovss        (%r8,%r11,4),%xmm2
-  196,131,105,33,20,136,16,               //vinsertps     $0x10,(%r8,%r9,4),%xmm2,%xmm2
-  196,129,122,16,28,176,                  //vmovss        (%r8,%r14,4),%xmm3
-  196,227,105,33,211,32,                  //vinsertps     $0x20,%xmm3,%xmm2,%xmm2
-  196,129,122,16,28,144,                  //vmovss        (%r8,%r10,4),%xmm3
-  196,227,105,33,211,48,                  //vinsertps     $0x30,%xmm3,%xmm2,%xmm2
-  196,227,109,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm2,%ymm1
-  72,139,64,24,                           //mov           0x18(%rax),%rax
-  196,193,105,114,208,16,                 //vpsrld        $0x10,%xmm8,%xmm2
-  196,193,97,114,210,16,                  //vpsrld        $0x10,%xmm10,%xmm3
-  196,227,109,24,211,1,                   //vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
-  197,180,84,210,                         //vandps        %ymm2,%ymm9,%ymm2
-  196,193,249,126,208,                    //vmovq         %xmm2,%r8
-  69,137,194,                             //mov           %r8d,%r10d
-  196,195,249,22,209,1,                   //vpextrq       $0x1,%xmm2,%r9
-  69,137,203,                             //mov           %r9d,%r11d
-  73,193,233,32,                          //shr           $0x20,%r9
-  73,193,232,32,                          //shr           $0x20,%r8
-  196,227,125,25,210,1,                   //vextractf128  $0x1,%ymm2,%xmm2
-  196,225,249,126,213,                    //vmovq         %xmm2,%rbp
-  65,137,238,                             //mov           %ebp,%r14d
-  196,227,249,22,211,1,                   //vpextrq       $0x1,%xmm2,%rbx
-  65,137,223,                             //mov           %ebx,%r15d
-  72,193,235,32,                          //shr           $0x20,%rbx
-  72,193,237,32,                          //shr           $0x20,%rbp
-  196,161,122,16,20,176,                  //vmovss        (%rax,%r14,4),%xmm2
-  196,227,105,33,20,168,16,               //vinsertps     $0x10,(%rax,%rbp,4),%xmm2,%xmm2
-  196,161,122,16,28,184,                  //vmovss        (%rax,%r15,4),%xmm3
-  196,227,105,33,211,32,                  //vinsertps     $0x20,%xmm3,%xmm2,%xmm2
-  197,250,16,28,152,                      //vmovss        (%rax,%rbx,4),%xmm3
-  196,99,105,33,203,48,                   //vinsertps     $0x30,%xmm3,%xmm2,%xmm9
-  196,161,122,16,28,144,                  //vmovss        (%rax,%r10,4),%xmm3
-  196,163,97,33,28,128,16,                //vinsertps     $0x10,(%rax,%r8,4),%xmm3,%xmm3
-  196,161,122,16,20,152,                  //vmovss        (%rax,%r11,4),%xmm2
-  196,227,97,33,210,32,                   //vinsertps     $0x20,%xmm2,%xmm3,%xmm2
-  196,161,122,16,28,136,                  //vmovss        (%rax,%r9,4),%xmm3
-  196,227,105,33,211,48,                  //vinsertps     $0x30,%xmm3,%xmm2,%xmm2
-  196,195,109,24,209,1,                   //vinsertf128   $0x1,%xmm9,%ymm2,%ymm2
-  196,193,57,114,208,24,                  //vpsrld        $0x18,%xmm8,%xmm8
-  196,193,97,114,210,24,                  //vpsrld        $0x18,%xmm10,%xmm3
-  196,227,61,24,219,1,                    //vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
-  197,124,91,195,                         //vcvtdq2ps     %ymm3,%ymm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,188,89,219,                         //vmulps        %ymm3,%ymm8,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  91,                                     //pop           %rbx
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  93,                                     //pop           %rbp
-  255,224,                                //jmpq          *%rax
-  137,203,                                //mov           %ecx,%ebx
-  128,227,7,                              //and           $0x7,%bl
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  254,203,                                //dec           %bl
-  128,251,6,                              //cmp           $0x6,%bl
-  15,135,185,253,255,255,                 //ja            8fa <_sk_load_tables_avx+0x1e>
-  15,182,219,                             //movzbl        %bl,%ebx
-  76,141,13,137,0,0,0,                    //lea           0x89(%rip),%r9        # bd4 <_sk_load_tables_avx+0x2f8>
-  73,99,28,153,                           //movslq        (%r9,%rbx,4),%rbx
-  76,1,203,                               //add           %r9,%rbx
-  255,227,                                //jmpq          *%rbx
-  196,193,121,110,68,184,24,              //vmovd         0x18(%r8,%rdi,4),%xmm0
-  197,249,112,192,68,                     //vpshufd       $0x44,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  196,99,117,12,192,64,                   //vblendps      $0x40,%ymm0,%ymm1,%ymm8
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,195,121,34,68,184,20,1,             //vpinsrd       $0x1,0x14(%r8,%rdi,4),%xmm0,%xmm0
-  196,99,61,24,192,1,                     //vinsertf128   $0x1,%xmm0,%ymm8,%ymm8
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,195,121,34,68,184,16,0,             //vpinsrd       $0x0,0x10(%r8,%rdi,4),%xmm0,%xmm0
-  196,99,61,24,192,1,                     //vinsertf128   $0x1,%xmm0,%ymm8,%ymm8
-  196,195,57,34,68,184,12,3,              //vpinsrd       $0x3,0xc(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  196,195,57,34,68,184,8,2,               //vpinsrd       $0x2,0x8(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  196,195,57,34,68,184,4,1,               //vpinsrd       $0x1,0x4(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  196,195,57,34,4,184,0,                  //vpinsrd       $0x0,(%r8,%rdi,4),%xmm8,%xmm0
-  196,99,61,12,192,15,                    //vblendps      $0xf,%ymm0,%ymm8,%ymm8
-  233,38,253,255,255,                     //jmpq          8fa <_sk_load_tables_avx+0x1e>
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,224,                                //jmpq          *%rax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,210,                                //callq         *%rdx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,196,                                //inc           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,176,255,255,255,156,                //pushq         -0x63000001(%rax)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-  128,255,255,                            //cmp           $0xff,%bh
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_a8_avx[] = {
-  73,137,200,                             //mov           %rcx,%r8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,1,248,                               //add           %rdi,%rax
-  77,133,192,                             //test          %r8,%r8
-  117,74,                                 //jne           c4a <_sk_load_a8_avx+0x5a>
-  197,250,126,0,                          //vmovq         (%rax),%xmm0
-  196,226,121,49,200,                     //vpmovzxbd     %xmm0,%xmm1
-  196,227,121,4,192,229,                  //vpermilps     $0xe5,%xmm0,%xmm0
-  196,226,121,49,192,                     //vpmovzxbd     %xmm0,%xmm0
-  196,227,117,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,252,89,217,                         //vmulps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  76,137,193,                             //mov           %r8,%rcx
-  255,224,                                //jmpq          *%rax
-  49,201,                                 //xor           %ecx,%ecx
-  77,137,194,                             //mov           %r8,%r10
-  69,49,201,                              //xor           %r9d,%r9d
-  68,15,182,24,                           //movzbl        (%rax),%r11d
-  72,255,192,                             //inc           %rax
-  73,211,227,                             //shl           %cl,%r11
-  77,9,217,                               //or            %r11,%r9
-  72,131,193,8,                           //add           $0x8,%rcx
-  73,255,202,                             //dec           %r10
-  117,234,                                //jne           c52 <_sk_load_a8_avx+0x62>
-  196,193,249,110,193,                    //vmovq         %r9,%xmm0
-  235,149,                                //jmp           c04 <_sk_load_a8_avx+0x14>
-};
-
-CODE const uint8_t sk_store_a8_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  196,65,57,103,192,                      //vpackuswb     %xmm8,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           cb1 <_sk_store_a8_avx+0x42>
-  196,65,123,17,4,57,                     //vmovsd        %xmm8,(%r9,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            cad <_sk_store_a8_avx+0x3e>
-  196,66,121,48,192,                      //vpmovzxbw     %xmm8,%xmm8
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,67,0,0,0,                      //lea           0x43(%rip),%r8        # d14 <_sk_store_a8_avx+0xa5>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,20,68,57,6,12,               //vpextrb       $0xc,%xmm8,0x6(%r9,%rdi,1)
-  196,67,121,20,68,57,5,10,               //vpextrb       $0xa,%xmm8,0x5(%r9,%rdi,1)
-  196,67,121,20,68,57,4,8,                //vpextrb       $0x8,%xmm8,0x4(%r9,%rdi,1)
-  196,67,121,20,68,57,3,6,                //vpextrb       $0x6,%xmm8,0x3(%r9,%rdi,1)
-  196,67,121,20,68,57,2,4,                //vpextrb       $0x4,%xmm8,0x2(%r9,%rdi,1)
-  196,67,121,20,68,57,1,2,                //vpextrb       $0x2,%xmm8,0x1(%r9,%rdi,1)
-  196,67,121,20,4,57,0,                   //vpextrb       $0x0,%xmm8,(%r9,%rdi,1)
-  235,154,                                //jmp           cad <_sk_store_a8_avx+0x3e>
-  144,                                    //nop
-  246,255,                                //idiv          %bh
-  255,                                    //(bad)
-  255,                                    //(bad)
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,230,                                //jmpq          *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  222,255,                                //fdivrp        %st,%st(7)
-  255,                                    //(bad)
-  255,214,                                //callq         *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,206,                                //dec           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,198,                                //inc           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_565_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,209,0,0,0,                       //jne           e0f <_sk_load_565_avx+0xdf>
-  196,193,122,111,4,122,                  //vmovdqu       (%r10,%rdi,2),%xmm0
-  197,241,239,201,                        //vpxor         %xmm1,%xmm1,%xmm1
-  197,249,105,201,                        //vpunpckhwd    %xmm1,%xmm0,%xmm1
-  196,226,121,51,192,                     //vpmovzxwd     %xmm0,%xmm0
-  196,227,125,24,209,1,                   //vinsertf128   $0x1,%xmm1,%ymm0,%ymm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,252,84,194,                         //vandps        %ymm2,%ymm0,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,252,89,193,                         //vmulps        %ymm1,%ymm0,%ymm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  197,249,112,201,0,                      //vpshufd       $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,244,84,202,                         //vandps        %ymm2,%ymm1,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,244,89,203,                         //vmulps        %ymm3,%ymm1,%ymm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,228,84,210,                         //vandps        %ymm2,%ymm3,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,236,89,211,                         //vmulps        %ymm3,%ymm2,%ymm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,29,255,255,255,                  //ja            d44 <_sk_load_565_avx+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,74,0,0,0,                     //lea           0x4a(%rip),%r9        # e7c <_sk_load_565_avx+0x14c>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  197,249,239,192,                        //vpxor         %xmm0,%xmm0,%xmm0
-  196,193,121,196,68,122,12,6,            //vpinsrw       $0x6,0xc(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,10,5,            //vpinsrw       $0x5,0xa(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,8,4,             //vpinsrw       $0x4,0x8(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,6,3,             //vpinsrw       $0x3,0x6(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,4,2,             //vpinsrw       $0x2,0x4(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,68,122,2,1,             //vpinsrw       $0x1,0x2(%r10,%rdi,2),%xmm0,%xmm0
-  196,193,121,196,4,122,0,                //vpinsrw       $0x0,(%r10,%rdi,2),%xmm0,%xmm0
-  233,201,254,255,255,                    //jmpq          d44 <_sk_load_565_avx+0x14>
-  144,                                    //nop
-  243,255,                                //repz          (bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  235,255,                                //jmp           e81 <_sk_load_565_avx+0x151>
-  255,                                    //(bad)
-  255,227,                                //jmpq          *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  219,255,                                //(bad)
-  255,                                    //(bad)
-  255,211,                                //callq         *%rbx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,203,                                //dec           %ebx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  191,                                    //.byte         0xbf
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_store_565_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,248,65,                         //mov           $0x41f80000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  196,193,41,114,241,11,                  //vpslld        $0xb,%xmm9,%xmm10
-  196,67,125,25,201,1,                    //vextractf128  $0x1,%ymm9,%xmm9
-  196,193,49,114,241,11,                  //vpslld        $0xb,%xmm9,%xmm9
-  196,67,45,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
-  184,0,0,124,66,                         //mov           $0x427c0000,%eax
-  197,121,110,208,                        //vmovd         %eax,%xmm10
-  196,67,121,4,210,0,                     //vpermilps     $0x0,%xmm10,%xmm10
-  196,67,45,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm10,%ymm10
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,33,114,242,5,                   //vpslld        $0x5,%xmm10,%xmm11
-  196,67,125,25,210,1,                    //vextractf128  $0x1,%ymm10,%xmm10
-  196,193,41,114,242,5,                   //vpslld        $0x5,%xmm10,%xmm10
-  196,67,37,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
-  196,65,45,86,201,                       //vorpd         %ymm9,%ymm10,%ymm9
-  197,60,89,194,                          //vmulps        %ymm2,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,65,53,86,192,                       //vorpd         %ymm8,%ymm9,%ymm8
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,66,57,43,193,                       //vpackusdw     %xmm9,%xmm8,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           f36 <_sk_store_565_avx+0x9e>
-  196,65,122,127,4,121,                   //vmovdqu       %xmm8,(%r9,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            f32 <_sk_store_565_avx+0x9a>
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,67,0,0,0,                      //lea           0x43(%rip),%r8        # f94 <_sk_store_565_avx+0xfc>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,121,21,68,121,12,6,              //vpextrw       $0x6,%xmm8,0xc(%r9,%rdi,2)
-  196,67,121,21,68,121,10,5,              //vpextrw       $0x5,%xmm8,0xa(%r9,%rdi,2)
-  196,67,121,21,68,121,8,4,               //vpextrw       $0x4,%xmm8,0x8(%r9,%rdi,2)
-  196,67,121,21,68,121,6,3,               //vpextrw       $0x3,%xmm8,0x6(%r9,%rdi,2)
-  196,67,121,21,68,121,4,2,               //vpextrw       $0x2,%xmm8,0x4(%r9,%rdi,2)
-  196,67,121,21,68,121,2,1,               //vpextrw       $0x1,%xmm8,0x2(%r9,%rdi,2)
-  196,67,121,21,4,121,0,                  //vpextrw       $0x0,%xmm8,(%r9,%rdi,2)
-  235,159,                                //jmp           f32 <_sk_store_565_avx+0x9a>
-  144,                                    //nop
-  246,255,                                //idiv          %bh
-  255,                                    //(bad)
-  255,                                    //(bad)
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,230,                                //jmpq          *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  222,255,                                //fdivrp        %st,%st(7)
-  255,                                    //(bad)
-  255,214,                                //callq         *%rsi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,206,                                //dec           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,198,                                //inc           %esi
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_8888_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,16,                              //mov           (%rax),%r10
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,157,0,0,0,                       //jne           105b <_sk_load_8888_avx+0xab>
-  196,65,124,16,12,186,                   //vmovups       (%r10,%rdi,4),%ymm9
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  197,249,110,192,                        //vmovd         %eax,%xmm0
-  197,249,112,192,0,                      //vpshufd       $0x0,%xmm0,%xmm0
-  196,99,125,24,216,1,                    //vinsertf128   $0x1,%xmm0,%ymm0,%ymm11
-  196,193,36,84,193,                      //vandps        %ymm9,%ymm11,%ymm0
-  197,252,91,192,                         //vcvtdq2ps     %ymm0,%ymm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,99,117,24,193,1,                    //vinsertf128   $0x1,%xmm1,%ymm1,%ymm8
-  196,193,124,89,192,                     //vmulps        %ymm8,%ymm0,%ymm0
-  196,193,41,114,209,8,                   //vpsrld        $0x8,%xmm9,%xmm10
-  196,99,125,25,203,1,                    //vextractf128  $0x1,%ymm9,%xmm3
-  197,241,114,211,8,                      //vpsrld        $0x8,%xmm3,%xmm1
-  196,227,45,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm10,%ymm1
-  197,164,84,201,                         //vandps        %ymm1,%ymm11,%ymm1
-  197,252,91,201,                         //vcvtdq2ps     %ymm1,%ymm1
-  196,193,116,89,200,                     //vmulps        %ymm8,%ymm1,%ymm1
-  196,193,41,114,209,16,                  //vpsrld        $0x10,%xmm9,%xmm10
-  197,233,114,211,16,                     //vpsrld        $0x10,%xmm3,%xmm2
-  196,227,45,24,210,1,                    //vinsertf128   $0x1,%xmm2,%ymm10,%ymm2
-  197,164,84,210,                         //vandps        %ymm2,%ymm11,%ymm2
-  197,252,91,210,                         //vcvtdq2ps     %ymm2,%ymm2
-  196,193,108,89,208,                     //vmulps        %ymm8,%ymm2,%ymm2
-  196,193,49,114,209,24,                  //vpsrld        $0x18,%xmm9,%xmm9
-  197,225,114,211,24,                     //vpsrld        $0x18,%xmm3,%xmm3
-  196,227,53,24,219,1,                    //vinsertf128   $0x1,%xmm3,%ymm9,%ymm3
-  197,252,91,219,                         //vcvtdq2ps     %ymm3,%ymm3
-  196,193,100,89,216,                     //vmulps        %ymm8,%ymm3,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  196,65,52,87,201,                       //vxorps        %ymm9,%ymm9,%ymm9
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  15,135,80,255,255,255,                  //ja            fc4 <_sk_load_8888_avx+0x14>
-  69,15,182,192,                          //movzbl        %r8b,%r8d
-  76,141,13,137,0,0,0,                    //lea           0x89(%rip),%r9        # 1108 <_sk_load_8888_avx+0x158>
-  75,99,4,129,                            //movslq        (%r9,%r8,4),%rax
-  76,1,200,                               //add           %r9,%rax
-  255,224,                                //jmpq          *%rax
-  196,193,121,110,68,186,24,              //vmovd         0x18(%r10,%rdi,4),%xmm0
-  197,249,112,192,68,                     //vpshufd       $0x44,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  196,99,117,12,200,64,                   //vblendps      $0x40,%ymm0,%ymm1,%ymm9
-  196,99,125,25,200,1,                    //vextractf128  $0x1,%ymm9,%xmm0
-  196,195,121,34,68,186,20,1,             //vpinsrd       $0x1,0x14(%r10,%rdi,4),%xmm0,%xmm0
-  196,99,53,24,200,1,                     //vinsertf128   $0x1,%xmm0,%ymm9,%ymm9
-  196,99,125,25,200,1,                    //vextractf128  $0x1,%ymm9,%xmm0
-  196,195,121,34,68,186,16,0,             //vpinsrd       $0x0,0x10(%r10,%rdi,4),%xmm0,%xmm0
-  196,99,53,24,200,1,                     //vinsertf128   $0x1,%xmm0,%ymm9,%ymm9
-  196,195,49,34,68,186,12,3,              //vpinsrd       $0x3,0xc(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  196,195,49,34,68,186,8,2,               //vpinsrd       $0x2,0x8(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  196,195,49,34,68,186,4,1,               //vpinsrd       $0x1,0x4(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  196,195,49,34,4,186,0,                  //vpinsrd       $0x0,(%r10,%rdi,4),%xmm9,%xmm0
-  196,99,53,12,200,15,                    //vblendps      $0xf,%ymm0,%ymm9,%ymm9
-  233,188,254,255,255,                    //jmpq          fc4 <_sk_load_8888_avx+0x14>
-  238,                                    //out           %al,(%dx)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,224,                                //jmpq          *%rax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,210,                                //callq         *%rdx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,196,                                //inc           %esp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,176,255,255,255,156,                //pushq         -0x63000001(%rax)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-  128,255,255,                            //cmp           $0xff,%bh
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_store_8888_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,8,                               //mov           (%rax),%r9
-  184,0,0,127,67,                         //mov           $0x437f0000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,67,121,4,192,0,                     //vpermilps     $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,65,125,91,201,                      //vcvtps2dq     %ymm9,%ymm9
-  197,60,89,209,                          //vmulps        %ymm1,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,33,114,242,8,                   //vpslld        $0x8,%xmm10,%xmm11
-  196,67,125,25,210,1,                    //vextractf128  $0x1,%ymm10,%xmm10
-  196,193,41,114,242,8,                   //vpslld        $0x8,%xmm10,%xmm10
-  196,67,37,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
-  196,65,45,86,201,                       //vorpd         %ymm9,%ymm10,%ymm9
-  197,60,89,210,                          //vmulps        %ymm2,%ymm8,%ymm10
-  196,65,125,91,210,                      //vcvtps2dq     %ymm10,%ymm10
-  196,193,33,114,242,16,                  //vpslld        $0x10,%xmm10,%xmm11
-  196,67,125,25,210,1,                    //vextractf128  $0x1,%ymm10,%xmm10
-  196,193,41,114,242,16,                  //vpslld        $0x10,%xmm10,%xmm10
-  196,67,37,24,210,1,                     //vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,65,125,91,192,                      //vcvtps2dq     %ymm8,%ymm8
-  196,193,33,114,240,24,                  //vpslld        $0x18,%xmm8,%xmm11
-  196,67,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm8
-  196,193,57,114,240,24,                  //vpslld        $0x18,%xmm8,%xmm8
-  196,67,37,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm11,%ymm8
-  196,65,45,86,192,                       //vorpd         %ymm8,%ymm10,%ymm8
-  196,65,53,86,192,                       //vorpd         %ymm8,%ymm9,%ymm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,10,                                 //jne           11c8 <_sk_store_8888_avx+0xa4>
-  196,65,124,17,4,185,                    //vmovups       %ymm8,(%r9,%rdi,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  65,137,200,                             //mov           %ecx,%r8d
-  65,128,224,7,                           //and           $0x7,%r8b
-  65,254,200,                             //dec           %r8b
-  65,128,248,6,                           //cmp           $0x6,%r8b
-  119,236,                                //ja            11c4 <_sk_store_8888_avx+0xa0>
-  65,15,182,192,                          //movzbl        %r8b,%eax
-  76,141,5,85,0,0,0,                      //lea           0x55(%rip),%r8        # 1238 <_sk_store_8888_avx+0x114>
-  73,99,4,128,                            //movslq        (%r8,%rax,4),%rax
-  76,1,192,                               //add           %r8,%rax
-  255,224,                                //jmpq          *%rax
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,67,121,22,76,185,24,2,              //vpextrd       $0x2,%xmm9,0x18(%r9,%rdi,4)
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,67,121,22,76,185,20,1,              //vpextrd       $0x1,%xmm9,0x14(%r9,%rdi,4)
-  196,67,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm9
-  196,65,122,17,76,185,16,                //vmovss        %xmm9,0x10(%r9,%rdi,4)
-  196,67,121,22,68,185,12,3,              //vpextrd       $0x3,%xmm8,0xc(%r9,%rdi,4)
-  196,67,121,22,68,185,8,2,               //vpextrd       $0x2,%xmm8,0x8(%r9,%rdi,4)
-  196,67,121,22,68,185,4,1,               //vpextrd       $0x1,%xmm8,0x4(%r9,%rdi,4)
-  196,65,121,126,4,185,                   //vmovd         %xmm8,(%r9,%rdi,4)
-  235,143,                                //jmp           11c4 <_sk_store_8888_avx+0xa0>
-  15,31,0,                                //nopl          (%rax)
-  245,                                    //cmc
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  237,                                    //in            (%dx),%eax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,229,                                //jmpq          *%rbp
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //(bad)
-  221,255,                                //(bad)
-  255,                                    //(bad)
-  255,208,                                //callq         *%rax
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,194,                                //inc           %edx
-  255,                                    //(bad)
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-  180,255,                                //mov           $0xff,%ah
-  255,                                    //(bad)
-  255,                                    //.byte         0xff
-};
-
-CODE const uint8_t sk_load_f16_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,133,201,                             //test          %rcx,%rcx
-  15,133,2,1,0,0,                         //jne           1364 <_sk_load_f16_avx+0x110>
-  197,121,16,4,248,                       //vmovupd       (%rax,%rdi,8),%xmm8
-  197,249,16,84,248,16,                   //vmovupd       0x10(%rax,%rdi,8),%xmm2
-  197,249,16,92,248,32,                   //vmovupd       0x20(%rax,%rdi,8),%xmm3
-  197,122,111,76,248,48,                  //vmovdqu       0x30(%rax,%rdi,8),%xmm9
-  197,185,97,194,                         //vpunpcklwd    %xmm2,%xmm8,%xmm0
-  197,185,105,210,                        //vpunpckhwd    %xmm2,%xmm8,%xmm2
-  196,193,97,97,201,                      //vpunpcklwd    %xmm9,%xmm3,%xmm1
-  196,193,97,105,217,                     //vpunpckhwd    %xmm9,%xmm3,%xmm3
-  197,121,97,194,                         //vpunpcklwd    %xmm2,%xmm0,%xmm8
-  197,249,105,194,                        //vpunpckhwd    %xmm2,%xmm0,%xmm0
-  197,241,97,211,                         //vpunpcklwd    %xmm3,%xmm1,%xmm2
-  197,113,105,203,                        //vpunpckhwd    %xmm3,%xmm1,%xmm9
-  184,0,4,0,4,                            //mov           $0x4000400,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  197,249,112,219,0,                      //vpshufd       $0x0,%xmm3,%xmm3
-  196,193,97,101,200,                     //vpcmpgtw      %xmm8,%xmm3,%xmm1
-  196,65,113,223,192,                     //vpandn        %xmm8,%xmm1,%xmm8
-  197,225,101,200,                        //vpcmpgtw      %xmm0,%xmm3,%xmm1
-  197,241,223,192,                        //vpandn        %xmm0,%xmm1,%xmm0
-  197,225,101,202,                        //vpcmpgtw      %xmm2,%xmm3,%xmm1
-  197,241,223,202,                        //vpandn        %xmm2,%xmm1,%xmm1
-  196,193,97,101,209,                     //vpcmpgtw      %xmm9,%xmm3,%xmm2
-  196,193,105,223,209,                    //vpandn        %xmm9,%xmm2,%xmm2
-  196,66,121,51,208,                      //vpmovzxwd     %xmm8,%xmm10
-  196,98,121,51,201,                      //vpmovzxwd     %xmm1,%xmm9
-  197,225,239,219,                        //vpxor         %xmm3,%xmm3,%xmm3
-  197,57,105,195,                         //vpunpckhwd    %xmm3,%xmm8,%xmm8
-  197,241,105,203,                        //vpunpckhwd    %xmm3,%xmm1,%xmm1
-  196,98,121,51,216,                      //vpmovzxwd     %xmm0,%xmm11
-  196,98,121,51,226,                      //vpmovzxwd     %xmm2,%xmm12
-  197,121,105,235,                        //vpunpckhwd    %xmm3,%xmm0,%xmm13
-  197,105,105,243,                        //vpunpckhwd    %xmm3,%xmm2,%xmm14
-  196,193,121,114,242,13,                 //vpslld        $0xd,%xmm10,%xmm0
-  196,193,105,114,241,13,                 //vpslld        $0xd,%xmm9,%xmm2
-  196,227,125,24,194,1,                   //vinsertf128   $0x1,%xmm2,%ymm0,%ymm0
-  184,0,0,128,119,                        //mov           $0x77800000,%eax
-  197,249,110,208,                        //vmovd         %eax,%xmm2
-  197,249,112,210,0,                      //vpshufd       $0x0,%xmm2,%xmm2
-  196,99,109,24,202,1,                    //vinsertf128   $0x1,%xmm2,%ymm2,%ymm9
-  197,180,89,192,                         //vmulps        %ymm0,%ymm9,%ymm0
-  196,193,105,114,240,13,                 //vpslld        $0xd,%xmm8,%xmm2
-  197,241,114,241,13,                     //vpslld        $0xd,%xmm1,%xmm1
-  196,227,109,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm2,%ymm1
-  197,180,89,201,                         //vmulps        %ymm1,%ymm9,%ymm1
-  196,193,105,114,243,13,                 //vpslld        $0xd,%xmm11,%xmm2
-  196,193,97,114,244,13,                  //vpslld        $0xd,%xmm12,%xmm3
-  196,227,109,24,211,1,                   //vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
-  197,180,89,210,                         //vmulps        %ymm2,%ymm9,%ymm2
-  196,193,57,114,245,13,                  //vpslld        $0xd,%xmm13,%xmm8
-  196,193,97,114,246,13,                  //vpslld        $0xd,%xmm14,%xmm3
-  196,227,61,24,219,1,                    //vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
-  197,180,89,219,                         //vmulps        %ymm3,%ymm9,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  197,123,16,4,248,                       //vmovsd        (%rax,%rdi,8),%xmm8
-  196,65,49,239,201,                      //vpxor         %xmm9,%xmm9,%xmm9
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,79,                                 //je            13c3 <_sk_load_f16_avx+0x16f>
-  197,57,22,68,248,8,                     //vmovhpd       0x8(%rax,%rdi,8),%xmm8,%xmm8
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,67,                                 //jb            13c3 <_sk_load_f16_avx+0x16f>
-  197,251,16,84,248,16,                   //vmovsd        0x10(%rax,%rdi,8),%xmm2
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  116,68,                                 //je            13d0 <_sk_load_f16_avx+0x17c>
-  197,233,22,84,248,24,                   //vmovhpd       0x18(%rax,%rdi,8),%xmm2,%xmm2
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,56,                                 //jb            13d0 <_sk_load_f16_avx+0x17c>
-  197,251,16,92,248,32,                   //vmovsd        0x20(%rax,%rdi,8),%xmm3
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  15,132,209,254,255,255,                 //je            1279 <_sk_load_f16_avx+0x25>
-  197,225,22,92,248,40,                   //vmovhpd       0x28(%rax,%rdi,8),%xmm3,%xmm3
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  15,130,193,254,255,255,                 //jb            1279 <_sk_load_f16_avx+0x25>
-  197,122,126,76,248,48,                  //vmovq         0x30(%rax,%rdi,8),%xmm9
-  233,182,254,255,255,                    //jmpq          1279 <_sk_load_f16_avx+0x25>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  197,233,87,210,                         //vxorpd        %xmm2,%xmm2,%xmm2
-  233,169,254,255,255,                    //jmpq          1279 <_sk_load_f16_avx+0x25>
-  197,225,87,219,                         //vxorpd        %xmm3,%xmm3,%xmm3
-  233,160,254,255,255,                    //jmpq          1279 <_sk_load_f16_avx+0x25>
-};
-
-CODE const uint8_t sk_store_f16_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  184,0,0,128,7,                          //mov           $0x7800000,%eax
-  197,121,110,192,                        //vmovd         %eax,%xmm8
-  196,65,121,112,192,0,                   //vpshufd       $0x0,%xmm8,%xmm8
-  196,67,61,24,192,1,                     //vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
-  197,60,89,200,                          //vmulps        %ymm0,%ymm8,%ymm9
-  196,67,125,25,202,1,                    //vextractf128  $0x1,%ymm9,%xmm10
-  196,193,41,114,210,13,                  //vpsrld        $0xd,%xmm10,%xmm10
-  196,193,49,114,209,13,                  //vpsrld        $0xd,%xmm9,%xmm9
-  197,60,89,217,                          //vmulps        %ymm1,%ymm8,%ymm11
-  196,67,125,25,220,1,                    //vextractf128  $0x1,%ymm11,%xmm12
-  196,193,25,114,212,13,                  //vpsrld        $0xd,%xmm12,%xmm12
-  196,193,33,114,211,13,                  //vpsrld        $0xd,%xmm11,%xmm11
-  197,60,89,234,                          //vmulps        %ymm2,%ymm8,%ymm13
-  196,67,125,25,238,1,                    //vextractf128  $0x1,%ymm13,%xmm14
-  196,193,9,114,214,13,                   //vpsrld        $0xd,%xmm14,%xmm14
-  196,193,17,114,213,13,                  //vpsrld        $0xd,%xmm13,%xmm13
-  197,60,89,195,                          //vmulps        %ymm3,%ymm8,%ymm8
-  196,67,125,25,199,1,                    //vextractf128  $0x1,%ymm8,%xmm15
-  196,193,1,114,215,13,                   //vpsrld        $0xd,%xmm15,%xmm15
-  196,193,57,114,208,13,                  //vpsrld        $0xd,%xmm8,%xmm8
-  196,193,33,115,251,2,                   //vpslldq       $0x2,%xmm11,%xmm11
-  196,65,33,235,201,                      //vpor          %xmm9,%xmm11,%xmm9
-  196,193,33,115,252,2,                   //vpslldq       $0x2,%xmm12,%xmm11
-  196,65,33,235,226,                      //vpor          %xmm10,%xmm11,%xmm12
-  196,193,57,115,248,2,                   //vpslldq       $0x2,%xmm8,%xmm8
-  196,65,57,235,197,                      //vpor          %xmm13,%xmm8,%xmm8
-  196,193,41,115,255,2,                   //vpslldq       $0x2,%xmm15,%xmm10
-  196,65,41,235,238,                      //vpor          %xmm14,%xmm10,%xmm13
-  196,65,49,98,216,                       //vpunpckldq    %xmm8,%xmm9,%xmm11
-  196,65,49,106,208,                      //vpunpckhdq    %xmm8,%xmm9,%xmm10
-  196,65,25,98,205,                       //vpunpckldq    %xmm13,%xmm12,%xmm9
-  196,65,25,106,197,                      //vpunpckhdq    %xmm13,%xmm12,%xmm8
-  72,133,201,                             //test          %rcx,%rcx
-  117,31,                                 //jne           14af <_sk_store_f16_avx+0xd6>
-  196,65,120,17,28,248,                   //vmovups       %xmm11,(%r8,%rdi,8)
-  196,65,120,17,84,248,16,                //vmovups       %xmm10,0x10(%r8,%rdi,8)
-  196,65,120,17,76,248,32,                //vmovups       %xmm9,0x20(%r8,%rdi,8)
-  196,65,122,127,68,248,48,               //vmovdqu       %xmm8,0x30(%r8,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  196,65,121,214,28,248,                  //vmovq         %xmm11,(%r8,%rdi,8)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,240,                                //je            14ab <_sk_store_f16_avx+0xd2>
-  196,65,121,23,92,248,8,                 //vmovhpd       %xmm11,0x8(%r8,%rdi,8)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,227,                                //jb            14ab <_sk_store_f16_avx+0xd2>
-  196,65,121,214,84,248,16,               //vmovq         %xmm10,0x10(%r8,%rdi,8)
-  116,218,                                //je            14ab <_sk_store_f16_avx+0xd2>
-  196,65,121,23,84,248,24,                //vmovhpd       %xmm10,0x18(%r8,%rdi,8)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,205,                                //jb            14ab <_sk_store_f16_avx+0xd2>
-  196,65,121,214,76,248,32,               //vmovq         %xmm9,0x20(%r8,%rdi,8)
-  116,196,                                //je            14ab <_sk_store_f16_avx+0xd2>
-  196,65,121,23,76,248,40,                //vmovhpd       %xmm9,0x28(%r8,%rdi,8)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,183,                                //jb            14ab <_sk_store_f16_avx+0xd2>
-  196,65,121,214,68,248,48,               //vmovq         %xmm8,0x30(%r8,%rdi,8)
-  235,174,                                //jmp           14ab <_sk_store_f16_avx+0xd2>
-};
-
-CODE const uint8_t sk_store_f32_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  76,139,0,                               //mov           (%rax),%r8
-  72,141,4,189,0,0,0,0,                   //lea           0x0(,%rdi,4),%rax
-  197,124,20,193,                         //vunpcklps     %ymm1,%ymm0,%ymm8
-  197,124,21,217,                         //vunpckhps     %ymm1,%ymm0,%ymm11
-  197,108,20,203,                         //vunpcklps     %ymm3,%ymm2,%ymm9
-  197,108,21,227,                         //vunpckhps     %ymm3,%ymm2,%ymm12
-  196,65,61,20,209,                       //vunpcklpd     %ymm9,%ymm8,%ymm10
-  196,65,61,21,201,                       //vunpckhpd     %ymm9,%ymm8,%ymm9
-  196,65,37,20,196,                       //vunpcklpd     %ymm12,%ymm11,%ymm8
-  196,65,37,21,220,                       //vunpckhpd     %ymm12,%ymm11,%ymm11
-  72,133,201,                             //test          %rcx,%rcx
-  117,55,                                 //jne           156a <_sk_store_f32_avx+0x6d>
-  196,67,45,24,225,1,                     //vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
-  196,67,61,24,235,1,                     //vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
-  196,67,45,6,201,49,                     //vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
-  196,67,61,6,195,49,                     //vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
-  196,65,125,17,36,128,                   //vmovupd       %ymm12,(%r8,%rax,4)
-  196,65,125,17,108,128,32,               //vmovupd       %ymm13,0x20(%r8,%rax,4)
-  196,65,125,17,76,128,64,                //vmovupd       %ymm9,0x40(%r8,%rax,4)
-  196,65,125,17,68,128,96,                //vmovupd       %ymm8,0x60(%r8,%rax,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-  196,65,121,17,20,128,                   //vmovupd       %xmm10,(%r8,%rax,4)
-  72,131,249,1,                           //cmp           $0x1,%rcx
-  116,240,                                //je            1566 <_sk_store_f32_avx+0x69>
-  196,65,121,17,76,128,16,                //vmovupd       %xmm9,0x10(%r8,%rax,4)
-  72,131,249,3,                           //cmp           $0x3,%rcx
-  114,227,                                //jb            1566 <_sk_store_f32_avx+0x69>
-  196,65,121,17,68,128,32,                //vmovupd       %xmm8,0x20(%r8,%rax,4)
-  116,218,                                //je            1566 <_sk_store_f32_avx+0x69>
-  196,65,121,17,92,128,48,                //vmovupd       %xmm11,0x30(%r8,%rax,4)
-  72,131,249,5,                           //cmp           $0x5,%rcx
-  114,205,                                //jb            1566 <_sk_store_f32_avx+0x69>
-  196,67,125,25,84,128,64,1,              //vextractf128  $0x1,%ymm10,0x40(%r8,%rax,4)
-  116,195,                                //je            1566 <_sk_store_f32_avx+0x69>
-  196,67,125,25,76,128,80,1,              //vextractf128  $0x1,%ymm9,0x50(%r8,%rax,4)
-  72,131,249,7,                           //cmp           $0x7,%rcx
-  114,181,                                //jb            1566 <_sk_store_f32_avx+0x69>
-  196,67,125,25,68,128,96,1,              //vextractf128  $0x1,%ymm8,0x60(%r8,%rax,4)
-  235,171,                                //jmp           1566 <_sk_store_f32_avx+0x69>
-};
-
-CODE const uint8_t sk_clamp_x_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,95,200,                          //vmaxps        %ymm0,%ymm8,%ymm9
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,121,254,194,                    //vpaddd        %xmm10,%xmm0,%xmm0
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,192,1,                    //vinsertf128   $0x1,%xmm0,%ymm8,%ymm0
-  197,180,93,192,                         //vminps        %ymm0,%ymm9,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,95,201,                          //vmaxps        %ymm1,%ymm8,%ymm9
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,99,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm1
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,113,254,202,                    //vpaddd        %xmm10,%xmm1,%xmm1
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm8,%ymm1
-  197,180,93,201,                         //vminps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,124,94,200,                      //vdivps        %ymm8,%ymm0,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,65,52,89,200,                       //vmulps        %ymm8,%ymm9,%ymm9
-  196,65,124,92,201,                      //vsubps        %ymm9,%ymm0,%ymm9
-  196,99,125,25,192,1,                    //vextractf128  $0x1,%ymm8,%xmm0
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,121,254,194,                    //vpaddd        %xmm10,%xmm0,%xmm0
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,192,1,                    //vinsertf128   $0x1,%xmm0,%ymm8,%ymm0
-  197,180,93,192,                         //vminps        %ymm0,%ymm9,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,65,116,94,200,                      //vdivps        %ymm8,%ymm1,%ymm9
-  196,67,125,8,201,1,                     //vroundps      $0x1,%ymm9,%ymm9
-  196,65,52,89,200,                       //vmulps        %ymm8,%ymm9,%ymm9
-  196,65,116,92,201,                      //vsubps        %ymm9,%ymm1,%ymm9
-  196,99,125,25,193,1,                    //vextractf128  $0x1,%ymm8,%xmm1
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,113,254,202,                    //vpaddd        %xmm10,%xmm1,%xmm1
-  196,65,57,254,194,                      //vpaddd        %xmm10,%xmm8,%xmm8
-  196,227,61,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm8,%ymm1
-  197,180,93,201,                         //vminps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,121,110,0,                          //vmovd         (%rax),%xmm8
-  196,65,121,112,200,0,                   //vpshufd       $0x0,%xmm8,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,124,92,209,                      //vsubps        %ymm9,%ymm0,%ymm10
-  196,193,58,88,192,                      //vaddss        %xmm8,%xmm8,%xmm0
-  196,227,121,4,192,0,                    //vpermilps     $0x0,%xmm0,%xmm0
-  196,227,125,24,192,1,                   //vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
-  197,44,94,192,                          //vdivps        %ymm0,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  197,188,89,192,                         //vmulps        %ymm0,%ymm8,%ymm0
-  197,172,92,192,                         //vsubps        %ymm0,%ymm10,%ymm0
-  196,193,124,92,193,                     //vsubps        %ymm9,%ymm0,%ymm0
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,192,                          //vsubps        %ymm0,%ymm8,%ymm8
-  197,60,84,192,                          //vandps        %ymm0,%ymm8,%ymm8
-  196,99,125,25,200,1,                    //vextractf128  $0x1,%ymm9,%xmm0
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,121,254,194,                    //vpaddd        %xmm10,%xmm0,%xmm0
-  196,65,49,254,202,                      //vpaddd        %xmm10,%xmm9,%xmm9
-  196,227,53,24,192,1,                    //vinsertf128   $0x1,%xmm0,%ymm9,%ymm0
-  197,188,93,192,                         //vminps        %ymm0,%ymm8,%ymm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,121,110,0,                          //vmovd         (%rax),%xmm8
-  196,65,121,112,200,0,                   //vpshufd       $0x0,%xmm8,%xmm9
-  196,67,53,24,201,1,                     //vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
-  196,65,116,92,209,                      //vsubps        %ymm9,%ymm1,%ymm10
-  196,193,58,88,200,                      //vaddss        %xmm8,%xmm8,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,44,94,193,                          //vdivps        %ymm1,%ymm10,%ymm8
-  196,67,125,8,192,1,                     //vroundps      $0x1,%ymm8,%ymm8
-  197,188,89,201,                         //vmulps        %ymm1,%ymm8,%ymm1
-  197,172,92,201,                         //vsubps        %ymm1,%ymm10,%ymm1
-  196,193,116,92,201,                     //vsubps        %ymm9,%ymm1,%ymm1
-  196,65,60,87,192,                       //vxorps        %ymm8,%ymm8,%ymm8
-  197,60,92,193,                          //vsubps        %ymm1,%ymm8,%ymm8
-  197,60,84,193,                          //vandps        %ymm1,%ymm8,%ymm8
-  196,99,125,25,201,1,                    //vextractf128  $0x1,%ymm9,%xmm1
-  196,65,41,118,210,                      //vpcmpeqd      %xmm10,%xmm10,%xmm10
-  196,193,113,254,202,                    //vpaddd        %xmm10,%xmm1,%xmm1
-  196,65,49,254,202,                      //vpaddd        %xmm10,%xmm9,%xmm9
-  196,227,53,24,201,1,                    //vinsertf128   $0x1,%xmm1,%ymm9,%ymm1
-  197,188,93,201,                         //vminps        %ymm1,%ymm8,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_avx[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,228,89,192,                         //vmulps        %ymm0,%ymm3,%ymm0
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  197,249,110,216,                        //vmovd         %eax,%xmm3
-  196,227,121,4,219,0,                    //vpermilps     $0x0,%xmm3,%xmm3
-  196,227,101,24,219,1,                   //vinsertf128   $0x1,%xmm3,%ymm3,%ymm3
-  197,228,89,201,                         //vmulps        %ymm1,%ymm3,%ymm1
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  197,249,110,200,                        //vmovd         %eax,%xmm1
-  196,227,121,4,201,0,                    //vpermilps     $0x0,%xmm1,%xmm1
-  196,227,117,24,201,1,                   //vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
-  197,244,89,202,                         //vmulps        %ymm2,%ymm1,%ymm1
-  197,252,88,217,                         //vaddps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,252,87,192,                         //vxorps        %ymm0,%ymm0,%ymm0
-  197,244,87,201,                         //vxorps        %ymm1,%ymm1,%ymm1
-  197,236,87,210,                         //vxorps        %ymm2,%ymm2,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,8,                     //vbroadcastss  0x8(%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,12,                    //vbroadcastss  0xc(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  197,172,89,201,                         //vmulps        %ymm1,%ymm10,%ymm1
-  196,193,116,88,203,                     //vaddps        %ymm11,%ymm1,%ymm1
-  197,180,89,192,                         //vmulps        %ymm0,%ymm9,%ymm0
-  197,252,88,201,                         //vaddps        %ymm1,%ymm0,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,12,                    //vbroadcastss  0xc(%rax),%ymm9
-  196,98,125,24,80,24,                    //vbroadcastss  0x18(%rax),%ymm10
-  196,98,125,24,88,36,                    //vbroadcastss  0x24(%rax),%ymm11
-  197,44,89,210,                          //vmulps        %ymm2,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  196,98,125,24,88,28,                    //vbroadcastss  0x1c(%rax),%ymm11
-  196,98,125,24,96,40,                    //vbroadcastss  0x28(%rax),%ymm12
-  197,36,89,218,                          //vmulps        %ymm2,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,200,                          //vmulps        %ymm0,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  196,98,125,24,96,32,                    //vbroadcastss  0x20(%rax),%ymm12
-  196,98,125,24,104,44,                   //vbroadcastss  0x2c(%rax),%ymm13
-  197,156,89,210,                         //vmulps        %ymm2,%ymm12,%ymm2
-  196,193,108,88,213,                     //vaddps        %ymm13,%ymm2,%ymm2
-  197,164,89,201,                         //vmulps        %ymm1,%ymm11,%ymm1
-  197,244,88,202,                         //vaddps        %ymm2,%ymm1,%ymm1
-  197,172,89,192,                         //vmulps        %ymm0,%ymm10,%ymm0
-  197,252,88,209,                         //vaddps        %ymm1,%ymm0,%ymm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,16,                    //vbroadcastss  0x10(%rax),%ymm9
-  196,98,125,24,80,32,                    //vbroadcastss  0x20(%rax),%ymm10
-  196,98,125,24,88,48,                    //vbroadcastss  0x30(%rax),%ymm11
-  196,98,125,24,96,64,                    //vbroadcastss  0x40(%rax),%ymm12
-  197,36,89,219,                          //vmulps        %ymm3,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,210,                          //vmulps        %ymm2,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,20,                    //vbroadcastss  0x14(%rax),%ymm10
-  196,98,125,24,88,36,                    //vbroadcastss  0x24(%rax),%ymm11
-  196,98,125,24,96,52,                    //vbroadcastss  0x34(%rax),%ymm12
-  196,98,125,24,104,68,                   //vbroadcastss  0x44(%rax),%ymm13
-  197,28,89,227,                          //vmulps        %ymm3,%ymm12,%ymm12
-  196,65,28,88,229,                       //vaddps        %ymm13,%ymm12,%ymm12
-  197,36,89,218,                          //vmulps        %ymm2,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,200,                          //vmulps        %ymm0,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  196,98,125,24,88,24,                    //vbroadcastss  0x18(%rax),%ymm11
-  196,98,125,24,96,40,                    //vbroadcastss  0x28(%rax),%ymm12
-  196,98,125,24,104,56,                   //vbroadcastss  0x38(%rax),%ymm13
-  196,98,125,24,112,72,                   //vbroadcastss  0x48(%rax),%ymm14
-  197,20,89,235,                          //vmulps        %ymm3,%ymm13,%ymm13
-  196,65,20,88,238,                       //vaddps        %ymm14,%ymm13,%ymm13
-  197,28,89,226,                          //vmulps        %ymm2,%ymm12,%ymm12
-  196,65,28,88,229,                       //vaddps        %ymm13,%ymm12,%ymm12
-  197,36,89,217,                          //vmulps        %ymm1,%ymm11,%ymm11
-  196,65,36,88,220,                       //vaddps        %ymm12,%ymm11,%ymm11
-  197,44,89,208,                          //vmulps        %ymm0,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  196,98,125,24,88,12,                    //vbroadcastss  0xc(%rax),%ymm11
-  196,98,125,24,96,28,                    //vbroadcastss  0x1c(%rax),%ymm12
-  196,98,125,24,104,44,                   //vbroadcastss  0x2c(%rax),%ymm13
-  196,98,125,24,112,60,                   //vbroadcastss  0x3c(%rax),%ymm14
-  196,98,125,24,120,76,                   //vbroadcastss  0x4c(%rax),%ymm15
-  197,140,89,219,                         //vmulps        %ymm3,%ymm14,%ymm3
-  196,193,100,88,223,                     //vaddps        %ymm15,%ymm3,%ymm3
-  197,148,89,210,                         //vmulps        %ymm2,%ymm13,%ymm2
-  197,236,88,211,                         //vaddps        %ymm3,%ymm2,%ymm2
-  197,156,89,201,                         //vmulps        %ymm1,%ymm12,%ymm1
-  197,244,88,202,                         //vaddps        %ymm2,%ymm1,%ymm1
-  197,164,89,192,                         //vmulps        %ymm0,%ymm11,%ymm0
-  197,252,88,217,                         //vaddps        %ymm1,%ymm0,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  197,124,41,201,                         //vmovaps       %ymm9,%ymm1
-  197,124,41,210,                         //vmovaps       %ymm10,%ymm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,98,125,24,0,                        //vbroadcastss  (%rax),%ymm8
-  196,98,125,24,72,4,                     //vbroadcastss  0x4(%rax),%ymm9
-  196,98,125,24,80,8,                     //vbroadcastss  0x8(%rax),%ymm10
-  197,52,89,201,                          //vmulps        %ymm1,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  197,60,89,192,                          //vmulps        %ymm0,%ymm8,%ymm8
-  196,65,60,88,193,                       //vaddps        %ymm9,%ymm8,%ymm8
-  196,98,125,24,72,12,                    //vbroadcastss  0xc(%rax),%ymm9
-  196,98,125,24,80,16,                    //vbroadcastss  0x10(%rax),%ymm10
-  196,98,125,24,88,20,                    //vbroadcastss  0x14(%rax),%ymm11
-  197,44,89,209,                          //vmulps        %ymm1,%ymm10,%ymm10
-  196,65,44,88,211,                       //vaddps        %ymm11,%ymm10,%ymm10
-  197,52,89,200,                          //vmulps        %ymm0,%ymm9,%ymm9
-  196,65,52,88,202,                       //vaddps        %ymm10,%ymm9,%ymm9
-  196,98,125,24,80,24,                    //vbroadcastss  0x18(%rax),%ymm10
-  196,98,125,24,88,28,                    //vbroadcastss  0x1c(%rax),%ymm11
-  196,98,125,24,96,32,                    //vbroadcastss  0x20(%rax),%ymm12
-  197,164,89,201,                         //vmulps        %ymm1,%ymm11,%ymm1
-  196,193,116,88,204,                     //vaddps        %ymm12,%ymm1,%ymm1
-  197,172,89,192,                         //vmulps        %ymm0,%ymm10,%ymm0
-  197,252,88,193,                         //vaddps        %ymm1,%ymm0,%ymm0
-  197,252,83,200,                         //vrcpps        %ymm0,%ymm1
-  197,188,89,193,                         //vmulps        %ymm1,%ymm8,%ymm0
-  197,180,89,201,                         //vmulps        %ymm1,%ymm9,%ymm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_avx[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  196,226,125,24,72,16,                   //vbroadcastss  0x10(%rax),%ymm1
-  196,226,125,24,16,                      //vbroadcastss  (%rax),%ymm2
-  197,244,89,200,                         //vmulps        %ymm0,%ymm1,%ymm1
-  197,108,88,193,                         //vaddps        %ymm1,%ymm2,%ymm8
-  196,226,125,24,72,20,                   //vbroadcastss  0x14(%rax),%ymm1
-  196,226,125,24,80,4,                    //vbroadcastss  0x4(%rax),%ymm2
-  197,244,89,200,                         //vmulps        %ymm0,%ymm1,%ymm1
-  197,236,88,201,                         //vaddps        %ymm1,%ymm2,%ymm1
-  196,226,125,24,80,24,                   //vbroadcastss  0x18(%rax),%ymm2
-  196,226,125,24,88,8,                    //vbroadcastss  0x8(%rax),%ymm3
-  197,236,89,208,                         //vmulps        %ymm0,%ymm2,%ymm2
-  197,228,88,210,                         //vaddps        %ymm2,%ymm3,%ymm2
-  196,226,125,24,88,28,                   //vbroadcastss  0x1c(%rax),%ymm3
-  196,98,125,24,72,12,                    //vbroadcastss  0xc(%rax),%ymm9
-  197,228,89,192,                         //vmulps        %ymm0,%ymm3,%ymm0
-  197,180,88,216,                         //vaddps        %ymm0,%ymm9,%ymm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  197,124,41,192,                         //vmovaps       %ymm8,%ymm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_start_pipeline_sse41[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  86,                                     //push          %rsi
-  87,                                     //push          %rdi
-  83,                                     //push          %rbx
-  72,129,236,160,0,0,0,                   //sub           $0xa0,%rsp
-  68,15,41,188,36,144,0,0,0,              //movaps        %xmm15,0x90(%rsp)
-  68,15,41,180,36,128,0,0,0,              //movaps        %xmm14,0x80(%rsp)
-  68,15,41,108,36,112,                    //movaps        %xmm13,0x70(%rsp)
-  68,15,41,100,36,96,                     //movaps        %xmm12,0x60(%rsp)
-  68,15,41,92,36,80,                      //movaps        %xmm11,0x50(%rsp)
-  68,15,41,84,36,64,                      //movaps        %xmm10,0x40(%rsp)
-  68,15,41,76,36,48,                      //movaps        %xmm9,0x30(%rsp)
-  68,15,41,68,36,32,                      //movaps        %xmm8,0x20(%rsp)
-  15,41,124,36,16,                        //movaps        %xmm7,0x10(%rsp)
-  15,41,52,36,                            //movaps        %xmm6,(%rsp)
-  77,137,207,                             //mov           %r9,%r15
-  77,137,198,                             //mov           %r8,%r14
-  72,137,203,                             //mov           %rcx,%rbx
-  72,137,214,                             //mov           %rdx,%rsi
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,196,                             //mov           %rax,%r12
-  73,137,245,                             //mov           %rsi,%r13
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  76,57,248,                              //cmp           %r15,%rax
-  118,5,                                  //jbe           73 <_sk_start_pipeline_sse41+0x73>
-  72,137,216,                             //mov           %rbx,%rax
-  235,52,                                 //jmp           a7 <_sk_start_pipeline_sse41+0xa7>
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,238,                             //mov           %r13,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,212,                             //callq         *%r12
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  72,131,195,8,                           //add           $0x8,%rbx
-  76,57,251,                              //cmp           %r15,%rbx
-  72,137,195,                             //mov           %rax,%rbx
-  118,204,                                //jbe           73 <_sk_start_pipeline_sse41+0x73>
-  15,40,52,36,                            //movaps        (%rsp),%xmm6
-  15,40,124,36,16,                        //movaps        0x10(%rsp),%xmm7
-  68,15,40,68,36,32,                      //movaps        0x20(%rsp),%xmm8
-  68,15,40,76,36,48,                      //movaps        0x30(%rsp),%xmm9
-  68,15,40,84,36,64,                      //movaps        0x40(%rsp),%xmm10
-  68,15,40,92,36,80,                      //movaps        0x50(%rsp),%xmm11
-  68,15,40,100,36,96,                     //movaps        0x60(%rsp),%xmm12
-  68,15,40,108,36,112,                    //movaps        0x70(%rsp),%xmm13
-  68,15,40,180,36,128,0,0,0,              //movaps        0x80(%rsp),%xmm14
-  68,15,40,188,36,144,0,0,0,              //movaps        0x90(%rsp),%xmm15
-  72,129,196,160,0,0,0,                   //add           $0xa0,%rsp
-  91,                                     //pop           %rbx
-  95,                                     //pop           %rdi
-  94,                                     //pop           %rsi
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_sse41[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  102,15,110,199,                         //movd          %edi,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  185,0,0,0,63,                           //mov           $0x3f000000,%ecx
-  102,15,110,209,                         //movd          %ecx,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,16,2,                                //movups        (%rdx),%xmm0
-  15,88,193,                              //addps         %xmm1,%xmm0
-  102,15,110,8,                           //movd          (%rax),%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,16,24,                               //movups        (%rax),%xmm3
-  15,40,195,                              //movaps        %xmm3,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__sse41[] = {
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,195,                           //subps         %xmm3,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,204,                           //mulps         %xmm4,%xmm9
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,205,                           //mulps         %xmm5,%xmm9
-  65,15,88,201,                           //addps         %xmm9,%xmm1
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,206,                           //mulps         %xmm6,%xmm9
-  65,15,88,209,                           //addps         %xmm9,%xmm2
-  68,15,89,199,                           //mulps         %xmm7,%xmm8
-  65,15,88,216,                           //addps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,199,                           //subps         %xmm7,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_sse41[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  65,15,95,192,                           //maxps         %xmm8,%xmm0
-  65,15,95,200,                           //maxps         %xmm8,%xmm1
-  65,15,95,208,                           //maxps         %xmm8,%xmm2
-  65,15,95,216,                           //maxps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,192,                           //minps         %xmm8,%xmm0
-  65,15,93,200,                           //minps         %xmm8,%xmm1
-  65,15,93,208,                           //minps         %xmm8,%xmm2
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_sse41[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  15,93,195,                              //minps         %xmm3,%xmm0
-  15,93,203,                              //minps         %xmm3,%xmm1
-  15,93,211,                              //minps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,15,16,80,8,                         //movss         0x8(%rax),%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_sse41[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_sse41[] = {
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  68,15,40,202,                           //movaps        %xmm2,%xmm9
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  68,15,40,216,                           //movaps        %xmm0,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  65,15,40,227,                           //movaps        %xmm11,%xmm4
-  65,15,40,234,                           //movaps        %xmm10,%xmm5
-  65,15,40,241,                           //movaps        %xmm9,%xmm6
-  65,15,40,248,                           //movaps        %xmm8,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,224,                              //movaps        %xmm0,%xmm4
-  15,40,233,                              //movaps        %xmm1,%xmm5
-  15,40,242,                              //movaps        %xmm2,%xmm6
-  15,40,251,                              //movaps        %xmm3,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_sse41[] = {
-  15,89,195,                              //mulps         %xmm3,%xmm0
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_sse41[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,94,203,                           //divps         %xmm3,%xmm9
-  68,15,194,195,4,                        //cmpneqps      %xmm3,%xmm8
-  69,15,84,193,                           //andps         %xmm9,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_sse41[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,40,211,                           //movaps        %xmm11,%xmm10
-  68,15,89,208,                           //mulps         %xmm0,%xmm10
-  68,15,40,240,                           //movaps        %xmm0,%xmm14
-  69,15,89,246,                           //mulps         %xmm14,%xmm14
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  69,15,88,204,                           //addps         %xmm12,%xmm9
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  102,68,15,110,232,                      //movd          %eax,%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  69,15,89,206,                           //mulps         %xmm14,%xmm9
-  69,15,88,205,                           //addps         %xmm13,%xmm9
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  102,68,15,110,240,                      //movd          %eax,%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  65,15,194,198,1,                        //cmpltps       %xmm14,%xmm0
-  102,69,15,56,20,202,                    //blendvps      %xmm0,%xmm10,%xmm9
-  69,15,40,251,                           //movaps        %xmm11,%xmm15
-  68,15,89,249,                           //mulps         %xmm1,%xmm15
-  15,40,193,                              //movaps        %xmm1,%xmm0
-  15,89,192,                              //mulps         %xmm0,%xmm0
-  69,15,40,208,                           //movaps        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  69,15,88,212,                           //addps         %xmm12,%xmm10
-  68,15,89,208,                           //mulps         %xmm0,%xmm10
-  69,15,88,213,                           //addps         %xmm13,%xmm10
-  65,15,194,206,1,                        //cmpltps       %xmm14,%xmm1
-  15,40,193,                              //movaps        %xmm1,%xmm0
-  102,69,15,56,20,215,                    //blendvps      %xmm0,%xmm15,%xmm10
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  15,89,192,                              //mulps         %xmm0,%xmm0
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  69,15,88,196,                           //addps         %xmm12,%xmm8
-  68,15,89,192,                           //mulps         %xmm0,%xmm8
-  69,15,88,197,                           //addps         %xmm13,%xmm8
-  65,15,194,214,1,                        //cmpltps       %xmm14,%xmm2
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  102,69,15,56,20,195,                    //blendvps      %xmm0,%xmm11,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,193,                           //movaps        %xmm9,%xmm0
-  65,15,40,202,                           //movaps        %xmm10,%xmm1
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_sse41[] = {
-  72,131,236,24,                          //sub           $0x18,%rsp
-  15,41,60,36,                            //movaps        %xmm7,(%rsp)
-  15,40,254,                              //movaps        %xmm6,%xmm7
-  15,40,245,                              //movaps        %xmm5,%xmm6
-  15,40,236,                              //movaps        %xmm4,%xmm5
-  15,40,227,                              //movaps        %xmm3,%xmm4
-  15,40,218,                              //movaps        %xmm2,%xmm3
-  15,40,209,                              //movaps        %xmm1,%xmm2
-  68,15,82,192,                           //rsqrtps       %xmm0,%xmm8
-  69,15,83,200,                           //rcpps         %xmm8,%xmm9
-  69,15,82,248,                           //rsqrtps       %xmm8,%xmm15
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,40,211,                           //movaps        %xmm11,%xmm10
-  68,15,89,208,                           //mulps         %xmm0,%xmm10
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  102,68,15,110,232,                      //movd          %eax,%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  102,68,15,110,240,                      //movd          %eax,%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  69,15,89,205,                           //mulps         %xmm13,%xmm9
-  69,15,88,206,                           //addps         %xmm14,%xmm9
-  69,15,89,252,                           //mulps         %xmm12,%xmm15
-  69,15,88,249,                           //addps         %xmm9,%xmm15
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  69,15,93,207,                           //minps         %xmm15,%xmm9
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  102,68,15,110,248,                      //movd          %eax,%xmm15
-  69,15,198,255,0,                        //shufps        $0x0,%xmm15,%xmm15
-  65,15,194,199,1,                        //cmpltps       %xmm15,%xmm0
-  102,69,15,56,20,202,                    //blendvps      %xmm0,%xmm10,%xmm9
-  68,15,82,210,                           //rsqrtps       %xmm2,%xmm10
-  65,15,83,194,                           //rcpps         %xmm10,%xmm0
-  69,15,82,210,                           //rsqrtps       %xmm10,%xmm10
-  65,15,89,197,                           //mulps         %xmm13,%xmm0
-  65,15,88,198,                           //addps         %xmm14,%xmm0
-  69,15,89,212,                           //mulps         %xmm12,%xmm10
-  68,15,88,208,                           //addps         %xmm0,%xmm10
-  65,15,40,200,                           //movaps        %xmm8,%xmm1
-  65,15,93,202,                           //minps         %xmm10,%xmm1
-  69,15,40,211,                           //movaps        %xmm11,%xmm10
-  68,15,89,210,                           //mulps         %xmm2,%xmm10
-  65,15,194,215,1,                        //cmpltps       %xmm15,%xmm2
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  102,65,15,56,20,202,                    //blendvps      %xmm0,%xmm10,%xmm1
-  15,82,195,                              //rsqrtps       %xmm3,%xmm0
-  15,83,208,                              //rcpps         %xmm0,%xmm2
-  65,15,89,213,                           //mulps         %xmm13,%xmm2
-  65,15,88,214,                           //addps         %xmm14,%xmm2
-  15,82,192,                              //rsqrtps       %xmm0,%xmm0
-  65,15,89,196,                           //mulps         %xmm12,%xmm0
-  15,88,194,                              //addps         %xmm2,%xmm0
-  68,15,93,192,                           //minps         %xmm0,%xmm8
-  68,15,89,219,                           //mulps         %xmm3,%xmm11
-  65,15,194,223,1,                        //cmpltps       %xmm15,%xmm3
-  15,40,195,                              //movaps        %xmm3,%xmm0
-  102,69,15,56,20,195,                    //blendvps      %xmm0,%xmm11,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,193,                           //movaps        %xmm9,%xmm0
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  15,40,220,                              //movaps        %xmm4,%xmm3
-  15,40,229,                              //movaps        %xmm5,%xmm4
-  15,40,238,                              //movaps        %xmm6,%xmm5
-  15,40,247,                              //movaps        %xmm7,%xmm6
-  15,40,60,36,                            //movaps        (%rsp),%xmm7
-  72,131,196,24,                          //add           $0x18,%rsp
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,56,49,4,56,                   //pmovzxbd      (%rax,%rdi,1),%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_1_float_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,56,49,4,56,                   //pmovzxbd      (%rax,%rdi,1),%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_565_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,56,51,4,120,                  //pmovzxwd      (%rax,%rdi,2),%xmm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,195,                           //cvtdq2ps      %xmm3,%xmm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,194,                           //mulps         %xmm10,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,203,                           //mulps         %xmm11,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_tables_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,8,                               //mov           (%rax),%rcx
-  76,139,64,8,                            //mov           0x8(%rax),%r8
-  243,68,15,111,4,185,                    //movdqu        (%rcx,%rdi,4),%xmm8
-  185,255,0,0,0,                          //mov           $0xff,%ecx
-  102,15,110,193,                         //movd          %ecx,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,65,15,111,200,                      //movdqa        %xmm8,%xmm1
-  102,15,114,209,8,                       //psrld         $0x8,%xmm1
-  102,15,219,200,                         //pand          %xmm0,%xmm1
-  102,65,15,111,208,                      //movdqa        %xmm8,%xmm2
-  102,15,114,210,16,                      //psrld         $0x10,%xmm2
-  102,15,219,208,                         //pand          %xmm0,%xmm2
-  102,65,15,219,192,                      //pand          %xmm8,%xmm0
-  102,72,15,58,22,193,1,                  //pextrq        $0x1,%xmm0,%rcx
-  65,137,201,                             //mov           %ecx,%r9d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,73,15,126,194,                      //movq          %xmm0,%r10
-  69,137,211,                             //mov           %r10d,%r11d
-  73,193,234,32,                          //shr           $0x20,%r10
-  243,67,15,16,4,152,                     //movss         (%r8,%r11,4),%xmm0
-  102,67,15,58,33,4,144,16,               //insertps      $0x10,(%r8,%r10,4),%xmm0
-  102,67,15,58,33,4,136,32,               //insertps      $0x20,(%r8,%r9,4),%xmm0
-  102,65,15,58,33,4,136,48,               //insertps      $0x30,(%r8,%rcx,4),%xmm0
-  76,139,64,16,                           //mov           0x10(%rax),%r8
-  102,73,15,58,22,202,1,                  //pextrq        $0x1,%xmm1,%r10
-  77,137,209,                             //mov           %r10,%r9
-  73,193,233,32,                          //shr           $0x20,%r9
-  102,72,15,126,201,                      //movq          %xmm1,%rcx
-  65,137,203,                             //mov           %ecx,%r11d
-  65,129,227,255,255,255,0,               //and           $0xffffff,%r11d
-  72,193,233,30,                          //shr           $0x1e,%rcx
-  65,129,226,255,255,255,0,               //and           $0xffffff,%r10d
-  243,67,15,16,12,152,                    //movss         (%r8,%r11,4),%xmm1
-  102,65,15,58,33,12,8,16,                //insertps      $0x10,(%r8,%rcx,1),%xmm1
-  243,67,15,16,28,144,                    //movss         (%r8,%r10,4),%xmm3
-  102,15,58,33,203,32,                    //insertps      $0x20,%xmm3,%xmm1
-  243,67,15,16,28,136,                    //movss         (%r8,%r9,4),%xmm3
-  102,15,58,33,203,48,                    //insertps      $0x30,%xmm3,%xmm1
-  76,139,72,24,                           //mov           0x18(%rax),%r9
-  102,72,15,58,22,209,1,                  //pextrq        $0x1,%xmm2,%rcx
-  68,15,183,193,                          //movzwl        %cx,%r8d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,72,15,126,208,                      //movq          %xmm2,%rax
-  68,15,183,208,                          //movzwl        %ax,%r10d
-  72,193,232,30,                          //shr           $0x1e,%rax
-  243,67,15,16,20,145,                    //movss         (%r9,%r10,4),%xmm2
-  102,65,15,58,33,20,1,16,                //insertps      $0x10,(%r9,%rax,1),%xmm2
-  243,67,15,16,28,129,                    //movss         (%r9,%r8,4),%xmm3
-  102,15,58,33,211,32,                    //insertps      $0x20,%xmm3,%xmm2
-  243,65,15,16,28,137,                    //movss         (%r9,%rcx,4),%xmm3
-  102,15,58,33,211,48,                    //insertps      $0x30,%xmm3,%xmm2
-  102,65,15,114,208,24,                   //psrld         $0x18,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_a8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,15,56,49,4,56,                      //pmovzxbd      (%rax,%rdi,1),%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_a8_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,69,15,56,43,192,                    //packusdw      %xmm8,%xmm8
-  102,69,15,103,192,                      //packuswb      %xmm8,%xmm8
-  102,68,15,126,4,56,                     //movd          %xmm8,(%rax,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_565_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,15,56,51,20,120,                    //pmovzxwd      (%rax,%rdi,2),%xmm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,219,194,                         //pand          %xmm2,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  102,15,219,202,                         //pand          %xmm2,%xmm1
-  15,91,217,                              //cvtdq2ps      %xmm1,%xmm3
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,15,219,218,                         //pand          %xmm2,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_565_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,248,65,                         //mov           $0x41f80000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,11,                   //pslld         $0xb,%xmm9
-  185,0,0,124,66,                         //mov           $0x427c0000,%ecx
-  102,68,15,110,209,                      //movd          %ecx,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,5,                    //pslld         $0x5,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,69,15,86,194,                       //orpd          %xmm10,%xmm8
-  102,69,15,56,43,192,                    //packusdw      %xmm8,%xmm8
-  102,68,15,214,4,120,                    //movq          %xmm8,(%rax,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_8888_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,28,184,                      //movdqu        (%rax,%rdi,4),%xmm3
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,15,114,209,8,                       //psrld         $0x8,%xmm1
-  102,15,219,200,                         //pand          %xmm0,%xmm1
-  102,15,111,211,                         //movdqa        %xmm3,%xmm2
-  102,15,114,210,16,                      //psrld         $0x10,%xmm2
-  102,15,219,208,                         //pand          %xmm0,%xmm2
-  102,15,219,195,                         //pand          %xmm3,%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,91,210,                              //cvtdq2ps      %xmm2,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  102,15,114,211,24,                      //psrld         $0x18,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_8888_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  69,15,40,208,                           //movaps        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,8,                    //pslld         $0x8,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,202,                           //mulps         %xmm2,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,16,                   //pslld         $0x10,%xmm9
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,65,15,114,240,24,                   //pslld         $0x18,%xmm8
-  102,69,15,235,193,                      //por           %xmm9,%xmm8
-  102,69,15,235,194,                      //por           %xmm10,%xmm8
-  243,68,15,127,4,184,                    //movdqu        %xmm8,(%rax,%rdi,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_f16_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,4,248,                       //movdqu        (%rax,%rdi,8),%xmm0
-  243,15,111,76,248,16,                   //movdqu        0x10(%rax,%rdi,8),%xmm1
-  102,15,111,208,                         //movdqa        %xmm0,%xmm2
-  102,15,97,209,                          //punpcklwd     %xmm1,%xmm2
-  102,15,105,193,                         //punpckhwd     %xmm1,%xmm0
-  102,68,15,111,194,                      //movdqa        %xmm2,%xmm8
-  102,68,15,97,192,                       //punpcklwd     %xmm0,%xmm8
-  102,15,105,208,                         //punpckhwd     %xmm0,%xmm2
-  184,0,4,0,4,                            //mov           $0x4000400,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,216,0,                       //pshufd        $0x0,%xmm0,%xmm3
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,65,15,101,200,                      //pcmpgtw       %xmm8,%xmm1
-  102,65,15,223,200,                      //pandn         %xmm8,%xmm1
-  102,15,101,218,                         //pcmpgtw       %xmm2,%xmm3
-  102,15,223,218,                         //pandn         %xmm2,%xmm3
-  102,15,56,51,193,                       //pmovzxwd      %xmm1,%xmm0
-  102,15,114,240,13,                      //pslld         $0xd,%xmm0
-  184,0,0,128,119,                        //mov           $0x77800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  102,68,15,112,194,0,                    //pshufd        $0x0,%xmm2,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  102,69,15,239,201,                      //pxor          %xmm9,%xmm9
-  102,65,15,105,201,                      //punpckhwd     %xmm9,%xmm1
-  102,15,114,241,13,                      //pslld         $0xd,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  102,15,56,51,211,                       //pmovzxwd      %xmm3,%xmm2
-  102,15,114,242,13,                      //pslld         $0xd,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  102,65,15,105,217,                      //punpckhwd     %xmm9,%xmm3
-  102,15,114,243,13,                      //pslld         $0xd,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f16_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,128,7,                          //mov           $0x7800000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  102,69,15,112,192,0,                    //pshufd        $0x0,%xmm8,%xmm8
-  102,69,15,111,200,                      //movdqa        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,65,15,114,209,13,                   //psrld         $0xd,%xmm9
-  102,69,15,111,208,                      //movdqa        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,65,15,114,210,13,                   //psrld         $0xd,%xmm10
-  102,69,15,111,216,                      //movdqa        %xmm8,%xmm11
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  102,65,15,114,211,13,                   //psrld         $0xd,%xmm11
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,65,15,114,208,13,                   //psrld         $0xd,%xmm8
-  102,65,15,115,250,2,                    //pslldq        $0x2,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  102,65,15,115,248,2,                    //pslldq        $0x2,%xmm8
-  102,69,15,235,195,                      //por           %xmm11,%xmm8
-  102,69,15,111,202,                      //movdqa        %xmm10,%xmm9
-  102,69,15,98,200,                       //punpckldq     %xmm8,%xmm9
-  243,68,15,127,12,248,                   //movdqu        %xmm9,(%rax,%rdi,8)
-  102,69,15,106,208,                      //punpckhdq     %xmm8,%xmm10
-  243,68,15,127,84,248,16,                //movdqu        %xmm10,0x10(%rax,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f32_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,137,249,                             //mov           %rdi,%rcx
-  72,193,225,4,                           //shl           $0x4,%rcx
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  68,15,20,201,                           //unpcklps      %xmm1,%xmm9
-  68,15,40,210,                           //movaps        %xmm2,%xmm10
-  68,15,40,218,                           //movaps        %xmm2,%xmm11
-  68,15,20,219,                           //unpcklps      %xmm3,%xmm11
-  68,15,21,193,                           //unpckhps      %xmm1,%xmm8
-  68,15,21,211,                           //unpckhps      %xmm3,%xmm10
-  69,15,40,225,                           //movaps        %xmm9,%xmm12
-  102,69,15,20,227,                       //unpcklpd      %xmm11,%xmm12
-  69,15,18,217,                           //movhlps       %xmm9,%xmm11
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  102,69,15,20,202,                       //unpcklpd      %xmm10,%xmm9
-  69,15,18,208,                           //movhlps       %xmm8,%xmm10
-  102,68,15,17,36,8,                      //movupd        %xmm12,(%rax,%rcx,1)
-  68,15,17,92,8,16,                       //movups        %xmm11,0x10(%rax,%rcx,1)
-  102,68,15,17,76,8,32,                   //movupd        %xmm9,0x20(%rax,%rcx,1)
-  68,15,17,84,8,48,                       //movups        %xmm10,0x30(%rax,%rcx,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_x_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,192,                           //maxps         %xmm0,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,192,                         //pcmpeqd       %xmm0,%xmm0
-  102,65,15,254,193,                      //paddd         %xmm9,%xmm0
-  68,15,93,192,                           //minps         %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,193,                           //maxps         %xmm1,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,201,                         //pcmpeqd       %xmm1,%xmm1
-  102,65,15,254,201,                      //paddd         %xmm9,%xmm1
-  68,15,93,193,                           //minps         %xmm1,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,200,                           //movaps        %xmm8,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  102,69,15,58,8,201,1,                   //roundps       $0x1,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,92,193,                           //subps         %xmm9,%xmm0
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,193,                           //minps         %xmm9,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  102,69,15,58,8,201,1,                   //roundps       $0x1,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,92,201,                           //subps         %xmm9,%xmm1
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,201,                           //minps         %xmm9,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  65,15,92,193,                           //subps         %xmm9,%xmm0
-  243,69,15,88,192,                       //addss         %xmm8,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,208,                           //movaps        %xmm0,%xmm10
-  69,15,94,208,                           //divps         %xmm8,%xmm10
-  102,69,15,58,8,210,1,                   //roundps       $0x1,%xmm10,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,194,                           //subps         %xmm10,%xmm0
-  65,15,92,193,                           //subps         %xmm9,%xmm0
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,92,192,                           //subps         %xmm0,%xmm8
-  65,15,84,192,                           //andps         %xmm8,%xmm0
-  102,69,15,118,192,                      //pcmpeqd       %xmm8,%xmm8
-  102,69,15,254,193,                      //paddd         %xmm9,%xmm8
-  65,15,93,192,                           //minps         %xmm8,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  65,15,92,201,                           //subps         %xmm9,%xmm1
-  243,69,15,88,192,                       //addss         %xmm8,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  69,15,94,208,                           //divps         %xmm8,%xmm10
-  102,69,15,58,8,210,1,                   //roundps       $0x1,%xmm10,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,202,                           //subps         %xmm10,%xmm1
-  65,15,92,201,                           //subps         %xmm9,%xmm1
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,92,193,                           //subps         %xmm1,%xmm8
-  65,15,84,200,                           //andps         %xmm8,%xmm1
-  102,69,15,118,192,                      //pcmpeqd       %xmm8,%xmm8
-  102,69,15,254,193,                      //paddd         %xmm9,%xmm8
-  65,15,93,200,                           //minps         %xmm8,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_sse41[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  15,88,195,                              //addps         %xmm3,%xmm0
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,218,                              //mulps         %xmm2,%xmm3
-  15,88,216,                              //addps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_sse41[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,16,                     //movss         0x10(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_sse41[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,36,                     //movss         0x24(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_sse41[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,32,                     //movss         0x20(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,48,                     //movss         0x30(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,64,                    //movss         0x40(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,20,                     //movss         0x14(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,36,                     //movss         0x24(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,52,                     //movss         0x34(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,68,                    //movss         0x44(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,56,                    //movss         0x38(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,72,                    //movss         0x48(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  68,15,89,235,                           //mulps         %xmm3,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  243,68,15,16,88,12,                     //movss         0xc(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,28,                     //movss         0x1c(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,60,                    //movss         0x3c(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  243,68,15,16,120,76,                    //movss         0x4c(%rax),%xmm15
-  69,15,198,255,0,                        //shufps        $0x0,%xmm15,%xmm15
-  68,15,89,243,                           //mulps         %xmm3,%xmm14
-  69,15,88,247,                           //addps         %xmm15,%xmm14
-  68,15,89,234,                           //mulps         %xmm2,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  69,15,89,225,                           //mulps         %xmm9,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,216,                           //mulps         %xmm8,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  65,15,40,219,                           //movaps        %xmm11,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_sse41[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,68,15,16,72,4,                      //movss         0x4(%rax),%xmm9
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  243,68,15,16,72,12,                     //movss         0xc(%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  243,68,15,16,80,24,                     //movss         0x18(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,217,                           //mulps         %xmm1,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,83,202,                           //rcpps         %xmm10,%xmm1
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_sse41[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  68,15,16,8,                             //movups        (%rax),%xmm9
-  15,16,88,16,                            //movups        0x10(%rax),%xmm3
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  68,15,89,192,                           //mulps         %xmm0,%xmm8
-  68,15,88,193,                           //addps         %xmm1,%xmm8
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  65,15,40,209,                           //movaps        %xmm9,%xmm2
-  15,198,210,85,                          //shufps        $0x55,%xmm2,%xmm2
-  15,89,200,                              //mulps         %xmm0,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  69,15,40,209,                           //movaps        %xmm9,%xmm10
-  69,15,198,210,170,                      //shufps        $0xaa,%xmm10,%xmm10
-  15,89,208,                              //mulps         %xmm0,%xmm2
-  65,15,88,210,                           //addps         %xmm10,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  69,15,198,201,255,                      //shufps        $0xff,%xmm9,%xmm9
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  65,15,88,217,                           //addps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_start_pipeline_sse2[] = {
-  65,87,                                  //push          %r15
-  65,86,                                  //push          %r14
-  65,85,                                  //push          %r13
-  65,84,                                  //push          %r12
-  86,                                     //push          %rsi
-  87,                                     //push          %rdi
-  83,                                     //push          %rbx
-  72,129,236,160,0,0,0,                   //sub           $0xa0,%rsp
-  68,15,41,188,36,144,0,0,0,              //movaps        %xmm15,0x90(%rsp)
-  68,15,41,180,36,128,0,0,0,              //movaps        %xmm14,0x80(%rsp)
-  68,15,41,108,36,112,                    //movaps        %xmm13,0x70(%rsp)
-  68,15,41,100,36,96,                     //movaps        %xmm12,0x60(%rsp)
-  68,15,41,92,36,80,                      //movaps        %xmm11,0x50(%rsp)
-  68,15,41,84,36,64,                      //movaps        %xmm10,0x40(%rsp)
-  68,15,41,76,36,48,                      //movaps        %xmm9,0x30(%rsp)
-  68,15,41,68,36,32,                      //movaps        %xmm8,0x20(%rsp)
-  15,41,124,36,16,                        //movaps        %xmm7,0x10(%rsp)
-  15,41,52,36,                            //movaps        %xmm6,(%rsp)
-  77,137,207,                             //mov           %r9,%r15
-  77,137,198,                             //mov           %r8,%r14
-  72,137,203,                             //mov           %rcx,%rbx
-  72,137,214,                             //mov           %rdx,%rsi
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  73,137,196,                             //mov           %rax,%r12
-  73,137,245,                             //mov           %rsi,%r13
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  76,57,248,                              //cmp           %r15,%rax
-  118,5,                                  //jbe           73 <_sk_start_pipeline_sse2+0x73>
-  72,137,216,                             //mov           %rbx,%rax
-  235,52,                                 //jmp           a7 <_sk_start_pipeline_sse2+0xa7>
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  72,137,223,                             //mov           %rbx,%rdi
-  76,137,238,                             //mov           %r13,%rsi
-  76,137,242,                             //mov           %r14,%rdx
-  65,255,212,                             //callq         *%r12
-  72,141,67,4,                            //lea           0x4(%rbx),%rax
-  72,131,195,8,                           //add           $0x8,%rbx
-  76,57,251,                              //cmp           %r15,%rbx
-  72,137,195,                             //mov           %rax,%rbx
-  118,204,                                //jbe           73 <_sk_start_pipeline_sse2+0x73>
-  15,40,52,36,                            //movaps        (%rsp),%xmm6
-  15,40,124,36,16,                        //movaps        0x10(%rsp),%xmm7
-  68,15,40,68,36,32,                      //movaps        0x20(%rsp),%xmm8
-  68,15,40,76,36,48,                      //movaps        0x30(%rsp),%xmm9
-  68,15,40,84,36,64,                      //movaps        0x40(%rsp),%xmm10
-  68,15,40,92,36,80,                      //movaps        0x50(%rsp),%xmm11
-  68,15,40,100,36,96,                     //movaps        0x60(%rsp),%xmm12
-  68,15,40,108,36,112,                    //movaps        0x70(%rsp),%xmm13
-  68,15,40,180,36,128,0,0,0,              //movaps        0x80(%rsp),%xmm14
-  68,15,40,188,36,144,0,0,0,              //movaps        0x90(%rsp),%xmm15
-  72,129,196,160,0,0,0,                   //add           $0xa0,%rsp
-  91,                                     //pop           %rbx
-  95,                                     //pop           %rdi
-  94,                                     //pop           %rsi
-  65,92,                                  //pop           %r12
-  65,93,                                  //pop           %r13
-  65,94,                                  //pop           %r14
-  65,95,                                  //pop           %r15
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_just_return_sse2[] = {
-  195,                                    //retq
-};
-
-CODE const uint8_t sk_seed_shader_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  102,15,110,199,                         //movd          %edi,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  185,0,0,0,63,                           //mov           $0x3f000000,%ecx
-  102,15,110,209,                         //movd          %ecx,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,16,2,                                //movups        (%rdx),%xmm0
-  15,88,193,                              //addps         %xmm1,%xmm0
-  102,15,110,8,                           //movd          (%rax),%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  15,87,228,                              //xorps         %xmm4,%xmm4
-  15,87,237,                              //xorps         %xmm5,%xmm5
-  15,87,246,                              //xorps         %xmm6,%xmm6
-  15,87,255,                              //xorps         %xmm7,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_constant_color_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,16,24,                               //movups        (%rax),%xmm3
-  15,40,195,                              //movaps        %xmm3,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clear_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  15,87,219,                              //xorps         %xmm3,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_plus__sse2[] = {
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_srcover_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,195,                           //subps         %xmm3,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,204,                           //mulps         %xmm4,%xmm9
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,205,                           //mulps         %xmm5,%xmm9
-  65,15,88,201,                           //addps         %xmm9,%xmm1
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,206,                           //mulps         %xmm6,%xmm9
-  65,15,88,209,                           //addps         %xmm9,%xmm2
-  68,15,89,199,                           //mulps         %xmm7,%xmm8
-  65,15,88,216,                           //addps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_dstover_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,92,199,                           //subps         %xmm7,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_0_sse2[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  65,15,95,192,                           //maxps         %xmm8,%xmm0
-  65,15,95,200,                           //maxps         %xmm8,%xmm1
-  65,15,95,208,                           //maxps         %xmm8,%xmm2
-  65,15,95,216,                           //maxps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_1_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,192,                           //minps         %xmm8,%xmm0
-  65,15,93,200,                           //minps         %xmm8,%xmm1
-  65,15,93,208,                           //minps         %xmm8,%xmm2
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_a_sse2[] = {
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,93,216,                           //minps         %xmm8,%xmm3
-  15,93,195,                              //minps         %xmm3,%xmm0
-  15,93,203,                              //minps         %xmm3,%xmm1
-  15,93,211,                              //minps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_set_rgb_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,15,16,80,8,                         //movss         0x8(%rax),%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_rb_sse2[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,194,                              //movaps        %xmm2,%xmm0
-  65,15,40,208,                           //movaps        %xmm8,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_swap_sse2[] = {
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  68,15,40,202,                           //movaps        %xmm2,%xmm9
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  68,15,40,216,                           //movaps        %xmm0,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  65,15,40,227,                           //movaps        %xmm11,%xmm4
-  65,15,40,234,                           //movaps        %xmm10,%xmm5
-  65,15,40,241,                           //movaps        %xmm9,%xmm6
-  65,15,40,248,                           //movaps        %xmm8,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_src_dst_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,224,                              //movaps        %xmm0,%xmm4
-  15,40,233,                              //movaps        %xmm1,%xmm5
-  15,40,242,                              //movaps        %xmm2,%xmm6
-  15,40,251,                              //movaps        %xmm3,%xmm7
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_move_dst_src_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,40,196,                              //movaps        %xmm4,%xmm0
-  15,40,205,                              //movaps        %xmm5,%xmm1
-  15,40,214,                              //movaps        %xmm6,%xmm2
-  15,40,223,                              //movaps        %xmm7,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_premul_sse2[] = {
-  15,89,195,                              //mulps         %xmm3,%xmm0
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_unpremul_sse2[] = {
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,94,203,                           //divps         %xmm3,%xmm9
-  68,15,194,195,4,                        //cmpneqps      %xmm3,%xmm8
-  69,15,84,193,                           //andps         %xmm9,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_from_srgb_sse2[] = {
-  184,145,131,158,61,                     //mov           $0x3d9e8391,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,232,                           //movaps        %xmm8,%xmm13
-  68,15,89,232,                           //mulps         %xmm0,%xmm13
-  68,15,40,224,                           //movaps        %xmm0,%xmm12
-  69,15,89,228,                           //mulps         %xmm12,%xmm12
-  184,154,153,153,62,                     //mov           $0x3e99999a,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  184,92,143,50,63,                       //mov           $0x3f328f5c,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  69,15,40,241,                           //movaps        %xmm9,%xmm14
-  68,15,89,240,                           //mulps         %xmm0,%xmm14
-  69,15,88,242,                           //addps         %xmm10,%xmm14
-  184,10,215,35,59,                       //mov           $0x3b23d70a,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,244,                           //mulps         %xmm12,%xmm14
-  69,15,88,243,                           //addps         %xmm11,%xmm14
-  184,174,71,97,61,                       //mov           $0x3d6147ae,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  65,15,194,196,1,                        //cmpltps       %xmm12,%xmm0
-  68,15,84,232,                           //andps         %xmm0,%xmm13
-  65,15,85,198,                           //andnps        %xmm14,%xmm0
-  65,15,86,197,                           //orps          %xmm13,%xmm0
-  69,15,40,232,                           //movaps        %xmm8,%xmm13
-  68,15,89,233,                           //mulps         %xmm1,%xmm13
-  68,15,40,241,                           //movaps        %xmm1,%xmm14
-  69,15,89,246,                           //mulps         %xmm14,%xmm14
-  69,15,40,249,                           //movaps        %xmm9,%xmm15
-  68,15,89,249,                           //mulps         %xmm1,%xmm15
-  69,15,88,250,                           //addps         %xmm10,%xmm15
-  69,15,89,254,                           //mulps         %xmm14,%xmm15
-  69,15,88,251,                           //addps         %xmm11,%xmm15
-  65,15,194,204,1,                        //cmpltps       %xmm12,%xmm1
-  68,15,84,233,                           //andps         %xmm1,%xmm13
-  65,15,85,207,                           //andnps        %xmm15,%xmm1
-  65,15,86,205,                           //orps          %xmm13,%xmm1
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  68,15,40,234,                           //movaps        %xmm2,%xmm13
-  69,15,89,237,                           //mulps         %xmm13,%xmm13
-  68,15,89,202,                           //mulps         %xmm2,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  69,15,89,205,                           //mulps         %xmm13,%xmm9
-  69,15,88,203,                           //addps         %xmm11,%xmm9
-  65,15,194,212,1,                        //cmpltps       %xmm12,%xmm2
-  68,15,84,194,                           //andps         %xmm2,%xmm8
-  65,15,85,209,                           //andnps        %xmm9,%xmm2
-  65,15,86,208,                           //orps          %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_to_srgb_sse2[] = {
-  68,15,82,192,                           //rsqrtps       %xmm0,%xmm8
-  69,15,83,248,                           //rcpps         %xmm8,%xmm15
-  69,15,82,232,                           //rsqrtps       %xmm8,%xmm13
-  184,41,92,71,65,                        //mov           $0x41475c29,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,240,                           //movaps        %xmm8,%xmm14
-  68,15,89,240,                           //mulps         %xmm0,%xmm14
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  184,194,135,210,62,                     //mov           $0x3ed287c2,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  184,206,111,48,63,                      //mov           $0x3f306fce,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  184,168,87,202,61,                      //mov           $0x3dca57a8,%eax
-  53,0,0,0,128,                           //xor           $0x80000000,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,89,251,                           //mulps         %xmm11,%xmm15
-  69,15,88,252,                           //addps         %xmm12,%xmm15
-  69,15,89,234,                           //mulps         %xmm10,%xmm13
-  69,15,88,239,                           //addps         %xmm15,%xmm13
-  69,15,40,249,                           //movaps        %xmm9,%xmm15
-  69,15,93,253,                           //minps         %xmm13,%xmm15
-  184,4,231,140,59,                       //mov           $0x3b8ce704,%eax
-  102,68,15,110,232,                      //movd          %eax,%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  65,15,194,197,1,                        //cmpltps       %xmm13,%xmm0
-  68,15,84,240,                           //andps         %xmm0,%xmm14
-  65,15,85,199,                           //andnps        %xmm15,%xmm0
-  65,15,86,198,                           //orps          %xmm14,%xmm0
-  68,15,82,241,                           //rsqrtps       %xmm1,%xmm14
-  69,15,83,254,                           //rcpps         %xmm14,%xmm15
-  69,15,82,246,                           //rsqrtps       %xmm14,%xmm14
-  69,15,89,251,                           //mulps         %xmm11,%xmm15
-  69,15,88,252,                           //addps         %xmm12,%xmm15
-  69,15,89,242,                           //mulps         %xmm10,%xmm14
-  69,15,88,247,                           //addps         %xmm15,%xmm14
-  69,15,40,249,                           //movaps        %xmm9,%xmm15
-  69,15,93,254,                           //minps         %xmm14,%xmm15
-  69,15,40,240,                           //movaps        %xmm8,%xmm14
-  68,15,89,241,                           //mulps         %xmm1,%xmm14
-  65,15,194,205,1,                        //cmpltps       %xmm13,%xmm1
-  68,15,84,241,                           //andps         %xmm1,%xmm14
-  65,15,85,207,                           //andnps        %xmm15,%xmm1
-  65,15,86,206,                           //orps          %xmm14,%xmm1
-  68,15,82,242,                           //rsqrtps       %xmm2,%xmm14
-  69,15,83,254,                           //rcpps         %xmm14,%xmm15
-  69,15,89,251,                           //mulps         %xmm11,%xmm15
-  69,15,88,252,                           //addps         %xmm12,%xmm15
-  69,15,82,222,                           //rsqrtps       %xmm14,%xmm11
-  69,15,89,218,                           //mulps         %xmm10,%xmm11
-  69,15,88,223,                           //addps         %xmm15,%xmm11
-  69,15,93,203,                           //minps         %xmm11,%xmm9
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  65,15,194,213,1,                        //cmpltps       %xmm13,%xmm2
-  68,15,84,194,                           //andps         %xmm2,%xmm8
-  65,15,85,209,                           //andnps        %xmm9,%xmm2
-  65,15,86,208,                           //orps          %xmm8,%xmm2
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_1_float_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_scale_u8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,110,4,56,                     //movd          (%rax,%rdi,1),%xmm8
-  102,69,15,239,201,                      //pxor          %xmm9,%xmm9
-  102,69,15,96,193,                       //punpcklbw     %xmm9,%xmm8
-  102,69,15,97,193,                       //punpcklwd     %xmm9,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_1_float_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_u8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,68,15,110,4,56,                     //movd          (%rax,%rdi,1),%xmm8
-  102,69,15,239,201,                      //pxor          %xmm9,%xmm9
-  102,69,15,96,193,                       //punpcklbw     %xmm9,%xmm8
-  102,69,15,97,193,                       //punpcklwd     %xmm9,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,200,                      //movd          %eax,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  15,92,223,                              //subps         %xmm7,%xmm3
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  15,88,223,                              //addps         %xmm7,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_lerp_565_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,68,15,126,4,120,                    //movq          (%rax,%rdi,2),%xmm8
-  102,15,239,219,                         //pxor          %xmm3,%xmm3
-  102,68,15,97,195,                       //punpcklwd     %xmm3,%xmm8
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,68,15,110,208,                      //movd          %eax,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,203,                           //cvtdq2ps      %xmm3,%xmm9
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,65,15,219,216,                      //pand          %xmm8,%xmm3
-  68,15,91,195,                           //cvtdq2ps      %xmm3,%xmm8
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  15,92,196,                              //subps         %xmm4,%xmm0
-  65,15,89,194,                           //mulps         %xmm10,%xmm0
-  15,88,196,                              //addps         %xmm4,%xmm0
-  15,92,205,                              //subps         %xmm5,%xmm1
-  65,15,89,203,                           //mulps         %xmm11,%xmm1
-  15,88,205,                              //addps         %xmm5,%xmm1
-  15,92,214,                              //subps         %xmm6,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  15,88,214,                              //addps         %xmm6,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_tables_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,8,                               //mov           (%rax),%rcx
-  76,139,64,8,                            //mov           0x8(%rax),%r8
-  243,68,15,111,4,185,                    //movdqu        (%rcx,%rdi,4),%xmm8
-  185,255,0,0,0,                          //mov           $0xff,%ecx
-  102,15,110,193,                         //movd          %ecx,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,69,15,111,200,                      //movdqa        %xmm8,%xmm9
-  102,65,15,114,209,8,                    //psrld         $0x8,%xmm9
-  102,68,15,219,200,                      //pand          %xmm0,%xmm9
-  102,69,15,111,208,                      //movdqa        %xmm8,%xmm10
-  102,65,15,114,210,16,                   //psrld         $0x10,%xmm10
-  102,68,15,219,208,                      //pand          %xmm0,%xmm10
-  102,65,15,219,192,                      //pand          %xmm8,%xmm0
-  102,15,112,216,78,                      //pshufd        $0x4e,%xmm0,%xmm3
-  102,72,15,126,217,                      //movq          %xmm3,%rcx
-  65,137,201,                             //mov           %ecx,%r9d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,73,15,126,194,                      //movq          %xmm0,%r10
-  69,137,211,                             //mov           %r10d,%r11d
-  73,193,234,32,                          //shr           $0x20,%r10
-  243,67,15,16,28,144,                    //movss         (%r8,%r10,4),%xmm3
-  243,65,15,16,4,136,                     //movss         (%r8,%rcx,4),%xmm0
-  15,20,216,                              //unpcklps      %xmm0,%xmm3
-  243,67,15,16,4,152,                     //movss         (%r8,%r11,4),%xmm0
-  243,67,15,16,12,136,                    //movss         (%r8,%r9,4),%xmm1
-  15,20,193,                              //unpcklps      %xmm1,%xmm0
-  15,20,195,                              //unpcklps      %xmm3,%xmm0
-  76,139,64,16,                           //mov           0x10(%rax),%r8
-  102,65,15,112,201,78,                   //pshufd        $0x4e,%xmm9,%xmm1
-  102,73,15,126,202,                      //movq          %xmm1,%r10
-  77,137,209,                             //mov           %r10,%r9
-  73,193,233,32,                          //shr           $0x20,%r9
-  102,76,15,126,201,                      //movq          %xmm9,%rcx
-  65,137,203,                             //mov           %ecx,%r11d
-  65,129,227,255,255,255,0,               //and           $0xffffff,%r11d
-  72,193,233,30,                          //shr           $0x1e,%rcx
-  65,129,226,255,255,255,0,               //and           $0xffffff,%r10d
-  243,65,15,16,28,8,                      //movss         (%r8,%rcx,1),%xmm3
-  243,67,15,16,12,136,                    //movss         (%r8,%r9,4),%xmm1
-  15,20,217,                              //unpcklps      %xmm1,%xmm3
-  243,67,15,16,12,152,                    //movss         (%r8,%r11,4),%xmm1
-  243,67,15,16,20,144,                    //movss         (%r8,%r10,4),%xmm2
-  15,20,202,                              //unpcklps      %xmm2,%xmm1
-  15,20,203,                              //unpcklps      %xmm3,%xmm1
-  76,139,72,24,                           //mov           0x18(%rax),%r9
-  102,65,15,112,210,78,                   //pshufd        $0x4e,%xmm10,%xmm2
-  102,72,15,126,209,                      //movq          %xmm2,%rcx
-  68,15,183,193,                          //movzwl        %cx,%r8d
-  72,193,233,32,                          //shr           $0x20,%rcx
-  102,76,15,126,208,                      //movq          %xmm10,%rax
-  68,15,183,208,                          //movzwl        %ax,%r10d
-  72,193,232,30,                          //shr           $0x1e,%rax
-  243,69,15,16,12,1,                      //movss         (%r9,%rax,1),%xmm9
-  243,65,15,16,20,137,                    //movss         (%r9,%rcx,4),%xmm2
-  68,15,20,202,                           //unpcklps      %xmm2,%xmm9
-  243,67,15,16,20,145,                    //movss         (%r9,%r10,4),%xmm2
-  243,67,15,16,28,129,                    //movss         (%r9,%r8,4),%xmm3
-  15,20,211,                              //unpcklps      %xmm3,%xmm2
-  65,15,20,209,                           //unpcklps      %xmm9,%xmm2
-  102,65,15,114,208,24,                   //psrld         $0x18,%xmm8
-  69,15,91,192,                           //cvtdq2ps      %xmm8,%xmm8
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_a8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  102,15,110,4,56,                        //movd          (%rax,%rdi,1),%xmm0
-  102,15,239,201,                         //pxor          %xmm1,%xmm1
-  102,15,96,193,                          //punpcklbw     %xmm1,%xmm0
-  102,15,97,193,                          //punpcklwd     %xmm1,%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  102,15,239,201,                         //pxor          %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_a8_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,65,15,114,240,16,                   //pslld         $0x10,%xmm8
-  102,65,15,114,224,16,                   //psrad         $0x10,%xmm8
-  102,69,15,107,192,                      //packssdw      %xmm8,%xmm8
-  102,69,15,103,192,                      //packuswb      %xmm8,%xmm8
-  102,68,15,126,4,56,                     //movd          %xmm8,(%rax,%rdi,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_565_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,126,20,120,                      //movq          (%rax,%rdi,2),%xmm2
-  102,15,239,192,                         //pxor          %xmm0,%xmm0
-  102,15,97,208,                          //punpcklwd     %xmm0,%xmm2
-  184,0,248,0,0,                          //mov           $0xf800,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,219,194,                         //pand          %xmm2,%xmm0
-  15,91,200,                              //cvtdq2ps      %xmm0,%xmm1
-  184,8,33,132,55,                        //mov           $0x37842108,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  184,224,7,0,0,                          //mov           $0x7e0,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  102,15,112,201,0,                       //pshufd        $0x0,%xmm1,%xmm1
-  102,15,219,202,                         //pand          %xmm2,%xmm1
-  15,91,217,                              //cvtdq2ps      %xmm1,%xmm3
-  184,33,8,2,58,                          //mov           $0x3a020821,%eax
-  102,15,110,200,                         //movd          %eax,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  15,89,203,                              //mulps         %xmm3,%xmm1
-  184,31,0,0,0,                           //mov           $0x1f,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  102,15,112,219,0,                       //pshufd        $0x0,%xmm3,%xmm3
-  102,15,219,218,                         //pand          %xmm2,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  184,8,33,4,61,                          //mov           $0x3d042108,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  15,198,210,0,                           //shufps        $0x0,%xmm2,%xmm2
-  15,89,211,                              //mulps         %xmm3,%xmm2
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_565_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,248,65,                         //mov           $0x41f80000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,11,                   //pslld         $0xb,%xmm9
-  185,0,0,124,66,                         //mov           $0x427c0000,%ecx
-  102,68,15,110,209,                      //movd          %ecx,%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,5,                    //pslld         $0x5,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  68,15,89,194,                           //mulps         %xmm2,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,69,15,86,194,                       //orpd          %xmm10,%xmm8
-  102,65,15,114,240,16,                   //pslld         $0x10,%xmm8
-  102,65,15,114,224,16,                   //psrad         $0x10,%xmm8
-  102,69,15,107,192,                      //packssdw      %xmm8,%xmm8
-  102,68,15,214,4,120,                    //movq          %xmm8,(%rax,%rdi,2)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_8888_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,28,184,                      //movdqu        (%rax,%rdi,4),%xmm3
-  184,255,0,0,0,                          //mov           $0xff,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,192,0,                       //pshufd        $0x0,%xmm0,%xmm0
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,15,114,209,8,                       //psrld         $0x8,%xmm1
-  102,15,219,200,                         //pand          %xmm0,%xmm1
-  102,15,111,211,                         //movdqa        %xmm3,%xmm2
-  102,15,114,210,16,                      //psrld         $0x10,%xmm2
-  102,15,219,208,                         //pand          %xmm0,%xmm2
-  102,15,219,195,                         //pand          %xmm3,%xmm0
-  15,91,192,                              //cvtdq2ps      %xmm0,%xmm0
-  184,129,128,128,59,                     //mov           $0x3b808081,%eax
-  102,68,15,110,192,                      //movd          %eax,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  15,91,201,                              //cvtdq2ps      %xmm1,%xmm1
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  15,91,210,                              //cvtdq2ps      %xmm2,%xmm2
-  65,15,89,208,                           //mulps         %xmm8,%xmm2
-  102,15,114,211,24,                      //psrld         $0x18,%xmm3
-  15,91,219,                              //cvtdq2ps      %xmm3,%xmm3
-  65,15,89,216,                           //mulps         %xmm8,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_8888_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,127,67,                         //mov           $0x437f0000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  69,15,40,208,                           //movaps        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,69,15,91,210,                       //cvtps2dq      %xmm10,%xmm10
-  102,65,15,114,242,8,                    //pslld         $0x8,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  68,15,89,202,                           //mulps         %xmm2,%xmm9
-  102,69,15,91,201,                       //cvtps2dq      %xmm9,%xmm9
-  102,65,15,114,241,16,                   //pslld         $0x10,%xmm9
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,69,15,91,192,                       //cvtps2dq      %xmm8,%xmm8
-  102,65,15,114,240,24,                   //pslld         $0x18,%xmm8
-  102,69,15,235,193,                      //por           %xmm9,%xmm8
-  102,69,15,235,194,                      //por           %xmm10,%xmm8
-  243,68,15,127,4,184,                    //movdqu        %xmm8,(%rax,%rdi,4)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_load_f16_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  243,15,111,4,248,                       //movdqu        (%rax,%rdi,8),%xmm0
-  243,15,111,76,248,16,                   //movdqu        0x10(%rax,%rdi,8),%xmm1
-  102,15,111,208,                         //movdqa        %xmm0,%xmm2
-  102,15,97,209,                          //punpcklwd     %xmm1,%xmm2
-  102,15,105,193,                         //punpckhwd     %xmm1,%xmm0
-  102,68,15,111,194,                      //movdqa        %xmm2,%xmm8
-  102,68,15,97,192,                       //punpcklwd     %xmm0,%xmm8
-  102,15,105,208,                         //punpckhwd     %xmm0,%xmm2
-  184,0,4,0,4,                            //mov           $0x4000400,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  102,15,112,216,0,                       //pshufd        $0x0,%xmm0,%xmm3
-  102,15,111,203,                         //movdqa        %xmm3,%xmm1
-  102,65,15,101,200,                      //pcmpgtw       %xmm8,%xmm1
-  102,65,15,223,200,                      //pandn         %xmm8,%xmm1
-  102,15,101,218,                         //pcmpgtw       %xmm2,%xmm3
-  102,15,223,218,                         //pandn         %xmm2,%xmm3
-  102,69,15,239,192,                      //pxor          %xmm8,%xmm8
-  102,15,111,193,                         //movdqa        %xmm1,%xmm0
-  102,65,15,97,192,                       //punpcklwd     %xmm8,%xmm0
-  102,15,114,240,13,                      //pslld         $0xd,%xmm0
-  184,0,0,128,119,                        //mov           $0x77800000,%eax
-  102,15,110,208,                         //movd          %eax,%xmm2
-  102,68,15,112,202,0,                    //pshufd        $0x0,%xmm2,%xmm9
-  65,15,89,193,                           //mulps         %xmm9,%xmm0
-  102,65,15,105,200,                      //punpckhwd     %xmm8,%xmm1
-  102,15,114,241,13,                      //pslld         $0xd,%xmm1
-  65,15,89,201,                           //mulps         %xmm9,%xmm1
-  102,15,111,211,                         //movdqa        %xmm3,%xmm2
-  102,65,15,97,208,                       //punpcklwd     %xmm8,%xmm2
-  102,15,114,242,13,                      //pslld         $0xd,%xmm2
-  65,15,89,209,                           //mulps         %xmm9,%xmm2
-  102,65,15,105,216,                      //punpckhwd     %xmm8,%xmm3
-  102,15,114,243,13,                      //pslld         $0xd,%xmm3
-  65,15,89,217,                           //mulps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f16_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  185,0,0,128,7,                          //mov           $0x7800000,%ecx
-  102,68,15,110,193,                      //movd          %ecx,%xmm8
-  102,69,15,112,192,0,                    //pshufd        $0x0,%xmm8,%xmm8
-  102,69,15,111,200,                      //movdqa        %xmm8,%xmm9
-  68,15,89,200,                           //mulps         %xmm0,%xmm9
-  102,65,15,114,209,13,                   //psrld         $0xd,%xmm9
-  102,69,15,111,208,                      //movdqa        %xmm8,%xmm10
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  102,65,15,114,210,13,                   //psrld         $0xd,%xmm10
-  102,69,15,111,216,                      //movdqa        %xmm8,%xmm11
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  102,65,15,114,211,13,                   //psrld         $0xd,%xmm11
-  68,15,89,195,                           //mulps         %xmm3,%xmm8
-  102,65,15,114,208,13,                   //psrld         $0xd,%xmm8
-  102,65,15,115,250,2,                    //pslldq        $0x2,%xmm10
-  102,69,15,235,209,                      //por           %xmm9,%xmm10
-  102,65,15,115,248,2,                    //pslldq        $0x2,%xmm8
-  102,69,15,235,195,                      //por           %xmm11,%xmm8
-  102,69,15,111,202,                      //movdqa        %xmm10,%xmm9
-  102,69,15,98,200,                       //punpckldq     %xmm8,%xmm9
-  243,68,15,127,12,248,                   //movdqu        %xmm9,(%rax,%rdi,8)
-  102,69,15,106,208,                      //punpckhdq     %xmm8,%xmm10
-  243,68,15,127,84,248,16,                //movdqu        %xmm10,0x10(%rax,%rdi,8)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_store_f32_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  72,139,0,                               //mov           (%rax),%rax
-  72,137,249,                             //mov           %rdi,%rcx
-  72,193,225,4,                           //shl           $0x4,%rcx
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  68,15,20,201,                           //unpcklps      %xmm1,%xmm9
-  68,15,40,210,                           //movaps        %xmm2,%xmm10
-  68,15,40,218,                           //movaps        %xmm2,%xmm11
-  68,15,20,219,                           //unpcklps      %xmm3,%xmm11
-  68,15,21,193,                           //unpckhps      %xmm1,%xmm8
-  68,15,21,211,                           //unpckhps      %xmm3,%xmm10
-  69,15,40,225,                           //movaps        %xmm9,%xmm12
-  102,69,15,20,227,                       //unpcklpd      %xmm11,%xmm12
-  69,15,18,217,                           //movhlps       %xmm9,%xmm11
-  69,15,40,200,                           //movaps        %xmm8,%xmm9
-  102,69,15,20,202,                       //unpcklpd      %xmm10,%xmm9
-  69,15,18,208,                           //movhlps       %xmm8,%xmm10
-  102,68,15,17,36,8,                      //movupd        %xmm12,(%rax,%rcx,1)
-  68,15,17,92,8,16,                       //movups        %xmm11,0x10(%rax,%rcx,1)
-  102,68,15,17,76,8,32,                   //movupd        %xmm9,0x20(%rax,%rcx,1)
-  68,15,17,84,8,48,                       //movups        %xmm10,0x30(%rax,%rcx,1)
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_x_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,192,                           //maxps         %xmm0,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,192,                         //pcmpeqd       %xmm0,%xmm0
-  102,65,15,254,193,                      //paddd         %xmm9,%xmm0
-  68,15,93,192,                           //minps         %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_clamp_y_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  69,15,87,192,                           //xorps         %xmm8,%xmm8
-  68,15,95,193,                           //maxps         %xmm1,%xmm8
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  102,15,118,201,                         //pcmpeqd       %xmm1,%xmm1
-  102,65,15,254,201,                      //paddd         %xmm9,%xmm1
-  68,15,93,193,                           //minps         %xmm1,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,200,                           //movaps        %xmm8,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_x_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,200,                           //movaps        %xmm0,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  243,69,15,91,209,                       //cvttps2dq     %xmm9,%xmm10
-  69,15,91,210,                           //cvtdq2ps      %xmm10,%xmm10
-  69,15,194,202,1,                        //cmpltps       %xmm10,%xmm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,84,217,                           //andps         %xmm9,%xmm11
-  69,15,92,211,                           //subps         %xmm11,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,194,                           //subps         %xmm10,%xmm0
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,193,                           //minps         %xmm9,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_repeat_y_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,0,                         //movss         (%rax),%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  69,15,94,200,                           //divps         %xmm8,%xmm9
-  243,69,15,91,209,                       //cvttps2dq     %xmm9,%xmm10
-  69,15,91,210,                           //cvtdq2ps      %xmm10,%xmm10
-  69,15,194,202,1,                        //cmpltps       %xmm10,%xmm9
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,216,                      //movd          %eax,%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,84,217,                           //andps         %xmm9,%xmm11
-  69,15,92,211,                           //subps         %xmm11,%xmm10
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  65,15,92,202,                           //subps         %xmm10,%xmm1
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,201,                           //minps         %xmm9,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_x_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,40,193,                           //movaps        %xmm9,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,92,192,                           //subps         %xmm8,%xmm0
-  243,69,15,88,201,                       //addss         %xmm9,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,40,208,                           //movaps        %xmm0,%xmm10
-  69,15,94,209,                           //divps         %xmm9,%xmm10
-  243,69,15,91,218,                       //cvttps2dq     %xmm10,%xmm11
-  69,15,91,219,                           //cvtdq2ps      %xmm11,%xmm11
-  69,15,194,211,1,                        //cmpltps       %xmm11,%xmm10
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,84,226,                           //andps         %xmm10,%xmm12
-  69,15,87,210,                           //xorps         %xmm10,%xmm10
-  69,15,92,220,                           //subps         %xmm12,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  65,15,92,195,                           //subps         %xmm11,%xmm0
-  65,15,92,192,                           //subps         %xmm8,%xmm0
-  68,15,92,208,                           //subps         %xmm0,%xmm10
-  65,15,84,194,                           //andps         %xmm10,%xmm0
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,193,                           //minps         %xmm9,%xmm0
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_mirror_y_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,68,15,16,8,                         //movss         (%rax),%xmm9
-  69,15,40,193,                           //movaps        %xmm9,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,92,200,                           //subps         %xmm8,%xmm1
-  243,69,15,88,201,                       //addss         %xmm9,%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  68,15,40,209,                           //movaps        %xmm1,%xmm10
-  69,15,94,209,                           //divps         %xmm9,%xmm10
-  243,69,15,91,218,                       //cvttps2dq     %xmm10,%xmm11
-  69,15,91,219,                           //cvtdq2ps      %xmm11,%xmm11
-  69,15,194,211,1,                        //cmpltps       %xmm11,%xmm10
-  184,0,0,128,63,                         //mov           $0x3f800000,%eax
-  102,68,15,110,224,                      //movd          %eax,%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  69,15,84,226,                           //andps         %xmm10,%xmm12
-  69,15,87,210,                           //xorps         %xmm10,%xmm10
-  69,15,92,220,                           //subps         %xmm12,%xmm11
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  65,15,92,203,                           //subps         %xmm11,%xmm1
-  65,15,92,200,                           //subps         %xmm8,%xmm1
-  68,15,92,209,                           //subps         %xmm1,%xmm10
-  65,15,84,202,                           //andps         %xmm10,%xmm1
-  102,69,15,118,201,                      //pcmpeqd       %xmm9,%xmm9
-  102,69,15,254,200,                      //paddd         %xmm8,%xmm9
-  65,15,93,201,                           //minps         %xmm9,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_luminance_to_alpha_sse2[] = {
-  184,208,179,89,62,                      //mov           $0x3e59b3d0,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  184,89,23,55,63,                        //mov           $0x3f371759,%eax
-  102,15,110,192,                         //movd          %eax,%xmm0
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  15,88,195,                              //addps         %xmm3,%xmm0
-  184,152,221,147,61,                     //mov           $0x3d93dd98,%eax
-  102,15,110,216,                         //movd          %eax,%xmm3
-  15,198,219,0,                           //shufps        $0x0,%xmm3,%xmm3
-  15,89,218,                              //mulps         %xmm2,%xmm3
-  15,88,216,                              //addps         %xmm0,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  15,87,192,                              //xorps         %xmm0,%xmm0
-  15,87,201,                              //xorps         %xmm1,%xmm1
-  15,87,210,                              //xorps         %xmm2,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_2x3_sse2[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,16,                     //movss         0x10(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_3x4_sse2[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,12,                     //movss         0xc(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,36,                     //movss         0x24(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_4x5_sse2[] = {
-  68,15,40,201,                           //movaps        %xmm1,%xmm9
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,15,16,72,4,                         //movss         0x4(%rax),%xmm1
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,32,                     //movss         0x20(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,48,                     //movss         0x30(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,64,                    //movss         0x40(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,194,                           //addps         %xmm10,%xmm0
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  243,68,15,16,80,20,                     //movss         0x14(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,36,                     //movss         0x24(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,52,                     //movss         0x34(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,68,                    //movss         0x44(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  68,15,89,227,                           //mulps         %xmm3,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  68,15,89,218,                           //mulps         %xmm2,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,209,                           //mulps         %xmm9,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,89,200,                           //mulps         %xmm8,%xmm1
-  65,15,88,202,                           //addps         %xmm10,%xmm1
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,24,                     //movss         0x18(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,40,                     //movss         0x28(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,56,                    //movss         0x38(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,72,                    //movss         0x48(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  68,15,89,235,                           //mulps         %xmm3,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  68,15,89,226,                           //mulps         %xmm2,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,217,                           //mulps         %xmm9,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  243,68,15,16,88,12,                     //movss         0xc(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,28,                     //movss         0x1c(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  243,68,15,16,104,44,                    //movss         0x2c(%rax),%xmm13
-  69,15,198,237,0,                        //shufps        $0x0,%xmm13,%xmm13
-  243,68,15,16,112,60,                    //movss         0x3c(%rax),%xmm14
-  69,15,198,246,0,                        //shufps        $0x0,%xmm14,%xmm14
-  243,68,15,16,120,76,                    //movss         0x4c(%rax),%xmm15
-  69,15,198,255,0,                        //shufps        $0x0,%xmm15,%xmm15
-  68,15,89,243,                           //mulps         %xmm3,%xmm14
-  69,15,88,247,                           //addps         %xmm15,%xmm14
-  68,15,89,234,                           //mulps         %xmm2,%xmm13
-  69,15,88,238,                           //addps         %xmm14,%xmm13
-  69,15,89,225,                           //mulps         %xmm9,%xmm12
-  69,15,88,229,                           //addps         %xmm13,%xmm12
-  69,15,89,216,                           //mulps         %xmm8,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,210,                           //movaps        %xmm10,%xmm2
-  65,15,40,219,                           //movaps        %xmm11,%xmm3
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_matrix_perspective_sse2[] = {
-  68,15,40,192,                           //movaps        %xmm0,%xmm8
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  243,15,16,0,                            //movss         (%rax),%xmm0
-  243,68,15,16,72,4,                      //movss         0x4(%rax),%xmm9
-  15,198,192,0,                           //shufps        $0x0,%xmm0,%xmm0
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,8,                      //movss         0x8(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  65,15,89,192,                           //mulps         %xmm8,%xmm0
-  65,15,88,193,                           //addps         %xmm9,%xmm0
-  243,68,15,16,72,12,                     //movss         0xc(%rax),%xmm9
-  69,15,198,201,0,                        //shufps        $0x0,%xmm9,%xmm9
-  243,68,15,16,80,16,                     //movss         0x10(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,20,                     //movss         0x14(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  68,15,89,209,                           //mulps         %xmm1,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  69,15,89,200,                           //mulps         %xmm8,%xmm9
-  69,15,88,202,                           //addps         %xmm10,%xmm9
-  243,68,15,16,80,24,                     //movss         0x18(%rax),%xmm10
-  69,15,198,210,0,                        //shufps        $0x0,%xmm10,%xmm10
-  243,68,15,16,88,28,                     //movss         0x1c(%rax),%xmm11
-  69,15,198,219,0,                        //shufps        $0x0,%xmm11,%xmm11
-  243,68,15,16,96,32,                     //movss         0x20(%rax),%xmm12
-  69,15,198,228,0,                        //shufps        $0x0,%xmm12,%xmm12
-  68,15,89,217,                           //mulps         %xmm1,%xmm11
-  69,15,88,220,                           //addps         %xmm12,%xmm11
-  69,15,89,208,                           //mulps         %xmm8,%xmm10
-  69,15,88,211,                           //addps         %xmm11,%xmm10
-  65,15,83,202,                           //rcpps         %xmm10,%xmm1
-  15,89,193,                              //mulps         %xmm1,%xmm0
-  68,15,89,201,                           //mulps         %xmm1,%xmm9
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  255,224,                                //jmpq          *%rax
-};
-
-CODE const uint8_t sk_linear_gradient_2stops_sse2[] = {
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  68,15,16,8,                             //movups        (%rax),%xmm9
-  15,16,88,16,                            //movups        0x10(%rax),%xmm3
-  68,15,40,195,                           //movaps        %xmm3,%xmm8
-  69,15,198,192,0,                        //shufps        $0x0,%xmm8,%xmm8
-  65,15,40,201,                           //movaps        %xmm9,%xmm1
-  15,198,201,0,                           //shufps        $0x0,%xmm1,%xmm1
-  68,15,89,192,                           //mulps         %xmm0,%xmm8
-  68,15,88,193,                           //addps         %xmm1,%xmm8
-  15,40,203,                              //movaps        %xmm3,%xmm1
-  15,198,201,85,                          //shufps        $0x55,%xmm1,%xmm1
-  65,15,40,209,                           //movaps        %xmm9,%xmm2
-  15,198,210,85,                          //shufps        $0x55,%xmm2,%xmm2
-  15,89,200,                              //mulps         %xmm0,%xmm1
-  15,88,202,                              //addps         %xmm2,%xmm1
-  15,40,211,                              //movaps        %xmm3,%xmm2
-  15,198,210,170,                         //shufps        $0xaa,%xmm2,%xmm2
-  69,15,40,209,                           //movaps        %xmm9,%xmm10
-  69,15,198,210,170,                      //shufps        $0xaa,%xmm10,%xmm10
-  15,89,208,                              //mulps         %xmm0,%xmm2
-  65,15,88,210,                           //addps         %xmm10,%xmm2
-  15,198,219,255,                         //shufps        $0xff,%xmm3,%xmm3
-  69,15,198,201,255,                      //shufps        $0xff,%xmm9,%xmm9
-  15,89,216,                              //mulps         %xmm0,%xmm3
-  65,15,88,217,                           //addps         %xmm9,%xmm3
-  72,173,                                 //lods          %ds:(%rsi),%rax
-  65,15,40,192,                           //movaps        %xmm8,%xmm0
-  255,224,                                //jmpq          *%rax
-};
-#endif
diff --git a/src/jumper/SkJumper_generated_win.S b/src/jumper/SkJumper_generated_win.S
new file mode 100644
index 0000000..99b5d7a
--- /dev/null
+++ b/src/jumper/SkJumper_generated_win.S
@@ -0,0 +1,27409 @@
+; Copyright 2017 Google Inc.
+;
+; Use of this source code is governed by a BSD-style license that can be
+; found in the LICENSE file.
+
+; This file is generated semi-automatically with this command:
+;   $ src/jumper/build_stages.py
+
+IFDEF RAX
+_text32 SEGMENT ALIGN(32) 'CODE'
+ALIGN 32
+
+PUBLIC _sk_start_pipeline_hsw
+_sk_start_pipeline_hsw LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  86                                  ; push          %rsi
+  DB  87                                  ; push          %rdi
+  DB  83                                  ; push          %rbx
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  197,120,41,125,176                  ; vmovaps       %xmm15,-0x50(%rbp)
+  DB  197,120,41,117,160                  ; vmovaps       %xmm14,-0x60(%rbp)
+  DB  197,120,41,109,144                  ; vmovaps       %xmm13,-0x70(%rbp)
+  DB  197,120,41,101,128                  ; vmovaps       %xmm12,-0x80(%rbp)
+  DB  197,120,41,157,112,255,255,255      ; vmovaps       %xmm11,-0x90(%rbp)
+  DB  197,120,41,149,96,255,255,255       ; vmovaps       %xmm10,-0xa0(%rbp)
+  DB  197,120,41,141,80,255,255,255       ; vmovaps       %xmm9,-0xb0(%rbp)
+  DB  197,120,41,133,64,255,255,255       ; vmovaps       %xmm8,-0xc0(%rbp)
+  DB  197,248,41,189,48,255,255,255       ; vmovaps       %xmm7,-0xd0(%rbp)
+  DB  197,248,41,181,32,255,255,255       ; vmovaps       %xmm6,-0xe0(%rbp)
+  DB  72,137,211                          ; mov           %rdx,%rbx
+  DB  73,137,207                          ; mov           %rcx,%r15
+  DB  76,139,117,48                       ; mov           0x30(%rbp),%r14
+  DB  76,137,206                          ; mov           %r9,%rsi
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,197                          ; mov           %rax,%r13
+  DB  73,137,244                          ; mov           %rsi,%r12
+  DB  73,141,79,8                         ; lea           0x8(%r15),%rcx
+  DB  76,57,193                           ; cmp           %r8,%rcx
+  DB  118,5                               ; jbe           7d <_sk_start_pipeline_hsw+0x7d>
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  235,83                              ; jmp           d0 <_sk_start_pipeline_hsw+0xd0>
+  DB  76,137,133,24,255,255,255           ; mov           %r8,-0xe8(%rbp)
+  DB  65,184,0,0,0,0                      ; mov           $0x0,%r8d
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  197,220,87,228                      ; vxorps        %ymm4,%ymm4,%ymm4
+  DB  197,212,87,237                      ; vxorps        %ymm5,%ymm5,%ymm5
+  DB  197,204,87,246                      ; vxorps        %ymm6,%ymm6,%ymm6
+  DB  197,196,87,255                      ; vxorps        %ymm7,%ymm7,%ymm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  76,139,133,24,255,255,255           ; mov           -0xe8(%rbp),%r8
+  DB  73,141,87,8                         ; lea           0x8(%r15),%rdx
+  DB  73,131,199,16                       ; add           $0x10,%r15
+  DB  77,57,199                           ; cmp           %r8,%r15
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  118,180                             ; jbe           84 <_sk_start_pipeline_hsw+0x84>
+  DB  73,41,208                           ; sub           %rdx,%r8
+  DB  116,44                              ; je            101 <_sk_start_pipeline_hsw+0x101>
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  197,220,87,228                      ; vxorps        %ymm4,%ymm4,%ymm4
+  DB  197,212,87,237                      ; vxorps        %ymm5,%ymm5,%ymm5
+  DB  197,204,87,246                      ; vxorps        %ymm6,%ymm6,%ymm6
+  DB  197,196,87,255                      ; vxorps        %ymm7,%ymm7,%ymm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  197,248,40,181,32,255,255,255       ; vmovaps       -0xe0(%rbp),%xmm6
+  DB  197,248,40,189,48,255,255,255       ; vmovaps       -0xd0(%rbp),%xmm7
+  DB  197,120,40,133,64,255,255,255       ; vmovaps       -0xc0(%rbp),%xmm8
+  DB  197,120,40,141,80,255,255,255       ; vmovaps       -0xb0(%rbp),%xmm9
+  DB  197,120,40,149,96,255,255,255       ; vmovaps       -0xa0(%rbp),%xmm10
+  DB  197,120,40,157,112,255,255,255      ; vmovaps       -0x90(%rbp),%xmm11
+  DB  197,120,40,101,128                  ; vmovaps       -0x80(%rbp),%xmm12
+  DB  197,120,40,109,144                  ; vmovaps       -0x70(%rbp),%xmm13
+  DB  197,120,40,117,160                  ; vmovaps       -0x60(%rbp),%xmm14
+  DB  197,120,40,125,176                  ; vmovaps       -0x50(%rbp),%xmm15
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  95                                  ; pop           %rdi
+  DB  94                                  ; pop           %rsi
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  197,248,119                         ; vzeroupper
+  DB  195                                 ; retq
+
+PUBLIC _sk_just_return_hsw
+_sk_just_return_hsw LABEL PROC
+  DB  195                                 ; retq
+
+PUBLIC _sk_seed_shader_hsw
+_sk_seed_shader_hsw LABEL PROC
+  DB  197,249,110,194                     ; vmovd         %edx,%xmm0
+  DB  196,226,125,88,192                  ; vpbroadcastd  %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,213,72,0,0        ; vbroadcastss  0x48d5(%rip),%ymm1        # 4a48 <_sk_callback_hsw+0x12c>
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,252,88,7                        ; vaddps        (%rdi),%ymm0,%ymm0
+  DB  197,249,110,209                     ; vmovd         %ecx,%xmm2
+  DB  196,226,125,88,210                  ; vpbroadcastd  %xmm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  197,236,88,201                      ; vaddps        %ymm1,%ymm2,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,21,181,72,0,0        ; vbroadcastss  0x48b5(%rip),%ymm2        # 4a4c <_sk_callback_hsw+0x130>
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  197,220,87,228                      ; vxorps        %ymm4,%ymm4,%ymm4
+  DB  197,212,87,237                      ; vxorps        %ymm5,%ymm5,%ymm5
+  DB  197,204,87,246                      ; vxorps        %ymm6,%ymm6,%ymm6
+  DB  197,196,87,255                      ; vxorps        %ymm7,%ymm7,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dither_hsw
+_sk_dither_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,121,110,194                     ; vmovd         %edx,%xmm8
+  DB  196,66,125,88,192                   ; vpbroadcastd  %xmm8,%ymm8
+  DB  197,61,254,71,32                    ; vpaddd        0x20(%rdi),%ymm8,%ymm8
+  DB  197,121,110,201                     ; vmovd         %ecx,%xmm9
+  DB  196,66,125,88,201                   ; vpbroadcastd  %xmm9,%ymm9
+  DB  196,65,53,239,200                   ; vpxor         %ymm8,%ymm9,%ymm9
+  DB  196,98,125,88,21,124,72,0,0         ; vpbroadcastd  0x487c(%rip),%ymm10        # 4a50 <_sk_callback_hsw+0x134>
+  DB  196,65,53,219,218                   ; vpand         %ymm10,%ymm9,%ymm11
+  DB  196,193,37,114,243,5                ; vpslld        $0x5,%ymm11,%ymm11
+  DB  196,65,61,219,210                   ; vpand         %ymm10,%ymm8,%ymm10
+  DB  196,193,45,114,242,4                ; vpslld        $0x4,%ymm10,%ymm10
+  DB  196,98,125,88,37,97,72,0,0          ; vpbroadcastd  0x4861(%rip),%ymm12        # 4a54 <_sk_callback_hsw+0x138>
+  DB  196,98,125,88,45,92,72,0,0          ; vpbroadcastd  0x485c(%rip),%ymm13        # 4a58 <_sk_callback_hsw+0x13c>
+  DB  196,65,53,219,245                   ; vpand         %ymm13,%ymm9,%ymm14
+  DB  196,193,13,114,246,2                ; vpslld        $0x2,%ymm14,%ymm14
+  DB  196,65,61,219,237                   ; vpand         %ymm13,%ymm8,%ymm13
+  DB  196,65,21,254,237                   ; vpaddd        %ymm13,%ymm13,%ymm13
+  DB  196,65,53,219,204                   ; vpand         %ymm12,%ymm9,%ymm9
+  DB  196,193,53,114,209,1                ; vpsrld        $0x1,%ymm9,%ymm9
+  DB  196,65,61,219,196                   ; vpand         %ymm12,%ymm8,%ymm8
+  DB  196,193,61,114,208,2                ; vpsrld        $0x2,%ymm8,%ymm8
+  DB  196,65,21,235,210                   ; vpor          %ymm10,%ymm13,%ymm10
+  DB  196,65,45,235,192                   ; vpor          %ymm8,%ymm10,%ymm8
+  DB  196,65,37,235,214                   ; vpor          %ymm14,%ymm11,%ymm10
+  DB  196,65,61,235,194                   ; vpor          %ymm10,%ymm8,%ymm8
+  DB  196,65,61,235,193                   ; vpor          %ymm9,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,13,14,72,0,0          ; vbroadcastss  0x480e(%rip),%ymm9        # 4a5c <_sk_callback_hsw+0x140>
+  DB  196,98,125,24,21,9,72,0,0           ; vbroadcastss  0x4809(%rip),%ymm10        # 4a60 <_sk_callback_hsw+0x144>
+  DB  196,66,61,184,209                   ; vfmadd231ps   %ymm9,%ymm8,%ymm10
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,65,60,89,194                    ; vmulps        %ymm10,%ymm8,%ymm8
+  DB  197,188,88,192                      ; vaddps        %ymm0,%ymm8,%ymm0
+  DB  197,188,88,201                      ; vaddps        %ymm1,%ymm8,%ymm1
+  DB  197,188,88,210                      ; vaddps        %ymm2,%ymm8,%ymm2
+  DB  197,252,93,195                      ; vminps        %ymm3,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,192                      ; vmaxps        %ymm0,%ymm8,%ymm0
+  DB  197,244,93,203                      ; vminps        %ymm3,%ymm1,%ymm1
+  DB  197,188,95,201                      ; vmaxps        %ymm1,%ymm8,%ymm1
+  DB  197,236,93,211                      ; vminps        %ymm3,%ymm2,%ymm2
+  DB  197,188,95,210                      ; vmaxps        %ymm2,%ymm8,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_constant_color_hsw
+_sk_constant_color_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,0                    ; vbroadcastss  (%rax),%ymm0
+  DB  196,226,125,24,72,4                 ; vbroadcastss  0x4(%rax),%ymm1
+  DB  196,226,125,24,80,8                 ; vbroadcastss  0x8(%rax),%ymm2
+  DB  196,226,125,24,88,12                ; vbroadcastss  0xc(%rax),%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_rgba_hsw
+_sk_load_rgba_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,0                        ; vmovups       (%rax),%ymm0
+  DB  197,252,16,72,32                    ; vmovups       0x20(%rax),%ymm1
+  DB  197,252,16,80,64                    ; vmovups       0x40(%rax),%ymm2
+  DB  197,252,16,88,96                    ; vmovups       0x60(%rax),%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_rgba_hsw
+_sk_store_rgba_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,17,0                        ; vmovups       %ymm0,(%rax)
+  DB  197,252,17,72,32                    ; vmovups       %ymm1,0x20(%rax)
+  DB  197,252,17,80,64                    ; vmovups       %ymm2,0x40(%rax)
+  DB  197,252,17,88,96                    ; vmovups       %ymm3,0x60(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clear_hsw
+_sk_clear_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcatop_hsw
+_sk_srcatop_hsw LABEL PROC
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  196,98,125,24,5,97,71,0,0           ; vbroadcastss  0x4761(%rip),%ymm8        # 4a64 <_sk_callback_hsw+0x148>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,226,61,184,196                  ; vfmadd231ps   %ymm4,%ymm8,%ymm0
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  196,226,61,184,205                  ; vfmadd231ps   %ymm5,%ymm8,%ymm1
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  196,226,61,184,214                  ; vfmadd231ps   %ymm6,%ymm8,%ymm2
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  196,194,69,168,216                  ; vfmadd213ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstatop_hsw
+_sk_dstatop_hsw LABEL PROC
+  DB  196,98,125,24,5,52,71,0,0           ; vbroadcastss  0x4734(%rip),%ymm8        # 4a68 <_sk_callback_hsw+0x14c>
+  DB  197,60,92,199                       ; vsubps        %ymm7,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  196,226,101,184,196                 ; vfmadd231ps   %ymm4,%ymm3,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  196,226,101,184,205                 ; vfmadd231ps   %ymm5,%ymm3,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  196,226,101,184,214                 ; vfmadd231ps   %ymm6,%ymm3,%ymm2
+  DB  197,60,89,195                       ; vmulps        %ymm3,%ymm8,%ymm8
+  DB  196,194,69,168,216                  ; vfmadd213ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcin_hsw
+_sk_srcin_hsw LABEL PROC
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstin_hsw
+_sk_dstin_hsw LABEL PROC
+  DB  197,228,89,196                      ; vmulps        %ymm4,%ymm3,%ymm0
+  DB  197,228,89,205                      ; vmulps        %ymm5,%ymm3,%ymm1
+  DB  197,228,89,214                      ; vmulps        %ymm6,%ymm3,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcout_hsw
+_sk_srcout_hsw LABEL PROC
+  DB  196,98,125,24,5,219,70,0,0          ; vbroadcastss  0x46db(%rip),%ymm8        # 4a6c <_sk_callback_hsw+0x150>
+  DB  197,60,92,199                       ; vsubps        %ymm7,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstout_hsw
+_sk_dstout_hsw LABEL PROC
+  DB  196,226,125,24,5,190,70,0,0         ; vbroadcastss  0x46be(%rip),%ymm0        # 4a70 <_sk_callback_hsw+0x154>
+  DB  197,252,92,219                      ; vsubps        %ymm3,%ymm0,%ymm3
+  DB  197,228,89,196                      ; vmulps        %ymm4,%ymm3,%ymm0
+  DB  197,228,89,205                      ; vmulps        %ymm5,%ymm3,%ymm1
+  DB  197,228,89,214                      ; vmulps        %ymm6,%ymm3,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_hsw
+_sk_srcover_hsw LABEL PROC
+  DB  196,98,125,24,5,161,70,0,0          ; vbroadcastss  0x46a1(%rip),%ymm8        # 4a74 <_sk_callback_hsw+0x158>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,194,93,184,192                  ; vfmadd231ps   %ymm8,%ymm4,%ymm0
+  DB  196,194,85,184,200                  ; vfmadd231ps   %ymm8,%ymm5,%ymm1
+  DB  196,194,77,184,208                  ; vfmadd231ps   %ymm8,%ymm6,%ymm2
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstover_hsw
+_sk_dstover_hsw LABEL PROC
+  DB  196,98,125,24,5,128,70,0,0          ; vbroadcastss  0x4680(%rip),%ymm8        # 4a78 <_sk_callback_hsw+0x15c>
+  DB  197,60,92,199                       ; vsubps        %ymm7,%ymm8,%ymm8
+  DB  196,226,61,168,196                  ; vfmadd213ps   %ymm4,%ymm8,%ymm0
+  DB  196,226,61,168,205                  ; vfmadd213ps   %ymm5,%ymm8,%ymm1
+  DB  196,226,61,168,214                  ; vfmadd213ps   %ymm6,%ymm8,%ymm2
+  DB  196,226,61,168,223                  ; vfmadd213ps   %ymm7,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_modulate_hsw
+_sk_modulate_hsw LABEL PROC
+  DB  197,252,89,196                      ; vmulps        %ymm4,%ymm0,%ymm0
+  DB  197,244,89,205                      ; vmulps        %ymm5,%ymm1,%ymm1
+  DB  197,236,89,214                      ; vmulps        %ymm6,%ymm2,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_multiply_hsw
+_sk_multiply_hsw LABEL PROC
+  DB  196,98,125,24,5,75,70,0,0           ; vbroadcastss  0x464b(%rip),%ymm8        # 4a7c <_sk_callback_hsw+0x160>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,52,89,208                       ; vmulps        %ymm0,%ymm9,%ymm10
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,98,61,184,212                   ; vfmadd231ps   %ymm4,%ymm8,%ymm10
+  DB  196,194,93,168,194                  ; vfmadd213ps   %ymm10,%ymm4,%ymm0
+  DB  197,52,89,209                       ; vmulps        %ymm1,%ymm9,%ymm10
+  DB  196,98,61,184,213                   ; vfmadd231ps   %ymm5,%ymm8,%ymm10
+  DB  196,194,85,168,202                  ; vfmadd213ps   %ymm10,%ymm5,%ymm1
+  DB  197,52,89,210                       ; vmulps        %ymm2,%ymm9,%ymm10
+  DB  196,98,61,184,214                   ; vfmadd231ps   %ymm6,%ymm8,%ymm10
+  DB  196,194,77,168,210                  ; vfmadd213ps   %ymm10,%ymm6,%ymm2
+  DB  197,52,89,203                       ; vmulps        %ymm3,%ymm9,%ymm9
+  DB  196,66,69,168,193                   ; vfmadd213ps   %ymm9,%ymm7,%ymm8
+  DB  196,194,69,168,216                  ; vfmadd213ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_plus__hsw
+_sk_plus__hsw LABEL PROC
+  DB  197,252,88,196                      ; vaddps        %ymm4,%ymm0,%ymm0
+  DB  197,244,88,205                      ; vaddps        %ymm5,%ymm1,%ymm1
+  DB  197,236,88,214                      ; vaddps        %ymm6,%ymm2,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_screen_hsw
+_sk_screen_hsw LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  196,194,93,172,192                  ; vfnmadd213ps  %ymm8,%ymm4,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  196,194,85,172,200                  ; vfnmadd213ps  %ymm8,%ymm5,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  196,194,77,172,208                  ; vfnmadd213ps  %ymm8,%ymm6,%ymm2
+  DB  197,100,88,199                      ; vaddps        %ymm7,%ymm3,%ymm8
+  DB  196,194,69,172,216                  ; vfnmadd213ps  %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xor__hsw
+_sk_xor__hsw LABEL PROC
+  DB  196,98,125,24,5,198,69,0,0          ; vbroadcastss  0x45c6(%rip),%ymm8        # 4a80 <_sk_callback_hsw+0x164>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,226,61,184,196                  ; vfmadd231ps   %ymm4,%ymm8,%ymm0
+  DB  197,180,89,201                      ; vmulps        %ymm1,%ymm9,%ymm1
+  DB  196,226,61,184,205                  ; vfmadd231ps   %ymm5,%ymm8,%ymm1
+  DB  197,180,89,210                      ; vmulps        %ymm2,%ymm9,%ymm2
+  DB  196,226,61,184,214                  ; vfmadd231ps   %ymm6,%ymm8,%ymm2
+  DB  197,180,89,219                      ; vmulps        %ymm3,%ymm9,%ymm3
+  DB  196,98,69,168,195                   ; vfmadd213ps   %ymm3,%ymm7,%ymm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,195                      ; vmovaps       %ymm8,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_darken_hsw
+_sk_darken_hsw LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  196,193,124,95,193                  ; vmaxps        %ymm9,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,193,116,95,201                  ; vmaxps        %ymm9,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,100,89,206                      ; vmulps        %ymm6,%ymm3,%ymm9
+  DB  196,193,108,95,209                  ; vmaxps        %ymm9,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,78,69,0,0           ; vbroadcastss  0x454e(%rip),%ymm8        # 4a84 <_sk_callback_hsw+0x168>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lighten_hsw
+_sk_lighten_hsw LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  196,193,124,93,193                  ; vminps        %ymm9,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,193,116,93,201                  ; vminps        %ymm9,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,100,89,206                      ; vmulps        %ymm6,%ymm3,%ymm9
+  DB  196,193,108,93,209                  ; vminps        %ymm9,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,253,68,0,0          ; vbroadcastss  0x44fd(%rip),%ymm8        # 4a88 <_sk_callback_hsw+0x16c>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_difference_hsw
+_sk_difference_hsw LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  196,193,124,93,193                  ; vminps        %ymm9,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,193,116,93,201                  ; vminps        %ymm9,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,100,89,206                      ; vmulps        %ymm6,%ymm3,%ymm9
+  DB  196,193,108,93,209                  ; vminps        %ymm9,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,160,68,0,0          ; vbroadcastss  0x44a0(%rip),%ymm8        # 4a8c <_sk_callback_hsw+0x170>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_exclusion_hsw
+_sk_exclusion_hsw LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,196                      ; vmulps        %ymm4,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,205                      ; vmulps        %ymm5,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,214                      ; vmulps        %ymm6,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,94,68,0,0           ; vbroadcastss  0x445e(%rip),%ymm8        # 4a90 <_sk_callback_hsw+0x174>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colorburn_hsw
+_sk_colorburn_hsw LABEL PROC
+  DB  196,98,125,24,5,76,68,0,0           ; vbroadcastss  0x444c(%rip),%ymm8        # 4a94 <_sk_callback_hsw+0x178>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,52,89,216                       ; vmulps        %ymm0,%ymm9,%ymm11
+  DB  196,65,44,87,210                    ; vxorps        %ymm10,%ymm10,%ymm10
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,228                       ; vmulps        %ymm4,%ymm8,%ymm12
+  DB  197,68,92,236                       ; vsubps        %ymm4,%ymm7,%ymm13
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  197,20,94,232                       ; vdivps        %ymm0,%ymm13,%ymm13
+  DB  196,65,68,93,237                    ; vminps        %ymm13,%ymm7,%ymm13
+  DB  196,65,68,92,237                    ; vsubps        %ymm13,%ymm7,%ymm13
+  DB  196,66,101,168,235                  ; vfmadd213ps   %ymm11,%ymm3,%ymm13
+  DB  196,65,28,88,237                    ; vaddps        %ymm13,%ymm12,%ymm13
+  DB  197,28,88,224                       ; vaddps        %ymm0,%ymm12,%ymm12
+  DB  196,193,124,194,194,0               ; vcmpeqps      %ymm10,%ymm0,%ymm0
+  DB  196,195,21,74,196,0                 ; vblendvps     %ymm0,%ymm12,%ymm13,%ymm0
+  DB  197,92,194,231,0                    ; vcmpeqps      %ymm7,%ymm4,%ymm12
+  DB  197,36,88,220                       ; vaddps        %ymm4,%ymm11,%ymm11
+  DB  196,195,125,74,195,192              ; vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  DB  197,52,89,217                       ; vmulps        %ymm1,%ymm9,%ymm11
+  DB  197,60,89,229                       ; vmulps        %ymm5,%ymm8,%ymm12
+  DB  197,68,92,237                       ; vsubps        %ymm5,%ymm7,%ymm13
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  197,20,94,233                       ; vdivps        %ymm1,%ymm13,%ymm13
+  DB  196,65,68,93,237                    ; vminps        %ymm13,%ymm7,%ymm13
+  DB  196,65,68,92,237                    ; vsubps        %ymm13,%ymm7,%ymm13
+  DB  196,66,101,168,235                  ; vfmadd213ps   %ymm11,%ymm3,%ymm13
+  DB  196,65,28,88,237                    ; vaddps        %ymm13,%ymm12,%ymm13
+  DB  197,28,88,225                       ; vaddps        %ymm1,%ymm12,%ymm12
+  DB  196,193,116,194,202,0               ; vcmpeqps      %ymm10,%ymm1,%ymm1
+  DB  196,195,21,74,204,16                ; vblendvps     %ymm1,%ymm12,%ymm13,%ymm1
+  DB  197,84,194,231,0                    ; vcmpeqps      %ymm7,%ymm5,%ymm12
+  DB  197,36,88,221                       ; vaddps        %ymm5,%ymm11,%ymm11
+  DB  196,195,117,74,203,192              ; vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  DB  197,52,89,202                       ; vmulps        %ymm2,%ymm9,%ymm9
+  DB  196,65,108,194,210,0                ; vcmpeqps      %ymm10,%ymm2,%ymm10
+  DB  197,60,89,222                       ; vmulps        %ymm6,%ymm8,%ymm11
+  DB  197,68,92,230                       ; vsubps        %ymm6,%ymm7,%ymm12
+  DB  197,28,89,227                       ; vmulps        %ymm3,%ymm12,%ymm12
+  DB  197,28,94,226                       ; vdivps        %ymm2,%ymm12,%ymm12
+  DB  197,164,88,210                      ; vaddps        %ymm2,%ymm11,%ymm2
+  DB  196,65,68,93,228                    ; vminps        %ymm12,%ymm7,%ymm12
+  DB  196,65,68,92,228                    ; vsubps        %ymm12,%ymm7,%ymm12
+  DB  196,66,101,168,225                  ; vfmadd213ps   %ymm9,%ymm3,%ymm12
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,227,37,74,210,160               ; vblendvps     %ymm10,%ymm2,%ymm11,%ymm2
+  DB  197,76,194,215,0                    ; vcmpeqps      %ymm7,%ymm6,%ymm10
+  DB  197,52,88,206                       ; vaddps        %ymm6,%ymm9,%ymm9
+  DB  196,195,109,74,209,160              ; vblendvps     %ymm10,%ymm9,%ymm2,%ymm2
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colordodge_hsw
+_sk_colordodge_hsw LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,98,125,24,13,87,67,0,0          ; vbroadcastss  0x4357(%rip),%ymm9        # 4a98 <_sk_callback_hsw+0x17c>
+  DB  197,52,92,215                       ; vsubps        %ymm7,%ymm9,%ymm10
+  DB  197,44,89,216                       ; vmulps        %ymm0,%ymm10,%ymm11
+  DB  197,52,92,203                       ; vsubps        %ymm3,%ymm9,%ymm9
+  DB  197,100,89,228                      ; vmulps        %ymm4,%ymm3,%ymm12
+  DB  197,100,92,232                      ; vsubps        %ymm0,%ymm3,%ymm13
+  DB  196,65,28,94,229                    ; vdivps        %ymm13,%ymm12,%ymm12
+  DB  197,52,89,236                       ; vmulps        %ymm4,%ymm9,%ymm13
+  DB  196,65,68,93,228                    ; vminps        %ymm12,%ymm7,%ymm12
+  DB  196,66,101,168,227                  ; vfmadd213ps   %ymm11,%ymm3,%ymm12
+  DB  196,65,20,88,228                    ; vaddps        %ymm12,%ymm13,%ymm12
+  DB  197,20,88,232                       ; vaddps        %ymm0,%ymm13,%ymm13
+  DB  197,252,194,195,0                   ; vcmpeqps      %ymm3,%ymm0,%ymm0
+  DB  196,195,29,74,197,0                 ; vblendvps     %ymm0,%ymm13,%ymm12,%ymm0
+  DB  196,65,92,194,224,0                 ; vcmpeqps      %ymm8,%ymm4,%ymm12
+  DB  197,36,88,220                       ; vaddps        %ymm4,%ymm11,%ymm11
+  DB  196,195,125,74,195,192              ; vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  DB  197,44,89,217                       ; vmulps        %ymm1,%ymm10,%ymm11
+  DB  197,100,89,229                      ; vmulps        %ymm5,%ymm3,%ymm12
+  DB  197,100,92,233                      ; vsubps        %ymm1,%ymm3,%ymm13
+  DB  196,65,28,94,229                    ; vdivps        %ymm13,%ymm12,%ymm12
+  DB  197,52,89,237                       ; vmulps        %ymm5,%ymm9,%ymm13
+  DB  196,65,68,93,228                    ; vminps        %ymm12,%ymm7,%ymm12
+  DB  196,66,101,168,227                  ; vfmadd213ps   %ymm11,%ymm3,%ymm12
+  DB  196,65,20,88,228                    ; vaddps        %ymm12,%ymm13,%ymm12
+  DB  197,20,88,233                       ; vaddps        %ymm1,%ymm13,%ymm13
+  DB  197,244,194,203,0                   ; vcmpeqps      %ymm3,%ymm1,%ymm1
+  DB  196,195,29,74,205,16                ; vblendvps     %ymm1,%ymm13,%ymm12,%ymm1
+  DB  196,65,84,194,224,0                 ; vcmpeqps      %ymm8,%ymm5,%ymm12
+  DB  197,36,88,221                       ; vaddps        %ymm5,%ymm11,%ymm11
+  DB  196,195,117,74,203,192              ; vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  197,100,89,222                      ; vmulps        %ymm6,%ymm3,%ymm11
+  DB  197,100,92,226                      ; vsubps        %ymm2,%ymm3,%ymm12
+  DB  196,65,36,94,220                    ; vdivps        %ymm12,%ymm11,%ymm11
+  DB  197,52,89,230                       ; vmulps        %ymm6,%ymm9,%ymm12
+  DB  196,65,68,93,219                    ; vminps        %ymm11,%ymm7,%ymm11
+  DB  196,66,101,168,218                  ; vfmadd213ps   %ymm10,%ymm3,%ymm11
+  DB  196,65,28,88,219                    ; vaddps        %ymm11,%ymm12,%ymm11
+  DB  197,28,88,226                       ; vaddps        %ymm2,%ymm12,%ymm12
+  DB  197,236,194,211,0                   ; vcmpeqps      %ymm3,%ymm2,%ymm2
+  DB  196,195,37,74,212,32                ; vblendvps     %ymm2,%ymm12,%ymm11,%ymm2
+  DB  196,65,76,194,192,0                 ; vcmpeqps      %ymm8,%ymm6,%ymm8
+  DB  197,44,88,214                       ; vaddps        %ymm6,%ymm10,%ymm10
+  DB  196,195,109,74,210,128              ; vblendvps     %ymm8,%ymm10,%ymm2,%ymm2
+  DB  196,194,69,184,217                  ; vfmadd231ps   %ymm9,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hardlight_hsw
+_sk_hardlight_hsw LABEL PROC
+  DB  196,98,125,24,5,120,66,0,0          ; vbroadcastss  0x4278(%rip),%ymm8        # 4a9c <_sk_callback_hsw+0x180>
+  DB  197,60,92,215                       ; vsubps        %ymm7,%ymm8,%ymm10
+  DB  197,44,89,216                       ; vmulps        %ymm0,%ymm10,%ymm11
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,98,61,184,220                   ; vfmadd231ps   %ymm4,%ymm8,%ymm11
+  DB  197,124,88,200                      ; vaddps        %ymm0,%ymm0,%ymm9
+  DB  197,52,194,227,2                    ; vcmpleps      %ymm3,%ymm9,%ymm12
+  DB  197,124,89,204                      ; vmulps        %ymm4,%ymm0,%ymm9
+  DB  196,65,52,88,233                    ; vaddps        %ymm9,%ymm9,%ymm13
+  DB  197,100,89,207                      ; vmulps        %ymm7,%ymm3,%ymm9
+  DB  197,68,92,244                       ; vsubps        %ymm4,%ymm7,%ymm14
+  DB  197,228,92,192                      ; vsubps        %ymm0,%ymm3,%ymm0
+  DB  196,193,124,89,198                  ; vmulps        %ymm14,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,180,92,192                      ; vsubps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,197,192              ; vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  DB  197,164,88,192                      ; vaddps        %ymm0,%ymm11,%ymm0
+  DB  197,44,89,217                       ; vmulps        %ymm1,%ymm10,%ymm11
+  DB  196,98,61,184,221                   ; vfmadd231ps   %ymm5,%ymm8,%ymm11
+  DB  197,116,88,225                      ; vaddps        %ymm1,%ymm1,%ymm12
+  DB  197,28,194,227,2                    ; vcmpleps      %ymm3,%ymm12,%ymm12
+  DB  197,116,89,237                      ; vmulps        %ymm5,%ymm1,%ymm13
+  DB  196,65,20,88,237                    ; vaddps        %ymm13,%ymm13,%ymm13
+  DB  197,68,92,245                       ; vsubps        %ymm5,%ymm7,%ymm14
+  DB  197,228,92,201                      ; vsubps        %ymm1,%ymm3,%ymm1
+  DB  196,193,116,89,206                  ; vmulps        %ymm14,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,180,92,201                      ; vsubps        %ymm1,%ymm9,%ymm1
+  DB  196,195,117,74,205,192              ; vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  196,98,61,184,214                   ; vfmadd231ps   %ymm6,%ymm8,%ymm10
+  DB  197,108,88,218                      ; vaddps        %ymm2,%ymm2,%ymm11
+  DB  197,36,194,219,2                    ; vcmpleps      %ymm3,%ymm11,%ymm11
+  DB  197,108,89,230                      ; vmulps        %ymm6,%ymm2,%ymm12
+  DB  196,65,28,88,228                    ; vaddps        %ymm12,%ymm12,%ymm12
+  DB  197,68,92,238                       ; vsubps        %ymm6,%ymm7,%ymm13
+  DB  197,228,92,210                      ; vsubps        %ymm2,%ymm3,%ymm2
+  DB  196,193,108,89,213                  ; vmulps        %ymm13,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,180,92,210                      ; vsubps        %ymm2,%ymm9,%ymm2
+  DB  196,195,109,74,212,176              ; vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_overlay_hsw
+_sk_overlay_hsw LABEL PROC
+  DB  196,98,125,24,5,176,65,0,0          ; vbroadcastss  0x41b0(%rip),%ymm8        # 4aa0 <_sk_callback_hsw+0x184>
+  DB  197,60,92,215                       ; vsubps        %ymm7,%ymm8,%ymm10
+  DB  197,44,89,216                       ; vmulps        %ymm0,%ymm10,%ymm11
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,98,61,184,220                   ; vfmadd231ps   %ymm4,%ymm8,%ymm11
+  DB  197,92,88,204                       ; vaddps        %ymm4,%ymm4,%ymm9
+  DB  197,52,194,231,2                    ; vcmpleps      %ymm7,%ymm9,%ymm12
+  DB  197,124,89,204                      ; vmulps        %ymm4,%ymm0,%ymm9
+  DB  196,65,52,88,233                    ; vaddps        %ymm9,%ymm9,%ymm13
+  DB  197,100,89,207                      ; vmulps        %ymm7,%ymm3,%ymm9
+  DB  197,68,92,244                       ; vsubps        %ymm4,%ymm7,%ymm14
+  DB  197,228,92,192                      ; vsubps        %ymm0,%ymm3,%ymm0
+  DB  196,193,124,89,198                  ; vmulps        %ymm14,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,180,92,192                      ; vsubps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,197,192              ; vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  DB  197,164,88,192                      ; vaddps        %ymm0,%ymm11,%ymm0
+  DB  197,44,89,217                       ; vmulps        %ymm1,%ymm10,%ymm11
+  DB  196,98,61,184,221                   ; vfmadd231ps   %ymm5,%ymm8,%ymm11
+  DB  197,84,88,229                       ; vaddps        %ymm5,%ymm5,%ymm12
+  DB  197,28,194,231,2                    ; vcmpleps      %ymm7,%ymm12,%ymm12
+  DB  197,116,89,237                      ; vmulps        %ymm5,%ymm1,%ymm13
+  DB  196,65,20,88,237                    ; vaddps        %ymm13,%ymm13,%ymm13
+  DB  197,68,92,245                       ; vsubps        %ymm5,%ymm7,%ymm14
+  DB  197,228,92,201                      ; vsubps        %ymm1,%ymm3,%ymm1
+  DB  196,193,116,89,206                  ; vmulps        %ymm14,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,180,92,201                      ; vsubps        %ymm1,%ymm9,%ymm1
+  DB  196,195,117,74,205,192              ; vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  196,98,61,184,214                   ; vfmadd231ps   %ymm6,%ymm8,%ymm10
+  DB  197,76,88,222                       ; vaddps        %ymm6,%ymm6,%ymm11
+  DB  197,36,194,223,2                    ; vcmpleps      %ymm7,%ymm11,%ymm11
+  DB  197,108,89,230                      ; vmulps        %ymm6,%ymm2,%ymm12
+  DB  196,65,28,88,228                    ; vaddps        %ymm12,%ymm12,%ymm12
+  DB  197,68,92,238                       ; vsubps        %ymm6,%ymm7,%ymm13
+  DB  197,228,92,210                      ; vsubps        %ymm2,%ymm3,%ymm2
+  DB  196,193,108,89,213                  ; vmulps        %ymm13,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,180,92,210                      ; vsubps        %ymm2,%ymm9,%ymm2
+  DB  196,195,109,74,212,176              ; vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_softlight_hsw
+_sk_softlight_hsw LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,252,17,84,36,32                 ; vmovups       %ymm2,0x20(%rsp)
+  DB  197,252,40,209                      ; vmovaps       %ymm1,%ymm2
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  196,65,52,87,201                    ; vxorps        %ymm9,%ymm9,%ymm9
+  DB  197,52,194,215,1                    ; vcmpltps      %ymm7,%ymm9,%ymm10
+  DB  197,92,94,199                       ; vdivps        %ymm7,%ymm4,%ymm8
+  DB  196,67,53,74,232,160                ; vblendvps     %ymm10,%ymm8,%ymm9,%ymm13
+  DB  196,65,20,88,197                    ; vaddps        %ymm13,%ymm13,%ymm8
+  DB  196,65,60,88,192                    ; vaddps        %ymm8,%ymm8,%ymm8
+  DB  196,66,61,168,192                   ; vfmadd213ps   %ymm8,%ymm8,%ymm8
+  DB  196,98,125,24,29,183,64,0,0         ; vbroadcastss  0x40b7(%rip),%ymm11        # 4aa8 <_sk_callback_hsw+0x18c>
+  DB  196,65,20,88,227                    ; vaddps        %ymm11,%ymm13,%ymm12
+  DB  196,65,28,89,192                    ; vmulps        %ymm8,%ymm12,%ymm8
+  DB  196,98,125,24,37,168,64,0,0         ; vbroadcastss  0x40a8(%rip),%ymm12        # 4aac <_sk_callback_hsw+0x190>
+  DB  196,66,21,184,196                   ; vfmadd231ps   %ymm12,%ymm13,%ymm8
+  DB  196,65,124,82,245                   ; vrsqrtps      %ymm13,%ymm14
+  DB  196,65,124,83,246                   ; vrcpps        %ymm14,%ymm14
+  DB  196,65,12,92,245                    ; vsubps        %ymm13,%ymm14,%ymm14
+  DB  197,92,88,252                       ; vaddps        %ymm4,%ymm4,%ymm15
+  DB  196,65,4,88,255                     ; vaddps        %ymm15,%ymm15,%ymm15
+  DB  197,4,194,255,2                     ; vcmpleps      %ymm7,%ymm15,%ymm15
+  DB  196,67,13,74,240,240                ; vblendvps     %ymm15,%ymm8,%ymm14,%ymm14
+  DB  197,116,88,249                      ; vaddps        %ymm1,%ymm1,%ymm15
+  DB  196,98,125,24,5,107,64,0,0          ; vbroadcastss  0x406b(%rip),%ymm8        # 4aa4 <_sk_callback_hsw+0x188>
+  DB  196,65,60,92,237                    ; vsubps        %ymm13,%ymm8,%ymm13
+  DB  197,132,92,195                      ; vsubps        %ymm3,%ymm15,%ymm0
+  DB  196,98,125,168,235                  ; vfmadd213ps   %ymm3,%ymm0,%ymm13
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  196,193,124,89,198                  ; vmulps        %ymm14,%ymm0,%ymm0
+  DB  197,20,89,236                       ; vmulps        %ymm4,%ymm13,%ymm13
+  DB  196,226,101,184,196                 ; vfmadd231ps   %ymm4,%ymm3,%ymm0
+  DB  197,4,194,243,2                     ; vcmpleps      %ymm3,%ymm15,%ymm14
+  DB  196,195,125,74,197,224              ; vblendvps     %ymm14,%ymm13,%ymm0,%ymm0
+  DB  197,252,17,4,36                     ; vmovups       %ymm0,(%rsp)
+  DB  197,212,94,199                      ; vdivps        %ymm7,%ymm5,%ymm0
+  DB  196,227,53,74,192,160               ; vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  DB  197,124,88,240                      ; vaddps        %ymm0,%ymm0,%ymm14
+  DB  196,65,12,88,246                    ; vaddps        %ymm14,%ymm14,%ymm14
+  DB  196,66,13,168,246                   ; vfmadd213ps   %ymm14,%ymm14,%ymm14
+  DB  196,65,124,88,251                   ; vaddps        %ymm11,%ymm0,%ymm15
+  DB  196,65,4,89,246                     ; vmulps        %ymm14,%ymm15,%ymm14
+  DB  196,66,125,184,244                  ; vfmadd231ps   %ymm12,%ymm0,%ymm14
+  DB  197,124,82,248                      ; vrsqrtps      %ymm0,%ymm15
+  DB  196,65,124,83,255                   ; vrcpps        %ymm15,%ymm15
+  DB  197,4,92,248                        ; vsubps        %ymm0,%ymm15,%ymm15
+  DB  197,84,88,237                       ; vaddps        %ymm5,%ymm5,%ymm13
+  DB  196,65,20,88,237                    ; vaddps        %ymm13,%ymm13,%ymm13
+  DB  197,20,194,239,2                    ; vcmpleps      %ymm7,%ymm13,%ymm13
+  DB  196,67,5,74,238,208                 ; vblendvps     %ymm13,%ymm14,%ymm15,%ymm13
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,108,88,242                      ; vaddps        %ymm2,%ymm2,%ymm14
+  DB  197,12,92,251                       ; vsubps        %ymm3,%ymm14,%ymm15
+  DB  196,226,5,168,195                   ; vfmadd213ps   %ymm3,%ymm15,%ymm0
+  DB  197,4,89,255                        ; vmulps        %ymm7,%ymm15,%ymm15
+  DB  196,65,4,89,237                     ; vmulps        %ymm13,%ymm15,%ymm13
+  DB  197,252,89,197                      ; vmulps        %ymm5,%ymm0,%ymm0
+  DB  196,98,101,184,237                  ; vfmadd231ps   %ymm5,%ymm3,%ymm13
+  DB  197,12,194,243,2                    ; vcmpleps      %ymm3,%ymm14,%ymm14
+  DB  196,99,21,74,240,224                ; vblendvps     %ymm14,%ymm0,%ymm13,%ymm14
+  DB  197,204,94,199                      ; vdivps        %ymm7,%ymm6,%ymm0
+  DB  196,227,53,74,192,160               ; vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  DB  197,124,88,200                      ; vaddps        %ymm0,%ymm0,%ymm9
+  DB  196,65,52,88,201                    ; vaddps        %ymm9,%ymm9,%ymm9
+  DB  196,66,53,168,201                   ; vfmadd213ps   %ymm9,%ymm9,%ymm9
+  DB  196,65,124,88,211                   ; vaddps        %ymm11,%ymm0,%ymm10
+  DB  196,65,44,89,201                    ; vmulps        %ymm9,%ymm10,%ymm9
+  DB  196,66,125,184,204                  ; vfmadd231ps   %ymm12,%ymm0,%ymm9
+  DB  197,124,82,208                      ; vrsqrtps      %ymm0,%ymm10
+  DB  196,65,124,83,210                   ; vrcpps        %ymm10,%ymm10
+  DB  197,44,92,208                       ; vsubps        %ymm0,%ymm10,%ymm10
+  DB  197,76,88,222                       ; vaddps        %ymm6,%ymm6,%ymm11
+  DB  196,65,36,88,219                    ; vaddps        %ymm11,%ymm11,%ymm11
+  DB  197,36,194,223,2                    ; vcmpleps      %ymm7,%ymm11,%ymm11
+  DB  196,67,45,74,201,176                ; vblendvps     %ymm11,%ymm9,%ymm10,%ymm9
+  DB  197,124,16,100,36,32                ; vmovups       0x20(%rsp),%ymm12
+  DB  196,65,28,88,212                    ; vaddps        %ymm12,%ymm12,%ymm10
+  DB  197,44,92,219                       ; vsubps        %ymm3,%ymm10,%ymm11
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  196,226,37,168,195                  ; vfmadd213ps   %ymm3,%ymm11,%ymm0
+  DB  197,36,89,223                       ; vmulps        %ymm7,%ymm11,%ymm11
+  DB  196,65,36,89,201                    ; vmulps        %ymm9,%ymm11,%ymm9
+  DB  197,252,89,198                      ; vmulps        %ymm6,%ymm0,%ymm0
+  DB  196,98,101,184,206                  ; vfmadd231ps   %ymm6,%ymm3,%ymm9
+  DB  197,44,194,211,2                    ; vcmpleps      %ymm3,%ymm10,%ymm10
+  DB  196,99,53,74,200,160                ; vblendvps     %ymm10,%ymm0,%ymm9,%ymm9
+  DB  197,60,92,215                       ; vsubps        %ymm7,%ymm8,%ymm10
+  DB  197,172,89,193                      ; vmulps        %ymm1,%ymm10,%ymm0
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,226,61,184,196                  ; vfmadd231ps   %ymm4,%ymm8,%ymm0
+  DB  197,252,88,4,36                     ; vaddps        (%rsp),%ymm0,%ymm0
+  DB  197,172,89,202                      ; vmulps        %ymm2,%ymm10,%ymm1
+  DB  196,226,61,184,205                  ; vfmadd231ps   %ymm5,%ymm8,%ymm1
+  DB  196,193,116,88,206                  ; vaddps        %ymm14,%ymm1,%ymm1
+  DB  196,193,44,89,212                   ; vmulps        %ymm12,%ymm10,%ymm2
+  DB  196,226,61,184,214                  ; vfmadd231ps   %ymm6,%ymm8,%ymm2
+  DB  196,193,108,88,209                  ; vaddps        %ymm9,%ymm2,%ymm2
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hue_hsw
+_sk_hue_hsw LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,124,40,194                      ; vmovaps       %ymm2,%ymm8
+  DB  197,124,17,68,36,32                 ; vmovups       %ymm8,0x20(%rsp)
+  DB  197,252,17,12,36                    ; vmovups       %ymm1,(%rsp)
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,108,89,203                      ; vmulps        %ymm3,%ymm2,%ymm9
+  DB  197,116,89,211                      ; vmulps        %ymm3,%ymm1,%ymm10
+  DB  197,60,89,219                       ; vmulps        %ymm3,%ymm8,%ymm11
+  DB  197,84,95,198                       ; vmaxps        %ymm6,%ymm5,%ymm8
+  DB  196,65,92,95,192                    ; vmaxps        %ymm8,%ymm4,%ymm8
+  DB  197,84,93,230                       ; vminps        %ymm6,%ymm5,%ymm12
+  DB  196,65,92,93,228                    ; vminps        %ymm12,%ymm4,%ymm12
+  DB  196,65,60,92,196                    ; vsubps        %ymm12,%ymm8,%ymm8
+  DB  197,60,89,227                       ; vmulps        %ymm3,%ymm8,%ymm12
+  DB  196,65,44,93,195                    ; vminps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,93,232                    ; vminps        %ymm8,%ymm9,%ymm13
+  DB  196,65,44,95,195                    ; vmaxps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,95,192                    ; vmaxps        %ymm8,%ymm9,%ymm8
+  DB  196,65,60,92,245                    ; vsubps        %ymm13,%ymm8,%ymm14
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,65,12,194,248,0                 ; vcmpeqps      %ymm8,%ymm14,%ymm15
+  DB  196,65,52,92,205                    ; vsubps        %ymm13,%ymm9,%ymm9
+  DB  196,65,28,89,201                    ; vmulps        %ymm9,%ymm12,%ymm9
+  DB  196,65,52,94,206                    ; vdivps        %ymm14,%ymm9,%ymm9
+  DB  196,67,53,74,200,240                ; vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  DB  196,65,44,92,213                    ; vsubps        %ymm13,%ymm10,%ymm10
+  DB  196,65,28,89,210                    ; vmulps        %ymm10,%ymm12,%ymm10
+  DB  196,65,44,94,214                    ; vdivps        %ymm14,%ymm10,%ymm10
+  DB  196,195,45,74,200,240               ; vblendvps     %ymm15,%ymm8,%ymm10,%ymm1
+  DB  196,65,36,92,213                    ; vsubps        %ymm13,%ymm11,%ymm10
+  DB  196,65,28,89,210                    ; vmulps        %ymm10,%ymm12,%ymm10
+  DB  196,65,44,94,214                    ; vdivps        %ymm14,%ymm10,%ymm10
+  DB  196,67,45,74,224,240                ; vblendvps     %ymm15,%ymm8,%ymm10,%ymm12
+  DB  196,98,125,24,53,106,62,0,0         ; vbroadcastss  0x3e6a(%rip),%ymm14        # 4ab0 <_sk_callback_hsw+0x194>
+  DB  196,98,125,24,61,101,62,0,0         ; vbroadcastss  0x3e65(%rip),%ymm15        # 4ab4 <_sk_callback_hsw+0x198>
+  DB  196,65,84,89,239                    ; vmulps        %ymm15,%ymm5,%ymm13
+  DB  196,66,93,184,238                   ; vfmadd231ps   %ymm14,%ymm4,%ymm13
+  DB  196,226,125,24,5,86,62,0,0          ; vbroadcastss  0x3e56(%rip),%ymm0        # 4ab8 <_sk_callback_hsw+0x19c>
+  DB  196,98,77,184,232                   ; vfmadd231ps   %ymm0,%ymm6,%ymm13
+  DB  196,65,116,89,215                   ; vmulps        %ymm15,%ymm1,%ymm10
+  DB  196,66,53,184,214                   ; vfmadd231ps   %ymm14,%ymm9,%ymm10
+  DB  196,98,29,184,208                   ; vfmadd231ps   %ymm0,%ymm12,%ymm10
+  DB  196,66,101,170,234                  ; vfmsub213ps   %ymm10,%ymm3,%ymm13
+  DB  196,65,52,88,213                    ; vaddps        %ymm13,%ymm9,%ymm10
+  DB  196,65,116,88,221                   ; vaddps        %ymm13,%ymm1,%ymm11
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  196,193,36,93,204                   ; vminps        %ymm12,%ymm11,%ymm1
+  DB  197,44,93,233                       ; vminps        %ymm1,%ymm10,%ymm13
+  DB  196,65,36,89,207                    ; vmulps        %ymm15,%ymm11,%ymm9
+  DB  196,66,45,184,206                   ; vfmadd231ps   %ymm14,%ymm10,%ymm9
+  DB  196,98,29,184,200                   ; vfmadd231ps   %ymm0,%ymm12,%ymm9
+  DB  196,193,44,92,193                   ; vsubps        %ymm9,%ymm10,%ymm0
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  196,193,52,92,205                   ; vsubps        %ymm13,%ymm9,%ymm1
+  DB  197,252,94,193                      ; vdivps        %ymm1,%ymm0,%ymm0
+  DB  196,65,36,92,241                    ; vsubps        %ymm9,%ymm11,%ymm14
+  DB  196,65,52,89,246                    ; vmulps        %ymm14,%ymm9,%ymm14
+  DB  197,12,94,241                       ; vdivps        %ymm1,%ymm14,%ymm14
+  DB  196,65,28,92,249                    ; vsubps        %ymm9,%ymm12,%ymm15
+  DB  196,65,52,89,255                    ; vmulps        %ymm15,%ymm9,%ymm15
+  DB  197,132,94,201                      ; vdivps        %ymm1,%ymm15,%ymm1
+  DB  196,65,60,194,237,2                 ; vcmpleps      %ymm13,%ymm8,%ymm13
+  DB  196,65,52,88,246                    ; vaddps        %ymm14,%ymm9,%ymm14
+  DB  196,67,13,74,243,208                ; vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  DB  196,65,36,95,220                    ; vmaxps        %ymm12,%ymm11,%ymm11
+  DB  197,180,88,201                      ; vaddps        %ymm1,%ymm9,%ymm1
+  DB  196,195,117,74,204,208              ; vblendvps     %ymm13,%ymm12,%ymm1,%ymm1
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,194,208              ; vblendvps     %ymm13,%ymm10,%ymm0,%ymm0
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,44,95,211                    ; vmaxps        %ymm11,%ymm10,%ymm10
+  DB  196,65,124,92,217                   ; vsubps        %ymm9,%ymm0,%ymm11
+  DB  196,65,28,92,233                    ; vsubps        %ymm9,%ymm12,%ymm13
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,28,194,250,1                 ; vcmpltps      %ymm10,%ymm12,%ymm15
+  DB  196,65,44,92,209                    ; vsubps        %ymm9,%ymm10,%ymm10
+  DB  196,65,36,94,218                    ; vdivps        %ymm10,%ymm11,%ymm11
+  DB  196,65,52,88,219                    ; vaddps        %ymm11,%ymm9,%ymm11
+  DB  196,195,125,74,195,240              ; vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  DB  196,65,12,92,217                    ; vsubps        %ymm9,%ymm14,%ymm11
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,36,94,218                    ; vdivps        %ymm10,%ymm11,%ymm11
+  DB  196,65,52,88,219                    ; vaddps        %ymm11,%ymm9,%ymm11
+  DB  196,67,13,74,219,240                ; vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  DB  196,65,116,92,241                   ; vsubps        %ymm9,%ymm1,%ymm14
+  DB  196,65,20,89,238                    ; vmulps        %ymm14,%ymm13,%ymm13
+  DB  196,65,20,94,210                    ; vdivps        %ymm10,%ymm13,%ymm10
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,195,117,74,201,240              ; vblendvps     %ymm15,%ymm9,%ymm1,%ymm1
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,65,36,95,200                    ; vmaxps        %ymm8,%ymm11,%ymm9
+  DB  196,65,116,95,192                   ; vmaxps        %ymm8,%ymm1,%ymm8
+  DB  196,226,125,24,13,67,61,0,0         ; vbroadcastss  0x3d43(%rip),%ymm1        # 4abc <_sk_callback_hsw+0x1a0>
+  DB  197,116,92,215                      ; vsubps        %ymm7,%ymm1,%ymm10
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  197,116,92,219                      ; vsubps        %ymm3,%ymm1,%ymm11
+  DB  196,226,37,184,212                  ; vfmadd231ps   %ymm4,%ymm11,%ymm2
+  DB  197,236,88,192                      ; vaddps        %ymm0,%ymm2,%ymm0
+  DB  197,172,89,12,36                    ; vmulps        (%rsp),%ymm10,%ymm1
+  DB  196,226,37,184,205                  ; vfmadd231ps   %ymm5,%ymm11,%ymm1
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  197,172,89,84,36,32                 ; vmulps        0x20(%rsp),%ymm10,%ymm2
+  DB  196,98,77,168,218                   ; vfmadd213ps   %ymm2,%ymm6,%ymm11
+  DB  196,193,36,88,208                   ; vaddps        %ymm8,%ymm11,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_saturation_hsw
+_sk_saturation_hsw LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,124,40,194                      ; vmovaps       %ymm2,%ymm8
+  DB  197,252,17,12,36                    ; vmovups       %ymm1,(%rsp)
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  197,100,89,213                      ; vmulps        %ymm5,%ymm3,%ymm10
+  DB  197,100,89,222                      ; vmulps        %ymm6,%ymm3,%ymm11
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,252,17,68,36,32                 ; vmovups       %ymm0,0x20(%rsp)
+  DB  197,116,95,192                      ; vmaxps        %ymm0,%ymm1,%ymm8
+  DB  196,65,108,95,192                   ; vmaxps        %ymm8,%ymm2,%ymm8
+  DB  197,116,93,224                      ; vminps        %ymm0,%ymm1,%ymm12
+  DB  196,65,108,93,228                   ; vminps        %ymm12,%ymm2,%ymm12
+  DB  196,65,60,92,196                    ; vsubps        %ymm12,%ymm8,%ymm8
+  DB  197,60,89,231                       ; vmulps        %ymm7,%ymm8,%ymm12
+  DB  196,65,44,93,195                    ; vminps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,93,232                    ; vminps        %ymm8,%ymm9,%ymm13
+  DB  196,65,44,95,195                    ; vmaxps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,95,192                    ; vmaxps        %ymm8,%ymm9,%ymm8
+  DB  196,65,60,92,245                    ; vsubps        %ymm13,%ymm8,%ymm14
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,65,12,194,248,0                 ; vcmpeqps      %ymm8,%ymm14,%ymm15
+  DB  196,65,52,92,205                    ; vsubps        %ymm13,%ymm9,%ymm9
+  DB  196,65,28,89,201                    ; vmulps        %ymm9,%ymm12,%ymm9
+  DB  196,65,52,94,206                    ; vdivps        %ymm14,%ymm9,%ymm9
+  DB  196,67,53,74,200,240                ; vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  DB  196,65,44,92,213                    ; vsubps        %ymm13,%ymm10,%ymm10
+  DB  196,65,28,89,210                    ; vmulps        %ymm10,%ymm12,%ymm10
+  DB  196,65,44,94,214                    ; vdivps        %ymm14,%ymm10,%ymm10
+  DB  196,195,45,74,200,240               ; vblendvps     %ymm15,%ymm8,%ymm10,%ymm1
+  DB  196,65,36,92,213                    ; vsubps        %ymm13,%ymm11,%ymm10
+  DB  196,65,28,89,210                    ; vmulps        %ymm10,%ymm12,%ymm10
+  DB  196,65,44,94,214                    ; vdivps        %ymm14,%ymm10,%ymm10
+  DB  196,67,45,74,224,240                ; vblendvps     %ymm15,%ymm8,%ymm10,%ymm12
+  DB  196,98,125,24,53,84,60,0,0          ; vbroadcastss  0x3c54(%rip),%ymm14        # 4ac0 <_sk_callback_hsw+0x1a4>
+  DB  196,98,125,24,61,79,60,0,0          ; vbroadcastss  0x3c4f(%rip),%ymm15        # 4ac4 <_sk_callback_hsw+0x1a8>
+  DB  196,65,84,89,239                    ; vmulps        %ymm15,%ymm5,%ymm13
+  DB  196,66,93,184,238                   ; vfmadd231ps   %ymm14,%ymm4,%ymm13
+  DB  196,226,125,24,5,64,60,0,0          ; vbroadcastss  0x3c40(%rip),%ymm0        # 4ac8 <_sk_callback_hsw+0x1ac>
+  DB  196,98,77,184,232                   ; vfmadd231ps   %ymm0,%ymm6,%ymm13
+  DB  196,65,116,89,215                   ; vmulps        %ymm15,%ymm1,%ymm10
+  DB  196,66,53,184,214                   ; vfmadd231ps   %ymm14,%ymm9,%ymm10
+  DB  196,98,29,184,208                   ; vfmadd231ps   %ymm0,%ymm12,%ymm10
+  DB  196,66,101,170,234                  ; vfmsub213ps   %ymm10,%ymm3,%ymm13
+  DB  196,65,52,88,213                    ; vaddps        %ymm13,%ymm9,%ymm10
+  DB  196,65,116,88,221                   ; vaddps        %ymm13,%ymm1,%ymm11
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  196,193,36,93,204                   ; vminps        %ymm12,%ymm11,%ymm1
+  DB  197,44,93,233                       ; vminps        %ymm1,%ymm10,%ymm13
+  DB  196,65,36,89,207                    ; vmulps        %ymm15,%ymm11,%ymm9
+  DB  196,66,45,184,206                   ; vfmadd231ps   %ymm14,%ymm10,%ymm9
+  DB  196,98,29,184,200                   ; vfmadd231ps   %ymm0,%ymm12,%ymm9
+  DB  196,193,44,92,193                   ; vsubps        %ymm9,%ymm10,%ymm0
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  196,193,52,92,205                   ; vsubps        %ymm13,%ymm9,%ymm1
+  DB  197,252,94,193                      ; vdivps        %ymm1,%ymm0,%ymm0
+  DB  196,65,36,92,241                    ; vsubps        %ymm9,%ymm11,%ymm14
+  DB  196,65,52,89,246                    ; vmulps        %ymm14,%ymm9,%ymm14
+  DB  197,12,94,241                       ; vdivps        %ymm1,%ymm14,%ymm14
+  DB  196,65,28,92,249                    ; vsubps        %ymm9,%ymm12,%ymm15
+  DB  196,65,52,89,255                    ; vmulps        %ymm15,%ymm9,%ymm15
+  DB  197,132,94,201                      ; vdivps        %ymm1,%ymm15,%ymm1
+  DB  196,65,60,194,237,2                 ; vcmpleps      %ymm13,%ymm8,%ymm13
+  DB  196,65,52,88,246                    ; vaddps        %ymm14,%ymm9,%ymm14
+  DB  196,67,13,74,243,208                ; vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  DB  196,65,36,95,220                    ; vmaxps        %ymm12,%ymm11,%ymm11
+  DB  197,180,88,201                      ; vaddps        %ymm1,%ymm9,%ymm1
+  DB  196,195,117,74,204,208              ; vblendvps     %ymm13,%ymm12,%ymm1,%ymm1
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,194,208              ; vblendvps     %ymm13,%ymm10,%ymm0,%ymm0
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,44,95,211                    ; vmaxps        %ymm11,%ymm10,%ymm10
+  DB  196,65,124,92,217                   ; vsubps        %ymm9,%ymm0,%ymm11
+  DB  196,65,28,92,233                    ; vsubps        %ymm9,%ymm12,%ymm13
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,28,194,250,1                 ; vcmpltps      %ymm10,%ymm12,%ymm15
+  DB  196,65,44,92,209                    ; vsubps        %ymm9,%ymm10,%ymm10
+  DB  196,65,36,94,218                    ; vdivps        %ymm10,%ymm11,%ymm11
+  DB  196,65,52,88,219                    ; vaddps        %ymm11,%ymm9,%ymm11
+  DB  196,195,125,74,195,240              ; vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  DB  196,65,12,92,217                    ; vsubps        %ymm9,%ymm14,%ymm11
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,36,94,218                    ; vdivps        %ymm10,%ymm11,%ymm11
+  DB  196,65,52,88,219                    ; vaddps        %ymm11,%ymm9,%ymm11
+  DB  196,67,13,74,219,240                ; vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  DB  196,65,116,92,241                   ; vsubps        %ymm9,%ymm1,%ymm14
+  DB  196,65,20,89,238                    ; vmulps        %ymm14,%ymm13,%ymm13
+  DB  196,65,20,94,210                    ; vdivps        %ymm10,%ymm13,%ymm10
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,195,117,74,201,240              ; vblendvps     %ymm15,%ymm9,%ymm1,%ymm1
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,65,36,95,200                    ; vmaxps        %ymm8,%ymm11,%ymm9
+  DB  196,65,116,95,192                   ; vmaxps        %ymm8,%ymm1,%ymm8
+  DB  196,226,125,24,13,45,59,0,0         ; vbroadcastss  0x3b2d(%rip),%ymm1        # 4acc <_sk_callback_hsw+0x1b0>
+  DB  197,116,92,215                      ; vsubps        %ymm7,%ymm1,%ymm10
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  197,116,92,219                      ; vsubps        %ymm3,%ymm1,%ymm11
+  DB  196,226,37,184,212                  ; vfmadd231ps   %ymm4,%ymm11,%ymm2
+  DB  197,236,88,192                      ; vaddps        %ymm0,%ymm2,%ymm0
+  DB  197,172,89,12,36                    ; vmulps        (%rsp),%ymm10,%ymm1
+  DB  196,226,37,184,205                  ; vfmadd231ps   %ymm5,%ymm11,%ymm1
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  197,172,89,84,36,32                 ; vmulps        0x20(%rsp),%ymm10,%ymm2
+  DB  196,98,77,168,218                   ; vfmadd213ps   %ymm2,%ymm6,%ymm11
+  DB  196,193,36,88,208                   ; vaddps        %ymm8,%ymm11,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_color_hsw
+_sk_color_hsw LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,124,40,202                      ; vmovaps       %ymm2,%ymm9
+  DB  197,124,17,76,36,32                 ; vmovups       %ymm9,0x20(%rsp)
+  DB  197,252,17,12,36                    ; vmovups       %ymm1,(%rsp)
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,108,89,199                      ; vmulps        %ymm7,%ymm2,%ymm8
+  DB  197,116,89,215                      ; vmulps        %ymm7,%ymm1,%ymm10
+  DB  197,52,89,223                       ; vmulps        %ymm7,%ymm9,%ymm11
+  DB  196,98,125,24,45,192,58,0,0         ; vbroadcastss  0x3ac0(%rip),%ymm13        # 4ad0 <_sk_callback_hsw+0x1b4>
+  DB  196,98,125,24,53,187,58,0,0         ; vbroadcastss  0x3abb(%rip),%ymm14        # 4ad4 <_sk_callback_hsw+0x1b8>
+  DB  196,65,84,89,230                    ; vmulps        %ymm14,%ymm5,%ymm12
+  DB  196,66,93,184,229                   ; vfmadd231ps   %ymm13,%ymm4,%ymm12
+  DB  196,98,125,24,61,172,58,0,0         ; vbroadcastss  0x3aac(%rip),%ymm15        # 4ad8 <_sk_callback_hsw+0x1bc>
+  DB  196,66,77,184,231                   ; vfmadd231ps   %ymm15,%ymm6,%ymm12
+  DB  196,65,44,89,206                    ; vmulps        %ymm14,%ymm10,%ymm9
+  DB  196,66,61,184,205                   ; vfmadd231ps   %ymm13,%ymm8,%ymm9
+  DB  196,66,37,184,207                   ; vfmadd231ps   %ymm15,%ymm11,%ymm9
+  DB  196,66,101,170,225                  ; vfmsub213ps   %ymm9,%ymm3,%ymm12
+  DB  196,65,60,88,204                    ; vaddps        %ymm12,%ymm8,%ymm9
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,65,44,93,195                    ; vminps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,93,224                    ; vminps        %ymm8,%ymm9,%ymm12
+  DB  196,65,44,89,198                    ; vmulps        %ymm14,%ymm10,%ymm8
+  DB  196,66,53,184,197                   ; vfmadd231ps   %ymm13,%ymm9,%ymm8
+  DB  196,66,37,184,199                   ; vfmadd231ps   %ymm15,%ymm11,%ymm8
+  DB  196,65,52,92,232                    ; vsubps        %ymm8,%ymm9,%ymm13
+  DB  196,65,60,89,237                    ; vmulps        %ymm13,%ymm8,%ymm13
+  DB  196,65,60,92,244                    ; vsubps        %ymm12,%ymm8,%ymm14
+  DB  196,193,20,94,198                   ; vdivps        %ymm14,%ymm13,%ymm0
+  DB  196,65,44,92,248                    ; vsubps        %ymm8,%ymm10,%ymm15
+  DB  196,65,60,89,255                    ; vmulps        %ymm15,%ymm8,%ymm15
+  DB  196,65,4,94,254                     ; vdivps        %ymm14,%ymm15,%ymm15
+  DB  196,65,36,92,232                    ; vsubps        %ymm8,%ymm11,%ymm13
+  DB  196,65,60,89,237                    ; vmulps        %ymm13,%ymm8,%ymm13
+  DB  196,65,20,94,238                    ; vdivps        %ymm14,%ymm13,%ymm13
+  DB  196,65,12,87,246                    ; vxorps        %ymm14,%ymm14,%ymm14
+  DB  196,65,12,194,228,2                 ; vcmpleps      %ymm12,%ymm14,%ymm12
+  DB  196,65,60,88,255                    ; vaddps        %ymm15,%ymm8,%ymm15
+  DB  196,67,5,74,250,192                 ; vblendvps     %ymm12,%ymm10,%ymm15,%ymm15
+  DB  196,65,44,95,211                    ; vmaxps        %ymm11,%ymm10,%ymm10
+  DB  196,65,60,88,237                    ; vaddps        %ymm13,%ymm8,%ymm13
+  DB  196,67,21,74,219,192                ; vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  DB  197,188,88,192                      ; vaddps        %ymm0,%ymm8,%ymm0
+  DB  196,195,125,74,201,192              ; vblendvps     %ymm12,%ymm9,%ymm0,%ymm1
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,52,95,202                    ; vmaxps        %ymm10,%ymm9,%ymm9
+  DB  196,65,116,92,208                   ; vsubps        %ymm8,%ymm1,%ymm10
+  DB  196,65,28,92,232                    ; vsubps        %ymm8,%ymm12,%ymm13
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,193,28,194,193,1                ; vcmpltps      %ymm9,%ymm12,%ymm0
+  DB  196,65,52,92,200                    ; vsubps        %ymm8,%ymm9,%ymm9
+  DB  196,65,44,94,209                    ; vdivps        %ymm9,%ymm10,%ymm10
+  DB  196,65,60,88,210                    ; vaddps        %ymm10,%ymm8,%ymm10
+  DB  196,195,117,74,202,0                ; vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  DB  196,65,4,92,208                     ; vsubps        %ymm8,%ymm15,%ymm10
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,65,44,94,209                    ; vdivps        %ymm9,%ymm10,%ymm10
+  DB  196,65,60,88,210                    ; vaddps        %ymm10,%ymm8,%ymm10
+  DB  196,67,5,74,210,0                   ; vblendvps     %ymm0,%ymm10,%ymm15,%ymm10
+  DB  196,65,36,92,248                    ; vsubps        %ymm8,%ymm11,%ymm15
+  DB  196,65,20,89,239                    ; vmulps        %ymm15,%ymm13,%ymm13
+  DB  196,65,20,94,201                    ; vdivps        %ymm9,%ymm13,%ymm9
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,195,37,74,192,0                 ; vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  DB  196,193,116,95,206                  ; vmaxps        %ymm14,%ymm1,%ymm1
+  DB  196,65,44,95,198                    ; vmaxps        %ymm14,%ymm10,%ymm8
+  DB  196,65,124,95,206                   ; vmaxps        %ymm14,%ymm0,%ymm9
+  DB  196,226,125,24,5,142,57,0,0         ; vbroadcastss  0x398e(%rip),%ymm0        # 4adc <_sk_callback_hsw+0x1c0>
+  DB  197,124,92,215                      ; vsubps        %ymm7,%ymm0,%ymm10
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  197,124,92,219                      ; vsubps        %ymm3,%ymm0,%ymm11
+  DB  196,226,37,184,212                  ; vfmadd231ps   %ymm4,%ymm11,%ymm2
+  DB  197,236,88,193                      ; vaddps        %ymm1,%ymm2,%ymm0
+  DB  197,172,89,12,36                    ; vmulps        (%rsp),%ymm10,%ymm1
+  DB  196,226,37,184,205                  ; vfmadd231ps   %ymm5,%ymm11,%ymm1
+  DB  196,193,116,88,200                  ; vaddps        %ymm8,%ymm1,%ymm1
+  DB  197,172,89,84,36,32                 ; vmulps        0x20(%rsp),%ymm10,%ymm2
+  DB  196,98,77,168,218                   ; vfmadd213ps   %ymm2,%ymm6,%ymm11
+  DB  196,193,36,88,209                   ; vaddps        %ymm9,%ymm11,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminosity_hsw
+_sk_luminosity_hsw LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,124,40,202                      ; vmovaps       %ymm2,%ymm9
+  DB  197,124,17,12,36                    ; vmovups       %ymm9,(%rsp)
+  DB  197,252,17,76,36,32                 ; vmovups       %ymm1,0x20(%rsp)
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,100,89,196                      ; vmulps        %ymm4,%ymm3,%ymm8
+  DB  197,100,89,213                      ; vmulps        %ymm5,%ymm3,%ymm10
+  DB  197,100,89,222                      ; vmulps        %ymm6,%ymm3,%ymm11
+  DB  196,98,125,24,45,33,57,0,0          ; vbroadcastss  0x3921(%rip),%ymm13        # 4ae0 <_sk_callback_hsw+0x1c4>
+  DB  196,98,125,24,53,28,57,0,0          ; vbroadcastss  0x391c(%rip),%ymm14        # 4ae4 <_sk_callback_hsw+0x1c8>
+  DB  196,65,116,89,230                   ; vmulps        %ymm14,%ymm1,%ymm12
+  DB  196,66,109,184,229                  ; vfmadd231ps   %ymm13,%ymm2,%ymm12
+  DB  196,98,125,24,61,13,57,0,0          ; vbroadcastss  0x390d(%rip),%ymm15        # 4ae8 <_sk_callback_hsw+0x1cc>
+  DB  196,66,53,184,231                   ; vfmadd231ps   %ymm15,%ymm9,%ymm12
+  DB  196,65,44,89,206                    ; vmulps        %ymm14,%ymm10,%ymm9
+  DB  196,66,61,184,205                   ; vfmadd231ps   %ymm13,%ymm8,%ymm9
+  DB  196,66,37,184,207                   ; vfmadd231ps   %ymm15,%ymm11,%ymm9
+  DB  196,66,69,170,225                   ; vfmsub213ps   %ymm9,%ymm7,%ymm12
+  DB  196,65,60,88,204                    ; vaddps        %ymm12,%ymm8,%ymm9
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,65,44,93,195                    ; vminps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,93,224                    ; vminps        %ymm8,%ymm9,%ymm12
+  DB  196,65,44,89,198                    ; vmulps        %ymm14,%ymm10,%ymm8
+  DB  196,66,53,184,197                   ; vfmadd231ps   %ymm13,%ymm9,%ymm8
+  DB  196,66,37,184,199                   ; vfmadd231ps   %ymm15,%ymm11,%ymm8
+  DB  196,65,52,92,232                    ; vsubps        %ymm8,%ymm9,%ymm13
+  DB  196,65,60,89,237                    ; vmulps        %ymm13,%ymm8,%ymm13
+  DB  196,65,60,92,244                    ; vsubps        %ymm12,%ymm8,%ymm14
+  DB  196,193,20,94,198                   ; vdivps        %ymm14,%ymm13,%ymm0
+  DB  196,65,44,92,248                    ; vsubps        %ymm8,%ymm10,%ymm15
+  DB  196,65,60,89,255                    ; vmulps        %ymm15,%ymm8,%ymm15
+  DB  196,65,4,94,254                     ; vdivps        %ymm14,%ymm15,%ymm15
+  DB  196,65,36,92,232                    ; vsubps        %ymm8,%ymm11,%ymm13
+  DB  196,65,60,89,237                    ; vmulps        %ymm13,%ymm8,%ymm13
+  DB  196,65,20,94,238                    ; vdivps        %ymm14,%ymm13,%ymm13
+  DB  196,65,12,87,246                    ; vxorps        %ymm14,%ymm14,%ymm14
+  DB  196,65,12,194,228,2                 ; vcmpleps      %ymm12,%ymm14,%ymm12
+  DB  196,65,60,88,255                    ; vaddps        %ymm15,%ymm8,%ymm15
+  DB  196,67,5,74,250,192                 ; vblendvps     %ymm12,%ymm10,%ymm15,%ymm15
+  DB  196,65,44,95,211                    ; vmaxps        %ymm11,%ymm10,%ymm10
+  DB  196,65,60,88,237                    ; vaddps        %ymm13,%ymm8,%ymm13
+  DB  196,67,21,74,219,192                ; vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  DB  197,188,88,192                      ; vaddps        %ymm0,%ymm8,%ymm0
+  DB  196,195,125,74,201,192              ; vblendvps     %ymm12,%ymm9,%ymm0,%ymm1
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,52,95,202                    ; vmaxps        %ymm10,%ymm9,%ymm9
+  DB  196,65,116,92,208                   ; vsubps        %ymm8,%ymm1,%ymm10
+  DB  196,65,28,92,232                    ; vsubps        %ymm8,%ymm12,%ymm13
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,193,28,194,193,1                ; vcmpltps      %ymm9,%ymm12,%ymm0
+  DB  196,65,52,92,200                    ; vsubps        %ymm8,%ymm9,%ymm9
+  DB  196,65,44,94,209                    ; vdivps        %ymm9,%ymm10,%ymm10
+  DB  196,65,60,88,210                    ; vaddps        %ymm10,%ymm8,%ymm10
+  DB  196,195,117,74,202,0                ; vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  DB  196,65,4,92,208                     ; vsubps        %ymm8,%ymm15,%ymm10
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,65,44,94,209                    ; vdivps        %ymm9,%ymm10,%ymm10
+  DB  196,65,60,88,210                    ; vaddps        %ymm10,%ymm8,%ymm10
+  DB  196,67,5,74,210,0                   ; vblendvps     %ymm0,%ymm10,%ymm15,%ymm10
+  DB  196,65,36,92,248                    ; vsubps        %ymm8,%ymm11,%ymm15
+  DB  196,65,20,89,239                    ; vmulps        %ymm15,%ymm13,%ymm13
+  DB  196,65,20,94,201                    ; vdivps        %ymm9,%ymm13,%ymm9
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,195,37,74,192,0                 ; vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  DB  196,193,116,95,206                  ; vmaxps        %ymm14,%ymm1,%ymm1
+  DB  196,65,44,95,198                    ; vmaxps        %ymm14,%ymm10,%ymm8
+  DB  196,65,124,95,206                   ; vmaxps        %ymm14,%ymm0,%ymm9
+  DB  196,226,125,24,5,239,55,0,0         ; vbroadcastss  0x37ef(%rip),%ymm0        # 4aec <_sk_callback_hsw+0x1d0>
+  DB  197,124,92,215                      ; vsubps        %ymm7,%ymm0,%ymm10
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  197,124,92,219                      ; vsubps        %ymm3,%ymm0,%ymm11
+  DB  196,226,37,184,212                  ; vfmadd231ps   %ymm4,%ymm11,%ymm2
+  DB  197,236,88,193                      ; vaddps        %ymm1,%ymm2,%ymm0
+  DB  197,172,89,76,36,32                 ; vmulps        0x20(%rsp),%ymm10,%ymm1
+  DB  196,226,37,184,205                  ; vfmadd231ps   %ymm5,%ymm11,%ymm1
+  DB  196,193,116,88,200                  ; vaddps        %ymm8,%ymm1,%ymm1
+  DB  197,172,89,20,36                    ; vmulps        (%rsp),%ymm10,%ymm2
+  DB  196,98,77,168,218                   ; vfmadd213ps   %ymm2,%ymm6,%ymm11
+  DB  196,193,36,88,209                   ; vaddps        %ymm9,%ymm11,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_rgba_8888_hsw
+_sk_srcover_rgba_8888_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,180,0,0,0                    ; jne           140f <_sk_srcover_rgba_8888_hsw+0xcd>
+  DB  196,193,124,16,58                   ; vmovups       (%r10),%ymm7
+  DB  197,196,84,37,248,58,0,0            ; vandps        0x3af8(%rip),%ymm7,%ymm4        # 4e60 <_sk_callback_hsw+0x544>
+  DB  197,252,91,228                      ; vcvtdq2ps     %ymm4,%ymm4
+  DB  196,226,69,0,45,11,59,0,0           ; vpshufb       0x3b0b(%rip),%ymm7,%ymm5        # 4e80 <_sk_callback_hsw+0x564>
+  DB  197,252,91,237                      ; vcvtdq2ps     %ymm5,%ymm5
+  DB  196,226,69,0,53,30,59,0,0           ; vpshufb       0x3b1e(%rip),%ymm7,%ymm6        # 4ea0 <_sk_callback_hsw+0x584>
+  DB  197,252,91,246                      ; vcvtdq2ps     %ymm6,%ymm6
+  DB  197,197,114,215,24                  ; vpsrld        $0x18,%ymm7,%ymm7
+  DB  197,252,91,255                      ; vcvtdq2ps     %ymm7,%ymm7
+  DB  196,98,125,24,5,88,55,0,0           ; vbroadcastss  0x3758(%rip),%ymm8        # 4af0 <_sk_callback_hsw+0x1d4>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,98,125,24,13,79,55,0,0          ; vbroadcastss  0x374f(%rip),%ymm9        # 4af4 <_sk_callback_hsw+0x1d8>
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  196,194,93,184,192                  ; vfmadd231ps   %ymm8,%ymm4,%ymm0
+  DB  196,193,116,89,201                  ; vmulps        %ymm9,%ymm1,%ymm1
+  DB  196,194,85,184,200                  ; vfmadd231ps   %ymm8,%ymm5,%ymm1
+  DB  196,193,108,89,209                  ; vmulps        %ymm9,%ymm2,%ymm2
+  DB  196,194,77,184,208                  ; vfmadd231ps   %ymm8,%ymm6,%ymm2
+  DB  196,193,100,89,217                  ; vmulps        %ymm9,%ymm3,%ymm3
+  DB  196,194,69,184,216                  ; vfmadd231ps   %ymm8,%ymm7,%ymm3
+  DB  197,125,91,192                      ; vcvtps2dq     %ymm0,%ymm8
+  DB  197,125,91,201                      ; vcvtps2dq     %ymm1,%ymm9
+  DB  196,193,53,114,241,8                ; vpslld        $0x8,%ymm9,%ymm9
+  DB  196,65,53,235,192                   ; vpor          %ymm8,%ymm9,%ymm8
+  DB  197,125,91,202                      ; vcvtps2dq     %ymm2,%ymm9
+  DB  196,193,53,114,241,16               ; vpslld        $0x10,%ymm9,%ymm9
+  DB  197,125,91,211                      ; vcvtps2dq     %ymm3,%ymm10
+  DB  196,193,45,114,242,24               ; vpslld        $0x18,%ymm10,%ymm10
+  DB  196,65,53,235,202                   ; vpor          %ymm10,%ymm9,%ymm9
+  DB  196,65,61,235,193                   ; vpor          %ymm9,%ymm8,%ymm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,53                              ; jne           1438 <_sk_srcover_rgba_8888_hsw+0xf6>
+  DB  196,65,124,17,2                     ; vmovups       %ymm8,(%r10)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,225,249,110,224                 ; vmovq         %rax,%xmm4
+  DB  196,226,125,33,228                  ; vpmovsxbd     %xmm4,%ymm4
+  DB  196,194,93,44,58                    ; vmaskmovps    (%r10),%ymm4,%ymm7
+  DB  233,40,255,255,255                  ; jmpq          1360 <_sk_srcover_rgba_8888_hsw+0x1e>
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,97,249,110,200                  ; vmovq         %rax,%xmm9
+  DB  196,66,125,33,201                   ; vpmovsxbd     %xmm9,%ymm9
+  DB  196,66,53,46,2                      ; vmaskmovps    %ymm8,%ymm9,(%r10)
+  DB  235,170                             ; jmp           1408 <_sk_srcover_rgba_8888_hsw+0xc6>
+
+PUBLIC _sk_clamp_0_hsw
+_sk_clamp_0_hsw LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,193,116,95,200                  ; vmaxps        %ymm8,%ymm1,%ymm1
+  DB  196,193,108,95,208                  ; vmaxps        %ymm8,%ymm2,%ymm2
+  DB  196,193,100,95,216                  ; vmaxps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_1_hsw
+_sk_clamp_1_hsw LABEL PROC
+  DB  196,98,125,24,5,116,54,0,0          ; vbroadcastss  0x3674(%rip),%ymm8        # 4af8 <_sk_callback_hsw+0x1dc>
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  196,193,116,93,200                  ; vminps        %ymm8,%ymm1,%ymm1
+  DB  196,193,108,93,208                  ; vminps        %ymm8,%ymm2,%ymm2
+  DB  196,193,100,93,216                  ; vminps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_a_hsw
+_sk_clamp_a_hsw LABEL PROC
+  DB  196,98,125,24,5,87,54,0,0           ; vbroadcastss  0x3657(%rip),%ymm8        # 4afc <_sk_callback_hsw+0x1e0>
+  DB  196,193,100,93,216                  ; vminps        %ymm8,%ymm3,%ymm3
+  DB  197,252,93,195                      ; vminps        %ymm3,%ymm0,%ymm0
+  DB  197,244,93,203                      ; vminps        %ymm3,%ymm1,%ymm1
+  DB  197,236,93,211                      ; vminps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_set_rgb_hsw
+_sk_set_rgb_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,0                    ; vbroadcastss  (%rax),%ymm0
+  DB  196,226,125,24,72,4                 ; vbroadcastss  0x4(%rax),%ymm1
+  DB  196,226,125,24,80,8                 ; vbroadcastss  0x8(%rax),%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_rb_hsw
+_sk_swap_rb_hsw LABEL PROC
+  DB  197,124,40,192                      ; vmovaps       %ymm0,%ymm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,194                      ; vmovaps       %ymm2,%ymm0
+  DB  197,124,41,194                      ; vmovaps       %ymm8,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_hsw
+_sk_swap_hsw LABEL PROC
+  DB  197,124,40,195                      ; vmovaps       %ymm3,%ymm8
+  DB  197,124,40,202                      ; vmovaps       %ymm2,%ymm9
+  DB  197,124,40,209                      ; vmovaps       %ymm1,%ymm10
+  DB  197,124,40,216                      ; vmovaps       %ymm0,%ymm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,196                      ; vmovaps       %ymm4,%ymm0
+  DB  197,252,40,205                      ; vmovaps       %ymm5,%ymm1
+  DB  197,252,40,214                      ; vmovaps       %ymm6,%ymm2
+  DB  197,252,40,223                      ; vmovaps       %ymm7,%ymm3
+  DB  197,124,41,220                      ; vmovaps       %ymm11,%ymm4
+  DB  197,124,41,213                      ; vmovaps       %ymm10,%ymm5
+  DB  197,124,41,206                      ; vmovaps       %ymm9,%ymm6
+  DB  197,124,41,199                      ; vmovaps       %ymm8,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_src_dst_hsw
+_sk_move_src_dst_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,224                      ; vmovaps       %ymm0,%ymm4
+  DB  197,252,40,233                      ; vmovaps       %ymm1,%ymm5
+  DB  197,252,40,242                      ; vmovaps       %ymm2,%ymm6
+  DB  197,252,40,251                      ; vmovaps       %ymm3,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_dst_src_hsw
+_sk_move_dst_src_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,196                      ; vmovaps       %ymm4,%ymm0
+  DB  197,252,40,205                      ; vmovaps       %ymm5,%ymm1
+  DB  197,252,40,214                      ; vmovaps       %ymm6,%ymm2
+  DB  197,252,40,223                      ; vmovaps       %ymm7,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_premul_hsw
+_sk_premul_hsw LABEL PROC
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  197,236,89,211                      ; vmulps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_unpremul_hsw
+_sk_unpremul_hsw LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,65,100,194,200,0                ; vcmpeqps      %ymm8,%ymm3,%ymm9
+  DB  196,98,125,24,21,159,53,0,0         ; vbroadcastss  0x359f(%rip),%ymm10        # 4b00 <_sk_callback_hsw+0x1e4>
+  DB  197,44,94,211                       ; vdivps        %ymm3,%ymm10,%ymm10
+  DB  196,67,45,74,192,144                ; vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_from_srgb_hsw
+_sk_from_srgb_hsw LABEL PROC
+  DB  196,98,125,24,5,128,53,0,0          ; vbroadcastss  0x3580(%rip),%ymm8        # 4b04 <_sk_callback_hsw+0x1e8>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  197,124,89,208                      ; vmulps        %ymm0,%ymm0,%ymm10
+  DB  196,98,125,24,29,114,53,0,0         ; vbroadcastss  0x3572(%rip),%ymm11        # 4b08 <_sk_callback_hsw+0x1ec>
+  DB  196,98,125,24,37,109,53,0,0         ; vbroadcastss  0x356d(%rip),%ymm12        # 4b0c <_sk_callback_hsw+0x1f0>
+  DB  196,65,124,40,236                   ; vmovaps       %ymm12,%ymm13
+  DB  196,66,125,168,235                  ; vfmadd213ps   %ymm11,%ymm0,%ymm13
+  DB  196,98,125,24,53,94,53,0,0          ; vbroadcastss  0x355e(%rip),%ymm14        # 4b10 <_sk_callback_hsw+0x1f4>
+  DB  196,66,45,168,238                   ; vfmadd213ps   %ymm14,%ymm10,%ymm13
+  DB  196,98,125,24,21,84,53,0,0          ; vbroadcastss  0x3554(%rip),%ymm10        # 4b14 <_sk_callback_hsw+0x1f8>
+  DB  196,193,124,194,194,1               ; vcmpltps      %ymm10,%ymm0,%ymm0
+  DB  196,195,21,74,193,0                 ; vblendvps     %ymm0,%ymm9,%ymm13,%ymm0
+  DB  196,65,116,89,200                   ; vmulps        %ymm8,%ymm1,%ymm9
+  DB  197,116,89,233                      ; vmulps        %ymm1,%ymm1,%ymm13
+  DB  196,65,124,40,252                   ; vmovaps       %ymm12,%ymm15
+  DB  196,66,117,168,251                  ; vfmadd213ps   %ymm11,%ymm1,%ymm15
+  DB  196,66,21,168,254                   ; vfmadd213ps   %ymm14,%ymm13,%ymm15
+  DB  196,193,116,194,202,1               ; vcmpltps      %ymm10,%ymm1,%ymm1
+  DB  196,195,5,74,201,16                 ; vblendvps     %ymm1,%ymm9,%ymm15,%ymm1
+  DB  196,65,108,89,192                   ; vmulps        %ymm8,%ymm2,%ymm8
+  DB  197,108,89,202                      ; vmulps        %ymm2,%ymm2,%ymm9
+  DB  196,66,109,168,227                  ; vfmadd213ps   %ymm11,%ymm2,%ymm12
+  DB  196,66,53,168,230                   ; vfmadd213ps   %ymm14,%ymm9,%ymm12
+  DB  196,193,108,194,210,1               ; vcmpltps      %ymm10,%ymm2,%ymm2
+  DB  196,195,29,74,208,32                ; vblendvps     %ymm2,%ymm8,%ymm12,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_to_srgb_hsw
+_sk_to_srgb_hsw LABEL PROC
+  DB  197,124,82,200                      ; vrsqrtps      %ymm0,%ymm9
+  DB  196,98,125,24,5,248,52,0,0          ; vbroadcastss  0x34f8(%rip),%ymm8        # 4b18 <_sk_callback_hsw+0x1fc>
+  DB  196,65,124,89,208                   ; vmulps        %ymm8,%ymm0,%ymm10
+  DB  196,98,125,24,29,238,52,0,0         ; vbroadcastss  0x34ee(%rip),%ymm11        # 4b1c <_sk_callback_hsw+0x200>
+  DB  196,98,125,24,37,233,52,0,0         ; vbroadcastss  0x34e9(%rip),%ymm12        # 4b20 <_sk_callback_hsw+0x204>
+  DB  196,65,124,40,236                   ; vmovaps       %ymm12,%ymm13
+  DB  196,66,53,168,235                   ; vfmadd213ps   %ymm11,%ymm9,%ymm13
+  DB  196,98,125,24,53,218,52,0,0         ; vbroadcastss  0x34da(%rip),%ymm14        # 4b24 <_sk_callback_hsw+0x208>
+  DB  196,66,53,168,238                   ; vfmadd213ps   %ymm14,%ymm9,%ymm13
+  DB  196,98,125,24,61,208,52,0,0         ; vbroadcastss  0x34d0(%rip),%ymm15        # 4b28 <_sk_callback_hsw+0x20c>
+  DB  196,65,52,88,207                    ; vaddps        %ymm15,%ymm9,%ymm9
+  DB  196,65,124,83,201                   ; vrcpps        %ymm9,%ymm9
+  DB  196,65,20,89,201                    ; vmulps        %ymm9,%ymm13,%ymm9
+  DB  196,98,125,24,45,188,52,0,0         ; vbroadcastss  0x34bc(%rip),%ymm13        # 4b2c <_sk_callback_hsw+0x210>
+  DB  196,193,124,194,197,1               ; vcmpltps      %ymm13,%ymm0,%ymm0
+  DB  196,195,53,74,194,0                 ; vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  DB  197,124,82,201                      ; vrsqrtps      %ymm1,%ymm9
+  DB  196,65,124,40,212                   ; vmovaps       %ymm12,%ymm10
+  DB  196,66,53,168,211                   ; vfmadd213ps   %ymm11,%ymm9,%ymm10
+  DB  196,66,53,168,214                   ; vfmadd213ps   %ymm14,%ymm9,%ymm10
+  DB  196,65,52,88,207                    ; vaddps        %ymm15,%ymm9,%ymm9
+  DB  196,65,124,83,201                   ; vrcpps        %ymm9,%ymm9
+  DB  196,65,44,89,201                    ; vmulps        %ymm9,%ymm10,%ymm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,193,116,194,205,1               ; vcmpltps      %ymm13,%ymm1,%ymm1
+  DB  196,195,53,74,202,16                ; vblendvps     %ymm1,%ymm10,%ymm9,%ymm1
+  DB  197,124,82,202                      ; vrsqrtps      %ymm2,%ymm9
+  DB  196,66,53,168,227                   ; vfmadd213ps   %ymm11,%ymm9,%ymm12
+  DB  196,66,53,168,230                   ; vfmadd213ps   %ymm14,%ymm9,%ymm12
+  DB  196,65,52,88,207                    ; vaddps        %ymm15,%ymm9,%ymm9
+  DB  196,65,124,83,201                   ; vrcpps        %ymm9,%ymm9
+  DB  196,65,28,89,201                    ; vmulps        %ymm9,%ymm12,%ymm9
+  DB  196,65,108,89,192                   ; vmulps        %ymm8,%ymm2,%ymm8
+  DB  196,193,108,194,213,1               ; vcmpltps      %ymm13,%ymm2,%ymm2
+  DB  196,195,53,74,208,32                ; vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_rgb_to_hsl_hsw
+_sk_rgb_to_hsl_hsw LABEL PROC
+  DB  197,124,95,193                      ; vmaxps        %ymm1,%ymm0,%ymm8
+  DB  197,60,95,194                       ; vmaxps        %ymm2,%ymm8,%ymm8
+  DB  197,124,93,201                      ; vminps        %ymm1,%ymm0,%ymm9
+  DB  197,52,93,202                       ; vminps        %ymm2,%ymm9,%ymm9
+  DB  196,65,60,92,209                    ; vsubps        %ymm9,%ymm8,%ymm10
+  DB  196,98,125,24,29,49,52,0,0          ; vbroadcastss  0x3431(%rip),%ymm11        # 4b30 <_sk_callback_hsw+0x214>
+  DB  196,65,36,94,218                    ; vdivps        %ymm10,%ymm11,%ymm11
+  DB  197,116,92,226                      ; vsubps        %ymm2,%ymm1,%ymm12
+  DB  197,116,194,234,1                   ; vcmpltps      %ymm2,%ymm1,%ymm13
+  DB  196,98,125,24,53,30,52,0,0          ; vbroadcastss  0x341e(%rip),%ymm14        # 4b34 <_sk_callback_hsw+0x218>
+  DB  196,65,4,87,255                     ; vxorps        %ymm15,%ymm15,%ymm15
+  DB  196,67,5,74,238,208                 ; vblendvps     %ymm13,%ymm14,%ymm15,%ymm13
+  DB  196,66,37,168,229                   ; vfmadd213ps   %ymm13,%ymm11,%ymm12
+  DB  197,236,92,208                      ; vsubps        %ymm0,%ymm2,%ymm2
+  DB  197,124,92,233                      ; vsubps        %ymm1,%ymm0,%ymm13
+  DB  196,98,125,24,53,5,52,0,0           ; vbroadcastss  0x3405(%rip),%ymm14        # 4b3c <_sk_callback_hsw+0x220>
+  DB  196,66,37,168,238                   ; vfmadd213ps   %ymm14,%ymm11,%ymm13
+  DB  196,98,125,24,53,243,51,0,0         ; vbroadcastss  0x33f3(%rip),%ymm14        # 4b38 <_sk_callback_hsw+0x21c>
+  DB  196,194,37,168,214                  ; vfmadd213ps   %ymm14,%ymm11,%ymm2
+  DB  197,188,194,201,0                   ; vcmpeqps      %ymm1,%ymm8,%ymm1
+  DB  196,227,21,74,202,16                ; vblendvps     %ymm1,%ymm2,%ymm13,%ymm1
+  DB  197,188,194,192,0                   ; vcmpeqps      %ymm0,%ymm8,%ymm0
+  DB  196,195,117,74,196,0                ; vblendvps     %ymm0,%ymm12,%ymm1,%ymm0
+  DB  196,193,60,88,201                   ; vaddps        %ymm9,%ymm8,%ymm1
+  DB  196,98,125,24,29,214,51,0,0         ; vbroadcastss  0x33d6(%rip),%ymm11        # 4b44 <_sk_callback_hsw+0x228>
+  DB  196,193,116,89,211                  ; vmulps        %ymm11,%ymm1,%ymm2
+  DB  197,36,194,218,1                    ; vcmpltps      %ymm2,%ymm11,%ymm11
+  DB  196,65,12,92,224                    ; vsubps        %ymm8,%ymm14,%ymm12
+  DB  196,65,28,92,225                    ; vsubps        %ymm9,%ymm12,%ymm12
+  DB  196,195,117,74,204,176              ; vblendvps     %ymm11,%ymm12,%ymm1,%ymm1
+  DB  196,65,60,194,193,0                 ; vcmpeqps      %ymm9,%ymm8,%ymm8
+  DB  197,172,94,201                      ; vdivps        %ymm1,%ymm10,%ymm1
+  DB  196,195,125,74,199,128              ; vblendvps     %ymm8,%ymm15,%ymm0,%ymm0
+  DB  196,195,117,74,207,128              ; vblendvps     %ymm8,%ymm15,%ymm1,%ymm1
+  DB  196,98,125,24,5,153,51,0,0          ; vbroadcastss  0x3399(%rip),%ymm8        # 4b40 <_sk_callback_hsw+0x224>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hsl_to_rgb_hsw
+_sk_hsl_to_rgb_hsw LABEL PROC
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  197,252,17,188,36,128,0,0,0         ; vmovups       %ymm7,0x80(%rsp)
+  DB  197,252,17,116,36,96                ; vmovups       %ymm6,0x60(%rsp)
+  DB  197,252,17,108,36,64                ; vmovups       %ymm5,0x40(%rsp)
+  DB  197,252,17,100,36,32                ; vmovups       %ymm4,0x20(%rsp)
+  DB  197,252,17,28,36                    ; vmovups       %ymm3,(%rsp)
+  DB  197,252,40,233                      ; vmovaps       %ymm1,%ymm5
+  DB  197,252,40,224                      ; vmovaps       %ymm0,%ymm4
+  DB  196,98,125,24,5,96,51,0,0           ; vbroadcastss  0x3360(%rip),%ymm8        # 4b48 <_sk_callback_hsw+0x22c>
+  DB  197,60,194,202,2                    ; vcmpleps      %ymm2,%ymm8,%ymm9
+  DB  197,84,89,210                       ; vmulps        %ymm2,%ymm5,%ymm10
+  DB  196,65,84,92,218                    ; vsubps        %ymm10,%ymm5,%ymm11
+  DB  196,67,45,74,203,144                ; vblendvps     %ymm9,%ymm11,%ymm10,%ymm9
+  DB  197,52,88,210                       ; vaddps        %ymm2,%ymm9,%ymm10
+  DB  196,98,125,24,13,67,51,0,0          ; vbroadcastss  0x3343(%rip),%ymm9        # 4b4c <_sk_callback_hsw+0x230>
+  DB  196,66,109,170,202                  ; vfmsub213ps   %ymm10,%ymm2,%ymm9
+  DB  196,98,125,24,29,57,51,0,0          ; vbroadcastss  0x3339(%rip),%ymm11        # 4b50 <_sk_callback_hsw+0x234>
+  DB  196,65,92,88,219                    ; vaddps        %ymm11,%ymm4,%ymm11
+  DB  196,67,125,8,227,1                  ; vroundps      $0x1,%ymm11,%ymm12
+  DB  196,65,36,92,252                    ; vsubps        %ymm12,%ymm11,%ymm15
+  DB  196,65,44,92,217                    ; vsubps        %ymm9,%ymm10,%ymm11
+  DB  196,98,125,24,45,35,51,0,0          ; vbroadcastss  0x3323(%rip),%ymm13        # 4b58 <_sk_callback_hsw+0x23c>
+  DB  196,193,4,89,197                    ; vmulps        %ymm13,%ymm15,%ymm0
+  DB  196,98,125,24,53,25,51,0,0          ; vbroadcastss  0x3319(%rip),%ymm14        # 4b5c <_sk_callback_hsw+0x240>
+  DB  197,12,92,224                       ; vsubps        %ymm0,%ymm14,%ymm12
+  DB  196,66,37,168,225                   ; vfmadd213ps   %ymm9,%ymm11,%ymm12
+  DB  196,226,125,24,29,255,50,0,0        ; vbroadcastss  0x32ff(%rip),%ymm3        # 4b54 <_sk_callback_hsw+0x238>
+  DB  196,193,100,194,255,2               ; vcmpleps      %ymm15,%ymm3,%ymm7
+  DB  196,195,29,74,249,112               ; vblendvps     %ymm7,%ymm9,%ymm12,%ymm7
+  DB  196,65,60,194,231,2                 ; vcmpleps      %ymm15,%ymm8,%ymm12
+  DB  196,227,45,74,255,192               ; vblendvps     %ymm12,%ymm7,%ymm10,%ymm7
+  DB  196,98,125,24,37,234,50,0,0         ; vbroadcastss  0x32ea(%rip),%ymm12        # 4b60 <_sk_callback_hsw+0x244>
+  DB  196,65,28,194,255,2                 ; vcmpleps      %ymm15,%ymm12,%ymm15
+  DB  196,194,37,168,193                  ; vfmadd213ps   %ymm9,%ymm11,%ymm0
+  DB  196,99,125,74,255,240               ; vblendvps     %ymm15,%ymm7,%ymm0,%ymm15
+  DB  196,227,125,8,196,1                 ; vroundps      $0x1,%ymm4,%ymm0
+  DB  197,220,92,192                      ; vsubps        %ymm0,%ymm4,%ymm0
+  DB  196,193,124,89,253                  ; vmulps        %ymm13,%ymm0,%ymm7
+  DB  197,140,92,207                      ; vsubps        %ymm7,%ymm14,%ymm1
+  DB  196,194,37,168,201                  ; vfmadd213ps   %ymm9,%ymm11,%ymm1
+  DB  197,228,194,240,2                   ; vcmpleps      %ymm0,%ymm3,%ymm6
+  DB  196,195,117,74,201,96               ; vblendvps     %ymm6,%ymm9,%ymm1,%ymm1
+  DB  197,188,194,240,2                   ; vcmpleps      %ymm0,%ymm8,%ymm6
+  DB  196,227,45,74,201,96                ; vblendvps     %ymm6,%ymm1,%ymm10,%ymm1
+  DB  197,156,194,192,2                   ; vcmpleps      %ymm0,%ymm12,%ymm0
+  DB  196,194,37,168,249                  ; vfmadd213ps   %ymm9,%ymm11,%ymm7
+  DB  196,227,69,74,201,0                 ; vblendvps     %ymm0,%ymm1,%ymm7,%ymm1
+  DB  196,226,125,24,5,150,50,0,0         ; vbroadcastss  0x3296(%rip),%ymm0        # 4b64 <_sk_callback_hsw+0x248>
+  DB  197,220,88,192                      ; vaddps        %ymm0,%ymm4,%ymm0
+  DB  196,227,125,8,224,1                 ; vroundps      $0x1,%ymm0,%ymm4
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  197,228,194,216,2                   ; vcmpleps      %ymm0,%ymm3,%ymm3
+  DB  196,193,124,89,229                  ; vmulps        %ymm13,%ymm0,%ymm4
+  DB  197,140,92,244                      ; vsubps        %ymm4,%ymm14,%ymm6
+  DB  196,194,37,168,241                  ; vfmadd213ps   %ymm9,%ymm11,%ymm6
+  DB  196,195,77,74,217,48                ; vblendvps     %ymm3,%ymm9,%ymm6,%ymm3
+  DB  197,188,194,240,2                   ; vcmpleps      %ymm0,%ymm8,%ymm6
+  DB  196,227,45,74,219,96                ; vblendvps     %ymm6,%ymm3,%ymm10,%ymm3
+  DB  196,98,37,184,204                   ; vfmadd231ps   %ymm4,%ymm11,%ymm9
+  DB  197,156,194,192,2                   ; vcmpleps      %ymm0,%ymm12,%ymm0
+  DB  196,227,53,74,219,0                 ; vblendvps     %ymm0,%ymm3,%ymm9,%ymm3
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,212,194,224,0                   ; vcmpeqps      %ymm0,%ymm5,%ymm4
+  DB  196,227,5,74,194,64                 ; vblendvps     %ymm4,%ymm2,%ymm15,%ymm0
+  DB  196,227,117,74,202,64               ; vblendvps     %ymm4,%ymm2,%ymm1,%ymm1
+  DB  196,227,101,74,210,64               ; vblendvps     %ymm4,%ymm2,%ymm3,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,28,36                    ; vmovups       (%rsp),%ymm3
+  DB  197,252,16,100,36,32                ; vmovups       0x20(%rsp),%ymm4
+  DB  197,252,16,108,36,64                ; vmovups       0x40(%rsp),%ymm5
+  DB  197,252,16,116,36,96                ; vmovups       0x60(%rsp),%ymm6
+  DB  197,252,16,188,36,128,0,0,0         ; vmovups       0x80(%rsp),%ymm7
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_1_float_hsw
+_sk_scale_1_float_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_u8_hsw
+_sk_scale_u8_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,51                              ; jne           19b4 <_sk_scale_u8_hsw+0x43>
+  DB  197,122,126,0                       ; vmovq         (%rax),%xmm8
+  DB  196,66,125,49,192                   ; vpmovzxbd     %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,13,208,49,0,0         ; vbroadcastss  0x31d0(%rip),%ymm9        # 4b68 <_sk_callback_hsw+0x24c>
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           19bd <_sk_scale_u8_hsw+0x4c>
+  DB  196,65,249,110,194                  ; vmovq         %r10,%xmm8
+  DB  91                                  ; pop           %rbx
+  DB  235,171                             ; jmp           1985 <_sk_scale_u8_hsw+0x14>
+
+PUBLIC _sk_lerp_1_float_hsw
+_sk_lerp_1_float_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  196,226,61,168,196                  ; vfmadd213ps   %ymm4,%ymm8,%ymm0
+  DB  197,244,92,205                      ; vsubps        %ymm5,%ymm1,%ymm1
+  DB  196,226,61,168,205                  ; vfmadd213ps   %ymm5,%ymm8,%ymm1
+  DB  197,236,92,214                      ; vsubps        %ymm6,%ymm2,%ymm2
+  DB  196,226,61,168,214                  ; vfmadd213ps   %ymm6,%ymm8,%ymm2
+  DB  197,228,92,223                      ; vsubps        %ymm7,%ymm3,%ymm3
+  DB  196,226,61,168,223                  ; vfmadd213ps   %ymm7,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lerp_u8_hsw
+_sk_lerp_u8_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,71                              ; jne           1a60 <_sk_lerp_u8_hsw+0x57>
+  DB  197,122,126,0                       ; vmovq         (%rax),%xmm8
+  DB  196,66,125,49,192                   ; vpmovzxbd     %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,13,60,49,0,0          ; vbroadcastss  0x313c(%rip),%ymm9        # 4b6c <_sk_callback_hsw+0x250>
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  196,226,61,168,196                  ; vfmadd213ps   %ymm4,%ymm8,%ymm0
+  DB  197,244,92,205                      ; vsubps        %ymm5,%ymm1,%ymm1
+  DB  196,226,61,168,205                  ; vfmadd213ps   %ymm5,%ymm8,%ymm1
+  DB  197,236,92,214                      ; vsubps        %ymm6,%ymm2,%ymm2
+  DB  196,226,61,168,214                  ; vfmadd213ps   %ymm6,%ymm8,%ymm2
+  DB  197,228,92,223                      ; vsubps        %ymm7,%ymm3,%ymm3
+  DB  196,226,61,168,223                  ; vfmadd213ps   %ymm7,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           1a69 <_sk_lerp_u8_hsw+0x60>
+  DB  196,65,249,110,194                  ; vmovq         %r10,%xmm8
+  DB  91                                  ; pop           %rbx
+  DB  235,151                             ; jmp           1a1d <_sk_lerp_u8_hsw+0x14>
+
+PUBLIC _sk_lerp_565_hsw
+_sk_lerp_565_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,169,0,0,0                    ; jne           1b3d <_sk_lerp_565_hsw+0xb7>
+  DB  196,65,122,111,4,83                 ; vmovdqu       (%r11,%rdx,2),%xmm8
+  DB  196,66,125,51,192                   ; vpmovzxwd     %xmm8,%ymm8
+  DB  196,98,125,88,13,200,48,0,0         ; vpbroadcastd  0x30c8(%rip),%ymm9        # 4b70 <_sk_callback_hsw+0x254>
+  DB  196,65,61,219,201                   ; vpand         %ymm9,%ymm8,%ymm9
+  DB  196,65,124,91,201                   ; vcvtdq2ps     %ymm9,%ymm9
+  DB  196,98,125,24,21,185,48,0,0         ; vbroadcastss  0x30b9(%rip),%ymm10        # 4b74 <_sk_callback_hsw+0x258>
+  DB  196,65,52,89,202                    ; vmulps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,88,21,175,48,0,0         ; vpbroadcastd  0x30af(%rip),%ymm10        # 4b78 <_sk_callback_hsw+0x25c>
+  DB  196,65,61,219,210                   ; vpand         %ymm10,%ymm8,%ymm10
+  DB  196,65,124,91,210                   ; vcvtdq2ps     %ymm10,%ymm10
+  DB  196,98,125,24,29,160,48,0,0         ; vbroadcastss  0x30a0(%rip),%ymm11        # 4b7c <_sk_callback_hsw+0x260>
+  DB  196,65,44,89,211                    ; vmulps        %ymm11,%ymm10,%ymm10
+  DB  196,98,125,88,29,150,48,0,0         ; vpbroadcastd  0x3096(%rip),%ymm11        # 4b80 <_sk_callback_hsw+0x264>
+  DB  196,65,61,219,195                   ; vpand         %ymm11,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,29,135,48,0,0         ; vbroadcastss  0x3087(%rip),%ymm11        # 4b84 <_sk_callback_hsw+0x268>
+  DB  196,65,60,89,195                    ; vmulps        %ymm11,%ymm8,%ymm8
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  196,226,53,168,196                  ; vfmadd213ps   %ymm4,%ymm9,%ymm0
+  DB  197,244,92,205                      ; vsubps        %ymm5,%ymm1,%ymm1
+  DB  196,226,45,168,205                  ; vfmadd213ps   %ymm5,%ymm10,%ymm1
+  DB  197,236,92,214                      ; vsubps        %ymm6,%ymm2,%ymm2
+  DB  196,226,61,168,214                  ; vfmadd213ps   %ymm6,%ymm8,%ymm2
+  DB  197,228,92,223                      ; vsubps        %ymm7,%ymm3,%ymm3
+  DB  196,98,101,168,207                  ; vfmadd213ps   %ymm7,%ymm3,%ymm9
+  DB  196,98,101,168,215                  ; vfmadd213ps   %ymm7,%ymm3,%ymm10
+  DB  196,98,101,168,199                  ; vfmadd213ps   %ymm7,%ymm3,%ymm8
+  DB  196,193,44,95,216                   ; vmaxps        %ymm8,%ymm10,%ymm3
+  DB  197,180,95,219                      ; vmaxps        %ymm3,%ymm9,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  196,65,57,239,192                   ; vpxor         %xmm8,%xmm8,%xmm8
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,68,255,255,255               ; ja            1a9a <_sk_lerp_565_hsw+0x14>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,75,0,0,0                  ; lea           0x4b(%rip),%r10        # 1bac <_sk_lerp_565_hsw+0x126>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,57,239,192                   ; vpxor         %xmm8,%xmm8,%xmm8
+  DB  196,65,57,196,68,83,12,6            ; vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,10,5            ; vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,8,4             ; vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,6,3             ; vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,4,2             ; vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,2,1             ; vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,4,83,0                ; vpinsrw       $0x0,(%r11,%rdx,2),%xmm8,%xmm8
+  DB  233,239,254,255,255                 ; jmpq          1a9a <_sk_lerp_565_hsw+0x14>
+  DB  144                                 ; nop
+  DB  243,255                             ; repz          (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  235,255                             ; jmp           1bb1 <_sk_lerp_565_hsw+0x12b>
+  DB  255                                 ; (bad)
+  DB  255,227                             ; jmpq          *%rbx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  219,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,211                             ; callq         *%rbx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,203                             ; dec           %ebx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  190                                 ; .byte         0xbe
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_tables_hsw
+_sk_load_tables_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,105                             ; jne           1c46 <_sk_load_tables_hsw+0x7e>
+  DB  196,193,124,16,26                   ; vmovups       (%r10),%ymm3
+  DB  197,228,84,13,214,50,0,0            ; vandps        0x32d6(%rip),%ymm3,%ymm1        # 4ec0 <_sk_callback_hsw+0x5a4>
+  DB  196,65,61,118,192                   ; vpcmpeqd      %ymm8,%ymm8,%ymm8
+  DB  72,139,72,8                         ; mov           0x8(%rax),%rcx
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  197,237,118,210                     ; vpcmpeqd      %ymm2,%ymm2,%ymm2
+  DB  196,226,109,146,4,137               ; vgatherdps    %ymm2,(%rcx,%ymm1,4),%ymm0
+  DB  196,226,101,0,21,214,50,0,0         ; vpshufb       0x32d6(%rip),%ymm3,%ymm2        # 4ee0 <_sk_callback_hsw+0x5c4>
+  DB  196,65,53,118,201                   ; vpcmpeqd      %ymm9,%ymm9,%ymm9
+  DB  196,194,53,146,12,146               ; vgatherdps    %ymm9,(%r10,%ymm2,4),%ymm1
+  DB  72,139,64,24                        ; mov           0x18(%rax),%rax
+  DB  196,98,101,0,13,222,50,0,0          ; vpshufb       0x32de(%rip),%ymm3,%ymm9        # 4f00 <_sk_callback_hsw+0x5e4>
+  DB  196,162,61,146,20,136               ; vgatherdps    %ymm8,(%rax,%ymm9,4),%ymm2
+  DB  197,229,114,211,24                  ; vpsrld        $0x18,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,98,125,24,5,78,47,0,0           ; vbroadcastss  0x2f4e(%rip),%ymm8        # 4b88 <_sk_callback_hsw+0x26c>
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  73,199,195,255,255,255,255          ; mov           $0xffffffffffffffff,%r11
+  DB  73,211,235                          ; shr           %cl,%r11
+  DB  196,193,249,110,195                 ; vmovq         %r11,%xmm0
+  DB  196,226,125,33,192                  ; vpmovsxbd     %xmm0,%ymm0
+  DB  196,194,125,44,26                   ; vmaskmovps    (%r10),%ymm0,%ymm3
+  DB  233,115,255,255,255                 ; jmpq          1be2 <_sk_load_tables_hsw+0x1a>
+
+PUBLIC _sk_load_tables_u16_be_hsw
+_sk_load_tables_u16_be_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,201,0,0,0                    ; jne           1d4e <_sk_load_tables_u16_be_hsw+0xdf>
+  DB  196,1,121,16,4,81                   ; vmovupd       (%r9,%r10,2),%xmm8
+  DB  196,129,121,16,84,81,16             ; vmovupd       0x10(%r9,%r10,2),%xmm2
+  DB  196,129,121,16,92,81,32             ; vmovupd       0x20(%r9,%r10,2),%xmm3
+  DB  196,1,122,111,76,81,48              ; vmovdqu       0x30(%r9,%r10,2),%xmm9
+  DB  197,185,97,194                      ; vpunpcklwd    %xmm2,%xmm8,%xmm0
+  DB  197,185,105,210                     ; vpunpckhwd    %xmm2,%xmm8,%xmm2
+  DB  196,193,97,97,201                   ; vpunpcklwd    %xmm9,%xmm3,%xmm1
+  DB  196,193,97,105,217                  ; vpunpckhwd    %xmm9,%xmm3,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,121,105,202                     ; vpunpckhwd    %xmm2,%xmm0,%xmm9
+  DB  197,241,97,195                      ; vpunpcklwd    %xmm3,%xmm1,%xmm0
+  DB  197,241,105,219                     ; vpunpckhwd    %xmm3,%xmm1,%xmm3
+  DB  197,185,108,200                     ; vpunpcklqdq   %xmm0,%xmm8,%xmm1
+  DB  197,185,109,208                     ; vpunpckhqdq   %xmm0,%xmm8,%xmm2
+  DB  197,49,108,195                      ; vpunpcklqdq   %xmm3,%xmm9,%xmm8
+  DB  197,121,111,21,106,51,0,0           ; vmovdqa       0x336a(%rip),%xmm10        # 5040 <_sk_callback_hsw+0x724>
+  DB  196,193,113,219,194                 ; vpand         %xmm10,%xmm1,%xmm0
+  DB  196,226,125,51,200                  ; vpmovzxwd     %xmm0,%ymm1
+  DB  196,65,37,118,219                   ; vpcmpeqd      %ymm11,%ymm11,%ymm11
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  196,65,29,118,228                   ; vpcmpeqd      %ymm12,%ymm12,%ymm12
+  DB  196,194,29,146,4,137                ; vgatherdps    %ymm12,(%r9,%ymm1,4),%ymm0
+  DB  196,193,105,219,202                 ; vpand         %xmm10,%xmm2,%xmm1
+  DB  196,226,125,51,209                  ; vpmovzxwd     %xmm1,%ymm2
+  DB  196,65,29,118,228                   ; vpcmpeqd      %ymm12,%ymm12,%ymm12
+  DB  196,194,29,146,12,146               ; vgatherdps    %ymm12,(%r10,%ymm2,4),%ymm1
+  DB  72,139,64,24                        ; mov           0x18(%rax),%rax
+  DB  196,193,57,219,210                  ; vpand         %xmm10,%xmm8,%xmm2
+  DB  196,98,125,51,194                   ; vpmovzxwd     %xmm2,%ymm8
+  DB  196,162,37,146,20,128               ; vgatherdps    %ymm11,(%rax,%ymm8,4),%ymm2
+  DB  197,177,109,219                     ; vpunpckhqdq   %xmm3,%xmm9,%xmm3
+  DB  197,185,113,243,8                   ; vpsllw        $0x8,%xmm3,%xmm8
+  DB  197,225,113,211,8                   ; vpsrlw        $0x8,%xmm3,%xmm3
+  DB  197,185,235,219                     ; vpor          %xmm3,%xmm8,%xmm3
+  DB  196,226,125,51,219                  ; vpmovzxwd     %xmm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,98,125,24,5,71,46,0,0           ; vbroadcastss  0x2e47(%rip),%ymm8        # 4b8c <_sk_callback_hsw+0x270>
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,1,123,16,4,81                   ; vmovsd        (%r9,%r10,2),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,85                              ; je            1db4 <_sk_load_tables_u16_be_hsw+0x145>
+  DB  196,1,57,22,68,81,8                 ; vmovhpd       0x8(%r9,%r10,2),%xmm8,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,72                              ; jb            1db4 <_sk_load_tables_u16_be_hsw+0x145>
+  DB  196,129,123,16,84,81,16             ; vmovsd        0x10(%r9,%r10,2),%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  116,72                              ; je            1dc1 <_sk_load_tables_u16_be_hsw+0x152>
+  DB  196,129,105,22,84,81,24             ; vmovhpd       0x18(%r9,%r10,2),%xmm2,%xmm2
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,59                              ; jb            1dc1 <_sk_load_tables_u16_be_hsw+0x152>
+  DB  196,129,123,16,92,81,32             ; vmovsd        0x20(%r9,%r10,2),%xmm3
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  15,132,9,255,255,255                ; je            1ca0 <_sk_load_tables_u16_be_hsw+0x31>
+  DB  196,129,97,22,92,81,40              ; vmovhpd       0x28(%r9,%r10,2),%xmm3,%xmm3
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  15,130,248,254,255,255              ; jb            1ca0 <_sk_load_tables_u16_be_hsw+0x31>
+  DB  196,1,122,126,76,81,48              ; vmovq         0x30(%r9,%r10,2),%xmm9
+  DB  233,236,254,255,255                 ; jmpq          1ca0 <_sk_load_tables_u16_be_hsw+0x31>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  197,233,87,210                      ; vxorpd        %xmm2,%xmm2,%xmm2
+  DB  233,223,254,255,255                 ; jmpq          1ca0 <_sk_load_tables_u16_be_hsw+0x31>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  233,214,254,255,255                 ; jmpq          1ca0 <_sk_load_tables_u16_be_hsw+0x31>
+
+PUBLIC _sk_load_tables_rgb_u16_be_hsw
+_sk_load_tables_rgb_u16_be_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,82                        ; lea           (%rdx,%rdx,2),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,193,0,0,0                    ; jne           1e9d <_sk_load_tables_rgb_u16_be_hsw+0xd3>
+  DB  196,129,122,111,4,81                ; vmovdqu       (%r9,%r10,2),%xmm0
+  DB  196,129,122,111,84,81,12            ; vmovdqu       0xc(%r9,%r10,2),%xmm2
+  DB  196,129,122,111,76,81,24            ; vmovdqu       0x18(%r9,%r10,2),%xmm1
+  DB  196,129,122,111,92,81,32            ; vmovdqu       0x20(%r9,%r10,2),%xmm3
+  DB  197,225,115,219,4                   ; vpsrldq       $0x4,%xmm3,%xmm3
+  DB  197,185,115,216,6                   ; vpsrldq       $0x6,%xmm0,%xmm8
+  DB  197,177,115,218,6                   ; vpsrldq       $0x6,%xmm2,%xmm9
+  DB  197,161,115,217,6                   ; vpsrldq       $0x6,%xmm1,%xmm11
+  DB  197,169,115,219,6                   ; vpsrldq       $0x6,%xmm3,%xmm10
+  DB  197,249,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm0
+  DB  196,193,57,97,209                   ; vpunpcklwd    %xmm9,%xmm8,%xmm2
+  DB  197,241,97,203                      ; vpunpcklwd    %xmm3,%xmm1,%xmm1
+  DB  196,193,33,97,218                   ; vpunpcklwd    %xmm10,%xmm11,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,249,105,194                     ; vpunpckhwd    %xmm2,%xmm0,%xmm0
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,241,105,203                     ; vpunpckhwd    %xmm3,%xmm1,%xmm1
+  DB  197,185,108,218                     ; vpunpcklqdq   %xmm2,%xmm8,%xmm3
+  DB  197,185,109,210                     ; vpunpckhqdq   %xmm2,%xmm8,%xmm2
+  DB  197,121,108,193                     ; vpunpcklqdq   %xmm1,%xmm0,%xmm8
+  DB  197,121,111,13,10,50,0,0            ; vmovdqa       0x320a(%rip),%xmm9        # 5050 <_sk_callback_hsw+0x734>
+  DB  196,193,97,219,193                  ; vpand         %xmm9,%xmm3,%xmm0
+  DB  196,226,125,51,200                  ; vpmovzxwd     %xmm0,%ymm1
+  DB  197,229,118,219                     ; vpcmpeqd      %ymm3,%ymm3,%ymm3
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  196,65,45,118,210                   ; vpcmpeqd      %ymm10,%ymm10,%ymm10
+  DB  196,194,45,146,4,137                ; vgatherdps    %ymm10,(%r9,%ymm1,4),%ymm0
+  DB  196,193,105,219,201                 ; vpand         %xmm9,%xmm2,%xmm1
+  DB  196,226,125,51,209                  ; vpmovzxwd     %xmm1,%ymm2
+  DB  196,65,45,118,210                   ; vpcmpeqd      %ymm10,%ymm10,%ymm10
+  DB  196,194,45,146,12,146               ; vgatherdps    %ymm10,(%r10,%ymm2,4),%ymm1
+  DB  72,139,64,24                        ; mov           0x18(%rax),%rax
+  DB  196,193,57,219,209                  ; vpand         %xmm9,%xmm8,%xmm2
+  DB  196,98,125,51,194                   ; vpmovzxwd     %xmm2,%ymm8
+  DB  196,162,101,146,20,128              ; vgatherdps    %ymm3,(%rax,%ymm8,4),%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,245,44,0,0        ; vbroadcastss  0x2cf5(%rip),%ymm3        # 4b90 <_sk_callback_hsw+0x274>
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,129,121,110,4,81                ; vmovd         (%r9,%r10,2),%xmm0
+  DB  196,129,121,196,68,81,4,2           ; vpinsrw       $0x2,0x4(%r9,%r10,2),%xmm0,%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,5                               ; jne           1eb6 <_sk_load_tables_rgb_u16_be_hsw+0xec>
+  DB  233,90,255,255,255                  ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  DB  196,129,121,110,76,81,6             ; vmovd         0x6(%r9,%r10,2),%xmm1
+  DB  196,1,113,196,68,81,10,2            ; vpinsrw       $0x2,0xa(%r9,%r10,2),%xmm1,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,26                              ; jb            1ee5 <_sk_load_tables_rgb_u16_be_hsw+0x11b>
+  DB  196,129,121,110,76,81,12            ; vmovd         0xc(%r9,%r10,2),%xmm1
+  DB  196,129,113,196,84,81,16,2          ; vpinsrw       $0x2,0x10(%r9,%r10,2),%xmm1,%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  117,10                              ; jne           1eea <_sk_load_tables_rgb_u16_be_hsw+0x120>
+  DB  233,43,255,255,255                  ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  DB  233,38,255,255,255                  ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  DB  196,129,121,110,76,81,18            ; vmovd         0x12(%r9,%r10,2),%xmm1
+  DB  196,1,113,196,76,81,22,2            ; vpinsrw       $0x2,0x16(%r9,%r10,2),%xmm1,%xmm9
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,26                              ; jb            1f19 <_sk_load_tables_rgb_u16_be_hsw+0x14f>
+  DB  196,129,121,110,76,81,24            ; vmovd         0x18(%r9,%r10,2),%xmm1
+  DB  196,129,113,196,76,81,28,2          ; vpinsrw       $0x2,0x1c(%r9,%r10,2),%xmm1,%xmm1
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  117,10                              ; jne           1f1e <_sk_load_tables_rgb_u16_be_hsw+0x154>
+  DB  233,247,254,255,255                 ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  DB  233,242,254,255,255                 ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  DB  196,129,121,110,92,81,30            ; vmovd         0x1e(%r9,%r10,2),%xmm3
+  DB  196,1,97,196,92,81,34,2             ; vpinsrw       $0x2,0x22(%r9,%r10,2),%xmm3,%xmm11
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,20                              ; jb            1f47 <_sk_load_tables_rgb_u16_be_hsw+0x17d>
+  DB  196,129,121,110,92,81,36            ; vmovd         0x24(%r9,%r10,2),%xmm3
+  DB  196,129,97,196,92,81,40,2           ; vpinsrw       $0x2,0x28(%r9,%r10,2),%xmm3,%xmm3
+  DB  233,201,254,255,255                 ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+  DB  233,196,254,255,255                 ; jmpq          1e10 <_sk_load_tables_rgb_u16_be_hsw+0x46>
+
+PUBLIC _sk_byte_tables_hsw
+_sk_byte_tables_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,61,44,0,0           ; vbroadcastss  0x2c3d(%rip),%ymm8        # 4b94 <_sk_callback_hsw+0x278>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  197,125,91,200                      ; vcvtps2dq     %ymm0,%ymm9
+  DB  196,65,249,126,201                  ; vmovq         %xmm9,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,131,121,32,4,19,0               ; vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  DB  196,67,249,22,202,1                 ; vpextrq       $0x1,%xmm9,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,121,32,20,11,1                ; vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,99,125,57,200,1                 ; vextracti128  $0x1,%ymm9,%xmm0
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,67,41,32,201,2                  ; vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,49,32,202,3                  ; vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,49,32,202,4                  ; vpinsrb       $0x4,%r10d,%xmm9,%xmm9
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,195,49,32,193,5                 ; vpinsrb       $0x5,%r9d,%xmm9,%xmm0
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,195,121,32,193,6                ; vpinsrb       $0x6,%r9d,%xmm0,%xmm0
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,121,32,202,7                 ; vpinsrb       $0x7,%r10d,%xmm0,%xmm9
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,4,25,0               ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,121,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,57,201,1                ; vextracti128  $0x1,%ymm1,%xmm1
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,2                ; vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,195,121,32,195,3                ; vpinsrb       $0x3,%r11d,%xmm0,%xmm0
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,195,121,32,195,4                ; vpinsrb       $0x4,%r11d,%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,5                ; vpinsrb       $0x5,%r10d,%xmm0,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,6                ; vpinsrb       $0x6,%r10d,%xmm0,%xmm0
+  DB  71,15,182,12,25                     ; movzbl        (%r9,%r11,1),%r9d
+  DB  196,195,121,32,201,7                ; vpinsrb       $0x7,%r9d,%xmm0,%xmm1
+  DB  76,139,72,16                        ; mov           0x10(%rax),%r9
+  DB  196,193,108,89,192                  ; vmulps        %ymm8,%ymm2,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,20,25,0              ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm2
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,20,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm2
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,105,32,210,2                ; vpinsrb       $0x2,%r10d,%xmm2,%xmm2
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,195,105,32,211,3                ; vpinsrb       $0x3,%r11d,%xmm2,%xmm2
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,195,105,32,211,4                ; vpinsrb       $0x4,%r11d,%xmm2,%xmm2
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,105,32,194,5                ; vpinsrb       $0x5,%r10d,%xmm2,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,6                ; vpinsrb       $0x6,%r10d,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  71,15,182,12,25                     ; movzbl        (%r9,%r11,1),%r9d
+  DB  196,195,121,32,209,7                ; vpinsrb       $0x7,%r9d,%xmm0,%xmm2
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  196,193,100,89,192                  ; vmulps        %ymm8,%ymm3,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,195,121,32,28,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm3
+  DB  196,227,249,22,192,1                ; vpextrq       $0x1,%xmm0,%rax
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,97,32,28,10,1               ; vpinsrb       $0x1,(%r10,%r9,1),%xmm3,%xmm3
+  DB  65,137,193                          ; mov           %eax,%r9d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,12,10                     ; movzbl        (%r10,%r9,1),%r9d
+  DB  196,195,97,32,217,2                 ; vpinsrb       $0x2,%r9d,%xmm3,%xmm3
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,227,97,32,216,3                 ; vpinsrb       $0x3,%eax,%xmm3,%xmm3
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,227,97,32,216,4                 ; vpinsrb       $0x4,%eax,%xmm3,%xmm3
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  67,15,182,4,10                      ; movzbl        (%r10,%r9,1),%eax
+  DB  196,227,97,32,192,5                 ; vpinsrb       $0x5,%eax,%xmm3,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,227,121,32,216,6                ; vpinsrb       $0x6,%eax,%xmm0,%xmm3
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,26                      ; movzbl        (%r10,%r11,1),%eax
+  DB  196,194,125,49,193                  ; vpmovzxbd     %xmm9,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,5,226,41,0,0          ; vbroadcastss  0x29e2(%rip),%ymm8        # 4b98 <_sk_callback_hsw+0x27c>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,226,125,49,201                  ; vpmovzxbd     %xmm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,226,125,49,210                  ; vpmovzxbd     %xmm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  196,227,97,32,216,7                 ; vpinsrb       $0x7,%eax,%xmm3,%xmm3
+  DB  196,226,125,49,219                  ; vpmovzxbd     %xmm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_byte_tables_rgb_hsw
+_sk_byte_tables_rgb_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,139,72,24                        ; mov           0x18(%rax),%r9d
+  DB  65,255,201                          ; dec           %r9d
+  DB  196,65,121,110,193                  ; vmovd         %r9d,%xmm8
+  DB  196,66,125,88,192                   ; vpbroadcastd  %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,125,91,200                      ; vcvtps2dq     %ymm0,%ymm9
+  DB  196,65,249,126,201                  ; vmovq         %xmm9,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,131,121,32,4,19,0               ; vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  DB  196,67,249,22,202,1                 ; vpextrq       $0x1,%xmm9,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,121,32,20,11,1                ; vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,99,125,57,200,1                 ; vextracti128  $0x1,%ymm9,%xmm0
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,67,41,32,201,2                  ; vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,49,32,202,3                  ; vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,49,32,202,4                  ; vpinsrb       $0x4,%r10d,%xmm9,%xmm9
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,195,49,32,193,5                 ; vpinsrb       $0x5,%r9d,%xmm9,%xmm0
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,195,121,32,193,6                ; vpinsrb       $0x6,%r9d,%xmm0,%xmm0
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,121,32,202,7                 ; vpinsrb       $0x7,%r10d,%xmm0,%xmm9
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,4,25,0               ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,121,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,57,201,1                ; vextracti128  $0x1,%ymm1,%xmm1
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,2                ; vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,195,121,32,195,3                ; vpinsrb       $0x3,%r11d,%xmm0,%xmm0
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,195,121,32,195,4                ; vpinsrb       $0x4,%r11d,%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,5                ; vpinsrb       $0x5,%r10d,%xmm0,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,6                ; vpinsrb       $0x6,%r10d,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  71,15,182,12,25                     ; movzbl        (%r9,%r11,1),%r9d
+  DB  196,195,121,32,201,7                ; vpinsrb       $0x7,%r9d,%xmm0,%xmm1
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  197,188,89,194                      ; vmulps        %ymm2,%ymm8,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,195,121,32,20,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm2
+  DB  196,227,249,22,192,1                ; vpextrq       $0x1,%xmm0,%rax
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,105,32,20,10,1              ; vpinsrb       $0x1,(%r10,%r9,1),%xmm2,%xmm2
+  DB  65,137,193                          ; mov           %eax,%r9d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,12,10                     ; movzbl        (%r10,%r9,1),%r9d
+  DB  196,195,105,32,209,2                ; vpinsrb       $0x2,%r9d,%xmm2,%xmm2
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,227,105,32,208,3                ; vpinsrb       $0x3,%eax,%xmm2,%xmm2
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,227,105,32,208,4                ; vpinsrb       $0x4,%eax,%xmm2,%xmm2
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  67,15,182,4,10                      ; movzbl        (%r10,%r9,1),%eax
+  DB  196,227,105,32,192,5                ; vpinsrb       $0x5,%eax,%xmm2,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,227,121,32,208,6                ; vpinsrb       $0x6,%eax,%xmm0,%xmm2
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,26                      ; movzbl        (%r10,%r11,1),%eax
+  DB  196,194,125,49,193                  ; vpmovzxbd     %xmm9,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,5,206,39,0,0          ; vbroadcastss  0x27ce(%rip),%ymm8        # 4b9c <_sk_callback_hsw+0x280>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,226,125,49,201                  ; vpmovzxbd     %xmm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,227,105,32,208,7                ; vpinsrb       $0x7,%eax,%xmm2,%xmm2
+  DB  196,226,125,49,210                  ; vpmovzxbd     %xmm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_r_hsw
+_sk_table_r_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,66,125,88,192                   ; vpbroadcastd  %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,125,91,192                      ; vcvtps2dq     %ymm0,%ymm8
+  DB  196,65,53,118,201                   ; vpcmpeqd      %ymm9,%ymm9,%ymm9
+  DB  196,130,53,146,4,129                ; vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_g_hsw
+_sk_table_g_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,66,125,88,192                   ; vpbroadcastd  %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,125,91,193                      ; vcvtps2dq     %ymm1,%ymm8
+  DB  196,65,53,118,201                   ; vpcmpeqd      %ymm9,%ymm9,%ymm9
+  DB  196,130,53,146,12,129               ; vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_b_hsw
+_sk_table_b_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,66,125,88,192                   ; vpbroadcastd  %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,125,91,194                      ; vcvtps2dq     %ymm2,%ymm8
+  DB  196,65,53,118,201                   ; vpcmpeqd      %ymm9,%ymm9,%ymm9
+  DB  196,130,53,146,20,129               ; vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_a_hsw
+_sk_table_a_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,66,125,88,192                   ; vpbroadcastd  %xmm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  197,125,91,195                      ; vcvtps2dq     %ymm3,%ymm8
+  DB  196,65,53,118,201                   ; vpcmpeqd      %ymm9,%ymm9,%ymm9
+  DB  196,130,53,146,28,129               ; vgatherdps    %ymm9,(%r9,%ymm8,4),%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_r_hsw
+_sk_parametric_r_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,124,194,192,2                ; vcmpleps      %ymm8,%ymm0,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,66,125,168,202                  ; vfmadd213ps   %ymm10,%ymm0,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  196,66,125,168,211                  ; vfmadd213ps   %ymm11,%ymm0,%ymm10
+  DB  196,226,125,24,0                    ; vbroadcastss  (%rax),%ymm0
+  DB  196,65,124,91,218                   ; vcvtdq2ps     %ymm10,%ymm11
+  DB  196,98,125,24,37,168,38,0,0         ; vbroadcastss  0x26a8(%rip),%ymm12        # 4ba0 <_sk_callback_hsw+0x284>
+  DB  196,98,125,24,45,163,38,0,0         ; vbroadcastss  0x26a3(%rip),%ymm13        # 4ba4 <_sk_callback_hsw+0x288>
+  DB  196,65,44,84,213                    ; vandps        %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,153,38,0,0         ; vbroadcastss  0x2699(%rip),%ymm13        # 4ba8 <_sk_callback_hsw+0x28c>
+  DB  196,65,44,86,213                    ; vorps         %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,143,38,0,0         ; vbroadcastss  0x268f(%rip),%ymm13        # 4bac <_sk_callback_hsw+0x290>
+  DB  196,66,37,184,236                   ; vfmadd231ps   %ymm12,%ymm11,%ymm13
+  DB  196,98,125,24,29,133,38,0,0         ; vbroadcastss  0x2685(%rip),%ymm11        # 4bb0 <_sk_callback_hsw+0x294>
+  DB  196,66,45,172,221                   ; vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  DB  196,98,125,24,37,123,38,0,0         ; vbroadcastss  0x267b(%rip),%ymm12        # 4bb4 <_sk_callback_hsw+0x298>
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,98,125,24,37,113,38,0,0         ; vbroadcastss  0x2671(%rip),%ymm12        # 4bb8 <_sk_callback_hsw+0x29c>
+  DB  196,65,28,94,210                    ; vdivps        %ymm10,%ymm12,%ymm10
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,193,124,89,194                  ; vmulps        %ymm10,%ymm0,%ymm0
+  DB  196,99,125,8,208,1                  ; vroundps      $0x1,%ymm0,%ymm10
+  DB  196,65,124,92,210                   ; vsubps        %ymm10,%ymm0,%ymm10
+  DB  196,98,125,24,29,82,38,0,0          ; vbroadcastss  0x2652(%rip),%ymm11        # 4bbc <_sk_callback_hsw+0x2a0>
+  DB  196,193,124,88,195                  ; vaddps        %ymm11,%ymm0,%ymm0
+  DB  196,98,125,24,29,72,38,0,0          ; vbroadcastss  0x2648(%rip),%ymm11        # 4bc0 <_sk_callback_hsw+0x2a4>
+  DB  196,98,45,172,216                   ; vfnmadd213ps  %ymm0,%ymm10,%ymm11
+  DB  196,226,125,24,5,62,38,0,0          ; vbroadcastss  0x263e(%rip),%ymm0        # 4bc4 <_sk_callback_hsw+0x2a8>
+  DB  196,193,124,92,194                  ; vsubps        %ymm10,%ymm0,%ymm0
+  DB  196,98,125,24,21,52,38,0,0          ; vbroadcastss  0x2634(%rip),%ymm10        # 4bc8 <_sk_callback_hsw+0x2ac>
+  DB  197,172,94,192                      ; vdivps        %ymm0,%ymm10,%ymm0
+  DB  197,164,88,192                      ; vaddps        %ymm0,%ymm11,%ymm0
+  DB  196,98,125,24,21,39,38,0,0          ; vbroadcastss  0x2627(%rip),%ymm10        # 4bcc <_sk_callback_hsw+0x2b0>
+  DB  196,193,124,89,194                  ; vmulps        %ymm10,%ymm0,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,124,88,194                  ; vaddps        %ymm10,%ymm0,%ymm0
+  DB  196,195,125,74,193,128              ; vblendvps     %ymm8,%ymm9,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,5,254,37,0,0          ; vbroadcastss  0x25fe(%rip),%ymm8        # 4bd0 <_sk_callback_hsw+0x2b4>
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_g_hsw
+_sk_parametric_g_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,116,194,192,2                ; vcmpleps      %ymm8,%ymm1,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,66,117,168,202                  ; vfmadd213ps   %ymm10,%ymm1,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  196,66,117,168,211                  ; vfmadd213ps   %ymm11,%ymm1,%ymm10
+  DB  196,226,125,24,8                    ; vbroadcastss  (%rax),%ymm1
+  DB  196,65,124,91,218                   ; vcvtdq2ps     %ymm10,%ymm11
+  DB  196,98,125,24,37,182,37,0,0         ; vbroadcastss  0x25b6(%rip),%ymm12        # 4bd4 <_sk_callback_hsw+0x2b8>
+  DB  196,98,125,24,45,177,37,0,0         ; vbroadcastss  0x25b1(%rip),%ymm13        # 4bd8 <_sk_callback_hsw+0x2bc>
+  DB  196,65,44,84,213                    ; vandps        %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,167,37,0,0         ; vbroadcastss  0x25a7(%rip),%ymm13        # 4bdc <_sk_callback_hsw+0x2c0>
+  DB  196,65,44,86,213                    ; vorps         %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,157,37,0,0         ; vbroadcastss  0x259d(%rip),%ymm13        # 4be0 <_sk_callback_hsw+0x2c4>
+  DB  196,66,37,184,236                   ; vfmadd231ps   %ymm12,%ymm11,%ymm13
+  DB  196,98,125,24,29,147,37,0,0         ; vbroadcastss  0x2593(%rip),%ymm11        # 4be4 <_sk_callback_hsw+0x2c8>
+  DB  196,66,45,172,221                   ; vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  DB  196,98,125,24,37,137,37,0,0         ; vbroadcastss  0x2589(%rip),%ymm12        # 4be8 <_sk_callback_hsw+0x2cc>
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,98,125,24,37,127,37,0,0         ; vbroadcastss  0x257f(%rip),%ymm12        # 4bec <_sk_callback_hsw+0x2d0>
+  DB  196,65,28,94,210                    ; vdivps        %ymm10,%ymm12,%ymm10
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,193,116,89,202                  ; vmulps        %ymm10,%ymm1,%ymm1
+  DB  196,99,125,8,209,1                  ; vroundps      $0x1,%ymm1,%ymm10
+  DB  196,65,116,92,210                   ; vsubps        %ymm10,%ymm1,%ymm10
+  DB  196,98,125,24,29,96,37,0,0          ; vbroadcastss  0x2560(%rip),%ymm11        # 4bf0 <_sk_callback_hsw+0x2d4>
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,29,86,37,0,0          ; vbroadcastss  0x2556(%rip),%ymm11        # 4bf4 <_sk_callback_hsw+0x2d8>
+  DB  196,98,45,172,217                   ; vfnmadd213ps  %ymm1,%ymm10,%ymm11
+  DB  196,226,125,24,13,76,37,0,0         ; vbroadcastss  0x254c(%rip),%ymm1        # 4bf8 <_sk_callback_hsw+0x2dc>
+  DB  196,193,116,92,202                  ; vsubps        %ymm10,%ymm1,%ymm1
+  DB  196,98,125,24,21,66,37,0,0          ; vbroadcastss  0x2542(%rip),%ymm10        # 4bfc <_sk_callback_hsw+0x2e0>
+  DB  197,172,94,201                      ; vdivps        %ymm1,%ymm10,%ymm1
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  196,98,125,24,21,53,37,0,0          ; vbroadcastss  0x2535(%rip),%ymm10        # 4c00 <_sk_callback_hsw+0x2e4>
+  DB  196,193,116,89,202                  ; vmulps        %ymm10,%ymm1,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  196,195,117,74,201,128              ; vblendvps     %ymm8,%ymm9,%ymm1,%ymm1
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,116,95,200                  ; vmaxps        %ymm8,%ymm1,%ymm1
+  DB  196,98,125,24,5,12,37,0,0           ; vbroadcastss  0x250c(%rip),%ymm8        # 4c04 <_sk_callback_hsw+0x2e8>
+  DB  196,193,116,93,200                  ; vminps        %ymm8,%ymm1,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_b_hsw
+_sk_parametric_b_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,108,194,192,2                ; vcmpleps      %ymm8,%ymm2,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,66,109,168,202                  ; vfmadd213ps   %ymm10,%ymm2,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  196,66,109,168,211                  ; vfmadd213ps   %ymm11,%ymm2,%ymm10
+  DB  196,226,125,24,16                   ; vbroadcastss  (%rax),%ymm2
+  DB  196,65,124,91,218                   ; vcvtdq2ps     %ymm10,%ymm11
+  DB  196,98,125,24,37,196,36,0,0         ; vbroadcastss  0x24c4(%rip),%ymm12        # 4c08 <_sk_callback_hsw+0x2ec>
+  DB  196,98,125,24,45,191,36,0,0         ; vbroadcastss  0x24bf(%rip),%ymm13        # 4c0c <_sk_callback_hsw+0x2f0>
+  DB  196,65,44,84,213                    ; vandps        %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,181,36,0,0         ; vbroadcastss  0x24b5(%rip),%ymm13        # 4c10 <_sk_callback_hsw+0x2f4>
+  DB  196,65,44,86,213                    ; vorps         %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,171,36,0,0         ; vbroadcastss  0x24ab(%rip),%ymm13        # 4c14 <_sk_callback_hsw+0x2f8>
+  DB  196,66,37,184,236                   ; vfmadd231ps   %ymm12,%ymm11,%ymm13
+  DB  196,98,125,24,29,161,36,0,0         ; vbroadcastss  0x24a1(%rip),%ymm11        # 4c18 <_sk_callback_hsw+0x2fc>
+  DB  196,66,45,172,221                   ; vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  DB  196,98,125,24,37,151,36,0,0         ; vbroadcastss  0x2497(%rip),%ymm12        # 4c1c <_sk_callback_hsw+0x300>
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,98,125,24,37,141,36,0,0         ; vbroadcastss  0x248d(%rip),%ymm12        # 4c20 <_sk_callback_hsw+0x304>
+  DB  196,65,28,94,210                    ; vdivps        %ymm10,%ymm12,%ymm10
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,193,108,89,210                  ; vmulps        %ymm10,%ymm2,%ymm2
+  DB  196,99,125,8,210,1                  ; vroundps      $0x1,%ymm2,%ymm10
+  DB  196,65,108,92,210                   ; vsubps        %ymm10,%ymm2,%ymm10
+  DB  196,98,125,24,29,110,36,0,0         ; vbroadcastss  0x246e(%rip),%ymm11        # 4c24 <_sk_callback_hsw+0x308>
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  196,98,125,24,29,100,36,0,0         ; vbroadcastss  0x2464(%rip),%ymm11        # 4c28 <_sk_callback_hsw+0x30c>
+  DB  196,98,45,172,218                   ; vfnmadd213ps  %ymm2,%ymm10,%ymm11
+  DB  196,226,125,24,21,90,36,0,0         ; vbroadcastss  0x245a(%rip),%ymm2        # 4c2c <_sk_callback_hsw+0x310>
+  DB  196,193,108,92,210                  ; vsubps        %ymm10,%ymm2,%ymm2
+  DB  196,98,125,24,21,80,36,0,0          ; vbroadcastss  0x2450(%rip),%ymm10        # 4c30 <_sk_callback_hsw+0x314>
+  DB  197,172,94,210                      ; vdivps        %ymm2,%ymm10,%ymm2
+  DB  197,164,88,210                      ; vaddps        %ymm2,%ymm11,%ymm2
+  DB  196,98,125,24,21,67,36,0,0          ; vbroadcastss  0x2443(%rip),%ymm10        # 4c34 <_sk_callback_hsw+0x318>
+  DB  196,193,108,89,210                  ; vmulps        %ymm10,%ymm2,%ymm2
+  DB  197,253,91,210                      ; vcvtps2dq     %ymm2,%ymm2
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  196,195,109,74,209,128              ; vblendvps     %ymm8,%ymm9,%ymm2,%ymm2
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,108,95,208                  ; vmaxps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,24,5,26,36,0,0           ; vbroadcastss  0x241a(%rip),%ymm8        # 4c38 <_sk_callback_hsw+0x31c>
+  DB  196,193,108,93,208                  ; vminps        %ymm8,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_a_hsw
+_sk_parametric_a_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,100,194,192,2                ; vcmpleps      %ymm8,%ymm3,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,66,101,168,202                  ; vfmadd213ps   %ymm10,%ymm3,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  196,66,101,168,211                  ; vfmadd213ps   %ymm11,%ymm3,%ymm10
+  DB  196,226,125,24,24                   ; vbroadcastss  (%rax),%ymm3
+  DB  196,65,124,91,218                   ; vcvtdq2ps     %ymm10,%ymm11
+  DB  196,98,125,24,37,210,35,0,0         ; vbroadcastss  0x23d2(%rip),%ymm12        # 4c3c <_sk_callback_hsw+0x320>
+  DB  196,98,125,24,45,205,35,0,0         ; vbroadcastss  0x23cd(%rip),%ymm13        # 4c40 <_sk_callback_hsw+0x324>
+  DB  196,65,44,84,213                    ; vandps        %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,195,35,0,0         ; vbroadcastss  0x23c3(%rip),%ymm13        # 4c44 <_sk_callback_hsw+0x328>
+  DB  196,65,44,86,213                    ; vorps         %ymm13,%ymm10,%ymm10
+  DB  196,98,125,24,45,185,35,0,0         ; vbroadcastss  0x23b9(%rip),%ymm13        # 4c48 <_sk_callback_hsw+0x32c>
+  DB  196,66,37,184,236                   ; vfmadd231ps   %ymm12,%ymm11,%ymm13
+  DB  196,98,125,24,29,175,35,0,0         ; vbroadcastss  0x23af(%rip),%ymm11        # 4c4c <_sk_callback_hsw+0x330>
+  DB  196,66,45,172,221                   ; vfnmadd213ps  %ymm13,%ymm10,%ymm11
+  DB  196,98,125,24,37,165,35,0,0         ; vbroadcastss  0x23a5(%rip),%ymm12        # 4c50 <_sk_callback_hsw+0x334>
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,98,125,24,37,155,35,0,0         ; vbroadcastss  0x239b(%rip),%ymm12        # 4c54 <_sk_callback_hsw+0x338>
+  DB  196,65,28,94,210                    ; vdivps        %ymm10,%ymm12,%ymm10
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,193,100,89,218                  ; vmulps        %ymm10,%ymm3,%ymm3
+  DB  196,99,125,8,211,1                  ; vroundps      $0x1,%ymm3,%ymm10
+  DB  196,65,100,92,210                   ; vsubps        %ymm10,%ymm3,%ymm10
+  DB  196,98,125,24,29,124,35,0,0         ; vbroadcastss  0x237c(%rip),%ymm11        # 4c58 <_sk_callback_hsw+0x33c>
+  DB  196,193,100,88,219                  ; vaddps        %ymm11,%ymm3,%ymm3
+  DB  196,98,125,24,29,114,35,0,0         ; vbroadcastss  0x2372(%rip),%ymm11        # 4c5c <_sk_callback_hsw+0x340>
+  DB  196,98,45,172,219                   ; vfnmadd213ps  %ymm3,%ymm10,%ymm11
+  DB  196,226,125,24,29,104,35,0,0        ; vbroadcastss  0x2368(%rip),%ymm3        # 4c60 <_sk_callback_hsw+0x344>
+  DB  196,193,100,92,218                  ; vsubps        %ymm10,%ymm3,%ymm3
+  DB  196,98,125,24,21,94,35,0,0          ; vbroadcastss  0x235e(%rip),%ymm10        # 4c64 <_sk_callback_hsw+0x348>
+  DB  197,172,94,219                      ; vdivps        %ymm3,%ymm10,%ymm3
+  DB  197,164,88,219                      ; vaddps        %ymm3,%ymm11,%ymm3
+  DB  196,98,125,24,21,81,35,0,0          ; vbroadcastss  0x2351(%rip),%ymm10        # 4c68 <_sk_callback_hsw+0x34c>
+  DB  196,193,100,89,218                  ; vmulps        %ymm10,%ymm3,%ymm3
+  DB  197,253,91,219                      ; vcvtps2dq     %ymm3,%ymm3
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,100,88,218                  ; vaddps        %ymm10,%ymm3,%ymm3
+  DB  196,195,101,74,217,128              ; vblendvps     %ymm8,%ymm9,%ymm3,%ymm3
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,100,95,216                  ; vmaxps        %ymm8,%ymm3,%ymm3
+  DB  196,98,125,24,5,40,35,0,0           ; vbroadcastss  0x2328(%rip),%ymm8        # 4c6c <_sk_callback_hsw+0x350>
+  DB  196,193,100,93,216                  ; vminps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lab_to_xyz_hsw
+_sk_lab_to_xyz_hsw LABEL PROC
+  DB  196,98,125,24,5,26,35,0,0           ; vbroadcastss  0x231a(%rip),%ymm8        # 4c70 <_sk_callback_hsw+0x354>
+  DB  196,98,125,24,13,21,35,0,0          ; vbroadcastss  0x2315(%rip),%ymm9        # 4c74 <_sk_callback_hsw+0x358>
+  DB  196,98,125,24,21,16,35,0,0          ; vbroadcastss  0x2310(%rip),%ymm10        # 4c78 <_sk_callback_hsw+0x35c>
+  DB  196,194,53,168,202                  ; vfmadd213ps   %ymm10,%ymm9,%ymm1
+  DB  196,194,53,168,210                  ; vfmadd213ps   %ymm10,%ymm9,%ymm2
+  DB  196,98,125,24,13,1,35,0,0           ; vbroadcastss  0x2301(%rip),%ymm9        # 4c7c <_sk_callback_hsw+0x360>
+  DB  196,66,125,184,200                  ; vfmadd231ps   %ymm8,%ymm0,%ymm9
+  DB  196,226,125,24,5,247,34,0,0         ; vbroadcastss  0x22f7(%rip),%ymm0        # 4c80 <_sk_callback_hsw+0x364>
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  196,98,125,24,5,238,34,0,0          ; vbroadcastss  0x22ee(%rip),%ymm8        # 4c84 <_sk_callback_hsw+0x368>
+  DB  196,98,117,168,192                  ; vfmadd213ps   %ymm0,%ymm1,%ymm8
+  DB  196,98,125,24,13,228,34,0,0         ; vbroadcastss  0x22e4(%rip),%ymm9        # 4c88 <_sk_callback_hsw+0x36c>
+  DB  196,98,109,172,200                  ; vfnmadd213ps  %ymm0,%ymm2,%ymm9
+  DB  196,193,60,89,200                   ; vmulps        %ymm8,%ymm8,%ymm1
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  196,226,125,24,21,209,34,0,0        ; vbroadcastss  0x22d1(%rip),%ymm2        # 4c8c <_sk_callback_hsw+0x370>
+  DB  197,108,194,209,1                   ; vcmpltps      %ymm1,%ymm2,%ymm10
+  DB  196,98,125,24,29,199,34,0,0         ; vbroadcastss  0x22c7(%rip),%ymm11        # 4c90 <_sk_callback_hsw+0x374>
+  DB  196,65,60,88,195                    ; vaddps        %ymm11,%ymm8,%ymm8
+  DB  196,98,125,24,37,189,34,0,0         ; vbroadcastss  0x22bd(%rip),%ymm12        # 4c94 <_sk_callback_hsw+0x378>
+  DB  196,65,60,89,196                    ; vmulps        %ymm12,%ymm8,%ymm8
+  DB  196,99,61,74,193,160                ; vblendvps     %ymm10,%ymm1,%ymm8,%ymm8
+  DB  197,252,89,200                      ; vmulps        %ymm0,%ymm0,%ymm1
+  DB  197,252,89,201                      ; vmulps        %ymm1,%ymm0,%ymm1
+  DB  197,108,194,209,1                   ; vcmpltps      %ymm1,%ymm2,%ymm10
+  DB  196,193,124,88,195                  ; vaddps        %ymm11,%ymm0,%ymm0
+  DB  196,193,124,89,196                  ; vmulps        %ymm12,%ymm0,%ymm0
+  DB  196,227,125,74,201,160              ; vblendvps     %ymm10,%ymm1,%ymm0,%ymm1
+  DB  196,193,52,89,193                   ; vmulps        %ymm9,%ymm9,%ymm0
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  197,236,194,208,1                   ; vcmpltps      %ymm0,%ymm2,%ymm2
+  DB  196,65,52,88,203                    ; vaddps        %ymm11,%ymm9,%ymm9
+  DB  196,65,52,89,204                    ; vmulps        %ymm12,%ymm9,%ymm9
+  DB  196,227,53,74,208,32                ; vblendvps     %ymm2,%ymm0,%ymm9,%ymm2
+  DB  196,226,125,24,5,114,34,0,0         ; vbroadcastss  0x2272(%rip),%ymm0        # 4c98 <_sk_callback_hsw+0x37c>
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  196,98,125,24,5,105,34,0,0          ; vbroadcastss  0x2269(%rip),%ymm8        # 4c9c <_sk_callback_hsw+0x380>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_a8_hsw
+_sk_load_a8_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,45                              ; jne           2a79 <_sk_load_a8_hsw+0x3d>
+  DB  197,250,126,0                       ; vmovq         (%rax),%xmm0
+  DB  196,226,125,49,192                  ; vpmovzxbd     %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,62,34,0,0         ; vbroadcastss  0x223e(%rip),%ymm1        # 4ca0 <_sk_callback_hsw+0x384>
+  DB  197,252,89,217                      ; vmulps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           2a82 <_sk_load_a8_hsw+0x46>
+  DB  196,193,249,110,194                 ; vmovq         %r10,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  235,177                             ; jmp           2a50 <_sk_load_a8_hsw+0x14>
+
+PUBLIC _sk_gather_a8_hsw
+_sk_gather_a8_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,32,12,1,0               ; vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm1
+  DB  196,227,249,22,192,1                ; vpextrq       $0x1,%xmm0,%rax
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,113,32,12,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm1
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,113,32,202,2                ; vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,113,32,200,3                ; vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,113,32,200,4                ; vpinsrb       $0x4,%eax,%xmm1,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,182,4,17                      ; movzbl        (%r9,%r10,1),%eax
+  DB  196,227,113,32,192,5                ; vpinsrb       $0x5,%eax,%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,121,32,192,6                ; vpinsrb       $0x6,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,25                      ; movzbl        (%r9,%r11,1),%eax
+  DB  196,227,121,32,192,7                ; vpinsrb       $0x7,%eax,%xmm0,%xmm0
+  DB  196,226,125,49,192                  ; vpmovzxbd     %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,79,33,0,0         ; vbroadcastss  0x214f(%rip),%ymm1        # 4ca4 <_sk_callback_hsw+0x388>
+  DB  197,252,89,217                      ; vmulps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,237,239,210                     ; vpxor         %ymm2,%ymm2,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_a8_hsw
+_sk_store_a8_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,98,125,24,5,49,33,0,0           ; vbroadcastss  0x2131(%rip),%ymm8        # 4ca8 <_sk_callback_hsw+0x38c>
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,67,125,25,193,1                 ; vextractf128  $0x1,%ymm8,%xmm9
+  DB  196,66,57,43,193                    ; vpackusdw     %xmm9,%xmm8,%xmm8
+  DB  196,65,57,103,192                   ; vpackuswb     %xmm8,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           2ba0 <_sk_store_a8_hsw+0x37>
+  DB  196,65,123,17,4,19                  ; vmovsd        %xmm8,(%r11,%rdx,1)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            2b9c <_sk_store_a8_hsw+0x33>
+  DB  196,66,121,48,192                   ; vpmovzxbw     %xmm8,%xmm8
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,68,0,0,0                  ; lea           0x44(%rip),%r10        # 2c04 <_sk_store_a8_hsw+0x9b>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,67,121,20,68,19,6,12            ; vpextrb       $0xc,%xmm8,0x6(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,5,10            ; vpextrb       $0xa,%xmm8,0x5(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,4,8             ; vpextrb       $0x8,%xmm8,0x4(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,3,6             ; vpextrb       $0x6,%xmm8,0x3(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,2,4             ; vpextrb       $0x4,%xmm8,0x2(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,1,2             ; vpextrb       $0x2,%xmm8,0x1(%r11,%rdx,1)
+  DB  196,67,121,20,4,19,0                ; vpextrb       $0x0,%xmm8,(%r11,%rdx,1)
+  DB  235,154                             ; jmp           2b9c <_sk_store_a8_hsw+0x33>
+  DB  102,144                             ; xchg          %ax,%ax
+  DB  245                                 ; cmc
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  237                                 ; in            (%dx),%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,229                             ; jmpq          *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  221,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,213                             ; callq         *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,205                             ; dec           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,197                             ; inc           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_g8_hsw
+_sk_load_g8_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,50                              ; jne           2c62 <_sk_load_g8_hsw+0x42>
+  DB  197,250,126,0                       ; vmovq         (%rax),%xmm0
+  DB  196,226,125,49,192                  ; vpmovzxbd     %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,102,32,0,0        ; vbroadcastss  0x2066(%rip),%ymm1        # 4cac <_sk_callback_hsw+0x390>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,91,32,0,0         ; vbroadcastss  0x205b(%rip),%ymm3        # 4cb0 <_sk_callback_hsw+0x394>
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           2c6b <_sk_load_g8_hsw+0x4b>
+  DB  196,193,249,110,194                 ; vmovq         %r10,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  235,172                             ; jmp           2c34 <_sk_load_g8_hsw+0x14>
+
+PUBLIC _sk_gather_g8_hsw
+_sk_gather_g8_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,32,12,1,0               ; vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm1
+  DB  196,227,249,22,192,1                ; vpextrq       $0x1,%xmm0,%rax
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,113,32,12,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm1
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,113,32,202,2                ; vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,113,32,200,3                ; vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,113,32,200,4                ; vpinsrb       $0x4,%eax,%xmm1,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,182,4,17                      ; movzbl        (%r9,%r10,1),%eax
+  DB  196,227,113,32,192,5                ; vpinsrb       $0x5,%eax,%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,121,32,192,6                ; vpinsrb       $0x6,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,25                      ; movzbl        (%r9,%r11,1),%eax
+  DB  196,227,121,32,192,7                ; vpinsrb       $0x7,%eax,%xmm0,%xmm0
+  DB  196,226,125,49,192                  ; vpmovzxbd     %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,118,31,0,0        ; vbroadcastss  0x1f76(%rip),%ymm1        # 4cb4 <_sk_callback_hsw+0x398>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,107,31,0,0        ; vbroadcastss  0x1f6b(%rip),%ymm3        # 4cb8 <_sk_callback_hsw+0x39c>
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gather_i8_hsw
+_sk_gather_i8_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,193                          ; mov           %rax,%r9
+  DB  77,133,201                          ; test          %r9,%r9
+  DB  116,5                               ; je            2d66 <_sk_gather_i8_hsw+0xf>
+  DB  76,137,200                          ; mov           %r9,%rax
+  DB  235,2                               ; jmp           2d68 <_sk_gather_i8_hsw+0x11>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  83                                  ; push          %rbx
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  196,193,249,126,195                 ; vmovq         %xmm0,%r11
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,195,121,32,12,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm1
+  DB  196,227,249,22,192,1                ; vpextrq       $0x1,%xmm0,%rax
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,131,113,32,12,26,1              ; vpinsrb       $0x1,(%r10,%r11,1),%xmm1,%xmm1
+  DB  65,137,195                          ; mov           %eax,%r11d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  196,131,113,32,12,26,2              ; vpinsrb       $0x2,(%r10,%r11,1),%xmm1,%xmm1
+  DB  196,193,249,126,195                 ; vmovq         %xmm0,%r11
+  DB  196,195,113,32,12,2,3               ; vpinsrb       $0x3,(%r10,%rax,1),%xmm1,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,195,113,32,12,2,4               ; vpinsrb       $0x4,(%r10,%rax,1),%xmm1,%xmm1
+  DB  196,227,249,22,195,1                ; vpextrq       $0x1,%xmm0,%rbx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,131,113,32,4,26,5               ; vpinsrb       $0x5,(%r10,%r11,1),%xmm1,%xmm0
+  DB  137,216                             ; mov           %ebx,%eax
+  DB  196,195,121,32,4,2,6                ; vpinsrb       $0x6,(%r10,%rax,1),%xmm0,%xmm0
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  196,195,121,32,4,26,7               ; vpinsrb       $0x7,(%r10,%rbx,1),%xmm0,%xmm0
+  DB  196,226,125,49,192                  ; vpmovzxbd     %xmm0,%ymm0
+  DB  73,139,65,8                         ; mov           0x8(%r9),%rax
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  196,226,117,144,28,128              ; vpgatherdd    %ymm1,(%rax,%ymm0,4),%ymm3
+  DB  197,229,219,5,19,33,0,0             ; vpand         0x2113(%rip),%ymm3,%ymm0        # 4f20 <_sk_callback_hsw+0x604>
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,5,162,30,0,0          ; vbroadcastss  0x1ea2(%rip),%ymm8        # 4cbc <_sk_callback_hsw+0x3a0>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,226,101,0,13,24,33,0,0          ; vpshufb       0x2118(%rip),%ymm3,%ymm1        # 4f40 <_sk_callback_hsw+0x624>
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,226,101,0,21,38,33,0,0          ; vpshufb       0x2126(%rip),%ymm3,%ymm2        # 4f60 <_sk_callback_hsw+0x644>
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,229,114,211,24                  ; vpsrld        $0x18,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_565_hsw
+_sk_load_565_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,114                             ; jne           2ed2 <_sk_load_565_hsw+0x7c>
+  DB  196,193,122,111,4,83                ; vmovdqu       (%r11,%rdx,2),%xmm0
+  DB  196,226,125,51,208                  ; vpmovzxwd     %xmm0,%ymm2
+  DB  196,226,125,88,5,76,30,0,0          ; vpbroadcastd  0x1e4c(%rip),%ymm0        # 4cc0 <_sk_callback_hsw+0x3a4>
+  DB  197,237,219,192                     ; vpand         %ymm0,%ymm2,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,63,30,0,0         ; vbroadcastss  0x1e3f(%rip),%ymm1        # 4cc4 <_sk_callback_hsw+0x3a8>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,88,13,54,30,0,0         ; vpbroadcastd  0x1e36(%rip),%ymm1        # 4cc8 <_sk_callback_hsw+0x3ac>
+  DB  197,237,219,201                     ; vpand         %ymm1,%ymm2,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,29,41,30,0,0         ; vbroadcastss  0x1e29(%rip),%ymm3        # 4ccc <_sk_callback_hsw+0x3b0>
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  196,226,125,88,29,32,30,0,0         ; vpbroadcastd  0x1e20(%rip),%ymm3        # 4cd0 <_sk_callback_hsw+0x3b4>
+  DB  197,237,219,211                     ; vpand         %ymm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,226,125,24,29,19,30,0,0         ; vbroadcastss  0x1e13(%rip),%ymm3        # 4cd4 <_sk_callback_hsw+0x3b8>
+  DB  197,236,89,211                      ; vmulps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,8,30,0,0          ; vbroadcastss  0x1e08(%rip),%ymm3        # 4cd8 <_sk_callback_hsw+0x3bc>
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,128                             ; ja            2e66 <_sk_load_565_hsw+0x10>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,75,0,0,0                  ; lea           0x4b(%rip),%r10        # 2f3c <_sk_load_565_hsw+0xe6>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  196,193,121,196,68,83,12,6          ; vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,10,5          ; vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,8,4           ; vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,6,3           ; vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,4,2           ; vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,2,1           ; vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,4,83,0              ; vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  DB  233,44,255,255,255                  ; jmpq          2e66 <_sk_load_565_hsw+0x10>
+  DB  102,144                             ; xchg          %ax,%ax
+  DB  242,255                             ; repnz         (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  234                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,226                             ; jmpq          *%rdx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  218,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,210                             ; callq         *%rdx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,202                             ; dec           %edx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  190                                 ; .byte         0xbe
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_gather_565_hsw
+_sk_gather_565_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,110,200                     ; vmovd         %eax,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,241,196,200,1                   ; vpinsrw       $0x1,%eax,%xmm1,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,2                   ; vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,241,196,200,3                   ; vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,4                   ; vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,241,196,192,5                   ; vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,196,192,6                   ; vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,249,196,192,7                   ; vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  DB  196,226,125,51,208                  ; vpmovzxwd     %xmm0,%ymm2
+  DB  196,226,125,88,5,211,28,0,0         ; vpbroadcastd  0x1cd3(%rip),%ymm0        # 4cdc <_sk_callback_hsw+0x3c0>
+  DB  197,237,219,192                     ; vpand         %ymm0,%ymm2,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,198,28,0,0        ; vbroadcastss  0x1cc6(%rip),%ymm1        # 4ce0 <_sk_callback_hsw+0x3c4>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,88,13,189,28,0,0        ; vpbroadcastd  0x1cbd(%rip),%ymm1        # 4ce4 <_sk_callback_hsw+0x3c8>
+  DB  197,237,219,201                     ; vpand         %ymm1,%ymm2,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,29,176,28,0,0        ; vbroadcastss  0x1cb0(%rip),%ymm3        # 4ce8 <_sk_callback_hsw+0x3cc>
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  196,226,125,88,29,167,28,0,0        ; vpbroadcastd  0x1ca7(%rip),%ymm3        # 4cec <_sk_callback_hsw+0x3d0>
+  DB  197,237,219,211                     ; vpand         %ymm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,226,125,24,29,154,28,0,0        ; vbroadcastss  0x1c9a(%rip),%ymm3        # 4cf0 <_sk_callback_hsw+0x3d4>
+  DB  197,236,89,211                      ; vmulps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,143,28,0,0        ; vbroadcastss  0x1c8f(%rip),%ymm3        # 4cf4 <_sk_callback_hsw+0x3d8>
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_565_hsw
+_sk_store_565_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,98,125,24,5,131,28,0,0          ; vbroadcastss  0x1c83(%rip),%ymm8        # 4cf8 <_sk_callback_hsw+0x3dc>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,193,53,114,241,11               ; vpslld        $0xb,%ymm9,%ymm9
+  DB  196,98,125,24,21,110,28,0,0         ; vbroadcastss  0x1c6e(%rip),%ymm10        # 4cfc <_sk_callback_hsw+0x3e0>
+  DB  196,65,116,89,210                   ; vmulps        %ymm10,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,45,114,242,5                ; vpslld        $0x5,%ymm10,%ymm10
+  DB  196,65,45,235,201                   ; vpor          %ymm9,%ymm10,%ymm9
+  DB  196,65,108,89,192                   ; vmulps        %ymm8,%ymm2,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,65,53,235,192                   ; vpor          %ymm8,%ymm9,%ymm8
+  DB  196,67,125,57,193,1                 ; vextracti128  $0x1,%ymm8,%xmm9
+  DB  196,66,57,43,193                    ; vpackusdw     %xmm9,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           30cc <_sk_store_565_hsw+0x65>
+  DB  196,65,122,127,4,83                 ; vmovdqu       %xmm8,(%r11,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            30c8 <_sk_store_565_hsw+0x61>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,69,0,0,0                  ; lea           0x45(%rip),%r10        # 312c <_sk_store_565_hsw+0xc5>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,67,121,21,68,83,12,6            ; vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,10,5            ; vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,8,4             ; vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,6,3             ; vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,4,2             ; vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,2,1             ; vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  DB  196,67,121,21,4,83,0                ; vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  DB  235,159                             ; jmp           30c8 <_sk_store_565_hsw+0x61>
+  DB  15,31,0                             ; nopl          (%rax)
+  DB  244                                 ; hlt
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  236                                 ; in            (%dx),%al
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,228                             ; jmpq          *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  220,255                             ; fdivr         %st,%st(7)
+  DB  255                                 ; (bad)
+  DB  255,212                             ; callq         *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,204                             ; dec           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,196                             ; inc           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_4444_hsw
+_sk_load_4444_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,138,0,0,0                    ; jne           31e0 <_sk_load_4444_hsw+0x98>
+  DB  196,193,122,111,4,83                ; vmovdqu       (%r11,%rdx,2),%xmm0
+  DB  196,226,125,51,216                  ; vpmovzxwd     %xmm0,%ymm3
+  DB  196,226,125,88,5,150,27,0,0         ; vpbroadcastd  0x1b96(%rip),%ymm0        # 4d00 <_sk_callback_hsw+0x3e4>
+  DB  197,229,219,192                     ; vpand         %ymm0,%ymm3,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,137,27,0,0        ; vbroadcastss  0x1b89(%rip),%ymm1        # 4d04 <_sk_callback_hsw+0x3e8>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,88,13,128,27,0,0        ; vpbroadcastd  0x1b80(%rip),%ymm1        # 4d08 <_sk_callback_hsw+0x3ec>
+  DB  197,229,219,201                     ; vpand         %ymm1,%ymm3,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,21,115,27,0,0        ; vbroadcastss  0x1b73(%rip),%ymm2        # 4d0c <_sk_callback_hsw+0x3f0>
+  DB  197,244,89,202                      ; vmulps        %ymm2,%ymm1,%ymm1
+  DB  196,226,125,88,21,106,27,0,0        ; vpbroadcastd  0x1b6a(%rip),%ymm2        # 4d10 <_sk_callback_hsw+0x3f4>
+  DB  197,229,219,210                     ; vpand         %ymm2,%ymm3,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,98,125,24,5,93,27,0,0           ; vbroadcastss  0x1b5d(%rip),%ymm8        # 4d14 <_sk_callback_hsw+0x3f8>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,88,5,83,27,0,0           ; vpbroadcastd  0x1b53(%rip),%ymm8        # 4d18 <_sk_callback_hsw+0x3fc>
+  DB  196,193,101,219,216                 ; vpand         %ymm8,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,98,125,24,5,69,27,0,0           ; vbroadcastss  0x1b45(%rip),%ymm8        # 4d1c <_sk_callback_hsw+0x400>
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,100,255,255,255              ; ja            315c <_sk_load_4444_hsw+0x14>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,73,0,0,0                  ; lea           0x49(%rip),%r10        # 324c <_sk_load_4444_hsw+0x104>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  196,193,121,196,68,83,12,6          ; vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,10,5          ; vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,8,4           ; vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,6,3           ; vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,4,2           ; vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,2,1           ; vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,4,83,0              ; vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  DB  233,16,255,255,255                  ; jmpq          315c <_sk_load_4444_hsw+0x14>
+  DB  244                                 ; hlt
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  236                                 ; in            (%dx),%al
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,228                             ; jmpq          *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  220,255                             ; fdivr         %st,%st(7)
+  DB  255                                 ; (bad)
+  DB  255,212                             ; callq         *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,204                             ; dec           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,192                             ; inc           %eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_gather_4444_hsw
+_sk_gather_4444_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,110,200                     ; vmovd         %eax,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,241,196,200,1                   ; vpinsrw       $0x1,%eax,%xmm1,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,2                   ; vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,241,196,200,3                   ; vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,4                   ; vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,241,196,192,5                   ; vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,196,192,6                   ; vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,249,196,192,7                   ; vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  DB  196,226,125,51,216                  ; vpmovzxwd     %xmm0,%ymm3
+  DB  196,226,125,88,5,7,26,0,0           ; vpbroadcastd  0x1a07(%rip),%ymm0        # 4d20 <_sk_callback_hsw+0x404>
+  DB  197,229,219,192                     ; vpand         %ymm0,%ymm3,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,250,25,0,0        ; vbroadcastss  0x19fa(%rip),%ymm1        # 4d24 <_sk_callback_hsw+0x408>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,88,13,241,25,0,0        ; vpbroadcastd  0x19f1(%rip),%ymm1        # 4d28 <_sk_callback_hsw+0x40c>
+  DB  197,229,219,201                     ; vpand         %ymm1,%ymm3,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,21,228,25,0,0        ; vbroadcastss  0x19e4(%rip),%ymm2        # 4d2c <_sk_callback_hsw+0x410>
+  DB  197,244,89,202                      ; vmulps        %ymm2,%ymm1,%ymm1
+  DB  196,226,125,88,21,219,25,0,0        ; vpbroadcastd  0x19db(%rip),%ymm2        # 4d30 <_sk_callback_hsw+0x414>
+  DB  197,229,219,210                     ; vpand         %ymm2,%ymm3,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,98,125,24,5,206,25,0,0          ; vbroadcastss  0x19ce(%rip),%ymm8        # 4d34 <_sk_callback_hsw+0x418>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,88,5,196,25,0,0          ; vpbroadcastd  0x19c4(%rip),%ymm8        # 4d38 <_sk_callback_hsw+0x41c>
+  DB  196,193,101,219,216                 ; vpand         %ymm8,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,98,125,24,5,182,25,0,0          ; vbroadcastss  0x19b6(%rip),%ymm8        # 4d3c <_sk_callback_hsw+0x420>
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_4444_hsw
+_sk_store_4444_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,98,125,24,5,163,25,0,0          ; vbroadcastss  0x19a3(%rip),%ymm8        # 4d40 <_sk_callback_hsw+0x424>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,193,53,114,241,12               ; vpslld        $0xc,%ymm9,%ymm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,45,114,242,8                ; vpslld        $0x8,%ymm10,%ymm10
+  DB  196,65,45,235,201                   ; vpor          %ymm9,%ymm10,%ymm9
+  DB  196,65,108,89,208                   ; vmulps        %ymm8,%ymm2,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,45,114,242,4                ; vpslld        $0x4,%ymm10,%ymm10
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,65,45,235,192                   ; vpor          %ymm8,%ymm10,%ymm8
+  DB  196,65,53,235,192                   ; vpor          %ymm8,%ymm9,%ymm8
+  DB  196,67,125,57,193,1                 ; vextracti128  $0x1,%ymm8,%xmm9
+  DB  196,66,57,43,193                    ; vpackusdw     %xmm9,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3400 <_sk_store_4444_hsw+0x71>
+  DB  196,65,122,127,4,83                 ; vmovdqu       %xmm8,(%r11,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            33fc <_sk_store_4444_hsw+0x6d>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,69,0,0,0                  ; lea           0x45(%rip),%r10        # 3460 <_sk_store_4444_hsw+0xd1>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,67,121,21,68,83,12,6            ; vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,10,5            ; vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,8,4             ; vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,6,3             ; vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,4,2             ; vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,2,1             ; vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  DB  196,67,121,21,4,83,0                ; vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  DB  235,159                             ; jmp           33fc <_sk_store_4444_hsw+0x6d>
+  DB  15,31,0                             ; nopl          (%rax)
+  DB  244                                 ; hlt
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  236                                 ; in            (%dx),%al
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,228                             ; jmpq          *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  220,255                             ; fdivr         %st,%st(7)
+  DB  255                                 ; (bad)
+  DB  255,212                             ; callq         *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,204                             ; dec           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,196                             ; inc           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_8888_hsw
+_sk_load_8888_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,88                              ; jne           34e9 <_sk_load_8888_hsw+0x6d>
+  DB  196,193,124,16,26                   ; vmovups       (%r10),%ymm3
+  DB  197,228,84,5,226,26,0,0             ; vandps        0x1ae2(%rip),%ymm3,%ymm0        # 4f80 <_sk_callback_hsw+0x664>
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,5,153,24,0,0          ; vbroadcastss  0x1899(%rip),%ymm8        # 4d44 <_sk_callback_hsw+0x428>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,226,101,0,13,231,26,0,0         ; vpshufb       0x1ae7(%rip),%ymm3,%ymm1        # 4fa0 <_sk_callback_hsw+0x684>
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,226,101,0,21,245,26,0,0         ; vpshufb       0x1af5(%rip),%ymm3,%ymm2        # 4fc0 <_sk_callback_hsw+0x6a4>
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,229,114,211,24                  ; vpsrld        $0x18,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,225,249,110,192                 ; vmovq         %rax,%xmm0
+  DB  196,226,125,33,192                  ; vpmovsxbd     %xmm0,%ymm0
+  DB  196,194,125,44,26                   ; vmaskmovps    (%r10),%ymm0,%ymm3
+  DB  235,135                             ; jmp           3496 <_sk_load_8888_hsw+0x1a>
+
+PUBLIC _sk_gather_8888_hsw
+_sk_gather_8888_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  196,194,117,144,28,129              ; vpgatherdd    %ymm1,(%r9,%ymm0,4),%ymm3
+  DB  197,229,219,5,163,26,0,0            ; vpand         0x1aa3(%rip),%ymm3,%ymm0        # 4fe0 <_sk_callback_hsw+0x6c4>
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,5,254,23,0,0          ; vbroadcastss  0x17fe(%rip),%ymm8        # 4d48 <_sk_callback_hsw+0x42c>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,226,101,0,13,168,26,0,0         ; vpshufb       0x1aa8(%rip),%ymm3,%ymm1        # 5000 <_sk_callback_hsw+0x6e4>
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,226,101,0,21,182,26,0,0         ; vpshufb       0x1ab6(%rip),%ymm3,%ymm2        # 5020 <_sk_callback_hsw+0x704>
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,229,114,211,24                  ; vpsrld        $0x18,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_8888_hsw
+_sk_store_8888_hsw LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  196,98,125,24,5,174,23,0,0          ; vbroadcastss  0x17ae(%rip),%ymm8        # 4d4c <_sk_callback_hsw+0x430>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,45,114,242,8                ; vpslld        $0x8,%ymm10,%ymm10
+  DB  196,65,45,235,201                   ; vpor          %ymm9,%ymm10,%ymm9
+  DB  196,65,108,89,208                   ; vmulps        %ymm8,%ymm2,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,45,114,242,16               ; vpslld        $0x10,%ymm10,%ymm10
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,193,61,114,240,24               ; vpslld        $0x18,%ymm8,%ymm8
+  DB  196,65,45,235,192                   ; vpor          %ymm8,%ymm10,%ymm8
+  DB  196,65,53,235,192                   ; vpor          %ymm8,%ymm9,%ymm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,12                              ; jne           35f8 <_sk_store_8888_hsw+0x73>
+  DB  196,65,124,17,2                     ; vmovups       %ymm8,(%r10)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,97,249,110,200                  ; vmovq         %rax,%xmm9
+  DB  196,66,125,33,201                   ; vpmovsxbd     %xmm9,%ymm9
+  DB  196,66,53,46,2                      ; vmaskmovps    %ymm8,%ymm9,(%r10)
+  DB  235,211                             ; jmp           35f1 <_sk_store_8888_hsw+0x6c>
+
+PUBLIC _sk_load_f16_hsw
+_sk_load_f16_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,97                              ; jne           3689 <_sk_load_f16_hsw+0x6b>
+  DB  197,121,16,4,208                    ; vmovupd       (%rax,%rdx,8),%xmm8
+  DB  197,249,16,84,208,16                ; vmovupd       0x10(%rax,%rdx,8),%xmm2
+  DB  197,249,16,92,208,32                ; vmovupd       0x20(%rax,%rdx,8),%xmm3
+  DB  197,122,111,76,208,48               ; vmovdqu       0x30(%rax,%rdx,8),%xmm9
+  DB  197,185,97,194                      ; vpunpcklwd    %xmm2,%xmm8,%xmm0
+  DB  197,185,105,210                     ; vpunpckhwd    %xmm2,%xmm8,%xmm2
+  DB  196,193,97,97,201                   ; vpunpcklwd    %xmm9,%xmm3,%xmm1
+  DB  196,193,97,105,217                  ; vpunpckhwd    %xmm9,%xmm3,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,121,105,202                     ; vpunpckhwd    %xmm2,%xmm0,%xmm9
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,241,105,219                     ; vpunpckhwd    %xmm3,%xmm1,%xmm3
+  DB  197,185,108,194                     ; vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  DB  196,226,125,19,192                  ; vcvtph2ps     %xmm0,%ymm0
+  DB  197,185,109,202                     ; vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  DB  196,226,125,19,201                  ; vcvtph2ps     %xmm1,%ymm1
+  DB  197,177,108,211                     ; vpunpcklqdq   %xmm3,%xmm9,%xmm2
+  DB  196,226,125,19,210                  ; vcvtph2ps     %xmm2,%ymm2
+  DB  197,177,109,219                     ; vpunpckhqdq   %xmm3,%xmm9,%xmm3
+  DB  196,226,125,19,219                  ; vcvtph2ps     %xmm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,123,16,4,208                    ; vmovsd        (%rax,%rdx,8),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,79                              ; je            36e8 <_sk_load_f16_hsw+0xca>
+  DB  197,57,22,68,208,8                  ; vmovhpd       0x8(%rax,%rdx,8),%xmm8,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,67                              ; jb            36e8 <_sk_load_f16_hsw+0xca>
+  DB  197,251,16,84,208,16                ; vmovsd        0x10(%rax,%rdx,8),%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  116,68                              ; je            36f5 <_sk_load_f16_hsw+0xd7>
+  DB  197,233,22,84,208,24                ; vmovhpd       0x18(%rax,%rdx,8),%xmm2,%xmm2
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,56                              ; jb            36f5 <_sk_load_f16_hsw+0xd7>
+  DB  197,251,16,92,208,32                ; vmovsd        0x20(%rax,%rdx,8),%xmm3
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  15,132,114,255,255,255              ; je            363f <_sk_load_f16_hsw+0x21>
+  DB  197,225,22,92,208,40                ; vmovhpd       0x28(%rax,%rdx,8),%xmm3,%xmm3
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  15,130,98,255,255,255               ; jb            363f <_sk_load_f16_hsw+0x21>
+  DB  197,122,126,76,208,48               ; vmovq         0x30(%rax,%rdx,8),%xmm9
+  DB  233,87,255,255,255                  ; jmpq          363f <_sk_load_f16_hsw+0x21>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  197,233,87,210                      ; vxorpd        %xmm2,%xmm2,%xmm2
+  DB  233,74,255,255,255                  ; jmpq          363f <_sk_load_f16_hsw+0x21>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  233,65,255,255,255                  ; jmpq          363f <_sk_load_f16_hsw+0x21>
+
+PUBLIC _sk_gather_f16_hsw
+_sk_gather_f16_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,226,125,88,80,16                ; vpbroadcastd  0x10(%rax),%ymm2
+  DB  196,226,109,64,201                  ; vpmulld       %ymm1,%ymm2,%ymm1
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  197,245,254,192                     ; vpaddd        %ymm0,%ymm1,%ymm0
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  197,237,118,210                     ; vpcmpeqd      %ymm2,%ymm2,%ymm2
+  DB  196,194,237,144,28,193              ; vpgatherdq    %ymm2,(%r9,%xmm0,8),%ymm3
+  DB  196,227,125,57,192,1                ; vextracti128  $0x1,%ymm0,%xmm0
+  DB  196,194,245,144,20,193              ; vpgatherdq    %ymm1,(%r9,%xmm0,8),%ymm2
+  DB  196,227,125,57,216,1                ; vextracti128  $0x1,%ymm3,%xmm0
+  DB  196,227,125,57,209,1                ; vextracti128  $0x1,%ymm2,%xmm1
+  DB  197,97,97,192                       ; vpunpcklwd    %xmm0,%xmm3,%xmm8
+  DB  197,225,105,192                     ; vpunpckhwd    %xmm0,%xmm3,%xmm0
+  DB  197,233,97,217                      ; vpunpcklwd    %xmm1,%xmm2,%xmm3
+  DB  197,233,105,201                     ; vpunpckhwd    %xmm1,%xmm2,%xmm1
+  DB  197,57,97,200                       ; vpunpcklwd    %xmm0,%xmm8,%xmm9
+  DB  197,57,105,192                      ; vpunpckhwd    %xmm0,%xmm8,%xmm8
+  DB  197,225,97,209                      ; vpunpcklwd    %xmm1,%xmm3,%xmm2
+  DB  197,225,105,217                     ; vpunpckhwd    %xmm1,%xmm3,%xmm3
+  DB  197,177,108,194                     ; vpunpcklqdq   %xmm2,%xmm9,%xmm0
+  DB  196,226,125,19,192                  ; vcvtph2ps     %xmm0,%ymm0
+  DB  197,177,109,202                     ; vpunpckhqdq   %xmm2,%xmm9,%xmm1
+  DB  196,226,125,19,201                  ; vcvtph2ps     %xmm1,%ymm1
+  DB  197,185,108,211                     ; vpunpcklqdq   %xmm3,%xmm8,%xmm2
+  DB  196,226,125,19,210                  ; vcvtph2ps     %xmm2,%ymm2
+  DB  197,185,109,219                     ; vpunpckhqdq   %xmm3,%xmm8,%xmm3
+  DB  196,226,125,19,219                  ; vcvtph2ps     %xmm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_f16_hsw
+_sk_store_f16_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  196,195,125,29,192,4                ; vcvtps2ph     $0x4,%ymm0,%xmm8
+  DB  196,195,125,29,201,4                ; vcvtps2ph     $0x4,%ymm1,%xmm9
+  DB  196,195,125,29,210,4                ; vcvtps2ph     $0x4,%ymm2,%xmm10
+  DB  196,195,125,29,219,4                ; vcvtps2ph     $0x4,%ymm3,%xmm11
+  DB  196,65,57,97,225                    ; vpunpcklwd    %xmm9,%xmm8,%xmm12
+  DB  196,65,57,105,193                   ; vpunpckhwd    %xmm9,%xmm8,%xmm8
+  DB  196,65,41,97,203                    ; vpunpcklwd    %xmm11,%xmm10,%xmm9
+  DB  196,65,41,105,235                   ; vpunpckhwd    %xmm11,%xmm10,%xmm13
+  DB  196,65,25,98,217                    ; vpunpckldq    %xmm9,%xmm12,%xmm11
+  DB  196,65,25,106,209                   ; vpunpckhdq    %xmm9,%xmm12,%xmm10
+  DB  196,65,57,98,205                    ; vpunpckldq    %xmm13,%xmm8,%xmm9
+  DB  196,65,57,106,197                   ; vpunpckhdq    %xmm13,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,27                              ; jne           37ed <_sk_store_f16_hsw+0x65>
+  DB  197,120,17,28,208                   ; vmovups       %xmm11,(%rax,%rdx,8)
+  DB  197,120,17,84,208,16                ; vmovups       %xmm10,0x10(%rax,%rdx,8)
+  DB  197,120,17,76,208,32                ; vmovups       %xmm9,0x20(%rax,%rdx,8)
+  DB  197,122,127,68,208,48               ; vmovdqu       %xmm8,0x30(%rax,%rdx,8)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,121,214,28,208                  ; vmovq         %xmm11,(%rax,%rdx,8)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,241                             ; je            37e9 <_sk_store_f16_hsw+0x61>
+  DB  197,121,23,92,208,8                 ; vmovhpd       %xmm11,0x8(%rax,%rdx,8)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,229                             ; jb            37e9 <_sk_store_f16_hsw+0x61>
+  DB  197,121,214,84,208,16               ; vmovq         %xmm10,0x10(%rax,%rdx,8)
+  DB  116,221                             ; je            37e9 <_sk_store_f16_hsw+0x61>
+  DB  197,121,23,84,208,24                ; vmovhpd       %xmm10,0x18(%rax,%rdx,8)
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,209                             ; jb            37e9 <_sk_store_f16_hsw+0x61>
+  DB  197,121,214,76,208,32               ; vmovq         %xmm9,0x20(%rax,%rdx,8)
+  DB  116,201                             ; je            37e9 <_sk_store_f16_hsw+0x61>
+  DB  197,121,23,76,208,40                ; vmovhpd       %xmm9,0x28(%rax,%rdx,8)
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,189                             ; jb            37e9 <_sk_store_f16_hsw+0x61>
+  DB  197,121,214,68,208,48               ; vmovq         %xmm8,0x30(%rax,%rdx,8)
+  DB  235,181                             ; jmp           37e9 <_sk_store_f16_hsw+0x61>
+
+PUBLIC _sk_load_u16_be_hsw
+_sk_load_u16_be_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,204,0,0,0                    ; jne           3916 <_sk_load_u16_be_hsw+0xe2>
+  DB  196,65,121,16,4,65                  ; vmovupd       (%r9,%rax,2),%xmm8
+  DB  196,193,121,16,84,65,16             ; vmovupd       0x10(%r9,%rax,2),%xmm2
+  DB  196,193,121,16,92,65,32             ; vmovupd       0x20(%r9,%rax,2),%xmm3
+  DB  196,65,122,111,76,65,48             ; vmovdqu       0x30(%r9,%rax,2),%xmm9
+  DB  197,185,97,194                      ; vpunpcklwd    %xmm2,%xmm8,%xmm0
+  DB  197,185,105,210                     ; vpunpckhwd    %xmm2,%xmm8,%xmm2
+  DB  196,193,97,97,201                   ; vpunpcklwd    %xmm9,%xmm3,%xmm1
+  DB  196,193,97,105,217                  ; vpunpckhwd    %xmm9,%xmm3,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,121,105,202                     ; vpunpckhwd    %xmm2,%xmm0,%xmm9
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,113,105,219                     ; vpunpckhwd    %xmm3,%xmm1,%xmm11
+  DB  197,185,108,194                     ; vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  DB  197,241,113,240,8                   ; vpsllw        $0x8,%xmm0,%xmm1
+  DB  197,249,113,208,8                   ; vpsrlw        $0x8,%xmm0,%xmm0
+  DB  197,241,235,192                     ; vpor          %xmm0,%xmm1,%xmm0
+  DB  196,226,125,51,192                  ; vpmovzxwd     %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,21,165,20,0,0         ; vbroadcastss  0x14a5(%rip),%ymm10        # 4d50 <_sk_callback_hsw+0x434>
+  DB  196,193,124,89,194                  ; vmulps        %ymm10,%ymm0,%ymm0
+  DB  197,185,109,202                     ; vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  DB  197,233,113,241,8                   ; vpsllw        $0x8,%xmm1,%xmm2
+  DB  197,241,113,209,8                   ; vpsrlw        $0x8,%xmm1,%xmm1
+  DB  197,233,235,201                     ; vpor          %xmm1,%xmm2,%xmm1
+  DB  196,226,125,51,201                  ; vpmovzxwd     %xmm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,202                  ; vmulps        %ymm10,%ymm1,%ymm1
+  DB  196,193,49,108,211                  ; vpunpcklqdq   %xmm11,%xmm9,%xmm2
+  DB  197,225,113,242,8                   ; vpsllw        $0x8,%xmm2,%xmm3
+  DB  197,233,113,210,8                   ; vpsrlw        $0x8,%xmm2,%xmm2
+  DB  197,225,235,210                     ; vpor          %xmm2,%xmm3,%xmm2
+  DB  196,226,125,51,210                  ; vpmovzxwd     %xmm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,210                  ; vmulps        %ymm10,%ymm2,%ymm2
+  DB  196,193,49,109,219                  ; vpunpckhqdq   %xmm11,%xmm9,%xmm3
+  DB  197,185,113,243,8                   ; vpsllw        $0x8,%xmm3,%xmm8
+  DB  197,225,113,211,8                   ; vpsrlw        $0x8,%xmm3,%xmm3
+  DB  197,185,235,219                     ; vpor          %xmm3,%xmm8,%xmm3
+  DB  196,226,125,51,219                  ; vpmovzxwd     %xmm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,218                  ; vmulps        %ymm10,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,123,16,4,65                  ; vmovsd        (%r9,%rax,2),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,85                              ; je            397c <_sk_load_u16_be_hsw+0x148>
+  DB  196,65,57,22,68,65,8                ; vmovhpd       0x8(%r9,%rax,2),%xmm8,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,72                              ; jb            397c <_sk_load_u16_be_hsw+0x148>
+  DB  196,193,123,16,84,65,16             ; vmovsd        0x10(%r9,%rax,2),%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  116,72                              ; je            3989 <_sk_load_u16_be_hsw+0x155>
+  DB  196,193,105,22,84,65,24             ; vmovhpd       0x18(%r9,%rax,2),%xmm2,%xmm2
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,59                              ; jb            3989 <_sk_load_u16_be_hsw+0x155>
+  DB  196,193,123,16,92,65,32             ; vmovsd        0x20(%r9,%rax,2),%xmm3
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  15,132,6,255,255,255                ; je            3865 <_sk_load_u16_be_hsw+0x31>
+  DB  196,193,97,22,92,65,40              ; vmovhpd       0x28(%r9,%rax,2),%xmm3,%xmm3
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  15,130,245,254,255,255              ; jb            3865 <_sk_load_u16_be_hsw+0x31>
+  DB  196,65,122,126,76,65,48             ; vmovq         0x30(%r9,%rax,2),%xmm9
+  DB  233,233,254,255,255                 ; jmpq          3865 <_sk_load_u16_be_hsw+0x31>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  197,233,87,210                      ; vxorpd        %xmm2,%xmm2,%xmm2
+  DB  233,220,254,255,255                 ; jmpq          3865 <_sk_load_u16_be_hsw+0x31>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  233,211,254,255,255                 ; jmpq          3865 <_sk_load_u16_be_hsw+0x31>
+
+PUBLIC _sk_load_rgb_u16_be_hsw
+_sk_load_rgb_u16_be_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,82                         ; lea           (%rdx,%rdx,2),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,204,0,0,0                    ; jne           3a70 <_sk_load_rgb_u16_be_hsw+0xde>
+  DB  196,193,122,111,4,65                ; vmovdqu       (%r9,%rax,2),%xmm0
+  DB  196,193,122,111,84,65,12            ; vmovdqu       0xc(%r9,%rax,2),%xmm2
+  DB  196,193,122,111,76,65,24            ; vmovdqu       0x18(%r9,%rax,2),%xmm1
+  DB  196,193,122,111,92,65,32            ; vmovdqu       0x20(%r9,%rax,2),%xmm3
+  DB  197,225,115,219,4                   ; vpsrldq       $0x4,%xmm3,%xmm3
+  DB  197,185,115,216,6                   ; vpsrldq       $0x6,%xmm0,%xmm8
+  DB  197,177,115,218,6                   ; vpsrldq       $0x6,%xmm2,%xmm9
+  DB  197,161,115,217,6                   ; vpsrldq       $0x6,%xmm1,%xmm11
+  DB  197,169,115,219,6                   ; vpsrldq       $0x6,%xmm3,%xmm10
+  DB  197,249,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm0
+  DB  196,193,57,97,209                   ; vpunpcklwd    %xmm9,%xmm8,%xmm2
+  DB  197,241,97,203                      ; vpunpcklwd    %xmm3,%xmm1,%xmm1
+  DB  196,193,33,97,218                   ; vpunpcklwd    %xmm10,%xmm11,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,121,105,202                     ; vpunpckhwd    %xmm2,%xmm0,%xmm9
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,241,105,219                     ; vpunpckhwd    %xmm3,%xmm1,%xmm3
+  DB  197,185,108,194                     ; vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  DB  197,241,113,240,8                   ; vpsllw        $0x8,%xmm0,%xmm1
+  DB  197,249,113,208,8                   ; vpsrlw        $0x8,%xmm0,%xmm0
+  DB  197,241,235,192                     ; vpor          %xmm0,%xmm1,%xmm0
+  DB  196,226,125,51,192                  ; vpmovzxwd     %xmm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,21,54,19,0,0          ; vbroadcastss  0x1336(%rip),%ymm10        # 4d54 <_sk_callback_hsw+0x438>
+  DB  196,193,124,89,194                  ; vmulps        %ymm10,%ymm0,%ymm0
+  DB  197,185,109,202                     ; vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  DB  197,233,113,241,8                   ; vpsllw        $0x8,%xmm1,%xmm2
+  DB  197,241,113,209,8                   ; vpsrlw        $0x8,%xmm1,%xmm1
+  DB  197,233,235,201                     ; vpor          %xmm1,%xmm2,%xmm1
+  DB  196,226,125,51,201                  ; vpmovzxwd     %xmm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,202                  ; vmulps        %ymm10,%ymm1,%ymm1
+  DB  197,177,108,211                     ; vpunpcklqdq   %xmm3,%xmm9,%xmm2
+  DB  197,225,113,242,8                   ; vpsllw        $0x8,%xmm2,%xmm3
+  DB  197,233,113,210,8                   ; vpsrlw        $0x8,%xmm2,%xmm2
+  DB  197,225,235,210                     ; vpor          %xmm2,%xmm3,%xmm2
+  DB  196,226,125,51,210                  ; vpmovzxwd     %xmm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,210                  ; vmulps        %ymm10,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,234,18,0,0        ; vbroadcastss  0x12ea(%rip),%ymm3        # 4d58 <_sk_callback_hsw+0x43c>
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,193,121,110,4,65                ; vmovd         (%r9,%rax,2),%xmm0
+  DB  196,193,121,196,68,65,4,2           ; vpinsrw       $0x2,0x4(%r9,%rax,2),%xmm0,%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,5                               ; jne           3a89 <_sk_load_rgb_u16_be_hsw+0xf7>
+  DB  233,79,255,255,255                  ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+  DB  196,193,121,110,76,65,6             ; vmovd         0x6(%r9,%rax,2),%xmm1
+  DB  196,65,113,196,68,65,10,2           ; vpinsrw       $0x2,0xa(%r9,%rax,2),%xmm1,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,26                              ; jb            3ab8 <_sk_load_rgb_u16_be_hsw+0x126>
+  DB  196,193,121,110,76,65,12            ; vmovd         0xc(%r9,%rax,2),%xmm1
+  DB  196,193,113,196,84,65,16,2          ; vpinsrw       $0x2,0x10(%r9,%rax,2),%xmm1,%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  117,10                              ; jne           3abd <_sk_load_rgb_u16_be_hsw+0x12b>
+  DB  233,32,255,255,255                  ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+  DB  233,27,255,255,255                  ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+  DB  196,193,121,110,76,65,18            ; vmovd         0x12(%r9,%rax,2),%xmm1
+  DB  196,65,113,196,76,65,22,2           ; vpinsrw       $0x2,0x16(%r9,%rax,2),%xmm1,%xmm9
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,26                              ; jb            3aec <_sk_load_rgb_u16_be_hsw+0x15a>
+  DB  196,193,121,110,76,65,24            ; vmovd         0x18(%r9,%rax,2),%xmm1
+  DB  196,193,113,196,76,65,28,2          ; vpinsrw       $0x2,0x1c(%r9,%rax,2),%xmm1,%xmm1
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  117,10                              ; jne           3af1 <_sk_load_rgb_u16_be_hsw+0x15f>
+  DB  233,236,254,255,255                 ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+  DB  233,231,254,255,255                 ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+  DB  196,193,121,110,92,65,30            ; vmovd         0x1e(%r9,%rax,2),%xmm3
+  DB  196,65,97,196,92,65,34,2            ; vpinsrw       $0x2,0x22(%r9,%rax,2),%xmm3,%xmm11
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,20                              ; jb            3b1a <_sk_load_rgb_u16_be_hsw+0x188>
+  DB  196,193,121,110,92,65,36            ; vmovd         0x24(%r9,%rax,2),%xmm3
+  DB  196,193,97,196,92,65,40,2           ; vpinsrw       $0x2,0x28(%r9,%rax,2),%xmm3,%xmm3
+  DB  233,190,254,255,255                 ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+  DB  233,185,254,255,255                 ; jmpq          39d8 <_sk_load_rgb_u16_be_hsw+0x46>
+
+PUBLIC _sk_store_u16_be_hsw
+_sk_store_u16_be_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  196,98,125,24,5,39,18,0,0           ; vbroadcastss  0x1227(%rip),%ymm8        # 4d5c <_sk_callback_hsw+0x440>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,67,125,25,202,1                 ; vextractf128  $0x1,%ymm9,%xmm10
+  DB  196,66,49,43,202                    ; vpackusdw     %xmm10,%xmm9,%xmm9
+  DB  196,193,41,113,241,8                ; vpsllw        $0x8,%xmm9,%xmm10
+  DB  196,193,49,113,209,8                ; vpsrlw        $0x8,%xmm9,%xmm9
+  DB  196,65,41,235,201                   ; vpor          %xmm9,%xmm10,%xmm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,67,125,25,211,1                 ; vextractf128  $0x1,%ymm10,%xmm11
+  DB  196,66,41,43,211                    ; vpackusdw     %xmm11,%xmm10,%xmm10
+  DB  196,193,33,113,242,8                ; vpsllw        $0x8,%xmm10,%xmm11
+  DB  196,193,41,113,210,8                ; vpsrlw        $0x8,%xmm10,%xmm10
+  DB  196,65,33,235,210                   ; vpor          %xmm10,%xmm11,%xmm10
+  DB  196,65,108,89,216                   ; vmulps        %ymm8,%ymm2,%ymm11
+  DB  196,65,125,91,219                   ; vcvtps2dq     %ymm11,%ymm11
+  DB  196,67,125,25,220,1                 ; vextractf128  $0x1,%ymm11,%xmm12
+  DB  196,66,33,43,220                    ; vpackusdw     %xmm12,%xmm11,%xmm11
+  DB  196,193,25,113,243,8                ; vpsllw        $0x8,%xmm11,%xmm12
+  DB  196,193,33,113,211,8                ; vpsrlw        $0x8,%xmm11,%xmm11
+  DB  196,65,25,235,219                   ; vpor          %xmm11,%xmm12,%xmm11
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,67,125,25,196,1                 ; vextractf128  $0x1,%ymm8,%xmm12
+  DB  196,66,57,43,196                    ; vpackusdw     %xmm12,%xmm8,%xmm8
+  DB  196,193,25,113,240,8                ; vpsllw        $0x8,%xmm8,%xmm12
+  DB  196,193,57,113,208,8                ; vpsrlw        $0x8,%xmm8,%xmm8
+  DB  196,65,25,235,192                   ; vpor          %xmm8,%xmm12,%xmm8
+  DB  196,65,49,97,226                    ; vpunpcklwd    %xmm10,%xmm9,%xmm12
+  DB  196,65,49,105,234                   ; vpunpckhwd    %xmm10,%xmm9,%xmm13
+  DB  196,65,33,97,200                    ; vpunpcklwd    %xmm8,%xmm11,%xmm9
+  DB  196,65,33,105,192                   ; vpunpckhwd    %xmm8,%xmm11,%xmm8
+  DB  196,65,25,98,217                    ; vpunpckldq    %xmm9,%xmm12,%xmm11
+  DB  196,65,25,106,209                   ; vpunpckhdq    %xmm9,%xmm12,%xmm10
+  DB  196,65,17,98,200                    ; vpunpckldq    %xmm8,%xmm13,%xmm9
+  DB  196,65,17,106,192                   ; vpunpckhdq    %xmm8,%xmm13,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,31                              ; jne           3c19 <_sk_store_u16_be_hsw+0xfa>
+  DB  196,65,120,17,28,65                 ; vmovups       %xmm11,(%r9,%rax,2)
+  DB  196,65,120,17,84,65,16              ; vmovups       %xmm10,0x10(%r9,%rax,2)
+  DB  196,65,120,17,76,65,32              ; vmovups       %xmm9,0x20(%r9,%rax,2)
+  DB  196,65,122,127,68,65,48             ; vmovdqu       %xmm8,0x30(%r9,%rax,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,121,214,28,65                ; vmovq         %xmm11,(%r9,%rax,2)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            3c15 <_sk_store_u16_be_hsw+0xf6>
+  DB  196,65,121,23,92,65,8               ; vmovhpd       %xmm11,0x8(%r9,%rax,2)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            3c15 <_sk_store_u16_be_hsw+0xf6>
+  DB  196,65,121,214,84,65,16             ; vmovq         %xmm10,0x10(%r9,%rax,2)
+  DB  116,218                             ; je            3c15 <_sk_store_u16_be_hsw+0xf6>
+  DB  196,65,121,23,84,65,24              ; vmovhpd       %xmm10,0x18(%r9,%rax,2)
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,205                             ; jb            3c15 <_sk_store_u16_be_hsw+0xf6>
+  DB  196,65,121,214,76,65,32             ; vmovq         %xmm9,0x20(%r9,%rax,2)
+  DB  116,196                             ; je            3c15 <_sk_store_u16_be_hsw+0xf6>
+  DB  196,65,121,23,76,65,40              ; vmovhpd       %xmm9,0x28(%r9,%rax,2)
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,183                             ; jb            3c15 <_sk_store_u16_be_hsw+0xf6>
+  DB  196,65,121,214,68,65,48             ; vmovq         %xmm8,0x30(%r9,%rax,2)
+  DB  235,174                             ; jmp           3c15 <_sk_store_u16_be_hsw+0xf6>
+
+PUBLIC _sk_load_f32_hsw
+_sk_load_f32_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  119,110                             ; ja            3cdd <_sk_load_f32_hsw+0x76>
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,141,29,135,0,0,0                 ; lea           0x87(%rip),%r11        # 3d08 <_sk_load_f32_hsw+0xa1>
+  DB  75,99,4,131                         ; movslq        (%r11,%r8,4),%rax
+  DB  76,1,216                            ; add           %r11,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,3,125,24,68,145,112,1           ; vinsertf128   $0x1,0x70(%r9,%r10,4),%ymm0,%ymm8
+  DB  196,131,125,24,92,145,96,1          ; vinsertf128   $0x1,0x60(%r9,%r10,4),%ymm0,%ymm3
+  DB  196,131,125,24,76,145,80,1          ; vinsertf128   $0x1,0x50(%r9,%r10,4),%ymm0,%ymm1
+  DB  196,131,125,24,84,145,64,1          ; vinsertf128   $0x1,0x40(%r9,%r10,4),%ymm0,%ymm2
+  DB  196,129,121,16,68,145,48            ; vmovupd       0x30(%r9,%r10,4),%xmm0
+  DB  196,195,125,13,192,12               ; vblendpd      $0xc,%ymm8,%ymm0,%ymm0
+  DB  196,1,121,16,68,145,32              ; vmovupd       0x20(%r9,%r10,4),%xmm8
+  DB  196,99,61,13,203,12                 ; vblendpd      $0xc,%ymm3,%ymm8,%ymm9
+  DB  196,129,121,16,92,145,16            ; vmovupd       0x10(%r9,%r10,4),%xmm3
+  DB  196,99,101,13,209,12                ; vblendpd      $0xc,%ymm1,%ymm3,%ymm10
+  DB  196,129,121,16,12,145               ; vmovupd       (%r9,%r10,4),%xmm1
+  DB  196,227,117,13,202,12               ; vblendpd      $0xc,%ymm2,%ymm1,%ymm1
+  DB  196,193,116,20,210                  ; vunpcklps     %ymm10,%ymm1,%ymm2
+  DB  196,193,116,21,218                  ; vunpckhps     %ymm10,%ymm1,%ymm3
+  DB  197,180,20,200                      ; vunpcklps     %ymm0,%ymm9,%ymm1
+  DB  197,52,21,192                       ; vunpckhps     %ymm0,%ymm9,%ymm8
+  DB  197,237,20,193                      ; vunpcklpd     %ymm1,%ymm2,%ymm0
+  DB  197,237,21,201                      ; vunpckhpd     %ymm1,%ymm2,%ymm1
+  DB  196,193,101,20,208                  ; vunpcklpd     %ymm8,%ymm3,%ymm2
+  DB  196,193,101,21,216                  ; vunpckhpd     %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  15,31,0                             ; nopl          (%rax)
+  DB  130                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,201                             ; dec           %ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  188,255,255,255,175                 ; mov           $0xafffffff,%esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,162,255,255,255,154             ; jmpq          *-0x65000001(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,146,255,255,255,138             ; callq         *-0x75000001(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_store_f32_hsw
+_sk_store_f32_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  197,124,20,193                      ; vunpcklps     %ymm1,%ymm0,%ymm8
+  DB  197,124,21,217                      ; vunpckhps     %ymm1,%ymm0,%ymm11
+  DB  197,108,20,203                      ; vunpcklps     %ymm3,%ymm2,%ymm9
+  DB  197,108,21,227                      ; vunpckhps     %ymm3,%ymm2,%ymm12
+  DB  196,65,61,20,209                    ; vunpcklpd     %ymm9,%ymm8,%ymm10
+  DB  196,65,61,21,201                    ; vunpckhpd     %ymm9,%ymm8,%ymm9
+  DB  196,65,37,20,196                    ; vunpcklpd     %ymm12,%ymm11,%ymm8
+  DB  196,65,37,21,220                    ; vunpckhpd     %ymm12,%ymm11,%ymm11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,55                              ; jne           3d95 <_sk_store_f32_hsw+0x6d>
+  DB  196,67,45,24,225,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
+  DB  196,67,61,24,235,1                  ; vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
+  DB  196,67,45,6,201,49                  ; vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
+  DB  196,67,61,6,195,49                  ; vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
+  DB  196,65,125,17,36,129                ; vmovupd       %ymm12,(%r9,%rax,4)
+  DB  196,65,125,17,108,129,32            ; vmovupd       %ymm13,0x20(%r9,%rax,4)
+  DB  196,65,125,17,76,129,64             ; vmovupd       %ymm9,0x40(%r9,%rax,4)
+  DB  196,65,125,17,68,129,96             ; vmovupd       %ymm8,0x60(%r9,%rax,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,121,17,20,129                ; vmovupd       %xmm10,(%r9,%rax,4)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            3d91 <_sk_store_f32_hsw+0x69>
+  DB  196,65,121,17,76,129,16             ; vmovupd       %xmm9,0x10(%r9,%rax,4)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            3d91 <_sk_store_f32_hsw+0x69>
+  DB  196,65,121,17,68,129,32             ; vmovupd       %xmm8,0x20(%r9,%rax,4)
+  DB  116,218                             ; je            3d91 <_sk_store_f32_hsw+0x69>
+  DB  196,65,121,17,92,129,48             ; vmovupd       %xmm11,0x30(%r9,%rax,4)
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,205                             ; jb            3d91 <_sk_store_f32_hsw+0x69>
+  DB  196,67,125,25,84,129,64,1           ; vextractf128  $0x1,%ymm10,0x40(%r9,%rax,4)
+  DB  116,195                             ; je            3d91 <_sk_store_f32_hsw+0x69>
+  DB  196,67,125,25,76,129,80,1           ; vextractf128  $0x1,%ymm9,0x50(%r9,%rax,4)
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,181                             ; jb            3d91 <_sk_store_f32_hsw+0x69>
+  DB  196,67,125,25,68,129,96,1           ; vextractf128  $0x1,%ymm8,0x60(%r9,%rax,4)
+  DB  235,171                             ; jmp           3d91 <_sk_store_f32_hsw+0x69>
+
+PUBLIC _sk_clamp_x_hsw
+_sk_clamp_x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,192                      ; vmaxps        %ymm0,%ymm8,%ymm0
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_y_hsw
+_sk_clamp_y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,201                      ; vmaxps        %ymm1,%ymm8,%ymm1
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,193,116,93,200                  ; vminps        %ymm8,%ymm1,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_hsw
+_sk_repeat_x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,8                     ; vbroadcastss  (%rax),%ymm9
+  DB  196,65,124,94,193                   ; vdivps        %ymm9,%ymm0,%ymm8
+  DB  196,67,125,8,192,1                  ; vroundps      $0x1,%ymm8,%ymm8
+  DB  196,98,53,172,192                   ; vfnmadd213ps  %ymm0,%ymm9,%ymm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_y_hsw
+_sk_repeat_y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,8                     ; vbroadcastss  (%rax),%ymm9
+  DB  196,65,116,94,193                   ; vdivps        %ymm9,%ymm1,%ymm8
+  DB  196,67,125,8,192,1                  ; vroundps      $0x1,%ymm8,%ymm8
+  DB  196,98,53,172,193                   ; vfnmadd213ps  %ymm1,%ymm9,%ymm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,193                      ; vmovaps       %ymm8,%ymm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_hsw
+_sk_mirror_x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,122,16,0                        ; vmovss        (%rax),%xmm8
+  DB  196,66,125,24,200                   ; vbroadcastss  %xmm8,%ymm9
+  DB  196,65,124,92,209                   ; vsubps        %ymm9,%ymm0,%ymm10
+  DB  196,193,58,88,192                   ; vaddss        %xmm8,%xmm8,%xmm0
+  DB  196,226,125,24,192                  ; vbroadcastss  %xmm0,%ymm0
+  DB  197,44,94,192                       ; vdivps        %ymm0,%ymm10,%ymm8
+  DB  196,67,125,8,192,1                  ; vroundps      $0x1,%ymm8,%ymm8
+  DB  196,66,125,172,194                  ; vfnmadd213ps  %ymm10,%ymm0,%ymm8
+  DB  196,193,60,92,193                   ; vsubps        %ymm9,%ymm8,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,192                       ; vsubps        %ymm0,%ymm8,%ymm8
+  DB  197,188,84,192                      ; vandps        %ymm0,%ymm8,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_y_hsw
+_sk_mirror_y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,122,16,0                        ; vmovss        (%rax),%xmm8
+  DB  196,66,125,24,200                   ; vbroadcastss  %xmm8,%ymm9
+  DB  196,65,116,92,209                   ; vsubps        %ymm9,%ymm1,%ymm10
+  DB  196,193,58,88,200                   ; vaddss        %xmm8,%xmm8,%xmm1
+  DB  196,226,125,24,201                  ; vbroadcastss  %xmm1,%ymm1
+  DB  197,44,94,193                       ; vdivps        %ymm1,%ymm10,%ymm8
+  DB  196,67,125,8,192,1                  ; vroundps      $0x1,%ymm8,%ymm8
+  DB  196,66,117,172,194                  ; vfnmadd213ps  %ymm10,%ymm1,%ymm8
+  DB  196,193,60,92,201                   ; vsubps        %ymm9,%ymm8,%ymm1
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,193                       ; vsubps        %ymm1,%ymm8,%ymm8
+  DB  197,188,84,201                      ; vandps        %ymm1,%ymm8,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_x_1_hsw
+_sk_clamp_x_1_hsw LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,192                      ; vmaxps        %ymm0,%ymm8,%ymm0
+  DB  196,98,125,24,5,122,14,0,0          ; vbroadcastss  0xe7a(%rip),%ymm8        # 4d60 <_sk_callback_hsw+0x444>
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_1_hsw
+_sk_repeat_x_1_hsw LABEL PROC
+  DB  196,99,125,8,192,1                  ; vroundps      $0x1,%ymm0,%ymm8
+  DB  196,193,124,92,192                  ; vsubps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_1_hsw
+_sk_mirror_x_1_hsw LABEL PROC
+  DB  196,98,125,24,5,93,14,0,0           ; vbroadcastss  0xe5d(%rip),%ymm8        # 4d64 <_sk_callback_hsw+0x448>
+  DB  196,193,124,88,192                  ; vaddps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,13,83,14,0,0          ; vbroadcastss  0xe53(%rip),%ymm9        # 4d68 <_sk_callback_hsw+0x44c>
+  DB  196,65,124,89,201                   ; vmulps        %ymm9,%ymm0,%ymm9
+  DB  196,67,125,8,201,1                  ; vroundps      $0x1,%ymm9,%ymm9
+  DB  196,65,52,88,201                    ; vaddps        %ymm9,%ymm9,%ymm9
+  DB  196,193,124,92,193                  ; vsubps        %ymm9,%ymm0,%ymm0
+  DB  196,193,124,88,192                  ; vaddps        %ymm8,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,192                       ; vsubps        %ymm0,%ymm8,%ymm8
+  DB  197,188,84,192                      ; vandps        %ymm0,%ymm8,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminance_to_alpha_hsw
+_sk_luminance_to_alpha_hsw LABEL PROC
+  DB  196,226,125,24,29,35,14,0,0         ; vbroadcastss  0xe23(%rip),%ymm3        # 4d6c <_sk_callback_hsw+0x450>
+  DB  196,98,125,24,5,30,14,0,0           ; vbroadcastss  0xe1e(%rip),%ymm8        # 4d70 <_sk_callback_hsw+0x454>
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,226,125,184,203                 ; vfmadd231ps   %ymm3,%ymm0,%ymm1
+  DB  196,226,125,24,29,15,14,0,0         ; vbroadcastss  0xe0f(%rip),%ymm3        # 4d74 <_sk_callback_hsw+0x458>
+  DB  196,226,109,168,217                 ; vfmadd213ps   %ymm1,%ymm2,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_2x3_hsw
+_sk_matrix_2x3_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,8                     ; vbroadcastss  (%rax),%ymm9
+  DB  196,98,125,24,80,8                  ; vbroadcastss  0x8(%rax),%ymm10
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,66,117,184,194                  ; vfmadd231ps   %ymm10,%ymm1,%ymm8
+  DB  196,66,125,184,193                  ; vfmadd231ps   %ymm9,%ymm0,%ymm8
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,12                 ; vbroadcastss  0xc(%rax),%ymm11
+  DB  196,98,125,24,72,20                 ; vbroadcastss  0x14(%rax),%ymm9
+  DB  196,66,117,184,203                  ; vfmadd231ps   %ymm11,%ymm1,%ymm9
+  DB  196,66,125,184,202                  ; vfmadd231ps   %ymm10,%ymm0,%ymm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_3x4_hsw
+_sk_matrix_3x4_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,8                     ; vbroadcastss  (%rax),%ymm9
+  DB  196,98,125,24,80,12                 ; vbroadcastss  0xc(%rax),%ymm10
+  DB  196,98,125,24,88,24                 ; vbroadcastss  0x18(%rax),%ymm11
+  DB  196,98,125,24,64,36                 ; vbroadcastss  0x24(%rax),%ymm8
+  DB  196,66,109,184,195                  ; vfmadd231ps   %ymm11,%ymm2,%ymm8
+  DB  196,66,117,184,194                  ; vfmadd231ps   %ymm10,%ymm1,%ymm8
+  DB  196,66,125,184,193                  ; vfmadd231ps   %ymm9,%ymm0,%ymm8
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,16                 ; vbroadcastss  0x10(%rax),%ymm11
+  DB  196,98,125,24,96,28                 ; vbroadcastss  0x1c(%rax),%ymm12
+  DB  196,98,125,24,72,40                 ; vbroadcastss  0x28(%rax),%ymm9
+  DB  196,66,109,184,204                  ; vfmadd231ps   %ymm12,%ymm2,%ymm9
+  DB  196,66,117,184,203                  ; vfmadd231ps   %ymm11,%ymm1,%ymm9
+  DB  196,66,125,184,202                  ; vfmadd231ps   %ymm10,%ymm0,%ymm9
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  196,98,125,24,96,20                 ; vbroadcastss  0x14(%rax),%ymm12
+  DB  196,98,125,24,104,32                ; vbroadcastss  0x20(%rax),%ymm13
+  DB  196,98,125,24,80,44                 ; vbroadcastss  0x2c(%rax),%ymm10
+  DB  196,66,109,184,213                  ; vfmadd231ps   %ymm13,%ymm2,%ymm10
+  DB  196,66,117,184,212                  ; vfmadd231ps   %ymm12,%ymm1,%ymm10
+  DB  196,66,125,184,211                  ; vfmadd231ps   %ymm11,%ymm0,%ymm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  197,124,41,210                      ; vmovaps       %ymm10,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x5_hsw
+_sk_matrix_4x5_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,8                     ; vbroadcastss  (%rax),%ymm9
+  DB  196,98,125,24,80,16                 ; vbroadcastss  0x10(%rax),%ymm10
+  DB  196,98,125,24,88,32                 ; vbroadcastss  0x20(%rax),%ymm11
+  DB  196,98,125,24,96,48                 ; vbroadcastss  0x30(%rax),%ymm12
+  DB  196,98,125,24,64,64                 ; vbroadcastss  0x40(%rax),%ymm8
+  DB  196,66,101,184,196                  ; vfmadd231ps   %ymm12,%ymm3,%ymm8
+  DB  196,66,109,184,195                  ; vfmadd231ps   %ymm11,%ymm2,%ymm8
+  DB  196,66,117,184,194                  ; vfmadd231ps   %ymm10,%ymm1,%ymm8
+  DB  196,66,125,184,193                  ; vfmadd231ps   %ymm9,%ymm0,%ymm8
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,20                 ; vbroadcastss  0x14(%rax),%ymm11
+  DB  196,98,125,24,96,36                 ; vbroadcastss  0x24(%rax),%ymm12
+  DB  196,98,125,24,104,52                ; vbroadcastss  0x34(%rax),%ymm13
+  DB  196,98,125,24,72,68                 ; vbroadcastss  0x44(%rax),%ymm9
+  DB  196,66,101,184,205                  ; vfmadd231ps   %ymm13,%ymm3,%ymm9
+  DB  196,66,109,184,204                  ; vfmadd231ps   %ymm12,%ymm2,%ymm9
+  DB  196,66,117,184,203                  ; vfmadd231ps   %ymm11,%ymm1,%ymm9
+  DB  196,66,125,184,202                  ; vfmadd231ps   %ymm10,%ymm0,%ymm9
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  196,98,125,24,96,24                 ; vbroadcastss  0x18(%rax),%ymm12
+  DB  196,98,125,24,104,40                ; vbroadcastss  0x28(%rax),%ymm13
+  DB  196,98,125,24,112,56                ; vbroadcastss  0x38(%rax),%ymm14
+  DB  196,98,125,24,80,72                 ; vbroadcastss  0x48(%rax),%ymm10
+  DB  196,66,101,184,214                  ; vfmadd231ps   %ymm14,%ymm3,%ymm10
+  DB  196,66,109,184,213                  ; vfmadd231ps   %ymm13,%ymm2,%ymm10
+  DB  196,66,117,184,212                  ; vfmadd231ps   %ymm12,%ymm1,%ymm10
+  DB  196,66,125,184,211                  ; vfmadd231ps   %ymm11,%ymm0,%ymm10
+  DB  196,98,125,24,96,12                 ; vbroadcastss  0xc(%rax),%ymm12
+  DB  196,98,125,24,104,28                ; vbroadcastss  0x1c(%rax),%ymm13
+  DB  196,98,125,24,112,44                ; vbroadcastss  0x2c(%rax),%ymm14
+  DB  196,98,125,24,120,60                ; vbroadcastss  0x3c(%rax),%ymm15
+  DB  196,98,125,24,88,76                 ; vbroadcastss  0x4c(%rax),%ymm11
+  DB  196,66,101,184,223                  ; vfmadd231ps   %ymm15,%ymm3,%ymm11
+  DB  196,66,109,184,222                  ; vfmadd231ps   %ymm14,%ymm2,%ymm11
+  DB  196,66,117,184,221                  ; vfmadd231ps   %ymm13,%ymm1,%ymm11
+  DB  196,66,125,184,220                  ; vfmadd231ps   %ymm12,%ymm0,%ymm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  197,124,41,210                      ; vmovaps       %ymm10,%ymm2
+  DB  197,124,41,219                      ; vmovaps       %ymm11,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x3_hsw
+_sk_matrix_4x3_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,16                   ; vbroadcastss  (%rax),%ymm2
+  DB  196,226,125,24,88,16                ; vbroadcastss  0x10(%rax),%ymm3
+  DB  196,98,125,24,64,32                 ; vbroadcastss  0x20(%rax),%ymm8
+  DB  196,98,117,184,195                  ; vfmadd231ps   %ymm3,%ymm1,%ymm8
+  DB  196,98,125,184,194                  ; vfmadd231ps   %ymm2,%ymm0,%ymm8
+  DB  196,226,125,24,80,4                 ; vbroadcastss  0x4(%rax),%ymm2
+  DB  196,226,125,24,88,20                ; vbroadcastss  0x14(%rax),%ymm3
+  DB  196,98,125,24,72,36                 ; vbroadcastss  0x24(%rax),%ymm9
+  DB  196,98,117,184,203                  ; vfmadd231ps   %ymm3,%ymm1,%ymm9
+  DB  196,98,125,184,202                  ; vfmadd231ps   %ymm2,%ymm0,%ymm9
+  DB  196,226,125,24,88,8                 ; vbroadcastss  0x8(%rax),%ymm3
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,226,125,24,80,40                ; vbroadcastss  0x28(%rax),%ymm2
+  DB  196,194,117,184,210                 ; vfmadd231ps   %ymm10,%ymm1,%ymm2
+  DB  196,226,125,184,211                 ; vfmadd231ps   %ymm3,%ymm0,%ymm2
+  DB  196,98,125,24,80,12                 ; vbroadcastss  0xc(%rax),%ymm10
+  DB  196,98,125,24,88,28                 ; vbroadcastss  0x1c(%rax),%ymm11
+  DB  196,226,125,24,88,44                ; vbroadcastss  0x2c(%rax),%ymm3
+  DB  196,194,117,184,219                 ; vfmadd231ps   %ymm11,%ymm1,%ymm3
+  DB  196,194,125,184,218                 ; vfmadd231ps   %ymm10,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_perspective_hsw
+_sk_matrix_perspective_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,98,125,24,72,4                  ; vbroadcastss  0x4(%rax),%ymm9
+  DB  196,98,125,24,80,8                  ; vbroadcastss  0x8(%rax),%ymm10
+  DB  196,66,117,184,209                  ; vfmadd231ps   %ymm9,%ymm1,%ymm10
+  DB  196,66,125,184,208                  ; vfmadd231ps   %ymm8,%ymm0,%ymm10
+  DB  196,98,125,24,64,12                 ; vbroadcastss  0xc(%rax),%ymm8
+  DB  196,98,125,24,72,16                 ; vbroadcastss  0x10(%rax),%ymm9
+  DB  196,98,125,24,88,20                 ; vbroadcastss  0x14(%rax),%ymm11
+  DB  196,66,117,184,217                  ; vfmadd231ps   %ymm9,%ymm1,%ymm11
+  DB  196,66,125,184,216                  ; vfmadd231ps   %ymm8,%ymm0,%ymm11
+  DB  196,98,125,24,64,24                 ; vbroadcastss  0x18(%rax),%ymm8
+  DB  196,98,125,24,72,28                 ; vbroadcastss  0x1c(%rax),%ymm9
+  DB  196,98,125,24,96,32                 ; vbroadcastss  0x20(%rax),%ymm12
+  DB  196,66,117,184,225                  ; vfmadd231ps   %ymm9,%ymm1,%ymm12
+  DB  196,66,125,184,224                  ; vfmadd231ps   %ymm8,%ymm0,%ymm12
+  DB  196,193,124,83,204                  ; vrcpps        %ymm12,%ymm1
+  DB  197,172,89,193                      ; vmulps        %ymm1,%ymm10,%ymm0
+  DB  197,164,89,201                      ; vmulps        %ymm1,%ymm11,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_gradient_hsw
+_sk_evenly_spaced_gradient_hsw LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  77,137,211                          ; mov           %r10,%r11
+  DB  73,255,203                          ; dec           %r11
+  DB  120,7                               ; js            421e <_sk_evenly_spaced_gradient_hsw+0x19>
+  DB  196,193,242,42,203                  ; vcvtsi2ss     %r11,%xmm1,%xmm1
+  DB  235,22                              ; jmp           4234 <_sk_evenly_spaced_gradient_hsw+0x2f>
+  DB  76,137,219                          ; mov           %r11,%rbx
+  DB  72,209,235                          ; shr           %rbx
+  DB  65,131,227,1                        ; and           $0x1,%r11d
+  DB  73,9,219                            ; or            %rbx,%r11
+  DB  196,193,242,42,203                  ; vcvtsi2ss     %r11,%xmm1,%xmm1
+  DB  197,242,88,201                      ; vaddss        %xmm1,%xmm1,%xmm1
+  DB  196,226,125,24,201                  ; vbroadcastss  %xmm1,%ymm1
+  DB  197,244,89,200                      ; vmulps        %ymm0,%ymm1,%ymm1
+  DB  197,126,91,217                      ; vcvttps2dq    %ymm1,%ymm11
+  DB  73,131,250,8                        ; cmp           $0x8,%r10
+  DB  119,70                              ; ja            428d <_sk_evenly_spaced_gradient_hsw+0x88>
+  DB  196,66,37,22,1                      ; vpermps       (%r9),%ymm11,%ymm8
+  DB  72,139,88,40                        ; mov           0x28(%rax),%rbx
+  DB  196,98,37,22,11                     ; vpermps       (%rbx),%ymm11,%ymm9
+  DB  72,139,88,16                        ; mov           0x10(%rax),%rbx
+  DB  76,139,72,24                        ; mov           0x18(%rax),%r9
+  DB  196,226,37,22,11                    ; vpermps       (%rbx),%ymm11,%ymm1
+  DB  72,139,88,48                        ; mov           0x30(%rax),%rbx
+  DB  196,98,37,22,19                     ; vpermps       (%rbx),%ymm11,%ymm10
+  DB  196,194,37,22,17                    ; vpermps       (%r9),%ymm11,%ymm2
+  DB  72,139,88,56                        ; mov           0x38(%rax),%rbx
+  DB  196,98,37,22,35                     ; vpermps       (%rbx),%ymm11,%ymm12
+  DB  72,139,88,32                        ; mov           0x20(%rax),%rbx
+  DB  196,226,37,22,27                    ; vpermps       (%rbx),%ymm11,%ymm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  196,98,37,22,40                     ; vpermps       (%rax),%ymm11,%ymm13
+  DB  235,110                             ; jmp           42fb <_sk_evenly_spaced_gradient_hsw+0xf6>
+  DB  196,65,13,118,246                   ; vpcmpeqd      %ymm14,%ymm14,%ymm14
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  196,2,117,146,4,153                 ; vgatherdps    %ymm1,(%r9,%ymm11,4),%ymm8
+  DB  72,139,88,40                        ; mov           0x28(%rax),%rbx
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  196,34,117,146,12,155               ; vgatherdps    %ymm1,(%rbx,%ymm11,4),%ymm9
+  DB  72,139,88,16                        ; mov           0x10(%rax),%rbx
+  DB  76,139,72,24                        ; mov           0x18(%rax),%r9
+  DB  197,237,118,210                     ; vpcmpeqd      %ymm2,%ymm2,%ymm2
+  DB  196,162,109,146,12,155              ; vgatherdps    %ymm2,(%rbx,%ymm11,4),%ymm1
+  DB  72,139,88,48                        ; mov           0x30(%rax),%rbx
+  DB  197,237,118,210                     ; vpcmpeqd      %ymm2,%ymm2,%ymm2
+  DB  196,34,109,146,20,155               ; vgatherdps    %ymm2,(%rbx,%ymm11,4),%ymm10
+  DB  197,229,118,219                     ; vpcmpeqd      %ymm3,%ymm3,%ymm3
+  DB  196,130,101,146,20,153              ; vgatherdps    %ymm3,(%r9,%ymm11,4),%ymm2
+  DB  72,139,88,56                        ; mov           0x38(%rax),%rbx
+  DB  197,229,118,219                     ; vpcmpeqd      %ymm3,%ymm3,%ymm3
+  DB  196,34,101,146,36,155               ; vgatherdps    %ymm3,(%rbx,%ymm11,4),%ymm12
+  DB  72,139,88,32                        ; mov           0x20(%rax),%rbx
+  DB  196,65,21,118,237                   ; vpcmpeqd      %ymm13,%ymm13,%ymm13
+  DB  196,162,21,146,28,155               ; vgatherdps    %ymm13,(%rbx,%ymm11,4),%ymm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  196,34,13,146,44,152                ; vgatherdps    %ymm14,(%rax,%ymm11,4),%ymm13
+  DB  196,66,125,168,193                  ; vfmadd213ps   %ymm9,%ymm0,%ymm8
+  DB  196,194,125,168,202                 ; vfmadd213ps   %ymm10,%ymm0,%ymm1
+  DB  196,194,125,168,212                 ; vfmadd213ps   %ymm12,%ymm0,%ymm2
+  DB  196,194,125,168,221                 ; vfmadd213ps   %ymm13,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gauss_a_to_rgba_hsw
+_sk_gauss_a_to_rgba_hsw LABEL PROC
+  DB  196,226,125,24,5,87,10,0,0          ; vbroadcastss  0xa57(%rip),%ymm0        # 4d78 <_sk_callback_hsw+0x45c>
+  DB  196,226,125,24,13,82,10,0,0         ; vbroadcastss  0xa52(%rip),%ymm1        # 4d7c <_sk_callback_hsw+0x460>
+  DB  196,226,101,168,200                 ; vfmadd213ps   %ymm0,%ymm3,%ymm1
+  DB  196,226,125,24,5,72,10,0,0          ; vbroadcastss  0xa48(%rip),%ymm0        # 4d80 <_sk_callback_hsw+0x464>
+  DB  196,226,101,184,193                 ; vfmadd231ps   %ymm1,%ymm3,%ymm0
+  DB  196,226,125,24,13,62,10,0,0         ; vbroadcastss  0xa3e(%rip),%ymm1        # 4d84 <_sk_callback_hsw+0x468>
+  DB  196,226,101,184,200                 ; vfmadd231ps   %ymm0,%ymm3,%ymm1
+  DB  196,226,125,24,5,52,10,0,0          ; vbroadcastss  0xa34(%rip),%ymm0        # 4d88 <_sk_callback_hsw+0x46c>
+  DB  196,226,101,184,193                 ; vfmadd231ps   %ymm1,%ymm3,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,252,40,216                      ; vmovaps       %ymm0,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gradient_hsw
+_sk_gradient_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  73,131,249,1                        ; cmp           $0x1,%r9
+  DB  15,134,180,0,0,0                    ; jbe           442c <_sk_gradient_hsw+0xc3>
+  DB  76,139,80,72                        ; mov           0x48(%rax),%r10
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  65,187,1,0,0,0                      ; mov           $0x1,%r11d
+  DB  196,226,125,24,21,253,9,0,0         ; vbroadcastss  0x9fd(%rip),%ymm2        # 4d8c <_sk_callback_hsw+0x470>
+  DB  196,65,53,239,201                   ; vpxor         %ymm9,%ymm9,%ymm9
+  DB  196,130,125,24,28,154               ; vbroadcastss  (%r10,%r11,4),%ymm3
+  DB  197,228,194,216,2                   ; vcmpleps      %ymm0,%ymm3,%ymm3
+  DB  196,227,117,74,218,48               ; vblendvps     %ymm3,%ymm2,%ymm1,%ymm3
+  DB  196,65,101,254,201                  ; vpaddd        %ymm9,%ymm3,%ymm9
+  DB  73,255,195                          ; inc           %r11
+  DB  77,57,217                           ; cmp           %r11,%r9
+  DB  117,226                             ; jne           4394 <_sk_gradient_hsw+0x2b>
+  DB  76,139,80,8                         ; mov           0x8(%rax),%r10
+  DB  73,131,249,8                        ; cmp           $0x8,%r9
+  DB  118,121                             ; jbe           4435 <_sk_gradient_hsw+0xcc>
+  DB  196,65,13,118,246                   ; vpcmpeqd      %ymm14,%ymm14,%ymm14
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  196,2,117,146,4,138                 ; vgatherdps    %ymm1,(%r10,%ymm9,4),%ymm8
+  DB  76,139,72,40                        ; mov           0x28(%rax),%r9
+  DB  197,245,118,201                     ; vpcmpeqd      %ymm1,%ymm1,%ymm1
+  DB  196,2,117,146,20,137                ; vgatherdps    %ymm1,(%r9,%ymm9,4),%ymm10
+  DB  76,139,72,16                        ; mov           0x10(%rax),%r9
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  197,237,118,210                     ; vpcmpeqd      %ymm2,%ymm2,%ymm2
+  DB  196,130,109,146,12,137              ; vgatherdps    %ymm2,(%r9,%ymm9,4),%ymm1
+  DB  76,139,72,48                        ; mov           0x30(%rax),%r9
+  DB  197,237,118,210                     ; vpcmpeqd      %ymm2,%ymm2,%ymm2
+  DB  196,2,109,146,28,137                ; vgatherdps    %ymm2,(%r9,%ymm9,4),%ymm11
+  DB  197,229,118,219                     ; vpcmpeqd      %ymm3,%ymm3,%ymm3
+  DB  196,130,101,146,20,138              ; vgatherdps    %ymm3,(%r10,%ymm9,4),%ymm2
+  DB  76,139,72,56                        ; mov           0x38(%rax),%r9
+  DB  197,229,118,219                     ; vpcmpeqd      %ymm3,%ymm3,%ymm3
+  DB  196,2,101,146,36,137                ; vgatherdps    %ymm3,(%r9,%ymm9,4),%ymm12
+  DB  76,139,72,32                        ; mov           0x20(%rax),%r9
+  DB  196,65,21,118,237                   ; vpcmpeqd      %ymm13,%ymm13,%ymm13
+  DB  196,130,21,146,28,137               ; vgatherdps    %ymm13,(%r9,%ymm9,4),%ymm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  196,34,13,146,44,136                ; vgatherdps    %ymm14,(%rax,%ymm9,4),%ymm13
+  DB  235,77                              ; jmp           4479 <_sk_gradient_hsw+0x110>
+  DB  76,139,80,8                         ; mov           0x8(%rax),%r10
+  DB  196,65,52,87,201                    ; vxorps        %ymm9,%ymm9,%ymm9
+  DB  196,66,53,22,2                      ; vpermps       (%r10),%ymm9,%ymm8
+  DB  76,139,72,40                        ; mov           0x28(%rax),%r9
+  DB  196,66,53,22,17                     ; vpermps       (%r9),%ymm9,%ymm10
+  DB  76,139,72,16                        ; mov           0x10(%rax),%r9
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  196,194,53,22,9                     ; vpermps       (%r9),%ymm9,%ymm1
+  DB  76,139,72,48                        ; mov           0x30(%rax),%r9
+  DB  196,66,53,22,25                     ; vpermps       (%r9),%ymm9,%ymm11
+  DB  196,194,53,22,18                    ; vpermps       (%r10),%ymm9,%ymm2
+  DB  76,139,72,56                        ; mov           0x38(%rax),%r9
+  DB  196,66,53,22,33                     ; vpermps       (%r9),%ymm9,%ymm12
+  DB  76,139,72,32                        ; mov           0x20(%rax),%r9
+  DB  196,194,53,22,25                    ; vpermps       (%r9),%ymm9,%ymm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  196,98,53,22,40                     ; vpermps       (%rax),%ymm9,%ymm13
+  DB  196,66,125,168,194                  ; vfmadd213ps   %ymm10,%ymm0,%ymm8
+  DB  196,194,125,168,203                 ; vfmadd213ps   %ymm11,%ymm0,%ymm1
+  DB  196,194,125,168,212                 ; vfmadd213ps   %ymm12,%ymm0,%ymm2
+  DB  196,194,125,168,221                 ; vfmadd213ps   %ymm13,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_2_stop_gradient_hsw
+_sk_evenly_spaced_2_stop_gradient_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,8                    ; vbroadcastss  (%rax),%ymm1
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,98,125,184,193                  ; vfmadd231ps   %ymm1,%ymm0,%ymm8
+  DB  196,226,125,24,80,4                 ; vbroadcastss  0x4(%rax),%ymm2
+  DB  196,226,125,24,72,20                ; vbroadcastss  0x14(%rax),%ymm1
+  DB  196,226,125,184,202                 ; vfmadd231ps   %ymm2,%ymm0,%ymm1
+  DB  196,226,125,24,88,8                 ; vbroadcastss  0x8(%rax),%ymm3
+  DB  196,226,125,24,80,24                ; vbroadcastss  0x18(%rax),%ymm2
+  DB  196,226,125,184,211                 ; vfmadd231ps   %ymm3,%ymm0,%ymm2
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,226,125,24,88,28                ; vbroadcastss  0x1c(%rax),%ymm3
+  DB  196,194,125,184,217                 ; vfmadd231ps   %ymm9,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_unit_angle_hsw
+_sk_xy_to_unit_angle_hsw LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,200                       ; vsubps        %ymm0,%ymm8,%ymm9
+  DB  197,52,84,200                       ; vandps        %ymm0,%ymm9,%ymm9
+  DB  197,60,92,209                       ; vsubps        %ymm1,%ymm8,%ymm10
+  DB  197,44,84,209                       ; vandps        %ymm1,%ymm10,%ymm10
+  DB  196,65,52,93,218                    ; vminps        %ymm10,%ymm9,%ymm11
+  DB  196,65,52,95,226                    ; vmaxps        %ymm10,%ymm9,%ymm12
+  DB  196,65,36,94,220                    ; vdivps        %ymm12,%ymm11,%ymm11
+  DB  196,65,36,89,227                    ; vmulps        %ymm11,%ymm11,%ymm12
+  DB  196,98,125,24,45,124,8,0,0          ; vbroadcastss  0x87c(%rip),%ymm13        # 4d90 <_sk_callback_hsw+0x474>
+  DB  196,98,125,24,53,119,8,0,0          ; vbroadcastss  0x877(%rip),%ymm14        # 4d94 <_sk_callback_hsw+0x478>
+  DB  196,66,29,184,245                   ; vfmadd231ps   %ymm13,%ymm12,%ymm14
+  DB  196,98,125,24,45,109,8,0,0          ; vbroadcastss  0x86d(%rip),%ymm13        # 4d98 <_sk_callback_hsw+0x47c>
+  DB  196,66,29,184,238                   ; vfmadd231ps   %ymm14,%ymm12,%ymm13
+  DB  196,98,125,24,53,99,8,0,0           ; vbroadcastss  0x863(%rip),%ymm14        # 4d9c <_sk_callback_hsw+0x480>
+  DB  196,66,29,184,245                   ; vfmadd231ps   %ymm13,%ymm12,%ymm14
+  DB  196,65,36,89,222                    ; vmulps        %ymm14,%ymm11,%ymm11
+  DB  196,65,52,194,202,1                 ; vcmpltps      %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,21,78,8,0,0           ; vbroadcastss  0x84e(%rip),%ymm10        # 4da0 <_sk_callback_hsw+0x484>
+  DB  196,65,44,92,211                    ; vsubps        %ymm11,%ymm10,%ymm10
+  DB  196,67,37,74,202,144                ; vblendvps     %ymm9,%ymm10,%ymm11,%ymm9
+  DB  196,193,124,194,192,1               ; vcmpltps      %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,21,56,8,0,0           ; vbroadcastss  0x838(%rip),%ymm10        # 4da4 <_sk_callback_hsw+0x488>
+  DB  196,65,44,92,209                    ; vsubps        %ymm9,%ymm10,%ymm10
+  DB  196,195,53,74,194,0                 ; vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  DB  196,65,116,194,200,1                ; vcmpltps      %ymm8,%ymm1,%ymm9
+  DB  196,98,125,24,21,34,8,0,0           ; vbroadcastss  0x822(%rip),%ymm10        # 4da8 <_sk_callback_hsw+0x48c>
+  DB  197,44,92,208                       ; vsubps        %ymm0,%ymm10,%ymm10
+  DB  196,195,125,74,194,144              ; vblendvps     %ymm9,%ymm10,%ymm0,%ymm0
+  DB  196,65,124,194,200,3                ; vcmpunordps   %ymm8,%ymm0,%ymm9
+  DB  196,195,125,74,192,144              ; vblendvps     %ymm9,%ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_radius_hsw
+_sk_xy_to_radius_hsw LABEL PROC
+  DB  197,116,89,193                      ; vmulps        %ymm1,%ymm1,%ymm8
+  DB  196,98,125,184,192                  ; vfmadd231ps   %ymm0,%ymm0,%ymm8
+  DB  196,193,124,81,192                  ; vsqrtps       %ymm8,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_save_xy_hsw
+_sk_save_xy_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,239,7,0,0           ; vbroadcastss  0x7ef(%rip),%ymm8        # 4dac <_sk_callback_hsw+0x490>
+  DB  196,65,124,88,200                   ; vaddps        %ymm8,%ymm0,%ymm9
+  DB  196,67,125,8,209,1                  ; vroundps      $0x1,%ymm9,%ymm10
+  DB  196,65,52,92,202                    ; vsubps        %ymm10,%ymm9,%ymm9
+  DB  196,65,116,88,192                   ; vaddps        %ymm8,%ymm1,%ymm8
+  DB  196,67,125,8,208,1                  ; vroundps      $0x1,%ymm8,%ymm10
+  DB  196,65,60,92,194                    ; vsubps        %ymm10,%ymm8,%ymm8
+  DB  197,252,17,0                        ; vmovups       %ymm0,(%rax)
+  DB  197,252,17,72,32                    ; vmovups       %ymm1,0x20(%rax)
+  DB  197,124,17,72,64                    ; vmovups       %ymm9,0x40(%rax)
+  DB  197,124,17,64,96                    ; vmovups       %ymm8,0x60(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_accumulate_hsw
+_sk_accumulate_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,16,128,128,0,0,0            ; vmovups       0x80(%rax),%ymm8
+  DB  197,60,89,128,160,0,0,0             ; vmulps        0xa0(%rax),%ymm8,%ymm8
+  DB  196,226,61,184,224                  ; vfmadd231ps   %ymm0,%ymm8,%ymm4
+  DB  196,226,61,184,233                  ; vfmadd231ps   %ymm1,%ymm8,%ymm5
+  DB  196,226,61,184,242                  ; vfmadd231ps   %ymm2,%ymm8,%ymm6
+  DB  196,98,101,168,199                  ; vfmadd213ps   %ymm7,%ymm3,%ymm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,199                      ; vmovaps       %ymm8,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_nx_hsw
+_sk_bilinear_nx_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,131,7,0,0          ; vbroadcastss  0x783(%rip),%ymm0        # 4db0 <_sk_callback_hsw+0x494>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  196,98,125,24,5,122,7,0,0           ; vbroadcastss  0x77a(%rip),%ymm8        # 4db4 <_sk_callback_hsw+0x498>
+  DB  197,60,92,64,64                     ; vsubps        0x40(%rax),%ymm8,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_px_hsw
+_sk_bilinear_px_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,98,7,0,0           ; vbroadcastss  0x762(%rip),%ymm0        # 4db8 <_sk_callback_hsw+0x49c>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  197,124,16,64,64                    ; vmovups       0x40(%rax),%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_ny_hsw
+_sk_bilinear_ny_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,70,7,0,0          ; vbroadcastss  0x746(%rip),%ymm1        # 4dbc <_sk_callback_hsw+0x4a0>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  196,98,125,24,5,60,7,0,0            ; vbroadcastss  0x73c(%rip),%ymm8        # 4dc0 <_sk_callback_hsw+0x4a4>
+  DB  197,60,92,64,96                     ; vsubps        0x60(%rax),%ymm8,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_py_hsw
+_sk_bilinear_py_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,36,7,0,0          ; vbroadcastss  0x724(%rip),%ymm1        # 4dc4 <_sk_callback_hsw+0x4a8>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  197,124,16,64,96                    ; vmovups       0x60(%rax),%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3x_hsw
+_sk_bicubic_n3x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,7,7,0,0            ; vbroadcastss  0x707(%rip),%ymm0        # 4dc8 <_sk_callback_hsw+0x4ac>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  196,98,125,24,5,254,6,0,0           ; vbroadcastss  0x6fe(%rip),%ymm8        # 4dcc <_sk_callback_hsw+0x4b0>
+  DB  197,60,92,64,64                     ; vsubps        0x40(%rax),%ymm8,%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,239,6,0,0          ; vbroadcastss  0x6ef(%rip),%ymm10        # 4dd0 <_sk_callback_hsw+0x4b4>
+  DB  196,98,125,24,29,234,6,0,0          ; vbroadcastss  0x6ea(%rip),%ymm11        # 4dd4 <_sk_callback_hsw+0x4b8>
+  DB  196,66,61,168,218                   ; vfmadd213ps   %ymm10,%ymm8,%ymm11
+  DB  196,65,36,89,193                    ; vmulps        %ymm9,%ymm11,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1x_hsw
+_sk_bicubic_n1x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,205,6,0,0          ; vbroadcastss  0x6cd(%rip),%ymm0        # 4dd8 <_sk_callback_hsw+0x4bc>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  196,98,125,24,5,196,6,0,0           ; vbroadcastss  0x6c4(%rip),%ymm8        # 4ddc <_sk_callback_hsw+0x4c0>
+  DB  197,60,92,64,64                     ; vsubps        0x40(%rax),%ymm8,%ymm8
+  DB  196,98,125,24,13,186,6,0,0          ; vbroadcastss  0x6ba(%rip),%ymm9        # 4de0 <_sk_callback_hsw+0x4c4>
+  DB  196,98,125,24,21,181,6,0,0          ; vbroadcastss  0x6b5(%rip),%ymm10        # 4de4 <_sk_callback_hsw+0x4c8>
+  DB  196,66,61,168,209                   ; vfmadd213ps   %ymm9,%ymm8,%ymm10
+  DB  196,98,125,24,13,171,6,0,0          ; vbroadcastss  0x6ab(%rip),%ymm9        # 4de8 <_sk_callback_hsw+0x4cc>
+  DB  196,66,61,184,202                   ; vfmadd231ps   %ymm10,%ymm8,%ymm9
+  DB  196,98,125,24,21,161,6,0,0          ; vbroadcastss  0x6a1(%rip),%ymm10        # 4dec <_sk_callback_hsw+0x4d0>
+  DB  196,66,61,184,209                   ; vfmadd231ps   %ymm9,%ymm8,%ymm10
+  DB  197,124,17,144,128,0,0,0            ; vmovups       %ymm10,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1x_hsw
+_sk_bicubic_p1x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,137,6,0,0           ; vbroadcastss  0x689(%rip),%ymm8        # 4df0 <_sk_callback_hsw+0x4d4>
+  DB  197,188,88,0                        ; vaddps        (%rax),%ymm8,%ymm0
+  DB  197,124,16,72,64                    ; vmovups       0x40(%rax),%ymm9
+  DB  196,98,125,24,21,123,6,0,0          ; vbroadcastss  0x67b(%rip),%ymm10        # 4df4 <_sk_callback_hsw+0x4d8>
+  DB  196,98,125,24,29,118,6,0,0          ; vbroadcastss  0x676(%rip),%ymm11        # 4df8 <_sk_callback_hsw+0x4dc>
+  DB  196,66,53,168,218                   ; vfmadd213ps   %ymm10,%ymm9,%ymm11
+  DB  196,66,53,168,216                   ; vfmadd213ps   %ymm8,%ymm9,%ymm11
+  DB  196,98,125,24,5,103,6,0,0           ; vbroadcastss  0x667(%rip),%ymm8        # 4dfc <_sk_callback_hsw+0x4e0>
+  DB  196,66,53,184,195                   ; vfmadd231ps   %ymm11,%ymm9,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3x_hsw
+_sk_bicubic_p3x_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,79,6,0,0           ; vbroadcastss  0x64f(%rip),%ymm0        # 4e00 <_sk_callback_hsw+0x4e4>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  197,124,16,64,64                    ; vmovups       0x40(%rax),%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,60,6,0,0           ; vbroadcastss  0x63c(%rip),%ymm10        # 4e04 <_sk_callback_hsw+0x4e8>
+  DB  196,98,125,24,29,55,6,0,0           ; vbroadcastss  0x637(%rip),%ymm11        # 4e08 <_sk_callback_hsw+0x4ec>
+  DB  196,66,61,168,218                   ; vfmadd213ps   %ymm10,%ymm8,%ymm11
+  DB  196,65,52,89,195                    ; vmulps        %ymm11,%ymm9,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3y_hsw
+_sk_bicubic_n3y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,26,6,0,0          ; vbroadcastss  0x61a(%rip),%ymm1        # 4e0c <_sk_callback_hsw+0x4f0>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  196,98,125,24,5,16,6,0,0            ; vbroadcastss  0x610(%rip),%ymm8        # 4e10 <_sk_callback_hsw+0x4f4>
+  DB  197,60,92,64,96                     ; vsubps        0x60(%rax),%ymm8,%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,1,6,0,0            ; vbroadcastss  0x601(%rip),%ymm10        # 4e14 <_sk_callback_hsw+0x4f8>
+  DB  196,98,125,24,29,252,5,0,0          ; vbroadcastss  0x5fc(%rip),%ymm11        # 4e18 <_sk_callback_hsw+0x4fc>
+  DB  196,66,61,168,218                   ; vfmadd213ps   %ymm10,%ymm8,%ymm11
+  DB  196,65,36,89,193                    ; vmulps        %ymm9,%ymm11,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1y_hsw
+_sk_bicubic_n1y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,223,5,0,0         ; vbroadcastss  0x5df(%rip),%ymm1        # 4e1c <_sk_callback_hsw+0x500>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  196,98,125,24,5,213,5,0,0           ; vbroadcastss  0x5d5(%rip),%ymm8        # 4e20 <_sk_callback_hsw+0x504>
+  DB  197,60,92,64,96                     ; vsubps        0x60(%rax),%ymm8,%ymm8
+  DB  196,98,125,24,13,203,5,0,0          ; vbroadcastss  0x5cb(%rip),%ymm9        # 4e24 <_sk_callback_hsw+0x508>
+  DB  196,98,125,24,21,198,5,0,0          ; vbroadcastss  0x5c6(%rip),%ymm10        # 4e28 <_sk_callback_hsw+0x50c>
+  DB  196,66,61,168,209                   ; vfmadd213ps   %ymm9,%ymm8,%ymm10
+  DB  196,98,125,24,13,188,5,0,0          ; vbroadcastss  0x5bc(%rip),%ymm9        # 4e2c <_sk_callback_hsw+0x510>
+  DB  196,66,61,184,202                   ; vfmadd231ps   %ymm10,%ymm8,%ymm9
+  DB  196,98,125,24,21,178,5,0,0          ; vbroadcastss  0x5b2(%rip),%ymm10        # 4e30 <_sk_callback_hsw+0x514>
+  DB  196,66,61,184,209                   ; vfmadd231ps   %ymm9,%ymm8,%ymm10
+  DB  197,124,17,144,160,0,0,0            ; vmovups       %ymm10,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1y_hsw
+_sk_bicubic_p1y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,154,5,0,0           ; vbroadcastss  0x59a(%rip),%ymm8        # 4e34 <_sk_callback_hsw+0x518>
+  DB  197,188,88,72,32                    ; vaddps        0x20(%rax),%ymm8,%ymm1
+  DB  197,124,16,72,96                    ; vmovups       0x60(%rax),%ymm9
+  DB  196,98,125,24,21,139,5,0,0          ; vbroadcastss  0x58b(%rip),%ymm10        # 4e38 <_sk_callback_hsw+0x51c>
+  DB  196,98,125,24,29,134,5,0,0          ; vbroadcastss  0x586(%rip),%ymm11        # 4e3c <_sk_callback_hsw+0x520>
+  DB  196,66,53,168,218                   ; vfmadd213ps   %ymm10,%ymm9,%ymm11
+  DB  196,66,53,168,216                   ; vfmadd213ps   %ymm8,%ymm9,%ymm11
+  DB  196,98,125,24,5,119,5,0,0           ; vbroadcastss  0x577(%rip),%ymm8        # 4e40 <_sk_callback_hsw+0x524>
+  DB  196,66,53,184,195                   ; vfmadd231ps   %ymm11,%ymm9,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3y_hsw
+_sk_bicubic_p3y_hsw LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,95,5,0,0          ; vbroadcastss  0x55f(%rip),%ymm1        # 4e44 <_sk_callback_hsw+0x528>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  197,124,16,64,96                    ; vmovups       0x60(%rax),%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,75,5,0,0           ; vbroadcastss  0x54b(%rip),%ymm10        # 4e48 <_sk_callback_hsw+0x52c>
+  DB  196,98,125,24,29,70,5,0,0           ; vbroadcastss  0x546(%rip),%ymm11        # 4e4c <_sk_callback_hsw+0x530>
+  DB  196,66,61,168,218                   ; vfmadd213ps   %ymm10,%ymm8,%ymm11
+  DB  196,65,52,89,195                    ; vmulps        %ymm11,%ymm9,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_callback_hsw
+_sk_callback_hsw LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,131,228,224                      ; and           $0xffffffffffffffe0,%rsp
+  DB  72,129,236,192,0,0,0                ; sub           $0xc0,%rsp
+  DB  197,252,41,188,36,128,0,0,0         ; vmovaps       %ymm7,0x80(%rsp)
+  DB  197,252,41,116,36,96                ; vmovaps       %ymm6,0x60(%rsp)
+  DB  197,252,41,108,36,64                ; vmovaps       %ymm5,0x40(%rsp)
+  DB  197,252,41,100,36,32                ; vmovaps       %ymm4,0x20(%rsp)
+  DB  77,137,196                          ; mov           %r8,%r12
+  DB  73,137,206                          ; mov           %rcx,%r14
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,137,195                          ; mov           %rax,%rbx
+  DB  197,252,20,225                      ; vunpcklps     %ymm1,%ymm0,%ymm4
+  DB  197,252,21,193                      ; vunpckhps     %ymm1,%ymm0,%ymm0
+  DB  197,236,20,203                      ; vunpcklps     %ymm3,%ymm2,%ymm1
+  DB  197,236,21,211                      ; vunpckhps     %ymm3,%ymm2,%ymm2
+  DB  197,221,20,217                      ; vunpcklpd     %ymm1,%ymm4,%ymm3
+  DB  197,221,21,201                      ; vunpckhpd     %ymm1,%ymm4,%ymm1
+  DB  197,253,20,226                      ; vunpcklpd     %ymm2,%ymm0,%ymm4
+  DB  197,253,21,194                      ; vunpckhpd     %ymm2,%ymm0,%ymm0
+  DB  196,227,101,24,209,1                ; vinsertf128   $0x1,%xmm1,%ymm3,%ymm2
+  DB  196,227,93,24,232,1                 ; vinsertf128   $0x1,%xmm0,%ymm4,%ymm5
+  DB  196,227,101,6,201,49                ; vperm2f128    $0x31,%ymm1,%ymm3,%ymm1
+  DB  196,227,93,6,192,49                 ; vperm2f128    $0x31,%ymm0,%ymm4,%ymm0
+  DB  197,253,17,83,8                     ; vmovupd       %ymm2,0x8(%rbx)
+  DB  197,253,17,107,40                   ; vmovupd       %ymm5,0x28(%rbx)
+  DB  197,253,17,75,72                    ; vmovupd       %ymm1,0x48(%rbx)
+  DB  197,253,17,67,104                   ; vmovupd       %ymm0,0x68(%rbx)
+  DB  77,133,228                          ; test          %r12,%r12
+  DB  186,8,0,0,0                         ; mov           $0x8,%edx
+  DB  65,15,69,212                        ; cmovne        %r12d,%edx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  197,248,119                         ; vzeroupper
+  DB  255,19                              ; callq         *(%rbx)
+  DB  72,139,131,136,0,0,0                ; mov           0x88(%rbx),%rax
+  DB  197,248,16,0                        ; vmovups       (%rax),%xmm0
+  DB  197,248,16,72,16                    ; vmovups       0x10(%rax),%xmm1
+  DB  197,248,16,80,32                    ; vmovups       0x20(%rax),%xmm2
+  DB  197,248,16,88,48                    ; vmovups       0x30(%rax),%xmm3
+  DB  196,227,101,24,88,112,1             ; vinsertf128   $0x1,0x70(%rax),%ymm3,%ymm3
+  DB  196,227,109,24,80,96,1              ; vinsertf128   $0x1,0x60(%rax),%ymm2,%ymm2
+  DB  196,227,117,24,72,80,1              ; vinsertf128   $0x1,0x50(%rax),%ymm1,%ymm1
+  DB  196,227,125,24,64,64,1              ; vinsertf128   $0x1,0x40(%rax),%ymm0,%ymm0
+  DB  197,252,20,225                      ; vunpcklps     %ymm1,%ymm0,%ymm4
+  DB  197,252,21,233                      ; vunpckhps     %ymm1,%ymm0,%ymm5
+  DB  197,236,20,203                      ; vunpcklps     %ymm3,%ymm2,%ymm1
+  DB  197,236,21,219                      ; vunpckhps     %ymm3,%ymm2,%ymm3
+  DB  197,221,20,193                      ; vunpcklpd     %ymm1,%ymm4,%ymm0
+  DB  197,221,21,201                      ; vunpckhpd     %ymm1,%ymm4,%ymm1
+  DB  197,213,20,211                      ; vunpcklpd     %ymm3,%ymm5,%ymm2
+  DB  197,213,21,219                      ; vunpckhpd     %ymm3,%ymm5,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  76,137,241                          ; mov           %r14,%rcx
+  DB  77,137,224                          ; mov           %r12,%r8
+  DB  197,252,40,100,36,32                ; vmovaps       0x20(%rsp),%ymm4
+  DB  197,252,40,108,36,64                ; vmovaps       0x40(%rsp),%ymm5
+  DB  197,252,40,116,36,96                ; vmovaps       0x60(%rsp),%ymm6
+  DB  197,252,40,188,36,128,0,0,0         ; vmovaps       0x80(%rsp),%ymm7
+  DB  72,141,101,224                      ; lea           -0x20(%rbp),%rsp
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+ALIGN 4
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,1                            ; cmpb          $0x1,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  252                                 ; cld
+  DB  190,0,0,128,63                      ; mov           $0x3f800000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,191,0,0,224                   ; add           %al,-0x1fffff41(%rax)
+  DB  64,154                              ; rex           (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 4af9 <.literal4+0xb1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 4b09 <.literal4+0xc1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 4b19 <.literal4+0xd1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 4b29 <.literal4+0xe1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,127                    ; add           %al,0x7f00003f(%rax)
+  DB  67,0,0                              ; rex.XB        add %al,(%r8)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  145                                 ; xchg          %eax,%ecx
+  DB  131,158,61,92,143,50,63             ; sbbl          $0x3f,0x328f5c3d(%rsi)
+  DB  154                                 ; (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,10,215                           ; ds            or  %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,97                               ; rex.RXB       (bad)
+  DB  61,82,184,78,65                     ; cmp           $0x414eb852,%eax
+  DB  186,159,98,60,57                    ; mov           $0x393c629f,%edx
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  32,187,109,165,144,63               ; and           %bh,0x3f90a56d(%rbx)
+  DB  252                                 ; cld
+  DB  191,16,62,168,177                   ; mov           $0xb1a83e10,%edi
+  DB  152                                 ; cwtl
+  DB  59,0                                ; cmp           (%rax),%eax
+  DB  0,128,63,0,0,192                    ; add           %al,-0x3fffffc1(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  0,64,0                              ; add           %al,0x0(%rax)
+  DB  0,128,64,171,170,42                 ; add           %al,0x2aaaab40(%rax)
+  DB  62,0,0                              ; add           %al,%ds:(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,64,171                            ; add           %al,-0x55(%rax)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  128,64,171,170                      ; addb          $0xaa,-0x55(%rax)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,129,128,128,59                  ; mov           $0x3b808081,%esi
+  DB  129,128,128,59,0,248,0,0,8,33       ; addl          $0x21080000,-0x7ffc480(%rax)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        4b81 <.literal4+0x139>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,129,128,128,59              ; and           %eax,0x3b808081(,%rdi,1)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  129,128,128,59,129,128,128,59,0,0   ; addl          $0x3b80,-0x7f7ec480(%rax)
+  DB  0,52,255                            ; add           %dh,(%rdi,%rdi,8)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            4ba8 <.literal4+0x160>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            4c21 <.literal4+0x1d9>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,0                      ; add           %al,0x3f(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            4bdc <.literal4+0x194>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            4c55 <.literal4+0x20d>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,0                      ; add           %al,0x3f(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            4c10 <.literal4+0x1c8>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            4c89 <.literal4+0x241>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,0                      ; add           %al,0x3f(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            4c44 <.literal4+0x1fc>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            4cbd <.literal4+0x275>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,200                    ; add           %al,-0x37ffffc1(%rax)
+  DB  66,0,0                              ; rex.X         add %al,(%rax)
+  DB  127,67                              ; jg            4cbb <.literal4+0x273>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,65,203,61                       ; addb          $0x3d,-0x35(%rcx)
+  DB  13,60,111,18,3                      ; or            $0x3126f3c,%eax
+  DB  59,10                               ; cmp           (%rdx),%ecx
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  163,59,194,24,17,60,203,61,13       ; movabs        %eax,0xd3dcb3c1118c23b
+  DB  190,80,128,3,62                     ; mov           $0x3e038050,%esi
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           4cdb <.literal4+0x293>
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  129,128,128,59,129,128,128,59,0,0   ; addl          $0x3b80,-0x7f7ec480(%rax)
+  DB  127,67                              ; jg            4cef <.literal4+0x2a7>
+  DB  129,128,128,59,0,0,128,63,129,128   ; addl          $0x80813f80,0x3b80(%rax)
+  DB  128,59,0                            ; cmpb          $0x0,(%rbx)
+  DB  0,128,63,129,128,128                ; add           %al,-0x7f7f7ec1(%rax)
+  DB  59,0                                ; cmp           (%rax),%eax
+  DB  248                                 ; clc
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        4cd1 <.literal4+0x289>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,0,0,128,63                  ; and           %eax,0x3f800000(,%rdi,1)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        4ced <.literal4+0x2a5>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,0,0,128,63                  ; and           %eax,0x3f800000(,%rdi,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  124,66                              ; jl            4d42 <.literal4+0x2fa>
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,0,15                 ; mov           %ecx,0xf003788(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,240,0                ; mov           %ecx,0xf03988(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,59,15,0                 ; mov           %ecx,0xf3b88(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,61,0,240                ; mov           %ecx,-0xfffc278(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,0,15                 ; mov           %ecx,0xf003788(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,240,0                ; mov           %ecx,0xf03988(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,59,15,0                 ; mov           %ecx,0xf3b88(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,61,0,0                  ; mov           %ecx,0x3d88(%rax)
+  DB  112,65                              ; jo            4d85 <.literal4+0x33d>
+  DB  129,128,128,59,129,128,128,59,0,0   ; addl          $0x3b80,-0x7f7ec480(%rax)
+  DB  127,67                              ; jg            4d93 <.literal4+0x34b>
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  255                                 ; (bad)
+  DB  127,71                              ; jg            4da7 <.literal4+0x35f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,191,0,0,0                     ; add           %al,0xbf(%rax)
+  DB  63                                  ; (bad)
+  DB  208                                 ; (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,89                               ; ds            pop %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  152                                 ; cwtl
+  DB  221,147,61,18,120,57                ; fstl          0x3978123d(%rbx)
+  DB  64,45,16,17,192,32                  ; rex           sub $0x20c01110,%eax
+  DB  148                                 ; xchg          %eax,%esp
+  DB  90                                  ; pop           %rdx
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,0,24                             ; add           %bl,%ds:(%rax)
+  DB  161,57,1,0,0,0,111,43,231           ; movabs        0xe72b6f0000000139,%eax
+  DB  187,159,215,202,60                  ; mov           $0x3ccad79f,%ebx
+  DB  212                                 ; (bad)
+  DB  100,84                              ; fs            push %rsp
+  DB  189,169,240,34,62                   ; mov           $0x3e22f0a9,%ebp
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,62,0                            ; cmpb          $0x0,(%rsi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,128,63                    ; add           %bh,0x3f800000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,128,63                    ; add           %bh,0x3f800000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,128,63,171              ; sarb          $0xab,0x3f800000(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,114,28,199,62                   ; mov           $0x3ec71c72,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,128,63                    ; add           %bh,0x3f800000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,85                           ; sarb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,0,0,0,63                        ; mov           $0x3f000000,%edi
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,85                           ; sarb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,57,142,99,61                    ; mov           $0x3d638e39,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,171                          ; sarb          $0xab,(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,114,28,199,62                   ; mov           $0x3ec71c72,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,128,63,171              ; sarb          $0xab,0x3f800000(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,114,28,199,62                   ; mov           $0x3ec71c72,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,128,63                    ; add           %bh,0x3f800000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,85                           ; sarb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,0,0,0,63                        ; mov           $0x3f000000,%edi
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,85                           ; sarb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,57,142,99,61                    ; mov           $0x3d638e39,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,171                          ; sarb          $0xab,(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,114,28,199,62                   ; mov           $0x3ec71c72,%esi
+
+ALIGN 32
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  1,255                               ; add           %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a004e88 <_sk_callback_hsw+0xa00056c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,17               ; decl          0x11ffffff(%rip)        # 12004e90 <_sk_callback_hsw+0x12000574>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,21,255,255,255,25               ; callq         *0x19ffffff(%rip)        # 1a004e98 <_sk_callback_hsw+0x1a00057c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,29,255,255,255,2                ; lcall         *0x2ffffff(%rip)        # 3004ea0 <_sk_callback_hsw+0x3000584>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,18                              ; callq         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,22                              ; callq         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,26                              ; lcall         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,30                              ; lcall         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  1,255                               ; add           %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a004ee8 <_sk_callback_hsw+0xa0005cc>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,17               ; decl          0x11ffffff(%rip)        # 12004ef0 <_sk_callback_hsw+0x120005d4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,21,255,255,255,25               ; callq         *0x19ffffff(%rip)        # 1a004ef8 <_sk_callback_hsw+0x1a0005dc>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,29,255,255,255,2                ; lcall         *0x2ffffff(%rip)        # 3004f00 <_sk_callback_hsw+0x30005e4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,18                              ; callq         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,22                              ; callq         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,26                              ; lcall         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,30                              ; lcall         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  1,255                               ; add           %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a004f48 <_sk_callback_hsw+0xa00062c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,17               ; decl          0x11ffffff(%rip)        # 12004f50 <_sk_callback_hsw+0x12000634>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,21,255,255,255,25               ; callq         *0x19ffffff(%rip)        # 1a004f58 <_sk_callback_hsw+0x1a00063c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,29,255,255,255,2                ; lcall         *0x2ffffff(%rip)        # 3004f60 <_sk_callback_hsw+0x3000644>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,18                              ; callq         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,22                              ; callq         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,26                              ; lcall         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,30                              ; lcall         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  1,255                               ; add           %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a004fa8 <_sk_callback_hsw+0xa00068c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,17               ; decl          0x11ffffff(%rip)        # 12004fb0 <_sk_callback_hsw+0x12000694>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,21,255,255,255,25               ; callq         *0x19ffffff(%rip)        # 1a004fb8 <_sk_callback_hsw+0x1a00069c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,29,255,255,255,2                ; lcall         *0x2ffffff(%rip)        # 3004fc0 <_sk_callback_hsw+0x30006a4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,18                              ; callq         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,22                              ; callq         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,26                              ; lcall         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,30                              ; lcall         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  1,255                               ; add           %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a005008 <_sk_callback_hsw+0xa0006ec>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,17               ; decl          0x11ffffff(%rip)        # 12005010 <_sk_callback_hsw+0x120006f4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,21,255,255,255,25               ; callq         *0x19ffffff(%rip)        # 1a005018 <_sk_callback_hsw+0x1a0006fc>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,29,255,255,255,2                ; lcall         *0x2ffffff(%rip)        # 3005020 <_sk_callback_hsw+0x3000704>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,18                              ; callq         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,22                              ; callq         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,26                              ; lcall         *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,30                              ; lcall         *(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+ALIGN 16
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+ALIGN 32
+
+PUBLIC _sk_start_pipeline_avx
+_sk_start_pipeline_avx LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  86                                  ; push          %rsi
+  DB  87                                  ; push          %rdi
+  DB  83                                  ; push          %rbx
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  197,120,41,125,176                  ; vmovaps       %xmm15,-0x50(%rbp)
+  DB  197,120,41,117,160                  ; vmovaps       %xmm14,-0x60(%rbp)
+  DB  197,120,41,109,144                  ; vmovaps       %xmm13,-0x70(%rbp)
+  DB  197,120,41,101,128                  ; vmovaps       %xmm12,-0x80(%rbp)
+  DB  197,120,41,157,112,255,255,255      ; vmovaps       %xmm11,-0x90(%rbp)
+  DB  197,120,41,149,96,255,255,255       ; vmovaps       %xmm10,-0xa0(%rbp)
+  DB  197,120,41,141,80,255,255,255       ; vmovaps       %xmm9,-0xb0(%rbp)
+  DB  197,120,41,133,64,255,255,255       ; vmovaps       %xmm8,-0xc0(%rbp)
+  DB  197,248,41,189,48,255,255,255       ; vmovaps       %xmm7,-0xd0(%rbp)
+  DB  197,248,41,181,32,255,255,255       ; vmovaps       %xmm6,-0xe0(%rbp)
+  DB  72,137,211                          ; mov           %rdx,%rbx
+  DB  73,137,207                          ; mov           %rcx,%r15
+  DB  76,139,117,48                       ; mov           0x30(%rbp),%r14
+  DB  76,137,206                          ; mov           %r9,%rsi
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,197                          ; mov           %rax,%r13
+  DB  73,137,244                          ; mov           %rsi,%r12
+  DB  73,141,79,8                         ; lea           0x8(%r15),%rcx
+  DB  76,57,193                           ; cmp           %r8,%rcx
+  DB  118,5                               ; jbe           7d <_sk_start_pipeline_avx+0x7d>
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  235,83                              ; jmp           d0 <_sk_start_pipeline_avx+0xd0>
+  DB  76,137,133,24,255,255,255           ; mov           %r8,-0xe8(%rbp)
+  DB  65,184,0,0,0,0                      ; mov           $0x0,%r8d
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  197,220,87,228                      ; vxorps        %ymm4,%ymm4,%ymm4
+  DB  197,212,87,237                      ; vxorps        %ymm5,%ymm5,%ymm5
+  DB  197,204,87,246                      ; vxorps        %ymm6,%ymm6,%ymm6
+  DB  197,196,87,255                      ; vxorps        %ymm7,%ymm7,%ymm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  76,139,133,24,255,255,255           ; mov           -0xe8(%rbp),%r8
+  DB  73,141,87,8                         ; lea           0x8(%r15),%rdx
+  DB  73,131,199,16                       ; add           $0x10,%r15
+  DB  77,57,199                           ; cmp           %r8,%r15
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  118,180                             ; jbe           84 <_sk_start_pipeline_avx+0x84>
+  DB  73,41,208                           ; sub           %rdx,%r8
+  DB  116,44                              ; je            101 <_sk_start_pipeline_avx+0x101>
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  197,220,87,228                      ; vxorps        %ymm4,%ymm4,%ymm4
+  DB  197,212,87,237                      ; vxorps        %ymm5,%ymm5,%ymm5
+  DB  197,204,87,246                      ; vxorps        %ymm6,%ymm6,%ymm6
+  DB  197,196,87,255                      ; vxorps        %ymm7,%ymm7,%ymm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  197,248,40,181,32,255,255,255       ; vmovaps       -0xe0(%rbp),%xmm6
+  DB  197,248,40,189,48,255,255,255       ; vmovaps       -0xd0(%rbp),%xmm7
+  DB  197,120,40,133,64,255,255,255       ; vmovaps       -0xc0(%rbp),%xmm8
+  DB  197,120,40,141,80,255,255,255       ; vmovaps       -0xb0(%rbp),%xmm9
+  DB  197,120,40,149,96,255,255,255       ; vmovaps       -0xa0(%rbp),%xmm10
+  DB  197,120,40,157,112,255,255,255      ; vmovaps       -0x90(%rbp),%xmm11
+  DB  197,120,40,101,128                  ; vmovaps       -0x80(%rbp),%xmm12
+  DB  197,120,40,109,144                  ; vmovaps       -0x70(%rbp),%xmm13
+  DB  197,120,40,117,160                  ; vmovaps       -0x60(%rbp),%xmm14
+  DB  197,120,40,125,176                  ; vmovaps       -0x50(%rbp),%xmm15
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  95                                  ; pop           %rdi
+  DB  94                                  ; pop           %rsi
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  197,248,119                         ; vzeroupper
+  DB  195                                 ; retq
+
+PUBLIC _sk_just_return_avx
+_sk_just_return_avx LABEL PROC
+  DB  195                                 ; retq
+
+PUBLIC _sk_seed_shader_avx
+_sk_seed_shader_avx LABEL PROC
+  DB  197,249,110,194                     ; vmovd         %edx,%xmm0
+  DB  197,249,112,192,0                   ; vpshufd       $0x0,%xmm0,%xmm0
+  DB  196,227,125,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,183,100,0,0       ; vbroadcastss  0x64b7(%rip),%ymm1        # 6630 <_sk_callback_avx+0x12c>
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,252,88,7                        ; vaddps        (%rdi),%ymm0,%ymm0
+  DB  197,249,110,209                     ; vmovd         %ecx,%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,227,109,24,210,1                ; vinsertf128   $0x1,%xmm2,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  197,236,88,201                      ; vaddps        %ymm1,%ymm2,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,21,145,100,0,0       ; vbroadcastss  0x6491(%rip),%ymm2        # 6634 <_sk_callback_avx+0x130>
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  197,220,87,228                      ; vxorps        %ymm4,%ymm4,%ymm4
+  DB  197,212,87,237                      ; vxorps        %ymm5,%ymm5,%ymm5
+  DB  197,204,87,246                      ; vxorps        %ymm6,%ymm6,%ymm6
+  DB  197,196,87,255                      ; vxorps        %ymm7,%ymm7,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dither_avx
+_sk_dither_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,16,71,32                    ; vmovups       0x20(%rdi),%ymm8
+  DB  196,67,125,25,193,1                 ; vextractf128  $0x1,%ymm8,%xmm9
+  DB  197,121,110,210                     ; vmovd         %edx,%xmm10
+  DB  196,65,121,112,210,0                ; vpshufd       $0x0,%xmm10,%xmm10
+  DB  196,65,49,254,202                   ; vpaddd        %xmm10,%xmm9,%xmm9
+  DB  196,65,57,254,194                   ; vpaddd        %xmm10,%xmm8,%xmm8
+  DB  196,67,61,24,193,1                  ; vinsertf128   $0x1,%xmm9,%ymm8,%ymm8
+  DB  197,121,110,201                     ; vmovd         %ecx,%xmm9
+  DB  196,65,121,112,201,0                ; vpshufd       $0x0,%xmm9,%xmm9
+  DB  196,67,53,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
+  DB  196,65,52,87,208                    ; vxorps        %ymm8,%ymm9,%ymm10
+  DB  196,98,125,24,29,58,100,0,0         ; vbroadcastss  0x643a(%rip),%ymm11        # 6638 <_sk_callback_avx+0x134>
+  DB  196,65,44,84,203                    ; vandps        %ymm11,%ymm10,%ymm9
+  DB  196,193,25,114,241,5                ; vpslld        $0x5,%xmm9,%xmm12
+  DB  196,67,125,25,201,1                 ; vextractf128  $0x1,%ymm9,%xmm9
+  DB  196,193,49,114,241,5                ; vpslld        $0x5,%xmm9,%xmm9
+  DB  196,67,29,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm12,%ymm9
+  DB  196,65,60,84,219                    ; vandps        %ymm11,%ymm8,%ymm11
+  DB  196,193,25,114,243,4                ; vpslld        $0x4,%xmm11,%xmm12
+  DB  196,67,125,25,219,1                 ; vextractf128  $0x1,%ymm11,%xmm11
+  DB  196,193,33,114,243,4                ; vpslld        $0x4,%xmm11,%xmm11
+  DB  196,67,29,24,219,1                  ; vinsertf128   $0x1,%xmm11,%ymm12,%ymm11
+  DB  196,98,125,24,37,251,99,0,0         ; vbroadcastss  0x63fb(%rip),%ymm12        # 663c <_sk_callback_avx+0x138>
+  DB  196,98,125,24,45,246,99,0,0         ; vbroadcastss  0x63f6(%rip),%ymm13        # 6640 <_sk_callback_avx+0x13c>
+  DB  196,65,44,84,245                    ; vandps        %ymm13,%ymm10,%ymm14
+  DB  196,193,1,114,246,2                 ; vpslld        $0x2,%xmm14,%xmm15
+  DB  196,67,125,25,246,1                 ; vextractf128  $0x1,%ymm14,%xmm14
+  DB  196,193,9,114,246,2                 ; vpslld        $0x2,%xmm14,%xmm14
+  DB  196,67,5,24,246,1                   ; vinsertf128   $0x1,%xmm14,%ymm15,%ymm14
+  DB  196,65,60,84,237                    ; vandps        %ymm13,%ymm8,%ymm13
+  DB  196,65,17,254,253                   ; vpaddd        %xmm13,%xmm13,%xmm15
+  DB  196,67,125,25,237,1                 ; vextractf128  $0x1,%ymm13,%xmm13
+  DB  196,65,17,254,237                   ; vpaddd        %xmm13,%xmm13,%xmm13
+  DB  196,67,5,24,237,1                   ; vinsertf128   $0x1,%xmm13,%ymm15,%ymm13
+  DB  196,65,44,84,212                    ; vandps        %ymm12,%ymm10,%ymm10
+  DB  196,193,1,114,210,1                 ; vpsrld        $0x1,%xmm10,%xmm15
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,210,1                ; vpsrld        $0x1,%xmm10,%xmm10
+  DB  196,67,5,24,210,1                   ; vinsertf128   $0x1,%xmm10,%ymm15,%ymm10
+  DB  196,65,60,84,196                    ; vandps        %ymm12,%ymm8,%ymm8
+  DB  196,193,25,114,208,2                ; vpsrld        $0x2,%xmm8,%xmm12
+  DB  196,67,125,25,192,1                 ; vextractf128  $0x1,%ymm8,%xmm8
+  DB  196,193,57,114,208,2                ; vpsrld        $0x2,%xmm8,%xmm8
+  DB  196,67,29,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm12,%ymm8
+  DB  196,65,20,86,219                    ; vorps         %ymm11,%ymm13,%ymm11
+  DB  196,65,36,86,192                    ; vorps         %ymm8,%ymm11,%ymm8
+  DB  196,65,52,86,206                    ; vorps         %ymm14,%ymm9,%ymm9
+  DB  196,65,60,86,193                    ; vorps         %ymm9,%ymm8,%ymm8
+  DB  196,65,60,86,194                    ; vorps         %ymm10,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,13,97,99,0,0          ; vbroadcastss  0x6361(%rip),%ymm9        # 6644 <_sk_callback_avx+0x140>
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,13,87,99,0,0          ; vbroadcastss  0x6357(%rip),%ymm9        # 6648 <_sk_callback_avx+0x144>
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,8                     ; vbroadcastss  (%rax),%ymm9
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  197,188,88,192                      ; vaddps        %ymm0,%ymm8,%ymm0
+  DB  197,188,88,201                      ; vaddps        %ymm1,%ymm8,%ymm1
+  DB  197,188,88,210                      ; vaddps        %ymm2,%ymm8,%ymm2
+  DB  197,252,93,195                      ; vminps        %ymm3,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,192                      ; vmaxps        %ymm0,%ymm8,%ymm0
+  DB  197,244,93,203                      ; vminps        %ymm3,%ymm1,%ymm1
+  DB  197,188,95,201                      ; vmaxps        %ymm1,%ymm8,%ymm1
+  DB  197,236,93,211                      ; vminps        %ymm3,%ymm2,%ymm2
+  DB  197,188,95,210                      ; vmaxps        %ymm2,%ymm8,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_constant_color_avx
+_sk_constant_color_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,0                    ; vbroadcastss  (%rax),%ymm0
+  DB  196,226,125,24,72,4                 ; vbroadcastss  0x4(%rax),%ymm1
+  DB  196,226,125,24,80,8                 ; vbroadcastss  0x8(%rax),%ymm2
+  DB  196,226,125,24,88,12                ; vbroadcastss  0xc(%rax),%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_rgba_avx
+_sk_load_rgba_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,0                        ; vmovups       (%rax),%ymm0
+  DB  197,252,16,72,32                    ; vmovups       0x20(%rax),%ymm1
+  DB  197,252,16,80,64                    ; vmovups       0x40(%rax),%ymm2
+  DB  197,252,16,88,96                    ; vmovups       0x60(%rax),%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_rgba_avx
+_sk_store_rgba_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,17,0                        ; vmovups       %ymm0,(%rax)
+  DB  197,252,17,72,32                    ; vmovups       %ymm1,0x20(%rax)
+  DB  197,252,17,80,64                    ; vmovups       %ymm2,0x40(%rax)
+  DB  197,252,17,88,96                    ; vmovups       %ymm3,0x60(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clear_avx
+_sk_clear_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  197,228,87,219                      ; vxorps        %ymm3,%ymm3,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcatop_avx
+_sk_srcatop_avx LABEL PROC
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  196,98,125,24,5,175,98,0,0          ; vbroadcastss  0x62af(%rip),%ymm8        # 664c <_sk_callback_avx+0x148>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,204                       ; vmulps        %ymm4,%ymm8,%ymm9
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,60,89,205                       ; vmulps        %ymm5,%ymm8,%ymm9
+  DB  197,180,88,201                      ; vaddps        %ymm1,%ymm9,%ymm1
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,60,89,206                       ; vmulps        %ymm6,%ymm8,%ymm9
+  DB  197,180,88,210                      ; vaddps        %ymm2,%ymm9,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  196,193,100,88,216                  ; vaddps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstatop_avx
+_sk_dstatop_avx LABEL PROC
+  DB  197,100,89,196                      ; vmulps        %ymm4,%ymm3,%ymm8
+  DB  196,98,125,24,13,113,98,0,0         ; vbroadcastss  0x6271(%rip),%ymm9        # 6650 <_sk_callback_avx+0x14c>
+  DB  197,52,92,207                       ; vsubps        %ymm7,%ymm9,%ymm9
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  197,188,88,192                      ; vaddps        %ymm0,%ymm8,%ymm0
+  DB  197,100,89,197                      ; vmulps        %ymm5,%ymm3,%ymm8
+  DB  197,180,89,201                      ; vmulps        %ymm1,%ymm9,%ymm1
+  DB  197,188,88,201                      ; vaddps        %ymm1,%ymm8,%ymm1
+  DB  197,100,89,198                      ; vmulps        %ymm6,%ymm3,%ymm8
+  DB  197,180,89,210                      ; vmulps        %ymm2,%ymm9,%ymm2
+  DB  197,188,88,210                      ; vaddps        %ymm2,%ymm8,%ymm2
+  DB  197,100,89,199                      ; vmulps        %ymm7,%ymm3,%ymm8
+  DB  197,180,89,219                      ; vmulps        %ymm3,%ymm9,%ymm3
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcin_avx
+_sk_srcin_avx LABEL PROC
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstin_avx
+_sk_dstin_avx LABEL PROC
+  DB  197,228,89,196                      ; vmulps        %ymm4,%ymm3,%ymm0
+  DB  197,228,89,205                      ; vmulps        %ymm5,%ymm3,%ymm1
+  DB  197,228,89,214                      ; vmulps        %ymm6,%ymm3,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcout_avx
+_sk_srcout_avx LABEL PROC
+  DB  196,98,125,24,5,16,98,0,0           ; vbroadcastss  0x6210(%rip),%ymm8        # 6654 <_sk_callback_avx+0x150>
+  DB  197,60,92,199                       ; vsubps        %ymm7,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstout_avx
+_sk_dstout_avx LABEL PROC
+  DB  196,226,125,24,5,243,97,0,0         ; vbroadcastss  0x61f3(%rip),%ymm0        # 6658 <_sk_callback_avx+0x154>
+  DB  197,252,92,219                      ; vsubps        %ymm3,%ymm0,%ymm3
+  DB  197,228,89,196                      ; vmulps        %ymm4,%ymm3,%ymm0
+  DB  197,228,89,205                      ; vmulps        %ymm5,%ymm3,%ymm1
+  DB  197,228,89,214                      ; vmulps        %ymm6,%ymm3,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_avx
+_sk_srcover_avx LABEL PROC
+  DB  196,98,125,24,5,214,97,0,0          ; vbroadcastss  0x61d6(%rip),%ymm8        # 665c <_sk_callback_avx+0x158>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,204                       ; vmulps        %ymm4,%ymm8,%ymm9
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  197,60,89,205                       ; vmulps        %ymm5,%ymm8,%ymm9
+  DB  197,180,88,201                      ; vaddps        %ymm1,%ymm9,%ymm1
+  DB  197,60,89,206                       ; vmulps        %ymm6,%ymm8,%ymm9
+  DB  197,180,88,210                      ; vaddps        %ymm2,%ymm9,%ymm2
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstover_avx
+_sk_dstover_avx LABEL PROC
+  DB  196,98,125,24,5,169,97,0,0          ; vbroadcastss  0x61a9(%rip),%ymm8        # 6660 <_sk_callback_avx+0x15c>
+  DB  197,60,92,199                       ; vsubps        %ymm7,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,252,88,196                      ; vaddps        %ymm4,%ymm0,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,244,88,205                      ; vaddps        %ymm5,%ymm1,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,236,88,214                      ; vaddps        %ymm6,%ymm2,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_modulate_avx
+_sk_modulate_avx LABEL PROC
+  DB  197,252,89,196                      ; vmulps        %ymm4,%ymm0,%ymm0
+  DB  197,244,89,205                      ; vmulps        %ymm5,%ymm1,%ymm1
+  DB  197,236,89,214                      ; vmulps        %ymm6,%ymm2,%ymm2
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_multiply_avx
+_sk_multiply_avx LABEL PROC
+  DB  196,98,125,24,5,104,97,0,0          ; vbroadcastss  0x6168(%rip),%ymm8        # 6664 <_sk_callback_avx+0x160>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,52,89,208                       ; vmulps        %ymm0,%ymm9,%ymm10
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,220                       ; vmulps        %ymm4,%ymm8,%ymm11
+  DB  196,65,36,88,210                    ; vaddps        %ymm10,%ymm11,%ymm10
+  DB  197,252,89,196                      ; vmulps        %ymm4,%ymm0,%ymm0
+  DB  196,193,124,88,194                  ; vaddps        %ymm10,%ymm0,%ymm0
+  DB  197,52,89,209                       ; vmulps        %ymm1,%ymm9,%ymm10
+  DB  197,60,89,221                       ; vmulps        %ymm5,%ymm8,%ymm11
+  DB  196,65,36,88,210                    ; vaddps        %ymm10,%ymm11,%ymm10
+  DB  197,244,89,205                      ; vmulps        %ymm5,%ymm1,%ymm1
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  197,52,89,210                       ; vmulps        %ymm2,%ymm9,%ymm10
+  DB  197,60,89,222                       ; vmulps        %ymm6,%ymm8,%ymm11
+  DB  196,65,36,88,210                    ; vaddps        %ymm10,%ymm11,%ymm10
+  DB  197,236,89,214                      ; vmulps        %ymm6,%ymm2,%ymm2
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  197,52,89,203                       ; vmulps        %ymm3,%ymm9,%ymm9
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,88,216                  ; vaddps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_plus__avx
+_sk_plus__avx LABEL PROC
+  DB  197,252,88,196                      ; vaddps        %ymm4,%ymm0,%ymm0
+  DB  197,244,88,205                      ; vaddps        %ymm5,%ymm1,%ymm1
+  DB  197,236,88,214                      ; vaddps        %ymm6,%ymm2,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_screen_avx
+_sk_screen_avx LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,196                      ; vmulps        %ymm4,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,205                      ; vmulps        %ymm5,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,214                      ; vmulps        %ymm6,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  197,100,88,199                      ; vaddps        %ymm7,%ymm3,%ymm8
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  197,188,92,219                      ; vsubps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xor__avx
+_sk_xor__avx LABEL PROC
+  DB  196,98,125,24,5,183,96,0,0          ; vbroadcastss  0x60b7(%rip),%ymm8        # 6668 <_sk_callback_avx+0x164>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,212                       ; vmulps        %ymm4,%ymm8,%ymm10
+  DB  197,172,88,192                      ; vaddps        %ymm0,%ymm10,%ymm0
+  DB  197,180,89,201                      ; vmulps        %ymm1,%ymm9,%ymm1
+  DB  197,60,89,213                       ; vmulps        %ymm5,%ymm8,%ymm10
+  DB  197,172,88,201                      ; vaddps        %ymm1,%ymm10,%ymm1
+  DB  197,180,89,210                      ; vmulps        %ymm2,%ymm9,%ymm2
+  DB  197,60,89,214                       ; vmulps        %ymm6,%ymm8,%ymm10
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  197,180,89,219                      ; vmulps        %ymm3,%ymm9,%ymm3
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_darken_avx
+_sk_darken_avx LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  196,193,124,95,193                  ; vmaxps        %ymm9,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,193,116,95,201                  ; vmaxps        %ymm9,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,100,89,206                      ; vmulps        %ymm6,%ymm3,%ymm9
+  DB  196,193,108,95,209                  ; vmaxps        %ymm9,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,55,96,0,0           ; vbroadcastss  0x6037(%rip),%ymm8        # 666c <_sk_callback_avx+0x168>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lighten_avx
+_sk_lighten_avx LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  196,193,124,93,193                  ; vminps        %ymm9,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,193,116,93,201                  ; vminps        %ymm9,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,100,89,206                      ; vmulps        %ymm6,%ymm3,%ymm9
+  DB  196,193,108,93,209                  ; vminps        %ymm9,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,227,95,0,0          ; vbroadcastss  0x5fe3(%rip),%ymm8        # 6670 <_sk_callback_avx+0x16c>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_difference_avx
+_sk_difference_avx LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  196,193,124,93,193                  ; vminps        %ymm9,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,207                      ; vmulps        %ymm7,%ymm1,%ymm1
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,193,116,93,201                  ; vminps        %ymm9,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,215                      ; vmulps        %ymm7,%ymm2,%ymm2
+  DB  197,100,89,206                      ; vmulps        %ymm6,%ymm3,%ymm9
+  DB  196,193,108,93,209                  ; vminps        %ymm9,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,131,95,0,0          ; vbroadcastss  0x5f83(%rip),%ymm8        # 6674 <_sk_callback_avx+0x170>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_exclusion_avx
+_sk_exclusion_avx LABEL PROC
+  DB  197,124,88,196                      ; vaddps        %ymm4,%ymm0,%ymm8
+  DB  197,252,89,196                      ; vmulps        %ymm4,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,116,88,197                      ; vaddps        %ymm5,%ymm1,%ymm8
+  DB  197,244,89,205                      ; vmulps        %ymm5,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,188,92,201                      ; vsubps        %ymm1,%ymm8,%ymm1
+  DB  197,108,88,198                      ; vaddps        %ymm6,%ymm2,%ymm8
+  DB  197,236,89,214                      ; vmulps        %ymm6,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,188,92,210                      ; vsubps        %ymm2,%ymm8,%ymm2
+  DB  196,98,125,24,5,62,95,0,0           ; vbroadcastss  0x5f3e(%rip),%ymm8        # 6678 <_sk_callback_avx+0x174>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colorburn_avx
+_sk_colorburn_avx LABEL PROC
+  DB  196,98,125,24,5,41,95,0,0           ; vbroadcastss  0x5f29(%rip),%ymm8        # 667c <_sk_callback_avx+0x178>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,52,89,216                       ; vmulps        %ymm0,%ymm9,%ymm11
+  DB  196,65,44,87,210                    ; vxorps        %ymm10,%ymm10,%ymm10
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,228                       ; vmulps        %ymm4,%ymm8,%ymm12
+  DB  197,68,92,236                       ; vsubps        %ymm4,%ymm7,%ymm13
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  197,20,94,232                       ; vdivps        %ymm0,%ymm13,%ymm13
+  DB  196,65,68,93,237                    ; vminps        %ymm13,%ymm7,%ymm13
+  DB  196,65,68,92,237                    ; vsubps        %ymm13,%ymm7,%ymm13
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  196,65,36,88,237                    ; vaddps        %ymm13,%ymm11,%ymm13
+  DB  196,65,28,88,237                    ; vaddps        %ymm13,%ymm12,%ymm13
+  DB  197,28,88,224                       ; vaddps        %ymm0,%ymm12,%ymm12
+  DB  196,193,124,194,194,0               ; vcmpeqps      %ymm10,%ymm0,%ymm0
+  DB  196,195,21,74,196,0                 ; vblendvps     %ymm0,%ymm12,%ymm13,%ymm0
+  DB  197,92,194,231,0                    ; vcmpeqps      %ymm7,%ymm4,%ymm12
+  DB  197,36,88,220                       ; vaddps        %ymm4,%ymm11,%ymm11
+  DB  196,195,125,74,195,192              ; vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  DB  197,52,89,217                       ; vmulps        %ymm1,%ymm9,%ymm11
+  DB  197,60,89,229                       ; vmulps        %ymm5,%ymm8,%ymm12
+  DB  197,68,92,237                       ; vsubps        %ymm5,%ymm7,%ymm13
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  197,20,94,233                       ; vdivps        %ymm1,%ymm13,%ymm13
+  DB  196,65,68,93,237                    ; vminps        %ymm13,%ymm7,%ymm13
+  DB  196,65,68,92,237                    ; vsubps        %ymm13,%ymm7,%ymm13
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  196,65,36,88,237                    ; vaddps        %ymm13,%ymm11,%ymm13
+  DB  196,65,28,88,237                    ; vaddps        %ymm13,%ymm12,%ymm13
+  DB  197,28,88,225                       ; vaddps        %ymm1,%ymm12,%ymm12
+  DB  196,193,116,194,202,0               ; vcmpeqps      %ymm10,%ymm1,%ymm1
+  DB  196,195,21,74,204,16                ; vblendvps     %ymm1,%ymm12,%ymm13,%ymm1
+  DB  197,84,194,231,0                    ; vcmpeqps      %ymm7,%ymm5,%ymm12
+  DB  197,36,88,221                       ; vaddps        %ymm5,%ymm11,%ymm11
+  DB  196,195,117,74,203,192              ; vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  DB  197,52,89,202                       ; vmulps        %ymm2,%ymm9,%ymm9
+  DB  196,65,108,194,210,0                ; vcmpeqps      %ymm10,%ymm2,%ymm10
+  DB  197,60,89,222                       ; vmulps        %ymm6,%ymm8,%ymm11
+  DB  197,68,92,230                       ; vsubps        %ymm6,%ymm7,%ymm12
+  DB  197,28,89,227                       ; vmulps        %ymm3,%ymm12,%ymm12
+  DB  197,28,94,226                       ; vdivps        %ymm2,%ymm12,%ymm12
+  DB  197,164,88,210                      ; vaddps        %ymm2,%ymm11,%ymm2
+  DB  196,65,68,93,228                    ; vminps        %ymm12,%ymm7,%ymm12
+  DB  196,65,68,92,228                    ; vsubps        %ymm12,%ymm7,%ymm12
+  DB  197,28,89,227                       ; vmulps        %ymm3,%ymm12,%ymm12
+  DB  196,65,52,88,228                    ; vaddps        %ymm12,%ymm9,%ymm12
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,227,37,74,210,160               ; vblendvps     %ymm10,%ymm2,%ymm11,%ymm2
+  DB  197,76,194,215,0                    ; vcmpeqps      %ymm7,%ymm6,%ymm10
+  DB  197,52,88,206                       ; vaddps        %ymm6,%ymm9,%ymm9
+  DB  196,195,109,74,209,160              ; vblendvps     %ymm10,%ymm9,%ymm2,%ymm2
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colordodge_avx
+_sk_colordodge_avx LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,98,125,24,13,37,94,0,0          ; vbroadcastss  0x5e25(%rip),%ymm9        # 6680 <_sk_callback_avx+0x17c>
+  DB  197,52,92,215                       ; vsubps        %ymm7,%ymm9,%ymm10
+  DB  197,44,89,216                       ; vmulps        %ymm0,%ymm10,%ymm11
+  DB  197,52,92,203                       ; vsubps        %ymm3,%ymm9,%ymm9
+  DB  197,100,89,228                      ; vmulps        %ymm4,%ymm3,%ymm12
+  DB  197,100,92,232                      ; vsubps        %ymm0,%ymm3,%ymm13
+  DB  196,65,28,94,229                    ; vdivps        %ymm13,%ymm12,%ymm12
+  DB  197,52,89,236                       ; vmulps        %ymm4,%ymm9,%ymm13
+  DB  196,65,68,93,228                    ; vminps        %ymm12,%ymm7,%ymm12
+  DB  197,28,89,227                       ; vmulps        %ymm3,%ymm12,%ymm12
+  DB  196,65,36,88,228                    ; vaddps        %ymm12,%ymm11,%ymm12
+  DB  196,65,20,88,228                    ; vaddps        %ymm12,%ymm13,%ymm12
+  DB  197,20,88,232                       ; vaddps        %ymm0,%ymm13,%ymm13
+  DB  197,252,194,195,0                   ; vcmpeqps      %ymm3,%ymm0,%ymm0
+  DB  196,195,29,74,197,0                 ; vblendvps     %ymm0,%ymm13,%ymm12,%ymm0
+  DB  196,65,92,194,224,0                 ; vcmpeqps      %ymm8,%ymm4,%ymm12
+  DB  197,36,88,220                       ; vaddps        %ymm4,%ymm11,%ymm11
+  DB  196,195,125,74,195,192              ; vblendvps     %ymm12,%ymm11,%ymm0,%ymm0
+  DB  197,44,89,217                       ; vmulps        %ymm1,%ymm10,%ymm11
+  DB  197,100,89,229                      ; vmulps        %ymm5,%ymm3,%ymm12
+  DB  197,100,92,233                      ; vsubps        %ymm1,%ymm3,%ymm13
+  DB  196,65,28,94,229                    ; vdivps        %ymm13,%ymm12,%ymm12
+  DB  197,52,89,237                       ; vmulps        %ymm5,%ymm9,%ymm13
+  DB  196,65,68,93,228                    ; vminps        %ymm12,%ymm7,%ymm12
+  DB  197,28,89,227                       ; vmulps        %ymm3,%ymm12,%ymm12
+  DB  196,65,36,88,228                    ; vaddps        %ymm12,%ymm11,%ymm12
+  DB  196,65,20,88,228                    ; vaddps        %ymm12,%ymm13,%ymm12
+  DB  197,20,88,233                       ; vaddps        %ymm1,%ymm13,%ymm13
+  DB  197,244,194,203,0                   ; vcmpeqps      %ymm3,%ymm1,%ymm1
+  DB  196,195,29,74,205,16                ; vblendvps     %ymm1,%ymm13,%ymm12,%ymm1
+  DB  196,65,84,194,224,0                 ; vcmpeqps      %ymm8,%ymm5,%ymm12
+  DB  197,36,88,221                       ; vaddps        %ymm5,%ymm11,%ymm11
+  DB  196,195,117,74,203,192              ; vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  197,100,89,222                      ; vmulps        %ymm6,%ymm3,%ymm11
+  DB  197,100,92,226                      ; vsubps        %ymm2,%ymm3,%ymm12
+  DB  196,65,36,94,220                    ; vdivps        %ymm12,%ymm11,%ymm11
+  DB  197,52,89,230                       ; vmulps        %ymm6,%ymm9,%ymm12
+  DB  196,65,68,93,219                    ; vminps        %ymm11,%ymm7,%ymm11
+  DB  197,36,89,219                       ; vmulps        %ymm3,%ymm11,%ymm11
+  DB  196,65,44,88,219                    ; vaddps        %ymm11,%ymm10,%ymm11
+  DB  196,65,28,88,219                    ; vaddps        %ymm11,%ymm12,%ymm11
+  DB  197,28,88,226                       ; vaddps        %ymm2,%ymm12,%ymm12
+  DB  197,236,194,211,0                   ; vcmpeqps      %ymm3,%ymm2,%ymm2
+  DB  196,195,37,74,212,32                ; vblendvps     %ymm2,%ymm12,%ymm11,%ymm2
+  DB  196,65,76,194,192,0                 ; vcmpeqps      %ymm8,%ymm6,%ymm8
+  DB  197,44,88,214                       ; vaddps        %ymm6,%ymm10,%ymm10
+  DB  196,195,109,74,210,128              ; vblendvps     %ymm8,%ymm10,%ymm2,%ymm2
+  DB  197,52,89,199                       ; vmulps        %ymm7,%ymm9,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hardlight_avx
+_sk_hardlight_avx LABEL PROC
+  DB  196,98,125,24,5,55,93,0,0           ; vbroadcastss  0x5d37(%rip),%ymm8        # 6684 <_sk_callback_avx+0x180>
+  DB  197,60,92,215                       ; vsubps        %ymm7,%ymm8,%ymm10
+  DB  197,44,89,200                       ; vmulps        %ymm0,%ymm10,%ymm9
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,220                       ; vmulps        %ymm4,%ymm8,%ymm11
+  DB  196,65,36,88,217                    ; vaddps        %ymm9,%ymm11,%ymm11
+  DB  197,124,88,200                      ; vaddps        %ymm0,%ymm0,%ymm9
+  DB  197,52,194,227,2                    ; vcmpleps      %ymm3,%ymm9,%ymm12
+  DB  197,124,89,204                      ; vmulps        %ymm4,%ymm0,%ymm9
+  DB  196,65,52,88,233                    ; vaddps        %ymm9,%ymm9,%ymm13
+  DB  197,100,89,207                      ; vmulps        %ymm7,%ymm3,%ymm9
+  DB  197,68,92,244                       ; vsubps        %ymm4,%ymm7,%ymm14
+  DB  197,228,92,192                      ; vsubps        %ymm0,%ymm3,%ymm0
+  DB  196,193,124,89,198                  ; vmulps        %ymm14,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,180,92,192                      ; vsubps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,197,192              ; vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  DB  197,164,88,192                      ; vaddps        %ymm0,%ymm11,%ymm0
+  DB  197,44,89,217                       ; vmulps        %ymm1,%ymm10,%ymm11
+  DB  197,60,89,229                       ; vmulps        %ymm5,%ymm8,%ymm12
+  DB  196,65,28,88,219                    ; vaddps        %ymm11,%ymm12,%ymm11
+  DB  197,116,88,225                      ; vaddps        %ymm1,%ymm1,%ymm12
+  DB  197,28,194,227,2                    ; vcmpleps      %ymm3,%ymm12,%ymm12
+  DB  197,116,89,237                      ; vmulps        %ymm5,%ymm1,%ymm13
+  DB  196,65,20,88,237                    ; vaddps        %ymm13,%ymm13,%ymm13
+  DB  197,68,92,245                       ; vsubps        %ymm5,%ymm7,%ymm14
+  DB  197,228,92,201                      ; vsubps        %ymm1,%ymm3,%ymm1
+  DB  196,193,116,89,206                  ; vmulps        %ymm14,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,180,92,201                      ; vsubps        %ymm1,%ymm9,%ymm1
+  DB  196,195,117,74,205,192              ; vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  197,60,89,222                       ; vmulps        %ymm6,%ymm8,%ymm11
+  DB  196,65,36,88,210                    ; vaddps        %ymm10,%ymm11,%ymm10
+  DB  197,108,88,218                      ; vaddps        %ymm2,%ymm2,%ymm11
+  DB  197,36,194,219,2                    ; vcmpleps      %ymm3,%ymm11,%ymm11
+  DB  197,108,89,230                      ; vmulps        %ymm6,%ymm2,%ymm12
+  DB  196,65,28,88,228                    ; vaddps        %ymm12,%ymm12,%ymm12
+  DB  197,68,92,238                       ; vsubps        %ymm6,%ymm7,%ymm13
+  DB  197,228,92,210                      ; vsubps        %ymm2,%ymm3,%ymm2
+  DB  196,193,108,89,213                  ; vmulps        %ymm13,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,180,92,210                      ; vsubps        %ymm2,%ymm9,%ymm2
+  DB  196,195,109,74,212,176              ; vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_overlay_avx
+_sk_overlay_avx LABEL PROC
+  DB  196,98,125,24,5,96,92,0,0           ; vbroadcastss  0x5c60(%rip),%ymm8        # 6688 <_sk_callback_avx+0x184>
+  DB  197,60,92,215                       ; vsubps        %ymm7,%ymm8,%ymm10
+  DB  197,44,89,200                       ; vmulps        %ymm0,%ymm10,%ymm9
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,220                       ; vmulps        %ymm4,%ymm8,%ymm11
+  DB  196,65,36,88,217                    ; vaddps        %ymm9,%ymm11,%ymm11
+  DB  197,92,88,204                       ; vaddps        %ymm4,%ymm4,%ymm9
+  DB  197,52,194,231,2                    ; vcmpleps      %ymm7,%ymm9,%ymm12
+  DB  197,124,89,204                      ; vmulps        %ymm4,%ymm0,%ymm9
+  DB  196,65,52,88,233                    ; vaddps        %ymm9,%ymm9,%ymm13
+  DB  197,100,89,207                      ; vmulps        %ymm7,%ymm3,%ymm9
+  DB  197,68,92,244                       ; vsubps        %ymm4,%ymm7,%ymm14
+  DB  197,228,92,192                      ; vsubps        %ymm0,%ymm3,%ymm0
+  DB  196,193,124,89,198                  ; vmulps        %ymm14,%ymm0,%ymm0
+  DB  197,252,88,192                      ; vaddps        %ymm0,%ymm0,%ymm0
+  DB  197,180,92,192                      ; vsubps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,197,192              ; vblendvps     %ymm12,%ymm13,%ymm0,%ymm0
+  DB  197,164,88,192                      ; vaddps        %ymm0,%ymm11,%ymm0
+  DB  197,44,89,217                       ; vmulps        %ymm1,%ymm10,%ymm11
+  DB  197,60,89,229                       ; vmulps        %ymm5,%ymm8,%ymm12
+  DB  196,65,28,88,219                    ; vaddps        %ymm11,%ymm12,%ymm11
+  DB  197,84,88,229                       ; vaddps        %ymm5,%ymm5,%ymm12
+  DB  197,28,194,231,2                    ; vcmpleps      %ymm7,%ymm12,%ymm12
+  DB  197,116,89,237                      ; vmulps        %ymm5,%ymm1,%ymm13
+  DB  196,65,20,88,237                    ; vaddps        %ymm13,%ymm13,%ymm13
+  DB  197,68,92,245                       ; vsubps        %ymm5,%ymm7,%ymm14
+  DB  197,228,92,201                      ; vsubps        %ymm1,%ymm3,%ymm1
+  DB  196,193,116,89,206                  ; vmulps        %ymm14,%ymm1,%ymm1
+  DB  197,244,88,201                      ; vaddps        %ymm1,%ymm1,%ymm1
+  DB  197,180,92,201                      ; vsubps        %ymm1,%ymm9,%ymm1
+  DB  196,195,117,74,205,192              ; vblendvps     %ymm12,%ymm13,%ymm1,%ymm1
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  197,60,89,222                       ; vmulps        %ymm6,%ymm8,%ymm11
+  DB  196,65,36,88,210                    ; vaddps        %ymm10,%ymm11,%ymm10
+  DB  197,76,88,222                       ; vaddps        %ymm6,%ymm6,%ymm11
+  DB  197,36,194,223,2                    ; vcmpleps      %ymm7,%ymm11,%ymm11
+  DB  197,108,89,230                      ; vmulps        %ymm6,%ymm2,%ymm12
+  DB  196,65,28,88,228                    ; vaddps        %ymm12,%ymm12,%ymm12
+  DB  197,68,92,238                       ; vsubps        %ymm6,%ymm7,%ymm13
+  DB  197,228,92,210                      ; vsubps        %ymm2,%ymm3,%ymm2
+  DB  196,193,108,89,213                  ; vmulps        %ymm13,%ymm2,%ymm2
+  DB  197,236,88,210                      ; vaddps        %ymm2,%ymm2,%ymm2
+  DB  197,180,92,210                      ; vsubps        %ymm2,%ymm9,%ymm2
+  DB  196,195,109,74,212,176              ; vblendvps     %ymm11,%ymm12,%ymm2,%ymm2
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_softlight_avx
+_sk_softlight_avx LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,252,17,84,36,32                 ; vmovups       %ymm2,0x20(%rsp)
+  DB  197,252,40,209                      ; vmovaps       %ymm1,%ymm2
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  196,65,52,87,201                    ; vxorps        %ymm9,%ymm9,%ymm9
+  DB  197,52,194,215,1                    ; vcmpltps      %ymm7,%ymm9,%ymm10
+  DB  197,92,94,199                       ; vdivps        %ymm7,%ymm4,%ymm8
+  DB  196,67,53,74,224,160                ; vblendvps     %ymm10,%ymm8,%ymm9,%ymm12
+  DB  196,65,28,88,196                    ; vaddps        %ymm12,%ymm12,%ymm8
+  DB  196,65,60,88,192                    ; vaddps        %ymm8,%ymm8,%ymm8
+  DB  196,65,60,89,216                    ; vmulps        %ymm8,%ymm8,%ymm11
+  DB  196,65,60,88,195                    ; vaddps        %ymm11,%ymm8,%ymm8
+  DB  196,98,125,24,29,83,91,0,0          ; vbroadcastss  0x5b53(%rip),%ymm11        # 6690 <_sk_callback_avx+0x18c>
+  DB  196,65,28,88,235                    ; vaddps        %ymm11,%ymm12,%ymm13
+  DB  196,65,20,89,192                    ; vmulps        %ymm8,%ymm13,%ymm8
+  DB  196,98,125,24,45,68,91,0,0          ; vbroadcastss  0x5b44(%rip),%ymm13        # 6694 <_sk_callback_avx+0x190>
+  DB  196,65,28,89,245                    ; vmulps        %ymm13,%ymm12,%ymm14
+  DB  196,65,12,88,192                    ; vaddps        %ymm8,%ymm14,%ymm8
+  DB  196,65,124,82,244                   ; vrsqrtps      %ymm12,%ymm14
+  DB  196,65,124,83,246                   ; vrcpps        %ymm14,%ymm14
+  DB  196,65,12,92,244                    ; vsubps        %ymm12,%ymm14,%ymm14
+  DB  197,92,88,252                       ; vaddps        %ymm4,%ymm4,%ymm15
+  DB  196,65,4,88,255                     ; vaddps        %ymm15,%ymm15,%ymm15
+  DB  197,4,194,255,2                     ; vcmpleps      %ymm7,%ymm15,%ymm15
+  DB  196,67,13,74,240,240                ; vblendvps     %ymm15,%ymm8,%ymm14,%ymm14
+  DB  197,116,88,249                      ; vaddps        %ymm1,%ymm1,%ymm15
+  DB  196,98,125,24,5,2,91,0,0            ; vbroadcastss  0x5b02(%rip),%ymm8        # 668c <_sk_callback_avx+0x188>
+  DB  196,65,60,92,228                    ; vsubps        %ymm12,%ymm8,%ymm12
+  DB  197,132,92,195                      ; vsubps        %ymm3,%ymm15,%ymm0
+  DB  196,65,124,89,228                   ; vmulps        %ymm12,%ymm0,%ymm12
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  196,193,124,89,198                  ; vmulps        %ymm14,%ymm0,%ymm0
+  DB  197,100,89,244                      ; vmulps        %ymm4,%ymm3,%ymm14
+  DB  197,140,88,192                      ; vaddps        %ymm0,%ymm14,%ymm0
+  DB  197,28,88,227                       ; vaddps        %ymm3,%ymm12,%ymm12
+  DB  197,28,89,228                       ; vmulps        %ymm4,%ymm12,%ymm12
+  DB  197,4,194,243,2                     ; vcmpleps      %ymm3,%ymm15,%ymm14
+  DB  196,195,125,74,196,224              ; vblendvps     %ymm14,%ymm12,%ymm0,%ymm0
+  DB  197,252,17,4,36                     ; vmovups       %ymm0,(%rsp)
+  DB  197,212,94,199                      ; vdivps        %ymm7,%ymm5,%ymm0
+  DB  196,227,53,74,192,160               ; vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  DB  197,124,88,240                      ; vaddps        %ymm0,%ymm0,%ymm14
+  DB  196,65,12,88,246                    ; vaddps        %ymm14,%ymm14,%ymm14
+  DB  196,65,12,89,254                    ; vmulps        %ymm14,%ymm14,%ymm15
+  DB  196,65,12,88,247                    ; vaddps        %ymm15,%ymm14,%ymm14
+  DB  196,65,124,88,251                   ; vaddps        %ymm11,%ymm0,%ymm15
+  DB  196,65,4,89,246                     ; vmulps        %ymm14,%ymm15,%ymm14
+  DB  196,65,124,89,253                   ; vmulps        %ymm13,%ymm0,%ymm15
+  DB  196,65,4,88,246                     ; vaddps        %ymm14,%ymm15,%ymm14
+  DB  197,124,82,248                      ; vrsqrtps      %ymm0,%ymm15
+  DB  196,65,124,83,255                   ; vrcpps        %ymm15,%ymm15
+  DB  197,4,92,248                        ; vsubps        %ymm0,%ymm15,%ymm15
+  DB  197,84,88,229                       ; vaddps        %ymm5,%ymm5,%ymm12
+  DB  196,65,28,88,228                    ; vaddps        %ymm12,%ymm12,%ymm12
+  DB  197,28,194,231,2                    ; vcmpleps      %ymm7,%ymm12,%ymm12
+  DB  196,67,5,74,230,192                 ; vblendvps     %ymm12,%ymm14,%ymm15,%ymm12
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,108,88,242                      ; vaddps        %ymm2,%ymm2,%ymm14
+  DB  197,12,92,251                       ; vsubps        %ymm3,%ymm14,%ymm15
+  DB  197,132,89,192                      ; vmulps        %ymm0,%ymm15,%ymm0
+  DB  197,4,89,255                        ; vmulps        %ymm7,%ymm15,%ymm15
+  DB  196,65,4,89,228                     ; vmulps        %ymm12,%ymm15,%ymm12
+  DB  197,100,89,253                      ; vmulps        %ymm5,%ymm3,%ymm15
+  DB  196,65,4,88,228                     ; vaddps        %ymm12,%ymm15,%ymm12
+  DB  197,252,88,195                      ; vaddps        %ymm3,%ymm0,%ymm0
+  DB  197,252,89,197                      ; vmulps        %ymm5,%ymm0,%ymm0
+  DB  197,12,194,243,2                    ; vcmpleps      %ymm3,%ymm14,%ymm14
+  DB  196,99,29,74,240,224                ; vblendvps     %ymm14,%ymm0,%ymm12,%ymm14
+  DB  197,204,94,199                      ; vdivps        %ymm7,%ymm6,%ymm0
+  DB  196,227,53,74,192,160               ; vblendvps     %ymm10,%ymm0,%ymm9,%ymm0
+  DB  197,124,88,200                      ; vaddps        %ymm0,%ymm0,%ymm9
+  DB  196,65,52,88,201                    ; vaddps        %ymm9,%ymm9,%ymm9
+  DB  196,65,52,89,209                    ; vmulps        %ymm9,%ymm9,%ymm10
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,65,124,88,211                   ; vaddps        %ymm11,%ymm0,%ymm10
+  DB  196,65,44,89,201                    ; vmulps        %ymm9,%ymm10,%ymm9
+  DB  196,65,124,89,213                   ; vmulps        %ymm13,%ymm0,%ymm10
+  DB  196,65,44,88,201                    ; vaddps        %ymm9,%ymm10,%ymm9
+  DB  197,124,82,208                      ; vrsqrtps      %ymm0,%ymm10
+  DB  196,65,124,83,210                   ; vrcpps        %ymm10,%ymm10
+  DB  197,44,92,208                       ; vsubps        %ymm0,%ymm10,%ymm10
+  DB  197,76,88,222                       ; vaddps        %ymm6,%ymm6,%ymm11
+  DB  196,65,36,88,219                    ; vaddps        %ymm11,%ymm11,%ymm11
+  DB  197,36,194,223,2                    ; vcmpleps      %ymm7,%ymm11,%ymm11
+  DB  196,67,45,74,201,176                ; vblendvps     %ymm11,%ymm9,%ymm10,%ymm9
+  DB  197,124,16,100,36,32                ; vmovups       0x20(%rsp),%ymm12
+  DB  196,65,28,88,212                    ; vaddps        %ymm12,%ymm12,%ymm10
+  DB  197,44,92,219                       ; vsubps        %ymm3,%ymm10,%ymm11
+  DB  197,188,92,192                      ; vsubps        %ymm0,%ymm8,%ymm0
+  DB  197,164,89,192                      ; vmulps        %ymm0,%ymm11,%ymm0
+  DB  197,36,89,223                       ; vmulps        %ymm7,%ymm11,%ymm11
+  DB  196,65,36,89,201                    ; vmulps        %ymm9,%ymm11,%ymm9
+  DB  197,100,89,222                      ; vmulps        %ymm6,%ymm3,%ymm11
+  DB  196,65,36,88,201                    ; vaddps        %ymm9,%ymm11,%ymm9
+  DB  197,252,88,195                      ; vaddps        %ymm3,%ymm0,%ymm0
+  DB  197,252,89,198                      ; vmulps        %ymm6,%ymm0,%ymm0
+  DB  197,44,194,211,2                    ; vcmpleps      %ymm3,%ymm10,%ymm10
+  DB  196,99,53,74,200,160                ; vblendvps     %ymm10,%ymm0,%ymm9,%ymm9
+  DB  197,60,92,215                       ; vsubps        %ymm7,%ymm8,%ymm10
+  DB  197,172,89,193                      ; vmulps        %ymm1,%ymm10,%ymm0
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,188,89,204                      ; vmulps        %ymm4,%ymm8,%ymm1
+  DB  197,244,88,192                      ; vaddps        %ymm0,%ymm1,%ymm0
+  DB  197,252,88,4,36                     ; vaddps        (%rsp),%ymm0,%ymm0
+  DB  197,172,89,202                      ; vmulps        %ymm2,%ymm10,%ymm1
+  DB  197,188,89,213                      ; vmulps        %ymm5,%ymm8,%ymm2
+  DB  197,236,88,201                      ; vaddps        %ymm1,%ymm2,%ymm1
+  DB  196,193,116,88,206                  ; vaddps        %ymm14,%ymm1,%ymm1
+  DB  196,193,44,89,212                   ; vmulps        %ymm12,%ymm10,%ymm2
+  DB  197,60,89,214                       ; vmulps        %ymm6,%ymm8,%ymm10
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  196,193,108,88,209                  ; vaddps        %ymm9,%ymm2,%ymm2
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  197,188,88,219                      ; vaddps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hue_avx
+_sk_hue_avx LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,252,17,84,36,32                 ; vmovups       %ymm2,0x20(%rsp)
+  DB  197,124,40,193                      ; vmovaps       %ymm1,%ymm8
+  DB  197,124,17,4,36                     ; vmovups       %ymm8,(%rsp)
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,116,89,203                      ; vmulps        %ymm3,%ymm1,%ymm9
+  DB  197,60,89,211                       ; vmulps        %ymm3,%ymm8,%ymm10
+  DB  197,108,89,219                      ; vmulps        %ymm3,%ymm2,%ymm11
+  DB  197,84,95,198                       ; vmaxps        %ymm6,%ymm5,%ymm8
+  DB  196,65,92,95,192                    ; vmaxps        %ymm8,%ymm4,%ymm8
+  DB  197,84,93,230                       ; vminps        %ymm6,%ymm5,%ymm12
+  DB  196,65,92,93,228                    ; vminps        %ymm12,%ymm4,%ymm12
+  DB  196,65,60,92,196                    ; vsubps        %ymm12,%ymm8,%ymm8
+  DB  197,60,89,227                       ; vmulps        %ymm3,%ymm8,%ymm12
+  DB  196,65,44,93,195                    ; vminps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,93,232                    ; vminps        %ymm8,%ymm9,%ymm13
+  DB  196,65,44,95,195                    ; vmaxps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,95,192                    ; vmaxps        %ymm8,%ymm9,%ymm8
+  DB  196,65,60,92,245                    ; vsubps        %ymm13,%ymm8,%ymm14
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,65,12,194,248,0                 ; vcmpeqps      %ymm8,%ymm14,%ymm15
+  DB  196,65,52,92,205                    ; vsubps        %ymm13,%ymm9,%ymm9
+  DB  196,65,28,89,201                    ; vmulps        %ymm9,%ymm12,%ymm9
+  DB  196,65,52,94,206                    ; vdivps        %ymm14,%ymm9,%ymm9
+  DB  196,67,53,74,200,240                ; vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  DB  196,65,44,92,213                    ; vsubps        %ymm13,%ymm10,%ymm10
+  DB  196,65,28,89,210                    ; vmulps        %ymm10,%ymm12,%ymm10
+  DB  196,65,44,94,214                    ; vdivps        %ymm14,%ymm10,%ymm10
+  DB  196,67,45,74,208,240                ; vblendvps     %ymm15,%ymm8,%ymm10,%ymm10
+  DB  196,65,36,92,221                    ; vsubps        %ymm13,%ymm11,%ymm11
+  DB  196,65,28,89,219                    ; vmulps        %ymm11,%ymm12,%ymm11
+  DB  196,65,36,94,222                    ; vdivps        %ymm14,%ymm11,%ymm11
+  DB  196,67,37,74,224,240                ; vblendvps     %ymm15,%ymm8,%ymm11,%ymm12
+  DB  196,98,125,24,53,204,88,0,0         ; vbroadcastss  0x58cc(%rip),%ymm14        # 6698 <_sk_callback_avx+0x194>
+  DB  196,65,92,89,222                    ; vmulps        %ymm14,%ymm4,%ymm11
+  DB  196,98,125,24,61,194,88,0,0         ; vbroadcastss  0x58c2(%rip),%ymm15        # 669c <_sk_callback_avx+0x198>
+  DB  196,65,84,89,239                    ; vmulps        %ymm15,%ymm5,%ymm13
+  DB  196,65,36,88,221                    ; vaddps        %ymm13,%ymm11,%ymm11
+  DB  196,226,125,24,5,179,88,0,0         ; vbroadcastss  0x58b3(%rip),%ymm0        # 66a0 <_sk_callback_avx+0x19c>
+  DB  197,76,89,232                       ; vmulps        %ymm0,%ymm6,%ymm13
+  DB  196,65,36,88,221                    ; vaddps        %ymm13,%ymm11,%ymm11
+  DB  196,65,52,89,238                    ; vmulps        %ymm14,%ymm9,%ymm13
+  DB  196,193,44,89,215                   ; vmulps        %ymm15,%ymm10,%ymm2
+  DB  197,148,88,210                      ; vaddps        %ymm2,%ymm13,%ymm2
+  DB  197,28,89,232                       ; vmulps        %ymm0,%ymm12,%ymm13
+  DB  196,193,108,88,213                  ; vaddps        %ymm13,%ymm2,%ymm2
+  DB  197,36,89,219                       ; vmulps        %ymm3,%ymm11,%ymm11
+  DB  197,164,92,210                      ; vsubps        %ymm2,%ymm11,%ymm2
+  DB  197,52,88,202                       ; vaddps        %ymm2,%ymm9,%ymm9
+  DB  197,44,88,218                       ; vaddps        %ymm2,%ymm10,%ymm11
+  DB  197,28,88,226                       ; vaddps        %ymm2,%ymm12,%ymm12
+  DB  196,193,36,93,212                   ; vminps        %ymm12,%ymm11,%ymm2
+  DB  197,52,93,234                       ; vminps        %ymm2,%ymm9,%ymm13
+  DB  196,193,52,89,214                   ; vmulps        %ymm14,%ymm9,%ymm2
+  DB  196,65,36,89,215                    ; vmulps        %ymm15,%ymm11,%ymm10
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  197,156,89,192                      ; vmulps        %ymm0,%ymm12,%ymm0
+  DB  197,124,88,210                      ; vaddps        %ymm2,%ymm0,%ymm10
+  DB  196,193,52,92,194                   ; vsubps        %ymm10,%ymm9,%ymm0
+  DB  197,172,89,192                      ; vmulps        %ymm0,%ymm10,%ymm0
+  DB  196,193,44,92,213                   ; vsubps        %ymm13,%ymm10,%ymm2
+  DB  197,252,94,194                      ; vdivps        %ymm2,%ymm0,%ymm0
+  DB  196,65,36,92,242                    ; vsubps        %ymm10,%ymm11,%ymm14
+  DB  196,65,44,89,246                    ; vmulps        %ymm14,%ymm10,%ymm14
+  DB  197,12,94,242                       ; vdivps        %ymm2,%ymm14,%ymm14
+  DB  196,65,28,92,250                    ; vsubps        %ymm10,%ymm12,%ymm15
+  DB  196,65,44,89,255                    ; vmulps        %ymm15,%ymm10,%ymm15
+  DB  197,132,94,210                      ; vdivps        %ymm2,%ymm15,%ymm2
+  DB  196,65,60,194,237,2                 ; vcmpleps      %ymm13,%ymm8,%ymm13
+  DB  196,65,44,88,246                    ; vaddps        %ymm14,%ymm10,%ymm14
+  DB  196,67,13,74,243,208                ; vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  DB  196,65,36,95,220                    ; vmaxps        %ymm12,%ymm11,%ymm11
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  196,195,109,74,212,208              ; vblendvps     %ymm13,%ymm12,%ymm2,%ymm2
+  DB  197,172,88,192                      ; vaddps        %ymm0,%ymm10,%ymm0
+  DB  196,195,125,74,193,208              ; vblendvps     %ymm13,%ymm9,%ymm0,%ymm0
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,52,95,203                    ; vmaxps        %ymm11,%ymm9,%ymm9
+  DB  196,65,124,92,218                   ; vsubps        %ymm10,%ymm0,%ymm11
+  DB  196,65,28,92,234                    ; vsubps        %ymm10,%ymm12,%ymm13
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,28,194,249,1                 ; vcmpltps      %ymm9,%ymm12,%ymm15
+  DB  196,65,52,92,202                    ; vsubps        %ymm10,%ymm9,%ymm9
+  DB  196,65,36,94,217                    ; vdivps        %ymm9,%ymm11,%ymm11
+  DB  196,65,44,88,219                    ; vaddps        %ymm11,%ymm10,%ymm11
+  DB  196,195,125,74,195,240              ; vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  DB  196,65,12,92,218                    ; vsubps        %ymm10,%ymm14,%ymm11
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,36,94,217                    ; vdivps        %ymm9,%ymm11,%ymm11
+  DB  196,65,44,88,219                    ; vaddps        %ymm11,%ymm10,%ymm11
+  DB  196,67,13,74,219,240                ; vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  DB  196,65,108,92,242                   ; vsubps        %ymm10,%ymm2,%ymm14
+  DB  196,65,20,89,238                    ; vmulps        %ymm14,%ymm13,%ymm13
+  DB  196,65,20,94,201                    ; vdivps        %ymm9,%ymm13,%ymm9
+  DB  196,65,44,88,201                    ; vaddps        %ymm9,%ymm10,%ymm9
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,65,36,95,208                    ; vmaxps        %ymm8,%ymm11,%ymm10
+  DB  196,195,109,74,209,240              ; vblendvps     %ymm15,%ymm9,%ymm2,%ymm2
+  DB  196,193,108,95,208                  ; vmaxps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,24,5,140,87,0,0          ; vbroadcastss  0x578c(%rip),%ymm8        # 66a4 <_sk_callback_avx+0x1a0>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,180,89,201                      ; vmulps        %ymm1,%ymm9,%ymm1
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,220                       ; vmulps        %ymm4,%ymm8,%ymm11
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  197,244,88,192                      ; vaddps        %ymm0,%ymm1,%ymm0
+  DB  197,180,89,12,36                    ; vmulps        (%rsp),%ymm9,%ymm1
+  DB  197,60,89,221                       ; vmulps        %ymm5,%ymm8,%ymm11
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  197,52,89,76,36,32                  ; vmulps        0x20(%rsp),%ymm9,%ymm9
+  DB  197,60,89,198                       ; vmulps        %ymm6,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,188,88,210                      ; vaddps        %ymm2,%ymm8,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_saturation_avx
+_sk_saturation_avx LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,124,40,193                      ; vmovaps       %ymm1,%ymm8
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,100,89,204                      ; vmulps        %ymm4,%ymm3,%ymm9
+  DB  197,100,89,213                      ; vmulps        %ymm5,%ymm3,%ymm10
+  DB  197,100,89,222                      ; vmulps        %ymm6,%ymm3,%ymm11
+  DB  197,252,17,84,36,32                 ; vmovups       %ymm2,0x20(%rsp)
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,252,17,4,36                     ; vmovups       %ymm0,(%rsp)
+  DB  197,124,95,194                      ; vmaxps        %ymm2,%ymm0,%ymm8
+  DB  196,65,116,95,192                   ; vmaxps        %ymm8,%ymm1,%ymm8
+  DB  197,124,93,226                      ; vminps        %ymm2,%ymm0,%ymm12
+  DB  196,65,116,93,228                   ; vminps        %ymm12,%ymm1,%ymm12
+  DB  196,65,60,92,196                    ; vsubps        %ymm12,%ymm8,%ymm8
+  DB  197,60,89,231                       ; vmulps        %ymm7,%ymm8,%ymm12
+  DB  196,65,44,93,195                    ; vminps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,93,232                    ; vminps        %ymm8,%ymm9,%ymm13
+  DB  196,65,44,95,195                    ; vmaxps        %ymm11,%ymm10,%ymm8
+  DB  196,65,52,95,192                    ; vmaxps        %ymm8,%ymm9,%ymm8
+  DB  196,65,60,92,245                    ; vsubps        %ymm13,%ymm8,%ymm14
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,65,12,194,248,0                 ; vcmpeqps      %ymm8,%ymm14,%ymm15
+  DB  196,65,52,92,205                    ; vsubps        %ymm13,%ymm9,%ymm9
+  DB  196,65,28,89,201                    ; vmulps        %ymm9,%ymm12,%ymm9
+  DB  196,65,52,94,206                    ; vdivps        %ymm14,%ymm9,%ymm9
+  DB  196,67,53,74,200,240                ; vblendvps     %ymm15,%ymm8,%ymm9,%ymm9
+  DB  196,65,44,92,213                    ; vsubps        %ymm13,%ymm10,%ymm10
+  DB  196,65,28,89,210                    ; vmulps        %ymm10,%ymm12,%ymm10
+  DB  196,65,44,94,214                    ; vdivps        %ymm14,%ymm10,%ymm10
+  DB  196,67,45,74,208,240                ; vblendvps     %ymm15,%ymm8,%ymm10,%ymm10
+  DB  196,65,36,92,221                    ; vsubps        %ymm13,%ymm11,%ymm11
+  DB  196,65,28,89,219                    ; vmulps        %ymm11,%ymm12,%ymm11
+  DB  196,65,36,94,222                    ; vdivps        %ymm14,%ymm11,%ymm11
+  DB  196,67,37,74,224,240                ; vblendvps     %ymm15,%ymm8,%ymm11,%ymm12
+  DB  196,98,125,24,53,148,86,0,0         ; vbroadcastss  0x5694(%rip),%ymm14        # 66a8 <_sk_callback_avx+0x1a4>
+  DB  196,65,92,89,222                    ; vmulps        %ymm14,%ymm4,%ymm11
+  DB  196,98,125,24,61,138,86,0,0         ; vbroadcastss  0x568a(%rip),%ymm15        # 66ac <_sk_callback_avx+0x1a8>
+  DB  196,65,84,89,239                    ; vmulps        %ymm15,%ymm5,%ymm13
+  DB  196,65,36,88,221                    ; vaddps        %ymm13,%ymm11,%ymm11
+  DB  196,226,125,24,5,123,86,0,0         ; vbroadcastss  0x567b(%rip),%ymm0        # 66b0 <_sk_callback_avx+0x1ac>
+  DB  197,76,89,232                       ; vmulps        %ymm0,%ymm6,%ymm13
+  DB  196,65,36,88,221                    ; vaddps        %ymm13,%ymm11,%ymm11
+  DB  196,65,52,89,238                    ; vmulps        %ymm14,%ymm9,%ymm13
+  DB  196,193,44,89,215                   ; vmulps        %ymm15,%ymm10,%ymm2
+  DB  197,148,88,210                      ; vaddps        %ymm2,%ymm13,%ymm2
+  DB  197,28,89,232                       ; vmulps        %ymm0,%ymm12,%ymm13
+  DB  196,193,108,88,213                  ; vaddps        %ymm13,%ymm2,%ymm2
+  DB  197,36,89,219                       ; vmulps        %ymm3,%ymm11,%ymm11
+  DB  197,164,92,210                      ; vsubps        %ymm2,%ymm11,%ymm2
+  DB  197,52,88,202                       ; vaddps        %ymm2,%ymm9,%ymm9
+  DB  197,44,88,218                       ; vaddps        %ymm2,%ymm10,%ymm11
+  DB  197,28,88,226                       ; vaddps        %ymm2,%ymm12,%ymm12
+  DB  196,193,36,93,212                   ; vminps        %ymm12,%ymm11,%ymm2
+  DB  197,52,93,234                       ; vminps        %ymm2,%ymm9,%ymm13
+  DB  196,193,52,89,214                   ; vmulps        %ymm14,%ymm9,%ymm2
+  DB  196,65,36,89,215                    ; vmulps        %ymm15,%ymm11,%ymm10
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  197,156,89,192                      ; vmulps        %ymm0,%ymm12,%ymm0
+  DB  197,124,88,210                      ; vaddps        %ymm2,%ymm0,%ymm10
+  DB  196,193,52,92,194                   ; vsubps        %ymm10,%ymm9,%ymm0
+  DB  197,172,89,192                      ; vmulps        %ymm0,%ymm10,%ymm0
+  DB  196,193,44,92,213                   ; vsubps        %ymm13,%ymm10,%ymm2
+  DB  197,252,94,194                      ; vdivps        %ymm2,%ymm0,%ymm0
+  DB  196,65,36,92,242                    ; vsubps        %ymm10,%ymm11,%ymm14
+  DB  196,65,44,89,246                    ; vmulps        %ymm14,%ymm10,%ymm14
+  DB  197,12,94,242                       ; vdivps        %ymm2,%ymm14,%ymm14
+  DB  196,65,28,92,250                    ; vsubps        %ymm10,%ymm12,%ymm15
+  DB  196,65,44,89,255                    ; vmulps        %ymm15,%ymm10,%ymm15
+  DB  197,132,94,210                      ; vdivps        %ymm2,%ymm15,%ymm2
+  DB  196,65,60,194,237,2                 ; vcmpleps      %ymm13,%ymm8,%ymm13
+  DB  196,65,44,88,246                    ; vaddps        %ymm14,%ymm10,%ymm14
+  DB  196,67,13,74,243,208                ; vblendvps     %ymm13,%ymm11,%ymm14,%ymm14
+  DB  196,65,36,95,220                    ; vmaxps        %ymm12,%ymm11,%ymm11
+  DB  197,172,88,210                      ; vaddps        %ymm2,%ymm10,%ymm2
+  DB  196,195,109,74,212,208              ; vblendvps     %ymm13,%ymm12,%ymm2,%ymm2
+  DB  197,172,88,192                      ; vaddps        %ymm0,%ymm10,%ymm0
+  DB  196,195,125,74,193,208              ; vblendvps     %ymm13,%ymm9,%ymm0,%ymm0
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,52,95,203                    ; vmaxps        %ymm11,%ymm9,%ymm9
+  DB  196,65,124,92,218                   ; vsubps        %ymm10,%ymm0,%ymm11
+  DB  196,65,28,92,234                    ; vsubps        %ymm10,%ymm12,%ymm13
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,28,194,249,1                 ; vcmpltps      %ymm9,%ymm12,%ymm15
+  DB  196,65,52,92,202                    ; vsubps        %ymm10,%ymm9,%ymm9
+  DB  196,65,36,94,217                    ; vdivps        %ymm9,%ymm11,%ymm11
+  DB  196,65,44,88,219                    ; vaddps        %ymm11,%ymm10,%ymm11
+  DB  196,195,125,74,195,240              ; vblendvps     %ymm15,%ymm11,%ymm0,%ymm0
+  DB  196,65,12,92,218                    ; vsubps        %ymm10,%ymm14,%ymm11
+  DB  196,65,20,89,219                    ; vmulps        %ymm11,%ymm13,%ymm11
+  DB  196,65,36,94,217                    ; vdivps        %ymm9,%ymm11,%ymm11
+  DB  196,65,44,88,219                    ; vaddps        %ymm11,%ymm10,%ymm11
+  DB  196,67,13,74,219,240                ; vblendvps     %ymm15,%ymm11,%ymm14,%ymm11
+  DB  196,65,108,92,242                   ; vsubps        %ymm10,%ymm2,%ymm14
+  DB  196,65,20,89,238                    ; vmulps        %ymm14,%ymm13,%ymm13
+  DB  196,65,20,94,201                    ; vdivps        %ymm9,%ymm13,%ymm9
+  DB  196,65,44,88,201                    ; vaddps        %ymm9,%ymm10,%ymm9
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,65,36,95,208                    ; vmaxps        %ymm8,%ymm11,%ymm10
+  DB  196,195,109,74,209,240              ; vblendvps     %ymm15,%ymm9,%ymm2,%ymm2
+  DB  196,193,108,95,208                  ; vmaxps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,24,5,84,85,0,0           ; vbroadcastss  0x5554(%rip),%ymm8        # 66b4 <_sk_callback_avx+0x1b0>
+  DB  197,60,92,207                       ; vsubps        %ymm7,%ymm8,%ymm9
+  DB  197,180,89,201                      ; vmulps        %ymm1,%ymm9,%ymm1
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  197,60,89,220                       ; vmulps        %ymm4,%ymm8,%ymm11
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  197,244,88,192                      ; vaddps        %ymm0,%ymm1,%ymm0
+  DB  197,180,89,12,36                    ; vmulps        (%rsp),%ymm9,%ymm1
+  DB  197,60,89,221                       ; vmulps        %ymm5,%ymm8,%ymm11
+  DB  197,164,88,201                      ; vaddps        %ymm1,%ymm11,%ymm1
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  197,52,89,76,36,32                  ; vmulps        0x20(%rsp),%ymm9,%ymm9
+  DB  197,60,89,198                       ; vmulps        %ymm6,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,188,88,210                      ; vaddps        %ymm2,%ymm8,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_color_avx
+_sk_color_avx LABEL PROC
+  DB  72,131,236,120                      ; sub           $0x78,%rsp
+  DB  197,252,17,20,36                    ; vmovups       %ymm2,(%rsp)
+  DB  197,252,17,76,36,64                 ; vmovups       %ymm1,0x40(%rsp)
+  DB  197,252,17,68,36,32                 ; vmovups       %ymm0,0x20(%rsp)
+  DB  197,124,89,199                      ; vmulps        %ymm7,%ymm0,%ymm8
+  DB  197,116,89,207                      ; vmulps        %ymm7,%ymm1,%ymm9
+  DB  196,98,125,24,45,228,84,0,0         ; vbroadcastss  0x54e4(%rip),%ymm13        # 66b8 <_sk_callback_avx+0x1b4>
+  DB  196,65,92,89,213                    ; vmulps        %ymm13,%ymm4,%ymm10
+  DB  196,98,125,24,53,218,84,0,0         ; vbroadcastss  0x54da(%rip),%ymm14        # 66bc <_sk_callback_avx+0x1b8>
+  DB  196,65,84,89,222                    ; vmulps        %ymm14,%ymm5,%ymm11
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  196,98,125,24,61,203,84,0,0         ; vbroadcastss  0x54cb(%rip),%ymm15        # 66c0 <_sk_callback_avx+0x1bc>
+  DB  196,65,76,89,223                    ; vmulps        %ymm15,%ymm6,%ymm11
+  DB  196,193,44,88,195                   ; vaddps        %ymm11,%ymm10,%ymm0
+  DB  196,65,60,89,221                    ; vmulps        %ymm13,%ymm8,%ymm11
+  DB  196,65,52,89,230                    ; vmulps        %ymm14,%ymm9,%ymm12
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  197,108,89,231                      ; vmulps        %ymm7,%ymm2,%ymm12
+  DB  196,65,28,89,215                    ; vmulps        %ymm15,%ymm12,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  196,193,124,92,194                  ; vsubps        %ymm10,%ymm0,%ymm0
+  DB  197,60,88,192                       ; vaddps        %ymm0,%ymm8,%ymm8
+  DB  197,52,88,208                       ; vaddps        %ymm0,%ymm9,%ymm10
+  DB  197,28,88,216                       ; vaddps        %ymm0,%ymm12,%ymm11
+  DB  196,193,44,93,195                   ; vminps        %ymm11,%ymm10,%ymm0
+  DB  197,60,93,224                       ; vminps        %ymm0,%ymm8,%ymm12
+  DB  196,193,60,89,197                   ; vmulps        %ymm13,%ymm8,%ymm0
+  DB  196,65,44,89,206                    ; vmulps        %ymm14,%ymm10,%ymm9
+  DB  196,193,124,88,193                  ; vaddps        %ymm9,%ymm0,%ymm0
+  DB  196,65,36,89,207                    ; vmulps        %ymm15,%ymm11,%ymm9
+  DB  197,52,88,200                       ; vaddps        %ymm0,%ymm9,%ymm9
+  DB  196,193,60,92,193                   ; vsubps        %ymm9,%ymm8,%ymm0
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  196,65,52,92,236                    ; vsubps        %ymm12,%ymm9,%ymm13
+  DB  196,193,124,94,197                  ; vdivps        %ymm13,%ymm0,%ymm0
+  DB  196,65,44,92,241                    ; vsubps        %ymm9,%ymm10,%ymm14
+  DB  196,65,52,89,246                    ; vmulps        %ymm14,%ymm9,%ymm14
+  DB  196,65,12,94,245                    ; vdivps        %ymm13,%ymm14,%ymm14
+  DB  196,65,36,92,249                    ; vsubps        %ymm9,%ymm11,%ymm15
+  DB  196,65,52,89,255                    ; vmulps        %ymm15,%ymm9,%ymm15
+  DB  196,65,4,94,237                     ; vdivps        %ymm13,%ymm15,%ymm13
+  DB  196,65,4,87,255                     ; vxorps        %ymm15,%ymm15,%ymm15
+  DB  196,65,4,194,228,2                  ; vcmpleps      %ymm12,%ymm15,%ymm12
+  DB  196,65,52,88,246                    ; vaddps        %ymm14,%ymm9,%ymm14
+  DB  196,67,13,74,242,192                ; vblendvps     %ymm12,%ymm10,%ymm14,%ymm14
+  DB  196,65,44,95,211                    ; vmaxps        %ymm11,%ymm10,%ymm10
+  DB  196,65,52,88,237                    ; vaddps        %ymm13,%ymm9,%ymm13
+  DB  196,67,21,74,219,192                ; vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,200,192              ; vblendvps     %ymm12,%ymm8,%ymm0,%ymm1
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,60,95,194                    ; vmaxps        %ymm10,%ymm8,%ymm8
+  DB  196,65,116,92,209                   ; vsubps        %ymm9,%ymm1,%ymm10
+  DB  196,65,28,92,233                    ; vsubps        %ymm9,%ymm12,%ymm13
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,193,28,194,192,1                ; vcmpltps      %ymm8,%ymm12,%ymm0
+  DB  196,65,60,92,193                    ; vsubps        %ymm9,%ymm8,%ymm8
+  DB  196,65,44,94,208                    ; vdivps        %ymm8,%ymm10,%ymm10
+  DB  196,65,52,88,210                    ; vaddps        %ymm10,%ymm9,%ymm10
+  DB  196,195,117,74,202,0                ; vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  DB  196,65,12,92,209                    ; vsubps        %ymm9,%ymm14,%ymm10
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,65,44,94,208                    ; vdivps        %ymm8,%ymm10,%ymm10
+  DB  196,65,52,88,210                    ; vaddps        %ymm10,%ymm9,%ymm10
+  DB  196,67,13,74,210,0                  ; vblendvps     %ymm0,%ymm10,%ymm14,%ymm10
+  DB  196,65,36,92,241                    ; vsubps        %ymm9,%ymm11,%ymm14
+  DB  196,65,20,89,238                    ; vmulps        %ymm14,%ymm13,%ymm13
+  DB  196,65,20,94,192                    ; vdivps        %ymm8,%ymm13,%ymm8
+  DB  196,65,52,88,192                    ; vaddps        %ymm8,%ymm9,%ymm8
+  DB  196,193,116,95,207                  ; vmaxps        %ymm15,%ymm1,%ymm1
+  DB  196,65,44,95,207                    ; vmaxps        %ymm15,%ymm10,%ymm9
+  DB  196,195,37,74,192,0                 ; vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  DB  196,65,124,95,199                   ; vmaxps        %ymm15,%ymm0,%ymm8
+  DB  196,226,125,24,5,146,83,0,0         ; vbroadcastss  0x5392(%rip),%ymm0        # 66c4 <_sk_callback_avx+0x1c0>
+  DB  197,124,92,215                      ; vsubps        %ymm7,%ymm0,%ymm10
+  DB  197,172,89,84,36,32                 ; vmulps        0x20(%rsp),%ymm10,%ymm2
+  DB  197,124,92,219                      ; vsubps        %ymm3,%ymm0,%ymm11
+  DB  197,164,89,196                      ; vmulps        %ymm4,%ymm11,%ymm0
+  DB  197,252,88,194                      ; vaddps        %ymm2,%ymm0,%ymm0
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,172,89,76,36,64                 ; vmulps        0x40(%rsp),%ymm10,%ymm1
+  DB  197,164,89,213                      ; vmulps        %ymm5,%ymm11,%ymm2
+  DB  197,236,88,201                      ; vaddps        %ymm1,%ymm2,%ymm1
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  197,172,89,20,36                    ; vmulps        (%rsp),%ymm10,%ymm2
+  DB  197,36,89,206                       ; vmulps        %ymm6,%ymm11,%ymm9
+  DB  197,180,88,210                      ; vaddps        %ymm2,%ymm9,%ymm2
+  DB  196,193,108,88,208                  ; vaddps        %ymm8,%ymm2,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,120                      ; add           $0x78,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminosity_avx
+_sk_luminosity_avx LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  197,124,40,226                      ; vmovaps       %ymm2,%ymm12
+  DB  197,124,17,36,36                    ; vmovups       %ymm12,(%rsp)
+  DB  197,252,17,76,36,32                 ; vmovups       %ymm1,0x20(%rsp)
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,100,89,196                      ; vmulps        %ymm4,%ymm3,%ymm8
+  DB  197,100,89,205                      ; vmulps        %ymm5,%ymm3,%ymm9
+  DB  196,98,125,24,45,30,83,0,0          ; vbroadcastss  0x531e(%rip),%ymm13        # 66c8 <_sk_callback_avx+0x1c4>
+  DB  196,65,108,89,213                   ; vmulps        %ymm13,%ymm2,%ymm10
+  DB  196,98,125,24,53,20,83,0,0          ; vbroadcastss  0x5314(%rip),%ymm14        # 66cc <_sk_callback_avx+0x1c8>
+  DB  196,65,116,89,222                   ; vmulps        %ymm14,%ymm1,%ymm11
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  196,98,125,24,61,5,83,0,0           ; vbroadcastss  0x5305(%rip),%ymm15        # 66d0 <_sk_callback_avx+0x1cc>
+  DB  196,65,28,89,223                    ; vmulps        %ymm15,%ymm12,%ymm11
+  DB  196,193,44,88,195                   ; vaddps        %ymm11,%ymm10,%ymm0
+  DB  196,65,60,89,221                    ; vmulps        %ymm13,%ymm8,%ymm11
+  DB  196,65,52,89,230                    ; vmulps        %ymm14,%ymm9,%ymm12
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  197,100,89,230                      ; vmulps        %ymm6,%ymm3,%ymm12
+  DB  196,65,28,89,215                    ; vmulps        %ymm15,%ymm12,%ymm10
+  DB  196,65,36,88,210                    ; vaddps        %ymm10,%ymm11,%ymm10
+  DB  197,252,89,199                      ; vmulps        %ymm7,%ymm0,%ymm0
+  DB  196,193,124,92,194                  ; vsubps        %ymm10,%ymm0,%ymm0
+  DB  197,60,88,192                       ; vaddps        %ymm0,%ymm8,%ymm8
+  DB  197,52,88,208                       ; vaddps        %ymm0,%ymm9,%ymm10
+  DB  197,28,88,216                       ; vaddps        %ymm0,%ymm12,%ymm11
+  DB  196,193,44,93,195                   ; vminps        %ymm11,%ymm10,%ymm0
+  DB  197,60,93,224                       ; vminps        %ymm0,%ymm8,%ymm12
+  DB  196,193,60,89,197                   ; vmulps        %ymm13,%ymm8,%ymm0
+  DB  196,65,44,89,206                    ; vmulps        %ymm14,%ymm10,%ymm9
+  DB  196,193,124,88,193                  ; vaddps        %ymm9,%ymm0,%ymm0
+  DB  196,65,36,89,207                    ; vmulps        %ymm15,%ymm11,%ymm9
+  DB  197,52,88,200                       ; vaddps        %ymm0,%ymm9,%ymm9
+  DB  196,193,60,92,193                   ; vsubps        %ymm9,%ymm8,%ymm0
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  196,65,52,92,236                    ; vsubps        %ymm12,%ymm9,%ymm13
+  DB  196,193,124,94,197                  ; vdivps        %ymm13,%ymm0,%ymm0
+  DB  196,65,44,92,241                    ; vsubps        %ymm9,%ymm10,%ymm14
+  DB  196,65,52,89,246                    ; vmulps        %ymm14,%ymm9,%ymm14
+  DB  196,65,12,94,245                    ; vdivps        %ymm13,%ymm14,%ymm14
+  DB  196,65,36,92,249                    ; vsubps        %ymm9,%ymm11,%ymm15
+  DB  196,65,52,89,255                    ; vmulps        %ymm15,%ymm9,%ymm15
+  DB  196,65,4,94,237                     ; vdivps        %ymm13,%ymm15,%ymm13
+  DB  196,65,4,87,255                     ; vxorps        %ymm15,%ymm15,%ymm15
+  DB  196,65,4,194,228,2                  ; vcmpleps      %ymm12,%ymm15,%ymm12
+  DB  196,65,52,88,246                    ; vaddps        %ymm14,%ymm9,%ymm14
+  DB  196,67,13,74,242,192                ; vblendvps     %ymm12,%ymm10,%ymm14,%ymm14
+  DB  196,65,44,95,211                    ; vmaxps        %ymm11,%ymm10,%ymm10
+  DB  196,65,52,88,237                    ; vaddps        %ymm13,%ymm9,%ymm13
+  DB  196,67,21,74,219,192                ; vblendvps     %ymm12,%ymm11,%ymm13,%ymm11
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  196,195,125,74,200,192              ; vblendvps     %ymm12,%ymm8,%ymm0,%ymm1
+  DB  197,100,89,231                      ; vmulps        %ymm7,%ymm3,%ymm12
+  DB  196,65,60,95,194                    ; vmaxps        %ymm10,%ymm8,%ymm8
+  DB  196,65,116,92,209                   ; vsubps        %ymm9,%ymm1,%ymm10
+  DB  196,65,28,92,233                    ; vsubps        %ymm9,%ymm12,%ymm13
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,193,28,194,192,1                ; vcmpltps      %ymm8,%ymm12,%ymm0
+  DB  196,65,60,92,193                    ; vsubps        %ymm9,%ymm8,%ymm8
+  DB  196,65,44,94,208                    ; vdivps        %ymm8,%ymm10,%ymm10
+  DB  196,65,52,88,210                    ; vaddps        %ymm10,%ymm9,%ymm10
+  DB  196,195,117,74,202,0                ; vblendvps     %ymm0,%ymm10,%ymm1,%ymm1
+  DB  196,65,12,92,209                    ; vsubps        %ymm9,%ymm14,%ymm10
+  DB  196,65,20,89,210                    ; vmulps        %ymm10,%ymm13,%ymm10
+  DB  196,65,44,94,208                    ; vdivps        %ymm8,%ymm10,%ymm10
+  DB  196,65,52,88,210                    ; vaddps        %ymm10,%ymm9,%ymm10
+  DB  196,67,13,74,210,0                  ; vblendvps     %ymm0,%ymm10,%ymm14,%ymm10
+  DB  196,65,36,92,241                    ; vsubps        %ymm9,%ymm11,%ymm14
+  DB  196,65,20,89,238                    ; vmulps        %ymm14,%ymm13,%ymm13
+  DB  196,65,20,94,192                    ; vdivps        %ymm8,%ymm13,%ymm8
+  DB  196,65,52,88,192                    ; vaddps        %ymm8,%ymm9,%ymm8
+  DB  196,193,116,95,207                  ; vmaxps        %ymm15,%ymm1,%ymm1
+  DB  196,65,44,95,207                    ; vmaxps        %ymm15,%ymm10,%ymm9
+  DB  196,195,37,74,192,0                 ; vblendvps     %ymm0,%ymm8,%ymm11,%ymm0
+  DB  196,65,124,95,199                   ; vmaxps        %ymm15,%ymm0,%ymm8
+  DB  196,226,125,24,5,204,81,0,0         ; vbroadcastss  0x51cc(%rip),%ymm0        # 66d4 <_sk_callback_avx+0x1d0>
+  DB  197,124,92,215                      ; vsubps        %ymm7,%ymm0,%ymm10
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  197,124,92,219                      ; vsubps        %ymm3,%ymm0,%ymm11
+  DB  197,164,89,196                      ; vmulps        %ymm4,%ymm11,%ymm0
+  DB  197,252,88,194                      ; vaddps        %ymm2,%ymm0,%ymm0
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,172,89,76,36,32                 ; vmulps        0x20(%rsp),%ymm10,%ymm1
+  DB  197,164,89,213                      ; vmulps        %ymm5,%ymm11,%ymm2
+  DB  197,236,88,201                      ; vaddps        %ymm1,%ymm2,%ymm1
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  197,172,89,20,36                    ; vmulps        (%rsp),%ymm10,%ymm2
+  DB  197,36,89,206                       ; vmulps        %ymm6,%ymm11,%ymm9
+  DB  197,180,88,210                      ; vaddps        %ymm2,%ymm9,%ymm2
+  DB  196,193,108,88,208                  ; vaddps        %ymm8,%ymm2,%ymm2
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,92,220                  ; vsubps        %ymm12,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_rgba_8888_avx
+_sk_srcover_rgba_8888_avx LABEL PROC
+  DB  72,131,236,16                       ; sub           $0x10,%rsp
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,47,1,0,0                     ; jne           16a2 <_sk_srcover_rgba_8888_avx+0x14c>
+  DB  196,193,124,16,58                   ; vmovups       (%r10),%ymm7
+  DB  197,124,40,13,128,85,0,0            ; vmovaps       0x5580(%rip),%ymm9        # 6b00 <_sk_callback_avx+0x5fc>
+  DB  196,193,68,84,225                   ; vandps        %ymm9,%ymm7,%ymm4
+  DB  197,252,91,228                      ; vcvtdq2ps     %ymm4,%ymm4
+  DB  197,209,114,215,8                   ; vpsrld        $0x8,%xmm7,%xmm5
+  DB  196,195,125,25,248,1                ; vextractf128  $0x1,%ymm7,%xmm8
+  DB  196,193,73,114,208,8                ; vpsrld        $0x8,%xmm8,%xmm6
+  DB  196,227,85,24,238,1                 ; vinsertf128   $0x1,%xmm6,%ymm5,%ymm5
+  DB  196,193,84,84,233                   ; vandps        %ymm9,%ymm5,%ymm5
+  DB  197,252,91,237                      ; vcvtdq2ps     %ymm5,%ymm5
+  DB  197,169,114,215,16                  ; vpsrld        $0x10,%xmm7,%xmm10
+  DB  196,193,73,114,208,16               ; vpsrld        $0x10,%xmm8,%xmm6
+  DB  196,227,45,24,246,1                 ; vinsertf128   $0x1,%xmm6,%ymm10,%ymm6
+  DB  196,193,76,84,241                   ; vandps        %ymm9,%ymm6,%ymm6
+  DB  197,252,91,246                      ; vcvtdq2ps     %ymm6,%ymm6
+  DB  197,177,114,215,24                  ; vpsrld        $0x18,%xmm7,%xmm9
+  DB  196,193,65,114,208,24               ; vpsrld        $0x18,%xmm8,%xmm7
+  DB  196,227,53,24,255,1                 ; vinsertf128   $0x1,%xmm7,%ymm9,%ymm7
+  DB  197,252,91,255                      ; vcvtdq2ps     %ymm7,%ymm7
+  DB  196,98,125,24,5,247,80,0,0          ; vbroadcastss  0x50f7(%rip),%ymm8        # 66d8 <_sk_callback_avx+0x1d4>
+  DB  197,60,92,195                       ; vsubps        %ymm3,%ymm8,%ymm8
+  DB  196,98,125,24,13,238,80,0,0         ; vbroadcastss  0x50ee(%rip),%ymm9        # 66dc <_sk_callback_avx+0x1d8>
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  197,60,89,212                       ; vmulps        %ymm4,%ymm8,%ymm10
+  DB  196,193,124,88,194                  ; vaddps        %ymm10,%ymm0,%ymm0
+  DB  196,193,116,89,201                  ; vmulps        %ymm9,%ymm1,%ymm1
+  DB  197,60,89,213                       ; vmulps        %ymm5,%ymm8,%ymm10
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  196,193,108,89,209                  ; vmulps        %ymm9,%ymm2,%ymm2
+  DB  197,60,89,214                       ; vmulps        %ymm6,%ymm8,%ymm10
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  196,193,100,89,217                  ; vmulps        %ymm9,%ymm3,%ymm3
+  DB  197,60,89,199                       ; vmulps        %ymm7,%ymm8,%ymm8
+  DB  196,193,100,88,216                  ; vaddps        %ymm8,%ymm3,%ymm3
+  DB  197,125,91,192                      ; vcvtps2dq     %ymm0,%ymm8
+  DB  197,125,91,201                      ; vcvtps2dq     %ymm1,%ymm9
+  DB  196,193,41,114,241,8                ; vpslld        $0x8,%xmm9,%xmm10
+  DB  196,67,125,25,201,1                 ; vextractf128  $0x1,%ymm9,%xmm9
+  DB  196,193,49,114,241,8                ; vpslld        $0x8,%xmm9,%xmm9
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,65,53,86,192                    ; vorpd         %ymm8,%ymm9,%ymm8
+  DB  197,125,91,202                      ; vcvtps2dq     %ymm2,%ymm9
+  DB  196,193,41,114,241,16               ; vpslld        $0x10,%xmm9,%xmm10
+  DB  196,67,125,25,201,1                 ; vextractf128  $0x1,%ymm9,%xmm9
+  DB  196,193,49,114,241,16               ; vpslld        $0x10,%xmm9,%xmm9
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  197,125,91,211                      ; vcvtps2dq     %ymm3,%ymm10
+  DB  196,193,33,114,242,24               ; vpslld        $0x18,%xmm10,%xmm11
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,242,24               ; vpslld        $0x18,%xmm10,%xmm10
+  DB  196,67,37,24,210,1                  ; vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  DB  196,65,53,86,202                    ; vorpd         %ymm10,%ymm9,%ymm9
+  DB  196,65,61,86,193                    ; vorpd         %ymm9,%ymm8,%ymm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,91                              ; jne           16ed <_sk_srcover_rgba_8888_avx+0x197>
+  DB  196,65,124,17,2                     ; vmovups       %ymm8,(%r10)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  72,131,196,16                       ; add           $0x10,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,225,249,110,224                 ; vmovq         %rax,%xmm4
+  DB  196,226,121,48,228                  ; vpmovzxbw     %xmm4,%xmm4
+  DB  196,226,89,0,45,150,83,0,0          ; vpshufb       0x5396(%rip),%xmm4,%xmm5        # 6a60 <_sk_callback_avx+0x55c>
+  DB  196,226,121,33,237                  ; vpmovsxbd     %xmm5,%xmm5
+  DB  196,226,89,0,37,152,83,0,0          ; vpshufb       0x5398(%rip),%xmm4,%xmm4        # 6a70 <_sk_callback_avx+0x56c>
+  DB  196,226,121,33,228                  ; vpmovsxbd     %xmm4,%xmm4
+  DB  196,227,85,24,228,1                 ; vinsertf128   $0x1,%xmm4,%ymm5,%ymm4
+  DB  196,194,93,44,58                    ; vmaskmovps    (%r10),%ymm4,%ymm7
+  DB  233,139,254,255,255                 ; jmpq          1578 <_sk_srcover_rgba_8888_avx+0x22>
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,97,249,110,200                  ; vmovq         %rax,%xmm9
+  DB  196,66,121,48,201                   ; vpmovzxbw     %xmm9,%xmm9
+  DB  196,98,49,0,21,75,83,0,0            ; vpshufb       0x534b(%rip),%xmm9,%xmm10        # 6a60 <_sk_callback_avx+0x55c>
+  DB  196,66,121,33,210                   ; vpmovsxbd     %xmm10,%xmm10
+  DB  196,98,49,0,13,77,83,0,0            ; vpshufb       0x534d(%rip),%xmm9,%xmm9        # 6a70 <_sk_callback_avx+0x56c>
+  DB  196,66,121,33,201                   ; vpmovsxbd     %xmm9,%xmm9
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,66,53,46,2                      ; vmaskmovps    %ymm8,%ymm9,(%r10)
+  DB  233,95,255,255,255                  ; jmpq          1697 <_sk_srcover_rgba_8888_avx+0x141>
+
+PUBLIC _sk_clamp_0_avx
+_sk_clamp_0_avx LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,193,116,95,200                  ; vmaxps        %ymm8,%ymm1,%ymm1
+  DB  196,193,108,95,208                  ; vmaxps        %ymm8,%ymm2,%ymm2
+  DB  196,193,100,95,216                  ; vmaxps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_1_avx
+_sk_clamp_1_avx LABEL PROC
+  DB  196,98,125,24,5,130,79,0,0          ; vbroadcastss  0x4f82(%rip),%ymm8        # 66e0 <_sk_callback_avx+0x1dc>
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  196,193,116,93,200                  ; vminps        %ymm8,%ymm1,%ymm1
+  DB  196,193,108,93,208                  ; vminps        %ymm8,%ymm2,%ymm2
+  DB  196,193,100,93,216                  ; vminps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_a_avx
+_sk_clamp_a_avx LABEL PROC
+  DB  196,98,125,24,5,101,79,0,0          ; vbroadcastss  0x4f65(%rip),%ymm8        # 66e4 <_sk_callback_avx+0x1e0>
+  DB  196,193,100,93,216                  ; vminps        %ymm8,%ymm3,%ymm3
+  DB  197,252,93,195                      ; vminps        %ymm3,%ymm0,%ymm0
+  DB  197,244,93,203                      ; vminps        %ymm3,%ymm1,%ymm1
+  DB  197,236,93,211                      ; vminps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_set_rgb_avx
+_sk_set_rgb_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,0                    ; vbroadcastss  (%rax),%ymm0
+  DB  196,226,125,24,72,4                 ; vbroadcastss  0x4(%rax),%ymm1
+  DB  196,226,125,24,80,8                 ; vbroadcastss  0x8(%rax),%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_rb_avx
+_sk_swap_rb_avx LABEL PROC
+  DB  197,124,40,192                      ; vmovaps       %ymm0,%ymm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,194                      ; vmovaps       %ymm2,%ymm0
+  DB  197,124,41,194                      ; vmovaps       %ymm8,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_avx
+_sk_swap_avx LABEL PROC
+  DB  197,124,40,195                      ; vmovaps       %ymm3,%ymm8
+  DB  197,124,40,202                      ; vmovaps       %ymm2,%ymm9
+  DB  197,124,40,209                      ; vmovaps       %ymm1,%ymm10
+  DB  197,124,40,216                      ; vmovaps       %ymm0,%ymm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,196                      ; vmovaps       %ymm4,%ymm0
+  DB  197,252,40,205                      ; vmovaps       %ymm5,%ymm1
+  DB  197,252,40,214                      ; vmovaps       %ymm6,%ymm2
+  DB  197,252,40,223                      ; vmovaps       %ymm7,%ymm3
+  DB  197,124,41,220                      ; vmovaps       %ymm11,%ymm4
+  DB  197,124,41,213                      ; vmovaps       %ymm10,%ymm5
+  DB  197,124,41,206                      ; vmovaps       %ymm9,%ymm6
+  DB  197,124,41,199                      ; vmovaps       %ymm8,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_src_dst_avx
+_sk_move_src_dst_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,224                      ; vmovaps       %ymm0,%ymm4
+  DB  197,252,40,233                      ; vmovaps       %ymm1,%ymm5
+  DB  197,252,40,242                      ; vmovaps       %ymm2,%ymm6
+  DB  197,252,40,251                      ; vmovaps       %ymm3,%ymm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_dst_src_avx
+_sk_move_dst_src_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,196                      ; vmovaps       %ymm4,%ymm0
+  DB  197,252,40,205                      ; vmovaps       %ymm5,%ymm1
+  DB  197,252,40,214                      ; vmovaps       %ymm6,%ymm2
+  DB  197,252,40,223                      ; vmovaps       %ymm7,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_premul_avx
+_sk_premul_avx LABEL PROC
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  197,236,89,211                      ; vmulps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_unpremul_avx
+_sk_unpremul_avx LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,65,100,194,200,0                ; vcmpeqps      %ymm8,%ymm3,%ymm9
+  DB  196,98,125,24,21,173,78,0,0         ; vbroadcastss  0x4ead(%rip),%ymm10        # 66e8 <_sk_callback_avx+0x1e4>
+  DB  197,44,94,211                       ; vdivps        %ymm3,%ymm10,%ymm10
+  DB  196,67,45,74,192,144                ; vblendvps     %ymm9,%ymm8,%ymm10,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_from_srgb_avx
+_sk_from_srgb_avx LABEL PROC
+  DB  196,98,125,24,5,142,78,0,0          ; vbroadcastss  0x4e8e(%rip),%ymm8        # 66ec <_sk_callback_avx+0x1e8>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  197,124,89,208                      ; vmulps        %ymm0,%ymm0,%ymm10
+  DB  196,98,125,24,29,128,78,0,0         ; vbroadcastss  0x4e80(%rip),%ymm11        # 66f0 <_sk_callback_avx+0x1ec>
+  DB  196,65,124,89,227                   ; vmulps        %ymm11,%ymm0,%ymm12
+  DB  196,98,125,24,45,118,78,0,0         ; vbroadcastss  0x4e76(%rip),%ymm13        # 66f4 <_sk_callback_avx+0x1f0>
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  196,65,44,89,212                    ; vmulps        %ymm12,%ymm10,%ymm10
+  DB  196,98,125,24,37,103,78,0,0         ; vbroadcastss  0x4e67(%rip),%ymm12        # 66f8 <_sk_callback_avx+0x1f4>
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,98,125,24,53,93,78,0,0          ; vbroadcastss  0x4e5d(%rip),%ymm14        # 66fc <_sk_callback_avx+0x1f8>
+  DB  196,193,124,194,198,1               ; vcmpltps      %ymm14,%ymm0,%ymm0
+  DB  196,195,45,74,193,0                 ; vblendvps     %ymm0,%ymm9,%ymm10,%ymm0
+  DB  196,65,116,89,200                   ; vmulps        %ymm8,%ymm1,%ymm9
+  DB  197,116,89,209                      ; vmulps        %ymm1,%ymm1,%ymm10
+  DB  196,65,116,89,251                   ; vmulps        %ymm11,%ymm1,%ymm15
+  DB  196,65,4,88,253                     ; vaddps        %ymm13,%ymm15,%ymm15
+  DB  196,65,44,89,215                    ; vmulps        %ymm15,%ymm10,%ymm10
+  DB  196,65,44,88,212                    ; vaddps        %ymm12,%ymm10,%ymm10
+  DB  196,193,116,194,206,1               ; vcmpltps      %ymm14,%ymm1,%ymm1
+  DB  196,195,45,74,201,16                ; vblendvps     %ymm1,%ymm9,%ymm10,%ymm1
+  DB  196,65,108,89,192                   ; vmulps        %ymm8,%ymm2,%ymm8
+  DB  197,108,89,202                      ; vmulps        %ymm2,%ymm2,%ymm9
+  DB  196,65,108,89,211                   ; vmulps        %ymm11,%ymm2,%ymm10
+  DB  196,65,44,88,213                    ; vaddps        %ymm13,%ymm10,%ymm10
+  DB  196,65,52,89,202                    ; vmulps        %ymm10,%ymm9,%ymm9
+  DB  196,65,52,88,204                    ; vaddps        %ymm12,%ymm9,%ymm9
+  DB  196,193,108,194,214,1               ; vcmpltps      %ymm14,%ymm2,%ymm2
+  DB  196,195,53,74,208,32                ; vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_to_srgb_avx
+_sk_to_srgb_avx LABEL PROC
+  DB  197,124,82,200                      ; vrsqrtps      %ymm0,%ymm9
+  DB  196,98,125,24,5,242,77,0,0          ; vbroadcastss  0x4df2(%rip),%ymm8        # 6700 <_sk_callback_avx+0x1fc>
+  DB  196,65,124,89,208                   ; vmulps        %ymm8,%ymm0,%ymm10
+  DB  196,98,125,24,29,232,77,0,0         ; vbroadcastss  0x4de8(%rip),%ymm11        # 6704 <_sk_callback_avx+0x200>
+  DB  196,65,52,89,227                    ; vmulps        %ymm11,%ymm9,%ymm12
+  DB  196,98,125,24,45,222,77,0,0         ; vbroadcastss  0x4dde(%rip),%ymm13        # 6708 <_sk_callback_avx+0x204>
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  196,65,52,89,228                    ; vmulps        %ymm12,%ymm9,%ymm12
+  DB  196,98,125,24,53,207,77,0,0         ; vbroadcastss  0x4dcf(%rip),%ymm14        # 670c <_sk_callback_avx+0x208>
+  DB  196,65,28,88,230                    ; vaddps        %ymm14,%ymm12,%ymm12
+  DB  196,98,125,24,61,197,77,0,0         ; vbroadcastss  0x4dc5(%rip),%ymm15        # 6710 <_sk_callback_avx+0x20c>
+  DB  196,65,52,88,207                    ; vaddps        %ymm15,%ymm9,%ymm9
+  DB  196,65,124,83,201                   ; vrcpps        %ymm9,%ymm9
+  DB  196,65,52,89,204                    ; vmulps        %ymm12,%ymm9,%ymm9
+  DB  196,98,125,24,37,177,77,0,0         ; vbroadcastss  0x4db1(%rip),%ymm12        # 6714 <_sk_callback_avx+0x210>
+  DB  196,193,124,194,196,1               ; vcmpltps      %ymm12,%ymm0,%ymm0
+  DB  196,195,53,74,194,0                 ; vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  DB  197,124,82,201                      ; vrsqrtps      %ymm1,%ymm9
+  DB  196,65,52,89,211                    ; vmulps        %ymm11,%ymm9,%ymm10
+  DB  196,65,44,88,213                    ; vaddps        %ymm13,%ymm10,%ymm10
+  DB  196,65,52,89,210                    ; vmulps        %ymm10,%ymm9,%ymm10
+  DB  196,65,44,88,214                    ; vaddps        %ymm14,%ymm10,%ymm10
+  DB  196,65,52,88,207                    ; vaddps        %ymm15,%ymm9,%ymm9
+  DB  196,65,124,83,201                   ; vrcpps        %ymm9,%ymm9
+  DB  196,65,52,89,202                    ; vmulps        %ymm10,%ymm9,%ymm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,193,116,194,204,1               ; vcmpltps      %ymm12,%ymm1,%ymm1
+  DB  196,195,53,74,202,16                ; vblendvps     %ymm1,%ymm10,%ymm9,%ymm1
+  DB  197,124,82,202                      ; vrsqrtps      %ymm2,%ymm9
+  DB  196,65,52,89,211                    ; vmulps        %ymm11,%ymm9,%ymm10
+  DB  196,65,44,88,213                    ; vaddps        %ymm13,%ymm10,%ymm10
+  DB  196,65,52,89,210                    ; vmulps        %ymm10,%ymm9,%ymm10
+  DB  196,65,44,88,214                    ; vaddps        %ymm14,%ymm10,%ymm10
+  DB  196,65,52,88,207                    ; vaddps        %ymm15,%ymm9,%ymm9
+  DB  196,65,124,83,201                   ; vrcpps        %ymm9,%ymm9
+  DB  196,65,52,89,202                    ; vmulps        %ymm10,%ymm9,%ymm9
+  DB  196,65,108,89,192                   ; vmulps        %ymm8,%ymm2,%ymm8
+  DB  196,193,108,194,212,1               ; vcmpltps      %ymm12,%ymm2,%ymm2
+  DB  196,195,53,74,208,32                ; vblendvps     %ymm2,%ymm8,%ymm9,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_rgb_to_hsl_avx
+_sk_rgb_to_hsl_avx LABEL PROC
+  DB  197,124,95,193                      ; vmaxps        %ymm1,%ymm0,%ymm8
+  DB  197,60,95,194                       ; vmaxps        %ymm2,%ymm8,%ymm8
+  DB  197,124,93,201                      ; vminps        %ymm1,%ymm0,%ymm9
+  DB  197,52,93,202                       ; vminps        %ymm2,%ymm9,%ymm9
+  DB  196,65,60,92,209                    ; vsubps        %ymm9,%ymm8,%ymm10
+  DB  196,98,125,24,29,23,77,0,0          ; vbroadcastss  0x4d17(%rip),%ymm11        # 6718 <_sk_callback_avx+0x214>
+  DB  196,65,36,94,218                    ; vdivps        %ymm10,%ymm11,%ymm11
+  DB  197,116,92,226                      ; vsubps        %ymm2,%ymm1,%ymm12
+  DB  196,65,28,89,227                    ; vmulps        %ymm11,%ymm12,%ymm12
+  DB  197,116,194,234,1                   ; vcmpltps      %ymm2,%ymm1,%ymm13
+  DB  197,60,194,241,0                    ; vcmpeqps      %ymm1,%ymm8,%ymm14
+  DB  197,236,92,208                      ; vsubps        %ymm0,%ymm2,%ymm2
+  DB  196,193,108,89,211                  ; vmulps        %ymm11,%ymm2,%ymm2
+  DB  197,252,92,201                      ; vsubps        %ymm1,%ymm0,%ymm1
+  DB  196,193,116,89,203                  ; vmulps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,29,240,76,0,0         ; vbroadcastss  0x4cf0(%rip),%ymm11        # 6724 <_sk_callback_avx+0x220>
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,29,222,76,0,0         ; vbroadcastss  0x4cde(%rip),%ymm11        # 6720 <_sk_callback_avx+0x21c>
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  196,227,117,74,202,224              ; vblendvps     %ymm14,%ymm2,%ymm1,%ymm1
+  DB  196,226,125,24,21,198,76,0,0        ; vbroadcastss  0x4cc6(%rip),%ymm2        # 671c <_sk_callback_avx+0x218>
+  DB  196,65,12,87,246                    ; vxorps        %ymm14,%ymm14,%ymm14
+  DB  196,227,13,74,210,208               ; vblendvps     %ymm13,%ymm2,%ymm14,%ymm2
+  DB  197,188,194,192,0                   ; vcmpeqps      %ymm0,%ymm8,%ymm0
+  DB  196,193,108,88,212                  ; vaddps        %ymm12,%ymm2,%ymm2
+  DB  196,227,117,74,194,0                ; vblendvps     %ymm0,%ymm2,%ymm1,%ymm0
+  DB  196,193,60,88,201                   ; vaddps        %ymm9,%ymm8,%ymm1
+  DB  196,98,125,24,37,173,76,0,0         ; vbroadcastss  0x4cad(%rip),%ymm12        # 672c <_sk_callback_avx+0x228>
+  DB  196,193,116,89,212                  ; vmulps        %ymm12,%ymm1,%ymm2
+  DB  197,28,194,226,1                    ; vcmpltps      %ymm2,%ymm12,%ymm12
+  DB  196,65,36,92,216                    ; vsubps        %ymm8,%ymm11,%ymm11
+  DB  196,65,36,92,217                    ; vsubps        %ymm9,%ymm11,%ymm11
+  DB  196,195,117,74,203,192              ; vblendvps     %ymm12,%ymm11,%ymm1,%ymm1
+  DB  196,65,60,194,193,0                 ; vcmpeqps      %ymm9,%ymm8,%ymm8
+  DB  197,172,94,201                      ; vdivps        %ymm1,%ymm10,%ymm1
+  DB  196,195,125,74,198,128              ; vblendvps     %ymm8,%ymm14,%ymm0,%ymm0
+  DB  196,195,117,74,206,128              ; vblendvps     %ymm8,%ymm14,%ymm1,%ymm1
+  DB  196,98,125,24,5,112,76,0,0          ; vbroadcastss  0x4c70(%rip),%ymm8        # 6728 <_sk_callback_avx+0x224>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hsl_to_rgb_avx
+_sk_hsl_to_rgb_avx LABEL PROC
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  197,252,17,188,36,128,0,0,0         ; vmovups       %ymm7,0x80(%rsp)
+  DB  197,252,17,116,36,96                ; vmovups       %ymm6,0x60(%rsp)
+  DB  197,252,17,108,36,64                ; vmovups       %ymm5,0x40(%rsp)
+  DB  197,252,17,100,36,32                ; vmovups       %ymm4,0x20(%rsp)
+  DB  197,252,17,28,36                    ; vmovups       %ymm3,(%rsp)
+  DB  197,252,40,225                      ; vmovaps       %ymm1,%ymm4
+  DB  197,252,40,216                      ; vmovaps       %ymm0,%ymm3
+  DB  196,98,125,24,5,55,76,0,0           ; vbroadcastss  0x4c37(%rip),%ymm8        # 6730 <_sk_callback_avx+0x22c>
+  DB  197,60,194,202,2                    ; vcmpleps      %ymm2,%ymm8,%ymm9
+  DB  197,92,89,210                       ; vmulps        %ymm2,%ymm4,%ymm10
+  DB  196,65,92,92,218                    ; vsubps        %ymm10,%ymm4,%ymm11
+  DB  196,67,45,74,203,144                ; vblendvps     %ymm9,%ymm11,%ymm10,%ymm9
+  DB  197,52,88,210                       ; vaddps        %ymm2,%ymm9,%ymm10
+  DB  197,108,88,202                      ; vaddps        %ymm2,%ymm2,%ymm9
+  DB  196,65,52,92,202                    ; vsubps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,29,17,76,0,0          ; vbroadcastss  0x4c11(%rip),%ymm11        # 6734 <_sk_callback_avx+0x230>
+  DB  196,65,100,88,219                   ; vaddps        %ymm11,%ymm3,%ymm11
+  DB  196,67,125,8,227,1                  ; vroundps      $0x1,%ymm11,%ymm12
+  DB  196,65,36,92,252                    ; vsubps        %ymm12,%ymm11,%ymm15
+  DB  196,65,44,92,217                    ; vsubps        %ymm9,%ymm10,%ymm11
+  DB  196,98,125,24,37,251,75,0,0         ; vbroadcastss  0x4bfb(%rip),%ymm12        # 673c <_sk_callback_avx+0x238>
+  DB  196,193,4,89,196                    ; vmulps        %ymm12,%ymm15,%ymm0
+  DB  196,98,125,24,45,241,75,0,0         ; vbroadcastss  0x4bf1(%rip),%ymm13        # 6740 <_sk_callback_avx+0x23c>
+  DB  197,20,92,240                       ; vsubps        %ymm0,%ymm13,%ymm14
+  DB  196,65,36,89,246                    ; vmulps        %ymm14,%ymm11,%ymm14
+  DB  196,65,52,88,246                    ; vaddps        %ymm14,%ymm9,%ymm14
+  DB  196,226,125,24,13,210,75,0,0        ; vbroadcastss  0x4bd2(%rip),%ymm1        # 6738 <_sk_callback_avx+0x234>
+  DB  196,193,116,194,255,2               ; vcmpleps      %ymm15,%ymm1,%ymm7
+  DB  196,195,13,74,249,112               ; vblendvps     %ymm7,%ymm9,%ymm14,%ymm7
+  DB  196,65,60,194,247,2                 ; vcmpleps      %ymm15,%ymm8,%ymm14
+  DB  196,227,45,74,255,224               ; vblendvps     %ymm14,%ymm7,%ymm10,%ymm7
+  DB  196,98,125,24,53,189,75,0,0         ; vbroadcastss  0x4bbd(%rip),%ymm14        # 6744 <_sk_callback_avx+0x240>
+  DB  196,65,12,194,255,2                 ; vcmpleps      %ymm15,%ymm14,%ymm15
+  DB  196,193,124,89,195                  ; vmulps        %ymm11,%ymm0,%ymm0
+  DB  197,180,88,192                      ; vaddps        %ymm0,%ymm9,%ymm0
+  DB  196,99,125,74,255,240               ; vblendvps     %ymm15,%ymm7,%ymm0,%ymm15
+  DB  196,227,125,8,195,1                 ; vroundps      $0x1,%ymm3,%ymm0
+  DB  197,228,92,192                      ; vsubps        %ymm0,%ymm3,%ymm0
+  DB  196,193,124,89,252                  ; vmulps        %ymm12,%ymm0,%ymm7
+  DB  197,148,92,247                      ; vsubps        %ymm7,%ymm13,%ymm6
+  DB  197,164,89,246                      ; vmulps        %ymm6,%ymm11,%ymm6
+  DB  197,180,88,246                      ; vaddps        %ymm6,%ymm9,%ymm6
+  DB  197,244,194,232,2                   ; vcmpleps      %ymm0,%ymm1,%ymm5
+  DB  196,195,77,74,233,80                ; vblendvps     %ymm5,%ymm9,%ymm6,%ymm5
+  DB  197,188,194,240,2                   ; vcmpleps      %ymm0,%ymm8,%ymm6
+  DB  196,227,45,74,237,96                ; vblendvps     %ymm6,%ymm5,%ymm10,%ymm5
+  DB  197,140,194,192,2                   ; vcmpleps      %ymm0,%ymm14,%ymm0
+  DB  197,164,89,247                      ; vmulps        %ymm7,%ymm11,%ymm6
+  DB  197,180,88,246                      ; vaddps        %ymm6,%ymm9,%ymm6
+  DB  196,227,77,74,237,0                 ; vblendvps     %ymm0,%ymm5,%ymm6,%ymm5
+  DB  196,226,125,24,5,95,75,0,0          ; vbroadcastss  0x4b5f(%rip),%ymm0        # 6748 <_sk_callback_avx+0x244>
+  DB  197,228,88,192                      ; vaddps        %ymm0,%ymm3,%ymm0
+  DB  196,227,125,8,216,1                 ; vroundps      $0x1,%ymm0,%ymm3
+  DB  197,252,92,195                      ; vsubps        %ymm3,%ymm0,%ymm0
+  DB  197,244,194,200,2                   ; vcmpleps      %ymm0,%ymm1,%ymm1
+  DB  196,193,124,89,220                  ; vmulps        %ymm12,%ymm0,%ymm3
+  DB  197,148,92,243                      ; vsubps        %ymm3,%ymm13,%ymm6
+  DB  197,164,89,246                      ; vmulps        %ymm6,%ymm11,%ymm6
+  DB  197,180,88,246                      ; vaddps        %ymm6,%ymm9,%ymm6
+  DB  196,195,77,74,201,16                ; vblendvps     %ymm1,%ymm9,%ymm6,%ymm1
+  DB  197,188,194,240,2                   ; vcmpleps      %ymm0,%ymm8,%ymm6
+  DB  196,227,45,74,201,96                ; vblendvps     %ymm6,%ymm1,%ymm10,%ymm1
+  DB  197,140,194,192,2                   ; vcmpleps      %ymm0,%ymm14,%ymm0
+  DB  197,164,89,219                      ; vmulps        %ymm3,%ymm11,%ymm3
+  DB  197,180,88,219                      ; vaddps        %ymm3,%ymm9,%ymm3
+  DB  196,227,101,74,217,0                ; vblendvps     %ymm0,%ymm1,%ymm3,%ymm3
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,220,194,224,0                   ; vcmpeqps      %ymm0,%ymm4,%ymm4
+  DB  196,227,5,74,194,64                 ; vblendvps     %ymm4,%ymm2,%ymm15,%ymm0
+  DB  196,227,85,74,202,64                ; vblendvps     %ymm4,%ymm2,%ymm5,%ymm1
+  DB  196,227,101,74,210,64               ; vblendvps     %ymm4,%ymm2,%ymm3,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,28,36                    ; vmovups       (%rsp),%ymm3
+  DB  197,252,16,100,36,32                ; vmovups       0x20(%rsp),%ymm4
+  DB  197,252,16,108,36,64                ; vmovups       0x40(%rsp),%ymm5
+  DB  197,252,16,116,36,96                ; vmovups       0x60(%rsp),%ymm6
+  DB  197,252,16,188,36,128,0,0,0         ; vmovups       0x80(%rsp),%ymm7
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_1_float_avx
+_sk_scale_1_float_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_u8_avx
+_sk_scale_u8_avx LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,68                              ; jne           1ce6 <_sk_scale_u8_avx+0x54>
+  DB  197,122,126,0                       ; vmovq         (%rax),%xmm8
+  DB  196,66,121,49,200                   ; vpmovzxbd     %xmm8,%xmm9
+  DB  196,67,121,4,192,229                ; vpermilps     $0xe5,%xmm8,%xmm8
+  DB  196,66,121,49,192                   ; vpmovzxbd     %xmm8,%xmm8
+  DB  196,67,53,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,13,130,74,0,0         ; vbroadcastss  0x4a82(%rip),%ymm9        # 674c <_sk_callback_avx+0x248>
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           1cef <_sk_scale_u8_avx+0x5d>
+  DB  196,65,249,110,194                  ; vmovq         %r10,%xmm8
+  DB  91                                  ; pop           %rbx
+  DB  235,154                             ; jmp           1ca6 <_sk_scale_u8_avx+0x14>
+
+PUBLIC _sk_lerp_1_float_avx
+_sk_lerp_1_float_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  197,252,88,196                      ; vaddps        %ymm4,%ymm0,%ymm0
+  DB  197,244,92,205                      ; vsubps        %ymm5,%ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  197,244,88,205                      ; vaddps        %ymm5,%ymm1,%ymm1
+  DB  197,236,92,214                      ; vsubps        %ymm6,%ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,236,88,214                      ; vaddps        %ymm6,%ymm2,%ymm2
+  DB  197,228,92,223                      ; vsubps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lerp_u8_avx
+_sk_lerp_u8_avx LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,104                             ; jne           1dc3 <_sk_lerp_u8_avx+0x78>
+  DB  197,122,126,0                       ; vmovq         (%rax),%xmm8
+  DB  196,66,121,49,200                   ; vpmovzxbd     %xmm8,%xmm9
+  DB  196,67,121,4,192,229                ; vpermilps     $0xe5,%xmm8,%xmm8
+  DB  196,66,121,49,192                   ; vpmovzxbd     %xmm8,%xmm8
+  DB  196,67,53,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm9,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,13,205,73,0,0         ; vbroadcastss  0x49cd(%rip),%ymm9        # 6750 <_sk_callback_avx+0x24c>
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  197,252,88,196                      ; vaddps        %ymm4,%ymm0,%ymm0
+  DB  197,244,92,205                      ; vsubps        %ymm5,%ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  197,244,88,205                      ; vaddps        %ymm5,%ymm1,%ymm1
+  DB  197,236,92,214                      ; vsubps        %ymm6,%ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,236,88,214                      ; vaddps        %ymm6,%ymm2,%ymm2
+  DB  197,228,92,223                      ; vsubps        %ymm7,%ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           1dcc <_sk_lerp_u8_avx+0x81>
+  DB  196,65,249,110,194                  ; vmovq         %r10,%xmm8
+  DB  91                                  ; pop           %rbx
+  DB  233,115,255,255,255                 ; jmpq          1d5f <_sk_lerp_u8_avx+0x14>
+
+PUBLIC _sk_lerp_565_avx
+_sk_lerp_565_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,208,0,0,0                    ; jne           1eca <_sk_lerp_565_avx+0xde>
+  DB  196,65,122,111,4,83                 ; vmovdqu       (%r11,%rdx,2),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  196,65,57,105,201                   ; vpunpckhwd    %xmm9,%xmm8,%xmm9
+  DB  196,66,121,51,192                   ; vpmovzxwd     %xmm8,%xmm8
+  DB  196,67,61,24,193,1                  ; vinsertf128   $0x1,%xmm9,%ymm8,%ymm8
+  DB  196,98,125,24,13,54,73,0,0          ; vbroadcastss  0x4936(%rip),%ymm9        # 6754 <_sk_callback_avx+0x250>
+  DB  196,65,60,84,201                    ; vandps        %ymm9,%ymm8,%ymm9
+  DB  196,65,124,91,201                   ; vcvtdq2ps     %ymm9,%ymm9
+  DB  196,98,125,24,21,39,73,0,0          ; vbroadcastss  0x4927(%rip),%ymm10        # 6758 <_sk_callback_avx+0x254>
+  DB  196,65,52,89,202                    ; vmulps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,21,29,73,0,0          ; vbroadcastss  0x491d(%rip),%ymm10        # 675c <_sk_callback_avx+0x258>
+  DB  196,65,60,84,210                    ; vandps        %ymm10,%ymm8,%ymm10
+  DB  196,65,124,91,210                   ; vcvtdq2ps     %ymm10,%ymm10
+  DB  196,98,125,24,29,14,73,0,0          ; vbroadcastss  0x490e(%rip),%ymm11        # 6760 <_sk_callback_avx+0x25c>
+  DB  196,65,44,89,211                    ; vmulps        %ymm11,%ymm10,%ymm10
+  DB  196,98,125,24,29,4,73,0,0           ; vbroadcastss  0x4904(%rip),%ymm11        # 6764 <_sk_callback_avx+0x260>
+  DB  196,65,60,84,195                    ; vandps        %ymm11,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  196,98,125,24,29,245,72,0,0         ; vbroadcastss  0x48f5(%rip),%ymm11        # 6768 <_sk_callback_avx+0x264>
+  DB  196,65,60,89,195                    ; vmulps        %ymm11,%ymm8,%ymm8
+  DB  197,252,92,196                      ; vsubps        %ymm4,%ymm0,%ymm0
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  197,252,88,196                      ; vaddps        %ymm4,%ymm0,%ymm0
+  DB  197,244,92,205                      ; vsubps        %ymm5,%ymm1,%ymm1
+  DB  196,193,116,89,202                  ; vmulps        %ymm10,%ymm1,%ymm1
+  DB  197,244,88,205                      ; vaddps        %ymm5,%ymm1,%ymm1
+  DB  197,236,92,214                      ; vsubps        %ymm6,%ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,236,88,214                      ; vaddps        %ymm6,%ymm2,%ymm2
+  DB  197,228,92,223                      ; vsubps        %ymm7,%ymm3,%ymm3
+  DB  196,65,100,89,201                   ; vmulps        %ymm9,%ymm3,%ymm9
+  DB  197,52,88,207                       ; vaddps        %ymm7,%ymm9,%ymm9
+  DB  196,65,100,89,210                   ; vmulps        %ymm10,%ymm3,%ymm10
+  DB  197,44,88,215                       ; vaddps        %ymm7,%ymm10,%ymm10
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  197,228,88,223                      ; vaddps        %ymm7,%ymm3,%ymm3
+  DB  197,172,95,219                      ; vmaxps        %ymm3,%ymm10,%ymm3
+  DB  197,180,95,219                      ; vmaxps        %ymm3,%ymm9,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  196,65,57,239,192                   ; vpxor         %xmm8,%xmm8,%xmm8
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,29,255,255,255               ; ja            1e00 <_sk_lerp_565_avx+0x14>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,74,0,0,0                  ; lea           0x4a(%rip),%r10        # 1f38 <_sk_lerp_565_avx+0x14c>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,57,239,192                   ; vpxor         %xmm8,%xmm8,%xmm8
+  DB  196,65,57,196,68,83,12,6            ; vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,10,5            ; vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,8,4             ; vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,6,3             ; vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,4,2             ; vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,68,83,2,1             ; vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm8,%xmm8
+  DB  196,65,57,196,4,83,0                ; vpinsrw       $0x0,(%r11,%rdx,2),%xmm8,%xmm8
+  DB  233,200,254,255,255                 ; jmpq          1e00 <_sk_lerp_565_avx+0x14>
+  DB  244                                 ; hlt
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  236                                 ; in            (%dx),%al
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,228                             ; jmpq          *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  220,255                             ; fdivr         %st,%st(7)
+  DB  255                                 ; (bad)
+  DB  255,212                             ; callq         *%rsp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,204                             ; dec           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  191                                 ; .byte         0xbf
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_tables_avx
+_sk_load_tables_avx LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,131,236,48                       ; sub           $0x30,%rsp
+  DB  197,252,17,60,36                    ; vmovups       %ymm7,(%rsp)
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,251,1,0,0                    ; jne           2172 <_sk_load_tables_avx+0x21e>
+  DB  196,65,124,16,18                    ; vmovups       (%r10),%ymm10
+  DB  197,124,40,13,156,75,0,0            ; vmovaps       0x4b9c(%rip),%ymm9        # 6b20 <_sk_callback_avx+0x61c>
+  DB  196,193,44,84,201                   ; vandps        %ymm9,%ymm10,%ymm1
+  DB  196,227,125,25,200,1                ; vextractf128  $0x1,%ymm1,%xmm0
+  DB  196,193,249,126,195                 ; vmovq         %xmm0,%r11
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  196,161,122,16,20,147               ; vmovss        (%rbx,%r10,4),%xmm2
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,163,105,33,4,155,16             ; vinsertps     $0x10,(%rbx,%r11,4),%xmm2,%xmm0
+  DB  68,137,209                          ; mov           %r10d,%ecx
+  DB  196,227,121,33,4,139,32             ; vinsertps     $0x20,(%rbx,%rcx,4),%xmm0,%xmm0
+  DB  196,193,249,126,203                 ; vmovq         %xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,35,121,33,44,147,48             ; vinsertps     $0x30,(%rbx,%r10,4),%xmm0,%xmm13
+  DB  68,137,217                          ; mov           %r11d,%ecx
+  DB  197,250,16,20,139                   ; vmovss        (%rbx,%rcx,4),%xmm2
+  DB  196,227,249,22,201,1                ; vpextrq       $0x1,%xmm1,%rcx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,163,105,33,12,155,16            ; vinsertps     $0x10,(%rbx,%r11,4),%xmm2,%xmm1
+  DB  65,137,202                          ; mov           %ecx,%r10d
+  DB  72,193,233,32                       ; shr           $0x20,%rcx
+  DB  196,163,113,33,12,147,32            ; vinsertps     $0x20,(%rbx,%r10,4),%xmm1,%xmm1
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  196,99,113,33,36,139,48             ; vinsertps     $0x30,(%rbx,%rcx,4),%xmm1,%xmm12
+  DB  196,193,105,114,210,8               ; vpsrld        $0x8,%xmm10,%xmm2
+  DB  196,67,125,25,208,1                 ; vextractf128  $0x1,%ymm10,%xmm8
+  DB  196,193,121,114,208,8               ; vpsrld        $0x8,%xmm8,%xmm0
+  DB  196,227,109,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm2,%ymm0
+  DB  196,193,124,84,209                  ; vandps        %ymm9,%ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  196,225,249,126,193                 ; vmovq         %xmm0,%rcx
+  DB  137,203                             ; mov           %ecx,%ebx
+  DB  196,193,122,16,12,154               ; vmovss        (%r10,%rbx,4),%xmm1
+  DB  196,227,249,22,195,1                ; vpextrq       $0x1,%xmm0,%rbx
+  DB  72,193,233,32                       ; shr           $0x20,%rcx
+  DB  196,67,113,33,52,138,16             ; vinsertps     $0x10,(%r10,%rcx,4),%xmm1,%xmm14
+  DB  137,217                             ; mov           %ebx,%ecx
+  DB  196,193,122,16,28,138               ; vmovss        (%r10,%rcx,4),%xmm3
+  DB  196,225,249,126,209                 ; vmovq         %xmm2,%rcx
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  196,193,122,16,12,154               ; vmovss        (%r10,%rbx,4),%xmm1
+  DB  137,203                             ; mov           %ecx,%ebx
+  DB  196,193,122,16,4,154                ; vmovss        (%r10,%rbx,4),%xmm0
+  DB  196,227,249,22,211,1                ; vpextrq       $0x1,%xmm2,%rbx
+  DB  72,193,233,32                       ; shr           $0x20,%rcx
+  DB  196,67,121,33,28,138,16             ; vinsertps     $0x10,(%r10,%rcx,4),%xmm0,%xmm11
+  DB  137,217                             ; mov           %ebx,%ecx
+  DB  196,65,122,16,60,138                ; vmovss        (%r10,%rcx,4),%xmm15
+  DB  196,195,29,24,197,1                 ; vinsertf128   $0x1,%xmm13,%ymm12,%ymm0
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  196,227,9,33,219,32                 ; vinsertps     $0x20,%xmm3,%xmm14,%xmm3
+  DB  196,227,97,33,249,48                ; vinsertps     $0x30,%xmm1,%xmm3,%xmm7
+  DB  196,65,122,16,52,154                ; vmovss        (%r10,%rbx,4),%xmm14
+  DB  72,139,64,24                        ; mov           0x18(%rax),%rax
+  DB  196,193,97,114,210,16               ; vpsrld        $0x10,%xmm10,%xmm3
+  DB  196,193,105,114,208,16              ; vpsrld        $0x10,%xmm8,%xmm2
+  DB  196,227,101,24,210,1                ; vinsertf128   $0x1,%xmm2,%ymm3,%ymm2
+  DB  196,65,108,84,201                   ; vandps        %ymm9,%ymm2,%ymm9
+  DB  196,99,125,25,202,1                 ; vextractf128  $0x1,%ymm9,%xmm2
+  DB  196,225,249,126,209                 ; vmovq         %xmm2,%rcx
+  DB  137,203                             ; mov           %ecx,%ebx
+  DB  197,250,16,28,152                   ; vmovss        (%rax,%rbx,4),%xmm3
+  DB  196,227,249,22,211,1                ; vpextrq       $0x1,%xmm2,%rbx
+  DB  72,193,233,32                       ; shr           $0x20,%rcx
+  DB  196,99,97,33,36,136,16              ; vinsertps     $0x10,(%rax,%rcx,4),%xmm3,%xmm12
+  DB  137,217                             ; mov           %ebx,%ecx
+  DB  197,250,16,28,136                   ; vmovss        (%rax,%rcx,4),%xmm3
+  DB  196,97,249,126,201                  ; vmovq         %xmm9,%rcx
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  197,250,16,20,152                   ; vmovss        (%rax,%rbx,4),%xmm2
+  DB  137,203                             ; mov           %ecx,%ebx
+  DB  197,250,16,12,152                   ; vmovss        (%rax,%rbx,4),%xmm1
+  DB  196,99,249,22,203,1                 ; vpextrq       $0x1,%xmm9,%rbx
+  DB  72,193,233,32                       ; shr           $0x20,%rcx
+  DB  196,99,113,33,12,136,16             ; vinsertps     $0x10,(%rax,%rcx,4),%xmm1,%xmm9
+  DB  137,217                             ; mov           %ebx,%ecx
+  DB  197,122,16,44,136                   ; vmovss        (%rax,%rcx,4),%xmm13
+  DB  196,195,33,33,207,32                ; vinsertps     $0x20,%xmm15,%xmm11,%xmm1
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  197,122,16,28,152                   ; vmovss        (%rax,%rbx,4),%xmm11
+  DB  196,195,113,33,206,48               ; vinsertps     $0x30,%xmm14,%xmm1,%xmm1
+  DB  196,227,117,24,207,1                ; vinsertf128   $0x1,%xmm7,%ymm1,%ymm1
+  DB  196,227,25,33,219,32                ; vinsertps     $0x20,%xmm3,%xmm12,%xmm3
+  DB  196,227,97,33,210,48                ; vinsertps     $0x30,%xmm2,%xmm3,%xmm2
+  DB  196,195,49,33,221,32                ; vinsertps     $0x20,%xmm13,%xmm9,%xmm3
+  DB  196,195,97,33,219,48                ; vinsertps     $0x30,%xmm11,%xmm3,%xmm3
+  DB  196,227,101,24,210,1                ; vinsertf128   $0x1,%xmm2,%ymm3,%ymm2
+  DB  196,193,97,114,210,24               ; vpsrld        $0x18,%xmm10,%xmm3
+  DB  196,193,65,114,208,24               ; vpsrld        $0x18,%xmm8,%xmm7
+  DB  196,227,101,24,223,1                ; vinsertf128   $0x1,%xmm7,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,226,125,24,61,15,70,0,0         ; vbroadcastss  0x460f(%rip),%ymm7        # 676c <_sk_callback_avx+0x268>
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  197,252,16,60,36                    ; vmovups       (%rsp),%ymm7
+  DB  72,131,196,48                       ; add           $0x30,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  73,199,195,255,255,255,255          ; mov           $0xffffffffffffffff,%r11
+  DB  73,211,235                          ; shr           %cl,%r11
+  DB  196,193,249,110,195                 ; vmovq         %r11,%xmm0
+  DB  196,226,121,48,192                  ; vpmovzxbw     %xmm0,%xmm0
+  DB  196,226,121,0,13,230,72,0,0         ; vpshufb       0x48e6(%rip),%xmm0,%xmm1        # 6a80 <_sk_callback_avx+0x57c>
+  DB  196,226,121,33,201                  ; vpmovsxbd     %xmm1,%xmm1
+  DB  196,226,121,0,5,232,72,0,0          ; vpshufb       0x48e8(%rip),%xmm0,%xmm0        # 6a90 <_sk_callback_avx+0x58c>
+  DB  196,226,121,33,192                  ; vpmovsxbd     %xmm0,%xmm0
+  DB  196,227,117,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  DB  196,66,125,44,18                    ; vmaskmovps    (%r10),%ymm0,%ymm10
+  DB  233,191,253,255,255                 ; jmpq          1f7c <_sk_load_tables_avx+0x28>
+
+PUBLIC _sk_load_tables_u16_be_avx
+_sk_load_tables_u16_be_avx LABEL PROC
+  DB  72,131,236,56                       ; sub           $0x38,%rsp
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  197,252,17,60,36                    ; vmovups       %ymm7,(%rsp)
+  DB  15,133,87,2,0,0                     ; jne           2433 <_sk_load_tables_u16_be_avx+0x276>
+  DB  196,1,121,16,4,81                   ; vmovupd       (%r9,%r10,2),%xmm8
+  DB  196,129,121,16,84,81,16             ; vmovupd       0x10(%r9,%r10,2),%xmm2
+  DB  196,129,121,16,92,81,32             ; vmovupd       0x20(%r9,%r10,2),%xmm3
+  DB  196,1,122,111,76,81,48              ; vmovdqu       0x30(%r9,%r10,2),%xmm9
+  DB  197,185,97,194                      ; vpunpcklwd    %xmm2,%xmm8,%xmm0
+  DB  197,185,105,210                     ; vpunpckhwd    %xmm2,%xmm8,%xmm2
+  DB  196,193,97,97,201                   ; vpunpcklwd    %xmm9,%xmm3,%xmm1
+  DB  196,193,97,105,217                  ; vpunpckhwd    %xmm9,%xmm3,%xmm3
+  DB  197,121,97,202                      ; vpunpcklwd    %xmm2,%xmm0,%xmm9
+  DB  197,121,105,194                     ; vpunpckhwd    %xmm2,%xmm0,%xmm8
+  DB  197,241,97,195                      ; vpunpcklwd    %xmm3,%xmm1,%xmm0
+  DB  197,113,105,219                     ; vpunpckhwd    %xmm3,%xmm1,%xmm11
+  DB  197,177,108,200                     ; vpunpcklqdq   %xmm0,%xmm9,%xmm1
+  DB  197,49,109,224                      ; vpunpckhqdq   %xmm0,%xmm9,%xmm12
+  DB  197,121,111,21,119,72,0,0           ; vmovdqa       0x4877(%rip),%xmm10        # 6aa0 <_sk_callback_avx+0x59c>
+  DB  196,193,113,219,202                 ; vpand         %xmm10,%xmm1,%xmm1
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  196,193,113,105,209                 ; vpunpckhwd    %xmm9,%xmm1,%xmm2
+  DB  196,193,249,126,209                 ; vmovq         %xmm2,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  76,139,88,8                         ; mov           0x8(%rax),%r11
+  DB  196,129,122,16,28,147               ; vmovss        (%r11,%r10,4),%xmm3
+  DB  196,195,249,22,210,1                ; vpextrq       $0x1,%xmm2,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,97,33,44,139,16               ; vinsertps     $0x10,(%r11,%r9,4),%xmm3,%xmm13
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,226,121,51,201                  ; vpmovzxwd     %xmm1,%xmm1
+  DB  196,129,122,16,28,139               ; vmovss        (%r11,%r9,4),%xmm3
+  DB  196,193,249,126,201                 ; vmovq         %xmm1,%r9
+  DB  196,129,122,16,4,147                ; vmovss        (%r11,%r10,4),%xmm0
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  196,129,122,16,20,147               ; vmovss        (%r11,%r10,4),%xmm2
+  DB  196,195,249,22,202,1                ; vpextrq       $0x1,%xmm1,%r10
+  DB  196,131,105,33,12,11,16             ; vinsertps     $0x10,(%r11,%r9,1),%xmm2,%xmm1
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  196,129,122,16,20,139               ; vmovss        (%r11,%r9,4),%xmm2
+  DB  76,139,72,16                        ; mov           0x10(%rax),%r9
+  DB  196,227,17,33,219,32                ; vinsertps     $0x20,%xmm3,%xmm13,%xmm3
+  DB  196,99,97,33,232,48                 ; vinsertps     $0x30,%xmm0,%xmm3,%xmm13
+  DB  196,99,113,33,242,32                ; vinsertps     $0x20,%xmm2,%xmm1,%xmm14
+  DB  196,1,122,16,60,19                  ; vmovss        (%r11,%r10,1),%xmm15
+  DB  196,193,25,219,210                  ; vpand         %xmm10,%xmm12,%xmm2
+  DB  196,193,105,105,193                 ; vpunpckhwd    %xmm9,%xmm2,%xmm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,129,122,16,12,153               ; vmovss        (%r9,%r11,4),%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,113,33,36,145,16              ; vinsertps     $0x10,(%r9,%r10,4),%xmm1,%xmm12
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,226,121,51,194                  ; vpmovzxwd     %xmm2,%xmm0
+  DB  196,129,122,16,20,145               ; vmovss        (%r9,%r10,4),%xmm2
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  196,129,122,16,28,153               ; vmovss        (%r9,%r11,4),%xmm3
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  196,129,122,16,12,153               ; vmovss        (%r9,%r11,4),%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  196,131,113,33,12,17,16             ; vinsertps     $0x10,(%r9,%r10,1),%xmm1,%xmm1
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  196,129,122,16,60,145               ; vmovss        (%r9,%r10,4),%xmm7
+  DB  196,195,9,33,199,48                 ; vinsertps     $0x30,%xmm15,%xmm14,%xmm0
+  DB  196,65,57,108,243                   ; vpunpcklqdq   %xmm11,%xmm8,%xmm14
+  DB  196,195,125,24,197,1                ; vinsertf128   $0x1,%xmm13,%ymm0,%ymm0
+  DB  73,193,235,30                       ; shr           $0x1e,%r11
+  DB  196,227,25,33,210,32                ; vinsertps     $0x20,%xmm2,%xmm12,%xmm2
+  DB  196,227,105,33,219,48               ; vinsertps     $0x30,%xmm3,%xmm2,%xmm3
+  DB  196,99,113,33,239,32                ; vinsertps     $0x20,%xmm7,%xmm1,%xmm13
+  DB  196,1,122,16,60,25                  ; vmovss        (%r9,%r11,1),%xmm15
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  196,193,9,219,250                   ; vpand         %xmm10,%xmm14,%xmm7
+  DB  196,193,65,105,209                  ; vpunpckhwd    %xmm9,%xmm7,%xmm2
+  DB  196,193,249,126,209                 ; vmovq         %xmm2,%r9
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,193,122,16,12,130               ; vmovss        (%r10,%rax,4),%xmm1
+  DB  196,227,249,22,208,1                ; vpextrq       $0x1,%xmm2,%rax
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,113,33,20,138,16              ; vinsertps     $0x10,(%r10,%r9,4),%xmm1,%xmm10
+  DB  65,137,193                          ; mov           %eax,%r9d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,226,121,51,207                  ; vpmovzxwd     %xmm7,%xmm1
+  DB  196,1,122,16,52,138                 ; vmovss        (%r10,%r9,4),%xmm14
+  DB  196,193,249,126,201                 ; vmovq         %xmm1,%r9
+  DB  196,65,122,16,36,130                ; vmovss        (%r10,%rax,4),%xmm12
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  196,193,122,16,20,130               ; vmovss        (%r10,%rax,4),%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  196,131,105,33,20,10,16             ; vinsertps     $0x10,(%r10,%r9,1),%xmm2,%xmm2
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,193,122,16,60,130               ; vmovss        (%r10,%rax,4),%xmm7
+  DB  196,195,17,33,207,48                ; vinsertps     $0x30,%xmm15,%xmm13,%xmm1
+  DB  73,193,235,30                       ; shr           $0x1e,%r11
+  DB  196,1,122,16,44,26                  ; vmovss        (%r10,%r11,1),%xmm13
+  DB  196,227,117,24,203,1                ; vinsertf128   $0x1,%xmm3,%ymm1,%ymm1
+  DB  196,195,41,33,222,32                ; vinsertps     $0x20,%xmm14,%xmm10,%xmm3
+  DB  196,195,97,33,220,48                ; vinsertps     $0x30,%xmm12,%xmm3,%xmm3
+  DB  196,227,105,33,215,32               ; vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  DB  196,195,105,33,213,48               ; vinsertps     $0x30,%xmm13,%xmm2,%xmm2
+  DB  196,227,109,24,211,1                ; vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  DB  196,193,57,109,219                  ; vpunpckhqdq   %xmm11,%xmm8,%xmm3
+  DB  197,193,113,243,8                   ; vpsllw        $0x8,%xmm3,%xmm7
+  DB  197,225,113,211,8                   ; vpsrlw        $0x8,%xmm3,%xmm3
+  DB  197,193,235,219                     ; vpor          %xmm3,%xmm7,%xmm3
+  DB  196,193,97,105,249                  ; vpunpckhwd    %xmm9,%xmm3,%xmm7
+  DB  196,226,121,51,219                  ; vpmovzxwd     %xmm3,%xmm3
+  DB  196,227,101,24,223,1                ; vinsertf128   $0x1,%xmm7,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,226,125,24,61,78,67,0,0         ; vbroadcastss  0x434e(%rip),%ymm7        # 6770 <_sk_callback_avx+0x26c>
+  DB  197,228,89,223                      ; vmulps        %ymm7,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,60,36                    ; vmovups       (%rsp),%ymm7
+  DB  72,131,196,56                       ; add           $0x38,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,1,123,16,4,81                   ; vmovsd        (%r9,%r10,2),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,85                              ; je            2499 <_sk_load_tables_u16_be_avx+0x2dc>
+  DB  196,1,57,22,68,81,8                 ; vmovhpd       0x8(%r9,%r10,2),%xmm8,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,72                              ; jb            2499 <_sk_load_tables_u16_be_avx+0x2dc>
+  DB  196,129,123,16,84,81,16             ; vmovsd        0x10(%r9,%r10,2),%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  116,72                              ; je            24a6 <_sk_load_tables_u16_be_avx+0x2e9>
+  DB  196,129,105,22,84,81,24             ; vmovhpd       0x18(%r9,%r10,2),%xmm2,%xmm2
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,59                              ; jb            24a6 <_sk_load_tables_u16_be_avx+0x2e9>
+  DB  196,129,123,16,92,81,32             ; vmovsd        0x20(%r9,%r10,2),%xmm3
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  15,132,123,253,255,255              ; je            21f7 <_sk_load_tables_u16_be_avx+0x3a>
+  DB  196,129,97,22,92,81,40              ; vmovhpd       0x28(%r9,%r10,2),%xmm3,%xmm3
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  15,130,106,253,255,255              ; jb            21f7 <_sk_load_tables_u16_be_avx+0x3a>
+  DB  196,1,122,126,76,81,48              ; vmovq         0x30(%r9,%r10,2),%xmm9
+  DB  233,94,253,255,255                  ; jmpq          21f7 <_sk_load_tables_u16_be_avx+0x3a>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  197,233,87,210                      ; vxorpd        %xmm2,%xmm2,%xmm2
+  DB  233,81,253,255,255                  ; jmpq          21f7 <_sk_load_tables_u16_be_avx+0x3a>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  233,72,253,255,255                  ; jmpq          21f7 <_sk_load_tables_u16_be_avx+0x3a>
+
+PUBLIC _sk_load_tables_rgb_u16_be_avx
+_sk_load_tables_rgb_u16_be_avx LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,82                        ; lea           (%rdx,%rdx,2),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  197,252,17,124,36,32                ; vmovups       %ymm7,0x20(%rsp)
+  DB  197,252,17,52,36                    ; vmovups       %ymm6,(%rsp)
+  DB  15,133,74,2,0,0                     ; jne           271a <_sk_load_tables_rgb_u16_be_avx+0x26b>
+  DB  196,129,122,111,4,81                ; vmovdqu       (%r9,%r10,2),%xmm0
+  DB  196,129,122,111,84,81,12            ; vmovdqu       0xc(%r9,%r10,2),%xmm2
+  DB  196,129,122,111,76,81,24            ; vmovdqu       0x18(%r9,%r10,2),%xmm1
+  DB  196,129,122,111,92,81,32            ; vmovdqu       0x20(%r9,%r10,2),%xmm3
+  DB  197,225,115,219,4                   ; vpsrldq       $0x4,%xmm3,%xmm3
+  DB  197,185,115,216,6                   ; vpsrldq       $0x6,%xmm0,%xmm8
+  DB  197,177,115,218,6                   ; vpsrldq       $0x6,%xmm2,%xmm9
+  DB  197,161,115,217,6                   ; vpsrldq       $0x6,%xmm1,%xmm11
+  DB  197,169,115,219,6                   ; vpsrldq       $0x6,%xmm3,%xmm10
+  DB  197,249,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm0
+  DB  196,193,57,97,209                   ; vpunpcklwd    %xmm9,%xmm8,%xmm2
+  DB  197,241,97,203                      ; vpunpcklwd    %xmm3,%xmm1,%xmm1
+  DB  196,193,33,97,218                   ; vpunpcklwd    %xmm10,%xmm11,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,249,105,194                     ; vpunpckhwd    %xmm2,%xmm0,%xmm0
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,241,105,203                     ; vpunpckhwd    %xmm3,%xmm1,%xmm1
+  DB  197,185,108,218                     ; vpunpcklqdq   %xmm2,%xmm8,%xmm3
+  DB  197,57,109,218                      ; vpunpckhqdq   %xmm2,%xmm8,%xmm11
+  DB  197,121,108,193                     ; vpunpcklqdq   %xmm1,%xmm0,%xmm8
+  DB  197,121,111,13,118,69,0,0           ; vmovdqa       0x4576(%rip),%xmm9        # 6ab0 <_sk_callback_avx+0x5ac>
+  DB  196,193,97,219,193                  ; vpand         %xmm9,%xmm3,%xmm0
+  DB  196,65,41,239,210                   ; vpxor         %xmm10,%xmm10,%xmm10
+  DB  196,193,121,105,202                 ; vpunpckhwd    %xmm10,%xmm0,%xmm1
+  DB  196,193,249,126,201                 ; vmovq         %xmm1,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  76,139,88,8                         ; mov           0x8(%rax),%r11
+  DB  196,129,122,16,20,147               ; vmovss        (%r11,%r10,4),%xmm2
+  DB  196,195,249,22,202,1                ; vpextrq       $0x1,%xmm1,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,105,33,36,139,16              ; vinsertps     $0x10,(%r11,%r9,4),%xmm2,%xmm12
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,129,122,16,20,139               ; vmovss        (%r11,%r9,4),%xmm2
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  196,129,122,16,12,147               ; vmovss        (%r11,%r10,4),%xmm1
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  196,129,122,16,28,147               ; vmovss        (%r11,%r10,4),%xmm3
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  196,131,97,33,28,11,16              ; vinsertps     $0x10,(%r11,%r9,1),%xmm3,%xmm3
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  196,129,122,16,4,139                ; vmovss        (%r11,%r9,4),%xmm0
+  DB  76,139,72,16                        ; mov           0x10(%rax),%r9
+  DB  196,227,25,33,210,32                ; vinsertps     $0x20,%xmm2,%xmm12,%xmm2
+  DB  196,227,105,33,201,48               ; vinsertps     $0x30,%xmm1,%xmm2,%xmm1
+  DB  196,129,122,16,20,19                ; vmovss        (%r11,%r10,1),%xmm2
+  DB  196,65,33,219,225                   ; vpand         %xmm9,%xmm11,%xmm12
+  DB  196,65,25,105,218                   ; vpunpckhwd    %xmm10,%xmm12,%xmm11
+  DB  196,65,249,126,218                  ; vmovq         %xmm11,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,1,122,16,44,153                 ; vmovss        (%r9,%r11,4),%xmm13
+  DB  196,67,249,22,219,1                 ; vpextrq       $0x1,%xmm11,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,17,33,28,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm13,%xmm11
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,66,121,51,244                   ; vpmovzxwd     %xmm12,%xmm14
+  DB  196,1,122,16,44,145                 ; vmovss        (%r9,%r10,4),%xmm13
+  DB  196,65,249,126,242                  ; vmovq         %xmm14,%r10
+  DB  196,1,122,16,36,153                 ; vmovss        (%r9,%r11,4),%xmm12
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  196,1,122,16,60,153                 ; vmovss        (%r9,%r11,4),%xmm15
+  DB  196,67,249,22,243,1                 ; vpextrq       $0x1,%xmm14,%r11
+  DB  196,3,1,33,52,17,16                 ; vinsertps     $0x10,(%r9,%r10,1),%xmm15,%xmm14
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  196,1,122,16,60,145                 ; vmovss        (%r9,%r10,4),%xmm15
+  DB  196,227,97,33,192,32                ; vinsertps     $0x20,%xmm0,%xmm3,%xmm0
+  DB  196,227,121,33,194,48               ; vinsertps     $0x30,%xmm2,%xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  73,193,235,30                       ; shr           $0x1e,%r11
+  DB  196,129,122,16,52,25                ; vmovss        (%r9,%r11,1),%xmm6
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  196,65,57,219,193                   ; vpand         %xmm9,%xmm8,%xmm8
+  DB  196,193,57,105,210                  ; vpunpckhwd    %xmm10,%xmm8,%xmm2
+  DB  196,193,249,126,209                 ; vmovq         %xmm2,%r9
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,193,122,16,12,130               ; vmovss        (%r10,%rax,4),%xmm1
+  DB  196,227,249,22,208,1                ; vpextrq       $0x1,%xmm2,%rax
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,113,33,12,138,16              ; vinsertps     $0x10,(%r10,%r9,4),%xmm1,%xmm9
+  DB  65,137,193                          ; mov           %eax,%r9d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,194,121,51,200                  ; vpmovzxwd     %xmm8,%xmm1
+  DB  196,1,122,16,4,138                  ; vmovss        (%r10,%r9,4),%xmm8
+  DB  196,193,249,126,201                 ; vmovq         %xmm1,%r9
+  DB  196,65,122,16,20,130                ; vmovss        (%r10,%rax,4),%xmm10
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  196,193,122,16,20,130               ; vmovss        (%r10,%rax,4),%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  196,131,105,33,20,10,16             ; vinsertps     $0x10,(%r10,%r9,1),%xmm2,%xmm2
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,193,122,16,60,130               ; vmovss        (%r10,%rax,4),%xmm7
+  DB  196,195,33,33,205,32                ; vinsertps     $0x20,%xmm13,%xmm11,%xmm1
+  DB  73,193,235,30                       ; shr           $0x1e,%r11
+  DB  196,1,122,16,28,26                  ; vmovss        (%r10,%r11,1),%xmm11
+  DB  196,195,113,33,204,48               ; vinsertps     $0x30,%xmm12,%xmm1,%xmm1
+  DB  196,195,9,33,223,32                 ; vinsertps     $0x20,%xmm15,%xmm14,%xmm3
+  DB  196,227,97,33,222,48                ; vinsertps     $0x30,%xmm6,%xmm3,%xmm3
+  DB  196,227,101,24,201,1                ; vinsertf128   $0x1,%xmm1,%ymm3,%ymm1
+  DB  196,195,49,33,216,32                ; vinsertps     $0x20,%xmm8,%xmm9,%xmm3
+  DB  196,195,97,33,218,48                ; vinsertps     $0x30,%xmm10,%xmm3,%xmm3
+  DB  196,227,105,33,215,32               ; vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  DB  196,195,105,33,211,48               ; vinsertps     $0x30,%xmm11,%xmm2,%xmm2
+  DB  196,227,109,24,211,1                ; vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,107,64,0,0        ; vbroadcastss  0x406b(%rip),%ymm3        # 6774 <_sk_callback_avx+0x270>
+  DB  197,252,16,52,36                    ; vmovups       (%rsp),%ymm6
+  DB  197,252,16,124,36,32                ; vmovups       0x20(%rsp),%ymm7
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,129,121,110,4,81                ; vmovd         (%r9,%r10,2),%xmm0
+  DB  196,129,121,196,68,81,4,2           ; vpinsrw       $0x2,0x4(%r9,%r10,2),%xmm0,%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,5                               ; jne           2733 <_sk_load_tables_rgb_u16_be_avx+0x284>
+  DB  233,209,253,255,255                 ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+  DB  196,129,121,110,76,81,6             ; vmovd         0x6(%r9,%r10,2),%xmm1
+  DB  196,1,113,196,68,81,10,2            ; vpinsrw       $0x2,0xa(%r9,%r10,2),%xmm1,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,26                              ; jb            2762 <_sk_load_tables_rgb_u16_be_avx+0x2b3>
+  DB  196,129,121,110,76,81,12            ; vmovd         0xc(%r9,%r10,2),%xmm1
+  DB  196,129,113,196,84,81,16,2          ; vpinsrw       $0x2,0x10(%r9,%r10,2),%xmm1,%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  117,10                              ; jne           2767 <_sk_load_tables_rgb_u16_be_avx+0x2b8>
+  DB  233,162,253,255,255                 ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+  DB  233,157,253,255,255                 ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+  DB  196,129,121,110,76,81,18            ; vmovd         0x12(%r9,%r10,2),%xmm1
+  DB  196,1,113,196,76,81,22,2            ; vpinsrw       $0x2,0x16(%r9,%r10,2),%xmm1,%xmm9
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,26                              ; jb            2796 <_sk_load_tables_rgb_u16_be_avx+0x2e7>
+  DB  196,129,121,110,76,81,24            ; vmovd         0x18(%r9,%r10,2),%xmm1
+  DB  196,129,113,196,76,81,28,2          ; vpinsrw       $0x2,0x1c(%r9,%r10,2),%xmm1,%xmm1
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  117,10                              ; jne           279b <_sk_load_tables_rgb_u16_be_avx+0x2ec>
+  DB  233,110,253,255,255                 ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+  DB  233,105,253,255,255                 ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+  DB  196,129,121,110,92,81,30            ; vmovd         0x1e(%r9,%r10,2),%xmm3
+  DB  196,1,97,196,92,81,34,2             ; vpinsrw       $0x2,0x22(%r9,%r10,2),%xmm3,%xmm11
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,20                              ; jb            27c4 <_sk_load_tables_rgb_u16_be_avx+0x315>
+  DB  196,129,121,110,92,81,36            ; vmovd         0x24(%r9,%r10,2),%xmm3
+  DB  196,129,97,196,92,81,40,2           ; vpinsrw       $0x2,0x28(%r9,%r10,2),%xmm3,%xmm3
+  DB  233,64,253,255,255                  ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+  DB  233,59,253,255,255                  ; jmpq          2504 <_sk_load_tables_rgb_u16_be_avx+0x55>
+
+PUBLIC _sk_byte_tables_avx
+_sk_byte_tables_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,164,63,0,0          ; vbroadcastss  0x3fa4(%rip),%ymm8        # 6778 <_sk_callback_avx+0x274>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  197,125,91,200                      ; vcvtps2dq     %ymm0,%ymm9
+  DB  196,65,249,126,201                  ; vmovq         %xmm9,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,131,121,32,4,19,0               ; vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  DB  196,67,249,22,202,1                 ; vpextrq       $0x1,%xmm9,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,121,32,20,11,1                ; vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,99,125,25,200,1                 ; vextractf128  $0x1,%ymm9,%xmm0
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,67,41,32,201,2                  ; vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,49,32,202,3                  ; vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  196,3,121,32,20,19,0                ; vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm10
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,41,32,4,11,1                ; vpinsrb       $0x1,(%r11,%r9,1),%xmm10,%xmm0
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,195,121,32,193,2                ; vpinsrb       $0x2,%r9d,%xmm0,%xmm0
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,121,32,210,3                 ; vpinsrb       $0x3,%r10d,%xmm0,%xmm10
+  DB  196,193,116,89,192                  ; vmulps        %ymm8,%ymm1,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,12,25,0              ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,113,32,12,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm1
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,25,192,1                ; vextractf128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,113,32,202,2                ; vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,67,113,32,227,3                 ; vpinsrb       $0x3,%r11d,%xmm1,%xmm12
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,12,25,0              ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,113,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm1,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,2                ; vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  DB  71,15,182,12,25                     ; movzbl        (%r9,%r11,1),%r9d
+  DB  196,67,121,32,233,3                 ; vpinsrb       $0x3,%r9d,%xmm0,%xmm13
+  DB  76,139,72,16                        ; mov           0x10(%rax),%r9
+  DB  196,193,108,89,200                  ; vmulps        %ymm8,%ymm2,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,20,25,0              ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,20,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm2
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,105,32,210,2                ; vpinsrb       $0x2,%r10d,%xmm2,%xmm2
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,67,105,32,219,3                 ; vpinsrb       $0x3,%r11d,%xmm2,%xmm11
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,20,25,0              ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,12,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm1
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,113,32,202,2                ; vpinsrb       $0x2,%r10d,%xmm1,%xmm1
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  71,15,182,12,25                     ; movzbl        (%r9,%r11,1),%r9d
+  DB  196,67,113,32,241,3                 ; vpinsrb       $0x3,%r9d,%xmm1,%xmm14
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  196,193,100,89,200                  ; vmulps        %ymm8,%ymm3,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,193,249,126,201                 ; vmovq         %xmm1,%r9
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,195,121,32,28,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm3
+  DB  196,227,249,22,200,1                ; vpextrq       $0x1,%xmm1,%rax
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,97,32,28,10,1               ; vpinsrb       $0x1,(%r10,%r9,1),%xmm3,%xmm3
+  DB  65,137,193                          ; mov           %eax,%r9d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  71,15,182,12,10                     ; movzbl        (%r10,%r9,1),%r9d
+  DB  196,195,97,32,217,2                 ; vpinsrb       $0x2,%r9d,%xmm3,%xmm3
+  DB  196,193,249,126,201                 ; vmovq         %xmm1,%r9
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,99,97,32,192,3                  ; vpinsrb       $0x3,%eax,%xmm3,%xmm8
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,195,121,32,4,2,0                ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,121,32,4,10,1               ; vpinsrb       $0x1,(%r10,%r9,1),%xmm0,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,99,121,32,248,2                 ; vpinsrb       $0x2,%eax,%xmm0,%xmm15
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,26                      ; movzbl        (%r10,%r11,1),%eax
+  DB  196,194,121,49,193                  ; vpmovzxbd     %xmm9,%xmm0
+  DB  196,194,121,49,202                  ; vpmovzxbd     %xmm10,%xmm1
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,194,121,49,204                  ; vpmovzxbd     %xmm12,%xmm1
+  DB  196,194,121,49,213                  ; vpmovzxbd     %xmm13,%xmm2
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,98,125,24,13,78,61,0,0          ; vbroadcastss  0x3d4e(%rip),%ymm9        # 677c <_sk_callback_avx+0x278>
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,201                  ; vmulps        %ymm9,%ymm1,%ymm1
+  DB  196,194,121,49,211                  ; vpmovzxbd     %xmm11,%xmm2
+  DB  196,194,121,49,222                  ; vpmovzxbd     %xmm14,%xmm3
+  DB  196,227,109,24,211,1                ; vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,209                  ; vmulps        %ymm9,%ymm2,%ymm2
+  DB  196,66,121,49,192                   ; vpmovzxbd     %xmm8,%xmm8
+  DB  196,227,1,32,216,3                  ; vpinsrb       $0x3,%eax,%xmm15,%xmm3
+  DB  196,226,121,49,219                  ; vpmovzxbd     %xmm3,%xmm3
+  DB  196,227,61,24,219,1                 ; vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,217                  ; vmulps        %ymm9,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_byte_tables_rgb_avx
+_sk_byte_tables_rgb_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,139,72,24                        ; mov           0x18(%rax),%r9d
+  DB  65,255,201                          ; dec           %r9d
+  DB  196,65,121,110,193                  ; vmovd         %r9d,%xmm8
+  DB  196,65,121,112,192,0                ; vpshufd       $0x0,%xmm8,%xmm8
+  DB  196,67,61,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,125,91,200                      ; vcvtps2dq     %ymm0,%ymm9
+  DB  196,65,249,126,201                  ; vmovq         %xmm9,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,131,121,32,4,19,0               ; vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm0
+  DB  196,67,249,22,202,1                 ; vpextrq       $0x1,%xmm9,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,3,121,32,20,11,1                ; vpinsrb       $0x1,(%r11,%r9,1),%xmm0,%xmm10
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,99,125,25,200,1                 ; vextractf128  $0x1,%ymm9,%xmm0
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,67,41,32,201,2                  ; vpinsrb       $0x2,%r9d,%xmm10,%xmm9
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,49,32,202,3                  ; vpinsrb       $0x3,%r10d,%xmm9,%xmm9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  196,3,121,32,20,19,0                ; vpinsrb       $0x0,(%r11,%r10,1),%xmm0,%xmm10
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,41,32,4,11,1                ; vpinsrb       $0x1,(%r11,%r9,1),%xmm10,%xmm0
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  71,15,182,12,11                     ; movzbl        (%r11,%r9,1),%r9d
+  DB  196,195,121,32,193,2                ; vpinsrb       $0x2,%r9d,%xmm0,%xmm0
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  71,15,182,20,19                     ; movzbl        (%r11,%r10,1),%r10d
+  DB  196,67,121,32,218,3                 ; vpinsrb       $0x3,%r10d,%xmm0,%xmm11
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,4,25,0               ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,121,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,2                ; vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  196,67,121,32,211,3                 ; vpinsrb       $0x3,%r11d,%xmm0,%xmm10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  196,131,121,32,4,25,0               ; vpinsrb       $0x0,(%r9,%r11,1),%xmm0,%xmm0
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,121,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm0,%xmm0
+  DB  69,137,218                          ; mov           %r11d,%r10d
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  196,195,121,32,194,2                ; vpinsrb       $0x2,%r10d,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  71,15,182,12,25                     ; movzbl        (%r9,%r11,1),%r9d
+  DB  196,67,121,32,225,3                 ; vpinsrb       $0x3,%r9d,%xmm0,%xmm12
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  197,188,89,194                      ; vmulps        %ymm2,%ymm8,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,195,121,32,20,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm2
+  DB  196,227,249,22,192,1                ; vpextrq       $0x1,%xmm0,%rax
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,105,32,20,10,1              ; vpinsrb       $0x1,(%r10,%r9,1),%xmm2,%xmm2
+  DB  65,137,193                          ; mov           %eax,%r9d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,227,125,25,192,1                ; vextractf128  $0x1,%ymm0,%xmm0
+  DB  71,15,182,12,10                     ; movzbl        (%r10,%r9,1),%r9d
+  DB  196,195,105,32,209,2                ; vpinsrb       $0x2,%r9d,%xmm2,%xmm2
+  DB  196,193,249,126,193                 ; vmovq         %xmm0,%r9
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,99,105,32,192,3                 ; vpinsrb       $0x3,%eax,%xmm2,%xmm8
+  DB  68,137,200                          ; mov           %r9d,%eax
+  DB  196,195,121,32,12,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,131,113,32,4,10,1               ; vpinsrb       $0x1,(%r10,%r9,1),%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  196,99,121,32,232,2                 ; vpinsrb       $0x2,%eax,%xmm0,%xmm13
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,26                      ; movzbl        (%r10,%r11,1),%eax
+  DB  196,194,121,49,193                  ; vpmovzxbd     %xmm9,%xmm0
+  DB  196,194,121,49,203                  ; vpmovzxbd     %xmm11,%xmm1
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,13,47,59,0,0          ; vbroadcastss  0x3b2f(%rip),%ymm9        # 6780 <_sk_callback_avx+0x27c>
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  196,194,121,49,202                  ; vpmovzxbd     %xmm10,%xmm1
+  DB  196,194,121,49,212                  ; vpmovzxbd     %xmm12,%xmm2
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,201                  ; vmulps        %ymm9,%ymm1,%ymm1
+  DB  196,66,121,49,192                   ; vpmovzxbd     %xmm8,%xmm8
+  DB  196,227,17,32,208,3                 ; vpinsrb       $0x3,%eax,%xmm13,%xmm2
+  DB  196,226,121,49,210                  ; vpmovzxbd     %xmm2,%xmm2
+  DB  196,227,61,24,210,1                 ; vinsertf128   $0x1,%xmm2,%ymm8,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,209                  ; vmulps        %ymm9,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_r_avx
+_sk_table_r_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,65,121,112,192,0                ; vpshufd       $0x0,%xmm8,%xmm8
+  DB  196,67,61,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,125,91,192                      ; vcvtps2dq     %ymm0,%ymm8
+  DB  196,99,125,25,192,1                 ; vextractf128  $0x1,%ymm8,%xmm0
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,65,122,16,12,129                ; vmovss        (%r9,%rax,4),%xmm9
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,49,33,12,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,20,129                ; vmovss        (%r9,%rax,4),%xmm10
+  DB  196,65,249,126,194                  ; vmovq         %xmm8,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,28,153                 ; vmovss        (%r9,%r11,4),%xmm11
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,193,122,16,4,129                ; vmovss        (%r9,%rax,4),%xmm0
+  DB  196,67,249,22,195,1                 ; vpextrq       $0x1,%xmm8,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,121,33,4,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm0,%xmm8
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,36,129                ; vmovss        (%r9,%rax,4),%xmm12
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,44,153                 ; vmovss        (%r9,%r11,4),%xmm13
+  DB  196,195,49,33,194,32                ; vinsertps     $0x20,%xmm10,%xmm9,%xmm0
+  DB  196,67,121,33,203,48                ; vinsertps     $0x30,%xmm11,%xmm0,%xmm9
+  DB  196,195,57,33,196,32                ; vinsertps     $0x20,%xmm12,%xmm8,%xmm0
+  DB  196,195,121,33,197,48               ; vinsertps     $0x30,%xmm13,%xmm0,%xmm0
+  DB  196,195,125,24,193,1                ; vinsertf128   $0x1,%xmm9,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_g_avx
+_sk_table_g_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,65,121,112,192,0                ; vpshufd       $0x0,%xmm8,%xmm8
+  DB  196,67,61,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,125,91,193                      ; vcvtps2dq     %ymm1,%ymm8
+  DB  196,99,125,25,193,1                 ; vextractf128  $0x1,%ymm8,%xmm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,65,122,16,12,129                ; vmovss        (%r9,%rax,4),%xmm9
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,49,33,12,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,20,129                ; vmovss        (%r9,%rax,4),%xmm10
+  DB  196,65,249,126,194                  ; vmovq         %xmm8,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,28,153                 ; vmovss        (%r9,%r11,4),%xmm11
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,193,122,16,12,129               ; vmovss        (%r9,%rax,4),%xmm1
+  DB  196,67,249,22,195,1                 ; vpextrq       $0x1,%xmm8,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,113,33,4,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm1,%xmm8
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,36,129                ; vmovss        (%r9,%rax,4),%xmm12
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,44,153                 ; vmovss        (%r9,%r11,4),%xmm13
+  DB  196,195,49,33,202,32                ; vinsertps     $0x20,%xmm10,%xmm9,%xmm1
+  DB  196,67,113,33,203,48                ; vinsertps     $0x30,%xmm11,%xmm1,%xmm9
+  DB  196,195,57,33,204,32                ; vinsertps     $0x20,%xmm12,%xmm8,%xmm1
+  DB  196,195,113,33,205,48               ; vinsertps     $0x30,%xmm13,%xmm1,%xmm1
+  DB  196,195,117,24,201,1                ; vinsertf128   $0x1,%xmm9,%ymm1,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_b_avx
+_sk_table_b_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,65,121,112,192,0                ; vpshufd       $0x0,%xmm8,%xmm8
+  DB  196,67,61,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,210                      ; vmulps        %ymm2,%ymm8,%ymm2
+  DB  197,125,91,194                      ; vcvtps2dq     %ymm2,%ymm8
+  DB  196,99,125,25,194,1                 ; vextractf128  $0x1,%ymm8,%xmm2
+  DB  196,193,249,126,210                 ; vmovq         %xmm2,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,65,122,16,12,129                ; vmovss        (%r9,%rax,4),%xmm9
+  DB  196,195,249,22,211,1                ; vpextrq       $0x1,%xmm2,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,49,33,12,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,20,129                ; vmovss        (%r9,%rax,4),%xmm10
+  DB  196,65,249,126,194                  ; vmovq         %xmm8,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,28,153                 ; vmovss        (%r9,%r11,4),%xmm11
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,193,122,16,20,129               ; vmovss        (%r9,%rax,4),%xmm2
+  DB  196,67,249,22,195,1                 ; vpextrq       $0x1,%xmm8,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,105,33,4,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm2,%xmm8
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,36,129                ; vmovss        (%r9,%rax,4),%xmm12
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,44,153                 ; vmovss        (%r9,%r11,4),%xmm13
+  DB  196,195,49,33,210,32                ; vinsertps     $0x20,%xmm10,%xmm9,%xmm2
+  DB  196,67,105,33,203,48                ; vinsertps     $0x30,%xmm11,%xmm2,%xmm9
+  DB  196,195,57,33,212,32                ; vinsertps     $0x20,%xmm12,%xmm8,%xmm2
+  DB  196,195,105,33,213,48               ; vinsertps     $0x30,%xmm13,%xmm2,%xmm2
+  DB  196,195,109,24,209,1                ; vinsertf128   $0x1,%xmm9,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_a_avx
+_sk_table_a_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  197,121,110,192                     ; vmovd         %eax,%xmm8
+  DB  196,65,121,112,192,0                ; vpshufd       $0x0,%xmm8,%xmm8
+  DB  196,67,61,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm8,%ymm8
+  DB  196,65,124,91,192                   ; vcvtdq2ps     %ymm8,%ymm8
+  DB  197,188,89,219                      ; vmulps        %ymm3,%ymm8,%ymm3
+  DB  197,125,91,195                      ; vcvtps2dq     %ymm3,%ymm8
+  DB  196,99,125,25,195,1                 ; vextractf128  $0x1,%ymm8,%xmm3
+  DB  196,193,249,126,218                 ; vmovq         %xmm3,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,65,122,16,12,129                ; vmovss        (%r9,%rax,4),%xmm9
+  DB  196,195,249,22,219,1                ; vpextrq       $0x1,%xmm3,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,49,33,12,145,16               ; vinsertps     $0x10,(%r9,%r10,4),%xmm9,%xmm9
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,20,129                ; vmovss        (%r9,%rax,4),%xmm10
+  DB  196,65,249,126,194                  ; vmovq         %xmm8,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,28,153                 ; vmovss        (%r9,%r11,4),%xmm11
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,193,122,16,28,129               ; vmovss        (%r9,%rax,4),%xmm3
+  DB  196,67,249,22,195,1                 ; vpextrq       $0x1,%xmm8,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,3,97,33,4,145,16                ; vinsertps     $0x10,(%r9,%r10,4),%xmm3,%xmm8
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,65,122,16,36,129                ; vmovss        (%r9,%rax,4),%xmm12
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,1,122,16,44,153                 ; vmovss        (%r9,%r11,4),%xmm13
+  DB  196,195,49,33,218,32                ; vinsertps     $0x20,%xmm10,%xmm9,%xmm3
+  DB  196,67,97,33,203,48                 ; vinsertps     $0x30,%xmm11,%xmm3,%xmm9
+  DB  196,195,57,33,220,32                ; vinsertps     $0x20,%xmm12,%xmm8,%xmm3
+  DB  196,195,97,33,221,48                ; vinsertps     $0x30,%xmm13,%xmm3,%xmm3
+  DB  196,195,101,24,217,1                ; vinsertf128   $0x1,%xmm9,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_r_avx
+_sk_parametric_r_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,124,194,192,2                ; vcmpleps      %ymm8,%ymm0,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  197,52,89,200                       ; vmulps        %ymm0,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  197,172,89,192                      ; vmulps        %ymm0,%ymm10,%ymm0
+  DB  196,193,124,88,195                  ; vaddps        %ymm11,%ymm0,%ymm0
+  DB  196,98,125,24,16                    ; vbroadcastss  (%rax),%ymm10
+  DB  197,124,91,216                      ; vcvtdq2ps     %ymm0,%ymm11
+  DB  196,98,125,24,37,220,55,0,0         ; vbroadcastss  0x37dc(%rip),%ymm12        # 6784 <_sk_callback_avx+0x280>
+  DB  196,65,36,89,220                    ; vmulps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,210,55,0,0         ; vbroadcastss  0x37d2(%rip),%ymm12        # 6788 <_sk_callback_avx+0x284>
+  DB  196,193,124,84,196                  ; vandps        %ymm12,%ymm0,%ymm0
+  DB  196,98,125,24,37,200,55,0,0         ; vbroadcastss  0x37c8(%rip),%ymm12        # 678c <_sk_callback_avx+0x288>
+  DB  196,193,124,86,196                  ; vorps         %ymm12,%ymm0,%ymm0
+  DB  196,98,125,24,37,190,55,0,0         ; vbroadcastss  0x37be(%rip),%ymm12        # 6790 <_sk_callback_avx+0x28c>
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,180,55,0,0         ; vbroadcastss  0x37b4(%rip),%ymm12        # 6794 <_sk_callback_avx+0x290>
+  DB  196,65,124,89,228                   ; vmulps        %ymm12,%ymm0,%ymm12
+  DB  196,65,36,92,220                    ; vsubps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,165,55,0,0         ; vbroadcastss  0x37a5(%rip),%ymm12        # 6798 <_sk_callback_avx+0x294>
+  DB  196,193,124,88,196                  ; vaddps        %ymm12,%ymm0,%ymm0
+  DB  196,98,125,24,37,155,55,0,0         ; vbroadcastss  0x379b(%rip),%ymm12        # 679c <_sk_callback_avx+0x298>
+  DB  197,156,94,192                      ; vdivps        %ymm0,%ymm12,%ymm0
+  DB  197,164,92,192                      ; vsubps        %ymm0,%ymm11,%ymm0
+  DB  197,172,89,192                      ; vmulps        %ymm0,%ymm10,%ymm0
+  DB  196,99,125,8,208,1                  ; vroundps      $0x1,%ymm0,%ymm10
+  DB  196,65,124,92,210                   ; vsubps        %ymm10,%ymm0,%ymm10
+  DB  196,98,125,24,29,127,55,0,0         ; vbroadcastss  0x377f(%rip),%ymm11        # 67a0 <_sk_callback_avx+0x29c>
+  DB  196,193,124,88,195                  ; vaddps        %ymm11,%ymm0,%ymm0
+  DB  196,98,125,24,29,117,55,0,0         ; vbroadcastss  0x3775(%rip),%ymm11        # 67a4 <_sk_callback_avx+0x2a0>
+  DB  196,65,44,89,219                    ; vmulps        %ymm11,%ymm10,%ymm11
+  DB  196,193,124,92,195                  ; vsubps        %ymm11,%ymm0,%ymm0
+  DB  196,98,125,24,29,102,55,0,0         ; vbroadcastss  0x3766(%rip),%ymm11        # 67a8 <_sk_callback_avx+0x2a4>
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,98,125,24,29,92,55,0,0          ; vbroadcastss  0x375c(%rip),%ymm11        # 67ac <_sk_callback_avx+0x2a8>
+  DB  196,65,36,94,210                    ; vdivps        %ymm10,%ymm11,%ymm10
+  DB  196,193,124,88,194                  ; vaddps        %ymm10,%ymm0,%ymm0
+  DB  196,98,125,24,21,77,55,0,0          ; vbroadcastss  0x374d(%rip),%ymm10        # 67b0 <_sk_callback_avx+0x2ac>
+  DB  196,193,124,89,194                  ; vmulps        %ymm10,%ymm0,%ymm0
+  DB  197,253,91,192                      ; vcvtps2dq     %ymm0,%ymm0
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,124,88,194                  ; vaddps        %ymm10,%ymm0,%ymm0
+  DB  196,195,125,74,193,128              ; vblendvps     %ymm8,%ymm9,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,124,95,192                  ; vmaxps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,5,36,55,0,0           ; vbroadcastss  0x3724(%rip),%ymm8        # 67b4 <_sk_callback_avx+0x2b0>
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_g_avx
+_sk_parametric_g_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,116,194,192,2                ; vcmpleps      %ymm8,%ymm1,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  197,52,89,201                       ; vmulps        %ymm1,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  197,172,89,201                      ; vmulps        %ymm1,%ymm10,%ymm1
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,16                    ; vbroadcastss  (%rax),%ymm10
+  DB  197,124,91,217                      ; vcvtdq2ps     %ymm1,%ymm11
+  DB  196,98,125,24,37,213,54,0,0         ; vbroadcastss  0x36d5(%rip),%ymm12        # 67b8 <_sk_callback_avx+0x2b4>
+  DB  196,65,36,89,220                    ; vmulps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,203,54,0,0         ; vbroadcastss  0x36cb(%rip),%ymm12        # 67bc <_sk_callback_avx+0x2b8>
+  DB  196,193,116,84,204                  ; vandps        %ymm12,%ymm1,%ymm1
+  DB  196,98,125,24,37,193,54,0,0         ; vbroadcastss  0x36c1(%rip),%ymm12        # 67c0 <_sk_callback_avx+0x2bc>
+  DB  196,193,116,86,204                  ; vorps         %ymm12,%ymm1,%ymm1
+  DB  196,98,125,24,37,183,54,0,0         ; vbroadcastss  0x36b7(%rip),%ymm12        # 67c4 <_sk_callback_avx+0x2c0>
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,173,54,0,0         ; vbroadcastss  0x36ad(%rip),%ymm12        # 67c8 <_sk_callback_avx+0x2c4>
+  DB  196,65,116,89,228                   ; vmulps        %ymm12,%ymm1,%ymm12
+  DB  196,65,36,92,220                    ; vsubps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,158,54,0,0         ; vbroadcastss  0x369e(%rip),%ymm12        # 67cc <_sk_callback_avx+0x2c8>
+  DB  196,193,116,88,204                  ; vaddps        %ymm12,%ymm1,%ymm1
+  DB  196,98,125,24,37,148,54,0,0         ; vbroadcastss  0x3694(%rip),%ymm12        # 67d0 <_sk_callback_avx+0x2cc>
+  DB  197,156,94,201                      ; vdivps        %ymm1,%ymm12,%ymm1
+  DB  197,164,92,201                      ; vsubps        %ymm1,%ymm11,%ymm1
+  DB  197,172,89,201                      ; vmulps        %ymm1,%ymm10,%ymm1
+  DB  196,99,125,8,209,1                  ; vroundps      $0x1,%ymm1,%ymm10
+  DB  196,65,116,92,210                   ; vsubps        %ymm10,%ymm1,%ymm10
+  DB  196,98,125,24,29,120,54,0,0         ; vbroadcastss  0x3678(%rip),%ymm11        # 67d4 <_sk_callback_avx+0x2d0>
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,29,110,54,0,0         ; vbroadcastss  0x366e(%rip),%ymm11        # 67d8 <_sk_callback_avx+0x2d4>
+  DB  196,65,44,89,219                    ; vmulps        %ymm11,%ymm10,%ymm11
+  DB  196,193,116,92,203                  ; vsubps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,29,95,54,0,0          ; vbroadcastss  0x365f(%rip),%ymm11        # 67dc <_sk_callback_avx+0x2d8>
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,98,125,24,29,85,54,0,0          ; vbroadcastss  0x3655(%rip),%ymm11        # 67e0 <_sk_callback_avx+0x2dc>
+  DB  196,65,36,94,210                    ; vdivps        %ymm10,%ymm11,%ymm10
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  196,98,125,24,21,70,54,0,0          ; vbroadcastss  0x3646(%rip),%ymm10        # 67e4 <_sk_callback_avx+0x2e0>
+  DB  196,193,116,89,202                  ; vmulps        %ymm10,%ymm1,%ymm1
+  DB  197,253,91,201                      ; vcvtps2dq     %ymm1,%ymm1
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,116,88,202                  ; vaddps        %ymm10,%ymm1,%ymm1
+  DB  196,195,117,74,201,128              ; vblendvps     %ymm8,%ymm9,%ymm1,%ymm1
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,116,95,200                  ; vmaxps        %ymm8,%ymm1,%ymm1
+  DB  196,98,125,24,5,29,54,0,0           ; vbroadcastss  0x361d(%rip),%ymm8        # 67e8 <_sk_callback_avx+0x2e4>
+  DB  196,193,116,93,200                  ; vminps        %ymm8,%ymm1,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_b_avx
+_sk_parametric_b_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,108,194,192,2                ; vcmpleps      %ymm8,%ymm2,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  197,52,89,202                       ; vmulps        %ymm2,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  196,98,125,24,16                    ; vbroadcastss  (%rax),%ymm10
+  DB  197,124,91,218                      ; vcvtdq2ps     %ymm2,%ymm11
+  DB  196,98,125,24,37,206,53,0,0         ; vbroadcastss  0x35ce(%rip),%ymm12        # 67ec <_sk_callback_avx+0x2e8>
+  DB  196,65,36,89,220                    ; vmulps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,196,53,0,0         ; vbroadcastss  0x35c4(%rip),%ymm12        # 67f0 <_sk_callback_avx+0x2ec>
+  DB  196,193,108,84,212                  ; vandps        %ymm12,%ymm2,%ymm2
+  DB  196,98,125,24,37,186,53,0,0         ; vbroadcastss  0x35ba(%rip),%ymm12        # 67f4 <_sk_callback_avx+0x2f0>
+  DB  196,193,108,86,212                  ; vorps         %ymm12,%ymm2,%ymm2
+  DB  196,98,125,24,37,176,53,0,0         ; vbroadcastss  0x35b0(%rip),%ymm12        # 67f8 <_sk_callback_avx+0x2f4>
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,166,53,0,0         ; vbroadcastss  0x35a6(%rip),%ymm12        # 67fc <_sk_callback_avx+0x2f8>
+  DB  196,65,108,89,228                   ; vmulps        %ymm12,%ymm2,%ymm12
+  DB  196,65,36,92,220                    ; vsubps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,151,53,0,0         ; vbroadcastss  0x3597(%rip),%ymm12        # 6800 <_sk_callback_avx+0x2fc>
+  DB  196,193,108,88,212                  ; vaddps        %ymm12,%ymm2,%ymm2
+  DB  196,98,125,24,37,141,53,0,0         ; vbroadcastss  0x358d(%rip),%ymm12        # 6804 <_sk_callback_avx+0x300>
+  DB  197,156,94,210                      ; vdivps        %ymm2,%ymm12,%ymm2
+  DB  197,164,92,210                      ; vsubps        %ymm2,%ymm11,%ymm2
+  DB  197,172,89,210                      ; vmulps        %ymm2,%ymm10,%ymm2
+  DB  196,99,125,8,210,1                  ; vroundps      $0x1,%ymm2,%ymm10
+  DB  196,65,108,92,210                   ; vsubps        %ymm10,%ymm2,%ymm10
+  DB  196,98,125,24,29,113,53,0,0         ; vbroadcastss  0x3571(%rip),%ymm11        # 6808 <_sk_callback_avx+0x304>
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  196,98,125,24,29,103,53,0,0         ; vbroadcastss  0x3567(%rip),%ymm11        # 680c <_sk_callback_avx+0x308>
+  DB  196,65,44,89,219                    ; vmulps        %ymm11,%ymm10,%ymm11
+  DB  196,193,108,92,211                  ; vsubps        %ymm11,%ymm2,%ymm2
+  DB  196,98,125,24,29,88,53,0,0          ; vbroadcastss  0x3558(%rip),%ymm11        # 6810 <_sk_callback_avx+0x30c>
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,98,125,24,29,78,53,0,0          ; vbroadcastss  0x354e(%rip),%ymm11        # 6814 <_sk_callback_avx+0x310>
+  DB  196,65,36,94,210                    ; vdivps        %ymm10,%ymm11,%ymm10
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  196,98,125,24,21,63,53,0,0          ; vbroadcastss  0x353f(%rip),%ymm10        # 6818 <_sk_callback_avx+0x314>
+  DB  196,193,108,89,210                  ; vmulps        %ymm10,%ymm2,%ymm2
+  DB  197,253,91,210                      ; vcvtps2dq     %ymm2,%ymm2
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,108,88,210                  ; vaddps        %ymm10,%ymm2,%ymm2
+  DB  196,195,109,74,209,128              ; vblendvps     %ymm8,%ymm9,%ymm2,%ymm2
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,108,95,208                  ; vmaxps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,24,5,22,53,0,0           ; vbroadcastss  0x3516(%rip),%ymm8        # 681c <_sk_callback_avx+0x318>
+  DB  196,193,108,93,208                  ; vminps        %ymm8,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_a_avx
+_sk_parametric_a_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,64,16                 ; vbroadcastss  0x10(%rax),%ymm8
+  DB  196,65,100,194,192,2                ; vcmpleps      %ymm8,%ymm3,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  197,52,89,203                       ; vmulps        %ymm3,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,4                  ; vbroadcastss  0x4(%rax),%ymm10
+  DB  196,98,125,24,88,8                  ; vbroadcastss  0x8(%rax),%ymm11
+  DB  197,172,89,219                      ; vmulps        %ymm3,%ymm10,%ymm3
+  DB  196,193,100,88,219                  ; vaddps        %ymm11,%ymm3,%ymm3
+  DB  196,98,125,24,16                    ; vbroadcastss  (%rax),%ymm10
+  DB  197,124,91,219                      ; vcvtdq2ps     %ymm3,%ymm11
+  DB  196,98,125,24,37,199,52,0,0         ; vbroadcastss  0x34c7(%rip),%ymm12        # 6820 <_sk_callback_avx+0x31c>
+  DB  196,65,36,89,220                    ; vmulps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,189,52,0,0         ; vbroadcastss  0x34bd(%rip),%ymm12        # 6824 <_sk_callback_avx+0x320>
+  DB  196,193,100,84,220                  ; vandps        %ymm12,%ymm3,%ymm3
+  DB  196,98,125,24,37,179,52,0,0         ; vbroadcastss  0x34b3(%rip),%ymm12        # 6828 <_sk_callback_avx+0x324>
+  DB  196,193,100,86,220                  ; vorps         %ymm12,%ymm3,%ymm3
+  DB  196,98,125,24,37,169,52,0,0         ; vbroadcastss  0x34a9(%rip),%ymm12        # 682c <_sk_callback_avx+0x328>
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,159,52,0,0         ; vbroadcastss  0x349f(%rip),%ymm12        # 6830 <_sk_callback_avx+0x32c>
+  DB  196,65,100,89,228                   ; vmulps        %ymm12,%ymm3,%ymm12
+  DB  196,65,36,92,220                    ; vsubps        %ymm12,%ymm11,%ymm11
+  DB  196,98,125,24,37,144,52,0,0         ; vbroadcastss  0x3490(%rip),%ymm12        # 6834 <_sk_callback_avx+0x330>
+  DB  196,193,100,88,220                  ; vaddps        %ymm12,%ymm3,%ymm3
+  DB  196,98,125,24,37,134,52,0,0         ; vbroadcastss  0x3486(%rip),%ymm12        # 6838 <_sk_callback_avx+0x334>
+  DB  197,156,94,219                      ; vdivps        %ymm3,%ymm12,%ymm3
+  DB  197,164,92,219                      ; vsubps        %ymm3,%ymm11,%ymm3
+  DB  197,172,89,219                      ; vmulps        %ymm3,%ymm10,%ymm3
+  DB  196,99,125,8,211,1                  ; vroundps      $0x1,%ymm3,%ymm10
+  DB  196,65,100,92,210                   ; vsubps        %ymm10,%ymm3,%ymm10
+  DB  196,98,125,24,29,106,52,0,0         ; vbroadcastss  0x346a(%rip),%ymm11        # 683c <_sk_callback_avx+0x338>
+  DB  196,193,100,88,219                  ; vaddps        %ymm11,%ymm3,%ymm3
+  DB  196,98,125,24,29,96,52,0,0          ; vbroadcastss  0x3460(%rip),%ymm11        # 6840 <_sk_callback_avx+0x33c>
+  DB  196,65,44,89,219                    ; vmulps        %ymm11,%ymm10,%ymm11
+  DB  196,193,100,92,219                  ; vsubps        %ymm11,%ymm3,%ymm3
+  DB  196,98,125,24,29,81,52,0,0          ; vbroadcastss  0x3451(%rip),%ymm11        # 6844 <_sk_callback_avx+0x340>
+  DB  196,65,36,92,210                    ; vsubps        %ymm10,%ymm11,%ymm10
+  DB  196,98,125,24,29,71,52,0,0          ; vbroadcastss  0x3447(%rip),%ymm11        # 6848 <_sk_callback_avx+0x344>
+  DB  196,65,36,94,210                    ; vdivps        %ymm10,%ymm11,%ymm10
+  DB  196,193,100,88,218                  ; vaddps        %ymm10,%ymm3,%ymm3
+  DB  196,98,125,24,21,56,52,0,0          ; vbroadcastss  0x3438(%rip),%ymm10        # 684c <_sk_callback_avx+0x348>
+  DB  196,193,100,89,218                  ; vmulps        %ymm10,%ymm3,%ymm3
+  DB  197,253,91,219                      ; vcvtps2dq     %ymm3,%ymm3
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,193,100,88,218                  ; vaddps        %ymm10,%ymm3,%ymm3
+  DB  196,195,101,74,217,128              ; vblendvps     %ymm8,%ymm9,%ymm3,%ymm3
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  196,193,100,95,216                  ; vmaxps        %ymm8,%ymm3,%ymm3
+  DB  196,98,125,24,5,15,52,0,0           ; vbroadcastss  0x340f(%rip),%ymm8        # 6850 <_sk_callback_avx+0x34c>
+  DB  196,193,100,93,216                  ; vminps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lab_to_xyz_avx
+_sk_lab_to_xyz_avx LABEL PROC
+  DB  196,98,125,24,5,1,52,0,0            ; vbroadcastss  0x3401(%rip),%ymm8        # 6854 <_sk_callback_avx+0x350>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,5,247,51,0,0          ; vbroadcastss  0x33f7(%rip),%ymm8        # 6858 <_sk_callback_avx+0x354>
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  196,98,125,24,13,237,51,0,0         ; vbroadcastss  0x33ed(%rip),%ymm9        # 685c <_sk_callback_avx+0x358>
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  196,193,108,88,209                  ; vaddps        %ymm9,%ymm2,%ymm2
+  DB  196,98,125,24,5,217,51,0,0          ; vbroadcastss  0x33d9(%rip),%ymm8        # 6860 <_sk_callback_avx+0x35c>
+  DB  196,193,124,88,192                  ; vaddps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,5,207,51,0,0          ; vbroadcastss  0x33cf(%rip),%ymm8        # 6864 <_sk_callback_avx+0x360>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,5,197,51,0,0          ; vbroadcastss  0x33c5(%rip),%ymm8        # 6868 <_sk_callback_avx+0x364>
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  197,252,88,201                      ; vaddps        %ymm1,%ymm0,%ymm1
+  DB  196,98,125,24,5,183,51,0,0          ; vbroadcastss  0x33b7(%rip),%ymm8        # 686c <_sk_callback_avx+0x368>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,252,92,210                      ; vsubps        %ymm2,%ymm0,%ymm2
+  DB  197,116,89,193                      ; vmulps        %ymm1,%ymm1,%ymm8
+  DB  196,65,116,89,192                   ; vmulps        %ymm8,%ymm1,%ymm8
+  DB  196,98,125,24,13,160,51,0,0         ; vbroadcastss  0x33a0(%rip),%ymm9        # 6870 <_sk_callback_avx+0x36c>
+  DB  196,65,52,194,208,1                 ; vcmpltps      %ymm8,%ymm9,%ymm10
+  DB  196,98,125,24,29,149,51,0,0         ; vbroadcastss  0x3395(%rip),%ymm11        # 6874 <_sk_callback_avx+0x370>
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  196,98,125,24,37,139,51,0,0         ; vbroadcastss  0x338b(%rip),%ymm12        # 6878 <_sk_callback_avx+0x374>
+  DB  196,193,116,89,204                  ; vmulps        %ymm12,%ymm1,%ymm1
+  DB  196,67,117,74,192,160               ; vblendvps     %ymm10,%ymm8,%ymm1,%ymm8
+  DB  197,252,89,200                      ; vmulps        %ymm0,%ymm0,%ymm1
+  DB  197,252,89,201                      ; vmulps        %ymm1,%ymm0,%ymm1
+  DB  197,52,194,209,1                    ; vcmpltps      %ymm1,%ymm9,%ymm10
+  DB  196,193,124,88,195                  ; vaddps        %ymm11,%ymm0,%ymm0
+  DB  196,193,124,89,196                  ; vmulps        %ymm12,%ymm0,%ymm0
+  DB  196,227,125,74,201,160              ; vblendvps     %ymm10,%ymm1,%ymm0,%ymm1
+  DB  197,236,89,194                      ; vmulps        %ymm2,%ymm2,%ymm0
+  DB  197,236,89,192                      ; vmulps        %ymm0,%ymm2,%ymm0
+  DB  197,52,194,200,1                    ; vcmpltps      %ymm0,%ymm9,%ymm9
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  196,193,108,89,212                  ; vmulps        %ymm12,%ymm2,%ymm2
+  DB  196,227,109,74,208,144              ; vblendvps     %ymm9,%ymm0,%ymm2,%ymm2
+  DB  196,226,125,24,5,65,51,0,0          ; vbroadcastss  0x3341(%rip),%ymm0        # 687c <_sk_callback_avx+0x378>
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  196,98,125,24,5,56,51,0,0           ; vbroadcastss  0x3338(%rip),%ymm8        # 6880 <_sk_callback_avx+0x37c>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_a8_avx
+_sk_load_a8_avx LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,62                              ; jne           359f <_sk_load_a8_avx+0x4e>
+  DB  197,250,126,0                       ; vmovq         (%rax),%xmm0
+  DB  196,226,121,49,200                  ; vpmovzxbd     %xmm0,%xmm1
+  DB  196,227,121,4,192,229               ; vpermilps     $0xe5,%xmm0,%xmm0
+  DB  196,226,121,49,192                  ; vpmovzxbd     %xmm0,%xmm0
+  DB  196,227,117,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,252,50,0,0        ; vbroadcastss  0x32fc(%rip),%ymm1        # 6884 <_sk_callback_avx+0x380>
+  DB  197,252,89,217                      ; vmulps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           35a8 <_sk_load_a8_avx+0x57>
+  DB  196,193,249,110,194                 ; vmovq         %r10,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  235,160                             ; jmp           3565 <_sk_load_a8_avx+0x14>
+
+PUBLIC _sk_gather_a8_avx
+_sk_gather_a8_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  197,249,110,80,16                   ; vmovd         0x10(%rax),%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,226,105,64,217                  ; vpmulld       %xmm1,%xmm2,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,226,105,64,201                  ; vpmulld       %xmm1,%xmm2,%xmm1
+  DB  197,254,91,208                      ; vcvttps2dq    %ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  197,241,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm0
+  DB  197,225,254,202                     ; vpaddd        %xmm2,%xmm3,%xmm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,32,20,1,0               ; vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,12,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,113,32,200,2                ; vpinsrb       $0x2,%eax,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,25                      ; movzbl        (%r9,%r11,1),%eax
+  DB  196,227,113,32,200,3                ; vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,32,20,1,0               ; vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,121,32,192,2                ; vpinsrb       $0x2,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,25                      ; movzbl        (%r9,%r11,1),%eax
+  DB  196,226,121,49,201                  ; vpmovzxbd     %xmm1,%xmm1
+  DB  196,227,121,32,192,3                ; vpinsrb       $0x3,%eax,%xmm0,%xmm0
+  DB  196,226,121,49,192                  ; vpmovzxbd     %xmm0,%xmm0
+  DB  196,227,117,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,247,49,0,0        ; vbroadcastss  0x31f7(%rip),%ymm1        # 6888 <_sk_callback_avx+0x384>
+  DB  197,252,89,217                      ; vmulps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_a8_avx
+_sk_store_a8_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,98,125,24,5,217,49,0,0          ; vbroadcastss  0x31d9(%rip),%ymm8        # 688c <_sk_callback_avx+0x388>
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,67,125,25,193,1                 ; vextractf128  $0x1,%ymm8,%xmm9
+  DB  196,66,57,43,193                    ; vpackusdw     %xmm9,%xmm8,%xmm8
+  DB  196,65,57,103,192                   ; vpackuswb     %xmm8,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           36dc <_sk_store_a8_avx+0x37>
+  DB  196,65,123,17,4,19                  ; vmovsd        %xmm8,(%r11,%rdx,1)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            36d8 <_sk_store_a8_avx+0x33>
+  DB  196,66,121,48,192                   ; vpmovzxbw     %xmm8,%xmm8
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,68,0,0,0                  ; lea           0x44(%rip),%r10        # 3740 <_sk_store_a8_avx+0x9b>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,67,121,20,68,19,6,12            ; vpextrb       $0xc,%xmm8,0x6(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,5,10            ; vpextrb       $0xa,%xmm8,0x5(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,4,8             ; vpextrb       $0x8,%xmm8,0x4(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,3,6             ; vpextrb       $0x6,%xmm8,0x3(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,2,4             ; vpextrb       $0x4,%xmm8,0x2(%r11,%rdx,1)
+  DB  196,67,121,20,68,19,1,2             ; vpextrb       $0x2,%xmm8,0x1(%r11,%rdx,1)
+  DB  196,67,121,20,4,19,0                ; vpextrb       $0x0,%xmm8,(%r11,%rdx,1)
+  DB  235,154                             ; jmp           36d8 <_sk_store_a8_avx+0x33>
+  DB  102,144                             ; xchg          %ax,%ax
+  DB  245                                 ; cmc
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  237                                 ; in            (%dx),%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,229                             ; jmpq          *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  221,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,213                             ; callq         *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,205                             ; dec           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,197                             ; inc           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_g8_avx
+_sk_load_g8_avx LABEL PROC
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  72,1,208                            ; add           %rdx,%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,67                              ; jne           37af <_sk_load_g8_avx+0x53>
+  DB  197,250,126,0                       ; vmovq         (%rax),%xmm0
+  DB  196,226,121,49,200                  ; vpmovzxbd     %xmm0,%xmm1
+  DB  196,227,121,4,192,229               ; vpermilps     $0xe5,%xmm0,%xmm0
+  DB  196,226,121,49,192                  ; vpmovzxbd     %xmm0,%xmm0
+  DB  196,227,117,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,253,48,0,0        ; vbroadcastss  0x30fd(%rip),%ymm1        # 6890 <_sk_callback_avx+0x38c>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,242,48,0,0        ; vbroadcastss  0x30f2(%rip),%ymm3        # 6894 <_sk_callback_avx+0x390>
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+  DB  83                                  ; push          %rbx
+  DB  49,201                              ; xor           %ecx,%ecx
+  DB  77,137,195                          ; mov           %r8,%r11
+  DB  69,49,210                           ; xor           %r10d,%r10d
+  DB  15,182,24                           ; movzbl        (%rax),%ebx
+  DB  72,255,192                          ; inc           %rax
+  DB  72,211,227                          ; shl           %cl,%rbx
+  DB  73,9,218                            ; or            %rbx,%r10
+  DB  72,131,193,8                        ; add           $0x8,%rcx
+  DB  73,255,203                          ; dec           %r11
+  DB  117,235                             ; jne           37b8 <_sk_load_g8_avx+0x5c>
+  DB  196,193,249,110,194                 ; vmovq         %r10,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  235,155                             ; jmp           3770 <_sk_load_g8_avx+0x14>
+
+PUBLIC _sk_gather_g8_avx
+_sk_gather_g8_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  197,249,110,80,16                   ; vmovd         0x10(%rax),%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,226,105,64,217                  ; vpmulld       %xmm1,%xmm2,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,226,105,64,201                  ; vpmulld       %xmm1,%xmm2,%xmm1
+  DB  197,254,91,208                      ; vcvttps2dq    %ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  197,241,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm0
+  DB  197,225,254,202                     ; vpaddd        %xmm2,%xmm3,%xmm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,32,20,1,0               ; vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,12,17,1              ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,113,32,200,2                ; vpinsrb       $0x2,%eax,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,25                      ; movzbl        (%r9,%r11,1),%eax
+  DB  196,227,113,32,200,3                ; vpinsrb       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,32,20,1,0               ; vpinsrb       $0x0,(%r9,%rax,1),%xmm0,%xmm2
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,32,4,17,1               ; vpinsrb       $0x1,(%r9,%r10,1),%xmm2,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  196,227,121,32,192,2                ; vpinsrb       $0x2,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,182,4,25                      ; movzbl        (%r9,%r11,1),%eax
+  DB  196,226,121,49,201                  ; vpmovzxbd     %xmm1,%xmm1
+  DB  196,227,121,32,192,3                ; vpinsrb       $0x3,%eax,%xmm0,%xmm0
+  DB  196,226,121,49,192                  ; vpmovzxbd     %xmm0,%xmm0
+  DB  196,227,117,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,247,47,0,0        ; vbroadcastss  0x2ff7(%rip),%ymm1        # 6898 <_sk_callback_avx+0x394>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,236,47,0,0        ; vbroadcastss  0x2fec(%rip),%ymm3        # 689c <_sk_callback_avx+0x398>
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gather_i8_avx
+_sk_gather_i8_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,193                          ; mov           %rax,%r9
+  DB  77,133,201                          ; test          %r9,%r9
+  DB  116,5                               ; je            38c9 <_sk_gather_i8_avx+0xf>
+  DB  76,137,200                          ; mov           %r9,%rax
+  DB  235,2                               ; jmp           38cb <_sk_gather_i8_avx+0x11>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  83                                  ; push          %rbx
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  197,254,91,209                      ; vcvttps2dq    %ymm1,%ymm2
+  DB  197,249,110,72,16                   ; vmovd         0x10(%rax),%xmm1
+  DB  197,249,112,217,0                   ; vpshufd       $0x0,%xmm1,%xmm3
+  DB  196,226,97,64,202                   ; vpmulld       %xmm2,%xmm3,%xmm1
+  DB  196,227,125,25,210,1                ; vextractf128  $0x1,%ymm2,%xmm2
+  DB  196,226,97,64,210                   ; vpmulld       %xmm2,%xmm3,%xmm2
+  DB  197,254,91,192                      ; vcvttps2dq    %ymm0,%ymm0
+  DB  196,227,125,25,195,1                ; vextractf128  $0x1,%ymm0,%xmm3
+  DB  197,233,254,211                     ; vpaddd        %xmm3,%xmm2,%xmm2
+  DB  196,193,249,126,211                 ; vmovq         %xmm2,%r11
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,195,121,32,28,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm3
+  DB  196,227,249,22,208,1                ; vpextrq       $0x1,%xmm2,%rax
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,131,97,32,20,26,1               ; vpinsrb       $0x1,(%r10,%r11,1),%xmm3,%xmm2
+  DB  65,137,195                          ; mov           %eax,%r11d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  197,241,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm0
+  DB  196,131,105,32,12,26,2              ; vpinsrb       $0x2,(%r10,%r11,1),%xmm2,%xmm1
+  DB  196,193,249,126,195                 ; vmovq         %xmm0,%r11
+  DB  196,195,113,32,12,2,3               ; vpinsrb       $0x3,(%r10,%rax,1),%xmm1,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,195,121,32,20,2,0               ; vpinsrb       $0x0,(%r10,%rax,1),%xmm0,%xmm2
+  DB  196,227,249,22,195,1                ; vpextrq       $0x1,%xmm0,%rbx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,131,105,32,4,26,1               ; vpinsrb       $0x1,(%r10,%r11,1),%xmm2,%xmm0
+  DB  137,216                             ; mov           %ebx,%eax
+  DB  196,195,121,32,4,2,2                ; vpinsrb       $0x2,(%r10,%rax,1),%xmm0,%xmm0
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  196,226,121,49,201                  ; vpmovzxbd     %xmm1,%xmm1
+  DB  196,195,121,32,4,26,3               ; vpinsrb       $0x3,(%r10,%rbx,1),%xmm0,%xmm0
+  DB  196,226,121,49,192                  ; vpmovzxbd     %xmm0,%xmm0
+  DB  77,139,89,8                         ; mov           0x8(%r9),%r11
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  69,137,209                          ; mov           %r10d,%r9d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  196,129,121,110,20,139              ; vmovd         (%r11,%r9,4),%xmm2
+  DB  196,227,249,22,195,1                ; vpextrq       $0x1,%xmm0,%rbx
+  DB  196,131,105,34,4,19,1               ; vpinsrd       $0x1,(%r11,%r10,1),%xmm2,%xmm0
+  DB  137,216                             ; mov           %ebx,%eax
+  DB  196,195,121,34,4,131,2              ; vpinsrd       $0x2,(%r11,%rax,4),%xmm0,%xmm0
+  DB  196,225,249,126,200                 ; vmovq         %xmm1,%rax
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  196,67,121,34,4,27,3                ; vpinsrd       $0x3,(%r11,%rbx,1),%xmm0,%xmm8
+  DB  137,195                             ; mov           %eax,%ebx
+  DB  196,193,121,110,4,155               ; vmovd         (%r11,%rbx,4),%xmm0
+  DB  196,227,249,22,203,1                ; vpextrq       $0x1,%xmm1,%rbx
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  196,195,121,34,4,3,1                ; vpinsrd       $0x1,(%r11,%rax,1),%xmm0,%xmm0
+  DB  137,216                             ; mov           %ebx,%eax
+  DB  196,195,121,34,4,131,2              ; vpinsrd       $0x2,(%r11,%rax,4),%xmm0,%xmm0
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  196,195,121,34,28,27,3              ; vpinsrd       $0x3,(%r11,%rbx,1),%xmm0,%xmm3
+  DB  196,227,61,24,195,1                 ; vinsertf128   $0x1,%xmm3,%ymm8,%ymm0
+  DB  197,124,40,21,87,49,0,0             ; vmovaps       0x3157(%rip),%ymm10        # 6b40 <_sk_callback_avx+0x63c>
+  DB  196,193,124,84,194                  ; vandps        %ymm10,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,13,165,46,0,0         ; vbroadcastss  0x2ea5(%rip),%ymm9        # 68a0 <_sk_callback_avx+0x39c>
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  196,193,113,114,208,8               ; vpsrld        $0x8,%xmm8,%xmm1
+  DB  197,233,114,211,8                   ; vpsrld        $0x8,%xmm3,%xmm2
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,193,116,84,202                  ; vandps        %ymm10,%ymm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,201                  ; vmulps        %ymm9,%ymm1,%ymm1
+  DB  196,193,33,114,208,16               ; vpsrld        $0x10,%xmm8,%xmm11
+  DB  197,233,114,211,16                  ; vpsrld        $0x10,%xmm3,%xmm2
+  DB  196,227,37,24,210,1                 ; vinsertf128   $0x1,%xmm2,%ymm11,%ymm2
+  DB  196,193,108,84,210                  ; vandps        %ymm10,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,209                  ; vmulps        %ymm9,%ymm2,%ymm2
+  DB  196,193,57,114,208,24               ; vpsrld        $0x18,%xmm8,%xmm8
+  DB  197,225,114,211,24                  ; vpsrld        $0x18,%xmm3,%xmm3
+  DB  196,227,61,24,219,1                 ; vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,217                  ; vmulps        %ymm9,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_565_avx
+_sk_load_565_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,128,0,0,0                    ; jne           3aeb <_sk_load_565_avx+0x8e>
+  DB  196,193,122,111,4,83                ; vmovdqu       (%r11,%rdx,2),%xmm0
+  DB  197,241,239,201                     ; vpxor         %xmm1,%xmm1,%xmm1
+  DB  197,249,105,201                     ; vpunpckhwd    %xmm1,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,209,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm2
+  DB  196,226,125,24,5,23,46,0,0          ; vbroadcastss  0x2e17(%rip),%ymm0        # 68a4 <_sk_callback_avx+0x3a0>
+  DB  197,236,84,192                      ; vandps        %ymm0,%ymm2,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,10,46,0,0         ; vbroadcastss  0x2e0a(%rip),%ymm1        # 68a8 <_sk_callback_avx+0x3a4>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,24,13,1,46,0,0          ; vbroadcastss  0x2e01(%rip),%ymm1        # 68ac <_sk_callback_avx+0x3a8>
+  DB  197,236,84,201                      ; vandps        %ymm1,%ymm2,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,29,244,45,0,0        ; vbroadcastss  0x2df4(%rip),%ymm3        # 68b0 <_sk_callback_avx+0x3ac>
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  196,226,125,24,29,235,45,0,0        ; vbroadcastss  0x2deb(%rip),%ymm3        # 68b4 <_sk_callback_avx+0x3b0>
+  DB  197,236,84,211                      ; vandps        %ymm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,226,125,24,29,222,45,0,0        ; vbroadcastss  0x2dde(%rip),%ymm3        # 68b8 <_sk_callback_avx+0x3b4>
+  DB  197,236,89,211                      ; vmulps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,211,45,0,0        ; vbroadcastss  0x2dd3(%rip),%ymm3        # 68bc <_sk_callback_avx+0x3b8>
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,110,255,255,255              ; ja            3a71 <_sk_load_565_avx+0x14>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,74,0,0,0                  ; lea           0x4a(%rip),%r10        # 3b58 <_sk_load_565_avx+0xfb>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  196,193,121,196,68,83,12,6          ; vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,10,5          ; vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,8,4           ; vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,6,3           ; vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,4,2           ; vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,2,1           ; vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,4,83,0              ; vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  DB  233,26,255,255,255                  ; jmpq          3a71 <_sk_load_565_avx+0x14>
+  DB  144                                 ; nop
+  DB  243,255                             ; repz          (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  235,255                             ; jmp           3b5d <_sk_load_565_avx+0x100>
+  DB  255                                 ; (bad)
+  DB  255,227                             ; jmpq          *%rbx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  219,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,211                             ; callq         *%rbx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,203                             ; dec           %ebx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  191                                 ; .byte         0xbf
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_gather_565_avx
+_sk_gather_565_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  197,249,110,80,16                   ; vmovd         0x10(%rax),%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,226,105,64,217                  ; vpmulld       %xmm1,%xmm2,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,226,105,64,201                  ; vpmulld       %xmm1,%xmm2,%xmm1
+  DB  197,254,91,208                      ; vcvttps2dq    %ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  197,241,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm0
+  DB  197,225,254,202                     ; vpaddd        %xmm2,%xmm3,%xmm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,110,208                     ; vmovd         %eax,%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,233,196,200,1                   ; vpinsrw       $0x1,%eax,%xmm2,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,2                   ; vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,241,196,200,3                   ; vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,4                   ; vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,241,196,192,5                   ; vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,196,192,6                   ; vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,249,196,192,7                   ; vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  DB  197,241,239,201                     ; vpxor         %xmm1,%xmm1,%xmm1
+  DB  197,249,105,201                     ; vpunpckhwd    %xmm1,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,209,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm2
+  DB  196,226,125,24,5,122,44,0,0         ; vbroadcastss  0x2c7a(%rip),%ymm0        # 68c0 <_sk_callback_avx+0x3bc>
+  DB  197,236,84,192                      ; vandps        %ymm0,%ymm2,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,109,44,0,0        ; vbroadcastss  0x2c6d(%rip),%ymm1        # 68c4 <_sk_callback_avx+0x3c0>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,24,13,100,44,0,0        ; vbroadcastss  0x2c64(%rip),%ymm1        # 68c8 <_sk_callback_avx+0x3c4>
+  DB  197,236,84,201                      ; vandps        %ymm1,%ymm2,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,29,87,44,0,0         ; vbroadcastss  0x2c57(%rip),%ymm3        # 68cc <_sk_callback_avx+0x3c8>
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  196,226,125,24,29,78,44,0,0         ; vbroadcastss  0x2c4e(%rip),%ymm3        # 68d0 <_sk_callback_avx+0x3cc>
+  DB  197,236,84,211                      ; vandps        %ymm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,226,125,24,29,65,44,0,0         ; vbroadcastss  0x2c41(%rip),%ymm3        # 68d4 <_sk_callback_avx+0x3d0>
+  DB  197,236,89,211                      ; vmulps        %ymm3,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,54,44,0,0         ; vbroadcastss  0x2c36(%rip),%ymm3        # 68d8 <_sk_callback_avx+0x3d4>
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_565_avx
+_sk_store_565_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,98,125,24,5,42,44,0,0           ; vbroadcastss  0x2c2a(%rip),%ymm8        # 68dc <_sk_callback_avx+0x3d8>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,193,41,114,241,11               ; vpslld        $0xb,%xmm9,%xmm10
+  DB  196,67,125,25,201,1                 ; vextractf128  $0x1,%ymm9,%xmm9
+  DB  196,193,49,114,241,11               ; vpslld        $0xb,%xmm9,%xmm9
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,98,125,24,21,3,44,0,0           ; vbroadcastss  0x2c03(%rip),%ymm10        # 68e0 <_sk_callback_avx+0x3dc>
+  DB  196,65,116,89,210                   ; vmulps        %ymm10,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,33,114,242,5                ; vpslld        $0x5,%xmm10,%xmm11
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,242,5                ; vpslld        $0x5,%xmm10,%xmm10
+  DB  196,67,37,24,210,1                  ; vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  DB  196,65,45,86,201                    ; vorpd         %ymm9,%ymm10,%ymm9
+  DB  196,65,108,89,192                   ; vmulps        %ymm8,%ymm2,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,65,53,86,192                    ; vorpd         %ymm8,%ymm9,%ymm8
+  DB  196,67,125,25,193,1                 ; vextractf128  $0x1,%ymm8,%xmm9
+  DB  196,66,57,43,193                    ; vpackusdw     %xmm9,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3d2d <_sk_store_565_avx+0x89>
+  DB  196,65,122,127,4,83                 ; vmovdqu       %xmm8,(%r11,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            3d29 <_sk_store_565_avx+0x85>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,68,0,0,0                  ; lea           0x44(%rip),%r10        # 3d8c <_sk_store_565_avx+0xe8>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,67,121,21,68,83,12,6            ; vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,10,5            ; vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,8,4             ; vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,6,3             ; vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,4,2             ; vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,2,1             ; vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  DB  196,67,121,21,4,83,0                ; vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  DB  235,159                             ; jmp           3d29 <_sk_store_565_avx+0x85>
+  DB  102,144                             ; xchg          %ax,%ax
+  DB  245                                 ; cmc
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  237                                 ; in            (%dx),%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,229                             ; jmpq          *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  221,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,213                             ; callq         *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,205                             ; dec           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,197                             ; inc           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_4444_avx
+_sk_load_4444_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,152,0,0,0                    ; jne           3e4e <_sk_load_4444_avx+0xa6>
+  DB  196,193,122,111,4,83                ; vmovdqu       (%r11,%rdx,2),%xmm0
+  DB  197,241,239,201                     ; vpxor         %xmm1,%xmm1,%xmm1
+  DB  197,249,105,201                     ; vpunpckhwd    %xmm1,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,217,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm3
+  DB  196,226,125,24,5,12,43,0,0          ; vbroadcastss  0x2b0c(%rip),%ymm0        # 68e4 <_sk_callback_avx+0x3e0>
+  DB  197,228,84,192                      ; vandps        %ymm0,%ymm3,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,255,42,0,0        ; vbroadcastss  0x2aff(%rip),%ymm1        # 68e8 <_sk_callback_avx+0x3e4>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,24,13,246,42,0,0        ; vbroadcastss  0x2af6(%rip),%ymm1        # 68ec <_sk_callback_avx+0x3e8>
+  DB  197,228,84,201                      ; vandps        %ymm1,%ymm3,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,21,233,42,0,0        ; vbroadcastss  0x2ae9(%rip),%ymm2        # 68f0 <_sk_callback_avx+0x3ec>
+  DB  197,244,89,202                      ; vmulps        %ymm2,%ymm1,%ymm1
+  DB  196,226,125,24,21,224,42,0,0        ; vbroadcastss  0x2ae0(%rip),%ymm2        # 68f4 <_sk_callback_avx+0x3f0>
+  DB  197,228,84,210                      ; vandps        %ymm2,%ymm3,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,98,125,24,5,211,42,0,0          ; vbroadcastss  0x2ad3(%rip),%ymm8        # 68f8 <_sk_callback_avx+0x3f4>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,24,5,201,42,0,0          ; vbroadcastss  0x2ac9(%rip),%ymm8        # 68fc <_sk_callback_avx+0x3f8>
+  DB  196,193,100,84,216                  ; vandps        %ymm8,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,98,125,24,5,187,42,0,0          ; vbroadcastss  0x2abb(%rip),%ymm8        # 6900 <_sk_callback_avx+0x3fc>
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,86,255,255,255               ; ja            3dbc <_sk_load_4444_avx+0x14>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,75,0,0,0                  ; lea           0x4b(%rip),%r10        # 3ebc <_sk_load_4444_avx+0x114>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,249,239,192                     ; vpxor         %xmm0,%xmm0,%xmm0
+  DB  196,193,121,196,68,83,12,6          ; vpinsrw       $0x6,0xc(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,10,5          ; vpinsrw       $0x5,0xa(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,8,4           ; vpinsrw       $0x4,0x8(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,6,3           ; vpinsrw       $0x3,0x6(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,4,2           ; vpinsrw       $0x2,0x4(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,68,83,2,1           ; vpinsrw       $0x1,0x2(%r11,%rdx,2),%xmm0,%xmm0
+  DB  196,193,121,196,4,83,0              ; vpinsrw       $0x0,(%r11,%rdx,2),%xmm0,%xmm0
+  DB  233,2,255,255,255                   ; jmpq          3dbc <_sk_load_4444_avx+0x14>
+  DB  102,144                             ; xchg          %ax,%ax
+  DB  242,255                             ; repnz         (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  234                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,226                             ; jmpq          *%rdx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  218,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,210                             ; callq         *%rdx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,202                             ; dec           %edx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  190                                 ; .byte         0xbe
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_gather_4444_avx
+_sk_gather_4444_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  197,249,110,80,16                   ; vmovd         0x10(%rax),%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,226,105,64,217                  ; vpmulld       %xmm1,%xmm2,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,226,105,64,201                  ; vpmulld       %xmm1,%xmm2,%xmm1
+  DB  197,254,91,208                      ; vcvttps2dq    %ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  197,241,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm0
+  DB  197,225,254,202                     ; vpaddd        %xmm2,%xmm3,%xmm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,110,208                     ; vmovd         %eax,%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,233,196,200,1                   ; vpinsrw       $0x1,%eax,%xmm2,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,2                   ; vpinsrw       $0x2,%eax,%xmm1,%xmm1
+  DB  196,193,249,126,194                 ; vmovq         %xmm0,%r10
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,241,196,200,3                   ; vpinsrw       $0x3,%eax,%xmm1,%xmm1
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,241,196,200,4                   ; vpinsrw       $0x4,%eax,%xmm1,%xmm1
+  DB  196,195,249,22,195,1                ; vpextrq       $0x1,%xmm0,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  67,15,183,4,81                      ; movzwl        (%r9,%r10,2),%eax
+  DB  197,241,196,192,5                   ; vpinsrw       $0x5,%eax,%xmm1,%xmm0
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  197,249,196,192,6                   ; vpinsrw       $0x6,%eax,%xmm0,%xmm0
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  67,15,183,4,89                      ; movzwl        (%r9,%r11,2),%eax
+  DB  197,249,196,192,7                   ; vpinsrw       $0x7,%eax,%xmm0,%xmm0
+  DB  197,241,239,201                     ; vpxor         %xmm1,%xmm1,%xmm1
+  DB  197,249,105,201                     ; vpunpckhwd    %xmm1,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,217,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm3
+  DB  196,226,125,24,5,90,41,0,0          ; vbroadcastss  0x295a(%rip),%ymm0        # 6904 <_sk_callback_avx+0x400>
+  DB  197,228,84,192                      ; vandps        %ymm0,%ymm3,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,226,125,24,13,77,41,0,0         ; vbroadcastss  0x294d(%rip),%ymm1        # 6908 <_sk_callback_avx+0x404>
+  DB  197,252,89,193                      ; vmulps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,24,13,68,41,0,0         ; vbroadcastss  0x2944(%rip),%ymm1        # 690c <_sk_callback_avx+0x408>
+  DB  197,228,84,201                      ; vandps        %ymm1,%ymm3,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,226,125,24,21,55,41,0,0         ; vbroadcastss  0x2937(%rip),%ymm2        # 6910 <_sk_callback_avx+0x40c>
+  DB  197,244,89,202                      ; vmulps        %ymm2,%ymm1,%ymm1
+  DB  196,226,125,24,21,46,41,0,0         ; vbroadcastss  0x292e(%rip),%ymm2        # 6914 <_sk_callback_avx+0x410>
+  DB  197,228,84,210                      ; vandps        %ymm2,%ymm3,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,98,125,24,5,33,41,0,0           ; vbroadcastss  0x2921(%rip),%ymm8        # 6918 <_sk_callback_avx+0x414>
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  196,98,125,24,5,23,41,0,0           ; vbroadcastss  0x2917(%rip),%ymm8        # 691c <_sk_callback_avx+0x418>
+  DB  196,193,100,84,216                  ; vandps        %ymm8,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,98,125,24,5,9,41,0,0            ; vbroadcastss  0x2909(%rip),%ymm8        # 6920 <_sk_callback_avx+0x41c>
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_4444_avx
+_sk_store_4444_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  196,98,125,24,5,246,40,0,0          ; vbroadcastss  0x28f6(%rip),%ymm8        # 6924 <_sk_callback_avx+0x420>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,193,41,114,241,12               ; vpslld        $0xc,%xmm9,%xmm10
+  DB  196,67,125,25,201,1                 ; vextractf128  $0x1,%ymm9,%xmm9
+  DB  196,193,49,114,241,12               ; vpslld        $0xc,%xmm9,%xmm9
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,33,114,242,8                ; vpslld        $0x8,%xmm10,%xmm11
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,242,8                ; vpslld        $0x8,%xmm10,%xmm10
+  DB  196,67,37,24,210,1                  ; vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  DB  196,65,45,86,201                    ; vorpd         %ymm9,%ymm10,%ymm9
+  DB  196,65,108,89,208                   ; vmulps        %ymm8,%ymm2,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,33,114,242,4                ; vpslld        $0x4,%xmm10,%xmm11
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,242,4                ; vpslld        $0x4,%xmm10,%xmm10
+  DB  196,67,37,24,210,1                  ; vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,65,45,86,192                    ; vorpd         %ymm8,%ymm10,%ymm8
+  DB  196,65,53,86,192                    ; vorpd         %ymm8,%ymm9,%ymm8
+  DB  196,67,125,25,193,1                 ; vextractf128  $0x1,%ymm8,%xmm9
+  DB  196,66,57,43,193                    ; vpackusdw     %xmm9,%xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           40c7 <_sk_store_4444_avx+0xa7>
+  DB  196,65,122,127,4,83                 ; vmovdqu       %xmm8,(%r11,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            40c3 <_sk_store_4444_avx+0xa3>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,66,0,0,0                  ; lea           0x42(%rip),%r10        # 4124 <_sk_store_4444_avx+0x104>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,67,121,21,68,83,12,6            ; vpextrw       $0x6,%xmm8,0xc(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,10,5            ; vpextrw       $0x5,%xmm8,0xa(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,8,4             ; vpextrw       $0x4,%xmm8,0x8(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,6,3             ; vpextrw       $0x3,%xmm8,0x6(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,4,2             ; vpextrw       $0x2,%xmm8,0x4(%r11,%rdx,2)
+  DB  196,67,121,21,68,83,2,1             ; vpextrw       $0x1,%xmm8,0x2(%r11,%rdx,2)
+  DB  196,67,121,21,4,83,0                ; vpextrw       $0x0,%xmm8,(%r11,%rdx,2)
+  DB  235,159                             ; jmp           40c3 <_sk_store_4444_avx+0xa3>
+  DB  247,255                             ; idiv          %edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  239                                 ; out           %eax,(%dx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,231                             ; jmpq          *%rdi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  223,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,215                             ; callq         *%rdi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,207                             ; dec           %edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,199                             ; inc           %edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_8888_avx
+_sk_load_8888_avx LABEL PROC
+  DB  80                                  ; push          %rax
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,139,0,0,0                    ; jne           41e5 <_sk_load_8888_avx+0xa5>
+  DB  196,193,124,16,26                   ; vmovups       (%r10),%ymm3
+  DB  197,124,40,21,249,41,0,0            ; vmovaps       0x29f9(%rip),%ymm10        # 6b60 <_sk_callback_avx+0x65c>
+  DB  196,193,100,84,194                  ; vandps        %ymm10,%ymm3,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,5,175,39,0,0          ; vbroadcastss  0x27af(%rip),%ymm8        # 6928 <_sk_callback_avx+0x424>
+  DB  196,193,124,89,192                  ; vmulps        %ymm8,%ymm0,%ymm0
+  DB  197,241,114,211,8                   ; vpsrld        $0x8,%xmm3,%xmm1
+  DB  196,195,125,25,217,1                ; vextractf128  $0x1,%ymm3,%xmm9
+  DB  196,193,105,114,209,8               ; vpsrld        $0x8,%xmm9,%xmm2
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,193,116,84,202                  ; vandps        %ymm10,%ymm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,200                  ; vmulps        %ymm8,%ymm1,%ymm1
+  DB  197,161,114,211,16                  ; vpsrld        $0x10,%xmm3,%xmm11
+  DB  196,193,105,114,209,16              ; vpsrld        $0x10,%xmm9,%xmm2
+  DB  196,227,37,24,210,1                 ; vinsertf128   $0x1,%xmm2,%ymm11,%ymm2
+  DB  196,193,108,84,210                  ; vandps        %ymm10,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,208                  ; vmulps        %ymm8,%ymm2,%ymm2
+  DB  197,169,114,211,24                  ; vpsrld        $0x18,%xmm3,%xmm10
+  DB  196,193,97,114,209,24               ; vpsrld        $0x18,%xmm9,%xmm3
+  DB  196,227,45,24,219,1                 ; vinsertf128   $0x1,%xmm3,%ymm10,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,216                  ; vmulps        %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  65,89                               ; pop           %r9
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,225,249,110,192                 ; vmovq         %rax,%xmm0
+  DB  196,226,121,48,192                  ; vpmovzxbw     %xmm0,%xmm0
+  DB  196,226,121,0,13,179,40,0,0         ; vpshufb       0x28b3(%rip),%xmm0,%xmm1        # 6ac0 <_sk_callback_avx+0x5bc>
+  DB  196,226,121,33,201                  ; vpmovsxbd     %xmm1,%xmm1
+  DB  196,226,121,0,5,181,40,0,0          ; vpshufb       0x28b5(%rip),%xmm0,%xmm0        # 6ad0 <_sk_callback_avx+0x5cc>
+  DB  196,226,121,33,192                  ; vpmovsxbd     %xmm0,%xmm0
+  DB  196,227,117,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm1,%ymm0
+  DB  196,194,125,44,26                   ; vmaskmovps    (%r10),%ymm0,%ymm3
+  DB  233,47,255,255,255                  ; jmpq          415f <_sk_load_8888_avx+0x1f>
+
+PUBLIC _sk_gather_8888_avx
+_sk_gather_8888_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  197,249,110,80,16                   ; vmovd         0x10(%rax),%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,226,105,64,217                  ; vpmulld       %xmm1,%xmm2,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,226,105,64,201                  ; vpmulld       %xmm1,%xmm2,%xmm1
+  DB  197,254,91,208                      ; vcvttps2dq    %ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  197,241,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm0
+  DB  197,225,254,202                     ; vpaddd        %xmm2,%xmm3,%xmm1
+  DB  196,193,249,126,202                 ; vmovq         %xmm1,%r10
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,193,121,110,20,129              ; vmovd         (%r9,%rax,4),%xmm2
+  DB  196,195,249,22,203,1                ; vpextrq       $0x1,%xmm1,%r11
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,105,34,12,145,1             ; vpinsrd       $0x1,(%r9,%r10,4),%xmm2,%xmm1
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,195,113,34,12,129,2             ; vpinsrd       $0x2,(%r9,%rax,4),%xmm1,%xmm1
+  DB  196,225,249,126,192                 ; vmovq         %xmm0,%rax
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,3,113,34,4,153,3                ; vpinsrd       $0x3,(%r9,%r11,4),%xmm1,%xmm8
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,129,121,110,12,145              ; vmovd         (%r9,%r10,4),%xmm1
+  DB  196,195,249,22,194,1                ; vpextrq       $0x1,%xmm0,%r10
+  DB  196,195,113,34,4,129,1              ; vpinsrd       $0x1,(%r9,%rax,4),%xmm1,%xmm0
+  DB  68,137,208                          ; mov           %r10d,%eax
+  DB  196,195,121,34,4,129,2              ; vpinsrd       $0x2,(%r9,%rax,4),%xmm0,%xmm0
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  196,131,121,34,28,145,3             ; vpinsrd       $0x3,(%r9,%r10,4),%xmm0,%xmm3
+  DB  196,227,61,24,195,1                 ; vinsertf128   $0x1,%xmm3,%ymm8,%ymm0
+  DB  197,124,40,21,165,40,0,0            ; vmovaps       0x28a5(%rip),%ymm10        # 6b80 <_sk_callback_avx+0x67c>
+  DB  196,193,124,84,194                  ; vandps        %ymm10,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,13,63,38,0,0          ; vbroadcastss  0x263f(%rip),%ymm9        # 692c <_sk_callback_avx+0x428>
+  DB  196,193,124,89,193                  ; vmulps        %ymm9,%ymm0,%ymm0
+  DB  196,193,113,114,208,8               ; vpsrld        $0x8,%xmm8,%xmm1
+  DB  197,233,114,211,8                   ; vpsrld        $0x8,%xmm3,%xmm2
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,193,116,84,202                  ; vandps        %ymm10,%ymm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,201                  ; vmulps        %ymm9,%ymm1,%ymm1
+  DB  196,193,33,114,208,16               ; vpsrld        $0x10,%xmm8,%xmm11
+  DB  197,233,114,211,16                  ; vpsrld        $0x10,%xmm3,%xmm2
+  DB  196,227,37,24,210,1                 ; vinsertf128   $0x1,%xmm2,%ymm11,%ymm2
+  DB  196,193,108,84,210                  ; vandps        %ymm10,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,209                  ; vmulps        %ymm9,%ymm2,%ymm2
+  DB  196,193,57,114,208,24               ; vpsrld        $0x18,%xmm8,%xmm8
+  DB  197,225,114,211,24                  ; vpsrld        $0x18,%xmm3,%xmm3
+  DB  196,227,61,24,219,1                 ; vinsertf128   $0x1,%xmm3,%ymm8,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,217                  ; vmulps        %ymm9,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_8888_avx
+_sk_store_8888_avx LABEL PROC
+  DB  80                                  ; push          %rax
+  DB  73,137,201                          ; mov           %rcx,%r9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,3,16                             ; add           (%rax),%r10
+  DB  196,98,125,24,5,200,37,0,0          ; vbroadcastss  0x25c8(%rip),%ymm8        # 6930 <_sk_callback_avx+0x42c>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,33,114,242,8                ; vpslld        $0x8,%xmm10,%xmm11
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,242,8                ; vpslld        $0x8,%xmm10,%xmm10
+  DB  196,67,37,24,210,1                  ; vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  DB  196,65,45,86,201                    ; vorpd         %ymm9,%ymm10,%ymm9
+  DB  196,65,108,89,208                   ; vmulps        %ymm8,%ymm2,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,193,33,114,242,16               ; vpslld        $0x10,%xmm10,%xmm11
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,242,16               ; vpslld        $0x10,%xmm10,%xmm10
+  DB  196,67,37,24,210,1                  ; vinsertf128   $0x1,%xmm10,%ymm11,%ymm10
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,193,33,114,240,24               ; vpslld        $0x18,%xmm8,%xmm11
+  DB  196,67,125,25,192,1                 ; vextractf128  $0x1,%ymm8,%xmm8
+  DB  196,193,57,114,240,24               ; vpslld        $0x18,%xmm8,%xmm8
+  DB  196,67,37,24,192,1                  ; vinsertf128   $0x1,%xmm8,%ymm11,%ymm8
+  DB  196,65,45,86,192                    ; vorpd         %ymm8,%ymm10,%ymm8
+  DB  196,65,53,86,192                    ; vorpd         %ymm8,%ymm9,%ymm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,14                              ; jne           43fa <_sk_store_8888_avx+0xac>
+  DB  196,65,124,17,2                     ; vmovups       %ymm8,(%r10)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,201                          ; mov           %r9,%rcx
+  DB  65,89                               ; pop           %r9
+  DB  255,224                             ; jmpq          *%rax
+  DB  185,8,0,0,0                         ; mov           $0x8,%ecx
+  DB  68,41,193                           ; sub           %r8d,%ecx
+  DB  192,225,3                           ; shl           $0x3,%cl
+  DB  72,199,192,255,255,255,255          ; mov           $0xffffffffffffffff,%rax
+  DB  72,211,232                          ; shr           %cl,%rax
+  DB  196,97,249,110,200                  ; vmovq         %rax,%xmm9
+  DB  196,66,121,48,201                   ; vpmovzxbw     %xmm9,%xmm9
+  DB  196,98,49,0,21,190,38,0,0           ; vpshufb       0x26be(%rip),%xmm9,%xmm10        # 6ae0 <_sk_callback_avx+0x5dc>
+  DB  196,66,121,33,210                   ; vpmovsxbd     %xmm10,%xmm10
+  DB  196,98,49,0,13,192,38,0,0           ; vpshufb       0x26c0(%rip),%xmm9,%xmm9        # 6af0 <_sk_callback_avx+0x5ec>
+  DB  196,66,121,33,201                   ; vpmovsxbd     %xmm9,%xmm9
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,66,53,46,2                      ; vmaskmovps    %ymm8,%ymm9,(%r10)
+  DB  235,175                             ; jmp           43f1 <_sk_store_8888_avx+0xa3>
+
+PUBLIC _sk_load_f16_avx
+_sk_load_f16_avx LABEL PROC
+  DB  72,129,236,152,0,0,0                ; sub           $0x98,%rsp
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  197,252,17,124,36,96                ; vmovups       %ymm7,0x60(%rsp)
+  DB  197,252,17,116,36,64                ; vmovups       %ymm6,0x40(%rsp)
+  DB  197,252,17,108,36,32                ; vmovups       %ymm5,0x20(%rsp)
+  DB  197,254,127,36,36                   ; vmovdqu       %ymm4,(%rsp)
+  DB  15,133,143,2,0,0                    ; jne           46fd <_sk_load_f16_avx+0x2bb>
+  DB  197,121,16,4,208                    ; vmovupd       (%rax,%rdx,8),%xmm8
+  DB  197,249,16,84,208,16                ; vmovupd       0x10(%rax,%rdx,8),%xmm2
+  DB  197,249,16,76,208,32                ; vmovupd       0x20(%rax,%rdx,8),%xmm1
+  DB  197,122,111,76,208,48               ; vmovdqu       0x30(%rax,%rdx,8),%xmm9
+  DB  197,185,97,194                      ; vpunpcklwd    %xmm2,%xmm8,%xmm0
+  DB  197,185,105,210                     ; vpunpckhwd    %xmm2,%xmm8,%xmm2
+  DB  196,193,113,97,217                  ; vpunpcklwd    %xmm9,%xmm1,%xmm3
+  DB  196,193,113,105,201                 ; vpunpckhwd    %xmm9,%xmm1,%xmm1
+  DB  197,121,97,250                      ; vpunpcklwd    %xmm2,%xmm0,%xmm15
+  DB  197,121,105,194                     ; vpunpckhwd    %xmm2,%xmm0,%xmm8
+  DB  197,225,97,209                      ; vpunpcklwd    %xmm1,%xmm3,%xmm2
+  DB  197,97,105,201                      ; vpunpckhwd    %xmm1,%xmm3,%xmm9
+  DB  197,129,108,194                     ; vpunpcklqdq   %xmm2,%xmm15,%xmm0
+  DB  197,241,239,201                     ; vpxor         %xmm1,%xmm1,%xmm1
+  DB  197,249,105,201                     ; vpunpckhwd    %xmm1,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  196,98,125,24,37,109,36,0,0         ; vbroadcastss  0x246d(%rip),%ymm12        # 6934 <_sk_callback_avx+0x430>
+  DB  196,193,124,84,204                  ; vandps        %ymm12,%ymm0,%ymm1
+  DB  197,252,87,193                      ; vxorps        %ymm1,%ymm0,%ymm0
+  DB  196,195,125,25,198,1                ; vextractf128  $0x1,%ymm0,%xmm14
+  DB  196,98,121,24,29,89,36,0,0          ; vbroadcastss  0x2459(%rip),%xmm11        # 6938 <_sk_callback_avx+0x434>
+  DB  196,193,8,87,219                    ; vxorps        %xmm11,%xmm14,%xmm3
+  DB  196,98,121,24,45,79,36,0,0          ; vbroadcastss  0x244f(%rip),%xmm13        # 693c <_sk_callback_avx+0x438>
+  DB  197,145,102,219                     ; vpcmpgtd      %xmm3,%xmm13,%xmm3
+  DB  196,65,120,87,211                   ; vxorps        %xmm11,%xmm0,%xmm10
+  DB  196,65,17,102,210                   ; vpcmpgtd      %xmm10,%xmm13,%xmm10
+  DB  196,99,45,24,211,1                  ; vinsertf128   $0x1,%xmm3,%ymm10,%ymm10
+  DB  197,225,114,241,16                  ; vpslld        $0x10,%xmm1,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  197,241,114,241,16                  ; vpslld        $0x10,%xmm1,%xmm1
+  DB  196,227,101,24,201,1                ; vinsertf128   $0x1,%xmm1,%ymm3,%ymm1
+  DB  197,249,114,240,13                  ; vpslld        $0xd,%xmm0,%xmm0
+  DB  196,193,97,114,246,13               ; vpslld        $0xd,%xmm14,%xmm3
+  DB  196,227,125,24,195,1                ; vinsertf128   $0x1,%xmm3,%ymm0,%ymm0
+  DB  197,252,86,193                      ; vorps         %ymm1,%ymm0,%ymm0
+  DB  196,227,125,25,193,1                ; vextractf128  $0x1,%ymm0,%xmm1
+  DB  196,226,121,24,29,5,36,0,0          ; vbroadcastss  0x2405(%rip),%xmm3        # 6940 <_sk_callback_avx+0x43c>
+  DB  197,241,254,203                     ; vpaddd        %xmm3,%xmm1,%xmm1
+  DB  197,249,254,195                     ; vpaddd        %xmm3,%xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  196,65,12,87,246                    ; vxorps        %ymm14,%ymm14,%ymm14
+  DB  196,195,125,74,198,160              ; vblendvps     %ymm10,%ymm14,%ymm0,%ymm0
+  DB  197,129,109,202                     ; vpunpckhqdq   %xmm2,%xmm15,%xmm1
+  DB  197,217,239,228                     ; vpxor         %xmm4,%xmm4,%xmm4
+  DB  197,241,105,212                     ; vpunpckhwd    %xmm4,%xmm1,%xmm2
+  DB  196,226,121,51,201                  ; vpmovzxwd     %xmm1,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,193,116,84,212                  ; vandps        %ymm12,%ymm1,%ymm2
+  DB  197,244,87,202                      ; vxorps        %ymm2,%ymm1,%ymm1
+  DB  196,195,125,25,202,1                ; vextractf128  $0x1,%ymm1,%xmm10
+  DB  196,193,40,87,251                   ; vxorps        %xmm11,%xmm10,%xmm7
+  DB  197,145,102,255                     ; vpcmpgtd      %xmm7,%xmm13,%xmm7
+  DB  196,193,112,87,243                  ; vxorps        %xmm11,%xmm1,%xmm6
+  DB  197,145,102,246                     ; vpcmpgtd      %xmm6,%xmm13,%xmm6
+  DB  196,227,77,24,247,1                 ; vinsertf128   $0x1,%xmm7,%ymm6,%ymm6
+  DB  197,193,114,242,16                  ; vpslld        $0x10,%xmm2,%xmm7
+  DB  196,227,125,25,210,1                ; vextractf128  $0x1,%ymm2,%xmm2
+  DB  197,233,114,242,16                  ; vpslld        $0x10,%xmm2,%xmm2
+  DB  196,227,69,24,210,1                 ; vinsertf128   $0x1,%xmm2,%ymm7,%ymm2
+  DB  197,241,114,241,13                  ; vpslld        $0xd,%xmm1,%xmm1
+  DB  196,193,65,114,242,13               ; vpslld        $0xd,%xmm10,%xmm7
+  DB  196,227,117,24,207,1                ; vinsertf128   $0x1,%xmm7,%ymm1,%ymm1
+  DB  197,244,86,202                      ; vorps         %ymm2,%ymm1,%ymm1
+  DB  196,227,125,25,202,1                ; vextractf128  $0x1,%ymm1,%xmm2
+  DB  197,233,254,211                     ; vpaddd        %xmm3,%xmm2,%xmm2
+  DB  197,241,254,203                     ; vpaddd        %xmm3,%xmm1,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,195,117,74,206,96               ; vblendvps     %ymm6,%ymm14,%ymm1,%ymm1
+  DB  196,193,57,108,209                  ; vpunpcklqdq   %xmm9,%xmm8,%xmm2
+  DB  197,233,105,244                     ; vpunpckhwd    %xmm4,%xmm2,%xmm6
+  DB  196,65,41,239,210                   ; vpxor         %xmm10,%xmm10,%xmm10
+  DB  196,226,121,51,210                  ; vpmovzxwd     %xmm2,%xmm2
+  DB  196,227,109,24,214,1                ; vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  DB  196,193,108,84,244                  ; vandps        %ymm12,%ymm2,%ymm6
+  DB  197,236,87,214                      ; vxorps        %ymm6,%ymm2,%ymm2
+  DB  196,227,125,25,215,1                ; vextractf128  $0x1,%ymm2,%xmm7
+  DB  196,193,64,87,235                   ; vxorps        %xmm11,%xmm7,%xmm5
+  DB  197,145,102,237                     ; vpcmpgtd      %xmm5,%xmm13,%xmm5
+  DB  196,193,104,87,227                  ; vxorps        %xmm11,%xmm2,%xmm4
+  DB  197,145,102,228                     ; vpcmpgtd      %xmm4,%xmm13,%xmm4
+  DB  196,227,93,24,229,1                 ; vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  DB  197,209,114,246,16                  ; vpslld        $0x10,%xmm6,%xmm5
+  DB  196,227,125,25,246,1                ; vextractf128  $0x1,%ymm6,%xmm6
+  DB  197,201,114,246,16                  ; vpslld        $0x10,%xmm6,%xmm6
+  DB  196,227,85,24,238,1                 ; vinsertf128   $0x1,%xmm6,%ymm5,%ymm5
+  DB  197,233,114,242,13                  ; vpslld        $0xd,%xmm2,%xmm2
+  DB  197,201,114,247,13                  ; vpslld        $0xd,%xmm7,%xmm6
+  DB  196,227,109,24,214,1                ; vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  DB  197,236,86,213                      ; vorps         %ymm5,%ymm2,%ymm2
+  DB  196,227,125,25,213,1                ; vextractf128  $0x1,%ymm2,%xmm5
+  DB  197,209,254,235                     ; vpaddd        %xmm3,%xmm5,%xmm5
+  DB  197,233,254,211                     ; vpaddd        %xmm3,%xmm2,%xmm2
+  DB  196,227,109,24,213,1                ; vinsertf128   $0x1,%xmm5,%ymm2,%ymm2
+  DB  196,195,109,74,214,64               ; vblendvps     %ymm4,%ymm14,%ymm2,%ymm2
+  DB  196,193,57,109,225                  ; vpunpckhqdq   %xmm9,%xmm8,%xmm4
+  DB  196,193,89,105,234                  ; vpunpckhwd    %xmm10,%xmm4,%xmm5
+  DB  196,226,121,51,228                  ; vpmovzxwd     %xmm4,%xmm4
+  DB  196,227,93,24,229,1                 ; vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  DB  196,193,92,84,236                   ; vandps        %ymm12,%ymm4,%ymm5
+  DB  197,220,87,229                      ; vxorps        %ymm5,%ymm4,%ymm4
+  DB  196,227,125,25,230,1                ; vextractf128  $0x1,%ymm4,%xmm6
+  DB  196,193,72,87,251                   ; vxorps        %xmm11,%xmm6,%xmm7
+  DB  197,17,102,199                      ; vpcmpgtd      %xmm7,%xmm13,%xmm8
+  DB  196,193,88,87,251                   ; vxorps        %xmm11,%xmm4,%xmm7
+  DB  197,145,102,255                     ; vpcmpgtd      %xmm7,%xmm13,%xmm7
+  DB  196,195,69,24,248,1                 ; vinsertf128   $0x1,%xmm8,%ymm7,%ymm7
+  DB  197,185,114,245,16                  ; vpslld        $0x10,%xmm5,%xmm8
+  DB  196,227,125,25,237,1                ; vextractf128  $0x1,%ymm5,%xmm5
+  DB  197,209,114,245,16                  ; vpslld        $0x10,%xmm5,%xmm5
+  DB  196,227,61,24,237,1                 ; vinsertf128   $0x1,%xmm5,%ymm8,%ymm5
+  DB  197,217,114,244,13                  ; vpslld        $0xd,%xmm4,%xmm4
+  DB  197,201,114,246,13                  ; vpslld        $0xd,%xmm6,%xmm6
+  DB  196,227,93,24,230,1                 ; vinsertf128   $0x1,%xmm6,%ymm4,%ymm4
+  DB  197,220,86,229                      ; vorps         %ymm5,%ymm4,%ymm4
+  DB  196,227,125,25,229,1                ; vextractf128  $0x1,%ymm4,%xmm5
+  DB  197,209,254,235                     ; vpaddd        %xmm3,%xmm5,%xmm5
+  DB  197,217,254,219                     ; vpaddd        %xmm3,%xmm4,%xmm3
+  DB  196,227,101,24,221,1                ; vinsertf128   $0x1,%xmm5,%ymm3,%ymm3
+  DB  196,195,101,74,222,112              ; vblendvps     %ymm7,%ymm14,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,36,36                    ; vmovups       (%rsp),%ymm4
+  DB  197,252,16,108,36,32                ; vmovups       0x20(%rsp),%ymm5
+  DB  197,252,16,116,36,64                ; vmovups       0x40(%rsp),%ymm6
+  DB  197,252,16,124,36,96                ; vmovups       0x60(%rsp),%ymm7
+  DB  72,129,196,152,0,0,0                ; add           $0x98,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,123,16,4,208                    ; vmovsd        (%rax,%rdx,8),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,79                              ; je            475c <_sk_load_f16_avx+0x31a>
+  DB  197,57,22,68,208,8                  ; vmovhpd       0x8(%rax,%rdx,8),%xmm8,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,67                              ; jb            475c <_sk_load_f16_avx+0x31a>
+  DB  197,251,16,84,208,16                ; vmovsd        0x10(%rax,%rdx,8),%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  116,68                              ; je            4769 <_sk_load_f16_avx+0x327>
+  DB  197,233,22,84,208,24                ; vmovhpd       0x18(%rax,%rdx,8),%xmm2,%xmm2
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,56                              ; jb            4769 <_sk_load_f16_avx+0x327>
+  DB  197,251,16,76,208,32                ; vmovsd        0x20(%rax,%rdx,8),%xmm1
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  15,132,68,253,255,255               ; je            4485 <_sk_load_f16_avx+0x43>
+  DB  197,241,22,76,208,40                ; vmovhpd       0x28(%rax,%rdx,8),%xmm1,%xmm1
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  15,130,52,253,255,255               ; jb            4485 <_sk_load_f16_avx+0x43>
+  DB  197,122,126,76,208,48               ; vmovq         0x30(%rax,%rdx,8),%xmm9
+  DB  233,41,253,255,255                  ; jmpq          4485 <_sk_load_f16_avx+0x43>
+  DB  197,241,87,201                      ; vxorpd        %xmm1,%xmm1,%xmm1
+  DB  197,233,87,210                      ; vxorpd        %xmm2,%xmm2,%xmm2
+  DB  233,28,253,255,255                  ; jmpq          4485 <_sk_load_f16_avx+0x43>
+  DB  197,241,87,201                      ; vxorpd        %xmm1,%xmm1,%xmm1
+  DB  233,19,253,255,255                  ; jmpq          4485 <_sk_load_f16_avx+0x43>
+
+PUBLIC _sk_gather_f16_avx
+_sk_gather_f16_avx LABEL PROC
+  DB  72,129,236,152,0,0,0                ; sub           $0x98,%rsp
+  DB  197,252,17,124,36,96                ; vmovups       %ymm7,0x60(%rsp)
+  DB  197,252,17,116,36,64                ; vmovups       %ymm6,0x40(%rsp)
+  DB  197,252,17,108,36,32                ; vmovups       %ymm5,0x20(%rsp)
+  DB  197,254,127,36,36                   ; vmovdqu       %ymm4,(%rsp)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  197,249,110,80,16                   ; vmovd         0x10(%rax),%xmm2
+  DB  197,249,112,210,0                   ; vpshufd       $0x0,%xmm2,%xmm2
+  DB  196,226,105,64,217                  ; vpmulld       %xmm1,%xmm2,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,226,105,64,201                  ; vpmulld       %xmm1,%xmm2,%xmm1
+  DB  197,254,91,208                      ; vcvttps2dq    %ymm0,%ymm2
+  DB  196,227,125,25,208,1                ; vextractf128  $0x1,%ymm2,%xmm0
+  DB  197,113,254,192                     ; vpaddd        %xmm0,%xmm1,%xmm8
+  DB  197,225,254,202                     ; vpaddd        %xmm2,%xmm3,%xmm1
+  DB  196,225,249,126,200                 ; vmovq         %xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,65,122,126,12,193               ; vmovq         (%r9,%rax,8),%xmm9
+  DB  196,227,249,22,200,1                ; vpextrq       $0x1,%xmm1,%rax
+  DB  196,1,122,126,20,209                ; vmovq         (%r9,%r10,8),%xmm10
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,65,122,126,28,193               ; vmovq         (%r9,%rax,8),%xmm11
+  DB  196,97,249,126,192                  ; vmovq         %xmm8,%rax
+  DB  196,1,122,126,36,209                ; vmovq         (%r9,%r10,8),%xmm12
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,65,122,126,44,193               ; vmovq         (%r9,%rax,8),%xmm13
+  DB  196,67,249,22,195,1                 ; vpextrq       $0x1,%xmm8,%r11
+  DB  196,1,122,126,4,209                 ; vmovq         (%r9,%r10,8),%xmm8
+  DB  76,137,216                          ; mov           %r11,%rax
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  196,65,122,126,52,193               ; vmovq         (%r9,%rax,8),%xmm14
+  DB  68,137,216                          ; mov           %r11d,%eax
+  DB  196,193,122,126,4,193               ; vmovq         (%r9,%rax,8),%xmm0
+  DB  196,193,41,108,209                  ; vpunpcklqdq   %xmm9,%xmm10,%xmm2
+  DB  196,193,25,108,203                  ; vpunpcklqdq   %xmm11,%xmm12,%xmm1
+  DB  196,193,57,108,221                  ; vpunpcklqdq   %xmm13,%xmm8,%xmm3
+  DB  196,193,121,108,198                 ; vpunpcklqdq   %xmm14,%xmm0,%xmm0
+  DB  197,105,97,193                      ; vpunpcklwd    %xmm1,%xmm2,%xmm8
+  DB  197,233,105,209                     ; vpunpckhwd    %xmm1,%xmm2,%xmm2
+  DB  197,225,97,200                      ; vpunpcklwd    %xmm0,%xmm3,%xmm1
+  DB  197,225,105,192                     ; vpunpckhwd    %xmm0,%xmm3,%xmm0
+  DB  197,57,97,250                       ; vpunpcklwd    %xmm2,%xmm8,%xmm15
+  DB  197,57,105,194                      ; vpunpckhwd    %xmm2,%xmm8,%xmm8
+  DB  197,241,97,208                      ; vpunpcklwd    %xmm0,%xmm1,%xmm2
+  DB  197,113,105,200                     ; vpunpckhwd    %xmm0,%xmm1,%xmm9
+  DB  197,129,108,194                     ; vpunpcklqdq   %xmm2,%xmm15,%xmm0
+  DB  197,241,239,201                     ; vpxor         %xmm1,%xmm1,%xmm1
+  DB  197,249,105,201                     ; vpunpckhwd    %xmm1,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  196,98,125,24,37,198,32,0,0         ; vbroadcastss  0x20c6(%rip),%ymm12        # 6944 <_sk_callback_avx+0x440>
+  DB  196,193,124,84,204                  ; vandps        %ymm12,%ymm0,%ymm1
+  DB  197,252,87,193                      ; vxorps        %ymm1,%ymm0,%ymm0
+  DB  196,195,125,25,198,1                ; vextractf128  $0x1,%ymm0,%xmm14
+  DB  196,98,121,24,29,178,32,0,0         ; vbroadcastss  0x20b2(%rip),%xmm11        # 6948 <_sk_callback_avx+0x444>
+  DB  196,193,8,87,219                    ; vxorps        %xmm11,%xmm14,%xmm3
+  DB  196,98,121,24,45,168,32,0,0         ; vbroadcastss  0x20a8(%rip),%xmm13        # 694c <_sk_callback_avx+0x448>
+  DB  197,145,102,219                     ; vpcmpgtd      %xmm3,%xmm13,%xmm3
+  DB  196,65,120,87,211                   ; vxorps        %xmm11,%xmm0,%xmm10
+  DB  196,65,17,102,210                   ; vpcmpgtd      %xmm10,%xmm13,%xmm10
+  DB  196,99,45,24,211,1                  ; vinsertf128   $0x1,%xmm3,%ymm10,%ymm10
+  DB  197,225,114,241,16                  ; vpslld        $0x10,%xmm1,%xmm3
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  197,241,114,241,16                  ; vpslld        $0x10,%xmm1,%xmm1
+  DB  196,227,101,24,201,1                ; vinsertf128   $0x1,%xmm1,%ymm3,%ymm1
+  DB  197,249,114,240,13                  ; vpslld        $0xd,%xmm0,%xmm0
+  DB  196,193,97,114,246,13               ; vpslld        $0xd,%xmm14,%xmm3
+  DB  196,227,125,24,195,1                ; vinsertf128   $0x1,%xmm3,%ymm0,%ymm0
+  DB  197,252,86,193                      ; vorps         %ymm1,%ymm0,%ymm0
+  DB  196,227,125,25,193,1                ; vextractf128  $0x1,%ymm0,%xmm1
+  DB  196,226,121,24,29,94,32,0,0         ; vbroadcastss  0x205e(%rip),%xmm3        # 6950 <_sk_callback_avx+0x44c>
+  DB  197,241,254,203                     ; vpaddd        %xmm3,%xmm1,%xmm1
+  DB  197,249,254,195                     ; vpaddd        %xmm3,%xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  196,65,12,87,246                    ; vxorps        %ymm14,%ymm14,%ymm14
+  DB  196,195,125,74,198,160              ; vblendvps     %ymm10,%ymm14,%ymm0,%ymm0
+  DB  197,129,109,202                     ; vpunpckhqdq   %xmm2,%xmm15,%xmm1
+  DB  197,217,239,228                     ; vpxor         %xmm4,%xmm4,%xmm4
+  DB  197,241,105,212                     ; vpunpckhwd    %xmm4,%xmm1,%xmm2
+  DB  196,226,121,51,201                  ; vpmovzxwd     %xmm1,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,193,116,84,212                  ; vandps        %ymm12,%ymm1,%ymm2
+  DB  197,244,87,202                      ; vxorps        %ymm2,%ymm1,%ymm1
+  DB  196,195,125,25,202,1                ; vextractf128  $0x1,%ymm1,%xmm10
+  DB  196,193,40,87,251                   ; vxorps        %xmm11,%xmm10,%xmm7
+  DB  197,145,102,255                     ; vpcmpgtd      %xmm7,%xmm13,%xmm7
+  DB  196,193,112,87,243                  ; vxorps        %xmm11,%xmm1,%xmm6
+  DB  197,145,102,246                     ; vpcmpgtd      %xmm6,%xmm13,%xmm6
+  DB  196,227,77,24,247,1                 ; vinsertf128   $0x1,%xmm7,%ymm6,%ymm6
+  DB  197,193,114,242,16                  ; vpslld        $0x10,%xmm2,%xmm7
+  DB  196,227,125,25,210,1                ; vextractf128  $0x1,%ymm2,%xmm2
+  DB  197,233,114,242,16                  ; vpslld        $0x10,%xmm2,%xmm2
+  DB  196,227,69,24,210,1                 ; vinsertf128   $0x1,%xmm2,%ymm7,%ymm2
+  DB  197,241,114,241,13                  ; vpslld        $0xd,%xmm1,%xmm1
+  DB  196,193,65,114,242,13               ; vpslld        $0xd,%xmm10,%xmm7
+  DB  196,227,117,24,207,1                ; vinsertf128   $0x1,%xmm7,%ymm1,%ymm1
+  DB  197,244,86,202                      ; vorps         %ymm2,%ymm1,%ymm1
+  DB  196,227,125,25,202,1                ; vextractf128  $0x1,%ymm1,%xmm2
+  DB  197,233,254,211                     ; vpaddd        %xmm3,%xmm2,%xmm2
+  DB  197,241,254,203                     ; vpaddd        %xmm3,%xmm1,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  196,195,117,74,206,96               ; vblendvps     %ymm6,%ymm14,%ymm1,%ymm1
+  DB  196,193,57,108,209                  ; vpunpcklqdq   %xmm9,%xmm8,%xmm2
+  DB  197,233,105,244                     ; vpunpckhwd    %xmm4,%xmm2,%xmm6
+  DB  196,65,41,239,210                   ; vpxor         %xmm10,%xmm10,%xmm10
+  DB  196,226,121,51,210                  ; vpmovzxwd     %xmm2,%xmm2
+  DB  196,227,109,24,214,1                ; vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  DB  196,193,108,84,244                  ; vandps        %ymm12,%ymm2,%ymm6
+  DB  197,236,87,214                      ; vxorps        %ymm6,%ymm2,%ymm2
+  DB  196,227,125,25,215,1                ; vextractf128  $0x1,%ymm2,%xmm7
+  DB  196,193,64,87,235                   ; vxorps        %xmm11,%xmm7,%xmm5
+  DB  197,145,102,237                     ; vpcmpgtd      %xmm5,%xmm13,%xmm5
+  DB  196,193,104,87,227                  ; vxorps        %xmm11,%xmm2,%xmm4
+  DB  197,145,102,228                     ; vpcmpgtd      %xmm4,%xmm13,%xmm4
+  DB  196,227,93,24,229,1                 ; vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  DB  197,209,114,246,16                  ; vpslld        $0x10,%xmm6,%xmm5
+  DB  196,227,125,25,246,1                ; vextractf128  $0x1,%ymm6,%xmm6
+  DB  197,201,114,246,16                  ; vpslld        $0x10,%xmm6,%xmm6
+  DB  196,227,85,24,238,1                 ; vinsertf128   $0x1,%xmm6,%ymm5,%ymm5
+  DB  197,233,114,242,13                  ; vpslld        $0xd,%xmm2,%xmm2
+  DB  197,201,114,247,13                  ; vpslld        $0xd,%xmm7,%xmm6
+  DB  196,227,109,24,214,1                ; vinsertf128   $0x1,%xmm6,%ymm2,%ymm2
+  DB  197,236,86,213                      ; vorps         %ymm5,%ymm2,%ymm2
+  DB  196,227,125,25,213,1                ; vextractf128  $0x1,%ymm2,%xmm5
+  DB  197,209,254,235                     ; vpaddd        %xmm3,%xmm5,%xmm5
+  DB  197,233,254,211                     ; vpaddd        %xmm3,%xmm2,%xmm2
+  DB  196,227,109,24,213,1                ; vinsertf128   $0x1,%xmm5,%ymm2,%ymm2
+  DB  196,195,109,74,214,64               ; vblendvps     %ymm4,%ymm14,%ymm2,%ymm2
+  DB  196,193,57,109,225                  ; vpunpckhqdq   %xmm9,%xmm8,%xmm4
+  DB  196,193,89,105,234                  ; vpunpckhwd    %xmm10,%xmm4,%xmm5
+  DB  196,226,121,51,228                  ; vpmovzxwd     %xmm4,%xmm4
+  DB  196,227,93,24,229,1                 ; vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  DB  196,193,92,84,236                   ; vandps        %ymm12,%ymm4,%ymm5
+  DB  197,220,87,229                      ; vxorps        %ymm5,%ymm4,%ymm4
+  DB  196,227,125,25,230,1                ; vextractf128  $0x1,%ymm4,%xmm6
+  DB  196,193,72,87,251                   ; vxorps        %xmm11,%xmm6,%xmm7
+  DB  197,17,102,199                      ; vpcmpgtd      %xmm7,%xmm13,%xmm8
+  DB  196,193,88,87,251                   ; vxorps        %xmm11,%xmm4,%xmm7
+  DB  197,145,102,255                     ; vpcmpgtd      %xmm7,%xmm13,%xmm7
+  DB  196,195,69,24,248,1                 ; vinsertf128   $0x1,%xmm8,%ymm7,%ymm7
+  DB  197,185,114,245,16                  ; vpslld        $0x10,%xmm5,%xmm8
+  DB  196,227,125,25,237,1                ; vextractf128  $0x1,%ymm5,%xmm5
+  DB  197,209,114,245,16                  ; vpslld        $0x10,%xmm5,%xmm5
+  DB  196,227,61,24,237,1                 ; vinsertf128   $0x1,%xmm5,%ymm8,%ymm5
+  DB  197,217,114,244,13                  ; vpslld        $0xd,%xmm4,%xmm4
+  DB  197,201,114,246,13                  ; vpslld        $0xd,%xmm6,%xmm6
+  DB  196,227,93,24,230,1                 ; vinsertf128   $0x1,%xmm6,%ymm4,%ymm4
+  DB  197,220,86,229                      ; vorps         %ymm5,%ymm4,%ymm4
+  DB  196,227,125,25,229,1                ; vextractf128  $0x1,%ymm4,%xmm5
+  DB  197,209,254,235                     ; vpaddd        %xmm3,%xmm5,%xmm5
+  DB  197,217,254,219                     ; vpaddd        %xmm3,%xmm4,%xmm3
+  DB  196,227,101,24,221,1                ; vinsertf128   $0x1,%xmm5,%ymm3,%ymm3
+  DB  196,195,101,74,222,112              ; vblendvps     %ymm7,%ymm14,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,36,36                    ; vmovups       (%rsp),%ymm4
+  DB  197,252,16,108,36,32                ; vmovups       0x20(%rsp),%ymm5
+  DB  197,252,16,116,36,64                ; vmovups       0x40(%rsp),%ymm6
+  DB  197,252,16,124,36,96                ; vmovups       0x60(%rsp),%ymm7
+  DB  72,129,196,152,0,0,0                ; add           $0x98,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_f16_avx
+_sk_store_f16_avx LABEL PROC
+  DB  72,129,236,216,0,0,0                ; sub           $0xd8,%rsp
+  DB  197,252,17,188,36,160,0,0,0         ; vmovups       %ymm7,0xa0(%rsp)
+  DB  197,252,17,180,36,128,0,0,0         ; vmovups       %ymm6,0x80(%rsp)
+  DB  197,252,17,108,36,96                ; vmovups       %ymm5,0x60(%rsp)
+  DB  197,252,17,100,36,64                ; vmovups       %ymm4,0x40(%rsp)
+  DB  196,98,125,24,13,114,30,0,0         ; vbroadcastss  0x1e72(%rip),%ymm9        # 6954 <_sk_callback_avx+0x450>
+  DB  196,65,124,84,209                   ; vandps        %ymm9,%ymm0,%ymm10
+  DB  197,252,17,4,36                     ; vmovups       %ymm0,(%rsp)
+  DB  196,65,124,87,218                   ; vxorps        %ymm10,%ymm0,%ymm11
+  DB  196,67,125,25,220,1                 ; vextractf128  $0x1,%ymm11,%xmm12
+  DB  196,98,121,24,5,88,30,0,0           ; vbroadcastss  0x1e58(%rip),%xmm8        # 6958 <_sk_callback_avx+0x454>
+  DB  196,65,57,102,236                   ; vpcmpgtd      %xmm12,%xmm8,%xmm13
+  DB  196,65,57,102,243                   ; vpcmpgtd      %xmm11,%xmm8,%xmm14
+  DB  196,67,13,24,237,1                  ; vinsertf128   $0x1,%xmm13,%ymm14,%ymm13
+  DB  196,193,9,114,210,16                ; vpsrld        $0x10,%xmm10,%xmm14
+  DB  196,67,125,25,210,1                 ; vextractf128  $0x1,%ymm10,%xmm10
+  DB  196,193,41,114,210,16               ; vpsrld        $0x10,%xmm10,%xmm10
+  DB  196,67,13,24,242,1                  ; vinsertf128   $0x1,%xmm10,%ymm14,%ymm14
+  DB  196,193,33,114,211,13               ; vpsrld        $0xd,%xmm11,%xmm11
+  DB  196,193,25,114,212,13               ; vpsrld        $0xd,%xmm12,%xmm12
+  DB  196,98,125,24,21,31,30,0,0          ; vbroadcastss  0x1e1f(%rip),%ymm10        # 695c <_sk_callback_avx+0x458>
+  DB  196,65,12,86,242                    ; vorps         %ymm10,%ymm14,%ymm14
+  DB  196,67,125,25,247,1                 ; vextractf128  $0x1,%ymm14,%xmm15
+  DB  196,65,1,254,228                    ; vpaddd        %xmm12,%xmm15,%xmm12
+  DB  196,65,9,254,219                    ; vpaddd        %xmm11,%xmm14,%xmm11
+  DB  196,67,37,24,228,1                  ; vinsertf128   $0x1,%xmm12,%ymm11,%ymm12
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  196,99,29,74,224,208                ; vblendvps     %ymm13,%ymm0,%ymm12,%ymm12
+  DB  196,65,116,84,233                   ; vandps        %ymm9,%ymm1,%ymm13
+  DB  197,252,17,76,36,32                 ; vmovups       %ymm1,0x20(%rsp)
+  DB  196,65,116,87,245                   ; vxorps        %ymm13,%ymm1,%ymm14
+  DB  196,67,125,25,247,1                 ; vextractf128  $0x1,%ymm14,%xmm15
+  DB  196,193,57,102,255                  ; vpcmpgtd      %xmm15,%xmm8,%xmm7
+  DB  196,65,57,102,222                   ; vpcmpgtd      %xmm14,%xmm8,%xmm11
+  DB  196,227,37,24,255,1                 ; vinsertf128   $0x1,%xmm7,%ymm11,%ymm7
+  DB  196,193,33,114,213,16               ; vpsrld        $0x10,%xmm13,%xmm11
+  DB  196,99,125,25,238,1                 ; vextractf128  $0x1,%ymm13,%xmm6
+  DB  197,201,114,214,16                  ; vpsrld        $0x10,%xmm6,%xmm6
+  DB  196,227,37,24,246,1                 ; vinsertf128   $0x1,%xmm6,%ymm11,%ymm6
+  DB  196,193,33,114,215,13               ; vpsrld        $0xd,%xmm15,%xmm11
+  DB  196,193,76,86,242                   ; vorps         %ymm10,%ymm6,%ymm6
+  DB  196,227,125,25,245,1                ; vextractf128  $0x1,%ymm6,%xmm5
+  DB  196,193,81,254,235                  ; vpaddd        %xmm11,%xmm5,%xmm5
+  DB  196,193,89,114,214,13               ; vpsrld        $0xd,%xmm14,%xmm4
+  DB  197,201,254,228                     ; vpaddd        %xmm4,%xmm6,%xmm4
+  DB  196,227,93,24,229,1                 ; vinsertf128   $0x1,%xmm5,%ymm4,%ymm4
+  DB  196,99,93,74,232,112                ; vblendvps     %ymm7,%ymm0,%ymm4,%ymm13
+  DB  196,193,108,84,225                  ; vandps        %ymm9,%ymm2,%ymm4
+  DB  197,236,87,236                      ; vxorps        %ymm4,%ymm2,%ymm5
+  DB  196,227,125,25,238,1                ; vextractf128  $0x1,%ymm5,%xmm6
+  DB  197,185,102,254                     ; vpcmpgtd      %xmm6,%xmm8,%xmm7
+  DB  197,57,102,221                      ; vpcmpgtd      %xmm5,%xmm8,%xmm11
+  DB  196,227,37,24,255,1                 ; vinsertf128   $0x1,%xmm7,%ymm11,%ymm7
+  DB  197,161,114,212,16                  ; vpsrld        $0x10,%xmm4,%xmm11
+  DB  196,227,125,25,228,1                ; vextractf128  $0x1,%ymm4,%xmm4
+  DB  197,217,114,212,16                  ; vpsrld        $0x10,%xmm4,%xmm4
+  DB  196,227,37,24,228,1                 ; vinsertf128   $0x1,%xmm4,%ymm11,%ymm4
+  DB  197,201,114,214,13                  ; vpsrld        $0xd,%xmm6,%xmm6
+  DB  196,193,92,86,226                   ; vorps         %ymm10,%ymm4,%ymm4
+  DB  196,227,125,25,225,1                ; vextractf128  $0x1,%ymm4,%xmm1
+  DB  197,241,254,206                     ; vpaddd        %xmm6,%xmm1,%xmm1
+  DB  197,209,114,213,13                  ; vpsrld        $0xd,%xmm5,%xmm5
+  DB  197,217,254,229                     ; vpaddd        %xmm5,%xmm4,%xmm4
+  DB  196,227,93,24,201,1                 ; vinsertf128   $0x1,%xmm1,%ymm4,%ymm1
+  DB  196,99,117,74,216,112               ; vblendvps     %ymm7,%ymm0,%ymm1,%ymm11
+  DB  196,193,100,84,225                  ; vandps        %ymm9,%ymm3,%ymm4
+  DB  197,228,87,236                      ; vxorps        %ymm4,%ymm3,%ymm5
+  DB  196,227,125,25,238,1                ; vextractf128  $0x1,%ymm5,%xmm6
+  DB  197,185,102,254                     ; vpcmpgtd      %xmm6,%xmm8,%xmm7
+  DB  197,57,102,197                      ; vpcmpgtd      %xmm5,%xmm8,%xmm8
+  DB  196,227,61,24,255,1                 ; vinsertf128   $0x1,%xmm7,%ymm8,%ymm7
+  DB  197,185,114,212,16                  ; vpsrld        $0x10,%xmm4,%xmm8
+  DB  196,227,125,25,228,1                ; vextractf128  $0x1,%ymm4,%xmm4
+  DB  197,217,114,212,16                  ; vpsrld        $0x10,%xmm4,%xmm4
+  DB  196,227,61,24,228,1                 ; vinsertf128   $0x1,%xmm4,%ymm8,%ymm4
+  DB  196,193,92,86,226                   ; vorps         %ymm10,%ymm4,%ymm4
+  DB  197,201,114,214,13                  ; vpsrld        $0xd,%xmm6,%xmm6
+  DB  196,227,125,25,225,1                ; vextractf128  $0x1,%ymm4,%xmm1
+  DB  197,241,254,206                     ; vpaddd        %xmm6,%xmm1,%xmm1
+  DB  197,209,114,213,13                  ; vpsrld        $0xd,%xmm5,%xmm5
+  DB  197,217,254,229                     ; vpaddd        %xmm5,%xmm4,%xmm4
+  DB  196,227,93,24,201,1                 ; vinsertf128   $0x1,%xmm1,%ymm4,%ymm1
+  DB  196,227,117,74,200,112              ; vblendvps     %ymm7,%ymm0,%ymm1,%ymm1
+  DB  196,99,125,25,224,1                 ; vextractf128  $0x1,%ymm12,%xmm0
+  DB  196,226,25,43,192                   ; vpackusdw     %xmm0,%xmm12,%xmm0
+  DB  196,99,125,25,236,1                 ; vextractf128  $0x1,%ymm13,%xmm4
+  DB  196,226,17,43,228                   ; vpackusdw     %xmm4,%xmm13,%xmm4
+  DB  196,99,125,25,221,1                 ; vextractf128  $0x1,%ymm11,%xmm5
+  DB  196,226,33,43,245                   ; vpackusdw     %xmm5,%xmm11,%xmm6
+  DB  196,227,125,25,205,1                ; vextractf128  $0x1,%ymm1,%xmm5
+  DB  196,226,113,43,205                  ; vpackusdw     %xmm5,%xmm1,%xmm1
+  DB  197,249,97,236                      ; vpunpcklwd    %xmm4,%xmm0,%xmm5
+  DB  197,249,105,196                     ; vpunpckhwd    %xmm4,%xmm0,%xmm0
+  DB  197,201,97,225                      ; vpunpcklwd    %xmm1,%xmm6,%xmm4
+  DB  197,201,105,201                     ; vpunpckhwd    %xmm1,%xmm6,%xmm1
+  DB  197,81,98,220                       ; vpunpckldq    %xmm4,%xmm5,%xmm11
+  DB  197,81,106,212                      ; vpunpckhdq    %xmm4,%xmm5,%xmm10
+  DB  197,121,98,201                      ; vpunpckldq    %xmm1,%xmm0,%xmm9
+  DB  197,121,106,193                     ; vpunpckhdq    %xmm1,%xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,75                              ; jne           4d24 <_sk_store_f16_avx+0x270>
+  DB  197,120,17,28,208                   ; vmovups       %xmm11,(%rax,%rdx,8)
+  DB  197,120,17,84,208,16                ; vmovups       %xmm10,0x10(%rax,%rdx,8)
+  DB  197,120,17,76,208,32                ; vmovups       %xmm9,0x20(%rax,%rdx,8)
+  DB  197,122,127,68,208,48               ; vmovdqu       %xmm8,0x30(%rax,%rdx,8)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,16,4,36                     ; vmovups       (%rsp),%ymm0
+  DB  197,252,16,76,36,32                 ; vmovups       0x20(%rsp),%ymm1
+  DB  197,252,16,100,36,64                ; vmovups       0x40(%rsp),%ymm4
+  DB  197,252,16,108,36,96                ; vmovups       0x60(%rsp),%ymm5
+  DB  197,252,16,180,36,128,0,0,0         ; vmovups       0x80(%rsp),%ymm6
+  DB  197,252,16,188,36,160,0,0,0         ; vmovups       0xa0(%rsp),%ymm7
+  DB  72,129,196,216,0,0,0                ; add           $0xd8,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  197,121,214,28,208                  ; vmovq         %xmm11,(%rax,%rdx,8)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,193                             ; je            4cf0 <_sk_store_f16_avx+0x23c>
+  DB  197,121,23,92,208,8                 ; vmovhpd       %xmm11,0x8(%rax,%rdx,8)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,181                             ; jb            4cf0 <_sk_store_f16_avx+0x23c>
+  DB  197,121,214,84,208,16               ; vmovq         %xmm10,0x10(%rax,%rdx,8)
+  DB  116,173                             ; je            4cf0 <_sk_store_f16_avx+0x23c>
+  DB  197,121,23,84,208,24                ; vmovhpd       %xmm10,0x18(%rax,%rdx,8)
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,161                             ; jb            4cf0 <_sk_store_f16_avx+0x23c>
+  DB  197,121,214,76,208,32               ; vmovq         %xmm9,0x20(%rax,%rdx,8)
+  DB  116,153                             ; je            4cf0 <_sk_store_f16_avx+0x23c>
+  DB  197,121,23,76,208,40                ; vmovhpd       %xmm9,0x28(%rax,%rdx,8)
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,141                             ; jb            4cf0 <_sk_store_f16_avx+0x23c>
+  DB  197,121,214,68,208,48               ; vmovq         %xmm8,0x30(%rax,%rdx,8)
+  DB  235,133                             ; jmp           4cf0 <_sk_store_f16_avx+0x23c>
+
+PUBLIC _sk_load_u16_be_avx
+_sk_load_u16_be_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,253,0,0,0                    ; jne           4e7e <_sk_load_u16_be_avx+0x113>
+  DB  196,65,121,16,4,65                  ; vmovupd       (%r9,%rax,2),%xmm8
+  DB  196,193,121,16,84,65,16             ; vmovupd       0x10(%r9,%rax,2),%xmm2
+  DB  196,193,121,16,92,65,32             ; vmovupd       0x20(%r9,%rax,2),%xmm3
+  DB  196,65,122,111,76,65,48             ; vmovdqu       0x30(%r9,%rax,2),%xmm9
+  DB  197,185,97,194                      ; vpunpcklwd    %xmm2,%xmm8,%xmm0
+  DB  197,185,105,210                     ; vpunpckhwd    %xmm2,%xmm8,%xmm2
+  DB  196,193,97,97,201                   ; vpunpcklwd    %xmm9,%xmm3,%xmm1
+  DB  196,193,97,105,217                  ; vpunpckhwd    %xmm9,%xmm3,%xmm3
+  DB  197,121,97,202                      ; vpunpcklwd    %xmm2,%xmm0,%xmm9
+  DB  197,121,105,194                     ; vpunpckhwd    %xmm2,%xmm0,%xmm8
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,113,105,227                     ; vpunpckhwd    %xmm3,%xmm1,%xmm12
+  DB  197,177,108,194                     ; vpunpcklqdq   %xmm2,%xmm9,%xmm0
+  DB  197,241,113,240,8                   ; vpsllw        $0x8,%xmm0,%xmm1
+  DB  197,249,113,208,8                   ; vpsrlw        $0x8,%xmm0,%xmm0
+  DB  197,241,235,192                     ; vpor          %xmm0,%xmm1,%xmm0
+  DB  196,65,41,239,210                   ; vpxor         %xmm10,%xmm10,%xmm10
+  DB  196,193,121,105,202                 ; vpunpckhwd    %xmm10,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,29,110,27,0,0         ; vbroadcastss  0x1b6e(%rip),%ymm11        # 6960 <_sk_callback_avx+0x45c>
+  DB  196,193,124,89,195                  ; vmulps        %ymm11,%ymm0,%ymm0
+  DB  197,177,109,202                     ; vpunpckhqdq   %xmm2,%xmm9,%xmm1
+  DB  197,233,113,241,8                   ; vpsllw        $0x8,%xmm1,%xmm2
+  DB  197,241,113,209,8                   ; vpsrlw        $0x8,%xmm1,%xmm1
+  DB  197,233,235,201                     ; vpor          %xmm1,%xmm2,%xmm1
+  DB  196,193,113,105,210                 ; vpunpckhwd    %xmm10,%xmm1,%xmm2
+  DB  196,226,121,51,201                  ; vpmovzxwd     %xmm1,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,203                  ; vmulps        %ymm11,%ymm1,%ymm1
+  DB  196,193,57,108,212                  ; vpunpcklqdq   %xmm12,%xmm8,%xmm2
+  DB  197,225,113,242,8                   ; vpsllw        $0x8,%xmm2,%xmm3
+  DB  197,233,113,210,8                   ; vpsrlw        $0x8,%xmm2,%xmm2
+  DB  197,225,235,210                     ; vpor          %xmm2,%xmm3,%xmm2
+  DB  196,193,105,105,218                 ; vpunpckhwd    %xmm10,%xmm2,%xmm3
+  DB  196,226,121,51,210                  ; vpmovzxwd     %xmm2,%xmm2
+  DB  196,227,109,24,211,1                ; vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,211                  ; vmulps        %ymm11,%ymm2,%ymm2
+  DB  196,193,57,109,220                  ; vpunpckhqdq   %xmm12,%xmm8,%xmm3
+  DB  197,185,113,243,8                   ; vpsllw        $0x8,%xmm3,%xmm8
+  DB  197,225,113,211,8                   ; vpsrlw        $0x8,%xmm3,%xmm3
+  DB  197,185,235,219                     ; vpor          %xmm3,%xmm8,%xmm3
+  DB  196,65,97,105,194                   ; vpunpckhwd    %xmm10,%xmm3,%xmm8
+  DB  196,226,121,51,219                  ; vpmovzxwd     %xmm3,%xmm3
+  DB  196,195,101,24,216,1                ; vinsertf128   $0x1,%xmm8,%ymm3,%ymm3
+  DB  197,252,91,219                      ; vcvtdq2ps     %ymm3,%ymm3
+  DB  196,193,100,89,219                  ; vmulps        %ymm11,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,123,16,4,65                  ; vmovsd        (%r9,%rax,2),%xmm8
+  DB  196,65,49,239,201                   ; vpxor         %xmm9,%xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,85                              ; je            4ee4 <_sk_load_u16_be_avx+0x179>
+  DB  196,65,57,22,68,65,8                ; vmovhpd       0x8(%r9,%rax,2),%xmm8,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,72                              ; jb            4ee4 <_sk_load_u16_be_avx+0x179>
+  DB  196,193,123,16,84,65,16             ; vmovsd        0x10(%r9,%rax,2),%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  116,72                              ; je            4ef1 <_sk_load_u16_be_avx+0x186>
+  DB  196,193,105,22,84,65,24             ; vmovhpd       0x18(%r9,%rax,2),%xmm2,%xmm2
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,59                              ; jb            4ef1 <_sk_load_u16_be_avx+0x186>
+  DB  196,193,123,16,92,65,32             ; vmovsd        0x20(%r9,%rax,2),%xmm3
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  15,132,213,254,255,255              ; je            4d9c <_sk_load_u16_be_avx+0x31>
+  DB  196,193,97,22,92,65,40              ; vmovhpd       0x28(%r9,%rax,2),%xmm3,%xmm3
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  15,130,196,254,255,255              ; jb            4d9c <_sk_load_u16_be_avx+0x31>
+  DB  196,65,122,126,76,65,48             ; vmovq         0x30(%r9,%rax,2),%xmm9
+  DB  233,184,254,255,255                 ; jmpq          4d9c <_sk_load_u16_be_avx+0x31>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  197,233,87,210                      ; vxorpd        %xmm2,%xmm2,%xmm2
+  DB  233,171,254,255,255                 ; jmpq          4d9c <_sk_load_u16_be_avx+0x31>
+  DB  197,225,87,219                      ; vxorpd        %xmm3,%xmm3,%xmm3
+  DB  233,162,254,255,255                 ; jmpq          4d9c <_sk_load_u16_be_avx+0x31>
+
+PUBLIC _sk_load_rgb_u16_be_avx
+_sk_load_rgb_u16_be_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,82                         ; lea           (%rdx,%rdx,2),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,243,0,0,0                    ; jne           4fff <_sk_load_rgb_u16_be_avx+0x105>
+  DB  196,193,122,111,4,65                ; vmovdqu       (%r9,%rax,2),%xmm0
+  DB  196,193,122,111,84,65,12            ; vmovdqu       0xc(%r9,%rax,2),%xmm2
+  DB  196,193,122,111,76,65,24            ; vmovdqu       0x18(%r9,%rax,2),%xmm1
+  DB  196,193,122,111,92,65,32            ; vmovdqu       0x20(%r9,%rax,2),%xmm3
+  DB  197,225,115,219,4                   ; vpsrldq       $0x4,%xmm3,%xmm3
+  DB  197,185,115,216,6                   ; vpsrldq       $0x6,%xmm0,%xmm8
+  DB  197,177,115,218,6                   ; vpsrldq       $0x6,%xmm2,%xmm9
+  DB  197,161,115,217,6                   ; vpsrldq       $0x6,%xmm1,%xmm11
+  DB  197,169,115,219,6                   ; vpsrldq       $0x6,%xmm3,%xmm10
+  DB  197,249,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm0
+  DB  196,193,57,97,209                   ; vpunpcklwd    %xmm9,%xmm8,%xmm2
+  DB  197,241,97,203                      ; vpunpcklwd    %xmm3,%xmm1,%xmm1
+  DB  196,193,33,97,218                   ; vpunpcklwd    %xmm10,%xmm11,%xmm3
+  DB  197,121,97,194                      ; vpunpcklwd    %xmm2,%xmm0,%xmm8
+  DB  197,121,105,202                     ; vpunpckhwd    %xmm2,%xmm0,%xmm9
+  DB  197,241,97,211                      ; vpunpcklwd    %xmm3,%xmm1,%xmm2
+  DB  197,113,105,211                     ; vpunpckhwd    %xmm3,%xmm1,%xmm10
+  DB  197,185,108,194                     ; vpunpcklqdq   %xmm2,%xmm8,%xmm0
+  DB  197,241,113,240,8                   ; vpsllw        $0x8,%xmm0,%xmm1
+  DB  197,249,113,208,8                   ; vpsrlw        $0x8,%xmm0,%xmm0
+  DB  197,241,235,192                     ; vpor          %xmm0,%xmm1,%xmm0
+  DB  196,65,25,239,228                   ; vpxor         %xmm12,%xmm12,%xmm12
+  DB  196,193,121,105,204                 ; vpunpckhwd    %xmm12,%xmm0,%xmm1
+  DB  196,226,121,51,192                  ; vpmovzxwd     %xmm0,%xmm0
+  DB  196,227,125,24,193,1                ; vinsertf128   $0x1,%xmm1,%ymm0,%ymm0
+  DB  197,252,91,192                      ; vcvtdq2ps     %ymm0,%ymm0
+  DB  196,98,125,24,29,206,25,0,0         ; vbroadcastss  0x19ce(%rip),%ymm11        # 6964 <_sk_callback_avx+0x460>
+  DB  196,193,124,89,195                  ; vmulps        %ymm11,%ymm0,%ymm0
+  DB  197,185,109,202                     ; vpunpckhqdq   %xmm2,%xmm8,%xmm1
+  DB  197,233,113,241,8                   ; vpsllw        $0x8,%xmm1,%xmm2
+  DB  197,241,113,209,8                   ; vpsrlw        $0x8,%xmm1,%xmm1
+  DB  197,233,235,201                     ; vpor          %xmm1,%xmm2,%xmm1
+  DB  196,193,113,105,212                 ; vpunpckhwd    %xmm12,%xmm1,%xmm2
+  DB  196,226,121,51,201                  ; vpmovzxwd     %xmm1,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  197,252,91,201                      ; vcvtdq2ps     %ymm1,%ymm1
+  DB  196,193,116,89,203                  ; vmulps        %ymm11,%ymm1,%ymm1
+  DB  196,193,49,108,210                  ; vpunpcklqdq   %xmm10,%xmm9,%xmm2
+  DB  197,225,113,242,8                   ; vpsllw        $0x8,%xmm2,%xmm3
+  DB  197,233,113,210,8                   ; vpsrlw        $0x8,%xmm2,%xmm2
+  DB  197,225,235,210                     ; vpor          %xmm2,%xmm3,%xmm2
+  DB  196,193,105,105,220                 ; vpunpckhwd    %xmm12,%xmm2,%xmm3
+  DB  196,226,121,51,210                  ; vpmovzxwd     %xmm2,%xmm2
+  DB  196,227,109,24,211,1                ; vinsertf128   $0x1,%xmm3,%ymm2,%ymm2
+  DB  197,252,91,210                      ; vcvtdq2ps     %ymm2,%ymm2
+  DB  196,193,108,89,211                  ; vmulps        %ymm11,%ymm2,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,29,107,25,0,0        ; vbroadcastss  0x196b(%rip),%ymm3        # 6968 <_sk_callback_avx+0x464>
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,193,121,110,4,65                ; vmovd         (%r9,%rax,2),%xmm0
+  DB  196,193,121,196,68,65,4,2           ; vpinsrw       $0x2,0x4(%r9,%rax,2),%xmm0,%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,5                               ; jne           5018 <_sk_load_rgb_u16_be_avx+0x11e>
+  DB  233,40,255,255,255                  ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+  DB  196,193,121,110,76,65,6             ; vmovd         0x6(%r9,%rax,2),%xmm1
+  DB  196,65,113,196,68,65,10,2           ; vpinsrw       $0x2,0xa(%r9,%rax,2),%xmm1,%xmm8
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,26                              ; jb            5047 <_sk_load_rgb_u16_be_avx+0x14d>
+  DB  196,193,121,110,76,65,12            ; vmovd         0xc(%r9,%rax,2),%xmm1
+  DB  196,193,113,196,84,65,16,2          ; vpinsrw       $0x2,0x10(%r9,%rax,2),%xmm1,%xmm2
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  117,10                              ; jne           504c <_sk_load_rgb_u16_be_avx+0x152>
+  DB  233,249,254,255,255                 ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+  DB  233,244,254,255,255                 ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+  DB  196,193,121,110,76,65,18            ; vmovd         0x12(%r9,%rax,2),%xmm1
+  DB  196,65,113,196,76,65,22,2           ; vpinsrw       $0x2,0x16(%r9,%rax,2),%xmm1,%xmm9
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,26                              ; jb            507b <_sk_load_rgb_u16_be_avx+0x181>
+  DB  196,193,121,110,76,65,24            ; vmovd         0x18(%r9,%rax,2),%xmm1
+  DB  196,193,113,196,76,65,28,2          ; vpinsrw       $0x2,0x1c(%r9,%rax,2),%xmm1,%xmm1
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  117,10                              ; jne           5080 <_sk_load_rgb_u16_be_avx+0x186>
+  DB  233,197,254,255,255                 ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+  DB  233,192,254,255,255                 ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+  DB  196,193,121,110,92,65,30            ; vmovd         0x1e(%r9,%rax,2),%xmm3
+  DB  196,65,97,196,92,65,34,2            ; vpinsrw       $0x2,0x22(%r9,%rax,2),%xmm3,%xmm11
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,20                              ; jb            50a9 <_sk_load_rgb_u16_be_avx+0x1af>
+  DB  196,193,121,110,92,65,36            ; vmovd         0x24(%r9,%rax,2),%xmm3
+  DB  196,193,97,196,92,65,40,2           ; vpinsrw       $0x2,0x28(%r9,%rax,2),%xmm3,%xmm3
+  DB  233,151,254,255,255                 ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+  DB  233,146,254,255,255                 ; jmpq          4f40 <_sk_load_rgb_u16_be_avx+0x46>
+
+PUBLIC _sk_store_u16_be_avx
+_sk_store_u16_be_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  196,98,125,24,5,168,24,0,0          ; vbroadcastss  0x18a8(%rip),%ymm8        # 696c <_sk_callback_avx+0x468>
+  DB  196,65,124,89,200                   ; vmulps        %ymm8,%ymm0,%ymm9
+  DB  196,65,125,91,201                   ; vcvtps2dq     %ymm9,%ymm9
+  DB  196,67,125,25,202,1                 ; vextractf128  $0x1,%ymm9,%xmm10
+  DB  196,66,49,43,202                    ; vpackusdw     %xmm10,%xmm9,%xmm9
+  DB  196,193,41,113,241,8                ; vpsllw        $0x8,%xmm9,%xmm10
+  DB  196,193,49,113,209,8                ; vpsrlw        $0x8,%xmm9,%xmm9
+  DB  196,65,41,235,201                   ; vpor          %xmm9,%xmm10,%xmm9
+  DB  196,65,116,89,208                   ; vmulps        %ymm8,%ymm1,%ymm10
+  DB  196,65,125,91,210                   ; vcvtps2dq     %ymm10,%ymm10
+  DB  196,67,125,25,211,1                 ; vextractf128  $0x1,%ymm10,%xmm11
+  DB  196,66,41,43,211                    ; vpackusdw     %xmm11,%xmm10,%xmm10
+  DB  196,193,33,113,242,8                ; vpsllw        $0x8,%xmm10,%xmm11
+  DB  196,193,41,113,210,8                ; vpsrlw        $0x8,%xmm10,%xmm10
+  DB  196,65,33,235,210                   ; vpor          %xmm10,%xmm11,%xmm10
+  DB  196,65,108,89,216                   ; vmulps        %ymm8,%ymm2,%ymm11
+  DB  196,65,125,91,219                   ; vcvtps2dq     %ymm11,%ymm11
+  DB  196,67,125,25,220,1                 ; vextractf128  $0x1,%ymm11,%xmm12
+  DB  196,66,33,43,220                    ; vpackusdw     %xmm12,%xmm11,%xmm11
+  DB  196,193,25,113,243,8                ; vpsllw        $0x8,%xmm11,%xmm12
+  DB  196,193,33,113,211,8                ; vpsrlw        $0x8,%xmm11,%xmm11
+  DB  196,65,25,235,219                   ; vpor          %xmm11,%xmm12,%xmm11
+  DB  196,65,100,89,192                   ; vmulps        %ymm8,%ymm3,%ymm8
+  DB  196,65,125,91,192                   ; vcvtps2dq     %ymm8,%ymm8
+  DB  196,67,125,25,196,1                 ; vextractf128  $0x1,%ymm8,%xmm12
+  DB  196,66,57,43,196                    ; vpackusdw     %xmm12,%xmm8,%xmm8
+  DB  196,193,25,113,240,8                ; vpsllw        $0x8,%xmm8,%xmm12
+  DB  196,193,57,113,208,8                ; vpsrlw        $0x8,%xmm8,%xmm8
+  DB  196,65,25,235,192                   ; vpor          %xmm8,%xmm12,%xmm8
+  DB  196,65,49,97,226                    ; vpunpcklwd    %xmm10,%xmm9,%xmm12
+  DB  196,65,49,105,234                   ; vpunpckhwd    %xmm10,%xmm9,%xmm13
+  DB  196,65,33,97,200                    ; vpunpcklwd    %xmm8,%xmm11,%xmm9
+  DB  196,65,33,105,192                   ; vpunpckhwd    %xmm8,%xmm11,%xmm8
+  DB  196,65,25,98,217                    ; vpunpckldq    %xmm9,%xmm12,%xmm11
+  DB  196,65,25,106,209                   ; vpunpckhdq    %xmm9,%xmm12,%xmm10
+  DB  196,65,17,98,200                    ; vpunpckldq    %xmm8,%xmm13,%xmm9
+  DB  196,65,17,106,192                   ; vpunpckhdq    %xmm8,%xmm13,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,31                              ; jne           51a8 <_sk_store_u16_be_avx+0xfa>
+  DB  196,65,120,17,28,65                 ; vmovups       %xmm11,(%r9,%rax,2)
+  DB  196,65,120,17,84,65,16              ; vmovups       %xmm10,0x10(%r9,%rax,2)
+  DB  196,65,120,17,76,65,32              ; vmovups       %xmm9,0x20(%r9,%rax,2)
+  DB  196,65,122,127,68,65,48             ; vmovdqu       %xmm8,0x30(%r9,%rax,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,121,214,28,65                ; vmovq         %xmm11,(%r9,%rax,2)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            51a4 <_sk_store_u16_be_avx+0xf6>
+  DB  196,65,121,23,92,65,8               ; vmovhpd       %xmm11,0x8(%r9,%rax,2)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            51a4 <_sk_store_u16_be_avx+0xf6>
+  DB  196,65,121,214,84,65,16             ; vmovq         %xmm10,0x10(%r9,%rax,2)
+  DB  116,218                             ; je            51a4 <_sk_store_u16_be_avx+0xf6>
+  DB  196,65,121,23,84,65,24              ; vmovhpd       %xmm10,0x18(%r9,%rax,2)
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,205                             ; jb            51a4 <_sk_store_u16_be_avx+0xf6>
+  DB  196,65,121,214,76,65,32             ; vmovq         %xmm9,0x20(%r9,%rax,2)
+  DB  116,196                             ; je            51a4 <_sk_store_u16_be_avx+0xf6>
+  DB  196,65,121,23,76,65,40              ; vmovhpd       %xmm9,0x28(%r9,%rax,2)
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,183                             ; jb            51a4 <_sk_store_u16_be_avx+0xf6>
+  DB  196,65,121,214,68,65,48             ; vmovq         %xmm8,0x30(%r9,%rax,2)
+  DB  235,174                             ; jmp           51a4 <_sk_store_u16_be_avx+0xf6>
+
+PUBLIC _sk_load_f32_avx
+_sk_load_f32_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  119,110                             ; ja            526c <_sk_load_f32_avx+0x76>
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  76,141,29,132,0,0,0                 ; lea           0x84(%rip),%r11        # 5294 <_sk_load_f32_avx+0x9e>
+  DB  75,99,4,131                         ; movslq        (%r11,%r8,4),%rax
+  DB  76,1,216                            ; add           %r11,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,3,125,24,68,145,112,1           ; vinsertf128   $0x1,0x70(%r9,%r10,4),%ymm0,%ymm8
+  DB  196,131,125,24,92,145,96,1          ; vinsertf128   $0x1,0x60(%r9,%r10,4),%ymm0,%ymm3
+  DB  196,131,125,24,76,145,80,1          ; vinsertf128   $0x1,0x50(%r9,%r10,4),%ymm0,%ymm1
+  DB  196,131,125,24,84,145,64,1          ; vinsertf128   $0x1,0x40(%r9,%r10,4),%ymm0,%ymm2
+  DB  196,129,121,16,68,145,48            ; vmovupd       0x30(%r9,%r10,4),%xmm0
+  DB  196,195,125,13,192,12               ; vblendpd      $0xc,%ymm8,%ymm0,%ymm0
+  DB  196,1,121,16,68,145,32              ; vmovupd       0x20(%r9,%r10,4),%xmm8
+  DB  196,99,61,13,203,12                 ; vblendpd      $0xc,%ymm3,%ymm8,%ymm9
+  DB  196,129,121,16,92,145,16            ; vmovupd       0x10(%r9,%r10,4),%xmm3
+  DB  196,99,101,13,209,12                ; vblendpd      $0xc,%ymm1,%ymm3,%ymm10
+  DB  196,129,121,16,12,145               ; vmovupd       (%r9,%r10,4),%xmm1
+  DB  196,227,117,13,202,12               ; vblendpd      $0xc,%ymm2,%ymm1,%ymm1
+  DB  196,193,116,20,210                  ; vunpcklps     %ymm10,%ymm1,%ymm2
+  DB  196,193,116,21,218                  ; vunpckhps     %ymm10,%ymm1,%ymm3
+  DB  197,180,20,200                      ; vunpcklps     %ymm0,%ymm9,%ymm1
+  DB  197,52,21,192                       ; vunpckhps     %ymm0,%ymm9,%ymm8
+  DB  197,237,20,193                      ; vunpcklpd     %ymm1,%ymm2,%ymm0
+  DB  197,237,21,201                      ; vunpckhpd     %ymm1,%ymm2,%ymm1
+  DB  196,193,101,20,208                  ; vunpcklpd     %ymm8,%ymm3,%ymm2
+  DB  196,193,101,21,216                  ; vunpckhpd     %ymm8,%ymm3,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  133,255                             ; test          %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,204                             ; dec           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  191,255,255,255,178                 ; mov           $0xb2ffffff,%edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,165,255,255,255,157             ; jmpq          *-0x62000001(%rbp)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,149,255,255,255,141             ; callq         *-0x72000001(%rbp)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_store_f32_avx
+_sk_store_f32_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  197,124,20,193                      ; vunpcklps     %ymm1,%ymm0,%ymm8
+  DB  197,124,21,217                      ; vunpckhps     %ymm1,%ymm0,%ymm11
+  DB  197,108,20,203                      ; vunpcklps     %ymm3,%ymm2,%ymm9
+  DB  197,108,21,227                      ; vunpckhps     %ymm3,%ymm2,%ymm12
+  DB  196,65,61,20,209                    ; vunpcklpd     %ymm9,%ymm8,%ymm10
+  DB  196,65,61,21,201                    ; vunpckhpd     %ymm9,%ymm8,%ymm9
+  DB  196,65,37,20,196                    ; vunpcklpd     %ymm12,%ymm11,%ymm8
+  DB  196,65,37,21,220                    ; vunpckhpd     %ymm12,%ymm11,%ymm11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,55                              ; jne           5321 <_sk_store_f32_avx+0x6d>
+  DB  196,67,45,24,225,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm12
+  DB  196,67,61,24,235,1                  ; vinsertf128   $0x1,%xmm11,%ymm8,%ymm13
+  DB  196,67,45,6,201,49                  ; vperm2f128    $0x31,%ymm9,%ymm10,%ymm9
+  DB  196,67,61,6,195,49                  ; vperm2f128    $0x31,%ymm11,%ymm8,%ymm8
+  DB  196,65,125,17,36,129                ; vmovupd       %ymm12,(%r9,%rax,4)
+  DB  196,65,125,17,108,129,32            ; vmovupd       %ymm13,0x20(%r9,%rax,4)
+  DB  196,65,125,17,76,129,64             ; vmovupd       %ymm9,0x40(%r9,%rax,4)
+  DB  196,65,125,17,68,129,96             ; vmovupd       %ymm8,0x60(%r9,%rax,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  196,65,121,17,20,129                ; vmovupd       %xmm10,(%r9,%rax,4)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            531d <_sk_store_f32_avx+0x69>
+  DB  196,65,121,17,76,129,16             ; vmovupd       %xmm9,0x10(%r9,%rax,4)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            531d <_sk_store_f32_avx+0x69>
+  DB  196,65,121,17,68,129,32             ; vmovupd       %xmm8,0x20(%r9,%rax,4)
+  DB  116,218                             ; je            531d <_sk_store_f32_avx+0x69>
+  DB  196,65,121,17,92,129,48             ; vmovupd       %xmm11,0x30(%r9,%rax,4)
+  DB  73,131,248,5                        ; cmp           $0x5,%r8
+  DB  114,205                             ; jb            531d <_sk_store_f32_avx+0x69>
+  DB  196,67,125,25,84,129,64,1           ; vextractf128  $0x1,%ymm10,0x40(%r9,%rax,4)
+  DB  116,195                             ; je            531d <_sk_store_f32_avx+0x69>
+  DB  196,67,125,25,76,129,80,1           ; vextractf128  $0x1,%ymm9,0x50(%r9,%rax,4)
+  DB  73,131,248,7                        ; cmp           $0x7,%r8
+  DB  114,181                             ; jb            531d <_sk_store_f32_avx+0x69>
+  DB  196,67,125,25,68,129,96,1           ; vextractf128  $0x1,%ymm8,0x60(%r9,%rax,4)
+  DB  235,171                             ; jmp           531d <_sk_store_f32_avx+0x69>
+
+PUBLIC _sk_clamp_x_avx
+_sk_clamp_x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,192                      ; vmaxps        %ymm0,%ymm8,%ymm0
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_y_avx
+_sk_clamp_y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,201                      ; vmaxps        %ymm1,%ymm8,%ymm1
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,193,116,93,200                  ; vminps        %ymm8,%ymm1,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_avx
+_sk_repeat_x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,65,124,94,200                   ; vdivps        %ymm8,%ymm0,%ymm9
+  DB  196,67,125,8,201,1                  ; vroundps      $0x1,%ymm9,%ymm9
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  196,193,124,92,192                  ; vsubps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_y_avx
+_sk_repeat_y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,65,116,94,200                   ; vdivps        %ymm8,%ymm1,%ymm9
+  DB  196,67,125,8,201,1                  ; vroundps      $0x1,%ymm9,%ymm9
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  196,193,116,92,200                  ; vsubps        %ymm8,%ymm1,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_avx
+_sk_mirror_x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,121,110,0                       ; vmovd         (%rax),%xmm8
+  DB  196,65,121,112,200,0                ; vpshufd       $0x0,%xmm8,%xmm9
+  DB  196,67,53,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
+  DB  196,65,124,92,209                   ; vsubps        %ymm9,%ymm0,%ymm10
+  DB  196,193,58,88,192                   ; vaddss        %xmm8,%xmm8,%xmm0
+  DB  196,227,121,4,192,0                 ; vpermilps     $0x0,%xmm0,%xmm0
+  DB  196,227,125,24,192,1                ; vinsertf128   $0x1,%xmm0,%ymm0,%ymm0
+  DB  197,44,94,192                       ; vdivps        %ymm0,%ymm10,%ymm8
+  DB  196,67,125,8,192,1                  ; vroundps      $0x1,%ymm8,%ymm8
+  DB  197,188,89,192                      ; vmulps        %ymm0,%ymm8,%ymm0
+  DB  197,172,92,192                      ; vsubps        %ymm0,%ymm10,%ymm0
+  DB  196,193,124,92,193                  ; vsubps        %ymm9,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,192                       ; vsubps        %ymm0,%ymm8,%ymm8
+  DB  197,188,84,192                      ; vandps        %ymm0,%ymm8,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_y_avx
+_sk_mirror_y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,121,110,0                       ; vmovd         (%rax),%xmm8
+  DB  196,65,121,112,200,0                ; vpshufd       $0x0,%xmm8,%xmm9
+  DB  196,67,53,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm9,%ymm9
+  DB  196,65,116,92,209                   ; vsubps        %ymm9,%ymm1,%ymm10
+  DB  196,193,58,88,200                   ; vaddss        %xmm8,%xmm8,%xmm1
+  DB  196,227,121,4,201,0                 ; vpermilps     $0x0,%xmm1,%xmm1
+  DB  196,227,117,24,201,1                ; vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
+  DB  197,44,94,193                       ; vdivps        %ymm1,%ymm10,%ymm8
+  DB  196,67,125,8,192,1                  ; vroundps      $0x1,%ymm8,%ymm8
+  DB  197,188,89,201                      ; vmulps        %ymm1,%ymm8,%ymm1
+  DB  197,172,92,201                      ; vsubps        %ymm1,%ymm10,%ymm1
+  DB  196,193,116,92,201                  ; vsubps        %ymm9,%ymm1,%ymm1
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,193                       ; vsubps        %ymm1,%ymm8,%ymm8
+  DB  197,188,84,201                      ; vandps        %ymm1,%ymm8,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_x_1_avx
+_sk_clamp_x_1_avx LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,188,95,192                      ; vmaxps        %ymm0,%ymm8,%ymm0
+  DB  196,98,125,24,5,218,20,0,0          ; vbroadcastss  0x14da(%rip),%ymm8        # 6970 <_sk_callback_avx+0x46c>
+  DB  196,193,124,93,192                  ; vminps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_1_avx
+_sk_repeat_x_1_avx LABEL PROC
+  DB  196,99,125,8,192,1                  ; vroundps      $0x1,%ymm0,%ymm8
+  DB  196,193,124,92,192                  ; vsubps        %ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_1_avx
+_sk_mirror_x_1_avx LABEL PROC
+  DB  196,98,125,24,5,189,20,0,0          ; vbroadcastss  0x14bd(%rip),%ymm8        # 6974 <_sk_callback_avx+0x470>
+  DB  196,193,124,88,192                  ; vaddps        %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,13,179,20,0,0         ; vbroadcastss  0x14b3(%rip),%ymm9        # 6978 <_sk_callback_avx+0x474>
+  DB  196,65,124,89,201                   ; vmulps        %ymm9,%ymm0,%ymm9
+  DB  196,67,125,8,201,1                  ; vroundps      $0x1,%ymm9,%ymm9
+  DB  196,65,52,88,201                    ; vaddps        %ymm9,%ymm9,%ymm9
+  DB  196,193,124,92,193                  ; vsubps        %ymm9,%ymm0,%ymm0
+  DB  196,193,124,88,192                  ; vaddps        %ymm8,%ymm0,%ymm0
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,192                       ; vsubps        %ymm0,%ymm8,%ymm8
+  DB  197,188,84,192                      ; vandps        %ymm0,%ymm8,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminance_to_alpha_avx
+_sk_luminance_to_alpha_avx LABEL PROC
+  DB  196,226,125,24,29,131,20,0,0        ; vbroadcastss  0x1483(%rip),%ymm3        # 697c <_sk_callback_avx+0x478>
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  196,226,125,24,29,122,20,0,0        ; vbroadcastss  0x147a(%rip),%ymm3        # 6980 <_sk_callback_avx+0x47c>
+  DB  197,244,89,203                      ; vmulps        %ymm3,%ymm1,%ymm1
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  196,226,125,24,13,109,20,0,0        ; vbroadcastss  0x146d(%rip),%ymm1        # 6984 <_sk_callback_avx+0x480>
+  DB  197,236,89,201                      ; vmulps        %ymm1,%ymm2,%ymm1
+  DB  197,252,88,217                      ; vaddps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,87,192                      ; vxorps        %ymm0,%ymm0,%ymm0
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  197,236,87,210                      ; vxorps        %ymm2,%ymm2,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_2x3_avx
+_sk_matrix_2x3_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,98,125,24,72,8                  ; vbroadcastss  0x8(%rax),%ymm9
+  DB  196,98,125,24,80,16                 ; vbroadcastss  0x10(%rax),%ymm10
+  DB  197,52,89,201                       ; vmulps        %ymm1,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  197,60,89,192                       ; vmulps        %ymm0,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,72,4                  ; vbroadcastss  0x4(%rax),%ymm9
+  DB  196,98,125,24,80,12                 ; vbroadcastss  0xc(%rax),%ymm10
+  DB  196,98,125,24,88,20                 ; vbroadcastss  0x14(%rax),%ymm11
+  DB  197,172,89,201                      ; vmulps        %ymm1,%ymm10,%ymm1
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  197,180,89,192                      ; vmulps        %ymm0,%ymm9,%ymm0
+  DB  197,252,88,201                      ; vaddps        %ymm1,%ymm0,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_3x4_avx
+_sk_matrix_3x4_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,98,125,24,88,36                 ; vbroadcastss  0x24(%rax),%ymm11
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  197,52,89,201                       ; vmulps        %ymm1,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  197,60,89,192                       ; vmulps        %ymm0,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,72,4                  ; vbroadcastss  0x4(%rax),%ymm9
+  DB  196,98,125,24,80,16                 ; vbroadcastss  0x10(%rax),%ymm10
+  DB  196,98,125,24,88,28                 ; vbroadcastss  0x1c(%rax),%ymm11
+  DB  196,98,125,24,96,40                 ; vbroadcastss  0x28(%rax),%ymm12
+  DB  197,36,89,218                       ; vmulps        %ymm2,%ymm11,%ymm11
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  197,44,89,209                       ; vmulps        %ymm1,%ymm10,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  197,52,89,200                       ; vmulps        %ymm0,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,8                  ; vbroadcastss  0x8(%rax),%ymm10
+  DB  196,98,125,24,88,20                 ; vbroadcastss  0x14(%rax),%ymm11
+  DB  196,98,125,24,96,32                 ; vbroadcastss  0x20(%rax),%ymm12
+  DB  196,98,125,24,104,44                ; vbroadcastss  0x2c(%rax),%ymm13
+  DB  197,156,89,210                      ; vmulps        %ymm2,%ymm12,%ymm2
+  DB  196,193,108,88,213                  ; vaddps        %ymm13,%ymm2,%ymm2
+  DB  197,164,89,201                      ; vmulps        %ymm1,%ymm11,%ymm1
+  DB  197,244,88,202                      ; vaddps        %ymm2,%ymm1,%ymm1
+  DB  197,172,89,192                      ; vmulps        %ymm0,%ymm10,%ymm0
+  DB  197,252,88,209                      ; vaddps        %ymm1,%ymm0,%ymm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x5_avx
+_sk_matrix_4x5_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,98,125,24,72,16                 ; vbroadcastss  0x10(%rax),%ymm9
+  DB  196,98,125,24,80,32                 ; vbroadcastss  0x20(%rax),%ymm10
+  DB  196,98,125,24,88,48                 ; vbroadcastss  0x30(%rax),%ymm11
+  DB  196,98,125,24,96,64                 ; vbroadcastss  0x40(%rax),%ymm12
+  DB  197,36,89,219                       ; vmulps        %ymm3,%ymm11,%ymm11
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  197,44,89,210                       ; vmulps        %ymm2,%ymm10,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  197,52,89,201                       ; vmulps        %ymm1,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  197,60,89,192                       ; vmulps        %ymm0,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,72,4                  ; vbroadcastss  0x4(%rax),%ymm9
+  DB  196,98,125,24,80,20                 ; vbroadcastss  0x14(%rax),%ymm10
+  DB  196,98,125,24,88,36                 ; vbroadcastss  0x24(%rax),%ymm11
+  DB  196,98,125,24,96,52                 ; vbroadcastss  0x34(%rax),%ymm12
+  DB  196,98,125,24,104,68                ; vbroadcastss  0x44(%rax),%ymm13
+  DB  197,28,89,227                       ; vmulps        %ymm3,%ymm12,%ymm12
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  197,36,89,218                       ; vmulps        %ymm2,%ymm11,%ymm11
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  197,44,89,209                       ; vmulps        %ymm1,%ymm10,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  197,52,89,200                       ; vmulps        %ymm0,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,8                  ; vbroadcastss  0x8(%rax),%ymm10
+  DB  196,98,125,24,88,24                 ; vbroadcastss  0x18(%rax),%ymm11
+  DB  196,98,125,24,96,40                 ; vbroadcastss  0x28(%rax),%ymm12
+  DB  196,98,125,24,104,56                ; vbroadcastss  0x38(%rax),%ymm13
+  DB  196,98,125,24,112,72                ; vbroadcastss  0x48(%rax),%ymm14
+  DB  197,20,89,235                       ; vmulps        %ymm3,%ymm13,%ymm13
+  DB  196,65,20,88,238                    ; vaddps        %ymm14,%ymm13,%ymm13
+  DB  197,28,89,226                       ; vmulps        %ymm2,%ymm12,%ymm12
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  197,36,89,217                       ; vmulps        %ymm1,%ymm11,%ymm11
+  DB  196,65,36,88,220                    ; vaddps        %ymm12,%ymm11,%ymm11
+  DB  197,44,89,208                       ; vmulps        %ymm0,%ymm10,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  196,98,125,24,88,12                 ; vbroadcastss  0xc(%rax),%ymm11
+  DB  196,98,125,24,96,28                 ; vbroadcastss  0x1c(%rax),%ymm12
+  DB  196,98,125,24,104,44                ; vbroadcastss  0x2c(%rax),%ymm13
+  DB  196,98,125,24,112,60                ; vbroadcastss  0x3c(%rax),%ymm14
+  DB  196,98,125,24,120,76                ; vbroadcastss  0x4c(%rax),%ymm15
+  DB  197,140,89,219                      ; vmulps        %ymm3,%ymm14,%ymm3
+  DB  196,193,100,88,223                  ; vaddps        %ymm15,%ymm3,%ymm3
+  DB  197,148,89,210                      ; vmulps        %ymm2,%ymm13,%ymm2
+  DB  197,236,88,211                      ; vaddps        %ymm3,%ymm2,%ymm2
+  DB  197,156,89,201                      ; vmulps        %ymm1,%ymm12,%ymm1
+  DB  197,244,88,202                      ; vaddps        %ymm2,%ymm1,%ymm1
+  DB  197,164,89,192                      ; vmulps        %ymm0,%ymm11,%ymm0
+  DB  197,252,88,217                      ; vaddps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  197,124,41,210                      ; vmovaps       %ymm10,%ymm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x3_avx
+_sk_matrix_4x3_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,16                   ; vbroadcastss  (%rax),%ymm2
+  DB  196,226,125,24,88,16                ; vbroadcastss  0x10(%rax),%ymm3
+  DB  196,98,125,24,64,32                 ; vbroadcastss  0x20(%rax),%ymm8
+  DB  197,228,89,217                      ; vmulps        %ymm1,%ymm3,%ymm3
+  DB  196,193,100,88,216                  ; vaddps        %ymm8,%ymm3,%ymm3
+  DB  197,236,89,208                      ; vmulps        %ymm0,%ymm2,%ymm2
+  DB  197,108,88,195                      ; vaddps        %ymm3,%ymm2,%ymm8
+  DB  196,226,125,24,80,4                 ; vbroadcastss  0x4(%rax),%ymm2
+  DB  196,226,125,24,88,20                ; vbroadcastss  0x14(%rax),%ymm3
+  DB  196,98,125,24,72,36                 ; vbroadcastss  0x24(%rax),%ymm9
+  DB  197,228,89,217                      ; vmulps        %ymm1,%ymm3,%ymm3
+  DB  196,193,100,88,217                  ; vaddps        %ymm9,%ymm3,%ymm3
+  DB  197,236,89,208                      ; vmulps        %ymm0,%ymm2,%ymm2
+  DB  197,108,88,203                      ; vaddps        %ymm3,%ymm2,%ymm9
+  DB  196,226,125,24,80,8                 ; vbroadcastss  0x8(%rax),%ymm2
+  DB  196,226,125,24,88,24                ; vbroadcastss  0x18(%rax),%ymm3
+  DB  196,98,125,24,80,40                 ; vbroadcastss  0x28(%rax),%ymm10
+  DB  197,228,89,217                      ; vmulps        %ymm1,%ymm3,%ymm3
+  DB  196,193,100,88,218                  ; vaddps        %ymm10,%ymm3,%ymm3
+  DB  197,236,89,208                      ; vmulps        %ymm0,%ymm2,%ymm2
+  DB  197,236,88,211                      ; vaddps        %ymm3,%ymm2,%ymm2
+  DB  196,226,125,24,88,12                ; vbroadcastss  0xc(%rax),%ymm3
+  DB  196,98,125,24,80,28                 ; vbroadcastss  0x1c(%rax),%ymm10
+  DB  196,98,125,24,88,44                 ; vbroadcastss  0x2c(%rax),%ymm11
+  DB  197,172,89,201                      ; vmulps        %ymm1,%ymm10,%ymm1
+  DB  196,193,116,88,203                  ; vaddps        %ymm11,%ymm1,%ymm1
+  DB  197,228,89,192                      ; vmulps        %ymm0,%ymm3,%ymm0
+  DB  197,252,88,217                      ; vaddps        %ymm1,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,124,41,201                      ; vmovaps       %ymm9,%ymm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_perspective_avx
+_sk_matrix_perspective_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,0                     ; vbroadcastss  (%rax),%ymm8
+  DB  196,98,125,24,72,4                  ; vbroadcastss  0x4(%rax),%ymm9
+  DB  196,98,125,24,80,8                  ; vbroadcastss  0x8(%rax),%ymm10
+  DB  197,52,89,201                       ; vmulps        %ymm1,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  197,60,89,192                       ; vmulps        %ymm0,%ymm8,%ymm8
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,72,12                 ; vbroadcastss  0xc(%rax),%ymm9
+  DB  196,98,125,24,80,16                 ; vbroadcastss  0x10(%rax),%ymm10
+  DB  196,98,125,24,88,20                 ; vbroadcastss  0x14(%rax),%ymm11
+  DB  197,44,89,209                       ; vmulps        %ymm1,%ymm10,%ymm10
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  197,52,89,200                       ; vmulps        %ymm0,%ymm9,%ymm9
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,80,24                 ; vbroadcastss  0x18(%rax),%ymm10
+  DB  196,98,125,24,88,28                 ; vbroadcastss  0x1c(%rax),%ymm11
+  DB  196,98,125,24,96,32                 ; vbroadcastss  0x20(%rax),%ymm12
+  DB  197,164,89,201                      ; vmulps        %ymm1,%ymm11,%ymm1
+  DB  196,193,116,88,204                  ; vaddps        %ymm12,%ymm1,%ymm1
+  DB  197,172,89,192                      ; vmulps        %ymm0,%ymm10,%ymm0
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,252,83,200                      ; vrcpps        %ymm0,%ymm1
+  DB  197,188,89,193                      ; vmulps        %ymm1,%ymm8,%ymm0
+  DB  197,180,89,201                      ; vmulps        %ymm1,%ymm9,%ymm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_gradient_avx
+_sk_evenly_spaced_gradient_avx LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,131,236,40                       ; sub           $0x28,%rsp
+  DB  197,252,17,60,36                    ; vmovups       %ymm7,(%rsp)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,24                           ; mov           (%rax),%rbx
+  DB  72,139,104,8                        ; mov           0x8(%rax),%rbp
+  DB  72,255,203                          ; dec           %rbx
+  DB  120,7                               ; js            5876 <_sk_evenly_spaced_gradient_avx+0x28>
+  DB  196,225,242,42,203                  ; vcvtsi2ss     %rbx,%xmm1,%xmm1
+  DB  235,21                              ; jmp           588b <_sk_evenly_spaced_gradient_avx+0x3d>
+  DB  73,137,217                          ; mov           %rbx,%r9
+  DB  73,209,233                          ; shr           %r9
+  DB  131,227,1                           ; and           $0x1,%ebx
+  DB  76,9,203                            ; or            %r9,%rbx
+  DB  196,225,242,42,203                  ; vcvtsi2ss     %rbx,%xmm1,%xmm1
+  DB  197,242,88,201                      ; vaddss        %xmm1,%xmm1,%xmm1
+  DB  196,227,121,4,201,0                 ; vpermilps     $0x0,%xmm1,%xmm1
+  DB  196,227,117,24,201,1                ; vinsertf128   $0x1,%xmm1,%ymm1,%ymm1
+  DB  197,244,89,200                      ; vmulps        %ymm0,%ymm1,%ymm1
+  DB  197,254,91,201                      ; vcvttps2dq    %ymm1,%ymm1
+  DB  196,195,249,22,201,1                ; vpextrq       $0x1,%xmm1,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,193,249,126,203                 ; vmovq         %xmm1,%r11
+  DB  69,137,222                          ; mov           %r11d,%r14d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,195,249,22,204,1                ; vpextrq       $0x1,%xmm1,%r12
+  DB  69,137,231                          ; mov           %r12d,%r15d
+  DB  73,193,236,32                       ; shr           $0x20,%r12
+  DB  196,225,249,126,203                 ; vmovq         %xmm1,%rbx
+  DB  65,137,221                          ; mov           %ebx,%r13d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  196,161,122,16,76,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm1
+  DB  196,227,113,33,76,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  DB  196,161,122,16,84,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm2
+  DB  196,33,122,16,68,165,0              ; vmovss        0x0(%rbp,%r12,4),%xmm8
+  DB  196,161,122,16,92,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm3
+  DB  196,35,97,33,76,157,0,16            ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm9
+  DB  196,161,122,16,124,149,0            ; vmovss        0x0(%rbp,%r10,4),%xmm7
+  DB  196,33,122,16,92,141,0              ; vmovss        0x0(%rbp,%r9,4),%xmm11
+  DB  196,99,113,33,226,32                ; vinsertps     $0x20,%xmm2,%xmm1,%xmm12
+  DB  72,139,104,40                       ; mov           0x28(%rax),%rbp
+  DB  196,161,122,16,84,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm2
+  DB  196,99,105,33,108,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm13
+  DB  196,161,122,16,92,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm3
+  DB  196,161,122,16,76,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm1
+  DB  196,161,122,16,84,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm2
+  DB  196,35,105,33,116,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm14
+  DB  196,33,122,16,124,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm15
+  DB  196,33,122,16,84,141,0              ; vmovss        0x0(%rbp,%r9,4),%xmm10
+  DB  196,67,25,33,192,48                 ; vinsertps     $0x30,%xmm8,%xmm12,%xmm8
+  DB  196,227,49,33,215,32                ; vinsertps     $0x20,%xmm7,%xmm9,%xmm2
+  DB  196,195,105,33,211,48               ; vinsertps     $0x30,%xmm11,%xmm2,%xmm2
+  DB  196,67,109,24,192,1                 ; vinsertf128   $0x1,%xmm8,%ymm2,%ymm8
+  DB  196,227,17,33,211,32                ; vinsertps     $0x20,%xmm3,%xmm13,%xmm2
+  DB  196,99,105,33,201,48                ; vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  DB  72,139,104,16                       ; mov           0x10(%rax),%rbp
+  DB  196,161,122,16,84,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm2
+  DB  196,99,105,33,92,157,0,16           ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm11
+  DB  196,33,122,16,100,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm12
+  DB  196,161,122,16,76,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm1
+  DB  196,161,122,16,124,181,0            ; vmovss        0x0(%rbp,%r14,4),%xmm7
+  DB  196,163,65,33,124,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  DB  196,161,122,16,92,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm3
+  DB  196,33,122,16,108,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm13
+  DB  196,195,9,33,215,32                 ; vinsertps     $0x20,%xmm15,%xmm14,%xmm2
+  DB  196,195,105,33,210,48               ; vinsertps     $0x30,%xmm10,%xmm2,%xmm2
+  DB  196,67,109,24,241,1                 ; vinsertf128   $0x1,%xmm9,%ymm2,%ymm14
+  DB  196,195,33,33,212,32                ; vinsertps     $0x20,%xmm12,%xmm11,%xmm2
+  DB  196,99,105,33,201,48                ; vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  DB  196,99,65,33,211,32                 ; vinsertps     $0x20,%xmm3,%xmm7,%xmm10
+  DB  72,139,104,48                       ; mov           0x30(%rax),%rbp
+  DB  196,161,122,16,92,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm3
+  DB  196,99,97,33,92,157,0,16            ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  DB  196,33,122,16,124,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm15
+  DB  196,33,122,16,100,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm12
+  DB  196,161,122,16,84,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm2
+  DB  196,163,105,33,84,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  DB  196,161,122,16,124,149,0            ; vmovss        0x0(%rbp,%r10,4),%xmm7
+  DB  196,161,122,16,92,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm3
+  DB  196,67,41,33,213,48                 ; vinsertps     $0x30,%xmm13,%xmm10,%xmm10
+  DB  196,67,45,24,233,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm13
+  DB  196,195,33,33,207,32                ; vinsertps     $0x20,%xmm15,%xmm11,%xmm1
+  DB  196,67,113,33,204,48                ; vinsertps     $0x30,%xmm12,%xmm1,%xmm9
+  DB  196,227,105,33,215,32               ; vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  DB  196,99,105,33,211,48                ; vinsertps     $0x30,%xmm3,%xmm2,%xmm10
+  DB  72,139,104,24                       ; mov           0x18(%rax),%rbp
+  DB  196,161,122,16,92,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm3
+  DB  196,99,97,33,92,157,0,16            ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  DB  196,33,122,16,100,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm12
+  DB  196,33,122,16,124,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm15
+  DB  196,161,122,16,84,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm2
+  DB  196,163,105,33,84,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  DB  196,161,122,16,92,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm3
+  DB  196,161,122,16,124,141,0            ; vmovss        0x0(%rbp,%r9,4),%xmm7
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,195,33,33,204,32                ; vinsertps     $0x20,%xmm12,%xmm11,%xmm1
+  DB  196,195,113,33,207,48               ; vinsertps     $0x30,%xmm15,%xmm1,%xmm1
+  DB  196,227,105,33,211,32               ; vinsertps     $0x20,%xmm3,%xmm2,%xmm2
+  DB  196,227,105,33,215,48               ; vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  DB  196,99,109,24,209,1                 ; vinsertf128   $0x1,%xmm1,%ymm2,%ymm10
+  DB  72,139,104,56                       ; mov           0x38(%rax),%rbp
+  DB  196,161,122,16,76,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm1
+  DB  196,99,113,33,92,157,0,16           ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm11
+  DB  196,33,122,16,100,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm12
+  DB  196,33,122,16,124,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm15
+  DB  196,161,122,16,124,181,0            ; vmovss        0x0(%rbp,%r14,4),%xmm7
+  DB  196,163,65,33,124,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  DB  196,161,122,16,76,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm1
+  DB  196,161,122,16,84,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm2
+  DB  196,195,33,33,220,32                ; vinsertps     $0x20,%xmm12,%xmm11,%xmm3
+  DB  196,195,97,33,223,48                ; vinsertps     $0x30,%xmm15,%xmm3,%xmm3
+  DB  196,227,65,33,201,32                ; vinsertps     $0x20,%xmm1,%xmm7,%xmm1
+  DB  196,227,113,33,202,48               ; vinsertps     $0x30,%xmm2,%xmm1,%xmm1
+  DB  196,99,117,24,219,1                 ; vinsertf128   $0x1,%xmm3,%ymm1,%ymm11
+  DB  72,139,104,32                       ; mov           0x20(%rax),%rbp
+  DB  196,161,122,16,76,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm1
+  DB  196,227,113,33,76,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  DB  196,161,122,16,84,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm2
+  DB  196,227,113,33,202,32               ; vinsertps     $0x20,%xmm2,%xmm1,%xmm1
+  DB  196,161,122,16,84,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm2
+  DB  196,161,122,16,92,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm3
+  DB  196,35,97,33,100,157,0,16           ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm12
+  DB  196,161,122,16,124,149,0            ; vmovss        0x0(%rbp,%r10,4),%xmm7
+  DB  196,161,122,16,92,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm3
+  DB  196,99,113,33,250,48                ; vinsertps     $0x30,%xmm2,%xmm1,%xmm15
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  196,161,122,16,20,168               ; vmovss        (%rax,%r13,4),%xmm2
+  DB  196,227,105,33,20,152,16            ; vinsertps     $0x10,(%rax,%rbx,4),%xmm2,%xmm2
+  DB  196,227,25,33,255,32                ; vinsertps     $0x20,%xmm7,%xmm12,%xmm7
+  DB  196,161,122,16,12,184               ; vmovss        (%rax,%r15,4),%xmm1
+  DB  196,227,65,33,219,48                ; vinsertps     $0x30,%xmm3,%xmm7,%xmm3
+  DB  196,161,122,16,60,160               ; vmovss        (%rax,%r12,4),%xmm7
+  DB  196,67,101,24,231,1                 ; vinsertf128   $0x1,%xmm15,%ymm3,%ymm12
+  DB  196,161,122,16,28,176               ; vmovss        (%rax,%r14,4),%xmm3
+  DB  196,163,97,33,28,152,16             ; vinsertps     $0x10,(%rax,%r11,4),%xmm3,%xmm3
+  DB  196,227,105,33,201,32               ; vinsertps     $0x20,%xmm1,%xmm2,%xmm1
+  DB  196,161,122,16,20,144               ; vmovss        (%rax,%r10,4),%xmm2
+  DB  196,227,113,33,207,48               ; vinsertps     $0x30,%xmm7,%xmm1,%xmm1
+  DB  196,161,122,16,60,136               ; vmovss        (%rax,%r9,4),%xmm7
+  DB  196,227,97,33,210,32                ; vinsertps     $0x20,%xmm2,%xmm3,%xmm2
+  DB  196,227,105,33,215,48               ; vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  DB  196,227,109,24,217,1                ; vinsertf128   $0x1,%xmm1,%ymm2,%ymm3
+  DB  197,188,89,200                      ; vmulps        %ymm0,%ymm8,%ymm1
+  DB  196,65,116,88,198                   ; vaddps        %ymm14,%ymm1,%ymm8
+  DB  197,148,89,200                      ; vmulps        %ymm0,%ymm13,%ymm1
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  197,172,89,208                      ; vmulps        %ymm0,%ymm10,%ymm2
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  197,156,89,192                      ; vmulps        %ymm0,%ymm12,%ymm0
+  DB  197,252,88,219                      ; vaddps        %ymm3,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,252,16,60,36                    ; vmovups       (%rsp),%ymm7
+  DB  72,131,196,40                       ; add           $0x28,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gauss_a_to_rgba_avx
+_sk_gauss_a_to_rgba_avx LABEL PROC
+  DB  196,226,125,24,5,150,13,0,0         ; vbroadcastss  0xd96(%rip),%ymm0        # 6988 <_sk_callback_avx+0x484>
+  DB  197,228,89,192                      ; vmulps        %ymm0,%ymm3,%ymm0
+  DB  196,226,125,24,13,141,13,0,0        ; vbroadcastss  0xd8d(%rip),%ymm1        # 698c <_sk_callback_avx+0x488>
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  196,226,125,24,13,128,13,0,0        ; vbroadcastss  0xd80(%rip),%ymm1        # 6990 <_sk_callback_avx+0x48c>
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  196,226,125,24,13,115,13,0,0        ; vbroadcastss  0xd73(%rip),%ymm1        # 6994 <_sk_callback_avx+0x490>
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  197,252,89,195                      ; vmulps        %ymm3,%ymm0,%ymm0
+  DB  196,226,125,24,13,102,13,0,0        ; vbroadcastss  0xd66(%rip),%ymm1        # 6998 <_sk_callback_avx+0x494>
+  DB  197,252,88,193                      ; vaddps        %ymm1,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,252,40,200                      ; vmovaps       %ymm0,%ymm1
+  DB  197,252,40,208                      ; vmovaps       %ymm0,%ymm2
+  DB  197,252,40,216                      ; vmovaps       %ymm0,%ymm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gradient_avx
+_sk_gradient_avx LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,131,236,40                       ; sub           $0x28,%rsp
+  DB  197,252,17,60,36                    ; vmovups       %ymm7,(%rsp)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  73,131,249,2                        ; cmp           $0x2,%r9
+  DB  114,80                              ; jb            5cb8 <_sk_gradient_avx+0x72>
+  DB  72,139,88,72                        ; mov           0x48(%rax),%rbx
+  DB  73,255,201                          ; dec           %r9
+  DB  72,131,195,4                        ; add           $0x4,%rbx
+  DB  196,65,52,87,201                    ; vxorps        %ymm9,%ymm9,%ymm9
+  DB  196,98,125,24,21,27,13,0,0          ; vbroadcastss  0xd1b(%rip),%ymm10        # 699c <_sk_callback_avx+0x498>
+  DB  197,244,87,201                      ; vxorps        %ymm1,%ymm1,%ymm1
+  DB  196,98,125,24,3                     ; vbroadcastss  (%rbx),%ymm8
+  DB  197,60,194,192,2                    ; vcmpleps      %ymm0,%ymm8,%ymm8
+  DB  196,67,53,74,194,128                ; vblendvps     %ymm8,%ymm10,%ymm9,%ymm8
+  DB  196,99,125,25,194,1                 ; vextractf128  $0x1,%ymm8,%xmm2
+  DB  196,227,125,25,203,1                ; vextractf128  $0x1,%ymm1,%xmm3
+  DB  197,233,254,211                     ; vpaddd        %xmm3,%xmm2,%xmm2
+  DB  197,185,254,201                     ; vpaddd        %xmm1,%xmm8,%xmm1
+  DB  196,227,117,24,202,1                ; vinsertf128   $0x1,%xmm2,%ymm1,%ymm1
+  DB  72,131,195,4                        ; add           $0x4,%rbx
+  DB  73,255,201                          ; dec           %r9
+  DB  117,205                             ; jne           5c85 <_sk_gradient_avx+0x3f>
+  DB  196,195,249,22,201,1                ; vpextrq       $0x1,%xmm1,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  196,193,249,126,203                 ; vmovq         %xmm1,%r11
+  DB  69,137,222                          ; mov           %r11d,%r14d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  196,227,125,25,201,1                ; vextractf128  $0x1,%ymm1,%xmm1
+  DB  196,195,249,22,204,1                ; vpextrq       $0x1,%xmm1,%r12
+  DB  69,137,231                          ; mov           %r12d,%r15d
+  DB  73,193,236,32                       ; shr           $0x20,%r12
+  DB  196,225,249,126,203                 ; vmovq         %xmm1,%rbx
+  DB  65,137,221                          ; mov           %ebx,%r13d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  72,139,104,8                        ; mov           0x8(%rax),%rbp
+  DB  196,161,122,16,76,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm1
+  DB  196,227,113,33,76,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  DB  196,161,122,16,84,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm2
+  DB  196,33,122,16,68,165,0              ; vmovss        0x0(%rbp,%r12,4),%xmm8
+  DB  196,161,122,16,92,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm3
+  DB  196,35,97,33,76,157,0,16            ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm9
+  DB  196,161,122,16,124,149,0            ; vmovss        0x0(%rbp,%r10,4),%xmm7
+  DB  196,33,122,16,92,141,0              ; vmovss        0x0(%rbp,%r9,4),%xmm11
+  DB  196,99,113,33,226,32                ; vinsertps     $0x20,%xmm2,%xmm1,%xmm12
+  DB  72,139,104,40                       ; mov           0x28(%rax),%rbp
+  DB  196,161,122,16,84,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm2
+  DB  196,99,105,33,108,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm13
+  DB  196,161,122,16,92,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm3
+  DB  196,161,122,16,76,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm1
+  DB  196,161,122,16,84,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm2
+  DB  196,35,105,33,116,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm14
+  DB  196,33,122,16,124,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm15
+  DB  196,33,122,16,84,141,0              ; vmovss        0x0(%rbp,%r9,4),%xmm10
+  DB  196,67,25,33,192,48                 ; vinsertps     $0x30,%xmm8,%xmm12,%xmm8
+  DB  196,227,49,33,215,32                ; vinsertps     $0x20,%xmm7,%xmm9,%xmm2
+  DB  196,195,105,33,211,48               ; vinsertps     $0x30,%xmm11,%xmm2,%xmm2
+  DB  196,67,109,24,192,1                 ; vinsertf128   $0x1,%xmm8,%ymm2,%ymm8
+  DB  196,227,17,33,211,32                ; vinsertps     $0x20,%xmm3,%xmm13,%xmm2
+  DB  196,99,105,33,201,48                ; vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  DB  72,139,104,16                       ; mov           0x10(%rax),%rbp
+  DB  196,161,122,16,84,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm2
+  DB  196,99,105,33,92,157,0,16           ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm2,%xmm11
+  DB  196,33,122,16,100,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm12
+  DB  196,161,122,16,76,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm1
+  DB  196,161,122,16,124,181,0            ; vmovss        0x0(%rbp,%r14,4),%xmm7
+  DB  196,163,65,33,124,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  DB  196,161,122,16,92,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm3
+  DB  196,33,122,16,108,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm13
+  DB  196,195,9,33,215,32                 ; vinsertps     $0x20,%xmm15,%xmm14,%xmm2
+  DB  196,195,105,33,210,48               ; vinsertps     $0x30,%xmm10,%xmm2,%xmm2
+  DB  196,67,109,24,241,1                 ; vinsertf128   $0x1,%xmm9,%ymm2,%ymm14
+  DB  196,195,33,33,212,32                ; vinsertps     $0x20,%xmm12,%xmm11,%xmm2
+  DB  196,99,105,33,201,48                ; vinsertps     $0x30,%xmm1,%xmm2,%xmm9
+  DB  196,99,65,33,211,32                 ; vinsertps     $0x20,%xmm3,%xmm7,%xmm10
+  DB  72,139,104,48                       ; mov           0x30(%rax),%rbp
+  DB  196,161,122,16,92,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm3
+  DB  196,99,97,33,92,157,0,16            ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  DB  196,33,122,16,124,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm15
+  DB  196,33,122,16,100,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm12
+  DB  196,161,122,16,84,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm2
+  DB  196,163,105,33,84,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  DB  196,161,122,16,124,149,0            ; vmovss        0x0(%rbp,%r10,4),%xmm7
+  DB  196,161,122,16,92,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm3
+  DB  196,67,41,33,213,48                 ; vinsertps     $0x30,%xmm13,%xmm10,%xmm10
+  DB  196,67,45,24,233,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm13
+  DB  196,195,33,33,207,32                ; vinsertps     $0x20,%xmm15,%xmm11,%xmm1
+  DB  196,67,113,33,204,48                ; vinsertps     $0x30,%xmm12,%xmm1,%xmm9
+  DB  196,227,105,33,215,32               ; vinsertps     $0x20,%xmm7,%xmm2,%xmm2
+  DB  196,99,105,33,211,48                ; vinsertps     $0x30,%xmm3,%xmm2,%xmm10
+  DB  72,139,104,24                       ; mov           0x18(%rax),%rbp
+  DB  196,161,122,16,92,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm3
+  DB  196,99,97,33,92,157,0,16            ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm3,%xmm11
+  DB  196,33,122,16,100,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm12
+  DB  196,33,122,16,124,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm15
+  DB  196,161,122,16,84,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm2
+  DB  196,163,105,33,84,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm2,%xmm2
+  DB  196,161,122,16,92,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm3
+  DB  196,161,122,16,124,141,0            ; vmovss        0x0(%rbp,%r9,4),%xmm7
+  DB  196,67,45,24,201,1                  ; vinsertf128   $0x1,%xmm9,%ymm10,%ymm9
+  DB  196,195,33,33,204,32                ; vinsertps     $0x20,%xmm12,%xmm11,%xmm1
+  DB  196,195,113,33,207,48               ; vinsertps     $0x30,%xmm15,%xmm1,%xmm1
+  DB  196,227,105,33,211,32               ; vinsertps     $0x20,%xmm3,%xmm2,%xmm2
+  DB  196,227,105,33,215,48               ; vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  DB  196,99,109,24,209,1                 ; vinsertf128   $0x1,%xmm1,%ymm2,%ymm10
+  DB  72,139,104,56                       ; mov           0x38(%rax),%rbp
+  DB  196,161,122,16,76,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm1
+  DB  196,99,113,33,92,157,0,16           ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm11
+  DB  196,33,122,16,100,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm12
+  DB  196,33,122,16,124,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm15
+  DB  196,161,122,16,124,181,0            ; vmovss        0x0(%rbp,%r14,4),%xmm7
+  DB  196,163,65,33,124,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm7,%xmm7
+  DB  196,161,122,16,76,149,0             ; vmovss        0x0(%rbp,%r10,4),%xmm1
+  DB  196,161,122,16,84,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm2
+  DB  196,195,33,33,220,32                ; vinsertps     $0x20,%xmm12,%xmm11,%xmm3
+  DB  196,195,97,33,223,48                ; vinsertps     $0x30,%xmm15,%xmm3,%xmm3
+  DB  196,227,65,33,201,32                ; vinsertps     $0x20,%xmm1,%xmm7,%xmm1
+  DB  196,227,113,33,202,48               ; vinsertps     $0x30,%xmm2,%xmm1,%xmm1
+  DB  196,99,117,24,219,1                 ; vinsertf128   $0x1,%xmm3,%ymm1,%ymm11
+  DB  72,139,104,32                       ; mov           0x20(%rax),%rbp
+  DB  196,161,122,16,76,173,0             ; vmovss        0x0(%rbp,%r13,4),%xmm1
+  DB  196,227,113,33,76,157,0,16          ; vinsertps     $0x10,0x0(%rbp,%rbx,4),%xmm1,%xmm1
+  DB  196,161,122,16,84,189,0             ; vmovss        0x0(%rbp,%r15,4),%xmm2
+  DB  196,227,113,33,202,32               ; vinsertps     $0x20,%xmm2,%xmm1,%xmm1
+  DB  196,161,122,16,84,165,0             ; vmovss        0x0(%rbp,%r12,4),%xmm2
+  DB  196,161,122,16,92,181,0             ; vmovss        0x0(%rbp,%r14,4),%xmm3
+  DB  196,35,97,33,100,157,0,16           ; vinsertps     $0x10,0x0(%rbp,%r11,4),%xmm3,%xmm12
+  DB  196,161,122,16,124,149,0            ; vmovss        0x0(%rbp,%r10,4),%xmm7
+  DB  196,161,122,16,92,141,0             ; vmovss        0x0(%rbp,%r9,4),%xmm3
+  DB  196,99,113,33,250,48                ; vinsertps     $0x30,%xmm2,%xmm1,%xmm15
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  196,161,122,16,20,168               ; vmovss        (%rax,%r13,4),%xmm2
+  DB  196,227,105,33,20,152,16            ; vinsertps     $0x10,(%rax,%rbx,4),%xmm2,%xmm2
+  DB  196,227,25,33,255,32                ; vinsertps     $0x20,%xmm7,%xmm12,%xmm7
+  DB  196,161,122,16,12,184               ; vmovss        (%rax,%r15,4),%xmm1
+  DB  196,227,65,33,219,48                ; vinsertps     $0x30,%xmm3,%xmm7,%xmm3
+  DB  196,161,122,16,60,160               ; vmovss        (%rax,%r12,4),%xmm7
+  DB  196,67,101,24,231,1                 ; vinsertf128   $0x1,%xmm15,%ymm3,%ymm12
+  DB  196,161,122,16,28,176               ; vmovss        (%rax,%r14,4),%xmm3
+  DB  196,163,97,33,28,152,16             ; vinsertps     $0x10,(%rax,%r11,4),%xmm3,%xmm3
+  DB  196,227,105,33,201,32               ; vinsertps     $0x20,%xmm1,%xmm2,%xmm1
+  DB  196,161,122,16,20,144               ; vmovss        (%rax,%r10,4),%xmm2
+  DB  196,227,113,33,207,48               ; vinsertps     $0x30,%xmm7,%xmm1,%xmm1
+  DB  196,161,122,16,60,136               ; vmovss        (%rax,%r9,4),%xmm7
+  DB  196,227,97,33,210,32                ; vinsertps     $0x20,%xmm2,%xmm3,%xmm2
+  DB  196,227,105,33,215,48               ; vinsertps     $0x30,%xmm7,%xmm2,%xmm2
+  DB  196,227,109,24,217,1                ; vinsertf128   $0x1,%xmm1,%ymm2,%ymm3
+  DB  197,188,89,200                      ; vmulps        %ymm0,%ymm8,%ymm1
+  DB  196,65,116,88,198                   ; vaddps        %ymm14,%ymm1,%ymm8
+  DB  197,148,89,200                      ; vmulps        %ymm0,%ymm13,%ymm1
+  DB  196,193,116,88,201                  ; vaddps        %ymm9,%ymm1,%ymm1
+  DB  197,172,89,208                      ; vmulps        %ymm0,%ymm10,%ymm2
+  DB  196,193,108,88,211                  ; vaddps        %ymm11,%ymm2,%ymm2
+  DB  197,156,89,192                      ; vmulps        %ymm0,%ymm12,%ymm0
+  DB  197,252,88,219                      ; vaddps        %ymm3,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  197,252,16,60,36                    ; vmovups       (%rsp),%ymm7
+  DB  72,131,196,40                       ; add           $0x28,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_2_stop_gradient_avx
+_sk_evenly_spaced_2_stop_gradient_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,8                    ; vbroadcastss  (%rax),%ymm1
+  DB  196,226,125,24,80,16                ; vbroadcastss  0x10(%rax),%ymm2
+  DB  197,244,89,200                      ; vmulps        %ymm0,%ymm1,%ymm1
+  DB  197,116,88,194                      ; vaddps        %ymm2,%ymm1,%ymm8
+  DB  196,226,125,24,72,4                 ; vbroadcastss  0x4(%rax),%ymm1
+  DB  196,226,125,24,80,20                ; vbroadcastss  0x14(%rax),%ymm2
+  DB  197,244,89,200                      ; vmulps        %ymm0,%ymm1,%ymm1
+  DB  197,244,88,202                      ; vaddps        %ymm2,%ymm1,%ymm1
+  DB  196,226,125,24,80,8                 ; vbroadcastss  0x8(%rax),%ymm2
+  DB  196,226,125,24,88,24                ; vbroadcastss  0x18(%rax),%ymm3
+  DB  197,236,89,208                      ; vmulps        %ymm0,%ymm2,%ymm2
+  DB  197,236,88,211                      ; vaddps        %ymm3,%ymm2,%ymm2
+  DB  196,226,125,24,88,12                ; vbroadcastss  0xc(%rax),%ymm3
+  DB  196,98,125,24,72,28                 ; vbroadcastss  0x1c(%rax),%ymm9
+  DB  197,228,89,192                      ; vmulps        %ymm0,%ymm3,%ymm0
+  DB  196,193,124,88,217                  ; vaddps        %ymm9,%ymm0,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,41,192                      ; vmovaps       %ymm8,%ymm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_unit_angle_avx
+_sk_xy_to_unit_angle_avx LABEL PROC
+  DB  196,65,60,87,192                    ; vxorps        %ymm8,%ymm8,%ymm8
+  DB  197,60,92,200                       ; vsubps        %ymm0,%ymm8,%ymm9
+  DB  197,52,84,200                       ; vandps        %ymm0,%ymm9,%ymm9
+  DB  197,60,92,209                       ; vsubps        %ymm1,%ymm8,%ymm10
+  DB  197,44,84,209                       ; vandps        %ymm1,%ymm10,%ymm10
+  DB  196,65,52,93,218                    ; vminps        %ymm10,%ymm9,%ymm11
+  DB  196,65,52,95,226                    ; vmaxps        %ymm10,%ymm9,%ymm12
+  DB  196,65,36,94,220                    ; vdivps        %ymm12,%ymm11,%ymm11
+  DB  196,65,36,89,227                    ; vmulps        %ymm11,%ymm11,%ymm12
+  DB  196,98,125,24,45,14,9,0,0           ; vbroadcastss  0x90e(%rip),%ymm13        # 69a0 <_sk_callback_avx+0x49c>
+  DB  196,65,28,89,237                    ; vmulps        %ymm13,%ymm12,%ymm13
+  DB  196,98,125,24,53,4,9,0,0            ; vbroadcastss  0x904(%rip),%ymm14        # 69a4 <_sk_callback_avx+0x4a0>
+  DB  196,65,20,88,238                    ; vaddps        %ymm14,%ymm13,%ymm13
+  DB  196,65,28,89,237                    ; vmulps        %ymm13,%ymm12,%ymm13
+  DB  196,98,125,24,53,245,8,0,0          ; vbroadcastss  0x8f5(%rip),%ymm14        # 69a8 <_sk_callback_avx+0x4a4>
+  DB  196,65,20,88,238                    ; vaddps        %ymm14,%ymm13,%ymm13
+  DB  196,65,28,89,229                    ; vmulps        %ymm13,%ymm12,%ymm12
+  DB  196,98,125,24,45,230,8,0,0          ; vbroadcastss  0x8e6(%rip),%ymm13        # 69ac <_sk_callback_avx+0x4a8>
+  DB  196,65,28,88,229                    ; vaddps        %ymm13,%ymm12,%ymm12
+  DB  196,65,36,89,220                    ; vmulps        %ymm12,%ymm11,%ymm11
+  DB  196,65,52,194,202,1                 ; vcmpltps      %ymm10,%ymm9,%ymm9
+  DB  196,98,125,24,21,209,8,0,0          ; vbroadcastss  0x8d1(%rip),%ymm10        # 69b0 <_sk_callback_avx+0x4ac>
+  DB  196,65,44,92,211                    ; vsubps        %ymm11,%ymm10,%ymm10
+  DB  196,67,37,74,202,144                ; vblendvps     %ymm9,%ymm10,%ymm11,%ymm9
+  DB  196,193,124,194,192,1               ; vcmpltps      %ymm8,%ymm0,%ymm0
+  DB  196,98,125,24,21,187,8,0,0          ; vbroadcastss  0x8bb(%rip),%ymm10        # 69b4 <_sk_callback_avx+0x4b0>
+  DB  196,65,44,92,209                    ; vsubps        %ymm9,%ymm10,%ymm10
+  DB  196,195,53,74,194,0                 ; vblendvps     %ymm0,%ymm10,%ymm9,%ymm0
+  DB  196,65,116,194,200,1                ; vcmpltps      %ymm8,%ymm1,%ymm9
+  DB  196,98,125,24,21,165,8,0,0          ; vbroadcastss  0x8a5(%rip),%ymm10        # 69b8 <_sk_callback_avx+0x4b4>
+  DB  197,44,92,208                       ; vsubps        %ymm0,%ymm10,%ymm10
+  DB  196,195,125,74,194,144              ; vblendvps     %ymm9,%ymm10,%ymm0,%ymm0
+  DB  196,65,124,194,200,3                ; vcmpunordps   %ymm8,%ymm0,%ymm9
+  DB  196,195,125,74,192,144              ; vblendvps     %ymm9,%ymm8,%ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_radius_avx
+_sk_xy_to_radius_avx LABEL PROC
+  DB  197,252,89,192                      ; vmulps        %ymm0,%ymm0,%ymm0
+  DB  197,116,89,193                      ; vmulps        %ymm1,%ymm1,%ymm8
+  DB  196,193,124,88,192                  ; vaddps        %ymm8,%ymm0,%ymm0
+  DB  197,252,81,192                      ; vsqrtps       %ymm0,%ymm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_save_xy_avx
+_sk_save_xy_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,111,8,0,0           ; vbroadcastss  0x86f(%rip),%ymm8        # 69bc <_sk_callback_avx+0x4b8>
+  DB  196,65,124,88,200                   ; vaddps        %ymm8,%ymm0,%ymm9
+  DB  196,67,125,8,209,1                  ; vroundps      $0x1,%ymm9,%ymm10
+  DB  196,65,52,92,202                    ; vsubps        %ymm10,%ymm9,%ymm9
+  DB  196,65,116,88,192                   ; vaddps        %ymm8,%ymm1,%ymm8
+  DB  196,67,125,8,208,1                  ; vroundps      $0x1,%ymm8,%ymm10
+  DB  196,65,60,92,194                    ; vsubps        %ymm10,%ymm8,%ymm8
+  DB  197,252,17,0                        ; vmovups       %ymm0,(%rax)
+  DB  197,252,17,72,32                    ; vmovups       %ymm1,0x20(%rax)
+  DB  197,124,17,72,64                    ; vmovups       %ymm9,0x40(%rax)
+  DB  197,124,17,64,96                    ; vmovups       %ymm8,0x60(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_accumulate_avx
+_sk_accumulate_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  197,124,16,128,128,0,0,0            ; vmovups       0x80(%rax),%ymm8
+  DB  197,60,89,128,160,0,0,0             ; vmulps        0xa0(%rax),%ymm8,%ymm8
+  DB  197,60,89,200                       ; vmulps        %ymm0,%ymm8,%ymm9
+  DB  197,180,88,228                      ; vaddps        %ymm4,%ymm9,%ymm4
+  DB  197,60,89,201                       ; vmulps        %ymm1,%ymm8,%ymm9
+  DB  197,180,88,237                      ; vaddps        %ymm5,%ymm9,%ymm5
+  DB  197,60,89,202                       ; vmulps        %ymm2,%ymm8,%ymm9
+  DB  197,180,88,246                      ; vaddps        %ymm6,%ymm9,%ymm6
+  DB  197,60,89,195                       ; vmulps        %ymm3,%ymm8,%ymm8
+  DB  197,188,88,255                      ; vaddps        %ymm7,%ymm8,%ymm7
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_nx_avx
+_sk_bilinear_nx_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,251,7,0,0          ; vbroadcastss  0x7fb(%rip),%ymm0        # 69c0 <_sk_callback_avx+0x4bc>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  196,98,125,24,5,242,7,0,0           ; vbroadcastss  0x7f2(%rip),%ymm8        # 69c4 <_sk_callback_avx+0x4c0>
+  DB  197,60,92,64,64                     ; vsubps        0x40(%rax),%ymm8,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_px_avx
+_sk_bilinear_px_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,218,7,0,0          ; vbroadcastss  0x7da(%rip),%ymm0        # 69c8 <_sk_callback_avx+0x4c4>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  197,124,16,64,64                    ; vmovups       0x40(%rax),%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_ny_avx
+_sk_bilinear_ny_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,190,7,0,0         ; vbroadcastss  0x7be(%rip),%ymm1        # 69cc <_sk_callback_avx+0x4c8>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  196,98,125,24,5,180,7,0,0           ; vbroadcastss  0x7b4(%rip),%ymm8        # 69d0 <_sk_callback_avx+0x4cc>
+  DB  197,60,92,64,96                     ; vsubps        0x60(%rax),%ymm8,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_py_avx
+_sk_bilinear_py_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,156,7,0,0         ; vbroadcastss  0x79c(%rip),%ymm1        # 69d4 <_sk_callback_avx+0x4d0>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  197,124,16,64,96                    ; vmovups       0x60(%rax),%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3x_avx
+_sk_bicubic_n3x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,127,7,0,0          ; vbroadcastss  0x77f(%rip),%ymm0        # 69d8 <_sk_callback_avx+0x4d4>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  196,98,125,24,5,118,7,0,0           ; vbroadcastss  0x776(%rip),%ymm8        # 69dc <_sk_callback_avx+0x4d8>
+  DB  197,60,92,64,64                     ; vsubps        0x40(%rax),%ymm8,%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,103,7,0,0          ; vbroadcastss  0x767(%rip),%ymm10        # 69e0 <_sk_callback_avx+0x4dc>
+  DB  196,65,60,89,194                    ; vmulps        %ymm10,%ymm8,%ymm8
+  DB  196,98,125,24,21,93,7,0,0           ; vbroadcastss  0x75d(%rip),%ymm10        # 69e4 <_sk_callback_avx+0x4e0>
+  DB  196,65,60,88,194                    ; vaddps        %ymm10,%ymm8,%ymm8
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1x_avx
+_sk_bicubic_n1x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,64,7,0,0           ; vbroadcastss  0x740(%rip),%ymm0        # 69e8 <_sk_callback_avx+0x4e4>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  196,98,125,24,5,55,7,0,0            ; vbroadcastss  0x737(%rip),%ymm8        # 69ec <_sk_callback_avx+0x4e8>
+  DB  197,60,92,64,64                     ; vsubps        0x40(%rax),%ymm8,%ymm8
+  DB  196,98,125,24,13,45,7,0,0           ; vbroadcastss  0x72d(%rip),%ymm9        # 69f0 <_sk_callback_avx+0x4ec>
+  DB  196,65,60,89,201                    ; vmulps        %ymm9,%ymm8,%ymm9
+  DB  196,98,125,24,21,35,7,0,0           ; vbroadcastss  0x723(%rip),%ymm10        # 69f4 <_sk_callback_avx+0x4f0>
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,65,60,89,201                    ; vmulps        %ymm9,%ymm8,%ymm9
+  DB  196,98,125,24,21,20,7,0,0           ; vbroadcastss  0x714(%rip),%ymm10        # 69f8 <_sk_callback_avx+0x4f4>
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,13,5,7,0,0            ; vbroadcastss  0x705(%rip),%ymm9        # 69fc <_sk_callback_avx+0x4f8>
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1x_avx
+_sk_bicubic_p1x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,237,6,0,0           ; vbroadcastss  0x6ed(%rip),%ymm8        # 6a00 <_sk_callback_avx+0x4fc>
+  DB  197,188,88,0                        ; vaddps        (%rax),%ymm8,%ymm0
+  DB  197,124,16,72,64                    ; vmovups       0x40(%rax),%ymm9
+  DB  196,98,125,24,21,223,6,0,0          ; vbroadcastss  0x6df(%rip),%ymm10        # 6a04 <_sk_callback_avx+0x500>
+  DB  196,65,52,89,210                    ; vmulps        %ymm10,%ymm9,%ymm10
+  DB  196,98,125,24,29,213,6,0,0          ; vbroadcastss  0x6d5(%rip),%ymm11        # 6a08 <_sk_callback_avx+0x504>
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  196,65,52,89,210                    ; vmulps        %ymm10,%ymm9,%ymm10
+  DB  196,65,44,88,192                    ; vaddps        %ymm8,%ymm10,%ymm8
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  196,98,125,24,13,188,6,0,0          ; vbroadcastss  0x6bc(%rip),%ymm9        # 6a0c <_sk_callback_avx+0x508>
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3x_avx
+_sk_bicubic_p3x_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,5,164,6,0,0          ; vbroadcastss  0x6a4(%rip),%ymm0        # 6a10 <_sk_callback_avx+0x50c>
+  DB  197,252,88,0                        ; vaddps        (%rax),%ymm0,%ymm0
+  DB  197,124,16,64,64                    ; vmovups       0x40(%rax),%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,145,6,0,0          ; vbroadcastss  0x691(%rip),%ymm10        # 6a14 <_sk_callback_avx+0x510>
+  DB  196,65,60,89,194                    ; vmulps        %ymm10,%ymm8,%ymm8
+  DB  196,98,125,24,21,135,6,0,0          ; vbroadcastss  0x687(%rip),%ymm10        # 6a18 <_sk_callback_avx+0x514>
+  DB  196,65,60,88,194                    ; vaddps        %ymm10,%ymm8,%ymm8
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  197,124,17,128,128,0,0,0            ; vmovups       %ymm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3y_avx
+_sk_bicubic_n3y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,106,6,0,0         ; vbroadcastss  0x66a(%rip),%ymm1        # 6a1c <_sk_callback_avx+0x518>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  196,98,125,24,5,96,6,0,0            ; vbroadcastss  0x660(%rip),%ymm8        # 6a20 <_sk_callback_avx+0x51c>
+  DB  197,60,92,64,96                     ; vsubps        0x60(%rax),%ymm8,%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,81,6,0,0           ; vbroadcastss  0x651(%rip),%ymm10        # 6a24 <_sk_callback_avx+0x520>
+  DB  196,65,60,89,194                    ; vmulps        %ymm10,%ymm8,%ymm8
+  DB  196,98,125,24,21,71,6,0,0           ; vbroadcastss  0x647(%rip),%ymm10        # 6a28 <_sk_callback_avx+0x524>
+  DB  196,65,60,88,194                    ; vaddps        %ymm10,%ymm8,%ymm8
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1y_avx
+_sk_bicubic_n1y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,42,6,0,0          ; vbroadcastss  0x62a(%rip),%ymm1        # 6a2c <_sk_callback_avx+0x528>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  196,98,125,24,5,32,6,0,0            ; vbroadcastss  0x620(%rip),%ymm8        # 6a30 <_sk_callback_avx+0x52c>
+  DB  197,60,92,64,96                     ; vsubps        0x60(%rax),%ymm8,%ymm8
+  DB  196,98,125,24,13,22,6,0,0           ; vbroadcastss  0x616(%rip),%ymm9        # 6a34 <_sk_callback_avx+0x530>
+  DB  196,65,60,89,201                    ; vmulps        %ymm9,%ymm8,%ymm9
+  DB  196,98,125,24,21,12,6,0,0           ; vbroadcastss  0x60c(%rip),%ymm10        # 6a38 <_sk_callback_avx+0x534>
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,65,60,89,201                    ; vmulps        %ymm9,%ymm8,%ymm9
+  DB  196,98,125,24,21,253,5,0,0          ; vbroadcastss  0x5fd(%rip),%ymm10        # 6a3c <_sk_callback_avx+0x538>
+  DB  196,65,52,88,202                    ; vaddps        %ymm10,%ymm9,%ymm9
+  DB  196,65,60,89,193                    ; vmulps        %ymm9,%ymm8,%ymm8
+  DB  196,98,125,24,13,238,5,0,0          ; vbroadcastss  0x5ee(%rip),%ymm9        # 6a40 <_sk_callback_avx+0x53c>
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1y_avx
+_sk_bicubic_p1y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,98,125,24,5,214,5,0,0           ; vbroadcastss  0x5d6(%rip),%ymm8        # 6a44 <_sk_callback_avx+0x540>
+  DB  197,188,88,72,32                    ; vaddps        0x20(%rax),%ymm8,%ymm1
+  DB  197,124,16,72,96                    ; vmovups       0x60(%rax),%ymm9
+  DB  196,98,125,24,21,199,5,0,0          ; vbroadcastss  0x5c7(%rip),%ymm10        # 6a48 <_sk_callback_avx+0x544>
+  DB  196,65,52,89,210                    ; vmulps        %ymm10,%ymm9,%ymm10
+  DB  196,98,125,24,29,189,5,0,0          ; vbroadcastss  0x5bd(%rip),%ymm11        # 6a4c <_sk_callback_avx+0x548>
+  DB  196,65,44,88,211                    ; vaddps        %ymm11,%ymm10,%ymm10
+  DB  196,65,52,89,210                    ; vmulps        %ymm10,%ymm9,%ymm10
+  DB  196,65,44,88,192                    ; vaddps        %ymm8,%ymm10,%ymm8
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  196,98,125,24,13,164,5,0,0          ; vbroadcastss  0x5a4(%rip),%ymm9        # 6a50 <_sk_callback_avx+0x54c>
+  DB  196,65,60,88,193                    ; vaddps        %ymm9,%ymm8,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3y_avx
+_sk_bicubic_p3y_avx LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  196,226,125,24,13,140,5,0,0         ; vbroadcastss  0x58c(%rip),%ymm1        # 6a54 <_sk_callback_avx+0x550>
+  DB  197,244,88,72,32                    ; vaddps        0x20(%rax),%ymm1,%ymm1
+  DB  197,124,16,64,96                    ; vmovups       0x60(%rax),%ymm8
+  DB  196,65,60,89,200                    ; vmulps        %ymm8,%ymm8,%ymm9
+  DB  196,98,125,24,21,120,5,0,0          ; vbroadcastss  0x578(%rip),%ymm10        # 6a58 <_sk_callback_avx+0x554>
+  DB  196,65,60,89,194                    ; vmulps        %ymm10,%ymm8,%ymm8
+  DB  196,98,125,24,21,110,5,0,0          ; vbroadcastss  0x56e(%rip),%ymm10        # 6a5c <_sk_callback_avx+0x558>
+  DB  196,65,60,88,194                    ; vaddps        %ymm10,%ymm8,%ymm8
+  DB  196,65,52,89,192                    ; vmulps        %ymm8,%ymm9,%ymm8
+  DB  197,124,17,128,160,0,0,0            ; vmovups       %ymm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_callback_avx
+_sk_callback_avx LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,131,228,224                      ; and           $0xffffffffffffffe0,%rsp
+  DB  72,129,236,192,0,0,0                ; sub           $0xc0,%rsp
+  DB  197,252,41,188,36,128,0,0,0         ; vmovaps       %ymm7,0x80(%rsp)
+  DB  197,252,41,116,36,96                ; vmovaps       %ymm6,0x60(%rsp)
+  DB  197,252,41,108,36,64                ; vmovaps       %ymm5,0x40(%rsp)
+  DB  197,252,41,100,36,32                ; vmovaps       %ymm4,0x20(%rsp)
+  DB  77,137,196                          ; mov           %r8,%r12
+  DB  73,137,206                          ; mov           %rcx,%r14
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,137,195                          ; mov           %rax,%rbx
+  DB  197,252,20,225                      ; vunpcklps     %ymm1,%ymm0,%ymm4
+  DB  197,252,21,193                      ; vunpckhps     %ymm1,%ymm0,%ymm0
+  DB  197,236,20,203                      ; vunpcklps     %ymm3,%ymm2,%ymm1
+  DB  197,236,21,211                      ; vunpckhps     %ymm3,%ymm2,%ymm2
+  DB  197,221,20,217                      ; vunpcklpd     %ymm1,%ymm4,%ymm3
+  DB  197,221,21,201                      ; vunpckhpd     %ymm1,%ymm4,%ymm1
+  DB  197,253,20,226                      ; vunpcklpd     %ymm2,%ymm0,%ymm4
+  DB  197,253,21,194                      ; vunpckhpd     %ymm2,%ymm0,%ymm0
+  DB  196,227,101,24,209,1                ; vinsertf128   $0x1,%xmm1,%ymm3,%ymm2
+  DB  196,227,93,24,232,1                 ; vinsertf128   $0x1,%xmm0,%ymm4,%ymm5
+  DB  196,227,101,6,201,49                ; vperm2f128    $0x31,%ymm1,%ymm3,%ymm1
+  DB  196,227,93,6,192,49                 ; vperm2f128    $0x31,%ymm0,%ymm4,%ymm0
+  DB  197,253,17,83,8                     ; vmovupd       %ymm2,0x8(%rbx)
+  DB  197,253,17,107,40                   ; vmovupd       %ymm5,0x28(%rbx)
+  DB  197,253,17,75,72                    ; vmovupd       %ymm1,0x48(%rbx)
+  DB  197,253,17,67,104                   ; vmovupd       %ymm0,0x68(%rbx)
+  DB  77,133,228                          ; test          %r12,%r12
+  DB  186,8,0,0,0                         ; mov           $0x8,%edx
+  DB  65,15,69,212                        ; cmovne        %r12d,%edx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  197,248,119                         ; vzeroupper
+  DB  255,19                              ; callq         *(%rbx)
+  DB  72,139,131,136,0,0,0                ; mov           0x88(%rbx),%rax
+  DB  197,248,16,0                        ; vmovups       (%rax),%xmm0
+  DB  197,248,16,72,16                    ; vmovups       0x10(%rax),%xmm1
+  DB  197,248,16,80,32                    ; vmovups       0x20(%rax),%xmm2
+  DB  197,248,16,88,48                    ; vmovups       0x30(%rax),%xmm3
+  DB  196,227,101,24,88,112,1             ; vinsertf128   $0x1,0x70(%rax),%ymm3,%ymm3
+  DB  196,227,109,24,80,96,1              ; vinsertf128   $0x1,0x60(%rax),%ymm2,%ymm2
+  DB  196,227,117,24,72,80,1              ; vinsertf128   $0x1,0x50(%rax),%ymm1,%ymm1
+  DB  196,227,125,24,64,64,1              ; vinsertf128   $0x1,0x40(%rax),%ymm0,%ymm0
+  DB  197,252,20,225                      ; vunpcklps     %ymm1,%ymm0,%ymm4
+  DB  197,252,21,233                      ; vunpckhps     %ymm1,%ymm0,%ymm5
+  DB  197,236,20,203                      ; vunpcklps     %ymm3,%ymm2,%ymm1
+  DB  197,236,21,219                      ; vunpckhps     %ymm3,%ymm2,%ymm3
+  DB  197,221,20,193                      ; vunpcklpd     %ymm1,%ymm4,%ymm0
+  DB  197,221,21,201                      ; vunpckhpd     %ymm1,%ymm4,%ymm1
+  DB  197,213,20,211                      ; vunpcklpd     %ymm3,%ymm5,%ymm2
+  DB  197,213,21,219                      ; vunpckhpd     %ymm3,%ymm5,%ymm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  76,137,241                          ; mov           %r14,%rcx
+  DB  77,137,224                          ; mov           %r12,%r8
+  DB  197,252,40,100,36,32                ; vmovaps       0x20(%rsp),%ymm4
+  DB  197,252,40,108,36,64                ; vmovaps       0x40(%rsp),%ymm5
+  DB  197,252,40,116,36,96                ; vmovaps       0x60(%rsp),%ymm6
+  DB  197,252,40,188,36,128,0,0,0         ; vmovaps       0x80(%rsp),%ymm7
+  DB  72,141,101,224                      ; lea           -0x20(%rbp),%rsp
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+ALIGN 4
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,1                            ; cmpb          $0x1,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  252                                 ; cld
+  DB  190,0,0,128,63                      ; mov           $0x3f800000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,191,0,0,224                   ; add           %al,-0x1fffff41(%rax)
+  DB  64,154                              ; rex           (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 66e1 <.literal4+0xb1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 66f1 <.literal4+0xc1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 6701 <.literal4+0xd1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,174                  ; ds            cmp $0xae3f170a,%eax
+  DB  71,225,61                           ; rex.RXB       loope 6711 <.literal4+0xe1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,127                    ; add           %al,0x7f00003f(%rax)
+  DB  67,0,0                              ; rex.XB        add %al,(%r8)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  145                                 ; xchg          %eax,%ecx
+  DB  131,158,61,154,153,153,62           ; sbbl          $0x3e,-0x666665c3(%rsi)
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,97                               ; rex.RXB       (bad)
+  DB  61,82,184,78,65                     ; cmp           $0x414eb852,%eax
+  DB  57,215                              ; cmp           %edx,%edi
+  DB  32,187,186,159,98,60                ; and           %bh,0x3c629fba(%rbx)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  252                                 ; cld
+  DB  191,16,62,168,177                   ; mov           $0xb1a83e10,%edi
+  DB  152                                 ; cwtl
+  DB  59,0                                ; cmp           (%rax),%eax
+  DB  0,128,63,0,0,192                    ; add           %al,-0x3fffffc1(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  0,64,0                              ; add           %al,0x0(%rax)
+  DB  0,128,64,171,170,42                 ; add           %al,0x2aaaab40(%rax)
+  DB  62,0,0                              ; add           %al,%ds:(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  128,64,171,170                      ; addb          $0xaa,-0x55(%rax)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,129,128,128,59                  ; mov           $0x3b808081,%esi
+  DB  129,128,128,59,0,248,0,0,8,33       ; addl          $0x21080000,-0x7ffc480(%rax)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        6765 <.literal4+0x135>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,129,128,128,59              ; and           %eax,0x3b808081(,%rdi,1)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  129,128,128,59,129,128,128,59,0,0   ; addl          $0x3b80,-0x7f7ec480(%rax)
+  DB  0,52,255                            ; add           %dh,(%rdi,%rdi,8)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            678c <.literal4+0x15c>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            6805 <.literal4+0x1d5>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,0                      ; add           %al,0x3f(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            67c0 <.literal4+0x190>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            6839 <.literal4+0x209>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,0                      ; add           %al,0x3f(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            67f4 <.literal4+0x1c4>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            686d <.literal4+0x23d>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,0                      ; add           %al,0x3f(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            6828 <.literal4+0x1f8>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            68a1 <.literal4+0x271>
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,249,68,180                   ; mov           $0xb444f93f,%edi
+  DB  62,163,233,220,63,81,140,242,66,141 ; movabs        %eax,%ds:0x8d42f28c513fdce9
+  DB  188,190,63,248,245                  ; mov           $0xf5f83fbe,%esp
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,128,63,0,0,200                    ; add           %al,-0x37ffffc1(%rax)
+  DB  66,0,0                              ; rex.X         add %al,(%rax)
+  DB  127,67                              ; jg            689f <.literal4+0x26f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,65,203,61                       ; addb          $0x3d,-0x35(%rcx)
+  DB  13,60,111,18,3                      ; or            $0x3126f3c,%eax
+  DB  59,10                               ; cmp           (%rdx),%ecx
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  163,59,194,24,17,60,203,61,13       ; movabs        %eax,0xd3dcb3c1118c23b
+  DB  190,80,128,3,62                     ; mov           $0x3e038050,%esi
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           68bf <.literal4+0x28f>
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  129,128,128,59,129,128,128,59,0,0   ; addl          $0x3b80,-0x7f7ec480(%rax)
+  DB  127,67                              ; jg            68d3 <.literal4+0x2a3>
+  DB  129,128,128,59,0,0,128,63,129,128   ; addl          $0x80813f80,0x3b80(%rax)
+  DB  128,59,0                            ; cmpb          $0x0,(%rbx)
+  DB  0,128,63,129,128,128                ; add           %al,-0x7f7f7ec1(%rax)
+  DB  59,0                                ; cmp           (%rax),%eax
+  DB  248                                 ; clc
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        68b5 <.literal4+0x285>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,0,0,128,63                  ; and           %eax,0x3f800000(,%rdi,1)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        68d1 <.literal4+0x2a1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,0,0,128,63                  ; and           %eax,0x3f800000(,%rdi,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  124,66                              ; jl            6926 <.literal4+0x2f6>
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,0,15                 ; mov           %ecx,0xf003788(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,240,0                ; mov           %ecx,0xf03988(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,59,15,0                 ; mov           %ecx,0xf3b88(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,61,0,240                ; mov           %ecx,-0xfffc278(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,0,15                 ; mov           %ecx,0xf003788(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,240,0                ; mov           %ecx,0xf03988(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,59,15,0                 ; mov           %ecx,0xf3b88(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,61,0,0                  ; mov           %ecx,0x3d88(%rax)
+  DB  112,65                              ; jo            6969 <.literal4+0x339>
+  DB  129,128,128,59,129,128,128,59,0,0   ; addl          $0x3b80,-0x7f7ec480(%rax)
+  DB  127,67                              ; jg            6977 <.literal4+0x347>
+  DB  0,128,0,0,0,0                       ; add           %al,0x0(%rax)
+  DB  0,128,0,4,0,128                     ; add           %al,-0x7ffffc00(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,128,0,0,0,0                       ; add           %al,0x0(%rax)
+  DB  0,128,0,4,0,128                     ; add           %al,-0x7ffffc00(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,128,56                    ; add           %al,0x38800000(%rax)
+  DB  0,64,254                            ; add           %al,-0x2(%rax)
+  DB  255,128,0,128,55,128                ; incl          -0x7fc88000(%rax)
+  DB  0,128,55,0,0,128                    ; add           %al,-0x7fffffc9(%rax)
+  DB  63                                  ; (bad)
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            69b7 <.literal4+0x387>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,191,0,0,0                     ; add           %al,0xbf(%rax)
+  DB  63                                  ; (bad)
+  DB  208                                 ; (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,89                               ; ds            pop %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  152                                 ; cwtl
+  DB  221,147,61,45,16,17                 ; fstl          0x11102d3d(%rbx)
+  DB  192,18,120                          ; rclb          $0x78,(%rdx)
+  DB  57,64,32                            ; cmp           %eax,0x20(%rax)
+  DB  148                                 ; xchg          %eax,%esp
+  DB  90                                  ; pop           %rdx
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,0,24                             ; add           %bl,%ds:(%rax)
+  DB  161,57,1,0,0,0,111,43,231           ; movabs        0xe72b6f0000000139,%eax
+  DB  187,159,215,202,60                  ; mov           $0x3ccad79f,%ebx
+  DB  212                                 ; (bad)
+  DB  100,84                              ; fs            push %rsp
+  DB  189,169,240,34,62                   ; mov           $0x3e22f0a9,%ebp
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,62,0                            ; cmpb          $0x0,(%rsi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,128,63                    ; add           %bh,0x3f800000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,128,63                    ; add           %bh,0x3f800000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,128,63,114              ; sarb          $0x72,0x3f800000(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,0,0,0,191                       ; mov           $0xbf000000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,85                           ; cmpb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,0,0,192,63                      ; mov           $0x3fc00000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,0,0,192,63                      ; mov           $0x3fc00000,%edi
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  192,63,114                          ; sarb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,0,0,192,191                     ; mov           $0xbfc00000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,114                          ; cmpb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,0,0,0,191                       ; mov           $0xbf000000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,85                           ; cmpb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,0,0,192,63                      ; mov           $0x3fc00000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,0,0,192,63                      ; mov           $0x3fc00000,%edi
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  192,63,114                          ; sarb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190                                 ; .byte         0xbe
+
+ALIGN 16
+  DB  0,2                                 ; add           %al,(%rdx)
+  DB  4,6                                 ; add           $0x6,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,10                                ; or            %cl,(%rdx)
+  DB  12,14                               ; or            $0xe,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,2                                 ; add           %al,(%rdx)
+  DB  4,6                                 ; add           $0x6,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,10                                ; or            %cl,(%rdx)
+  DB  12,14                               ; or            $0xe,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,2                                 ; add           %al,(%rdx)
+  DB  4,6                                 ; add           $0x6,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,10                                ; or            %cl,(%rdx)
+  DB  12,14                               ; or            $0xe,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,2                                 ; add           %al,(%rdx)
+  DB  4,6                                 ; add           $0x6,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,10                                ; or            %cl,(%rdx)
+  DB  12,14                               ; or            $0xe,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+
+ALIGN 32
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+ALIGN 32
+
+PUBLIC _sk_start_pipeline_sse41
+_sk_start_pipeline_sse41 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  86                                  ; push          %rsi
+  DB  87                                  ; push          %rdi
+  DB  83                                  ; push          %rbx
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  68,15,41,125,176                    ; movaps        %xmm15,-0x50(%rbp)
+  DB  68,15,41,117,160                    ; movaps        %xmm14,-0x60(%rbp)
+  DB  68,15,41,109,144                    ; movaps        %xmm13,-0x70(%rbp)
+  DB  68,15,41,101,128                    ; movaps        %xmm12,-0x80(%rbp)
+  DB  68,15,41,157,112,255,255,255        ; movaps        %xmm11,-0x90(%rbp)
+  DB  68,15,41,149,96,255,255,255         ; movaps        %xmm10,-0xa0(%rbp)
+  DB  68,15,41,141,80,255,255,255         ; movaps        %xmm9,-0xb0(%rbp)
+  DB  68,15,41,133,64,255,255,255         ; movaps        %xmm8,-0xc0(%rbp)
+  DB  15,41,189,48,255,255,255            ; movaps        %xmm7,-0xd0(%rbp)
+  DB  15,41,181,32,255,255,255            ; movaps        %xmm6,-0xe0(%rbp)
+  DB  72,137,211                          ; mov           %rdx,%rbx
+  DB  73,137,207                          ; mov           %rcx,%r15
+  DB  76,139,117,48                       ; mov           0x30(%rbp),%r14
+  DB  76,137,206                          ; mov           %r9,%rsi
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,197                          ; mov           %rax,%r13
+  DB  73,137,244                          ; mov           %rsi,%r12
+  DB  73,141,79,4                         ; lea           0x4(%r15),%rcx
+  DB  76,57,193                           ; cmp           %r8,%rcx
+  DB  118,5                               ; jbe           7b <_sk_start_pipeline_sse41+0x7b>
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  235,75                              ; jmp           c6 <_sk_start_pipeline_sse41+0xc6>
+  DB  76,137,133,24,255,255,255           ; mov           %r8,-0xe8(%rbp)
+  DB  65,184,0,0,0,0                      ; mov           $0x0,%r8d
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  76,139,133,24,255,255,255           ; mov           -0xe8(%rbp),%r8
+  DB  73,141,87,4                         ; lea           0x4(%r15),%rdx
+  DB  73,131,199,8                        ; add           $0x8,%r15
+  DB  77,57,199                           ; cmp           %r8,%r15
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  118,188                             ; jbe           82 <_sk_start_pipeline_sse41+0x82>
+  DB  73,41,208                           ; sub           %rdx,%r8
+  DB  116,36                              ; je            ef <_sk_start_pipeline_sse41+0xef>
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  15,40,181,32,255,255,255            ; movaps        -0xe0(%rbp),%xmm6
+  DB  15,40,189,48,255,255,255            ; movaps        -0xd0(%rbp),%xmm7
+  DB  68,15,40,133,64,255,255,255         ; movaps        -0xc0(%rbp),%xmm8
+  DB  68,15,40,141,80,255,255,255         ; movaps        -0xb0(%rbp),%xmm9
+  DB  68,15,40,149,96,255,255,255         ; movaps        -0xa0(%rbp),%xmm10
+  DB  68,15,40,157,112,255,255,255        ; movaps        -0x90(%rbp),%xmm11
+  DB  68,15,40,101,128                    ; movaps        -0x80(%rbp),%xmm12
+  DB  68,15,40,109,144                    ; movaps        -0x70(%rbp),%xmm13
+  DB  68,15,40,117,160                    ; movaps        -0x60(%rbp),%xmm14
+  DB  68,15,40,125,176                    ; movaps        -0x50(%rbp),%xmm15
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  95                                  ; pop           %rdi
+  DB  94                                  ; pop           %rsi
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  195                                 ; retq
+
+PUBLIC _sk_just_return_sse41
+_sk_just_return_sse41 LABEL PROC
+  DB  195                                 ; retq
+
+PUBLIC _sk_seed_shader_sse41
+_sk_seed_shader_sse41 LABEL PROC
+  DB  102,15,110,194                      ; movd          %edx,%xmm0
+  DB  102,15,112,192,0                    ; pshufd        $0x0,%xmm0,%xmm0
+  DB  15,91,200                           ; cvtdq2ps      %xmm0,%xmm1
+  DB  15,40,21,23,80,0,0                  ; movaps        0x5017(%rip),%xmm2        # 5170 <_sk_callback_sse41+0xd6>
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  15,16,7                             ; movups        (%rdi),%xmm0
+  DB  15,88,193                           ; addps         %xmm1,%xmm0
+  DB  102,15,110,201                      ; movd          %ecx,%xmm1
+  DB  102,15,112,201,0                    ; pshufd        $0x0,%xmm1,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,21,6,80,0,0                   ; movaps        0x5006(%rip),%xmm2        # 5180 <_sk_callback_sse41+0xe6>
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dither_sse41
+_sk_dither_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  102,68,15,110,194                   ; movd          %edx,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  243,68,15,111,79,32                 ; movdqu        0x20(%rdi),%xmm9
+  DB  102,69,15,254,200                   ; paddd         %xmm8,%xmm9
+  DB  102,68,15,110,193                   ; movd          %ecx,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  102,69,15,239,193                   ; pxor          %xmm9,%xmm8
+  DB  102,68,15,111,21,212,79,0,0         ; movdqa        0x4fd4(%rip),%xmm10        # 5190 <_sk_callback_sse41+0xf6>
+  DB  102,69,15,111,216                   ; movdqa        %xmm8,%xmm11
+  DB  102,69,15,219,218                   ; pand          %xmm10,%xmm11
+  DB  102,65,15,114,243,5                 ; pslld         $0x5,%xmm11
+  DB  102,69,15,219,209                   ; pand          %xmm9,%xmm10
+  DB  102,65,15,114,242,4                 ; pslld         $0x4,%xmm10
+  DB  102,68,15,111,37,192,79,0,0         ; movdqa        0x4fc0(%rip),%xmm12        # 51a0 <_sk_callback_sse41+0x106>
+  DB  102,68,15,111,45,199,79,0,0         ; movdqa        0x4fc7(%rip),%xmm13        # 51b0 <_sk_callback_sse41+0x116>
+  DB  102,69,15,111,240                   ; movdqa        %xmm8,%xmm14
+  DB  102,69,15,219,245                   ; pand          %xmm13,%xmm14
+  DB  102,65,15,114,246,2                 ; pslld         $0x2,%xmm14
+  DB  102,69,15,219,233                   ; pand          %xmm9,%xmm13
+  DB  102,69,15,254,237                   ; paddd         %xmm13,%xmm13
+  DB  102,69,15,219,196                   ; pand          %xmm12,%xmm8
+  DB  102,65,15,114,208,1                 ; psrld         $0x1,%xmm8
+  DB  102,69,15,219,204                   ; pand          %xmm12,%xmm9
+  DB  102,65,15,114,209,2                 ; psrld         $0x2,%xmm9
+  DB  102,69,15,235,234                   ; por           %xmm10,%xmm13
+  DB  102,69,15,235,233                   ; por           %xmm9,%xmm13
+  DB  102,69,15,235,243                   ; por           %xmm11,%xmm14
+  DB  102,69,15,235,245                   ; por           %xmm13,%xmm14
+  DB  102,69,15,235,240                   ; por           %xmm8,%xmm14
+  DB  69,15,91,198                        ; cvtdq2ps      %xmm14,%xmm8
+  DB  68,15,89,5,130,79,0,0               ; mulps         0x4f82(%rip),%xmm8        # 51c0 <_sk_callback_sse41+0x126>
+  DB  68,15,88,5,138,79,0,0               ; addps         0x4f8a(%rip),%xmm8        # 51d0 <_sk_callback_sse41+0x136>
+  DB  243,68,15,16,16                     ; movss         (%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  68,15,88,210                        ; addps         %xmm2,%xmm10
+  DB  15,93,195                           ; minps         %xmm3,%xmm0
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  15,93,203                           ; minps         %xmm3,%xmm1
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  68,15,95,201                        ; maxps         %xmm1,%xmm9
+  DB  68,15,93,211                        ; minps         %xmm3,%xmm10
+  DB  65,15,95,210                        ; maxps         %xmm10,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_constant_color_sse41
+_sk_constant_color_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,12                     ; movss         0xc(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_rgba_sse41
+_sk_load_rgba_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  15,16,72,16                         ; movups        0x10(%rax),%xmm1
+  DB  15,16,80,32                         ; movups        0x20(%rax),%xmm2
+  DB  15,16,88,48                         ; movups        0x30(%rax),%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_rgba_sse41
+_sk_store_rgba_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,17,0                             ; movups        %xmm0,(%rax)
+  DB  15,17,72,16                         ; movups        %xmm1,0x10(%rax)
+  DB  15,17,80,32                         ; movups        %xmm2,0x20(%rax)
+  DB  15,17,88,48                         ; movups        %xmm3,0x30(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clear_sse41
+_sk_clear_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcatop_sse41
+_sk_srcatop_sse41 LABEL PROC
+  DB  15,89,199                           ; mulps         %xmm7,%xmm0
+  DB  68,15,40,5,228,78,0,0               ; movaps        0x4ee4(%rip),%xmm8        # 51e0 <_sk_callback_sse41+0x146>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  65,15,88,193                        ; addps         %xmm9,%xmm0
+  DB  15,89,207                           ; mulps         %xmm7,%xmm1
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,88,209                        ; addps         %xmm9,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstatop_sse41
+_sk_dstatop_sse41 LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  68,15,40,13,167,78,0,0              ; movaps        0x4ea7(%rip),%xmm9        # 51f0 <_sk_callback_sse41+0x156>
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,197                        ; mulps         %xmm5,%xmm8
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,198                        ; mulps         %xmm6,%xmm8
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  65,15,88,208                        ; addps         %xmm8,%xmm2
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcin_sse41
+_sk_srcin_sse41 LABEL PROC
+  DB  15,89,199                           ; mulps         %xmm7,%xmm0
+  DB  15,89,207                           ; mulps         %xmm7,%xmm1
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstin_sse41
+_sk_dstin_sse41 LABEL PROC
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcout_sse41
+_sk_srcout_sse41 LABEL PROC
+  DB  68,15,40,5,75,78,0,0                ; movaps        0x4e4b(%rip),%xmm8        # 5200 <_sk_callback_sse41+0x166>
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstout_sse41
+_sk_dstout_sse41 LABEL PROC
+  DB  68,15,40,5,59,78,0,0                ; movaps        0x4e3b(%rip),%xmm8        # 5210 <_sk_callback_sse41+0x176>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_sse41
+_sk_srcover_sse41 LABEL PROC
+  DB  68,15,40,5,30,78,0,0                ; movaps        0x4e1e(%rip),%xmm8        # 5220 <_sk_callback_sse41+0x186>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  65,15,88,193                        ; addps         %xmm9,%xmm0
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,88,209                        ; addps         %xmm9,%xmm2
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstover_sse41
+_sk_dstover_sse41 LABEL PROC
+  DB  68,15,40,5,242,77,0,0               ; movaps        0x4df2(%rip),%xmm8        # 5230 <_sk_callback_sse41+0x196>
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_modulate_sse41
+_sk_modulate_sse41 LABEL PROC
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_multiply_sse41
+_sk_multiply_sse41 LABEL PROC
+  DB  68,15,40,5,198,77,0,0               ; movaps        0x4dc6(%rip),%xmm8        # 5240 <_sk_callback_sse41+0x1a6>
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,89,208                        ; mulps         %xmm0,%xmm10
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,220                        ; mulps         %xmm4,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,221                        ; mulps         %xmm5,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,222                        ; mulps         %xmm6,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  65,15,88,211                        ; addps         %xmm11,%xmm2
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_plus__sse41
+_sk_plus__sse41 LABEL PROC
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_screen_sse41
+_sk_screen_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  65,15,92,192                        ; subps         %xmm8,%xmm0
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,88,197                        ; addps         %xmm5,%xmm8
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  68,15,92,193                        ; subps         %xmm1,%xmm8
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  68,15,88,206                        ; addps         %xmm6,%xmm9
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,92,202                        ; subps         %xmm2,%xmm9
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,88,215                        ; addps         %xmm7,%xmm10
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xor__sse41
+_sk_xor__sse41 LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  15,40,29,251,76,0,0                 ; movaps        0x4cfb(%rip),%xmm3        # 5250 <_sk_callback_sse41+0x1b6>
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,92,216                        ; subps         %xmm8,%xmm3
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,89,212                        ; mulps         %xmm4,%xmm10
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,89,213                        ; mulps         %xmm5,%xmm10
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,89,214                        ; mulps         %xmm6,%xmm10
+  DB  65,15,88,210                        ; addps         %xmm10,%xmm2
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_darken_sse41
+_sk_darken_sse41 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,95,201                        ; maxps         %xmm1,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,95,193                        ; maxps         %xmm9,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,95,209                        ; maxps         %xmm9,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,102,76,0,0                 ; movaps        0x4c66(%rip),%xmm2        # 5260 <_sk_callback_sse41+0x1c6>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lighten_sse41
+_sk_lighten_sse41 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,93,201                        ; minps         %xmm1,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,93,193                        ; minps         %xmm9,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,93,209                        ; minps         %xmm9,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,11,76,0,0                  ; movaps        0x4c0b(%rip),%xmm2        # 5270 <_sk_callback_sse41+0x1d6>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_difference_sse41
+_sk_difference_sse41 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,93,201                        ; minps         %xmm1,%xmm9
+  DB  69,15,88,201                        ; addps         %xmm9,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,93,193                        ; minps         %xmm9,%xmm8
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,93,209                        ; minps         %xmm9,%xmm2
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,165,75,0,0                 ; movaps        0x4ba5(%rip),%xmm2        # 5280 <_sk_callback_sse41+0x1e6>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_exclusion_sse41
+_sk_exclusion_sse41 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,197                        ; mulps         %xmm5,%xmm8
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,101,75,0,0                 ; movaps        0x4b65(%rip),%xmm2        # 5290 <_sk_callback_sse41+0x1f6>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colorburn_sse41
+_sk_colorburn_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  68,15,40,21,88,75,0,0               ; movaps        0x4b58(%rip),%xmm10        # 52a0 <_sk_callback_sse41+0x206>
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  68,15,92,223                        ; subps         %xmm7,%xmm11
+  DB  69,15,40,203                        ; movaps        %xmm11,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  65,15,94,192                        ; divps         %xmm8,%xmm0
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,93,224                        ; minps         %xmm0,%xmm12
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,233                        ; addps         %xmm9,%xmm13
+  DB  69,15,40,225                        ; movaps        %xmm9,%xmm12
+  DB  68,15,88,228                        ; addps         %xmm4,%xmm12
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,194,193,0                     ; cmpeqps       %xmm9,%xmm0
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  102,69,15,56,20,232                 ; blendvps      %xmm0,%xmm8,%xmm13
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,194,199,0                        ; cmpeqps       %xmm7,%xmm0
+  DB  69,15,88,197                        ; addps         %xmm13,%xmm8
+  DB  102,69,15,56,20,196                 ; blendvps      %xmm0,%xmm12,%xmm8
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  68,15,89,225                        ; mulps         %xmm1,%xmm12
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  15,92,197                           ; subps         %xmm5,%xmm0
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,94,193                           ; divps         %xmm1,%xmm0
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,93,232                        ; minps         %xmm0,%xmm13
+  DB  68,15,40,247                        ; movaps        %xmm7,%xmm14
+  DB  69,15,92,245                        ; subps         %xmm13,%xmm14
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,194,193,0                     ; cmpeqps       %xmm9,%xmm0
+  DB  68,15,89,243                        ; mulps         %xmm3,%xmm14
+  DB  69,15,88,244                        ; addps         %xmm12,%xmm14
+  DB  102,68,15,56,20,241                 ; blendvps      %xmm0,%xmm1,%xmm14
+  DB  68,15,88,229                        ; addps         %xmm5,%xmm12
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  15,194,199,0                        ; cmpeqps       %xmm7,%xmm0
+  DB  102,65,15,56,20,204                 ; blendvps      %xmm0,%xmm12,%xmm1
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  15,92,198                           ; subps         %xmm6,%xmm0
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,94,194                           ; divps         %xmm2,%xmm0
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,93,224                        ; minps         %xmm0,%xmm12
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  68,15,194,202,0                     ; cmpeqps       %xmm2,%xmm9
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,235                        ; addps         %xmm11,%xmm13
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,68,15,56,20,234                 ; blendvps      %xmm0,%xmm2,%xmm13
+  DB  68,15,88,222                        ; addps         %xmm6,%xmm11
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  65,15,88,213                        ; addps         %xmm13,%xmm2
+  DB  15,40,198                           ; movaps        %xmm6,%xmm0
+  DB  15,194,199,0                        ; cmpeqps       %xmm7,%xmm0
+  DB  102,65,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm2
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colordodge_sse41
+_sk_colordodge_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  68,15,40,21,54,74,0,0               ; movaps        0x4a36(%rip),%xmm10        # 52b0 <_sk_callback_sse41+0x216>
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  68,15,92,223                        ; subps         %xmm7,%xmm11
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,89,224                        ; mulps         %xmm8,%xmm12
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  65,15,92,192                        ; subps         %xmm8,%xmm0
+  DB  68,15,94,200                        ; divps         %xmm0,%xmm9
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,40,247                        ; movaps        %xmm7,%xmm14
+  DB  69,15,93,241                        ; minps         %xmm9,%xmm14
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,194,195,0                        ; cmpeqps       %xmm3,%xmm0
+  DB  68,15,89,243                        ; mulps         %xmm3,%xmm14
+  DB  69,15,88,244                        ; addps         %xmm12,%xmm14
+  DB  102,69,15,56,20,240                 ; blendvps      %xmm0,%xmm8,%xmm14
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  68,15,88,228                        ; addps         %xmm4,%xmm12
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  69,15,88,198                        ; addps         %xmm14,%xmm8
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  65,15,194,193,0                     ; cmpeqps       %xmm9,%xmm0
+  DB  102,69,15,56,20,196                 ; blendvps      %xmm0,%xmm12,%xmm8
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  68,15,89,229                        ; mulps         %xmm5,%xmm12
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  68,15,94,224                        ; divps         %xmm0,%xmm12
+  DB  69,15,40,243                        ; movaps        %xmm11,%xmm14
+  DB  68,15,89,241                        ; mulps         %xmm1,%xmm14
+  DB  69,15,93,236                        ; minps         %xmm12,%xmm13
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  15,194,195,0                        ; cmpeqps       %xmm3,%xmm0
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,238                        ; addps         %xmm14,%xmm13
+  DB  102,68,15,56,20,233                 ; blendvps      %xmm0,%xmm1,%xmm13
+  DB  68,15,88,245                        ; addps         %xmm5,%xmm14
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,88,205                        ; addps         %xmm13,%xmm1
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  65,15,194,193,0                     ; cmpeqps       %xmm9,%xmm0
+  DB  102,65,15,56,20,206                 ; blendvps      %xmm0,%xmm14,%xmm1
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  68,15,89,230                        ; mulps         %xmm6,%xmm12
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,92,194                           ; subps         %xmm2,%xmm0
+  DB  68,15,94,224                        ; divps         %xmm0,%xmm12
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,93,236                        ; minps         %xmm12,%xmm13
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  15,194,195,0                        ; cmpeqps       %xmm3,%xmm0
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,235                        ; addps         %xmm11,%xmm13
+  DB  102,68,15,56,20,234                 ; blendvps      %xmm0,%xmm2,%xmm13
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  65,15,88,213                        ; addps         %xmm13,%xmm2
+  DB  68,15,194,206,0                     ; cmpeqps       %xmm6,%xmm9
+  DB  68,15,88,222                        ; addps         %xmm6,%xmm11
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,65,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm2
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hardlight_sse41
+_sk_hardlight_sse41 LABEL PROC
+  DB  72,131,236,24                       ; sub           $0x18,%rsp
+  DB  15,41,52,36                         ; movaps        %xmm6,(%rsp)
+  DB  68,15,40,229                        ; movaps        %xmm5,%xmm12
+  DB  15,40,244                           ; movaps        %xmm4,%xmm6
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  68,15,40,21,12,73,0,0               ; movaps        0x490c(%rip),%xmm10        # 52c0 <_sk_callback_sse41+0x226>
+  DB  65,15,40,234                        ; movaps        %xmm10,%xmm5
+  DB  15,92,239                           ; subps         %xmm7,%xmm5
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  68,15,92,212                        ; subps         %xmm4,%xmm10
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  68,15,89,198                        ; mulps         %xmm6,%xmm8
+  DB  68,15,88,192                        ; addps         %xmm0,%xmm8
+  DB  68,15,40,252                        ; movaps        %xmm4,%xmm15
+  DB  69,15,92,249                        ; subps         %xmm9,%xmm15
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,40,247                        ; movaps        %xmm7,%xmm14
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  15,92,198                           ; subps         %xmm6,%xmm0
+  DB  65,15,89,199                        ; mulps         %xmm15,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  68,15,40,251                        ; movaps        %xmm3,%xmm15
+  DB  68,15,92,248                        ; subps         %xmm0,%xmm15
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,196,2                        ; cmpleps       %xmm4,%xmm0
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  69,15,88,201                        ; addps         %xmm9,%xmm9
+  DB  102,69,15,56,20,249                 ; blendvps      %xmm0,%xmm9,%xmm15
+  DB  68,15,40,221                        ; movaps        %xmm5,%xmm11
+  DB  68,15,89,217                        ; mulps         %xmm1,%xmm11
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  69,15,40,204                        ; movaps        %xmm12,%xmm9
+  DB  69,15,92,233                        ; subps         %xmm9,%xmm13
+  DB  68,15,89,232                        ; mulps         %xmm0,%xmm13
+  DB  69,15,88,237                        ; addps         %xmm13,%xmm13
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,196,2                        ; cmpleps       %xmm4,%xmm0
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  102,68,15,56,20,225                 ; blendvps      %xmm0,%xmm1,%xmm12
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  69,15,40,202                        ; movaps        %xmm10,%xmm9
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  69,15,88,199                        ; addps         %xmm15,%xmm8
+  DB  65,15,89,205                        ; mulps         %xmm13,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  65,15,88,204                        ; addps         %xmm12,%xmm1
+  DB  15,89,234                           ; mulps         %xmm2,%xmm5
+  DB  68,15,40,28,36                      ; movaps        (%rsp),%xmm11
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  68,15,88,205                        ; addps         %xmm5,%xmm9
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,196,2                        ; cmpleps       %xmm4,%xmm0
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,92,234                           ; subps         %xmm2,%xmm5
+  DB  65,15,89,211                        ; mulps         %xmm11,%xmm2
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  69,15,92,243                        ; subps         %xmm11,%xmm14
+  DB  68,15,89,245                        ; mulps         %xmm5,%xmm14
+  DB  69,15,88,246                        ; addps         %xmm14,%xmm14
+  DB  65,15,92,222                        ; subps         %xmm14,%xmm3
+  DB  102,15,56,20,218                    ; blendvps      %xmm0,%xmm2,%xmm3
+  DB  68,15,88,203                        ; addps         %xmm3,%xmm9
+  DB  65,15,88,226                        ; addps         %xmm10,%xmm4
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,40,220                           ; movaps        %xmm4,%xmm3
+  DB  15,40,230                           ; movaps        %xmm6,%xmm4
+  DB  65,15,40,237                        ; movaps        %xmm13,%xmm5
+  DB  65,15,40,243                        ; movaps        %xmm11,%xmm6
+  DB  72,131,196,24                       ; add           $0x18,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_overlay_sse41
+_sk_overlay_sse41 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,240                        ; movaps        %xmm0,%xmm14
+  DB  68,15,40,21,238,71,0,0              ; movaps        0x47ee(%rip),%xmm10        # 52d0 <_sk_callback_sse41+0x236>
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  68,15,92,223                        ; subps         %xmm7,%xmm11
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,89,198                        ; mulps         %xmm14,%xmm0
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  68,15,88,192                        ; addps         %xmm0,%xmm8
+  DB  68,15,40,235                        ; movaps        %xmm3,%xmm13
+  DB  69,15,92,238                        ; subps         %xmm14,%xmm13
+  DB  68,15,89,244                        ; mulps         %xmm4,%xmm14
+  DB  15,40,207                           ; movaps        %xmm7,%xmm1
+  DB  15,92,204                           ; subps         %xmm4,%xmm1
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,199,2                        ; cmpleps       %xmm7,%xmm0
+  DB  69,15,88,246                        ; addps         %xmm14,%xmm14
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  68,15,89,231                        ; mulps         %xmm7,%xmm12
+  DB  65,15,89,205                        ; mulps         %xmm13,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  69,15,40,236                        ; movaps        %xmm12,%xmm13
+  DB  68,15,92,233                        ; subps         %xmm1,%xmm13
+  DB  102,69,15,56,20,238                 ; blendvps      %xmm0,%xmm14,%xmm13
+  DB  69,15,88,197                        ; addps         %xmm13,%xmm8
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  68,15,40,235                        ; movaps        %xmm3,%xmm13
+  DB  69,15,92,233                        ; subps         %xmm9,%xmm13
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  68,15,40,247                        ; movaps        %xmm7,%xmm14
+  DB  68,15,92,245                        ; subps         %xmm5,%xmm14
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,199,2                        ; cmpleps       %xmm7,%xmm0
+  DB  69,15,88,201                        ; addps         %xmm9,%xmm9
+  DB  69,15,89,245                        ; mulps         %xmm13,%xmm14
+  DB  69,15,88,246                        ; addps         %xmm14,%xmm14
+  DB  69,15,40,236                        ; movaps        %xmm12,%xmm13
+  DB  69,15,92,238                        ; subps         %xmm14,%xmm13
+  DB  102,69,15,56,20,233                 ; blendvps      %xmm0,%xmm9,%xmm13
+  DB  65,15,88,205                        ; addps         %xmm13,%xmm1
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,40,202                        ; movaps        %xmm10,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  69,15,88,203                        ; addps         %xmm11,%xmm9
+  DB  68,15,40,219                        ; movaps        %xmm3,%xmm11
+  DB  68,15,92,218                        ; subps         %xmm2,%xmm11
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,92,238                        ; subps         %xmm6,%xmm13
+  DB  15,40,198                           ; movaps        %xmm6,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,199,2                        ; cmpleps       %xmm7,%xmm0
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  69,15,89,235                        ; mulps         %xmm11,%xmm13
+  DB  69,15,88,237                        ; addps         %xmm13,%xmm13
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  102,68,15,56,20,226                 ; blendvps      %xmm0,%xmm2,%xmm12
+  DB  69,15,88,204                        ; addps         %xmm12,%xmm9
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_softlight_sse41
+_sk_softlight_sse41 LABEL PROC
+  DB  72,131,236,72                       ; sub           $0x48,%rsp
+  DB  15,41,116,36,32                     ; movaps        %xmm6,0x20(%rsp)
+  DB  15,40,244                           ; movaps        %xmm4,%xmm6
+  DB  15,41,84,36,48                      ; movaps        %xmm2,0x30(%rsp)
+  DB  15,41,76,36,16                      ; movaps        %xmm1,0x10(%rsp)
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  68,15,194,207,1                     ; cmpltps       %xmm7,%xmm9
+  DB  15,40,198                           ; movaps        %xmm6,%xmm0
+  DB  15,94,199                           ; divps         %xmm7,%xmm0
+  DB  65,15,84,193                        ; andps         %xmm9,%xmm0
+  DB  15,40,13,193,70,0,0                 ; movaps        0x46c1(%rip),%xmm1        # 52e0 <_sk_callback_sse41+0x246>
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  68,15,92,208                        ; subps         %xmm0,%xmm10
+  DB  68,15,40,240                        ; movaps        %xmm0,%xmm14
+  DB  68,15,40,248                        ; movaps        %xmm0,%xmm15
+  DB  15,82,208                           ; rsqrtps       %xmm0,%xmm2
+  DB  68,15,83,218                        ; rcpps         %xmm2,%xmm11
+  DB  68,15,92,216                        ; subps         %xmm0,%xmm11
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,89,210                           ; mulps         %xmm2,%xmm2
+  DB  15,88,208                           ; addps         %xmm0,%xmm2
+  DB  68,15,40,45,159,70,0,0              ; movaps        0x469f(%rip),%xmm13        # 52f0 <_sk_callback_sse41+0x256>
+  DB  69,15,88,245                        ; addps         %xmm13,%xmm14
+  DB  68,15,89,242                        ; mulps         %xmm2,%xmm14
+  DB  68,15,40,37,159,70,0,0              ; movaps        0x469f(%rip),%xmm12        # 5300 <_sk_callback_sse41+0x266>
+  DB  69,15,89,252                        ; mulps         %xmm12,%xmm15
+  DB  69,15,88,254                        ; addps         %xmm14,%xmm15
+  DB  15,40,198                           ; movaps        %xmm6,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,199,2                        ; cmpleps       %xmm7,%xmm0
+  DB  102,69,15,56,20,223                 ; blendvps      %xmm0,%xmm15,%xmm11
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  68,15,88,211                        ; addps         %xmm3,%xmm10
+  DB  68,15,89,214                        ; mulps         %xmm6,%xmm10
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,88,218                        ; addps         %xmm2,%xmm11
+  DB  15,194,195,2                        ; cmpleps       %xmm3,%xmm0
+  DB  102,69,15,56,20,218                 ; blendvps      %xmm0,%xmm10,%xmm11
+  DB  68,15,40,213                        ; movaps        %xmm5,%xmm10
+  DB  68,15,94,215                        ; divps         %xmm7,%xmm10
+  DB  69,15,84,209                        ; andps         %xmm9,%xmm10
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,89,210                           ; mulps         %xmm2,%xmm2
+  DB  15,88,208                           ; addps         %xmm0,%xmm2
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  65,15,88,197                        ; addps         %xmm13,%xmm0
+  DB  15,89,194                           ; mulps         %xmm2,%xmm0
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,89,212                        ; mulps         %xmm12,%xmm2
+  DB  15,88,208                           ; addps         %xmm0,%xmm2
+  DB  65,15,82,194                        ; rsqrtps       %xmm10,%xmm0
+  DB  68,15,83,240                        ; rcpps         %xmm0,%xmm14
+  DB  69,15,92,242                        ; subps         %xmm10,%xmm14
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,199,2                        ; cmpleps       %xmm7,%xmm0
+  DB  102,68,15,56,20,242                 ; blendvps      %xmm0,%xmm2,%xmm14
+  DB  68,15,40,249                        ; movaps        %xmm1,%xmm15
+  DB  69,15,92,250                        ; subps         %xmm10,%xmm15
+  DB  15,40,108,36,16                     ; movaps        0x10(%rsp),%xmm5
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  68,15,89,250                        ; mulps         %xmm2,%xmm15
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,89,242                        ; mulps         %xmm2,%xmm14
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,41,36,36                         ; movaps        %xmm4,(%rsp)
+  DB  15,89,212                           ; mulps         %xmm4,%xmm2
+  DB  68,15,88,242                        ; addps         %xmm2,%xmm14
+  DB  68,15,88,251                        ; addps         %xmm3,%xmm15
+  DB  68,15,89,252                        ; mulps         %xmm4,%xmm15
+  DB  15,194,195,2                        ; cmpleps       %xmm3,%xmm0
+  DB  102,69,15,56,20,247                 ; blendvps      %xmm0,%xmm15,%xmm14
+  DB  68,15,40,249                        ; movaps        %xmm1,%xmm15
+  DB  15,40,100,36,32                     ; movaps        0x20(%rsp),%xmm4
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,94,199                           ; divps         %xmm7,%xmm0
+  DB  65,15,84,193                        ; andps         %xmm9,%xmm0
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  15,92,200                           ; subps         %xmm0,%xmm1
+  DB  68,15,88,232                        ; addps         %xmm0,%xmm13
+  DB  68,15,89,224                        ; mulps         %xmm0,%xmm12
+  DB  15,82,208                           ; rsqrtps       %xmm0,%xmm2
+  DB  68,15,83,202                        ; rcpps         %xmm2,%xmm9
+  DB  68,15,92,200                        ; subps         %xmm0,%xmm9
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,89,210                           ; mulps         %xmm2,%xmm2
+  DB  15,88,208                           ; addps         %xmm0,%xmm2
+  DB  68,15,89,234                        ; mulps         %xmm2,%xmm13
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,194,199,2                        ; cmpleps       %xmm7,%xmm0
+  DB  102,69,15,56,20,204                 ; blendvps      %xmm0,%xmm12,%xmm9
+  DB  68,15,40,100,36,48                  ; movaps        0x30(%rsp),%xmm12
+  DB  65,15,40,196                        ; movaps        %xmm12,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,202                           ; mulps         %xmm2,%xmm1
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,89,202                        ; mulps         %xmm2,%xmm9
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,89,212                           ; mulps         %xmm4,%xmm2
+  DB  68,15,88,202                        ; addps         %xmm2,%xmm9
+  DB  15,88,203                           ; addps         %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  15,194,195,2                        ; cmpleps       %xmm3,%xmm0
+  DB  102,68,15,56,20,201                 ; blendvps      %xmm0,%xmm1,%xmm9
+  DB  68,15,92,255                        ; subps         %xmm7,%xmm15
+  DB  69,15,89,199                        ; mulps         %xmm15,%xmm8
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  65,15,89,207                        ; mulps         %xmm15,%xmm1
+  DB  69,15,89,252                        ; mulps         %xmm12,%xmm15
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,89,198                           ; mulps         %xmm6,%xmm0
+  DB  68,15,88,192                        ; addps         %xmm0,%xmm8
+  DB  69,15,88,195                        ; addps         %xmm11,%xmm8
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,40,44,36                         ; movaps        (%rsp),%xmm5
+  DB  15,89,197                           ; mulps         %xmm5,%xmm0
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,40,212                           ; movaps        %xmm4,%xmm2
+  DB  65,15,88,199                        ; addps         %xmm15,%xmm0
+  DB  68,15,88,200                        ; addps         %xmm0,%xmm9
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,230                           ; movaps        %xmm6,%xmm4
+  DB  15,40,242                           ; movaps        %xmm2,%xmm6
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  72,131,196,72                       ; add           $0x48,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hue_sse41
+_sk_hue_sse41 LABEL PROC
+  DB  72,131,236,104                      ; sub           $0x68,%rsp
+  DB  15,41,60,36                         ; movaps        %xmm7,(%rsp)
+  DB  68,15,40,246                        ; movaps        %xmm6,%xmm14
+  DB  15,40,244                           ; movaps        %xmm4,%xmm6
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  15,41,84,36,80                      ; movaps        %xmm2,0x50(%rsp)
+  DB  15,41,76,36,64                      ; movaps        %xmm1,0x40(%rsp)
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  68,15,41,84,36,48                   ; movaps        %xmm10,0x30(%rsp)
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  68,15,40,234                        ; movaps        %xmm2,%xmm13
+  DB  69,15,89,232                        ; mulps         %xmm8,%xmm13
+  DB  68,15,40,205                        ; movaps        %xmm5,%xmm9
+  DB  68,15,40,221                        ; movaps        %xmm5,%xmm11
+  DB  15,41,108,36,32                     ; movaps        %xmm5,0x20(%rsp)
+  DB  69,15,95,222                        ; maxps         %xmm14,%xmm11
+  DB  15,40,254                           ; movaps        %xmm6,%xmm7
+  DB  68,15,40,230                        ; movaps        %xmm6,%xmm12
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  65,15,95,211                        ; maxps         %xmm11,%xmm2
+  DB  65,15,40,230                        ; movaps        %xmm14,%xmm4
+  DB  15,41,100,36,16                     ; movaps        %xmm4,0x10(%rsp)
+  DB  68,15,93,204                        ; minps         %xmm4,%xmm9
+  DB  65,15,93,249                        ; minps         %xmm9,%xmm7
+  DB  15,92,215                           ; subps         %xmm7,%xmm2
+  DB  15,40,249                           ; movaps        %xmm1,%xmm7
+  DB  65,15,93,253                        ; minps         %xmm13,%xmm7
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,93,223                           ; minps         %xmm7,%xmm3
+  DB  15,40,249                           ; movaps        %xmm1,%xmm7
+  DB  65,15,95,253                        ; maxps         %xmm13,%xmm7
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,95,199                           ; maxps         %xmm7,%xmm0
+  DB  15,40,253                           ; movaps        %xmm5,%xmm7
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,92,195                           ; subps         %xmm3,%xmm0
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  15,92,203                           ; subps         %xmm3,%xmm1
+  DB  68,15,92,235                        ; subps         %xmm3,%xmm13
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  68,15,94,208                        ; divps         %xmm0,%xmm10
+  DB  15,89,202                           ; mulps         %xmm2,%xmm1
+  DB  15,94,200                           ; divps         %xmm0,%xmm1
+  DB  68,15,89,234                        ; mulps         %xmm2,%xmm13
+  DB  68,15,94,232                        ; divps         %xmm0,%xmm13
+  DB  15,194,195,4                        ; cmpneqps      %xmm3,%xmm0
+  DB  68,15,84,208                        ; andps         %xmm0,%xmm10
+  DB  15,84,200                           ; andps         %xmm0,%xmm1
+  DB  68,15,84,232                        ; andps         %xmm0,%xmm13
+  DB  15,40,5,5,68,0,0                    ; movaps        0x4405(%rip),%xmm0        # 5310 <_sk_callback_sse41+0x276>
+  DB  68,15,89,224                        ; mulps         %xmm0,%xmm12
+  DB  15,40,21,10,68,0,0                  ; movaps        0x440a(%rip),%xmm2        # 5320 <_sk_callback_sse41+0x286>
+  DB  15,89,250                           ; mulps         %xmm2,%xmm7
+  DB  65,15,88,252                        ; addps         %xmm12,%xmm7
+  DB  68,15,40,53,11,68,0,0               ; movaps        0x440b(%rip),%xmm14        # 5330 <_sk_callback_sse41+0x296>
+  DB  68,15,40,252                        ; movaps        %xmm4,%xmm15
+  DB  69,15,89,254                        ; mulps         %xmm14,%xmm15
+  DB  68,15,88,255                        ; addps         %xmm7,%xmm15
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,89,216                           ; mulps         %xmm0,%xmm3
+  DB  15,40,249                           ; movaps        %xmm1,%xmm7
+  DB  15,89,250                           ; mulps         %xmm2,%xmm7
+  DB  15,88,251                           ; addps         %xmm3,%xmm7
+  DB  65,15,40,221                        ; movaps        %xmm13,%xmm3
+  DB  65,15,89,222                        ; mulps         %xmm14,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  69,15,89,248                        ; mulps         %xmm8,%xmm15
+  DB  68,15,92,251                        ; subps         %xmm3,%xmm15
+  DB  69,15,88,215                        ; addps         %xmm15,%xmm10
+  DB  65,15,88,207                        ; addps         %xmm15,%xmm1
+  DB  69,15,88,253                        ; addps         %xmm13,%xmm15
+  DB  15,40,217                           ; movaps        %xmm1,%xmm3
+  DB  65,15,93,223                        ; minps         %xmm15,%xmm3
+  DB  65,15,40,250                        ; movaps        %xmm10,%xmm7
+  DB  15,93,251                           ; minps         %xmm3,%xmm7
+  DB  65,15,89,194                        ; mulps         %xmm10,%xmm0
+  DB  15,89,209                           ; mulps         %xmm1,%xmm2
+  DB  15,88,208                           ; addps         %xmm0,%xmm2
+  DB  69,15,89,247                        ; mulps         %xmm15,%xmm14
+  DB  68,15,88,242                        ; addps         %xmm2,%xmm14
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  68,15,194,207,2                     ; cmpleps       %xmm7,%xmm9
+  DB  65,15,40,222                        ; movaps        %xmm14,%xmm3
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  69,15,40,234                        ; movaps        %xmm10,%xmm13
+  DB  69,15,92,238                        ; subps         %xmm14,%xmm13
+  DB  69,15,89,238                        ; mulps         %xmm14,%xmm13
+  DB  68,15,94,235                        ; divps         %xmm3,%xmm13
+  DB  69,15,88,238                        ; addps         %xmm14,%xmm13
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,69,15,56,20,234                 ; blendvps      %xmm0,%xmm10,%xmm13
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,95,199                        ; maxps         %xmm15,%xmm0
+  DB  68,15,95,208                        ; maxps         %xmm0,%xmm10
+  DB  65,15,40,248                        ; movaps        %xmm8,%xmm7
+  DB  15,40,44,36                         ; movaps        (%rsp),%xmm5
+  DB  15,89,253                           ; mulps         %xmm5,%xmm7
+  DB  15,40,231                           ; movaps        %xmm7,%xmm4
+  DB  65,15,194,226,1                     ; cmpltps       %xmm10,%xmm4
+  DB  65,15,40,213                        ; movaps        %xmm13,%xmm2
+  DB  65,15,92,214                        ; subps         %xmm14,%xmm2
+  DB  68,15,40,223                        ; movaps        %xmm7,%xmm11
+  DB  69,15,92,222                        ; subps         %xmm14,%xmm11
+  DB  65,15,89,211                        ; mulps         %xmm11,%xmm2
+  DB  69,15,92,214                        ; subps         %xmm14,%xmm10
+  DB  65,15,94,210                        ; divps         %xmm10,%xmm2
+  DB  65,15,88,214                        ; addps         %xmm14,%xmm2
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  102,68,15,56,20,234                 ; blendvps      %xmm0,%xmm2,%xmm13
+  DB  68,15,40,225                        ; movaps        %xmm1,%xmm12
+  DB  69,15,92,230                        ; subps         %xmm14,%xmm12
+  DB  69,15,89,230                        ; mulps         %xmm14,%xmm12
+  DB  68,15,94,227                        ; divps         %xmm3,%xmm12
+  DB  69,15,88,230                        ; addps         %xmm14,%xmm12
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,68,15,56,20,225                 ; blendvps      %xmm0,%xmm1,%xmm12
+  DB  65,15,40,204                        ; movaps        %xmm12,%xmm1
+  DB  65,15,92,206                        ; subps         %xmm14,%xmm1
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  65,15,94,202                        ; divps         %xmm10,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  102,68,15,56,20,225                 ; blendvps      %xmm0,%xmm1,%xmm12
+  DB  65,15,40,207                        ; movaps        %xmm15,%xmm1
+  DB  65,15,92,206                        ; subps         %xmm14,%xmm1
+  DB  65,15,89,206                        ; mulps         %xmm14,%xmm1
+  DB  15,94,203                           ; divps         %xmm3,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,65,15,56,20,207                 ; blendvps      %xmm0,%xmm15,%xmm1
+  DB  15,40,209                           ; movaps        %xmm1,%xmm2
+  DB  65,15,92,214                        ; subps         %xmm14,%xmm2
+  DB  65,15,89,211                        ; mulps         %xmm11,%xmm2
+  DB  65,15,94,210                        ; divps         %xmm10,%xmm2
+  DB  65,15,88,214                        ; addps         %xmm14,%xmm2
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  102,15,56,20,202                    ; blendvps      %xmm0,%xmm2,%xmm1
+  DB  68,15,40,13,208,66,0,0              ; movaps        0x42d0(%rip),%xmm9        # 5340 <_sk_callback_sse41+0x2a6>
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  15,92,229                           ; subps         %xmm5,%xmm4
+  DB  15,40,68,36,48                      ; movaps        0x30(%rsp),%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,40,92,36,64                      ; movaps        0x40(%rsp),%xmm3
+  DB  15,89,220                           ; mulps         %xmm4,%xmm3
+  DB  15,89,100,36,80                     ; mulps         0x50(%rsp),%xmm4
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,88,197                        ; addps         %xmm5,%xmm8
+  DB  68,15,40,213                        ; movaps        %xmm5,%xmm10
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  68,15,95,234                        ; maxps         %xmm2,%xmm13
+  DB  68,15,95,226                        ; maxps         %xmm2,%xmm12
+  DB  15,95,202                           ; maxps         %xmm2,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,88,194                           ; addps         %xmm2,%xmm0
+  DB  65,15,88,197                        ; addps         %xmm13,%xmm0
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,40,108,36,32                     ; movaps        0x20(%rsp),%xmm5
+  DB  15,89,213                           ; mulps         %xmm5,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  65,15,88,220                        ; addps         %xmm12,%xmm3
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,40,92,36,16                      ; movaps        0x10(%rsp),%xmm3
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  68,15,88,204                        ; addps         %xmm4,%xmm9
+  DB  68,15,88,201                        ; addps         %xmm1,%xmm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,230                           ; movaps        %xmm6,%xmm4
+  DB  15,40,243                           ; movaps        %xmm3,%xmm6
+  DB  15,40,202                           ; movaps        %xmm2,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  65,15,40,250                        ; movaps        %xmm10,%xmm7
+  DB  72,131,196,104                      ; add           $0x68,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_saturation_sse41
+_sk_saturation_sse41 LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  68,15,40,206                        ; movaps        %xmm6,%xmm9
+  DB  15,40,245                           ; movaps        %xmm5,%xmm6
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  15,41,76,36,48                      ; movaps        %xmm1,0x30(%rsp)
+  DB  15,41,68,36,32                      ; movaps        %xmm0,0x20(%rsp)
+  DB  68,15,40,212                        ; movaps        %xmm4,%xmm10
+  DB  68,15,89,213                        ; mulps         %xmm5,%xmm10
+  DB  68,15,40,220                        ; movaps        %xmm4,%xmm11
+  DB  68,15,89,222                        ; mulps         %xmm6,%xmm11
+  DB  68,15,40,196                        ; movaps        %xmm4,%xmm8
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  69,15,40,241                        ; movaps        %xmm9,%xmm14
+  DB  68,15,41,116,36,16                  ; movaps        %xmm14,0x10(%rsp)
+  DB  15,40,217                           ; movaps        %xmm1,%xmm3
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  68,15,41,76,36,64                   ; movaps        %xmm9,0x40(%rsp)
+  DB  65,15,95,217                        ; maxps         %xmm9,%xmm3
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,95,211                           ; maxps         %xmm3,%xmm2
+  DB  68,15,40,225                        ; movaps        %xmm1,%xmm12
+  DB  69,15,93,225                        ; minps         %xmm9,%xmm12
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  65,15,93,220                        ; minps         %xmm12,%xmm3
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,41,36,36                      ; movaps        %xmm12,(%rsp)
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,93,192                        ; minps         %xmm8,%xmm0
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,93,216                           ; minps         %xmm0,%xmm3
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,95,192                        ; maxps         %xmm8,%xmm0
+  DB  65,15,40,250                        ; movaps        %xmm10,%xmm7
+  DB  15,95,248                           ; maxps         %xmm0,%xmm7
+  DB  15,92,251                           ; subps         %xmm3,%xmm7
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  68,15,94,215                        ; divps         %xmm7,%xmm10
+  DB  68,15,92,219                        ; subps         %xmm3,%xmm11
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  68,15,94,223                        ; divps         %xmm7,%xmm11
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  68,15,94,199                        ; divps         %xmm7,%xmm8
+  DB  15,194,248,4                        ; cmpneqps      %xmm0,%xmm7
+  DB  68,15,84,215                        ; andps         %xmm7,%xmm10
+  DB  68,15,84,223                        ; andps         %xmm7,%xmm11
+  DB  68,15,84,199                        ; andps         %xmm7,%xmm8
+  DB  15,40,21,131,65,0,0                 ; movaps        0x4183(%rip),%xmm2        # 5350 <_sk_callback_sse41+0x2b6>
+  DB  15,40,221                           ; movaps        %xmm5,%xmm3
+  DB  15,89,218                           ; mulps         %xmm2,%xmm3
+  DB  15,40,13,134,65,0,0                 ; movaps        0x4186(%rip),%xmm1        # 5360 <_sk_callback_sse41+0x2c6>
+  DB  15,40,254                           ; movaps        %xmm6,%xmm7
+  DB  15,89,249                           ; mulps         %xmm1,%xmm7
+  DB  15,88,251                           ; addps         %xmm3,%xmm7
+  DB  68,15,40,45,133,65,0,0              ; movaps        0x4185(%rip),%xmm13        # 5370 <_sk_callback_sse41+0x2d6>
+  DB  69,15,89,245                        ; mulps         %xmm13,%xmm14
+  DB  68,15,88,247                        ; addps         %xmm7,%xmm14
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,89,218                           ; mulps         %xmm2,%xmm3
+  DB  65,15,40,251                        ; movaps        %xmm11,%xmm7
+  DB  15,89,249                           ; mulps         %xmm1,%xmm7
+  DB  15,88,251                           ; addps         %xmm3,%xmm7
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  65,15,89,221                        ; mulps         %xmm13,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  68,15,89,244                        ; mulps         %xmm4,%xmm14
+  DB  68,15,92,243                        ; subps         %xmm3,%xmm14
+  DB  69,15,88,214                        ; addps         %xmm14,%xmm10
+  DB  69,15,88,222                        ; addps         %xmm14,%xmm11
+  DB  69,15,88,240                        ; addps         %xmm8,%xmm14
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,93,198                        ; minps         %xmm14,%xmm0
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,93,216                           ; minps         %xmm0,%xmm3
+  DB  65,15,89,210                        ; mulps         %xmm10,%xmm2
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  69,15,89,238                        ; mulps         %xmm14,%xmm13
+  DB  68,15,88,233                        ; addps         %xmm1,%xmm13
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  68,15,194,203,2                     ; cmpleps       %xmm3,%xmm9
+  DB  65,15,40,253                        ; movaps        %xmm13,%xmm7
+  DB  15,92,251                           ; subps         %xmm3,%xmm7
+  DB  69,15,40,250                        ; movaps        %xmm10,%xmm15
+  DB  69,15,92,253                        ; subps         %xmm13,%xmm15
+  DB  69,15,89,253                        ; mulps         %xmm13,%xmm15
+  DB  68,15,94,255                        ; divps         %xmm7,%xmm15
+  DB  69,15,88,253                        ; addps         %xmm13,%xmm15
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,69,15,56,20,250                 ; blendvps      %xmm0,%xmm10,%xmm15
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,95,198                        ; maxps         %xmm14,%xmm0
+  DB  68,15,95,208                        ; maxps         %xmm0,%xmm10
+  DB  15,40,212                           ; movaps        %xmm4,%xmm2
+  DB  65,15,89,212                        ; mulps         %xmm12,%xmm2
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  69,15,194,194,1                     ; cmpltps       %xmm10,%xmm8
+  DB  65,15,40,223                        ; movaps        %xmm15,%xmm3
+  DB  65,15,92,221                        ; subps         %xmm13,%xmm3
+  DB  15,40,202                           ; movaps        %xmm2,%xmm1
+  DB  65,15,92,205                        ; subps         %xmm13,%xmm1
+  DB  15,89,217                           ; mulps         %xmm1,%xmm3
+  DB  69,15,92,213                        ; subps         %xmm13,%xmm10
+  DB  65,15,94,218                        ; divps         %xmm10,%xmm3
+  DB  65,15,88,221                        ; addps         %xmm13,%xmm3
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  102,68,15,56,20,251                 ; blendvps      %xmm0,%xmm3,%xmm15
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  68,15,94,231                        ; divps         %xmm7,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,69,15,56,20,227                 ; blendvps      %xmm0,%xmm11,%xmm12
+  DB  65,15,40,220                        ; movaps        %xmm12,%xmm3
+  DB  65,15,92,221                        ; subps         %xmm13,%xmm3
+  DB  15,89,217                           ; mulps         %xmm1,%xmm3
+  DB  65,15,94,218                        ; divps         %xmm10,%xmm3
+  DB  65,15,88,221                        ; addps         %xmm13,%xmm3
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  102,68,15,56,20,227                 ; blendvps      %xmm0,%xmm3,%xmm12
+  DB  69,15,40,222                        ; movaps        %xmm14,%xmm11
+  DB  69,15,92,221                        ; subps         %xmm13,%xmm11
+  DB  69,15,89,221                        ; mulps         %xmm13,%xmm11
+  DB  68,15,94,223                        ; divps         %xmm7,%xmm11
+  DB  69,15,88,221                        ; addps         %xmm13,%xmm11
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,69,15,56,20,222                 ; blendvps      %xmm0,%xmm14,%xmm11
+  DB  65,15,40,251                        ; movaps        %xmm11,%xmm7
+  DB  65,15,92,253                        ; subps         %xmm13,%xmm7
+  DB  15,89,249                           ; mulps         %xmm1,%xmm7
+  DB  65,15,94,250                        ; divps         %xmm10,%xmm7
+  DB  65,15,88,253                        ; addps         %xmm13,%xmm7
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  102,68,15,56,20,223                 ; blendvps      %xmm0,%xmm7,%xmm11
+  DB  68,15,40,13,75,64,0,0               ; movaps        0x404b(%rip),%xmm9        # 5380 <_sk_callback_sse41+0x2e6>
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,92,204                        ; subps         %xmm4,%xmm9
+  DB  15,40,60,36                         ; movaps        (%rsp),%xmm7
+  DB  15,88,231                           ; addps         %xmm7,%xmm4
+  DB  15,92,226                           ; subps         %xmm2,%xmm4
+  DB  15,40,220                           ; movaps        %xmm4,%xmm3
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  68,15,95,248                        ; maxps         %xmm0,%xmm15
+  DB  68,15,95,224                        ; maxps         %xmm0,%xmm12
+  DB  68,15,95,216                        ; maxps         %xmm0,%xmm11
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  15,40,68,36,32                      ; movaps        0x20(%rsp),%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,193                           ; addps         %xmm1,%xmm0
+  DB  65,15,88,199                        ; addps         %xmm15,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  15,89,206                           ; mulps         %xmm6,%xmm1
+  DB  15,40,84,36,48                      ; movaps        0x30(%rsp),%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,209                           ; addps         %xmm1,%xmm2
+  DB  65,15,88,212                        ; addps         %xmm12,%xmm2
+  DB  15,40,202                           ; movaps        %xmm2,%xmm1
+  DB  68,15,89,68,36,64                   ; mulps         0x40(%rsp),%xmm8
+  DB  15,40,84,36,16                      ; movaps        0x10(%rsp),%xmm2
+  DB  68,15,89,202                        ; mulps         %xmm2,%xmm9
+  DB  69,15,88,200                        ; addps         %xmm8,%xmm9
+  DB  69,15,88,203                        ; addps         %xmm11,%xmm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,40,238                           ; movaps        %xmm6,%xmm5
+  DB  15,40,242                           ; movaps        %xmm2,%xmm6
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_color_sse41
+_sk_color_sse41 LABEL PROC
+  DB  72,131,236,72                       ; sub           $0x48,%rsp
+  DB  68,15,40,230                        ; movaps        %xmm6,%xmm12
+  DB  68,15,41,100,36,16                  ; movaps        %xmm12,0x10(%rsp)
+  DB  68,15,40,221                        ; movaps        %xmm5,%xmm11
+  DB  68,15,41,92,36,32                   ; movaps        %xmm11,0x20(%rsp)
+  DB  68,15,40,212                        ; movaps        %xmm4,%xmm10
+  DB  68,15,41,84,36,48                   ; movaps        %xmm10,0x30(%rsp)
+  DB  15,40,243                           ; movaps        %xmm3,%xmm6
+  DB  15,41,20,36                         ; movaps        %xmm2,(%rsp)
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,40,240                        ; movaps        %xmm8,%xmm14
+  DB  15,40,231                           ; movaps        %xmm7,%xmm4
+  DB  68,15,89,244                        ; mulps         %xmm4,%xmm14
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,40,13,144,63,0,0              ; movaps        0x3f90(%rip),%xmm9        # 5390 <_sk_callback_sse41+0x2f6>
+  DB  65,15,40,250                        ; movaps        %xmm10,%xmm7
+  DB  65,15,89,249                        ; mulps         %xmm9,%xmm7
+  DB  68,15,40,21,144,63,0,0              ; movaps        0x3f90(%rip),%xmm10        # 53a0 <_sk_callback_sse41+0x306>
+  DB  65,15,40,219                        ; movaps        %xmm11,%xmm3
+  DB  65,15,89,218                        ; mulps         %xmm10,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  68,15,40,29,141,63,0,0              ; movaps        0x3f8d(%rip),%xmm11        # 53b0 <_sk_callback_sse41+0x316>
+  DB  69,15,40,236                        ; movaps        %xmm12,%xmm13
+  DB  69,15,89,235                        ; mulps         %xmm11,%xmm13
+  DB  68,15,88,235                        ; addps         %xmm3,%xmm13
+  DB  65,15,40,222                        ; movaps        %xmm14,%xmm3
+  DB  65,15,89,217                        ; mulps         %xmm9,%xmm3
+  DB  15,40,249                           ; movaps        %xmm1,%xmm7
+  DB  65,15,89,250                        ; mulps         %xmm10,%xmm7
+  DB  15,88,251                           ; addps         %xmm3,%xmm7
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  65,15,89,219                        ; mulps         %xmm11,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  68,15,89,238                        ; mulps         %xmm6,%xmm13
+  DB  68,15,92,235                        ; subps         %xmm3,%xmm13
+  DB  69,15,88,245                        ; addps         %xmm13,%xmm14
+  DB  65,15,88,205                        ; addps         %xmm13,%xmm1
+  DB  68,15,88,232                        ; addps         %xmm0,%xmm13
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,93,197                        ; minps         %xmm13,%xmm0
+  DB  65,15,40,222                        ; movaps        %xmm14,%xmm3
+  DB  15,93,216                           ; minps         %xmm0,%xmm3
+  DB  69,15,89,206                        ; mulps         %xmm14,%xmm9
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  69,15,88,209                        ; addps         %xmm9,%xmm10
+  DB  69,15,89,221                        ; mulps         %xmm13,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  68,15,194,203,2                     ; cmpleps       %xmm3,%xmm9
+  DB  69,15,40,230                        ; movaps        %xmm14,%xmm12
+  DB  69,15,92,227                        ; subps         %xmm11,%xmm12
+  DB  69,15,89,227                        ; mulps         %xmm11,%xmm12
+  DB  65,15,40,251                        ; movaps        %xmm11,%xmm7
+  DB  15,92,251                           ; subps         %xmm3,%xmm7
+  DB  68,15,94,231                        ; divps         %xmm7,%xmm12
+  DB  69,15,88,227                        ; addps         %xmm11,%xmm12
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,69,15,56,20,230                 ; blendvps      %xmm0,%xmm14,%xmm12
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,95,197                        ; maxps         %xmm13,%xmm0
+  DB  65,15,40,214                        ; movaps        %xmm14,%xmm2
+  DB  15,95,208                           ; maxps         %xmm0,%xmm2
+  DB  15,40,222                           ; movaps        %xmm6,%xmm3
+  DB  15,89,220                           ; mulps         %xmm4,%xmm3
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,194,210,1                     ; cmpltps       %xmm2,%xmm10
+  DB  69,15,40,244                        ; movaps        %xmm12,%xmm14
+  DB  69,15,92,243                        ; subps         %xmm11,%xmm14
+  DB  68,15,40,251                        ; movaps        %xmm3,%xmm15
+  DB  69,15,92,251                        ; subps         %xmm11,%xmm15
+  DB  69,15,89,247                        ; mulps         %xmm15,%xmm14
+  DB  65,15,92,211                        ; subps         %xmm11,%xmm2
+  DB  68,15,94,242                        ; divps         %xmm2,%xmm14
+  DB  69,15,88,243                        ; addps         %xmm11,%xmm14
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,69,15,56,20,230                 ; blendvps      %xmm0,%xmm14,%xmm12
+  DB  68,15,40,241                        ; movaps        %xmm1,%xmm14
+  DB  69,15,92,243                        ; subps         %xmm11,%xmm14
+  DB  69,15,89,243                        ; mulps         %xmm11,%xmm14
+  DB  68,15,94,247                        ; divps         %xmm7,%xmm14
+  DB  69,15,88,243                        ; addps         %xmm11,%xmm14
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,68,15,56,20,241                 ; blendvps      %xmm0,%xmm1,%xmm14
+  DB  65,15,40,206                        ; movaps        %xmm14,%xmm1
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  65,15,89,207                        ; mulps         %xmm15,%xmm1
+  DB  15,94,202                           ; divps         %xmm2,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,68,15,56,20,241                 ; blendvps      %xmm0,%xmm1,%xmm14
+  DB  65,15,40,205                        ; movaps        %xmm13,%xmm1
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  15,94,207                           ; divps         %xmm7,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,65,15,56,20,205                 ; blendvps      %xmm0,%xmm13,%xmm1
+  DB  15,40,249                           ; movaps        %xmm1,%xmm7
+  DB  65,15,92,251                        ; subps         %xmm11,%xmm7
+  DB  65,15,89,255                        ; mulps         %xmm15,%xmm7
+  DB  15,94,250                           ; divps         %xmm2,%xmm7
+  DB  65,15,88,251                        ; addps         %xmm11,%xmm7
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,15,56,20,207                    ; blendvps      %xmm0,%xmm7,%xmm1
+  DB  68,15,40,13,73,62,0,0               ; movaps        0x3e49(%rip),%xmm9        # 53c0 <_sk_callback_sse41+0x326>
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  15,89,232                           ; mulps         %xmm0,%xmm5
+  DB  15,89,4,36                          ; mulps         (%rsp),%xmm0
+  DB  68,15,92,206                        ; subps         %xmm6,%xmm9
+  DB  15,88,244                           ; addps         %xmm4,%xmm6
+  DB  15,40,252                           ; movaps        %xmm4,%xmm7
+  DB  15,92,243                           ; subps         %xmm3,%xmm6
+  DB  15,40,222                           ; movaps        %xmm6,%xmm3
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  68,15,95,226                        ; maxps         %xmm2,%xmm12
+  DB  68,15,95,242                        ; maxps         %xmm2,%xmm14
+  DB  15,95,202                           ; maxps         %xmm2,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,40,100,36,48                     ; movaps        0x30(%rsp),%xmm4
+  DB  15,89,212                           ; mulps         %xmm4,%xmm2
+  DB  68,15,88,194                        ; addps         %xmm2,%xmm8
+  DB  69,15,88,196                        ; addps         %xmm12,%xmm8
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,40,116,36,32                     ; movaps        0x20(%rsp),%xmm6
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,40,213                        ; movaps        %xmm5,%xmm10
+  DB  68,15,88,210                        ; addps         %xmm2,%xmm10
+  DB  69,15,88,214                        ; addps         %xmm14,%xmm10
+  DB  15,40,84,36,16                      ; movaps        0x10(%rsp),%xmm2
+  DB  68,15,89,202                        ; mulps         %xmm2,%xmm9
+  DB  68,15,88,200                        ; addps         %xmm0,%xmm9
+  DB  68,15,88,201                        ; addps         %xmm1,%xmm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,238                           ; movaps        %xmm6,%xmm5
+  DB  15,40,242                           ; movaps        %xmm2,%xmm6
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  72,131,196,72                       ; add           $0x48,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminosity_sse41
+_sk_luminosity_sse41 LABEL PROC
+  DB  72,131,236,72                       ; sub           $0x48,%rsp
+  DB  15,41,116,36,16                     ; movaps        %xmm6,0x10(%rsp)
+  DB  15,41,108,36,48                     ; movaps        %xmm5,0x30(%rsp)
+  DB  68,15,40,196                        ; movaps        %xmm4,%xmm8
+  DB  68,15,41,68,36,32                   ; movaps        %xmm8,0x20(%rsp)
+  DB  15,41,20,36                         ; movaps        %xmm2,(%rsp)
+  DB  15,40,224                           ; movaps        %xmm0,%xmm4
+  DB  68,15,40,219                        ; movaps        %xmm3,%xmm11
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  68,15,40,5,155,61,0,0               ; movaps        0x3d9b(%rip),%xmm8        # 53d0 <_sk_callback_sse41+0x336>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  68,15,40,21,159,61,0,0              ; movaps        0x3d9f(%rip),%xmm10        # 53e0 <_sk_callback_sse41+0x346>
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  65,15,89,234                        ; mulps         %xmm10,%xmm5
+  DB  15,88,232                           ; addps         %xmm0,%xmm5
+  DB  68,15,40,37,157,61,0,0              ; movaps        0x3d9d(%rip),%xmm12        # 53f0 <_sk_callback_sse41+0x356>
+  DB  68,15,40,242                        ; movaps        %xmm2,%xmm14
+  DB  69,15,89,244                        ; mulps         %xmm12,%xmm14
+  DB  68,15,88,245                        ; addps         %xmm5,%xmm14
+  DB  65,15,40,235                        ; movaps        %xmm11,%xmm5
+  DB  65,15,89,232                        ; mulps         %xmm8,%xmm5
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  65,15,89,210                        ; mulps         %xmm10,%xmm2
+  DB  15,88,213                           ; addps         %xmm5,%xmm2
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,89,198                           ; mulps         %xmm6,%xmm0
+  DB  15,40,232                           ; movaps        %xmm0,%xmm5
+  DB  65,15,89,236                        ; mulps         %xmm12,%xmm5
+  DB  15,88,234                           ; addps         %xmm2,%xmm5
+  DB  68,15,89,247                        ; mulps         %xmm7,%xmm14
+  DB  68,15,92,245                        ; subps         %xmm5,%xmm14
+  DB  69,15,88,222                        ; addps         %xmm14,%xmm11
+  DB  69,15,88,206                        ; addps         %xmm14,%xmm9
+  DB  68,15,88,240                        ; addps         %xmm0,%xmm14
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,93,198                        ; minps         %xmm14,%xmm0
+  DB  65,15,40,235                        ; movaps        %xmm11,%xmm5
+  DB  15,93,232                           ; minps         %xmm0,%xmm5
+  DB  69,15,89,195                        ; mulps         %xmm11,%xmm8
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,208                        ; addps         %xmm8,%xmm10
+  DB  69,15,89,230                        ; mulps         %xmm14,%xmm12
+  DB  69,15,88,226                        ; addps         %xmm10,%xmm12
+  DB  69,15,87,210                        ; xorps         %xmm10,%xmm10
+  DB  68,15,194,213,2                     ; cmpleps       %xmm5,%xmm10
+  DB  69,15,40,235                        ; movaps        %xmm11,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  69,15,89,236                        ; mulps         %xmm12,%xmm13
+  DB  65,15,40,244                        ; movaps        %xmm12,%xmm6
+  DB  15,92,245                           ; subps         %xmm5,%xmm6
+  DB  68,15,94,238                        ; divps         %xmm6,%xmm13
+  DB  69,15,88,236                        ; addps         %xmm12,%xmm13
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,69,15,56,20,235                 ; blendvps      %xmm0,%xmm11,%xmm13
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,95,198                        ; maxps         %xmm14,%xmm0
+  DB  65,15,40,211                        ; movaps        %xmm11,%xmm2
+  DB  15,95,208                           ; maxps         %xmm0,%xmm2
+  DB  15,40,235                           ; movaps        %xmm3,%xmm5
+  DB  15,89,239                           ; mulps         %xmm7,%xmm5
+  DB  68,15,40,221                        ; movaps        %xmm5,%xmm11
+  DB  68,15,194,218,1                     ; cmpltps       %xmm2,%xmm11
+  DB  69,15,40,197                        ; movaps        %xmm13,%xmm8
+  DB  69,15,92,196                        ; subps         %xmm12,%xmm8
+  DB  68,15,40,253                        ; movaps        %xmm5,%xmm15
+  DB  69,15,92,252                        ; subps         %xmm12,%xmm15
+  DB  69,15,89,199                        ; mulps         %xmm15,%xmm8
+  DB  65,15,92,212                        ; subps         %xmm12,%xmm2
+  DB  68,15,94,194                        ; divps         %xmm2,%xmm8
+  DB  69,15,88,196                        ; addps         %xmm12,%xmm8
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  102,69,15,56,20,232                 ; blendvps      %xmm0,%xmm8,%xmm13
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  69,15,92,196                        ; subps         %xmm12,%xmm8
+  DB  69,15,89,196                        ; mulps         %xmm12,%xmm8
+  DB  68,15,94,198                        ; divps         %xmm6,%xmm8
+  DB  69,15,88,196                        ; addps         %xmm12,%xmm8
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,69,15,56,20,193                 ; blendvps      %xmm0,%xmm9,%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  69,15,89,207                        ; mulps         %xmm15,%xmm9
+  DB  68,15,94,202                        ; divps         %xmm2,%xmm9
+  DB  69,15,88,204                        ; addps         %xmm12,%xmm9
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  102,69,15,56,20,193                 ; blendvps      %xmm0,%xmm9,%xmm8
+  DB  69,15,40,206                        ; movaps        %xmm14,%xmm9
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  69,15,89,204                        ; mulps         %xmm12,%xmm9
+  DB  68,15,94,206                        ; divps         %xmm6,%xmm9
+  DB  69,15,88,204                        ; addps         %xmm12,%xmm9
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,69,15,56,20,206                 ; blendvps      %xmm0,%xmm14,%xmm9
+  DB  65,15,40,241                        ; movaps        %xmm9,%xmm6
+  DB  65,15,92,244                        ; subps         %xmm12,%xmm6
+  DB  65,15,89,247                        ; mulps         %xmm15,%xmm6
+  DB  15,94,242                           ; divps         %xmm2,%xmm6
+  DB  65,15,88,244                        ; addps         %xmm12,%xmm6
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  102,68,15,56,20,206                 ; blendvps      %xmm0,%xmm6,%xmm9
+  DB  15,40,5,83,60,0,0                   ; movaps        0x3c53(%rip),%xmm0        # 5400 <_sk_callback_sse41+0x366>
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,92,215                           ; subps         %xmm7,%xmm2
+  DB  15,89,226                           ; mulps         %xmm2,%xmm4
+  DB  15,89,202                           ; mulps         %xmm2,%xmm1
+  DB  15,89,20,36                         ; mulps         (%rsp),%xmm2
+  DB  15,92,195                           ; subps         %xmm3,%xmm0
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  15,92,221                           ; subps         %xmm5,%xmm3
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  68,15,95,237                        ; maxps         %xmm5,%xmm13
+  DB  68,15,95,197                        ; maxps         %xmm5,%xmm8
+  DB  68,15,95,205                        ; maxps         %xmm5,%xmm9
+  DB  15,40,232                           ; movaps        %xmm0,%xmm5
+  DB  68,15,40,84,36,32                   ; movaps        0x20(%rsp),%xmm10
+  DB  65,15,89,234                        ; mulps         %xmm10,%xmm5
+  DB  15,88,229                           ; addps         %xmm5,%xmm4
+  DB  65,15,88,229                        ; addps         %xmm13,%xmm4
+  DB  15,40,240                           ; movaps        %xmm0,%xmm6
+  DB  15,40,108,36,48                     ; movaps        0x30(%rsp),%xmm5
+  DB  15,89,245                           ; mulps         %xmm5,%xmm6
+  DB  15,88,206                           ; addps         %xmm6,%xmm1
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  15,40,116,36,16                     ; movaps        0x10(%rsp),%xmm6
+  DB  15,89,198                           ; mulps         %xmm6,%xmm0
+  DB  15,88,194                           ; addps         %xmm2,%xmm0
+  DB  68,15,88,200                        ; addps         %xmm0,%xmm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  65,15,40,226                        ; movaps        %xmm10,%xmm4
+  DB  72,131,196,72                       ; add           $0x48,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_rgba_8888_sse41
+_sk_srcover_rgba_8888_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,217,0,0,0                    ; jne           1904 <_sk_srcover_rgba_8888_sse41+0xe7>
+  DB  243,15,111,60,144                   ; movdqu        (%rax,%rdx,4),%xmm7
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  102,15,111,37,213,59,0,0            ; movdqa        0x3bd5(%rip),%xmm4        # 5410 <_sk_callback_sse41+0x376>
+  DB  102,15,219,231                      ; pand          %xmm7,%xmm4
+  DB  15,91,228                           ; cvtdq2ps      %xmm4,%xmm4
+  DB  102,15,111,239                      ; movdqa        %xmm7,%xmm5
+  DB  102,15,56,0,45,209,59,0,0           ; pshufb        0x3bd1(%rip),%xmm5        # 5420 <_sk_callback_sse41+0x386>
+  DB  15,91,237                           ; cvtdq2ps      %xmm5,%xmm5
+  DB  102,15,111,247                      ; movdqa        %xmm7,%xmm6
+  DB  102,15,56,0,53,209,59,0,0           ; pshufb        0x3bd1(%rip),%xmm6        # 5430 <_sk_callback_sse41+0x396>
+  DB  15,91,246                           ; cvtdq2ps      %xmm6,%xmm6
+  DB  102,15,114,215,24                   ; psrld         $0x18,%xmm7
+  DB  15,91,255                           ; cvtdq2ps      %xmm7,%xmm7
+  DB  68,15,40,5,206,59,0,0               ; movaps        0x3bce(%rip),%xmm8        # 5440 <_sk_callback_sse41+0x3a6>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  68,15,40,37,210,59,0,0              ; movaps        0x3bd2(%rip),%xmm12        # 5450 <_sk_callback_sse41+0x3b6>
+  DB  65,15,89,196                        ; mulps         %xmm12,%xmm0
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  68,15,88,200                        ; addps         %xmm0,%xmm9
+  DB  65,15,89,204                        ; mulps         %xmm12,%xmm1
+  DB  69,15,40,208                        ; movaps        %xmm8,%xmm10
+  DB  68,15,89,213                        ; mulps         %xmm5,%xmm10
+  DB  68,15,88,209                        ; addps         %xmm1,%xmm10
+  DB  65,15,89,212                        ; mulps         %xmm12,%xmm2
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,222                        ; mulps         %xmm6,%xmm11
+  DB  68,15,88,218                        ; addps         %xmm2,%xmm11
+  DB  65,15,89,220                        ; mulps         %xmm12,%xmm3
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,88,195                        ; addps         %xmm3,%xmm8
+  DB  102,65,15,91,193                    ; cvtps2dq      %xmm9,%xmm0
+  DB  102,65,15,91,202                    ; cvtps2dq      %xmm10,%xmm1
+  DB  102,15,114,241,8                    ; pslld         $0x8,%xmm1
+  DB  102,15,235,200                      ; por           %xmm0,%xmm1
+  DB  102,65,15,91,211                    ; cvtps2dq      %xmm11,%xmm2
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,65,15,91,192                    ; cvtps2dq      %xmm8,%xmm0
+  DB  102,15,114,240,24                   ; pslld         $0x18,%xmm0
+  DB  102,15,235,194                      ; por           %xmm2,%xmm0
+  DB  102,15,235,193                      ; por           %xmm1,%xmm0
+  DB  117,89                              ; jne           1944 <_sk_srcover_rgba_8888_sse41+0x127>
+  DB  243,15,127,4,144                    ; movdqu        %xmm0,(%rax,%rdx,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  65,15,40,211                        ; movaps        %xmm11,%xmm2
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,255                      ; pxor          %xmm7,%xmm7
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,35                              ; je            1938 <_sk_srcover_rgba_8888_sse41+0x11b>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            1930 <_sk_srcover_rgba_8888_sse41+0x113>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  15,133,11,255,255,255               ; jne           1830 <_sk_srcover_rgba_8888_sse41+0x13>
+  DB  102,15,110,100,144,8                ; movd          0x8(%rax,%rdx,4),%xmm4
+  DB  102,15,112,252,69                   ; pshufd        $0x45,%xmm4,%xmm7
+  DB  102,15,58,34,124,144,4,1            ; pinsrd        $0x1,0x4(%rax,%rdx,4),%xmm7
+  DB  102,15,58,34,60,144,0               ; pinsrd        $0x0,(%rax,%rdx,4),%xmm7
+  DB  233,236,254,255,255                 ; jmpq          1830 <_sk_srcover_rgba_8888_sse41+0x13>
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,28                              ; je            196d <_sk_srcover_rgba_8888_sse41+0x150>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,14                              ; je            1965 <_sk_srcover_rgba_8888_sse41+0x148>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,147                             ; jne           18f0 <_sk_srcover_rgba_8888_sse41+0xd3>
+  DB  102,15,58,22,68,144,8,2             ; pextrd        $0x2,%xmm0,0x8(%rax,%rdx,4)
+  DB  102,15,58,22,68,144,4,1             ; pextrd        $0x1,%xmm0,0x4(%rax,%rdx,4)
+  DB  102,15,126,4,144                    ; movd          %xmm0,(%rax,%rdx,4)
+  DB  233,121,255,255,255                 ; jmpq          18f0 <_sk_srcover_rgba_8888_sse41+0xd3>
+
+PUBLIC _sk_clamp_0_sse41
+_sk_clamp_0_sse41 LABEL PROC
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  65,15,95,192                        ; maxps         %xmm8,%xmm0
+  DB  65,15,95,200                        ; maxps         %xmm8,%xmm1
+  DB  65,15,95,208                        ; maxps         %xmm8,%xmm2
+  DB  65,15,95,216                        ; maxps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_1_sse41
+_sk_clamp_1_sse41 LABEL PROC
+  DB  68,15,40,5,201,58,0,0               ; movaps        0x3ac9(%rip),%xmm8        # 5460 <_sk_callback_sse41+0x3c6>
+  DB  65,15,93,192                        ; minps         %xmm8,%xmm0
+  DB  65,15,93,200                        ; minps         %xmm8,%xmm1
+  DB  65,15,93,208                        ; minps         %xmm8,%xmm2
+  DB  65,15,93,216                        ; minps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_a_sse41
+_sk_clamp_a_sse41 LABEL PROC
+  DB  15,93,29,190,58,0,0                 ; minps         0x3abe(%rip),%xmm3        # 5470 <_sk_callback_sse41+0x3d6>
+  DB  15,93,195                           ; minps         %xmm3,%xmm0
+  DB  15,93,203                           ; minps         %xmm3,%xmm1
+  DB  15,93,211                           ; minps         %xmm3,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_set_rgb_sse41
+_sk_set_rgb_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_rb_sse41
+_sk_swap_rb_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_sse41
+_sk_swap_sse41 LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  68,15,40,216                        ; movaps        %xmm0,%xmm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  65,15,40,227                        ; movaps        %xmm11,%xmm4
+  DB  65,15,40,234                        ; movaps        %xmm10,%xmm5
+  DB  65,15,40,241                        ; movaps        %xmm9,%xmm6
+  DB  65,15,40,248                        ; movaps        %xmm8,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_src_dst_sse41
+_sk_move_src_dst_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,224                           ; movaps        %xmm0,%xmm4
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  15,40,242                           ; movaps        %xmm2,%xmm6
+  DB  15,40,251                           ; movaps        %xmm3,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_dst_src_sse41
+_sk_move_dst_src_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_premul_sse41
+_sk_premul_sse41 LABEL PROC
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,89,203                           ; mulps         %xmm3,%xmm1
+  DB  15,89,211                           ; mulps         %xmm3,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_unpremul_sse41
+_sk_unpremul_sse41 LABEL PROC
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,40,13,41,58,0,0               ; movaps        0x3a29(%rip),%xmm9        # 5480 <_sk_callback_sse41+0x3e6>
+  DB  68,15,94,203                        ; divps         %xmm3,%xmm9
+  DB  68,15,194,195,4                     ; cmpneqps      %xmm3,%xmm8
+  DB  69,15,84,193                        ; andps         %xmm9,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_from_srgb_sse41
+_sk_from_srgb_sse41 LABEL PROC
+  DB  68,15,40,29,20,58,0,0               ; movaps        0x3a14(%rip),%xmm11        # 5490 <_sk_callback_sse41+0x3f6>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  69,15,89,210                        ; mulps         %xmm10,%xmm10
+  DB  68,15,40,37,12,58,0,0               ; movaps        0x3a0c(%rip),%xmm12        # 54a0 <_sk_callback_sse41+0x406>
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,89,196                        ; mulps         %xmm12,%xmm8
+  DB  68,15,40,45,12,58,0,0               ; movaps        0x3a0c(%rip),%xmm13        # 54b0 <_sk_callback_sse41+0x416>
+  DB  69,15,88,197                        ; addps         %xmm13,%xmm8
+  DB  69,15,89,194                        ; mulps         %xmm10,%xmm8
+  DB  68,15,40,53,12,58,0,0               ; movaps        0x3a0c(%rip),%xmm14        # 54c0 <_sk_callback_sse41+0x426>
+  DB  69,15,88,198                        ; addps         %xmm14,%xmm8
+  DB  68,15,40,61,16,58,0,0               ; movaps        0x3a10(%rip),%xmm15        # 54d0 <_sk_callback_sse41+0x436>
+  DB  65,15,194,199,1                     ; cmpltps       %xmm15,%xmm0
+  DB  102,69,15,56,20,193                 ; blendvps      %xmm0,%xmm9,%xmm8
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,89,211                        ; mulps         %xmm11,%xmm10
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  15,89,192                           ; mulps         %xmm0,%xmm0
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  69,15,89,204                        ; mulps         %xmm12,%xmm9
+  DB  69,15,88,205                        ; addps         %xmm13,%xmm9
+  DB  68,15,89,200                        ; mulps         %xmm0,%xmm9
+  DB  69,15,88,206                        ; addps         %xmm14,%xmm9
+  DB  65,15,194,207,1                     ; cmpltps       %xmm15,%xmm1
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  102,69,15,56,20,202                 ; blendvps      %xmm0,%xmm10,%xmm9
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  68,15,40,210                        ; movaps        %xmm2,%xmm10
+  DB  69,15,89,210                        ; mulps         %xmm10,%xmm10
+  DB  68,15,89,226                        ; mulps         %xmm2,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,212                        ; mulps         %xmm12,%xmm10
+  DB  69,15,88,214                        ; addps         %xmm14,%xmm10
+  DB  65,15,194,215,1                     ; cmpltps       %xmm15,%xmm2
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  102,69,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_to_srgb_sse41
+_sk_to_srgb_sse41 LABEL PROC
+  DB  72,131,236,24                       ; sub           $0x18,%rsp
+  DB  15,41,60,36                         ; movaps        %xmm7,(%rsp)
+  DB  15,40,254                           ; movaps        %xmm6,%xmm7
+  DB  15,40,245                           ; movaps        %xmm5,%xmm6
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  15,40,218                           ; movaps        %xmm2,%xmm3
+  DB  15,40,209                           ; movaps        %xmm1,%xmm2
+  DB  68,15,82,192                        ; rsqrtps       %xmm0,%xmm8
+  DB  68,15,40,29,133,57,0,0              ; movaps        0x3985(%rip),%xmm11        # 54e0 <_sk_callback_sse41+0x446>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  68,15,40,37,133,57,0,0              ; movaps        0x3985(%rip),%xmm12        # 54f0 <_sk_callback_sse41+0x456>
+  DB  69,15,40,248                        ; movaps        %xmm8,%xmm15
+  DB  69,15,89,252                        ; mulps         %xmm12,%xmm15
+  DB  68,15,40,21,133,57,0,0              ; movaps        0x3985(%rip),%xmm10        # 5500 <_sk_callback_sse41+0x466>
+  DB  69,15,88,250                        ; addps         %xmm10,%xmm15
+  DB  69,15,89,248                        ; mulps         %xmm8,%xmm15
+  DB  68,15,40,45,133,57,0,0              ; movaps        0x3985(%rip),%xmm13        # 5510 <_sk_callback_sse41+0x476>
+  DB  69,15,88,253                        ; addps         %xmm13,%xmm15
+  DB  68,15,40,53,137,57,0,0              ; movaps        0x3989(%rip),%xmm14        # 5520 <_sk_callback_sse41+0x486>
+  DB  69,15,88,198                        ; addps         %xmm14,%xmm8
+  DB  69,15,83,192                        ; rcpps         %xmm8,%xmm8
+  DB  69,15,89,199                        ; mulps         %xmm15,%xmm8
+  DB  68,15,40,61,133,57,0,0              ; movaps        0x3985(%rip),%xmm15        # 5530 <_sk_callback_sse41+0x496>
+  DB  65,15,194,199,1                     ; cmpltps       %xmm15,%xmm0
+  DB  102,69,15,56,20,193                 ; blendvps      %xmm0,%xmm9,%xmm8
+  DB  68,15,82,202                        ; rsqrtps       %xmm2,%xmm9
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,89,196                        ; mulps         %xmm12,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,88,197                        ; addps         %xmm13,%xmm0
+  DB  69,15,88,206                        ; addps         %xmm14,%xmm9
+  DB  69,15,83,201                        ; rcpps         %xmm9,%xmm9
+  DB  68,15,89,200                        ; mulps         %xmm0,%xmm9
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  65,15,194,215,1                     ; cmpltps       %xmm15,%xmm2
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  102,68,15,56,20,201                 ; blendvps      %xmm0,%xmm1,%xmm9
+  DB  15,82,195                           ; rsqrtps       %xmm3,%xmm0
+  DB  68,15,89,224                        ; mulps         %xmm0,%xmm12
+  DB  69,15,88,226                        ; addps         %xmm10,%xmm12
+  DB  68,15,89,224                        ; mulps         %xmm0,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  65,15,88,198                        ; addps         %xmm14,%xmm0
+  DB  68,15,83,208                        ; rcpps         %xmm0,%xmm10
+  DB  69,15,89,212                        ; mulps         %xmm12,%xmm10
+  DB  68,15,89,219                        ; mulps         %xmm3,%xmm11
+  DB  65,15,194,223,1                     ; cmpltps       %xmm15,%xmm3
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  102,69,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  15,40,220                           ; movaps        %xmm4,%xmm3
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,40,238                           ; movaps        %xmm6,%xmm5
+  DB  15,40,247                           ; movaps        %xmm7,%xmm6
+  DB  15,40,60,36                         ; movaps        (%rsp),%xmm7
+  DB  72,131,196,24                       ; add           $0x18,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_rgb_to_hsl_sse41
+_sk_rgb_to_hsl_sse41 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  69,15,95,217                        ; maxps         %xmm9,%xmm11
+  DB  68,15,95,218                        ; maxps         %xmm2,%xmm11
+  DB  69,15,40,224                        ; movaps        %xmm8,%xmm12
+  DB  69,15,93,225                        ; minps         %xmm9,%xmm12
+  DB  68,15,93,226                        ; minps         %xmm2,%xmm12
+  DB  65,15,40,203                        ; movaps        %xmm11,%xmm1
+  DB  65,15,92,204                        ; subps         %xmm12,%xmm1
+  DB  68,15,40,53,207,56,0,0              ; movaps        0x38cf(%rip),%xmm14        # 5540 <_sk_callback_sse41+0x4a6>
+  DB  68,15,94,241                        ; divps         %xmm1,%xmm14
+  DB  69,15,40,211                        ; movaps        %xmm11,%xmm10
+  DB  69,15,194,208,0                     ; cmpeqps       %xmm8,%xmm10
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  15,92,194                           ; subps         %xmm2,%xmm0
+  DB  65,15,89,198                        ; mulps         %xmm14,%xmm0
+  DB  69,15,40,249                        ; movaps        %xmm9,%xmm15
+  DB  68,15,194,250,1                     ; cmpltps       %xmm2,%xmm15
+  DB  68,15,84,61,182,56,0,0              ; andps         0x38b6(%rip),%xmm15        # 5550 <_sk_callback_sse41+0x4b6>
+  DB  68,15,88,248                        ; addps         %xmm0,%xmm15
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,194,193,0                     ; cmpeqps       %xmm9,%xmm0
+  DB  65,15,92,208                        ; subps         %xmm8,%xmm2
+  DB  65,15,89,214                        ; mulps         %xmm14,%xmm2
+  DB  68,15,40,45,169,56,0,0              ; movaps        0x38a9(%rip),%xmm13        # 5560 <_sk_callback_sse41+0x4c6>
+  DB  65,15,88,213                        ; addps         %xmm13,%xmm2
+  DB  69,15,92,193                        ; subps         %xmm9,%xmm8
+  DB  69,15,89,198                        ; mulps         %xmm14,%xmm8
+  DB  68,15,88,5,165,56,0,0               ; addps         0x38a5(%rip),%xmm8        # 5570 <_sk_callback_sse41+0x4d6>
+  DB  102,68,15,56,20,194                 ; blendvps      %xmm0,%xmm2,%xmm8
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  102,69,15,56,20,199                 ; blendvps      %xmm0,%xmm15,%xmm8
+  DB  68,15,89,5,157,56,0,0               ; mulps         0x389d(%rip),%xmm8        # 5580 <_sk_callback_sse41+0x4e6>
+  DB  69,15,40,203                        ; movaps        %xmm11,%xmm9
+  DB  69,15,194,204,4                     ; cmpneqps      %xmm12,%xmm9
+  DB  69,15,84,193                        ; andps         %xmm9,%xmm8
+  DB  69,15,92,235                        ; subps         %xmm11,%xmm13
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  15,40,5,145,56,0,0                  ; movaps        0x3891(%rip),%xmm0        # 5590 <_sk_callback_sse41+0x4f6>
+  DB  65,15,40,211                        ; movaps        %xmm11,%xmm2
+  DB  15,89,208                           ; mulps         %xmm0,%xmm2
+  DB  15,194,194,1                        ; cmpltps       %xmm2,%xmm0
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  102,69,15,56,20,221                 ; blendvps      %xmm0,%xmm13,%xmm11
+  DB  65,15,94,203                        ; divps         %xmm11,%xmm1
+  DB  65,15,84,201                        ; andps         %xmm9,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hsl_to_rgb_sse41
+_sk_hsl_to_rgb_sse41 LABEL PROC
+  DB  72,131,236,104                      ; sub           $0x68,%rsp
+  DB  15,41,124,36,80                     ; movaps        %xmm7,0x50(%rsp)
+  DB  15,41,116,36,64                     ; movaps        %xmm6,0x40(%rsp)
+  DB  15,41,108,36,48                     ; movaps        %xmm5,0x30(%rsp)
+  DB  15,41,100,36,32                     ; movaps        %xmm4,0x20(%rsp)
+  DB  15,41,92,36,16                      ; movaps        %xmm3,0x10(%rsp)
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  68,15,40,13,83,56,0,0               ; movaps        0x3853(%rip),%xmm9        # 55a0 <_sk_callback_sse41+0x506>
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  15,194,194,2                        ; cmpleps       %xmm2,%xmm0
+  DB  15,40,217                           ; movaps        %xmm1,%xmm3
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  15,89,234                           ; mulps         %xmm2,%xmm5
+  DB  15,92,221                           ; subps         %xmm5,%xmm3
+  DB  102,15,56,20,235                    ; blendvps      %xmm0,%xmm3,%xmm5
+  DB  15,88,234                           ; addps         %xmm2,%xmm5
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  15,41,20,36                         ; movaps        %xmm2,(%rsp)
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  68,15,92,197                        ; subps         %xmm5,%xmm8
+  DB  68,15,40,53,47,56,0,0               ; movaps        0x382f(%rip),%xmm14        # 55b0 <_sk_callback_sse41+0x516>
+  DB  69,15,88,242                        ; addps         %xmm10,%xmm14
+  DB  102,65,15,58,8,198,1                ; roundps       $0x1,%xmm14,%xmm0
+  DB  68,15,92,240                        ; subps         %xmm0,%xmm14
+  DB  68,15,40,29,40,56,0,0               ; movaps        0x3828(%rip),%xmm11        # 55c0 <_sk_callback_sse41+0x526>
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,194,198,2                     ; cmpleps       %xmm14,%xmm0
+  DB  15,40,245                           ; movaps        %xmm5,%xmm6
+  DB  65,15,92,240                        ; subps         %xmm8,%xmm6
+  DB  15,40,61,33,56,0,0                  ; movaps        0x3821(%rip),%xmm7        # 55d0 <_sk_callback_sse41+0x536>
+  DB  69,15,40,238                        ; movaps        %xmm14,%xmm13
+  DB  68,15,89,239                        ; mulps         %xmm7,%xmm13
+  DB  15,40,29,34,56,0,0                  ; movaps        0x3822(%rip),%xmm3        # 55e0 <_sk_callback_sse41+0x546>
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,89,230                        ; mulps         %xmm6,%xmm12
+  DB  69,15,88,224                        ; addps         %xmm8,%xmm12
+  DB  102,69,15,56,20,224                 ; blendvps      %xmm0,%xmm8,%xmm12
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,194,198,2                     ; cmpleps       %xmm14,%xmm0
+  DB  68,15,40,253                        ; movaps        %xmm5,%xmm15
+  DB  102,69,15,56,20,252                 ; blendvps      %xmm0,%xmm12,%xmm15
+  DB  68,15,40,37,1,56,0,0                ; movaps        0x3801(%rip),%xmm12        # 55f0 <_sk_callback_sse41+0x556>
+  DB  65,15,40,196                        ; movaps        %xmm12,%xmm0
+  DB  65,15,194,198,2                     ; cmpleps       %xmm14,%xmm0
+  DB  68,15,89,238                        ; mulps         %xmm6,%xmm13
+  DB  69,15,88,232                        ; addps         %xmm8,%xmm13
+  DB  102,69,15,56,20,239                 ; blendvps      %xmm0,%xmm15,%xmm13
+  DB  69,15,87,246                        ; xorps         %xmm14,%xmm14
+  DB  68,15,194,241,0                     ; cmpeqps       %xmm1,%xmm14
+  DB  65,15,40,198                        ; movaps        %xmm14,%xmm0
+  DB  102,68,15,56,20,234                 ; blendvps      %xmm0,%xmm2,%xmm13
+  DB  102,65,15,58,8,194,1                ; roundps       $0x1,%xmm10,%xmm0
+  DB  69,15,40,250                        ; movaps        %xmm10,%xmm15
+  DB  68,15,92,248                        ; subps         %xmm0,%xmm15
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,194,199,2                     ; cmpleps       %xmm15,%xmm0
+  DB  65,15,40,207                        ; movaps        %xmm15,%xmm1
+  DB  15,89,207                           ; mulps         %xmm7,%xmm1
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,92,209                           ; subps         %xmm1,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  65,15,88,208                        ; addps         %xmm8,%xmm2
+  DB  102,65,15,56,20,208                 ; blendvps      %xmm0,%xmm8,%xmm2
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,194,199,2                     ; cmpleps       %xmm15,%xmm0
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  102,15,56,20,226                    ; blendvps      %xmm0,%xmm2,%xmm4
+  DB  65,15,40,196                        ; movaps        %xmm12,%xmm0
+  DB  65,15,194,199,2                     ; cmpleps       %xmm15,%xmm0
+  DB  15,89,206                           ; mulps         %xmm6,%xmm1
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  102,15,56,20,204                    ; blendvps      %xmm0,%xmm4,%xmm1
+  DB  65,15,40,198                        ; movaps        %xmm14,%xmm0
+  DB  15,40,20,36                         ; movaps        (%rsp),%xmm2
+  DB  102,15,56,20,202                    ; blendvps      %xmm0,%xmm2,%xmm1
+  DB  68,15,88,21,122,55,0,0              ; addps         0x377a(%rip),%xmm10        # 5600 <_sk_callback_sse41+0x566>
+  DB  102,65,15,58,8,194,1                ; roundps       $0x1,%xmm10,%xmm0
+  DB  68,15,92,208                        ; subps         %xmm0,%xmm10
+  DB  69,15,194,218,2                     ; cmpleps       %xmm10,%xmm11
+  DB  65,15,89,250                        ; mulps         %xmm10,%xmm7
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  15,89,222                           ; mulps         %xmm6,%xmm3
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  102,65,15,56,20,216                 ; blendvps      %xmm0,%xmm8,%xmm3
+  DB  69,15,194,202,2                     ; cmpleps       %xmm10,%xmm9
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,15,56,20,235                    ; blendvps      %xmm0,%xmm3,%xmm5
+  DB  69,15,194,226,2                     ; cmpleps       %xmm10,%xmm12
+  DB  15,89,254                           ; mulps         %xmm6,%xmm7
+  DB  68,15,88,199                        ; addps         %xmm7,%xmm8
+  DB  65,15,40,196                        ; movaps        %xmm12,%xmm0
+  DB  102,68,15,56,20,197                 ; blendvps      %xmm0,%xmm5,%xmm8
+  DB  65,15,40,198                        ; movaps        %xmm14,%xmm0
+  DB  102,68,15,56,20,194                 ; blendvps      %xmm0,%xmm2,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,197                        ; movaps        %xmm13,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,40,92,36,16                      ; movaps        0x10(%rsp),%xmm3
+  DB  15,40,100,36,32                     ; movaps        0x20(%rsp),%xmm4
+  DB  15,40,108,36,48                     ; movaps        0x30(%rsp),%xmm5
+  DB  15,40,116,36,64                     ; movaps        0x40(%rsp),%xmm6
+  DB  15,40,124,36,80                     ; movaps        0x50(%rsp),%xmm7
+  DB  72,131,196,104                      ; add           $0x68,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_1_float_sse41
+_sk_scale_1_float_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_u8_sse41
+_sk_scale_u8_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,52                              ; jne           1f63 <_sk_scale_u8_sse41+0x3e>
+  DB  102,69,15,56,49,4,18                ; pmovzxbd      (%r10,%rdx,1),%xmm8
+  DB  102,68,15,219,5,209,54,0,0          ; pand          0x36d1(%rip),%xmm8        # 5610 <_sk_callback_sse41+0x576>
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,5,213,54,0,0               ; mulps         0x36d5(%rip),%xmm8        # 5620 <_sk_callback_sse41+0x586>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,42                              ; je            1f9f <_sk_scale_u8_sse41+0x7a>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,23                              ; je            1f92 <_sk_scale_u8_sse41+0x6d>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,181                             ; jne           1f36 <_sk_scale_u8_sse41+0x11>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,69                ; pshufd        $0x45,%xmm8,%xmm8
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,68,15,58,34,192,1               ; pinsrd        $0x1,%eax,%xmm8
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,68,15,58,34,192,0               ; pinsrd        $0x0,%eax,%xmm8
+  DB  235,137                             ; jmp           1f36 <_sk_scale_u8_sse41+0x11>
+
+PUBLIC _sk_lerp_1_float_sse41
+_sk_lerp_1_float_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,92,205                           ; subps         %xmm5,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,92,214                           ; subps         %xmm6,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lerp_u8_sse41
+_sk_lerp_u8_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,72                              ; jne           2037 <_sk_lerp_u8_sse41+0x52>
+  DB  102,69,15,56,49,4,18                ; pmovzxbd      (%r10,%rdx,1),%xmm8
+  DB  102,68,15,219,5,49,54,0,0           ; pand          0x3631(%rip),%xmm8        # 5630 <_sk_callback_sse41+0x596>
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,5,53,54,0,0                ; mulps         0x3635(%rip),%xmm8        # 5640 <_sk_callback_sse41+0x5a6>
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,92,205                           ; subps         %xmm5,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,92,214                           ; subps         %xmm6,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,42                              ; je            2073 <_sk_lerp_u8_sse41+0x8e>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,23                              ; je            2066 <_sk_lerp_u8_sse41+0x81>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,161                             ; jne           1ff6 <_sk_lerp_u8_sse41+0x11>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,69                ; pshufd        $0x45,%xmm8,%xmm8
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,68,15,58,34,192,1               ; pinsrd        $0x1,%eax,%xmm8
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,68,15,58,34,192,0               ; pinsrd        $0x0,%eax,%xmm8
+  DB  233,114,255,255,255                 ; jmpq          1ff6 <_sk_lerp_u8_sse41+0x11>
+
+PUBLIC _sk_lerp_565_sse41
+_sk_lerp_565_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,152,0,0,0                    ; jne           212a <_sk_lerp_565_sse41+0xa6>
+  DB  102,69,15,56,51,12,82               ; pmovzxwd      (%r10,%rdx,2),%xmm9
+  DB  102,68,15,111,5,174,53,0,0          ; movdqa        0x35ae(%rip),%xmm8        # 5650 <_sk_callback_sse41+0x5b6>
+  DB  102,69,15,219,193                   ; pand          %xmm9,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,5,173,53,0,0               ; mulps         0x35ad(%rip),%xmm8        # 5660 <_sk_callback_sse41+0x5c6>
+  DB  102,68,15,111,21,180,53,0,0         ; movdqa        0x35b4(%rip),%xmm10        # 5670 <_sk_callback_sse41+0x5d6>
+  DB  102,69,15,219,209                   ; pand          %xmm9,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  68,15,89,21,179,53,0,0              ; mulps         0x35b3(%rip),%xmm10        # 5680 <_sk_callback_sse41+0x5e6>
+  DB  102,68,15,219,13,186,53,0,0         ; pand          0x35ba(%rip),%xmm9        # 5690 <_sk_callback_sse41+0x5f6>
+  DB  69,15,91,201                        ; cvtdq2ps      %xmm9,%xmm9
+  DB  68,15,89,13,190,53,0,0              ; mulps         0x35be(%rip),%xmm9        # 56a0 <_sk_callback_sse41+0x606>
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,92,205                           ; subps         %xmm5,%xmm1
+  DB  65,15,89,202                        ; mulps         %xmm10,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,92,214                           ; subps         %xmm6,%xmm2
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  68,15,88,199                        ; addps         %xmm7,%xmm8
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  68,15,88,215                        ; addps         %xmm7,%xmm10
+  DB  65,15,89,217                        ; mulps         %xmm9,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  68,15,95,211                        ; maxps         %xmm3,%xmm10
+  DB  69,15,95,194                        ; maxps         %xmm10,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,46                              ; je            216a <_sk_lerp_565_sse41+0xe6>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,27                              ; je            215d <_sk_lerp_565_sse41+0xd9>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  15,133,77,255,255,255               ; jne           2099 <_sk_lerp_565_sse41+0x15>
+  DB  65,15,183,68,82,4                   ; movzwl        0x4(%r10,%rdx,2),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,200,69                ; pshufd        $0x45,%xmm8,%xmm9
+  DB  65,15,183,68,82,2                   ; movzwl        0x2(%r10,%rdx,2),%eax
+  DB  102,68,15,58,34,200,1               ; pinsrd        $0x1,%eax,%xmm9
+  DB  65,15,183,4,82                      ; movzwl        (%r10,%rdx,2),%eax
+  DB  102,68,15,58,34,200,0               ; pinsrd        $0x0,%eax,%xmm9
+  DB  233,30,255,255,255                  ; jmpq          2099 <_sk_lerp_565_sse41+0x15>
+
+PUBLIC _sk_load_tables_sse41
+_sk_load_tables_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,24,1,0,0                     ; jne           22a1 <_sk_load_tables_sse41+0x126>
+  DB  243,69,15,111,4,145                 ; movdqu        (%r9,%rdx,4),%xmm8
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  102,15,111,5,20,53,0,0              ; movdqa        0x3514(%rip),%xmm0        # 56b0 <_sk_callback_sse41+0x616>
+  DB  102,65,15,219,192                   ; pand          %xmm8,%xmm0
+  DB  102,73,15,58,22,193,1               ; pextrq        $0x1,%xmm0,%r9
+  DB  102,73,15,126,194                   ; movq          %xmm0,%r10
+  DB  69,15,182,218                       ; movzbl        %r10b,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  69,15,182,241                       ; movzbl        %r9b,%r14d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,66,15,16,4,155                  ; movss         (%rbx,%r11,4),%xmm0
+  DB  102,66,15,58,33,4,19,16             ; insertps      $0x10,(%rbx,%r10,1),%xmm0
+  DB  102,66,15,58,33,4,179,32            ; insertps      $0x20,(%rbx,%r14,4),%xmm0
+  DB  102,66,15,58,33,4,11,48             ; insertps      $0x30,(%rbx,%r9,1),%xmm0
+  DB  102,65,15,111,200                   ; movdqa        %xmm8,%xmm1
+  DB  102,15,56,0,13,207,52,0,0           ; pshufb        0x34cf(%rip),%xmm1        # 56c0 <_sk_callback_sse41+0x626>
+  DB  102,73,15,58,22,201,1               ; pextrq        $0x1,%xmm1,%r9
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  68,15,182,211                       ; movzbl        %bl,%r10d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  69,15,182,217                       ; movzbl        %r9b,%r11d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  243,67,15,16,12,151                 ; movss         (%r15,%r10,4),%xmm1
+  DB  102,65,15,58,33,12,31,16            ; insertps      $0x10,(%r15,%rbx,1),%xmm1
+  DB  243,67,15,16,20,159                 ; movss         (%r15,%r11,4),%xmm2
+  DB  102,15,58,33,202,32                 ; insertps      $0x20,%xmm2,%xmm1
+  DB  243,67,15,16,20,15                  ; movss         (%r15,%r9,1),%xmm2
+  DB  102,15,58,33,202,48                 ; insertps      $0x30,%xmm2,%xmm1
+  DB  76,139,72,24                        ; mov           0x18(%rax),%r9
+  DB  102,65,15,111,208                   ; movdqa        %xmm8,%xmm2
+  DB  102,15,56,0,21,139,52,0,0           ; pshufb        0x348b(%rip),%xmm2        # 56d0 <_sk_callback_sse41+0x636>
+  DB  102,72,15,58,22,211,1               ; pextrq        $0x1,%xmm2,%rbx
+  DB  102,72,15,126,208                   ; movq          %xmm2,%rax
+  DB  68,15,182,208                       ; movzbl        %al,%r10d
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  68,15,182,219                       ; movzbl        %bl,%r11d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  243,67,15,16,20,145                 ; movss         (%r9,%r10,4),%xmm2
+  DB  102,65,15,58,33,20,1,16             ; insertps      $0x10,(%r9,%rax,1),%xmm2
+  DB  243,67,15,16,28,153                 ; movss         (%r9,%r11,4),%xmm3
+  DB  102,15,58,33,211,32                 ; insertps      $0x20,%xmm3,%xmm2
+  DB  243,65,15,16,28,25                  ; movss         (%r9,%rbx,1),%xmm3
+  DB  102,15,58,33,211,48                 ; insertps      $0x30,%xmm3,%xmm2
+  DB  102,65,15,114,208,24                ; psrld         $0x18,%xmm8
+  DB  65,15,91,216                        ; cvtdq2ps      %xmm8,%xmm3
+  DB  15,89,29,72,52,0,0                  ; mulps         0x3448(%rip),%xmm3        # 56e0 <_sk_callback_sse41+0x646>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,194                          ; mov           %r8d,%r10d
+  DB  65,128,226,3                        ; and           $0x3,%r10b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,128,250,1                        ; cmp           $0x1,%r10b
+  DB  116,38                              ; je            22d9 <_sk_load_tables_sse41+0x15e>
+  DB  65,128,250,2                        ; cmp           $0x2,%r10b
+  DB  116,23                              ; je            22d0 <_sk_load_tables_sse41+0x155>
+  DB  65,128,250,3                        ; cmp           $0x3,%r10b
+  DB  15,133,204,254,255,255              ; jne           218f <_sk_load_tables_sse41+0x14>
+  DB  102,65,15,110,68,145,8              ; movd          0x8(%r9,%rdx,4),%xmm0
+  DB  102,68,15,112,192,69                ; pshufd        $0x45,%xmm0,%xmm8
+  DB  102,69,15,58,34,68,145,4,1          ; pinsrd        $0x1,0x4(%r9,%rdx,4),%xmm8
+  DB  102,69,15,58,34,4,145,0             ; pinsrd        $0x0,(%r9,%rdx,4),%xmm8
+  DB  233,169,254,255,255                 ; jmpq          218f <_sk_load_tables_sse41+0x14>
+
+PUBLIC _sk_load_tables_u16_be_sse41
+_sk_load_tables_u16_be_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,99,1,0,0                     ; jne           245f <_sk_load_tables_u16_be_sse41+0x179>
+  DB  102,67,15,16,4,81                   ; movupd        (%r9,%r10,2),%xmm0
+  DB  243,67,15,111,76,81,16              ; movdqu        0x10(%r9,%r10,2),%xmm1
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  102,68,15,40,200                    ; movapd        %xmm0,%xmm9
+  DB  102,68,15,97,201                    ; punpcklwd     %xmm1,%xmm9
+  DB  102,15,105,193                      ; punpckhwd     %xmm1,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,15,97,200                       ; punpcklwd     %xmm0,%xmm1
+  DB  102,68,15,105,200                   ; punpckhwd     %xmm0,%xmm9
+  DB  102,68,15,111,5,189,51,0,0          ; movdqa        0x33bd(%rip),%xmm8        # 56f0 <_sk_callback_sse41+0x656>
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,65,15,219,192                   ; pand          %xmm8,%xmm0
+  DB  102,15,56,51,192                    ; pmovzxwd      %xmm0,%xmm0
+  DB  102,73,15,58,22,193,1               ; pextrq        $0x1,%xmm0,%r9
+  DB  102,73,15,126,194                   ; movq          %xmm0,%r10
+  DB  69,15,182,218                       ; movzbl        %r10b,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  69,15,182,241                       ; movzbl        %r9b,%r14d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,66,15,16,4,155                  ; movss         (%rbx,%r11,4),%xmm0
+  DB  102,66,15,58,33,4,19,16             ; insertps      $0x10,(%rbx,%r10,1),%xmm0
+  DB  243,66,15,16,20,179                 ; movss         (%rbx,%r14,4),%xmm2
+  DB  102,15,58,33,194,32                 ; insertps      $0x20,%xmm2,%xmm0
+  DB  243,66,15,16,20,11                  ; movss         (%rbx,%r9,1),%xmm2
+  DB  102,15,58,33,194,48                 ; insertps      $0x30,%xmm2,%xmm0
+  DB  102,15,56,0,13,108,51,0,0           ; pshufb        0x336c(%rip),%xmm1        # 5700 <_sk_callback_sse41+0x666>
+  DB  102,15,56,51,201                    ; pmovzxwd      %xmm1,%xmm1
+  DB  102,73,15,58,22,201,1               ; pextrq        $0x1,%xmm1,%r9
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  68,15,182,211                       ; movzbl        %bl,%r10d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  69,15,182,217                       ; movzbl        %r9b,%r11d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  243,67,15,16,12,151                 ; movss         (%r15,%r10,4),%xmm1
+  DB  102,65,15,58,33,12,31,16            ; insertps      $0x10,(%r15,%rbx,1),%xmm1
+  DB  243,67,15,16,20,159                 ; movss         (%r15,%r11,4),%xmm2
+  DB  102,15,58,33,202,32                 ; insertps      $0x20,%xmm2,%xmm1
+  DB  243,67,15,16,20,15                  ; movss         (%r15,%r9,1),%xmm2
+  DB  102,15,58,33,202,48                 ; insertps      $0x30,%xmm2,%xmm1
+  DB  76,139,72,24                        ; mov           0x18(%rax),%r9
+  DB  102,69,15,219,193                   ; pand          %xmm9,%xmm8
+  DB  102,65,15,56,51,208                 ; pmovzxwd      %xmm8,%xmm2
+  DB  102,72,15,58,22,211,1               ; pextrq        $0x1,%xmm2,%rbx
+  DB  102,72,15,126,208                   ; movq          %xmm2,%rax
+  DB  68,15,182,208                       ; movzbl        %al,%r10d
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  68,15,182,219                       ; movzbl        %bl,%r11d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  243,67,15,16,20,145                 ; movss         (%r9,%r10,4),%xmm2
+  DB  102,65,15,58,33,20,1,16             ; insertps      $0x10,(%r9,%rax,1),%xmm2
+  DB  243,67,15,16,28,153                 ; movss         (%r9,%r11,4),%xmm3
+  DB  102,15,58,33,211,32                 ; insertps      $0x20,%xmm3,%xmm2
+  DB  243,65,15,16,28,25                  ; movss         (%r9,%rbx,1),%xmm3
+  DB  102,15,58,33,211,48                 ; insertps      $0x30,%xmm3,%xmm2
+  DB  102,65,15,112,217,78                ; pshufd        $0x4e,%xmm9,%xmm3
+  DB  102,68,15,111,195                   ; movdqa        %xmm3,%xmm8
+  DB  102,65,15,113,240,8                 ; psllw         $0x8,%xmm8
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,65,15,235,216                   ; por           %xmm8,%xmm3
+  DB  102,15,56,51,219                    ; pmovzxwd      %xmm3,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  15,89,29,186,50,0,0                 ; mulps         0x32ba(%rip),%xmm3        # 5710 <_sk_callback_sse41+0x676>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+  DB  242,67,15,16,4,81                   ; movsd         (%r9,%r10,2),%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,13                              ; jne           2478 <_sk_load_tables_u16_be_sse41+0x192>
+  DB  243,15,126,192                      ; movq          %xmm0,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,145,254,255,255                 ; jmpq          2309 <_sk_load_tables_u16_be_sse41+0x23>
+  DB  102,67,15,22,68,81,8                ; movhpd        0x8(%r9,%r10,2),%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  15,130,124,254,255,255              ; jb            2309 <_sk_load_tables_u16_be_sse41+0x23>
+  DB  243,67,15,126,76,81,16              ; movq          0x10(%r9,%r10,2),%xmm1
+  DB  233,112,254,255,255                 ; jmpq          2309 <_sk_load_tables_u16_be_sse41+0x23>
+
+PUBLIC _sk_load_tables_rgb_u16_be_sse41
+_sk_load_tables_rgb_u16_be_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,82                        ; lea           (%rdx,%rdx,2),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,83,1,0,0                     ; jne           25fe <_sk_load_tables_rgb_u16_be_sse41+0x165>
+  DB  243,67,15,111,20,81                 ; movdqu        (%r9,%r10,2),%xmm2
+  DB  243,67,15,111,76,81,8               ; movdqu        0x8(%r9,%r10,2),%xmm1
+  DB  102,15,115,217,4                    ; psrldq        $0x4,%xmm1
+  DB  102,68,15,111,202                   ; movdqa        %xmm2,%xmm9
+  DB  102,65,15,115,217,6                 ; psrldq        $0x6,%xmm9
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,115,216,6                    ; psrldq        $0x6,%xmm0
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  102,15,97,209                       ; punpcklwd     %xmm1,%xmm2
+  DB  102,68,15,97,200                    ; punpcklwd     %xmm0,%xmm9
+  DB  102,15,111,202                      ; movdqa        %xmm2,%xmm1
+  DB  102,65,15,97,201                    ; punpcklwd     %xmm9,%xmm1
+  DB  102,68,15,111,5,47,50,0,0           ; movdqa        0x322f(%rip),%xmm8        # 5720 <_sk_callback_sse41+0x686>
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,65,15,219,192                   ; pand          %xmm8,%xmm0
+  DB  102,15,56,51,192                    ; pmovzxwd      %xmm0,%xmm0
+  DB  102,73,15,58,22,193,1               ; pextrq        $0x1,%xmm0,%r9
+  DB  102,73,15,126,194                   ; movq          %xmm0,%r10
+  DB  69,15,182,218                       ; movzbl        %r10b,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  69,15,182,241                       ; movzbl        %r9b,%r14d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,66,15,16,4,155                  ; movss         (%rbx,%r11,4),%xmm0
+  DB  102,66,15,58,33,4,19,16             ; insertps      $0x10,(%rbx,%r10,1),%xmm0
+  DB  243,66,15,16,28,179                 ; movss         (%rbx,%r14,4),%xmm3
+  DB  102,15,58,33,195,32                 ; insertps      $0x20,%xmm3,%xmm0
+  DB  243,66,15,16,28,11                  ; movss         (%rbx,%r9,1),%xmm3
+  DB  102,15,58,33,195,48                 ; insertps      $0x30,%xmm3,%xmm0
+  DB  102,15,56,0,13,222,49,0,0           ; pshufb        0x31de(%rip),%xmm1        # 5730 <_sk_callback_sse41+0x696>
+  DB  102,15,56,51,201                    ; pmovzxwd      %xmm1,%xmm1
+  DB  102,73,15,58,22,201,1               ; pextrq        $0x1,%xmm1,%r9
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  68,15,182,211                       ; movzbl        %bl,%r10d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  69,15,182,217                       ; movzbl        %r9b,%r11d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  243,67,15,16,12,151                 ; movss         (%r15,%r10,4),%xmm1
+  DB  102,65,15,58,33,12,31,16            ; insertps      $0x10,(%r15,%rbx,1),%xmm1
+  DB  243,67,15,16,28,159                 ; movss         (%r15,%r11,4),%xmm3
+  DB  102,15,58,33,203,32                 ; insertps      $0x20,%xmm3,%xmm1
+  DB  243,67,15,16,28,15                  ; movss         (%r15,%r9,1),%xmm3
+  DB  102,15,58,33,203,48                 ; insertps      $0x30,%xmm3,%xmm1
+  DB  76,139,72,24                        ; mov           0x18(%rax),%r9
+  DB  102,65,15,105,209                   ; punpckhwd     %xmm9,%xmm2
+  DB  102,65,15,219,208                   ; pand          %xmm8,%xmm2
+  DB  102,15,56,51,210                    ; pmovzxwd      %xmm2,%xmm2
+  DB  102,72,15,58,22,211,1               ; pextrq        $0x1,%xmm2,%rbx
+  DB  102,72,15,126,208                   ; movq          %xmm2,%rax
+  DB  68,15,182,208                       ; movzbl        %al,%r10d
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  68,15,182,219                       ; movzbl        %bl,%r11d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  243,67,15,16,20,145                 ; movss         (%r9,%r10,4),%xmm2
+  DB  102,65,15,58,33,20,1,16             ; insertps      $0x10,(%r9,%rax,1),%xmm2
+  DB  243,67,15,16,28,153                 ; movss         (%r9,%r11,4),%xmm3
+  DB  102,15,58,33,211,32                 ; insertps      $0x20,%xmm3,%xmm2
+  DB  243,65,15,16,28,25                  ; movss         (%r9,%rbx,1),%xmm3
+  DB  102,15,58,33,211,48                 ; insertps      $0x30,%xmm3,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,73,49,0,0                  ; movaps        0x3149(%rip),%xmm3        # 5740 <_sk_callback_sse41+0x6a6>
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,67,15,110,20,81                 ; movd          (%r9,%r10,2),%xmm2
+  DB  102,67,15,196,84,81,4,2             ; pinsrw        $0x2,0x4(%r9,%r10,2),%xmm2
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,14                              ; jne           2624 <_sk_load_tables_rgb_u16_be_sse41+0x18b>
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  233,173,254,255,255                 ; jmpq          24d1 <_sk_load_tables_rgb_u16_be_sse41+0x38>
+  DB  102,71,15,110,76,81,6               ; movd          0x6(%r9,%r10,2),%xmm9
+  DB  102,71,15,196,76,81,10,2            ; pinsrw        $0x2,0xa(%r9,%r10,2),%xmm9
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,24                              ; jb            2655 <_sk_load_tables_rgb_u16_be_sse41+0x1bc>
+  DB  102,67,15,110,76,81,12              ; movd          0xc(%r9,%r10,2),%xmm1
+  DB  102,67,15,196,76,81,16,2            ; pinsrw        $0x2,0x10(%r9,%r10,2),%xmm1
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  233,124,254,255,255                 ; jmpq          24d1 <_sk_load_tables_rgb_u16_be_sse41+0x38>
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,115,254,255,255                 ; jmpq          24d1 <_sk_load_tables_rgb_u16_be_sse41+0x38>
+
+PUBLIC _sk_byte_tables_sse41
+_sk_byte_tables_sse41 LABEL PROC
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,225,48,0,0               ; movaps        0x30e1(%rip),%xmm8        # 5750 <_sk_callback_sse41+0x6b6>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,91,192                       ; cvtps2dq      %xmm0,%xmm0
+  DB  102,73,15,58,22,193,1               ; pextrq        $0x1,%xmm0,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  69,137,222                          ; mov           %r11d,%r14d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  76,139,32                           ; mov           (%rax),%r12
+  DB  76,139,120,8                        ; mov           0x8(%rax),%r15
+  DB  102,67,15,58,32,4,52,0              ; pinsrb        $0x0,(%r12,%r14,1),%xmm0
+  DB  102,67,15,58,32,4,28,1              ; pinsrb        $0x1,(%r12,%r11,1),%xmm0
+  DB  67,15,182,28,20                     ; movzbl        (%r12,%r10,1),%ebx
+  DB  102,15,58,32,195,2                  ; pinsrb        $0x2,%ebx,%xmm0
+  DB  67,15,182,28,12                     ; movzbl        (%r12,%r9,1),%ebx
+  DB  102,15,58,32,195,3                  ; pinsrb        $0x3,%ebx,%xmm0
+  DB  102,15,56,49,192                    ; pmovzxbd      %xmm0,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,13,146,48,0,0              ; movaps        0x3092(%rip),%xmm9        # 5760 <_sk_callback_sse41+0x6c6>
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,91,201                       ; cvtps2dq      %xmm1,%xmm1
+  DB  102,72,15,58,22,203,1               ; pextrq        $0x1,%xmm1,%rbx
+  DB  65,137,217                          ; mov           %ebx,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,73,15,126,202                   ; movq          %xmm1,%r10
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  73,193,234,32                       ; shr           $0x20,%r10
+  DB  102,67,15,58,32,12,31,0             ; pinsrb        $0x0,(%r15,%r11,1),%xmm1
+  DB  102,67,15,58,32,12,23,1             ; pinsrb        $0x1,(%r15,%r10,1),%xmm1
+  DB  71,15,182,12,15                     ; movzbl        (%r15,%r9,1),%r9d
+  DB  102,65,15,58,32,201,2               ; pinsrb        $0x2,%r9d,%xmm1
+  DB  65,15,182,28,31                     ; movzbl        (%r15,%rbx,1),%ebx
+  DB  102,15,58,32,203,3                  ; pinsrb        $0x3,%ebx,%xmm1
+  DB  102,15,56,49,201                    ; pmovzxbd      %xmm1,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  76,139,88,16                        ; mov           0x10(%rax),%r11
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,91,210                       ; cvtps2dq      %xmm2,%xmm2
+  DB  102,73,15,58,22,209,1               ; pextrq        $0x1,%xmm2,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  65,137,222                          ; mov           %ebx,%r14d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,67,15,58,32,20,51,0             ; pinsrb        $0x0,(%r11,%r14,1),%xmm2
+  DB  102,65,15,58,32,20,27,1             ; pinsrb        $0x1,(%r11,%rbx,1),%xmm2
+  DB  67,15,182,28,19                     ; movzbl        (%r11,%r10,1),%ebx
+  DB  102,15,58,32,211,2                  ; pinsrb        $0x2,%ebx,%xmm2
+  DB  67,15,182,28,11                     ; movzbl        (%r11,%r9,1),%ebx
+  DB  102,15,58,32,211,3                  ; pinsrb        $0x3,%ebx,%xmm2
+  DB  102,15,56,49,210                    ; pmovzxbd      %xmm2,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  102,15,91,219                       ; cvtps2dq      %xmm3,%xmm3
+  DB  102,72,15,58,22,219,1               ; pextrq        $0x1,%xmm3,%rbx
+  DB  65,137,217                          ; mov           %ebx,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,72,15,126,216                   ; movq          %xmm3,%rax
+  DB  65,137,195                          ; mov           %eax,%r11d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,67,15,58,32,28,26,0             ; pinsrb        $0x0,(%r10,%r11,1),%xmm3
+  DB  102,65,15,58,32,28,2,1              ; pinsrb        $0x1,(%r10,%rax,1),%xmm3
+  DB  67,15,182,4,10                      ; movzbl        (%r10,%r9,1),%eax
+  DB  102,15,58,32,216,2                  ; pinsrb        $0x2,%eax,%xmm3
+  DB  65,15,182,4,26                      ; movzbl        (%r10,%rbx,1),%eax
+  DB  102,15,58,32,216,3                  ; pinsrb        $0x3,%eax,%xmm3
+  DB  102,15,56,49,219                    ; pmovzxbd      %xmm3,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,217                        ; mulps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_byte_tables_rgb_sse41
+_sk_byte_tables_rgb_sse41 LABEL PROC
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,139,72,24                        ; mov           0x18(%rax),%r9d
+  DB  65,255,201                          ; dec           %r9d
+  DB  102,69,15,110,193                   ; movd          %r9d,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,91,192                       ; cvtps2dq      %xmm0,%xmm0
+  DB  102,73,15,58,22,193,1               ; pextrq        $0x1,%xmm0,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  77,137,203                          ; mov           %r9,%r11
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,73,15,126,193                   ; movq          %xmm0,%r9
+  DB  69,137,206                          ; mov           %r9d,%r14d
+  DB  77,137,207                          ; mov           %r9,%r15
+  DB  73,193,239,32                       ; shr           $0x20,%r15
+  DB  76,139,32                           ; mov           (%rax),%r12
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  102,67,15,58,32,4,52,0              ; pinsrb        $0x0,(%r12,%r14,1),%xmm0
+  DB  102,67,15,58,32,4,60,1              ; pinsrb        $0x1,(%r12,%r15,1),%xmm0
+  DB  67,15,182,28,20                     ; movzbl        (%r12,%r10,1),%ebx
+  DB  102,15,58,32,195,2                  ; pinsrb        $0x2,%ebx,%xmm0
+  DB  67,15,182,28,28                     ; movzbl        (%r12,%r11,1),%ebx
+  DB  102,15,58,32,195,3                  ; pinsrb        $0x3,%ebx,%xmm0
+  DB  102,15,56,49,192                    ; pmovzxbd      %xmm0,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,13,10,47,0,0               ; movaps        0x2f0a(%rip),%xmm9        # 5770 <_sk_callback_sse41+0x6d6>
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,91,201                       ; cvtps2dq      %xmm1,%xmm1
+  DB  102,72,15,58,22,203,1               ; pextrq        $0x1,%xmm1,%rbx
+  DB  65,137,218                          ; mov           %ebx,%r10d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,73,15,126,203                   ; movq          %xmm1,%r11
+  DB  69,137,222                          ; mov           %r11d,%r14d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,67,15,58,32,12,49,0             ; pinsrb        $0x0,(%r9,%r14,1),%xmm1
+  DB  102,67,15,58,32,12,25,1             ; pinsrb        $0x1,(%r9,%r11,1),%xmm1
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  102,65,15,58,32,202,2               ; pinsrb        $0x2,%r10d,%xmm1
+  DB  65,15,182,28,25                     ; movzbl        (%r9,%rbx,1),%ebx
+  DB  102,15,58,32,203,3                  ; pinsrb        $0x3,%ebx,%xmm1
+  DB  102,15,56,49,201                    ; pmovzxbd      %xmm1,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,91,210                       ; cvtps2dq      %xmm2,%xmm2
+  DB  102,72,15,58,22,211,1               ; pextrq        $0x1,%xmm2,%rbx
+  DB  65,137,217                          ; mov           %ebx,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,72,15,126,208                   ; movq          %xmm2,%rax
+  DB  65,137,195                          ; mov           %eax,%r11d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,67,15,58,32,20,26,0             ; pinsrb        $0x0,(%r10,%r11,1),%xmm2
+  DB  102,65,15,58,32,20,2,1              ; pinsrb        $0x1,(%r10,%rax,1),%xmm2
+  DB  67,15,182,4,10                      ; movzbl        (%r10,%r9,1),%eax
+  DB  102,15,58,32,208,2                  ; pinsrb        $0x2,%eax,%xmm2
+  DB  65,15,182,4,26                      ; movzbl        (%r10,%rbx,1),%eax
+  DB  102,15,58,32,208,3                  ; pinsrb        $0x3,%eax,%xmm2
+  DB  102,15,56,49,210                    ; pmovzxbd      %xmm2,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_r_sse41
+_sk_table_r_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  102,65,15,91,192                    ; cvtps2dq      %xmm8,%xmm0
+  DB  102,72,15,58,22,192,1               ; pextrq        $0x1,%xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,65,15,16,4,153                  ; movss         (%r9,%rbx,4),%xmm0
+  DB  102,67,15,58,33,4,153,16            ; insertps      $0x10,(%r9,%r11,4),%xmm0
+  DB  243,71,15,16,4,145                  ; movss         (%r9,%r10,4),%xmm8
+  DB  102,65,15,58,33,192,32              ; insertps      $0x20,%xmm8,%xmm0
+  DB  243,69,15,16,4,129                  ; movss         (%r9,%rax,4),%xmm8
+  DB  102,65,15,58,33,192,48              ; insertps      $0x30,%xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_g_sse41
+_sk_table_g_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,193                        ; mulps         %xmm1,%xmm8
+  DB  102,65,15,91,200                    ; cvtps2dq      %xmm8,%xmm1
+  DB  102,72,15,58,22,200,1               ; pextrq        $0x1,%xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,203                   ; movq          %xmm1,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,65,15,16,12,153                 ; movss         (%r9,%rbx,4),%xmm1
+  DB  102,67,15,58,33,12,153,16           ; insertps      $0x10,(%r9,%r11,4),%xmm1
+  DB  243,71,15,16,4,145                  ; movss         (%r9,%r10,4),%xmm8
+  DB  102,65,15,58,33,200,32              ; insertps      $0x20,%xmm8,%xmm1
+  DB  243,69,15,16,4,129                  ; movss         (%r9,%rax,4),%xmm8
+  DB  102,65,15,58,33,200,48              ; insertps      $0x30,%xmm8,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_b_sse41
+_sk_table_b_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  102,65,15,91,208                    ; cvtps2dq      %xmm8,%xmm2
+  DB  102,72,15,58,22,208,1               ; pextrq        $0x1,%xmm2,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,211                   ; movq          %xmm2,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,65,15,16,20,153                 ; movss         (%r9,%rbx,4),%xmm2
+  DB  102,67,15,58,33,20,153,16           ; insertps      $0x10,(%r9,%r11,4),%xmm2
+  DB  243,71,15,16,4,145                  ; movss         (%r9,%r10,4),%xmm8
+  DB  102,65,15,58,33,208,32              ; insertps      $0x20,%xmm8,%xmm2
+  DB  243,69,15,16,4,129                  ; movss         (%r9,%rax,4),%xmm8
+  DB  102,65,15,58,33,208,48              ; insertps      $0x30,%xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_a_sse41
+_sk_table_a_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,65,15,91,216                    ; cvtps2dq      %xmm8,%xmm3
+  DB  102,72,15,58,22,216,1               ; pextrq        $0x1,%xmm3,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,219                   ; movq          %xmm3,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,65,15,16,28,153                 ; movss         (%r9,%rbx,4),%xmm3
+  DB  102,67,15,58,33,28,153,16           ; insertps      $0x10,(%r9,%r11,4),%xmm3
+  DB  243,71,15,16,4,145                  ; movss         (%r9,%r10,4),%xmm8
+  DB  102,65,15,58,33,216,32              ; insertps      $0x20,%xmm8,%xmm3
+  DB  243,69,15,16,4,129                  ; movss         (%r9,%rax,4),%xmm8
+  DB  102,65,15,58,33,216,48              ; insertps      $0x30,%xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_r_sse41
+_sk_parametric_r_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,64,16                  ; movss         0x10(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  243,68,15,16,72,12                  ; movss         0xc(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  68,15,89,200                        ; mulps         %xmm0,%xmm9
+  DB  243,68,15,16,80,4                   ; movss         0x4(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,208                        ; mulps         %xmm0,%xmm10
+  DB  65,15,194,192,2                     ; cmpleps       %xmm8,%xmm0
+  DB  243,68,15,16,64,24                  ; movss         0x18(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  69,15,88,200                        ; addps         %xmm8,%xmm9
+  DB  243,68,15,16,24                     ; movss         (%rax),%xmm11
+  DB  243,68,15,16,64,8                   ; movss         0x8(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  69,15,88,208                        ; addps         %xmm8,%xmm10
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,91,194                        ; cvtdq2ps      %xmm10,%xmm8
+  DB  68,15,89,5,82,44,0,0                ; mulps         0x2c52(%rip),%xmm8        # 5780 <_sk_callback_sse41+0x6e6>
+  DB  68,15,84,21,90,44,0,0               ; andps         0x2c5a(%rip),%xmm10        # 5790 <_sk_callback_sse41+0x6f6>
+  DB  68,15,86,21,98,44,0,0               ; orps          0x2c62(%rip),%xmm10        # 57a0 <_sk_callback_sse41+0x706>
+  DB  68,15,88,5,106,44,0,0               ; addps         0x2c6a(%rip),%xmm8        # 57b0 <_sk_callback_sse41+0x716>
+  DB  68,15,40,37,114,44,0,0              ; movaps        0x2c72(%rip),%xmm12        # 57c0 <_sk_callback_sse41+0x726>
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  69,15,92,196                        ; subps         %xmm12,%xmm8
+  DB  68,15,88,21,114,44,0,0              ; addps         0x2c72(%rip),%xmm10        # 57d0 <_sk_callback_sse41+0x736>
+  DB  68,15,40,37,122,44,0,0              ; movaps        0x2c7a(%rip),%xmm12        # 57e0 <_sk_callback_sse41+0x746>
+  DB  69,15,94,226                        ; divps         %xmm10,%xmm12
+  DB  69,15,92,196                        ; subps         %xmm12,%xmm8
+  DB  69,15,89,195                        ; mulps         %xmm11,%xmm8
+  DB  102,69,15,58,8,208,1                ; roundps       $0x1,%xmm8,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  68,15,88,5,103,44,0,0               ; addps         0x2c67(%rip),%xmm8        # 57f0 <_sk_callback_sse41+0x756>
+  DB  68,15,40,21,111,44,0,0              ; movaps        0x2c6f(%rip),%xmm10        # 5800 <_sk_callback_sse41+0x766>
+  DB  69,15,89,211                        ; mulps         %xmm11,%xmm10
+  DB  69,15,92,194                        ; subps         %xmm10,%xmm8
+  DB  68,15,40,21,111,44,0,0              ; movaps        0x2c6f(%rip),%xmm10        # 5810 <_sk_callback_sse41+0x776>
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  68,15,40,29,115,44,0,0              ; movaps        0x2c73(%rip),%xmm11        # 5820 <_sk_callback_sse41+0x786>
+  DB  69,15,94,218                        ; divps         %xmm10,%xmm11
+  DB  69,15,88,216                        ; addps         %xmm8,%xmm11
+  DB  68,15,89,29,115,44,0,0              ; mulps         0x2c73(%rip),%xmm11        # 5830 <_sk_callback_sse41+0x796>
+  DB  102,69,15,91,211                    ; cvtps2dq      %xmm11,%xmm10
+  DB  243,68,15,16,64,20                  ; movss         0x14(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  69,15,88,194                        ; addps         %xmm10,%xmm8
+  DB  102,69,15,56,20,193                 ; blendvps      %xmm0,%xmm9,%xmm8
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  68,15,93,5,90,44,0,0                ; minps         0x2c5a(%rip),%xmm8        # 5840 <_sk_callback_sse41+0x7a6>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_g_sse41
+_sk_parametric_g_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,72,12                  ; movss         0xc(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  243,68,15,16,88,4                   ; movss         0x4(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  68,15,89,217                        ; mulps         %xmm1,%xmm11
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,194,194,2                     ; cmpleps       %xmm10,%xmm0
+  DB  243,15,16,72,24                     ; movss         0x18(%rax),%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  68,15,88,201                        ; addps         %xmm1,%xmm9
+  DB  243,68,15,16,16                     ; movss         (%rax),%xmm10
+  DB  243,15,16,72,8                      ; movss         0x8(%rax),%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  68,15,88,217                        ; addps         %xmm1,%xmm11
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  69,15,91,227                        ; cvtdq2ps      %xmm11,%xmm12
+  DB  68,15,89,37,251,43,0,0              ; mulps         0x2bfb(%rip),%xmm12        # 5850 <_sk_callback_sse41+0x7b6>
+  DB  68,15,84,29,3,44,0,0                ; andps         0x2c03(%rip),%xmm11        # 5860 <_sk_callback_sse41+0x7c6>
+  DB  68,15,86,29,11,44,0,0               ; orps          0x2c0b(%rip),%xmm11        # 5870 <_sk_callback_sse41+0x7d6>
+  DB  68,15,88,37,19,44,0,0               ; addps         0x2c13(%rip),%xmm12        # 5880 <_sk_callback_sse41+0x7e6>
+  DB  15,40,13,28,44,0,0                  ; movaps        0x2c1c(%rip),%xmm1        # 5890 <_sk_callback_sse41+0x7f6>
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  68,15,92,225                        ; subps         %xmm1,%xmm12
+  DB  68,15,88,29,28,44,0,0               ; addps         0x2c1c(%rip),%xmm11        # 58a0 <_sk_callback_sse41+0x806>
+  DB  15,40,13,37,44,0,0                  ; movaps        0x2c25(%rip),%xmm1        # 58b0 <_sk_callback_sse41+0x816>
+  DB  65,15,94,203                        ; divps         %xmm11,%xmm1
+  DB  68,15,92,225                        ; subps         %xmm1,%xmm12
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  102,69,15,58,8,212,1                ; roundps       $0x1,%xmm12,%xmm10
+  DB  69,15,40,220                        ; movaps        %xmm12,%xmm11
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  68,15,88,37,18,44,0,0               ; addps         0x2c12(%rip),%xmm12        # 58c0 <_sk_callback_sse41+0x826>
+  DB  15,40,13,27,44,0,0                  ; movaps        0x2c1b(%rip),%xmm1        # 58d0 <_sk_callback_sse41+0x836>
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  68,15,92,225                        ; subps         %xmm1,%xmm12
+  DB  68,15,40,21,27,44,0,0               ; movaps        0x2c1b(%rip),%xmm10        # 58e0 <_sk_callback_sse41+0x846>
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  15,40,13,32,44,0,0                  ; movaps        0x2c20(%rip),%xmm1        # 58f0 <_sk_callback_sse41+0x856>
+  DB  65,15,94,202                        ; divps         %xmm10,%xmm1
+  DB  65,15,88,204                        ; addps         %xmm12,%xmm1
+  DB  15,89,13,33,44,0,0                  ; mulps         0x2c21(%rip),%xmm1        # 5900 <_sk_callback_sse41+0x866>
+  DB  102,68,15,91,209                    ; cvtps2dq      %xmm1,%xmm10
+  DB  243,15,16,72,20                     ; movss         0x14(%rax),%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  102,65,15,56,20,201                 ; blendvps      %xmm0,%xmm9,%xmm1
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,95,200                           ; maxps         %xmm0,%xmm1
+  DB  15,93,13,12,44,0,0                  ; minps         0x2c0c(%rip),%xmm1        # 5910 <_sk_callback_sse41+0x876>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_b_sse41
+_sk_parametric_b_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,72,12                  ; movss         0xc(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  68,15,89,202                        ; mulps         %xmm2,%xmm9
+  DB  243,68,15,16,88,4                   ; movss         0x4(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  65,15,194,194,2                     ; cmpleps       %xmm10,%xmm0
+  DB  243,15,16,80,24                     ; movss         0x18(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  68,15,88,202                        ; addps         %xmm2,%xmm9
+  DB  243,68,15,16,16                     ; movss         (%rax),%xmm10
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  68,15,88,218                        ; addps         %xmm2,%xmm11
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  69,15,91,227                        ; cvtdq2ps      %xmm11,%xmm12
+  DB  68,15,89,37,173,43,0,0              ; mulps         0x2bad(%rip),%xmm12        # 5920 <_sk_callback_sse41+0x886>
+  DB  68,15,84,29,181,43,0,0              ; andps         0x2bb5(%rip),%xmm11        # 5930 <_sk_callback_sse41+0x896>
+  DB  68,15,86,29,189,43,0,0              ; orps          0x2bbd(%rip),%xmm11        # 5940 <_sk_callback_sse41+0x8a6>
+  DB  68,15,88,37,197,43,0,0              ; addps         0x2bc5(%rip),%xmm12        # 5950 <_sk_callback_sse41+0x8b6>
+  DB  15,40,21,206,43,0,0                 ; movaps        0x2bce(%rip),%xmm2        # 5960 <_sk_callback_sse41+0x8c6>
+  DB  65,15,89,211                        ; mulps         %xmm11,%xmm2
+  DB  68,15,92,226                        ; subps         %xmm2,%xmm12
+  DB  68,15,88,29,206,43,0,0              ; addps         0x2bce(%rip),%xmm11        # 5970 <_sk_callback_sse41+0x8d6>
+  DB  15,40,21,215,43,0,0                 ; movaps        0x2bd7(%rip),%xmm2        # 5980 <_sk_callback_sse41+0x8e6>
+  DB  65,15,94,211                        ; divps         %xmm11,%xmm2
+  DB  68,15,92,226                        ; subps         %xmm2,%xmm12
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  102,69,15,58,8,212,1                ; roundps       $0x1,%xmm12,%xmm10
+  DB  69,15,40,220                        ; movaps        %xmm12,%xmm11
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  68,15,88,37,196,43,0,0              ; addps         0x2bc4(%rip),%xmm12        # 5990 <_sk_callback_sse41+0x8f6>
+  DB  15,40,21,205,43,0,0                 ; movaps        0x2bcd(%rip),%xmm2        # 59a0 <_sk_callback_sse41+0x906>
+  DB  65,15,89,211                        ; mulps         %xmm11,%xmm2
+  DB  68,15,92,226                        ; subps         %xmm2,%xmm12
+  DB  68,15,40,21,205,43,0,0              ; movaps        0x2bcd(%rip),%xmm10        # 59b0 <_sk_callback_sse41+0x916>
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  15,40,21,210,43,0,0                 ; movaps        0x2bd2(%rip),%xmm2        # 59c0 <_sk_callback_sse41+0x926>
+  DB  65,15,94,210                        ; divps         %xmm10,%xmm2
+  DB  65,15,88,212                        ; addps         %xmm12,%xmm2
+  DB  15,89,21,211,43,0,0                 ; mulps         0x2bd3(%rip),%xmm2        # 59d0 <_sk_callback_sse41+0x936>
+  DB  102,68,15,91,210                    ; cvtps2dq      %xmm2,%xmm10
+  DB  243,15,16,80,20                     ; movss         0x14(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  65,15,88,210                        ; addps         %xmm10,%xmm2
+  DB  102,65,15,56,20,209                 ; blendvps      %xmm0,%xmm9,%xmm2
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,95,208                           ; maxps         %xmm0,%xmm2
+  DB  15,93,21,190,43,0,0                 ; minps         0x2bbe(%rip),%xmm2        # 59e0 <_sk_callback_sse41+0x946>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_a_sse41
+_sk_parametric_a_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,72,12                  ; movss         0xc(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  243,68,15,16,88,4                   ; movss         0x4(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  68,15,89,219                        ; mulps         %xmm3,%xmm11
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  65,15,194,194,2                     ; cmpleps       %xmm10,%xmm0
+  DB  243,15,16,88,24                     ; movss         0x18(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  68,15,88,203                        ; addps         %xmm3,%xmm9
+  DB  243,68,15,16,16                     ; movss         (%rax),%xmm10
+  DB  243,15,16,88,8                      ; movss         0x8(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  68,15,88,219                        ; addps         %xmm3,%xmm11
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  69,15,91,227                        ; cvtdq2ps      %xmm11,%xmm12
+  DB  68,15,89,37,95,43,0,0               ; mulps         0x2b5f(%rip),%xmm12        # 59f0 <_sk_callback_sse41+0x956>
+  DB  68,15,84,29,103,43,0,0              ; andps         0x2b67(%rip),%xmm11        # 5a00 <_sk_callback_sse41+0x966>
+  DB  68,15,86,29,111,43,0,0              ; orps          0x2b6f(%rip),%xmm11        # 5a10 <_sk_callback_sse41+0x976>
+  DB  68,15,88,37,119,43,0,0              ; addps         0x2b77(%rip),%xmm12        # 5a20 <_sk_callback_sse41+0x986>
+  DB  15,40,29,128,43,0,0                 ; movaps        0x2b80(%rip),%xmm3        # 5a30 <_sk_callback_sse41+0x996>
+  DB  65,15,89,219                        ; mulps         %xmm11,%xmm3
+  DB  68,15,92,227                        ; subps         %xmm3,%xmm12
+  DB  68,15,88,29,128,43,0,0              ; addps         0x2b80(%rip),%xmm11        # 5a40 <_sk_callback_sse41+0x9a6>
+  DB  15,40,29,137,43,0,0                 ; movaps        0x2b89(%rip),%xmm3        # 5a50 <_sk_callback_sse41+0x9b6>
+  DB  65,15,94,219                        ; divps         %xmm11,%xmm3
+  DB  68,15,92,227                        ; subps         %xmm3,%xmm12
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  102,69,15,58,8,212,1                ; roundps       $0x1,%xmm12,%xmm10
+  DB  69,15,40,220                        ; movaps        %xmm12,%xmm11
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  68,15,88,37,118,43,0,0              ; addps         0x2b76(%rip),%xmm12        # 5a60 <_sk_callback_sse41+0x9c6>
+  DB  15,40,29,127,43,0,0                 ; movaps        0x2b7f(%rip),%xmm3        # 5a70 <_sk_callback_sse41+0x9d6>
+  DB  65,15,89,219                        ; mulps         %xmm11,%xmm3
+  DB  68,15,92,227                        ; subps         %xmm3,%xmm12
+  DB  68,15,40,21,127,43,0,0              ; movaps        0x2b7f(%rip),%xmm10        # 5a80 <_sk_callback_sse41+0x9e6>
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  15,40,29,132,43,0,0                 ; movaps        0x2b84(%rip),%xmm3        # 5a90 <_sk_callback_sse41+0x9f6>
+  DB  65,15,94,218                        ; divps         %xmm10,%xmm3
+  DB  65,15,88,220                        ; addps         %xmm12,%xmm3
+  DB  15,89,29,133,43,0,0                 ; mulps         0x2b85(%rip),%xmm3        # 5aa0 <_sk_callback_sse41+0xa06>
+  DB  102,68,15,91,211                    ; cvtps2dq      %xmm3,%xmm10
+  DB  243,15,16,88,20                     ; movss         0x14(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  102,65,15,56,20,217                 ; blendvps      %xmm0,%xmm9,%xmm3
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,95,216                           ; maxps         %xmm0,%xmm3
+  DB  15,93,29,112,43,0,0                 ; minps         0x2b70(%rip),%xmm3        # 5ab0 <_sk_callback_sse41+0xa16>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lab_to_xyz_sse41
+_sk_lab_to_xyz_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  68,15,89,5,108,43,0,0               ; mulps         0x2b6c(%rip),%xmm8        # 5ac0 <_sk_callback_sse41+0xa26>
+  DB  68,15,40,13,116,43,0,0              ; movaps        0x2b74(%rip),%xmm9        # 5ad0 <_sk_callback_sse41+0xa36>
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  15,40,5,121,43,0,0                  ; movaps        0x2b79(%rip),%xmm0        # 5ae0 <_sk_callback_sse41+0xa46>
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,208                           ; addps         %xmm0,%xmm2
+  DB  68,15,88,5,119,43,0,0               ; addps         0x2b77(%rip),%xmm8        # 5af0 <_sk_callback_sse41+0xa56>
+  DB  68,15,89,5,127,43,0,0               ; mulps         0x2b7f(%rip),%xmm8        # 5b00 <_sk_callback_sse41+0xa66>
+  DB  15,89,13,136,43,0,0                 ; mulps         0x2b88(%rip),%xmm1        # 5b10 <_sk_callback_sse41+0xa76>
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  15,89,21,141,43,0,0                 ; mulps         0x2b8d(%rip),%xmm2        # 5b20 <_sk_callback_sse41+0xa86>
+  DB  69,15,40,208                        ; movaps        %xmm8,%xmm10
+  DB  68,15,92,210                        ; subps         %xmm2,%xmm10
+  DB  68,15,40,217                        ; movaps        %xmm1,%xmm11
+  DB  69,15,89,219                        ; mulps         %xmm11,%xmm11
+  DB  68,15,89,217                        ; mulps         %xmm1,%xmm11
+  DB  68,15,40,13,129,43,0,0              ; movaps        0x2b81(%rip),%xmm9        # 5b30 <_sk_callback_sse41+0xa96>
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,194,195,1                     ; cmpltps       %xmm11,%xmm0
+  DB  15,40,21,129,43,0,0                 ; movaps        0x2b81(%rip),%xmm2        # 5b40 <_sk_callback_sse41+0xaa6>
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  68,15,40,37,134,43,0,0              ; movaps        0x2b86(%rip),%xmm12        # 5b50 <_sk_callback_sse41+0xab6>
+  DB  65,15,89,204                        ; mulps         %xmm12,%xmm1
+  DB  102,65,15,56,20,203                 ; blendvps      %xmm0,%xmm11,%xmm1
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  69,15,89,219                        ; mulps         %xmm11,%xmm11
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,194,195,1                     ; cmpltps       %xmm11,%xmm0
+  DB  68,15,88,194                        ; addps         %xmm2,%xmm8
+  DB  69,15,89,196                        ; mulps         %xmm12,%xmm8
+  DB  102,69,15,56,20,195                 ; blendvps      %xmm0,%xmm11,%xmm8
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  69,15,89,219                        ; mulps         %xmm11,%xmm11
+  DB  69,15,89,218                        ; mulps         %xmm10,%xmm11
+  DB  69,15,194,203,1                     ; cmpltps       %xmm11,%xmm9
+  DB  65,15,88,210                        ; addps         %xmm10,%xmm2
+  DB  65,15,89,212                        ; mulps         %xmm12,%xmm2
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,65,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm2
+  DB  15,89,13,63,43,0,0                  ; mulps         0x2b3f(%rip),%xmm1        # 5b60 <_sk_callback_sse41+0xac6>
+  DB  15,89,21,72,43,0,0                  ; mulps         0x2b48(%rip),%xmm2        # 5b70 <_sk_callback_sse41+0xad6>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_a8_sse41
+_sk_load_a8_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,38                              ; jne           3063 <_sk_load_a8_sse41+0x30>
+  DB  102,65,15,56,49,4,18                ; pmovzxbd      (%r10,%rdx,1),%xmm0
+  DB  102,15,219,5,52,43,0,0              ; pand          0x2b34(%rip),%xmm0        # 5b80 <_sk_callback_sse41+0xae6>
+  DB  15,91,216                           ; cvtdq2ps      %xmm0,%xmm3
+  DB  15,89,29,58,43,0,0                  ; mulps         0x2b3a(%rip),%xmm3        # 5b90 <_sk_callback_sse41+0xaf6>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,39                              ; je            309b <_sk_load_a8_sse41+0x68>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            308f <_sk_load_a8_sse41+0x5c>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,196                             ; jne           3044 <_sk_load_a8_sse41+0x11>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,192,69                   ; pshufd        $0x45,%xmm0,%xmm0
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,15,58,34,192,1                  ; pinsrd        $0x1,%eax,%xmm0
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,15,58,34,192,0                  ; pinsrd        $0x0,%eax,%xmm0
+  DB  235,156                             ; jmp           3044 <_sk_load_a8_sse41+0x11>
+
+PUBLIC _sk_gather_a8_sse41
+_sk_gather_a8_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,58,22,192,1               ; pextrq        $0x1,%xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,65,15,58,32,4,25,0              ; pinsrb        $0x0,(%r9,%rbx,1),%xmm0
+  DB  102,67,15,58,32,4,25,1              ; pinsrb        $0x1,(%r9,%r11,1),%xmm0
+  DB  67,15,182,28,17                     ; movzbl        (%r9,%r10,1),%ebx
+  DB  102,15,58,32,195,2                  ; pinsrb        $0x2,%ebx,%xmm0
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  102,15,58,32,192,3                  ; pinsrb        $0x3,%eax,%xmm0
+  DB  102,15,56,49,192                    ; pmovzxbd      %xmm0,%xmm0
+  DB  15,91,216                           ; cvtdq2ps      %xmm0,%xmm3
+  DB  15,89,29,136,42,0,0                 ; mulps         0x2a88(%rip),%xmm3        # 5ba0 <_sk_callback_sse41+0xb06>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,15,239,210                      ; pxor          %xmm2,%xmm2
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_a8_sse41
+_sk_store_a8_sse41 LABEL PROC
+  DB  72,131,236,4                        ; sub           $0x4,%rsp
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  68,15,40,5,119,42,0,0               ; movaps        0x2a77(%rip),%xmm8        # 5bb0 <_sk_callback_sse41+0xb16>
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,69,15,56,43,192                 ; packusdw      %xmm8,%xmm8
+  DB  102,69,15,103,192                   ; packuswb      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,17                              ; jne           3163 <_sk_store_a8_sse41+0x3b>
+  DB  102,68,15,126,192                   ; movd          %xmm8,%eax
+  DB  65,137,4,18                         ; mov           %eax,(%r10,%rdx,1)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,4                        ; add           $0x4,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,69,15,56,49,192                 ; pmovzxbd      %xmm8,%xmm8
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,30                              ; je            3194 <_sk_store_a8_sse41+0x6c>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,15                              ; je            318b <_sk_store_a8_sse41+0x63>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,217                             ; jne           315b <_sk_store_a8_sse41+0x33>
+  DB  102,69,15,58,20,68,18,2,8           ; pextrb        $0x8,%xmm8,0x2(%r10,%rdx,1)
+  DB  102,69,15,58,20,68,18,1,4           ; pextrb        $0x4,%xmm8,0x1(%r10,%rdx,1)
+  DB  102,69,15,58,20,4,18,0              ; pextrb        $0x0,%xmm8,(%r10,%rdx,1)
+  DB  235,189                             ; jmp           315b <_sk_store_a8_sse41+0x33>
+
+PUBLIC _sk_load_g8_sse41
+_sk_load_g8_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,42                              ; jne           31d2 <_sk_load_g8_sse41+0x34>
+  DB  102,65,15,56,49,4,18                ; pmovzxbd      (%r10,%rdx,1),%xmm0
+  DB  102,15,219,5,9,42,0,0               ; pand          0x2a09(%rip),%xmm0        # 5bc0 <_sk_callback_sse41+0xb26>
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,15,42,0,0                   ; mulps         0x2a0f(%rip),%xmm0        # 5bd0 <_sk_callback_sse41+0xb36>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,22,42,0,0                  ; movaps        0x2a16(%rip),%xmm3        # 5be0 <_sk_callback_sse41+0xb46>
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,39                              ; je            320a <_sk_load_g8_sse41+0x6c>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            31fe <_sk_load_g8_sse41+0x60>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,192                             ; jne           31af <_sk_load_g8_sse41+0x11>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,192,69                   ; pshufd        $0x45,%xmm0,%xmm0
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,15,58,34,192,1                  ; pinsrd        $0x1,%eax,%xmm0
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,15,58,34,192,0                  ; pinsrd        $0x0,%eax,%xmm0
+  DB  235,152                             ; jmp           31af <_sk_load_g8_sse41+0x11>
+
+PUBLIC _sk_gather_g8_sse41
+_sk_gather_g8_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,58,22,192,1               ; pextrq        $0x1,%xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,65,15,58,32,4,25,0              ; pinsrb        $0x0,(%r9,%rbx,1),%xmm0
+  DB  102,67,15,58,32,4,25,1              ; pinsrb        $0x1,(%r9,%r11,1),%xmm0
+  DB  67,15,182,28,17                     ; movzbl        (%r9,%r10,1),%ebx
+  DB  102,15,58,32,195,2                  ; pinsrb        $0x2,%ebx,%xmm0
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  102,15,58,32,192,3                  ; pinsrb        $0x3,%eax,%xmm0
+  DB  102,15,56,49,192                    ; pmovzxbd      %xmm0,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,105,41,0,0                  ; mulps         0x2969(%rip),%xmm0        # 5bf0 <_sk_callback_sse41+0xb56>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,112,41,0,0                 ; movaps        0x2970(%rip),%xmm3        # 5c00 <_sk_callback_sse41+0xb66>
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gather_i8_sse41
+_sk_gather_i8_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,193                          ; mov           %rax,%r9
+  DB  77,133,201                          ; test          %r9,%r9
+  DB  116,5                               ; je            32a8 <_sk_gather_i8_sse41+0xf>
+  DB  76,137,200                          ; mov           %r9,%rax
+  DB  235,2                               ; jmp           32aa <_sk_gather_i8_sse41+0x11>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,58,22,192,1               ; pextrq        $0x1,%xmm0,%rax
+  DB  65,137,195                          ; mov           %eax,%r11d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,72,15,126,195                   ; movq          %xmm0,%rbx
+  DB  65,137,222                          ; mov           %ebx,%r14d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,67,15,58,32,4,50,0              ; pinsrb        $0x0,(%r10,%r14,1),%xmm0
+  DB  102,65,15,58,32,4,26,1              ; pinsrb        $0x1,(%r10,%rbx,1),%xmm0
+  DB  102,67,15,58,32,4,26,2              ; pinsrb        $0x2,(%r10,%r11,1),%xmm0
+  DB  102,65,15,58,32,4,2,3               ; pinsrb        $0x3,(%r10,%rax,1),%xmm0
+  DB  102,15,56,49,192                    ; pmovzxbd      %xmm0,%xmm0
+  DB  102,73,15,58,22,194,1               ; pextrq        $0x1,%xmm0,%r10
+  DB  102,72,15,126,195                   ; movq          %xmm0,%rbx
+  DB  73,139,65,8                         ; mov           0x8(%r9),%rax
+  DB  65,137,217                          ; mov           %ebx,%r9d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  69,137,211                          ; mov           %r10d,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  102,66,15,110,28,136                ; movd          (%rax,%r9,4),%xmm3
+  DB  102,15,58,34,28,24,1                ; pinsrd        $0x1,(%rax,%rbx,1),%xmm3
+  DB  102,66,15,58,34,28,152,2            ; pinsrd        $0x2,(%rax,%r11,4),%xmm3
+  DB  102,66,15,58,34,28,16,3             ; pinsrd        $0x3,(%rax,%r10,1),%xmm3
+  DB  102,15,111,5,195,40,0,0             ; movdqa        0x28c3(%rip),%xmm0        # 5c10 <_sk_callback_sse41+0xb76>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,196,40,0,0               ; movaps        0x28c4(%rip),%xmm8        # 5c20 <_sk_callback_sse41+0xb86>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,56,0,13,195,40,0,0           ; pshufb        0x28c3(%rip),%xmm1        # 5c30 <_sk_callback_sse41+0xb96>
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,111,211                      ; movdqa        %xmm3,%xmm2
+  DB  102,15,56,0,21,191,40,0,0           ; pshufb        0x28bf(%rip),%xmm2        # 5c40 <_sk_callback_sse41+0xba6>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,114,211,24                   ; psrld         $0x18,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_565_sse41
+_sk_load_565_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,80                              ; jne           33f5 <_sk_load_565_sse41+0x5a>
+  DB  102,65,15,56,51,20,82               ; pmovzxwd      (%r10,%rdx,2),%xmm2
+  DB  102,15,111,5,156,40,0,0             ; movdqa        0x289c(%rip),%xmm0        # 5c50 <_sk_callback_sse41+0xbb6>
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,158,40,0,0                  ; mulps         0x289e(%rip),%xmm0        # 5c60 <_sk_callback_sse41+0xbc6>
+  DB  102,15,111,13,166,40,0,0            ; movdqa        0x28a6(%rip),%xmm1        # 5c70 <_sk_callback_sse41+0xbd6>
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,168,40,0,0                 ; mulps         0x28a8(%rip),%xmm1        # 5c80 <_sk_callback_sse41+0xbe6>
+  DB  102,15,219,21,176,40,0,0            ; pand          0x28b0(%rip),%xmm2        # 5c90 <_sk_callback_sse41+0xbf6>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,182,40,0,0                 ; mulps         0x28b6(%rip),%xmm2        # 5ca0 <_sk_callback_sse41+0xc06>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,189,40,0,0                 ; movaps        0x28bd(%rip),%xmm3        # 5cb0 <_sk_callback_sse41+0xc16>
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,210                      ; pxor          %xmm2,%xmm2
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,39                              ; je            342d <_sk_load_565_sse41+0x92>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            3421 <_sk_load_565_sse41+0x86>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,154                             ; jne           33ac <_sk_load_565_sse41+0x11>
+  DB  65,15,183,68,82,4                   ; movzwl        0x4(%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,208,69                   ; pshufd        $0x45,%xmm0,%xmm2
+  DB  65,15,183,68,82,2                   ; movzwl        0x2(%r10,%rdx,2),%eax
+  DB  102,15,58,34,208,1                  ; pinsrd        $0x1,%eax,%xmm2
+  DB  65,15,183,4,82                      ; movzwl        (%r10,%rdx,2),%eax
+  DB  102,15,58,34,208,0                  ; pinsrd        $0x0,%eax,%xmm2
+  DB  233,111,255,255,255                 ; jmpq          33ac <_sk_load_565_sse41+0x11>
+
+PUBLIC _sk_gather_565_sse41
+_sk_gather_565_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,58,22,192,1               ; pextrq        $0x1,%xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,65,15,196,4,89,0                ; pinsrw        $0x0,(%r9,%rbx,2),%xmm0
+  DB  102,67,15,196,4,89,1                ; pinsrw        $0x1,(%r9,%r11,2),%xmm0
+  DB  67,15,183,28,81                     ; movzwl        (%r9,%r10,2),%ebx
+  DB  102,15,196,195,2                    ; pinsrw        $0x2,%ebx,%xmm0
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  102,15,196,192,3                    ; pinsrw        $0x3,%eax,%xmm0
+  DB  102,15,56,51,208                    ; pmovzxwd      %xmm0,%xmm2
+  DB  102,15,111,5,25,40,0,0              ; movdqa        0x2819(%rip),%xmm0        # 5cc0 <_sk_callback_sse41+0xc26>
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,27,40,0,0                   ; mulps         0x281b(%rip),%xmm0        # 5cd0 <_sk_callback_sse41+0xc36>
+  DB  102,15,111,13,35,40,0,0             ; movdqa        0x2823(%rip),%xmm1        # 5ce0 <_sk_callback_sse41+0xc46>
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,37,40,0,0                  ; mulps         0x2825(%rip),%xmm1        # 5cf0 <_sk_callback_sse41+0xc56>
+  DB  102,15,219,21,45,40,0,0             ; pand          0x282d(%rip),%xmm2        # 5d00 <_sk_callback_sse41+0xc66>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,51,40,0,0                  ; mulps         0x2833(%rip),%xmm2        # 5d10 <_sk_callback_sse41+0xc76>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,58,40,0,0                  ; movaps        0x283a(%rip),%xmm3        # 5d20 <_sk_callback_sse41+0xc86>
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_565_sse41
+_sk_store_565_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  68,15,40,5,58,40,0,0                ; movaps        0x283a(%rip),%xmm8        # 5d30 <_sk_callback_sse41+0xc96>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,11                ; pslld         $0xb,%xmm9
+  DB  68,15,40,21,47,40,0,0               ; movaps        0x282f(%rip),%xmm10        # 5d40 <_sk_callback_sse41+0xca6>
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,5                 ; pslld         $0x5,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,69,15,86,194                    ; orpd          %xmm10,%xmm8
+  DB  102,69,15,56,43,192                 ; packusdw      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3548 <_sk_store_565_sse41+0x5f>
+  DB  242,68,15,17,4,80                   ; movsd         %xmm8,(%rax,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,69,15,56,51,192                 ; pmovzxwd      %xmm8,%xmm8
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,30                              ; je            3579 <_sk_store_565_sse41+0x90>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,15                              ; je            3570 <_sk_store_565_sse41+0x87>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,221                             ; jne           3544 <_sk_store_565_sse41+0x5b>
+  DB  102,68,15,58,21,68,80,4,4           ; pextrw        $0x4,%xmm8,0x4(%rax,%rdx,2)
+  DB  102,68,15,58,21,68,80,2,2           ; pextrw        $0x2,%xmm8,0x2(%rax,%rdx,2)
+  DB  102,68,15,58,21,4,80,0              ; pextrw        $0x0,%xmm8,(%rax,%rdx,2)
+  DB  235,193                             ; jmp           3544 <_sk_store_565_sse41+0x5b>
+
+PUBLIC _sk_load_4444_sse41
+_sk_load_4444_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,95                              ; jne           35ec <_sk_load_4444_sse41+0x69>
+  DB  102,65,15,56,51,28,82               ; pmovzxwd      (%r10,%rdx,2),%xmm3
+  DB  102,15,111,5,180,39,0,0             ; movdqa        0x27b4(%rip),%xmm0        # 5d50 <_sk_callback_sse41+0xcb6>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,182,39,0,0                  ; mulps         0x27b6(%rip),%xmm0        # 5d60 <_sk_callback_sse41+0xcc6>
+  DB  102,15,111,13,190,39,0,0            ; movdqa        0x27be(%rip),%xmm1        # 5d70 <_sk_callback_sse41+0xcd6>
+  DB  102,15,219,203                      ; pand          %xmm3,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,192,39,0,0                 ; mulps         0x27c0(%rip),%xmm1        # 5d80 <_sk_callback_sse41+0xce6>
+  DB  102,15,111,21,200,39,0,0            ; movdqa        0x27c8(%rip),%xmm2        # 5d90 <_sk_callback_sse41+0xcf6>
+  DB  102,15,219,211                      ; pand          %xmm3,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,202,39,0,0                 ; mulps         0x27ca(%rip),%xmm2        # 5da0 <_sk_callback_sse41+0xd06>
+  DB  102,15,219,29,210,39,0,0            ; pand          0x27d2(%rip),%xmm3        # 5db0 <_sk_callback_sse41+0xd16>
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  15,89,29,216,39,0,0                 ; mulps         0x27d8(%rip),%xmm3        # 5dc0 <_sk_callback_sse41+0xd26>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,39                              ; je            3624 <_sk_load_4444_sse41+0xa1>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            3618 <_sk_load_4444_sse41+0x95>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,139                             ; jne           3594 <_sk_load_4444_sse41+0x11>
+  DB  65,15,183,68,82,4                   ; movzwl        0x4(%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,216,69                   ; pshufd        $0x45,%xmm0,%xmm3
+  DB  65,15,183,68,82,2                   ; movzwl        0x2(%r10,%rdx,2),%eax
+  DB  102,15,58,34,216,1                  ; pinsrd        $0x1,%eax,%xmm3
+  DB  65,15,183,4,82                      ; movzwl        (%r10,%rdx,2),%eax
+  DB  102,15,58,34,216,0                  ; pinsrd        $0x0,%eax,%xmm3
+  DB  233,96,255,255,255                  ; jmpq          3594 <_sk_load_4444_sse41+0x11>
+
+PUBLIC _sk_gather_4444_sse41
+_sk_gather_4444_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,58,22,192,1               ; pextrq        $0x1,%xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,65,15,196,4,89,0                ; pinsrw        $0x0,(%r9,%rbx,2),%xmm0
+  DB  102,67,15,196,4,89,1                ; pinsrw        $0x1,(%r9,%r11,2),%xmm0
+  DB  67,15,183,28,81                     ; movzwl        (%r9,%r10,2),%ebx
+  DB  102,15,196,195,2                    ; pinsrw        $0x2,%ebx,%xmm0
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  102,15,196,192,3                    ; pinsrw        $0x3,%eax,%xmm0
+  DB  102,15,56,51,216                    ; pmovzxwd      %xmm0,%xmm3
+  DB  102,15,111,5,50,39,0,0              ; movdqa        0x2732(%rip),%xmm0        # 5dd0 <_sk_callback_sse41+0xd36>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,52,39,0,0                   ; mulps         0x2734(%rip),%xmm0        # 5de0 <_sk_callback_sse41+0xd46>
+  DB  102,15,111,13,60,39,0,0             ; movdqa        0x273c(%rip),%xmm1        # 5df0 <_sk_callback_sse41+0xd56>
+  DB  102,15,219,203                      ; pand          %xmm3,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,62,39,0,0                  ; mulps         0x273e(%rip),%xmm1        # 5e00 <_sk_callback_sse41+0xd66>
+  DB  102,15,111,21,70,39,0,0             ; movdqa        0x2746(%rip),%xmm2        # 5e10 <_sk_callback_sse41+0xd76>
+  DB  102,15,219,211                      ; pand          %xmm3,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,72,39,0,0                  ; mulps         0x2748(%rip),%xmm2        # 5e20 <_sk_callback_sse41+0xd86>
+  DB  102,15,219,29,80,39,0,0             ; pand          0x2750(%rip),%xmm3        # 5e30 <_sk_callback_sse41+0xd96>
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  15,89,29,86,39,0,0                  ; mulps         0x2756(%rip),%xmm3        # 5e40 <_sk_callback_sse41+0xda6>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_4444_sse41
+_sk_store_4444_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  68,15,40,5,84,39,0,0                ; movaps        0x2754(%rip),%xmm8        # 5e50 <_sk_callback_sse41+0xdb6>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,12                ; pslld         $0xc,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,8                 ; pslld         $0x8,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,4                 ; pslld         $0x4,%xmm9
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,69,15,86,193                    ; orpd          %xmm9,%xmm8
+  DB  102,69,15,86,194                    ; orpd          %xmm10,%xmm8
+  DB  102,69,15,56,43,192                 ; packusdw      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3762 <_sk_store_4444_sse41+0x73>
+  DB  242,68,15,17,4,80                   ; movsd         %xmm8,(%rax,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,69,15,56,51,192                 ; pmovzxwd      %xmm8,%xmm8
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,30                              ; je            3793 <_sk_store_4444_sse41+0xa4>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,15                              ; je            378a <_sk_store_4444_sse41+0x9b>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,221                             ; jne           375e <_sk_store_4444_sse41+0x6f>
+  DB  102,68,15,58,21,68,80,4,4           ; pextrw        $0x4,%xmm8,0x4(%rax,%rdx,2)
+  DB  102,68,15,58,21,68,80,2,2           ; pextrw        $0x2,%xmm8,0x2(%rax,%rdx,2)
+  DB  102,68,15,58,21,4,80,0              ; pextrw        $0x0,%xmm8,(%rax,%rdx,2)
+  DB  235,193                             ; jmp           375e <_sk_store_4444_sse41+0x6f>
+
+PUBLIC _sk_load_8888_sse41
+_sk_load_8888_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,88                              ; jne           37ff <_sk_load_8888_sse41+0x62>
+  DB  243,15,111,28,144                   ; movdqu        (%rax,%rdx,4),%xmm3
+  DB  102,15,111,5,172,38,0,0             ; movdqa        0x26ac(%rip),%xmm0        # 5e60 <_sk_callback_sse41+0xdc6>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,173,38,0,0               ; movaps        0x26ad(%rip),%xmm8        # 5e70 <_sk_callback_sse41+0xdd6>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,56,0,13,172,38,0,0           ; pshufb        0x26ac(%rip),%xmm1        # 5e80 <_sk_callback_sse41+0xde6>
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,111,211                      ; movdqa        %xmm3,%xmm2
+  DB  102,15,56,0,21,168,38,0,0           ; pshufb        0x26a8(%rip),%xmm2        # 5e90 <_sk_callback_sse41+0xdf6>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,114,211,24                   ; psrld         $0x18,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,31                              ; je            382f <_sk_load_8888_sse41+0x92>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,17                              ; je            3827 <_sk_load_8888_sse41+0x8a>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,144                             ; jne           37ac <_sk_load_8888_sse41+0xf>
+  DB  102,15,110,68,144,8                 ; movd          0x8(%rax,%rdx,4),%xmm0
+  DB  102,15,112,216,69                   ; pshufd        $0x45,%xmm0,%xmm3
+  DB  102,15,58,34,92,144,4,1             ; pinsrd        $0x1,0x4(%rax,%rdx,4),%xmm3
+  DB  102,15,58,34,28,144,0               ; pinsrd        $0x0,(%rax,%rdx,4),%xmm3
+  DB  233,113,255,255,255                 ; jmpq          37ac <_sk_load_8888_sse41+0xf>
+
+PUBLIC _sk_gather_8888_sse41
+_sk_gather_8888_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,126,192                   ; movq          %xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,58,22,195,1               ; pextrq        $0x1,%xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,67,15,110,28,145                ; movd          (%r9,%r10,4),%xmm3
+  DB  102,65,15,58,34,28,129,1            ; pinsrd        $0x1,(%r9,%rax,4),%xmm3
+  DB  102,65,15,58,34,28,153,2            ; pinsrd        $0x2,(%r9,%rbx,4),%xmm3
+  DB  102,67,15,58,34,28,153,3            ; pinsrd        $0x3,(%r9,%r11,4),%xmm3
+  DB  102,15,111,5,4,38,0,0               ; movdqa        0x2604(%rip),%xmm0        # 5ea0 <_sk_callback_sse41+0xe06>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,5,38,0,0                 ; movaps        0x2605(%rip),%xmm8        # 5eb0 <_sk_callback_sse41+0xe16>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,56,0,13,4,38,0,0             ; pshufb        0x2604(%rip),%xmm1        # 5ec0 <_sk_callback_sse41+0xe26>
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,111,211                      ; movdqa        %xmm3,%xmm2
+  DB  102,15,56,0,21,0,38,0,0             ; pshufb        0x2600(%rip),%xmm2        # 5ed0 <_sk_callback_sse41+0xe36>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,114,211,24                   ; psrld         $0x18,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_8888_sse41
+_sk_store_8888_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  68,15,40,5,235,37,0,0               ; movaps        0x25eb(%rip),%xmm8        # 5ee0 <_sk_callback_sse41+0xe46>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,8                 ; pslld         $0x8,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,16                ; pslld         $0x10,%xmm9
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,114,240,24                ; pslld         $0x18,%xmm8
+  DB  102,69,15,235,193                   ; por           %xmm9,%xmm8
+  DB  102,69,15,235,194                   ; por           %xmm10,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3955 <_sk_store_8888_sse41+0x6d>
+  DB  243,68,15,127,4,144                 ; movdqu        %xmm8,(%rax,%rdx,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,30                              ; je            3980 <_sk_store_8888_sse41+0x98>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,15                              ; je            3977 <_sk_store_8888_sse41+0x8f>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,227                             ; jne           3951 <_sk_store_8888_sse41+0x69>
+  DB  102,68,15,58,22,68,144,8,2          ; pextrd        $0x2,%xmm8,0x8(%rax,%rdx,4)
+  DB  102,68,15,58,22,68,144,4,1          ; pextrd        $0x1,%xmm8,0x4(%rax,%rdx,4)
+  DB  102,68,15,126,4,144                 ; movd          %xmm8,(%rax,%rdx,4)
+  DB  235,201                             ; jmp           3951 <_sk_store_8888_sse41+0x69>
+
+PUBLIC _sk_load_f16_sse41
+_sk_load_f16_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,60,1,0,0                     ; jne           3ad2 <_sk_load_f16_sse41+0x14a>
+  DB  102,15,16,4,208                     ; movupd        (%rax,%rdx,8),%xmm0
+  DB  243,15,111,76,208,16                ; movdqu        0x10(%rax,%rdx,8),%xmm1
+  DB  102,68,15,40,200                    ; movapd        %xmm0,%xmm9
+  DB  102,68,15,97,201                    ; punpcklwd     %xmm1,%xmm9
+  DB  102,15,105,193                      ; punpckhwd     %xmm1,%xmm0
+  DB  102,69,15,111,217                   ; movdqa        %xmm9,%xmm11
+  DB  102,68,15,97,216                    ; punpcklwd     %xmm0,%xmm11
+  DB  102,68,15,105,200                   ; punpckhwd     %xmm0,%xmm9
+  DB  102,65,15,56,51,203                 ; pmovzxwd      %xmm11,%xmm1
+  DB  102,68,15,111,5,35,37,0,0           ; movdqa        0x2523(%rip),%xmm8        # 5ef0 <_sk_callback_sse41+0xe56>
+  DB  102,15,111,209                      ; movdqa        %xmm1,%xmm2
+  DB  102,65,15,219,208                   ; pand          %xmm8,%xmm2
+  DB  102,15,239,202                      ; pxor          %xmm2,%xmm1
+  DB  102,15,111,29,30,37,0,0             ; movdqa        0x251e(%rip),%xmm3        # 5f00 <_sk_callback_sse41+0xe66>
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,56,63,195                    ; pmaxud        %xmm3,%xmm0
+  DB  102,15,118,193                      ; pcmpeqd       %xmm1,%xmm0
+  DB  102,15,114,241,13                   ; pslld         $0xd,%xmm1
+  DB  102,15,235,202                      ; por           %xmm2,%xmm1
+  DB  102,68,15,111,21,10,37,0,0          ; movdqa        0x250a(%rip),%xmm10        # 5f10 <_sk_callback_sse41+0xe76>
+  DB  102,65,15,254,202                   ; paddd         %xmm10,%xmm1
+  DB  102,15,219,193                      ; pand          %xmm1,%xmm0
+  DB  102,65,15,115,219,8                 ; psrldq        $0x8,%xmm11
+  DB  102,69,15,56,51,219                 ; pmovzxwd      %xmm11,%xmm11
+  DB  102,65,15,111,211                   ; movdqa        %xmm11,%xmm2
+  DB  102,65,15,219,208                   ; pand          %xmm8,%xmm2
+  DB  102,68,15,239,218                   ; pxor          %xmm2,%xmm11
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,65,15,111,203                   ; movdqa        %xmm11,%xmm1
+  DB  102,15,56,63,203                    ; pmaxud        %xmm3,%xmm1
+  DB  102,65,15,118,203                   ; pcmpeqd       %xmm11,%xmm1
+  DB  102,65,15,114,243,13                ; pslld         $0xd,%xmm11
+  DB  102,68,15,235,218                   ; por           %xmm2,%xmm11
+  DB  102,69,15,254,218                   ; paddd         %xmm10,%xmm11
+  DB  102,65,15,219,203                   ; pand          %xmm11,%xmm1
+  DB  102,69,15,56,51,217                 ; pmovzxwd      %xmm9,%xmm11
+  DB  102,69,15,111,227                   ; movdqa        %xmm11,%xmm12
+  DB  102,69,15,219,224                   ; pand          %xmm8,%xmm12
+  DB  102,69,15,239,220                   ; pxor          %xmm12,%xmm11
+  DB  102,65,15,114,244,16                ; pslld         $0x10,%xmm12
+  DB  102,65,15,111,211                   ; movdqa        %xmm11,%xmm2
+  DB  102,15,56,63,211                    ; pmaxud        %xmm3,%xmm2
+  DB  102,65,15,118,211                   ; pcmpeqd       %xmm11,%xmm2
+  DB  102,65,15,114,243,13                ; pslld         $0xd,%xmm11
+  DB  102,69,15,235,220                   ; por           %xmm12,%xmm11
+  DB  102,69,15,254,218                   ; paddd         %xmm10,%xmm11
+  DB  102,65,15,219,211                   ; pand          %xmm11,%xmm2
+  DB  102,65,15,115,217,8                 ; psrldq        $0x8,%xmm9
+  DB  102,69,15,56,51,201                 ; pmovzxwd      %xmm9,%xmm9
+  DB  102,69,15,219,193                   ; pand          %xmm9,%xmm8
+  DB  102,69,15,239,200                   ; pxor          %xmm8,%xmm9
+  DB  102,65,15,114,240,16                ; pslld         $0x10,%xmm8
+  DB  102,65,15,56,63,217                 ; pmaxud        %xmm9,%xmm3
+  DB  102,65,15,118,217                   ; pcmpeqd       %xmm9,%xmm3
+  DB  102,65,15,114,241,13                ; pslld         $0xd,%xmm9
+  DB  102,69,15,235,200                   ; por           %xmm8,%xmm9
+  DB  102,69,15,254,202                   ; paddd         %xmm10,%xmm9
+  DB  102,65,15,219,217                   ; pand          %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  242,15,16,4,208                     ; movsd         (%rax,%rdx,8),%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,13                              ; jne           3aea <_sk_load_f16_sse41+0x162>
+  DB  243,15,126,192                      ; movq          %xmm0,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,183,254,255,255                 ; jmpq          39a1 <_sk_load_f16_sse41+0x19>
+  DB  102,15,22,68,208,8                  ; movhpd        0x8(%rax,%rdx,8),%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  15,130,163,254,255,255              ; jb            39a1 <_sk_load_f16_sse41+0x19>
+  DB  243,15,126,76,208,16                ; movq          0x10(%rax,%rdx,8),%xmm1
+  DB  233,152,254,255,255                 ; jmpq          39a1 <_sk_load_f16_sse41+0x19>
+
+PUBLIC _sk_gather_f16_sse41
+_sk_gather_f16_sse41 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,56,64,209                    ; pmulld        %xmm1,%xmm2
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,194                      ; paddd         %xmm2,%xmm0
+  DB  102,72,15,126,192                   ; movq          %xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,58,22,195,1               ; pextrq        $0x1,%xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,67,15,126,4,217                 ; movq          (%r9,%r11,8),%xmm0
+  DB  243,65,15,126,12,217                ; movq          (%r9,%rbx,8),%xmm1
+  DB  102,15,108,200                      ; punpcklqdq    %xmm0,%xmm1
+  DB  243,65,15,126,4,193                 ; movq          (%r9,%rax,8),%xmm0
+  DB  243,67,15,126,20,209                ; movq          (%r9,%r10,8),%xmm2
+  DB  102,15,108,208                      ; punpcklqdq    %xmm0,%xmm2
+  DB  102,68,15,111,202                   ; movdqa        %xmm2,%xmm9
+  DB  102,68,15,97,201                    ; punpcklwd     %xmm1,%xmm9
+  DB  102,15,105,209                      ; punpckhwd     %xmm1,%xmm2
+  DB  102,69,15,111,217                   ; movdqa        %xmm9,%xmm11
+  DB  102,68,15,97,218                    ; punpcklwd     %xmm2,%xmm11
+  DB  102,68,15,105,202                   ; punpckhwd     %xmm2,%xmm9
+  DB  102,65,15,56,51,203                 ; pmovzxwd      %xmm11,%xmm1
+  DB  102,68,15,111,5,144,35,0,0          ; movdqa        0x2390(%rip),%xmm8        # 5f20 <_sk_callback_sse41+0xe86>
+  DB  102,15,111,209                      ; movdqa        %xmm1,%xmm2
+  DB  102,65,15,219,208                   ; pand          %xmm8,%xmm2
+  DB  102,15,239,202                      ; pxor          %xmm2,%xmm1
+  DB  102,15,111,29,139,35,0,0            ; movdqa        0x238b(%rip),%xmm3        # 5f30 <_sk_callback_sse41+0xe96>
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,56,63,195                    ; pmaxud        %xmm3,%xmm0
+  DB  102,15,118,193                      ; pcmpeqd       %xmm1,%xmm0
+  DB  102,15,114,241,13                   ; pslld         $0xd,%xmm1
+  DB  102,15,235,202                      ; por           %xmm2,%xmm1
+  DB  102,68,15,111,21,119,35,0,0         ; movdqa        0x2377(%rip),%xmm10        # 5f40 <_sk_callback_sse41+0xea6>
+  DB  102,65,15,254,202                   ; paddd         %xmm10,%xmm1
+  DB  102,15,219,193                      ; pand          %xmm1,%xmm0
+  DB  102,65,15,115,219,8                 ; psrldq        $0x8,%xmm11
+  DB  102,69,15,56,51,219                 ; pmovzxwd      %xmm11,%xmm11
+  DB  102,65,15,111,211                   ; movdqa        %xmm11,%xmm2
+  DB  102,65,15,219,208                   ; pand          %xmm8,%xmm2
+  DB  102,68,15,239,218                   ; pxor          %xmm2,%xmm11
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,65,15,111,203                   ; movdqa        %xmm11,%xmm1
+  DB  102,15,56,63,203                    ; pmaxud        %xmm3,%xmm1
+  DB  102,65,15,118,203                   ; pcmpeqd       %xmm11,%xmm1
+  DB  102,65,15,114,243,13                ; pslld         $0xd,%xmm11
+  DB  102,68,15,235,218                   ; por           %xmm2,%xmm11
+  DB  102,69,15,254,218                   ; paddd         %xmm10,%xmm11
+  DB  102,65,15,219,203                   ; pand          %xmm11,%xmm1
+  DB  102,69,15,56,51,217                 ; pmovzxwd      %xmm9,%xmm11
+  DB  102,69,15,111,227                   ; movdqa        %xmm11,%xmm12
+  DB  102,69,15,219,224                   ; pand          %xmm8,%xmm12
+  DB  102,69,15,239,220                   ; pxor          %xmm12,%xmm11
+  DB  102,65,15,114,244,16                ; pslld         $0x10,%xmm12
+  DB  102,65,15,111,211                   ; movdqa        %xmm11,%xmm2
+  DB  102,15,56,63,211                    ; pmaxud        %xmm3,%xmm2
+  DB  102,65,15,118,211                   ; pcmpeqd       %xmm11,%xmm2
+  DB  102,65,15,114,243,13                ; pslld         $0xd,%xmm11
+  DB  102,69,15,235,220                   ; por           %xmm12,%xmm11
+  DB  102,69,15,254,218                   ; paddd         %xmm10,%xmm11
+  DB  102,65,15,219,211                   ; pand          %xmm11,%xmm2
+  DB  102,65,15,115,217,8                 ; psrldq        $0x8,%xmm9
+  DB  102,69,15,56,51,201                 ; pmovzxwd      %xmm9,%xmm9
+  DB  102,69,15,219,193                   ; pand          %xmm9,%xmm8
+  DB  102,69,15,239,200                   ; pxor          %xmm8,%xmm9
+  DB  102,65,15,114,240,16                ; pslld         $0x10,%xmm8
+  DB  102,65,15,56,63,217                 ; pmaxud        %xmm9,%xmm3
+  DB  102,65,15,118,217                   ; pcmpeqd       %xmm9,%xmm3
+  DB  102,65,15,114,241,13                ; pslld         $0xd,%xmm9
+  DB  102,69,15,235,200                   ; por           %xmm8,%xmm9
+  DB  102,69,15,254,202                   ; paddd         %xmm10,%xmm9
+  DB  102,65,15,219,217                   ; pand          %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_f16_sse41
+_sk_store_f16_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  102,68,15,111,21,172,34,0,0         ; movdqa        0x22ac(%rip),%xmm10        # 5f50 <_sk_callback_sse41+0xeb6>
+  DB  102,68,15,111,216                   ; movdqa        %xmm0,%xmm11
+  DB  102,69,15,219,218                   ; pand          %xmm10,%xmm11
+  DB  102,68,15,111,232                   ; movdqa        %xmm0,%xmm13
+  DB  102,69,15,239,235                   ; pxor          %xmm11,%xmm13
+  DB  102,68,15,111,13,159,34,0,0         ; movdqa        0x229f(%rip),%xmm9        # 5f60 <_sk_callback_sse41+0xec6>
+  DB  102,65,15,114,211,16                ; psrld         $0x10,%xmm11
+  DB  102,69,15,111,193                   ; movdqa        %xmm9,%xmm8
+  DB  102,69,15,102,197                   ; pcmpgtd       %xmm13,%xmm8
+  DB  102,65,15,114,213,13                ; psrld         $0xd,%xmm13
+  DB  102,68,15,111,37,144,34,0,0         ; movdqa        0x2290(%rip),%xmm12        # 5f70 <_sk_callback_sse41+0xed6>
+  DB  102,69,15,235,220                   ; por           %xmm12,%xmm11
+  DB  102,69,15,254,221                   ; paddd         %xmm13,%xmm11
+  DB  102,69,15,223,195                   ; pandn         %xmm11,%xmm8
+  DB  102,69,15,56,43,192                 ; packusdw      %xmm8,%xmm8
+  DB  102,68,15,111,217                   ; movdqa        %xmm1,%xmm11
+  DB  102,69,15,219,218                   ; pand          %xmm10,%xmm11
+  DB  102,68,15,111,241                   ; movdqa        %xmm1,%xmm14
+  DB  102,69,15,239,243                   ; pxor          %xmm11,%xmm14
+  DB  102,65,15,114,211,16                ; psrld         $0x10,%xmm11
+  DB  102,69,15,111,233                   ; movdqa        %xmm9,%xmm13
+  DB  102,69,15,102,238                   ; pcmpgtd       %xmm14,%xmm13
+  DB  102,65,15,114,214,13                ; psrld         $0xd,%xmm14
+  DB  102,69,15,235,220                   ; por           %xmm12,%xmm11
+  DB  102,69,15,254,222                   ; paddd         %xmm14,%xmm11
+  DB  102,69,15,223,235                   ; pandn         %xmm11,%xmm13
+  DB  102,69,15,56,43,237                 ; packusdw      %xmm13,%xmm13
+  DB  102,68,15,111,242                   ; movdqa        %xmm2,%xmm14
+  DB  102,69,15,219,242                   ; pand          %xmm10,%xmm14
+  DB  102,68,15,111,250                   ; movdqa        %xmm2,%xmm15
+  DB  102,69,15,239,254                   ; pxor          %xmm14,%xmm15
+  DB  102,65,15,114,214,16                ; psrld         $0x10,%xmm14
+  DB  102,69,15,111,217                   ; movdqa        %xmm9,%xmm11
+  DB  102,69,15,102,223                   ; pcmpgtd       %xmm15,%xmm11
+  DB  102,65,15,114,215,13                ; psrld         $0xd,%xmm15
+  DB  102,69,15,235,244                   ; por           %xmm12,%xmm14
+  DB  102,69,15,254,247                   ; paddd         %xmm15,%xmm14
+  DB  102,69,15,223,222                   ; pandn         %xmm14,%xmm11
+  DB  102,69,15,56,43,219                 ; packusdw      %xmm11,%xmm11
+  DB  102,68,15,219,211                   ; pand          %xmm3,%xmm10
+  DB  102,68,15,111,243                   ; movdqa        %xmm3,%xmm14
+  DB  102,69,15,239,242                   ; pxor          %xmm10,%xmm14
+  DB  102,65,15,114,210,16                ; psrld         $0x10,%xmm10
+  DB  102,69,15,102,206                   ; pcmpgtd       %xmm14,%xmm9
+  DB  102,65,15,114,214,13                ; psrld         $0xd,%xmm14
+  DB  102,69,15,235,212                   ; por           %xmm12,%xmm10
+  DB  102,69,15,254,214                   ; paddd         %xmm14,%xmm10
+  DB  102,69,15,223,202                   ; pandn         %xmm10,%xmm9
+  DB  102,69,15,56,43,201                 ; packusdw      %xmm9,%xmm9
+  DB  102,69,15,97,197                    ; punpcklwd     %xmm13,%xmm8
+  DB  102,69,15,97,217                    ; punpcklwd     %xmm9,%xmm11
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,69,15,98,203                    ; punpckldq     %xmm11,%xmm9
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,21                              ; jne           3dd6 <_sk_store_f16_sse41+0x140>
+  DB  68,15,17,12,208                     ; movups        %xmm9,(%rax,%rdx,8)
+  DB  102,69,15,106,195                   ; punpckhdq     %xmm11,%xmm8
+  DB  243,68,15,127,68,208,16             ; movdqu        %xmm8,0x10(%rax,%rdx,8)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,68,15,214,12,208                ; movq          %xmm9,(%rax,%rdx,8)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            3dd2 <_sk_store_f16_sse41+0x13c>
+  DB  102,68,15,23,76,208,8               ; movhpd        %xmm9,0x8(%rax,%rdx,8)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            3dd2 <_sk_store_f16_sse41+0x13c>
+  DB  102,69,15,106,195                   ; punpckhdq     %xmm11,%xmm8
+  DB  102,68,15,214,68,208,16             ; movq          %xmm8,0x10(%rax,%rdx,8)
+  DB  235,213                             ; jmp           3dd2 <_sk_store_f16_sse41+0x13c>
+
+PUBLIC _sk_load_u16_be_sse41
+_sk_load_u16_be_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,185,0,0,0                    ; jne           3ecc <_sk_load_u16_be_sse41+0xcf>
+  DB  102,65,15,16,4,65                   ; movupd        (%r9,%rax,2),%xmm0
+  DB  243,65,15,111,76,65,16              ; movdqu        0x10(%r9,%rax,2),%xmm1
+  DB  102,15,40,208                       ; movapd        %xmm0,%xmm2
+  DB  102,15,97,209                       ; punpcklwd     %xmm1,%xmm2
+  DB  102,15,105,193                      ; punpckhwd     %xmm1,%xmm0
+  DB  102,15,111,202                      ; movdqa        %xmm2,%xmm1
+  DB  102,15,97,200                       ; punpcklwd     %xmm0,%xmm1
+  DB  102,15,105,208                      ; punpckhwd     %xmm0,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,113,240,8                    ; psllw         $0x8,%xmm0
+  DB  102,15,112,217,78                   ; pshufd        $0x4e,%xmm1,%xmm3
+  DB  102,15,113,209,8                    ; psrlw         $0x8,%xmm1
+  DB  102,15,235,200                      ; por           %xmm0,%xmm1
+  DB  102,15,56,51,193                    ; pmovzxwd      %xmm1,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,33,33,0,0                ; movaps        0x2121(%rip),%xmm8        # 5f80 <_sk_callback_sse41+0xee6>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,113,241,8                    ; psllw         $0x8,%xmm1
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,15,235,217                      ; por           %xmm1,%xmm3
+  DB  102,15,56,51,203                    ; pmovzxwd      %xmm3,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,68,15,111,202                   ; movdqa        %xmm2,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,15,112,218,78                   ; pshufd        $0x4e,%xmm2,%xmm3
+  DB  102,15,113,210,8                    ; psrlw         $0x8,%xmm2
+  DB  102,65,15,235,209                   ; por           %xmm9,%xmm2
+  DB  102,15,56,51,210                    ; pmovzxwd      %xmm2,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,68,15,111,203                   ; movdqa        %xmm3,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,65,15,235,217                   ; por           %xmm9,%xmm3
+  DB  102,15,56,51,219                    ; pmovzxwd      %xmm3,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  242,65,15,16,4,65                   ; movsd         (%r9,%rax,2),%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,13                              ; jne           3ee5 <_sk_load_u16_be_sse41+0xe8>
+  DB  243,15,126,192                      ; movq          %xmm0,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,59,255,255,255                  ; jmpq          3e20 <_sk_load_u16_be_sse41+0x23>
+  DB  102,65,15,22,68,65,8                ; movhpd        0x8(%r9,%rax,2),%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  15,130,38,255,255,255               ; jb            3e20 <_sk_load_u16_be_sse41+0x23>
+  DB  243,65,15,126,76,65,16              ; movq          0x10(%r9,%rax,2),%xmm1
+  DB  233,26,255,255,255                  ; jmpq          3e20 <_sk_load_u16_be_sse41+0x23>
+
+PUBLIC _sk_load_rgb_u16_be_sse41
+_sk_load_rgb_u16_be_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,82                         ; lea           (%rdx,%rdx,2),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,170,0,0,0                    ; jne           3fc2 <_sk_load_rgb_u16_be_sse41+0xbc>
+  DB  243,65,15,111,20,65                 ; movdqu        (%r9,%rax,2),%xmm2
+  DB  243,65,15,111,92,65,8               ; movdqu        0x8(%r9,%rax,2),%xmm3
+  DB  102,15,115,219,4                    ; psrldq        $0x4,%xmm3
+  DB  102,15,111,194                      ; movdqa        %xmm2,%xmm0
+  DB  102,15,115,216,6                    ; psrldq        $0x6,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,115,217,6                    ; psrldq        $0x6,%xmm1
+  DB  102,15,97,211                       ; punpcklwd     %xmm3,%xmm2
+  DB  102,15,97,193                       ; punpcklwd     %xmm1,%xmm0
+  DB  102,15,111,202                      ; movdqa        %xmm2,%xmm1
+  DB  102,15,97,200                       ; punpcklwd     %xmm0,%xmm1
+  DB  102,15,112,217,78                   ; pshufd        $0x4e,%xmm1,%xmm3
+  DB  102,15,105,208                      ; punpckhwd     %xmm0,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,113,240,8                    ; psllw         $0x8,%xmm0
+  DB  102,15,113,209,8                    ; psrlw         $0x8,%xmm1
+  DB  102,15,235,200                      ; por           %xmm0,%xmm1
+  DB  102,15,56,51,193                    ; pmovzxwd      %xmm1,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,25,32,0,0                ; movaps        0x2019(%rip),%xmm8        # 5f90 <_sk_callback_sse41+0xef6>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,113,241,8                    ; psllw         $0x8,%xmm1
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,15,235,217                      ; por           %xmm1,%xmm3
+  DB  102,15,56,51,203                    ; pmovzxwd      %xmm3,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,111,218                      ; movdqa        %xmm2,%xmm3
+  DB  102,15,113,243,8                    ; psllw         $0x8,%xmm3
+  DB  102,15,113,210,8                    ; psrlw         $0x8,%xmm2
+  DB  102,15,235,211                      ; por           %xmm3,%xmm2
+  DB  102,15,56,51,210                    ; pmovzxwd      %xmm2,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,224,31,0,0                 ; movaps        0x1fe0(%rip),%xmm3        # 5fa0 <_sk_callback_sse41+0xf06>
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,65,15,110,20,65                 ; movd          (%r9,%rax,2),%xmm2
+  DB  102,65,15,196,84,65,4,2             ; pinsrw        $0x2,0x4(%r9,%rax,2),%xmm2
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,13                              ; jne           3fe7 <_sk_load_rgb_u16_be_sse41+0xe1>
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  233,85,255,255,255                  ; jmpq          3f3c <_sk_load_rgb_u16_be_sse41+0x36>
+  DB  102,65,15,110,68,65,6               ; movd          0x6(%r9,%rax,2),%xmm0
+  DB  102,65,15,196,68,65,10,2            ; pinsrw        $0x2,0xa(%r9,%rax,2),%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,24                              ; jb            4018 <_sk_load_rgb_u16_be_sse41+0x112>
+  DB  102,65,15,110,92,65,12              ; movd          0xc(%r9,%rax,2),%xmm3
+  DB  102,65,15,196,92,65,16,2            ; pinsrw        $0x2,0x10(%r9,%rax,2),%xmm3
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,36,255,255,255                  ; jmpq          3f3c <_sk_load_rgb_u16_be_sse41+0x36>
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  233,27,255,255,255                  ; jmpq          3f3c <_sk_load_rgb_u16_be_sse41+0x36>
+
+PUBLIC _sk_store_u16_be_sse41
+_sk_store_u16_be_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  68,15,40,21,122,31,0,0              ; movaps        0x1f7a(%rip),%xmm10        # 5fb0 <_sk_callback_sse41+0xf16>
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,89,194                        ; mulps         %xmm10,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,69,15,56,43,192                 ; packusdw      %xmm8,%xmm8
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,65,15,113,208,8                 ; psrlw         $0x8,%xmm8
+  DB  102,69,15,235,193                   ; por           %xmm9,%xmm8
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  69,15,89,202                        ; mulps         %xmm10,%xmm9
+  DB  102,69,15,91,217                    ; cvtps2dq      %xmm9,%xmm11
+  DB  102,69,15,56,43,219                 ; packusdw      %xmm11,%xmm11
+  DB  102,69,15,111,203                   ; movdqa        %xmm11,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,65,15,113,211,8                 ; psrlw         $0x8,%xmm11
+  DB  102,69,15,235,217                   ; por           %xmm9,%xmm11
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  69,15,89,202                        ; mulps         %xmm10,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,69,15,56,43,201                 ; packusdw      %xmm9,%xmm9
+  DB  102,69,15,111,225                   ; movdqa        %xmm9,%xmm12
+  DB  102,65,15,113,244,8                 ; psllw         $0x8,%xmm12
+  DB  102,65,15,113,209,8                 ; psrlw         $0x8,%xmm9
+  DB  102,69,15,235,204                   ; por           %xmm12,%xmm9
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,69,15,56,43,210                 ; packusdw      %xmm10,%xmm10
+  DB  102,69,15,111,226                   ; movdqa        %xmm10,%xmm12
+  DB  102,65,15,113,244,8                 ; psllw         $0x8,%xmm12
+  DB  102,65,15,113,210,8                 ; psrlw         $0x8,%xmm10
+  DB  102,69,15,235,212                   ; por           %xmm12,%xmm10
+  DB  102,69,15,97,195                    ; punpcklwd     %xmm11,%xmm8
+  DB  102,69,15,97,202                    ; punpcklwd     %xmm10,%xmm9
+  DB  102,69,15,111,208                   ; movdqa        %xmm8,%xmm10
+  DB  102,69,15,98,209                    ; punpckldq     %xmm9,%xmm10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,21                              ; jne           4104 <_sk_store_u16_be_sse41+0xe3>
+  DB  69,15,17,20,65                      ; movups        %xmm10,(%r9,%rax,2)
+  DB  102,69,15,106,193                   ; punpckhdq     %xmm9,%xmm8
+  DB  243,69,15,127,68,65,16              ; movdqu        %xmm8,0x10(%r9,%rax,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,69,15,214,20,65                 ; movq          %xmm10,(%r9,%rax,2)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            4100 <_sk_store_u16_be_sse41+0xdf>
+  DB  102,69,15,23,84,65,8                ; movhpd        %xmm10,0x8(%r9,%rax,2)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            4100 <_sk_store_u16_be_sse41+0xdf>
+  DB  102,69,15,106,193                   ; punpckhdq     %xmm9,%xmm8
+  DB  102,69,15,214,68,65,16              ; movq          %xmm8,0x10(%r9,%rax,2)
+  DB  235,213                             ; jmp           4100 <_sk_store_u16_be_sse41+0xdf>
+
+PUBLIC _sk_load_f32_sse41
+_sk_load_f32_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  76,141,12,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r9
+  DB  72,137,208                          ; mov           %rdx,%rax
+  DB  72,193,224,4                        ; shl           $0x4,%rax
+  DB  69,15,16,4,2                        ; movups        (%r10,%rax,1),%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,66                              ; jne           418b <_sk_load_f32_sse41+0x60>
+  DB  67,15,16,68,138,16                  ; movups        0x10(%r10,%r9,4),%xmm0
+  DB  67,15,16,92,138,32                  ; movups        0x20(%r10,%r9,4),%xmm3
+  DB  71,15,16,76,138,48                  ; movups        0x30(%r10,%r9,4),%xmm9
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,20,208                           ; unpcklps      %xmm0,%xmm2
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  65,15,20,201                        ; unpcklps      %xmm9,%xmm1
+  DB  68,15,21,192                        ; unpckhps      %xmm0,%xmm8
+  DB  65,15,21,217                        ; unpckhps      %xmm9,%xmm3
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  15,18,202                           ; movhlps       %xmm2,%xmm1
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  102,15,20,211                       ; unpcklpd      %xmm3,%xmm2
+  DB  65,15,18,216                        ; movhlps       %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,8                               ; jne           419d <_sk_load_f32_sse41+0x72>
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  235,190                             ; jmp           415b <_sk_load_f32_sse41+0x30>
+  DB  67,15,16,68,138,16                  ; movups        0x10(%r10,%r9,4),%xmm0
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,8                               ; jb            41b1 <_sk_load_f32_sse41+0x86>
+  DB  67,15,16,92,138,32                  ; movups        0x20(%r10,%r9,4),%xmm3
+  DB  235,170                             ; jmp           415b <_sk_load_f32_sse41+0x30>
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  235,165                             ; jmp           415b <_sk_load_f32_sse41+0x30>
+
+PUBLIC _sk_store_f32_sse41
+_sk_store_f32_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  76,141,12,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r9
+  DB  72,137,208                          ; mov           %rdx,%rax
+  DB  72,193,224,4                        ; shl           $0x4,%rax
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  68,15,20,201                        ; unpcklps      %xmm1,%xmm9
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,20,195                        ; unpcklps      %xmm3,%xmm8
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  68,15,21,209                        ; unpckhps      %xmm1,%xmm10
+  DB  68,15,40,218                        ; movaps        %xmm2,%xmm11
+  DB  68,15,21,219                        ; unpckhps      %xmm3,%xmm11
+  DB  69,15,40,225                        ; movaps        %xmm9,%xmm12
+  DB  102,69,15,20,224                    ; unpcklpd      %xmm8,%xmm12
+  DB  69,15,18,193                        ; movhlps       %xmm9,%xmm8
+  DB  69,15,40,202                        ; movaps        %xmm10,%xmm9
+  DB  102,69,15,20,203                    ; unpcklpd      %xmm11,%xmm9
+  DB  102,69,15,17,36,2                   ; movupd        %xmm12,(%r10,%rax,1)
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,29                              ; jne           4228 <_sk_store_f32_sse41+0x72>
+  DB  102,69,15,21,211                    ; unpckhpd      %xmm11,%xmm10
+  DB  71,15,17,68,138,16                  ; movups        %xmm8,0x10(%r10,%r9,4)
+  DB  102,71,15,17,76,138,32              ; movupd        %xmm9,0x20(%r10,%r9,4)
+  DB  102,71,15,17,84,138,48              ; movupd        %xmm10,0x30(%r10,%r9,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,246                             ; je            4224 <_sk_store_f32_sse41+0x6e>
+  DB  71,15,17,68,138,16                  ; movups        %xmm8,0x10(%r10,%r9,4)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,234                             ; jb            4224 <_sk_store_f32_sse41+0x6e>
+  DB  102,71,15,17,76,138,32              ; movupd        %xmm9,0x20(%r10,%r9,4)
+  DB  235,225                             ; jmp           4224 <_sk_store_f32_sse41+0x6e>
+
+PUBLIC _sk_clamp_x_sse41
+_sk_clamp_x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  68,15,93,192                        ; minps         %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_y_sse41
+_sk_clamp_y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,193                        ; maxps         %xmm1,%xmm8
+  DB  243,15,16,8                         ; movss         (%rax),%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  68,15,93,193                        ; minps         %xmm1,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_sse41
+_sk_repeat_x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,94,200                        ; divps         %xmm8,%xmm9
+  DB  102,69,15,58,8,201,1                ; roundps       $0x1,%xmm9,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_y_sse41
+_sk_repeat_y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  69,15,94,200                        ; divps         %xmm8,%xmm9
+  DB  102,69,15,58,8,201,1                ; roundps       $0x1,%xmm9,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  65,15,92,201                        ; subps         %xmm9,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_sse41
+_sk_mirror_x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  243,69,15,88,192                    ; addss         %xmm8,%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  69,15,94,208                        ; divps         %xmm8,%xmm10
+  DB  102,69,15,58,8,210,1                ; roundps       $0x1,%xmm10,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,92,194                        ; subps         %xmm10,%xmm0
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,92,192                        ; subps         %xmm0,%xmm8
+  DB  65,15,84,192                        ; andps         %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_y_sse41
+_sk_mirror_y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  65,15,92,201                        ; subps         %xmm9,%xmm1
+  DB  243,69,15,88,192                    ; addss         %xmm8,%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,94,208                        ; divps         %xmm8,%xmm10
+  DB  102,69,15,58,8,210,1                ; roundps       $0x1,%xmm10,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,92,202                        ; subps         %xmm10,%xmm1
+  DB  65,15,92,201                        ; subps         %xmm9,%xmm1
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,92,193                        ; subps         %xmm1,%xmm8
+  DB  65,15,84,200                        ; andps         %xmm8,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_x_1_sse41
+_sk_clamp_x_1_sse41 LABEL PROC
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  68,15,93,5,81,28,0,0                ; minps         0x1c51(%rip),%xmm8        # 5fc0 <_sk_callback_sse41+0xf26>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_1_sse41
+_sk_repeat_x_1_sse41 LABEL PROC
+  DB  102,68,15,58,8,192,1                ; roundps       $0x1,%xmm0,%xmm8
+  DB  65,15,92,192                        ; subps         %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_1_sse41
+_sk_mirror_x_1_sse41 LABEL PROC
+  DB  68,15,40,5,66,28,0,0                ; movaps        0x1c42(%rip),%xmm8        # 5fd0 <_sk_callback_sse41+0xf36>
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,13,70,28,0,0               ; movaps        0x1c46(%rip),%xmm9        # 5fe0 <_sk_callback_sse41+0xf46>
+  DB  68,15,89,200                        ; mulps         %xmm0,%xmm9
+  DB  102,69,15,58,8,201,1                ; roundps       $0x1,%xmm9,%xmm9
+  DB  69,15,88,201                        ; addps         %xmm9,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,92,192                        ; subps         %xmm0,%xmm8
+  DB  65,15,84,192                        ; andps         %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminance_to_alpha_sse41
+_sk_luminance_to_alpha_sse41 LABEL PROC
+  DB  15,40,218                           ; movaps        %xmm2,%xmm3
+  DB  15,89,5,37,28,0,0                   ; mulps         0x1c25(%rip),%xmm0        # 5ff0 <_sk_callback_sse41+0xf56>
+  DB  15,89,13,46,28,0,0                  ; mulps         0x1c2e(%rip),%xmm1        # 6000 <_sk_callback_sse41+0xf66>
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  15,89,29,52,28,0,0                  ; mulps         0x1c34(%rip),%xmm3        # 6010 <_sk_callback_sse41+0xf76>
+  DB  15,88,217                           ; addps         %xmm1,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_2x3_sse41
+_sk_matrix_2x3_sse41 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,16                  ; movss         0x10(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,68,15,16,80,12                  ; movss         0xc(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,20                  ; movss         0x14(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_3x4_sse41
+_sk_matrix_3x4_sse41 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,68,15,16,80,12                  ; movss         0xc(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,24                  ; movss         0x18(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,36                  ; movss         0x24(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,28                  ; movss         0x1c(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,40                  ; movss         0x28(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,20                  ; movss         0x14(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,32                  ; movss         0x20(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,44                 ; movss         0x2c(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  68,15,89,226                        ; mulps         %xmm2,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,217                        ; mulps         %xmm9,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x5_sse41
+_sk_matrix_4x5_sse41 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,32                  ; movss         0x20(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,48                  ; movss         0x30(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,64                 ; movss         0x40(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,68,15,16,80,20                  ; movss         0x14(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,36                  ; movss         0x24(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,52                  ; movss         0x34(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,68                 ; movss         0x44(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,24                  ; movss         0x18(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,40                  ; movss         0x28(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,56                 ; movss         0x38(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  243,68,15,16,112,72                 ; movss         0x48(%rax),%xmm14
+  DB  69,15,198,246,0                     ; shufps        $0x0,%xmm14,%xmm14
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,238                        ; addps         %xmm14,%xmm13
+  DB  68,15,89,226                        ; mulps         %xmm2,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,217                        ; mulps         %xmm9,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  243,68,15,16,88,12                  ; movss         0xc(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,28                  ; movss         0x1c(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,44                 ; movss         0x2c(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  243,68,15,16,112,60                 ; movss         0x3c(%rax),%xmm14
+  DB  69,15,198,246,0                     ; shufps        $0x0,%xmm14,%xmm14
+  DB  243,68,15,16,120,76                 ; movss         0x4c(%rax),%xmm15
+  DB  69,15,198,255,0                     ; shufps        $0x0,%xmm15,%xmm15
+  DB  68,15,89,243                        ; mulps         %xmm3,%xmm14
+  DB  69,15,88,247                        ; addps         %xmm15,%xmm14
+  DB  68,15,89,234                        ; mulps         %xmm2,%xmm13
+  DB  69,15,88,238                        ; addps         %xmm14,%xmm13
+  DB  69,15,89,225                        ; mulps         %xmm9,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,40,219                        ; movaps        %xmm11,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x3_sse41
+_sk_matrix_4x3_sse41 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,15,16,80,16                     ; movss         0x10(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,32                     ; movss         0x20(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,194                           ; addps         %xmm2,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,20                     ; movss         0x14(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,36                     ; movss         0x24(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,24                     ; movss         0x18(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  243,68,15,16,80,40                  ; movss         0x28(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  65,15,89,217                        ; mulps         %xmm9,%xmm3
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  243,15,16,88,12                     ; movss         0xc(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  243,68,15,16,80,28                  ; movss         0x1c(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,44                  ; movss         0x2c(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_perspective_sse41
+_sk_matrix_perspective_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,68,15,16,72,4                   ; movss         0x4(%rax),%xmm9
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,193                        ; addps         %xmm9,%xmm0
+  DB  243,68,15,16,72,12                  ; movss         0xc(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,20                  ; movss         0x14(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  243,68,15,16,80,24                  ; movss         0x18(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,28                  ; movss         0x1c(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,32                  ; movss         0x20(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  68,15,89,217                        ; mulps         %xmm1,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,83,202                        ; rcpps         %xmm10,%xmm1
+  DB  15,89,193                           ; mulps         %xmm1,%xmm0
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_gradient_sse41
+_sk_evenly_spaced_gradient_sse41 LABEL PROC
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,24                           ; mov           (%rax),%rbx
+  DB  76,139,112,8                        ; mov           0x8(%rax),%r14
+  DB  72,255,203                          ; dec           %rbx
+  DB  120,7                               ; js            481a <_sk_evenly_spaced_gradient_sse41+0x1a>
+  DB  243,72,15,42,203                    ; cvtsi2ss      %rbx,%xmm1
+  DB  235,21                              ; jmp           482f <_sk_evenly_spaced_gradient_sse41+0x2f>
+  DB  73,137,217                          ; mov           %rbx,%r9
+  DB  73,209,233                          ; shr           %r9
+  DB  131,227,1                           ; and           $0x1,%ebx
+  DB  76,9,203                            ; or            %r9,%rbx
+  DB  243,72,15,42,203                    ; cvtsi2ss      %rbx,%xmm1
+  DB  243,15,88,201                       ; addss         %xmm1,%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  15,89,200                           ; mulps         %xmm0,%xmm1
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,73,15,58,22,201,1               ; pextrq        $0x1,%xmm1,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  102,73,15,126,203                   ; movq          %xmm1,%r11
+  DB  69,137,223                          ; mov           %r11d,%r15d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,71,15,16,4,190                  ; movss         (%r14,%r15,4),%xmm8
+  DB  102,71,15,58,33,4,158,16            ; insertps      $0x10,(%r14,%r11,4),%xmm8
+  DB  243,67,15,16,12,150                 ; movss         (%r14,%r10,4),%xmm1
+  DB  102,68,15,58,33,193,32              ; insertps      $0x20,%xmm1,%xmm8
+  DB  243,67,15,16,12,142                 ; movss         (%r14,%r9,4),%xmm1
+  DB  102,68,15,58,33,193,48              ; insertps      $0x30,%xmm1,%xmm8
+  DB  72,139,88,40                        ; mov           0x28(%rax),%rbx
+  DB  243,70,15,16,12,187                 ; movss         (%rbx,%r15,4),%xmm9
+  DB  102,70,15,58,33,12,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm9
+  DB  243,66,15,16,12,147                 ; movss         (%rbx,%r10,4),%xmm1
+  DB  102,68,15,58,33,201,32              ; insertps      $0x20,%xmm1,%xmm9
+  DB  243,66,15,16,12,139                 ; movss         (%rbx,%r9,4),%xmm1
+  DB  102,68,15,58,33,201,48              ; insertps      $0x30,%xmm1,%xmm9
+  DB  72,139,88,16                        ; mov           0x10(%rax),%rbx
+  DB  243,66,15,16,12,187                 ; movss         (%rbx,%r15,4),%xmm1
+  DB  102,66,15,58,33,12,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm1
+  DB  243,66,15,16,20,147                 ; movss         (%rbx,%r10,4),%xmm2
+  DB  102,15,58,33,202,32                 ; insertps      $0x20,%xmm2,%xmm1
+  DB  243,66,15,16,20,139                 ; movss         (%rbx,%r9,4),%xmm2
+  DB  102,15,58,33,202,48                 ; insertps      $0x30,%xmm2,%xmm1
+  DB  72,139,88,48                        ; mov           0x30(%rax),%rbx
+  DB  243,70,15,16,20,187                 ; movss         (%rbx,%r15,4),%xmm10
+  DB  102,70,15,58,33,20,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm10
+  DB  243,66,15,16,20,147                 ; movss         (%rbx,%r10,4),%xmm2
+  DB  102,68,15,58,33,210,32              ; insertps      $0x20,%xmm2,%xmm10
+  DB  243,66,15,16,20,139                 ; movss         (%rbx,%r9,4),%xmm2
+  DB  102,68,15,58,33,210,48              ; insertps      $0x30,%xmm2,%xmm10
+  DB  72,139,88,24                        ; mov           0x18(%rax),%rbx
+  DB  243,66,15,16,20,187                 ; movss         (%rbx,%r15,4),%xmm2
+  DB  102,66,15,58,33,20,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm2
+  DB  243,66,15,16,28,147                 ; movss         (%rbx,%r10,4),%xmm3
+  DB  102,15,58,33,211,32                 ; insertps      $0x20,%xmm3,%xmm2
+  DB  243,66,15,16,28,139                 ; movss         (%rbx,%r9,4),%xmm3
+  DB  102,15,58,33,211,48                 ; insertps      $0x30,%xmm3,%xmm2
+  DB  72,139,88,56                        ; mov           0x38(%rax),%rbx
+  DB  243,70,15,16,28,187                 ; movss         (%rbx,%r15,4),%xmm11
+  DB  102,70,15,58,33,28,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm11
+  DB  243,66,15,16,28,147                 ; movss         (%rbx,%r10,4),%xmm3
+  DB  102,68,15,58,33,219,32              ; insertps      $0x20,%xmm3,%xmm11
+  DB  243,66,15,16,28,139                 ; movss         (%rbx,%r9,4),%xmm3
+  DB  102,68,15,58,33,219,48              ; insertps      $0x30,%xmm3,%xmm11
+  DB  72,139,88,32                        ; mov           0x20(%rax),%rbx
+  DB  243,66,15,16,28,187                 ; movss         (%rbx,%r15,4),%xmm3
+  DB  102,66,15,58,33,28,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm3
+  DB  243,70,15,16,36,147                 ; movss         (%rbx,%r10,4),%xmm12
+  DB  102,65,15,58,33,220,32              ; insertps      $0x20,%xmm12,%xmm3
+  DB  243,70,15,16,36,139                 ; movss         (%rbx,%r9,4),%xmm12
+  DB  102,65,15,58,33,220,48              ; insertps      $0x30,%xmm12,%xmm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  243,70,15,16,36,184                 ; movss         (%rax,%r15,4),%xmm12
+  DB  102,70,15,58,33,36,152,16           ; insertps      $0x10,(%rax,%r11,4),%xmm12
+  DB  243,70,15,16,44,144                 ; movss         (%rax,%r10,4),%xmm13
+  DB  102,69,15,58,33,229,32              ; insertps      $0x20,%xmm13,%xmm12
+  DB  243,70,15,16,44,136                 ; movss         (%rax,%r9,4),%xmm13
+  DB  102,69,15,58,33,229,48              ; insertps      $0x30,%xmm13,%xmm12
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  15,89,200                           ; mulps         %xmm0,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  15,89,208                           ; mulps         %xmm0,%xmm2
+  DB  65,15,88,211                        ; addps         %xmm11,%xmm2
+  DB  15,89,216                           ; mulps         %xmm0,%xmm3
+  DB  65,15,88,220                        ; addps         %xmm12,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gauss_a_to_rgba_sse41
+_sk_gauss_a_to_rgba_sse41 LABEL PROC
+  DB  15,40,5,67,22,0,0                   ; movaps        0x1643(%rip),%xmm0        # 6020 <_sk_callback_sse41+0xf86>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,73,22,0,0                   ; addps         0x1649(%rip),%xmm0        # 6030 <_sk_callback_sse41+0xf96>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,79,22,0,0                   ; addps         0x164f(%rip),%xmm0        # 6040 <_sk_callback_sse41+0xfa6>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,85,22,0,0                   ; addps         0x1655(%rip),%xmm0        # 6050 <_sk_callback_sse41+0xfb6>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,91,22,0,0                   ; addps         0x165b(%rip),%xmm0        # 6060 <_sk_callback_sse41+0xfc6>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gradient_sse41
+_sk_gradient_sse41 LABEL PROC
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,249,2                        ; cmp           $0x2,%r9
+  DB  114,50                              ; jb            4a58 <_sk_gradient_sse41+0x46>
+  DB  72,139,88,72                        ; mov           0x48(%rax),%rbx
+  DB  73,255,201                          ; dec           %r9
+  DB  72,131,195,4                        ; add           $0x4,%rbx
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  15,40,21,52,22,0,0                  ; movaps        0x1634(%rip),%xmm2        # 6070 <_sk_callback_sse41+0xfd6>
+  DB  243,15,16,27                        ; movss         (%rbx),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  15,194,216,2                        ; cmpleps       %xmm0,%xmm3
+  DB  15,84,218                           ; andps         %xmm2,%xmm3
+  DB  102,15,254,203                      ; paddd         %xmm3,%xmm1
+  DB  72,131,195,4                        ; add           $0x4,%rbx
+  DB  73,255,201                          ; dec           %r9
+  DB  117,228                             ; jne           4a3c <_sk_gradient_sse41+0x2a>
+  DB  102,73,15,58,22,201,1               ; pextrq        $0x1,%xmm1,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  102,73,15,126,203                   ; movq          %xmm1,%r11
+  DB  69,137,222                          ; mov           %r11d,%r14d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,70,15,16,4,179                  ; movss         (%rbx,%r14,4),%xmm8
+  DB  102,70,15,58,33,4,155,16            ; insertps      $0x10,(%rbx,%r11,4),%xmm8
+  DB  243,66,15,16,12,147                 ; movss         (%rbx,%r10,4),%xmm1
+  DB  102,68,15,58,33,193,32              ; insertps      $0x20,%xmm1,%xmm8
+  DB  243,66,15,16,12,139                 ; movss         (%rbx,%r9,4),%xmm1
+  DB  102,68,15,58,33,193,48              ; insertps      $0x30,%xmm1,%xmm8
+  DB  72,139,88,40                        ; mov           0x28(%rax),%rbx
+  DB  243,70,15,16,12,179                 ; movss         (%rbx,%r14,4),%xmm9
+  DB  102,70,15,58,33,12,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm9
+  DB  243,66,15,16,12,147                 ; movss         (%rbx,%r10,4),%xmm1
+  DB  102,68,15,58,33,201,32              ; insertps      $0x20,%xmm1,%xmm9
+  DB  243,66,15,16,12,139                 ; movss         (%rbx,%r9,4),%xmm1
+  DB  102,68,15,58,33,201,48              ; insertps      $0x30,%xmm1,%xmm9
+  DB  243,67,15,16,12,183                 ; movss         (%r15,%r14,4),%xmm1
+  DB  102,67,15,58,33,12,159,16           ; insertps      $0x10,(%r15,%r11,4),%xmm1
+  DB  243,67,15,16,20,151                 ; movss         (%r15,%r10,4),%xmm2
+  DB  102,15,58,33,202,32                 ; insertps      $0x20,%xmm2,%xmm1
+  DB  243,67,15,16,20,143                 ; movss         (%r15,%r9,4),%xmm2
+  DB  102,15,58,33,202,48                 ; insertps      $0x30,%xmm2,%xmm1
+  DB  72,139,88,48                        ; mov           0x30(%rax),%rbx
+  DB  243,70,15,16,20,179                 ; movss         (%rbx,%r14,4),%xmm10
+  DB  102,70,15,58,33,20,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm10
+  DB  243,66,15,16,20,147                 ; movss         (%rbx,%r10,4),%xmm2
+  DB  102,68,15,58,33,210,32              ; insertps      $0x20,%xmm2,%xmm10
+  DB  243,66,15,16,20,139                 ; movss         (%rbx,%r9,4),%xmm2
+  DB  102,68,15,58,33,210,48              ; insertps      $0x30,%xmm2,%xmm10
+  DB  72,139,88,24                        ; mov           0x18(%rax),%rbx
+  DB  243,66,15,16,20,179                 ; movss         (%rbx,%r14,4),%xmm2
+  DB  102,66,15,58,33,20,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm2
+  DB  243,66,15,16,28,147                 ; movss         (%rbx,%r10,4),%xmm3
+  DB  102,15,58,33,211,32                 ; insertps      $0x20,%xmm3,%xmm2
+  DB  243,66,15,16,28,139                 ; movss         (%rbx,%r9,4),%xmm3
+  DB  102,15,58,33,211,48                 ; insertps      $0x30,%xmm3,%xmm2
+  DB  72,139,88,56                        ; mov           0x38(%rax),%rbx
+  DB  243,70,15,16,28,179                 ; movss         (%rbx,%r14,4),%xmm11
+  DB  102,70,15,58,33,28,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm11
+  DB  243,66,15,16,28,147                 ; movss         (%rbx,%r10,4),%xmm3
+  DB  102,68,15,58,33,219,32              ; insertps      $0x20,%xmm3,%xmm11
+  DB  243,66,15,16,28,139                 ; movss         (%rbx,%r9,4),%xmm3
+  DB  102,68,15,58,33,219,48              ; insertps      $0x30,%xmm3,%xmm11
+  DB  72,139,88,32                        ; mov           0x20(%rax),%rbx
+  DB  243,66,15,16,28,179                 ; movss         (%rbx,%r14,4),%xmm3
+  DB  102,66,15,58,33,28,155,16           ; insertps      $0x10,(%rbx,%r11,4),%xmm3
+  DB  243,70,15,16,36,147                 ; movss         (%rbx,%r10,4),%xmm12
+  DB  102,65,15,58,33,220,32              ; insertps      $0x20,%xmm12,%xmm3
+  DB  243,70,15,16,36,139                 ; movss         (%rbx,%r9,4),%xmm12
+  DB  102,65,15,58,33,220,48              ; insertps      $0x30,%xmm12,%xmm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  243,70,15,16,36,176                 ; movss         (%rax,%r14,4),%xmm12
+  DB  102,70,15,58,33,36,152,16           ; insertps      $0x10,(%rax,%r11,4),%xmm12
+  DB  243,70,15,16,44,144                 ; movss         (%rax,%r10,4),%xmm13
+  DB  102,69,15,58,33,229,32              ; insertps      $0x20,%xmm13,%xmm12
+  DB  243,70,15,16,44,136                 ; movss         (%rax,%r9,4),%xmm13
+  DB  102,69,15,58,33,229,48              ; insertps      $0x30,%xmm13,%xmm12
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  15,89,200                           ; mulps         %xmm0,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  15,89,208                           ; mulps         %xmm0,%xmm2
+  DB  65,15,88,211                        ; addps         %xmm11,%xmm2
+  DB  15,89,216                           ; mulps         %xmm0,%xmm3
+  DB  65,15,88,220                        ; addps         %xmm12,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_2_stop_gradient_sse41
+_sk_evenly_spaced_2_stop_gradient_sse41 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,15,16,80,16                     ; movss         0x10(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,194                           ; addps         %xmm2,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,20                     ; movss         0x14(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,24                     ; movss         0x18(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  243,15,16,88,12                     ; movss         0xc(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  243,68,15,16,72,28                  ; movss         0x1c(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_unit_angle_sse41
+_sk_xy_to_unit_angle_sse41 LABEL PROC
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,84,193                        ; andps         %xmm9,%xmm0
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  68,15,92,217                        ; subps         %xmm1,%xmm11
+  DB  68,15,84,217                        ; andps         %xmm1,%xmm11
+  DB  68,15,40,224                        ; movaps        %xmm0,%xmm12
+  DB  69,15,93,227                        ; minps         %xmm11,%xmm12
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  69,15,95,211                        ; maxps         %xmm11,%xmm10
+  DB  69,15,94,226                        ; divps         %xmm10,%xmm12
+  DB  69,15,40,236                        ; movaps        %xmm12,%xmm13
+  DB  69,15,89,237                        ; mulps         %xmm13,%xmm13
+  DB  68,15,40,21,213,19,0,0              ; movaps        0x13d5(%rip),%xmm10        # 6080 <_sk_callback_sse41+0xfe6>
+  DB  69,15,89,213                        ; mulps         %xmm13,%xmm10
+  DB  68,15,88,21,217,19,0,0              ; addps         0x13d9(%rip),%xmm10        # 6090 <_sk_callback_sse41+0xff6>
+  DB  69,15,89,213                        ; mulps         %xmm13,%xmm10
+  DB  68,15,88,21,221,19,0,0              ; addps         0x13dd(%rip),%xmm10        # 60a0 <_sk_callback_sse41+0x1006>
+  DB  69,15,89,213                        ; mulps         %xmm13,%xmm10
+  DB  68,15,88,21,225,19,0,0              ; addps         0x13e1(%rip),%xmm10        # 60b0 <_sk_callback_sse41+0x1016>
+  DB  69,15,89,212                        ; mulps         %xmm12,%xmm10
+  DB  65,15,194,195,1                     ; cmpltps       %xmm11,%xmm0
+  DB  68,15,40,29,224,19,0,0              ; movaps        0x13e0(%rip),%xmm11        # 60c0 <_sk_callback_sse41+0x1026>
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  102,69,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm10
+  DB  69,15,194,200,1                     ; cmpltps       %xmm8,%xmm9
+  DB  68,15,40,29,217,19,0,0              ; movaps        0x13d9(%rip),%xmm11        # 60d0 <_sk_callback_sse41+0x1036>
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  102,69,15,56,20,211                 ; blendvps      %xmm0,%xmm11,%xmm10
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,194,192,1                     ; cmpltps       %xmm8,%xmm0
+  DB  68,15,40,13,203,19,0,0              ; movaps        0x13cb(%rip),%xmm9        # 60e0 <_sk_callback_sse41+0x1046>
+  DB  69,15,92,202                        ; subps         %xmm10,%xmm9
+  DB  102,69,15,56,20,209                 ; blendvps      %xmm0,%xmm9,%xmm10
+  DB  69,15,194,194,7                     ; cmpordps      %xmm10,%xmm8
+  DB  69,15,84,194                        ; andps         %xmm10,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_radius_sse41
+_sk_xy_to_radius_sse41 LABEL PROC
+  DB  15,89,192                           ; mulps         %xmm0,%xmm0
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  69,15,89,192                        ; mulps         %xmm8,%xmm8
+  DB  68,15,88,192                        ; addps         %xmm0,%xmm8
+  DB  65,15,81,192                        ; sqrtps        %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_save_xy_sse41
+_sk_save_xy_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,159,19,0,0               ; movaps        0x139f(%rip),%xmm8        # 60f0 <_sk_callback_sse41+0x1056>
+  DB  15,17,0                             ; movups        %xmm0,(%rax)
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,88,200                        ; addps         %xmm8,%xmm9
+  DB  102,69,15,58,8,209,1                ; roundps       $0x1,%xmm9,%xmm10
+  DB  69,15,92,202                        ; subps         %xmm10,%xmm9
+  DB  68,15,88,193                        ; addps         %xmm1,%xmm8
+  DB  102,69,15,58,8,208,1                ; roundps       $0x1,%xmm8,%xmm10
+  DB  69,15,92,194                        ; subps         %xmm10,%xmm8
+  DB  15,17,72,32                         ; movups        %xmm1,0x20(%rax)
+  DB  68,15,17,72,64                      ; movups        %xmm9,0x40(%rax)
+  DB  68,15,17,64,96                      ; movups        %xmm8,0x60(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_accumulate_sse41
+_sk_accumulate_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,16,128,128,0,0,0              ; movups        0x80(%rax),%xmm8
+  DB  68,15,16,136,160,0,0,0              ; movups        0xa0(%rax),%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  65,15,88,224                        ; addps         %xmm8,%xmm4
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,193                        ; mulps         %xmm1,%xmm8
+  DB  65,15,88,232                        ; addps         %xmm8,%xmm5
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  65,15,88,240                        ; addps         %xmm8,%xmm6
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  65,15,88,249                        ; addps         %xmm9,%xmm7
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_nx_sse41
+_sk_bilinear_nx_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,33,19,0,0                   ; addps         0x1321(%rip),%xmm0        # 6100 <_sk_callback_sse41+0x1066>
+  DB  68,15,40,13,41,19,0,0               ; movaps        0x1329(%rip),%xmm9        # 6110 <_sk_callback_sse41+0x1076>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,17,136,128,0,0,0              ; movups        %xmm9,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_px_sse41
+_sk_bilinear_px_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,24,19,0,0                   ; addps         0x1318(%rip),%xmm0        # 6120 <_sk_callback_sse41+0x1086>
+  DB  68,15,17,128,128,0,0,0              ; movups        %xmm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_ny_sse41
+_sk_bilinear_ny_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,10,19,0,0                  ; addps         0x130a(%rip),%xmm1        # 6130 <_sk_callback_sse41+0x1096>
+  DB  68,15,40,13,18,19,0,0               ; movaps        0x1312(%rip),%xmm9        # 6140 <_sk_callback_sse41+0x10a6>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,17,136,160,0,0,0              ; movups        %xmm9,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_py_sse41
+_sk_bilinear_py_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,0,19,0,0                   ; addps         0x1300(%rip),%xmm1        # 6150 <_sk_callback_sse41+0x10b6>
+  DB  68,15,17,128,160,0,0,0              ; movups        %xmm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3x_sse41
+_sk_bicubic_n3x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,243,18,0,0                  ; addps         0x12f3(%rip),%xmm0        # 6160 <_sk_callback_sse41+0x10c6>
+  DB  68,15,40,13,251,18,0,0              ; movaps        0x12fb(%rip),%xmm9        # 6170 <_sk_callback_sse41+0x10d6>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  69,15,89,192                        ; mulps         %xmm8,%xmm8
+  DB  68,15,89,13,247,18,0,0              ; mulps         0x12f7(%rip),%xmm9        # 6180 <_sk_callback_sse41+0x10e6>
+  DB  68,15,88,13,255,18,0,0              ; addps         0x12ff(%rip),%xmm9        # 6190 <_sk_callback_sse41+0x10f6>
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  68,15,17,136,128,0,0,0              ; movups        %xmm9,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1x_sse41
+_sk_bicubic_n1x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,238,18,0,0                  ; addps         0x12ee(%rip),%xmm0        # 61a0 <_sk_callback_sse41+0x1106>
+  DB  68,15,40,13,246,18,0,0              ; movaps        0x12f6(%rip),%xmm9        # 61b0 <_sk_callback_sse41+0x1116>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,40,5,250,18,0,0               ; movaps        0x12fa(%rip),%xmm8        # 61c0 <_sk_callback_sse41+0x1126>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,254,18,0,0               ; addps         0x12fe(%rip),%xmm8        # 61d0 <_sk_callback_sse41+0x1136>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,2,19,0,0                 ; addps         0x1302(%rip),%xmm8        # 61e0 <_sk_callback_sse41+0x1146>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,6,19,0,0                 ; addps         0x1306(%rip),%xmm8        # 61f0 <_sk_callback_sse41+0x1156>
+  DB  68,15,17,128,128,0,0,0              ; movups        %xmm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1x_sse41
+_sk_bicubic_p1x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,0,19,0,0                 ; movaps        0x1300(%rip),%xmm8        # 6200 <_sk_callback_sse41+0x1166>
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,72,64                      ; movups        0x40(%rax),%xmm9
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,21,252,18,0,0              ; movaps        0x12fc(%rip),%xmm10        # 6210 <_sk_callback_sse41+0x1176>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,0,19,0,0                ; addps         0x1300(%rip),%xmm10        # 6220 <_sk_callback_sse41+0x1186>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,208                        ; addps         %xmm8,%xmm10
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,252,18,0,0              ; addps         0x12fc(%rip),%xmm10        # 6230 <_sk_callback_sse41+0x1196>
+  DB  68,15,17,144,128,0,0,0              ; movups        %xmm10,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3x_sse41
+_sk_bicubic_p3x_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,239,18,0,0                  ; addps         0x12ef(%rip),%xmm0        # 6240 <_sk_callback_sse41+0x11a6>
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,89,201                        ; mulps         %xmm9,%xmm9
+  DB  68,15,89,5,239,18,0,0               ; mulps         0x12ef(%rip),%xmm8        # 6250 <_sk_callback_sse41+0x11b6>
+  DB  68,15,88,5,247,18,0,0               ; addps         0x12f7(%rip),%xmm8        # 6260 <_sk_callback_sse41+0x11c6>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,17,128,128,0,0,0              ; movups        %xmm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3y_sse41
+_sk_bicubic_n3y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,229,18,0,0                 ; addps         0x12e5(%rip),%xmm1        # 6270 <_sk_callback_sse41+0x11d6>
+  DB  68,15,40,13,237,18,0,0              ; movaps        0x12ed(%rip),%xmm9        # 6280 <_sk_callback_sse41+0x11e6>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  69,15,89,192                        ; mulps         %xmm8,%xmm8
+  DB  68,15,89,13,233,18,0,0              ; mulps         0x12e9(%rip),%xmm9        # 6290 <_sk_callback_sse41+0x11f6>
+  DB  68,15,88,13,241,18,0,0              ; addps         0x12f1(%rip),%xmm9        # 62a0 <_sk_callback_sse41+0x1206>
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  68,15,17,136,160,0,0,0              ; movups        %xmm9,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1y_sse41
+_sk_bicubic_n1y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,223,18,0,0                 ; addps         0x12df(%rip),%xmm1        # 62b0 <_sk_callback_sse41+0x1216>
+  DB  68,15,40,13,231,18,0,0              ; movaps        0x12e7(%rip),%xmm9        # 62c0 <_sk_callback_sse41+0x1226>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,40,5,235,18,0,0               ; movaps        0x12eb(%rip),%xmm8        # 62d0 <_sk_callback_sse41+0x1236>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,239,18,0,0               ; addps         0x12ef(%rip),%xmm8        # 62e0 <_sk_callback_sse41+0x1246>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,243,18,0,0               ; addps         0x12f3(%rip),%xmm8        # 62f0 <_sk_callback_sse41+0x1256>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,247,18,0,0               ; addps         0x12f7(%rip),%xmm8        # 6300 <_sk_callback_sse41+0x1266>
+  DB  68,15,17,128,160,0,0,0              ; movups        %xmm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1y_sse41
+_sk_bicubic_p1y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,241,18,0,0               ; movaps        0x12f1(%rip),%xmm8        # 6310 <_sk_callback_sse41+0x1276>
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,72,96                      ; movups        0x60(%rax),%xmm9
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  68,15,40,21,236,18,0,0              ; movaps        0x12ec(%rip),%xmm10        # 6320 <_sk_callback_sse41+0x1286>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,240,18,0,0              ; addps         0x12f0(%rip),%xmm10        # 6330 <_sk_callback_sse41+0x1296>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,208                        ; addps         %xmm8,%xmm10
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,236,18,0,0              ; addps         0x12ec(%rip),%xmm10        # 6340 <_sk_callback_sse41+0x12a6>
+  DB  68,15,17,144,160,0,0,0              ; movups        %xmm10,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3y_sse41
+_sk_bicubic_p3y_sse41 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,222,18,0,0                 ; addps         0x12de(%rip),%xmm1        # 6350 <_sk_callback_sse41+0x12b6>
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,89,201                        ; mulps         %xmm9,%xmm9
+  DB  68,15,89,5,222,18,0,0               ; mulps         0x12de(%rip),%xmm8        # 6360 <_sk_callback_sse41+0x12c6>
+  DB  68,15,88,5,230,18,0,0               ; addps         0x12e6(%rip),%xmm8        # 6370 <_sk_callback_sse41+0x12d6>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,17,128,160,0,0,0              ; movups        %xmm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_callback_sse41
+_sk_callback_sse41 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,131,236,32                       ; sub           $0x20,%rsp
+  DB  68,15,40,197                        ; movaps        %xmm5,%xmm8
+  DB  68,15,40,204                        ; movaps        %xmm4,%xmm9
+  DB  77,137,196                          ; mov           %r8,%r12
+  DB  73,137,206                          ; mov           %rcx,%r14
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,137,195                          ; mov           %rax,%rbx
+  DB  15,40,224                           ; movaps        %xmm0,%xmm4
+  DB  15,20,225                           ; unpcklps      %xmm1,%xmm4
+  DB  15,40,234                           ; movaps        %xmm2,%xmm5
+  DB  15,20,235                           ; unpcklps      %xmm3,%xmm5
+  DB  15,21,193                           ; unpckhps      %xmm1,%xmm0
+  DB  15,21,211                           ; unpckhps      %xmm3,%xmm2
+  DB  15,40,204                           ; movaps        %xmm4,%xmm1
+  DB  102,15,20,205                       ; unpcklpd      %xmm5,%xmm1
+  DB  15,18,236                           ; movhlps       %xmm4,%xmm5
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  102,15,20,218                       ; unpcklpd      %xmm2,%xmm3
+  DB  102,15,17,75,8                      ; movupd        %xmm1,0x8(%rbx)
+  DB  15,18,208                           ; movhlps       %xmm0,%xmm2
+  DB  15,17,107,24                        ; movups        %xmm5,0x18(%rbx)
+  DB  102,15,17,91,40                     ; movupd        %xmm3,0x28(%rbx)
+  DB  15,17,83,56                         ; movups        %xmm2,0x38(%rbx)
+  DB  77,133,228                          ; test          %r12,%r12
+  DB  186,4,0,0,0                         ; mov           $0x4,%edx
+  DB  65,15,69,212                        ; cmovne        %r12d,%edx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  255,19                              ; callq         *(%rbx)
+  DB  72,139,131,136,0,0,0                ; mov           0x88(%rbx),%rax
+  DB  15,16,32                            ; movups        (%rax),%xmm4
+  DB  15,16,64,16                         ; movups        0x10(%rax),%xmm0
+  DB  15,16,88,32                         ; movups        0x20(%rax),%xmm3
+  DB  15,16,80,48                         ; movups        0x30(%rax),%xmm2
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,20,232                           ; unpcklps      %xmm0,%xmm5
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  15,21,224                           ; unpckhps      %xmm0,%xmm4
+  DB  15,21,218                           ; unpckhps      %xmm2,%xmm3
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  15,18,205                           ; movhlps       %xmm5,%xmm1
+  DB  15,40,212                           ; movaps        %xmm4,%xmm2
+  DB  102,15,20,211                       ; unpcklpd      %xmm3,%xmm2
+  DB  15,18,220                           ; movhlps       %xmm4,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  76,137,241                          ; mov           %r14,%rcx
+  DB  77,137,224                          ; mov           %r12,%r8
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  65,15,40,232                        ; movaps        %xmm8,%xmm5
+  DB  72,131,196,32                       ; add           $0x20,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+ALIGN 16
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,1                            ; cmpb          $0x1,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  252                                 ; cld
+  DB  190,0,0,252,190                     ; mov           $0xbefc0000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  252                                 ; cld
+  DB  190,0,0,252,190                     ; mov           $0xbefc0000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  191,0,0,128,191                     ; mov           $0xbf800000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,191,0,0,128,191,0               ; cmpb          $0x0,-0x40800000(%rdi)
+  DB  0,224                               ; add           %ah,%al
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  224,64                              ; loopne        5348 <.literal16+0x1d8>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,64                              ; loopne        534c <.literal16+0x1dc>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,64                              ; loopne        5350 <.literal16+0x1e0>
+  DB  154                                 ; (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5371 <.literal16+0x201>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5375 <.literal16+0x205>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5379 <.literal16+0x209>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 537d <.literal16+0x20d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53b1 <.literal16+0x241>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53b5 <.literal16+0x245>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53b9 <.literal16+0x249>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53bd <.literal16+0x24d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53f1 <.literal16+0x281>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53f5 <.literal16+0x285>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53f9 <.literal16+0x289>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 53fd <.literal16+0x28d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5431 <.literal16+0x2c1>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5435 <.literal16+0x2c5>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5439 <.literal16+0x2c9>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 543d <.literal16+0x2cd>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,255                          ; cmpb          $0xff,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,255                               ; add           %bh,%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,255                               ; add           %bh,%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,255                               ; add           %bh,%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a005428 <_sk_callback_sse41+0xa00038e>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,2                ; decl          0x2ffffff(%rip)        # 3005430 <_sk_callback_sse41+0x3000396>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,127                    ; add           %al,0x7f00003f(%rax)
+  DB  67,0,0                              ; rex.XB        add %al,(%r8)
+  DB  127,67                              ; jg            549b <.literal16+0x32b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            549f <.literal16+0x32f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            54a3 <.literal16+0x333>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  145                                 ; xchg          %eax,%ecx
+  DB  131,158,61,145,131,158,61           ; sbbl          $0x3d,-0x617c6ec3(%rsi)
+  DB  145                                 ; xchg          %eax,%ecx
+  DB  131,158,61,145,131,158,61           ; sbbl          $0x3d,-0x617c6ec3(%rsi)
+  DB  154                                 ; (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,92                               ; ds            pop %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,97                               ; rex.RXB       (bad)
+  DB  61,174,71,97,61                     ; cmp           $0x3d6147ae,%eax
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,97                               ; rex.RXB       (bad)
+  DB  61,174,71,97,61                     ; cmp           $0x3d6147ae,%eax
+  DB  82                                  ; push          %rdx
+  DB  184,78,65,82,184                    ; mov           $0xb852414e,%eax
+  DB  78                                  ; rex.WRX
+  DB  65,82                               ; push          %r10
+  DB  184,78,65,82,184                    ; mov           $0xb852414e,%eax
+  DB  78                                  ; rex.WRX
+  DB  65,57,215                           ; cmp           %edx,%r15d
+  DB  32,187,57,215,32,187                ; and           %bh,-0x44df28c7(%rbx)
+  DB  57,215                              ; cmp           %edx,%edi
+  DB  32,187,57,215,32,187                ; and           %bh,-0x44df28c7(%rbx)
+  DB  186,159,98,60,186                   ; mov           $0xba3c629f,%edx
+  DB  159                                 ; lahf
+  DB  98                                  ; (bad)
+  DB  60,186                              ; cmp           $0xba,%al
+  DB  159                                 ; lahf
+  DB  98                                  ; (bad)
+  DB  60,186                              ; cmp           $0xba,%al
+  DB  159                                 ; lahf
+  DB  98                                  ; (bad)
+  DB  60,109                              ; cmp           $0x6d,%al
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  252                                 ; cld
+  DB  191,16,62,252,191                   ; mov           $0xbffc3e10,%edi
+  DB  16,62                               ; adc           %bh,(%rsi)
+  DB  252                                 ; cld
+  DB  191,16,62,252,191                   ; mov           $0xbffc3e10,%edi
+  DB  16,62                               ; adc           %bh,(%rsi)
+  DB  168,177                             ; test          $0xb1,%al
+  DB  152                                 ; cwtl
+  DB  59,168,177,152,59,168               ; cmp           -0x57c4674f(%rax),%ebp
+  DB  177,152                             ; mov           $0x98,%cl
+  DB  59,168,177,152,59,0                 ; cmp           0x3b98b1(%rax),%ebp
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,192                    ; add           %al,-0x3fffffc1(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  0,64,0                              ; add           %al,0x0(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  0,64,0                              ; add           %al,0x0(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,171,170                      ; addb          $0xaa,-0x55(%rax)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,171,170                      ; addb          $0xaa,-0x55(%rax)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,255,0,0,0                ; addb          $0x0,0xff3b(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,248,0,0                ; addb          $0x0,0xf8003b(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        5679 <.literal16+0x509>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        567d <.literal16+0x50d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5681 <.literal16+0x511>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5685 <.literal16+0x515>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,8,33,4,61                   ; and           %eax,0x3d042108(,%rdi,1)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  1,255                               ; add           %edi,%edi
+  DB  255                                 ; (bad)
+  DB  255,5,255,255,255,9                 ; incl          0x9ffffff(%rip)        # a0056c8 <_sk_callback_sse41+0xa00062e>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,2                ; decl          0x2ffffff(%rip)        # 30056d0 <_sk_callback_sse41+0x3000636>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,129,128,128,59,129              ; incl          -0x7ec47f80(%rcx)
+  DB  128,128,59,129,128,128,59           ; addb          $0x3b,-0x7f7f7ec5(%rax)
+  DB  129,128,128,59,255,0,255,0,255,0    ; addl          $0xff00ff,0xff3b80(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,255                               ; or            %bh,%bh
+  DB  10,255                              ; or            %bh,%bh
+  DB  12,255                              ; or            $0xff,%al
+  DB  14                                  ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,128,55,128                  ; add           %al,-0x7fc88000(%rax)
+  DB  0,128,55,128,0,128                  ; add           %al,-0x7fff7fc9(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,128,10,128,12,128                 ; or            %al,-0x7ff37ff6(%rax)
+  DB  14                                  ; (bad)
+  DB  128,0,0                             ; addb          $0x0,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            579b <.literal16+0x62b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            579f <.literal16+0x62f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            57a3 <.literal16+0x633>
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,129,128,128,59           ; addb          $0x3b,-0x7f7f7ec5(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,0                            ; cmpb          $0x0,(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5794 <.literal16+0x624>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5798 <.literal16+0x628>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            579c <.literal16+0x62c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            57a0 <.literal16+0x630>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            5825 <.literal16+0x6b5>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5789 <.literal16+0x619>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a397ca <_sk_callback_sse41+0xffffffffe9a34730>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  81                                  ; push          %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5864 <.literal16+0x6f4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5868 <.literal16+0x6f8>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            586c <.literal16+0x6fc>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5870 <.literal16+0x700>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            58f5 <.literal16+0x785>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5859 <.literal16+0x6e9>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a3989a <_sk_callback_sse41+0xffffffffe9a34800>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  81                                  ; push          %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5934 <.literal16+0x7c4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5938 <.literal16+0x7c8>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            593c <.literal16+0x7cc>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5940 <.literal16+0x7d0>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            59c5 <.literal16+0x855>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5929 <.literal16+0x7b9>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a3996a <_sk_callback_sse41+0xffffffffe9a348d0>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  81                                  ; push          %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5a04 <.literal16+0x894>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5a08 <.literal16+0x898>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5a0c <.literal16+0x89c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5a10 <.literal16+0x8a0>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            5a95 <.literal16+0x925>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           59f9 <.literal16+0x889>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a39a3a <_sk_callback_sse41+0xffffffffe9a349a0>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  81                                  ; push          %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,200                               ; add           %cl,%al
+  DB  66,0,0                              ; rex.X         add %al,(%rax)
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  127,67                              ; jg            5b17 <.literal16+0x9a7>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5b1b <.literal16+0x9ab>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5b1f <.literal16+0x9af>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5b23 <.literal16+0x9b3>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,65,0,0                          ; addb          $0x0,0x0(%rcx)
+  DB  128,65,0,0                          ; addb          $0x0,0x0(%rcx)
+  DB  128,65,0,0                          ; addb          $0x0,0x0(%rcx)
+  DB  128,65,203,61                       ; addb          $0x3d,-0x35(%rcx)
+  DB  13,60,203,61,13                     ; or            $0xd3dcb3c,%eax
+  DB  60,203                              ; cmp           $0xcb,%al
+  DB  61,13,60,203,61                     ; cmp           $0x3dcb3c0d,%eax
+  DB  13,60,111,18,3                      ; or            $0x3126f3c,%eax
+  DB  59,111,18                           ; cmp           0x12(%rdi),%ebp
+  DB  3,59                                ; add           (%rbx),%edi
+  DB  111                                 ; outsl         %ds:(%rsi),(%dx)
+  DB  18,3                                ; adc           (%rbx),%al
+  DB  59,111,18                           ; cmp           0x12(%rdi),%ebp
+  DB  3,59                                ; add           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  163,59,10,215,163,59,10,215,163     ; movabs        %eax,0xa3d70a3ba3d70a3b
+  DB  59,10                               ; cmp           (%rdx),%ecx
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  163,59,194,24,17,60,194,24,17       ; movabs        %eax,0x1118c23c1118c23b
+  DB  60,194                              ; cmp           $0xc2,%al
+  DB  24,17                               ; sbb           %dl,(%rcx)
+  DB  60,194                              ; cmp           $0xc2,%al
+  DB  24,17                               ; sbb           %dl,(%rcx)
+  DB  60,203                              ; cmp           $0xcb,%al
+  DB  61,13,190,203,61                    ; cmp           $0x3dcbbe0d,%eax
+  DB  13,190,203,61,13                    ; or            $0xd3dcbbe,%eax
+  DB  190,203,61,13,190                   ; mov           $0xbe0d3dcb,%esi
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           5ba3 <.literal16+0xa33>
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           5ba7 <.literal16+0xa37>
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           5bab <.literal16+0xa3b>
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           5baf <.literal16+0xa3f>
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,129,128,128,59           ; addb          $0x3b,-0x7f7f7ec5(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,0                            ; cmpb          $0x0,(%rbx)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5bfb <.literal16+0xa8b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5bff <.literal16+0xa8f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5c03 <.literal16+0xa93>
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,0,128,63               ; addb          $0x3f,-0x7fffffc5(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,0,128,63               ; addb          $0x3f,-0x7fffffc5(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,1,255,255,255            ; addb          $0xff,-0xfec5(%rax)
+  DB  5,255,255,255,9                     ; add           $0x9ffffff,%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,2                ; decl          0x2ffffff(%rip)        # 3005c40 <_sk_callback_sse41+0x3000ba6>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  248                                 ; clc
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        5c79 <.literal16+0xb09>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5c7d <.literal16+0xb0d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5c81 <.literal16+0xb11>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5c85 <.literal16+0xb15>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,8,33,4,61                   ; and           %eax,0x3d042108(,%rdi,1)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  248                                 ; clc
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        5ce9 <.literal16+0xb79>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5ced <.literal16+0xb7d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5cf1 <.literal16+0xb81>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5cf5 <.literal16+0xb85>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,8,33,4,61                   ; and           %eax,0x3d042108(,%rdi,1)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,248                               ; add           %bh,%al
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  124,66                              ; jl            5d86 <.literal16+0xc16>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  124,66                              ; jl            5d8a <.literal16+0xc1a>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  124,66                              ; jl            5d8e <.literal16+0xc1e>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  124,66                              ; jl            5d92 <.literal16+0xc22>
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  240,0,0                             ; lock          add %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,59,137                ; add           %cl,-0x76c47778(%rcx)
+  DB  136,136,59,137,136,136              ; mov           %cl,-0x777776c5(%rax)
+  DB  59,137,136,136,59,15                ; cmp           0xf3b8888(%rcx),%ecx
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,61,137                ; add           %cl,-0x76c27778(%rcx)
+  DB  136,136,61,137,136,136              ; mov           %cl,-0x777776c3(%rax)
+  DB  61,137,136,136,61                   ; cmp           $0x3d888889,%eax
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  240,0,0                             ; lock          add %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,59,137                ; add           %cl,-0x76c47778(%rcx)
+  DB  136,136,59,137,136,136              ; mov           %cl,-0x777776c5(%rax)
+  DB  59,137,136,136,59,15                ; cmp           0xf3b8888(%rcx),%ecx
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,61,137                ; add           %cl,-0x76c27778(%rcx)
+  DB  136,136,61,137,136,136              ; mov           %cl,-0x777776c3(%rax)
+  DB  61,137,136,136,61                   ; cmp           $0x3d888889,%eax
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            5e95 <.literal16+0xd25>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            5e99 <.literal16+0xd29>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            5e9d <.literal16+0xd2d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            5ea1 <.literal16+0xd31>
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,1,255,255,255            ; addb          $0xff,-0xfec5(%rax)
+  DB  5,255,255,255,9                     ; add           $0x9ffffff,%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,2                ; decl          0x2ffffff(%rip)        # 3005e90 <_sk_callback_sse41+0x3000df6>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,1,255,255,255            ; addb          $0xff,-0xfec5(%rax)
+  DB  5,255,255,255,9                     ; add           $0x9ffffff,%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,13,255,255,255,2                ; decl          0x2ffffff(%rip)        # 3005ed0 <_sk_callback_sse41+0x3000e36>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,6                               ; incl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,10                              ; decl          (%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,14                              ; decl          (%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5f2b <.literal16+0xdbb>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5f2f <.literal16+0xdbf>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5f33 <.literal16+0xdc3>
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  128,0,0                             ; addb          $0x0,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,4                       ; add           %al,0x4000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,0,0                             ; addb          $0x0,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,128,56                    ; add           %al,0x38800000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,56,0                            ; cmpb          $0x0,(%rax)
+  DB  0,128,56,0,0,128                    ; add           %al,-0x7fffffc8(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,128,0,128,55,128                ; incl          -0x7fc88000(%rax)
+  DB  0,128,55,128,0,128                  ; add           %al,-0x7fff7fc9(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  255                                 ; (bad)
+  DB  127,71                              ; jg            5ffb <.literal16+0xe8b>
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            5fff <.literal16+0xe8f>
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            6003 <.literal16+0xe93>
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            6007 <.literal16+0xe97>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,191,0,0,128                   ; add           %al,-0x7fffff41(%rax)
+  DB  191,0,0,128,191                     ; mov           $0xbf800000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,191,0,0,0,63,0                  ; cmpb          $0x0,0x3f000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  208                                 ; (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,208                              ; ds            (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,208                              ; ds            (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,208                              ; ds            (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,89                               ; ds            pop %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  89                                  ; pop           %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  89                                  ; pop           %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  89                                  ; pop           %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  152                                 ; cwtl
+  DB  221,147,61,152,221,147              ; fstl          -0x6c2267c3(%rbx)
+  DB  61,152,221,147,61                   ; cmp           $0x3d93dd98,%eax
+  DB  152                                 ; cwtl
+  DB  221,147,61,45,16,17                 ; fstl          0x11102d3d(%rbx)
+  DB  192,45,16,17,192,45,16              ; shrb          $0x10,0x2dc01110(%rip)        # 2dc0713a <_sk_callback_sse41+0x2dc020a0>
+  DB  17,192                              ; adc           %eax,%eax
+  DB  45,16,17,192,18                     ; sub           $0x12c01110,%eax
+  DB  120,57                              ; js            606c <.literal16+0xefc>
+  DB  64,18,120,57                        ; adc           0x39(%rax),%dil
+  DB  64,18,120,57                        ; adc           0x39(%rax),%dil
+  DB  64,18,120,57                        ; adc           0x39(%rax),%dil
+  DB  64,32,148,90,62,32,148,90           ; and           %dl,0x5a94203e(%rdx,%rbx,2)
+  DB  62,32,148,90,62,32,148,90           ; and           %dl,%ds:0x5a94203e(%rdx,%rbx,2)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,0,24                             ; add           %bl,%ds:(%rax)
+  DB  161,57,0,24,161,57,0,24,161         ; movabs        0xa1180039a1180039,%eax
+  DB  57,0                                ; cmp           %eax,(%rax)
+  DB  24,161,57,1,0,0                     ; sbb           %ah,0x139(%rcx)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,111,43                            ; add           %ch,0x2b(%rdi)
+  DB  231,187                             ; out           %eax,$0xbb
+  DB  111                                 ; outsl         %ds:(%rsi),(%dx)
+  DB  43,231                              ; sub           %edi,%esp
+  DB  187,111,43,231,187                  ; mov           $0xbbe72b6f,%ebx
+  DB  111                                 ; outsl         %ds:(%rsi),(%dx)
+  DB  43,231                              ; sub           %edi,%esp
+  DB  187,159,215,202,60                  ; mov           $0x3ccad79f,%ebx
+  DB  159                                 ; lahf
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  202,60,159                          ; lret          $0x9f3c
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  202,60,159                          ; lret          $0x9f3c
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  202,60,212                          ; lret          $0xd43c
+  DB  100,84                              ; fs            push %rsp
+  DB  189,212,100,84,189                  ; mov           $0xbd5464d4,%ebp
+  DB  212                                 ; (bad)
+  DB  100,84                              ; fs            push %rsp
+  DB  189,212,100,84,189                  ; mov           $0xbd5464d4,%ebp
+  DB  169,240,34,62,169                   ; test          $0xa93e22f0,%eax
+  DB  240,34,62                           ; lock          and (%rsi),%bh
+  DB  169,240,34,62,169                   ; test          $0xa93e22f0,%eax
+  DB  240,34,62                           ; lock          and (%rsi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,62,0                            ; cmpb          $0x0,(%rsi)
+  DB  0,128,62,0,0,128                    ; add           %al,-0x7fffffc2(%rax)
+  DB  62,0,0                              ; add           %al,%ds:(%rax)
+  DB  128,62,0                            ; cmpb          $0x0,(%rsi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,192,191,0               ; sarb          $0x0,-0x40400000(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  191,0,0,192,191                     ; mov           $0xbfc00000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,114                          ; cmpb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,114,28                           ; jb,pt         61a2 <.literal16+0x1032>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         61a6 <.literal16+0x1036>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         61aa <.literal16+0x103a>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,85                           ; cmpb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f035 <_sk_callback_sse41+0x3d639f9b>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f075 <_sk_callback_sse41+0x3d639fdb>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  114,28                              ; jb            626e <.literal16+0x10fe>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         6272 <.literal16+0x1102>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         6276 <.literal16+0x1106>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         627a <.literal16+0x110a>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,192,191,0               ; sarb          $0x0,-0x40400000(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  191,0,0,192,191                     ; mov           $0xbfc00000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,114                          ; cmpb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,114,28                           ; jb,pt         62b2 <.literal16+0x1142>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         62b6 <.literal16+0x1146>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         62ba <.literal16+0x114a>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,85                           ; cmpb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f145 <_sk_callback_sse41+0x3d63a0ab>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f185 <_sk_callback_sse41+0x3d63a0eb>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  114,28                              ; jb            637e <.literal16+0x120e>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         6382 <_sk_callback_sse41+0x12e8>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         6386 <_sk_callback_sse41+0x12ec>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         638a <_sk_callback_sse41+0x12f0>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+ALIGN 32
+
+PUBLIC _sk_start_pipeline_sse2
+_sk_start_pipeline_sse2 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  86                                  ; push          %rsi
+  DB  87                                  ; push          %rdi
+  DB  83                                  ; push          %rbx
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  68,15,41,125,176                    ; movaps        %xmm15,-0x50(%rbp)
+  DB  68,15,41,117,160                    ; movaps        %xmm14,-0x60(%rbp)
+  DB  68,15,41,109,144                    ; movaps        %xmm13,-0x70(%rbp)
+  DB  68,15,41,101,128                    ; movaps        %xmm12,-0x80(%rbp)
+  DB  68,15,41,157,112,255,255,255        ; movaps        %xmm11,-0x90(%rbp)
+  DB  68,15,41,149,96,255,255,255         ; movaps        %xmm10,-0xa0(%rbp)
+  DB  68,15,41,141,80,255,255,255         ; movaps        %xmm9,-0xb0(%rbp)
+  DB  68,15,41,133,64,255,255,255         ; movaps        %xmm8,-0xc0(%rbp)
+  DB  15,41,189,48,255,255,255            ; movaps        %xmm7,-0xd0(%rbp)
+  DB  15,41,181,32,255,255,255            ; movaps        %xmm6,-0xe0(%rbp)
+  DB  72,137,211                          ; mov           %rdx,%rbx
+  DB  73,137,207                          ; mov           %rcx,%r15
+  DB  76,139,117,48                       ; mov           0x30(%rbp),%r14
+  DB  76,137,206                          ; mov           %r9,%rsi
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,197                          ; mov           %rax,%r13
+  DB  73,137,244                          ; mov           %rsi,%r12
+  DB  73,141,79,4                         ; lea           0x4(%r15),%rcx
+  DB  76,57,193                           ; cmp           %r8,%rcx
+  DB  118,5                               ; jbe           7b <_sk_start_pipeline_sse2+0x7b>
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  235,75                              ; jmp           c6 <_sk_start_pipeline_sse2+0xc6>
+  DB  76,137,133,24,255,255,255           ; mov           %r8,-0xe8(%rbp)
+  DB  65,184,0,0,0,0                      ; mov           $0x0,%r8d
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  76,139,133,24,255,255,255           ; mov           -0xe8(%rbp),%r8
+  DB  73,141,87,4                         ; lea           0x4(%r15),%rdx
+  DB  73,131,199,8                        ; add           $0x8,%r15
+  DB  77,57,199                           ; cmp           %r8,%r15
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  118,188                             ; jbe           82 <_sk_start_pipeline_sse2+0x82>
+  DB  73,41,208                           ; sub           %rdx,%r8
+  DB  116,36                              ; je            ef <_sk_start_pipeline_sse2+0xef>
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  15,40,181,32,255,255,255            ; movaps        -0xe0(%rbp),%xmm6
+  DB  15,40,189,48,255,255,255            ; movaps        -0xd0(%rbp),%xmm7
+  DB  68,15,40,133,64,255,255,255         ; movaps        -0xc0(%rbp),%xmm8
+  DB  68,15,40,141,80,255,255,255         ; movaps        -0xb0(%rbp),%xmm9
+  DB  68,15,40,149,96,255,255,255         ; movaps        -0xa0(%rbp),%xmm10
+  DB  68,15,40,157,112,255,255,255        ; movaps        -0x90(%rbp),%xmm11
+  DB  68,15,40,101,128                    ; movaps        -0x80(%rbp),%xmm12
+  DB  68,15,40,109,144                    ; movaps        -0x70(%rbp),%xmm13
+  DB  68,15,40,117,160                    ; movaps        -0x60(%rbp),%xmm14
+  DB  68,15,40,125,176                    ; movaps        -0x50(%rbp),%xmm15
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  95                                  ; pop           %rdi
+  DB  94                                  ; pop           %rsi
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  195                                 ; retq
+
+PUBLIC _sk_just_return_sse2
+_sk_just_return_sse2 LABEL PROC
+  DB  195                                 ; retq
+
+PUBLIC _sk_seed_shader_sse2
+_sk_seed_shader_sse2 LABEL PROC
+  DB  102,15,110,194                      ; movd          %edx,%xmm0
+  DB  102,15,112,192,0                    ; pshufd        $0x0,%xmm0,%xmm0
+  DB  15,91,200                           ; cvtdq2ps      %xmm0,%xmm1
+  DB  15,40,21,103,85,0,0                 ; movaps        0x5567(%rip),%xmm2        # 56c0 <_sk_callback_sse2+0xd9>
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  15,16,7                             ; movups        (%rdi),%xmm0
+  DB  15,88,193                           ; addps         %xmm1,%xmm0
+  DB  102,15,110,201                      ; movd          %ecx,%xmm1
+  DB  102,15,112,201,0                    ; pshufd        $0x0,%xmm1,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,21,86,85,0,0                  ; movaps        0x5556(%rip),%xmm2        # 56d0 <_sk_callback_sse2+0xe9>
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dither_sse2
+_sk_dither_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  102,68,15,110,194                   ; movd          %edx,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  243,68,15,111,79,32                 ; movdqu        0x20(%rdi),%xmm9
+  DB  102,69,15,254,200                   ; paddd         %xmm8,%xmm9
+  DB  102,68,15,110,193                   ; movd          %ecx,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  102,69,15,239,193                   ; pxor          %xmm9,%xmm8
+  DB  102,68,15,111,21,36,85,0,0          ; movdqa        0x5524(%rip),%xmm10        # 56e0 <_sk_callback_sse2+0xf9>
+  DB  102,69,15,111,216                   ; movdqa        %xmm8,%xmm11
+  DB  102,69,15,219,218                   ; pand          %xmm10,%xmm11
+  DB  102,65,15,114,243,5                 ; pslld         $0x5,%xmm11
+  DB  102,69,15,219,209                   ; pand          %xmm9,%xmm10
+  DB  102,65,15,114,242,4                 ; pslld         $0x4,%xmm10
+  DB  102,68,15,111,37,16,85,0,0          ; movdqa        0x5510(%rip),%xmm12        # 56f0 <_sk_callback_sse2+0x109>
+  DB  102,68,15,111,45,23,85,0,0          ; movdqa        0x5517(%rip),%xmm13        # 5700 <_sk_callback_sse2+0x119>
+  DB  102,69,15,111,240                   ; movdqa        %xmm8,%xmm14
+  DB  102,69,15,219,245                   ; pand          %xmm13,%xmm14
+  DB  102,65,15,114,246,2                 ; pslld         $0x2,%xmm14
+  DB  102,69,15,219,233                   ; pand          %xmm9,%xmm13
+  DB  102,69,15,254,237                   ; paddd         %xmm13,%xmm13
+  DB  102,69,15,219,196                   ; pand          %xmm12,%xmm8
+  DB  102,65,15,114,208,1                 ; psrld         $0x1,%xmm8
+  DB  102,69,15,219,204                   ; pand          %xmm12,%xmm9
+  DB  102,65,15,114,209,2                 ; psrld         $0x2,%xmm9
+  DB  102,69,15,235,234                   ; por           %xmm10,%xmm13
+  DB  102,69,15,235,233                   ; por           %xmm9,%xmm13
+  DB  102,69,15,235,243                   ; por           %xmm11,%xmm14
+  DB  102,69,15,235,245                   ; por           %xmm13,%xmm14
+  DB  102,69,15,235,240                   ; por           %xmm8,%xmm14
+  DB  69,15,91,198                        ; cvtdq2ps      %xmm14,%xmm8
+  DB  68,15,89,5,210,84,0,0               ; mulps         0x54d2(%rip),%xmm8        # 5710 <_sk_callback_sse2+0x129>
+  DB  68,15,88,5,218,84,0,0               ; addps         0x54da(%rip),%xmm8        # 5720 <_sk_callback_sse2+0x139>
+  DB  243,68,15,16,16                     ; movss         (%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  68,15,88,210                        ; addps         %xmm2,%xmm10
+  DB  15,93,195                           ; minps         %xmm3,%xmm0
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  15,93,203                           ; minps         %xmm3,%xmm1
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  68,15,95,201                        ; maxps         %xmm1,%xmm9
+  DB  68,15,93,211                        ; minps         %xmm3,%xmm10
+  DB  65,15,95,210                        ; maxps         %xmm10,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_constant_color_sse2
+_sk_constant_color_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,12                     ; movss         0xc(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_rgba_sse2
+_sk_load_rgba_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  15,16,72,16                         ; movups        0x10(%rax),%xmm1
+  DB  15,16,80,32                         ; movups        0x20(%rax),%xmm2
+  DB  15,16,88,48                         ; movups        0x30(%rax),%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_rgba_sse2
+_sk_store_rgba_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,17,0                             ; movups        %xmm0,(%rax)
+  DB  15,17,72,16                         ; movups        %xmm1,0x10(%rax)
+  DB  15,17,80,32                         ; movups        %xmm2,0x20(%rax)
+  DB  15,17,88,48                         ; movups        %xmm3,0x30(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clear_sse2
+_sk_clear_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcatop_sse2
+_sk_srcatop_sse2 LABEL PROC
+  DB  15,89,199                           ; mulps         %xmm7,%xmm0
+  DB  68,15,40,5,52,84,0,0                ; movaps        0x5434(%rip),%xmm8        # 5730 <_sk_callback_sse2+0x149>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  65,15,88,193                        ; addps         %xmm9,%xmm0
+  DB  15,89,207                           ; mulps         %xmm7,%xmm1
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,88,209                        ; addps         %xmm9,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstatop_sse2
+_sk_dstatop_sse2 LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  68,15,40,13,247,83,0,0              ; movaps        0x53f7(%rip),%xmm9        # 5740 <_sk_callback_sse2+0x159>
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,197                        ; mulps         %xmm5,%xmm8
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,198                        ; mulps         %xmm6,%xmm8
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  65,15,88,208                        ; addps         %xmm8,%xmm2
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcin_sse2
+_sk_srcin_sse2 LABEL PROC
+  DB  15,89,199                           ; mulps         %xmm7,%xmm0
+  DB  15,89,207                           ; mulps         %xmm7,%xmm1
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstin_sse2
+_sk_dstin_sse2 LABEL PROC
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  15,40,211                           ; movaps        %xmm3,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcout_sse2
+_sk_srcout_sse2 LABEL PROC
+  DB  68,15,40,5,155,83,0,0               ; movaps        0x539b(%rip),%xmm8        # 5750 <_sk_callback_sse2+0x169>
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstout_sse2
+_sk_dstout_sse2 LABEL PROC
+  DB  68,15,40,5,139,83,0,0               ; movaps        0x538b(%rip),%xmm8        # 5760 <_sk_callback_sse2+0x179>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_sse2
+_sk_srcover_sse2 LABEL PROC
+  DB  68,15,40,5,110,83,0,0               ; movaps        0x536e(%rip),%xmm8        # 5770 <_sk_callback_sse2+0x189>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  65,15,88,193                        ; addps         %xmm9,%xmm0
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,88,209                        ; addps         %xmm9,%xmm2
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstover_sse2
+_sk_dstover_sse2 LABEL PROC
+  DB  68,15,40,5,66,83,0,0                ; movaps        0x5342(%rip),%xmm8        # 5780 <_sk_callback_sse2+0x199>
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_modulate_sse2
+_sk_modulate_sse2 LABEL PROC
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_multiply_sse2
+_sk_multiply_sse2 LABEL PROC
+  DB  68,15,40,5,22,83,0,0                ; movaps        0x5316(%rip),%xmm8        # 5790 <_sk_callback_sse2+0x1a9>
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,89,208                        ; mulps         %xmm0,%xmm10
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,220                        ; mulps         %xmm4,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,221                        ; mulps         %xmm5,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,222                        ; mulps         %xmm6,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  65,15,88,211                        ; addps         %xmm11,%xmm2
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  65,15,88,216                        ; addps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_plus__sse2
+_sk_plus__sse2 LABEL PROC
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_screen_sse2
+_sk_screen_sse2 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  65,15,92,192                        ; subps         %xmm8,%xmm0
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,88,197                        ; addps         %xmm5,%xmm8
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  68,15,92,193                        ; subps         %xmm1,%xmm8
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  68,15,88,206                        ; addps         %xmm6,%xmm9
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,92,202                        ; subps         %xmm2,%xmm9
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,88,215                        ; addps         %xmm7,%xmm10
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xor__sse2
+_sk_xor__sse2 LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  15,40,29,75,82,0,0                  ; movaps        0x524b(%rip),%xmm3        # 57a0 <_sk_callback_sse2+0x1b9>
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  65,15,92,216                        ; subps         %xmm8,%xmm3
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,89,212                        ; mulps         %xmm4,%xmm10
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,89,213                        ; mulps         %xmm5,%xmm10
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,89,214                        ; mulps         %xmm6,%xmm10
+  DB  65,15,88,210                        ; addps         %xmm10,%xmm2
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  15,89,223                           ; mulps         %xmm7,%xmm3
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_darken_sse2
+_sk_darken_sse2 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,95,201                        ; maxps         %xmm1,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,95,193                        ; maxps         %xmm9,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,95,209                        ; maxps         %xmm9,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,182,81,0,0                 ; movaps        0x51b6(%rip),%xmm2        # 57b0 <_sk_callback_sse2+0x1c9>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lighten_sse2
+_sk_lighten_sse2 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,93,201                        ; minps         %xmm1,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,93,193                        ; minps         %xmm9,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,93,209                        ; minps         %xmm9,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,91,81,0,0                  ; movaps        0x515b(%rip),%xmm2        # 57c0 <_sk_callback_sse2+0x1d9>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_difference_sse2
+_sk_difference_sse2 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  68,15,93,201                        ; minps         %xmm1,%xmm9
+  DB  69,15,88,201                        ; addps         %xmm9,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,93,193                        ; minps         %xmm9,%xmm8
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,93,209                        ; minps         %xmm9,%xmm2
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,245,80,0,0                 ; movaps        0x50f5(%rip),%xmm2        # 57d0 <_sk_callback_sse2+0x1e9>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_exclusion_sse2
+_sk_exclusion_sse2 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  68,15,89,197                        ; mulps         %xmm5,%xmm8
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  65,15,92,200                        ; subps         %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,88,198                        ; addps         %xmm6,%xmm8
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  68,15,92,194                        ; subps         %xmm2,%xmm8
+  DB  15,40,21,181,80,0,0                 ; movaps        0x50b5(%rip),%xmm2        # 57e0 <_sk_callback_sse2+0x1f9>
+  DB  15,92,211                           ; subps         %xmm3,%xmm2
+  DB  15,89,215                           ; mulps         %xmm7,%xmm2
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colorburn_sse2
+_sk_colorburn_sse2 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  68,15,40,21,168,80,0,0              ; movaps        0x50a8(%rip),%xmm10        # 57f0 <_sk_callback_sse2+0x209>
+  DB  69,15,40,202                        ; movaps        %xmm10,%xmm9
+  DB  68,15,92,207                        ; subps         %xmm7,%xmm9
+  DB  69,15,40,217                        ; movaps        %xmm9,%xmm11
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  65,15,94,192                        ; divps         %xmm8,%xmm0
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,93,224                        ; minps         %xmm0,%xmm12
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,235                        ; addps         %xmm11,%xmm13
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  69,15,40,224                        ; movaps        %xmm8,%xmm12
+  DB  69,15,194,227,0                     ; cmpeqps       %xmm11,%xmm12
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  69,15,84,196                        ; andps         %xmm12,%xmm8
+  DB  69,15,85,229                        ; andnps        %xmm13,%xmm12
+  DB  69,15,40,234                        ; movaps        %xmm10,%xmm13
+  DB  68,15,89,236                        ; mulps         %xmm4,%xmm13
+  DB  69,15,86,224                        ; orps          %xmm8,%xmm12
+  DB  68,15,40,196                        ; movaps        %xmm4,%xmm8
+  DB  68,15,194,199,0                     ; cmpeqps       %xmm7,%xmm8
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  65,15,84,192                        ; andps         %xmm8,%xmm0
+  DB  69,15,85,196                        ; andnps        %xmm12,%xmm8
+  DB  65,15,86,192                        ; orps          %xmm8,%xmm0
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,193                        ; mulps         %xmm1,%xmm8
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,92,229                        ; subps         %xmm5,%xmm12
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  68,15,94,225                        ; divps         %xmm1,%xmm12
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  69,15,93,236                        ; minps         %xmm12,%xmm13
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,40,233                        ; movaps        %xmm1,%xmm13
+  DB  69,15,194,235,0                     ; cmpeqps       %xmm11,%xmm13
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  69,15,88,224                        ; addps         %xmm8,%xmm12
+  DB  65,15,84,205                        ; andps         %xmm13,%xmm1
+  DB  69,15,85,236                        ; andnps        %xmm12,%xmm13
+  DB  68,15,88,197                        ; addps         %xmm5,%xmm8
+  DB  68,15,86,233                        ; orps          %xmm1,%xmm13
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  68,15,88,233                        ; addps         %xmm1,%xmm13
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,194,207,0                        ; cmpeqps       %xmm7,%xmm1
+  DB  68,15,84,193                        ; andps         %xmm1,%xmm8
+  DB  65,15,85,205                        ; andnps        %xmm13,%xmm1
+  DB  68,15,86,193                        ; orps          %xmm1,%xmm8
+  DB  15,40,207                           ; movaps        %xmm7,%xmm1
+  DB  15,92,206                           ; subps         %xmm6,%xmm1
+  DB  15,89,203                           ; mulps         %xmm3,%xmm1
+  DB  15,94,202                           ; divps         %xmm2,%xmm1
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,93,225                        ; minps         %xmm1,%xmm12
+  DB  15,40,207                           ; movaps        %xmm7,%xmm1
+  DB  65,15,92,204                        ; subps         %xmm12,%xmm1
+  DB  68,15,89,202                        ; mulps         %xmm2,%xmm9
+  DB  68,15,194,218,0                     ; cmpeqps       %xmm2,%xmm11
+  DB  15,89,203                           ; mulps         %xmm3,%xmm1
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  65,15,84,211                        ; andps         %xmm11,%xmm2
+  DB  68,15,85,217                        ; andnps        %xmm1,%xmm11
+  DB  68,15,88,206                        ; addps         %xmm6,%xmm9
+  DB  68,15,86,218                        ; orps          %xmm2,%xmm11
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  15,89,206                           ; mulps         %xmm6,%xmm1
+  DB  68,15,88,217                        ; addps         %xmm1,%xmm11
+  DB  15,40,206                           ; movaps        %xmm6,%xmm1
+  DB  15,194,207,0                        ; cmpeqps       %xmm7,%xmm1
+  DB  68,15,84,201                        ; andps         %xmm1,%xmm9
+  DB  65,15,85,203                        ; andnps        %xmm11,%xmm1
+  DB  68,15,86,201                        ; orps          %xmm1,%xmm9
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_colordodge_sse2
+_sk_colordodge_sse2 LABEL PROC
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  68,15,40,21,94,79,0,0               ; movaps        0x4f5e(%rip),%xmm10        # 5800 <_sk_callback_sse2+0x219>
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  68,15,92,223                        ; subps         %xmm7,%xmm11
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,89,225                        ; mulps         %xmm9,%xmm12
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,89,196                        ; mulps         %xmm4,%xmm8
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  68,15,94,192                        ; divps         %xmm0,%xmm8
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  65,15,93,192                        ; minps         %xmm8,%xmm0
+  DB  69,15,40,241                        ; movaps        %xmm9,%xmm14
+  DB  68,15,194,243,0                     ; cmpeqps       %xmm3,%xmm14
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  65,15,88,196                        ; addps         %xmm12,%xmm0
+  DB  69,15,84,206                        ; andps         %xmm14,%xmm9
+  DB  68,15,85,240                        ; andnps        %xmm0,%xmm14
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  65,15,40,196                        ; movaps        %xmm12,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  68,15,92,211                        ; subps         %xmm3,%xmm10
+  DB  69,15,86,241                        ; orps          %xmm9,%xmm14
+  DB  69,15,40,202                        ; movaps        %xmm10,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  69,15,88,241                        ; addps         %xmm9,%xmm14
+  DB  68,15,40,204                        ; movaps        %xmm4,%xmm9
+  DB  69,15,194,200,0                     ; cmpeqps       %xmm8,%xmm9
+  DB  65,15,84,193                        ; andps         %xmm9,%xmm0
+  DB  69,15,85,206                        ; andnps        %xmm14,%xmm9
+  DB  65,15,86,193                        ; orps          %xmm9,%xmm0
+  DB  68,15,40,203                        ; movaps        %xmm3,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  68,15,92,225                        ; subps         %xmm1,%xmm12
+  DB  69,15,94,204                        ; divps         %xmm12,%xmm9
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  68,15,89,225                        ; mulps         %xmm1,%xmm12
+  DB  69,15,93,233                        ; minps         %xmm9,%xmm13
+  DB  68,15,40,241                        ; movaps        %xmm1,%xmm14
+  DB  68,15,194,243,0                     ; cmpeqps       %xmm3,%xmm14
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,236                        ; addps         %xmm12,%xmm13
+  DB  65,15,84,206                        ; andps         %xmm14,%xmm1
+  DB  69,15,85,245                        ; andnps        %xmm13,%xmm14
+  DB  69,15,40,204                        ; movaps        %xmm12,%xmm9
+  DB  68,15,88,205                        ; addps         %xmm5,%xmm9
+  DB  68,15,86,241                        ; orps          %xmm1,%xmm14
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  68,15,88,241                        ; addps         %xmm1,%xmm14
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  65,15,194,200,0                     ; cmpeqps       %xmm8,%xmm1
+  DB  68,15,84,201                        ; andps         %xmm1,%xmm9
+  DB  65,15,85,206                        ; andnps        %xmm14,%xmm1
+  DB  68,15,86,201                        ; orps          %xmm1,%xmm9
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  68,15,89,230                        ; mulps         %xmm6,%xmm12
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,92,202                           ; subps         %xmm2,%xmm1
+  DB  68,15,94,225                        ; divps         %xmm1,%xmm12
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,93,236                        ; minps         %xmm12,%xmm13
+  DB  15,40,202                           ; movaps        %xmm2,%xmm1
+  DB  15,194,203,0                        ; cmpeqps       %xmm3,%xmm1
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,235                        ; addps         %xmm11,%xmm13
+  DB  15,84,209                           ; andps         %xmm1,%xmm2
+  DB  65,15,85,205                        ; andnps        %xmm13,%xmm1
+  DB  15,86,202                           ; orps          %xmm2,%xmm1
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  68,15,194,198,0                     ; cmpeqps       %xmm6,%xmm8
+  DB  68,15,88,222                        ; addps         %xmm6,%xmm11
+  DB  69,15,84,216                        ; andps         %xmm8,%xmm11
+  DB  68,15,85,193                        ; andnps        %xmm1,%xmm8
+  DB  69,15,86,195                        ; orps          %xmm11,%xmm8
+  DB  68,15,89,215                        ; mulps         %xmm7,%xmm10
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hardlight_sse2
+_sk_hardlight_sse2 LABEL PROC
+  DB  72,131,236,24                       ; sub           $0x18,%rsp
+  DB  15,41,52,36                         ; movaps        %xmm6,(%rsp)
+  DB  15,40,245                           ; movaps        %xmm5,%xmm6
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  68,15,40,29,16,78,0,0               ; movaps        0x4e10(%rip),%xmm11        # 5810 <_sk_callback_sse2+0x229>
+  DB  69,15,40,211                        ; movaps        %xmm11,%xmm10
+  DB  68,15,92,215                        ; subps         %xmm7,%xmm10
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  68,15,92,219                        ; subps         %xmm3,%xmm11
+  DB  69,15,40,203                        ; movaps        %xmm11,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  69,15,88,200                        ; addps         %xmm8,%xmm9
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,92,192                        ; subps         %xmm0,%xmm8
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  15,89,231                           ; mulps         %xmm7,%xmm4
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,40,247                        ; movaps        %xmm7,%xmm14
+  DB  68,15,40,255                        ; movaps        %xmm7,%xmm15
+  DB  68,15,92,253                        ; subps         %xmm5,%xmm15
+  DB  69,15,89,248                        ; mulps         %xmm8,%xmm15
+  DB  69,15,88,255                        ; addps         %xmm15,%xmm15
+  DB  68,15,40,228                        ; movaps        %xmm4,%xmm12
+  DB  69,15,92,231                        ; subps         %xmm15,%xmm12
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  68,15,194,195,2                     ; cmpleps       %xmm3,%xmm8
+  DB  15,89,197                           ; mulps         %xmm5,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  65,15,84,192                        ; andps         %xmm8,%xmm0
+  DB  69,15,85,196                        ; andnps        %xmm12,%xmm8
+  DB  68,15,86,192                        ; orps          %xmm0,%xmm8
+  DB  69,15,40,251                        ; movaps        %xmm11,%xmm15
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  68,15,89,223                        ; mulps         %xmm7,%xmm11
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,89,193                           ; mulps         %xmm1,%xmm0
+  DB  68,15,89,254                        ; mulps         %xmm6,%xmm15
+  DB  68,15,88,248                        ; addps         %xmm0,%xmm15
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  68,15,92,238                        ; subps         %xmm6,%xmm13
+  DB  68,15,89,232                        ; mulps         %xmm0,%xmm13
+  DB  69,15,88,237                        ; addps         %xmm13,%xmm13
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  65,15,92,197                        ; subps         %xmm13,%xmm0
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  69,15,88,201                        ; addps         %xmm9,%xmm9
+  DB  68,15,194,203,2                     ; cmpleps       %xmm3,%xmm9
+  DB  15,89,206                           ; mulps         %xmm6,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  65,15,84,201                        ; andps         %xmm9,%xmm1
+  DB  68,15,85,200                        ; andnps        %xmm0,%xmm9
+  DB  68,15,86,201                        ; orps          %xmm1,%xmm9
+  DB  69,15,88,207                        ; addps         %xmm15,%xmm9
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  68,15,40,44,36                      ; movaps        (%rsp),%xmm13
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,88,226                        ; addps         %xmm10,%xmm12
+  DB  68,15,40,210                        ; movaps        %xmm2,%xmm10
+  DB  69,15,88,210                        ; addps         %xmm10,%xmm10
+  DB  68,15,194,211,2                     ; cmpleps       %xmm3,%xmm10
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,92,194                           ; subps         %xmm2,%xmm0
+  DB  65,15,89,213                        ; mulps         %xmm13,%xmm2
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  69,15,92,245                        ; subps         %xmm13,%xmm14
+  DB  68,15,89,240                        ; mulps         %xmm0,%xmm14
+  DB  69,15,88,246                        ; addps         %xmm14,%xmm14
+  DB  65,15,92,230                        ; subps         %xmm14,%xmm4
+  DB  65,15,84,210                        ; andps         %xmm10,%xmm2
+  DB  68,15,85,212                        ; andnps        %xmm4,%xmm10
+  DB  68,15,86,210                        ; orps          %xmm2,%xmm10
+  DB  69,15,88,212                        ; addps         %xmm12,%xmm10
+  DB  65,15,88,219                        ; addps         %xmm11,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,40,238                           ; movaps        %xmm6,%xmm5
+  DB  65,15,40,245                        ; movaps        %xmm13,%xmm6
+  DB  72,131,196,24                       ; add           $0x18,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_overlay_sse2
+_sk_overlay_sse2 LABEL PROC
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  68,15,40,232                        ; movaps        %xmm0,%xmm13
+  DB  68,15,40,13,219,76,0,0              ; movaps        0x4cdb(%rip),%xmm9        # 5820 <_sk_callback_sse2+0x239>
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  68,15,92,215                        ; subps         %xmm7,%xmm10
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  69,15,89,221                        ; mulps         %xmm13,%xmm11
+  DB  68,15,92,203                        ; subps         %xmm3,%xmm9
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,89,236                        ; mulps         %xmm4,%xmm13
+  DB  68,15,40,247                        ; movaps        %xmm7,%xmm14
+  DB  68,15,92,244                        ; subps         %xmm4,%xmm14
+  DB  15,40,204                           ; movaps        %xmm4,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,194,207,2                        ; cmpleps       %xmm7,%xmm1
+  DB  69,15,88,237                        ; addps         %xmm13,%xmm13
+  DB  68,15,40,219                        ; movaps        %xmm3,%xmm11
+  DB  68,15,89,223                        ; mulps         %xmm7,%xmm11
+  DB  69,15,89,244                        ; mulps         %xmm12,%xmm14
+  DB  69,15,88,246                        ; addps         %xmm14,%xmm14
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,92,230                        ; subps         %xmm14,%xmm12
+  DB  68,15,84,233                        ; andps         %xmm1,%xmm13
+  DB  65,15,85,204                        ; andnps        %xmm12,%xmm1
+  DB  65,15,86,205                        ; orps          %xmm13,%xmm1
+  DB  15,88,193                           ; addps         %xmm1,%xmm0
+  DB  69,15,40,226                        ; movaps        %xmm10,%xmm12
+  DB  69,15,89,224                        ; mulps         %xmm8,%xmm12
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  65,15,88,204                        ; addps         %xmm12,%xmm1
+  DB  68,15,40,227                        ; movaps        %xmm3,%xmm12
+  DB  69,15,92,224                        ; subps         %xmm8,%xmm12
+  DB  68,15,89,197                        ; mulps         %xmm5,%xmm8
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  68,15,92,237                        ; subps         %xmm5,%xmm13
+  DB  68,15,40,245                        ; movaps        %xmm5,%xmm14
+  DB  69,15,88,246                        ; addps         %xmm14,%xmm14
+  DB  68,15,194,247,2                     ; cmpleps       %xmm7,%xmm14
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  69,15,89,236                        ; mulps         %xmm12,%xmm13
+  DB  69,15,88,237                        ; addps         %xmm13,%xmm13
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  69,15,84,198                        ; andps         %xmm14,%xmm8
+  DB  69,15,85,244                        ; andnps        %xmm12,%xmm14
+  DB  69,15,86,240                        ; orps          %xmm8,%xmm14
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,198                        ; mulps         %xmm6,%xmm8
+  DB  69,15,88,194                        ; addps         %xmm10,%xmm8
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,92,210                        ; subps         %xmm2,%xmm10
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,92,230                        ; subps         %xmm6,%xmm12
+  DB  68,15,40,238                        ; movaps        %xmm6,%xmm13
+  DB  69,15,88,237                        ; addps         %xmm13,%xmm13
+  DB  68,15,194,239,2                     ; cmpleps       %xmm7,%xmm13
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  69,15,88,228                        ; addps         %xmm12,%xmm12
+  DB  69,15,92,220                        ; subps         %xmm12,%xmm11
+  DB  65,15,84,213                        ; andps         %xmm13,%xmm2
+  DB  69,15,85,235                        ; andnps        %xmm11,%xmm13
+  DB  68,15,86,234                        ; orps          %xmm2,%xmm13
+  DB  69,15,88,197                        ; addps         %xmm13,%xmm8
+  DB  68,15,89,207                        ; mulps         %xmm7,%xmm9
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_softlight_sse2
+_sk_softlight_sse2 LABEL PROC
+  DB  72,131,236,40                       ; sub           $0x28,%rsp
+  DB  15,41,52,36                         ; movaps        %xmm6,(%rsp)
+  DB  15,40,245                           ; movaps        %xmm5,%xmm6
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,41,84,36,16                      ; movaps        %xmm2,0x10(%rsp)
+  DB  15,40,209                           ; movaps        %xmm1,%xmm2
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,87,228                        ; xorps         %xmm12,%xmm12
+  DB  68,15,194,231,1                     ; cmpltps       %xmm7,%xmm12
+  DB  68,15,40,213                        ; movaps        %xmm5,%xmm10
+  DB  68,15,94,215                        ; divps         %xmm7,%xmm10
+  DB  69,15,84,212                        ; andps         %xmm12,%xmm10
+  DB  68,15,40,13,149,75,0,0              ; movaps        0x4b95(%rip),%xmm9        # 5830 <_sk_callback_sse2+0x249>
+  DB  69,15,40,249                        ; movaps        %xmm9,%xmm15
+  DB  69,15,92,250                        ; subps         %xmm10,%xmm15
+  DB  69,15,40,218                        ; movaps        %xmm10,%xmm11
+  DB  69,15,40,234                        ; movaps        %xmm10,%xmm13
+  DB  65,15,82,194                        ; rsqrtps       %xmm10,%xmm0
+  DB  15,83,200                           ; rcpps         %xmm0,%xmm1
+  DB  65,15,92,202                        ; subps         %xmm10,%xmm1
+  DB  69,15,88,210                        ; addps         %xmm10,%xmm10
+  DB  69,15,88,210                        ; addps         %xmm10,%xmm10
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  15,89,192                           ; mulps         %xmm0,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  68,15,40,53,111,75,0,0              ; movaps        0x4b6f(%rip),%xmm14        # 5840 <_sk_callback_sse2+0x259>
+  DB  69,15,88,222                        ; addps         %xmm14,%xmm11
+  DB  68,15,89,216                        ; mulps         %xmm0,%xmm11
+  DB  68,15,40,21,111,75,0,0              ; movaps        0x4b6f(%rip),%xmm10        # 5850 <_sk_callback_sse2+0x269>
+  DB  69,15,89,234                        ; mulps         %xmm10,%xmm13
+  DB  69,15,88,235                        ; addps         %xmm11,%xmm13
+  DB  15,88,228                           ; addps         %xmm4,%xmm4
+  DB  15,88,228                           ; addps         %xmm4,%xmm4
+  DB  15,194,231,2                        ; cmpleps       %xmm7,%xmm4
+  DB  68,15,84,236                        ; andps         %xmm4,%xmm13
+  DB  15,85,225                           ; andnps        %xmm1,%xmm4
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,88,192                           ; addps         %xmm0,%xmm0
+  DB  65,15,86,229                        ; orps          %xmm13,%xmm4
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,92,203                           ; subps         %xmm3,%xmm1
+  DB  68,15,89,249                        ; mulps         %xmm1,%xmm15
+  DB  15,89,207                           ; mulps         %xmm7,%xmm1
+  DB  15,89,225                           ; mulps         %xmm1,%xmm4
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  15,88,225                           ; addps         %xmm1,%xmm4
+  DB  69,15,40,217                        ; movaps        %xmm9,%xmm11
+  DB  68,15,92,219                        ; subps         %xmm3,%xmm11
+  DB  65,15,40,203                        ; movaps        %xmm11,%xmm1
+  DB  15,89,205                           ; mulps         %xmm5,%xmm1
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  68,15,92,239                        ; subps         %xmm7,%xmm13
+  DB  69,15,89,197                        ; mulps         %xmm13,%xmm8
+  DB  68,15,88,193                        ; addps         %xmm1,%xmm8
+  DB  68,15,88,251                        ; addps         %xmm3,%xmm15
+  DB  68,15,89,253                        ; mulps         %xmm5,%xmm15
+  DB  15,194,195,2                        ; cmpleps       %xmm3,%xmm0
+  DB  68,15,84,248                        ; andps         %xmm0,%xmm15
+  DB  15,85,196                           ; andnps        %xmm4,%xmm0
+  DB  65,15,86,199                        ; orps          %xmm15,%xmm0
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,198                        ; movaps        %xmm6,%xmm8
+  DB  68,15,94,199                        ; divps         %xmm7,%xmm8
+  DB  69,15,84,196                        ; andps         %xmm12,%xmm8
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,40,225                           ; movaps        %xmm1,%xmm4
+  DB  15,89,228                           ; mulps         %xmm4,%xmm4
+  DB  15,88,225                           ; addps         %xmm1,%xmm4
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  69,15,40,249                        ; movaps        %xmm9,%xmm15
+  DB  69,15,92,248                        ; subps         %xmm8,%xmm15
+  DB  65,15,82,224                        ; rsqrtps       %xmm8,%xmm4
+  DB  15,83,228                           ; rcpps         %xmm4,%xmm4
+  DB  65,15,92,224                        ; subps         %xmm8,%xmm4
+  DB  69,15,89,194                        ; mulps         %xmm10,%xmm8
+  DB  68,15,88,193                        ; addps         %xmm1,%xmm8
+  DB  15,40,206                           ; movaps        %xmm6,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,194,207,2                        ; cmpleps       %xmm7,%xmm1
+  DB  68,15,84,193                        ; andps         %xmm1,%xmm8
+  DB  15,85,204                           ; andnps        %xmm4,%xmm1
+  DB  65,15,86,200                        ; orps          %xmm8,%xmm1
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  69,15,88,192                        ; addps         %xmm8,%xmm8
+  DB  65,15,40,224                        ; movaps        %xmm8,%xmm4
+  DB  15,92,227                           ; subps         %xmm3,%xmm4
+  DB  68,15,89,252                        ; mulps         %xmm4,%xmm15
+  DB  15,89,231                           ; mulps         %xmm7,%xmm4
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  15,89,230                           ; mulps         %xmm6,%xmm4
+  DB  15,88,204                           ; addps         %xmm4,%xmm1
+  DB  65,15,40,227                        ; movaps        %xmm11,%xmm4
+  DB  15,89,230                           ; mulps         %xmm6,%xmm4
+  DB  65,15,89,213                        ; mulps         %xmm13,%xmm2
+  DB  15,88,212                           ; addps         %xmm4,%xmm2
+  DB  68,15,88,251                        ; addps         %xmm3,%xmm15
+  DB  68,15,89,254                        ; mulps         %xmm6,%xmm15
+  DB  68,15,194,195,2                     ; cmpleps       %xmm3,%xmm8
+  DB  69,15,84,248                        ; andps         %xmm8,%xmm15
+  DB  68,15,85,193                        ; andnps        %xmm1,%xmm8
+  DB  69,15,86,199                        ; orps          %xmm15,%xmm8
+  DB  68,15,88,194                        ; addps         %xmm2,%xmm8
+  DB  68,15,40,60,36                      ; movaps        (%rsp),%xmm15
+  DB  65,15,40,207                        ; movaps        %xmm15,%xmm1
+  DB  15,94,207                           ; divps         %xmm7,%xmm1
+  DB  65,15,84,204                        ; andps         %xmm12,%xmm1
+  DB  68,15,92,201                        ; subps         %xmm1,%xmm9
+  DB  68,15,88,241                        ; addps         %xmm1,%xmm14
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  15,82,209                           ; rsqrtps       %xmm1,%xmm2
+  DB  15,83,210                           ; rcpps         %xmm2,%xmm2
+  DB  15,92,209                           ; subps         %xmm1,%xmm2
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,40,225                           ; movaps        %xmm1,%xmm4
+  DB  15,89,228                           ; mulps         %xmm4,%xmm4
+  DB  15,88,225                           ; addps         %xmm1,%xmm4
+  DB  68,15,89,244                        ; mulps         %xmm4,%xmm14
+  DB  69,15,88,214                        ; addps         %xmm14,%xmm10
+  DB  65,15,40,207                        ; movaps        %xmm15,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,88,201                           ; addps         %xmm1,%xmm1
+  DB  15,194,207,2                        ; cmpleps       %xmm7,%xmm1
+  DB  68,15,84,209                        ; andps         %xmm1,%xmm10
+  DB  15,85,202                           ; andnps        %xmm2,%xmm1
+  DB  15,40,84,36,16                      ; movaps        0x10(%rsp),%xmm2
+  DB  68,15,89,234                        ; mulps         %xmm2,%xmm13
+  DB  15,88,210                           ; addps         %xmm2,%xmm2
+  DB  65,15,86,202                        ; orps          %xmm10,%xmm1
+  DB  15,40,226                           ; movaps        %xmm2,%xmm4
+  DB  15,92,227                           ; subps         %xmm3,%xmm4
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  15,89,231                           ; mulps         %xmm7,%xmm4
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  65,15,89,231                        ; mulps         %xmm15,%xmm4
+  DB  15,88,204                           ; addps         %xmm4,%xmm1
+  DB  65,15,40,227                        ; movaps        %xmm11,%xmm4
+  DB  65,15,89,231                        ; mulps         %xmm15,%xmm4
+  DB  65,15,88,229                        ; addps         %xmm13,%xmm4
+  DB  68,15,88,203                        ; addps         %xmm3,%xmm9
+  DB  69,15,89,207                        ; mulps         %xmm15,%xmm9
+  DB  69,15,40,215                        ; movaps        %xmm15,%xmm10
+  DB  15,194,211,2                        ; cmpleps       %xmm3,%xmm2
+  DB  68,15,84,202                        ; andps         %xmm2,%xmm9
+  DB  15,85,209                           ; andnps        %xmm1,%xmm2
+  DB  65,15,86,209                        ; orps          %xmm9,%xmm2
+  DB  15,88,212                           ; addps         %xmm4,%xmm2
+  DB  68,15,89,223                        ; mulps         %xmm7,%xmm11
+  DB  65,15,88,219                        ; addps         %xmm11,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,40,238                           ; movaps        %xmm6,%xmm5
+  DB  65,15,40,242                        ; movaps        %xmm10,%xmm6
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  72,131,196,40                       ; add           $0x28,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hue_sse2
+_sk_hue_sse2 LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  68,15,40,225                        ; movaps        %xmm1,%xmm12
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  68,15,40,5,171,73,0,0               ; movaps        0x49ab(%rip),%xmm8        # 5890 <_sk_callback_sse2+0x2a9>
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  15,40,207                           ; movaps        %xmm7,%xmm1
+  DB  68,15,92,217                        ; subps         %xmm1,%xmm11
+  DB  65,15,89,195                        ; mulps         %xmm11,%xmm0
+  DB  15,41,68,36,64                      ; movaps        %xmm0,0x40(%rsp)
+  DB  69,15,89,227                        ; mulps         %xmm11,%xmm12
+  DB  68,15,41,100,36,48                  ; movaps        %xmm12,0x30(%rsp)
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  15,89,211                           ; mulps         %xmm3,%xmm2
+  DB  68,15,40,229                        ; movaps        %xmm5,%xmm12
+  DB  68,15,40,237                        ; movaps        %xmm5,%xmm13
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  15,41,68,36,32                      ; movaps        %xmm0,0x20(%rsp)
+  DB  15,40,254                           ; movaps        %xmm6,%xmm7
+  DB  68,15,95,239                        ; maxps         %xmm7,%xmm13
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  68,15,40,245                        ; movaps        %xmm5,%xmm14
+  DB  68,15,40,253                        ; movaps        %xmm5,%xmm15
+  DB  69,15,95,253                        ; maxps         %xmm13,%xmm15
+  DB  68,15,93,231                        ; minps         %xmm7,%xmm12
+  DB  69,15,93,244                        ; minps         %xmm12,%xmm14
+  DB  69,15,92,254                        ; subps         %xmm14,%xmm15
+  DB  69,15,40,226                        ; movaps        %xmm10,%xmm12
+  DB  68,15,93,226                        ; minps         %xmm2,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,93,236                        ; minps         %xmm12,%xmm13
+  DB  69,15,40,226                        ; movaps        %xmm10,%xmm12
+  DB  68,15,95,226                        ; maxps         %xmm2,%xmm12
+  DB  69,15,40,241                        ; movaps        %xmm9,%xmm14
+  DB  69,15,95,244                        ; maxps         %xmm12,%xmm14
+  DB  69,15,92,245                        ; subps         %xmm13,%xmm14
+  DB  69,15,92,205                        ; subps         %xmm13,%xmm9
+  DB  69,15,92,213                        ; subps         %xmm13,%xmm10
+  DB  65,15,92,213                        ; subps         %xmm13,%xmm2
+  DB  15,40,240                           ; movaps        %xmm0,%xmm6
+  DB  68,15,89,251                        ; mulps         %xmm3,%xmm15
+  DB  69,15,89,207                        ; mulps         %xmm15,%xmm9
+  DB  69,15,89,215                        ; mulps         %xmm15,%xmm10
+  DB  65,15,89,215                        ; mulps         %xmm15,%xmm2
+  DB  69,15,87,228                        ; xorps         %xmm12,%xmm12
+  DB  69,15,94,206                        ; divps         %xmm14,%xmm9
+  DB  69,15,94,214                        ; divps         %xmm14,%xmm10
+  DB  65,15,94,214                        ; divps         %xmm14,%xmm2
+  DB  69,15,194,244,4                     ; cmpneqps      %xmm12,%xmm14
+  DB  69,15,84,206                        ; andps         %xmm14,%xmm9
+  DB  69,15,84,214                        ; andps         %xmm14,%xmm10
+  DB  65,15,84,214                        ; andps         %xmm14,%xmm2
+  DB  68,15,40,61,184,72,0,0              ; movaps        0x48b8(%rip),%xmm15        # 5860 <_sk_callback_sse2+0x279>
+  DB  65,15,89,231                        ; mulps         %xmm15,%xmm4
+  DB  15,40,5,189,72,0,0                  ; movaps        0x48bd(%rip),%xmm0        # 5870 <_sk_callback_sse2+0x289>
+  DB  15,89,240                           ; mulps         %xmm0,%xmm6
+  DB  15,88,244                           ; addps         %xmm4,%xmm6
+  DB  68,15,40,53,191,72,0,0              ; movaps        0x48bf(%rip),%xmm14        # 5880 <_sk_callback_sse2+0x299>
+  DB  68,15,40,239                        ; movaps        %xmm7,%xmm13
+  DB  69,15,89,238                        ; mulps         %xmm14,%xmm13
+  DB  68,15,88,238                        ; addps         %xmm6,%xmm13
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  65,15,89,231                        ; mulps         %xmm15,%xmm4
+  DB  65,15,40,242                        ; movaps        %xmm10,%xmm6
+  DB  15,89,240                           ; mulps         %xmm0,%xmm6
+  DB  15,88,244                           ; addps         %xmm4,%xmm6
+  DB  15,40,226                           ; movaps        %xmm2,%xmm4
+  DB  65,15,89,230                        ; mulps         %xmm14,%xmm4
+  DB  15,88,230                           ; addps         %xmm6,%xmm4
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  68,15,92,236                        ; subps         %xmm4,%xmm13
+  DB  69,15,88,205                        ; addps         %xmm13,%xmm9
+  DB  69,15,88,213                        ; addps         %xmm13,%xmm10
+  DB  68,15,88,234                        ; addps         %xmm2,%xmm13
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,93,213                        ; minps         %xmm13,%xmm2
+  DB  65,15,40,241                        ; movaps        %xmm9,%xmm6
+  DB  15,93,242                           ; minps         %xmm2,%xmm6
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,95,213                        ; maxps         %xmm13,%xmm2
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  15,95,226                           ; maxps         %xmm2,%xmm4
+  DB  69,15,89,249                        ; mulps         %xmm9,%xmm15
+  DB  65,15,89,194                        ; mulps         %xmm10,%xmm0
+  DB  65,15,88,199                        ; addps         %xmm15,%xmm0
+  DB  69,15,89,245                        ; mulps         %xmm13,%xmm14
+  DB  68,15,88,240                        ; addps         %xmm0,%xmm14
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,194,214,2                        ; cmpleps       %xmm6,%xmm2
+  DB  69,15,40,254                        ; movaps        %xmm14,%xmm15
+  DB  68,15,92,254                        ; subps         %xmm6,%xmm15
+  DB  65,15,40,241                        ; movaps        %xmm9,%xmm6
+  DB  65,15,92,246                        ; subps         %xmm14,%xmm6
+  DB  65,15,89,246                        ; mulps         %xmm14,%xmm6
+  DB  65,15,94,247                        ; divps         %xmm15,%xmm6
+  DB  65,15,88,246                        ; addps         %xmm14,%xmm6
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  15,85,198                           ; andnps        %xmm6,%xmm0
+  DB  68,15,84,202                        ; andps         %xmm2,%xmm9
+  DB  68,15,86,200                        ; orps          %xmm0,%xmm9
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,41,76,36,16                      ; movaps        %xmm1,0x10(%rsp)
+  DB  15,89,193                           ; mulps         %xmm1,%xmm0
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  15,88,217                           ; addps         %xmm1,%xmm3
+  DB  15,92,216                           ; subps         %xmm0,%xmm3
+  DB  15,41,28,36                         ; movaps        %xmm3,(%rsp)
+  DB  15,40,240                           ; movaps        %xmm0,%xmm6
+  DB  15,194,196,1                        ; cmpltps       %xmm4,%xmm0
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  65,15,85,201                        ; andnps        %xmm9,%xmm1
+  DB  69,15,92,206                        ; subps         %xmm14,%xmm9
+  DB  65,15,92,246                        ; subps         %xmm14,%xmm6
+  DB  68,15,89,206                        ; mulps         %xmm6,%xmm9
+  DB  65,15,92,230                        ; subps         %xmm14,%xmm4
+  DB  68,15,94,204                        ; divps         %xmm4,%xmm9
+  DB  69,15,88,206                        ; addps         %xmm14,%xmm9
+  DB  68,15,84,200                        ; andps         %xmm0,%xmm9
+  DB  68,15,86,201                        ; orps          %xmm1,%xmm9
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  65,15,92,206                        ; subps         %xmm14,%xmm1
+  DB  65,15,89,206                        ; mulps         %xmm14,%xmm1
+  DB  65,15,94,207                        ; divps         %xmm15,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  15,40,218                           ; movaps        %xmm2,%xmm3
+  DB  15,85,217                           ; andnps        %xmm1,%xmm3
+  DB  68,15,84,210                        ; andps         %xmm2,%xmm10
+  DB  68,15,86,211                        ; orps          %xmm3,%xmm10
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  65,15,85,202                        ; andnps        %xmm10,%xmm1
+  DB  69,15,92,214                        ; subps         %xmm14,%xmm10
+  DB  68,15,89,214                        ; mulps         %xmm6,%xmm10
+  DB  68,15,94,212                        ; divps         %xmm4,%xmm10
+  DB  69,15,88,214                        ; addps         %xmm14,%xmm10
+  DB  68,15,84,208                        ; andps         %xmm0,%xmm10
+  DB  68,15,86,209                        ; orps          %xmm1,%xmm10
+  DB  65,15,40,205                        ; movaps        %xmm13,%xmm1
+  DB  65,15,92,206                        ; subps         %xmm14,%xmm1
+  DB  65,15,89,206                        ; mulps         %xmm14,%xmm1
+  DB  65,15,94,207                        ; divps         %xmm15,%xmm1
+  DB  65,15,88,206                        ; addps         %xmm14,%xmm1
+  DB  68,15,84,234                        ; andps         %xmm2,%xmm13
+  DB  15,85,209                           ; andnps        %xmm1,%xmm2
+  DB  65,15,86,213                        ; orps          %xmm13,%xmm2
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,85,202                           ; andnps        %xmm2,%xmm1
+  DB  65,15,92,214                        ; subps         %xmm14,%xmm2
+  DB  15,89,214                           ; mulps         %xmm6,%xmm2
+  DB  15,94,212                           ; divps         %xmm4,%xmm2
+  DB  65,15,88,214                        ; addps         %xmm14,%xmm2
+  DB  15,84,208                           ; andps         %xmm0,%xmm2
+  DB  15,86,209                           ; orps          %xmm1,%xmm2
+  DB  69,15,95,204                        ; maxps         %xmm12,%xmm9
+  DB  69,15,95,212                        ; maxps         %xmm12,%xmm10
+  DB  65,15,95,212                        ; maxps         %xmm12,%xmm2
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,89,197                           ; mulps         %xmm5,%xmm0
+  DB  15,40,76,36,64                      ; movaps        0x40(%rsp),%xmm1
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  15,40,116,36,32                     ; movaps        0x20(%rsp),%xmm6
+  DB  15,89,222                           ; mulps         %xmm6,%xmm3
+  DB  15,40,76,36,48                      ; movaps        0x30(%rsp),%xmm1
+  DB  15,88,203                           ; addps         %xmm3,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  69,15,88,195                        ; addps         %xmm11,%xmm8
+  DB  68,15,88,194                        ; addps         %xmm2,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,40,238                           ; movaps        %xmm6,%xmm5
+  DB  15,40,247                           ; movaps        %xmm7,%xmm6
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,40,28,36                         ; movaps        (%rsp),%xmm3
+  DB  15,40,124,36,16                     ; movaps        0x10(%rsp),%xmm7
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_saturation_sse2
+_sk_saturation_sse2 LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  68,15,40,231                        ; movaps        %xmm7,%xmm12
+  DB  68,15,40,198                        ; movaps        %xmm6,%xmm8
+  DB  15,40,251                           ; movaps        %xmm3,%xmm7
+  DB  68,15,40,216                        ; movaps        %xmm0,%xmm11
+  DB  68,15,40,215                        ; movaps        %xmm7,%xmm10
+  DB  68,15,89,212                        ; mulps         %xmm4,%xmm10
+  DB  15,40,244                           ; movaps        %xmm4,%xmm6
+  DB  15,41,116,36,16                     ; movaps        %xmm6,0x10(%rsp)
+  DB  68,15,40,207                        ; movaps        %xmm7,%xmm9
+  DB  68,15,89,205                        ; mulps         %xmm5,%xmm9
+  DB  15,41,108,36,32                     ; movaps        %xmm5,0x20(%rsp)
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  69,15,40,232                        ; movaps        %xmm8,%xmm13
+  DB  68,15,41,44,36                      ; movaps        %xmm13,(%rsp)
+  DB  15,40,225                           ; movaps        %xmm1,%xmm4
+  DB  15,41,100,36,48                     ; movaps        %xmm4,0x30(%rsp)
+  DB  15,40,220                           ; movaps        %xmm4,%xmm3
+  DB  15,41,84,36,64                      ; movaps        %xmm2,0x40(%rsp)
+  DB  15,95,218                           ; maxps         %xmm2,%xmm3
+  DB  65,15,40,203                        ; movaps        %xmm11,%xmm1
+  DB  15,95,203                           ; maxps         %xmm3,%xmm1
+  DB  15,40,220                           ; movaps        %xmm4,%xmm3
+  DB  15,93,218                           ; minps         %xmm2,%xmm3
+  DB  65,15,40,211                        ; movaps        %xmm11,%xmm2
+  DB  15,93,211                           ; minps         %xmm3,%xmm2
+  DB  15,92,202                           ; subps         %xmm2,%xmm1
+  DB  65,15,89,204                        ; mulps         %xmm12,%xmm1
+  DB  65,15,40,228                        ; movaps        %xmm12,%xmm4
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,93,208                           ; minps         %xmm0,%xmm2
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  68,15,93,194                        ; minps         %xmm2,%xmm8
+  DB  65,15,40,209                        ; movaps        %xmm9,%xmm2
+  DB  15,95,208                           ; maxps         %xmm0,%xmm2
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,95,218                           ; maxps         %xmm2,%xmm3
+  DB  65,15,92,216                        ; subps         %xmm8,%xmm3
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  69,15,92,208                        ; subps         %xmm8,%xmm10
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  68,15,94,211                        ; divps         %xmm3,%xmm10
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  68,15,94,203                        ; divps         %xmm3,%xmm9
+  DB  65,15,92,192                        ; subps         %xmm8,%xmm0
+  DB  15,89,193                           ; mulps         %xmm1,%xmm0
+  DB  15,94,195                           ; divps         %xmm3,%xmm0
+  DB  15,194,218,4                        ; cmpneqps      %xmm2,%xmm3
+  DB  68,15,84,211                        ; andps         %xmm3,%xmm10
+  DB  68,15,84,203                        ; andps         %xmm3,%xmm9
+  DB  15,84,195                           ; andps         %xmm3,%xmm0
+  DB  68,15,40,5,76,70,0,0                ; movaps        0x464c(%rip),%xmm8        # 58a0 <_sk_callback_sse2+0x2b9>
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,40,13,78,70,0,0                  ; movaps        0x464e(%rip),%xmm1        # 58b0 <_sk_callback_sse2+0x2c9>
+  DB  15,40,221                           ; movaps        %xmm5,%xmm3
+  DB  15,89,217                           ; mulps         %xmm1,%xmm3
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  68,15,40,37,77,70,0,0               ; movaps        0x464d(%rip),%xmm12        # 58c0 <_sk_callback_sse2+0x2d9>
+  DB  69,15,89,236                        ; mulps         %xmm12,%xmm13
+  DB  68,15,88,235                        ; addps         %xmm3,%xmm13
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  65,15,40,217                        ; movaps        %xmm9,%xmm3
+  DB  15,89,217                           ; mulps         %xmm1,%xmm3
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  65,15,89,212                        ; mulps         %xmm12,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  68,15,89,239                        ; mulps         %xmm7,%xmm13
+  DB  68,15,92,234                        ; subps         %xmm2,%xmm13
+  DB  69,15,88,213                        ; addps         %xmm13,%xmm10
+  DB  69,15,88,205                        ; addps         %xmm13,%xmm9
+  DB  68,15,88,232                        ; addps         %xmm0,%xmm13
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,93,197                        ; minps         %xmm13,%xmm0
+  DB  65,15,40,218                        ; movaps        %xmm10,%xmm3
+  DB  15,93,216                           ; minps         %xmm0,%xmm3
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,95,197                        ; maxps         %xmm13,%xmm0
+  DB  69,15,40,242                        ; movaps        %xmm10,%xmm14
+  DB  68,15,95,240                        ; maxps         %xmm0,%xmm14
+  DB  69,15,89,194                        ; mulps         %xmm10,%xmm8
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  68,15,88,225                        ; addps         %xmm1,%xmm12
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,194,211,2                        ; cmpleps       %xmm3,%xmm2
+  DB  65,15,40,244                        ; movaps        %xmm12,%xmm6
+  DB  15,92,243                           ; subps         %xmm3,%xmm6
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  65,15,92,196                        ; subps         %xmm12,%xmm0
+  DB  65,15,89,196                        ; mulps         %xmm12,%xmm0
+  DB  15,94,198                           ; divps         %xmm6,%xmm0
+  DB  65,15,88,196                        ; addps         %xmm12,%xmm0
+  DB  15,40,202                           ; movaps        %xmm2,%xmm1
+  DB  15,85,200                           ; andnps        %xmm0,%xmm1
+  DB  68,15,84,210                        ; andps         %xmm2,%xmm10
+  DB  68,15,86,209                        ; orps          %xmm1,%xmm10
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,89,221                           ; mulps         %xmm5,%xmm3
+  DB  68,15,40,5,178,69,0,0               ; movaps        0x45b2(%rip),%xmm8        # 58d0 <_sk_callback_sse2+0x2e9>
+  DB  65,15,40,224                        ; movaps        %xmm8,%xmm4
+  DB  68,15,92,199                        ; subps         %xmm7,%xmm8
+  DB  15,88,253                           ; addps         %xmm5,%xmm7
+  DB  15,92,251                           ; subps         %xmm3,%xmm7
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  65,15,194,222,1                     ; cmpltps       %xmm14,%xmm3
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  65,15,85,194                        ; andnps        %xmm10,%xmm0
+  DB  69,15,92,212                        ; subps         %xmm12,%xmm10
+  DB  65,15,92,204                        ; subps         %xmm12,%xmm1
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  69,15,92,244                        ; subps         %xmm12,%xmm14
+  DB  69,15,94,214                        ; divps         %xmm14,%xmm10
+  DB  69,15,88,212                        ; addps         %xmm12,%xmm10
+  DB  68,15,84,211                        ; andps         %xmm3,%xmm10
+  DB  68,15,86,208                        ; orps          %xmm0,%xmm10
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,92,196                        ; subps         %xmm12,%xmm0
+  DB  65,15,89,196                        ; mulps         %xmm12,%xmm0
+  DB  15,94,198                           ; divps         %xmm6,%xmm0
+  DB  65,15,88,196                        ; addps         %xmm12,%xmm0
+  DB  68,15,40,250                        ; movaps        %xmm2,%xmm15
+  DB  68,15,85,248                        ; andnps        %xmm0,%xmm15
+  DB  68,15,84,202                        ; andps         %xmm2,%xmm9
+  DB  69,15,86,207                        ; orps          %xmm15,%xmm9
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  65,15,85,193                        ; andnps        %xmm9,%xmm0
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  69,15,94,206                        ; divps         %xmm14,%xmm9
+  DB  69,15,88,204                        ; addps         %xmm12,%xmm9
+  DB  68,15,84,203                        ; andps         %xmm3,%xmm9
+  DB  68,15,86,200                        ; orps          %xmm0,%xmm9
+  DB  65,15,40,197                        ; movaps        %xmm13,%xmm0
+  DB  65,15,92,196                        ; subps         %xmm12,%xmm0
+  DB  65,15,89,196                        ; mulps         %xmm12,%xmm0
+  DB  15,94,198                           ; divps         %xmm6,%xmm0
+  DB  65,15,88,196                        ; addps         %xmm12,%xmm0
+  DB  68,15,84,234                        ; andps         %xmm2,%xmm13
+  DB  15,85,208                           ; andnps        %xmm0,%xmm2
+  DB  65,15,86,213                        ; orps          %xmm13,%xmm2
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,85,194                           ; andnps        %xmm2,%xmm0
+  DB  65,15,92,212                        ; subps         %xmm12,%xmm2
+  DB  15,89,209                           ; mulps         %xmm1,%xmm2
+  DB  65,15,94,214                        ; divps         %xmm14,%xmm2
+  DB  65,15,88,212                        ; addps         %xmm12,%xmm2
+  DB  15,84,211                           ; andps         %xmm3,%xmm2
+  DB  15,86,208                           ; orps          %xmm0,%xmm2
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  68,15,95,208                        ; maxps         %xmm0,%xmm10
+  DB  68,15,95,200                        ; maxps         %xmm0,%xmm9
+  DB  15,95,208                           ; maxps         %xmm0,%xmm2
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,40,92,36,16                      ; movaps        0x10(%rsp),%xmm3
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  68,15,40,229                        ; movaps        %xmm5,%xmm12
+  DB  65,15,92,228                        ; subps         %xmm12,%xmm4
+  DB  68,15,89,220                        ; mulps         %xmm4,%xmm11
+  DB  68,15,88,216                        ; addps         %xmm0,%xmm11
+  DB  69,15,88,218                        ; addps         %xmm10,%xmm11
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,40,108,36,32                     ; movaps        0x20(%rsp),%xmm5
+  DB  15,89,197                           ; mulps         %xmm5,%xmm0
+  DB  15,40,76,36,48                      ; movaps        0x30(%rsp),%xmm1
+  DB  15,89,204                           ; mulps         %xmm4,%xmm1
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  15,89,100,36,64                     ; mulps         0x40(%rsp),%xmm4
+  DB  15,40,4,36                          ; movaps        (%rsp),%xmm0
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  68,15,88,196                        ; addps         %xmm4,%xmm8
+  DB  68,15,88,194                        ; addps         %xmm2,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,227                           ; movaps        %xmm3,%xmm4
+  DB  15,40,240                           ; movaps        %xmm0,%xmm6
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  65,15,40,252                        ; movaps        %xmm12,%xmm7
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_color_sse2
+_sk_color_sse2 LABEL PROC
+  DB  72,131,236,72                       ; sub           $0x48,%rsp
+  DB  68,15,40,199                        ; movaps        %xmm7,%xmm8
+  DB  68,15,40,230                        ; movaps        %xmm6,%xmm12
+  DB  68,15,41,100,36,32                  ; movaps        %xmm12,0x20(%rsp)
+  DB  68,15,40,221                        ; movaps        %xmm5,%xmm11
+  DB  68,15,41,92,36,48                   ; movaps        %xmm11,0x30(%rsp)
+  DB  15,40,244                           ; movaps        %xmm4,%xmm6
+  DB  15,41,20,36                         ; movaps        %xmm2,(%rsp)
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  15,40,248                           ; movaps        %xmm0,%xmm7
+  DB  68,15,40,207                        ; movaps        %xmm7,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  68,15,40,213                        ; movaps        %xmm5,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  68,15,40,45,74,68,0,0               ; movaps        0x444a(%rip),%xmm13        # 58e0 <_sk_callback_sse2+0x2f9>
+  DB  68,15,40,198                        ; movaps        %xmm6,%xmm8
+  DB  69,15,89,197                        ; mulps         %xmm13,%xmm8
+  DB  68,15,40,53,74,68,0,0               ; movaps        0x444a(%rip),%xmm14        # 58f0 <_sk_callback_sse2+0x309>
+  DB  65,15,40,195                        ; movaps        %xmm11,%xmm0
+  DB  65,15,89,198                        ; mulps         %xmm14,%xmm0
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,29,70,68,0,0               ; movaps        0x4446(%rip),%xmm11        # 5900 <_sk_callback_sse2+0x319>
+  DB  69,15,89,227                        ; mulps         %xmm11,%xmm12
+  DB  68,15,88,224                        ; addps         %xmm0,%xmm12
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,89,197                        ; mulps         %xmm13,%xmm0
+  DB  69,15,40,250                        ; movaps        %xmm10,%xmm15
+  DB  69,15,89,254                        ; mulps         %xmm14,%xmm15
+  DB  68,15,88,248                        ; addps         %xmm0,%xmm15
+  DB  68,15,40,5,50,68,0,0                ; movaps        0x4432(%rip),%xmm8        # 5910 <_sk_callback_sse2+0x329>
+  DB  65,15,40,224                        ; movaps        %xmm8,%xmm4
+  DB  15,92,226                           ; subps         %xmm2,%xmm4
+  DB  15,89,252                           ; mulps         %xmm4,%xmm7
+  DB  15,89,236                           ; mulps         %xmm4,%xmm5
+  DB  15,40,12,36                         ; movaps        (%rsp),%xmm1
+  DB  15,89,225                           ; mulps         %xmm1,%xmm4
+  DB  15,89,202                           ; mulps         %xmm2,%xmm1
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,89,195                        ; mulps         %xmm11,%xmm0
+  DB  65,15,88,199                        ; addps         %xmm15,%xmm0
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  68,15,92,224                        ; subps         %xmm0,%xmm12
+  DB  69,15,88,204                        ; addps         %xmm12,%xmm9
+  DB  69,15,88,212                        ; addps         %xmm12,%xmm10
+  DB  68,15,88,225                        ; addps         %xmm1,%xmm12
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  65,15,93,196                        ; minps         %xmm12,%xmm0
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  15,93,200                           ; minps         %xmm0,%xmm1
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  65,15,95,196                        ; maxps         %xmm12,%xmm0
+  DB  69,15,40,249                        ; movaps        %xmm9,%xmm15
+  DB  68,15,95,248                        ; maxps         %xmm0,%xmm15
+  DB  69,15,89,233                        ; mulps         %xmm9,%xmm13
+  DB  69,15,89,242                        ; mulps         %xmm10,%xmm14
+  DB  69,15,88,245                        ; addps         %xmm13,%xmm14
+  DB  69,15,89,220                        ; mulps         %xmm12,%xmm11
+  DB  69,15,88,222                        ; addps         %xmm14,%xmm11
+  DB  69,15,87,237                        ; xorps         %xmm13,%xmm13
+  DB  68,15,194,233,2                     ; cmpleps       %xmm1,%xmm13
+  DB  69,15,40,243                        ; movaps        %xmm11,%xmm14
+  DB  68,15,92,241                        ; subps         %xmm1,%xmm14
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  65,15,94,206                        ; divps         %xmm14,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  65,15,40,197                        ; movaps        %xmm13,%xmm0
+  DB  15,85,193                           ; andnps        %xmm1,%xmm0
+  DB  69,15,84,205                        ; andps         %xmm13,%xmm9
+  DB  68,15,86,200                        ; orps          %xmm0,%xmm9
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  15,41,84,36,16                      ; movaps        %xmm2,0x10(%rsp)
+  DB  15,89,194                           ; mulps         %xmm2,%xmm0
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  15,92,216                           ; subps         %xmm0,%xmm3
+  DB  15,41,28,36                         ; movaps        %xmm3,(%rsp)
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  65,15,194,199,1                     ; cmpltps       %xmm15,%xmm0
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  65,15,85,201                        ; andnps        %xmm9,%xmm1
+  DB  69,15,92,203                        ; subps         %xmm11,%xmm9
+  DB  65,15,92,219                        ; subps         %xmm11,%xmm3
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  69,15,92,251                        ; subps         %xmm11,%xmm15
+  DB  69,15,94,207                        ; divps         %xmm15,%xmm9
+  DB  69,15,88,203                        ; addps         %xmm11,%xmm9
+  DB  68,15,84,200                        ; andps         %xmm0,%xmm9
+  DB  68,15,86,201                        ; orps          %xmm1,%xmm9
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  65,15,94,206                        ; divps         %xmm14,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  65,15,40,213                        ; movaps        %xmm13,%xmm2
+  DB  15,85,209                           ; andnps        %xmm1,%xmm2
+  DB  69,15,84,213                        ; andps         %xmm13,%xmm10
+  DB  68,15,86,210                        ; orps          %xmm2,%xmm10
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  65,15,85,202                        ; andnps        %xmm10,%xmm1
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  69,15,94,215                        ; divps         %xmm15,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  68,15,84,208                        ; andps         %xmm0,%xmm10
+  DB  68,15,86,209                        ; orps          %xmm1,%xmm10
+  DB  65,15,40,204                        ; movaps        %xmm12,%xmm1
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  65,15,94,206                        ; divps         %xmm14,%xmm1
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  69,15,84,229                        ; andps         %xmm13,%xmm12
+  DB  68,15,85,233                        ; andnps        %xmm1,%xmm13
+  DB  69,15,86,236                        ; orps          %xmm12,%xmm13
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  65,15,85,205                        ; andnps        %xmm13,%xmm1
+  DB  69,15,92,235                        ; subps         %xmm11,%xmm13
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,94,239                        ; divps         %xmm15,%xmm13
+  DB  69,15,88,235                        ; addps         %xmm11,%xmm13
+  DB  68,15,84,232                        ; andps         %xmm0,%xmm13
+  DB  68,15,86,233                        ; orps          %xmm1,%xmm13
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  68,15,95,200                        ; maxps         %xmm0,%xmm9
+  DB  68,15,95,208                        ; maxps         %xmm0,%xmm10
+  DB  68,15,95,232                        ; maxps         %xmm0,%xmm13
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,89,198                           ; mulps         %xmm6,%xmm0
+  DB  15,88,248                           ; addps         %xmm0,%xmm7
+  DB  65,15,88,249                        ; addps         %xmm9,%xmm7
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,40,84,36,48                      ; movaps        0x30(%rsp),%xmm2
+  DB  15,89,194                           ; mulps         %xmm2,%xmm0
+  DB  15,88,232                           ; addps         %xmm0,%xmm5
+  DB  65,15,88,234                        ; addps         %xmm10,%xmm5
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,68,36,32                      ; movaps        0x20(%rsp),%xmm0
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  68,15,88,196                        ; addps         %xmm4,%xmm8
+  DB  69,15,88,197                        ; addps         %xmm13,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,230                           ; movaps        %xmm6,%xmm4
+  DB  15,40,234                           ; movaps        %xmm2,%xmm5
+  DB  15,40,240                           ; movaps        %xmm0,%xmm6
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,40,28,36                         ; movaps        (%rsp),%xmm3
+  DB  15,40,124,36,16                     ; movaps        0x10(%rsp),%xmm7
+  DB  72,131,196,72                       ; add           $0x48,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminosity_sse2
+_sk_luminosity_sse2 LABEL PROC
+  DB  72,131,236,88                       ; sub           $0x58,%rsp
+  DB  68,15,40,215                        ; movaps        %xmm7,%xmm10
+  DB  15,41,116,36,32                     ; movaps        %xmm6,0x20(%rsp)
+  DB  15,40,245                           ; movaps        %xmm5,%xmm6
+  DB  15,41,116,36,64                     ; movaps        %xmm6,0x40(%rsp)
+  DB  15,41,100,36,48                     ; movaps        %xmm4,0x30(%rsp)
+  DB  15,40,235                           ; movaps        %xmm3,%xmm5
+  DB  15,40,248                           ; movaps        %xmm0,%xmm7
+  DB  68,15,40,205                        ; movaps        %xmm5,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  15,89,222                           ; mulps         %xmm6,%xmm3
+  DB  68,15,40,37,68,66,0,0               ; movaps        0x4244(%rip),%xmm12        # 5920 <_sk_callback_sse2+0x339>
+  DB  68,15,40,199                        ; movaps        %xmm7,%xmm8
+  DB  69,15,89,196                        ; mulps         %xmm12,%xmm8
+  DB  68,15,40,45,68,66,0,0               ; movaps        0x4244(%rip),%xmm13        # 5930 <_sk_callback_sse2+0x349>
+  DB  68,15,40,241                        ; movaps        %xmm1,%xmm14
+  DB  69,15,89,245                        ; mulps         %xmm13,%xmm14
+  DB  69,15,88,240                        ; addps         %xmm8,%xmm14
+  DB  68,15,40,29,64,66,0,0               ; movaps        0x4240(%rip),%xmm11        # 5940 <_sk_callback_sse2+0x359>
+  DB  68,15,40,5,72,66,0,0                ; movaps        0x4248(%rip),%xmm8        # 5950 <_sk_callback_sse2+0x369>
+  DB  69,15,40,248                        ; movaps        %xmm8,%xmm15
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  68,15,92,248                        ; subps         %xmm0,%xmm15
+  DB  65,15,89,255                        ; mulps         %xmm15,%xmm7
+  DB  65,15,89,207                        ; mulps         %xmm15,%xmm1
+  DB  15,41,76,36,16                      ; movaps        %xmm1,0x10(%rsp)
+  DB  68,15,89,250                        ; mulps         %xmm2,%xmm15
+  DB  65,15,89,211                        ; mulps         %xmm11,%xmm2
+  DB  65,15,88,214                        ; addps         %xmm14,%xmm2
+  DB  69,15,40,241                        ; movaps        %xmm9,%xmm14
+  DB  69,15,89,244                        ; mulps         %xmm12,%xmm14
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  69,15,89,213                        ; mulps         %xmm13,%xmm10
+  DB  69,15,88,214                        ; addps         %xmm14,%xmm10
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,40,116,36,32                     ; movaps        0x20(%rsp),%xmm6
+  DB  15,89,230                           ; mulps         %xmm6,%xmm4
+  DB  68,15,40,244                        ; movaps        %xmm4,%xmm14
+  DB  69,15,89,243                        ; mulps         %xmm11,%xmm14
+  DB  69,15,88,242                        ; addps         %xmm10,%xmm14
+  DB  15,89,208                           ; mulps         %xmm0,%xmm2
+  DB  65,15,92,214                        ; subps         %xmm14,%xmm2
+  DB  68,15,88,202                        ; addps         %xmm2,%xmm9
+  DB  15,88,218                           ; addps         %xmm2,%xmm3
+  DB  15,88,212                           ; addps         %xmm4,%xmm2
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,93,210                        ; minps         %xmm2,%xmm10
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  65,15,93,226                        ; minps         %xmm10,%xmm4
+  DB  68,15,40,211                        ; movaps        %xmm3,%xmm10
+  DB  68,15,95,210                        ; maxps         %xmm2,%xmm10
+  DB  69,15,40,241                        ; movaps        %xmm9,%xmm14
+  DB  69,15,95,242                        ; maxps         %xmm10,%xmm14
+  DB  69,15,89,225                        ; mulps         %xmm9,%xmm12
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,236                        ; addps         %xmm12,%xmm13
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,221                        ; addps         %xmm13,%xmm11
+  DB  69,15,87,228                        ; xorps         %xmm12,%xmm12
+  DB  68,15,194,228,2                     ; cmpleps       %xmm4,%xmm12
+  DB  69,15,40,211                        ; movaps        %xmm11,%xmm10
+  DB  68,15,92,212                        ; subps         %xmm4,%xmm10
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  65,15,92,227                        ; subps         %xmm11,%xmm4
+  DB  65,15,89,227                        ; mulps         %xmm11,%xmm4
+  DB  65,15,94,226                        ; divps         %xmm10,%xmm4
+  DB  65,15,88,227                        ; addps         %xmm11,%xmm4
+  DB  69,15,40,236                        ; movaps        %xmm12,%xmm13
+  DB  68,15,85,236                        ; andnps        %xmm4,%xmm13
+  DB  69,15,84,204                        ; andps         %xmm12,%xmm9
+  DB  69,15,86,205                        ; orps          %xmm13,%xmm9
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,41,4,36                          ; movaps        %xmm0,(%rsp)
+  DB  15,89,224                           ; mulps         %xmm0,%xmm4
+  DB  68,15,92,197                        ; subps         %xmm5,%xmm8
+  DB  15,88,232                           ; addps         %xmm0,%xmm5
+  DB  15,92,236                           ; subps         %xmm4,%xmm5
+  DB  68,15,40,236                        ; movaps        %xmm4,%xmm13
+  DB  65,15,194,230,1                     ; cmpltps       %xmm14,%xmm4
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  65,15,85,193                        ; andnps        %xmm9,%xmm0
+  DB  69,15,92,203                        ; subps         %xmm11,%xmm9
+  DB  69,15,92,235                        ; subps         %xmm11,%xmm13
+  DB  69,15,89,205                        ; mulps         %xmm13,%xmm9
+  DB  69,15,92,243                        ; subps         %xmm11,%xmm14
+  DB  69,15,94,206                        ; divps         %xmm14,%xmm9
+  DB  69,15,88,203                        ; addps         %xmm11,%xmm9
+  DB  68,15,84,204                        ; andps         %xmm4,%xmm9
+  DB  68,15,86,200                        ; orps          %xmm0,%xmm9
+  DB  15,40,195                           ; movaps        %xmm3,%xmm0
+  DB  65,15,92,195                        ; subps         %xmm11,%xmm0
+  DB  65,15,89,195                        ; mulps         %xmm11,%xmm0
+  DB  65,15,94,194                        ; divps         %xmm10,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  65,15,40,204                        ; movaps        %xmm12,%xmm1
+  DB  15,85,200                           ; andnps        %xmm0,%xmm1
+  DB  65,15,84,220                        ; andps         %xmm12,%xmm3
+  DB  15,86,217                           ; orps          %xmm1,%xmm3
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,85,195                           ; andnps        %xmm3,%xmm0
+  DB  65,15,92,219                        ; subps         %xmm11,%xmm3
+  DB  65,15,89,221                        ; mulps         %xmm13,%xmm3
+  DB  65,15,94,222                        ; divps         %xmm14,%xmm3
+  DB  65,15,88,219                        ; addps         %xmm11,%xmm3
+  DB  15,84,220                           ; andps         %xmm4,%xmm3
+  DB  15,86,216                           ; orps          %xmm0,%xmm3
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  65,15,92,195                        ; subps         %xmm11,%xmm0
+  DB  65,15,89,195                        ; mulps         %xmm11,%xmm0
+  DB  65,15,94,194                        ; divps         %xmm10,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  65,15,84,212                        ; andps         %xmm12,%xmm2
+  DB  68,15,85,224                        ; andnps        %xmm0,%xmm12
+  DB  68,15,86,226                        ; orps          %xmm2,%xmm12
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  65,15,85,196                        ; andnps        %xmm12,%xmm0
+  DB  69,15,92,227                        ; subps         %xmm11,%xmm12
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,94,230                        ; divps         %xmm14,%xmm12
+  DB  69,15,88,227                        ; addps         %xmm11,%xmm12
+  DB  68,15,84,228                        ; andps         %xmm4,%xmm12
+  DB  68,15,86,224                        ; orps          %xmm0,%xmm12
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  68,15,95,200                        ; maxps         %xmm0,%xmm9
+  DB  15,95,216                           ; maxps         %xmm0,%xmm3
+  DB  68,15,95,224                        ; maxps         %xmm0,%xmm12
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,40,100,36,48                     ; movaps        0x30(%rsp),%xmm4
+  DB  15,89,196                           ; mulps         %xmm4,%xmm0
+  DB  15,88,248                           ; addps         %xmm0,%xmm7
+  DB  65,15,88,249                        ; addps         %xmm9,%xmm7
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  68,15,40,84,36,64                   ; movaps        0x40(%rsp),%xmm10
+  DB  65,15,89,194                        ; mulps         %xmm10,%xmm0
+  DB  15,40,76,36,16                      ; movaps        0x10(%rsp),%xmm1
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  15,88,203                           ; addps         %xmm3,%xmm1
+  DB  68,15,89,198                        ; mulps         %xmm6,%xmm8
+  DB  69,15,88,199                        ; addps         %xmm15,%xmm8
+  DB  69,15,88,196                        ; addps         %xmm12,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,199                           ; movaps        %xmm7,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,40,221                           ; movaps        %xmm5,%xmm3
+  DB  65,15,40,234                        ; movaps        %xmm10,%xmm5
+  DB  15,40,60,36                         ; movaps        (%rsp),%xmm7
+  DB  72,131,196,88                       ; add           $0x58,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_rgba_8888_sse2
+_sk_srcover_rgba_8888_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,227,0,0,0                    ; jne           19e2 <_sk_srcover_rgba_8888_sse2+0xf1>
+  DB  243,68,15,111,4,144                 ; movdqu        (%rax,%rdx,4),%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  102,15,111,53,80,64,0,0             ; movdqa        0x4050(%rip),%xmm6        # 5960 <_sk_callback_sse2+0x379>
+  DB  102,65,15,111,224                   ; movdqa        %xmm8,%xmm4
+  DB  102,15,219,230                      ; pand          %xmm6,%xmm4
+  DB  15,91,228                           ; cvtdq2ps      %xmm4,%xmm4
+  DB  102,65,15,111,232                   ; movdqa        %xmm8,%xmm5
+  DB  102,15,114,213,8                    ; psrld         $0x8,%xmm5
+  DB  102,15,219,238                      ; pand          %xmm6,%xmm5
+  DB  15,91,237                           ; cvtdq2ps      %xmm5,%xmm5
+  DB  102,65,15,111,248                   ; movdqa        %xmm8,%xmm7
+  DB  102,15,114,215,16                   ; psrld         $0x10,%xmm7
+  DB  102,15,219,254                      ; pand          %xmm6,%xmm7
+  DB  15,91,247                           ; cvtdq2ps      %xmm7,%xmm6
+  DB  102,65,15,114,208,24                ; psrld         $0x18,%xmm8
+  DB  65,15,91,248                        ; cvtdq2ps      %xmm8,%xmm7
+  DB  68,15,40,5,32,64,0,0                ; movaps        0x4020(%rip),%xmm8        # 5970 <_sk_callback_sse2+0x389>
+  DB  68,15,92,195                        ; subps         %xmm3,%xmm8
+  DB  68,15,40,37,36,64,0,0               ; movaps        0x4024(%rip),%xmm12        # 5980 <_sk_callback_sse2+0x399>
+  DB  65,15,89,196                        ; mulps         %xmm12,%xmm0
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  68,15,89,204                        ; mulps         %xmm4,%xmm9
+  DB  68,15,88,200                        ; addps         %xmm0,%xmm9
+  DB  65,15,89,204                        ; mulps         %xmm12,%xmm1
+  DB  69,15,40,208                        ; movaps        %xmm8,%xmm10
+  DB  68,15,89,213                        ; mulps         %xmm5,%xmm10
+  DB  68,15,88,209                        ; addps         %xmm1,%xmm10
+  DB  65,15,89,212                        ; mulps         %xmm12,%xmm2
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  68,15,89,222                        ; mulps         %xmm6,%xmm11
+  DB  68,15,88,218                        ; addps         %xmm2,%xmm11
+  DB  65,15,89,220                        ; mulps         %xmm12,%xmm3
+  DB  68,15,89,199                        ; mulps         %xmm7,%xmm8
+  DB  68,15,88,195                        ; addps         %xmm3,%xmm8
+  DB  102,65,15,91,193                    ; cvtps2dq      %xmm9,%xmm0
+  DB  102,65,15,91,202                    ; cvtps2dq      %xmm10,%xmm1
+  DB  102,15,114,241,8                    ; pslld         $0x8,%xmm1
+  DB  102,15,235,200                      ; por           %xmm0,%xmm1
+  DB  102,65,15,91,211                    ; cvtps2dq      %xmm11,%xmm2
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,65,15,91,192                    ; cvtps2dq      %xmm8,%xmm0
+  DB  102,15,114,240,24                   ; pslld         $0x18,%xmm0
+  DB  102,15,235,194                      ; por           %xmm2,%xmm0
+  DB  102,15,235,193                      ; por           %xmm1,%xmm0
+  DB  117,106                             ; jne           1a33 <_sk_srcover_rgba_8888_sse2+0x142>
+  DB  243,15,127,4,144                    ; movdqu        %xmm0,(%rax,%rdx,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  65,15,40,211                        ; movaps        %xmm11,%xmm2
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,48                              ; je            1a24 <_sk_srcover_rgba_8888_sse2+0x133>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,22                              ; je            1a10 <_sk_srcover_rgba_8888_sse2+0x11f>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  15,133,1,255,255,255                ; jne           1905 <_sk_srcover_rgba_8888_sse2+0x14>
+  DB  102,15,110,100,144,8                ; movd          0x8(%rax,%rdx,4),%xmm4
+  DB  102,68,15,112,196,69                ; pshufd        $0x45,%xmm4,%xmm8
+  DB  243,15,16,100,144,4                 ; movss         0x4(%rax,%rdx,4),%xmm4
+  DB  65,15,198,224,0                     ; shufps        $0x0,%xmm8,%xmm4
+  DB  65,15,198,224,226                   ; shufps        $0xe2,%xmm8,%xmm4
+  DB  68,15,40,196                        ; movaps        %xmm4,%xmm8
+  DB  243,15,16,36,144                    ; movss         (%rax,%rdx,4),%xmm4
+  DB  243,68,15,16,196                    ; movss         %xmm4,%xmm8
+  DB  233,210,254,255,255                 ; jmpq          1905 <_sk_srcover_rgba_8888_sse2+0x14>
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,34                              ; je            1a62 <_sk_srcover_rgba_8888_sse2+0x171>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,17                              ; je            1a57 <_sk_srcover_rgba_8888_sse2+0x166>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,130                             ; jne           19ce <_sk_srcover_rgba_8888_sse2+0xdd>
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,15,126,76,144,8                 ; movd          %xmm1,0x8(%rax,%rdx,4)
+  DB  102,15,112,200,229                  ; pshufd        $0xe5,%xmm0,%xmm1
+  DB  102,15,126,76,144,4                 ; movd          %xmm1,0x4(%rax,%rdx,4)
+  DB  102,15,126,4,144                    ; movd          %xmm0,(%rax,%rdx,4)
+  DB  233,98,255,255,255                  ; jmpq          19ce <_sk_srcover_rgba_8888_sse2+0xdd>
+
+PUBLIC _sk_clamp_0_sse2
+_sk_clamp_0_sse2 LABEL PROC
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  65,15,95,192                        ; maxps         %xmm8,%xmm0
+  DB  65,15,95,200                        ; maxps         %xmm8,%xmm1
+  DB  65,15,95,208                        ; maxps         %xmm8,%xmm2
+  DB  65,15,95,216                        ; maxps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_1_sse2
+_sk_clamp_1_sse2 LABEL PROC
+  DB  68,15,40,5,4,63,0,0                 ; movaps        0x3f04(%rip),%xmm8        # 5990 <_sk_callback_sse2+0x3a9>
+  DB  65,15,93,192                        ; minps         %xmm8,%xmm0
+  DB  65,15,93,200                        ; minps         %xmm8,%xmm1
+  DB  65,15,93,208                        ; minps         %xmm8,%xmm2
+  DB  65,15,93,216                        ; minps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_a_sse2
+_sk_clamp_a_sse2 LABEL PROC
+  DB  15,93,29,249,62,0,0                 ; minps         0x3ef9(%rip),%xmm3        # 59a0 <_sk_callback_sse2+0x3b9>
+  DB  15,93,195                           ; minps         %xmm3,%xmm0
+  DB  15,93,203                           ; minps         %xmm3,%xmm1
+  DB  15,93,211                           ; minps         %xmm3,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_set_rgb_sse2
+_sk_set_rgb_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_rb_sse2
+_sk_swap_rb_sse2 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_sse2
+_sk_swap_sse2 LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  68,15,40,216                        ; movaps        %xmm0,%xmm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  65,15,40,227                        ; movaps        %xmm11,%xmm4
+  DB  65,15,40,234                        ; movaps        %xmm10,%xmm5
+  DB  65,15,40,241                        ; movaps        %xmm9,%xmm6
+  DB  65,15,40,248                        ; movaps        %xmm8,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_src_dst_sse2
+_sk_move_src_dst_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,224                           ; movaps        %xmm0,%xmm4
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  15,40,242                           ; movaps        %xmm2,%xmm6
+  DB  15,40,251                           ; movaps        %xmm3,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_dst_src_sse2
+_sk_move_dst_src_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_premul_sse2
+_sk_premul_sse2 LABEL PROC
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,89,203                           ; mulps         %xmm3,%xmm1
+  DB  15,89,211                           ; mulps         %xmm3,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_unpremul_sse2
+_sk_unpremul_sse2 LABEL PROC
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,40,13,100,62,0,0              ; movaps        0x3e64(%rip),%xmm9        # 59b0 <_sk_callback_sse2+0x3c9>
+  DB  68,15,94,203                        ; divps         %xmm3,%xmm9
+  DB  68,15,194,195,4                     ; cmpneqps      %xmm3,%xmm8
+  DB  69,15,84,193                        ; andps         %xmm9,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_from_srgb_sse2
+_sk_from_srgb_sse2 LABEL PROC
+  DB  68,15,40,5,79,62,0,0                ; movaps        0x3e4f(%rip),%xmm8        # 59c0 <_sk_callback_sse2+0x3d9>
+  DB  68,15,40,232                        ; movaps        %xmm0,%xmm13
+  DB  69,15,89,232                        ; mulps         %xmm8,%xmm13
+  DB  68,15,40,216                        ; movaps        %xmm0,%xmm11
+  DB  69,15,89,219                        ; mulps         %xmm11,%xmm11
+  DB  68,15,40,13,71,62,0,0               ; movaps        0x3e47(%rip),%xmm9        # 59d0 <_sk_callback_sse2+0x3e9>
+  DB  68,15,40,240                        ; movaps        %xmm0,%xmm14
+  DB  69,15,89,241                        ; mulps         %xmm9,%xmm14
+  DB  68,15,40,21,71,62,0,0               ; movaps        0x3e47(%rip),%xmm10        # 59e0 <_sk_callback_sse2+0x3f9>
+  DB  69,15,88,242                        ; addps         %xmm10,%xmm14
+  DB  69,15,89,243                        ; mulps         %xmm11,%xmm14
+  DB  68,15,40,29,71,62,0,0               ; movaps        0x3e47(%rip),%xmm11        # 59f0 <_sk_callback_sse2+0x409>
+  DB  69,15,88,243                        ; addps         %xmm11,%xmm14
+  DB  68,15,40,37,75,62,0,0               ; movaps        0x3e4b(%rip),%xmm12        # 5a00 <_sk_callback_sse2+0x419>
+  DB  65,15,194,196,1                     ; cmpltps       %xmm12,%xmm0
+  DB  68,15,84,232                        ; andps         %xmm0,%xmm13
+  DB  65,15,85,198                        ; andnps        %xmm14,%xmm0
+  DB  65,15,86,197                        ; orps          %xmm13,%xmm0
+  DB  68,15,40,233                        ; movaps        %xmm1,%xmm13
+  DB  69,15,89,232                        ; mulps         %xmm8,%xmm13
+  DB  68,15,40,241                        ; movaps        %xmm1,%xmm14
+  DB  69,15,89,246                        ; mulps         %xmm14,%xmm14
+  DB  68,15,40,249                        ; movaps        %xmm1,%xmm15
+  DB  69,15,89,249                        ; mulps         %xmm9,%xmm15
+  DB  69,15,88,250                        ; addps         %xmm10,%xmm15
+  DB  69,15,89,254                        ; mulps         %xmm14,%xmm15
+  DB  69,15,88,251                        ; addps         %xmm11,%xmm15
+  DB  65,15,194,204,1                     ; cmpltps       %xmm12,%xmm1
+  DB  68,15,84,233                        ; andps         %xmm1,%xmm13
+  DB  65,15,85,207                        ; andnps        %xmm15,%xmm1
+  DB  65,15,86,205                        ; orps          %xmm13,%xmm1
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  68,15,40,234                        ; movaps        %xmm2,%xmm13
+  DB  69,15,89,237                        ; mulps         %xmm13,%xmm13
+  DB  68,15,89,202                        ; mulps         %xmm2,%xmm9
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  69,15,89,205                        ; mulps         %xmm13,%xmm9
+  DB  69,15,88,203                        ; addps         %xmm11,%xmm9
+  DB  65,15,194,212,1                     ; cmpltps       %xmm12,%xmm2
+  DB  68,15,84,194                        ; andps         %xmm2,%xmm8
+  DB  65,15,85,209                        ; andnps        %xmm9,%xmm2
+  DB  65,15,86,208                        ; orps          %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_to_srgb_sse2
+_sk_to_srgb_sse2 LABEL PROC
+  DB  68,15,82,232                        ; rsqrtps       %xmm0,%xmm13
+  DB  68,15,40,5,216,61,0,0               ; movaps        0x3dd8(%rip),%xmm8        # 5a10 <_sk_callback_sse2+0x429>
+  DB  68,15,40,240                        ; movaps        %xmm0,%xmm14
+  DB  69,15,89,240                        ; mulps         %xmm8,%xmm14
+  DB  68,15,40,13,216,61,0,0              ; movaps        0x3dd8(%rip),%xmm9        # 5a20 <_sk_callback_sse2+0x439>
+  DB  69,15,40,253                        ; movaps        %xmm13,%xmm15
+  DB  69,15,89,249                        ; mulps         %xmm9,%xmm15
+  DB  68,15,40,21,216,61,0,0              ; movaps        0x3dd8(%rip),%xmm10        # 5a30 <_sk_callback_sse2+0x449>
+  DB  69,15,88,250                        ; addps         %xmm10,%xmm15
+  DB  69,15,89,253                        ; mulps         %xmm13,%xmm15
+  DB  68,15,40,29,216,61,0,0              ; movaps        0x3dd8(%rip),%xmm11        # 5a40 <_sk_callback_sse2+0x459>
+  DB  69,15,88,251                        ; addps         %xmm11,%xmm15
+  DB  68,15,40,37,220,61,0,0              ; movaps        0x3ddc(%rip),%xmm12        # 5a50 <_sk_callback_sse2+0x469>
+  DB  69,15,88,236                        ; addps         %xmm12,%xmm13
+  DB  69,15,83,237                        ; rcpps         %xmm13,%xmm13
+  DB  69,15,89,239                        ; mulps         %xmm15,%xmm13
+  DB  68,15,40,61,216,61,0,0              ; movaps        0x3dd8(%rip),%xmm15        # 5a60 <_sk_callback_sse2+0x479>
+  DB  65,15,194,199,1                     ; cmpltps       %xmm15,%xmm0
+  DB  68,15,84,240                        ; andps         %xmm0,%xmm14
+  DB  65,15,85,197                        ; andnps        %xmm13,%xmm0
+  DB  65,15,86,198                        ; orps          %xmm14,%xmm0
+  DB  68,15,82,233                        ; rsqrtps       %xmm1,%xmm13
+  DB  69,15,40,245                        ; movaps        %xmm13,%xmm14
+  DB  69,15,89,241                        ; mulps         %xmm9,%xmm14
+  DB  69,15,88,242                        ; addps         %xmm10,%xmm14
+  DB  69,15,89,245                        ; mulps         %xmm13,%xmm14
+  DB  69,15,88,243                        ; addps         %xmm11,%xmm14
+  DB  69,15,88,236                        ; addps         %xmm12,%xmm13
+  DB  69,15,83,237                        ; rcpps         %xmm13,%xmm13
+  DB  69,15,89,238                        ; mulps         %xmm14,%xmm13
+  DB  68,15,40,241                        ; movaps        %xmm1,%xmm14
+  DB  69,15,89,240                        ; mulps         %xmm8,%xmm14
+  DB  65,15,194,207,1                     ; cmpltps       %xmm15,%xmm1
+  DB  68,15,84,241                        ; andps         %xmm1,%xmm14
+  DB  65,15,85,205                        ; andnps        %xmm13,%xmm1
+  DB  65,15,86,206                        ; orps          %xmm14,%xmm1
+  DB  68,15,82,234                        ; rsqrtps       %xmm2,%xmm13
+  DB  69,15,89,205                        ; mulps         %xmm13,%xmm9
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  69,15,89,205                        ; mulps         %xmm13,%xmm9
+  DB  69,15,88,203                        ; addps         %xmm11,%xmm9
+  DB  69,15,88,236                        ; addps         %xmm12,%xmm13
+  DB  69,15,83,213                        ; rcpps         %xmm13,%xmm10
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  65,15,194,215,1                     ; cmpltps       %xmm15,%xmm2
+  DB  68,15,84,194                        ; andps         %xmm2,%xmm8
+  DB  65,15,85,210                        ; andnps        %xmm10,%xmm2
+  DB  65,15,86,208                        ; orps          %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_rgb_to_hsl_sse2
+_sk_rgb_to_hsl_sse2 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,40,208                        ; movaps        %xmm8,%xmm10
+  DB  69,15,95,209                        ; maxps         %xmm9,%xmm10
+  DB  68,15,95,210                        ; maxps         %xmm2,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  69,15,93,217                        ; minps         %xmm9,%xmm11
+  DB  68,15,93,218                        ; minps         %xmm2,%xmm11
+  DB  65,15,40,202                        ; movaps        %xmm10,%xmm1
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  68,15,40,45,49,61,0,0               ; movaps        0x3d31(%rip),%xmm13        # 5a70 <_sk_callback_sse2+0x489>
+  DB  68,15,94,233                        ; divps         %xmm1,%xmm13
+  DB  65,15,40,194                        ; movaps        %xmm10,%xmm0
+  DB  65,15,194,192,0                     ; cmpeqps       %xmm8,%xmm0
+  DB  69,15,40,225                        ; movaps        %xmm9,%xmm12
+  DB  68,15,92,226                        ; subps         %xmm2,%xmm12
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,40,241                        ; movaps        %xmm9,%xmm14
+  DB  68,15,194,242,1                     ; cmpltps       %xmm2,%xmm14
+  DB  68,15,84,53,23,61,0,0               ; andps         0x3d17(%rip),%xmm14        # 5a80 <_sk_callback_sse2+0x499>
+  DB  69,15,88,244                        ; addps         %xmm12,%xmm14
+  DB  69,15,40,250                        ; movaps        %xmm10,%xmm15
+  DB  69,15,194,249,0                     ; cmpeqps       %xmm9,%xmm15
+  DB  65,15,92,208                        ; subps         %xmm8,%xmm2
+  DB  65,15,89,213                        ; mulps         %xmm13,%xmm2
+  DB  68,15,40,37,10,61,0,0               ; movaps        0x3d0a(%rip),%xmm12        # 5a90 <_sk_callback_sse2+0x4a9>
+  DB  65,15,88,212                        ; addps         %xmm12,%xmm2
+  DB  69,15,92,193                        ; subps         %xmm9,%xmm8
+  DB  69,15,89,197                        ; mulps         %xmm13,%xmm8
+  DB  68,15,88,5,6,61,0,0                 ; addps         0x3d06(%rip),%xmm8        # 5aa0 <_sk_callback_sse2+0x4b9>
+  DB  65,15,84,215                        ; andps         %xmm15,%xmm2
+  DB  69,15,85,248                        ; andnps        %xmm8,%xmm15
+  DB  68,15,86,250                        ; orps          %xmm2,%xmm15
+  DB  68,15,84,240                        ; andps         %xmm0,%xmm14
+  DB  65,15,85,199                        ; andnps        %xmm15,%xmm0
+  DB  65,15,86,198                        ; orps          %xmm14,%xmm0
+  DB  15,89,5,247,60,0,0                  ; mulps         0x3cf7(%rip),%xmm0        # 5ab0 <_sk_callback_sse2+0x4c9>
+  DB  69,15,40,194                        ; movaps        %xmm10,%xmm8
+  DB  69,15,194,195,4                     ; cmpneqps      %xmm11,%xmm8
+  DB  65,15,84,192                        ; andps         %xmm8,%xmm0
+  DB  69,15,92,226                        ; subps         %xmm10,%xmm12
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  68,15,40,13,234,60,0,0              ; movaps        0x3cea(%rip),%xmm9        # 5ac0 <_sk_callback_sse2+0x4d9>
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  68,15,194,202,1                     ; cmpltps       %xmm2,%xmm9
+  DB  69,15,92,227                        ; subps         %xmm11,%xmm12
+  DB  69,15,84,225                        ; andps         %xmm9,%xmm12
+  DB  69,15,85,202                        ; andnps        %xmm10,%xmm9
+  DB  69,15,86,204                        ; orps          %xmm12,%xmm9
+  DB  65,15,94,201                        ; divps         %xmm9,%xmm1
+  DB  65,15,84,200                        ; andps         %xmm8,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_hsl_to_rgb_sse2
+_sk_hsl_to_rgb_sse2 LABEL PROC
+  DB  72,131,236,120                      ; sub           $0x78,%rsp
+  DB  15,41,124,36,96                     ; movaps        %xmm7,0x60(%rsp)
+  DB  15,41,116,36,80                     ; movaps        %xmm6,0x50(%rsp)
+  DB  15,41,108,36,64                     ; movaps        %xmm5,0x40(%rsp)
+  DB  15,41,100,36,48                     ; movaps        %xmm4,0x30(%rsp)
+  DB  15,41,92,36,32                      ; movaps        %xmm3,0x20(%rsp)
+  DB  68,15,40,218                        ; movaps        %xmm2,%xmm11
+  DB  15,40,240                           ; movaps        %xmm0,%xmm6
+  DB  68,15,40,13,165,60,0,0              ; movaps        0x3ca5(%rip),%xmm9        # 5ad0 <_sk_callback_sse2+0x4e9>
+  DB  69,15,40,209                        ; movaps        %xmm9,%xmm10
+  DB  69,15,194,211,2                     ; cmpleps       %xmm11,%xmm10
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,194,217,0                        ; cmpeqps       %xmm1,%xmm3
+  DB  15,40,251                           ; movaps        %xmm3,%xmm7
+  DB  15,41,60,36                         ; movaps        %xmm7,(%rsp)
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  65,15,84,194                        ; andps         %xmm10,%xmm0
+  DB  68,15,85,209                        ; andnps        %xmm1,%xmm10
+  DB  68,15,86,208                        ; orps          %xmm0,%xmm10
+  DB  68,15,41,92,36,16                   ; movaps        %xmm11,0x10(%rsp)
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  69,15,88,219                        ; addps         %xmm11,%xmm11
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  15,40,5,111,60,0,0                  ; movaps        0x3c6f(%rip),%xmm0        # 5ae0 <_sk_callback_sse2+0x4f9>
+  DB  15,88,198                           ; addps         %xmm6,%xmm0
+  DB  243,15,91,200                       ; cvttps2dq     %xmm0,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  15,194,217,1                        ; cmpltps       %xmm1,%xmm3
+  DB  15,84,29,103,60,0,0                 ; andps         0x3c67(%rip),%xmm3        # 5af0 <_sk_callback_sse2+0x509>
+  DB  15,92,203                           ; subps         %xmm3,%xmm1
+  DB  15,92,193                           ; subps         %xmm1,%xmm0
+  DB  68,15,40,45,105,60,0,0              ; movaps        0x3c69(%rip),%xmm13        # 5b00 <_sk_callback_sse2+0x519>
+  DB  69,15,40,197                        ; movaps        %xmm13,%xmm8
+  DB  68,15,194,192,2                     ; cmpleps       %xmm0,%xmm8
+  DB  69,15,40,242                        ; movaps        %xmm10,%xmm14
+  DB  69,15,92,243                        ; subps         %xmm11,%xmm14
+  DB  65,15,40,217                        ; movaps        %xmm9,%xmm3
+  DB  15,194,216,2                        ; cmpleps       %xmm0,%xmm3
+  DB  15,40,21,121,60,0,0                 ; movaps        0x3c79(%rip),%xmm2        # 5b30 <_sk_callback_sse2+0x549>
+  DB  68,15,40,250                        ; movaps        %xmm2,%xmm15
+  DB  68,15,194,248,2                     ; cmpleps       %xmm0,%xmm15
+  DB  15,40,13,73,60,0,0                  ; movaps        0x3c49(%rip),%xmm1        # 5b10 <_sk_callback_sse2+0x529>
+  DB  15,89,193                           ; mulps         %xmm1,%xmm0
+  DB  15,40,45,79,60,0,0                  ; movaps        0x3c4f(%rip),%xmm5        # 5b20 <_sk_callback_sse2+0x539>
+  DB  15,40,229                           ; movaps        %xmm5,%xmm4
+  DB  15,92,224                           ; subps         %xmm0,%xmm4
+  DB  65,15,89,230                        ; mulps         %xmm14,%xmm4
+  DB  65,15,88,227                        ; addps         %xmm11,%xmm4
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,84,224                        ; andps         %xmm8,%xmm12
+  DB  68,15,85,196                        ; andnps        %xmm4,%xmm8
+  DB  69,15,86,196                        ; orps          %xmm12,%xmm8
+  DB  68,15,84,195                        ; andps         %xmm3,%xmm8
+  DB  65,15,85,218                        ; andnps        %xmm10,%xmm3
+  DB  65,15,86,216                        ; orps          %xmm8,%xmm3
+  DB  65,15,89,198                        ; mulps         %xmm14,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  65,15,84,223                        ; andps         %xmm15,%xmm3
+  DB  68,15,85,248                        ; andnps        %xmm0,%xmm15
+  DB  68,15,86,251                        ; orps          %xmm3,%xmm15
+  DB  68,15,40,199                        ; movaps        %xmm7,%xmm8
+  DB  69,15,85,199                        ; andnps        %xmm15,%xmm8
+  DB  243,15,91,198                       ; cvttps2dq     %xmm6,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,40,222                           ; movaps        %xmm6,%xmm3
+  DB  15,194,216,1                        ; cmpltps       %xmm0,%xmm3
+  DB  15,84,29,196,59,0,0                 ; andps         0x3bc4(%rip),%xmm3        # 5af0 <_sk_callback_sse2+0x509>
+  DB  15,92,195                           ; subps         %xmm3,%xmm0
+  DB  68,15,40,230                        ; movaps        %xmm6,%xmm12
+  DB  68,15,92,224                        ; subps         %xmm0,%xmm12
+  DB  69,15,40,253                        ; movaps        %xmm13,%xmm15
+  DB  69,15,194,252,2                     ; cmpleps       %xmm12,%xmm15
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  65,15,194,228,2                     ; cmpleps       %xmm12,%xmm4
+  DB  15,40,218                           ; movaps        %xmm2,%xmm3
+  DB  65,15,194,220,2                     ; cmpleps       %xmm12,%xmm3
+  DB  68,15,89,225                        ; mulps         %xmm1,%xmm12
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  65,15,92,196                        ; subps         %xmm12,%xmm0
+  DB  65,15,89,198                        ; mulps         %xmm14,%xmm0
+  DB  65,15,88,195                        ; addps         %xmm11,%xmm0
+  DB  65,15,40,251                        ; movaps        %xmm11,%xmm7
+  DB  65,15,84,255                        ; andps         %xmm15,%xmm7
+  DB  68,15,85,248                        ; andnps        %xmm0,%xmm15
+  DB  68,15,86,255                        ; orps          %xmm7,%xmm15
+  DB  68,15,84,252                        ; andps         %xmm4,%xmm15
+  DB  65,15,85,226                        ; andnps        %xmm10,%xmm4
+  DB  65,15,86,231                        ; orps          %xmm15,%xmm4
+  DB  69,15,89,230                        ; mulps         %xmm14,%xmm12
+  DB  69,15,88,227                        ; addps         %xmm11,%xmm12
+  DB  15,84,227                           ; andps         %xmm3,%xmm4
+  DB  65,15,85,220                        ; andnps        %xmm12,%xmm3
+  DB  15,86,220                           ; orps          %xmm4,%xmm3
+  DB  15,40,60,36                         ; movaps        (%rsp),%xmm7
+  DB  15,40,231                           ; movaps        %xmm7,%xmm4
+  DB  15,85,227                           ; andnps        %xmm3,%xmm4
+  DB  15,88,53,157,59,0,0                 ; addps         0x3b9d(%rip),%xmm6        # 5b40 <_sk_callback_sse2+0x559>
+  DB  243,15,91,198                       ; cvttps2dq     %xmm6,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,40,222                           ; movaps        %xmm6,%xmm3
+  DB  15,194,216,1                        ; cmpltps       %xmm0,%xmm3
+  DB  15,84,29,56,59,0,0                  ; andps         0x3b38(%rip),%xmm3        # 5af0 <_sk_callback_sse2+0x509>
+  DB  15,92,195                           ; subps         %xmm3,%xmm0
+  DB  15,92,240                           ; subps         %xmm0,%xmm6
+  DB  15,89,206                           ; mulps         %xmm6,%xmm1
+  DB  15,92,233                           ; subps         %xmm1,%xmm5
+  DB  65,15,89,238                        ; mulps         %xmm14,%xmm5
+  DB  65,15,89,206                        ; mulps         %xmm14,%xmm1
+  DB  65,15,88,235                        ; addps         %xmm11,%xmm5
+  DB  65,15,88,203                        ; addps         %xmm11,%xmm1
+  DB  68,15,194,238,2                     ; cmpleps       %xmm6,%xmm13
+  DB  69,15,84,221                        ; andps         %xmm13,%xmm11
+  DB  68,15,85,237                        ; andnps        %xmm5,%xmm13
+  DB  69,15,86,235                        ; orps          %xmm11,%xmm13
+  DB  68,15,194,206,2                     ; cmpleps       %xmm6,%xmm9
+  DB  69,15,84,233                        ; andps         %xmm9,%xmm13
+  DB  69,15,85,202                        ; andnps        %xmm10,%xmm9
+  DB  69,15,86,205                        ; orps          %xmm13,%xmm9
+  DB  15,194,214,2                        ; cmpleps       %xmm6,%xmm2
+  DB  68,15,84,202                        ; andps         %xmm2,%xmm9
+  DB  15,85,209                           ; andnps        %xmm1,%xmm2
+  DB  65,15,86,209                        ; orps          %xmm9,%xmm2
+  DB  15,40,68,36,16                      ; movaps        0x10(%rsp),%xmm0
+  DB  15,40,207                           ; movaps        %xmm7,%xmm1
+  DB  15,84,193                           ; andps         %xmm1,%xmm0
+  DB  15,85,202                           ; andnps        %xmm2,%xmm1
+  DB  68,15,86,192                        ; orps          %xmm0,%xmm8
+  DB  15,86,224                           ; orps          %xmm0,%xmm4
+  DB  15,86,193                           ; orps          %xmm1,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  15,40,204                           ; movaps        %xmm4,%xmm1
+  DB  15,40,92,36,32                      ; movaps        0x20(%rsp),%xmm3
+  DB  15,40,100,36,48                     ; movaps        0x30(%rsp),%xmm4
+  DB  15,40,108,36,64                     ; movaps        0x40(%rsp),%xmm5
+  DB  15,40,116,36,80                     ; movaps        0x50(%rsp),%xmm6
+  DB  15,40,124,36,96                     ; movaps        0x60(%rsp),%xmm7
+  DB  72,131,196,120                      ; add           $0x78,%rsp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_1_float_sse2
+_sk_scale_1_float_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_u8_sse2
+_sk_scale_u8_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,61                              ; jne           20af <_sk_scale_u8_sse2+0x47>
+  DB  102,69,15,110,4,18                  ; movd          (%r10,%rdx,1),%xmm8
+  DB  102,68,15,96,192                    ; punpcklbw     %xmm0,%xmm8
+  DB  102,68,15,97,192                    ; punpcklwd     %xmm0,%xmm8
+  DB  102,68,15,219,5,197,58,0,0          ; pand          0x3ac5(%rip),%xmm8        # 5b50 <_sk_callback_sse2+0x569>
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,5,201,58,0,0               ; mulps         0x3ac9(%rip),%xmm8        # 5b60 <_sk_callback_sse2+0x579>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,54                              ; je            20f7 <_sk_scale_u8_sse2+0x8f>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,23                              ; je            20de <_sk_scale_u8_sse2+0x76>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,181                             ; jne           2082 <_sk_scale_u8_sse2+0x1a>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,69                ; pshufd        $0x45,%xmm8,%xmm8
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,68,15,110,200                   ; movd          %eax,%xmm9
+  DB  69,15,198,200,0                     ; shufps        $0x0,%xmm8,%xmm9
+  DB  69,15,198,200,226                   ; shufps        $0xe2,%xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,68,15,110,200                   ; movd          %eax,%xmm9
+  DB  243,69,15,16,193                    ; movss         %xmm9,%xmm8
+  DB  233,119,255,255,255                 ; jmpq          2082 <_sk_scale_u8_sse2+0x1a>
+
+PUBLIC _sk_lerp_1_float_sse2
+_sk_lerp_1_float_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,92,205                           ; subps         %xmm5,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,92,214                           ; subps         %xmm6,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lerp_u8_sse2
+_sk_lerp_u8_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,81                              ; jne           219e <_sk_lerp_u8_sse2+0x5b>
+  DB  102,69,15,110,4,18                  ; movd          (%r10,%rdx,1),%xmm8
+  DB  102,68,15,96,192                    ; punpcklbw     %xmm0,%xmm8
+  DB  102,68,15,97,192                    ; punpcklwd     %xmm0,%xmm8
+  DB  102,68,15,219,5,10,58,0,0           ; pand          0x3a0a(%rip),%xmm8        # 5b70 <_sk_callback_sse2+0x589>
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,5,14,58,0,0                ; mulps         0x3a0e(%rip),%xmm8        # 5b80 <_sk_callback_sse2+0x599>
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,92,205                           ; subps         %xmm5,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,92,214                           ; subps         %xmm6,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,54                              ; je            21e6 <_sk_lerp_u8_sse2+0xa3>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,23                              ; je            21cd <_sk_lerp_u8_sse2+0x8a>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,161                             ; jne           215d <_sk_lerp_u8_sse2+0x1a>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,69                ; pshufd        $0x45,%xmm8,%xmm8
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,68,15,110,200                   ; movd          %eax,%xmm9
+  DB  69,15,198,200,0                     ; shufps        $0x0,%xmm8,%xmm9
+  DB  69,15,198,200,226                   ; shufps        $0xe2,%xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,68,15,110,200                   ; movd          %eax,%xmm9
+  DB  243,69,15,16,193                    ; movss         %xmm9,%xmm8
+  DB  233,99,255,255,255                  ; jmpq          215d <_sk_lerp_u8_sse2+0x1a>
+
+PUBLIC _sk_lerp_565_sse2
+_sk_lerp_565_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,156,0,0,0                    ; jne           22a4 <_sk_lerp_565_sse2+0xaa>
+  DB  243,69,15,126,12,82                 ; movq          (%r10,%rdx,2),%xmm9
+  DB  102,68,15,97,200                    ; punpcklwd     %xmm0,%xmm9
+  DB  102,68,15,111,5,116,57,0,0          ; movdqa        0x3974(%rip),%xmm8        # 5b90 <_sk_callback_sse2+0x5a9>
+  DB  102,69,15,219,193                   ; pand          %xmm9,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,5,115,57,0,0               ; mulps         0x3973(%rip),%xmm8        # 5ba0 <_sk_callback_sse2+0x5b9>
+  DB  102,68,15,111,21,122,57,0,0         ; movdqa        0x397a(%rip),%xmm10        # 5bb0 <_sk_callback_sse2+0x5c9>
+  DB  102,69,15,219,209                   ; pand          %xmm9,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  68,15,89,21,121,57,0,0              ; mulps         0x3979(%rip),%xmm10        # 5bc0 <_sk_callback_sse2+0x5d9>
+  DB  102,68,15,219,13,128,57,0,0         ; pand          0x3980(%rip),%xmm9        # 5bd0 <_sk_callback_sse2+0x5e9>
+  DB  69,15,91,201                        ; cvtdq2ps      %xmm9,%xmm9
+  DB  68,15,89,13,132,57,0,0              ; mulps         0x3984(%rip),%xmm9        # 5be0 <_sk_callback_sse2+0x5f9>
+  DB  15,92,196                           ; subps         %xmm4,%xmm0
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,196                           ; addps         %xmm4,%xmm0
+  DB  15,92,205                           ; subps         %xmm5,%xmm1
+  DB  65,15,89,202                        ; mulps         %xmm10,%xmm1
+  DB  15,88,205                           ; addps         %xmm5,%xmm1
+  DB  15,92,214                           ; subps         %xmm6,%xmm2
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,214                           ; addps         %xmm6,%xmm2
+  DB  15,92,223                           ; subps         %xmm7,%xmm3
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  68,15,88,199                        ; addps         %xmm7,%xmm8
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  68,15,88,215                        ; addps         %xmm7,%xmm10
+  DB  65,15,89,217                        ; mulps         %xmm9,%xmm3
+  DB  15,88,223                           ; addps         %xmm7,%xmm3
+  DB  68,15,95,211                        ; maxps         %xmm3,%xmm10
+  DB  69,15,95,194                        ; maxps         %xmm10,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,216                        ; movaps        %xmm8,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,58                              ; je            22f0 <_sk_lerp_565_sse2+0xf6>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,27                              ; je            22d7 <_sk_lerp_565_sse2+0xdd>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  15,133,77,255,255,255               ; jne           2213 <_sk_lerp_565_sse2+0x19>
+  DB  65,15,183,68,82,4                   ; movzwl        0x4(%r10,%rdx,2),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,200,69                ; pshufd        $0x45,%xmm8,%xmm9
+  DB  65,15,183,68,82,2                   ; movzwl        0x2(%r10,%rdx,2),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  69,15,198,193,0                     ; shufps        $0x0,%xmm9,%xmm8
+  DB  69,15,198,193,226                   ; shufps        $0xe2,%xmm9,%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  65,15,183,4,82                      ; movzwl        (%r10,%rdx,2),%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  243,69,15,16,200                    ; movss         %xmm8,%xmm9
+  DB  233,15,255,255,255                  ; jmpq          2213 <_sk_lerp_565_sse2+0x19>
+
+PUBLIC _sk_load_tables_sse2
+_sk_load_tables_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,36,1,0,0                     ; jne           2436 <_sk_load_tables_sse2+0x132>
+  DB  243,69,15,111,12,145                ; movdqu        (%r9,%rdx,4),%xmm9
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  102,68,15,111,5,202,56,0,0          ; movdqa        0x38ca(%rip),%xmm8        # 5bf0 <_sk_callback_sse2+0x609>
+  DB  102,65,15,111,193                   ; movdqa        %xmm9,%xmm0
+  DB  102,65,15,219,192                   ; pand          %xmm8,%xmm0
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,73,15,126,201                   ; movq          %xmm1,%r9
+  DB  102,73,15,126,194                   ; movq          %xmm0,%r10
+  DB  69,15,182,218                       ; movzbl        %r10b,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  69,15,182,241                       ; movzbl        %r9b,%r14d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,66,15,16,12,19                  ; movss         (%rbx,%r10,1),%xmm1
+  DB  243,66,15,16,4,11                   ; movss         (%rbx,%r9,1),%xmm0
+  DB  15,20,200                           ; unpcklps      %xmm0,%xmm1
+  DB  243,66,15,16,4,155                  ; movss         (%rbx,%r11,4),%xmm0
+  DB  243,66,15,16,20,179                 ; movss         (%rbx,%r14,4),%xmm2
+  DB  15,20,194                           ; unpcklps      %xmm2,%xmm0
+  DB  15,20,193                           ; unpcklps      %xmm1,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,15,114,209,8                    ; psrld         $0x8,%xmm1
+  DB  102,65,15,219,200                   ; pand          %xmm8,%xmm1
+  DB  102,15,112,209,78                   ; pshufd        $0x4e,%xmm1,%xmm2
+  DB  102,73,15,126,209                   ; movq          %xmm2,%r9
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  68,15,182,211                       ; movzbl        %bl,%r10d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  69,15,182,217                       ; movzbl        %r9b,%r11d
+  DB  73,193,233,30                       ; shr           $0x1e,%r9
+  DB  243,65,15,16,20,31                  ; movss         (%r15,%rbx,1),%xmm2
+  DB  243,67,15,16,12,15                  ; movss         (%r15,%r9,1),%xmm1
+  DB  15,20,209                           ; unpcklps      %xmm1,%xmm2
+  DB  243,67,15,16,12,151                 ; movss         (%r15,%r10,4),%xmm1
+  DB  243,67,15,16,28,159                 ; movss         (%r15,%r11,4),%xmm3
+  DB  15,20,203                           ; unpcklps      %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  76,139,72,24                        ; mov           0x18(%rax),%r9
+  DB  102,65,15,111,209                   ; movdqa        %xmm9,%xmm2
+  DB  102,15,114,210,16                   ; psrld         $0x10,%xmm2
+  DB  102,65,15,219,208                   ; pand          %xmm8,%xmm2
+  DB  102,15,112,218,78                   ; pshufd        $0x4e,%xmm2,%xmm3
+  DB  102,72,15,126,219                   ; movq          %xmm3,%rbx
+  DB  102,72,15,126,208                   ; movq          %xmm2,%rax
+  DB  68,15,182,208                       ; movzbl        %al,%r10d
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  68,15,182,219                       ; movzbl        %bl,%r11d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  243,69,15,16,4,1                    ; movss         (%r9,%rax,1),%xmm8
+  DB  243,65,15,16,20,25                  ; movss         (%r9,%rbx,1),%xmm2
+  DB  68,15,20,194                        ; unpcklps      %xmm2,%xmm8
+  DB  243,67,15,16,20,145                 ; movss         (%r9,%r10,4),%xmm2
+  DB  243,67,15,16,28,153                 ; movss         (%r9,%r11,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  65,15,20,208                        ; unpcklps      %xmm8,%xmm2
+  DB  102,65,15,114,209,24                ; psrld         $0x18,%xmm9
+  DB  65,15,91,217                        ; cvtdq2ps      %xmm9,%xmm3
+  DB  15,89,29,211,55,0,0                 ; mulps         0x37d3(%rip),%xmm3        # 5c00 <_sk_callback_sse2+0x619>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,194                          ; mov           %r8d,%r10d
+  DB  65,128,226,3                        ; and           $0x3,%r10b
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  65,128,250,1                        ; cmp           $0x1,%r10b
+  DB  116,50                              ; je            247a <_sk_load_tables_sse2+0x176>
+  DB  65,128,250,2                        ; cmp           $0x2,%r10b
+  DB  116,23                              ; je            2465 <_sk_load_tables_sse2+0x161>
+  DB  65,128,250,3                        ; cmp           $0x3,%r10b
+  DB  15,133,192,254,255,255              ; jne           2318 <_sk_load_tables_sse2+0x14>
+  DB  102,65,15,110,68,145,8              ; movd          0x8(%r9,%rdx,4),%xmm0
+  DB  102,68,15,112,200,69                ; pshufd        $0x45,%xmm0,%xmm9
+  DB  243,65,15,16,68,145,4               ; movss         0x4(%r9,%rdx,4),%xmm0
+  DB  65,15,198,193,0                     ; shufps        $0x0,%xmm9,%xmm0
+  DB  65,15,198,193,226                   ; shufps        $0xe2,%xmm9,%xmm0
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  243,65,15,16,4,145                  ; movss         (%r9,%rdx,4),%xmm0
+  DB  243,68,15,16,200                    ; movss         %xmm0,%xmm9
+  DB  233,142,254,255,255                 ; jmpq          2318 <_sk_load_tables_sse2+0x14>
+
+PUBLIC _sk_load_tables_u16_be_sse2
+_sk_load_tables_u16_be_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,101,1,0,0                    ; jne           2605 <_sk_load_tables_u16_be_sse2+0x17b>
+  DB  102,67,15,16,4,81                   ; movupd        (%r9,%r10,2),%xmm0
+  DB  102,67,15,16,76,81,16               ; movupd        0x10(%r9,%r10,2),%xmm1
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  102,68,15,40,200                    ; movapd        %xmm0,%xmm9
+  DB  102,68,15,97,201                    ; punpcklwd     %xmm1,%xmm9
+  DB  102,15,105,193                      ; punpckhwd     %xmm1,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,15,97,200                       ; punpcklwd     %xmm0,%xmm1
+  DB  102,68,15,105,200                   ; punpckhwd     %xmm0,%xmm9
+  DB  102,68,15,111,21,57,55,0,0          ; movdqa        0x3739(%rip),%xmm10        # 5c10 <_sk_callback_sse2+0x629>
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,65,15,219,194                   ; pand          %xmm10,%xmm0
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  102,65,15,97,192                    ; punpcklwd     %xmm8,%xmm0
+  DB  102,15,112,216,78                   ; pshufd        $0x4e,%xmm0,%xmm3
+  DB  102,73,15,126,217                   ; movq          %xmm3,%r9
+  DB  69,15,182,209                       ; movzbl        %r9b,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  69,15,182,243                       ; movzbl        %r11b,%r14d
+  DB  73,193,235,30                       ; shr           $0x1e,%r11
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,66,15,16,28,27                  ; movss         (%rbx,%r11,1),%xmm3
+  DB  243,66,15,16,4,139                  ; movss         (%rbx,%r9,4),%xmm0
+  DB  15,20,216                           ; unpcklps      %xmm0,%xmm3
+  DB  243,66,15,16,4,179                  ; movss         (%rbx,%r14,4),%xmm0
+  DB  243,66,15,16,20,147                 ; movss         (%rbx,%r10,4),%xmm2
+  DB  15,20,194                           ; unpcklps      %xmm2,%xmm0
+  DB  15,20,195                           ; unpcklps      %xmm3,%xmm0
+  DB  102,15,115,217,8                    ; psrldq        $0x8,%xmm1
+  DB  102,65,15,219,202                   ; pand          %xmm10,%xmm1
+  DB  102,65,15,97,200                    ; punpcklwd     %xmm8,%xmm1
+  DB  102,15,112,209,78                   ; pshufd        $0x4e,%xmm1,%xmm2
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  68,15,182,203                       ; movzbl        %bl,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,73,15,126,202                   ; movq          %xmm1,%r10
+  DB  69,15,182,218                       ; movzbl        %r10b,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  243,67,15,16,20,23                  ; movss         (%r15,%r10,1),%xmm2
+  DB  243,65,15,16,12,159                 ; movss         (%r15,%rbx,4),%xmm1
+  DB  15,20,209                           ; unpcklps      %xmm1,%xmm2
+  DB  243,67,15,16,12,159                 ; movss         (%r15,%r11,4),%xmm1
+  DB  243,67,15,16,28,143                 ; movss         (%r15,%r9,4),%xmm3
+  DB  15,20,203                           ; unpcklps      %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  102,69,15,219,209                   ; pand          %xmm9,%xmm10
+  DB  102,69,15,97,208                    ; punpcklwd     %xmm8,%xmm10
+  DB  102,65,15,112,210,78                ; pshufd        $0x4e,%xmm10,%xmm2
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  68,15,182,203                       ; movzbl        %bl,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,76,15,126,208                   ; movq          %xmm10,%rax
+  DB  68,15,182,216                       ; movzbl        %al,%r11d
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  243,69,15,16,20,2                   ; movss         (%r10,%rax,1),%xmm10
+  DB  243,65,15,16,20,154                 ; movss         (%r10,%rbx,4),%xmm2
+  DB  68,15,20,210                        ; unpcklps      %xmm2,%xmm10
+  DB  243,67,15,16,20,154                 ; movss         (%r10,%r11,4),%xmm2
+  DB  243,67,15,16,28,138                 ; movss         (%r10,%r9,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  65,15,20,210                        ; unpcklps      %xmm10,%xmm2
+  DB  102,65,15,112,217,78                ; pshufd        $0x4e,%xmm9,%xmm3
+  DB  102,68,15,111,203                   ; movdqa        %xmm3,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,65,15,235,217                   ; por           %xmm9,%xmm3
+  DB  102,65,15,97,216                    ; punpcklwd     %xmm8,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  15,89,29,36,54,0,0                  ; mulps         0x3624(%rip),%xmm3        # 5c20 <_sk_callback_sse2+0x639>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+  DB  242,67,15,16,4,81                   ; movsd         (%r9,%r10,2),%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,17                              ; jne           2622 <_sk_load_tables_u16_be_sse2+0x198>
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  233,139,254,255,255                 ; jmpq          24ad <_sk_load_tables_u16_be_sse2+0x23>
+  DB  102,67,15,22,68,81,8                ; movhpd        0x8(%r9,%r10,2),%xmm0
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  15,130,118,254,255,255              ; jb            24ad <_sk_load_tables_u16_be_sse2+0x23>
+  DB  242,67,15,16,76,81,16               ; movsd         0x10(%r9,%r10,2),%xmm1
+  DB  233,106,254,255,255                 ; jmpq          24ad <_sk_load_tables_u16_be_sse2+0x23>
+
+PUBLIC _sk_load_tables_rgb_u16_be_sse2
+_sk_load_tables_rgb_u16_be_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  76,141,20,82                        ; lea           (%rdx,%rdx,2),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,84,1,0,0                     ; jne           27a9 <_sk_load_tables_rgb_u16_be_sse2+0x166>
+  DB  243,71,15,111,28,81                 ; movdqu        (%r9,%r10,2),%xmm11
+  DB  243,67,15,111,76,81,8               ; movdqu        0x8(%r9,%r10,2),%xmm1
+  DB  102,15,115,217,4                    ; psrldq        $0x4,%xmm1
+  DB  102,69,15,111,211                   ; movdqa        %xmm11,%xmm10
+  DB  102,65,15,115,218,6                 ; psrldq        $0x6,%xmm10
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,115,216,6                    ; psrldq        $0x6,%xmm0
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  102,68,15,97,217                    ; punpcklwd     %xmm1,%xmm11
+  DB  102,68,15,97,208                    ; punpcklwd     %xmm0,%xmm10
+  DB  102,65,15,111,195                   ; movdqa        %xmm11,%xmm0
+  DB  102,65,15,97,194                    ; punpcklwd     %xmm10,%xmm0
+  DB  102,68,15,111,5,147,53,0,0          ; movdqa        0x3593(%rip),%xmm8        # 5c30 <_sk_callback_sse2+0x649>
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,65,15,219,192                   ; pand          %xmm8,%xmm0
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  102,65,15,97,193                    ; punpcklwd     %xmm9,%xmm0
+  DB  102,15,112,216,78                   ; pshufd        $0x4e,%xmm0,%xmm3
+  DB  102,73,15,126,217                   ; movq          %xmm3,%r9
+  DB  69,15,182,209                       ; movzbl        %r9b,%r10d
+  DB  73,193,233,32                       ; shr           $0x20,%r9
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  69,15,182,243                       ; movzbl        %r11b,%r14d
+  DB  73,193,235,30                       ; shr           $0x1e,%r11
+  DB  72,139,88,8                         ; mov           0x8(%rax),%rbx
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,66,15,16,28,27                  ; movss         (%rbx,%r11,1),%xmm3
+  DB  243,66,15,16,4,139                  ; movss         (%rbx,%r9,4),%xmm0
+  DB  15,20,216                           ; unpcklps      %xmm0,%xmm3
+  DB  243,66,15,16,4,179                  ; movss         (%rbx,%r14,4),%xmm0
+  DB  243,66,15,16,20,147                 ; movss         (%rbx,%r10,4),%xmm2
+  DB  15,20,194                           ; unpcklps      %xmm2,%xmm0
+  DB  15,20,195                           ; unpcklps      %xmm3,%xmm0
+  DB  102,65,15,219,200                   ; pand          %xmm8,%xmm1
+  DB  102,65,15,97,201                    ; punpcklwd     %xmm9,%xmm1
+  DB  102,15,112,209,78                   ; pshufd        $0x4e,%xmm1,%xmm2
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  68,15,182,203                       ; movzbl        %bl,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,73,15,126,202                   ; movq          %xmm1,%r10
+  DB  69,15,182,218                       ; movzbl        %r10b,%r11d
+  DB  73,193,234,30                       ; shr           $0x1e,%r10
+  DB  243,67,15,16,20,23                  ; movss         (%r15,%r10,1),%xmm2
+  DB  243,65,15,16,12,159                 ; movss         (%r15,%rbx,4),%xmm1
+  DB  15,20,209                           ; unpcklps      %xmm1,%xmm2
+  DB  243,67,15,16,12,159                 ; movss         (%r15,%r11,4),%xmm1
+  DB  243,67,15,16,28,143                 ; movss         (%r15,%r9,4),%xmm3
+  DB  15,20,203                           ; unpcklps      %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  76,139,80,24                        ; mov           0x18(%rax),%r10
+  DB  102,69,15,105,218                   ; punpckhwd     %xmm10,%xmm11
+  DB  102,69,15,219,216                   ; pand          %xmm8,%xmm11
+  DB  102,69,15,97,217                    ; punpcklwd     %xmm9,%xmm11
+  DB  102,65,15,112,211,78                ; pshufd        $0x4e,%xmm11,%xmm2
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  68,15,182,203                       ; movzbl        %bl,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,76,15,126,216                   ; movq          %xmm11,%rax
+  DB  68,15,182,216                       ; movzbl        %al,%r11d
+  DB  72,193,232,30                       ; shr           $0x1e,%rax
+  DB  243,69,15,16,4,2                    ; movss         (%r10,%rax,1),%xmm8
+  DB  243,65,15,16,20,154                 ; movss         (%r10,%rbx,4),%xmm2
+  DB  68,15,20,194                        ; unpcklps      %xmm2,%xmm8
+  DB  243,67,15,16,20,154                 ; movss         (%r10,%r11,4),%xmm2
+  DB  243,67,15,16,28,138                 ; movss         (%r10,%r9,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  65,15,20,208                        ; unpcklps      %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,158,52,0,0                 ; movaps        0x349e(%rip),%xmm3        # 5c40 <_sk_callback_sse2+0x659>
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,71,15,110,28,81                 ; movd          (%r9,%r10,2),%xmm11
+  DB  102,71,15,196,92,81,4,2             ; pinsrw        $0x2,0x4(%r9,%r10,2),%xmm11
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,14                              ; jne           27cf <_sk_load_tables_rgb_u16_be_sse2+0x18c>
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,69,15,239,210                   ; pxor          %xmm10,%xmm10
+  DB  233,172,254,255,255                 ; jmpq          267b <_sk_load_tables_rgb_u16_be_sse2+0x38>
+  DB  102,71,15,110,84,81,6               ; movd          0x6(%r9,%r10,2),%xmm10
+  DB  102,71,15,196,84,81,10,2            ; pinsrw        $0x2,0xa(%r9,%r10,2),%xmm10
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,24                              ; jb            2800 <_sk_load_tables_rgb_u16_be_sse2+0x1bd>
+  DB  102,67,15,110,76,81,12              ; movd          0xc(%r9,%r10,2),%xmm1
+  DB  102,67,15,196,76,81,16,2            ; pinsrw        $0x2,0x10(%r9,%r10,2),%xmm1
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  233,123,254,255,255                 ; jmpq          267b <_sk_load_tables_rgb_u16_be_sse2+0x38>
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,114,254,255,255                 ; jmpq          267b <_sk_load_tables_rgb_u16_be_sse2+0x38>
+
+PUBLIC _sk_byte_tables_sse2
+_sk_byte_tables_sse2 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,55,52,0,0                ; movaps        0x3437(%rip),%xmm8        # 5c50 <_sk_callback_sse2+0x669>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,91,192                       ; cvtps2dq      %xmm0,%xmm0
+  DB  102,73,15,126,193                   ; movq          %xmm0,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  77,137,203                          ; mov           %r9,%r11
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,15,112,192,78                   ; pshufd        $0x4e,%xmm0,%xmm0
+  DB  102,73,15,126,193                   ; movq          %xmm0,%r9
+  DB  69,137,206                          ; mov           %r9d,%r14d
+  DB  77,137,207                          ; mov           %r9,%r15
+  DB  73,193,239,32                       ; shr           $0x20,%r15
+  DB  72,139,24                           ; mov           (%rax),%rbx
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  70,15,182,52,51                     ; movzbl        (%rbx,%r14,1),%r14d
+  DB  66,15,182,44,59                     ; movzbl        (%rbx,%r15,1),%ebp
+  DB  193,229,8                           ; shl           $0x8,%ebp
+  DB  68,9,245                            ; or            %r14d,%ebp
+  DB  70,15,182,20,19                     ; movzbl        (%rbx,%r10,1),%r10d
+  DB  66,15,182,28,27                     ; movzbl        (%rbx,%r11,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,211                            ; or            %r10d,%ebx
+  DB  102,15,196,195,0                    ; pinsrw        $0x0,%ebx,%xmm0
+  DB  102,15,196,197,1                    ; pinsrw        $0x1,%ebp,%xmm0
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  102,65,15,96,193                    ; punpcklbw     %xmm9,%xmm0
+  DB  102,65,15,97,193                    ; punpcklwd     %xmm9,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,21,209,51,0,0              ; movaps        0x33d1(%rip),%xmm10        # 5c60 <_sk_callback_sse2+0x679>
+  DB  65,15,89,194                        ; mulps         %xmm10,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,91,201                       ; cvtps2dq      %xmm1,%xmm1
+  DB  102,72,15,126,205                   ; movq          %xmm1,%rbp
+  DB  65,137,234                          ; mov           %ebp,%r10d
+  DB  72,193,237,32                       ; shr           $0x20,%rbp
+  DB  102,15,112,201,78                   ; pshufd        $0x4e,%xmm1,%xmm1
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  65,137,219                          ; mov           %ebx,%r11d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  65,15,182,28,25                     ; movzbl        (%r9,%rbx,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,219                            ; or            %r11d,%ebx
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  65,15,182,44,41                     ; movzbl        (%r9,%rbp,1),%ebp
+  DB  193,229,8                           ; shl           $0x8,%ebp
+  DB  68,9,213                            ; or            %r10d,%ebp
+  DB  102,15,196,205,0                    ; pinsrw        $0x0,%ebp,%xmm1
+  DB  102,15,196,203,1                    ; pinsrw        $0x1,%ebx,%xmm1
+  DB  102,65,15,96,201                    ; punpcklbw     %xmm9,%xmm1
+  DB  102,65,15,97,201                    ; punpcklwd     %xmm9,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,202                        ; mulps         %xmm10,%xmm1
+  DB  76,139,80,16                        ; mov           0x10(%rax),%r10
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,91,210                       ; cvtps2dq      %xmm2,%xmm2
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  65,137,217                          ; mov           %ebx,%r9d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  102,15,112,210,78                   ; pshufd        $0x4e,%xmm2,%xmm2
+  DB  102,72,15,126,213                   ; movq          %xmm2,%rbp
+  DB  65,137,235                          ; mov           %ebp,%r11d
+  DB  72,193,237,32                       ; shr           $0x20,%rbp
+  DB  71,15,182,28,26                     ; movzbl        (%r10,%r11,1),%r11d
+  DB  65,15,182,44,42                     ; movzbl        (%r10,%rbp,1),%ebp
+  DB  193,229,8                           ; shl           $0x8,%ebp
+  DB  68,9,221                            ; or            %r11d,%ebp
+  DB  71,15,182,12,10                     ; movzbl        (%r10,%r9,1),%r9d
+  DB  65,15,182,28,26                     ; movzbl        (%r10,%rbx,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,203                            ; or            %r9d,%ebx
+  DB  102,15,196,211,0                    ; pinsrw        $0x0,%ebx,%xmm2
+  DB  102,15,196,213,1                    ; pinsrw        $0x1,%ebp,%xmm2
+  DB  102,65,15,96,209                    ; punpcklbw     %xmm9,%xmm2
+  DB  102,65,15,97,209                    ; punpcklwd     %xmm9,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,210                        ; mulps         %xmm10,%xmm2
+  DB  72,139,64,24                        ; mov           0x18(%rax),%rax
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  102,15,91,219                       ; cvtps2dq      %xmm3,%xmm3
+  DB  102,72,15,126,221                   ; movq          %xmm3,%rbp
+  DB  65,137,233                          ; mov           %ebp,%r9d
+  DB  72,193,237,32                       ; shr           $0x20,%rbp
+  DB  102,15,112,219,78                   ; pshufd        $0x4e,%xmm3,%xmm3
+  DB  102,72,15,126,219                   ; movq          %xmm3,%rbx
+  DB  65,137,218                          ; mov           %ebx,%r10d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  70,15,182,20,16                     ; movzbl        (%rax,%r10,1),%r10d
+  DB  15,182,28,24                        ; movzbl        (%rax,%rbx,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,211                            ; or            %r10d,%ebx
+  DB  70,15,182,12,8                      ; movzbl        (%rax,%r9,1),%r9d
+  DB  15,182,4,40                         ; movzbl        (%rax,%rbp,1),%eax
+  DB  193,224,8                           ; shl           $0x8,%eax
+  DB  68,9,200                            ; or            %r9d,%eax
+  DB  102,15,196,216,0                    ; pinsrw        $0x0,%eax,%xmm3
+  DB  102,15,196,219,1                    ; pinsrw        $0x1,%ebx,%xmm3
+  DB  102,65,15,96,217                    ; punpcklbw     %xmm9,%xmm3
+  DB  102,65,15,97,217                    ; punpcklwd     %xmm9,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,218                        ; mulps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_byte_tables_rgb_sse2
+_sk_byte_tables_rgb_sse2 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,139,72,24                        ; mov           0x18(%rax),%r9d
+  DB  65,255,201                          ; dec           %r9d
+  DB  102,69,15,110,193                   ; movd          %r9d,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,91,192                       ; cvtps2dq      %xmm0,%xmm0
+  DB  102,73,15,126,193                   ; movq          %xmm0,%r9
+  DB  69,137,202                          ; mov           %r9d,%r10d
+  DB  77,137,203                          ; mov           %r9,%r11
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,15,112,192,78                   ; pshufd        $0x4e,%xmm0,%xmm0
+  DB  102,73,15,126,193                   ; movq          %xmm0,%r9
+  DB  69,137,206                          ; mov           %r9d,%r14d
+  DB  77,137,207                          ; mov           %r9,%r15
+  DB  73,193,239,32                       ; shr           $0x20,%r15
+  DB  72,139,24                           ; mov           (%rax),%rbx
+  DB  76,139,72,8                         ; mov           0x8(%rax),%r9
+  DB  70,15,182,52,51                     ; movzbl        (%rbx,%r14,1),%r14d
+  DB  66,15,182,44,59                     ; movzbl        (%rbx,%r15,1),%ebp
+  DB  193,229,8                           ; shl           $0x8,%ebp
+  DB  68,9,245                            ; or            %r14d,%ebp
+  DB  70,15,182,20,19                     ; movzbl        (%rbx,%r10,1),%r10d
+  DB  66,15,182,28,27                     ; movzbl        (%rbx,%r11,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,211                            ; or            %r10d,%ebx
+  DB  102,15,196,195,0                    ; pinsrw        $0x0,%ebx,%xmm0
+  DB  102,15,196,197,1                    ; pinsrw        $0x1,%ebp,%xmm0
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  102,65,15,96,193                    ; punpcklbw     %xmm9,%xmm0
+  DB  102,65,15,97,193                    ; punpcklwd     %xmm9,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,21,25,50,0,0               ; movaps        0x3219(%rip),%xmm10        # 5c70 <_sk_callback_sse2+0x689>
+  DB  65,15,89,194                        ; mulps         %xmm10,%xmm0
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,15,91,201                       ; cvtps2dq      %xmm1,%xmm1
+  DB  102,72,15,126,205                   ; movq          %xmm1,%rbp
+  DB  65,137,234                          ; mov           %ebp,%r10d
+  DB  72,193,237,32                       ; shr           $0x20,%rbp
+  DB  102,15,112,201,78                   ; pshufd        $0x4e,%xmm1,%xmm1
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  65,137,219                          ; mov           %ebx,%r11d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  71,15,182,28,25                     ; movzbl        (%r9,%r11,1),%r11d
+  DB  65,15,182,28,25                     ; movzbl        (%r9,%rbx,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,219                            ; or            %r11d,%ebx
+  DB  71,15,182,20,17                     ; movzbl        (%r9,%r10,1),%r10d
+  DB  65,15,182,44,41                     ; movzbl        (%r9,%rbp,1),%ebp
+  DB  193,229,8                           ; shl           $0x8,%ebp
+  DB  68,9,213                            ; or            %r10d,%ebp
+  DB  102,15,196,205,0                    ; pinsrw        $0x0,%ebp,%xmm1
+  DB  102,15,196,203,1                    ; pinsrw        $0x1,%ebx,%xmm1
+  DB  102,65,15,96,201                    ; punpcklbw     %xmm9,%xmm1
+  DB  102,65,15,97,201                    ; punpcklwd     %xmm9,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,202                        ; mulps         %xmm10,%xmm1
+  DB  72,139,64,16                        ; mov           0x10(%rax),%rax
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,15,91,210                       ; cvtps2dq      %xmm2,%xmm2
+  DB  102,72,15,126,213                   ; movq          %xmm2,%rbp
+  DB  65,137,233                          ; mov           %ebp,%r9d
+  DB  72,193,237,32                       ; shr           $0x20,%rbp
+  DB  102,15,112,210,78                   ; pshufd        $0x4e,%xmm2,%xmm2
+  DB  102,72,15,126,211                   ; movq          %xmm2,%rbx
+  DB  65,137,218                          ; mov           %ebx,%r10d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  70,15,182,20,16                     ; movzbl        (%rax,%r10,1),%r10d
+  DB  15,182,28,24                        ; movzbl        (%rax,%rbx,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  68,9,211                            ; or            %r10d,%ebx
+  DB  70,15,182,12,8                      ; movzbl        (%rax,%r9,1),%r9d
+  DB  15,182,4,40                         ; movzbl        (%rax,%rbp,1),%eax
+  DB  193,224,8                           ; shl           $0x8,%eax
+  DB  68,9,200                            ; or            %r9d,%eax
+  DB  102,15,196,208,0                    ; pinsrw        $0x0,%eax,%xmm2
+  DB  102,15,196,211,1                    ; pinsrw        $0x1,%ebx,%xmm2
+  DB  102,65,15,96,209                    ; punpcklbw     %xmm9,%xmm2
+  DB  102,65,15,97,209                    ; punpcklwd     %xmm9,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,210                        ; mulps         %xmm10,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_r_sse2
+_sk_table_r_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,112,192,78                ; pshufd        $0x4e,%xmm8,%xmm0
+  DB  102,72,15,126,192                   ; movq          %xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,77,15,126,195                   ; movq          %xmm8,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,71,15,16,4,153                  ; movss         (%r9,%r11,4),%xmm8
+  DB  243,65,15,16,4,129                  ; movss         (%r9,%rax,4),%xmm0
+  DB  68,15,20,192                        ; unpcklps      %xmm0,%xmm8
+  DB  243,65,15,16,4,153                  ; movss         (%r9,%rbx,4),%xmm0
+  DB  243,71,15,16,12,145                 ; movss         (%r9,%r10,4),%xmm9
+  DB  65,15,20,193                        ; unpcklps      %xmm9,%xmm0
+  DB  65,15,20,192                        ; unpcklps      %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_g_sse2
+_sk_table_g_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,193                        ; mulps         %xmm1,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,112,200,78                ; pshufd        $0x4e,%xmm8,%xmm1
+  DB  102,72,15,126,200                   ; movq          %xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,77,15,126,195                   ; movq          %xmm8,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,71,15,16,4,153                  ; movss         (%r9,%r11,4),%xmm8
+  DB  243,65,15,16,12,129                 ; movss         (%r9,%rax,4),%xmm1
+  DB  68,15,20,193                        ; unpcklps      %xmm1,%xmm8
+  DB  243,65,15,16,12,153                 ; movss         (%r9,%rbx,4),%xmm1
+  DB  243,71,15,16,12,145                 ; movss         (%r9,%r10,4),%xmm9
+  DB  65,15,20,201                        ; unpcklps      %xmm9,%xmm1
+  DB  65,15,20,200                        ; unpcklps      %xmm8,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_b_sse2
+_sk_table_b_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,112,208,78                ; pshufd        $0x4e,%xmm8,%xmm2
+  DB  102,72,15,126,208                   ; movq          %xmm2,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,77,15,126,195                   ; movq          %xmm8,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,71,15,16,4,153                  ; movss         (%r9,%r11,4),%xmm8
+  DB  243,65,15,16,20,129                 ; movss         (%r9,%rax,4),%xmm2
+  DB  68,15,20,194                        ; unpcklps      %xmm2,%xmm8
+  DB  243,65,15,16,20,153                 ; movss         (%r9,%rbx,4),%xmm2
+  DB  243,71,15,16,12,145                 ; movss         (%r9,%r10,4),%xmm9
+  DB  65,15,20,209                        ; unpcklps      %xmm9,%xmm2
+  DB  65,15,20,208                        ; unpcklps      %xmm8,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_table_a_sse2
+_sk_table_a_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  139,64,8                            ; mov           0x8(%rax),%eax
+  DB  255,200                             ; dec           %eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  102,69,15,112,192,0                 ; pshufd        $0x0,%xmm8,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,112,216,78                ; pshufd        $0x4e,%xmm8,%xmm3
+  DB  102,72,15,126,216                   ; movq          %xmm3,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,77,15,126,195                   ; movq          %xmm8,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,71,15,16,4,153                  ; movss         (%r9,%r11,4),%xmm8
+  DB  243,65,15,16,28,129                 ; movss         (%r9,%rax,4),%xmm3
+  DB  68,15,20,195                        ; unpcklps      %xmm3,%xmm8
+  DB  243,65,15,16,28,153                 ; movss         (%r9,%rbx,4),%xmm3
+  DB  243,71,15,16,12,145                 ; movss         (%r9,%r10,4),%xmm9
+  DB  65,15,20,217                        ; unpcklps      %xmm9,%xmm3
+  DB  65,15,20,216                        ; unpcklps      %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_r_sse2
+_sk_parametric_r_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,72,16                  ; movss         0x10(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,64,12                  ; movss         0xc(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  243,68,15,16,80,4                   ; movss         0x4(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,208                        ; mulps         %xmm0,%xmm10
+  DB  65,15,194,193,2                     ; cmpleps       %xmm9,%xmm0
+  DB  243,68,15,16,72,24                  ; movss         0x18(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  243,68,15,16,24                     ; movss         (%rax),%xmm11
+  DB  243,68,15,16,72,8                   ; movss         0x8(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,209                        ; addps         %xmm9,%xmm10
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,91,202                        ; cvtdq2ps      %xmm10,%xmm9
+  DB  68,15,89,13,77,47,0,0               ; mulps         0x2f4d(%rip),%xmm9        # 5c80 <_sk_callback_sse2+0x699>
+  DB  68,15,84,21,85,47,0,0               ; andps         0x2f55(%rip),%xmm10        # 5c90 <_sk_callback_sse2+0x6a9>
+  DB  68,15,86,21,93,47,0,0               ; orps          0x2f5d(%rip),%xmm10        # 5ca0 <_sk_callback_sse2+0x6b9>
+  DB  68,15,88,13,101,47,0,0              ; addps         0x2f65(%rip),%xmm9        # 5cb0 <_sk_callback_sse2+0x6c9>
+  DB  68,15,40,37,109,47,0,0              ; movaps        0x2f6d(%rip),%xmm12        # 5cc0 <_sk_callback_sse2+0x6d9>
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,88,21,109,47,0,0              ; addps         0x2f6d(%rip),%xmm10        # 5cd0 <_sk_callback_sse2+0x6e9>
+  DB  68,15,40,37,117,47,0,0              ; movaps        0x2f75(%rip),%xmm12        # 5ce0 <_sk_callback_sse2+0x6f9>
+  DB  69,15,94,226                        ; divps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,226                        ; cvtdq2ps      %xmm10,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,194,236,1                     ; cmpltps       %xmm12,%xmm13
+  DB  68,15,40,21,95,47,0,0               ; movaps        0x2f5f(%rip),%xmm10        # 5cf0 <_sk_callback_sse2+0x709>
+  DB  69,15,84,234                        ; andps         %xmm10,%xmm13
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,88,13,83,47,0,0               ; addps         0x2f53(%rip),%xmm9        # 5d00 <_sk_callback_sse2+0x719>
+  DB  68,15,40,37,91,47,0,0               ; movaps        0x2f5b(%rip),%xmm12        # 5d10 <_sk_callback_sse2+0x729>
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,40,37,91,47,0,0               ; movaps        0x2f5b(%rip),%xmm12        # 5d20 <_sk_callback_sse2+0x739>
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,40,45,95,47,0,0               ; movaps        0x2f5f(%rip),%xmm13        # 5d30 <_sk_callback_sse2+0x749>
+  DB  69,15,94,236                        ; divps         %xmm12,%xmm13
+  DB  69,15,88,233                        ; addps         %xmm9,%xmm13
+  DB  68,15,89,45,95,47,0,0               ; mulps         0x2f5f(%rip),%xmm13        # 5d40 <_sk_callback_sse2+0x759>
+  DB  102,69,15,91,205                    ; cvtps2dq      %xmm13,%xmm9
+  DB  243,68,15,16,96,20                  ; movss         0x14(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  69,15,88,225                        ; addps         %xmm9,%xmm12
+  DB  68,15,84,192                        ; andps         %xmm0,%xmm8
+  DB  65,15,85,196                        ; andnps        %xmm12,%xmm0
+  DB  65,15,86,192                        ; orps          %xmm8,%xmm0
+  DB  65,15,95,195                        ; maxps         %xmm11,%xmm0
+  DB  65,15,93,194                        ; minps         %xmm10,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_g_sse2
+_sk_parametric_g_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,72,16                  ; movss         0x10(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,64,12                  ; movss         0xc(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,89,193                        ; mulps         %xmm1,%xmm8
+  DB  243,68,15,16,80,4                   ; movss         0x4(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  65,15,194,201,2                     ; cmpleps       %xmm9,%xmm1
+  DB  243,68,15,16,72,24                  ; movss         0x18(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  243,68,15,16,24                     ; movss         (%rax),%xmm11
+  DB  243,68,15,16,72,8                   ; movss         0x8(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,209                        ; addps         %xmm9,%xmm10
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,91,202                        ; cvtdq2ps      %xmm10,%xmm9
+  DB  68,15,89,13,223,46,0,0              ; mulps         0x2edf(%rip),%xmm9        # 5d50 <_sk_callback_sse2+0x769>
+  DB  68,15,84,21,231,46,0,0              ; andps         0x2ee7(%rip),%xmm10        # 5d60 <_sk_callback_sse2+0x779>
+  DB  68,15,86,21,239,46,0,0              ; orps          0x2eef(%rip),%xmm10        # 5d70 <_sk_callback_sse2+0x789>
+  DB  68,15,88,13,247,46,0,0              ; addps         0x2ef7(%rip),%xmm9        # 5d80 <_sk_callback_sse2+0x799>
+  DB  68,15,40,37,255,46,0,0              ; movaps        0x2eff(%rip),%xmm12        # 5d90 <_sk_callback_sse2+0x7a9>
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,88,21,255,46,0,0              ; addps         0x2eff(%rip),%xmm10        # 5da0 <_sk_callback_sse2+0x7b9>
+  DB  68,15,40,37,7,47,0,0                ; movaps        0x2f07(%rip),%xmm12        # 5db0 <_sk_callback_sse2+0x7c9>
+  DB  69,15,94,226                        ; divps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,226                        ; cvtdq2ps      %xmm10,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,194,236,1                     ; cmpltps       %xmm12,%xmm13
+  DB  68,15,40,21,241,46,0,0              ; movaps        0x2ef1(%rip),%xmm10        # 5dc0 <_sk_callback_sse2+0x7d9>
+  DB  69,15,84,234                        ; andps         %xmm10,%xmm13
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,88,13,229,46,0,0              ; addps         0x2ee5(%rip),%xmm9        # 5dd0 <_sk_callback_sse2+0x7e9>
+  DB  68,15,40,37,237,46,0,0              ; movaps        0x2eed(%rip),%xmm12        # 5de0 <_sk_callback_sse2+0x7f9>
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,40,37,237,46,0,0              ; movaps        0x2eed(%rip),%xmm12        # 5df0 <_sk_callback_sse2+0x809>
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,40,45,241,46,0,0              ; movaps        0x2ef1(%rip),%xmm13        # 5e00 <_sk_callback_sse2+0x819>
+  DB  69,15,94,236                        ; divps         %xmm12,%xmm13
+  DB  69,15,88,233                        ; addps         %xmm9,%xmm13
+  DB  68,15,89,45,241,46,0,0              ; mulps         0x2ef1(%rip),%xmm13        # 5e10 <_sk_callback_sse2+0x829>
+  DB  102,69,15,91,205                    ; cvtps2dq      %xmm13,%xmm9
+  DB  243,68,15,16,96,20                  ; movss         0x14(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  69,15,88,225                        ; addps         %xmm9,%xmm12
+  DB  68,15,84,193                        ; andps         %xmm1,%xmm8
+  DB  65,15,85,204                        ; andnps        %xmm12,%xmm1
+  DB  65,15,86,200                        ; orps          %xmm8,%xmm1
+  DB  65,15,95,203                        ; maxps         %xmm11,%xmm1
+  DB  65,15,93,202                        ; minps         %xmm10,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_b_sse2
+_sk_parametric_b_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,72,16                  ; movss         0x10(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,64,12                  ; movss         0xc(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  243,68,15,16,80,4                   ; movss         0x4(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,210                        ; mulps         %xmm2,%xmm10
+  DB  65,15,194,209,2                     ; cmpleps       %xmm9,%xmm2
+  DB  243,68,15,16,72,24                  ; movss         0x18(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  243,68,15,16,24                     ; movss         (%rax),%xmm11
+  DB  243,68,15,16,72,8                   ; movss         0x8(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,209                        ; addps         %xmm9,%xmm10
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,91,202                        ; cvtdq2ps      %xmm10,%xmm9
+  DB  68,15,89,13,113,46,0,0              ; mulps         0x2e71(%rip),%xmm9        # 5e20 <_sk_callback_sse2+0x839>
+  DB  68,15,84,21,121,46,0,0              ; andps         0x2e79(%rip),%xmm10        # 5e30 <_sk_callback_sse2+0x849>
+  DB  68,15,86,21,129,46,0,0              ; orps          0x2e81(%rip),%xmm10        # 5e40 <_sk_callback_sse2+0x859>
+  DB  68,15,88,13,137,46,0,0              ; addps         0x2e89(%rip),%xmm9        # 5e50 <_sk_callback_sse2+0x869>
+  DB  68,15,40,37,145,46,0,0              ; movaps        0x2e91(%rip),%xmm12        # 5e60 <_sk_callback_sse2+0x879>
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,88,21,145,46,0,0              ; addps         0x2e91(%rip),%xmm10        # 5e70 <_sk_callback_sse2+0x889>
+  DB  68,15,40,37,153,46,0,0              ; movaps        0x2e99(%rip),%xmm12        # 5e80 <_sk_callback_sse2+0x899>
+  DB  69,15,94,226                        ; divps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,226                        ; cvtdq2ps      %xmm10,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,194,236,1                     ; cmpltps       %xmm12,%xmm13
+  DB  68,15,40,21,131,46,0,0              ; movaps        0x2e83(%rip),%xmm10        # 5e90 <_sk_callback_sse2+0x8a9>
+  DB  69,15,84,234                        ; andps         %xmm10,%xmm13
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,88,13,119,46,0,0              ; addps         0x2e77(%rip),%xmm9        # 5ea0 <_sk_callback_sse2+0x8b9>
+  DB  68,15,40,37,127,46,0,0              ; movaps        0x2e7f(%rip),%xmm12        # 5eb0 <_sk_callback_sse2+0x8c9>
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,40,37,127,46,0,0              ; movaps        0x2e7f(%rip),%xmm12        # 5ec0 <_sk_callback_sse2+0x8d9>
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,40,45,131,46,0,0              ; movaps        0x2e83(%rip),%xmm13        # 5ed0 <_sk_callback_sse2+0x8e9>
+  DB  69,15,94,236                        ; divps         %xmm12,%xmm13
+  DB  69,15,88,233                        ; addps         %xmm9,%xmm13
+  DB  68,15,89,45,131,46,0,0              ; mulps         0x2e83(%rip),%xmm13        # 5ee0 <_sk_callback_sse2+0x8f9>
+  DB  102,69,15,91,205                    ; cvtps2dq      %xmm13,%xmm9
+  DB  243,68,15,16,96,20                  ; movss         0x14(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  69,15,88,225                        ; addps         %xmm9,%xmm12
+  DB  68,15,84,194                        ; andps         %xmm2,%xmm8
+  DB  65,15,85,212                        ; andnps        %xmm12,%xmm2
+  DB  65,15,86,208                        ; orps          %xmm8,%xmm2
+  DB  65,15,95,211                        ; maxps         %xmm11,%xmm2
+  DB  65,15,93,210                        ; minps         %xmm10,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_parametric_a_sse2
+_sk_parametric_a_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,72,16                  ; movss         0x10(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,64,12                  ; movss         0xc(%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  243,68,15,16,80,4                   ; movss         0x4(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  65,15,194,217,2                     ; cmpleps       %xmm9,%xmm3
+  DB  243,68,15,16,72,24                  ; movss         0x18(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  243,68,15,16,24                     ; movss         (%rax),%xmm11
+  DB  243,68,15,16,72,8                   ; movss         0x8(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  69,15,88,209                        ; addps         %xmm9,%xmm10
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,91,202                        ; cvtdq2ps      %xmm10,%xmm9
+  DB  68,15,89,13,3,46,0,0                ; mulps         0x2e03(%rip),%xmm9        # 5ef0 <_sk_callback_sse2+0x909>
+  DB  68,15,84,21,11,46,0,0               ; andps         0x2e0b(%rip),%xmm10        # 5f00 <_sk_callback_sse2+0x919>
+  DB  68,15,86,21,19,46,0,0               ; orps          0x2e13(%rip),%xmm10        # 5f10 <_sk_callback_sse2+0x929>
+  DB  68,15,88,13,27,46,0,0               ; addps         0x2e1b(%rip),%xmm9        # 5f20 <_sk_callback_sse2+0x939>
+  DB  68,15,40,37,35,46,0,0               ; movaps        0x2e23(%rip),%xmm12        # 5f30 <_sk_callback_sse2+0x949>
+  DB  69,15,89,226                        ; mulps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,88,21,35,46,0,0               ; addps         0x2e23(%rip),%xmm10        # 5f40 <_sk_callback_sse2+0x959>
+  DB  68,15,40,37,43,46,0,0               ; movaps        0x2e2b(%rip),%xmm12        # 5f50 <_sk_callback_sse2+0x969>
+  DB  69,15,94,226                        ; divps         %xmm10,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,226                        ; cvtdq2ps      %xmm10,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,194,236,1                     ; cmpltps       %xmm12,%xmm13
+  DB  68,15,40,21,21,46,0,0               ; movaps        0x2e15(%rip),%xmm10        # 5f60 <_sk_callback_sse2+0x979>
+  DB  69,15,84,234                        ; andps         %xmm10,%xmm13
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  69,15,40,233                        ; movaps        %xmm9,%xmm13
+  DB  69,15,92,236                        ; subps         %xmm12,%xmm13
+  DB  68,15,88,13,9,46,0,0                ; addps         0x2e09(%rip),%xmm9        # 5f70 <_sk_callback_sse2+0x989>
+  DB  68,15,40,37,17,46,0,0               ; movaps        0x2e11(%rip),%xmm12        # 5f80 <_sk_callback_sse2+0x999>
+  DB  69,15,89,229                        ; mulps         %xmm13,%xmm12
+  DB  69,15,92,204                        ; subps         %xmm12,%xmm9
+  DB  68,15,40,37,17,46,0,0               ; movaps        0x2e11(%rip),%xmm12        # 5f90 <_sk_callback_sse2+0x9a9>
+  DB  69,15,92,229                        ; subps         %xmm13,%xmm12
+  DB  68,15,40,45,21,46,0,0               ; movaps        0x2e15(%rip),%xmm13        # 5fa0 <_sk_callback_sse2+0x9b9>
+  DB  69,15,94,236                        ; divps         %xmm12,%xmm13
+  DB  69,15,88,233                        ; addps         %xmm9,%xmm13
+  DB  68,15,89,45,21,46,0,0               ; mulps         0x2e15(%rip),%xmm13        # 5fb0 <_sk_callback_sse2+0x9c9>
+  DB  102,69,15,91,205                    ; cvtps2dq      %xmm13,%xmm9
+  DB  243,68,15,16,96,20                  ; movss         0x14(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  69,15,88,225                        ; addps         %xmm9,%xmm12
+  DB  68,15,84,195                        ; andps         %xmm3,%xmm8
+  DB  65,15,85,220                        ; andnps        %xmm12,%xmm3
+  DB  65,15,86,216                        ; orps          %xmm8,%xmm3
+  DB  65,15,95,219                        ; maxps         %xmm11,%xmm3
+  DB  65,15,93,218                        ; minps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lab_to_xyz_sse2
+_sk_lab_to_xyz_sse2 LABEL PROC
+  DB  15,89,5,242,45,0,0                  ; mulps         0x2df2(%rip),%xmm0        # 5fc0 <_sk_callback_sse2+0x9d9>
+  DB  68,15,40,5,250,45,0,0               ; movaps        0x2dfa(%rip),%xmm8        # 5fd0 <_sk_callback_sse2+0x9e9>
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  68,15,40,13,254,45,0,0              ; movaps        0x2dfe(%rip),%xmm9        # 5fe0 <_sk_callback_sse2+0x9f9>
+  DB  65,15,88,201                        ; addps         %xmm9,%xmm1
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  65,15,88,209                        ; addps         %xmm9,%xmm2
+  DB  15,88,5,251,45,0,0                  ; addps         0x2dfb(%rip),%xmm0        # 5ff0 <_sk_callback_sse2+0xa09>
+  DB  15,89,5,4,46,0,0                    ; mulps         0x2e04(%rip),%xmm0        # 6000 <_sk_callback_sse2+0xa19>
+  DB  15,89,13,13,46,0,0                  ; mulps         0x2e0d(%rip),%xmm1        # 6010 <_sk_callback_sse2+0xa29>
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  15,89,21,19,46,0,0                  ; mulps         0x2e13(%rip),%xmm2        # 6020 <_sk_callback_sse2+0xa39>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  68,15,92,202                        ; subps         %xmm2,%xmm9
+  DB  68,15,40,225                        ; movaps        %xmm1,%xmm12
+  DB  69,15,89,228                        ; mulps         %xmm12,%xmm12
+  DB  68,15,89,225                        ; mulps         %xmm1,%xmm12
+  DB  15,40,21,8,46,0,0                   ; movaps        0x2e08(%rip),%xmm2        # 6030 <_sk_callback_sse2+0xa49>
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  69,15,194,196,1                     ; cmpltps       %xmm12,%xmm8
+  DB  68,15,40,21,7,46,0,0                ; movaps        0x2e07(%rip),%xmm10        # 6040 <_sk_callback_sse2+0xa59>
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  68,15,40,29,11,46,0,0               ; movaps        0x2e0b(%rip),%xmm11        # 6050 <_sk_callback_sse2+0xa69>
+  DB  65,15,89,203                        ; mulps         %xmm11,%xmm1
+  DB  69,15,84,224                        ; andps         %xmm8,%xmm12
+  DB  68,15,85,193                        ; andnps        %xmm1,%xmm8
+  DB  69,15,86,196                        ; orps          %xmm12,%xmm8
+  DB  68,15,40,224                        ; movaps        %xmm0,%xmm12
+  DB  69,15,89,228                        ; mulps         %xmm12,%xmm12
+  DB  68,15,89,224                        ; mulps         %xmm0,%xmm12
+  DB  15,40,202                           ; movaps        %xmm2,%xmm1
+  DB  65,15,194,204,1                     ; cmpltps       %xmm12,%xmm1
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  65,15,89,195                        ; mulps         %xmm11,%xmm0
+  DB  68,15,84,225                        ; andps         %xmm1,%xmm12
+  DB  15,85,200                           ; andnps        %xmm0,%xmm1
+  DB  65,15,86,204                        ; orps          %xmm12,%xmm1
+  DB  65,15,40,193                        ; movaps        %xmm9,%xmm0
+  DB  15,89,192                           ; mulps         %xmm0,%xmm0
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  15,194,208,1                        ; cmpltps       %xmm0,%xmm2
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  69,15,89,203                        ; mulps         %xmm11,%xmm9
+  DB  15,84,194                           ; andps         %xmm2,%xmm0
+  DB  65,15,85,209                        ; andnps        %xmm9,%xmm2
+  DB  15,86,208                           ; orps          %xmm0,%xmm2
+  DB  68,15,89,5,187,45,0,0               ; mulps         0x2dbb(%rip),%xmm8        # 6060 <_sk_callback_sse2+0xa79>
+  DB  15,89,21,196,45,0,0                 ; mulps         0x2dc4(%rip),%xmm2        # 6070 <_sk_callback_sse2+0xa89>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_a8_sse2
+_sk_load_a8_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,45                              ; jne           32eb <_sk_load_a8_sse2+0x37>
+  DB  102,65,15,110,4,18                  ; movd          (%r10,%rdx,1),%xmm0
+  DB  102,15,96,192                       ; punpcklbw     %xmm0,%xmm0
+  DB  102,15,97,192                       ; punpcklwd     %xmm0,%xmm0
+  DB  102,15,219,5,172,45,0,0             ; pand          0x2dac(%rip),%xmm0        # 6080 <_sk_callback_sse2+0xa99>
+  DB  15,91,216                           ; cvtdq2ps      %xmm0,%xmm3
+  DB  15,89,29,178,45,0,0                 ; mulps         0x2db2(%rip),%xmm3        # 6090 <_sk_callback_sse2+0xaa9>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,48                              ; je            332c <_sk_load_a8_sse2+0x78>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            3317 <_sk_load_a8_sse2+0x63>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,196                             ; jne           32cc <_sk_load_a8_sse2+0x18>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,192,69                   ; pshufd        $0x45,%xmm0,%xmm0
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,15,110,200                      ; movd          %eax,%xmm1
+  DB  15,198,200,0                        ; shufps        $0x0,%xmm0,%xmm1
+  DB  15,198,200,226                      ; shufps        $0xe2,%xmm0,%xmm1
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,15,110,200                      ; movd          %eax,%xmm1
+  DB  243,15,16,193                       ; movss         %xmm1,%xmm0
+  DB  235,145                             ; jmp           32cc <_sk_load_a8_sse2+0x18>
+
+PUBLIC _sk_gather_a8_sse2
+_sk_gather_a8_sse2 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,72,15,126,192                   ; movq          %xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,15,112,192,78                   ; pshufd        $0x4e,%xmm0,%xmm0
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  65,15,182,44,25                     ; movzbl        (%r9,%rbx,1),%ebp
+  DB  67,15,182,28,25                     ; movzbl        (%r9,%r11,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  9,235                               ; or            %ebp,%ebx
+  DB  67,15,182,44,17                     ; movzbl        (%r9,%r10,1),%ebp
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  193,224,8                           ; shl           $0x8,%eax
+  DB  9,232                               ; or            %ebp,%eax
+  DB  102,15,196,192,0                    ; pinsrw        $0x0,%eax,%xmm0
+  DB  102,15,196,195,1                    ; pinsrw        $0x1,%ebx,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,15,96,193                       ; punpcklbw     %xmm1,%xmm0
+  DB  102,15,97,193                       ; punpcklwd     %xmm1,%xmm0
+  DB  15,91,216                           ; cvtdq2ps      %xmm0,%xmm3
+  DB  15,89,29,210,44,0,0                 ; mulps         0x2cd2(%rip),%xmm3        # 60a0 <_sk_callback_sse2+0xab9>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,15,239,210                      ; pxor          %xmm2,%xmm2
+  DB  91                                  ; pop           %rbx
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_a8_sse2
+_sk_store_a8_sse2 LABEL PROC
+  DB  72,131,236,56                       ; sub           $0x38,%rsp
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  68,15,40,5,192,44,0,0               ; movaps        0x2cc0(%rip),%xmm8        # 60b0 <_sk_callback_sse2+0xac9>
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,114,240,16                ; pslld         $0x10,%xmm8
+  DB  102,65,15,114,224,16                ; psrad         $0x10,%xmm8
+  DB  102,69,15,107,192                   ; packssdw      %xmm8,%xmm8
+  DB  102,69,15,103,192                   ; packuswb      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,17                              ; jne           3425 <_sk_store_a8_sse2+0x46>
+  DB  102,68,15,126,192                   ; movd          %xmm8,%eax
+  DB  65,137,4,18                         ; mov           %eax,(%r10,%rdx,1)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,131,196,56                       ; add           $0x38,%rsp
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,68,15,96,192                    ; punpcklbw     %xmm0,%xmm8
+  DB  102,68,15,97,192                    ; punpcklwd     %xmm0,%xmm8
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,44                              ; je            3468 <_sk_store_a8_sse2+0x89>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,22                              ; je            3458 <_sk_store_a8_sse2+0x79>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,213                             ; jne           341d <_sk_store_a8_sse2+0x3e>
+  DB  102,68,15,127,68,36,32              ; movdqa        %xmm8,0x20(%rsp)
+  DB  138,68,36,40                        ; mov           0x28(%rsp),%al
+  DB  65,136,68,18,2                      ; mov           %al,0x2(%r10,%rdx,1)
+  DB  102,68,15,127,68,36,16              ; movdqa        %xmm8,0x10(%rsp)
+  DB  138,68,36,20                        ; mov           0x14(%rsp),%al
+  DB  65,136,68,18,1                      ; mov           %al,0x1(%r10,%rdx,1)
+  DB  102,68,15,127,4,36                  ; movdqa        %xmm8,(%rsp)
+  DB  138,4,36                            ; mov           (%rsp),%al
+  DB  65,136,4,18                         ; mov           %al,(%r10,%rdx,1)
+  DB  235,166                             ; jmp           341d <_sk_store_a8_sse2+0x3e>
+
+PUBLIC _sk_load_g8_sse2
+_sk_load_g8_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,49                              ; jne           34b2 <_sk_load_g8_sse2+0x3b>
+  DB  102,65,15,110,4,18                  ; movd          (%r10,%rdx,1),%xmm0
+  DB  102,15,96,192                       ; punpcklbw     %xmm0,%xmm0
+  DB  102,15,97,192                       ; punpcklwd     %xmm0,%xmm0
+  DB  102,15,219,5,41,44,0,0              ; pand          0x2c29(%rip),%xmm0        # 60c0 <_sk_callback_sse2+0xad9>
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,47,44,0,0                   ; mulps         0x2c2f(%rip),%xmm0        # 60d0 <_sk_callback_sse2+0xae9>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,54,44,0,0                  ; movaps        0x2c36(%rip),%xmm3        # 60e0 <_sk_callback_sse2+0xaf9>
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,48                              ; je            34f3 <_sk_load_g8_sse2+0x7c>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            34de <_sk_load_g8_sse2+0x67>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,192                             ; jne           348f <_sk_load_g8_sse2+0x18>
+  DB  65,15,182,68,18,2                   ; movzbl        0x2(%r10,%rdx,1),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,192,69                   ; pshufd        $0x45,%xmm0,%xmm0
+  DB  65,15,182,68,18,1                   ; movzbl        0x1(%r10,%rdx,1),%eax
+  DB  102,15,110,200                      ; movd          %eax,%xmm1
+  DB  15,198,200,0                        ; shufps        $0x0,%xmm0,%xmm1
+  DB  15,198,200,226                      ; shufps        $0xe2,%xmm0,%xmm1
+  DB  15,40,193                           ; movaps        %xmm1,%xmm0
+  DB  65,15,182,4,18                      ; movzbl        (%r10,%rdx,1),%eax
+  DB  102,15,110,200                      ; movd          %eax,%xmm1
+  DB  243,15,16,193                       ; movss         %xmm1,%xmm0
+  DB  235,141                             ; jmp           348f <_sk_load_g8_sse2+0x18>
+
+PUBLIC _sk_gather_g8_sse2
+_sk_gather_g8_sse2 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,72,15,126,192                   ; movq          %xmm0,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,15,112,192,78                   ; pshufd        $0x4e,%xmm0,%xmm0
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  65,15,182,44,25                     ; movzbl        (%r9,%rbx,1),%ebp
+  DB  67,15,182,28,25                     ; movzbl        (%r9,%r11,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  9,235                               ; or            %ebp,%ebx
+  DB  67,15,182,44,17                     ; movzbl        (%r9,%r10,1),%ebp
+  DB  65,15,182,4,1                       ; movzbl        (%r9,%rax,1),%eax
+  DB  193,224,8                           ; shl           $0x8,%eax
+  DB  9,232                               ; or            %ebp,%eax
+  DB  102,15,196,192,0                    ; pinsrw        $0x0,%eax,%xmm0
+  DB  102,15,196,195,1                    ; pinsrw        $0x1,%ebx,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,15,96,193                       ; punpcklbw     %xmm1,%xmm0
+  DB  102,15,97,193                       ; punpcklwd     %xmm1,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,91,43,0,0                   ; mulps         0x2b5b(%rip),%xmm0        # 60f0 <_sk_callback_sse2+0xb09>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,98,43,0,0                  ; movaps        0x2b62(%rip),%xmm3        # 6100 <_sk_callback_sse2+0xb19>
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  91                                  ; pop           %rbx
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gather_i8_sse2
+_sk_gather_i8_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,193                          ; mov           %rax,%r9
+  DB  77,133,201                          ; test          %r9,%r9
+  DB  116,5                               ; je            35b7 <_sk_gather_i8_sse2+0xf>
+  DB  76,137,200                          ; mov           %r9,%rax
+  DB  235,2                               ; jmp           35b9 <_sk_gather_i8_sse2+0x11>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  85                                  ; push          %rbp
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,72,15,126,192                   ; movq          %xmm0,%rax
+  DB  65,137,195                          ; mov           %eax,%r11d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,15,112,192,78                   ; pshufd        $0x4e,%xmm0,%xmm0
+  DB  102,72,15,126,195                   ; movq          %xmm0,%rbx
+  DB  65,137,222                          ; mov           %ebx,%r14d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  67,15,182,44,50                     ; movzbl        (%r10,%r14,1),%ebp
+  DB  65,15,182,28,26                     ; movzbl        (%r10,%rbx,1),%ebx
+  DB  193,227,8                           ; shl           $0x8,%ebx
+  DB  9,235                               ; or            %ebp,%ebx
+  DB  67,15,182,44,26                     ; movzbl        (%r10,%r11,1),%ebp
+  DB  65,15,182,4,2                       ; movzbl        (%r10,%rax,1),%eax
+  DB  193,224,8                           ; shl           $0x8,%eax
+  DB  9,232                               ; or            %ebp,%eax
+  DB  102,15,196,192,0                    ; pinsrw        $0x0,%eax,%xmm0
+  DB  102,15,196,195,1                    ; pinsrw        $0x1,%ebx,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,15,96,193                       ; punpcklbw     %xmm1,%xmm0
+  DB  102,15,97,193                       ; punpcklwd     %xmm1,%xmm0
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,72,15,126,200                   ; movq          %xmm1,%rax
+  DB  68,15,182,208                       ; movzbl        %al,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,72,15,126,195                   ; movq          %xmm0,%rbx
+  DB  73,139,105,8                        ; mov           0x8(%r9),%rbp
+  DB  68,15,182,203                       ; movzbl        %bl,%r9d
+  DB  72,193,235,30                       ; shr           $0x1e,%rbx
+  DB  102,15,110,68,29,0                  ; movd          0x0(%rbp,%rbx,1),%xmm0
+  DB  102,15,110,76,133,0                 ; movd          0x0(%rbp,%rax,4),%xmm1
+  DB  102,15,98,193                       ; punpckldq     %xmm1,%xmm0
+  DB  102,70,15,110,76,141,0              ; movd          0x0(%rbp,%r9,4),%xmm9
+  DB  102,66,15,110,76,149,0              ; movd          0x0(%rbp,%r10,4),%xmm1
+  DB  102,68,15,98,201                    ; punpckldq     %xmm1,%xmm9
+  DB  102,68,15,98,200                    ; punpckldq     %xmm0,%xmm9
+  DB  102,15,111,21,123,42,0,0            ; movdqa        0x2a7b(%rip),%xmm2        # 6110 <_sk_callback_sse2+0xb29>
+  DB  102,65,15,111,193                   ; movdqa        %xmm9,%xmm0
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,119,42,0,0               ; movaps        0x2a77(%rip),%xmm8        # 6120 <_sk_callback_sse2+0xb39>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,15,114,209,8                    ; psrld         $0x8,%xmm1
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,65,15,111,217                   ; movdqa        %xmm9,%xmm3
+  DB  102,15,114,211,16                   ; psrld         $0x10,%xmm3
+  DB  102,15,219,218                      ; pand          %xmm2,%xmm3
+  DB  15,91,211                           ; cvtdq2ps      %xmm3,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,65,15,114,209,24                ; psrld         $0x18,%xmm9
+  DB  65,15,91,217                        ; cvtdq2ps      %xmm9,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_565_sse2
+_sk_load_565_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,83                              ; jne           374a <_sk_load_565_sse2+0x5d>
+  DB  243,65,15,126,20,82                 ; movq          (%r10,%rdx,2),%xmm2
+  DB  102,15,97,208                       ; punpcklwd     %xmm0,%xmm2
+  DB  102,15,111,5,39,42,0,0              ; movdqa        0x2a27(%rip),%xmm0        # 6130 <_sk_callback_sse2+0xb49>
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,41,42,0,0                   ; mulps         0x2a29(%rip),%xmm0        # 6140 <_sk_callback_sse2+0xb59>
+  DB  102,15,111,13,49,42,0,0             ; movdqa        0x2a31(%rip),%xmm1        # 6150 <_sk_callback_sse2+0xb69>
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,51,42,0,0                  ; mulps         0x2a33(%rip),%xmm1        # 6160 <_sk_callback_sse2+0xb79>
+  DB  102,15,219,21,59,42,0,0             ; pand          0x2a3b(%rip),%xmm2        # 6170 <_sk_callback_sse2+0xb89>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,65,42,0,0                  ; mulps         0x2a41(%rip),%xmm2        # 6180 <_sk_callback_sse2+0xb99>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,72,42,0,0                  ; movaps        0x2a48(%rip),%xmm3        # 6190 <_sk_callback_sse2+0xba9>
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,210                      ; pxor          %xmm2,%xmm2
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,48                              ; je            378b <_sk_load_565_sse2+0x9e>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            3776 <_sk_load_565_sse2+0x89>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,154                             ; jne           3701 <_sk_load_565_sse2+0x14>
+  DB  65,15,183,68,82,4                   ; movzwl        0x4(%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,208,69                   ; pshufd        $0x45,%xmm0,%xmm2
+  DB  65,15,183,68,82,2                   ; movzwl        0x2(%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  15,198,194,0                        ; shufps        $0x0,%xmm2,%xmm0
+  DB  15,198,194,226                      ; shufps        $0xe2,%xmm2,%xmm0
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  65,15,183,4,82                      ; movzwl        (%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  243,15,16,208                       ; movss         %xmm0,%xmm2
+  DB  233,100,255,255,255                 ; jmpq          3701 <_sk_load_565_sse2+0x14>
+
+PUBLIC _sk_gather_565_sse2
+_sk_gather_565_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,72,15,126,200                   ; movq          %xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,65,15,196,20,89,0               ; pinsrw        $0x0,(%r9,%rbx,2),%xmm2
+  DB  102,67,15,196,20,89,1               ; pinsrw        $0x1,(%r9,%r11,2),%xmm2
+  DB  67,15,183,28,81                     ; movzwl        (%r9,%r10,2),%ebx
+  DB  102,15,196,211,2                    ; pinsrw        $0x2,%ebx,%xmm2
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  102,15,196,208,3                    ; pinsrw        $0x3,%eax,%xmm2
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  102,15,97,208                       ; punpcklwd     %xmm0,%xmm2
+  DB  102,15,111,5,125,41,0,0             ; movdqa        0x297d(%rip),%xmm0        # 61a0 <_sk_callback_sse2+0xbb9>
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,127,41,0,0                  ; mulps         0x297f(%rip),%xmm0        # 61b0 <_sk_callback_sse2+0xbc9>
+  DB  102,15,111,13,135,41,0,0            ; movdqa        0x2987(%rip),%xmm1        # 61c0 <_sk_callback_sse2+0xbd9>
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,137,41,0,0                 ; mulps         0x2989(%rip),%xmm1        # 61d0 <_sk_callback_sse2+0xbe9>
+  DB  102,15,219,21,145,41,0,0            ; pand          0x2991(%rip),%xmm2        # 61e0 <_sk_callback_sse2+0xbf9>
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,151,41,0,0                 ; mulps         0x2997(%rip),%xmm2        # 61f0 <_sk_callback_sse2+0xc09>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,158,41,0,0                 ; movaps        0x299e(%rip),%xmm3        # 6200 <_sk_callback_sse2+0xc19>
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_565_sse2
+_sk_store_565_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  68,15,40,5,158,41,0,0               ; movaps        0x299e(%rip),%xmm8        # 6210 <_sk_callback_sse2+0xc29>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,11                ; pslld         $0xb,%xmm9
+  DB  68,15,40,21,147,41,0,0              ; movaps        0x2993(%rip),%xmm10        # 6220 <_sk_callback_sse2+0xc39>
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,5                 ; pslld         $0x5,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,69,15,86,194                    ; orpd          %xmm10,%xmm8
+  DB  102,65,15,114,240,16                ; pslld         $0x10,%xmm8
+  DB  102,65,15,114,224,16                ; psrad         $0x10,%xmm8
+  DB  102,69,15,107,192                   ; packssdw      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           38cf <_sk_store_565_sse2+0x6a>
+  DB  242,69,15,17,4,82                   ; movsd         %xmm8,(%r10,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,68,15,97,192                    ; punpcklwd     %xmm0,%xmm8
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,36                              ; je            3905 <_sk_store_565_sse2+0xa0>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,18                              ; je            38f9 <_sk_store_565_sse2+0x94>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,222                             ; jne           38cb <_sk_store_565_sse2+0x66>
+  DB  102,65,15,197,192,4                 ; pextrw        $0x4,%xmm8,%eax
+  DB  102,65,137,68,82,4                  ; mov           %ax,0x4(%r10,%rdx,2)
+  DB  102,65,15,197,192,2                 ; pextrw        $0x2,%xmm8,%eax
+  DB  102,65,137,68,82,2                  ; mov           %ax,0x2(%r10,%rdx,2)
+  DB  102,68,15,126,192                   ; movd          %xmm8,%eax
+  DB  102,65,137,4,82                     ; mov           %ax,(%r10,%rdx,2)
+  DB  235,186                             ; jmp           38cb <_sk_store_565_sse2+0x66>
+
+PUBLIC _sk_load_4444_sse2
+_sk_load_4444_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,98                              ; jne           397d <_sk_load_4444_sse2+0x6c>
+  DB  243,65,15,126,28,82                 ; movq          (%r10,%rdx,2),%xmm3
+  DB  102,15,97,216                       ; punpcklwd     %xmm0,%xmm3
+  DB  102,15,111,5,3,41,0,0               ; movdqa        0x2903(%rip),%xmm0        # 6230 <_sk_callback_sse2+0xc49>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,5,41,0,0                    ; mulps         0x2905(%rip),%xmm0        # 6240 <_sk_callback_sse2+0xc59>
+  DB  102,15,111,13,13,41,0,0             ; movdqa        0x290d(%rip),%xmm1        # 6250 <_sk_callback_sse2+0xc69>
+  DB  102,15,219,203                      ; pand          %xmm3,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,15,41,0,0                  ; mulps         0x290f(%rip),%xmm1        # 6260 <_sk_callback_sse2+0xc79>
+  DB  102,15,111,21,23,41,0,0             ; movdqa        0x2917(%rip),%xmm2        # 6270 <_sk_callback_sse2+0xc89>
+  DB  102,15,219,211                      ; pand          %xmm3,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,25,41,0,0                  ; mulps         0x2919(%rip),%xmm2        # 6280 <_sk_callback_sse2+0xc99>
+  DB  102,15,219,29,33,41,0,0             ; pand          0x2921(%rip),%xmm3        # 6290 <_sk_callback_sse2+0xca9>
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  15,89,29,39,41,0,0                  ; mulps         0x2927(%rip),%xmm3        # 62a0 <_sk_callback_sse2+0xcb9>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,48                              ; je            39be <_sk_load_4444_sse2+0xad>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,21                              ; je            39a9 <_sk_load_4444_sse2+0x98>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,139                             ; jne           3925 <_sk_load_4444_sse2+0x14>
+  DB  65,15,183,68,82,4                   ; movzwl        0x4(%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  102,15,112,216,69                   ; pshufd        $0x45,%xmm0,%xmm3
+  DB  65,15,183,68,82,2                   ; movzwl        0x2(%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  15,198,195,0                        ; shufps        $0x0,%xmm3,%xmm0
+  DB  15,198,195,226                      ; shufps        $0xe2,%xmm3,%xmm0
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  65,15,183,4,82                      ; movzwl        (%r10,%rdx,2),%eax
+  DB  102,15,110,192                      ; movd          %eax,%xmm0
+  DB  243,15,16,216                       ; movss         %xmm0,%xmm3
+  DB  233,85,255,255,255                  ; jmpq          3925 <_sk_load_4444_sse2+0x14>
+
+PUBLIC _sk_gather_4444_sse2
+_sk_gather_4444_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,72,15,126,200                   ; movq          %xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,65,15,196,28,89,0               ; pinsrw        $0x0,(%r9,%rbx,2),%xmm3
+  DB  102,67,15,196,28,89,1               ; pinsrw        $0x1,(%r9,%r11,2),%xmm3
+  DB  67,15,183,28,81                     ; movzwl        (%r9,%r10,2),%ebx
+  DB  102,15,196,219,2                    ; pinsrw        $0x2,%ebx,%xmm3
+  DB  65,15,183,4,65                      ; movzwl        (%r9,%rax,2),%eax
+  DB  102,15,196,216,3                    ; pinsrw        $0x3,%eax,%xmm3
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  102,15,97,216                       ; punpcklwd     %xmm0,%xmm3
+  DB  102,15,111,5,90,40,0,0              ; movdqa        0x285a(%rip),%xmm0        # 62b0 <_sk_callback_sse2+0xcc9>
+  DB  102,15,219,195                      ; pand          %xmm3,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  15,89,5,92,40,0,0                   ; mulps         0x285c(%rip),%xmm0        # 62c0 <_sk_callback_sse2+0xcd9>
+  DB  102,15,111,13,100,40,0,0            ; movdqa        0x2864(%rip),%xmm1        # 62d0 <_sk_callback_sse2+0xce9>
+  DB  102,15,219,203                      ; pand          %xmm3,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  15,89,13,102,40,0,0                 ; mulps         0x2866(%rip),%xmm1        # 62e0 <_sk_callback_sse2+0xcf9>
+  DB  102,15,111,21,110,40,0,0            ; movdqa        0x286e(%rip),%xmm2        # 62f0 <_sk_callback_sse2+0xd09>
+  DB  102,15,219,211                      ; pand          %xmm3,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  15,89,21,112,40,0,0                 ; mulps         0x2870(%rip),%xmm2        # 6300 <_sk_callback_sse2+0xd19>
+  DB  102,15,219,29,120,40,0,0            ; pand          0x2878(%rip),%xmm3        # 6310 <_sk_callback_sse2+0xd29>
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  15,89,29,126,40,0,0                 ; mulps         0x287e(%rip),%xmm3        # 6320 <_sk_callback_sse2+0xd39>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_4444_sse2
+_sk_store_4444_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  68,15,40,5,124,40,0,0               ; movaps        0x287c(%rip),%xmm8        # 6330 <_sk_callback_sse2+0xd49>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,12                ; pslld         $0xc,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,8                 ; pslld         $0x8,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,4                 ; pslld         $0x4,%xmm9
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,69,15,86,193                    ; orpd          %xmm9,%xmm8
+  DB  102,69,15,86,194                    ; orpd          %xmm10,%xmm8
+  DB  102,65,15,114,240,16                ; pslld         $0x10,%xmm8
+  DB  102,65,15,114,224,16                ; psrad         $0x10,%xmm8
+  DB  102,69,15,107,192                   ; packssdw      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3b25 <_sk_store_4444_sse2+0x7e>
+  DB  242,69,15,17,4,82                   ; movsd         %xmm8,(%r10,%rdx,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,68,15,97,192                    ; punpcklwd     %xmm0,%xmm8
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,36                              ; je            3b5b <_sk_store_4444_sse2+0xb4>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,18                              ; je            3b4f <_sk_store_4444_sse2+0xa8>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,222                             ; jne           3b21 <_sk_store_4444_sse2+0x7a>
+  DB  102,65,15,197,192,4                 ; pextrw        $0x4,%xmm8,%eax
+  DB  102,65,137,68,82,4                  ; mov           %ax,0x4(%r10,%rdx,2)
+  DB  102,65,15,197,192,2                 ; pextrw        $0x2,%xmm8,%eax
+  DB  102,65,137,68,82,2                  ; mov           %ax,0x2(%r10,%rdx,2)
+  DB  102,68,15,126,192                   ; movd          %xmm8,%eax
+  DB  102,65,137,4,82                     ; mov           %ax,(%r10,%rdx,2)
+  DB  235,186                             ; jmp           3b21 <_sk_store_4444_sse2+0x7a>
+
+PUBLIC _sk_load_8888_sse2
+_sk_load_8888_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,98                              ; jne           3bd3 <_sk_load_8888_sse2+0x6c>
+  DB  243,68,15,111,12,144                ; movdqu        (%rax,%rdx,4),%xmm9
+  DB  102,15,111,21,193,39,0,0            ; movdqa        0x27c1(%rip),%xmm2        # 6340 <_sk_callback_sse2+0xd59>
+  DB  102,65,15,111,193                   ; movdqa        %xmm9,%xmm0
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,189,39,0,0               ; movaps        0x27bd(%rip),%xmm8        # 6350 <_sk_callback_sse2+0xd69>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,15,114,209,8                    ; psrld         $0x8,%xmm1
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,65,15,111,217                   ; movdqa        %xmm9,%xmm3
+  DB  102,15,114,211,16                   ; psrld         $0x10,%xmm3
+  DB  102,15,219,218                      ; pand          %xmm2,%xmm3
+  DB  15,91,211                           ; cvtdq2ps      %xmm3,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,65,15,114,209,24                ; psrld         $0x18,%xmm9
+  DB  65,15,91,217                        ; cvtdq2ps      %xmm9,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,44                              ; je            3c11 <_sk_load_8888_sse2+0xaa>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,18                              ; je            3bfd <_sk_load_8888_sse2+0x96>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,134                             ; jne           3b77 <_sk_load_8888_sse2+0x10>
+  DB  102,15,110,68,144,8                 ; movd          0x8(%rax,%rdx,4),%xmm0
+  DB  102,68,15,112,200,69                ; pshufd        $0x45,%xmm0,%xmm9
+  DB  243,15,16,68,144,4                  ; movss         0x4(%rax,%rdx,4),%xmm0
+  DB  65,15,198,193,0                     ; shufps        $0x0,%xmm9,%xmm0
+  DB  65,15,198,193,226                   ; shufps        $0xe2,%xmm9,%xmm0
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  243,15,16,4,144                     ; movss         (%rax,%rdx,4),%xmm0
+  DB  243,68,15,16,200                    ; movss         %xmm0,%xmm9
+  DB  233,87,255,255,255                  ; jmpq          3b77 <_sk_load_8888_sse2+0x10>
+
+PUBLIC _sk_gather_8888_sse2
+_sk_gather_8888_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,72,15,126,200                   ; movq          %xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,67,15,110,4,153                 ; movd          (%r9,%r11,4),%xmm0
+  DB  102,65,15,110,12,129                ; movd          (%r9,%rax,4),%xmm1
+  DB  102,15,98,193                       ; punpckldq     %xmm1,%xmm0
+  DB  102,69,15,110,12,153                ; movd          (%r9,%rbx,4),%xmm9
+  DB  102,67,15,110,12,145                ; movd          (%r9,%r10,4),%xmm1
+  DB  102,68,15,98,201                    ; punpckldq     %xmm1,%xmm9
+  DB  102,68,15,98,200                    ; punpckldq     %xmm0,%xmm9
+  DB  102,15,111,21,190,38,0,0            ; movdqa        0x26be(%rip),%xmm2        # 6360 <_sk_callback_sse2+0xd79>
+  DB  102,65,15,111,193                   ; movdqa        %xmm9,%xmm0
+  DB  102,15,219,194                      ; pand          %xmm2,%xmm0
+  DB  15,91,192                           ; cvtdq2ps      %xmm0,%xmm0
+  DB  68,15,40,5,186,38,0,0               ; movaps        0x26ba(%rip),%xmm8        # 6370 <_sk_callback_sse2+0xd89>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,15,114,209,8                    ; psrld         $0x8,%xmm1
+  DB  102,15,219,202                      ; pand          %xmm2,%xmm1
+  DB  15,91,201                           ; cvtdq2ps      %xmm1,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,65,15,111,217                   ; movdqa        %xmm9,%xmm3
+  DB  102,15,114,211,16                   ; psrld         $0x10,%xmm3
+  DB  102,15,219,218                      ; pand          %xmm2,%xmm3
+  DB  15,91,211                           ; cvtdq2ps      %xmm3,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,65,15,114,209,24                ; psrld         $0x18,%xmm9
+  DB  65,15,91,217                        ; cvtdq2ps      %xmm9,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_8888_sse2
+_sk_store_8888_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  68,15,40,5,124,38,0,0               ; movaps        0x267c(%rip),%xmm8        # 6380 <_sk_callback_sse2+0xd99>
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,8                 ; pslld         $0x8,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,16                ; pslld         $0x10,%xmm9
+  DB  68,15,89,195                        ; mulps         %xmm3,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,114,240,24                ; pslld         $0x18,%xmm8
+  DB  102,69,15,235,193                   ; por           %xmm9,%xmm8
+  DB  102,69,15,235,194                   ; por           %xmm10,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           3d64 <_sk_store_8888_sse2+0x6d>
+  DB  243,68,15,127,4,144                 ; movdqu        %xmm8,(%rax,%rdx,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,3                        ; and           $0x3,%r9b
+  DB  65,128,249,1                        ; cmp           $0x1,%r9b
+  DB  116,38                              ; je            3d97 <_sk_store_8888_sse2+0xa0>
+  DB  65,128,249,2                        ; cmp           $0x2,%r9b
+  DB  116,19                              ; je            3d8a <_sk_store_8888_sse2+0x93>
+  DB  65,128,249,3                        ; cmp           $0x3,%r9b
+  DB  117,227                             ; jne           3d60 <_sk_store_8888_sse2+0x69>
+  DB  102,69,15,112,200,78                ; pshufd        $0x4e,%xmm8,%xmm9
+  DB  102,68,15,126,76,144,8              ; movd          %xmm9,0x8(%rax,%rdx,4)
+  DB  102,69,15,112,200,229               ; pshufd        $0xe5,%xmm8,%xmm9
+  DB  102,68,15,126,76,144,4              ; movd          %xmm9,0x4(%rax,%rdx,4)
+  DB  102,68,15,126,4,144                 ; movd          %xmm8,(%rax,%rdx,4)
+  DB  235,193                             ; jmp           3d60 <_sk_store_8888_sse2+0x69>
+
+PUBLIC _sk_load_f16_sse2
+_sk_load_f16_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,96,1,0,0                     ; jne           3f0d <_sk_load_f16_sse2+0x16e>
+  DB  102,15,16,4,208                     ; movupd        (%rax,%rdx,8),%xmm0
+  DB  102,15,16,76,208,16                 ; movupd        0x10(%rax,%rdx,8),%xmm1
+  DB  102,68,15,40,192                    ; movapd        %xmm0,%xmm8
+  DB  102,68,15,97,193                    ; punpcklwd     %xmm1,%xmm8
+  DB  102,15,105,193                      ; punpckhwd     %xmm1,%xmm0
+  DB  102,69,15,111,240                   ; movdqa        %xmm8,%xmm14
+  DB  102,68,15,97,240                    ; punpcklwd     %xmm0,%xmm14
+  DB  102,68,15,105,192                   ; punpckhwd     %xmm0,%xmm8
+  DB  102,69,15,239,210                   ; pxor          %xmm10,%xmm10
+  DB  102,65,15,111,206                   ; movdqa        %xmm14,%xmm1
+  DB  102,65,15,97,202                    ; punpcklwd     %xmm10,%xmm1
+  DB  102,68,15,111,13,163,37,0,0         ; movdqa        0x25a3(%rip),%xmm9        # 6390 <_sk_callback_sse2+0xda9>
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,65,15,219,193                   ; pand          %xmm9,%xmm0
+  DB  102,15,239,200                      ; pxor          %xmm0,%xmm1
+  DB  102,15,114,240,16                   ; pslld         $0x10,%xmm0
+  DB  102,68,15,111,233                   ; movdqa        %xmm1,%xmm13
+  DB  102,65,15,114,245,13                ; pslld         $0xd,%xmm13
+  DB  102,68,15,235,232                   ; por           %xmm0,%xmm13
+  DB  102,68,15,111,29,136,37,0,0         ; movdqa        0x2588(%rip),%xmm11        # 63a0 <_sk_callback_sse2+0xdb9>
+  DB  102,69,15,254,235                   ; paddd         %xmm11,%xmm13
+  DB  102,68,15,111,37,138,37,0,0         ; movdqa        0x258a(%rip),%xmm12        # 63b0 <_sk_callback_sse2+0xdc9>
+  DB  102,65,15,239,204                   ; pxor          %xmm12,%xmm1
+  DB  102,15,111,29,141,37,0,0            ; movdqa        0x258d(%rip),%xmm3        # 63c0 <_sk_callback_sse2+0xdd9>
+  DB  102,15,111,195                      ; movdqa        %xmm3,%xmm0
+  DB  102,15,102,193                      ; pcmpgtd       %xmm1,%xmm0
+  DB  102,65,15,223,197                   ; pandn         %xmm13,%xmm0
+  DB  102,65,15,115,222,8                 ; psrldq        $0x8,%xmm14
+  DB  102,69,15,97,242                    ; punpcklwd     %xmm10,%xmm14
+  DB  102,65,15,111,206                   ; movdqa        %xmm14,%xmm1
+  DB  102,65,15,219,201                   ; pand          %xmm9,%xmm1
+  DB  102,68,15,239,241                   ; pxor          %xmm1,%xmm14
+  DB  102,15,114,241,16                   ; pslld         $0x10,%xmm1
+  DB  102,65,15,111,214                   ; movdqa        %xmm14,%xmm2
+  DB  102,15,114,242,13                   ; pslld         $0xd,%xmm2
+  DB  102,15,235,209                      ; por           %xmm1,%xmm2
+  DB  102,65,15,254,211                   ; paddd         %xmm11,%xmm2
+  DB  102,69,15,239,244                   ; pxor          %xmm12,%xmm14
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,65,15,102,206                   ; pcmpgtd       %xmm14,%xmm1
+  DB  102,15,223,202                      ; pandn         %xmm2,%xmm1
+  DB  102,69,15,111,232                   ; movdqa        %xmm8,%xmm13
+  DB  102,69,15,97,234                    ; punpcklwd     %xmm10,%xmm13
+  DB  102,65,15,111,213                   ; movdqa        %xmm13,%xmm2
+  DB  102,65,15,219,209                   ; pand          %xmm9,%xmm2
+  DB  102,68,15,239,234                   ; pxor          %xmm2,%xmm13
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,69,15,111,245                   ; movdqa        %xmm13,%xmm14
+  DB  102,65,15,114,246,13                ; pslld         $0xd,%xmm14
+  DB  102,68,15,235,242                   ; por           %xmm2,%xmm14
+  DB  102,69,15,254,243                   ; paddd         %xmm11,%xmm14
+  DB  102,69,15,239,236                   ; pxor          %xmm12,%xmm13
+  DB  102,15,111,211                      ; movdqa        %xmm3,%xmm2
+  DB  102,65,15,102,213                   ; pcmpgtd       %xmm13,%xmm2
+  DB  102,65,15,223,214                   ; pandn         %xmm14,%xmm2
+  DB  102,65,15,115,216,8                 ; psrldq        $0x8,%xmm8
+  DB  102,69,15,97,194                    ; punpcklwd     %xmm10,%xmm8
+  DB  102,69,15,219,200                   ; pand          %xmm8,%xmm9
+  DB  102,69,15,239,193                   ; pxor          %xmm9,%xmm8
+  DB  102,65,15,114,241,16                ; pslld         $0x10,%xmm9
+  DB  102,69,15,111,208                   ; movdqa        %xmm8,%xmm10
+  DB  102,65,15,114,242,13                ; pslld         $0xd,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  102,69,15,254,211                   ; paddd         %xmm11,%xmm10
+  DB  102,69,15,239,196                   ; pxor          %xmm12,%xmm8
+  DB  102,65,15,102,216                   ; pcmpgtd       %xmm8,%xmm3
+  DB  102,65,15,223,218                   ; pandn         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  242,15,16,4,208                     ; movsd         (%rax,%rdx,8),%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,17                              ; jne           3f29 <_sk_load_f16_sse2+0x18a>
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  233,143,254,255,255                 ; jmpq          3db8 <_sk_load_f16_sse2+0x19>
+  DB  102,15,22,68,208,8                  ; movhpd        0x8(%rax,%rdx,8),%xmm0
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  15,130,123,254,255,255              ; jb            3db8 <_sk_load_f16_sse2+0x19>
+  DB  242,15,16,76,208,16                 ; movsd         0x10(%rax,%rdx,8),%xmm1
+  DB  233,112,254,255,255                 ; jmpq          3db8 <_sk_load_f16_sse2+0x19>
+
+PUBLIC _sk_gather_f16_sse2
+_sk_gather_f16_sse2 LABEL PROC
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,110,80,16                    ; movd          0x10(%rax),%xmm2
+  DB  102,15,112,210,0                    ; pshufd        $0x0,%xmm2,%xmm2
+  DB  102,15,112,217,245                  ; pshufd        $0xf5,%xmm1,%xmm3
+  DB  102,15,244,218                      ; pmuludq       %xmm2,%xmm3
+  DB  102,15,112,219,232                  ; pshufd        $0xe8,%xmm3,%xmm3
+  DB  102,15,244,209                      ; pmuludq       %xmm1,%xmm2
+  DB  102,15,112,202,232                  ; pshufd        $0xe8,%xmm2,%xmm1
+  DB  102,15,98,203                       ; punpckldq     %xmm3,%xmm1
+  DB  243,15,91,192                       ; cvttps2dq     %xmm0,%xmm0
+  DB  102,15,254,193                      ; paddd         %xmm1,%xmm0
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,72,15,126,200                   ; movq          %xmm1,%rax
+  DB  65,137,194                          ; mov           %eax,%r10d
+  DB  72,193,232,32                       ; shr           $0x20,%rax
+  DB  102,73,15,126,195                   ; movq          %xmm0,%r11
+  DB  68,137,219                          ; mov           %r11d,%ebx
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  243,67,15,126,4,217                 ; movq          (%r9,%r11,8),%xmm0
+  DB  243,65,15,126,12,217                ; movq          (%r9,%rbx,8),%xmm1
+  DB  102,15,108,200                      ; punpcklqdq    %xmm0,%xmm1
+  DB  243,65,15,126,4,193                 ; movq          (%r9,%rax,8),%xmm0
+  DB  243,67,15,126,20,209                ; movq          (%r9,%r10,8),%xmm2
+  DB  102,15,108,208                      ; punpcklqdq    %xmm0,%xmm2
+  DB  102,68,15,111,193                   ; movdqa        %xmm1,%xmm8
+  DB  102,68,15,97,194                    ; punpcklwd     %xmm2,%xmm8
+  DB  102,15,105,202                      ; punpckhwd     %xmm2,%xmm1
+  DB  102,69,15,111,240                   ; movdqa        %xmm8,%xmm14
+  DB  102,68,15,97,241                    ; punpcklwd     %xmm1,%xmm14
+  DB  102,68,15,105,193                   ; punpckhwd     %xmm1,%xmm8
+  DB  102,69,15,239,210                   ; pxor          %xmm10,%xmm10
+  DB  102,65,15,111,206                   ; movdqa        %xmm14,%xmm1
+  DB  102,65,15,97,202                    ; punpcklwd     %xmm10,%xmm1
+  DB  102,68,15,111,13,223,35,0,0         ; movdqa        0x23df(%rip),%xmm9        # 63d0 <_sk_callback_sse2+0xde9>
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,65,15,219,193                   ; pand          %xmm9,%xmm0
+  DB  102,15,239,200                      ; pxor          %xmm0,%xmm1
+  DB  102,15,114,240,16                   ; pslld         $0x10,%xmm0
+  DB  102,68,15,111,233                   ; movdqa        %xmm1,%xmm13
+  DB  102,65,15,114,245,13                ; pslld         $0xd,%xmm13
+  DB  102,68,15,235,232                   ; por           %xmm0,%xmm13
+  DB  102,68,15,111,29,196,35,0,0         ; movdqa        0x23c4(%rip),%xmm11        # 63e0 <_sk_callback_sse2+0xdf9>
+  DB  102,69,15,254,235                   ; paddd         %xmm11,%xmm13
+  DB  102,68,15,111,37,198,35,0,0         ; movdqa        0x23c6(%rip),%xmm12        # 63f0 <_sk_callback_sse2+0xe09>
+  DB  102,65,15,239,204                   ; pxor          %xmm12,%xmm1
+  DB  102,15,111,29,201,35,0,0            ; movdqa        0x23c9(%rip),%xmm3        # 6400 <_sk_callback_sse2+0xe19>
+  DB  102,15,111,195                      ; movdqa        %xmm3,%xmm0
+  DB  102,15,102,193                      ; pcmpgtd       %xmm1,%xmm0
+  DB  102,65,15,223,197                   ; pandn         %xmm13,%xmm0
+  DB  102,65,15,115,222,8                 ; psrldq        $0x8,%xmm14
+  DB  102,69,15,97,242                    ; punpcklwd     %xmm10,%xmm14
+  DB  102,65,15,111,206                   ; movdqa        %xmm14,%xmm1
+  DB  102,65,15,219,201                   ; pand          %xmm9,%xmm1
+  DB  102,68,15,239,241                   ; pxor          %xmm1,%xmm14
+  DB  102,15,114,241,16                   ; pslld         $0x10,%xmm1
+  DB  102,65,15,111,214                   ; movdqa        %xmm14,%xmm2
+  DB  102,15,114,242,13                   ; pslld         $0xd,%xmm2
+  DB  102,15,235,209                      ; por           %xmm1,%xmm2
+  DB  102,65,15,254,211                   ; paddd         %xmm11,%xmm2
+  DB  102,69,15,239,244                   ; pxor          %xmm12,%xmm14
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,65,15,102,206                   ; pcmpgtd       %xmm14,%xmm1
+  DB  102,15,223,202                      ; pandn         %xmm2,%xmm1
+  DB  102,69,15,111,232                   ; movdqa        %xmm8,%xmm13
+  DB  102,69,15,97,234                    ; punpcklwd     %xmm10,%xmm13
+  DB  102,65,15,111,213                   ; movdqa        %xmm13,%xmm2
+  DB  102,65,15,219,209                   ; pand          %xmm9,%xmm2
+  DB  102,68,15,239,234                   ; pxor          %xmm2,%xmm13
+  DB  102,15,114,242,16                   ; pslld         $0x10,%xmm2
+  DB  102,69,15,111,245                   ; movdqa        %xmm13,%xmm14
+  DB  102,65,15,114,246,13                ; pslld         $0xd,%xmm14
+  DB  102,68,15,235,242                   ; por           %xmm2,%xmm14
+  DB  102,69,15,254,243                   ; paddd         %xmm11,%xmm14
+  DB  102,69,15,239,236                   ; pxor          %xmm12,%xmm13
+  DB  102,15,111,211                      ; movdqa        %xmm3,%xmm2
+  DB  102,65,15,102,213                   ; pcmpgtd       %xmm13,%xmm2
+  DB  102,65,15,223,214                   ; pandn         %xmm14,%xmm2
+  DB  102,65,15,115,216,8                 ; psrldq        $0x8,%xmm8
+  DB  102,69,15,97,194                    ; punpcklwd     %xmm10,%xmm8
+  DB  102,69,15,219,200                   ; pand          %xmm8,%xmm9
+  DB  102,69,15,239,193                   ; pxor          %xmm9,%xmm8
+  DB  102,65,15,114,241,16                ; pslld         $0x10,%xmm9
+  DB  102,69,15,111,208                   ; movdqa        %xmm8,%xmm10
+  DB  102,65,15,114,242,13                ; pslld         $0xd,%xmm10
+  DB  102,69,15,235,209                   ; por           %xmm9,%xmm10
+  DB  102,69,15,254,211                   ; paddd         %xmm11,%xmm10
+  DB  102,69,15,239,196                   ; pxor          %xmm12,%xmm8
+  DB  102,65,15,102,216                   ; pcmpgtd       %xmm8,%xmm3
+  DB  102,65,15,223,218                   ; pandn         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  91                                  ; pop           %rbx
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_store_f16_sse2
+_sk_store_f16_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,0                            ; mov           (%rax),%rax
+  DB  102,68,15,111,21,240,34,0,0         ; movdqa        0x22f0(%rip),%xmm10        # 6410 <_sk_callback_sse2+0xe29>
+  DB  102,68,15,111,224                   ; movdqa        %xmm0,%xmm12
+  DB  102,69,15,219,226                   ; pand          %xmm10,%xmm12
+  DB  102,68,15,111,232                   ; movdqa        %xmm0,%xmm13
+  DB  102,69,15,239,236                   ; pxor          %xmm12,%xmm13
+  DB  102,68,15,111,13,227,34,0,0         ; movdqa        0x22e3(%rip),%xmm9        # 6420 <_sk_callback_sse2+0xe39>
+  DB  102,65,15,114,212,16                ; psrld         $0x10,%xmm12
+  DB  102,69,15,111,193                   ; movdqa        %xmm9,%xmm8
+  DB  102,69,15,102,197                   ; pcmpgtd       %xmm13,%xmm8
+  DB  102,65,15,114,213,13                ; psrld         $0xd,%xmm13
+  DB  102,68,15,111,29,212,34,0,0         ; movdqa        0x22d4(%rip),%xmm11        # 6430 <_sk_callback_sse2+0xe49>
+  DB  102,69,15,235,227                   ; por           %xmm11,%xmm12
+  DB  102,69,15,254,229                   ; paddd         %xmm13,%xmm12
+  DB  102,65,15,114,244,16                ; pslld         $0x10,%xmm12
+  DB  102,65,15,114,228,16                ; psrad         $0x10,%xmm12
+  DB  102,69,15,223,196                   ; pandn         %xmm12,%xmm8
+  DB  102,69,15,107,192                   ; packssdw      %xmm8,%xmm8
+  DB  102,68,15,111,225                   ; movdqa        %xmm1,%xmm12
+  DB  102,69,15,219,226                   ; pand          %xmm10,%xmm12
+  DB  102,68,15,111,241                   ; movdqa        %xmm1,%xmm14
+  DB  102,69,15,239,244                   ; pxor          %xmm12,%xmm14
+  DB  102,65,15,114,212,16                ; psrld         $0x10,%xmm12
+  DB  102,69,15,111,233                   ; movdqa        %xmm9,%xmm13
+  DB  102,69,15,102,238                   ; pcmpgtd       %xmm14,%xmm13
+  DB  102,65,15,114,214,13                ; psrld         $0xd,%xmm14
+  DB  102,69,15,235,227                   ; por           %xmm11,%xmm12
+  DB  102,69,15,254,230                   ; paddd         %xmm14,%xmm12
+  DB  102,65,15,114,244,16                ; pslld         $0x10,%xmm12
+  DB  102,65,15,114,228,16                ; psrad         $0x10,%xmm12
+  DB  102,69,15,223,236                   ; pandn         %xmm12,%xmm13
+  DB  102,69,15,107,237                   ; packssdw      %xmm13,%xmm13
+  DB  102,68,15,111,242                   ; movdqa        %xmm2,%xmm14
+  DB  102,69,15,219,242                   ; pand          %xmm10,%xmm14
+  DB  102,68,15,111,250                   ; movdqa        %xmm2,%xmm15
+  DB  102,69,15,239,254                   ; pxor          %xmm14,%xmm15
+  DB  102,65,15,114,214,16                ; psrld         $0x10,%xmm14
+  DB  102,69,15,111,225                   ; movdqa        %xmm9,%xmm12
+  DB  102,69,15,102,231                   ; pcmpgtd       %xmm15,%xmm12
+  DB  102,65,15,114,215,13                ; psrld         $0xd,%xmm15
+  DB  102,69,15,235,243                   ; por           %xmm11,%xmm14
+  DB  102,69,15,254,247                   ; paddd         %xmm15,%xmm14
+  DB  102,65,15,114,246,16                ; pslld         $0x10,%xmm14
+  DB  102,65,15,114,230,16                ; psrad         $0x10,%xmm14
+  DB  102,69,15,223,230                   ; pandn         %xmm14,%xmm12
+  DB  102,69,15,107,228                   ; packssdw      %xmm12,%xmm12
+  DB  102,68,15,219,211                   ; pand          %xmm3,%xmm10
+  DB  102,68,15,111,243                   ; movdqa        %xmm3,%xmm14
+  DB  102,69,15,239,242                   ; pxor          %xmm10,%xmm14
+  DB  102,65,15,114,210,16                ; psrld         $0x10,%xmm10
+  DB  102,69,15,102,206                   ; pcmpgtd       %xmm14,%xmm9
+  DB  102,65,15,114,214,13                ; psrld         $0xd,%xmm14
+  DB  102,69,15,235,211                   ; por           %xmm11,%xmm10
+  DB  102,69,15,254,214                   ; paddd         %xmm14,%xmm10
+  DB  102,65,15,114,242,16                ; pslld         $0x10,%xmm10
+  DB  102,65,15,114,226,16                ; psrad         $0x10,%xmm10
+  DB  102,69,15,223,202                   ; pandn         %xmm10,%xmm9
+  DB  102,69,15,107,201                   ; packssdw      %xmm9,%xmm9
+  DB  102,69,15,97,197                    ; punpcklwd     %xmm13,%xmm8
+  DB  102,69,15,97,225                    ; punpcklwd     %xmm9,%xmm12
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,69,15,98,204                    ; punpckldq     %xmm12,%xmm9
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,21                              ; jne           427e <_sk_store_f16_sse2+0x16c>
+  DB  68,15,17,12,208                     ; movups        %xmm9,(%rax,%rdx,8)
+  DB  102,69,15,106,196                   ; punpckhdq     %xmm12,%xmm8
+  DB  243,68,15,127,68,208,16             ; movdqu        %xmm8,0x10(%rax,%rdx,8)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,68,15,214,12,208                ; movq          %xmm9,(%rax,%rdx,8)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            427a <_sk_store_f16_sse2+0x168>
+  DB  102,68,15,23,76,208,8               ; movhpd        %xmm9,0x8(%rax,%rdx,8)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            427a <_sk_store_f16_sse2+0x168>
+  DB  102,69,15,106,196                   ; punpckhdq     %xmm12,%xmm8
+  DB  102,68,15,214,68,208,16             ; movq          %xmm8,0x10(%rax,%rdx,8)
+  DB  235,213                             ; jmp           427a <_sk_store_f16_sse2+0x168>
+
+PUBLIC _sk_load_u16_be_sse2
+_sk_load_u16_be_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,190,0,0,0                    ; jne           4379 <_sk_load_u16_be_sse2+0xd4>
+  DB  102,65,15,16,4,65                   ; movupd        (%r9,%rax,2),%xmm0
+  DB  102,65,15,16,76,65,16               ; movupd        0x10(%r9,%rax,2),%xmm1
+  DB  102,15,40,208                       ; movapd        %xmm0,%xmm2
+  DB  102,15,97,209                       ; punpcklwd     %xmm1,%xmm2
+  DB  102,15,105,193                      ; punpckhwd     %xmm1,%xmm0
+  DB  102,15,111,202                      ; movdqa        %xmm2,%xmm1
+  DB  102,15,97,200                       ; punpcklwd     %xmm0,%xmm1
+  DB  102,15,105,208                      ; punpckhwd     %xmm0,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,113,240,8                    ; psllw         $0x8,%xmm0
+  DB  102,15,112,217,78                   ; pshufd        $0x4e,%xmm1,%xmm3
+  DB  102,15,113,209,8                    ; psrlw         $0x8,%xmm1
+  DB  102,15,235,200                      ; por           %xmm0,%xmm1
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  102,65,15,97,201                    ; punpcklwd     %xmm9,%xmm1
+  DB  15,91,193                           ; cvtdq2ps      %xmm1,%xmm0
+  DB  68,15,40,5,52,33,0,0                ; movaps        0x2134(%rip),%xmm8        # 6440 <_sk_callback_sse2+0xe59>
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,113,241,8                    ; psllw         $0x8,%xmm1
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,15,235,217                      ; por           %xmm1,%xmm3
+  DB  102,65,15,97,217                    ; punpcklwd     %xmm9,%xmm3
+  DB  15,91,203                           ; cvtdq2ps      %xmm3,%xmm1
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  102,68,15,111,210                   ; movdqa        %xmm2,%xmm10
+  DB  102,65,15,113,242,8                 ; psllw         $0x8,%xmm10
+  DB  102,15,112,218,78                   ; pshufd        $0x4e,%xmm2,%xmm3
+  DB  102,15,113,210,8                    ; psrlw         $0x8,%xmm2
+  DB  102,65,15,235,210                   ; por           %xmm10,%xmm2
+  DB  102,65,15,97,209                    ; punpcklwd     %xmm9,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  102,68,15,111,211                   ; movdqa        %xmm3,%xmm10
+  DB  102,65,15,113,242,8                 ; psllw         $0x8,%xmm10
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,65,15,235,218                   ; por           %xmm10,%xmm3
+  DB  102,65,15,97,217                    ; punpcklwd     %xmm9,%xmm3
+  DB  15,91,219                           ; cvtdq2ps      %xmm3,%xmm3
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  242,65,15,16,4,65                   ; movsd         (%r9,%rax,2),%xmm0
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,17                              ; jne           4396 <_sk_load_u16_be_sse2+0xf1>
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  233,50,255,255,255                  ; jmpq          42c8 <_sk_load_u16_be_sse2+0x23>
+  DB  102,65,15,22,68,65,8                ; movhpd        0x8(%r9,%rax,2),%xmm0
+  DB  102,15,87,201                       ; xorpd         %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  15,130,29,255,255,255               ; jb            42c8 <_sk_load_u16_be_sse2+0x23>
+  DB  242,65,15,16,76,65,16               ; movsd         0x10(%r9,%rax,2),%xmm1
+  DB  233,17,255,255,255                  ; jmpq          42c8 <_sk_load_u16_be_sse2+0x23>
+
+PUBLIC _sk_load_rgb_u16_be_sse2
+_sk_load_rgb_u16_be_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,82                         ; lea           (%rdx,%rdx,2),%rax
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,175,0,0,0                    ; jne           4478 <_sk_load_rgb_u16_be_sse2+0xc1>
+  DB  243,65,15,111,20,65                 ; movdqu        (%r9,%rax,2),%xmm2
+  DB  243,65,15,111,92,65,8               ; movdqu        0x8(%r9,%rax,2),%xmm3
+  DB  102,15,115,219,4                    ; psrldq        $0x4,%xmm3
+  DB  102,15,111,194                      ; movdqa        %xmm2,%xmm0
+  DB  102,15,115,216,6                    ; psrldq        $0x6,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,115,217,6                    ; psrldq        $0x6,%xmm1
+  DB  102,15,97,211                       ; punpcklwd     %xmm3,%xmm2
+  DB  102,15,97,193                       ; punpcklwd     %xmm1,%xmm0
+  DB  102,15,111,202                      ; movdqa        %xmm2,%xmm1
+  DB  102,15,97,200                       ; punpcklwd     %xmm0,%xmm1
+  DB  102,15,112,217,78                   ; pshufd        $0x4e,%xmm1,%xmm3
+  DB  102,15,105,208                      ; punpckhwd     %xmm0,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,113,240,8                    ; psllw         $0x8,%xmm0
+  DB  102,15,113,209,8                    ; psrlw         $0x8,%xmm1
+  DB  102,15,235,200                      ; por           %xmm0,%xmm1
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  102,65,15,97,200                    ; punpcklwd     %xmm8,%xmm1
+  DB  15,91,193                           ; cvtdq2ps      %xmm1,%xmm0
+  DB  68,15,40,13,35,32,0,0               ; movaps        0x2023(%rip),%xmm9        # 6450 <_sk_callback_sse2+0xe69>
+  DB  65,15,89,193                        ; mulps         %xmm9,%xmm0
+  DB  102,15,111,203                      ; movdqa        %xmm3,%xmm1
+  DB  102,15,113,241,8                    ; psllw         $0x8,%xmm1
+  DB  102,15,113,211,8                    ; psrlw         $0x8,%xmm3
+  DB  102,15,235,217                      ; por           %xmm1,%xmm3
+  DB  102,65,15,97,216                    ; punpcklwd     %xmm8,%xmm3
+  DB  15,91,203                           ; cvtdq2ps      %xmm3,%xmm1
+  DB  65,15,89,201                        ; mulps         %xmm9,%xmm1
+  DB  102,15,111,218                      ; movdqa        %xmm2,%xmm3
+  DB  102,15,113,243,8                    ; psllw         $0x8,%xmm3
+  DB  102,15,113,210,8                    ; psrlw         $0x8,%xmm2
+  DB  102,15,235,211                      ; por           %xmm3,%xmm2
+  DB  102,65,15,97,208                    ; punpcklwd     %xmm8,%xmm2
+  DB  15,91,210                           ; cvtdq2ps      %xmm2,%xmm2
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,234,31,0,0                 ; movaps        0x1fea(%rip),%xmm3        # 6460 <_sk_callback_sse2+0xe79>
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,65,15,110,20,65                 ; movd          (%r9,%rax,2),%xmm2
+  DB  102,65,15,196,84,65,4,2             ; pinsrw        $0x2,0x4(%r9,%rax,2),%xmm2
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,13                              ; jne           449d <_sk_load_rgb_u16_be_sse2+0xe6>
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  233,80,255,255,255                  ; jmpq          43ed <_sk_load_rgb_u16_be_sse2+0x36>
+  DB  102,65,15,110,68,65,6               ; movd          0x6(%r9,%rax,2),%xmm0
+  DB  102,65,15,196,68,65,10,2            ; pinsrw        $0x2,0xa(%r9,%rax,2),%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,24                              ; jb            44ce <_sk_load_rgb_u16_be_sse2+0x117>
+  DB  102,65,15,110,92,65,12              ; movd          0xc(%r9,%rax,2),%xmm3
+  DB  102,65,15,196,92,65,16,2            ; pinsrw        $0x2,0x10(%r9,%rax,2),%xmm3
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  233,31,255,255,255                  ; jmpq          43ed <_sk_load_rgb_u16_be_sse2+0x36>
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  233,22,255,255,255                  ; jmpq          43ed <_sk_load_rgb_u16_be_sse2+0x36>
+
+PUBLIC _sk_store_u16_be_sse2
+_sk_store_u16_be_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  72,141,4,149,0,0,0,0                ; lea           0x0(,%rdx,4),%rax
+  DB  68,15,40,21,132,31,0,0              ; movaps        0x1f84(%rip),%xmm10        # 6470 <_sk_callback_sse2+0xe89>
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  69,15,89,194                        ; mulps         %xmm10,%xmm8
+  DB  102,69,15,91,192                    ; cvtps2dq      %xmm8,%xmm8
+  DB  102,65,15,114,240,16                ; pslld         $0x10,%xmm8
+  DB  102,65,15,114,224,16                ; psrad         $0x10,%xmm8
+  DB  102,69,15,107,192                   ; packssdw      %xmm8,%xmm8
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,65,15,113,208,8                 ; psrlw         $0x8,%xmm8
+  DB  102,69,15,235,193                   ; por           %xmm9,%xmm8
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  69,15,89,202                        ; mulps         %xmm10,%xmm9
+  DB  102,69,15,91,217                    ; cvtps2dq      %xmm9,%xmm11
+  DB  102,65,15,114,243,16                ; pslld         $0x10,%xmm11
+  DB  102,65,15,114,227,16                ; psrad         $0x10,%xmm11
+  DB  102,69,15,107,219                   ; packssdw      %xmm11,%xmm11
+  DB  102,69,15,111,203                   ; movdqa        %xmm11,%xmm9
+  DB  102,65,15,113,241,8                 ; psllw         $0x8,%xmm9
+  DB  102,65,15,113,211,8                 ; psrlw         $0x8,%xmm11
+  DB  102,69,15,235,217                   ; por           %xmm9,%xmm11
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  69,15,89,202                        ; mulps         %xmm10,%xmm9
+  DB  102,69,15,91,201                    ; cvtps2dq      %xmm9,%xmm9
+  DB  102,65,15,114,241,16                ; pslld         $0x10,%xmm9
+  DB  102,65,15,114,225,16                ; psrad         $0x10,%xmm9
+  DB  102,69,15,107,201                   ; packssdw      %xmm9,%xmm9
+  DB  102,69,15,111,225                   ; movdqa        %xmm9,%xmm12
+  DB  102,65,15,113,244,8                 ; psllw         $0x8,%xmm12
+  DB  102,65,15,113,209,8                 ; psrlw         $0x8,%xmm9
+  DB  102,69,15,235,204                   ; por           %xmm12,%xmm9
+  DB  68,15,89,211                        ; mulps         %xmm3,%xmm10
+  DB  102,69,15,91,210                    ; cvtps2dq      %xmm10,%xmm10
+  DB  102,65,15,114,242,16                ; pslld         $0x10,%xmm10
+  DB  102,65,15,114,226,16                ; psrad         $0x10,%xmm10
+  DB  102,69,15,107,210                   ; packssdw      %xmm10,%xmm10
+  DB  102,69,15,111,226                   ; movdqa        %xmm10,%xmm12
+  DB  102,65,15,113,244,8                 ; psllw         $0x8,%xmm12
+  DB  102,65,15,113,210,8                 ; psrlw         $0x8,%xmm10
+  DB  102,69,15,235,212                   ; por           %xmm12,%xmm10
+  DB  102,69,15,97,195                    ; punpcklwd     %xmm11,%xmm8
+  DB  102,69,15,97,202                    ; punpcklwd     %xmm10,%xmm9
+  DB  102,69,15,111,208                   ; movdqa        %xmm8,%xmm10
+  DB  102,69,15,98,209                    ; punpckldq     %xmm9,%xmm10
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,21                              ; jne           45e6 <_sk_store_u16_be_sse2+0x10f>
+  DB  69,15,17,20,65                      ; movups        %xmm10,(%r9,%rax,2)
+  DB  102,69,15,106,193                   ; punpckhdq     %xmm9,%xmm8
+  DB  243,69,15,127,68,65,16              ; movdqu        %xmm8,0x10(%r9,%rax,2)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,69,15,214,20,65                 ; movq          %xmm10,(%r9,%rax,2)
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,240                             ; je            45e2 <_sk_store_u16_be_sse2+0x10b>
+  DB  102,69,15,23,84,65,8                ; movhpd        %xmm10,0x8(%r9,%rax,2)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,227                             ; jb            45e2 <_sk_store_u16_be_sse2+0x10b>
+  DB  102,69,15,106,193                   ; punpckhdq     %xmm9,%xmm8
+  DB  102,69,15,214,68,65,16              ; movq          %xmm8,0x10(%r9,%rax,2)
+  DB  235,213                             ; jmp           45e2 <_sk_store_u16_be_sse2+0x10b>
+
+PUBLIC _sk_load_f32_sse2
+_sk_load_f32_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  76,141,12,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r9
+  DB  72,137,208                          ; mov           %rdx,%rax
+  DB  72,193,224,4                        ; shl           $0x4,%rax
+  DB  69,15,16,4,2                        ; movups        (%r10,%rax,1),%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,66                              ; jne           466d <_sk_load_f32_sse2+0x60>
+  DB  67,15,16,68,138,16                  ; movups        0x10(%r10,%r9,4),%xmm0
+  DB  67,15,16,92,138,32                  ; movups        0x20(%r10,%r9,4),%xmm3
+  DB  71,15,16,76,138,48                  ; movups        0x30(%r10,%r9,4),%xmm9
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  15,20,208                           ; unpcklps      %xmm0,%xmm2
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  65,15,20,201                        ; unpcklps      %xmm9,%xmm1
+  DB  68,15,21,192                        ; unpckhps      %xmm0,%xmm8
+  DB  65,15,21,217                        ; unpckhps      %xmm9,%xmm3
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  15,18,202                           ; movhlps       %xmm2,%xmm1
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  102,15,20,211                       ; unpcklpd      %xmm3,%xmm2
+  DB  65,15,18,216                        ; movhlps       %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  117,8                               ; jne           467f <_sk_load_f32_sse2+0x72>
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  235,190                             ; jmp           463d <_sk_load_f32_sse2+0x30>
+  DB  67,15,16,68,138,16                  ; movups        0x10(%r10,%r9,4),%xmm0
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,8                               ; jb            4693 <_sk_load_f32_sse2+0x86>
+  DB  67,15,16,92,138,32                  ; movups        0x20(%r10,%r9,4),%xmm3
+  DB  235,170                             ; jmp           463d <_sk_load_f32_sse2+0x30>
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  235,165                             ; jmp           463d <_sk_load_f32_sse2+0x30>
+
+PUBLIC _sk_store_f32_sse2
+_sk_store_f32_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,16                           ; mov           (%rax),%r10
+  DB  76,141,12,149,0,0,0,0               ; lea           0x0(,%rdx,4),%r9
+  DB  72,137,208                          ; mov           %rdx,%rax
+  DB  72,193,224,4                        ; shl           $0x4,%rax
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  68,15,20,201                        ; unpcklps      %xmm1,%xmm9
+  DB  68,15,40,194                        ; movaps        %xmm2,%xmm8
+  DB  68,15,20,195                        ; unpcklps      %xmm3,%xmm8
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  68,15,21,209                        ; unpckhps      %xmm1,%xmm10
+  DB  68,15,40,218                        ; movaps        %xmm2,%xmm11
+  DB  68,15,21,219                        ; unpckhps      %xmm3,%xmm11
+  DB  69,15,40,225                        ; movaps        %xmm9,%xmm12
+  DB  102,69,15,20,224                    ; unpcklpd      %xmm8,%xmm12
+  DB  69,15,18,193                        ; movhlps       %xmm9,%xmm8
+  DB  69,15,40,202                        ; movaps        %xmm10,%xmm9
+  DB  102,69,15,20,203                    ; unpcklpd      %xmm11,%xmm9
+  DB  102,69,15,17,36,2                   ; movupd        %xmm12,(%r10,%rax,1)
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,29                              ; jne           470a <_sk_store_f32_sse2+0x72>
+  DB  102,69,15,21,211                    ; unpckhpd      %xmm11,%xmm10
+  DB  71,15,17,68,138,16                  ; movups        %xmm8,0x10(%r10,%r9,4)
+  DB  102,71,15,17,76,138,32              ; movupd        %xmm9,0x20(%r10,%r9,4)
+  DB  102,71,15,17,84,138,48              ; movupd        %xmm10,0x30(%r10,%r9,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  73,131,248,1                        ; cmp           $0x1,%r8
+  DB  116,246                             ; je            4706 <_sk_store_f32_sse2+0x6e>
+  DB  71,15,17,68,138,16                  ; movups        %xmm8,0x10(%r10,%r9,4)
+  DB  73,131,248,3                        ; cmp           $0x3,%r8
+  DB  114,234                             ; jb            4706 <_sk_store_f32_sse2+0x6e>
+  DB  102,71,15,17,76,138,32              ; movupd        %xmm9,0x20(%r10,%r9,4)
+  DB  235,225                             ; jmp           4706 <_sk_store_f32_sse2+0x6e>
+
+PUBLIC _sk_clamp_x_sse2
+_sk_clamp_x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  68,15,93,192                        ; minps         %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_y_sse2
+_sk_clamp_y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,193                        ; maxps         %xmm1,%xmm8
+  DB  243,15,16,8                         ; movss         (%rax),%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  68,15,93,193                        ; minps         %xmm1,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,200                        ; movaps        %xmm8,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_sse2
+_sk_repeat_x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,94,200                        ; divps         %xmm8,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  69,15,194,202,1                     ; cmpltps       %xmm10,%xmm9
+  DB  68,15,84,13,245,28,0,0              ; andps         0x1cf5(%rip),%xmm9        # 6480 <_sk_callback_sse2+0xe99>
+  DB  69,15,92,209                        ; subps         %xmm9,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,92,194                        ; subps         %xmm10,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_y_sse2
+_sk_repeat_y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  69,15,94,200                        ; divps         %xmm8,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  69,15,194,202,1                     ; cmpltps       %xmm10,%xmm9
+  DB  68,15,84,13,203,28,0,0              ; andps         0x1ccb(%rip),%xmm9        # 6490 <_sk_callback_sse2+0xea9>
+  DB  69,15,92,209                        ; subps         %xmm9,%xmm10
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  65,15,92,202                        ; subps         %xmm10,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_sse2
+_sk_mirror_x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  243,69,15,88,192                    ; addss         %xmm8,%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,208                        ; movaps        %xmm0,%xmm10
+  DB  69,15,94,208                        ; divps         %xmm8,%xmm10
+  DB  243,69,15,91,218                    ; cvttps2dq     %xmm10,%xmm11
+  DB  69,15,91,219                        ; cvtdq2ps      %xmm11,%xmm11
+  DB  69,15,194,211,1                     ; cmpltps       %xmm11,%xmm10
+  DB  68,15,84,21,143,28,0,0              ; andps         0x1c8f(%rip),%xmm10        # 64a0 <_sk_callback_sse2+0xeb9>
+  DB  69,15,87,228                        ; xorps         %xmm12,%xmm12
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  65,15,92,195                        ; subps         %xmm11,%xmm0
+  DB  65,15,92,193                        ; subps         %xmm9,%xmm0
+  DB  68,15,92,224                        ; subps         %xmm0,%xmm12
+  DB  65,15,84,196                        ; andps         %xmm12,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_y_sse2
+_sk_mirror_y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  65,15,92,201                        ; subps         %xmm9,%xmm1
+  DB  243,69,15,88,192                    ; addss         %xmm8,%xmm8
+  DB  69,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm8
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  69,15,94,208                        ; divps         %xmm8,%xmm10
+  DB  243,69,15,91,218                    ; cvttps2dq     %xmm10,%xmm11
+  DB  69,15,91,219                        ; cvtdq2ps      %xmm11,%xmm11
+  DB  69,15,194,211,1                     ; cmpltps       %xmm11,%xmm10
+  DB  68,15,84,21,67,28,0,0               ; andps         0x1c43(%rip),%xmm10        # 64b0 <_sk_callback_sse2+0xec9>
+  DB  69,15,87,228                        ; xorps         %xmm12,%xmm12
+  DB  69,15,92,218                        ; subps         %xmm10,%xmm11
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  65,15,92,203                        ; subps         %xmm11,%xmm1
+  DB  65,15,92,201                        ; subps         %xmm9,%xmm1
+  DB  68,15,92,225                        ; subps         %xmm1,%xmm12
+  DB  65,15,84,204                        ; andps         %xmm12,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clamp_x_1_sse2
+_sk_clamp_x_1_sse2 LABEL PROC
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  68,15,95,192                        ; maxps         %xmm0,%xmm8
+  DB  68,15,93,5,35,28,0,0                ; minps         0x1c23(%rip),%xmm8        # 64c0 <_sk_callback_sse2+0xed9>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_repeat_x_1_sse2
+_sk_repeat_x_1_sse2 LABEL PROC
+  DB  243,68,15,91,192                    ; cvttps2dq     %xmm0,%xmm8
+  DB  69,15,91,192                        ; cvtdq2ps      %xmm8,%xmm8
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,194,200,1                     ; cmpltps       %xmm8,%xmm9
+  DB  68,15,84,13,17,28,0,0               ; andps         0x1c11(%rip),%xmm9        # 64d0 <_sk_callback_sse2+0xee9>
+  DB  69,15,92,193                        ; subps         %xmm9,%xmm8
+  DB  65,15,92,192                        ; subps         %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_mirror_x_1_sse2
+_sk_mirror_x_1_sse2 LABEL PROC
+  DB  68,15,40,5,13,28,0,0                ; movaps        0x1c0d(%rip),%xmm8        # 64e0 <_sk_callback_sse2+0xef9>
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,13,17,28,0,0               ; movaps        0x1c11(%rip),%xmm9        # 64f0 <_sk_callback_sse2+0xf09>
+  DB  68,15,89,200                        ; mulps         %xmm0,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  69,15,194,202,1                     ; cmpltps       %xmm10,%xmm9
+  DB  68,15,84,13,7,28,0,0                ; andps         0x1c07(%rip),%xmm9        # 6500 <_sk_callback_sse2+0xf19>
+  DB  69,15,87,219                        ; xorps         %xmm11,%xmm11
+  DB  69,15,92,209                        ; subps         %xmm9,%xmm10
+  DB  69,15,88,210                        ; addps         %xmm10,%xmm10
+  DB  65,15,92,194                        ; subps         %xmm10,%xmm0
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,92,216                        ; subps         %xmm0,%xmm11
+  DB  65,15,84,195                        ; andps         %xmm11,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_luminance_to_alpha_sse2
+_sk_luminance_to_alpha_sse2 LABEL PROC
+  DB  15,40,218                           ; movaps        %xmm2,%xmm3
+  DB  15,89,5,237,27,0,0                  ; mulps         0x1bed(%rip),%xmm0        # 6510 <_sk_callback_sse2+0xf29>
+  DB  15,89,13,246,27,0,0                 ; mulps         0x1bf6(%rip),%xmm1        # 6520 <_sk_callback_sse2+0xf39>
+  DB  15,88,200                           ; addps         %xmm0,%xmm1
+  DB  15,89,29,252,27,0,0                 ; mulps         0x1bfc(%rip),%xmm3        # 6530 <_sk_callback_sse2+0xf49>
+  DB  15,88,217                           ; addps         %xmm1,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_2x3_sse2
+_sk_matrix_2x3_sse2 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,16                  ; movss         0x10(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,68,15,16,80,12                  ; movss         0xc(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,20                  ; movss         0x14(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_3x4_sse2
+_sk_matrix_3x4_sse2 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,68,15,16,80,12                  ; movss         0xc(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,24                  ; movss         0x18(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,36                  ; movss         0x24(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,28                  ; movss         0x1c(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,40                  ; movss         0x28(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,20                  ; movss         0x14(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,32                  ; movss         0x20(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,44                 ; movss         0x2c(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  68,15,89,226                        ; mulps         %xmm2,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,217                        ; mulps         %xmm9,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x5_sse2
+_sk_matrix_4x5_sse2 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,32                  ; movss         0x20(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,48                  ; movss         0x30(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,64                 ; movss         0x40(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,194                        ; addps         %xmm10,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,68,15,16,80,20                  ; movss         0x14(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,36                  ; movss         0x24(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,52                  ; movss         0x34(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,68                 ; movss         0x44(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  68,15,89,227                        ; mulps         %xmm3,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  68,15,89,218                        ; mulps         %xmm2,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,24                  ; movss         0x18(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,40                  ; movss         0x28(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,56                 ; movss         0x38(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  243,68,15,16,112,72                 ; movss         0x48(%rax),%xmm14
+  DB  69,15,198,246,0                     ; shufps        $0x0,%xmm14,%xmm14
+  DB  68,15,89,235                        ; mulps         %xmm3,%xmm13
+  DB  69,15,88,238                        ; addps         %xmm14,%xmm13
+  DB  68,15,89,226                        ; mulps         %xmm2,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,217                        ; mulps         %xmm9,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  243,68,15,16,88,12                  ; movss         0xc(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,28                  ; movss         0x1c(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  243,68,15,16,104,44                 ; movss         0x2c(%rax),%xmm13
+  DB  69,15,198,237,0                     ; shufps        $0x0,%xmm13,%xmm13
+  DB  243,68,15,16,112,60                 ; movss         0x3c(%rax),%xmm14
+  DB  69,15,198,246,0                     ; shufps        $0x0,%xmm14,%xmm14
+  DB  243,68,15,16,120,76                 ; movss         0x4c(%rax),%xmm15
+  DB  69,15,198,255,0                     ; shufps        $0x0,%xmm15,%xmm15
+  DB  68,15,89,243                        ; mulps         %xmm3,%xmm14
+  DB  69,15,88,247                        ; addps         %xmm15,%xmm14
+  DB  68,15,89,234                        ; mulps         %xmm2,%xmm13
+  DB  69,15,88,238                        ; addps         %xmm14,%xmm13
+  DB  69,15,89,225                        ; mulps         %xmm9,%xmm12
+  DB  69,15,88,229                        ; addps         %xmm13,%xmm12
+  DB  69,15,89,216                        ; mulps         %xmm8,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,210                        ; movaps        %xmm10,%xmm2
+  DB  65,15,40,219                        ; movaps        %xmm11,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_4x3_sse2
+_sk_matrix_4x3_sse2 LABEL PROC
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,15,16,80,16                     ; movss         0x10(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,32                     ; movss         0x20(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,194                           ; addps         %xmm2,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,20                     ; movss         0x14(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,36                     ; movss         0x24(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,89,209                        ; mulps         %xmm9,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,24                     ; movss         0x18(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  243,68,15,16,80,40                  ; movss         0x28(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  65,15,89,217                        ; mulps         %xmm9,%xmm3
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  243,15,16,88,12                     ; movss         0xc(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  243,68,15,16,80,28                  ; movss         0x1c(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,44                  ; movss         0x2c(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  65,15,88,218                        ; addps         %xmm10,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_matrix_perspective_sse2
+_sk_matrix_perspective_sse2 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,68,15,16,72,4                   ; movss         0x4(%rax),%xmm9
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,80,8                   ; movss         0x8(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  65,15,88,193                        ; addps         %xmm9,%xmm0
+  DB  243,68,15,16,72,12                  ; movss         0xc(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  243,68,15,16,80,16                  ; movss         0x10(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,20                  ; movss         0x14(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  68,15,89,209                        ; mulps         %xmm1,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  69,15,88,202                        ; addps         %xmm10,%xmm9
+  DB  243,68,15,16,80,24                  ; movss         0x18(%rax),%xmm10
+  DB  69,15,198,210,0                     ; shufps        $0x0,%xmm10,%xmm10
+  DB  243,68,15,16,88,28                  ; movss         0x1c(%rax),%xmm11
+  DB  69,15,198,219,0                     ; shufps        $0x0,%xmm11,%xmm11
+  DB  243,68,15,16,96,32                  ; movss         0x20(%rax),%xmm12
+  DB  69,15,198,228,0                     ; shufps        $0x0,%xmm12,%xmm12
+  DB  68,15,89,217                        ; mulps         %xmm1,%xmm11
+  DB  69,15,88,220                        ; addps         %xmm12,%xmm11
+  DB  69,15,89,208                        ; mulps         %xmm8,%xmm10
+  DB  69,15,88,211                        ; addps         %xmm11,%xmm10
+  DB  65,15,83,202                        ; rcpps         %xmm10,%xmm1
+  DB  15,89,193                           ; mulps         %xmm1,%xmm0
+  DB  68,15,89,201                        ; mulps         %xmm1,%xmm9
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,201                        ; movaps        %xmm9,%xmm1
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_gradient_sse2
+_sk_evenly_spaced_gradient_sse2 LABEL PROC
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,139,24                           ; mov           (%rax),%rbx
+  DB  76,139,112,8                        ; mov           0x8(%rax),%r14
+  DB  72,255,203                          ; dec           %rbx
+  DB  120,7                               ; js            4d70 <_sk_evenly_spaced_gradient_sse2+0x18>
+  DB  243,72,15,42,203                    ; cvtsi2ss      %rbx,%xmm1
+  DB  235,21                              ; jmp           4d85 <_sk_evenly_spaced_gradient_sse2+0x2d>
+  DB  73,137,217                          ; mov           %rbx,%r9
+  DB  73,209,233                          ; shr           %r9
+  DB  131,227,1                           ; and           $0x1,%ebx
+  DB  76,9,203                            ; or            %r9,%rbx
+  DB  243,72,15,42,203                    ; cvtsi2ss      %rbx,%xmm1
+  DB  243,15,88,201                       ; addss         %xmm1,%xmm1
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  15,89,200                           ; mulps         %xmm0,%xmm1
+  DB  243,15,91,201                       ; cvttps2dq     %xmm1,%xmm1
+  DB  102,15,112,209,78                   ; pshufd        $0x4e,%xmm1,%xmm2
+  DB  102,73,15,126,211                   ; movq          %xmm2,%r11
+  DB  69,137,217                          ; mov           %r11d,%r9d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  65,137,218                          ; mov           %ebx,%r10d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  243,65,15,16,12,158                 ; movss         (%r14,%rbx,4),%xmm1
+  DB  243,67,15,16,20,158                 ; movss         (%r14,%r11,4),%xmm2
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  243,71,15,16,4,150                  ; movss         (%r14,%r10,4),%xmm8
+  DB  243,67,15,16,20,142                 ; movss         (%r14,%r9,4),%xmm2
+  DB  68,15,20,194                        ; unpcklps      %xmm2,%xmm8
+  DB  68,15,20,193                        ; unpcklps      %xmm1,%xmm8
+  DB  76,139,112,40                       ; mov           0x28(%rax),%r14
+  DB  243,65,15,16,12,158                 ; movss         (%r14,%rbx,4),%xmm1
+  DB  243,67,15,16,20,158                 ; movss         (%r14,%r11,4),%xmm2
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  243,71,15,16,12,150                 ; movss         (%r14,%r10,4),%xmm9
+  DB  243,67,15,16,20,142                 ; movss         (%r14,%r9,4),%xmm2
+  DB  68,15,20,202                        ; unpcklps      %xmm2,%xmm9
+  DB  68,15,20,201                        ; unpcklps      %xmm1,%xmm9
+  DB  76,139,112,16                       ; mov           0x10(%rax),%r14
+  DB  243,65,15,16,20,158                 ; movss         (%r14,%rbx,4),%xmm2
+  DB  243,67,15,16,12,158                 ; movss         (%r14,%r11,4),%xmm1
+  DB  15,20,209                           ; unpcklps      %xmm1,%xmm2
+  DB  243,67,15,16,12,150                 ; movss         (%r14,%r10,4),%xmm1
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  15,20,203                           ; unpcklps      %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  76,139,112,48                       ; mov           0x30(%rax),%r14
+  DB  243,65,15,16,20,158                 ; movss         (%r14,%rbx,4),%xmm2
+  DB  243,67,15,16,28,158                 ; movss         (%r14,%r11,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  243,71,15,16,20,150                 ; movss         (%r14,%r10,4),%xmm10
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  68,15,20,211                        ; unpcklps      %xmm3,%xmm10
+  DB  68,15,20,210                        ; unpcklps      %xmm2,%xmm10
+  DB  76,139,112,24                       ; mov           0x18(%rax),%r14
+  DB  243,69,15,16,28,158                 ; movss         (%r14,%rbx,4),%xmm11
+  DB  243,67,15,16,20,158                 ; movss         (%r14,%r11,4),%xmm2
+  DB  68,15,20,218                        ; unpcklps      %xmm2,%xmm11
+  DB  243,67,15,16,20,150                 ; movss         (%r14,%r10,4),%xmm2
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  65,15,20,211                        ; unpcklps      %xmm11,%xmm2
+  DB  76,139,112,56                       ; mov           0x38(%rax),%r14
+  DB  243,69,15,16,36,158                 ; movss         (%r14,%rbx,4),%xmm12
+  DB  243,67,15,16,28,158                 ; movss         (%r14,%r11,4),%xmm3
+  DB  68,15,20,227                        ; unpcklps      %xmm3,%xmm12
+  DB  243,71,15,16,28,150                 ; movss         (%r14,%r10,4),%xmm11
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  68,15,20,219                        ; unpcklps      %xmm3,%xmm11
+  DB  69,15,20,220                        ; unpcklps      %xmm12,%xmm11
+  DB  76,139,112,32                       ; mov           0x20(%rax),%r14
+  DB  243,69,15,16,36,158                 ; movss         (%r14,%rbx,4),%xmm12
+  DB  243,67,15,16,28,158                 ; movss         (%r14,%r11,4),%xmm3
+  DB  68,15,20,227                        ; unpcklps      %xmm3,%xmm12
+  DB  243,67,15,16,28,150                 ; movss         (%r14,%r10,4),%xmm3
+  DB  243,71,15,16,44,142                 ; movss         (%r14,%r9,4),%xmm13
+  DB  65,15,20,221                        ; unpcklps      %xmm13,%xmm3
+  DB  65,15,20,220                        ; unpcklps      %xmm12,%xmm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  243,68,15,16,36,152                 ; movss         (%rax,%rbx,4),%xmm12
+  DB  243,70,15,16,44,152                 ; movss         (%rax,%r11,4),%xmm13
+  DB  69,15,20,229                        ; unpcklps      %xmm13,%xmm12
+  DB  243,70,15,16,44,144                 ; movss         (%rax,%r10,4),%xmm13
+  DB  243,70,15,16,52,136                 ; movss         (%rax,%r9,4),%xmm14
+  DB  69,15,20,238                        ; unpcklps      %xmm14,%xmm13
+  DB  69,15,20,236                        ; unpcklps      %xmm12,%xmm13
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  15,89,200                           ; mulps         %xmm0,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  15,89,208                           ; mulps         %xmm0,%xmm2
+  DB  65,15,88,211                        ; addps         %xmm11,%xmm2
+  DB  15,89,216                           ; mulps         %xmm0,%xmm3
+  DB  65,15,88,221                        ; addps         %xmm13,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gauss_a_to_rgba_sse2
+_sk_gauss_a_to_rgba_sse2 LABEL PROC
+  DB  15,40,5,47,22,0,0                   ; movaps        0x162f(%rip),%xmm0        # 6540 <_sk_callback_sse2+0xf59>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,53,22,0,0                   ; addps         0x1635(%rip),%xmm0        # 6550 <_sk_callback_sse2+0xf69>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,59,22,0,0                   ; addps         0x163b(%rip),%xmm0        # 6560 <_sk_callback_sse2+0xf79>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,65,22,0,0                   ; addps         0x1641(%rip),%xmm0        # 6570 <_sk_callback_sse2+0xf89>
+  DB  15,89,195                           ; mulps         %xmm3,%xmm0
+  DB  15,88,5,71,22,0,0                   ; addps         0x1647(%rip),%xmm0        # 6580 <_sk_callback_sse2+0xf99>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,200                           ; movaps        %xmm0,%xmm1
+  DB  15,40,208                           ; movaps        %xmm0,%xmm2
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_gradient_sse2
+_sk_gradient_sse2 LABEL PROC
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  83                                  ; push          %rbx
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,8                            ; mov           (%rax),%r9
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  73,131,249,2                        ; cmp           $0x2,%r9
+  DB  114,50                              ; jb            4f8c <_sk_gradient_sse2+0x46>
+  DB  72,139,88,72                        ; mov           0x48(%rax),%rbx
+  DB  73,255,201                          ; dec           %r9
+  DB  72,131,195,4                        ; add           $0x4,%rbx
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  15,40,21,32,22,0,0                  ; movaps        0x1620(%rip),%xmm2        # 6590 <_sk_callback_sse2+0xfa9>
+  DB  243,15,16,27                        ; movss         (%rbx),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  15,194,216,2                        ; cmpleps       %xmm0,%xmm3
+  DB  15,84,218                           ; andps         %xmm2,%xmm3
+  DB  102,15,254,203                      ; paddd         %xmm3,%xmm1
+  DB  72,131,195,4                        ; add           $0x4,%rbx
+  DB  73,255,201                          ; dec           %r9
+  DB  117,228                             ; jne           4f70 <_sk_gradient_sse2+0x2a>
+  DB  102,15,112,209,78                   ; pshufd        $0x4e,%xmm1,%xmm2
+  DB  102,73,15,126,211                   ; movq          %xmm2,%r11
+  DB  69,137,217                          ; mov           %r11d,%r9d
+  DB  73,193,235,32                       ; shr           $0x20,%r11
+  DB  102,72,15,126,203                   ; movq          %xmm1,%rbx
+  DB  65,137,218                          ; mov           %ebx,%r10d
+  DB  72,193,235,32                       ; shr           $0x20,%rbx
+  DB  76,139,112,8                        ; mov           0x8(%rax),%r14
+  DB  76,139,120,16                       ; mov           0x10(%rax),%r15
+  DB  243,65,15,16,12,158                 ; movss         (%r14,%rbx,4),%xmm1
+  DB  243,67,15,16,20,158                 ; movss         (%r14,%r11,4),%xmm2
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  243,71,15,16,4,150                  ; movss         (%r14,%r10,4),%xmm8
+  DB  243,67,15,16,20,142                 ; movss         (%r14,%r9,4),%xmm2
+  DB  68,15,20,194                        ; unpcklps      %xmm2,%xmm8
+  DB  68,15,20,193                        ; unpcklps      %xmm1,%xmm8
+  DB  76,139,112,40                       ; mov           0x28(%rax),%r14
+  DB  243,65,15,16,12,158                 ; movss         (%r14,%rbx,4),%xmm1
+  DB  243,67,15,16,20,158                 ; movss         (%r14,%r11,4),%xmm2
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  243,71,15,16,12,150                 ; movss         (%r14,%r10,4),%xmm9
+  DB  243,67,15,16,20,142                 ; movss         (%r14,%r9,4),%xmm2
+  DB  68,15,20,202                        ; unpcklps      %xmm2,%xmm9
+  DB  68,15,20,201                        ; unpcklps      %xmm1,%xmm9
+  DB  243,65,15,16,20,159                 ; movss         (%r15,%rbx,4),%xmm2
+  DB  243,67,15,16,12,159                 ; movss         (%r15,%r11,4),%xmm1
+  DB  15,20,209                           ; unpcklps      %xmm1,%xmm2
+  DB  243,67,15,16,12,151                 ; movss         (%r15,%r10,4),%xmm1
+  DB  243,67,15,16,28,143                 ; movss         (%r15,%r9,4),%xmm3
+  DB  15,20,203                           ; unpcklps      %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  76,139,112,48                       ; mov           0x30(%rax),%r14
+  DB  243,65,15,16,20,158                 ; movss         (%r14,%rbx,4),%xmm2
+  DB  243,67,15,16,28,158                 ; movss         (%r14,%r11,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  243,71,15,16,20,150                 ; movss         (%r14,%r10,4),%xmm10
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  68,15,20,211                        ; unpcklps      %xmm3,%xmm10
+  DB  68,15,20,210                        ; unpcklps      %xmm2,%xmm10
+  DB  76,139,112,24                       ; mov           0x18(%rax),%r14
+  DB  243,69,15,16,28,158                 ; movss         (%r14,%rbx,4),%xmm11
+  DB  243,67,15,16,20,158                 ; movss         (%r14,%r11,4),%xmm2
+  DB  68,15,20,218                        ; unpcklps      %xmm2,%xmm11
+  DB  243,67,15,16,20,150                 ; movss         (%r14,%r10,4),%xmm2
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  15,20,211                           ; unpcklps      %xmm3,%xmm2
+  DB  65,15,20,211                        ; unpcklps      %xmm11,%xmm2
+  DB  76,139,112,56                       ; mov           0x38(%rax),%r14
+  DB  243,69,15,16,36,158                 ; movss         (%r14,%rbx,4),%xmm12
+  DB  243,67,15,16,28,158                 ; movss         (%r14,%r11,4),%xmm3
+  DB  68,15,20,227                        ; unpcklps      %xmm3,%xmm12
+  DB  243,71,15,16,28,150                 ; movss         (%r14,%r10,4),%xmm11
+  DB  243,67,15,16,28,142                 ; movss         (%r14,%r9,4),%xmm3
+  DB  68,15,20,219                        ; unpcklps      %xmm3,%xmm11
+  DB  69,15,20,220                        ; unpcklps      %xmm12,%xmm11
+  DB  76,139,112,32                       ; mov           0x20(%rax),%r14
+  DB  243,69,15,16,36,158                 ; movss         (%r14,%rbx,4),%xmm12
+  DB  243,67,15,16,28,158                 ; movss         (%r14,%r11,4),%xmm3
+  DB  68,15,20,227                        ; unpcklps      %xmm3,%xmm12
+  DB  243,67,15,16,28,150                 ; movss         (%r14,%r10,4),%xmm3
+  DB  243,71,15,16,44,142                 ; movss         (%r14,%r9,4),%xmm13
+  DB  65,15,20,221                        ; unpcklps      %xmm13,%xmm3
+  DB  65,15,20,220                        ; unpcklps      %xmm12,%xmm3
+  DB  72,139,64,64                        ; mov           0x40(%rax),%rax
+  DB  243,68,15,16,36,152                 ; movss         (%rax,%rbx,4),%xmm12
+  DB  243,70,15,16,44,152                 ; movss         (%rax,%r11,4),%xmm13
+  DB  69,15,20,229                        ; unpcklps      %xmm13,%xmm12
+  DB  243,70,15,16,44,144                 ; movss         (%rax,%r10,4),%xmm13
+  DB  243,70,15,16,52,136                 ; movss         (%rax,%r9,4),%xmm14
+  DB  69,15,20,238                        ; unpcklps      %xmm14,%xmm13
+  DB  69,15,20,236                        ; unpcklps      %xmm12,%xmm13
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  69,15,88,193                        ; addps         %xmm9,%xmm8
+  DB  15,89,200                           ; mulps         %xmm0,%xmm1
+  DB  65,15,88,202                        ; addps         %xmm10,%xmm1
+  DB  15,89,208                           ; mulps         %xmm0,%xmm2
+  DB  65,15,88,211                        ; addps         %xmm11,%xmm2
+  DB  15,89,216                           ; mulps         %xmm0,%xmm3
+  DB  65,15,88,221                        ; addps         %xmm13,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,15,40,192                        ; movaps        %xmm8,%xmm0
+  DB  91                                  ; pop           %rbx
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_evenly_spaced_2_stop_gradient_sse2
+_sk_evenly_spaced_2_stop_gradient_sse2 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  15,198,192,0                        ; shufps        $0x0,%xmm0,%xmm0
+  DB  243,15,16,80,16                     ; movss         0x10(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  65,15,89,192                        ; mulps         %xmm8,%xmm0
+  DB  15,88,194                           ; addps         %xmm2,%xmm0
+  DB  15,198,201,0                        ; shufps        $0x0,%xmm1,%xmm1
+  DB  243,15,16,80,20                     ; movss         0x14(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  65,15,89,200                        ; mulps         %xmm8,%xmm1
+  DB  15,88,202                           ; addps         %xmm2,%xmm1
+  DB  243,15,16,80,8                      ; movss         0x8(%rax),%xmm2
+  DB  15,198,210,0                        ; shufps        $0x0,%xmm2,%xmm2
+  DB  243,15,16,88,24                     ; movss         0x18(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  65,15,89,208                        ; mulps         %xmm8,%xmm2
+  DB  15,88,211                           ; addps         %xmm3,%xmm2
+  DB  243,15,16,88,12                     ; movss         0xc(%rax),%xmm3
+  DB  15,198,219,0                        ; shufps        $0x0,%xmm3,%xmm3
+  DB  243,68,15,16,72,28                  ; movss         0x1c(%rax),%xmm9
+  DB  69,15,198,201,0                     ; shufps        $0x0,%xmm9,%xmm9
+  DB  65,15,89,216                        ; mulps         %xmm8,%xmm3
+  DB  65,15,88,217                        ; addps         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_unit_angle_sse2
+_sk_xy_to_unit_angle_sse2 LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  69,15,87,201                        ; xorps         %xmm9,%xmm9
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  69,15,84,200                        ; andps         %xmm8,%xmm9
+  DB  69,15,87,210                        ; xorps         %xmm10,%xmm10
+  DB  68,15,92,209                        ; subps         %xmm1,%xmm10
+  DB  68,15,84,209                        ; andps         %xmm1,%xmm10
+  DB  69,15,40,217                        ; movaps        %xmm9,%xmm11
+  DB  69,15,93,218                        ; minps         %xmm10,%xmm11
+  DB  69,15,40,225                        ; movaps        %xmm9,%xmm12
+  DB  69,15,95,226                        ; maxps         %xmm10,%xmm12
+  DB  69,15,94,220                        ; divps         %xmm12,%xmm11
+  DB  69,15,40,227                        ; movaps        %xmm11,%xmm12
+  DB  69,15,89,228                        ; mulps         %xmm12,%xmm12
+  DB  68,15,40,45,225,19,0,0              ; movaps        0x13e1(%rip),%xmm13        # 65a0 <_sk_callback_sse2+0xfb9>
+  DB  69,15,89,236                        ; mulps         %xmm12,%xmm13
+  DB  68,15,88,45,229,19,0,0              ; addps         0x13e5(%rip),%xmm13        # 65b0 <_sk_callback_sse2+0xfc9>
+  DB  69,15,89,236                        ; mulps         %xmm12,%xmm13
+  DB  68,15,88,45,233,19,0,0              ; addps         0x13e9(%rip),%xmm13        # 65c0 <_sk_callback_sse2+0xfd9>
+  DB  69,15,89,236                        ; mulps         %xmm12,%xmm13
+  DB  68,15,88,45,237,19,0,0              ; addps         0x13ed(%rip),%xmm13        # 65d0 <_sk_callback_sse2+0xfe9>
+  DB  69,15,89,235                        ; mulps         %xmm11,%xmm13
+  DB  69,15,194,202,1                     ; cmpltps       %xmm10,%xmm9
+  DB  68,15,40,21,236,19,0,0              ; movaps        0x13ec(%rip),%xmm10        # 65e0 <_sk_callback_sse2+0xff9>
+  DB  69,15,92,213                        ; subps         %xmm13,%xmm10
+  DB  69,15,84,209                        ; andps         %xmm9,%xmm10
+  DB  69,15,85,205                        ; andnps        %xmm13,%xmm9
+  DB  69,15,86,202                        ; orps          %xmm10,%xmm9
+  DB  68,15,194,192,1                     ; cmpltps       %xmm0,%xmm8
+  DB  68,15,40,21,223,19,0,0              ; movaps        0x13df(%rip),%xmm10        # 65f0 <_sk_callback_sse2+0x1009>
+  DB  69,15,92,209                        ; subps         %xmm9,%xmm10
+  DB  69,15,84,208                        ; andps         %xmm8,%xmm10
+  DB  69,15,85,193                        ; andnps        %xmm9,%xmm8
+  DB  69,15,86,194                        ; orps          %xmm10,%xmm8
+  DB  68,15,40,201                        ; movaps        %xmm1,%xmm9
+  DB  68,15,194,200,1                     ; cmpltps       %xmm0,%xmm9
+  DB  68,15,40,21,206,19,0,0              ; movaps        0x13ce(%rip),%xmm10        # 6600 <_sk_callback_sse2+0x1019>
+  DB  69,15,92,208                        ; subps         %xmm8,%xmm10
+  DB  69,15,84,209                        ; andps         %xmm9,%xmm10
+  DB  69,15,85,200                        ; andnps        %xmm8,%xmm9
+  DB  69,15,86,202                        ; orps          %xmm10,%xmm9
+  DB  65,15,194,193,7                     ; cmpordps      %xmm9,%xmm0
+  DB  65,15,84,193                        ; andps         %xmm9,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xy_to_radius_sse2
+_sk_xy_to_radius_sse2 LABEL PROC
+  DB  15,89,192                           ; mulps         %xmm0,%xmm0
+  DB  68,15,40,193                        ; movaps        %xmm1,%xmm8
+  DB  69,15,89,192                        ; mulps         %xmm8,%xmm8
+  DB  68,15,88,192                        ; addps         %xmm0,%xmm8
+  DB  65,15,81,192                        ; sqrtps        %xmm8,%xmm0
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_save_xy_sse2
+_sk_save_xy_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,160,19,0,0               ; movaps        0x13a0(%rip),%xmm8        # 6610 <_sk_callback_sse2+0x1029>
+  DB  15,17,0                             ; movups        %xmm0,(%rax)
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  69,15,88,200                        ; addps         %xmm8,%xmm9
+  DB  243,69,15,91,209                    ; cvttps2dq     %xmm9,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  69,15,40,217                        ; movaps        %xmm9,%xmm11
+  DB  69,15,194,218,1                     ; cmpltps       %xmm10,%xmm11
+  DB  68,15,40,37,139,19,0,0              ; movaps        0x138b(%rip),%xmm12        # 6620 <_sk_callback_sse2+0x1039>
+  DB  69,15,84,220                        ; andps         %xmm12,%xmm11
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  69,15,92,202                        ; subps         %xmm10,%xmm9
+  DB  68,15,88,193                        ; addps         %xmm1,%xmm8
+  DB  243,69,15,91,208                    ; cvttps2dq     %xmm8,%xmm10
+  DB  69,15,91,210                        ; cvtdq2ps      %xmm10,%xmm10
+  DB  69,15,40,216                        ; movaps        %xmm8,%xmm11
+  DB  69,15,194,218,1                     ; cmpltps       %xmm10,%xmm11
+  DB  69,15,84,220                        ; andps         %xmm12,%xmm11
+  DB  69,15,92,211                        ; subps         %xmm11,%xmm10
+  DB  69,15,92,194                        ; subps         %xmm10,%xmm8
+  DB  15,17,72,32                         ; movups        %xmm1,0x20(%rax)
+  DB  68,15,17,72,64                      ; movups        %xmm9,0x40(%rax)
+  DB  68,15,17,64,96                      ; movups        %xmm8,0x60(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_accumulate_sse2
+_sk_accumulate_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,16,128,128,0,0,0              ; movups        0x80(%rax),%xmm8
+  DB  68,15,16,136,160,0,0,0              ; movups        0xa0(%rax),%xmm9
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,192                        ; mulps         %xmm0,%xmm8
+  DB  65,15,88,224                        ; addps         %xmm8,%xmm4
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,193                        ; mulps         %xmm1,%xmm8
+  DB  65,15,88,232                        ; addps         %xmm8,%xmm5
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  68,15,89,194                        ; mulps         %xmm2,%xmm8
+  DB  65,15,88,240                        ; addps         %xmm8,%xmm6
+  DB  68,15,89,203                        ; mulps         %xmm3,%xmm9
+  DB  65,15,88,249                        ; addps         %xmm9,%xmm7
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_nx_sse2
+_sk_bilinear_nx_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,4,19,0,0                    ; addps         0x1304(%rip),%xmm0        # 6630 <_sk_callback_sse2+0x1049>
+  DB  68,15,40,13,12,19,0,0               ; movaps        0x130c(%rip),%xmm9        # 6640 <_sk_callback_sse2+0x1059>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,17,136,128,0,0,0              ; movups        %xmm9,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_px_sse2
+_sk_bilinear_px_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,251,18,0,0                  ; addps         0x12fb(%rip),%xmm0        # 6650 <_sk_callback_sse2+0x1069>
+  DB  68,15,17,128,128,0,0,0              ; movups        %xmm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_ny_sse2
+_sk_bilinear_ny_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,237,18,0,0                 ; addps         0x12ed(%rip),%xmm1        # 6660 <_sk_callback_sse2+0x1079>
+  DB  68,15,40,13,245,18,0,0              ; movaps        0x12f5(%rip),%xmm9        # 6670 <_sk_callback_sse2+0x1089>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,17,136,160,0,0,0              ; movups        %xmm9,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bilinear_py_sse2
+_sk_bilinear_py_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,227,18,0,0                 ; addps         0x12e3(%rip),%xmm1        # 6680 <_sk_callback_sse2+0x1099>
+  DB  68,15,17,128,160,0,0,0              ; movups        %xmm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3x_sse2
+_sk_bicubic_n3x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,214,18,0,0                  ; addps         0x12d6(%rip),%xmm0        # 6690 <_sk_callback_sse2+0x10a9>
+  DB  68,15,40,13,222,18,0,0              ; movaps        0x12de(%rip),%xmm9        # 66a0 <_sk_callback_sse2+0x10b9>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  69,15,89,192                        ; mulps         %xmm8,%xmm8
+  DB  68,15,89,13,218,18,0,0              ; mulps         0x12da(%rip),%xmm9        # 66b0 <_sk_callback_sse2+0x10c9>
+  DB  68,15,88,13,226,18,0,0              ; addps         0x12e2(%rip),%xmm9        # 66c0 <_sk_callback_sse2+0x10d9>
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  68,15,17,136,128,0,0,0              ; movups        %xmm9,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1x_sse2
+_sk_bicubic_n1x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,209,18,0,0                  ; addps         0x12d1(%rip),%xmm0        # 66d0 <_sk_callback_sse2+0x10e9>
+  DB  68,15,40,13,217,18,0,0              ; movaps        0x12d9(%rip),%xmm9        # 66e0 <_sk_callback_sse2+0x10f9>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,40,5,221,18,0,0               ; movaps        0x12dd(%rip),%xmm8        # 66f0 <_sk_callback_sse2+0x1109>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,225,18,0,0               ; addps         0x12e1(%rip),%xmm8        # 6700 <_sk_callback_sse2+0x1119>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,229,18,0,0               ; addps         0x12e5(%rip),%xmm8        # 6710 <_sk_callback_sse2+0x1129>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,233,18,0,0               ; addps         0x12e9(%rip),%xmm8        # 6720 <_sk_callback_sse2+0x1139>
+  DB  68,15,17,128,128,0,0,0              ; movups        %xmm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1x_sse2
+_sk_bicubic_p1x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,227,18,0,0               ; movaps        0x12e3(%rip),%xmm8        # 6730 <_sk_callback_sse2+0x1149>
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,72,64                      ; movups        0x40(%rax),%xmm9
+  DB  65,15,88,192                        ; addps         %xmm8,%xmm0
+  DB  68,15,40,21,223,18,0,0              ; movaps        0x12df(%rip),%xmm10        # 6740 <_sk_callback_sse2+0x1159>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,227,18,0,0              ; addps         0x12e3(%rip),%xmm10        # 6750 <_sk_callback_sse2+0x1169>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,208                        ; addps         %xmm8,%xmm10
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,223,18,0,0              ; addps         0x12df(%rip),%xmm10        # 6760 <_sk_callback_sse2+0x1179>
+  DB  68,15,17,144,128,0,0,0              ; movups        %xmm10,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3x_sse2
+_sk_bicubic_p3x_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,0                             ; movups        (%rax),%xmm0
+  DB  68,15,16,64,64                      ; movups        0x40(%rax),%xmm8
+  DB  15,88,5,210,18,0,0                  ; addps         0x12d2(%rip),%xmm0        # 6770 <_sk_callback_sse2+0x1189>
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,89,201                        ; mulps         %xmm9,%xmm9
+  DB  68,15,89,5,210,18,0,0               ; mulps         0x12d2(%rip),%xmm8        # 6780 <_sk_callback_sse2+0x1199>
+  DB  68,15,88,5,218,18,0,0               ; addps         0x12da(%rip),%xmm8        # 6790 <_sk_callback_sse2+0x11a9>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,17,128,128,0,0,0              ; movups        %xmm8,0x80(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n3y_sse2
+_sk_bicubic_n3y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,200,18,0,0                 ; addps         0x12c8(%rip),%xmm1        # 67a0 <_sk_callback_sse2+0x11b9>
+  DB  68,15,40,13,208,18,0,0              ; movaps        0x12d0(%rip),%xmm9        # 67b0 <_sk_callback_sse2+0x11c9>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  69,15,40,193                        ; movaps        %xmm9,%xmm8
+  DB  69,15,89,192                        ; mulps         %xmm8,%xmm8
+  DB  68,15,89,13,204,18,0,0              ; mulps         0x12cc(%rip),%xmm9        # 67c0 <_sk_callback_sse2+0x11d9>
+  DB  68,15,88,13,212,18,0,0              ; addps         0x12d4(%rip),%xmm9        # 67d0 <_sk_callback_sse2+0x11e9>
+  DB  69,15,89,200                        ; mulps         %xmm8,%xmm9
+  DB  68,15,17,136,160,0,0,0              ; movups        %xmm9,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_n1y_sse2
+_sk_bicubic_n1y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,194,18,0,0                 ; addps         0x12c2(%rip),%xmm1        # 67e0 <_sk_callback_sse2+0x11f9>
+  DB  68,15,40,13,202,18,0,0              ; movaps        0x12ca(%rip),%xmm9        # 67f0 <_sk_callback_sse2+0x1209>
+  DB  69,15,92,200                        ; subps         %xmm8,%xmm9
+  DB  68,15,40,5,206,18,0,0               ; movaps        0x12ce(%rip),%xmm8        # 6800 <_sk_callback_sse2+0x1219>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,210,18,0,0               ; addps         0x12d2(%rip),%xmm8        # 6810 <_sk_callback_sse2+0x1229>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,214,18,0,0               ; addps         0x12d6(%rip),%xmm8        # 6820 <_sk_callback_sse2+0x1239>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,88,5,218,18,0,0               ; addps         0x12da(%rip),%xmm8        # 6830 <_sk_callback_sse2+0x1249>
+  DB  68,15,17,128,160,0,0,0              ; movups        %xmm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p1y_sse2
+_sk_bicubic_p1y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  68,15,40,5,212,18,0,0               ; movaps        0x12d4(%rip),%xmm8        # 6840 <_sk_callback_sse2+0x1259>
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,72,96                      ; movups        0x60(%rax),%xmm9
+  DB  65,15,88,200                        ; addps         %xmm8,%xmm1
+  DB  68,15,40,21,207,18,0,0              ; movaps        0x12cf(%rip),%xmm10        # 6850 <_sk_callback_sse2+0x1269>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,211,18,0,0              ; addps         0x12d3(%rip),%xmm10        # 6860 <_sk_callback_sse2+0x1279>
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  69,15,88,208                        ; addps         %xmm8,%xmm10
+  DB  69,15,89,209                        ; mulps         %xmm9,%xmm10
+  DB  68,15,88,21,207,18,0,0              ; addps         0x12cf(%rip),%xmm10        # 6870 <_sk_callback_sse2+0x1289>
+  DB  68,15,17,144,160,0,0,0              ; movups        %xmm10,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_bicubic_p3y_sse2
+_sk_bicubic_p3y_sse2 LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,72,32                         ; movups        0x20(%rax),%xmm1
+  DB  68,15,16,64,96                      ; movups        0x60(%rax),%xmm8
+  DB  15,88,13,193,18,0,0                 ; addps         0x12c1(%rip),%xmm1        # 6880 <_sk_callback_sse2+0x1299>
+  DB  69,15,40,200                        ; movaps        %xmm8,%xmm9
+  DB  69,15,89,201                        ; mulps         %xmm9,%xmm9
+  DB  68,15,89,5,193,18,0,0               ; mulps         0x12c1(%rip),%xmm8        # 6890 <_sk_callback_sse2+0x12a9>
+  DB  68,15,88,5,201,18,0,0               ; addps         0x12c9(%rip),%xmm8        # 68a0 <_sk_callback_sse2+0x12b9>
+  DB  69,15,89,193                        ; mulps         %xmm9,%xmm8
+  DB  68,15,17,128,160,0,0,0              ; movups        %xmm8,0xa0(%rax)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_callback_sse2
+_sk_callback_sse2 LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,84                               ; push          %r12
+  DB  83                                  ; push          %rbx
+  DB  72,131,236,32                       ; sub           $0x20,%rsp
+  DB  68,15,40,197                        ; movaps        %xmm5,%xmm8
+  DB  68,15,40,204                        ; movaps        %xmm4,%xmm9
+  DB  77,137,196                          ; mov           %r8,%r12
+  DB  73,137,206                          ; mov           %rcx,%r14
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  72,137,195                          ; mov           %rax,%rbx
+  DB  15,40,224                           ; movaps        %xmm0,%xmm4
+  DB  15,20,225                           ; unpcklps      %xmm1,%xmm4
+  DB  15,40,234                           ; movaps        %xmm2,%xmm5
+  DB  15,20,235                           ; unpcklps      %xmm3,%xmm5
+  DB  15,21,193                           ; unpckhps      %xmm1,%xmm0
+  DB  15,21,211                           ; unpckhps      %xmm3,%xmm2
+  DB  15,40,204                           ; movaps        %xmm4,%xmm1
+  DB  102,15,20,205                       ; unpcklpd      %xmm5,%xmm1
+  DB  15,18,236                           ; movhlps       %xmm4,%xmm5
+  DB  15,40,216                           ; movaps        %xmm0,%xmm3
+  DB  102,15,20,218                       ; unpcklpd      %xmm2,%xmm3
+  DB  102,15,17,75,8                      ; movupd        %xmm1,0x8(%rbx)
+  DB  15,18,208                           ; movhlps       %xmm0,%xmm2
+  DB  15,17,107,24                        ; movups        %xmm5,0x18(%rbx)
+  DB  102,15,17,91,40                     ; movupd        %xmm3,0x28(%rbx)
+  DB  15,17,83,56                         ; movups        %xmm2,0x38(%rbx)
+  DB  77,133,228                          ; test          %r12,%r12
+  DB  186,4,0,0,0                         ; mov           $0x4,%edx
+  DB  65,15,69,212                        ; cmovne        %r12d,%edx
+  DB  72,137,217                          ; mov           %rbx,%rcx
+  DB  255,19                              ; callq         *(%rbx)
+  DB  72,139,131,136,0,0,0                ; mov           0x88(%rbx),%rax
+  DB  15,16,32                            ; movups        (%rax),%xmm4
+  DB  15,16,64,16                         ; movups        0x10(%rax),%xmm0
+  DB  15,16,88,32                         ; movups        0x20(%rax),%xmm3
+  DB  15,16,80,48                         ; movups        0x30(%rax),%xmm2
+  DB  15,40,236                           ; movaps        %xmm4,%xmm5
+  DB  15,20,232                           ; unpcklps      %xmm0,%xmm5
+  DB  15,40,203                           ; movaps        %xmm3,%xmm1
+  DB  15,20,202                           ; unpcklps      %xmm2,%xmm1
+  DB  15,21,224                           ; unpckhps      %xmm0,%xmm4
+  DB  15,21,218                           ; unpckhps      %xmm2,%xmm3
+  DB  15,40,197                           ; movaps        %xmm5,%xmm0
+  DB  102,15,20,193                       ; unpcklpd      %xmm1,%xmm0
+  DB  15,18,205                           ; movhlps       %xmm5,%xmm1
+  DB  15,40,212                           ; movaps        %xmm4,%xmm2
+  DB  102,15,20,211                       ; unpcklpd      %xmm3,%xmm2
+  DB  15,18,220                           ; movhlps       %xmm4,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  76,137,241                          ; mov           %r14,%rcx
+  DB  77,137,224                          ; mov           %r12,%r8
+  DB  65,15,40,225                        ; movaps        %xmm9,%xmm4
+  DB  65,15,40,232                        ; movaps        %xmm8,%xmm5
+  DB  72,131,196,32                       ; add           $0x20,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  65,92                               ; pop           %r12
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  255,224                             ; jmpq          *%rax
+
+ALIGN 16
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,1                            ; cmpb          $0x1,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  4,0                                 ; add           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  2,0                                 ; add           (%rax),%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  128,60,0,0                          ; cmpb          $0x0,(%rax,%rax,1)
+  DB  252                                 ; cld
+  DB  190,0,0,252,190                     ; mov           $0xbefc0000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  252                                 ; cld
+  DB  190,0,0,252,190                     ; mov           $0xbefc0000,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  191,0,0,128,191                     ; mov           $0xbf800000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,191,0,0,128,191,0               ; cmpb          $0x0,-0x40800000(%rdi)
+  DB  0,224                               ; add           %ah,%al
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  224,64                              ; loopne        5898 <.literal16+0x1d8>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,64                              ; loopne        589c <.literal16+0x1dc>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,64                              ; loopne        58a0 <.literal16+0x1e0>
+  DB  154                                 ; (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 58c1 <.literal16+0x201>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 58c5 <.literal16+0x205>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 58c9 <.literal16+0x209>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 58cd <.literal16+0x20d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5901 <.literal16+0x241>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5905 <.literal16+0x245>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5909 <.literal16+0x249>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 590d <.literal16+0x24d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5941 <.literal16+0x281>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5945 <.literal16+0x285>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5949 <.literal16+0x289>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 594d <.literal16+0x28d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,154                          ; cmpb          $0x9a,(%rdi)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,61,10,23,63,61                   ; ds            cmp $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  61,10,23,63,61                      ; cmp           $0x3d3f170a,%eax
+  DB  10,23                               ; or            (%rdi),%dl
+  DB  63                                  ; (bad)
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5981 <.literal16+0x2c1>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5985 <.literal16+0x2c5>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 5989 <.literal16+0x2c9>
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,225,61                           ; rex.RXB       loope 598d <.literal16+0x2cd>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,255                          ; cmpb          $0xff,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,255                               ; add           %bh,%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,255                               ; add           %bh,%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,255                               ; add           %bh,%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,127                    ; add           %al,0x7f00003f(%rax)
+  DB  67,0,0                              ; rex.XB        add %al,(%r8)
+  DB  127,67                              ; jg            59cb <.literal16+0x30b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            59cf <.literal16+0x30f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            59d3 <.literal16+0x313>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  145                                 ; xchg          %eax,%ecx
+  DB  131,158,61,145,131,158,61           ; sbbl          $0x3d,-0x617c6ec3(%rsi)
+  DB  145                                 ; xchg          %eax,%ecx
+  DB  131,158,61,145,131,158,61           ; sbbl          $0x3d,-0x617c6ec3(%rsi)
+  DB  154                                 ; (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,154                              ; ds            (bad)
+  DB  153                                 ; cltd
+  DB  153                                 ; cltd
+  DB  62,92                               ; ds            pop %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  92                                  ; pop           %rsp
+  DB  143                                 ; (bad)
+  DB  50,63                               ; xor           (%rdi),%bh
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  35,59                               ; and           (%rbx),%edi
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,97                               ; rex.RXB       (bad)
+  DB  61,174,71,97,61                     ; cmp           $0x3d6147ae,%eax
+  DB  174                                 ; scas          %es:(%rdi),%al
+  DB  71,97                               ; rex.RXB       (bad)
+  DB  61,174,71,97,61                     ; cmp           $0x3d6147ae,%eax
+  DB  82                                  ; push          %rdx
+  DB  184,78,65,82,184                    ; mov           $0xb852414e,%eax
+  DB  78                                  ; rex.WRX
+  DB  65,82                               ; push          %r10
+  DB  184,78,65,82,184                    ; mov           $0xb852414e,%eax
+  DB  78                                  ; rex.WRX
+  DB  65,57,215                           ; cmp           %edx,%r15d
+  DB  32,187,57,215,32,187                ; and           %bh,-0x44df28c7(%rbx)
+  DB  57,215                              ; cmp           %edx,%edi
+  DB  32,187,57,215,32,187                ; and           %bh,-0x44df28c7(%rbx)
+  DB  186,159,98,60,186                   ; mov           $0xba3c629f,%edx
+  DB  159                                 ; lahf
+  DB  98                                  ; (bad)
+  DB  60,186                              ; cmp           $0xba,%al
+  DB  159                                 ; lahf
+  DB  98                                  ; (bad)
+  DB  60,186                              ; cmp           $0xba,%al
+  DB  159                                 ; lahf
+  DB  98                                  ; (bad)
+  DB  60,109                              ; cmp           $0x6d,%al
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  109                                 ; insl          (%dx),%es:(%rdi)
+  DB  165                                 ; movsl         %ds:(%rsi),%es:(%rdi)
+  DB  144                                 ; nop
+  DB  63                                  ; (bad)
+  DB  252                                 ; cld
+  DB  191,16,62,252,191                   ; mov           $0xbffc3e10,%edi
+  DB  16,62                               ; adc           %bh,(%rsi)
+  DB  252                                 ; cld
+  DB  191,16,62,252,191                   ; mov           $0xbffc3e10,%edi
+  DB  16,62                               ; adc           %bh,(%rsi)
+  DB  168,177                             ; test          $0xb1,%al
+  DB  152                                 ; cwtl
+  DB  59,168,177,152,59,168               ; cmp           -0x57c4674f(%rax),%ebp
+  DB  177,152                             ; mov           $0x98,%cl
+  DB  59,168,177,152,59,0                 ; cmp           0x3b98b1(%rax),%ebp
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,192                    ; add           %al,-0x3fffffc1(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  0,64,0                              ; add           %al,0x0(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  0,64,0                              ; add           %al,0x0(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  64,0,0                              ; add           %al,(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,171,170                      ; addb          $0xaa,-0x55(%rax)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  62,0,0                              ; add           %al,%ds:(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,171                          ; cmpb          $0xab,(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,63                               ; sub           (%rdi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  192,64,0,0                          ; rolb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,0,0                          ; addb          $0x0,0x0(%rax)
+  DB  128,64,171,170                      ; addb          $0xaa,-0x55(%rax)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  42,62                               ; sub           (%rsi),%bh
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,255,0,0,0                ; addb          $0x0,0xff3b(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,248,0,0                ; addb          $0x0,0xf8003b(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        5bb9 <.literal16+0x4f9>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5bbd <.literal16+0x4fd>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5bc1 <.literal16+0x501>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        5bc5 <.literal16+0x505>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,8,33,4,61                   ; and           %eax,0x3d042108(,%rdi,1)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,255,0,255,0              ; addb          $0x0,-0xff00c5(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5c9b <.literal16+0x5db>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5c9f <.literal16+0x5df>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            5ca3 <.literal16+0x5e3>
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,129,128,128,59           ; addb          $0x3b,-0x7f7f7ec5(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,0                            ; cmpb          $0x0,(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5c94 <.literal16+0x5d4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5c98 <.literal16+0x5d8>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5c9c <.literal16+0x5dc>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5ca0 <.literal16+0x5e0>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            5d25 <.literal16+0x665>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5c89 <.literal16+0x5c9>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a39cca <_sk_callback_sse2+0xffffffffe9a346e3>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,81                           ; cmpb          $0x51,(%rdi)
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,52,0                              ; add           %dh,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5d64 <.literal16+0x6a4>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5d68 <.literal16+0x6a8>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5d6c <.literal16+0x6ac>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5d70 <.literal16+0x6b0>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            5df5 <.literal16+0x735>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5d59 <.literal16+0x699>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a39d9a <_sk_callback_sse2+0xffffffffe9a347b3>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,81                           ; cmpb          $0x51,(%rdi)
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,52,0                              ; add           %dh,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5e34 <.literal16+0x774>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5e38 <.literal16+0x778>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5e3c <.literal16+0x77c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5e40 <.literal16+0x780>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            5ec5 <.literal16+0x805>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5e29 <.literal16+0x769>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a39e6a <_sk_callback_sse2+0xffffffffe9a34883>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,81                           ; cmpb          $0x51,(%rdi)
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,52,0                              ; add           %dh,(%rax,%rax,1)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,0                                ; xor           $0x0,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  52,255                              ; xor           $0xff,%al
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5f04 <.literal16+0x844>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5f08 <.literal16+0x848>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5f0c <.literal16+0x84c>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  127,0                               ; jg            5f10 <.literal16+0x850>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  119,115                             ; ja            5f95 <.literal16+0x8d5>
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,119,115                         ; retq          $0x7377
+  DB  248                                 ; clc
+  DB  194,117,191                         ; retq          $0xbf75
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  117,191                             ; jne           5ef9 <.literal16+0x839>
+  DB  191,63,117,191,191                  ; mov           $0xbfbf753f,%edi
+  DB  63                                  ; (bad)
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  249                                 ; stc
+  DB  68,180,62                           ; rex.R         mov $0x3e,%spl
+  DB  163,233,220,63,163,233,220,63,163   ; movabs        %eax,0xa33fdce9a33fdce9
+  DB  233,220,63,163,233                  ; jmpq          ffffffffe9a39f3a <_sk_callback_sse2+0xffffffffe9a34953>
+  DB  220,63                              ; fdivrl        (%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,81                           ; cmpb          $0x51,(%rdi)
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,81                               ; rex.X         push %rcx
+  DB  140,242                             ; mov           %?,%edx
+  DB  66,141,188,190,63,141,188,190       ; lea           -0x414372c1(%rsi,%r15,4),%edi
+  DB  63                                  ; (bad)
+  DB  141,188,190,63,141,188,190          ; lea           -0x414372c1(%rsi,%rdi,4),%edi
+  DB  63                                  ; (bad)
+  DB  248                                 ; clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,248                              ; rex           clc
+  DB  245                                 ; cmc
+  DB  154                                 ; (bad)
+  DB  64,254                              ; rex           (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,254                              ; rex.B         (bad)
+  DB  210,221                             ; rcr           %cl,%ch
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  0,75,0                              ; add           %cl,0x0(%rbx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  75,0,0                              ; rex.WXB       add %al,(%r8)
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  200,66,0,0                          ; enterq        $0x42,$0x0
+  DB  127,67                              ; jg            6017 <.literal16+0x957>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            601b <.literal16+0x95b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            601f <.literal16+0x95f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            6023 <.literal16+0x963>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,195                               ; add           %al,%bl
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,65,0,0                          ; addb          $0x0,0x0(%rcx)
+  DB  128,65,0,0                          ; addb          $0x0,0x0(%rcx)
+  DB  128,65,0,0                          ; addb          $0x0,0x0(%rcx)
+  DB  128,65,203,61                       ; addb          $0x3d,-0x35(%rcx)
+  DB  13,60,203,61,13                     ; or            $0xd3dcb3c,%eax
+  DB  60,203                              ; cmp           $0xcb,%al
+  DB  61,13,60,203,61                     ; cmp           $0x3dcb3c0d,%eax
+  DB  13,60,111,18,3                      ; or            $0x3126f3c,%eax
+  DB  59,111,18                           ; cmp           0x12(%rdi),%ebp
+  DB  3,59                                ; add           (%rbx),%edi
+  DB  111                                 ; outsl         %ds:(%rsi),(%dx)
+  DB  18,3                                ; adc           (%rbx),%al
+  DB  59,111,18                           ; cmp           0x12(%rdi),%ebp
+  DB  3,59                                ; add           (%rbx),%edi
+  DB  10,215                              ; or            %bh,%dl
+  DB  163,59,10,215,163,59,10,215,163     ; movabs        %eax,0xa3d70a3ba3d70a3b
+  DB  59,10                               ; cmp           (%rdx),%ecx
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  163,59,194,24,17,60,194,24,17       ; movabs        %eax,0x1118c23c1118c23b
+  DB  60,194                              ; cmp           $0xc2,%al
+  DB  24,17                               ; sbb           %dl,(%rcx)
+  DB  60,194                              ; cmp           $0xc2,%al
+  DB  24,17                               ; sbb           %dl,(%rcx)
+  DB  60,203                              ; cmp           $0xcb,%al
+  DB  61,13,190,203,61                    ; cmp           $0x3dcbbe0d,%eax
+  DB  13,190,203,61,13                    ; or            $0xd3dcbbe,%eax
+  DB  190,203,61,13,190                   ; mov           $0xbe0d3dcb,%esi
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  80                                  ; push          %rax
+  DB  128,3,62                            ; addb          $0x3e,(%rbx)
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           60a3 <.literal16+0x9e3>
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           60a7 <.literal16+0x9e7>
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           60ab <.literal16+0x9eb>
+  DB  31                                  ; (bad)
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  118,63                              ; jbe           60af <.literal16+0x9ef>
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  246,64,83,63                        ; testb         $0x3f,0x53(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,129,128,128,59           ; addb          $0x3b,-0x7f7f7ec5(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,0                            ; cmpb          $0x0,(%rbx)
+  DB  0,127,67                            ; add           %bh,0x43(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            60fb <.literal16+0xa3b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            60ff <.literal16+0xa3f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            6103 <.literal16+0xa43>
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,0,128,63               ; addb          $0x3f,-0x7fffffc5(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,0,128,63               ; addb          $0x3f,-0x7fffffc5(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,248,0,0                ; addb          $0x0,0xf8003b(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        6159 <.literal16+0xa99>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        615d <.literal16+0xa9d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        6161 <.literal16+0xaa1>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        6165 <.literal16+0xaa5>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,8,33,4,61                   ; and           %eax,0x3d042108(,%rdi,1)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  248                                 ; clc
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,248                               ; add           %bh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  132,55                              ; test          %dh,(%rdi)
+  DB  224,7                               ; loopne        61c9 <.literal16+0xb09>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        61cd <.literal16+0xb0d>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        61d1 <.literal16+0xb11>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  224,7                               ; loopne        61d5 <.literal16+0xb15>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  33,8                                ; and           %ecx,(%rax)
+  DB  2,58                                ; add           (%rdx),%bh
+  DB  31                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,31                                ; add           %bl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,8                                 ; add           %cl,(%rax)
+  DB  33,4,61,8,33,4,61                   ; and           %eax,0x3d042108(,%rdi,1)
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  8,33                                ; or            %ah,(%rcx)
+  DB  4,61                                ; add           $0x3d,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,248                               ; add           %bh,%al
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  248                                 ; clc
+  DB  65,0,0                              ; add           %al,(%r8)
+  DB  124,66                              ; jl            6266 <.literal16+0xba6>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  124,66                              ; jl            626a <.literal16+0xbaa>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  124,66                              ; jl            626e <.literal16+0xbae>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  124,66                              ; jl            6272 <.literal16+0xbb2>
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  240,0,0                             ; lock          add %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,59,137                ; add           %cl,-0x76c47778(%rcx)
+  DB  136,136,59,137,136,136              ; mov           %cl,-0x777776c5(%rax)
+  DB  59,137,136,136,59,15                ; cmp           0xf3b8888(%rcx),%ecx
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,61,137                ; add           %cl,-0x76c27778(%rcx)
+  DB  136,136,61,137,136,136              ; mov           %cl,-0x777776c3(%rax)
+  DB  61,137,136,136,61                   ; cmp           $0x3d888889,%eax
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  137,136,136,55,137,136              ; mov           %ecx,-0x7776c878(%rax)
+  DB  136,55                              ; mov           %dh,(%rdi)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  137,136,136,57,137,136              ; mov           %ecx,-0x7776c678(%rax)
+  DB  136,57                              ; mov           %bh,(%rcx)
+  DB  240,0,0                             ; lock          add %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,240                               ; add           %dh,%al
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,59,137                ; add           %cl,-0x76c47778(%rcx)
+  DB  136,136,59,137,136,136              ; mov           %cl,-0x777776c5(%rax)
+  DB  59,137,136,136,59,15                ; cmp           0xf3b8888(%rcx),%ecx
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,15                                ; add           %cl,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,137,136,136,61,137                ; add           %cl,-0x76c27778(%rcx)
+  DB  136,136,61,137,136,136              ; mov           %cl,-0x777776c3(%rax)
+  DB  61,137,136,136,61                   ; cmp           $0x3d888889,%eax
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            6375 <.literal16+0xcb5>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            6379 <.literal16+0xcb9>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            637d <.literal16+0xcbd>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  112,65                              ; jo            6381 <.literal16+0xcc1>
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,255,0,0,0                ; addb          $0x0,0xff3b(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  129,128,128,59,129,128,128,59,129,128; addl          $0x80813b80,-0x7f7ec480(%rax)
+  DB  128,59,129                          ; cmpb          $0x81,(%rbx)
+  DB  128,128,59,0,0,127,67               ; addb          $0x43,0x7f00003b(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            63cb <.literal16+0xd0b>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            63cf <.literal16+0xd0f>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  127,67                              ; jg            63d3 <.literal16+0xd13>
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  128,0,4                             ; addb          $0x4,(%rax)
+  DB  0,128,0,4,0,128                     ; add           %al,-0x7ffffc00(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,0                       ; add           %al,0x0(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,56                                ; add           %bh,(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  128,0,4                             ; addb          $0x4,(%rax)
+  DB  0,128,0,4,0,128                     ; add           %al,-0x7ffffc00(%rax)
+  DB  0,4,0                               ; add           %al,(%rax,%rax,1)
+  DB  128,0,0                             ; addb          $0x0,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,128,0,0,0,128                     ; add           %al,-0x80000000(%rax)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,56,0                            ; cmpb          $0x0,(%rax)
+  DB  0,128,56,0,0,128                    ; add           %al,-0x7fffffc8(%rax)
+  DB  56,0                                ; cmp           %al,(%rax)
+  DB  0,128,56,0,64,254                   ; add           %al,-0x1bfffc8(%rax)
+  DB  255,0                               ; incl          (%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,0                               ; incl          (%rax)
+  DB  64,254                              ; rex           (bad)
+  DB  255,128,0,128,55,128                ; incl          -0x7fc88000(%rax)
+  DB  0,128,55,128,0,128                  ; add           %al,-0x7fff7fc9(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  128,0,128                           ; addb          $0x80,(%rax)
+  DB  55                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  255                                 ; (bad)
+  DB  127,71                              ; jg            64bb <.literal16+0xdfb>
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            64bf <.literal16+0xdff>
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            64c3 <.literal16+0xe03>
+  DB  0,255                               ; add           %bh,%bh
+  DB  127,71                              ; jg            64c7 <.literal16+0xe07>
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,191,0,0,128,191,0               ; cmpb          $0x0,-0x40800000(%rdi)
+  DB  0,128,191,0,0,128                   ; add           %al,-0x7fffff41(%rax)
+  DB  191,0,0,0,63                        ; mov           $0x3f000000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,208                          ; cmpb          $0xd0,(%rdi)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,208                              ; ds            (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,208                              ; ds            (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,208                              ; ds            (bad)
+  DB  179,89                              ; mov           $0x59,%bl
+  DB  62,89                               ; ds            pop %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  89                                  ; pop           %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  89                                  ; pop           %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  89                                  ; pop           %rcx
+  DB  23                                  ; (bad)
+  DB  55                                  ; (bad)
+  DB  63                                  ; (bad)
+  DB  152                                 ; cwtl
+  DB  221,147,61,152,221,147              ; fstl          -0x6c2267c3(%rbx)
+  DB  61,152,221,147,61                   ; cmp           $0x3d93dd98,%eax
+  DB  152                                 ; cwtl
+  DB  221,147,61,45,16,17                 ; fstl          0x11102d3d(%rbx)
+  DB  192,45,16,17,192,45,16              ; shrb          $0x10,0x2dc01110(%rip)        # 2dc0765a <_sk_callback_sse2+0x2dc02073>
+  DB  17,192                              ; adc           %eax,%eax
+  DB  45,16,17,192,18                     ; sub           $0x12c01110,%eax
+  DB  120,57                              ; js            658c <.literal16+0xecc>
+  DB  64,18,120,57                        ; adc           0x39(%rax),%dil
+  DB  64,18,120,57                        ; adc           0x39(%rax),%dil
+  DB  64,18,120,57                        ; adc           0x39(%rax),%dil
+  DB  64,32,148,90,62,32,148,90           ; and           %dl,0x5a94203e(%rdx,%rbx,2)
+  DB  62,32,148,90,62,32,148,90           ; and           %dl,%ds:0x5a94203e(%rdx,%rbx,2)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,4,157                            ; ds            add $0x9d,%al
+  DB  30                                  ; (bad)
+  DB  62,0,24                             ; add           %bl,%ds:(%rax)
+  DB  161,57,0,24,161,57,0,24,161         ; movabs        0xa1180039a1180039,%eax
+  DB  57,0                                ; cmp           %eax,(%rax)
+  DB  24,161,57,1,0,0                     ; sbb           %ah,0x139(%rcx)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,1                                 ; add           %al,(%rcx)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,111,43                            ; add           %ch,0x2b(%rdi)
+  DB  231,187                             ; out           %eax,$0xbb
+  DB  111                                 ; outsl         %ds:(%rsi),(%dx)
+  DB  43,231                              ; sub           %edi,%esp
+  DB  187,111,43,231,187                  ; mov           $0xbbe72b6f,%ebx
+  DB  111                                 ; outsl         %ds:(%rsi),(%dx)
+  DB  43,231                              ; sub           %edi,%esp
+  DB  187,159,215,202,60                  ; mov           $0x3ccad79f,%ebx
+  DB  159                                 ; lahf
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  202,60,159                          ; lret          $0x9f3c
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  202,60,159                          ; lret          $0x9f3c
+  DB  215                                 ; xlat          %ds:(%rbx)
+  DB  202,60,212                          ; lret          $0xd43c
+  DB  100,84                              ; fs            push %rsp
+  DB  189,212,100,84,189                  ; mov           $0xbd5464d4,%ebp
+  DB  212                                 ; (bad)
+  DB  100,84                              ; fs            push %rsp
+  DB  189,212,100,84,189                  ; mov           $0xbd5464d4,%ebp
+  DB  169,240,34,62,169                   ; test          $0xa93e22f0,%eax
+  DB  240,34,62                           ; lock          and (%rsi),%bh
+  DB  169,240,34,62,169                   ; test          $0xa93e22f0,%eax
+  DB  240,34,62                           ; lock          and (%rsi),%bh
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,62,0                            ; cmpb          $0x0,(%rsi)
+  DB  0,128,62,0,0,128                    ; add           %al,-0x7fffffc2(%rax)
+  DB  62,0,0                              ; add           %al,%ds:(%rax)
+  DB  128,62,0                            ; cmpb          $0x0,(%rsi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  191,0,0,0,191                       ; mov           $0xbf000000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,192,191,0               ; sarb          $0x0,-0x40400000(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  191,0,0,192,191                     ; mov           $0xbfc00000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,114                          ; cmpb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,114,28                           ; jb,pt         66d2 <.literal16+0x1012>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         66d6 <.literal16+0x1016>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         66da <.literal16+0x101a>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,85                           ; cmpb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f565 <_sk_callback_sse2+0x3d639f7e>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f5a5 <_sk_callback_sse2+0x3d639fbe>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  114,28                              ; jb            679e <.literal16+0x10de>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         67a2 <.literal16+0x10e2>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         67a6 <.literal16+0x10e6>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         67aa <.literal16+0x10ea>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,191,0,0,192,191,0               ; sarb          $0x0,-0x40400000(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  191,0,0,192,191                     ; mov           $0xbfc00000,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,114                          ; cmpb          $0x72,(%rdi)
+  DB  28,199                              ; sbb           $0xc7,%al
+  DB  62,114,28                           ; jb,pt         67e2 <.literal16+0x1122>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         67e6 <.literal16+0x1126>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         67ea <.literal16+0x112a>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,191,0,0,0,191                     ; add           %bh,-0x41000000(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,0                            ; cmpb          $0x0,(%rdi)
+  DB  0,128,63,0,0,128                    ; add           %al,-0x7fffffc1(%rax)
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,63,85                           ; cmpb          $0x55,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f675 <_sk_callback_sse2+0x3d63a08e>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  0,63                                ; add           %bh,(%rdi)
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  85                                  ; push          %rbp
+  DB  85                                  ; push          %rbp
+  DB  149                                 ; xchg          %eax,%ebp
+  DB  191,85,85,149,191                   ; mov           $0xbf955555,%edi
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  57,142,99,61,57,142                 ; cmp           %ecx,-0x71c6c29d(%rsi)
+  DB  99,61,57,142,99,61                  ; movslq        0x3d638e39(%rip),%edi        # 3d63f6b5 <_sk_callback_sse2+0x3d63a0ce>
+  DB  57,142,99,61,0,0                    ; cmp           %ecx,0x3d63(%rsi)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  192,63,0                            ; sarb          $0x0,(%rdi)
+  DB  0,192                               ; add           %al,%al
+  DB  63                                  ; (bad)
+  DB  114,28                              ; jb            68ae <.literal16+0x11ee>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         68b2 <_sk_callback_sse2+0x12cb>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         68b6 <_sk_callback_sse2+0x12cf>
+  DB  199                                 ; (bad)
+  DB  62,114,28                           ; jb,pt         68ba <_sk_callback_sse2+0x12d3>
+  DB  199                                 ; (bad)
+  DB  62,171                              ; ds            stos %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+  DB  171                                 ; stos          %eax,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  170                                 ; stos          %al,%es:(%rdi)
+  DB  190,171,170,170,190                 ; mov           $0xbeaaaaab,%esi
+ALIGN 32
+
+PUBLIC _sk_start_pipeline_ssse3_lowp
+_sk_start_pipeline_ssse3_lowp LABEL PROC
+  DB  85                                  ; push          %rbp
+  DB  72,137,229                          ; mov           %rsp,%rbp
+  DB  65,87                               ; push          %r15
+  DB  65,86                               ; push          %r14
+  DB  65,85                               ; push          %r13
+  DB  65,84                               ; push          %r12
+  DB  86                                  ; push          %rsi
+  DB  87                                  ; push          %rdi
+  DB  83                                  ; push          %rbx
+  DB  72,129,236,184,0,0,0                ; sub           $0xb8,%rsp
+  DB  68,15,41,125,176                    ; movaps        %xmm15,-0x50(%rbp)
+  DB  68,15,41,117,160                    ; movaps        %xmm14,-0x60(%rbp)
+  DB  68,15,41,109,144                    ; movaps        %xmm13,-0x70(%rbp)
+  DB  68,15,41,101,128                    ; movaps        %xmm12,-0x80(%rbp)
+  DB  68,15,41,157,112,255,255,255        ; movaps        %xmm11,-0x90(%rbp)
+  DB  68,15,41,149,96,255,255,255         ; movaps        %xmm10,-0xa0(%rbp)
+  DB  68,15,41,141,80,255,255,255         ; movaps        %xmm9,-0xb0(%rbp)
+  DB  68,15,41,133,64,255,255,255         ; movaps        %xmm8,-0xc0(%rbp)
+  DB  15,41,189,48,255,255,255            ; movaps        %xmm7,-0xd0(%rbp)
+  DB  15,41,181,32,255,255,255            ; movaps        %xmm6,-0xe0(%rbp)
+  DB  76,137,195                          ; mov           %r8,%rbx
+  DB  73,137,210                          ; mov           %rdx,%r10
+  DB  73,137,207                          ; mov           %rcx,%r15
+  DB  76,139,117,48                       ; mov           0x30(%rbp),%r14
+  DB  76,137,206                          ; mov           %r9,%rsi
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  73,137,197                          ; mov           %rax,%r13
+  DB  73,137,244                          ; mov           %rsi,%r12
+  DB  73,141,79,8                         ; lea           0x8(%r15),%rcx
+  DB  72,57,217                           ; cmp           %rbx,%rcx
+  DB  118,5                               ; jbe           7e <_sk_start_pipeline_ssse3_lowp+0x7e>
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  235,81                              ; jmp           cf <_sk_start_pipeline_ssse3_lowp+0xcf>
+  DB  72,137,157,24,255,255,255           ; mov           %rbx,-0xe8(%rbp)
+  DB  65,184,0,0,0,0                      ; mov           $0x0,%r8d
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  76,137,250                          ; mov           %r15,%rdx
+  DB  76,137,209                          ; mov           %r10,%rcx
+  DB  76,137,211                          ; mov           %r10,%rbx
+  DB  65,255,213                          ; callq         *%r13
+  DB  73,137,218                          ; mov           %rbx,%r10
+  DB  72,139,157,24,255,255,255           ; mov           -0xe8(%rbp),%rbx
+  DB  73,141,87,8                         ; lea           0x8(%r15),%rdx
+  DB  73,131,199,16                       ; add           $0x10,%r15
+  DB  73,57,223                           ; cmp           %rbx,%r15
+  DB  73,137,215                          ; mov           %rdx,%r15
+  DB  118,182                             ; jbe           85 <_sk_start_pipeline_ssse3_lowp+0x85>
+  DB  73,137,216                          ; mov           %rbx,%r8
+  DB  73,41,208                           ; sub           %rdx,%r8
+  DB  116,36                              ; je            fb <_sk_start_pipeline_ssse3_lowp+0xfb>
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  15,87,228                           ; xorps         %xmm4,%xmm4
+  DB  15,87,237                           ; xorps         %xmm5,%xmm5
+  DB  15,87,246                           ; xorps         %xmm6,%xmm6
+  DB  15,87,255                           ; xorps         %xmm7,%xmm7
+  DB  76,137,247                          ; mov           %r14,%rdi
+  DB  76,137,230                          ; mov           %r12,%rsi
+  DB  76,137,209                          ; mov           %r10,%rcx
+  DB  65,255,213                          ; callq         *%r13
+  DB  72,137,216                          ; mov           %rbx,%rax
+  DB  15,40,181,32,255,255,255            ; movaps        -0xe0(%rbp),%xmm6
+  DB  15,40,189,48,255,255,255            ; movaps        -0xd0(%rbp),%xmm7
+  DB  68,15,40,133,64,255,255,255         ; movaps        -0xc0(%rbp),%xmm8
+  DB  68,15,40,141,80,255,255,255         ; movaps        -0xb0(%rbp),%xmm9
+  DB  68,15,40,149,96,255,255,255         ; movaps        -0xa0(%rbp),%xmm10
+  DB  68,15,40,157,112,255,255,255        ; movaps        -0x90(%rbp),%xmm11
+  DB  68,15,40,101,128                    ; movaps        -0x80(%rbp),%xmm12
+  DB  68,15,40,109,144                    ; movaps        -0x70(%rbp),%xmm13
+  DB  68,15,40,117,160                    ; movaps        -0x60(%rbp),%xmm14
+  DB  68,15,40,125,176                    ; movaps        -0x50(%rbp),%xmm15
+  DB  72,129,196,184,0,0,0                ; add           $0xb8,%rsp
+  DB  91                                  ; pop           %rbx
+  DB  95                                  ; pop           %rdi
+  DB  94                                  ; pop           %rsi
+  DB  65,92                               ; pop           %r12
+  DB  65,93                               ; pop           %r13
+  DB  65,94                               ; pop           %r14
+  DB  65,95                               ; pop           %r15
+  DB  93                                  ; pop           %rbp
+  DB  195                                 ; retq
+
+PUBLIC _sk_just_return_ssse3_lowp
+_sk_just_return_ssse3_lowp LABEL PROC
+  DB  195                                 ; retq
+
+PUBLIC _sk_constant_color_ssse3_lowp
+_sk_constant_color_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,16,24                            ; movups        (%rax),%xmm3
+  DB  15,88,29,239,16,0,0                 ; addps         0x10ef(%rip),%xmm3        # 1250 <_sk_xor__ssse3_lowp+0xa3>
+  DB  242,15,112,195,0                    ; pshuflw       $0x0,%xmm3,%xmm0
+  DB  102,15,112,192,80                   ; pshufd        $0x50,%xmm0,%xmm0
+  DB  242,15,112,203,170                  ; pshuflw       $0xaa,%xmm3,%xmm1
+  DB  102,15,112,201,80                   ; pshufd        $0x50,%xmm1,%xmm1
+  DB  243,15,112,211,0                    ; pshufhw       $0x0,%xmm3,%xmm2
+  DB  102,15,112,210,250                  ; pshufd        $0xfa,%xmm2,%xmm2
+  DB  243,15,112,219,170                  ; pshufhw       $0xaa,%xmm3,%xmm3
+  DB  102,15,112,219,250                  ; pshufd        $0xfa,%xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_set_rgb_ssse3_lowp
+_sk_set_rgb_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,15,16,21,25,18,0,0              ; movss         0x1219(%rip),%xmm2        # 13b0 <_sk_xor__ssse3_lowp+0x203>
+  DB  243,15,16,0                         ; movss         (%rax),%xmm0
+  DB  243,15,88,194                       ; addss         %xmm2,%xmm0
+  DB  102,65,15,126,193                   ; movd          %xmm0,%r9d
+  DB  102,65,15,110,193                   ; movd          %r9d,%xmm0
+  DB  242,15,112,192,0                    ; pshuflw       $0x0,%xmm0,%xmm0
+  DB  102,15,112,192,80                   ; pshufd        $0x50,%xmm0,%xmm0
+  DB  243,15,16,72,4                      ; movss         0x4(%rax),%xmm1
+  DB  243,15,88,202                       ; addss         %xmm2,%xmm1
+  DB  102,65,15,126,201                   ; movd          %xmm1,%r9d
+  DB  102,65,15,110,201                   ; movd          %r9d,%xmm1
+  DB  242,15,112,201,0                    ; pshuflw       $0x0,%xmm1,%xmm1
+  DB  102,15,112,201,80                   ; pshufd        $0x50,%xmm1,%xmm1
+  DB  243,15,88,80,8                      ; addss         0x8(%rax),%xmm2
+  DB  102,15,126,208                      ; movd          %xmm2,%eax
+  DB  102,15,110,208                      ; movd          %eax,%xmm2
+  DB  242,15,112,210,0                    ; pshuflw       $0x0,%xmm2,%xmm2
+  DB  102,15,112,210,80                   ; pshufd        $0x50,%xmm2,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_premul_ssse3_lowp
+_sk_premul_ssse3_lowp LABEL PROC
+  DB  102,15,56,11,195                    ; pmulhrsw      %xmm3,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,15,56,11,203                    ; pmulhrsw      %xmm3,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,15,56,11,211                    ; pmulhrsw      %xmm3,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_load_8888_ssse3_lowp
+_sk_load_8888_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,113                             ; jne           288 <_sk_load_8888_ssse3_lowp+0x7b>
+  DB  69,15,16,76,147,16                  ; movups        0x10(%r11,%rdx,4),%xmm9
+  DB  69,15,16,4,147                      ; movups        (%r11,%rdx,4),%xmm8
+  DB  102,15,111,5,54,16,0,0              ; movdqa        0x1036(%rip),%xmm0        # 1260 <_sk_xor__ssse3_lowp+0xb3>
+  DB  102,68,15,56,0,192                  ; pshufb        %xmm0,%xmm8
+  DB  102,68,15,56,0,200                  ; pshufb        %xmm0,%xmm9
+  DB  102,65,15,111,208                   ; movdqa        %xmm8,%xmm2
+  DB  102,65,15,98,209                    ; punpckldq     %xmm9,%xmm2
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  102,15,96,194                       ; punpcklbw     %xmm2,%xmm0
+  DB  102,15,239,201                      ; pxor          %xmm1,%xmm1
+  DB  102,15,104,202                      ; punpckhbw     %xmm2,%xmm1
+  DB  102,69,15,106,193                   ; punpckhdq     %xmm9,%xmm8
+  DB  102,15,239,210                      ; pxor          %xmm2,%xmm2
+  DB  102,65,15,96,208                    ; punpcklbw     %xmm8,%xmm2
+  DB  102,65,15,104,216                   ; punpckhbw     %xmm8,%xmm3
+  DB  102,68,15,111,5,0,16,0,0            ; movdqa        0x1000(%rip),%xmm8        # 1270 <_sk_xor__ssse3_lowp+0xc3>
+  DB  102,65,15,228,192                   ; pmulhuw       %xmm8,%xmm0
+  DB  102,65,15,228,200                   ; pmulhuw       %xmm8,%xmm1
+  DB  102,65,15,228,208                   ; pmulhuw       %xmm8,%xmm2
+  DB  102,65,15,228,216                   ; pmulhuw       %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,129                             ; ja            222 <_sk_load_8888_ssse3_lowp+0x15>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,132,0,0,0                 ; lea           0x84(%rip),%r10        # 330 <_sk_load_8888_ssse3_lowp+0x123>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,65,15,110,68,147,24             ; movd          0x18(%r11,%rdx,4),%xmm0
+  DB  102,68,15,112,200,69                ; pshufd        $0x45,%xmm0,%xmm9
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  243,65,15,16,68,147,20              ; movss         0x14(%r11,%rdx,4),%xmm0
+  DB  65,15,198,193,0                     ; shufps        $0x0,%xmm9,%xmm0
+  DB  65,15,198,193,226                   ; shufps        $0xe2,%xmm9,%xmm0
+  DB  68,15,40,200                        ; movaps        %xmm0,%xmm9
+  DB  243,65,15,16,68,147,16              ; movss         0x10(%r11,%rdx,4),%xmm0
+  DB  243,68,15,16,200                    ; movss         %xmm0,%xmm9
+  DB  243,65,15,16,68,147,12              ; movss         0xc(%r11,%rdx,4),%xmm0
+  DB  65,15,198,192,32                    ; shufps        $0x20,%xmm8,%xmm0
+  DB  68,15,198,192,36                    ; shufps        $0x24,%xmm0,%xmm8
+  DB  243,65,15,16,68,147,8               ; movss         0x8(%r11,%rdx,4),%xmm0
+  DB  65,15,198,192,48                    ; shufps        $0x30,%xmm8,%xmm0
+  DB  68,15,198,192,132                   ; shufps        $0x84,%xmm0,%xmm8
+  DB  243,65,15,16,68,147,4               ; movss         0x4(%r11,%rdx,4),%xmm0
+  DB  65,15,198,192,0                     ; shufps        $0x0,%xmm8,%xmm0
+  DB  65,15,198,192,226                   ; shufps        $0xe2,%xmm8,%xmm0
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  243,65,15,16,4,147                  ; movss         (%r11,%rdx,4),%xmm0
+  DB  243,68,15,16,192                    ; movss         %xmm0,%xmm8
+  DB  233,244,254,255,255                 ; jmpq          222 <_sk_load_8888_ssse3_lowp+0x15>
+  DB  102,144                             ; xchg          %ax,%ax
+  DB  238                                 ; out           %al,(%dx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  217,255                             ; fcos
+  DB  255                                 ; (bad)
+  DB  255,200                             ; dec           %eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,183,255,255,255,171             ; pushq         -0x54000001(%rdi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,150,255,255,255,133             ; callq         *-0x7a000001(%rsi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_store_8888_ssse3_lowp
+_sk_store_8888_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  102,68,15,111,208                   ; movdqa        %xmm0,%xmm10
+  DB  102,65,15,113,210,7                 ; psrlw         $0x7,%xmm10
+  DB  102,68,15,111,194                   ; movdqa        %xmm2,%xmm8
+  DB  102,65,15,113,208,7                 ; psrlw         $0x7,%xmm8
+  DB  102,69,15,103,208                   ; packuswb      %xmm8,%xmm10
+  DB  102,68,15,111,193                   ; movdqa        %xmm1,%xmm8
+  DB  102,65,15,113,208,7                 ; psrlw         $0x7,%xmm8
+  DB  102,68,15,111,203                   ; movdqa        %xmm3,%xmm9
+  DB  102,65,15,113,209,7                 ; psrlw         $0x7,%xmm9
+  DB  102,69,15,103,193                   ; packuswb      %xmm9,%xmm8
+  DB  102,69,15,111,202                   ; movdqa        %xmm10,%xmm9
+  DB  102,69,15,96,200                    ; punpcklbw     %xmm8,%xmm9
+  DB  102,69,15,104,208                   ; punpckhbw     %xmm8,%xmm10
+  DB  102,69,15,111,193                   ; movdqa        %xmm9,%xmm8
+  DB  102,69,15,97,194                    ; punpcklwd     %xmm10,%xmm8
+  DB  102,69,15,105,202                   ; punpckhwd     %xmm10,%xmm9
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,17                              ; jne           3bb <_sk_store_8888_ssse3_lowp+0x6f>
+  DB  243,69,15,127,76,147,16             ; movdqu        %xmm9,0x10(%r11,%rdx,4)
+  DB  243,69,15,127,4,147                 ; movdqu        %xmm8,(%r11,%rdx,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            3b7 <_sk_store_8888_ssse3_lowp+0x6b>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,90,0,0,0                  ; lea           0x5a(%rip),%r10        # 430 <_sk_store_8888_ssse3_lowp+0xe4>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,69,15,112,209,78                ; pshufd        $0x4e,%xmm9,%xmm10
+  DB  102,69,15,126,84,147,24             ; movd          %xmm10,0x18(%r11,%rdx,4)
+  DB  102,69,15,112,209,229               ; pshufd        $0xe5,%xmm9,%xmm10
+  DB  102,69,15,126,84,147,20             ; movd          %xmm10,0x14(%r11,%rdx,4)
+  DB  102,69,15,126,76,147,16             ; movd          %xmm9,0x10(%r11,%rdx,4)
+  DB  102,69,15,112,200,231               ; pshufd        $0xe7,%xmm8,%xmm9
+  DB  102,69,15,126,76,147,12             ; movd          %xmm9,0xc(%r11,%rdx,4)
+  DB  102,69,15,112,200,78                ; pshufd        $0x4e,%xmm8,%xmm9
+  DB  102,69,15,126,76,147,8              ; movd          %xmm9,0x8(%r11,%rdx,4)
+  DB  102,69,15,112,200,229               ; pshufd        $0xe5,%xmm8,%xmm9
+  DB  102,69,15,126,76,147,4              ; movd          %xmm9,0x4(%r11,%rdx,4)
+  DB  102,69,15,126,4,147                 ; movd          %xmm8,(%r11,%rdx,4)
+  DB  235,136                             ; jmp           3b7 <_sk_store_8888_ssse3_lowp+0x6b>
+  DB  144                                 ; nop
+  DB  247,255                             ; idiv          %edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  234                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  221,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,208                             ; callq         *%rax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,201                             ; dec           %ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  188,255,255,255,175                 ; mov           $0xafffffff,%esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_a8_ssse3_lowp
+_sk_load_a8_ssse3_lowp LABEL PROC
+  DB  80                                  ; push          %rax
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,39                              ; jne           47e <_sk_load_a8_ssse3_lowp+0x32>
+  DB  243,65,15,126,28,19                 ; movq          (%r11,%rdx,1),%xmm3
+  DB  102,15,96,216                       ; punpcklbw     %xmm0,%xmm3
+  DB  102,15,113,243,8                    ; psllw         $0x8,%xmm3
+  DB  102,15,228,29,18,14,0,0             ; pmulhuw       0xe12(%rip),%xmm3        # 1280 <_sk_xor__ssse3_lowp+0xd3>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  65,89                               ; pop           %r9
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,207                             ; ja            461 <_sk_load_a8_ssse3_lowp+0x15>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,95,0,0,0                  ; lea           0x5f(%rip),%r10        # 4fc <_sk_load_a8_ssse3_lowp+0xb0>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  65,15,182,68,19,6                   ; movzbl        0x6(%r11,%rdx,1),%eax
+  DB  102,15,239,219                      ; pxor          %xmm3,%xmm3
+  DB  102,15,196,216,6                    ; pinsrw        $0x6,%eax,%xmm3
+  DB  65,15,182,68,19,5                   ; movzbl        0x5(%r11,%rdx,1),%eax
+  DB  102,15,196,216,5                    ; pinsrw        $0x5,%eax,%xmm3
+  DB  65,15,182,68,19,4                   ; movzbl        0x4(%r11,%rdx,1),%eax
+  DB  102,15,196,216,4                    ; pinsrw        $0x4,%eax,%xmm3
+  DB  65,15,182,68,19,3                   ; movzbl        0x3(%r11,%rdx,1),%eax
+  DB  102,15,196,216,3                    ; pinsrw        $0x3,%eax,%xmm3
+  DB  65,15,182,68,19,2                   ; movzbl        0x2(%r11,%rdx,1),%eax
+  DB  102,15,196,216,2                    ; pinsrw        $0x2,%eax,%xmm3
+  DB  65,15,182,68,19,1                   ; movzbl        0x1(%r11,%rdx,1),%eax
+  DB  102,15,196,216,1                    ; pinsrw        $0x1,%eax,%xmm3
+  DB  65,15,182,4,19                      ; movzbl        (%r11,%rdx,1),%eax
+  DB  102,15,196,216,0                    ; pinsrw        $0x0,%eax,%xmm3
+  DB  233,102,255,255,255                 ; jmpq          461 <_sk_load_a8_ssse3_lowp+0x15>
+  DB  144                                 ; nop
+  DB  240,255                             ; lock          (bad)
+  DB  255                                 ; (bad)
+  DB  255,229                             ; jmpq          *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  218,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,207                             ; dec           %edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,196                             ; inc           %esp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  185,255,255,255,170                 ; mov           $0xaaffffff,%ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_store_a8_ssse3_lowp
+_sk_store_a8_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  102,68,15,111,195                   ; movdqa        %xmm3,%xmm8
+  DB  102,65,15,113,208,7                 ; psrlw         $0x7,%xmm8
+  DB  102,69,15,103,192                   ; packuswb      %xmm8,%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,10                              ; jne           53c <_sk_store_a8_ssse3_lowp+0x24>
+  DB  242,69,15,17,4,19                   ; movsd         %xmm8,(%r11,%rdx,1)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,236                             ; ja            538 <_sk_store_a8_ssse3_lowp+0x20>
+  DB  72,131,236,120                      ; sub           $0x78,%rsp
+  DB  102,68,15,96,192                    ; punpcklbw     %xmm0,%xmm8
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,128,0,0,0                 ; lea           0x80(%rip),%r10        # 5e0 <_sk_store_a8_ssse3_lowp+0xc8>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,68,15,127,68,36,96              ; movdqa        %xmm8,0x60(%rsp)
+  DB  138,68,36,108                       ; mov           0x6c(%rsp),%al
+  DB  65,136,68,19,6                      ; mov           %al,0x6(%r11,%rdx,1)
+  DB  102,68,15,127,68,36,80              ; movdqa        %xmm8,0x50(%rsp)
+  DB  138,68,36,90                        ; mov           0x5a(%rsp),%al
+  DB  65,136,68,19,5                      ; mov           %al,0x5(%r11,%rdx,1)
+  DB  102,68,15,127,68,36,64              ; movdqa        %xmm8,0x40(%rsp)
+  DB  138,68,36,72                        ; mov           0x48(%rsp),%al
+  DB  65,136,68,19,4                      ; mov           %al,0x4(%r11,%rdx,1)
+  DB  102,68,15,127,68,36,48              ; movdqa        %xmm8,0x30(%rsp)
+  DB  138,68,36,54                        ; mov           0x36(%rsp),%al
+  DB  65,136,68,19,3                      ; mov           %al,0x3(%r11,%rdx,1)
+  DB  102,68,15,127,68,36,32              ; movdqa        %xmm8,0x20(%rsp)
+  DB  138,68,36,36                        ; mov           0x24(%rsp),%al
+  DB  65,136,68,19,2                      ; mov           %al,0x2(%r11,%rdx,1)
+  DB  102,68,15,127,68,36,16              ; movdqa        %xmm8,0x10(%rsp)
+  DB  138,68,36,18                        ; mov           0x12(%rsp),%al
+  DB  65,136,68,19,1                      ; mov           %al,0x1(%r11,%rdx,1)
+  DB  102,68,15,127,4,36                  ; movdqa        %xmm8,(%rsp)
+  DB  138,4,36                            ; mov           (%rsp),%al
+  DB  65,136,4,19                         ; mov           %al,(%r11,%rdx,1)
+  DB  72,131,196,120                      ; add           $0x78,%rsp
+  DB  233,89,255,255,255                  ; jmpq          538 <_sk_store_a8_ssse3_lowp+0x20>
+  DB  144                                 ; nop
+  DB  233,255,255,255,217                 ; jmpq          ffffffffda0005e4 <_sk_xor__ssse3_lowp+0xffffffffd9fff437>
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,201                             ; dec           %ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  185,255,255,255,169                 ; mov           $0xa9ffffff,%ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,153,255,255,255,137             ; lcall         *-0x76000001(%rcx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_load_g8_ssse3_lowp
+_sk_load_g8_ssse3_lowp LABEL PROC
+  DB  80                                  ; push          %rax
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,44                              ; jne           633 <_sk_load_g8_ssse3_lowp+0x37>
+  DB  243,65,15,126,4,19                  ; movq          (%r11,%rdx,1),%xmm0
+  DB  102,15,96,192                       ; punpcklbw     %xmm0,%xmm0
+  DB  102,15,113,240,8                    ; psllw         $0x8,%xmm0
+  DB  102,15,228,5,114,12,0,0             ; pmulhuw       0xc72(%rip),%xmm0        # 1290 <_sk_xor__ssse3_lowp+0xe3>
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,29,121,12,0,0                 ; movaps        0xc79(%rip),%xmm3        # 12a0 <_sk_xor__ssse3_lowp+0xf3>
+  DB  102,15,111,200                      ; movdqa        %xmm0,%xmm1
+  DB  102,15,111,208                      ; movdqa        %xmm0,%xmm2
+  DB  65,89                               ; pop           %r9
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,202                             ; ja            611 <_sk_load_g8_ssse3_lowp+0x15>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,94,0,0,0                  ; lea           0x5e(%rip),%r10        # 6b0 <_sk_load_g8_ssse3_lowp+0xb4>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  65,15,182,68,19,6                   ; movzbl        0x6(%r11,%rdx,1),%eax
+  DB  102,15,239,192                      ; pxor          %xmm0,%xmm0
+  DB  102,15,196,192,6                    ; pinsrw        $0x6,%eax,%xmm0
+  DB  65,15,182,68,19,5                   ; movzbl        0x5(%r11,%rdx,1),%eax
+  DB  102,15,196,192,5                    ; pinsrw        $0x5,%eax,%xmm0
+  DB  65,15,182,68,19,4                   ; movzbl        0x4(%r11,%rdx,1),%eax
+  DB  102,15,196,192,4                    ; pinsrw        $0x4,%eax,%xmm0
+  DB  65,15,182,68,19,3                   ; movzbl        0x3(%r11,%rdx,1),%eax
+  DB  102,15,196,192,3                    ; pinsrw        $0x3,%eax,%xmm0
+  DB  65,15,182,68,19,2                   ; movzbl        0x2(%r11,%rdx,1),%eax
+  DB  102,15,196,192,2                    ; pinsrw        $0x2,%eax,%xmm0
+  DB  65,15,182,68,19,1                   ; movzbl        0x1(%r11,%rdx,1),%eax
+  DB  102,15,196,192,1                    ; pinsrw        $0x1,%eax,%xmm0
+  DB  65,15,182,4,19                      ; movzbl        (%r11,%rdx,1),%eax
+  DB  102,15,196,192,0                    ; pinsrw        $0x0,%eax,%xmm0
+  DB  233,97,255,255,255                  ; jmpq          611 <_sk_load_g8_ssse3_lowp+0x15>
+  DB  241                                 ; icebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,230                             ; jmpq          *%rsi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  219,255                             ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,208                             ; callq         *%rax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,197                             ; inc           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  186,255,255,255,171                 ; mov           $0xabffffff,%edx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_srcover_rgba_8888_ssse3_lowp
+_sk_srcover_rgba_8888_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,66,1,0,0                     ; jne           81c <_sk_srcover_rgba_8888_ssse3_lowp+0x150>
+  DB  69,15,16,76,147,16                  ; movups        0x10(%r11,%rdx,4),%xmm9
+  DB  69,15,16,4,147                      ; movups        (%r11,%rdx,4),%xmm8
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  102,15,111,37,192,11,0,0            ; movdqa        0xbc0(%rip),%xmm4        # 12b0 <_sk_xor__ssse3_lowp+0x103>
+  DB  102,68,15,56,0,196                  ; pshufb        %xmm4,%xmm8
+  DB  102,68,15,56,0,204                  ; pshufb        %xmm4,%xmm9
+  DB  102,65,15,111,240                   ; movdqa        %xmm8,%xmm6
+  DB  102,65,15,98,241                    ; punpckldq     %xmm9,%xmm6
+  DB  102,15,239,255                      ; pxor          %xmm7,%xmm7
+  DB  102,15,239,228                      ; pxor          %xmm4,%xmm4
+  DB  102,15,96,230                       ; punpcklbw     %xmm6,%xmm4
+  DB  102,15,239,237                      ; pxor          %xmm5,%xmm5
+  DB  102,15,104,238                      ; punpckhbw     %xmm6,%xmm5
+  DB  102,69,15,106,193                   ; punpckhdq     %xmm9,%xmm8
+  DB  102,15,239,246                      ; pxor          %xmm6,%xmm6
+  DB  102,65,15,96,240                    ; punpcklbw     %xmm8,%xmm6
+  DB  102,65,15,104,248                   ; punpckhbw     %xmm8,%xmm7
+  DB  102,68,15,111,5,138,11,0,0          ; movdqa        0xb8a(%rip),%xmm8        # 12c0 <_sk_xor__ssse3_lowp+0x113>
+  DB  102,65,15,228,224                   ; pmulhuw       %xmm8,%xmm4
+  DB  102,65,15,228,232                   ; pmulhuw       %xmm8,%xmm5
+  DB  102,65,15,228,240                   ; pmulhuw       %xmm8,%xmm6
+  DB  102,65,15,228,248                   ; pmulhuw       %xmm8,%xmm7
+  DB  102,68,15,111,29,125,11,0,0         ; movdqa        0xb7d(%rip),%xmm11        # 12d0 <_sk_xor__ssse3_lowp+0x123>
+  DB  102,68,15,249,219                   ; psubw         %xmm3,%xmm11
+  DB  102,68,15,111,196                   ; movdqa        %xmm4,%xmm8
+  DB  102,69,15,56,11,195                 ; pmulhrsw      %xmm11,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,68,15,253,192                   ; paddw         %xmm0,%xmm8
+  DB  102,15,111,197                      ; movdqa        %xmm5,%xmm0
+  DB  102,65,15,56,11,195                 ; pmulhrsw      %xmm11,%xmm0
+  DB  102,68,15,56,29,200                 ; pabsw         %xmm0,%xmm9
+  DB  102,68,15,253,201                   ; paddw         %xmm1,%xmm9
+  DB  102,15,111,198                      ; movdqa        %xmm6,%xmm0
+  DB  102,65,15,56,11,195                 ; pmulhrsw      %xmm11,%xmm0
+  DB  102,68,15,56,29,208                 ; pabsw         %xmm0,%xmm10
+  DB  102,68,15,253,210                   ; paddw         %xmm2,%xmm10
+  DB  102,68,15,56,11,223                 ; pmulhrsw      %xmm7,%xmm11
+  DB  102,69,15,56,29,219                 ; pabsw         %xmm11,%xmm11
+  DB  102,68,15,253,219                   ; paddw         %xmm3,%xmm11
+  DB  102,65,15,111,208                   ; movdqa        %xmm8,%xmm2
+  DB  102,15,113,210,7                    ; psrlw         $0x7,%xmm2
+  DB  102,65,15,111,194                   ; movdqa        %xmm10,%xmm0
+  DB  102,15,113,208,7                    ; psrlw         $0x7,%xmm0
+  DB  102,15,103,208                      ; packuswb      %xmm0,%xmm2
+  DB  102,65,15,111,193                   ; movdqa        %xmm9,%xmm0
+  DB  102,15,113,208,7                    ; psrlw         $0x7,%xmm0
+  DB  102,65,15,111,203                   ; movdqa        %xmm11,%xmm1
+  DB  102,15,113,209,7                    ; psrlw         $0x7,%xmm1
+  DB  102,15,103,193                      ; packuswb      %xmm1,%xmm0
+  DB  102,15,111,202                      ; movdqa        %xmm2,%xmm1
+  DB  102,15,96,200                       ; punpcklbw     %xmm0,%xmm1
+  DB  102,15,104,208                      ; punpckhbw     %xmm0,%xmm2
+  DB  102,15,111,193                      ; movdqa        %xmm1,%xmm0
+  DB  102,15,97,194                       ; punpcklwd     %xmm2,%xmm0
+  DB  102,15,105,202                      ; punpckhwd     %xmm2,%xmm1
+  DB  15,133,207,0,0,0                    ; jne           8c6 <_sk_srcover_rgba_8888_ssse3_lowp+0x1fa>
+  DB  243,65,15,127,76,147,16             ; movdqu        %xmm1,0x10(%r11,%rdx,4)
+  DB  243,65,15,127,4,147                 ; movdqu        %xmm0,(%r11,%rdx,4)
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  102,65,15,111,192                   ; movdqa        %xmm8,%xmm0
+  DB  102,65,15,111,201                   ; movdqa        %xmm9,%xmm1
+  DB  102,65,15,111,210                   ; movdqa        %xmm10,%xmm2
+  DB  102,65,15,111,219                   ; movdqa        %xmm11,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  102,69,15,239,201                   ; pxor          %xmm9,%xmm9
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,172,254,255,255              ; ja            6e5 <_sk_srcover_rgba_8888_ssse3_lowp+0x19>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,248,0,0,0                 ; lea           0xf8(%rip),%r10        # 93c <_sk_srcover_rgba_8888_ssse3_lowp+0x270>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,65,15,110,100,147,24            ; movd          0x18(%r11,%rdx,4),%xmm4
+  DB  102,68,15,112,204,69                ; pshufd        $0x45,%xmm4,%xmm9
+  DB  69,15,87,192                        ; xorps         %xmm8,%xmm8
+  DB  243,65,15,16,100,147,20             ; movss         0x14(%r11,%rdx,4),%xmm4
+  DB  65,15,198,225,0                     ; shufps        $0x0,%xmm9,%xmm4
+  DB  65,15,198,225,226                   ; shufps        $0xe2,%xmm9,%xmm4
+  DB  68,15,40,204                        ; movaps        %xmm4,%xmm9
+  DB  243,65,15,16,100,147,16             ; movss         0x10(%r11,%rdx,4),%xmm4
+  DB  243,68,15,16,204                    ; movss         %xmm4,%xmm9
+  DB  243,65,15,16,100,147,12             ; movss         0xc(%r11,%rdx,4),%xmm4
+  DB  65,15,198,224,32                    ; shufps        $0x20,%xmm8,%xmm4
+  DB  68,15,198,196,36                    ; shufps        $0x24,%xmm4,%xmm8
+  DB  243,65,15,16,100,147,8              ; movss         0x8(%r11,%rdx,4),%xmm4
+  DB  65,15,198,224,48                    ; shufps        $0x30,%xmm8,%xmm4
+  DB  68,15,198,196,132                   ; shufps        $0x84,%xmm4,%xmm8
+  DB  243,65,15,16,100,147,4              ; movss         0x4(%r11,%rdx,4),%xmm4
+  DB  65,15,198,224,0                     ; shufps        $0x0,%xmm8,%xmm4
+  DB  65,15,198,224,226                   ; shufps        $0xe2,%xmm8,%xmm4
+  DB  68,15,40,196                        ; movaps        %xmm4,%xmm8
+  DB  243,65,15,16,36,147                 ; movss         (%r11,%rdx,4),%xmm4
+  DB  243,68,15,16,196                    ; movss         %xmm4,%xmm8
+  DB  233,31,254,255,255                  ; jmpq          6e5 <_sk_srcover_rgba_8888_ssse3_lowp+0x19>
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,42,255,255,255               ; ja            804 <_sk_srcover_rgba_8888_ssse3_lowp+0x138>
+  DB  65,15,182,193                       ; movzbl        %r9b,%eax
+  DB  76,141,13,115,0,0,0                 ; lea           0x73(%rip),%r9        # 958 <_sk_srcover_rgba_8888_ssse3_lowp+0x28c>
+  DB  73,99,4,129                         ; movslq        (%r9,%rax,4),%rax
+  DB  76,1,200                            ; add           %r9,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  102,15,112,209,78                   ; pshufd        $0x4e,%xmm1,%xmm2
+  DB  102,65,15,126,84,147,24             ; movd          %xmm2,0x18(%r11,%rdx,4)
+  DB  102,15,112,209,229                  ; pshufd        $0xe5,%xmm1,%xmm2
+  DB  102,65,15,126,84,147,20             ; movd          %xmm2,0x14(%r11,%rdx,4)
+  DB  102,65,15,126,76,147,16             ; movd          %xmm1,0x10(%r11,%rdx,4)
+  DB  102,15,112,200,231                  ; pshufd        $0xe7,%xmm0,%xmm1
+  DB  102,65,15,126,76,147,12             ; movd          %xmm1,0xc(%r11,%rdx,4)
+  DB  102,15,112,200,78                   ; pshufd        $0x4e,%xmm0,%xmm1
+  DB  102,65,15,126,76,147,8              ; movd          %xmm1,0x8(%r11,%rdx,4)
+  DB  102,15,112,200,229                  ; pshufd        $0xe5,%xmm0,%xmm1
+  DB  102,65,15,126,76,147,4              ; movd          %xmm1,0x4(%r11,%rdx,4)
+  DB  102,65,15,126,4,147                 ; movd          %xmm0,(%r11,%rdx,4)
+  DB  233,200,254,255,255                 ; jmpq          804 <_sk_srcover_rgba_8888_ssse3_lowp+0x138>
+  DB  122,255                             ; jp            93d <_sk_srcover_rgba_8888_ssse3_lowp+0x271>
+  DB  255                                 ; (bad)
+  DB  255,101,255                         ; jmpq          *-0x1(%rbp)
+  DB  255                                 ; (bad)
+  DB  255,84,255,255                      ; callq         *-0x1(%rdi,%rdi,8)
+  DB  255,67,255                          ; incl          -0x1(%rbx)
+  DB  255                                 ; (bad)
+  DB  255,55                              ; pushq         (%rdi)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,34                              ; jmpq          *(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,17                              ; callq         *(%rcx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  217,255                             ; fcos
+  DB  255                                 ; (bad)
+  DB  255,205                             ; dec           %ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,193                             ; inc           %ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,181,255,255,255,174             ; pushq         -0x51000001(%rbp)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,162,255,255,255,150             ; jmpq          *-0x69000001(%rdx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_scale_1_float_ssse3_lowp
+_sk_scale_1_float_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  243,68,15,88,5,48,10,0,0            ; addss         0xa30(%rip),%xmm8        # 13b4 <_sk_xor__ssse3_lowp+0x207>
+  DB  102,68,15,126,192                   ; movd          %xmm8,%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  242,69,15,112,192,0                 ; pshuflw       $0x0,%xmm8,%xmm8
+  DB  102,69,15,112,192,80                ; pshufd        $0x50,%xmm8,%xmm8
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm3
+  DB  102,15,56,29,219                    ; pabsw         %xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_scale_u8_ssse3_lowp
+_sk_scale_u8_ssse3_lowp LABEL PROC
+  DB  80                                  ; push          %rax
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  117,76                              ; jne           a21 <_sk_scale_u8_ssse3_lowp+0x57>
+  DB  243,69,15,126,4,19                  ; movq          (%r11,%rdx,1),%xmm8
+  DB  102,68,15,96,192                    ; punpcklbw     %xmm0,%xmm8
+  DB  102,65,15,113,240,8                 ; psllw         $0x8,%xmm8
+  DB  102,68,15,228,5,241,8,0,0           ; pmulhuw       0x8f1(%rip),%xmm8        # 12e0 <_sk_xor__ssse3_lowp+0x133>
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm3
+  DB  102,15,56,29,219                    ; pabsw         %xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,89                               ; pop           %r9
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  119,170                             ; ja            9e0 <_sk_scale_u8_ssse3_lowp+0x16>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,103,0,0,0                 ; lea           0x67(%rip),%r10        # aa8 <_sk_scale_u8_ssse3_lowp+0xde>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  65,15,182,68,19,6                   ; movzbl        0x6(%r11,%rdx,1),%eax
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  102,68,15,196,192,6                 ; pinsrw        $0x6,%eax,%xmm8
+  DB  65,15,182,68,19,5                   ; movzbl        0x5(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,5                 ; pinsrw        $0x5,%eax,%xmm8
+  DB  65,15,182,68,19,4                   ; movzbl        0x4(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,4                 ; pinsrw        $0x4,%eax,%xmm8
+  DB  65,15,182,68,19,3                   ; movzbl        0x3(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,3                 ; pinsrw        $0x3,%eax,%xmm8
+  DB  65,15,182,68,19,2                   ; movzbl        0x2(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,2                 ; pinsrw        $0x2,%eax,%xmm8
+  DB  65,15,182,68,19,1                   ; movzbl        0x1(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,1                 ; pinsrw        $0x1,%eax,%xmm8
+  DB  65,15,182,4,19                      ; movzbl        (%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,0                 ; pinsrw        $0x0,%eax,%xmm8
+  DB  233,57,255,255,255                  ; jmpq          9e0 <_sk_scale_u8_ssse3_lowp+0x16>
+  DB  144                                 ; nop
+  DB  239                                 ; out           %eax,(%dx)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,227                             ; jmpq          *%rbx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,215                             ; callq         *%rdi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,203                             ; dec           %ebx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  191,255,255,255,179                 ; mov           $0xb3ffffff,%edi
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+  DB  162                                 ; .byte         0xa2
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_lerp_1_float_ssse3_lowp
+_sk_lerp_1_float_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  243,68,15,16,0                      ; movss         (%rax),%xmm8
+  DB  243,68,15,88,5,228,8,0,0            ; addss         0x8e4(%rip),%xmm8        # 13b8 <_sk_xor__ssse3_lowp+0x20b>
+  DB  102,68,15,126,192                   ; movd          %xmm8,%eax
+  DB  102,68,15,110,192                   ; movd          %eax,%xmm8
+  DB  242,69,15,112,192,0                 ; pshuflw       $0x0,%xmm8,%xmm8
+  DB  102,69,15,112,192,80                ; pshufd        $0x50,%xmm8,%xmm8
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,68,15,56,29,200                 ; pabsw         %xmm0,%xmm9
+  DB  102,68,15,111,21,241,7,0,0          ; movdqa        0x7f1(%rip),%xmm10        # 12f0 <_sk_xor__ssse3_lowp+0x143>
+  DB  102,69,15,249,208                   ; psubw         %xmm8,%xmm10
+  DB  102,15,111,196                      ; movdqa        %xmm4,%xmm0
+  DB  102,65,15,56,11,194                 ; pmulhrsw      %xmm10,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,253,193                   ; paddw         %xmm9,%xmm0
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,68,15,56,29,201                 ; pabsw         %xmm1,%xmm9
+  DB  102,15,111,205                      ; movdqa        %xmm5,%xmm1
+  DB  102,65,15,56,11,202                 ; pmulhrsw      %xmm10,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,253,201                   ; paddw         %xmm9,%xmm1
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,68,15,56,29,202                 ; pabsw         %xmm2,%xmm9
+  DB  102,15,111,214                      ; movdqa        %xmm6,%xmm2
+  DB  102,65,15,56,11,210                 ; pmulhrsw      %xmm10,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,253,209                   ; paddw         %xmm9,%xmm2
+  DB  102,65,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm3
+  DB  102,68,15,56,29,195                 ; pabsw         %xmm3,%xmm8
+  DB  102,68,15,56,11,215                 ; pmulhrsw      %xmm7,%xmm10
+  DB  102,65,15,56,29,218                 ; pabsw         %xmm10,%xmm3
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_lerp_u8_ssse3_lowp
+_sk_lerp_u8_ssse3_lowp LABEL PROC
+  DB  80                                  ; push          %rax
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  76,139,24                           ; mov           (%rax),%r11
+  DB  77,133,192                          ; test          %r8,%r8
+  DB  15,133,171,0,0,0                    ; jne           c33 <_sk_lerp_u8_ssse3_lowp+0xba>
+  DB  243,69,15,126,4,19                  ; movq          (%r11,%rdx,1),%xmm8
+  DB  102,68,15,96,192                    ; punpcklbw     %xmm0,%xmm8
+  DB  102,65,15,113,240,8                 ; psllw         $0x8,%xmm8
+  DB  102,68,15,228,5,94,7,0,0            ; pmulhuw       0x75e(%rip),%xmm8        # 1300 <_sk_xor__ssse3_lowp+0x153>
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,68,15,56,29,200                 ; pabsw         %xmm0,%xmm9
+  DB  102,68,15,111,21,89,7,0,0           ; movdqa        0x759(%rip),%xmm10        # 1310 <_sk_xor__ssse3_lowp+0x163>
+  DB  102,69,15,249,208                   ; psubw         %xmm8,%xmm10
+  DB  102,15,111,196                      ; movdqa        %xmm4,%xmm0
+  DB  102,65,15,56,11,194                 ; pmulhrsw      %xmm10,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,253,193                   ; paddw         %xmm9,%xmm0
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,68,15,56,29,201                 ; pabsw         %xmm1,%xmm9
+  DB  102,15,111,205                      ; movdqa        %xmm5,%xmm1
+  DB  102,65,15,56,11,202                 ; pmulhrsw      %xmm10,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,253,201                   ; paddw         %xmm9,%xmm1
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,68,15,56,29,202                 ; pabsw         %xmm2,%xmm9
+  DB  102,15,111,214                      ; movdqa        %xmm6,%xmm2
+  DB  102,65,15,56,11,210                 ; pmulhrsw      %xmm10,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,253,209                   ; paddw         %xmm9,%xmm2
+  DB  102,65,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm3
+  DB  102,68,15,56,29,195                 ; pabsw         %xmm3,%xmm8
+  DB  102,68,15,56,11,215                 ; pmulhrsw      %xmm7,%xmm10
+  DB  102,65,15,56,29,218                 ; pabsw         %xmm10,%xmm3
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  65,89                               ; pop           %r9
+  DB  255,224                             ; jmpq          *%rax
+  DB  69,137,193                          ; mov           %r8d,%r9d
+  DB  65,128,225,7                        ; and           $0x7,%r9b
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  65,254,201                          ; dec           %r9b
+  DB  65,128,249,6                        ; cmp           $0x6,%r9b
+  DB  15,135,71,255,255,255               ; ja            b93 <_sk_lerp_u8_ssse3_lowp+0x1a>
+  DB  69,15,182,201                       ; movzbl        %r9b,%r9d
+  DB  76,141,21,105,0,0,0                 ; lea           0x69(%rip),%r10        # cc0 <_sk_lerp_u8_ssse3_lowp+0x147>
+  DB  75,99,4,138                         ; movslq        (%r10,%r9,4),%rax
+  DB  76,1,208                            ; add           %r10,%rax
+  DB  255,224                             ; jmpq          *%rax
+  DB  65,15,182,68,19,6                   ; movzbl        0x6(%r11,%rdx,1),%eax
+  DB  102,69,15,239,192                   ; pxor          %xmm8,%xmm8
+  DB  102,68,15,196,192,6                 ; pinsrw        $0x6,%eax,%xmm8
+  DB  65,15,182,68,19,5                   ; movzbl        0x5(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,5                 ; pinsrw        $0x5,%eax,%xmm8
+  DB  65,15,182,68,19,4                   ; movzbl        0x4(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,4                 ; pinsrw        $0x4,%eax,%xmm8
+  DB  65,15,182,68,19,3                   ; movzbl        0x3(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,3                 ; pinsrw        $0x3,%eax,%xmm8
+  DB  65,15,182,68,19,2                   ; movzbl        0x2(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,2                 ; pinsrw        $0x2,%eax,%xmm8
+  DB  65,15,182,68,19,1                   ; movzbl        0x1(%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,1                 ; pinsrw        $0x1,%eax,%xmm8
+  DB  65,15,182,4,19                      ; movzbl        (%r11,%rdx,1),%eax
+  DB  102,68,15,196,192,0                 ; pinsrw        $0x0,%eax,%xmm8
+  DB  233,214,254,255,255                 ; jmpq          b93 <_sk_lerp_u8_ssse3_lowp+0x1a>
+  DB  15,31,0                             ; nopl          (%rax)
+  DB  237                                 ; in            (%dx),%eax
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,225                             ; jmpq          *%rcx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,213                             ; callq         *%rbp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255,201                             ; dec           %ecx
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  189,255,255,255,177                 ; mov           $0xb1ffffff,%ebp
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+  DB  160                                 ; .byte         0xa0
+  DB  255                                 ; (bad)
+  DB  255                                 ; (bad)
+  DB  255                                 ; .byte         0xff
+
+PUBLIC _sk_swap_rb_ssse3_lowp
+_sk_swap_rb_ssse3_lowp LABEL PROC
+  DB  68,15,40,192                        ; movaps        %xmm0,%xmm8
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,194                           ; movaps        %xmm2,%xmm0
+  DB  65,15,40,208                        ; movaps        %xmm8,%xmm2
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_swap_ssse3_lowp
+_sk_swap_ssse3_lowp LABEL PROC
+  DB  68,15,40,195                        ; movaps        %xmm3,%xmm8
+  DB  68,15,40,202                        ; movaps        %xmm2,%xmm9
+  DB  68,15,40,209                        ; movaps        %xmm1,%xmm10
+  DB  68,15,40,216                        ; movaps        %xmm0,%xmm11
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  65,15,40,227                        ; movaps        %xmm11,%xmm4
+  DB  65,15,40,234                        ; movaps        %xmm10,%xmm5
+  DB  65,15,40,241                        ; movaps        %xmm9,%xmm6
+  DB  65,15,40,248                        ; movaps        %xmm8,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_src_dst_ssse3_lowp
+_sk_move_src_dst_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,224                           ; movaps        %xmm0,%xmm4
+  DB  15,40,233                           ; movaps        %xmm1,%xmm5
+  DB  15,40,242                           ; movaps        %xmm2,%xmm6
+  DB  15,40,251                           ; movaps        %xmm3,%xmm7
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_move_dst_src_ssse3_lowp
+_sk_move_dst_src_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,40,196                           ; movaps        %xmm4,%xmm0
+  DB  15,40,205                           ; movaps        %xmm5,%xmm1
+  DB  15,40,214                           ; movaps        %xmm6,%xmm2
+  DB  15,40,223                           ; movaps        %xmm7,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_clear_ssse3_lowp
+_sk_clear_ssse3_lowp LABEL PROC
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  15,87,192                           ; xorps         %xmm0,%xmm0
+  DB  15,87,201                           ; xorps         %xmm1,%xmm1
+  DB  15,87,210                           ; xorps         %xmm2,%xmm2
+  DB  15,87,219                           ; xorps         %xmm3,%xmm3
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcatop_ssse3_lowp
+_sk_srcatop_ssse3_lowp LABEL PROC
+  DB  102,15,56,11,199                    ; pmulhrsw      %xmm7,%xmm0
+  DB  102,68,15,56,29,192                 ; pabsw         %xmm0,%xmm8
+  DB  102,68,15,111,13,193,5,0,0          ; movdqa        0x5c1(%rip),%xmm9        # 1320 <_sk_xor__ssse3_lowp+0x173>
+  DB  102,68,15,249,203                   ; psubw         %xmm3,%xmm9
+  DB  102,15,111,196                      ; movdqa        %xmm4,%xmm0
+  DB  102,65,15,56,11,193                 ; pmulhrsw      %xmm9,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,253,192                   ; paddw         %xmm8,%xmm0
+  DB  102,15,56,11,207                    ; pmulhrsw      %xmm7,%xmm1
+  DB  102,68,15,56,29,193                 ; pabsw         %xmm1,%xmm8
+  DB  102,15,111,205                      ; movdqa        %xmm5,%xmm1
+  DB  102,65,15,56,11,201                 ; pmulhrsw      %xmm9,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,253,200                   ; paddw         %xmm8,%xmm1
+  DB  102,15,56,11,215                    ; pmulhrsw      %xmm7,%xmm2
+  DB  102,68,15,56,29,194                 ; pabsw         %xmm2,%xmm8
+  DB  102,15,111,214                      ; movdqa        %xmm6,%xmm2
+  DB  102,65,15,56,11,209                 ; pmulhrsw      %xmm9,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,253,208                   ; paddw         %xmm8,%xmm2
+  DB  102,15,56,11,223                    ; pmulhrsw      %xmm7,%xmm3
+  DB  102,68,15,56,29,195                 ; pabsw         %xmm3,%xmm8
+  DB  102,68,15,56,11,207                 ; pmulhrsw      %xmm7,%xmm9
+  DB  102,65,15,56,29,217                 ; pabsw         %xmm9,%xmm3
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstatop_ssse3_lowp
+_sk_dstatop_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,196                   ; movdqa        %xmm4,%xmm8
+  DB  102,68,15,56,11,195                 ; pmulhrsw      %xmm3,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,68,15,111,13,64,5,0,0           ; movdqa        0x540(%rip),%xmm9        # 1330 <_sk_xor__ssse3_lowp+0x183>
+  DB  102,68,15,249,207                   ; psubw         %xmm7,%xmm9
+  DB  102,65,15,56,11,193                 ; pmulhrsw      %xmm9,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,253,192                   ; paddw         %xmm8,%xmm0
+  DB  102,68,15,111,197                   ; movdqa        %xmm5,%xmm8
+  DB  102,68,15,56,11,195                 ; pmulhrsw      %xmm3,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,65,15,56,11,201                 ; pmulhrsw      %xmm9,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,253,200                   ; paddw         %xmm8,%xmm1
+  DB  102,68,15,111,198                   ; movdqa        %xmm6,%xmm8
+  DB  102,68,15,56,11,195                 ; pmulhrsw      %xmm3,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,65,15,56,11,209                 ; pmulhrsw      %xmm9,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,253,208                   ; paddw         %xmm8,%xmm2
+  DB  102,68,15,111,199                   ; movdqa        %xmm7,%xmm8
+  DB  102,68,15,56,11,195                 ; pmulhrsw      %xmm3,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,68,15,56,11,203                 ; pmulhrsw      %xmm3,%xmm9
+  DB  102,65,15,56,29,217                 ; pabsw         %xmm9,%xmm3
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcin_ssse3_lowp
+_sk_srcin_ssse3_lowp LABEL PROC
+  DB  102,15,56,11,199                    ; pmulhrsw      %xmm7,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,15,56,11,207                    ; pmulhrsw      %xmm7,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,15,56,11,215                    ; pmulhrsw      %xmm7,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,15,56,11,223                    ; pmulhrsw      %xmm7,%xmm3
+  DB  102,15,56,29,219                    ; pabsw         %xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstin_ssse3_lowp
+_sk_dstin_ssse3_lowp LABEL PROC
+  DB  102,15,111,196                      ; movdqa        %xmm4,%xmm0
+  DB  102,15,56,11,195                    ; pmulhrsw      %xmm3,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,15,111,205                      ; movdqa        %xmm5,%xmm1
+  DB  102,15,56,11,203                    ; pmulhrsw      %xmm3,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,15,111,214                      ; movdqa        %xmm6,%xmm2
+  DB  102,15,56,11,211                    ; pmulhrsw      %xmm3,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,15,56,11,223                    ; pmulhrsw      %xmm7,%xmm3
+  DB  102,15,56,29,219                    ; pabsw         %xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcout_ssse3_lowp
+_sk_srcout_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,102,4,0,0           ; movdqa        0x466(%rip),%xmm8        # 1340 <_sk_xor__ssse3_lowp+0x193>
+  DB  102,68,15,249,199                   ; psubw         %xmm7,%xmm8
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,68,15,56,11,195                 ; pmulhrsw      %xmm3,%xmm8
+  DB  102,65,15,56,29,216                 ; pabsw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstout_ssse3_lowp
+_sk_dstout_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,55,4,0,0            ; movdqa        0x437(%rip),%xmm8        # 1350 <_sk_xor__ssse3_lowp+0x1a3>
+  DB  102,68,15,249,195                   ; psubw         %xmm3,%xmm8
+  DB  102,15,111,196                      ; movdqa        %xmm4,%xmm0
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,15,111,205                      ; movdqa        %xmm5,%xmm1
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,15,111,214                      ; movdqa        %xmm6,%xmm2
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,68,15,56,11,199                 ; pmulhrsw      %xmm7,%xmm8
+  DB  102,65,15,56,29,216                 ; pabsw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_srcover_ssse3_lowp
+_sk_srcover_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,252,3,0,0           ; movdqa        0x3fc(%rip),%xmm8        # 1360 <_sk_xor__ssse3_lowp+0x1b3>
+  DB  102,68,15,249,195                   ; psubw         %xmm3,%xmm8
+  DB  102,68,15,111,204                   ; movdqa        %xmm4,%xmm9
+  DB  102,69,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,65,15,253,193                   ; paddw         %xmm9,%xmm0
+  DB  102,68,15,111,205                   ; movdqa        %xmm5,%xmm9
+  DB  102,69,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,65,15,253,201                   ; paddw         %xmm9,%xmm1
+  DB  102,68,15,111,206                   ; movdqa        %xmm6,%xmm9
+  DB  102,69,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,65,15,253,209                   ; paddw         %xmm9,%xmm2
+  DB  102,68,15,56,11,199                 ; pmulhrsw      %xmm7,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_dstover_ssse3_lowp
+_sk_dstover_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,167,3,0,0           ; movdqa        0x3a7(%rip),%xmm8        # 1370 <_sk_xor__ssse3_lowp+0x1c3>
+  DB  102,68,15,249,199                   ; psubw         %xmm7,%xmm8
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,15,253,196                      ; paddw         %xmm4,%xmm0
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,15,253,205                      ; paddw         %xmm5,%xmm1
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,15,253,214                      ; paddw         %xmm6,%xmm2
+  DB  102,68,15,56,11,195                 ; pmulhrsw      %xmm3,%xmm8
+  DB  102,65,15,56,29,216                 ; pabsw         %xmm8,%xmm3
+  DB  102,15,253,223                      ; paddw         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_modulate_ssse3_lowp
+_sk_modulate_ssse3_lowp LABEL PROC
+  DB  102,15,56,11,196                    ; pmulhrsw      %xmm4,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,15,56,11,205                    ; pmulhrsw      %xmm5,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,15,56,11,214                    ; pmulhrsw      %xmm6,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,15,56,11,223                    ; pmulhrsw      %xmm7,%xmm3
+  DB  102,15,56,29,219                    ; pabsw         %xmm3,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_multiply_ssse3_lowp
+_sk_multiply_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,60,3,0,0            ; movdqa        0x33c(%rip),%xmm8        # 1380 <_sk_xor__ssse3_lowp+0x1d3>
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,68,15,249,207                   ; psubw         %xmm7,%xmm9
+  DB  102,68,15,111,208                   ; movdqa        %xmm0,%xmm10
+  DB  102,69,15,56,11,209                 ; pmulhrsw      %xmm9,%xmm10
+  DB  102,69,15,56,29,210                 ; pabsw         %xmm10,%xmm10
+  DB  102,68,15,249,195                   ; psubw         %xmm3,%xmm8
+  DB  102,15,56,11,196                    ; pmulhrsw      %xmm4,%xmm0
+  DB  102,68,15,111,220                   ; movdqa        %xmm4,%xmm11
+  DB  102,69,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm11
+  DB  102,69,15,56,29,219                 ; pabsw         %xmm11,%xmm11
+  DB  102,69,15,253,218                   ; paddw         %xmm10,%xmm11
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,253,195                   ; paddw         %xmm11,%xmm0
+  DB  102,68,15,111,209                   ; movdqa        %xmm1,%xmm10
+  DB  102,69,15,56,11,209                 ; pmulhrsw      %xmm9,%xmm10
+  DB  102,69,15,56,29,210                 ; pabsw         %xmm10,%xmm10
+  DB  102,15,56,11,205                    ; pmulhrsw      %xmm5,%xmm1
+  DB  102,68,15,111,221                   ; movdqa        %xmm5,%xmm11
+  DB  102,69,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm11
+  DB  102,69,15,56,29,219                 ; pabsw         %xmm11,%xmm11
+  DB  102,69,15,253,218                   ; paddw         %xmm10,%xmm11
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,253,203                   ; paddw         %xmm11,%xmm1
+  DB  102,68,15,111,210                   ; movdqa        %xmm2,%xmm10
+  DB  102,69,15,56,11,209                 ; pmulhrsw      %xmm9,%xmm10
+  DB  102,69,15,56,29,210                 ; pabsw         %xmm10,%xmm10
+  DB  102,15,56,11,214                    ; pmulhrsw      %xmm6,%xmm2
+  DB  102,68,15,111,222                   ; movdqa        %xmm6,%xmm11
+  DB  102,69,15,56,11,216                 ; pmulhrsw      %xmm8,%xmm11
+  DB  102,69,15,56,29,219                 ; pabsw         %xmm11,%xmm11
+  DB  102,69,15,253,218                   ; paddw         %xmm10,%xmm11
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,253,211                   ; paddw         %xmm11,%xmm2
+  DB  102,68,15,56,11,203                 ; pmulhrsw      %xmm3,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,68,15,56,11,199                 ; pmulhrsw      %xmm7,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,69,15,253,193                   ; paddw         %xmm9,%xmm8
+  DB  102,15,56,11,223                    ; pmulhrsw      %xmm7,%xmm3
+  DB  102,15,56,29,219                    ; pabsw         %xmm3,%xmm3
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_plus__ssse3_lowp
+_sk_plus__ssse3_lowp LABEL PROC
+  DB  102,15,253,196                      ; paddw         %xmm4,%xmm0
+  DB  102,15,253,205                      ; paddw         %xmm5,%xmm1
+  DB  102,15,253,214                      ; paddw         %xmm6,%xmm2
+  DB  102,15,253,223                      ; paddw         %xmm7,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_screen_ssse3_lowp
+_sk_screen_ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,78,2,0,0            ; movdqa        0x24e(%rip),%xmm8        # 1390 <_sk_xor__ssse3_lowp+0x1e3>
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,68,15,249,200                   ; psubw         %xmm0,%xmm9
+  DB  102,68,15,56,11,204                 ; pmulhrsw      %xmm4,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,65,15,253,193                   ; paddw         %xmm9,%xmm0
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,68,15,249,201                   ; psubw         %xmm1,%xmm9
+  DB  102,68,15,56,11,205                 ; pmulhrsw      %xmm5,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,65,15,253,201                   ; paddw         %xmm9,%xmm1
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,68,15,249,202                   ; psubw         %xmm2,%xmm9
+  DB  102,68,15,56,11,206                 ; pmulhrsw      %xmm6,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,65,15,253,209                   ; paddw         %xmm9,%xmm2
+  DB  102,68,15,249,195                   ; psubw         %xmm3,%xmm8
+  DB  102,68,15,56,11,199                 ; pmulhrsw      %xmm7,%xmm8
+  DB  102,69,15,56,29,192                 ; pabsw         %xmm8,%xmm8
+  DB  102,65,15,253,216                   ; paddw         %xmm8,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+PUBLIC _sk_xor__ssse3_lowp
+_sk_xor__ssse3_lowp LABEL PROC
+  DB  102,68,15,111,5,234,1,0,0           ; movdqa        0x1ea(%rip),%xmm8        # 13a0 <_sk_xor__ssse3_lowp+0x1f3>
+  DB  102,69,15,111,200                   ; movdqa        %xmm8,%xmm9
+  DB  102,68,15,249,207                   ; psubw         %xmm7,%xmm9
+  DB  102,65,15,56,11,193                 ; pmulhrsw      %xmm9,%xmm0
+  DB  102,68,15,56,29,208                 ; pabsw         %xmm0,%xmm10
+  DB  102,68,15,249,195                   ; psubw         %xmm3,%xmm8
+  DB  102,15,111,196                      ; movdqa        %xmm4,%xmm0
+  DB  102,65,15,56,11,192                 ; pmulhrsw      %xmm8,%xmm0
+  DB  102,15,56,29,192                    ; pabsw         %xmm0,%xmm0
+  DB  102,65,15,253,194                   ; paddw         %xmm10,%xmm0
+  DB  102,65,15,56,11,201                 ; pmulhrsw      %xmm9,%xmm1
+  DB  102,68,15,56,29,209                 ; pabsw         %xmm1,%xmm10
+  DB  102,15,111,205                      ; movdqa        %xmm5,%xmm1
+  DB  102,65,15,56,11,200                 ; pmulhrsw      %xmm8,%xmm1
+  DB  102,15,56,29,201                    ; pabsw         %xmm1,%xmm1
+  DB  102,65,15,253,202                   ; paddw         %xmm10,%xmm1
+  DB  102,65,15,56,11,209                 ; pmulhrsw      %xmm9,%xmm2
+  DB  102,68,15,56,29,210                 ; pabsw         %xmm2,%xmm10
+  DB  102,15,111,214                      ; movdqa        %xmm6,%xmm2
+  DB  102,65,15,56,11,208                 ; pmulhrsw      %xmm8,%xmm2
+  DB  102,15,56,29,210                    ; pabsw         %xmm2,%xmm2
+  DB  102,65,15,253,210                   ; paddw         %xmm10,%xmm2
+  DB  102,68,15,56,11,203                 ; pmulhrsw      %xmm3,%xmm9
+  DB  102,69,15,56,29,201                 ; pabsw         %xmm9,%xmm9
+  DB  102,68,15,56,11,199                 ; pmulhrsw      %xmm7,%xmm8
+  DB  102,65,15,56,29,216                 ; pabsw         %xmm8,%xmm3
+  DB  102,65,15,253,217                   ; paddw         %xmm9,%xmm3
+  DB  72,173                              ; lods          %ds:(%rsi),%rax
+  DB  255,224                             ; jmpq          *%rax
+
+ALIGN 16
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,67,0,0                          ; addb          $0x0,0x0(%rbx)
+  DB  128,67,0,0                          ; addb          $0x0,0x0(%rbx)
+  DB  128,67,0,0                          ; addb          $0x0,0x0(%rbx)
+  DB  128,67,0,4                          ; addb          $0x4,0x0(%rbx)
+  DB  8,12,1                              ; or            %cl,(%rcx,%rax,1)
+  DB  5,9,13,2,6                          ; add           $0x6020d09,%eax
+  DB  10,14                               ; or            (%rsi),%cl
+  DB  3,7                                 ; add           (%rdi),%eax
+  DB  11,15                               ; or            (%rdi),%ecx
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,129,128,0,128; addl          $0x80008081,-0x7f7e7f7f(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,4,8,12                      ; add           %al,0xc080400(%rax)
+  DB  1,5,9,13,2,6                        ; add           %eax,0x6020d09(%rip)        # 6021fc3 <_sk_xor__ssse3_lowp+0x6020e16>
+  DB  10,14                               ; or            (%rsi),%cl
+  DB  3,7                                 ; add           (%rdi),%eax
+  DB  11,15                               ; or            (%rdi),%ecx
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,0,128,0,128 ; addl          $0x80008000,-0x7f7e7f7f(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,0,128,0,128 ; addl          $0x80008000,-0x7f7e7f7f(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  129,128,129,128,129,128,129,128,129,128; addl          $0x80818081,-0x7f7e7f7f(%rax)
+  DB  129,128,129,128,129,128,0,128,0,128 ; addl          $0x80008000,-0x7f7e7f7f(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+  DB  0,128,0,128,0,128                   ; add           %al,-0x7fff8000(%rax)
+
+ALIGN 4
+  DB  0,0                                 ; add           %al,(%rax)
+  DB  128,67,0,0                          ; addb          $0x0,0x0(%rbx)
+  DB  128,67,0,0                          ; addb          $0x0,0x0(%rbx)
+  DB  128                                 ; .byte         0x80
+  DB  67                                  ; rex.XB
+ENDIF
+END
diff --git a/src/jumper/SkJumper_misc.h b/src/jumper/SkJumper_misc.h
new file mode 100644
index 0000000..8d7bfeb
--- /dev/null
+++ b/src/jumper/SkJumper_misc.h
@@ -0,0 +1,81 @@
+/*
+ * 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 SkJumper_misc_DEFINED
+#define SkJumper_misc_DEFINED
+
+#include "SkJumper.h"  // for memcpy()
+
+// Miscellany used by SkJumper_stages.cpp and SkJumper_vectors.h.
+
+// Every function in this file should be marked static and inline using SI.
+#if defined(JUMPER)
+    #define SI __attribute__((always_inline)) static inline
+#else
+    #define SI static inline
+#endif
+
+
+template <typename T, typename P>
+SI T unaligned_load(const P* p) {  // const void* would work too, but const P* helps ARMv7 codegen.
+    T v;
+    memcpy(&v, p, sizeof(v));
+    return v;
+}
+
+template <typename T, typename P>
+SI void unaligned_store(P* p, T v) {
+    memcpy(p, &v, sizeof(v));
+}
+
+template <typename Dst, typename Src>
+SI Dst bit_cast(const Src& src) {
+    static_assert(sizeof(Dst) == sizeof(Src), "");
+    return unaligned_load<Dst>(&src);
+}
+
+template <typename Dst, typename Src>
+SI Dst widen_cast(const Src& src) {
+    static_assert(sizeof(Dst) > sizeof(Src), "");
+    Dst dst;
+    memcpy(&dst, &src, sizeof(Src));
+    return dst;
+}
+
+// Our program is an array of void*, either
+//   - 1 void* per stage with no context pointer, the next stage;
+//   - 2 void* per stage with a context pointer, first the context pointer, then the next stage.
+
+// load_and_inc() steps the program forward by 1 void*, returning that pointer.
+SI void* load_and_inc(void**& program) {
+#if defined(__GNUC__) && defined(__x86_64__)
+    // If program is in %rsi (we try to make this likely) then this is a single instruction.
+    void* rax;
+    asm("lodsq" : "=a"(rax), "+S"(program));  // Write-only %rax, read-write %rsi.
+    return rax;
+#else
+    // On ARM *program++ compiles into pretty ideal code without any handholding.
+    return *program++;
+#endif
+}
+
+// LazyCtx doesn't do anything unless you call operator T*(), encapsulating the logic
+// from above that stages without a context pointer are represented by just 1 void*.
+struct LazyCtx {
+    void*   ptr;
+    void**& program;
+
+    explicit LazyCtx(void**& p) : ptr(nullptr), program(p) {}
+
+    template <typename T>
+    operator T*() {
+        if (!ptr) { ptr = load_and_inc(program); }
+        return (T*)ptr;
+    }
+};
+
+#endif//SkJumper_misc_DEFINED
diff --git a/src/jumper/SkJumper_stages.cpp b/src/jumper/SkJumper_stages.cpp
index 1d5337e..42f4b93 100644
--- a/src/jumper/SkJumper_stages.cpp
+++ b/src/jumper/SkJumper_stages.cpp
@@ -6,255 +6,88 @@
  */
 
 #include "SkJumper.h"
-#include <string.h>
+#include "SkJumper_misc.h"     // SI, unaligned_load(), bit_cast()
+#include "SkJumper_vectors.h"  // F, I32, U32, U16, U8, cast(), expand()
 
-#define SI static inline
-
-template <typename T, typename P>
-SI T unaligned_load(const P* p) {
-    T v;
-    memcpy(&v, p, sizeof(v));
-    return v;
-}
-
-template <typename Dst, typename Src>
-SI Dst bit_cast(const Src& src) {
-    static_assert(sizeof(Dst) == sizeof(Src), "");
-    return unaligned_load<Dst>(&src);
-}
-
-// A couple functions for embedding constants directly into code,
-// so that no .const or .literal4 section is created.
-SI int C(int x) {
-#if defined(JUMPER) && defined(__x86_64__)
-    // Move x-the-compile-time-constant as a literal into x-the-register.
-    asm("mov %1, %0" : "=r"(x) : "i"(x));
-#endif
-    return x;
-}
-SI float C(float f) {
-    int x = C(unaligned_load<int>(&f));
-    return unaligned_load<float>(&x);
-}
-SI int   operator "" _i(unsigned long long int i) { return C(  (int)i); }
-SI float operator "" _f(           long double f) { return C((float)f); }
-
-// Not all constants can be generated using C() or _i/_f.  We read the rest from this struct.
-using K = const SkJumper_constants;
-
-#if !defined(JUMPER)
-    // This path should lead to portable code that can be compiled directly into Skia.
-    // (All other paths are compiled offline by Clang into SkJumper_generated.h.)
-    #include <math.h>
-
-    using F   = float;
-    using I32 =  int32_t;
-    using U32 = uint32_t;
-    using U16 = uint16_t;
-    using U8  = uint8_t;
-
-    SI F   mad(F f, F m, F a)   { return f*m+a; }
-    SI F   min(F a, F b)        { return fminf(a,b); }
-    SI F   max(F a, F b)        { return fmaxf(a,b); }
-    SI F   abs_  (F v)          { return fabsf(v); }
-    SI F   floor_(F v)          { return floorf(v); }
-    SI F   rcp   (F v)          { return 1.0f / v; }
-    SI F   rsqrt (F v)          { return 1.0f / sqrtf(v); }
-    SI U32 round (F v, F scale) { return (uint32_t)lrintf(v*scale); }
-    SI U16 pack(U32 v)          { return (U16)v; }
-    SI U8  pack(U16 v)          { return  (U8)v; }
-
-    SI F if_then_else(I32 c, F t, F e) { return c ? t : e; }
-
-    SI F gather(const float* p, U32 ix) { return p[ix]; }
-
-    #define WRAP(name) sk_##name
-
-#elif defined(__aarch64__)
-    #include <arm_neon.h>
-
-    // Since we know we're using Clang, we can use its vector extensions.
-    using F   = float    __attribute__((ext_vector_type(4)));
-    using I32 =  int32_t __attribute__((ext_vector_type(4)));
-    using U32 = uint32_t __attribute__((ext_vector_type(4)));
-    using U16 = uint16_t __attribute__((ext_vector_type(4)));
-    using U8  = uint8_t  __attribute__((ext_vector_type(4)));
-
-    // We polyfill a few routines that Clang doesn't build into ext_vector_types.
-    SI F   mad(F f, F m, F a)                    { return vfmaq_f32(a,f,m);        }
-    SI F   min(F a, F b)                         { return vminq_f32(a,b);          }
-    SI F   max(F a, F b)                         { return vmaxq_f32(a,b);          }
-    SI F   abs_  (F v)                           { return vabsq_f32(v);            }
-    SI F   floor_(F v)                           { return vrndmq_f32(v);           }
-    SI F   rcp   (F v) { auto e = vrecpeq_f32 (v); return vrecpsq_f32 (v,e  ) * e; }
-    SI F   rsqrt (F v) { auto e = vrsqrteq_f32(v); return vrsqrtsq_f32(v,e*e) * e; }
-    SI U32 round (F v, F scale)                  { return vcvtnq_u32_f32(v*scale); }
-    SI U16 pack(U32 v)                           { return __builtin_convertvector(v, U16); }
-    SI U8  pack(U16 v)                           { return __builtin_convertvector(v,  U8); }
-
-    SI F if_then_else(I32 c, F t, F e) { return vbslq_f32((U32)c,t,e); }
-
-    SI F gather(const float* p, U32 ix) { return {p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]]}; }
-
-    #define WRAP(name) sk_##name##_aarch64
-
-#elif defined(__arm__)
-    #if defined(__thumb2__) || !defined(__ARM_ARCH_7A__) || !defined(__ARM_VFPV4__)
-        #error On ARMv7, compile with -march=armv7-a -mfpu=neon-vfp4, without -mthumb.
-    #endif
-    #include <arm_neon.h>
-
-    // We can pass {s0-s15} as arguments under AAPCS-VFP.  We'll slice that as 8 d-registers.
-    using F   = float    __attribute__((ext_vector_type(2)));
-    using I32 =  int32_t __attribute__((ext_vector_type(2)));
-    using U32 = uint32_t __attribute__((ext_vector_type(2)));
-    using U16 = uint16_t __attribute__((ext_vector_type(2)));
-    using U8  = uint8_t  __attribute__((ext_vector_type(2)));
-
-    SI F   mad(F f, F m, F a)                  { return vfma_f32(a,f,m);        }
-    SI F   min(F a, F b)                       { return vmin_f32(a,b);          }
-    SI F   max(F a, F b)                       { return vmax_f32(a,b);          }
-    SI F   abs_ (F v)                          { return vabs_f32(v);            }
-    SI F   rcp  (F v) { auto e = vrecpe_f32 (v); return vrecps_f32 (v,e  ) * e; }
-    SI F   rsqrt(F v) { auto e = vrsqrte_f32(v); return vrsqrts_f32(v,e*e) * e; }
-    SI U32 round(F v, F scale)                 { return vcvt_u32_f32(mad(v,scale,0.5f)); }
-    SI U16 pack(U32 v)                         { return __builtin_convertvector(v, U16); }
-    SI U8  pack(U16 v)                         { return __builtin_convertvector(v,  U8); }
-
-    SI F if_then_else(I32 c, F t, F e) { return vbsl_f32((U32)c,t,e); }
-
-    SI F floor_(F v) {
-        F roundtrip = vcvt_f32_s32(vcvt_s32_f32(v));
-        return roundtrip - if_then_else(roundtrip > v, 1.0_f, 0);
-    }
-
-    SI F gather(const float* p, U32 ix) { return {p[ix[0]], p[ix[1]]}; }
-
-    #define WRAP(name) sk_##name##_vfp4
-
-#elif defined(__AVX__)
-    #include <immintrin.h>
-
-    // These are __m256 and __m256i, but friendlier and strongly-typed.
-    using F   = float    __attribute__((ext_vector_type(8)));
-    using I32 =  int32_t __attribute__((ext_vector_type(8)));
-    using U32 = uint32_t __attribute__((ext_vector_type(8)));
-    using U16 = uint16_t __attribute__((ext_vector_type(8)));
-    using U8  = uint8_t  __attribute__((ext_vector_type(8)));
-
-    SI F mad(F f, F m, F a)  {
-    #if defined(__FMA__)
-        return _mm256_fmadd_ps(f,m,a);
-    #else
-        return f*m+a;
-    #endif
-    }
-
-    SI F   min(F a, F b)        { return _mm256_min_ps(a,b);    }
-    SI F   max(F a, F b)        { return _mm256_max_ps(a,b);    }
-    SI F   abs_  (F v)          { return _mm256_and_ps(v, 0-v); }
-    SI F   floor_(F v)          { return _mm256_floor_ps(v);    }
-    SI F   rcp   (F v)          { return _mm256_rcp_ps  (v);    }
-    SI F   rsqrt (F v)          { return _mm256_rsqrt_ps(v);    }
-    SI U32 round (F v, F scale) { return _mm256_cvtps_epi32(v*scale); }
-
-    SI U16 pack(U32 v) {
-        return _mm_packus_epi32(_mm256_extractf128_si256(v, 0),
-                                _mm256_extractf128_si256(v, 1));
-    }
-    SI U8 pack(U16 v) {
-        auto r = _mm_packus_epi16(v,v);
-        return unaligned_load<U8>(&r);
-    }
-
-    SI F if_then_else(I32 c, F t, F e) { return _mm256_blendv_ps(e,t,c); }
-
-    SI F gather(const float* p, U32 ix) {
-    #if defined(__AVX2__)
-        return _mm256_i32gather_ps(p, ix, 4);
-    #else
-        return { p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]],
-                 p[ix[4]], p[ix[5]], p[ix[6]], p[ix[7]], };
-    #endif
-    }
-
-    #if defined(__AVX2__) && defined(__F16C__) && defined(__FMA__)
-        #define WRAP(name) sk_##name##_hsw
-    #else
-        #define WRAP(name) sk_##name##_avx
-    #endif
-
-#elif defined(__SSE2__)
-    #include <immintrin.h>
-
-    using F   = float    __attribute__((ext_vector_type(4)));
-    using I32 =  int32_t __attribute__((ext_vector_type(4)));
-    using U32 = uint32_t __attribute__((ext_vector_type(4)));
-    using U16 = uint16_t __attribute__((ext_vector_type(4)));
-    using U8  = uint8_t  __attribute__((ext_vector_type(4)));
-
-    SI F   mad(F f, F m, F a)  { return f*m+a;              }
-    SI F   min(F a, F b)       { return _mm_min_ps(a,b);    }
-    SI F   max(F a, F b)       { return _mm_max_ps(a,b);    }
-    SI F   abs_(F v)           { return _mm_and_ps(v, 0-v); }
-    SI F   rcp  (F v)          { return _mm_rcp_ps  (v);    }
-    SI F   rsqrt(F v)          { return _mm_rsqrt_ps(v);    }
-    SI U32 round(F v, F scale) { return _mm_cvtps_epi32(v*scale); }
-
-    SI U16 pack(U32 v) {
-    #if defined(__SSE4_1__)
-        auto p = _mm_packus_epi32(v,v);
-    #else
-        // Sign extend so that _mm_packs_epi32() does the pack we want.
-        auto p = _mm_srai_epi32(_mm_slli_epi32(v, 16), 16);
-        p = _mm_packs_epi32(p,p);
-    #endif
-        return unaligned_load<U16>(&p);  // We have two copies.  Return (the lower) one.
-    }
-    SI U8 pack(U16 v) {
-        __m128i r;
-        memcpy(&r, &v, sizeof(v));
-        r = _mm_packus_epi16(r,r);
-        return unaligned_load<U8>(&r);
-    }
-
-    SI F if_then_else(I32 c, F t, F e) {
-        return _mm_or_ps(_mm_and_ps(c, t), _mm_andnot_ps(c, e));
-    }
-
-    SI F floor_(F v) {
-    #if defined(__SSE4_1__)
-        return _mm_floor_ps(v);
-    #else
-        F roundtrip = _mm_cvtepi32_ps(_mm_cvttps_epi32(v));
-        return roundtrip - if_then_else(roundtrip > v, 1.0_f, 0);
-    #endif
-    }
-
-    SI F gather(const float* p, U32 ix) { return {p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]]}; }
-
-    #if defined(__SSE4_1__)
-        #define WRAP(name) sk_##name##_sse41
-    #else
-        #define WRAP(name) sk_##name##_sse2
-    #endif
-#endif
-
+// Our fundamental vector depth is our pixel stride.
 static const size_t kStride = sizeof(F) / sizeof(float);
 
-// We need to be a careful with casts.
-// (F)x means cast x to float in the portable path, but bit_cast x to float in the others.
-// These named casts and bit_cast() are always what they seem to be.
-#if defined(JUMPER)
-    SI F   cast  (U32 v) { return __builtin_convertvector((I32)v, F);   }
-    SI U32 expand(U16 v) { return __builtin_convertvector(     v, U32); }
-    SI U32 expand(U8  v) { return __builtin_convertvector(     v, U32); }
-#else
-    SI F   cast  (U32 v) { return   (F)v; }
-    SI U32 expand(U16 v) { return (U32)v; }
-    SI U32 expand(U8  v) { return (U32)v; }
+// A reminder:
+// Code guarded by defined(JUMPER) can assume that it will be compiled by Clang
+// and that F, I32, etc. are kStride-deep ext_vector_types of the appropriate type.
+// Otherwise, F, I32, etc. just alias the basic scalar types (and so kStride == 1).
+
+// You can use most constants in this file, but in a few rare exceptions we read from this struct.
+using K = const SkJumper_constants;
+
+// A little wrapper macro to name Stages differently depending on the instruction set.
+// That lets us link together several options.
+#if !defined(JUMPER)
+    #define WRAP(name) sk_##name
+#elif defined(__aarch64__)
+    #define WRAP(name) sk_##name##_aarch64
+#elif defined(__arm__)
+    #define WRAP(name) sk_##name##_vfp4
+#elif defined(__AVX2__)
+    #define WRAP(name) sk_##name##_hsw
+#elif defined(__AVX__)
+    #define WRAP(name) sk_##name##_avx
+#elif defined(__SSE4_1__)
+    #define WRAP(name) sk_##name##_sse41
+#elif defined(__SSE2__)
+    #define WRAP(name) sk_##name##_sse2
 #endif
 
+// We're finally going to get to what a Stage function looks like!
+//    tail == 0 ~~> work on a full kStride pixels
+//    tail != 0 ~~> work on only the first tail pixels
+// tail is always < kStride.
+//
+// We keep program the second argument, so that it's passed in rsi for load_and_inc().
+using Stage = void(K* k, void** program, size_t x, size_t y, size_t tail, F,F,F,F, F,F,F,F);
+
+#if defined(JUMPER) && defined(__AVX__)
+    // We really want to make sure all paths go through this function's (implicit) vzeroupper.
+    // If they don't, we'll experience severe slowdowns when we first use SSE instructions again.
+    __attribute__((disable_tail_calls))
+#endif
+MAYBE_MSABI
+extern "C" void WRAP(start_pipeline)(size_t x, size_t y, size_t limit, void** program, K* k) {
+    F v{};
+    auto start = (Stage*)load_and_inc(program);
+    while (x + kStride <= limit) {
+        start(k,program,x,y,0,    v,v,v,v, v,v,v,v);
+        x += kStride;
+    }
+    if (size_t tail = limit - x) {
+        start(k,program,x,y,tail, v,v,v,v, v,v,v,v);
+    }
+}
+
+#define STAGE(name)                                                                   \
+    SI void name##_k(K* k, LazyCtx ctx, size_t x, size_t y, size_t tail,              \
+                     F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da);             \
+    extern "C" void WRAP(name)(K* k, void** program, size_t x, size_t y, size_t tail, \
+                               F r, F g, F b, F a, F dr, F dg, F db, F da) {          \
+        LazyCtx ctx(program);                                                         \
+        name##_k(k,ctx,x,y,tail, r,g,b,a, dr,dg,db,da);                               \
+        auto next = (Stage*)load_and_inc(program);                                    \
+        next(k,program,x,y,tail, r,g,b,a, dr,dg,db,da);                               \
+    }                                                                                 \
+    SI void name##_k(K* k, LazyCtx ctx, size_t x, size_t y, size_t tail,              \
+                     F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da)
+
+
+// just_return() is a simple no-op stage that only exists to end the chain,
+// returning back up to start_pipeline(), and from there to the caller.
+extern "C" void WRAP(just_return)(K*, void**, size_t,size_t,size_t, F,F,F,F, F,F,F,F) {}
+
+
+// We could start defining normal Stages now.  But first, some helper functions.
+
+// These load() and store() methods are tail-aware,
+// but focus mainly on keeping the at-stride tail==0 case fast.
+
 template <typename V, typename T>
 SI V load(const T* src, size_t tail) {
 #if defined(JUMPER)
@@ -293,10 +126,12 @@
         return;
     }
 #endif
-    memcpy(dst, &v, sizeof(v));
+    unaligned_store(dst, v);
 }
 
-#if 1 && defined(JUMPER) && defined(__AVX__)
+// This doesn't look strictly necessary, but without it Clang would generate load() using
+// compiler-generated constants that we can't support.  This version doesn't need constants.
+#if defined(JUMPER) && defined(__AVX__)
     template <>
     inline U8 load(const uint8_t* src, size_t tail) {
         if (__builtin_expect(tail, 0)) {
@@ -313,21 +148,26 @@
     }
 #endif
 
-#if 1 && defined(JUMPER) && defined(__AVX2__)
+// AVX adds some mask loads and stores that make for shorter, faster code.
+#if defined(JUMPER) && defined(__AVX__)
     SI U32 mask(size_t tail) {
+        // We go a little out of our way to avoid needing large constant values here.
+
         // It's easiest to build the mask as 8 8-bit values, either 0x00 or 0xff.
         // Start fully on, then shift away lanes from the top until we've got our mask.
         uint64_t mask = 0xffffffffffffffff >> 8*(kStride-tail);
 
         // Sign-extend each mask lane to its full width, 0x00000000 or 0xffffffff.
-        return _mm256_cvtepi8_epi32(_mm_cvtsi64_si128((int64_t)mask));
+        using S8  = int8_t  __attribute__((ext_vector_type(8)));
+        using S32 = int32_t __attribute__((ext_vector_type(8)));
+        return (U32)__builtin_convertvector(unaligned_load<S8>(&mask), S32);
     }
 
     template <>
     inline U32 load(const uint32_t* src, size_t tail) {
         __builtin_assume(tail < kStride);
         if (__builtin_expect(tail, 0)) {
-            return _mm256_maskload_epi32((const int*)src, mask(tail));
+            return (U32)_mm256_maskload_ps((const float*)src, mask(tail));
         }
         return unaligned_load<U32>(src);
     }
@@ -336,214 +176,323 @@
     inline void store(uint32_t* dst, U32 v, size_t tail) {
         __builtin_assume(tail < kStride);
         if (__builtin_expect(tail, 0)) {
-            return _mm256_maskstore_epi32((int*)dst, mask(tail), v);
+            return _mm256_maskstore_ps((float*)dst, mask(tail), (F)v);
         }
-        memcpy(dst, &v, sizeof(v));
+        unaligned_store(dst, v);
     }
 #endif
 
-
-SI F lerp(F from, F to, F t) {
-    return mad(to-from, t, from);
+SI F from_byte(U8 b) {
+    return cast(expand(b)) * (1/255.0f);
 }
-
 SI void from_565(U16 _565, F* r, F* g, F* b) {
     U32 wide = expand(_565);
-    *r = cast(wide & C(31<<11)) * C(1.0f / (31<<11));
-    *g = cast(wide & C(63<< 5)) * C(1.0f / (63<< 5));
-    *b = cast(wide & C(31<< 0)) * C(1.0f / (31<< 0));
+    *r = cast(wide & (31<<11)) * (1.0f / (31<<11));
+    *g = cast(wide & (63<< 5)) * (1.0f / (63<< 5));
+    *b = cast(wide & (31<< 0)) * (1.0f / (31<< 0));
+}
+SI void from_4444(U16 _4444, F* r, F* g, F* b, F* a) {
+    U32 wide = expand(_4444);
+    *r = cast(wide & (15<<12)) * (1.0f / (15<<12));
+    *g = cast(wide & (15<< 8)) * (1.0f / (15<< 8));
+    *b = cast(wide & (15<< 4)) * (1.0f / (15<< 4));
+    *a = cast(wide & (15<< 0)) * (1.0f / (15<< 0));
+}
+SI void from_8888(U32 _8888, F* r, F* g, F* b, F* a) {
+    *r = cast((_8888      ) & 0xff) * (1/255.0f);
+    *g = cast((_8888 >>  8) & 0xff) * (1/255.0f);
+    *b = cast((_8888 >> 16) & 0xff) * (1/255.0f);
+    *a = cast((_8888 >> 24)       ) * (1/255.0f);
 }
 
-// Sometimes we want to work with 4 floats directly, regardless of the depth of the F vector.
-#if defined(JUMPER)
-    using F4 = float __attribute__((ext_vector_type(4)));
-#else
-    struct F4 {
-        float vals[4];
-        float operator[](int i) const { return vals[i]; }
-    };
-#endif
-
-SI void* load_and_inc(void**& program) {
-#if defined(__GNUC__) && defined(__x86_64__)
-    // Passing program as the second Stage argument makes it likely that it's in %rsi,
-    // so this is usually a single instruction *program++.
-    void* rax;
-    asm("lodsq" : "=a"(rax), "+S"(program));  // Write-only %rax, read-write %rsi.
-    return rax;
-    // When a Stage uses its ctx pointer, this optimization typically cuts an instruction:
-    //    mov    (%rsi), %rcx     // ctx  = program[0]
-    //    ...
-    //    mov 0x8(%rsi), %rax     // next = program[1]
-    //    add $0x10, %rsi         // program += 2
-    //    jmpq *%rax              // JUMP!
-    // becomes
-    //    lods   %ds:(%rsi),%rax  // ctx  = *program++;
-    //    ...
-    //    lods   %ds:(%rsi),%rax  // next = *program++;
-    //    jmpq *%rax              // JUMP!
-    //
-    // When a Stage doesn't use its ctx pointer, it's 3 instructions either way,
-    // but using lodsq (a 2-byte instruction) tends to trim a few bytes.
-#else
-    // On ARM *program++ compiles into a single instruction without any handholding.
-    return *program++;
-#endif
+template <typename T>
+SI U32 ix_and_ptr(T** ptr, const SkJumper_GatherCtx* ctx, F x, F y) {
+    *ptr = (const T*)ctx->pixels;
+    return trunc_(y)*ctx->stride + trunc_(x);
 }
 
-// Doesn't do anything unless you resolve it, either by casting to a pointer or calling load().
-// This makes it free in stages that have no context pointer to load (i.e. built with nullptr).
-struct LazyCtx {
-    void*   ptr;
-    void**& program;
-
-    explicit LazyCtx(void**& p) : ptr(nullptr), program(p) {}
-
-    template <typename T>
-    operator T*() {
-        if (!ptr) { ptr = load_and_inc(program); }
-        return (T*)ptr;
-    }
-
-    template <typename T>
-    T load() {
-        if (!ptr) { ptr = load_and_inc(program); }
-        return unaligned_load<T>(ptr);
-    }
-};
-
-#if defined(JUMPER) && defined(__AVX__)
-    // There's a big cost to switch between SSE and AVX+, so we do a little
-    // extra work to handle even the jagged <kStride tail in AVX+ mode.
-    using Stage = void(size_t x, void** program, K* k, size_t tail, F,F,F,F, F,F,F,F);
-
-    #if defined(JUMPER) && defined(WIN)
-    __attribute__((ms_abi))
-    #endif
-    extern "C" size_t WRAP(start_pipeline)(size_t x, void** program, K* k, size_t limit) {
-        F v{};
-        auto start = (Stage*)load_and_inc(program);
-        while (x + kStride <= limit) {
-            start(x,program,k,0,    v,v,v,v, v,v,v,v);
-            x += kStride;
-        }
-        if (size_t tail = limit - x) {
-            start(x,program,k,tail, v,v,v,v, v,v,v,v);
-        }
-        return limit;
-    }
-
-    #define STAGE(name)                                                           \
-        SI void name##_k(size_t x, LazyCtx ctx, K* k, size_t tail,                \
-                         F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da);     \
-        extern "C" void WRAP(name)(size_t x, void** program, K* k, size_t tail,   \
-                                   F r, F g, F b, F a, F dr, F dg, F db, F da) {  \
-            LazyCtx ctx(program);                                                 \
-            name##_k(x,ctx,k,tail, r,g,b,a, dr,dg,db,da);                         \
-            auto next = (Stage*)load_and_inc(program);                            \
-            next(x,program,k,tail, r,g,b,a, dr,dg,db,da);                         \
-        }                                                                         \
-        SI void name##_k(size_t x, LazyCtx ctx, K* k, size_t tail,                \
-                         F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da)
-
-#else
-    // Other instruction sets (SSE, NEON, portable) can fall back on narrower
-    // pipelines cheaply, which frees us to always assume tail==0.
-
-    // Stages tail call between each other by following program,
-    // an interlaced sequence of Stage pointers and context pointers.
-    using Stage = void(size_t x, void** program, K* k, F,F,F,F, F,F,F,F);
-
-    #if defined(JUMPER) && defined(WIN)
-    __attribute__((ms_abi))
-    #endif
-    extern "C" size_t WRAP(start_pipeline)(size_t x, void** program, K* k, size_t limit) {
-        F v{};
-        auto start = (Stage*)load_and_inc(program);
-        while (x + kStride <= limit) {
-            start(x,program,k, v,v,v,v, v,v,v,v);
-            x += kStride;
-        }
-        return x;
-    }
-
-    #define STAGE(name)                                                           \
-        SI void name##_k(size_t x, LazyCtx ctx, K* k, size_t tail,                \
-                         F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da);     \
-        extern "C" void WRAP(name)(size_t x, void** program, K* k,                \
-                                   F r, F g, F b, F a, F dr, F dg, F db, F da) {  \
-            LazyCtx ctx(program);                                                 \
-            name##_k(x,ctx,k,0, r,g,b,a, dr,dg,db,da);                            \
-            auto next = (Stage*)load_and_inc(program);                            \
-            next(x,program,k, r,g,b,a, dr,dg,db,da);                              \
-        }                                                                         \
-        SI void name##_k(size_t x, LazyCtx ctx, K* k, size_t tail,                \
-                         F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da)
-#endif
-
-// Ends the chain of tail calls, returning back up to start_pipeline (and from there to the caller).
-extern "C" void WRAP(just_return)(size_t, void**, K*, F,F,F,F, F,F,F,F) {}
-
-// We can now define Stages!
-
-// Some things to keep in mind while writing Stages:
-//   - do not branch;                                           (i.e. avoid jmp)
-//   - do not call functions that don't inline;                 (i.e. avoid call, ret)
-//   - do not use constant literals other than 0, ~0 and 0.0f.  (i.e. avoid rip relative addressing)
-//
-// Some things that should work fine:
-//   - 0, ~0, and 0.0f;
-//   - arithmetic;
-//   - functions of F and U32 that we've defined above;
-//   - temporary values;
-//   - lambdas;
-//   - memcpy() with a compile-time constant size argument.
+// Now finally, normal Stages!
 
 STAGE(seed_shader) {
-    auto y = *(const int*)ctx;
-
     // It's important for speed to explicitly cast(x) and cast(y),
     // which has the effect of splatting them to vectors before converting to floats.
     // On Intel this breaks a data dependency on previous loop iterations' registers.
-    r = cast(x) + 0.5_f + unaligned_load<F>(k->iota);
-    g = cast(y) + 0.5_f;
-    b = 1.0_f;
+    r = cast(x) + 0.5f + unaligned_load<F>(k->iota_F);
+    g = cast(y) + 0.5f;
+    b = 1.0f;
     a = 0;
     dr = dg = db = da = 0;
 }
 
+STAGE(dither) {
+    auto rate = *(const float*)ctx;
+
+    // Get [(x,y), (x+1,y), (x+2,y), ...] loaded up in integer vectors.
+    U32 X = x + unaligned_load<U32>(k->iota_U32),
+        Y = y;
+
+    // We're doing 8x8 ordered dithering, see https://en.wikipedia.org/wiki/Ordered_dithering.
+    // In this case n=8 and we're using the matrix that looks like 1/64 x [ 0 48 12 60 ... ].
+
+    // We only need X and X^Y from here on, so it's easier to just think of that as "Y".
+    Y ^= X;
+
+    // We'll mix the bottom 3 bits of each of X and Y to make 6 bits,
+    // for 2^6 == 64 == 8x8 matrix values.  If X=abc and Y=def, we make fcebda.
+    U32 M = (Y & 1) << 5 | (X & 1) << 4
+          | (Y & 2) << 2 | (X & 2) << 1
+          | (Y & 4) >> 1 | (X & 4) >> 2;
+
+    // Scale that dither to [0,1), then (-0.5,+0.5), here using 63/128 = 0.4921875 as 0.5-epsilon.
+    // We want to make sure our dither is less than 0.5 in either direction to keep exact values
+    // like 0 and 1 unchanged after rounding.
+    F dither = cast(M) * (2/128.0f) - (63/128.0f);
+
+    r += rate*dither;
+    g += rate*dither;
+    b += rate*dither;
+
+    r = max(0, min(r, a));
+    g = max(0, min(g, a));
+    b = max(0, min(b, a));
+}
+
+// load 4 floats from memory, and splat them into r,g,b,a
 STAGE(constant_color) {
-    auto rgba = ctx.load<F4>();
+    auto rgba = (const float*)ctx;
     r = rgba[0];
     g = rgba[1];
     b = rgba[2];
     a = rgba[3];
 }
 
-STAGE(clear) {
-    r = g = b = a = 0;
+// load registers r,g,b,a from context (mirrors store_rgba)
+STAGE(load_rgba) {
+    auto ptr = (const float*)ctx;
+    r = unaligned_load<F>(ptr + 0*kStride);
+    g = unaligned_load<F>(ptr + 1*kStride);
+    b = unaligned_load<F>(ptr + 2*kStride);
+    a = unaligned_load<F>(ptr + 3*kStride);
 }
 
-STAGE(plus_) {
-    r = r + dr;
-    g = g + dg;
-    b = b + db;
-    a = a + da;
+// store registers r,g,b,a into context (mirrors load_rgba)
+STAGE(store_rgba) {
+    auto ptr = (float*)ctx;
+    unaligned_store(ptr + 0*kStride, r);
+    unaligned_store(ptr + 1*kStride, g);
+    unaligned_store(ptr + 2*kStride, b);
+    unaligned_store(ptr + 3*kStride, a);
 }
 
-STAGE(srcover) {
-    auto A = C(1.0f) - a;
-    r = mad(dr, A, r);
-    g = mad(dg, A, g);
-    b = mad(db, A, b);
-    a = mad(da, A, a);
+// Most blend modes apply the same logic to each channel.
+#define BLEND_MODE(name)                       \
+    SI F name##_channel(F s, F d, F sa, F da); \
+    STAGE(name) {                              \
+        r = name##_channel(r,dr,a,da);         \
+        g = name##_channel(g,dg,a,da);         \
+        b = name##_channel(b,db,a,da);         \
+        a = name##_channel(a,da,a,da);         \
+    }                                          \
+    SI F name##_channel(F s, F d, F sa, F da)
+
+SI F inv(F x) { return 1.0f - x; }
+SI F two(F x) { return x + x; }
+
+BLEND_MODE(clear)    { return 0; }
+BLEND_MODE(srcatop)  { return s*da + d*inv(sa); }
+BLEND_MODE(dstatop)  { return d*sa + s*inv(da); }
+BLEND_MODE(srcin)    { return s * da; }
+BLEND_MODE(dstin)    { return d * sa; }
+BLEND_MODE(srcout)   { return s * inv(da); }
+BLEND_MODE(dstout)   { return d * inv(sa); }
+BLEND_MODE(srcover)  { return mad(d, inv(sa), s); }
+BLEND_MODE(dstover)  { return mad(s, inv(da), d); }
+
+BLEND_MODE(modulate) { return s*d; }
+BLEND_MODE(multiply) { return s*inv(da) + d*inv(sa) + s*d; }
+BLEND_MODE(plus_)    { return s + d; }
+BLEND_MODE(screen)   { return s + d - s*d; }
+BLEND_MODE(xor_)     { return s*inv(da) + d*inv(sa); }
+#undef BLEND_MODE
+
+// Most other blend modes apply the same logic to colors, and srcover to alpha.
+#define BLEND_MODE(name)                       \
+    SI F name##_channel(F s, F d, F sa, F da); \
+    STAGE(name) {                              \
+        r = name##_channel(r,dr,a,da);         \
+        g = name##_channel(g,dg,a,da);         \
+        b = name##_channel(b,db,a,da);         \
+        a = mad(da, inv(a), a);                \
+    }                                          \
+    SI F name##_channel(F s, F d, F sa, F da)
+
+BLEND_MODE(darken)     { return s + d -     max(s*da, d*sa) ; }
+BLEND_MODE(lighten)    { return s + d -     min(s*da, d*sa) ; }
+BLEND_MODE(difference) { return s + d - two(min(s*da, d*sa)); }
+BLEND_MODE(exclusion)  { return s + d - two(s*d); }
+
+BLEND_MODE(colorburn) {
+    return if_then_else(d == da, d + s*inv(da),
+           if_then_else(s ==  0, s + d*inv(sa),
+                                 sa*(da - min(da, (da-d)*sa/s)) + s*inv(da) + d*inv(sa)));
 }
-STAGE(dstover) {
-    auto DA = 1.0_f - da;
-    r = mad(r, DA, dr);
-    g = mad(g, DA, dg);
-    b = mad(b, DA, db);
-    a = mad(a, DA, da);
+BLEND_MODE(colordodge) {
+    return if_then_else(d ==  0, d + s*inv(da),
+           if_then_else(s == sa, s + d*inv(sa),
+                                 sa*min(da, (d*sa)/(sa - s)) + s*inv(da) + d*inv(sa)));
+}
+BLEND_MODE(hardlight) {
+    return s*inv(da) + d*inv(sa)
+         + if_then_else(two(s) <= sa, two(s*d), sa*da - two((da-d)*(sa-s)));
+}
+BLEND_MODE(overlay) {
+    return s*inv(da) + d*inv(sa)
+         + if_then_else(two(d) <= da, two(s*d), sa*da - two((da-d)*(sa-s)));
+}
+
+BLEND_MODE(softlight) {
+    F m  = if_then_else(da > 0, d / da, 0),
+      s2 = two(s),
+      m4 = two(two(m));
+
+    // The logic forks three ways:
+    //    1. dark src?
+    //    2. light src, dark dst?
+    //    3. light src, light dst?
+    F darkSrc = d*(sa + (s2 - sa)*(1.0f - m)),     // Used in case 1.
+      darkDst = (m4*m4 + m4)*(m - 1.0f) + 7.0f*m,  // Used in case 2.
+      liteDst = rcp(rsqrt(m)) - m,                 // Used in case 3.
+      liteSrc = d*sa + da*(s2 - sa) * if_then_else(two(two(d)) <= da, darkDst, liteDst); // 2 or 3?
+    return s*inv(da) + d*inv(sa) + if_then_else(s2 <= sa, darkSrc, liteSrc);      // 1 or (2 or 3)?
+}
+#undef BLEND_MODE
+
+// We're basing our implemenation of non-separable blend modes on
+//   https://www.w3.org/TR/compositing-1/#blendingnonseparable.
+// and
+//   https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf
+// They're equivalent, but ES' math has been better simplified.
+//
+// Anything extra we add beyond that is to make the math work with premul inputs.
+
+SI F max(F r, F g, F b) { return max(r, max(g, b)); }
+SI F min(F r, F g, F b) { return min(r, min(g, b)); }
+
+SI F sat(F r, F g, F b) { return max(r,g,b) - min(r,g,b); }
+SI F lum(F r, F g, F b) { return r*0.30f + g*0.59f + b*0.11f; }
+
+SI void set_sat(F* r, F* g, F* b, F s) {
+    F mn  = min(*r,*g,*b),
+      mx  = max(*r,*g,*b),
+      sat = mx - mn;
+
+    // Map min channel to 0, max channel to s, and scale the middle proportionally.
+    auto scale = [=](F c) {
+        return if_then_else(sat == 0, 0, (c - mn) * s / sat);
+    };
+    *r = scale(*r);
+    *g = scale(*g);
+    *b = scale(*b);
+}
+SI void set_lum(F* r, F* g, F* b, F l) {
+    F diff = l - lum(*r, *g, *b);
+    *r += diff;
+    *g += diff;
+    *b += diff;
+}
+SI void clip_color(F* r, F* g, F* b, F a) {
+    F mn = min(*r, *g, *b),
+      mx = max(*r, *g, *b),
+      l  = lum(*r, *g, *b);
+
+    auto clip = [=](F c) {
+        c = if_then_else(mn >= 0, c, l + (c - l) * (    l) / (l - mn)   );
+        c = if_then_else(mx >  a,    l + (c - l) * (a - l) / (mx - l), c);
+        c = max(c, 0);  // Sometimes without this we may dip just a little negative.
+        return c;
+    };
+    *r = clip(*r);
+    *g = clip(*g);
+    *b = clip(*b);
+}
+
+STAGE(hue) {
+    F R = r*a,
+      G = g*a,
+      B = b*a;
+
+    set_sat(&R, &G, &B, sat(dr,dg,db)*a);
+    set_lum(&R, &G, &B, lum(dr,dg,db)*a);
+    clip_color(&R,&G,&B, a*da);
+
+    r = r*inv(da) + dr*inv(a) + R;
+    g = g*inv(da) + dg*inv(a) + G;
+    b = b*inv(da) + db*inv(a) + B;
+    a = a + da - a*da;
+}
+STAGE(saturation) {
+    F R = dr*a,
+      G = dg*a,
+      B = db*a;
+
+    set_sat(&R, &G, &B, sat( r, g, b)*da);
+    set_lum(&R, &G, &B, lum(dr,dg,db)* a);  // (This is not redundant.)
+    clip_color(&R,&G,&B, a*da);
+
+    r = r*inv(da) + dr*inv(a) + R;
+    g = g*inv(da) + dg*inv(a) + G;
+    b = b*inv(da) + db*inv(a) + B;
+    a = a + da - a*da;
+}
+STAGE(color) {
+    F R = r*da,
+      G = g*da,
+      B = b*da;
+
+    set_lum(&R, &G, &B, lum(dr,dg,db)*a);
+    clip_color(&R,&G,&B, a*da);
+
+    r = r*inv(da) + dr*inv(a) + R;
+    g = g*inv(da) + dg*inv(a) + G;
+    b = b*inv(da) + db*inv(a) + B;
+    a = a + da - a*da;
+}
+STAGE(luminosity) {
+    F R = dr*a,
+      G = dg*a,
+      B = db*a;
+
+    set_lum(&R, &G, &B, lum(r,g,b)*da);
+    clip_color(&R,&G,&B, a*da);
+
+    r = r*inv(da) + dr*inv(a) + R;
+    g = g*inv(da) + dg*inv(a) + G;
+    b = b*inv(da) + db*inv(a) + B;
+    a = a + da - a*da;
+}
+
+STAGE(srcover_rgba_8888) {
+    auto ptr = *(uint32_t**)ctx + x;
+
+    U32 dst = load<U32>(ptr, tail);
+    dr = cast((dst      ) & 0xff);
+    dg = cast((dst >>  8) & 0xff);
+    db = cast((dst >> 16) & 0xff);
+    da = cast((dst >> 24)       );
+    // {dr,dg,db,da} are in [0,255]
+    // { r, g, b, a} are in [0,  1]
+
+    r = mad(dr, inv(a), r*255.0f);
+    g = mad(dg, inv(a), g*255.0f);
+    b = mad(db, inv(a), b*255.0f);
+    a = mad(da, inv(a), a*255.0f);
+    // { r, g, b, a} are now in [0,255]
+
+    dst = round(r, 1.0f)
+        | round(g, 1.0f) <<  8
+        | round(b, 1.0f) << 16
+        | round(a, 1.0f) << 24;
+    store(ptr, dst, tail);
 }
 
 STAGE(clamp_0) {
@@ -554,14 +503,14 @@
 }
 
 STAGE(clamp_1) {
-    r = min(r, 1.0_f);
-    g = min(g, 1.0_f);
-    b = min(b, 1.0_f);
-    a = min(a, 1.0_f);
+    r = min(r, 1.0f);
+    g = min(g, 1.0f);
+    b = min(b, 1.0f);
+    a = min(a, 1.0f);
 }
 
 STAGE(clamp_a) {
-    a = min(a, 1.0_f);
+    a = min(a, 1.0f);
     r = min(r, a);
     g = min(g, a);
     b = min(b, a);
@@ -609,17 +558,17 @@
     b = b * a;
 }
 STAGE(unpremul) {
-    auto scale = if_then_else(a == 0, 0, 1.0_f / a);
-    r = r * scale;
-    g = g * scale;
-    b = b * scale;
+    auto scale = if_then_else(a == 0, 0, 1.0f / a);
+    r *= scale;
+    g *= scale;
+    b *= scale;
 }
 
 STAGE(from_srgb) {
     auto fn = [&](F s) {
-        auto lo = s * C(1/12.92f);
-        auto hi = mad(s*s, mad(s, 0.3000_f, 0.6975_f), 0.0025_f);
-        return if_then_else(s < 0.055_f, lo, hi);
+        auto lo = s * (1/12.92f);
+        auto hi = mad(s*s, mad(s, 0.3000f, 0.6975f), 0.0025f);
+        return if_then_else(s < 0.055f, lo, hi);
     };
     r = fn(r);
     g = fn(g);
@@ -627,18 +576,71 @@
 }
 STAGE(to_srgb) {
     auto fn = [&](F l) {
-        F sqrt = rcp  (rsqrt(l)),
-          ftrt = rsqrt(rsqrt(l));
-        auto lo = l * 12.46_f;
-        auto hi = min(1.0_f, mad(0.411192_f, ftrt,
-                             mad(0.689206_f, sqrt, -0.0988_f)));
-        return if_then_else(l < 0.0043_f, lo, hi);
+        // We tweak c and d for each instruction set to make sure fn(1) is exactly 1.
+    #if defined(JUMPER) && defined(__SSE2__)
+        const float c = 1.130048394203f,
+                    d = 0.141357362270f;
+    #elif defined(JUMPER) && (defined(__aarch64__) || defined(__arm__))
+        const float c = 1.129999995232f,
+                    d = 0.141381442547f;
+    #else
+        const float c = 1.129999995232f,
+                    d = 0.141377761960f;
+    #endif
+        F t = rsqrt(l);
+        auto lo = l * 12.92f;
+        auto hi = mad(t, mad(t, -0.0024542345f, 0.013832027f), c)
+                * rcp(d + t);
+        return if_then_else(l < 0.00465985f, lo, hi);
     };
     r = fn(r);
     g = fn(g);
     b = fn(b);
 }
 
+STAGE(rgb_to_hsl) {
+    F mx = max(max(r,g), b),
+      mn = min(min(r,g), b),
+      d = mx - mn,
+      d_rcp = 1.0f / d;
+
+    F h = (1/6.0f) *
+          if_then_else(mx == mn, 0,
+          if_then_else(mx ==  r, (g-b)*d_rcp + if_then_else(g < b, 6.0f, 0),
+          if_then_else(mx ==  g, (b-r)*d_rcp + 2.0f,
+                                 (r-g)*d_rcp + 4.0f)));
+
+    F l = (mx + mn) * 0.5f;
+    F s = if_then_else(mx == mn, 0,
+                       d / if_then_else(l > 0.5f, 2.0f-mx-mn, mx+mn));
+
+    r = h;
+    g = s;
+    b = l;
+}
+STAGE(hsl_to_rgb) {
+    F h = r,
+      s = g,
+      l = b;
+
+    F q = l + if_then_else(l >= 0.5f, s - l*s, l*s),
+      p = 2.0f*l - q;
+
+    auto hue_to_rgb = [&](F t) {
+        t = fract(t);
+
+        F r = p;
+        r = if_then_else(t >= 4/6.0f, r, p + (q-p)*(4.0f - 6.0f*t));
+        r = if_then_else(t >= 3/6.0f, r, q);
+        r = if_then_else(t >= 1/6.0f, r, p + (q-p)*(       6.0f*t));
+        return r;
+    };
+
+    r = if_then_else(s == 0, l, hue_to_rgb(h + (1/3.0f)));
+    g = if_then_else(s == 0, l, hue_to_rgb(h           ));
+    b = if_then_else(s == 0, l, hue_to_rgb(h - (1/3.0f)));
+}
+
 STAGE(scale_1_float) {
     auto c = *(const float*)ctx;
 
@@ -651,7 +653,7 @@
     auto ptr = *(const uint8_t**)ctx + x;
 
     auto scales = load<U8>(ptr, tail);
-    auto c = cast(expand(scales)) * C(1/255.0f);
+    auto c = from_byte(scales);
 
     r = r * c;
     g = g * c;
@@ -659,6 +661,10 @@
     a = a * c;
 }
 
+SI F lerp(F from, F to, F t) {
+    return mad(to-from, t, from);
+}
+
 STAGE(lerp_1_float) {
     auto c = *(const float*)ctx;
 
@@ -671,7 +677,7 @@
     auto ptr = *(const uint8_t**)ctx + x;
 
     auto scales = load<U8>(ptr, tail);
-    auto c = cast(expand(scales)) * C(1/255.0f);
+    auto c = from_byte(scales);
 
     r = lerp(dr, r, c);
     g = lerp(dg, g, c);
@@ -687,400 +693,280 @@
     r = lerp(dr, r, cr);
     g = lerp(dg, g, cg);
     b = lerp(db, b, cb);
-    a = 1.0_f;
+    a = max(lerp(da, a, cr), lerp(da, a, cg), lerp(da, a, cb));
 }
 
 STAGE(load_tables) {
-    struct Ctx {
-        const uint32_t* src;
-        const float *r, *g, *b;
-    };
-    auto c = (const Ctx*)ctx;
+    auto c = (const SkJumper_LoadTablesCtx*)ctx;
 
-    auto px = load<U32>(c->src + x, tail);
-    r = gather(c->r, (px      ) & 0xff_i);
-    g = gather(c->g, (px >>  8) & 0xff_i);
-    b = gather(c->b, (px >> 16) & 0xff_i);
-    a = cast(        (px >> 24)) * C(1/255.0f);
+    auto px = load<U32>((const uint32_t*)c->src + x, tail);
+    r = gather(c->r, (px      ) & 0xff);
+    g = gather(c->g, (px >>  8) & 0xff);
+    b = gather(c->b, (px >> 16) & 0xff);
+    a = cast(        (px >> 24)) * (1/255.0f);
+}
+STAGE(load_tables_u16_be) {
+    auto c = (const SkJumper_LoadTablesCtx*)ctx;
+    auto ptr = (const uint16_t*)c->src + 4*x;
+
+    U16 R,G,B,A;
+    load4(ptr, tail, &R,&G,&B,&A);
+
+    // c->src is big-endian, so & 0xff grabs the 8 most signficant bits.
+    r = gather(c->r, expand(R) & 0xff);
+    g = gather(c->g, expand(G) & 0xff);
+    b = gather(c->b, expand(B) & 0xff);
+    a = (1/65535.0f) * cast(expand(bswap(A)));
+}
+STAGE(load_tables_rgb_u16_be) {
+    auto c = (const SkJumper_LoadTablesCtx*)ctx;
+    auto ptr = (const uint16_t*)c->src + 3*x;
+
+    U16 R,G,B;
+    load3(ptr, tail, &R,&G,&B);
+
+    // c->src is big-endian, so & 0xff grabs the 8 most signficant bits.
+    r = gather(c->r, expand(R) & 0xff);
+    g = gather(c->g, expand(G) & 0xff);
+    b = gather(c->b, expand(B) & 0xff);
+    a = 1.0f;
+}
+
+STAGE(byte_tables) {
+    struct Tables { const uint8_t *r, *g, *b, *a; };
+    auto tables = (const Tables*)ctx;
+
+    r = from_byte(gather(tables->r, round(r, 255.0f)));
+    g = from_byte(gather(tables->g, round(g, 255.0f)));
+    b = from_byte(gather(tables->b, round(b, 255.0f)));
+    a = from_byte(gather(tables->a, round(a, 255.0f)));
+}
+
+STAGE(byte_tables_rgb) {
+    struct Tables { const uint8_t *r, *g, *b; int n; };
+    auto tables = (const Tables*)ctx;
+
+    F scale = tables->n - 1;
+    r = from_byte(gather(tables->r, round(r, scale)));
+    g = from_byte(gather(tables->g, round(g, scale)));
+    b = from_byte(gather(tables->b, round(b, scale)));
+}
+
+SI F table(F v, const SkJumper_TableCtx* ctx) {
+    return gather(ctx->table, round(v, ctx->size - 1));
+}
+STAGE(table_r) { r = table(r, ctx); }
+STAGE(table_g) { g = table(g, ctx); }
+STAGE(table_b) { b = table(b, ctx); }
+STAGE(table_a) { a = table(a, ctx); }
+
+SI F parametric(F v, const SkJumper_ParametricTransferFunction* ctx) {
+    F r = if_then_else(v <= ctx->D, mad(ctx->C, v, ctx->F)
+                                  , approx_powf(mad(ctx->A, v, ctx->B), ctx->G) + ctx->E);
+    return min(max(r, 0), 1.0f);  // Clamp to [0,1], with argument order mattering to handle NaN.
+}
+STAGE(parametric_r) { r = parametric(r, ctx); }
+STAGE(parametric_g) { g = parametric(g, ctx); }
+STAGE(parametric_b) { b = parametric(b, ctx); }
+STAGE(parametric_a) { a = parametric(a, ctx); }
+
+STAGE(lab_to_xyz) {
+    F L = r * 100.0f,
+      A = g * 255.0f - 128.0f,
+      B = b * 255.0f - 128.0f;
+
+    F Y = (L + 16.0f) * (1/116.0f),
+      X = Y + A*(1/500.0f),
+      Z = Y - B*(1/200.0f);
+
+    X = if_then_else(X*X*X > 0.008856f, X*X*X, (X - (16/116.0f)) * (1/7.787f));
+    Y = if_then_else(Y*Y*Y > 0.008856f, Y*Y*Y, (Y - (16/116.0f)) * (1/7.787f));
+    Z = if_then_else(Z*Z*Z > 0.008856f, Z*Z*Z, (Z - (16/116.0f)) * (1/7.787f));
+
+    // Adjust to D50 illuminant.
+    r = X * 0.96422f;
+    g = Y           ;
+    b = Z * 0.82521f;
 }
 
 STAGE(load_a8) {
     auto ptr = *(const uint8_t**)ctx + x;
 
     r = g = b = 0.0f;
-    a = cast(expand(load<U8>(ptr, tail))) * C(1/255.0f);
+    a = from_byte(load<U8>(ptr, tail));
+}
+STAGE(gather_a8) {
+    const uint8_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    r = g = b = 0.0f;
+    a = from_byte(gather(ptr, ix));
 }
 STAGE(store_a8) {
     auto ptr = *(uint8_t**)ctx + x;
 
-    U8 packed = pack(pack(round(a, 255.0_f)));
+    U8 packed = pack(pack(round(a, 255.0f)));
     store(ptr, packed, tail);
 }
 
+STAGE(load_g8) {
+    auto ptr = *(const uint8_t**)ctx + x;
+
+    r = g = b = from_byte(load<U8>(ptr, tail));
+    a = 1.0f;
+}
+STAGE(gather_g8) {
+    const uint8_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    r = g = b = from_byte(gather(ptr, ix));
+    a = 1.0f;
+}
+
+STAGE(gather_i8) {
+    auto c = (const SkJumper_GatherCtx*)ctx;
+    const uint8_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    ix = expand(gather(ptr, ix));
+    from_8888(gather(c->ctable, ix), &r,&g,&b,&a);
+}
+
 STAGE(load_565) {
     auto ptr = *(const uint16_t**)ctx + x;
 
     from_565(load<U16>(ptr, tail), &r,&g,&b);
-    a = 1.0_f;
+    a = 1.0f;
+}
+STAGE(gather_565) {
+    const uint16_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    from_565(gather(ptr, ix), &r,&g,&b);
+    a = 1.0f;
 }
 STAGE(store_565) {
     auto ptr = *(uint16_t**)ctx + x;
 
-    U16 px = pack( round(r, 31.0_f) << 11
-                 | round(g, 63.0_f) <<  5
-                 | round(b, 31.0_f)      );
+    U16 px = pack( round(r, 31.0f) << 11
+                 | round(g, 63.0f) <<  5
+                 | round(b, 31.0f)      );
+    store(ptr, px, tail);
+}
+
+STAGE(load_4444) {
+    auto ptr = *(const uint16_t**)ctx + x;
+    from_4444(load<U16>(ptr, tail), &r,&g,&b,&a);
+}
+STAGE(gather_4444) {
+    const uint16_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    from_4444(gather(ptr, ix), &r,&g,&b,&a);
+}
+STAGE(store_4444) {
+    auto ptr = *(uint16_t**)ctx + x;
+    U16 px = pack( round(r, 15.0f) << 12
+                 | round(g, 15.0f) <<  8
+                 | round(b, 15.0f) <<  4
+                 | round(a, 15.0f)      );
     store(ptr, px, tail);
 }
 
 STAGE(load_8888) {
     auto ptr = *(const uint32_t**)ctx + x;
-
-    auto px = load<U32>(ptr, tail);
-    r = cast((px      ) & 0xff_i) * C(1/255.0f);
-    g = cast((px >>  8) & 0xff_i) * C(1/255.0f);
-    b = cast((px >> 16) & 0xff_i) * C(1/255.0f);
-    a = cast((px >> 24)         ) * C(1/255.0f);
+    from_8888(load<U32>(ptr, tail), &r,&g,&b,&a);
 }
-
+STAGE(gather_8888) {
+    const uint32_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    from_8888(gather(ptr, ix), &r,&g,&b,&a);
+}
 STAGE(store_8888) {
     auto ptr = *(uint32_t**)ctx + x;
 
-    U32 px = round(r, 255.0_f)
-           | round(g, 255.0_f) <<  8
-           | round(b, 255.0_f) << 16
-           | round(a, 255.0_f) << 24;
+    U32 px = round(r, 255.0f)
+           | round(g, 255.0f) <<  8
+           | round(b, 255.0f) << 16
+           | round(a, 255.0f) << 24;
     store(ptr, px, tail);
 }
 
 STAGE(load_f16) {
     auto ptr = *(const uint64_t**)ctx + x;
 
-#if !defined(JUMPER)
-    auto half_to_float = [&](int16_t h) {
-        if (h < 0x0400) { h = 0; }            // Flush denorm and negative to zero.
-        return bit_cast<F>(h << 13)           // Line up the mantissa,
-             * bit_cast<F>(U32(0x77800000));  // then fix up the exponent.
-    };
-    auto rgba = (const int16_t*)ptr;
-    r = half_to_float(rgba[0]);
-    g = half_to_float(rgba[1]);
-    b = half_to_float(rgba[2]);
-    a = half_to_float(rgba[3]);
-#elif defined(__aarch64__)
-    auto halfs = vld4_f16((const float16_t*)ptr);
-    r = vcvt_f32_f16(halfs.val[0]);
-    g = vcvt_f32_f16(halfs.val[1]);
-    b = vcvt_f32_f16(halfs.val[2]);
-    a = vcvt_f32_f16(halfs.val[3]);
-#elif defined(__arm__)
-    auto rb_ga = vld2_f16((const float16_t*)ptr);
-    auto rb = vcvt_f32_f16(rb_ga.val[0]),
-         ga = vcvt_f32_f16(rb_ga.val[1]);
-    r = {rb[0], rb[2]};
-    g = {ga[0], ga[2]};
-    b = {rb[1], rb[3]};
-    a = {ga[1], ga[3]};
-#elif defined(__AVX2__) && defined(__FMA__) && defined(__F16C__)
-    __m128i _01, _23, _45, _67;
-    if (__builtin_expect(tail,0)) {
-        auto src = (const double*)ptr;
-        _01 = _23 = _45 = _67 = _mm_setzero_si128();
-        if (tail > 0) { _01 = _mm_loadl_pd(_01, src+0); }
-        if (tail > 1) { _01 = _mm_loadh_pd(_01, src+1); }
-        if (tail > 2) { _23 = _mm_loadl_pd(_23, src+2); }
-        if (tail > 3) { _23 = _mm_loadh_pd(_23, src+3); }
-        if (tail > 4) { _45 = _mm_loadl_pd(_45, src+4); }
-        if (tail > 5) { _45 = _mm_loadh_pd(_45, src+5); }
-        if (tail > 6) { _67 = _mm_loadl_pd(_67, src+6); }
-    } else {
-        _01 = _mm_loadu_si128(((__m128i*)ptr) + 0);
-        _23 = _mm_loadu_si128(((__m128i*)ptr) + 1);
-        _45 = _mm_loadu_si128(((__m128i*)ptr) + 2);
-        _67 = _mm_loadu_si128(((__m128i*)ptr) + 3);
-    }
-
-    auto _02 = _mm_unpacklo_epi16(_01, _23),  // r0 r2 g0 g2 b0 b2 a0 a2
-         _13 = _mm_unpackhi_epi16(_01, _23),  // r1 r3 g1 g3 b1 b3 a1 a3
-         _46 = _mm_unpacklo_epi16(_45, _67),
-         _57 = _mm_unpackhi_epi16(_45, _67);
-
-    auto rg0123 = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
-         ba0123 = _mm_unpackhi_epi16(_02, _13),  // b0 b1 b2 b3 a0 a1 a2 a3
-         rg4567 = _mm_unpacklo_epi16(_46, _57),
-         ba4567 = _mm_unpackhi_epi16(_46, _57);
-
-    r = _mm256_cvtph_ps(_mm_unpacklo_epi64(rg0123, rg4567));
-    g = _mm256_cvtph_ps(_mm_unpackhi_epi64(rg0123, rg4567));
-    b = _mm256_cvtph_ps(_mm_unpacklo_epi64(ba0123, ba4567));
-    a = _mm256_cvtph_ps(_mm_unpackhi_epi64(ba0123, ba4567));
-#elif defined(__AVX__)
-    __m128i _01, _23, _45, _67;
-    if (__builtin_expect(tail,0)) {
-        auto src = (const double*)ptr;
-        _01 = _23 = _45 = _67 = _mm_setzero_si128();
-        if (tail > 0) { _01 = _mm_loadl_pd(_01, src+0); }
-        if (tail > 1) { _01 = _mm_loadh_pd(_01, src+1); }
-        if (tail > 2) { _23 = _mm_loadl_pd(_23, src+2); }
-        if (tail > 3) { _23 = _mm_loadh_pd(_23, src+3); }
-        if (tail > 4) { _45 = _mm_loadl_pd(_45, src+4); }
-        if (tail > 5) { _45 = _mm_loadh_pd(_45, src+5); }
-        if (tail > 6) { _67 = _mm_loadl_pd(_67, src+6); }
-    } else {
-        _01 = _mm_loadu_si128(((__m128i*)ptr) + 0);
-        _23 = _mm_loadu_si128(((__m128i*)ptr) + 1);
-        _45 = _mm_loadu_si128(((__m128i*)ptr) + 2);
-        _67 = _mm_loadu_si128(((__m128i*)ptr) + 3);
-    }
-
-    auto _02 = _mm_unpacklo_epi16(_01, _23),  // r0 r2 g0 g2 b0 b2 a0 a2
-         _13 = _mm_unpackhi_epi16(_01, _23),  // r1 r3 g1 g3 b1 b3 a1 a3
-         _46 = _mm_unpacklo_epi16(_45, _67),
-         _57 = _mm_unpackhi_epi16(_45, _67);
-
-    auto rg0123 = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
-         ba0123 = _mm_unpackhi_epi16(_02, _13),  // b0 b1 b2 b3 a0 a1 a2 a3
-         rg4567 = _mm_unpacklo_epi16(_46, _57),
-         ba4567 = _mm_unpackhi_epi16(_46, _57);
-
-    // half_to_float() slows down ~10x for denorm inputs, so we flush them to zero.
-    // With a signed comparison this conveniently also flushes negative half floats to zero.
-    auto ftz = [](__m128i v) {
-        return _mm_andnot_si128(_mm_cmplt_epi16(v, _mm_set1_epi32(0x04000400_i)), v);
-    };
-    rg0123 = ftz(rg0123);
-    ba0123 = ftz(ba0123);
-    rg4567 = ftz(rg4567);
-    ba4567 = ftz(ba4567);
-
-    U32 R = _mm256_setr_m128i(_mm_unpacklo_epi16(rg0123, _mm_setzero_si128()),
-                              _mm_unpacklo_epi16(rg4567, _mm_setzero_si128())),
-        G = _mm256_setr_m128i(_mm_unpackhi_epi16(rg0123, _mm_setzero_si128()),
-                              _mm_unpackhi_epi16(rg4567, _mm_setzero_si128())),
-        B = _mm256_setr_m128i(_mm_unpacklo_epi16(ba0123, _mm_setzero_si128()),
-                              _mm_unpacklo_epi16(ba4567, _mm_setzero_si128())),
-        A = _mm256_setr_m128i(_mm_unpackhi_epi16(ba0123, _mm_setzero_si128()),
-                              _mm_unpackhi_epi16(ba4567, _mm_setzero_si128()));
-
-    auto half_to_float = [&](U32 h) {
-        return bit_cast<F>(h << 13)             // Line up the mantissa,
-             * bit_cast<F>(U32(0x77800000_i));  // then fix up the exponent.
-    };
-
-    r = half_to_float(R);
-    g = half_to_float(G);
-    b = half_to_float(B);
-    a = half_to_float(A);
-
-#elif defined(__SSE2__)
-    auto _01 = _mm_loadu_si128(((__m128i*)ptr) + 0),
-         _23 = _mm_loadu_si128(((__m128i*)ptr) + 1);
-
-    auto _02 = _mm_unpacklo_epi16(_01, _23),  // r0 r2 g0 g2 b0 b2 a0 a2
-         _13 = _mm_unpackhi_epi16(_01, _23);  // r1 r3 g1 g3 b1 b3 a1 a3
-
-    auto rg = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
-         ba = _mm_unpackhi_epi16(_02, _13);  // b0 b1 b2 b3 a0 a1 a2 a3
-
-    // Same deal as AVX, flush denorms and negatives to zero.
-    auto ftz = [](__m128i v) {
-        return _mm_andnot_si128(_mm_cmplt_epi16(v, _mm_set1_epi32(0x04000400_i)), v);
-    };
-    rg = ftz(rg);
-    ba = ftz(ba);
-
-    auto half_to_float = [&](U32 h) {
-        return bit_cast<F>(h << 13)             // Line up the mantissa,
-             * bit_cast<F>(U32(0x77800000_i));  // then fix up the exponent.
-    };
-
-    r = half_to_float(_mm_unpacklo_epi16(rg, _mm_setzero_si128()));
-    g = half_to_float(_mm_unpackhi_epi16(rg, _mm_setzero_si128()));
-    b = half_to_float(_mm_unpacklo_epi16(ba, _mm_setzero_si128()));
-    a = half_to_float(_mm_unpackhi_epi16(ba, _mm_setzero_si128()));
-#endif
+    U16 R,G,B,A;
+    load4((const uint16_t*)ptr,tail, &R,&G,&B,&A);
+    r = from_half(R);
+    g = from_half(G);
+    b = from_half(B);
+    a = from_half(A);
 }
+STAGE(gather_f16) {
+    const uint64_t* ptr;
+    U32 ix = ix_and_ptr(&ptr, ctx, r,g);
+    auto px = gather(ptr, ix);
 
+    U16 R,G,B,A;
+    load4((const uint16_t*)&px,0, &R,&G,&B,&A);
+    r = from_half(R);
+    g = from_half(G);
+    b = from_half(B);
+    a = from_half(A);
+}
 STAGE(store_f16) {
     auto ptr = *(uint64_t**)ctx + x;
-
-#if !defined(JUMPER)
-    auto float_to_half = [&](F f) {
-        return bit_cast<U32>(f * bit_cast<F>(U32(0x07800000_i)))  // Fix up the exponent,
-            >> 13;                                                // then line up the mantissa.
-    };
-    auto rgba = (int16_t*)ptr;
-    rgba[0] = float_to_half(r);
-    rgba[1] = float_to_half(g);
-    rgba[2] = float_to_half(b);
-    rgba[3] = float_to_half(a);
-#elif defined(__aarch64__)
-    float16x4x4_t halfs = {{
-        vcvt_f16_f32(r),
-        vcvt_f16_f32(g),
-        vcvt_f16_f32(b),
-        vcvt_f16_f32(a),
-    }};
-    vst4_f16((float16_t*)ptr, halfs);
-#elif defined(__arm__)
-    float16x4x2_t rb_ga = {{
-        vcvt_f16_f32(float32x4_t{r[0], b[0], r[1], b[1]}),
-        vcvt_f16_f32(float32x4_t{g[0], a[0], g[1], a[1]}),
-    }};
-    vst2_f16((float16_t*)ptr, rb_ga);
-#elif defined(__AVX2__) && defined(__FMA__) && defined(__F16C__)
-    auto R = _mm256_cvtps_ph(r, _MM_FROUND_CUR_DIRECTION),
-         G = _mm256_cvtps_ph(g, _MM_FROUND_CUR_DIRECTION),
-         B = _mm256_cvtps_ph(b, _MM_FROUND_CUR_DIRECTION),
-         A = _mm256_cvtps_ph(a, _MM_FROUND_CUR_DIRECTION);
-
-    auto rg0123 = _mm_unpacklo_epi16(R, G),  // r0 g0 r1 g1 r2 g2 r3 g3
-         rg4567 = _mm_unpackhi_epi16(R, G),  // r4 g4 r5 g5 r6 g6 r7 g7
-         ba0123 = _mm_unpacklo_epi16(B, A),
-         ba4567 = _mm_unpackhi_epi16(B, A);
-
-    auto _01 = _mm_unpacklo_epi32(rg0123, ba0123),
-         _23 = _mm_unpackhi_epi32(rg0123, ba0123),
-         _45 = _mm_unpacklo_epi32(rg4567, ba4567),
-         _67 = _mm_unpackhi_epi32(rg4567, ba4567);
-
-    if (__builtin_expect(tail,0)) {
-        auto dst = (double*)ptr;
-        if (tail > 0) { _mm_storel_pd(dst+0, _01); }
-        if (tail > 1) { _mm_storeh_pd(dst+1, _01); }
-        if (tail > 2) { _mm_storel_pd(dst+2, _23); }
-        if (tail > 3) { _mm_storeh_pd(dst+3, _23); }
-        if (tail > 4) { _mm_storel_pd(dst+4, _45); }
-        if (tail > 5) { _mm_storeh_pd(dst+5, _45); }
-        if (tail > 6) { _mm_storel_pd(dst+6, _67); }
-    } else {
-        _mm_storeu_si128((__m128i*)ptr + 0, _01);
-        _mm_storeu_si128((__m128i*)ptr + 1, _23);
-        _mm_storeu_si128((__m128i*)ptr + 2, _45);
-        _mm_storeu_si128((__m128i*)ptr + 3, _67);
-    }
-#elif defined(__AVX__)
-    auto float_to_half = [&](F f) {
-        return bit_cast<U32>(f * bit_cast<F>(U32(0x07800000_i)))  // Fix up the exponent,
-            >> 13;                                                // then line up the mantissa.
-    };
-    U32 R = float_to_half(r),
-        G = float_to_half(g),
-        B = float_to_half(b),
-        A = float_to_half(a);
-    auto r0123 = _mm256_extractf128_si256(R, 0),
-         r4567 = _mm256_extractf128_si256(R, 1),
-         g0123 = _mm256_extractf128_si256(G, 0),
-         g4567 = _mm256_extractf128_si256(G, 1),
-         b0123 = _mm256_extractf128_si256(B, 0),
-         b4567 = _mm256_extractf128_si256(B, 1),
-         a0123 = _mm256_extractf128_si256(A, 0),
-         a4567 = _mm256_extractf128_si256(A, 1);
-    auto rg0123 = r0123 | _mm_slli_si128(g0123,2),
-         rg4567 = r4567 | _mm_slli_si128(g4567,2),
-         ba0123 = b0123 | _mm_slli_si128(a0123,2),
-         ba4567 = b4567 | _mm_slli_si128(a4567,2);
-
-    auto _01 = _mm_unpacklo_epi32(rg0123, ba0123),
-         _23 = _mm_unpackhi_epi32(rg0123, ba0123),
-         _45 = _mm_unpacklo_epi32(rg4567, ba4567),
-         _67 = _mm_unpackhi_epi32(rg4567, ba4567);
-
-    if (__builtin_expect(tail,0)) {
-        auto dst = (double*)ptr;
-        if (tail > 0) { _mm_storel_pd(dst+0, _01); }
-        if (tail > 1) { _mm_storeh_pd(dst+1, _01); }
-        if (tail > 2) { _mm_storel_pd(dst+2, _23); }
-        if (tail > 3) { _mm_storeh_pd(dst+3, _23); }
-        if (tail > 4) { _mm_storel_pd(dst+4, _45); }
-        if (tail > 5) { _mm_storeh_pd(dst+5, _45); }
-        if (tail > 6) { _mm_storel_pd(dst+6, _67); }
-    } else {
-        _mm_storeu_si128((__m128i*)ptr + 0, _01);
-        _mm_storeu_si128((__m128i*)ptr + 1, _23);
-        _mm_storeu_si128((__m128i*)ptr + 2, _45);
-        _mm_storeu_si128((__m128i*)ptr + 3, _67);
-    }
-#elif defined(__SSE2__)
-    auto float_to_half = [&](F f) {
-        return bit_cast<U32>(f * bit_cast<F>(U32(0x07800000_i)))  // Fix up the exponent,
-            >> 13;                                                // then line up the mantissa.
-    };
-    U32 R = float_to_half(r),
-        G = float_to_half(g),
-        B = float_to_half(b),
-        A = float_to_half(a);
-    U32 rg = R | _mm_slli_si128(G,2),
-        ba = B | _mm_slli_si128(A,2);
-    _mm_storeu_si128((__m128i*)ptr + 0, _mm_unpacklo_epi32(rg, ba));
-    _mm_storeu_si128((__m128i*)ptr + 1, _mm_unpackhi_epi32(rg, ba));
-#endif
+    store4((uint16_t*)ptr,tail, to_half(r)
+                              , to_half(g)
+                              , to_half(b)
+                              , to_half(a));
 }
 
+STAGE(load_u16_be) {
+    auto ptr = *(const uint16_t**)ctx + 4*x;
+
+    U16 R,G,B,A;
+    load4(ptr,tail, &R,&G,&B,&A);
+
+    r = (1/65535.0f) * cast(expand(bswap(R)));
+    g = (1/65535.0f) * cast(expand(bswap(G)));
+    b = (1/65535.0f) * cast(expand(bswap(B)));
+    a = (1/65535.0f) * cast(expand(bswap(A)));
+}
+STAGE(load_rgb_u16_be) {
+    auto ptr = *(const uint16_t**)ctx + 3*x;
+
+    U16 R,G,B;
+    load3(ptr,tail, &R,&G,&B);
+
+    r = (1/65535.0f) * cast(expand(bswap(R)));
+    g = (1/65535.0f) * cast(expand(bswap(G)));
+    b = (1/65535.0f) * cast(expand(bswap(B)));
+    a = 1.0f;
+}
+STAGE(store_u16_be) {
+    auto ptr = *(uint16_t**)ctx + 4*x;
+
+    U16 R = bswap(pack(round(r, 65535.0f))),
+        G = bswap(pack(round(g, 65535.0f))),
+        B = bswap(pack(round(b, 65535.0f))),
+        A = bswap(pack(round(a, 65535.0f)));
+
+    store4(ptr,tail, R,G,B,A);
+}
+
+STAGE(load_f32) {
+    auto ptr = *(const float**)ctx + 4*x;
+    load4(ptr,tail, &r,&g,&b,&a);
+}
 STAGE(store_f32) {
     auto ptr = *(float**)ctx + 4*x;
-
-#if !defined(JUMPER)
-    ptr[0] = r;
-    ptr[1] = g;
-    ptr[2] = b;
-    ptr[3] = a;
-#elif defined(__aarch64__)
-    vst4q_f32(ptr, (float32x4x4_t{{r,g,b,a}}));
-#elif defined(__arm__)
-    vst4_f32(ptr, (float32x2x4_t{{r,g,b,a}}));
-#elif defined(__AVX__)
-    F rg0145 = _mm256_unpacklo_ps(r, g),  // r0 g0 r1 g1 | r4 g4 r5 g5
-      rg2367 = _mm256_unpackhi_ps(r, g),  // r2 ...      | r6 ...
-      ba0145 = _mm256_unpacklo_ps(b, a),  // b0 a0 b1 a1 | b4 a4 b5 a5
-      ba2367 = _mm256_unpackhi_ps(b, a);  // b2 ...      | b6 ...
-
-    F _04 = _mm256_unpacklo_pd(rg0145, ba0145),  // r0 g0 b0 a0 | r4 g4 b4 a4
-      _15 = _mm256_unpackhi_pd(rg0145, ba0145),  // r1 ...      | r5 ...
-      _26 = _mm256_unpacklo_pd(rg2367, ba2367),  // r2 ...      | r6 ...
-      _37 = _mm256_unpackhi_pd(rg2367, ba2367);  // r3 ...      | r7 ...
-
-    if (__builtin_expect(tail, 0)) {
-        if (tail > 0) { _mm_storeu_ps(ptr+ 0, _mm256_extractf128_ps(_04, 0)); }
-        if (tail > 1) { _mm_storeu_ps(ptr+ 4, _mm256_extractf128_ps(_15, 0)); }
-        if (tail > 2) { _mm_storeu_ps(ptr+ 8, _mm256_extractf128_ps(_26, 0)); }
-        if (tail > 3) { _mm_storeu_ps(ptr+12, _mm256_extractf128_ps(_37, 0)); }
-        if (tail > 4) { _mm_storeu_ps(ptr+16, _mm256_extractf128_ps(_04, 1)); }
-        if (tail > 5) { _mm_storeu_ps(ptr+20, _mm256_extractf128_ps(_15, 1)); }
-        if (tail > 6) { _mm_storeu_ps(ptr+24, _mm256_extractf128_ps(_26, 1)); }
-    } else {
-        F _01 = _mm256_permute2f128_ps(_04, _15, 32),  // 32 == 0010 0000 == lo, lo
-          _23 = _mm256_permute2f128_ps(_26, _37, 32),
-          _45 = _mm256_permute2f128_ps(_04, _15, 49),  // 49 == 0011 0001 == hi, hi
-          _67 = _mm256_permute2f128_ps(_26, _37, 49);
-        _mm256_storeu_ps(ptr+ 0, _01);
-        _mm256_storeu_ps(ptr+ 8, _23);
-        _mm256_storeu_ps(ptr+16, _45);
-        _mm256_storeu_ps(ptr+24, _67);
-    }
-#elif defined(__SSE2__)
-    auto v0 = r, v1 = g, v2 = b, v3 = a;
-    _MM_TRANSPOSE4_PS(v0, v1, v2, v3);
-    memcpy(ptr+ 0, &v0, sizeof(v0));
-    memcpy(ptr+ 4, &v1, sizeof(v1));
-    memcpy(ptr+ 8, &v2, sizeof(v2));
-    memcpy(ptr+12, &v3, sizeof(v3));
-#endif
+    store4(ptr,tail, r,g,b,a);
 }
 
-SI F ulp_before(F v) {
-    return bit_cast<F>(bit_cast<U32>(v) + U32(0xffffffff));
-}
 SI F clamp(F v, float limit) {
-    v = max(0, v);
-    return min(v, ulp_before(limit));
+    return min(max(0, v), limit);
 }
 SI F repeat(F v, float limit) {
-    v = v - floor_(v/limit)*limit;
-    return min(v, ulp_before(limit));
+    return v - floor_(v/limit)*limit;
 }
 SI F mirror(F v, float limit) {
-    v = abs_( (v-limit) - (limit+limit)*floor_((v-limit)/(limit+limit)) - limit );
-    return min(v, ulp_before(limit));
+    return abs_( (v-limit) - (limit+limit)*floor_((v-limit)/(limit+limit)) - limit );
 }
 STAGE(clamp_x)  { r = clamp (r, *(const float*)ctx); }
 STAGE(clamp_y)  { g = clamp (g, *(const float*)ctx); }
@@ -1089,8 +975,12 @@
 STAGE(mirror_x) { r = mirror(r, *(const float*)ctx); }
 STAGE(mirror_y) { g = mirror(g, *(const float*)ctx); }
 
+STAGE( clamp_x_1) { r = clamp (r, 1.0f); }
+STAGE(repeat_x_1) { r = repeat(r, 1.0f); }
+STAGE(mirror_x_1) { r = abs_( (r-1.0f) - two(floor_((r-1.0f)*0.5f)) - 1.0f ); }
+
 STAGE(luminance_to_alpha) {
-    a = r*0.2126_f + g*0.7152_f + b*0.0722_f;
+    a = r*0.2126f + g*0.7152f + b*0.0722f;
     r = g = b = 0;
 }
 
@@ -1124,6 +1014,16 @@
     b = B;
     a = A;
 }
+STAGE(matrix_4x3) {
+    auto m = (const float*)ctx;
+    auto X = r,
+         Y = g;
+
+    r = mad(X, m[0], mad(Y, m[4], m[ 8]));
+    g = mad(X, m[1], mad(Y, m[5], m[ 9]));
+    b = mad(X, m[2], mad(Y, m[6], m[10]));
+    a = mad(X, m[3], mad(Y, m[7], m[11]));
+}
 STAGE(matrix_perspective) {
     // N.B. Unlike the other matrix_ stages, this matrix is row-major.
     auto m = (const float*)ctx;
@@ -1135,13 +1035,231 @@
     g = G * rcp(Z);
 }
 
-STAGE(linear_gradient_2stops) {
-    struct Ctx { F4 c0, dc; };
-    auto c = ctx.load<Ctx>();
+SI void gradient_lookup(const SkJumper_GradientCtx* c, U32 idx, F t,
+                        F* r, F* g, F* b, F* a) {
+    F fr, br, fg, bg, fb, bb, fa, ba;
+#if defined(JUMPER) && defined(__AVX2__)
+    if (c->stopCount <=8) {
+        fr = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[0]), idx);
+        br = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[0]), idx);
+        fg = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[1]), idx);
+        bg = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[1]), idx);
+        fb = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[2]), idx);
+        bb = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[2]), idx);
+        fa = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->fs[3]), idx);
+        ba = _mm256_permutevar8x32_ps(_mm256_loadu_ps(c->bs[3]), idx);
+    } else
+#endif
+    {
+        fr = gather(c->fs[0], idx);
+        br = gather(c->bs[0], idx);
+        fg = gather(c->fs[1], idx);
+        bg = gather(c->bs[1], idx);
+        fb = gather(c->fs[2], idx);
+        bb = gather(c->bs[2], idx);
+        fa = gather(c->fs[3], idx);
+        ba = gather(c->bs[3], idx);
+    }
+
+    *r = mad(t, fr, br);
+    *g = mad(t, fg, bg);
+    *b = mad(t, fb, bb);
+    *a = mad(t, fa, ba);
+}
+
+STAGE(evenly_spaced_gradient) {
+    auto c = (const SkJumper_GradientCtx*)ctx;
+    auto t = r;
+    auto idx = trunc_(t * (c->stopCount-1));
+    gradient_lookup(c, idx, t, &r, &g, &b, &a);
+}
+
+STAGE(gauss_a_to_rgba) {
+    // x = 1 - x;
+    // exp(-x * x * 4) - 0.018f;
+    // ... now approximate with quartic
+    //
+    const float c4 = -2.26661229133605957031f;
+    const float c3 = 2.89795351028442382812f;
+    const float c2 = 0.21345567703247070312f;
+    const float c1 = 0.15489584207534790039f;
+    const float c0 = 0.00030726194381713867f;
+    a = mad(a, mad(a, mad(a, mad(a, c4, c3), c2), c1), c0);
+    r = a;
+    g = a;
+    b = a;
+}
+
+STAGE(gradient) {
+    auto c = (const SkJumper_GradientCtx*)ctx;
+    auto t = r;
+    U32 idx = 0;
+
+    // N.B. The loop starts at 1 because idx 0 is the color to use before the first stop.
+    for (size_t i = 1; i < c->stopCount; i++) {
+        idx += if_then_else(t >= c->ts[i], U32(1), U32(0));
+    }
+
+    gradient_lookup(c, idx, t, &r, &g, &b, &a);
+}
+
+STAGE(evenly_spaced_2_stop_gradient) {
+    struct Ctx { float f[4], b[4]; };
+    auto c = (const Ctx*)ctx;
 
     auto t = r;
-    r = mad(t, c.dc[0], c.c0[0]);
-    g = mad(t, c.dc[1], c.c0[1]);
-    b = mad(t, c.dc[2], c.c0[2]);
-    a = mad(t, c.dc[3], c.c0[3]);
+    r = mad(t, c->f[0], c->b[0]);
+    g = mad(t, c->f[1], c->b[1]);
+    b = mad(t, c->f[2], c->b[2]);
+    a = mad(t, c->f[3], c->b[3]);
+}
+
+STAGE(xy_to_unit_angle) {
+    F X = r,
+      Y = g;
+    F xabs = abs_(X),
+      yabs = abs_(Y);
+
+    F slope = min(xabs, yabs)/max(xabs, yabs);
+    F s = slope * slope;
+
+    // Use a 7th degree polynomial to approximate atan.
+    // This was generated using sollya.gforge.inria.fr.
+    // A float optimized polynomial was generated using the following command.
+    // P1 = fpminimax((1/(2*Pi))*atan(x),[|1,3,5,7|],[|24...|],[2^(-40),1],relative);
+    F phi = slope
+             * (0.15912117063999176025390625f     + s
+             * (-5.185396969318389892578125e-2f   + s
+             * (2.476101927459239959716796875e-2f + s
+             * (-7.0547382347285747528076171875e-3f))));
+
+    phi = if_then_else(xabs < yabs, 1.0f/4.0f - phi, phi);
+    phi = if_then_else(X < 0.0f   , 1.0f/2.0f - phi, phi);
+    phi = if_then_else(Y < 0.0f   , 1.0f - phi     , phi);
+    phi = if_then_else(phi != phi , 0              , phi);  // Check for NaN.
+    r = phi;
+}
+
+STAGE(xy_to_radius) {
+    F X2 = r * r,
+      Y2 = g * g;
+    r = sqrt_(X2 + Y2);
+}
+
+STAGE(save_xy) {
+    auto c = (SkJumper_SamplerCtx*)ctx;
+
+    // Whether bilinear or bicubic, all sample points are at the same fractional offset (fx,fy).
+    // They're either the 4 corners of a logical 1x1 pixel or the 16 corners of a 3x3 grid
+    // surrounding (x,y) at (0.5,0.5) off-center.
+    F fx = fract(r + 0.5f),
+      fy = fract(g + 0.5f);
+
+    // Samplers will need to load x and fx, or y and fy.
+    unaligned_store(c->x,  r);
+    unaligned_store(c->y,  g);
+    unaligned_store(c->fx, fx);
+    unaligned_store(c->fy, fy);
+}
+
+STAGE(accumulate) {
+    auto c = (const SkJumper_SamplerCtx*)ctx;
+
+    // Bilinear and bicubic filters are both separable, so we produce independent contributions
+    // from x and y, multiplying them together here to get each pixel's total scale factor.
+    auto scale = unaligned_load<F>(c->scalex)
+               * unaligned_load<F>(c->scaley);
+    dr = mad(scale, r, dr);
+    dg = mad(scale, g, dg);
+    db = mad(scale, b, db);
+    da = mad(scale, a, da);
+}
+
+// In bilinear interpolation, the 4 pixels at +/- 0.5 offsets from the sample pixel center
+// are combined in direct proportion to their area overlapping that logical query pixel.
+// At positive offsets, the x-axis contribution to that rectangle is fx, or (1-fx) at negative x.
+// The y-axis is symmetric.
+
+template <int kScale>
+SI void bilinear_x(SkJumper_SamplerCtx* ctx, F* x) {
+    *x = unaligned_load<F>(ctx->x) + (kScale * 0.5f);
+    F fx = unaligned_load<F>(ctx->fx);
+
+    F scalex;
+    if (kScale == -1) { scalex = 1.0f - fx; }
+    if (kScale == +1) { scalex =        fx; }
+    unaligned_store(ctx->scalex, scalex);
+}
+template <int kScale>
+SI void bilinear_y(SkJumper_SamplerCtx* ctx, F* y) {
+    *y = unaligned_load<F>(ctx->y) + (kScale * 0.5f);
+    F fy = unaligned_load<F>(ctx->fy);
+
+    F scaley;
+    if (kScale == -1) { scaley = 1.0f - fy; }
+    if (kScale == +1) { scaley =        fy; }
+    unaligned_store(ctx->scaley, scaley);
+}
+
+STAGE(bilinear_nx) { bilinear_x<-1>(ctx, &r); }
+STAGE(bilinear_px) { bilinear_x<+1>(ctx, &r); }
+STAGE(bilinear_ny) { bilinear_y<-1>(ctx, &g); }
+STAGE(bilinear_py) { bilinear_y<+1>(ctx, &g); }
+
+
+// In bicubic interpolation, the 16 pixels and +/- 0.5 and +/- 1.5 offsets from the sample
+// pixel center are combined with a non-uniform cubic filter, with higher values near the center.
+//
+// We break this function into two parts, one for near 0.5 offsets and one for far 1.5 offsets.
+// See GrCubicEffect for details of this particular filter.
+
+SI F bicubic_near(F t) {
+    // 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
+    return mad(t, mad(t, mad((-21/18.0f), t, (27/18.0f)), (9/18.0f)), (1/18.0f));
+}
+SI F bicubic_far(F t) {
+    // 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
+    return (t*t)*mad((7/18.0f), t, (-6/18.0f));
+}
+
+template <int kScale>
+SI void bicubic_x(SkJumper_SamplerCtx* ctx, F* x) {
+    *x = unaligned_load<F>(ctx->x) + (kScale * 0.5f);
+    F fx = unaligned_load<F>(ctx->fx);
+
+    F scalex;
+    if (kScale == -3) { scalex = bicubic_far (1.0f - fx); }
+    if (kScale == -1) { scalex = bicubic_near(1.0f - fx); }
+    if (kScale == +1) { scalex = bicubic_near(       fx); }
+    if (kScale == +3) { scalex = bicubic_far (       fx); }
+    unaligned_store(ctx->scalex, scalex);
+}
+template <int kScale>
+SI void bicubic_y(SkJumper_SamplerCtx* ctx, F* y) {
+    *y = unaligned_load<F>(ctx->y) + (kScale * 0.5f);
+    F fy = unaligned_load<F>(ctx->fy);
+
+    F scaley;
+    if (kScale == -3) { scaley = bicubic_far (1.0f - fy); }
+    if (kScale == -1) { scaley = bicubic_near(1.0f - fy); }
+    if (kScale == +1) { scaley = bicubic_near(       fy); }
+    if (kScale == +3) { scaley = bicubic_far (       fy); }
+    unaligned_store(ctx->scaley, scaley);
+}
+
+STAGE(bicubic_n3x) { bicubic_x<-3>(ctx, &r); }
+STAGE(bicubic_n1x) { bicubic_x<-1>(ctx, &r); }
+STAGE(bicubic_p1x) { bicubic_x<+1>(ctx, &r); }
+STAGE(bicubic_p3x) { bicubic_x<+3>(ctx, &r); }
+
+STAGE(bicubic_n3y) { bicubic_y<-3>(ctx, &g); }
+STAGE(bicubic_n1y) { bicubic_y<-1>(ctx, &g); }
+STAGE(bicubic_p1y) { bicubic_y<+1>(ctx, &g); }
+STAGE(bicubic_p3y) { bicubic_y<+3>(ctx, &g); }
+
+STAGE(callback) {
+    auto c = (SkJumper_CallbackCtx*)ctx;
+    store4(c->rgba,0, r,g,b,a);
+    c->fn(c, tail ? tail : kStride);
+    load4(c->read_from,0, &r,&g,&b,&a);
 }
diff --git a/src/jumper/SkJumper_stages_lowp.cpp b/src/jumper/SkJumper_stages_lowp.cpp
new file mode 100644
index 0000000..73b6def
--- /dev/null
+++ b/src/jumper/SkJumper_stages_lowp.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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 "SkJumper.h"
+#include "SkJumper_misc.h"
+#include <immintrin.h>
+
+#if !defined(__SSSE3__) || !defined(__clang__) || !defined(__x86_64__)
+    #error "We're starting with just SSSE3 x86-64 for now, and will always require Clang."
+#endif
+
+#define WRAP(name) sk_##name##_ssse3_lowp
+
+using K = const SkJumper_constants;
+static const size_t kStride = 8;
+
+template <typename T> using V = T __attribute__((ext_vector_type(8)));
+using U8  = V<uint8_t>;
+using U16 = V<uint16_t>;
+using U32 = V<uint32_t>;
+
+// See SkFixed15.h for details on this format and its operations.
+struct F {
+    U16 vec;
+
+    F() = default;
+    F(float f) {
+        // After adding 256.0f, the SkFixed15 value is the bottom two bytes of the float.
+        f += 256.0f;
+        vec = unaligned_load<uint16_t>(&f);
+    }
+
+    F(U16 v) : vec(v) {}
+    operator U16() const { return vec; }
+};
+
+SI F operator+(F x, F y) { return x.vec + y.vec; }
+SI F operator-(F x, F y) { return x.vec - y.vec; }
+SI F operator*(F x, F y) { return _mm_abs_epi16(_mm_mulhrs_epi16(x.vec, y.vec)); }
+SI F mad(F f, F m, F a) { return f*m+a; }
+SI F inv(F v) { return 1.0f - v; }
+SI F two(F v) { return v + v; }
+SI F lerp(F from, F to, F t) { return to*t + from*inv(t); }
+
+SI F operator<<(F x, int bits) { return x.vec << bits; }
+SI F operator>>(F x, int bits) { return x.vec >> bits; }
+
+using Stage = void(K* k, void** program, size_t x, size_t y, size_t tail, F,F,F,F, F,F,F,F);
+
+MAYBE_MSABI
+extern "C" size_t WRAP(start_pipeline)(size_t x, size_t y, size_t limit, void** program, K* k) {
+    F v{};
+    auto start = (Stage*)load_and_inc(program);
+    while (x + kStride <= limit) {
+        start(k,program,x,y,0,    v,v,v,v, v,v,v,v);
+        x += kStride;
+    }
+    if (size_t tail = limit - x) {
+        start(k,program,x,y,tail, v,v,v,v, v,v,v,v);
+    }
+    return limit;
+}
+extern "C" void WRAP(just_return)(K*, void**, size_t,size_t,size_t, F,F,F,F, F,F,F,F) {}
+
+#define STAGE(name)                                                                   \
+    SI void name##_k(K* k, LazyCtx ctx, size_t x, size_t y, size_t tail,              \
+                     F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da);             \
+    extern "C" void WRAP(name)(K* k, void** program, size_t x, size_t y, size_t tail, \
+                               F r, F g, F b, F a, F dr, F dg, F db, F da) {          \
+        LazyCtx ctx(program);                                                         \
+        name##_k(k,ctx,x,y,tail, r,g,b,a, dr,dg,db,da);                               \
+        auto next = (Stage*)load_and_inc(program);                                    \
+        next(k,program,x,y,tail, r,g,b,a, dr,dg,db,da);                               \
+    }                                                                                 \
+    SI void name##_k(K* k, LazyCtx ctx, size_t x, size_t y, size_t tail,              \
+                     F& r, F& g, F& b, F& a, F& dr, F& dg, F& db, F& da)
+
+
+// Helper functions used by multiple stages.
+
+template <typename V, typename T>
+SI V load(const T* src, size_t tail) {
+#if defined(JUMPER)
+    __builtin_assume(tail < kStride);
+    if (__builtin_expect(tail, 0)) {
+        V v{};  // Any inactive lanes are zeroed.
+        switch (tail-1) {
+            case 6: v[6] = src[6];
+            case 5: v[5] = src[5];
+            case 4: v[4] = src[4];
+            case 3: v[3] = src[3];
+            case 2: v[2] = src[2];
+            case 1: v[1] = src[1];
+            case 0: v[0] = src[0];
+        }
+        return v;
+    }
+#endif
+    return unaligned_load<V>(src);
+}
+
+template <typename V, typename T>
+SI void store(T* dst, V v, size_t tail) {
+#if defined(JUMPER)
+    __builtin_assume(tail < kStride);
+    if (__builtin_expect(tail, 0)) {
+        switch (tail-1) {
+            case 6: dst[6] = v[6];
+            case 5: dst[5] = v[5];
+            case 4: dst[4] = v[4];
+            case 3: dst[3] = v[3];
+            case 2: dst[2] = v[2];
+            case 1: dst[1] = v[1];
+            case 0: dst[0] = v[0];
+        }
+        return;
+    }
+#endif
+    unaligned_store(dst, v);
+}
+
+SI void from_8888(U32 rgba, F* r, F* g, F* b, F* a) {
+    // Split the 8 pixels into low and high halves, and reinterpret as vectors of 16-bit values.
+    U16 lo = unaligned_load<U16>((const uint32_t*)&rgba + 0),
+        hi = unaligned_load<U16>((const uint32_t*)&rgba + 4);
+
+    // Shuffle so that the 4 bytes of each color channel are contiguous...
+    lo = _mm_shuffle_epi8(lo, _mm_setr_epi8(0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15));
+    hi = _mm_shuffle_epi8(hi, _mm_setr_epi8(0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15));
+
+    // ...then get all 8 bytes of each color channel together into a single register.
+    U16 rg = _mm_unpacklo_epi32(lo,hi),
+        ba = _mm_unpackhi_epi32(lo,hi);
+
+    // Unpack as 16-bit values into the high half of each 16-bit lane, to get a free *256.
+    U16 R = _mm_unpacklo_epi8(U16(0), rg),
+        G = _mm_unpackhi_epi8(U16(0), rg),
+        B = _mm_unpacklo_epi8(U16(0), ba),
+        A = _mm_unpackhi_epi8(U16(0), ba);
+
+    // Now we scale from [0,255] to [0,32768].  Ideally that's 32768/255 = 128.50196,
+    // but we can approximate that very cheaply as 256*32897/65536 = 128.50391.
+    // 0 and 255 map to 0 and 32768 correctly, and nothing else is off by more than 1.
+    *r = _mm_mulhi_epu16(R, U16(32897));
+    *g = _mm_mulhi_epu16(G, U16(32897));
+    *b = _mm_mulhi_epu16(B, U16(32897));
+    *a = _mm_mulhi_epu16(A, U16(32897));
+}
+SI F from_byte(U8 bytes) {
+    // See from_8888() just above.
+    U16 hi = _mm_unpacklo_epi8(U16(0), widen_cast<__m128i>(bytes));
+    return (F)_mm_mulhi_epu16(hi, U16(32897));
+}
+
+SI U32 to_8888(F r, F g, F b, F a) {
+    // We want to interlace and pack these values from [0,32768] to [0,255].
+    // Luckily the simplest possible thing works great: >>7, then saturate.
+    // The 'u' in packus handles the saturation to [0,255] we need.
+    U16 rb = _mm_packus_epi16(r>>7,b>>7), // r0 r1 r2 r3 r4 r5 r6 r7 b0 b1 b2 b3 b4 b5 b6 b7
+        ga = _mm_packus_epi16(g>>7,a>>7);
+
+    U16 rg = _mm_unpacklo_epi8(rb, ga),   // r0 g0 r1 g1 ...                           r7 g7
+        ba = _mm_unpackhi_epi8(rb, ga);   // b0 a0       ...                           b7 a7
+
+    U16 lo = _mm_unpacklo_epi16(rg, ba),  // r0 g0 b0 a0 ...                     r3 g3 b3 a3
+        hi = _mm_unpackhi_epi16(rg, ba);  // r4 g4 b4 a4 ...                     r7 g7 b7 a7
+
+    U32 px;
+    memcpy((uint32_t*)&px + 0, &lo, sizeof(lo));
+    memcpy((uint32_t*)&px + 4, &hi, sizeof(hi));
+    return px;
+}
+SI U8 to_byte(F v) {
+    // See to_8888() just above.
+    U16 packed = _mm_packus_epi16(v>>7, v>>7);  // Doesn't really matter what we pack on top.
+    return unaligned_load<U8>(&packed);
+}
+
+// Stages!
+
+STAGE(constant_color) {
+    // We're converting to fixed point, which lets us play some IEEE representation tricks,
+    // replacing a naive *32768 and float->int conversion with a simple float add.
+    __m128i bits = _mm_loadu_ps((const float*)ctx) + _mm_set1_ps(256.0f);
+    r = _mm_shuffle_epi8(bits, _mm_set1_epi16(0x0100));
+    g = _mm_shuffle_epi8(bits, _mm_set1_epi16(0x0504));
+    b = _mm_shuffle_epi8(bits, _mm_set1_epi16(0x0908));
+    a = _mm_shuffle_epi8(bits, _mm_set1_epi16(0x0d0c));
+}
+
+STAGE(set_rgb) {
+    auto rgb = (const float*)ctx;
+    r = rgb[0];
+    g = rgb[1];
+    b = rgb[2];
+}
+
+STAGE(premul) {
+    r = r * a;
+    g = g * a;
+    b = b * a;
+}
+
+STAGE(load_8888) {
+    auto ptr = *(const uint32_t**)ctx + x;
+    from_8888(load<U32>(ptr, tail), &r,&g,&b,&a);
+}
+STAGE(store_8888) {
+    auto ptr = *(uint32_t**)ctx + x;
+    store(ptr, to_8888(r,g,b,a), tail);
+}
+
+STAGE(load_a8) {
+    auto ptr = *(const uint8_t**)ctx + x;
+    r = g = b = 0.0f;
+    a = from_byte(load<U8>(ptr, tail));
+}
+STAGE(store_a8) {
+    auto ptr = *(uint8_t**)ctx + x;
+    store(ptr, to_byte(a), tail);
+}
+
+STAGE(load_g8) {
+    auto ptr = *(const uint8_t**)ctx + x;
+    r = g = b = from_byte(load<U8>(ptr, tail));
+    a = 1.0f;
+}
+
+STAGE(srcover_rgba_8888) {
+    auto ptr = *(uint32_t**)ctx + x;
+
+    from_8888(load<U32>(ptr, tail), &dr,&dg,&db,&da);
+
+    r = mad(dr, inv(a), r);
+    g = mad(dg, inv(a), g);
+    b = mad(db, inv(a), b);
+    a = mad(da, inv(a), a);
+
+    store(ptr, to_8888(r,g,b,a), tail);
+}
+
+STAGE(scale_1_float) {
+    float c = *(const float*)ctx;
+
+    r = r * c;
+    g = g * c;
+    b = b * c;
+    a = a * c;
+}
+STAGE(scale_u8) {
+    auto ptr = *(const uint8_t**)ctx + x;
+
+    U8 scales = load<U8>(ptr, tail);
+    F c = from_byte(scales);
+
+    r = r * c;
+    g = g * c;
+    b = b * c;
+    a = a * c;
+}
+
+STAGE(lerp_1_float) {
+    float c = *(const float*)ctx;
+
+    r = lerp(dr, r, c);
+    g = lerp(dg, g, c);
+    b = lerp(db, b, c);
+    a = lerp(da, a, c);
+}
+STAGE(lerp_u8) {
+    auto ptr = *(const uint8_t**)ctx + x;
+
+    U8 scales = load<U8>(ptr, tail);
+    F c = from_byte(scales);
+
+    r = lerp(dr, r, c);
+    g = lerp(dg, g, c);
+    b = lerp(db, b, c);
+    a = lerp(da, a, c);
+}
+
+STAGE(swap_rb) {
+    auto tmp = r;
+    r = b;
+    b = tmp;
+}
+
+STAGE(swap) {
+    auto swap = [](F& v, F& dv) {
+        auto tmp = v;
+        v = dv;
+        dv = tmp;
+    };
+    swap(r, dr);
+    swap(g, dg);
+    swap(b, db);
+    swap(a, da);
+}
+STAGE(move_src_dst) {
+    dr = r;
+    dg = g;
+    db = b;
+    da = a;
+}
+STAGE(move_dst_src) {
+    r = dr;
+    g = dg;
+    b = db;
+    a = da;
+}
+
+// Most blend modes apply the same logic to each channel.
+#define BLEND_MODE(name)                       \
+    SI F name##_channel(F s, F d, F sa, F da); \
+    STAGE(name) {                              \
+        r = name##_channel(r,dr,a,da);         \
+        g = name##_channel(g,dg,a,da);         \
+        b = name##_channel(b,db,a,da);         \
+        a = name##_channel(a,da,a,da);         \
+    }                                          \
+    SI F name##_channel(F s, F d, F sa, F da)
+
+BLEND_MODE(clear)    { return 0.0f; }
+BLEND_MODE(srcatop)  { return s*da + d*inv(sa); }
+BLEND_MODE(dstatop)  { return d*sa + s*inv(da); }
+BLEND_MODE(srcin)    { return s * da; }
+BLEND_MODE(dstin)    { return d * sa; }
+BLEND_MODE(srcout)   { return s * inv(da); }
+BLEND_MODE(dstout)   { return d * inv(sa); }
+BLEND_MODE(srcover)  { return mad(d, inv(sa), s); }
+BLEND_MODE(dstover)  { return mad(s, inv(da), d); }
+
+BLEND_MODE(modulate) { return s*d; }
+BLEND_MODE(multiply) { return s*inv(da) + d*inv(sa) + s*d; }
+BLEND_MODE(plus_)    { return s + d; }
+BLEND_MODE(screen)   { return s + inv(s)*d; }
+BLEND_MODE(xor_)     { return s*inv(da) + d*inv(sa); }
+
+#undef BLEND_MODE
diff --git a/src/jumper/SkJumper_vectors.h b/src/jumper/SkJumper_vectors.h
new file mode 100644
index 0000000..a789839
--- /dev/null
+++ b/src/jumper/SkJumper_vectors.h
@@ -0,0 +1,766 @@
+/*
+ * 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 SkJumper_vectors_DEFINED
+#define SkJumper_vectors_DEFINED
+
+#include "SkJumper.h"
+#include "SkJumper_misc.h"
+
+// This file contains vector types that SkJumper_stages.cpp uses to define stages.
+
+// Every function in this file should be marked static and inline using SI (see SkJumper_misc.h).
+
+#if !defined(JUMPER)
+    // This path should lead to portable code that can be compiled directly into Skia.
+    // (All other paths are compiled offline by Clang into SkJumper_generated.S.)
+    #include <math.h>
+
+    using F   = float   ;
+    using I32 =  int32_t;
+    using U64 = uint64_t;
+    using U32 = uint32_t;
+    using U16 = uint16_t;
+    using U8  = uint8_t ;
+
+    SI F   mad(F f, F m, F a)   { return f*m+a; }
+    SI F   min(F a, F b)        { return fminf(a,b); }
+    SI F   max(F a, F b)        { return fmaxf(a,b); }
+    SI F   abs_  (F v)          { return fabsf(v); }
+    SI F   floor_(F v)          { return floorf(v); }
+    SI F   rcp   (F v)          { return 1.0f / v; }
+    SI F   rsqrt (F v)          { return 1.0f / sqrtf(v); }
+    SI F    sqrt_(F v)          { return sqrtf(v); }
+    SI U32 round (F v, F scale) { return (uint32_t)(v*scale + 0.5f); }
+    SI U16 pack(U32 v)          { return (U16)v; }
+    SI U8  pack(U16 v)          { return  (U8)v; }
+
+    SI F if_then_else(I32 c, F t, F e) { return c ? t : e; }
+
+    template <typename T>
+    SI T gather(const T* p, U32 ix) { return p[ix]; }
+
+    SI void load3(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b) {
+        *r = ptr[0];
+        *g = ptr[1];
+        *b = ptr[2];
+    }
+    SI void load4(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b, U16* a) {
+        *r = ptr[0];
+        *g = ptr[1];
+        *b = ptr[2];
+        *a = ptr[3];
+    }
+    SI void store4(uint16_t* ptr, size_t tail, U16 r, U16 g, U16 b, U16 a) {
+        ptr[0] = r;
+        ptr[1] = g;
+        ptr[2] = b;
+        ptr[3] = a;
+    }
+
+    SI void load4(const float* ptr, size_t tail, F* r, F* g, F* b, F* a) {
+        *r = ptr[0];
+        *g = ptr[1];
+        *b = ptr[2];
+        *a = ptr[3];
+    }
+    SI void store4(float* ptr, size_t tail, F r, F g, F b, F a) {
+        ptr[0] = r;
+        ptr[1] = g;
+        ptr[2] = b;
+        ptr[3] = a;
+    }
+
+#elif defined(__aarch64__)
+    #include <arm_neon.h>
+
+    // Since we know we're using Clang, we can use its vector extensions.
+    template <typename T> using V = T __attribute__((ext_vector_type(4)));
+    using F   = V<float   >;
+    using I32 = V< int32_t>;
+    using U64 = V<uint64_t>;
+    using U32 = V<uint32_t>;
+    using U16 = V<uint16_t>;
+    using U8  = V<uint8_t >;
+
+    // We polyfill a few routines that Clang doesn't build into ext_vector_types.
+    SI F   mad(F f, F m, F a)                    { return vfmaq_f32(a,f,m);        }
+    SI F   min(F a, F b)                         { return vminq_f32(a,b);          }
+    SI F   max(F a, F b)                         { return vmaxq_f32(a,b);          }
+    SI F   abs_  (F v)                           { return vabsq_f32(v);            }
+    SI F   floor_(F v)                           { return vrndmq_f32(v);           }
+    SI F   rcp   (F v) { auto e = vrecpeq_f32 (v); return vrecpsq_f32 (v,e  ) * e; }
+    SI F   rsqrt (F v) { auto e = vrsqrteq_f32(v); return vrsqrtsq_f32(v,e*e) * e; }
+    SI F    sqrt_(F v)                           { return vsqrtq_f32(v); }
+    SI U32 round (F v, F scale)                  { return vcvtnq_u32_f32(v*scale); }
+    SI U16 pack(U32 v)                           { return __builtin_convertvector(v, U16); }
+    SI U8  pack(U16 v)                           { return __builtin_convertvector(v,  U8); }
+
+    SI F if_then_else(I32 c, F t, F e) { return vbslq_f32((U32)c,t,e); }
+
+    template <typename T>
+    SI V<T> gather(const T* p, U32 ix) {
+        return {p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]]};
+    }
+
+    SI void load3(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b) {
+        uint16x4x3_t rgb;
+        if (__builtin_expect(tail,0)) {
+            if (  true  ) { rgb = vld3_lane_u16(ptr + 0, rgb, 0); }
+            if (tail > 1) { rgb = vld3_lane_u16(ptr + 3, rgb, 1); }
+            if (tail > 2) { rgb = vld3_lane_u16(ptr + 6, rgb, 2); }
+        } else {
+            rgb = vld3_u16(ptr);
+        }
+        *r = rgb.val[0];
+        *g = rgb.val[1];
+        *b = rgb.val[2];
+    }
+    SI void load4(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b, U16* a) {
+        uint16x4x4_t rgba;
+        if (__builtin_expect(tail,0)) {
+            if (  true  ) { rgba = vld4_lane_u16(ptr + 0, rgba, 0); }
+            if (tail > 1) { rgba = vld4_lane_u16(ptr + 4, rgba, 1); }
+            if (tail > 2) { rgba = vld4_lane_u16(ptr + 8, rgba, 2); }
+        } else {
+            rgba = vld4_u16(ptr);
+        }
+        *r = rgba.val[0];
+        *g = rgba.val[1];
+        *b = rgba.val[2];
+        *a = rgba.val[3];
+    }
+    SI void store4(uint16_t* ptr, size_t tail, U16 r, U16 g, U16 b, U16 a) {
+        if (__builtin_expect(tail,0)) {
+            if (  true  ) { vst4_lane_u16(ptr + 0, (uint16x4x4_t{{r,g,b,a}}), 0); }
+            if (tail > 1) { vst4_lane_u16(ptr + 4, (uint16x4x4_t{{r,g,b,a}}), 1); }
+            if (tail > 2) { vst4_lane_u16(ptr + 8, (uint16x4x4_t{{r,g,b,a}}), 2); }
+        } else {
+            vst4_u16(ptr, (uint16x4x4_t{{r,g,b,a}}));
+        }
+    }
+    SI void load4(const float* ptr, size_t tail, F* r, F* g, F* b, F* a) {
+        float32x4x4_t rgba;
+        if (__builtin_expect(tail,0)) {
+            if (  true  ) { rgba = vld4q_lane_f32(ptr + 0, rgba, 0); }
+            if (tail > 1) { rgba = vld4q_lane_f32(ptr + 4, rgba, 1); }
+            if (tail > 2) { rgba = vld4q_lane_f32(ptr + 8, rgba, 2); }
+        } else {
+            rgba = vld4q_f32(ptr);
+        }
+        *r = rgba.val[0];
+        *g = rgba.val[1];
+        *b = rgba.val[2];
+        *a = rgba.val[3];
+    }
+    SI void store4(float* ptr, size_t tail, F r, F g, F b, F a) {
+        if (__builtin_expect(tail,0)) {
+            if (  true  ) { vst4q_lane_f32(ptr + 0, (float32x4x4_t{{r,g,b,a}}), 0); }
+            if (tail > 1) { vst4q_lane_f32(ptr + 4, (float32x4x4_t{{r,g,b,a}}), 1); }
+            if (tail > 2) { vst4q_lane_f32(ptr + 8, (float32x4x4_t{{r,g,b,a}}), 2); }
+        } else {
+            vst4q_f32(ptr, (float32x4x4_t{{r,g,b,a}}));
+        }
+    }
+
+#elif defined(__arm__)
+    #if defined(__thumb2__) || !defined(__ARM_ARCH_7A__) || !defined(__ARM_VFPV4__)
+        #error On ARMv7, compile with -march=armv7-a -mfpu=neon-vfp4, without -mthumb.
+    #endif
+    #include <arm_neon.h>
+
+    // We can pass {s0-s15} as arguments under AAPCS-VFP.  We'll slice that as 8 d-registers.
+    template <typename T> using V = T __attribute__((ext_vector_type(2)));
+    using F   = V<float   >;
+    using I32 = V< int32_t>;
+    using U64 = V<uint64_t>;
+    using U32 = V<uint32_t>;
+    using U16 = V<uint16_t>;
+    using U8  = V<uint8_t >;
+
+    SI F   mad(F f, F m, F a)                  { return vfma_f32(a,f,m);        }
+    SI F   min(F a, F b)                       { return vmin_f32(a,b);          }
+    SI F   max(F a, F b)                       { return vmax_f32(a,b);          }
+    SI F   abs_ (F v)                          { return vabs_f32(v);            }
+    SI F   rcp  (F v) { auto e = vrecpe_f32 (v); return vrecps_f32 (v,e  ) * e; }
+    SI F   rsqrt(F v) { auto e = vrsqrte_f32(v); return vrsqrts_f32(v,e*e) * e; }
+    SI U32 round(F v, F scale)                 { return vcvt_u32_f32(mad(v,scale,0.5f)); }
+    SI U16 pack(U32 v)                         { return __builtin_convertvector(v, U16); }
+    SI U8  pack(U16 v)                         { return __builtin_convertvector(v,  U8); }
+
+    SI F sqrt_(F v) {
+        auto e = vrsqrte_f32(v);  // Estimate and two refinement steps for e = rsqrt(v).
+        e *= vrsqrts_f32(v,e*e);
+        e *= vrsqrts_f32(v,e*e);
+        return v*e;               // sqrt(v) == v*rsqrt(v).
+    }
+
+    SI F if_then_else(I32 c, F t, F e) { return vbsl_f32((U32)c,t,e); }
+
+    SI F floor_(F v) {
+        F roundtrip = vcvt_f32_s32(vcvt_s32_f32(v));
+        return roundtrip - if_then_else(roundtrip > v, 1, 0);
+    }
+
+    template <typename T>
+    SI V<T> gather(const T* p, U32 ix) {
+        return {p[ix[0]], p[ix[1]]};
+    }
+
+    SI void load3(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b) {
+        uint16x4x3_t rgb;
+        rgb = vld3_lane_u16(ptr + 0, rgb, 0);
+        if (__builtin_expect(tail, 0)) {
+            vset_lane_u16(0, rgb.val[0], 1);
+            vset_lane_u16(0, rgb.val[1], 1);
+            vset_lane_u16(0, rgb.val[2], 1);
+        } else {
+            rgb = vld3_lane_u16(ptr + 3, rgb, 1);
+        }
+        *r = unaligned_load<U16>(rgb.val+0);
+        *g = unaligned_load<U16>(rgb.val+1);
+        *b = unaligned_load<U16>(rgb.val+2);
+    }
+    SI void load4(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b, U16* a) {
+        uint16x4x4_t rgba;
+        rgba = vld4_lane_u16(ptr + 0, rgba, 0);
+        if (__builtin_expect(tail, 0)) {
+            vset_lane_u16(0, rgba.val[0], 1);
+            vset_lane_u16(0, rgba.val[1], 1);
+            vset_lane_u16(0, rgba.val[2], 1);
+            vset_lane_u16(0, rgba.val[3], 1);
+        } else {
+            rgba = vld4_lane_u16(ptr + 4, rgba, 1);
+        }
+        *r = unaligned_load<U16>(rgba.val+0);
+        *g = unaligned_load<U16>(rgba.val+1);
+        *b = unaligned_load<U16>(rgba.val+2);
+        *a = unaligned_load<U16>(rgba.val+3);
+    }
+    SI void store4(uint16_t* ptr, size_t tail, U16 r, U16 g, U16 b, U16 a) {
+        uint16x4x4_t rgba = {{
+            widen_cast<uint16x4_t>(r),
+            widen_cast<uint16x4_t>(g),
+            widen_cast<uint16x4_t>(b),
+            widen_cast<uint16x4_t>(a),
+        }};
+        vst4_lane_u16(ptr + 0, rgba, 0);
+        if (__builtin_expect(tail == 0, true)) {
+            vst4_lane_u16(ptr + 4, rgba, 1);
+        }
+    }
+
+    SI void load4(const float* ptr, size_t tail, F* r, F* g, F* b, F* a) {
+        float32x2x4_t rgba;
+        if (__builtin_expect(tail, 0)) {
+            rgba = vld4_dup_f32(ptr);
+        } else {
+            rgba = vld4_f32(ptr);
+        }
+        *r = rgba.val[0];
+        *g = rgba.val[1];
+        *b = rgba.val[2];
+        *a = rgba.val[3];
+    }
+    SI void store4(float* ptr, size_t tail, F r, F g, F b, F a) {
+        if (__builtin_expect(tail, 0)) {
+            vst4_lane_f32(ptr, (float32x2x4_t{{r,g,b,a}}), 0);
+        } else {
+            vst4_f32(ptr, (float32x2x4_t{{r,g,b,a}}));
+        }
+    }
+
+
+#elif defined(__AVX__)
+    #include <immintrin.h>
+
+    // These are __m256 and __m256i, but friendlier and strongly-typed.
+    template <typename T> using V = T __attribute__((ext_vector_type(8)));
+    using F   = V<float   >;
+    using I32 = V< int32_t>;
+    using U64 = V<uint64_t>;
+    using U32 = V<uint32_t>;
+    using U16 = V<uint16_t>;
+    using U8  = V<uint8_t >;
+
+    SI F mad(F f, F m, F a)  {
+    #if defined(__FMA__)
+        return _mm256_fmadd_ps(f,m,a);
+    #else
+        return f*m+a;
+    #endif
+    }
+
+    SI F   min(F a, F b)        { return _mm256_min_ps(a,b);    }
+    SI F   max(F a, F b)        { return _mm256_max_ps(a,b);    }
+    SI F   abs_  (F v)          { return _mm256_and_ps(v, 0-v); }
+    SI F   floor_(F v)          { return _mm256_floor_ps(v);    }
+    SI F   rcp   (F v)          { return _mm256_rcp_ps  (v);    }
+    SI F   rsqrt (F v)          { return _mm256_rsqrt_ps(v);    }
+    SI F    sqrt_(F v)          { return _mm256_sqrt_ps (v);    }
+    SI U32 round (F v, F scale) { return _mm256_cvtps_epi32(v*scale); }
+
+    SI U16 pack(U32 v) {
+        return _mm_packus_epi32(_mm256_extractf128_si256(v, 0),
+                                _mm256_extractf128_si256(v, 1));
+    }
+    SI U8 pack(U16 v) {
+        auto r = _mm_packus_epi16(v,v);
+        return unaligned_load<U8>(&r);
+    }
+
+    SI F if_then_else(I32 c, F t, F e) { return _mm256_blendv_ps(e,t,c); }
+
+    template <typename T>
+    SI V<T> gather(const T* p, U32 ix) {
+        return { p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]],
+                 p[ix[4]], p[ix[5]], p[ix[6]], p[ix[7]], };
+    }
+    #if defined(__AVX2__)
+        SI F   gather(const float*    p, U32 ix) { return _mm256_i32gather_ps   (p, ix, 4); }
+        SI U32 gather(const uint32_t* p, U32 ix) { return _mm256_i32gather_epi32(p, ix, 4); }
+        SI U64 gather(const uint64_t* p, U32 ix) {
+            __m256i parts[] = {
+                _mm256_i32gather_epi64(p, _mm256_extracti128_si256(ix,0), 8),
+                _mm256_i32gather_epi64(p, _mm256_extracti128_si256(ix,1), 8),
+            };
+            return bit_cast<U64>(parts);
+        }
+    #endif
+
+    SI void load3(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b) {
+        __m128i _0,_1,_2,_3,_4,_5,_6,_7;
+        if (__builtin_expect(tail,0)) {
+            auto load_rgb = [](const uint16_t* src) {
+                auto v = _mm_cvtsi32_si128(*(const uint32_t*)src);
+                return _mm_insert_epi16(v, src[2], 2);
+            };
+            if (tail > 0) { _0 = load_rgb(ptr +  0); }
+            if (tail > 1) { _1 = load_rgb(ptr +  3); }
+            if (tail > 2) { _2 = load_rgb(ptr +  6); }
+            if (tail > 3) { _3 = load_rgb(ptr +  9); }
+            if (tail > 4) { _4 = load_rgb(ptr + 12); }
+            if (tail > 5) { _5 = load_rgb(ptr + 15); }
+            if (tail > 6) { _6 = load_rgb(ptr + 18); }
+        } else {
+            // Load 0+1, 2+3, 4+5 normally, and 6+7 backed up 4 bytes so we don't run over.
+            auto _01 =                _mm_loadu_si128((const __m128i*)(ptr +  0))    ;
+            auto _23 =                _mm_loadu_si128((const __m128i*)(ptr +  6))    ;
+            auto _45 =                _mm_loadu_si128((const __m128i*)(ptr + 12))    ;
+            auto _67 = _mm_srli_si128(_mm_loadu_si128((const __m128i*)(ptr + 16)), 4);
+            _0 = _01; _1 = _mm_srli_si128(_01, 6),
+            _2 = _23; _3 = _mm_srli_si128(_23, 6),
+            _4 = _45; _5 = _mm_srli_si128(_45, 6),
+            _6 = _67; _7 = _mm_srli_si128(_67, 6);
+        }
+
+        auto _02 = _mm_unpacklo_epi16(_0, _2),  // r0 r2 g0 g2 b0 b2 xx xx
+             _13 = _mm_unpacklo_epi16(_1, _3),
+             _46 = _mm_unpacklo_epi16(_4, _6),
+             _57 = _mm_unpacklo_epi16(_5, _7);
+
+        auto rg0123 = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
+             bx0123 = _mm_unpackhi_epi16(_02, _13),  // b0 b1 b2 b3 xx xx xx xx
+             rg4567 = _mm_unpacklo_epi16(_46, _57),
+             bx4567 = _mm_unpackhi_epi16(_46, _57);
+
+        *r = _mm_unpacklo_epi64(rg0123, rg4567);
+        *g = _mm_unpackhi_epi64(rg0123, rg4567);
+        *b = _mm_unpacklo_epi64(bx0123, bx4567);
+    }
+    SI void load4(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b, U16* a) {
+        __m128i _01, _23, _45, _67;
+        if (__builtin_expect(tail,0)) {
+            auto src = (const double*)ptr;
+            _01 = _23 = _45 = _67 = _mm_setzero_si128();
+            if (tail > 0) { _01 = _mm_loadl_pd(_01, src+0); }
+            if (tail > 1) { _01 = _mm_loadh_pd(_01, src+1); }
+            if (tail > 2) { _23 = _mm_loadl_pd(_23, src+2); }
+            if (tail > 3) { _23 = _mm_loadh_pd(_23, src+3); }
+            if (tail > 4) { _45 = _mm_loadl_pd(_45, src+4); }
+            if (tail > 5) { _45 = _mm_loadh_pd(_45, src+5); }
+            if (tail > 6) { _67 = _mm_loadl_pd(_67, src+6); }
+        } else {
+            _01 = _mm_loadu_si128(((__m128i*)ptr) + 0);
+            _23 = _mm_loadu_si128(((__m128i*)ptr) + 1);
+            _45 = _mm_loadu_si128(((__m128i*)ptr) + 2);
+            _67 = _mm_loadu_si128(((__m128i*)ptr) + 3);
+        }
+
+        auto _02 = _mm_unpacklo_epi16(_01, _23),  // r0 r2 g0 g2 b0 b2 a0 a2
+             _13 = _mm_unpackhi_epi16(_01, _23),  // r1 r3 g1 g3 b1 b3 a1 a3
+             _46 = _mm_unpacklo_epi16(_45, _67),
+             _57 = _mm_unpackhi_epi16(_45, _67);
+
+        auto rg0123 = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
+             ba0123 = _mm_unpackhi_epi16(_02, _13),  // b0 b1 b2 b3 a0 a1 a2 a3
+             rg4567 = _mm_unpacklo_epi16(_46, _57),
+             ba4567 = _mm_unpackhi_epi16(_46, _57);
+
+        *r = _mm_unpacklo_epi64(rg0123, rg4567);
+        *g = _mm_unpackhi_epi64(rg0123, rg4567);
+        *b = _mm_unpacklo_epi64(ba0123, ba4567);
+        *a = _mm_unpackhi_epi64(ba0123, ba4567);
+    }
+    SI void store4(uint16_t* ptr, size_t tail, U16 r, U16 g, U16 b, U16 a) {
+        auto rg0123 = _mm_unpacklo_epi16(r, g),  // r0 g0 r1 g1 r2 g2 r3 g3
+             rg4567 = _mm_unpackhi_epi16(r, g),  // r4 g4 r5 g5 r6 g6 r7 g7
+             ba0123 = _mm_unpacklo_epi16(b, a),
+             ba4567 = _mm_unpackhi_epi16(b, a);
+
+        auto _01 = _mm_unpacklo_epi32(rg0123, ba0123),
+             _23 = _mm_unpackhi_epi32(rg0123, ba0123),
+             _45 = _mm_unpacklo_epi32(rg4567, ba4567),
+             _67 = _mm_unpackhi_epi32(rg4567, ba4567);
+
+        if (__builtin_expect(tail,0)) {
+            auto dst = (double*)ptr;
+            if (tail > 0) { _mm_storel_pd(dst+0, _01); }
+            if (tail > 1) { _mm_storeh_pd(dst+1, _01); }
+            if (tail > 2) { _mm_storel_pd(dst+2, _23); }
+            if (tail > 3) { _mm_storeh_pd(dst+3, _23); }
+            if (tail > 4) { _mm_storel_pd(dst+4, _45); }
+            if (tail > 5) { _mm_storeh_pd(dst+5, _45); }
+            if (tail > 6) { _mm_storel_pd(dst+6, _67); }
+        } else {
+            _mm_storeu_si128((__m128i*)ptr + 0, _01);
+            _mm_storeu_si128((__m128i*)ptr + 1, _23);
+            _mm_storeu_si128((__m128i*)ptr + 2, _45);
+            _mm_storeu_si128((__m128i*)ptr + 3, _67);
+        }
+    }
+
+    SI void load4(const float* ptr, size_t tail, F* r, F* g, F* b, F* a) {
+        F _04, _15, _26, _37;
+
+        switch (tail) {
+            case 0: _37 = _mm256_insertf128_ps(_37, _mm_loadu_ps(ptr+28), 1);
+            case 7: _26 = _mm256_insertf128_ps(_26, _mm_loadu_ps(ptr+24), 1);
+            case 6: _15 = _mm256_insertf128_ps(_15, _mm_loadu_ps(ptr+20), 1);
+            case 5: _04 = _mm256_insertf128_ps(_04, _mm_loadu_ps(ptr+16), 1);
+            case 4: _37 = _mm256_insertf128_ps(_37, _mm_loadu_ps(ptr+12), 0);
+            case 3: _26 = _mm256_insertf128_ps(_26, _mm_loadu_ps(ptr+ 8), 0);
+            case 2: _15 = _mm256_insertf128_ps(_15, _mm_loadu_ps(ptr+ 4), 0);
+            case 1: _04 = _mm256_insertf128_ps(_04, _mm_loadu_ps(ptr+ 0), 0);
+        }
+
+        F rg0145 = _mm256_unpacklo_ps(_04,_15),  // r0 r1 g0 g1 | r4 r5 g4 g5
+          ba0145 = _mm256_unpackhi_ps(_04,_15),
+          rg2367 = _mm256_unpacklo_ps(_26,_37),
+          ba2367 = _mm256_unpackhi_ps(_26,_37);
+
+        *r = _mm256_unpacklo_pd(rg0145, rg2367);
+        *g = _mm256_unpackhi_pd(rg0145, rg2367);
+        *b = _mm256_unpacklo_pd(ba0145, ba2367);
+        *a = _mm256_unpackhi_pd(ba0145, ba2367);
+    }
+    SI void store4(float* ptr, size_t tail, F r, F g, F b, F a) {
+        F rg0145 = _mm256_unpacklo_ps(r, g),  // r0 g0 r1 g1 | r4 g4 r5 g5
+          rg2367 = _mm256_unpackhi_ps(r, g),  // r2 ...      | r6 ...
+          ba0145 = _mm256_unpacklo_ps(b, a),  // b0 a0 b1 a1 | b4 a4 b5 a5
+          ba2367 = _mm256_unpackhi_ps(b, a);  // b2 ...      | b6 ...
+
+        F _04 = _mm256_unpacklo_pd(rg0145, ba0145),  // r0 g0 b0 a0 | r4 g4 b4 a4
+          _15 = _mm256_unpackhi_pd(rg0145, ba0145),  // r1 ...      | r5 ...
+          _26 = _mm256_unpacklo_pd(rg2367, ba2367),  // r2 ...      | r6 ...
+          _37 = _mm256_unpackhi_pd(rg2367, ba2367);  // r3 ...      | r7 ...
+
+        if (__builtin_expect(tail, 0)) {
+            if (tail > 0) { _mm_storeu_ps(ptr+ 0, _mm256_extractf128_ps(_04, 0)); }
+            if (tail > 1) { _mm_storeu_ps(ptr+ 4, _mm256_extractf128_ps(_15, 0)); }
+            if (tail > 2) { _mm_storeu_ps(ptr+ 8, _mm256_extractf128_ps(_26, 0)); }
+            if (tail > 3) { _mm_storeu_ps(ptr+12, _mm256_extractf128_ps(_37, 0)); }
+            if (tail > 4) { _mm_storeu_ps(ptr+16, _mm256_extractf128_ps(_04, 1)); }
+            if (tail > 5) { _mm_storeu_ps(ptr+20, _mm256_extractf128_ps(_15, 1)); }
+            if (tail > 6) { _mm_storeu_ps(ptr+24, _mm256_extractf128_ps(_26, 1)); }
+        } else {
+            F _01 = _mm256_permute2f128_ps(_04, _15, 32),  // 32 == 0010 0000 == lo, lo
+              _23 = _mm256_permute2f128_ps(_26, _37, 32),
+              _45 = _mm256_permute2f128_ps(_04, _15, 49),  // 49 == 0011 0001 == hi, hi
+              _67 = _mm256_permute2f128_ps(_26, _37, 49);
+            _mm256_storeu_ps(ptr+ 0, _01);
+            _mm256_storeu_ps(ptr+ 8, _23);
+            _mm256_storeu_ps(ptr+16, _45);
+            _mm256_storeu_ps(ptr+24, _67);
+        }
+    }
+
+#elif defined(__SSE2__)
+    #include <immintrin.h>
+
+    template <typename T> using V = T __attribute__((ext_vector_type(4)));
+    using F   = V<float   >;
+    using I32 = V< int32_t>;
+    using U64 = V<uint64_t>;
+    using U32 = V<uint32_t>;
+    using U16 = V<uint16_t>;
+    using U8  = V<uint8_t >;
+
+    SI F   mad(F f, F m, F a)  { return f*m+a;              }
+    SI F   min(F a, F b)       { return _mm_min_ps(a,b);    }
+    SI F   max(F a, F b)       { return _mm_max_ps(a,b);    }
+    SI F   abs_(F v)           { return _mm_and_ps(v, 0-v); }
+    SI F   rcp   (F v)         { return _mm_rcp_ps  (v);    }
+    SI F   rsqrt (F v)         { return _mm_rsqrt_ps(v);    }
+    SI F    sqrt_(F v)         { return _mm_sqrt_ps (v);    }
+    SI U32 round(F v, F scale) { return _mm_cvtps_epi32(v*scale); }
+
+    SI U16 pack(U32 v) {
+    #if defined(__SSE4_1__)
+        auto p = _mm_packus_epi32(v,v);
+    #else
+        // Sign extend so that _mm_packs_epi32() does the pack we want.
+        auto p = _mm_srai_epi32(_mm_slli_epi32(v, 16), 16);
+        p = _mm_packs_epi32(p,p);
+    #endif
+        return unaligned_load<U16>(&p);  // We have two copies.  Return (the lower) one.
+    }
+    SI U8 pack(U16 v) {
+        auto r = widen_cast<__m128i>(v);
+        r = _mm_packus_epi16(r,r);
+        return unaligned_load<U8>(&r);
+    }
+
+    SI F if_then_else(I32 c, F t, F e) {
+        return _mm_or_ps(_mm_and_ps(c, t), _mm_andnot_ps(c, e));
+    }
+
+    SI F floor_(F v) {
+    #if defined(__SSE4_1__)
+        return _mm_floor_ps(v);
+    #else
+        F roundtrip = _mm_cvtepi32_ps(_mm_cvttps_epi32(v));
+        return roundtrip - if_then_else(roundtrip > v, 1, 0);
+    #endif
+    }
+
+    template <typename T>
+    SI V<T> gather(const T* p, U32 ix) {
+        return {p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]]};
+    }
+
+    SI void load3(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b) {
+        __m128i _0, _1, _2, _3;
+        if (__builtin_expect(tail,0)) {
+            _1 = _2 = _3 = _mm_setzero_si128();
+            auto load_rgb = [](const uint16_t* src) {
+                auto v = _mm_cvtsi32_si128(*(const uint32_t*)src);
+                return _mm_insert_epi16(v, src[2], 2);
+            };
+            if (  true  ) { _0 = load_rgb(ptr + 0); }
+            if (tail > 1) { _1 = load_rgb(ptr + 3); }
+            if (tail > 2) { _2 = load_rgb(ptr + 6); }
+        } else {
+            // Load slightly weirdly to make sure we don't load past the end of 4x48 bits.
+            auto _01 =                _mm_loadu_si128((const __m128i*)(ptr + 0))    ,
+                 _23 = _mm_srli_si128(_mm_loadu_si128((const __m128i*)(ptr + 4)), 4);
+
+            // Each _N holds R,G,B for pixel N in its lower 3 lanes (upper 5 are ignored).
+            _0 = _01;
+            _1 = _mm_srli_si128(_01, 6);
+            _2 = _23;
+            _3 = _mm_srli_si128(_23, 6);
+        }
+
+        // De-interlace to R,G,B.
+        auto _02 = _mm_unpacklo_epi16(_0, _2),  // r0 r2 g0 g2 b0 b2 xx xx
+             _13 = _mm_unpacklo_epi16(_1, _3);  // r1 r3 g1 g3 b1 b3 xx xx
+
+        auto R = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
+             G = _mm_srli_si128(R, 8),
+             B = _mm_unpackhi_epi16(_02, _13);  // b0 b1 b2 b3 xx xx xx xx
+
+        *r = unaligned_load<U16>(&R);
+        *g = unaligned_load<U16>(&G);
+        *b = unaligned_load<U16>(&B);
+    }
+
+    SI void load4(const uint16_t* ptr, size_t tail, U16* r, U16* g, U16* b, U16* a) {
+        __m128i _01, _23;
+        if (__builtin_expect(tail,0)) {
+            _01 = _23 = _mm_setzero_si128();
+            auto src = (const double*)ptr;
+            if (  true  ) { _01 = _mm_loadl_pd(_01, src + 0); } // r0 g0 b0 a0 00 00 00 00
+            if (tail > 1) { _01 = _mm_loadh_pd(_01, src + 1); } // r0 g0 b0 a0 r1 g1 b1 a1
+            if (tail > 2) { _23 = _mm_loadl_pd(_23, src + 2); } // r2 g2 b2 a2 00 00 00 00
+        } else {
+            _01 = _mm_loadu_si128(((__m128i*)ptr) + 0); // r0 g0 b0 a0 r1 g1 b1 a1
+            _23 = _mm_loadu_si128(((__m128i*)ptr) + 1); // r2 g2 b2 a2 r3 g3 b3 a3
+        }
+
+        auto _02 = _mm_unpacklo_epi16(_01, _23),  // r0 r2 g0 g2 b0 b2 a0 a2
+             _13 = _mm_unpackhi_epi16(_01, _23);  // r1 r3 g1 g3 b1 b3 a1 a3
+
+        auto rg = _mm_unpacklo_epi16(_02, _13),  // r0 r1 r2 r3 g0 g1 g2 g3
+             ba = _mm_unpackhi_epi16(_02, _13);  // b0 b1 b2 b3 a0 a1 a2 a3
+
+        *r = unaligned_load<U16>((uint16_t*)&rg + 0);
+        *g = unaligned_load<U16>((uint16_t*)&rg + 4);
+        *b = unaligned_load<U16>((uint16_t*)&ba + 0);
+        *a = unaligned_load<U16>((uint16_t*)&ba + 4);
+    }
+
+    SI void store4(uint16_t* ptr, size_t tail, U16 r, U16 g, U16 b, U16 a) {
+        auto rg = _mm_unpacklo_epi16(widen_cast<__m128i>(r), widen_cast<__m128i>(g)),
+             ba = _mm_unpacklo_epi16(widen_cast<__m128i>(b), widen_cast<__m128i>(a));
+
+        if (__builtin_expect(tail, 0)) {
+            auto dst = (double*)ptr;
+            if (  true  ) { _mm_storel_pd(dst + 0, _mm_unpacklo_epi32(rg, ba)); }
+            if (tail > 1) { _mm_storeh_pd(dst + 1, _mm_unpacklo_epi32(rg, ba)); }
+            if (tail > 2) { _mm_storel_pd(dst + 2, _mm_unpackhi_epi32(rg, ba)); }
+        } else {
+            _mm_storeu_si128((__m128i*)ptr + 0, _mm_unpacklo_epi32(rg, ba));
+            _mm_storeu_si128((__m128i*)ptr + 1, _mm_unpackhi_epi32(rg, ba));
+        }
+    }
+
+    SI void load4(const float* ptr, size_t tail, F* r, F* g, F* b, F* a) {
+        F _0, _1, _2, _3;
+        if (__builtin_expect(tail, 0)) {
+            _1 = _2 = _3 = _mm_setzero_si128();
+            if (  true  ) { _0 = _mm_loadu_ps(ptr + 0); }
+            if (tail > 1) { _1 = _mm_loadu_ps(ptr + 4); }
+            if (tail > 2) { _2 = _mm_loadu_ps(ptr + 8); }
+        } else {
+            _0 = _mm_loadu_ps(ptr + 0);
+            _1 = _mm_loadu_ps(ptr + 4);
+            _2 = _mm_loadu_ps(ptr + 8);
+            _3 = _mm_loadu_ps(ptr +12);
+        }
+        _MM_TRANSPOSE4_PS(_0,_1,_2,_3);
+        *r = _0;
+        *g = _1;
+        *b = _2;
+        *a = _3;
+    }
+
+    SI void store4(float* ptr, size_t tail, F r, F g, F b, F a) {
+        _MM_TRANSPOSE4_PS(r,g,b,a);
+        if (__builtin_expect(tail, 0)) {
+            if (  true  ) { _mm_storeu_ps(ptr + 0, r); }
+            if (tail > 1) { _mm_storeu_ps(ptr + 4, g); }
+            if (tail > 2) { _mm_storeu_ps(ptr + 8, b); }
+        } else {
+            _mm_storeu_ps(ptr + 0, r);
+            _mm_storeu_ps(ptr + 4, g);
+            _mm_storeu_ps(ptr + 8, b);
+            _mm_storeu_ps(ptr +12, a);
+        }
+    }
+#endif
+
+// We need to be a careful with casts.
+// (F)x means cast x to float in the portable path, but bit_cast x to float in the others.
+// These named casts and bit_cast() are always what they seem to be.
+#if defined(JUMPER)
+    SI F   cast  (U32 v) { return      __builtin_convertvector((I32)v,   F); }
+    SI U32 trunc_(F   v) { return (U32)__builtin_convertvector(     v, I32); }
+    SI U32 expand(U16 v) { return      __builtin_convertvector(     v, U32); }
+    SI U32 expand(U8  v) { return      __builtin_convertvector(     v, U32); }
+#else
+    SI F   cast  (U32 v) { return   (F)v; }
+    SI U32 trunc_(F   v) { return (U32)v; }
+    SI U32 expand(U16 v) { return (U32)v; }
+    SI U32 expand(U8  v) { return (U32)v; }
+#endif
+
+template <typename V>
+SI V if_then_else(I32 c, V t, V e) {
+    return bit_cast<V>(if_then_else(c, bit_cast<F>(t), bit_cast<F>(e)));
+}
+
+SI U16 bswap(U16 x) {
+#if defined(JUMPER) && defined(__SSE2__) && !defined(__AVX__)
+    // Somewhat inexplicably Clang decides to do (x<<8) | (x>>8) in 32-bit lanes
+    // when generating code for SSE2 and SSE4.1.  We'll do it manually...
+    auto v = widen_cast<__m128i>(x);
+    v = _mm_slli_epi16(v,8) | _mm_srli_epi16(v,8);
+    return unaligned_load<U16>(&v);
+#else
+    return (x<<8) | (x>>8);
+#endif
+}
+
+SI F fract(F v) { return v - floor_(v); }
+
+// See http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html.
+SI F approx_log2(F x) {
+    // e - 127 is a fair approximation of log2(x) in its own right...
+    F e = cast(bit_cast<U32>(x)) * (1.0f / (1<<23));
+
+    // ... but using the mantissa to refine its error is _much_ better.
+    F m = bit_cast<F>((bit_cast<U32>(x) & 0x007fffff) | 0x3f000000);
+    return e
+         - 124.225514990f
+         -   1.498030302f * m
+         -   1.725879990f / (0.3520887068f + m);
+}
+SI F approx_pow2(F x) {
+    F f = fract(x);
+    return bit_cast<F>(round(1.0f * (1<<23),
+                             x + 121.274057500f
+                               -   1.490129070f * f
+                               +  27.728023300f / (4.84252568f - f)));
+}
+
+SI F approx_powf(F x, F y) {
+    return approx_pow2(approx_log2(x) * y);
+}
+
+SI F from_half(U16 h) {
+#if defined(JUMPER) && defined(__aarch64__)
+    return vcvt_f32_f16(h);
+
+#elif defined(JUMPER) && defined(__arm__)
+    auto v = widen_cast<uint16x4_t>(h);
+    return vget_low_f32(vcvt_f32_f16(v));
+
+#elif defined(JUMPER) && defined(__AVX2__)
+    return _mm256_cvtph_ps(h);
+
+#else
+    // Remember, a half is 1-5-10 (sign-exponent-mantissa) with 15 exponent bias.
+    U32 sem = expand(h),
+        s   = sem & 0x8000,
+         em = sem ^ s;
+
+    // Convert to 1-8-23 float with 127 bias, flushing denorm halfs (including zero) to zero.
+    auto denorm = (I32)em < 0x0400;      // I32 comparison is often quicker, and always safe here.
+    return if_then_else(denorm, F(0)
+                              , bit_cast<F>( (s<<16) + (em<<13) + ((127-15)<<23) ));
+#endif
+}
+
+SI U16 to_half(F f) {
+#if defined(JUMPER) && defined(__aarch64__)
+    return vcvt_f16_f32(f);
+
+#elif defined(JUMPER) && defined(__arm__)
+    auto v = widen_cast<float32x4_t>(f);
+    uint16x4_t h = vcvt_f16_f32(v);
+    return unaligned_load<U16>(&h);
+
+#elif defined(JUMPER) && defined(__AVX2__)
+    return _mm256_cvtps_ph(f, _MM_FROUND_CUR_DIRECTION);
+
+#else
+    // Remember, a float is 1-8-23 (sign-exponent-mantissa) with 127 exponent bias.
+    U32 sem = bit_cast<U32>(f),
+        s   = sem & 0x80000000,
+         em = sem ^ s;
+
+    // Convert to 1-5-10 half with 15 bias, flushing denorm halfs (including zero) to zero.
+    auto denorm = (I32)em < 0x38800000;  // I32 comparison is often quicker, and always safe here.
+    return pack(if_then_else(denorm, U32(0)
+                                   , (s>>16) + (em>>13) - ((127-15)<<10)));
+#endif
+}
+
+
+
+#endif//SkJumper_vectors_DEFINED
diff --git a/src/jumper/build_stages.py b/src/jumper/build_stages.py
index fd50f1a..02ebab0 100755
--- a/src/jumper/build_stages.py
+++ b/src/jumper/build_stages.py
@@ -9,83 +9,106 @@
 import subprocess
 import sys
 
-#clang = ['clang++']
-clang = ['ccache', 'clang-4.0', '-x', 'c++']
+clang   = sys.argv[1] if len(sys.argv) > 1 else 'clang-4.0'
+objdump = sys.argv[2] if len(sys.argv) > 2 else 'gobjdump'
+ccache  = sys.argv[3] if len(sys.argv) > 3 else 'ccache'
 
-ndk = '/Users/mtklein/brew/opt/android-ndk/'
-objdump = 'gobjdump'
+clang = [ccache, clang, '-x', 'c++']
 
-#ndk = '/home/mtklein/ndk/'
-#objdump = '/home/mtklein/binutils-2.27/binutils/objdump'
 
 cflags = ['-std=c++11', '-Os', '-DJUMPER',
-          '-fomit-frame-pointer', '-ffp-contract=fast' ]
+          '-momit-leaf-frame-pointer', '-ffp-contract=fast',
+          '-fno-exceptions', '-fno-rtti', '-fno-unwind-tables']
 
-sse2 = '-mno-red-zone -msse2 -mno-sse3 -mno-ssse3 -mno-sse4.1'.split()
+win = ['-DWIN', '-mno-red-zone']
+sse2 = ['-msse2', '-mno-sse3', '-mno-ssse3', '-mno-sse4.1']
 subprocess.check_call(clang + cflags + sse2 +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'sse2.o'])
-subprocess.check_call(clang + cflags + sse2 + ['-DWIN'] +
+subprocess.check_call(clang + cflags + sse2 + win +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'win_sse2.o'])
 
-sse41 = '-mno-red-zone -msse4.1'.split()
+ssse3 = ['-mssse3', '-mno-sse4.1']
+subprocess.check_call(clang + cflags + ssse3 +
+                      ['-c', 'src/jumper/SkJumper_stages_lowp.cpp'] +
+                      ['-o', 'lowp_ssse3.o'])
+subprocess.check_call(clang + cflags + ssse3 + win +
+                      ['-c', 'src/jumper/SkJumper_stages_lowp.cpp'] +
+                      ['-o', 'win_lowp_ssse3.o'])
+
+sse41 = ['-msse4.1']
 subprocess.check_call(clang + cflags + sse41 +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'sse41.o'])
-subprocess.check_call(clang + cflags + sse41 + ['-DWIN'] +
+subprocess.check_call(clang + cflags + sse41 + win +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'win_sse41.o'])
 
-avx = '-mno-red-zone -mavx'.split()
+avx = ['-mavx']
 subprocess.check_call(clang + cflags + avx +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'avx.o'])
-subprocess.check_call(clang + cflags + avx + ['-DWIN'] +
+subprocess.check_call(clang + cflags + avx + win +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'win_avx.o'])
 
-hsw = '-mno-red-zone -mavx2 -mfma -mf16c'.split()
+hsw = ['-mavx2', '-mfma', '-mf16c']
 subprocess.check_call(clang + cflags + hsw +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'hsw.o'])
-subprocess.check_call(clang + cflags + hsw + ['-DWIN'] +
+subprocess.check_call(clang + cflags + hsw + win +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'win_hsw.o'])
 
-aarch64 = [
-    '--target=aarch64-linux-android',
-    '--sysroot=' + ndk + 'platforms/android-21/arch-arm64',
-]
+aarch64 = [ '--target=aarch64' ]
 subprocess.check_call(clang + cflags + aarch64 +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'aarch64.o'])
 
 vfp4 = [
-    '--target=armv7a-linux-android',
-    '--sysroot=' + ndk + 'platforms/android-18/arch-arm',
+    '--target=armv7a-linux-gnueabihf',
     '-mfpu=neon-vfpv4',
-    '-mfloat-abi=hard',
 ]
 subprocess.check_call(clang + cflags + vfp4 +
                       ['-c', 'src/jumper/SkJumper_stages.cpp'] +
                       ['-o', 'vfp4.o'])
 
-def parse_object_file(dot_o, array_type, target=None):
+def parse_object_file(dot_o, directive, target=None):
+  globl, hidden, label, comment, align = \
+      '.globl', 'HIDDEN', ':', '// ', 'BALIGN'
+  if 'win' in dot_o:
+    globl, hidden, label, comment, align = \
+        'PUBLIC', '', ' LABEL PROC', '; ', 'ALIGN '
+
   cmd = [objdump]
   if target:
     cmd += ['--target', target]
 
   # Look for sections we know we can't handle.
   section_headers = subprocess.check_output(cmd + ['-h', dot_o])
-  for snippet in ['.literal', '.const', '.rodata']:
+  for snippet in ['.rodata']:
     if snippet in section_headers:
       print >>sys.stderr, 'Found %s in section.' % snippet
       assert snippet not in section_headers
 
+  if directive == '.long':
+    disassemble = ['-d', dot_o]
+    dehex = lambda h: '0x'+h
+  else:
+    # x86-64... as long as we're using %rip-relative addressing,
+    # literal sections should be fine to just dump in with .text.
+    disassemble = ['-d',               # DO NOT USE -D.
+                   '-z',               # Print zero bytes instead of ...
+                   '--insn-width=11',
+                   '-j', '.text',
+                   '-j', '.literal4',
+                   '-j', '.literal16',
+                   '-j', '.const',
+                   dot_o]
+    dehex = lambda h: str(int(h,16))
+
   # Ok.  Let's disassemble.
-  active = False
-  disassemble = ['-d', '--insn-width=10', dot_o]
   for line in subprocess.check_output(cmd + disassemble).split('\n'):
     line = line.strip()
 
@@ -95,14 +118,26 @@
     # E.g. 00000000000003a4 <_load_f16>:
     m = re.match('''[0-9a-f]+ <_?(.*)>:''', line)
     if m:
-      if active:
-        print '};'
       print
-      print 'CODE const', array_type, m.group(1) + '[] = {'
-      active = True
+      sym = m.group(1)
+      if sym.startswith('.literal'):  # .literal4, .literal16, etc
+        print sym.replace('.literal', align)
+      elif sym.startswith('.const'):  # 32-byte constants
+        print align + '32'
+      elif not sym.startswith('sk_'):
+        print >>sys.stderr, "build_stages.py can't handle '%s' (yet?)." % sym
+        assert sym.startswith('sk_')
+      else:  # a stage function
+        if hidden:
+          print hidden + ' _' + sym
+        print globl + ' _' + sym
+        if 'win' not in dot_o:
+          print 'FUNCTION(_' + sym + ')'
+        print '_' + sym + label
       continue
 
     columns = line.split('\t')
+   #print >>sys.stderr, columns
     code = columns[1]
     if len(columns) >= 4:
       inst = columns[2]
@@ -113,54 +148,79 @@
         inst, args = columns[2].split(' ', 1)
     code, inst, args = code.strip(), inst.strip(), args.strip()
 
-    dehex = lambda x: '0x'+x
-    if array_type == 'uint8_t':
-      dehex = lambda x: str(int(x, 16))
+    hexed = ','.join(dehex(x) for x in code.split(' '))
+    print '  ' + directive + '  ' + hexed + ' '*(36-len(hexed)) + \
+          comment + inst + (' '*(14-len(inst)) + args if args else '')
 
-    hexed = ''.join(dehex(x) + ',' for x in code.split(' '))
-    print '  ' + hexed + ' '*(40-len(hexed)) + \
-          '//' + inst  + (' '*(14-len(inst)) + args if args else '')
-  print '};'
+sys.stdout = open('src/jumper/SkJumper_generated.S', 'w')
 
-sys.stdout = open('src/jumper/SkJumper_generated.cpp', 'w')
+print '''# Copyright 2017 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 
-print '''/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-// This file is generated semi-automatically with this command:
-//   $ src/jumper/build_stages.py
-
-#include <stdint.h>
-
-#if defined(_MSC_VER)
-    #pragma section("code", read,execute)
-    #define CODE extern "C" __declspec(allocate("code"))
-#elif defined(__MACH__)
-    #define CODE extern "C" __attribute__((section("__TEXT,__text")))
-#else
-    #define CODE extern "C" __attribute__((section(".text")))
-#endif
+# This file is generated semi-automatically with this command:
+#   $ src/jumper/build_stages.py
 '''
+print '#if defined(__MACH__)'
+print '    #define HIDDEN .private_extern'
+print '    #define FUNCTION(name)'
+print '    #define BALIGN4  .align 2'
+print '    #define BALIGN16 .align 4'
+print '    #define BALIGN32 .align 5'
+print '#else'
+print '    .section .note.GNU-stack,"",%progbits'
+print '    #define HIDDEN .hidden'
+print '    #define FUNCTION(name) .type name,%function'
+print '    #define BALIGN4  .balign 4'
+print '    #define BALIGN16 .balign 16'
+print '    #define BALIGN32 .balign 32'
+print '#endif'
+
+print '.text'
 print '#if defined(__aarch64__)'
-parse_object_file('aarch64.o', 'uint32_t')
+print 'BALIGN4'
+parse_object_file('aarch64.o', '.long')
 
 print '#elif defined(__arm__)'
-parse_object_file('vfp4.o', 'uint32_t', target='elf32-littlearm')
+print 'BALIGN4'
+parse_object_file('vfp4.o', '.long', target='elf32-littlearm')
 
 print '#elif defined(__x86_64__)'
-parse_object_file('hsw.o',   'uint8_t')
-parse_object_file('avx.o',   'uint8_t')
-parse_object_file('sse41.o', 'uint8_t')
-parse_object_file('sse2.o',  'uint8_t')
-
-print '#elif defined(_M_X64)'
-parse_object_file('win_hsw.o',   'uint8_t')
-parse_object_file('win_avx.o',   'uint8_t')
-parse_object_file('win_sse41.o', 'uint8_t')
-parse_object_file('win_sse2.o',  'uint8_t')
+print 'BALIGN32'
+parse_object_file('hsw.o',   '.byte')
+print 'BALIGN32'
+parse_object_file('avx.o',   '.byte')
+print 'BALIGN32'
+parse_object_file('sse41.o', '.byte')
+print 'BALIGN32'
+parse_object_file('sse2.o',  '.byte')
+print 'BALIGN32'
+parse_object_file('lowp_ssse3.o',  '.byte')
 
 print '#endif'
+
+sys.stdout = open('src/jumper/SkJumper_generated_win.S', 'w')
+print '''; Copyright 2017 Google Inc.
+;
+; Use of this source code is governed by a BSD-style license that can be
+; found in the LICENSE file.
+
+; This file is generated semi-automatically with this command:
+;   $ src/jumper/build_stages.py
+'''
+
+print 'IFDEF RAX'
+print "_text32 SEGMENT ALIGN(32) 'CODE'"
+print 'ALIGN 32'
+parse_object_file('win_hsw.o',   'DB')
+print 'ALIGN 32'
+parse_object_file('win_avx.o',   'DB')
+print 'ALIGN 32'
+parse_object_file('win_sse41.o', 'DB')
+print 'ALIGN 32'
+parse_object_file('win_sse2.o',  'DB')
+print 'ALIGN 32'
+parse_object_file('win_lowp_ssse3.o',  'DB')
+print 'ENDIF'
+print 'END'
diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp
index 8a6cb52..6e5582a 100644
--- a/src/lazy/SkDiscardableMemoryPool.cpp
+++ b/src/lazy/SkDiscardableMemoryPool.cpp
@@ -5,13 +5,13 @@
  * found in the LICENSE file.
  */
 
-#include "SkDiscardableMemory.h"
 #include "SkDiscardableMemoryPool.h"
-#include "SkImageGenerator.h"
+#include "SkDiscardableMemory.h"
+#include "SkMakeUnique.h"
 #include "SkMalloc.h"
 #include "SkMutex.h"
-#include "SkOnce.h"
 #include "SkTInternalLList.h"
+#include "SkTemplates.h"
 
 // Note:
 // A PoolDiscardableMemory is memory that is counted in a pool.
@@ -27,13 +27,13 @@
  */
 class DiscardableMemoryPool : public SkDiscardableMemoryPool {
 public:
-    /**
-     *  Without mutex, will be not be thread safe.
-     */
-    DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = nullptr);
+    DiscardableMemoryPool(size_t budget);
     ~DiscardableMemoryPool() override;
 
-    SkDiscardableMemory* create(size_t bytes) override;
+    std::unique_ptr<SkDiscardableMemory> make(size_t bytes);
+    SkDiscardableMemory* create(size_t bytes) override {
+        return this->make(bytes).release();  // TODO: change API
+    }
 
     size_t getRAMUsed() override;
     void setRAMBudget(size_t budget) override;
@@ -53,7 +53,7 @@
     #endif  // SK_LAZY_CACHE_STATS
 
 private:
-    SkBaseMutex* fMutex;
+    SkMutex      fMutex;
     size_t       fBudget;
     size_t       fUsed;
     SkTInternalLList<PoolDiscardableMemory> fList;
@@ -61,7 +61,7 @@
     /** Function called to free memory if needed */
     void dumpDownTo(size_t budget);
     /** called by DiscardableMemoryPool upon destruction */
-    void free(PoolDiscardableMemory* dm);
+    void removeFromPool(PoolDiscardableMemory* dm);
     /** called by DiscardableMemoryPool::lock() */
     bool lock(PoolDiscardableMemory* dm);
     /** called by DiscardableMemoryPool::unlock() */
@@ -78,8 +78,7 @@
  */
 class PoolDiscardableMemory : public SkDiscardableMemory {
 public:
-    PoolDiscardableMemory(DiscardableMemoryPool* pool,
-                            void* pointer, size_t bytes);
+    PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool, SkAutoFree pointer, size_t bytes);
     ~PoolDiscardableMemory() override;
     bool lock() override;
     void* data() override;
@@ -87,29 +86,24 @@
     friend class DiscardableMemoryPool;
 private:
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
-    DiscardableMemoryPool* const fPool;
+    sk_sp<DiscardableMemoryPool> fPool;
     bool                         fLocked;
-    void*                        fPointer;
+    SkAutoFree                   fPointer;
     const size_t                 fBytes;
 };
 
-PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
-                                             void* pointer,
+PoolDiscardableMemory::PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,
+                                             SkAutoFree pointer,
                                              size_t bytes)
-    : fPool(pool)
-    , fLocked(true)
-    , fPointer(pointer)
-    , fBytes(bytes) {
+        : fPool(std::move(pool)), fLocked(true), fPointer(std::move(pointer)), fBytes(bytes) {
     SkASSERT(fPool != nullptr);
     SkASSERT(fPointer != nullptr);
     SkASSERT(fBytes > 0);
-    fPool->ref();
 }
 
 PoolDiscardableMemory::~PoolDiscardableMemory() {
     SkASSERT(!fLocked); // contract for SkDiscardableMemory
-    fPool->free(this);
-    fPool->unref();
+    fPool->removeFromPool(this);
 }
 
 bool PoolDiscardableMemory::lock() {
@@ -119,7 +113,7 @@
 
 void* PoolDiscardableMemory::data() {
     SkASSERT(fLocked); // contract for SkDiscardableMemory
-    return fPointer;
+    return fPointer.get();
 }
 
 void PoolDiscardableMemory::unlock() {
@@ -129,10 +123,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
-                                             SkBaseMutex* mutex)
-    : fMutex(mutex)
-    , fBudget(budget)
+DiscardableMemoryPool::DiscardableMemoryPool(size_t budget)
+    : fBudget(budget)
     , fUsed(0) {
     #if SK_LAZY_CACHE_STATS
     fCacheHits = 0;
@@ -147,20 +139,17 @@
 }
 
 void DiscardableMemoryPool::dumpDownTo(size_t budget) {
-    if (fMutex != nullptr) {
-        fMutex->assertHeld();
-    }
+    fMutex.assertHeld();
     if (fUsed <= budget) {
         return;
     }
-    typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
+    using Iter = SkTInternalLList<PoolDiscardableMemory>::Iter;
     Iter iter;
     PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
     while ((fUsed > budget) && (cur)) {
         if (!cur->fLocked) {
             PoolDiscardableMemory* dm = cur;
             SkASSERT(dm->fPointer != nullptr);
-            sk_free(dm->fPointer);
             dm->fPointer = nullptr;
             SkASSERT(fUsed >= dm->fBytes);
             fUsed -= dm->fBytes;
@@ -174,25 +163,23 @@
     }
 }
 
-SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
-    void* addr = sk_malloc_flags(bytes, 0);
+std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) {
+    SkAutoFree addr(sk_malloc_flags(bytes, 0));
     if (nullptr == addr) {
         return nullptr;
     }
-    PoolDiscardableMemory* dm = new PoolDiscardableMemory(this, addr, bytes);
+    auto dm = skstd::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes);
     SkAutoMutexAcquire autoMutexAcquire(fMutex);
-    fList.addToHead(dm);
+    fList.addToHead(dm.get());
     fUsed += bytes;
     this->dumpDownTo(fBudget);
-    return dm;
+    return std::move(dm);
 }
 
-void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
+void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) {
     SkAutoMutexAcquire autoMutexAcquire(fMutex);
     // This is called by dm's destructor.
     if (dm->fPointer != nullptr) {
-        sk_free(dm->fPointer);
-        dm->fPointer = nullptr;
         SkASSERT(fUsed >= dm->fBytes);
         fUsed -= dm->fBytes;
         fList.remove(dm);
@@ -242,18 +229,13 @@
 
 }  // namespace
 
-SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
-    return new DiscardableMemoryPool(size, mutex);
+sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) {
+    return sk_make_sp<DiscardableMemoryPool>(size);
 }
 
-SK_DECLARE_STATIC_MUTEX(gMutex);
-
 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
-    static SkOnce once;
-    static SkDiscardableMemoryPool* global;
-    once([]{
-        global = SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
-                                                 &gMutex);
-    });
+    // Intentionally leak this global pool.
+    static SkDiscardableMemoryPool* global =
+            new DiscardableMemoryPool(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE);
     return global;
 }
diff --git a/src/lazy/SkDiscardableMemoryPool.h b/src/lazy/SkDiscardableMemoryPool.h
index 92ba48b..facdf3d 100644
--- a/src/lazy/SkDiscardableMemoryPool.h
+++ b/src/lazy/SkDiscardableMemoryPool.h
@@ -50,10 +50,8 @@
     /**
      *  This non-global pool can be used for unit tests to verify that
      *  the pool works.
-     *  Without mutex, will be not be thread safe.
      */
-    static SkDiscardableMemoryPool* Create(
-            size_t size, SkBaseMutex* mutex = nullptr);
+    static sk_sp<SkDiscardableMemoryPool> Make(size_t size);
 };
 
 /**
diff --git a/src/opts/SkBlend_opts.h b/src/opts/SkBlend_opts.h
index 1da4c4f..86ae502 100644
--- a/src/opts/SkBlend_opts.h
+++ b/src/opts/SkBlend_opts.h
@@ -15,7 +15,7 @@
 #include "SkNx.h"
 #include "SkPM4fPriv.h"
 
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
     #include <immintrin.h>
 #endif
 
@@ -38,7 +38,7 @@
     srcover_srgb_srgb_1(dst  , *src  );
 }
 
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
 
     static inline __m128i load(const uint32_t* p) {
         return _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
@@ -48,124 +48,35 @@
         _mm_storeu_si128(reinterpret_cast<__m128i*>(p), v);
     }
 
-    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
-
-        static void srcover_srgb_srgb(
+    static void srcover_srgb_srgb(
             uint32_t* dst, const uint32_t* const srcStart, int ndst, const int nsrc) {
-            const __m128i alphaMask = _mm_set1_epi32(0xFF000000);
-            while (ndst > 0) {
-                int count = SkTMin(ndst, nsrc);
-                ndst -= count;
-                const uint32_t* src = srcStart;
-                const uint32_t* end = dst + (count & ~3);
-                ptrdiff_t delta = src - dst;
+        const __m128i alphaMask = _mm_set1_epi32(0xFF000000);
+        while (ndst > 0) {
+            int count = SkTMin(ndst, nsrc);
+            ndst -= count;
+            const uint32_t* src = srcStart;
+            const uint32_t* end = dst + (count & ~3);
 
-                while (dst < end) {
-                    __m128i pixels = load(src);
-                    if (_mm_testc_si128(pixels, alphaMask)) {
-                         uint32_t* start = dst;
-                        do {
-                            store(dst, pixels);
-                            dst += 4;
-                        } while (dst < end
-                                 && _mm_testc_si128(pixels = load(dst + delta), alphaMask));
-                        src += dst - start;
-                    } else if (_mm_testz_si128(pixels, alphaMask)) {
-                        do {
-                            dst += 4;
-                            src += 4;
-                        } while (dst < end
-                                 && _mm_testz_si128(pixels = load(src), alphaMask));
-                    } else {
-                        uint32_t* start = dst;
-                        do {
-                            srcover_srgb_srgb_4(dst, dst + delta);
-                            dst += 4;
-                        } while (dst < end
-                                 && _mm_testnzc_si128(pixels = load(dst + delta), alphaMask));
-                        src += dst - start;
-                    }
-                }
-
-                count = count & 3;
-                while (count-- > 0) {
-                    srcover_srgb_srgb_1(dst++, *src++);
-                }
-            }
-        }
-    #else
-    // SSE2 versions
-
-        // Note: In the next three comparisons a group of 4 pixels is converted to a group of
-        // "signed" pixels because the sse2 does not have an unsigned comparison.
-        // Make it so that we can use the signed comparison operators by biasing
-        // 0x00xxxxxx to 0x80xxxxxxx which is the smallest values and biasing 0xffxxxxxx to
-        // 0x7fxxxxxx which is the largest set of values.
-        static inline bool check_opaque_alphas(__m128i pixels) {
-            __m128i signedPixels = _mm_xor_si128(pixels, _mm_set1_epi32(0x80000000));
-            int mask =
-                _mm_movemask_epi8(
-                    _mm_cmplt_epi32(signedPixels, _mm_set1_epi32(0x7F000000)));
-            return mask == 0;
-        }
-
-        static inline bool check_transparent_alphas(__m128i pixels) {
-            __m128i signedPixels = _mm_xor_si128(pixels, _mm_set1_epi32(0x80000000));
-            int mask =
-                _mm_movemask_epi8(
-                    _mm_cmpgt_epi32(signedPixels, _mm_set1_epi32(0x80FFFFFF)));
-            return mask == 0;
-        }
-
-        static inline bool check_partial_alphas(__m128i pixels) {
-            __m128i signedPixels = _mm_xor_si128(pixels, _mm_set1_epi32(0x80000000));
-            __m128i opaque       = _mm_cmplt_epi32(signedPixels, _mm_set1_epi32(0x7F000000));
-            __m128i transparent  = _mm_cmpgt_epi32(signedPixels, _mm_set1_epi32(0x80FFFFFF));
-            int mask             = _mm_movemask_epi8(_mm_xor_si128(opaque, transparent));
-            return mask == 0;
-        }
-
-        static void srcover_srgb_srgb(
-            uint32_t* dst, const uint32_t* const srcStart, int ndst, const int nsrc) {
-            while (ndst > 0) {
-                int count = SkTMin(ndst, nsrc);
-                ndst -= count;
-                const uint32_t* src = srcStart;
-                const uint32_t* end = dst + (count & ~3);
-                const ptrdiff_t delta = src - dst;
-
+            while (dst < end) {
                 __m128i pixels = load(src);
-                do {
-                    if (check_opaque_alphas(pixels)) {
-                        uint32_t* start = dst;
-                        do {
-                            store(dst, pixels);
-                            dst += 4;
-                        } while (dst < end && check_opaque_alphas((pixels = load(dst + delta))));
-                        src += dst - start;
-                    } else if (check_transparent_alphas(pixels)) {
-                        const uint32_t* start = dst;
-                        do {
-                            dst += 4;
-                        } while (dst < end && check_transparent_alphas(pixels = load(dst + delta)));
-                        src += dst - start;
-                    } else {
-                        const uint32_t* start = dst;
-                        do {
-                            srcover_srgb_srgb_4(dst, dst + delta);
-                            dst += 4;
-                        } while (dst < end && check_partial_alphas(pixels = load(dst + delta)));
-                        src += dst - start;
-                    }
-                } while (dst < end);
 
-                count = count & 3;
-                while (count-- > 0) {
-                    srcover_srgb_srgb_1(dst++, *src++);
+                if (_mm_testc_si128(pixels, alphaMask)) {
+                    store(dst, pixels);
+                } else if (!_mm_testz_si128(pixels, alphaMask)) {
+                    srcover_srgb_srgb_4(dst, src);
                 }
+
+                dst += 4;
+                src += 4;
+            }
+
+            count = count & 3;
+            while (count-- > 0) {
+                srcover_srgb_srgb_1(dst++, *src++);
             }
         }
-    #endif
+    }
+
 #else
 
     static void srcover_srgb_srgb(
diff --git a/src/opts/SkBlitRow_opts.h b/src/opts/SkBlitRow_opts.h
index 2a123f2..74dead5 100644
--- a/src/opts/SkBlitRow_opts.h
+++ b/src/opts/SkBlitRow_opts.h
@@ -37,6 +37,45 @@
     });
 }
 
+#if defined(SK_ARM_HAS_NEON)
+
+// Return a uint8x8_t value, r, computed as r[i] = SkMulDiv255Round(x[i], y[i]), where r[i], x[i],
+// y[i] are the i-th lanes of the corresponding NEON vectors.
+static inline uint8x8_t SkMulDiv255Round_neon8(uint8x8_t x, uint8x8_t y) {
+    uint16x8_t prod = vmull_u8(x, y);
+    return vraddhn_u16(prod, vrshrq_n_u16(prod, 8));
+}
+
+// The implementations of SkPMSrcOver below perform alpha blending consistently with
+// SkMulDiv255Round. They compute the color components (numbers in the interval [0, 255]) as:
+//
+//   result_i = src_i + rint(g(src_alpha, dst_i))
+//
+// where g(x, y) = ((255.0 - x) * y) / 255.0 and rint rounds to the nearest integer.
+
+// In this variant of SkPMSrcOver each NEON register, dst.val[i], src.val[i], contains the value
+// of the same color component for 8 consecutive pixels. The result of this function follows the
+// same convention.
+static inline uint8x8x4_t SkPMSrcOver_neon8(uint8x8x4_t dst, uint8x8x4_t src) {
+    uint8x8_t nalphas = vmvn_u8(src.val[3]);
+    uint8x8x4_t result;
+    result.val[0] = vadd_u8(src.val[0], SkMulDiv255Round_neon8(nalphas,  dst.val[0]));
+    result.val[1] = vadd_u8(src.val[1], SkMulDiv255Round_neon8(nalphas,  dst.val[1]));
+    result.val[2] = vadd_u8(src.val[2], SkMulDiv255Round_neon8(nalphas,  dst.val[2]));
+    result.val[3] = vadd_u8(src.val[3], SkMulDiv255Round_neon8(nalphas,  dst.val[3]));
+    return result;
+}
+
+// In this variant of SkPMSrcOver dst and src contain the color components of two consecutive
+// pixels. The return value follows the same convention.
+static inline uint8x8_t SkPMSrcOver_neon2(uint8x8_t dst, uint8x8_t src) {
+    const uint8x8_t alpha_indices = vcreate_u8(0x0707070703030303);
+    uint8x8_t nalphas = vmvn_u8(vtbl1_u8(src, alpha_indices));
+    return vadd_u8(src, SkMulDiv255Round_neon8(nalphas, dst));
+}
+
+#endif
+
 static inline
 void blit_row_s32a_opaque(SkPMColor* dst, const SkPMColor* src, int len, U8CPU alpha) {
     SkASSERT(alpha == 0xFF);
@@ -142,51 +181,52 @@
     }
 
 #elif defined(SK_ARM_HAS_NEON)
-    while (len >= 4) {
-        if ((src[0] | src[1] | src[2] | src[3]) == 0x00000000) {
-            // All 16 source pixels are transparent.  Nothing to do.
-            src += 4;
-            dst += 4;
-            len -= 4;
+    // Do 8-pixels at a time. A 16-pixels at a time version of this code was also tested, but it
+    // underperformed on some of the platforms under test for inputs with frequent transitions of
+    // alpha (corresponding to changes of the conditions [~]alpha_u64 == 0 below). It may be worth
+    // revisiting the situation in the future.
+    while (len >= 8) {
+        // Load 8 pixels in 4 NEON registers. src_col.val[i] will contain the same color component
+        // for 8 consecutive pixels (e.g. src_col.val[3] will contain all alpha components of 8
+        // pixels).
+        uint8x8x4_t src_col = vld4_u8(reinterpret_cast<const uint8_t*>(src));
+        src += 8;
+        len -= 8;
+
+        // We now detect 2 special cases: the first occurs when all alphas are zero (the 8 pixels
+        // are all transparent), the second when all alphas are fully set (they are all opaque).
+        uint8x8_t alphas = src_col.val[3];
+        uint64_t alphas_u64 = vget_lane_u64(vreinterpret_u64_u8(alphas), 0);
+        if (alphas_u64 == 0) {
+            // All pixels transparent.
+            dst += 8;
             continue;
         }
 
-        if ((src[0] & src[1] & src[2] & src[3]) >= 0xFF000000) {
-            // All 16 source pixels are opaque.  SrcOver becomes Src.
-            dst[0] = src[0];
-            dst[1] = src[1];
-            dst[2] = src[2];
-            dst[3] = src[3];
-            src += 4;
-            dst += 4;
-            len -= 4;
+        if (~alphas_u64 == 0) {
+            // All pixels opaque.
+            vst4_u8(reinterpret_cast<uint8_t*>(dst), src_col);
+            dst += 8;
             continue;
         }
 
-        // Load 4 source and destination pixels.
-        auto src0 = vreinterpret_u8_u32(vld1_u32(src+0)),
-             src2 = vreinterpret_u8_u32(vld1_u32(src+2)),
-             dst0 = vreinterpret_u8_u32(vld1_u32(dst+0)),
-             dst2 = vreinterpret_u8_u32(vld1_u32(dst+2));
-
-        // TODO: This math is wrong.
-        const uint8x8_t alphas = vcreate_u8(0x0707070703030303);
-        auto invSA0_w = vsubw_u8(vdupq_n_u16(256), vtbl1_u8(src0, alphas)),
-             invSA2_w = vsubw_u8(vdupq_n_u16(256), vtbl1_u8(src2, alphas));
-
-        auto dstInvSA0 = vmulq_u16(invSA0_w, vmovl_u8(dst0)),
-             dstInvSA2 = vmulq_u16(invSA2_w, vmovl_u8(dst2));
-
-        dst0 = vadd_u8(src0, vshrn_n_u16(dstInvSA0, 8));
-        dst2 = vadd_u8(src2, vshrn_n_u16(dstInvSA2, 8));
-
-        vst1_u32(dst+0, vreinterpret_u32_u8(dst0));
-        vst1_u32(dst+2, vreinterpret_u32_u8(dst2));
-
-        src += 4;
-        dst += 4;
-        len -= 4;
+        uint8x8x4_t dst_col = vld4_u8(reinterpret_cast<uint8_t*>(dst));
+        vst4_u8(reinterpret_cast<uint8_t*>(dst), SkPMSrcOver_neon8(dst_col, src_col));
+        dst += 8;
     }
+
+    // Deal with leftover pixels.
+    for (; len >= 2; len -= 2, src += 2, dst += 2) {
+        uint8x8_t src2 = vld1_u8(reinterpret_cast<const uint8_t*>(src));
+        uint8x8_t dst2 = vld1_u8(reinterpret_cast<const uint8_t*>(dst));
+        vst1_u8(reinterpret_cast<uint8_t*>(dst), SkPMSrcOver_neon2(dst2, src2));
+    }
+
+    if (len != 0) {
+        uint8x8_t result = SkPMSrcOver_neon2(vcreate_u8(*dst), vcreate_u8(*src));
+        vst1_lane_u32(dst, vreinterpret_u32_u8(result), 0);
+    }
+    return;
 #endif
 
     while (len-- > 0) {
diff --git a/src/opts/SkBlitRow_opts_SSE2.cpp b/src/opts/SkBlitRow_opts_SSE2.cpp
index 7ce1fc9..7f03907 100644
--- a/src/opts/SkBlitRow_opts_SSE2.cpp
+++ b/src/opts/SkBlitRow_opts_SSE2.cpp
@@ -103,75 +103,6 @@
     }
 }
 
-void Color32A_D565_SSE2(uint16_t dst[], SkPMColor src, int count, int x, int y) {
-    SkASSERT(count > 0);
-
-    uint32_t src_expand = (SkGetPackedG32(src) << 24) |
-                          (SkGetPackedR32(src) << 13) |
-                          (SkGetPackedB32(src) << 2);
-    unsigned scale = SkAlpha255To256(0xFF - SkGetPackedA32(src)) >> 3;
-
-    // Check if we have enough pixels to run SIMD
-    if (count >= (int)(8 + (((16 - (size_t)dst) & 0x0F) >> 1))) {
-        __m128i* dst_wide;
-        const __m128i src_R_wide = _mm_set1_epi16(SkGetPackedR32(src) << 2);
-        const __m128i src_G_wide = _mm_set1_epi16(SkGetPackedG32(src) << 3);
-        const __m128i src_B_wide = _mm_set1_epi16(SkGetPackedB32(src) << 2);
-        const __m128i scale_wide = _mm_set1_epi16(scale);
-        const __m128i mask_blue  = _mm_set1_epi16(SK_B16_MASK);
-        const __m128i mask_green = _mm_set1_epi16(SK_G16_MASK << SK_G16_SHIFT);
-
-        // Align dst to an even 16 byte address (0-7 pixels)
-        while (((((size_t)dst) & 0x0F) != 0) && (count > 0)) {
-            *dst = SkBlend32_RGB16(src_expand, *dst, scale);
-            dst += 1;
-            count--;
-        }
-
-        dst_wide = reinterpret_cast<__m128i*>(dst);
-        do {
-            // Load eight RGB565 pixels
-            __m128i pixels = _mm_load_si128(dst_wide);
-
-            // Mask out sub-pixels
-            __m128i pixel_R = _mm_srli_epi16(pixels, SK_R16_SHIFT);
-            __m128i pixel_G = _mm_slli_epi16(pixels, SK_R16_BITS);
-            pixel_G = _mm_srli_epi16(pixel_G, SK_R16_BITS + SK_B16_BITS);
-            __m128i pixel_B = _mm_and_si128(pixels, mask_blue);
-
-            // Scale with alpha
-            pixel_R = _mm_mullo_epi16(pixel_R, scale_wide);
-            pixel_G = _mm_mullo_epi16(pixel_G, scale_wide);
-            pixel_B = _mm_mullo_epi16(pixel_B, scale_wide);
-
-            // Add src_X_wide and shift down again
-            pixel_R = _mm_add_epi16(pixel_R, src_R_wide);
-            pixel_R = _mm_srli_epi16(pixel_R, 5);
-            pixel_G = _mm_add_epi16(pixel_G, src_G_wide);
-            pixel_B = _mm_add_epi16(pixel_B, src_B_wide);
-            pixel_B = _mm_srli_epi16(pixel_B, 5);
-
-            // Combine into RGB565 and store
-            pixel_R = _mm_slli_epi16(pixel_R, SK_R16_SHIFT);
-            pixel_G = _mm_and_si128(pixel_G, mask_green);
-            pixels = _mm_or_si128(pixel_R, pixel_G);
-            pixels = _mm_or_si128(pixels, pixel_B);
-            _mm_store_si128(dst_wide, pixels);
-            count -= 8;
-            dst_wide++;
-        } while (count >= 8);
-
-        dst = reinterpret_cast<uint16_t*>(dst_wide);
-    }
-
-    // Small loop to handle remaining pixels.
-    while (count > 0) {
-        *dst = SkBlend32_RGB16(src_expand, *dst, scale);
-        dst += 1;
-        count--;
-    }
-}
-
 // The following (left) shifts cause the top 5 bits of the mask components to
 // line up with the corresponding components in an SkPMColor.
 // Note that the mask's RGB16 order may differ from the SkPMColor order.
@@ -510,481 +441,3 @@
         width--;
     }
 }
-
-/* SSE2 version of S32_D565_Opaque()
- * portable version is in core/SkBlitRow_D16.cpp
- */
-void S32_D565_Opaque_SSE2(uint16_t* SK_RESTRICT dst,
-                          const SkPMColor* SK_RESTRICT src, int count,
-                          U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 == alpha);
-
-    if (count <= 0) {
-        return;
-    }
-
-    if (count >= 8) {
-        while (((size_t)dst & 0x0F) != 0) {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-
-            *dst++ = SkPixel32ToPixel16_ToU16(c);
-            count--;
-        }
-
-        const __m128i* s = reinterpret_cast<const __m128i*>(src);
-        __m128i* d = reinterpret_cast<__m128i*>(dst);
-
-        while (count >= 8) {
-            // Load 8 pixels of src.
-            __m128i src_pixel1 = _mm_loadu_si128(s++);
-            __m128i src_pixel2 = _mm_loadu_si128(s++);
-
-            __m128i d_pixel = SkPixel32ToPixel16_ToU16_SSE2(src_pixel1, src_pixel2);
-            _mm_store_si128(d++, d_pixel);
-            count -= 8;
-        }
-        src = reinterpret_cast<const SkPMColor*>(s);
-        dst = reinterpret_cast<uint16_t*>(d);
-    }
-
-    if (count > 0) {
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            *dst++ = SkPixel32ToPixel16_ToU16(c);
-        } while (--count != 0);
-    }
-}
-
-/* SSE2 version of S32A_D565_Opaque()
- * portable version is in core/SkBlitRow_D16.cpp
- */
-void S32A_D565_Opaque_SSE2(uint16_t* SK_RESTRICT dst,
-                           const SkPMColor* SK_RESTRICT src,
-                           int count, U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 == alpha);
-
-    if (count <= 0) {
-        return;
-    }
-
-    if (count >= 8) {
-        // Make dst 16 bytes alignment
-        while (((size_t)dst & 0x0F) != 0) {
-            SkPMColor c = *src++;
-            if (c) {
-              *dst = SkSrcOver32To16(c, *dst);
-            }
-            dst += 1;
-            count--;
-        }
-
-        const __m128i* s = reinterpret_cast<const __m128i*>(src);
-        __m128i* d = reinterpret_cast<__m128i*>(dst);
-        __m128i var255 = _mm_set1_epi16(255);
-        __m128i r16_mask = _mm_set1_epi16(SK_R16_MASK);
-        __m128i g16_mask = _mm_set1_epi16(SK_G16_MASK);
-        __m128i b16_mask = _mm_set1_epi16(SK_B16_MASK);
-
-        while (count >= 8) {
-            // Load 8 pixels of src.
-            __m128i src_pixel1 = _mm_loadu_si128(s++);
-            __m128i src_pixel2 = _mm_loadu_si128(s++);
-
-            // Check whether src pixels are equal to 0 and get the highest bit
-            // of each byte of result, if src pixels are all zero, src_cmp1 and
-            // src_cmp2 will be 0xFFFF.
-            int src_cmp1 = _mm_movemask_epi8(_mm_cmpeq_epi16(src_pixel1,
-                                             _mm_setzero_si128()));
-            int src_cmp2 = _mm_movemask_epi8(_mm_cmpeq_epi16(src_pixel2,
-                                             _mm_setzero_si128()));
-            if (src_cmp1 == 0xFFFF && src_cmp2 == 0xFFFF) {
-                d++;
-                count -= 8;
-                continue;
-            }
-
-            // Load 8 pixels of dst.
-            __m128i dst_pixel = _mm_load_si128(d);
-
-            // Extract A from src.
-            __m128i sa1 = _mm_slli_epi32(src_pixel1, (24 - SK_A32_SHIFT));
-            sa1 = _mm_srli_epi32(sa1, 24);
-            __m128i sa2 = _mm_slli_epi32(src_pixel2, (24 - SK_A32_SHIFT));
-            sa2 = _mm_srli_epi32(sa2, 24);
-            __m128i sa = _mm_packs_epi32(sa1, sa2);
-
-            // Extract R from src.
-            __m128i sr1 = _mm_slli_epi32(src_pixel1, (24 - SK_R32_SHIFT));
-            sr1 = _mm_srli_epi32(sr1, 24);
-            __m128i sr2 = _mm_slli_epi32(src_pixel2, (24 - SK_R32_SHIFT));
-            sr2 = _mm_srli_epi32(sr2, 24);
-            __m128i sr = _mm_packs_epi32(sr1, sr2);
-
-            // Extract G from src.
-            __m128i sg1 = _mm_slli_epi32(src_pixel1, (24 - SK_G32_SHIFT));
-            sg1 = _mm_srli_epi32(sg1, 24);
-            __m128i sg2 = _mm_slli_epi32(src_pixel2, (24 - SK_G32_SHIFT));
-            sg2 = _mm_srli_epi32(sg2, 24);
-            __m128i sg = _mm_packs_epi32(sg1, sg2);
-
-            // Extract B from src.
-            __m128i sb1 = _mm_slli_epi32(src_pixel1, (24 - SK_B32_SHIFT));
-            sb1 = _mm_srli_epi32(sb1, 24);
-            __m128i sb2 = _mm_slli_epi32(src_pixel2, (24 - SK_B32_SHIFT));
-            sb2 = _mm_srli_epi32(sb2, 24);
-            __m128i sb = _mm_packs_epi32(sb1, sb2);
-
-            // Extract R G B from dst.
-            __m128i dr = _mm_srli_epi16(dst_pixel, SK_R16_SHIFT);
-            dr = _mm_and_si128(dr, r16_mask);
-            __m128i dg = _mm_srli_epi16(dst_pixel, SK_G16_SHIFT);
-            dg = _mm_and_si128(dg, g16_mask);
-            __m128i db = _mm_srli_epi16(dst_pixel, SK_B16_SHIFT);
-            db = _mm_and_si128(db, b16_mask);
-
-            __m128i isa = _mm_sub_epi16(var255, sa); // 255 -sa
-
-            // Calculate R G B of result.
-            // Original algorithm is in SkSrcOver32To16().
-            dr = _mm_add_epi16(sr, SkMul16ShiftRound_SSE2(dr, isa, SK_R16_BITS));
-            dr = _mm_srli_epi16(dr, 8 - SK_R16_BITS);
-            dg = _mm_add_epi16(sg, SkMul16ShiftRound_SSE2(dg, isa, SK_G16_BITS));
-            dg = _mm_srli_epi16(dg, 8 - SK_G16_BITS);
-            db = _mm_add_epi16(sb, SkMul16ShiftRound_SSE2(db, isa, SK_B16_BITS));
-            db = _mm_srli_epi16(db, 8 - SK_B16_BITS);
-
-            // Pack R G B into 16-bit color.
-            __m128i d_pixel = SkPackRGB16_SSE2(dr, dg, db);
-
-            // Store 8 16-bit colors in dst.
-            _mm_store_si128(d++, d_pixel);
-            count -= 8;
-        }
-
-        src = reinterpret_cast<const SkPMColor*>(s);
-        dst = reinterpret_cast<uint16_t*>(d);
-    }
-
-    if (count > 0) {
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            if (c) {
-                *dst = SkSrcOver32To16(c, *dst);
-            }
-            dst += 1;
-        } while (--count != 0);
-    }
-}
-
-void S32_D565_Opaque_Dither_SSE2(uint16_t* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src,
-                                 int count, U8CPU alpha, int x, int y) {
-    SkASSERT(255 == alpha);
-
-    if (count <= 0) {
-        return;
-    }
-
-    if (count >= 8) {
-        while (((size_t)dst & 0x0F) != 0) {
-            DITHER_565_SCAN(y);
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-
-            unsigned dither = DITHER_VALUE(x);
-            *dst++ = SkDitherRGB32To565(c, dither);
-            DITHER_INC_X(x);
-            count--;
-        }
-
-        unsigned short dither_value[8];
-        __m128i dither;
-#ifdef ENABLE_DITHER_MATRIX_4X4
-        const uint8_t* dither_scan = gDitherMatrix_3Bit_4X4[(y) & 3];
-        dither_value[0] = dither_value[4] = dither_scan[(x) & 3];
-        dither_value[1] = dither_value[5] = dither_scan[(x + 1) & 3];
-        dither_value[2] = dither_value[6] = dither_scan[(x + 2) & 3];
-        dither_value[3] = dither_value[7] = dither_scan[(x + 3) & 3];
-#else
-        const uint16_t dither_scan = gDitherMatrix_3Bit_16[(y) & 3];
-        dither_value[0] = dither_value[4] = (dither_scan
-                                             >> (((x) & 3) << 2)) & 0xF;
-        dither_value[1] = dither_value[5] = (dither_scan
-                                             >> (((x + 1) & 3) << 2)) & 0xF;
-        dither_value[2] = dither_value[6] = (dither_scan
-                                             >> (((x + 2) & 3) << 2)) & 0xF;
-        dither_value[3] = dither_value[7] = (dither_scan
-                                             >> (((x + 3) & 3) << 2)) & 0xF;
-#endif
-        dither = _mm_loadu_si128((__m128i*) dither_value);
-
-        const __m128i* s = reinterpret_cast<const __m128i*>(src);
-        __m128i* d = reinterpret_cast<__m128i*>(dst);
-
-        while (count >= 8) {
-            // Load 8 pixels of src.
-            __m128i src_pixel1 = _mm_loadu_si128(s++);
-            __m128i src_pixel2 = _mm_loadu_si128(s++);
-
-            // Extract R from src.
-            __m128i sr1 = _mm_slli_epi32(src_pixel1, (24 - SK_R32_SHIFT));
-            sr1 = _mm_srli_epi32(sr1, 24);
-            __m128i sr2 = _mm_slli_epi32(src_pixel2, (24 - SK_R32_SHIFT));
-            sr2 = _mm_srli_epi32(sr2, 24);
-            __m128i sr = _mm_packs_epi32(sr1, sr2);
-
-            // SkDITHER_R32To565(sr, dither)
-            __m128i sr_offset = _mm_srli_epi16(sr, 5);
-            sr = _mm_add_epi16(sr, dither);
-            sr = _mm_sub_epi16(sr, sr_offset);
-            sr = _mm_srli_epi16(sr, SK_R32_BITS - SK_R16_BITS);
-
-            // Extract G from src.
-            __m128i sg1 = _mm_slli_epi32(src_pixel1, (24 - SK_G32_SHIFT));
-            sg1 = _mm_srli_epi32(sg1, 24);
-            __m128i sg2 = _mm_slli_epi32(src_pixel2, (24 - SK_G32_SHIFT));
-            sg2 = _mm_srli_epi32(sg2, 24);
-            __m128i sg = _mm_packs_epi32(sg1, sg2);
-
-            // SkDITHER_R32To565(sg, dither)
-            __m128i sg_offset = _mm_srli_epi16(sg, 6);
-            sg = _mm_add_epi16(sg, _mm_srli_epi16(dither, 1));
-            sg = _mm_sub_epi16(sg, sg_offset);
-            sg = _mm_srli_epi16(sg, SK_G32_BITS - SK_G16_BITS);
-
-            // Extract B from src.
-            __m128i sb1 = _mm_slli_epi32(src_pixel1, (24 - SK_B32_SHIFT));
-            sb1 = _mm_srli_epi32(sb1, 24);
-            __m128i sb2 = _mm_slli_epi32(src_pixel2, (24 - SK_B32_SHIFT));
-            sb2 = _mm_srli_epi32(sb2, 24);
-            __m128i sb = _mm_packs_epi32(sb1, sb2);
-
-            // SkDITHER_R32To565(sb, dither)
-            __m128i sb_offset = _mm_srli_epi16(sb, 5);
-            sb = _mm_add_epi16(sb, dither);
-            sb = _mm_sub_epi16(sb, sb_offset);
-            sb = _mm_srli_epi16(sb, SK_B32_BITS - SK_B16_BITS);
-
-            // Pack and store 16-bit dst pixel.
-            __m128i d_pixel = SkPackRGB16_SSE2(sr, sg, sb);
-            _mm_store_si128(d++, d_pixel);
-
-            count -= 8;
-            x += 8;
-        }
-
-        src = reinterpret_cast<const SkPMColor*>(s);
-        dst = reinterpret_cast<uint16_t*>(d);
-    }
-
-    if (count > 0) {
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-
-            unsigned dither = DITHER_VALUE(x);
-            *dst++ = SkDitherRGB32To565(c, dither);
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
-
-/* SSE2 version of S32A_D565_Opaque_Dither()
- * portable version is in core/SkBlitRow_D16.cpp
- */
-void S32A_D565_Opaque_Dither_SSE2(uint16_t* SK_RESTRICT dst,
-                                  const SkPMColor* SK_RESTRICT src,
-                                  int count, U8CPU alpha, int x, int y) {
-    SkASSERT(255 == alpha);
-
-    if (count <= 0) {
-        return;
-    }
-
-    if (count >= 8) {
-        while (((size_t)dst & 0x0F) != 0) {
-            DITHER_565_SCAN(y);
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            if (c) {
-                unsigned a = SkGetPackedA32(c);
-
-                int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
-
-                unsigned sr = SkGetPackedR32(c);
-                unsigned sg = SkGetPackedG32(c);
-                unsigned sb = SkGetPackedB32(c);
-                sr = SkDITHER_R32_FOR_565(sr, d);
-                sg = SkDITHER_G32_FOR_565(sg, d);
-                sb = SkDITHER_B32_FOR_565(sb, d);
-
-                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
-                uint32_t dst_expanded = SkExpand_rgb_16(*dst);
-                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
-                // now src and dst expanded are in g:11 r:10 x:1 b:10
-                *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
-            }
-            dst += 1;
-            DITHER_INC_X(x);
-            count--;
-        }
-
-        unsigned short dither_value[8];
-        __m128i dither, dither_cur;
-#ifdef ENABLE_DITHER_MATRIX_4X4
-        const uint8_t* dither_scan = gDitherMatrix_3Bit_4X4[(y) & 3];
-        dither_value[0] = dither_value[4] = dither_scan[(x) & 3];
-        dither_value[1] = dither_value[5] = dither_scan[(x + 1) & 3];
-        dither_value[2] = dither_value[6] = dither_scan[(x + 2) & 3];
-        dither_value[3] = dither_value[7] = dither_scan[(x + 3) & 3];
-#else
-        const uint16_t dither_scan = gDitherMatrix_3Bit_16[(y) & 3];
-        dither_value[0] = dither_value[4] = (dither_scan
-                                             >> (((x) & 3) << 2)) & 0xF;
-        dither_value[1] = dither_value[5] = (dither_scan
-                                             >> (((x + 1) & 3) << 2)) & 0xF;
-        dither_value[2] = dither_value[6] = (dither_scan
-                                             >> (((x + 2) & 3) << 2)) & 0xF;
-        dither_value[3] = dither_value[7] = (dither_scan
-                                             >> (((x + 3) & 3) << 2)) & 0xF;
-#endif
-        dither = _mm_loadu_si128((__m128i*) dither_value);
-
-        const __m128i* s = reinterpret_cast<const __m128i*>(src);
-        __m128i* d = reinterpret_cast<__m128i*>(dst);
-        __m128i var256 = _mm_set1_epi16(256);
-        __m128i r16_mask = _mm_set1_epi16(SK_R16_MASK);
-        __m128i g16_mask = _mm_set1_epi16(SK_G16_MASK);
-        __m128i b16_mask = _mm_set1_epi16(SK_B16_MASK);
-
-        while (count >= 8) {
-            // Load 8 pixels of src and dst.
-            __m128i src_pixel1 = _mm_loadu_si128(s++);
-            __m128i src_pixel2 = _mm_loadu_si128(s++);
-            __m128i dst_pixel = _mm_load_si128(d);
-
-            // Extract A from src.
-            __m128i sa1 = _mm_slli_epi32(src_pixel1, (24 - SK_A32_SHIFT));
-            sa1 = _mm_srli_epi32(sa1, 24);
-            __m128i sa2 = _mm_slli_epi32(src_pixel2, (24 - SK_A32_SHIFT));
-            sa2 = _mm_srli_epi32(sa2, 24);
-            __m128i sa = _mm_packs_epi32(sa1, sa2);
-
-            // Calculate current dither value.
-            dither_cur = _mm_mullo_epi16(dither,
-                                         _mm_add_epi16(sa, _mm_set1_epi16(1)));
-            dither_cur = _mm_srli_epi16(dither_cur, 8);
-
-            // Extract R from src.
-            __m128i sr1 = _mm_slli_epi32(src_pixel1, (24 - SK_R32_SHIFT));
-            sr1 = _mm_srli_epi32(sr1, 24);
-            __m128i sr2 = _mm_slli_epi32(src_pixel2, (24 - SK_R32_SHIFT));
-            sr2 = _mm_srli_epi32(sr2, 24);
-            __m128i sr = _mm_packs_epi32(sr1, sr2);
-
-            // SkDITHER_R32_FOR_565(sr, d)
-            __m128i sr_offset = _mm_srli_epi16(sr, 5);
-            sr = _mm_add_epi16(sr, dither_cur);
-            sr = _mm_sub_epi16(sr, sr_offset);
-
-            // Expand sr.
-            sr = _mm_slli_epi16(sr, 2);
-
-            // Extract G from src.
-            __m128i sg1 = _mm_slli_epi32(src_pixel1, (24 - SK_G32_SHIFT));
-            sg1 = _mm_srli_epi32(sg1, 24);
-            __m128i sg2 = _mm_slli_epi32(src_pixel2, (24 - SK_G32_SHIFT));
-            sg2 = _mm_srli_epi32(sg2, 24);
-            __m128i sg = _mm_packs_epi32(sg1, sg2);
-
-            // sg = SkDITHER_G32_FOR_565(sg, d).
-            __m128i sg_offset = _mm_srli_epi16(sg, 6);
-            sg = _mm_add_epi16(sg, _mm_srli_epi16(dither_cur, 1));
-            sg = _mm_sub_epi16(sg, sg_offset);
-
-            // Expand sg.
-            sg = _mm_slli_epi16(sg, 3);
-
-            // Extract B from src.
-            __m128i sb1 = _mm_slli_epi32(src_pixel1, (24 - SK_B32_SHIFT));
-            sb1 = _mm_srli_epi32(sb1, 24);
-            __m128i sb2 = _mm_slli_epi32(src_pixel2, (24 - SK_B32_SHIFT));
-            sb2 = _mm_srli_epi32(sb2, 24);
-            __m128i sb = _mm_packs_epi32(sb1, sb2);
-
-            // sb = SkDITHER_B32_FOR_565(sb, d).
-            __m128i sb_offset = _mm_srli_epi16(sb, 5);
-            sb = _mm_add_epi16(sb, dither_cur);
-            sb = _mm_sub_epi16(sb, sb_offset);
-
-            // Expand sb.
-            sb = _mm_slli_epi16(sb, 2);
-
-            // Extract R G B from dst.
-            __m128i dr = _mm_srli_epi16(dst_pixel, SK_R16_SHIFT);
-            dr = _mm_and_si128(dr, r16_mask);
-            __m128i dg = _mm_srli_epi16(dst_pixel, SK_G16_SHIFT);
-            dg = _mm_and_si128(dg, g16_mask);
-            __m128i db = _mm_srli_epi16(dst_pixel, SK_B16_SHIFT);
-            db = _mm_and_si128(db, b16_mask);
-
-            // SkAlpha255To256(255 - a) >> 3
-            __m128i isa = _mm_sub_epi16(var256, sa);
-            isa = _mm_srli_epi16(isa, 3);
-
-            dr = _mm_mullo_epi16(dr, isa);
-            dr = _mm_add_epi16(dr, sr);
-            dr = _mm_srli_epi16(dr, 5);
-
-            dg = _mm_mullo_epi16(dg, isa);
-            dg = _mm_add_epi16(dg, sg);
-            dg = _mm_srli_epi16(dg, 5);
-
-            db = _mm_mullo_epi16(db, isa);
-            db = _mm_add_epi16(db, sb);
-            db = _mm_srli_epi16(db, 5);
-
-            // Package and store dst pixel.
-            __m128i d_pixel = SkPackRGB16_SSE2(dr, dg, db);
-            _mm_store_si128(d++, d_pixel);
-
-            count -= 8;
-            x += 8;
-        }
-
-        src = reinterpret_cast<const SkPMColor*>(s);
-        dst = reinterpret_cast<uint16_t*>(d);
-    }
-
-    if (count > 0) {
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            if (c) {
-                unsigned a = SkGetPackedA32(c);
-
-                int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
-
-                unsigned sr = SkGetPackedR32(c);
-                unsigned sg = SkGetPackedG32(c);
-                unsigned sb = SkGetPackedB32(c);
-                sr = SkDITHER_R32_FOR_565(sr, d);
-                sg = SkDITHER_G32_FOR_565(sg, d);
-                sb = SkDITHER_B32_FOR_565(sb, d);
-
-                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
-                uint32_t dst_expanded = SkExpand_rgb_16(*dst);
-                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
-                // now src and dst expanded are in g:11 r:10 x:1 b:10
-                *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
-            }
-            dst += 1;
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
diff --git a/src/opts/SkBlitRow_opts_SSE2.h b/src/opts/SkBlitRow_opts_SSE2.h
index 652ff6e..cb93da6 100644
--- a/src/opts/SkBlitRow_opts_SSE2.h
+++ b/src/opts/SkBlitRow_opts_SSE2.h
@@ -18,24 +18,9 @@
                                const SkPMColor* SK_RESTRICT src,
                                int count, U8CPU alpha);
 
-void Color32A_D565_SSE2(uint16_t dst[], SkPMColor src, int count, int x,
-                        int y);
-
 void SkBlitLCD16Row_SSE2(SkPMColor dst[], const uint16_t src[],
                          SkColor color, int width, SkPMColor);
 void SkBlitLCD16OpaqueRow_SSE2(SkPMColor dst[], const uint16_t src[],
                                SkColor color, int width, SkPMColor opaqueDst);
 
-void S32_D565_Opaque_SSE2(uint16_t* SK_RESTRICT dst,
-                          const SkPMColor* SK_RESTRICT src, int count,
-                          U8CPU alpha, int /*x*/, int /*y*/);
-void S32A_D565_Opaque_SSE2(uint16_t* SK_RESTRICT dst,
-                           const SkPMColor* SK_RESTRICT src,
-                           int count, U8CPU alpha, int /*x*/, int /*y*/);
-void S32_D565_Opaque_Dither_SSE2(uint16_t* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src,
-                                 int count, U8CPU alpha, int x, int y);
-void S32A_D565_Opaque_Dither_SSE2(uint16_t* SK_RESTRICT dst,
-                                  const SkPMColor* SK_RESTRICT src,
-                                  int count, U8CPU alpha, int x, int y);
 #endif
diff --git a/src/opts/SkBlitRow_opts_arm.cpp b/src/opts/SkBlitRow_opts_arm.cpp
index d4b1d0d..543640a 100644
--- a/src/opts/SkBlitRow_opts_arm.cpp
+++ b/src/opts/SkBlitRow_opts_arm.cpp
@@ -10,26 +10,10 @@
 
 #include "SkBlitRow_opts_arm_neon.h"
 
-extern const SkBlitRow::Proc16 sk_blitrow_platform_565_procs_arm[] = {
-    nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
-};
-
-extern const SkBlitRow::ColorProc16 sk_blitrow_platform_565_colorprocs_arm[] = {
-    nullptr, nullptr,
-};
-
 extern const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm[] = {
     nullptr, nullptr, nullptr, nullptr,
 };
 
-SkBlitRow::Proc16 SkBlitRow::PlatformFactory565(unsigned flags) {
-    return SK_ARM_NEON_WRAP(sk_blitrow_platform_565_procs_arm)[flags];
-}
-
-SkBlitRow::ColorProc16 SkBlitRow::PlatformColorFactory565(unsigned flags) {
-    return SK_ARM_NEON_WRAP(sk_blitrow_platform_565_colorprocs_arm)[flags];
-}
-
 SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
     return SK_ARM_NEON_WRAP(sk_blitrow_platform_32_procs_arm)[flags];
 }
diff --git a/src/opts/SkBlitRow_opts_arm_neon.cpp b/src/opts/SkBlitRow_opts_arm_neon.cpp
index 623d08b..7a9534c 100644
--- a/src/opts/SkBlitRow_opts_arm_neon.cpp
+++ b/src/opts/SkBlitRow_opts_arm_neon.cpp
@@ -17,860 +17,6 @@
 #include "SkColor_opts_neon.h"
 #include <arm_neon.h>
 
-#ifdef SK_CPU_ARM64
-static inline uint8x8x4_t sk_vld4_u8_arm64_3(const SkPMColor* SK_RESTRICT & src) {
-    uint8x8x4_t vsrc;
-    uint8x8_t vsrc_0, vsrc_1, vsrc_2;
-
-    asm (
-        "ld4    {v0.8b - v3.8b}, [%[src]], #32 \t\n"
-        "mov    %[vsrc0].8b, v0.8b             \t\n"
-        "mov    %[vsrc1].8b, v1.8b             \t\n"
-        "mov    %[vsrc2].8b, v2.8b             \t\n"
-        : [vsrc0] "=w" (vsrc_0), [vsrc1] "=w" (vsrc_1),
-          [vsrc2] "=w" (vsrc_2), [src] "+&r" (src)
-        : : "v0", "v1", "v2", "v3"
-    );
-
-    vsrc.val[0] = vsrc_0;
-    vsrc.val[1] = vsrc_1;
-    vsrc.val[2] = vsrc_2;
-
-    return vsrc;
-}
-
-static inline uint8x8x4_t sk_vld4_u8_arm64_4(const SkPMColor* SK_RESTRICT & src) {
-    uint8x8x4_t vsrc;
-    uint8x8_t vsrc_0, vsrc_1, vsrc_2, vsrc_3;
-
-    asm (
-        "ld4    {v0.8b - v3.8b}, [%[src]], #32 \t\n"
-        "mov    %[vsrc0].8b, v0.8b             \t\n"
-        "mov    %[vsrc1].8b, v1.8b             \t\n"
-        "mov    %[vsrc2].8b, v2.8b             \t\n"
-        "mov    %[vsrc3].8b, v3.8b             \t\n"
-        : [vsrc0] "=w" (vsrc_0), [vsrc1] "=w" (vsrc_1),
-          [vsrc2] "=w" (vsrc_2), [vsrc3] "=w" (vsrc_3),
-          [src] "+&r" (src)
-        : : "v0", "v1", "v2", "v3"
-    );
-
-    vsrc.val[0] = vsrc_0;
-    vsrc.val[1] = vsrc_1;
-    vsrc.val[2] = vsrc_2;
-    vsrc.val[3] = vsrc_3;
-
-    return vsrc;
-}
-#endif
-
-void S32_D565_Opaque_neon(uint16_t* SK_RESTRICT dst,
-                           const SkPMColor* SK_RESTRICT src, int count,
-                           U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 == alpha);
-
-    while (count >= 8) {
-        uint8x8x4_t vsrc;
-        uint16x8_t vdst;
-
-        // Load
-#ifdef SK_CPU_ARM64
-        vsrc = sk_vld4_u8_arm64_3(src);
-#else
-        vsrc = vld4_u8((uint8_t*)src);
-        src += 8;
-#endif
-
-        // Convert src to 565
-        vdst = SkPixel32ToPixel16_neon8(vsrc);
-
-        // Store
-        vst1q_u16(dst, vdst);
-
-        // Prepare next iteration
-        dst += 8;
-        count -= 8;
-    };
-
-    // Leftovers
-    while (count > 0) {
-        SkPMColor c = *src++;
-        SkPMColorAssert(c);
-        *dst = SkPixel32ToPixel16_ToU16(c);
-        dst++;
-        count--;
-    };
-}
-
-void S32_D565_Blend_neon(uint16_t* SK_RESTRICT dst,
-                          const SkPMColor* SK_RESTRICT src, int count,
-                          U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 > alpha);
-
-    uint16x8_t vmask_blue, vscale;
-
-    // prepare constants
-    vscale = vdupq_n_u16(SkAlpha255To256(alpha));
-    vmask_blue = vmovq_n_u16(0x1F);
-
-    while (count >= 8) {
-        uint8x8x4_t vsrc;
-        uint16x8_t vdst, vdst_r, vdst_g, vdst_b;
-        uint16x8_t vres_r, vres_g, vres_b;
-
-        // Load src
-#ifdef SK_CPU_ARM64
-        vsrc = sk_vld4_u8_arm64_3(src);
-#else
-        {
-        register uint8x8_t d0 asm("d0");
-        register uint8x8_t d1 asm("d1");
-        register uint8x8_t d2 asm("d2");
-        register uint8x8_t d3 asm("d3");
-
-        asm (
-            "vld4.8    {d0-d3},[%[src]]!"
-            : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3), [src] "+&r" (src)
-            :
-        );
-        vsrc.val[0] = d0;
-        vsrc.val[1] = d1;
-        vsrc.val[2] = d2;
-        }
-#endif
-
-        // Load and unpack dst
-        vdst = vld1q_u16(dst);
-        vdst_g = vshlq_n_u16(vdst, 5);        // shift green to top of lanes
-        vdst_b = vandq_u16(vdst, vmask_blue); // extract blue
-        vdst_r = vshrq_n_u16(vdst, 6+5);      // extract red
-        vdst_g = vshrq_n_u16(vdst_g, 5+5);    // extract green
-
-        // Shift src to 565 range
-        vsrc.val[NEON_R] = vshr_n_u8(vsrc.val[NEON_R], 3);
-        vsrc.val[NEON_G] = vshr_n_u8(vsrc.val[NEON_G], 2);
-        vsrc.val[NEON_B] = vshr_n_u8(vsrc.val[NEON_B], 3);
-
-        // Scale src - dst
-        vres_r = vmovl_u8(vsrc.val[NEON_R]) - vdst_r;
-        vres_g = vmovl_u8(vsrc.val[NEON_G]) - vdst_g;
-        vres_b = vmovl_u8(vsrc.val[NEON_B]) - vdst_b;
-
-        vres_r = vshrq_n_u16(vres_r * vscale, 8);
-        vres_g = vshrq_n_u16(vres_g * vscale, 8);
-        vres_b = vshrq_n_u16(vres_b * vscale, 8);
-
-        vres_r += vdst_r;
-        vres_g += vdst_g;
-        vres_b += vdst_b;
-
-        // Combine
-        vres_b = vsliq_n_u16(vres_b, vres_g, 5);    // insert green into blue
-        vres_b = vsliq_n_u16(vres_b, vres_r, 6+5);  // insert red into green/blue
-
-        // Store
-        vst1q_u16(dst, vres_b);
-        dst += 8;
-        count -= 8;
-    }
-    if (count > 0) {
-        int scale = SkAlpha255To256(alpha);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            uint16_t d = *dst;
-            *dst++ = SkPackRGB16(
-                    SkAlphaBlend(SkPacked32ToR16(c), SkGetPackedR16(d), scale),
-                    SkAlphaBlend(SkPacked32ToG16(c), SkGetPackedG16(d), scale),
-                    SkAlphaBlend(SkPacked32ToB16(c), SkGetPackedB16(d), scale));
-        } while (--count != 0);
-    }
-}
-
-#ifdef SK_CPU_ARM32
-void S32A_D565_Opaque_neon(uint16_t* SK_RESTRICT dst,
-                           const SkPMColor* SK_RESTRICT src, int count,
-                           U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 == alpha);
-
-    if (count >= 8) {
-        uint16_t* SK_RESTRICT keep_dst = 0;
-
-        asm volatile (
-                      "ands       ip, %[count], #7            \n\t"
-                      "vmov.u8    d31, #1<<7                  \n\t"
-                      "vld1.16    {q12}, [%[dst]]             \n\t"
-                      "vld4.8     {d0-d3}, [%[src]]           \n\t"
-                      // Thumb does not support the standard ARM conditional
-                      // instructions but instead requires the 'it' instruction
-                      // to signal conditional execution
-                      "it eq                                  \n\t"
-                      "moveq      ip, #8                      \n\t"
-                      "mov        %[keep_dst], %[dst]         \n\t"
-
-                      "add        %[src], %[src], ip, LSL#2   \n\t"
-                      "add        %[dst], %[dst], ip, LSL#1   \n\t"
-                      "subs       %[count], %[count], ip      \n\t"
-                      "b          9f                          \n\t"
-                      // LOOP
-                      "2:                                         \n\t"
-
-                      "vld1.16    {q12}, [%[dst]]!            \n\t"
-                      "vld4.8     {d0-d3}, [%[src]]!          \n\t"
-                      "vst1.16    {q10}, [%[keep_dst]]        \n\t"
-                      "sub        %[keep_dst], %[dst], #8*2   \n\t"
-                      "subs       %[count], %[count], #8      \n\t"
-                      "9:                                         \n\t"
-                      "pld        [%[dst],#32]                \n\t"
-                      // expand 0565 q12 to 8888 {d4-d7}
-                      "vmovn.u16  d4, q12                     \n\t"
-                      "vshr.u16   q11, q12, #5                \n\t"
-                      "vshr.u16   q10, q12, #6+5              \n\t"
-                      "vmovn.u16  d5, q11                     \n\t"
-                      "vmovn.u16  d6, q10                     \n\t"
-                      "vshl.u8    d4, d4, #3                  \n\t"
-                      "vshl.u8    d5, d5, #2                  \n\t"
-                      "vshl.u8    d6, d6, #3                  \n\t"
-
-                      "vmovl.u8   q14, d31                    \n\t"
-                      "vmovl.u8   q13, d31                    \n\t"
-                      "vmovl.u8   q12, d31                    \n\t"
-
-                      // duplicate in 4/2/1 & 8pix vsns
-                      "vmvn.8     d30, d3                     \n\t"
-                      "vmlal.u8   q14, d30, d6                \n\t"
-                      "vmlal.u8   q13, d30, d5                \n\t"
-                      "vmlal.u8   q12, d30, d4                \n\t"
-                      "vshr.u16   q8, q14, #5                 \n\t"
-                      "vshr.u16   q9, q13, #6                 \n\t"
-                      "vaddhn.u16 d6, q14, q8                 \n\t"
-                      "vshr.u16   q8, q12, #5                 \n\t"
-                      "vaddhn.u16 d5, q13, q9                 \n\t"
-                      "vaddhn.u16 d4, q12, q8                 \n\t"
-                      // intentionally don't calculate alpha
-                      // result in d4-d6
-
-            #ifdef SK_PMCOLOR_IS_RGBA
-                      "vqadd.u8   d6, d6, d0                  \n\t"
-                      "vqadd.u8   d5, d5, d1                  \n\t"
-                      "vqadd.u8   d4, d4, d2                  \n\t"
-            #else
-                      "vqadd.u8   d6, d6, d2                  \n\t"
-                      "vqadd.u8   d5, d5, d1                  \n\t"
-                      "vqadd.u8   d4, d4, d0                  \n\t"
-            #endif
-
-                      // pack 8888 {d4-d6} to 0565 q10
-                      "vshll.u8   q10, d6, #8                 \n\t"
-                      "vshll.u8   q3, d5, #8                  \n\t"
-                      "vshll.u8   q2, d4, #8                  \n\t"
-                      "vsri.u16   q10, q3, #5                 \n\t"
-                      "vsri.u16   q10, q2, #11                \n\t"
-
-                      "bne        2b                          \n\t"
-
-                      "1:                                         \n\t"
-                      "vst1.16      {q10}, [%[keep_dst]]      \n\t"
-                      : [count] "+r" (count)
-                      : [dst] "r" (dst), [keep_dst] "r" (keep_dst), [src] "r" (src)
-                      : "ip", "cc", "memory", "d0","d1","d2","d3","d4","d5","d6","d7",
-                      "d16","d17","d18","d19","d20","d21","d22","d23","d24","d25","d26","d27","d28","d29",
-                      "d30","d31"
-                      );
-    }
-    else
-    {   // handle count < 8
-        uint16_t* SK_RESTRICT keep_dst = 0;
-
-        asm volatile (
-                      "vmov.u8    d31, #1<<7                  \n\t"
-                      "mov        %[keep_dst], %[dst]         \n\t"
-
-                      "tst        %[count], #4                \n\t"
-                      "beq        14f                         \n\t"
-                      "vld1.16    {d25}, [%[dst]]!            \n\t"
-                      "vld1.32    {q1}, [%[src]]!             \n\t"
-
-                      "14:                                        \n\t"
-                      "tst        %[count], #2                \n\t"
-                      "beq        12f                         \n\t"
-                      "vld1.32    {d24[1]}, [%[dst]]!         \n\t"
-                      "vld1.32    {d1}, [%[src]]!             \n\t"
-
-                      "12:                                        \n\t"
-                      "tst        %[count], #1                \n\t"
-                      "beq        11f                         \n\t"
-                      "vld1.16    {d24[1]}, [%[dst]]!         \n\t"
-                      "vld1.32    {d0[1]}, [%[src]]!          \n\t"
-
-                      "11:                                        \n\t"
-                      // unzips achieve the same as a vld4 operation
-                      "vuzp.u16   q0, q1                      \n\t"
-                      "vuzp.u8    d0, d1                      \n\t"
-                      "vuzp.u8    d2, d3                      \n\t"
-                      // expand 0565 q12 to 8888 {d4-d7}
-                      "vmovn.u16  d4, q12                     \n\t"
-                      "vshr.u16   q11, q12, #5                \n\t"
-                      "vshr.u16   q10, q12, #6+5              \n\t"
-                      "vmovn.u16  d5, q11                     \n\t"
-                      "vmovn.u16  d6, q10                     \n\t"
-                      "vshl.u8    d4, d4, #3                  \n\t"
-                      "vshl.u8    d5, d5, #2                  \n\t"
-                      "vshl.u8    d6, d6, #3                  \n\t"
-
-                      "vmovl.u8   q14, d31                    \n\t"
-                      "vmovl.u8   q13, d31                    \n\t"
-                      "vmovl.u8   q12, d31                    \n\t"
-
-                      // duplicate in 4/2/1 & 8pix vsns
-                      "vmvn.8     d30, d3                     \n\t"
-                      "vmlal.u8   q14, d30, d6                \n\t"
-                      "vmlal.u8   q13, d30, d5                \n\t"
-                      "vmlal.u8   q12, d30, d4                \n\t"
-                      "vshr.u16   q8, q14, #5                 \n\t"
-                      "vshr.u16   q9, q13, #6                 \n\t"
-                      "vaddhn.u16 d6, q14, q8                 \n\t"
-                      "vshr.u16   q8, q12, #5                 \n\t"
-                      "vaddhn.u16 d5, q13, q9                 \n\t"
-                      "vaddhn.u16 d4, q12, q8                 \n\t"
-                      // intentionally don't calculate alpha
-                      // result in d4-d6
-
-            #ifdef SK_PMCOLOR_IS_RGBA
-                      "vqadd.u8   d6, d6, d0                  \n\t"
-                      "vqadd.u8   d5, d5, d1                  \n\t"
-                      "vqadd.u8   d4, d4, d2                  \n\t"
-            #else
-                      "vqadd.u8   d6, d6, d2                  \n\t"
-                      "vqadd.u8   d5, d5, d1                  \n\t"
-                      "vqadd.u8   d4, d4, d0                  \n\t"
-            #endif
-
-                      // pack 8888 {d4-d6} to 0565 q10
-                      "vshll.u8   q10, d6, #8                 \n\t"
-                      "vshll.u8   q3, d5, #8                  \n\t"
-                      "vshll.u8   q2, d4, #8                  \n\t"
-                      "vsri.u16   q10, q3, #5                 \n\t"
-                      "vsri.u16   q10, q2, #11                \n\t"
-
-                      // store
-                      "tst        %[count], #4                \n\t"
-                      "beq        24f                         \n\t"
-                      "vst1.16    {d21}, [%[keep_dst]]!       \n\t"
-
-                      "24:                                        \n\t"
-                      "tst        %[count], #2                \n\t"
-                      "beq        22f                         \n\t"
-                      "vst1.32    {d20[1]}, [%[keep_dst]]!    \n\t"
-
-                      "22:                                        \n\t"
-                      "tst        %[count], #1                \n\t"
-                      "beq        21f                         \n\t"
-                      "vst1.16    {d20[1]}, [%[keep_dst]]!    \n\t"
-
-                      "21:                                        \n\t"
-                      : [count] "+r" (count)
-                      : [dst] "r" (dst), [keep_dst] "r" (keep_dst), [src] "r" (src)
-                      : "ip", "cc", "memory", "d0","d1","d2","d3","d4","d5","d6","d7",
-                      "d16","d17","d18","d19","d20","d21","d22","d23","d24","d25","d26","d27","d28","d29",
-                      "d30","d31"
-                      );
-    }
-}
-
-#else // #ifdef SK_CPU_ARM32
-
-void S32A_D565_Opaque_neon(uint16_t* SK_RESTRICT dst,
-                           const SkPMColor* SK_RESTRICT src, int count,
-                           U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 == alpha);
-
-    if (count >= 16) {
-        asm (
-            "movi    v4.8h, #0x80                   \t\n"
-
-            "1:                                     \t\n"
-            "sub     %w[count], %w[count], #16      \t\n"
-            "ld1     {v16.8h-v17.8h}, [%[dst]]      \t\n"
-            "ld4     {v0.16b-v3.16b}, [%[src]], #64 \t\n"
-            "prfm    pldl1keep, [%[src],#512]       \t\n"
-            "prfm    pldl1keep, [%[dst],#256]       \t\n"
-            "ushr    v20.8h, v17.8h, #5             \t\n"
-            "ushr    v31.8h, v16.8h, #5             \t\n"
-            "xtn     v6.8b, v31.8h                  \t\n"
-            "xtn2    v6.16b, v20.8h                 \t\n"
-            "ushr    v20.8h, v17.8h, #11            \t\n"
-            "shl     v19.16b, v6.16b, #2            \t\n"
-            "ushr    v31.8h, v16.8h, #11            \t\n"
-            "xtn     v22.8b, v31.8h                 \t\n"
-            "xtn2    v22.16b, v20.8h                \t\n"
-            "shl     v18.16b, v22.16b, #3           \t\n"
-            "mvn     v3.16b, v3.16b                 \t\n"
-            "xtn     v16.8b, v16.8h                 \t\n"
-            "mov     v7.16b, v4.16b                 \t\n"
-            "xtn2    v16.16b, v17.8h                \t\n"
-            "umlal   v7.8h, v3.8b, v19.8b           \t\n"
-            "shl     v16.16b, v16.16b, #3           \t\n"
-            "mov     v22.16b, v4.16b                \t\n"
-            "ushr    v24.8h, v7.8h, #6              \t\n"
-            "umlal   v22.8h, v3.8b, v18.8b          \t\n"
-            "ushr    v20.8h, v22.8h, #5             \t\n"
-            "addhn   v20.8b, v22.8h, v20.8h         \t\n"
-            "cmp     %w[count], #16                 \t\n"
-            "mov     v6.16b, v4.16b                 \t\n"
-            "mov     v5.16b, v4.16b                 \t\n"
-            "umlal   v6.8h, v3.8b, v16.8b           \t\n"
-            "umlal2  v5.8h, v3.16b, v19.16b         \t\n"
-            "mov     v17.16b, v4.16b                \t\n"
-            "ushr    v19.8h, v6.8h, #5              \t\n"
-            "umlal2  v17.8h, v3.16b, v18.16b        \t\n"
-            "addhn   v7.8b, v7.8h, v24.8h           \t\n"
-            "ushr    v18.8h, v5.8h, #6              \t\n"
-            "ushr    v21.8h, v17.8h, #5             \t\n"
-            "addhn2  v7.16b, v5.8h, v18.8h          \t\n"
-            "addhn2  v20.16b, v17.8h, v21.8h        \t\n"
-            "mov     v22.16b, v4.16b                \t\n"
-            "addhn   v6.8b, v6.8h, v19.8h           \t\n"
-            "umlal2  v22.8h, v3.16b, v16.16b        \t\n"
-            "ushr    v5.8h, v22.8h, #5              \t\n"
-            "addhn2  v6.16b, v22.8h, v5.8h          \t\n"
-            "uqadd   v7.16b, v1.16b, v7.16b         \t\n"
-#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
-            "uqadd   v20.16b, v2.16b, v20.16b       \t\n"
-            "uqadd   v6.16b, v0.16b, v6.16b         \t\n"
-#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
-            "uqadd   v20.16b, v0.16b, v20.16b       \t\n"
-            "uqadd   v6.16b, v2.16b, v6.16b         \t\n"
-#else
-#error "This function only supports BGRA and RGBA."
-#endif
-            "shll    v22.8h, v20.8b, #8             \t\n"
-            "shll    v5.8h, v7.8b, #8               \t\n"
-            "sri     v22.8h, v5.8h, #5              \t\n"
-            "shll    v17.8h, v6.8b, #8              \t\n"
-            "shll2   v23.8h, v20.16b, #8            \t\n"
-            "shll2   v7.8h, v7.16b, #8              \t\n"
-            "sri     v22.8h, v17.8h, #11            \t\n"
-            "sri     v23.8h, v7.8h, #5              \t\n"
-            "shll2   v6.8h, v6.16b, #8              \t\n"
-            "st1     {v22.8h}, [%[dst]], #16        \t\n"
-            "sri     v23.8h, v6.8h, #11             \t\n"
-            "st1     {v23.8h}, [%[dst]], #16        \t\n"
-            "b.ge    1b                             \t\n"
-            : [dst] "+&r" (dst), [src] "+&r" (src), [count] "+&r" (count)
-            :: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
-               "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24",
-               "v31"
-        );
-    }
-        // Leftovers
-    if (count > 0) {
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            if (c) {
-                *dst = SkSrcOver32To16(c, *dst);
-            }
-            dst += 1;
-        } while (--count != 0);
-    }
-}
-#endif // #ifdef SK_CPU_ARM32
-
-static uint32_t pmcolor_to_expand16(SkPMColor c) {
-    unsigned r = SkGetPackedR32(c);
-    unsigned g = SkGetPackedG32(c);
-    unsigned b = SkGetPackedB32(c);
-    return (g << 24) | (r << 13) | (b << 2);
-}
-
-void Color32A_D565_neon(uint16_t dst[], SkPMColor src, int count, int x, int y) {
-    uint32_t src_expand;
-    unsigned scale;
-    uint16x8_t vmask_blue;
-
-    if (count <= 0) return;
-    SkASSERT(((size_t)dst & 0x01) == 0);
-
-    /*
-     * This preamble code is in order to make dst aligned to 8 bytes
-     * in the next mutiple bytes read & write access.
-     */
-    src_expand = pmcolor_to_expand16(src);
-    scale = SkAlpha255To256(0xFF - SkGetPackedA32(src)) >> 3;
-
-#define DST_ALIGN 8
-
-    /*
-     * preamble_size is in byte, meantime, this blend32_16_row_neon updates 2 bytes at a time.
-     */
-    int preamble_size = (DST_ALIGN - (size_t)dst) & (DST_ALIGN - 1);
-
-    for (int i = 0; i < preamble_size; i+=2, dst++) {
-        uint32_t dst_expand = SkExpand_rgb_16(*dst) * scale;
-        *dst = SkCompact_rgb_16((src_expand + dst_expand) >> 5);
-        if (--count == 0)
-            break;
-    }
-
-    int count16 = 0;
-    count16 = count >> 4;
-    vmask_blue = vmovq_n_u16(SK_B16_MASK);
-
-    if (count16) {
-        uint16x8_t wide_sr;
-        uint16x8_t wide_sg;
-        uint16x8_t wide_sb;
-        uint16x8_t wide_256_sa;
-
-        unsigned sr = SkGetPackedR32(src);
-        unsigned sg = SkGetPackedG32(src);
-        unsigned sb = SkGetPackedB32(src);
-        unsigned sa = SkGetPackedA32(src);
-
-        // Operation: dst_rgb = src_rgb + ((256 - src_a) >> 3) x dst_rgb
-        // sr: 8-bit based, dr: 5-bit based, with dr x ((256-sa)>>3), 5-bit left shifted,
-        //thus, for sr, do 2-bit left shift to match MSB : (8 + 2 = 5 + 5)
-        wide_sr = vshlq_n_u16(vmovl_u8(vdup_n_u8(sr)), 2); // widen and src_red shift
-
-        // sg: 8-bit based, dg: 6-bit based, with dg x ((256-sa)>>3), 5-bit left shifted,
-        //thus, for sg, do 3-bit left shift to match MSB : (8 + 3 = 6 + 5)
-        wide_sg = vshlq_n_u16(vmovl_u8(vdup_n_u8(sg)), 3); // widen and src_grn shift
-
-        // sb: 8-bit based, db: 5-bit based, with db x ((256-sa)>>3), 5-bit left shifted,
-        //thus, for sb, do 2-bit left shift to match MSB : (8 + 2 = 5 + 5)
-        wide_sb = vshlq_n_u16(vmovl_u8(vdup_n_u8(sb)), 2); // widen and src blu shift
-
-        wide_256_sa =
-            vshrq_n_u16(vsubw_u8(vdupq_n_u16(256), vdup_n_u8(sa)), 3); // (256 - sa) >> 3
-
-        while (count16-- > 0) {
-            uint16x8_t vdst1, vdst1_r, vdst1_g, vdst1_b;
-            uint16x8_t vdst2, vdst2_r, vdst2_g, vdst2_b;
-            vdst1 = vld1q_u16(dst);
-            dst += 8;
-            vdst2 = vld1q_u16(dst);
-            dst -= 8;    //to store dst again.
-
-            vdst1_g = vshlq_n_u16(vdst1, SK_R16_BITS);                 // shift green to top of lanes
-            vdst1_b = vdst1 & vmask_blue;                              // extract blue
-            vdst1_r = vshrq_n_u16(vdst1, SK_R16_SHIFT);                // extract red
-            vdst1_g = vshrq_n_u16(vdst1_g, SK_R16_BITS + SK_B16_BITS); // extract green
-
-            vdst2_g = vshlq_n_u16(vdst2, SK_R16_BITS);                 // shift green to top of lanes
-            vdst2_b = vdst2 & vmask_blue;                              // extract blue
-            vdst2_r = vshrq_n_u16(vdst2, SK_R16_SHIFT);                // extract red
-            vdst2_g = vshrq_n_u16(vdst2_g, SK_R16_BITS + SK_B16_BITS); // extract green
-
-            vdst1_r = vmlaq_u16(wide_sr, wide_256_sa, vdst1_r);        // sr + (256-sa) x dr1
-            vdst1_g = vmlaq_u16(wide_sg, wide_256_sa, vdst1_g);        // sg + (256-sa) x dg1
-            vdst1_b = vmlaq_u16(wide_sb, wide_256_sa, vdst1_b);        // sb + (256-sa) x db1
-
-            vdst2_r = vmlaq_u16(wide_sr, wide_256_sa, vdst2_r);        // sr + (256-sa) x dr2
-            vdst2_g = vmlaq_u16(wide_sg, wide_256_sa, vdst2_g);        // sg + (256-sa) x dg2
-            vdst2_b = vmlaq_u16(wide_sb, wide_256_sa, vdst2_b);        // sb + (256-sa) x db2
-
-            vdst1_r = vshrq_n_u16(vdst1_r, 5);                         // 5-bit right shift for 5-bit red
-            vdst1_g = vshrq_n_u16(vdst1_g, 5);                         // 5-bit right shift for 6-bit green
-            vdst1_b = vshrq_n_u16(vdst1_b, 5);                         // 5-bit right shift for 5-bit blue
-
-            vdst1 = vsliq_n_u16(vdst1_b, vdst1_g, SK_G16_SHIFT);       // insert green into blue
-            vdst1 = vsliq_n_u16(vdst1, vdst1_r, SK_R16_SHIFT);         // insert red into green/blue
-
-            vdst2_r = vshrq_n_u16(vdst2_r, 5);                         // 5-bit right shift for 5-bit red
-            vdst2_g = vshrq_n_u16(vdst2_g, 5);                         // 5-bit right shift for 6-bit green
-            vdst2_b = vshrq_n_u16(vdst2_b, 5);                         // 5-bit right shift for 5-bit blue
-
-            vdst2 = vsliq_n_u16(vdst2_b, vdst2_g, SK_G16_SHIFT);       // insert green into blue
-            vdst2 = vsliq_n_u16(vdst2, vdst2_r, SK_R16_SHIFT);         // insert red into green/blue
-
-            vst1q_u16(dst, vdst1);
-            dst += 8;
-            vst1q_u16(dst, vdst2);
-            dst += 8;
-        }
-    }
-
-    count &= 0xF;
-    if (count > 0) {
-        do {
-            uint32_t dst_expand = SkExpand_rgb_16(*dst) * scale;
-            *dst = SkCompact_rgb_16((src_expand + dst_expand) >> 5);
-            dst += 1;
-        } while (--count != 0);
-    }
-}
-
-static inline uint16x8_t SkDiv255Round_neon8(uint16x8_t prod) {
-    prod += vdupq_n_u16(128);
-    prod += vshrq_n_u16(prod, 8);
-    return vshrq_n_u16(prod, 8);
-}
-
-void S32A_D565_Blend_neon(uint16_t* SK_RESTRICT dst,
-                          const SkPMColor* SK_RESTRICT src, int count,
-                          U8CPU alpha, int /*x*/, int /*y*/) {
-   SkASSERT(255 > alpha);
-
-    /* This code implements a Neon version of S32A_D565_Blend. The results have
-     * a few mismatches compared to the original code. These mismatches never
-     * exceed 1.
-     */
-
-    if (count >= 8) {
-        uint16x8_t valpha_max, vmask_blue;
-        uint8x8_t valpha;
-
-        // prepare constants
-        valpha_max = vmovq_n_u16(255);
-        valpha = vdup_n_u8(alpha);
-        vmask_blue = vmovq_n_u16(SK_B16_MASK);
-
-        do {
-            uint16x8_t vdst, vdst_r, vdst_g, vdst_b;
-            uint16x8_t vres_a, vres_r, vres_g, vres_b;
-            uint8x8x4_t vsrc;
-
-            // load pixels
-            vdst = vld1q_u16(dst);
-#ifdef SK_CPU_ARM64
-            vsrc = sk_vld4_u8_arm64_4(src);
-#elif (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))
-            asm (
-                "vld4.u8 %h[vsrc], [%[src]]!"
-                : [vsrc] "=w" (vsrc), [src] "+&r" (src)
-                : :
-            );
-#else
-            register uint8x8_t d0 asm("d0");
-            register uint8x8_t d1 asm("d1");
-            register uint8x8_t d2 asm("d2");
-            register uint8x8_t d3 asm("d3");
-
-            asm volatile (
-                "vld4.u8    {d0-d3},[%[src]]!;"
-                : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3),
-                  [src] "+&r" (src)
-                : :
-            );
-            vsrc.val[0] = d0;
-            vsrc.val[1] = d1;
-            vsrc.val[2] = d2;
-            vsrc.val[3] = d3;
-#endif
-
-
-            // deinterleave dst
-            vdst_g = vshlq_n_u16(vdst, SK_R16_BITS);        // shift green to top of lanes
-            vdst_b = vdst & vmask_blue;                     // extract blue
-            vdst_r = vshrq_n_u16(vdst, SK_R16_SHIFT);       // extract red
-            vdst_g = vshrq_n_u16(vdst_g, SK_R16_BITS + SK_B16_BITS); // extract green
-
-            // shift src to 565
-            vsrc.val[NEON_R] = vshr_n_u8(vsrc.val[NEON_R], 8 - SK_R16_BITS);
-            vsrc.val[NEON_G] = vshr_n_u8(vsrc.val[NEON_G], 8 - SK_G16_BITS);
-            vsrc.val[NEON_B] = vshr_n_u8(vsrc.val[NEON_B], 8 - SK_B16_BITS);
-
-            // calc src * src_scale
-            vres_a = vmull_u8(vsrc.val[NEON_A], valpha);
-            vres_r = vmull_u8(vsrc.val[NEON_R], valpha);
-            vres_g = vmull_u8(vsrc.val[NEON_G], valpha);
-            vres_b = vmull_u8(vsrc.val[NEON_B], valpha);
-
-            // prepare dst_scale
-            vres_a = SkDiv255Round_neon8(vres_a);
-            vres_a = valpha_max - vres_a; // 255 - (sa * src_scale) / 255
-
-            // add dst * dst_scale to previous result
-            vres_r = vmlaq_u16(vres_r, vdst_r, vres_a);
-            vres_g = vmlaq_u16(vres_g, vdst_g, vres_a);
-            vres_b = vmlaq_u16(vres_b, vdst_b, vres_a);
-
-#ifdef S32A_D565_BLEND_EXACT
-            // It is possible to get exact results with this but it is slow,
-            // even slower than C code in some cases
-            vres_r = SkDiv255Round_neon8(vres_r);
-            vres_g = SkDiv255Round_neon8(vres_g);
-            vres_b = SkDiv255Round_neon8(vres_b);
-#else
-            vres_r = vrshrq_n_u16(vres_r, 8);
-            vres_g = vrshrq_n_u16(vres_g, 8);
-            vres_b = vrshrq_n_u16(vres_b, 8);
-#endif
-            // pack result
-            vres_b = vsliq_n_u16(vres_b, vres_g, SK_G16_SHIFT); // insert green into blue
-            vres_b = vsliq_n_u16(vres_b, vres_r, SK_R16_SHIFT); // insert red into green/blue
-
-            // store
-            vst1q_u16(dst, vres_b);
-            dst += 8;
-            count -= 8;
-        } while (count >= 8);
-    }
-
-    // leftovers
-    while (count-- > 0) {
-        SkPMColor sc = *src++;
-        if (sc) {
-            uint16_t dc = *dst;
-            unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
-            unsigned dr = (SkPacked32ToR16(sc) * alpha) + (SkGetPackedR16(dc) * dst_scale);
-            unsigned dg = (SkPacked32ToG16(sc) * alpha) + (SkGetPackedG16(dc) * dst_scale);
-            unsigned db = (SkPacked32ToB16(sc) * alpha) + (SkGetPackedB16(dc) * dst_scale);
-            *dst = SkPackRGB16(SkDiv255Round(dr), SkDiv255Round(dg), SkDiv255Round(db));
-        }
-        dst += 1;
-    }
-}
-
-/* dither matrix for Neon, derived from gDitherMatrix_3Bit_16.
- * each dither value is spaced out into byte lanes, and repeated
- * to allow an 8-byte load from offsets 0, 1, 2 or 3 from the
- * start of each row.
- */
-static const uint8_t gDitherMatrix_Neon[48] = {
-    0, 4, 1, 5, 0, 4, 1, 5, 0, 4, 1, 5,
-    6, 2, 7, 3, 6, 2, 7, 3, 6, 2, 7, 3,
-    1, 5, 0, 4, 1, 5, 0, 4, 1, 5, 0, 4,
-    7, 3, 6, 2, 7, 3, 6, 2, 7, 3, 6, 2,
-
-};
-
-void S32_D565_Blend_Dither_neon(uint16_t *dst, const SkPMColor *src,
-                                int count, U8CPU alpha, int x, int y)
-{
-
-    SkASSERT(255 > alpha);
-
-    // rescale alpha to range 1 - 256
-    int scale = SkAlpha255To256(alpha);
-
-    if (count >= 8) {
-        /* select row and offset for dither array */
-        const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
-
-        uint8x8_t vdither = vld1_u8(dstart);         // load dither values
-        uint8x8_t vdither_g = vshr_n_u8(vdither, 1); // calc. green dither values
-
-        int16x8_t vscale = vdupq_n_s16(scale);        // duplicate scale into neon reg
-        uint16x8_t vmask_b = vdupq_n_u16(0x1F);         // set up blue mask
-
-        do {
-
-            uint8x8x4_t vsrc;
-            uint8x8_t vsrc_r, vsrc_g, vsrc_b;
-            uint8x8_t vsrc565_r, vsrc565_g, vsrc565_b;
-            uint16x8_t vsrc_dit_r, vsrc_dit_g, vsrc_dit_b;
-            uint16x8_t vsrc_res_r, vsrc_res_g, vsrc_res_b;
-            uint16x8_t vdst;
-            uint16x8_t vdst_r, vdst_g, vdst_b;
-            int16x8_t vres_r, vres_g, vres_b;
-            int8x8_t vres8_r, vres8_g, vres8_b;
-
-            // Load source and add dither
-#ifdef SK_CPU_ARM64
-            vsrc = sk_vld4_u8_arm64_3(src);
-#else
-            {
-            register uint8x8_t d0 asm("d0");
-            register uint8x8_t d1 asm("d1");
-            register uint8x8_t d2 asm("d2");
-            register uint8x8_t d3 asm("d3");
-
-            asm (
-                "vld4.8    {d0-d3},[%[src]]! "
-                : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3), [src] "+&r" (src)
-                :
-            );
-            vsrc.val[0] = d0;
-            vsrc.val[1] = d1;
-            vsrc.val[2] = d2;
-            }
-#endif
-            vsrc_r = vsrc.val[NEON_R];
-            vsrc_g = vsrc.val[NEON_G];
-            vsrc_b = vsrc.val[NEON_B];
-
-            vsrc565_g = vshr_n_u8(vsrc_g, 6); // calc. green >> 6
-            vsrc565_r = vshr_n_u8(vsrc_r, 5); // calc. red >> 5
-            vsrc565_b = vshr_n_u8(vsrc_b, 5); // calc. blue >> 5
-
-            vsrc_dit_g = vaddl_u8(vsrc_g, vdither_g); // add in dither to green and widen
-            vsrc_dit_r = vaddl_u8(vsrc_r, vdither);   // add in dither to red and widen
-            vsrc_dit_b = vaddl_u8(vsrc_b, vdither);   // add in dither to blue and widen
-
-            vsrc_dit_r = vsubw_u8(vsrc_dit_r, vsrc565_r);  // sub shifted red from result
-            vsrc_dit_g = vsubw_u8(vsrc_dit_g, vsrc565_g);  // sub shifted green from result
-            vsrc_dit_b = vsubw_u8(vsrc_dit_b, vsrc565_b);  // sub shifted blue from result
-
-            vsrc_res_r = vshrq_n_u16(vsrc_dit_r, 3);
-            vsrc_res_g = vshrq_n_u16(vsrc_dit_g, 2);
-            vsrc_res_b = vshrq_n_u16(vsrc_dit_b, 3);
-
-            // Load dst and unpack
-            vdst = vld1q_u16(dst);
-            vdst_g = vshrq_n_u16(vdst, 5);                   // shift down to get green
-            vdst_r = vshrq_n_u16(vshlq_n_u16(vdst, 5), 5+5); // double shift to extract red
-            vdst_b = vandq_u16(vdst, vmask_b);               // mask to get blue
-
-            // subtract dst from src and widen
-            vres_r = vsubq_s16(vreinterpretq_s16_u16(vsrc_res_r), vreinterpretq_s16_u16(vdst_r));
-            vres_g = vsubq_s16(vreinterpretq_s16_u16(vsrc_res_g), vreinterpretq_s16_u16(vdst_g));
-            vres_b = vsubq_s16(vreinterpretq_s16_u16(vsrc_res_b), vreinterpretq_s16_u16(vdst_b));
-
-            // multiply diffs by scale and shift
-            vres_r = vmulq_s16(vres_r, vscale);
-            vres_g = vmulq_s16(vres_g, vscale);
-            vres_b = vmulq_s16(vres_b, vscale);
-
-            vres8_r = vshrn_n_s16(vres_r, 8);
-            vres8_g = vshrn_n_s16(vres_g, 8);
-            vres8_b = vshrn_n_s16(vres_b, 8);
-
-            // add dst to result
-            vres_r = vaddw_s8(vreinterpretq_s16_u16(vdst_r), vres8_r);
-            vres_g = vaddw_s8(vreinterpretq_s16_u16(vdst_g), vres8_g);
-            vres_b = vaddw_s8(vreinterpretq_s16_u16(vdst_b), vres8_b);
-
-            // put result into 565 format
-            vres_b = vsliq_n_s16(vres_b, vres_g, 5);   // shift up green and insert into blue
-            vres_b = vsliq_n_s16(vres_b, vres_r, 6+5); // shift up red and insert into blue
-
-            // Store result
-            vst1q_u16(dst, vreinterpretq_u16_s16(vres_b));
-
-            // Next iteration
-            dst += 8;
-            count -= 8;
-
-        } while (count >= 8);
-    }
-
-    // Leftovers
-    if (count > 0) {
-        int scale = SkAlpha255To256(alpha);
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-
-            int dither = DITHER_VALUE(x);
-            int sr = SkGetPackedR32(c);
-            int sg = SkGetPackedG32(c);
-            int sb = SkGetPackedB32(c);
-            sr = SkDITHER_R32To565(sr, dither);
-            sg = SkDITHER_G32To565(sg, dither);
-            sb = SkDITHER_B32To565(sb, dither);
-
-            uint16_t d = *dst;
-            *dst++ = SkPackRGB16(SkAlphaBlend(sr, SkGetPackedR16(d), scale),
-                                 SkAlphaBlend(sg, SkGetPackedG16(d), scale),
-                                 SkAlphaBlend(sb, SkGetPackedB16(d), scale));
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
-
 /* Neon version of S32_Blend_BlitRow32()
  * portable version is in src/core/SkBlitRow_D32.cpp
  */
@@ -1042,253 +188,8 @@
 
 #endif // #ifdef SK_CPU_ARM32
 
-void S32A_D565_Opaque_Dither_neon (uint16_t * SK_RESTRICT dst,
-                                   const SkPMColor* SK_RESTRICT src,
-                                   int count, U8CPU alpha, int x, int y) {
-    SkASSERT(255 == alpha);
-
-#define    UNROLL    8
-
-    if (count >= UNROLL) {
-
-    uint8x8_t dbase;
-    const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
-    dbase = vld1_u8(dstart);
-
-        do {
-        uint8x8x4_t vsrc;
-        uint8x8_t sr, sg, sb, sa, d;
-        uint16x8_t dst8, scale8, alpha8;
-        uint16x8_t dst_r, dst_g, dst_b;
-
-#ifdef SK_CPU_ARM64
-        vsrc = sk_vld4_u8_arm64_4(src);
-#else
-        {
-        register uint8x8_t d0 asm("d0");
-        register uint8x8_t d1 asm("d1");
-        register uint8x8_t d2 asm("d2");
-        register uint8x8_t d3 asm("d3");
-
-        asm ("vld4.8    {d0-d3},[%[src]]! "
-            : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3), [src] "+r" (src)
-            :
-        );
-        vsrc.val[0] = d0;
-        vsrc.val[1] = d1;
-        vsrc.val[2] = d2;
-        vsrc.val[3] = d3;
-        }
-#endif
-        sa = vsrc.val[NEON_A];
-        sr = vsrc.val[NEON_R];
-        sg = vsrc.val[NEON_G];
-        sb = vsrc.val[NEON_B];
-
-        /* calculate 'd', which will be 0..7
-         * dbase[] is 0..7; alpha is 0..256; 16 bits suffice
-         */
-        alpha8 = vmovl_u8(dbase);
-        alpha8 = vmlal_u8(alpha8, sa, dbase);
-        d = vshrn_n_u16(alpha8, 8);    // narrowing too
-
-        // sr = sr - (sr>>5) + d
-        /* watching for 8-bit overflow.  d is 0..7; risky range of
-         * sr is >248; and then (sr>>5) is 7 so it offsets 'd';
-         * safe  as long as we do ((sr-sr>>5) + d)
-         */
-        sr = vsub_u8(sr, vshr_n_u8(sr, 5));
-        sr = vadd_u8(sr, d);
-
-        // sb = sb - (sb>>5) + d
-        sb = vsub_u8(sb, vshr_n_u8(sb, 5));
-        sb = vadd_u8(sb, d);
-
-        // sg = sg - (sg>>6) + d>>1; similar logic for overflows
-        sg = vsub_u8(sg, vshr_n_u8(sg, 6));
-        sg = vadd_u8(sg, vshr_n_u8(d,1));
-
-        // need to pick up 8 dst's -- at 16 bits each, 128 bits
-        dst8 = vld1q_u16(dst);
-        dst_b = vandq_u16(dst8, vdupq_n_u16(SK_B16_MASK));
-        dst_g = vshrq_n_u16(vshlq_n_u16(dst8, SK_R16_BITS), SK_R16_BITS + SK_B16_BITS);
-        dst_r = vshrq_n_u16(dst8, SK_R16_SHIFT);    // clearing hi bits
-
-        // blend
-        scale8 = vsubw_u8(vdupq_n_u16(256), sa);
-
-        // combine the addq and mul, save 3 insns
-        scale8 = vshrq_n_u16(scale8, 3);
-        dst_b = vmlaq_u16(vshll_n_u8(sb,2), dst_b, scale8);
-        dst_g = vmlaq_u16(vshll_n_u8(sg,3), dst_g, scale8);
-        dst_r = vmlaq_u16(vshll_n_u8(sr,2), dst_r, scale8);
-
-        // repack to store
-        dst8 = vshrq_n_u16(dst_b, 5);
-        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dst_g, 5), 5);
-        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dst_r,5), 11);
-
-        vst1q_u16(dst, dst8);
-
-        dst += UNROLL;
-        count -= UNROLL;
-        // skip x += UNROLL, since it's unchanged mod-4
-        } while (count >= UNROLL);
-    }
-#undef    UNROLL
-
-    // residuals
-    if (count > 0) {
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            if (c) {
-                unsigned a = SkGetPackedA32(c);
-
-                // dither and alpha are just temporary variables to work-around
-                // an ICE in debug.
-                unsigned dither = DITHER_VALUE(x);
-                unsigned alpha = SkAlpha255To256(a);
-                int d = SkAlphaMul(dither, alpha);
-
-                unsigned sr = SkGetPackedR32(c);
-                unsigned sg = SkGetPackedG32(c);
-                unsigned sb = SkGetPackedB32(c);
-                sr = SkDITHER_R32_FOR_565(sr, d);
-                sg = SkDITHER_G32_FOR_565(sg, d);
-                sb = SkDITHER_B32_FOR_565(sb, d);
-
-                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
-                uint32_t dst_expanded = SkExpand_rgb_16(*dst);
-                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
-                // now src and dst expanded are in g:11 r:10 x:1 b:10
-                *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
-            }
-            dst += 1;
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
-void S32_D565_Opaque_Dither_neon(uint16_t* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src,
-                                 int count, U8CPU alpha, int x, int y) {
-    SkASSERT(255 == alpha);
-
-#define    UNROLL    8
-    if (count >= UNROLL) {
-    uint8x8_t d;
-    const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
-    d = vld1_u8(dstart);
-
-    while (count >= UNROLL) {
-        uint8x8_t sr, sg, sb;
-        uint16x8_t dr, dg, db;
-        uint16x8_t dst8;
-        uint8x8x4_t vsrc;
-
-#ifdef SK_CPU_ARM64
-        vsrc = sk_vld4_u8_arm64_3(src);
-#else
-        {
-        register uint8x8_t d0 asm("d0");
-        register uint8x8_t d1 asm("d1");
-        register uint8x8_t d2 asm("d2");
-        register uint8x8_t d3 asm("d3");
-
-        asm (
-            "vld4.8    {d0-d3},[%[src]]! "
-            : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3), [src] "+&r" (src)
-            :
-        );
-        vsrc.val[0] = d0;
-        vsrc.val[1] = d1;
-        vsrc.val[2] = d2;
-        }
-#endif
-        sr = vsrc.val[NEON_R];
-        sg = vsrc.val[NEON_G];
-        sb = vsrc.val[NEON_B];
-
-        /* XXX: if we want to prefetch, hide it in the above asm()
-         * using the gcc __builtin_prefetch(), the prefetch will
-         * fall to the bottom of the loop -- it won't stick up
-         * at the top of the loop, just after the vld4.
-         */
-
-        // sr = sr - (sr>>5) + d
-        sr = vsub_u8(sr, vshr_n_u8(sr, 5));
-        dr = vaddl_u8(sr, d);
-
-        // sb = sb - (sb>>5) + d
-        sb = vsub_u8(sb, vshr_n_u8(sb, 5));
-        db = vaddl_u8(sb, d);
-
-        // sg = sg - (sg>>6) + d>>1; similar logic for overflows
-        sg = vsub_u8(sg, vshr_n_u8(sg, 6));
-        dg = vaddl_u8(sg, vshr_n_u8(d, 1));
-
-        // pack high bits of each into 565 format  (rgb, b is lsb)
-        dst8 = vshrq_n_u16(db, 3);
-        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dg, 2), 5);
-        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dr, 3), 11);
-
-        // store it
-        vst1q_u16(dst, dst8);
-
-        dst += UNROLL;
-        // we don't need to increment src as the asm above has already done it
-        count -= UNROLL;
-        x += UNROLL;        // probably superfluous
-    }
-    }
-#undef    UNROLL
-
-    // residuals
-    if (count > 0) {
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            SkASSERT(SkGetPackedA32(c) == 255);
-
-            unsigned dither = DITHER_VALUE(x);
-            *dst++ = SkDitherRGB32To565(c, dither);
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-const SkBlitRow::Proc16 sk_blitrow_platform_565_procs_arm_neon[] = {
-    // no dither
-    S32_D565_Opaque_neon,
-    S32_D565_Blend_neon,
-    S32A_D565_Opaque_neon,
-#if 0
-    S32A_D565_Blend_neon,
-#else
-    nullptr,   // https://code.google.com/p/skia/issues/detail?id=2797
-#endif
-
-    // dither
-    S32_D565_Opaque_Dither_neon,
-    S32_D565_Blend_Dither_neon,
-    S32A_D565_Opaque_Dither_neon,
-    nullptr,   // S32A_D565_Blend_Dither
-};
-
-const SkBlitRow::ColorProc16 sk_blitrow_platform_565_colorprocs_arm_neon[] = {
-    Color32A_D565_neon,    // Color32_D565,
-    Color32A_D565_neon,    // Color32A_D565,
-    Color32A_D565_neon,    // Color32_D565_Dither,
-    Color32A_D565_neon,    // Color32A_D565_Dither
-};
-
 const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm_neon[] = {
     nullptr,   // S32_Opaque,
     S32_Blend_BlitRow32_neon,        // S32_Blend,
diff --git a/src/opts/SkBlitRow_opts_arm_neon.h b/src/opts/SkBlitRow_opts_arm_neon.h
index 159a466..815c2b7 100644
--- a/src/opts/SkBlitRow_opts_arm_neon.h
+++ b/src/opts/SkBlitRow_opts_arm_neon.h
@@ -9,8 +9,6 @@
 
 #include "SkBlitRow.h"
 
-extern const SkBlitRow::Proc16 sk_blitrow_platform_565_procs_arm_neon[];
-extern const SkBlitRow::ColorProc16 sk_blitrow_platform_565_colorprocs_arm_neon[];
 extern const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm_neon[];
 
 #endif
diff --git a/src/opts/SkBlitRow_opts_none.cpp b/src/opts/SkBlitRow_opts_none.cpp
index a9abe06..289bb7e 100644
--- a/src/opts/SkBlitRow_opts_none.cpp
+++ b/src/opts/SkBlitRow_opts_none.cpp
@@ -9,14 +9,6 @@
 
 // Platform impl of Platform_procs with no overrides
 
-SkBlitRow::Proc16 SkBlitRow::PlatformFactory565(unsigned flags) {
-    return nullptr;
-}
-
-SkBlitRow::ColorProc16 SkBlitRow::PlatformColorFactory565(unsigned flags) {
-    return nullptr;
-}
-
 SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
     return nullptr;
 }
diff --git a/src/opts/SkChecksum_opts.h b/src/opts/SkChecksum_opts.h
index 3e1acf0..089e87c 100644
--- a/src/opts/SkChecksum_opts.h
+++ b/src/opts/SkChecksum_opts.h
@@ -13,7 +13,7 @@
 
 #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE42
     #include <immintrin.h>
-#elif defined(SK_CPU_ARM64) && defined(SK_ARM_HAS_CRC32)
+#elif defined(SK_ARM_HAS_CRC32)
     #include <arm_acle.h>
 #endif
 
@@ -127,7 +127,7 @@
         return hash;
     }
 
-#elif defined(SK_CPU_ARM64) && defined(SK_ARM_HAS_CRC32)
+#elif defined(SK_ARM_HAS_CRC32)
     static uint32_t hash_fn(const void* vdata, size_t bytes, uint32_t hash) {
         auto data = (const uint8_t*)vdata;
         if (bytes >= 24) {
diff --git a/src/opts/SkNx_sse.h b/src/opts/SkNx_sse.h
index e818722..3e59a9d 100644
--- a/src/opts/SkNx_sse.h
+++ b/src/opts/SkNx_sse.h
@@ -329,7 +329,6 @@
     AI void store(void* ptr) const { _mm_storeu_si128((__m128i*)ptr, fVec); }
 
     AI static void Load4(const void* ptr, SkNx* r, SkNx* g, SkNx* b, SkNx* a) {
-        // TODO: AVX2 version
         __m128i _01 = _mm_loadu_si128(((__m128i*)ptr) + 0),
                 _23 = _mm_loadu_si128(((__m128i*)ptr) + 1),
                 _45 = _mm_loadu_si128(((__m128i*)ptr) + 2),
@@ -351,7 +350,6 @@
         *a = _mm_unpackhi_epi64(ba0123, ba4567);
     }
     AI static void Load3(const void* ptr, SkNx* r, SkNx* g, SkNx* b) {
-        // TODO: AVX2 version
         const uint8_t* ptr8 = (const uint8_t*) ptr;
         __m128i rgb0 = _mm_loadu_si128((const __m128i*) (ptr8 +  0*2));
         __m128i rgb1 = _mm_srli_si128(rgb0, 3*2);
@@ -377,7 +375,6 @@
         *b = _mm_unpacklo_epi64(bx03, bx47);
     }
     AI static void Store4(void* ptr, const SkNx& r, const SkNx& g, const SkNx& b, const SkNx& a) {
-        // TODO: AVX2 version
         __m128i rg0123 = _mm_unpacklo_epi16(r.fVec, g.fVec),  // r0 g0 r1 g1 r2 g2 r3 g3
                 rg4567 = _mm_unpackhi_epi16(r.fVec, g.fVec),  // r4 g4 r5 g5 r6 g6 r7 g7
                 ba0123 = _mm_unpacklo_epi16(b.fVec, a.fVec),
@@ -486,243 +483,6 @@
     __m128i fVec;
 };
 
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
-
-    template <>
-    class SkNx<8, uint8_t> {
-    public:
-        AI SkNx(const __m128i& vec) : fVec(vec) {}
-
-        AI SkNx() {}
-        AI SkNx(uint8_t v) : fVec(_mm_set1_epi8(v)) {}
-        AI SkNx(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
-                uint8_t e, uint8_t f, uint8_t g, uint8_t h)
-            : fVec(_mm_setr_epi8(a,b,c,d, e,f,g,h, 0,0,0,0, 0,0,0,0)) {}
-
-
-        AI static SkNx Load(const void* ptr) { return _mm_loadl_epi64((const __m128i*)ptr); }
-        AI void store(void* ptr) const { _mm_storel_epi64((__m128i*)ptr, fVec); }
-
-        AI uint8_t operator[](int k) const {
-            SkASSERT(0 <= k && k < 8);
-            union { __m128i v; uint8_t us[16]; } pun = {fVec};
-            return pun.us[k&7];
-        }
-
-        __m128i fVec;
-    };
-
-    template <>
-    class SkNx<8, int32_t> {
-    public:
-        AI SkNx(const __m256i& vec) : fVec(vec) {}
-
-        AI SkNx() {}
-        AI SkNx(int32_t v) : fVec(_mm256_set1_epi32(v)) {}
-        AI SkNx(int32_t a, int32_t b, int32_t c, int32_t d,
-                int32_t e, int32_t f, int32_t g, int32_t h)
-            : fVec(_mm256_setr_epi32(a,b,c,d, e,f,g,h)) {}
-
-        AI static SkNx Load(const void* ptr) { return _mm256_loadu_si256((const __m256i*)ptr); }
-        AI void store(void* ptr) const { _mm256_storeu_si256((__m256i*)ptr, fVec); }
-
-        AI SkNx operator + (const SkNx& o) const { return _mm256_add_epi32(fVec, o.fVec); }
-        AI SkNx operator - (const SkNx& o) const { return _mm256_sub_epi32(fVec, o.fVec); }
-        AI SkNx operator * (const SkNx& o) const { return _mm256_mullo_epi32(fVec, o.fVec); }
-
-        AI SkNx operator & (const SkNx& o) const { return _mm256_and_si256(fVec, o.fVec); }
-        AI SkNx operator | (const SkNx& o) const { return _mm256_or_si256(fVec, o.fVec); }
-        AI SkNx operator ^ (const SkNx& o) const { return _mm256_xor_si256(fVec, o.fVec); }
-
-        AI SkNx operator << (int bits) const { return _mm256_slli_epi32(fVec, bits); }
-        AI SkNx operator >> (int bits) const { return _mm256_srai_epi32(fVec, bits); }
-
-        AI int32_t operator[](int k) const {
-            SkASSERT(0 <= k && k < 8);
-            union { __m256i v; int32_t is[8]; } pun = {fVec};
-            return pun.is[k&7];
-        }
-
-        __m256i fVec;
-    };
-
-    template <>
-    class SkNx<8, uint32_t> {
-    public:
-        AI SkNx(const __m256i& vec) : fVec(vec) {}
-
-        AI SkNx() {}
-        AI SkNx(uint32_t v) : fVec(_mm256_set1_epi32(v)) {}
-        AI SkNx(uint32_t a, uint32_t b, uint32_t c, uint32_t d,
-                uint32_t e, uint32_t f, uint32_t g, uint32_t h)
-            : fVec(_mm256_setr_epi32(a,b,c,d, e,f,g,h)) {}
-
-        AI static SkNx Load(const void* ptr) { return _mm256_loadu_si256((const __m256i*)ptr); }
-        AI void store(void* ptr) const { _mm256_storeu_si256((__m256i*)ptr, fVec); }
-
-        AI SkNx operator + (const SkNx& o) const { return _mm256_add_epi32(fVec, o.fVec); }
-        AI SkNx operator - (const SkNx& o) const { return _mm256_sub_epi32(fVec, o.fVec); }
-        AI SkNx operator * (const SkNx& o) const { return _mm256_mullo_epi32(fVec, o.fVec); }
-
-        AI SkNx operator & (const SkNx& o) const { return _mm256_and_si256(fVec, o.fVec); }
-        AI SkNx operator | (const SkNx& o) const { return _mm256_or_si256(fVec, o.fVec); }
-        AI SkNx operator ^ (const SkNx& o) const { return _mm256_xor_si256(fVec, o.fVec); }
-
-        AI SkNx operator << (int bits) const { return _mm256_slli_epi32(fVec, bits); }
-        AI SkNx operator >> (int bits) const { return _mm256_srli_epi32(fVec, bits); }
-
-        AI uint32_t operator[](int k) const {
-            SkASSERT(0 <= k && k < 8);
-            union { __m256i v; uint32_t us[8]; } pun = {fVec};
-            return pun.us[k&7];
-        }
-
-        __m256i fVec;
-    };
-
-    // _mm256_unpack{lo,hi}_pd() auto-casting to and from __m256d.
-    AI static __m256 unpacklo_pd(__m256 x, __m256 y) {
-        return _mm256_castpd_ps(_mm256_unpacklo_pd(_mm256_castps_pd(x), _mm256_castps_pd(y)));
-    }
-    AI static __m256 unpackhi_pd(__m256 x, __m256 y) {
-        return _mm256_castpd_ps(_mm256_unpackhi_pd(_mm256_castps_pd(x), _mm256_castps_pd(y)));
-    }
-
-    template <>
-    class SkNx<8, float> {
-    public:
-        AI SkNx(const __m256& vec) : fVec(vec) {}
-
-        AI SkNx() {}
-        AI SkNx(float val) : fVec(_mm256_set1_ps(val)) {}
-        AI SkNx(float a, float b, float c, float d,
-                float e, float f, float g, float h) : fVec(_mm256_setr_ps(a,b,c,d,e,f,g,h)) {}
-
-        AI static SkNx Load(const void* ptr) { return _mm256_loadu_ps((const float*)ptr); }
-        AI void store(void* ptr) const { _mm256_storeu_ps((float*)ptr, fVec); }
-
-        AI static void Store4(void* ptr,
-                              const SkNx& r, const SkNx& g, const SkNx& b, const SkNx& a) {
-            __m256 rg0145 = _mm256_unpacklo_ps(r.fVec, g.fVec),  // r0 g0 r1 g1 | r4 g4 r5 g5
-                   rg2367 = _mm256_unpackhi_ps(r.fVec, g.fVec),  // r2 ...      | r6 ...
-                   ba0145 = _mm256_unpacklo_ps(b.fVec, a.fVec),  // b0 a0 b1 a1 | b4 a4 b5 a5
-                   ba2367 = _mm256_unpackhi_ps(b.fVec, a.fVec);  // b2 ...      | b6 ...
-
-            __m256 _04 = unpacklo_pd(rg0145, ba0145),  // r0 g0 b0 a0 | r4 g4 b4 a4
-                   _15 = unpackhi_pd(rg0145, ba0145),  // r1 ...      | r5 ...
-                   _26 = unpacklo_pd(rg2367, ba2367),  // r2 ...      | r6 ...
-                   _37 = unpackhi_pd(rg2367, ba2367);  // r3 ...      | r7 ...
-
-            __m256 _01 = _mm256_permute2f128_ps(_04, _15, 32),  // 32 == 0010 0000 == lo, lo
-                   _23 = _mm256_permute2f128_ps(_26, _37, 32),
-                   _45 = _mm256_permute2f128_ps(_04, _15, 49),  // 49 == 0011 0001 == hi, hi
-                   _67 = _mm256_permute2f128_ps(_26, _37, 49);
-
-            _mm256_storeu_ps((float*)ptr + 0*8, _01);
-            _mm256_storeu_ps((float*)ptr + 1*8, _23);
-            _mm256_storeu_ps((float*)ptr + 2*8, _45);
-            _mm256_storeu_ps((float*)ptr + 3*8, _67);
-        }
-        AI static void Load4(const void* ptr, SkNx* r, SkNx* g, SkNx* b, SkNx* a) {
-            Sk4f rl, gl, bl, al,
-                 rh, gh, bh, ah;
-            Sk4f::Load4((const float*)ptr +  0, &rl, &gl, &bl, &al);
-            Sk4f::Load4((const float*)ptr + 16, &rh, &gh, &bh, &ah);
-            *r = _mm256_setr_m128(rl.fVec, rh.fVec);
-            *g = _mm256_setr_m128(gl.fVec, gh.fVec);
-            *b = _mm256_setr_m128(bl.fVec, bh.fVec);
-            *a = _mm256_setr_m128(al.fVec, ah.fVec);
-        }
-
-        AI SkNx operator+(const SkNx& o) const { return _mm256_add_ps(fVec, o.fVec); }
-        AI SkNx operator-(const SkNx& o) const { return _mm256_sub_ps(fVec, o.fVec); }
-        AI SkNx operator*(const SkNx& o) const { return _mm256_mul_ps(fVec, o.fVec); }
-        AI SkNx operator/(const SkNx& o) const { return _mm256_div_ps(fVec, o.fVec); }
-
-        AI SkNx operator==(const SkNx& o) const { return _mm256_cmp_ps(fVec, o.fVec, _CMP_EQ_OQ); }
-        AI SkNx operator!=(const SkNx& o) const { return _mm256_cmp_ps(fVec, o.fVec, _CMP_NEQ_OQ); }
-        AI SkNx operator <(const SkNx& o) const { return _mm256_cmp_ps(fVec, o.fVec, _CMP_LT_OQ); }
-        AI SkNx operator >(const SkNx& o) const { return _mm256_cmp_ps(fVec, o.fVec, _CMP_GT_OQ); }
-        AI SkNx operator<=(const SkNx& o) const { return _mm256_cmp_ps(fVec, o.fVec, _CMP_LE_OQ); }
-        AI SkNx operator>=(const SkNx& o) const { return _mm256_cmp_ps(fVec, o.fVec, _CMP_GE_OQ); }
-
-        AI static SkNx Min(const SkNx& l, const SkNx& r) { return _mm256_min_ps(l.fVec, r.fVec); }
-        AI static SkNx Max(const SkNx& l, const SkNx& r) { return _mm256_max_ps(l.fVec, r.fVec); }
-
-        AI SkNx   sqrt() const { return _mm256_sqrt_ps (fVec); }
-        AI SkNx  rsqrt() const { return _mm256_rsqrt_ps(fVec); }
-        AI SkNx invert() const { return _mm256_rcp_ps  (fVec); }
-
-        AI SkNx abs() const { return _mm256_andnot_ps(_mm256_set1_ps(-0.0f), fVec); }
-        AI SkNx floor() const { return _mm256_floor_ps(fVec); }
-
-        AI float operator[](int k) const {
-            SkASSERT(0 <= k && k < 8);
-            union { __m256 v; float fs[8]; } pun = {fVec};
-            return pun.fs[k&7];
-        }
-
-        AI SkNx thenElse(const SkNx& t, const SkNx& e) const {
-            return _mm256_blendv_ps(e.fVec, t.fVec, fVec);
-        }
-
-        __m256 fVec;
-    };
-
-    AI static void SkNx_split(const Sk8f& v, Sk4f* lo, Sk4f* hi) {
-        *lo = _mm256_extractf128_ps(v.fVec, 0);
-        *hi = _mm256_extractf128_ps(v.fVec, 1);
-    }
-
-    AI static Sk8f SkNx_join(const Sk4f& lo, const Sk4f& hi) {
-        return _mm256_insertf128_ps(_mm256_castps128_ps256(lo.fVec), hi.fVec, 1);
-    }
-
-    AI static Sk8f SkNx_fma(const Sk8f& a, const Sk8f& b, const Sk8f& c) {
-        return _mm256_fmadd_ps(a.fVec, b.fVec, c.fVec);
-    }
-
-    template<> AI /*static*/ Sk8i SkNx_cast<int>(const Sk8b& src) {
-        return _mm256_cvtepu8_epi32(src.fVec);
-    }
-
-    template<> AI /*static*/ Sk8f SkNx_cast<float>(const Sk8b& src) {
-        return _mm256_cvtepi32_ps(SkNx_cast<int>(src).fVec);
-    }
-
-    template<> AI /*static*/ Sk8i SkNx_cast<int>(const Sk8h& src) {
-        return _mm256_cvtepu16_epi32(src.fVec);
-    }
-
-    template<> AI /*static*/ Sk8f SkNx_cast<float>(const Sk8h& src) {
-        return _mm256_cvtepi32_ps(SkNx_cast<int>(src).fVec);
-    }
-
-    template<> AI /*static*/ Sk8f SkNx_cast<float>(const Sk8i& src) {
-        return _mm256_cvtepi32_ps(src.fVec);
-    }
-
-    template<> AI /*static*/ Sk8i SkNx_cast<int>(const Sk8f& src) {
-        return _mm256_cvttps_epi32(src.fVec);
-    }
-
-    template<> AI /*static*/ Sk8h SkNx_cast<uint16_t>(const Sk8i& src) {
-        __m128i lo = _mm256_extractf128_si256(src.fVec, 0),
-                hi = _mm256_extractf128_si256(src.fVec, 1);
-        return _mm_packus_epi32(lo, hi);
-    }
-
-    template<> AI /*static*/ Sk8h SkNx_cast<uint16_t>(const Sk8f& src) {
-        return SkNx_cast<uint16_t>(SkNx_cast<int>(src));
-    }
-
-    template<> AI /*static*/ Sk8b SkNx_cast<uint8_t>(const Sk8i& src) {
-        auto _16 = SkNx_cast<uint16_t>(src);
-        return _mm_packus_epi16(_16.fVec, _16.fVec);
-    }
-
-#endif
-
 template<> AI /*static*/ Sk4f SkNx_cast<float, int32_t>(const Sk4i& src) {
     return _mm_cvtepi32_ps(src.fVec);
 }
diff --git a/src/opts/SkOpts_avx.cpp b/src/opts/SkOpts_avx.cpp
index 06e46d9..7e34330 100644
--- a/src/opts/SkOpts_avx.cpp
+++ b/src/opts/SkOpts_avx.cpp
@@ -8,12 +8,17 @@
 #include "SkSafe_math.h"   // Keep this first.
 #include "SkOpts.h"
 
-#define SK_OPTS_NS avx
-
 #if defined(_INC_MATH) && !defined(INC_MATH_IS_SAFE_NOW)
     #error We have included ucrt\math.h without protecting it against ODR violation.
 #endif
 
+#define SK_OPTS_NS avx
+#include "SkUtils_opts.h"
+
 namespace SkOpts {
-    void Init_avx() { }
+    void Init_avx() {
+        memset16 = SK_OPTS_NS::memset16;
+        memset32 = SK_OPTS_NS::memset32;
+        memset64 = SK_OPTS_NS::memset64;
+    }
 }
diff --git a/src/opts/SkOpts_sse41.cpp b/src/opts/SkOpts_sse41.cpp
index 7a90f76..17ce066 100644
--- a/src/opts/SkOpts_sse41.cpp
+++ b/src/opts/SkOpts_sse41.cpp
@@ -11,7 +11,6 @@
 #include "SkBlurImageFilter_opts.h"
 #include "SkBlitRow_opts.h"
 #include "SkBlend_opts.h"
-#include "SkRasterPipeline_opts.h"
 
 namespace SkOpts {
     void Init_sse41() {
@@ -20,6 +19,5 @@
         box_blur_yx          = sse41::box_blur_yx;
         srcover_srgb_srgb    = sse41::srcover_srgb_srgb;
         blit_row_s32a_opaque = sse41::blit_row_s32a_opaque;
-        run_pipeline         = sse41::run_pipeline;
     }
 }
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
deleted file mode 100644
index 3b15efa..0000000
--- a/src/opts/SkRasterPipeline_opts.h
+++ /dev/null
@@ -1,1253 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRasterPipeline_opts_DEFINED
-#define SkRasterPipeline_opts_DEFINED
-
-#include "SkColorPriv.h"
-#include "SkColorLookUpTable.h"
-#include "SkColorSpaceXform_A2B.h"
-#include "SkColorSpaceXformPriv.h"
-#include "SkHalf.h"
-#include "SkImageShaderContext.h"
-#include "SkMSAN.h"
-#include "SkPM4f.h"
-#include "SkPM4fPriv.h"
-#include "SkRasterPipeline.h"
-#include "SkShader.h"
-#include "SkSRGB.h"
-
-namespace {
-
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
-    static constexpr int N = 8;
-#else
-    static constexpr int N = 4;
-#endif
-
-    using SkNf = SkNx<N, float>;
-    using SkNi = SkNx<N, int32_t>;
-    using SkNu = SkNx<N, uint32_t>;
-    using SkNh = SkNx<N, uint16_t>;
-    using SkNb = SkNx<N, uint8_t>;
-
-    using Fn = void(SK_VECTORCALL *)(size_t x_tail, void** p, SkNf,SkNf,SkNf,SkNf,
-                                                              SkNf,SkNf,SkNf,SkNf);
-    // x_tail encodes two values x and tail as x*N+tail, where 0 <= tail < N.
-    // x is the induction variable we're walking along, incrementing by N each step.
-    // tail == 0 means work with a full N pixels; otherwise use only the low tail pixels.
-    //
-    // p is our program, a sequence of Fn to call interlaced with any void* context pointers.  E.g.
-    //    &load_8888
-    //    (src ptr)
-    //    &from_srgb
-    //    &move_src_dst
-    //    &load_f16
-    //    (dst ptr)
-    //    &swap
-    //    &srcover
-    //    &store_f16
-    //    (dst ptr)
-    //    &just_return
-
-}  // namespace
-
-#define SI static inline
-
-// Basically, return *(*ptr)++, maybe faster than the compiler can do it.
-SI void* load_and_increment(void*** ptr) {
-    // We do this often enough that it's worth hyper-optimizing.
-    // x86 can do this in one instruction if ptr is in rsi.
-    // (This is why p is the second argument to Fn: it's passed in rsi.)
-#if defined(__GNUC__) && defined(__x86_64__)
-    void* rax;
-    __asm__("lodsq" : "=a"(rax), "+S"(*ptr));
-    return rax;
-#else
-    return *(*ptr)++;
-#endif
-}
-
-// Stages are logically a pipeline, and physically are contiguous in an array.
-// To get to the next stage, we just increment our pointer to the next array element.
-SI void SK_VECTORCALL next(size_t x_tail, void** p, SkNf  r, SkNf  g, SkNf  b, SkNf  a,
-                                                    SkNf dr, SkNf dg, SkNf db, SkNf da) {
-    auto next = (Fn)load_and_increment(&p);
-    next(x_tail,p, r,g,b,a, dr,dg,db,da);
-}
-
-// Stages defined below always call next.
-// This is always the last stage, a backstop that actually returns to the caller when done.
-SI void SK_VECTORCALL just_return(size_t, void**, SkNf, SkNf, SkNf, SkNf,
-                                                  SkNf, SkNf, SkNf, SkNf) {}
-
-#define STAGE(name)                                                                      \
-    static SK_ALWAYS_INLINE void name##_kernel(size_t x, size_t tail,                    \
-                                               SkNf&  r, SkNf&  g, SkNf&  b, SkNf&  a,   \
-                                               SkNf& dr, SkNf& dg, SkNf& db, SkNf& da);  \
-    SI void SK_VECTORCALL name(size_t x_tail, void** p,                                  \
-                               SkNf  r, SkNf  g, SkNf  b, SkNf  a,                       \
-                               SkNf dr, SkNf dg, SkNf db, SkNf da) {                     \
-        name##_kernel(x_tail/N, x_tail%N, r,g,b,a, dr,dg,db,da);                         \
-        next(x_tail,p, r,g,b,a, dr,dg,db,da);                                            \
-    }                                                                                    \
-    static SK_ALWAYS_INLINE void name##_kernel(size_t x, size_t tail,                    \
-                                               SkNf&  r, SkNf&  g, SkNf&  b, SkNf&  a,   \
-                                               SkNf& dr, SkNf& dg, SkNf& db, SkNf& da)
-
-#define STAGE_CTX(name, Ctx)                                                             \
-    static SK_ALWAYS_INLINE void name##_kernel(Ctx ctx, size_t x, size_t tail,           \
-                                               SkNf&  r, SkNf&  g, SkNf&  b, SkNf&  a,   \
-                                               SkNf& dr, SkNf& dg, SkNf& db, SkNf& da);  \
-    SI void SK_VECTORCALL name(size_t x_tail, void** p,                                  \
-                               SkNf  r, SkNf  g, SkNf  b, SkNf  a,                       \
-                               SkNf dr, SkNf dg, SkNf db, SkNf da) {                     \
-        auto ctx = (Ctx)load_and_increment(&p);                                          \
-        name##_kernel(ctx, x_tail/N, x_tail%N, r,g,b,a, dr,dg,db,da);                    \
-        next(x_tail,p, r,g,b,a, dr,dg,db,da);                                            \
-    }                                                                                    \
-    static SK_ALWAYS_INLINE void name##_kernel(Ctx ctx, size_t x, size_t tail,           \
-                                               SkNf&  r, SkNf&  g, SkNf&  b, SkNf&  a,   \
-                                               SkNf& dr, SkNf& dg, SkNf& db, SkNf& da)
-
-// Many xfermodes apply the same logic to each channel.
-#define RGBA_XFERMODE(name)                                                     \
-    static SK_ALWAYS_INLINE SkNf name##_kernel(const SkNf& s, const SkNf& sa,   \
-                                               const SkNf& d, const SkNf& da);  \
-    SI void SK_VECTORCALL name(size_t x_tail, void** p,                         \
-                               SkNf  r, SkNf  g, SkNf  b, SkNf  a,              \
-                               SkNf dr, SkNf dg, SkNf db, SkNf da) {            \
-        r = name##_kernel(r,a,dr,da);                                           \
-        g = name##_kernel(g,a,dg,da);                                           \
-        b = name##_kernel(b,a,db,da);                                           \
-        a = name##_kernel(a,a,da,da);                                           \
-        next(x_tail,p, r,g,b,a, dr,dg,db,da);                                   \
-    }                                                                           \
-    static SK_ALWAYS_INLINE SkNf name##_kernel(const SkNf& s, const SkNf& sa,   \
-                                               const SkNf& d, const SkNf& da)
-
-// Most of the rest apply the same logic to color channels and use srcover's alpha logic.
-#define RGB_XFERMODE(name)                                                      \
-    static SK_ALWAYS_INLINE SkNf name##_kernel(const SkNf& s, const SkNf& sa,   \
-                                               const SkNf& d, const SkNf& da);  \
-    SI void SK_VECTORCALL name(size_t x_tail, void** p,                         \
-                               SkNf  r, SkNf  g, SkNf  b, SkNf  a,              \
-                               SkNf dr, SkNf dg, SkNf db, SkNf da) {            \
-        r = name##_kernel(r,a,dr,da);                                           \
-        g = name##_kernel(g,a,dg,da);                                           \
-        b = name##_kernel(b,a,db,da);                                           \
-        a = a + (da * (1.0f-a));                                                \
-        next(x_tail,p, r,g,b,a, dr,dg,db,da);                                   \
-    }                                                                           \
-    static SK_ALWAYS_INLINE SkNf name##_kernel(const SkNf& s, const SkNf& sa,   \
-                                               const SkNf& d, const SkNf& da)
-
-template <typename T>
-SI SkNx<N,T> load(size_t tail, const T* src) {
-    if (tail) {
-        T buf[8];
-        memset(buf, 0, 8*sizeof(T));
-        switch (tail & (N-1)) {
-            case 7: buf[6] = src[6];
-            case 6: buf[5] = src[5];
-            case 5: buf[4] = src[4];
-            case 4: buf[3] = src[3];
-            case 3: buf[2] = src[2];
-            case 2: buf[1] = src[1];
-        }
-        buf[0] = src[0];
-        return SkNx<N,T>::Load(buf);
-    }
-    return SkNx<N,T>::Load(src);
-}
-template <typename T>
-SI SkNx<N,T> gather(size_t tail, const T* src, const SkNi& offset) {
-    if (tail) {
-        T buf[8] = {0};
-        switch (tail & (N-1)) {
-            case 7: buf[6] = src[offset[6]];
-            case 6: buf[5] = src[offset[5]];
-            case 5: buf[4] = src[offset[4]];
-            case 4: buf[3] = src[offset[3]];
-            case 3: buf[2] = src[offset[2]];
-            case 2: buf[1] = src[offset[1]];
-        }
-        buf[0] = src[offset[0]];
-        return SkNx<N,T>::Load(buf);
-    }
-    T buf[8];
-    for (size_t i = 0; i < N; i++) {
-        buf[i] = src[offset[i]];
-    }
-    return SkNx<N,T>::Load(buf);
-}
-template <typename T>
-SI void store(size_t tail, const SkNx<N,T>& v, T* dst) {
-    if (tail) {
-        switch (tail & (N-1)) {
-            case 7: dst[6] = v[6];
-            case 6: dst[5] = v[5];
-            case 5: dst[4] = v[4];
-            case 4: dst[3] = v[3];
-            case 3: dst[2] = v[2];
-            case 2: dst[1] = v[1];
-        }
-        dst[0] = v[0];
-        return;
-    }
-    v.store(dst);
-}
-
-#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2
-    SI __m256i mask(size_t tail) {
-        static const int masks[][8] = {
-            {~0,~0,~0,~0, ~0,~0,~0,~0 },  // remember, tail == 0 ~~> load all N
-            {~0, 0, 0, 0,  0, 0, 0, 0 },
-            {~0,~0, 0, 0,  0, 0, 0, 0 },
-            {~0,~0,~0, 0,  0, 0, 0, 0 },
-            {~0,~0,~0,~0,  0, 0, 0, 0 },
-            {~0,~0,~0,~0, ~0, 0, 0, 0 },
-            {~0,~0,~0,~0, ~0,~0, 0, 0 },
-            {~0,~0,~0,~0, ~0,~0,~0, 0 },
-        };
-        return SkNi::Load(masks + tail).fVec;
-    }
-
-    SI SkNi load(size_t tail, const  int32_t* src) {
-        return tail ? _mm256_maskload_epi32((const int*)src, mask(tail))
-                    : SkNi::Load(src);
-    }
-    SI SkNu load(size_t tail, const uint32_t* src) {
-        return tail ? _mm256_maskload_epi32((const int*)src, mask(tail))
-                    : SkNu::Load(src);
-    }
-    SI SkNf load(size_t tail, const float* src) {
-        return tail ? _mm256_maskload_ps((const float*)src, mask(tail))
-                    : SkNf::Load(src);
-    }
-    SI SkNi gather(size_t tail, const  int32_t* src, const SkNi& offset) {
-        auto m = mask(tail);
-        return _mm256_mask_i32gather_epi32(SkNi(0).fVec, (const int*)src, offset.fVec, m, 4);
-    }
-    SI SkNu gather(size_t tail, const uint32_t* src, const SkNi& offset) {
-        auto m = mask(tail);
-        return _mm256_mask_i32gather_epi32(SkNi(0).fVec, (const int*)src, offset.fVec, m, 4);
-    }
-    SI SkNf gather(size_t tail, const float* src, const SkNi& offset) {
-        auto m = _mm256_castsi256_ps(mask(tail));
-        return _mm256_mask_i32gather_ps(SkNf(0).fVec, (const float*)src, offset.fVec, m, 4);
-    }
-
-    static const char* bug = "I don't think MSAN understands maskstore.";
-
-    SI void store(size_t tail, const SkNi& v,  int32_t* dst) {
-        if (tail) {
-            _mm256_maskstore_epi32((int*)dst, mask(tail), v.fVec);
-            return sk_msan_mark_initialized(dst, dst+tail, bug);
-        }
-        v.store(dst);
-    }
-    SI void store(size_t tail, const SkNu& v, uint32_t* dst) {
-        if (tail) {
-            _mm256_maskstore_epi32((int*)dst, mask(tail), v.fVec);
-            return sk_msan_mark_initialized(dst, dst+tail, bug);
-        }
-        v.store(dst);
-    }
-    SI void store(size_t tail, const SkNf& v, float* dst) {
-        if (tail) {
-            _mm256_maskstore_ps((float*)dst, mask(tail), v.fVec);
-            return sk_msan_mark_initialized(dst, dst+tail, bug);
-        }
-        v.store(dst);
-    }
-#endif
-
-SI SkNf SkNf_fma(const SkNf& f, const SkNf& m, const SkNf& a) { return SkNx_fma(f,m,a); }
-
-SI SkNi SkNf_round(const SkNf& x, const SkNf& scale) {
-    // Every time I try, _mm_cvtps_epi32 benches as slower than using FMA and _mm_cvttps_epi32.  :/
-    return SkNx_cast<int>(SkNf_fma(x,scale, 0.5f));
-}
-
-SI SkNf SkNf_from_byte(const SkNi& x) {
-    // Same trick as in store_8888: 0x470000BB == 32768.0f + BB/256.0f for all bytes BB.
-    auto v = 0x47000000 | x;
-    // Read this as (pun_float(v) - 32768.0f) * (256/255.0f), redistributed to be an FMA.
-    return SkNf_fma(SkNf::Load(&v), 256/255.0f, -32768*256/255.0f);
-}
-SI SkNf SkNf_from_byte(const SkNu& x) { return SkNf_from_byte(SkNi::Load(&x)); }
-SI SkNf SkNf_from_byte(const SkNb& x) { return SkNf_from_byte(SkNx_cast<int>(x)); }
-
-SI void from_8888(const SkNu& _8888, SkNf* r, SkNf* g, SkNf* b, SkNf* a) {
-    *r = SkNf_from_byte((_8888      ) & 0xff);
-    *g = SkNf_from_byte((_8888 >>  8) & 0xff);
-    *b = SkNf_from_byte((_8888 >> 16) & 0xff);
-    *a = SkNf_from_byte((_8888 >> 24)       );
-}
-SI void from_4444(const SkNh& _4444, SkNf* r, SkNf* g, SkNf* b, SkNf* a) {
-    auto _32_bit = SkNx_cast<int>(_4444);
-
-    *r = SkNx_cast<float>(_32_bit & (0xF << SK_R4444_SHIFT)) * (1.0f / (0xF << SK_R4444_SHIFT));
-    *g = SkNx_cast<float>(_32_bit & (0xF << SK_G4444_SHIFT)) * (1.0f / (0xF << SK_G4444_SHIFT));
-    *b = SkNx_cast<float>(_32_bit & (0xF << SK_B4444_SHIFT)) * (1.0f / (0xF << SK_B4444_SHIFT));
-    *a = SkNx_cast<float>(_32_bit & (0xF << SK_A4444_SHIFT)) * (1.0f / (0xF << SK_A4444_SHIFT));
-}
-SI void from_565(const SkNh& _565, SkNf* r, SkNf* g, SkNf* b) {
-    auto _32_bit = SkNx_cast<int>(_565);
-
-    *r = SkNx_cast<float>(_32_bit & SK_R16_MASK_IN_PLACE) * (1.0f / SK_R16_MASK_IN_PLACE);
-    *g = SkNx_cast<float>(_32_bit & SK_G16_MASK_IN_PLACE) * (1.0f / SK_G16_MASK_IN_PLACE);
-    *b = SkNx_cast<float>(_32_bit & SK_B16_MASK_IN_PLACE) * (1.0f / SK_B16_MASK_IN_PLACE);
-}
-SI void from_f16(const void* px, SkNf* r, SkNf* g, SkNf* b, SkNf* a) {
-    SkNh rh, gh, bh, ah;
-    SkNh::Load4(px, &rh, &gh, &bh, &ah);
-
-    *r = SkHalfToFloat_finite_ftz(rh);
-    *g = SkHalfToFloat_finite_ftz(gh);
-    *b = SkHalfToFloat_finite_ftz(bh);
-    *a = SkHalfToFloat_finite_ftz(ah);
-}
-
-STAGE_CTX(trace, const char*) {
-    SkDebugf("%s\n", ctx);
-}
-STAGE(registers) {
-    auto print = [](const char* name, const SkNf& v) {
-        SkDebugf("%s:", name);
-        for (int i = 0; i < N; i++) {
-            SkDebugf(" %g", v[i]);
-        }
-        SkDebugf("\n");
-    };
-    print(" r",  r);
-    print(" g",  g);
-    print(" b",  b);
-    print(" a",  a);
-    print("dr", dr);
-    print("dg", dg);
-    print("db", db);
-    print("da", da);
-}
-
-STAGE(clamp_0) {
-    a = SkNf::Max(a, 0.0f);
-    r = SkNf::Max(r, 0.0f);
-    g = SkNf::Max(g, 0.0f);
-    b = SkNf::Max(b, 0.0f);
-}
-STAGE(clamp_1) {
-    a = SkNf::Min(a, 1.0f);
-    r = SkNf::Min(r, 1.0f);
-    g = SkNf::Min(g, 1.0f);
-    b = SkNf::Min(b, 1.0f);
-}
-STAGE(clamp_a) {
-    a = SkNf::Min(a, 1.0f);
-    r = SkNf::Min(r, a);
-    g = SkNf::Min(g, a);
-    b = SkNf::Min(b, a);
-}
-
-STAGE(unpremul) {
-    auto scale = (a == 0.0f).thenElse(0.0f, 1.0f/a);
-    r *= scale;
-    g *= scale;
-    b *= scale;
-}
-STAGE(premul) {
-    r *= a;
-    g *= a;
-    b *= a;
-}
-
-STAGE_CTX(set_rgb, const float*) {
-    r = ctx[0];
-    g = ctx[1];
-    b = ctx[2];
-}
-STAGE(swap_rb) { SkTSwap(r,b); }
-
-STAGE(move_src_dst) {
-    dr = r;
-    dg = g;
-    db = b;
-    da = a;
-}
-STAGE(move_dst_src) {
-    r = dr;
-    g = dg;
-    b = db;
-    a = da;
-}
-STAGE(swap) {
-    SkTSwap(r,dr);
-    SkTSwap(g,dg);
-    SkTSwap(b,db);
-    SkTSwap(a,da);
-}
-
-STAGE(from_srgb) {
-    r = sk_linear_from_srgb_math(r);
-    g = sk_linear_from_srgb_math(g);
-    b = sk_linear_from_srgb_math(b);
-}
-STAGE(to_srgb) {
-    r = sk_linear_to_srgb_needs_round(r);
-    g = sk_linear_to_srgb_needs_round(g);
-    b = sk_linear_to_srgb_needs_round(b);
-}
-
-STAGE(from_2dot2) {
-    auto from_2dot2 = [](const SkNf& x) {
-        // x^(141/64) = x^(2.20312) is a great approximation of the true value, x^(2.2).
-        // (note: x^(35/16) = x^(2.1875) is an okay one as well and would be quicker)
-        auto x16 = x.rsqrt().rsqrt().rsqrt().rsqrt();   // x^(1/16) = x^(4/64);
-        auto x64 = x16.rsqrt().rsqrt();                 // x^(1/64)
-
-        // x^(141/64) = x^(128/64) * x^(12/64) * x^(1/64)
-        return SkNf::Max((x*x) * (x16*x16*x16) * (x64), 0.0f);
-    };
-
-    r = from_2dot2(r);
-    g = from_2dot2(g);
-    b = from_2dot2(b);
-}
-STAGE(to_2dot2) {
-    auto to_2dot2 = [](const SkNf& x) {
-        // x^(29/64) is a very good approximation of the true value, x^(1/2.2).
-        auto x2  = x.rsqrt(),                            // x^(-1/2)
-             x32 = x2.rsqrt().rsqrt().rsqrt().rsqrt(),   // x^(-1/32)
-             x64 = x32.rsqrt();                          // x^(+1/64)
-
-        // 29 = 32 - 2 - 1
-        return SkNf::Max(x2.invert() * x32 * x64.invert(), 0.0f); // Watch out for NaN.
-    };
-
-    r = to_2dot2(r);
-    g = to_2dot2(g);
-    b = to_2dot2(b);
-}
-
-// The default shader produces a constant color (from the SkPaint).
-STAGE_CTX(constant_color, const SkPM4f*) {
-    r = ctx->r();
-    g = ctx->g();
-    b = ctx->b();
-    a = ctx->a();
-}
-
-// Set up registers with values relevant to shaders.
-STAGE_CTX(seed_shader, const int*) {
-    int y = *ctx;
-
-    static const float dx[] = { 0,1,2,3,4,5,6,7 };
-    r = x + 0.5f + SkNf::Load(dx);  // dst pixel center x coordinates
-    g = y + 0.5f;                   // dst pixel center y coordinate(s)
-    b = 1.0f;
-    a = 0.0f;
-    dr = dg = db = da = 0.0f;
-}
-
-// s' = sc for a scalar c.
-STAGE_CTX(scale_1_float, const float*) {
-    SkNf c = *ctx;
-
-    r *= c;
-    g *= c;
-    b *= c;
-    a *= c;
-}
-// s' = sc for 8-bit c.
-STAGE_CTX(scale_u8, const uint8_t**) {
-    auto ptr = *ctx + x;
-    SkNf c = SkNf_from_byte(load(tail, ptr));
-
-    r = r*c;
-    g = g*c;
-    b = b*c;
-    a = a*c;
-}
-
-SI SkNf lerp(const SkNf& from, const SkNf& to, const SkNf& cov) {
-    return SkNf_fma(to-from, cov, from);
-}
-
-// s' = d(1-c) + sc, for a scalar c.
-STAGE_CTX(lerp_1_float, const float*) {
-    SkNf c = *ctx;
-
-    r = lerp(dr, r, c);
-    g = lerp(dg, g, c);
-    b = lerp(db, b, c);
-    a = lerp(da, a, c);
-}
-
-// s' = d(1-c) + sc for 8-bit c.
-STAGE_CTX(lerp_u8, const uint8_t**) {
-    auto ptr = *ctx + x;
-    SkNf c = SkNf_from_byte(load(tail, ptr));
-
-    r = lerp(dr, r, c);
-    g = lerp(dg, g, c);
-    b = lerp(db, b, c);
-    a = lerp(da, a, c);
-}
-
-// s' = d(1-c) + sc for 565 c.
-STAGE_CTX(lerp_565, const uint16_t**) {
-    auto ptr = *ctx + x;
-    SkNf cr, cg, cb;
-    from_565(load(tail, ptr), &cr, &cg, &cb);
-
-    r = lerp(dr, r, cr);
-    g = lerp(dg, g, cg);
-    b = lerp(db, b, cb);
-    a = 1.0f;
-}
-
-STAGE_CTX(load_a8, const uint8_t**) {
-    auto ptr = *ctx + x;
-    r = g = b = 0.0f;
-    a = SkNf_from_byte(load(tail, ptr));
-}
-STAGE_CTX(store_a8, uint8_t**) {
-    auto ptr = *ctx + x;
-    store(tail, SkNx_cast<uint8_t>(SkNf_round(255.0f, a)), ptr);
-}
-
-STAGE_CTX(load_g8, const uint8_t**) {
-    auto ptr = *ctx + x;
-    r = g = b = SkNf_from_byte(load(tail, ptr));
-    a = 1.0f;
-}
-
-STAGE_CTX(load_565, const uint16_t**) {
-    auto ptr = *ctx + x;
-    from_565(load(tail, ptr), &r,&g,&b);
-    a = 1.0f;
-}
-STAGE_CTX(store_565, uint16_t**) {
-    auto ptr = *ctx + x;
-    store(tail, SkNx_cast<uint16_t>( SkNf_round(r, SK_R16_MASK) << SK_R16_SHIFT
-                                   | SkNf_round(g, SK_G16_MASK) << SK_G16_SHIFT
-                                   | SkNf_round(b, SK_B16_MASK) << SK_B16_SHIFT), ptr);
-}
-
-STAGE_CTX(load_4444, const uint16_t**) {
-    auto ptr = *ctx + x;
-    from_4444(load(tail, ptr), &r,&g,&b,&a);
-}
-STAGE_CTX(store_4444, uint16_t**) {
-    auto ptr = *ctx + x;
-    store(tail, SkNx_cast<uint16_t>( SkNf_round(r, 0xF) << SK_R4444_SHIFT
-                                   | SkNf_round(g, 0xF) << SK_G4444_SHIFT
-                                   | SkNf_round(b, 0xF) << SK_B4444_SHIFT
-                                   | SkNf_round(a, 0xF) << SK_A4444_SHIFT), ptr);
-}
-
-STAGE_CTX(load_f16, const uint64_t**) {
-    auto ptr = *ctx + x;
-
-    const void* src = ptr;
-    SkNx<N, uint64_t> px;
-    if (tail) {
-        px = load(tail, ptr);
-        src = &px;
-    }
-    from_f16(src, &r, &g, &b, &a);
-}
-STAGE_CTX(store_f16, uint64_t**) {
-    auto ptr = *ctx + x;
-
-    SkNx<N, uint64_t> px;
-    SkNh::Store4(tail ? (void*)&px : (void*)ptr, SkFloatToHalf_finite_ftz(r),
-                                                 SkFloatToHalf_finite_ftz(g),
-                                                 SkFloatToHalf_finite_ftz(b),
-                                                 SkFloatToHalf_finite_ftz(a));
-    if (tail) {
-        store(tail, px, ptr);
-    }
-}
-
-STAGE_CTX(load_f32, const SkPM4f**) {
-    auto ptr = *ctx + x;
-
-    const void* src = ptr;
-    SkNx<N, SkPM4f> px;
-    if (tail) {
-        px = load(tail, ptr);
-        src = &px;
-    }
-    SkNf::Load4(src, &r, &g, &b, &a);
-}
-STAGE_CTX(store_f32, SkPM4f**) {
-    auto ptr = *ctx + x;
-
-    SkNx<N, SkPM4f> px;
-    SkNf::Store4(tail ? (void*)&px : (void*)ptr, r,g,b,a);
-    if (tail) {
-        store(tail, px, ptr);
-    }
-}
-
-
-STAGE_CTX(load_8888, const uint32_t**) {
-    auto ptr = *ctx + x;
-    from_8888(load(tail, ptr), &r, &g, &b, &a);
-}
-STAGE_CTX(store_8888, uint32_t**) {
-    auto byte = [](const SkNf& x, int ix) {
-        // Here's a neat trick: 0x47000000 == 32768.0f, and 0x470000ff == 32768.0f + (255/256.0f).
-        auto v = SkNf_fma(255/256.0f, x, 32768.0f);
-        switch (ix) {
-            case 0: return SkNi::Load(&v) & 0xff;  // R
-            case 3: return SkNi::Load(&v) << 24;   // A
-        }
-        return (SkNi::Load(&v) & 0xff) << (8*ix);  // B or G
-    };
-
-    auto ptr = *ctx + x;
-    store(tail, byte(r,0)|byte(g,1)|byte(b,2)|byte(a,3), (int*)ptr);
-}
-
-STAGE_CTX(load_u16_be, const uint64_t**) {
-    auto ptr = *ctx + x;
-    const void* src = ptr;
-    SkNx<N, uint64_t> px;
-    if (tail) {
-        px = load(tail, ptr);
-        src = &px;
-    }
-
-    SkNh rh, gh, bh, ah;
-    SkNh::Load4(src, &rh, &gh, &bh, &ah);
-    r = (1.0f / 65535.0f) * SkNx_cast<float>((rh << 8) | (rh >> 8));
-    g = (1.0f / 65535.0f) * SkNx_cast<float>((gh << 8) | (gh >> 8));
-    b = (1.0f / 65535.0f) * SkNx_cast<float>((bh << 8) | (bh >> 8));
-    a = (1.0f / 65535.0f) * SkNx_cast<float>((ah << 8) | (ah >> 8));
-}
-
-STAGE_CTX(load_rgb_u16_be, const uint16_t**) {
-    auto ptr = *ctx + 3*x;
-    const void* src = ptr;
-    uint16_t buf[N*3] = {0};
-    if (tail) {
-        memcpy(buf, src, tail*3*sizeof(uint16_t));
-        src = buf;
-    }
-
-    SkNh rh, gh, bh;
-    SkNh::Load3(src, &rh, &gh, &bh);
-    r = (1.0f / 65535.0f) * SkNx_cast<float>((rh << 8) | (rh >> 8));
-    g = (1.0f / 65535.0f) * SkNx_cast<float>((gh << 8) | (gh >> 8));
-    b = (1.0f / 65535.0f) * SkNx_cast<float>((bh << 8) | (bh >> 8));
-    a = 1.0f;
-}
-
-STAGE_CTX(store_u16_be, uint64_t**) {
-    auto to_u16_be = [](const SkNf& x) {
-        SkNh x16 = SkNx_cast<uint16_t>(65535.0f * x);
-        return (x16 << 8) | (x16 >> 8);
-    };
-
-    auto ptr = *ctx + x;
-    SkNx<N, uint64_t> px;
-    SkNh::Store4(tail ? (void*)&px : (void*)ptr, to_u16_be(r),
-                                                 to_u16_be(g),
-                                                 to_u16_be(b),
-                                                 to_u16_be(a));
-    if (tail) {
-        store(tail, px, ptr);
-    }
-}
-
-STAGE_CTX(load_tables, const LoadTablesContext*) {
-    auto ptr = (const uint32_t*)ctx->fSrc + x;
-
-    SkNu rgba = load(tail, ptr);
-    auto to_int = [](const SkNu& v) { return SkNi::Load(&v); };
-    r = gather(tail, ctx->fR, to_int((rgba >>  0) & 0xff));
-    g = gather(tail, ctx->fG, to_int((rgba >>  8) & 0xff));
-    b = gather(tail, ctx->fB, to_int((rgba >> 16) & 0xff));
-    a = SkNf_from_byte(rgba >> 24);
-}
-
-STAGE_CTX(load_tables_u16_be, const LoadTablesContext*) {
-    auto ptr = (const uint64_t*)ctx->fSrc + x;
-    const void* src = ptr;
-    SkNx<N, uint64_t> px;
-    if (tail) {
-        px = load(tail, ptr);
-        src = &px;
-    }
-
-    SkNh rh, gh, bh, ah;
-    SkNh::Load4(src, &rh, &gh, &bh, &ah);
-
-    // ctx->fSrc is big-endian, so "& 0xff" grabs the 8 most significant bits of each component.
-    r = gather(tail, ctx->fR, SkNx_cast<int>(rh & 0xff));
-    g = gather(tail, ctx->fG, SkNx_cast<int>(gh & 0xff));
-    b = gather(tail, ctx->fB, SkNx_cast<int>(bh & 0xff));
-    a = (1.0f / 65535.0f) * SkNx_cast<float>((ah << 8) | (ah >> 8));
-}
-
-STAGE_CTX(load_tables_rgb_u16_be, const LoadTablesContext*) {
-    auto ptr = (const uint16_t*)ctx->fSrc + 3*x;
-    const void* src = ptr;
-    uint16_t buf[N*3] = {0};
-    if (tail) {
-        memcpy(buf, src, tail*3*sizeof(uint16_t));
-        src = buf;
-    }
-
-    SkNh rh, gh, bh;
-    SkNh::Load3(src, &rh, &gh, &bh);
-
-    // ctx->fSrc is big-endian, so "& 0xff" grabs the 8 most significant bits of each component.
-    r = gather(tail, ctx->fR, SkNx_cast<int>(rh & 0xff));
-    g = gather(tail, ctx->fG, SkNx_cast<int>(gh & 0xff));
-    b = gather(tail, ctx->fB, SkNx_cast<int>(bh & 0xff));
-    a = 1.0f;
-}
-
-SI SkNf inv(const SkNf& x) { return 1.0f - x; }
-
-RGBA_XFERMODE(clear)    { return 0.0f; }
-RGBA_XFERMODE(srcatop)  { return s*da + d*inv(sa); }
-RGBA_XFERMODE(srcin)    { return s * da; }
-RGBA_XFERMODE(srcout)   { return s * inv(da); }
-RGBA_XFERMODE(srcover)  { return SkNf_fma(d, inv(sa), s); }
-RGBA_XFERMODE(dstatop)  { return srcatop_kernel(d,da,s,sa); }
-RGBA_XFERMODE(dstin)    { return srcin_kernel  (d,da,s,sa); }
-RGBA_XFERMODE(dstout)   { return srcout_kernel (d,da,s,sa); }
-RGBA_XFERMODE(dstover)  { return srcover_kernel(d,da,s,sa); }
-
-RGBA_XFERMODE(modulate) { return s*d; }
-RGBA_XFERMODE(multiply) { return s*inv(da) + d*inv(sa) + s*d; }
-RGBA_XFERMODE(plus_)    { return s + d; }
-RGBA_XFERMODE(screen)   { return s + d - s*d; }
-RGBA_XFERMODE(xor_)     { return s*inv(da) + d*inv(sa); }
-
-RGB_XFERMODE(colorburn) {
-    return (d == da  ).thenElse(d + s*inv(da),
-           (s == 0.0f).thenElse(s + d*inv(sa),
-                                sa*(da - SkNf::Min(da, (da-d)*sa/s)) + s*inv(da) + d*inv(sa)));
-}
-RGB_XFERMODE(colordodge) {
-    return (d == 0.0f).thenElse(d + s*inv(da),
-           (s == sa  ).thenElse(s + d*inv(sa),
-                                sa*SkNf::Min(da, (d*sa)/(sa - s)) + s*inv(da) + d*inv(sa)));
-}
-RGB_XFERMODE(darken)     { return s + d - SkNf::Max(s*da, d*sa); }
-RGB_XFERMODE(difference) { return s + d - 2.0f*SkNf::Min(s*da,d*sa); }
-RGB_XFERMODE(exclusion)  { return s + d - 2.0f*s*d; }
-RGB_XFERMODE(hardlight) {
-    return s*inv(da) + d*inv(sa)
-         + (2.0f*s <= sa).thenElse(2.0f*s*d, sa*da - 2.0f*(da-d)*(sa-s));
-}
-RGB_XFERMODE(lighten) { return s + d - SkNf::Min(s*da, d*sa); }
-RGB_XFERMODE(overlay) { return hardlight_kernel(d,da,s,sa); }
-RGB_XFERMODE(softlight) {
-    SkNf m  = (da > 0.0f).thenElse(d / da, 0.0f),
-         s2 = 2.0f*s,
-         m4 = 4.0f*m;
-
-    // The logic forks three ways:
-    //    1. dark src?
-    //    2. light src, dark dst?
-    //    3. light src, light dst?
-    SkNf darkSrc = d*(sa + (s2 - sa)*(1.0f - m)),     // Used in case 1.
-         darkDst = (m4*m4 + m4)*(m - 1.0f) + 7.0f*m,  // Used in case 2.
-         liteDst = m.rsqrt().invert() - m,            // Used in case 3.
-         liteSrc = d*sa + da*(s2 - sa) * (4.0f*d <= da).thenElse(darkDst, liteDst);  // 2 or 3?
-    return s*inv(da) + d*inv(sa) + (s2 <= sa).thenElse(darkSrc, liteSrc);  // 1 or (2 or 3)?
-}
-
-STAGE(luminance_to_alpha) {
-    a = SK_LUM_COEFF_R*r + SK_LUM_COEFF_G*g + SK_LUM_COEFF_B*b;
-    r = g = b = 0;
-}
-
-STAGE(rgb_to_hsl) {
-    auto max = SkNf::Max(SkNf::Max(r, g), b);
-    auto min = SkNf::Min(SkNf::Min(r, g), b);
-    auto l = 0.5f * (max + min);
-
-    auto d = max - min;
-    auto d_inv = 1.0f/d;
-    auto s = (max == min).thenElse(0.0f,
-        d/(l > 0.5f).thenElse(2.0f - max - min, max + min));
-    SkNf h = (max != r).thenElse(0.0f,
-        (g - b)*d_inv + (g < b).thenElse(6.0f, 0.0f));
-    h = (max == g).thenElse((b - r)*d_inv + 2.0f, h);
-    h = (max == b).thenElse((r - g)*d_inv + 4.0f, h);
-    h *= (1/6.0f);
-
-    h = (max == min).thenElse(0.0f, h);
-
-    r = h;
-    g = s;
-    b = l;
-}
-
-STAGE(hsl_to_rgb) {
-    auto h = r;
-    auto s = g;
-    auto l = b;
-    auto q = (l < 0.5f).thenElse(l*(1.0f + s), l + s - l*s);
-    auto p = 2.0f*l - q;
-
-    auto hue_to_rgb = [](const SkNf& p, const SkNf& q, const SkNf& t) {
-        auto t2 = (t < 0.0f).thenElse(t + 1.0f, (t > 1.0f).thenElse(t - 1.0f, t));
-        return (t2 < (1/6.0f)).thenElse(
-            p + (q - p)*6.0f*t, (t2 < (3/6.0f)).thenElse(
-                q, (t2 < (4/6.0f)).thenElse(
-                    p + (q - p)*((4/6.0f) - t2)*6.0f, p)));
-    };
-
-    r = (s == 0.f).thenElse(l, hue_to_rgb(p, q, h + (1/3.0f)));
-    g = (s == 0.f).thenElse(l, hue_to_rgb(p, q, h));
-    b = (s == 0.f).thenElse(l, hue_to_rgb(p, q, h - (1/3.0f)));
-}
-
-STAGE_CTX(matrix_2x3, const float*) {
-    auto m = ctx;
-
-    auto R = SkNf_fma(r,m[0], SkNf_fma(g,m[2], m[4])),
-         G = SkNf_fma(r,m[1], SkNf_fma(g,m[3], m[5]));
-    r = R;
-    g = G;
-}
-STAGE_CTX(matrix_3x4, const float*) {
-    auto m = ctx;
-
-    auto R = SkNf_fma(r,m[0], SkNf_fma(g,m[3], SkNf_fma(b,m[6], m[ 9]))),
-         G = SkNf_fma(r,m[1], SkNf_fma(g,m[4], SkNf_fma(b,m[7], m[10]))),
-         B = SkNf_fma(r,m[2], SkNf_fma(g,m[5], SkNf_fma(b,m[8], m[11])));
-    r = R;
-    g = G;
-    b = B;
-}
-STAGE_CTX(matrix_4x5, const float*) {
-    auto m = ctx;
-
-    auto R = SkNf_fma(r,m[0], SkNf_fma(g,m[4], SkNf_fma(b,m[ 8], SkNf_fma(a,m[12], m[16])))),
-         G = SkNf_fma(r,m[1], SkNf_fma(g,m[5], SkNf_fma(b,m[ 9], SkNf_fma(a,m[13], m[17])))),
-         B = SkNf_fma(r,m[2], SkNf_fma(g,m[6], SkNf_fma(b,m[10], SkNf_fma(a,m[14], m[18])))),
-         A = SkNf_fma(r,m[3], SkNf_fma(g,m[7], SkNf_fma(b,m[11], SkNf_fma(a,m[15], m[19]))));
-    r = R;
-    g = G;
-    b = B;
-    a = A;
-}
-STAGE_CTX(matrix_perspective, const float*) {
-    // N.B. unlike the matrix_NxM stages, this takes a row-major matrix.
-    auto m = ctx;
-
-    auto R = SkNf_fma(r,m[0], SkNf_fma(g,m[1], m[2])),
-         G = SkNf_fma(r,m[3], SkNf_fma(g,m[4], m[5])),
-         Z = SkNf_fma(r,m[6], SkNf_fma(g,m[7], m[8]));
-    r = R * Z.invert();
-    g = G * Z.invert();
-}
-
-SI SkNf parametric(const SkNf& v, const SkColorSpaceTransferFn& p) {
-    float result[N];   // Unconstrained powf() doesn't vectorize well...
-    for (int i = 0; i < N; i++) {
-        float s = v[i];
-        result[i] = (s <= p.fD) ? p.fC * s + p.fF
-                                : powf(s * p.fA + p.fB, p.fG) + p.fE;
-    }
-    // Clamp the output to [0, 1].
-    // Max(NaN, 0) = 0, but Max(0, NaN) = NaN, so we want this exact order to ensure NaN => 0
-    return SkNf::Min(SkNf::Max(SkNf::Load(result), 0.0f), 1.0f);
-}
-STAGE_CTX(parametric_r, const SkColorSpaceTransferFn*) { r = parametric(r, *ctx); }
-STAGE_CTX(parametric_g, const SkColorSpaceTransferFn*) { g = parametric(g, *ctx); }
-STAGE_CTX(parametric_b, const SkColorSpaceTransferFn*) { b = parametric(b, *ctx); }
-STAGE_CTX(parametric_a, const SkColorSpaceTransferFn*) { a = parametric(a, *ctx); }
-
-SI SkNf table(const SkNf& v, const SkTableTransferFn& table) {
-    float result[N];
-    for (int i = 0; i < N; i++) {
-        result[i] = interp_lut(v[i], table.fData, table.fSize);
-    }
-    // no need to clamp - tables are by-design [0,1] -> [0,1]
-    return SkNf::Load(result);
-}
-STAGE_CTX(table_r, const SkTableTransferFn*) { r = table(r, *ctx); }
-STAGE_CTX(table_g, const SkTableTransferFn*) { g = table(g, *ctx); }
-STAGE_CTX(table_b, const SkTableTransferFn*) { b = table(b, *ctx); }
-STAGE_CTX(table_a, const SkTableTransferFn*) { a = table(a, *ctx); }
-
-STAGE_CTX(color_lookup_table, const SkColorLookUpTable*) {
-    const SkColorLookUpTable* colorLUT = ctx;
-    SkASSERT(3 == colorLUT->inputChannels() || 4 == colorLUT->inputChannels());
-    SkASSERT(3 == colorLUT->outputChannels());
-    float result[3][N];
-    for (int i = 0; i < N; ++i) {
-        const float in[4] = { r[i], g[i], b[i], a[i] };
-        float out[3];
-        colorLUT->interp(out, in);
-        for (int j = 0; j < colorLUT->outputChannels(); ++j) {
-            result[j][i] = out[j];
-        }
-    }
-    r = SkNf::Load(result[0]);
-    g = SkNf::Load(result[1]);
-    b = SkNf::Load(result[2]);
-    if (4 == colorLUT->inputChannels()) {
-        // we must set the pixel to opaque, as the alpha channel was used
-        // as input before this.
-        a = 1.f;
-    }
-}
-
-STAGE(lab_to_xyz) {
-    const auto lab_l = r * 100.0f;
-    const auto lab_a = g * 255.0f - 128.0f;
-    const auto lab_b = b * 255.0f - 128.0f;
-    auto Y = (lab_l + 16.0f) * (1/116.0f);
-    auto X = lab_a * (1/500.0f) + Y;
-    auto Z = Y - (lab_b * (1/200.0f));
-
-    const auto X3 = X*X*X;
-    X = (X3 > 0.008856f).thenElse(X3, (X - (16/116.0f)) * (1/7.787f));
-    const auto Y3 = Y*Y*Y;
-    Y = (Y3 > 0.008856f).thenElse(Y3, (Y - (16/116.0f)) * (1/7.787f));
-    const auto Z3 = Z*Z*Z;
-    Z = (Z3 > 0.008856f).thenElse(Z3, (Z - (16/116.0f)) * (1/7.787f));
-
-    // adjust to D50 illuminant
-    X *= 0.96422f;
-    Y *= 1.00000f;
-    Z *= 0.82521f;
-
-    r = X;
-    g = Y;
-    b = Z;
-}
-
-SI SkNf assert_in_tile(const SkNf& v, float limit) {
-    for (int i = 0; i < N; i++) {
-        SkASSERT(0 <= v[i] && v[i] < limit);
-    }
-    return v;
-}
-
-SI SkNf ulp_before(float v) {
-    SkASSERT(v > 0);
-    SkNf vs(v);
-    SkNu uvs = SkNu::Load(&vs) - 1;
-    return SkNf::Load(&uvs);
-}
-
-SI SkNf clamp(const SkNf& v, float limit) {
-    SkNf result = SkNf::Max(0, SkNf::Min(v, ulp_before(limit)));
-    return assert_in_tile(result, limit);
-}
-SI SkNf repeat(const SkNf& v, float limit) {
-    SkNf result = v - (v/limit).floor()*limit;
-    // For small negative v, (v/limit).floor()*limit can dominate v in the subtraction,
-    // which leaves result == limit.  We want result < limit, so clamp it one ULP.
-    result = SkNf::Min(result, ulp_before(limit));
-    return assert_in_tile(result, limit);
-}
-SI SkNf mirror(const SkNf& v, float l/*imit*/) {
-    SkNf result = ((v - l) - ((v - l) / (2*l)).floor()*(2*l) - l).abs();
-    // Same deal as repeat.
-    result = SkNf::Min(result, ulp_before(l));
-    return assert_in_tile(result, l);
-}
-STAGE_CTX( clamp_x, const float*) { r = clamp (r, *ctx); }
-STAGE_CTX(repeat_x, const float*) { r = repeat(r, *ctx); }
-STAGE_CTX(mirror_x, const float*) { r = mirror(r, *ctx); }
-STAGE_CTX( clamp_y, const float*) { g = clamp (g, *ctx); }
-STAGE_CTX(repeat_y, const float*) { g = repeat(g, *ctx); }
-STAGE_CTX(mirror_y, const float*) { g = mirror(g, *ctx); }
-
-STAGE_CTX(save_xy, SkImageShaderContext*) {
-    r.store(ctx->x);
-    g.store(ctx->y);
-
-    // Whether bilinear or bicubic, all sample points have the same fractional offset (fx,fy).
-    // They're either the 4 corners of a logical 1x1 pixel or the 16 corners of a 3x3 grid
-    // surrounding (x,y), all (0.5,0.5) off-center.
-    auto fract = [](const SkNf& v) { return v - v.floor(); };
-    fract(r + 0.5f).store(ctx->fx);
-    fract(g + 0.5f).store(ctx->fy);
-}
-
-STAGE_CTX(accumulate, const SkImageShaderContext*) {
-    // Bilinear and bicubic filtering are both separable, so we'll end up with independent
-    // scale contributions in x and y that we multiply together to get each pixel's scale factor.
-    auto scale = SkNf::Load(ctx->scalex) * SkNf::Load(ctx->scaley);
-    dr = SkNf_fma(scale, r, dr);
-    dg = SkNf_fma(scale, g, dg);
-    db = SkNf_fma(scale, b, db);
-    da = SkNf_fma(scale, a, da);
-}
-
-// In bilinear interpolation, the 4 pixels at +/- 0.5 offsets from the sample pixel center
-// are combined in direct proportion to their area overlapping that logical query pixel.
-// At positive offsets, the x-axis contribution to that rectangular area is fx; (1-fx)
-// at negative x offsets.  The y-axis is treated symmetrically.
-template <int Scale>
-SI void bilinear_x(SkImageShaderContext* ctx, SkNf* x) {
-    *x = SkNf::Load(ctx->x) + Scale*0.5f;
-    auto fx = SkNf::Load(ctx->fx);
-    (Scale > 0 ? fx : (1.0f - fx)).store(ctx->scalex);
-}
-template <int Scale>
-SI void bilinear_y(SkImageShaderContext* ctx, SkNf* y) {
-    *y = SkNf::Load(ctx->y) + Scale*0.5f;
-    auto fy = SkNf::Load(ctx->fy);
-    (Scale > 0 ? fy : (1.0f - fy)).store(ctx->scaley);
-}
-STAGE_CTX(bilinear_nx, SkImageShaderContext*) { bilinear_x<-1>(ctx, &r); }
-STAGE_CTX(bilinear_px, SkImageShaderContext*) { bilinear_x<+1>(ctx, &r); }
-STAGE_CTX(bilinear_ny, SkImageShaderContext*) { bilinear_y<-1>(ctx, &g); }
-STAGE_CTX(bilinear_py, SkImageShaderContext*) { bilinear_y<+1>(ctx, &g); }
-
-
-// In bilinear interpolation, the 16 pixels at +/- 0.5 and +/- 1.5 offsets from the sample
-// pixel center are combined with a non-uniform cubic filter, with high filter values near
-// the center and lower values farther away.
-//
-// We break this filter function into two parts, one for near +/- 0.5 offsets,
-// and one for far +/- 1.5 offsets.
-//
-// See GrBicubicEffect for details about this particular Mitchell-Netravali filter.
-SI SkNf bicubic_near(const SkNf& t) {
-    // 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
-    return SkNf_fma(t, SkNf_fma(t, SkNf_fma(-21/18.0f, t, 27/18.0f), 9/18.0f), 1/18.0f);
-}
-SI SkNf bicubic_far(const SkNf& t) {
-    // 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
-    return (t*t)*SkNf_fma(7/18.0f, t, -6/18.0f);
-}
-
-template <int Scale>
-SI void bicubic_x(SkImageShaderContext* ctx, SkNf* x) {
-    *x = SkNf::Load(ctx->x) + Scale*0.5f;
-    auto fx = SkNf::Load(ctx->fx);
-    if (Scale == -3) { return bicubic_far (1.0f - fx).store(ctx->scalex); }
-    if (Scale == -1) { return bicubic_near(1.0f - fx).store(ctx->scalex); }
-    if (Scale == +1) { return bicubic_near(       fx).store(ctx->scalex); }
-    if (Scale == +3) { return bicubic_far (       fx).store(ctx->scalex); }
-    SkDEBUGFAIL("unreachable");
-}
-template <int Scale>
-SI void bicubic_y(SkImageShaderContext* ctx, SkNf* y) {
-    *y = SkNf::Load(ctx->y) + Scale*0.5f;
-    auto fy = SkNf::Load(ctx->fy);
-    if (Scale == -3) { return bicubic_far (1.0f - fy).store(ctx->scaley); }
-    if (Scale == -1) { return bicubic_near(1.0f - fy).store(ctx->scaley); }
-    if (Scale == +1) { return bicubic_near(       fy).store(ctx->scaley); }
-    if (Scale == +3) { return bicubic_far (       fy).store(ctx->scaley); }
-    SkDEBUGFAIL("unreachable");
-}
-STAGE_CTX(bicubic_n3x, SkImageShaderContext*) { bicubic_x<-3>(ctx, &r); }
-STAGE_CTX(bicubic_n1x, SkImageShaderContext*) { bicubic_x<-1>(ctx, &r); }
-STAGE_CTX(bicubic_p1x, SkImageShaderContext*) { bicubic_x<+1>(ctx, &r); }
-STAGE_CTX(bicubic_p3x, SkImageShaderContext*) { bicubic_x<+3>(ctx, &r); }
-
-STAGE_CTX(bicubic_n3y, SkImageShaderContext*) { bicubic_y<-3>(ctx, &g); }
-STAGE_CTX(bicubic_n1y, SkImageShaderContext*) { bicubic_y<-1>(ctx, &g); }
-STAGE_CTX(bicubic_p1y, SkImageShaderContext*) { bicubic_y<+1>(ctx, &g); }
-STAGE_CTX(bicubic_p3y, SkImageShaderContext*) { bicubic_y<+3>(ctx, &g); }
-
-
-template <typename T>
-SI SkNi offset_and_ptr(T** ptr, const SkImageShaderContext* ctx, const SkNf& x, const SkNf& y) {
-    SkNi ix = SkNx_cast<int>(x),
-         iy = SkNx_cast<int>(y);
-    SkNi offset = iy*ctx->stride + ix;
-
-    *ptr = (const T*)ctx->pixels;
-    return offset;
-}
-
-STAGE_CTX(gather_a8, const SkImageShaderContext*) {
-    const uint8_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    r = g = b = 0.0f;
-    a = SkNf_from_byte(gather(tail, p, offset));
-}
-STAGE_CTX(gather_i8, const SkImageShaderContext*) {
-    const uint8_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    SkNi ix = SkNx_cast<int>(gather(tail, p, offset));
-    from_8888(gather(tail, ctx->ctable->readColors(), ix), &r, &g, &b, &a);
-}
-STAGE_CTX(gather_g8, const SkImageShaderContext*) {
-    const uint8_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    r = g = b = SkNf_from_byte(gather(tail, p, offset));
-    a = 1.0f;
-}
-STAGE_CTX(gather_565, const SkImageShaderContext*) {
-    const uint16_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    from_565(gather(tail, p, offset), &r, &g, &b);
-    a = 1.0f;
-}
-STAGE_CTX(gather_4444, const SkImageShaderContext*) {
-    const uint16_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    from_4444(gather(tail, p, offset), &r, &g, &b, &a);
-}
-STAGE_CTX(gather_8888, const SkImageShaderContext*) {
-    const uint32_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    from_8888(gather(tail, p, offset), &r, &g, &b, &a);
-}
-STAGE_CTX(gather_f16, const SkImageShaderContext*) {
-    const uint64_t* p;
-    SkNi offset = offset_and_ptr(&p, ctx, r, g);
-
-    auto px = gather(tail, p, offset);
-    from_f16(&px, &r, &g, &b, &a);
-}
-
-STAGE_CTX(linear_gradient_2stops, const SkPM4f*) {
-    auto t = r;
-    SkPM4f c0 = ctx[0],
-           dc = ctx[1];
-
-    r = SkNf_fma(t, dc.r(), c0.r());
-    g = SkNf_fma(t, dc.g(), c0.g());
-    b = SkNf_fma(t, dc.b(), c0.b());
-    a = SkNf_fma(t, dc.a(), c0.a());
-}
-
-STAGE_CTX(byte_tables, const void*) {
-    struct Tables { const uint8_t *r, *g, *b, *a; };
-    auto tables = (const Tables*)ctx;
-
-    r = SkNf_from_byte(gather(tail, tables->r, SkNf_round(255.0f, r)));
-    g = SkNf_from_byte(gather(tail, tables->g, SkNf_round(255.0f, g)));
-    b = SkNf_from_byte(gather(tail, tables->b, SkNf_round(255.0f, b)));
-    a = SkNf_from_byte(gather(tail, tables->a, SkNf_round(255.0f, a)));
-}
-
-STAGE_CTX(byte_tables_rgb, const void*) {
-    struct Tables { const uint8_t *r, *g, *b; int n; };
-    auto tables = (const Tables*)ctx;
-
-    float scale = tables->n - 1;
-    r = SkNf_from_byte(gather(tail, tables->r, SkNf_round(scale, r)));
-    g = SkNf_from_byte(gather(tail, tables->g, SkNf_round(scale, g)));
-    b = SkNf_from_byte(gather(tail, tables->b, SkNf_round(scale, b)));
-}
-
-STAGE_CTX(shader_adapter, SkShader::Context*) {
-    SkPM4f buf[N];
-    static_assert(sizeof(buf) == sizeof(r) + sizeof(g) + sizeof(b) + sizeof(a), "");
-    ctx->shadeSpan4f(x, (int)g[0], buf, N);
-    SkNf::Load4(buf, &r, &g, &b, &a);
-}
-
-SI Fn enum_to_Fn(SkRasterPipeline::StockStage st) {
-    switch (st) {
-    #define M(stage) case SkRasterPipeline::stage: return stage;
-        SK_RASTER_PIPELINE_STAGES(M)
-    #undef M
-    }
-    SkASSERT(false);
-    return just_return;
-}
-
-namespace {
-
-    static void build_program(void** program, const SkRasterPipeline::Stage* stages, int nstages) {
-        for (int i = 0; i < nstages; i++) {
-            *program++ = (void*)enum_to_Fn(stages[i].stage);
-            if (stages[i].ctx) {
-                *program++ = stages[i].ctx;
-            }
-        }
-        *program++ = (void*)just_return;
-    }
-
-    static void run_program(void** program, size_t x, size_t n) {
-        SkNf u;  // fastest to start uninitialized.
-
-        auto start = (Fn)load_and_increment(&program);
-        while (n >= N) {
-            start(x*N, program, u,u,u,u, u,u,u,u);
-            x += N;
-            n -= N;
-        }
-        if (n) {
-            start(x*N+n, program, u,u,u,u, u,u,u,u);
-        }
-    }
-
-    // Compiled manages its memory manually because it's not safe to use
-    // std::vector, SkTDArray, etc without setting us up for big ODR violations.
-    struct Compiled {
-        Compiled(const SkRasterPipeline::Stage* stages, int nstages) {
-            int slots = nstages + 1;  // One extra for just_return.
-            for (int i = 0; i < nstages; i++) {
-                if (stages[i].ctx) {
-                    slots++;
-                }
-            }
-            fProgram = (void**)sk_malloc_throw(slots * sizeof(void*));
-            build_program(fProgram, stages, nstages);
-        }
-        ~Compiled() { sk_free(fProgram); }
-
-        Compiled(const Compiled& o) {
-            int slots = 0;
-            while (o.fProgram[slots++] != (void*)just_return);
-
-            fProgram = (void**)sk_malloc_throw(slots * sizeof(void*));
-            memcpy(fProgram, o.fProgram, slots * sizeof(void*));
-        }
-
-        void operator()(size_t x, size_t n) {
-            run_program(fProgram, x, n);
-        }
-
-        void** fProgram;
-    };
-}
-
-namespace SK_OPTS_NS {
-
-    SI void run_pipeline(size_t x, size_t n,
-                         const SkRasterPipeline::Stage* stages, int nstages) {
-        static const int kStackMax = 256;
-        // Worst case is nstages stages with nstages context pointers, and just_return.
-        if (2*nstages+1 <= kStackMax) {
-            void* program[kStackMax];
-            build_program(program, stages, nstages);
-            run_program(program, x,n);
-        } else {
-            Compiled{stages,nstages}(x,n);
-        }
-    }
-
-}  // namespace SK_OPTS_NS
-
-#undef SI
-#undef STAGE
-#undef STAGE_CTX
-#undef RGBA_XFERMODE
-#undef RGB_XFERMODE
-
-#endif//SkRasterPipeline_opts_DEFINED
diff --git a/src/opts/SkUtils_opts.h b/src/opts/SkUtils_opts.h
new file mode 100644
index 0000000..c9390af
--- /dev/null
+++ b/src/opts/SkUtils_opts.h
@@ -0,0 +1,31 @@
+/*
+ * 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 SkUtils_opts_DEFINED
+#define SkUtils_opts_DEFINED
+
+namespace SK_OPTS_NS {
+
+    static void memset16(uint16_t buffer[], uint16_t value, int count) {
+        for (int i = 0; i < count; i++) {
+            buffer[i] = value;
+        }
+    }
+    static void memset32(uint32_t buffer[], uint32_t value, int count) {
+        for (int i = 0; i < count; i++) {
+            buffer[i] = value;
+        }
+    }
+    static void memset64(uint64_t buffer[], uint64_t value, int count) {
+        for (int i = 0; i < count; i++) {
+            buffer[i] = value;
+        }
+    }
+
+}
+
+#endif//SkUtils_opts_DEFINED
diff --git a/src/opts/SkXfermode_opts.h b/src/opts/SkXfermode_opts.h
index 81e5bb9..e7db073 100644
--- a/src/opts/SkXfermode_opts.h
+++ b/src/opts/SkXfermode_opts.h
@@ -229,39 +229,6 @@
         }
     }
 
-    void xfer16(uint16_t dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override {
-        mark_dst_initialized_if_safe<Xfermode>(dst, dst+n);
-        SkPMColor dst32[4];
-        while (n >= 4) {
-            dst32[0] = SkPixel16ToPixel32(dst[0]);
-            dst32[1] = SkPixel16ToPixel32(dst[1]);
-            dst32[2] = SkPixel16ToPixel32(dst[2]);
-            dst32[3] = SkPixel16ToPixel32(dst[3]);
-
-            this->xfer32(dst32, src, 4, aa);
-
-            dst[0] = SkPixel32ToPixel16(dst32[0]);
-            dst[1] = SkPixel32ToPixel16(dst32[1]);
-            dst[2] = SkPixel32ToPixel16(dst32[2]);
-            dst[3] = SkPixel32ToPixel16(dst32[3]);
-
-            dst += 4;
-            src += 4;
-            aa  += aa ? 4 : 0;
-            n -= 4;
-        }
-        while (n) {
-            SkPMColor dst32 = SkPixel16ToPixel32(*dst);
-            this->xfer32(&dst32, src, 1, aa);
-            *dst = SkPixel32ToPixel16(dst32);
-
-            dst += 1;
-            src += 1;
-            aa  += aa ? 1 : 0;
-            n   -= 1;
-        }
-    }
-
 private:
     typedef SkProcCoeffXfermode INHERITED;
 };
@@ -278,14 +245,6 @@
         }
     }
 
-    void xfer16(uint16_t dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override {
-        for (int i = 0; i < n; i++) {
-            SkPMColor dst32 = SkPixel16ToPixel32(dst[i]);
-            dst32 = Xfer32_1(dst32, src[i], aa ? aa+i : nullptr);
-            dst[i] = SkPixel32ToPixel16(dst32);
-        }
-    }
-
 private:
     static SkPMColor Xfer32_1(SkPMColor dst, const SkPMColor src, const SkAlpha* aa) {
         Sk4f d = Load(dst),
diff --git a/src/opts/opts_check_x86.cpp b/src/opts/opts_check_x86.cpp
index 4b8c8a1..7917259 100644
--- a/src/opts/opts_check_x86.cpp
+++ b/src/opts/opts_check_x86.cpp
@@ -78,43 +78,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-static const SkBlitRow::Proc16 platform_16_procs[] = {
-    S32_D565_Opaque_SSE2,               // S32_D565_Opaque
-    nullptr,                               // S32_D565_Blend
-    S32A_D565_Opaque_SSE2,              // S32A_D565_Opaque
-    nullptr,                               // S32A_D565_Blend
-    S32_D565_Opaque_Dither_SSE2,        // S32_D565_Opaque_Dither
-    nullptr,                               // S32_D565_Blend_Dither
-    S32A_D565_Opaque_Dither_SSE2,       // S32A_D565_Opaque_Dither
-    nullptr,                               // S32A_D565_Blend_Dither
-};
-
-SkBlitRow::Proc16 SkBlitRow::PlatformFactory565(unsigned flags) {
-    if (SkCpu::Supports(SkCpu::SSE2)) {
-        return platform_16_procs[flags];
-    } else {
-        return nullptr;
-    }
-}
-
-static const SkBlitRow::ColorProc16 platform_565_colorprocs_SSE2[] = {
-    Color32A_D565_SSE2,                 // Color32A_D565,
-    nullptr,                               // Color32A_D565_Dither
-};
-
-SkBlitRow::ColorProc16 SkBlitRow::PlatformColorFactory565(unsigned flags) {
-/* If you're thinking about writing an SSE4 version of this, do check it's
- * actually faster on Atom. Our original SSE4 version was slower than this
- * SSE2 version on Silvermont, and only marginally faster on a Core i7,
- * mainly due to the MULLD timings.
- */
-    if (SkCpu::Supports(SkCpu::SSE2)) {
-        return platform_565_colorprocs_SSE2[flags];
-    } else {
-        return nullptr;
-    }
-}
-
 static const SkBlitRow::Proc32 platform_32_procs_SSE2[] = {
     nullptr,                               // S32_Opaque,
     S32_Blend_BlitRow32_SSE2,           // S32_Blend,
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index c4eb0a9..a7aa92f 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -13,8 +13,7 @@
 #include "SkPathOpsCommon.h"
 
 static bool one_contour(const SkPath& path) {
-    char storage[256];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<256> allocator;
     int verbCount = path.countVerbs();
     uint8_t* verbs = (uint8_t*) allocator.makeArrayDefault<uint8_t>(verbCount);
     (void) path.getVerbs(verbs, verbCount);
@@ -51,8 +50,7 @@
         path->setFillType(fillType);
         return true;
     }
-    char storage[4096];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<4096> allocator;
     SkOpContourHead contourHead;
     SkOpGlobalState globalState(&contourHead, &allocator  SkDEBUGPARAMS(false)
             SkDEBUGPARAMS(nullptr));
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index 93a6d66..2f9d28f 100644
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -266,8 +266,7 @@
     SkOPASSERT(!oppPtTEnd->deleted());
     DebugCheckAdd(fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
     DebugCheckAdd(fTop, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
-    SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(
-            this->globalState()->allocator());
+    SkCoincidentSpans* coinRec = this->globalState()->allocator()->make<SkCoincidentSpans>();
     coinRec->init(SkDEBUGCODE(fGlobalState));
     coinRec->set(this->fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
     fHead = coinRec;
@@ -611,7 +610,7 @@
 /* Please keep this in sync with debugAddIfMissing() */
 // note that over1s, over1e, over2s, over2e are ordered
 bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over2s,
-        double tStart, double tEnd, SkOpSegment* coinSeg, SkOpSegment* oppSeg, bool* added  
+        double tStart, double tEnd, SkOpSegment* coinSeg, SkOpSegment* oppSeg, bool* added
         SkDEBUGPARAMS(const SkOpPtT* over1e) SkDEBUGPARAMS(const SkOpPtT* over2e)) {
     SkASSERT(tStart < tEnd);
     SkASSERT(over1s->fT < over1e->fT);
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index 92076b1..37498c2 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -7,8 +7,8 @@
 #ifndef SkOpCoincidence_DEFINED
 #define SkOpCoincidence_DEFINED
 
+#include "SkArenaAlloc.h"
 #include "SkTDArray.h"
-#include "SkOpTAllocator.h"
 #include "SkOpSpan.h"
 #include "SkPathOpsTypes.h"
 
@@ -60,7 +60,7 @@
     SkDEBUGCODE(SkOpGlobalState* globalState() { return fGlobalState; })
 
     void init(SkDEBUGCODE(SkOpGlobalState* globalState)) {
-        sk_bzero(this, sizeof(*this)); 
+        sk_bzero(this, sizeof(*this));
         SkDEBUGCODE(fGlobalState = globalState);
     }
 
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index ea1659e..f579947 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -5,7 +5,6 @@
 * found in the LICENSE file.
 */
 #include "SkOpContour.h"
-#include "SkOpTAllocator.h"
 #include "SkPathWriter.h"
 #include "SkReduceOrder.h"
 #include "SkTSort.h"
@@ -65,17 +64,17 @@
     SkArenaAlloc* allocator = fContour->globalState()->allocator();
     switch (verb) {
         case SkPath::kQuad_Verb: {
-            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
+            SkPoint* ptStorage = allocator->makeArrayDefault<SkPoint>(3);
             memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
             this->addQuad(ptStorage);
         } break;
         case SkPath::kConic_Verb: {
-            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
+            SkPoint* ptStorage = allocator->makeArrayDefault<SkPoint>(3);
             memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
             this->addConic(ptStorage, weight);
         } break;
         case SkPath::kCubic_Verb: {
-            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
+            SkPoint* ptStorage = allocator->makeArrayDefault<SkPoint>(4);
             memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
             this->addCubic(ptStorage);
         } break;
@@ -107,7 +106,7 @@
     if (!fLastIsLine)
         return;
     SkArenaAlloc* allocator = fContour->globalState()->allocator();
-    SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
+    SkPoint* ptStorage = allocator->makeArrayDefault<SkPoint>(2);
     memcpy(ptStorage, fLastLine, sizeof(fLastLine));
     (void) fContour->addLine(ptStorage);
     fLastIsLine = false;
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index c283226..17effc6 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -45,8 +45,8 @@
     }
 
     SkOpSegment& appendSegment() {
-        SkOpSegment* result = fCount++
-            ? SkOpTAllocator<SkOpSegment>::Allocate(this->globalState()->allocator()) : &fHead;
+        SkOpSegment* result = fCount++ ? this->globalState()->allocator()->make<SkOpSegment>()
+                                       : &fHead;
         result->setPrev(fTail);
         if (fTail) {
             fTail->setNext(result);
@@ -391,7 +391,7 @@
 class SkOpContourHead : public SkOpContour {
 public:
     SkOpContour* appendContour() {
-        SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
+        SkOpContour* contour = this->globalState()->allocator()->make<SkOpContour>();
         contour->setNext(nullptr);
         SkOpContour* prev = this;
         SkOpContour* next;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 1dbeaf6..f266ed9 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -286,8 +286,7 @@
     SkOpSpanBase* spanBase = fHead.next();
     while (spanBase != &fTail) {
         if (activePrior) {
-            SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(
-                    this->globalState()->allocator());
+            SkOpAngle* priorAngle = this->globalState()->allocator()->make<SkOpAngle>();
             priorAngle->set(spanBase, prior);
             spanBase->setFromAngle(priorAngle);
         }
@@ -295,8 +294,7 @@
         bool active = !span->isCanceled();
         SkOpSpanBase* next = span->next();
         if (active) {
-            SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(
-                    this->globalState()->allocator());
+            SkOpAngle* angle = this->globalState()->allocator()->make<SkOpAngle>();
             angle->set(span, next);
             span->setToAngle(angle);
         }
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index 4bde816..ea2504a 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -7,9 +7,9 @@
 #ifndef SkOpSegment_DEFINE
 #define SkOpSegment_DEFINE
 
+#include "SkArenaAlloc.h"
 #include "SkOpAngle.h"
 #include "SkOpSpan.h"
-#include "SkOpTAllocator.h"
 #include "SkPathOpsBounds.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsCurve.h"
@@ -60,7 +60,7 @@
     bool addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const;
 
     SkOpAngle* addEndSpan() {
-        SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
+        SkOpAngle* angle = this->globalState()->allocator()->make<SkOpAngle>();
         angle->set(&fTail, fTail.prev());
         fTail.setFromAngle(angle);
         return angle;
@@ -78,7 +78,7 @@
     SkOpPtT* addMissing(double t, SkOpSegment* opp, bool* allExist);
 
     SkOpAngle* addStartSpan() {
-        SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
+        SkOpAngle* angle = this->globalState()->allocator()->make<SkOpAngle>();
         angle->set(&fHead, fHead.next());
         fHead.setToAngle(angle);
         return angle;
@@ -95,10 +95,6 @@
     SkOpPtT* addT(double t);
     SkOpPtT* addT(double t, const SkPoint& pt);
 
-    template<typename T> T* allocateArray(int count) {
-        return SkOpTAllocator<T>::AllocateArray(this->globalState()->allocator(), count);
-    }
-
     const SkPathOpsBounds& bounds() const {
         return fBounds;
     }
@@ -237,7 +233,7 @@
     SkOpSpan* insert(SkOpSpan* prev) {
         SkOpGlobalState* globalState = this->globalState();
         globalState->setAllocatedOpSpan();
-        SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(globalState->allocator());
+        SkOpSpan* result = globalState->allocator()->make<SkOpSpan>();
         SkOpSpanBase* next = prev->next();
         result->setPrev(prev);
         prev->setNext(result);
diff --git a/src/pathops/SkOpTAllocator.h b/src/pathops/SkOpTAllocator.h
deleted file mode 100644
index 599c445..0000000
--- a/src/pathops/SkOpTAllocator.h
+++ /dev/null
@@ -1,29 +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 SkOpTAllocator_DEFINED
-#define SkOpTAllocator_DEFINED
-
-#include "SkArenaAlloc.h"
-
-// T is SkOpAngle2, SkOpSpan2, or SkOpSegment2
-template<typename T>
-class SkOpTAllocator {
-public:
-    static T* Allocate(SkArenaAlloc* allocator) {
-        return allocator->make<T>();
-    }
-
-    static T* AllocateArray(SkArenaAlloc* allocator, int count) {
-        return allocator->makeArrayDefault<T>(count);
-    }
-
-    static T* New(SkArenaAlloc* allocator) {
-        return allocator->make<T>();
-    }
-};
-
-#endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index d842e2c..794e54f 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -248,29 +248,29 @@
     if (cubic.monotonicInX() && cubic.monotonicInY()) {
         return 0;
     }
-    SkScalar d[3];
+    SkScalar d[4];
     SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
     switch (cubicType) {
-        case kLoop_SkCubicType: {
+        case SkCubicType::kLoop: {
             // crib code from gpu path utils that finds t values where loop self-intersects
             // use it to find mid of t values which should be a friendly place to chop
-            SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
-            SkScalar ls = d[1] - tempSqrt;
-            SkScalar lt = 2.f * d[0];
-            SkScalar ms = d[1] + tempSqrt;
-            SkScalar mt = 2.f * d[0];
-            if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) {
-                ls = ls / lt;
-                ms = ms / mt;
-                SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1));
-                t[0] = (ls + ms) / 2;
+            SkASSERT(d[0] < 0);
+            const SkScalar q = d[2] + SkScalarCopySign(SkScalarSqrt(-d[0]), d[2]);
+            const SkScalar td = q;
+            const SkScalar sd = 2 * d[1];
+            const SkScalar te = 2 * (d[2] * d[2] - d[3] * d[1]);
+            const SkScalar se = d[1] * q;
+            if (roughly_between(0, td, sd) && roughly_between(0, te, se)) {
+                SkASSERT(roughly_between(0, td / sd, 1) && roughly_between(0, te / se, 1));
+                t[0] = (td * se + te * sd) / (2 * sd * se);
                 SkASSERT(roughly_between(0, *t, 1));
                 return (int) (t[0] > 0 && t[0] < 1);
             }
         }
         // fall through if no t value found
-        case kSerpentine_SkCubicType:
-        case kCusp_SkCubicType: {
+        case SkCubicType::kSerpentine:
+        case SkCubicType::kLocalCusp:
+        case SkCubicType::kInfiniteCusp: {
             double inflectionTs[2];
             int infTCount = cubic.findInflections(inflectionTs);
             double maxCurvature[3];
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index fd08aaa..5449510 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -217,8 +217,7 @@
 
 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
         SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
-    char storage[4096];
-    SkArenaAlloc allocator(storage);  // FIXME: add a constant expression here, tune
+    SkSTArenaAlloc<4096> allocator;  // FIXME: add a constant expression here, tune
     SkOpContour contour;
     SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
     SkOpGlobalState globalState(contourList, &allocator
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index e671ab8..d9d0879 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -146,8 +146,7 @@
         return true;
     }
     // turn path into list of segments
-    char storage[4096];
-    SkArenaAlloc allocator(storage);  // FIXME: constant-ize, tune
+    SkSTArenaAlloc<4096> allocator;  // FIXME: constant-ize, tune
     SkOpContour contour;
     SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
     SkOpGlobalState globalState(contourList, &allocator
diff --git a/src/pathops/SkPathOpsTightBounds.cpp b/src/pathops/SkPathOpsTightBounds.cpp
index 4236d2d..8b62a6a 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -47,8 +47,7 @@
         *result = path.getBounds();
         return true;
     }
-    char storage[4096];
-    SkArenaAlloc allocator(storage);  // FIXME: constant-ize, tune
+    SkSTArenaAlloc<4096> allocator;  // FIXME: constant-ize, tune
     SkOpContour contour;
     SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
     SkOpGlobalState globalState(contourList, &allocator  SkDEBUGPARAMS(false)
diff --git a/src/pathops/SkPathOpsWinding.cpp b/src/pathops/SkPathOpsWinding.cpp
index 724e6f4..4c0f043 100644
--- a/src/pathops/SkPathOpsWinding.cpp
+++ b/src/pathops/SkPathOpsWinding.cpp
@@ -174,7 +174,7 @@
         } else if (!span->windValue() && !span->oppValue()) {
             continue;
         }
-        SkOpRayHit* newHit = SkOpTAllocator<SkOpRayHit>::Allocate(allocator);
+        SkOpRayHit* newHit = allocator->make<SkOpRayHit>();
         newHit->fNext = *hits;
         newHit->fPt = pt;
         newHit->fSlope = slope;
@@ -233,8 +233,7 @@
 }
 
 bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
-    char storage[1024];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<1024> allocator;
     int dirOffset;
     double t = get_t_guess(fTopTTry++, &dirOffset);
     SkOpRayHit hitBase;
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index 511fca8..fa09be3 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -23,8 +23,6 @@
         if (dst->colorType() != kIndex_8_SkColorType) {
             return;
         }
-        // We must check to see if the bitmap has a color table.
-        SkAutoLockPixels autoLockPixels(*dst);
         if (!dst->getColorTable()) {
             // We can't use an indexed bitmap with no colortable.
             dst->reset();
@@ -95,6 +93,7 @@
 
 // unpremultiply and extract R, G, B components.
 static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
+    SkPMColorAssert(color);
     uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
     rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
     rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
@@ -125,6 +124,7 @@
         uint32_t* scanline = bm.getAddr32(0, y);
         for (int x = xmin; x <= xmax; ++x) {
             uint32_t color = scanline[x];
+            SkPMColorAssert(color);
             a += SkGetA32Component(color, ct);
             r += SkGetR32Component(color, ct);
             g += SkGetG32Component(color, ct);
@@ -149,7 +149,8 @@
         return input;
     }
     // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
-    SkAssertResult(input.copyTo(copy, kN32_SkColorType));
+    copy->allocPixels(input.info().makeColorType(kN32_SkColorType));
+    SkAssertResult(input.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0));
     copy->setImmutable();
     return *copy;
 }
@@ -181,7 +182,6 @@
     }
     SkBitmap copy;
     const SkBitmap& bm = not4444(bitmap, &copy);
-    SkAutoLockPixels autoLockPixels(bm);
     SkColorType colorType = bm.colorType();
     SkAlphaType alphaType = bm.alphaType();
     switch (colorType) {
@@ -257,7 +257,6 @@
     }
     SkBitmap copy;
     const SkBitmap& bm = not4444(bitmap, &copy);
-    SkAutoLockPixels autoLockPixels(bm);
     SkColorType colorType = bm.colorType();
     switch (colorType) {
         case kRGBA_8888_SkColorType:
@@ -350,7 +349,6 @@
                                const SkPDFObjNumMap& objNumMap) {
     SkBitmap bitmap;
     image_get_ro_pixels(image, &bitmap);      // TODO(halcanary): test
-    SkAutoLockPixels autoLockPixels(bitmap);  // with malformed images.
 
     // Write to a temporary buffer to get the compressed length.
     SkDynamicMemoryWStream buffer;
@@ -360,8 +358,7 @@
     } else {
         bitmap_to_pdf_pixels(bitmap, &deflateWStream);
     }
-    deflateWStream.finalize();  // call before detachAsStream().
-    std::unique_ptr<SkStreamAsset> asset(buffer.detachAsStream());
+    deflateWStream.finalize();  // call before buffer.bytesWritten().
 
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
@@ -384,11 +381,11 @@
     }
     pdfDict.insertInt("BitsPerComponent", 8);
     pdfDict.insertName("Filter", "FlateDecode");
-    pdfDict.insertInt("Length", asset->getLength());
+    pdfDict.insertInt("Length", buffer.bytesWritten());
     pdfDict.emitObject(stream, objNumMap);
 
     pdf_stream_begin(stream);
-    stream->writeStream(asset.get(), asset->getLength());
+    buffer.writeToAndReset(stream);
     pdf_stream_end(stream);
 }
 
@@ -502,11 +499,10 @@
 
     if (pixelSerializer) {
         SkBitmap bm;
-        SkAutoPixmapUnlock apu;
+        SkPixmap pmap;
         SkColorSpace* legacyColorSpace = nullptr;
-        if (as_IB(image.get())->getROPixels(&bm, legacyColorSpace) &&
-            bm.requestLock(&apu)) {
-            data.reset(pixelSerializer->encode(apu.pixmap()));
+        if (as_IB(image.get())->getROPixels(&bm, legacyColorSpace) && bm.peekPixels(&pmap)) {
+            data.reset(pixelSerializer->encode(pmap));
             if (data && SkIsJFIF(data.get(), &info)) {
                 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
                 if (info.fSize == image->dimensions()) {  // Sanity check.
diff --git a/src/pdf/SkPDFCanon.cpp b/src/pdf/SkPDFCanon.cpp
index a804d6b..5e8a2cb 100644
--- a/src/pdf/SkPDFCanon.cpp
+++ b/src/pdf/SkPDFCanon.cpp
@@ -12,25 +12,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-namespace {
-template <typename K, typename V> struct UnrefValue {
-    void operator()(K, V** v) { SkSafeUnref(*v); }
-};
-}
-
 SkPDFCanon::~SkPDFCanon() {
-    // TODO(halcanary): make SkTHashSet work nicely with sk_sp<>,
-    // or use std::unordered_set<>
     fGraphicStateRecords.foreach ([](WrapGS w) { w.fPtr->unref(); });
-    fPDFBitmapMap.foreach(UnrefValue<SkBitmapKey, SkPDFObject>());
-    fTypefaceMetrics.foreach(UnrefValue<uint32_t, SkAdvancedTypefaceMetrics>());
-    fFontDescriptors.foreach(UnrefValue<uint32_t, SkPDFDict>());
-    fFontMap.foreach(UnrefValue<uint64_t, SkPDFFont>());
-}
-
-void SkPDFCanon::reset() {
-    this->~SkPDFCanon();
-    new (this)SkPDFCanon;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -52,7 +35,7 @@
 }
 void SkPDFCanon::addFunctionShader(sk_sp<SkPDFObject> pdfShader,
                                    SkPDFShader::State state) {
-    fFunctionShaderRecords.emplace_back(std::move(state), std::move(pdfShader));
+    fFunctionShaderRecords.emplace_back(ShaderRec{std::move(state), std::move(pdfShader)});
 }
 
 sk_sp<SkPDFObject> SkPDFCanon::findAlphaShader(
@@ -61,7 +44,7 @@
 }
 void SkPDFCanon::addAlphaShader(sk_sp<SkPDFObject> pdfShader,
                                 SkPDFShader::State state) {
-    fAlphaShaderRecords.emplace_back(std::move(state), std::move(pdfShader));
+    fAlphaShaderRecords.emplace_back(ShaderRec{std::move(state), std::move(pdfShader)});
 }
 
 sk_sp<SkPDFObject> SkPDFCanon::findImageShader(
@@ -71,7 +54,7 @@
 
 void SkPDFCanon::addImageShader(sk_sp<SkPDFObject> pdfShader,
                                 SkPDFShader::State state) {
-    fImageShaderRecords.emplace_back(std::move(state), std::move(pdfShader));
+    fImageShaderRecords.emplace_back(ShaderRec{std::move(state), std::move(pdfShader)});
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -89,37 +72,3 @@
     fGraphicStateRecords.add(w);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkPDFObject> SkPDFCanon::findPDFBitmap(SkBitmapKey key) const {
-    SkPDFObject** ptr = fPDFBitmapMap.find(key);
-    return ptr ? sk_ref_sp(*ptr) : sk_sp<SkPDFObject>();
-}
-
-void SkPDFCanon::addPDFBitmap(SkBitmapKey key, sk_sp<SkPDFObject> pdfBitmap) {
-    fPDFBitmapMap.set(key, pdfBitmap.release());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkPDFStream> SkPDFCanon::makeInvertFunction() {
-    if (fInvertFunction) {
-        return fInvertFunction;
-    }
-    fInvertFunction = SkPDFGraphicState::MakeInvertFunction();
-    return fInvertFunction;
-}
-sk_sp<SkPDFDict> SkPDFCanon::makeNoSmaskGraphicState() {
-    if (fNoSmaskGraphicState) {
-        return fNoSmaskGraphicState;
-    }
-    fNoSmaskGraphicState = SkPDFGraphicState::MakeNoSmaskGraphicState();
-    return fNoSmaskGraphicState;
-}
-sk_sp<SkPDFArray> SkPDFCanon::makeRangeObject() {
-    if (fRangeObject) {
-        return fRangeObject;
-    }
-    fRangeObject = SkPDFShader::MakeRangeObject();
-    return fRangeObject;
-}
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index 2da9e52..0aac2b5 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -14,8 +14,8 @@
 #include "SkTHash.h"
 #include "SkBitmapKey.h"
 
-class SkAdvancedTypefaceMetrics;
 class SkPDFFont;
+struct SkAdvancedTypefaceMetrics;
 
 /**
  *  The SkPDFCanon canonicalizes objects across PDF pages
@@ -38,9 +38,6 @@
 public:
     ~SkPDFCanon();
 
-    // reset to original setting, unrefs all objects.
-    void reset();
-
     sk_sp<SkPDFObject> findFunctionShader(const SkPDFShader::State&) const;
     void addFunctionShader(sk_sp<SkPDFObject>, SkPDFShader::State);
 
@@ -53,28 +50,21 @@
     const SkPDFGraphicState* findGraphicState(const SkPDFGraphicState&) const;
     void addGraphicState(const SkPDFGraphicState*);
 
-    sk_sp<SkPDFObject> findPDFBitmap(SkBitmapKey key) const;
-    void addPDFBitmap(SkBitmapKey key, sk_sp<SkPDFObject>);
+    SkTHashMap<SkBitmapKey, sk_sp<SkPDFObject>> fPDFBitmapMap;
 
-    SkTHashMap<uint32_t, SkAdvancedTypefaceMetrics*> fTypefaceMetrics;
-    SkTHashMap<uint32_t, SkPDFDict*> fFontDescriptors;
-    SkTHashMap<uint64_t, SkPDFFont*> fFontMap;
+    SkTHashMap<uint32_t, std::unique_ptr<SkAdvancedTypefaceMetrics>> fTypefaceMetrics;
+    SkTHashMap<uint32_t, sk_sp<SkPDFDict>> fFontDescriptors;
+    SkTHashMap<uint64_t, sk_sp<SkPDFFont>> fFontMap;
 
-    SkPixelSerializer* getPixelSerializer() const { return fPixelSerializer.get(); }
-    void setPixelSerializer(sk_sp<SkPixelSerializer> ps) {
-        fPixelSerializer = std::move(ps);
-    }
-
-    sk_sp<SkPDFStream> makeInvertFunction();
-    sk_sp<SkPDFDict> makeNoSmaskGraphicState();
-    sk_sp<SkPDFArray> makeRangeObject();
+    sk_sp<SkPixelSerializer> fPixelSerializer;
+    sk_sp<SkPDFStream> fInvertFunction;
+    sk_sp<SkPDFDict> fNoSmaskGraphicState;
+    sk_sp<SkPDFArray> fRangeObject;
 
 private:
     struct ShaderRec {
         SkPDFShader::State fShaderState;
         sk_sp<SkPDFObject> fShaderObject;
-        ShaderRec(SkPDFShader::State s, sk_sp<SkPDFObject> o)
-            : fShaderState(std::move(s)), fShaderObject(std::move(o)) {}
     };
     SkTArray<ShaderRec> fFunctionShaderRecords;
     SkTArray<ShaderRec> fAlphaShaderRecords;
@@ -96,13 +86,5 @@
         };
     };
     SkTHashSet<WrapGS, WrapGS::Hash> fGraphicStateRecords;
-
-    // TODO(halcanary): make SkTHashMap<K, sk_sp<V>> work correctly.
-    SkTHashMap<SkBitmapKey, SkPDFObject*> fPDFBitmapMap;
-
-    sk_sp<SkPixelSerializer> fPixelSerializer;
-    sk_sp<SkPDFStream> fInvertFunction;
-    sk_sp<SkPDFDict> fNoSmaskGraphicState;
-    sk_sp<SkPDFArray> fRangeObject;
 };
 #endif  // SkPDFCanon_DEFINED
diff --git a/src/pdf/SkPDFCanvas.cpp b/src/pdf/SkPDFCanvas.cpp
index d946f48..c5cde3d 100644
--- a/src/pdf/SkPDFCanvas.cpp
+++ b/src/pdf/SkPDFCanvas.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkImage.h"
 #include "SkLatticeIter.h"
 #include "SkPDFCanvas.h"
 #include "SkPDFDevice.h"
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 10b4241..f4cdd86 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -11,18 +11,19 @@
 #include "SkAnnotationKeys.h"
 #include "SkBitmapDevice.h"
 #include "SkBitmapKey.h"
+#include "SkClipOpPriv.h"
 #include "SkColor.h"
 #include "SkColorFilter.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
 #include "SkGlyphCache.h"
 #include "SkImageFilterCache.h"
+#include "SkJpegEncoder.h"
 #include "SkMakeUnique.h"
-#include "SkPath.h"
-#include "SkPathEffect.h"
-#include "SkPathOps.h"
+#include "SkMaskFilter.h"
 #include "SkPDFBitmap.h"
 #include "SkPDFCanon.h"
+#include "SkPDFCanvas.h"
 #include "SkPDFDocument.h"
 #include "SkPDFFont.h"
 #include "SkPDFFormXObject.h"
@@ -31,9 +32,12 @@
 #include "SkPDFShader.h"
 #include "SkPDFTypes.h"
 #include "SkPDFUtils.h"
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkPathOps.h"
 #include "SkPixelRef.h"
-#include "SkRasterClip.h"
 #include "SkRRect.h"
+#include "SkRasterClip.h"
 #include "SkScopeExit.h"
 #include "SkString.h"
 #include "SkSurface.h"
@@ -42,7 +46,17 @@
 #include "SkTextFormatParams.h"
 #include "SkUtils.h"
 #include "SkXfermodeInterpretation.h"
-#include "SkClipOpPriv.h"
+
+#ifndef SK_PDF_MASK_QUALITY
+    // If MASK_QUALITY is in [0,100], will be used for JpegEncoder.
+    // Otherwise, just encode masks losslessly.
+    #define SK_PDF_MASK_QUALITY 50
+    // Since these masks are used for blurry shadows, we shouldn't need
+    // high quality.  Raise this value if your shadows have visible JPEG
+    // artifacts.
+    // If SkJpegEncoder::Encode fails, we will fall back to the lossless
+    // encoding.
+#endif
 
 #define DPI_FOR_RASTER_SCALE_ONE 72
 
@@ -73,6 +87,23 @@
     }
 }
 
+// A shader's matrix is:  CTMM x LocalMatrix x WrappingLocalMatrix.  We want to
+// switch to device space, where CTM = I, while keeping the original behavior.
+//
+//               I * LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
+//                   LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
+//  InvLocalMatrix * LocalMatrix * NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
+//                                 NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
+//
+static void transform_shader(SkPaint* paint, const SkMatrix& ctm) {
+    SkMatrix lm = SkPDFUtils::GetShaderLocalMatrix(paint->getShader());
+    SkMatrix lmInv;
+    if (lm.invert(&lmInv)) {
+        SkMatrix m = SkMatrix::Concat(SkMatrix::Concat(lmInv, ctm), lm);
+        paint->setShader(paint->getShader()->makeWithLocalMatrix(m));
+    }
+}
+
 static void emit_pdf_color(SkColor color, SkWStream* result) {
     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
     SkPDFUtils::AppendColorComponent(SkColorGetR(color), result);
@@ -104,12 +135,12 @@
 static SkImageSubset make_image_subset(const SkBitmap& bitmap) {
     SkASSERT(!bitmap.drawsNothing());
     SkIRect subset = bitmap.getSubset();
-    SkAutoLockPixels autoLockPixels(bitmap);
     SkASSERT(bitmap.pixelRef());
     SkBitmap tmp;
-    tmp.setInfo(bitmap.pixelRef()->info(), bitmap.rowBytes());
+    SkImageInfo pixelRefInfo =
+            bitmap.info().makeWH(bitmap.pixelRef()->width(), bitmap.pixelRef()->height());
+    tmp.setInfo(pixelRefInfo, bitmap.rowBytes());
     tmp.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0);
-    tmp.lockPixels();
     auto img = SkImage::MakeFromBitmap(tmp);
     if (img) {
         SkASSERT(!bitmap.isImmutable() || img->uniqueID() == bitmap.getGenerationID());
@@ -122,6 +153,19 @@
     return imageSubset;
 }
 
+// If the paint has a color filter, apply the color filter to the shader or the
+// paint color.  Remove the color filter.
+void remove_color_filter(SkPaint* paint) {
+    if (SkColorFilter* cf = paint->getColorFilter()) {
+        if (SkShader* shader = paint->getShader()) {
+            paint->setShader(shader->makeWithColorFilter(paint->refColorFilter()));
+        } else {
+            paint->setColor(cf->filterColor(paint->getColor()));
+        }
+        paint->setColorFilter(nullptr);
+    }
+}
+
 SkPDFDevice::GraphicStateEntry::GraphicStateEntry()
     : fColor(SK_ColorBLACK)
     , fTextScaleX(SK_Scalar1)
@@ -209,6 +253,7 @@
             return Op(u, v, kDifference_SkPathOp, r);
         case SkClipOp::kIntersect:
             return Op(u, v, kIntersect_SkPathOp, r);
+#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
         case SkClipOp::kUnion_deprecated:
             return Op(u, v, kUnion_SkPathOp, r);
         case SkClipOp::kXOR_deprecated:
@@ -218,6 +263,7 @@
         case SkClipOp::kReplace_deprecated:
             *r = v;
             return true;
+#endif
         default:
             return false;
     }
@@ -519,7 +565,7 @@
         if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) {
             SkPoint transformedPoint;
             this->ctm().mapXY(rect.x(), rect.y(), &transformedPoint);
-            fNamedDestinations.emplace_back(value, transformedPoint);
+            fNamedDestinations.emplace_back(NamedDestination{sk_ref_sp(value), transformedPoint});
         }
         return;
     }
@@ -536,18 +582,26 @@
         return;
     }
     if (!strcmp(SkAnnotationKeys::URL_Key(), key)) {
-        fLinkToURLs.emplace_back(transformedRect, value);
+        fLinkToURLs.emplace_back(RectWithData{transformedRect, sk_ref_sp(value)});
     } else if (!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
-        fLinkToDestinations.emplace_back(transformedRect, value);
+        fLinkToDestinations.emplace_back(RectWithData{transformedRect, sk_ref_sp(value)});
     }
 }
 
-void SkPDFDevice::drawPaint(const SkPaint& paint) {
-    SkPaint newPaint = paint;
+void SkPDFDevice::drawPaint(const SkPaint& srcPaint) {
+    SkPaint newPaint = srcPaint;
+    remove_color_filter(&newPaint);
     replace_srcmode_on_opaque_paint(&newPaint);
-
     newPaint.setStyle(SkPaint::kFill_Style);
-    ScopedContentEntry content(this, newPaint);
+
+    SkMatrix ctm = this->ctm();
+    if (ctm.getType() & SkMatrix::kPerspective_Mask) {
+        if (newPaint.getShader()) {
+            transform_shader(&newPaint, ctm);
+        }
+        ctm = SkMatrix::I();
+    }
+    ScopedContentEntry content(this, this->cs(), ctm, newPaint);
     this->internalDrawPaint(newPaint, content.entry());
 }
 
@@ -574,6 +628,7 @@
                              const SkPoint* points,
                              const SkPaint& srcPaint) {
     SkPaint passedPaint = srcPaint;
+    remove_color_filter(&passedPaint);
     replace_srcmode_on_opaque_paint(&passedPaint);
 
     if (count == 0) {
@@ -701,11 +756,12 @@
 void SkPDFDevice::drawRect(const SkRect& rect,
                            const SkPaint& srcPaint) {
     SkPaint paint = srcPaint;
+    remove_color_filter(&paint);
     replace_srcmode_on_opaque_paint(&paint);
     SkRect r = rect;
     r.sort();
 
-    if (paint.getPathEffect()) {
+    if (paint.getPathEffect() || paint.getMaskFilter()) {
         if (this->cs().isEmpty(size(*this))) {
             return;
         }
@@ -727,6 +783,7 @@
 void SkPDFDevice::drawRRect(const SkRRect& rrect,
                             const SkPaint& srcPaint) {
     SkPaint paint = srcPaint;
+    remove_color_filter(&paint);
     replace_srcmode_on_opaque_paint(&paint);
     SkPath  path;
     path.addRRect(rrect);
@@ -736,6 +793,7 @@
 void SkPDFDevice::drawOval(const SkRect& oval,
                            const SkPaint& srcPaint) {
     SkPaint paint = srcPaint;
+    remove_color_filter(&paint);
     replace_srcmode_on_opaque_paint(&paint);
     SkPath  path;
     path.addOval(oval);
@@ -750,6 +808,91 @@
             this->cs(), this->ctm(), origPath, srcPaint, prePathMatrix, pathIsMutable);
 }
 
+void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
+                                             const SkMatrix& ctm,
+                                             const SkPath& origPath,
+                                             const SkPaint& origPaint,
+                                             const SkMatrix* prePathMatrix) {
+    SkASSERT(origPaint.getMaskFilter());
+    SkPath path(origPath);
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+    if (prePathMatrix) {
+        path.transform(*prePathMatrix, &path);
+    }
+    SkStrokeRec::InitStyle initStyle = paint->getFillPath(path, &path)
+                                     ? SkStrokeRec::kFill_InitStyle
+                                     : SkStrokeRec::kHairline_InitStyle;
+    path.transform(ctm, &path);
+
+    // TODO(halcanary): respect fRasterDpi.
+    //        SkScalar rasterScale = (float)fRasterDpi / DPI_FOR_RASTER_SCALE_ONE;
+    // Would it be easier to just change the device size (and pre-scale the canvas)?
+    SkIRect bounds = clipStack.bounds(size(*this)).roundOut();
+    SkMask sourceMask;
+    if (!SkDraw::DrawToMask(path, &bounds, paint->getMaskFilter(), &SkMatrix::I(),
+                            &sourceMask, SkMask::kComputeBoundsAndRenderImage_CreateMode,
+                            initStyle)) {
+        return;
+    }
+    SkAutoMaskFreeImage srcAutoMaskFreeImage(sourceMask.fImage);
+    SkMask dstMask;
+    SkIPoint margin;
+    if (!paint->getMaskFilter()->filterMask(&dstMask, sourceMask, ctm, &margin)) {
+        return;
+    }
+    SkPixmap pm(SkImageInfo::Make(dstMask.fBounds.width(), dstMask.fBounds.height(),
+                                  kGray_8_SkColorType, kOpaque_SkAlphaType),
+                 dstMask.fImage, dstMask.fRowBytes);
+    sk_sp<SkImage> mask;
+    const int maskQuality = SK_PDF_MASK_QUALITY;
+    if (maskQuality <= 100 && maskQuality >= 0) {
+        SkDynamicMemoryWStream buffer;
+        SkJpegEncoder::Options jpegOptions;
+        jpegOptions.fQuality = maskQuality;
+        if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) {
+            mask = SkImage::MakeFromEncoded(buffer.detachAsData());
+            SkASSERT(mask);
+            if (mask) {
+                SkMask::FreeImage(dstMask.fImage);
+            }
+        }
+    }
+    if (!mask) {
+        mask = SkImage::MakeFromRaster(
+                pm, [](const void* p, void*) { SkMask::FreeImage((void*)p); }, nullptr);
+    }
+    // PDF doesn't seem to allow masking vector graphics with an Image XObject.
+    // Must mask with a Form XObject.
+    sk_sp<SkPDFDevice> maskDevice(SkPDFDevice::CreateUnflipped(fPageSize, fRasterDpi, fDocument));
+    {
+        SkPDFCanvas canvas(maskDevice);
+        canvas.drawImage(mask, dstMask.fBounds.x(), dstMask.fBounds.y());
+    }
+    sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
+                                 maskDevice->makeFormXObjectFromDevice(), false,
+                                 SkPDFGraphicState::kLuminosity_SMaskMode, fDocument->canon());
+    maskDevice = nullptr;
+    if (!ctm.isIdentity() && paint->getShader()) {
+        transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
+    }
+    ScopedContentEntry content(this, clipStack, SkMatrix::I(), *paint);
+    if (!content.entry()) {
+        return;
+    }
+    SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(sMaskGS.get()),
+                                  &content.entry()->fContent);
+
+    SkRect dstBounds = SkRect::Make(dstMask.fBounds);
+    SkPDFUtils::AppendRectangle(dstBounds, &content.entry()->fContent);
+    SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), &content.entry()->fContent);
+
+    // The no-softmask graphic state is used to "turn off" the mask for later draw calls.
+    auto noSMaskGS = SkPDFUtils::GetCachedT(&fDocument->canon()->fNoSmaskGraphicState,
+                                            &SkPDFGraphicState::MakeNoSmaskGraphicState);
+    SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(noSMaskGS.get()),
+                                                 &content.entry()->fContent);
+}
+
 void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
                                    const SkMatrix& ctm,
                                    const SkPath& origPath,
@@ -757,10 +900,16 @@
                                    const SkMatrix* prePathMatrix,
                                    bool pathIsMutable) {
     SkPaint paint = srcPaint;
+    remove_color_filter(&paint);
     replace_srcmode_on_opaque_paint(&paint);
     SkPath modifiedPath;
     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
 
+    if (paint.getMaskFilter()) {
+        this->internalDrawPathWithFilter(clipStack, ctm, origPath, paint, prePathMatrix);
+        return;
+    }
+
     SkMatrix matrix = ctm;
     if (prePathMatrix) {
         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
@@ -779,26 +928,34 @@
             return;
         }
         if (!pathIsMutable) {
+            modifiedPath = origPath;
             pathPtr = &modifiedPath;
             pathIsMutable = true;
         }
-        bool fill = paint.getFillPath(origPath, pathPtr);
-
-        SkPaint noEffectPaint(paint);
-        noEffectPaint.setPathEffect(nullptr);
-        if (fill) {
-            noEffectPaint.setStyle(SkPaint::kFill_Style);
+        if (paint.getFillPath(*pathPtr, pathPtr)) {
+            paint.setStyle(SkPaint::kFill_Style);
         } else {
-            noEffectPaint.setStyle(SkPaint::kStroke_Style);
-            noEffectPaint.setStrokeWidth(0);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(0);
         }
-        this->internalDrawPath(clipStack, ctm, *pathPtr, noEffectPaint, nullptr, true);
-        return;
+        paint.setPathEffect(nullptr);
     }
 
-    if (this->handleInversePath(origPath, paint, pathIsMutable, prePathMatrix)) {
+    if (this->handleInversePath(*pathPtr, paint, pathIsMutable, prePathMatrix)) {
         return;
     }
+    if (matrix.getType() & SkMatrix::kPerspective_Mask) {
+        if (!pathIsMutable) {
+            modifiedPath = origPath;
+            pathPtr = &modifiedPath;
+            pathIsMutable = true;
+        }
+        pathPtr->transform(matrix);
+        if (paint.getShader()) {
+            transform_shader(&paint, matrix);
+        }
+        matrix = SkMatrix::I();
+    }
 
     ScopedContentEntry content(this, clipStack, matrix, paint);
     if (!content.entry()) {
@@ -1235,15 +1392,41 @@
     wStream->writeText(" Tf\n");
 }
 
+static SkPath draw_text_as_path(const void* sourceText, size_t sourceByteCount,
+                               const SkScalar pos[], SkTextBlob::GlyphPositioning positioning,
+                               SkPoint offset, const SkPaint& srcPaint) {
+    SkPath path;
+    int glyphCount;
+    SkAutoTMalloc<SkPoint> tmpPoints;
+    switch (positioning) {
+        case SkTextBlob::kDefault_Positioning:
+            srcPaint.getTextPath(sourceText, sourceByteCount, offset.x(), offset.y(), &path);
+            break;
+        case SkTextBlob::kHorizontal_Positioning:
+            glyphCount = srcPaint.countText(sourceText, sourceByteCount);
+            tmpPoints.realloc(glyphCount);
+            for (int i = 0; i < glyphCount; ++i) {
+                tmpPoints[i] = {pos[i] + offset.x(), offset.y()};
+            }
+            srcPaint.getPosTextPath(sourceText, sourceByteCount, tmpPoints.get(), &path);
+            break;
+        case SkTextBlob::kFull_Positioning:
+            srcPaint.getPosTextPath(sourceText, sourceByteCount, (const SkPoint*)pos, &path);
+            path.offset(offset.x(), offset.y());
+            break;
+    }
+    return path;
+}
+
 void SkPDFDevice::internalDrawText(
         const void* sourceText, size_t sourceByteCount,
         const SkScalar pos[], SkTextBlob::GlyphPositioning positioning,
         SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters,
         uint32_t textByteLength, const char* utf8Text) {
-    NOT_IMPLEMENTED(srcPaint.getMaskFilter() != nullptr, false);
-    if (srcPaint.getMaskFilter() != nullptr) {
-        // Don't pretend we support drawing MaskFilters, it makes for artifacts
-        // making text unreadable (e.g. same text twice when using CSS shadows).
+    if (0 == sourceByteCount || !sourceText) {
+        return;
+    }
+    if (this->cs().isEmpty(size(*this))) {
         return;
     }
     NOT_IMPLEMENTED(srcPaint.isVerticalText(), false);
@@ -1252,12 +1435,18 @@
         // clear to me how to switch to "vertical writing" mode in PDF.
         // Currently neither Chromium or Android set this flag.
         // https://bug.skia.org/5665
-        return;
     }
-    if (0 == sourceByteCount || !sourceText) {
+    if (srcPaint.getPathEffect()
+            || srcPaint.getMaskFilter()
+            || SkPaint::kFill_Style != srcPaint.getStyle()) {
+        // Stroked Text doesn't work well with Type3 fonts.
+        SkPath path = draw_text_as_path(sourceText, sourceByteCount, pos,
+                                        positioning, offset, srcPaint);
+        this->drawPath(path, srcPaint, nullptr, true);
         return;
     }
     SkPaint paint = calculate_text_paint(srcPaint);
+    remove_color_filter(&paint);
     replace_srcmode_on_opaque_paint(&paint);
     if (!paint.getTypeface()) {
         paint.setTypeface(SkTypeface::MakeDefault());
@@ -1480,15 +1669,15 @@
     SkScalar scalarY = SkIntToScalar(y);
     for (const RectWithData& l : pdfDevice->fLinkToURLs) {
         SkRect r = l.rect.makeOffset(scalarX, scalarY);
-        fLinkToURLs.emplace_back(r, l.data.get());
+        fLinkToURLs.emplace_back(RectWithData{r, l.data});
     }
     for (const RectWithData& l : pdfDevice->fLinkToDestinations) {
         SkRect r = l.rect.makeOffset(scalarX, scalarY);
-        fLinkToDestinations.emplace_back(r, l.data.get());
+        fLinkToDestinations.emplace_back(RectWithData{r, l.data});
     }
     for (const NamedDestination& d : pdfDevice->fNamedDestinations) {
         SkPoint p = d.point + SkPoint::Make(scalarX, scalarY);
-        fNamedDestinations.emplace_back(d.nameData.get(), p);
+        fNamedDestinations.emplace_back(NamedDestination{d.nameData, p});
     }
 
     if (pdfDevice->isContentEmpty()) {
@@ -1704,10 +1893,8 @@
                                   &content.entry()->fContent);
     SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
 
-    // Call makeNoSmaskGraphicState() instead of
-    // SkPDFGraphicState::MakeNoSmaskGraphicState so that the canon
-    // can deduplicate.
-    sMaskGS = fDocument->canon()->makeNoSmaskGraphicState();
+    sMaskGS = SkPDFUtils::GetCachedT(&fDocument->canon()->fNoSmaskGraphicState,
+                                     &SkPDFGraphicState::MakeNoSmaskGraphicState);
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &content.entry()->fContent);
 }
@@ -1984,13 +2171,11 @@
 
     sk_sp<SkPDFGraphicState> newGraphicState;
     if (color == paint.getColor()) {
-        newGraphicState.reset(
-                SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint));
+        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
     } else {
         SkPaint newPaint = paint;
         newPaint.setColor(color);
-        newGraphicState.reset(
-                SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint));
+        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
     }
     int resourceIndex = addGraphicStateResource(newGraphicState.get());
     entry->fGraphicStateIndex = resourceIndex;
@@ -2028,8 +2213,7 @@
 }
 
 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
-    sk_sp<SkPDFFont> newFont(
-            SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID));
+    sk_sp<SkPDFFont> newFont = SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID);
     if (!newFont) {
         return -1;
     }
@@ -2042,9 +2226,7 @@
     return resourceIndex;
 }
 
-static SkSize rect_to_size(const SkRect& r) {
-    return SkSize::Make(r.width(), r.height());
-}
+static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
 
 static sk_sp<SkImage> color_filter(const SkImageSubset& imageSubset,
                                    SkColorFilter* colorFilter) {
@@ -2180,19 +2362,20 @@
     }
 
     SkBitmapKey key = imageSubset.getKey();
-    sk_sp<SkPDFObject> pdfimage = fDocument->canon()->findPDFBitmap(key);
+    sk_sp<SkPDFObject>* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
+    sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
     if (!pdfimage) {
         sk_sp<SkImage> img = imageSubset.makeImage();
         if (!img) {
             return;
         }
-        pdfimage = SkPDFCreateBitmapObject(
-                std::move(img), fDocument->canon()->getPixelSerializer());
+        pdfimage =
+                SkPDFCreateBitmapObject(std::move(img), fDocument->canon()->fPixelSerializer.get());
         if (!pdfimage) {
             return;
         }
         fDocument->serialize(pdfimage);  // serialize images early.
-        fDocument->canon()->addPDFBitmap(key, pdfimage);
+        fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
     }
     // TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject>
     SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()),
@@ -2204,10 +2387,12 @@
 #include "SkSpecialImage.h"
 #include "SkImageFilter.h"
 
-void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
-                              const SkPaint& paint) {
+void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, const SkPaint& paint,
+                              SkImage* clipImage, const SkMatrix& clipMatrix) {
     SkASSERT(!srcImg->isTextureBacked());
 
+    //TODO: clipImage support
+
     SkBitmap resultBM;
 
     SkImageFilter* filter = paint.getImageFilter();
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 93eb78d..61c08c3 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -170,7 +170,8 @@
 
     void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
 
-    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
+    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                     SkImage*, const SkMatrix&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
     sk_sp<SkSpecialImage> snapSpecial() override;
@@ -180,19 +181,11 @@
     struct RectWithData {
         SkRect rect;
         sk_sp<SkData> data;
-        RectWithData(const SkRect& rect, SkData* data)
-            : rect(rect), data(SkRef(data)) {}
-        RectWithData(RectWithData&&) = default;
-        RectWithData& operator=(RectWithData&& other) = default;
     };
 
     struct NamedDestination {
         sk_sp<SkData> nameData;
         SkPoint point;
-        NamedDestination(SkData* nameData, const SkPoint& point)
-            : nameData(SkRef(nameData)), point(point) {}
-        NamedDestination(NamedDestination&&) = default;
-        NamedDestination& operator=(NamedDestination&&) = default;
     };
 
     // TODO(vandebo): push most of SkPDFDevice's state into a core object in
@@ -281,6 +274,12 @@
                           const SkMatrix* prePathMatrix,
                           bool pathIsMutable);
 
+    void internalDrawPathWithFilter(const SkClipStack& clipStack,
+                                    const SkMatrix& ctm,
+                                    const SkPath& origPath,
+                                    const SkPaint& paint,
+                                    const SkMatrix* prePathMatrix);
+
     bool handleInversePath(const SkPath& origPath,
                            const SkPaint& paint, bool pathIsMutable,
                            const SkMatrix* prePathMatrix = nullptr);
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 92e82fc..931ce99 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -180,7 +180,7 @@
     , fRasterDpi(rasterDpi)
     , fMetadata(metadata)
     , fPDFA(pdfa) {
-    fCanon.setPixelSerializer(std::move(jpegEncoder));
+    fCanon.fPixelSerializer = std::move(jpegEncoder);
 }
 
 SkPDFDocument::~SkPDFDocument() {
@@ -218,8 +218,10 @@
     fPageDevice.reset(
             SkPDFDevice::Create(pageSize, fRasterDpi, this));
     fCanvas.reset(new SkPDFCanvas(fPageDevice));
-    fCanvas->clipRect(trimBox);
-    fCanvas->translate(trimBox.x(), trimBox.y());
+    if (SkRect::MakeWH(width, height) != trimBox) {
+        fCanvas->clipRect(trimBox);
+        fCanvas->translate(trimBox.x(), trimBox.y());
+    }
     return fCanvas.get();
 }
 
@@ -251,7 +253,7 @@
 void SkPDFDocument::reset() {
     fCanvas.reset(nullptr);
     fPages.reset();
-    fCanon.reset();
+    renew(&fCanon);
     renew(&fObjectSerializer);
     fFonts.reset();
 }
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 09d133e..f94dba7 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -7,14 +7,15 @@
 
 #include "SkData.h"
 #include "SkGlyphCache.h"
-#include "SkPaint.h"
+#include "SkMakeUnique.h"
 #include "SkPDFCanon.h"
 #include "SkPDFConvertType1FontStream.h"
 #include "SkPDFDevice.h"
+#include "SkPDFFont.h"
 #include "SkPDFMakeCIDGlyphWidthsArray.h"
 #include "SkPDFMakeToUnicodeCmap.h"
-#include "SkPDFFont.h"
 #include "SkPDFUtils.h"
+#include "SkPaint.h"
 #include "SkRefCnt.h"
 #include "SkScalar.h"
 #include "SkStream.h"
@@ -142,8 +143,8 @@
                                                        SkPDFCanon* canon) {
     SkASSERT(typeface);
     SkFontID id = typeface->uniqueID();
-    if (SkAdvancedTypefaceMetrics** ptr = canon->fTypefaceMetrics.find(id)) {
-        return *ptr;
+    if (std::unique_ptr<SkAdvancedTypefaceMetrics>* ptr = canon->fTypefaceMetrics.find(id)) {
+        return ptr->get();  // canon retains ownership.
     }
     int count = typeface->countGlyphs();
     if (count <= 0 || count > 1 + SK_MaxU16) {
@@ -151,14 +152,11 @@
         canon->fTypefaceMetrics.set(id, nullptr);
         return nullptr;
     }
-    sk_sp<SkAdvancedTypefaceMetrics> metrics(
-            typeface->getAdvancedTypefaceMetrics(
-                    SkTypeface::kGlyphNames_PerGlyphInfo | SkTypeface::kToUnicode_PerGlyphInfo,
-                    nullptr, 0));
+    std::unique_ptr<SkAdvancedTypefaceMetrics> metrics = typeface->getAdvancedMetrics();
     if (!metrics) {
-        metrics = sk_make_sp<SkAdvancedTypefaceMetrics>();
+        metrics = skstd::make_unique<SkAdvancedTypefaceMetrics>();
     }
-    return *canon->fTypefaceMetrics.set(id, metrics.release());
+    return canon->fTypefaceMetrics.set(id, std::move(metrics))->get();
 }
 
 SkAdvancedTypefaceMetrics::FontType SkPDFFont::FontType(const SkAdvancedTypefaceMetrics& metrics) {
@@ -174,9 +172,9 @@
     return gid != 0 ? gid - (gid - 1) % 255 : 1;
 }
 
-SkPDFFont* SkPDFFont::GetFontResource(SkPDFCanon* canon,
-                                      SkTypeface* face,
-                                      SkGlyphID glyphID) {
+sk_sp<SkPDFFont> SkPDFFont::GetFontResource(SkPDFCanon* canon,
+                                            SkTypeface* face,
+                                            SkGlyphID glyphID) {
     SkASSERT(canon);
     SkASSERT(face);  // All SkPDFDevice::internalDrawText ensures this.
     const SkAdvancedTypefaceMetrics* fontMetrics = SkPDFFont::GetMetrics(face, canon);
@@ -188,10 +186,10 @@
     SkGlyphID subsetCode = multibyte ? 0 : first_nonzero_glyph_for_single_byte_encoding(glyphID);
     uint64_t fontID = (SkTypeface::UniqueID(face) << 16) | subsetCode;
 
-    if (SkPDFFont** found = canon->fFontMap.find(fontID)) {
-        SkPDFFont* foundFont = *found;
+    if (sk_sp<SkPDFFont>* found = canon->fFontMap.find(fontID)) {
+        SkDEBUGCODE(SkPDFFont* foundFont = found->get());
         SkASSERT(foundFont && multibyte == foundFont->multiByteGlyphs());
-        return SkRef(foundFont);
+        return *found;
     }
 
     sk_sp<SkTypeface> typeface(sk_ref_sp(face));
@@ -227,8 +225,8 @@
             font = sk_make_sp<SkPDFType3Font>(std::move(info), metrics);
             break;
     }
-    canon->fFontMap.set(fontID, SkRef(font.get()));
-    return font.release();  // TODO(halcanary) return sk_sp<SkPDFFont>.
+    canon->fFontMap.set(fontID, font);
+    return font;
 }
 
 SkPDFFont::SkPDFFont(SkPDFFont::Info info)
@@ -544,11 +542,11 @@
 {
     SkFontID fontID = this->typeface()->uniqueID();
     sk_sp<SkPDFDict> fontDescriptor;
-    if (SkPDFDict** ptr = canon->fFontDescriptors.find(fontID)) {
-        fontDescriptor = sk_ref_sp(*ptr);
+    if (sk_sp<SkPDFDict>* ptr = canon->fFontDescriptors.find(fontID)) {
+        fontDescriptor = *ptr;
     } else {
         fontDescriptor = make_type1_font_descriptor(this->typeface(), metrics);
-        canon->fFontDescriptors.set(fontID, SkRef(fontDescriptor.get()));
+        canon->fFontDescriptors.set(fontID, fontDescriptor);
     }
     this->insertObjRef("FontDescriptor", std::move(fontDescriptor));
     // TODO(halcanary): subset this (advances and names).
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index 67786f3..5639d87 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -83,12 +83,11 @@
      *  @param typeface  The typeface to find, not nullptr.
      *  @param glyphID   Specify which section of a large font is of interest.
      */
-    static SkPDFFont* GetFontResource(SkPDFCanon* canon,
-                                      SkTypeface* typeface,
-                                      SkGlyphID glyphID);
+    static sk_sp<SkPDFFont> GetFontResource(SkPDFCanon* canon,
+                                            SkTypeface* typeface,
+                                            SkGlyphID glyphID);
 
-    /** Uses (kGlyphNames_PerGlyphInfo | kToUnicode_PerGlyphInfo) to get 
-     *  SkAdvancedTypefaceMetrics, and caches the result.
+    /** Gets SkAdvancedTypefaceMetrics, and caches the result.
      *  @param typeface can not be nullptr.
      *  @return nullptr only when typeface is bad.
      */
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index d60526c..068f9bd 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -104,9 +104,8 @@
     , fStrokeJoin(SkToU8(p.getStrokeJoin()))
     , fMode(SkToU8((unsigned)mode_for_pdf(p.getBlendMode()))) {}
 
-// static
-SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
-        SkPDFCanon* canon, const SkPaint& paint) {
+sk_sp<SkPDFGraphicState> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
+                                                                    const SkPaint& paint) {
     SkASSERT(canon);
     SkPDFGraphicState key(paint);
     if (const SkPDFGraphicState* canonGS = canon->findGraphicState(key)) {
@@ -114,14 +113,14 @@
         // since the emitObject() interface is non-const.  But We
         // promise that there is no way to mutate this object from
         // here on out.
-        return SkRef(const_cast<SkPDFGraphicState*>(canonGS));
+        return sk_sp<SkPDFGraphicState>(SkRef(const_cast<SkPDFGraphicState*>(canonGS)));
     }
-    SkPDFGraphicState* pdfGraphicState = new SkPDFGraphicState(paint);
-    canon->addGraphicState(pdfGraphicState);
+    sk_sp<SkPDFGraphicState> pdfGraphicState(new SkPDFGraphicState(paint));
+    canon->addGraphicState(pdfGraphicState.get());
     return pdfGraphicState;
 }
 
-sk_sp<SkPDFStream> SkPDFGraphicState::MakeInvertFunction() {
+static sk_sp<SkPDFStream> make_invert_function() {
     // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
     // a type 2 function, so we use a type 4 function.
     auto domainAndRange = sk_make_sp<SkPDFArray>();
@@ -156,7 +155,8 @@
     if (invert) {
         // Instead of calling SkPDFGraphicState::MakeInvertFunction,
         // let the canon deduplicate this object.
-        sMaskDict->insertObjRef("TR", canon->makeInvertFunction());
+        sMaskDict->insertObjRef(
+                "TR", SkPDFUtils::GetCachedT(&canon->fInvertFunction, &make_invert_function));
     }
 
     auto result = sk_make_sp<SkPDFDict>("ExtGState");
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index 8ee6728..310c1cf 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -33,15 +33,11 @@
     void emitObject(SkWStream* stream,
                     const SkPDFObjNumMap& objNumMap) const override;
 
-    /** Get the graphic state for the passed SkPaint. The reference count of
-     *  the object is incremented and it is the caller's responsibility to
-     *  unreference it when done. This is needed to accommodate the weak
-     *  reference pattern used when the returned object is new and has no
-     *  other references.
+    /** Get the graphic state for the passed SkPaint.
      *  @param paint  The SkPaint to emulate.
      */
-    static SkPDFGraphicState* GetGraphicStateForPaint(SkPDFCanon* canon,
-                                                      const SkPaint& paint);
+    static sk_sp<SkPDFGraphicState> GetGraphicStateForPaint(SkPDFCanon* canon,
+                                                            const SkPaint& paint);
 
     /** Make a graphic state that only sets the passed soft mask.
      *  @param sMask     The form xobject to use as a soft mask.
diff --git a/src/pdf/SkPDFMetadata.cpp b/src/pdf/SkPDFMetadata.cpp
index e434b03..ee5c19b 100644
--- a/src/pdf/SkPDFMetadata.cpp
+++ b/src/pdf/SkPDFMetadata.cpp
@@ -11,7 +11,9 @@
 #include "SkPDFTypes.h"
 #include <utility>
 
-#define SKPDF_PRODUCER "Skia/PDF [" SK_MILESTONE "]"
+#define SKPDF_STRING(X) SKPDF_STRING_IMPL(X)
+#define SKPDF_STRING_IMPL(X) #X
+#define SKPDF_PRODUCER "Skia/PDF m" SKPDF_STRING(SK_MILESTONE)
 #define SKPDF_CUSTOM_PRODUCER_KEY "ProductionLibrary"
 
 static SkString pdf_date(const SkTime::DateTime& dt) {
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index bc87c4a..49af685 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -737,7 +737,7 @@
     return true;
 }
 
-sk_sp<SkPDFArray> SkPDFShader::MakeRangeObject() {
+static sk_sp<SkPDFArray> make_range_object() {
     auto range = sk_make_sp<SkPDFArray>();
     range->reserve(6);
     range->appendInt(0);
@@ -933,14 +933,11 @@
         
         pdfShader->insertObject("Domain", domain);
 
-        // Call canon->makeRangeObject() instead of
-        // SkPDFShader::MakeRangeObject() so that the canon can
-        // deduplicate.
-        std::unique_ptr<SkStreamAsset> functionStream(
-                functionCode.detachAsStream());
-        sk_sp<SkPDFStream> function = make_ps_function(std::move(functionStream),
-                                                       std::move(domain),
-                                                       canon->makeRangeObject());
+        std::unique_ptr<SkStreamAsset> functionStream(functionCode.detachAsStream());
+        sk_sp<SkPDFArray> rangeObject =
+                SkPDFUtils::GetCachedT(&canon->fRangeObject, &make_range_object);
+        sk_sp<SkPDFStream> function = make_ps_function(std::move(functionStream), std::move(domain),
+                                                       std::move(rangeObject));
         pdfShader->insertObjRef("Function", std::move(function));
     }
 
@@ -962,7 +959,6 @@
                                             SkBitmap image) {
     SkASSERT(state.fBitmapKey ==
              (SkBitmapKey{image.getSubset(), image.getGenerationID()}));
-    SkAutoLockPixels SkAutoLockPixels(image);
 
     // The image shader pattern cell will be drawn into a separate device
     // in pattern cell space (no scaling on the bitmap, though there may be
@@ -1234,7 +1230,7 @@
 
     if (fType != SkShader::kNone_GradientType) {
         fBitmapKey = SkBitmapKey{{0, 0, 0, 0}, 0};
-        fShaderTransform = shader->getLocalMatrix();
+        fShaderTransform = SkPDFUtils::GetShaderLocalMatrix(shader);
         this->allocateGradientInfoStorage();
         shader->asAGradient(&fInfo);
         return;
@@ -1269,10 +1265,10 @@
         rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
     }
 
-    SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
-                                 SkScalarRoundToInt(rasterScale * bbox.height()));
-    SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(),
-                                SkIntToScalar(size.height()) / shaderRect.height());
+    SkISize size = {SkScalarRoundToInt(rasterScale * bbox.width()),
+                    SkScalarRoundToInt(rasterScale * bbox.height())};
+    SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(),
+                    SkIntToScalar(size.height()) / shaderRect.height()};
 
     imageDst->allocN32Pixels(size.width(), size.height());
     imageDst->eraseColor(SK_ColorTRANSPARENT);
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 42b4ed4..2559f93 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -383,32 +383,29 @@
     }
 }
 
-SkPDFDict::Record::Record(SkPDFUnion&& k, SkPDFUnion&& v)
-    : fKey(std::move(k)), fValue(std::move(v)) {}
-
 int SkPDFDict::size() const { return fRecords.count(); }
 
 void SkPDFDict::insertObjRef(const char key[], sk_sp<SkPDFObject> objSp) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp)));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
 }
 
 void SkPDFDict::insertObjRef(const SkString& key, sk_sp<SkPDFObject> objSp) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp)));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
 }
 
 void SkPDFDict::insertObject(const char key[], sk_sp<SkPDFObject> objSp) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp)));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp))});
 }
 void SkPDFDict::insertObject(const SkString& key, sk_sp<SkPDFObject> objSp) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp)));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp))});
 }
 
 void SkPDFDict::insertBool(const char key[], bool value) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Bool(value)});
 }
 
 void SkPDFDict::insertInt(const char key[], int32_t value) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Int(value)});
 }
 
 void SkPDFDict::insertInt(const char key[], size_t value) {
@@ -416,23 +413,23 @@
 }
 
 void SkPDFDict::insertScalar(const char key[], SkScalar value) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Scalar(value)});
 }
 
 void SkPDFDict::insertName(const char key[], const char name[]) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Name(name)});
 }
 
 void SkPDFDict::insertName(const char key[], const SkString& name) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Name(name)});
 }
 
 void SkPDFDict::insertString(const char key[], const char value[]) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::String(value));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::String(value)});
 }
 
 void SkPDFDict::insertString(const char key[], const SkString& value) {
-    fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::String(value));
+    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::String(value)});
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -492,7 +489,7 @@
     SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap);
     stream->writeText(">>");
     stream->writeText(" stream\n");
-    buffer.writeToStream(stream);
+    buffer.writeToAndReset(stream);
     stream->writeText("\nendstream");
 }
 #endif
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 0be20f1..06e4858 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -278,11 +278,6 @@
     struct Record {
         SkPDFUnion fKey;
         SkPDFUnion fValue;
-        Record(SkPDFUnion&&, SkPDFUnion&&);
-        Record(Record&&) = default;
-        Record& operator=(Record&&) = default;
-        Record(const Record&) = delete;
-        Record& operator=(const Record&) = delete;
     };
     SkTArray<Record> fRecords;
     SkDEBUGCODE(bool fDumped;)
diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h
index 5c9e46b..c1d9a72 100644
--- a/src/pdf/SkPDFUtils.h
+++ b/src/pdf/SkPDFUtils.h
@@ -11,6 +11,7 @@
 
 #include "SkPaint.h"
 #include "SkPath.h"
+#include "SkShader.h"
 #include "SkStream.h"
 #include "SkUtils.h"
 
@@ -102,6 +103,23 @@
         SkPDFUtils::WriteUInt16BE(wStream, utf16[1]);
     }
 }
+
+template <class T>
+static sk_sp<T> GetCachedT(sk_sp<T>* cachedT, sk_sp<T> (*makeNewT)()) {
+    if (*cachedT) {
+        return *cachedT;
+    }
+    *cachedT = (*makeNewT)();
+    return *cachedT;
+}
+
+inline SkMatrix GetShaderLocalMatrix(const SkShader* shader) {
+    SkMatrix localMatrix;
+    if (sk_sp<SkShader> s = shader->makeAsALocalMatrixShader(&localMatrix)) {
+        return SkMatrix::Concat(s->getLocalMatrix(), localMatrix);
+    }
+    return shader->getLocalMatrix();
+}
 }  // namespace SkPDFUtils
 
 #endif
diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp
index 1473b84..7b665d9 100644
--- a/src/pipe/SkPipeCanvas.cpp
+++ b/src/pipe/SkPipeCanvas.cpp
@@ -240,6 +240,12 @@
     if (rec.fBackdrop) {
         extra |= kHasBackdrop_SaveLayerMask;
     }
+    if (rec.fClipMask) {
+        extra |= kHasClipMask_SaveLayerMask;
+    }
+    if (rec.fClipMatrix) {
+        extra |= kHasClipMatrix_SaveLayerMask;
+    }
 
     writer.write32(pack_verb(SkPipeVerb::kSaveLayer, extra));
     if (rec.fBounds) {
@@ -251,6 +257,13 @@
     if (rec.fBackdrop) {
         writer.writeFlattenable(rec.fBackdrop);
     }
+    if (rec.fClipMask) {
+        writer.writeImage(rec.fClipMask);
+    }
+    if (rec.fClipMatrix) {
+        writer.writeMatrix(*rec.fClipMatrix);
+    }
+
     return kNoLayer_SaveLayerStrategy;
 }
 
diff --git a/src/pipe/SkPipeFormat.h b/src/pipe/SkPipeFormat.h
index 9a1d30c..a652127 100644
--- a/src/pipe/SkPipeFormat.h
+++ b/src/pipe/SkPipeFormat.h
@@ -107,6 +107,8 @@
     kHasPaint_SaveLayerMask         = 1 << 9,
     kHasBackdrop_SaveLayerMask      = 1 << 10,
     kDontClipToLayer_SaveLayerMask  = 1 << 11,
+    kHasClipMask_SaveLayerMask      = 1 << 12,
+    kHasClipMatrix_SaveLayerMask    = 1 << 13,
 };
 
 enum {
diff --git a/src/pipe/SkPipeReader.cpp b/src/pipe/SkPipeReader.cpp
index 6310b15..07b360c 100644
--- a/src/pipe/SkPipeReader.cpp
+++ b/src/pipe/SkPipeReader.cpp
@@ -17,6 +17,7 @@
 #include "SkRSXform.h"
 #include "SkTextBlob.h"
 #include "SkTypeface.h"
+#include "SkVertices.h"
 
 class SkPipeReader;
 
@@ -243,6 +244,14 @@
     if (extra & kHasBackdrop_SaveLayerMask) {
         backdrop = reader.readImageFilter();
     }
+    sk_sp<SkImage> clipMask;
+    if (extra & kHasClipMask_SaveLayerMask) {
+        clipMask = reader.readImage();
+    }
+    SkMatrix clipMatrix;
+    if (extra & kHasClipMatrix_SaveLayerMask) {
+        reader.readMatrix(&clipMatrix);
+    }
     SkCanvas::SaveLayerFlags flags = (SkCanvas::SaveLayerFlags)(extra & kFlags_SaveLayerMask);
 
     // unremap this wacky flag
@@ -250,7 +259,8 @@
         flags |= (1 << 31);//SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag;
     }
 
-    canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), flags));
+    canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), clipMask.get(),
+                      (extra & kHasClipMatrix_SaveLayerMask) ? &clipMatrix : nullptr, flags));
 }
 
 static void restore_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {
@@ -567,32 +577,9 @@
 
 static void drawVertices_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {
     SkASSERT(SkPipeVerb::kDrawVertices == unpack_verb(packedVerb));
-    SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)
-            ((packedVerb & kVMode_DrawVerticesMask) >> kVMode_DrawVerticesShift);
-    int vertexCount = packedVerb & kVCount_DrawVerticesMask;
-    if (0 == vertexCount) {
-        vertexCount = reader.read32();
-    }
-    SkBlendMode bmode = (SkBlendMode)
-            ((packedVerb & kXMode_DrawVerticesMask) >> kXMode_DrawVerticesShift);
-    const SkPoint* vertices = skip<SkPoint>(reader, vertexCount);
-    const SkPoint* texs = nullptr;
-    if (packedVerb & kHasTex_DrawVerticesMask) {
-        texs = skip<SkPoint>(reader, vertexCount);
-    }
-    const SkColor* colors = nullptr;
-    if (packedVerb & kHasColors_DrawVerticesMask) {
-        colors = skip<SkColor>(reader, vertexCount);
-    }
-    int indexCount = 0;
-    const uint16_t* indices = nullptr;
-    if (packedVerb & kHasIndices_DrawVerticesMask) {
-        indexCount = reader.read32();
-        indices = skip<uint16_t>(reader, indexCount);
-    }
-
-    canvas->drawVertices(vmode, vertexCount, vertices, texs, colors, bmode,
-                         indices, indexCount, read_paint(reader));
+    SkBlendMode bmode = (SkBlendMode)unpack_verb_extra(packedVerb);
+    sk_sp<SkData> data = reader.readByteArrayAsData();
+    canvas->drawVertices(SkVertices::Decode(data->data(), data->size()), bmode, read_paint(reader));
 }
 
 static void drawPicture_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 240f75c..b87af6f 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -540,17 +540,14 @@
     }
 }
 
-SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo perGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const {
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_FreeType::onGetAdvancedMetrics() const {
     AutoFTAccess fta(this);
     FT_Face face = fta.face();
     if (!face) {
         return nullptr;
     }
 
-    SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
     info->fFontName.set(FT_Get_Postscript_Name(face));
 
     if (FT_HAS_MULTIPLE_MASTERS(face)) {
@@ -650,13 +647,9 @@
     info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin, face->bbox.yMax,
                                     face->bbox.xMax, face->bbox.yMin);
 
-    if (!FT_IS_SCALABLE(face)) {
-        perGlyphInfo = kNo_PerGlyphInfo;
-    }
+    bool perGlyphInfo = FT_IS_SCALABLE(face);
 
-    if (perGlyphInfo & kGlyphNames_PerGlyphInfo &&
-        info->fType == SkAdvancedTypefaceMetrics::kType1_Font)
-    {
+    if (perGlyphInfo && info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {
         // Postscript fonts may contain more than 255 glyphs, so we end up
         // using multiple font descriptions with a glyph ordering.  Record
         // the name of each glyph.
@@ -668,7 +661,7 @@
         }
     }
 
-    if (perGlyphInfo & kToUnicode_PerGlyphInfo &&
+    if (perGlyphInfo &&
         info->fType != SkAdvancedTypefaceMetrics::kType1_Font &&
         face->num_charmaps)
     {
@@ -1855,7 +1848,9 @@
         const SkScalar axisMin = SkFixedToScalar(axisDefinition.fMinimum);
         const SkScalar axisMax = SkFixedToScalar(axisDefinition.fMaximum);
         axisValues[i] = axisDefinition.fDefault;
-        for (int j = 0; j < position.coordinateCount; ++j) {
+        // The position may be over specified. If there are multiple values for a given axis,
+        // use the last one since that's what css-fonts-4 requires.
+        for (int j = position.coordinateCount; j --> 0;) {
             const auto& coordinate = position.coordinates[j];
             if (axisDefinition.fTag == coordinate.axis) {
                 const SkScalar axisValue = SkTPin(coordinate.value, axisMin, axisMax);
diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h
index b9e8b13..5270745 100644
--- a/src/ports/SkFontHost_FreeType_common.h
+++ b/src/ports/SkFontHost_FreeType_common.h
@@ -80,8 +80,7 @@
     virtual SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
                                                    const SkDescriptor*) const override;
     void onFilterRec(SkScalerContextRec*) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                        PerGlyphInfo, const uint32_t*, uint32_t) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     int onGetUPEM() const override;
     bool onGetKerningPairAdjustments(const uint16_t glyphs[], int count,
                                      int32_t adjustments[]) const override;
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index ca236a3..92334cb 100644
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -46,6 +46,8 @@
 #include "SkTypeface_mac.h"
 #include "SkUtils.h"
 
+#include <dlfcn.h>
+
 // Experimental code to use a global lock whenever we access CG, to see if this reduces
 // crashes in Chrome
 #define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
@@ -60,6 +62,10 @@
 // Set to make glyph bounding boxes visible.
 #define SK_SHOW_TEXT_BLIT_COVERAGE 0
 
+CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
+    return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
+}
+
 class SkScalerContext_Mac;
 
 struct CFSafeRelease {
@@ -311,6 +317,56 @@
     CGFloat operator()(CGFloat s) { return s; }
 };
 
+/** Returns the [-1, 1] CTFontDescriptor weights for the
+ *  <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
+ *
+ *  It is assumed that the values will be interpolated linearly between these points.
+ *  NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
+ *  The actual values appear to be stable, but they may change in the future without notice.
+ */
+static CGFloat(&get_NSFontWeight_mapping())[11] {
+
+    // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
+#ifdef SK_BUILD_FOR_MAC
+#  define SK_KIT_FONT_WEIGHT_PREFIX "NS"
+#endif
+#ifdef SK_BUILD_FOR_IOS
+#  define SK_KIT_FONT_WEIGHT_PREFIX "UI"
+#endif
+    static constexpr struct {
+        CGFloat defaultValue;
+        const char* name;
+    } nsFontWeightLoaderInfos[] = {
+        { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
+        { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
+        { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
+        {  0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
+        {  0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
+        {  0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
+        {  0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
+        {  0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
+        {  0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
+    };
+
+    static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
+    static CGFloat nsFontWeights[11];
+    static SkOnce once;
+    once([&] {
+        size_t i = 0;
+        nsFontWeights[i++] = -1.00;
+        for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
+            void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
+            if (nsFontWeightValuePtr) {
+                nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
+            } else {
+                nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
+            }
+        }
+        nsFontWeights[i++] = 1.00;
+    });
+    return nsFontWeights;
+}
+
 /** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
  *
  *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
@@ -322,31 +378,15 @@
     // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
     // However, on this end we can't tell, so this is ignored.
 
-    /** This mapping for native fonts is determined by running the following in an .mm file
-     *  #include <AppKit/AppKit>
-     *  printf("{  100, % #.2f },\n", NSFontWeightUltraLight);
-     *  printf("{  200, % #.2f },\n", NSFontWeightThin);
-     *  printf("{  300, % #.2f },\n", NSFontWeightLight);
-     *  printf("{  400, % #.2f },\n", NSFontWeightRegular);
-     *  printf("{  500, % #.2f },\n", NSFontWeightMedium);
-     *  printf("{  600, % #.2f },\n", NSFontWeightSemibold);
-     *  printf("{  700, % #.2f },\n", NSFontWeightBold);
-     *  printf("{  800, % #.2f },\n", NSFontWeightHeavy);
-     *  printf("{  900, % #.2f },\n", NSFontWeightBlack);
-     */
-    static constexpr Interpolator::Mapping nativeWeightMappings[] = {
-        {    0, -1.00 },
-        {  100, -0.80 },
-        {  200, -0.60 },
-        {  300, -0.40 },
-        {  400,  0.00 },
-        {  500,  0.23 },
-        {  600,  0.30 },
-        {  700,  0.40 },
-        {  800,  0.56 },
-        {  900,  0.62 },
-        { 1000,  1.00 },
-    };
+    static Interpolator::Mapping nativeWeightMappings[11];
+    static SkOnce once;
+    once([&] {
+        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+        for (int i = 0; i < 11; ++i) {
+            nativeWeightMappings[i].src_val = i * 100;
+            nativeWeightMappings[i].dst_val = nsFontWeights[i];
+        }
+    });
     static constexpr Interpolator nativeInterpolator(
             nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
 
@@ -385,31 +425,15 @@
     static constexpr Interpolator dataProviderInterpolator(
             dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
 
-    /** This mapping for native fonts is determined by running the following in an .mm file
-     *  #include <AppKit/AppKit>
-     *  printf("{ % #.2f,  100 },\n", NSFontWeightUltraLight);
-     *  printf("{ % #.2f,  200 },\n", NSFontWeightThin);
-     *  printf("{ % #.2f,  300 },\n", NSFontWeightLight);
-     *  printf("{ % #.2f,  400 },\n", NSFontWeightRegular);
-     *  printf("{ % #.2f,  500 },\n", NSFontWeightMedium);
-     *  printf("{ % #.2f,  600 },\n", NSFontWeightSemibold);
-     *  printf("{ % #.2f,  700 },\n", NSFontWeightBold);
-     *  printf("{ % #.2f,  800 },\n", NSFontWeightHeavy);
-     *  printf("{ % #.2f,  900 },\n", NSFontWeightBlack);
-     */
-    static constexpr Interpolator::Mapping nativeWeightMappings[] = {
-        { -1.00,    0 },
-        { -0.80,  100 },
-        { -0.60,  200 },
-        { -0.40,  300 },
-        {  0.00,  400 },
-        {  0.23,  500 },
-        {  0.30,  600 },
-        {  0.40,  700 },
-        {  0.56,  800 },
-        {  0.62,  900 },
-        {  1.00, 1000 },
-    };
+    static Interpolator::Mapping nativeWeightMappings[11];
+    static SkOnce once;
+    once([&] {
+        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+        for (int i = 0; i < 11; ++i) {
+            nativeWeightMappings[i].src_val = nsFontWeights[i];
+            nativeWeightMappings[i].dst_val = i * 100;
+        }
+    });
     static constexpr Interpolator nativeInterpolator(
             nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
 
@@ -502,12 +526,13 @@
                                            const SkDescriptor*) const override;
     void onFilterRec(SkScalerContextRec*) const override;
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-            PerGlyphInfo, const uint32_t* glyphIDs, uint32_t glyphIDsCount) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     int onCharsToGlyphs(const void* chars, Encoding,
                         uint16_t glyphs[], int glyphCount) const override;
     int onCountGlyphs() const override;
 
+    void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
+
 private:
     bool fIsLocalStream;
 
@@ -516,7 +541,7 @@
 
 static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
     CTFontRef self = (CTFontRef)context;
-    CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef.get();
+    CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
 
     return CFEqual(self, other);
 }
@@ -633,12 +658,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
-CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
-    const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
-    return macface ? macface->fFontRef.get() : nullptr;
-}
-
 /*  This function is visible on the outside. It first searches the cache, and if
  *  not found, returns a new entry (after adding it to the cache).
  */
@@ -759,7 +778,7 @@
 {
     AUTO_CG_LOCK();
 
-    CTFontRef ctFont = static_cast<SkTypeface_Mac*>(this->getTypeface())->fFontRef.get();
+    CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
     CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
     SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
     fGlyphCount = SkToU16(numGlyphs);
@@ -1403,8 +1422,10 @@
     while (glyphCount > 0) {
         CGGlyph glyph;
         if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
-            out[glyph] = unichar;
-            --glyphCount;
+            if (out[glyph] != 0) {
+                out[glyph] = unichar;
+                --glyphCount;
+            }
         }
         if (++unichar == 0) {
             break;
@@ -1469,17 +1490,14 @@
     dst->resize(strlen(dst->c_str()));
 }
 
-SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo perGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const {
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
 
     AUTO_CG_LOCK();
 
     UniqueCFRef<CTFontRef> ctFont =
             ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
 
-    SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
 
     {
         UniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
@@ -1502,9 +1520,7 @@
 
     CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
 
-    if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
-        populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
-    }
+    populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
 
     // If it's not a truetype font, mark it as 'other'. Assume that TrueType
     // fonts always have both glyf and loca tables. At the least, this is what
@@ -1826,8 +1842,8 @@
 {
     // The CGFont variation data does not contain the tag.
 
-    // This call always returns nullptr on 10.10 and under for CGFontCreateWithDataProvider fonts.
-    // When this happens, there is no API to provide the tag.
+    // 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.
     UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
     if (!ctAxes) {
         return -1;
@@ -2340,14 +2356,17 @@
     }
 
     SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
+        if (!familyName) {
+            return nullptr;
+        }
         UniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
         return CreateSet(cfName.get());
     }
 
     SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                   const SkFontStyle& fontStyle) const override {
-        sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
-        return sset->matchStyle(fontStyle);
+                                   const SkFontStyle& style) const override {
+        UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+        return create_from_desc(desc.get());
     }
 
     SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
@@ -2403,8 +2422,8 @@
         // CTFont variation dictionary runs into bugs. So use the CTFont variation data
         // to match names to tags to create the appropriate CGFont.
         UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
-        // This call always returns nullptr on 10.10 and under.
-        // When this happens, there is no API to provide the tag.
+        // 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.
         UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
         if (!ctAxes) {
             return nullptr;
@@ -2468,7 +2487,9 @@
             }
 
             double value = defDouble;
-            for (int j = 0; j < position.coordinateCount; ++j) {
+            // The position may be over specified. If there are multiple values for a given axis,
+            // use the last one since that's what css-fonts-4 requires.
+            for (int j = position.coordinateCount; j --> 0;) {
                 if (position.coordinates[j].axis == tagLong) {
                     value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
                                    minDouble, maxDouble);
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 25c6088..4a0aabe 100644
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -259,8 +259,7 @@
     SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
                                            const SkDescriptor*) const override;
     void onFilterRec(SkScalerContextRec*) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo, const uint32_t*, uint32_t) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
     int onCharsToGlyphs(const void* chars, Encoding encoding,
                         uint16_t glyphs[], int glyphCount) const override;
@@ -1725,12 +1724,9 @@
     *isLocalStream = this->fSerializeAsStream;
 }
 
-SkAdvancedTypefaceMetrics* LogFontTypeface::onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo perGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const {
+std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const {
     LOGFONT lf = fLogFont;
-    SkAdvancedTypefaceMetrics* info = nullptr;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
 
     HDC hdc = CreateCompatibleDC(nullptr);
     HFONT font = CreateFontIndirect(&lf);
@@ -1760,7 +1756,7 @@
     }
     glyphCount = calculateGlyphCount(hdc, fLogFont);
 
-    info = new SkAdvancedTypefaceMetrics;
+    info.reset(new SkAdvancedTypefaceMetrics);
     tchar_to_skstring(lf.lfFaceName, &info->fFontName);
     // If bit 1 is set, the font may not be embedded in a document.
     // If bit 1 is clear, the font can be embedded.
@@ -1769,9 +1765,7 @@
         info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
     }
 
-    if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
-        populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
-    }
+    populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
 
     if (glyphCount > 0 &&
         (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
diff --git a/src/ports/SkFontMgr_fontconfig.cpp b/src/ports/SkFontMgr_fontconfig.cpp
index bceb4de..be4b17d 100644
--- a/src/ports/SkFontMgr_fontconfig.cpp
+++ b/src/ports/SkFontMgr_fontconfig.cpp
@@ -489,12 +489,9 @@
         this->INHERITED::onFilterRec(rec);
     }
 
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(PerGlyphInfo perGlyphInfo,
-                                                            const uint32_t* glyphIDs,
-                                                            uint32_t glyphIDsCount) const override
-    {
-        SkAdvancedTypefaceMetrics* info =
-            this->INHERITED::onGetAdvancedTypefaceMetrics(perGlyphInfo, glyphIDs, glyphIDsCount);
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
+        std::unique_ptr<SkAdvancedTypefaceMetrics> info =
+            this->INHERITED::onGetAdvancedMetrics();
 
         // Simulated fonts shouldn't be considered to be of the type of their data.
         if (get_matrix(fPattern, FC_MATRIX) || get_bool(fPattern, FC_EMBOLDEN)) {
@@ -752,6 +749,9 @@
     }
 
     SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
+        if (!familyName) {
+            return nullptr;
+        }
         FCLocker lock;
 
         SkAutoFcPattern pattern;
diff --git a/src/ports/SkFontMgr_win_dw.cpp b/src/ports/SkFontMgr_win_dw.cpp
index f985c4f..1fd300c 100644
--- a/src/ports/SkFontMgr_win_dw.cpp
+++ b/src/ports/SkFontMgr_win_dw.cpp
@@ -484,6 +484,10 @@
 }
 
 SkFontStyleSet* SkFontMgr_DirectWrite::onMatchFamily(const char familyName[]) const {
+    if (!familyName) {
+        return nullptr;
+    }
+
     SkSMallocWCHAR dwFamilyName;
     HRN(sk_cstring_to_wchar(familyName, &dwFamilyName));
 
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index a449324..de9b61a 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -17,13 +17,11 @@
 #include "SkColorMatrixFilterRowMajor255.h"
 #include "SkComposeImageFilter.h"
 #include "SkCornerPathEffect.h"
-#include "SkDashPathEffect.h"
+#include "../../src/effects/SkDashImpl.h"
 #include "SkDiscretePathEffect.h"
 #include "SkDisplacementMapEffect.h"
 #include "SkDropShadowImageFilter.h"
 #include "../../src/effects/SkEmbossMaskFilter.h"
-#include "SkGaussianEdgeShader.h"
-#include "SkRRectsGaussianEdgeMaskFilter.h"
 #include "SkGradientShader.h"
 #include "SkHighContrastFilter.h"
 #include "SkImageSource.h"
@@ -43,6 +41,7 @@
 #include "SkPaintImageFilter.h"
 #include "SkPerlinNoiseShader.h"
 #include "SkPictureImageFilter.h"
+#include "SkRRectsGaussianEdgeMaskFilter.h"
 #include "SkTableColorFilter.h"
 #include "SkTileImageFilter.h"
 #include "SkXfermodeImageFilter.h"
@@ -88,16 +87,15 @@
     SkHighContrastFilter::InitializeFlattenables();
 
     // Shader
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader)
+    SkPerlinNoiseShader::InitializeFlattenables();
     SkGradientShader::InitializeFlattenables();
     SkLightingShader::InitializeFlattenables();
     SkNormalSource::InitializeFlattenables();
-    SkGaussianEdgeShader::InitializeFlattenables();
 
     // PathEffect
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArcToPathEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashImpl)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath1DPathEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLine2DPathEffect)
diff --git a/src/ports/SkImageEncoder_CG.cpp b/src/ports/SkImageEncoder_CG.cpp
index b3fd243..8c2a542 100644
--- a/src/ports/SkImageEncoder_CG.cpp
+++ b/src/ports/SkImageEncoder_CG.cpp
@@ -89,9 +89,10 @@
             // <Error>: CGImageDestinationFinalize image destination does not have enough images
             // So instead we copy to 8888.
             if (bm.colorType() == kARGB_4444_SkColorType) {
-                SkBitmap bitmap8888;
-                bm.copyTo(&bitmap8888, kN32_SkColorType);
-                bm.swap(bitmap8888);
+                SkBitmap bitmapN32;
+                bitmapN32.allocPixels(bm.info().makeColorType(kN32_SkColorType));
+                bm.readPixels(bitmapN32.info(), bitmapN32.getPixels(), bitmapN32.rowBytes(), 0, 0);
+                bm.swap(bitmapN32);
             }
             type = kUTTypePNG;
             break;
diff --git a/src/ports/SkImageEncoder_WIC.cpp b/src/ports/SkImageEncoder_WIC.cpp
index 6d355c1..1ae9257 100644
--- a/src/ports/SkImageEncoder_WIC.cpp
+++ b/src/ports/SkImageEncoder_WIC.cpp
@@ -70,7 +70,9 @@
 
     // First convert to BGRA if necessary.
     SkBitmap bitmap;
-    if (!bitmapOrig.copyTo(&bitmap, kBGRA_8888_SkColorType)) {
+    if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) ||
+        !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0))
+    {
         return false;
     }
 
diff --git a/src/ports/SkImageGeneratorCG.cpp b/src/ports/SkImageGeneratorCG.cpp
index d2e6436..a2fe6a4 100644
--- a/src/ports/SkImageGeneratorCG.cpp
+++ b/src/ports/SkImageGeneratorCG.cpp
@@ -75,12 +75,12 @@
     , fData(SkRef(data))
 {}
 
-SkData* SkImageGeneratorCG::onRefEncodedData(GrContext* ctx) {
+SkData* SkImageGeneratorCG::onRefEncodedData() {
     return SkRef(fData.get());
 }
 
 bool SkImageGeneratorCG::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-        SkPMColor ctable[], int* ctableCount) {
+        const Options&) {
     if (kN32_SkColorType != info.colorType()) {
         // FIXME: Support other colorTypes.
         return false;
diff --git a/src/ports/SkImageGeneratorCG.h b/src/ports/SkImageGeneratorCG.h
index 9ecfe1b..65300a6 100644
--- a/src/ports/SkImageGeneratorCG.h
+++ b/src/ports/SkImageGeneratorCG.h
@@ -22,10 +22,10 @@
     static SkImageGenerator* NewFromEncodedCG(SkData* data);
 
 protected:
-    SkData* onRefEncodedData(GrContext* ctx) override;
+    SkData* onRefEncodedData() override;
 
-    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
-            int* ctableCount) override;
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&)
+    override;
 
 private:
     /*
diff --git a/src/ports/SkImageGeneratorWIC.cpp b/src/ports/SkImageGeneratorWIC.cpp
index 7a36785..e69b2ee 100644
--- a/src/ports/SkImageGeneratorWIC.cpp
+++ b/src/ports/SkImageGeneratorWIC.cpp
@@ -132,12 +132,12 @@
     , fData(SkRef(data))
 {}
 
-SkData* SkImageGeneratorWIC::onRefEncodedData(GrContext* ctx) {
+SkData* SkImageGeneratorWIC::onRefEncodedData() {
     return SkRef(fData.get());
 }
 
 bool SkImageGeneratorWIC::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-        SkPMColor ctable[], int* ctableCount) {
+        const Options&) {
     if (kN32_SkColorType != info.colorType()) {
         return false;
     }
diff --git a/src/ports/SkImageGeneratorWIC.h b/src/ports/SkImageGeneratorWIC.h
index eb65e0b..4770ee2 100644
--- a/src/ports/SkImageGeneratorWIC.h
+++ b/src/ports/SkImageGeneratorWIC.h
@@ -39,10 +39,10 @@
     static SkImageGenerator* NewFromEncodedWIC(SkData* data);
 
 protected:
-    SkData* onRefEncodedData(GrContext* ctx) override;
+    SkData* onRefEncodedData() override;
 
-    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
-            int* ctableCount) override;
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&)
+    override;
 
 private:
     /*
diff --git a/src/ports/SkOSFile_posix.cpp b/src/ports/SkOSFile_posix.cpp
index 4002824..48b5b95 100644
--- a/src/ports/SkOSFile_posix.cpp
+++ b/src/ports/SkOSFile_posix.cpp
@@ -19,25 +19,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-size_t sk_fgetsize(FILE* f) {
-    int fd = fileno(f);
-    if (fd < 0) {
-        return 0;
-    }
-
-    struct stat status;
-    if (0 != fstat(fd, &status)) {
-        return 0;
-    }
-    if (!S_ISREG(status.st_mode)) {
-        return 0;
-    }
-    if (!SkTFitsIn<size_t>(status.st_size)) {
-        return 0;
-    }
-    return static_cast<size_t>(status.st_size);
-}
-
 bool sk_exists(const char *path, SkFILE_Flags flags) {
     int mode = F_OK;
     if (flags & kRead_SkFILE_Flag) {
diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
index e79d87f..68c2d3d 100644
--- a/src/ports/SkOSFile_stdio.cpp
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -87,6 +87,24 @@
     return file;
 }
 
+size_t sk_fgetsize(FILE* f) {
+    SkASSERT(f);
+
+    long curr = ftell(f); // remember where we are
+    if (curr < 0) {
+        return 0;
+    }
+
+    fseek(f, 0, SEEK_END); // go to the end
+    long size = ftell(f); // record the size
+    if (size < 0) {
+        size = 0;
+    }
+
+    fseek(f, curr, SEEK_SET); // go back to our prev location
+    return size;
+}
+
 size_t sk_fwrite(const void* buffer, size_t byteCount, FILE* f) {
     SkASSERT(f);
     return fwrite(buffer, 1, byteCount, f);
diff --git a/src/ports/SkOSFile_win.cpp b/src/ports/SkOSFile_win.cpp
index e66bcb8..7e194cf 100644
--- a/src/ports/SkOSFile_win.cpp
+++ b/src/ports/SkOSFile_win.cpp
@@ -17,27 +17,6 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
-size_t sk_fgetsize(FILE* f) {
-    int fileno = sk_fileno(f);
-    if (fileno < 0) {
-        return 0;
-    }
-
-    HANDLE file = (HANDLE)_get_osfhandle(fileno);
-    if (INVALID_HANDLE_VALUE == file) {
-        return 0;
-    }
-
-    LARGE_INTEGER fileSize;
-    if (0 == GetFileSizeEx(file, &fileSize)) {
-        return 0;
-    }
-    if (!SkTFitsIn<size_t>(fileSize.QuadPart)) {
-        return 0;
-    }
-    return static_cast<size_t>(fileSize.QuadPart);
-}
-
 bool sk_exists(const char *path, SkFILE_Flags flags) {
     int mode = 0; // existence
     if (flags & kRead_SkFILE_Flag) {
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index 1fc067e..d1278b1 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -46,7 +46,7 @@
     return SkMask::kLCD16_Format == rec.fMaskFormat;
 }
 
-static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) {
+static bool is_hinted(DWriteFontTypeface* typeface) {
     SkAutoExclusive l(DWriteFactoryMutex);
     AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
     if (!maxp.fExists) {
@@ -58,22 +58,17 @@
     if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
         return false;
     }
-
-    if (0 == maxp->version.tt.maxSizeOfInstructions) {
-        // No hints.
-        return false;
-    }
-
-    AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
-    return !gasp.fExists;
+    return (0 != maxp->version.tt.maxSizeOfInstructions);
 }
 
 /** A GaspRange is inclusive, [min, max]. */
 struct GaspRange {
     using Behavior = SkOTTableGridAndScanProcedure::GaspRange::behavior;
-    GaspRange(int min, int max, Behavior flags) : fMin(min), fMax(max), fFlags(flags) { }
+    GaspRange(int min, int max, int version, Behavior flags)
+        : fMin(min), fMax(max), fVersion(version), fFlags(flags) { }
     int fMin;
     int fMax;
+    int fVersion;
     Behavior fFlags;
 };
 
@@ -107,6 +102,7 @@
         if (minPPEM < size && size <= maxPPEM) {
             range->fMin = minPPEM + 1;
             range->fMax = maxPPEM;
+            range->fVersion = SkEndian_SwapBE16(gasp->version);
             range->fFlags = rangeTable->flags;
             return true;
         }
@@ -121,14 +117,6 @@
     return flags.raw.value == GaspRange::Behavior::Raw::GridfitMask;
 }
 
-/** If the rendering mode for the specified 'size' sets SymmetricSmoothing, return true. */
-static bool gasp_allows_cleartype_symmetric(GaspRange::Behavior flags) {
-#ifdef SK_IGNORE_DIRECTWRITE_GASP_FIX
-    return true;
-#endif
-    return flags.field.SymmetricSmoothing;
-}
-
 static bool has_bitmap_strike(DWriteFontTypeface* typeface, GaspRange range) {
     SkAutoExclusive l(DWriteFactoryMutex);
     {
@@ -243,9 +231,7 @@
     // horizontal glyphs and the subpixel flag should not affect glyph shapes.
 
     SkVector scale;
-    SkMatrix GsA;
-    fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
-                         &scale, &fSkXform, &GsA, &fG_inv);
+    fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &fSkXform);
 
     fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
     fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
@@ -254,13 +240,6 @@
     fXform.dx = 0;
     fXform.dy = 0;
 
-    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
-    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
-    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
-    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
-    fGsA.dx = 0;
-    fGsA.dy = 0;
-
     // realTextSize is the actual device size we want (as opposed to the size the user requested).
     // gdiTextSize is the size we request when GDI compatible.
     // If the scale is negative, this means the matrix will do the flip anyway.
@@ -278,10 +257,10 @@
         // When embedded bitmaps are requested, treat the entire range like
         // a bitmap strike if the range is gridfit only and contains a bitmap.
         int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
-        GaspRange range(bitmapPPEM, bitmapPPEM, GaspRange::Behavior());
+        GaspRange range(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
         if (get_gasp_range(typeface, bitmapPPEM, &range)) {
             if (!is_gridfit_only(range.fFlags)) {
-                range = GaspRange(bitmapPPEM, bitmapPPEM, GaspRange::Behavior());
+                range = GaspRange(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
             }
         }
         treatLikeBitmap = has_bitmap_strike(typeface, range);
@@ -289,6 +268,8 @@
         axisAlignedBitmap = is_axis_aligned(fRec);
     }
 
+    GaspRange range(0, 0xFFFF, 0, GaspRange::Behavior());
+
     // If the user requested aliased, do so with aliased compatible metrics.
     if (SkMask::kBW_Format == fRec.fMaskFormat) {
         fTextSizeRender = gdiTextSize;
@@ -315,25 +296,38 @@
         fTextSizeMeasure = gdiTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
 
-    // Fonts that have hints but no gasp table get non-symmetric rendering.
-    // Usually such fonts have low quality hints which were never tested
-    // with anything but GDI ClearType classic. Such fonts often rely on
-    // drop out control in the y direction in order to be legible.
-    } else if (is_hinted_without_gasp(typeface)) {
-        fTextSizeRender = gdiTextSize;
-        fRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
+    // If the font has a gasp table version 1, use it to determine symmetric rendering.
+    } else if (get_gasp_range(typeface, SkScalarRoundToInt(gdiTextSize), &range) &&
+               range.fVersion >= 1)
+    {
+        fTextSizeRender = realTextSize;
+        fRenderingMode = range.fFlags.field.SymmetricSmoothing
+                       ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
+                       : DWRITE_RENDERING_MODE_NATURAL;
         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
 
-    // The normal case is to use natural symmetric rendering (if permitted) and linear metrics.
-    } else {
+    // If the requested size is above 20px or there are no bytecode hints, use symmetric rendering.
+    } else if (realTextSize > SkIntToScalar(20) || !is_hinted(typeface)) {
         fTextSizeRender = realTextSize;
-        GaspRange range(0, 0xFFFF, GaspRange::Behavior());
-        get_gasp_range(typeface, SkScalarTruncToInt(fTextSizeRender), &range);
-        fRenderingMode = gasp_allows_cleartype_symmetric(range.fFlags)
-                       ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
-                       : DWRITE_RENDERING_MODE_NATURAL;
+        fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
+        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+        fTextSizeMeasure = realTextSize;
+        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
+
+    // Fonts with hints, no gasp or gasp version 0, and below 20px get non-symmetric rendering.
+    // Often such fonts have hints which were only tested with GDI ClearType classic.
+    // Some of these fonts rely on drop out control in the y direction in order to be legible.
+    // Tenor Sans
+    //    https://fonts.google.com/specimen/Tenor+Sans
+    // Gill Sans W04
+    //    https://cdn.leagueoflegends.com/lolkit/1.1.9/resources/fonts/gill-sans-w04-book.woff
+    //    https://na.leagueoflegends.com/en/news/game-updates/patch/patch-410-notes
+    // See https://crbug.com/385897
+    } else {
+        fTextSizeRender = gdiTextSize;
+        fRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
@@ -341,7 +335,6 @@
 
     // DirectWrite2 allows for grayscale hinting.
     fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE;
-#ifndef SK_IGNORE_DW_GRAY_FIX
     if (typeface->fFactory2 && typeface->fDWriteFontFace2 &&
         SkMask::kA8_Format == fRec.fMaskFormat &&
         !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag))
@@ -350,7 +343,6 @@
         fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
         fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE;
     }
-#endif
 
     // DirectWrite2 allows hinting to be disabled.
     fGridFitMode = DWRITE_GRID_FIT_MODE_ENABLED;
@@ -404,7 +396,9 @@
         HRVM(this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
                  fTextSizeMeasure,
                  1.0f, // pixelsPerDip
-                 &fGsA,
+                 // This parameter does not act like the lpmat2 parameter to GetGlyphOutlineW.
+                 // If it did then GsA here and G_inv below to mapVectors.
+                 nullptr,
                  DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
                  &glyphId, 1,
                  &gm),
@@ -422,20 +416,18 @@
     }
     SkScalar advanceX = fTextSizeMeasure * gm.advanceWidth / dwfm.designUnitsPerEm;
 
-    SkVector vecs[1] = { { advanceX, 0 } };
+    SkVector advance = { advanceX, 0 };
     if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
         DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
     {
         // DirectWrite produced 'compatible' metrics, but while close,
         // the end result is not always an integer as it would be with GDI.
-        vecs[0].fX = SkScalarRoundToScalar(advanceX);
-        fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
-    } else {
-        fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
+        advance.fX = SkScalarRoundToScalar(advance.fX);
     }
+    fSkXform.mapVectors(&advance, 1);
 
-    glyph->fAdvanceX = SkScalarToFloat(vecs[0].fX);
-    glyph->fAdvanceY = SkScalarToFloat(vecs[0].fY);
+    glyph->fAdvanceX = SkScalarToFloat(advance.fX);
+    glyph->fAdvanceY = SkScalarToFloat(advance.fY);
 }
 
 HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph,
diff --git a/src/ports/SkScalerContext_win_dw.h b/src/ports/SkScalerContext_win_dw.h
index f186ea5..9b0f244 100644
--- a/src/ports/SkScalerContext_win_dw.h
+++ b/src/ports/SkScalerContext_win_dw.h
@@ -60,14 +60,6 @@
     SkMatrix fSkXform;
     /** The total matrix without the text height scale. */
     DWRITE_MATRIX fXform;
-    /** The non-rotational part of total matrix without the text height scale.
-     *  This is used to find the magnitude of gdi compatible advances.
-     */
-    DWRITE_MATRIX fGsA;
-    /** The inverse of the rotational part of the total matrix.
-     *  This is used to find the direction of gdi compatible advances.
-     */
-    SkMatrix fG_inv;
     /** The text size to render with. */
     SkScalar fTextSizeRender;
     /** The text size to measure with. */
diff --git a/src/ports/SkTypeface_win_dw.cpp b/src/ports/SkTypeface_win_dw.cpp
index 7d18da9..fe20cc9 100644
--- a/src/ports/SkTypeface_win_dw.cpp
+++ b/src/ports/SkTypeface_win_dw.cpp
@@ -18,6 +18,7 @@
 #include "SkDWriteFontFileStream.h"
 #include "SkFontDescriptor.h"
 #include "SkFontStream.h"
+#include "SkOTTable_fvar.h"
 #include "SkOTTable_head.h"
 #include "SkOTTable_hhea.h"
 #include "SkOTTable_OS_2.h"
@@ -297,9 +298,11 @@
                                       const unsigned glyphCount,
                                       SkTDArray<SkUnichar>* glyphToUnicode) {
     //Do this like free type instead
-    SkAutoTMalloc<SkUnichar> glyphToUni(glyphCount);
+    SkAutoTMalloc<SkUnichar> glyphToUni(
+            (SkUnichar*)sk_calloc_throw(sizeof(SkUnichar) * glyphCount));
     int maxGlyph = -1;
-    for (UINT32 c = 0; c < 0x10FFFF; ++c) {
+    unsigned remainingGlyphCount = glyphCount;
+    for (UINT32 c = 0; c < 0x10FFFF && remainingGlyphCount != 0; ++c) {
         UINT16 glyph = 0;
         HRVM(fontFace->GetGlyphIndices(&c, 1, &glyph),
              "Failed to get glyph index.");
@@ -307,21 +310,18 @@
         if (glyph >= glyphCount) {
           return;
         }
-        if (0 < glyph) {
+        if (0 < glyph && glyphToUni[glyph] == 0) {
             maxGlyph = SkTMax(static_cast<int>(glyph), maxGlyph);
-            glyphToUni[glyph] = c;
+            glyphToUni[glyph] = c;  // Always use lowest-index unichar.
+            --remainingGlyphCount;
         }
     }
-
     SkTDArray<SkUnichar>(glyphToUni, maxGlyph + 1).swap(*glyphToUnicode);
 }
 
-SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics(
-        PerGlyphInfo perGlyphInfo,
-        const uint32_t* glyphIDs,
-        uint32_t glyphIDsCount) const {
+std::unique_ptr<SkAdvancedTypefaceMetrics> DWriteFontTypeface::onGetAdvancedMetrics() const {
 
-    SkAdvancedTypefaceMetrics* info = nullptr;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
 
     HRESULT hr = S_OK;
 
@@ -330,7 +330,7 @@
     DWRITE_FONT_METRICS dwfm;
     fDWriteFontFace->GetMetrics(&dwfm);
 
-    info = new SkAdvancedTypefaceMetrics;
+    info.reset(new SkAdvancedTypefaceMetrics);
 
     info->fAscent = SkToS16(dwfm.ascent);
     info->fDescent = SkToS16(dwfm.descent);
@@ -350,9 +350,7 @@
 
     hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName);
 
-    if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
-        populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
-    }
+    populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
 
     DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType();
     if (fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE &&
@@ -374,6 +372,13 @@
         return info;
     }
 
+    // There are versions of DirectWrite which support named instances for system variation fonts,
+    // but no means to indicate that such a typeface is a variation.
+    AutoTDWriteTable<SkOTTableFontVariations> fvarTable(fDWriteFontFace.get());
+    if (fvarTable.fExists) {
+        info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
+    }
+
     //There exist CJK fonts which set the IsFixedPitch and Monospace bits,
     //but have full width, latin half-width, and half-width kana.
     bool fixedWidth = (postTable->isFixedPitch &&
diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h
index d7c73e2..7abbde5 100644
--- a/src/ports/SkTypeface_win_dw.h
+++ b/src/ports/SkTypeface_win_dw.h
@@ -104,8 +104,7 @@
     SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
                                            const SkDescriptor*) const override;
     void onFilterRec(SkScalerContextRec*) const override;
-    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo, const uint32_t*, uint32_t) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
     int onCharsToGlyphs(const void* chars, Encoding encoding,
                         uint16_t glyphs[], int glyphCount) const override;
diff --git a/src/sfnt/SkOTTable_fvar.h b/src/sfnt/SkOTTable_fvar.h
new file mode 100644
index 0000000..5d6ffbe
--- /dev/null
+++ b/src/sfnt/SkOTTable_fvar.h
@@ -0,0 +1,56 @@
+/*
+ * 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 SkOTTable_fvar_DEFINED
+#define SkOTTable_fvar_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableFontVariations {
+    static const SK_OT_CHAR TAG0 = 'f';
+    static const SK_OT_CHAR TAG1 = 'v';
+    static const SK_OT_CHAR TAG2 = 'a';
+    static const SK_OT_CHAR TAG3 = 'r';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableFontVariations>::value;
+
+    SK_OT_USHORT majorVersion;
+    SK_OT_USHORT minorVersion;
+    SK_OT_USHORT offsetToAxesArray;
+    SK_OT_USHORT reserved;
+    SK_OT_USHORT axisCount;
+    SK_OT_USHORT axisSize; // Must be 0x0014 in v1.0
+    SK_OT_USHORT instanceCount;
+    SK_OT_USHORT instanceSize; // Must be axisCount * sizeof(Fixed) + (4 | 6)
+
+    struct VariationAxisRecord {
+        SK_OT_ULONG axisTag;
+        SK_OT_Fixed minValue;
+        SK_OT_Fixed defaultValue;
+        SK_OT_Fixed maxValue;
+        SK_OT_USHORT flags; // Must be 0
+        SK_OT_USHORT axisNameID;
+    }; // axes[axisCount];
+
+    template <size_t AxisCount> struct InstanceRecord {
+        SK_OT_USHORT subfamilyNameID;
+        SK_OT_USHORT flags; // Must be 0
+        SK_OT_Fixed coordinates[AxisCount];
+        SK_OT_USHORT postScriptNameID;
+    }; // instances[instanceCount];
+};
+
+#pragma pack(pop)
+
+
+#include <stddef.h>
+static_assert(offsetof(SkOTTableFontVariations, instanceSize) == 14, "SkOTTableFontVariations_instanceSize_not_at_14");
+static_assert(sizeof(SkOTTableFontVariations) == 16, "sizeof_SkOTTableFontVariations_not_16");
+
+#endif
diff --git a/src/shaders/SkBitmapProcShader.cpp b/src/shaders/SkBitmapProcShader.cpp
new file mode 100644
index 0000000..5410447
--- /dev/null
+++ b/src/shaders/SkBitmapProcShader.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapProcShader.h"
+
+#include "SkArenaAlloc.h"
+#include "SkBitmapProcState.h"
+#include "SkBitmapProvider.h"
+#include "SkXfermodePriv.h"
+
+static bool only_scale_and_translate(const SkMatrix& matrix) {
+    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+    return (matrix.getType() & ~mask) == 0;
+}
+
+class BitmapProcInfoContext : public SkShaderBase::Context {
+public:
+    // The info has been allocated elsewhere, but we are responsible for calling its destructor.
+    BitmapProcInfoContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec,
+                            SkBitmapProcInfo* info)
+        : INHERITED(shader, rec)
+        , fInfo(info)
+    {
+        fFlags = 0;
+        if (fInfo->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) {
+            fFlags |= SkShaderBase::kOpaqueAlpha_Flag;
+        }
+
+        if (1 == fInfo->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) {
+            fFlags |= SkShaderBase::kConstInY32_Flag;
+        }
+    }
+
+    uint32_t getFlags() const override { return fFlags; }
+
+private:
+    SkBitmapProcInfo*   fInfo;
+    uint32_t            fFlags;
+
+    typedef SkShaderBase::Context INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class BitmapProcShaderContext : public BitmapProcInfoContext {
+public:
+    BitmapProcShaderContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec,
+                            SkBitmapProcState* state)
+        : INHERITED(shader, rec, state)
+        , fState(state)
+    {}
+
+    void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
+        const SkBitmapProcState& state = *fState;
+        if (state.getShaderProc32()) {
+            state.getShaderProc32()(&state, x, y, dstC, count);
+            return;
+        }
+
+        const int BUF_MAX = 128;
+        uint32_t buffer[BUF_MAX];
+        SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
+        SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
+        const int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
+
+        SkASSERT(state.fPixmap.addr());
+
+        for (;;) {
+            int n = SkTMin(count, max);
+            SkASSERT(n > 0 && n < BUF_MAX*2);
+            mproc(state, buffer, n, x, y);
+            sproc(state, buffer, n, dstC);
+
+            if ((count -= n) == 0) {
+                break;
+            }
+            SkASSERT(count > 0);
+            x += n;
+            dstC += n;
+        }
+    }
+
+    ShadeProc asAShadeProc(void** ctx) override {
+        if (fState->getShaderProc32()) {
+            *ctx = fState;
+            return (ShadeProc)fState->getShaderProc32();
+        }
+        return nullptr;
+    }
+
+private:
+    SkBitmapProcState*  fState;
+
+    typedef BitmapProcInfoContext INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkLinearBitmapPipeline.h"
+#include "SkPM4f.h"
+
+class LinearPipelineContext : public BitmapProcInfoContext {
+public:
+    LinearPipelineContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec,
+                          SkBitmapProcInfo* info, SkArenaAlloc* alloc)
+        : INHERITED(shader, rec, info), fAllocator{alloc}
+    {
+        // Save things off in case we need to build a blitter pipeline.
+        fSrcPixmap = info->fPixmap;
+        fAlpha = SkColorGetA(info->fPaintColor) / 255.0f;
+        fFilterQuality = info->fFilterQuality;
+        fMatrixTypeMask = info->fRealInvMatrix.getType();
+
+        fShaderPipeline = alloc->make<SkLinearBitmapPipeline>(
+            info->fRealInvMatrix, info->fFilterQuality,
+            info->fTileModeX, info->fTileModeY,
+            info->fPaintColor,
+            info->fPixmap,
+            fAllocator);
+
+        // To implement the old shadeSpan entry-point, we need to efficiently convert our native
+        // floats into SkPMColor. The SkXfermode::D32Procs do exactly that.
+        //
+        fSrcModeProc = SkXfermode::GetD32Proc(SkBlendMode::kSrc, 0);
+    }
+
+    void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override {
+        fShaderPipeline->shadeSpan4f(x, y, dstC, count);
+    }
+
+    void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
+        const int N = 128;
+        SkPM4f  tmp[N];
+
+        while (count > 0) {
+            const int n = SkTMin(count, N);
+            fShaderPipeline->shadeSpan4f(x, y, tmp, n);
+            fSrcModeProc(SkBlendMode::kSrc, dstC, tmp, n, nullptr);
+            dstC += n;
+            x += n;
+            count -= n;
+        }
+    }
+
+private:
+    // Store the allocator from the context creation incase we are asked to build a blitter.
+    SkArenaAlloc*           fAllocator;
+    SkLinearBitmapPipeline* fShaderPipeline;
+    SkXfermode::D32Proc     fSrcModeProc;
+    SkPixmap                fSrcPixmap;
+    float                   fAlpha;
+    SkMatrix::TypeMask      fMatrixTypeMask;
+    SkFilterQuality         fFilterQuality;
+
+    typedef BitmapProcInfoContext INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool choose_linear_pipeline(const SkShaderBase::ContextRec& rec, const SkImageInfo& srcInfo) {
+    // If we get here, we can reasonably use either context, respect the caller's preference
+    //
+    bool needsPremul = srcInfo.alphaType() == kUnpremul_SkAlphaType;
+    bool needsSwizzle = srcInfo.bytesPerPixel() == 4 && srcInfo.colorType() != kN32_SkColorType;
+    return SkShaderBase::ContextRec::kPM4f_DstType == rec.fPreferredDstType
+           || needsPremul || needsSwizzle;
+}
+
+size_t SkBitmapProcLegacyShader::ContextSize(const ContextRec& rec, const SkImageInfo& srcInfo) {
+    size_t size0 = sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState);
+    size_t size1 = sizeof(LinearPipelineContext) + sizeof(SkBitmapProcInfo);
+    size_t s = SkTMax(size0, size1);
+    return s;
+}
+
+SkShaderBase::Context* SkBitmapProcLegacyShader::MakeContext(
+    const SkShaderBase& shader, TileMode tmx, TileMode tmy,
+    const SkBitmapProvider& provider, const ContextRec& rec, SkArenaAlloc* alloc)
+{
+    SkMatrix totalInverse;
+    // Do this first, so we know the matrix can be inverted.
+    if (!shader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &totalInverse)) {
+        return nullptr;
+    }
+
+    // Decide if we can/want to use the new linear pipeline
+    bool useLinearPipeline = choose_linear_pipeline(rec, provider.info());
+
+    if (useLinearPipeline) {
+        SkBitmapProcInfo* info = alloc->make<SkBitmapProcInfo>(provider, tmx, tmy);
+        if (!info->init(totalInverse, *rec.fPaint)) {
+            return nullptr;
+        }
+
+        return alloc->make<LinearPipelineContext>(shader, rec, info, alloc);
+    } else {
+        SkBitmapProcState* state = alloc->make<SkBitmapProcState>(provider, tmx, tmy);
+        if (!state->setup(totalInverse, *rec.fPaint)) {
+            return nullptr;
+        }
+        return alloc->make<BitmapProcShaderContext>(shader, rec, state);
+    }
+}
diff --git a/src/shaders/SkBitmapProcShader.h b/src/shaders/SkBitmapProcShader.h
new file mode 100644
index 0000000..2a2599c
--- /dev/null
+++ b/src/shaders/SkBitmapProcShader.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkBitmapProcShader_DEFINED
+#define SkBitmapProcShader_DEFINED
+
+#include "SkImagePriv.h"
+#include "SkShaderBase.h"
+
+class SkBitmapProvider;
+
+class SkBitmapProcLegacyShader : public SkShaderBase {
+private:
+    friend class SkImageShader;
+
+    static size_t ContextSize(const ContextRec&, const SkImageInfo& srcInfo);
+    static Context* MakeContext(const SkShaderBase&, TileMode tmx, TileMode tmy,
+                                const SkBitmapProvider&, const ContextRec&, SkArenaAlloc* alloc);
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkColorFilterShader.cpp b/src/shaders/SkColorFilterShader.cpp
new file mode 100644
index 0000000..b7d6b15
--- /dev/null
+++ b/src/shaders/SkColorFilterShader.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkArenaAlloc.h"
+#include "SkColorFilterShader.h"
+#include "SkColorSpaceXformer.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkShader.h"
+#include "SkString.h"
+
+#if SK_SUPPORT_GPU
+#include "GrFragmentProcessor.h"
+#endif
+
+SkColorFilterShader::SkColorFilterShader(sk_sp<SkShader> shader, sk_sp<SkColorFilter> filter)
+    : fShader(std::move(shader))
+    , fFilter(std::move(filter))
+{
+    SkASSERT(fShader);
+    SkASSERT(fFilter);
+}
+
+sk_sp<SkFlattenable> SkColorFilterShader::CreateProc(SkReadBuffer& buffer) {
+    auto shader = buffer.readShader();
+    auto filter = buffer.readColorFilter();
+    if (!shader || !filter) {
+        return nullptr;
+    }
+    return sk_make_sp<SkColorFilterShader>(shader, filter);
+}
+
+void SkColorFilterShader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeFlattenable(fShader.get());
+    buffer.writeFlattenable(fFilter.get());
+}
+
+uint32_t SkColorFilterShader::FilterShaderContext::getFlags() const {
+    const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader);
+
+    uint32_t shaderF = fShaderContext->getFlags();
+    uint32_t filterF = filterShader.fFilter->getFlags();
+
+    // If the filter does not support a given feature, but sure to clear the corresponding flag
+    // in the shader flags.
+    //
+    if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) {
+        shaderF &= ~kOpaqueAlpha_Flag;
+    }
+    return shaderF;
+}
+
+bool SkColorFilterShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS,
+                                         SkArenaAlloc* alloc, const SkMatrix& ctm,
+                                         const SkPaint& paint, const SkMatrix* localM) const {
+    if (!as_SB(fShader)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) {
+        return false;
+    }
+    fFilter->appendStages(pipeline, dstCS, alloc, fShader->isOpaque());
+    return true;
+}
+
+SkShaderBase::Context* SkColorFilterShader::onMakeContext(const ContextRec& rec,
+                                                          SkArenaAlloc* alloc) const {
+    auto* shaderContext = as_SB(fShader)->makeContext(rec, alloc);
+    if (nullptr == shaderContext) {
+        return nullptr;
+    }
+    return alloc->make<FilterShaderContext>(*this, shaderContext, rec);
+}
+
+sk_sp<SkShader> SkColorFilterShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return xformer->apply(fShader.get())->makeWithColorFilter(xformer->apply(fFilter.get()));
+}
+
+SkColorFilterShader::FilterShaderContext::FilterShaderContext(
+                                                         const SkColorFilterShader& filterShader,
+                                                         SkShaderBase::Context* shaderContext,
+                                                         const ContextRec& rec)
+    : INHERITED(filterShader, rec)
+    , fShaderContext(shaderContext)
+{}
+
+void SkColorFilterShader::FilterShaderContext::shadeSpan(int x, int y, SkPMColor result[],
+                                                         int count) {
+    const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader);
+
+    fShaderContext->shadeSpan(x, y, result, count);
+    filterShader.fFilter->filterSpan(result, count, result);
+}
+
+#include "SkRasterPipeline.h"
+void SkColorFilterShader::FilterShaderContext::shadeSpan4f(int x, int y, SkPM4f result[],
+                                                           int count) {
+    const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader);
+
+    fShaderContext->shadeSpan4f(x, y, result, count);
+
+    // now apply the filter
+
+    SkSTArenaAlloc<128> alloc;
+    SkRasterPipeline    pipeline(&alloc);
+
+    const SkPM4f* src = result;
+    pipeline.append(SkRasterPipeline::load_f32, &src);
+    filterShader.fFilter->appendStages(&pipeline, nullptr, &alloc, filterShader.isOpaque());
+    SkPM4f* dst = result;
+    pipeline.append(SkRasterPipeline::store_f32, &dst);
+    pipeline.run(0,y, count);
+}
+
+#if SK_SUPPORT_GPU
+/////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkColorFilterShader::asFragmentProcessor(const AsFPArgs& args) const {
+
+    sk_sp<GrFragmentProcessor> fp1(as_SB(fShader)->asFragmentProcessor(args));
+    if (!fp1) {
+        return nullptr;
+    }
+
+    sk_sp<GrFragmentProcessor> fp2(fFilter->asFragmentProcessor(args.fContext,
+                                                                args.fDstColorSpace));
+    if (!fp2) {
+        return fp1;
+    }
+
+    sk_sp<GrFragmentProcessor> fpSeries[] = { std::move(fp1), std::move(fp2) };
+    return GrFragmentProcessor::RunInSeries(fpSeries, 2);
+}
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkColorFilterShader::toString(SkString* str) const {
+    str->append("SkColorFilterShader: (");
+
+    str->append("Shader: ");
+    as_SB(fShader)->toString(str);
+    str->append(" Filter: ");
+    // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkShader> SkShader::makeWithColorFilter(sk_sp<SkColorFilter> filter) const {
+    SkShader* base = const_cast<SkShader*>(this);
+    if (!filter) {
+        return sk_ref_sp(base);
+    }
+    return sk_make_sp<SkColorFilterShader>(sk_ref_sp(base), filter);
+}
diff --git a/src/shaders/SkColorFilterShader.h b/src/shaders/SkColorFilterShader.h
new file mode 100644
index 0000000..6a3f65e
--- /dev/null
+++ b/src/shaders/SkColorFilterShader.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorFilterShader_DEFINED
+#define SkColorFilterShader_DEFINED
+
+#include "SkColorFilter.h"
+#include "SkShaderBase.h"
+
+class SkArenaAlloc;
+
+class SkColorFilterShader : public SkShaderBase {
+public:
+    SkColorFilterShader(sk_sp<SkShader> shader, sk_sp<SkColorFilter> filter);
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    class FilterShaderContext : public Context {
+    public:
+        // Takes ownership of shaderContext and calls its destructor.
+        FilterShaderContext(const SkColorFilterShader&, SkShaderBase::Context*, const ContextRec&);
+
+        uint32_t getFlags() const override;
+
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
+        void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
+
+        void set3DMask(const SkMask* mask) override {
+            // forward to our proxy
+            fShaderContext->set3DMask(mask);
+        }
+
+    private:
+        SkShaderBase::Context* fShaderContext;
+
+        typedef Context INHERITED;
+    };
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterShader)
+
+protected:
+    void flatten(SkWriteBuffer&) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc* alloc) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*,
+                        const SkMatrix&, const SkPaint&, const SkMatrix* localM) const override;
+
+private:
+    sk_sp<SkShader>      fShader;
+    sk_sp<SkColorFilter> fFilter;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkColorShader.cpp b/src/shaders/SkColorShader.cpp
new file mode 100644
index 0000000..7b6d0a9
--- /dev/null
+++ b/src/shaders/SkColorShader.cpp
@@ -0,0 +1,274 @@
+/*
+ * 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 "SkArenaAlloc.h"
+#include "SkColorShader.h"
+#include "SkColorSpace.h"
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
+#include "SkReadBuffer.h"
+#include "SkUtils.h"
+
+SkColorShader::SkColorShader(SkColor c) : fColor(c) {}
+
+bool SkColorShader::isOpaque() const {
+    return SkColorGetA(fColor) == 255;
+}
+
+sk_sp<SkFlattenable> SkColorShader::CreateProc(SkReadBuffer& buffer) {
+    return sk_make_sp<SkColorShader>(buffer.readColor());
+}
+
+void SkColorShader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeColor(fColor);
+}
+
+uint32_t SkColorShader::ColorShaderContext::getFlags() const {
+    return fFlags;
+}
+
+SkShaderBase::Context* SkColorShader::onMakeContext(const ContextRec& rec,
+                                                    SkArenaAlloc* alloc) const {
+    return alloc->make<ColorShaderContext>(*this, rec);
+}
+
+SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shader,
+                                                      const ContextRec& rec)
+    : INHERITED(shader, rec)
+{
+    SkColor color = shader.fColor;
+    unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha()));
+
+    unsigned r = SkColorGetR(color);
+    unsigned g = SkColorGetG(color);
+    unsigned b = SkColorGetB(color);
+
+    if (a != 255) {
+        r = SkMulDiv255Round(r, a);
+        g = SkMulDiv255Round(g, a);
+        b = SkMulDiv255Round(b, a);
+    }
+    fPMColor = SkPackARGB32(a, r, g, b);
+
+    SkColor4f c4 = SkColor4f::FromColor(shader.fColor);
+    c4.fA *= rec.fPaint->getAlpha() / 255.0f;
+    fPM4f = c4.premul();
+
+    fFlags = kConstInY32_Flag;
+    if (255 == a) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+}
+
+void SkColorShader::ColorShaderContext::shadeSpan(int x, int y, SkPMColor span[], int count) {
+    sk_memset32(span, fPMColor, count);
+}
+
+void SkColorShader::ColorShaderContext::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    memset(alpha, SkGetPackedA32(fPMColor), count);
+}
+
+void SkColorShader::ColorShaderContext::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
+    for (int i = 0; i < count; ++i) {
+        span[i] = fPM4f;
+    }
+}
+
+SkShader::GradientType SkColorShader::asAGradient(GradientInfo* info) const {
+    if (info) {
+        if (info->fColors && info->fColorCount >= 1) {
+            info->fColors[0] = fColor;
+        }
+        info->fColorCount = 1;
+        info->fTileMode = SkShader::kRepeat_TileMode;
+    }
+    return kColor_GradientType;
+}
+
+#if SK_SUPPORT_GPU
+
+#include "SkGr.h"
+#include "effects/GrConstColorProcessor.h"
+sk_sp<GrFragmentProcessor> SkColorShader::asFragmentProcessor(const AsFPArgs& args) const {
+    GrColor4f color = SkColorToPremulGrColor4f(fColor, args.fDstColorSpace);
+    return GrConstColorProcessor::Make(color, GrConstColorProcessor::kModulateA_InputMode);
+}
+
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkColorShader::toString(SkString* str) const {
+    str->append("SkColorShader: (");
+
+    str->append("Color: ");
+    str->appendHex(fColor);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static unsigned unit_to_byte(float unit) {
+    SkASSERT(unit >= 0 && unit <= 1);
+    return (unsigned)(unit * 255 + 0.5);
+}
+
+static SkColor unit_to_skcolor(const SkColor4f& unit, SkColorSpace* cs) {
+    return SkColorSetARGB(unit_to_byte(unit.fA), unit_to_byte(unit.fR),
+                          unit_to_byte(unit.fG), unit_to_byte(unit.fB));
+}
+
+SkColor4Shader::SkColor4Shader(const SkColor4f& color, sk_sp<SkColorSpace> space)
+    : fColorSpace(std::move(space))
+    , fColor4(color)
+    , fCachedByteColor(unit_to_skcolor(color.pin(), space.get()))
+{}
+
+sk_sp<SkFlattenable> SkColor4Shader::CreateProc(SkReadBuffer& buffer) {
+    SkColor4f color;
+    buffer.readColor4f(&color);
+    if (buffer.readBool()) {
+        // TODO how do we unflatten colorspaces
+    }
+    return SkShader::MakeColorShader(color, nullptr);
+}
+
+void SkColor4Shader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeColor4f(fColor4);
+    buffer.writeBool(false);    // TODO how do we flatten colorspaces?
+}
+
+uint32_t SkColor4Shader::Color4Context::getFlags() const {
+    return fFlags;
+}
+
+SkShaderBase::Context* SkColor4Shader::onMakeContext(const ContextRec& rec,
+                                                     SkArenaAlloc* alloc) const {
+    return alloc->make<Color4Context>(*this, rec);
+}
+
+SkColor4Shader::Color4Context::Color4Context(const SkColor4Shader& shader,
+                                                      const ContextRec& rec)
+: INHERITED(shader, rec)
+{
+    SkColor color = shader.fCachedByteColor;
+    unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha()));
+
+    unsigned r = SkColorGetR(color);
+    unsigned g = SkColorGetG(color);
+    unsigned b = SkColorGetB(color);
+
+    if (a != 255) {
+        r = SkMulDiv255Round(r, a);
+        g = SkMulDiv255Round(g, a);
+        b = SkMulDiv255Round(b, a);
+    }
+    fPMColor = SkPackARGB32(a, r, g, b);
+
+    SkColor4f c4 = shader.fColor4;
+    c4.fA *= rec.fPaint->getAlpha() * (1 / 255.0f);
+    fPM4f = c4.premul();
+
+    fFlags = kConstInY32_Flag;
+    if (255 == a) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+}
+
+void SkColor4Shader::Color4Context::shadeSpan(int x, int y, SkPMColor span[], int count) {
+    sk_memset32(span, fPMColor, count);
+}
+
+void SkColor4Shader::Color4Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    memset(alpha, SkGetPackedA32(fPMColor), count);
+}
+
+void SkColor4Shader::Color4Context::shadeSpan4f(int x, int y, SkPM4f span[], int count) {
+    for (int i = 0; i < count; ++i) {
+        span[i] = fPM4f;
+    }
+}
+
+// TODO: do we need an updated version of this method for color4+colorspace?
+SkShader::GradientType SkColor4Shader::asAGradient(GradientInfo* info) const {
+    if (info) {
+        if (info->fColors && info->fColorCount >= 1) {
+            info->fColors[0] = fCachedByteColor;
+        }
+        info->fColorCount = 1;
+        info->fTileMode = SkShader::kRepeat_TileMode;
+    }
+    return kColor_GradientType;
+}
+
+#if SK_SUPPORT_GPU
+
+#include "SkGr.h"
+#include "effects/GrConstColorProcessor.h"
+#include "GrColorSpaceXform.h"
+sk_sp<GrFragmentProcessor> SkColor4Shader::asFragmentProcessor(const AsFPArgs& args) const {
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+                                                                       args.fDstColorSpace);
+    GrColor4f color = GrColor4f::FromSkColor4f(fColor4);
+    if (colorSpaceXform) {
+        color = colorSpaceXform->apply(color);
+    }
+    return GrConstColorProcessor::Make(color.premul(), GrConstColorProcessor::kModulateA_InputMode);
+}
+
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkColor4Shader::toString(SkString* str) const {
+    str->append("SkColor4Shader: (");
+
+    str->append("RGBA:");
+    for (int i = 0; i < 4; ++i) {
+        str->appendf(" %g", fColor4.vec()[i]);
+    }
+    str->append(" )");
+}
+#endif
+
+sk_sp<SkShader> SkColor4Shader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return SkShader::MakeColorShader(xformer->apply(fCachedByteColor));
+}
+
+sk_sp<SkShader> SkShader::MakeColorShader(const SkColor4f& color, sk_sp<SkColorSpace> space) {
+    if (!SkScalarsAreFinite(color.vec(), 4)) {
+        return nullptr;
+    }
+    return sk_make_sp<SkColor4Shader>(color, std::move(space));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkColorShader::onAppendStages(SkRasterPipeline* p,
+                                   SkColorSpace* dst,
+                                   SkArenaAlloc* scratch,
+                                   const SkMatrix&,
+                                   const SkPaint&,
+                                   const SkMatrix*) const {
+    auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
+    p->append(SkRasterPipeline::constant_color, color);
+    return true;
+}
+
+bool SkColor4Shader::onAppendStages(SkRasterPipeline* p,
+                                    SkColorSpace* dst,
+                                    SkArenaAlloc* scratch,
+                                    const SkMatrix&,
+                                    const SkPaint&,
+                                    const SkMatrix*) const {
+    auto color = scratch->make<SkPM4f>(to_colorspace(fColor4, fColorSpace.get(), dst).premul());
+    p->append(SkRasterPipeline::constant_color, color);
+    return true;
+}
diff --git a/src/shaders/SkColorShader.h b/src/shaders/SkColorShader.h
new file mode 100644
index 0000000..9690649
--- /dev/null
+++ b/src/shaders/SkColorShader.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorShader_DEFINED
+#define SkColorShader_DEFINED
+
+#include "SkColorSpaceXformer.h"
+#include "SkShaderBase.h"
+#include "SkPM4f.h"
+
+/** \class SkColorShader
+    A Shader that represents a single color. In general, this effect can be
+    accomplished by just using the color field on the paint, but if an
+    actual shader object is needed, this provides that feature.
+*/
+class SkColorShader : public SkShaderBase {
+public:
+    /** Create a ColorShader that ignores the color in the paint, and uses the
+        specified color. Note: like all shaders, at draw time the paint's alpha
+        will be respected, and is applied to the specified color.
+    */
+    explicit SkColorShader(SkColor c);
+
+    bool isOpaque() const override;
+    bool isConstant() const override { return true; }
+
+    class ColorShaderContext : public Context {
+    public:
+        ColorShaderContext(const SkColorShader& shader, const ContextRec&);
+
+        uint32_t getFlags() const override;
+        void shadeSpan(int x, int y, SkPMColor span[], int count) override;
+        void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override;
+        void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
+
+    private:
+        SkPM4f      fPM4f;
+        SkPMColor   fPMColor;
+        uint32_t    fFlags;
+
+        typedef Context INHERITED;
+    };
+
+    GradientType asAGradient(GradientInfo* info) const override;
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
+
+protected:
+    SkColorShader(SkReadBuffer&);
+    void flatten(SkWriteBuffer&) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override;
+
+    bool onAsLuminanceColor(SkColor* lum) const override {
+        *lum = fColor;
+        return true;
+    }
+
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                        const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
+
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return SkShader::MakeColorShader(xformer->apply(fColor));
+    }
+
+private:
+    SkColor fColor;
+
+    typedef SkShaderBase INHERITED;
+};
+
+class SkColor4Shader : public SkShaderBase {
+public:
+    SkColor4Shader(const SkColor4f&, sk_sp<SkColorSpace>);
+
+    bool isOpaque() const override {
+        return SkColorGetA(fCachedByteColor) == 255;
+    }
+    bool isConstant() const override { return true; }
+
+    class Color4Context : public Context {
+    public:
+        Color4Context(const SkColor4Shader& shader, const ContextRec&);
+
+        uint32_t getFlags() const override;
+        void shadeSpan(int x, int y, SkPMColor span[], int count) override;
+        void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override;
+        void shadeSpan4f(int x, int y, SkPM4f[], int count) override;
+
+    private:
+        SkPM4f      fPM4f;
+        SkPMColor   fPMColor;
+        uint32_t    fFlags;
+
+        typedef Context INHERITED;
+    };
+
+    GradientType asAGradient(GradientInfo* info) const override;
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
+
+protected:
+    SkColor4Shader(SkReadBuffer&);
+    void flatten(SkWriteBuffer&) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    bool onAsLuminanceColor(SkColor* lum) const override {
+        *lum = fCachedByteColor;
+        return true;
+    }
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                        const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
+
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
+private:
+    sk_sp<SkColorSpace> fColorSpace;
+    const SkColor4f     fColor4;
+    const SkColor       fCachedByteColor;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp
new file mode 100644
index 0000000..4455f49
--- /dev/null
+++ b/src/shaders/SkComposeShader.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkArenaAlloc.h"
+#include "SkBlendModePriv.h"
+#include "SkComposeShader.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkColorShader.h"
+#include "SkRasterPipeline.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkString.h"
+#include "../jumper/SkJumper.h"
+
+sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
+                                            SkBlendMode mode) {
+    if (!src || !dst) {
+        return nullptr;
+    }
+    if (SkBlendMode::kSrc == mode) {
+        return src;
+    }
+    if (SkBlendMode::kDst == mode) {
+        return dst;
+    }
+    return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
+    sk_sp<SkShader> shaderA(buffer.readShader());
+    sk_sp<SkShader> shaderB(buffer.readShader());
+    SkBlendMode mode;
+    if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) {
+        sk_sp<SkXfermode> xfer = buffer.readXfermode();
+        mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver;
+    } else {
+        mode = (SkBlendMode)buffer.read32();
+    }
+    if (!shaderA || !shaderB) {
+        return nullptr;
+    }
+    return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
+}
+
+void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeFlattenable(fShaderA.get());
+    buffer.writeFlattenable(fShaderB.get());
+    buffer.write32((int)fMode);
+}
+
+sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return SkShader::MakeComposeShader(xformer->apply(fShaderA.get()),
+                                       xformer->apply(fShaderB.get()), fMode);
+}
+
+bool SkComposeShader::asACompose(ComposeRec* rec) const {
+    if (rec) {
+        rec->fShaderA   = fShaderA.get();
+        rec->fShaderB   = fShaderB.get();
+        rec->fBlendMode = fMode;
+    }
+    return true;
+}
+
+bool SkComposeShader::isRasterPipelineOnly() const {
+    return true;
+}
+
+bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS,
+                                     SkArenaAlloc* alloc, const SkMatrix& ctm,
+                                     const SkPaint& paint, const SkMatrix* localM) const {
+    struct Storage {
+        float   fRGBA[4 * SkJumper_kMaxStride];
+        float   fAlpha;
+    };
+    auto storage = alloc->make<Storage>();
+
+    if (!as_SB(fShaderB)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // SRC
+        return false;
+    }
+    // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now
+    // since fShaderB will overwrite them.
+    pipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
+
+    if (!as_SB(fShaderA)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) {  // DST
+        return false;
+    }
+    // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode
+    // so we have to shuttle them. If we had a stage the would load_into_dst, then we could
+    // reverse the two shader invocations, and avoid this move...
+    pipeline->append(SkRasterPipeline::move_src_dst);
+    pipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
+
+    // Idea: should time this, and see if it helps to have custom versions of the overflow modes
+    //       that do their own clamping, avoiding the overhead of an extra stage.
+    SkBlendMode_AppendStages(fMode, pipeline);
+    if (SkBlendMode_CanOverflow(fMode)) {
+        pipeline->append(SkRasterPipeline::clamp_a);
+    }
+    return true;
+}
+
+#if SK_SUPPORT_GPU
+
+#include "effects/GrConstColorProcessor.h"
+#include "effects/GrXfermodeFragmentProcessor.h"
+
+/////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
+    switch (fMode) {
+        case SkBlendMode::kClear:
+            return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
+                                               GrConstColorProcessor::kIgnore_InputMode);
+            break;
+        case SkBlendMode::kSrc:
+            return as_SB(fShaderB)->asFragmentProcessor(args);
+            break;
+        case SkBlendMode::kDst:
+            return as_SB(fShaderA)->asFragmentProcessor(args);
+            break;
+        default:
+            sk_sp<GrFragmentProcessor> fpA(as_SB(fShaderA)->asFragmentProcessor(args));
+            if (!fpA) {
+                return nullptr;
+            }
+            sk_sp<GrFragmentProcessor> fpB(as_SB(fShaderB)->asFragmentProcessor(args));
+            if (!fpB) {
+                return nullptr;
+            }
+            return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
+                                                                      std::move(fpA), fMode);
+    }
+}
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkComposeShader::toString(SkString* str) const {
+    str->append("SkComposeShader: (");
+
+    str->append("ShaderA: ");
+    as_SB(fShaderA)->toString(str);
+    str->append(" ShaderB: ");
+    as_SB(fShaderB)->toString(str);
+    if (SkBlendMode::kSrcOver != fMode) {
+        str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode));
+    }
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/shaders/SkComposeShader.h b/src/shaders/SkComposeShader.h
new file mode 100644
index 0000000..39c43d6
--- /dev/null
+++ b/src/shaders/SkComposeShader.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkComposeShader_DEFINED
+#define SkComposeShader_DEFINED
+
+#include "SkShaderBase.h"
+#include "SkBlendMode.h"
+
+class SkColorSpacXformer;
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+/** \class SkComposeShader
+    This subclass of shader returns the composition of two other shaders, combined by
+    a xfermode.
+*/
+class SkComposeShader : public SkShaderBase {
+public:
+    /** Create a new compose shader, given shaders A, B, and a combining xfermode mode.
+        When the xfermode is called, it will be given the result from shader A as its
+        "dst", and the result from shader B as its "src".
+        mode->xfer32(sA_result, sB_result, ...)
+        @param shaderA  The colors from this shader are seen as the "dst" by the xfermode
+        @param shaderB  The colors from this shader are seen as the "src" by the xfermode
+        @param mode     The xfermode that combines the colors from the two shaders. If mode
+                        is null, then SRC_OVER is assumed.
+    */
+    SkComposeShader(sk_sp<SkShader> sA, sk_sp<SkShader> sB, SkBlendMode mode)
+        : fShaderA(std::move(sA))
+        , fShaderB(std::move(sB))
+        , fMode(mode)
+    {}
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+#ifdef SK_DEBUG
+    SkShader* getShaderA() { return fShaderA.get(); }
+    SkShader* getShaderB() { return fShaderB.get(); }
+#endif
+
+    bool asACompose(ComposeRec* rec) const override;
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader)
+
+protected:
+    SkComposeShader(SkReadBuffer&);
+    void flatten(SkWriteBuffer&) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*,
+                        const SkMatrix&, const SkPaint&, const SkMatrix* localM) const override;
+
+    bool isRasterPipelineOnly() const final;
+
+private:
+    sk_sp<SkShader>     fShaderA;
+    sk_sp<SkShader>     fShaderB;
+    SkBlendMode         fMode;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkEmptyShader.h b/src/shaders/SkEmptyShader.h
new file mode 100644
index 0000000..d4809e0
--- /dev/null
+++ b/src/shaders/SkEmptyShader.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkEmptyShader_DEFINED
+#define SkEmptyShader_DEFINED
+
+#include "SkShaderBase.h"
+
+// TODO: move this to private, as there is a public factory on SkShader
+
+/**
+ *  \class SkEmptyShader
+ *  A Shader that always draws nothing. Its createContext always returns nullptr.
+ */
+class SkEmptyShader : public SkShaderBase {
+public:
+    SkEmptyShader() {}
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader)
+
+protected:
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override {
+        return nullptr;
+    }
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        // Do nothing.
+        // We just don't want to fall through to SkShader::flatten(),
+        // which will write data we don't care to serialize or decode.
+    }
+
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, const SkMatrix&,
+                        const SkPaint&, const SkMatrix*) const override {
+        return false;
+    }
+
+private:
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
new file mode 100644
index 0000000..bdbd382
--- /dev/null
+++ b/src/shaders/SkImageShader.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkArenaAlloc.h"
+#include "SkBitmapController.h"
+#include "SkBitmapProcShader.h"
+#include "SkBitmapProvider.h"
+#include "SkColorTable.h"
+#include "SkEmptyShader.h"
+#include "SkImage_Base.h"
+#include "SkImageShader.h"
+#include "SkPM4fPriv.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "../jumper/SkJumper.h"
+
+SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
+    : INHERITED(matrix)
+    , fImage(std::move(img))
+    , fTileModeX(tmx)
+    , fTileModeY(tmy)
+{}
+
+sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
+    const TileMode tx = (TileMode)buffer.readUInt();
+    const TileMode ty = (TileMode)buffer.readUInt();
+    SkMatrix matrix;
+    buffer.readMatrix(&matrix);
+    sk_sp<SkImage> img = buffer.readImage();
+    if (!img) {
+        return nullptr;
+    }
+    return SkImageShader::Make(std::move(img), tx, ty, &matrix);
+}
+
+void SkImageShader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeUInt(fTileModeX);
+    buffer.writeUInt(fTileModeY);
+    buffer.writeMatrix(this->getLocalMatrix());
+    buffer.writeImage(fImage.get());
+}
+
+bool SkImageShader::isOpaque() const {
+    return fImage->isOpaque();
+}
+
+SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
+                                                    SkArenaAlloc* alloc) const {
+    return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
+                                                 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
+                                                 rec, alloc);
+}
+
+SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
+    if (texM) {
+        *texM = this->getLocalMatrix();
+    }
+    if (xy) {
+        xy[0] = (TileMode)fTileModeX;
+        xy[1] = (TileMode)fTileModeY;
+    }
+    return const_cast<SkImage*>(fImage.get());
+}
+
+#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
+bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
+    const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
+    if (!bm) {
+        return false;
+    }
+
+    if (texture) {
+        *texture = *bm;
+    }
+    if (texM) {
+        *texM = this->getLocalMatrix();
+    }
+    if (xy) {
+        xy[0] = (TileMode)fTileModeX;
+        xy[1] = (TileMode)fTileModeY;
+    }
+    return true;
+}
+#endif
+
+static bool bitmap_is_too_big(int w, int h) {
+    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
+    // communicates between its matrix-proc and its sampler-proc. Until we can
+    // widen that, we have to reject bitmaps that are larger.
+    //
+    static const int kMaxSize = 65535;
+
+    return w > kMaxSize || h > kMaxSize;
+}
+
+sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
+                                    const SkMatrix* localMatrix) {
+    if (!image || bitmap_is_too_big(image->width(), image->height())) {
+        return sk_make_sp<SkEmptyShader>();
+    } else {
+        return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix);
+    }
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkImageShader::toString(SkString* str) const {
+    const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
+
+    str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
+    fImage->toString(str);
+    this->INHERITED::toString(str);
+    str->append(")");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "SkGr.h"
+#include "GrContext.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "effects/GrBicubicEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
+
+sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const {
+
+    SkMatrix lmInverse;
+    if (!this->getLocalMatrix().invert(&lmInverse)) {
+        return nullptr;
+    }
+    if (args.fLocalMatrix) {
+        SkMatrix inv;
+        if (!args.fLocalMatrix->invert(&inv)) {
+            return nullptr;
+        }
+        lmInverse.postConcat(inv);
+    }
+
+    SkShader::TileMode tm[] = { fTileModeX, fTileModeY };
+
+    // Must set wrap and filter on the sampler before requesting a texture. In two places below
+    // we check the matrix scale factors to determine how to interpret the filter quality setting.
+    // This completely ignores the complexity of the drawVertices case where explicit local coords
+    // are provided by the caller.
+    bool doBicubic;
+    GrSamplerParams::FilterMode textureFilterMode =
+    GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(),
+                                    &doBicubic);
+    GrSamplerParams params(tm, textureFilterMode);
+    sk_sp<SkColorSpace> texColorSpace;
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params,
+                                                                 args.fDstColorSpace,
+                                                                 &texColorSpace, scaleAdjust));
+    if (!proxy) {
+        return nullptr;
+    }
+
+    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
+
+    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
+
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
+                                                                       args.fDstColorSpace);
+    sk_sp<GrFragmentProcessor> inner;
+    if (doBicubic) {
+        inner = GrBicubicEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
+                                      std::move(colorSpaceXform), lmInverse, tm);
+    } else {
+        inner = GrSimpleTextureEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
+                                            std::move(colorSpaceXform), lmInverse, params);
+    }
+
+    if (isAlphaOnly) {
+        return inner;
+    }
+    return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)));
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkImagePriv.h"
+
+sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
+                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
+                                   SkCopyPixelsMode cpm) {
+    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
+                               tmx, tmy, localMatrix);
+}
+
+static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
+    SkMatrix lm;
+    buffer.readMatrix(&lm);
+    sk_sp<SkImage> image = buffer.readBitmapAsImage();
+    SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
+    SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
+    return image ? image->makeShader(mx, my, &lm) : nullptr;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase)
+SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
+SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShaderBase_Type);
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
+
+
+bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dstCS, SkArenaAlloc* alloc,
+                                   const SkMatrix& ctm, const SkPaint& paint,
+                                   const SkMatrix* localM) const {
+    auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix());
+    if (localM) {
+        matrix.preConcat(*localM);
+    }
+
+    if (!matrix.invert(&matrix)) {
+        return false;
+    }
+    auto quality = paint.getFilterQuality();
+
+    SkBitmapProvider provider(fImage.get(), dstCS);
+    SkDefaultBitmapController controller(SkDefaultBitmapController::CanShadeHQ::kYes);
+    std::unique_ptr<SkBitmapController::State> state {
+        controller.requestBitmap(provider, matrix, quality)
+    };
+    if (!state) {
+        return false;
+    }
+
+    const SkPixmap& pm = state->pixmap();
+    matrix  = state->invMatrix();
+    quality = state->quality();
+    auto info = pm.info();
+
+    // When the matrix is just an integer translate, bilerp == nearest neighbor.
+    if (quality == kLow_SkFilterQuality &&
+        matrix.getType() <= SkMatrix::kTranslate_Mask &&
+        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
+        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
+        quality = kNone_SkFilterQuality;
+    }
+
+    // See skia:4649 and the GM image_scale_aligned.
+    if (quality == kNone_SkFilterQuality) {
+        if (matrix.getScaleX() >= 0) {
+            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
+                                            floorf(matrix.getTranslateX())));
+        }
+        if (matrix.getScaleY() >= 0) {
+            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
+                                            floorf(matrix.getTranslateY())));
+        }
+    }
+
+    p->append(SkRasterPipeline::seed_shader);
+
+    struct MiscCtx {
+        std::unique_ptr<SkBitmapController::State> state;
+        SkColor4f paint_color;
+        float     matrix[9];
+    };
+    auto misc = alloc->make<MiscCtx>();
+    misc->state       = std::move(state);  // Extend lifetime to match the pipeline's.
+    misc->paint_color = SkColor4f_from_SkColor(paint.getColor(), dstCS);
+    if (matrix.asAffine(misc->matrix)) {
+        p->append(SkRasterPipeline::matrix_2x3, misc->matrix);
+    } else {
+        matrix.get9(misc->matrix);
+        p->append(SkRasterPipeline::matrix_perspective, misc->matrix);
+    }
+
+    auto gather = alloc->make<SkJumper_GatherCtx>();
+    gather->pixels  = pm.addr();
+    gather->ctable  = pm.ctable() ? pm.ctable()->readColors() : nullptr;
+    gather->stride  = pm.rowBytesAsPixels();
+
+    // Tiling stages (clamp_x, mirror_y, etc.) are inclusive of their limit,
+    // so we tick down our width and height by one float to make them exclusive.
+    auto ulp_before = [](float f) {
+        uint32_t bits;
+        memcpy(&bits, &f, 4);
+        bits--;
+        memcpy(&f, &bits, 4);
+        return f;
+    };
+    auto limit_x = alloc->make<float>(ulp_before((float)pm. width())),
+         limit_y = alloc->make<float>(ulp_before((float)pm.height()));
+
+    auto append_tiling_and_gather = [&] {
+        switch (fTileModeX) {
+            case kClamp_TileMode:  p->append(SkRasterPipeline::clamp_x,  limit_x); break;
+            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x); break;
+            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x); break;
+        }
+        switch (fTileModeY) {
+            case kClamp_TileMode:  p->append(SkRasterPipeline::clamp_y,  limit_y); break;
+            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y); break;
+            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y); break;
+        }
+        switch (info.colorType()) {
+            case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::gather_a8,   gather); break;
+            case kIndex_8_SkColorType:   p->append(SkRasterPipeline::gather_i8,   gather); break;
+            case kGray_8_SkColorType:    p->append(SkRasterPipeline::gather_g8,   gather); break;
+            case kRGB_565_SkColorType:   p->append(SkRasterPipeline::gather_565,  gather); break;
+            case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, gather); break;
+            case kRGBA_8888_SkColorType:
+            case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, gather); break;
+            case kRGBA_F16_SkColorType:  p->append(SkRasterPipeline::gather_f16,  gather); break;
+            default: SkASSERT(false);
+        }
+        if (dstCS && (!info.colorSpace() || info.gammaCloseToSRGB())) {
+            p->append_from_srgb(info.alphaType());
+        }
+    };
+
+    SkJumper_SamplerCtx* sampler = nullptr;
+    if (quality != kNone_SkFilterQuality) {
+        sampler = alloc->make<SkJumper_SamplerCtx>();
+    }
+
+    auto sample = [&](SkRasterPipeline::StockStage setup_x,
+                      SkRasterPipeline::StockStage setup_y) {
+        p->append(setup_x, sampler);
+        p->append(setup_y, sampler);
+        append_tiling_and_gather();
+        p->append(SkRasterPipeline::accumulate, sampler);
+    };
+
+    if (quality == kNone_SkFilterQuality) {
+        append_tiling_and_gather();
+    } else if (quality == kLow_SkFilterQuality) {
+        p->append(SkRasterPipeline::save_xy, sampler);
+
+        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
+        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
+        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
+        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
+
+        p->append(SkRasterPipeline::move_dst_src);
+    } else {
+        p->append(SkRasterPipeline::save_xy, sampler);
+
+        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
+        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
+        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
+        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
+
+        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
+        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
+        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
+        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
+
+        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
+        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
+        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
+        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
+
+        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
+        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
+        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
+        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
+
+        p->append(SkRasterPipeline::move_dst_src);
+    }
+
+    auto effective_color_type = [](SkColorType ct) {
+        return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct;
+    };
+
+    if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) {
+        p->append(SkRasterPipeline::swap_rb);
+    }
+    if (info.colorType() == kAlpha_8_SkColorType) {
+        p->append(SkRasterPipeline::set_rgb, &misc->paint_color);
+    }
+    if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
+        p->append(SkRasterPipeline::premul);
+    }
+    if (quality > kLow_SkFilterQuality) {
+        // Bicubic filtering naturally produces out of range values on both sides.
+        p->append(SkRasterPipeline::clamp_0);
+        p->append(SkRasterPipeline::clamp_a);
+    }
+    append_gamut_transform(p, alloc, info.colorSpace(), dstCS, kPremul_SkAlphaType);
+    return true;
+}
diff --git a/src/shaders/SkImageShader.h b/src/shaders/SkImageShader.h
new file mode 100644
index 0000000..7be982c
--- /dev/null
+++ b/src/shaders/SkImageShader.h
@@ -0,0 +1,58 @@
+/*
+ * 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 SkImageShader_DEFINED
+#define SkImageShader_DEFINED
+
+#include "SkBitmapProcShader.h"
+#include "SkColorSpaceXformer.h"
+#include "SkImage.h"
+#include "SkShaderBase.h"
+
+class SkImageShader : public SkShaderBase {
+public:
+    static sk_sp<SkShader> Make(sk_sp<SkImage>, TileMode tx, TileMode ty,
+                                const SkMatrix* localMatrix);
+
+    bool isOpaque() const override;
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageShader)
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SkImageShader(sk_sp<SkImage>, TileMode tx, TileMode ty, const SkMatrix* localMatrix);
+
+protected:
+    void flatten(SkWriteBuffer&) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override;
+#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
+    bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override;
+#endif
+    SkImage* onIsAImage(SkMatrix*, TileMode*) const override;
+
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                        const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
+
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return xformer->apply(fImage.get())->makeShader(fTileModeX, fTileModeY,
+                                                        &this->getLocalMatrix());
+    }
+
+    sk_sp<SkImage>  fImage;
+    const TileMode  fTileModeX;
+    const TileMode  fTileModeY;
+
+private:
+    friend class SkShaderBase;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkLightingShader.cpp b/src/shaders/SkLightingShader.cpp
new file mode 100644
index 0000000..cdfa528
--- /dev/null
+++ b/src/shaders/SkLightingShader.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkArenaAlloc.h"
+#include "SkBitmapProcShader.h"
+#include "SkBitmapProcState.h"
+#include "SkColor.h"
+#include "SkColorSpaceXformer.h"
+#include "SkEmptyShader.h"
+#include "SkLightingShader.h"
+#include "SkMathPriv.h"
+#include "SkNormalSource.h"
+#include "SkPoint3.h"
+#include "SkReadBuffer.h"
+#include "SkShaderBase.h"
+#include "SkWriteBuffer.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+/*
+   SkLightingShader TODOs:
+        support different light types
+        support multiple lights
+        fix non-opaque diffuse textures
+
+    To Test:
+        A8 diffuse textures
+        down & upsampled draws
+*/
+
+
+
+/** \class SkLightingShaderImpl
+    This subclass of shader applies lighting.
+*/
+class SkLightingShaderImpl : public SkShaderBase {
+public:
+    /** Create a new lighting shader that uses the provided normal map and
+        lights to light the diffuse bitmap.
+        @param diffuseShader     the shader that provides the diffuse colors
+        @param normalSource      the source of normals for lighting computation
+        @param lights            the lights applied to the geometry
+    */
+    SkLightingShaderImpl(sk_sp<SkShader> diffuseShader,
+                         sk_sp<SkNormalSource> normalSource,
+                         sk_sp<SkLights> lights)
+        : fDiffuseShader(std::move(diffuseShader))
+        , fNormalSource(std::move(normalSource))
+        , fLights(std::move(lights)) {}
+
+    bool isOpaque() const override;
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    class LightingShaderContext : public Context {
+    public:
+        // The context takes ownership of the context and provider. It will call their destructors
+        // and then indirectly free their memory by calling free() on heapAllocated
+        LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&,
+                              SkShaderBase::Context* diffuseContext, SkNormalSource::Provider*,
+                              void* heapAllocated);
+
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
+
+        uint32_t getFlags() const override { return fFlags; }
+
+    private:
+        SkShaderBase::Context*    fDiffuseContext;
+        SkNormalSource::Provider* fNormalProvider;
+        SkColor                   fPaintColor;
+        uint32_t                  fFlags;
+
+        typedef Context INHERITED;
+    };
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingShaderImpl)
+
+protected:
+    void flatten(SkWriteBuffer&) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
+private:
+    sk_sp<SkShader> fDiffuseShader;
+    sk_sp<SkNormalSource> fNormalSource;
+    sk_sp<SkLights> fLights;
+
+    friend class SkLightingShader;
+
+    typedef SkShaderBase INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrCoordTransform.h"
+#include "GrFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
+#include "SkGr.h"
+
+// This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is
+// handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it
+// premul'd.
+class LightingFP : public GrFragmentProcessor {
+public:
+    LightingFP(sk_sp<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights)
+            : INHERITED(kPreservesOpaqueInput_OptimizationFlag) {
+        // fuse all ambient lights into a single one
+        fAmbientColor = lights->ambientLightColor();
+        for (int i = 0; i < lights->numLights(); ++i) {
+            if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) {
+                fDirectionalLights.push_back(lights->light(i));
+                // TODO get the handle to the shadow map if there is one
+            } else {
+                SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP");
+            }
+        }
+
+        this->registerChildProcessor(std::move(normalFP));
+        this->initClassID<LightingFP>();
+    }
+
+    class GLSLLightingFP : public GrGLSLFragmentProcessor {
+    public:
+        GLSLLightingFP() {
+            fAmbientColor.fX = 0.0f;
+        }
+
+        void emitCode(EmitArgs& args) override {
+
+            GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+            const LightingFP& lightingFP = args.fFp.cast<LightingFP>();
+
+            const char *lightDirsUniName = nullptr;
+            const char *lightColorsUniName = nullptr;
+            if (lightingFP.fDirectionalLights.count() != 0) {
+                fLightDirsUni = uniformHandler->addUniformArray(
+                        kFragment_GrShaderFlag,
+                        kVec3f_GrSLType,
+                        kDefault_GrSLPrecision,
+                        "LightDir",
+                        lightingFP.fDirectionalLights.count(),
+                        &lightDirsUniName);
+                fLightColorsUni = uniformHandler->addUniformArray(
+                        kFragment_GrShaderFlag,
+                        kVec3f_GrSLType,
+                        kDefault_GrSLPrecision,
+                        "LightColor",
+                        lightingFP.fDirectionalLights.count(),
+                        &lightColorsUniName);
+            }
+
+            const char* ambientColorUniName = nullptr;
+            fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                                          kVec3f_GrSLType, kDefault_GrSLPrecision,
+                                                          "AmbientColor", &ambientColorUniName);
+
+            fragBuilder->codeAppendf("vec4 diffuseColor = %s;", args.fInputColor);
+
+            SkString dstNormalName("dstNormal");
+            this->emitChild(0, &dstNormalName, args);
+
+            fragBuilder->codeAppendf("vec3 normal = %s.xyz;", dstNormalName.c_str());
+
+            fragBuilder->codeAppend( "vec3 result = vec3(0.0);");
+
+            // diffuse light
+            if (lightingFP.fDirectionalLights.count() != 0) {
+                fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {",
+                                         lightingFP.fDirectionalLights.count());
+                // TODO: modulate the contribution from each light based on the shadow map
+                fragBuilder->codeAppendf("    float NdotL = clamp(dot(normal, %s[i]), 0.0, 1.0);",
+                                         lightDirsUniName);
+                fragBuilder->codeAppendf("    result += %s[i]*diffuseColor.rgb*NdotL;",
+                                         lightColorsUniName);
+                fragBuilder->codeAppend("}");
+            }
+
+            // ambient light
+            fragBuilder->codeAppendf("result += %s * diffuseColor.rgb;", ambientColorUniName);
+
+            // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0)
+            fragBuilder->codeAppendf("%s = vec4(clamp(result.rgb, 0.0, diffuseColor.a), "
+                                               "diffuseColor.a);", args.fOutputColor);
+        }
+
+        static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+            const LightingFP& lightingFP = proc.cast<LightingFP>();
+            b->add32(lightingFP.fDirectionalLights.count());
+        }
+
+    protected:
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& proc) override {
+            const LightingFP& lightingFP = proc.cast<LightingFP>();
+
+            const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights();
+            if (directionalLights != fDirectionalLights) {
+                SkTArray<SkColor3f> lightDirs(directionalLights.count());
+                SkTArray<SkVector3> lightColors(directionalLights.count());
+                for (const SkLights::Light& light : directionalLights) {
+                    lightDirs.push_back(light.dir());
+                    lightColors.push_back(light.color());
+                }
+
+                pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX));
+                pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX));
+
+                fDirectionalLights = directionalLights;
+            }
+
+            const SkColor3f& ambientColor = lightingFP.ambientColor();
+            if (ambientColor != fAmbientColor) {
+                pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
+                fAmbientColor = ambientColor;
+            }
+        }
+
+    private:
+        SkTArray<SkLights::Light> fDirectionalLights;
+        GrGLSLProgramDataManager::UniformHandle fLightDirsUni;
+        GrGLSLProgramDataManager::UniformHandle fLightColorsUni;
+
+        SkColor3f fAmbientColor;
+        GrGLSLProgramDataManager::UniformHandle fAmbientColorUni;
+    };
+
+    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
+        GLSLLightingFP::GenKey(*this, caps, b);
+    }
+
+    const char* name() const override { return "LightingFP"; }
+
+    const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; }
+    const SkColor3f& ambientColor() const { return fAmbientColor; }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; }
+
+    bool onIsEqual(const GrFragmentProcessor& proc) const override {
+        const LightingFP& lightingFP = proc.cast<LightingFP>();
+        return fDirectionalLights == lightingFP.fDirectionalLights &&
+               fAmbientColor == lightingFP.fAmbientColor;
+    }
+
+    SkTArray<SkLights::Light> fDirectionalLights;
+    SkColor3f                 fAmbientColor;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const AsFPArgs& args) const {
+    sk_sp<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args));
+    if (!normalFP) {
+        return nullptr;
+    }
+
+    if (fDiffuseShader) {
+        sk_sp<GrFragmentProcessor> fpPipeline[] = {
+            as_SB(fDiffuseShader)->asFragmentProcessor(args),
+            sk_make_sp<LightingFP>(std::move(normalFP), fLights)
+        };
+        if(!fpPipeline[0]) {
+            return nullptr;
+        }
+
+        sk_sp<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2);
+        // FP is wrapped because paint's alpha needs to be applied to output
+        return GrFragmentProcessor::MulOutputByInputAlpha(std::move(innerLightFP));
+    } else {
+        // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP
+        // expects premul'd color.
+        return GrFragmentProcessor::PremulInput(sk_make_sp<LightingFP>(std::move(normalFP),
+                                                                       fLights));
+    }
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+
+bool SkLightingShaderImpl::isOpaque() const {
+    return (fDiffuseShader ? fDiffuseShader->isOpaque() : false);
+}
+
+SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(
+        const SkLightingShaderImpl& shader, const ContextRec& rec,
+        SkShaderBase::Context* diffuseContext, SkNormalSource::Provider* normalProvider,
+        void* heapAllocated)
+    : INHERITED(shader, rec)
+    , fDiffuseContext(diffuseContext)
+    , fNormalProvider(normalProvider) {
+    bool isOpaque = shader.isOpaque();
+
+    // update fFlags
+    uint32_t flags = 0;
+    if (isOpaque && (255 == this->getPaintAlpha())) {
+        flags |= kOpaqueAlpha_Flag;
+    }
+
+    fPaintColor = rec.fPaint->getColor();
+    fFlags = flags;
+}
+
+static inline SkPMColor convert(SkColor3f color, U8CPU a) {
+    if (color.fX <= 0.0f) {
+        color.fX = 0.0f;
+    } else if (color.fX >= 255.0f) {
+        color.fX = 255.0f;
+    }
+
+    if (color.fY <= 0.0f) {
+        color.fY = 0.0f;
+    } else if (color.fY >= 255.0f) {
+        color.fY = 255.0f;
+    }
+
+    if (color.fZ <= 0.0f) {
+        color.fZ = 0.0f;
+    } else if (color.fZ >= 255.0f) {
+        color.fZ = 255.0f;
+    }
+
+    return SkPreMultiplyARGB(a, (int) color.fX,  (int) color.fY, (int) color.fZ);
+}
+
+// larger is better (fewer times we have to loop), but we shouldn't
+// take up too much stack-space (each one here costs 16 bytes)
+#define BUFFER_MAX 16
+void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y,
+                                                            SkPMColor result[], int count) {
+    const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader);
+
+    SkPMColor diffuse[BUFFER_MAX];
+    SkPoint3 normals[BUFFER_MAX];
+
+    SkColor diffColor = fPaintColor;
+
+    do {
+        int n = SkTMin(count, BUFFER_MAX);
+
+        fNormalProvider->fillScanLine(x, y, normals, n);
+
+        if (fDiffuseContext) {
+            fDiffuseContext->shadeSpan(x, y, diffuse, n);
+        }
+
+        for (int i = 0; i < n; ++i) {
+            if (fDiffuseContext) {
+                diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]);
+            }
+
+            SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f);
+
+            // Adding ambient light
+            accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor);
+            accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor);
+            accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor);
+
+            // This is all done in linear unpremul color space (each component 0..255.0f though)
+            for (int l = 0; l < lightShader.fLights->numLights(); ++l) {
+                const SkLights::Light& light = lightShader.fLights->light(l);
+
+                SkScalar illuminanceScalingFactor = 1.0f;
+
+                if (SkLights::Light::kDirectional_LightType == light.type()) {
+                    illuminanceScalingFactor = normals[i].dot(light.dir());
+                    if (illuminanceScalingFactor < 0.0f) {
+                        illuminanceScalingFactor = 0.0f;
+                    }
+                }
+
+                accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor;
+                accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor;
+                accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor;
+            }
+
+            // convert() premultiplies the accumulate color with alpha
+            result[i] = convert(accum, SkColorGetA(diffColor));
+        }
+
+        result += n;
+        x += n;
+        count -= n;
+    } while (count > 0);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef SK_IGNORE_TO_STRING
+void SkLightingShaderImpl::toString(SkString* str) const {
+    str->appendf("LightingShader: ()");
+}
+#endif
+
+sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
+
+    // Discarding SkShader flattenable params
+    bool hasLocalMatrix = buf.readBool();
+    SkAssertResult(!hasLocalMatrix);
+
+    sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf);
+
+    sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>());
+
+    bool hasDiffuse = buf.readBool();
+    sk_sp<SkShader> diffuseShader = nullptr;
+    if (hasDiffuse) {
+        diffuseShader = buf.readFlattenable<SkShaderBase>();
+    }
+
+    return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
+                                            std::move(lights));
+}
+
+void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const {
+    this->INHERITED::flatten(buf);
+
+    fLights->flatten(buf);
+
+    buf.writeFlattenable(fNormalSource.get());
+    buf.writeBool(fDiffuseShader);
+    if (fDiffuseShader) {
+        buf.writeFlattenable(fDiffuseShader.get());
+    }
+}
+
+SkShaderBase::Context* SkLightingShaderImpl::onMakeContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const
+{
+    SkShaderBase::Context *diffuseContext = nullptr;
+    if (fDiffuseShader) {
+        diffuseContext = as_SB(fDiffuseShader)->makeContext(rec, alloc);
+        if (!diffuseContext) {
+            return nullptr;
+        }
+    }
+
+    SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc);
+    if (!normalProvider) {
+        return nullptr;
+    }
+
+    return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr);
+}
+
+sk_sp<SkShader> SkLightingShaderImpl::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    sk_sp<SkShader> xformedDiffuseShader =
+            fDiffuseShader ? xformer->apply(fDiffuseShader.get()) : nullptr;
+    return SkLightingShader::Make(std::move(xformedDiffuseShader), fNormalSource,
+                                  fLights->makeColorSpace(xformer));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader,
+                                       sk_sp<SkNormalSource> normalSource,
+                                       sk_sp<SkLights> lights) {
+    SkASSERT(lights);
+    if (!normalSource) {
+        normalSource = SkNormalSource::MakeFlat();
+    }
+
+    return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
+                                            std::move(lights));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingShaderImpl)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkLightingShader.h b/src/shaders/SkLightingShader.h
similarity index 100%
rename from src/core/SkLightingShader.h
rename to src/shaders/SkLightingShader.h
diff --git a/src/shaders/SkLocalMatrixShader.cpp b/src/shaders/SkLocalMatrixShader.cpp
new file mode 100644
index 0000000..e21e4a8
--- /dev/null
+++ b/src/shaders/SkLocalMatrixShader.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "SkLocalMatrixShader.h"
+
+#if SK_SUPPORT_GPU
+#include "GrFragmentProcessor.h"
+#endif
+
+#if SK_SUPPORT_GPU
+sk_sp<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(const AsFPArgs& args) const {
+    SkMatrix tmp = this->getLocalMatrix();
+    if (args.fLocalMatrix) {
+        tmp.preConcat(*args.fLocalMatrix);
+    }
+    return as_SB(fProxyShader)->asFragmentProcessor(AsFPArgs(
+        args.fContext, args.fViewMatrix, &tmp, args.fFilterQuality, args.fDstColorSpace));
+}
+#endif
+
+sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) {
+    SkMatrix lm;
+    buffer.readMatrix(&lm);
+    auto baseShader(buffer.readShader());
+    if (!baseShader) {
+        return nullptr;
+    }
+    return baseShader->makeWithLocalMatrix(lm);
+}
+
+void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeMatrix(this->getLocalMatrix());
+    buffer.writeFlattenable(fProxyShader.get());
+}
+
+SkShaderBase::Context* SkLocalMatrixShader::onMakeContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const
+{
+    ContextRec newRec(rec);
+    SkMatrix tmp;
+    if (rec.fLocalMatrix) {
+        tmp.setConcat(*rec.fLocalMatrix, this->getLocalMatrix());
+        newRec.fLocalMatrix = &tmp;
+    } else {
+        newRec.fLocalMatrix = &this->getLocalMatrix();
+    }
+    return as_SB(fProxyShader)->makeContext(newRec, alloc);
+}
+
+SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, enum TileMode* mode) const {
+    SkMatrix imageMatrix;
+    SkImage* image = fProxyShader->isAImage(&imageMatrix, mode);
+    if (image && outMatrix) {
+        // Local matrix must be applied first so it is on the right side of the concat.
+        *outMatrix = SkMatrix::Concat(imageMatrix, this->getLocalMatrix());
+    }
+
+    return image;
+}
+
+bool SkLocalMatrixShader::onAppendStages(SkRasterPipeline* p,
+                                         SkColorSpace* dst,
+                                         SkArenaAlloc* scratch,
+                                         const SkMatrix& ctm,
+                                         const SkPaint& paint,
+                                         const SkMatrix* localM) const {
+    SkMatrix tmp;
+    if (localM) {
+        tmp.setConcat(*localM, this->getLocalMatrix());
+    }
+    return as_SB(fProxyShader)->appendStages(p, dst, scratch, ctm, paint,
+                                             localM ? &tmp : &this->getLocalMatrix());
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkLocalMatrixShader::toString(SkString* str) const {
+    str->append("SkLocalMatrixShader: (");
+
+    as_SB(fProxyShader)->toString(str);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
+    if (localMatrix.isIdentity()) {
+        return sk_ref_sp(const_cast<SkShader*>(this));
+    }
+
+    const SkMatrix* lm = &localMatrix;
+
+    sk_sp<SkShader> baseShader;
+    SkMatrix otherLocalMatrix;
+    sk_sp<SkShader> proxy(as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix));
+    if (proxy) {
+        otherLocalMatrix.preConcat(localMatrix);
+        lm = &otherLocalMatrix;
+        baseShader = proxy;
+    } else {
+        baseShader = sk_ref_sp(const_cast<SkShader*>(this));
+    }
+
+    return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
+}
diff --git a/src/shaders/SkLocalMatrixShader.h b/src/shaders/SkLocalMatrixShader.h
new file mode 100644
index 0000000..4572e9f
--- /dev/null
+++ b/src/shaders/SkLocalMatrixShader.h
@@ -0,0 +1,75 @@
+/*
+ * 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 SkLocalMatrixShader_DEFINED
+#define SkLocalMatrixShader_DEFINED
+
+#include "SkShaderBase.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+
+class GrFragmentProcessor;
+class SkArenaAlloc;
+class SkColorSpaceXformer;
+
+class SkLocalMatrixShader : public SkShaderBase {
+public:
+    SkLocalMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix)
+    : INHERITED(&localMatrix)
+    , fProxyShader(std::move(proxy))
+    {}
+
+    GradientType asAGradient(GradientInfo* info) const override {
+        return fProxyShader->asAGradient(info);
+    }
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    sk_sp<SkShader> makeAsALocalMatrixShader(SkMatrix* localMatrix) const override {
+        if (localMatrix) {
+            *localMatrix = this->getLocalMatrix();
+        }
+        return fProxyShader;
+    }
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLocalMatrixShader)
+
+protected:
+    void flatten(SkWriteBuffer&) const override;
+
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+
+    SkImage* onIsAImage(SkMatrix* matrix, TileMode* mode) const override;
+
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                        const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
+
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return as_SB(fProxyShader)->makeColorSpace(xformer)->makeWithLocalMatrix(
+            this->getLocalMatrix());
+    }
+
+#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
+    bool onIsABitmap(SkBitmap* bitmap, SkMatrix* matrix, TileMode* mode) const override {
+        return fProxyShader->isABitmap(bitmap, matrix, mode);
+    }
+#endif
+
+    bool isRasterPipelineOnly() const final {
+        return as_SB(fProxyShader)->isRasterPipelineOnly();
+    }
+
+private:
+    sk_sp<SkShader> fProxyShader;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/SkPerlinNoiseShader.cpp b/src/shaders/SkPerlinNoiseShader.cpp
new file mode 100644
index 0000000..ddcd295
--- /dev/null
+++ b/src/shaders/SkPerlinNoiseShader.cpp
@@ -0,0 +1,1491 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPerlinNoiseShader.h"
+
+#include "SkArenaAlloc.h"
+#include "SkDither.h"
+#include "SkColorFilter.h"
+#include "SkMakeUnique.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkShader.h"
+#include "SkUnPreMultiply.h"
+#include "SkString.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrCoordTransform.h"
+#include "SkGr.h"
+#include "effects/GrConstColorProcessor.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
+#endif
+
+static const int kBlockSize = 256;
+static const int kBlockMask = kBlockSize - 1;
+static const int kPerlinNoise = 4096;
+static const int kRandMaximum = SK_MaxS32; // 2**31 - 1
+
+static uint8_t improved_noise_permutations[] = {
+    151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225, 140,  36, 103,
+     30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148, 247, 120, 234,  75,   0,  26,
+    197,  62,  94, 252, 219, 203, 117,  35,  11,  32,  57, 177,  33,  88, 237, 149,  56,  87, 174,
+     20, 125, 136, 171, 168,  68, 175,  74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,
+     83, 111, 229, 122,  60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,
+     54,  65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169, 200, 196,
+    135, 130, 116, 188, 159,  86, 164, 100, 109, 198, 173, 186,   3,  64,  52, 217, 226, 250, 124,
+    123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212, 207, 206,  59, 227,  47,  16,  58,  17,
+    182, 189,  28,  42, 223, 183, 170, 213, 119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101,
+    155, 167,  43, 172,   9, 129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185,
+    112, 104, 218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241,  81,
+     51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157, 184,  84, 204, 176,
+    115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93, 222, 114,  67,  29,  24,  72, 243,
+    141, 128, 195,  78,  66, 215,  61, 156, 180,
+    151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225, 140,  36, 103,
+     30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148, 247, 120, 234,  75,   0,  26,
+    197,  62,  94, 252, 219, 203, 117,  35,  11,  32,  57, 177,  33,  88, 237, 149,  56,  87, 174,
+     20, 125, 136, 171, 168,  68, 175,  74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,
+     83, 111, 229, 122,  60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,
+     54,  65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169, 200, 196,
+    135, 130, 116, 188, 159,  86, 164, 100, 109, 198, 173, 186,   3,  64,  52, 217, 226, 250, 124,
+    123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212, 207, 206,  59, 227,  47,  16,  58,  17,
+    182, 189,  28,  42, 223, 183, 170, 213, 119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101,
+    155, 167,  43, 172,   9, 129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185,
+    112, 104, 218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241,  81,
+     51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157, 184,  84, 204, 176,
+    115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93, 222, 114,  67,  29,  24,  72, 243,
+    141, 128, 195,  78,  66, 215,  61, 156, 180
+};
+
+class SkPerlinNoiseShaderImpl : public SkShaderBase {
+public:
+    struct StitchData {
+        StitchData()
+          : fWidth(0)
+          , fWrapX(0)
+          , fHeight(0)
+          , fWrapY(0)
+        {}
+
+        bool operator==(const StitchData& other) const {
+            return fWidth == other.fWidth &&
+                   fWrapX == other.fWrapX &&
+                   fHeight == other.fHeight &&
+                   fWrapY == other.fWrapY;
+        }
+
+        int fWidth; // How much to subtract to wrap for stitching.
+        int fWrapX; // Minimum value to wrap.
+        int fHeight;
+        int fWrapY;
+    };
+
+    struct PaintingData {
+        PaintingData(const SkISize& tileSize, SkScalar seed,
+                     SkScalar baseFrequencyX, SkScalar baseFrequencyY,
+                     const SkMatrix& matrix)
+        {
+            SkVector vec[2] = {
+                { SkScalarInvert(baseFrequencyX),   SkScalarInvert(baseFrequencyY)  },
+                { SkIntToScalar(tileSize.fWidth),   SkIntToScalar(tileSize.fHeight) },
+            };
+            matrix.mapVectors(vec, 2);
+
+            fBaseFrequency.set(SkScalarInvert(vec[0].fX), SkScalarInvert(vec[0].fY));
+            fTileSize.set(SkScalarRoundToInt(vec[1].fX), SkScalarRoundToInt(vec[1].fY));
+            this->init(seed);
+            if (!fTileSize.isEmpty()) {
+                this->stitch();
+            }
+
+    #if SK_SUPPORT_GPU
+            fPermutationsBitmap.setInfo(SkImageInfo::MakeA8(kBlockSize, 1));
+            fPermutationsBitmap.setPixels(fLatticeSelector);
+
+            fNoiseBitmap.setInfo(SkImageInfo::MakeN32Premul(kBlockSize, 4));
+            fNoiseBitmap.setPixels(fNoise[0][0]);
+
+            fImprovedPermutationsBitmap.setInfo(SkImageInfo::MakeA8(256, 1));
+            fImprovedPermutationsBitmap.setPixels(improved_noise_permutations);
+
+            fGradientBitmap.setInfo(SkImageInfo::MakeN32Premul(16, 1));
+            static uint8_t gradients[] = { 2, 2, 1, 0,
+                                           0, 2, 1, 0,
+                                           2, 0, 1, 0,
+                                           0, 0, 1, 0,
+                                           2, 1, 2, 0,
+                                           0, 1, 2, 0,
+                                           2, 1, 0, 0,
+                                           0, 1, 0, 0,
+                                           1, 2, 2, 0,
+                                           1, 0, 2, 0,
+                                           1, 2, 0, 0,
+                                           1, 0, 0, 0,
+                                           2, 2, 1, 0,
+                                           1, 0, 2, 0,
+                                           0, 2, 1, 0,
+                                           1, 0, 0, 0 };
+            fGradientBitmap.setPixels(gradients);
+    #endif
+        }
+
+        int         fSeed;
+        uint8_t     fLatticeSelector[kBlockSize];
+        uint16_t    fNoise[4][kBlockSize][2];
+        SkPoint     fGradient[4][kBlockSize];
+        SkISize     fTileSize;
+        SkVector    fBaseFrequency;
+        StitchData  fStitchDataInit;
+
+    private:
+
+    #if SK_SUPPORT_GPU
+        SkBitmap   fPermutationsBitmap;
+        SkBitmap   fNoiseBitmap;
+        SkBitmap   fImprovedPermutationsBitmap;
+        SkBitmap   fGradientBitmap;
+    #endif
+
+        inline int random()  {
+            static const int gRandAmplitude = 16807; // 7**5; primitive root of m
+            static const int gRandQ = 127773; // m / a
+            static const int gRandR = 2836; // m % a
+
+            int result = gRandAmplitude * (fSeed % gRandQ) - gRandR * (fSeed / gRandQ);
+            if (result <= 0)
+                result += kRandMaximum;
+            fSeed = result;
+            return result;
+        }
+
+        // Only called once. Could be part of the constructor.
+        void init(SkScalar seed)
+        {
+            static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize));
+
+            // According to the SVG spec, we must truncate (not round) the seed value.
+            fSeed = SkScalarTruncToInt(seed);
+            // The seed value clamp to the range [1, kRandMaximum - 1].
+            if (fSeed <= 0) {
+                fSeed = -(fSeed % (kRandMaximum - 1)) + 1;
+            }
+            if (fSeed > kRandMaximum - 1) {
+                fSeed = kRandMaximum - 1;
+            }
+            for (int channel = 0; channel < 4; ++channel) {
+                for (int i = 0; i < kBlockSize; ++i) {
+                    fLatticeSelector[i] = i;
+                    fNoise[channel][i][0] = (random() % (2 * kBlockSize));
+                    fNoise[channel][i][1] = (random() % (2 * kBlockSize));
+                }
+            }
+            for (int i = kBlockSize - 1; i > 0; --i) {
+                int k = fLatticeSelector[i];
+                int j = random() % kBlockSize;
+                SkASSERT(j >= 0);
+                SkASSERT(j < kBlockSize);
+                fLatticeSelector[i] = fLatticeSelector[j];
+                fLatticeSelector[j] = k;
+            }
+
+            // Perform the permutations now
+            {
+                // Copy noise data
+                uint16_t noise[4][kBlockSize][2];
+                for (int i = 0; i < kBlockSize; ++i) {
+                    for (int channel = 0; channel < 4; ++channel) {
+                        for (int j = 0; j < 2; ++j) {
+                            noise[channel][i][j] = fNoise[channel][i][j];
+                        }
+                    }
+                }
+                // Do permutations on noise data
+                for (int i = 0; i < kBlockSize; ++i) {
+                    for (int channel = 0; channel < 4; ++channel) {
+                        for (int j = 0; j < 2; ++j) {
+                            fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j];
+                        }
+                    }
+                }
+            }
+
+            // Half of the largest possible value for 16 bit unsigned int
+            static const SkScalar gHalfMax16bits = 32767.5f;
+
+            // Compute gradients from permutated noise data
+            for (int channel = 0; channel < 4; ++channel) {
+                for (int i = 0; i < kBlockSize; ++i) {
+                    fGradient[channel][i] = SkPoint::Make(
+                        (fNoise[channel][i][0] - kBlockSize) * gInvBlockSizef,
+                        (fNoise[channel][i][1] - kBlockSize) * gInvBlockSizef);
+                    fGradient[channel][i].normalize();
+                    // Put the normalized gradient back into the noise data
+                    fNoise[channel][i][0] = SkScalarRoundToInt(
+                                                   (fGradient[channel][i].fX + 1) * gHalfMax16bits);
+                    fNoise[channel][i][1] = SkScalarRoundToInt(
+                                                   (fGradient[channel][i].fY + 1) * gHalfMax16bits);
+                }
+            }
+        }
+
+        // Only called once. Could be part of the constructor.
+        void stitch() {
+            SkScalar tileWidth  = SkIntToScalar(fTileSize.width());
+            SkScalar tileHeight = SkIntToScalar(fTileSize.height());
+            SkASSERT(tileWidth > 0 && tileHeight > 0);
+            // When stitching tiled turbulence, the frequencies must be adjusted
+            // so that the tile borders will be continuous.
+            if (fBaseFrequency.fX) {
+                SkScalar lowFrequencx =
+                    SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
+                SkScalar highFrequencx =
+                    SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
+                // BaseFrequency should be non-negative according to the standard.
+                if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) {
+                    fBaseFrequency.fX = lowFrequencx;
+                } else {
+                    fBaseFrequency.fX = highFrequencx;
+                }
+            }
+            if (fBaseFrequency.fY) {
+                SkScalar lowFrequency =
+                    SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
+                SkScalar highFrequency =
+                    SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
+                if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) {
+                    fBaseFrequency.fY = lowFrequency;
+                } else {
+                    fBaseFrequency.fY = highFrequency;
+                }
+            }
+            // Set up TurbulenceInitial stitch values.
+            fStitchDataInit.fWidth  =
+                SkScalarRoundToInt(tileWidth * fBaseFrequency.fX);
+            fStitchDataInit.fWrapX  = kPerlinNoise + fStitchDataInit.fWidth;
+            fStitchDataInit.fHeight =
+                SkScalarRoundToInt(tileHeight * fBaseFrequency.fY);
+            fStitchDataInit.fWrapY  = kPerlinNoise + fStitchDataInit.fHeight;
+        }
+
+    public:
+
+#if SK_SUPPORT_GPU
+        const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; }
+
+        const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; }
+
+        const SkBitmap& getImprovedPermutationsBitmap() const { return fImprovedPermutationsBitmap; }
+
+        const SkBitmap& getGradientBitmap() const { return fGradientBitmap; }
+#endif
+    };
+
+    /**
+     *  About the noise types : the difference between the first 2 is just minor tweaks to the
+     *  algorithm, they're not 2 entirely different noises. The output looks different, but once the
+     *  noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by
+     *  doing :
+     *  kFractalNoise_Type : noise * 0.5 + 0.5
+     *  kTurbulence_Type   : abs(noise)
+     *  Very little differences between the 2 types, although you can tell the difference visually.
+     *  "Improved" is based on the Improved Perlin Noise algorithm described at
+     *  http://mrl.nyu.edu/~perlin/noise/. It is quite distinct from the other two, and the noise is
+     *  a 2D slice of a 3D noise texture. Minor changes to the Z coordinate will result in minor
+     *  changes to the noise, making it suitable for animated noise.
+     */
+    enum Type {
+        kFractalNoise_Type,
+        kTurbulence_Type,
+        kImprovedNoise_Type,
+        kFirstType = kFractalNoise_Type,
+        kLastType = kImprovedNoise_Type
+    };
+
+    SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type, SkScalar baseFrequencyX,
+                      SkScalar baseFrequencyY, int numOctaves, SkScalar seed,
+                      const SkISize* tileSize);
+
+    class PerlinNoiseShaderContext : public Context {
+    public:
+        PerlinNoiseShaderContext(const SkPerlinNoiseShaderImpl& shader, const ContextRec&);
+
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
+
+    private:
+        SkPMColor shade(const SkPoint& point, StitchData& stitchData) const;
+        SkScalar calculateTurbulenceValueForPoint(
+                                                  int channel,
+                                                  StitchData& stitchData, const SkPoint& point) const;
+        SkScalar calculateImprovedNoiseValueForPoint(int channel, const SkPoint& point) const;
+        SkScalar noise2D(int channel,
+                         const StitchData& stitchData, const SkPoint& noiseVector) const;
+
+        SkMatrix     fMatrix;
+        PaintingData fPaintingData;
+
+        typedef Context INHERITED;
+    };
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPerlinNoiseShaderImpl)
+
+protected:
+    void flatten(SkWriteBuffer&) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+
+private:
+    const SkPerlinNoiseShaderImpl::Type fType;
+    const SkScalar                  fBaseFrequencyX;
+    const SkScalar                  fBaseFrequencyY;
+    const int                       fNumOctaves;
+    const SkScalar                  fSeed;
+    const SkISize                   fTileSize;
+    const bool                      fStitchTiles;
+
+    friend class ::SkPerlinNoiseShader;
+
+    typedef SkShaderBase INHERITED;
+};
+
+namespace {
+
+// noiseValue is the color component's value (or color)
+// limitValue is the maximum perlin noise array index value allowed
+// newValue is the current noise dimension (either width or height)
+inline int checkNoise(int noiseValue, int limitValue, int newValue) {
+    // If the noise value would bring us out of bounds of the current noise array while we are
+    // stiching noise tiles together, wrap the noise around the current dimension of the noise to
+    // stay within the array bounds in a continuous fashion (so that tiling lines are not visible)
+    if (noiseValue >= limitValue) {
+        noiseValue -= newValue;
+    }
+    return noiseValue;
+}
+
+inline SkScalar smoothCurve(SkScalar t) {
+    return t * t * (3 - 2 * t);
+}
+
+} // end namespace
+
+SkPerlinNoiseShaderImpl::SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type,
+                                         SkScalar baseFrequencyX,
+                                         SkScalar baseFrequencyY,
+                                         int numOctaves,
+                                         SkScalar seed,
+                                         const SkISize* tileSize)
+  : fType(type)
+  , fBaseFrequencyX(baseFrequencyX)
+  , fBaseFrequencyY(baseFrequencyY)
+  , fNumOctaves(numOctaves > 255 ? 255 : numOctaves/*[0,255] octaves allowed*/)
+  , fSeed(seed)
+  , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize)
+  , fStitchTiles(!fTileSize.isEmpty())
+{
+    SkASSERT(numOctaves >= 0 && numOctaves < 256);
+}
+
+sk_sp<SkFlattenable> SkPerlinNoiseShaderImpl::CreateProc(SkReadBuffer& buffer) {
+    Type type = (Type)buffer.readInt();
+    SkScalar freqX = buffer.readScalar();
+    SkScalar freqY = buffer.readScalar();
+    int octaves = buffer.readInt();
+    SkScalar seed = buffer.readScalar();
+    SkISize tileSize;
+    tileSize.fWidth = buffer.readInt();
+    tileSize.fHeight = buffer.readInt();
+
+    switch (type) {
+        case kFractalNoise_Type:
+            return SkPerlinNoiseShader::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize);
+        case kTurbulence_Type:
+            return SkPerlinNoiseShader::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize);
+        case kImprovedNoise_Type:
+            return SkPerlinNoiseShader::MakeImprovedNoise(freqX, freqY, octaves, seed);
+        default:
+            return nullptr;
+    }
+}
+
+void SkPerlinNoiseShaderImpl::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeInt((int) fType);
+    buffer.writeScalar(fBaseFrequencyX);
+    buffer.writeScalar(fBaseFrequencyY);
+    buffer.writeInt(fNumOctaves);
+    buffer.writeScalar(fSeed);
+    buffer.writeInt(fTileSize.fWidth);
+    buffer.writeInt(fTileSize.fHeight);
+}
+
+SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::noise2D(
+        int channel, const StitchData& stitchData, const SkPoint& noiseVector) const {
+    struct Noise {
+        int noisePositionIntegerValue;
+        int nextNoisePositionIntegerValue;
+        SkScalar noisePositionFractionValue;
+        Noise(SkScalar component)
+        {
+            SkScalar position = component + kPerlinNoise;
+            noisePositionIntegerValue = SkScalarFloorToInt(position);
+            noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue);
+            nextNoisePositionIntegerValue = noisePositionIntegerValue + 1;
+        }
+    };
+    Noise noiseX(noiseVector.x());
+    Noise noiseY(noiseVector.y());
+    SkScalar u, v;
+    const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader);
+    // If stitching, adjust lattice points accordingly.
+    if (perlinNoiseShader.fStitchTiles) {
+        noiseX.noisePositionIntegerValue =
+            checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
+        noiseY.noisePositionIntegerValue =
+            checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
+        noiseX.nextNoisePositionIntegerValue =
+            checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
+        noiseY.nextNoisePositionIntegerValue =
+            checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
+    }
+    noiseX.noisePositionIntegerValue &= kBlockMask;
+    noiseY.noisePositionIntegerValue &= kBlockMask;
+    noiseX.nextNoisePositionIntegerValue &= kBlockMask;
+    noiseY.nextNoisePositionIntegerValue &= kBlockMask;
+    int i = fPaintingData.fLatticeSelector[noiseX.noisePositionIntegerValue];
+    int j = fPaintingData.fLatticeSelector[noiseX.nextNoisePositionIntegerValue];
+    int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask;
+    int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask;
+    int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
+    int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
+    SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue);
+    SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue);
+
+    if (sx < 0 || sy < 0 || sx > 1 || sy > 1) {
+        return 0;  // Check for pathological inputs.
+    }
+    
+    // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
+    SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue,
+                                          noiseY.noisePositionFractionValue); // Offset (0,0)
+    u = fPaintingData.fGradient[channel][b00].dot(fractionValue);
+    fractionValue.fX -= SK_Scalar1; // Offset (-1,0)
+    v = fPaintingData.fGradient[channel][b10].dot(fractionValue);
+    SkScalar a = SkScalarInterp(u, v, sx);
+    fractionValue.fY -= SK_Scalar1; // Offset (-1,-1)
+    v = fPaintingData.fGradient[channel][b11].dot(fractionValue);
+    fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1)
+    u = fPaintingData.fGradient[channel][b01].dot(fractionValue);
+    SkScalar b = SkScalarInterp(u, v, sx);
+    return SkScalarInterp(a, b, sy);
+}
+
+SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint(
+        int channel, StitchData& stitchData, const SkPoint& point) const {
+    const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader);
+    if (perlinNoiseShader.fStitchTiles) {
+        // Set up TurbulenceInitial stitch values.
+        stitchData = fPaintingData.fStitchDataInit;
+    }
+    SkScalar turbulenceFunctionResult = 0;
+    SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData.fBaseFrequency.fX,
+                                      point.y() * fPaintingData.fBaseFrequency.fY));
+    SkScalar ratio = SK_Scalar1;
+    for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
+        SkScalar noise = noise2D(channel, stitchData, noiseVector);
+        SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ?
+                            noise : SkScalarAbs(noise);
+        turbulenceFunctionResult += numer / ratio;
+        noiseVector.fX *= 2;
+        noiseVector.fY *= 2;
+        ratio *= 2;
+        if (perlinNoiseShader.fStitchTiles) {
+            // Update stitch values
+            stitchData.fWidth  *= 2;
+            stitchData.fWrapX   = stitchData.fWidth + kPerlinNoise;
+            stitchData.fHeight *= 2;
+            stitchData.fWrapY   = stitchData.fHeight + kPerlinNoise;
+        }
+    }
+
+    // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
+    // by fractalNoise and (turbulenceFunctionResult) by turbulence.
+    if (perlinNoiseShader.fType == kFractalNoise_Type) {
+        turbulenceFunctionResult = SkScalarHalf(turbulenceFunctionResult + 1);
+    }
+
+    if (channel == 3) { // Scale alpha by paint value
+        turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255;
+    }
+
+    // Clamp result
+    return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Improved Perlin Noise based on Java implementation found at http://mrl.nyu.edu/~perlin/noise/
+static SkScalar fade(SkScalar t) {
+    return t * t * t * (t * (t * 6 - 15) + 10);
+}
+
+static SkScalar lerp(SkScalar t, SkScalar a, SkScalar b) {
+    return a + t * (b - a);
+}
+
+static SkScalar grad(int hash, SkScalar x, SkScalar y, SkScalar z) {
+    int h = hash & 15;
+    SkScalar u = h < 8 ? x : y;
+    SkScalar v = h < 4 ? y : h == 12 || h == 14 ? x : z;
+    return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
+}
+
+SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateImprovedNoiseValueForPoint(
+        int channel, const SkPoint& point) const {
+    const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader);
+    SkScalar x = point.fX * perlinNoiseShader.fBaseFrequencyX;
+    SkScalar y = point.fY * perlinNoiseShader.fBaseFrequencyY;
+    // z offset between different channels, chosen arbitrarily
+    static const SkScalar CHANNEL_DELTA = 1000.0f;
+    SkScalar z = channel * CHANNEL_DELTA + perlinNoiseShader.fSeed;
+    SkScalar result = 0;
+    SkScalar ratio = SK_Scalar1;
+    for (int i = 0; i < perlinNoiseShader.fNumOctaves; i++) {
+        int X = SkScalarFloorToInt(x) & 255;
+        int Y = SkScalarFloorToInt(y) & 255;
+        int Z = SkScalarFloorToInt(z) & 255;
+        SkScalar px = x - SkScalarFloorToScalar(x);
+        SkScalar py = y - SkScalarFloorToScalar(y);
+        SkScalar pz = z - SkScalarFloorToScalar(z);
+        SkScalar u = fade(px);
+        SkScalar v = fade(py);
+        SkScalar w = fade(pz);
+        uint8_t* permutations = improved_noise_permutations;
+        int A  = permutations[X] + Y;
+        int AA = permutations[A] + Z;
+        int AB = permutations[A + 1] + Z;
+        int B  = permutations[X + 1] + Y;
+        int BA = permutations[B] + Z;
+        int BB = permutations[B + 1] + Z;
+        result += lerp(w, lerp(v, lerp(u, grad(permutations[AA    ], px    , py    , pz    ),
+                                          grad(permutations[BA    ], px - 1, py    , pz    )),
+                                  lerp(u, grad(permutations[AB    ], px    , py - 1, pz    ),
+                                          grad(permutations[BB    ], px - 1, py - 1, pz    ))),
+                          lerp(v, lerp(u, grad(permutations[AA + 1], px    , py    , pz - 1),
+                                          grad(permutations[BA + 1], px - 1, py    , pz - 1)),
+                                  lerp(u, grad(permutations[AB + 1], px    , py - 1, pz - 1),
+                                          grad(permutations[BB + 1], px - 1, py - 1, pz - 1)))) /
+                   ratio;
+        x *= 2;
+        y *= 2;
+        ratio *= 2;
+    }
+    result = SkScalarClampMax((result + 1.0f) / 2.0f, 1.0f);
+    return result;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkPMColor SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shade(
+        const SkPoint& point, StitchData& stitchData) const {
+    const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader);
+    SkPoint newPoint;
+    fMatrix.mapPoints(&newPoint, &point, 1);
+    newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
+    newPoint.fY = SkScalarRoundToScalar(newPoint.fY);
+
+    U8CPU rgba[4];
+    for (int channel = 3; channel >= 0; --channel) {
+        SkScalar value;
+        if (perlinNoiseShader.fType == kImprovedNoise_Type) {
+            value = calculateImprovedNoiseValueForPoint(channel, newPoint);
+        }
+        else {
+            value = calculateTurbulenceValueForPoint(channel, stitchData, newPoint);
+        }
+        rgba[channel] = SkScalarFloorToInt(255 * value);
+    }
+    return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
+}
+
+SkShaderBase::Context* SkPerlinNoiseShaderImpl::onMakeContext(const ContextRec& rec,
+                                                           SkArenaAlloc* alloc) const {
+    return alloc->make<PerlinNoiseShaderContext>(*this, rec);
+}
+
+static inline SkMatrix total_matrix(const SkShaderBase::ContextRec& rec,
+                                    const SkShaderBase& shader) {
+    SkMatrix matrix = SkMatrix::Concat(*rec.fMatrix, shader.getLocalMatrix());
+    if (rec.fLocalMatrix) {
+        matrix.preConcat(*rec.fLocalMatrix);
+    }
+
+    return matrix;
+}
+
+SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::PerlinNoiseShaderContext(
+        const SkPerlinNoiseShaderImpl& shader, const ContextRec& rec)
+    : INHERITED(shader, rec)
+    , fMatrix(total_matrix(rec, shader)) // used for temp storage, adjusted below
+    , fPaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX,
+                    shader.fBaseFrequencyY, fMatrix)
+{
+    // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
+    // (as opposed to 0 based, usually). The same adjustment is in the setData() function.
+    fMatrix.setTranslate(-fMatrix.getTranslateX() + SK_Scalar1,
+                         -fMatrix.getTranslateY() + SK_Scalar1);
+}
+
+void SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shadeSpan(
+        int x, int y, SkPMColor result[], int count) {
+    SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
+    StitchData stitchData;
+    for (int i = 0; i < count; ++i) {
+        result[i] = shade(point, stitchData);
+        point.fX += SK_Scalar1;
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+class GrGLPerlinNoise : public GrGLSLFragmentProcessor {
+public:
+    void emitCode(EmitArgs&) override;
+
+    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+private:
+    GrGLSLProgramDataManager::UniformHandle fStitchDataUni;
+    GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
+
+    typedef GrGLSLFragmentProcessor INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrPerlinNoise2Effect : public GrFragmentProcessor {
+public:
+    static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
+                                           SkPerlinNoiseShaderImpl::Type type,
+                                           int numOctaves, bool stitchTiles,
+                                           std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData,
+                                           sk_sp<GrTextureProxy> permutationsProxy,
+                                           sk_sp<GrTextureProxy> noiseProxy,
+                                           const SkMatrix& matrix) {
+        return sk_sp<GrFragmentProcessor>(
+            new GrPerlinNoise2Effect(resourceProvider, type, numOctaves, stitchTiles,
+                                     std::move(paintingData),
+                                     std::move(permutationsProxy), std::move(noiseProxy), matrix));
+    }
+
+    const char* name() const override { return "PerlinNoise"; }
+
+    const SkPerlinNoiseShaderImpl::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; }
+
+    SkPerlinNoiseShaderImpl::Type type() const { return fType; }
+    bool stitchTiles() const { return fStitchTiles; }
+    const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
+    int numOctaves() const { return fNumOctaves; }
+    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+        return new GrGLPerlinNoise;
+    }
+
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                       GrProcessorKeyBuilder* b) const override {
+        GrGLPerlinNoise::GenKey(*this, caps, b);
+    }
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const GrPerlinNoise2Effect& s = sBase.cast<GrPerlinNoise2Effect>();
+        return fType == s.fType &&
+               fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency &&
+               fNumOctaves == s.fNumOctaves &&
+               fStitchTiles == s.fStitchTiles &&
+               fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
+    }
+
+    GrPerlinNoise2Effect(GrResourceProvider* resourceProvider,
+                         SkPerlinNoiseShaderImpl::Type type, int numOctaves, bool stitchTiles,
+                         std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData,
+                         sk_sp<GrTextureProxy> permutationsProxy,
+                         sk_sp<GrTextureProxy> noiseProxy,
+                         const SkMatrix& matrix)
+            : INHERITED(kNone_OptimizationFlags)
+            , fType(type)
+            , fNumOctaves(numOctaves)
+            , fStitchTiles(stitchTiles)
+            , fPermutationsSampler(resourceProvider, std::move(permutationsProxy))
+            , fNoiseSampler(resourceProvider, std::move(noiseProxy))
+            , fPaintingData(std::move(paintingData)) {
+        this->initClassID<GrPerlinNoise2Effect>();
+        this->addTextureSampler(&fPermutationsSampler);
+        this->addTextureSampler(&fNoiseSampler);
+        fCoordTransform.reset(matrix);
+        this->addCoordTransform(&fCoordTransform);
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    SkPerlinNoiseShaderImpl::Type           fType;
+    GrCoordTransform                    fCoordTransform;
+    int                                 fNumOctaves;
+    bool                                fStitchTiles;
+    TextureSampler                      fPermutationsSampler;
+    TextureSampler                      fNoiseSampler;
+    std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> fPaintingData;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoise2Effect);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> GrPerlinNoise2Effect::TestCreate(GrProcessorTestData* d) {
+    int      numOctaves = d->fRandom->nextRangeU(2, 10);
+    bool     stitchTiles = d->fRandom->nextBool();
+    SkScalar seed = SkIntToScalar(d->fRandom->nextU());
+    SkISize  tileSize = SkISize::Make(d->fRandom->nextRangeU(4, 4096),
+                                      d->fRandom->nextRangeU(4, 4096));
+    SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f,
+                                                          0.99f);
+    SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f,
+                                                          0.99f);
+
+    sk_sp<SkShader> shader(d->fRandom->nextBool() ?
+        SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed,
+                                               stitchTiles ? &tileSize : nullptr) :
+        SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed,
+                                             stitchTiles ? &tileSize : nullptr));
+
+    GrTest::TestAsFPArgs asFPArgs(d);
+    return as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+}
+#endif
+
+void GrGLPerlinNoise::emitCode(EmitArgs& args) {
+    const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>();
+
+    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+
+    fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                                   kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                                   "baseFrequency");
+    const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
+
+    const char* stitchDataUni = nullptr;
+    if (pne.stitchTiles()) {
+        fStitchDataUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                                    kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                                    "stitchData");
+        stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni);
+    }
+
+    // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8
+    const char* chanCoordR  = "0.125";
+    const char* chanCoordG  = "0.375";
+    const char* chanCoordB  = "0.625";
+    const char* chanCoordA  = "0.875";
+    const char* chanCoord   = "chanCoord";
+    const char* stitchData  = "stitchData";
+    const char* ratio       = "ratio";
+    const char* noiseVec    = "noiseVec";
+    const char* noiseSmooth = "noiseSmooth";
+    const char* floorVal    = "floorVal";
+    const char* fractVal    = "fractVal";
+    const char* uv          = "uv";
+    const char* ab          = "ab";
+    const char* latticeIdx  = "latticeIdx";
+    const char* bcoords     = "bcoords";
+    const char* lattice     = "lattice";
+    const char* inc8bit     = "0.00390625";  // 1.0 / 256.0
+    // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
+    // [-1,1] vector and perform a dot product between that vector and the provided vector.
+    const char* dotLattice  = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);";
+
+    // Add noise function
+    static const GrShaderVar gPerlinNoiseArgs[] =  {
+        GrShaderVar(chanCoord, kFloat_GrSLType),
+        GrShaderVar(noiseVec, kVec2f_GrSLType)
+    };
+
+    static const GrShaderVar gPerlinNoiseStitchArgs[] =  {
+        GrShaderVar(chanCoord, kFloat_GrSLType),
+        GrShaderVar(noiseVec, kVec2f_GrSLType),
+        GrShaderVar(stitchData, kVec2f_GrSLType)
+    };
+
+    SkString noiseCode;
+
+    noiseCode.appendf("\tvec4 %s;\n", floorVal);
+    noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec);
+    noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal);
+    noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec);
+
+    // smooth curve : t * t * (3 - 2 * t)
+    noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);",
+        noiseSmooth, fractVal, fractVal, fractVal);
+
+    // Adjust frequencies if we're stitching tiles
+    if (pne.stitchTiles()) {
+        noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }",
+            floorVal, stitchData, floorVal, stitchData);
+        noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }",
+            floorVal, stitchData, floorVal, stitchData);
+        noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }",
+            floorVal, stitchData, floorVal, stitchData);
+        noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }",
+            floorVal, stitchData, floorVal, stitchData);
+    }
+
+    // Get texture coordinates and normalize
+    noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n",
+        floorVal, floorVal);
+
+    // Get permutation for x
+    {
+        SkString xCoords("");
+        xCoords.appendf("vec2(%s.x, 0.5)", floorVal);
+
+        noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx);
+        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(),
+                                         kVec2f_GrSLType);
+        noiseCode.append(".r;");
+    }
+
+    // Get permutation for x + 1
+    {
+        SkString xCoords("");
+        xCoords.appendf("vec2(%s.z, 0.5)", floorVal);
+
+        noiseCode.appendf("\n\t%s.y = ", latticeIdx);
+        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(),
+                                         kVec2f_GrSLType);
+        noiseCode.append(".r;");
+    }
+
+#if defined(SK_BUILD_FOR_ANDROID)
+    // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3).
+    // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit
+    // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725
+    // (or 0.484368 here). The following rounding operation prevents these precision issues from
+    // affecting the result of the noise by making sure that we only have multiples of 1/255.
+    // (Note that 1/255 is about 0.003921569, which is the value used here).
+    noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);",
+                      latticeIdx, latticeIdx);
+#endif
+
+    // Get (x,y) coordinates with the permutated x
+    noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal);
+
+    noiseCode.appendf("\n\n\tvec2 %s;", uv);
+    // Compute u, at offset (0,0)
+    {
+        SkString latticeCoords("");
+        latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord);
+        noiseCode.appendf("\n\tvec4 %s = ", lattice);
+        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
+                                         kVec2f_GrSLType);
+        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
+        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
+    }
+
+    noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal);
+    // Compute v, at offset (-1,0)
+    {
+        SkString latticeCoords("");
+        latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord);
+        noiseCode.append("\n\tlattice = ");
+        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
+                                         kVec2f_GrSLType);
+        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
+        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
+    }
+
+    // Compute 'a' as a linear interpolation of 'u' and 'v'
+    noiseCode.appendf("\n\tvec2 %s;", ab);
+    noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
+
+    noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal);
+    // Compute v, at offset (-1,-1)
+    {
+        SkString latticeCoords("");
+        latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord);
+        noiseCode.append("\n\tlattice = ");
+        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
+                                         kVec2f_GrSLType);
+        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
+        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
+    }
+
+    noiseCode.appendf("\n\t%s.x += 1.0;", fractVal);
+    // Compute u, at offset (0,-1)
+    {
+        SkString latticeCoords("");
+        latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord);
+        noiseCode.append("\n\tlattice = ");
+        fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(),
+                                         kVec2f_GrSLType);
+        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
+        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
+    }
+
+    // Compute 'b' as a linear interpolation of 'u' and 'v'
+    noiseCode.appendf("\n\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
+    // Compute the noise as a linear interpolation of 'a' and 'b'
+    noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth);
+
+    SkString noiseFuncName;
+    if (pne.stitchTiles()) {
+        fragBuilder->emitFunction(kFloat_GrSLType,
+                                  "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs),
+                                  gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName);
+    } else {
+        fragBuilder->emitFunction(kFloat_GrSLType,
+                                  "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs),
+                                  gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName);
+    }
+
+    // There are rounding errors if the floor operation is not performed here
+    fragBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;",
+                             noiseVec, vCoords.c_str(), baseFrequencyUni);
+
+    // Clear the color accumulator
+    fragBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", args.fOutputColor);
+
+    if (pne.stitchTiles()) {
+        // Set up TurbulenceInitial stitch values.
+        fragBuilder->codeAppendf("\n\t\tvec2 %s = %s;", stitchData, stitchDataUni);
+    }
+
+    fragBuilder->codeAppendf("\n\t\tfloat %s = 1.0;", ratio);
+
+    // Loop over all octaves
+    fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves());
+
+    fragBuilder->codeAppendf("\n\t\t\t%s += ", args.fOutputColor);
+    if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) {
+        fragBuilder->codeAppend("abs(");
+    }
+    if (pne.stitchTiles()) {
+        fragBuilder->codeAppendf(
+            "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s),"
+                 "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))",
+            noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData,
+            noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData,
+            noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData,
+            noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData);
+    } else {
+        fragBuilder->codeAppendf(
+            "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s),"
+                 "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))",
+            noiseFuncName.c_str(), chanCoordR, noiseVec,
+            noiseFuncName.c_str(), chanCoordG, noiseVec,
+            noiseFuncName.c_str(), chanCoordB, noiseVec,
+            noiseFuncName.c_str(), chanCoordA, noiseVec);
+    }
+    if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) {
+        fragBuilder->codeAppendf(")"); // end of "abs("
+    }
+    fragBuilder->codeAppendf(" * %s;", ratio);
+
+    fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec);
+    fragBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio);
+
+    if (pne.stitchTiles()) {
+        fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData);
+    }
+    fragBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves
+
+    if (pne.type() == SkPerlinNoiseShaderImpl::kFractalNoise_Type) {
+        // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
+        // by fractalNoise and (turbulenceFunctionResult) by turbulence.
+        fragBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);",
+                               args.fOutputColor,args.fOutputColor);
+    }
+
+    // Clamp values
+    fragBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor);
+
+    // Pre-multiply the result
+    fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
+                             args.fOutputColor, args.fOutputColor,
+                             args.fOutputColor, args.fOutputColor);
+}
+
+void GrGLPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&,
+                             GrProcessorKeyBuilder* b) {
+    const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>();
+
+    uint32_t key = turbulence.numOctaves();
+
+    key = key << 3; // Make room for next 3 bits
+
+    switch (turbulence.type()) {
+        case SkPerlinNoiseShaderImpl::kFractalNoise_Type:
+            key |= 0x1;
+            break;
+        case SkPerlinNoiseShaderImpl::kTurbulence_Type:
+            key |= 0x2;
+            break;
+        default:
+            // leave key at 0
+            break;
+    }
+
+    if (turbulence.stitchTiles()) {
+        key |= 0x4; // Flip the 3rd bit if tile stitching is on
+    }
+
+    b->add32(key);
+}
+
+void GrGLPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman,
+                                const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+
+    const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>();
+
+    const SkVector& baseFrequency = turbulence.baseFrequency();
+    pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
+
+    if (turbulence.stitchTiles()) {
+        const SkPerlinNoiseShaderImpl::StitchData& stitchData = turbulence.stitchData();
+        pdman.set2f(fStitchDataUni, SkIntToScalar(stitchData.fWidth),
+                                   SkIntToScalar(stitchData.fHeight));
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+class GrGLImprovedPerlinNoise : public GrGLSLFragmentProcessor {
+public:
+    void emitCode(EmitArgs&) override;
+
+    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+private:
+    GrGLSLProgramDataManager::UniformHandle fZUni;
+    GrGLSLProgramDataManager::UniformHandle fOctavesUni;
+    GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
+
+    typedef GrGLSLFragmentProcessor INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrImprovedPerlinNoiseEffect : public GrFragmentProcessor {
+public:
+    static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
+                                           int octaves, SkScalar z,
+                                           std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData,
+                                           sk_sp<GrTextureProxy> permutationsProxy,
+                                           sk_sp<GrTextureProxy> gradientProxy,
+                                           const SkMatrix& matrix) {
+        return sk_sp<GrFragmentProcessor>(
+            new GrImprovedPerlinNoiseEffect(resourceProvider, octaves, z, std::move(paintingData),
+                                            std::move(permutationsProxy),
+                                            std::move(gradientProxy), matrix));
+    }
+
+    const char* name() const override { return "ImprovedPerlinNoise"; }
+
+    const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
+    SkScalar z() const { return fZ; }
+    int octaves() const { return fOctaves; }
+    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+        return new GrGLImprovedPerlinNoise;
+    }
+
+    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
+        GrGLImprovedPerlinNoise::GenKey(*this, caps, b);
+    }
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const GrImprovedPerlinNoiseEffect& s = sBase.cast<GrImprovedPerlinNoiseEffect>();
+        return fZ == fZ &&
+               fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency;
+    }
+
+    GrImprovedPerlinNoiseEffect(GrResourceProvider* resourceProvider,
+                                int octaves, SkScalar z,
+                                std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData,
+                                sk_sp<GrTextureProxy> permutationsProxy,
+                                sk_sp<GrTextureProxy> gradientProxy,
+                                const SkMatrix& matrix)
+            : INHERITED(kNone_OptimizationFlags)
+            , fOctaves(octaves)
+            , fZ(z)
+            , fPermutationsSampler(resourceProvider, std::move(permutationsProxy))
+            , fGradientSampler(resourceProvider, std::move(gradientProxy))
+            , fPaintingData(std::move(paintingData)) {
+        this->initClassID<GrImprovedPerlinNoiseEffect>();
+        this->addTextureSampler(&fPermutationsSampler);
+        this->addTextureSampler(&fGradientSampler);
+        fCoordTransform.reset(matrix);
+        this->addCoordTransform(&fCoordTransform);
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    GrCoordTransform                    fCoordTransform;
+    int                                 fOctaves;
+    SkScalar                            fZ;
+    TextureSampler                      fPermutationsSampler;
+    TextureSampler                      fGradientSampler;
+    std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> fPaintingData;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrImprovedPerlinNoiseEffect);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> GrImprovedPerlinNoiseEffect::TestCreate(GrProcessorTestData* d) {
+    SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f,
+                                                          0.99f);
+    SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f,
+                                                          0.99f);
+    int numOctaves = d->fRandom->nextRangeU(2, 10);
+    SkScalar z = SkIntToScalar(d->fRandom->nextU());
+
+    sk_sp<SkShader> shader(SkPerlinNoiseShader::MakeImprovedNoise(baseFrequencyX,
+                                                                   baseFrequencyY,
+                                                                   numOctaves,
+                                                                   z));
+
+    GrTest::TestAsFPArgs asFPArgs(d);
+    return as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+}
+#endif
+
+void GrGLImprovedPerlinNoise::emitCode(EmitArgs& args) {
+    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+
+    fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                                   kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                                   "baseFrequency");
+    const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
+
+    fOctavesUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                             kFloat_GrSLType, kDefault_GrSLPrecision,
+                                             "octaves");
+    const char* octavesUni = uniformHandler->getUniformCStr(fOctavesUni);
+
+    fZUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                       kFloat_GrSLType, kDefault_GrSLPrecision,
+                                       "z");
+    const char* zUni = uniformHandler->getUniformCStr(fZUni);
+
+    // fade function
+    static const GrShaderVar fadeArgs[] =  {
+        GrShaderVar("t", kVec3f_GrSLType)
+    };
+    SkString fadeFuncName;
+    fragBuilder->emitFunction(kVec3f_GrSLType, "fade", SK_ARRAY_COUNT(fadeArgs),
+                              fadeArgs,
+                              "return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);",
+                              &fadeFuncName);
+
+    // perm function
+    static const GrShaderVar permArgs[] =  {
+        GrShaderVar("x", kFloat_GrSLType)
+    };
+    SkString permFuncName;
+    SkString permCode("return ");
+    // FIXME even though I'm creating these textures with kRepeat_TileMode, they're clamped. Not
+    // sure why. Using fract() (here and the next texture lookup) as a workaround.
+    fragBuilder->appendTextureLookup(&permCode, args.fTexSamplers[0], "vec2(fract(x / 256.0), 0.0)",
+                                     kVec2f_GrSLType);
+    permCode.append(".r * 255.0;");
+    fragBuilder->emitFunction(kFloat_GrSLType, "perm", SK_ARRAY_COUNT(permArgs), permArgs,
+                              permCode.c_str(), &permFuncName);
+
+    // grad function
+    static const GrShaderVar gradArgs[] =  {
+        GrShaderVar("x", kFloat_GrSLType),
+        GrShaderVar("p", kVec3f_GrSLType)
+    };
+    SkString gradFuncName;
+    SkString gradCode("return dot(");
+    fragBuilder->appendTextureLookup(&gradCode, args.fTexSamplers[1], "vec2(fract(x / 16.0), 0.0)",
+                                     kVec2f_GrSLType);
+    gradCode.append(".rgb * 255.0 - vec3(1.0), p);");
+    fragBuilder->emitFunction(kFloat_GrSLType, "grad", SK_ARRAY_COUNT(gradArgs), gradArgs,
+                              gradCode.c_str(), &gradFuncName);
+
+    // lerp function
+    static const GrShaderVar lerpArgs[] =  {
+        GrShaderVar("a", kFloat_GrSLType),
+        GrShaderVar("b", kFloat_GrSLType),
+        GrShaderVar("w", kFloat_GrSLType)
+    };
+    SkString lerpFuncName;
+    fragBuilder->emitFunction(kFloat_GrSLType, "lerp", SK_ARRAY_COUNT(lerpArgs), lerpArgs,
+                              "return a + w * (b - a);", &lerpFuncName);
+
+    // noise function
+    static const GrShaderVar noiseArgs[] =  {
+        GrShaderVar("p", kVec3f_GrSLType),
+    };
+    SkString noiseFuncName;
+    SkString noiseCode;
+    noiseCode.append("vec3 P = mod(floor(p), 256.0);");
+    noiseCode.append("p -= floor(p);");
+    noiseCode.appendf("vec3 f = %s(p);", fadeFuncName.c_str());
+    noiseCode.appendf("float A = %s(P.x) + P.y;", permFuncName.c_str());
+    noiseCode.appendf("float AA = %s(A) + P.z;", permFuncName.c_str());
+    noiseCode.appendf("float AB = %s(A + 1.0) + P.z;", permFuncName.c_str());
+    noiseCode.appendf("float B =  %s(P.x + 1.0) + P.y;", permFuncName.c_str());
+    noiseCode.appendf("float BA = %s(B) + P.z;", permFuncName.c_str());
+    noiseCode.appendf("float BB = %s(B + 1.0) + P.z;", permFuncName.c_str());
+    noiseCode.appendf("float result = %s(", lerpFuncName.c_str());
+    noiseCode.appendf("%s(%s(%s(%s(AA), p),", lerpFuncName.c_str(), lerpFuncName.c_str(),
+                      gradFuncName.c_str(), permFuncName.c_str());
+    noiseCode.appendf("%s(%s(BA), p + vec3(-1.0, 0.0, 0.0)), f.x),", gradFuncName.c_str(),
+                      permFuncName.c_str());
+    noiseCode.appendf("%s(%s(%s(AB), p + vec3(0.0, -1.0, 0.0)),", lerpFuncName.c_str(),
+                      gradFuncName.c_str(), permFuncName.c_str());
+    noiseCode.appendf("%s(%s(BB), p + vec3(-1.0, -1.0, 0.0)), f.x), f.y),",
+                      gradFuncName.c_str(), permFuncName.c_str());
+    noiseCode.appendf("%s(%s(%s(%s(AA + 1.0), p + vec3(0.0, 0.0, -1.0)),",
+                      lerpFuncName.c_str(), lerpFuncName.c_str(), gradFuncName.c_str(),
+                      permFuncName.c_str());
+    noiseCode.appendf("%s(%s(BA + 1.0), p + vec3(-1.0, 0.0, -1.0)), f.x),",
+                      gradFuncName.c_str(), permFuncName.c_str());
+    noiseCode.appendf("%s(%s(%s(AB + 1.0), p + vec3(0.0, -1.0, -1.0)),",
+                      lerpFuncName.c_str(), gradFuncName.c_str(), permFuncName.c_str());
+    noiseCode.appendf("%s(%s(BB + 1.0), p + vec3(-1.0, -1.0, -1.0)), f.x), f.y), f.z);",
+                      gradFuncName.c_str(), permFuncName.c_str());
+    noiseCode.append("return result;");
+    fragBuilder->emitFunction(kFloat_GrSLType, "noise", SK_ARRAY_COUNT(noiseArgs), noiseArgs,
+                              noiseCode.c_str(), &noiseFuncName);
+
+    // noiseOctaves function
+    static const GrShaderVar noiseOctavesArgs[] =  {
+        GrShaderVar("p", kVec3f_GrSLType),
+        GrShaderVar("octaves", kFloat_GrSLType),
+    };
+    SkString noiseOctavesFuncName;
+    SkString noiseOctavesCode;
+    noiseOctavesCode.append("float result = 0.0;");
+    noiseOctavesCode.append("float ratio = 1.0;");
+    noiseOctavesCode.append("for (float i = 0.0; i < octaves; i++) {");
+    noiseOctavesCode.appendf("result += %s(p) / ratio;", noiseFuncName.c_str());
+    noiseOctavesCode.append("p *= 2.0;");
+    noiseOctavesCode.append("ratio *= 2.0;");
+    noiseOctavesCode.append("}");
+    noiseOctavesCode.append("return (result + 1.0) / 2.0;");
+    fragBuilder->emitFunction(kFloat_GrSLType, "noiseOctaves", SK_ARRAY_COUNT(noiseOctavesArgs),
+                              noiseOctavesArgs, noiseOctavesCode.c_str(), &noiseOctavesFuncName);
+
+    fragBuilder->codeAppendf("vec2 coords = %s * %s;", vCoords.c_str(), baseFrequencyUni);
+    fragBuilder->codeAppendf("float r = %s(vec3(coords, %s), %s);", noiseOctavesFuncName.c_str(),
+                             zUni, octavesUni);
+    fragBuilder->codeAppendf("float g = %s(vec3(coords, %s + 0000.0), %s);",
+                             noiseOctavesFuncName.c_str(), zUni, octavesUni);
+    fragBuilder->codeAppendf("float b = %s(vec3(coords, %s + 0000.0), %s);",
+                             noiseOctavesFuncName.c_str(), zUni, octavesUni);
+    fragBuilder->codeAppendf("float a = %s(vec3(coords, %s + 0000.0), %s);",
+                             noiseOctavesFuncName.c_str(), zUni, octavesUni);
+    fragBuilder->codeAppendf("%s = vec4(r, g, b, a);", args.fOutputColor);
+
+    // Clamp values
+    fragBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor);
+
+    // Pre-multiply the result
+    fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
+                             args.fOutputColor, args.fOutputColor,
+                             args.fOutputColor, args.fOutputColor);
+}
+
+void GrGLImprovedPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&,
+                                     GrProcessorKeyBuilder* b) {
+}
+
+void GrGLImprovedPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman,
+                                        const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+
+    const GrImprovedPerlinNoiseEffect& noise = processor.cast<GrImprovedPerlinNoiseEffect>();
+
+    const SkVector& baseFrequency = noise.baseFrequency();
+    pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
+
+    pdman.set1f(fOctavesUni, SkIntToScalar(noise.octaves()));
+
+    pdman.set1f(fZUni, noise.z());
+}
+
+/////////////////////////////////////////////////////////////////////
+sk_sp<GrFragmentProcessor> SkPerlinNoiseShaderImpl::asFragmentProcessor(const AsFPArgs& args) const {
+    SkASSERT(args.fContext);
+
+    SkMatrix localMatrix = this->getLocalMatrix();
+    if (args.fLocalMatrix) {
+        localMatrix.preConcat(*args.fLocalMatrix);
+    }
+
+    SkMatrix matrix = *args.fViewMatrix;
+    matrix.preConcat(localMatrix);
+
+    // Either we don't stitch tiles, either we have a valid tile size
+    SkASSERT(!fStitchTiles || !fTileSize.isEmpty());
+
+    std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData =
+        skstd::make_unique<SkPerlinNoiseShaderImpl::PaintingData>(fTileSize,
+                                                                  fSeed,
+                                                                  fBaseFrequencyX,
+                                                                  fBaseFrequencyY,
+                                                                  matrix);
+
+    SkMatrix m = *args.fViewMatrix;
+    m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1);
+    m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1);
+
+    if (fType == kImprovedNoise_Type) {
+        GrSamplerParams textureParams(SkShader::TileMode::kRepeat_TileMode,
+                                      GrSamplerParams::FilterMode::kNone_FilterMode);
+        sk_sp<GrTextureProxy> permutationsTexture(
+            GrRefCachedBitmapTextureProxy(args.fContext,
+                                          paintingData->getImprovedPermutationsBitmap(),
+                                          textureParams, nullptr));
+        sk_sp<GrTextureProxy> gradientTexture(
+            GrRefCachedBitmapTextureProxy(args.fContext,
+                                          paintingData->getGradientBitmap(),
+                                          textureParams, nullptr));
+        return GrImprovedPerlinNoiseEffect::Make(args.fContext->resourceProvider(),
+                                                 fNumOctaves, fSeed, std::move(paintingData),
+                                                 std::move(permutationsTexture),
+                                                 std::move(gradientTexture), m);
+    }
+
+    if (0 == fNumOctaves) {
+        if (kFractalNoise_Type == fType) {
+            // Extract the incoming alpha and emit rgba = (a/4, a/4, a/4, a/2)
+            // TODO: Either treat the output of this shader as sRGB or allow client to specify a
+            // color space of the noise. Either way, this case (and the GLSL) need to convert to
+            // the destination.
+            sk_sp<GrFragmentProcessor> inner(
+                GrConstColorProcessor::Make(GrColor4f::FromGrColor(0x80404040),
+                                            GrConstColorProcessor::kModulateRGBA_InputMode));
+            return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+        }
+        // Emit zero.
+        return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
+                                           GrConstColorProcessor::kIgnore_InputMode);
+    }
+
+    sk_sp<GrTextureProxy> permutationsProxy = GrMakeCachedBitmapProxy(
+                                                         args.fContext->resourceProvider(),
+                                                         paintingData->getPermutationsBitmap());
+    sk_sp<GrTextureProxy> noiseProxy = GrMakeCachedBitmapProxy(args.fContext->resourceProvider(),
+                                                               paintingData->getNoiseBitmap());
+
+    if (permutationsProxy && noiseProxy) {
+        sk_sp<GrFragmentProcessor> inner(
+            GrPerlinNoise2Effect::Make(args.fContext->resourceProvider(),
+                                       fType,
+                                       fNumOctaves,
+                                       fStitchTiles,
+                                       std::move(paintingData),
+                                       std::move(permutationsProxy),
+                                       std::move(noiseProxy),
+                                       m));
+        return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+    }
+    return nullptr;
+}
+
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkPerlinNoiseShaderImpl::toString(SkString* str) const {
+    str->append("SkPerlinNoiseShaderImpl: (");
+
+    str->append("type: ");
+    switch (fType) {
+        case kFractalNoise_Type:
+            str->append("\"fractal noise\"");
+            break;
+        case kTurbulence_Type:
+            str->append("\"turbulence\"");
+            break;
+        default:
+            str->append("\"unknown\"");
+            break;
+    }
+    str->append(" base frequency: (");
+    str->appendScalar(fBaseFrequencyX);
+    str->append(", ");
+    str->appendScalar(fBaseFrequencyY);
+    str->append(") number of octaves: ");
+    str->appendS32(fNumOctaves);
+    str->append(" seed: ");
+    str->appendScalar(fSeed);
+    str->append(" stitch tiles: ");
+    str->append(fStitchTiles ? "true " : "false ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkShader> SkPerlinNoiseShader::MakeFractalNoise(SkScalar baseFrequencyX,
+                                                      SkScalar baseFrequencyY,
+                                                      int numOctaves, SkScalar seed,
+                                                      const SkISize* tileSize) {
+    return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kFractalNoise_Type,
+                                                 baseFrequencyX, baseFrequencyY, numOctaves, seed,
+                                                 tileSize));
+}
+
+sk_sp<SkShader> SkPerlinNoiseShader::MakeTurbulence(SkScalar baseFrequencyX,
+                                                    SkScalar baseFrequencyY,
+                                                    int numOctaves, SkScalar seed,
+                                                    const SkISize* tileSize) {
+    return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kTurbulence_Type,
+                                                 baseFrequencyX, baseFrequencyY, numOctaves, seed,
+                                                 tileSize));
+}
+
+sk_sp<SkShader> SkPerlinNoiseShader::MakeImprovedNoise(SkScalar baseFrequencyX,
+                                                       SkScalar baseFrequencyY,
+                                                       int numOctaves, SkScalar z) {
+    return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kImprovedNoise_Type,
+                                                 baseFrequencyX, baseFrequencyY, numOctaves, z,
+                                                 nullptr));
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkPerlinNoiseShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShaderImpl)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp
new file mode 100644
index 0000000..d6ee941
--- /dev/null
+++ b/src/shaders/SkPictureShader.cpp
@@ -0,0 +1,364 @@
+/*
+ * 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 "SkPictureShader.h"
+
+#include "SkArenaAlloc.h"
+#include "SkBitmap.h"
+#include "SkBitmapProcShader.h"
+#include "SkCanvas.h"
+#include "SkColorSpaceXformCanvas.h"
+#include "SkImage.h"
+#include "SkImageShader.h"
+#include "SkMatrixUtils.h"
+#include "SkPicture.h"
+#include "SkPictureImageGenerator.h"
+#include "SkReadBuffer.h"
+#include "SkResourceCache.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrCaps.h"
+#include "GrFragmentProcessor.h"
+#endif
+
+namespace {
+static unsigned gBitmapSkaderKeyNamespaceLabel;
+
+struct BitmapShaderKey : public SkResourceCache::Key {
+public:
+    BitmapShaderKey(sk_sp<SkColorSpace> colorSpace,
+                    uint32_t pictureID,
+                    const SkRect& tile,
+                    SkShader::TileMode tmx,
+                    SkShader::TileMode tmy,
+                    const SkSize& scale,
+                    const SkMatrix& localMatrix,
+                    SkTransferFunctionBehavior blendBehavior)
+        : fColorSpace(std::move(colorSpace))
+        , fPictureID(pictureID)
+        , fTile(tile)
+        , fTmx(tmx)
+        , fTmy(tmy)
+        , fScale(scale)
+        , fBlendBehavior(blendBehavior) {
+
+        for (int i = 0; i < 9; ++i) {
+            fLocalMatrixStorage[i] = localMatrix[i];
+        }
+
+        static const size_t keySize = sizeof(fColorSpace) +
+                                      sizeof(fPictureID) +
+                                      sizeof(fTile) +
+                                      sizeof(fTmx) + sizeof(fTmy) +
+                                      sizeof(fScale) +
+                                      sizeof(fLocalMatrixStorage) +
+                                      sizeof(fBlendBehavior);
+        // This better be packed.
+        SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - (uint32_t*)&fColorSpace) == keySize);
+        this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize);
+    }
+
+private:
+    sk_sp<SkColorSpace>        fColorSpace;
+    uint32_t                   fPictureID;
+    SkRect                     fTile;
+    SkShader::TileMode         fTmx, fTmy;
+    SkSize                     fScale;
+    SkScalar                   fLocalMatrixStorage[9];
+    SkTransferFunctionBehavior fBlendBehavior;
+
+    SkDEBUGCODE(uint32_t fEndOfStruct;)
+};
+
+struct BitmapShaderRec : public SkResourceCache::Rec {
+    BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
+        : fKey(key)
+        , fShader(SkRef(tileShader)) {}
+
+    BitmapShaderKey fKey;
+    sk_sp<SkShader> fShader;
+    size_t          fBitmapBytes;
+
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override {
+        // Just the record overhead -- the actual pixels are accounted by SkImageCacherator.
+        return sizeof(fKey) + sizeof(SkImageShader);
+    }
+    const char* getCategory() const override { return "bitmap-shader"; }
+    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
+
+    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
+        const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
+        sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
+
+        *result = rec.fShader;
+
+        // The bitmap shader is backed by an image generator, thus it can always re-generate its
+        // pixels if discarded.
+        return true;
+    }
+};
+
+} // namespace
+
+SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
+                                 const SkMatrix* localMatrix, const SkRect* tile,
+                                 sk_sp<SkColorSpace> colorSpace)
+    : INHERITED(localMatrix)
+    , fPicture(std::move(picture))
+    , fTile(tile ? *tile : fPicture->cullRect())
+    , fTmx(tmx)
+    , fTmy(tmy)
+    , fColorSpace(std::move(colorSpace))
+{}
+
+sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
+                                      const SkMatrix* localMatrix, const SkRect* tile) {
+    if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
+        return SkShader::MakeEmptyShader();
+    }
+    return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile,
+                                               nullptr));
+}
+
+sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
+    SkMatrix lm;
+    buffer.readMatrix(&lm);
+    TileMode mx = (TileMode)buffer.read32();
+    TileMode my = (TileMode)buffer.read32();
+    SkRect tile;
+    buffer.readRect(&tile);
+
+    sk_sp<SkPicture> picture;
+
+    if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
+        if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) {
+            // Older code blindly serialized pictures.  We don't trust them.
+            buffer.validate(false);
+            return nullptr;
+        }
+        // Newer code won't serialize pictures in disallow-cross-process-picture mode.
+        // Assert that they didn't serialize anything except a false here.
+        buffer.validate(!buffer.readBool());
+    } else {
+        // Old code always serialized the picture.  New code writes a 'true' first if it did.
+        if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) ||
+            buffer.readBool()) {
+            picture = SkPicture::MakeFromBuffer(buffer);
+        }
+    }
+    return SkPictureShader::Make(picture, mx, my, &lm, &tile);
+}
+
+void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeMatrix(this->getLocalMatrix());
+    buffer.write32(fTmx);
+    buffer.write32(fTmy);
+    buffer.writeRect(fTile);
+
+    // The deserialization code won't trust that our serialized picture is safe to deserialize.
+    // So write a 'false' telling it that we're not serializing a picture.
+    if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
+        buffer.writeBool(false);
+    } else {
+        buffer.writeBool(true);
+        fPicture->flatten(buffer);
+    }
+}
+
+sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM,
+                                                 SkColorSpace* dstColorSpace,
+                                                 const int maxTextureSize) const {
+    SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
+
+    SkMatrix m;
+    m.setConcat(viewMatrix, this->getLocalMatrix());
+    if (localM) {
+        m.preConcat(*localM);
+    }
+
+    // Use a rotation-invariant scale
+    SkPoint scale;
+    //
+    // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
+    //
+    if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
+        // Decomposition failed, use an approximation.
+        scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
+                  SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
+    }
+    SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
+                                     SkScalarAbs(scale.y() * fTile.height()));
+
+    // Clamp the tile size to about 4M pixels
+    static const SkScalar kMaxTileArea = 2048 * 2048;
+    SkScalar tileArea = scaledSize.width() * scaledSize.height();
+    if (tileArea > kMaxTileArea) {
+        SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
+        scaledSize.set(scaledSize.width() * clampScale,
+                       scaledSize.height() * clampScale);
+    }
+#if SK_SUPPORT_GPU
+    // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
+    if (maxTextureSize) {
+        if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
+            SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height());
+            scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
+                           SkScalarFloorToScalar(scaledSize.height() * downScale));
+        }
+    }
+#endif
+
+#ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING
+    const SkISize tileSize = scaledSize.toRound();
+#else
+    const SkISize tileSize = scaledSize.toCeil();
+#endif
+    if (tileSize.isEmpty()) {
+        return SkShader::MakeEmptyShader();
+    }
+
+    // The actual scale, compensating for rounding & clamping.
+    const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
+                                          SkIntToScalar(tileSize.height()) / fTile.height());
+
+    // |fColorSpace| will only be set when using an SkColorSpaceXformCanvas to do pre-draw xforms.
+    // This canvas is strictly for legacy mode.  A non-null |dstColorSpace| indicates that we
+    // should perform color correct rendering and xform at draw time.
+    SkASSERT(!fColorSpace || !dstColorSpace);
+    sk_sp<SkColorSpace> keyCS = dstColorSpace ? sk_ref_sp(dstColorSpace) : fColorSpace;
+    SkTransferFunctionBehavior blendBehavior = dstColorSpace ? SkTransferFunctionBehavior::kRespect
+                                                             : SkTransferFunctionBehavior::kIgnore;
+
+    sk_sp<SkShader> tileShader;
+    BitmapShaderKey key(std::move(keyCS),
+                        fPicture->uniqueID(),
+                        fTile,
+                        fTmx,
+                        fTmy,
+                        tileScale,
+                        this->getLocalMatrix(),
+                        blendBehavior);
+
+    if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
+        SkMatrix tileMatrix;
+        tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
+                                 SkMatrix::kFill_ScaleToFit);
+
+        sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator(
+                SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr,
+                                              SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace)));
+        if (!tileImage) {
+            return nullptr;
+        }
+
+        if (fColorSpace) {
+            tileImage = tileImage->makeColorSpace(fColorSpace, SkTransferFunctionBehavior::kIgnore);
+        }
+
+        SkMatrix shaderMatrix = this->getLocalMatrix();
+        shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
+        tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix);
+
+        SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
+    }
+
+    return tileShader;
+}
+
+bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
+                                     const SkMatrix& ctm, const SkPaint& paint,
+                                     const SkMatrix* localMatrix) const {
+    // Keep bitmapShader alive by using alloc instead of stack memory
+    auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
+    bitmapShader = this->refBitmapShader(ctm, localMatrix, cs);
+    return bitmapShader && as_SB(bitmapShader)->appendStages(p, cs, alloc, ctm, paint);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
+const {
+    sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix,
+                                                       rec.fDstColorSpace));
+    if (!bitmapShader) {
+        return nullptr;
+    }
+
+    PictureShaderContext* ctx =
+        alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc);
+    if (nullptr == ctx->fBitmapShaderContext) {
+        ctx = nullptr;
+    }
+    return ctx;
+}
+
+sk_sp<SkShader> SkPictureShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return sk_sp<SkPictureShader>(new SkPictureShader(fPicture, fTmx, fTmy, &this->getLocalMatrix(),
+                                                      &fTile, xformer->dst()));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+SkPictureShader::PictureShaderContext::PictureShaderContext(
+        const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
+        SkArenaAlloc* alloc)
+    : INHERITED(shader, rec)
+    , fBitmapShader(std::move(bitmapShader))
+{
+    fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc);
+    //if fBitmapShaderContext is null, we are invalid
+}
+
+uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
+    SkASSERT(fBitmapShaderContext);
+    return fBitmapShaderContext->getFlags();
+}
+
+SkShaderBase::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
+    SkASSERT(fBitmapShaderContext);
+    return fBitmapShaderContext->asAShadeProc(ctx);
+}
+
+void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+    SkASSERT(fBitmapShaderContext);
+    fBitmapShaderContext->shadeSpan(x, y, dstC, count);
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkPictureShader::toString(SkString* str) const {
+    static const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
+
+    str->appendf("PictureShader: [%f:%f:%f:%f] ",
+                 fPicture->cullRect().fLeft,
+                 fPicture->cullRect().fTop,
+                 fPicture->cullRect().fRight,
+                 fPicture->cullRect().fBottom);
+
+    str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
+
+    this->INHERITED::toString(str);
+}
+#endif
+
+#if SK_SUPPORT_GPU
+sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const {
+    int maxTextureSize = 0;
+    if (args.fContext) {
+        maxTextureSize = args.fContext->caps()->maxTextureSize();
+    }
+    sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix,
+                                                       args.fDstColorSpace, maxTextureSize));
+    if (!bitmapShader) {
+        return nullptr;
+    }
+    return as_SB(bitmapShader)->asFragmentProcessor(SkShaderBase::AsFPArgs(
+        args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace));
+}
+#endif
diff --git a/src/shaders/SkPictureShader.h b/src/shaders/SkPictureShader.h
new file mode 100644
index 0000000..f7a509f
--- /dev/null
+++ b/src/shaders/SkPictureShader.h
@@ -0,0 +1,79 @@
+/*
+ * 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 SkPictureShader_DEFINED
+#define SkPictureShader_DEFINED
+
+#include "SkShaderBase.h"
+
+class SkArenaAlloc;
+class SkBitmap;
+class SkPicture;
+
+/*
+ * An SkPictureShader can be used to draw SkPicture-based patterns.
+ *
+ * The SkPicture is first rendered into a tile, which is then used to shade the area according
+ * to specified tiling rules.
+ */
+class SkPictureShader : public SkShaderBase {
+public:
+    static sk_sp<SkShader> Make(sk_sp<SkPicture>, TileMode, TileMode, const SkMatrix*,
+                                const SkRect*);
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureShader)
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+protected:
+    SkPictureShader(SkReadBuffer&);
+    void flatten(SkWriteBuffer&) const override;
+    bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
+                        const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
+private:
+    SkPictureShader(sk_sp<SkPicture>, TileMode, TileMode, const SkMatrix*, const SkRect*,
+                    sk_sp<SkColorSpace>);
+
+    sk_sp<SkShader> refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix,
+                                    SkColorSpace* dstColorSpace,
+                                    const int maxTextureSize = 0) const;
+
+    sk_sp<SkPicture>    fPicture;
+    SkRect              fTile;
+    TileMode            fTmx, fTmy;
+
+    class PictureShaderContext : public Context {
+    public:
+        PictureShaderContext(
+            const SkPictureShader&, const ContextRec&, sk_sp<SkShader> bitmapShader, SkArenaAlloc*);
+
+        uint32_t getFlags() const override;
+
+        ShadeProc asAShadeProc(void** ctx) override;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+
+        sk_sp<SkShader>         fBitmapShader;
+        SkShaderBase::Context*  fBitmapShaderContext;
+        void*                   fBitmapShaderContextStorage;
+
+        typedef Context INHERITED;
+    };
+
+    // Should never be set by a public constructor.  This is only used when onMakeColorSpace()
+    // forces a deferred color space xform.
+    sk_sp<SkColorSpace>   fColorSpace;
+
+    typedef SkShaderBase INHERITED;
+};
+
+#endif // SkPictureShader_DEFINED
diff --git a/src/shaders/SkShader.cpp b/src/shaders/SkShader.cpp
new file mode 100644
index 0000000..07ddc6d
--- /dev/null
+++ b/src/shaders/SkShader.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkArenaAlloc.h"
+#include "SkAtomics.h"
+#include "SkBitmapProcShader.h"
+#include "SkColorShader.h"
+#include "SkColorSpaceXformer.h"
+#include "SkEmptyShader.h"
+#include "SkMallocPixelRef.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkPictureShader.h"
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
+#include "SkReadBuffer.h"
+#include "SkScalar.h"
+#include "SkShaderBase.h"
+#include "SkTLazy.h"
+#include "SkWriteBuffer.h"
+#include "../jumper/SkJumper.h"
+
+#if SK_SUPPORT_GPU
+#include "GrFragmentProcessor.h"
+#endif
+
+//#define SK_TRACK_SHADER_LIFETIME
+
+#ifdef SK_TRACK_SHADER_LIFETIME
+    static int32_t gShaderCounter;
+#endif
+
+static inline void inc_shader_counter() {
+#ifdef SK_TRACK_SHADER_LIFETIME
+    int32_t prev = sk_atomic_inc(&gShaderCounter);
+    SkDebugf("+++ shader counter %d\n", prev + 1);
+#endif
+}
+static inline void dec_shader_counter() {
+#ifdef SK_TRACK_SHADER_LIFETIME
+    int32_t prev = sk_atomic_dec(&gShaderCounter);
+    SkDebugf("--- shader counter %d\n", prev - 1);
+#endif
+}
+
+SkShaderBase::SkShaderBase(const SkMatrix* localMatrix)
+    : fLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I()) {
+    inc_shader_counter();
+    // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe.
+    (void)fLocalMatrix.getType();
+}
+
+SkShaderBase::~SkShaderBase() {
+    dec_shader_counter();
+}
+
+void SkShaderBase::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    bool hasLocalM = !fLocalMatrix.isIdentity();
+    buffer.writeBool(hasLocalM);
+    if (hasLocalM) {
+        buffer.writeMatrix(fLocalMatrix);
+    }
+}
+
+bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm,
+                                       const SkMatrix* outerLocalMatrix,
+                                       SkMatrix* totalInverse) const {
+    SkMatrix total = SkMatrix::Concat(ctm, fLocalMatrix);
+    if (outerLocalMatrix) {
+        total.preConcat(*outerLocalMatrix);
+    }
+
+    return total.invert(totalInverse);
+}
+
+bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const {
+    SkColor storage;
+    if (nullptr == colorPtr) {
+        colorPtr = &storage;
+    }
+    if (this->onAsLuminanceColor(colorPtr)) {
+        *colorPtr = SkColorSetA(*colorPtr, 0xFF);   // we only return opaque
+        return true;
+    }
+    return false;
+}
+
+SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
+    if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) {
+        return nullptr;
+    }
+    return this->onMakeContext(rec, alloc);
+}
+
+SkShaderBase::Context* SkShaderBase::makeBurstPipelineContext(const ContextRec& rec,
+                                                              SkArenaAlloc* alloc) const {
+
+    SkASSERT(rec.fPreferredDstType == ContextRec::kPM4f_DstType);
+
+    return this->onMakeBurstPipelineContext(rec, alloc);
+}
+
+SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
+    : fShader(shader), fCTM(*rec.fMatrix)
+{
+    // We should never use a context for RP-only shaders.
+    SkASSERT(!shader.isRasterPipelineOnly());
+
+    // Because the context parameters must be valid at this point, we know that the matrix is
+    // invertible.
+    SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse));
+    fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse);
+
+    fPaintAlpha = rec.fPaint->getAlpha();
+}
+
+SkShaderBase::Context::~Context() {}
+
+SkShaderBase::Context::ShadeProc SkShaderBase::Context::asAShadeProc(void** ctx) {
+    return nullptr;
+}
+
+void SkShaderBase::Context::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
+    const int N = 128;
+    SkPMColor tmp[N];
+    while (count > 0) {
+        int n = SkTMin(count, N);
+        this->shadeSpan(x, y, tmp, n);
+        for (int i = 0; i < n; ++i) {
+            dst[i] = SkPM4f::FromPMColor(tmp[i]);
+        }
+        dst += n;
+        x += n;
+        count -= n;
+    }
+}
+
+#include "SkColorPriv.h"
+
+#define kTempColorQuadCount 6   // balance between speed (larger) and saving stack-space
+#define kTempColorCount     (kTempColorQuadCount << 2)
+
+#ifdef SK_CPU_BENDIAN
+    #define SkU32BitShiftToByteOffset(shift)    (3 - ((shift) >> 3))
+#else
+    #define SkU32BitShiftToByteOffset(shift)    ((shift) >> 3)
+#endif
+
+void SkShaderBase::Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    SkASSERT(count > 0);
+
+    SkPMColor   colors[kTempColorCount];
+
+    while ((count -= kTempColorCount) >= 0) {
+        this->shadeSpan(x, y, colors, kTempColorCount);
+        x += kTempColorCount;
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        int quads = kTempColorQuadCount;
+        do {
+            U8CPU a0 = srcA[0];
+            U8CPU a1 = srcA[4];
+            U8CPU a2 = srcA[8];
+            U8CPU a3 = srcA[12];
+            srcA += 4*4;
+            *alpha++ = SkToU8(a0);
+            *alpha++ = SkToU8(a1);
+            *alpha++ = SkToU8(a2);
+            *alpha++ = SkToU8(a3);
+        } while (--quads != 0);
+    }
+    SkASSERT(count < 0);
+    SkASSERT(count + kTempColorCount >= 0);
+    if (count += kTempColorCount) {
+        this->shadeSpan(x, y, colors, count);
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        do {
+            *alpha++ = *srcA;
+            srcA += 4;
+        } while (--count != 0);
+    }
+#if 0
+    do {
+        int n = count;
+        if (n > kTempColorCount)
+            n = kTempColorCount;
+        SkASSERT(n > 0);
+
+        this->shadeSpan(x, y, colors, n);
+        x += n;
+        count -= n;
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        do {
+            *alpha++ = *srcA;
+            srcA += 4;
+        } while (--n != 0);
+    } while (count > 0);
+#endif
+}
+
+SkShaderBase::Context::MatrixClass SkShaderBase::Context::ComputeMatrixClass(const SkMatrix& mat) {
+    MatrixClass mc = kLinear_MatrixClass;
+
+    if (mat.hasPerspective()) {
+        if (mat.isFixedStepInX()) {
+            mc = kFixedStepInX_MatrixClass;
+        } else {
+            mc = kPerspective_MatrixClass;
+        }
+    }
+    return mc;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+const SkMatrix& SkShader::getLocalMatrix() const {
+    return as_SB(this)->getLocalMatrix();
+}
+
+#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
+bool SkShader::isABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, TileMode xy[2]) const {
+    return  as_SB(this)->onIsABitmap(outTexture, outMatrix, xy);
+}
+#endif
+
+SkImage* SkShader::isAImage(SkMatrix* localMatrix, TileMode xy[2]) const {
+    return as_SB(this)->onIsAImage(localMatrix, xy);
+}
+
+SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
+    return kNone_GradientType;
+}
+
+#if SK_SUPPORT_GPU
+sk_sp<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const AsFPArgs&) const {
+    return nullptr;
+}
+#endif
+
+sk_sp<SkShader> SkShader::makeAsALocalMatrixShader(SkMatrix*) const {
+    return nullptr;
+}
+
+sk_sp<SkShader> SkShader::MakeEmptyShader() { return sk_make_sp<SkEmptyShader>(); }
+
+sk_sp<SkShader> SkShader::MakeColorShader(SkColor color) { return sk_make_sp<SkColorShader>(color); }
+
+sk_sp<SkShader> SkShader::MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy,
+                                           const SkMatrix* localMatrix) {
+    if (localMatrix && !localMatrix->invert(nullptr)) {
+        return nullptr;
+    }
+    return SkMakeBitmapShader(src, tmx, tmy, localMatrix, kIfMutable_SkCopyPixelsMode);
+}
+
+sk_sp<SkShader> SkShader::MakePictureShader(sk_sp<SkPicture> src, TileMode tmx, TileMode tmy,
+                                            const SkMatrix* localMatrix, const SkRect* tile) {
+    if (localMatrix && !localMatrix->invert(nullptr)) {
+        return nullptr;
+    }
+    return SkPictureShader::Make(std::move(src), tmx, tmy, localMatrix, tile);
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkShaderBase::toString(SkString* str) const {
+    if (!fLocalMatrix.isIdentity()) {
+        str->append(" ");
+        fLocalMatrix.toString(str);
+    }
+}
+#endif
+
+bool SkShaderBase::appendStages(SkRasterPipeline* p,
+                                SkColorSpace* dstCS,
+                                SkArenaAlloc* alloc,
+                                const SkMatrix& ctm,
+                                const SkPaint& paint,
+                                const SkMatrix* localM) const {
+    return this->onAppendStages(p, dstCS, alloc, ctm, paint, localM);
+}
+
+bool SkShaderBase::onAppendStages(SkRasterPipeline* p,
+                                  SkColorSpace* dstCS,
+                                  SkArenaAlloc* alloc,
+                                  const SkMatrix& ctm,
+                                  const SkPaint& paint,
+                                  const SkMatrix* localM) const {
+    // SkShader::Context::shadeSpan4f() handles the paint opacity internally,
+    // but SkRasterPipelineBlitter applies it as a separate stage.
+    // We skip the internal shadeSpan4f() step by forcing the paint opaque.
+    SkTCopyOnFirstWrite<SkPaint> opaquePaint(paint);
+    if (paint.getAlpha() != SK_AlphaOPAQUE) {
+        opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE);
+    }
+
+    ContextRec rec(*opaquePaint, ctm, localM, ContextRec::kPM4f_DstType, dstCS);
+
+    struct CallbackCtx : SkJumper_CallbackCtx {
+        sk_sp<SkShader> shader;
+        Context*        ctx;
+    };
+    auto cb = alloc->make<CallbackCtx>();
+    cb->shader = dstCS ? SkColorSpaceXformer::Make(sk_ref_sp(dstCS))->apply(this)
+                       : sk_ref_sp((SkShader*)this);
+    cb->ctx = as_SB(cb->shader)->makeContext(rec, alloc);
+    cb->fn  = [](SkJumper_CallbackCtx* self, int active_pixels) {
+        auto c = (CallbackCtx*)self;
+        int x = (int)c->rgba[0],
+        y = (int)c->rgba[1];
+        c->ctx->shadeSpan4f(x,y, (SkPM4f*)c->rgba, active_pixels);
+    };
+
+    if (cb->ctx) {
+        p->append(SkRasterPipeline::seed_shader);
+        p->append(SkRasterPipeline::callback, cb);
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
+    return SkShader::MakeEmptyShader();
+}
+
+#ifndef SK_IGNORE_TO_STRING
+#include "SkEmptyShader.h"
+
+void SkEmptyShader::toString(SkString* str) const {
+    str->append("SkEmptyShader: (");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/shaders/SkShaderBase.h b/src/shaders/SkShaderBase.h
new file mode 100644
index 0000000..8dc9354
--- /dev/null
+++ b/src/shaders/SkShaderBase.h
@@ -0,0 +1,290 @@
+/*
+ * 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 SkShaderBase_DEFINED
+#define SkShaderBase_DEFINED
+
+#include "SkFilterQuality.h"
+#include "SkMatrix.h"
+#include "SkShader.h"
+
+class GrContext;
+class GrFragmentProcessor;
+class SkArenaAlloc;
+class SkColorSpace;
+class SkColorSpaceXformer;
+class SkImage;
+struct SkImageInfo;
+class SkPaint;
+class SkRasterPipeline;
+
+class SkShaderBase : public SkShader {
+public:
+    ~SkShaderBase() override;
+
+    /**
+     *  Returns true if the shader is guaranteed to produce only a single color.
+     *  Subclasses can override this to allow loop-hoisting optimization.
+     */
+    virtual bool isConstant() const { return false; }
+
+    const SkMatrix& getLocalMatrix() const { return fLocalMatrix; }
+
+    enum Flags {
+        //!< set if all of the colors will be opaque
+        kOpaqueAlpha_Flag = 1 << 0,
+
+        /** set if the spans only vary in X (const in Y).
+            e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient
+            that varies from left-to-right. This flag specifies this for
+            shadeSpan().
+         */
+        kConstInY32_Flag = 1 << 1,
+
+        /** hint for the blitter that 4f is the preferred shading mode.
+         */
+        kPrefers4f_Flag  = 1 << 2,
+    };
+
+    /**
+     *  ContextRec acts as a parameter bundle for creating Contexts.
+     */
+    struct ContextRec {
+        enum DstType {
+            kPMColor_DstType, // clients prefer shading into PMColor dest
+            kPM4f_DstType,    // clients prefer shading into PM4f dest
+        };
+
+        ContextRec(const SkPaint& paint, const SkMatrix& matrix, const SkMatrix* localM,
+                   DstType dstType, SkColorSpace* dstColorSpace)
+            : fPaint(&paint)
+            , fMatrix(&matrix)
+            , fLocalMatrix(localM)
+            , fPreferredDstType(dstType)
+            , fDstColorSpace(dstColorSpace) {}
+
+        const SkPaint*  fPaint;            // the current paint associated with the draw
+        const SkMatrix* fMatrix;           // the current matrix in the canvas
+        const SkMatrix* fLocalMatrix;      // optional local matrix
+        const DstType   fPreferredDstType; // the "natural" client dest type
+        SkColorSpace*   fDstColorSpace;    // the color space of the dest surface (if any)
+    };
+
+    class Context : public ::SkNoncopyable {
+    public:
+        Context(const SkShaderBase& shader, const ContextRec&);
+
+        virtual ~Context();
+
+        /**
+         *  Called sometimes before drawing with this shader. Return the type of
+         *  alpha your shader will return. The default implementation returns 0.
+         *  Your subclass should override if it can (even sometimes) report a
+         *  non-zero value, since that will enable various blitters to perform
+         *  faster.
+         */
+        virtual uint32_t getFlags() const { return 0; }
+
+        /**
+         *  Called for each span of the object being drawn. Your subclass should
+         *  set the appropriate colors (with premultiplied alpha) that correspond
+         *  to the specified device coordinates.
+         */
+        virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0;
+
+        virtual void shadeSpan4f(int x, int y, SkPM4f[], int count);
+
+        /**
+         * The const void* ctx is only const because all the implementations are const.
+         * This can be changed to non-const if a new shade proc needs to change the ctx.
+         */
+        typedef void (*ShadeProc)(const void* ctx, int x, int y, SkPMColor[], int count);
+        virtual ShadeProc asAShadeProc(void** ctx);
+
+        /**
+         *  Similar to shadeSpan, but only returns the alpha-channel for a span.
+         *  The default implementation calls shadeSpan() and then extracts the alpha
+         *  values from the returned colors.
+         */
+        virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
+
+        // Notification from blitter::blitMask in case we need to see the non-alpha channels
+        virtual void set3DMask(const SkMask*) {}
+
+    protected:
+        // Reference to shader, so we don't have to dupe information.
+        const SkShaderBase& fShader;
+
+        enum MatrixClass {
+            kLinear_MatrixClass,            // no perspective
+            kFixedStepInX_MatrixClass,      // fast perspective, need to call fixedStepInX() each
+                                            // scanline
+            kPerspective_MatrixClass        // slow perspective, need to mappoints each pixel
+        };
+        static MatrixClass ComputeMatrixClass(const SkMatrix&);
+
+        uint8_t         getPaintAlpha() const { return fPaintAlpha; }
+        const SkMatrix& getTotalInverse() const { return fTotalInverse; }
+        MatrixClass     getInverseClass() const { return (MatrixClass)fTotalInverseClass; }
+        const SkMatrix& getCTM() const { return fCTM; }
+
+    private:
+        SkMatrix    fCTM;
+        SkMatrix    fTotalInverse;
+        uint8_t     fPaintAlpha;
+        uint8_t     fTotalInverseClass;
+
+        typedef SkNoncopyable INHERITED;
+    };
+
+    /**
+     * Make a context using the memory provided by the arena.
+     *
+     * @return pointer to context or nullptr if can't be created
+     */
+    Context* makeContext(const ContextRec&, SkArenaAlloc*) const;
+
+    /**
+     * Shaders may opt-in for burst mode, if they can operate
+     * significantly more efficiently in that mode.
+     *
+     * Burst mode is prioritized in SkRasterPipelineBlitter over
+     * regular (appendStages) pipeline operation.
+     */
+    Context* makeBurstPipelineContext(const ContextRec&, SkArenaAlloc*) const;
+
+#if SK_SUPPORT_GPU
+    struct AsFPArgs {
+        AsFPArgs() {}
+        AsFPArgs(GrContext* context,
+                 const SkMatrix* viewMatrix,
+                 const SkMatrix* localMatrix,
+                 SkFilterQuality filterQuality,
+                 SkColorSpace* dstColorSpace)
+            : fContext(context)
+            , fViewMatrix(viewMatrix)
+            , fLocalMatrix(localMatrix)
+            , fFilterQuality(filterQuality)
+            , fDstColorSpace(dstColorSpace) {}
+
+        GrContext*                    fContext;
+        const SkMatrix*               fViewMatrix;
+        const SkMatrix*               fLocalMatrix;
+        SkFilterQuality               fFilterQuality;
+        SkColorSpace*                 fDstColorSpace;
+    };
+
+    /**
+     *  Returns a GrFragmentProcessor that implements the shader for the GPU backend. NULL is
+     *  returned if there is no GPU implementation.
+     *
+     *  The GPU device does not call SkShader::createContext(), instead we pass the view matrix,
+     *  local matrix, and filter quality directly.
+     *
+     *  The GrContext may be used by the to create textures that are required by the returned
+     *  processor.
+     *
+     *  The returned GrFragmentProcessor should expect an unpremultiplied input color and
+     *  produce a premultiplied output.
+     */
+    virtual sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const;
+#endif
+
+    /**
+     *  If the shader can represent its "average" luminance in a single color, return true and
+     *  if color is not NULL, return that color. If it cannot, return false and ignore the color
+     *  parameter.
+     *
+     *  Note: if this returns true, the returned color will always be opaque, as only the RGB
+     *  components are used to compute luminance.
+     */
+    bool asLuminanceColor(SkColor*) const;
+
+    /**
+     *  Returns a shader transformed into a new color space via the |xformer|.
+     */
+    sk_sp<SkShader> makeColorSpace(SkColorSpaceXformer* xformer) const {
+        return this->onMakeColorSpace(xformer);
+    }
+
+    virtual bool isRasterPipelineOnly() const { return false; }
+
+    // If this returns false, then we draw nothing (do not fall back to shader context)
+    bool appendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*,
+                      const SkMatrix& ctm, const SkPaint&, const SkMatrix* localM=nullptr) const;
+
+    bool computeTotalInverse(const SkMatrix& ctm,
+                             const SkMatrix* outerLocalMatrix,
+                             SkMatrix* totalInverse) const;
+
+#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
+    virtual bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode[2]) const {
+        return false;
+    }
+#endif
+
+    virtual SkImage* onIsAImage(SkMatrix*, TileMode[2]) const {
+        return nullptr;
+    }
+
+    SK_TO_STRING_VIRT()
+
+    SK_DEFINE_FLATTENABLE_TYPE(SkShaderBase)
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
+
+protected:
+    SkShaderBase(const SkMatrix* localMatrix = nullptr);
+
+    void flatten(SkWriteBuffer&) const override;
+
+    /**
+     * Specialize creating a SkShader context using the supplied allocator.
+     * @return pointer to context owned by the arena allocator.
+     */
+    virtual Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const {
+        return nullptr;
+    }
+
+    /**
+     * Overriden by shaders which prefer burst mode.
+     */
+    virtual Context* onMakeBurstPipelineContext(const ContextRec&, SkArenaAlloc*) const {
+        return nullptr;
+    }
+
+    virtual bool onAsLuminanceColor(SkColor*) const {
+        return false;
+    }
+
+    virtual sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer*) const {
+        return sk_ref_sp(const_cast<SkShaderBase*>(this));
+    }
+
+    // Default impl creates shadercontext and calls that (not very efficient)
+    virtual bool onAppendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*,
+                                const SkMatrix&, const SkPaint&, const SkMatrix* localM) const;
+
+private:
+    // This is essentially const, but not officially so it can be modified in constructors.
+    SkMatrix fLocalMatrix;
+
+    typedef SkShader INHERITED;
+};
+
+inline SkShaderBase* as_SB(SkShader* shader) {
+    return static_cast<SkShaderBase*>(shader);
+}
+
+inline const SkShaderBase* as_SB(const SkShader* shader) {
+    return static_cast<const SkShaderBase*>(shader);
+}
+
+inline const SkShaderBase* as_SB(const sk_sp<SkShader>& shader) {
+    return static_cast<SkShaderBase*>(shader.get());
+}
+
+#endif // SkShaderBase_DEFINED
diff --git a/src/shaders/gradients/Sk4fGradientBase.cpp b/src/shaders/gradients/Sk4fGradientBase.cpp
new file mode 100644
index 0000000..bf884ac
--- /dev/null
+++ b/src/shaders/gradients/Sk4fGradientBase.cpp
@@ -0,0 +1,456 @@
+/*
+ * 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 "Sk4fGradientBase.h"
+
+#include <functional>
+
+namespace {
+
+Sk4f pack_color(const SkColor4f& c4f, bool premul, const Sk4f& component_scale) {
+    const Sk4f pm4f = premul
+        ? c4f.premul().to4f()
+        : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA};
+
+    return pm4f * component_scale;
+}
+
+class IntervalIterator {
+public:
+    IntervalIterator(const SkGradientShaderBase& shader, SkColorSpace* dstCS, bool reverse)
+        : fShader(shader)
+        , fDstCS(dstCS)
+        , fFirstPos(reverse ? SK_Scalar1 : 0)
+        , fBegin(reverse ? shader.fColorCount - 1 : 0)
+        , fAdvance(reverse ? -1 : 1) {
+        SkASSERT(shader.fColorCount > 0);
+    }
+
+    void iterate(std::function<void(const SkColor4f&, const SkColor4f&,
+                                    SkScalar, SkScalar)> func) const {
+        if (!fShader.fOrigPos) {
+            this->iterateImplicitPos(func);
+            return;
+        }
+
+        const int end = fBegin + fAdvance * (fShader.fColorCount - 1);
+        const SkScalar lastPos = 1 - fFirstPos;
+        int prev = fBegin;
+        SkScalar prevPos = fFirstPos;
+
+        do {
+            const int curr = prev + fAdvance;
+            SkASSERT(curr >= 0 && curr < fShader.fColorCount);
+
+            // TODO: this sanitization should be done in SkGradientShaderBase
+            const SkScalar currPos = (fAdvance > 0)
+                ? SkTPin(fShader.fOrigPos[curr], prevPos, lastPos)
+                : SkTPin(fShader.fOrigPos[curr], lastPos, prevPos);
+
+            if (currPos != prevPos) {
+                SkASSERT((currPos - prevPos > 0) == (fAdvance > 0));
+                func(fShader.getXformedColor(prev, fDstCS), fShader.getXformedColor(curr, fDstCS),
+                     prevPos, currPos);
+            }
+
+            prev = curr;
+            prevPos = currPos;
+        } while (prev != end);
+    }
+
+private:
+    void iterateImplicitPos(std::function<void(const SkColor4f&, const SkColor4f&,
+                                               SkScalar, SkScalar)> func) const {
+        // When clients don't provide explicit color stop positions (fPos == nullptr),
+        // the color stops are distributed evenly across the unit interval
+        // (implicit positioning).
+        const SkScalar dt = fAdvance * SK_Scalar1 / (fShader.fColorCount - 1);
+        const int end = fBegin + fAdvance * (fShader.fColorCount - 2);
+        int prev = fBegin;
+        SkScalar prevPos = fFirstPos;
+
+        while (prev != end) {
+            const int curr = prev + fAdvance;
+            SkASSERT(curr >= 0 && curr < fShader.fColorCount);
+
+            const SkScalar currPos = prevPos + dt;
+            func(fShader.getXformedColor(prev, fDstCS),
+                 fShader.getXformedColor(curr, fDstCS),
+                 prevPos, currPos);
+            prev = curr;
+            prevPos = currPos;
+        }
+
+        // emit the last interval with a pinned end position, to avoid precision issues
+        func(fShader.getXformedColor(prev, fDstCS),
+             fShader.getXformedColor(prev + fAdvance, fDstCS),
+             prevPos, 1 - fFirstPos);
+    }
+
+    const SkGradientShaderBase& fShader;
+    SkColorSpace*               fDstCS;
+    const SkScalar              fFirstPos;
+    const int                   fBegin;
+    const int                   fAdvance;
+};
+
+void addMirrorIntervals(const SkGradientShaderBase& shader,
+                        SkColorSpace* dstCS,
+                        const Sk4f& componentScale,
+                        bool premulColors, bool reverse,
+                        Sk4fGradientIntervalBuffer::BufferType* buffer) {
+    const IntervalIterator iter(shader, dstCS, reverse);
+    iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
+        SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0);
+
+        const auto mirror_t0 = 2 - t0;
+        const auto mirror_t1 = 2 - t1;
+        // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid
+        // triggering Interval asserts.
+        if (mirror_t0 != mirror_t1) {
+            buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_t0,
+                                 pack_color(c1, premulColors, componentScale), mirror_t1);
+        }
+    });
+}
+
+} // anonymous namespace
+
+Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar t0,
+                                           const Sk4f& c1, SkScalar t1)
+    : fT0(t0)
+    , fT1(t1) {
+    SkASSERT(t0 != t1);
+    // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals.
+    SkASSERT(SkScalarIsFinite(t0) || SkScalarIsFinite(t1));
+
+    const auto dt = t1 - t0;
+
+    // Clamp edge intervals are always zero-ramp.
+    SkASSERT(SkScalarIsFinite(dt) || (c0 == c1).allTrue());
+    SkASSERT(SkScalarIsFinite(t0) || (c0 == c1).allTrue());
+    const Sk4f   dc = SkScalarIsFinite(dt) ? (c1 - c0) / dt : 0;
+    const Sk4f bias = c0 - (SkScalarIsFinite(t0) ? t0 * dc : 0);
+
+    bias.store(&fCb.fVec);
+    dc.store(&fCg.fVec);
+}
+
+void Sk4fGradientIntervalBuffer::init(const SkGradientShaderBase& shader, SkColorSpace* dstCS,
+                                      SkShader::TileMode tileMode, bool premulColors,
+                                      SkScalar alpha, bool reverse) {
+    // The main job here is to build a specialized interval list: a different
+    // representation of the color stops data, optimized for efficient scan line
+    // access during shading.
+    //
+    //   [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1})
+    //
+    // The list may be inverted when requested (such that e.g. points are sorted
+    // in increasing x order when dx < 0).
+    //
+    // Note: the current representation duplicates pos data; we could refactor to
+    //       avoid this if interval storage size becomes a concern.
+    //
+    // Aside from reordering, we also perform two more pre-processing steps at
+    // this stage:
+    //
+    //   1) scale the color components depending on paint alpha and the requested
+    //      interpolation space (note: the interval color storage is SkPM4f, but
+    //      that doesn't necessarily mean the colors are premultiplied; that
+    //      property is tracked in fColorsArePremul)
+    //
+    //   2) inject synthetic intervals to support tiling.
+    //
+    //      * for kRepeat, no extra intervals are needed - the iterator just
+    //        wraps around at the end:
+    //
+    //          ->[P0,P1)->..[Pn-1,Pn)->
+    //
+    //      * for kClamp, we add two "infinite" intervals before/after:
+    //
+    //          [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf)
+    //
+    //        (the iterator should never run off the end in this mode)
+    //
+    //      * for kMirror, we extend the range to [0..2] and add a flipped
+    //        interval series - then the iterator operates just as in the
+    //        kRepeat case:
+    //
+    //          ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)->
+    //
+    // TODO: investigate collapsing intervals << 1px.
+
+    const auto count = shader.fColorCount;
+
+    SkASSERT(count > 0);
+
+    fIntervals.reset();
+
+    const Sk4f componentScale = premulColors
+        ? Sk4f(alpha)
+        : Sk4f(1.0f, 1.0f, 1.0f, alpha);
+    const int first_index = reverse ? count - 1 : 0;
+    const int last_index = count - 1 - first_index;
+    const SkScalar first_pos = reverse ? SK_Scalar1 : 0;
+    const SkScalar last_pos = SK_Scalar1 - first_pos;
+
+    if (tileMode == SkShader::kClamp_TileMode) {
+        // synthetic edge interval: -/+inf .. P0
+        const Sk4f clamp_color = pack_color(shader.getXformedColor(first_index, dstCS),
+                                            premulColors, componentScale);
+        const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity;
+        fIntervals.emplace_back(clamp_color, clamp_pos,
+                                clamp_color, first_pos);
+    } else if (tileMode == SkShader::kMirror_TileMode && reverse) {
+        // synthetic mirror intervals injected before main intervals: (2 .. 1]
+        addMirrorIntervals(shader, dstCS, componentScale, premulColors, false, &fIntervals);
+    }
+
+    const IntervalIterator iter(shader, dstCS, reverse);
+    iter.iterate([&] (const SkColor4f& c0, const SkColor4f& c1, SkScalar t0, SkScalar t1) {
+        SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0);
+
+        fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0,
+                                pack_color(c1, premulColors, componentScale), t1);
+    });
+
+    if (tileMode == SkShader::kClamp_TileMode) {
+        // synthetic edge interval: Pn .. +/-inf
+        const Sk4f clamp_color = pack_color(shader.getXformedColor(last_index, dstCS),
+                                            premulColors, componentScale);
+        const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity;
+        fIntervals.emplace_back(clamp_color, last_pos,
+                                clamp_color, clamp_pos);
+    } else if (tileMode == SkShader::kMirror_TileMode && !reverse) {
+        // synthetic mirror intervals injected after main intervals: [1 .. 2)
+        addMirrorIntervals(shader, dstCS, componentScale, premulColors, true, &fIntervals);
+    }
+}
+
+const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const {
+    // Binary search.
+    const auto* i0 = fIntervals.begin();
+    const auto* i1 = fIntervals.end() - 1;
+
+    while (i0 != i1) {
+        SkASSERT(i0 < i1);
+        SkASSERT(t >= i0->fT0 && t <= i1->fT1);
+
+        const auto* i = i0 + ((i1 - i0) >> 1);
+
+        if (t > i->fT1) {
+            i0 = i + 1;
+        } else {
+            i1 = i;
+        }
+    }
+
+    SkASSERT(i0->contains(t));
+    return i0;
+}
+
+const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext(
+    SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const {
+
+    SkASSERT(!prev->contains(t));
+    SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end());
+    SkASSERT(t >= fIntervals.front().fT0 && t <= fIntervals.back().fT1);
+
+    const auto* i = prev;
+
+    // Use the |increasing| signal to figure which direction we should search for
+    // the next interval, then perform a linear search.
+    if (increasing) {
+        do {
+            i += 1;
+            if (i >= fIntervals.end()) {
+                i = fIntervals.begin();
+            }
+        } while (!i->contains(t));
+    } else {
+        do {
+            i -= 1;
+            if (i < fIntervals.begin()) {
+                i = fIntervals.end() - 1;
+            }
+        } while (!i->contains(t));
+    }
+
+    return i;
+}
+
+SkGradientShaderBase::
+GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader,
+                                                         const ContextRec& rec)
+    : INHERITED(shader, rec)
+    , fFlags(this->INHERITED::getFlags())
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
+    , fDither(true)
+#else
+    , fDither(rec.fPaint->isDither())
+#endif
+{
+    const SkMatrix& inverse = this->getTotalInverse();
+    fDstToPos.setConcat(shader.fPtsToUnit, inverse);
+    fDstToPosProc = fDstToPos.getMapXYProc();
+    fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos));
+
+    if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+
+    fColorsArePremul =
+        (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag)
+        || shader.fColorsAreOpaque;
+}
+
+bool SkGradientShaderBase::
+GradientShaderBase4fContext::isValid() const {
+    return fDstToPos.isFinite();
+}
+
+void SkGradientShaderBase::
+GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
+    if (fColorsArePremul) {
+        this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count);
+    } else {
+        this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count);
+    }
+}
+
+void SkGradientShaderBase::
+GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
+    if (fColorsArePremul) {
+        this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count);
+    } else {
+        this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count);
+    }
+}
+
+template<DstType dstType, ApplyPremul premul>
+void SkGradientShaderBase::
+GradientShaderBase4fContext::shadePremulSpan(int x, int y,
+                                             typename DstTraits<dstType, premul>::Type dst[],
+                                             int count) const {
+    const SkGradientShaderBase& shader =
+        static_cast<const SkGradientShaderBase&>(fShader);
+
+    switch (shader.fTileMode) {
+    case kClamp_TileMode:
+        this->shadeSpanInternal<dstType,
+                                premul,
+                                kClamp_TileMode>(x, y, dst, count);
+        break;
+    case kRepeat_TileMode:
+        this->shadeSpanInternal<dstType,
+                                premul,
+                                kRepeat_TileMode>(x, y, dst, count);
+        break;
+    case kMirror_TileMode:
+        this->shadeSpanInternal<dstType,
+                                premul,
+                                kMirror_TileMode>(x, y, dst, count);
+        break;
+    }
+}
+
+template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
+void SkGradientShaderBase::
+GradientShaderBase4fContext::shadeSpanInternal(int x, int y,
+                                               typename DstTraits<dstType, premul>::Type dst[],
+                                               int count) const {
+    static const int kBufSize = 128;
+    SkScalar ts[kBufSize];
+    TSampler<dstType, premul, tileMode> sampler(*this);
+
+    SkASSERT(count > 0);
+    do {
+        const int n = SkTMin(kBufSize, count);
+        this->mapTs(x, y, ts, n);
+        for (int i = 0; i < n; ++i) {
+            const Sk4f c = sampler.sample(ts[i]);
+            DstTraits<dstType, premul>::store(c, dst++);
+        }
+        x += n;
+        count -= n;
+    } while (count > 0);
+}
+
+template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
+class SkGradientShaderBase::GradientShaderBase4fContext::TSampler {
+public:
+    TSampler(const GradientShaderBase4fContext& ctx)
+        : fCtx(ctx)
+        , fInterval(nullptr) {
+        switch (tileMode) {
+        case kClamp_TileMode:
+            fLargestIntervalValue = SK_ScalarInfinity;
+            break;
+        case kRepeat_TileMode:
+            fLargestIntervalValue = nextafterf(1, 0);
+            break;
+        case kMirror_TileMode:
+            fLargestIntervalValue = nextafterf(2.0f, 0);
+            break;
+        }
+    }
+
+    Sk4f sample(SkScalar t) {
+        const auto tiled_t = tileProc(t);
+
+        if (!fInterval) {
+            // Very first sample => locate the initial interval.
+            // TODO: maybe do this in ctor to remove a branch?
+            fInterval = fCtx.fIntervals.find(tiled_t);
+            this->loadIntervalData(fInterval);
+        } else if (!fInterval->contains(tiled_t)) {
+            fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT);
+            this->loadIntervalData(fInterval);
+        }
+
+        fPrevT = t;
+        return lerp(tiled_t);
+    }
+
+private:
+    SkScalar tileProc(SkScalar t) const {
+        switch (tileMode) {
+        case kClamp_TileMode:
+            // synthetic clamp-mode edge intervals allow for a free-floating t:
+            //   [-inf..0)[0..1)[1..+inf)
+            return t;
+        case kRepeat_TileMode:
+            // t % 1  (intervals range: [0..1))
+            // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1.
+            return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue);
+        case kMirror_TileMode:
+            // t % 2  (synthetic mirror intervals expand the range to [0..2)
+            // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2.
+            return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue);
+        }
+
+        SK_ABORT("Unhandled tile mode.");
+        return 0;
+    }
+
+    Sk4f lerp(SkScalar t) {
+        SkASSERT(fInterval->contains(t));
+        return fCb + fCg * t;
+    }
+
+    void loadIntervalData(const Sk4fGradientInterval* i) {
+        fCb = DstTraits<dstType, premul>::load(i->fCb);
+        fCg = DstTraits<dstType, premul>::load(i->fCg);
+    }
+
+    const GradientShaderBase4fContext& fCtx;
+    const Sk4fGradientInterval*        fInterval;
+    SkScalar                           fPrevT;
+    SkScalar                           fLargestIntervalValue;
+    Sk4f                               fCb;
+    Sk4f                               fCg;
+};
diff --git a/src/shaders/gradients/Sk4fGradientBase.h b/src/shaders/gradients/Sk4fGradientBase.h
new file mode 100644
index 0000000..bd0aab4
--- /dev/null
+++ b/src/shaders/gradients/Sk4fGradientBase.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Sk4fGradientBase_DEFINED
+#define Sk4fGradientBase_DEFINED
+
+#include "Sk4fGradientPriv.h"
+#include "SkColor.h"
+#include "SkGradientShaderPriv.h"
+#include "SkMatrix.h"
+#include "SkNx.h"
+#include "SkPM4f.h"
+#include "SkShaderBase.h"
+#include "SkTArray.h"
+
+struct Sk4fGradientInterval {
+    Sk4fGradientInterval(const Sk4f& c0, SkScalar t0,
+                         const Sk4f& c1, SkScalar t1);
+
+    bool contains(SkScalar t) const {
+        // True if t is in [p0,p1].  Note: this helper assumes a
+        // natural/increasing interval - so it's not usable in Sk4fLinearGradient.
+        SkASSERT(fT0 < fT1);
+        return t >= fT0 && t <= fT1;
+    }
+
+    // Color bias and color gradient, such that for a t in this interval
+    //
+    //   C = fCb + t * fCg;
+    SkPM4f   fCb, fCg;
+    SkScalar fT0, fT1;
+};
+
+class Sk4fGradientIntervalBuffer {
+public:
+    void init(const SkGradientShaderBase&, SkColorSpace* dstCS, SkShader::TileMode tileMode,
+              bool premulColors, SkScalar alpha, bool reverse);
+
+    const Sk4fGradientInterval* find(SkScalar t) const;
+    const Sk4fGradientInterval* findNext(SkScalar t, const Sk4fGradientInterval* prev,
+                                         bool increasing) const;
+
+    using BufferType = SkSTArray<8, Sk4fGradientInterval, true>;
+
+    const BufferType* operator->() const { return &fIntervals; }
+
+private:
+    BufferType fIntervals;
+};
+
+class SkGradientShaderBase::
+GradientShaderBase4fContext : public Context {
+public:
+    GradientShaderBase4fContext(const SkGradientShaderBase&,
+                                const ContextRec&);
+
+    uint32_t getFlags() const override { return fFlags; }
+
+    void shadeSpan(int x, int y, SkPMColor dst[], int count) override;
+    void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override;
+
+    bool isValid() const;
+
+protected:
+    virtual void mapTs(int x, int y, SkScalar ts[], int count) const = 0;
+
+    Sk4fGradientIntervalBuffer fIntervals;
+    SkMatrix                   fDstToPos;
+    SkMatrix::MapXYProc        fDstToPosProc;
+    uint8_t                    fDstToPosClass;
+    uint8_t                    fFlags;
+    bool                       fDither;
+    bool                       fColorsArePremul;
+
+private:
+    using INHERITED = Context;
+
+    void addMirrorIntervals(const SkGradientShaderBase&,
+                            const Sk4f& componentScale, bool reverse);
+
+    template<DstType, ApplyPremul, SkShader::TileMode tileMode>
+    class TSampler;
+
+    template <DstType dstType, ApplyPremul premul>
+    void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[],
+                         int count) const;
+
+    template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
+    void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[],
+                           int count) const;
+};
+
+#endif // Sk4fGradientBase_DEFINED
diff --git a/src/shaders/gradients/Sk4fGradientPriv.h b/src/shaders/gradients/Sk4fGradientPriv.h
new file mode 100644
index 0000000..f18d6ce
--- /dev/null
+++ b/src/shaders/gradients/Sk4fGradientPriv.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Sk4fGradientPriv_DEFINED
+#define Sk4fGradientPriv_DEFINED
+
+#include "SkColor.h"
+#include "SkHalf.h"
+#include "SkImageInfo.h"
+#include "SkNx.h"
+#include "SkPM4f.h"
+#include "SkPM4fPriv.h"
+#include "SkUtils.h"
+
+// Templates shared by various 4f gradient flavors.
+
+namespace {
+
+enum class ApplyPremul { True, False };
+
+enum class DstType {
+    L32,  // Linear 32bit.
+    F32,  // Linear float.
+};
+
+template <ApplyPremul>
+struct PremulTraits;
+
+template <>
+struct PremulTraits<ApplyPremul::False> {
+    static Sk4f apply(const Sk4f& c) { return c; }
+};
+
+template <>
+struct PremulTraits<ApplyPremul::True> {
+    static Sk4f apply(const Sk4f& c) {
+        const float alpha = c[SkPM4f::A];
+        // FIXME: portable swizzle?
+        return c * Sk4f(alpha, alpha, alpha, 1);
+    }
+};
+
+// Struct encapsulating various dest-dependent ops:
+//
+//   - load()       Load a SkPM4f value into Sk4f.  Normally called once per interval
+//                  advance.  Also applies a scale and swizzle suitable for DstType.
+//
+//   - store()      Store one Sk4f to dest.  Optionally handles premul, color space
+//                  conversion, etc.
+//
+//   - store(count) Store the Sk4f value repeatedly to dest, count times.
+//
+//   - store4x()    Store 4 Sk4f values to dest (opportunistic optimization).
+//
+template <DstType, ApplyPremul premul>
+struct DstTraits;
+
+template <ApplyPremul premul>
+struct DstTraits<DstType::L32, premul> {
+    using PM   = PremulTraits<premul>;
+    using Type = SkPMColor;
+
+    // For L32, prescaling by 255 saves a per-pixel multiplication when premul is not needed.
+    static Sk4f load(const SkPM4f& c) {
+        return premul == ApplyPremul::False
+            ? c.to4f_pmorder() * Sk4f(255)
+            : c.to4f_pmorder();
+    }
+
+    static void store(const Sk4f& c, Type* dst) {
+        if (premul == ApplyPremul::False) {
+            // c is prescaled by 255, just store.
+            SkNx_cast<uint8_t>(c).store(dst);
+        } else {
+            *dst = Sk4f_toL32(PM::apply(c));
+        }
+    }
+
+    static void store(const Sk4f& c, Type* dst, int n) {
+        Type pmc;
+        store(c, &pmc);
+        sk_memset32(dst, pmc, n);
+    }
+
+    static void store4x(const Sk4f& c0, const Sk4f& c1,
+                        const Sk4f& c2, const Sk4f& c3,
+                        Type* dst) {
+        if (premul == ApplyPremul::False) {
+            Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3);
+        } else {
+            store(c0, dst + 0);
+            store(c1, dst + 1);
+            store(c2, dst + 2);
+            store(c3, dst + 3);
+        }
+    }
+};
+
+template <ApplyPremul premul>
+struct DstTraits<DstType::F32, premul> {
+    using PM   = PremulTraits<premul>;
+    using Type = SkPM4f;
+
+    static Sk4f load(const SkPM4f& c) {
+        return c.to4f();
+    }
+
+    static void store(const Sk4f& c, Type* dst) {
+        PM::apply(c).store(dst->fVec);
+    }
+
+    static void store(const Sk4f& c, Type* dst, int n) {
+        const Sk4f pmc = PM::apply(c);
+        for (int i = 0; i < n; ++i) {
+            pmc.store(dst[i].fVec);
+        }
+    }
+
+    static void store4x(const Sk4f& c0, const Sk4f& c1,
+                        const Sk4f& c2, const Sk4f& c3,
+                        Type* dst) {
+        store(c0, dst + 0);
+        store(c1, dst + 1);
+        store(c2, dst + 2);
+        store(c3, dst + 3);
+    }
+};
+
+} // anonymous namespace
+
+#endif // Sk4fGradientPriv_DEFINED
diff --git a/src/shaders/gradients/Sk4fLinearGradient.cpp b/src/shaders/gradients/Sk4fLinearGradient.cpp
new file mode 100644
index 0000000..202461c
--- /dev/null
+++ b/src/shaders/gradients/Sk4fLinearGradient.cpp
@@ -0,0 +1,430 @@
+/*
+ * 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 "Sk4fLinearGradient.h"
+#include "Sk4x4f.h"
+
+#include <cmath>
+
+namespace {
+
+template<DstType dstType, ApplyPremul premul>
+void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) {
+    SkASSERT(n > 0);
+
+    const Sk4f dc2 = dc + dc;
+    const Sk4f dc4 = dc2 + dc2;
+
+    Sk4f c0 = c ;
+    Sk4f c1 = c + dc;
+    Sk4f c2 = c0 + dc2;
+    Sk4f c3 = c1 + dc2;
+
+    while (n >= 4) {
+        DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst);
+        dst += 4;
+
+        c0 = c0 + dc4;
+        c1 = c1 + dc4;
+        c2 = c2 + dc4;
+        c3 = c3 + dc4;
+        n -= 4;
+    }
+    if (n & 2) {
+        DstTraits<dstType, premul>::store(c0, dst++);
+        DstTraits<dstType, premul>::store(c1, dst++);
+        c0 = c0 + dc2;
+    }
+    if (n & 1) {
+        DstTraits<dstType, premul>::store(c0, dst);
+    }
+}
+
+template<SkShader::TileMode>
+SkScalar pinFx(SkScalar);
+
+template<>
+SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) {
+    return fx;
+}
+
+template<>
+SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) {
+    SkScalar f = SkScalarFraction(fx);
+    if (f < 0) {
+        f = SkTMin(f + 1, nextafterf(1, 0));
+    }
+    SkASSERT(f >= 0);
+    SkASSERT(f < 1.0f);
+    return f;
+}
+
+template<>
+SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) {
+    SkScalar f = SkScalarMod(fx, 2.0f);
+    if (f < 0) {
+        f = SkTMin(f + 2, nextafterf(2, 0));
+    }
+    SkASSERT(f >= 0);
+    SkASSERT(f < 2.0f);
+    return f;
+}
+
+// true when x is in [k1,k2], or [k2, k1] when the interval is reversed.
+// TODO(fmalita): hoist the reversed interval check out of this helper.
+bool in_range(SkScalar x, SkScalar k1, SkScalar k2) {
+    SkASSERT(k1 != k2);
+    return (k1 < k2)
+        ? (x >= k1 && x <= k2)
+        : (x >= k2 && x <= k1);
+}
+
+} // anonymous namespace
+
+SkLinearGradient::
+LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader,
+                                                 const ContextRec& rec)
+    : INHERITED(shader, rec) {
+
+    // Our fast path expects interval points to be monotonically increasing in x.
+    const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX());
+    fIntervals.init(shader, rec.fDstColorSpace, shader.fTileMode,
+                    fColorsArePremul, rec.fPaint->getAlpha() * (1.0f / 255), reverseIntervals);
+
+    SkASSERT(fIntervals->count() > 0);
+    fCachedInterval = fIntervals->begin();
+}
+
+const Sk4fGradientInterval*
+SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const {
+    SkASSERT(in_range(fx, fIntervals->front().fT0, fIntervals->back().fT1));
+
+    if (1) {
+        // Linear search, using the last scanline interval as a starting point.
+        SkASSERT(fCachedInterval >= fIntervals->begin());
+        SkASSERT(fCachedInterval < fIntervals->end());
+        const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1;
+        while (!in_range(fx, fCachedInterval->fT0, fCachedInterval->fT1)) {
+            fCachedInterval += search_dir;
+            if (fCachedInterval >= fIntervals->end()) {
+                fCachedInterval = fIntervals->begin();
+            } else if (fCachedInterval < fIntervals->begin()) {
+                fCachedInterval = fIntervals->end() - 1;
+            }
+        }
+        return fCachedInterval;
+    } else {
+        // Binary search.  Seems less effective than linear + caching.
+        const auto* i0 = fIntervals->begin();
+        const auto* i1 = fIntervals->end() - 1;
+
+        while (i0 != i1) {
+            SkASSERT(i0 < i1);
+            SkASSERT(in_range(fx, i0->fT0, i1->fT1));
+
+            const auto* i = i0 + ((i1 - i0) >> 1);
+
+            if (in_range(fx, i0->fT0, i->fT1)) {
+                i1 = i;
+            } else {
+                SkASSERT(in_range(fx, i->fT1, i1->fT1));
+                i0 = i + 1;
+            }
+        }
+
+        SkASSERT(in_range(fx, i0->fT0, i0->fT1));
+        return i0;
+    }
+}
+
+void SkLinearGradient::
+LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) {
+    if (!this->isFast()) {
+        this->INHERITED::shadeSpan(x, y, dst, count);
+        return;
+    }
+
+    // TODO: plumb dithering
+    SkASSERT(count > 0);
+    if (fColorsArePremul) {
+        this->shadePremulSpan<DstType::L32,
+                              ApplyPremul::False>(x, y, dst, count);
+    } else {
+        this->shadePremulSpan<DstType::L32,
+                              ApplyPremul::True>(x, y, dst, count);
+    }
+}
+
+void SkLinearGradient::
+LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) {
+    if (!this->isFast()) {
+        this->INHERITED::shadeSpan4f(x, y, dst, count);
+        return;
+    }
+
+    // TONOTDO: plumb dithering
+    SkASSERT(count > 0);
+    if (fColorsArePremul) {
+        this->shadePremulSpan<DstType::F32,
+                              ApplyPremul::False>(x, y, dst, count);
+    } else {
+        this->shadePremulSpan<DstType::F32,
+                              ApplyPremul::True>(x, y, dst, count);
+    }
+}
+
+template<DstType dstType, ApplyPremul premul>
+void SkLinearGradient::
+LinearGradient4fContext::shadePremulSpan(int x, int y,
+                                         typename DstTraits<dstType, premul>::Type dst[],
+                                         int count) const {
+    const SkLinearGradient& shader =
+        static_cast<const SkLinearGradient&>(fShader);
+    switch (shader.fTileMode) {
+    case kClamp_TileMode:
+        this->shadeSpanInternal<dstType,
+                                premul,
+                                kClamp_TileMode>(x, y, dst, count);
+        break;
+    case kRepeat_TileMode:
+        this->shadeSpanInternal<dstType,
+                                premul,
+                                kRepeat_TileMode>(x, y, dst, count);
+        break;
+    case kMirror_TileMode:
+        this->shadeSpanInternal<dstType,
+                                premul,
+                                kMirror_TileMode>(x, y, dst, count);
+        break;
+    }
+}
+
+template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
+void SkLinearGradient::
+LinearGradient4fContext::shadeSpanInternal(int x, int y,
+                                           typename DstTraits<dstType, premul>::Type dst[],
+                                           int count) const {
+    SkPoint pt;
+    fDstToPosProc(fDstToPos,
+                  x + SK_ScalarHalf,
+                  y + SK_ScalarHalf,
+                  &pt);
+    const SkScalar fx = pinFx<tileMode>(pt.x());
+    const SkScalar dx = fDstToPos.getScaleX();
+    LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(),
+                                                            fIntervals->end() - 1,
+                                                            this->findInterval(fx),
+                                                            fx,
+                                                            dx,
+                                                            SkScalarNearlyZero(dx * count));
+    while (count > 0) {
+        // What we really want here is SkTPin(advance, 1, count)
+        // but that's a significant perf hit for >> stops; investigate.
+        const int n = SkScalarTruncToInt(
+            SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count)));
+
+        // The current interval advance can be +inf (e.g. when reaching
+        // the clamp mode end intervals) - when that happens, we expect to
+        //   a) consume all remaining count in one swoop
+        //   b) return a zero color gradient
+        SkASSERT(SkScalarIsFinite(proc.currentAdvance())
+            || (n == count && proc.currentRampIsZero()));
+
+        if (proc.currentRampIsZero()) {
+            DstTraits<dstType, premul>::store(proc.currentColor(),
+                                              dst, n);
+        } else {
+            ramp<dstType, premul>(proc.currentColor(),
+                                  proc.currentColorGrad(),
+                                  dst, n);
+        }
+
+        proc.advance(SkIntToScalar(n));
+        count -= n;
+        dst   += n;
+    }
+}
+
+template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
+class SkLinearGradient::
+LinearGradient4fContext::LinearIntervalProcessor {
+public:
+    LinearIntervalProcessor(const Sk4fGradientInterval* firstInterval,
+                            const Sk4fGradientInterval* lastInterval,
+                            const Sk4fGradientInterval* i,
+                            SkScalar fx,
+                            SkScalar dx,
+                            bool is_vertical)
+        : fAdvX(is_vertical ? SK_ScalarInfinity : (i->fT1 - fx) / dx)
+        , fFirstInterval(firstInterval)
+        , fLastInterval(lastInterval)
+        , fInterval(i)
+        , fDx(dx)
+        , fIsVertical(is_vertical)
+    {
+        SkASSERT(fAdvX >= 0);
+        SkASSERT(firstInterval <= lastInterval);
+
+        if (tileMode != kClamp_TileMode && !is_vertical) {
+            const auto spanX = (lastInterval->fT1 - firstInterval->fT0) / dx;
+            SkASSERT(spanX >= 0);
+
+            // If we're in a repeating tile mode and the whole gradient is compressed into a
+            // fraction of a pixel, we just use the average color in zero-ramp mode.
+            // This also avoids cases where we make no progress due to interval advances being
+            // close to zero.
+            static constexpr SkScalar kMinSpanX = .25f;
+            if (spanX < kMinSpanX) {
+                this->init_average_props();
+                return;
+            }
+        }
+
+        this->compute_interval_props(fx);
+    }
+
+    SkScalar currentAdvance() const {
+        SkASSERT(fAdvX >= 0);
+        SkASSERT(fAdvX <= (fInterval->fT1 - fInterval->fT0) / fDx || !std::isfinite(fAdvX));
+        return fAdvX;
+    }
+
+    bool currentRampIsZero() const { return fZeroRamp; }
+    const Sk4f& currentColor() const { return fCc; }
+    const Sk4f& currentColorGrad() const { return fDcDx; }
+
+    void advance(SkScalar advX) {
+        SkASSERT(advX > 0);
+        SkASSERT(fAdvX >= 0);
+
+        if (advX >= fAdvX) {
+            advX = this->advance_interval(advX);
+        }
+        SkASSERT(advX < fAdvX);
+
+        fCc = fCc + fDcDx * Sk4f(advX);
+        fAdvX -= advX;
+    }
+
+private:
+    void compute_interval_props(SkScalar t) {
+        SkASSERT(in_range(t, fInterval->fT0, fInterval->fT1));
+
+        const Sk4f dc = DstTraits<dstType, premul>::load(fInterval->fCg);
+                  fCc = DstTraits<dstType, premul>::load(fInterval->fCb) + dc * Sk4f(t);
+                fDcDx = dc * fDx;
+            fZeroRamp = fIsVertical || (dc == 0).allTrue();
+    }
+
+    void init_average_props() {
+        fAdvX     = SK_ScalarInfinity;
+        fZeroRamp = true;
+        fDcDx     = 0;
+        fCc       = Sk4f(0);
+
+        // TODO: precompute the average at interval setup time?
+        for (const auto* i = fFirstInterval; i <= fLastInterval; ++i) {
+            // Each interval contributes its average color to the total/weighted average:
+            //
+            //   C = (c0 + c1) / 2 = (Cb + Cg * t0 + Cb + Cg * t1) / 2 = Cb + Cg *(t0 + t1) / 2
+            //
+            //   Avg += C * (t1 - t0)
+            //
+            const auto c = DstTraits<dstType, premul>::load(i->fCb)
+                         + DstTraits<dstType, premul>::load(i->fCg) * (i->fT0 + i->fT1) * 0.5f;
+            fCc = fCc + c * (i->fT1 - i->fT0);
+        }
+    }
+
+    const Sk4fGradientInterval* next_interval(const Sk4fGradientInterval* i) const {
+        SkASSERT(i >= fFirstInterval);
+        SkASSERT(i <= fLastInterval);
+        i++;
+
+        if (tileMode == kClamp_TileMode) {
+            SkASSERT(i <= fLastInterval);
+            return i;
+        }
+
+        return (i <= fLastInterval) ? i : fFirstInterval;
+    }
+
+    SkScalar advance_interval(SkScalar advX) {
+        SkASSERT(advX >= fAdvX);
+
+        do {
+            advX -= fAdvX;
+            fInterval = this->next_interval(fInterval);
+            fAdvX = (fInterval->fT1 - fInterval->fT0) / fDx;
+            SkASSERT(fAdvX > 0);
+        } while (advX >= fAdvX);
+
+        compute_interval_props(fInterval->fT0);
+
+        SkASSERT(advX >= 0);
+        return advX;
+    }
+
+    // Current interval properties.
+    Sk4f            fDcDx;      // dst color gradient (dc/dx)
+    Sk4f            fCc;        // current color, interpolated in dst
+    SkScalar        fAdvX;      // remaining interval advance in dst
+    bool            fZeroRamp;  // current interval color grad is 0
+
+    const Sk4fGradientInterval* fFirstInterval;
+    const Sk4fGradientInterval* fLastInterval;
+    const Sk4fGradientInterval* fInterval;  // current interval
+    const SkScalar              fDx;        // 'dx' for consistency with other impls; actually dt/dx
+    const bool                  fIsVertical;
+};
+
+void SkLinearGradient::
+LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const {
+    SkASSERT(count > 0);
+    SkASSERT(fDstToPosClass != kLinear_MatrixClass);
+
+    SkScalar sx = x + SK_ScalarHalf;
+    const SkScalar sy = y + SK_ScalarHalf;
+    SkPoint pt;
+
+    if (fDstToPosClass != kPerspective_MatrixClass) {
+        // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline
+        const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x();
+        fDstToPosProc(fDstToPos, sx, sy, &pt);
+
+        const Sk4f dtdx4 = Sk4f(4 * dtdx);
+        Sk4f t4 = Sk4f(pt.x() + 0 * dtdx,
+                       pt.x() + 1 * dtdx,
+                       pt.x() + 2 * dtdx,
+                       pt.x() + 3 * dtdx);
+
+        while (count >= 4) {
+            t4.store(ts);
+            t4 = t4 + dtdx4;
+            ts += 4;
+            count -= 4;
+        }
+
+        if (count & 2) {
+            *ts++ = t4[0];
+            *ts++ = t4[1];
+            t4 = SkNx_shuffle<2, 0, 1, 3>(t4);
+        }
+
+        if (count & 1) {
+            *ts++ = t4[0];
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            fDstToPosProc(fDstToPos, sx, sy, &pt);
+            // Perspective may yield NaN values.
+            // Short of a better idea, drop to 0.
+            ts[i] = SkScalarIsNaN(pt.x()) ? 0 : pt.x();
+            sx += SK_Scalar1;
+        }
+    }
+}
diff --git a/src/shaders/gradients/Sk4fLinearGradient.h b/src/shaders/gradients/Sk4fLinearGradient.h
new file mode 100644
index 0000000..f1c0bb5
--- /dev/null
+++ b/src/shaders/gradients/Sk4fLinearGradient.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Sk4fLinearGradient_DEFINED
+#define Sk4fLinearGradient_DEFINED
+
+#include "Sk4fGradientBase.h"
+#include "SkLinearGradient.h"
+
+class SkLinearGradient::
+LinearGradient4fContext final : public GradientShaderBase4fContext {
+public:
+    LinearGradient4fContext(const SkLinearGradient&, const ContextRec&);
+
+    void shadeSpan(int x, int y, SkPMColor dst[], int count) override;
+    void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override;
+
+protected:
+    void mapTs(int x, int y, SkScalar ts[], int count) const override;
+
+private:
+    using INHERITED = GradientShaderBase4fContext;
+
+    template<DstType, ApplyPremul, TileMode>
+    class LinearIntervalProcessor;
+
+    template <DstType dstType, ApplyPremul premul>
+    void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[],
+                         int count) const;
+
+    template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode>
+    void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[],
+                           int count) const;
+
+    const Sk4fGradientInterval* findInterval(SkScalar fx) const;
+
+    bool isFast() const { return fDstToPosClass == kLinear_MatrixClass; }
+
+    mutable const Sk4fGradientInterval* fCachedInterval;
+};
+
+#endif // Sk4fLinearGradient_DEFINED
diff --git a/src/effects/gradients/SkClampRange.cpp b/src/shaders/gradients/SkClampRange.cpp
similarity index 100%
rename from src/effects/gradients/SkClampRange.cpp
rename to src/shaders/gradients/SkClampRange.cpp
diff --git a/src/effects/gradients/SkClampRange.h b/src/shaders/gradients/SkClampRange.h
similarity index 100%
rename from src/effects/gradients/SkClampRange.h
rename to src/shaders/gradients/SkClampRange.h
diff --git a/src/effects/gradients/SkGradientBitmapCache.cpp b/src/shaders/gradients/SkGradientBitmapCache.cpp
similarity index 100%
rename from src/effects/gradients/SkGradientBitmapCache.cpp
rename to src/shaders/gradients/SkGradientBitmapCache.cpp
diff --git a/src/effects/gradients/SkGradientBitmapCache.h b/src/shaders/gradients/SkGradientBitmapCache.h
similarity index 100%
rename from src/effects/gradients/SkGradientBitmapCache.h
rename to src/shaders/gradients/SkGradientBitmapCache.h
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
new file mode 100644
index 0000000..ab774f6
--- /dev/null
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -0,0 +1,2010 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <algorithm>
+#include "Sk4fLinearGradient.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkGradientShaderPriv.h"
+#include "SkHalf.h"
+#include "SkLinearGradient.h"
+#include "SkMallocPixelRef.h"
+#include "SkRadialGradient.h"
+#include "SkSweepGradient.h"
+#include "SkTwoPointConicalGradient.h"
+#include "../../jumper/SkJumper.h"
+
+
+enum GradientSerializationFlags {
+    // Bits 29:31 used for various boolean flags
+    kHasPosition_GSF    = 0x80000000,
+    kHasLocalMatrix_GSF = 0x40000000,
+    kHasColorSpace_GSF  = 0x20000000,
+
+    // Bits 12:28 unused
+
+    // Bits 8:11 for fTileMode
+    kTileModeShift_GSF  = 8,
+    kTileModeMask_GSF   = 0xF,
+
+    // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
+    kGradFlagsShift_GSF = 0,
+    kGradFlagsMask_GSF  = 0xFF,
+};
+
+void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
+    uint32_t flags = 0;
+    if (fPos) {
+        flags |= kHasPosition_GSF;
+    }
+    if (fLocalMatrix) {
+        flags |= kHasLocalMatrix_GSF;
+    }
+    sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
+    if (colorSpaceData) {
+        flags |= kHasColorSpace_GSF;
+    }
+    SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
+    flags |= (fTileMode << kTileModeShift_GSF);
+    SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
+    flags |= (fGradFlags << kGradFlagsShift_GSF);
+
+    buffer.writeUInt(flags);
+
+    buffer.writeColor4fArray(fColors, fCount);
+    if (colorSpaceData) {
+        buffer.writeDataAsByteArray(colorSpaceData.get());
+    }
+    if (fPos) {
+        buffer.writeScalarArray(fPos, fCount);
+    }
+    if (fLocalMatrix) {
+        buffer.writeMatrix(*fLocalMatrix);
+    }
+}
+
+bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
+    if (buffer.isVersionLT(SkReadBuffer::kGradientShaderFloatColor_Version)) {
+        fCount = buffer.getArrayCount();
+        if (fCount > kStorageCount) {
+            size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
+            fDynamicStorage.reset(allocSize);
+            fColors = (SkColor4f*)fDynamicStorage.get();
+            fPos = (SkScalar*)(fColors + fCount);
+        } else {
+            fColors = fColorStorage;
+            fPos = fPosStorage;
+        }
+
+        // Old gradients serialized SkColor. Read that to a temporary location, then convert.
+        SkSTArray<2, SkColor, true> colors;
+        colors.resize_back(fCount);
+        if (!buffer.readColorArray(colors.begin(), fCount)) {
+            return false;
+        }
+        for (int i = 0; i < fCount; ++i) {
+            mutableColors()[i] = SkColor4f::FromColor(colors[i]);
+        }
+
+        if (buffer.readBool()) {
+            if (!buffer.readScalarArray(const_cast<SkScalar*>(fPos), fCount)) {
+                return false;
+            }
+        } else {
+            fPos = nullptr;
+        }
+
+        fColorSpace = nullptr;
+        fTileMode = (SkShader::TileMode)buffer.read32();
+        fGradFlags = buffer.read32();
+
+        if (buffer.readBool()) {
+            fLocalMatrix = &fLocalMatrixStorage;
+            buffer.readMatrix(&fLocalMatrixStorage);
+        } else {
+            fLocalMatrix = nullptr;
+        }
+    } else {
+        // New gradient format. Includes floating point color, color space, densely packed flags
+        uint32_t flags = buffer.readUInt();
+
+        fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
+        fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
+
+        fCount = buffer.getArrayCount();
+        if (fCount > kStorageCount) {
+            size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount;
+            fDynamicStorage.reset(allocSize);
+            fColors = (SkColor4f*)fDynamicStorage.get();
+            fPos = (SkScalar*)(fColors + fCount);
+        } else {
+            fColors = fColorStorage;
+            fPos = fPosStorage;
+        }
+        if (!buffer.readColor4fArray(mutableColors(), fCount)) {
+            return false;
+        }
+        if (SkToBool(flags & kHasColorSpace_GSF)) {
+            sk_sp<SkData> data = buffer.readByteArrayAsData();
+            fColorSpace = SkColorSpace::Deserialize(data->data(), data->size());
+        } else {
+            fColorSpace = nullptr;
+        }
+        if (SkToBool(flags & kHasPosition_GSF)) {
+            if (!buffer.readScalarArray(mutablePos(), fCount)) {
+                return false;
+            }
+        } else {
+            fPos = nullptr;
+        }
+        if (SkToBool(flags & kHasLocalMatrix_GSF)) {
+            fLocalMatrix = &fLocalMatrixStorage;
+            buffer.readMatrix(&fLocalMatrixStorage);
+        } else {
+            fLocalMatrix = nullptr;
+        }
+    }
+    return buffer.isValid();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
+    : INHERITED(desc.fLocalMatrix)
+    , fPtsToUnit(ptsToUnit)
+{
+    fPtsToUnit.getType();  // Precache so reads are threadsafe.
+    SkASSERT(desc.fCount > 1);
+
+    fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
+
+    SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount);
+    SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
+    fTileMode = desc.fTileMode;
+    fTileProc = gTileProcs[desc.fTileMode];
+
+    /*  Note: we let the caller skip the first and/or last position.
+        i.e. pos[0] = 0.3, pos[1] = 0.7
+        In these cases, we insert dummy entries to ensure that the final data
+        will be bracketed by [0, 1].
+        i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
+
+        Thus colorCount (the caller's value, and fColorCount (our value) may
+        differ by up to 2. In the above example:
+            colorCount = 2
+            fColorCount = 4
+     */
+    fColorCount = desc.fCount;
+    // check if we need to add in dummy start and/or end position/colors
+    bool dummyFirst = false;
+    bool dummyLast = false;
+    if (desc.fPos) {
+        dummyFirst = desc.fPos[0] != 0;
+        dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
+        fColorCount += dummyFirst + dummyLast;
+    }
+
+    if (fColorCount > kColorStorageCount) {
+        size_t size = sizeof(SkColor) + sizeof(SkColor4f) + sizeof(Rec);
+        if (desc.fPos) {
+            size += sizeof(SkScalar);
+        }
+        fOrigColors = reinterpret_cast<SkColor*>(sk_malloc_throw(size * fColorCount));
+    }
+    else {
+        fOrigColors = fStorage;
+    }
+
+    fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount);
+
+    // Now copy over the colors, adding the dummies as needed
+    SkColor4f* origColors = fOrigColors4f;
+    if (dummyFirst) {
+        *origColors++ = desc.fColors[0];
+    }
+    memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor4f));
+    if (dummyLast) {
+        origColors += desc.fCount;
+        *origColors = desc.fColors[desc.fCount - 1];
+    }
+
+    // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the
+    // source colors are not in sRGB gamut. We would need to do a gamut transformation, but
+    // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU
+    // support compiled in here. For the common case (sRGB colors), this does the right thing.
+    for (int i = 0; i < fColorCount; ++i) {
+        fOrigColors[i] = fOrigColors4f[i].toSkColor();
+    }
+
+    if (!desc.fColorSpace) {
+        // This happens if we were constructed from SkColors, so our colors are really sRGB
+        fColorSpace = SkColorSpace::MakeSRGBLinear();
+    } else {
+        // The color space refers to the float colors, so it must be linear gamma
+        SkASSERT(desc.fColorSpace->gammaIsLinear());
+        fColorSpace = desc.fColorSpace;
+    }
+
+    if (desc.fPos && fColorCount) {
+        fOrigPos = (SkScalar*)(fOrigColors4f + fColorCount);
+        fRecs = (Rec*)(fOrigPos + fColorCount);
+    } else {
+        fOrigPos = nullptr;
+        fRecs = (Rec*)(fOrigColors4f + fColorCount);
+    }
+
+    if (fColorCount > 2) {
+        Rec* recs = fRecs;
+        recs->fPos = 0;
+        //  recs->fScale = 0; // unused;
+        recs += 1;
+        if (desc.fPos) {
+            SkScalar* origPosPtr = fOrigPos;
+            *origPosPtr++ = 0;
+
+            /*  We need to convert the user's array of relative positions into
+                fixed-point positions and scale factors. We need these results
+                to be strictly monotonic (no two values equal or out of order).
+                Hence this complex loop that just jams a zero for the scale
+                value if it sees a segment out of order, and it assures that
+                we start at 0 and end at 1.0
+            */
+            SkScalar prev = 0;
+            int startIndex = dummyFirst ? 0 : 1;
+            int count = desc.fCount + dummyLast;
+            for (int i = startIndex; i < count; i++) {
+                // force the last value to be 1.0
+                SkScalar curr;
+                if (i == desc.fCount) {  // we're really at the dummyLast
+                    curr = 1;
+                } else {
+                    curr = SkScalarPin(desc.fPos[i], 0, 1);
+                }
+                *origPosPtr++ = curr;
+
+                recs->fPos = SkScalarToFixed(curr);
+                SkFixed diff = SkScalarToFixed(curr - prev);
+                if (diff > 0) {
+                    recs->fScale = (1 << 24) / diff;
+                } else {
+                    recs->fScale = 0; // ignore this segment
+                }
+                // get ready for the next value
+                prev = curr;
+                recs += 1;
+            }
+        } else {    // assume even distribution
+            fOrigPos = nullptr;
+
+            SkFixed dp = SK_Fixed1 / (desc.fCount - 1);
+            SkFixed p = dp;
+            SkFixed scale = (desc.fCount - 1) << 8;  // (1 << 24) / dp
+            for (int i = 1; i < desc.fCount - 1; i++) {
+                recs->fPos   = p;
+                recs->fScale = scale;
+                recs += 1;
+                p += dp;
+            }
+            recs->fPos = SK_Fixed1;
+            recs->fScale = scale;
+        }
+    } else if (desc.fPos) {
+        SkASSERT(2 == fColorCount);
+        fOrigPos[0] = SkScalarPin(desc.fPos[0], 0, 1);
+        fOrigPos[1] = SkScalarPin(desc.fPos[1], fOrigPos[0], 1);
+        if (0 == fOrigPos[0] && 1 == fOrigPos[1]) {
+            fOrigPos = nullptr;
+        }
+    }
+    this->initCommon();
+}
+
+SkGradientShaderBase::~SkGradientShaderBase() {
+    if (fOrigColors != fStorage) {
+        sk_free(fOrigColors);
+    }
+}
+
+void SkGradientShaderBase::initCommon() {
+    unsigned colorAlpha = 0xFF;
+    for (int i = 0; i < fColorCount; i++) {
+        colorAlpha &= SkColorGetA(fOrigColors[i]);
+    }
+    fColorsAreOpaque = colorAlpha == 0xFF;
+}
+
+void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
+    Descriptor desc;
+    desc.fColors = fOrigColors4f;
+    desc.fColorSpace = fColorSpace;
+    desc.fPos = fOrigPos;
+    desc.fCount = fColorCount;
+    desc.fTileMode = fTileMode;
+    desc.fGradFlags = fGradFlags;
+
+    const SkMatrix& m = this->getLocalMatrix();
+    desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
+    desc.flatten(buffer);
+}
+
+void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst,
+                                              SkColor* colorSrc, Rec* recSrc,
+                                              int count) {
+    SkAutoSTArray<8, SkColor> colorsTemp(count);
+    for (int i = 0; i < count; ++i) {
+        int offset = count - i - 1;
+        colorsTemp[i] = colorSrc[offset];
+    }
+    if (count > 2) {
+        SkAutoSTArray<8, Rec> recsTemp(count);
+        for (int i = 0; i < count; ++i) {
+            int offset = count - i - 1;
+            recsTemp[i].fPos = SK_Fixed1 - recSrc[offset].fPos;
+            recsTemp[i].fScale = recSrc[offset].fScale;
+        }
+        memcpy(recDst, recsTemp.get(), count * sizeof(Rec));
+    }
+    memcpy(colorDst, colorsTemp.get(), count * sizeof(SkColor));
+}
+
+static void add_stop_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f Fs, SkPM4f Bs) {
+    (ctx->fs[0])[stop] = Fs.r();
+    (ctx->fs[1])[stop] = Fs.g();
+    (ctx->fs[2])[stop] = Fs.b();
+    (ctx->fs[3])[stop] = Fs.a();
+    (ctx->bs[0])[stop] = Bs.r();
+    (ctx->bs[1])[stop] = Bs.g();
+    (ctx->bs[2])[stop] = Bs.b();
+    (ctx->bs[3])[stop] = Bs.a();
+}
+
+static void add_const_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f color) {
+    add_stop_color(ctx, stop, SkPM4f::FromPremulRGBA(0,0,0,0), color);
+}
+
+// Calculate a factor F and a bias B so that color = F*t + B when t is in range of
+// the stop. Assume that the distance between stops is 1/gapCount.
+static void init_stop_evenly(
+    SkJumper_GradientCtx* ctx, float gapCount, size_t stop, SkPM4f c_l, SkPM4f c_r) {
+    // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
+    SkPM4f Fs = {{
+        (c_r.r() - c_l.r()) * gapCount,
+        (c_r.g() - c_l.g()) * gapCount,
+        (c_r.b() - c_l.b()) * gapCount,
+        (c_r.a() - c_l.a()) * gapCount,
+    }};
+    SkPM4f Bs = {{
+        c_l.r() - Fs.r()*(stop/gapCount),
+        c_l.g() - Fs.g()*(stop/gapCount),
+        c_l.b() - Fs.b()*(stop/gapCount),
+        c_l.a() - Fs.a()*(stop/gapCount),
+    }};
+    add_stop_color(ctx, stop, Fs, Bs);
+}
+
+// For each stop we calculate a bias B and a scale factor F, such that
+// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
+static void init_stop_pos(
+    SkJumper_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPM4f c_l, SkPM4f c_r) {
+    // See note about Clankium's old compiler in init_stop_evenly().
+    SkPM4f Fs = {{
+        (c_r.r() - c_l.r()) / (t_r - t_l),
+        (c_r.g() - c_l.g()) / (t_r - t_l),
+        (c_r.b() - c_l.b()) / (t_r - t_l),
+        (c_r.a() - c_l.a()) / (t_r - t_l),
+    }};
+    SkPM4f Bs = {{
+        c_l.r() - Fs.r()*t_l,
+        c_l.g() - Fs.g()*t_l,
+        c_l.b() - Fs.b()*t_l,
+        c_l.a() - Fs.a()*t_l,
+    }};
+    ctx->ts[stop] = t_l;
+    add_stop_color(ctx, stop, Fs, Bs);
+}
+
+bool SkGradientShaderBase::onAppendStages(SkRasterPipeline* p,
+                                          SkColorSpace* dstCS,
+                                          SkArenaAlloc* alloc,
+                                          const SkMatrix& ctm,
+                                          const SkPaint& paint,
+                                          const SkMatrix* localM) const {
+    SkMatrix matrix;
+    if (!this->computeTotalInverse(ctm, localM, &matrix)) {
+        return false;
+    }
+
+    SkRasterPipeline_<256> subclass;
+    if (!this->adjustMatrixAndAppendStages(alloc, &matrix, &subclass)) {
+        return this->INHERITED::onAppendStages(p, dstCS, alloc, ctm, paint, localM);
+    }
+
+    p->append(SkRasterPipeline::seed_shader);
+
+    auto* m = alloc->makeArrayDefault<float>(9);
+    if (matrix.asAffine(m)) {
+        p->append(SkRasterPipeline::matrix_2x3, m);
+    } else {
+        matrix.get9(m);
+        p->append(SkRasterPipeline::matrix_perspective, m);
+    }
+
+    p->extend(subclass);
+
+    switch(fTileMode) {
+        case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x_1); break;
+        case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x_1); break;
+        case kClamp_TileMode:
+            if (!fOrigPos) {
+                // We clamp only when the stops are evenly spaced.
+                // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
+                // In that case, we must make sure we're using the general "gradient" stage,
+                // which is the only stage that will correctly handle unclamped t.
+                p->append(SkRasterPipeline::clamp_x_1);
+            }
+    }
+
+    const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
+    auto prepareColor = [premulGrad, dstCS, this](int i) {
+        SkColor4f c = this->getXformedColor(i, dstCS);
+        return premulGrad ? c.premul()
+                          : SkPM4f::From4f(Sk4f::Load(&c));
+    };
+
+    // The two-stop case with stops at 0 and 1.
+    if (fColorCount == 2 && fOrigPos == nullptr) {
+        const SkPM4f c_l = prepareColor(0),
+            c_r = prepareColor(1);
+
+        // See F and B below.
+        auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2);
+        f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f());
+        f_and_b[1] = c_l;
+
+        p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, f_and_b);
+    } else {
+        auto* ctx = alloc->make<SkJumper_GradientCtx>();
+
+        // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
+        // at -inf. Therefore, the max number of stops is fColorCount+1.
+        for (int i = 0; i < 4; i++) {
+            // Allocate at least at for the AVX2 gather from a YMM register.
+            ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
+            ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
+        }
+
+        if (fOrigPos == nullptr) {
+            // Handle evenly distributed stops.
+
+            size_t stopCount = fColorCount;
+            float gapCount = stopCount - 1;
+
+            SkPM4f c_l = prepareColor(0);
+            for (size_t i = 0; i < stopCount - 1; i++) {
+                SkPM4f c_r = prepareColor(i + 1);
+                init_stop_evenly(ctx, gapCount, i, c_l, c_r);
+                c_l = c_r;
+            }
+            add_const_color(ctx, stopCount - 1, c_l);
+
+            ctx->stopCount = stopCount;
+            p->append(SkRasterPipeline::evenly_spaced_gradient, ctx);
+        } else {
+            // Handle arbitrary stops.
+
+            ctx->ts = alloc->makeArray<float>(fColorCount+1);
+
+            // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
+            // because they are naturally handled by the search method.
+            int firstStop;
+            int lastStop;
+            if (fColorCount > 2) {
+                firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
+                lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
+                           ? fColorCount - 1 : fColorCount - 2;
+            } else {
+                firstStop = 0;
+                lastStop = 1;
+            }
+
+            size_t stopCount = 0;
+            float  t_l = fOrigPos[firstStop];
+            SkPM4f c_l = prepareColor(firstStop);
+            add_const_color(ctx, stopCount++, c_l);
+            // N.B. lastStop is the index of the last stop, not one after.
+            for (int i = firstStop; i < lastStop; i++) {
+                float  t_r = fOrigPos[i + 1];
+                SkPM4f c_r = prepareColor(i + 1);
+                if (t_l < t_r) {
+                    init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
+                    stopCount += 1;
+                }
+                t_l = t_r;
+                c_l = c_r;
+            }
+
+            ctx->ts[stopCount] = t_l;
+            add_const_color(ctx, stopCount++, c_l);
+
+            ctx->stopCount = stopCount;
+            p->append(SkRasterPipeline::gradient, ctx);
+        }
+    }
+
+    if (!premulGrad && !this->colorsAreOpaque()) {
+        p->append(SkRasterPipeline::premul);
+    }
+
+    return true;
+}
+
+
+bool SkGradientShaderBase::isOpaque() const {
+    return fColorsAreOpaque;
+}
+
+static unsigned rounded_divide(unsigned numer, unsigned denom) {
+    return (numer + (denom >> 1)) / denom;
+}
+
+bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
+    // we just compute an average color.
+    // possibly we could weight this based on the proportional width for each color
+    //   assuming they are not evenly distributed in the fPos array.
+    int r = 0;
+    int g = 0;
+    int b = 0;
+    const int n = fColorCount;
+    for (int i = 0; i < n; ++i) {
+        SkColor c = fOrigColors[i];
+        r += SkColorGetR(c);
+        g += SkColorGetG(c);
+        b += SkColorGetB(c);
+    }
+    *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
+    return true;
+}
+
+SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext(
+        const SkGradientShaderBase& shader, const ContextRec& rec)
+    : INHERITED(shader, rec)
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING
+    , fDither(true)
+#else
+    , fDither(rec.fPaint->isDither())
+#endif
+    , fCache(shader.refCache(getPaintAlpha(), fDither))
+{
+    const SkMatrix& inverse = this->getTotalInverse();
+
+    fDstToIndex.setConcat(shader.fPtsToUnit, inverse);
+
+    fDstToIndexProc = fDstToIndex.getMapXYProc();
+    fDstToIndexClass = (uint8_t)SkShaderBase::Context::ComputeMatrixClass(fDstToIndex);
+
+    // now convert our colors in to PMColors
+    unsigned paintAlpha = this->getPaintAlpha();
+
+    fFlags = this->INHERITED::getFlags();
+    if (shader.fColorsAreOpaque && paintAlpha == 0xFF) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+}
+
+bool SkGradientShaderBase::GradientShaderBaseContext::isValid() const {
+    return fDstToIndex.isFinite();
+}
+
+SkGradientShaderBase::GradientShaderCache::GradientShaderCache(
+        U8CPU alpha, bool dither, const SkGradientShaderBase& shader)
+    : fCacheAlpha(alpha)
+    , fCacheDither(dither)
+    , fShader(shader)
+{
+    // Only initialize the cache in getCache32.
+    fCache32 = nullptr;
+}
+
+SkGradientShaderBase::GradientShaderCache::~GradientShaderCache() {}
+
+/*
+ *  r,g,b used to be SkFixed, but on gcc (4.2.1 mac and 4.6.3 goobuntu) in
+ *  release builds, we saw a compiler error where the 0xFF parameter in
+ *  SkPackARGB32() was being totally ignored whenever it was called with
+ *  a non-zero add (e.g. 0x8000).
+ *
+ *  We found two work-arounds:
+ *      1. change r,g,b to unsigned (or just one of them)
+ *      2. change SkPackARGB32 to + its (a << SK_A32_SHIFT) value instead
+ *         of using |
+ *
+ *  We chose #1 just because it was more localized.
+ *  See http://code.google.com/p/skia/issues/detail?id=1113
+ *
+ *  The type SkUFixed encapsulate this need for unsigned, but logically Fixed.
+ */
+typedef uint32_t SkUFixed;
+
+void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
+        SkPMColor cache[], SkColor c0, SkColor c1,
+        int count, U8CPU paintAlpha, uint32_t gradFlags, bool dither) {
+    SkASSERT(count > 1);
+
+    // need to apply paintAlpha to our two endpoints
+    uint32_t a0 = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
+    uint32_t a1 = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
+
+
+    const bool interpInPremul = SkToBool(gradFlags &
+                           SkGradientShader::kInterpolateColorsInPremul_Flag);
+
+    uint32_t r0 = SkColorGetR(c0);
+    uint32_t g0 = SkColorGetG(c0);
+    uint32_t b0 = SkColorGetB(c0);
+
+    uint32_t r1 = SkColorGetR(c1);
+    uint32_t g1 = SkColorGetG(c1);
+    uint32_t b1 = SkColorGetB(c1);
+
+    if (interpInPremul) {
+        r0 = SkMulDiv255Round(r0, a0);
+        g0 = SkMulDiv255Round(g0, a0);
+        b0 = SkMulDiv255Round(b0, a0);
+
+        r1 = SkMulDiv255Round(r1, a1);
+        g1 = SkMulDiv255Round(g1, a1);
+        b1 = SkMulDiv255Round(b1, a1);
+    }
+
+    SkFixed da = SkIntToFixed(a1 - a0) / (count - 1);
+    SkFixed dr = SkIntToFixed(r1 - r0) / (count - 1);
+    SkFixed dg = SkIntToFixed(g1 - g0) / (count - 1);
+    SkFixed db = SkIntToFixed(b1 - b0) / (count - 1);
+
+    /*  We pre-add 1/8 to avoid having to add this to our [0] value each time
+        in the loop. Without this, the bias for each would be
+            0x2000  0xA000  0xE000  0x6000
+        With this trick, we can add 0 for the first (no-op) and just adjust the
+        others.
+     */
+    const SkUFixed bias0 = dither ? 0x2000 : 0x8000;
+    const SkUFixed bias1 = dither ? 0x8000 : 0;
+    const SkUFixed bias2 = dither ? 0xC000 : 0;
+    const SkUFixed bias3 = dither ? 0x4000 : 0;
+
+    SkUFixed a = SkIntToFixed(a0) + bias0;
+    SkUFixed r = SkIntToFixed(r0) + bias0;
+    SkUFixed g = SkIntToFixed(g0) + bias0;
+    SkUFixed b = SkIntToFixed(b0) + bias0;
+
+    /*
+     *  Our dither-cell (spatially) is
+     *      0 2
+     *      3 1
+     *  Where
+     *      [0] -> [-1/8 ... 1/8 ) values near 0
+     *      [1] -> [ 1/8 ... 3/8 ) values near 1/4
+     *      [2] -> [ 3/8 ... 5/8 ) values near 1/2
+     *      [3] -> [ 5/8 ... 7/8 ) values near 3/4
+     */
+
+    if (0xFF == a0 && 0 == da) {
+        do {
+            cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0    ) >> 16,
+                                                        (g + 0    ) >> 16,
+                                                        (b + 0    ) >> 16);
+            cache[kCache32Count*1] = SkPackARGB32(0xFF, (r + bias1) >> 16,
+                                                        (g + bias1) >> 16,
+                                                        (b + bias1) >> 16);
+            cache[kCache32Count*2] = SkPackARGB32(0xFF, (r + bias2) >> 16,
+                                                        (g + bias2) >> 16,
+                                                        (b + bias2) >> 16);
+            cache[kCache32Count*3] = SkPackARGB32(0xFF, (r + bias3) >> 16,
+                                                        (g + bias3) >> 16,
+                                                        (b + bias3) >> 16);
+            cache += 1;
+            r += dr;
+            g += dg;
+            b += db;
+        } while (--count != 0);
+    } else if (interpInPremul) {
+        do {
+            cache[kCache32Count*0] = SkPackARGB32((a + 0    ) >> 16,
+                                                  (r + 0    ) >> 16,
+                                                  (g + 0    ) >> 16,
+                                                  (b + 0    ) >> 16);
+            cache[kCache32Count*1] = SkPackARGB32((a + bias1) >> 16,
+                                                  (r + bias1) >> 16,
+                                                  (g + bias1) >> 16,
+                                                  (b + bias1) >> 16);
+            cache[kCache32Count*2] = SkPackARGB32((a + bias2) >> 16,
+                                                  (r + bias2) >> 16,
+                                                  (g + bias2) >> 16,
+                                                  (b + bias2) >> 16);
+            cache[kCache32Count*3] = SkPackARGB32((a + bias3) >> 16,
+                                                  (r + bias3) >> 16,
+                                                  (g + bias3) >> 16,
+                                                  (b + bias3) >> 16);
+            cache += 1;
+            a += da;
+            r += dr;
+            g += dg;
+            b += db;
+        } while (--count != 0);
+    } else {    // interpolate in unpreml space
+        do {
+            cache[kCache32Count*0] = SkPremultiplyARGBInline((a + 0     ) >> 16,
+                                                             (r + 0     ) >> 16,
+                                                             (g + 0     ) >> 16,
+                                                             (b + 0     ) >> 16);
+            cache[kCache32Count*1] = SkPremultiplyARGBInline((a + bias1) >> 16,
+                                                             (r + bias1) >> 16,
+                                                             (g + bias1) >> 16,
+                                                             (b + bias1) >> 16);
+            cache[kCache32Count*2] = SkPremultiplyARGBInline((a + bias2) >> 16,
+                                                             (r + bias2) >> 16,
+                                                             (g + bias2) >> 16,
+                                                             (b + bias2) >> 16);
+            cache[kCache32Count*3] = SkPremultiplyARGBInline((a + bias3) >> 16,
+                                                             (r + bias3) >> 16,
+                                                             (g + bias3) >> 16,
+                                                             (b + bias3) >> 16);
+            cache += 1;
+            a += da;
+            r += dr;
+            g += dg;
+            b += db;
+        } while (--count != 0);
+    }
+}
+
+static inline int SkFixedToFFFF(SkFixed x) {
+    SkASSERT((unsigned)x <= SK_Fixed1);
+    return x - (x >> 16);
+}
+
+const SkPMColor* SkGradientShaderBase::GradientShaderCache::getCache32() {
+    fCache32InitOnce(SkGradientShaderBase::GradientShaderCache::initCache32, this);
+    SkASSERT(fCache32);
+    return fCache32;
+}
+
+void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache* cache) {
+    const int kNumberOfDitherRows = 4;
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(kCache32Count, kNumberOfDitherRows);
+
+    SkASSERT(nullptr == cache->fCache32PixelRef);
+    cache->fCache32PixelRef = SkMallocPixelRef::MakeAllocate(info, 0, nullptr);
+    cache->fCache32 = (SkPMColor*)cache->fCache32PixelRef->pixels();
+    if (cache->fShader.fColorCount == 2) {
+        Build32bitCache(cache->fCache32, cache->fShader.fOrigColors[0],
+                        cache->fShader.fOrigColors[1], kCache32Count, cache->fCacheAlpha,
+                        cache->fShader.fGradFlags, cache->fCacheDither);
+    } else {
+        Rec* rec = cache->fShader.fRecs;
+        int prevIndex = 0;
+        for (int i = 1; i < cache->fShader.fColorCount; i++) {
+            int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
+            SkASSERT(nextIndex < kCache32Count);
+
+            if (nextIndex > prevIndex)
+                Build32bitCache(cache->fCache32 + prevIndex, cache->fShader.fOrigColors[i-1],
+                                cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1,
+                                cache->fCacheAlpha, cache->fShader.fGradFlags, cache->fCacheDither);
+            prevIndex = nextIndex;
+        }
+    }
+}
+
+void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap) const {
+    const bool interpInPremul = SkToBool(fGradFlags &
+                                         SkGradientShader::kInterpolateColorsInPremul_Flag);
+    SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels());
+    uint32_t* pixelsS32 = reinterpret_cast<uint32_t*>(bitmap->getPixels());
+
+    typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t;
+
+    pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) {
+        Sk4h c = SkFloatToHalf_finite_ftz(x);
+        pixelsF16[4*index+0] = c[0];
+        pixelsF16[4*index+1] = c[1];
+        pixelsF16[4*index+2] = c[2];
+        pixelsF16[4*index+3] = c[3];
+    };
+    pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) {
+        pixelsS32[index] = Sk4f_toS32(c);
+    };
+
+    pixelWriteFn_t writeSizedPixel =
+        (kRGBA_F16_SkColorType == bitmap->colorType()) ? writeF16Pixel : writeS32Pixel;
+    pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) {
+        writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index);
+    };
+
+    pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel;
+
+    int prevIndex = 0;
+    for (int i = 1; i < fColorCount; i++) {
+        int nextIndex = (fColorCount == 2) ? (kCache32Count - 1)
+            : SkFixedToFFFF(fRecs[i].fPos) >> kCache32Shift;
+        SkASSERT(nextIndex < kCache32Count);
+
+        if (nextIndex > prevIndex) {
+            Sk4f c0 = Sk4f::Load(fOrigColors4f[i - 1].vec());
+            Sk4f c1 = Sk4f::Load(fOrigColors4f[i].vec());
+            if (interpInPremul) {
+                c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f);
+                c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f);
+            }
+
+            Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex));
+            Sk4f delta = (c1 - c0) * step;
+
+            for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) {
+                writePixel(c0, curIndex);
+                c0 += delta;
+            }
+        }
+        prevIndex = nextIndex;
+    }
+    SkASSERT(prevIndex == kCache32Count - 1);
+}
+
+/*
+ *  The gradient holds a cache for the most recent value of alpha. Successive
+ *  callers with the same alpha value will share the same cache.
+ */
+sk_sp<SkGradientShaderBase::GradientShaderCache> SkGradientShaderBase::refCache(U8CPU alpha,
+                                                                          bool dither) const {
+    SkAutoMutexAcquire ama(fCacheMutex);
+    if (!fCache || fCache->getAlpha() != alpha || fCache->getDither() != dither) {
+        fCache.reset(new GradientShaderCache(alpha, dither, *this));
+    }
+    // Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
+    // Otherwise, the pointer may have been overwritten on a different thread before the object's
+    // ref count was incremented.
+    return fCache;
+}
+
+SkColor4f SkGradientShaderBase::getXformedColor(size_t i, SkColorSpace* dstCS) const {
+    return dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS)
+                 : SkColor4f_from_SkColor(fOrigColors[i], nullptr);
+}
+
+SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex);
+/*
+ *  Because our caller might rebuild the same (logically the same) gradient
+ *  over and over, we'd like to return exactly the same "bitmap" if possible,
+ *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
+ *  To do that, we maintain a private cache of built-bitmaps, based on our
+ *  colors and positions. Note: we don't try to flatten the fMapper, so if one
+ *  is present, we skip the cache for now.
+ */
+void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap,
+                                                  GradientBitmapType bitmapType) const {
+    // our caller assumes no external alpha, so we ensure that our cache is built with 0xFF
+    sk_sp<GradientShaderCache> cache(this->refCache(0xFF, true));
+
+    // build our key: [numColors + colors[] + {positions[]} + flags + colorType ]
+    int count = 1 + fColorCount + 1 + 1;
+    if (fColorCount > 2) {
+        count += fColorCount - 1;    // fRecs[].fPos
+    }
+
+    SkAutoSTMalloc<16, int32_t> storage(count);
+    int32_t* buffer = storage.get();
+
+    *buffer++ = fColorCount;
+    memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
+    buffer += fColorCount;
+    if (fColorCount > 2) {
+        for (int i = 1; i < fColorCount; i++) {
+            *buffer++ = fRecs[i].fPos;
+        }
+    }
+    *buffer++ = fGradFlags;
+    *buffer++ = static_cast<int32_t>(bitmapType);
+    SkASSERT(buffer - storage.get() == count);
+
+    ///////////////////////////////////
+
+    static SkGradientBitmapCache* gCache;
+    // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp
+    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
+    SkAutoMutexAcquire ama(gGradientCacheMutex);
+
+    if (nullptr == gCache) {
+        gCache = new SkGradientBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
+    }
+    size_t size = count * sizeof(int32_t);
+
+    if (!gCache->find(storage.get(), size, bitmap)) {
+        if (GradientBitmapType::kLegacy == bitmapType) {
+            // force our cache32pixelref to be built
+            (void)cache->getCache32();
+            bitmap->setInfo(SkImageInfo::MakeN32Premul(kCache32Count, 1));
+            bitmap->setPixelRef(sk_ref_sp(cache->getCache32PixelRef()), 0, 0);
+        } else {
+            // For these cases we use the bitmap cache, but not the GradientShaderCache. So just
+            // allocate and populate the bitmap's data directly.
+
+            SkImageInfo info;
+            switch (bitmapType) {
+                case GradientBitmapType::kSRGB:
+                    info = SkImageInfo::Make(kCache32Count, 1, kRGBA_8888_SkColorType,
+                                             kPremul_SkAlphaType,
+                                             SkColorSpace::MakeSRGB());
+                    break;
+                case GradientBitmapType::kHalfFloat:
+                    info = SkImageInfo::Make(
+                        kCache32Count, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType,
+                        SkColorSpace::MakeSRGBLinear());
+                    break;
+                default:
+                    SkFAIL("Unexpected bitmap type");
+                    return;
+            }
+            bitmap->allocPixels(info);
+            this->initLinearBitmap(bitmap);
+        }
+        gCache->add(storage.get(), size, *bitmap);
+    }
+}
+
+void SkGradientShaderBase::commonAsAGradient(GradientInfo* info, bool flipGrad) const {
+    if (info) {
+        if (info->fColorCount >= fColorCount) {
+            SkColor* colorLoc;
+            Rec*     recLoc;
+            SkAutoSTArray<8, SkColor> colorStorage;
+            SkAutoSTArray<8, Rec> recStorage;
+            if (flipGrad && (info->fColors || info->fColorOffsets)) {
+                colorStorage.reset(fColorCount);
+                recStorage.reset(fColorCount);
+                colorLoc = colorStorage.get();
+                recLoc = recStorage.get();
+                FlipGradientColors(colorLoc, recLoc, fOrigColors, fRecs, fColorCount);
+            } else {
+                colorLoc = fOrigColors;
+                recLoc = fRecs;
+            }
+            if (info->fColors) {
+                memcpy(info->fColors, colorLoc, fColorCount * sizeof(SkColor));
+            }
+            if (info->fColorOffsets) {
+                if (fColorCount == 2) {
+                    info->fColorOffsets[0] = 0;
+                    info->fColorOffsets[1] = SK_Scalar1;
+                } else if (fColorCount > 2) {
+                    for (int i = 0; i < fColorCount; ++i) {
+                        info->fColorOffsets[i] = SkFixedToScalar(recLoc[i].fPos);
+                    }
+                }
+            }
+        }
+        info->fColorCount = fColorCount;
+        info->fTileMode = fTileMode;
+        info->fGradientFlags = fGradFlags;
+    }
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkGradientShaderBase::toString(SkString* str) const {
+
+    str->appendf("%d colors: ", fColorCount);
+
+    for (int i = 0; i < fColorCount; ++i) {
+        str->appendHex(fOrigColors[i], 8);
+        if (i < fColorCount-1) {
+            str->append(", ");
+        }
+    }
+
+    if (fColorCount > 2) {
+        str->append(" points: (");
+        for (int i = 0; i < fColorCount; ++i) {
+            str->appendScalar(SkFixedToScalar(fRecs[i].fPos));
+            if (i < fColorCount-1) {
+                str->append(", ");
+            }
+        }
+        str->append(")");
+    }
+
+    static const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
+
+    str->append(" ");
+    str->append(gTileModeName[fTileMode]);
+
+    this->INHERITED::toString(str);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// Return true if these parameters are valid/legal/safe to construct a gradient
+//
+static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
+                       unsigned tileMode) {
+    return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount;
+}
+
+static void desc_init(SkGradientShaderBase::Descriptor* desc,
+                      const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
+                      const SkScalar pos[], int colorCount,
+                      SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
+    SkASSERT(colorCount > 1);
+
+    desc->fColors       = colors;
+    desc->fColorSpace   = std::move(colorSpace);
+    desc->fPos          = pos;
+    desc->fCount        = colorCount;
+    desc->fTileMode     = mode;
+    desc->fGradFlags    = flags;
+    desc->fLocalMatrix  = localMatrix;
+}
+
+// assumes colors is SkColor4f* and pos is SkScalar*
+#define EXPAND_1_COLOR(count)                \
+     SkColor4f tmp[2];                       \
+     do {                                    \
+         if (1 == count) {                   \
+             tmp[0] = tmp[1] = colors[0];    \
+             colors = tmp;                   \
+             pos = nullptr;                  \
+             count = 2;                      \
+         }                                   \
+     } while (0)
+
+struct ColorStopOptimizer {
+    ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos,
+                       int count, SkShader::TileMode mode)
+        : fColors(colors)
+        , fPos(pos)
+        , fCount(count) {
+
+            if (!pos || count != 3) {
+                return;
+            }
+
+            if (SkScalarNearlyEqual(pos[0], 0.0f) &&
+                SkScalarNearlyEqual(pos[1], 0.0f) &&
+                SkScalarNearlyEqual(pos[2], 1.0f)) {
+
+                if (SkShader::kRepeat_TileMode == mode ||
+                    SkShader::kMirror_TileMode == mode ||
+                    colors[0] == colors[1]) {
+
+                    // Ignore the leftmost color/pos.
+                    fColors += 1;
+                    fPos    += 1;
+                    fCount   = 2;
+                }
+            } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
+                       SkScalarNearlyEqual(pos[1], 1.0f) &&
+                       SkScalarNearlyEqual(pos[2], 1.0f)) {
+
+                if (SkShader::kRepeat_TileMode == mode ||
+                    SkShader::kMirror_TileMode == mode ||
+                    colors[1] == colors[2]) {
+
+                    // Ignore the rightmost color/pos.
+                    fCount  = 2;
+                }
+            }
+    }
+
+    const SkColor4f* fColors;
+    const SkScalar*  fPos;
+    int              fCount;
+};
+
+struct ColorConverter {
+    ColorConverter(const SkColor* colors, int count) {
+        for (int i = 0; i < count; ++i) {
+            fColors4f.push_back(SkColor4f::FromColor(colors[i]));
+        }
+    }
+
+    SkSTArray<2, SkColor4f, true> fColors4f;
+};
+
+sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
+                                             const SkColor colors[],
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
+                      localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
+                                             const SkColor4f colors[],
+                                             sk_sp<SkColorSpace> colorSpace,
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
+    if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
+        return nullptr;
+    }
+    if (!valid_grad(colors, pos, colorCount, mode)) {
+        return nullptr;
+    }
+    if (1 == colorCount) {
+        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
+    }
+    if (localMatrix && !localMatrix->invert(nullptr)) {
+        return nullptr;
+    }
+
+    ColorStopOptimizer opt(colors, pos, colorCount, mode);
+
+    SkGradientShaderBase::Descriptor desc;
+    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+              localMatrix);
+    return sk_make_sp<SkLinearGradient>(pts, desc);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
+                                             const SkColor colors[],
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
+                      flags, localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
+                                             const SkColor4f colors[],
+                                             sk_sp<SkColorSpace> colorSpace,
+                                             const SkScalar pos[], int colorCount,
+                                             SkShader::TileMode mode,
+                                             uint32_t flags,
+                                             const SkMatrix* localMatrix) {
+    if (radius <= 0) {
+        return nullptr;
+    }
+    if (!valid_grad(colors, pos, colorCount, mode)) {
+        return nullptr;
+    }
+    if (1 == colorCount) {
+        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
+    }
+    if (localMatrix && !localMatrix->invert(nullptr)) {
+        return nullptr;
+    }
+
+    ColorStopOptimizer opt(colors, pos, colorCount, mode);
+
+    SkGradientShaderBase::Descriptor desc;
+    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+              localMatrix);
+    return sk_make_sp<SkRadialGradient>(center, radius, desc);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
+                                                      SkScalar startRadius,
+                                                      const SkPoint& end,
+                                                      SkScalar endRadius,
+                                                      const SkColor colors[],
+                                                      const SkScalar pos[],
+                                                      int colorCount,
+                                                      SkShader::TileMode mode,
+                                                      uint32_t flags,
+                                                      const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
+                               nullptr, pos, colorCount, mode, flags, localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
+                                                      SkScalar startRadius,
+                                                      const SkPoint& end,
+                                                      SkScalar endRadius,
+                                                      const SkColor4f colors[],
+                                                      sk_sp<SkColorSpace> colorSpace,
+                                                      const SkScalar pos[],
+                                                      int colorCount,
+                                                      SkShader::TileMode mode,
+                                                      uint32_t flags,
+                                                      const SkMatrix* localMatrix) {
+    if (startRadius < 0 || endRadius < 0) {
+        return nullptr;
+    }
+    if (!valid_grad(colors, pos, colorCount, mode)) {
+        return nullptr;
+    }
+    if (startRadius == endRadius) {
+        if (start == end || startRadius == 0) {
+            return SkShader::MakeEmptyShader();
+        }
+    }
+    if (localMatrix && !localMatrix->invert(nullptr)) {
+        return nullptr;
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    ColorStopOptimizer opt(colors, pos, colorCount, mode);
+
+    bool flipGradient = startRadius > endRadius;
+
+    SkGradientShaderBase::Descriptor desc;
+
+    if (!flipGradient) {
+        desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+                  localMatrix);
+        return sk_make_sp<SkTwoPointConicalGradient>(start, startRadius, end, endRadius,
+                                                     flipGradient, desc);
+    } else {
+        SkAutoSTArray<8, SkColor4f> colorsNew(opt.fCount);
+        SkAutoSTArray<8, SkScalar> posNew(opt.fCount);
+        for (int i = 0; i < opt.fCount; ++i) {
+            colorsNew[i] = opt.fColors[opt.fCount - i - 1];
+        }
+
+        if (pos) {
+            for (int i = 0; i < opt.fCount; ++i) {
+                posNew[i] = 1 - opt.fPos[opt.fCount - i - 1];
+            }
+            desc_init(&desc, colorsNew.get(), std::move(colorSpace), posNew.get(), opt.fCount, mode,
+                      flags, localMatrix);
+        } else {
+            desc_init(&desc, colorsNew.get(), std::move(colorSpace), nullptr, opt.fCount, mode,
+                      flags, localMatrix);
+        }
+
+        return sk_make_sp<SkTwoPointConicalGradient>(end, endRadius, start, startRadius,
+                                                     flipGradient, desc);
+    }
+}
+
+sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
+                                            const SkColor colors[],
+                                            const SkScalar pos[],
+                                            int colorCount,
+                                            uint32_t flags,
+                                            const SkMatrix* localMatrix) {
+    ColorConverter converter(colors, colorCount);
+    return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, flags,
+                     localMatrix);
+}
+
+sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
+                                            const SkColor4f colors[],
+                                            sk_sp<SkColorSpace> colorSpace,
+                                            const SkScalar pos[],
+                                            int colorCount,
+                                            uint32_t flags,
+                                            const SkMatrix* localMatrix) {
+    if (!valid_grad(colors, pos, colorCount, SkShader::kClamp_TileMode)) {
+        return nullptr;
+    }
+    if (1 == colorCount) {
+        return SkShader::MakeColorShader(colors[0], std::move(colorSpace));
+    }
+    if (localMatrix && !localMatrix->invert(nullptr)) {
+        return nullptr;
+    }
+
+    auto mode = SkShader::kClamp_TileMode;
+
+    ColorStopOptimizer opt(colors, pos, colorCount, mode);
+
+    SkGradientShaderBase::Descriptor desc;
+    desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
+              localMatrix);
+    return sk_make_sp<SkSweepGradient>(cx, cy, desc);
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrShaderCaps.h"
+#include "GrTextureStripAtlas.h"
+#include "gl/GrGLContext.h"
+#include "glsl/GrGLSLColorSpaceXformHelper.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
+#include "SkGr.h"
+
+static inline bool close_to_one_half(const SkFixed& val) {
+    return SkScalarNearlyEqual(SkFixedToScalar(val), SK_ScalarHalf);
+}
+
+static inline int color_type_to_color_count(GrGradientEffect::ColorType colorType) {
+    switch (colorType) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case GrGradientEffect::kSingleHardStop_ColorType:
+            return 4;
+        case GrGradientEffect::kHardStopLeftEdged_ColorType:
+        case GrGradientEffect::kHardStopRightEdged_ColorType:
+            return 3;
+#endif
+        case GrGradientEffect::kTwo_ColorType:
+            return 2;
+        case GrGradientEffect::kThree_ColorType:
+            return 3;
+        case GrGradientEffect::kTexture_ColorType:
+            return 0;
+    }
+
+    SkDEBUGFAIL("Unhandled ColorType in color_type_to_color_count()");
+    return -1;
+}
+
+GrGradientEffect::ColorType GrGradientEffect::determineColorType(
+        const SkGradientShaderBase& shader) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+    if (shader.fOrigPos) {
+        if (4 == shader.fColorCount) {
+            if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2]) &&
+                SkScalarNearlyEqual(shader.fOrigPos[3], 1.0f)) {
+
+                return kSingleHardStop_ColorType;
+            }
+        } else if (3 == shader.fColorCount) {
+            if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[1], 0.0f) &&
+                SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
+
+                return kHardStopLeftEdged_ColorType;
+            } else if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) &&
+                       SkScalarNearlyEqual(shader.fOrigPos[1], 1.0f) &&
+                       SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) {
+
+                return kHardStopRightEdged_ColorType;
+            }
+        }
+    }
+#endif
+
+    if (SkShader::kClamp_TileMode == shader.getTileMode()) {
+        if (2 == shader.fColorCount) {
+            return kTwo_ColorType;
+        } else if (3 == shader.fColorCount &&
+                   close_to_one_half(shader.getRecs()[1].fPos)) {
+            return kThree_ColorType;
+        }
+    }
+
+    return kTexture_ColorType;
+}
+
+void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler,
+                                                   const GrGradientEffect& ge) {
+    if (int colorCount = color_type_to_color_count(ge.getColorType())) {
+        fColorsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
+                                                     kVec4f_GrSLType,
+                                                     kDefault_GrSLPrecision,
+                                                     "Colors",
+                                                     colorCount);
+        if (ge.fColorType == kSingleHardStop_ColorType) {
+            fHardStopT = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
+                                                    kDefault_GrSLPrecision, "HardStopT");
+        }
+    } else {
+        fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                             kFloat_GrSLType, kDefault_GrSLPrecision,
+                                             "GradientYCoordFS");
+    }
+}
+
+static inline void set_after_interp_color_uni_array(
+                                                  const GrGLSLProgramDataManager& pdman,
+                                                  const GrGLSLProgramDataManager::UniformHandle uni,
+                                                  const SkTDArray<SkColor4f>& colors,
+                                                  const GrColorSpaceXform* colorSpaceXform) {
+    int count = colors.count();
+    if (colorSpaceXform) {
+        constexpr int kSmallCount = 10;
+        SkAutoSTArray<4 * kSmallCount, float> vals(4 * count);
+
+        for (int i = 0; i < count; i++) {
+            colorSpaceXform->srcToDst().mapScalars(colors[i].vec(), &vals[4 * i]);
+        }
+
+        pdman.set4fv(uni, count, vals.get());
+    } else {
+        pdman.set4fv(uni, count, (float*)&colors[0]);
+    }
+}
+
+static inline void set_before_interp_color_uni_array(
+                                                  const GrGLSLProgramDataManager& pdman,
+                                                  const GrGLSLProgramDataManager::UniformHandle uni,
+                                                  const SkTDArray<SkColor4f>& colors,
+                                                  const GrColorSpaceXform* colorSpaceXform) {
+    int count = colors.count();
+    constexpr int kSmallCount = 10;
+    SkAutoSTArray<4 * kSmallCount, float> vals(4 * count);
+
+    for (int i = 0; i < count; i++) {
+        float a = colors[i].fA;
+        vals[4 * i + 0] = colors[i].fR * a;
+        vals[4 * i + 1] = colors[i].fG * a;
+        vals[4 * i + 2] = colors[i].fB * a;
+        vals[4 * i + 3] = a;
+    }
+
+    if (colorSpaceXform) {
+        for (int i = 0; i < count; i++) {
+            colorSpaceXform->srcToDst().mapScalars(&vals[4 * i]);
+        }
+    }
+
+    pdman.set4fv(uni, count, vals.get());
+}
+
+static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
+                                       const GrGLSLProgramDataManager::UniformHandle uni,
+                                       const SkTDArray<SkColor>& colors) {
+    int count = colors.count();
+    constexpr int kSmallCount = 10;
+
+    SkAutoSTArray<4*kSmallCount, float> vals(4*count);
+
+    for (int i = 0; i < colors.count(); i++) {
+        // RGBA
+        vals[4*i + 0] = SkColorGetR(colors[i]) / 255.f;
+        vals[4*i + 1] = SkColorGetG(colors[i]) / 255.f;
+        vals[4*i + 2] = SkColorGetB(colors[i]) / 255.f;
+        vals[4*i + 3] = SkColorGetA(colors[i]) / 255.f;
+    }
+
+    pdman.set4fv(uni, colors.count(), vals.get());
+}
+
+static inline void set_before_interp_color_uni_array(const GrGLSLProgramDataManager& pdman,
+                                              const GrGLSLProgramDataManager::UniformHandle uni,
+                                              const SkTDArray<SkColor>& colors) {
+    int count = colors.count();
+    constexpr int kSmallCount = 10;
+
+    SkAutoSTArray<4*kSmallCount, float> vals(4*count);
+
+    for (int i = 0; i < count; i++) {
+        float a = SkColorGetA(colors[i]) / 255.f;
+        float aDiv255 = a / 255.f;
+
+        // RGBA
+        vals[4*i + 0] = SkColorGetR(colors[i]) * aDiv255;
+        vals[4*i + 1] = SkColorGetG(colors[i]) * aDiv255;
+        vals[4*i + 2] = SkColorGetB(colors[i]) * aDiv255;
+        vals[4*i + 3] = a;
+    }
+
+    pdman.set4fv(uni, count, vals.get());
+}
+
+void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman,
+                                                const GrFragmentProcessor& processor) {
+    const GrGradientEffect& e = processor.cast<GrGradientEffect>();
+
+    switch (e.getColorType()) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case GrGradientEffect::kSingleHardStop_ColorType:
+            pdman.set1f(fHardStopT, e.fPositions[1]);
+            // fall through
+        case GrGradientEffect::kHardStopLeftEdged_ColorType:
+        case GrGradientEffect::kHardStopRightEdged_ColorType:
+#endif
+        case GrGradientEffect::kTwo_ColorType:
+        case GrGradientEffect::kThree_ColorType: {
+            if (e.fColors4f.count() > 0) {
+                // Gamma-correct / color-space aware
+                if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
+                    set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors4f,
+                                                      e.fColorSpaceXform.get());
+                } else {
+                    set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors4f,
+                                                     e.fColorSpaceXform.get());
+                }
+            } else {
+                // Legacy mode. Would be nice if we had converted the 8-bit colors to float earlier
+                if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
+                    set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+                } else {
+                    set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors);
+                }
+            }
+
+            break;
+        }
+
+        case GrGradientEffect::kTexture_ColorType: {
+            SkScalar yCoord = e.getYCoord();
+            if (yCoord != fCachedYCoord) {
+                pdman.set1f(fFSYUni, yCoord);
+                fCachedYCoord = yCoord;
+            }
+            if (SkToBool(e.fColorSpaceXform)) {
+                fColorSpaceHelper.setData(pdman, e.fColorSpaceXform.get());
+            }
+            break;
+        }
+    }
+}
+
+uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) {
+    const GrGradientEffect& e = processor.cast<GrGradientEffect>();
+
+    uint32_t key = 0;
+
+    if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
+        key |= kPremulBeforeInterpKey;
+    }
+
+    if (GrGradientEffect::kTwo_ColorType == e.getColorType()) {
+        key |= kTwoColorKey;
+    } else if (GrGradientEffect::kThree_ColorType == e.getColorType()) {
+        key |= kThreeColorKey;
+    }
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+    else if (GrGradientEffect::kSingleHardStop_ColorType == e.getColorType()) {
+        key |= kHardStopCenteredKey;
+    } else if (GrGradientEffect::kHardStopLeftEdged_ColorType == e.getColorType()) {
+        key |= kHardStopZeroZeroOneKey;
+    } else if (GrGradientEffect::kHardStopRightEdged_ColorType == e.getColorType()) {
+        key |= kHardStopZeroOneOneKey;
+    }
+
+    if (SkShader::TileMode::kClamp_TileMode == e.fTileMode) {
+        key |= kClampTileMode;
+    } else if (SkShader::TileMode::kRepeat_TileMode == e.fTileMode) {
+        key |= kRepeatTileMode;
+    } else {
+        key |= kMirrorTileMode;
+    }
+#endif
+
+    key |= GrColorSpaceXform::XformKey(e.fColorSpaceXform.get()) << kReservedBits;
+
+    return key;
+}
+
+void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
+                                                GrGLSLUniformHandler* uniformHandler,
+                                                const GrShaderCaps* shaderCaps,
+                                                const GrGradientEffect& ge,
+                                                const char* gradientTValue,
+                                                const char* outputColor,
+                                                const char* inputColor,
+                                                const TextureSamplers& texSamplers) {
+    switch (ge.getColorType()) {
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case kSingleHardStop_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+            const char* stopT = uniformHandler->getUniformCStr(fHardStopT);
+
+            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
+
+            // Account for tile mode
+            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
+            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
+                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
+                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
+                fragBuilder->codeAppendf("    } else {");
+                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
+                fragBuilder->codeAppendf("    }");
+                fragBuilder->codeAppendf("}");
+            }
+
+            // Calculate color
+            fragBuilder->codeAppend ("vec4 start, end;");
+            fragBuilder->codeAppend ("float relative_t;");
+            fragBuilder->codeAppendf("if (clamp_t < %s) {", stopT);
+            fragBuilder->codeAppendf("    start = %s[0];", colors);
+            fragBuilder->codeAppendf("    end   = %s[1];", colors);
+            fragBuilder->codeAppendf("    relative_t = clamp_t / %s;", stopT);
+            fragBuilder->codeAppend ("} else {");
+            fragBuilder->codeAppendf("    start = %s[2];", colors);
+            fragBuilder->codeAppendf("    end   = %s[3];", colors);
+            fragBuilder->codeAppendf("    relative_t = (clamp_t - %s) / (1 - %s);", stopT, stopT);
+            fragBuilder->codeAppend ("}");
+            fragBuilder->codeAppend ("vec4 colorTemp = mix(start, end, relative_t);");
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            if (ge.fColorSpaceXform) {
+                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
+            }
+            fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
+
+            break;
+        }
+
+        case kHardStopLeftEdged_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
+
+            // Account for tile mode
+            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
+            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
+                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
+                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
+                fragBuilder->codeAppendf("    } else {");
+                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
+                fragBuilder->codeAppendf("    }");
+                fragBuilder->codeAppendf("}");
+            }
+
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[1], %s[2], clamp_t);", colors,
+                                     colors);
+            if (SkShader::kClamp_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0) {", t);
+                fragBuilder->codeAppendf("    colorTemp = %s[0];", colors);
+                fragBuilder->codeAppendf("}");
+            }
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            if (ge.fColorSpaceXform) {
+                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
+            }
+            fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
+
+            break;
+        }
+
+        case kHardStopRightEdged_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t);
+
+            // Account for tile mode
+            if (SkShader::kRepeat_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("clamp_t = fract(%s);", t);
+            } else if (SkShader::kMirror_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t);
+                fragBuilder->codeAppendf("    if (mod(floor(%s), 2.0) == 0.0) {", t);
+                fragBuilder->codeAppendf("        clamp_t = fract(%s);", t);
+                fragBuilder->codeAppendf("    } else {");
+                fragBuilder->codeAppendf("        clamp_t = 1.0 - fract(%s);", t);
+                fragBuilder->codeAppendf("    }");
+                fragBuilder->codeAppendf("}");
+            }
+
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp_t);", colors,
+                                     colors);
+            if (SkShader::kClamp_TileMode == ge.fTileMode) {
+                fragBuilder->codeAppendf("if (%s > 1.0) {", t);
+                fragBuilder->codeAppendf("    colorTemp = %s[2];", colors);
+                fragBuilder->codeAppendf("}");
+            }
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            if (ge.fColorSpaceXform) {
+                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
+            }
+            fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
+
+            break;
+        }
+#endif
+
+        case kTwo_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp(%s, 0.0, 1.0));",
+                                     colors, colors, t);
+
+            // We could skip this step if both colors are known to be opaque. Two
+            // considerations:
+            // The gradient SkShader reporting opaque is more restrictive than necessary in the two
+            // pt case. Make sure the key reflects this optimization (and note that it can use the
+            // same shader as thekBeforeIterp case). This same optimization applies to the 3 color
+            // case below.
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            if (ge.fColorSpaceXform) {
+                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
+            }
+
+            fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
+
+            break;
+        }
+
+        case kThree_ColorType: {
+            const char* t      = gradientTValue;
+            const char* colors = uniformHandler->getUniformCStr(fColorsUni);
+
+            fragBuilder->codeAppendf("float oneMinus2t = 1.0 - (2.0 * %s);", t);
+            fragBuilder->codeAppendf("vec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s[0];",
+                                     colors);
+            if (!shaderCaps->canUseMinAndAbsTogether()) {
+                // The Tegra3 compiler will sometimes never return if we have
+                // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression.
+                fragBuilder->codeAppendf("float minAbs = abs(oneMinus2t);");
+                fragBuilder->codeAppendf("minAbs = minAbs > 1.0 ? 1.0 : minAbs;");
+                fragBuilder->codeAppendf("colorTemp += (1.0 - minAbs) * %s[1];", colors);
+            } else {
+                fragBuilder->codeAppendf("colorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s[1];",
+                                         colors);
+            }
+            fragBuilder->codeAppendf("colorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s[2];", colors);
+
+            if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) {
+                fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;");
+            }
+            if (ge.fColorSpaceXform) {
+                fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);");
+            }
+
+            fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor);
+
+            break;
+        }
+
+        case kTexture_ColorType: {
+            fColorSpaceHelper.emitCode(uniformHandler, ge.fColorSpaceXform.get());
+
+            const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni);
+
+            fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni);
+            fragBuilder->codeAppendf("%s = ", outputColor);
+            fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord",
+                                                        kVec2f_GrSLType, &fColorSpaceHelper);
+            fragBuilder->codeAppend(";");
+
+            break;
+        }
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+inline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool isOpaque) {
+    return isOpaque
+                   ? kPreservesOpaqueInput_OptimizationFlag |
+                             kCompatibleWithCoverageAsAlpha_OptimizationFlag
+                   : kCompatibleWithCoverageAsAlpha_OptimizationFlag;
+}
+
+GrGradientEffect::GrGradientEffect(const CreateArgs& args, bool isOpaque)
+        : INHERITED(OptFlags(isOpaque)) {
+    const SkGradientShaderBase& shader(*args.fShader);
+
+    fIsOpaque = shader.isOpaque();
+
+    fColorType = this->determineColorType(shader);
+    fColorSpaceXform = std::move(args.fColorSpaceXform);
+
+    if (kTexture_ColorType != fColorType) {
+        SkASSERT(shader.fOrigColors && shader.fOrigColors4f);
+        if (args.fGammaCorrect) {
+            fColors4f = SkTDArray<SkColor4f>(shader.fOrigColors4f, shader.fColorCount);
+        } else {
+            fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount);
+        }
+
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        if (shader.fOrigPos) {
+            fPositions = SkTDArray<SkScalar>(shader.fOrigPos, shader.fColorCount);
+        }
+#endif
+    }
+
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+    fTileMode = args.fTileMode;
+#endif
+
+    switch (fColorType) {
+        // The two and three color specializations do not currently support tiling.
+        case kTwo_ColorType:
+        case kThree_ColorType:
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        case kHardStopLeftEdged_ColorType:
+        case kHardStopRightEdged_ColorType:
+        case kSingleHardStop_ColorType:
+#endif
+            fRow = -1;
+
+            if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) {
+                fPremulType = kBeforeInterp_PremulType;
+            } else {
+                fPremulType = kAfterInterp_PremulType;
+            }
+
+            fCoordTransform.reset(*args.fMatrix);
+
+            break;
+        case kTexture_ColorType:
+            // doesn't matter how this is set, just be consistent because it is part of the
+            // effect key.
+            fPremulType = kBeforeInterp_PremulType;
+
+            SkGradientShaderBase::GradientBitmapType bitmapType =
+                SkGradientShaderBase::GradientBitmapType::kLegacy;
+            if (args.fGammaCorrect) {
+                // Try to use F16 if we can
+                if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+                    bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat;
+                } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) {
+                    bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB;
+                } else {
+                    // This can happen, but only if someone explicitly creates an unsupported
+                    // (eg sRGB) surface. Just fall back to legacy behavior.
+                }
+            }
+
+            SkBitmap bitmap;
+            shader.getGradientTableBitmap(&bitmap, bitmapType);
+            SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
+
+
+            GrTextureStripAtlas::Desc desc;
+            desc.fWidth  = bitmap.width();
+            desc.fHeight = 32;
+            desc.fRowHeight = bitmap.height();
+            desc.fContext = args.fContext;
+            desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps());
+            fAtlas = GrTextureStripAtlas::GetAtlas(desc);
+            SkASSERT(fAtlas);
+
+            // We always filter the gradient table. Each table is one row of a texture, always
+            // y-clamp.
+            GrSamplerParams params;
+            params.setFilterMode(GrSamplerParams::kBilerp_FilterMode);
+            params.setTileModeX(args.fTileMode);
+
+            fRow = fAtlas->lockRow(bitmap);
+            if (-1 != fRow) {
+                fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
+                // This is 1/2 places where auto-normalization is disabled
+                fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix,
+                                      fAtlas->asTextureProxyRef().get(), false);
+                fTextureSampler.reset(args.fContext->resourceProvider(),
+                                      fAtlas->asTextureProxyRef(), params);
+            } else {
+                // In this instance we know the params are:
+                //   clampY, bilerp
+                // and the proxy is:
+                //   exact fit, power of two in both dimensions
+                // Only the x-tileMode is unknown. However, given all the other knowns we know
+                // that GrMakeCachedBitmapProxy is sufficient (i.e., it won't need to be
+                // extracted to a subset or mipmapped).
+                sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(
+                                                                args.fContext->resourceProvider(),
+                                                                bitmap);
+                if (!proxy) {
+                    return;
+                }
+                // This is 2/2 places where auto-normalization is disabled
+                fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix,
+                                      proxy.get(), false);
+                fTextureSampler.reset(args.fContext->resourceProvider(),
+                                      std::move(proxy), params);
+                fYCoord = SK_ScalarHalf;
+            }
+
+            this->addTextureSampler(&fTextureSampler);
+
+            break;
+    }
+
+    this->addCoordTransform(&fCoordTransform);
+}
+
+GrGradientEffect::~GrGradientEffect() {
+    if (this->useAtlas()) {
+        fAtlas->unlockRow(fRow);
+    }
+}
+
+bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const {
+    const GrGradientEffect& ge = processor.cast<GrGradientEffect>();
+
+    if (this->fColorType != ge.getColorType()) {
+        return false;
+    }
+    SkASSERT(this->useAtlas() == ge.useAtlas());
+    if (kTexture_ColorType == fColorType) {
+        if (fYCoord != ge.getYCoord()) {
+            return false;
+        }
+    } else {
+        if (kSingleHardStop_ColorType == fColorType) {
+            if (!SkScalarNearlyEqual(ge.fPositions[1], fPositions[1])) {
+                return false;
+            }
+        }
+        if (this->getPremulType() != ge.getPremulType() ||
+            this->fColors.count() != ge.fColors.count() ||
+            this->fColors4f.count() != ge.fColors4f.count()) {
+            return false;
+        }
+
+        for (int i = 0; i < this->fColors.count(); i++) {
+            if (*this->getColors(i) != *ge.getColors(i)) {
+                return false;
+            }
+        }
+        for (int i = 0; i < this->fColors4f.count(); i++) {
+            if (*this->getColors4f(i) != *ge.getColors4f(i)) {
+                return false;
+            }
+        }
+    }
+    return GrColorSpaceXform::Equals(this->fColorSpaceXform.get(), ge.fColorSpaceXform.get());
+}
+
+#if GR_TEST_UTILS
+GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) {
+    // Set color count to min of 2 so that we don't trigger the const color optimization and make
+    // a non-gradient processor.
+    fColorCount = random->nextRangeU(2, kMaxRandomGradientColors);
+    fUseColors4f = random->nextBool();
+
+    // if one color, omit stops, otherwise randomly decide whether or not to
+    if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) {
+        fStops = nullptr;
+    } else {
+        fStops = fStopStorage;
+    }
+
+    // if using SkColor4f, attach a random (possibly null) color space (with linear gamma)
+    if (fUseColors4f) {
+        fColorSpace = GrTest::TestColorSpace(random);
+        if (fColorSpace) {
+            SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(fColorSpace)->type());
+            fColorSpace = static_cast<SkColorSpace_XYZ*>(fColorSpace.get())->makeLinearGamma();
+        }
+    }
+
+    SkScalar stop = 0.f;
+    for (int i = 0; i < fColorCount; ++i) {
+        if (fUseColors4f) {
+            fColors4f[i].fR = random->nextUScalar1();
+            fColors4f[i].fG = random->nextUScalar1();
+            fColors4f[i].fB = random->nextUScalar1();
+            fColors4f[i].fA = random->nextUScalar1();
+        } else {
+            fColors[i] = random->nextU();
+        }
+        if (fStops) {
+            fStops[i] = stop;
+            stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f;
+        }
+    }
+    fTileMode = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount));
+}
+#endif
+
+#endif
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
new file mode 100644
index 0000000..c7c7761
--- /dev/null
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkGradientShaderPriv_DEFINED
+#define SkGradientShaderPriv_DEFINED
+
+#include "SkGradientBitmapCache.h"
+#include "SkGradientShader.h"
+
+#include "SkArenaAlloc.h"
+#include "SkAutoMalloc.h"
+#include "SkClampRange.h"
+#include "SkColorPriv.h"
+#include "SkColorSpace.h"
+#include "SkOnce.h"
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
+#include "SkReadBuffer.h"
+#include "SkShaderBase.h"
+#include "SkUtils.h"
+#include "SkWriteBuffer.h"
+
+#if SK_SUPPORT_GPU
+    #define GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS 1
+#endif
+
+static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
+                               int count) {
+    if (count > 0) {
+        if (v0 == v1) {
+            sk_memset32(dst, v0, count);
+        } else {
+            int pairs = count >> 1;
+            for (int i = 0; i < pairs; i++) {
+                *dst++ = v0;
+                *dst++ = v1;
+            }
+            if (count & 1) {
+                *dst = v0;
+            }
+        }
+    }
+}
+
+//  Clamp
+
+static inline SkFixed clamp_tileproc(SkFixed x) {
+    return SkClampMax(x, 0xFFFF);
+}
+
+// Repeat
+
+static inline SkFixed repeat_tileproc(SkFixed x) {
+    return x & 0xFFFF;
+}
+
+// Mirror
+
+static inline SkFixed mirror_tileproc(SkFixed x) {
+    int s = SkLeftShift(x, 15) >> 31;
+    return (x ^ s) & 0xFFFF;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef SkFixed (*TileProc)(SkFixed);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const TileProc gTileProcs[] = {
+    clamp_tileproc,
+    repeat_tileproc,
+    mirror_tileproc
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGradientShaderBase : public SkShaderBase {
+public:
+    struct Descriptor {
+        Descriptor() {
+            sk_bzero(this, sizeof(*this));
+            fTileMode = SkShader::kClamp_TileMode;
+        }
+
+        const SkMatrix*     fLocalMatrix;
+        const SkColor4f*    fColors;
+        sk_sp<SkColorSpace> fColorSpace;
+        const SkScalar*     fPos;
+        int                 fCount;
+        SkShader::TileMode  fTileMode;
+        uint32_t            fGradFlags;
+
+        void flatten(SkWriteBuffer&) const;
+    };
+
+    class DescriptorScope : public Descriptor {
+    public:
+        DescriptorScope() {}
+
+        bool unflatten(SkReadBuffer&);
+
+        // fColors and fPos always point into local memory, so they can be safely mutated
+        //
+        SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); }
+        SkScalar* mutablePos() { return const_cast<SkScalar*>(fPos); }
+
+    private:
+        enum {
+            kStorageCount = 16
+        };
+        SkColor4f fColorStorage[kStorageCount];
+        SkScalar fPosStorage[kStorageCount];
+        SkMatrix fLocalMatrixStorage;
+        SkAutoMalloc fDynamicStorage;
+    };
+
+    SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit);
+    ~SkGradientShaderBase() override;
+
+    // The cache is initialized on-demand when getCache32 is called.
+    class GradientShaderCache : public SkRefCnt {
+    public:
+        GradientShaderCache(U8CPU alpha, bool dither, const SkGradientShaderBase& shader);
+        ~GradientShaderCache();
+
+        const SkPMColor*    getCache32();
+
+        SkPixelRef* getCache32PixelRef() const { return fCache32PixelRef.get(); }
+
+        unsigned getAlpha() const { return fCacheAlpha; }
+        bool getDither() const { return fCacheDither; }
+
+    private:
+        // Working pointer. If it's nullptr, we need to recompute the cache values.
+        SkPMColor*  fCache32;
+
+        sk_sp<SkPixelRef> fCache32PixelRef;
+        const unsigned    fCacheAlpha;        // The alpha value we used when we computed the cache.
+                                              // Larger than 8bits so we can store uninitialized
+                                              // value.
+        const bool        fCacheDither;       // The dither flag used when we computed the cache.
+
+        const SkGradientShaderBase& fShader;
+
+        // Make sure we only initialize the cache once.
+        SkOnce fCache32InitOnce;
+
+        static void initCache32(GradientShaderCache* cache);
+
+        static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
+                                    U8CPU alpha, uint32_t gradFlags, bool dither);
+    };
+
+    class GradientShaderBaseContext : public Context {
+    public:
+        GradientShaderBaseContext(const SkGradientShaderBase& shader, const ContextRec&);
+
+        uint32_t getFlags() const override { return fFlags; }
+
+        bool isValid() const;
+
+    protected:
+        SkMatrix    fDstToIndex;
+        SkMatrix::MapXYProc fDstToIndexProc;
+        uint8_t     fDstToIndexClass;
+        uint8_t     fFlags;
+        bool        fDither;
+
+        sk_sp<GradientShaderCache> fCache;
+
+    private:
+        typedef Context INHERITED;
+    };
+
+    bool isOpaque() const override;
+
+    enum class GradientBitmapType : uint8_t {
+        kLegacy,
+        kSRGB,
+        kHalfFloat,
+    };
+
+    void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const;
+
+    enum {
+        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
+        /// it, use a larger cache.
+        kCache32Bits    = 8,
+        kCache32Count   = (1 << kCache32Bits),
+        kCache32Shift   = 16 - kCache32Bits,
+        kSqrt32Shift    = 8 - kCache32Bits,
+
+        /// This value is used to *read* the dither cache; it may be 0
+        /// if dithering is disabled.
+        kDitherStride32 = kCache32Count,
+    };
+
+    uint32_t getGradFlags() const { return fGradFlags; }
+
+    SkColor4f getXformedColor(size_t index, SkColorSpace*) const;
+
+protected:
+    struct Rec {
+        SkFixed     fPos;   // 0...1
+        uint32_t    fScale; // (1 << 24) / range
+    };
+
+    class GradientShaderBase4fContext;
+
+    SkGradientShaderBase(SkReadBuffer& );
+    void flatten(SkWriteBuffer&) const override;
+    SK_TO_STRING_OVERRIDE()
+
+    void commonAsAGradient(GradientInfo*, bool flipGrad = false) const;
+
+    bool onAsLuminanceColor(SkColor*) const override;
+
+    void initLinearBitmap(SkBitmap* bitmap) const;
+
+    /*
+     * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively.
+     * Count is the number of colors in the gradient
+     * It will then flip all the color and rec information and return in their respective Dst
+     * pointers. It is assumed that space has already been allocated for the Dst pointers.
+     * The rec src and dst are only assumed to be valid if count > 2
+     */
+    static void FlipGradientColors(SkColor* colorDst, Rec* recDst,
+                                   SkColor* colorSrc, Rec* recSrc,
+                                   int count);
+
+    bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
+                        const SkMatrix& ctm, const SkPaint& paint,
+                        const SkMatrix* localM) const override;
+
+    virtual bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                             SkMatrix* matrix,
+                                             SkRasterPipeline* p) const { return false; }
+
+    template <typename T, typename... Args>
+    static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) {
+        auto* ctx = alloc->make<T>(std::forward<Args>(args)...);
+        if (!ctx->isValid()) {
+            return nullptr;
+        }
+        return ctx;
+    }
+
+    const SkMatrix fPtsToUnit;
+    TileMode       fTileMode;
+    TileProc       fTileProc;
+    uint8_t        fGradFlags;
+    Rec*           fRecs;
+
+private:
+    enum {
+        kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
+
+        kStorageSize = kColorStorageCount *
+                       (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec) + sizeof(SkColor4f))
+    };
+    SkColor             fStorage[(kStorageSize + 3) >> 2];
+public:
+    SkColor*            fOrigColors;   // original colors, before modulation by paint in context.
+    SkColor4f*          fOrigColors4f; // original colors, as linear floats
+    SkScalar*           fOrigPos;      // original positions
+    int                 fColorCount;
+    sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops
+
+    bool colorsAreOpaque() const { return fColorsAreOpaque; }
+
+    TileMode getTileMode() const { return fTileMode; }
+    Rec* getRecs() const { return fRecs; }
+
+private:
+    bool                fColorsAreOpaque;
+
+    sk_sp<GradientShaderCache> refCache(U8CPU alpha, bool dither) const;
+    mutable SkMutex                    fCacheMutex;
+    mutable sk_sp<GradientShaderCache> fCache;
+
+    void initCommon();
+
+    typedef SkShaderBase INHERITED;
+};
+
+
+static inline int init_dither_toggle(int x, int y) {
+    x &= 1;
+    y = (y & 1) << 1;
+    return (x | y) * SkGradientShaderBase::kDitherStride32;
+}
+
+static inline int next_dither_toggle(int toggle) {
+    return toggle ^ SkGradientShaderBase::kDitherStride32;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrColorSpaceXform.h"
+#include "GrCoordTransform.h"
+#include "GrFragmentProcessor.h"
+#include "glsl/GrGLSLColorSpaceXformHelper.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+
+class GrInvariantOutput;
+
+/*
+ * The interpretation of the texture matrix depends on the sample mode. The
+ * texture matrix is applied both when the texture coordinates are explicit
+ * and  when vertex positions are used as texture  coordinates. In the latter
+ * case the texture matrix is applied to the pre-view-matrix position
+ * values.
+ *
+ * Normal SampleMode
+ *  The post-matrix texture coordinates are in normalize space with (0,0) at
+ *  the top-left and (1,1) at the bottom right.
+ * RadialGradient
+ *  The matrix specifies the radial gradient parameters.
+ *  (0,0) in the post-matrix space is center of the radial gradient.
+ * Radial2Gradient
+ *   Matrix transforms to space where first circle is centered at the
+ *   origin. The second circle will be centered (x, 0) where x may be
+ *   0 and is provided by setRadial2Params. The post-matrix space is
+ *   normalized such that 1 is the second radius - first radius.
+ * SweepGradient
+ *  The angle from the origin of texture coordinates in post-matrix space
+ *  determines the gradient value.
+ */
+
+ class GrTextureStripAtlas;
+
+// Base class for Gr gradient effects
+class GrGradientEffect : public GrFragmentProcessor {
+public:
+    struct CreateArgs {
+        CreateArgs(GrContext* context,
+                   const SkGradientShaderBase* shader,
+                   const SkMatrix* matrix,
+                   SkShader::TileMode tileMode,
+                   sk_sp<GrColorSpaceXform> colorSpaceXform,
+                   bool gammaCorrect)
+            : fContext(context)
+            , fShader(shader)
+            , fMatrix(matrix)
+            , fTileMode(tileMode)
+            , fColorSpaceXform(std::move(colorSpaceXform))
+            , fGammaCorrect(gammaCorrect) {}
+
+        GrContext*                  fContext;
+        const SkGradientShaderBase* fShader;
+        const SkMatrix*             fMatrix;
+        SkShader::TileMode          fTileMode;
+        sk_sp<GrColorSpaceXform>    fColorSpaceXform;
+        bool                        fGammaCorrect;
+    };
+
+    class GLSLProcessor;
+
+    ~GrGradientEffect() override;
+
+    bool useAtlas() const { return SkToBool(-1 != fRow); }
+    SkScalar getYCoord() const { return fYCoord; }
+
+    enum ColorType {
+        kTwo_ColorType,
+        kThree_ColorType, // Symmetric three color
+        kTexture_ColorType,
+
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        kSingleHardStop_ColorType,     // 0, t, t, 1
+        kHardStopLeftEdged_ColorType,  // 0, 0, 1
+        kHardStopRightEdged_ColorType, // 0, 1, 1
+#endif
+    };
+
+    ColorType getColorType() const { return fColorType; }
+
+    // Determines the type of gradient, one of:
+    //    - Two-color
+    //    - Symmetric three-color
+    //    - Texture
+    //    - Centered hard stop
+    //    - Left-edged hard stop
+    //    - Right-edged hard stop
+    ColorType determineColorType(const SkGradientShaderBase& shader);
+
+    enum PremulType {
+        kBeforeInterp_PremulType,
+        kAfterInterp_PremulType,
+    };
+
+    PremulType getPremulType() const { return fPremulType; }
+
+    const SkColor* getColors(int pos) const {
+        SkASSERT(fColorType != kTexture_ColorType);
+        SkASSERT(pos < fColors.count());
+        return &fColors[pos];
+    }
+
+    const SkColor4f* getColors4f(int pos) const {
+        SkASSERT(fColorType != kTexture_ColorType);
+        SkASSERT(pos < fColors4f.count());
+        return &fColors4f[pos];
+    }
+
+protected:
+    GrGradientEffect(const CreateArgs&, bool isOpaque);
+
+    #if GR_TEST_UTILS
+    /** Helper struct that stores (and populates) parameters to construct a random gradient.
+        If fUseColors4f is true, then the SkColor4f factory should be called, with fColors4f and
+        fColorSpace. Otherwise, the SkColor factory should be called, with fColors. fColorCount
+        will be the number of color stops in either case, and fColors and fStops can be passed to
+        the gradient factory. (The constructor may decide not to use stops, in which case fStops
+        will be nullptr). */
+    struct RandomGradientParams {
+        static const int kMaxRandomGradientColors = 5;
+
+        RandomGradientParams(SkRandom* r);
+
+        bool fUseColors4f;
+        SkColor fColors[kMaxRandomGradientColors];
+        SkColor4f fColors4f[kMaxRandomGradientColors];
+        sk_sp<SkColorSpace> fColorSpace;
+        SkScalar fStopStorage[kMaxRandomGradientColors];
+        SkShader::TileMode fTileMode;
+        int fColorCount;
+        SkScalar* fStops;
+    };
+    #endif
+
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+
+    const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
+
+private:
+    static OptimizationFlags OptFlags(bool isOpaque);
+
+    // If we're in legacy mode, then fColors will be populated. If we're gamma-correct, then
+    // fColors4f and fColorSpaceXform will be populated.
+    SkTDArray<SkColor>       fColors;
+
+    SkTDArray<SkColor4f>     fColors4f;
+    sk_sp<GrColorSpaceXform> fColorSpaceXform;
+
+    SkTDArray<SkScalar>      fPositions;
+    SkShader::TileMode       fTileMode;
+
+    GrCoordTransform fCoordTransform;
+    TextureSampler fTextureSampler;
+    SkScalar fYCoord;
+    GrTextureStripAtlas* fAtlas;
+    int fRow;
+    bool fIsOpaque;
+    ColorType fColorType;
+    PremulType fPremulType; // This is already baked into the table for texture gradients, and
+                            // only changes behavior for gradients that don't use a texture.
+    typedef GrFragmentProcessor INHERITED;
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Base class for GL gradient effects
+class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor {
+public:
+    GLSLProcessor() {
+        fCachedYCoord = SK_ScalarMax;
+    }
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+protected:
+    /**
+     * Subclasses must call this. It will return a key for the part of the shader code controlled
+     * by the base class. The subclasses must stick it in their key and then pass it to the below
+     * emit* functions from their emitCode function.
+     */
+    static uint32_t GenBaseGradientKey(const GrProcessor&);
+
+    // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
+    // should call this method from their emitCode().
+    void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&);
+
+    // Emit code that gets a fragment's color from an expression for t; has branches for
+    // several control flows inside -- 2-color gradients, 3-color symmetric gradients, 4+
+    // color gradients that use the traditional texture lookup, as well as several varieties
+    // of hard stop gradients
+    void emitColor(GrGLSLFPFragmentBuilder* fragBuilder,
+                   GrGLSLUniformHandler* uniformHandler,
+                   const GrShaderCaps* shaderCaps,
+                   const GrGradientEffect&,
+                   const char* gradientTValue,
+                   const char* outputColor,
+                   const char* inputColor,
+                   const TextureSamplers&);
+
+private:
+    enum {
+        // First bit for premul before/after interp
+        kPremulBeforeInterpKey  =  1,
+
+        // Next three bits for 2/3 color type or different special
+        // hard stop cases (neither means using texture atlas)
+        kTwoColorKey            =  2,
+        kThreeColorKey          =  4,
+#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS
+        kHardStopCenteredKey    =  6,
+        kHardStopZeroZeroOneKey =  8,
+        kHardStopZeroOneOneKey  = 10,
+
+        // Next two bits for tile mode
+        kClampTileMode          = 16,
+        kRepeatTileMode         = 32,
+        kMirrorTileMode         = 48,
+
+        // Lower six bits for premul, 2/3 color type, and tile mode
+        kReservedBits           = 6,
+#endif
+    };
+
+    SkScalar fCachedYCoord;
+    GrGLSLProgramDataManager::UniformHandle fColorsUni;
+    GrGLSLProgramDataManager::UniformHandle fHardStopT;
+    GrGLSLProgramDataManager::UniformHandle fFSYUni;
+    GrGLSLColorSpaceXformHelper             fColorSpaceHelper;
+
+    typedef GrGLSLFragmentProcessor INHERITED;
+};
+
+#endif
+
+#endif
diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp
new file mode 100644
index 0000000..52f380b
--- /dev/null
+++ b/src/shaders/gradients/SkLinearGradient.cpp
@@ -0,0 +1,827 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Sk4fLinearGradient.h"
+#include "SkColorSpaceXformer.h"
+#include "SkLinearGradient.h"
+#include "SkRefCnt.h"
+
+// define to test the 4f gradient path
+// #define FORCE_4F_CONTEXT
+
+static const float kInv255Float = 1.0f / 255;
+
+static inline int repeat_8bits(int x) {
+    return x & 0xFF;
+}
+
+static inline int mirror_8bits(int x) {
+    if (x & 256) {
+        x = ~x;
+    }
+    return x & 255;
+}
+
+static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
+    SkVector    vec = pts[1] - pts[0];
+    SkScalar    mag = vec.length();
+    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
+
+    vec.scale(inv);
+    SkMatrix matrix;
+    matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
+    matrix.postTranslate(-pts[0].fX, -pts[0].fY);
+    matrix.postScale(inv, inv);
+    return matrix;
+}
+
+static bool use_4f_context(const SkShaderBase::ContextRec& rec, uint32_t flags) {
+#ifdef FORCE_4F_CONTEXT
+    return true;
+#else
+    return rec.fPreferredDstType == SkShaderBase::ContextRec::kPM4f_DstType
+        || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
+    : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
+    , fStart(pts[0])
+    , fEnd(pts[1]) {
+}
+
+sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
+    DescriptorScope desc;
+    if (!desc.unflatten(buffer)) {
+        return nullptr;
+    }
+    SkPoint pts[2];
+    pts[0] = buffer.readPoint();
+    pts[1] = buffer.readPoint();
+    return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos,
+                                        desc.fCount, desc.fTileMode, desc.fGradFlags,
+                                        desc.fLocalMatrix);
+}
+
+void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fStart);
+    buffer.writePoint(fEnd);
+}
+
+SkShaderBase::Context* SkLinearGradient::onMakeContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const
+{
+    return use_4f_context(rec, fGradFlags)
+           ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec)
+           : CheckedMakeContext<  LinearGradientContext>(alloc, *this, rec);
+}
+
+SkShaderBase::Context* SkLinearGradient::onMakeBurstPipelineContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const {
+
+    // TODO: refine heuristic.
+    if (fColorCount <= 8) {
+        return nullptr;
+    }
+    return CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec);
+}
+
+bool SkLinearGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                                   SkMatrix* matrix,
+                                                   SkRasterPipeline* p) const {
+    *matrix = SkMatrix::Concat(fPtsToUnit, *matrix);
+    // If the gradient is less than a quarter of a pixel, this falls into the
+    // subpixel gradient code handled on a different path.
+    SkVector dx = matrix->mapVector(1, 0);
+    if (dx.fX >= 4) {
+        return false;
+    }
+    return true;
+}
+
+sk_sp<SkShader> SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkPoint pts[2] = { fStart, fEnd };
+    SkSTArray<8, SkColor> xformedColors(fColorCount);
+    xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
+    return SkGradientShader::MakeLinear(pts, xformedColors.begin(), fOrigPos, fColorCount,
+                                        fTileMode, fGradFlags, &this->getLocalMatrix());
+}
+
+// This swizzles SkColor into the same component order as SkPMColor, but does not actually
+// "pre" multiply the color components.
+//
+// This allows us to map directly to Sk4f, and eventually scale down to bytes to output a
+// SkPMColor from the floats, without having to swizzle each time.
+//
+static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
+    return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
+}
+
+SkLinearGradient::LinearGradientContext::LinearGradientContext(
+        const SkLinearGradient& shader, const ContextRec& ctx)
+    : INHERITED(shader, ctx)
+{
+    // setup for Sk4f
+    const int count = shader.fColorCount;
+    SkASSERT(count > 1);
+
+    fRecs.setCount(count);
+    Rec* rec = fRecs.begin();
+    if (shader.fOrigPos) {
+        rec[0].fPos = 0;
+        SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;)   // should never get used
+        for (int i = 1; i < count; ++i) {
+            rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f);
+            float diff = rec[i].fPos - rec[i - 1].fPos;
+            if (diff > 0) {
+                rec[i].fPosScale = 1.0f / diff;
+            } else {
+                rec[i].fPosScale = 0;
+            }
+        }
+    } else {
+        // no pos specified, so we compute evenly spaced values
+        const float scale = float(count - 1);
+        const float invScale = 1.0f / scale;
+        for (int i = 0; i < count; ++i) {
+            rec[i].fPos = i * invScale;
+            rec[i].fPosScale = scale;
+        }
+    }
+    rec[count - 1].fPos = 1;    // overwrite the last value just to be sure we end at 1.0
+
+    fApplyAlphaAfterInterp = true;
+    if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) ||
+        shader.colorsAreOpaque())
+    {
+        fApplyAlphaAfterInterp = false;
+    }
+
+    if (fApplyAlphaAfterInterp) {
+        // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to
+        // interpolate in unpremultiplied space first, and then scale by alpha right before we
+        // convert to SkPMColor bytes.
+        const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
+        const Sk4f scale(1, 1, 1, paintAlpha);
+        for (int i = 0; i < count; ++i) {
+            uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
+            rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale;
+            if (i > 0) {
+                SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
+            }
+        }
+    } else {
+        // Our fColor values are premultiplied, so converting to SkPMColor is just a matter
+        // of converting the floats down to bytes.
+        unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7);
+        for (int i = 0; i < count; ++i) {
+            SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
+            pmc = SkAlphaMulQ(pmc, alphaScale);
+            rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc));
+            if (i > 0) {
+                SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
+            }
+        }
+    }
+}
+
+#define NO_CHECK_ITER               \
+    do {                            \
+    unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
+    SkASSERT(fi <= 0xFF);           \
+    fx += dx;                       \
+    *dstC++ = cache[toggle + fi];   \
+    toggle = next_dither_toggle(toggle); \
+    } while (0)
+
+namespace {
+
+typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx,
+                                SkPMColor* dstC, const SkPMColor* cache,
+                                int toggle, int count);
+
+// Linear interpolation (lerp) is unnecessary if there are no sharp
+// discontinuities in the gradient - which must be true if there are
+// only 2 colors - but it's cheap.
+void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
+                                    SkPMColor* SK_RESTRICT dstC,
+                                    const SkPMColor* SK_RESTRICT cache,
+                                    int toggle, int count) {
+    // We're a vertical gradient, so no change in a span.
+    // If colors change sharply across the gradient, dithering is
+    // insufficient (it subsamples the color space) and we need to lerp.
+    unsigned fullIndex = proc(SkGradFixedToFixed(fx));
+    unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
+    unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
+
+    int index0 = fi + toggle;
+    int index1 = index0;
+    if (fi < SkGradientShaderBase::kCache32Count - 1) {
+        index1 += 1;
+    }
+    SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
+    index0 ^= SkGradientShaderBase::kDitherStride32;
+    index1 ^= SkGradientShaderBase::kDitherStride32;
+    SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
+    sk_memset32_dither(dstC, lerp, dlerp, count);
+}
+
+void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx,
+                            SkPMColor* SK_RESTRICT dstC,
+                            const SkPMColor* SK_RESTRICT cache,
+                            int toggle, int count) {
+    SkClampRange range;
+    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
+    range.validate(count);
+
+    if ((count = range.fCount0) > 0) {
+        sk_memset32_dither(dstC,
+            cache[toggle + range.fV0],
+            cache[next_dither_toggle(toggle) + range.fV0],
+            count);
+        dstC += count;
+    }
+    if ((count = range.fCount1) > 0) {
+        int unroll = count >> 3;
+        fx = range.fFx1;
+        for (int i = 0; i < unroll; i++) {
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+        }
+        if ((count &= 7) > 0) {
+            do {
+                NO_CHECK_ITER;
+            } while (--count != 0);
+        }
+    }
+    if ((count = range.fCount2) > 0) {
+        sk_memset32_dither(dstC,
+            cache[toggle + range.fV1],
+            cache[next_dither_toggle(toggle) + range.fV1],
+            count);
+    }
+}
+
+void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx,
+                             SkPMColor* SK_RESTRICT dstC,
+                             const SkPMColor* SK_RESTRICT cache,
+                             int toggle, int count) {
+    do {
+        unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8);
+        SkASSERT(fi <= 0xFF);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle = next_dither_toggle(toggle);
+    } while (--count != 0);
+}
+
+void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx,
+        SkPMColor* SK_RESTRICT dstC,
+        const SkPMColor* SK_RESTRICT cache,
+        int toggle, int count) {
+    do {
+        unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8);
+        SkASSERT(fi <= 0xFF);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle = next_dither_toggle(toggle);
+    } while (--count != 0);
+}
+
+}
+
+void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
+                                                        int count) {
+    SkASSERT(count > 0);
+    const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
+
+    if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
+        kLinear_MatrixClass == fDstToIndexClass)
+    {
+        this->shade4_clamp(x, y, dstC, count);
+        return;
+    }
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = linearGradient.fTileProc;
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
+    int                 toggle = init_dither_toggle(x, y);
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkGradFixed dx, fx = SkScalarPinToGradFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
+            // todo: do we need a real/high-precision value for dx here?
+            dx = SkScalarPinToGradFixed(step.fX);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX());
+        }
+
+        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
+        if (0 == dx) {
+            shadeProc = shadeSpan_linear_vertical_lerp;
+        } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
+            shadeProc = shadeSpan_linear_clamp;
+        } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
+            shadeProc = shadeSpan_linear_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
+        }
+        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
+    } else {
+        SkScalar    dstX = SkIntToScalar(x);
+        SkScalar    dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
+            SkASSERT(fi <= 0xFFFF);
+            *dstC++ = cache[toggle + (fi >> kCache32Shift)];
+            toggle = next_dither_toggle(toggle);
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fStart;
+        info->fPoint[1] = fEnd;
+    }
+    return kLinear_GradientType;
+}
+
+#if SK_SUPPORT_GPU
+
+#include "GrColorSpaceXform.h"
+#include "GrShaderCaps.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "SkGr.h"
+
+/////////////////////////////////////////////////////////////////////
+
+class GrLinearGradient : public GrGradientEffect {
+public:
+    class GLSLLinearProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
+        return sk_sp<GrFragmentProcessor>(new GrLinearGradient(args));
+    }
+
+    ~GrLinearGradient() override {}
+
+    const char* name() const override { return "Linear Gradient"; }
+
+private:
+    GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
+        this->initClassID<GrLinearGradient>();
+    }
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                       GrProcessorKeyBuilder* b) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLLinearProcessor(const GrProcessor&) {}
+
+    ~GLSLLinearProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+        b->add32(GenBaseGradientKey(processor));
+    }
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const {
+    return new GrLinearGradient::GLSLLinearProcessor(*this);
+}
+
+void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                             GrProcessorKeyBuilder* b) const {
+    GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) {
+    SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()},
+                        {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}};
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops,
+                                     params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeLinear(points, params.fColors, params.fStops,
+                                     params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////
+
+void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) {
+    const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>();
+    this->emitUniforms(args.fUniformHandler, ge);
+    SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    t.append(".x");
+    this->emitColor(args.fFragBuilder,
+                    args.fUniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    t.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const {
+    SkASSERT(args.fContext);
+
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return nullptr;
+    }
+    if (args.fLocalMatrix) {
+        SkMatrix inv;
+        if (!args.fLocalMatrix->invert(&inv)) {
+            return nullptr;
+        }
+        matrix.postConcat(inv);
+    }
+    matrix.postConcat(fPtsToUnit);
+
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+                                                                       args.fDstColorSpace);
+    sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make(
+        GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
+                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
+    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+}
+
+
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkLinearGradient::toString(SkString* str) const {
+    str->append("SkLinearGradient (");
+
+    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
+    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkNx.h"
+
+static const SkLinearGradient::LinearGradientContext::Rec*
+find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
+    SkASSERT(tiledX >= 0 && tiledX <= 1);
+
+    SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
+    SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
+    SkASSERT(rec[0].fPos <= rec[1].fPos);
+    rec += 1;
+    while (rec->fPos < tiledX || rec->fPosScale == 0) {
+        SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
+        SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
+        SkASSERT(rec[0].fPos <= rec[1].fPos);
+        rec += 1;
+    }
+    return rec - 1;
+}
+
+static const SkLinearGradient::LinearGradientContext::Rec*
+find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) {
+    SkASSERT(tiledX >= 0 && tiledX <= 1);
+
+    SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
+    SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
+    SkASSERT(rec[0].fPos <= rec[1].fPos);
+    while (tiledX < rec->fPos || rec[1].fPosScale == 0) {
+        rec -= 1;
+        SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1);
+        SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1);
+        SkASSERT(rec[0].fPos <= rec[1].fPos);
+    }
+    return rec;
+}
+
+// As an optimization, we can apply the dither bias before interpolation -- but only when
+// operating in premul space (apply_alpha == false).  When apply_alpha == true, we must
+// defer the bias application until after premul.
+//
+// The following two helpers encapsulate this logic: pre_bias is called before interpolation,
+// and effects the bias when apply_alpha == false, while post_bias is called after premul and
+// effects the bias for the apply_alpha == true case.
+
+template <bool apply_alpha>
+Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) {
+    return apply_alpha ? x : x + bias;
+}
+
+template <bool apply_alpha>
+Sk4f post_bias(const Sk4f& x, const Sk4f& bias) {
+    return apply_alpha ? x + bias : x;
+}
+
+template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) {
+    SkPMColor c;
+    Sk4f c4f255 = x;
+    if (apply_alpha) {
+#ifdef SK_SUPPORT_LEGACY_GRADIENT_ALPHATRUNC
+        static constexpr float alphaScale = 1;
+#else
+        // Due to use of multiplication by the 1/255 reciprocal instead of division by 255,
+        // non-integer alpha values very close to their ceiling can push the color values
+        // above the alpha value, which will become an invalid premultiplied color. So nudge
+        // alpha up slightly by a compensating scale to keep it above the color values.
+        // To do this, multiply alpha by a number slightly greater than 1 to compensate
+        // for error in scaling from the 1/255 approximation. Since this error is then
+        // scaled by the alpha value, we need to scale the epsilon by 255 to get a safe
+        // upper bound on the error.
+        static constexpr float alphaScale = 1 + 255*std::numeric_limits<float>::epsilon();
+#endif
+        const float scale = x[SkPM4f::A] * (1 / 255.f);
+        c4f255 *= Sk4f(scale, scale, scale, alphaScale);
+    }
+    SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c);
+
+    return c;
+}
+
+template <bool apply_alpha> void fill(SkPMColor dst[], int count,
+                                      const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) {
+    const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0);
+    const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1);
+    sk_memset32_dither(dst, c0, c1, count);
+}
+
+template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) {
+    // Assumes that c4 does not need to be dithered.
+    sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count);
+}
+
+/*
+ *  TODOs
+ *
+ *  - tilemodes
+ *  - interp before or after premul
+ *  - perspective
+ *  - optimizations
+ *      - use fixed (32bit or 16bit) instead of floats?
+ */
+
+static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) {
+    SkASSERT(fx >= rec[0].fPos);
+    SkASSERT(fx <= rec[1].fPos);
+
+    const float p0 = rec[0].fPos;
+    const Sk4f c0 = rec[0].fColor;
+    const Sk4f c1 = rec[1].fColor;
+    const Sk4f diffc = c1 - c0;
+    const float scale = rec[1].fPosScale;
+    const float t = (fx - p0) * scale;
+    return c0 + Sk4f(t) * diffc;
+}
+
+template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc,
+                                      const Sk4f& dither0, const Sk4f& dither1) {
+    Sk4f dc2 = dc + dc;
+    Sk4f dc4 = dc2 + dc2;
+    Sk4f cd0 = pre_bias<apply_alpha>(c     , dither0);
+    Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1);
+    Sk4f cd2 = cd0 + dc2;
+    Sk4f cd3 = cd1 + dc2;
+    while (n >= 4) {
+        if (!apply_alpha) {
+            Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3);
+            dstC += 4;
+        } else {
+            *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
+            *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
+            *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0);
+            *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1);
+        }
+        cd0 = cd0 + dc4;
+        cd1 = cd1 + dc4;
+        cd2 = cd2 + dc4;
+        cd3 = cd3 + dc4;
+        n -= 4;
+    }
+    if (n & 2) {
+        *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
+        *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1);
+        cd0 = cd0 + dc2;
+    }
+    if (n & 1) {
+        *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0);
+    }
+}
+
+template <bool apply_alpha, bool dx_is_pos>
+void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count,
+                                                              float fx, float dx, float invDx,
+                                                              const float dither[2]) {
+    Sk4f dither0(dither[0]);
+    Sk4f dither1(dither[1]);
+    const Rec* rec = fRecs.begin();
+
+    const Sk4f dx4 = Sk4f(dx);
+    SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
+
+    if (dx_is_pos) {
+        if (fx < 0) {
+            // count is guaranteed to be positive, but the first arg may overflow int32 after
+            // increment => casting to uint32 ensures correct clamping.
+            int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1,
+                                     count);
+            SkASSERT(n > 0);
+            fill<apply_alpha>(dstC, n, rec[0].fColor);
+            count -= n;
+            dstC += n;
+            fx += n * dx;
+            SkASSERT(0 == count || fx >= 0);
+            if (n & 1) {
+                SkTSwap(dither0, dither1);
+            }
+        }
+    } else { // dx < 0
+        if (fx > 1) {
+            // count is guaranteed to be positive, but the first arg may overflow int32 after
+            // increment => casting to uint32 ensures correct clamping.
+            int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1,
+                                     count);
+            SkASSERT(n > 0);
+            fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor);
+            count -= n;
+            dstC += n;
+            fx += n * dx;
+            SkASSERT(0 == count || fx <= 1);
+            if (n & 1) {
+                SkTSwap(dither0, dither1);
+            }
+        }
+    }
+    SkASSERT(count >= 0);
+
+    const Rec* r;
+    if (dx_is_pos) {
+        r = fRecs.begin();                      // start at the beginning
+    } else {
+        r = fRecs.begin() + fRecs.count() - 2;  // start at the end
+    }
+
+    while (count > 0) {
+        if (dx_is_pos) {
+            if (fx >= 1) {
+                fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor);
+                return;
+            }
+        } else {    // dx < 0
+            if (fx <= 0) {
+                fill<apply_alpha>(dstC, count, rec[0].fColor);
+                return;
+            }
+        }
+
+        if (dx_is_pos) {
+            r = find_forward(r, fx);
+        } else {
+            r = find_backward(r, fx);
+        }
+        SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1);
+
+        const float p0 = r[0].fPos;
+        const Sk4f c0 = r[0].fColor;
+        const float p1 = r[1].fPos;
+        const Sk4f diffc = Sk4f(r[1].fColor) - c0;
+        const float scale = r[1].fPosScale;
+        const float t = (fx - p0) * scale;
+        const Sk4f c = c0 + Sk4f(t) * diffc;
+        const Sk4f dc = diffc * dx4 * Sk4f(scale);
+
+        int n;
+        if (dx_is_pos) {
+            n = SkTMin((int)((p1 - fx) * invDx) + 1, count);
+        } else {
+            n = SkTMin((int)((p0 - fx) * invDx) + 1, count);
+        }
+
+        fx += n * dx;
+        // fx should now outside of the p0..p1 interval. However, due to float precision loss,
+        // its possible that fx is slightly too small/large, so we clamp it.
+        if (dx_is_pos) {
+            fx = SkTMax(fx, p1);
+        } else {
+            fx = SkTMin(fx, p0);
+        }
+
+        ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
+        dstC += n;
+        SkASSERT(dstC <= endDstC);
+
+        if (n & 1) {
+            SkTSwap(dither0, dither1);
+        }
+
+        count -= n;
+        SkASSERT(count >= 0);
+    }
+}
+
+void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[],
+                                                           int count) {
+    SkASSERT(count > 0);
+    SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
+
+    SkPoint srcPt;
+    fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
+    float fx = srcPt.x();
+    const float dx = fDstToIndex.getScaleX();
+
+    // Default our dither bias values to 1/2, (rounding), which is no dithering
+    float dither0 = 0.5f;
+    float dither1 = 0.5f;
+    if (fDither) {
+        const float ditherCell[] = {
+            1/8.0f,   5/8.0f,
+            7/8.0f,   3/8.0f,
+        };
+        const int rowIndex = (y & 1) << 1;
+        dither0 = ditherCell[rowIndex];
+        dither1 = ditherCell[rowIndex + 1];
+        if (x & 1) {
+            SkTSwap(dither0, dither1);
+        }
+    }
+    const float dither[2] = { dither0, dither1 };
+
+    if (SkScalarNearlyZero(dx * count)) { // gradient is vertical
+        const float pinFx = SkTPin(fx, 0.0f, 1.0f);
+        Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx));
+        if (fApplyAlphaAfterInterp) {
+            fill<true>(dstC, count, c, dither0, dither1);
+        } else {
+            fill<false>(dstC, count, c, dither0, dither1);
+        }
+        return;
+    }
+
+    SkASSERT(0.f != dx);
+    const float invDx = 1 / dx;
+    if (dx > 0) {
+        if (fApplyAlphaAfterInterp) {
+            this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither);
+        } else {
+            this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither);
+        }
+    } else {
+        if (fApplyAlphaAfterInterp) {
+            this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither);
+        } else {
+            this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither);
+        }
+    }
+}
diff --git a/src/shaders/gradients/SkLinearGradient.h b/src/shaders/gradients/SkLinearGradient.h
new file mode 100644
index 0000000..300807c
--- /dev/null
+++ b/src/shaders/gradients/SkLinearGradient.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkLinearGradient_DEFINED
+#define SkLinearGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+#include "SkNx.h"
+
+struct Sk4fStorage {
+    float fArray[4];
+
+    operator Sk4f() const {
+        return Sk4f::Load(fArray);
+    }
+
+    Sk4fStorage& operator=(const Sk4f& src) {
+        src.store(fArray);
+        return *this;
+    }
+};
+
+class SkLinearGradient : public SkGradientShaderBase {
+public:
+    enum {
+        // Temp flag for testing the 4f impl.
+        kForce4fContext_PrivateFlag     = 1 << 7,
+    };
+
+    SkLinearGradient(const SkPoint pts[2], const Descriptor&);
+
+    class LinearGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        LinearGradientContext(const SkLinearGradient&, const ContextRec&);
+
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+
+        struct Rec {
+            Sk4fStorage fColor;
+            float       fPos;
+            float       fPosScale;
+        };
+    private:
+        SkTDArray<Rec>  fRecs;
+        bool            fApplyAlphaAfterInterp;
+
+        void shade4_clamp(int x, int y, SkPMColor dstC[], int count);
+        template <bool, bool> void shade4_dx_clamp(SkPMColor dstC[], int count, float fx, float dx,
+                                                   float invDx, const float dither[2]);
+
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
+
+    GradientType asAGradient(GradientInfo* info) const override;
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient)
+
+protected:
+    SkLinearGradient(SkReadBuffer& buffer);
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    Context* onMakeBurstPipelineContext(const ContextRec&, SkArenaAlloc*) const override;
+
+    bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                     SkMatrix* matrix,
+                                     SkRasterPipeline* p) const final;
+
+
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
+private:
+    class LinearGradient4fContext;
+
+    friend class SkGradientShader;
+    typedef SkGradientShaderBase INHERITED;
+    const SkPoint fStart;
+    const SkPoint fEnd;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp
new file mode 100644
index 0000000..d49b3dd
--- /dev/null
+++ b/src/shaders/gradients/SkRadialGradient.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorSpaceXformer.h"
+#include "SkRadialGradient.h"
+#include "SkNx.h"
+
+namespace {
+
+// GCC doesn't like using static functions as template arguments.  So force these to be non-static.
+inline SkFixed mirror_tileproc_nonstatic(SkFixed x) {
+    return mirror_tileproc(x);
+}
+
+inline SkFixed repeat_tileproc_nonstatic(SkFixed x) {
+    return repeat_tileproc(x);
+}
+
+SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
+    SkScalar    inv = SkScalarInvert(radius);
+
+    SkMatrix matrix;
+    matrix.setTranslate(-center.fX, -center.fY);
+    matrix.postScale(inv, inv);
+    return matrix;
+}
+
+
+}  // namespace
+
+/////////////////////////////////////////////////////////////////////
+
+SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
+    : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
+    , fCenter(center)
+    , fRadius(radius) {
+}
+
+SkShaderBase::Context* SkRadialGradient::onMakeContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const
+{
+    return CheckedMakeContext<RadialGradientContext>(alloc, *this, rec);
+}
+
+SkRadialGradient::RadialGradientContext::RadialGradientContext(
+        const SkRadialGradient& shader, const ContextRec& rec)
+    : INHERITED(shader, rec) {}
+
+SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fCenter;
+        info->fRadius[0] = fRadius;
+    }
+    return kRadial_GradientType;
+}
+
+sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) {
+    DescriptorScope desc;
+    if (!desc.unflatten(buffer)) {
+        return nullptr;
+    }
+    const SkPoint center = buffer.readPoint();
+    const SkScalar radius = buffer.readScalar();
+    return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace),
+                                        desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags,
+                                        desc.fLocalMatrix);
+}
+
+void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter);
+    buffer.writeScalar(fRadius);
+}
+
+namespace {
+
+inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
+    // fast, overly-conservative test: checks unit square instead of unit circle
+    bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
+    bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
+    return xClamped || yClamped;
+}
+
+typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* dstC, const SkPMColor* cache,
+        int count, int toggle);
+
+static inline Sk4f fast_sqrt(const Sk4f& R) {
+    return R * R.rsqrt();
+}
+
+static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
+    return a * a + b * b;
+}
+
+void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
+                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+                             int count, int toggle) {
+    if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
+        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
+        sk_memset32_dither(dstC,
+                           cache[toggle + fi],
+                           cache[next_dither_toggle(toggle) + fi],
+                           count);
+    } else {
+        const Sk4f min(SK_ScalarNearlyZero);
+        const Sk4f max(255);
+        const float scale = 255;
+        sfx *= scale;
+        sfy *= scale;
+        sdx *= scale;
+        sdy *= scale;
+        const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
+        const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
+        const Sk4f dx4(sdx * 4);
+        const Sk4f dy4(sdy * 4);
+
+        Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
+        Sk4f tmpdxdy = sum_squares(dx4, dy4);
+        Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min);
+        Sk4f dR = tmpxy + tmpxy + tmpdxdy;
+        const Sk4f ddR = tmpdxdy + tmpdxdy;
+
+        for (int i = 0; i < (count >> 2); ++i) {
+            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
+            R = Sk4f::Max(R + dR, min);
+            dR = dR + ddR;
+
+            uint8_t fi[4];
+            SkNx_cast<uint8_t>(dist).store(fi);
+
+            for (int i = 0; i < 4; i++) {
+                *dstC++ = cache[toggle + fi[i]];
+                toggle = next_dither_toggle(toggle);
+            }
+        }
+        count &= 3;
+        if (count) {
+            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
+
+            uint8_t fi[4];
+            SkNx_cast<uint8_t>(dist).store(fi);
+            for (int i = 0; i < count; i++) {
+                *dstC++ = cache[toggle + fi[i]];
+                toggle = next_dither_toggle(toggle);
+            }
+        }
+    }
+}
+
+// Unrolling this loop doesn't seem to help (when float); we're stalling to
+// get the results of the sqrt (?), and don't have enough extra registers to
+// have many in flight.
+template <SkFixed (*TileProc)(SkFixed)>
+void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
+                      SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+                      int count, int toggle) {
+    do {
+        const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy));
+        const unsigned fi = TileProc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
+        toggle = next_dither_toggle(toggle);
+        fx += dx;
+        fy += dy;
+    } while (--count != 0);
+}
+
+void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
+                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+                             int count, int toggle) {
+    shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
+}
+
+void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy,
+                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+                             int count, int toggle) {
+    shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle);
+}
+
+}  // namespace
+
+void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
+                                                        SkPMColor* SK_RESTRICT dstC, int count) {
+    SkASSERT(count > 0);
+
+    const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = radialGradient.fTileProc;
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
+    int toggle = init_dither_toggle(x, y);
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar sdx = fDstToIndex.getScaleX();
+        SkScalar sdy = fDstToIndex.getSkewY();
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
+            sdx = step.fX;
+            sdy = step.fY;
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+        }
+
+        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
+        if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
+            shadeProc = shadeSpan_radial_clamp2;
+        } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
+            shadeProc = shadeSpan_radial_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
+        }
+        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
+    } else {    // perspective case
+        SkScalar dstX = SkIntToScalar(x);
+        SkScalar dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
+            SkASSERT(fi <= 0xFFFF);
+            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "SkGr.h"
+#include "GrShaderCaps.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+class GrRadialGradient : public GrGradientEffect {
+public:
+    class GLSLRadialProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
+        return sk_sp<GrFragmentProcessor>(new GrRadialGradient(args));
+    }
+
+    ~GrRadialGradient() override {}
+
+    const char* name() const override { return "Radial Gradient"; }
+
+private:
+    GrRadialGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
+        this->initClassID<GrRadialGradient>();
+    }
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                       GrProcessorKeyBuilder* b) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLRadialProcessor(const GrProcessor&) {}
+    ~GLSLRadialProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+        b->add32(GenBaseGradientKey(processor));
+    }
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const {
+    return new GrRadialGradient::GLSLRadialProcessor(*this);
+}
+
+void GrRadialGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                             GrProcessorKeyBuilder* b) const {
+    GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) {
+    sk_sp<SkShader> shader;
+    do {
+        RandomGradientParams params(d->fRandom);
+        SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+        SkScalar radius = d->fRandom->nextUScalar1();
+        shader = params.fUseColors4f
+                         ? SkGradientShader::MakeRadial(center, radius, params.fColors4f,
+                                                        params.fColorSpace, params.fStops,
+                                                        params.fColorCount, params.fTileMode)
+                         : SkGradientShader::MakeRadial(center, radius, params.fColors,
+                                                        params.fStops, params.fColorCount,
+                                                        params.fTileMode);
+    } while (!shader);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////
+
+void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) {
+    const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>();
+    this->emitUniforms(args.fUniformHandler, ge);
+    SkString t("length(");
+    t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]));
+    t.append(")");
+    this->emitColor(args.fFragBuilder,
+                    args.fUniformHandler,
+                    args.fShaderCaps,
+                    ge, t.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const {
+    SkASSERT(args.fContext);
+
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return nullptr;
+    }
+    if (args.fLocalMatrix) {
+        SkMatrix inv;
+        if (!args.fLocalMatrix->invert(&inv)) {
+            return nullptr;
+        }
+        matrix.postConcat(inv);
+    }
+    matrix.postConcat(fPtsToUnit);
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+                                                                       args.fDstColorSpace);
+    sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make(
+        GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
+                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
+    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+}
+
+#endif
+
+sk_sp<SkShader> SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<8, SkColor> xformedColors(fColorCount);
+    xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
+    return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.begin(), fOrigPos,
+                                        fColorCount, fTileMode, fGradFlags,
+                                        &this->getLocalMatrix());
+}
+
+bool SkRadialGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                 SkMatrix* matrix,
+                                 SkRasterPipeline* p) const {
+    matrix->postTranslate(-fCenter.fX, -fCenter.fY);
+    matrix->postScale(1/fRadius, 1/fRadius);
+
+    p->append(SkRasterPipeline::xy_to_radius);
+    return true;
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkRadialGradient::toString(SkString* str) const {
+    str->append("SkRadialGradient: (");
+
+    str->append("center: (");
+    str->appendScalar(fCenter.fX);
+    str->append(", ");
+    str->appendScalar(fCenter.fY);
+    str->append(") radius: ");
+    str->appendScalar(fRadius);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/shaders/gradients/SkRadialGradient.h b/src/shaders/gradients/SkRadialGradient.h
new file mode 100644
index 0000000..69ec4b1
--- /dev/null
+++ b/src/shaders/gradients/SkRadialGradient.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRadialGradient_DEFINED
+#define SkRadialGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+
+class SkRadialGradient : public SkGradientShaderBase {
+public:
+    SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&);
+
+    class RadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        RadialGradientContext(const SkRadialGradient&, const ContextRec&);
+
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
+
+    GradientType asAGradient(GradientInfo* info) const override;
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient)
+
+protected:
+    SkRadialGradient(SkReadBuffer& buffer);
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+    
+    bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                     SkMatrix* matrix,
+                                     SkRasterPipeline* p) const final;
+
+private:
+    const SkPoint fCenter;
+    const SkScalar fRadius;
+
+    friend class SkGradientShader;
+    typedef SkGradientShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
new file mode 100644
index 0000000..1e583c2
--- /dev/null
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorSpaceXformer.h"
+#include "SkSweepGradient.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "SkPM4fPriv.h"
+#include "SkRasterPipeline.h"
+
+static SkMatrix translate(SkScalar dx, SkScalar dy) {
+    SkMatrix matrix;
+    matrix.setTranslate(dx, dy);
+    return matrix;
+}
+
+SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor& desc)
+    : SkGradientShaderBase(desc, translate(-cx, -cy))
+    , fCenter(SkPoint::Make(cx, cy))
+{
+    // overwrite the tilemode to a canonical value (since sweep ignores it)
+    fTileMode = SkShader::kClamp_TileMode;
+}
+
+SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fCenter;
+    }
+    return kSweep_GradientType;
+}
+
+sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) {
+    DescriptorScope desc;
+    if (!desc.unflatten(buffer)) {
+        return nullptr;
+    }
+    const SkPoint center = buffer.readPoint();
+    return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors,
+                                       std::move(desc.fColorSpace), desc.fPos, desc.fCount,
+                                       desc.fGradFlags, desc.fLocalMatrix);
+}
+
+void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter);
+}
+
+SkShaderBase::Context* SkSweepGradient::onMakeContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const
+{
+    return CheckedMakeContext<SweepGradientContext>(alloc, *this, rec);
+}
+
+SkSweepGradient::SweepGradientContext::SweepGradientContext(
+        const SkSweepGradient& shader, const ContextRec& rec)
+    : INHERITED(shader, rec) {}
+
+bool SkSweepGradient::isRasterPipelineOnly() const {
+#ifdef SK_LEGACY_SWEEP_GRADIENT
+    return false;
+#else
+    return true;
+#endif
+}
+
+//  returns angle in a circle [0..2PI) -> [0..255]
+static unsigned SkATan2_255(float y, float x) {
+    //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
+    static const float g255Over2PI = 40.584510488433314f;
+
+    float result = sk_float_atan2(y, x);
+    if (!SkScalarIsFinite(result)) {
+        return 0;
+    }
+    if (result < 0) {
+        result += 2 * SK_ScalarPI;
+    }
+    SkASSERT(result >= 0);
+    // since our value is always >= 0, we can cast to int, which is faster than
+    // calling floorf()
+    int ir = (int)(result * g255Over2PI);
+    SkASSERT(ir >= 0 && ir <= 255);
+    return ir;
+}
+
+void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
+                                                      int count) {
+    SkMatrix::MapXYProc proc = fDstToIndexProc;
+    const SkMatrix&     matrix = fDstToIndex;
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
+    int                 toggle = init_dither_toggle(x, y);
+    SkPoint             srcPt;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar dx, fx = srcPt.fX;
+        SkScalar dy, fy = srcPt.fY;
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            const auto step = matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf);
+            dx = step.fX;
+            dy = step.fY;
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = matrix.getScaleX();
+            dy = matrix.getSkewY();
+        }
+
+        for (; count > 0; --count) {
+            *dstC++ = cache[toggle + SkATan2_255(fy, fx)];
+            fx += dx;
+            fy += dy;
+            toggle = next_dither_toggle(toggle);
+        }
+    } else {  // perspective case
+        for (int stop = x + count; x < stop; x++) {
+            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+            *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)];
+            toggle = next_dither_toggle(toggle);
+        }
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "SkGr.h"
+#include "GrShaderCaps.h"
+#include "gl/GrGLContext.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+class GrSweepGradient : public GrGradientEffect {
+public:
+    class GLSLSweepProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
+        return sk_sp<GrFragmentProcessor>(new GrSweepGradient(args));
+    }
+    ~GrSweepGradient() override {}
+
+    const char* name() const override { return "Sweep Gradient"; }
+
+private:
+    GrSweepGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) {
+        this->initClassID<GrSweepGradient>();
+    }
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                       GrProcessorKeyBuilder* b) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrSweepGradient::GLSLSweepProcessor : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLSweepProcessor(const GrProcessor&) {}
+    ~GLSLSweepProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+        b->add32(GenBaseGradientKey(processor));
+    }
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLSLFragmentProcessor* GrSweepGradient::onCreateGLSLInstance() const {
+    return new GrSweepGradient::GLSLSweepProcessor(*this);
+}
+
+void GrSweepGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                            GrProcessorKeyBuilder* b) const {
+    GrSweepGradient::GLSLSweepProcessor::GenKey(*this, caps, b);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSweepGradient);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> GrSweepGradient::TestCreate(GrProcessorTestData* d) {
+    SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors4f, params.fColorSpace,
+                                    params.fStops, params.fColorCount) :
+        SkGradientShader::MakeSweep(center.fX, center.fY,  params.fColors,
+                                    params.fStops, params.fColorCount);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////
+
+void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) {
+    const GrSweepGradient& ge = args.fFp.cast<GrSweepGradient>();
+    this->emitUniforms(args.fUniformHandler, ge);
+    SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    SkString t;
+    // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
+    if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) {
+        // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
+        // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in
+        // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
+        // handle the undefined behavior of the second paramenter being 0 instead of doing the
+        // divide ourselves and using atan instead.
+        t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)",
+                 coords2D.c_str(), coords2D.c_str(), coords2D.c_str());
+    } else {
+        t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)",
+                 coords2D.c_str(), coords2D.c_str());
+    }
+    this->emitColor(args.fFragBuilder,
+                    args.fUniformHandler,
+                    args.fShaderCaps,
+                    ge, t.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs& args) const {
+
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return nullptr;
+    }
+    if (args.fLocalMatrix) {
+        SkMatrix inv;
+        if (!args.fLocalMatrix->invert(&inv)) {
+            return nullptr;
+        }
+        matrix.postConcat(inv);
+    }
+    matrix.postConcat(fPtsToUnit);
+
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+                                                                       args.fDstColorSpace);
+    sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make(
+        GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode,
+                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
+    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+}
+
+#endif
+
+sk_sp<SkShader> SkSweepGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<8, SkColor> xformedColors(fColorCount);
+    xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
+    return SkGradientShader::MakeSweep(fCenter.fX, fCenter.fY, xformedColors.begin(), fOrigPos,
+                                       fColorCount, fGradFlags, &this->getLocalMatrix());
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkSweepGradient::toString(SkString* str) const {
+    str->append("SkSweepGradient: (");
+
+    str->append("center: (");
+    str->appendScalar(fCenter.fX);
+    str->append(", ");
+    str->appendScalar(fCenter.fY);
+    str->append(") ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+
+bool SkSweepGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                                  SkMatrix* matrix,
+                                                  SkRasterPipeline* p) const {
+    matrix->postTranslate(-fCenter.fX, -fCenter.fY);
+    p->append(SkRasterPipeline::xy_to_unit_angle);
+
+    return true;
+}
+
+#endif
diff --git a/src/shaders/gradients/SkSweepGradient.h b/src/shaders/gradients/SkSweepGradient.h
new file mode 100644
index 0000000..b7ed7e5
--- /dev/null
+++ b/src/shaders/gradients/SkSweepGradient.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSweepGradient_DEFINED
+#define SkSweepGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+
+class SkSweepGradient : public SkGradientShaderBase {
+public:
+    SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&);
+
+    class SweepGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        SweepGradientContext(const SkSweepGradient& shader, const ContextRec&);
+
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
+
+    GradientType asAGradient(GradientInfo* info) const override;
+
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient)
+
+protected:
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
+    bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
+                                     SkMatrix* matrix,
+                                     SkRasterPipeline* p) const final;
+
+    bool isRasterPipelineOnly() const final;
+
+private:
+    const SkPoint fCenter;
+
+    friend class SkGradientShader;
+    typedef SkGradientShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
new file mode 100644
index 0000000..4549527
--- /dev/null
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTwoPointConicalGradient.h"
+
+struct TwoPtRadialContext {
+    const TwoPtRadial&  fRec;
+    float               fRelX, fRelY;
+    const float         fIncX, fIncY;
+    float               fB;
+    const float         fDB;
+
+    TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
+                       SkScalar dfx, SkScalar dfy);
+    SkFixed nextT();
+};
+
+static int valid_divide(float numer, float denom, float* ratio) {
+    SkASSERT(ratio);
+    if (0 == denom) {
+        return 0;
+    }
+    *ratio = numer / denom;
+    return 1;
+}
+
+// Return the number of distinct real roots, and write them into roots[] in
+// ascending order
+static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
+    SkASSERT(roots);
+
+    if (A == 0) {
+        return valid_divide(-C, B, roots);
+    }
+
+    float R = B*B - 4*A*C;
+    if (R < 0) {
+        return 0;
+    }
+    R = sk_float_sqrt(R);
+
+#if 1
+    float Q = B;
+    if (Q < 0) {
+        Q -= R;
+    } else {
+        Q += R;
+    }
+#else
+    // on 10.6 this was much slower than the above branch :(
+    float Q = B + copysignf(R, B);
+#endif
+    Q *= -0.5f;
+    if (0 == Q) {
+        roots[0] = 0;
+        return 1;
+    }
+
+    float r0 = Q / A;
+    float r1 = C / Q;
+    roots[0] = r0 < r1 ? r0 : r1;
+    roots[1] = r0 > r1 ? r0 : r1;
+    if (descendingOrder) {
+        SkTSwap(roots[0], roots[1]);
+    }
+    return 2;
+}
+
+static float lerp(float x, float dx, float t) {
+    return x + t * dx;
+}
+
+static float sqr(float x) { return x * x; }
+
+void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
+                       const SkPoint& center1, SkScalar rad1,
+                       bool flipped) {
+    fCenterX = SkScalarToFloat(center0.fX);
+    fCenterY = SkScalarToFloat(center0.fY);
+    fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
+    fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
+    fRadius = SkScalarToFloat(rad0);
+    fDRadius = SkScalarToFloat(rad1) - fRadius;
+
+    fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
+    fRadius2 = sqr(fRadius);
+    fRDR = fRadius * fDRadius;
+
+    fFlipped = flipped;
+}
+
+TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
+                                       SkScalar dfx, SkScalar dfy)
+    : fRec(rec)
+    , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
+    , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
+    , fIncX(SkScalarToFloat(dfx))
+    , fIncY(SkScalarToFloat(dfy))
+    , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
+    , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
+
+SkFixed TwoPtRadialContext::nextT() {
+    float roots[2];
+
+    float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
+    int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
+
+    fRelX += fIncX;
+    fRelY += fIncY;
+    fB += fDB;
+
+    if (0 == countRoots) {
+        return TwoPtRadial::kDontDrawT;
+    }
+
+    // Prefer the bigger t value if both give a radius(t) > 0
+    // find_quad_roots returns the values sorted, so we start with the last
+    float t = roots[countRoots - 1];
+    float r = lerp(fRec.fRadius, fRec.fDRadius, t);
+    if (r < 0) {
+        t = roots[0];   // might be the same as roots[countRoots-1]
+        r = lerp(fRec.fRadius, fRec.fDRadius, t);
+        if (r < 0) {
+            return TwoPtRadial::kDontDrawT;
+        }
+    }
+    return SkFloatToFixed(t);
+}
+
+typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
+                                    const SkPMColor* cache, int toggle, int count);
+
+static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
+                           const SkPMColor* SK_RESTRICT cache, int toggle,
+                           int count) {
+    for (; count > 0; --count) {
+        SkFixed t = rec->nextT();
+        if (TwoPtRadial::DontDrawT(t)) {
+            *dstC++ = 0;
+        } else {
+            SkFixed index = SkClampMax(t, 0xFFFF);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[toggle +
+                            (index >> SkGradientShaderBase::kCache32Shift)];
+        }
+        toggle = next_dither_toggle(toggle);
+    }
+}
+
+static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
+                            const SkPMColor* SK_RESTRICT cache, int toggle,
+                            int count) {
+    for (; count > 0; --count) {
+        SkFixed t = rec->nextT();
+        if (TwoPtRadial::DontDrawT(t)) {
+            *dstC++ = 0;
+        } else {
+            SkFixed index = repeat_tileproc(t);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[toggle +
+                            (index >> SkGradientShaderBase::kCache32Shift)];
+        }
+        toggle = next_dither_toggle(toggle);
+    }
+}
+
+static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
+                            const SkPMColor* SK_RESTRICT cache, int toggle,
+                            int count) {
+    for (; count > 0; --count) {
+        SkFixed t = rec->nextT();
+        if (TwoPtRadial::DontDrawT(t)) {
+            *dstC++ = 0;
+        } else {
+            SkFixed index = mirror_tileproc(t);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[toggle +
+                            (index >> SkGradientShaderBase::kCache32Shift)];
+        }
+        toggle = next_dither_toggle(toggle);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+SkTwoPointConicalGradient::SkTwoPointConicalGradient(
+        const SkPoint& start, SkScalar startRadius,
+        const SkPoint& end, SkScalar endRadius,
+        bool flippedGrad, const Descriptor& desc)
+    : SkGradientShaderBase(desc, SkMatrix::I())
+    , fCenter1(start)
+    , fCenter2(end)
+    , fRadius1(startRadius)
+    , fRadius2(endRadius)
+    , fFlippedGrad(flippedGrad)
+{
+    // this is degenerate, and should be caught by our caller
+    SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
+    fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
+}
+
+bool SkTwoPointConicalGradient::isOpaque() const {
+    // Because areas outside the cone are left untouched, we cannot treat the
+    // shader as opaque even if the gradient itself is opaque.
+    // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
+    return false;
+}
+
+SkShaderBase::Context* SkTwoPointConicalGradient::onMakeContext(
+    const ContextRec& rec, SkArenaAlloc* alloc) const {
+    return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec);
+}
+
+SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
+        const SkTwoPointConicalGradient& shader, const ContextRec& rec)
+    : INHERITED(shader, rec)
+{
+    // in general, we might discard based on computed-radius, so clear
+    // this flag (todo: sometimes we can detect that we never discard...)
+    fFlags &= ~kOpaqueAlpha_Flag;
+}
+
+void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
+        int x, int y, SkPMColor* dstCParam, int count) {
+    const SkTwoPointConicalGradient& twoPointConicalGradient =
+            static_cast<const SkTwoPointConicalGradient&>(fShader);
+
+    int toggle = init_dither_toggle(x, y);
+
+    SkASSERT(count > 0);
+
+    SkPMColor* SK_RESTRICT dstC = dstCParam;
+
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
+
+    TwoPointConicalProc shadeProc = twopoint_repeat;
+    if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
+        shadeProc = twopoint_clamp;
+    } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
+        shadeProc = twopoint_mirror;
+    } else {
+        SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
+    }
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        SkPoint srcPt;
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar dx, fx = srcPt.fX;
+        SkScalar dy, fy = srcPt.fY;
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y));
+            dx = step.fX;
+            dy = step.fY;
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = fDstToIndex.getScaleX();
+            dy = fDstToIndex.getSkewY();
+        }
+
+        TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
+        (*shadeProc)(&rec, dstC, cache, toggle, count);
+    } else {    // perspective case
+        SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
+        SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
+        for (; count > 0; --count) {
+            SkPoint srcPt;
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
+            (*shadeProc)(&rec, dstC, cache, toggle, 1);
+
+            dstX += SK_Scalar1;
+            toggle = next_dither_toggle(toggle);
+            dstC += 1;
+        }
+    }
+}
+
+// Returns the original non-sorted version of the gradient
+SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
+    GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info, fFlippedGrad);
+        info->fPoint[0] = fCenter1;
+        info->fPoint[1] = fCenter2;
+        info->fRadius[0] = fRadius1;
+        info->fRadius[1] = fRadius2;
+        if (fFlippedGrad) {
+            SkTSwap(info->fPoint[0], info->fPoint[1]);
+            SkTSwap(info->fRadius[0], info->fRadius[1]);
+        }
+    }
+    return kConical_GradientType;
+}
+
+sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
+    DescriptorScope desc;
+    if (!desc.unflatten(buffer)) {
+        return nullptr;
+    }
+    SkPoint c1 = buffer.readPoint();
+    SkPoint c2 = buffer.readPoint();
+    SkScalar r1 = buffer.readScalar();
+    SkScalar r2 = buffer.readScalar();
+
+    if (buffer.readBool()) {    // flipped
+        SkTSwap(c1, c2);
+        SkTSwap(r1, r2);
+
+        SkColor4f* colors = desc.mutableColors();
+        SkScalar* pos = desc.mutablePos();
+        const int last = desc.fCount - 1;
+        const int half = desc.fCount >> 1;
+        for (int i = 0; i < half; ++i) {
+            SkTSwap(colors[i], colors[last - i]);
+            if (pos) {
+                SkScalar tmp = pos[i];
+                pos[i] = SK_Scalar1 - pos[last - i];
+                pos[last - i] = SK_Scalar1 - tmp;
+            }
+        }
+        if (pos) {
+            if (desc.fCount & 1) {
+                pos[half] = SK_Scalar1 - pos[half];
+            }
+        }
+    }
+
+    return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
+                                                 std::move(desc.fColorSpace), desc.fPos,
+                                                 desc.fCount, desc.fTileMode, desc.fGradFlags,
+                                                 desc.fLocalMatrix);
+}
+
+void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter1);
+    buffer.writePoint(fCenter2);
+    buffer.writeScalar(fRadius1);
+    buffer.writeScalar(fRadius2);
+    buffer.writeBool(fFlippedGrad);
+}
+
+#if SK_SUPPORT_GPU
+
+#include "SkGr.h"
+#include "SkTwoPointConicalGradient_gpu.h"
+
+sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
+        const AsFPArgs& args) const {
+    SkASSERT(args.fContext);
+    SkASSERT(fPtsToUnit.isIdentity());
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
+                                                                       args.fDstColorSpace);
+    sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
+        GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
+                                     std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
+    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
+}
+
+#endif
+
+sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<8, SkColor> origColorsStorage(fColorCount);
+    SkSTArray<8, SkScalar> origPosStorage(fColorCount);
+    SkSTArray<8, SkColor> xformedColorsStorage(fColorCount);
+    SkColor* origColors = origColorsStorage.begin();
+    SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr;
+    SkColor* xformedColors = xformedColorsStorage.begin();
+
+    // Flip if necessary
+    SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1;
+    SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2;
+    SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1;
+    SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2;
+    for (int i = 0; i < fColorCount; i++) {
+        origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i];
+        if (origPos) {
+            origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i];
+        }
+    }
+
+    xformer->apply(xformedColors, origColors, fColorCount);
+    return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors,
+                                                 origPos, fColorCount, fTileMode, fGradFlags,
+                                                 &this->getLocalMatrix());
+}
+
+
+#ifndef SK_IGNORE_TO_STRING
+void SkTwoPointConicalGradient::toString(SkString* str) const {
+    str->append("SkTwoPointConicalGradient: (");
+
+    str->append("center1: (");
+    str->appendScalar(fCenter1.fX);
+    str->append(", ");
+    str->appendScalar(fCenter1.fY);
+    str->append(") radius1: ");
+    str->appendScalar(fRadius1);
+    str->append(" ");
+
+    str->append("center2: (");
+    str->appendScalar(fCenter2.fX);
+    str->append(", ");
+    str->appendScalar(fCenter2.fY);
+    str->append(") radius2: ");
+    str->appendScalar(fRadius2);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.h b/src/shaders/gradients/SkTwoPointConicalGradient.h
new file mode 100644
index 0000000..b32f52c
--- /dev/null
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTwoPointConicalGradient_DEFINED
+#define SkTwoPointConicalGradient_DEFINED
+
+#include "SkColorSpaceXformer.h"
+#include "SkGradientShaderPriv.h"
+
+// TODO(dominikg): Worth making it truly immutable (i.e. set values in constructor)?
+// Should only be initialized once via init(). Immutable afterwards.
+struct TwoPtRadial {
+    enum {
+        // This value is outside the range SK_FixedMin to SK_FixedMax.
+        kDontDrawT  = 0x80000000
+    };
+
+    float   fCenterX, fCenterY;
+    float   fDCenterX, fDCenterY;
+    float   fRadius;
+    float   fDRadius;
+    float   fA;
+    float   fRadius2;
+    float   fRDR;
+    bool    fFlipped;
+
+    void init(const SkPoint& center0, SkScalar rad0,
+              const SkPoint& center1, SkScalar rad1,
+              bool flipped);
+
+    static bool DontDrawT(SkFixed t) {
+        return kDontDrawT == (uint32_t)t;
+    }
+};
+
+
+class SkTwoPointConicalGradient : public SkGradientShaderBase {
+    TwoPtRadial fRec;
+public:
+    SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius,
+                              const SkPoint& end, SkScalar endRadius,
+                              bool flippedGrad, const Descriptor&);
+
+    class TwoPointConicalGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        TwoPointConicalGradientContext(const SkTwoPointConicalGradient&, const ContextRec&);
+        ~TwoPointConicalGradientContext() override {}
+
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
+
+    SkShader::GradientType asAGradient(GradientInfo* info) const  override;
+#if SK_SUPPORT_GPU
+    sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override;
+#endif
+    bool isOpaque() const override;
+
+    SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
+    SkScalar getStartRadius() const { return fRadius1; }
+    SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
+    const SkPoint& getStartCenter() const { return fCenter1; }
+    const SkPoint& getEndCenter() const { return fCenter2; }
+    SkScalar getEndRadius() const { return fRadius2; }
+    bool isFlippedGrad() const { return fFlippedGrad; }
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient)
+
+protected:
+    SkTwoPointConicalGradient(SkReadBuffer& buffer);
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
+private:
+    SkPoint fCenter1;
+    SkPoint fCenter2;
+    SkScalar fRadius1;
+    SkScalar fRadius2;
+    bool fFlippedGrad;
+
+    friend class SkGradientShader;
+    typedef SkGradientShaderBase INHERITED;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
new file mode 100644
index 0000000..8402199
--- /dev/null
+++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -0,0 +1,1341 @@
+/*
+ * 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 "SkTwoPointConicalGradient.h"
+
+#if SK_SUPPORT_GPU
+#include "GrCoordTransform.h"
+#include "GrPaint.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramDataManager.h"
+#include "glsl/GrGLSLUniformHandler.h"
+#include "SkTwoPointConicalGradient_gpu.h"
+
+// For brevity
+typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
+
+static const SkScalar kErrorTol = 0.00001f;
+static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
+
+/**
+ * We have three general cases for 2pt conical gradients. First we always assume that
+ * the start radius <= end radius. Our first case (kInside_) is when the start circle
+ * is completely enclosed by the end circle. The second case (kOutside_) is the case
+ * when the start circle is either completely outside the end circle or the circles
+ * overlap. The final case (kEdge_) is when the start circle is inside the end one,
+ * but the two are just barely touching at 1 point along their edges.
+ */
+enum ConicalType {
+    kInside_ConicalType,
+    kOutside_ConicalType,
+    kEdge_ConicalType,
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
+                                    SkMatrix* invLMatrix) {
+    // Inverse of the current local matrix is passed in then,
+    // translate to center1, rotate so center2 is on x axis.
+    const SkPoint& center1 = shader.getStartCenter();
+    const SkPoint& center2 = shader.getEndCenter();
+
+    invLMatrix->postTranslate(-center1.fX, -center1.fY);
+
+    SkPoint diff = center2 - center1;
+    SkScalar diffLen = diff.length();
+    if (0 != diffLen) {
+        SkScalar invDiffLen = SkScalarInvert(diffLen);
+        SkMatrix rot;
+        rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX);
+        invLMatrix->postConcat(rot);
+    }
+}
+
+class Edge2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLEdge2PtConicalProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
+        return sk_sp<GrFragmentProcessor>(new Edge2PtConicalEffect(args));
+    }
+
+    ~Edge2PtConicalEffect() override {}
+
+    const char* name() const override {
+        return "Two-Point Conical Gradient Edge Touching";
+    }
+
+    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
+    SkScalar center() const { return fCenterX1; }
+    SkScalar diffRadius() const { return fDiffRadius; }
+    SkScalar radius() const { return fRadius0; }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fDiffRadius == s.fDiffRadius);
+    }
+
+    Edge2PtConicalEffect(const CreateArgs& args)
+            : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) {
+        const SkTwoPointConicalGradient& shader =
+            *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
+        fCenterX1 = shader.getCenterX1();
+        fRadius0 = shader.getStartRadius();
+        fDiffRadius = shader.getDiffRadius();
+        this->initClassID<Edge2PtConicalEffect>();
+        // We should only be calling this shader if we are degenerate case with touching circles
+        // When deciding if we are in edge case, we scaled by the end radius for cases when the
+        // start radius was close to zero, otherwise we scaled by the start radius.  In addition
+        // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
+        // need the sqrt value below
+        SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
+                 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
+                                         fRadius0 * sqrt(kEdgeErrorTol)));
+
+        // We pass the linear part of the quadratic as a varying.
+        //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
+        fBTransform = this->getCoordTransform();
+        SkMatrix& bMatrix = *fBTransform.accessMatrix();
+        SkScalar r0dr = fRadius0 * fDiffRadius;
+        bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] +
+                                            r0dr * bMatrix[SkMatrix::kMPersp0]);
+        bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] +
+                                           r0dr * bMatrix[SkMatrix::kMPersp1]);
+        bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] +
+                                            r0dr * bMatrix[SkMatrix::kMPersp2]);
+        this->addCoordTransform(&fBTransform);
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    // @{
+    // Cache of values - these can change arbitrarily, EXCEPT
+    // we shouldn't change between degenerate and non-degenerate?!
+
+    GrCoordTransform fBTransform;
+    SkScalar         fCenterX1;
+    SkScalar         fRadius0;
+    SkScalar         fDiffRadius;
+
+    // @}
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLEdge2PtConicalProcessor(const GrProcessor&);
+    ~GLSLEdge2PtConicalProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedRadius;
+    SkScalar fCachedDiffRadius;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                 GrProcessorKeyBuilder* b) const {
+    Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
+    return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = d->fRandom->nextUScalar1();
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // If the circles are identical the factory will give us an empty shader.
+        // This will happen if we pick identical centers
+    } while (center1 == center2);
+
+    // Below makes sure that circle one is contained within circle two
+    // and both circles are touching on an edge
+    SkPoint diff = center2 - center1;
+    SkScalar diffLen = diff.length();
+    radius2 = radius1 + diffLen;
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedRadius(-SK_ScalarMax)
+    , fCachedDiffRadius(-SK_ScalarMax) {}
+
+void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                           kVec3f_GrSLType, kDefault_GrSLPrecision,
+                                           "Conical2FSParams");
+
+    SkString cName("c");
+    SkString tName("t");
+    SkString p0; // start radius
+    SkString p1; // start radius squared
+    SkString p2; // difference in radii (r1 - r0)
+
+
+    p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+    p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+    p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+
+    // We interpolate the linear component in coords[1].
+    SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType());
+    const char* coords2D;
+    SkString bVar;
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    if (kVec3f_GrSLType == args.fTransformedCoords[0].getType()) {
+        fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
+                                 args.fTransformedCoords[0].c_str(),
+                                 args.fTransformedCoords[0].c_str(),
+                                 args.fTransformedCoords[1].c_str(),
+                                 args.fTransformedCoords[1].c_str());
+        coords2D = "interpolants.xy";
+        bVar = "interpolants.z";
+    } else {
+        coords2D = args.fTransformedCoords[0].c_str();
+        bVar.printf("%s.x", args.fTransformedCoords[1].c_str());
+    }
+
+    // output will default to transparent black (we simply won't write anything
+    // else to it if invalid, instead of discarding or returning prematurely)
+    fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+
+    // c = (x^2)+(y^2) - params[1]
+    fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
+                           cName.c_str(), coords2D, coords2D, p1.c_str());
+
+    // linear case: t = -c/b
+    fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
+                           cName.c_str(), bVar.c_str());
+
+    // if r(t) > 0, then t will be the x coordinate
+    fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
+                           p2.c_str(), p0.c_str());
+    fragBuilder->codeAppend("\t");
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+    fragBuilder->codeAppend("\t}\n");
+}
+
+void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
+    SkScalar radius0 = data.radius();
+    SkScalar diffRadius = data.diffRadius();
+
+    if (fCachedRadius != radius0 ||
+        fCachedDiffRadius != diffRadius) {
+
+        pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius);
+        fCachedRadius = radius0;
+        fCachedDiffRadius = diffRadius;
+    }
+}
+
+void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(const GrProcessor& processor,
+                                    const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+    b->add32(GenBaseGradientKey(processor));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Focal Conical Gradients
+//////////////////////////////////////////////////////////////////////////////
+
+static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
+                                            SkMatrix* invLMatrix, SkScalar* focalX) {
+    // Inverse of the current local matrix is passed in then,
+    // translate, scale, and rotate such that endCircle is unit circle on x-axis,
+    // and focal point is at the origin.
+    ConicalType conicalType;
+    const SkPoint& focal = shader.getStartCenter();
+    const SkPoint& centerEnd = shader.getEndCenter();
+    SkScalar radius = shader.getEndRadius();
+    SkScalar invRadius = 1.f / radius;
+
+    SkMatrix matrix;
+
+    matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
+    matrix.postScale(invRadius, invRadius);
+
+    SkPoint focalTrans;
+    matrix.mapPoints(&focalTrans, &focal, 1);
+    *focalX = focalTrans.length();
+
+    if (0.f != *focalX) {
+        SkScalar invFocalX = SkScalarInvert(*focalX);
+        SkMatrix rot;
+        rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX);
+        matrix.postConcat(rot);
+    }
+
+    matrix.postTranslate(-(*focalX), 0.f);
+
+    // If the focal point is touching the edge of the circle it will
+    // cause a degenerate case that must be handled separately
+    // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
+    // stability trade off versus the linear approx used in the Edge Shader
+    if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
+        return kEdge_ConicalType;
+    }
+
+    // Scale factor 1 / (1 - focalX * focalX)
+    SkScalar oneMinusF2 = 1.f - *focalX * *focalX;
+    SkScalar s = SkScalarInvert(oneMinusF2);
+
+
+    if (s >= 0.f) {
+        conicalType = kInside_ConicalType;
+        matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
+    } else {
+        conicalType = kOutside_ConicalType;
+        matrix.postScale(s, s);
+    }
+
+    invLMatrix->postConcat(matrix);
+
+    return conicalType;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class FocalOutside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLFocalOutside2PtConicalProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
+        return sk_sp<GrFragmentProcessor>(
+            new FocalOutside2PtConicalEffect(args, focalX));
+    }
+
+    ~FocalOutside2PtConicalEffect() override {}
+
+    const char* name() const override {
+        return "Two-Point Conical Gradient Focal Outside";
+    }
+
+    bool isFlipped() const { return fIsFlipped; }
+    SkScalar focal() const { return fFocalX; }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fFocalX == s.fFocalX &&
+                this->fIsFlipped == s.fIsFlipped);
+    }
+
+    static bool IsFlipped(const CreateArgs& args) {
+        // eww.
+        return static_cast<const SkTwoPointConicalGradient*>(args.fShader)->isFlippedGrad();
+    }
+
+    FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
+            : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
+            , fFocalX(focalX)
+            , fIsFlipped(IsFlipped(args)) {
+        this->initClassID<FocalOutside2PtConicalEffect>();
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    SkScalar         fFocalX;
+    bool             fIsFlipped;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLFocalOutside2PtConicalProcessor(const GrProcessor&);
+    ~GLSLFocalOutside2PtConicalProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsFlipped;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedFocal;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                         GrProcessorKeyBuilder* b) const {
+    FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = 0.f;
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // Need to make sure the centers are not the same or else focal point will be inside
+    } while (center1 == center2);
+
+    SkPoint diff = center2 - center1;
+    SkScalar diffLen = diff.length();
+    // Below makes sure that the focal point is not contained within circle two
+    radius2 = d->fRandom->nextRangeF(0.f, diffLen);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
+                            ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedFocal(SK_ScalarMax) {
+    const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
+    fIsFlipped = data.isFlipped();
+}
+
+void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                           kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                           "Conical2FSParams");
+    SkString tName("t");
+    SkString p0; // focalX
+    SkString p1; // 1 - focalX * focalX
+
+    p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+    p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
+
+    // output will default to transparent black (we simply won't write anything
+    // else to it if invalid, instead of discarding or returning prematurely)
+    fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+
+    fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
+
+    // Must check to see if we flipped the circle order (to make sure start radius < end radius)
+    // If so we must also flip sign on sqrt
+    if (!fIsFlipped) {
+        fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  + sqrt(d);\n", tName.c_str(),
+                                 coords2D, p0.c_str());
+    } else {
+        fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  - sqrt(d);\n", tName.c_str(),
+                                 coords2D, p0.c_str());
+    }
+
+    fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
+    fragBuilder->codeAppend("\t\t");
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+    fragBuilder->codeAppend("\t}\n");
+}
+
+void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
+    SkASSERT(data.isFlipped() == fIsFlipped);
+    SkScalar focal = data.focal();
+
+    if (fCachedFocal != focal) {
+        SkScalar oneMinus2F = 1.f - focal * focal;
+
+        pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F));
+        fCachedFocal = focal;
+    }
+}
+
+void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(
+                                            const GrProcessor& processor,
+                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+    uint32_t* key = b->add32n(2);
+    key[0] = GenBaseGradientKey(processor);
+    key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class FocalInside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLFocalInside2PtConicalProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
+        return sk_sp<GrFragmentProcessor>(
+            new FocalInside2PtConicalEffect(args, focalX));
+    }
+
+    ~FocalInside2PtConicalEffect() override {}
+
+    const char* name() const override {
+        return "Two-Point Conical Gradient Focal Inside";
+    }
+
+    SkScalar focal() const { return fFocalX; }
+
+    typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor;
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fFocalX == s.fFocalX);
+    }
+
+    FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
+            : INHERITED(args, args.fShader->colorsAreOpaque()), fFocalX(focalX) {
+        this->initClassID<FocalInside2PtConicalEffect>();
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    SkScalar         fFocalX;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor 
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLFocalInside2PtConicalProcessor(const GrProcessor&);
+    ~GLSLFocalInside2PtConicalProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fFocalUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedFocal;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                        GrProcessorKeyBuilder* b) const {
+    FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = 0.f;
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // Below makes sure radius2 is larger enouch such that the focal point
+        // is inside the end circle
+        SkScalar increase = d->fRandom->nextUScalar1();
+        SkPoint diff = center2 - center1;
+        SkScalar diffLen = diff.length();
+        radius2 = diffLen + increase;
+        // If the circles are identical the factory will give us an empty shader.
+    } while (radius1 == radius2 && center1 == center2);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
+                           ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedFocal(SK_ScalarMax) {}
+
+void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                           kFloat_GrSLType, kDefault_GrSLPrecision,
+                                           "Conical2FSParams");
+    SkString tName("t");
+
+    // this is the distance along x-axis from the end center to focal point in
+    // transformed coordinates
+    GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // t = p.x * focalX + length(p)
+    fragBuilder->codeAppendf("\tfloat %s = %s.x * %s  + length(%s);\n", tName.c_str(),
+                             coords2D, focal.c_str(), coords2D);
+
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
+    SkScalar focal = data.focal();
+
+    if (fCachedFocal != focal) {
+        pdman.set1f(fFocalUni, SkScalarToFloat(focal));
+        fCachedFocal = focal;
+    }
+}
+
+void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(
+                                            const GrProcessor& processor,
+                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+    b->add32(GenBaseGradientKey(processor));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Conical Gradients
+//////////////////////////////////////////////////////////////////////////////
+
+struct CircleConicalInfo {
+    SkPoint fCenterEnd;
+    SkScalar fA;
+    SkScalar fB;
+    SkScalar fC;
+};
+
+// Returns focal distance along x-axis in transformed coords
+static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
+                                             SkMatrix* invLMatrix, CircleConicalInfo* info) {
+    // Inverse of the current local matrix is passed in then,
+    // translate and scale such that start circle is on the origin and has radius 1
+    const SkPoint& centerStart = shader.getStartCenter();
+    const SkPoint& centerEnd = shader.getEndCenter();
+    SkScalar radiusStart = shader.getStartRadius();
+    SkScalar radiusEnd = shader.getEndRadius();
+
+    SkMatrix matrix;
+
+    matrix.setTranslate(-centerStart.fX, -centerStart.fY);
+
+    SkScalar invStartRad = 1.f / radiusStart;
+    matrix.postScale(invStartRad, invStartRad);
+
+    radiusEnd /= radiusStart;
+
+    SkPoint centerEndTrans;
+    matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
+
+    SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
+                 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
+
+    // Check to see if start circle is inside end circle with edges touching.
+    // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
+    // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
+    // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
+    // still accurate.
+    if (SkScalarAbs(A) < kEdgeErrorTol) {
+        return kEdge_ConicalType;
+    }
+
+    SkScalar C = 1.f / A;
+    SkScalar B = (radiusEnd - 1.f) * C;
+
+    matrix.postScale(C, C);
+
+    invLMatrix->postConcat(matrix);
+
+    info->fCenterEnd = centerEndTrans;
+    info->fA = A;
+    info->fB = B;
+    info->fC = C;
+
+    // if A ends up being negative, the start circle is contained completely inside the end cirlce
+    if (A < 0.f) {
+        return kInside_ConicalType;
+    }
+    return kOutside_ConicalType;
+}
+
+class CircleInside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLCircleInside2PtConicalProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
+        return sk_sp<GrFragmentProcessor>(
+            new CircleInside2PtConicalEffect(args, info));
+    }
+
+    ~CircleInside2PtConicalEffect() override {}
+
+    const char* name() const override { return "Two-Point Conical Gradient Inside"; }
+
+    SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
+    SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
+    SkScalar A() const { return fInfo.fA; }
+    SkScalar B() const { return fInfo.fB; }
+    SkScalar C() const { return fInfo.fC; }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                       GrProcessorKeyBuilder* b) const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
+                this->fInfo.fA == s.fInfo.fA &&
+                this->fInfo.fB == s.fInfo.fB &&
+                this->fInfo.fC == s.fInfo.fC);
+    }
+
+    CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
+            : INHERITED(args, args.fShader->colorsAreOpaque()), fInfo(info) {
+        this->initClassID<CircleInside2PtConicalEffect>();
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    const CircleConicalInfo fInfo;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor 
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLCircleInside2PtConicalProcessor(const GrProcessor&);
+    ~GLSLCircleInside2PtConicalProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fCenterUni;
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedCenterX;
+    SkScalar fCachedCenterY;
+    SkScalar fCachedA;
+    SkScalar fCachedB;
+    SkScalar fCachedC;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                         GrProcessorKeyBuilder* b) const {
+    CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // Below makes sure that circle one is contained within circle two
+        SkScalar increase = d->fRandom->nextUScalar1();
+        SkPoint diff = center2 - center1;
+        SkScalar diffLen = diff.length();
+        radius2 = radius1 + diffLen + increase;
+        // If the circles are identical the factory will give us an empty shader.
+    } while (radius1 == radius2 && center1 == center2);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
+                            ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedCenterX(SK_ScalarMax)
+    , fCachedCenterY(SK_ScalarMax)
+    , fCachedA(SK_ScalarMax)
+    , fCachedB(SK_ScalarMax)
+    , fCachedC(SK_ScalarMax) {}
+
+void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                            "Conical2FSCenter");
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                           kVec3f_GrSLType, kDefault_GrSLPrecision,
+                                           "Conical2FSParams");
+    SkString tName("t");
+
+    GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
+    // params.x = A
+    // params.y = B
+    // params.z = C
+    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // p = coords2D
+    // e = center end
+    // r = radius end
+    // A = dot(e, e) - r^2 + 2 * r - 1
+    // B = (r -1) / A
+    // C = 1 / A
+    // d = dot(e, p) + B
+    // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
+    fragBuilder->codeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
+                             params.c_str());
+    fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
+                             tName.c_str(), params.c_str(), params.c_str());
+
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+}
+
+void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
+    SkScalar centerX = data.centerX();
+    SkScalar centerY = data.centerY();
+    SkScalar A = data.A();
+    SkScalar B = data.B();
+    SkScalar C = data.C();
+
+    if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
+        fCachedA != A || fCachedB != B || fCachedC != C) {
+
+        pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
+        pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
+
+        fCachedCenterX = centerX;
+        fCachedCenterY = centerY;
+        fCachedA = A;
+        fCachedB = B;
+        fCachedC = C;
+    }
+}
+
+void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(
+                                            const GrProcessor& processor,
+                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+    b->add32(GenBaseGradientKey(processor));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CircleOutside2PtConicalEffect : public GrGradientEffect {
+public:
+    class GLSLCircleOutside2PtConicalProcessor;
+
+    static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
+        return sk_sp<GrFragmentProcessor>(
+            new CircleOutside2PtConicalEffect(args, info));
+    }
+
+    ~CircleOutside2PtConicalEffect() override {}
+
+    const char* name() const override { return "Two-Point Conical Gradient Outside"; }
+
+    SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
+    SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
+    SkScalar A() const { return fInfo.fA; }
+    SkScalar B() const { return fInfo.fB; }
+    SkScalar C() const { return fInfo.fC; }
+    SkScalar tLimit() const { return fTLimit; }
+    bool isFlipped() const { return fIsFlipped; }
+
+private:
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+        const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
+                this->fInfo.fA == s.fInfo.fA &&
+                this->fInfo.fB == s.fInfo.fB &&
+                this->fInfo.fC == s.fInfo.fC &&
+                this->fTLimit == s.fTLimit &&
+                this->fIsFlipped == s.fIsFlipped);
+    }
+
+    CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
+            : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
+            , fInfo(info) {
+        this->initClassID<CircleOutside2PtConicalEffect>();
+        const SkTwoPointConicalGradient& shader =
+            *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
+        if (shader.getStartRadius() != shader.getEndRadius()) {
+            fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
+        } else {
+            fTLimit = SK_ScalarMin;
+        }
+
+        fIsFlipped = shader.isFlippedGrad();
+    }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    const CircleConicalInfo fInfo;
+    SkScalar fTLimit;
+    bool fIsFlipped;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
+    : public GrGradientEffect::GLSLProcessor {
+public:
+    GLSLCircleOutside2PtConicalProcessor(const GrProcessor&);
+    ~GLSLCircleOutside2PtConicalProcessor() override {}
+
+    virtual void emitCode(EmitArgs&) override;
+
+    static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
+
+protected:
+    void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+    UniformHandle fCenterUni;
+    UniformHandle fParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsFlipped;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedCenterX;
+    SkScalar fCachedCenterY;
+    SkScalar fCachedA;
+    SkScalar fCachedB;
+    SkScalar fCachedC;
+    SkScalar fCachedTLimit;
+
+    // @}
+
+private:
+    typedef GrGradientEffect::GLSLProcessor INHERITED;
+
+};
+
+void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                          GrProcessorKeyBuilder* b) const {
+    CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(*this, caps, b);
+}
+
+GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
+    return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this);
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
+
+/*
+ * All Two point conical gradient test create functions may occasionally create edge case shaders
+ */
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
+    SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+    SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
+    SkPoint center2;
+    SkScalar radius2;
+    SkScalar diffLen;
+    do {
+        center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
+        // If the circles share a center than we can't be in the outside case
+    } while (center1 == center2);
+    SkPoint diff = center2 - center1;
+    diffLen = diff.length();
+    // Below makes sure that circle one is not contained within circle two
+    // and have radius2 >= radius to match sorting on cpu side
+    radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
+
+    RandomGradientParams params(d->fRandom);
+    auto shader = params.fUseColors4f ?
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors4f, params.fColorSpace, params.fStops,
+                                              params.fColorCount, params.fTileMode) :
+        SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
+                                              params.fColors, params.fStops,
+                                              params.fColorCount, params.fTileMode);
+    GrTest::TestAsFPArgs asFPArgs(d);
+    sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
+    GrAlwaysAssert(fp);
+    return fp;
+}
+#endif
+
+CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
+                             ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor)
+    : fVSVaryingName(nullptr)
+    , fFSVaryingName(nullptr)
+    , fCachedCenterX(SK_ScalarMax)
+    , fCachedCenterY(SK_ScalarMax)
+    , fCachedA(SK_ScalarMax)
+    , fCachedB(SK_ScalarMax)
+    , fCachedC(SK_ScalarMax)
+    , fCachedTLimit(SK_ScalarMax) {
+    const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
+    fIsFlipped = data.isFlipped();
+    }
+
+void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
+    const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
+    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+    this->emitUniforms(uniformHandler, ge);
+    fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                            "Conical2FSCenter");
+    fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+                                           kVec4f_GrSLType, kDefault_GrSLPrecision,
+                                           "Conical2FSParams");
+    SkString tName("t");
+
+    GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
+    // params.x = A
+    // params.y = B
+    // params.z = C
+    GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
+
+    // if we have a vec3 from being in perspective, convert it to a vec2 first
+    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+    SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+    const char* coords2D = coords2DString.c_str();
+
+    // output will default to transparent black (we simply won't write anything
+    // else to it if invalid, instead of discarding or returning prematurely)
+    fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+
+    // p = coords2D
+    // e = center end
+    // r = radius end
+    // A = dot(e, e) - r^2 + 2 * r - 1
+    // B = (r -1) / A
+    // C = 1 / A
+    // d = dot(e, p) + B
+    // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
+
+    fragBuilder->codeAppendf("\tfloat pDotp = dot(%s,  %s);\n", coords2D, coords2D);
+    fragBuilder->codeAppendf("\tfloat d = dot(%s,  %s) + %s.y;\n", coords2D, center.c_str(),
+                             params.c_str());
+    fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
+                             params.c_str());
+
+    // Must check to see if we flipped the circle order (to make sure start radius < end radius)
+    // If so we must also flip sign on sqrt
+    if (!fIsFlipped) {
+        fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
+    } else {
+        fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
+    }
+
+    fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n",
+                             tName.c_str(), params.c_str());
+    fragBuilder->codeAppend("\t\t");
+    this->emitColor(fragBuilder,
+                    uniformHandler,
+                    args.fShaderCaps,
+                    ge,
+                    tName.c_str(),
+                    args.fOutputColor,
+                    args.fInputColor,
+                    args.fTexSamplers);
+    fragBuilder->codeAppend("\t}\n");
+}
+
+void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData(
+        const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
+    INHERITED::onSetData(pdman, processor);
+    const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
+    SkASSERT(data.isFlipped() == fIsFlipped);
+    SkScalar centerX = data.centerX();
+    SkScalar centerY = data.centerY();
+    SkScalar A = data.A();
+    SkScalar B = data.B();
+    SkScalar C = data.C();
+    SkScalar tLimit = data.tLimit();
+
+    if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
+        fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
+
+        pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
+        pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
+                   SkScalarToFloat(tLimit));
+
+        fCachedCenterX = centerX;
+        fCachedCenterY = centerY;
+        fCachedA = A;
+        fCachedB = B;
+        fCachedC = C;
+        fCachedTLimit = tLimit;
+    }
+}
+
+void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(
+                                            const GrProcessor& processor,
+                                            const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+    uint32_t* key = b->add32n(2);
+    key[0] = GenBaseGradientKey(processor);
+    key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
+                                                         const GrGradientEffect::CreateArgs& args) {
+    const SkTwoPointConicalGradient& shader =
+        *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
+
+    SkMatrix matrix;
+    if (!shader.getLocalMatrix().invert(&matrix)) {
+        return nullptr;
+    }
+    if (args.fMatrix) {
+        SkMatrix inv;
+        if (!args.fMatrix->invert(&inv)) {
+            return nullptr;
+        }
+        matrix.postConcat(inv);
+    }
+
+    GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode,
+                                         std::move(args.fColorSpaceXform), args.fGammaCorrect);
+
+    if (shader.getStartRadius() < kErrorTol) {
+        SkScalar focalX;
+        ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
+        if (type == kInside_ConicalType) {
+            return FocalInside2PtConicalEffect::Make(newArgs, focalX);
+        } else if(type == kEdge_ConicalType) {
+            set_matrix_edge_conical(shader, &matrix);
+            return Edge2PtConicalEffect::Make(newArgs);
+        } else {
+            return FocalOutside2PtConicalEffect::Make(newArgs, focalX);
+        }
+    }
+
+    CircleConicalInfo info;
+    ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
+
+    if (type == kInside_ConicalType) {
+        return CircleInside2PtConicalEffect::Make(newArgs, info);
+    } else if (type == kEdge_ConicalType) {
+        set_matrix_edge_conical(shader, &matrix);
+        return Edge2PtConicalEffect::Make(newArgs);
+    } else {
+        return CircleOutside2PtConicalEffect::Make(newArgs, info);
+    }
+}
+
+#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.h b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.h
similarity index 100%
rename from src/effects/gradients/SkTwoPointConicalGradient_gpu.h
rename to src/shaders/gradients/SkTwoPointConicalGradient_gpu.h
diff --git a/src/sksl/README b/src/sksl/README
index 25b07c1..a16dd80 100644
--- a/src/sksl/README
+++ b/src/sksl/README
@@ -1,7 +1,7 @@
 Overview
 ========
 
-SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's 
+SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's
 internal shading language. SkSL is, at its heart, a single standardized version
 of GLSL which avoids all of the various version and dialect differences found
 in GLSL "in the wild", but it does bring a few of its own changes to the table.
@@ -15,9 +15,12 @@
 SkSL is based on GLSL 4.5. For the most part, write SkSL exactly as you would
 desktop GLSL, and the SkSL compiler will take care of version and dialect
 differences (for instance, you always use "in" and "out", and skslc will handle
-translating them to "varying" and "attribute" as appropriate). Be aware of the 
+translating them to "varying" and "attribute" as appropriate). Be aware of the
 following differences between SkSL and GLSL:
 
+* "@if" and "@switch" are static versions of if and switch. They behave exactly
+  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,
   as appropriate. As SkSL supports constant folding and branch elimination, this
@@ -36,11 +39,11 @@
 * use sk_VertexID instead of gl_VertexID
 * the fragment coordinate is sk_FragCoord, and is always relative to the upper
   left.
-* lowp, mediump, and highp are always permitted (but will only be respected if 
+* lowp, mediump, and highp are always permitted (but will only be respected if
   you run on a device which supports them)
 * you do not need to include ".0" to make a number a float (meaning that
   "vec2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would often
-  have to be expressed "vec2(x, y) * 4.0". There is no performance penalty for 
+  have to be expressed "vec2(x, y) * 4.0". There is no performance penalty for
   this, as the number is converted to a float at compile time)
 * type suffixes on numbers (1.0f, 0xFFu) are both unnecessary and unsupported
 * creating a smaller vector from a larger vector (e.g. vec2(vec3(1))) is
diff --git a/src/sksl/SkSLCFGGenerator.cpp b/src/sksl/SkSLCFGGenerator.cpp
index 28533df..2fe049d 100644
--- a/src/sksl/SkSLCFGGenerator.cpp
+++ b/src/sksl/SkSLCFGGenerator.cpp
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #include "SkSLCFGGenerator.h"
 
 #include "ir/SkSLConstructor.h"
@@ -56,7 +56,7 @@
         const char* separator = "";
         for (auto iter = fBlocks[i].fBefore.begin(); iter != fBlocks[i].fBefore.end(); iter++) {
             printf("%s%s = %s", separator, iter->first->description().c_str(),
-                   *iter->second ? (*iter->second)->description().c_str() : "<undefined>");
+                   iter->second ? (*iter->second)->description().c_str() : "<undefined>");
             separator = ", ";
         }
         printf("\nEntrances: ");
@@ -68,9 +68,9 @@
         printf("\n");
         for (size_t j = 0; j < fBlocks[i].fNodes.size(); j++) {
             BasicBlock::Node& n = fBlocks[i].fNodes[j];
-            printf("Node %d: %s\n", (int) j, n.fKind == BasicBlock::Node::kExpression_Kind
-                                                           ? (*n.fExpression)->description().c_str()
-                                                           : n.fStatement->description().c_str());
+            printf("Node %d (%p): %s\n", (int) j, &n, n.fKind == BasicBlock::Node::kExpression_Kind
+                                                         ? (*n.expression())->description().c_str()
+                                                         : (*n.statement())->description().c_str());
         }
         printf("Exits: ");
         separator = "";
@@ -82,6 +82,205 @@
     }
 }
 
+bool BasicBlock::tryRemoveExpressionBefore(std::vector<BasicBlock::Node>::iterator* iter,
+                                           Expression* e) {
+    if (e->fKind == Expression::kTernary_Kind) {
+        return false;
+    }
+    bool result;
+    if ((*iter)->fKind == BasicBlock::Node::kExpression_Kind) {
+        ASSERT((*iter)->expression()->get() != e);
+        Expression* old = (*iter)->expression()->get();
+        do {
+            if ((*iter) == fNodes.begin()) {
+                return false;
+            }
+            --(*iter);
+        } while ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
+                 (*iter)->expression()->get() != e);
+        result = this->tryRemoveExpression(iter);
+        while ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
+               (*iter)->expression()->get() != old) {
+            ASSERT(*iter != fNodes.end());
+            ++(*iter);
+        }
+    } else {
+        Statement* old = (*iter)->statement()->get();
+        do {
+            if ((*iter) == fNodes.begin()) {
+                return false;
+            }
+            --(*iter);
+        } while ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
+                 (*iter)->expression()->get() != e);
+        result = this->tryRemoveExpression(iter);
+        while ((*iter)->fKind != BasicBlock::Node::kStatement_Kind ||
+               (*iter)->statement()->get() != old) {
+            ASSERT(*iter != fNodes.end());
+            ++(*iter);
+        }
+    }
+    return result;
+}
+
+bool BasicBlock::tryRemoveLValueBefore(std::vector<BasicBlock::Node>::iterator* iter,
+                                       Expression* lvalue) {
+    switch (lvalue->fKind) {
+        case Expression::kVariableReference_Kind:
+            return true;
+        case Expression::kSwizzle_Kind:
+            return this->tryRemoveLValueBefore(iter, ((Swizzle*) lvalue)->fBase.get());
+        case Expression::kFieldAccess_Kind:
+            return this->tryRemoveLValueBefore(iter, ((FieldAccess*) lvalue)->fBase.get());
+        case Expression::kIndex_Kind:
+            if (!this->tryRemoveLValueBefore(iter, ((IndexExpression*) lvalue)->fBase.get())) {
+                return false;
+            }
+            return this->tryRemoveExpressionBefore(iter, ((IndexExpression*) lvalue)->fIndex.get());
+        default:
+            ABORT("invalid lvalue: %s\n", lvalue->description().c_str());
+    }
+}
+
+bool BasicBlock::tryRemoveExpression(std::vector<BasicBlock::Node>::iterator* iter) {
+    Expression* expr = (*iter)->expression()->get();
+    switch (expr->fKind) {
+        case Expression::kBinary_Kind: {
+            BinaryExpression* b = (BinaryExpression*) expr;
+            if (b->fOperator == Token::EQ) {
+                if (!this->tryRemoveLValueBefore(iter, b->fLeft.get())) {
+                    return false;
+                }
+            } else if (!this->tryRemoveExpressionBefore(iter, b->fLeft.get())) {
+                return false;
+            }
+            if (!this->tryRemoveExpressionBefore(iter, b->fRight.get())) {
+                return false;
+            }
+            ASSERT((*iter)->expression()->get() == expr);
+            *iter = fNodes.erase(*iter);
+            return true;
+        }
+        case Expression::kTernary_Kind: {
+            // ternaries cross basic block boundaries, must regenerate the CFG to remove it
+            return false;
+        }
+        case Expression::kFieldAccess_Kind: {
+            FieldAccess* f = (FieldAccess*) expr;
+            if (!this->tryRemoveExpressionBefore(iter, f->fBase.get())) {
+                return false;
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        }
+        case Expression::kSwizzle_Kind: {
+            Swizzle* s = (Swizzle*) expr;
+            if (!this->tryRemoveExpressionBefore(iter, s->fBase.get())) {
+                return false;
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        }
+        case Expression::kIndex_Kind: {
+            IndexExpression* idx = (IndexExpression*) expr;
+            if (!this->tryRemoveExpressionBefore(iter, idx->fBase.get())) {
+                return false;
+            }
+            if (!this->tryRemoveExpressionBefore(iter, idx->fIndex.get())) {
+                return false;
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        }
+        case Expression::kConstructor_Kind: {
+            Constructor* c = (Constructor*) expr;
+            for (auto& arg : c->fArguments) {
+                if (!this->tryRemoveExpressionBefore(iter, arg.get())) {
+                    return false;
+                }
+                ASSERT((*iter)->expression()->get() == expr);
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        }
+        case Expression::kFunctionCall_Kind: {
+            FunctionCall* f = (FunctionCall*) expr;
+            for (auto& arg : f->fArguments) {
+                if (!this->tryRemoveExpressionBefore(iter, arg.get())) {
+                    return false;
+                }
+                ASSERT((*iter)->expression()->get() == expr);
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        }
+        case Expression::kPrefix_Kind:
+            if (!this->tryRemoveExpressionBefore(iter,
+                                                 ((PrefixExpression*) expr)->fOperand.get())) {
+                return false;
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        case Expression::kPostfix_Kind:
+            if (!this->tryRemoveExpressionBefore(iter,
+                                                 ((PrefixExpression*) expr)->fOperand.get())) {
+                return false;
+            }
+            *iter = fNodes.erase(*iter);
+            return true;
+        case Expression::kBoolLiteral_Kind:  // fall through
+        case Expression::kFloatLiteral_Kind: // fall through
+        case Expression::kIntLiteral_Kind:   // fall through
+        case Expression::kVariableReference_Kind:
+            *iter = fNodes.erase(*iter);
+            return true;
+        default:
+            ABORT("unhandled expression: %s\n", expr->description().c_str());
+    }
+}
+
+bool BasicBlock::tryInsertExpression(std::vector<BasicBlock::Node>::iterator* iter,
+                                     std::unique_ptr<Expression>* expr) {
+    switch ((*expr)->fKind) {
+        case Expression::kBinary_Kind: {
+            BinaryExpression* b = (BinaryExpression*) expr->get();
+            if (!this->tryInsertExpression(iter, &b->fRight)) {
+                return false;
+            }
+            ++(*iter);
+            if (!this->tryInsertExpression(iter, &b->fLeft)) {
+                return false;
+            }
+            ++(*iter);
+            BasicBlock::Node node = { BasicBlock::Node::kExpression_Kind, true, expr, nullptr };
+            *iter = fNodes.insert(*iter, node);
+            return true;
+        }
+        case Expression::kBoolLiteral_Kind:  // fall through
+        case Expression::kFloatLiteral_Kind: // fall through
+        case Expression::kIntLiteral_Kind:   // fall through
+        case Expression::kVariableReference_Kind: {
+            BasicBlock::Node node = { BasicBlock::Node::kExpression_Kind, true, expr, nullptr };
+            *iter = fNodes.insert(*iter, node);
+            return true;
+        }
+        case Expression::kConstructor_Kind: {
+            Constructor* c = (Constructor*) expr->get();
+            for (auto& arg : c->fArguments) {
+                if (!this->tryInsertExpression(iter, &arg)) {
+                    return false;
+                }
+                ++(*iter);
+            }
+            BasicBlock::Node node = { BasicBlock::Node::kExpression_Kind, true, expr, nullptr };
+            *iter = fNodes.insert(*iter, node);
+            return true;
+        }
+        default:
+            return false;
+    }
+}
+
 void CFGGenerator::addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool constantPropagate) {
     ASSERT(e);
     switch ((*e)->fKind) {
@@ -99,6 +298,12 @@
                     this->addExpression(cfg, &b->fRight, constantPropagate);
                     cfg.newBlock();
                     cfg.addExit(start, cfg.fCurrent);
+                    cfg.fBlocks[cfg.fCurrent].fNodes.push_back({
+                        BasicBlock::Node::kExpression_Kind,
+                        constantPropagate,
+                        e,
+                        nullptr
+                    });
                     break;
                 }
                 case Token::EQ: {
@@ -182,6 +387,8 @@
         case Expression::kTernary_Kind: {
             TernaryExpression* t = (TernaryExpression*) e->get();
             this->addExpression(cfg, &t->fTest, constantPropagate);
+            cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
+                                                         constantPropagate, e, nullptr });
             BlockId start = cfg.fCurrent;
             cfg.newBlock();
             this->addExpression(cfg, &t->fIfTrue, constantPropagate);
@@ -223,24 +430,26 @@
     }
 }
 
-void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
-    switch (s->fKind) {
+void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) {
+    switch ((*s)->fKind) {
         case Statement::kBlock_Kind:
-            for (const auto& child : ((const Block*) s)->fStatements) {
-                addStatement(cfg, child.get());
+            for (auto& child : ((Block&) **s).fStatements) {
+                addStatement(cfg, &child);
             }
             break;
         case Statement::kIf_Kind: {
-            IfStatement* ifs = (IfStatement*) s;
-            this->addExpression(cfg, &ifs->fTest, true);
+            IfStatement& ifs = (IfStatement&) **s;
+            this->addExpression(cfg, &ifs.fTest, true);
+            cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
+                                                         nullptr, s });
             BlockId start = cfg.fCurrent;
             cfg.newBlock();
-            this->addStatement(cfg, ifs->fIfTrue.get());
+            this->addStatement(cfg, &ifs.fIfTrue);
             BlockId next = cfg.newBlock();
-            if (ifs->fIfFalse) {
+            if (ifs.fIfFalse) {
                 cfg.fCurrent = start;
                 cfg.newBlock();
-                this->addStatement(cfg, ifs->fIfFalse.get());
+                this->addStatement(cfg, &ifs.fIfFalse);
                 cfg.addExit(cfg.fCurrent, next);
                 cfg.fCurrent = next;
             } else {
@@ -249,18 +458,24 @@
             break;
         }
         case Statement::kExpression_Kind: {
-            this->addExpression(cfg, &((ExpressionStatement&) *s).fExpression, true);
+            this->addExpression(cfg, &((ExpressionStatement&) **s).fExpression, true);
+            cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
+                                                         nullptr, s });
             break;
         }
         case Statement::kVarDeclarations_Kind: {
-            VarDeclarationsStatement& decls = ((VarDeclarationsStatement&) *s);
-            for (auto& vd : decls.fDeclaration->fVars) {
+            VarDeclarationsStatement& decls = ((VarDeclarationsStatement&) **s);
+            for (auto& stmt : decls.fDeclaration->fVars) {
+                if (stmt->fKind == Statement::kNop_Kind) {
+                    continue;
+                }
+                VarDeclaration& vd = (VarDeclaration&) *stmt;
                 if (vd.fValue) {
                     this->addExpression(cfg, &vd.fValue, true);
                 }
+                cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind,
+                                                             false, nullptr, &stmt });
             }
-            cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
-                                                         nullptr, s });
             break;
         }
         case Statement::kDiscard_Kind:
@@ -269,7 +484,7 @@
             cfg.fCurrent = cfg.newIsolatedBlock();
             break;
         case Statement::kReturn_Kind: {
-            ReturnStatement& r = ((ReturnStatement&) *s);
+            ReturnStatement& r = ((ReturnStatement&) **s);
             if (r.fExpression) {
                 this->addExpression(cfg, &r.fExpression, true);
             }
@@ -291,16 +506,16 @@
             cfg.fCurrent = cfg.newIsolatedBlock();
             break;
         case Statement::kWhile_Kind: {
-            WhileStatement* w = (WhileStatement*) s;
+            WhileStatement& w = (WhileStatement&) **s;
             BlockId loopStart = cfg.newBlock();
             fLoopContinues.push(loopStart);
             BlockId loopExit = cfg.newIsolatedBlock();
             fLoopExits.push(loopExit);
-            this->addExpression(cfg, &w->fTest, true);
+            this->addExpression(cfg, &w.fTest, true);
             BlockId test = cfg.fCurrent;
             cfg.addExit(test, loopExit);
             cfg.newBlock();
-            this->addStatement(cfg, w->fStatement.get());
+            this->addStatement(cfg, &w.fStatement);
             cfg.addExit(cfg.fCurrent, loopStart);
             fLoopContinues.pop();
             fLoopExits.pop();
@@ -308,13 +523,13 @@
             break;
         }
         case Statement::kDo_Kind: {
-            DoStatement* d = (DoStatement*) s;
+            DoStatement& d = (DoStatement&) **s;
             BlockId loopStart = cfg.newBlock();
             fLoopContinues.push(loopStart);
             BlockId loopExit = cfg.newIsolatedBlock();
             fLoopExits.push(loopExit);
-            this->addStatement(cfg, d->fStatement.get());
-            this->addExpression(cfg, &d->fTest, true);
+            this->addStatement(cfg, &d.fStatement);
+            this->addExpression(cfg, &d.fTest, true);
             cfg.addExit(cfg.fCurrent, loopExit);
             cfg.addExit(cfg.fCurrent, loopStart);
             fLoopContinues.pop();
@@ -323,40 +538,48 @@
             break;
         }
         case Statement::kFor_Kind: {
-            ForStatement* f = (ForStatement*) s;
-            if (f->fInitializer) {
-                this->addStatement(cfg, f->fInitializer.get());
+            ForStatement& f = (ForStatement&) **s;
+            if (f.fInitializer) {
+                this->addStatement(cfg, &f.fInitializer);
             }
             BlockId loopStart = cfg.newBlock();
             BlockId next = cfg.newIsolatedBlock();
             fLoopContinues.push(next);
             BlockId loopExit = cfg.newIsolatedBlock();
             fLoopExits.push(loopExit);
-            if (f->fTest) {
-                this->addExpression(cfg, &f->fTest, true);
-                BlockId test = cfg.fCurrent;
-                cfg.addExit(test, loopExit);
+            if (f.fTest) {
+                this->addExpression(cfg, &f.fTest, true);
+                // this isn't quite right; we should have an exit from here to the loop exit, and
+                // remove the exit from the loop body to the loop exit. Structuring it like this
+                // forces the optimizer to believe that the loop body is always executed at least
+                // once. While not strictly correct, this avoids incorrect "variable not assigned"
+                // errors on variables which are assigned within the loop. The correct solution to
+                // this is to analyze the loop to see whether or not at least one iteration is
+                // guaranteed to happen, but for the time being we take the easy way out.
             }
             cfg.newBlock();
-            this->addStatement(cfg, f->fStatement.get());
+            this->addStatement(cfg, &f.fStatement);
             cfg.addExit(cfg.fCurrent, next);
             cfg.fCurrent = next;
-            if (f->fNext) {
-                this->addExpression(cfg, &f->fNext, true);
+            if (f.fNext) {
+                this->addExpression(cfg, &f.fNext, true);
             }
             cfg.addExit(cfg.fCurrent, loopStart);
+            cfg.addExit(cfg.fCurrent, loopExit);
             fLoopContinues.pop();
             fLoopExits.pop();
             cfg.fCurrent = loopExit;
             break;
         }
         case Statement::kSwitch_Kind: {
-            SwitchStatement* ss = (SwitchStatement*) s;
-            this->addExpression(cfg, &ss->fValue, true);
+            SwitchStatement& ss = (SwitchStatement&) **s;
+            this->addExpression(cfg, &ss.fValue, true);
+            cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
+                                                         nullptr, s });
             BlockId start = cfg.fCurrent;
             BlockId switchExit = cfg.newIsolatedBlock();
             fLoopExits.push(switchExit);
-            for (const auto& c : ss->fCases) {
+            for (const auto& c : ss.fCases) {
                 cfg.newBlock();
                 cfg.addExit(start, cfg.fCurrent);
                 if (c->fValue) {
@@ -364,13 +587,13 @@
                     // because it must be constant. Not worth running two loops for.
                     this->addExpression(cfg, &c->fValue, true);
                 }
-                for (const auto& caseStatement : c->fStatements) {
-                    this->addStatement(cfg, caseStatement.get());
+                for (auto& caseStatement : c->fStatements) {
+                    this->addStatement(cfg, &caseStatement);
                 }
             }
             cfg.addExit(cfg.fCurrent, switchExit);
             // note that unlike GLSL, our grammar requires the default case to be last
-            if (0 == ss->fCases.size() || ss->fCases[ss->fCases.size() - 1]->fValue) {
+            if (0 == ss.fCases.size() || ss.fCases[ss.fCases.size() - 1]->fValue) {
                 // switch does not have a default clause, mark that it can skip straight to the end
                 cfg.addExit(start, switchExit);
             }
@@ -378,17 +601,19 @@
             cfg.fCurrent = switchExit;
             break;
         }
+        case Statement::kNop_Kind:
+            break;
         default:
-            printf("statement: %s\n", s->description().c_str());
+            printf("statement: %s\n", (*s)->description().c_str());
             ABORT("unsupported statement kind");
     }
 }
 
-CFG CFGGenerator::getCFG(const FunctionDefinition& f) {
+CFG CFGGenerator::getCFG(FunctionDefinition& f) {
     CFG result;
     result.fStart = result.newBlock();
     result.fCurrent = result.fStart;
-    this->addStatement(result, f.fBody.get());
+    this->addStatement(result, &f.fBody);
     result.newBlock();
     result.fExit = result.fCurrent;
     return result;
diff --git a/src/sksl/SkSLCFGGenerator.h b/src/sksl/SkSLCFGGenerator.h
index 337fdfa..885d926 100644
--- a/src/sksl/SkSLCFGGenerator.h
+++ b/src/sksl/SkSLCFGGenerator.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_CFGGENERATOR
 #define SKSL_CFGGENERATOR
 
@@ -26,6 +26,42 @@
             kExpression_Kind
         };
 
+        Node(Kind kind, bool constantPropagation, std::unique_ptr<Expression>* expression,
+             std::unique_ptr<Statement>* statement)
+        : fKind(kind)
+        , fConstantPropagation(constantPropagation)
+        , fExpression(expression)
+        , fStatement(statement) {}
+
+        std::unique_ptr<Expression>* expression() const {
+            ASSERT(fKind == kExpression_Kind);
+            return fExpression;
+        }
+
+        void setExpression(std::unique_ptr<Expression> expr) {
+            ASSERT(fKind == kExpression_Kind);
+            *fExpression = std::move(expr);
+        }
+
+        std::unique_ptr<Statement>* statement() const {
+            ASSERT(fKind == kStatement_Kind);
+            return fStatement;
+        }
+
+        void setStatement(std::unique_ptr<Statement> stmt) {
+            ASSERT(fKind == kStatement_Kind);
+            *fStatement = std::move(stmt);
+        }
+
+        String description() const {
+            if (fKind == kStatement_Kind) {
+                return (*fStatement)->description();
+            } else {
+                ASSERT(fKind == kExpression_Kind);
+                return (*fExpression)->description();
+            }
+        }
+
         Kind fKind;
         // if false, this node should not be subject to constant propagation. This happens with
         // compound assignment (i.e. x *= 2), in which the value x is used as an rvalue for
@@ -35,10 +71,45 @@
         // assignment if the target is constant (i.e. x = 1; x *= 2; should become x = 1; x = 1 * 2;
         // and then collapse down to a simple x = 2;).
         bool fConstantPropagation;
+
+    private:
+        // we store pointers to the unique_ptrs so that we can replace expressions or statements
+        // during optimization without having to regenerate the entire tree
         std::unique_ptr<Expression>* fExpression;
-        const Statement* fStatement;
+        std::unique_ptr<Statement>* fStatement;
     };
 
+    /**
+     * Attempts to remove the expression (and its subexpressions) pointed to by the iterator. If the
+     * expression can be cleanly removed, returns true and updates the iterator to point to the
+     * expression after the deleted expression. Otherwise returns false (and the CFG will need to be
+     * regenerated).
+     */
+    bool tryRemoveExpression(std::vector<BasicBlock::Node>::iterator* iter);
+
+    /**
+     * Locates and attempts remove an expression occurring before the expression pointed to by iter.
+     * If the expression can be cleanly removed, returns true and resets iter to a valid iterator
+     * pointing to the same expression it did initially. Otherwise returns false (and the CFG will
+     * need to be regenerated).
+     */
+    bool tryRemoveExpressionBefore(std::vector<BasicBlock::Node>::iterator* iter, Expression* e);
+
+    /**
+     * As tryRemoveExpressionBefore, but for lvalues. As lvalues are at most partially evaluated
+     * (for instance, x[i] = 0 evaluates i but not x) this will only look for the parts of the
+     * lvalue that are actually evaluated.
+     */
+    bool tryRemoveLValueBefore(std::vector<BasicBlock::Node>::iterator* iter, Expression* lvalue);
+
+    /**
+     * Attempts to inserts a new expression before the node pointed to by iter. If the
+     * expression can be cleanly inserted, returns true and updates the iterator to point to the
+     * newly inserted expression. Otherwise returns false (and the CFG will need to be regenerated).
+     */
+    bool tryInsertExpression(std::vector<BasicBlock::Node>::iterator* iter,
+                             std::unique_ptr<Expression>* expr);
+
     std::vector<Node> fNodes;
     std::set<BlockId> fEntrances;
     std::set<BlockId> fExits;
@@ -81,10 +152,10 @@
 public:
     CFGGenerator() {}
 
-    CFG getCFG(const FunctionDefinition& f);
+    CFG getCFG(FunctionDefinition& f);
 
 private:
-    void addStatement(CFG& cfg, const Statement* s);
+    void addStatement(CFG& cfg, std::unique_ptr<Statement>* s);
 
     void addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool constantPropagate);
 
diff --git a/src/sksl/SkSLCodeGenerator.h b/src/sksl/SkSLCodeGenerator.h
index 111e73a..f6f9905 100644
--- a/src/sksl/SkSLCodeGenerator.h
+++ b/src/sksl/SkSLCodeGenerator.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_CODEGENERATOR
 #define SKSL_CODEGENERATOR
 
@@ -18,7 +18,7 @@
  */
 class CodeGenerator {
 public:
-    CodeGenerator(const Program* program, ErrorReporter* errors, SkWStream* out)
+    CodeGenerator(const Program* program, ErrorReporter* errors, OutputStream* out)
     : fProgram(*program)
     , fErrors(*errors)
     , fOut(out) {}
@@ -31,7 +31,7 @@
 
     const Program& fProgram;
     ErrorReporter& fErrors;
-    SkWStream* fOut;
+    OutputStream* fOut;
 };
 
 } // namespace
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 1cbc441..dd20b5c 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -14,12 +14,14 @@
 #include "SkSLParser.h"
 #include "SkSLSPIRVCodeGenerator.h"
 #include "ir/SkSLExpression.h"
+#include "ir/SkSLExpressionStatement.h"
 #include "ir/SkSLIntLiteral.h"
 #include "ir/SkSLModifiersDeclaration.h"
+#include "ir/SkSLNop.h"
 #include "ir/SkSLSymbolTable.h"
+#include "ir/SkSLTernaryExpression.h"
 #include "ir/SkSLUnresolvedFunction.h"
 #include "ir/SkSLVarDeclarations.h"
-#include "SkMutex.h"
 
 #ifdef SK_ENABLE_SPIRV_VALIDATION
 #include "spirv-tools/libspirv.hpp"
@@ -49,8 +51,8 @@
 
 Compiler::Compiler()
 : fErrorCount(0) {
-    auto types = std::shared_ptr<SymbolTable>(new SymbolTable(*this));
-    auto symbols = std::shared_ptr<SymbolTable>(new SymbolTable(types, *this));
+    auto types = std::shared_ptr<SymbolTable>(new SymbolTable(this));
+    auto symbols = std::shared_ptr<SymbolTable>(new SymbolTable(types, this));
     fIRGenerator = new IRGenerator(&fContext, symbols, *this);
     fTypes = types;
     #define ADD_TYPE(t) types->addWithoutOwnership(fContext.f ## t ## _Type->fName, \
@@ -77,17 +79,17 @@
     ADD_TYPE(BVec3);
     ADD_TYPE(BVec4);
     ADD_TYPE(Mat2x2);
-    types->addWithoutOwnership(SkString("mat2x2"), fContext.fMat2x2_Type.get());
+    types->addWithoutOwnership(String("mat2x2"), fContext.fMat2x2_Type.get());
     ADD_TYPE(Mat2x3);
     ADD_TYPE(Mat2x4);
     ADD_TYPE(Mat3x2);
     ADD_TYPE(Mat3x3);
-    types->addWithoutOwnership(SkString("mat3x3"), fContext.fMat3x3_Type.get());
+    types->addWithoutOwnership(String("mat3x3"), fContext.fMat3x3_Type.get());
     ADD_TYPE(Mat3x4);
     ADD_TYPE(Mat4x2);
     ADD_TYPE(Mat4x3);
     ADD_TYPE(Mat4x4);
-    types->addWithoutOwnership(SkString("mat4x4"), fContext.fMat4x4_Type.get());
+    types->addWithoutOwnership(String("mat4x4"), fContext.fMat4x4_Type.get());
     ADD_TYPE(GenType);
     ADD_TYPE(GenDType);
     ADD_TYPE(GenIType);
@@ -147,14 +149,14 @@
     ADD_TYPE(GSampler2DArrayShadow);
     ADD_TYPE(GSamplerCubeArrayShadow);
 
-    SkString skCapsName("sk_Caps");
-    Variable* skCaps = new Variable(Position(), Modifiers(), skCapsName, 
+    String skCapsName("sk_Caps");
+    Variable* skCaps = new Variable(Position(), Modifiers(), skCapsName,
                                     *fContext.fSkCaps_Type, Variable::kGlobal_Storage);
     fIRGenerator->fSymbolTable->add(skCapsName, std::unique_ptr<Symbol>(skCaps));
 
     Modifiers::Flag ignored1;
     std::vector<std::unique_ptr<ProgramElement>> ignored2;
-    this->internalConvertProgram(SkString(SKSL_INCLUDE), &ignored1, &ignored2);
+    this->internalConvertProgram(String(SKSL_INCLUDE), &ignored1, &ignored2);
     fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
     ASSERT(!fErrorCount);
 }
@@ -208,8 +210,8 @@
                               DefinitionMap* definitions) {
     switch (node.fKind) {
         case BasicBlock::Node::kExpression_Kind: {
-            ASSERT(node.fExpression);
-            const Expression* expr = (Expression*) node.fExpression->get();
+            ASSERT(node.expression());
+            const Expression* expr = (Expression*) node.expression()->get();
             switch (expr->fKind) {
                 case Expression::kBinary_Kind: {
                     BinaryExpression* b = (BinaryExpression*) expr;
@@ -241,23 +243,29 @@
                                        p->fOperand.get(),
                                        (std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
                                        definitions);
-
                     }
                     break;
                 }
+                case Expression::kVariableReference_Kind: {
+                    const VariableReference* v = (VariableReference*) expr;
+                    if (v->fRefKind != VariableReference::kRead_RefKind) {
+                        this->addDefinition(
+                                       v,
+                                       (std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
+                                       definitions);
+                    }
+                }
                 default:
                     break;
             }
             break;
         }
         case BasicBlock::Node::kStatement_Kind: {
-            const Statement* stmt = (Statement*) node.fStatement;
-            if (stmt->fKind == Statement::kVarDeclarations_Kind) {
-                VarDeclarationsStatement* vd = (VarDeclarationsStatement*) stmt;
-                for (VarDeclaration& decl : vd->fDeclaration->fVars) {
-                    if (decl.fValue) {
-                        (*definitions)[decl.fVar] = &decl.fValue;
-                    }
+            const Statement* stmt = (Statement*) node.statement()->get();
+            if (stmt->fKind == Statement::kVarDeclaration_Kind) {
+                VarDeclaration& vd = (VarDeclaration&) *stmt;
+                if (vd.fValue) {
+                    (*definitions)[vd.fVar] = &vd.fValue;
                 }
             }
             break;
@@ -309,12 +317,12 @@
     for (const auto& block : cfg.fBlocks) {
         for (const auto& node : block.fNodes) {
             if (node.fKind == BasicBlock::Node::kStatement_Kind) {
-                ASSERT(node.fStatement);
-                const Statement* s = node.fStatement;
+                ASSERT(node.statement());
+                const Statement* s = node.statement()->get();
                 if (s->fKind == Statement::kVarDeclarations_Kind) {
                     const VarDeclarationsStatement* vd = (const VarDeclarationsStatement*) s;
-                    for (const VarDeclaration& decl : vd->fDeclaration->fVars) {
-                        result[decl.fVar] = nullptr;
+                    for (const auto& decl : vd->fDeclaration->fVars) {
+                        result[((VarDeclaration&) *decl).fVar] = nullptr;
                     }
                 }
             }
@@ -323,20 +331,648 @@
     return result;
 }
 
-void Compiler::scanCFG(const FunctionDefinition& f) {
-    CFG cfg = CFGGenerator().getCFG(f);
+/**
+ * Returns true if assigning to this lvalue has no effect.
+ */
+static bool is_dead(const Expression& lvalue) {
+    switch (lvalue.fKind) {
+        case Expression::kVariableReference_Kind:
+            return ((VariableReference&) lvalue).fVariable.dead();
+        case Expression::kSwizzle_Kind:
+            return is_dead(*((Swizzle&) lvalue).fBase);
+        case Expression::kFieldAccess_Kind:
+            return is_dead(*((FieldAccess&) lvalue).fBase);
+        case Expression::kIndex_Kind: {
+            const IndexExpression& idx = (IndexExpression&) lvalue;
+            return is_dead(*idx.fBase) && !idx.fIndex->hasSideEffects();
+        }
+        default:
+            ABORT("invalid lvalue: %s\n", lvalue.description().c_str());
+    }
+}
 
-    // compute the data flow
-    cfg.fBlocks[cfg.fStart].fBefore = compute_start_state(cfg);
+/**
+ * Returns true if this is an assignment which can be collapsed down to just the right hand side due
+ * to a dead target and lack of side effects on the left hand side.
+ */
+static bool dead_assignment(const BinaryExpression& b) {
+    if (!Token::IsAssignment(b.fOperator)) {
+        return false;
+    }
+    return is_dead(*b.fLeft);
+}
+
+void Compiler::computeDataFlow(CFG* cfg) {
+    cfg->fBlocks[cfg->fStart].fBefore = compute_start_state(*cfg);
     std::set<BlockId> workList;
-    for (BlockId i = 0; i < cfg.fBlocks.size(); i++) {
+    for (BlockId i = 0; i < cfg->fBlocks.size(); i++) {
         workList.insert(i);
     }
     while (workList.size()) {
         BlockId next = *workList.begin();
         workList.erase(workList.begin());
-        this->scanCFG(&cfg, next, &workList);
+        this->scanCFG(cfg, next, &workList);
     }
+}
+
+/**
+ * Attempts to replace the expression pointed to by iter with a new one (in both the CFG and the
+ * IR). If the expression can be cleanly removed, returns true and updates the iterator to point to
+ * the newly-inserted element. Otherwise updates only the IR and returns false (and the CFG will
+ * need to be regenerated).
+ */
+bool try_replace_expression(BasicBlock* b,
+                            std::vector<BasicBlock::Node>::iterator* iter,
+                            std::unique_ptr<Expression>* newExpression) {
+    std::unique_ptr<Expression>* target = (*iter)->expression();
+    if (!b->tryRemoveExpression(iter)) {
+        *target = std::move(*newExpression);
+        return false;
+    }
+    *target = std::move(*newExpression);
+    return b->tryInsertExpression(iter, target);
+}
+
+/**
+ * Returns true if the expression is a constant numeric literal with the specified value, or a
+ * constant vector with all elements equal to the specified value.
+ */
+bool is_constant(const Expression& expr, double value) {
+    switch (expr.fKind) {
+        case Expression::kIntLiteral_Kind:
+            return ((IntLiteral&) expr).fValue == value;
+        case Expression::kFloatLiteral_Kind:
+            return ((FloatLiteral&) expr).fValue == value;
+        case Expression::kConstructor_Kind: {
+            Constructor& c = (Constructor&) expr;
+            if (c.fType.kind() == Type::kVector_Kind && c.isConstant()) {
+                for (int i = 0; i < c.fType.columns(); ++i) {
+                    if (!is_constant(c.getVecComponent(i), value)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+        default:
+            return false;
+    }
+}
+
+/**
+ * Collapses the binary expression pointed to by iter down to just the right side (in both the IR
+ * and CFG structures).
+ */
+void delete_left(BasicBlock* b,
+                 std::vector<BasicBlock::Node>::iterator* iter,
+                 bool* outUpdated,
+                 bool* outNeedsRescan) {
+    *outUpdated = true;
+    std::unique_ptr<Expression>* target = (*iter)->expression();
+    ASSERT((*target)->fKind == Expression::kBinary_Kind);
+    BinaryExpression& bin = (BinaryExpression&) **target;
+    bool result;
+    if (bin.fOperator == Token::EQ) {
+        result = b->tryRemoveLValueBefore(iter, bin.fLeft.get());
+    } else {
+        result = b->tryRemoveExpressionBefore(iter, bin.fLeft.get());
+    }
+    *target = std::move(bin.fRight);
+    if (!result) {
+        *outNeedsRescan = true;
+        return;
+    }
+    if (*iter == b->fNodes.begin()) {
+        *outNeedsRescan = true;
+        return;
+    }
+    --(*iter);
+    if ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
+        (*iter)->expression() != &bin.fRight) {
+        *outNeedsRescan = true;
+        return;
+    }
+    *iter = b->fNodes.erase(*iter);
+    ASSERT((*iter)->expression() == target);
+}
+
+/**
+ * Collapses the binary expression pointed to by iter down to just the left side (in both the IR and
+ * CFG structures).
+ */
+void delete_right(BasicBlock* b,
+                  std::vector<BasicBlock::Node>::iterator* iter,
+                  bool* outUpdated,
+                  bool* outNeedsRescan) {
+    *outUpdated = true;
+    std::unique_ptr<Expression>* target = (*iter)->expression();
+    ASSERT((*target)->fKind == Expression::kBinary_Kind);
+    BinaryExpression& bin = (BinaryExpression&) **target;
+    if (!b->tryRemoveExpressionBefore(iter, bin.fRight.get())) {
+        *target = std::move(bin.fLeft);
+        *outNeedsRescan = true;
+        return;
+    }
+    *target = std::move(bin.fLeft);
+    if (*iter == b->fNodes.begin()) {
+        *outNeedsRescan = true;
+        return;
+    }
+    --(*iter);
+    if (((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
+        (*iter)->expression() != &bin.fLeft)) {
+        *outNeedsRescan = true;
+        return;
+    }
+    *iter = b->fNodes.erase(*iter);
+    ASSERT((*iter)->expression() == target);
+}
+
+/**
+ * Constructs the specified type using a single argument.
+ */
+static std::unique_ptr<Expression> construct(const Type& type, std::unique_ptr<Expression> v) {
+    std::vector<std::unique_ptr<Expression>> args;
+    args.push_back(std::move(v));
+    auto result = std::unique_ptr<Expression>(new Constructor(Position(), type, std::move(args)));
+    return result;
+}
+
+/**
+ * Used in the implementations of vectorize_left and vectorize_right. Given a vector type and an
+ * expression x, deletes the expression pointed to by iter and replaces it with <type>(x).
+ */
+static void vectorize(BasicBlock* b,
+                      std::vector<BasicBlock::Node>::iterator* iter,
+                      const Type& type,
+                      std::unique_ptr<Expression>* otherExpression,
+                      bool* outUpdated,
+                      bool* outNeedsRescan) {
+    ASSERT((*(*iter)->expression())->fKind == Expression::kBinary_Kind);
+    ASSERT(type.kind() == Type::kVector_Kind);
+    ASSERT((*otherExpression)->fType.kind() == Type::kScalar_Kind);
+    *outUpdated = true;
+    std::unique_ptr<Expression>* target = (*iter)->expression();
+    if (!b->tryRemoveExpression(iter)) {
+        *target = construct(type, std::move(*otherExpression));
+        *outNeedsRescan = true;
+    } else {
+        *target = construct(type, std::move(*otherExpression));
+        if (!b->tryInsertExpression(iter, target)) {
+            *outNeedsRescan = true;
+        }
+    }
+}
+
+/**
+ * Given a binary expression of the form x <op> vec<n>(y), deletes the right side and vectorizes the
+ * left to yield vec<n>(x).
+ */
+static void vectorize_left(BasicBlock* b,
+                           std::vector<BasicBlock::Node>::iterator* iter,
+                           bool* outUpdated,
+                           bool* outNeedsRescan) {
+    BinaryExpression& bin = (BinaryExpression&) **(*iter)->expression();
+    vectorize(b, iter, bin.fRight->fType, &bin.fLeft, outUpdated, outNeedsRescan);
+}
+
+/**
+ * Given a binary expression of the form vec<n>(x) <op> y, deletes the left side and vectorizes the
+ * right to yield vec<n>(y).
+ */
+static void vectorize_right(BasicBlock* b,
+                            std::vector<BasicBlock::Node>::iterator* iter,
+                            bool* outUpdated,
+                            bool* outNeedsRescan) {
+    BinaryExpression& bin = (BinaryExpression&) **(*iter)->expression();
+    vectorize(b, iter, bin.fLeft->fType, &bin.fRight, outUpdated, outNeedsRescan);
+}
+
+// Mark that an expression which we were writing to is no longer being written to
+void clear_write(const Expression& expr) {
+    switch (expr.fKind) {
+        case Expression::kVariableReference_Kind: {
+            ((VariableReference&) expr).setRefKind(VariableReference::kRead_RefKind);
+            break;
+        }
+        case Expression::kFieldAccess_Kind:
+            clear_write(*((FieldAccess&) expr).fBase);
+            break;
+        case Expression::kSwizzle_Kind:
+            clear_write(*((Swizzle&) expr).fBase);
+            break;
+        case Expression::kIndex_Kind:
+            clear_write(*((IndexExpression&) expr).fBase);
+            break;
+        default:
+            ABORT("shouldn't be writing to this kind of expression\n");
+            break;
+    }
+}
+
+void Compiler::simplifyExpression(DefinitionMap& definitions,
+                                  BasicBlock& b,
+                                  std::vector<BasicBlock::Node>::iterator* iter,
+                                  std::unordered_set<const Variable*>* undefinedVariables,
+                                  bool* outUpdated,
+                                  bool* outNeedsRescan) {
+    Expression* expr = (*iter)->expression()->get();
+    ASSERT(expr);
+    if ((*iter)->fConstantPropagation) {
+        std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator, definitions);
+        if (optimized) {
+            *outUpdated = true;
+            if (!try_replace_expression(&b, iter, &optimized)) {
+                *outNeedsRescan = true;
+                return;
+            }
+            ASSERT((*iter)->fKind == BasicBlock::Node::kExpression_Kind);
+            expr = (*iter)->expression()->get();
+        }
+    }
+    switch (expr->fKind) {
+        case Expression::kVariableReference_Kind: {
+            const Variable& var = ((VariableReference*) expr)->fVariable;
+            if (var.fStorage == Variable::kLocal_Storage && !definitions[&var] &&
+                (*undefinedVariables).find(&var) == (*undefinedVariables).end()) {
+                (*undefinedVariables).insert(&var);
+                this->error(expr->fPosition,
+                            "'" + var.fName + "' has not been assigned");
+            }
+            break;
+        }
+        case Expression::kTernary_Kind: {
+            TernaryExpression* t = (TernaryExpression*) expr;
+            if (t->fTest->fKind == Expression::kBoolLiteral_Kind) {
+                // ternary has a constant test, replace it with either the true or
+                // false branch
+                if (((BoolLiteral&) *t->fTest).fValue) {
+                    (*iter)->setExpression(std::move(t->fIfTrue));
+                } else {
+                    (*iter)->setExpression(std::move(t->fIfFalse));
+                }
+                *outUpdated = true;
+                *outNeedsRescan = true;
+            }
+            break;
+        }
+        case Expression::kBinary_Kind: {
+            BinaryExpression* bin = (BinaryExpression*) expr;
+            if (dead_assignment(*bin)) {
+                delete_left(&b, iter, outUpdated, outNeedsRescan);
+                break;
+            }
+            // collapse useless expressions like x * 1 or x + 0
+            if (((bin->fLeft->fType.kind()  != Type::kScalar_Kind) &&
+                 (bin->fLeft->fType.kind()  != Type::kVector_Kind)) ||
+                ((bin->fRight->fType.kind() != Type::kScalar_Kind) &&
+                 (bin->fRight->fType.kind() != Type::kVector_Kind))) {
+                break;
+            }
+            switch (bin->fOperator) {
+                case Token::STAR:
+                    if (is_constant(*bin->fLeft, 1)) {
+                        if (bin->fLeft->fType.kind() == Type::kVector_Kind &&
+                            bin->fRight->fType.kind() == Type::kScalar_Kind) {
+                            // vec4(1) * x -> vec4(x)
+                            vectorize_right(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // 1 * x -> x
+                            // 1 * vec4(x) -> vec4(x)
+                            // vec4(1) * vec4(x) -> vec4(x)
+                            delete_left(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    else if (is_constant(*bin->fLeft, 0)) {
+                        if (bin->fLeft->fType.kind() == Type::kScalar_Kind &&
+                            bin->fRight->fType.kind() == Type::kVector_Kind) {
+                            // 0 * vec4(x) -> vec4(0)
+                            vectorize_left(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // 0 * x -> 0
+                            // vec4(0) * x -> vec4(0)
+                            // vec4(0) * vec4(x) -> vec4(0)
+                            delete_right(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    else if (is_constant(*bin->fRight, 1)) {
+                        if (bin->fLeft->fType.kind() == Type::kScalar_Kind &&
+                            bin->fRight->fType.kind() == Type::kVector_Kind) {
+                            // x * vec4(1) -> vec4(x)
+                            vectorize_left(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // x * 1 -> x
+                            // vec4(x) * 1 -> vec4(x)
+                            // vec4(x) * vec4(1) -> vec4(x)
+                            delete_right(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    else if (is_constant(*bin->fRight, 0)) {
+                        if (bin->fLeft->fType.kind() == Type::kVector_Kind &&
+                            bin->fRight->fType.kind() == Type::kScalar_Kind) {
+                            // vec4(x) * 0 -> vec4(0)
+                            vectorize_right(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // x * 0 -> 0
+                            // x * vec4(0) -> vec4(0)
+                            // vec4(x) * vec4(0) -> vec4(0)
+                            delete_left(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    break;
+                case Token::PLUS:
+                    if (is_constant(*bin->fLeft, 0)) {
+                        if (bin->fLeft->fType.kind() == Type::kVector_Kind &&
+                            bin->fRight->fType.kind() == Type::kScalar_Kind) {
+                            // vec4(0) + x -> vec4(x)
+                            vectorize_right(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // 0 + x -> x
+                            // 0 + vec4(x) -> vec4(x)
+                            // vec4(0) + vec4(x) -> vec4(x)
+                            delete_left(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    } else if (is_constant(*bin->fRight, 0)) {
+                        if (bin->fLeft->fType.kind() == Type::kScalar_Kind &&
+                            bin->fRight->fType.kind() == Type::kVector_Kind) {
+                            // x + vec4(0) -> vec4(x)
+                            vectorize_left(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // x + 0 -> x
+                            // vec4(x) + 0 -> vec4(x)
+                            // vec4(x) + vec4(0) -> vec4(x)
+                            delete_right(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    break;
+                case Token::MINUS:
+                    if (is_constant(*bin->fRight, 0)) {
+                        if (bin->fLeft->fType.kind() == Type::kScalar_Kind &&
+                            bin->fRight->fType.kind() == Type::kVector_Kind) {
+                            // x - vec4(0) -> vec4(x)
+                            vectorize_left(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // x - 0 -> x
+                            // vec4(x) - 0 -> vec4(x)
+                            // vec4(x) - vec4(0) -> vec4(x)
+                            delete_right(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    break;
+                case Token::SLASH:
+                    if (is_constant(*bin->fRight, 1)) {
+                        if (bin->fLeft->fType.kind() == Type::kScalar_Kind &&
+                            bin->fRight->fType.kind() == Type::kVector_Kind) {
+                            // x / vec4(1) -> vec4(x)
+                            vectorize_left(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // x / 1 -> x
+                            // vec4(x) / 1 -> vec4(x)
+                            // vec4(x) / vec4(1) -> vec4(x)
+                            delete_right(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    } else if (is_constant(*bin->fLeft, 0)) {
+                        if (bin->fLeft->fType.kind() == Type::kScalar_Kind &&
+                            bin->fRight->fType.kind() == Type::kVector_Kind) {
+                            // 0 / vec4(x) -> vec4(0)
+                            vectorize_left(&b, iter, outUpdated, outNeedsRescan);
+                        } else {
+                            // 0 / x -> 0
+                            // vec4(0) / x -> vec4(0)
+                            // vec4(0) / vec4(x) -> vec4(0)
+                            delete_right(&b, iter, outUpdated, outNeedsRescan);
+                        }
+                    }
+                    break;
+                case Token::PLUSEQ:
+                    if (is_constant(*bin->fRight, 0)) {
+                        clear_write(*bin->fLeft);
+                        delete_right(&b, iter, outUpdated, outNeedsRescan);
+                    }
+                    break;
+                case Token::MINUSEQ:
+                    if (is_constant(*bin->fRight, 0)) {
+                        clear_write(*bin->fLeft);
+                        delete_right(&b, iter, outUpdated, outNeedsRescan);
+                    }
+                    break;
+                case Token::STAREQ:
+                    if (is_constant(*bin->fRight, 1)) {
+                        clear_write(*bin->fLeft);
+                        delete_right(&b, iter, outUpdated, outNeedsRescan);
+                    }
+                    break;
+                case Token::SLASHEQ:
+                    if (is_constant(*bin->fRight, 1)) {
+                        clear_write(*bin->fLeft);
+                        delete_right(&b, iter, outUpdated, outNeedsRescan);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        default:
+            break;
+    }
+}
+
+
+// returns true if this statement could potentially execute a break at the current level (we ignore
+// nested loops and switches, since any breaks inside of them will merely break the loop / switch)
+static bool contains_break(Statement& s) {
+    switch (s.fKind) {
+        case Statement::kBlock_Kind:
+            for (const auto& sub : ((Block&) s).fStatements) {
+                if (contains_break(*sub)) {
+                    return true;
+                }
+            }
+            return false;
+        case Statement::kBreak_Kind:
+            return true;
+        case Statement::kIf_Kind: {
+            const IfStatement& i = (IfStatement&) s;
+            return contains_break(*i.fIfTrue) || (i.fIfFalse && contains_break(*i.fIfFalse));
+        }
+        default:
+            return false;
+    }
+}
+
+// Returns a block containing all of the statements that will be run if the given case matches
+// (which, owing to the statements being owned by unique_ptrs, means the switch itself will be
+// broken by this call and must then be discarded).
+// Returns null (and leaves the switch unmodified) if no such simple reduction is possible, such as
+// when break statements appear inside conditionals.
+static std::unique_ptr<Statement> block_for_case(SwitchStatement* s, SwitchCase* c) {
+    bool capturing = false;
+    std::vector<std::unique_ptr<Statement>*> statementPtrs;
+    for (const auto& current : s->fCases) {
+        if (current.get() == c) {
+            capturing = true;
+        }
+        if (capturing) {
+            for (auto& stmt : current->fStatements) {
+                if (stmt->fKind == Statement::kBreak_Kind) {
+                    capturing = false;
+                    break;
+                }
+                if (contains_break(*stmt)) {
+                    return nullptr;
+                }
+                statementPtrs.push_back(&stmt);
+            }
+            if (!capturing) {
+                break;
+            }
+        }
+    }
+    std::vector<std::unique_ptr<Statement>> statements;
+    for (const auto& s : statementPtrs) {
+        statements.push_back(std::move(*s));
+    }
+    return std::unique_ptr<Statement>(new Block(Position(), std::move(statements)));
+}
+
+void Compiler::simplifyStatement(DefinitionMap& definitions,
+                                 BasicBlock& b,
+                                 std::vector<BasicBlock::Node>::iterator* iter,
+                                 std::unordered_set<const Variable*>* undefinedVariables,
+                                 bool* outUpdated,
+                                 bool* outNeedsRescan) {
+    Statement* stmt = (*iter)->statement()->get();
+    switch (stmt->fKind) {
+        case Statement::kVarDeclaration_Kind: {
+            const auto& varDecl = (VarDeclaration&) *stmt;
+            if (varDecl.fVar->dead() &&
+                (!varDecl.fValue ||
+                 !varDecl.fValue->hasSideEffects())) {
+                if (varDecl.fValue) {
+                    ASSERT((*iter)->statement()->get() == stmt);
+                    if (!b.tryRemoveExpressionBefore(iter, varDecl.fValue.get())) {
+                        *outNeedsRescan = true;
+                    }
+                }
+                (*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
+                *outUpdated = true;
+            }
+            break;
+        }
+        case Statement::kIf_Kind: {
+            IfStatement& i = (IfStatement&) *stmt;
+            if (i.fTest->fKind == Expression::kBoolLiteral_Kind) {
+                // constant if, collapse down to a single branch
+                if (((BoolLiteral&) *i.fTest).fValue) {
+                    ASSERT(i.fIfTrue);
+                    (*iter)->setStatement(std::move(i.fIfTrue));
+                } else {
+                    if (i.fIfFalse) {
+                        (*iter)->setStatement(std::move(i.fIfFalse));
+                    } else {
+                        (*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
+                    }
+                }
+                *outUpdated = true;
+                *outNeedsRescan = true;
+                break;
+            }
+            if (i.fIfFalse && i.fIfFalse->isEmpty()) {
+                // else block doesn't do anything, remove it
+                i.fIfFalse.reset();
+                *outUpdated = true;
+                *outNeedsRescan = true;
+            }
+            if (!i.fIfFalse && i.fIfTrue->isEmpty()) {
+                // if block doesn't do anything, no else block
+                if (i.fTest->hasSideEffects()) {
+                    // test has side effects, keep it
+                    (*iter)->setStatement(std::unique_ptr<Statement>(
+                                                      new ExpressionStatement(std::move(i.fTest))));
+                } else {
+                    // no if, no else, no test side effects, kill the whole if
+                    // statement
+                    (*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
+                }
+                *outUpdated = true;
+                *outNeedsRescan = true;
+            }
+            break;
+        }
+        case Statement::kSwitch_Kind: {
+            SwitchStatement& s = (SwitchStatement&) *stmt;
+            if (s.fValue->isConstant()) {
+                // switch is constant, replace it with the case that matches
+                bool found = false;
+                SwitchCase* defaultCase = nullptr;
+                for (const auto& c : s.fCases) {
+                    if (!c->fValue) {
+                        defaultCase = c.get();
+                        continue;
+                    }
+                    ASSERT(c->fValue->fKind == s.fValue->fKind);
+                    found = c->fValue->compareConstant(fContext, *s.fValue);
+                    if (found) {
+                        std::unique_ptr<Statement> newBlock = block_for_case(&s, c.get());
+                        if (newBlock) {
+                            (*iter)->setStatement(std::move(newBlock));
+                            break;
+                        } else {
+                            if (s.fIsStatic) {
+                                this->error(s.fPosition,
+                                            "static switch contains non-static conditional break");
+                                s.fIsStatic = false;
+                            }
+                            return; // can't simplify
+                        }
+                    }
+                }
+                if (!found) {
+                    // no matching case. use default if it exists, or kill the whole thing
+                    if (defaultCase) {
+                        std::unique_ptr<Statement> newBlock = block_for_case(&s, defaultCase);
+                        if (newBlock) {
+                            (*iter)->setStatement(std::move(newBlock));
+                        } else {
+                            if (s.fIsStatic) {
+                                this->error(s.fPosition,
+                                            "static switch contains non-static conditional break");
+                                s.fIsStatic = false;
+                            }
+                            return; // can't simplify
+                        }
+                    } else {
+                        (*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
+                    }
+                }
+                *outUpdated = true;
+                *outNeedsRescan = true;
+            }
+            break;
+        }
+        case Statement::kExpression_Kind: {
+            ExpressionStatement& e = (ExpressionStatement&) *stmt;
+            ASSERT((*iter)->statement()->get() == &e);
+            if (!e.fExpression->hasSideEffects()) {
+                // Expression statement with no side effects, kill it
+                if (!b.tryRemoveExpressionBefore(iter, e.fExpression.get())) {
+                    *outNeedsRescan = true;
+                }
+                ASSERT((*iter)->statement()->get() == stmt);
+                (*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
+                *outUpdated = true;
+            }
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+void Compiler::scanCFG(FunctionDefinition& f) {
+    CFG cfg = CFGGenerator().getCFG(f);
+    this->computeDataFlow(&cfg);
 
     // check for unreachable code
     for (size_t i = 0; i < cfg.fBlocks.size(); i++) {
@@ -345,56 +981,85 @@
             Position p;
             switch (cfg.fBlocks[i].fNodes[0].fKind) {
                 case BasicBlock::Node::kStatement_Kind:
-                    p = cfg.fBlocks[i].fNodes[0].fStatement->fPosition;
+                    p = (*cfg.fBlocks[i].fNodes[0].statement())->fPosition;
                     break;
                 case BasicBlock::Node::kExpression_Kind:
-                    p = (*cfg.fBlocks[i].fNodes[0].fExpression)->fPosition;
+                    p = (*cfg.fBlocks[i].fNodes[0].expression())->fPosition;
                     break;
             }
-            this->error(p, SkString("unreachable"));
+            this->error(p, String("unreachable"));
         }
     }
     if (fErrorCount) {
         return;
     }
 
-    // check for undefined variables, perform constant propagation
+    // check for dead code & undefined variables, perform constant propagation
+    std::unordered_set<const Variable*> undefinedVariables;
+    bool updated;
+    bool needsRescan = false;
+    do {
+        if (needsRescan) {
+            cfg = CFGGenerator().getCFG(f);
+            this->computeDataFlow(&cfg);
+            needsRescan = false;
+        }
+
+        updated = false;
+        for (BasicBlock& b : cfg.fBlocks) {
+            DefinitionMap definitions = b.fBefore;
+
+            for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() && !needsRescan; ++iter) {
+                if (iter->fKind == BasicBlock::Node::kExpression_Kind) {
+                    this->simplifyExpression(definitions, b, &iter, &undefinedVariables, &updated,
+                                             &needsRescan);
+                } else {
+                    this->simplifyStatement(definitions, b, &iter, &undefinedVariables, &updated,
+                                             &needsRescan);
+                }
+                if (needsRescan) {
+                    break;
+                }
+                this->addDefinitions(*iter, &definitions);
+            }
+        }
+    } while (updated);
+    ASSERT(!needsRescan);
+
+    // verify static ifs & switches
     for (BasicBlock& b : cfg.fBlocks) {
         DefinitionMap definitions = b.fBefore;
-        for (BasicBlock::Node& n : b.fNodes) {
-            if (n.fKind == BasicBlock::Node::kExpression_Kind) {
-                ASSERT(n.fExpression);
-                Expression* expr = n.fExpression->get();
-                if (n.fConstantPropagation) {
-                    std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator,
-                                                                                    definitions);
-                    if (optimized) {
-                        n.fExpression->reset(optimized.release());
-                        expr = n.fExpression->get();
-                    }
-                }
-                if (expr->fKind == Expression::kVariableReference_Kind) {
-                    const Variable& var = ((VariableReference*) expr)->fVariable;
-                    if (var.fStorage == Variable::kLocal_Storage &&
-                        !definitions[&var]) {
-                        this->error(expr->fPosition,
-                                    "'" + var.fName + "' has not been assigned");
-                    }
+
+        for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() && !needsRescan; ++iter) {
+            if (iter->fKind == BasicBlock::Node::kStatement_Kind) {
+                const Statement& s = **iter->statement();
+                switch (s.fKind) {
+                    case Statement::kIf_Kind:
+                        if (((const IfStatement&) s).fIsStatic) {
+                            this->error(s.fPosition, "static if has non-static test");
+                        }
+                        break;
+                    case Statement::kSwitch_Kind:
+                        if (((const SwitchStatement&) s).fIsStatic) {
+                            this->error(s.fPosition, "static switch has non-static test");
+                        }
+                        break;
+                    default:
+                        break;
                 }
             }
-            this->addDefinitions(n, &definitions);
         }
     }
 
     // check for missing return
     if (f.fDeclaration.fReturnType != *fContext.fVoid_Type) {
         if (cfg.fBlocks[cfg.fExit].fEntrances.size()) {
-            this->error(f.fPosition, SkString("function can exit without returning a value"));
+            this->error(f.fPosition, String("function can exit without returning a value"));
         }
     }
 }
 
-void Compiler::internalConvertProgram(SkString text,
+void Compiler::internalConvertProgram(String text,
                                       Modifiers::Flag* defaultPrecision,
                                       std::vector<std::unique_ptr<ProgramElement>>* result) {
     Parser parser(text, *fTypes, *this);
@@ -457,7 +1122,7 @@
     }
 }
 
-std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, SkString text,
+std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, String text,
                                                   const Program::Settings& settings) {
     fErrorText = "";
     fErrorCount = 0;
@@ -466,13 +1131,13 @@
     Modifiers::Flag ignored;
     switch (kind) {
         case Program::kVertex_Kind:
-            this->internalConvertProgram(SkString(SKSL_VERT_INCLUDE), &ignored, &elements);
+            this->internalConvertProgram(String(SKSL_VERT_INCLUDE), &ignored, &elements);
             break;
         case Program::kFragment_Kind:
-            this->internalConvertProgram(SkString(SKSL_FRAG_INCLUDE), &ignored, &elements);
+            this->internalConvertProgram(String(SKSL_FRAG_INCLUDE), &ignored, &elements);
             break;
         case Program::kGeometry_Kind:
-            this->internalConvertProgram(SkString(SKSL_GEOM_INCLUDE), &ignored, &elements);
+            this->internalConvertProgram(String(SKSL_GEOM_INCLUDE), &ignored, &elements);
             break;
     }
     fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
@@ -490,23 +1155,22 @@
     return result;
 }
 
-bool Compiler::toSPIRV(const Program& program, SkWStream& out) {
+bool Compiler::toSPIRV(const Program& program, OutputStream& out) {
 #ifdef SK_ENABLE_SPIRV_VALIDATION
-    SkDynamicMemoryWStream buffer;
+    StringStream buffer;
     SPIRVCodeGenerator cg(&fContext, &program, this, &buffer);
     bool result = cg.generateCode();
     if (result) {
-        sk_sp<SkData> data(buffer.detachAsData());
         spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
-        SkASSERT(0 == data->size() % 4);
+        ASSERT(0 == buffer.size() % 4);
         auto dumpmsg = [](spv_message_level_t, const char*, const spv_position_t&, const char* m) {
             SkDebugf("SPIR-V validation error: %s\n", m);
         };
         tools.SetMessageConsumer(dumpmsg);
         // Verify that the SPIR-V we produced is valid. If this assert fails, check the logs prior
         // to the failure to see the validation errors.
-        SkAssertResult(tools.Validate((const uint32_t*) data->data(), data->size() / 4));
-        out.write(data->data(), data->size());
+        ASSERT_RESULT(tools.Validate((const uint32_t*) buffer.data(), buffer.size() / 4));
+        out.write(buffer.data(), buffer.size());
     }
 #else
     SPIRVCodeGenerator cg(&fContext, &program, this, &out);
@@ -516,41 +1180,39 @@
     return result;
 }
 
-bool Compiler::toSPIRV(const Program& program, SkString* out) {
-    SkDynamicMemoryWStream buffer;
+bool Compiler::toSPIRV(const Program& program, String* out) {
+    StringStream buffer;
     bool result = this->toSPIRV(program, buffer);
     if (result) {
-        sk_sp<SkData> data(buffer.detachAsData());
-        *out = SkString((const char*) data->data(), data->size());
+        *out = String(buffer.data(), buffer.size());
     }
     return result;
 }
 
-bool Compiler::toGLSL(const Program& program, SkWStream& out) {
+bool Compiler::toGLSL(const Program& program, OutputStream& out) {
     GLSLCodeGenerator cg(&fContext, &program, this, &out);
     bool result = cg.generateCode();
     this->writeErrorCount();
     return result;
 }
 
-bool Compiler::toGLSL(const Program& program, SkString* out) {
-    SkDynamicMemoryWStream buffer;
+bool Compiler::toGLSL(const Program& program, String* out) {
+    StringStream buffer;
     bool result = this->toGLSL(program, buffer);
     if (result) {
-        sk_sp<SkData> data(buffer.detachAsData());
-        *out = SkString((const char*) data->data(), data->size());
+        *out = String(buffer.data(), buffer.size());
     }
     return result;
 }
 
 
-void Compiler::error(Position position, SkString msg) {
+void Compiler::error(Position position, String msg) {
     fErrorCount++;
     fErrorText += "error: " + position.description() + ": " + msg.c_str() + "\n";
 }
 
-SkString Compiler::errorText() {
-    SkString result = fErrorText;
+String Compiler::errorText() {
+    String result = fErrorText;
     return result;
 }
 
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index ec02c5a..5e7bc9e 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -4,11 +4,12 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_COMPILER
 #define SKSL_COMPILER
 
 #include <set>
+#include <unordered_set>
 #include <vector>
 #include "ir/SkSLProgram.h"
 #include "ir/SkSLSymbolTable.h"
@@ -42,20 +43,20 @@
 
     ~Compiler() override;
 
-    std::unique_ptr<Program> convertProgram(Program::Kind kind, SkString text,
+    std::unique_ptr<Program> convertProgram(Program::Kind kind, String text,
                                             const Program::Settings& settings);
 
-    bool toSPIRV(const Program& program, SkWStream& out);
+    bool toSPIRV(const Program& program, OutputStream& out);
 
-    bool toSPIRV(const Program& program, SkString* out);
+    bool toSPIRV(const Program& program, String* out);
 
-    bool toGLSL(const Program& program, SkWStream& out);
+    bool toGLSL(const Program& program, OutputStream& out);
 
-    bool toGLSL(const Program& program, SkString* out);
+    bool toGLSL(const Program& program, String* out);
 
-    void error(Position position, SkString msg) override;
+    void error(Position position, String msg) override;
 
-    SkString errorText();
+    String errorText();
 
     void writeErrorCount();
 
@@ -71,19 +72,43 @@
 
     void scanCFG(CFG* cfg, BlockId block, std::set<BlockId>* workList);
 
-    void scanCFG(const FunctionDefinition& f);
+    void computeDataFlow(CFG* cfg);
 
-    void internalConvertProgram(SkString text,
+    /**
+     * Simplifies the expression pointed to by iter (in both the IR and CFG structures), if
+     * possible.
+     */
+    void simplifyExpression(DefinitionMap& definitions,
+                            BasicBlock& b,
+                            std::vector<BasicBlock::Node>::iterator* iter,
+                            std::unordered_set<const Variable*>* undefinedVariables,
+                            bool* outUpdated,
+                            bool* outNeedsRescan);
+
+    /**
+     * Simplifies the statement pointed to by iter (in both the IR and CFG structures), if
+     * possible.
+     */
+    void simplifyStatement(DefinitionMap& definitions,
+                           BasicBlock& b,
+                           std::vector<BasicBlock::Node>::iterator* iter,
+                           std::unordered_set<const Variable*>* undefinedVariables,
+                           bool* outUpdated,
+                           bool* outNeedsRescan);
+
+    void scanCFG(FunctionDefinition& f);
+
+    void internalConvertProgram(String text,
                                 Modifiers::Flag* defaultPrecision,
                                 std::vector<std::unique_ptr<ProgramElement>>* result);
 
     std::shared_ptr<SymbolTable> fTypes;
     IRGenerator* fIRGenerator;
-    SkString fSkiaVertText; // FIXME store parsed version instead
+    String fSkiaVertText; // FIXME store parsed version instead
 
     Context fContext;
     int fErrorCount;
-    SkString fErrorText;
+    String fErrorText;
 };
 
 } // namespace
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index df651bc..53e5ea0 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_CONTEXT
 #define SKSL_CONTEXT
 
@@ -19,114 +19,115 @@
 class Context {
 public:
     Context()
-    : fInvalid_Type(new Type(SkString("<INVALID>")))
-    , fVoid_Type(new Type(SkString("void")))
-    , fDouble_Type(new Type(SkString("double"), true))
-    , fDVec2_Type(new Type(SkString("dvec2"), *fDouble_Type, 2))
-    , fDVec3_Type(new Type(SkString("dvec3"), *fDouble_Type, 3))
-    , fDVec4_Type(new Type(SkString("dvec4"), *fDouble_Type, 4))
-    , fFloat_Type(new Type(SkString("float"), true, { fDouble_Type.get() }))
-    , fVec2_Type(new Type(SkString("vec2"), *fFloat_Type, 2))
-    , fVec3_Type(new Type(SkString("vec3"), *fFloat_Type, 3))
-    , fVec4_Type(new Type(SkString("vec4"), *fFloat_Type, 4))
-    , fUInt_Type(new Type(SkString("uint"), true, { fFloat_Type.get(), fDouble_Type.get() }))
-    , fUVec2_Type(new Type(SkString("uvec2"), *fUInt_Type, 2))
-    , fUVec3_Type(new Type(SkString("uvec3"), *fUInt_Type, 3))
-    , fUVec4_Type(new Type(SkString("uvec4"), *fUInt_Type, 4))
-    , fInt_Type(new Type(SkString("int"), true, { fUInt_Type.get(), fFloat_Type.get(),
+    : fInvalid_Type(new Type(String("<INVALID>")))
+    , fVoid_Type(new Type(String("void")))
+    , fDouble_Type(new Type(String("double"), true))
+    , fDVec2_Type(new Type(String("dvec2"), *fDouble_Type, 2))
+    , fDVec3_Type(new Type(String("dvec3"), *fDouble_Type, 3))
+    , fDVec4_Type(new Type(String("dvec4"), *fDouble_Type, 4))
+    , fFloat_Type(new Type(String("float"), true, { fDouble_Type.get() }))
+    , fVec2_Type(new Type(String("vec2"), *fFloat_Type, 2))
+    , fVec3_Type(new Type(String("vec3"), *fFloat_Type, 3))
+    , fVec4_Type(new Type(String("vec4"), *fFloat_Type, 4))
+    , fUInt_Type(new Type(String("uint"), true, { fFloat_Type.get(), fDouble_Type.get() }))
+    , fUVec2_Type(new Type(String("uvec2"), *fUInt_Type, 2))
+    , fUVec3_Type(new Type(String("uvec3"), *fUInt_Type, 3))
+    , fUVec4_Type(new Type(String("uvec4"), *fUInt_Type, 4))
+    , fInt_Type(new Type(String("int"), true, { fUInt_Type.get(), fFloat_Type.get(),
                                                   fDouble_Type.get() }))
-    , fIVec2_Type(new Type(SkString("ivec2"), *fInt_Type, 2))
-    , fIVec3_Type(new Type(SkString("ivec3"), *fInt_Type, 3))
-    , fIVec4_Type(new Type(SkString("ivec4"), *fInt_Type, 4))
-    , fBool_Type(new Type(SkString("bool"), false))
-    , fBVec2_Type(new Type(SkString("bvec2"), *fBool_Type, 2))
-    , fBVec3_Type(new Type(SkString("bvec3"), *fBool_Type, 3))
-    , fBVec4_Type(new Type(SkString("bvec4"), *fBool_Type, 4))
-    , fMat2x2_Type(new Type(SkString("mat2"),   *fFloat_Type, 2, 2))
-    , fMat2x3_Type(new Type(SkString("mat2x3"), *fFloat_Type, 2, 3))
-    , fMat2x4_Type(new Type(SkString("mat2x4"), *fFloat_Type, 2, 4))
-    , fMat3x2_Type(new Type(SkString("mat3x2"), *fFloat_Type, 3, 2))
-    , fMat3x3_Type(new Type(SkString("mat3"),   *fFloat_Type, 3, 3))
-    , fMat3x4_Type(new Type(SkString("mat3x4"), *fFloat_Type, 3, 4))
-    , fMat4x2_Type(new Type(SkString("mat4x2"), *fFloat_Type, 4, 2))
-    , fMat4x3_Type(new Type(SkString("mat4x3"), *fFloat_Type, 4, 3))
-    , fMat4x4_Type(new Type(SkString("mat4"),   *fFloat_Type, 4, 4))
-    , fDMat2x2_Type(new Type(SkString("dmat2"),   *fFloat_Type, 2, 2))
-    , fDMat2x3_Type(new Type(SkString("dmat2x3"), *fFloat_Type, 2, 3))
-    , fDMat2x4_Type(new Type(SkString("dmat2x4"), *fFloat_Type, 2, 4))
-    , fDMat3x2_Type(new Type(SkString("dmat3x2"), *fFloat_Type, 3, 2))
-    , fDMat3x3_Type(new Type(SkString("dmat3"),   *fFloat_Type, 3, 3))
-    , fDMat3x4_Type(new Type(SkString("dmat3x4"), *fFloat_Type, 3, 4))
-    , fDMat4x2_Type(new Type(SkString("dmat4x2"), *fFloat_Type, 4, 2))
-    , fDMat4x3_Type(new Type(SkString("dmat4x3"), *fFloat_Type, 4, 3))
-    , fDMat4x4_Type(new Type(SkString("dmat4"),   *fFloat_Type, 4, 4))
-    , fSampler1D_Type(new Type(SkString("sampler1D"), SpvDim1D, false, false, false, true))
-    , fSampler2D_Type(new Type(SkString("sampler2D"), SpvDim2D, false, false, false, true))
-    , fSampler3D_Type(new Type(SkString("sampler3D"), SpvDim3D, false, false, false, true))
-    , fSamplerExternalOES_Type(new Type(SkString("samplerExternalOES"), SpvDim2D, false, false,
+    , fIVec2_Type(new Type(String("ivec2"), *fInt_Type, 2))
+    , fIVec3_Type(new Type(String("ivec3"), *fInt_Type, 3))
+    , fIVec4_Type(new Type(String("ivec4"), *fInt_Type, 4))
+    , fBool_Type(new Type(String("bool"), false))
+    , fBVec2_Type(new Type(String("bvec2"), *fBool_Type, 2))
+    , fBVec3_Type(new Type(String("bvec3"), *fBool_Type, 3))
+    , fBVec4_Type(new Type(String("bvec4"), *fBool_Type, 4))
+    , fMat2x2_Type(new Type(String("mat2"),   *fFloat_Type, 2, 2))
+    , fMat2x3_Type(new Type(String("mat2x3"), *fFloat_Type, 2, 3))
+    , fMat2x4_Type(new Type(String("mat2x4"), *fFloat_Type, 2, 4))
+    , fMat3x2_Type(new Type(String("mat3x2"), *fFloat_Type, 3, 2))
+    , fMat3x3_Type(new Type(String("mat3"),   *fFloat_Type, 3, 3))
+    , fMat3x4_Type(new Type(String("mat3x4"), *fFloat_Type, 3, 4))
+    , fMat4x2_Type(new Type(String("mat4x2"), *fFloat_Type, 4, 2))
+    , fMat4x3_Type(new Type(String("mat4x3"), *fFloat_Type, 4, 3))
+    , fMat4x4_Type(new Type(String("mat4"),   *fFloat_Type, 4, 4))
+    , fDMat2x2_Type(new Type(String("dmat2"),   *fFloat_Type, 2, 2))
+    , fDMat2x3_Type(new Type(String("dmat2x3"), *fFloat_Type, 2, 3))
+    , fDMat2x4_Type(new Type(String("dmat2x4"), *fFloat_Type, 2, 4))
+    , fDMat3x2_Type(new Type(String("dmat3x2"), *fFloat_Type, 3, 2))
+    , fDMat3x3_Type(new Type(String("dmat3"),   *fFloat_Type, 3, 3))
+    , fDMat3x4_Type(new Type(String("dmat3x4"), *fFloat_Type, 3, 4))
+    , fDMat4x2_Type(new Type(String("dmat4x2"), *fFloat_Type, 4, 2))
+    , fDMat4x3_Type(new Type(String("dmat4x3"), *fFloat_Type, 4, 3))
+    , fDMat4x4_Type(new Type(String("dmat4"),   *fFloat_Type, 4, 4))
+    , fSampler1D_Type(new Type(String("sampler1D"), SpvDim1D, false, false, false, true))
+    , fSampler2D_Type(new Type(String("sampler2D"), SpvDim2D, false, false, false, true))
+    , fSampler3D_Type(new Type(String("sampler3D"), SpvDim3D, false, false, false, true))
+    , fSamplerExternalOES_Type(new Type(String("samplerExternalOES"), SpvDim2D, false, false,
                                         false, true))
-    , fSamplerCube_Type(new Type(SkString("samplerCube"), SpvDimCube, false, false, false, true))
-    , fSampler2DRect_Type(new Type(SkString("sampler2DRect"), SpvDimRect, false, false, false, 
+    , fSamplerCube_Type(new Type(String("samplerCube"), SpvDimCube, false, false, false, true))
+    , fSampler2DRect_Type(new Type(String("sampler2DRect"), SpvDimRect, false, false, false,
                                    true))
-    , fSampler1DArray_Type(new Type(SkString("sampler1DArray")))
-    , fSampler2DArray_Type(new Type(SkString("sampler2DArray")))
-    , fSamplerCubeArray_Type(new Type(SkString("samplerCubeArray")))
-    , fSamplerBuffer_Type(new Type(SkString("samplerBuffer")))
-    , fSampler2DMS_Type(new Type(SkString("sampler2DMS")))
-    , fSampler2DMSArray_Type(new Type(SkString("sampler2DMSArray")))
-    , fSampler1DShadow_Type(new Type(SkString("sampler1DShadow")))
-    , fSampler2DShadow_Type(new Type(SkString("sampler2DShadow")))
-    , fSamplerCubeShadow_Type(new Type(SkString("samplerCubeShadow")))
-    , fSampler2DRectShadow_Type(new Type(SkString("sampler2DRectShadow")))
-    , fSampler1DArrayShadow_Type(new Type(SkString("sampler1DArrayShadow")))
-    , fSampler2DArrayShadow_Type(new Type(SkString("sampler2DArrayShadow")))
-    , fSamplerCubeArrayShadow_Type(new Type(SkString("samplerCubeArrayShadow")))
+    , fSampler1DArray_Type(new Type(String("sampler1DArray")))
+    , fSampler2DArray_Type(new Type(String("sampler2DArray")))
+    , fSamplerCubeArray_Type(new Type(String("samplerCubeArray")))
+    , fSamplerBuffer_Type(new Type(String("samplerBuffer"), SpvDimBuffer, false, false, false,
+                                   true))
+    , fSampler2DMS_Type(new Type(String("sampler2DMS")))
+    , fSampler2DMSArray_Type(new Type(String("sampler2DMSArray")))
+    , fSampler1DShadow_Type(new Type(String("sampler1DShadow")))
+    , fSampler2DShadow_Type(new Type(String("sampler2DShadow")))
+    , fSamplerCubeShadow_Type(new Type(String("samplerCubeShadow")))
+    , fSampler2DRectShadow_Type(new Type(String("sampler2DRectShadow")))
+    , fSampler1DArrayShadow_Type(new Type(String("sampler1DArrayShadow")))
+    , fSampler2DArrayShadow_Type(new Type(String("sampler2DArrayShadow")))
+    , fSamplerCubeArrayShadow_Type(new Type(String("samplerCubeArrayShadow")))
 
     // Related to below FIXME, gsampler*s don't currently expand to cover integer case.
-    , fISampler2D_Type(new Type(SkString("isampler2D"), SpvDim2D, false, false, false, true))
+    , fISampler2D_Type(new Type(String("isampler2D"), SpvDim2D, false, false, false, true))
 
     // FIXME express these as "gimage2D" that expand to image2D, iimage2D, and uimage2D.
-    , fImage2D_Type(new Type(SkString("image2D"), SpvDim2D, false, false, false, true))
-    , fIImage2D_Type(new Type(SkString("iimage2D"), SpvDim2D, false, false, false, true))
+    , fImage2D_Type(new Type(String("image2D"), SpvDim2D, false, false, false, true))
+    , fIImage2D_Type(new Type(String("iimage2D"), SpvDim2D, false, false, false, true))
 
     // FIXME express these as "gsubpassInput" that expand to subpassInput, isubpassInput,
     // and usubpassInput.
-    , fSubpassInput_Type(new Type(SkString("subpassInput"), SpvDimSubpassData, false, false,
+    , fSubpassInput_Type(new Type(String("subpassInput"), SpvDimSubpassData, false, false,
                                            false, false))
-    , fSubpassInputMS_Type(new Type(SkString("subpassInputMS"), SpvDimSubpassData, false, false,
+    , fSubpassInputMS_Type(new Type(String("subpassInputMS"), SpvDimSubpassData, false, false,
                                              true, false))
 
     // FIXME figure out what we're supposed to do with the gsampler et al. types)
-    , fGSampler1D_Type(new Type(SkString("$gsampler1D"), static_type(*fSampler1D_Type)))
-    , fGSampler2D_Type(new Type(SkString("$gsampler2D"), static_type(*fSampler2D_Type)))
-    , fGSampler3D_Type(new Type(SkString("$gsampler3D"), static_type(*fSampler3D_Type)))
-    , fGSamplerCube_Type(new Type(SkString("$gsamplerCube"), static_type(*fSamplerCube_Type)))
-    , fGSampler2DRect_Type(new Type(SkString("$gsampler2DRect"), static_type(*fSampler2DRect_Type)))
-    , fGSampler1DArray_Type(new Type(SkString("$gsampler1DArray"),
+    , fGSampler1D_Type(new Type(String("$gsampler1D"), static_type(*fSampler1D_Type)))
+    , fGSampler2D_Type(new Type(String("$gsampler2D"), static_type(*fSampler2D_Type)))
+    , fGSampler3D_Type(new Type(String("$gsampler3D"), static_type(*fSampler3D_Type)))
+    , fGSamplerCube_Type(new Type(String("$gsamplerCube"), static_type(*fSamplerCube_Type)))
+    , fGSampler2DRect_Type(new Type(String("$gsampler2DRect"), static_type(*fSampler2DRect_Type)))
+    , fGSampler1DArray_Type(new Type(String("$gsampler1DArray"),
                                      static_type(*fSampler1DArray_Type)))
-    , fGSampler2DArray_Type(new Type(SkString("$gsampler2DArray"),
+    , fGSampler2DArray_Type(new Type(String("$gsampler2DArray"),
                                      static_type(*fSampler2DArray_Type)))
-    , fGSamplerCubeArray_Type(new Type(SkString("$gsamplerCubeArray"),
+    , fGSamplerCubeArray_Type(new Type(String("$gsamplerCubeArray"),
                                        static_type(*fSamplerCubeArray_Type)))
-    , fGSamplerBuffer_Type(new Type(SkString("$gsamplerBuffer"), static_type(*fSamplerBuffer_Type)))
-    , fGSampler2DMS_Type(new Type(SkString("$gsampler2DMS"), static_type(*fSampler2DMS_Type)))
-    , fGSampler2DMSArray_Type(new Type(SkString("$gsampler2DMSArray"),
+    , fGSamplerBuffer_Type(new Type(String("$gsamplerBuffer"), static_type(*fSamplerBuffer_Type)))
+    , fGSampler2DMS_Type(new Type(String("$gsampler2DMS"), static_type(*fSampler2DMS_Type)))
+    , fGSampler2DMSArray_Type(new Type(String("$gsampler2DMSArray"),
                                        static_type(*fSampler2DMSArray_Type)))
-    , fGSampler2DArrayShadow_Type(new Type(SkString("$gsampler2DArrayShadow"),
+    , fGSampler2DArrayShadow_Type(new Type(String("$gsampler2DArrayShadow"),
                                            static_type(*fSampler2DArrayShadow_Type)))
-    , fGSamplerCubeArrayShadow_Type(new Type(SkString("$gsamplerCubeArrayShadow"),
+    , fGSamplerCubeArrayShadow_Type(new Type(String("$gsamplerCubeArrayShadow"),
                                              static_type(*fSamplerCubeArrayShadow_Type)))
-    , fGenType_Type(new Type(SkString("$genType"), { fFloat_Type.get(), fVec2_Type.get(),
+    , fGenType_Type(new Type(String("$genType"), { fFloat_Type.get(), fVec2_Type.get(),
                                                      fVec3_Type.get(), fVec4_Type.get() }))
-    , fGenDType_Type(new Type(SkString("$genDType"), { fDouble_Type.get(), fDVec2_Type.get(),
+    , fGenDType_Type(new Type(String("$genDType"), { fDouble_Type.get(), fDVec2_Type.get(),
                                                        fDVec3_Type.get(), fDVec4_Type.get() }))
-    , fGenIType_Type(new Type(SkString("$genIType"), { fInt_Type.get(), fIVec2_Type.get(),
+    , fGenIType_Type(new Type(String("$genIType"), { fInt_Type.get(), fIVec2_Type.get(),
                                                        fIVec3_Type.get(), fIVec4_Type.get() }))
-    , fGenUType_Type(new Type(SkString("$genUType"), { fUInt_Type.get(), fUVec2_Type.get(),
+    , fGenUType_Type(new Type(String("$genUType"), { fUInt_Type.get(), fUVec2_Type.get(),
                                                        fUVec3_Type.get(), fUVec4_Type.get() }))
-    , fGenBType_Type(new Type(SkString("$genBType"), { fBool_Type.get(), fBVec2_Type.get(),
+    , fGenBType_Type(new Type(String("$genBType"), { fBool_Type.get(), fBVec2_Type.get(),
                                                        fBVec3_Type.get(), fBVec4_Type.get() }))
-    , fMat_Type(new Type(SkString("$mat"), { fMat2x2_Type.get(), fMat2x3_Type.get(),
+    , fMat_Type(new Type(String("$mat"), { fMat2x2_Type.get(), fMat2x3_Type.get(),
                                              fMat2x4_Type.get(), fMat3x2_Type.get(),
                                              fMat3x3_Type.get(), fMat3x4_Type.get(),
                                              fMat4x2_Type.get(), fMat4x3_Type.get(),
@@ -135,21 +136,21 @@
                                              fDMat3x2_Type.get(), fDMat3x3_Type.get(),
                                              fDMat3x4_Type.get(), fDMat4x2_Type.get(),
                                              fDMat4x3_Type.get(), fDMat4x4_Type.get() }))
-    , fVec_Type(new Type(SkString("$vec"), { fInvalid_Type.get(), fVec2_Type.get(),
+    , fVec_Type(new Type(String("$vec"), { fInvalid_Type.get(), fVec2_Type.get(),
                                              fVec3_Type.get(), fVec4_Type.get() }))
-    , fGVec_Type(new Type(SkString("$gvec")))
-    , fGVec2_Type(new Type(SkString("$gvec2")))
-    , fGVec3_Type(new Type(SkString("$gvec3")))
-    , fGVec4_Type(new Type(SkString("$gvec4"), static_type(*fVec4_Type)))
-    , fDVec_Type(new Type(SkString("$dvec"), { fInvalid_Type.get(), fDVec2_Type.get(),
+    , fGVec_Type(new Type(String("$gvec")))
+    , fGVec2_Type(new Type(String("$gvec2")))
+    , fGVec3_Type(new Type(String("$gvec3")))
+    , fGVec4_Type(new Type(String("$gvec4"), static_type(*fVec4_Type)))
+    , fDVec_Type(new Type(String("$dvec"), { fInvalid_Type.get(), fDVec2_Type.get(),
                                               fDVec3_Type.get(), fDVec4_Type.get() }))
-    , fIVec_Type(new Type(SkString("$ivec"), { fInvalid_Type.get(), fIVec2_Type.get(),
+    , fIVec_Type(new Type(String("$ivec"), { fInvalid_Type.get(), fIVec2_Type.get(),
                                                fIVec3_Type.get(), fIVec4_Type.get() }))
-    , fUVec_Type(new Type(SkString("$uvec"), { fInvalid_Type.get(), fUVec2_Type.get(),
+    , fUVec_Type(new Type(String("$uvec"), { fInvalid_Type.get(), fUVec2_Type.get(),
                                                fUVec3_Type.get(), fUVec4_Type.get() }))
-    , fBVec_Type(new Type(SkString("$bvec"), { fInvalid_Type.get(), fBVec2_Type.get(),
+    , fBVec_Type(new Type(String("$bvec"), { fInvalid_Type.get(), fBVec2_Type.get(),
                                                fBVec3_Type.get(), fBVec4_Type.get() }))
-    , fSkCaps_Type(new Type(SkString("$sk_Caps")))
+    , fSkCaps_Type(new Type(String("$sk_Caps")))
     , fDefined_Expression(new Defined(*fInvalid_Type)) {}
 
     static std::vector<const Type*> static_type(const Type& t) {
@@ -269,19 +270,23 @@
 
     const std::unique_ptr<Type> fSkCaps_Type;
 
-    // dummy expression used to mark that a variable has a value during dataflow analysis (when it 
+    // dummy expression used to mark that a variable has a value during dataflow analysis (when it
     // could have several different values, or the analyzer is otherwise unable to assign it a
     // specific expression)
     const std::unique_ptr<Expression> fDefined_Expression;
 
-private:    
+private:
     class Defined : public Expression {
     public:
         Defined(const Type& type)
         : INHERITED(Position(), kDefined_Kind, type) {}
 
-        virtual SkString description() const override {
-            return SkString("<defined>");
+        virtual String description() const override {
+            return String("<defined>");
+        }
+        
+        bool hasSideEffects() const override {
+            return false;
         }
 
         typedef Expression INHERITED;
diff --git a/src/sksl/SkSLErrorReporter.h b/src/sksl/SkSLErrorReporter.h
index 85d386d..172e488 100644
--- a/src/sksl/SkSLErrorReporter.h
+++ b/src/sksl/SkSLErrorReporter.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ERRORREPORTER
 #define SKSL_ERRORREPORTER
 
@@ -20,10 +20,10 @@
     virtual ~ErrorReporter() {}
 
     void error(Position position, const char* msg) {
-        this->error(position, SkString(msg));
+        this->error(position, String(msg));
     }
 
-    virtual void error(Position position, SkString msg) = 0;
+    virtual void error(Position position, String msg) = 0;
 
     virtual int errorCount() = 0;
 };
diff --git a/src/sksl/SkSLFileOutputStream.h b/src/sksl/SkSLFileOutputStream.h
new file mode 100644
index 0000000..7eba19f
--- /dev/null
+++ b/src/sksl/SkSLFileOutputStream.h
@@ -0,0 +1,76 @@
+/*
+ * 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 SKSL_FILEOUTPUTSTREAM
+#define SKSL_FILEOUTPUTSTREAM
+
+#include "SkSLOutputStream.h"
+#include "SkSLUtil.h"
+#include <stdio.h>
+
+namespace SkSL {
+
+class FileOutputStream : public OutputStream {
+public:
+    FileOutputStream(const char* name) {
+        fFile = fopen(name, "w");
+    }
+
+    ~FileOutputStream() override {
+        ASSERT(!fOpen);
+    }
+
+    bool isValid() const override {
+        return nullptr != fFile;
+    }
+
+    void write8(uint8_t b) override {
+        ASSERT(fOpen);
+        if (isValid()) {
+            if (EOF == fputc(b, fFile)) {
+                fFile = nullptr;
+            }
+        }
+    }
+
+    void writeText(const char* s) override {
+        ASSERT(fOpen);
+        if (isValid()) {
+            if (EOF == fputs(s, fFile)) {
+                fFile = nullptr;
+            }
+        }
+    }
+
+    void write(const void* s, size_t size) override {
+        if (isValid()) {
+            size_t written = fwrite(s, 1, size, fFile);
+            if (written != size) {
+                fFile = nullptr;
+            }
+        }
+    }
+
+    bool close() {
+        fOpen = false;
+        if (isValid() && fclose(fFile)) {
+            fFile = nullptr;
+            return false;
+        }
+        return true;
+    }
+
+private:
+    bool fOpen = true;
+    FILE *fFile;
+
+    typedef OutputStream INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index a19de8f..c3168e3 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -7,8 +7,6 @@
 
 #include "SkSLGLSLCodeGenerator.h"
 
-#include "string.h"
-
 #include "GLSL.std.450.h"
 
 #include "SkSLCompiler.h"
@@ -16,6 +14,7 @@
 #include "ir/SkSLExtension.h"
 #include "ir/SkSLIndexExpression.h"
 #include "ir/SkSLModifiersDeclaration.h"
+#include "ir/SkSLNop.h"
 #include "ir/SkSLVariableReference.h"
 
 namespace SkSL {
@@ -35,15 +34,15 @@
 
 void GLSLCodeGenerator::writeLine(const char* s) {
     this->write(s);
-    fOut->writeText("\n");
+    fOut->write8('\n');
     fAtLineStart = true;
 }
 
-void GLSLCodeGenerator::write(const SkString& s) {
+void GLSLCodeGenerator::write(const String& s) {
     this->write(s.c_str());
 }
 
-void GLSLCodeGenerator::writeLine(const SkString& s) {
+void GLSLCodeGenerator::writeLine(const String& s) {
     this->writeLine(s.c_str());
 }
 
@@ -137,8 +136,8 @@
 // Tegra3 compiler bug.
 void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) {
     ASSERT(!fProgram.fSettings.fCaps->canUseMinAndAbsTogether());
-    SkString tmpVar1 = "minAbsHackVar" + to_string(fVarCount++);
-    SkString tmpVar2 = "minAbsHackVar" + to_string(fVarCount++);
+    String tmpVar1 = "minAbsHackVar" + to_string(fVarCount++);
+    String tmpVar2 = "minAbsHackVar" + to_string(fVarCount++);
     this->fFunctionHeader += "    " + absExpr.fType.name() + " " + tmpVar1 + ";\n";
     this->fFunctionHeader += "    " + otherExpr.fType.name() + " " + tmpVar2 + ";\n";
     this->write("((" + tmpVar1 + " = ");
@@ -407,11 +406,12 @@
         case Token::BITWISEANDEQ: // fall through
         case Token::BITWISEXOREQ: // fall through
         case Token::BITWISEOREQ:  return GLSLCodeGenerator::kAssignment_Precedence;
+        case Token::COMMA:        return GLSLCodeGenerator::kSequence_Precedence;
         default: ABORT("unsupported binary operator");
     }
 }
 
-void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b, 
+void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                               Precedence parentPrecedence) {
     Precedence precedence = get_binary_precedence(b.fOperator);
     if (precedence >= parentPrecedence) {
@@ -425,7 +425,7 @@
     }
 }
 
-void GLSLCodeGenerator::writeTernaryExpression(const TernaryExpression& t, 
+void GLSLCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
                                                Precedence parentPrecedence) {
     if (kTernary_Precedence >= parentPrecedence) {
         this->write("(");
@@ -440,7 +440,7 @@
     }
 }
 
-void GLSLCodeGenerator::writePrefixExpression(const PrefixExpression& p, 
+void GLSLCodeGenerator::writePrefixExpression(const PrefixExpression& p,
                                               Precedence parentPrecedence) {
     if (kPrefix_Precedence >= parentPrecedence) {
         this->write("(");
@@ -452,7 +452,7 @@
     }
 }
 
-void GLSLCodeGenerator::writePostfixExpression(const PostfixExpression& p, 
+void GLSLCodeGenerator::writePostfixExpression(const PostfixExpression& p,
                                                Precedence parentPrecedence) {
     if (kPostfix_Precedence >= parentPrecedence) {
         this->write("(");
@@ -507,21 +507,17 @@
     this->writeLine(") {");
 
     fFunctionHeader = "";
-    SkWStream* oldOut = fOut;
-    SkDynamicMemoryWStream buffer;
+    OutputStream* oldOut = fOut;
+    StringStream buffer;
     fOut = &buffer;
     fIndentation++;
-    for (const auto& s : f.fBody->fStatements) {
-        this->writeStatement(*s);
-        this->writeLine();
-    }
+    this->writeStatements(((Block&) *f.fBody).fStatements);
     fIndentation--;
     this->writeLine("}");
 
     fOut = oldOut;
     this->write(fFunctionHeader);
-    sk_sp<SkData> data(buffer.detachAsData());
-    this->write(SkString((const char*) data->data(), data->size()));
+    this->write(String(buffer.data(), buffer.size()));
 }
 
 void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers,
@@ -532,7 +528,7 @@
     if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
         this->write("noperspective ");
     }
-    SkString layout = modifiers.fLayout.description();
+    String layout = modifiers.fLayout.description();
     if (layout.size()) {
         this->write(layout + " ");
     }
@@ -623,13 +619,20 @@
 
 void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
     ASSERT(decl.fVars.size() > 0);
-    this->writeModifiers(decl.fVars[0].fVar->fModifiers, global);
-    this->writeType(decl.fBaseType);
-    SkString separator(" ");
-    for (const auto& var : decl.fVars) {
-        ASSERT(var.fVar->fModifiers == decl.fVars[0].fVar->fModifiers);
-        this->write(separator);
-        separator = SkString(", ");
+    bool wroteType = false;
+    for (const auto& stmt : decl.fVars) {
+        if (stmt->fKind == Statement::kNop_Kind) {
+            continue;
+        }
+        VarDeclaration& var = (VarDeclaration&) *stmt;
+        if (wroteType) {
+            this->write(", ");
+        } else {
+            this->writeModifiers(var.fVar->fModifiers, global);
+            this->writeType(decl.fBaseType);
+            this->write(" ");
+            wroteType = true;
+        }
         this->write(var.fVar->fName);
         for (const auto& size : var.fSizes) {
             this->write("[");
@@ -651,7 +654,9 @@
             fFoundImageDecl = true;
         }
     }
-    this->write(";");
+    if (wroteType) {
+        this->write(";");
+    }
 }
 
 void GLSLCodeGenerator::writeStatement(const Statement& s) {
@@ -663,7 +668,7 @@
             this->writeExpression(*((ExpressionStatement&) s).fExpression, kTopLevel_Precedence);
             this->write(";");
             break;
-        case Statement::kReturn_Kind: 
+        case Statement::kReturn_Kind:
             this->writeReturnStatement((ReturnStatement&) s);
             break;
         case Statement::kVarDeclarations_Kind:
@@ -693,18 +698,27 @@
         case Statement::kDiscard_Kind:
             this->write("discard;");
             break;
+        case Statement::kNop_Kind:
+            this->write(";");
+            break;
         default:
             ABORT("unsupported statement: %s", s.description().c_str());
     }
 }
 
+void GLSLCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
+    for (const auto& s : statements) {
+        if (!s->isEmpty()) {
+            this->writeStatement(*s);
+            this->writeLine();
+        }
+    }
+}
+
 void GLSLCodeGenerator::writeBlock(const Block& b) {
     this->writeLine("{");
     fIndentation++;
-    for (const auto& s : b.fStatements) {
-        this->writeStatement(*s);
-        this->writeLine();
-    }
+    this->writeStatements(b.fStatements);
     fIndentation--;
     this->write("}");
 }
@@ -787,7 +801,7 @@
 }
 
 bool GLSLCodeGenerator::generateCode() {
-    SkWStream* rawOut = fOut;
+    OutputStream* rawOut = fOut;
     fOut = &fHeader;
     fProgramKind = fProgram.fKind;
     this->write(fProgram.fSettings.fCaps->versionDeclString());
@@ -797,7 +811,7 @@
             this->writeExtension((Extension&) *e);
         }
     }
-    SkDynamicMemoryWStream body;
+    StringStream body;
     fOut = &body;
     if (fProgram.fSettings.fCaps->usesPrecisionModifiers()) {
         this->write("precision ");
@@ -824,7 +838,8 @@
             case ProgramElement::kVar_Kind: {
                 VarDeclarations& decl = (VarDeclarations&) *e;
                 if (decl.fVars.size() > 0) {
-                    int builtin = decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin;
+                    int builtin =
+                               ((VarDeclaration&) *decl.fVars[0]).fVar->fModifiers.fLayout.fBuiltin;
                     if (builtin == -1) {
                         // normal var
                         this->writeVarDeclarations(decl, true);
@@ -857,8 +872,8 @@
     }
     fOut = nullptr;
 
-    write_data(*fHeader.detachAsData(), *rawOut);
-    write_data(*body.detachAsData(), *rawOut);
+    write_stringstream(fHeader, *rawOut);
+    write_stringstream(body, *rawOut);
     return true;
 }
 
diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h
index 907c305..032b70e 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/SkSLGLSLCodeGenerator.h
@@ -12,7 +12,6 @@
 #include <tuple>
 #include <unordered_map>
 
-#include "SkStream.h"
 #include "SkSLCodeGenerator.h"
 #include "ir/SkSLBinaryExpression.h"
 #include "ir/SkSLBoolLiteral.h"
@@ -69,11 +68,11 @@
         kTernary_Precedence        = 15,
         kAssignment_Precedence     = 16,
         kSequence_Precedence       = 17,
-        kTopLevel_Precedence       = 18
+        kTopLevel_Precedence       = kSequence_Precedence
     };
 
     GLSLCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
-                      SkWStream* out)
+                      OutputStream* out)
     : INHERITED(program, errors, out)
     , fContext(*context) {}
 
@@ -86,9 +85,9 @@
 
     void writeLine(const char* s);
 
-    void write(const SkString& s);
+    void write(const String& s);
 
-    void writeLine(const SkString& s);
+    void writeLine(const String& s);
 
     void writeType(const Type& type);
 
@@ -97,7 +96,7 @@
     void writeInterfaceBlock(const InterfaceBlock& intf);
 
     void writeFunctionStart(const FunctionDeclaration& f);
-    
+
     void writeFunctionDeclaration(const FunctionDeclaration& f);
 
     void writeFunction(const FunctionDefinition& f);
@@ -146,6 +145,8 @@
 
     void writeStatement(const Statement& s);
 
+    void writeStatements(const std::vector<std::unique_ptr<Statement>>& statements);
+
     void writeBlock(const Block& b);
 
     void writeIfStatement(const IfStatement& stmt);
@@ -161,8 +162,8 @@
     void writeReturnStatement(const ReturnStatement& r);
 
     const Context& fContext;
-    SkDynamicMemoryWStream fHeader;
-    SkString fFunctionHeader;
+    StringStream fHeader;
+    String fFunctionHeader;
     Program::Kind fProgramKind;
     int fVarCount = 0;
     int fIndentation = 0;
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index ae2a90f..f85ea10 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -108,15 +108,15 @@
 , fErrors(errorReporter) {}
 
 void IRGenerator::pushSymbolTable() {
-    fSymbolTable.reset(new SymbolTable(std::move(fSymbolTable), fErrors));
+    fSymbolTable.reset(new SymbolTable(std::move(fSymbolTable), &fErrors));
 }
 
 void IRGenerator::popSymbolTable() {
     fSymbolTable = fSymbolTable->fParent;
 }
 
-static void fill_caps(const GrShaderCaps& caps, std::unordered_map<SkString, CapValue>* capsMap) {
-#define CAP(name) capsMap->insert(std::make_pair(SkString(#name), CapValue(caps.name())));
+static void fill_caps(const SKSL_CAPS_CLASS& caps, std::unordered_map<String, CapValue>* capsMap) {
+#define CAP(name) capsMap->insert(std::make_pair(String(#name), CapValue(caps.name())));
     CAP(fbFetchSupport);
     CAP(fbFetchNeedsCustomOutput);
     CAP(bindlessTextureSupport);
@@ -210,7 +210,7 @@
 
 std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTVarDeclarations& decl,
                                                                      Variable::Storage storage) {
-    std::vector<VarDeclaration> variables;
+    std::vector<std::unique_ptr<VarDeclaration>> variables;
     const Type* baseType = this->convertType(*decl.fType);
     if (!baseType) {
         return nullptr;
@@ -224,7 +224,7 @@
                 if (!size) {
                     return nullptr;
                 }
-                SkString name = type->fName;
+                String name = type->fName;
                 int64_t count;
                 if (size->fKind == Expression::kIntLiteral_Kind) {
                     count = ((IntLiteral&) *size).fValue;
@@ -254,8 +254,9 @@
                 return nullptr;
             }
             value = this->coerce(std::move(value), *type);
+            var->fWriteCount = 1;
         }
-        if (storage == Variable::kGlobal_Storage && varDecl.fName == SkString("sk_FragColor") &&
+        if (storage == Variable::kGlobal_Storage && varDecl.fName == String("sk_FragColor") &&
             (*fSymbolTable)[varDecl.fName]) {
             // already defined, ignore
         } else if (storage == Variable::kGlobal_Storage && (*fSymbolTable)[varDecl.fName] &&
@@ -265,7 +266,8 @@
             Variable* old = (Variable*) (*fSymbolTable)[varDecl.fName];
             old->fModifiers = var->fModifiers;
         } else {
-            variables.emplace_back(var.get(), std::move(sizes), std::move(value));
+            variables.emplace_back(new VarDeclaration(var.get(), std::move(sizes),
+                                                      std::move(value)));
             fSymbolTable->add(varDecl.fName, std::move(var));
         }
     }
@@ -309,7 +311,7 @@
                                                         fSymbolTable));
         }
     }
-    return std::unique_ptr<Statement>(new IfStatement(s.fPosition, std::move(test),
+    return std::unique_ptr<Statement>(new IfStatement(s.fPosition, s.fIsStatic, std::move(test),
                                                       std::move(ifTrue), std::move(ifFalse)));
 }
 
@@ -427,8 +429,8 @@
         cases.emplace_back(new SwitchCase(c->fPosition, std::move(caseValue),
                                           std::move(statements)));
     }
-    return std::unique_ptr<Statement>(new SwitchStatement(s.fPosition, std::move(value),
-                                                          std::move(cases)));
+    return std::unique_ptr<Statement>(new SwitchStatement(s.fPosition, s.fIsStatic,
+                                                          std::move(value), std::move(cases)));
 }
 
 std::unique_ptr<Statement> IRGenerator::convertExpressionStatement(
@@ -501,12 +503,12 @@
         }
         for (int j = (int) param->fSizes.size() - 1; j >= 0; j--) {
             int size = param->fSizes[j];
-            SkString name = type->name() + "[" + to_string(size) + "]";
+            String name = type->name() + "[" + to_string(size) + "]";
             Type* newType = new Type(std::move(name), Type::kArray_Kind, *type, size);
             fSymbolTable->takeOwnership(newType);
             type = newType;
         }
-        SkString name = param->fName;
+        String name = param->fName;
         Position pos = param->fPosition;
         Variable* var = new Variable(pos, param->fModifiers, std::move(name), *type,
                                      Variable::kParameter_Storage);
@@ -542,7 +544,8 @@
                 }
                 if (match) {
                     if (*returnType != other->fReturnType) {
-                        FunctionDeclaration newDecl(f.fPosition, f.fName, parameters, *returnType);
+                        FunctionDeclaration newDecl(f.fPosition, f.fModifiers, f.fName, parameters,
+                                                    *returnType);
                         fErrors.error(f.fPosition, "functions '" + newDecl.description() +
                                                    "' and '" + other->description() +
                                                    "' differ only in return type");
@@ -570,6 +573,7 @@
     if (!decl) {
         // couldn't find an existing declaration
         auto newDecl = std::unique_ptr<FunctionDeclaration>(new FunctionDeclaration(f.fPosition,
+                                                                                    f.fModifiers,
                                                                                     f.fName,
                                                                                     parameters,
                                                                                     *returnType));
@@ -590,6 +594,8 @@
         if (!body) {
             return nullptr;
         }
+        // conservatively assume all user-defined functions have side effects
+        ((Modifiers&) decl->fModifiers).fFlags |= Modifiers::kHasSideEffects_Flag;
         return std::unique_ptr<FunctionDefinition>(new FunctionDefinition(f.fPosition, *decl,
                                                                           std::move(body)));
     }
@@ -600,6 +606,7 @@
     std::shared_ptr<SymbolTable> old = fSymbolTable;
     AutoSymbolTable table(this);
     std::vector<Type::Field> fields;
+    bool haveRuntimeArray = false;
     for (size_t i = 0; i < intf.fDeclarations.size(); i++) {
         std::unique_ptr<VarDeclarations> decl = this->convertVarDeclarations(
                                                                          *intf.fDeclarations[i],
@@ -607,20 +614,31 @@
         if (!decl) {
             return nullptr;
         }
-        for (const auto& var : decl->fVars) {
-            fields.push_back(Type::Field(var.fVar->fModifiers, var.fVar->fName,
-                                         &var.fVar->fType));
-            if (var.fValue) {
+        for (const auto& stmt : decl->fVars) {
+            VarDeclaration& vd = (VarDeclaration&) *stmt;
+            if (haveRuntimeArray) {
+                fErrors.error(decl->fPosition,
+                              "only the last entry in an interface block may be a runtime-sized "
+                              "array");
+            }
+            fields.push_back(Type::Field(vd.fVar->fModifiers, vd.fVar->fName,
+                                         &vd.fVar->fType));
+            if (vd.fValue) {
                 fErrors.error(decl->fPosition,
                               "initializers are not permitted on interface block fields");
             }
-            if (var.fVar->fModifiers.fFlags & (Modifiers::kIn_Flag |
-                                               Modifiers::kOut_Flag |
-                                               Modifiers::kUniform_Flag |
-                                               Modifiers::kConst_Flag)) {
+            if (vd.fVar->fModifiers.fFlags & (Modifiers::kIn_Flag |
+                                                Modifiers::kOut_Flag |
+                                                Modifiers::kUniform_Flag |
+                                                Modifiers::kBuffer_Flag |
+                                                Modifiers::kConst_Flag)) {
                 fErrors.error(decl->fPosition,
                               "interface block fields may not have storage qualifiers");
             }
+            if (vd.fVar->fType.kind() == Type::kArray_Kind &&
+                vd.fVar->fType.columns() == -1) {
+                haveRuntimeArray = true;
+            }
         }
     }
     Type* type = new Type(intf.fPosition, intf.fTypeName, fields);
@@ -632,7 +650,7 @@
             if (!converted) {
                 return nullptr;
             }
-            SkString name = type->fName;
+            String name = type->fName;
             int64_t count;
             if (converted->fKind == Expression::kIntLiteral_Kind) {
                 count = ((IntLiteral&) *converted).fValue;
@@ -665,7 +683,8 @@
                                                                        (int) i)));
         }
     }
-    return std::unique_ptr<InterfaceBlock>(new InterfaceBlock(intf.fPosition, *var,
+    return std::unique_ptr<InterfaceBlock>(new InterfaceBlock(intf.fPosition,
+                                                              var,
                                                               intf.fTypeName,
                                                               intf.fInstanceName,
                                                               std::move(sizes),
@@ -676,7 +695,7 @@
     const Symbol* result = (*fSymbolTable)[type.fName];
     if (result && result->fKind == Symbol::kType_Kind) {
         for (int size : type.fSizes) {
-            SkString name = result->fName + "[";
+            String name = result->fName + "[";
             if (size != -1) {
                 name += to_string(size);
             }
@@ -899,10 +918,15 @@
         case Token::MINUS:   // fall through
         case Token::MINUSEQ: // fall through
         case Token::SLASH:   // fall through
-        case Token::SLASHEQ:
+        case Token::SLASHEQ: // fall through
             isLogical = false;
             validMatrixOrVectorOp = true;
             break;
+        case Token::COMMA:
+            *outLeftType = &left;
+            *outRightType = &right;
+            *outResultType = &right;
+            return true;
         default:
             isLogical = false;
             validMatrixOrVectorOp = false;
@@ -943,6 +967,9 @@
 std::unique_ptr<Expression> IRGenerator::constantFold(const Expression& left,
                                                       Token::Kind op,
                                                       const Expression& right) const {
+    if (!left.isConstant() || !right.isConstant()) {
+        return nullptr;
+    }
     // Note that we expressly do not worry about precision and overflow here -- we use the maximum
     // precision to calculate the results and hope the result makes sense. The plan is to move the
     // Skia caps into SkSL, so we have access to all of them including the precisions of the various
@@ -966,9 +993,9 @@
         int64_t leftVal  = ((IntLiteral&) left).fValue;
         int64_t rightVal = ((IntLiteral&) right).fValue;
         switch (op) {
-            case Token::PLUS:       return RESULT(Int,  +);
-            case Token::MINUS:      return RESULT(Int,  -);
-            case Token::STAR:       return RESULT(Int,  *);
+            case Token::PLUS:       return RESULT(Int, +);
+            case Token::MINUS:      return RESULT(Int, -);
+            case Token::STAR:       return RESULT(Int, *);
             case Token::SLASH:
                 if (rightVal) {
                     return RESULT(Int, /);
@@ -1009,15 +1036,57 @@
                 }
                 fErrors.error(right.fPosition, "division by zero");
                 return nullptr;
-            case Token::EQEQ:       return RESULT(Bool,  ==);
-            case Token::NEQ:        return RESULT(Bool,  !=);
-            case Token::GT:         return RESULT(Bool,  >);
-            case Token::GTEQ:       return RESULT(Bool,  >=);
-            case Token::LT:         return RESULT(Bool,  <);
-            case Token::LTEQ:       return RESULT(Bool,  <=);
+            case Token::EQEQ:       return RESULT(Bool, ==);
+            case Token::NEQ:        return RESULT(Bool, !=);
+            case Token::GT:         return RESULT(Bool, >);
+            case Token::GTEQ:       return RESULT(Bool, >=);
+            case Token::LT:         return RESULT(Bool, <);
+            case Token::LTEQ:       return RESULT(Bool, <=);
             default:                return nullptr;
         }
     }
+    if (left.fType.kind() == Type::kVector_Kind &&
+        left.fType.componentType() == *fContext.fFloat_Type &&
+        left.fType == right.fType) {
+        ASSERT(left.fKind  == Expression::kConstructor_Kind);
+        ASSERT(right.fKind == Expression::kConstructor_Kind);
+        std::vector<std::unique_ptr<Expression>> args;
+        #define RETURN_VEC_COMPONENTWISE_RESULT(op)                                    \
+            for (int i = 0; i < left.fType.columns(); i++) {                           \
+                float value = ((Constructor&) left).getFVecComponent(i) op             \
+                              ((Constructor&) right).getFVecComponent(i);              \
+                args.emplace_back(new FloatLiteral(fContext, Position(), value));      \
+            }                                                                          \
+            return std::unique_ptr<Expression>(new Constructor(Position(), left.fType, \
+                                                               std::move(args)));
+        switch (op) {
+            case Token::EQEQ:
+                return std::unique_ptr<Expression>(new BoolLiteral(fContext, Position(),
+                                                            left.compareConstant(fContext, right)));
+            case Token::NEQ:
+                return std::unique_ptr<Expression>(new BoolLiteral(fContext, Position(),
+                                                           !left.compareConstant(fContext, right)));
+            case Token::PLUS:  RETURN_VEC_COMPONENTWISE_RESULT(+);
+            case Token::MINUS: RETURN_VEC_COMPONENTWISE_RESULT(-);
+            case Token::STAR:  RETURN_VEC_COMPONENTWISE_RESULT(*);
+            case Token::SLASH: RETURN_VEC_COMPONENTWISE_RESULT(/);
+            default:           return nullptr;
+        }
+    }
+    if (left.fType.kind() == Type::kMatrix_Kind &&
+        right.fType.kind() == Type::kMatrix_Kind &&
+        left.fKind == right.fKind) {
+        switch (op) {
+            case Token::EQEQ:
+                return std::unique_ptr<Expression>(new BoolLiteral(fContext, Position(),
+                                                            left.compareConstant(fContext, right)));
+            case Token::NEQ:
+                return std::unique_ptr<Expression>(new BoolLiteral(fContext, Position(),
+                                                           !left.compareConstant(fContext, right)));
+            default:
+                return nullptr;
+        }
+    }
     #undef RESULT
     return nullptr;
 }
@@ -1111,11 +1180,42 @@
                                                              std::move(ifFalse)));
 }
 
+// scales the texture coordinates by the texture size for sampling rectangle textures.
+// For vec2 coordinates, implements the transformation:
+//     texture(sampler, coord) -> texture(sampler, textureSize(sampler) * coord)
+// For vec3 coordinates, implements the transformation:
+//     texture(sampler, coord) -> texture(sampler, vec3(textureSize(sampler), 1.0) * coord))
+void IRGenerator::fixRectSampling(std::vector<std::unique_ptr<Expression>>& arguments) {
+    ASSERT(arguments.size() == 2);
+    ASSERT(arguments[0]->fType == *fContext.fSampler2DRect_Type);
+    ASSERT(arguments[0]->fKind == Expression::kVariableReference_Kind);
+    const Variable& sampler = ((VariableReference&) *arguments[0]).fVariable;
+    const Symbol* textureSizeSymbol = (*fSymbolTable)["textureSize"];
+    ASSERT(textureSizeSymbol->fKind == Symbol::kFunctionDeclaration_Kind);
+    const FunctionDeclaration& textureSize = (FunctionDeclaration&) *textureSizeSymbol;
+    std::vector<std::unique_ptr<Expression>> sizeArguments;
+    sizeArguments.emplace_back(new VariableReference(Position(), sampler));
+    std::unique_ptr<Expression> vec2Size = call(Position(), textureSize, std::move(sizeArguments));
+    const Type& type = arguments[1]->fType;
+    std::unique_ptr<Expression> scale;
+    if (type == *fContext.fVec2_Type) {
+        scale = std::move(vec2Size);
+    } else {
+        ASSERT(type == *fContext.fVec3_Type);
+        std::vector<std::unique_ptr<Expression>> vec3Arguments;
+        vec3Arguments.push_back(std::move(vec2Size));
+        vec3Arguments.emplace_back(new FloatLiteral(fContext, Position(), 1.0));
+        scale.reset(new Constructor(Position(), *fContext.fVec3_Type, std::move(vec3Arguments)));
+    }
+    arguments[1].reset(new BinaryExpression(Position(), std::move(scale), Token::STAR,
+                                            std::move(arguments[1]), type));
+}
+
 std::unique_ptr<Expression> IRGenerator::call(Position position,
                                               const FunctionDeclaration& function,
                                               std::vector<std::unique_ptr<Expression>> arguments) {
     if (function.fParameters.size() != arguments.size()) {
-        SkString msg = "call to '" + function.fName + "' expected " +
+        String msg = "call to '" + function.fName + "' expected " +
                                  to_string((uint64_t) function.fParameters.size()) +
                                  " argument";
         if (function.fParameters.size() != 1) {
@@ -1128,8 +1228,8 @@
     std::vector<const Type*> types;
     const Type* returnType;
     if (!function.determineFinalTypes(arguments, &types, &returnType)) {
-        SkString msg = "no match for " + function.fName + "(";
-        SkString separator;
+        String msg = "no match for " + function.fName + "(";
+        String separator;
         for (size_t i = 0; i < arguments.size(); i++) {
             msg += separator;
             separator = ", ";
@@ -1145,9 +1245,14 @@
             return nullptr;
         }
         if (arguments[i] && (function.fParameters[i]->fModifiers.fFlags & Modifiers::kOut_Flag)) {
-            this->markWrittenTo(*arguments[i], true);
+            this->markWrittenTo(*arguments[i], 
+                                function.fParameters[i]->fModifiers.fFlags & Modifiers::kIn_Flag);
         }
     }
+    if (function.fBuiltin && function.fName == "texture" &&
+        arguments[0]->fType == *fContext.fSampler2DRect_Type) {
+        this->fixRectSampling(arguments);
+    }
     return std::unique_ptr<FunctionCall>(new FunctionCall(position, *returnType, function,
                                                           std::move(arguments)));
 }
@@ -1207,8 +1312,8 @@
         if (best) {
             return this->call(position, *best, std::move(arguments));
         }
-        SkString msg = "no match for " + ref->fFunctions[0]->fName + "(";
-        SkString separator;
+        String msg = "no match for " + ref->fFunctions[0]->fName + "(";
+        String separator;
         for (size_t i = 0; i < arguments.size(); i++) {
             msg += separator;
             separator = ", ";
@@ -1465,7 +1570,7 @@
 }
 
 std::unique_ptr<Expression> IRGenerator::convertField(std::unique_ptr<Expression> base,
-                                                      const SkString& field) {
+                                                      const String& field) {
     auto fields = base->fType.fields();
     for (size_t i = 0; i < fields.size(); i++) {
         if (fields[i].fName == field) {
@@ -1478,7 +1583,7 @@
 }
 
 std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expression> base,
-                                                        const SkString& fields) {
+                                                        const String& fields) {
     if (base->fType.kind() != Type::kVector_Kind) {
         fErrors.error(base->fPosition, "cannot swizzle type '" + base->fType.description() + "'");
         return nullptr;
@@ -1516,7 +1621,7 @@
                 }
                 // fall through
             default:
-                fErrors.error(base->fPosition, SkStringPrintf("invalid swizzle component '%c'",
+                fErrors.error(base->fPosition, String::printf("invalid swizzle component '%c'",
                                                               fields[i]));
                 return nullptr;
         }
@@ -1529,7 +1634,7 @@
     return std::unique_ptr<Expression>(new Swizzle(fContext, std::move(base), swizzleComponents));
 }
 
-std::unique_ptr<Expression> IRGenerator::getCap(Position position, SkString name) {
+std::unique_ptr<Expression> IRGenerator::getCap(Position position, String name) {
     auto found = fCapsMap.find(name);
     if (found == fCapsMap.end()) {
         fErrors.error(position, "unknown capability flag '" + name + "'");
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index fb79cda..d4a6846 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -154,22 +154,23 @@
     Modifiers convertModifiers(const Modifiers& m);
     std::unique_ptr<Expression> convertPrefixExpression(const ASTPrefixExpression& expression);
     std::unique_ptr<Statement> convertReturn(const ASTReturnStatement& r);
-    std::unique_ptr<Expression> getCap(Position position, SkString name);
+    std::unique_ptr<Expression> getCap(Position position, String name);
     std::unique_ptr<Expression> convertSuffixExpression(const ASTSuffixExpression& expression);
     std::unique_ptr<Expression> convertField(std::unique_ptr<Expression> base,
-                                             const SkString& field);
+                                             const String& field);
     std::unique_ptr<Expression> convertSwizzle(std::unique_ptr<Expression> base,
-                                               const SkString& fields);
+                                               const String& fields);
     std::unique_ptr<Expression> convertTernaryExpression(const ASTTernaryExpression& expression);
     std::unique_ptr<Statement> convertVarDeclarationStatement(const ASTVarDeclarationStatement& s);
     std::unique_ptr<Statement> convertWhile(const ASTWhileStatement& w);
 
+    void fixRectSampling(std::vector<std::unique_ptr<Expression>>& arguments);
     void checkValid(const Expression& expr);
     void markWrittenTo(const Expression& expr, bool readWrite);
 
     const FunctionDeclaration* fCurrentFunction;
     const Program::Settings* fSettings;
-    std::unordered_map<SkString, CapValue> fCapsMap;
+    std::unordered_map<String, CapValue> fCapsMap;
     std::shared_ptr<SymbolTable> fSymbolTable;
     int fLoopLevel;
     int fSwitchLevel;
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
index 46e9c18..1461bf9 100644
--- a/src/sksl/SkSLMain.cpp
+++ b/src/sksl/SkSLMain.cpp
@@ -8,7 +8,7 @@
 #include "stdio.h"
 #include <fstream>
 #include "SkSLCompiler.h"
-#include "GrContextOptions.h"
+#include "SkSLFileOutputStream.h"
 
 /**
  * Very simple standalone executable to facilitate testing.
@@ -34,17 +34,15 @@
     std::ifstream in(argv[1]);
     std::string stdText((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
-    SkString text(stdText.c_str());
+    SkSL::String text(stdText.c_str());
     if (in.rdstate()) {
         printf("error reading '%s'\n", argv[1]);
         exit(2);
     }
     SkSL::Program::Settings settings;
-    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
-    settings.fCaps = caps.get();
-    SkString name(argv[2]);
+    SkSL::String name(argv[2]);
     if (name.endsWith(".spirv")) {
-        SkFILEWStream out(argv[2]);
+        SkSL::FileOutputStream out(argv[2]);
         SkSL::Compiler compiler;
         if (!out.isValid()) {
             printf("error writing '%s'\n", argv[2]);
@@ -55,8 +53,12 @@
             printf("%s", compiler.errorText().c_str());
             exit(3);
         }
+        if (!out.close()) {
+            printf("error writing '%s'\n", argv[2]);
+            exit(4);
+        }
     } else if (name.endsWith(".glsl")) {
-        SkFILEWStream out(argv[2]);
+        SkSL::FileOutputStream out(argv[2]);
         SkSL::Compiler compiler;
         if (!out.isValid()) {
             printf("error writing '%s'\n", argv[2]);
@@ -67,6 +69,10 @@
             printf("%s", compiler.errorText().c_str());
             exit(3);
         }
+        if (!out.close()) {
+            printf("error writing '%s'\n", argv[2]);
+            exit(4);
+        }
     } else {
         printf("expected output filename to end with '.spirv' or '.glsl'");
     }
diff --git a/src/sksl/SkSLMemoryLayout.h b/src/sksl/SkSLMemoryLayout.h
index 95a292f..61712ec 100644
--- a/src/sksl/SkSLMemoryLayout.h
+++ b/src/sksl/SkSLMemoryLayout.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKIASL_MEMORYLAYOUT
 #define SKIASL_MEMORYLAYOUT
 
@@ -19,14 +19,14 @@
         k430_Standard
     };
 
-    MemoryLayout(Standard std) 
+    MemoryLayout(Standard std)
     : fStd(std) {}
 
     static size_t vector_alignment(size_t componentSize, int columns) {
         return componentSize * (columns + columns % 2);
     }
 
-    /** 
+    /**
      * Rounds up to the nearest multiple of 16 if in std140, otherwise returns the parameter
      * unchanged (std140 requires various things to be rounded up to the nearest multiple of 16,
      * std430 does not).
@@ -50,7 +50,7 @@
             case Type::kVector_Kind:
                 return vector_alignment(this->size(type.componentType()), type.columns());
             case Type::kMatrix_Kind:
-                return this->roundUpIfNeeded(vector_alignment(this->size(type.componentType()), 
+                return this->roundUpIfNeeded(vector_alignment(this->size(type.componentType()),
                                                               type.rows()));
             case Type::kArray_Kind:
                 return this->roundUpIfNeeded(this->alignment(type.componentType()));
@@ -65,7 +65,7 @@
                 return this->roundUpIfNeeded(result);
             }
             default:
-                ABORT(("cannot determine size of type " + type.name()).c_str());
+                ABORT("cannot determine size of type %s", type.name().c_str());
         }
     }
 
@@ -111,12 +111,12 @@
                     total += this->size(*f.fType);
                 }
                 size_t alignment = this->alignment(type);
-                ASSERT(!type.fields().size() || 
+                ASSERT(!type.fields().size() ||
                        (0 == alignment % this->alignment(*type.fields()[0].fType)));
                 return (total + alignment - 1) & ~(alignment - 1);
             }
             default:
-                ABORT(("cannot determine size of type " + type.name()).c_str());
+                ABORT("cannot determine size of type %s", type.name().c_str());
         }
     }
 
diff --git a/src/sksl/SkSLOutputStream.h b/src/sksl/SkSLOutputStream.h
new file mode 100644
index 0000000..62be61e
--- /dev/null
+++ b/src/sksl/SkSLOutputStream.h
@@ -0,0 +1,36 @@
+/*
+ * 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 SKSL_OUTPUTSTREAM
+#define SKSL_OUTPUTSTREAM
+
+#include "SkSLString.h"
+
+namespace SkSL {
+
+class OutputStream {
+public:
+    virtual bool isValid() const {
+        return true;
+    }
+
+    virtual void write8(uint8_t b) = 0;
+
+    virtual void writeText(const char* s) = 0;
+
+    virtual void write(const void* s, size_t size) = 0;
+
+    void writeString(String s) {
+        this->write(s.c_str(), s.size());
+    }
+
+    virtual ~OutputStream() {}
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index e24685a..5e8ec63 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -12,6 +12,9 @@
 #define register
 #include "disable_flex_warnings.h"
 #include "lex.sksl.c"
+static_assert(YY_FLEX_MAJOR_VERSION * 10000 + YY_FLEX_MINOR_VERSION * 100 +
+              YY_FLEX_SUBMINOR_VERSION >= 20601,
+              "we require Flex 2.6.1 or better for security reasons");
 #undef register
 #ifdef __clang__
 #pragma clang diagnostic pop
@@ -79,7 +82,7 @@
 
     bool checkValid() {
         if (fParser->fDepth > MAX_PARSE_DEPTH) {
-            fParser->error(fParser->peek().fPosition, SkString("exceeded max parse depth"));
+            fParser->error(fParser->peek().fPosition, String("exceeded max parse depth"));
             return false;
         }
         return true;
@@ -89,8 +92,8 @@
     Parser* fParser;
 };
 
-Parser::Parser(SkString text, SymbolTable& types, ErrorReporter& errors)
-: fPushback(Position(-1, -1), Token::INVALID_TOKEN, SkString())
+Parser::Parser(String text, SymbolTable& types, ErrorReporter& errors)
+: fPushback(Position(-1, -1), Token::INVALID_TOKEN, String())
 , fTypes(types)
 , fErrors(errors) {
     sksllex_init(&fScanner);
@@ -150,21 +153,25 @@
         return result;
     }
     int token = sksllex(fScanner);
-    SkString text;
+    String text;
     switch ((Token::Kind) token) {
         case Token::IDENTIFIER:    // fall through
         case Token::INT_LITERAL:   // fall through
         case Token::FLOAT_LITERAL: // fall through
         case Token::DIRECTIVE:
-            text = SkString(skslget_text(fScanner));
+            text = String(skslget_text(fScanner));
             break;
         default:
 #ifdef SK_DEBUG
-            text = SkString(skslget_text(fScanner));
+            text = String(skslget_text(fScanner));
 #endif
             break;
     }
-    return Token(Position(skslget_lineno(fScanner), -1), (Token::Kind) token, text);
+    Position p = Position(skslget_lineno(fScanner), -1);
+    if (token == Token::INVALID_TOKEN) {
+        this->error(p, "invalid token: '" + text + "'");
+    }
+    return Token(p, (Token::Kind) token, text);
 }
 
 void Parser::pushback(Token t) {
@@ -177,12 +184,23 @@
     return fPushback;
 }
 
-
-bool Parser::expect(Token::Kind kind, const char* expected, Token* result) {
-    return this->expect(kind, SkString(expected), result);
+bool Parser::checkNext(Token::Kind kind, Token* result) {
+    Token next = this->nextToken();
+    if (next.fKind == kind) {
+        if (result) {
+            *result = next;
+        }
+        return true;
+    }
+    this->pushback(next);
+    return false;
 }
 
-bool Parser::expect(Token::Kind kind, SkString expected, Token* result) {
+bool Parser::expect(Token::Kind kind, const char* expected, Token* result) {
+    return this->expect(kind, String(expected), result);
+}
+
+bool Parser::expect(Token::Kind kind, String expected, Token* result) {
     Token next = this->nextToken();
     if (next.fKind == kind) {
         if (result) {
@@ -201,14 +219,14 @@
 }
 
 void Parser::error(Position p, const char* msg) {
-    this->error(p, SkString(msg));
+    this->error(p, String(msg));
 }
 
-void Parser::error(Position p, SkString msg) {
+void Parser::error(Position p, String msg) {
     fErrors.error(p, msg);
 }
 
-bool Parser::isType(SkString name) {
+bool Parser::isType(String name) {
     return nullptr != fTypes[name];
 }
 
@@ -298,16 +316,14 @@
     if (!type) {
         return nullptr;
     }
-    if (type->fKind == ASTType::kStruct_Kind && peek().fKind == Token::SEMICOLON) {
-        this->nextToken();
+    if (type->fKind == ASTType::kStruct_Kind && this->checkNext(Token::SEMICOLON)) {
         return nullptr;
     }
     Token name;
     if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
         return nullptr;
     }
-    if (!modifiers.fFlags && this->peek().fKind == Token::LPAREN) {
-        this->nextToken();
+    if (this->checkNext(Token::LPAREN)) {
         std::vector<std::unique_ptr<ASTParameter>> parameters;
         while (this->peek().fKind != Token::RPAREN) {
             if (parameters.size() > 0) {
@@ -323,15 +339,15 @@
         }
         this->nextToken();
         std::unique_ptr<ASTBlock> body;
-        if (this->peek().fKind == Token::SEMICOLON) {
-            this->nextToken();
-        } else {
+        if (!this->checkNext(Token::SEMICOLON)) {
             body = this->block();
             if (!body) {
                 return nullptr;
             }
         }
-        return std::unique_ptr<ASTDeclaration>(new ASTFunction(name.fPosition, std::move(type),
+        return std::unique_ptr<ASTDeclaration>(new ASTFunction(name.fPosition,
+                                                               modifiers,
+                                                               std::move(type),
                                                                std::move(name.fText),
                                                                std::move(parameters),
                                                                std::move(body)));
@@ -380,7 +396,7 @@
                     return nullptr;
                 }
                 uint64_t columns = ((ASTIntLiteral&) *var.fSizes[i]).fValue;
-                SkString name = type->name() + "[" + to_string(columns) + "]";
+                String name = type->name() + "[" + to_string(columns) + "]";
                 type = new Type(name, Type::kArray_Kind, *type, (int) columns);
                 fTypes.takeOwnership((Type*) type);
             }
@@ -404,8 +420,8 @@
     if (!type) {
         return nullptr;
     }
-    if (peek().fKind == Token::IDENTIFIER) {
-        Token name = this->nextToken();
+    Token name;
+    if (this->checkNext(Token::IDENTIFIER, &name)) {
         std::unique_ptr<ASTVarDeclarations> result = this->varDeclarationEnd(modifiers,
                                                                              std::move(type),
                                                                              std::move(name.fText));
@@ -423,17 +439,15 @@
     return nullptr;
 }
 
-/* (LBRACKET expression? RBRACKET)* (EQ expression)? (COMMA IDENTIFER
-   (LBRACKET expression? RBRACKET)* (EQ expression)?)* SEMICOLON */
+/* (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)? (COMMA IDENTIFER
+   (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)?)* SEMICOLON */
 std::unique_ptr<ASTVarDeclarations> Parser::varDeclarationEnd(Modifiers mods,
                                                               std::unique_ptr<ASTType> type,
-                                                              SkString name) {
+                                                              String name) {
     std::vector<ASTVarDeclaration> vars;
     std::vector<std::unique_ptr<ASTExpression>> currentVarSizes;
-    while (this->peek().fKind == Token::LBRACKET) {
-        this->nextToken();
-        if (this->peek().fKind == Token::RBRACKET) {
-            this->nextToken();
+    while (this->checkNext(Token::LBRACKET)) {
+        if (this->checkNext(Token::RBRACKET)) {
             currentVarSizes.push_back(nullptr);
         } else {
             std::unique_ptr<ASTExpression> size(this->expression());
@@ -447,26 +461,22 @@
         }
     }
     std::unique_ptr<ASTExpression> value;
-    if (this->peek().fKind == Token::EQ) {
-        this->nextToken();
-        value = this->expression();
+    if (this->checkNext(Token::EQ)) {
+        value = this->assignmentExpression();
         if (!value) {
             return nullptr;
         }
     }
     vars.emplace_back(std::move(name), std::move(currentVarSizes), std::move(value));
-    while (this->peek().fKind == Token::COMMA) {
-        this->nextToken();
+    while (this->checkNext(Token::COMMA)) {
         Token name;
         if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
             return nullptr;
         }
         currentVarSizes.clear();
         value.reset();
-        while (this->peek().fKind == Token::LBRACKET) {
-            this->nextToken();
-            if (this->peek().fKind == Token::RBRACKET) {
-                this->nextToken();
+        while (this->checkNext(Token::LBRACKET)) {
+            if (this->checkNext(Token::RBRACKET)) {
                 currentVarSizes.push_back(nullptr);
             } else {
                 std::unique_ptr<ASTExpression> size(this->expression());
@@ -479,9 +489,8 @@
                 }
             }
         }
-        if (this->peek().fKind == Token::EQ) {
-            this->nextToken();
-            value = this->expression();
+        if (this->checkNext(Token::EQ)) {
+            value = this->assignmentExpression();
             if (!value) {
                 return nullptr;
             }
@@ -498,7 +507,7 @@
 
 /* modifiers type IDENTIFIER (LBRACKET INT_LITERAL RBRACKET)? */
 std::unique_ptr<ASTParameter> Parser::parameter() {
-    Modifiers modifiers = this->modifiersWithDefaults(Modifiers::kIn_Flag);
+    Modifiers modifiers = this->modifiersWithDefaults(0);
     std::unique_ptr<ASTType> type = this->type();
     if (!type) {
         return nullptr;
@@ -508,8 +517,7 @@
         return nullptr;
     }
     std::vector<int> sizes;
-    while (this->peek().fKind == Token::LBRACKET) {
-        this->nextToken();
+    while (this->checkNext(Token::LBRACKET)) {
         Token sizeToken;
         if (!this->expect(Token::INT_LITERAL, "a positive integer", &sizeToken)) {
             return nullptr;
@@ -553,8 +561,7 @@
     Layout::Primitive primitive = Layout::kUnspecified_Primitive;
     int maxVertices = -1;
     int invocations = -1;
-    if (this->peek().fKind == Token::LAYOUT) {
-        this->nextToken();
+    if (this->checkNext(Token::LAYOUT)) {
         if (!this->expect(Token::LPAREN, "'('")) {
             return Layout(location, offset, binding, index, set, builtin, inputAttachmentIndex,
                           originUpperLeft, overrideCoverage, blendSupportAllEquations, format,
@@ -635,8 +642,7 @@
                 this->error(t.fPosition, ("'" + t.fText +
                                           "' is not a valid layout qualifier").c_str());
             }
-            if (this->peek().fKind == Token::RPAREN) {
-                this->nextToken();
+            if (this->checkNext(Token::RPAREN)) {
                 break;
             }
             if (!this->expect(Token::COMMA, "','")) {
@@ -650,7 +656,7 @@
 }
 
 /* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
-            READONLY | WRITEONLY | COHERENT | VOLATILE | RESTRICT)* */
+            READONLY | WRITEONLY | COHERENT | VOLATILE | RESTRICT | BUFFER)* */
 Modifiers Parser::modifiers() {
     Layout layout = this->layout();
     int flags = 0;
@@ -718,6 +724,14 @@
                 this->nextToken();
                 flags |= Modifiers::kRestrict_Flag;
                 break;
+            case Token::BUFFER:
+                this->nextToken();
+                flags |= Modifiers::kBuffer_Flag;
+                break;
+            case Token::HASSIDEEFFECTS:
+                this->nextToken();
+                flags |= Modifiers::kHasSideEffects_Flag;
+                break;
             default:
                 return Modifiers(layout, flags);
         }
@@ -736,7 +750,8 @@
 std::unique_ptr<ASTStatement> Parser::statement() {
     Token start = this->peek();
     switch (start.fKind) {
-        case Token::IF:
+        case Token::IF: // fall through
+        case Token::STATIC_IF:
             return this->ifStatement();
         case Token::FOR:
             return this->forStatement();
@@ -744,7 +759,8 @@
             return this->doStatement();
         case Token::WHILE:
             return this->whileStatement();
-        case Token::SWITCH:
+        case Token::SWITCH: // fall through
+        case Token::STATIC_SWITCH:
             return this->switchStatement();
         case Token::RETURN:
             return this->returnStatement();
@@ -796,8 +812,7 @@
         return nullptr;
     }
     std::vector<int> sizes;
-    while (this->peek().fKind == Token::LBRACKET) {
-        this->expect(Token::LBRACKET, "'['");
+    while (this->checkNext(Token::LBRACKET)) {
         if (this->peek().fKind != Token::RBRACKET) {
             int64_t i;
             if (this->intLiteral(&i)) {
@@ -837,12 +852,10 @@
         decls.push_back(std::move(decl));
     }
     this->nextToken();
-    SkString instanceName;
     std::vector<std::unique_ptr<ASTExpression>> sizes;
-    if (this->peek().fKind == Token::IDENTIFIER) {
-        instanceName = this->nextToken().fText;
-        while (this->peek().fKind == Token::LBRACKET) {
-            this->expect(Token::LBRACKET, "'['");
+    Token instanceName;
+    if (this->checkNext(Token::IDENTIFIER, &instanceName)) {
+        while (this->checkNext(Token::LBRACKET)) {
             if (this->peek().fKind != Token::RBRACKET) {
                 std::unique_ptr<ASTExpression> size = this->expression();
                 if (!size) {
@@ -858,14 +871,15 @@
     this->expect(Token::SEMICOLON, "';'");
     return std::unique_ptr<ASTDeclaration>(new ASTInterfaceBlock(name.fPosition, mods,
                                                                  name.fText, std::move(decls),
-                                                                 std::move(instanceName),
+                                                                 std::move(instanceName.fText),
                                                                  std::move(sizes)));
 }
 
 /* IF LPAREN expression RPAREN statement (ELSE statement)? */
 std::unique_ptr<ASTIfStatement> Parser::ifStatement() {
     Token start;
-    if (!this->expect(Token::IF, "'if'", &start)) {
+    bool isStatic = this->checkNext(Token::STATIC_IF, &start);
+    if (!isStatic && !this->expect(Token::IF, "'if'", &start)) {
         return nullptr;
     }
     if (!this->expect(Token::LPAREN, "'('")) {
@@ -883,14 +897,15 @@
         return nullptr;
     }
     std::unique_ptr<ASTStatement> ifFalse;
-    if (this->peek().fKind == Token::ELSE) {
-        this->nextToken();
+    if (this->checkNext(Token::ELSE)) {
         ifFalse = this->statement();
         if (!ifFalse) {
             return nullptr;
         }
     }
-    return std::unique_ptr<ASTIfStatement>(new ASTIfStatement(start.fPosition, std::move(test),
+    return std::unique_ptr<ASTIfStatement>(new ASTIfStatement(start.fPosition,
+                                                              isStatic,
+                                                              std::move(test),
                                                               std::move(ifTrue),
                                                               std::move(ifFalse)));
 }
@@ -980,7 +995,8 @@
 /* SWITCH LPAREN expression RPAREN LBRACE switchCase* (DEFAULT COLON statement*)? RBRACE */
 std::unique_ptr<ASTStatement> Parser::switchStatement() {
     Token start;
-    if (!this->expect(Token::SWITCH, "'switch'", &start)) {
+    bool isStatic = this->checkNext(Token::STATIC_SWITCH, &start);
+    if (!isStatic && !this->expect(Token::SWITCH, "'switch'", &start)) {
         return nullptr;
     }
     if (!this->expect(Token::LPAREN, "'('")) {
@@ -1008,7 +1024,7 @@
     // parts of the compiler may rely upon this assumption.
     if (this->peek().fKind == Token::DEFAULT) {
         Token defaultStart;
-        SkAssertResult(this->expect(Token::DEFAULT, "'default'", &defaultStart));
+        ASSERT_RESULT(this->expect(Token::DEFAULT, "'default'", &defaultStart));
         if (!this->expect(Token::COLON, "':'")) {
             return nullptr;
         }
@@ -1027,6 +1043,7 @@
         return nullptr;
     }
     return std::unique_ptr<ASTStatement>(new ASTSwitchStatement(start.fPosition,
+                                                                isStatic,
                                                                 std::move(value),
                                                                 std::move(cases)));
 }
@@ -1205,7 +1222,24 @@
     if (!depth.checkValid()) {
         return nullptr;
     }
-    return this->assignmentExpression();
+    return this->commaExpression();
+}
+
+/* assignmentExpression (COMMA assignmentExpression)* */
+std::unique_ptr<ASTExpression> Parser::commaExpression() {
+    std::unique_ptr<ASTExpression> result = this->assignmentExpression();
+    if (!result) {
+        return nullptr;
+    }
+    Token t;
+    while (this->checkNext(Token::COMMA, &t)) {
+        std::unique_ptr<ASTExpression> right = this->commaExpression();
+        if (!right) {
+            return nullptr;
+        }
+        result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+    }
+    return result;
 }
 
 /* ternaryExpression ((EQEQ | STAREQ | SLASHEQ | PERCENTEQ | PLUSEQ | MINUSEQ | SHLEQ | SHREQ |
@@ -1254,8 +1288,7 @@
     if (!result) {
         return nullptr;
     }
-    if (this->peek().fKind == Token::QUESTION) {
-        Token question = this->nextToken();
+    if (this->checkNext(Token::QUESTION)) {
         std::unique_ptr<ASTExpression> trueExpr = this->expression();
         if (!trueExpr) {
             return nullptr;
@@ -1277,8 +1310,8 @@
     if (!result) {
         return nullptr;
     }
-    while (this->peek().fKind == Token::LOGICALOR) {
-        Token t = this->nextToken();
+    Token t;
+    while (this->checkNext(Token::LOGICALOR, &t)) {
         std::unique_ptr<ASTExpression> right = this->logicalXorExpression();
         if (!right) {
             return nullptr;
@@ -1294,8 +1327,8 @@
     if (!result) {
         return nullptr;
     }
-    while (this->peek().fKind == Token::LOGICALXOR) {
-        Token t = this->nextToken();
+    Token t;
+    while (this->checkNext(Token::LOGICALXOR, &t)) {
         std::unique_ptr<ASTExpression> right = this->logicalAndExpression();
         if (!right) {
             return nullptr;
@@ -1311,8 +1344,8 @@
     if (!result) {
         return nullptr;
     }
-    while (this->peek().fKind == Token::LOGICALAND) {
-        Token t = this->nextToken();
+    Token t;
+    while (this->checkNext(Token::LOGICALAND, &t)) {
         std::unique_ptr<ASTExpression> right = this->bitwiseOrExpression();
         if (!right) {
             return nullptr;
@@ -1328,8 +1361,8 @@
     if (!result) {
         return nullptr;
     }
-    while (this->peek().fKind == Token::BITWISEOR) {
-        Token t = this->nextToken();
+    Token t;
+    while (this->checkNext(Token::BITWISEOR, &t)) {
         std::unique_ptr<ASTExpression> right = this->bitwiseXorExpression();
         if (!right) {
             return nullptr;
@@ -1345,8 +1378,8 @@
     if (!result) {
         return nullptr;
     }
-    while (this->peek().fKind == Token::BITWISEXOR) {
-        Token t = this->nextToken();
+    Token t;
+    while (this->checkNext(Token::BITWISEXOR, &t)) {
         std::unique_ptr<ASTExpression> right = this->bitwiseAndExpression();
         if (!right) {
             return nullptr;
@@ -1362,8 +1395,8 @@
     if (!result) {
         return nullptr;
     }
-    while (this->peek().fKind == Token::BITWISEAND) {
-        Token t = this->nextToken();
+    Token t;
+    while (this->checkNext(Token::BITWISEAND, &t)) {
         std::unique_ptr<ASTExpression> right = this->equalityExpression();
         if (!right) {
             return nullptr;
@@ -1549,8 +1582,7 @@
     Token next = this->nextToken();
     switch (next.fKind) {
         case Token::LBRACKET: {
-            if (this->peek().fKind == Token::RBRACKET) {
-                this->nextToken();
+            if (this->checkNext(Token::RBRACKET)) {
                 return std::unique_ptr<ASTSuffix>(new ASTIndexSuffix(next.fPosition));
             }
             std::unique_ptr<ASTExpression> e = this->expression();
@@ -1562,7 +1594,7 @@
         }
         case Token::DOT: {
             Position pos = this->peek().fPosition;
-            SkString text;
+            String text;
             if (this->identifier(&text)) {
                 return std::unique_ptr<ASTSuffix>(new ASTFieldSuffix(pos, std::move(text)));
             }
@@ -1572,15 +1604,14 @@
             std::vector<std::unique_ptr<ASTExpression>> parameters;
             if (this->peek().fKind != Token::RPAREN) {
                 for (;;) {
-                    std::unique_ptr<ASTExpression> expr = this->expression();
+                    std::unique_ptr<ASTExpression> expr = this->assignmentExpression();
                     if (!expr) {
                         return nullptr;
                     }
                     parameters.push_back(std::move(expr));
-                    if (this->peek().fKind != Token::COMMA) {
+                    if (!this->checkNext(Token::COMMA)) {
                         break;
                     }
-                    this->nextToken();
                 }
             }
             this->expect(Token::RPAREN, "')' to complete function parameters");
@@ -1607,7 +1638,7 @@
     Token t = this->peek();
     switch (t.fKind) {
         case Token::IDENTIFIER: {
-            SkString text;
+            String text;
             if (this->identifier(&text)) {
                 result.reset(new ASTIdentifier(t.fPosition, std::move(text)));
             }
@@ -1688,7 +1719,7 @@
 }
 
 /* IDENTIFIER */
-bool Parser::identifier(SkString* dest) {
+bool Parser::identifier(String* dest) {
     Token t;
     if (this->expect(Token::IDENTIFIER, "identifier", &t)) {
         *dest = t.fText;
diff --git a/src/sksl/SkSLParser.h b/src/sksl/SkSLParser.h
index 9be017c..2f55b34 100644
--- a/src/sksl/SkSLParser.h
+++ b/src/sksl/SkSLParser.h
@@ -51,7 +51,7 @@
  */
 class Parser {
 public:
-    Parser(SkString text, SymbolTable& types, ErrorReporter& errors);
+    Parser(String text, SymbolTable& types, ErrorReporter& errors);
 
     ~Parser();
 
@@ -81,6 +81,12 @@
     Token peek();
 
     /**
+     * Checks to see if the next token is of the specified type. If so, stores it in result (if
+     * result is non-null) and returns true. Otherwise, pushes it back and returns false.
+     */
+    bool checkNext(Token::Kind kind, Token* result = nullptr);
+
+    /**
      * Reads the next token and generates an error if it is not the expected type. The 'expected'
      * string is part of the error message, which reads:
      *
@@ -90,16 +96,16 @@
      * Returns true if the read token was as expected, false otherwise.
      */
     bool expect(Token::Kind kind, const char* expected, Token* result = nullptr);
-    bool expect(Token::Kind kind, SkString expected, Token* result = nullptr);
+    bool expect(Token::Kind kind, String expected, Token* result = nullptr);
 
     void error(Position p, const char* msg);
-    void error(Position p, SkString msg);
-   
+    void error(Position p, String msg);
+
     /**
      * Returns true if the 'name' identifier refers to a type name. For instance, isType("int") will
      * always return true.
      */
-    bool isType(SkString name);
+    bool isType(String name);
 
     // these functions parse individual grammar rules from the current parse position; you probably
     // don't need to call any of these outside of the parser. The function declarations in the .cpp
@@ -119,12 +125,12 @@
 
     std::unique_ptr<ASTVarDeclarations> varDeclarationEnd(Modifiers modifiers,
                                                           std::unique_ptr<ASTType> type,
-                                                          SkString name);
+                                                          String name);
 
     std::unique_ptr<ASTParameter> parameter();
 
     int layoutInt();
-   
+
     Layout layout();
 
     Modifiers modifiers();
@@ -163,8 +169,10 @@
 
     std::unique_ptr<ASTExpression> expression();
 
+    std::unique_ptr<ASTExpression> commaExpression();
+
     std::unique_ptr<ASTExpression> assignmentExpression();
-   
+
     std::unique_ptr<ASTExpression> ternaryExpression();
 
     std::unique_ptr<ASTExpression> logicalOrExpression();
@@ -203,7 +211,7 @@
 
     bool boolLiteral(bool* dest);
 
-    bool identifier(SkString* dest);
+    bool identifier(String* dest);
 
     void* fScanner;
     void* fLayoutScanner;
diff --git a/src/sksl/SkSLPosition.h b/src/sksl/SkSLPosition.h
index b1841c5..83cfe82 100644
--- a/src/sksl/SkSLPosition.h
+++ b/src/sksl/SkSLPosition.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_POSITION
 #define SKSL_POSITION
 
@@ -17,15 +17,15 @@
  * ignored.
  */
 struct Position {
-    Position() 
+    Position()
     : fLine(-1)
     , fColumn(-1) {}
-    
+
     Position(int line, int column)
     : fLine(line)
     , fColumn(column) {}
 
-    SkString description() const {
+    String description() const {
         return to_string(fLine);
     }
 
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 93ec4ce..e3786a7 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -4,10 +4,8 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
-#include "SkSLSPIRVCodeGenerator.h"
 
-#include "string.h"
+#include "SkSLSPIRVCodeGenerator.h"
 
 #include "GLSL.std.450.h"
 
@@ -34,112 +32,111 @@
 #define SPECIAL(x) std::make_tuple(kSpecial_IntrinsicKind, k ## x ## _SpecialIntrinsic, \
                                    k ## x ## _SpecialIntrinsic, k ## x ## _SpecialIntrinsic, \
                                    k ## x ## _SpecialIntrinsic)
-    fIntrinsicMap[SkString("round")]         = ALL_GLSL(Round);
-    fIntrinsicMap[SkString("roundEven")]     = ALL_GLSL(RoundEven);
-    fIntrinsicMap[SkString("trunc")]         = ALL_GLSL(Trunc);
-    fIntrinsicMap[SkString("abs")]           = BY_TYPE_GLSL(FAbs, SAbs, SAbs);
-    fIntrinsicMap[SkString("sign")]          = BY_TYPE_GLSL(FSign, SSign, SSign);
-    fIntrinsicMap[SkString("floor")]         = ALL_GLSL(Floor);
-    fIntrinsicMap[SkString("ceil")]          = ALL_GLSL(Ceil);
-    fIntrinsicMap[SkString("fract")]         = ALL_GLSL(Fract);
-    fIntrinsicMap[SkString("radians")]       = ALL_GLSL(Radians);
-    fIntrinsicMap[SkString("degrees")]       = ALL_GLSL(Degrees);
-    fIntrinsicMap[SkString("sin")]           = ALL_GLSL(Sin);
-    fIntrinsicMap[SkString("cos")]           = ALL_GLSL(Cos);
-    fIntrinsicMap[SkString("tan")]           = ALL_GLSL(Tan);
-    fIntrinsicMap[SkString("asin")]          = ALL_GLSL(Asin);
-    fIntrinsicMap[SkString("acos")]          = ALL_GLSL(Acos);
-    fIntrinsicMap[SkString("atan")]          = SPECIAL(Atan);
-    fIntrinsicMap[SkString("sinh")]          = ALL_GLSL(Sinh);
-    fIntrinsicMap[SkString("cosh")]          = ALL_GLSL(Cosh);
-    fIntrinsicMap[SkString("tanh")]          = ALL_GLSL(Tanh);
-    fIntrinsicMap[SkString("asinh")]         = ALL_GLSL(Asinh);
-    fIntrinsicMap[SkString("acosh")]         = ALL_GLSL(Acosh);
-    fIntrinsicMap[SkString("atanh")]         = ALL_GLSL(Atanh);
-    fIntrinsicMap[SkString("pow")]           = ALL_GLSL(Pow);
-    fIntrinsicMap[SkString("exp")]           = ALL_GLSL(Exp);
-    fIntrinsicMap[SkString("log")]           = ALL_GLSL(Log);
-    fIntrinsicMap[SkString("exp2")]          = ALL_GLSL(Exp2);
-    fIntrinsicMap[SkString("log2")]          = ALL_GLSL(Log2);
-    fIntrinsicMap[SkString("sqrt")]          = ALL_GLSL(Sqrt);
-    fIntrinsicMap[SkString("inversesqrt")]   = ALL_GLSL(InverseSqrt);
-    fIntrinsicMap[SkString("determinant")]   = ALL_GLSL(Determinant);
-    fIntrinsicMap[SkString("matrixInverse")] = ALL_GLSL(MatrixInverse);
-    fIntrinsicMap[SkString("mod")]           = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpFMod,
-                                                               SpvOpSMod, SpvOpUMod, SpvOpUndef);
-    fIntrinsicMap[SkString("min")]           = BY_TYPE_GLSL(FMin, SMin, UMin);
-    fIntrinsicMap[SkString("max")]           = BY_TYPE_GLSL(FMax, SMax, UMax);
-    fIntrinsicMap[SkString("clamp")]         = BY_TYPE_GLSL(FClamp, SClamp, UClamp);
-    fIntrinsicMap[SkString("dot")]           = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDot,
-                                                               SpvOpUndef, SpvOpUndef, SpvOpUndef);
-    fIntrinsicMap[SkString("mix")]           = ALL_GLSL(FMix);
-    fIntrinsicMap[SkString("step")]          = ALL_GLSL(Step);
-    fIntrinsicMap[SkString("smoothstep")]    = ALL_GLSL(SmoothStep);
-    fIntrinsicMap[SkString("fma")]           = ALL_GLSL(Fma);
-    fIntrinsicMap[SkString("frexp")]         = ALL_GLSL(Frexp);
-    fIntrinsicMap[SkString("ldexp")]         = ALL_GLSL(Ldexp);
+    fIntrinsicMap[String("round")]         = ALL_GLSL(Round);
+    fIntrinsicMap[String("roundEven")]     = ALL_GLSL(RoundEven);
+    fIntrinsicMap[String("trunc")]         = ALL_GLSL(Trunc);
+    fIntrinsicMap[String("abs")]           = BY_TYPE_GLSL(FAbs, SAbs, SAbs);
+    fIntrinsicMap[String("sign")]          = BY_TYPE_GLSL(FSign, SSign, SSign);
+    fIntrinsicMap[String("floor")]         = ALL_GLSL(Floor);
+    fIntrinsicMap[String("ceil")]          = ALL_GLSL(Ceil);
+    fIntrinsicMap[String("fract")]         = ALL_GLSL(Fract);
+    fIntrinsicMap[String("radians")]       = ALL_GLSL(Radians);
+    fIntrinsicMap[String("degrees")]       = ALL_GLSL(Degrees);
+    fIntrinsicMap[String("sin")]           = ALL_GLSL(Sin);
+    fIntrinsicMap[String("cos")]           = ALL_GLSL(Cos);
+    fIntrinsicMap[String("tan")]           = ALL_GLSL(Tan);
+    fIntrinsicMap[String("asin")]          = ALL_GLSL(Asin);
+    fIntrinsicMap[String("acos")]          = ALL_GLSL(Acos);
+    fIntrinsicMap[String("atan")]          = SPECIAL(Atan);
+    fIntrinsicMap[String("sinh")]          = ALL_GLSL(Sinh);
+    fIntrinsicMap[String("cosh")]          = ALL_GLSL(Cosh);
+    fIntrinsicMap[String("tanh")]          = ALL_GLSL(Tanh);
+    fIntrinsicMap[String("asinh")]         = ALL_GLSL(Asinh);
+    fIntrinsicMap[String("acosh")]         = ALL_GLSL(Acosh);
+    fIntrinsicMap[String("atanh")]         = ALL_GLSL(Atanh);
+    fIntrinsicMap[String("pow")]           = ALL_GLSL(Pow);
+    fIntrinsicMap[String("exp")]           = ALL_GLSL(Exp);
+    fIntrinsicMap[String("log")]           = ALL_GLSL(Log);
+    fIntrinsicMap[String("exp2")]          = ALL_GLSL(Exp2);
+    fIntrinsicMap[String("log2")]          = ALL_GLSL(Log2);
+    fIntrinsicMap[String("sqrt")]          = ALL_GLSL(Sqrt);
+    fIntrinsicMap[String("inversesqrt")]   = ALL_GLSL(InverseSqrt);
+    fIntrinsicMap[String("determinant")]   = ALL_GLSL(Determinant);
+    fIntrinsicMap[String("matrixInverse")] = ALL_GLSL(MatrixInverse);
+    fIntrinsicMap[String("mod")]           = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpFMod,
+                                                             SpvOpSMod, SpvOpUMod, SpvOpUndef);
+    fIntrinsicMap[String("min")]           = BY_TYPE_GLSL(FMin, SMin, UMin);
+    fIntrinsicMap[String("max")]           = BY_TYPE_GLSL(FMax, SMax, UMax);
+    fIntrinsicMap[String("clamp")]         = BY_TYPE_GLSL(FClamp, SClamp, UClamp);
+    fIntrinsicMap[String("dot")]           = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDot,
+                                                             SpvOpUndef, SpvOpUndef, SpvOpUndef);
+    fIntrinsicMap[String("mix")]           = ALL_GLSL(FMix);
+    fIntrinsicMap[String("step")]          = ALL_GLSL(Step);
+    fIntrinsicMap[String("smoothstep")]    = ALL_GLSL(SmoothStep);
+    fIntrinsicMap[String("fma")]           = ALL_GLSL(Fma);
+    fIntrinsicMap[String("frexp")]         = ALL_GLSL(Frexp);
+    fIntrinsicMap[String("ldexp")]         = ALL_GLSL(Ldexp);
 
-#define PACK(type) fIntrinsicMap[SkString("pack" #type)] = ALL_GLSL(Pack ## type); \
-                   fIntrinsicMap[SkString("unpack" #type)] = ALL_GLSL(Unpack ## type)
+#define PACK(type) fIntrinsicMap[String("pack" #type)] = ALL_GLSL(Pack ## type); \
+                   fIntrinsicMap[String("unpack" #type)] = ALL_GLSL(Unpack ## type)
     PACK(Snorm4x8);
     PACK(Unorm4x8);
     PACK(Snorm2x16);
     PACK(Unorm2x16);
     PACK(Half2x16);
     PACK(Double2x32);
-    fIntrinsicMap[SkString("length")]      = ALL_GLSL(Length);
-    fIntrinsicMap[SkString("distance")]    = ALL_GLSL(Distance);
-    fIntrinsicMap[SkString("cross")]       = ALL_GLSL(Cross);
-    fIntrinsicMap[SkString("normalize")]   = ALL_GLSL(Normalize);
-    fIntrinsicMap[SkString("faceForward")] = ALL_GLSL(FaceForward);
-    fIntrinsicMap[SkString("reflect")]     = ALL_GLSL(Reflect);
-    fIntrinsicMap[SkString("refract")]     = ALL_GLSL(Refract);
-    fIntrinsicMap[SkString("findLSB")]     = ALL_GLSL(FindILsb);
-    fIntrinsicMap[SkString("findMSB")]     = BY_TYPE_GLSL(FindSMsb, FindSMsb, FindUMsb);
-    fIntrinsicMap[SkString("dFdx")]        = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdx,
-                                                             SpvOpUndef, SpvOpUndef, SpvOpUndef);
-    fIntrinsicMap[SkString("dFdy")]        = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdy,
-                                                             SpvOpUndef, SpvOpUndef, SpvOpUndef);
-    fIntrinsicMap[SkString("dFdy")]        = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdy,
-                                                             SpvOpUndef, SpvOpUndef, SpvOpUndef);
-    fIntrinsicMap[SkString("texture")]     = SPECIAL(Texture);
+    fIntrinsicMap[String("length")]      = ALL_GLSL(Length);
+    fIntrinsicMap[String("distance")]    = ALL_GLSL(Distance);
+    fIntrinsicMap[String("cross")]       = ALL_GLSL(Cross);
+    fIntrinsicMap[String("normalize")]   = ALL_GLSL(Normalize);
+    fIntrinsicMap[String("faceForward")] = ALL_GLSL(FaceForward);
+    fIntrinsicMap[String("reflect")]     = ALL_GLSL(Reflect);
+    fIntrinsicMap[String("refract")]     = ALL_GLSL(Refract);
+    fIntrinsicMap[String("findLSB")]     = ALL_GLSL(FindILsb);
+    fIntrinsicMap[String("findMSB")]     = BY_TYPE_GLSL(FindSMsb, FindSMsb, FindUMsb);
+    fIntrinsicMap[String("dFdx")]        = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdx,
+                                                           SpvOpUndef, SpvOpUndef, SpvOpUndef);
+    fIntrinsicMap[String("dFdy")]        = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdy,
+                                                           SpvOpUndef, SpvOpUndef, SpvOpUndef);
+    fIntrinsicMap[String("dFdy")]        = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdy,
+                                                           SpvOpUndef, SpvOpUndef, SpvOpUndef);
+    fIntrinsicMap[String("texture")]     = SPECIAL(Texture);
+    fIntrinsicMap[String("texelFetch")]  = SPECIAL(TexelFetch);
+    fIntrinsicMap[String("subpassLoad")] = SPECIAL(SubpassLoad);
 
-    fIntrinsicMap[SkString("subpassLoad")] = SPECIAL(SubpassLoad);
-
-    fIntrinsicMap[SkString("any")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
-                                                                  SpvOpUndef, SpvOpUndef, SpvOpAny);
-    fIntrinsicMap[SkString("all")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
-                                                                  SpvOpUndef, SpvOpUndef, SpvOpAll);
-    fIntrinsicMap[SkString("equal")]            = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpFOrdEqual, SpvOpIEqual,
-                                                                  SpvOpIEqual, SpvOpLogicalEqual);
-    fIntrinsicMap[SkString("notEqual")]         = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpFOrdNotEqual, SpvOpINotEqual,
-                                                                  SpvOpINotEqual,
-                                                                  SpvOpLogicalNotEqual);
-    fIntrinsicMap[SkString("lessThan")]         = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpSLessThan, SpvOpULessThan,
-                                                                  SpvOpFOrdLessThan, SpvOpUndef);
-    fIntrinsicMap[SkString("lessThanEqual")]    = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpSLessThanEqual,
-                                                                  SpvOpULessThanEqual,
-                                                                  SpvOpFOrdLessThanEqual,
-                                                                  SpvOpUndef);
-    fIntrinsicMap[SkString("greaterThan")]      = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpSGreaterThan,
-                                                                  SpvOpUGreaterThan,
-                                                                  SpvOpFOrdGreaterThan,
-                                                                  SpvOpUndef);
-    fIntrinsicMap[SkString("greaterThanEqual")] = std::make_tuple(kSPIRV_IntrinsicKind,
-                                                                  SpvOpSGreaterThanEqual,
-                                                                  SpvOpUGreaterThanEqual,
-                                                                  SpvOpFOrdGreaterThanEqual,
-                                                                  SpvOpUndef);
-
+    fIntrinsicMap[String("any")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
+                                                                SpvOpUndef, SpvOpUndef, SpvOpAny);
+    fIntrinsicMap[String("all")]              = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
+                                                                SpvOpUndef, SpvOpUndef, SpvOpAll);
+    fIntrinsicMap[String("equal")]            = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                SpvOpFOrdEqual, SpvOpIEqual,
+                                                                SpvOpIEqual, SpvOpLogicalEqual);
+    fIntrinsicMap[String("notEqual")]         = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                SpvOpFOrdNotEqual, SpvOpINotEqual,
+                                                                SpvOpINotEqual,
+                                                                SpvOpLogicalNotEqual);
+    fIntrinsicMap[String("lessThan")]         = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                SpvOpSLessThan, SpvOpULessThan,
+                                                                SpvOpFOrdLessThan, SpvOpUndef);
+    fIntrinsicMap[String("lessThanEqual")]    = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                SpvOpSLessThanEqual,
+                                                                SpvOpULessThanEqual,
+                                                                SpvOpFOrdLessThanEqual,
+                                                                SpvOpUndef);
+    fIntrinsicMap[String("greaterThan")]      = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                SpvOpSGreaterThan,
+                                                                SpvOpUGreaterThan,
+                                                                SpvOpFOrdGreaterThan,
+                                                                SpvOpUndef);
+    fIntrinsicMap[String("greaterThanEqual")] = std::make_tuple(kSPIRV_IntrinsicKind,
+                                                                SpvOpSGreaterThanEqual,
+                                                                SpvOpUGreaterThanEqual,
+                                                                SpvOpFOrdGreaterThanEqual,
+                                                                SpvOpUndef);
 // interpolateAt* not yet supported...
 }
 
-void SPIRVCodeGenerator::writeWord(int32_t word, SkWStream& out) {
+void SPIRVCodeGenerator::writeWord(int32_t word, OutputStream& out) {
 #if SPIRV_DEBUG
     out << "(" << word << ") ";
 #else
@@ -180,603 +177,603 @@
 }
 
 #if SPIRV_DEBUG
-static SkString opcode_text(SpvOp_ opCode) {
+static String opcode_text(SpvOp_ opCode) {
     switch (opCode) {
         case SpvOpNop:
-            return SkString("Nop");
+            return String("Nop");
         case SpvOpUndef:
-            return SkString("Undef");
+            return String("Undef");
         case SpvOpSourceContinued:
-            return SkString("SourceContinued");
+            return String("SourceContinued");
         case SpvOpSource:
-            return SkString("Source");
+            return String("Source");
         case SpvOpSourceExtension:
-            return SkString("SourceExtension");
+            return String("SourceExtension");
         case SpvOpName:
-            return SkString("Name");
+            return String("Name");
         case SpvOpMemberName:
-            return SkString("MemberName");
+            return String("MemberName");
         case SpvOpString:
-            return SkString("String");
+            return String("String");
         case SpvOpLine:
-            return SkString("Line");
+            return String("Line");
         case SpvOpExtension:
-            return SkString("Extension");
+            return String("Extension");
         case SpvOpExtInstImport:
-            return SkString("ExtInstImport");
+            return String("ExtInstImport");
         case SpvOpExtInst:
-            return SkString("ExtInst");
+            return String("ExtInst");
         case SpvOpMemoryModel:
-            return SkString("MemoryModel");
+            return String("MemoryModel");
         case SpvOpEntryPoint:
-            return SkString("EntryPoint");
+            return String("EntryPoint");
         case SpvOpExecutionMode:
-            return SkString("ExecutionMode");
+            return String("ExecutionMode");
         case SpvOpCapability:
-            return SkString("Capability");
+            return String("Capability");
         case SpvOpTypeVoid:
-            return SkString("TypeVoid");
+            return String("TypeVoid");
         case SpvOpTypeBool:
-            return SkString("TypeBool");
+            return String("TypeBool");
         case SpvOpTypeInt:
-            return SkString("TypeInt");
+            return String("TypeInt");
         case SpvOpTypeFloat:
-            return SkString("TypeFloat");
+            return String("TypeFloat");
         case SpvOpTypeVector:
-            return SkString("TypeVector");
+            return String("TypeVector");
         case SpvOpTypeMatrix:
-            return SkString("TypeMatrix");
+            return String("TypeMatrix");
         case SpvOpTypeImage:
-            return SkString("TypeImage");
+            return String("TypeImage");
         case SpvOpTypeSampler:
-            return SkString("TypeSampler");
+            return String("TypeSampler");
         case SpvOpTypeSampledImage:
-            return SkString("TypeSampledImage");
+            return String("TypeSampledImage");
         case SpvOpTypeArray:
-            return SkString("TypeArray");
+            return String("TypeArray");
         case SpvOpTypeRuntimeArray:
-            return SkString("TypeRuntimeArray");
+            return String("TypeRuntimeArray");
         case SpvOpTypeStruct:
-            return SkString("TypeStruct");
+            return String("TypeStruct");
         case SpvOpTypeOpaque:
-            return SkString("TypeOpaque");
+            return String("TypeOpaque");
         case SpvOpTypePointer:
-            return SkString("TypePointer");
+            return String("TypePointer");
         case SpvOpTypeFunction:
-            return SkString("TypeFunction");
+            return String("TypeFunction");
         case SpvOpTypeEvent:
-            return SkString("TypeEvent");
+            return String("TypeEvent");
         case SpvOpTypeDeviceEvent:
-            return SkString("TypeDeviceEvent");
+            return String("TypeDeviceEvent");
         case SpvOpTypeReserveId:
-            return SkString("TypeReserveId");
+            return String("TypeReserveId");
         case SpvOpTypeQueue:
-            return SkString("TypeQueue");
+            return String("TypeQueue");
         case SpvOpTypePipe:
-            return SkString("TypePipe");
+            return String("TypePipe");
         case SpvOpTypeForwardPointer:
-            return SkString("TypeForwardPointer");
+            return String("TypeForwardPointer");
         case SpvOpConstantTrue:
-            return SkString("ConstantTrue");
+            return String("ConstantTrue");
         case SpvOpConstantFalse:
-            return SkString("ConstantFalse");
+            return String("ConstantFalse");
         case SpvOpConstant:
-            return SkString("Constant");
+            return String("Constant");
         case SpvOpConstantComposite:
-            return SkString("ConstantComposite");
+            return String("ConstantComposite");
         case SpvOpConstantSampler:
-            return SkString("ConstantSampler");
+            return String("ConstantSampler");
         case SpvOpConstantNull:
-            return SkString("ConstantNull");
+            return String("ConstantNull");
         case SpvOpSpecConstantTrue:
-            return SkString("SpecConstantTrue");
+            return String("SpecConstantTrue");
         case SpvOpSpecConstantFalse:
-            return SkString("SpecConstantFalse");
+            return String("SpecConstantFalse");
         case SpvOpSpecConstant:
-            return SkString("SpecConstant");
+            return String("SpecConstant");
         case SpvOpSpecConstantComposite:
-            return SkString("SpecConstantComposite");
+            return String("SpecConstantComposite");
         case SpvOpSpecConstantOp:
-            return SkString("SpecConstantOp");
+            return String("SpecConstantOp");
         case SpvOpFunction:
-            return SkString("Function");
+            return String("Function");
         case SpvOpFunctionParameter:
-            return SkString("FunctionParameter");
+            return String("FunctionParameter");
         case SpvOpFunctionEnd:
-            return SkString("FunctionEnd");
+            return String("FunctionEnd");
         case SpvOpFunctionCall:
-            return SkString("FunctionCall");
+            return String("FunctionCall");
         case SpvOpVariable:
-            return SkString("Variable");
+            return String("Variable");
         case SpvOpImageTexelPointer:
-            return SkString("ImageTexelPointer");
+            return String("ImageTexelPointer");
         case SpvOpLoad:
-            return SkString("Load");
+            return String("Load");
         case SpvOpStore:
-            return SkString("Store");
+            return String("Store");
         case SpvOpCopyMemory:
-            return SkString("CopyMemory");
+            return String("CopyMemory");
         case SpvOpCopyMemorySized:
-            return SkString("CopyMemorySized");
+            return String("CopyMemorySized");
         case SpvOpAccessChain:
-            return SkString("AccessChain");
+            return String("AccessChain");
         case SpvOpInBoundsAccessChain:
-            return SkString("InBoundsAccessChain");
+            return String("InBoundsAccessChain");
         case SpvOpPtrAccessChain:
-            return SkString("PtrAccessChain");
+            return String("PtrAccessChain");
         case SpvOpArrayLength:
-            return SkString("ArrayLength");
+            return String("ArrayLength");
         case SpvOpGenericPtrMemSemantics:
-            return SkString("GenericPtrMemSemantics");
+            return String("GenericPtrMemSemantics");
         case SpvOpInBoundsPtrAccessChain:
-            return SkString("InBoundsPtrAccessChain");
+            return String("InBoundsPtrAccessChain");
         case SpvOpDecorate:
-            return SkString("Decorate");
+            return String("Decorate");
         case SpvOpMemberDecorate:
-            return SkString("MemberDecorate");
+            return String("MemberDecorate");
         case SpvOpDecorationGroup:
-            return SkString("DecorationGroup");
+            return String("DecorationGroup");
         case SpvOpGroupDecorate:
-            return SkString("GroupDecorate");
+            return String("GroupDecorate");
         case SpvOpGroupMemberDecorate:
-            return SkString("GroupMemberDecorate");
+            return String("GroupMemberDecorate");
         case SpvOpVectorExtractDynamic:
-            return SkString("VectorExtractDynamic");
+            return String("VectorExtractDynamic");
         case SpvOpVectorInsertDynamic:
-            return SkString("VectorInsertDynamic");
+            return String("VectorInsertDynamic");
         case SpvOpVectorShuffle:
-            return SkString("VectorShuffle");
+            return String("VectorShuffle");
         case SpvOpCompositeConstruct:
-            return SkString("CompositeConstruct");
+            return String("CompositeConstruct");
         case SpvOpCompositeExtract:
-            return SkString("CompositeExtract");
+            return String("CompositeExtract");
         case SpvOpCompositeInsert:
-            return SkString("CompositeInsert");
+            return String("CompositeInsert");
         case SpvOpCopyObject:
-            return SkString("CopyObject");
+            return String("CopyObject");
         case SpvOpTranspose:
-            return SkString("Transpose");
+            return String("Transpose");
         case SpvOpSampledImage:
-            return SkString("SampledImage");
+            return String("SampledImage");
         case SpvOpImageSampleImplicitLod:
-            return SkString("ImageSampleImplicitLod");
+            return String("ImageSampleImplicitLod");
         case SpvOpImageSampleExplicitLod:
-            return SkString("ImageSampleExplicitLod");
+            return String("ImageSampleExplicitLod");
         case SpvOpImageSampleDrefImplicitLod:
-            return SkString("ImageSampleDrefImplicitLod");
+            return String("ImageSampleDrefImplicitLod");
         case SpvOpImageSampleDrefExplicitLod:
-            return SkString("ImageSampleDrefExplicitLod");
+            return String("ImageSampleDrefExplicitLod");
         case SpvOpImageSampleProjImplicitLod:
-            return SkString("ImageSampleProjImplicitLod");
+            return String("ImageSampleProjImplicitLod");
         case SpvOpImageSampleProjExplicitLod:
-            return SkString("ImageSampleProjExplicitLod");
+            return String("ImageSampleProjExplicitLod");
         case SpvOpImageSampleProjDrefImplicitLod:
-            return SkString("ImageSampleProjDrefImplicitLod");
+            return String("ImageSampleProjDrefImplicitLod");
         case SpvOpImageSampleProjDrefExplicitLod:
-            return SkString("ImageSampleProjDrefExplicitLod");
+            return String("ImageSampleProjDrefExplicitLod");
         case SpvOpImageFetch:
-            return SkString("ImageFetch");
+            return String("ImageFetch");
         case SpvOpImageGather:
-            return SkString("ImageGather");
+            return String("ImageGather");
         case SpvOpImageDrefGather:
-            return SkString("ImageDrefGather");
+            return String("ImageDrefGather");
         case SpvOpImageRead:
-            return SkString("ImageRead");
+            return String("ImageRead");
         case SpvOpImageWrite:
-            return SkString("ImageWrite");
+            return String("ImageWrite");
         case SpvOpImage:
-            return SkString("Image");
+            return String("Image");
         case SpvOpImageQueryFormat:
-            return SkString("ImageQueryFormat");
+            return String("ImageQueryFormat");
         case SpvOpImageQueryOrder:
-            return SkString("ImageQueryOrder");
+            return String("ImageQueryOrder");
         case SpvOpImageQuerySizeLod:
-            return SkString("ImageQuerySizeLod");
+            return String("ImageQuerySizeLod");
         case SpvOpImageQuerySize:
-            return SkString("ImageQuerySize");
+            return String("ImageQuerySize");
         case SpvOpImageQueryLod:
-            return SkString("ImageQueryLod");
+            return String("ImageQueryLod");
         case SpvOpImageQueryLevels:
-            return SkString("ImageQueryLevels");
+            return String("ImageQueryLevels");
         case SpvOpImageQuerySamples:
-            return SkString("ImageQuerySamples");
+            return String("ImageQuerySamples");
         case SpvOpConvertFToU:
-            return SkString("ConvertFToU");
+            return String("ConvertFToU");
         case SpvOpConvertFToS:
-            return SkString("ConvertFToS");
+            return String("ConvertFToS");
         case SpvOpConvertSToF:
-            return SkString("ConvertSToF");
+            return String("ConvertSToF");
         case SpvOpConvertUToF:
-            return SkString("ConvertUToF");
+            return String("ConvertUToF");
         case SpvOpUConvert:
-            return SkString("UConvert");
+            return String("UConvert");
         case SpvOpSConvert:
-            return SkString("SConvert");
+            return String("SConvert");
         case SpvOpFConvert:
-            return SkString("FConvert");
+            return String("FConvert");
         case SpvOpQuantizeToF16:
-            return SkString("QuantizeToF16");
+            return String("QuantizeToF16");
         case SpvOpConvertPtrToU:
-            return SkString("ConvertPtrToU");
+            return String("ConvertPtrToU");
         case SpvOpSatConvertSToU:
-            return SkString("SatConvertSToU");
+            return String("SatConvertSToU");
         case SpvOpSatConvertUToS:
-            return SkString("SatConvertUToS");
+            return String("SatConvertUToS");
         case SpvOpConvertUToPtr:
-            return SkString("ConvertUToPtr");
+            return String("ConvertUToPtr");
         case SpvOpPtrCastToGeneric:
-            return SkString("PtrCastToGeneric");
+            return String("PtrCastToGeneric");
         case SpvOpGenericCastToPtr:
-            return SkString("GenericCastToPtr");
+            return String("GenericCastToPtr");
         case SpvOpGenericCastToPtrExplicit:
-            return SkString("GenericCastToPtrExplicit");
+            return String("GenericCastToPtrExplicit");
         case SpvOpBitcast:
-            return SkString("Bitcast");
+            return String("Bitcast");
         case SpvOpSNegate:
-            return SkString("SNegate");
+            return String("SNegate");
         case SpvOpFNegate:
-            return SkString("FNegate");
+            return String("FNegate");
         case SpvOpIAdd:
-            return SkString("IAdd");
+            return String("IAdd");
         case SpvOpFAdd:
-            return SkString("FAdd");
+            return String("FAdd");
         case SpvOpISub:
-            return SkString("ISub");
+            return String("ISub");
         case SpvOpFSub:
-            return SkString("FSub");
+            return String("FSub");
         case SpvOpIMul:
-            return SkString("IMul");
+            return String("IMul");
         case SpvOpFMul:
-            return SkString("FMul");
+            return String("FMul");
         case SpvOpUDiv:
-            return SkString("UDiv");
+            return String("UDiv");
         case SpvOpSDiv:
-            return SkString("SDiv");
+            return String("SDiv");
         case SpvOpFDiv:
-            return SkString("FDiv");
+            return String("FDiv");
         case SpvOpUMod:
-            return SkString("UMod");
+            return String("UMod");
         case SpvOpSRem:
-            return SkString("SRem");
+            return String("SRem");
         case SpvOpSMod:
-            return SkString("SMod");
+            return String("SMod");
         case SpvOpFRem:
-            return SkString("FRem");
+            return String("FRem");
         case SpvOpFMod:
-            return SkString("FMod");
+            return String("FMod");
         case SpvOpVectorTimesScalar:
-            return SkString("VectorTimesScalar");
+            return String("VectorTimesScalar");
         case SpvOpMatrixTimesScalar:
-            return SkString("MatrixTimesScalar");
+            return String("MatrixTimesScalar");
         case SpvOpVectorTimesMatrix:
-            return SkString("VectorTimesMatrix");
+            return String("VectorTimesMatrix");
         case SpvOpMatrixTimesVector:
-            return SkString("MatrixTimesVector");
+            return String("MatrixTimesVector");
         case SpvOpMatrixTimesMatrix:
-            return SkString("MatrixTimesMatrix");
+            return String("MatrixTimesMatrix");
         case SpvOpOuterProduct:
-            return SkString("OuterProduct");
+            return String("OuterProduct");
         case SpvOpDot:
-            return SkString("Dot");
+            return String("Dot");
         case SpvOpIAddCarry:
-            return SkString("IAddCarry");
+            return String("IAddCarry");
         case SpvOpISubBorrow:
-            return SkString("ISubBorrow");
+            return String("ISubBorrow");
         case SpvOpUMulExtended:
-            return SkString("UMulExtended");
+            return String("UMulExtended");
         case SpvOpSMulExtended:
-            return SkString("SMulExtended");
+            return String("SMulExtended");
         case SpvOpAny:
-            return SkString("Any");
+            return String("Any");
         case SpvOpAll:
-            return SkString("All");
+            return String("All");
         case SpvOpIsNan:
-            return SkString("IsNan");
+            return String("IsNan");
         case SpvOpIsInf:
-            return SkString("IsInf");
+            return String("IsInf");
         case SpvOpIsFinite:
-            return SkString("IsFinite");
+            return String("IsFinite");
         case SpvOpIsNormal:
-            return SkString("IsNormal");
+            return String("IsNormal");
         case SpvOpSignBitSet:
-            return SkString("SignBitSet");
+            return String("SignBitSet");
         case SpvOpLessOrGreater:
-            return SkString("LessOrGreater");
+            return String("LessOrGreater");
         case SpvOpOrdered:
-            return SkString("Ordered");
+            return String("Ordered");
         case SpvOpUnordered:
-            return SkString("Unordered");
+            return String("Unordered");
         case SpvOpLogicalEqual:
-            return SkString("LogicalEqual");
+            return String("LogicalEqual");
         case SpvOpLogicalNotEqual:
-            return SkString("LogicalNotEqual");
+            return String("LogicalNotEqual");
         case SpvOpLogicalOr:
-            return SkString("LogicalOr");
+            return String("LogicalOr");
         case SpvOpLogicalAnd:
-            return SkString("LogicalAnd");
+            return String("LogicalAnd");
         case SpvOpLogicalNot:
-            return SkString("LogicalNot");
+            return String("LogicalNot");
         case SpvOpSelect:
-            return SkString("Select");
+            return String("Select");
         case SpvOpIEqual:
-            return SkString("IEqual");
+            return String("IEqual");
         case SpvOpINotEqual:
-            return SkString("INotEqual");
+            return String("INotEqual");
         case SpvOpUGreaterThan:
-            return SkString("UGreaterThan");
+            return String("UGreaterThan");
         case SpvOpSGreaterThan:
-            return SkString("SGreaterThan");
+            return String("SGreaterThan");
         case SpvOpUGreaterThanEqual:
-            return SkString("UGreaterThanEqual");
+            return String("UGreaterThanEqual");
         case SpvOpSGreaterThanEqual:
-            return SkString("SGreaterThanEqual");
+            return String("SGreaterThanEqual");
         case SpvOpULessThan:
-            return SkString("ULessThan");
+            return String("ULessThan");
         case SpvOpSLessThan:
-            return SkString("SLessThan");
+            return String("SLessThan");
         case SpvOpULessThanEqual:
-            return SkString("ULessThanEqual");
+            return String("ULessThanEqual");
         case SpvOpSLessThanEqual:
-            return SkString("SLessThanEqual");
+            return String("SLessThanEqual");
         case SpvOpFOrdEqual:
-            return SkString("FOrdEqual");
+            return String("FOrdEqual");
         case SpvOpFUnordEqual:
-            return SkString("FUnordEqual");
+            return String("FUnordEqual");
         case SpvOpFOrdNotEqual:
-            return SkString("FOrdNotEqual");
+            return String("FOrdNotEqual");
         case SpvOpFUnordNotEqual:
-            return SkString("FUnordNotEqual");
+            return String("FUnordNotEqual");
         case SpvOpFOrdLessThan:
-            return SkString("FOrdLessThan");
+            return String("FOrdLessThan");
         case SpvOpFUnordLessThan:
-            return SkString("FUnordLessThan");
+            return String("FUnordLessThan");
         case SpvOpFOrdGreaterThan:
-            return SkString("FOrdGreaterThan");
+            return String("FOrdGreaterThan");
         case SpvOpFUnordGreaterThan:
-            return SkString("FUnordGreaterThan");
+            return String("FUnordGreaterThan");
         case SpvOpFOrdLessThanEqual:
-            return SkString("FOrdLessThanEqual");
+            return String("FOrdLessThanEqual");
         case SpvOpFUnordLessThanEqual:
-            return SkString("FUnordLessThanEqual");
+            return String("FUnordLessThanEqual");
         case SpvOpFOrdGreaterThanEqual:
-            return SkString("FOrdGreaterThanEqual");
+            return String("FOrdGreaterThanEqual");
         case SpvOpFUnordGreaterThanEqual:
-            return SkString("FUnordGreaterThanEqual");
+            return String("FUnordGreaterThanEqual");
         case SpvOpShiftRightLogical:
-            return SkString("ShiftRightLogical");
+            return String("ShiftRightLogical");
         case SpvOpShiftRightArithmetic:
-            return SkString("ShiftRightArithmetic");
+            return String("ShiftRightArithmetic");
         case SpvOpShiftLeftLogical:
-            return SkString("ShiftLeftLogical");
+            return String("ShiftLeftLogical");
         case SpvOpBitwiseOr:
-            return SkString("BitwiseOr");
+            return String("BitwiseOr");
         case SpvOpBitwiseXor:
-            return SkString("BitwiseXor");
+            return String("BitwiseXor");
         case SpvOpBitwiseAnd:
-            return SkString("BitwiseAnd");
+            return String("BitwiseAnd");
         case SpvOpNot:
-            return SkString("Not");
+            return String("Not");
         case SpvOpBitFieldInsert:
-            return SkString("BitFieldInsert");
+            return String("BitFieldInsert");
         case SpvOpBitFieldSExtract:
-            return SkString("BitFieldSExtract");
+            return String("BitFieldSExtract");
         case SpvOpBitFieldUExtract:
-            return SkString("BitFieldUExtract");
+            return String("BitFieldUExtract");
         case SpvOpBitReverse:
-            return SkString("BitReverse");
+            return String("BitReverse");
         case SpvOpBitCount:
-            return SkString("BitCount");
+            return String("BitCount");
         case SpvOpDPdx:
-            return SkString("DPdx");
+            return String("DPdx");
         case SpvOpDPdy:
-            return SkString("DPdy");
+            return String("DPdy");
         case SpvOpFwidth:
-            return SkString("Fwidth");
+            return String("Fwidth");
         case SpvOpDPdxFine:
-            return SkString("DPdxFine");
+            return String("DPdxFine");
         case SpvOpDPdyFine:
-            return SkString("DPdyFine");
+            return String("DPdyFine");
         case SpvOpFwidthFine:
-            return SkString("FwidthFine");
+            return String("FwidthFine");
         case SpvOpDPdxCoarse:
-            return SkString("DPdxCoarse");
+            return String("DPdxCoarse");
         case SpvOpDPdyCoarse:
-            return SkString("DPdyCoarse");
+            return String("DPdyCoarse");
         case SpvOpFwidthCoarse:
-            return SkString("FwidthCoarse");
+            return String("FwidthCoarse");
         case SpvOpEmitVertex:
-            return SkString("EmitVertex");
+            return String("EmitVertex");
         case SpvOpEndPrimitive:
-            return SkString("EndPrimitive");
+            return String("EndPrimitive");
         case SpvOpEmitStreamVertex:
-            return SkString("EmitStreamVertex");
+            return String("EmitStreamVertex");
         case SpvOpEndStreamPrimitive:
-            return SkString("EndStreamPrimitive");
+            return String("EndStreamPrimitive");
         case SpvOpControlBarrier:
-            return SkString("ControlBarrier");
+            return String("ControlBarrier");
         case SpvOpMemoryBarrier:
-            return SkString("MemoryBarrier");
+            return String("MemoryBarrier");
         case SpvOpAtomicLoad:
-            return SkString("AtomicLoad");
+            return String("AtomicLoad");
         case SpvOpAtomicStore:
-            return SkString("AtomicStore");
+            return String("AtomicStore");
         case SpvOpAtomicExchange:
-            return SkString("AtomicExchange");
+            return String("AtomicExchange");
         case SpvOpAtomicCompareExchange:
-            return SkString("AtomicCompareExchange");
+            return String("AtomicCompareExchange");
         case SpvOpAtomicCompareExchangeWeak:
-            return SkString("AtomicCompareExchangeWeak");
+            return String("AtomicCompareExchangeWeak");
         case SpvOpAtomicIIncrement:
-            return SkString("AtomicIIncrement");
+            return String("AtomicIIncrement");
         case SpvOpAtomicIDecrement:
-            return SkString("AtomicIDecrement");
+            return String("AtomicIDecrement");
         case SpvOpAtomicIAdd:
-            return SkString("AtomicIAdd");
+            return String("AtomicIAdd");
         case SpvOpAtomicISub:
-            return SkString("AtomicISub");
+            return String("AtomicISub");
         case SpvOpAtomicSMin:
-            return SkString("AtomicSMin");
+            return String("AtomicSMin");
         case SpvOpAtomicUMin:
-            return SkString("AtomicUMin");
+            return String("AtomicUMin");
         case SpvOpAtomicSMax:
-            return SkString("AtomicSMax");
+            return String("AtomicSMax");
         case SpvOpAtomicUMax:
-            return SkString("AtomicUMax");
+            return String("AtomicUMax");
         case SpvOpAtomicAnd:
-            return SkString("AtomicAnd");
+            return String("AtomicAnd");
         case SpvOpAtomicOr:
-            return SkString("AtomicOr");
+            return String("AtomicOr");
         case SpvOpAtomicXor:
-            return SkString("AtomicXor");
+            return String("AtomicXor");
         case SpvOpPhi:
-            return SkString("Phi");
+            return String("Phi");
         case SpvOpLoopMerge:
-            return SkString("LoopMerge");
+            return String("LoopMerge");
         case SpvOpSelectionMerge:
-            return SkString("SelectionMerge");
+            return String("SelectionMerge");
         case SpvOpLabel:
-            return SkString("Label");
+            return String("Label");
         case SpvOpBranch:
-            return SkString("Branch");
+            return String("Branch");
         case SpvOpBranchConditional:
-            return SkString("BranchConditional");
+            return String("BranchConditional");
         case SpvOpSwitch:
-            return SkString("Switch");
+            return String("Switch");
         case SpvOpKill:
-            return SkString("Kill");
+            return String("Kill");
         case SpvOpReturn:
-            return SkString("Return");
+            return String("Return");
         case SpvOpReturnValue:
-            return SkString("ReturnValue");
+            return String("ReturnValue");
         case SpvOpUnreachable:
-            return SkString("Unreachable");
+            return String("Unreachable");
         case SpvOpLifetimeStart:
-            return SkString("LifetimeStart");
+            return String("LifetimeStart");
         case SpvOpLifetimeStop:
-            return SkString("LifetimeStop");
+            return String("LifetimeStop");
         case SpvOpGroupAsyncCopy:
-            return SkString("GroupAsyncCopy");
+            return String("GroupAsyncCopy");
         case SpvOpGroupWaitEvents:
-            return SkString("GroupWaitEvents");
+            return String("GroupWaitEvents");
         case SpvOpGroupAll:
-            return SkString("GroupAll");
+            return String("GroupAll");
         case SpvOpGroupAny:
-            return SkString("GroupAny");
+            return String("GroupAny");
         case SpvOpGroupBroadcast:
-            return SkString("GroupBroadcast");
+            return String("GroupBroadcast");
         case SpvOpGroupIAdd:
-            return SkString("GroupIAdd");
+            return String("GroupIAdd");
         case SpvOpGroupFAdd:
-            return SkString("GroupFAdd");
+            return String("GroupFAdd");
         case SpvOpGroupFMin:
-            return SkString("GroupFMin");
+            return String("GroupFMin");
         case SpvOpGroupUMin:
-            return SkString("GroupUMin");
+            return String("GroupUMin");
         case SpvOpGroupSMin:
-            return SkString("GroupSMin");
+            return String("GroupSMin");
         case SpvOpGroupFMax:
-            return SkString("GroupFMax");
+            return String("GroupFMax");
         case SpvOpGroupUMax:
-            return SkString("GroupUMax");
+            return String("GroupUMax");
         case SpvOpGroupSMax:
-            return SkString("GroupSMax");
+            return String("GroupSMax");
         case SpvOpReadPipe:
-            return SkString("ReadPipe");
+            return String("ReadPipe");
         case SpvOpWritePipe:
-            return SkString("WritePipe");
+            return String("WritePipe");
         case SpvOpReservedReadPipe:
-            return SkString("ReservedReadPipe");
+            return String("ReservedReadPipe");
         case SpvOpReservedWritePipe:
-            return SkString("ReservedWritePipe");
+            return String("ReservedWritePipe");
         case SpvOpReserveReadPipePackets:
-            return SkString("ReserveReadPipePackets");
+            return String("ReserveReadPipePackets");
         case SpvOpReserveWritePipePackets:
-            return SkString("ReserveWritePipePackets");
+            return String("ReserveWritePipePackets");
         case SpvOpCommitReadPipe:
-            return SkString("CommitReadPipe");
+            return String("CommitReadPipe");
         case SpvOpCommitWritePipe:
-            return SkString("CommitWritePipe");
+            return String("CommitWritePipe");
         case SpvOpIsValidReserveId:
-            return SkString("IsValidReserveId");
+            return String("IsValidReserveId");
         case SpvOpGetNumPipePackets:
-            return SkString("GetNumPipePackets");
+            return String("GetNumPipePackets");
         case SpvOpGetMaxPipePackets:
-            return SkString("GetMaxPipePackets");
+            return String("GetMaxPipePackets");
         case SpvOpGroupReserveReadPipePackets:
-            return SkString("GroupReserveReadPipePackets");
+            return String("GroupReserveReadPipePackets");
         case SpvOpGroupReserveWritePipePackets:
-            return SkString("GroupReserveWritePipePackets");
+            return String("GroupReserveWritePipePackets");
         case SpvOpGroupCommitReadPipe:
-            return SkString("GroupCommitReadPipe");
+            return String("GroupCommitReadPipe");
         case SpvOpGroupCommitWritePipe:
-            return SkString("GroupCommitWritePipe");
+            return String("GroupCommitWritePipe");
         case SpvOpEnqueueMarker:
-            return SkString("EnqueueMarker");
+            return String("EnqueueMarker");
         case SpvOpEnqueueKernel:
-            return SkString("EnqueueKernel");
+            return String("EnqueueKernel");
         case SpvOpGetKernelNDrangeSubGroupCount:
-            return SkString("GetKernelNDrangeSubGroupCount");
+            return String("GetKernelNDrangeSubGroupCount");
         case SpvOpGetKernelNDrangeMaxSubGroupSize:
-            return SkString("GetKernelNDrangeMaxSubGroupSize");
+            return String("GetKernelNDrangeMaxSubGroupSize");
         case SpvOpGetKernelWorkGroupSize:
-            return SkString("GetKernelWorkGroupSize");
+            return String("GetKernelWorkGroupSize");
         case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
-            return SkString("GetKernelPreferredWorkGroupSizeMultiple");
+            return String("GetKernelPreferredWorkGroupSizeMultiple");
         case SpvOpRetainEvent:
-            return SkString("RetainEvent");
+            return String("RetainEvent");
         case SpvOpReleaseEvent:
-            return SkString("ReleaseEvent");
+            return String("ReleaseEvent");
         case SpvOpCreateUserEvent:
-            return SkString("CreateUserEvent");
+            return String("CreateUserEvent");
         case SpvOpIsValidEvent:
-            return SkString("IsValidEvent");
+            return String("IsValidEvent");
         case SpvOpSetUserEventStatus:
-            return SkString("SetUserEventStatus");
+            return String("SetUserEventStatus");
         case SpvOpCaptureEventProfilingInfo:
-            return SkString("CaptureEventProfilingInfo");
+            return String("CaptureEventProfilingInfo");
         case SpvOpGetDefaultQueue:
-            return SkString("GetDefaultQueue");
+            return String("GetDefaultQueue");
         case SpvOpBuildNDRange:
-            return SkString("BuildNDRange");
+            return String("BuildNDRange");
         case SpvOpImageSparseSampleImplicitLod:
-            return SkString("ImageSparseSampleImplicitLod");
+            return String("ImageSparseSampleImplicitLod");
         case SpvOpImageSparseSampleExplicitLod:
-            return SkString("ImageSparseSampleExplicitLod");
+            return String("ImageSparseSampleExplicitLod");
         case SpvOpImageSparseSampleDrefImplicitLod:
-            return SkString("ImageSparseSampleDrefImplicitLod");
+            return String("ImageSparseSampleDrefImplicitLod");
         case SpvOpImageSparseSampleDrefExplicitLod:
-            return SkString("ImageSparseSampleDrefExplicitLod");
+            return String("ImageSparseSampleDrefExplicitLod");
         case SpvOpImageSparseSampleProjImplicitLod:
-            return SkString("ImageSparseSampleProjImplicitLod");
+            return String("ImageSparseSampleProjImplicitLod");
         case SpvOpImageSparseSampleProjExplicitLod:
-            return SkString("ImageSparseSampleProjExplicitLod");
+            return String("ImageSparseSampleProjExplicitLod");
         case SpvOpImageSparseSampleProjDrefImplicitLod:
-            return SkString("ImageSparseSampleProjDrefImplicitLod");
+            return String("ImageSparseSampleProjDrefImplicitLod");
         case SpvOpImageSparseSampleProjDrefExplicitLod:
-            return SkString("ImageSparseSampleProjDrefExplicitLod");
+            return String("ImageSparseSampleProjDrefExplicitLod");
         case SpvOpImageSparseFetch:
-            return SkString("ImageSparseFetch");
+            return String("ImageSparseFetch");
         case SpvOpImageSparseGather:
-            return SkString("ImageSparseGather");
+            return String("ImageSparseGather");
         case SpvOpImageSparseDrefGather:
-            return SkString("ImageSparseDrefGather");
+            return String("ImageSparseDrefGather");
         case SpvOpImageSparseTexelsResident:
-            return SkString("ImageSparseTexelsResident");
+            return String("ImageSparseTexelsResident");
         case SpvOpNoLine:
-            return SkString("NoLine");
+            return String("NoLine");
         case SpvOpAtomicFlagTestAndSet:
-            return SkString("AtomicFlagTestAndSet");
+            return String("AtomicFlagTestAndSet");
         case SpvOpAtomicFlagClear:
-            return SkString("AtomicFlagClear");
+            return String("AtomicFlagClear");
         case SpvOpImageSparseRead:
-            return SkString("ImageSparseRead");
+            return String("ImageSparseRead");
         default:
-            ABORT("unsupported SPIR-V op");    
+            ABORT("unsupported SPIR-V op");
     }
 }
 #endif
 
-void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, SkWStream& out) {
+void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, OutputStream& out) {
     ASSERT(opCode != SpvOpUndef);
     switch (opCode) {
         case SpvOpReturn:      // fall through
@@ -830,23 +827,23 @@
 #endif
 }
 
-void SPIRVCodeGenerator::writeLabel(SpvId label, SkWStream& out) {
+void SPIRVCodeGenerator::writeLabel(SpvId label, OutputStream& out) {
     fCurrentBlock = label;
     this->writeInstruction(SpvOpLabel, label, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, SkWStream& out) {
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, OutputStream& out) {
     this->writeOpCode(opCode, 1, out);
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, SkWStream& out) {
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out) {
     this->writeOpCode(opCode, 2, out);
     this->writeWord(word1, out);
 }
 
-void SPIRVCodeGenerator::writeString(const char* string, SkWStream& out) {
+void SPIRVCodeGenerator::writeString(const char* string, OutputStream& out) {
     size_t length = strlen(string);
-    out.writeText(string);
+    out.write(string, length);
     switch (length % 4) {
         case 1:
             out.write8(0);
@@ -862,7 +859,7 @@
     }
 }
 
-void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, const char* string, SkWStream& out) {
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, const char* string, OutputStream& out) {
     int32_t length = (int32_t) strlen(string);
     this->writeOpCode(opCode, 1 + (length + 4) / 4, out);
     this->writeString(string, out);
@@ -870,7 +867,7 @@
 
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, const char* string,
-                                          SkWStream& out) {
+                                          OutputStream& out) {
     int32_t length = (int32_t) strlen(string);
     this->writeOpCode(opCode, 2 + (length + 4) / 4, out);
     this->writeWord(word1, out);
@@ -878,7 +875,7 @@
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
-                                          const char* string, SkWStream& out) {
+                                          const char* string, OutputStream& out) {
     int32_t length = (int32_t) strlen(string);
     this->writeOpCode(opCode, 3 + (length + 4) / 4, out);
     this->writeWord(word1, out);
@@ -887,14 +884,14 @@
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
-                                          SkWStream& out) {
+                                          OutputStream& out) {
     this->writeOpCode(opCode, 3, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
-                                          int32_t word3, SkWStream& out) {
+                                          int32_t word3, OutputStream& out) {
     this->writeOpCode(opCode, 4, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
@@ -902,7 +899,7 @@
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
-                                          int32_t word3, int32_t word4, SkWStream& out) {
+                                          int32_t word3, int32_t word4, OutputStream& out) {
     this->writeOpCode(opCode, 5, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
@@ -912,7 +909,7 @@
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
-                                          SkWStream& out) {
+                                          OutputStream& out) {
     this->writeOpCode(opCode, 6, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
@@ -923,7 +920,7 @@
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
-                                          int32_t word6, SkWStream& out) {
+                                          int32_t word6, OutputStream& out) {
     this->writeOpCode(opCode, 7, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
@@ -935,7 +932,7 @@
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
-                                          int32_t word6, int32_t word7, SkWStream& out) {
+                                          int32_t word6, int32_t word7, OutputStream& out) {
     this->writeOpCode(opCode, 8, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
@@ -949,7 +946,7 @@
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           int32_t word3, int32_t word4, int32_t word5,
                                           int32_t word6, int32_t word7, int32_t word8,
-                                          SkWStream& out) {
+                                          OutputStream& out) {
     this->writeOpCode(opCode, 9, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
@@ -961,7 +958,7 @@
     this->writeWord(word8, out);
 }
 
-void SPIRVCodeGenerator::writeCapabilities(SkWStream& out) {
+void SPIRVCodeGenerator::writeCapabilities(OutputStream& out) {
     for (uint64_t i = 0, bit = 1; i <= kLast_Capability; i++, bit <<= 1) {
         if (fCapabilities & bit) {
             this->writeInstruction(SpvOpCapability, (SpvId) i, out);
@@ -1037,7 +1034,7 @@
 }
 
 SpvId SPIRVCodeGenerator::getType(const Type& type, const MemoryLayout& layout) {
-    SkString key = type.name() + to_string((int) layout.fStd);
+    String key = type.name() + to_string((int) layout.fStd);
     auto entry = fTypeMap.find(key);
     if (entry == fTypeMap.end()) {
         SpvId result = this->nextId();
@@ -1077,13 +1074,15 @@
                                            this->getType(type.componentType(), layout),
                                            this->writeIntLiteral(count), fConstantBuffer);
                     this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride,
-                                           (int32_t) layout.stride(type), 
+                                           (int32_t) layout.stride(type),
                                            fDecorationBuffer);
                 } else {
-                    ABORT("runtime-sized arrays are not yet supported");
                     this->writeInstruction(SpvOpTypeRuntimeArray, result,
                                            this->getType(type.componentType(), layout),
                                            fConstantBuffer);
+                    this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride,
+                                           (int32_t) layout.stride(type),
+                                           fDecorationBuffer);
                 }
                 break;
             }
@@ -1092,11 +1091,15 @@
                 if (SpvDimSubpassData != type.dimensions()) {
                     image = this->nextId();
                 }
+                if (SpvDimBuffer == type.dimensions()) {
+                    fCapabilities |= (((uint64_t) 1) << SpvCapabilitySampledBuffer);
+                }
                 this->writeInstruction(SpvOpTypeImage, image,
                                        this->getType(*fContext.fFloat_Type, layout),
                                        type.dimensions(), type.isDepth(), type.isArrayed(),
                                        type.isMultisampled(), type.isSampled() ? 1 : 2,
                                        SpvImageFormatUnknown, fConstantBuffer);
+                fImageTypeMap[key] = image;
                 if (SpvDimSubpassData != type.dimensions()) {
                     this->writeInstruction(SpvOpTypeSampledImage, result, image, fConstantBuffer);
                 }
@@ -1115,9 +1118,17 @@
     return entry->second;
 }
 
+SpvId SPIRVCodeGenerator::getImageType(const Type& type) {
+    ASSERT(type.kind() == Type::kSampler_Kind);
+    this->getType(type);
+    String key = type.name() + to_string((int) fDefaultLayout.fStd);
+    ASSERT(fImageTypeMap.find(key) != fImageTypeMap.end());
+    return fImageTypeMap[key];
+}
+
 SpvId SPIRVCodeGenerator::getFunctionType(const FunctionDeclaration& function) {
-    SkString key = function.fReturnType.description() + "(";
-    SkString separator;
+    String key = function.fReturnType.description() + "(";
+    String separator;
     for (size_t i = 0; i < function.fParameters.size(); i++) {
         key += separator;
         separator = ", ";
@@ -1178,7 +1189,7 @@
 
 SpvId SPIRVCodeGenerator::getPointerType(const Type& type, const MemoryLayout& layout,
                                          SpvStorageClass_ storageClass) {
-    SkString key = type.description() + "*" + to_string(layout.fStd) + to_string(storageClass);
+    String key = type.description() + "*" + to_string(layout.fStd) + to_string(storageClass);
     auto entry = fTypeMap.find(key);
     if (entry == fTypeMap.end()) {
         SpvId result = this->nextId();
@@ -1190,7 +1201,7 @@
     return entry->second;
 }
 
-SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, OutputStream& out) {
     switch (expr.fKind) {
         case Expression::kBinary_Kind:
             return this->writeBinaryExpression((BinaryExpression&) expr, out);
@@ -1224,7 +1235,7 @@
     return -1;
 }
 
-SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, OutputStream& out) {
     auto intrinsic = fIntrinsicMap.find(c.fFunction.fName);
     ASSERT(intrinsic != fIntrinsicMap.end());
     const Type& type = c.fArguments[0]->fType;
@@ -1280,7 +1291,7 @@
 }
 
 SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind,
-                                                SkWStream& out) {
+                                                OutputStream& out) {
     SpvId result = this->nextId();
     switch (kind) {
         case kAtan_SpecialIntrinsic: {
@@ -1296,7 +1307,51 @@
             for (SpvId id : arguments) {
                 this->writeWord(id, out);
             }
-            return result;
+            break;
+        }
+        case kSubpassLoad_SpecialIntrinsic: {
+            SpvId img = this->writeExpression(*c.fArguments[0], out);
+            std::vector<std::unique_ptr<Expression>> args;
+            args.emplace_back(new FloatLiteral(fContext, Position(), 0.0));
+            args.emplace_back(new FloatLiteral(fContext, Position(), 0.0));
+            Constructor ctor(Position(), *fContext.fVec2_Type, std::move(args));
+            SpvId coords = this->writeConstantVector(ctor);
+            if (1 == c.fArguments.size()) {
+                this->writeInstruction(SpvOpImageRead,
+                                       this->getType(c.fType),
+                                       result,
+                                       img,
+                                       coords,
+                                       out);
+            } else {
+                ASSERT(2 == c.fArguments.size());
+                SpvId sample = this->writeExpression(*c.fArguments[1], out);
+                this->writeInstruction(SpvOpImageRead,
+                                       this->getType(c.fType),
+                                       result,
+                                       img,
+                                       coords,
+                                       SpvImageOperandsSampleMask,
+                                       sample,
+                                       out);
+            }
+            break;
+        }
+        case kTexelFetch_SpecialIntrinsic: {
+            ASSERT(c.fArguments.size() == 2);
+            SpvId image = this->nextId();
+            this->writeInstruction(SpvOpImage,
+                                   this->getImageType(c.fArguments[0]->fType),
+                                   image,
+                                   this->writeExpression(*c.fArguments[0], out),
+                                   out);
+            this->writeInstruction(SpvOpImageFetch,
+                                   this->getType(c.fType),
+                                   result,
+                                   image,
+                                   this->writeExpression(*c.fArguments[1], out),
+                                   out);
+            break;
         }
         case kTexture_SpecialIntrinsic: {
             SpvOp_ op = SpvOpImageSampleImplicitLod;
@@ -1343,39 +1398,11 @@
             }
             break;
         }
-        case kSubpassLoad_SpecialIntrinsic: {
-            SpvId img = this->writeExpression(*c.fArguments[0], out);
-            std::vector<std::unique_ptr<Expression>> args;
-            args.emplace_back(new FloatLiteral(fContext, Position(), 0.0));
-            args.emplace_back(new FloatLiteral(fContext, Position(), 0.0));
-            Constructor ctor(Position(), *fContext.fVec2_Type, std::move(args));
-            SpvId coords = this->writeConstantVector(ctor);
-            if (1 == c.fArguments.size()) {
-                this->writeInstruction(SpvOpImageRead,
-                                       this->getType(c.fType),
-                                       result,
-                                       img,
-                                       coords,
-                                       out);
-            } else {
-                SkASSERT(2 == c.fArguments.size());
-                SpvId sample = this->writeExpression(*c.fArguments[1], out);
-                this->writeInstruction(SpvOpImageRead,
-                                       this->getType(c.fType),
-                                       result,
-                                       img,
-                                       coords,
-                                       SpvImageOperandsSampleMask,
-                                       sample,
-                                       out);
-            }
-            break;
-        }
     }
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, OutputStream& out) {
     const auto& entry = fFunctionMap.find(&c.fFunction);
     if (entry == fFunctionMap.end()) {
         return this->writeIntrinsicCall(c, out);
@@ -1464,7 +1491,7 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeFloatConstructor(const Constructor& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeFloatConstructor(const Constructor& c, OutputStream& out) {
     ASSERT(c.fType == *fContext.fFloat_Type);
     ASSERT(c.fArguments.size() == 1);
     ASSERT(c.fArguments[0]->fType.isNumber());
@@ -1482,7 +1509,7 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeIntConstructor(const Constructor& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeIntConstructor(const Constructor& c, OutputStream& out) {
     ASSERT(c.fType == *fContext.fInt_Type);
     ASSERT(c.fArguments.size() == 1);
     ASSERT(c.fArguments[0]->fType.isNumber());
@@ -1501,7 +1528,7 @@
 }
 
 void SPIRVCodeGenerator::writeUniformScaleMatrix(SpvId id, SpvId diagonal, const Type& type,
-                                                 SkWStream& out) {
+                                                 OutputStream& out) {
     FloatLiteral zero(fContext, Position(), 0);
     SpvId zeroId = this->writeFloatLiteral(zero);
     std::vector<SpvId> columnIds;
@@ -1527,11 +1554,11 @@
 }
 
 void SPIRVCodeGenerator::writeMatrixCopy(SpvId id, SpvId src, const Type& srcType,
-                                         const Type& dstType, SkWStream& out) {
+                                         const Type& dstType, OutputStream& out) {
     ABORT("unimplemented");
 }
 
-SpvId SPIRVCodeGenerator::writeMatrixConstructor(const Constructor& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeMatrixConstructor(const Constructor& c, OutputStream& out) {
     ASSERT(c.fType.kind() == Type::kMatrix_Kind);
     // go ahead and write the arguments so we don't try to write new instructions in the middle of
     // an instruction
@@ -1580,7 +1607,7 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeVectorConstructor(const Constructor& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeVectorConstructor(const Constructor& c, OutputStream& out) {
     ASSERT(c.fType.kind() == Type::kVector_Kind);
     if (c.isConstant()) {
         return this->writeConstantVector(c);
@@ -1610,7 +1637,7 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeConstructor(const Constructor& c, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeConstructor(const Constructor& c, OutputStream& out) {
     if (c.fType == *fContext.fFloat_Type) {
         return this->writeFloatConstructor(c, out);
     } else if (c.fType == *fContext.fInt_Type) {
@@ -1645,8 +1672,13 @@
 
 SpvStorageClass_ get_storage_class(const Expression& expr) {
     switch (expr.fKind) {
-        case Expression::kVariableReference_Kind:
-            return get_storage_class(((VariableReference&) expr).fVariable.fModifiers);
+        case Expression::kVariableReference_Kind: {
+            const Variable& var = ((VariableReference&) expr).fVariable;
+            if (var.fStorage != Variable::kGlobal_Storage) {
+                return SpvStorageClassFunction;
+            }
+            return get_storage_class(var.fModifiers);
+        }
         case Expression::kFieldAccess_Kind:
             return get_storage_class(*((FieldAccess&) expr).fBase);
         case Expression::kIndex_Kind:
@@ -1656,7 +1688,7 @@
     }
 }
 
-std::vector<SpvId> SPIRVCodeGenerator::getAccessChain(const Expression& expr, SkWStream& out) {
+std::vector<SpvId> SPIRVCodeGenerator::getAccessChain(const Expression& expr, OutputStream& out) {
     std::vector<SpvId> chain;
     switch (expr.fKind) {
         case Expression::kIndex_Kind: {
@@ -1689,13 +1721,13 @@
         return fPointer;
     }
 
-    virtual SpvId load(SkWStream& out) override {
+    virtual SpvId load(OutputStream& out) override {
         SpvId result = fGen.nextId();
         fGen.writeInstruction(SpvOpLoad, fType, result, fPointer, out);
         return result;
     }
 
-    virtual void store(SpvId value, SkWStream& out) override {
+    virtual void store(SpvId value, OutputStream& out) override {
         fGen.writeInstruction(SpvOpStore, fPointer, value, out);
     }
 
@@ -1719,7 +1751,7 @@
         return 0;
     }
 
-    virtual SpvId load(SkWStream& out) override {
+    virtual SpvId load(OutputStream& out) override {
         SpvId base = fGen.nextId();
         fGen.writeInstruction(SpvOpLoad, fGen.getType(fBaseType), base, fVecPointer, out);
         SpvId result = fGen.nextId();
@@ -1734,7 +1766,7 @@
         return result;
     }
 
-    virtual void store(SpvId value, SkWStream& out) override {
+    virtual void store(SpvId value, OutputStream& out) override {
         // use OpVectorShuffle to mix and match the vector components. We effectively create
         // a virtual vector out of the concatenation of the left and right vectors, and then
         // select components from this virtual vector to make the result vector. For
@@ -1781,7 +1813,7 @@
 };
 
 std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const Expression& expr,
-                                                                          SkWStream& out) {
+                                                                          OutputStream& out) {
     switch (expr.fKind) {
         case Expression::kVariableReference_Kind: {
             const Variable& var = ((VariableReference&) expr).fVariable;
@@ -1854,7 +1886,7 @@
     }
 }
 
-SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, OutputStream& out) {
     SpvId result = this->nextId();
     auto entry = fVariableMap.find(&ref.fVariable);
     ASSERT(entry != fVariableMap.end());
@@ -1865,18 +1897,23 @@
         // need to remap to a top-left coordinate system
         if (fRTHeightStructId == (SpvId) -1) {
             // height variable hasn't been written yet
-            std::shared_ptr<SymbolTable> st(new SymbolTable(fErrors));
+            std::shared_ptr<SymbolTable> st(new SymbolTable(&fErrors));
             ASSERT(fRTHeightFieldIndex == (SpvId) -1);
             std::vector<Type::Field> fields;
-            fields.emplace_back(Modifiers(), SkString(SKSL_RTHEIGHT_NAME),
+            fields.emplace_back(Modifiers(), String(SKSL_RTHEIGHT_NAME),
                                 fContext.fFloat_Type.get());
-            SkString name("sksl_synthetic_uniforms");
+            String name("sksl_synthetic_uniforms");
             Type intfStruct(Position(), name, fields);
-            Layout layout(-1, -1, 1, -1, -1, -1, -1, false, false, false, Layout::Format::kUnspecified,
-                          false, Layout::kUnspecified_Primitive, -1, -1);
-            Variable intfVar(Position(), Modifiers(layout, Modifiers::kUniform_Flag), name,
-                             intfStruct, Variable::kGlobal_Storage);
-            InterfaceBlock intf(Position(), intfVar, name, SkString(""),
+            Layout layout(-1, -1, 1, -1, -1, -1, -1, false, false, false,
+                          Layout::Format::kUnspecified, false, Layout::kUnspecified_Primitive, -1,
+                          -1);
+            Variable* intfVar = new Variable(Position(),
+                                             Modifiers(layout, Modifiers::kUniform_Flag),
+                                             name,
+                                             intfStruct,
+                                             Variable::kGlobal_Storage);
+            fSynthetics.takeOwnership(intfVar);
+            InterfaceBlock intf(Position(), intfVar, name, String(""),
                                 std::vector<std::unique_ptr<Expression>>(), st);
             fRTHeightStructId = this->writeInterfaceBlock(intf);
             fRTHeightFieldIndex = 0;
@@ -1920,15 +1957,15 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, OutputStream& out) {
     return getLValue(expr, out)->load(out);
 }
 
-SpvId SPIRVCodeGenerator::writeFieldAccess(const FieldAccess& f, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeFieldAccess(const FieldAccess& f, OutputStream& out) {
     return getLValue(f, out)->load(out);
 }
 
-SpvId SPIRVCodeGenerator::writeSwizzle(const Swizzle& swizzle, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeSwizzle(const Swizzle& swizzle, OutputStream& out) {
     SpvId base = this->writeExpression(*swizzle.fBase, out);
     SpvId result = this->nextId();
     size_t count = swizzle.fComponents.size();
@@ -1951,7 +1988,7 @@
 SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType,
                                                const Type& operandType, SpvId lhs,
                                                SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt,
-                                               SpvOp_ ifUInt, SpvOp_ ifBool, SkWStream& out) {
+                                               SpvOp_ ifUInt, SpvOp_ ifBool, OutputStream& out) {
     SpvId result = this->nextId();
     if (is_float(fContext, operandType)) {
         this->writeInstruction(ifFloat, this->getType(resultType), result, lhs, rhs, out);
@@ -1989,7 +2026,7 @@
     }
 }
 
-SpvId SPIRVCodeGenerator::foldToBool(SpvId id, const Type& operandType, SkWStream& out) {
+SpvId SPIRVCodeGenerator::foldToBool(SpvId id, const Type& operandType, OutputStream& out) {
     if (operandType.kind() == Type::kVector_Kind) {
         SpvId result = this->nextId();
         this->writeInstruction(SpvOpAll, this->getType(*fContext.fBool_Type), result, id, out);
@@ -1998,7 +2035,7 @@
     return id;
 }
 
-SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, OutputStream& out) {
     // handle cases where we don't necessarily evaluate both LHS and RHS
     switch (b.fOperator) {
         case Token::EQ: {
@@ -2151,6 +2188,9 @@
         case Token::SLASH:
             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv,
                                               SpvOpSDiv, SpvOpUDiv, SpvOpUndef, out);
+        case Token::PERCENT:
+            return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMod,
+                                              SpvOpSMod, SpvOpUMod, SpvOpUndef, out);
         case Token::PLUSEQ: {
             SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd,
                                                       SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
@@ -2189,13 +2229,20 @@
             lvalue->store(result, out);
             return result;
         }
+        case Token::PERCENTEQ: {
+            SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMod,
+                                                      SpvOpSMod, SpvOpUMod, SpvOpUndef, out);
+            ASSERT(lvalue);
+            lvalue->store(result, out);
+            return result;
+        }
         default:
             // FIXME: missing support for some operators (bitwise, &&=, ||=, shift...)
             ABORT("unsupported binary expression: %s", b.description().c_str());
     }
 }
 
-SpvId SPIRVCodeGenerator::writeLogicalAnd(const BinaryExpression& a, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeLogicalAnd(const BinaryExpression& a, OutputStream& out) {
     ASSERT(a.fOperator == Token::LOGICALAND);
     BoolLiteral falseLiteral(fContext, Position(), false);
     SpvId falseConstant = this->writeBoolLiteral(falseLiteral);
@@ -2216,7 +2263,7 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeLogicalOr(const BinaryExpression& o, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeLogicalOr(const BinaryExpression& o, OutputStream& out) {
     ASSERT(o.fOperator == Token::LOGICALOR);
     BoolLiteral trueLiteral(fContext, Position(), true);
     SpvId trueConstant = this->writeBoolLiteral(trueLiteral);
@@ -2237,7 +2284,7 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, OutputStream& out) {
     SpvId test = this->writeExpression(*t.fTest, out);
     if (t.fIfTrue->isConstant() && t.fIfFalse->isConstant()) {
         // both true and false are constants, can just use OpSelect
@@ -2277,11 +2324,11 @@
     else if (type == *context.fFloat_Type) {
         return std::unique_ptr<Expression>(new FloatLiteral(context, Position(), 1.0));
     } else {
-        ABORT("math is unsupported on type '%s'")
+        ABORT("math is unsupported on type '%s'", type.name().c_str());
     }
 }
 
-SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, OutputStream& out) {
     if (p.fOperator == Token::MINUS) {
         SpvId result = this->nextId();
         SpvId typeId = this->getType(p.fType);
@@ -2334,7 +2381,7 @@
     }
 }
 
-SpvId SPIRVCodeGenerator::writePostfixExpression(const PostfixExpression& p, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writePostfixExpression(const PostfixExpression& p, OutputStream& out) {
     std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
     SpvId result = lv->load(out);
     SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
@@ -2431,7 +2478,7 @@
     }
 }
 
-SpvId SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, OutputStream& out) {
     SpvId result = fFunctionMap[&f];
     this->writeInstruction(SpvOpFunction, this->getType(f.fReturnType), result,
                            SpvFunctionControlMaskNone, this->getFunctionType(f), out);
@@ -2446,16 +2493,17 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeFunction(const FunctionDefinition& f, SkWStream& out) {
+SpvId SPIRVCodeGenerator::writeFunction(const FunctionDefinition& f, OutputStream& out) {
+    fVariableBuffer.reset();
     SpvId result = this->writeFunctionStart(f.fDeclaration, out);
     this->writeLabel(this->nextId(), out);
     if (f.fDeclaration.fName == "main") {
-        write_data(*fGlobalInitializersBuffer.detachAsData(), out);
+        write_stringstream(fGlobalInitializersBuffer, out);
     }
-    SkDynamicMemoryWStream bodyBuffer;
-    this->writeBlock(*f.fBody, bodyBuffer);
-    write_data(*fVariableBuffer.detachAsData(), out);
-    write_data(*bodyBuffer.detachAsData(), out);
+    StringStream bodyBuffer;
+    this->writeBlock((Block&) *f.fBody, bodyBuffer);
+    write_stringstream(fVariableBuffer, out);
+    write_stringstream(bodyBuffer, out);
     if (fCurrentBlock) {
         this->writeInstruction(SpvOpReturn, out);
     }
@@ -2518,7 +2566,8 @@
 }
 
 SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
-    MemoryLayout layout = intf.fVariable.fModifiers.fLayout.fPushConstant ?
+    bool isBuffer = (0 != (intf.fVariable.fModifiers.fFlags & Modifiers::kBuffer_Flag));
+    MemoryLayout layout = (intf.fVariable.fModifiers.fLayout.fPushConstant || isBuffer) ?
                           MemoryLayout(MemoryLayout::k430_Standard) :
                           fDefaultLayout;
     SpvId result = this->nextId();
@@ -2529,11 +2578,15 @@
         std::vector<Type::Field> fields = type->fields();
         fRTHeightStructId = result;
         fRTHeightFieldIndex = fields.size();
-        fields.emplace_back(Modifiers(), SkString(SKSL_RTHEIGHT_NAME), fContext.fFloat_Type.get());
+        fields.emplace_back(Modifiers(), String(SKSL_RTHEIGHT_NAME), fContext.fFloat_Type.get());
         type = new Type(type->fPosition, type->name(), fields);
     }
     SpvId typeId = this->getType(*type, layout);
-    this->writeInstruction(SpvOpDecorate, typeId, SpvDecorationBlock, fDecorationBuffer);
+    if (intf.fVariable.fModifiers.fFlags & Modifiers::kBuffer_Flag) {
+        this->writeInstruction(SpvOpDecorate, typeId, SpvDecorationBufferBlock, fDecorationBuffer);
+    } else {
+        this->writeInstruction(SpvOpDecorate, typeId, SpvDecorationBlock, fDecorationBuffer);
+    }
     SpvStorageClass_ storageClass = get_storage_class(intf.fVariable.fModifiers);
     SpvId ptrType = this->nextId();
     this->writeInstruction(SpvOpTypePointer, ptrType, storageClass, typeId, fConstantBuffer);
@@ -2548,9 +2601,12 @@
 
 #define BUILTIN_IGNORE 9999
 void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl,
-                                         SkWStream& out) {
+                                         OutputStream& out) {
     for (size_t i = 0; i < decl.fVars.size(); i++) {
-        const VarDeclaration& varDecl = decl.fVars[i];
+        if (decl.fVars[i]->fKind == Statement::kNop_Kind) {
+            continue;
+        }
+        const VarDeclaration& varDecl = (VarDeclaration&) *decl.fVars[i];
         const Variable* var = varDecl.fVar;
         // These haven't been implemented in our SPIR-V generator yet and we only currently use them
         // in the OpenGL backend.
@@ -2569,7 +2625,8 @@
         if (!var->fReadCount && !var->fWriteCount &&
                 !(var->fModifiers.fFlags & (Modifiers::kIn_Flag |
                                             Modifiers::kOut_Flag |
-                                            Modifiers::kUniform_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)
             continue;
@@ -2610,8 +2667,12 @@
     }
 }
 
-void SPIRVCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, SkWStream& out) {
-    for (const auto& varDecl : decl.fVars) {
+void SPIRVCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, OutputStream& out) {
+    for (const auto& stmt : decl.fVars) {
+        if (stmt->fKind == Statement::kNop_Kind) {
+            continue;
+        }
+        VarDeclaration& varDecl = (VarDeclaration&) *stmt;
         const Variable* var = varDecl.fVar;
         // These haven't been implemented in our SPIR-V generator yet and we only currently use them
         // in the OpenGL backend.
@@ -2632,8 +2693,10 @@
     }
 }
 
-void SPIRVCodeGenerator::writeStatement(const Statement& s, SkWStream& out) {
+void SPIRVCodeGenerator::writeStatement(const Statement& s, OutputStream& out) {
     switch (s.fKind) {
+        case Statement::kNop_Kind:
+            break;
         case Statement::kBlock_Kind:
             this->writeBlock((Block&) s, out);
             break;
@@ -2672,13 +2735,13 @@
     }
 }
 
-void SPIRVCodeGenerator::writeBlock(const Block& b, SkWStream& out) {
+void SPIRVCodeGenerator::writeBlock(const Block& b, OutputStream& out) {
     for (size_t i = 0; i < b.fStatements.size(); i++) {
         this->writeStatement(*b.fStatements[i], out);
     }
 }
 
-void SPIRVCodeGenerator::writeIfStatement(const IfStatement& stmt, SkWStream& out) {
+void SPIRVCodeGenerator::writeIfStatement(const IfStatement& stmt, OutputStream& out) {
     SpvId test = this->writeExpression(*stmt.fTest, out);
     SpvId ifTrue = this->nextId();
     SpvId ifFalse = this->nextId();
@@ -2709,7 +2772,7 @@
     }
 }
 
-void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, SkWStream& out) {
+void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, OutputStream& out) {
     if (f.fInitializer) {
         this->writeStatement(*f.fInitializer, out);
     }
@@ -2744,7 +2807,7 @@
     fContinueTarget.pop();
 }
 
-void SPIRVCodeGenerator::writeWhileStatement(const WhileStatement& w, SkWStream& out) {
+void SPIRVCodeGenerator::writeWhileStatement(const WhileStatement& w, OutputStream& out) {
     // We believe the while loop code below will work, but Skia doesn't actually use them and
     // adequately testing this code in the absence of Skia exercising it isn't straightforward. For
     // the time being, we just fail with an error due to the lack of testing. If you encounter this
@@ -2776,7 +2839,7 @@
     fContinueTarget.pop();
 }
 
-void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, SkWStream& out) {
+void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, OutputStream& out) {
     // We believe the do loop code below will work, but Skia doesn't actually use them and
     // adequately testing this code in the absence of Skia exercising it isn't straightforward. For
     // the time being, we just fail with an error due to the lack of testing. If you encounter this
@@ -2808,7 +2871,7 @@
     fContinueTarget.pop();
 }
 
-void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, SkWStream& out) {
+void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, OutputStream& out) {
     if (r.fExpression) {
         this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.fExpression, out),
                                out);
@@ -2817,9 +2880,9 @@
     }
 }
 
-void SPIRVCodeGenerator::writeInstructions(const Program& program, SkWStream& out) {
+void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream& out) {
     fGLSLExtendedInstructions = this->nextId();
-    SkDynamicMemoryWStream body;
+    StringStream body;
     std::set<SpvId> interfaceVars;
     // assign IDs to functions
     for (size_t i = 0; i < program.fElements.size(); i++) {
@@ -2899,12 +2962,12 @@
         }
     }
 
-    write_data(*fExtraGlobalsBuffer.detachAsData(), out);
-    write_data(*fNameBuffer.detachAsData(), out);
-    write_data(*fDecorationBuffer.detachAsData(), out);
-    write_data(*fConstantBuffer.detachAsData(), out);
-    write_data(*fExternalFunctionsBuffer.detachAsData(), out);
-    write_data(*body.detachAsData(), out);
+    write_stringstream(fExtraGlobalsBuffer, out);
+    write_stringstream(fNameBuffer, out);
+    write_stringstream(fDecorationBuffer, out);
+    write_stringstream(fConstantBuffer, out);
+    write_stringstream(fExternalFunctionsBuffer, out);
+    write_stringstream(body, out);
 }
 
 bool SPIRVCodeGenerator::generateCode() {
@@ -2912,11 +2975,11 @@
     this->writeWord(SpvMagicNumber, *fOut);
     this->writeWord(SpvVersion, *fOut);
     this->writeWord(SKSL_MAGIC, *fOut);
-    SkDynamicMemoryWStream buffer;
+    StringStream buffer;
     this->writeInstructions(fProgram, buffer);
     this->writeWord(fIdCount, *fOut);
     this->writeWord(0, *fOut); // reserved, always zero
-    write_data(*buffer.detachAsData(), *fOut);
+    write_stringstream(buffer, *fOut);
     return 0 == fErrors.errorCount();
 }
 
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index 1cdc653..a343905 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_SPIRVCODEGENERATOR
 #define SKSL_SPIRVCODEGENERATOR
 
@@ -12,7 +12,6 @@
 #include <tuple>
 #include <unordered_map>
 
-#include "SkStream.h"
 #include "SkSLCodeGenerator.h"
 #include "SkSLMemoryLayout.h"
 #include "ir/SkSLBinaryExpression.h"
@@ -59,13 +58,13 @@
         // by a pointer (e.g. vector swizzles), returns 0.
         virtual SpvId getPointer() = 0;
 
-        virtual SpvId load(SkWStream& out) = 0;
+        virtual SpvId load(OutputStream& out) = 0;
 
-        virtual void store(SpvId value, SkWStream& out) = 0;
+        virtual void store(SpvId value, OutputStream& out) = 0;
     };
 
     SPIRVCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
-                       SkWStream* out)
+                       OutputStream* out)
     : INHERITED(program, errors, out)
     , fContext(*context)
     , fDefaultLayout(MemoryLayout::k140_Standard)
@@ -74,7 +73,8 @@
     , fBoolTrue(0)
     , fBoolFalse(0)
     , fSetupFragPosition(false)
-    , fCurrentBlock(0) {
+    , fCurrentBlock(0)
+    , fSynthetics(nullptr, errors) {
         this->setupIntrinsics();
     }
 
@@ -89,8 +89,9 @@
 
     enum SpecialIntrinsic {
         kAtan_SpecialIntrinsic,
-        kTexture_SpecialIntrinsic,
         kSubpassLoad_SpecialIntrinsic,
+        kTexelFetch_SpecialIntrinsic,
+        kTexture_SpecialIntrinsic,
     };
 
     void setupIntrinsics();
@@ -101,6 +102,8 @@
 
     SpvId getType(const Type& type, const MemoryLayout& layout);
 
+    SpvId getImageType(const Type& type);
+
     SpvId getFunctionType(const FunctionDeclaration& function);
 
     SpvId getPointerType(const Type& type, SpvStorageClass_ storageClass);
@@ -108,7 +111,7 @@
     SpvId getPointerType(const Type& type, const MemoryLayout& layout,
                          SpvStorageClass_ storageClass);
 
-    std::vector<SpvId> getAccessChain(const Expression& expr, SkWStream& out);
+    std::vector<SpvId> getAccessChain(const Expression& expr, OutputStream& out);
 
     void writeLayout(const Layout& layout, SpvId target);
 
@@ -116,43 +119,43 @@
 
     void writeStruct(const Type& type, const MemoryLayout& layout, SpvId resultId);
 
-    void writeProgramElement(const ProgramElement& pe, SkWStream& out);
+    void writeProgramElement(const ProgramElement& pe, OutputStream& out);
 
     SpvId writeInterfaceBlock(const InterfaceBlock& intf);
 
-    SpvId writeFunctionStart(const FunctionDeclaration& f, SkWStream& out);
-    
-    SpvId writeFunctionDeclaration(const FunctionDeclaration& f, SkWStream& out);
+    SpvId writeFunctionStart(const FunctionDeclaration& f, OutputStream& out);
 
-    SpvId writeFunction(const FunctionDefinition& f, SkWStream& out);
+    SpvId writeFunctionDeclaration(const FunctionDeclaration& f, OutputStream& out);
 
-    void writeGlobalVars(Program::Kind kind, const VarDeclarations& v, SkWStream& out);
+    SpvId writeFunction(const FunctionDefinition& f, OutputStream& out);
 
-    void writeVarDeclarations(const VarDeclarations& decl, SkWStream& out);
+    void writeGlobalVars(Program::Kind kind, const VarDeclarations& v, OutputStream& out);
 
-    SpvId writeVariableReference(const VariableReference& ref, SkWStream& out);
+    void writeVarDeclarations(const VarDeclarations& decl, OutputStream& out);
 
-    std::unique_ptr<LValue> getLValue(const Expression& value, SkWStream& out);
+    SpvId writeVariableReference(const VariableReference& ref, OutputStream& out);
 
-    SpvId writeExpression(const Expression& expr, SkWStream& out);
-    
-    SpvId writeIntrinsicCall(const FunctionCall& c, SkWStream& out);
+    std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out);
 
-    SpvId writeFunctionCall(const FunctionCall& c, SkWStream& out);
+    SpvId writeExpression(const Expression& expr, OutputStream& out);
 
-    SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, SkWStream& out);
+    SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out);
+
+    SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out);
+
+    SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, OutputStream& out);
 
     SpvId writeConstantVector(const Constructor& c);
 
-    SpvId writeFloatConstructor(const Constructor& c, SkWStream& out);
+    SpvId writeFloatConstructor(const Constructor& c, OutputStream& out);
 
-    SpvId writeIntConstructor(const Constructor& c, SkWStream& out);
+    SpvId writeIntConstructor(const Constructor& c, OutputStream& out);
 
     /**
      * Writes a matrix with the diagonal entries all equal to the provided expression, and all other
      * entries equal to zero.
      */
-    void writeUniformScaleMatrix(SpvId id, SpvId diagonal, const Type& type, SkWStream& out);
+    void writeUniformScaleMatrix(SpvId id, SpvId diagonal, const Type& type, OutputStream& out);
 
     /**
      * Writes a potentially-different-sized copy of a matrix. Entries which do not exist in the
@@ -160,17 +163,17 @@
      * ignored.
      */
     void writeMatrixCopy(SpvId id, SpvId src, const Type& srcType, const Type& dstType,
-                         SkWStream& out);
+                         OutputStream& out);
 
-    SpvId writeMatrixConstructor(const Constructor& c, SkWStream& out);
+    SpvId writeMatrixConstructor(const Constructor& c, OutputStream& out);
 
-    SpvId writeVectorConstructor(const Constructor& c, SkWStream& out);
+    SpvId writeVectorConstructor(const Constructor& c, OutputStream& out);
 
-    SpvId writeConstructor(const Constructor& c, SkWStream& out);
+    SpvId writeConstructor(const Constructor& c, OutputStream& out);
 
-    SpvId writeFieldAccess(const FieldAccess& f, SkWStream& out);
+    SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out);
 
-    SpvId writeSwizzle(const Swizzle& swizzle, SkWStream& out);
+    SpvId writeSwizzle(const Swizzle& swizzle, OutputStream& out);
 
     /**
      * Folds the potentially-vector result of a logical operation down to a single bool. If
@@ -178,28 +181,28 @@
      * same dimensions, and applys all() to it to fold it down to a single bool value. Otherwise,
      * returns the original id value.
      */
-    SpvId foldToBool(SpvId id, const Type& operandType, SkWStream& out);
+    SpvId foldToBool(SpvId id, const Type& operandType, OutputStream& out);
 
     SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs,
                                SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
-                               SpvOp_ ifBool, SkWStream& out);
+                               SpvOp_ ifBool, OutputStream& out);
 
     SpvId writeBinaryOperation(const BinaryExpression& expr, SpvOp_ ifFloat, SpvOp_ ifInt,
-                               SpvOp_ ifUInt, SkWStream& out);
+                               SpvOp_ ifUInt, OutputStream& out);
 
-    SpvId writeBinaryExpression(const BinaryExpression& b, SkWStream& out);
+    SpvId writeBinaryExpression(const BinaryExpression& b, OutputStream& out);
 
-    SpvId writeTernaryExpression(const TernaryExpression& t, SkWStream& out);
+    SpvId writeTernaryExpression(const TernaryExpression& t, OutputStream& out);
 
-    SpvId writeIndexExpression(const IndexExpression& expr, SkWStream& out);
+    SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out);
 
-    SpvId writeLogicalAnd(const BinaryExpression& b, SkWStream& out);
+    SpvId writeLogicalAnd(const BinaryExpression& b, OutputStream& out);
 
-    SpvId writeLogicalOr(const BinaryExpression& o, SkWStream& out);
+    SpvId writeLogicalOr(const BinaryExpression& o, OutputStream& out);
 
-    SpvId writePrefixExpression(const PrefixExpression& p, SkWStream& out);
+    SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out);
 
-    SpvId writePostfixExpression(const PostfixExpression& p, SkWStream& out);
+    SpvId writePostfixExpression(const PostfixExpression& p, OutputStream& out);
 
     SpvId writeBoolLiteral(const BoolLiteral& b);
 
@@ -207,63 +210,63 @@
 
     SpvId writeFloatLiteral(const FloatLiteral& f);
 
-    void writeStatement(const Statement& s, SkWStream& out);
+    void writeStatement(const Statement& s, OutputStream& out);
 
-    void writeBlock(const Block& b, SkWStream& out);
+    void writeBlock(const Block& b, OutputStream& out);
 
-    void writeIfStatement(const IfStatement& stmt, SkWStream& out);
+    void writeIfStatement(const IfStatement& stmt, OutputStream& out);
 
-    void writeForStatement(const ForStatement& f, SkWStream& out);
+    void writeForStatement(const ForStatement& f, OutputStream& out);
 
-    void writeWhileStatement(const WhileStatement& w, SkWStream& out);
+    void writeWhileStatement(const WhileStatement& w, OutputStream& out);
 
-    void writeDoStatement(const DoStatement& d, SkWStream& out);
+    void writeDoStatement(const DoStatement& d, OutputStream& out);
 
-    void writeReturnStatement(const ReturnStatement& r, SkWStream& out);
+    void writeReturnStatement(const ReturnStatement& r, OutputStream& out);
 
-    void writeCapabilities(SkWStream& out);
+    void writeCapabilities(OutputStream& out);
 
-    void writeInstructions(const Program& program, SkWStream& out);
+    void writeInstructions(const Program& program, OutputStream& out);
 
-    void writeOpCode(SpvOp_ opCode, int length, SkWStream& out);
+    void writeOpCode(SpvOp_ opCode, int length, OutputStream& out);
 
-    void writeWord(int32_t word, SkWStream& out);
+    void writeWord(int32_t word, OutputStream& out);
 
-    void writeString(const char* string, SkWStream& out);
+    void writeString(const char* string, OutputStream& out);
 
-    void writeLabel(SpvId id, SkWStream& out);
+    void writeLabel(SpvId id, OutputStream& out);
 
-    void writeInstruction(SpvOp_ opCode, SkWStream& out);
+    void writeInstruction(SpvOp_ opCode, OutputStream& out);
 
-    void writeInstruction(SpvOp_ opCode, const char* string, SkWStream& out);
+    void writeInstruction(SpvOp_ opCode, const char* string, OutputStream& out);
 
-    void writeInstruction(SpvOp_ opCode, int32_t word1, SkWStream& out);
+    void writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out);
 
-    void writeInstruction(SpvOp_ opCode, int32_t word1, const char* string, SkWStream& out);
+    void writeInstruction(SpvOp_ opCode, int32_t word1, const char* string, OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, const char* string,
-                          SkWStream& out);
+                          OutputStream& out);
 
-    void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, SkWStream& out);
+    void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3,
-                          SkWStream& out);
+                          OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
-                          SkWStream& out);
+                          OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
-                          int32_t word5, SkWStream& out);
+                          int32_t word5, OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
-                          int32_t word5, int32_t word6, SkWStream& out);
+                          int32_t word5, int32_t word6, OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
-                          int32_t word5, int32_t word6, int32_t word7, SkWStream& out);
+                          int32_t word5, int32_t word6, int32_t word7, OutputStream& out);
 
     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
                           int32_t word5, int32_t word6, int32_t word7, int32_t word8,
-                          SkWStream& out);
+                          OutputStream& out);
 
     const Context& fContext;
     const MemoryLayout fDefaultLayout;
@@ -272,19 +275,20 @@
     SpvId fIdCount;
     SpvId fGLSLExtendedInstructions;
     typedef std::tuple<IntrinsicKind, int32_t, int32_t, int32_t, int32_t> Intrinsic;
-    std::unordered_map<SkString, Intrinsic> fIntrinsicMap;
+    std::unordered_map<String, Intrinsic> fIntrinsicMap;
     std::unordered_map<const FunctionDeclaration*, SpvId> fFunctionMap;
     std::unordered_map<const Variable*, SpvId> fVariableMap;
     std::unordered_map<const Variable*, int32_t> fInterfaceBlockMap;
-    std::unordered_map<SkString, SpvId> fTypeMap;
-    SkDynamicMemoryWStream fCapabilitiesBuffer;
-    SkDynamicMemoryWStream fGlobalInitializersBuffer;
-    SkDynamicMemoryWStream fConstantBuffer;
-    SkDynamicMemoryWStream fExtraGlobalsBuffer;
-    SkDynamicMemoryWStream fExternalFunctionsBuffer;
-    SkDynamicMemoryWStream fVariableBuffer;
-    SkDynamicMemoryWStream fNameBuffer;
-    SkDynamicMemoryWStream fDecorationBuffer;
+    std::unordered_map<String, SpvId> fImageTypeMap;
+    std::unordered_map<String, SpvId> fTypeMap;
+    StringStream fCapabilitiesBuffer;
+    StringStream fGlobalInitializersBuffer;
+    StringStream fConstantBuffer;
+    StringStream fExtraGlobalsBuffer;
+    StringStream fExternalFunctionsBuffer;
+    StringStream fVariableBuffer;
+    StringStream fNameBuffer;
+    StringStream fDecorationBuffer;
 
     SpvId fBoolTrue;
     SpvId fBoolFalse;
@@ -299,6 +303,8 @@
     std::stack<SpvId> fContinueTarget;
     SpvId fRTHeightStructId = (SpvId) -1;
     SpvId fRTHeightFieldIndex = (SpvId) -1;
+    // holds variables synthesized during output, for lifetime purposes
+    SymbolTable fSynthetics;
 
     friend class PointerLValue;
     friend class SwizzleLValue;
diff --git a/src/sksl/SkSLString.cpp b/src/sksl/SkSLString.cpp
new file mode 100644
index 0000000..9b2c178
--- /dev/null
+++ b/src/sksl/SkSLString.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "SkSLString.h"
+
+#include "SkSLUtil.h"
+#include <errno.h>
+#include <limits.h>
+#include <locale>
+#include <sstream>
+#include <string>
+
+namespace SkSL {
+
+String String::printf(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    String result;
+    result.vappendf(fmt, args);
+    return result;
+}
+
+#ifdef SKSL_STANDALONE
+void String::appendf(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    this->vappendf(fmt, args);
+}
+#endif
+
+void String::vappendf(const char* fmt, va_list args) {
+#ifdef SKSL_BUILD_FOR_WIN
+    #define VSNPRINTF    _vsnprintf
+#else
+    #define VSNPRINTF    vsnprintf
+#endif
+    #define BUFFER_SIZE 256
+    char buffer[BUFFER_SIZE];
+    size_t size = VSNPRINTF(buffer, BUFFER_SIZE, fmt, args);
+    if (BUFFER_SIZE >= size) {
+        this->append(buffer, size);
+    } else {
+        auto newBuffer = std::unique_ptr<char[]>(new char[size]);
+        VSNPRINTF(newBuffer.get(), size, fmt, args);
+        this->append(newBuffer.get(), size);
+    }
+    va_end(args);
+}
+
+
+bool String::startsWith(const char* s) const {
+    return !strncmp(c_str(), s, strlen(s));
+}
+
+bool String::endsWith(const char* s) const {
+    size_t len = strlen(s);
+    if (size() < len) {
+        return false;
+    }
+    return !strncmp(c_str() + size() - len, s, len);
+}
+
+String String::operator+(const char* s) const {
+    String result(*this);
+    result.append(s);
+    return result;
+}
+
+String String::operator+(const String& s) const {
+    String result(*this);
+    result.append(s);
+    return result;
+}
+
+bool String::operator==(const String& s) const {
+    return this->size() == s.size() && !memcmp(c_str(), s.c_str(), this->size());
+}
+
+bool String::operator!=(const String& s) const {
+    return !(*this == s);
+}
+
+bool String::operator==(const char* s) const {
+    return this->size() == strlen(s) && !memcmp(c_str(), s, this->size());
+}
+
+bool String::operator!=(const char* s) const {
+    return !(*this == s);
+}
+
+String operator+(const char* s1, const String& s2) {
+    String result(s1);
+    result.append(s2);
+    return result;
+}
+
+bool operator==(const char* s1, const String& s2) {
+    return s2 == s1;
+}
+
+bool operator!=(const char* s1, const String& s2) {
+    return s2 != s1;
+}
+
+String to_string(int32_t value) {
+    return SkSL::String::printf("%d", value);
+}
+
+String to_string(uint32_t value) {
+    return SkSL::String::printf("%u", value);
+}
+
+String to_string(int64_t value) {
+    std::stringstream buffer;
+    buffer << value;
+    return String(buffer.str().c_str());
+}
+
+String to_string(uint64_t value) {
+    std::stringstream buffer;
+    buffer << value;
+    return String(buffer.str().c_str());
+}
+
+String to_string(double value) {
+#ifdef SKSL_BUILD_FOR_WIN
+    #define SNPRINTF    _snprintf
+#else
+    #define SNPRINTF    snprintf
+#endif
+#define MAX_DOUBLE_CHARS 25
+    char buffer[MAX_DOUBLE_CHARS];
+    SKSL_DEBUGCODE(int len = )SNPRINTF(buffer, sizeof(buffer), "%.17g", value);
+    ASSERT(len < MAX_DOUBLE_CHARS);
+    String result(buffer);
+    if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
+        result += ".0";
+    }
+    return result;
+#undef SNPRINTF
+#undef MAX_DOUBLE_CHARS
+}
+
+int stoi(String s) {
+    char* p;
+    SKSL_DEBUGCODE(errno = 0;)
+    long result = strtoul(s.c_str(), &p, 0);
+    ASSERT(*p == 0);
+    ASSERT(!errno);
+    return (int) result;
+}
+
+double stod(String s) {
+    double result;
+    std::string str(s.c_str(), s.size());
+    std::stringstream buffer(str);
+    buffer.imbue(std::locale::classic());
+    buffer >> result;
+    ASSERT(!buffer.fail());
+    return result;
+}
+
+long stol(String s) {
+    char* p;
+    SKSL_DEBUGCODE(errno = 0;)
+    long result = strtoul(s.c_str(), &p, 0);
+    ASSERT(*p == 0);
+    ASSERT(!errno);
+    return result;
+}
+
+} // namespace
diff --git a/src/sksl/SkSLString.h b/src/sksl/SkSLString.h
new file mode 100644
index 0000000..73ba746
--- /dev/null
+++ b/src/sksl/SkSLString.h
@@ -0,0 +1,105 @@
+/*
+ * 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 SKSL_STRING
+#define SKSL_STRING
+
+
+#ifdef SKSL_STANDALONE
+    #define SKSL_STRING_BASE std::string
+    #include <string>
+#else
+    #define SKSL_STRING_BASE SkString
+    #include "SkString.h"
+#endif
+
+namespace SkSL {
+
+class String : public SKSL_STRING_BASE {
+public:
+    String() = default;
+    String(const String&) = default;
+    String(String&&) = default;
+    String& operator=(const String&) = default;
+    String& operator=(String&&) = default;
+
+#ifndef SKSL_STANDALONE
+    String(const SkString& s)
+    : INHERITED(s) {}
+#endif
+
+    String(const char* s)
+    : INHERITED(s) {}
+
+    String(const char* s, size_t size)
+    : INHERITED(s, size) {}
+
+    static String printf(const char* fmt, ...);
+
+#ifdef SKSL_STANDALONE
+    void appendf(const char* fmt, ...);
+#endif
+    void vappendf(const char* fmt, va_list va);
+
+    bool startsWith(const char* s) const;
+    bool endsWith(const char* s) const;
+
+    String operator+(const char* s) const;
+    String operator+(const String& s) const;
+    bool operator==(const char* s) const;
+    bool operator!=(const char* s) const;
+    bool operator==(const String& s) const;
+    bool operator!=(const String& s) const;
+    friend String operator+(const char* s1, const String& s2);
+    friend bool operator==(const char* s1, const String& s2);
+    friend bool operator!=(const char* s1, const String& s2);
+
+private:
+    typedef SKSL_STRING_BASE INHERITED;
+};
+
+String operator+(const char* s1, const String& s2);
+bool operator!=(const char* s1, const String& s2);
+
+String to_string(double value);
+
+String to_string(int32_t value);
+
+String to_string(uint32_t value);
+
+String to_string(int64_t value);
+
+String to_string(uint64_t value);
+
+int stoi(String s);
+
+double stod(String s);
+
+long stol(String s);
+
+} // namespace
+
+#ifdef SKSL_STANDALONE
+namespace std {
+    template<> struct hash<SkSL::String> {
+        size_t operator()(const SkSL::String& s) const {
+            return hash<std::string>{}(s);
+        }
+    };
+} // namespace
+#else
+#include "SkOpts.h"
+namespace std {
+    template<> struct hash<SkSL::String> {
+        size_t operator()(const SkSL::String& s) const {
+            return SkOpts::hash_fn(s.c_str(), s.size(), 0);
+        }
+    };
+} // namespace
+#endif // SKIA_STANDALONE
+
+#endif
diff --git a/src/sksl/SkSLStringStream.h b/src/sksl/SkSLStringStream.h
new file mode 100644
index 0000000..9061432
--- /dev/null
+++ b/src/sksl/SkSLStringStream.h
@@ -0,0 +1,99 @@
+/*
+ * 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 SKSL_STRINGSTREAM
+#define SKSL_STRINGSTREAM
+
+#include "SkSLOutputStream.h"
+
+#ifdef SKSL_STANDALONE
+
+namespace SkSL {
+
+class StringStream : public OutputStream {
+public:
+    void write8(uint8_t b) override {
+        fBuffer += (char) b;
+    }
+
+    void writeText(const char* s) override {
+        fBuffer += s;
+    }
+
+    void write(const void* s, size_t size) override {
+        fBuffer.append((const char*) s, size);
+    }
+
+    const char* data() const {
+        return fBuffer.c_str();
+    }
+
+    size_t size() const {
+        return fBuffer.size();
+    }
+
+    void reset() {
+        fBuffer = "";
+    }
+
+private:
+    String fBuffer;
+};
+
+#else
+
+#include "SkData.h"
+#include "SkStream.h"
+
+namespace SkSL {
+
+class StringStream : public OutputStream {
+public:
+    void write8(uint8_t b) override {
+        SkASSERT(!fData);
+        fStream.write8(b);
+    }
+
+    void writeText(const char* s) override {
+        SkASSERT(!fData);
+        fStream.writeText(s);
+    }
+
+    void write(const void* s, size_t size) override {
+        SkASSERT(!fData);
+        fStream.write(s, size);
+    }
+
+    const char* data() const {
+        if (!fData) {
+            fData = fStream.detachAsData();
+        }
+        return (const char*) fData->data();
+    }
+
+    size_t size() const {
+        if (!fData) {
+            fData = fStream.detachAsData();
+        }
+        return fData->size();
+    }
+
+    void reset() {
+        fStream.reset();
+        fData = nullptr;
+    }
+
+private:
+    mutable SkDynamicMemoryWStream fStream;
+    mutable sk_sp<SkData> fData;
+};
+
+#endif // SKSL_STANDALONE
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLToken.h b/src/sksl/SkSLToken.h
index 6bacb90..92193b9 100644
--- a/src/sksl/SkSLToken.h
+++ b/src/sksl/SkSLToken.h
@@ -4,13 +4,13 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_TOKEN
 #define SKSL_TOKEN
 
 #include "SkSLPosition.h"
 #include "SkSLUtil.h"
- 
+
 namespace SkSL {
 
 #undef IN
@@ -78,11 +78,13 @@
         LOGICALANDEQ,
         SEMICOLON,
         IF,
+        STATIC_IF,
         ELSE,
         FOR,
         WHILE,
         DO,
         SWITCH,
+        STATIC_SWITCH,
         CASE,
         DEFAULT,
         RETURN,
@@ -104,6 +106,8 @@
         COHERENT,
         VOLATILE,
         RESTRICT,
+        BUFFER,
+        HASSIDEEFFECTS,
         STRUCT,
         LAYOUT,
         DIRECTIVE,
@@ -131,54 +135,55 @@
         INVALID_TOKEN
     };
 
-    static SkString OperatorName(Kind kind) {
+    static String OperatorName(Kind kind) {
         switch (kind) {
-            case Token::PLUS:         return SkString("+");
-            case Token::MINUS:        return SkString("-");
-            case Token::STAR:         return SkString("*");
-            case Token::SLASH:        return SkString("/");
-            case Token::PERCENT:      return SkString("%");
-            case Token::SHL:          return SkString("<<");
-            case Token::SHR:          return SkString(">>");
-            case Token::LOGICALNOT:   return SkString("!");
-            case Token::LOGICALAND:   return SkString("&&");
-            case Token::LOGICALOR:    return SkString("||");
-            case Token::LOGICALXOR:   return SkString("^^");
-            case Token::BITWISENOT:   return SkString("~");
-            case Token::BITWISEAND:   return SkString("&");
-            case Token::BITWISEOR:    return SkString("|");
-            case Token::BITWISEXOR:   return SkString("^");
-            case Token::EQ:           return SkString("=");
-            case Token::EQEQ:         return SkString("==");
-            case Token::NEQ:          return SkString("!=");
-            case Token::LT:           return SkString("<");
-            case Token::GT:           return SkString(">");
-            case Token::LTEQ:         return SkString("<=");
-            case Token::GTEQ:         return SkString(">=");
-            case Token::PLUSEQ:       return SkString("+=");
-            case Token::MINUSEQ:      return SkString("-=");
-            case Token::STAREQ:       return SkString("*=");
-            case Token::SLASHEQ:      return SkString("/=");
-            case Token::PERCENTEQ:    return SkString("%=");
-            case Token::SHLEQ:        return SkString("<<=");
-            case Token::SHREQ:        return SkString(">>=");
-            case Token::LOGICALANDEQ: return SkString("&&=");
-            case Token::LOGICALOREQ:  return SkString("||=");
-            case Token::LOGICALXOREQ: return SkString("^^=");
-            case Token::BITWISEANDEQ: return SkString("&=");
-            case Token::BITWISEOREQ:  return SkString("|=");
-            case Token::BITWISEXOREQ: return SkString("^=");
-            case Token::PLUSPLUS:     return SkString("++");
-            case Token::MINUSMINUS:   return SkString("--");
+            case Token::PLUS:         return String("+");
+            case Token::MINUS:        return String("-");
+            case Token::STAR:         return String("*");
+            case Token::SLASH:        return String("/");
+            case Token::PERCENT:      return String("%");
+            case Token::SHL:          return String("<<");
+            case Token::SHR:          return String(">>");
+            case Token::LOGICALNOT:   return String("!");
+            case Token::LOGICALAND:   return String("&&");
+            case Token::LOGICALOR:    return String("||");
+            case Token::LOGICALXOR:   return String("^^");
+            case Token::BITWISENOT:   return String("~");
+            case Token::BITWISEAND:   return String("&");
+            case Token::BITWISEOR:    return String("|");
+            case Token::BITWISEXOR:   return String("^");
+            case Token::EQ:           return String("=");
+            case Token::EQEQ:         return String("==");
+            case Token::NEQ:          return String("!=");
+            case Token::LT:           return String("<");
+            case Token::GT:           return String(">");
+            case Token::LTEQ:         return String("<=");
+            case Token::GTEQ:         return String(">=");
+            case Token::PLUSEQ:       return String("+=");
+            case Token::MINUSEQ:      return String("-=");
+            case Token::STAREQ:       return String("*=");
+            case Token::SLASHEQ:      return String("/=");
+            case Token::PERCENTEQ:    return String("%=");
+            case Token::SHLEQ:        return String("<<=");
+            case Token::SHREQ:        return String(">>=");
+            case Token::LOGICALANDEQ: return String("&&=");
+            case Token::LOGICALOREQ:  return String("||=");
+            case Token::LOGICALXOREQ: return String("^^=");
+            case Token::BITWISEANDEQ: return String("&=");
+            case Token::BITWISEOREQ:  return String("|=");
+            case Token::BITWISEXOREQ: return String("^=");
+            case Token::PLUSPLUS:     return String("++");
+            case Token::MINUSMINUS:   return String("--");
+            case Token::COMMA:        return String(",");
             default:
-                ABORT("unsupported operator: %d\n", kind); 
-        }        
+                ABORT("unsupported operator: %d\n", kind);
+        }
     }
 
     Token() {
     }
 
-    Token(Position position, Kind kind, SkString text)
+    Token(Position position, Kind kind, String text)
     : fPosition(position)
     , fKind(kind)
     , fText(std::move(text)) {}
@@ -209,7 +214,7 @@
     Kind fKind;
     // will be the empty string unless the token has variable text content (identifiers, numeric
     // literals, and directives)
-    SkString fText;
+    String fText;
 };
 
 } // namespace
diff --git a/src/sksl/SkSLUtil.cpp b/src/sksl/SkSLUtil.cpp
index e93a953..c715cf1 100644
--- a/src/sksl/SkSLUtil.cpp
+++ b/src/sksl/SkSLUtil.cpp
@@ -10,117 +10,24 @@
 #ifndef __STDC_FORMAT_MACROS
 #define __STDC_FORMAT_MACROS
 #endif
-#include <cinttypes>
-#include <locale>
-#include <sstream>
-#include <string>
 
 namespace SkSL {
 
-SkString to_string(double value) {
-#ifdef SK_BUILD_FOR_WIN
-    #define SNPRINTF    _snprintf
-#else
-    #define SNPRINTF    snprintf
+#ifdef SKSL_STANDALONE
+StandaloneShaderCaps standaloneCaps;
 #endif
-#define MAX_DOUBLE_CHARS 25
-    char buffer[MAX_DOUBLE_CHARS];
-    SkDEBUGCODE(int len = )SNPRINTF(buffer, sizeof(buffer), "%.17g", value);
-    ASSERT(len < MAX_DOUBLE_CHARS);
-    SkString result(buffer);
-    if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
-        result += ".0";
-    }
-    return result;
-#undef SNPRINTF
-#undef MAX_DOUBLE_CHARS
-}
-
-SkString to_string(int32_t value) {
-    return SkStringPrintf("%d", value);
-}
-
-SkString to_string(uint32_t value) {
-    return SkStringPrintf("%u", value);
-}
-
-SkString to_string(int64_t value) {
-    return SkStringPrintf("%" PRId64, value);
-}
-
-SkString to_string(uint64_t value) {
-    return SkStringPrintf("%" PRIu64, value);
-}
-
-int stoi(SkString s) {
-    if (s.size() > 2 && s[0] == '0' && s[1] == 'x') {
-        char* p;
-        int result = strtoul(s.c_str() + 2, &p, 16);
-        ASSERT(*p == 0);
-        return result;
-    }
-    return atoi(s.c_str());
-}
-
-double stod(SkString s) {
-    double result;
-    std::string str(s.c_str(), s.size());
-    std::stringstream buffer(str);
-    buffer.imbue(std::locale::classic());
-    buffer >> result;
-    return result;
-}
-
-long stol(SkString s) {
-    if (s.size() > 2 && s[0] == '0' && s[1] == 'x') {
-        char* p;
-        long result = strtoul(s.c_str() + 2, &p, 16);
-        ASSERT(*p == 0);
-        return result;
-    }
-    return atol(s.c_str());
-}
 
 void sksl_abort() {
-#ifdef SKIA
+#ifdef SKSL_STANDALONE
+    abort();
+#else
     sk_abort_no_print();
     exit(1);
-#else
-    abort();
 #endif
 }
 
-void write_data(const SkData& data, SkWStream& out) {
-    out.write(data.data(), data.size());
+void write_stringstream(const StringStream& s, OutputStream& out) {
+    out.write(s.data(), s.size());
 }
 
-SkString operator+(const SkString& s, const char* c) {
-    SkString result(s);
-    result += c;
-    return result;
-}
-
-SkString operator+(const char* c, const SkString& s) {
-    SkString result(c);
-    result += s;
-    return result;
-}
-
-SkString operator+(const SkString& s1, const SkString& s2) {
-    SkString result(s1);
-    result += s2;
-    return result;
-}
-
-bool operator==(const SkString& s1, const char* s2) {
-    return !strcmp(s1.c_str(), s2);
-}
-
-bool operator!=(const SkString& s1, const char* s2) {
-    return strcmp(s1.c_str(), s2);
-}
-
-bool operator!=(const char* s1, const SkString& s2) {
-    return strcmp(s1, s2.c_str());
-}
 } // namespace
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index 678241d..d1af9bb 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -4,22 +4,159 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_UTIL
 #define SKSL_UTIL
 
+#include <cstdarg>
+#include <memory>
 #include "stdlib.h"
+#include "string.h"
 #include "assert.h"
-#include "SkOpts.h"
-#include "SkRefCnt.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkTypes.h"
+#include "SkSLString.h"
+#include "SkSLStringStream.h"
+
+#ifndef SKSL_STANDALONE
 #include "GrContextOptions.h"
 #include "GrShaderCaps.h"
+#endif
+
+#ifdef SKSL_STANDALONE
+#if defined(_WIN32) || defined(__SYMBIAN32__)
+#define SKSL_BUILD_FOR_WIN
+#endif
+#else
+#ifdef SK_BUILD_FOR_WIN
+#define SKSL_BUILD_FOR_WIN
+#endif // SK_BUILD_FOR_WIN
+#endif // SKSL_STANDALONE
 
 namespace SkSL {
 
+#ifdef SKSL_STANDALONE
+
+// we're being compiled standalone, so we don't have access to caps...
+enum GrGLSLGeneration {
+    k110_GrGLSLGeneration,
+    k130_GrGLSLGeneration,
+    k140_GrGLSLGeneration,
+    k150_GrGLSLGeneration,
+    k330_GrGLSLGeneration,
+    k400_GrGLSLGeneration,
+    k420_GrGLSLGeneration,
+    k310es_GrGLSLGeneration,
+    k320es_GrGLSLGeneration,
+};
+
+#define SKSL_CAPS_CLASS StandaloneShaderCaps
+class StandaloneShaderCaps {
+public:
+    GrGLSLGeneration generation() const {
+        return k400_GrGLSLGeneration;
+    }
+
+    bool canUseMinAndAbsTogether() const {
+        return true;
+    }
+
+    bool mustForceNegatedAtanParamToFloat() const {
+        return false;
+    }
+
+    bool shaderDerivativeSupport() const {
+        return true;
+    }
+
+    bool usesPrecisionModifiers() const {
+        return true;
+    }
+
+    bool mustDeclareFragmentShaderOutput() const {
+        return true;
+    }
+
+    bool fbFetchSupport() const {
+        return true;
+    }
+
+    bool fbFetchNeedsCustomOutput() const {
+        return false;
+    }
+
+    bool bindlessTextureSupport() const {
+        return false;
+    }
+
+    bool dropsTileOnZeroDivide() const {
+        return false;
+    }
+
+    bool flatInterpolationSupport() const {
+        return true;
+    }
+
+    bool noperspectiveInterpolationSupport() const {
+        return true;
+    }
+
+    bool multisampleInterpolationSupport() const {
+        return true;
+    }
+
+    bool sampleVariablesSupport() const {
+        return true;
+    }
+
+    bool sampleMaskOverrideCoverageSupport() const {
+        return true;
+    }
+
+    bool externalTextureSupport() const {
+        return true;
+    }
+
+    bool texelFetchSupport() const {
+        return true;
+    }
+
+    bool imageLoadStoreSupport() const {
+        return true;
+    }
+
+    bool mustEnableAdvBlendEqs() const {
+        return false;
+    }
+
+    bool mustEnableSpecificAdvBlendEqs() const {
+        return false;
+    }
+
+    bool canUseAnyFunctionInShader() const {
+        return false;
+    }
+
+    const char* shaderDerivativeExtensionString() const {
+        return nullptr;
+    }
+
+    const char* fragCoordConventionsExtensionString() const {
+        return nullptr;
+    }
+
+    const char* imageLoadStoreExtensionString() const {
+        return nullptr;
+    }
+
+    const char* versionDeclString() const {
+        return "";
+    }
+};
+
+extern StandaloneShaderCaps standaloneCaps;
+
+#else
+
+#define SKSL_CAPS_CLASS GrShaderCaps
 // Various sets of caps for use in tests
 class ShaderCapsFactory {
 public:
@@ -98,60 +235,38 @@
         return result;
     }
 };
+#endif
 
-void write_data(const SkData& d, SkWStream& out);
-
-SkString operator+(const SkString& s, const char* c);
-
-SkString operator+(const char* c, const SkString& s);
-
-SkString operator+(const SkString& s1, const SkString& s2);
-
-bool operator==(const SkString& s1, const char* s2);
-
-bool operator!=(const SkString& s1, const char* s2);
-
-bool operator!=(const char* s1, const SkString& s2);
-
-SkString to_string(double value);
-
-SkString to_string(int32_t value);
-
-SkString to_string(uint32_t value);
-
-SkString to_string(int64_t value);
-
-SkString to_string(uint64_t value);
+void write_stringstream(const StringStream& d, OutputStream& out);
 
 #if _MSC_VER
 #define NORETURN __declspec(noreturn)
 #else
 #define NORETURN __attribute__((__noreturn__))
 #endif
-int stoi(SkString s);
-
-double stod(SkString s);
-
-long stol(SkString s);
 
 NORETURN void sksl_abort();
 
 } // namespace
 
-#define ASSERT(x) SkASSERT(x)
-#define ASSERT_RESULT(x) SkAssertResult(x);
-
-#ifdef SKIA
-#define ABORT(...) { SkDebugf(__VA_ARGS__); sksl_abort(); }
+#ifdef SKSL_STANDALONE
+#define ASSERT(x) (void)((x) || (ABORT("failed assert(%s): %s:%d\n", #x, __FILE__, __LINE__), 0))
+#define ASSERT_RESULT(x) ASSERT(x)
+#define SKSL_DEBUGCODE(x) x
 #else
-#define ABORT(...) { sksl_abort(); }
+#define ASSERT SkASSERT
+#define ASSERT_RESULT(x) SkAssertResult(x)
+#define SKSL_DEBUGCODE(x) SkDEBUGCODE(x)
 #endif
 
-namespace std {
-    template<> struct hash<SkString> {
-        size_t operator()(const SkString& s) const {
-            return SkOpts::hash_fn(s.c_str(), s.size(), 0);
-        }
-    };
-}
+#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+
+#if defined(__clang__) || defined(__GNUC__)
+#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
+#else
+#define SKSL_PRINTF_LIKE(A, B)
+#endif
+
+#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
+
 #endif
diff --git a/src/sksl/ast/SkSLASTBinaryExpression.h b/src/sksl/ast/SkSLASTBinaryExpression.h
index c4b6e3a..9a24970 100644
--- a/src/sksl/ast/SkSLASTBinaryExpression.h
+++ b/src/sksl/ast/SkSLASTBinaryExpression.h
@@ -14,7 +14,7 @@
 namespace SkSL {
 
 /**
- * Represents a binary operation, with the operator represented by the token's type. 
+ * Represents a binary operation, with the operator represented by the token's type.
  */
 struct ASTBinaryExpression : public ASTExpression {
     ASTBinaryExpression(std::unique_ptr<ASTExpression> left, Token op,
@@ -24,7 +24,7 @@
     , fOperator(op.fKind)
     , fRight(std::move(right)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "(" + fLeft->description() + " " + Token::OperatorName(fOperator) + " " +
                fRight->description() + ")";
     }
diff --git a/src/sksl/ast/SkSLASTBlock.h b/src/sksl/ast/SkSLASTBlock.h
index 6b1e9c5..37c0e81 100644
--- a/src/sksl/ast/SkSLASTBlock.h
+++ b/src/sksl/ast/SkSLASTBlock.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTBLOCK
 #define SKSL_ASTBLOCK
 
@@ -13,21 +13,21 @@
 namespace SkSL {
 
 /**
- * Represents a curly-braced block of statements. 
+ * Represents a curly-braced block of statements.
  */
 struct ASTBlock : public ASTStatement {
     ASTBlock(Position position, std::vector<std::unique_ptr<ASTStatement>> statements)
     : INHERITED(position, kBlock_Kind)
     , fStatements(std::move(statements)) {}
 
-    SkString description() const override {
-        SkString result("{");
+    String description() const override {
+        String result("{");
         for (size_t i = 0; i < fStatements.size(); i++) {
             result += "\n";
             result += fStatements[i]->description();
         }
         result += "\n}\n";
-        return result;        
+        return result;
     }
 
     const std::vector<std::unique_ptr<ASTStatement>> fStatements;
diff --git a/src/sksl/ast/SkSLASTBoolLiteral.h b/src/sksl/ast/SkSLASTBoolLiteral.h
index 02f4bac..48e916e 100644
--- a/src/sksl/ast/SkSLASTBoolLiteral.h
+++ b/src/sksl/ast/SkSLASTBoolLiteral.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTBOOLLITERAL
 #define SKSL_ASTBOOLLITERAL
 
@@ -13,15 +13,15 @@
 namespace SkSL {
 
 /**
- * Represents "true" or "false". 
+ * Represents "true" or "false".
  */
 struct ASTBoolLiteral : public ASTExpression {
     ASTBoolLiteral(Position position, bool value)
     : INHERITED(position, kBool_Kind)
     , fValue(value) {}
 
-    SkString description() const override {
-        return SkString(fValue ? "true" : "false");
+    String description() const override {
+        return String(fValue ? "true" : "false");
     }
 
     const bool fValue;
diff --git a/src/sksl/ast/SkSLASTBreakStatement.h b/src/sksl/ast/SkSLASTBreakStatement.h
index dad2a85..079ee76 100644
--- a/src/sksl/ast/SkSLASTBreakStatement.h
+++ b/src/sksl/ast/SkSLASTBreakStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTBREAKSTATEMENT
 #define SKSL_ASTBREAKSTATEMENT
 
@@ -13,14 +13,14 @@
 namespace SkSL {
 
 /**
- * A 'break' statement. 
+ * A 'break' statement.
  */
 struct ASTBreakStatement : public ASTStatement {
     ASTBreakStatement(Position position)
     : INHERITED(position, kBreak_Kind) {}
 
-    SkString description() const override {
-        return SkString("break;");
+    String description() const override {
+        return String("break;");
     }
 
     typedef ASTStatement INHERITED;
diff --git a/src/sksl/ast/SkSLASTCallSuffix.h b/src/sksl/ast/SkSLASTCallSuffix.h
index 356ac85..3ba3f0e 100644
--- a/src/sksl/ast/SkSLASTCallSuffix.h
+++ b/src/sksl/ast/SkSLASTCallSuffix.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTCALLSUFFIX
 #define SKSL_ASTCALLSUFFIX
 
@@ -14,16 +14,16 @@
 namespace SkSL {
 
 /**
- * A parenthesized list of arguments following an expression, indicating a function call. 
+ * A parenthesized list of arguments following an expression, indicating a function call.
  */
 struct ASTCallSuffix : public ASTSuffix {
-    ASTCallSuffix(Position position, std::vector<std::unique_ptr<ASTExpression>> arguments) 
+    ASTCallSuffix(Position position, std::vector<std::unique_ptr<ASTExpression>> arguments)
     : INHERITED(position, ASTSuffix::kCall_Kind)
     , fArguments(std::move(arguments)) {}
 
-    SkString description() const override {
-        SkString result("(");
-        SkString separator;
+    String description() const override {
+        String result("(");
+        String separator;
         for (size_t i = 0; i < fArguments.size(); ++i) {
             result += separator;
             separator = ", ";
diff --git a/src/sksl/ast/SkSLASTContinueStatement.h b/src/sksl/ast/SkSLASTContinueStatement.h
index 4cded3b..fdfce85 100644
--- a/src/sksl/ast/SkSLASTContinueStatement.h
+++ b/src/sksl/ast/SkSLASTContinueStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTCONTINUESTATEMENT
 #define SKSL_ASTCONTINUESTATEMENT
 
@@ -13,14 +13,14 @@
 namespace SkSL {
 
 /**
- * A 'continue' statement. 
+ * A 'continue' statement.
  */
 struct ASTContinueStatement : public ASTStatement {
     ASTContinueStatement(Position position)
     : INHERITED(position, kContinue_Kind) {}
 
-    SkString description() const override {
-        return SkString("continue;");
+    String description() const override {
+        return String("continue;");
     }
 
     typedef ASTStatement INHERITED;
diff --git a/src/sksl/ast/SkSLASTDeclaration.h b/src/sksl/ast/SkSLASTDeclaration.h
index 70f0ebc..0395ef9 100644
--- a/src/sksl/ast/SkSLASTDeclaration.h
+++ b/src/sksl/ast/SkSLASTDeclaration.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTDECLARATION
 #define SKSL_ASTDECLARATION
 
@@ -13,7 +13,7 @@
 namespace SkSL {
 
 /**
- * Abstract supertype of declarations such as variables and functions. 
+ * Abstract supertype of declarations such as variables and functions.
  */
 struct ASTDeclaration : public ASTPositionNode {
     enum Kind {
diff --git a/src/sksl/ast/SkSLASTDiscardStatement.h b/src/sksl/ast/SkSLASTDiscardStatement.h
index 754bf95..dcf6b15 100644
--- a/src/sksl/ast/SkSLASTDiscardStatement.h
+++ b/src/sksl/ast/SkSLASTDiscardStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTDISCARDSTATEMENT
 #define SKSL_ASTDISCARDSTATEMENT
 
@@ -13,14 +13,14 @@
 namespace SkSL {
 
 /**
- * A 'discard' statement. 
+ * A 'discard' statement.
  */
 struct ASTDiscardStatement : public ASTStatement {
     ASTDiscardStatement(Position position)
     : INHERITED(position, kDiscard_Kind) {}
 
-    SkString description() const override {
-        return SkString("discard;");
+    String description() const override {
+        return String("discard;");
     }
 
     typedef ASTStatement INHERITED;
diff --git a/src/sksl/ast/SkSLASTDoStatement.h b/src/sksl/ast/SkSLASTDoStatement.h
index 9a0cace..fc97d9e 100644
--- a/src/sksl/ast/SkSLASTDoStatement.h
+++ b/src/sksl/ast/SkSLASTDoStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTDOSTATEMENT
 #define SKSL_ASTDOSTATEMENT
 
@@ -13,7 +13,7 @@
 namespace SkSL {
 
 /**
- * A 'do' loop. 
+ * A 'do' loop.
  */
 struct ASTDoStatement : public ASTStatement {
     ASTDoStatement(Position position, std::unique_ptr<ASTStatement> statement,
@@ -22,7 +22,7 @@
     , fStatement(std::move(statement))
     , fTest(std::move(test)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "do " + fStatement->description() + " while (" + fTest->description() + ");";
     }
 
diff --git a/src/sksl/ast/SkSLASTExpression.h b/src/sksl/ast/SkSLASTExpression.h
index 8a48271..11815ae 100644
--- a/src/sksl/ast/SkSLASTExpression.h
+++ b/src/sksl/ast/SkSLASTExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTEXPRESSION
 #define SKSL_ASTEXPRESSION
 
@@ -13,7 +13,7 @@
 namespace SkSL {
 
 /**
- * Abstract supertype of all expressions. 
+ * Abstract supertype of all expressions.
  */
 struct ASTExpression : public ASTPositionNode {
     enum Kind {
diff --git a/src/sksl/ast/SkSLASTExpressionStatement.h b/src/sksl/ast/SkSLASTExpressionStatement.h
index 2dbd209..398a16a 100644
--- a/src/sksl/ast/SkSLASTExpressionStatement.h
+++ b/src/sksl/ast/SkSLASTExpressionStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTEXPRESSIONSTATEMENT
 #define SKSL_ASTEXPRESSIONSTATEMENT
 
@@ -13,14 +13,14 @@
 namespace SkSL {
 
 /**
- * A lone expression being used as a statement. 
+ * A lone expression being used as a statement.
  */
 struct ASTExpressionStatement : public ASTStatement {
     ASTExpressionStatement(std::unique_ptr<ASTExpression> expression)
     : INHERITED(expression->fPosition, kExpression_Kind)
     , fExpression(std::move(expression)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fExpression->description() + ";";
     }
 
diff --git a/src/sksl/ast/SkSLASTExtension.h b/src/sksl/ast/SkSLASTExtension.h
index b9df3c5..a6fde06 100644
--- a/src/sksl/ast/SkSLASTExtension.h
+++ b/src/sksl/ast/SkSLASTExtension.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTEXTENSION
 #define SKSL_ASTEXTENSION
 
@@ -12,19 +12,19 @@
 
 namespace SkSL {
 
-/** 
- * An extension declaration. 
+/**
+ * An extension declaration.
  */
 struct ASTExtension : public ASTDeclaration {
-    ASTExtension(Position position, SkString name)
+    ASTExtension(Position position, String name)
     : INHERITED(position, kExtension_Kind)
     , fName(std::move(name)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "#extension " + fName + " : enable";
     }
 
-    const SkString fName;
+    const String fName;
 
     typedef ASTDeclaration INHERITED;
 };
diff --git a/src/sksl/ast/SkSLASTFieldSuffix.h b/src/sksl/ast/SkSLASTFieldSuffix.h
index 9ee8531..bde1e4a 100644
--- a/src/sksl/ast/SkSLASTFieldSuffix.h
+++ b/src/sksl/ast/SkSLASTFieldSuffix.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTFIELDSUFFIX
 #define SKSL_ASTFIELDSUFFIX
 
@@ -17,15 +17,15 @@
  * actually vector swizzle (which looks the same to the parser).
  */
 struct ASTFieldSuffix : public ASTSuffix {
-    ASTFieldSuffix(Position position, SkString field) 
+    ASTFieldSuffix(Position position, String field)
     : INHERITED(position, ASTSuffix::kField_Kind)
     , fField(std::move(field)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "." + fField;
     }
 
-    SkString fField;
+    String fField;
 
     typedef ASTSuffix INHERITED;
 };
diff --git a/src/sksl/ast/SkSLASTFloatLiteral.h b/src/sksl/ast/SkSLASTFloatLiteral.h
index ea0f595..15fe836 100644
--- a/src/sksl/ast/SkSLASTFloatLiteral.h
+++ b/src/sksl/ast/SkSLASTFloatLiteral.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTFLOATLITERAL
 #define SKSL_ASTFLOATLITERAL
 
@@ -13,14 +13,14 @@
 namespace SkSL {
 
 /**
- * A literal floating point number. 
+ * A literal floating point number.
  */
 struct ASTFloatLiteral : public ASTExpression {
     ASTFloatLiteral(Position position, double value)
     : INHERITED(position, kFloat_Kind)
     , fValue(value) {}
 
-    SkString description() const override {
+    String description() const override {
         return to_string(fValue);
     }
 
diff --git a/src/sksl/ast/SkSLASTForStatement.h b/src/sksl/ast/SkSLASTForStatement.h
index 2706a39..326713e 100644
--- a/src/sksl/ast/SkSLASTForStatement.h
+++ b/src/sksl/ast/SkSLASTForStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTFORSTATEMENT
 #define SKSL_ASTFORSTATEMENT
 
@@ -13,10 +13,10 @@
 namespace SkSL {
 
 /**
- * A 'for' loop. 
+ * A 'for' loop.
  */
 struct ASTForStatement : public ASTStatement {
-    ASTForStatement(Position position, std::unique_ptr<ASTStatement> initializer, 
+    ASTForStatement(Position position, std::unique_ptr<ASTStatement> initializer,
                    std::unique_ptr<ASTExpression> test, std::unique_ptr<ASTExpression> next,
                    std::unique_ptr<ASTStatement> statement)
     : INHERITED(position, kFor_Kind)
@@ -25,8 +25,8 @@
     , fNext(std::move(next))
     , fStatement(std::move(statement)) {}
 
-    SkString description() const override {
-        SkString result("for (");
+    String description() const override {
+        String result("for (");
         if (fInitializer) {
             result.append(fInitializer->description());
         }
diff --git a/src/sksl/ast/SkSLASTFunction.h b/src/sksl/ast/SkSLASTFunction.h
index 32f4da7..0dff3ae 100644
--- a/src/sksl/ast/SkSLASTFunction.h
+++ b/src/sksl/ast/SkSLASTFunction.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTFUNCTION
 #define SKSL_ASTFUNCTION
 
@@ -16,20 +16,21 @@
 namespace SkSL {
 
 /**
- * A function declaration or definition. The fBody field will be null for declarations. 
+ * A function declaration or definition. The fBody field will be null for declarations.
  */
 struct ASTFunction : public ASTDeclaration {
-    ASTFunction(Position position, std::unique_ptr<ASTType> returnType, SkString name,
-                std::vector<std::unique_ptr<ASTParameter>> parameters, 
+    ASTFunction(Position position, Modifiers modifiers,  std::unique_ptr<ASTType> returnType,
+                String name, std::vector<std::unique_ptr<ASTParameter>> parameters, 
                 std::unique_ptr<ASTBlock> body)
     : INHERITED(position, kFunction_Kind)
+    , fModifiers(modifiers)
     , fReturnType(std::move(returnType))
     , fName(std::move(name))
     , fParameters(std::move(parameters))
     , fBody(std::move(body)) {}
 
-    SkString description() const override {
-        SkString result = fReturnType->description() + " " + fName + "(";
+    String description() const override {
+        String result = fReturnType->description() + " " + fName + "(";
         for (size_t i = 0; i < fParameters.size(); i++) {
             if (i > 0) {
                 result += ", ";
@@ -41,11 +42,12 @@
         } else {
             result += ");";
         }
-        return result;        
+        return result;
     }
 
+    const Modifiers fModifiers;
     const std::unique_ptr<ASTType> fReturnType;
-    const SkString fName;
+    const String fName;
     const std::vector<std::unique_ptr<ASTParameter>> fParameters;
     const std::unique_ptr<ASTBlock> fBody;
 
diff --git a/src/sksl/ast/SkSLASTIdentifier.h b/src/sksl/ast/SkSLASTIdentifier.h
index aa0179a..016123c 100644
--- a/src/sksl/ast/SkSLASTIdentifier.h
+++ b/src/sksl/ast/SkSLASTIdentifier.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTIDENTIFIER
 #define SKSL_ASTIDENTIFIER
 
@@ -13,18 +13,18 @@
 namespace SkSL {
 
 /**
- * An identifier in an expression context. 
+ * An identifier in an expression context.
  */
 struct ASTIdentifier : public ASTExpression {
-    ASTIdentifier(Position position, SkString text)
+    ASTIdentifier(Position position, String text)
     : INHERITED(position, kIdentifier_Kind)
     , fText(std::move(text)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fText;
     }
 
-    const SkString fText;
+    const String fText;
 
     typedef ASTExpression INHERITED;
 };
diff --git a/src/sksl/ast/SkSLASTIfStatement.h b/src/sksl/ast/SkSLASTIfStatement.h
index d169702..2752b2b 100644
--- a/src/sksl/ast/SkSLASTIfStatement.h
+++ b/src/sksl/ast/SkSLASTIfStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTIFSTATEMENT
 #define SKSL_ASTIFSTATEMENT
 
@@ -13,18 +13,23 @@
 namespace SkSL {
 
 /**
- * An 'if' statement. 
+ * An 'if' statement.
  */
 struct ASTIfStatement : public ASTStatement {
-    ASTIfStatement(Position position, std::unique_ptr<ASTExpression> test, 
+    ASTIfStatement(Position position, bool isStatic, std::unique_ptr<ASTExpression> test,
                    std::unique_ptr<ASTStatement> ifTrue, std::unique_ptr<ASTStatement> ifFalse)
     : INHERITED(position, kIf_Kind)
+    , fIsStatic(isStatic)
     , fTest(std::move(test))
     , fIfTrue(std::move(ifTrue))
     , fIfFalse(std::move(ifFalse)) {}
 
-    SkString description() const override {
-        SkString result("if (");
+    String description() const override {
+        String result;
+        if (fIsStatic) {
+            result += "@";
+        }
+        result += "if (";
         result += fTest->description();
         result += ") ";
         result += fIfTrue->description();
@@ -32,9 +37,10 @@
             result += " else ";
             result += fIfFalse->description();
         }
-        return result;        
+        return result;
     }
 
+    const bool fIsStatic;
     const std::unique_ptr<ASTExpression> fTest;
     const std::unique_ptr<ASTStatement> fIfTrue;
     const std::unique_ptr<ASTStatement> fIfFalse;
diff --git a/src/sksl/ast/SkSLASTIndexSuffix.h b/src/sksl/ast/SkSLASTIndexSuffix.h
index 2b7cd48..31142e3 100644
--- a/src/sksl/ast/SkSLASTIndexSuffix.h
+++ b/src/sksl/ast/SkSLASTIndexSuffix.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTINDEXSUFFIX
 #define SKSL_ASTINDEXSUFFIX
 
@@ -18,19 +18,19 @@
  * 'float[](5, 6)' are represented with a null fExpression.
  */
 struct ASTIndexSuffix : public ASTSuffix {
-    ASTIndexSuffix(Position position) 
+    ASTIndexSuffix(Position position)
     : INHERITED(position, ASTSuffix::kIndex_Kind)
     , fExpression(nullptr) {}
 
-    ASTIndexSuffix(std::unique_ptr<ASTExpression> expression) 
+    ASTIndexSuffix(std::unique_ptr<ASTExpression> expression)
     : INHERITED(expression ? expression->fPosition : Position(), ASTSuffix::kIndex_Kind)
     , fExpression(std::move(expression)) {}
 
-    SkString description() const override {
+    String description() const override {
         if (fExpression) {
             return "[" + fExpression->description() + "]";
         } else {
-            return SkString("[]");
+            return String("[]");
         }
     }
 
diff --git a/src/sksl/ast/SkSLASTIntLiteral.h b/src/sksl/ast/SkSLASTIntLiteral.h
index f524bc0..fe04347 100644
--- a/src/sksl/ast/SkSLASTIntLiteral.h
+++ b/src/sksl/ast/SkSLASTIntLiteral.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTINTLITERAL
 #define SKSL_ASTINTLITERAL
 
@@ -21,7 +21,7 @@
     : INHERITED(position, kInt_Kind)
     , fValue(value) {}
 
-    SkString description() const override {
+    String description() const override {
         return to_string(fValue);
     }
 
diff --git a/src/sksl/ast/SkSLASTInterfaceBlock.h b/src/sksl/ast/SkSLASTInterfaceBlock.h
index 7647fb5..e727ae9 100644
--- a/src/sksl/ast/SkSLASTInterfaceBlock.h
+++ b/src/sksl/ast/SkSLASTInterfaceBlock.h
@@ -25,9 +25,9 @@
     // valueName is empty when it was not present in the source
     ASTInterfaceBlock(Position position,
                       Modifiers modifiers,
-                      SkString typeName,
+                      String typeName,
                       std::vector<std::unique_ptr<ASTVarDeclarations>> declarations,
-                      SkString instanceName,
+                      String instanceName,
                       std::vector<std::unique_ptr<ASTExpression>> sizes)
     : INHERITED(position, kInterfaceBlock_Kind)
     , fModifiers(modifiers)
@@ -36,8 +36,8 @@
     , fInstanceName(std::move(instanceName))
     , fSizes(std::move(sizes)) {}
 
-    SkString description() const override {
-        SkString result = fModifiers.description() + fTypeName + " {\n";
+    String description() const override {
+        String result = fModifiers.description() + fTypeName + " {\n";
         for (size_t i = 0; i < fDeclarations.size(); i++) {
             result += fDeclarations[i]->description() + "\n";
         }
@@ -56,9 +56,9 @@
     }
 
     const Modifiers fModifiers;
-    const SkString fTypeName;
+    const String fTypeName;
     const std::vector<std::unique_ptr<ASTVarDeclarations>> fDeclarations;
-    const SkString fInstanceName;
+    const String fInstanceName;
     const std::vector<std::unique_ptr<ASTExpression>> fSizes;
 
     typedef ASTDeclaration INHERITED;
diff --git a/src/sksl/ast/SkSLASTModifiersDeclaration.h b/src/sksl/ast/SkSLASTModifiersDeclaration.h
index 7950f6d..ba07f16 100644
--- a/src/sksl/ast/SkSLASTModifiersDeclaration.h
+++ b/src/sksl/ast/SkSLASTModifiersDeclaration.h
@@ -23,7 +23,7 @@
     : INHERITED(Position(), kModifiers_Kind)
     , fModifiers(modifiers) {}
 
-    SkString description() const {
+    String description() const {
         return fModifiers.description() + ";";
     }
 
diff --git a/src/sksl/ast/SkSLASTNode.h b/src/sksl/ast/SkSLASTNode.h
index af06595..b08bc2a 100644
--- a/src/sksl/ast/SkSLASTNode.h
+++ b/src/sksl/ast/SkSLASTNode.h
@@ -4,11 +4,11 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTNODE
 #define SKSL_ASTNODE
 
-#include "SkString.h"
+#include "SkSLString.h"
 
 namespace SkSL {
 
@@ -18,8 +18,8 @@
  */
 struct ASTNode {
     virtual ~ASTNode() {}
-     
-    virtual SkString description() const = 0;
+
+    virtual String description() const = 0;
 };
 
 } // namespace
diff --git a/src/sksl/ast/SkSLASTParameter.h b/src/sksl/ast/SkSLASTParameter.h
index 6bb13ac..01227c6 100644
--- a/src/sksl/ast/SkSLASTParameter.h
+++ b/src/sksl/ast/SkSLASTParameter.h
@@ -21,15 +21,15 @@
     // 'sizes' is a list of the array sizes appearing on a parameter, in source order.
     // e.g. int x[3][1] would have sizes [3, 1].
     ASTParameter(Position position, Modifiers modifiers, std::unique_ptr<ASTType> type,
-                 SkString name, std::vector<int> sizes)
+                 String name, std::vector<int> sizes)
     : INHERITED(position)
     , fModifiers(modifiers)
     , fType(std::move(type))
     , fName(std::move(name))
     , fSizes(std::move(sizes)) {}
 
-    SkString description() const override {
-        SkString result = fModifiers.description() + fType->description() + " " + fName;
+    String description() const override {
+        String result = fModifiers.description() + fType->description() + " " + fName;
         for (int size : fSizes) {
             result += "[" + to_string(size) + "]";
         }
@@ -38,7 +38,7 @@
 
     const Modifiers fModifiers;
     const std::unique_ptr<ASTType> fType;
-    const SkString fName;
+    const String fName;
     const std::vector<int> fSizes;
 
     typedef ASTPositionNode INHERITED;
diff --git a/src/sksl/ast/SkSLASTPositionNode.h b/src/sksl/ast/SkSLASTPositionNode.h
index 226b4ae..cc435c4 100644
--- a/src/sksl/ast/SkSLASTPositionNode.h
+++ b/src/sksl/ast/SkSLASTPositionNode.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTPOSITIONNODE
 #define SKSL_ASTPOSITIONNODE
 
diff --git a/src/sksl/ast/SkSLASTPrecision.h b/src/sksl/ast/SkSLASTPrecision.h
index a2f427c..4b50ed3 100644
--- a/src/sksl/ast/SkSLASTPrecision.h
+++ b/src/sksl/ast/SkSLASTPrecision.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTPRECISION
 #define SKSL_ASTPRECISION
 
@@ -22,17 +22,17 @@
     : INHERITED(position, kPrecision_Kind)
     , fPrecision(precision) {}
 
-    SkString description() const {
+    String description() const {
         switch (fPrecision) {
-            case Modifiers::kLowp_Flag: return SkString("precision lowp float;");
-            case Modifiers::kMediump_Flag: return SkString("precision mediump float;");
-            case Modifiers::kHighp_Flag: return SkString("precision highp float;");
-            default: 
-                ASSERT(false); 
-                return SkString("<error>");
+            case Modifiers::kLowp_Flag: return String("precision lowp float;");
+            case Modifiers::kMediump_Flag: return String("precision mediump float;");
+            case Modifiers::kHighp_Flag: return String("precision highp float;");
+            default:
+                ASSERT(false);
+                return String("<error>");
         }
         ASSERT(false);
-        return SkString("<error>");
+        return String("<error>");
     }
 
     const Modifiers::Flag fPrecision;
diff --git a/src/sksl/ast/SkSLASTPrefixExpression.h b/src/sksl/ast/SkSLASTPrefixExpression.h
index e06ec41..08e50f7 100644
--- a/src/sksl/ast/SkSLASTPrefixExpression.h
+++ b/src/sksl/ast/SkSLASTPrefixExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTPREFIXEXPRESSION
 #define SKSL_ASTPREFIXEXPRESSION
 
@@ -22,7 +22,7 @@
     , fOperator(op.fKind)
     , fOperand(std::move(operand)) {}
 
-    SkString description() const override {
+    String description() const override {
         return Token::OperatorName(fOperator) + fOperand->description();
     }
 
diff --git a/src/sksl/ast/SkSLASTReturnStatement.h b/src/sksl/ast/SkSLASTReturnStatement.h
index ed24d4a..6762eb3 100644
--- a/src/sksl/ast/SkSLASTReturnStatement.h
+++ b/src/sksl/ast/SkSLASTReturnStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTRETURNSTATEMENT
 #define SKSL_ASTRETURNSTATEMENT
 
@@ -21,12 +21,12 @@
     : INHERITED(position, kReturn_Kind)
     , fExpression(std::move(expression)) {}
 
-    SkString description() const override {
-        SkString result("return");
+    String description() const override {
+        String result("return");
         if (fExpression) {
             result += " " + fExpression->description();
         }
-        return result + ";";        
+        return result + ";";
     }
 
     const std::unique_ptr<ASTExpression> fExpression;
diff --git a/src/sksl/ast/SkSLASTStatement.h b/src/sksl/ast/SkSLASTStatement.h
index 6ce320e..1989a1f 100644
--- a/src/sksl/ast/SkSLASTStatement.h
+++ b/src/sksl/ast/SkSLASTStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTSTATEMENT
 #define SKSL_ASTSTATEMENT
 
diff --git a/src/sksl/ast/SkSLASTSuffix.h b/src/sksl/ast/SkSLASTSuffix.h
index 64178c7..f06c6fd 100644
--- a/src/sksl/ast/SkSLASTSuffix.h
+++ b/src/sksl/ast/SkSLASTSuffix.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTSUFFIX
 #define SKSL_ASTSUFFIX
 
@@ -30,15 +30,15 @@
     : INHERITED(position)
     , fKind(kind) {}
 
-    SkString description() const override {
+    String description() const override {
         switch (fKind) {
             case kPostIncrement_Kind:
-                return SkString("++");
+                return String("++");
             case kPostDecrement_Kind:
-                return SkString("--");
+                return String("--");
             default:
                 ABORT("unsupported suffix operator");
-        }        
+        }
     }
 
     Kind fKind;
diff --git a/src/sksl/ast/SkSLASTSuffixExpression.h b/src/sksl/ast/SkSLASTSuffixExpression.h
index 7ee200f..2cff9a8 100644
--- a/src/sksl/ast/SkSLASTSuffixExpression.h
+++ b/src/sksl/ast/SkSLASTSuffixExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTSUFFIXEXPRESSION
 #define SKSL_ASTSUFFIXEXPRESSION
 
@@ -22,7 +22,7 @@
     , fBase(std::move(base))
     , fSuffix(std::move(suffix)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fBase->description() + fSuffix->description();
     }
 
diff --git a/src/sksl/ast/SkSLASTSwitchCase.h b/src/sksl/ast/SkSLASTSwitchCase.h
index 2c0a01c..405013a 100644
--- a/src/sksl/ast/SkSLASTSwitchCase.h
+++ b/src/sksl/ast/SkSLASTSwitchCase.h
@@ -23,8 +23,8 @@
     , fValue(std::move(value))
     , fStatements(std::move(statements)) {}
 
-    SkString description() const override {
-        SkString result;
+    String description() const override {
+        String result;
         if (fValue) {
             result.appendf("case %s:\n", fValue->description().c_str());
         } else {
diff --git a/src/sksl/ast/SkSLASTSwitchStatement.h b/src/sksl/ast/SkSLASTSwitchStatement.h
index 3031a7d..ecd0a67 100644
--- a/src/sksl/ast/SkSLASTSwitchStatement.h
+++ b/src/sksl/ast/SkSLASTSwitchStatement.h
@@ -17,14 +17,19 @@
  * A 'switch' statement.
  */
 struct ASTSwitchStatement : public ASTStatement {
-    ASTSwitchStatement(Position position, std::unique_ptr<ASTExpression> value,
+    ASTSwitchStatement(Position position, bool isStatic, std::unique_ptr<ASTExpression> value,
                        std::vector<std::unique_ptr<ASTSwitchCase>> cases)
     : INHERITED(position, kSwitch_Kind)
+    , fIsStatic(isStatic)
     , fValue(std::move(value))
     , fCases(std::move(cases)) {}
 
-    SkString description() const override {
-        SkString result = SkStringPrintf("switch (%s) {\n", + fValue->description().c_str());
+    String description() const override {
+        String result;
+        if (fIsStatic) {
+            result += "@";
+        }
+        result += String::printf("switch (%s) {\n", fValue->description().c_str());
         for (const auto& c : fCases) {
             result += c->description();
         }
@@ -32,6 +37,7 @@
         return result;
     }
 
+    bool fIsStatic;
     const std::unique_ptr<ASTExpression> fValue;
     const std::vector<std::unique_ptr<ASTSwitchCase>> fCases;
 
diff --git a/src/sksl/ast/SkSLASTTernaryExpression.h b/src/sksl/ast/SkSLASTTernaryExpression.h
index ddf8e3d..07c9297 100644
--- a/src/sksl/ast/SkSLASTTernaryExpression.h
+++ b/src/sksl/ast/SkSLASTTernaryExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTTERNARYEXPRESSION
 #define SKSL_ASTTERNARYEXPRESSION
 
@@ -24,9 +24,9 @@
     , fIfTrue(std::move(ifTrue))
     , fIfFalse(std::move(ifFalse)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "(" + fTest->description() + " ? " + fIfTrue->description() + " : " +
-               fIfFalse->description() + ")";        
+               fIfFalse->description() + ")";
     }
 
     const std::unique_ptr<ASTExpression> fTest;
diff --git a/src/sksl/ast/SkSLASTType.h b/src/sksl/ast/SkSLASTType.h
index b95c3d7..57a8025 100644
--- a/src/sksl/ast/SkSLASTType.h
+++ b/src/sksl/ast/SkSLASTType.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTTYPE
 #define SKSL_ASTTYPE
 
@@ -21,17 +21,17 @@
         kStruct_Kind
     };
 
-    ASTType(Position position, SkString name, Kind kind, std::vector<int> sizes)
+    ASTType(Position position, String name, Kind kind, std::vector<int> sizes)
     : INHERITED(position)
     , fName(std::move(name))
     , fKind(kind)
     , fSizes(std::move(sizes)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fName;
     }
 
-    const SkString fName;
+    const String fName;
 
     const Kind fKind;
 
diff --git a/src/sksl/ast/SkSLASTVarDeclaration.h b/src/sksl/ast/SkSLASTVarDeclaration.h
index 7d50a06..2dcb978 100644
--- a/src/sksl/ast/SkSLASTVarDeclaration.h
+++ b/src/sksl/ast/SkSLASTVarDeclaration.h
@@ -22,15 +22,15 @@
  * instances.
  */
 struct ASTVarDeclaration {
-    ASTVarDeclaration(const SkString name,
+    ASTVarDeclaration(const String name,
                       std::vector<std::unique_ptr<ASTExpression>> sizes,
                       std::unique_ptr<ASTExpression> value)
     : fName(name)
     , fSizes(std::move(sizes))
     , fValue(std::move(value)) {}
 
-    SkString description() const {
-        SkString result = fName;
+    String description() const {
+        String result = fName;
         for (const auto& size : fSizes) {
             if (size) {
                 result += "[" + size->description() + "]";
@@ -44,7 +44,7 @@
         return result;
     }
 
-    SkString fName;
+    String fName;
 
     // array sizes, if any. e.g. 'foo[3][]' has sizes [3, null]
     std::vector<std::unique_ptr<ASTExpression>> fSizes;
@@ -65,9 +65,9 @@
     , fType(std::move(type))
     , fVars(std::move(vars)) {}
 
-    SkString description() const override {
-        SkString result = fModifiers.description() + fType->description() + " ";
-        SkString separator;
+    String description() const override {
+        String result = fModifiers.description() + fType->description() + " ";
+        String separator;
         for (const auto& var : fVars) {
             result += separator;
             separator = ", ";
diff --git a/src/sksl/ast/SkSLASTVarDeclarationStatement.h b/src/sksl/ast/SkSLASTVarDeclarationStatement.h
index d71639d..c3a4069 100644
--- a/src/sksl/ast/SkSLASTVarDeclarationStatement.h
+++ b/src/sksl/ast/SkSLASTVarDeclarationStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTVARDECLARATIONSTATEMENT
 #define SKSL_ASTVARDECLARATIONSTATEMENT
 
@@ -21,7 +21,7 @@
     : INHERITED(decl->fPosition, kVarDeclaration_Kind)
     , fDeclarations(std::move(decl)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fDeclarations->description() + ";";
     }
 
diff --git a/src/sksl/ast/SkSLASTWhileStatement.h b/src/sksl/ast/SkSLASTWhileStatement.h
index 853ac80..e63c502 100644
--- a/src/sksl/ast/SkSLASTWhileStatement.h
+++ b/src/sksl/ast/SkSLASTWhileStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_ASTWHILESTATEMENT
 #define SKSL_ASTWHILESTATEMENT
 
@@ -16,13 +16,13 @@
  * A 'while' statement.
  */
 struct ASTWhileStatement : public ASTStatement {
-    ASTWhileStatement(Position position, std::unique_ptr<ASTExpression> test, 
+    ASTWhileStatement(Position position, std::unique_ptr<ASTExpression> test,
                       std::unique_ptr<ASTStatement> statement)
     : INHERITED(position, kWhile_Kind)
     , fTest(std::move(test))
     , fStatement(std::move(statement)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "while (" + fTest->description() + ") " + fStatement->description();
     }
 
diff --git a/src/sksl/ir/SkSLBinaryExpression.h b/src/sksl/ir/SkSLBinaryExpression.h
index de85e48..789db57 100644
--- a/src/sksl/ir/SkSLBinaryExpression.h
+++ b/src/sksl/ir/SkSLBinaryExpression.h
@@ -26,15 +26,19 @@
     , fOperator(op)
     , fRight(std::move(right)) {}
 
-    virtual std::unique_ptr<Expression> constantPropagate(
-                                                        const IRGenerator& irGenerator,
-                                                        const DefinitionMap& definitions) override {
+    std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
+                                                  const DefinitionMap& definitions) override {
         return irGenerator.constantFold(*fLeft,
                                         fOperator,
                                         *fRight);
     }
 
-    virtual SkString description() const override {
+    bool hasSideEffects() const override {
+        return Token::IsAssignment(fOperator) || fLeft->hasSideEffects() ||
+               fRight->hasSideEffects();
+    }
+
+    String description() const override {
         return "(" + fLeft->description() + " " + Token::OperatorName(fOperator) + " " +
                fRight->description() + ")";
     }
diff --git a/src/sksl/ir/SkSLBlock.h b/src/sksl/ir/SkSLBlock.h
index 17970fd..bcd4bb1 100644
--- a/src/sksl/ir/SkSLBlock.h
+++ b/src/sksl/ir/SkSLBlock.h
@@ -18,13 +18,22 @@
  */
 struct Block : public Statement {
     Block(Position position, std::vector<std::unique_ptr<Statement>> statements,
-          const std::shared_ptr<SymbolTable> symbols)
+          const std::shared_ptr<SymbolTable> symbols = nullptr)
     : INHERITED(position, kBlock_Kind)
     , fSymbols(std::move(symbols))
     , fStatements(std::move(statements)) {}
 
-    SkString description() const override {
-        SkString result("{");
+    bool isEmpty() const override {
+        for (const auto& s : fStatements) {
+            if (!s->isEmpty()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    String description() const override {
+        String result("{");
         for (size_t i = 0; i < fStatements.size(); i++) {
             result += "\n";
             result += fStatements[i]->description();
@@ -36,7 +45,7 @@
     // it's important to keep fStatements defined after (and thus destroyed before) fSymbols,
     // because destroying statements can modify reference counts in symbols
     const std::shared_ptr<SymbolTable> fSymbols;
-    const std::vector<std::unique_ptr<Statement>> fStatements;
+    std::vector<std::unique_ptr<Statement>> fStatements;
 
     typedef Statement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLBoolLiteral.h b/src/sksl/ir/SkSLBoolLiteral.h
index b372f2f..a4151b8 100644
--- a/src/sksl/ir/SkSLBoolLiteral.h
+++ b/src/sksl/ir/SkSLBoolLiteral.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_BOOLLITERAL
 #define SKSL_BOOLLITERAL
 
@@ -21,14 +21,23 @@
     : INHERITED(position, kBoolLiteral_Kind, *context.fBool_Type)
     , fValue(value) {}
 
-    SkString description() const override {
-        return SkString(fValue ? "true" : "false");
+    String description() const override {
+        return String(fValue ? "true" : "false");
+    }
+
+    bool hasSideEffects() const override {
+        return false;
     }
 
     bool isConstant() const override {
         return true;
     }
 
+    bool compareConstant(const Context& context, const Expression& other) const override {
+        BoolLiteral& b = (BoolLiteral&) other;
+        return fValue == b.fValue;
+    }
+
     const bool fValue;
 
     typedef Expression INHERITED;
diff --git a/src/sksl/ir/SkSLBreakStatement.h b/src/sksl/ir/SkSLBreakStatement.h
index cd633c7..f6edc55 100644
--- a/src/sksl/ir/SkSLBreakStatement.h
+++ b/src/sksl/ir/SkSLBreakStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_BREAKSTATEMENT
 #define SKSL_BREAKSTATEMENT
 
@@ -14,14 +14,14 @@
 namespace SkSL {
 
 /**
- * A 'break' statement. 
+ * A 'break' statement.
  */
 struct BreakStatement : public Statement {
     BreakStatement(Position position)
     : INHERITED(position, kBreak_Kind) {}
 
-    SkString description() const override {
-        return SkString("break;");
+    String description() const override {
+        return String("break;");
     }
 
     typedef Statement INHERITED;
diff --git a/src/sksl/ir/SkSLConstructor.h b/src/sksl/ir/SkSLConstructor.h
index 3360ace..05f4096 100644
--- a/src/sksl/ir/SkSLConstructor.h
+++ b/src/sksl/ir/SkSLConstructor.h
@@ -30,23 +30,39 @@
     : INHERITED(position, kConstructor_Kind, type)
     , fArguments(std::move(arguments)) {}
 
-    virtual std::unique_ptr<Expression> constantPropagate(
-                                                        const IRGenerator& irGenerator,
-                                                        const DefinitionMap& definitions) override {
-        if (fArguments.size() == 1 && fArguments[0]->fKind == Expression::kIntLiteral_Kind &&
-            // promote float(1) to 1.0
-            fType == *irGenerator.fContext.fFloat_Type) {
-            int64_t intValue = ((IntLiteral&) *fArguments[0]).fValue;
-            return std::unique_ptr<Expression>(new FloatLiteral(irGenerator.fContext,
-                                                                fPosition,
-                                                                intValue));
+    std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
+                                                  const DefinitionMap& definitions) override {
+        if (fArguments.size() == 1 && fArguments[0]->fKind == Expression::kIntLiteral_Kind) {
+            if (fType == *irGenerator.fContext.fFloat_Type) {
+                // promote float(1) to 1.0
+                int64_t intValue = ((IntLiteral&) *fArguments[0]).fValue;
+                return std::unique_ptr<Expression>(new FloatLiteral(irGenerator.fContext,
+                                                                    fPosition,
+                                                                    intValue));
+            } else if (fType == *irGenerator.fContext.fUInt_Type) {
+                // promote uint(1) to 1u
+                int64_t intValue = ((IntLiteral&) *fArguments[0]).fValue;
+                return std::unique_ptr<Expression>(new IntLiteral(irGenerator.fContext,
+                                                                  fPosition,
+                                                                  intValue,
+                                                                  &fType));
+            }
         }
         return nullptr;
     }
 
-    SkString description() const override {
-        SkString result = fType.description() + "(";
-        SkString separator;
+    bool hasSideEffects() const override {
+        for (const auto& arg : fArguments) {
+            if (arg->hasSideEffects()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    String description() const override {
+        String result = fType.description() + "(";
+        String separator;
         for (size_t i = 0; i < fArguments.size(); i++) {
             result += separator;
             result += fArguments[i]->description();
@@ -65,6 +81,126 @@
         return true;
     }
 
+    bool compareConstant(const Context& context, const Expression& other) const override {
+        ASSERT(other.fKind == Expression::kConstructor_Kind && other.fType == fType);
+        Constructor& c = (Constructor&) other;
+        if (c.fType.kind() == Type::kVector_Kind) {
+            for (int i = 0; i < fType.columns(); i++) {
+                if (!this->getVecComponent(i).compareConstant(context, c.getVecComponent(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        // shouldn't be possible to have a constant constructor that isn't a vector or matrix;
+        // a constant scalar constructor should have been collapsed down to the appropriate
+        // literal
+        ASSERT(fType.kind() == Type::kMatrix_Kind);
+        const FloatLiteral fzero(context, Position(), 0);
+        const IntLiteral izero(context, Position(), 0);
+        const Expression* zero;
+        if (fType.componentType() == *context.fFloat_Type) {
+            zero = &fzero;
+        } else {
+            ASSERT(fType.componentType() == *context.fInt_Type);
+            zero = &izero;
+        }
+        for (int col = 0; col < fType.columns(); col++) {
+            for (int row = 0; row < fType.rows(); row++) {
+                const Expression* component1 = getMatComponent(col, row);
+                const Expression* component2 = c.getMatComponent(col, row);
+                if (!(component1 ? component1 : zero)->compareConstant(
+                                                                context,
+                                                                component2 ? *component2 : *zero)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    const Expression& getVecComponent(int index) const {
+        ASSERT(fType.kind() == Type::kVector_Kind);
+        if (fArguments.size() == 1 && fArguments[0]->fType.kind() == Type::kScalar_Kind) {
+            return *fArguments[0];
+        }
+        int current = 0;
+        for (const auto& arg : fArguments) {
+            ASSERT(current <= index);
+            if (arg->fType.kind() == Type::kScalar_Kind) {
+                if (index == current) {
+                    return *arg;
+                }
+                current++;
+            } else {
+                ASSERT(arg->fType.kind() == Type::kVector_Kind);
+                ASSERT(arg->fKind == Expression::kConstructor_Kind);
+                if (current + arg->fType.columns() > index) {
+                    return ((const Constructor&) *arg).getVecComponent(index - current);
+                }
+                current += arg->fType.columns();
+            }
+        }
+        ABORT("failed to find vector component %d in %s\n", index, description().c_str());
+    }
+
+    double getFVecComponent(int index) const {
+        const Expression& c = this->getVecComponent(index);
+        ASSERT(c.fKind == Expression::kFloatLiteral_Kind);
+        return ((FloatLiteral&) c).fValue;
+    }
+
+    int64_t getIVecComponent(int index) const {
+        const Expression& c = this->getVecComponent(index);
+        ASSERT(c.fKind == Expression::kIntLiteral_Kind);
+        return ((IntLiteral&) c).fValue;
+    }
+
+    // null return should be interpreted as zero
+    const Expression* getMatComponent(int col, int row) const {
+        ASSERT(this->isConstant());
+        ASSERT(fType.kind() == Type::kMatrix_Kind);
+        ASSERT(col < fType.columns() && row < fType.rows());
+        if (fArguments.size() == 1) {
+            if (fArguments[0]->fType.kind() == Type::kScalar_Kind) {
+                // single scalar argument, so matrix is of the form:
+                // x 0 0
+                // 0 x 0
+                // 0 0 x
+                // return x if col == row
+                return col == row ? fArguments[0].get() : nullptr;
+            }
+            if (fArguments[0]->fType.kind() == Type::kMatrix_Kind) {
+                ASSERT(fArguments[0]->fKind == Expression::kConstructor_Kind);
+                // single matrix argument. make sure we're within the argument's bounds.
+                const Type& argType = ((Constructor&) *fArguments[0]).fType;
+                if (col < argType.columns() && row < argType.rows()) {
+                    // within bounds, defer to argument
+                    return ((Constructor&) *fArguments[0]).getMatComponent(col, row);
+                }
+                // out of bounds, return 0
+                return nullptr;
+            }
+        }
+        int currentIndex = 0;
+        int targetIndex = col * fType.rows() + row;
+        for (const auto& arg : fArguments) {
+            ASSERT(targetIndex >= currentIndex);
+            ASSERT(arg->fType.rows() == 1);
+            if (currentIndex + arg->fType.columns() > targetIndex) {
+                if (arg->fType.columns() == 1) {
+                    return arg.get();
+                } else {
+                    ASSERT(arg->fType.kind() == Type::kVector_Kind);
+                    ASSERT(arg->fKind == Expression::kConstructor_Kind);
+                    return &((Constructor&) *arg).getVecComponent(targetIndex - currentIndex);
+                }
+            }
+            currentIndex += arg->fType.columns();
+        }
+        ABORT("can't happen, matrix component out of bounds");
+    }
+
     std::vector<std::unique_ptr<Expression>> fArguments;
 
     typedef Expression INHERITED;
diff --git a/src/sksl/ir/SkSLContinueStatement.h b/src/sksl/ir/SkSLContinueStatement.h
index b444694..3f5bc1d 100644
--- a/src/sksl/ir/SkSLContinueStatement.h
+++ b/src/sksl/ir/SkSLContinueStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_CONTINUESTATEMENT
 #define SKSL_CONTINUESTATEMENT
 
@@ -14,14 +14,14 @@
 namespace SkSL {
 
 /**
- * A 'continue' statement. 
+ * A 'continue' statement.
  */
 struct ContinueStatement : public Statement {
     ContinueStatement(Position position)
     : INHERITED(position, kContinue_Kind) {}
 
-    SkString description() const override {
-        return SkString("continue;");
+    String description() const override {
+        return String("continue;");
     }
 
     typedef Statement INHERITED;
diff --git a/src/sksl/ir/SkSLDiscardStatement.h b/src/sksl/ir/SkSLDiscardStatement.h
index 3ab6b27..6212466 100644
--- a/src/sksl/ir/SkSLDiscardStatement.h
+++ b/src/sksl/ir/SkSLDiscardStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_DISCARDSTATEMENT
 #define SKSL_DISCARDSTATEMENT
 
@@ -14,14 +14,14 @@
 namespace SkSL {
 
 /**
- * A 'discard' statement. 
+ * A 'discard' statement.
  */
 struct DiscardStatement : public Statement {
     DiscardStatement(Position position)
     : INHERITED(position, kDiscard_Kind) {}
 
-    SkString description() const override {
-        return SkString("discard;");
+    String description() const override {
+        return String("discard;");
     }
 
     typedef Statement INHERITED;
diff --git a/src/sksl/ir/SkSLDoStatement.h b/src/sksl/ir/SkSLDoStatement.h
index e26d3dc..f1ecd9a 100644
--- a/src/sksl/ir/SkSLDoStatement.h
+++ b/src/sksl/ir/SkSLDoStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_DOSTATEMENT
 #define SKSL_DOSTATEMENT
 
@@ -23,11 +23,11 @@
     , fStatement(std::move(statement))
     , fTest(std::move(test)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "do " + fStatement->description() + " while (" + fTest->description() + ");";
     }
 
-    const std::unique_ptr<Statement> fStatement;
+    std::unique_ptr<Statement> fStatement;
     std::unique_ptr<Expression> fTest;
 
     typedef Statement INHERITED;
diff --git a/src/sksl/ir/SkSLExpression.h b/src/sksl/ir/SkSLExpression.h
index f87d810..07dad1d 100644
--- a/src/sksl/ir/SkSLExpression.h
+++ b/src/sksl/ir/SkSLExpression.h
@@ -48,11 +48,31 @@
     , fKind(kind)
     , fType(std::move(type)) {}
 
+    /**
+     * Returns true if this expression is constant. compareConstant must be implemented for all
+     * constants!
+     */
     virtual bool isConstant() const {
         return false;
     }
 
     /**
+     * Compares this constant expression against another constant expression of the same type. It is
+     * an error to call this on non-constant expressions, or if the types of the expressions do not
+     * match.
+     */
+    virtual bool compareConstant(const Context& context, const Expression& other) const {
+        ABORT("cannot call compareConstant on this type");
+    }
+
+    /**
+     * Returns true if evaluating the expression potentially has side effects. Expressions may never
+     * return false if they actually have side effects, but it is legal (though suboptimal) to
+     * return true if there are not actually any side effects.
+     */
+    virtual bool hasSideEffects() const = 0;
+
+    /**
      * Given a map of known constant variable values, substitute them in for references to those
      * variables occurring in this expression and its subexpressions.  Similar simplifications, such
      * as folding a constant binary expression down to a single value, may also be performed.
diff --git a/src/sksl/ir/SkSLExpressionStatement.h b/src/sksl/ir/SkSLExpressionStatement.h
index 088b1c9..d1ab8e9 100644
--- a/src/sksl/ir/SkSLExpressionStatement.h
+++ b/src/sksl/ir/SkSLExpressionStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_EXPRESSIONSTATEMENT
 #define SKSL_EXPRESSIONSTATEMENT
 
@@ -14,14 +14,14 @@
 namespace SkSL {
 
 /**
- * A lone expression being used as a statement. 
+ * A lone expression being used as a statement.
  */
 struct ExpressionStatement : public Statement {
     ExpressionStatement(std::unique_ptr<Expression> expression)
     : INHERITED(expression->fPosition, kExpression_Kind)
     , fExpression(std::move(expression)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fExpression->description() + ";";
     }
 
diff --git a/src/sksl/ir/SkSLExtension.h b/src/sksl/ir/SkSLExtension.h
index ea5e044..70dc6b3 100644
--- a/src/sksl/ir/SkSLExtension.h
+++ b/src/sksl/ir/SkSLExtension.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_EXTENSION
 #define SKSL_EXTENSION
 
@@ -12,19 +12,19 @@
 
 namespace SkSL {
 
-/** 
- * An extension declaration. 
+/**
+ * An extension declaration.
  */
 struct Extension : public ProgramElement {
-    Extension(Position position, SkString name)
-    : INHERITED(position, kExtension_Kind) 
+    Extension(Position position, String name)
+    : INHERITED(position, kExtension_Kind)
     , fName(std::move(name)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "#extension " + fName + " : enable";
     }
 
-    const SkString fName;
+    const String fName;
 
     typedef ProgramElement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLField.h b/src/sksl/ir/SkSLField.h
index 53d85e0..abea730 100644
--- a/src/sksl/ir/SkSLField.h
+++ b/src/sksl/ir/SkSLField.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FIELD
 #define SKSL_FIELD
 
@@ -16,9 +16,9 @@
 
 namespace SkSL {
 
-/** 
- * A symbol which should be interpreted as a field access. Fields are added to the symboltable 
- * whenever a bare reference to an identifier should refer to a struct field; in GLSL, this is the 
+/**
+ * A symbol which should be interpreted as a field access. Fields are added to the symboltable
+ * whenever a bare reference to an identifier should refer to a struct field; in GLSL, this is the
  * result of declaring anonymous interface blocks.
  */
 struct Field : public Symbol {
@@ -27,7 +27,7 @@
     , fOwner(owner)
     , fFieldIndex(fieldIndex) {}
 
-    virtual SkString description() const override {
+    virtual String description() const override {
         return fOwner.description() + "." + fOwner.fType.fields()[fFieldIndex].fName;
     }
 
diff --git a/src/sksl/ir/SkSLFieldAccess.h b/src/sksl/ir/SkSLFieldAccess.h
index de26a3f..e0a335f 100644
--- a/src/sksl/ir/SkSLFieldAccess.h
+++ b/src/sksl/ir/SkSLFieldAccess.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FIELDACCESS
 #define SKSL_FIELDACCESS
 
@@ -24,14 +24,18 @@
         kAnonymousInterfaceBlock_OwnerKind
     };
 
-    FieldAccess(std::unique_ptr<Expression> base, int fieldIndex, 
+    FieldAccess(std::unique_ptr<Expression> base, int fieldIndex,
                 OwnerKind ownerKind = kDefault_OwnerKind)
     : INHERITED(base->fPosition, kFieldAccess_Kind, *base->fType.fields()[fieldIndex].fType)
     , fBase(std::move(base))
     , fFieldIndex(fieldIndex)
     , fOwnerKind(ownerKind) {}
 
-    virtual SkString description() const override {
+    bool hasSideEffects() const override {
+        return fBase->hasSideEffects();
+    }
+
+    String description() const override {
         return fBase->description() + "." + fBase->fType.fields()[fFieldIndex].fName;
     }
 
diff --git a/src/sksl/ir/SkSLFloatLiteral.h b/src/sksl/ir/SkSLFloatLiteral.h
index 8a1a5ad..21a485f 100644
--- a/src/sksl/ir/SkSLFloatLiteral.h
+++ b/src/sksl/ir/SkSLFloatLiteral.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FLOATLITERAL
 #define SKSL_FLOATLITERAL
 
@@ -17,18 +17,28 @@
  * A literal floating point number.
  */
 struct FloatLiteral : public Expression {
-    FloatLiteral(const Context& context, Position position, double value)
-    : INHERITED(position, kFloatLiteral_Kind, *context.fFloat_Type)
+    FloatLiteral(const Context& context, Position position, double value,
+                 const Type* type = nullptr)
+    : INHERITED(position, kFloatLiteral_Kind, type ? *type : *context.fFloat_Type)
     , fValue(value) {}
 
-    virtual SkString description() const override {
+    String description() const override {
         return to_string(fValue);
     }
 
+    bool hasSideEffects() const override {
+        return false;
+    }
+
     bool isConstant() const override {
         return true;
     }
 
+    bool compareConstant(const Context& context, const Expression& other) const override {
+        FloatLiteral& f = (FloatLiteral&) other;
+        return fValue == f.fValue;
+    }
+
     const double fValue;
 
     typedef Expression INHERITED;
diff --git a/src/sksl/ir/SkSLForStatement.h b/src/sksl/ir/SkSLForStatement.h
index f2bf880..ca3e6cf 100644
--- a/src/sksl/ir/SkSLForStatement.h
+++ b/src/sksl/ir/SkSLForStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FORSTATEMENT
 #define SKSL_FORSTATEMENT
 
@@ -18,8 +18,8 @@
  * A 'for' statement.
  */
 struct ForStatement : public Statement {
-    ForStatement(Position position, std::unique_ptr<Statement> initializer, 
-                 std::unique_ptr<Expression> test, std::unique_ptr<Expression> next, 
+    ForStatement(Position position, std::unique_ptr<Statement> initializer,
+                 std::unique_ptr<Expression> test, std::unique_ptr<Expression> next,
                  std::unique_ptr<Statement> statement, std::shared_ptr<SymbolTable> symbols)
     : INHERITED(position, kFor_Kind)
     , fSymbols(symbols)
@@ -28,15 +28,15 @@
     , fNext(std::move(next))
     , fStatement(std::move(statement)) {}
 
-    SkString description() const override {
-        SkString result("for (");
+    String description() const override {
+        String result("for (");
         if (fInitializer) {
             result += fInitializer->description();
-        } 
+        }
         result += " ";
         if (fTest) {
             result += fTest->description();
-        } 
+        }
         result += "; ";
         if (fNext) {
             result += fNext->description();
@@ -48,10 +48,10 @@
     // it's important to keep fSymbols defined first (and thus destroyed last) because destroying
     // the other fields can update symbol reference counts
     const std::shared_ptr<SymbolTable> fSymbols;
-    const std::unique_ptr<Statement> fInitializer;
+    std::unique_ptr<Statement> fInitializer;
     std::unique_ptr<Expression> fTest;
     std::unique_ptr<Expression> fNext;
-    const std::unique_ptr<Statement> fStatement;
+    std::unique_ptr<Statement> fStatement;
 
     typedef Statement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLFunctionCall.h b/src/sksl/ir/SkSLFunctionCall.h
index 1838076..44f8c7e 100644
--- a/src/sksl/ir/SkSLFunctionCall.h
+++ b/src/sksl/ir/SkSLFunctionCall.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FUNCTIONCALL
 #define SKSL_FUNCTIONCALL
 
@@ -23,9 +23,18 @@
     , fFunction(std::move(function))
     , fArguments(std::move(arguments)) {}
 
-    SkString description() const override {
-        SkString result = fFunction.fName + "(";
-        SkString separator;
+    bool hasSideEffects() const override {
+        for (const auto& arg : fArguments) {
+            if (arg->hasSideEffects()) {
+                return true;
+            }
+        }
+        return fFunction.fModifiers.fFlags & Modifiers::kHasSideEffects_Flag;
+    }
+
+    String description() const override {
+        String result = fFunction.fName + "(";
+        String separator;
         for (size_t i = 0; i < fArguments.size(); i++) {
             result += separator;
             result += fArguments[i]->description();
diff --git a/src/sksl/ir/SkSLFunctionDeclaration.h b/src/sksl/ir/SkSLFunctionDeclaration.h
index c15d2b9..8704c9a 100644
--- a/src/sksl/ir/SkSLFunctionDeclaration.h
+++ b/src/sksl/ir/SkSLFunctionDeclaration.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FUNCTIONDECLARATION
 #define SKSL_FUNCTIONDECLARATION
 
@@ -21,17 +21,18 @@
  * A function declaration (not a definition -- does not contain a body).
  */
 struct FunctionDeclaration : public Symbol {
-    FunctionDeclaration(Position position, SkString name, 
+    FunctionDeclaration(Position position, Modifiers modifiers, String name,
                         std::vector<const Variable*> parameters, const Type& returnType)
     : INHERITED(position, kFunctionDeclaration_Kind, std::move(name))
     , fDefined(false)
     , fBuiltin(false)
+    , fModifiers(modifiers)
     , fParameters(std::move(parameters))
     , fReturnType(returnType) {}
 
-    SkString description() const override {
-        SkString result = fReturnType.description() + " " + fName + "(";
-        SkString separator;
+    String description() const override {
+        String result = fReturnType.description() + " " + fName + "(";
+        String separator;
         for (auto p : fParameters) {
             result += separator;
             separator = ", ";
@@ -58,7 +59,7 @@
 
     /**
      * Determine the effective types of this function's parameters and return value when called with
-     * the given arguments. This is relevant for functions with generic parameter types, where this 
+     * the given arguments. This is relevant for functions with generic parameter types, where this
      * will collapse the generic types down into specific concrete types.
      *
      * Returns true if it was able to select a concrete set of types for the generic function, false
@@ -102,6 +103,7 @@
 
     mutable bool fDefined;
     bool fBuiltin;
+    Modifiers fModifiers;
     const std::vector<const Variable*> fParameters;
     const Type& fReturnType;
 
diff --git a/src/sksl/ir/SkSLFunctionDefinition.h b/src/sksl/ir/SkSLFunctionDefinition.h
index bae8825..0277db1 100644
--- a/src/sksl/ir/SkSLFunctionDefinition.h
+++ b/src/sksl/ir/SkSLFunctionDefinition.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FUNCTIONDEFINITION
 #define SKSL_FUNCTIONDEFINITION
 
@@ -18,18 +18,18 @@
  * A function definition (a declaration plus an associated block of code).
  */
 struct FunctionDefinition : public ProgramElement {
-    FunctionDefinition(Position position, const FunctionDeclaration& declaration, 
-                       std::unique_ptr<Block> body)
+    FunctionDefinition(Position position, const FunctionDeclaration& declaration,
+                       std::unique_ptr<Statement> body)
     : INHERITED(position, kFunction_Kind)
     , fDeclaration(declaration)
     , fBody(std::move(body)) {}
 
-    SkString description() const override {
+    String description() const override {
         return fDeclaration.description() + " " + fBody->description();
     }
 
     const FunctionDeclaration& fDeclaration;
-    const std::unique_ptr<Block> fBody;
+    std::unique_ptr<Statement> fBody;
 
     typedef ProgramElement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLFunctionReference.h b/src/sksl/ir/SkSLFunctionReference.h
index e95833d..ee761c2 100644
--- a/src/sksl/ir/SkSLFunctionReference.h
+++ b/src/sksl/ir/SkSLFunctionReference.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_FUNCTIONREFERENCE
 #define SKSL_FUNCTIONREFERENCE
 
@@ -15,18 +15,22 @@
 namespace SkSL {
 
 /**
- * An identifier referring to a function name. This is an intermediate value: FunctionReferences are 
+ * An identifier referring to a function name. This is an intermediate value: FunctionReferences are
  * always eventually replaced by FunctionCalls in valid programs.
  */
 struct FunctionReference : public Expression {
-    FunctionReference(const Context& context, Position position, 
+    FunctionReference(const Context& context, Position position,
                       std::vector<const FunctionDeclaration*> function)
     : INHERITED(position, kFunctionReference_Kind, *context.fInvalid_Type)
     , fFunctions(function) {}
 
-    virtual SkString description() const override {
+    bool hasSideEffects() const override {
+        return false;
+    }
+
+    String description() const override {
         ASSERT(false);
-        return SkString("<function>");
+        return String("<function>");
     }
 
     const std::vector<const FunctionDeclaration*> fFunctions;
diff --git a/src/sksl/ir/SkSLIRNode.h b/src/sksl/ir/SkSLIRNode.h
index 9a04cdd..139be32 100644
--- a/src/sksl/ir/SkSLIRNode.h
+++ b/src/sksl/ir/SkSLIRNode.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_IRNODE
 #define SKSL_IRNODE
 
@@ -13,7 +13,7 @@
 namespace SkSL {
 
 /**
- * Represents a node in the intermediate representation (IR) tree. The IR is a fully-resolved 
+ * Represents a node in the intermediate representation (IR) tree. The IR is a fully-resolved
  * version of the program (all types determined, everything validated), ready for code generation.
  */
 struct IRNode {
@@ -22,7 +22,7 @@
 
     virtual ~IRNode() {}
 
-    virtual SkString description() const = 0;
+    virtual String description() const = 0;
 
     const Position fPosition;
 };
diff --git a/src/sksl/ir/SkSLIfStatement.h b/src/sksl/ir/SkSLIfStatement.h
index 8667e93..b09c10e 100644
--- a/src/sksl/ir/SkSLIfStatement.h
+++ b/src/sksl/ir/SkSLIfStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_IFSTATEMENT
 #define SKSL_IFSTATEMENT
 
@@ -17,24 +17,31 @@
  * An 'if' statement.
  */
 struct IfStatement : public Statement {
-    IfStatement(Position position, std::unique_ptr<Expression> test, 
+    IfStatement(Position position, bool isStatic, std::unique_ptr<Expression> test,
                 std::unique_ptr<Statement> ifTrue, std::unique_ptr<Statement> ifFalse)
     : INHERITED(position, kIf_Kind)
+    , fIsStatic(isStatic)
     , fTest(std::move(test))
     , fIfTrue(std::move(ifTrue))
     , fIfFalse(std::move(ifFalse)) {}
 
-    SkString description() const override {
-        SkString result = "if (" + fTest->description() + ") " + fIfTrue->description();
+    String description() const override {
+        String result;
+        if (fIsStatic) {
+            result += "@";
+        }
+        result += "if (" + fTest->description() + ") " + fIfTrue->description();
         if (fIfFalse) {
             result += " else " + fIfFalse->description();
         }
         return result;
     }
 
+    bool fIsStatic;
     std::unique_ptr<Expression> fTest;
-    const std::unique_ptr<Statement> fIfTrue;
-    const std::unique_ptr<Statement> fIfFalse;
+    std::unique_ptr<Statement> fIfTrue;
+    // may be null
+    std::unique_ptr<Statement> fIfFalse;
 
     typedef Statement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLIndexExpression.h b/src/sksl/ir/SkSLIndexExpression.h
index d255c7d..49633b6 100644
--- a/src/sksl/ir/SkSLIndexExpression.h
+++ b/src/sksl/ir/SkSLIndexExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_INDEX
 #define SKSL_INDEX
 
@@ -43,7 +43,7 @@
  * An expression which extracts a value from an array or matrix, as in 'm[2]'.
  */
 struct IndexExpression : public Expression {
-    IndexExpression(const Context& context, std::unique_ptr<Expression> base, 
+    IndexExpression(const Context& context, std::unique_ptr<Expression> base,
                     std::unique_ptr<Expression> index)
     : INHERITED(base->fPosition, kIndex_Kind, index_type(context, base->fType))
     , fBase(std::move(base))
@@ -51,7 +51,11 @@
         ASSERT(fIndex->fType == *context.fInt_Type || fIndex->fType == *context.fUInt_Type);
     }
 
-    SkString description() const override {
+    bool hasSideEffects() const override {
+        return fBase->hasSideEffects() || fIndex->hasSideEffects();
+    }
+
+    String description() const override {
         return fBase->description() + "[" + fIndex->description() + "]";
     }
 
diff --git a/src/sksl/ir/SkSLIntLiteral.h b/src/sksl/ir/SkSLIntLiteral.h
index b6a23d6..d8eba55 100644
--- a/src/sksl/ir/SkSLIntLiteral.h
+++ b/src/sksl/ir/SkSLIntLiteral.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_INTLITERAL
 #define SKSL_INTLITERAL
 
@@ -23,14 +23,23 @@
     : INHERITED(position, kIntLiteral_Kind, type ? *type : *context.fInt_Type)
     , fValue(value) {}
 
-    virtual SkString description() const override {
+    String description() const override {
         return to_string(fValue);
     }
 
-   bool isConstant() const override {
+    bool hasSideEffects() const override {
+        return false;
+    }
+
+    bool isConstant() const override {
         return true;
     }
 
+    bool compareConstant(const Context& context, const Expression& other) const override {
+        IntLiteral& i = (IntLiteral&) other;
+        return fValue == i.fValue;
+    }
+
     const int64_t fValue;
 
     typedef Expression INHERITED;
diff --git a/src/sksl/ir/SkSLInterfaceBlock.h b/src/sksl/ir/SkSLInterfaceBlock.h
index 0de37c5..fc6ccec 100644
--- a/src/sksl/ir/SkSLInterfaceBlock.h
+++ b/src/sksl/ir/SkSLInterfaceBlock.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_INTERFACEBLOCK
 #define SKSL_INTERFACEBLOCK
 
@@ -25,18 +25,18 @@
  * At the IR level, this is represented by a single variable of struct type.
  */
 struct InterfaceBlock : public ProgramElement {
-    InterfaceBlock(Position position, const Variable& var, SkString typeName, SkString instanceName,
+    InterfaceBlock(Position position, const Variable* var, String typeName, String instanceName,
                    std::vector<std::unique_ptr<Expression>> sizes,
                    std::shared_ptr<SymbolTable> typeOwner)
     : INHERITED(position, kInterfaceBlock_Kind)
-    , fVariable(std::move(var))
+    , fVariable(*var)
     , fTypeName(std::move(typeName))
     , fInstanceName(std::move(instanceName))
     , fSizes(std::move(sizes))
     , fTypeOwner(typeOwner) {}
 
-    SkString description() const override {
-        SkString result = fVariable.fModifiers.description() + fTypeName + " {\n";
+    String description() const override {
+        String result = fVariable.fModifiers.description() + fTypeName + " {\n";
         const Type* structType = &fVariable.fType;
         while (structType->kind() == Type::kArray_Kind) {
             structType = &structType->componentType();
@@ -59,8 +59,8 @@
     }
 
     const Variable& fVariable;
-    const SkString fTypeName;
-    const SkString fInstanceName;
+    const String fTypeName;
+    const String fInstanceName;
     const std::vector<std::unique_ptr<Expression>> fSizes;
     const std::shared_ptr<SymbolTable> fTypeOwner;
 
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index 5e7ec44..3a8416ac 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -8,7 +8,6 @@
 #ifndef SKSL_LAYOUT
 #define SKSL_LAYOUT
 
-#include "SkString.h"
 #include "SkSLUtil.h"
 
 namespace SkSL {
@@ -55,11 +54,11 @@
             case Format::kRGBA8I:       return "rgba8i";
             case Format::kR8I:          return "r8i";
         }
-        SkFAIL("Unexpected format");
+        ABORT("Unexpected format");
         return "";
     }
 
-    static bool ReadFormat(SkString str, Format* format) {
+    static bool ReadFormat(String str, Format* format) {
         if (str == "rgba32f") {
             *format = Format::kRGBA32F;
             return true;
@@ -125,9 +124,9 @@
     , fMaxVertices(-1)
     , fInvocations(-1) {}
 
-    SkString description() const {
-        SkString result;
-        SkString separator;
+    String description() const {
+        String result;
+        String separator;
         if (fLocation >= 0) {
             result += separator + "location = " + to_string(fLocation);
             separator = ", ";
diff --git a/src/sksl/ir/SkSLModifiers.h b/src/sksl/ir/SkSLModifiers.h
index c7a5639..80ef5d7 100644
--- a/src/sksl/ir/SkSLModifiers.h
+++ b/src/sksl/ir/SkSLModifiers.h
@@ -17,21 +17,23 @@
  */
 struct Modifiers {
     enum Flag {
-        kNo_Flag            =    0,
-        kConst_Flag         =    1,
-        kIn_Flag            =    2,
-        kOut_Flag           =    4,
-        kLowp_Flag          =    8,
-        kMediump_Flag       =   16,
-        kHighp_Flag         =   32,
-        kUniform_Flag       =   64,
-        kFlat_Flag          =  128,
-        kNoPerspective_Flag =  256,
-        kReadOnly_Flag      =  512,
-        kWriteOnly_Flag     = 1024,
-        kCoherent_Flag      = 2048,
-        kVolatile_Flag      = 4096,
-        kRestrict_Flag      = 8192
+        kNo_Flag             =       0,
+        kConst_Flag          = 1 <<  0,
+        kIn_Flag             = 1 <<  1,
+        kOut_Flag            = 1 <<  2,
+        kLowp_Flag           = 1 <<  3,
+        kMediump_Flag        = 1 <<  4,
+        kHighp_Flag          = 1 <<  5,
+        kUniform_Flag        = 1 <<  6,
+        kFlat_Flag           = 1 <<  7,
+        kNoPerspective_Flag  = 1 <<  8,
+        kReadOnly_Flag       = 1 <<  9,
+        kWriteOnly_Flag      = 1 << 10,
+        kCoherent_Flag       = 1 << 11,
+        kVolatile_Flag       = 1 << 12,
+        kRestrict_Flag       = 1 << 13,
+        kBuffer_Flag         = 1 << 14,
+        kHasSideEffects_Flag = 1 << 15
     };
 
     Modifiers()
@@ -42,8 +44,8 @@
     : fLayout(layout)
     , fFlags(flags) {}
 
-    SkString description() const {
-        SkString result = fLayout.description();
+    String description() const {
+        String result = fLayout.description();
         if (fFlags & kUniform_Flag) {
             result += "uniform ";
         }
@@ -80,6 +82,12 @@
         if (fFlags & kRestrict_Flag) {
             result += "restrict ";
         }
+        if (fFlags & kBuffer_Flag) {
+            result += "buffer ";
+        }
+        if (fFlags & kHasSideEffects_Flag) {
+            result += "sk_has_side_effects ";
+        }
 
         if ((fFlags & kIn_Flag) && (fFlags & kOut_Flag)) {
             result += "inout ";
diff --git a/src/sksl/ir/SkSLModifiersDeclaration.h b/src/sksl/ir/SkSLModifiersDeclaration.h
index 625954d..a0ce748 100644
--- a/src/sksl/ir/SkSLModifiersDeclaration.h
+++ b/src/sksl/ir/SkSLModifiersDeclaration.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_MODIFIERDECLARATION
 #define SKSL_MODIFIERDECLARATION
 
@@ -23,7 +23,7 @@
     : INHERITED(Position(), kModifiers_Kind)
     , fModifiers(modifiers) {}
 
-    SkString description() const {
+    String description() const {
         return fModifiers.description() + ";";
     }
 
diff --git a/src/sksl/ir/SkSLNop.h b/src/sksl/ir/SkSLNop.h
new file mode 100644
index 0000000..5ebea40
--- /dev/null
+++ b/src/sksl/ir/SkSLNop.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_NOP
+#define SKSL_NOP
+
+#include "SkSLStatement.h"
+#include "SkSLSymbolTable.h"
+
+namespace SkSL {
+
+/**
+ * A no-op statement that does nothing.
+ */
+struct Nop : public Statement {
+    Nop()
+    : INHERITED(Position(), kNop_Kind) {}
+
+    virtual bool isEmpty() const override {
+        return true;
+    }
+
+    String description() const override {
+        return String(";");
+    }
+
+    typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLPostfixExpression.h b/src/sksl/ir/SkSLPostfixExpression.h
index 05ff167..e02555d 100644
--- a/src/sksl/ir/SkSLPostfixExpression.h
+++ b/src/sksl/ir/SkSLPostfixExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_POSTFIXEXPRESSION
 #define SKSL_POSTFIXEXPRESSION
 
@@ -22,7 +22,11 @@
     , fOperand(std::move(operand))
     , fOperator(op) {}
 
-    virtual SkString description() const override {
+    bool hasSideEffects() const override {
+        return true;
+    }
+
+    String description() const override {
         return fOperand->description() + Token::OperatorName(fOperator);
     }
 
diff --git a/src/sksl/ir/SkSLPrefixExpression.h b/src/sksl/ir/SkSLPrefixExpression.h
index dafe1e9..acab37e 100644
--- a/src/sksl/ir/SkSLPrefixExpression.h
+++ b/src/sksl/ir/SkSLPrefixExpression.h
@@ -4,11 +4,13 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_PREFIXEXPRESSION
 #define SKSL_PREFIXEXPRESSION
 
 #include "SkSLExpression.h"
+#include "SkSLFloatLiteral.h"
+#include "SkSLIRGenerator.h"
 #include "SkSLToken.h"
 
 namespace SkSL {
@@ -22,7 +24,29 @@
     , fOperand(std::move(operand))
     , fOperator(op) {}
 
-    virtual SkString description() const override {
+    bool isConstant() const override {
+        return fOperator == Token::MINUS && fOperand->isConstant();
+    }
+
+    bool hasSideEffects() const override {
+        return fOperator == Token::PLUSPLUS || fOperator == Token::MINUSMINUS ||
+               fOperand->hasSideEffects();
+    }
+
+    virtual std::unique_ptr<Expression> constantPropagate(
+                                                        const IRGenerator& irGenerator,
+                                                        const DefinitionMap& definitions) override {
+        if (fOperand->fKind == Expression::kFloatLiteral_Kind) {
+            return std::unique_ptr<Expression>(new FloatLiteral(
+                                                              irGenerator.fContext,
+                                                              Position(),
+                                                              -((FloatLiteral&) *fOperand).fValue));
+
+        }
+        return nullptr;
+    }
+
+    String description() const override {
         return Token::OperatorName(fOperator) + fOperand->description();
     }
 
diff --git a/src/sksl/ir/SkSLProgram.h b/src/sksl/ir/SkSLProgram.h
index 2ca9372..96bd5c4 100644
--- a/src/sksl/ir/SkSLProgram.h
+++ b/src/sksl/ir/SkSLProgram.h
@@ -26,7 +26,11 @@
  */
 struct Program {
     struct Settings {
+#ifdef SKSL_STANDALONE
+        const StandaloneShaderCaps* fCaps = &standaloneCaps;
+#else
         const GrShaderCaps* fCaps = nullptr;
+#endif
         // if false, sk_FragCoord is exactly the same as gl_FragCoord. If true, the y coordinate
         // must be flipped.
         bool fFlipY = false;
diff --git a/src/sksl/ir/SkSLProgramElement.h b/src/sksl/ir/SkSLProgramElement.h
index 2f1ce77..ebb4e9a 100644
--- a/src/sksl/ir/SkSLProgramElement.h
+++ b/src/sksl/ir/SkSLProgramElement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_PROGRAMELEMENT
 #define SKSL_PROGRAMELEMENT
 
diff --git a/src/sksl/ir/SkSLReturnStatement.h b/src/sksl/ir/SkSLReturnStatement.h
index dc5ec9a..841db94 100644
--- a/src/sksl/ir/SkSLReturnStatement.h
+++ b/src/sksl/ir/SkSLReturnStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_RETURNSTATEMENT
 #define SKSL_RETURNSTATEMENT
 
@@ -21,14 +21,14 @@
     : INHERITED(position, kReturn_Kind) {}
 
     ReturnStatement(std::unique_ptr<Expression> expression)
-    : INHERITED(expression->fPosition, kReturn_Kind) 
+    : INHERITED(expression->fPosition, kReturn_Kind)
     , fExpression(std::move(expression)) {}
 
-    SkString description() const override {
+    String description() const override {
         if (fExpression) {
             return "return " + fExpression->description() + ";";
         } else {
-            return SkString("return;");
+            return String("return;");
         }
     }
 
diff --git a/src/sksl/ir/SkSLStatement.h b/src/sksl/ir/SkSLStatement.h
index c3a6f95..1bc5244 100644
--- a/src/sksl/ir/SkSLStatement.h
+++ b/src/sksl/ir/SkSLStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_STATEMENT
 #define SKSL_STATEMENT
 
@@ -25,9 +25,12 @@
         kDo_Kind,
         kExpression_Kind,
         kFor_Kind,
+        kGroup_Kind,
         kIf_Kind,
+        kNop_Kind,
         kReturn_Kind,
         kSwitch_Kind,
+        kVarDeclaration_Kind,
         kVarDeclarations_Kind,
         kWhile_Kind
     };
@@ -36,6 +39,10 @@
     : INHERITED(position)
     , fKind(kind) {}
 
+    virtual bool isEmpty() const {
+        return false;
+    }
+
     const Kind fKind;
 
     typedef IRNode INHERITED;
diff --git a/src/sksl/ir/SkSLSwitchCase.h b/src/sksl/ir/SkSLSwitchCase.h
index 1fc2e85..8043f2e 100644
--- a/src/sksl/ir/SkSLSwitchCase.h
+++ b/src/sksl/ir/SkSLSwitchCase.h
@@ -23,8 +23,8 @@
     , fValue(std::move(value))
     , fStatements(std::move(statements)) {}
 
-    SkString description() const override {
-        SkString result;
+    String description() const override {
+        String result;
         if (fValue) {
             result.appendf("case %s:\n", fValue->description().c_str());
         } else {
diff --git a/src/sksl/ir/SkSLSwitchStatement.h b/src/sksl/ir/SkSLSwitchStatement.h
index 31765c4..3837554 100644
--- a/src/sksl/ir/SkSLSwitchStatement.h
+++ b/src/sksl/ir/SkSLSwitchStatement.h
@@ -17,14 +17,19 @@
  * A 'switch' statement.
  */
 struct SwitchStatement : public Statement {
-    SwitchStatement(Position position, std::unique_ptr<Expression> value,
+    SwitchStatement(Position position, bool isStatic, std::unique_ptr<Expression> value,
                     std::vector<std::unique_ptr<SwitchCase>> cases)
     : INHERITED(position, kSwitch_Kind)
+    , fIsStatic(isStatic)
     , fValue(std::move(value))
     , fCases(std::move(cases)) {}
 
-    SkString description() const override {
-        SkString result = SkStringPrintf("switch (%s) {\n", + fValue->description().c_str());
+    String description() const override {
+        String result;
+        if (fIsStatic) {
+            result += "@";
+        }
+        result += String::printf("switch (%s) {\n", fValue->description().c_str());
         for (const auto& c : fCases) {
             result += c->description();
         }
@@ -32,6 +37,7 @@
         return result;
     }
 
+    bool fIsStatic;
     std::unique_ptr<Expression> fValue;
     std::vector<std::unique_ptr<SwitchCase>> fCases;
 
diff --git a/src/sksl/ir/SkSLSwizzle.h b/src/sksl/ir/SkSLSwizzle.h
index 0803f3b..1e36c41 100644
--- a/src/sksl/ir/SkSLSwizzle.h
+++ b/src/sksl/ir/SkSLSwizzle.h
@@ -4,18 +4,20 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_SWIZZLE
 #define SKSL_SWIZZLE
 
+#include "SkSLConstructor.h"
 #include "SkSLContext.h"
 #include "SkSLExpression.h"
+#include "SkSLIRGenerator.h"
 #include "SkSLUtil.h"
 
 namespace SkSL {
 
 /**
- * Given a type and a swizzle component count, returns the type that will result from swizzling. For 
+ * Given a type and a swizzle component count, returns the type that will result from swizzling. For
  * instance, swizzling a vec3 with two components will result in a vec2. It is possible to swizzle
  * with more components than the source vector, as in 'vec2(1).xxxx'.
  */
@@ -69,8 +71,36 @@
         ASSERT(fComponents.size() >= 1 && fComponents.size() <= 4);
     }
 
-    SkString description() const override {
-        SkString result = fBase->description() + ".";
+    virtual std::unique_ptr<Expression> constantPropagate(
+                                                        const IRGenerator& irGenerator,
+                                                        const DefinitionMap& definitions) override {
+
+        if (fBase->fKind == Expression::kConstructor_Kind && fBase->isConstant()) {
+            // we're swizzling a constant vector, e.g. vec4(1).x. Simplify it.
+            ASSERT(fBase->fKind == Expression::kConstructor_Kind);
+            if (fType == *irGenerator.fContext.fInt_Type) {
+                ASSERT(fComponents.size() == 1);
+                int64_t value = ((Constructor&) *fBase).getIVecComponent(fComponents[0]);
+                return std::unique_ptr<Expression>(new IntLiteral(irGenerator.fContext,
+                                                                    Position(),
+                                                                    value));
+            } else if (fType == *irGenerator.fContext.fFloat_Type) {
+                ASSERT(fComponents.size() == 1);
+                double value = ((Constructor&) *fBase).getFVecComponent(fComponents[0]);
+                return std::unique_ptr<Expression>(new FloatLiteral(irGenerator.fContext,
+                                                                    Position(),
+                                                                    value));
+            }
+        }
+        return nullptr;
+    }
+
+    bool hasSideEffects() const override {
+        return fBase->hasSideEffects();
+    }
+
+    String description() const override {
+        String result = fBase->description() + ".";
         for (int x : fComponents) {
             result += "xyzw"[x];
         }
diff --git a/src/sksl/ir/SkSLSymbol.h b/src/sksl/ir/SkSLSymbol.h
index 10dcaf9..e883ea7 100644
--- a/src/sksl/ir/SkSLSymbol.h
+++ b/src/sksl/ir/SkSLSymbol.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_SYMBOL
 #define SKSL_SYMBOL
 
@@ -24,13 +24,13 @@
         kField_Kind
     };
 
-    Symbol(Position position, Kind kind, SkString name)
+    Symbol(Position position, Kind kind, String name)
     : INHERITED(position)
     , fKind(kind)
     , fName(std::move(name)) {}
 
     const Kind fKind;
-    const SkString fName;
+    const String fName;
 
     typedef IRNode INHERITED;
 };
diff --git a/src/sksl/ir/SkSLSymbolTable.cpp b/src/sksl/ir/SkSLSymbolTable.cpp
index 3ceeab9..4d39e8b 100644
--- a/src/sksl/ir/SkSLSymbolTable.cpp
+++ b/src/sksl/ir/SkSLSymbolTable.cpp
@@ -21,7 +21,7 @@
     }
 }
 
-const Symbol* SymbolTable::operator[](const SkString& name) {
+const Symbol* SymbolTable::operator[](const String& name) {
     const auto& entry = fSymbols.find(name);
     if (entry == fSymbols.end()) {
         if (fParent) {
@@ -64,12 +64,12 @@
     return s;
 }
 
-void SymbolTable::add(const SkString& name, std::unique_ptr<Symbol> symbol) {
+void SymbolTable::add(const String& name, std::unique_ptr<Symbol> symbol) {
     this->addWithoutOwnership(name, symbol.get());
     fOwnedPointers.push_back(std::move(symbol));
 }
 
-void SymbolTable::addWithoutOwnership(const SkString& name, const Symbol* symbol) {
+void SymbolTable::addWithoutOwnership(const String& name, const Symbol* symbol) {
     const auto& existing = fSymbols.find(name);
     if (existing == fSymbols.end()) {
         fSymbols[name] = symbol;
diff --git a/src/sksl/ir/SkSLSymbolTable.h b/src/sksl/ir/SkSLSymbolTable.h
index df8dc71..6bafef2 100644
--- a/src/sksl/ir/SkSLSymbolTable.h
+++ b/src/sksl/ir/SkSLSymbolTable.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_SYMBOLTABLE
 #define SKSL_SYMBOLTABLE
 
@@ -24,18 +24,18 @@
  */
 class SymbolTable {
 public:
-    SymbolTable(ErrorReporter& errorReporter)
-    : fErrorReporter(errorReporter) {}
+    SymbolTable(ErrorReporter* errorReporter)
+    : fErrorReporter(*errorReporter) {}
 
-    SymbolTable(std::shared_ptr<SymbolTable> parent, ErrorReporter& errorReporter)
+    SymbolTable(std::shared_ptr<SymbolTable> parent, ErrorReporter* errorReporter)
     : fParent(parent)
-    , fErrorReporter(errorReporter) {}
+    , fErrorReporter(*errorReporter) {}
 
-    const Symbol* operator[](const SkString& name);
+    const Symbol* operator[](const String& name);
 
-    void add(const SkString& name, std::unique_ptr<Symbol> symbol);
+    void add(const String& name, std::unique_ptr<Symbol> symbol);
 
-    void addWithoutOwnership(const SkString& name, const Symbol* symbol);
+    void addWithoutOwnership(const String& name, const Symbol* symbol);
 
     Symbol* takeOwnership(Symbol* s);
 
@@ -48,7 +48,7 @@
 
     std::vector<std::unique_ptr<Symbol>> fOwnedPointers;
 
-    std::unordered_map<SkString, const Symbol*> fSymbols;
+    std::unordered_map<String, const Symbol*> fSymbols;
 
     ErrorReporter& fErrorReporter;
 };
diff --git a/src/sksl/ir/SkSLTernaryExpression.h b/src/sksl/ir/SkSLTernaryExpression.h
index 0275004..567af56 100644
--- a/src/sksl/ir/SkSLTernaryExpression.h
+++ b/src/sksl/ir/SkSLTernaryExpression.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_TERNARYEXPRESSION
 #define SKSL_TERNARYEXPRESSION
 
@@ -26,8 +26,12 @@
         ASSERT(fIfTrue->fType == fIfFalse->fType);
     }
 
-    SkString description() const override {
-        return "(" + fTest->description() + " ? " + fIfTrue->description() + " : " + 
+    bool hasSideEffects() const override {
+        return fTest->hasSideEffects() || fIfTrue->hasSideEffects() || fIfFalse->hasSideEffects();
+    }
+
+    String description() const override {
+        return "(" + fTest->description() + " ? " + fIfTrue->description() + " : " +
                fIfFalse->description() + ")";
     }
 
diff --git a/src/sksl/ir/SkSLType.cpp b/src/sksl/ir/SkSLType.cpp
index d28c4f0..c919cbc 100644
--- a/src/sksl/ir/SkSLType.cpp
+++ b/src/sksl/ir/SkSLType.cpp
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #include "SkSLType.h"
 #include "SkSLContext.h"
 
@@ -22,7 +22,7 @@
         return false;
     }
     if (this->kind() == kMatrix_Kind) {
-        if (this->columns() == other.columns() && 
+        if (this->columns() == other.columns() &&
             this->rows() == other.rows()) {
             return this->componentType().determineCoercionCost(other.componentType(), outCost);
         }
diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h
index 2f98e53..44bc262 100644
--- a/src/sksl/ir/SkSLType.h
+++ b/src/sksl/ir/SkSLType.h
@@ -26,17 +26,17 @@
 class Type : public Symbol {
 public:
     struct Field {
-        Field(Modifiers modifiers, SkString name, const Type* type)
+        Field(Modifiers modifiers, String name, const Type* type)
         : fModifiers(modifiers)
         , fName(std::move(name))
         , fType(std::move(type)) {}
 
-        const SkString description() const {
+        const String description() const {
             return fType->description() + " " + fName + ";";
         }
 
         Modifiers fModifiers;
-        SkString fName;
+        String fName;
         const Type* fType;
     };
 
@@ -53,24 +53,24 @@
 
     // Create an "other" (special) type with the given name. These types cannot be directly
     // referenced from user code.
-    Type(SkString name)
+    Type(String name)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kOther_Kind) {}
 
     // Create a generic type which maps to the listed types.
-    Type(SkString name, std::vector<const Type*> types)
+    Type(String name, std::vector<const Type*> types)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kGeneric_Kind)
     , fCoercibleTypes(std::move(types)) {}
 
     // Create a struct type with the given fields.
-    Type(Position position, SkString name, std::vector<Field> fields)
+    Type(Position position, String name, std::vector<Field> fields)
     : INHERITED(position, kType_Kind, std::move(name))
     , fTypeKind(kStruct_Kind)
     , fFields(std::move(fields)) {}
 
     // Create a scalar type.
-    Type(SkString name, bool isNumber)
+    Type(String name, bool isNumber)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kScalar_Kind)
     , fIsNumber(isNumber)
@@ -78,7 +78,7 @@
     , fRows(1) {}
 
     // Create a scalar type which can be coerced to the listed types.
-    Type(SkString name, bool isNumber, std::vector<const Type*> coercibleTypes)
+    Type(String name, bool isNumber, std::vector<const Type*> coercibleTypes)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kScalar_Kind)
     , fIsNumber(isNumber)
@@ -87,11 +87,11 @@
     , fRows(1) {}
 
     // Create a vector type.
-    Type(SkString name, const Type& componentType, int columns)
+    Type(String name, const Type& componentType, int columns)
     : Type(name, kVector_Kind, componentType, columns) {}
 
     // Create a vector or array type.
-    Type(SkString name, Kind kind, const Type& componentType, int columns)
+    Type(String name, Kind kind, const Type& componentType, int columns)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kind)
     , fComponentType(&componentType)
@@ -100,7 +100,7 @@
     , fDimensions(SpvDim1D) {}
 
     // Create a matrix type.
-    Type(SkString name, const Type& componentType, int columns, int rows)
+    Type(String name, const Type& componentType, int columns, int rows)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kMatrix_Kind)
     , fComponentType(&componentType)
@@ -109,7 +109,7 @@
     , fDimensions(SpvDim1D) {}
 
     // Create a sampler type.
-    Type(SkString name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled,
+    Type(String name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled,
          bool isSampled)
     : INHERITED(Position(), kType_Kind, std::move(name))
     , fTypeKind(kSampler_Kind)
@@ -119,11 +119,11 @@
     , fIsMultisampled(isMultisampled)
     , fIsSampled(isSampled) {}
 
-    SkString name() const {
+    String name() const {
         return fName;
     }
 
-    SkString description() const override {
+    String description() const override {
         return fName;
     }
 
diff --git a/src/sksl/ir/SkSLTypeReference.h b/src/sksl/ir/SkSLTypeReference.h
index 1c6f16c..eae1989 100644
--- a/src/sksl/ir/SkSLTypeReference.h
+++ b/src/sksl/ir/SkSLTypeReference.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_TYPEREFERENCE
 #define SKSL_TYPEREFERENCE
 
@@ -14,7 +14,7 @@
 namespace SkSL {
 
 /**
- * Represents an identifier referring to a type. This is an intermediate value: TypeReferences are 
+ * Represents an identifier referring to a type. This is an intermediate value: TypeReferences are
  * always eventually replaced by Constructors in valid programs.
  */
 struct TypeReference : public Expression {
@@ -22,7 +22,11 @@
     : INHERITED(position, kTypeReference_Kind, *context.fInvalid_Type)
     , fValue(type) {}
 
-    SkString description() const override {
+    bool hasSideEffects() const override {
+        return false;
+    }
+
+    String description() const override {
         return fValue.name();
     }
 
diff --git a/src/sksl/ir/SkSLUnresolvedFunction.h b/src/sksl/ir/SkSLUnresolvedFunction.h
index 76741cf..b222bc3 100644
--- a/src/sksl/ir/SkSLUnresolvedFunction.h
+++ b/src/sksl/ir/SkSLUnresolvedFunction.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_UNRESOLVEDFUNCTION
 #define SKSL_UNRESOLVEDFUNCTION
 
@@ -26,7 +26,7 @@
 #endif
     }
 
-    virtual SkString description() const override {
+    String description() const override {
         return fName;
     }
 
diff --git a/src/sksl/ir/SkSLVarDeclarations.h b/src/sksl/ir/SkSLVarDeclarations.h
index 5781230..c07fee8 100644
--- a/src/sksl/ir/SkSLVarDeclarations.h
+++ b/src/sksl/ir/SkSLVarDeclarations.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_VARDECLARATIONS
 #define SKSL_VARDECLARATIONS
 
@@ -17,19 +17,20 @@
 
 /**
  * A single variable declaration within a var declaration statement. For instance, the statement
- * 'int x = 2, y[3];' is a VarDeclarations statement containing two individual VarDeclaration 
+ * 'int x = 2, y[3];' is a VarDeclarations statement containing two individual VarDeclaration
  * instances.
  */
-struct VarDeclaration {
+struct VarDeclaration : public Statement {
     VarDeclaration(const Variable* var,
                    std::vector<std::unique_ptr<Expression>> sizes,
                    std::unique_ptr<Expression> value)
-    : fVar(var)
+    : INHERITED(var->fPosition, Statement::kVarDeclaration_Kind)
+    , fVar(var)
     , fSizes(std::move(sizes))
     , fValue(std::move(value)) {}
 
-    SkString description() const {
-        SkString result = fVar->fName;
+    String description() const {
+        String result = fVar->fName;
         for (const auto& size : fSizes) {
             if (size) {
                 result += "[" + size->description() + "]";
@@ -40,12 +41,14 @@
         if (fValue) {
             result += " = " + fValue->description();
         }
-        return result;        
+        return result;
     }
 
     const Variable* fVar;
     std::vector<std::unique_ptr<Expression>> fSizes;
     std::unique_ptr<Expression> fValue;
+
+    typedef Statement INHERITED;
 };
 
 /**
@@ -53,27 +56,33 @@
  */
 struct VarDeclarations : public ProgramElement {
     VarDeclarations(Position position, const Type* baseType, 
-                    std::vector<VarDeclaration> vars)
+                    std::vector<std::unique_ptr<VarDeclaration>> vars)
     : INHERITED(position, kVar_Kind)
-    , fBaseType(*baseType)
-    , fVars(std::move(vars)) {}
-
-    SkString description() const override {
-        if (!fVars.size()) {
-            return SkString();
+    , fBaseType(*baseType) {
+        for (auto& var : vars) {
+            fVars.push_back(std::unique_ptr<Statement>(var.release()));
         }
-        SkString result = fVars[0].fVar->fModifiers.description() + fBaseType.description() + " ";
-        SkString separator;
+    }
+
+    String description() const override {
+        if (!fVars.size()) {
+            return String();
+        }
+        String result = ((VarDeclaration&) *fVars[0]).fVar->fModifiers.description() +
+                fBaseType.description() + " ";
+        String separator;
         for (const auto& var : fVars) {
             result += separator;
             separator = ", ";
-            result += var.description();
+            result += var->description();
         }
         return result;
     }
 
     const Type& fBaseType;
-    std::vector<VarDeclaration> fVars;
+    // this *should* be a vector of unique_ptr<VarDeclaration>, but it significantly simplifies the
+    // CFG to only have to worry about unique_ptr<Statement>
+    std::vector<std::unique_ptr<Statement>> fVars;
 
     typedef ProgramElement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLVarDeclarationsStatement.h b/src/sksl/ir/SkSLVarDeclarationsStatement.h
index 66b570f..ab67536 100644
--- a/src/sksl/ir/SkSLVarDeclarationsStatement.h
+++ b/src/sksl/ir/SkSLVarDeclarationsStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_VARDECLARATIONSSTATEMENT
 #define SKSL_VARDECLARATIONSSTATEMENT
 
@@ -21,7 +21,16 @@
     : INHERITED(decl->fPosition, kVarDeclarations_Kind)
     , fDeclaration(std::move(decl)) {}
 
-    SkString description() const override {
+    bool isEmpty() const override {
+        for (const auto& s : fDeclaration->fVars) {
+            if (!s->isEmpty()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    String description() const override {
         return fDeclaration->description();
     }
 
diff --git a/src/sksl/ir/SkSLVariable.h b/src/sksl/ir/SkSLVariable.h
index 2c3391d..05bba20 100644
--- a/src/sksl/ir/SkSLVariable.h
+++ b/src/sksl/ir/SkSLVariable.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_VARIABLE
 #define SKSL_VARIABLE
 
@@ -27,7 +27,7 @@
         kParameter_Storage
     };
 
-    Variable(Position position, Modifiers modifiers, SkString name, const Type& type,
+    Variable(Position position, Modifiers modifiers, String name, const Type& type,
              Storage storage)
     : INHERITED(position, kVariable_Kind, std::move(name))
     , fModifiers(modifiers)
@@ -36,10 +36,14 @@
     , fReadCount(0)
     , fWriteCount(0) {}
 
-    virtual SkString description() const override {
+    virtual String description() const override {
         return fModifiers.description() + fType.fName + " " + fName;
     }
 
+    bool dead() const {
+        return !fWriteCount || (!fReadCount && !(fModifiers.fFlags & Modifiers::kOut_Flag));
+    }
+
     mutable Modifiers fModifiers;
     const Type& fType;
     const Storage fStorage;
diff --git a/src/sksl/ir/SkSLVariableReference.h b/src/sksl/ir/SkSLVariableReference.h
index 7d90511..92aef94 100644
--- a/src/sksl/ir/SkSLVariableReference.h
+++ b/src/sksl/ir/SkSLVariableReference.h
@@ -4,10 +4,12 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_VARIABLEREFERENCE
 #define SKSL_VARIABLEREFERENCE
 
+#include "SkSLBoolLiteral.h"
+#include "SkSLConstructor.h"
 #include "SkSLExpression.h"
 #include "SkSLFloatLiteral.h"
 #include "SkSLIRGenerator.h"
@@ -41,7 +43,7 @@
         }
     }
 
-    virtual ~VariableReference() override {
+    ~VariableReference() override {
         if (fRefKind != kWrite_RefKind) {
             fVariable.fReadCount--;
         }
@@ -67,39 +69,63 @@
         fRefKind = refKind;
     }
 
-    SkString description() const override {
+    bool hasSideEffects() const override {
+        return false;
+    }
+
+    String description() const override {
         return fVariable.fName;
     }
 
-    virtual std::unique_ptr<Expression> constantPropagate(
-                                                        const IRGenerator& irGenerator,
-                                                        const DefinitionMap& definitions) override {
-        auto exprIter = definitions.find(&fVariable);
-        if (exprIter != definitions.end() && exprIter->second) {
-            const Expression* expr = exprIter->second->get();
-            switch (expr->fKind) {
-                case Expression::kIntLiteral_Kind:
-                    return std::unique_ptr<Expression>(new IntLiteral(
-                                                                     irGenerator.fContext,
-                                                                     Position(),
-                                                                     ((IntLiteral*) expr)->fValue));
-                case Expression::kFloatLiteral_Kind:
-                    return std::unique_ptr<Expression>(new FloatLiteral(
-                                                                   irGenerator.fContext,
+    static std::unique_ptr<Expression> copy_constant(const IRGenerator& irGenerator,
+                                                     const Expression* expr) {
+        ASSERT(expr->isConstant());
+        switch (expr->fKind) {
+            case Expression::kIntLiteral_Kind:
+                return std::unique_ptr<Expression>(new IntLiteral(
+                                                                 irGenerator.fContext,
+                                                                 Position(),
+                                                                 ((IntLiteral*) expr)->fValue));
+            case Expression::kFloatLiteral_Kind:
+                return std::unique_ptr<Expression>(new FloatLiteral(
+                                                               irGenerator.fContext,
+                                                               Position(),
+                                                               ((FloatLiteral*) expr)->fValue));
+            case Expression::kBoolLiteral_Kind:
+                return std::unique_ptr<Expression>(new BoolLiteral(irGenerator.fContext,
                                                                    Position(),
-                                                                   ((FloatLiteral*) expr)->fValue));
-                default:
-                    break;
+                                                                   ((BoolLiteral*) expr)->fValue));
+            case Expression::kConstructor_Kind: {
+                const Constructor* c = (const Constructor*) expr;
+                std::vector<std::unique_ptr<Expression>> args;
+                for (const auto& arg : c->fArguments) {
+                    args.push_back(copy_constant(irGenerator, arg.get()));
+                }
+                return std::unique_ptr<Expression>(new Constructor(Position(), c->fType,
+                                                                   std::move(args)));
             }
+            default:
+                ABORT("unsupported constant\n");
+        }
+    }
+
+    std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
+                                                  const DefinitionMap& definitions) override {
+        if (fRefKind != kRead_RefKind) {
+            return nullptr;
+        }
+        auto exprIter = definitions.find(&fVariable);
+        if (exprIter != definitions.end() && exprIter->second &&
+            (*exprIter->second)->isConstant()) {
+            return copy_constant(irGenerator, exprIter->second->get());
         }
         return nullptr;
     }
 
     const Variable& fVariable;
-
-private:
     RefKind fRefKind;
 
+private:
     typedef Expression INHERITED;
 };
 
diff --git a/src/sksl/ir/SkSLWhileStatement.h b/src/sksl/ir/SkSLWhileStatement.h
index a741a04..6df1619 100644
--- a/src/sksl/ir/SkSLWhileStatement.h
+++ b/src/sksl/ir/SkSLWhileStatement.h
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #ifndef SKSL_WHILESTATEMENT
 #define SKSL_WHILESTATEMENT
 
@@ -17,18 +17,18 @@
  * A 'while' loop.
  */
 struct WhileStatement : public Statement {
-    WhileStatement(Position position, std::unique_ptr<Expression> test, 
+    WhileStatement(Position position, std::unique_ptr<Expression> test,
                    std::unique_ptr<Statement> statement)
     : INHERITED(position, kWhile_Kind)
     , fTest(std::move(test))
     , fStatement(std::move(statement)) {}
 
-    SkString description() const override {
+    String description() const override {
         return "while (" + fTest->description() + ") " + fStatement->description();
     }
 
     std::unique_ptr<Expression> fTest;
-    const std::unique_ptr<Statement> fStatement;
+    std::unique_ptr<Statement> fStatement;
 
     typedef Statement INHERITED;
 };
diff --git a/src/sksl/lex.layout.c b/src/sksl/lex.layout.c
index ef5b312..42db0fe 100644
--- a/src/sksl/lex.layout.c
+++ b/src/sksl/lex.layout.c
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 
-#line 3 "lex.layout.c"
+#line 2 "lex.layout.c"
 
 #define  YY_INT_ALIGNED short int
 
@@ -13,12 +13,82 @@
 
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 35
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 3
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
 
+    #define yy_create_buffer layout_create_buffer
+
+    #define yy_delete_buffer layout_delete_buffer
+
+    #define yy_scan_buffer layout_scan_buffer
+
+    #define yy_scan_string layout_scan_string
+
+    #define yy_scan_bytes layout_scan_bytes
+
+    #define yy_init_buffer layout_init_buffer
+
+    #define yy_flush_buffer layout_flush_buffer
+
+    #define yy_load_buffer_state layout_load_buffer_state
+
+    #define yy_switch_to_buffer layout_switch_to_buffer
+
+    #define yypush_buffer_state layoutpush_buffer_state
+
+    #define yypop_buffer_state layoutpop_buffer_state
+
+    #define yyensure_buffer_stack layoutensure_buffer_stack
+
+    #define yylex layoutlex
+
+    #define yyrestart layoutrestart
+
+    #define yylex_init layoutlex_init
+
+    #define yylex_init_extra layoutlex_init_extra
+
+    #define yylex_destroy layoutlex_destroy
+
+    #define yyget_debug layoutget_debug
+
+    #define yyset_debug layoutset_debug
+
+    #define yyget_extra layoutget_extra
+
+    #define yyset_extra layoutset_extra
+
+    #define yyget_in layoutget_in
+
+    #define yyset_in layoutset_in
+
+    #define yyget_out layoutget_out
+
+    #define yyset_out layoutset_out
+
+    #define yyget_leng layoutget_leng
+
+    #define yyget_text layoutget_text
+
+    #define yyget_lineno layoutget_lineno
+
+    #define yyset_lineno layoutset_lineno
+
+        #define yyget_column layoutget_column
+
+        #define yyset_column layoutset_column
+
+    #define yywrap layoutwrap
+
+    #define yyalloc layoutalloc
+
+    #define yyrealloc layoutrealloc
+
+    #define yyfree layoutfree
+
 /* First, we deal with  platform-specific or compiler-specific issues. */
 
 /* begin standard C headers. */
@@ -52,7 +122,6 @@
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
-typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -60,7 +129,6 @@
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
-#endif /* ! C99 */
 
 /* Limits of integral types. */
 #ifndef INT8_MIN
@@ -91,38 +159,26 @@
 #define UINT32_MAX             (4294967295U)
 #endif
 
+#endif /* ! C99 */
+
 #endif /* ! FLEXINT_H */
 
-#ifdef __cplusplus
-
-/* The "const" storage-class-modifier is valid. */
-#define YY_USE_CONST
-
-#else	/* ! __cplusplus */
-
-/* C99 requires __STDC__ to be defined as 1. */
-#if defined (__STDC__)
-
-#define YY_USE_CONST
-
-#endif	/* defined (__STDC__) */
-#endif	/* ! __cplusplus */
-
-#ifdef YY_USE_CONST
+/* TODO: this is always defined, so inline it */
 #define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
 #else
-#define yyconst
+#define yynoreturn
 #endif
 
 /* Returned upon end-of-file. */
 #define YY_NULL 0
 
-/* Promotes a possibly negative, possibly signed char to an unsigned
- * integer for use as an array index.  If the signed char is negative,
- * we want to instead treat it as an 8-bit unsigned char, hence the
- * double cast.
+/* Promotes a possibly negative, possibly signed char to an
+ *   integer in range [0..255] for use as an array index.
  */
-#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
 
 /* An opaque pointer. */
 #ifndef YY_TYPEDEF_YY_SCANNER_T
@@ -146,25 +202,29 @@
  * definition of BEGIN.
  */
 #define BEGIN yyg->yy_start = 1 + 2 *
-
 /* Translate the current start state into a value that can be later handed
  * to BEGIN to return to the state.  The YYSTATE alias is for lex
  * compatibility.
  */
 #define YY_START ((yyg->yy_start - 1) / 2)
 #define YYSTATE YY_START
-
 /* Action number for EOF rule of a given start state. */
 #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
-
 /* Special action meaning "start processing a new file". */
 #define YY_NEW_FILE layoutrestart(yyin ,yyscanner )
-
 #define YY_END_OF_BUFFER_CHAR 0
 
 /* Size of default input buffer. */
 #ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
 #define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
 #endif
 
 /* The state buf must be large enough to hold one state per character in the main buffer.
@@ -184,21 +244,28 @@
 #define EOB_ACT_CONTINUE_SCAN 0
 #define EOB_ACT_END_OF_FILE 1
 #define EOB_ACT_LAST_MATCH 2
-
+    
     /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
      *       access to the local variable yy_act. Since yyless() is a macro, it would break
-     *       existing scanners that call yyless() from OUTSIDE layoutlex. 
+     *       existing scanners that call yyless() from OUTSIDE layoutlex.
      *       One obvious solution it to make yy_act a global. I tried that, and saw
      *       a 5% performance hit in a non-yylineno scanner, because yy_act is
      *       normally declared as a register variable-- so it is not worth it.
      */
     #define  YY_LESS_LINENO(n) \
             do { \
-                yy_size_t yyl;\
+                int yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
             }while(0)
+    #define YY_LINENO_REWIND_TO(dst) \
+            do {\
+                const char *p;\
+                for ( p = yy_cp-1; p >= (dst); --p)\
+                    if ( *p == '\n' )\
+                        --yylineno;\
+            }while(0)
     
 /* Return all but the first "n" matched characters back to the input stream. */
 #define yyless(n) \
@@ -213,7 +280,6 @@
 		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
 		} \
 	while ( 0 )
-
 #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
 
 #ifndef YY_STRUCT_YY_BUFFER_STATE
@@ -228,12 +294,12 @@
 	/* Size of input buffer in bytes, not including room for EOB
 	 * characters.
 	 */
-	yy_size_t yy_buf_size;
+	int yy_buf_size;
 
 	/* Number of characters read into yy_ch_buf, not including EOB
 	 * characters.
 	 */
-	yy_size_t yy_n_chars;
+	int yy_n_chars;
 
 	/* Whether we "own" the buffer - i.e., we know we created it,
 	 * and can realloc() it to grow it, and should free() it to
@@ -256,7 +322,7 @@
 
     int yy_bs_lineno; /**< The line count. */
     int yy_bs_column; /**< The column count. */
-    
+
 	/* Whether to try to fill the input buffer when we reach the
 	 * end of it.
 	 */
@@ -290,36 +356,33 @@
 #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
                           ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
                           : NULL)
-
 /* Same as previous macro, but useful when we know that the buffer stack is not
  * NULL or when we need an lvalue. For internal use only.
  */
 #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
 
-void layoutrestart (FILE *input_file ,yyscan_t yyscanner );
-void layout_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
-YY_BUFFER_STATE layout_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
-void layout_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
-void layout_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
-void layoutpush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
-void layoutpop_buffer_state (yyscan_t yyscanner );
+void layoutrestart ( FILE *input_file , yyscan_t yyscanner );
+void layout_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE layout_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void layout_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void layout_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void layoutpush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void layoutpop_buffer_state ( yyscan_t yyscanner );
 
-static void layoutensure_buffer_stack (yyscan_t yyscanner );
-static void layout_load_buffer_state (yyscan_t yyscanner );
-static void layout_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
-
+static void layoutensure_buffer_stack ( yyscan_t yyscanner );
+static void layout_load_buffer_state ( yyscan_t yyscanner );
+static void layout_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner );
 #define YY_FLUSH_BUFFER layout_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
 
-YY_BUFFER_STATE layout_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
-YY_BUFFER_STATE layout_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
-YY_BUFFER_STATE layout_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+YY_BUFFER_STATE layout_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE layout_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE layout_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
 
-void *layoutalloc (yy_size_t ,yyscan_t yyscanner );
-void *layoutrealloc (void *,yy_size_t ,yyscan_t yyscanner );
-void layoutfree (void * ,yyscan_t yyscanner );
+void *layoutalloc ( yy_size_t , yyscan_t yyscanner );
+void *layoutrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void layoutfree ( void * , yyscan_t yyscanner );
 
 #define yy_new_buffer layout_create_buffer
-
 #define yy_set_interactive(is_interactive) \
 	{ \
 	if ( ! YY_CURRENT_BUFFER ){ \
@@ -329,7 +392,6 @@
 	} \
 	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
 	}
-
 #define yy_set_bol(at_bol) \
 	{ \
 	if ( ! YY_CURRENT_BUFFER ){\
@@ -339,32 +401,29 @@
 	} \
 	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
 	}
-
 #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
 
 /* Begin user sect3 */
-
-typedef unsigned char YY_CHAR;
+typedef flex_uint8_t YY_CHAR;
 
 typedef int yy_state_type;
 
 #define yytext_ptr yytext_r
 
-static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
-static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
-static int yy_get_next_buffer (yyscan_t yyscanner );
-static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+static yy_state_type yy_get_previous_state ( yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state  , yyscan_t yyscanner);
+static int yy_get_next_buffer ( yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
 
 /* Done after the current pattern has been matched and before the
  * corresponding action - sets up yytext.
  */
 #define YY_DO_BEFORE_ACTION \
 	yyg->yytext_ptr = yy_bp; \
-	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyleng = (int) (yy_cp - yy_bp); \
 	yyg->yy_hold_char = *yy_cp; \
 	*yy_cp = '\0'; \
 	yyg->yy_c_buf_p = yy_cp;
-
 #define YY_NUM_RULES 22
 #define YY_END_OF_BUFFER 23
 /* This struct is not used in this scanner,
@@ -374,7 +433,7 @@
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[204] =
+static const flex_int16_t yy_accept[204] =
     {   0,
         0,    0,   23,   21,   22,   21,   21,   21,   21,   21,
        21,   21,   21,    0,    0,    0,    0,    0,    0,    0,
@@ -401,7 +460,7 @@
         0,   10,    0
     } ;
 
-static yyconst flex_int32_t yy_ec[256] =
+static const YY_CHAR yy_ec[256] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    2,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -433,14 +492,14 @@
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[27] =
+static const YY_CHAR yy_meta[27] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int16_t yy_base[205] =
+static const flex_int16_t yy_base[205] =
     {   0,
         0,   21,  218,  219,  219,   17,  201,    7,  212,   21,
        11,  207,  194,  197,  204,  199,   30,  194,  203,  183,
@@ -467,7 +526,7 @@
        11,  219,  219,    0
     } ;
 
-static yyconst flex_int16_t yy_def[205] =
+static const flex_int16_t yy_def[205] =
     {   0,
       204,  204,  203,  203,  203,  203,  203,  203,  203,  203,
       203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
@@ -494,7 +553,7 @@
       203,  203,    0,  203
     } ;
 
-static yyconst flex_int16_t yy_nxt[246] =
+static const flex_int16_t yy_nxt[246] =
     {   0,
         4,    5,  203,  203,    6,  203,   65,  203,  123,  203,
       203,    7,  203,    8,    9,  203,   10,   11,   18,  203,
@@ -525,7 +584,7 @@
       203,  203,  203,  203,  203
     } ;
 
-static yyconst flex_int16_t yy_chk[246] =
+static const flex_int16_t yy_chk[246] =
     {   0,
       204,    1,    0,    0,    1,    0,   50,    0,  113,    0,
         0,    1,    0,    1,    1,    0,    1,    1,    8,    0,
@@ -557,7 +616,7 @@
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[23] =
+static const flex_int32_t yy_rule_can_match_eol[23] =
     {   0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0,     };
@@ -592,7 +651,8 @@
 #define YY_NO_UNISTD_H 1
 #line 29 "layout.flex"
 #include "SkSLToken.h"
-#line 590 "lex.layout.c"
+#line 648 "lex.layout.c"
+#line 649 "lex.layout.c"
 
 #define INITIAL 0
 
@@ -621,8 +681,8 @@
     size_t yy_buffer_stack_max; /**< capacity of stack. */
     YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
     char yy_hold_char;
-    yy_size_t yy_n_chars;
-    yy_size_t yyleng_r;
+    int yy_n_chars;
+    int yyleng_r;
     char *yy_c_buf_p;
     int yy_init;
     int yy_start;
@@ -642,40 +702,44 @@
 
     }; /* end struct yyguts_t */
 
-static int yy_init_globals (yyscan_t yyscanner );
+static int yy_init_globals ( yyscan_t yyscanner );
 
 int layoutlex_init (yyscan_t* scanner);
 
-int layoutlex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+int layoutlex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
 
 /* Accessor methods to globals.
    These are made visible to non-reentrant scanners for convenience. */
 
-int layoutlex_destroy (yyscan_t yyscanner );
+int layoutlex_destroy ( yyscan_t yyscanner );
 
-int layoutget_debug (yyscan_t yyscanner );
+int layoutget_debug ( yyscan_t yyscanner );
 
-void layoutset_debug (int debug_flag ,yyscan_t yyscanner );
+void layoutset_debug ( int debug_flag , yyscan_t yyscanner );
 
-YY_EXTRA_TYPE layoutget_extra (yyscan_t yyscanner );
+YY_EXTRA_TYPE layoutget_extra ( yyscan_t yyscanner );
 
-void layoutset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+void layoutset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
 
-FILE *layoutget_in (yyscan_t yyscanner );
+FILE *layoutget_in ( yyscan_t yyscanner );
 
-void layoutset_in  (FILE * in_str ,yyscan_t yyscanner );
+void layoutset_in  ( FILE * _in_str , yyscan_t yyscanner );
 
-FILE *layoutget_out (yyscan_t yyscanner );
+FILE *layoutget_out ( yyscan_t yyscanner );
 
-void layoutset_out  (FILE * out_str ,yyscan_t yyscanner );
+void layoutset_out  ( FILE * _out_str , yyscan_t yyscanner );
 
-yy_size_t layoutget_leng (yyscan_t yyscanner );
+			int layoutget_leng ( yyscan_t yyscanner );
 
-char *layoutget_text (yyscan_t yyscanner );
+char *layoutget_text ( yyscan_t yyscanner );
 
-int layoutget_lineno (yyscan_t yyscanner );
+int layoutget_lineno ( yyscan_t yyscanner );
 
-void layoutset_lineno (int line_number ,yyscan_t yyscanner );
+void layoutset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int layoutget_column  ( yyscan_t yyscanner );
+
+void layoutset_column ( int _column_no , yyscan_t yyscanner );
 
 /* Macros after this point can all be overridden by user definitions in
  * section 1.
@@ -683,35 +747,43 @@
 
 #ifndef YY_SKIP_YYWRAP
 #ifdef __cplusplus
-extern "C" int layoutwrap (yyscan_t yyscanner );
+extern "C" int layoutwrap ( yyscan_t yyscanner );
 #else
-extern int layoutwrap (yyscan_t yyscanner );
+extern int layoutwrap ( yyscan_t yyscanner );
 #endif
 #endif
 
-    static void yyunput (int c,char *buf_ptr  ,yyscan_t yyscanner);
+#ifndef YY_NO_UNPUT
     
+    static void yyunput ( int c, char *buf_ptr  , yyscan_t yyscanner);
+    
+#endif
+
 #ifndef yytext_ptr
-static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
 #endif
 
 #ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
 #endif
 
 #ifndef YY_NO_INPUT
-
 #ifdef __cplusplus
-static int yyinput (yyscan_t yyscanner );
+static int yyinput ( yyscan_t yyscanner );
 #else
-static int input (yyscan_t yyscanner );
+static int input ( yyscan_t yyscanner );
 #endif
 
 #endif
 
 /* Amount of stuff to slurp up with each read. */
 #ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
 #define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
 #endif
 
 /* Copy whatever the last rule matched to the standard output. */
@@ -719,7 +791,7 @@
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  */
-#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
 #endif
 
 /* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
@@ -730,7 +802,7 @@
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		yy_size_t n; \
+		int n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -743,7 +815,7 @@
 	else \
 		{ \
 		errno=0; \
-		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+		while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
 			{ \
 			if( errno != EINTR) \
 				{ \
@@ -798,7 +870,7 @@
 
 /* Code executed at the end of each rule. */
 #ifndef YY_BREAK
-#define YY_BREAK break;
+#define YY_BREAK /*LINTED*/break;
 #endif
 
 #define YY_RULE_SETUP \
@@ -808,16 +880,11 @@
  */
 YY_DECL
 {
-	register yy_state_type yy_current_state;
-	register char *yy_cp, *yy_bp;
-	register int yy_act;
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
-#line 32 "layout.flex"
-
-
-#line 814 "lex.layout.c"
-
 	if ( !yyg->yy_init )
 		{
 		yyg->yy_init = 1;
@@ -844,7 +911,13 @@
 		layout_load_buffer_state(yyscanner );
 		}
 
-	while ( 1 )		/* loops until end-of-file is reached */
+	{
+#line 32 "layout.flex"
+
+
+#line 912 "lex.layout.c"
+
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
 		yy_cp = yyg->yy_c_buf_p;
 
@@ -860,7 +933,7 @@
 yy_match:
 		do
 			{
-			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
 			if ( yy_accept[yy_current_state] )
 				{
 				yyg->yy_last_accepting_state = yy_current_state;
@@ -870,9 +943,9 @@
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
 				if ( yy_current_state >= 204 )
-					yy_c = yy_meta[(unsigned int) yy_c];
+					yy_c = yy_meta[yy_c];
 				}
-			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
 			++yy_cp;
 			}
 		while ( yy_current_state != 203 );
@@ -886,10 +959,10 @@
 
 		if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
 			{
-			yy_size_t yyl;
+			int yyl;
 			for ( yyl = 0; yyl < yyleng; ++yyl )
 				if ( yytext[yyl] == '\n' )
-					   
+					
     do{ yylineno++;
         yycolumn=0;
     }while(0)
@@ -1017,7 +1090,7 @@
 #line 57 "layout.flex"
 ECHO;
 	YY_BREAK
-#line 1015 "lex.layout.c"
+#line 1087 "lex.layout.c"
 case YY_STATE_EOF(INITIAL):
 	yyterminate();
 
@@ -1149,6 +1222,7 @@
 			"fatal flex scanner internal error--no action found" );
 	} /* end of action switch */
 		} /* end of scanning one token */
+	} /* end of user's declarations */
 } /* end of layoutlex */
 
 /* yy_get_next_buffer - try to read in a new buffer
@@ -1161,9 +1235,9 @@
 static int yy_get_next_buffer (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
-	register char *source = yyg->yytext_ptr;
-	register int number_to_move, i;
+	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = yyg->yytext_ptr;
+	int number_to_move, i;
 	int ret_val;
 
 	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
@@ -1192,7 +1266,7 @@
 	/* Try to read more data. */
 
 	/* First move last chars to start of buffer. */
-	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1);
 
 	for ( i = 0; i < number_to_move; ++i )
 		*(dest++) = *(source++);
@@ -1205,21 +1279,21 @@
 
 	else
 		{
-			yy_size_t num_to_read =
+			int num_to_read =
 			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
 
 		while ( num_to_read <= 0 )
 			{ /* Not enough room in the buffer - grow it. */
 
 			/* just a shorter name for the current buffer */
-			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
 
 			int yy_c_buf_p_offset =
 				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
 
 			if ( b->yy_is_our_buffer )
 				{
-				yy_size_t new_size = b->yy_buf_size * 2;
+				int new_size = b->yy_buf_size * 2;
 
 				if ( new_size <= 0 )
 					b->yy_buf_size += b->yy_buf_size / 8;
@@ -1228,11 +1302,11 @@
 
 				b->yy_ch_buf = (char *)
 					/* Include room in for 2 EOB chars. */
-					layoutrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+					layoutrealloc((void *) b->yy_ch_buf,(yy_size_t) (b->yy_buf_size + 2) ,yyscanner );
 				}
 			else
 				/* Can't grow it, we don't own it. */
-				b->yy_ch_buf = 0;
+				b->yy_ch_buf = NULL;
 
 			if ( ! b->yy_ch_buf )
 				YY_FATAL_ERROR(
@@ -1274,10 +1348,10 @@
 	else
 		ret_val = EOB_ACT_CONTINUE_SCAN;
 
-	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+	if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
 		/* Extend the array by 50%, plus the number we really need. */
-		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
-		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) layoutrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) layoutrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,(yy_size_t) new_size ,yyscanner );
 		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
 			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
 	}
@@ -1295,15 +1369,15 @@
 
     static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
 {
-	register yy_state_type yy_current_state;
-	register char *yy_cp;
+	yy_state_type yy_current_state;
+	char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
 	yy_current_state = yyg->yy_start;
 
 	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
 		{
-		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
 		if ( yy_accept[yy_current_state] )
 			{
 			yyg->yy_last_accepting_state = yy_current_state;
@@ -1313,9 +1387,9 @@
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
 			if ( yy_current_state >= 204 )
-				yy_c = yy_meta[(unsigned int) yy_c];
+				yy_c = yy_meta[yy_c];
 			}
-		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
 		}
 
 	return yy_current_state;
@@ -1328,11 +1402,11 @@
  */
     static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
 {
-	register int yy_is_jam;
+	int yy_is_jam;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
-	register char *yy_cp = yyg->yy_c_buf_p;
+	char *yy_cp = yyg->yy_c_buf_p;
 
-	register YY_CHAR yy_c = 1;
+	YY_CHAR yy_c = 1;
 	if ( yy_accept[yy_current_state] )
 		{
 		yyg->yy_last_accepting_state = yy_current_state;
@@ -1342,17 +1416,20 @@
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
 		if ( yy_current_state >= 204 )
-			yy_c = yy_meta[(unsigned int) yy_c];
+			yy_c = yy_meta[yy_c];
 		}
-	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
 	yy_is_jam = (yy_current_state == 203);
 
+	(void)yyg;
 	return yy_is_jam ? 0 : yy_current_state;
 }
 
-    static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+#ifndef YY_NO_UNPUT
+
+    static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
 {
-	register char *yy_cp;
+	char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
     yy_cp = yyg->yy_c_buf_p;
@@ -1363,10 +1440,10 @@
 	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
 		{ /* need to shift things up to make room */
 		/* +2 for EOB chars. */
-		register yy_size_t number_to_move = yyg->yy_n_chars + 2;
-		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+		int number_to_move = yyg->yy_n_chars + 2;
+		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
 					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
-		register char *source =
+		char *source =
 				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
 
 		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
@@ -1375,7 +1452,7 @@
 		yy_cp += (int) (dest - source);
 		yy_bp += (int) (dest - source);
 		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
-			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+			yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
 
 		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
 			YY_FATAL_ERROR( "flex scanner push-back overflow" );
@@ -1392,6 +1469,8 @@
 	yyg->yy_c_buf_p = yy_cp;
 }
 
+#endif
+
 #ifndef YY_NO_INPUT
 #ifdef __cplusplus
     static int yyinput (yyscan_t yyscanner)
@@ -1417,7 +1496,7 @@
 
 		else
 			{ /* need more input */
-			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
 			++yyg->yy_c_buf_p;
 
 			switch ( yy_get_next_buffer( yyscanner ) )
@@ -1464,7 +1543,7 @@
 	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
 
 	if ( c == '\n' )
-		   
+		
     do{ yylineno++;
         yycolumn=0;
     }while(0)
@@ -1557,7 +1636,7 @@
 	/* yy_ch_buf has to be 2 characters longer than the size given because
 	 * we need to put in 2 end-of-buffer characters.
 	 */
-	b->yy_ch_buf = (char *) layoutalloc(b->yy_buf_size + 2 ,yyscanner );
+	b->yy_ch_buf = (char *) layoutalloc((yy_size_t) (b->yy_buf_size + 2) ,yyscanner );
 	if ( ! b->yy_ch_buf )
 		YY_FATAL_ERROR( "out of dynamic memory in layout_create_buffer()" );
 
@@ -1713,15 +1792,15 @@
 		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
 		 * immediate realloc on the next call.
          */
-		num_to_alloc = 1;
+      num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
 		yyg->yy_buffer_stack = (struct yy_buffer_state**)layoutalloc
 								(num_to_alloc * sizeof(struct yy_buffer_state*)
 								, yyscanner);
 		if ( ! yyg->yy_buffer_stack )
 			YY_FATAL_ERROR( "out of dynamic memory in layoutensure_buffer_stack()" );
-								  
+
 		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
-				
+
 		yyg->yy_buffer_stack_max = num_to_alloc;
 		yyg->yy_buffer_stack_top = 0;
 		return;
@@ -1730,7 +1809,7 @@
 	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
 
 		/* Increase the buffer to prepare for a possible push. */
-		int grow_size = 8 /* arbitrary grow size */;
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
 
 		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
 		yyg->yy_buffer_stack = (struct yy_buffer_state**)layoutrealloc
@@ -1750,7 +1829,7 @@
  * @param base the character buffer
  * @param size the size in bytes of the character buffer
  * @param yyscanner The scanner object.
- * @return the newly allocated buffer state object. 
+ * @return the newly allocated buffer state object.
  */
 YY_BUFFER_STATE layout_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
 {
@@ -1760,16 +1839,16 @@
 	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
 	     base[size-1] != YY_END_OF_BUFFER_CHAR )
 		/* They forgot to leave room for the EOB's. */
-		return 0;
+		return NULL;
 
 	b = (YY_BUFFER_STATE) layoutalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
 	if ( ! b )
 		YY_FATAL_ERROR( "out of dynamic memory in layout_scan_buffer()" );
 
-	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_size = (int) (size - 2);	/* "- 2" to take care of EOB's */
 	b->yy_buf_pos = b->yy_ch_buf = base;
 	b->yy_is_our_buffer = 0;
-	b->yy_input_file = 0;
+	b->yy_input_file = NULL;
 	b->yy_n_chars = b->yy_buf_size;
 	b->yy_is_interactive = 0;
 	b->yy_at_bol = 1;
@@ -1789,27 +1868,28 @@
  * @note If you want to scan bytes that may contain NUL values, then use
  *       layout_scan_bytes() instead.
  */
-YY_BUFFER_STATE layout_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+YY_BUFFER_STATE layout_scan_string (const char * yystr , yyscan_t yyscanner)
 {
     
-	return layout_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+	return layout_scan_bytes(yystr,(int) strlen(yystr) ,yyscanner);
 }
 
 /** Setup the input buffer state to scan the given bytes. The next call to layoutlex() will
  * scan from a @e copy of @a bytes.
- * @param bytes the byte buffer to scan
- * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
  * @param yyscanner The scanner object.
  * @return the newly allocated buffer state object.
  */
-YY_BUFFER_STATE layout_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+YY_BUFFER_STATE layout_scan_bytes  (const char * yybytes, int  _yybytes_len , yyscan_t yyscanner)
 {
 	YY_BUFFER_STATE b;
 	char *buf;
-	yy_size_t n, i;
+	yy_size_t n;
+	int i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
-	n = _yybytes_len + 2;
+	n = (yy_size_t) (_yybytes_len + 2);
 	buf = (char *) layoutalloc(n ,yyscanner );
 	if ( ! buf )
 		YY_FATAL_ERROR( "out of dynamic memory in layout_scan_bytes()" );
@@ -1835,9 +1915,11 @@
 #define YY_EXIT_FAILURE 2
 #endif
 
-static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner)
 {
-    	(void) fprintf( stderr, "%s\n", msg );
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	(void) fprintf( stderr, "%s\n", msg );
 	exit( YY_EXIT_FAILURE );
 }
 
@@ -1875,7 +1957,7 @@
 int layoutget_lineno  (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    
+
         if (! YY_CURRENT_BUFFER)
             return 0;
     
@@ -1888,7 +1970,7 @@
 int layoutget_column  (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    
+
         if (! YY_CURRENT_BUFFER)
             return 0;
     
@@ -1916,7 +1998,7 @@
 /** Get the length of the current token.
  * @param yyscanner The scanner object.
  */
-yy_size_t layoutget_leng  (yyscan_t yyscanner)
+int layoutget_leng  (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
     return yyleng;
@@ -1943,51 +2025,51 @@
 }
 
 /** Set the current line number.
- * @param line_number
+ * @param _line_number line number
  * @param yyscanner The scanner object.
  */
-void layoutset_lineno (int  line_number , yyscan_t yyscanner)
+void layoutset_lineno (int  _line_number , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
         /* lineno is only valid if an input buffer exists. */
         if (! YY_CURRENT_BUFFER )
-           yy_fatal_error( "layoutset_lineno called with no buffer" , yyscanner); 
+           YY_FATAL_ERROR( "layoutset_lineno called with no buffer" );
     
-    yylineno = line_number;
+    yylineno = _line_number;
 }
 
 /** Set the current column.
- * @param line_number
+ * @param _column_no column number
  * @param yyscanner The scanner object.
  */
-void layoutset_column (int  column_no , yyscan_t yyscanner)
+void layoutset_column (int  _column_no , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
         /* column is only valid if an input buffer exists. */
         if (! YY_CURRENT_BUFFER )
-           yy_fatal_error( "layoutset_column called with no buffer" , yyscanner); 
+           YY_FATAL_ERROR( "layoutset_column called with no buffer" );
     
-    yycolumn = column_no;
+    yycolumn = _column_no;
 }
 
 /** Set the input stream. This does not discard the current
  * input buffer.
- * @param in_str A readable stream.
+ * @param _in_str A readable stream.
  * @param yyscanner The scanner object.
  * @see layout_switch_to_buffer
  */
-void layoutset_in (FILE *  in_str , yyscan_t yyscanner)
+void layoutset_in (FILE *  _in_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyin = in_str ;
+    yyin = _in_str ;
 }
 
-void layoutset_out (FILE *  out_str , yyscan_t yyscanner)
+void layoutset_out (FILE *  _out_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyout = out_str ;
+    yyout = _out_str ;
 }
 
 int layoutget_debug  (yyscan_t yyscanner)
@@ -1996,10 +2078,10 @@
     return yy_flex_debug;
 }
 
-void layoutset_debug (int  bdebug , yyscan_t yyscanner)
+void layoutset_debug (int  _bdebug , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yy_flex_debug = bdebug ;
+    yy_flex_debug = _bdebug ;
 }
 
 /* Accessor methods for yylval and yylloc */
@@ -2010,9 +2092,7 @@
  * the ONLY reentrant function that doesn't take the scanner as the last argument.
  * That's why we explicitly handle the declaration, instead of using our macros.
  */
-
 int layoutlex_init(yyscan_t* ptr_yy_globals)
-
 {
     if (ptr_yy_globals == NULL){
         errno = EINVAL;
@@ -2039,9 +2119,7 @@
  * The user defined value in the first argument will be available to layoutalloc in
  * the yyextra field.
  */
-
 int layoutlex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
-
 {
     struct yyguts_t dummy_yyguts;
 
@@ -2051,20 +2129,20 @@
         errno = EINVAL;
         return 1;
     }
-	
+
     *ptr_yy_globals = (yyscan_t) layoutalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
-	
+
     if (*ptr_yy_globals == NULL){
         errno = ENOMEM;
         return 1;
     }
-    
+
     /* By setting to 0xAA, we expose bugs in
     yy_init_globals. Leave at 0x00 for releases. */
     memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
-    
+
     layoutset_extra (yy_user_defined, *ptr_yy_globals);
-    
+
     return yy_init_globals ( *ptr_yy_globals );
 }
 
@@ -2075,10 +2153,10 @@
      * This function is called from layoutlex_destroy(), so don't allocate here.
      */
 
-    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack = NULL;
     yyg->yy_buffer_stack_top = 0;
     yyg->yy_buffer_stack_max = 0;
-    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_c_buf_p = NULL;
     yyg->yy_init = 0;
     yyg->yy_start = 0;
 
@@ -2091,8 +2169,8 @@
     yyin = stdin;
     yyout = stdout;
 #else
-    yyin = (FILE *) 0;
-    yyout = (FILE *) 0;
+    yyin = NULL;
+    yyout = NULL;
 #endif
 
     /* For future reference: Set errno on error, since we are called by
@@ -2136,18 +2214,21 @@
  */
 
 #ifndef yytext_ptr
-static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner)
 {
-	register int i;
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	int i;
 	for ( i = 0; i < n; ++i )
 		s1[i] = s2[i];
 }
 #endif
 
 #ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+static int yy_flex_strlen (const char * s , yyscan_t yyscanner)
 {
-	register int n;
+	int n;
 	for ( n = 0; s[n]; ++n )
 		;
 
@@ -2157,11 +2238,16 @@
 
 void *layoutalloc (yy_size_t  size , yyscan_t yyscanner)
 {
-	return (void *) malloc( size );
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	return malloc(size);
 }
 
 void *layoutrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
 	/* The cast to (char *) in the following accommodates both
 	 * implementations that use char* generic pointers, and those
 	 * that use void* generic pointers.  It works with the latter
@@ -2169,11 +2255,13 @@
 	 * any pointer type to void*, and deal with argument conversions
 	 * as though doing an assignment.
 	 */
-	return (void *) realloc( (char *) ptr, size );
+	return realloc(ptr, size);
 }
 
 void layoutfree (void * ptr , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
 	free( (char *) ptr );	/* see layoutrealloc() for (char *) cast */
 }
 
@@ -2182,7 +2270,6 @@
 #line 57 "layout.flex"
 
 
-
 int layoutwrap(yyscan_t scanner) {
     return 1; // terminate
 }
diff --git a/src/sksl/lex.layout.cpp b/src/sksl/lex.layout.cpp
index 1d03e29..4289b11 100644
--- a/src/sksl/lex.layout.cpp
+++ b/src/sksl/lex.layout.cpp
@@ -7,3 +7,6 @@
 
 #include "disable_flex_warnings.h"
 #include "lex.layout.c"
+static_assert(YY_FLEX_MAJOR_VERSION * 100 + YY_FLEX_MINOR_VERSION * 10 +
+              YY_FLEX_SUBMINOR_VERSION >= 261,
+              "we require Flex 2.6.1 or better for security reasons");
diff --git a/src/sksl/lex.sksl.c b/src/sksl/lex.sksl.c
index 2c5eb73..ebf94ed 100644
--- a/src/sksl/lex.sksl.c
+++ b/src/sksl/lex.sksl.c
@@ -1,9 +1,3 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
 
 #line 3 "lex.sksl.c"
 
@@ -13,8 +7,8 @@
 
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 39
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 1
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -93,25 +87,13 @@
 
 #endif /* ! FLEXINT_H */
 
-#ifdef __cplusplus
-
-/* The "const" storage-class-modifier is valid. */
-#define YY_USE_CONST
-
-#else	/* ! __cplusplus */
-
-/* C99 requires __STDC__ to be defined as 1. */
-#if defined (__STDC__)
-
-#define YY_USE_CONST
-
-#endif	/* defined (__STDC__) */
-#endif	/* ! __cplusplus */
-
-#ifdef YY_USE_CONST
+/* TODO: this is always defined, so inline it */
 #define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
 #else
-#define yyconst
+#define yynoreturn
 #endif
 
 /* Returned upon end-of-file. */
@@ -243,12 +225,12 @@
 	/* Size of input buffer in bytes, not including room for EOB
 	 * characters.
 	 */
-	yy_size_t yy_buf_size;
+	int yy_buf_size;
 
 	/* Number of characters read into yy_ch_buf, not including EOB
 	 * characters.
 	 */
-	yy_size_t yy_n_chars;
+	int yy_n_chars;
 
 	/* Whether we "own" the buffer - i.e., we know we created it,
 	 * and can realloc() it to grow it, and should free() it to
@@ -327,7 +309,7 @@
 
 YY_BUFFER_STATE sksl_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
 YY_BUFFER_STATE sksl_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
-YY_BUFFER_STATE sksl_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+YY_BUFFER_STATE sksl_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
 
 void *skslalloc (yy_size_t ,yyscan_t yyscanner );
 void *skslrealloc (void *,yy_size_t ,yyscan_t yyscanner );
@@ -368,20 +350,20 @@
 static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
 static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
 static int yy_get_next_buffer (yyscan_t yyscanner );
-static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error (yyconst char* msg ,yyscan_t yyscanner );
 
 /* Done after the current pattern has been matched and before the
  * corresponding action - sets up yytext.
  */
 #define YY_DO_BEFORE_ACTION \
 	yyg->yytext_ptr = yy_bp; \
-	yyleng = (size_t) (yy_cp - yy_bp); \
+	yyleng = (int) (yy_cp - yy_bp); \
 	yyg->yy_hold_char = *yy_cp; \
 	*yy_cp = '\0'; \
 	yyg->yy_c_buf_p = yy_cp;
 
-#define YY_NUM_RULES 92
-#define YY_END_OF_BUFFER 93
+#define YY_NUM_RULES 96
+#define YY_END_OF_BUFFER 97
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -389,39 +371,42 @@
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[253] =
+static yyconst flex_int16_t yy_accept[285] =
     {   0,
-        0,    0,   93,   91,   90,   90,   64,   91,   38,   54,
-       59,   40,   41,   52,   50,   47,   51,   46,   53,    4,
-        4,   66,   87,   71,   67,   70,   65,   44,   45,   58,
-       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
-       38,   38,   38,   38,   38,   38,   38,   38,   42,   57,
-       43,   60,   90,   69,   39,   38,   78,   63,   83,   76,
-       48,   74,   49,   75,    1,    0,   88,   77,    2,    4,
-        0,    0,   55,   73,   68,   72,   56,   82,   62,   38,
-       38,   38,   38,   38,   12,   38,   38,   38,   38,   38,
-        8,   20,   38,   38,   38,   38,   38,   38,   38,   38,
+        0,    0,   97,   95,   94,   94,   68,   95,   42,   58,
+       63,   44,   45,   56,   54,   51,   55,   50,   57,    4,
+        4,   70,   91,   75,   71,   74,   69,   95,   48,   49,
+       62,   42,   42,   42,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42,   42,   42,   46,
+       61,   47,   64,   94,   73,   43,   42,   82,   67,   87,
+       80,   52,   78,   53,   79,    1,    0,   92,   81,    2,
+        4,    0,    0,   59,   77,   72,   76,   60,    0,    0,
+       86,   66,   42,   42,   42,   42,   42,   42,   13,   42,
+       42,   42,   42,   42,    8,   22,   42,   42,   42,   42,
 
-       38,   38,   38,   38,   38,   38,   81,   61,   39,   86,
-        0,    0,    0,   88,    1,    0,    0,    3,    5,   79,
-       80,   85,   38,   38,   38,   38,   38,   38,   38,   38,
-       38,   10,   38,   38,   38,   38,   38,   38,   21,   38,
-       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
-       84,    0,    1,   89,    0,    0,    2,   38,   14,   38,
-       38,   38,   38,   38,    9,   38,   28,   38,   38,   38,
-       25,   38,   38,   38,   38,   38,   38,   38,   38,    6,
-       38,   38,   38,   38,    0,    1,   16,   38,   24,   38,
-       38,   38,    7,   27,   22,   38,   38,   38,   38,   38,
+       42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
+       42,   85,   65,   43,   90,    0,    0,    0,   92,    1,
+        0,    0,    3,    5,   83,   84,    9,    0,   89,   42,
+       42,   42,   42,   42,   42,   42,   42,   42,   42,   11,
+       42,   42,   42,   42,   42,   42,   23,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42,   42,   42,   88,
+        0,    1,   93,    0,    0,    2,    0,   42,   42,   16,
+       42,   42,   42,   42,   42,   10,   42,   30,   42,   42,
+       42,   27,   42,   42,   42,   42,   42,   42,   42,   42,
+       42,    6,   42,   42,   42,   42,    0,    1,    0,   18,
 
-       38,   38,   38,   38,   38,   38,   11,   38,   38,   38,
-       38,   38,   36,   38,   38,   38,   38,   38,   19,   35,
-       13,   38,   38,   38,   38,   38,   15,   18,   26,   38,
-       38,   38,   38,   23,   38,   38,   32,   17,   38,   38,
-       30,   34,   33,   38,   38,   37,   31,   38,   38,   38,
-       29,    0
+       42,   42,   26,   42,   42,   42,    7,   29,   24,   42,
+       42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
+       42,   12,   42,    0,   37,   42,   42,   42,   42,   40,
+       42,   42,   42,   42,   42,   21,   42,   39,   14,   42,
+       42,   42,   15,   42,   42,   17,   20,   28,   42,   42,
+       42,   42,   42,   25,   42,   42,   34,   19,   42,   42,
+       32,   36,   42,   35,   42,   42,   41,   42,   33,   42,
+       42,   42,   42,   42,   42,   31,   42,   42,   42,   42,
+       42,   42,   38,    0
     } ;
 
-static yyconst flex_int32_t yy_ec[256] =
+static yyconst YY_CHAR yy_ec[256] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
         1,    1,    2,    1,    1,    1,    1,    1,    1,    1,
@@ -429,14 +414,14 @@
         1,    2,    4,    1,    5,    6,    7,    8,    1,    9,
        10,   11,   12,   13,   14,   15,   16,   17,   18,   18,
        18,   18,   18,   18,   18,   18,   18,   19,   20,   21,
-       22,   23,   24,    1,   25,   25,   25,   25,   26,   25,
+       22,   23,   24,   25,   26,   26,   26,   26,   27,   26,
         6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
         6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
-       27,    1,   28,   29,    6,    1,   30,   31,   32,   33,
+       28,    1,   29,   30,   31,    1,   32,   33,   34,   35,
 
-       34,   35,   36,   37,   38,    6,   39,   40,   41,   42,
-       43,   44,    6,   45,   46,   47,   48,   49,   50,   51,
-       52,    6,   53,   54,   55,   56,    1,    1,    1,    1,
+       36,   37,   38,   39,   40,    6,   41,   42,   43,   44,
+       45,   46,    6,   47,   48,   49,   50,   51,   52,   53,
+       54,    6,   55,   56,   57,   58,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -453,178 +438,194 @@
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[57] =
+static yyconst YY_CHAR yy_meta[59] =
     {   0,
         1,    1,    2,    1,    1,    3,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    4,    4,    1,    1,
-        1,    1,    1,    1,    5,    5,    1,    1,    1,    5,
-        5,    5,    5,    5,    5,    3,    3,    3,    3,    3,
+        1,    1,    1,    1,    1,    5,    5,    1,    1,    1,
+        3,    5,    5,    5,    5,    5,    5,    3,    3,    3,
         3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
-        3,    3,    1,    1,    1,    1
+        3,    3,    3,    3,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int16_t yy_base[259] =
+static yyconst flex_uint16_t yy_base[291] =
     {   0,
-        0,    0,  320,  321,   55,   57,  297,    0,    0,  296,
-       53,  321,  321,  295,   50,  321,   49,   47,   57,   52,
-       59,  321,  321,   59,  294,   60,  321,  321,  321,   62,
-      270,   57,   54,  274,   59,  275,   59,   65,  278,  268,
-      262,  264,  274,   57,  262,  264,  262,   53,  321,   74,
-      321,  321,  103,  321,    0,    0,  321,  282,  321,  321,
-      321,  321,  321,  321,   92,  292,    0,  321,   95,   99,
-      118,    0,  280,  321,  321,  321,  279,  321,  278,  265,
-      252,   78,  262,  250,    0,  249,  254,  263,  247,  255,
-        0,  247,  237,  238,  254,  242,  238,  250,   92,  238,
+        0,    0,  353,  354,   57,   59,  330,    0,    0,  329,
+       55,  354,  354,  328,   52,  354,   51,   49,   59,   61,
+       65,  354,  354,   47,  327,   49,  354,   45,  354,  354,
+       64,   37,   57,   55,  306,   64,  307,   61,   58,  310,
+      300,  294,  296,  306,   58,  294,  296,  294,   65,  354,
+       86,  354,  354,  113,  354,    0,    0,  354,  316,  354,
+      354,  354,  354,  354,  354,  100,  326,    0,  354,  102,
+      107,  114,    0,  314,  354,  354,  354,  313,  297,  281,
+      354,  310,  295,  293,  281,   91,  291,  279,    0,  278,
+      283,  292,  276,  284,    0,  276,  266,  267,  283,  271,
 
-      244,  233,  242,  239,  240,  239,  321,  254,    0,  321,
-      128,  264,  258,    0,  126,  136,  106,  138,    0,  321,
-      321,  321,  243,  238,  237,  111,  240,  237,  234,  221,
-      219,    0,  228,  216,  220,  218,  223,  226,    0,  227,
-      225,  210,  208,  207,  207,  219,  217,  221,  210,  202,
-      321,  144,  146,  321,  153,  151,  155,  209,    0,  202,
-      199,  207,  196,  213,    0,  208,    0,  197,  193,  191,
-        0,  190,  192,  198,  192,  189,  188,  200,  199,    0,
-      187,  182,  194,  193,  157,  159,    0,  192,    0,  183,
-      184,  178,    0,    0,    0,  175,  180,  174,  173,  176,
+      267,  279,   91,  283,  266,  272,  261,  270,  267,  268,
+      267,  354,  284,    0,  354,  132,  294,  288,    0,  130,
+      141,  134,  143,    0,  354,  354,  354,  263,  354,  270,
+      264,  264,  263,  114,  266,  263,  260,  247,  245,    0,
+      254,  242,  246,  244,  249,  252,    0,  253,  251,  236,
+      234,  244,  232,  232,  244,  242,  246,  235,  227,  354,
+      147,  150,  354,  157,  155,  159,  226,  233,  237,    0,
+      225,  222,  230,  219,  236,    0,  231,    0,  220,  216,
+      214,    0,  213,  215,  221,  215,  212,  211,  225,  222,
+      221,    0,  209,  204,  216,  215,  161,  163,  216,    0,
 
-      179,  174,  168,  177,  168,  174,    0,  168,  168,  161,
-      161,  174,    0,  162,  161,  166,  163,  170,    0,    0,
-        0,  160,  160,  157,  146,  145,    0,    0,    0,  132,
-      116,   99,  102,    0,  113,  101,    0,    0,  105,   92,
-        0,    0,    0,   79,   80,    0,    0,   81,   62,   32,
-        0,  321,  175,  178,  181,  186,  191,  193
+      202,  212,    0,  203,  204,  198,    0,    0,    0,  195,
+      200,  194,  193,  196,  199,  194,  189,  187,  196,  187,
+      193,    0,  187,  192,    0,  186,  179,  179,  192,    0,
+      180,  179,  184,  181,  188,    0,  190,    0,    0,  177,
+      177,  174,  354,  168,  180,    0,    0,    0,  179,  169,
+      159,  163,  163,    0,  174,  167,    0,    0,  174,  163,
+        0,    0,  166,    0,  151,  155,    0,  168,    0,  157,
+      147,  119,  125,  118,  109,    0,  104,  100,   97,   87,
+       64,   63,    0,  354,  179,  182,  185,  190,  195,  197
     } ;
 
-static yyconst flex_int16_t yy_def[259] =
+static yyconst flex_int16_t yy_def[291] =
     {   0,
-      252,    1,  252,  252,  252,  252,  252,  253,  254,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  252,  252,
-      252,  252,  252,  252,  255,  254,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  256,  257,  252,  252,  252,
-      252,  258,  252,  252,  252,  252,  252,  252,  252,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      284,    1,  284,  284,  284,  284,  284,  285,  286,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  284,
+      284,  284,  284,  284,  284,  287,  286,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  288,  289,  284,  284,
+      284,  284,  290,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
 
-      254,  254,  254,  254,  254,  254,  252,  252,  255,  252,
-      252,  256,  256,  257,  252,  252,  252,  252,  258,  252,
-      252,  252,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      252,  252,  252,  252,  252,  252,  252,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  252,  252,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  284,  284,  287,  284,  284,  288,  288,  289,  284,
+      284,  284,  284,  290,  284,  284,  284,  284,  284,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  284,
+      284,  284,  284,  284,  284,  284,  284,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  284,  284,  284,  286,
 
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
-      254,    0,  252,  252,  252,  252,  252,  252
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  284,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  284,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,  286,  286,  286,  286,  286,  286,  286,
+      286,  286,  286,    0,  284,  284,  284,  284,  284,  284
     } ;
 
-static yyconst flex_int16_t yy_nxt[378] =
+static yyconst flex_uint16_t yy_nxt[413] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   15,   16,   17,   18,   19,   20,   21,   22,   23,
-       24,   25,   26,   27,    9,    9,   28,   29,   30,    9,
-       31,   32,   33,   34,   35,    9,   36,   37,    9,   38,
-       39,   40,   41,   42,   43,   44,   45,   46,   47,   48,
-        9,    9,   49,   50,   51,   52,   53,   53,   53,   53,
-       58,   61,   63,   65,   65,  251,   69,   66,   70,   70,
-       64,   62,   67,   69,   59,   70,   70,   71,   68,   73,
-       74,   76,   77,   78,   71,   71,   81,   83,   87,  105,
-       79,   84,   71,   91,   93,  107,   85,  106,   88,   82,
+       24,   25,   26,   27,   28,    9,    9,   29,   30,   31,
+        9,    9,   32,   33,   34,   35,   36,    9,   37,   38,
+        9,   39,   40,   41,   42,   43,   44,   45,   46,   47,
+       48,   49,    9,    9,   50,   51,   52,   53,   54,   54,
+       54,   54,   59,   62,   64,   66,   66,   74,   75,   67,
+       77,   78,   65,   63,   68,   70,   60,   71,   71,   70,
+       69,   71,   71,   83,   79,   81,   84,   72,   85,   97,
+       87,   72,   80,   82,   88,   91,   72,   95,  104,   89,
 
-       92,   89,   72,  100,   53,   53,  101,   94,   65,   65,
-      250,  115,  115,   69,  125,   70,   70,  111,  249,  126,
-      116,  141,  118,  118,   71,  111,  248,  108,  116,  117,
-      247,  117,   71,  246,  118,  118,  245,  142,  143,  152,
-      244,  152,  115,  115,  153,  153,  243,  156,  242,  156,
-      241,  155,  157,  157,  118,  118,  161,  162,  240,  155,
-      153,  153,  153,  153,  185,  239,  185,  157,  157,  186,
-      186,  157,  157,  186,  186,  186,  186,   55,  238,   55,
-       56,   56,   56,  109,  109,  109,  112,  112,  112,  112,
-      112,  114,  237,  114,  114,  114,  119,  119,  236,  235,
+       72,   86,   98,  110,   96,   92,  105,  112,   93,  106,
+      283,  111,  282,   73,   54,   54,   66,   66,  120,  120,
+      281,   70,  149,   71,   71,  122,  116,  122,  121,  133,
+      123,  123,  280,   72,  134,  116,  279,  121,  150,  151,
+      278,  113,   72,  161,  277,  161,  120,  120,  162,  162,
+      123,  123,  165,  276,  165,  275,  164,  166,  166,  123,
+      123,  172,  173,  162,  162,  164,  162,  162,  197,  274,
+      197,  166,  166,  198,  198,  166,  166,  198,  198,  198,
+      198,   56,  273,   56,   57,   57,   57,  114,  114,  114,
+      117,  117,  117,  117,  117,  119,  272,  119,  119,  119,
 
-      234,  233,  232,  231,  230,  229,  228,  227,  226,  225,
-      224,  223,  222,  221,  220,  219,  218,  217,  216,  215,
-      214,  213,  212,  211,  210,  209,  208,  207,  206,  205,
-      204,  203,  202,  201,  200,  199,  198,  197,  196,  195,
-      194,  193,  192,  191,  190,  189,  188,  187,  184,  183,
-      182,  181,  180,  179,  178,  177,  176,  175,  174,  173,
-      172,  171,  170,  169,  168,  167,  166,  165,  164,  163,
-      160,  159,  158,  154,  113,  151,  150,  149,  148,  147,
-      146,  145,  144,  140,  139,  138,  137,  136,  135,  134,
-      133,  132,  131,  130,  129,  128,  127,  124,  123,  122,
+      124,  124,  271,  270,  269,  268,  267,  266,  265,  264,
+      263,  262,  261,  260,  259,  258,  257,  256,  255,  254,
+      253,  252,  251,  250,  249,  248,  247,  246,  245,  244,
+      243,  242,  241,  240,  239,  238,  237,  236,  235,  234,
+      233,  232,  231,  230,  229,  228,  227,  226,  225,  224,
+      223,  222,  221,  220,  219,  218,  217,  216,  215,  214,
+      213,  212,  211,  210,  209,  208,  207,  206,  205,  204,
+      203,  202,  201,  200,  199,  196,  195,  194,  193,  192,
+      191,  190,  189,  188,  187,  186,  185,  184,  183,  182,
+      181,  180,  179,  178,  177,  176,  175,  174,  171,  170,
 
-      121,  120,  113,  110,  104,  103,  102,   99,   98,   97,
-       96,   95,   90,   86,   80,   75,   60,   57,   54,  252,
-        3,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252
+      169,  168,  167,  163,  118,  160,  159,  158,  157,  156,
+      155,  154,  153,  152,  148,  147,  146,  145,  144,  143,
+      142,  141,  140,  139,  138,  137,  136,  135,  132,  131,
+      130,  129,  128,  127,  126,  125,  118,  115,  109,  108,
+      107,  103,  102,  101,  100,   99,   94,   90,   76,   61,
+       58,   55,  284,    3,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284
     } ;
 
-static yyconst flex_int16_t yy_chk[378] =
+static yyconst flex_int16_t yy_chk[413] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    5,    5,    6,    6,
-       11,   15,   17,   18,   18,  250,   20,   19,   20,   20,
-       17,   15,   19,   21,   11,   21,   21,   20,   19,   24,
-       24,   26,   26,   30,   21,   20,   32,   33,   35,   48,
-       30,   33,   21,   37,   38,   50,   33,   48,   35,   32,
+        1,    1,    1,    1,    1,    1,    1,    1,    5,    5,
+        6,    6,   11,   15,   17,   18,   18,   24,   24,   19,
+       26,   26,   17,   15,   19,   20,   11,   20,   20,   21,
+       19,   21,   21,   32,   28,   31,   32,   20,   33,   39,
+       34,   21,   28,   31,   34,   36,   20,   38,   45,   34,
 
-       37,   35,   20,   44,   53,   53,   44,   38,   65,   65,
-      249,   69,   69,   70,   82,   70,   70,   65,  248,   82,
-       69,   99,  117,  117,   70,   65,  245,   50,   69,   71,
-      244,   71,   70,  240,   71,   71,  239,   99,   99,  111,
-      236,  111,  115,  115,  111,  111,  235,  116,  233,  116,
-      232,  115,  116,  116,  118,  118,  126,  126,  231,  115,
-      152,  152,  153,  153,  155,  230,  155,  156,  156,  155,
-      155,  157,  157,  185,  185,  186,  186,  253,  226,  253,
-      254,  254,  254,  255,  255,  255,  256,  256,  256,  256,
-      256,  257,  225,  257,  257,  257,  258,  258,  224,  223,
+       21,   33,   39,   49,   38,   36,   45,   51,   36,   45,
+      282,   49,  281,   20,   54,   54,   66,   66,   70,   70,
+      280,   71,  103,   71,   71,   72,   66,   72,   70,   86,
+       72,   72,  279,   71,   86,   66,  278,   70,  103,  103,
+      277,   51,   71,  116,  275,  116,  120,  120,  116,  116,
+      122,  122,  121,  274,  121,  273,  120,  121,  121,  123,
+      123,  134,  134,  161,  161,  120,  162,  162,  164,  272,
+      164,  165,  165,  164,  164,  166,  166,  197,  197,  198,
+      198,  285,  271,  285,  286,  286,  286,  287,  287,  287,
+      288,  288,  288,  288,  288,  289,  270,  289,  289,  289,
 
-      222,  218,  217,  216,  215,  214,  212,  211,  210,  209,
-      208,  206,  205,  204,  203,  202,  201,  200,  199,  198,
-      197,  196,  192,  191,  190,  188,  184,  183,  182,  181,
-      179,  178,  177,  176,  175,  174,  173,  172,  170,  169,
-      168,  166,  164,  163,  162,  161,  160,  158,  150,  149,
-      148,  147,  146,  145,  144,  143,  142,  141,  140,  138,
-      137,  136,  135,  134,  133,  131,  130,  129,  128,  127,
-      125,  124,  123,  113,  112,  108,  106,  105,  104,  103,
-      102,  101,  100,   98,   97,   96,   95,   94,   93,   92,
-       90,   89,   88,   87,   86,   84,   83,   81,   80,   79,
+      290,  290,  268,  266,  265,  263,  260,  259,  256,  255,
+      253,  252,  251,  250,  249,  245,  244,  242,  241,  240,
+      237,  235,  234,  233,  232,  231,  229,  228,  227,  226,
+      224,  223,  221,  220,  219,  218,  217,  216,  215,  214,
+      213,  212,  211,  210,  206,  205,  204,  202,  201,  199,
+      196,  195,  194,  193,  191,  190,  189,  188,  187,  186,
+      185,  184,  183,  181,  180,  179,  177,  175,  174,  173,
+      172,  171,  169,  168,  167,  159,  158,  157,  156,  155,
+      154,  153,  152,  151,  150,  149,  148,  146,  145,  144,
+      143,  142,  141,  139,  138,  137,  136,  135,  133,  132,
 
-       77,   73,   66,   58,   47,   46,   45,   43,   42,   41,
-       40,   39,   36,   34,   31,   25,   14,   10,    7,    3,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
-      252,  252,  252,  252,  252,  252,  252
+      131,  130,  128,  118,  117,  113,  111,  110,  109,  108,
+      107,  106,  105,  104,  102,  101,  100,   99,   98,   97,
+       96,   94,   93,   92,   91,   90,   88,   87,   85,   84,
+       83,   82,   80,   79,   78,   74,   67,   59,   48,   47,
+       46,   44,   43,   42,   41,   40,   37,   35,   25,   14,
+       10,    7,    3,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+
+      284,  284,  284,  284,  284,  284,  284,  284,  284,  284,
+      284,  284
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[93] =
+static yyconst flex_int32_t yy_rule_can_match_eol[97] =
     {   0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,     };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,     };
 
 /* The intent behind this definition is that it'll catch
  * any uses of REJECT which flex missed.
@@ -635,19 +636,25 @@
 #define YY_RESTORE_YY_MORE_OFFSET
 #line 1 "sksl.flex"
 /*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/*
 
-	This file is IGNORED during the build process!
+    This file is IGNORED during the build process!
 
-	As this file is updated so infrequently and flex is not universally present on build machines,
-	the lex.sksl.c file must be manually regenerated if you make any changes to this file. Just run:
+    As this file is updated so infrequently and flex is not universally present on build machines,
+    the lex.sksl.c file must be manually regenerated if you make any changes to this file. Just run:
 
-		flex sksl.flex
+        flex sksl.flex
 
     You will have to manually add a copyright notice to the top of lex.sksl.c.
-    
+
 */
 #define YY_NO_UNISTD_H 1
-#line 645 "lex.sksl.c"
+#line 658 "lex.sksl.c"
 
 #define INITIAL 0
 
@@ -676,8 +683,8 @@
     size_t yy_buffer_stack_max; /**< capacity of stack. */
     YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
     char yy_hold_char;
-    yy_size_t yy_n_chars;
-    yy_size_t yyleng_r;
+    int yy_n_chars;
+    int yyleng_r;
     char *yy_c_buf_p;
     int yy_init;
     int yy_start;
@@ -718,23 +725,23 @@
 
 FILE *skslget_in (yyscan_t yyscanner );
 
-void skslset_in  (FILE * in_str ,yyscan_t yyscanner );
+void skslset_in  (FILE * _in_str ,yyscan_t yyscanner );
 
 FILE *skslget_out (yyscan_t yyscanner );
 
-void skslset_out  (FILE * out_str ,yyscan_t yyscanner );
+void skslset_out  (FILE * _out_str ,yyscan_t yyscanner );
 
-yy_size_t skslget_leng (yyscan_t yyscanner );
+			int skslget_leng (yyscan_t yyscanner );
 
 char *skslget_text (yyscan_t yyscanner );
 
 int skslget_lineno (yyscan_t yyscanner );
 
-void skslset_lineno (int line_number ,yyscan_t yyscanner );
+void skslset_lineno (int _line_number ,yyscan_t yyscanner );
 
 int skslget_column  (yyscan_t yyscanner );
 
-void skslset_column (int column_no ,yyscan_t yyscanner );
+void skslset_column (int _column_no ,yyscan_t yyscanner );
 
 /* Macros after this point can all be overridden by user definitions in
  * section 1.
@@ -748,8 +755,12 @@
 #endif
 #endif
 
+#ifndef YY_NO_UNPUT
+    
     static void yyunput (int c,char *buf_ptr  ,yyscan_t yyscanner);
     
+#endif
+
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
 #endif
@@ -783,7 +794,7 @@
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
 #endif
 
 /* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
@@ -807,7 +818,7 @@
 	else \
 		{ \
 		errno=0; \
-		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+		while ( (result = (int) fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
 			{ \
 			if( errno != EINTR) \
 				{ \
@@ -862,7 +873,7 @@
 
 /* Code executed at the end of each rule. */
 #ifndef YY_BREAK
-#define YY_BREAK break;
+#define YY_BREAK /*LINTED*/break;
 #endif
 
 #define YY_RULE_SETUP \
@@ -872,9 +883,9 @@
  */
 YY_DECL
 {
-	register yy_state_type yy_current_state;
-	register char *yy_cp, *yy_bp;
-	register int yy_act;
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
 	if ( !yyg->yy_init )
@@ -904,12 +915,12 @@
 		}
 
 	{
-#line 23 "sksl.flex"
+#line 30 "sksl.flex"
 
 
-#line 905 "lex.sksl.c"
+#line 922 "lex.sksl.c"
 
-	while ( 1 )		/* loops until end-of-file is reached */
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
 		yy_cp = yyg->yy_c_buf_p;
 
@@ -925,7 +936,7 @@
 yy_match:
 		do
 			{
-			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+			YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
 			if ( yy_accept[yy_current_state] )
 				{
 				yyg->yy_last_accepting_state = yy_current_state;
@@ -934,13 +945,13 @@
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 253 )
+				if ( yy_current_state >= 285 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
-			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c];
 			++yy_cp;
 			}
-		while ( yy_current_state != 252 );
+		while ( yy_current_state != 284 );
 		yy_cp = yyg->yy_last_accepting_cpos;
 		yy_current_state = yyg->yy_last_accepting_state;
 
@@ -974,467 +985,487 @@
 
 case 1:
 YY_RULE_SETUP
-#line 25 "sksl.flex"
+#line 32 "sksl.flex"
 { return SkSL::Token::FLOAT_LITERAL; }
 	YY_BREAK
 case 2:
 YY_RULE_SETUP
-#line 27 "sksl.flex"
+#line 34 "sksl.flex"
 { return SkSL::Token::FLOAT_LITERAL; }
 	YY_BREAK
 case 3:
 YY_RULE_SETUP
-#line 29 "sksl.flex"
+#line 36 "sksl.flex"
 { return SkSL::Token::FLOAT_LITERAL; }
 	YY_BREAK
 case 4:
 YY_RULE_SETUP
-#line 31 "sksl.flex"
+#line 38 "sksl.flex"
 { return SkSL::Token::INT_LITERAL; }
 	YY_BREAK
 case 5:
 YY_RULE_SETUP
-#line 33 "sksl.flex"
+#line 40 "sksl.flex"
 { return SkSL::Token::INT_LITERAL; }
 	YY_BREAK
 case 6:
 YY_RULE_SETUP
-#line 35 "sksl.flex"
+#line 42 "sksl.flex"
 { return SkSL::Token::TRUE_LITERAL; }
 	YY_BREAK
 case 7:
 YY_RULE_SETUP
-#line 37 "sksl.flex"
+#line 44 "sksl.flex"
 { return SkSL::Token::FALSE_LITERAL; }
 	YY_BREAK
 case 8:
 YY_RULE_SETUP
-#line 39 "sksl.flex"
+#line 46 "sksl.flex"
 { return SkSL::Token::IF; }
 	YY_BREAK
 case 9:
 YY_RULE_SETUP
-#line 41 "sksl.flex"
-{ return SkSL::Token::ELSE; }
+#line 48 "sksl.flex"
+{ return SkSL::Token::STATIC_IF; }
 	YY_BREAK
 case 10:
 YY_RULE_SETUP
-#line 43 "sksl.flex"
-{ return SkSL::Token::FOR; }
+#line 50 "sksl.flex"
+{ return SkSL::Token::ELSE; }
 	YY_BREAK
 case 11:
 YY_RULE_SETUP
-#line 45 "sksl.flex"
-{ return SkSL::Token::WHILE; }
+#line 52 "sksl.flex"
+{ return SkSL::Token::FOR; }
 	YY_BREAK
 case 12:
 YY_RULE_SETUP
-#line 47 "sksl.flex"
-{ return SkSL::Token::DO; }
+#line 54 "sksl.flex"
+{ return SkSL::Token::WHILE; }
 	YY_BREAK
 case 13:
 YY_RULE_SETUP
-#line 49 "sksl.flex"
-{ return SkSL::Token::SWITCH; }
+#line 56 "sksl.flex"
+{ return SkSL::Token::DO; }
 	YY_BREAK
 case 14:
 YY_RULE_SETUP
-#line 51 "sksl.flex"
-{ return SkSL::Token::CASE; }
+#line 58 "sksl.flex"
+{ return SkSL::Token::SWITCH; }
 	YY_BREAK
 case 15:
 YY_RULE_SETUP
-#line 53 "sksl.flex"
-{ return SkSL::Token::DEFAULT; }
+#line 60 "sksl.flex"
+{ return SkSL::Token::STATIC_SWITCH; }
 	YY_BREAK
 case 16:
 YY_RULE_SETUP
-#line 55 "sksl.flex"
-{ return SkSL::Token::BREAK; }
+#line 62 "sksl.flex"
+{ return SkSL::Token::CASE; }
 	YY_BREAK
 case 17:
 YY_RULE_SETUP
-#line 57 "sksl.flex"
-{ return SkSL::Token::CONTINUE; }
+#line 64 "sksl.flex"
+{ return SkSL::Token::DEFAULT; }
 	YY_BREAK
 case 18:
 YY_RULE_SETUP
-#line 59 "sksl.flex"
-{ return SkSL::Token::DISCARD; }
+#line 66 "sksl.flex"
+{ return SkSL::Token::BREAK; }
 	YY_BREAK
 case 19:
 YY_RULE_SETUP
-#line 61 "sksl.flex"
-{ return SkSL::Token::RETURN; }
+#line 68 "sksl.flex"
+{ return SkSL::Token::CONTINUE; }
 	YY_BREAK
 case 20:
 YY_RULE_SETUP
-#line 63 "sksl.flex"
-{ return SkSL::Token::IN; }
+#line 70 "sksl.flex"
+{ return SkSL::Token::DISCARD; }
 	YY_BREAK
 case 21:
 YY_RULE_SETUP
-#line 65 "sksl.flex"
-{ return SkSL::Token::OUT; }
+#line 72 "sksl.flex"
+{ return SkSL::Token::RETURN; }
 	YY_BREAK
 case 22:
 YY_RULE_SETUP
-#line 67 "sksl.flex"
-{ return SkSL::Token::INOUT; }
+#line 74 "sksl.flex"
+{ return SkSL::Token::IN; }
 	YY_BREAK
 case 23:
 YY_RULE_SETUP
-#line 69 "sksl.flex"
-{ return SkSL::Token::UNIFORM; }
+#line 76 "sksl.flex"
+{ return SkSL::Token::OUT; }
 	YY_BREAK
 case 24:
 YY_RULE_SETUP
-#line 71 "sksl.flex"
-{ return SkSL::Token::CONST; }
+#line 78 "sksl.flex"
+{ return SkSL::Token::INOUT; }
 	YY_BREAK
 case 25:
 YY_RULE_SETUP
-#line 73 "sksl.flex"
-{ return SkSL::Token::LOWP; }
+#line 80 "sksl.flex"
+{ return SkSL::Token::UNIFORM; }
 	YY_BREAK
 case 26:
 YY_RULE_SETUP
-#line 75 "sksl.flex"
-{ return SkSL::Token::MEDIUMP; }
+#line 82 "sksl.flex"
+{ return SkSL::Token::CONST; }
 	YY_BREAK
 case 27:
 YY_RULE_SETUP
-#line 77 "sksl.flex"
-{ return SkSL::Token::HIGHP; }
+#line 84 "sksl.flex"
+{ return SkSL::Token::LOWP; }
 	YY_BREAK
 case 28:
 YY_RULE_SETUP
-#line 79 "sksl.flex"
-{ return SkSL::Token::FLAT; }
+#line 86 "sksl.flex"
+{ return SkSL::Token::MEDIUMP; }
 	YY_BREAK
 case 29:
 YY_RULE_SETUP
-#line 81 "sksl.flex"
-{ return SkSL::Token::NOPERSPECTIVE; }
+#line 88 "sksl.flex"
+{ return SkSL::Token::HIGHP; }
 	YY_BREAK
 case 30:
 YY_RULE_SETUP
-#line 83 "sksl.flex"
-{ return SkSL::Token::READONLY; }
+#line 90 "sksl.flex"
+{ return SkSL::Token::FLAT; }
 	YY_BREAK
 case 31:
 YY_RULE_SETUP
-#line 85 "sksl.flex"
-{ return SkSL::Token::WRITEONLY; }
+#line 92 "sksl.flex"
+{ return SkSL::Token::NOPERSPECTIVE; }
 	YY_BREAK
 case 32:
 YY_RULE_SETUP
-#line 87 "sksl.flex"
-{ return SkSL::Token::COHERENT; }
+#line 94 "sksl.flex"
+{ return SkSL::Token::READONLY; }
 	YY_BREAK
 case 33:
 YY_RULE_SETUP
-#line 89 "sksl.flex"
-{ return SkSL::Token::VOLATILE; }
+#line 96 "sksl.flex"
+{ return SkSL::Token::WRITEONLY; }
 	YY_BREAK
 case 34:
 YY_RULE_SETUP
-#line 91 "sksl.flex"
-{ return SkSL::Token::RESTRICT; }
+#line 98 "sksl.flex"
+{ return SkSL::Token::COHERENT; }
 	YY_BREAK
 case 35:
 YY_RULE_SETUP
-#line 93 "sksl.flex"
-{ return SkSL::Token::STRUCT; }
+#line 100 "sksl.flex"
+{ return SkSL::Token::VOLATILE; }
 	YY_BREAK
 case 36:
 YY_RULE_SETUP
-#line 95 "sksl.flex"
-{ return SkSL::Token::LAYOUT; }
+#line 102 "sksl.flex"
+{ return SkSL::Token::RESTRICT; }
 	YY_BREAK
 case 37:
 YY_RULE_SETUP
-#line 97 "sksl.flex"
-{ return SkSL::Token::PRECISION; }
+#line 104 "sksl.flex"
+{ return SkSL::Token::BUFFER; }
 	YY_BREAK
 case 38:
 YY_RULE_SETUP
-#line 99 "sksl.flex"
-{ return SkSL::Token::IDENTIFIER; }
+#line 106 "sksl.flex"
+{ return SkSL::Token::HASSIDEEFFECTS; }
 	YY_BREAK
 case 39:
 YY_RULE_SETUP
-#line 101 "sksl.flex"
-{ return SkSL::Token::DIRECTIVE; }
+#line 108 "sksl.flex"
+{ return SkSL::Token::STRUCT; }
 	YY_BREAK
 case 40:
 YY_RULE_SETUP
-#line 103 "sksl.flex"
-{ return SkSL::Token::LPAREN; }
+#line 110 "sksl.flex"
+{ return SkSL::Token::LAYOUT; }
 	YY_BREAK
 case 41:
 YY_RULE_SETUP
-#line 105 "sksl.flex"
-{ return SkSL::Token::RPAREN; }
+#line 112 "sksl.flex"
+{ return SkSL::Token::PRECISION; }
 	YY_BREAK
 case 42:
 YY_RULE_SETUP
-#line 107 "sksl.flex"
-{ return SkSL::Token::LBRACE; }
+#line 114 "sksl.flex"
+{ return SkSL::Token::IDENTIFIER; }
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
-#line 109 "sksl.flex"
-{ return SkSL::Token::RBRACE; }
+#line 116 "sksl.flex"
+{ return SkSL::Token::DIRECTIVE; }
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
-#line 111 "sksl.flex"
-{ return SkSL::Token::LBRACKET; }
+#line 118 "sksl.flex"
+{ return SkSL::Token::LPAREN; }
 	YY_BREAK
 case 45:
 YY_RULE_SETUP
-#line 113 "sksl.flex"
-{ return SkSL::Token::RBRACKET; }
+#line 120 "sksl.flex"
+{ return SkSL::Token::RPAREN; }
 	YY_BREAK
 case 46:
 YY_RULE_SETUP
-#line 115 "sksl.flex"
-{ return SkSL::Token::DOT; }
+#line 122 "sksl.flex"
+{ return SkSL::Token::LBRACE; }
 	YY_BREAK
 case 47:
 YY_RULE_SETUP
-#line 117 "sksl.flex"
-{ return SkSL::Token::COMMA; }
+#line 124 "sksl.flex"
+{ return SkSL::Token::RBRACE; }
 	YY_BREAK
 case 48:
 YY_RULE_SETUP
-#line 119 "sksl.flex"
-{ return SkSL::Token::PLUSPLUS; }
+#line 126 "sksl.flex"
+{ return SkSL::Token::LBRACKET; }
 	YY_BREAK
 case 49:
 YY_RULE_SETUP
-#line 121 "sksl.flex"
-{ return SkSL::Token::MINUSMINUS; }
+#line 128 "sksl.flex"
+{ return SkSL::Token::RBRACKET; }
 	YY_BREAK
 case 50:
 YY_RULE_SETUP
-#line 123 "sksl.flex"
-{ return SkSL::Token::PLUS; }
+#line 130 "sksl.flex"
+{ return SkSL::Token::DOT; }
 	YY_BREAK
 case 51:
 YY_RULE_SETUP
-#line 125 "sksl.flex"
-{ return SkSL::Token::MINUS; }
+#line 132 "sksl.flex"
+{ return SkSL::Token::COMMA; }
 	YY_BREAK
 case 52:
 YY_RULE_SETUP
-#line 127 "sksl.flex"
-{ return SkSL::Token::STAR; }
+#line 134 "sksl.flex"
+{ return SkSL::Token::PLUSPLUS; }
 	YY_BREAK
 case 53:
 YY_RULE_SETUP
-#line 129 "sksl.flex"
-{ return SkSL::Token::SLASH; }
+#line 136 "sksl.flex"
+{ return SkSL::Token::MINUSMINUS; }
 	YY_BREAK
 case 54:
 YY_RULE_SETUP
-#line 131 "sksl.flex"
-{ return SkSL::Token::PERCENT; }
+#line 138 "sksl.flex"
+{ return SkSL::Token::PLUS; }
 	YY_BREAK
 case 55:
 YY_RULE_SETUP
-#line 133 "sksl.flex"
-{ return SkSL::Token::SHL; }
+#line 140 "sksl.flex"
+{ return SkSL::Token::MINUS; }
 	YY_BREAK
 case 56:
 YY_RULE_SETUP
-#line 135 "sksl.flex"
-{ return SkSL::Token::SHR; }
+#line 142 "sksl.flex"
+{ return SkSL::Token::STAR; }
 	YY_BREAK
 case 57:
 YY_RULE_SETUP
-#line 137 "sksl.flex"
-{ return SkSL::Token::BITWISEOR; }
+#line 144 "sksl.flex"
+{ return SkSL::Token::SLASH; }
 	YY_BREAK
 case 58:
 YY_RULE_SETUP
-#line 139 "sksl.flex"
-{ return SkSL::Token::BITWISEXOR; }
+#line 146 "sksl.flex"
+{ return SkSL::Token::PERCENT; }
 	YY_BREAK
 case 59:
 YY_RULE_SETUP
-#line 141 "sksl.flex"
-{ return SkSL::Token::BITWISEAND; }
+#line 148 "sksl.flex"
+{ return SkSL::Token::SHL; }
 	YY_BREAK
 case 60:
 YY_RULE_SETUP
-#line 143 "sksl.flex"
-{ return SkSL::Token::BITWISENOT; }
+#line 150 "sksl.flex"
+{ return SkSL::Token::SHR; }
 	YY_BREAK
 case 61:
 YY_RULE_SETUP
-#line 145 "sksl.flex"
-{ return SkSL::Token::LOGICALOR; }
+#line 152 "sksl.flex"
+{ return SkSL::Token::BITWISEOR; }
 	YY_BREAK
 case 62:
 YY_RULE_SETUP
-#line 147 "sksl.flex"
-{ return SkSL::Token::LOGICALXOR; }
+#line 154 "sksl.flex"
+{ return SkSL::Token::BITWISEXOR; }
 	YY_BREAK
 case 63:
 YY_RULE_SETUP
-#line 149 "sksl.flex"
-{ return SkSL::Token::LOGICALAND; }
+#line 156 "sksl.flex"
+{ return SkSL::Token::BITWISEAND; }
 	YY_BREAK
 case 64:
 YY_RULE_SETUP
-#line 151 "sksl.flex"
-{ return SkSL::Token::LOGICALNOT; }
+#line 158 "sksl.flex"
+{ return SkSL::Token::BITWISENOT; }
 	YY_BREAK
 case 65:
 YY_RULE_SETUP
-#line 153 "sksl.flex"
-{ return SkSL::Token::QUESTION; }
+#line 160 "sksl.flex"
+{ return SkSL::Token::LOGICALOR; }
 	YY_BREAK
 case 66:
 YY_RULE_SETUP
-#line 155 "sksl.flex"
-{ return SkSL::Token::COLON; }
+#line 162 "sksl.flex"
+{ return SkSL::Token::LOGICALXOR; }
 	YY_BREAK
 case 67:
 YY_RULE_SETUP
-#line 157 "sksl.flex"
-{ return SkSL::Token::EQ; }
+#line 164 "sksl.flex"
+{ return SkSL::Token::LOGICALAND; }
 	YY_BREAK
 case 68:
 YY_RULE_SETUP
-#line 159 "sksl.flex"
-{ return SkSL::Token::EQEQ; }
+#line 166 "sksl.flex"
+{ return SkSL::Token::LOGICALNOT; }
 	YY_BREAK
 case 69:
 YY_RULE_SETUP
-#line 161 "sksl.flex"
-{ return SkSL::Token::NEQ; }
+#line 168 "sksl.flex"
+{ return SkSL::Token::QUESTION; }
 	YY_BREAK
 case 70:
 YY_RULE_SETUP
-#line 163 "sksl.flex"
-{ return SkSL::Token::GT; }
+#line 170 "sksl.flex"
+{ return SkSL::Token::COLON; }
 	YY_BREAK
 case 71:
 YY_RULE_SETUP
-#line 165 "sksl.flex"
-{ return SkSL::Token::LT; }
+#line 172 "sksl.flex"
+{ return SkSL::Token::EQ; }
 	YY_BREAK
 case 72:
 YY_RULE_SETUP
-#line 167 "sksl.flex"
-{ return SkSL::Token::GTEQ; }
+#line 174 "sksl.flex"
+{ return SkSL::Token::EQEQ; }
 	YY_BREAK
 case 73:
 YY_RULE_SETUP
-#line 169 "sksl.flex"
-{ return SkSL::Token::LTEQ; }
+#line 176 "sksl.flex"
+{ return SkSL::Token::NEQ; }
 	YY_BREAK
 case 74:
 YY_RULE_SETUP
-#line 171 "sksl.flex"
-{ return SkSL::Token::PLUSEQ; }
+#line 178 "sksl.flex"
+{ return SkSL::Token::GT; }
 	YY_BREAK
 case 75:
 YY_RULE_SETUP
-#line 173 "sksl.flex"
-{ return SkSL::Token::MINUSEQ; }
+#line 180 "sksl.flex"
+{ return SkSL::Token::LT; }
 	YY_BREAK
 case 76:
 YY_RULE_SETUP
-#line 175 "sksl.flex"
-{ return SkSL::Token::STAREQ; }
+#line 182 "sksl.flex"
+{ return SkSL::Token::GTEQ; }
 	YY_BREAK
 case 77:
 YY_RULE_SETUP
-#line 177 "sksl.flex"
-{ return SkSL::Token::SLASHEQ; }
+#line 184 "sksl.flex"
+{ return SkSL::Token::LTEQ; }
 	YY_BREAK
 case 78:
 YY_RULE_SETUP
-#line 179 "sksl.flex"
-{ return SkSL::Token::PERCENTEQ; }
+#line 186 "sksl.flex"
+{ return SkSL::Token::PLUSEQ; }
 	YY_BREAK
 case 79:
 YY_RULE_SETUP
-#line 181 "sksl.flex"
-{ return SkSL::Token::SHLEQ; }
+#line 188 "sksl.flex"
+{ return SkSL::Token::MINUSEQ; }
 	YY_BREAK
 case 80:
 YY_RULE_SETUP
-#line 183 "sksl.flex"
-{ return SkSL::Token::SHREQ; }
+#line 190 "sksl.flex"
+{ return SkSL::Token::STAREQ; }
 	YY_BREAK
 case 81:
 YY_RULE_SETUP
-#line 185 "sksl.flex"
-{ return SkSL::Token::BITWISEOREQ; }
+#line 192 "sksl.flex"
+{ return SkSL::Token::SLASHEQ; }
 	YY_BREAK
 case 82:
 YY_RULE_SETUP
-#line 187 "sksl.flex"
-{ return SkSL::Token::BITWISEXOREQ; }
+#line 194 "sksl.flex"
+{ return SkSL::Token::PERCENTEQ; }
 	YY_BREAK
 case 83:
 YY_RULE_SETUP
-#line 189 "sksl.flex"
-{ return SkSL::Token::BITWISEANDEQ; }
+#line 196 "sksl.flex"
+{ return SkSL::Token::SHLEQ; }
 	YY_BREAK
 case 84:
 YY_RULE_SETUP
-#line 191 "sksl.flex"
-{ return SkSL::Token::LOGICALOREQ; }
+#line 198 "sksl.flex"
+{ return SkSL::Token::SHREQ; }
 	YY_BREAK
 case 85:
 YY_RULE_SETUP
-#line 193 "sksl.flex"
-{ return SkSL::Token::LOGICALXOREQ; }
+#line 200 "sksl.flex"
+{ return SkSL::Token::BITWISEOREQ; }
 	YY_BREAK
 case 86:
 YY_RULE_SETUP
-#line 195 "sksl.flex"
-{ return SkSL::Token::LOGICALANDEQ; }
+#line 202 "sksl.flex"
+{ return SkSL::Token::BITWISEXOREQ; }
 	YY_BREAK
 case 87:
 YY_RULE_SETUP
-#line 197 "sksl.flex"
-{ return SkSL::Token::SEMICOLON; }
+#line 204 "sksl.flex"
+{ return SkSL::Token::BITWISEANDEQ; }
 	YY_BREAK
 case 88:
 YY_RULE_SETUP
-#line 199 "sksl.flex"
-/* line comment */
+#line 206 "sksl.flex"
+{ return SkSL::Token::LOGICALOREQ; }
 	YY_BREAK
 case 89:
-/* rule 89 can match eol */
 YY_RULE_SETUP
-#line 201 "sksl.flex"
-/* block comment */
+#line 208 "sksl.flex"
+{ return SkSL::Token::LOGICALXOREQ; }
 	YY_BREAK
 case 90:
-/* rule 90 can match eol */
 YY_RULE_SETUP
-#line 203 "sksl.flex"
-/* whitespace */
+#line 210 "sksl.flex"
+{ return SkSL::Token::LOGICALANDEQ; }
 	YY_BREAK
 case 91:
 YY_RULE_SETUP
-#line 205 "sksl.flex"
-{ return SkSL::Token::INVALID_TOKEN; }
+#line 212 "sksl.flex"
+{ return SkSL::Token::SEMICOLON; }
 	YY_BREAK
 case 92:
 YY_RULE_SETUP
-#line 207 "sksl.flex"
+#line 214 "sksl.flex"
+/* line comment */
+	YY_BREAK
+case 93:
+/* rule 93 can match eol */
+YY_RULE_SETUP
+#line 216 "sksl.flex"
+/* block comment */
+	YY_BREAK
+case 94:
+/* rule 94 can match eol */
+YY_RULE_SETUP
+#line 218 "sksl.flex"
+/* whitespace */
+	YY_BREAK
+case 95:
+YY_RULE_SETUP
+#line 220 "sksl.flex"
+{ return SkSL::Token::INVALID_TOKEN; }
+	YY_BREAK
+case 96:
+YY_RULE_SETUP
+#line 222 "sksl.flex"
 ECHO;
 	YY_BREAK
-#line 1432 "lex.sksl.c"
+#line 1469 "lex.sksl.c"
 case YY_STATE_EOF(INITIAL):
 	yyterminate();
 
@@ -1579,9 +1610,9 @@
 static int yy_get_next_buffer (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
-	register char *source = yyg->yytext_ptr;
-	register int number_to_move, i;
+	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = yyg->yytext_ptr;
+	yy_size_t number_to_move, i;
 	int ret_val;
 
 	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
@@ -1610,7 +1641,7 @@
 	/* Try to read more data. */
 
 	/* First move last chars to start of buffer. */
-	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+	number_to_move = (yy_size_t) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
 
 	for ( i = 0; i < number_to_move; ++i )
 		*(dest++) = *(source++);
@@ -1623,7 +1654,7 @@
 
 	else
 		{
-			yy_size_t num_to_read =
+			int num_to_read =
 			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
 
 		while ( num_to_read <= 0 )
@@ -1637,7 +1668,7 @@
 
 			if ( b->yy_is_our_buffer )
 				{
-				yy_size_t new_size = b->yy_buf_size * 2;
+				int new_size = b->yy_buf_size * 2;
 
 				if ( new_size <= 0 )
 					b->yy_buf_size += b->yy_buf_size / 8;
@@ -1650,7 +1681,7 @@
 				}
 			else
 				/* Can't grow it, we don't own it. */
-				b->yy_ch_buf = 0;
+				b->yy_ch_buf = NULL;
 
 			if ( ! b->yy_ch_buf )
 				YY_FATAL_ERROR(
@@ -1692,9 +1723,9 @@
 	else
 		ret_val = EOB_ACT_CONTINUE_SCAN;
 
-	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+	if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
 		/* Extend the array by 50%, plus the number we really need. */
-		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
 		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) skslrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
 		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
 			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
@@ -1713,15 +1744,15 @@
 
     static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
 {
-	register yy_state_type yy_current_state;
-	register char *yy_cp;
+	yy_state_type yy_current_state;
+	char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
 	yy_current_state = yyg->yy_start;
 
 	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
 		{
-		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
 		if ( yy_accept[yy_current_state] )
 			{
 			yyg->yy_last_accepting_state = yy_current_state;
@@ -1730,10 +1761,10 @@
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 253 )
+			if ( yy_current_state >= 285 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
-		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c];
 		}
 
 	return yy_current_state;
@@ -1746,11 +1777,11 @@
  */
     static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
 {
-	register int yy_is_jam;
+	int yy_is_jam;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
-	register char *yy_cp = yyg->yy_c_buf_p;
+	char *yy_cp = yyg->yy_c_buf_p;
 
-	register YY_CHAR yy_c = 1;
+	YY_CHAR yy_c = 1;
 	if ( yy_accept[yy_current_state] )
 		{
 		yyg->yy_last_accepting_state = yy_current_state;
@@ -1759,19 +1790,21 @@
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 253 )
+		if ( yy_current_state >= 285 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
-	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 252);
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c];
+	yy_is_jam = (yy_current_state == 284);
 
 	(void)yyg;
 	return yy_is_jam ? 0 : yy_current_state;
 }
 
-    static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+#ifndef YY_NO_UNPUT
+
+    static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
 {
-	register char *yy_cp;
+	char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
     yy_cp = yyg->yy_c_buf_p;
@@ -1782,10 +1815,10 @@
 	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
 		{ /* need to shift things up to make room */
 		/* +2 for EOB chars. */
-		register yy_size_t number_to_move = yyg->yy_n_chars + 2;
-		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+		int number_to_move = yyg->yy_n_chars + 2;
+		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
 					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
-		register char *source =
+		char *source =
 				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
 
 		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
@@ -1794,7 +1827,7 @@
 		yy_cp += (int) (dest - source);
 		yy_bp += (int) (dest - source);
 		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
-			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+			yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
 
 		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
 			YY_FATAL_ERROR( "flex scanner push-back overflow" );
@@ -1811,6 +1844,8 @@
 	yyg->yy_c_buf_p = yy_cp;
 }
 
+#endif
+
 #ifndef YY_NO_INPUT
 #ifdef __cplusplus
     static int yyinput (yyscan_t yyscanner)
@@ -1836,7 +1871,7 @@
 
 		else
 			{ /* need more input */
-			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
 			++yyg->yy_c_buf_p;
 
 			switch ( yy_get_next_buffer( yyscanner ) )
@@ -1860,7 +1895,7 @@
 				case EOB_ACT_END_OF_FILE:
 					{
 					if ( skslwrap(yyscanner ) )
-						return EOF;
+						return 0;
 
 					if ( ! yyg->yy_did_buffer_switch_on_eof )
 						YY_NEW_FILE;
@@ -1971,7 +2006,7 @@
 	if ( ! b )
 		YY_FATAL_ERROR( "out of dynamic memory in sksl_create_buffer()" );
 
-	b->yy_buf_size = size;
+	b->yy_buf_size = (yy_size_t)size;
 
 	/* yy_ch_buf has to be 2 characters longer than the size given because
 	 * we need to put in 2 end-of-buffer characters.
@@ -2123,7 +2158,7 @@
  */
 static void skslensure_buffer_stack (yyscan_t yyscanner)
 {
-	yy_size_t num_to_alloc;
+	int num_to_alloc;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
 	if (!yyg->yy_buffer_stack) {
@@ -2132,7 +2167,7 @@
 		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
 		 * immediate realloc on the next call.
          */
-		num_to_alloc = 1;
+      num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
 		yyg->yy_buffer_stack = (struct yy_buffer_state**)skslalloc
 								(num_to_alloc * sizeof(struct yy_buffer_state*)
 								, yyscanner);
@@ -2149,7 +2184,7 @@
 	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
 
 		/* Increase the buffer to prepare for a possible push. */
-		int grow_size = 8 /* arbitrary grow size */;
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
 
 		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
 		yyg->yy_buffer_stack = (struct yy_buffer_state**)skslrealloc
@@ -2179,7 +2214,7 @@
 	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
 	     base[size-1] != YY_END_OF_BUFFER_CHAR )
 		/* They forgot to leave room for the EOB's. */
-		return 0;
+		return NULL;
 
 	b = (YY_BUFFER_STATE) skslalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
 	if ( ! b )
@@ -2188,7 +2223,7 @@
 	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
 	b->yy_buf_pos = b->yy_ch_buf = base;
 	b->yy_is_our_buffer = 0;
-	b->yy_input_file = 0;
+	b->yy_input_file = NULL;
 	b->yy_n_chars = b->yy_buf_size;
 	b->yy_is_interactive = 0;
 	b->yy_at_bol = 1;
@@ -2211,7 +2246,7 @@
 YY_BUFFER_STATE sksl_scan_string (yyconst char * yystr , yyscan_t yyscanner)
 {
     
-	return sksl_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+	return sksl_scan_bytes(yystr,(int) strlen(yystr) ,yyscanner);
 }
 
 /** Setup the input buffer state to scan the given bytes. The next call to sksllex() will
@@ -2221,7 +2256,7 @@
  * @param yyscanner The scanner object.
  * @return the newly allocated buffer state object.
  */
-YY_BUFFER_STATE sksl_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+YY_BUFFER_STATE sksl_scan_bytes  (yyconst char * yybytes, int  _yybytes_len , yyscan_t yyscanner)
 {
 	YY_BUFFER_STATE b;
 	char *buf;
@@ -2229,7 +2264,7 @@
 	yy_size_t i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
-	n = _yybytes_len + 2;
+	n = (yy_size_t) _yybytes_len + 2;
 	buf = (char *) skslalloc(n ,yyscanner );
 	if ( ! buf )
 		YY_FATAL_ERROR( "out of dynamic memory in sksl_scan_bytes()" );
@@ -2255,9 +2290,11 @@
 #define YY_EXIT_FAILURE 2
 #endif
 
-static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+static void yynoreturn yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
 {
-    	(void) fprintf( stderr, "%s\n", msg );
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	(void) fprintf( stderr, "%s\n", msg );
 	exit( YY_EXIT_FAILURE );
 }
 
@@ -2336,7 +2373,7 @@
 /** Get the length of the current token.
  * @param yyscanner The scanner object.
  */
-yy_size_t skslget_leng  (yyscan_t yyscanner)
+int skslget_leng  (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
     return yyleng;
@@ -2363,10 +2400,10 @@
 }
 
 /** Set the current line number.
- * @param line_number
+ * @param _line_number line number
  * @param yyscanner The scanner object.
  */
-void skslset_lineno (int  line_number , yyscan_t yyscanner)
+void skslset_lineno (int  _line_number , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
@@ -2374,14 +2411,14 @@
         if (! YY_CURRENT_BUFFER )
            YY_FATAL_ERROR( "skslset_lineno called with no buffer" );
     
-    yylineno = line_number;
+    yylineno = _line_number;
 }
 
 /** Set the current column.
- * @param line_number
+ * @param _column_no column number
  * @param yyscanner The scanner object.
  */
-void skslset_column (int  column_no , yyscan_t yyscanner)
+void skslset_column (int  _column_no , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
@@ -2389,25 +2426,25 @@
         if (! YY_CURRENT_BUFFER )
            YY_FATAL_ERROR( "skslset_column called with no buffer" );
     
-    yycolumn = column_no;
+    yycolumn = _column_no;
 }
 
 /** Set the input stream. This does not discard the current
  * input buffer.
- * @param in_str A readable stream.
+ * @param _in_str A readable stream.
  * @param yyscanner The scanner object.
  * @see sksl_switch_to_buffer
  */
-void skslset_in (FILE *  in_str , yyscan_t yyscanner)
+void skslset_in (FILE *  _in_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyin = in_str ;
+    yyin = _in_str ;
 }
 
-void skslset_out (FILE *  out_str , yyscan_t yyscanner)
+void skslset_out (FILE *  _out_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyout = out_str ;
+    yyout = _out_str ;
 }
 
 int skslget_debug  (yyscan_t yyscanner)
@@ -2416,10 +2453,10 @@
     return yy_flex_debug;
 }
 
-void skslset_debug (int  bdebug , yyscan_t yyscanner)
+void skslset_debug (int  _bdebug , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yy_flex_debug = bdebug ;
+    yy_flex_debug = _bdebug ;
 }
 
 /* Accessor methods for yylval and yylloc */
@@ -2495,10 +2532,10 @@
      * This function is called from sksllex_destroy(), so don't allocate here.
      */
 
-    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack = NULL;
     yyg->yy_buffer_stack_top = 0;
     yyg->yy_buffer_stack_max = 0;
-    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_c_buf_p = NULL;
     yyg->yy_init = 0;
     yyg->yy_start = 0;
 
@@ -2511,8 +2548,8 @@
     yyin = stdin;
     yyout = stdout;
 #else
-    yyin = (FILE *) 0;
-    yyout = (FILE *) 0;
+    yyin = NULL;
+    yyout = NULL;
 #endif
 
     /* For future reference: Set errno on error, since we are called by
@@ -2558,7 +2595,10 @@
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
 {
-	register int i;
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
+	int i;
 	for ( i = 0; i < n; ++i )
 		s1[i] = s2[i];
 }
@@ -2567,7 +2607,7 @@
 #ifdef YY_NEED_STRLEN
 static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
 {
-	register int n;
+	int n;
 	for ( n = 0; s[n]; ++n )
 		;
 
@@ -2577,11 +2617,16 @@
 
 void *skslalloc (yy_size_t  size , yyscan_t yyscanner)
 {
-	return (void *) malloc( size );
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+	return malloc(size);
 }
 
 void *skslrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
+
 	/* The cast to (char *) in the following accommodates both
 	 * implementations that use char* generic pointers, and those
 	 * that use void* generic pointers.  It works with the latter
@@ -2589,17 +2634,19 @@
 	 * any pointer type to void*, and deal with argument conversions
 	 * as though doing an assignment.
 	 */
-	return (void *) realloc( (char *) ptr, size );
+	return realloc(ptr, size);
 }
 
 void skslfree (void * ptr , yyscan_t yyscanner)
 {
+	struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	(void)yyg;
 	free( (char *) ptr );	/* see skslrealloc() for (char *) cast */
 }
 
 #define YYTABLES_NAME "yytables"
 
-#line 206 "sksl.flex"
+#line 222 "sksl.flex"
 
 
 
diff --git a/src/sksl/sksl.flex b/src/sksl/sksl.flex
index bbb106c..c5d5ec3 100644
--- a/src/sksl/sksl.flex
+++ b/src/sksl/sksl.flex
@@ -45,6 +45,8 @@
 
 if { return SkSL::Token::IF; }
 
+@if { return SkSL::Token::STATIC_IF; }
+
 else { return SkSL::Token::ELSE; }
 
 for { return SkSL::Token::FOR; }
@@ -55,6 +57,8 @@
 
 switch { return SkSL::Token::SWITCH; }
 
+@switch { return SkSL::Token::STATIC_SWITCH; }
+
 case { return SkSL::Token::CASE; }
 
 default { return SkSL::Token::DEFAULT; }
@@ -97,6 +101,10 @@
 
 restrict { return SkSL::Token::RESTRICT; }
 
+buffer { return SkSL::Token::BUFFER; }
+
+sk_has_side_effects { return SkSL::Token::HASSIDEEFFECTS; }
+
 struct { return SkSL::Token::STRUCT; }
 
 layout { return SkSL::Token::LAYOUT; }
diff --git a/src/sksl/sksl.include b/src/sksl/sksl.include
index 11e3710..18a5516 100644
--- a/src/sksl/sksl.include
+++ b/src/sksl/sksl.include
@@ -281,6 +281,7 @@
 */
 
 $gvec4 texture($gsampler2DRect sampler, vec2 P);
+$gvec4 texture($gsampler2DRect sampler, vec3 P);
 
 /*
 float texture(sampler2DRectShadow sampler, vec3 P);
@@ -312,7 +313,6 @@
 float textureProj(sampler1DShadow sampler, vec4 P, float bias);
 float textureProj(sampler2DShadow sampler, vec4 P);
 float textureProj(sampler2DShadow sampler, vec4 P, float bias);
-$gvec4 textureProj($gsampler2DRect sampler, vec3 P);
 $gvec4 textureProj($gsampler2DRect sampler, vec4 P);
 float textureProj(sampler2DRectShadow sampler, vec4 P);
 $gvec4 textureLod($gsampler1D sampler, float P, float lod);
@@ -345,9 +345,10 @@
 float textureOffset(sampler1DArrayShadow sampler, vec3 P, int offset, float bias);
 float textureOffset(sampler2DArrayShadow sampler, vec4 P, ivec2 offset);
 */
+vec4 texelFetch(samplerBuffer sampler, int P);
+
 $gvec4 texelFetch($gsampler1D sampler, int P, int lod);
 $gvec4 texelFetch($gsampler2D sampler, ivec2 P, int lod);
-$gvec4 texelFetch($gsamplerBuffer sampler, int P);
 $gvec4 texelFetch($gsampler2DRect sampler, ivec2 P);
 /*
 $gvec4 texelFetch($gsampler3D sampler, ivec3 P, int lod);
diff --git a/src/sksl/sksl_frag.include b/src/sksl/sksl_frag.include
index d65545d..f1192fe 100644
--- a/src/sksl/sksl_frag.include
+++ b/src/sksl/sksl_frag.include
@@ -14,7 +14,7 @@
 layout(builtin=9999) vec4 gl_LastFragColorARM;
 layout(builtin=9999) int gl_SampleMaskIn[1];
 layout(builtin=9999) out int gl_SampleMask[1];
-layout(builtin=9999) vec4 gl_SecondaryFragColorEXT;
+layout(builtin=9999) out vec4 gl_SecondaryFragColorEXT;
 
 layout(location=0,index=0,builtin=10001) out vec4 sk_FragColor;
 
diff --git a/src/sksl/sksl_geom.include b/src/sksl/sksl_geom.include
index 18e779f..e980d6b 100644
--- a/src/sksl/sksl_geom.include
+++ b/src/sksl/sksl_geom.include
@@ -16,9 +16,9 @@
 
 layout(builtin=8) int sk_InvocationID;
 
-void EmitStreamVertex(int stream);
-void EndStreamPrimitive(int stream);
-void EmitVertex();
-void EndPrimitive();
+sk_has_side_effects void EmitStreamVertex(int stream);
+sk_has_side_effects void EndStreamPrimitive(int stream);
+sk_has_side_effects void EmitVertex();
+sk_has_side_effects void EndPrimitive();
 
 )
diff --git a/src/sksl/sksl_vert.include b/src/sksl/sksl_vert.include
index e7e9d59..2c38a8b 100644
--- a/src/sksl/sksl_vert.include
+++ b/src/sksl/sksl_vert.include
@@ -8,7 +8,7 @@
     layout(builtin=3) float sk_ClipDistance[1];
 };
 
-layout(builtin=5) int sk_VertexID;
+layout(builtin=5) in int sk_VertexID;
 
 )
 
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 975f496..a98d4b7 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -63,7 +63,8 @@
     }
     fRecs.reset();
     if (canvas) {
-        this->resetForNextPicture(SkIRect::MakeSize(canvas->getBaseLayerSize()));
+        this->resetCanvas(canvas->getBaseLayerSize().width(),
+                          canvas->getBaseLayerSize().height());
         fCanvas = canvas;
     }
 }
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index 030f4de..05cabad 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -7,6 +7,7 @@
 
 #include "SkData.h"
 #include "SkDumpCanvas.h"
+#include "SkImage.h"
 #include "SkPatchUtils.h"
 #include "SkPicture.h"
 #include "SkPixelRef.h"
@@ -121,7 +122,7 @@
     }
 }
 
-static const char* toString(SkCanvas::VertexMode vm) {
+static const char* toString(SkVertices::VertexMode vm) {
     static const char* gVMNames[] = {
         "TRIANGLES", "STRIP", "FAN"
     };
@@ -478,14 +479,10 @@
     this->dump(kDrawPatch_Verb, &paint, "drawPatch(Vertices{[%f, %f], [%f, %f], [%f, %f], [%f, %f]}\
               | Colors{[0x%x], [0x%x], [0x%x], [0x%x]} | TexCoords{[%f,%f], [%f,%f], [%f,%f], \
                [%f,%f]})",
-              cubics[SkPatchUtils::kTopP0_CubicCtrlPts].fX,
-              cubics[SkPatchUtils::kTopP0_CubicCtrlPts].fY,
-              cubics[SkPatchUtils::kTopP3_CubicCtrlPts].fX,
-              cubics[SkPatchUtils::kTopP3_CubicCtrlPts].fY,
-              cubics[SkPatchUtils::kBottomP3_CubicCtrlPts].fX,
-              cubics[SkPatchUtils::kBottomP3_CubicCtrlPts].fY,
-              cubics[SkPatchUtils::kBottomP0_CubicCtrlPts].fX,
-              cubics[SkPatchUtils::kBottomP0_CubicCtrlPts].fY,
+              cubics[0].fX, cubics[0].fY,
+              cubics[3].fX, cubics[3].fY,
+              cubics[6].fX, cubics[6].fY,
+              cubics[9].fX, cubics[9].fY,
               colors[0], colors[1], colors[2], colors[3],
               texCoords[0].x(), texCoords[0].y(), texCoords[1].x(), texCoords[1].y(),
               texCoords[2].x(), texCoords[2].y(), texCoords[3].x(), texCoords[3].y());
diff --git a/src/utils/SkEventTracer.cpp b/src/utils/SkEventTracer.cpp
index 0a748d1..5fb60bd 100644
--- a/src/utils/SkEventTracer.cpp
+++ b/src/utils/SkEventTracer.cpp
@@ -42,11 +42,15 @@
 // We prefer gUserTracer if it's been set, otherwise we fall back on a default tracer;
 static SkEventTracer* gUserTracer = nullptr;
 
-void SkEventTracer::SetInstance(SkEventTracer* tracer) {
-    SkASSERT(nullptr == sk_atomic_load(&gUserTracer, sk_memory_order_acquire));
-    sk_atomic_store(&gUserTracer, tracer, sk_memory_order_release);
+bool SkEventTracer::SetInstance(SkEventTracer* tracer) {
+    SkEventTracer* expected = nullptr;
+    if (!sk_atomic_compare_exchange(&gUserTracer, &expected, tracer)) {
+        delete tracer;
+        return false;
+    }
     // An atomic load during process shutdown is probably overkill, but safe overkill.
-    atexit([]() { delete sk_atomic_load(&gUserTracer, sk_memory_order_acquire); });
+    atexit([]() { delete sk_atomic_load(&gUserTracer); });
+    return true;
 }
 
 SkEventTracer* SkEventTracer::GetInstance() {
diff --git a/src/utils/SkInsetConvexPolygon.cpp b/src/utils/SkInsetConvexPolygon.cpp
index 5b9d84b..bb46942 100755
--- a/src/utils/SkInsetConvexPolygon.cpp
+++ b/src/utils/SkInsetConvexPolygon.cpp
@@ -154,8 +154,7 @@
         SkVector v0 = poly[j] - poly[i];
         SkVector v1 = poly[k] - poly[i];
         SkScalar perpDot = v0.cross(v1);
-        int side = winding*perpDot;
-        if (side < 0) {
+        if (winding*perpDot < 0) {
             return false;
         }
     }
diff --git a/src/utils/SkMultiPictureDocument.cpp b/src/utils/SkMultiPictureDocument.cpp
index d987818..9868ca2 100644
--- a/src/utils/SkMultiPictureDocument.cpp
+++ b/src/utils/SkMultiPictureDocument.cpp
@@ -7,11 +7,14 @@
 
 #include "SkMultiPictureDocument.h"
 #include "SkMultiPictureDocumentPriv.h"
+#include "SkNWayCanvas.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
 #include "SkStream.h"
 #include "SkTArray.h"
 
+#include <limits.h>
+
 /*
   File format:
       BEGINNING_OF_FILE:
@@ -26,6 +29,21 @@
 */
 
 namespace {
+// The unique file signature for this file type.
+static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
+
+static constexpr char kEndPage[] = "SkMultiPictureEndPage";
+
+const uint32_t kVersion = 2;
+
+static SkSize join(const SkTArray<SkSize>& sizes) {
+    SkSize joined = {0, 0};
+    for (SkSize s : sizes) {
+        joined = SkSize{SkTMax(joined.width(), s.width()), SkTMax(joined.height(), s.height())};
+    }
+    return joined;
+}
+
 static SkCanvas* trim(SkCanvas* canvas,
                       SkScalar w, SkScalar h,
                       const SkRect& trimBox) {
@@ -59,19 +77,17 @@
     void onClose(SkWStream* wStream) override {
         SkASSERT(wStream);
         SkASSERT(wStream->bytesWritten() == 0);
-        wStream->writeText(SkMultiPictureDocumentProtocol::kMagic);
-        wStream->write32(SkMultiPictureDocumentProtocol::kVersion);
+        wStream->writeText(kMagic);
+        wStream->write32(kVersion);
         wStream->write32(SkToU32(fPages.count()));
         for (SkSize s : fSizes) {
             wStream->write(&s, sizeof(s));
         }
-        SkSize bigsize = SkMultiPictureDocumentProtocol::Join(fSizes);
+        SkSize bigsize = join(fSizes);
         SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
         for (const sk_sp<SkPicture>& page : fPages) {
             c->drawPicture(page);
-            c->drawAnnotation(SkRect::MakeEmpty(),
-                              SkMultiPictureDocumentProtocol::kEndPage,
-                              nullptr);
+            c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, nullptr);
         }
         sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
         p->serialize(wStream);
@@ -89,3 +105,103 @@
 sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream) {
     return sk_make_sp<MultiPictureDocument>(wStream, nullptr);
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) {
+    if (!stream) {
+        return 0;
+    }
+    stream->seek(0);
+    const size_t size = sizeof(kMagic) - 1;
+    char buffer[size];
+    if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
+        stream = nullptr;
+        return 0;
+    }
+    uint32_t versionNumber = stream->readU32();
+    if (versionNumber != kVersion) {
+        return 0;
+    }
+    uint32_t pageCount = stream->readU32();
+    if (pageCount > INT_MAX) {
+        return 0;
+    }
+    // leave stream position right here.
+    return (int)pageCount;
+}
+
+bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream,
+                                         SkDocumentPage* dstArray,
+                                         int dstArrayCount) {
+    if (!dstArray || dstArrayCount < 1) {
+        return false;
+    }
+    int pageCount = SkMultiPictureDocumentReadPageCount(stream);
+    if (pageCount < 1 || pageCount != dstArrayCount) {
+        return false;
+    }
+    for (int i = 0; i < pageCount; ++i) {
+        SkSize& s = dstArray[i].fSize;
+        if (sizeof(s) != stream->read(&s, sizeof(s))) {
+            return false;
+        }
+    }
+    // leave stream position right here.
+    return true;
+}
+
+namespace {
+struct PagerCanvas : public SkNWayCanvas {
+    SkPictureRecorder fRecorder;
+    SkDocumentPage* fDst;
+    int fCount;
+    int fIndex = 0;
+    PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
+            : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
+        this->nextCanvas();
+    }
+    void nextCanvas() {
+        if (fIndex < fCount) {
+            SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
+            this->addCanvas(fRecorder.beginRecording(bounds));
+        }
+    }
+    void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
+        if (0 == strcmp(key, kEndPage)) {
+            this->removeAll();
+            if (fIndex < fCount) {
+                fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
+                ++fIndex;
+            }
+            this->nextCanvas();
+        } else {
+            this->SkNWayCanvas::onDrawAnnotation(r, key, d);
+        }
+    }
+};
+}  // namespace
+
+bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
+                                SkDocumentPage* dstArray,
+                                int dstArrayCount) {
+    if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) {
+        return false;
+    }
+    SkSize joined = {0.0f, 0.0f};
+    for (int i = 0; i < dstArrayCount; ++i) {
+        joined = SkSize{SkTMax(joined.width(), dstArray[i].fSize.width()),
+                        SkTMax(joined.height(), dstArray[i].fSize.height())};
+    }
+
+    auto picture = SkPicture::MakeFromStream(stream);
+
+    PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
+    // Must call playback(), not drawPicture() to reach
+    // PagerCanvas::onDrawAnnotation().
+    picture->playback(&canvas);
+    if (canvas.fIndex != dstArrayCount) {
+        SkDEBUGF(("Malformed SkMultiPictureDocument\n"));
+    }
+    return true;
+}
diff --git a/src/utils/SkMultiPictureDocument.h b/src/utils/SkMultiPictureDocument.h
index ac78260..0ca8c2d 100644
--- a/src/utils/SkMultiPictureDocument.h
+++ b/src/utils/SkMultiPictureDocument.h
@@ -7,42 +7,32 @@
 #ifndef SkMultiPictureDocument_DEFINED
 #define SkMultiPictureDocument_DEFINED
 
-/*
-  This format is not intended to be used in production.
-
-  For clients looking for a way to represent a document in memory,
-
-    struct Doc {
-        std::vector<sk_sp<SkPicture>> fPages;
-        std::vector<SkSize> fPageSizes;
-    };
-
-  or
-
-    struct Page {
-        sk_sp<SkPicture> fPage;
-        SkSize fPageSize;
-    };
-    std::vector<Page> pages;
-
-  would work much better.
-
-  Multi-SkPicture (MSKP) files are still useful for debugging and
-  testing.
-
-  The downsides of this format are currently:
-  - no way to extract a single page; must read the entire file at once.
-  - must use `dm` to convert to another format before passing into
-    standard skp tools.
-  - `dm` can extract the first page to skp, but no others.
-
-  TODO(halcanary): replace with somthing that addresses these issues.
- */
-
 #include "SkDocument.h"
 
-/** Writes into an experimental, undocumented file format that is
-    useful for debugging documents printed via Skia. */
+class SkStreamSeekable;
+
+/**
+ *  Writes into a file format that is similar to SkPicture::serialize()
+ */
 SK_API sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* dst);
 
+struct SkDocumentPage {
+    sk_sp<SkPicture> fPicture;
+    SkSize fSize;
+};
+
+/**
+ *  Returns the number of pages in the SkMultiPictureDocument.
+ */
+SK_API 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,
+                                       SkDocumentPage* dstArray,
+                                       int dstArrayCount);
+
 #endif  // SkMultiPictureDocument_DEFINED
diff --git a/src/utils/SkMultiPictureDocumentPriv.h b/src/utils/SkMultiPictureDocumentPriv.h
index 6d5ab47..aff5b55 100644
--- a/src/utils/SkMultiPictureDocumentPriv.h
+++ b/src/utils/SkMultiPictureDocumentPriv.h
@@ -8,25 +8,14 @@
 #ifndef SkMultiPictureDocumentPriv_DEFINED
 #define SkMultiPictureDocumentPriv_DEFINED
 
-#include "SkTArray.h"
-#include "SkSize.h"
+#include "SkMultiPictureDocument.h"
 
-namespace SkMultiPictureDocumentProtocol {
-static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
-
-static constexpr char kEndPage[] = "SkMultiPictureEndPage";
-
-const uint32_t kVersion = 2;
-
-inline SkSize Join(const SkTArray<SkSize>& sizes) {
-    SkSize joined = SkSize::Make(0, 0);
-    for (SkSize s : sizes) {
-        joined = SkSize::Make(SkTMax(joined.width(), s.width()),
-                              SkTMax(joined.height(), s.height()));
-    }
-    return joined;
-}
-
-}
+/**
+ *  Additional API allows one to read the array of page-sizes without parsing
+ *  the entire file.  Used by DM.
+ */
+bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* src,
+                                         SkDocumentPage* dstArray,
+                                         int dstArrayCount);
 
 #endif  // SkMultiPictureDocumentPriv_DEFINED
diff --git a/src/utils/SkMultiPictureDocumentReader.cpp b/src/utils/SkMultiPictureDocumentReader.cpp
deleted file mode 100644
index 3924f3e..0000000
--- a/src/utils/SkMultiPictureDocumentReader.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 "SkMultiPictureDocumentPriv.h"
-#include "SkMultiPictureDocumentReader.h"
-#include "SkPicture.h"
-#include "SkStream.h"
-#include "SkPictureRecorder.h"
-#include "SkNWayCanvas.h"
-
-bool SkMultiPictureDocumentReader::init(SkStreamSeekable* stream) {
-    if (!stream) {
-        return false;
-    }
-    stream->seek(0);
-    const size_t size = sizeof(SkMultiPictureDocumentProtocol::kMagic) - 1;
-    char buffer[size];
-    if (size != stream->read(buffer, size) ||
-        0 != memcmp(SkMultiPictureDocumentProtocol::kMagic, buffer, size)) {
-        stream = nullptr;
-        return false;
-    }
-    bool good = true;
-    uint32_t versionNumber = stream->readU32();
-    if (versionNumber != SkMultiPictureDocumentProtocol::kVersion) {
-        return false;
-    }
-    uint32_t pageCount = stream->readU32();
-    fSizes.reset(pageCount);
-    for (uint32_t i = 0; i < pageCount; ++i) {
-        SkSize size;
-        good &= sizeof(size) == stream->read(&size, sizeof(size));
-        fSizes[i] = size;
-    }
-    fOffset = stream->getPosition();
-    return good;
-}
-
-namespace {
-struct PagerCanvas : public SkNWayCanvas {
-    SkPictureRecorder fRecorder;
-    const SkTArray<SkSize>* fSizes;
-    SkTArray<sk_sp<SkPicture>>* fDest;
-    PagerCanvas(SkISize  wh,
-                const SkTArray<SkSize>* s,
-                SkTArray<sk_sp<SkPicture>>* d)
-        : SkNWayCanvas(wh.width(), wh.height()), fSizes(s), fDest(d) {
-        this->nextCanvas();
-    }
-    void nextCanvas() {
-        int i = fDest->count();
-        if (i < fSizes->count()) {
-            SkRect bounds = SkRect::MakeSize((*fSizes)[i]);
-            this->addCanvas(fRecorder.beginRecording(bounds));
-        }
-    }
-    void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
-        if (0 == strcmp(key, SkMultiPictureDocumentProtocol::kEndPage)) {
-            this->removeAll();
-            if (fRecorder.getRecordingCanvas()) {
-                fDest->emplace_back(fRecorder.finishRecordingAsPicture());
-            }
-            this->nextCanvas();
-        } else {
-            this->SkNWayCanvas::onDrawAnnotation(r, key, d);
-        }
-    }
-};
-}  // namespace
-
-sk_sp<SkPicture> SkMultiPictureDocumentReader::readPage(SkStreamSeekable* stream,
-                                                        int pageNumber) const {
-    SkASSERT(pageNumber >= 0);
-    SkASSERT(pageNumber < fSizes.count());
-    if (0 == fPages.count()) {
-        stream->seek(fOffset); // jump to beginning of skp
-        auto picture = SkPicture::MakeFromStream(stream);
-        SkISize size = SkMultiPictureDocumentProtocol::Join(fSizes).toCeil();
-        PagerCanvas canvas(size, &fSizes, &this->fPages);
-        // Must call playback(), not drawPicture() to reach
-        // PagerCanvas::onDrawAnnotation().
-        picture->playback(&canvas);
-        if (fPages.count() != fSizes.count()) {
-            SkDEBUGF(("Malformed SkMultiPictureDocument\n"));
-        }
-    }
-    // Allow for malformed document.
-    return pageNumber < fPages.count() ? fPages[pageNumber] : nullptr;
-}
diff --git a/src/utils/SkMultiPictureDocumentReader.h b/src/utils/SkMultiPictureDocumentReader.h
deleted file mode 100644
index e0473a6..0000000
--- a/src/utils/SkMultiPictureDocumentReader.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkMultiPictureDocumentReader_DEFINED
-#define SkMultiPictureDocumentReader_DEFINED
-
-#include "../private/SkTArray.h"
-#include "SkPicture.h"
-#include "SkSize.h"
-#include "SkStream.h"
-
-/** A lightweight helper class for reading a Skia MultiPictureDocument. */
-class SkMultiPictureDocumentReader {
-public:
-    /** Initialize the MultiPictureDocument.  Does not take ownership
-        of the SkStreamSeekable. */
-    bool init(SkStreamSeekable*);
-
-    /** Return to factory settings. */
-    void reset() {
-        fSizes.reset();
-        fPages.reset();
-    }
-
-    /** Call this after calling init() (otherwise you'll always get zero). */
-    int pageCount() const { return fSizes.count(); }
-
-    /** Deserialize a page from the stream.  Call init() first.  The
-        SkStreamSeekable doesn't need to be the same object, but
-        should point to the same information as before. */
-    sk_sp<SkPicture> readPage(SkStreamSeekable*, int) const;
-
-    /** Fetch the size of the given page, without deserializing the
-        entire page. */
-    SkSize pageSize(int i) const { return fSizes[i]; }
-
-private:
-    SkTArray<SkSize> fSizes;
-    size_t fOffset;
-    mutable SkTArray<sk_sp<SkPicture>> fPages;
-};
-
-#endif  // SkMultiPictureDocumentReader_DEFINED
diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
index 4f6ad46..208f3b4 100644
--- a/src/utils/SkNWayCanvas.cpp
+++ b/src/utils/SkNWayCanvas.cpp
@@ -299,6 +299,13 @@
     }
 }
 
+void SkNWayCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->private_draw_shadow_rec(path, rec);
+    }
+}
+
 void SkNWayCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* data) {
     Iter iter(fList);
     while (iter.next()) {
diff --git a/src/utils/SkPatchUtils.cpp b/src/utils/SkPatchUtils.cpp
index 52ecfb1..5820d85 100644
--- a/src/utils/SkPatchUtils.cpp
+++ b/src/utils/SkPatchUtils.cpp
@@ -8,7 +8,41 @@
 #include "SkPatchUtils.h"
 
 #include "SkColorPriv.h"
+#include "SkColorSpace_Base.h"
 #include "SkGeometry.h"
+#include "SkPM4f.h"
+
+namespace {
+    enum CubicCtrlPts {
+        kTopP0_CubicCtrlPts = 0,
+        kTopP1_CubicCtrlPts = 1,
+        kTopP2_CubicCtrlPts = 2,
+        kTopP3_CubicCtrlPts = 3,
+
+        kRightP0_CubicCtrlPts = 3,
+        kRightP1_CubicCtrlPts = 4,
+        kRightP2_CubicCtrlPts = 5,
+        kRightP3_CubicCtrlPts = 6,
+
+        kBottomP0_CubicCtrlPts = 9,
+        kBottomP1_CubicCtrlPts = 8,
+        kBottomP2_CubicCtrlPts = 7,
+        kBottomP3_CubicCtrlPts = 6,
+
+        kLeftP0_CubicCtrlPts = 0,
+        kLeftP1_CubicCtrlPts = 11,
+        kLeftP2_CubicCtrlPts = 10,
+        kLeftP3_CubicCtrlPts = 9,
+    };
+
+    // Enum for corner also clockwise.
+    enum Corner {
+        kTopLeft_Corner = 0,
+        kTopRight_Corner,
+        kBottomRight_Corner,
+        kBottomLeft_Corner
+    };
+}
 
 /**
  * Evaluator to sample the values of a cubic bezier using forward differences.
@@ -108,29 +142,36 @@
 }
 
 static SkScalar bilerp(SkScalar tx, SkScalar ty, SkScalar c00, SkScalar c10, SkScalar c01,
-                      SkScalar c11) {
+                       SkScalar c11) {
     SkScalar a = c00 * (1.f - tx) + c10 * tx;
     SkScalar b = c01 * (1.f - tx) + c11 * tx;
     return a * (1.f - ty) + b * ty;
 }
 
+static Sk4f bilerp(SkScalar tx, SkScalar ty,
+                   const Sk4f& c00, const Sk4f& c10, const Sk4f& c01, const Sk4f& c11) {
+    Sk4f a = c00 * (1.f - tx) + c10 * tx;
+    Sk4f b = c01 * (1.f - tx) + c11 * tx;
+    return a * (1.f - ty) + b * ty;
+}
+
 SkISize SkPatchUtils::GetLevelOfDetail(const SkPoint cubics[12], const SkMatrix* matrix) {
 
     // Approximate length of each cubic.
     SkPoint pts[kNumPtsCubic];
-    SkPatchUtils::getTopCubic(cubics, pts);
+    SkPatchUtils::GetTopCubic(cubics, pts);
     matrix->mapPoints(pts, kNumPtsCubic);
     SkScalar topLength = approx_arc_length(pts, kNumPtsCubic);
 
-    SkPatchUtils::getBottomCubic(cubics, pts);
+    SkPatchUtils::GetBottomCubic(cubics, pts);
     matrix->mapPoints(pts, kNumPtsCubic);
     SkScalar bottomLength = approx_arc_length(pts, kNumPtsCubic);
 
-    SkPatchUtils::getLeftCubic(cubics, pts);
+    SkPatchUtils::GetLeftCubic(cubics, pts);
     matrix->mapPoints(pts, kNumPtsCubic);
     SkScalar leftLength = approx_arc_length(pts, kNumPtsCubic);
 
-    SkPatchUtils::getRightCubic(cubics, pts);
+    SkPatchUtils::GetRightCubic(cubics, pts);
     matrix->mapPoints(pts, kNumPtsCubic);
     SkScalar rightLength = approx_arc_length(pts, kNumPtsCubic);
 
@@ -141,179 +182,116 @@
     return SkISize::Make(SkMax32(8, lodX), SkMax32(8, lodY));
 }
 
-void SkPatchUtils::getTopCubic(const SkPoint cubics[12], SkPoint points[4]) {
+void SkPatchUtils::GetTopCubic(const SkPoint cubics[12], SkPoint points[4]) {
     points[0] = cubics[kTopP0_CubicCtrlPts];
     points[1] = cubics[kTopP1_CubicCtrlPts];
     points[2] = cubics[kTopP2_CubicCtrlPts];
     points[3] = cubics[kTopP3_CubicCtrlPts];
 }
 
-void SkPatchUtils::getBottomCubic(const SkPoint cubics[12], SkPoint points[4]) {
+void SkPatchUtils::GetBottomCubic(const SkPoint cubics[12], SkPoint points[4]) {
     points[0] = cubics[kBottomP0_CubicCtrlPts];
     points[1] = cubics[kBottomP1_CubicCtrlPts];
     points[2] = cubics[kBottomP2_CubicCtrlPts];
     points[3] = cubics[kBottomP3_CubicCtrlPts];
 }
 
-void SkPatchUtils::getLeftCubic(const SkPoint cubics[12], SkPoint points[4]) {
+void SkPatchUtils::GetLeftCubic(const SkPoint cubics[12], SkPoint points[4]) {
     points[0] = cubics[kLeftP0_CubicCtrlPts];
     points[1] = cubics[kLeftP1_CubicCtrlPts];
     points[2] = cubics[kLeftP2_CubicCtrlPts];
     points[3] = cubics[kLeftP3_CubicCtrlPts];
 }
 
-void SkPatchUtils::getRightCubic(const SkPoint cubics[12], SkPoint points[4]) {
+void SkPatchUtils::GetRightCubic(const SkPoint cubics[12], SkPoint points[4]) {
     points[0] = cubics[kRightP0_CubicCtrlPts];
     points[1] = cubics[kRightP1_CubicCtrlPts];
     points[2] = cubics[kRightP2_CubicCtrlPts];
     points[3] = cubics[kRightP3_CubicCtrlPts];
 }
 
-bool SkPatchUtils::getVertexData(SkPatchUtils::VertexData* data, const SkPoint cubics[12],
-                   const SkColor colors[4], const SkPoint texCoords[4], int lodX, int lodY) {
-    if (lodX < 1 || lodY < 1 || nullptr == cubics || nullptr == data) {
-        return false;
+#include "SkPM4fPriv.h"
+#include "SkColorSpace_Base.h"
+#include "SkColorSpaceXform.h"
+
+struct SkRGBAf {
+    float fVec[4];
+
+    static SkRGBAf From4f(const Sk4f& x) {
+        SkRGBAf c;
+        x.store(c.fVec);
+        return c;
     }
 
-    // check for overflow in multiplication
-    const int64_t lodX64 = (lodX + 1),
-                   lodY64 = (lodY + 1),
-                   mult64 = lodX64 * lodY64;
-    if (mult64 > SK_MaxS32) {
-        return false;
+    static SkRGBAf FromBGRA32(SkColor c) {
+        return From4f(swizzle_rb(SkNx_cast<float>(Sk4b::Load(&c)) * (1/255.0f)));
     }
-    data->fVertexCount = SkToS32(mult64);
 
-    // it is recommended to generate draw calls of no more than 65536 indices, so we never generate
-    // more than 60000 indices. To accomplish that we resize the LOD and vertex count
-    if (data->fVertexCount > 10000 || lodX > 200 || lodY > 200) {
-        SkScalar weightX = static_cast<SkScalar>(lodX) / (lodX + lodY);
-        SkScalar weightY = static_cast<SkScalar>(lodY) / (lodX + lodY);
-
-        // 200 comes from the 100 * 2 which is the max value of vertices because of the limit of
-        // 60000 indices ( sqrt(60000 / 6) that comes from data->fIndexCount = lodX * lodY * 6)
-        lodX = static_cast<int>(weightX * 200);
-        lodY = static_cast<int>(weightY * 200);
-        data->fVertexCount = (lodX + 1) * (lodY + 1);
+    Sk4f to4f() const {
+        return Sk4f::Load(fVec);
     }
-    data->fIndexCount = lodX * lodY * 6;
 
-    data->fPoints = new SkPoint[data->fVertexCount];
-    data->fIndices = new uint16_t[data->fIndexCount];
+    SkColor toBGRA32() const {
+        SkColor color;
+        SkNx_cast<uint8_t>(swizzle_rb(this->to4f()) * Sk4f(255) + Sk4f(0.5f)).store(&color);
+        return color;
+    }
 
-    // if colors is not null then create array for colors
-    SkPMColor colorsPM[kNumCorners];
-    if (colors) {
-        // premultiply colors to avoid color bleeding.
-        for (int i = 0; i < kNumCorners; i++) {
-            colorsPM[i] = SkPreMultiplyColor(colors[i]);
+    SkRGBAf premul() const {
+        float a = fVec[3];
+        return From4f(this->to4f() * Sk4f(a, a, a, 1));
+    }
+
+    SkRGBAf unpremul() const {
+        float a = fVec[3];
+        float inv = a ? 1/a : 0;
+        return From4f(this->to4f() * Sk4f(inv, inv, inv, 1));
+    }
+};
+
+static void skcolor_to_linear(SkRGBAf dst[], const SkColor src[], int count, SkColorSpace* cs,
+                              bool doPremul) {
+    if (cs) {
+        auto srcCS = SkColorSpace::MakeSRGB();
+        auto dstCS = as_CSB(cs)->makeLinearGamma();
+        auto op = doPremul ? SkColorSpaceXform::kPremul_AlphaOp
+                           : SkColorSpaceXform::kPreserve_AlphaOp;
+        SkColorSpaceXform::Apply(dstCS.get(), SkColorSpaceXform::kRGBA_F32_ColorFormat,  dst,
+                                 srcCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, src,
+                                 count, op);
+    } else {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = SkRGBAf::FromBGRA32(src[i]);
+            if (doPremul) {
+                dst[i] = dst[i].premul();
+            }
         }
-        data->fColors = new uint32_t[data->fVertexCount];
     }
-
-    // if texture coordinates are not null then create array for them
-    if (texCoords) {
-        data->fTexCoords = new SkPoint[data->fVertexCount];
-    }
-
-    SkPoint pts[kNumPtsCubic];
-    SkPatchUtils::getBottomCubic(cubics, pts);
-    FwDCubicEvaluator fBottom(pts);
-    SkPatchUtils::getTopCubic(cubics, pts);
-    FwDCubicEvaluator fTop(pts);
-    SkPatchUtils::getLeftCubic(cubics, pts);
-    FwDCubicEvaluator fLeft(pts);
-    SkPatchUtils::getRightCubic(cubics, pts);
-    FwDCubicEvaluator fRight(pts);
-
-    fBottom.restart(lodX);
-    fTop.restart(lodX);
-
-    SkScalar u = 0.0f;
-    int stride = lodY + 1;
-    for (int x = 0; x <= lodX; x++) {
-        SkPoint bottom = fBottom.next(), top = fTop.next();
-        fLeft.restart(lodY);
-        fRight.restart(lodY);
-        SkScalar v = 0.f;
-        for (int y = 0; y <= lodY; y++) {
-            int dataIndex = x * (lodY + 1) + y;
-
-            SkPoint left = fLeft.next(), right = fRight.next();
-
-            SkPoint s0 = SkPoint::Make((1.0f - v) * top.x() + v * bottom.x(),
-                                       (1.0f - v) * top.y() + v * bottom.y());
-            SkPoint s1 = SkPoint::Make((1.0f - u) * left.x() + u * right.x(),
-                                       (1.0f - u) * left.y() + u * right.y());
-            SkPoint s2 = SkPoint::Make(
-                                       (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].x()
-                                                     + u * fTop.getCtrlPoints()[3].x())
-                                       + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].x()
-                                              + u * fBottom.getCtrlPoints()[3].x()),
-                                       (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].y()
-                                                     + u * fTop.getCtrlPoints()[3].y())
-                                       + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].y()
-                                              + u * fBottom.getCtrlPoints()[3].y()));
-            data->fPoints[dataIndex] = s0 + s1 - s2;
-
-            if (colors) {
-                uint8_t a = uint8_t(bilerp(u, v,
-                                   SkScalar(SkColorGetA(colorsPM[kTopLeft_Corner])),
-                                   SkScalar(SkColorGetA(colorsPM[kTopRight_Corner])),
-                                   SkScalar(SkColorGetA(colorsPM[kBottomLeft_Corner])),
-                                   SkScalar(SkColorGetA(colorsPM[kBottomRight_Corner]))));
-                uint8_t r = uint8_t(bilerp(u, v,
-                                   SkScalar(SkColorGetR(colorsPM[kTopLeft_Corner])),
-                                   SkScalar(SkColorGetR(colorsPM[kTopRight_Corner])),
-                                   SkScalar(SkColorGetR(colorsPM[kBottomLeft_Corner])),
-                                   SkScalar(SkColorGetR(colorsPM[kBottomRight_Corner]))));
-                uint8_t g = uint8_t(bilerp(u, v,
-                                   SkScalar(SkColorGetG(colorsPM[kTopLeft_Corner])),
-                                   SkScalar(SkColorGetG(colorsPM[kTopRight_Corner])),
-                                   SkScalar(SkColorGetG(colorsPM[kBottomLeft_Corner])),
-                                   SkScalar(SkColorGetG(colorsPM[kBottomRight_Corner]))));
-                uint8_t b = uint8_t(bilerp(u, v,
-                                   SkScalar(SkColorGetB(colorsPM[kTopLeft_Corner])),
-                                   SkScalar(SkColorGetB(colorsPM[kTopRight_Corner])),
-                                   SkScalar(SkColorGetB(colorsPM[kBottomLeft_Corner])),
-                                   SkScalar(SkColorGetB(colorsPM[kBottomRight_Corner]))));
-                data->fColors[dataIndex] = SkPackARGB32(a,r,g,b);
-            }
-
-            if (texCoords) {
-                data->fTexCoords[dataIndex] = SkPoint::Make(
-                                            bilerp(u, v, texCoords[kTopLeft_Corner].x(),
-                                                   texCoords[kTopRight_Corner].x(),
-                                                   texCoords[kBottomLeft_Corner].x(),
-                                                   texCoords[kBottomRight_Corner].x()),
-                                            bilerp(u, v, texCoords[kTopLeft_Corner].y(),
-                                                   texCoords[kTopRight_Corner].y(),
-                                                   texCoords[kBottomLeft_Corner].y(),
-                                                   texCoords[kBottomRight_Corner].y()));
-
-            }
-
-            if(x < lodX && y < lodY) {
-                int i = 6 * (x * lodY + y);
-                data->fIndices[i] = x * stride + y;
-                data->fIndices[i + 1] = x * stride + 1 + y;
-                data->fIndices[i + 2] = (x + 1) * stride + 1 + y;
-                data->fIndices[i + 3] = data->fIndices[i];
-                data->fIndices[i + 4] = data->fIndices[i + 2];
-                data->fIndices[i + 5] = (x + 1) * stride + y;
-            }
-            v = SkScalarClampMax(v + 1.f / lodY, 1);
-        }
-        u = SkScalarClampMax(u + 1.f / lodX, 1);
-    }
-    return true;
-
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+static void linear_to_skcolor(SkColor dst[], const SkRGBAf src[], int count, SkColorSpace* cs) {
+    if (cs) {
+        auto srcCS = as_CSB(cs)->makeLinearGamma();
+        auto dstCS = SkColorSpace::MakeSRGB();
+        SkColorSpaceXform::Apply(dstCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, dst,
+                                 srcCS.get(), SkColorSpaceXform::kRGBA_F32_ColorFormat,  src,
+                                 count, SkColorSpaceXform::kPreserve_AlphaOp);
+    } else {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = src[i].toBGRA32();
+        }
+    }
+}
+
+static void unpremul(SkRGBAf array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = array[i].unpremul();
+    }
+}
 
 sk_sp<SkVertices> SkPatchUtils::MakeVertices(const SkPoint cubics[12], const SkColor srcColors[4],
-                                            const SkPoint srcTexCoords[4], int lodX, int lodY) {
+                                             const SkPoint srcTexCoords[4], int lodX, int lodY,
+                                             bool interpColorsLinearly) {
     if (lodX < 1 || lodY < 1 || nullptr == cubics) {
         return nullptr;
     }
@@ -348,30 +326,44 @@
         flags |= SkVertices::kHasColors_BuilderFlag;
     }
 
-    SkVertices::Builder builder(SkCanvas::kTriangles_VertexMode, vertexCount, indexCount, flags);
+    SkSTArenaAlloc<2048> alloc;
+    SkRGBAf* cornerColors = srcColors ? alloc.makeArray<SkRGBAf>(4) : nullptr;
+    SkRGBAf* tmpColors = srcColors ? alloc.makeArray<SkRGBAf>(vertexCount) : nullptr;
+    auto convertCS = interpColorsLinearly ? SkColorSpace::MakeSRGB() : nullptr;
+
+    SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vertexCount, indexCount, flags);
     SkPoint* pos = builder.positions();
     SkPoint* texs = builder.texCoords();
-    SkColor* colors = builder.colors();
     uint16_t* indices = builder.indices();
+    bool is_opaque = false;
 
-    // if colors is not null then create array for colors
-    SkPMColor colorsPM[kNumCorners];
-    if (srcColors) {
-        // premultiply colors to avoid color bleeding.
+    /*
+     *  1. Should we offer this as a runtime choice, as we do in gradients?
+     *  2. Since drawing the vertices wants premul, shoudl we extend SkVertices to store
+     *     premul colors (as floats, w/ a colorspace)?
+     */
+    bool doPremul = true;
+    if (cornerColors) {
+        SkColor c = ~0;
         for (int i = 0; i < kNumCorners; i++) {
-            colorsPM[i] = SkPreMultiplyColor(srcColors[i]);
+            c &= srcColors[i];
         }
-        srcColors = colorsPM;
+        is_opaque = (SkColorGetA(c) == 0xFF);
+        if (is_opaque) {
+            doPremul = false;   // no need
+        }
+
+        skcolor_to_linear(cornerColors, srcColors, kNumCorners, convertCS.get(), doPremul);
     }
 
     SkPoint pts[kNumPtsCubic];
-    SkPatchUtils::getBottomCubic(cubics, pts);
+    SkPatchUtils::GetBottomCubic(cubics, pts);
     FwDCubicEvaluator fBottom(pts);
-    SkPatchUtils::getTopCubic(cubics, pts);
+    SkPatchUtils::GetTopCubic(cubics, pts);
     FwDCubicEvaluator fTop(pts);
-    SkPatchUtils::getLeftCubic(cubics, pts);
+    SkPatchUtils::GetLeftCubic(cubics, pts);
     FwDCubicEvaluator fLeft(pts);
-    SkPatchUtils::getRightCubic(cubics, pts);
+    SkPatchUtils::GetRightCubic(cubics, pts);
     FwDCubicEvaluator fRight(pts);
 
     fBottom.restart(lodX);
@@ -404,28 +396,14 @@
                                               + u * fBottom.getCtrlPoints()[3].y()));
             pos[dataIndex] = s0 + s1 - s2;
 
-            if (colors) {
-                uint8_t a = uint8_t(bilerp(u, v,
-                                           SkScalar(SkColorGetA(colorsPM[kTopLeft_Corner])),
-                                           SkScalar(SkColorGetA(colorsPM[kTopRight_Corner])),
-                                           SkScalar(SkColorGetA(colorsPM[kBottomLeft_Corner])),
-                                           SkScalar(SkColorGetA(colorsPM[kBottomRight_Corner]))));
-                uint8_t r = uint8_t(bilerp(u, v,
-                                           SkScalar(SkColorGetR(colorsPM[kTopLeft_Corner])),
-                                           SkScalar(SkColorGetR(colorsPM[kTopRight_Corner])),
-                                           SkScalar(SkColorGetR(colorsPM[kBottomLeft_Corner])),
-                                           SkScalar(SkColorGetR(colorsPM[kBottomRight_Corner]))));
-                uint8_t g = uint8_t(bilerp(u, v,
-                                           SkScalar(SkColorGetG(colorsPM[kTopLeft_Corner])),
-                                           SkScalar(SkColorGetG(colorsPM[kTopRight_Corner])),
-                                           SkScalar(SkColorGetG(colorsPM[kBottomLeft_Corner])),
-                                           SkScalar(SkColorGetG(colorsPM[kBottomRight_Corner]))));
-                uint8_t b = uint8_t(bilerp(u, v,
-                                           SkScalar(SkColorGetB(colorsPM[kTopLeft_Corner])),
-                                           SkScalar(SkColorGetB(colorsPM[kTopRight_Corner])),
-                                           SkScalar(SkColorGetB(colorsPM[kBottomLeft_Corner])),
-                                           SkScalar(SkColorGetB(colorsPM[kBottomRight_Corner]))));
-                colors[dataIndex] = SkPackARGB32(a,r,g,b);
+            if (cornerColors) {
+                bilerp(u, v, cornerColors[kTopLeft_Corner].to4f(),
+                             cornerColors[kTopRight_Corner].to4f(),
+                             cornerColors[kBottomLeft_Corner].to4f(),
+                             cornerColors[kBottomRight_Corner].to4f()).store(tmpColors[dataIndex].fVec);
+                if (is_opaque) {
+                    tmpColors[dataIndex].fVec[3] = 1;
+                }
             }
 
             if (texs) {
@@ -453,5 +431,12 @@
         }
         u = SkScalarClampMax(u + 1.f / lodX, 1);
     }
+
+    if (tmpColors) {
+        if (doPremul) {
+            unpremul(tmpColors, vertexCount);
+        }
+        linear_to_skcolor(builder.colors(), tmpColors, vertexCount, convertCS.get());
+    }
     return builder.detach();
 }
diff --git a/src/utils/SkPatchUtils.h b/src/utils/SkPatchUtils.h
index c1e8ac1..75c10e8 100644
--- a/src/utils/SkPatchUtils.h
+++ b/src/utils/SkPatchUtils.h
@@ -15,66 +15,7 @@
 class SK_API SkPatchUtils {
 
 public:
-    // DEPRECATED -- use MakeVertices()
-    /**
-     * Structure that holds the vertex data related to the tessellation of a patch. It is passed
-     * as a parameter to the function getVertexData which sets the points, colors and texture
-     * coordinates of the vertices and the indices for them to be drawn as triangles.
-     */
-    struct VertexData {
-        int fVertexCount, fIndexCount;
-        SkPoint* fPoints;
-        SkPoint* fTexCoords;
-        uint32_t* fColors;
-        uint16_t* fIndices;
-
-        VertexData()
-        : fVertexCount(0)
-        , fIndexCount(0)
-        , fPoints(nullptr)
-        , fTexCoords(nullptr)
-        , fColors(nullptr)
-        , fIndices(nullptr) { }
-
-        ~VertexData() {
-            delete[] fPoints;
-            delete[] fTexCoords;
-            delete[] fColors;
-            delete[] fIndices;
-        }
-    };
-
     // Enums for control points based on the order specified in the constructor (clockwise).
-    enum CubicCtrlPts {
-        kTopP0_CubicCtrlPts = 0,
-        kTopP1_CubicCtrlPts = 1,
-        kTopP2_CubicCtrlPts = 2,
-        kTopP3_CubicCtrlPts = 3,
-
-        kRightP0_CubicCtrlPts = 3,
-        kRightP1_CubicCtrlPts = 4,
-        kRightP2_CubicCtrlPts = 5,
-        kRightP3_CubicCtrlPts = 6,
-
-        kBottomP0_CubicCtrlPts = 9,
-        kBottomP1_CubicCtrlPts = 8,
-        kBottomP2_CubicCtrlPts = 7,
-        kBottomP3_CubicCtrlPts = 6,
-
-        kLeftP0_CubicCtrlPts = 0,
-        kLeftP1_CubicCtrlPts = 11,
-        kLeftP2_CubicCtrlPts = 10,
-        kLeftP3_CubicCtrlPts = 9,
-    };
-
-    // Enum for corner also clockwise.
-    enum Corner {
-        kTopLeft_Corner = 0,
-        kTopRight_Corner,
-        kBottomRight_Corner,
-        kBottomLeft_Corner
-    };
-
     enum {
         kNumCtrlPts = 12,
         kNumCorners = 4,
@@ -82,46 +23,33 @@
     };
 
     /**
-     * Method that calculates a level of detail (number of subdivisions) for a patch in both axis.
-     */
-    static SkISize GetLevelOfDetail(const SkPoint cubics[12], const SkMatrix* matrix);
-
-    /**
      * Get the points corresponding to the top cubic of cubics.
      */
-    static void getTopCubic(const SkPoint cubics[12], SkPoint points[4]);
+    static void GetTopCubic(const SkPoint cubics[12], SkPoint points[4]);
 
     /**
      * Get the points corresponding to the bottom cubic of cubics.
      */
-    static void getBottomCubic(const SkPoint cubics[12], SkPoint points[4]);
+    static void GetBottomCubic(const SkPoint cubics[12], SkPoint points[4]);
 
     /**
      * Get the points corresponding to the left cubic of cubics.
      */
-    static void getLeftCubic(const SkPoint cubics[12], SkPoint points[4]);
+    static void GetLeftCubic(const SkPoint cubics[12], SkPoint points[4]);
 
     /**
      * Get the points corresponding to the right cubic of cubics.
      */
-    static void getRightCubic(const SkPoint cubics[12], SkPoint points[4]);
+    static void GetRightCubic(const SkPoint cubics[12], SkPoint points[4]);
 
-    // DEPRECATED -- use MakeVertices()
     /**
-     * Function that evaluates the coons patch interpolation.
-     * data refers to the pointer of the PatchData struct in which the tessellation data is set.
-     * cubics refers to the points of the cubics.
-     * lod refers the level of detail for each axis.
-     * colors refers to the corner colors that will be bilerp across the patch (optional parameter)
-     * texCoords refers to the corner texture coordinates that will be bilerp across the patch
-        (optional parameter)
+     * Method that calculates a level of detail (number of subdivisions) for a patch in both axis.
      */
-    static bool getVertexData(SkPatchUtils::VertexData* data, const SkPoint cubics[12],
-                              const SkColor colors[4], const SkPoint texCoords[4],
-                              int lodX, int lodY);
+    static SkISize GetLevelOfDetail(const SkPoint cubics[12], const SkMatrix* matrix);
 
     static sk_sp<SkVertices> MakeVertices(const SkPoint cubics[12], const SkColor colors[4],
-                                          const SkPoint texCoords[4], int lodX, int lodY);
+                                          const SkPoint texCoords[4], int lodX, int lodY,
+                                          bool interpColorsLinearly = false);
 };
 
 #endif
diff --git a/src/utils/SkRGBAToYUV.cpp b/src/utils/SkRGBAToYUV.cpp
deleted file mode 100644
index 0528b14..0000000
--- a/src/utils/SkRGBAToYUV.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 "SkRGBAToYUV.h"
-#include "SkCanvas.h"
-#include "SkColorMatrixFilterRowMajor255.h"
-#include "SkImage.h"
-#include "SkPaint.h"
-#include "SkSurface.h"
-
-bool SkRGBAToYUV(const SkImage* image, const SkISize sizes[3], void* const planes[3],
-                 const size_t rowBytes[3], SkYUVColorSpace colorSpace) {
-    // Matrices that go from RGBA to YUV.
-    static const SkScalar kYUVColorSpaceInvMatrices[][15] = {
-        // kJPEG_SkYUVColorSpace
-        { 0.299001f,  0.586998f,   0.114001f,  0.f, 0.0000821798f * 255.f,
-         -0.168736f, -0.331263f,   0.499999f,  0.f, 0.499954f * 255.f,
-          0.499999f, -0.418686f,  -0.0813131f, 0.f, 0.499941f * 255.f},
-
-        // kRec601_SkYUVColorSpace
-        { 0.256951f,  0.504421f,   0.0977346f, 0.f, 0.0625f * 255.f,
-         -0.148212f, -0.290954f,   0.439166f,  0.f, 0.5f * 255.f,
-          0.439166f,  -0.367886f, -0.0712802f, 0.f, 0.5f * 255.f},
-
-        // kRec709_SkYUVColorSpace
-        { 0.182663f,  0.614473f,  0.061971f,  0.f, 0.0625f * 255.f,
-         -0.100672f, -0.338658f,  0.43933f,   0.f, 0.5f * 255.f,
-          0.439142f, -0.39891f,  -0.040231f,  0.f, 0.5f * 255.f},
-    };
-    static_assert(kLastEnum_SkYUVColorSpace == 2, "yuv color matrix array problem");
-    static_assert(kJPEG_SkYUVColorSpace     == 0, "yuv color matrix array problem");
-    static_assert(kRec601_SkYUVColorSpace   == 1, "yuv color matrix array problem");
-    static_assert(kRec709_SkYUVColorSpace   == 2, "yuv color matrix array problem");
-
-    for (int i = 0; i < 3; ++i) {
-        size_t rb = rowBytes[i] ? rowBytes[i] : sizes[i].fWidth;
-        auto surface(SkSurface::MakeRasterDirect(
-                SkImageInfo::MakeA8(sizes[i].fWidth, sizes[i].fHeight), planes[i], rb));
-        if (!surface) {
-            return false;
-        }
-        SkPaint paint;
-        paint.setFilterQuality(kLow_SkFilterQuality);
-        paint.setBlendMode(SkBlendMode::kSrc);
-        int rowStartIdx = 5 * i;
-        const SkScalar* row = kYUVColorSpaceInvMatrices[colorSpace] + rowStartIdx;
-        paint.setColorFilter(
-                SkColorMatrixFilterRowMajor255::MakeSingleChannelOutput(row));
-        surface->getCanvas()->drawImageRect(image, SkIRect::MakeWH(image->width(), image->height()),
-                                            SkRect::MakeIWH(surface->width(), surface->height()),
-                                            &paint);
-    }
-    return true;
-}
diff --git a/src/utils/SkRGBAToYUV.h b/src/utils/SkRGBAToYUV.h
deleted file mode 100644
index 5c3c1b1..0000000
--- a/src/utils/SkRGBAToYUV.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRGBAToYUV_DEFINED
-#define SkRGBAToYUV_DEFINED
-
-#include "SkPixmap.h"
-#include "SkSize.h"
-
-class SkImage;
-// Works with any image type at the moment, but in the future it may only work with raster-backed
-// images. This really should take a SkPixmap for the input, however the implementation for the
-// time being requires an image.
-bool SkRGBAToYUV(const SkImage*, const SkISize [3], void* const planes[3],
-                 const size_t rowBytes[3], SkYUVColorSpace);
-
-#endif
diff --git a/src/utils/SkShadowPaintFilterCanvas.cpp b/src/utils/SkShadowPaintFilterCanvas.cpp
deleted file mode 100644
index 289ae3c..0000000
--- a/src/utils/SkShadowPaintFilterCanvas.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * 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 "SkPathEffect.h"
-#include "SkShadowPaintFilterCanvas.h"
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-SkShadowPaintFilterCanvas::SkShadowPaintFilterCanvas(SkCanvas *canvas)
-        : SkPaintFilterCanvas(canvas) {
-    fShadowParams.fShadowRadius = 0.0f;
-    fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType;
-    fShadowParams.fBiasingConstant = 0.0f;
-    fShadowParams.fMinVariance = 0.0f;
-}
-
-// TODO use a shader instead
-bool SkShadowPaintFilterCanvas::onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const {
-    if (*paint) {
-        int z = this->getZ();
-        SkASSERT(z <= 0xFF && z >= 0x00);
-
-        SkPaint newPaint;
-        newPaint.setPathEffect(sk_ref_sp<SkPathEffect>((*paint)->getPathEffect()));
-
-        SkColor color = 0xFF000000; // init color to opaque black
-        color |= z; // Put the index into the blue component
-
-        if (fShadowParams.fType == SkShadowParams::kVariance_ShadowType) {
-            int z2 = z * z;
-            if (z2 > 255 * 256) {
-                color |= 0xff00;
-            } else {
-                // Let's only store the more significant bits of z2 to save space.
-                // In practice, this should barely impact shadow blur quality.
-                color |= z2 & 0x0000ff00;
-            }
-        }
-        newPaint.setColor(color);
-
-        *paint->writable() = newPaint;
-    }
-
-    return true;
-}
-
-SkISize SkShadowPaintFilterCanvas::ComputeDepthMapSize(const SkLights::Light& light, int maxDepth,
-                                                       int width, int height) {
-    if (light.type() != SkLights::Light::kDirectional_LightType) {
-        // Calculating the right depth map size for point lights is complex,
-        // as it depends on the max depth, the max depth delta, the location
-        // of the point light and the shapes, etc... If we take upper bounds
-        // on those metrics, the shadow map will be pretty big in any case.
-        // Thus, just using 4x the width and height seems to work for most scenes.
-        return SkISize::Make(width * 4, height * 4);
-    }
-
-    int dMapWidth = SkMin32(maxDepth * fabs(light.dir().fX) + width,
-                            width * 2);
-    int dMapHeight = SkMin32(maxDepth * fabs(light.dir().fY) + height,
-                             height * 2);
-    return SkISize::Make(dMapWidth, dMapHeight);
-}
-
-void SkShadowPaintFilterCanvas::setShadowParams(const SkShadowParams &params) {
-    fShadowParams = params;
-}
-
-void SkShadowPaintFilterCanvas::onDrawPicture(const SkPicture *picture, const SkMatrix *matrix,
-                                              const SkPaint *paint) {
-    SkTCopyOnFirstWrite<SkPaint> filteredPaint(paint);
-    if (this->onFilter(&filteredPaint, kPicture_Type)) {
-        SkCanvas::onDrawPicture(picture, matrix, filteredPaint);
-    }
-}
-
-void SkShadowPaintFilterCanvas::updateMatrix() {
-    //  It is up to the user to set the 0th light in fLights to
-    //  the light the want to render the depth map with.
-    if (this->fLights->light(0).type() == SkLights::Light::kDirectional_LightType) {
-        const SkVector3& lightDir = this->fLights->light(0).dir();
-        SkScalar x = lightDir.fX * this->getZ();
-        SkScalar y = lightDir.fY * this->getZ();
-
-        this->translate(x, y);
-    } else if (this->fLights->light(0).type() == SkLights::Light::kPoint_LightType) {
-        SkISize size = this->getBaseLayerSize();
-
-        SkPoint3 lightPos = this->fLights->light(0).pos();
-
-        // shadow maps for point lights are 4x the size of the diffuse map, by experimentation
-        // (see SPFCanvas::ComputeDepthMapSize())
-        SkScalar diffuseHeight = size.fHeight / 4.0f;
-
-        // move point light with canvas's CTM
-        SkPoint lightPoint = SkPoint::Make(lightPos.fX, diffuseHeight - lightPos.fY);
-        SkMatrix mat = this->getTotalMatrix();
-        if (mat.invert(&mat)) {
-            mat.mapPoints(&lightPoint, 1);
-        }
-        lightPoint.set(lightPoint.fX, diffuseHeight - lightPoint.fY);
-
-        // center the shadow map
-        // note: the 3/8 constant is specific to the 4.0 depth map size multiplier
-        mat = this->getTotalMatrix();
-        mat.postTranslate(size.width() * 0.375f, size.height() * 0.375f);
-        this->setMatrix(mat);
-
-        // project shapes onto canvas as shadows
-        SkScalar scale = (lightPos.fZ) / (lightPos.fZ - this->getZ());
-        this->scale(scale, scale);
-
-        this->translate(-lightPoint.fX * this->getZ() /
-                        ((lightPos.fZ - this->getZ()) * scale),
-                        -(diffuseHeight - lightPoint.fY) * this->getZ() /
-                        ((lightPos.fZ - this->getZ()) * scale));
-    }
-}
-
-void SkShadowPaintFilterCanvas::onDrawPaint(const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawPaint(paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
-                                             const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawPoints(mode, count, pts, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawRect(const SkRect &rect, const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawRect(rect, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawRRect(const SkRRect &rrect, const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawRRect(rrect, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawDRRect(const SkRRect &outer, const SkRRect &inner,
-                  const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawDRRect(outer, inner, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawOval(const SkRect &rect, const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawOval(rect, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawArc(const SkRect &rect, SkScalar startAngle,
-                                          SkScalar sweepAngle, bool useCenter,
-                                          const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawArc(rect, startAngle, sweepAngle, useCenter, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawPath(const SkPath &path, const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawPath(path, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawBitmap(const SkBitmap &bm, SkScalar left, SkScalar top,
-                                             const SkPaint *paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawBitmap(bm, left, top, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawBitmapRect(const SkBitmap &bm, const SkRect *src,
-                                                 const SkRect &dst, const SkPaint *paint,
-                                                 SrcRectConstraint constraint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawBitmapRect(bm, src, dst, paint, constraint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawBitmapNine(const SkBitmap &bm, const SkIRect &center,
-                                                 const SkRect &dst, const SkPaint *paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawBitmapNine(bm, center, dst, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawImage(const SkImage *image, SkScalar left,
-                                            SkScalar top, const SkPaint *paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawImage(image, left, top, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawImageRect(const SkImage *image, const SkRect *src,
-                                                const SkRect &dst, const SkPaint *paint,
-                                                SrcRectConstraint constraint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawImageRect(image, src, dst, paint, constraint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawImageNine(const SkImage *image, const SkIRect &center,
-                                                const SkRect &dst, const SkPaint *paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawImageNine(image, center, dst, paint);
-    this->restore();
-}
-
-
-void SkShadowPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
-                                               const SkPoint vertices[], const SkPoint texs[],
-                                               const SkColor colors[], SkXfermode *xmode,
-                                               const uint16_t indices[], int indexCount,
-                                               const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawVertices(vmode, vertexCount, vertices, texs, colors,
-                                    xmode, indices, indexCount, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawPatch(const SkPoint cubics[], const SkColor colors[],
-                                            const SkPoint texCoords[], SkXfermode *xmode,
-                                            const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawText(const void *text, size_t byteLength, SkScalar x,
-                                           SkScalar y, const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawText(text, byteLength, x, y, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawPosText(const void *text, size_t byteLength,
-                                              const SkPoint pos[], const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawPosText(text, byteLength, pos, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawPosTextH(const void *text, size_t byteLength,
-                                               const SkScalar xpos[],
-                                               SkScalar constY, const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawTextOnPath(const void *text, size_t byteLength,
-                                                 const SkPath &path, const SkMatrix *matrix,
-                                                 const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawTextRSXform(const void *text, size_t byteLength,
-                                                  const SkRSXform xform[], const SkRect *cull,
-                                                  const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawTextRSXform(text, byteLength, xform, cull, paint);
-    this->restore();
-}
-
-void SkShadowPaintFilterCanvas::onDrawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y,
-                                               const SkPaint &paint) {
-    this->save();
-    this->updateMatrix();
-    this->INHERITED::onDrawTextBlob(blob, x, y, paint);
-    this->restore();
-}
-
-#endif
diff --git a/src/utils/SkShadowPaintFilterCanvas.h b/src/utils/SkShadowPaintFilterCanvas.h
deleted file mode 100644
index 190c68b..0000000
--- a/src/utils/SkShadowPaintFilterCanvas.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkShadowPaintFilterCanvas_DEFINED
-#define SkShadowPaintFilterCanvas_DEFINED
-
-#include "SkPaintFilterCanvas.h"
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-
-/** \class SkShadowPaintFilterCanvas
- *
- *  A utility proxy class for implementing shadow maps.
- *
- *  We override the onFilter method to draw depths into the canvas
- *  depending on the current draw depth of the canvas, throwing out
- *  the actual draw color.
- *
- *  Note that we can only do this for one light at a time!
- *  It is up to the user to set the 0th light in fLights to
- *  the light the want to render the depth map with.
- */
-class SkShadowPaintFilterCanvas : public SkPaintFilterCanvas {
-public:
-
-    SkShadowPaintFilterCanvas(SkCanvas *canvas);
-
-    // TODO use a shader instead
-    bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const override;
-
-    static SkISize ComputeDepthMapSize(const SkLights::Light& light, int maxDepth,
-                                       int width, int height);
-
-    void setShadowParams(const SkShadowParams &params);
-protected:
-    void updateMatrix();
-
-    void onDrawPicture(const SkPicture *picture, const SkMatrix *matrix,
-                       const SkPaint *paint) override;
-
-    void onDrawPaint(const SkPaint &paint) override;
-
-    void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
-                      const SkPaint &paint) override;
-
-    void onDrawRect(const SkRect &rect, const SkPaint &paint) override;
-
-    void onDrawRRect(const SkRRect &rrect, const SkPaint &paint) override;
-
-    void onDrawDRRect(const SkRRect &outer, const SkRRect &inner,
-                      const SkPaint &paint) override;
-
-    void onDrawOval(const SkRect &rect, const SkPaint &paint) override;
-
-    void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override;
-
-    void onDrawPath(const SkPath &path, const SkPaint &paint) override;
-
-    void onDrawBitmap(const SkBitmap &bm, SkScalar left, SkScalar top,
-                      const SkPaint *paint) override;
-
-    void onDrawBitmapRect(const SkBitmap &bm, const SkRect *src, const SkRect &dst,
-                          const SkPaint *paint, SrcRectConstraint constraint) override;
-
-    void onDrawBitmapNine(const SkBitmap &bm, const SkIRect &center,
-                          const SkRect &dst, const SkPaint *paint) override;
-
-    void onDrawImage(const SkImage *image, SkScalar left, SkScalar top,
-                     const SkPaint *paint) override;
-
-    void onDrawImageRect(const SkImage *image, const SkRect *src,
-                         const SkRect &dst, const SkPaint *paint,
-                         SrcRectConstraint constraint) override;
-
-    void onDrawImageNine(const SkImage *image, const SkIRect &center,
-                         const SkRect &dst, const SkPaint *paint) override;
-
-    void onDrawVertices(VertexMode vmode, int vertexCount,
-                        const SkPoint vertices[], const SkPoint texs[],
-                        const SkColor colors[], SkXfermode *xmode,
-                        const uint16_t indices[], int indexCount,
-                        const SkPaint &paint) override;
-
-    void onDrawPatch(const SkPoint cubics[], const SkColor colors[],
-                     const SkPoint texCoords[], SkXfermode *xmode,
-                     const SkPaint &paint) override;
-
-    void onDrawText(const void *text, size_t byteLength, SkScalar x, SkScalar y,
-                    const SkPaint &paint) override;
-
-    void onDrawPosText(const void *text, size_t byteLength, const SkPoint pos[],
-                       const SkPaint &paint) override;
-
-    void onDrawPosTextH(const void *text, size_t byteLength, const SkScalar xpos[],
-                        SkScalar constY, const SkPaint &paint) override;
-
-    void onDrawTextOnPath(const void *text, size_t byteLength, const SkPath &path,
-                          const SkMatrix *matrix, const SkPaint &paint) override;
-
-    void onDrawTextRSXform(const void *text, size_t byteLength,
-                           const SkRSXform xform[], const SkRect *cull,
-                           const SkPaint &paint) override;
-
-    void onDrawTextBlob(const SkTextBlob *blob, SkScalar x,
-                        SkScalar y, const SkPaint &paint) override;
-private:
-    SkShadowParams fShadowParams;
-    typedef SkPaintFilterCanvas INHERITED;
-};
-
-
-#endif
-#endif
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index 3c19cec..c84490f 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -22,14 +22,14 @@
  */
 class SkBaseShadowTessellator {
 public:
-    SkBaseShadowTessellator(SkShadowTessellator::HeightFunc, bool transparent);
+    SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent);
     virtual ~SkBaseShadowTessellator() {}
 
     sk_sp<SkVertices> releaseVertices() {
         if (!fSucceeded) {
             return nullptr;
         }
-        return SkVertices::MakeCopy(SkCanvas::kTriangles_VertexMode, this->vertexCount(),
+        return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
                                     fPositions.begin(), nullptr, fColors.begin(),
                                     this->indexCount(), fIndices.begin());
     }
@@ -54,13 +54,17 @@
 
     bool setTransformedHeightFunc(const SkMatrix& ctm);
 
-    void addArc(const SkVector& nextNormal, bool finishArc);
+    bool addArc(const SkVector& nextNormal, bool finishArc);
 
-    SkShadowTessellator::HeightFunc         fHeightFunc;
+    SkScalar heightFunc(SkScalar x, SkScalar y) {
+        return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
+    }
+
+    SkPoint3                                fZPlaneParams;
     std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
     SkScalar                                fZOffset;
     // members for perspective height function
-    SkScalar                                fZParams[3];
+    SkPoint3                                fTransformedZParams;
     SkScalar                                fPartialDeterminants[3];
 
     // first two points
@@ -72,8 +76,8 @@
     SkTDArray<SkColor>  fColors;
     SkTDArray<uint16_t> fIndices;
 
-    int                 fFirstVertex;
-    SkVector            fFirstNormal;
+    int                 fFirstVertexIndex;
+    SkVector            fFirstOutset;
     SkPoint             fFirstPoint;
 
     bool                fSucceeded;
@@ -85,7 +89,7 @@
     SkScalar            fRadius;
     SkScalar            fDirection;
     int                 fPrevUmbraIndex;
-    SkVector            fPrevNormal;
+    SkVector            fPrevOutset;
     SkPoint             fPrevPoint;
 };
 
@@ -105,24 +109,23 @@
 
 static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
                                  SkScalar* rotSin, SkScalar* rotCos, int* n) {
-    const SkScalar kRecipPixelsPerArcSegment = 0.25f;
+    const SkScalar kRecipPixelsPerArcSegment = 0.125f;
 
     SkScalar rCos = v1.dot(v2);
     SkScalar rSin = v1.cross(v2);
     SkScalar theta = SkScalarATan2(rSin, rCos);
 
-    SkScalar steps = r*theta*kRecipPixelsPerArcSegment;
+    int steps = SkScalarFloorToInt(r*theta*kRecipPixelsPerArcSegment);
 
     SkScalar dTheta = theta / steps;
     *rotSin = SkScalarSinCos(dTheta, rotCos);
-    *n = SkScalarFloorToInt(steps);
+    *n = steps;
 }
 
-SkBaseShadowTessellator::SkBaseShadowTessellator(SkShadowTessellator::HeightFunc heightFunc,
-                                                 bool transparent)
-        : fHeightFunc(heightFunc)
+SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
+        : fZPlaneParams(zPlaneParams)
         , fZOffset(0)
-        , fFirstVertex(-1)
+        , fFirstVertexIndex(-1)
         , fSucceeded(false)
         , fTransparent(transparent)
         , fDirection(1)
@@ -133,17 +136,17 @@
 }
 
 bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
-    SkScalar minZ = fHeightFunc(bounds.fLeft, bounds.fTop);
+    SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
     if (perspective) {
-        SkScalar z = fHeightFunc(bounds.fLeft, bounds.fBottom);
+        SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
         if (z < minZ) {
             minZ = z;
         }
-        z = fHeightFunc(bounds.fRight, bounds.fTop);
+        z = this->heightFunc(bounds.fRight, bounds.fTop);
         if (z < minZ) {
             minZ = z;
         }
-        z = fHeightFunc(bounds.fRight, bounds.fBottom);
+        z = this->heightFunc(bounds.fRight, bounds.fBottom);
         if (z < minZ) {
             minZ = z;
         }
@@ -234,86 +237,96 @@
     }
 }
 
-void SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
+bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
     // fill in fan from previous quad
     SkScalar rotSin, rotCos;
     int numSteps;
-    compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
-    SkVector prevNormal = fPrevNormal;
-    for (int i = 0; i < numSteps; ++i) {
+    compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
+    SkVector prevNormal = fPrevOutset;
+    for (int i = 0; i < numSteps-1; ++i) {
         SkVector currNormal;
         currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
         currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
         *fPositions.push() = fPrevPoint + currNormal;
         *fColors.push() = fPenumbraColor;
         *fIndices.push() = fPrevUmbraIndex;
-        *fIndices.push() = fPositions.count() - 2;
         *fIndices.push() = fPositions.count() - 1;
+        *fIndices.push() = fPositions.count() - 2;
 
         prevNormal = currNormal;
     }
-    if (finishArc) {
+    if (finishArc && numSteps) {
         *fPositions.push() = fPrevPoint + nextNormal;
         *fColors.push() = fPenumbraColor;
         *fIndices.push() = fPrevUmbraIndex;
-        *fIndices.push() = fPositions.count() - 2;
         *fIndices.push() = fPositions.count() - 1;
+        *fIndices.push() = fPositions.count() - 2;
     }
-    fPrevNormal = nextNormal;
+    fPrevOutset = nextNormal;
+
+    return (numSteps > 0);
 }
 
 bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
-    if (!ctm.hasPerspective()) {
+    if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
         fTransformedHeightFunc = [this](const SkPoint& p) {
-            return this->fHeightFunc(0, 0);
+            return fZPlaneParams.fZ;
         };
     } else {
         SkMatrix ctmInverse;
         if (!ctm.invert(&ctmInverse)) {
             return false;
         }
-        SkScalar C = fHeightFunc(0, 0);
-        SkScalar A = fHeightFunc(1, 0) - C;
-        SkScalar B = fHeightFunc(0, 1) - C;
-
         // multiply by transpose
-        fZParams[0] = ctmInverse[SkMatrix::kMScaleX] * A +
-                      ctmInverse[SkMatrix::kMSkewY] * B +
-                      ctmInverse[SkMatrix::kMPersp0] * C;
-        fZParams[1] = ctmInverse[SkMatrix::kMSkewX] * A +
-                      ctmInverse[SkMatrix::kMScaleY] * B +
-                      ctmInverse[SkMatrix::kMPersp1] * C;
-        fZParams[2] = ctmInverse[SkMatrix::kMTransX] * A +
-                      ctmInverse[SkMatrix::kMTransY] * B +
-                      ctmInverse[SkMatrix::kMPersp2] * C;
+        fTransformedZParams = SkPoint3::Make(
+            ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
+            ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
+            ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
 
-        // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
-        // so pre-compute those values that are independent of X and Y.
-        // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
-        fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
-                                  ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
-        fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
-                                  ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
-        fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
-                                  ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
-        SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
-                                  ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
-                                  ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
+            ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
+            ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
+            ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
 
-        // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
-        // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
-        fZParams[0] *= ctmDeterminant;
-        fZParams[1] *= ctmDeterminant;
-        fZParams[2] *= ctmDeterminant;
+            ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
+            ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
+            ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
+        );
 
-        fTransformedHeightFunc = [this](const SkPoint& p) {
-            SkScalar denom = p.fX * this->fPartialDeterminants[0] +
-                             p.fY * this->fPartialDeterminants[1] +
-                             this->fPartialDeterminants[2];
-            SkScalar w = SkScalarFastInvert(denom);
-            return (this->fZParams[0] * p.fX + this->fZParams[1] * p.fY + this->fZParams[2])*w +
-                   this->fZOffset;
-        };
+        if (ctm.hasPerspective()) {
+            // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
+            // so pre-compute those values that are independent of X and Y.
+            // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
+            fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
+                                      ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
+            fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
+                                      ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
+            fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
+                                      ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
+            SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
+                                      ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
+                                      ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
+
+            // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
+            // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
+            fTransformedZParams.fX *= ctmDeterminant;
+            fTransformedZParams.fY *= ctmDeterminant;
+            fTransformedZParams.fZ *= ctmDeterminant;
+
+            fTransformedHeightFunc = [this](const SkPoint& p) {
+                SkScalar denom = p.fX * fPartialDeterminants[0] +
+                                 p.fY * fPartialDeterminants[1] +
+                                 fPartialDeterminants[2];
+                SkScalar w = SkScalarFastInvert(denom);
+                return fZOffset + w*(fTransformedZParams.fX * p.fX +
+                                     fTransformedZParams.fY * p.fY +
+                                     fTransformedZParams.fZ);
+            };
+        } else {
+            fTransformedHeightFunc = [this](const SkPoint& p) {
+                return fZOffset + fTransformedZParams.fX * p.fX +
+                       fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
+            };
+        }
     }
 
     return true;
@@ -325,8 +338,7 @@
 class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
 public:
     SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
-                               SkShadowTessellator::HeightFunc heightFunc,
-                               SkScalar ambientAlpha, bool transparent);
+                               const SkPoint3& zPlaneParams, bool transparent);
 
 private:
     void handleLine(const SkPoint& p) override;
@@ -335,28 +347,30 @@
     static constexpr auto kHeightFactor = 1.0f / 128.0f;
     static constexpr auto kGeomFactor = 64.0f;
     static constexpr auto kMaxEdgeLenSqr = 20 * 20;
+    static constexpr auto kInsetFactor = -0.5f;
 
     SkScalar offset(SkScalar z) {
         return z * kHeightFactor * kGeomFactor;
     }
     SkColor umbraColor(SkScalar z) {
         SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(z*kHeightFactor, 0.0f)));
-        return SkColorSetARGB(255, 0, fAmbientAlpha * 255.9999f, umbraAlpha * 255.9999f);
+        return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
     }
 
-    SkScalar            fAmbientAlpha;
     int                 fCentroidCount;
+    bool                fSplitFirstEdge;
+    bool                fSplitPreviousEdge;
 
     typedef SkBaseShadowTessellator INHERITED;
 };
 
 SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
                                                        const SkMatrix& ctm,
-                                                       SkShadowTessellator::HeightFunc heightFunc,
-                                                       SkScalar ambientAlpha,
+                                                       const SkPoint3& zPlaneParams,
                                                        bool transparent)
-        : INHERITED(heightFunc, transparent)
-        , fAmbientAlpha(ambientAlpha) {
+        : INHERITED(zPlaneParams, transparent)
+        , fSplitFirstEdge(false)
+        , fSplitPreviousEdge(false) {
     // Set base colors
     SkScalar occluderHeight = heightFunc(0, 0);
     SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*kHeightFactor, 0.0f)));
@@ -364,8 +378,8 @@
     // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
     // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
     // the final alpha.
-    fUmbraColor = SkColorSetARGB(255, 0, ambientAlpha * 255.9999f, umbraAlpha * 255.9999f);
-    fPenumbraColor = SkColorSetARGB(255, 0, ambientAlpha * 255.9999f, 0);
+    fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
+    fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
 
     // make sure we're not below the canvas plane
     this->setZOffset(path.getBounds(), ctm.hasPerspective());
@@ -415,6 +429,7 @@
         return;
     }
 
+    // Finish up
     SkVector normal;
     if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
         SkScalar z = fTransformedHeightFunc(fPrevPoint);
@@ -423,29 +438,64 @@
         scaledNormal *= fRadius;
         this->addArc(scaledNormal, true);
 
+        // fix-up the last and first umbra points
+        SkVector inset = normal;
+        // adding to an average, so multiply by an additional half
+        inset *= 0.5f*kInsetFactor;
+        fPositions[fPrevUmbraIndex] += inset;
+        fPositions[fFirstVertexIndex] += inset;
+        // we multiply by another half because now we're adding to an average of an average
+        inset *= 0.5f;
+        if (fSplitPreviousEdge) {
+            fPositions[fPrevUmbraIndex - 2] += inset;
+        }
+        if (fSplitFirstEdge) {
+            fPositions[fFirstVertexIndex + 2] += inset;
+        }
+
         // set up for final edge
         z = fTransformedHeightFunc(fFirstPoint);
         normal *= this->offset(z);
 
         // make sure we don't end up with a sharp alpha edge along the quad diagonal
-        if (fColors[fPrevUmbraIndex] != fColors[fFirstVertex] &&
+        if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
             fFirstPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
-            SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fFirstPoint;
+            SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
             centerPoint *= 0.5f;
             *fPositions.push() = centerPoint;
-            *fColors.push() = SkPMLerp(fColors[fFirstVertex], fColors[fPrevUmbraIndex], 128);
-            SkVector midNormal = fPrevNormal + normal;
-            midNormal *= 0.5f;
-            *fPositions.push() = centerPoint + midNormal;
+            *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
+            centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
+            centerPoint *= 0.5f;
+            *fPositions.push() = centerPoint;
             *fColors.push() = fPenumbraColor;
 
-            *fIndices.push() = fPrevUmbraIndex;
-            *fIndices.push() = fPositions.count() - 3;
-            *fIndices.push() = fPositions.count() - 2;
+            if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
+                *fIndices.push() = fPrevUmbraIndex;
+                *fIndices.push() = fPositions.count() - 3;
+                *fIndices.push() = fPositions.count() - 2;
 
-            *fIndices.push() = fPositions.count() - 3;
-            *fIndices.push() = fPositions.count() - 1;
-            *fIndices.push() = fPositions.count() - 2;
+                *fIndices.push() = fPositions.count() - 3;
+                *fIndices.push() = fPositions.count() - 1;
+                *fIndices.push() = fPositions.count() - 2;
+            } else {
+                *fIndices.push() = fPrevUmbraIndex;
+                *fIndices.push() = fPositions.count() - 2;
+                *fIndices.push() = fPositions.count() - 1;
+
+                *fIndices.push() = fPrevUmbraIndex;
+                *fIndices.push() = fPositions.count() - 1;
+                *fIndices.push() = fPositions.count() - 3;
+            }
+
+            // if transparent, add point to first one in array and add to center fan
+            if (fTransparent) {
+                fPositions[0] += centerPoint;
+                ++fCentroidCount;
+
+                *fIndices.push() = 0;
+                *fIndices.push() = fPrevUmbraIndex;
+                *fIndices.push() = fPositions.count() - 2;
+            }
 
             fPrevUmbraIndex = fPositions.count() - 2;
         }
@@ -454,14 +504,14 @@
         *fPositions.push() = fFirstPoint + normal;
         *fColors.push() = fPenumbraColor;
 
-        if (fColors[fPrevUmbraIndex] > fColors[fFirstVertex]) {
+        if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
             *fIndices.push() = fPrevUmbraIndex;
             *fIndices.push() = fPositions.count() - 2;
-            *fIndices.push() = fFirstVertex;
+            *fIndices.push() = fFirstVertexIndex;
 
             *fIndices.push() = fPositions.count() - 2;
             *fIndices.push() = fPositions.count() - 1;
-            *fIndices.push() = fFirstVertex;
+            *fIndices.push() = fFirstVertexIndex;
         } else {
             *fIndices.push() = fPrevUmbraIndex;
             *fIndices.push() = fPositions.count() - 2;
@@ -469,30 +519,35 @@
 
             *fIndices.push() = fPrevUmbraIndex;
             *fIndices.push() = fPositions.count() - 1;
-            *fIndices.push() = fFirstVertex;
+            *fIndices.push() = fFirstVertexIndex;
         }
-        fPrevNormal = normal;
+        fPrevOutset = normal;
     }
 
     // finalize centroid
     if (fTransparent) {
         fPositions[0] *= SkScalarFastInvert(fCentroidCount);
+        fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
 
         *fIndices.push() = 0;
         *fIndices.push() = fPrevUmbraIndex;
-        *fIndices.push() = fFirstVertex;
+        *fIndices.push() = fFirstVertexIndex;
     }
 
     // final fan
     if (fPositions.count() >= 3) {
-        fPrevUmbraIndex = fFirstVertex;
+        fPrevUmbraIndex = fFirstVertexIndex;
         fPrevPoint = fFirstPoint;
         fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
-        this->addArc(fFirstNormal, false);
-
-        *fIndices.push() = fFirstVertex;
-        *fIndices.push() = fPositions.count() - 1;
-        *fIndices.push() = fFirstVertex + 1;
+        if (this->addArc(fFirstOutset, false)) {
+            *fIndices.push() = fFirstVertexIndex;
+            *fIndices.push() = fPositions.count() - 1;
+            *fIndices.push() = fFirstVertexIndex + 1;
+        } else {
+            // arc is too small, set the first penumbra point to be the same position
+            // as the last one
+            fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
+        }
     }
     fSucceeded = true;
 }
@@ -526,18 +581,18 @@
         }
 
         fFirstPoint = fInitPoints[0];
-        fFirstVertex = fPositions.count();
+        fFirstVertexIndex = fPositions.count();
         SkScalar z = fTransformedHeightFunc(fFirstPoint);
-        fFirstNormal = normal;
-        fFirstNormal *= this->offset(z);
+        fFirstOutset = normal;
+        fFirstOutset *= this->offset(z);
 
-        fPrevNormal = fFirstNormal;
+        fPrevOutset = fFirstOutset;
         fPrevPoint = fFirstPoint;
-        fPrevUmbraIndex = fFirstVertex;
+        fPrevUmbraIndex = fFirstVertexIndex;
 
         *fPositions.push() = fFirstPoint;
         *fColors.push() = this->umbraColor(z);
-        *fPositions.push() = fFirstPoint + fFirstNormal;
+        *fPositions.push() = fFirstPoint + fFirstOutset;
         *fColors.push() = fPenumbraColor;
         if (fTransparent) {
             fPositions[0] += fFirstPoint;
@@ -548,7 +603,6 @@
         z = fTransformedHeightFunc(fInitPoints[1]);
         fRadius = this->offset(z);
         fUmbraColor = this->umbraColor(z);
-        normal *= fRadius;
         this->addEdge(fInitPoints[1], normal);
 
         // to ensure we skip this block next time
@@ -556,29 +610,49 @@
     }
 
     SkVector normal;
-    if (compute_normal(fPositions[fPrevUmbraIndex], p, fDirection, &normal)) {
+    if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
         SkVector scaledNormal = normal;
         scaledNormal *= fRadius;
         this->addArc(scaledNormal, true);
         SkScalar z = fTransformedHeightFunc(p);
         fRadius = this->offset(z);
         fUmbraColor = this->umbraColor(z);
-        normal *= fRadius;
         this->addEdge(p, normal);
     }
 }
 
 void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
-    // make sure we don't end up with a sharp alpha edge along the quad diagonal
+    // We compute the inset in two stages: first we inset by half the current normal,
+    // then on the next addEdge() we add half of the next normal to get an average of the two
+    SkVector insetNormal = nextNormal;
+    insetNormal *= 0.5f*kInsetFactor;
+
+    // Adding the other half of the average for the previous edge
+    fPositions[fPrevUmbraIndex] += insetNormal;
+
+    SkPoint umbraPoint = nextPoint + insetNormal;
+    SkVector outsetNormal = nextNormal;
+    outsetNormal *= fRadius;
+    SkPoint penumbraPoint = nextPoint + outsetNormal;
+
+    // For split edges, we're adding an average of two averages, so we multiply by another half
+    if (fSplitPreviousEdge) {
+        insetNormal *= 0.5f;
+        fPositions[fPrevUmbraIndex - 2] += insetNormal;
+    }
+
+    // Split the edge to make sure we don't end up with a sharp alpha edge along the quad diagonal
     if (fColors[fPrevUmbraIndex] != fUmbraColor &&
         nextPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
-        SkPoint centerPoint = fPositions[fPrevUmbraIndex] + nextPoint;
+
+        // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
+        SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
         centerPoint *= 0.5f;
         *fPositions.push() = centerPoint;
         *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
-        SkVector midNormal = fPrevNormal + nextNormal;
-        midNormal *= 0.5f;
-        *fPositions.push() = centerPoint + midNormal;
+        centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
+        centerPoint *= 0.5f;
+        *fPositions.push() = centerPoint;
         *fColors.push() = fPenumbraColor;
 
         // set triangularization to get best interpolation of color
@@ -600,13 +674,29 @@
             *fIndices.push() = fPositions.count() - 3;
         }
 
+        // if transparent, add point to first one in array and add to center fan
+        if (fTransparent) {
+            fPositions[0] += centerPoint;
+            ++fCentroidCount;
+
+            *fIndices.push() = 0;
+            *fIndices.push() = fPrevUmbraIndex;
+            *fIndices.push() = fPositions.count() - 2;
+        }
+
+        fSplitPreviousEdge = true;
+        if (fPrevUmbraIndex == fFirstVertexIndex) {
+            fSplitFirstEdge = true;
+        }
         fPrevUmbraIndex = fPositions.count() - 2;
+    } else {
+        fSplitPreviousEdge = false;
     }
 
     // add next quad
-    *fPositions.push() = nextPoint;
+    *fPositions.push() = umbraPoint;
     *fColors.push() = fUmbraColor;
-    *fPositions.push() = nextPoint + nextNormal;
+    *fPositions.push() = penumbraPoint;
     *fColors.push() = fPenumbraColor;
 
     // set triangularization to get best interpolation of color
@@ -639,8 +729,8 @@
     }
 
     fPrevUmbraIndex = fPositions.count() - 2;
-    fPrevNormal = nextNormal;
     fPrevPoint = nextPoint;
+    fPrevOutset = outsetNormal;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -648,9 +738,8 @@
 class SkSpotShadowTessellator : public SkBaseShadowTessellator {
 public:
     SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
-                            SkShadowTessellator::HeightFunc heightFunc,
-                            const SkPoint3& lightPos, SkScalar lightRadius,
-                            SkScalar spotAlpha, bool transparent);
+                            const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
+                            SkScalar lightRadius, bool transparent);
 
 private:
     void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
@@ -692,10 +781,10 @@
 };
 
 SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
-                                                 SkShadowTessellator::HeightFunc heightFunc,
+                                                 const SkPoint3& zPlaneParams,
                                                  const SkPoint3& lightPos, SkScalar lightRadius,
-                                                 SkScalar spotAlpha, bool transparent)
-    : INHERITED(heightFunc, transparent)
+                                                 bool transparent)
+    : INHERITED(zPlaneParams, transparent)
     , fLightZ(lightPos.fZ)
     , fLightRadius(lightRadius)
     , fOffsetAdjust(0)
@@ -713,12 +802,12 @@
 
     // Set radius and colors
     SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
-    SkScalar occluderHeight = heightFunc(center.fX, center.fY) + fZOffset;
+    SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
     float zRatio = SkTPin(occluderHeight / (fLightZ - occluderHeight), 0.0f, 0.95f);
     SkScalar radius = lightRadius * zRatio;
     fRadius = radius;
-    fUmbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 255);
-    fPenumbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 0);
+    fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
+    fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
 
     // Compute the scale and translation for the spot shadow.
     SkMatrix shadowTransform;
@@ -778,7 +867,7 @@
         // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
         SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
         fOffsetAdjust = newRadius - radius;
-        SkScalar ratio = 256 * newRadius / radius;
+        SkScalar ratio = 128 * (newRadius + radius) / radius;
         // they aren't PMColors, but the interpolation algorithm is the same
         fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
         radius = newRadius;
@@ -818,23 +907,23 @@
         if (fTransparent) {
             *fIndices.push() = 0;
             *fIndices.push() = fPrevUmbraIndex;
-            *fIndices.push() = fFirstVertex;
+            *fIndices.push() = fFirstVertexIndex;
             // or to clip ring
         } else {
             if (fFirstUmbraOutside) {
                 *fIndices.push() = fPrevUmbraIndex;
-                *fIndices.push() = fFirstVertex;
-                *fIndices.push() = fFirstVertex + 1;
+                *fIndices.push() = fFirstVertexIndex;
+                *fIndices.push() = fFirstVertexIndex + 1;
                 if (fPrevUmbraOutside) {
                     // fill out quad
                     *fIndices.push() = fPrevUmbraIndex;
-                    *fIndices.push() = fFirstVertex + 1;
+                    *fIndices.push() = fFirstVertexIndex + 1;
                     *fIndices.push() = fPrevUmbraIndex + 1;
                 }
             } else if (fPrevUmbraOutside) {
                 // add tri
                 *fIndices.push() = fPrevUmbraIndex;
-                *fIndices.push() = fFirstVertex;
+                *fIndices.push() = fFirstVertexIndex;
                 *fIndices.push() = fPrevUmbraIndex + 1;
             }
         }
@@ -845,27 +934,34 @@
 
         *fIndices.push() = fPrevUmbraIndex;
         *fIndices.push() = fPositions.count() - 2;
-        *fIndices.push() = fFirstVertex;
+        *fIndices.push() = fFirstVertexIndex;
 
         *fIndices.push() = fPositions.count() - 2;
         *fIndices.push() = fPositions.count() - 1;
-        *fIndices.push() = fFirstVertex;
+        *fIndices.push() = fFirstVertexIndex;
 
-        fPrevNormal = normal;
+        fPrevOutset = normal;
     }
 
     // final fan
     if (fPositions.count() >= 3) {
-        fPrevUmbraIndex = fFirstVertex;
+        fPrevUmbraIndex = fFirstVertexIndex;
         fPrevPoint = fFirstPoint;
-        this->addArc(fFirstNormal, false);
-
-        *fIndices.push() = fFirstVertex;
-        *fIndices.push() = fPositions.count() - 1;
-        if (fFirstUmbraOutside) {
-            *fIndices.push() = fFirstVertex + 2;
+        if (this->addArc(fFirstOutset, false)) {
+            *fIndices.push() = fFirstVertexIndex;
+            *fIndices.push() = fPositions.count() - 1;
+            if (fFirstUmbraOutside) {
+                *fIndices.push() = fFirstVertexIndex + 2;
+            } else {
+                *fIndices.push() = fFirstVertexIndex + 1;
+            }
         } else {
-            *fIndices.push() = fFirstVertex + 1;
+            // no arc added, fix up by setting first penumbra point position to last one
+            if (fFirstUmbraOutside) {
+                fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
+            } else {
+                fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
+            }
         }
     }
 
@@ -1149,24 +1245,25 @@
         fDirection = (perpDot > 0) ? -1 : 1;
 
         // add first quad
-        if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstNormal)) {
+        if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
             // first two points are incident, make the third point the second and continue
             fInitPoints[1] = p;
             return;
         }
 
-        fFirstNormal *= fRadius;
+        fFirstOutset *= fRadius;
         fFirstPoint = fInitPoints[0];
-        fFirstVertex = fPositions.count();
-        fPrevNormal = fFirstNormal;
+        fFirstVertexIndex = fPositions.count();
+        fPrevOutset = fFirstOutset;
         fPrevPoint = fFirstPoint;
-        fPrevUmbraIndex = fFirstVertex;
+        fPrevUmbraIndex = -1;
 
         this->addInnerPoint(fFirstPoint);
+        fPrevUmbraIndex = fFirstVertexIndex;
 
         if (!fTransparent) {
             SkPoint clipPoint;
-            bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertex], fCentroid, &clipPoint);
+            bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex], fCentroid, &clipPoint);
             if (isOutside) {
                 *fPositions.push() = clipPoint;
                 *fColors.push() = fUmbraColor;
@@ -1175,10 +1272,10 @@
             fFirstUmbraOutside = isOutside;
         }
 
-        SkPoint newPoint = fFirstPoint + fFirstNormal;
+        SkPoint newPoint = fFirstPoint + fFirstOutset;
         *fPositions.push() = newPoint;
         *fColors.push() = fPenumbraColor;
-        this->addEdge(fInitPoints[1], fFirstNormal);
+        this->addEdge(fInitPoints[1], fFirstOutset);
 
         // to ensure we skip this block next time
         *fInitPoints.push() = p;
@@ -1205,7 +1302,7 @@
     fPrevPoint = pathPoint;
 
     // merge "close" points
-    if (fPrevUmbraIndex == fFirstVertex ||
+    if (fPrevUmbraIndex == -1 ||
         !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
         *fPositions.push() = umbraPoint;
         *fColors.push() = fUmbraColor;
@@ -1272,23 +1369,20 @@
     *fIndices.push() = currUmbraIndex;
 
     fPrevUmbraIndex = currUmbraIndex;
-    fPrevNormal = nextNormal;
+    fPrevOutset = nextNormal;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
-                                                   HeightFunc heightFunc, SkScalar ambientAlpha,
-                                                   bool transparent) {
-    SkAmbientShadowTessellator ambientTess(path, ctm, heightFunc, ambientAlpha, transparent);
+                                                   const SkPoint3& zPlane, bool transparent) {
+    SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
     return ambientTess.releaseVertices();
 }
 
 sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
-                                                HeightFunc heightFunc,
-                                                const SkPoint3& lightPos, SkScalar lightRadius,
-                                                SkScalar spotAlpha, bool transparent) {
-    SkSpotShadowTessellator spotTess(path, ctm, heightFunc, lightPos, lightRadius,
-                                     spotAlpha, transparent);
+                                                const SkPoint3& zPlane, const SkPoint3& lightPos,
+                                                SkScalar lightRadius,  bool transparent) {
+    SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
     return spotTess.releaseVertices();
 }
diff --git a/src/utils/SkShadowTessellator.h b/src/utils/SkShadowTessellator.h
index f9f4a14..351beee 100644
--- a/src/utils/SkShadowTessellator.h
+++ b/src/utils/SkShadowTessellator.h
@@ -26,16 +26,15 @@
  * If transparent is true, then the center of the ambient shadow will be filled in.
  */
 sk_sp<SkVertices> MakeAmbient(const SkPath& path, const SkMatrix& ctm,
-                              HeightFunc heightFunc, SkScalar ambientAlpha, bool transparent);
+                              const SkPoint3& zPlane, bool transparent);
 
 /**
  * This function generates a spot shadow mesh for a path by walking the transformed path,
  * further transforming by the scale and translation, and outsetting and insetting by a radius.
  * The center will be clipped against the original path unless transparent is true.
  */
-sk_sp<SkVertices> MakeSpot(const SkPath& path, const SkMatrix& ctm, HeightFunc heightFunc,
-                           const SkPoint3& lightPos, SkScalar lightRadius,
-                           SkScalar spotAlpha, bool transparent);
+sk_sp<SkVertices> MakeSpot(const SkPath& path, const SkMatrix& ctm, const SkPoint3& zPlane,
+                           const SkPoint3& lightPos, SkScalar lightRadius, bool transparent);
 }
 
 #endif
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 2e560b4..4b5ed5f 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -8,8 +8,13 @@
 #include "SkShadowUtils.h"
 #include "SkCanvas.h"
 #include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkDrawShadowRec.h"
 #include "SkPath.h"
+#include "SkPM4f.h"
 #include "SkRandom.h"
+#include "SkRasterPipeline.h"
 #include "SkResourceCache.h"
 #include "SkShadowTessellator.h"
 #include "SkString.h"
@@ -19,8 +24,6 @@
 #include "GrShape.h"
 #include "effects/GrBlurredEdgeFragmentProcessor.h"
 #endif
-#include "../../src/effects/shadows/SkAmbientShadowMaskFilter.h"
-#include "../../src/effects/shadows/SkSpotShadowMaskFilter.h"
 
 /**
 *  Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
@@ -45,21 +48,81 @@
 
 protected:
     void flatten(SkWriteBuffer&) const override {}
-
+    void onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
+                        bool shaderIsOpaque) const override {
+        pipeline->append(SkRasterPipeline::gauss_a_to_rgba);
+    }
 private:
     SkGaussianColorFilter() : INHERITED() {}
 
     typedef SkColorFilter INHERITED;
 };
 
+static inline float eval_gaussian(float x) {
+    // x = 1 - x;
+    // return sk_float_exp(-x * x * 4) - 0.018f;
+
+    return 0.00030726194381713867f +
+             x*(0.15489584207534790039f +
+               x*(0.21345567703247070312f +
+                 (2.89795351028442382812f - 2.26661229133605957031f*x)*x));
+}
+
+static void build_table() {
+    SkDebugf("const uint8_t gByteExpU8Table[256] = {");
+    for (int i = 0; i <= 255; ++i) {
+        if (!(i % 8)) {
+            SkDebugf("\n");
+        }
+        int v = (int)(eval_gaussian(i / 255.f) * 256);
+        SkDebugf(" 0x%02X,", v);
+    }
+    SkDebugf("\n};\n");
+}
+
+const uint8_t gByteExpU8Table[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+    0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
+    0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07,
+    0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
+    0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D,
+    0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11,
+    0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15,
+    0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B,
+    0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21,
+    0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+    0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39,
+    0x3A, 0x3B, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x42,
+    0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4C, 0x4D,
+    0x4E, 0x50, 0x51, 0x53, 0x54, 0x55, 0x57, 0x58,
+    0x5A, 0x5B, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64,
+    0x66, 0x68, 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71,
+    0x73, 0x75, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F,
+    0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8A, 0x8C,
+    0x8E, 0x90, 0x91, 0x93, 0x95, 0x96, 0x98, 0x9A,
+    0x9C, 0x9D, 0x9F, 0xA1, 0xA2, 0xA4, 0xA6, 0xA8,
+    0xA9, 0xAB, 0xAD, 0xAE, 0xB0, 0xB2, 0xB3, 0xB5,
+    0xB7, 0xB8, 0xBA, 0xBC, 0xBD, 0xBF, 0xC0, 0xC2,
+    0xC3, 0xC5, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, 0xCE,
+    0xCF, 0xD1, 0xD2, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9,
+    0xDA, 0xDC, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3,
+    0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
+    0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF0, 0xF1, 0xF2,
+    0xF3, 0xF3, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF7,
+    0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xFA, 0xFA,
+    0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
+};
+
 void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
+    // to re-build the table, call build_table() which will dump it out using SkDebugf.
+    if (false) {
+        build_table();
+    }
     for (int i = 0; i < count; ++i) {
         SkPMColor c = src[i];
-
-        SkScalar factor = SK_Scalar1 - SkGetPackedB32(c) / 255.f;
-        factor = SkScalarExp(-factor * factor * 4) - 0.018f;
-
-        SkScalar a = factor * SkGetPackedG32(c);
+        uint8_t a = gByteExpU8Table[SkGetPackedA32(c)];
         dst[i] = SkPackARGB32(a, a, a, a);
     }
 }
@@ -93,58 +156,63 @@
 /** Factory for an ambient shadow mesh with particular shadow properties. */
 struct AmbientVerticesFactory {
     SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
-    SkScalar fAmbientAlpha;
     bool fTransparent;
+    SkVector fOffset;
 
     bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
-        if (fOccluderHeight != that.fOccluderHeight || fAmbientAlpha != that.fAmbientAlpha ||
-            fTransparent != that.fTransparent) {
+        if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
             return false;
         }
-        translate->set(0, 0);
+        *translate = that.fOffset;
         return true;
     }
 
-    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
-        SkScalar z = fOccluderHeight;
-        return SkShadowTessellator::MakeAmbient(path, ctm,
-                                                [z](SkScalar, SkScalar) { return z; },
-                                                fAmbientAlpha, fTransparent);
+    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
+                                   SkVector* translate) const {
+        SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
+        // pick a canonical place to generate shadow
+        SkMatrix noTrans(ctm);
+        if (!ctm.hasPerspective()) {
+            noTrans[SkMatrix::kMTransX] = 0;
+            noTrans[SkMatrix::kMTransY] = 0;
+        }
+        *translate = fOffset;
+        return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
     }
 };
 
 /** Factory for an spot shadow mesh with particular shadow properties. */
 struct SpotVerticesFactory {
     enum class OccluderType {
-        // The umbra cannot be dropped out because the occluder is not opaque.
+        // The umbra cannot be dropped out because either the occluder is not opaque,
+        // or the center of the umbra is visible.
         kTransparent,
         // The umbra can be dropped where it is occluded.
-        kOpaque,
+        kOpaquePartialUmbra,
         // It is known that the entire umbra is occluded.
-        kOpaqueCoversUmbra
+        kOpaqueNoUmbra
     };
 
     SkVector fOffset;
+    SkPoint  fLocalCenter;
     SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
     SkPoint3 fDevLightPos;
     SkScalar fLightRadius;
-    SkScalar fSpotAlpha;
     OccluderType fOccluderType;
 
     bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
         if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
-            fLightRadius != that.fLightRadius || fSpotAlpha != that.fSpotAlpha ||
-            fOccluderType != that.fOccluderType) {
+            fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
             return false;
         }
         switch (fOccluderType) {
             case OccluderType::kTransparent:
-            case OccluderType::kOpaqueCoversUmbra:
+            case OccluderType::kOpaqueNoUmbra:
                 // 'this' and 'that' will either both have no umbra removed or both have all the
                 // umbra removed.
-                *translate = that.fOffset - fOffset;
+                *translate = that.fOffset;
                 return true;
-            case OccluderType::kOpaque:
+            case OccluderType::kOpaquePartialUmbra:
                 // In this case we partially remove the umbra differently for 'this' and 'that'
                 // if the offsets don't match.
                 if (fOffset == that.fOffset) {
@@ -157,20 +225,33 @@
         return false;
     }
 
-    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
+    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
+                                   SkVector* translate) const {
         bool transparent = OccluderType::kTransparent == fOccluderType;
-        SkScalar z = fOccluderHeight;
-        return SkShadowTessellator::MakeSpot(path, ctm,
-                                             [z](SkScalar, SkScalar) -> SkScalar { return z; },
-                                             fDevLightPos, fLightRadius,
-                                             fSpotAlpha, transparent);
+        SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
+        if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
+            translate->set(0, 0);
+            return SkShadowTessellator::MakeSpot(path, ctm, zParams,
+                                                 fDevLightPos, fLightRadius, transparent);
+        } else {
+            // pick a canonical place to generate shadow, with light centered over path
+            SkMatrix noTrans(ctm);
+            noTrans[SkMatrix::kMTransX] = 0;
+            noTrans[SkMatrix::kMTransY] = 0;
+            SkPoint devCenter(fLocalCenter);
+            noTrans.mapPoints(&devCenter, 1);
+            SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
+            *translate = fOffset;
+            return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
+                                                 centerLightPos, fLightRadius, transparent);
+        }
     }
 };
 
 /**
  * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
  * records are immutable this is not itself a Rec. When we need to update it we return this on
- * the FindVisitor and let the cache destory the Rec. We'll update the tessellations and then add
+ * the FindVisitor and let the cache destroy the Rec. We'll update the tessellations and then add
  * a new Rec with an adjusted size for any deletions/additions.
  */
 class CachedTessellations : public SkRefCnt {
@@ -183,8 +264,8 @@
     }
 
     sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
-                          const SkMatrix& matrix) {
-        return fAmbientSet.add(devPath, ambient, matrix);
+                          const SkMatrix& matrix, SkVector* translate) {
+        return fAmbientSet.add(devPath, ambient, matrix, translate);
     }
 
     sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
@@ -193,8 +274,8 @@
     }
 
     sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
-                          const SkMatrix& matrix) {
-        return fSpotSet.add(devPath, spot, matrix);
+                          const SkMatrix& matrix, SkVector* translate) {
+        return fSpotSet.add(devPath, spot, matrix, translate);
     }
 
 private:
@@ -218,16 +299,15 @@
                                matrix.getSkewY() != m.getSkewY()) {
                         continue;
                     }
-                    *translate += SkVector{matrix.getTranslateX() - m.getTranslateX(),
-                                           matrix.getTranslateY() - m.getTranslateY()};
                     return fEntries[i].fVertices;
                 }
             }
             return nullptr;
         }
 
-        sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix) {
-            sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix);
+        sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
+                              SkVector* translate) {
+            sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
             if (!vertices) {
                 return nullptr;
             }
@@ -235,7 +315,7 @@
             if (fCount < MAX_ENTRIES) {
                 i = fCount++;
             } else {
-                i = gRandom.nextULessThan(MAX_ENTRIES);
+                i = fRandom.nextULessThan(MAX_ENTRIES);
                 fSize -= fEntries[i].fVertices->approximateSize();
             }
             fEntries[i].fFactory = factory;
@@ -254,16 +334,13 @@
         Entry fEntries[MAX_ENTRIES];
         int fCount = 0;
         size_t fSize = 0;
+        SkRandom fRandom;
     };
 
     Set<AmbientVerticesFactory, 4> fAmbientSet;
     Set<SpotVerticesFactory, 4> fSpotSet;
-
-    static SkRandom gRandom;
 };
 
-SkRandom CachedTessellations::gRandom;
-
 /**
  * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
  * path. The key represents the path's geometry and not any shadow params.
@@ -382,8 +459,9 @@
  * they are first found in SkResourceCache.
  */
 template <typename FACTORY>
-void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color,
-                 SkResourceCache* cache) {
+    void draw_shadow(const FACTORY& factory,
+                     std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
+                     SkScalar tx, SkScalar ty)> drawProc, ShadowedPath& path, SkColor color) {
     FindContext<FACTORY> context(&path.viewMatrix(), &factory);
 
     SkResourceCache::Key* key = nullptr;
@@ -394,20 +472,13 @@
         key = new (keyStorage.begin()) SkResourceCache::Key();
         path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
         key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
-        if (cache) {
-            cache->find(*key, FindVisitor<FACTORY>, &context);
-        } else {
-            SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
-        }
+        SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
     }
 
     sk_sp<SkVertices> vertices;
-    const SkVector* translate;
-    static constexpr SkVector kZeroTranslate = {0, 0};
     bool foundInCache = SkToBool(context.fVertices);
     if (foundInCache) {
         vertices = std::move(context.fVertices);
-        translate = &context.fTranslate;
     } else {
         // TODO: handle transforming the path as part of the tessellator
         if (key) {
@@ -418,23 +489,20 @@
             } else {
                 tessellations.reset(new CachedTessellations());
             }
-            vertices = tessellations->add(path.path(), factory, path.viewMatrix());
+            vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
+                                          &context.fTranslate);
             if (!vertices) {
                 return;
             }
             auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
-            if (cache) {
-                cache->add(rec);
-            } else {
-                SkResourceCache::Add(rec);
-            }
+            SkResourceCache::Add(rec);
         } else {
-            vertices = factory.makeVertices(path.path(), path.viewMatrix());
+            vertices = factory.makeVertices(path.path(), path.viewMatrix(),
+                                            &context.fTranslate);
             if (!vertices) {
                 return;
             }
         }
-        translate = &kZeroTranslate;
     }
 
     SkPaint paint;
@@ -443,157 +511,168 @@
     paint.setColorFilter(SkColorFilter::MakeComposeFilter(
             SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
             SkGaussianColorFilter::Make()));
-    if (translate->fX || translate->fY) {
-        canvas->save();
-        canvas->translate(translate->fX, translate->fY);
-    }
-    canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
-    if (translate->fX || translate->fY) {
-        canvas->restore();
-    }
+
+    drawProc(vertices.get(), SkBlendMode::kModulate, paint,
+             context.fTranslate.fX, context.fTranslate.fY);
 }
 }
 
+static bool tilted(const SkPoint3& zPlaneParams) {
+    return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
+}
+
+static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
+    SkPoint3 result;
+    m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
+    result.fZ = pt.fZ;
+    return result;
+}
+
+static SkColor compute_render_color(SkColor color, float alpha) {
+    return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
+                          SkColorGetG(color), SkColorGetB(color));
+}
+
 // Draw an offset spot shadow and outlining ambient shadow for the given path.
-void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
+void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
                                const SkPoint3& devLightPos, SkScalar lightRadius,
                                SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
-                               uint32_t flags, SkResourceCache* cache) {
-    SkAutoCanvasRestore acr(canvas, true);
-    SkMatrix viewMatrix = canvas->getTotalMatrix();
-
-    // try circular fast path
-    SkRect rect;
-    if (viewMatrix.isSimilarity() &&
-        path.isOval(&rect) && rect.width() == rect.height()) {
-        SkPaint newPaint;
-        newPaint.setColor(color);
-        if (ambientAlpha > 0) {
-            newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha,
-                                                                   flags));
-            canvas->drawPath(path, newPaint);
-        }
-        if (spotAlpha > 0) {
-            newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderHeight, devLightPos,
-                                                                lightRadius, spotAlpha, flags));
-            canvas->drawPath(path, newPaint);
-        }
+                               uint32_t flags) {
+    SkMatrix inverse;
+    if (!canvas->getTotalMatrix().invert(&inverse)) {
         return;
     }
+    SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
 
-    canvas->resetMatrix();
+    SkDrawShadowRec rec;
+    rec.fZPlaneParams   = zPlaneParams;
+    rec.fLightPos       = { pt.fX, pt.fY, devLightPos.fZ };
+    rec.fLightRadius    = lightRadius;
+    rec.fAmbientAlpha   = SkScalarToFloat(ambientAlpha);
+    rec.fSpotAlpha      = SkScalarToFloat(spotAlpha);
+    rec.fColor          = color;
+    rec.fFlags          = flags;
+
+    canvas->private_draw_shadow_rec(path, rec);
+}
+
+void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
+    auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
+                                SkScalar tx, SkScalar ty) {
+        SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
+                                                          SkMatrix::MakeTrans(tx, ty)));
+        this->drawVertices(vertices, mode, paint);
+    };
+
+    SkMatrix viewMatrix = this->ctm();
+    SkAutoDeviceCTMRestore adr(this, SkMatrix::I());
 
     ShadowedPath shadowedPath(&path, &viewMatrix);
 
-    bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
+    bool tiltZPlane = tilted(rec.fZPlaneParams);
+    bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
+    bool uncached = tiltZPlane || path.isVolatile();
 
+    SkColor color = rec.fColor;
+    SkPoint3 zPlaneParams = rec.fZPlaneParams;
+    SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
+    float lightRadius = rec.fLightRadius;
+
+    float ambientAlpha = rec.fAmbientAlpha;
     if (ambientAlpha > 0) {
         ambientAlpha = SkTMin(ambientAlpha, 1.f);
-        AmbientVerticesFactory factory;
-        factory.fOccluderHeight = occluderHeight;
-        factory.fAmbientAlpha = ambientAlpha;
-        factory.fTransparent = transparent;
-
-        draw_shadow(factory, canvas, shadowedPath, color, cache);
-    }
-
-    if (spotAlpha > 0) {
-        spotAlpha = SkTMin(spotAlpha, 1.f);
-        SpotVerticesFactory factory;
-        float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
-        SkScalar radius = lightRadius * zRatio;
-
-        // Compute the scale and translation for the spot shadow.
-        SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
-
-        SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
-        viewMatrix.mapPoints(&center, 1);
-        factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
-                                         zRatio * (center.fY - devLightPos.fY));
-        factory.fOccluderHeight = occluderHeight;
-        factory.fDevLightPos = devLightPos;
-        factory.fLightRadius = lightRadius;
-        factory.fSpotAlpha = spotAlpha;
-
-        SkRRect rrect;
-        if (transparent) {
-            factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
+        if (uncached) {
+            sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
+                                                                          zPlaneParams,
+                                                                          transparent);
+            SkColor renderColor = compute_render_color(color, ambientAlpha);
+            SkPaint paint;
+            // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
+            // result of that against our 'color' param.
+            paint.setColorFilter(SkColorFilter::MakeComposeFilter(
+                                                                  SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
+                                                                  SkGaussianColorFilter::Make()));
+            this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
         } else {
-            factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaque;
-            if (shadowedPath.isRRect(&rrect)) {
-                SkRRect devRRect;
-                if (rrect.transform(viewMatrix, &devRRect)) {
-                    SkScalar s = 1.f - scale;
-                    SkScalar w = devRRect.width();
-                    SkScalar h = devRRect.height();
-                    SkScalar hw = w / 2.f;
-                    SkScalar hh = h / 2.f;
-                    SkScalar umbraInsetX = s * hw + radius;
-                    SkScalar umbraInsetY = s * hh + radius;
-                    // The umbra is inset by radius along the diagonal, so adjust for that.
-                    SkScalar d = 1.f / SkScalarSqrt(hw * hw + hh * hh);
-                    umbraInsetX *= hw * d;
-                    umbraInsetY *= hh * d;
-                    if (umbraInsetX > hw || umbraInsetY > hh) {
-                        // There is no umbra to occlude.
-                        factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
-                    } else if (fabsf(factory.fOffset.fX) < umbraInsetX &&
-                               fabsf(factory.fOffset.fY) < umbraInsetY) {
-                        factory.fOccluderType =
-                                SpotVerticesFactory::OccluderType::kOpaqueCoversUmbra;
-                    } else if (factory.fOffset.fX > w - umbraInsetX ||
-                               factory.fOffset.fY > h - umbraInsetY) {
-                        // There umbra is fully exposed, there is nothing to omit.
-                        factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
-                    }
-                }
+            AmbientVerticesFactory factory;
+            factory.fOccluderHeight = zPlaneParams.fZ;
+            factory.fTransparent = transparent;
+            if (viewMatrix.hasPerspective()) {
+                factory.fOffset.set(0, 0);
+            } else {
+                factory.fOffset.fX = viewMatrix.getTranslateX();
+                factory.fOffset.fY = viewMatrix.getTranslateY();
             }
+
+            SkColor renderColor = compute_render_color(color, ambientAlpha);
+            draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
         }
-        if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) {
-            factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
-        }
-        draw_shadow(factory, canvas, shadowedPath, color, cache);
-    }
-}
-
-// Draw an offset spot shadow and outlining ambient shadow for the given path,
-// without caching and using a function based on local position to compute the height.
-void SkShadowUtils::DrawUncachedShadow(SkCanvas* canvas, const SkPath& path,
-                                       std::function<SkScalar(SkScalar, SkScalar)> heightFunc,
-                                       const SkPoint3& lightPos, SkScalar lightRadius,
-                                       SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
-                                       uint32_t flags) {
-    SkAutoCanvasRestore acr(canvas, true);
-    SkMatrix viewMatrix = canvas->getTotalMatrix();
-    canvas->resetMatrix();
-
-    bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
-
-    if (ambientAlpha > 0) {
-        ambientAlpha = SkTMin(ambientAlpha, 1.f);
-        sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
-                                                                      heightFunc, ambientAlpha,
-                                                                      transparent);
-        SkPaint paint;
-        // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
-        // result of that against our 'color' param.
-        paint.setColorFilter(SkColorFilter::MakeComposeFilter(
-            SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
-            SkGaussianColorFilter::Make()));
-        canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
     }
 
+    float spotAlpha = rec.fSpotAlpha;
     if (spotAlpha > 0) {
         spotAlpha = SkTMin(spotAlpha, 1.f);
-        sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, heightFunc,
-                                                                   lightPos, lightRadius,
-                                                                   spotAlpha, transparent);
-        SkPaint paint;
-        // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
-        // result of that against our 'color' param.
-        paint.setColorFilter(SkColorFilter::MakeComposeFilter(
-            SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
-            SkGaussianColorFilter::Make()));
-        canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
+        if (uncached) {
+            sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
+                                                                       zPlaneParams,
+                                                                       devLightPos, lightRadius,
+                                                                       transparent);
+            SkColor renderColor = compute_render_color(color, spotAlpha);
+            SkPaint paint;
+            // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
+            // result of that against our 'color' param.
+            paint.setColorFilter(SkColorFilter::MakeComposeFilter(
+                SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
+                SkGaussianColorFilter::Make()));
+            this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
+        } else {
+            SpotVerticesFactory factory;
+            SkScalar occluderHeight = zPlaneParams.fZ;
+            float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
+            SkScalar radius = lightRadius * zRatio;
+
+            // Compute the scale and translation for the spot shadow.
+            SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
+            SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
+            factory.fLocalCenter = center;
+            viewMatrix.mapPoints(&center, 1);
+            factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
+                                             zRatio * (center.fY - devLightPos.fY));
+            factory.fOccluderHeight = occluderHeight;
+            factory.fDevLightPos = devLightPos;
+            factory.fLightRadius = lightRadius;
+            SkRect devBounds;
+            viewMatrix.mapRect(&devBounds, path.getBounds());
+            if (transparent ||
+                SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
+                SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
+                // if the translation of the shadow is big enough we're going to end up
+                // filling the entire umbra, so we can treat these as all the same
+                factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
+            } else if (factory.fOffset.length()*scale + scale < radius) {
+                // if we don't translate more than the blur distance, can assume umbra is covered
+                factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
+            } else {
+                factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
+            }
+            // need to add this after we classify the shadow
+            factory.fOffset.fX += viewMatrix.getTranslateX();
+            factory.fOffset.fY += viewMatrix.getTranslateY();
+#ifdef DEBUG_SHADOW_CHECKS
+            switch (factory.fOccluderType) {
+                case SpotVerticesFactory::OccluderType::kTransparent:
+                    color = 0xFFD2B48C;  // tan for transparent
+                    break;
+                case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
+                    color = 0xFFFFA500;   // orange for opaque
+                    break;
+                case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
+                    color = 0xFFE5E500;  // corn yellow for covered
+                    break;
+            }
+#endif
+            SkColor renderColor = compute_render_color(color, spotAlpha);
+            draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
+        }
     }
 }
diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp
index e373592..b5df423 100644
--- a/src/utils/mac/SkCreateCGImageRef.cpp
+++ b/src/utils/mac/SkCreateCGImageRef.cpp
@@ -110,9 +110,10 @@
     SkBitmap* copy;
     if (upscaleTo32) {
         copy = new SkBitmap;
-        // here we make a ceep copy of the pixels, since CG won't take our
+        // here we make a deep copy of the pixels, since CG won't take our
         // 565 directly
-        bm.copyTo(copy, kN32_SkColorType);
+        copy->allocPixels(bm.info().makeColorType(kN32_SkColorType));
+        bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
     } else {
         copy = new SkBitmap(bm);
     }
@@ -134,9 +135,6 @@
     const size_t s = bitmap->getSize();
 
     // our provider "owns" the bitmap*, and will take care of deleting it
-    // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
-    // proc, which will in turn unlock the pixels
-    bitmap->lockPixels();
     CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
                                                              SkBitmap_ReleaseInfo);
 
diff --git a/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp
index 752828e..cd7388b 100644
--- a/src/views/SkTouchGesture.cpp
+++ b/src/views/SkTouchGesture.cpp
@@ -331,10 +331,12 @@
     return found;
 }
 
-void SkTouchGesture::setTransLimit(const SkRect& contentRect, const SkRect& windowRect) {
+void SkTouchGesture::setTransLimit(const SkRect& contentRect, const SkRect& windowRect,
+                                   const SkMatrix& preTouchMatrix) {
     fIsTransLimited = true;
     fContentRect = contentRect;
     fWindowRect = windowRect;
+    fPreTouchM = preTouchMatrix;
 }
 
 void SkTouchGesture::limitTrans() {
@@ -343,6 +345,7 @@
     }
 
     SkRect scaledContent = fContentRect;
+    fPreTouchM.mapRect(&scaledContent);
     fGlobalM.mapRect(&scaledContent);
     const SkScalar ZERO = 0;
 
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
index f86bb9f..ba06a1f 100644
--- a/src/views/SkWindow.cpp
+++ b/src/views/SkWindow.cpp
@@ -316,6 +316,7 @@
 
 #if SK_SUPPORT_GPU
 
+#include "GrBackendSurface.h"
 #include "GrContext.h"
 #include "gl/GrGLInterface.h"
 #include "gl/GrGLUtil.h"
@@ -324,10 +325,9 @@
 sk_sp<SkSurface> SkWindow::makeGpuBackedSurface(const AttachmentInfo& attachmentInfo,
                                                 const GrGLInterface* interface,
                                                 GrContext* grContext) {
-    GrBackendRenderTargetDesc desc;
-    desc.fWidth = SkScalarRoundToInt(this->width());
-    desc.fHeight = SkScalarRoundToInt(this->height());
-    if (0 == desc.fWidth || 0 == desc.fHeight) {
+    int width = SkScalarRoundToInt(this->width());
+    int height = SkScalarRoundToInt(this->height());
+    if (0 == width || 0 == height) {
         return nullptr;
     }
 
@@ -340,22 +340,28 @@
     //
     // ... and, if we're using a 10-bit/channel FB0, it doesn't do sRGB conversion on write,
     // so pretend that it's non-sRGB 8888:
-    desc.fConfig =
-        grContext->caps()->srgbSupport() &&
-        info().colorSpace() &&
-        (attachmentInfo.fColorBits != 30)
-        ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
-    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    desc.fSampleCnt = attachmentInfo.fSampleCount;
-    desc.fStencilBits = attachmentInfo.fStencilBits;
+    GrPixelConfig config = grContext->caps()->srgbSupport() &&
+                           info().colorSpace() &&
+                           (attachmentInfo.fColorBits != 30)
+                           ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
+    GrGLFramebufferInfo fbInfo;
     GrGLint buffer;
     GR_GL_GetIntegerv(interface, GR_GL_FRAMEBUFFER_BINDING, &buffer);
-    desc.fRenderTargetHandle = buffer;
+    fbInfo.fFBOID = buffer;
+
+    GrBackendRenderTarget backendRT(width,
+                                    height,
+                                    attachmentInfo.fSampleCount,
+                                    attachmentInfo.fStencilBits,
+                                    config,
+                                    fbInfo);
+
 
     sk_sp<SkColorSpace> colorSpace =
         grContext->caps()->srgbSupport() && info().colorSpace()
         ? SkColorSpace::MakeSRGB() : nullptr;
-    return SkSurface::MakeFromBackendRenderTarget(grContext, desc, colorSpace, &fSurfaceProps);
+    return SkSurface::MakeFromBackendRenderTarget(grContext, backendRT, kBottomLeft_GrSurfaceOrigin,
+                                                  colorSpace, &fSurfaceProps);
 }
 
 #endif
diff --git a/src/views/win/SkOSWindow_win.cpp b/src/views/win/SkOSWindow_win.cpp
index 983b253..8be15d1 100644
--- a/src/views/win/SkOSWindow_win.cpp
+++ b/src/views/win/SkOSWindow_win.cpp
@@ -207,7 +207,6 @@
         //       seems to be to copy the bitmap to a temporary (contiguous)
         //       buffer before passing to SetDIBitsToDevice().
         SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes());
-        bitmap.lockPixels();
         int ret = SetDIBitsToDevice(hdc,
             0, 0,
             bitmap.width(), bitmap.height(),
@@ -217,7 +216,6 @@
             &bmi,
             DIB_RGB_COLORS);
         (void)ret; // we're ignoring potential failures for now.
-        bitmap.unlockPixels();
     }
 }
 
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index 4a018a9..2022590 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -749,8 +749,6 @@
         if (SkShader::kClamp_TileMode == xy[0] &&
             SkShader::kClamp_TileMode == xy[1]) {
 
-            SkAutoLockPixels alp(bitmap);
-
             const SkColor tlColor = bitmap.getColor(0,0);
             const SkRect tlArea = SkRect::MakeLTRB(-BIG, -BIG, 0, 0);
             HR(this->cornerOfClamp(tlArea, tlColor, brushVisuals.get()));
diff --git a/src/xps/SkXPSDocument.cpp b/src/xps/SkXPSDocument.cpp
index 80dd5ee..0d12e4f 100644
--- a/src/xps/SkXPSDocument.cpp
+++ b/src/xps/SkXPSDocument.cpp
@@ -36,8 +36,7 @@
 SkCanvas* SkXPSDocument::onBeginPage(SkScalar width,
                                      SkScalar height,
                                      const SkRect& trimBox) {
-    fDevice.beginSheet(fUnitsPerMeter, fPixelsPerMeter,
-                       SkSize::Make(width, height));
+    fDevice.beginSheet(fUnitsPerMeter, fPixelsPerMeter, {width, height});
     fCanvas.reset(new SkCanvas(&fDevice));
     fCanvas->clipRect(trimBox);
     fCanvas->translate(trimBox.x(), trimBox.y());
diff --git a/tests/ArenaAllocTest.cpp b/tests/ArenaAllocTest.cpp
index c27c202..137e60e 100644
--- a/tests/ArenaAllocTest.cpp
+++ b/tests/ArenaAllocTest.cpp
@@ -68,7 +68,7 @@
         created = 0;
         destroyed = 0;
 
-        SkArenaAlloc arena{nullptr, 0};
+        SkArenaAlloc arena{0};
         REPORTER_ASSERT(r, *arena.make<int>(3) == 3);
         Foo* foo = arena.make<Foo>(3, 4.0f);
         REPORTER_ASSERT(r, foo->x == 3);
@@ -93,8 +93,7 @@
     {
         created = 0;
         destroyed = 0;
-        char block[64];
-        SkArenaAlloc arena{block};
+        SkSTArenaAlloc<64> arena;
 
         REPORTER_ASSERT(r, *arena.make<int>(3) == 3);
         Foo* foo = arena.make<Foo>(3, 4.0f);
@@ -121,7 +120,7 @@
         created = 0;
         destroyed = 0;
         std::unique_ptr<char[]> block{new char[1024]};
-        SkArenaAlloc arena{block.get(), 1024};
+        SkArenaAlloc arena{block.get(), 1024, 0};
 
         REPORTER_ASSERT(r, *arena.make<int>(3) == 3);
         Foo* foo = arena.make<Foo>(3, 4.0f);
@@ -145,8 +144,7 @@
     REPORTER_ASSERT(r, destroyed == 11);
 
     {
-        char storage[64];
-        SkArenaAlloc arena{storage};
+        SkSTArenaAlloc<64> arena;
         arena.makeArrayDefault<char>(256);
         arena.reset();
         arena.reset();
@@ -155,8 +153,7 @@
     {
         created = 0;
         destroyed = 0;
-        char storage[64];
-        SkArenaAlloc arena{storage};
+        SkSTArenaAlloc<64> arena;
 
         Start start;
         Node* current = nullptr;
@@ -173,8 +170,7 @@
     {
         created = 0;
         destroyed = 0;
-        char storage[64];
-        SkArenaAlloc arena{storage};
+        SkSTArenaAlloc<64> arena;
 
         sk_sp<FooRefCnt> f = arena.makeSkSp<FooRefCnt>(4, 5.0f);
         REPORTER_ASSERT(r, f->x == 4);
diff --git a/tests/BitmapCopyTest.cpp b/tests/BitmapCopyTest.cpp
index c30eaf5..dd5bf1a 100644
--- a/tests/BitmapCopyTest.cpp
+++ b/tests/BitmapCopyTest.cpp
@@ -9,69 +9,9 @@
 #include "SkRect.h"
 #include "SkTemplates.h"
 #include "Test.h"
-
-static const char* boolStr(bool value) {
-    return value ? "true" : "false";
-}
-
-static const char* color_type_name(SkColorType colorType) {
-    switch (colorType) {
-        case kUnknown_SkColorType:
-            return "None";
-        case kAlpha_8_SkColorType:
-            return "A8";
-        case kRGB_565_SkColorType:
-            return "565";
-        case kARGB_4444_SkColorType:
-            return "4444";
-        case kRGBA_8888_SkColorType:
-            return "RGBA";
-        case kBGRA_8888_SkColorType:
-            return "BGRA";
-        case kIndex_8_SkColorType:
-            return "Index8";
-        case kGray_8_SkColorType:
-            return "Gray8";
-        case kRGBA_F16_SkColorType:
-            return "F16";
-    }
-    return "";
-}
-
-static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src,
-                              const SkBitmap& dst) {
-    ERRORF(reporter, "src %s opaque:%d, dst %s opaque:%d",
-           color_type_name(src.colorType()), src.isOpaque(),
-           color_type_name(dst.colorType()), dst.isOpaque());
-}
-
-static bool canHaveAlpha(SkColorType ct) {
-    return kRGB_565_SkColorType != ct;
-}
-
-// copyTo() should preserve isOpaque when it makes sense
-static void test_isOpaque(skiatest::Reporter* reporter,
-                          const SkBitmap& srcOpaque, const SkBitmap& srcPremul,
-                          SkColorType dstColorType) {
-    SkBitmap dst;
-
-    if (canHaveAlpha(srcPremul.colorType()) && canHaveAlpha(dstColorType)) {
-        REPORTER_ASSERT(reporter, srcPremul.copyTo(&dst, dstColorType));
-        REPORTER_ASSERT(reporter, dst.colorType() == dstColorType);
-        if (srcPremul.isOpaque() != dst.isOpaque()) {
-            report_opaqueness(reporter, srcPremul, dst);
-        }
-    }
-
-    REPORTER_ASSERT(reporter, srcOpaque.copyTo(&dst, dstColorType));
-    REPORTER_ASSERT(reporter, dst.colorType() == dstColorType);
-    if (srcOpaque.isOpaque() != dst.isOpaque()) {
-        report_opaqueness(reporter, srcOpaque, dst);
-    }
-}
+#include "sk_tool_utils.h"
 
 static void init_src(const SkBitmap& bitmap) {
-    SkAutoLockPixels lock(bitmap);
     if (bitmap.getPixels()) {
         if (bitmap.getColorTable()) {
             sk_bzero(bitmap.getPixels(), bitmap.getSize());
@@ -81,11 +21,11 @@
     }
 }
 
-static SkColorTable* init_ctable() {
+static sk_sp<SkColorTable> init_ctable() {
     static const SkColor colors[] = {
         SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
     };
-    return new SkColorTable(colors, SK_ARRAY_COUNT(colors));
+    return SkColorTable::Make(colors, SK_ARRAY_COUNT(colors));
 }
 
 struct Pair {
@@ -101,60 +41,6 @@
 // reportCopyVerification()
 // writeCoordPixels()
 
-// Utility function to read the value of a given pixel in bm. All
-// values converted to uint32_t for simplification of comparisons.
-static uint32_t getPixel(int x, int y, const SkBitmap& bm) {
-    uint32_t val = 0;
-    uint16_t val16;
-    uint8_t val8;
-    SkAutoLockPixels lock(bm);
-    const void* rawAddr = bm.getAddr(x,y);
-
-    switch (bm.bytesPerPixel()) {
-        case 4:
-            memcpy(&val, rawAddr, sizeof(uint32_t));
-            break;
-        case 2:
-            memcpy(&val16, rawAddr, sizeof(uint16_t));
-            val = val16;
-            break;
-        case 1:
-            memcpy(&val8, rawAddr, sizeof(uint8_t));
-            val = val8;
-            break;
-        default:
-            break;
-    }
-    return val;
-}
-
-// Utility function to set value of any pixel in bm.
-// bm.getConfig() specifies what format 'val' must be
-// converted to, but at present uint32_t can handle all formats.
-static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) {
-    uint16_t val16;
-    uint8_t val8;
-    SkAutoLockPixels lock(bm);
-    void* rawAddr = bm.getAddr(x,y);
-
-    switch (bm.bytesPerPixel()) {
-        case 4:
-            memcpy(rawAddr, &val, sizeof(uint32_t));
-            break;
-        case 2:
-            val16 = val & 0xFFFF;
-            memcpy(rawAddr, &val16, sizeof(uint16_t));
-            break;
-        case 1:
-            val8 = val & 0xFF;
-            memcpy(rawAddr, &val8, sizeof(uint8_t));
-            break;
-        default:
-            // Ignore.
-            break;
-    }
-}
-
 // Helper struct to contain pixel locations, while avoiding need for STL.
 struct Coordinates {
 
@@ -174,31 +60,6 @@
     }
 };
 
-// A function to verify that two bitmaps contain the same pixel values
-// at all coordinates indicated by coords. Simplifies verification of
-// copied bitmaps.
-static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2,
-                            Coordinates& coords,
-                            const char* msg,
-                            skiatest::Reporter* reporter){
-    // Confirm all pixels in the list match.
-    for (int i = 0; i < coords.length; ++i) {
-        uint32_t p1 = getPixel(coords[i]->fX, coords[i]->fY, bm1);
-        uint32_t p2 = getPixel(coords[i]->fX, coords[i]->fY, bm2);
-//        SkDebugf("[%d] (%d %d) p1=%x p2=%x\n", i, coords[i]->fX, coords[i]->fY, p1, p2);
-        if (p1 != p2) {
-            ERRORF(reporter, "%s [colortype = %s]", msg, color_type_name(bm1.colorType()));
-            break;
-        }
-    }
-}
-
-// Writes unique pixel values at locations specified by coords.
-static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) {
-    for (int i = 0; i < coords.length; ++i)
-        setPixel(coords[i]->fX, coords[i]->fY, i, bm);
-}
-
 static const Pair gPairs[] = {
     { kUnknown_SkColorType,     "0000000"  },
     { kAlpha_8_SkColorType,     "0100000"  },
@@ -214,7 +75,7 @@
 
 static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul,
                               SkColorType ct) {
-    SkColorTable* ctable = nullptr;
+    sk_sp<SkColorTable> ctable;
     if (kIndex_8_SkColorType == ct) {
         ctable = init_ctable();
     }
@@ -224,12 +85,8 @@
         colorSpace = SkColorSpace::MakeSRGBLinear();
     }
 
-    srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace),
-                           nullptr, ctable);
-    srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace),
-                           nullptr, ctable);
-    SkSafeUnref(ctable);
-
+    srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace), ctable);
+    srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace), ctable);
     init_src(*srcOpaque);
     init_src(*srcPremul);
 }
@@ -256,7 +113,7 @@
             // Test copying an extracted subset.
             for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
                 SkBitmap copy;
-                bool success = subset.copyTo(&copy, gPairs[j].fColorType);
+                bool success = sk_tool_utils::copy_to(&copy, gPairs[j].fColorType, subset);
                 if (!success) {
                     // Skip checking that success matches fValid, which is redundant
                     // with the code below.
@@ -273,8 +130,6 @@
                 REPORTER_ASSERT(reporter, copy.height() == 2);
 
                 if (gPairs[i].fColorType == gPairs[j].fColorType) {
-                    SkAutoLockPixels alp0(subset);
-                    SkAutoLockPixels alp1(copy);
                     // they should both have, or both not-have, a colortable
                     bool hasCT = subset.getColorTable() != nullptr;
                     REPORTER_ASSERT(reporter, (copy.getColorTable() != nullptr) == hasCT);
@@ -291,284 +146,6 @@
     }
 }
 
-DEF_TEST(BitmapCopy, reporter) {
-    static const bool isExtracted[] = {
-        false, true
-    };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
-        SkBitmap srcOpaque, srcPremul;
-        setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType);
-
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
-            SkBitmap dst;
-
-            bool success = srcPremul.copyTo(&dst, gPairs[j].fColorType);
-            bool expected = gPairs[i].fValid[j] != '0';
-            if (success != expected) {
-                ERRORF(reporter, "SkBitmap::copyTo from %s to %s. expected %s "
-                       "returned %s", color_type_name(gPairs[i].fColorType),
-                       color_type_name(gPairs[j].fColorType),
-                       boolStr(expected), boolStr(success));
-            }
-
-            bool canSucceed = srcPremul.canCopyTo(gPairs[j].fColorType);
-            if (success != canSucceed) {
-                ERRORF(reporter, "SkBitmap::copyTo from %s to %s. returned %s "
-                       "canCopyTo %s", color_type_name(gPairs[i].fColorType),
-                       color_type_name(gPairs[j].fColorType),
-                       boolStr(success), boolStr(canSucceed));
-            }
-
-            if (success) {
-                REPORTER_ASSERT(reporter, srcPremul.width() == dst.width());
-                REPORTER_ASSERT(reporter, srcPremul.height() == dst.height());
-                REPORTER_ASSERT(reporter, dst.colorType() == gPairs[j].fColorType);
-                test_isOpaque(reporter, srcOpaque, srcPremul, dst.colorType());
-                if (srcPremul.colorType() == dst.colorType()) {
-                    SkAutoLockPixels srcLock(srcPremul);
-                    SkAutoLockPixels dstLock(dst);
-                    REPORTER_ASSERT(reporter, srcPremul.readyToDraw());
-                    REPORTER_ASSERT(reporter, dst.readyToDraw());
-                    const char* srcP = (const char*)srcPremul.getAddr(0, 0);
-                    const char* dstP = (const char*)dst.getAddr(0, 0);
-                    REPORTER_ASSERT(reporter, srcP != dstP);
-                    REPORTER_ASSERT(reporter, !memcmp(srcP, dstP,
-                                                      srcPremul.getSize()));
-                    REPORTER_ASSERT(reporter, srcPremul.getGenerationID() == dst.getGenerationID());
-                } else {
-                    REPORTER_ASSERT(reporter, srcPremul.getGenerationID() != dst.getGenerationID());
-                }
-            } else {
-                // dst should be unchanged from its initial state
-                REPORTER_ASSERT(reporter, dst.colorType() == kUnknown_SkColorType);
-                REPORTER_ASSERT(reporter, dst.width() == 0);
-                REPORTER_ASSERT(reporter, dst.height() == 0);
-            }
-        } // for (size_t j = ...
-
-        // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(),
-        // copyPixelsFrom().
-        //
-        for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted);
-             ++copyCase) {
-            // Test copying to/from external buffer.
-            // Note: the tests below have hard-coded values ---
-            //       Please take care if modifying.
-
-            // Tests for getSafeSize64().
-            // Test with a very large configuration without pixel buffer
-            // attached.
-            SkBitmap tstSafeSize;
-            tstSafeSize.setInfo(SkImageInfo::Make(100000000U, 100000000U,
-                                                  gPairs[i].fColorType, kPremul_SkAlphaType));
-            int64_t safeSize = tstSafeSize.computeSafeSize64();
-            if (safeSize < 0) {
-                ERRORF(reporter, "getSafeSize64() negative: %s",
-                       color_type_name(tstSafeSize.colorType()));
-            }
-            bool sizeFail = false;
-            // Compare against hand-computed values.
-            switch (gPairs[i].fColorType) {
-                case kUnknown_SkColorType:
-                    break;
-
-                case kAlpha_8_SkColorType:
-                case kIndex_8_SkColorType:
-                    if (safeSize != 0x2386F26FC10000LL) {
-                        sizeFail = true;
-                    }
-                    break;
-
-                case kRGB_565_SkColorType:
-                case kARGB_4444_SkColorType:
-                    if (safeSize != 0x470DE4DF820000LL) {
-                        sizeFail = true;
-                    }
-                    break;
-
-                case kN32_SkColorType:
-                    if (safeSize != 0x8E1BC9BF040000LL) {
-                        sizeFail = true;
-                    }
-                    break;
-
-                default:
-                    break;
-            }
-            if (sizeFail) {
-                ERRORF(reporter, "computeSafeSize64() wrong size: %s",
-                       color_type_name(tstSafeSize.colorType()));
-            }
-
-            int subW = 2;
-            int subH = 2;
-
-            // Create bitmap to act as source for copies and subsets.
-            SkBitmap src, subset;
-            SkColorTable* ct = nullptr;
-            if (kIndex_8_SkColorType == src.colorType()) {
-                ct = init_ctable();
-            }
-
-            int localSubW;
-            if (isExtracted[copyCase]) { // A larger image to extract from.
-                localSubW = 2 * subW + 1;
-            } else { // Tests expect a 2x2 bitmap, so make smaller.
-                localSubW = subW;
-            }
-            // could fail if we pass kIndex_8 for the colortype
-            if (src.tryAllocPixels(SkImageInfo::Make(localSubW, subH, gPairs[i].fColorType,
-                                                     kPremul_SkAlphaType))) {
-                // failure is fine, as we will notice later on
-            }
-            SkSafeUnref(ct);
-
-            // Either copy src or extract into 'subset', which is used
-            // for subsequent calls to copyPixelsTo/From.
-            bool srcReady = false;
-            // Test relies on older behavior that extractSubset will fail on
-            // kUnknown_SkColorType
-            if (kUnknown_SkColorType != src.colorType() &&
-                isExtracted[copyCase]) {
-                // The extractedSubset() test case allows us to test copy-
-                // ing when src and dst mave possibly different strides.
-                SkIRect r;
-                r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap
-
-                srcReady = src.extractSubset(&subset, r);
-            } else {
-                srcReady = src.copyTo(&subset);
-            }
-
-            // Not all configurations will generate a valid 'subset'.
-            if (srcReady) {
-
-                // Allocate our target buffer 'buf' for all copies.
-                // To simplify verifying correctness of copies attach
-                // buf to a SkBitmap, but copies are done using the
-                // raw buffer pointer.
-                const size_t bufSize = subH *
-                    SkColorTypeMinRowBytes(src.colorType(), subW) * 2;
-                SkAutoTMalloc<uint8_t> autoBuf (bufSize);
-                uint8_t* buf = autoBuf.get();
-
-                SkBitmap bufBm; // Attach buf to this bitmap.
-                bool successExpected;
-
-                // Set up values for each pixel being copied.
-                Coordinates coords(subW * subH);
-                for (int x = 0; x < subW; ++x)
-                    for (int y = 0; y < subH; ++y)
-                    {
-                        int index = y * subW + x;
-                        SkASSERT(index < coords.length);
-                        coords[index]->fX = x;
-                        coords[index]->fY = y;
-                    }
-
-                writeCoordPixels(subset, coords);
-
-                // Test #1 ////////////////////////////////////////////
-
-                const SkImageInfo info = SkImageInfo::Make(subW, subH,
-                                                           gPairs[i].fColorType,
-                                                           kPremul_SkAlphaType);
-                // Before/after comparisons easier if we attach buf
-                // to an appropriately configured SkBitmap.
-                memset(buf, 0xFF, bufSize);
-                // Config with stride greater than src but that fits in buf.
-                bufBm.installPixels(info, buf, info.minRowBytes() * 2);
-                successExpected = false;
-                // Then attempt to copy with a stride that is too large
-                // to fit in the buffer.
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3)
-                    == successExpected);
-
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                        "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)",
-                        reporter);
-
-                // Test #2 ////////////////////////////////////////////
-                // This test should always succeed, but in the case
-                // of extracted bitmaps only because we handle the
-                // issue of getSafeSize(). Without getSafeSize()
-                // buffer overrun/read would occur.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, subset.rowBytes());
-                successExpected = subset.getSafeSize() <= bufSize;
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsTo(buf, bufSize) ==
-                        successExpected);
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                    "copyPixelsTo(buf, bufSize)", reporter);
-
-                // Test #3 ////////////////////////////////////////////
-                // Copy with different stride between src and dst.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, subset.rowBytes()+1);
-                successExpected = true; // Should always work.
-                REPORTER_ASSERT(reporter,
-                        subset.copyPixelsTo(buf, bufSize,
-                            subset.rowBytes()+1) == successExpected);
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                    "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter);
-
-                // Test #4 ////////////////////////////////////////////
-                // Test copy with stride too small.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, info.minRowBytes());
-                successExpected = false;
-                // Request copy with stride too small.
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1)
-                        == successExpected);
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                    "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter);
-
-#if 0   // copyPixelsFrom is gone
-                // Test #5 ////////////////////////////////////////////
-                // Tests the case where the source stride is too small
-                // for the source configuration.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, info.minRowBytes());
-                writeCoordPixels(bufBm, coords);
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsFrom(buf, bufSize, 1) == false);
-
-                // Test #6 ///////////////////////////////////////////
-                // Tests basic copy from an external buffer to the bitmap.
-                // If the bitmap is "extracted", this also tests the case
-                // where the source stride is different from the dest.
-                // stride.
-                // We've made the buffer large enough to always succeed.
-                bufBm.installPixels(info, buf, info.minRowBytes());
-                writeCoordPixels(bufBm, coords);
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) ==
-                        true);
-                reportCopyVerification(bufBm, subset, coords,
-                    "copyPixelsFrom(buf, bufSize)",
-                    reporter);
-
-                // Test #7 ////////////////////////////////////////////
-                // Tests the case where the source buffer is too small
-                // for the transfer.
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsFrom(buf, 1, subset.rowBytes()) ==
-                        false);
-
-#endif
-            }
-        } // for (size_t copyCase ...
-    }
-}
-
 #include "SkColorPriv.h"
 #include "SkUtils.h"
 
@@ -660,20 +237,3 @@
         }
     }
 }
-
-DEF_TEST(BitmapCopy_ColorSpaceMatch, r) {
-    // We should support matching color spaces, even if they are parametric.
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f;
-    sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(fn, SkColorSpace::kRec2020_Gamut);
-
-    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1, cs);
-    SkBitmap bitmap;
-    bitmap.allocPixels(info);
-    bitmap.eraseColor(0);
-
-    SkBitmap copy;
-    bool success = bitmap.copyTo(&copy, kN32_SkColorType, nullptr);
-    REPORTER_ASSERT(r, success);
-    REPORTER_ASSERT(r, cs.get() == copy.colorSpace());
-}
diff --git a/tests/BitmapTest.cpp b/tests/BitmapTest.cpp
index 9342d26..e0a7f78 100644
--- a/tests/BitmapTest.cpp
+++ b/tests/BitmapTest.cpp
@@ -9,6 +9,7 @@
 #include "SkMallocPixelRef.h"
 #include "SkRandom.h"
 #include "Test.h"
+#include "sk_tool_utils.h"
 
 static void test_peekpixels(skiatest::Reporter* reporter) {
     const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
@@ -44,7 +45,7 @@
     SkBitmap bm;
     REPORTER_ASSERT(reporter, !bm.tryAllocPixels(info));
 
-    SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, info.minRowBytes(), nullptr);
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, info.minRowBytes(), nullptr);
     REPORTER_ASSERT(reporter, !pr);
 }
 
@@ -137,12 +138,10 @@
     };
     for (SkColorType ct : colorTypes) {
         SkBitmap copy;
-        if (!source.copyTo(&copy, ct)) {
+        if (!sk_tool_utils::copy_to(&copy, ct, source)) {
             ERRORF(r, "SkBitmap::copy failed %d", (int)ct);
             continue;
         }
-        SkAutoLockPixels autoLockPixels1(copy);
-        SkAutoLockPixels autoLockPixels2(source);
         REPORTER_ASSERT(r, source.getColor(0, 0) == copy.getColor(0, 0));
     }
 }
diff --git a/tests/BlendTest.cpp b/tests/BlendTest.cpp
index 785acb8..20ea6e0 100644
--- a/tests/BlendTest.cpp
+++ b/tests/BlendTest.cpp
@@ -5,11 +5,25 @@
  * found in the LICENSE file.
  */
 
-#include "Test.h"
+#include <functional>
+#include "SkBitmap.h"
+#include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkColorPriv.h"
+#include "SkSurface.h"
 #include "SkTaskGroup.h"
-#include <functional>
+#include "SkUtils.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
+#include "GrTest.h"
+#include "GrTexture.h"
+#endif
 
 struct Results { int diffs, diffs_0x00, diffs_0xff, diffs_by_1; };
 
@@ -66,3 +80,127 @@
     };
     for (auto multiply : perfect) { REPORTER_ASSERT(r, test(multiply).diffs == 0); }
 }
+
+#if SK_SUPPORT_GPU
+namespace {
+static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target(
+        GrContext* context, int sampleCnt, int width, int height, GrPixelConfig config,
+        GrSurfaceOrigin origin,
+        sk_sp<GrTexture>* backingSurface) {
+    GrSurfaceDesc backingDesc;
+    backingDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    backingDesc.fOrigin = origin;
+    backingDesc.fWidth = width;
+    backingDesc.fHeight = height;
+    backingDesc.fConfig = config;
+    backingDesc.fSampleCnt = sampleCnt;
+
+    *backingSurface = context->resourceProvider()->createTexture(backingDesc, SkBudgeted::kNo);
+    if (!(*backingSurface)) {
+        return nullptr;
+    }
+
+    GrBackendTexture backendTex =
+            GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                         width,
+                                         height,
+                                         config,
+                                         (*backingSurface)->getTextureHandle());
+    sk_sp<SkSurface> surface =
+            SkSurface::MakeFromBackendTextureAsRenderTarget(context, backendTex, origin,
+                                                            sampleCnt, nullptr, nullptr);
+
+    return surface;
+}
+}
+
+// Tests blending to a surface with no texture available.
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ES2BlendWithNoTexture, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    const int kWidth = 10;
+    const int kHeight = 10;
+    const GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
+    const SkColorType kColorType = kRGBA_8888_SkColorType;
+
+    // Build our test cases:
+    struct RectAndSamplePoint {
+        SkRect rect;
+        SkIPoint outPoint;
+        SkIPoint inPoint;
+    } allRectsAndPoints[3] = {
+            {SkRect::MakeXYWH(0, 0, 5, 5), SkIPoint::Make(7, 7), SkIPoint::Make(2, 2)},
+            {SkRect::MakeXYWH(2, 2, 5, 5), SkIPoint::Make(1, 1), SkIPoint::Make(4, 4)},
+            {SkRect::MakeXYWH(5, 5, 5, 5), SkIPoint::Make(2, 2), SkIPoint::Make(7, 7)},
+    };
+
+    struct TestCase {
+        RectAndSamplePoint fRectAndPoints;
+        SkRect             fClip;
+        int                fSampleCnt;
+        GrSurfaceOrigin    fOrigin;
+    };
+    std::vector<TestCase> testCases;
+
+    for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
+        for (int sampleCnt : {0, 4}) {
+            for (auto rectAndPoints : allRectsAndPoints) {
+                for (auto clip : {SkRect::MakeXYWH(0, 0, 10, 10), SkRect::MakeXYWH(1, 1, 8, 8)}) {
+                    testCases.push_back({rectAndPoints, clip, sampleCnt, origin});
+                }
+            }
+        }
+    }
+
+    // Run each test case:
+    for (auto testCase : testCases) {
+        int sampleCnt = testCase.fSampleCnt;
+        SkRect paintRect = testCase.fRectAndPoints.rect;
+        SkIPoint outPoint = testCase.fRectAndPoints.outPoint;
+        SkIPoint inPoint = testCase.fRectAndPoints.inPoint;
+        GrSurfaceOrigin origin = testCase.fOrigin;
+
+        sk_sp<GrTexture> backingSurface;
+        // BGRA forces a framebuffer blit on ES2.
+        sk_sp<SkSurface> surface = create_gpu_surface_backend_texture_as_render_target(
+                context, sampleCnt, kWidth, kHeight, kConfig, origin, &backingSurface);
+
+        if (!surface && sampleCnt > 0) {
+            // Some platforms don't support MSAA.
+            continue;
+        }
+        REPORTER_ASSERT(reporter, !!surface);
+
+        // Fill our canvas with 0xFFFF80
+        SkCanvas* canvas = surface->getCanvas();
+        canvas->clipRect(testCase.fClip, false);
+        SkPaint black_paint;
+        black_paint.setColor(SkColorSetRGB(0xFF, 0xFF, 0x80));
+        canvas->drawRect(SkRect::MakeXYWH(0, 0, kWidth, kHeight), black_paint);
+
+        // Blend 2x2 pixels at 5,5 with 0x80FFFF. Use multiply blend mode as this will trigger
+        // a copy of the destination.
+        SkPaint white_paint;
+        white_paint.setColor(SkColorSetRGB(0x80, 0xFF, 0xFF));
+        white_paint.setBlendMode(SkBlendMode::kMultiply);
+        canvas->drawRect(paintRect, white_paint);
+
+        // Read the result into a bitmap.
+        SkBitmap bitmap;
+        REPORTER_ASSERT(reporter, bitmap.tryAllocPixels(SkImageInfo::Make(
+                                          kWidth, kHeight, kColorType, kPremul_SkAlphaType)));
+        REPORTER_ASSERT(
+                reporter,
+                surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0));
+
+        // Check the in/out pixels.
+        REPORTER_ASSERT(reporter, bitmap.getColor(outPoint.x(), outPoint.y()) ==
+                                          SkColorSetRGB(0xFF, 0xFF, 0x80));
+        REPORTER_ASSERT(reporter, bitmap.getColor(inPoint.x(), inPoint.y()) ==
+                                          SkColorSetRGB(0x80, 0xFF, 0x80));
+
+        // Clean up - surface depends on backingSurface and must be released first.
+        surface.reset();
+        backingSurface.reset();
+    }
+}
+#endif
diff --git a/tests/BlitRowTest.cpp b/tests/BlitRowTest.cpp
deleted file mode 100644
index 3439a5e..0000000
--- a/tests/BlitRowTest.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColorPriv.h"
-#include "SkGradientShader.h"
-#include "SkRect.h"
-#include "Test.h"
-
-#include "sk_tool_utils.h"
-
-// these are in the same order as the SkColorType enum
-static const char* gColorTypeName[] = {
-    "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8"
-};
-
-/** Returns -1 on success, else the x coord of the first bad pixel, return its
-    value in bad
- */
-typedef int (*Proc)(const void*, int width, uint32_t expected, uint32_t* bad);
-
-static int proc_32(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
-    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
-    for (int x = 0; x < w; x++) {
-        if (addr[x] != expected) {
-            *bad = addr[x];
-            return x;
-        }
-    }
-    return -1;
-}
-
-static int proc_16(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
-    const uint16_t* addr = static_cast<const uint16_t*>(ptr);
-    for (int x = 0; x < w; x++) {
-        if (addr[x] != expected) {
-            *bad = addr[x];
-            return x;
-        }
-    }
-    return -1;
-}
-
-static int proc_8(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
-    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
-    for (int x = 0; x < w; x++) {
-        if (SkGetPackedA32(addr[x]) != expected) {
-            *bad = SkGetPackedA32(addr[x]);
-            return x;
-        }
-    }
-    return -1;
-}
-
-static int proc_bad(const void*, int, uint32_t, uint32_t* bad) {
-    *bad = 0;
-    return 0;
-}
-
-static Proc find_proc(const SkBitmap& bm, SkPMColor expect32, uint16_t expect16,
-                      uint8_t expect8, uint32_t* expect) {
-    switch (bm.colorType()) {
-        case kN32_SkColorType:
-            *expect = expect32;
-            return proc_32;
-        case kARGB_4444_SkColorType:
-        case kRGB_565_SkColorType:
-            *expect = expect16;
-            return proc_16;
-        case kAlpha_8_SkColorType:
-            *expect = expect8;
-            return proc_8;
-        default:
-            *expect = 0;
-            return proc_bad;
-    }
-}
-
-static bool check_color(const SkBitmap& bm, SkPMColor expect32,
-                        uint16_t expect16, uint8_t expect8,
-                        skiatest::Reporter* reporter) {
-    uint32_t expect;
-    Proc proc = find_proc(bm, expect32, expect16, expect8, &expect);
-    for (int y = 0; y < bm.height(); y++) {
-        uint32_t bad;
-        int x = proc(bm.getAddr(0, y), bm.width(), expect, &bad);
-        if (x >= 0) {
-            ERRORF(reporter, "BlitRow colortype=%s [%d %d] expected %x got %x",
-                   gColorTypeName[bm.colorType()], x, y, expect, bad);
-            return false;
-        }
-    }
-    return true;
-}
-
-// Make sure our blits always map src==0 to a noop, and src==FF to full opaque
-static void test_00_FF(skiatest::Reporter* reporter) {
-    static const int W = 256;
-
-    static const SkColorType gDstColorType[] = {
-        kN32_SkColorType,
-        kRGB_565_SkColorType,
-    };
-
-    static const struct {
-        SkColor     fSrc;
-        SkColor     fDst;
-        SkPMColor   fResult32;
-        uint16_t    fResult16;
-        uint8_t     fResult8;
-    } gSrcRec[] = {
-        { 0,            0,          0,                                    0,      0 },
-        { 0,            0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
-        { 0xFFFFFFFF,   0,          SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
-        { 0xFFFFFFFF,   0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
-    };
-
-    SkPaint paint;
-
-    SkBitmap srcBM;
-    srcBM.allocN32Pixels(W, 1);
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstColorType); i++) {
-        SkImageInfo info = SkImageInfo::Make(W, 1, gDstColorType[i],
-                                             kPremul_SkAlphaType);
-        SkBitmap dstBM;
-        dstBM.allocPixels(info);
-
-        SkCanvas canvas(dstBM);
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) {
-            srcBM.eraseColor(gSrcRec[j].fSrc);
-            dstBM.eraseColor(gSrcRec[j].fDst);
-
-            for (int k = 0; k < 4; k++) {
-                bool dither = (k & 1) != 0;
-                bool blend = (k & 2) != 0;
-                if (gSrcRec[j].fSrc != 0 && blend) {
-                    // can't make a numerical promise about blending anything
-                    // but 0
-                 //   continue;
-                }
-                paint.setDither(dither);
-                paint.setAlpha(blend ? 0x80 : 0xFF);
-                canvas.drawBitmap(srcBM, 0, 0, &paint);
-                if (!check_color(dstBM, gSrcRec[j].fResult32, gSrcRec[j].fResult16,
-                                 gSrcRec[j].fResult8, reporter)) {
-                    SkDebugf("--- src index %d dither %d blend %d\n", j, dither, blend);
-                }
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct Mesh {
-    SkPoint     fPts[4];
-
-    Mesh(const SkBitmap& bm, SkPaint* paint) {
-        const SkScalar w = SkIntToScalar(bm.width());
-        const SkScalar h = SkIntToScalar(bm.height());
-        fPts[0].set(0, 0);
-        fPts[1].set(w, 0);
-        fPts[2].set(w, h);
-        fPts[3].set(0, h);
-        paint->setShader(SkShader::MakeBitmapShader(bm, SkShader::kClamp_TileMode,
-                                                    SkShader::kClamp_TileMode));
-    }
-
-    void draw(SkCanvas* canvas, SkPaint* paint) {
-        canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, fPts, fPts,
-                             nullptr, SkBlendMode::kModulate, nullptr, 0, *paint);
-    }
-};
-
-#include "SkImageEncoder.h"
-static void save_bm(const SkBitmap& bm, const char name[]) {
-    sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
-}
-
-static bool gOnce;
-
-// Make sure our blits are invariant with the width of the blit (i.e. that
-// special case for 8 at a time have the same results as narrower blits)
-static void test_diagonal(skiatest::Reporter* reporter) {
-    static const int W = 64;
-    static const int H = W;
-
-    static const SkColorType gDstColorType[] = {
-        kN32_SkColorType,
-        kRGB_565_SkColorType,
-    };
-
-    static const SkColor gDstBG[] = { 0, 0xFFFFFFFF };
-    const SkRect srcR = SkRect::MakeIWH(W, H);
-
-    SkBitmap srcBM;
-    srcBM.allocN32Pixels(W, H);
-    SkImageInfo info = SkImageInfo::Make(W, H, kUnknown_SkColorType, kPremul_SkAlphaType);
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstColorType); i++) {
-        info = info.makeColorType(gDstColorType[i]);
-
-        SkBitmap dstBM0, dstBM1;
-        dstBM0.allocPixels(info);
-        dstBM1.allocPixels(info);
-
-        SkCanvas canvas0(dstBM0);
-        SkCanvas canvas1(dstBM1);
-        SkColor bgColor;
-
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gDstBG); j++) {
-            bgColor = gDstBG[j];
-
-            for (int c = 0; c <= 0xFF; c++) {
-                // cons up a mesh to draw the bitmap with
-                SkPaint paint;
-                srcBM.eraseARGB(0xFF, c, c, c);
-                Mesh mesh(srcBM, &paint);
-
-                for (int k = 0; k < 4; k++) {
-                    bool dither = (k & 1) != 0;
-                    uint8_t alpha = (k & 2) ? 0x80 : 0xFF;
-                    paint.setDither(dither);
-                    paint.setAlpha(alpha);
-
-                    dstBM0.eraseColor(bgColor);
-                    dstBM1.eraseColor(bgColor);
-
-                    canvas0.drawRect(srcR, paint);
-                    mesh.draw(&canvas1, &paint);
-
-                    if (!gOnce && false) {
-                        save_bm(dstBM0, "drawBitmap.png");
-                        save_bm(dstBM1, "drawMesh.png");
-                        gOnce = true;
-                    }
-
-                    if (memcmp(dstBM0.getPixels(), dstBM1.getPixels(), dstBM0.getSize())) {
-                        ERRORF(reporter, "Diagonal colortype=%s bg=0x%x dither=%d"
-                               " alpha=0x%x src=0x%x",
-                               gColorTypeName[gDstColorType[i]], bgColor, dither,
-                               alpha, c);
-                    }
-                }
-            }
-        }
-    }
-}
-
-DEF_TEST(BlitRow, reporter) {
-    test_00_FF(reporter);
-    test_diagonal(reporter);
-}
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
index 744e202..5d95718 100644
--- a/tests/BlurTest.cpp
+++ b/tests/BlurTest.cpp
@@ -76,9 +76,6 @@
     const int xOff = itest.fLeft - iref.fLeft;
     const int yOff = itest.fTop - iref.fTop;
 
-    SkAutoLockPixels alpRef(ref);
-    SkAutoLockPixels alpTest(test);
-
     for (int y = 0; y < test.height(); ++y) {
         for (int x = 0; x < test.width(); ++x) {
             SkColor testColor = test.getColor(x, y);
@@ -242,12 +239,8 @@
 static void readback(SkCanvas* canvas, int* result, int resultCount) {
     SkBitmap readback;
     readback.allocN32Pixels(resultCount, 30);
+    canvas->readPixels(readback, 0, 0);
 
-    SkIRect readBackRect = { 0, 0, resultCount, 30 };
-
-    canvas->readPixels(readBackRect, &readback);
-
-    readback.lockPixels();
     SkPMColor* pixels = (SkPMColor*) readback.getAddr32(0, 15);
 
     for (int i = 0; i < resultCount; ++i) {
diff --git a/tests/CachedDataTest.cpp b/tests/CachedDataTest.cpp
index b2d4785..fe7bb5f 100644
--- a/tests/CachedDataTest.cpp
+++ b/tests/CachedDataTest.cpp
@@ -72,7 +72,7 @@
  *  and when the cache is.
  */
 DEF_TEST(CachedData, reporter) {
-    sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Create(1000));
+    sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1000));
 
     for (int useDiscardable = 0; useDiscardable <= 1; ++useDiscardable) {
         const size_t size = 100;
diff --git a/tests/CachedDecodingPixelRefTest.cpp b/tests/CachedDecodingPixelRefTest.cpp
index c423441..0eecdbb 100644
--- a/tests/CachedDecodingPixelRefTest.cpp
+++ b/tests/CachedDecodingPixelRefTest.cpp
@@ -29,7 +29,7 @@
     static int Width() { return 10; }
     static int Height() { return 10; }
     // value choosen so that there is no loss when converting to to RGB565 and back
-    static SkColor Color() { return 0xff10345a; }
+    static SkColor Color() { return 0xff10355a; }
     static SkPMColor PMColor() { return SkPreMultiplyColor(Color()); }
 
     TestImageGenerator(TestType type, skiatest::Reporter* reporter,
@@ -46,7 +46,7 @@
     }
 
     bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                     SkPMColor ctable[], int* ctableCount) override {
+                     const Options& options) override {
         REPORTER_ASSERT(fReporter, pixels != nullptr);
         REPORTER_ASSERT(fReporter, rowBytes >= info.minRowBytes());
         if (fType != kSucceedGetPixels_TestType) {
@@ -64,14 +64,6 @@
                     bytePtr += rowBytes;
                 }
                 break;
-            case kIndex_8_SkColorType:
-                *ctableCount = 1;
-                ctable[0] = TestImageGenerator::PMColor();
-                for (int y = 0; y < info.height(); ++y) {
-                    memset(bytePtr, 0, info.width());
-                    bytePtr += rowBytes;
-                }
-                break;
             case kRGB_565_SkColorType:
                 for (int y = 0; y < info.height(); ++y) {
                     sk_memset16((uint16_t*)bytePtr,
@@ -101,7 +93,6 @@
     };
     const SkColorType testColorTypes[] = {
         kN32_SkColorType,
-        kIndex_8_SkColorType,
         kRGB_565_SkColorType
     };
     for (size_t i = 0; i < SK_ARRAY_COUNT(testTypes); ++i) {
diff --git a/tests/CanvasStateTest.cpp b/tests/CanvasStateTest.cpp
index 73c8a0e..ae417d4 100644
--- a/tests/CanvasStateTest.cpp
+++ b/tests/CanvasStateTest.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "CanvasStateHelpers.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkClipOpPriv.h"
 #include "SkCanvasStateUtils.h"
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index 4a32163..e64f5e9 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -65,6 +65,7 @@
 #include "SkSurface.h"
 #include "SkTemplates.h"
 #include "SkTDArray.h"
+#include "SkVertices.h"
 #include "Test.h"
 
 DEF_TEST(canvas_clipbounds, reporter) {
@@ -129,6 +130,7 @@
     canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);
 
+#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
     // now test that expanding clipops can't exceed the restriction
     const SkClipOp expanders[] = {
         SkClipOp::kUnion_deprecated,
@@ -146,6 +148,7 @@
         REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds()));
         canvas->restore();
     }
+#endif
 }
 
 /**
@@ -274,24 +277,6 @@
     }
 };
 
-class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor {
-public:
-    Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {}
-
-    void clipRect(const SkRect& r, SkClipOp op, bool aa) override {
-        fTarget->clipRect(r, op, aa);
-    }
-    void clipRRect(const SkRRect& r, SkClipOp op, bool aa) override {
-        fTarget->clipRRect(r, op, aa);
-    }
-    void clipPath(const SkPath& p, SkClipOp op, bool aa) override {
-        fTarget->clipPath(p, op, aa);
-    }
-
-private:
-    SkCanvas* fTarget;
-};
-
 // Format strings that describe the test context.  The %s token is where
 // the name of the test step is inserted.  The context is required for
 // disambiguating the error in the case of failures that are reported in
@@ -476,8 +461,9 @@
     SkPaint paint;
     paint.setShader(SkShader::MakeBitmapShader(d.fBitmap, SkShader::kClamp_TileMode,
                                                SkShader::kClamp_TileMode));
-    canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts,
-                         nullptr, SkBlendMode::kModulate, nullptr, 0, paint);
+    canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts,
+                                              nullptr),
+                         SkBlendMode::kModulate, paint);
 }
 // NYI: issue 240.
 TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep);
@@ -557,45 +543,6 @@
 }
 TEST_STEP(NestedSaveRestoreWithFlush, NestedSaveRestoreWithFlushTestStep);
 
-static void DescribeTopLayerTestStep(SkCanvas* canvas,
-                                     const TestData& d,
-                                     skiatest::Reporter* reporter,
-                                     CanvasTestStep* testStep) {
-    SkMatrix m;
-    SkIRect r;
-    // NOTE: adjustToTopLayer() does *not* reduce the clip size, even if the canvas
-    // is smaller than 10x10!
-
-    canvas->temporary_internal_describeTopLayer(&m, &r);
-    REPORTER_ASSERT_MESSAGE(reporter, m.isIdentity(), testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, r == SkIRect::MakeXYWH(0, 0, 2, 2),
-                            testStep->assertMessage());
-
-    // Putting a full-canvas layer on it should make no change to the results.
-    SkRect layerBounds = SkRect::MakeXYWH(0.f, 0.f, 10.f, 10.f);
-    canvas->saveLayer(layerBounds, nullptr);
-    canvas->temporary_internal_describeTopLayer(&m, &r);
-    REPORTER_ASSERT_MESSAGE(reporter, m.isIdentity(), testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, r == SkIRect::MakeXYWH(0, 0, 2, 2),
-                            testStep->assertMessage());
-    canvas->restore();
-
-    // Adding a translated layer translates the results.
-    // Default canvas is only 2x2, so can't offset our layer by very much at all;
-    // saveLayer() aborts if the bounds don't intersect.
-    layerBounds = SkRect::MakeXYWH(1.f, 1.f, 6.f, 6.f);
-    canvas->saveLayer(layerBounds, nullptr);
-    canvas->temporary_internal_describeTopLayer(&m, &r);
-    REPORTER_ASSERT_MESSAGE(reporter, m == SkMatrix::MakeTrans(-1.f, -1.f),
-                            testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, r == SkIRect::MakeXYWH(0, 0, 1, 1),
-                            testStep->assertMessage());
-    canvas->restore();
-
-}
-TEST_STEP(DescribeTopLayer, DescribeTopLayerTestStep);
-
-
 static void TestPdfDevice(skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* step) {
     SkDynamicMemoryWStream outStream;
     sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
@@ -713,46 +660,6 @@
     canvas.restore();
 }
 
-#define SHADOW_TEST_CANVAS_CONST 10
-#ifdef SK_EXPERIMENTAL_SHADOWING
-class SkShadowTestCanvas : public SkPaintFilterCanvas {
-public:
-
-    SkShadowTestCanvas(int x, int y, skiatest::Reporter* reporter)
-        : INHERITED(x,y)
-        , fReporter(reporter) {}
-
-    bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const {
-        REPORTER_ASSERT(this->fReporter, this->getZ() == SHADOW_TEST_CANVAS_CONST);
-
-        return true;
-    }
-
-    void testUpdateDepth(skiatest::Reporter *reporter) {
-        // set some depths (with picture enabled), then check them as they get set
-
-        REPORTER_ASSERT(reporter, this->getZ() == 0);
-        this->translateZ(-10);
-        REPORTER_ASSERT(reporter, this->getZ() == -10);
-
-        this->save();
-        this->translateZ(20);
-        REPORTER_ASSERT(reporter, this->getZ() == 10);
-
-        this->restore();
-        REPORTER_ASSERT(reporter, this->getZ() == -10);
-
-        this->translateZ(13.14f);
-        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(this->getZ(), 3.14f));
-    }
-
-private:
-    skiatest::Reporter* fReporter;
-
-    typedef SkPaintFilterCanvas INHERITED;
-};
-#endif
-
 namespace {
 
 class MockFilterCanvas : public SkPaintFilterCanvas {
@@ -782,22 +689,6 @@
     filterCanvas.scale(0.75f, 0.5f);
     REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
     REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
-
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    SkShadowTestCanvas* tCanvas = new SkShadowTestCanvas(100,100, reporter);
-    tCanvas->testUpdateDepth(reporter);
-    delete(tCanvas);
-
-    SkPictureRecorder recorder;
-    SkShadowTestCanvas *tSCanvas = new SkShadowTestCanvas(100, 100, reporter);
-    SkCanvas *tPCanvas = recorder.beginRecording(SkRect::MakeIWH(100, 100));
-
-    tPCanvas->translateZ(SHADOW_TEST_CANVAS_CONST);
-    sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
-    tSCanvas->drawPicture(pic);
-
-    delete(tSCanvas);
-#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/ClearTest.cpp b/tests/ClearTest.cpp
index d4b8587..b6ae685 100644
--- a/tests/ClearTest.cpp
+++ b/tests/ClearTest.cpp
@@ -17,12 +17,17 @@
 
 static bool check_rect(GrRenderTargetContext* rtc, const SkIRect& rect, uint32_t expectedValue,
                        uint32_t* actualValue, int* failX, int* failY) {
-    GrRenderTarget* rt = rtc->accessRenderTarget();
     int w = rect.width();
     int h = rect.height();
     std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
     memset(pixels.get(), ~expectedValue, sizeof(uint32_t) * w * h);
-    rt->readPixels(rect.fLeft, rect.fTop, w, h, kRGBA_8888_GrPixelConfig, pixels.get());
+
+    SkImageInfo dstInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+    if (!rtc->readPixels(dstInfo, pixels.get(), 0, rect.fLeft, rect.fTop)) {
+        return false;
+    }
+
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
             uint32_t pixel = pixels.get()[y * w + x];
@@ -37,27 +42,9 @@
     return true;
 }
 
-// TODO: this test does this thorough purging of the rendertargets b.c. right now
-// the clear optimizations rely on the rendertarget's uniqueID. It can be
-// relaxed when we switch that over to using rendertargetcontext ids (although
-// we probably will want to have more clear values then too)
-static bool reset_rtc(sk_sp<GrRenderTargetContext>* rtc, GrContext* context, int w, int h) {
-#ifdef SK_DEBUG
-    GrGpuResource::UniqueID oldID = GrGpuResource::UniqueID::InvalidID();
-#endif
-
-    if (*rtc) {
-        SkDEBUGCODE(oldID = (*rtc)->accessRenderTarget()->uniqueID();)
-        rtc->reset(nullptr);
-    }
-    context->freeGpuResources();
-
-    *rtc = context->makeRenderTargetContext(SkBackingFit::kExact, w, h, kRGBA_8888_GrPixelConfig,
-                                            nullptr);
-
-    SkASSERT((*rtc)->accessRenderTarget()->uniqueID() != oldID);
-
-    return *rtc != nullptr;
+sk_sp<GrRenderTargetContext> newRTC(GrContext* context, int w, int h) {
+    return context->makeDeferredRenderTargetContext(SkBackingFit::kExact, w, h,
+                                                    kRGBA_8888_GrPixelConfig, nullptr);
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ClearOp, reporter, ctxInfo) {
@@ -90,10 +77,9 @@
     static const GrColor kColor1 = 0xABCDEF01;
     static const GrColor kColor2 = ~kColor1;
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Check a full clear
     rtContext->clear(&fullRect, kColor1, false);
     if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
@@ -101,10 +87,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Check two full clears, same color
     rtContext->clear(&fullRect, kColor1, false);
     rtContext->clear(&fullRect, kColor1, false);
@@ -113,10 +98,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Check two full clears, different colors
     rtContext->clear(&fullRect, kColor1, false);
     rtContext->clear(&fullRect, kColor2, false);
@@ -125,10 +109,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Test a full clear followed by a same color inset clear
     rtContext->clear(&fullRect, kColor1, false);
     rtContext->clear(&mid1Rect, kColor1, false);
@@ -137,10 +120,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Test a inset clear followed by same color full clear
     rtContext->clear(&mid1Rect, kColor1, false);
     rtContext->clear(&fullRect, kColor1, false);
@@ -149,10 +131,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Test a full clear followed by a different color inset clear
     rtContext->clear(&fullRect, kColor1, false);
     rtContext->clear(&mid1Rect, kColor2, false);
@@ -168,10 +149,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Test a inset clear followed by a different full clear
     rtContext->clear(&mid1Rect, kColor2, false);
     rtContext->clear(&fullRect, kColor1, false);
@@ -180,10 +160,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Check three nested clears from largest to smallest where outermost and innermost are same
     // color.
     rtContext->clear(&fullRect, kColor1, false);
@@ -208,10 +187,9 @@
                failX, failY);
     }
 
-    if (!reset_rtc(&rtContext, context, kW, kH)) {
-        ERRORF(reporter, "Could not create render target context.");
-        return;
-    }
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
     // Swap the order of the second two clears in the above test.
     rtContext->clear(&fullRect, kColor1, false);
     rtContext->clear(&mid2Rect, kColor1, false);
diff --git a/tests/ClipCubicTest.cpp b/tests/ClipCubicTest.cpp
index 1d88eae..854ac32 100644
--- a/tests/ClipCubicTest.cpp
+++ b/tests/ClipCubicTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkCubicClipper.h"
 #include "SkGeometry.h"
@@ -165,41 +166,41 @@
 
 #include "SkSurface.h"
 
-DEF_TEST(test_fuzz_crbug_698714, reporter) {

-    auto surface(SkSurface::MakeRasterN32Premul(500, 500));

-    SkCanvas* canvas = surface->getCanvas();

-    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

-    path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x434300be));  //-7.2741e-07f, 195.003f

-    // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 3.85088e-29f,1.86082e-39f

-    path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341),

-            SkBits2Float(0xb74343bd), SkBits2Float(0x01434343),

-            SkBits2Float(0x10434343), SkBits2Float(0x00144332));

-    // 4.11823e-38f, 195.263f, 195.263f, 195.263f, -7.2741e-07f, 195.263f

-    path.cubicTo(SkBits2Float(0x016037c0), SkBits2Float(0x43434343),

-            SkBits2Float(0x43434343), SkBits2Float(0x43434343),

-            SkBits2Float(0xb5434343), SkBits2Float(0x43434343));

-    // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 195.263f, -2

-    path.cubicTo(SkBits2Float(0x43434344), SkBits2Float(0x43434341),

-            SkBits2Float(0xb74343bd), SkBits2Float(0x01434343),

-            SkBits2Float(0x43434343), SkBits2Float(0xc0000014));

-    // -5.87228e+06f, 3.7773e-07f, 3.60231e-13f, -6.64511e+06f,2.77692e-15f, 2.48803e-15f

-    path.cubicTo(SkBits2Float(0xcab33535), SkBits2Float(0x34cacaca),

-            SkBits2Float(0x2acacaca), SkBits2Float(0xcacacae3),

-            SkBits2Float(0x27481927), SkBits2Float(0x27334805));

-    path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x43434343));  //-7.2741e-07f, 195.263f

-    // 195.263f, 195.263f, -1.16387e-05f, 195.212f, 195.263f, -2

-    path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341),

-            SkBits2Float(0xb74343b9), SkBits2Float(0x43433643),

-            SkBits2Float(0x43434343), SkBits2Float(0xc0000014));

-    path.lineTo(SkBits2Float(0xc7004343), SkBits2Float(0x27480527));  //-32835.3f, 2.77584e-15f

-    path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0,0

-    path.close();

-    canvas->clipRect({0, 0, 65, 202});

-    canvas->drawPath(path, paint);

-}

+DEF_TEST(test_fuzz_crbug_698714, reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(500, 500));
+    SkCanvas* canvas = surface->getCanvas();
+    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
+    path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x434300be));  //-7.2741e-07f, 195.003f
+    // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 3.85088e-29f,1.86082e-39f
+    path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341),
+            SkBits2Float(0xb74343bd), SkBits2Float(0x01434343),
+            SkBits2Float(0x10434343), SkBits2Float(0x00144332));
+    // 4.11823e-38f, 195.263f, 195.263f, 195.263f, -7.2741e-07f, 195.263f
+    path.cubicTo(SkBits2Float(0x016037c0), SkBits2Float(0x43434343),
+            SkBits2Float(0x43434343), SkBits2Float(0x43434343),
+            SkBits2Float(0xb5434343), SkBits2Float(0x43434343));
+    // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 195.263f, -2
+    path.cubicTo(SkBits2Float(0x43434344), SkBits2Float(0x43434341),
+            SkBits2Float(0xb74343bd), SkBits2Float(0x01434343),
+            SkBits2Float(0x43434343), SkBits2Float(0xc0000014));
+    // -5.87228e+06f, 3.7773e-07f, 3.60231e-13f, -6.64511e+06f,2.77692e-15f, 2.48803e-15f
+    path.cubicTo(SkBits2Float(0xcab33535), SkBits2Float(0x34cacaca),
+            SkBits2Float(0x2acacaca), SkBits2Float(0xcacacae3),
+            SkBits2Float(0x27481927), SkBits2Float(0x27334805));
+    path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x43434343));  //-7.2741e-07f, 195.263f
+    // 195.263f, 195.263f, -1.16387e-05f, 195.212f, 195.263f, -2
+    path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341),
+            SkBits2Float(0xb74343b9), SkBits2Float(0x43433643),
+            SkBits2Float(0x43434343), SkBits2Float(0xc0000014));
+    path.lineTo(SkBits2Float(0xc7004343), SkBits2Float(0x27480527));  //-32835.3f, 2.77584e-15f
+    path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0,0
+    path.close();
+    canvas->clipRect({0, 0, 65, 202});
+    canvas->drawPath(path, paint);
+}
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 31dffcb..1cb644a 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -16,6 +16,7 @@
 #include "GrClipStackClip.h"
 #include "GrReducedClip.h"
 #include "GrResourceCache.h"
+#include "GrSurfaceProxyPriv.h"
 #include "GrTextureProxy.h"
 typedef GrReducedClip::ElementList ElementList;
 typedef GrReducedClip::InitialState InitialState;
@@ -1356,6 +1357,36 @@
     }
 }
 
+static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
+    // https://bugs.chromium.org/p/skia/issues/detail?id=5990
+    const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
+
+    SkClipStack rectStack;
+    rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
+
+    SkPath clipPath;
+    clipPath.moveTo(clipBounds.left(), clipBounds.top());
+    clipPath.quadTo(clipBounds.right(), clipBounds.top(),
+                    clipBounds.right(), clipBounds.bottom());
+    clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
+                    clipBounds.left(), clipBounds.top());
+    SkClipStack pathStack;
+    pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
+
+    for (const SkClipStack& stack : {rectStack, pathStack}) {
+        for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
+                                   SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
+                                   SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
+                                   SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
+            const GrReducedClip reduced(stack, queryBounds);
+            REPORTER_ASSERT(reporter, !reduced.hasIBounds());
+            REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
+            REPORTER_ASSERT(reporter,
+                            GrReducedClip::InitialState::kAllOut == reduced.initialState());
+        }
+    }
+}
+
 #endif
 
 DEF_TEST(ClipStack, reporter) {
@@ -1408,6 +1439,7 @@
     test_reduced_clip_stack_genid(reporter);
     test_reduced_clip_stack_no_aa_crash(reporter);
     test_reduced_clip_stack_aa(reporter);
+    test_tiny_query_bounds_assertion_bug(reporter);
 #endif
 }
 
@@ -1442,7 +1474,8 @@
         stack.save();
         stack.clipPath(path, m, SkClipOp::kIntersect, true);
         sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
-        GrTexture* tex = mask->instantiate(context->resourceProvider());
+        mask->instantiate(context->resourceProvider());
+        GrTexture* tex = mask->priv().peekTexture();
         REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
         // Make sure mask isn't pinned in cache.
         mask.reset(nullptr);
diff --git a/tests/ClipperTest.cpp b/tests/ClipperTest.cpp
index 8ebd9b4..641ed01 100644
--- a/tests/ClipperTest.cpp
+++ b/tests/ClipperTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkEdgeClipper.h"
 #include "SkLineClipper.h"
diff --git a/tests/CodecAnimTest.cpp b/tests/CodecAnimTest.cpp
index 9f9a160..087f5d8 100644
--- a/tests/CodecAnimTest.cpp
+++ b/tests/CodecAnimTest.cpp
@@ -14,6 +14,7 @@
 
 #include "Resources.h"
 #include "Test.h"
+#include "sk_tool_utils.h"
 
 #include <initializer_list>
 #include <vector>
@@ -31,153 +32,278 @@
     }
 }
 
+DEF_TEST(Codec_trunc, r) {
+    sk_sp<SkData> data(GetResourceAsData("box.gif"));
+    if (!data) {
+        return;
+    }
+    data = SkData::MakeSubset(data.get(), 0, 23);
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    codec->getFrameInfo();
+}
+
+// 565 does not support alpha, but there is no reason for it not to support an
+// animated image with a frame that has alpha but then blends onto an opaque
+// frame making the result opaque. Test that we can decode such a frame.
+DEF_TEST(Codec_565, r) {
+    sk_sp<SkData> data(GetResourceAsData("blendBG.webp"));
+    if (!data) {
+        return;
+    }
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(data)));
+    auto info = codec->getInfo().makeColorType(kRGB_565_SkColorType);
+    SkBitmap bm;
+    bm.allocPixels(info);
+
+    SkCodec::Options options;
+    options.fFrameIndex = 1;
+    options.fHasPriorFrame = false;
+
+    const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(),
+                                         &options, nullptr, nullptr);
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+}
+
+
 DEF_TEST(Codec_frames, r) {
+    #define kOpaque     kOpaque_SkAlphaType
+    #define kUnpremul   kUnpremul_SkAlphaType
     static const struct {
-        const char*         fName;
-        size_t              fFrameCount;
+        const char*              fName;
+        int                      fFrameCount;
         // One less than fFramecount, since the first frame is always
         // independent.
-        std::vector<size_t> fRequiredFrames;
+        std::vector<int>         fRequiredFrames;
+        // Same, since the first frame should match getInfo.
+        std::vector<SkAlphaType> fAlphaTypes;
         // The size of this one should match fFrameCount for animated, empty
         // otherwise.
-        std::vector<size_t> fDurations;
-        int                 fRepetitionCount;
+        std::vector<int>         fDurations;
+        int                      fRepetitionCount;
     } gRecs[] = {
+        { "alphabetAnim.gif", 13,
+            { SkCodec::kNone, 0, 0, 0, 0, 5, 6, SkCodec::kNone,
+              SkCodec::kNone, SkCodec::kNone, 10, 11 },
+            { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
+              kUnpremul, kUnpremul, kUnpremul, kOpaque, kOpaque, kUnpremul },
+            { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 },
+            0 },
+        { "randPixelsAnim2.gif", 4,
+            // required frames
+            { 0, 0, 1 },
+            // alphas
+            { kOpaque, kOpaque, kOpaque },
+            // durations
+            { 0, 1000, 170, 40 },
+            // repetition count
+            0 },
         { "randPixelsAnim.gif", 13,
             // required frames
-            { SkCodec::kNone, 1, 2, 3, 4, 4, 6, 7, 7, 7, 7, 7 },
+            { SkCodec::kNone, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 },
+            { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
+              kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
             // durations
             { 0, 1000, 170, 40, 220, 7770, 90, 90, 90, 90, 90, 90, 90 },
             // repetition count
             0 },
-        { "box.gif", 1, {}, {}, 0 },
-        { "color_wheel.gif", 1, {}, {}, 0 },
-        { "test640x479.gif", 4, { 0, 1, 2 }, { 200, 200, 200, 200 },
+        { "box.gif", 1, {}, {}, {}, 0 },
+        { "color_wheel.gif", 1, {}, {}, {}, 0 },
+        { "test640x479.gif", 4, { 0, 1, 2 },
+                { kOpaque, kOpaque, kOpaque },
+                { 200, 200, 200, 200 },
                 SkCodec::kRepetitionCountInfinite },
-        { "colorTables.gif", 2, { 0 }, { 1000, 1000 }, 5 },
+        { "colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5 },
 
-        { "arrow.png",  1, {}, {}, 0 },
-        { "google_chrome.ico", 1, {}, {}, 0 },
-        { "brickwork-texture.jpg", 1, {}, {}, 0 },
+        { "arrow.png",  1, {}, {}, {}, 0 },
+        { "google_chrome.ico", 1, {}, {}, {}, 0 },
+        { "brickwork-texture.jpg", 1, {}, {}, {}, 0 },
 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
-        { "dng_with_preview.dng", 1, {}, {}, 0 },
+        { "dng_with_preview.dng", 1, {}, {}, {}, 0 },
 #endif
-        { "mandrill.wbmp", 1, {}, {}, 0 },
-        { "randPixels.bmp", 1, {}, {}, 0 },
-        { "yellow_rose.webp", 1, {}, {}, 0 },
+        { "mandrill.wbmp", 1, {}, {}, {}, 0 },
+        { "randPixels.bmp", 1, {}, {}, {}, 0 },
+        { "yellow_rose.webp", 1, {}, {}, {}, 0 },
+        { "webp-animated.webp", 3, { 0, 1 }, { kOpaque, kOpaque, kOpaque },
+            { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite },
+        { "blendBG.webp", 7, { 0, SkCodec::kNone, SkCodec::kNone, SkCodec::kNone,
+                               3, 3 },
+            { kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul },
+            { 525, 500, 525, 437, 609, 729, 444 }, 7 },
     };
+    #undef kOpaque
+    #undef kUnpremul
 
-    for (auto rec : gRecs) {
-        std::unique_ptr<SkStream> stream(GetResourceAsStream(rec.fName));
-        if (!stream) {
+    for (const auto& rec : gRecs) {
+        sk_sp<SkData> data(GetResourceAsData(rec.fName));
+        if (!data) {
             // Useful error statement, but sometimes people run tests without
             // resources, and they do not want to see these messages.
             //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
             continue;
         }
 
-        std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+        std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
         if (!codec) {
             ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
             continue;
         }
 
+        {
+            SkCodec::FrameInfo frameInfo;
+            REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
+        }
+
         const int repetitionCount = codec->getRepetitionCount();
         if (repetitionCount != rec.fRepetitionCount) {
             ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
                       rec.fName, rec.fRepetitionCount, repetitionCount);
         }
 
-        const size_t expected = rec.fFrameCount;
-        const auto frameInfos = codec->getFrameInfo();
-        // getFrameInfo returns empty set for non-animated.
-        const size_t frameCount = frameInfos.size() == 0 ? 1 : frameInfos.size();
-        if (frameCount != expected) {
-            ERRORF(r, "'%s' expected frame count: %i\tactual: %i", rec.fName, expected, frameCount);
-            continue;
-        }
-
-        if (rec.fRequiredFrames.size() + 1 != expected) {
+        const int expected = rec.fFrameCount;
+        if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) {
             ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i",
                    rec.fName, expected, rec.fRequiredFrames.size() + 1);
             continue;
         }
 
-        if (1 == frameCount) {
-            continue;
-        }
-
-        // From here on, we are only concerned with animated images.
-        REPORTER_ASSERT(r, frameInfos[0].fRequiredFrame == SkCodec::kNone);
-        for (size_t i = 1; i < frameCount; i++) {
-            if (rec.fRequiredFrames[i-1] != frameInfos[i].fRequiredFrame) {
-                ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
-                       rec.fName, i, rec.fRequiredFrames[i-1], frameInfos[i].fRequiredFrame);
-            }
-        }
-
-        // Compare decoding in two ways:
-        // 1. Provide the frame that a frame depends on, so the codec just has to blend.
-        //    (in the array cachedFrames)
-        // 2. Do not provide the frame that a frame depends on, so the codec has to decode all the
-        //    way back to a key-frame. (in a local variable uncachedFrame)
-        // The two should look the same.
-        std::vector<SkBitmap> cachedFrames(frameCount);
-        const auto& info = codec->getInfo().makeColorType(kN32_SkColorType);
-
-        auto decode = [&](SkBitmap* bm, bool cached, size_t index) {
-            bm->allocPixels(info);
-            if (cached) {
-                // First copy the pixels from the cached frame
-                const size_t requiredFrame = frameInfos[index].fRequiredFrame;
-                if (requiredFrame != SkCodec::kNone) {
-                    const bool success = cachedFrames[requiredFrame].copyTo(bm);
-                    REPORTER_ASSERT(r, success);
-                }
-            }
-            SkCodec::Options opts;
-            opts.fFrameIndex = index;
-            opts.fHasPriorFrame = cached;
-            const SkCodec::Result result = codec->getPixels(info, bm->getPixels(), bm->rowBytes(),
-                                                            &opts, nullptr, nullptr);
-            REPORTER_ASSERT(r, result == SkCodec::kSuccess);
-        };
-
-        for (size_t i = 0; i < frameCount; i++) {
-            SkBitmap& cachedFrame = cachedFrames[i];
-            decode(&cachedFrame, true, i);
-            SkBitmap uncachedFrame;
-            decode(&uncachedFrame, false, i);
-
-            // Now verify they're equal.
-            const size_t rowLen = info.bytesPerPixel() * info.width();
-            for (int y = 0; y < info.height(); y++) {
-                const void* cachedAddr = cachedFrame.getAddr(0, y);
-                SkASSERT(cachedAddr != nullptr);
-                const void* uncachedAddr = uncachedFrame.getAddr(0, y);
-                SkASSERT(uncachedAddr != nullptr);
-                const bool lineMatches = memcmp(cachedAddr, uncachedAddr, rowLen) == 0;
-                if (!lineMatches) {
-                    SkString name = SkStringPrintf("cached_%i", i);
-                    write_bm(name.c_str(), cachedFrame);
-                    name = SkStringPrintf("uncached_%i", i);
-                    write_bm(name.c_str(), uncachedFrame);
-                    ERRORF(r, "%s's frame %i is different depending on caching!", rec.fName, i);
-                    break;
-                }
-            }
-        }
-
-        if (rec.fDurations.size() != expected) {
+        if (rec.fDurations.size() != static_cast<size_t>(expected)) {
             ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i",
                    rec.fName, expected, rec.fDurations.size());
             continue;
         }
 
-        for (size_t i = 0; i < frameCount; i++) {
-            if (rec.fDurations[i] != frameInfos[i].fDuration) {
-                ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
-                       rec.fName, i, rec.fDurations[i], frameInfos[i].fDuration);
+        enum class TestMode {
+            kVector,
+            kIndividual,
+        };
+
+        for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
+            // Re-create the codec to reset state and test parsing.
+            codec.reset(SkCodec::NewFromData(data));
+
+            int frameCount;
+            std::vector<SkCodec::FrameInfo> frameInfos;
+            switch (mode) {
+                case TestMode::kVector:
+                    frameInfos = codec->getFrameInfo();
+                    // getFrameInfo returns empty set for non-animated.
+                    frameCount = frameInfos.empty() ? 1 : frameInfos.size();
+                    break;
+                case TestMode::kIndividual:
+                    frameCount = codec->getFrameCount();
+                    break;
+            }
+
+            if (frameCount != expected) {
+                ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
+                       rec.fName, expected, frameCount);
+                continue;
+            }
+
+            // From here on, we are only concerned with animated images.
+            if (1 == frameCount) {
+                continue;
+            }
+
+            for (int i = 0; i < frameCount; i++) {
+                SkCodec::FrameInfo frameInfo;
+                switch (mode) {
+                    case TestMode::kVector:
+                        frameInfo = frameInfos[i];
+                        break;
+                    case TestMode::kIndividual:
+                        REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
+                        REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
+                        break;
+                }
+
+                if (rec.fDurations[i] != frameInfo.fDuration) {
+                    ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
+                           rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
+                }
+
+                auto to_string = [](SkAlphaType type) {
+                    switch (type) {
+                        case kUnpremul_SkAlphaType:
+                            return "unpremul";
+                        case kOpaque_SkAlphaType:
+                            return "opaque";
+                        default:
+                            return "other";
+                    }
+                };
+
+                auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphaTypes[i-1];
+                auto alpha = frameInfo.fAlphaType;
+                if (expectedAlpha != alpha) {
+                    ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
+                           rec.fName, i, to_string(expectedAlpha), to_string(alpha));
+                }
+
+                if (0 == i) {
+                    REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNone);
+                } else if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) {
+                    ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
+                           rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame);
+                }
+            }
+
+            if (TestMode::kIndividual == mode) {
+                // No need to test decoding twice.
+                return;
+            }
+
+            // Compare decoding in two ways:
+            // 1. Provide the frame that a frame depends on, so the codec just has to blend.
+            //    (in the array cachedFrames)
+            // 2. Do not provide the frame that a frame depends on, so the codec has to decode
+            //    all the way back to a key-frame. (in a local variable uncachedFrame)
+            // The two should look the same.
+            std::vector<SkBitmap> cachedFrames(frameCount);
+            const auto& info = codec->getInfo().makeColorType(kN32_SkColorType);
+
+            auto decode = [&](SkBitmap* bm, bool cached, int index) {
+                bm->allocPixels(info);
+                if (cached) {
+                    // First copy the pixels from the cached frame
+                    const int requiredFrame = frameInfos[index].fRequiredFrame;
+                    if (requiredFrame != SkCodec::kNone) {
+                        const bool success = sk_tool_utils::copy_to(bm, kN32_SkColorType,
+                                cachedFrames[requiredFrame]);
+                        REPORTER_ASSERT(r, success);
+                    }
+                }
+                SkCodec::Options opts;
+                opts.fFrameIndex = index;
+                opts.fHasPriorFrame = cached;
+                auto result = codec->getPixels(info, bm->getPixels(), bm->rowBytes(),
+                                               &opts, nullptr, nullptr);
+                REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+            };
+
+            for (int i = 0; i < frameCount; i++) {
+                SkBitmap& cachedFrame = cachedFrames[i];
+                decode(&cachedFrame, true, i);
+                SkBitmap uncachedFrame;
+                decode(&uncachedFrame, false, i);
+
+                // Now verify they're equal.
+                const size_t rowLen = info.bytesPerPixel() * info.width();
+                for (int y = 0; y < info.height(); y++) {
+                    const void* cachedAddr = cachedFrame.getAddr(0, y);
+                    SkASSERT(cachedAddr != nullptr);
+                    const void* uncachedAddr = uncachedFrame.getAddr(0, y);
+                    SkASSERT(uncachedAddr != nullptr);
+                    const bool lineMatches = memcmp(cachedAddr, uncachedAddr, rowLen) == 0;
+                    if (!lineMatches) {
+                        SkString name = SkStringPrintf("cached_%i", i);
+                        write_bm(name.c_str(), cachedFrame);
+                        name = SkStringPrintf("uncached_%i", i);
+                        write_bm(name.c_str(), uncachedFrame);
+                        ERRORF(r, "%s's frame %i is different depending on caching!", rec.fName, i);
+                        break;
+                    }
+                }
             }
         }
     }
diff --git a/tests/CodecPartialTest.cpp b/tests/CodecPartialTest.cpp
index 1de1625..a232290 100644
--- a/tests/CodecPartialTest.cpp
+++ b/tests/CodecPartialTest.cpp
@@ -280,10 +280,6 @@
         frameInfo = partialCodec->getFrameInfo();
         REPORTER_ASSERT(r, frameInfo.size() == i + 1);
         REPORTER_ASSERT(r, frameInfo[i].fFullyReceived);
-
-        // allocPixels locked the pixels for frame, but frames[i] was copied
-        // from another bitmap, and did not retain the locked status.
-        SkAutoLockPixels alp(frames[i]);
         compare_bitmaps(r, frames[i], frame);
     }
 }
@@ -410,7 +406,6 @@
     const char* name = "baby_tux.png";
     sk_sp<SkData> file = GetResourceAsData(name);
     if (!file) {
-        SkDebugf("REMOVE\n");
         return;
     }
 
diff --git a/tests/CodecPriv.h b/tests/CodecPriv.h
index 50c205c..cfa794e 100644
--- a/tests/CodecPriv.h
+++ b/tests/CodecPriv.h
@@ -29,7 +29,7 @@
         colorCountPtr = &maxColors;
     }
 
-    bm->allocPixels(codec->getInfo(), nullptr, colorTable.get());
+    bm->allocPixels(codec->getInfo(), colorTable);
     const SkCodec::Result result = codec->getPixels(codec->getInfo(), bm->getPixels(),
             bm->rowBytes(), nullptr, colorPtr, colorCountPtr);
     return result == SkCodec::kSuccess || result == SkCodec::kIncompleteInput;
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index 4a9c4a6..ffaeb2b 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -20,10 +20,13 @@
 #include "SkImageEncoderPriv.h"
 #include "SkMD5.h"
 #include "SkOSPath.h"
+#include "SkJpegEncoder.h"
 #include "SkPngChunkReader.h"
+#include "SkPngEncoder.h"
 #include "SkRandom.h"
 #include "SkStream.h"
 #include "SkStreamPriv.h"
+#include "SkWebpEncoder.h"
 #include "Test.h"
 
 #include "png.h"
@@ -38,7 +41,6 @@
 #endif
 
 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
-    SkAutoLockPixels autoLockPixels(bm);
     SkASSERT(bm.getPixels());
     SkMD5 md5;
     size_t rowLen = bm.info().bytesPerPixel() * bm.width();
@@ -72,7 +74,6 @@
                       SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) {
     SkBitmap bm;
     bm.allocPixels(info);
-    SkAutoLockPixels autoLockPixels(bm);
 
     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
     REPORTER_ASSERT(r, result == expectedResult);
@@ -98,7 +99,6 @@
         const SkMD5::Digest& goodDigest) {
     SkBitmap bm;
     bm.allocPixels(info);
-    SkAutoLockPixels autoLockPixels(bm);
 
     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(),
                                                                           bm.rowBytes()));
@@ -157,7 +157,6 @@
 
     REPORTER_ASSERT(r, info.dimensions() == size);
     bm.allocPixels(info);
-    SkAutoLockPixels autoLockPixels(bm);
 
     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
     REPORTER_ASSERT(r, result == expectedResult);
@@ -174,7 +173,6 @@
             // Decoding to 565 should succeed.
             SkBitmap bm565;
             bm565.allocPixels(info565);
-            SkAutoLockPixels alp(bm565);
 
             // This will allow comparison even if the image is incomplete.
             bm565.eraseColor(SK_ColorBLACK);
@@ -199,7 +197,6 @@
         SkImageInfo grayInfo = codec->getInfo();
         SkBitmap grayBm;
         grayBm.allocPixels(grayInfo);
-        SkAutoLockPixels alp(grayBm);
 
         grayBm.eraseColor(SK_ColorBLACK);
 
@@ -442,7 +439,6 @@
                 SkCodecImageGenerator::MakeFromEncodedCodec(fullData));
         SkBitmap bm;
         bm.allocPixels(info);
-        SkAutoLockPixels autoLockPixels(bm);
         REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes()));
         compare_to_good_digest(r, codecDigest, bm);
 
@@ -700,8 +696,7 @@
     } else if (SkCodec::kUnimplemented == result) {
         // New method should be supported:
         SkBitmap bm;
-        sk_sp<SkColorTable> colorTable(new SkColorTable(colorStorage, 256));
-        bm.allocPixels(info, nullptr, colorTable.get());
+        bm.allocPixels(info, SkColorTable::Make(colorStorage, 256));
         result = decoder->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), nullptr,
                                                  colorStorage, &colorCount);
         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
@@ -858,7 +853,7 @@
 
     if (decodedBm.colorType() != bm.colorType()) {
         SkBitmap tmp;
-        bool success = decodedBm.copyTo(&tmp, bm.colorType());
+        bool success = sk_tool_utils::copy_to(&tmp, bm.colorType(), decodedBm);
         REPORTER_ASSERT(r, success);
         if (!success) {
             return;
@@ -1108,8 +1103,8 @@
 static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) {
     SkBitmap bm1;
     SkPMColor colors[256];
-    sk_sp<SkColorTable> colorTable1(new SkColorTable(colors, 256));
-    bm1.allocPixels(info, nullptr, colorTable1.get());
+    sk_sp<SkColorTable> colorTable1 = SkColorTable::Make(colors, 256);
+    bm1.allocPixels(info, colorTable1);
     int numColors;
     SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes(), nullptr,
                                                   const_cast<SkPMColor*>(colorTable1->readColors()),
@@ -1126,8 +1121,8 @@
     REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType()));
 
     SkBitmap bm2;
-    sk_sp<SkColorTable> colorTable2(new SkColorTable(colors, 256));
-    bm2.allocPixels(info, nullptr, colorTable2.get());
+    sk_sp<SkColorTable> colorTable2 = SkColorTable::Make(colors, 256);
+    bm2.allocPixels(info, colorTable2);
     result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes(), nullptr,
                               const_cast<SkPMColor*>(colorTable2->readColors()), &numColors);
     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
@@ -1455,37 +1450,9 @@
     REPORTER_ASSERT(r, rowsDecoded == 0);
 }
 
-static void test_invalid_images(skiatest::Reporter* r, const char* path, bool shouldSucceed) {
-    SkBitmap bitmap;
-    const bool success = GetResourceAsBitmap(path, &bitmap);
-    REPORTER_ASSERT(r, success == shouldSucceed);
-}
-
-DEF_TEST(Codec_InvalidImages, r) {
-    // ASAN will complain if there is an issue.
-    test_invalid_images(r, "invalid_images/int_overflow.ico", false);
-    test_invalid_images(r, "invalid_images/skbug5887.gif", true);
-    test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", false);
-}
-
-DEF_TEST(Codec_InvalidBmp, r) {
-    // These files report values that have caused problems with SkFILEStreams.
-    // They are invalid, and should not create SkCodecs.
-    for (auto* bmp : { "b33651913.bmp", "b34778578.bmp" } ) {
-        SkString path = SkOSPath::Join("invalid_images", bmp);
-        path = GetResourcePath(path.c_str());
-        std::unique_ptr<SkFILEStream> stream(new SkFILEStream(path.c_str()));
-        if (!stream->isValid()) {
-            return;
-        }
-
-        std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
-        REPORTER_ASSERT(r, !codec);
-    }
-}
-
-DEF_TEST(Codec_InvalidRLEBmp, r) {
-    auto* stream = GetResourceAsStream("invalid_images/b33251605.bmp");
+static void test_invalid_images(skiatest::Reporter* r, const char* path,
+                                SkCodec::Result expectedResult) {
+    auto* stream = GetResourceAsStream(path);
     if (!stream) {
         return;
     }
@@ -1493,7 +1460,36 @@
     std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
     REPORTER_ASSERT(r, codec);
 
-    test_info(r, codec.get(), codec->getInfo(), SkCodec::kIncompleteInput, nullptr);
+    test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult,
+              nullptr);
+}
+
+DEF_TEST(Codec_InvalidImages, r) {
+    // ASAN will complain if there is an issue.
+    test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kIncompleteInput);
+    test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput);
+    test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput);
+    test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput);
+}
+
+static void test_invalid_header(skiatest::Reporter* r, const char* path) {
+    SkString resourcePath = GetResourcePath(path);
+    std::unique_ptr<SkFILEStream> stream(new SkFILEStream(resourcePath.c_str()));
+    if (!stream->isValid()) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, !codec);
+}
+
+DEF_TEST(Codec_InvalidHeader, r) {
+    test_invalid_header(r, "invalid_images/int_overflow.ico");
+
+    // These files report values that have caused problems with SkFILEStreams.
+    // They are invalid, and should not create SkCodecs.
+    test_invalid_header(r, "invalid_images/b33651913.bmp");
+    test_invalid_header(r, "invalid_images/b34778578.bmp");
 }
 
 DEF_TEST(Codec_InvalidAnimated, r) {
@@ -1516,7 +1512,7 @@
 
     auto frameInfos = codec->getFrameInfo();
     SkCodec::Options opts;
-    for (size_t i = 0; i < frameInfos.size(); i++) {
+    for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
         opts.fFrameIndex = i;
         opts.fHasPriorFrame = frameInfos[i].fRequiredFrame == i - 1;
         auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts);
@@ -1531,16 +1527,21 @@
 }
 
 static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap,
-                          const SkEncodeOptions& opts, SkEncodedImageFormat format) {
+                          SkTransferFunctionBehavior unpremulBehavior,
+                          SkEncodedImageFormat format) {
+    SkPngEncoder::Options pngOptions;
+    SkWebpEncoder::Options webpOptions;
+    pngOptions.fUnpremulBehavior = unpremulBehavior;
+    webpOptions.fUnpremulBehavior = unpremulBehavior;
     switch (format) {
         case SkEncodedImageFormat::kPNG:
-            SkEncodeImageAsPNG(stream, pixmap, opts);
+            SkPngEncoder::Encode(stream, pixmap, pngOptions);
             break;
         case SkEncodedImageFormat::kJPEG:
-            SkEncodeImageAsJPEG(stream, pixmap, opts);
+            SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options());
             break;
         case SkEncodedImageFormat::kWEBP:
-            SkEncodeImageAsWEBP(stream, pixmap, opts);
+            SkWebpEncoder::Encode(stream, pixmap, webpOptions);
             break;
         default:
             SkASSERT(false);
@@ -1558,9 +1559,7 @@
     SkPixmap pixmap;
     srgbBitmap.peekPixels(&pixmap);
     SkDynamicMemoryWStream srgbBuf;
-    SkEncodeOptions opts;
-    opts.fUnpremulBehavior = unpremulBehavior;
-    encode_format(&srgbBuf, pixmap, opts, format);
+    encode_format(&srgbBuf, pixmap, unpremulBehavior, format);
     sk_sp<SkData> srgbData = srgbBuf.detachAsData();
     std::unique_ptr<SkCodec> srgbCodec(SkCodec::NewFromData(srgbData));
     REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == SkColorSpace::MakeSRGB().get());
@@ -1570,7 +1569,7 @@
     sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
                                                    SkColorSpace::kDCIP3_D65_Gamut);
     pixmap.setColorSpace(p3);
-    encode_format(&p3Buf, pixmap, opts, format);
+    encode_format(&p3Buf, pixmap, unpremulBehavior, format);
     sk_sp<SkData> p3Data = p3Buf.detachAsData();
     std::unique_ptr<SkCodec> p3Codec(SkCodec::NewFromData(p3Data));
     REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB());
diff --git a/tests/ColorSpaceTest.cpp b/tests/ColorSpaceTest.cpp
index 2eb7347..9b2e1b3 100644
--- a/tests/ColorSpaceTest.cpp
+++ b/tests/ColorSpaceTest.cpp
@@ -446,10 +446,21 @@
 
 DEF_TEST(ColorSpace_InvalidICC, r) {
     // This color space has a matrix that is not D50.
-    sk_sp<SkData> data = SkData::MakeFromFileName(
-            GetResourcePath("icc_profiles/SM2333SW.icc").c_str());
+    sk_sp<SkData> data = GetResourceAsData("icc_profiles/SM2333SW.icc");
+    if (!data) {
+        return;
+    }
     sk_sp<SkColorSpace> cs = SkColorSpace::MakeICC(data->data(), data->size());
     REPORTER_ASSERT(r, !cs);
+
+    // The color space has a color lut with only one entry in each dimension.
+    data = GetResourceAsData("icc_profiles/invalid_color_lut.icc");
+    if (!data) {
+        return;
+    }
+
+    cs = SkColorSpace::MakeICC(data->data(), data->size());
+    REPORTER_ASSERT(r, !cs);
 }
 
 DEF_TEST(ColorSpace_MatrixHash, r) {
diff --git a/tests/ColorSpaceXformTest.cpp b/tests/ColorSpaceXformTest.cpp
index f8a5c8f..83317d9 100644
--- a/tests/ColorSpaceXformTest.cpp
+++ b/tests/ColorSpaceXformTest.cpp
@@ -67,8 +67,8 @@
     }
 };
 
-static bool almost_equal(int x, int y) {
-    return SkTAbs(x - y) <= 1;
+static bool almost_equal(int x, int y, int tol=1) {
+    return SkTAbs(x-y) <= tol;
 }
 
 static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& gammas,
@@ -108,7 +108,7 @@
 }
 
 static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNamed,
-                                    const sk_sp<SkGammas>& gammas) {
+                                    const sk_sp<SkGammas>& gammas, int tol=1) {
     // Arbitrary set of 10 pixels
     constexpr int width = 10;
     constexpr uint32_t srcPixels[width] = {
@@ -124,16 +124,16 @@
     REPORTER_ASSERT(r, result);
 
     // Since the src->dst matrix is the identity, and the gamma curves match,
-    // the pixels should be unchanged.
+    // the pixels should be ~unchanged.
     for (int i = 0; i < width; i++) {
         REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >>  0) & 0xFF),
-                                        SkGetPackedB32(dstPixels[i])));
+                                        SkGetPackedB32(dstPixels[i]), tol));
         REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >>  8) & 0xFF),
-                                        SkGetPackedG32(dstPixels[i])));
+                                        SkGetPackedG32(dstPixels[i]), tol));
         REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF),
-                                        SkGetPackedR32(dstPixels[i])));
+                                        SkGetPackedR32(dstPixels[i]), tol));
         REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF),
-                                        SkGetPackedA32(dstPixels[i])));
+                                        SkGetPackedA32(dstPixels[i]), tol));
     }
 }
 
@@ -149,7 +149,6 @@
     }
 
     float* table = SkTAddOffset<float>(memory, sizeof(SkGammas));
-
     table[0] = 0.00f;
     table[1] = 0.05f;
     table[2] = 0.10f;
@@ -160,8 +159,13 @@
     table[7] = 0.60f;
     table[8] = 0.75f;
     table[9] = 1.00f;
+    // This table's pretty small compared to real ones in the wild (think 256),
+    // so we give test_identity_xform_A2B a wide tolerance.
+    // This lets us implement table transfer functions with a single lookup.
+    const int tolerance = 13;
+
     test_identity_xform(r, gammas, true);
-    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance);
 }
 
 DEF_TEST(ColorSpaceXform_ParametricGamma, r) {
@@ -248,6 +252,9 @@
     gammas->fType[0] = SkGammas::Type::kValue_Type;
     gammas->fData[0].fValue = 1.2f;
 
+    // See ColorSpaceXform_TableGamma... we've decided to allow some tolerance
+    // for SkJumper's implementation of tables.
+    const int tolerance = 12;
     gammas->fType[1] = SkGammas::Type::kTable_Type;
     gammas->fData[1].fTable.fSize = tableSize;
     gammas->fData[1].fTable.fOffset = 0;
@@ -256,7 +263,7 @@
     gammas->fData[2].fParamOffset = sizeof(float) * tableSize;
 
     test_identity_xform(r, gammas, true);
-    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance);
 }
 
 DEF_TEST(ColorSpaceXform_A2BCLUT, r) {
diff --git a/tests/CrossContextImageTest.cpp b/tests/CrossContextImageTest.cpp
deleted file mode 100644
index 5c8b21e..0000000
--- a/tests/CrossContextImageTest.cpp
+++ /dev/null
@@ -1,240 +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 "SkTypes.h"
-
-#if SK_SUPPORT_GPU
-
-#include "GrContextFactory.h"
-#include "Resources.h"
-#include "SkAutoPixmapStorage.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkCrossContextImageData.h"
-#include "SkSemaphore.h"
-#include "SkSurface.h"
-#include "SkThreadUtils.h"
-#include "Test.h"
-
-using namespace sk_gpu_test;
-
-static SkImageInfo read_pixels_info(SkImage* image) {
-    return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
-}
-
-static bool colors_are_close(SkColor a, SkColor b, int error) {
-    return SkTAbs((int)SkColorGetR(a) - (int)SkColorGetR(b)) <= error &&
-           SkTAbs((int)SkColorGetG(a) - (int)SkColorGetG(b)) <= error &&
-           SkTAbs((int)SkColorGetB(a) - (int)SkColorGetB(b)) <= error;
-}
-
-static void assert_equal(skiatest::Reporter* reporter, SkImage* a, SkImage* b, int error) {
-    REPORTER_ASSERT(reporter, a->width() == b->width());
-    REPORTER_ASSERT(reporter, a->height() == b->height());
-
-    SkAutoPixmapStorage pmapA, pmapB;
-    pmapA.alloc(read_pixels_info(a));
-    pmapB.alloc(read_pixels_info(b));
-
-    REPORTER_ASSERT(reporter, a->readPixels(pmapA, 0, 0));
-    REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0));
-
-    for (int y = 0; y < a->height(); ++y) {
-        for (int x = 0; x < a->width(); ++x) {
-            SkColor ca = pmapA.getColor(x, y);
-            SkColor cb = pmapB.getColor(x, y);
-            if (!error) {
-                if (ca != cb) {
-                    ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d)", ca, cb, x, y);
-                    return;
-                }
-            } else {
-                if (!colors_are_close(ca, cb, error)) {
-                    ERRORF(reporter, "Expected 0x%08x +-%d but got 0x%08x at (%d, %d)",
-                           ca, error, cb, x, y);
-                    return;
-                }
-            }
-        }
-    }
-}
-
-static void draw_image_test_pattern(SkCanvas* canvas) {
-    canvas->clear(SK_ColorWHITE);
-    SkPaint paint;
-    paint.setColor(SK_ColorBLACK);
-    canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint);
-}
-
-static sk_sp<SkImage> create_test_image() {
-    SkBitmap bm;
-    bm.allocN32Pixels(20, 20, true);
-    SkCanvas canvas(bm);
-    draw_image_test_pattern(&canvas);
-
-    return SkImage::MakeFromBitmap(bm);
-}
-
-static sk_sp<SkData> create_test_data(SkEncodedImageFormat format) {
-    auto image = create_test_image();
-    return sk_sp<SkData>(image->encode(format, 100));
-}
-
-DEF_GPUTEST(CrossContextImage_SameContext, reporter, /*factory*/) {
-    GrContextFactory factory;
-    sk_sp<SkImage> testImage = create_test_image();
-
-    // Test both PNG and JPG, to exercise GPU YUV conversion
-    for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) {
-        sk_sp<SkData> encoded = create_test_data(format);
-
-        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
-            GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
-            if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) {
-                continue;
-            }
-
-            ContextInfo info = factory.getContextInfo(ctxType);
-            if (!info.grContext()) {
-                continue;
-            }
-
-            auto ccid = SkCrossContextImageData::MakeFromEncoded(info.grContext(), encoded,
-                                                                 nullptr);
-            REPORTER_ASSERT(reporter, ccid != nullptr);
-
-            auto image = SkImage::MakeFromCrossContextImageData(info.grContext(), std::move(ccid));
-            REPORTER_ASSERT(reporter, image != nullptr);
-
-            // JPEG encode -> decode won't round trip the image perfectly
-            assert_equal(reporter, testImage.get(), image.get(),
-                         SkEncodedImageFormat::kJPEG == format ? 2 : 0);
-        }
-    }
-}
-
-DEF_GPUTEST(CrossContextImage_SharedContextSameThread, reporter, /*factory*/) {
-    GrContextFactory factory;
-    sk_sp<SkImage> testImage = create_test_image();
-
-    // Test both PNG and JPG, to exercise GPU YUV conversion
-    for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) {
-        sk_sp<SkData> encoded = create_test_data(format);
-
-        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
-            GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
-            if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) {
-                continue;
-            }
-
-            ContextInfo info = factory.getContextInfo(ctxType);
-            if (!info.grContext()) {
-                continue;
-            }
-            auto ccid = SkCrossContextImageData::MakeFromEncoded(info.grContext(), encoded,
-                                                                 nullptr);
-            REPORTER_ASSERT(reporter, ccid != nullptr);
-
-            ContextInfo info2 = factory.getSharedContextInfo(info.grContext());
-            GrContext* ctx2 = info2.grContext();
-            int resourceCountBefore = 0, resourceCountAfter = 0;
-            size_t resourceBytesBefore = 0, resourceBytesAfter = 0;
-            if (ctx2 && info.grContext()->caps()->crossContextTextureSupport()) {
-                ctx2->getResourceCacheUsage(&resourceCountBefore, &resourceBytesBefore);
-            }
-
-            auto image = SkImage::MakeFromCrossContextImageData(ctx2, std::move(ccid));
-            REPORTER_ASSERT(reporter, image != nullptr);
-
-            if (ctx2 && info.grContext()->caps()->crossContextTextureSupport()) {
-                // MakeFromCrossContextImageData should have imported the texture back into our
-                // cache, so we should see an uptick. (If we have crossContextTextureSupport,
-                // otherwise we're just handing around a CPU or codec-backed image, so no cache
-                // impact will occur).
-                ctx2->getResourceCacheUsage(&resourceCountAfter, &resourceBytesAfter);
-                REPORTER_ASSERT(reporter, resourceCountAfter == resourceCountBefore + 1);
-                REPORTER_ASSERT(reporter, resourceBytesAfter > resourceBytesBefore);
-            }
-
-            // JPEG encode -> decode won't round trip the image perfectly
-            assert_equal(reporter, testImage.get(), image.get(),
-                         SkEncodedImageFormat::kJPEG == format ? 2 : 0);
-        }
-    }
-}
-
-namespace {
-struct CrossContextImage_ThreadContext {
-    GrContext* fGrContext;
-    sk_gpu_test::TestContext* fTestContext;
-    SkSemaphore fSemaphore;
-    std::unique_ptr<SkCrossContextImageData> fCCID;
-    sk_sp<SkData> fEncoded;
-};
-}
-
-static void upload_image_thread_proc(void* data) {
-    CrossContextImage_ThreadContext* ctx = static_cast<CrossContextImage_ThreadContext*>(data);
-    ctx->fTestContext->makeCurrent();
-    ctx->fCCID = SkCrossContextImageData::MakeFromEncoded(ctx->fGrContext, ctx->fEncoded, nullptr);
-    ctx->fSemaphore.signal();
-}
-
-DEF_GPUTEST(CrossContextImage_SharedContextOtherThread, reporter, /*factory*/) {
-    sk_sp<SkImage> testImage = create_test_image();
-
-    // Test both PNG and JPG, to exercise GPU YUV conversion
-    for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) {
-        // Use a new factory for each batch of tests. Otherwise the shared context will still be
-        // current on the upload thread when we do the second iteration, and we get undefined
-        // behavior.
-        GrContextFactory factory;
-        sk_sp<SkData> encoded = create_test_data(format);
-
-        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
-            GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
-            if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) {
-                continue;
-            }
-
-            // Create two GrContexts in a share group
-            ContextInfo info = factory.getContextInfo(ctxType);
-            if (!info.grContext()) {
-                continue;
-            }
-            ContextInfo info2 = factory.getSharedContextInfo(info.grContext());
-            if (!info2.grContext()) {
-                continue;
-            }
-
-            // Make the first one current (on this thread) again
-            info.testContext()->makeCurrent();
-
-            // Bundle up data for the worker thread
-            CrossContextImage_ThreadContext ctx;
-            ctx.fGrContext = info2.grContext();
-            ctx.fTestContext = info2.testContext();
-            ctx.fEncoded = encoded;
-
-            SkThread uploadThread(upload_image_thread_proc, &ctx);
-            SkAssertResult(uploadThread.start());
-
-            ctx.fSemaphore.wait();
-            auto image = SkImage::MakeFromCrossContextImageData(info.grContext(),
-                                                                std::move(ctx.fCCID));
-            REPORTER_ASSERT(reporter, image != nullptr);
-
-            // JPEG encode -> decode won't round trip the image perfectly
-            assert_equal(reporter, testImage.get(), image.get(),
-                         SkEncodedImageFormat::kJPEG == format ? 2 : 0);
-
-            uploadThread.join();
-        }
-    }
-}
-
-#endif
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
index d0cafc3..74d511c 100644
--- a/tests/DataRefTest.cpp
+++ b/tests/DataRefTest.cpp
@@ -12,6 +12,7 @@
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkStream.h"
+#include "SkTArray.h"
 #include "Test.h"
 
 static void test_is_equal(skiatest::Reporter* reporter,
@@ -255,16 +256,16 @@
 DEF_TEST(RWBuffer, reporter) {
     // Knowing that the default capacity is 4096, choose N large enough so we force it to use
     // multiple buffers internally.
-    const int N = 1000;
-    SkROBuffer* readers[N];
-    SkStream* streams[N];
+    static constexpr int N = 1000;
+    SkSTArray<N, sk_sp<SkROBuffer>> readers;
+    SkSTArray<N, std::unique_ptr<SkStream>> streams;
 
     {
         SkRWBuffer buffer;
         for (int i = 0; i < N; ++i) {
             buffer.append(gABC, 26);
-            readers[i] = buffer.newRBufferSnapshot();
-            streams[i] = buffer.newStreamSnapshot();
+            readers.push_back(buffer.makeROBufferSnapshot());
+            streams.push_back(buffer.makeStreamSnapshot());
         }
         REPORTER_ASSERT(reporter, N*26 == buffer.size());
     }
@@ -272,10 +273,8 @@
     // Verify that although the SkRWBuffer's destructor has run, the readers are still valid.
     for (int i = 0; i < N; ++i) {
         REPORTER_ASSERT(reporter, (i + 1) * 26U == readers[i]->size());
-        check_alphabet_buffer(reporter, readers[i]);
-        check_alphabet_stream(reporter, streams[i]);
-        readers[i]->unref();
-        delete streams[i];
+        check_alphabet_buffer(reporter, readers[i].get());
+        check_alphabet_stream(reporter, streams[i].get());
     }
 }
 
@@ -287,8 +286,8 @@
     SkRWBuffer buffer;
     for (int i = 0; i < N; ++i) {
         buffer.append(gABC, 26);
-        sk_sp<SkROBuffer> reader = sk_sp<SkROBuffer>(buffer.newRBufferSnapshot());
-        SkStream* stream = buffer.newStreamSnapshot();
+        sk_sp<SkROBuffer> reader = buffer.makeROBufferSnapshot();
+        SkStream* stream = buffer.makeStreamSnapshot().release();
         REPORTER_ASSERT(reporter, reader->size() == buffer.size());
         REPORTER_ASSERT(reporter, stream->getLength() == buffer.size());
 
@@ -313,7 +312,7 @@
     SkRWBuffer buffer;
     buffer.append(gABC, 26);
 
-    sk_sp<SkROBuffer> roBuffer(buffer.newRBufferSnapshot());
+    sk_sp<SkROBuffer> roBuffer(buffer.makeROBufferSnapshot());
     SkROBuffer::Iter iter(roBuffer.get());
     REPORTER_ASSERT(r, iter.data());
     REPORTER_ASSERT(r, iter.size() == 26);
@@ -329,7 +328,7 @@
     SkRWBuffer buffer;
     REPORTER_ASSERT(r, 0 == buffer.size());
 
-    sk_sp<SkROBuffer> roBuffer = sk_sp<SkROBuffer>(buffer.newRBufferSnapshot());
+    sk_sp<SkROBuffer> roBuffer = buffer.makeROBufferSnapshot();
     REPORTER_ASSERT(r, roBuffer);
     if (roBuffer) {
         REPORTER_ASSERT(r, roBuffer->size() == 0);
@@ -339,7 +338,7 @@
         REPORTER_ASSERT(r, !iter.next());
     }
 
-    std::unique_ptr<SkStream> stream(buffer.newStreamSnapshot());
+    std::unique_ptr<SkStream> stream(buffer.makeStreamSnapshot());
     REPORTER_ASSERT(r, stream);
     if (stream) {
         REPORTER_ASSERT(r, stream->hasLength());
diff --git a/tests/DetermineDomainModeTest.cpp b/tests/DetermineDomainModeTest.cpp
index add746f..1ce38d3 100644
--- a/tests/DetermineDomainModeTest.cpp
+++ b/tests/DetermineDomainModeTest.cpp
@@ -341,6 +341,8 @@
                          insetAmount, halfFilterWidth, 0, name);
 }
 
+#ifndef SK_DISABLE_DEFERRED_PROXIES
+
 static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) {
     GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
     SkRect actualDomainRect;
@@ -444,3 +446,5 @@
 }
 
 #endif
+
+#endif
diff --git a/tests/DiscardableMemoryPoolTest.cpp b/tests/DiscardableMemoryPoolTest.cpp
index 79b257d..c91e827 100644
--- a/tests/DiscardableMemoryPoolTest.cpp
+++ b/tests/DiscardableMemoryPoolTest.cpp
@@ -9,8 +9,7 @@
 #include "Test.h"
 
 DEF_TEST(DiscardableMemoryPool, reporter) {
-    sk_sp<SkDiscardableMemoryPool> pool(
-        SkDiscardableMemoryPool::Create(1, nullptr));
+    sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1));
     pool->setRAMBudget(3);
     REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
 
diff --git a/tests/DiscardableMemoryTest.cpp b/tests/DiscardableMemoryTest.cpp
index 5d2d6b9..818fbea 100644
--- a/tests/DiscardableMemoryTest.cpp
+++ b/tests/DiscardableMemoryTest.cpp
@@ -52,8 +52,8 @@
 }
 
 DEF_TEST(DiscardableMemory_nonglobal, reporter) {
-    std::unique_ptr<SkDiscardableMemoryPool> pool(
-        SkDiscardableMemoryPool::Create(1024, /* mutex = */ nullptr));
+    sk_sp<SkDiscardableMemoryPool> pool(
+        SkDiscardableMemoryPool::Make(1024));
     std::unique_ptr<SkDiscardableMemory> dm(pool->create(kTestStringLength));
     test_dm(reporter, dm.get(), true);
 }
diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp
index 62da1c2..9912901 100644
--- a/tests/DrawBitmapRectTest.cpp
+++ b/tests/DrawBitmapRectTest.cpp
@@ -19,46 +19,6 @@
 #include "SkSurface.h"
 #include "Test.h"
 
-class FailurePixelRef : public SkPixelRef {
-public:
-    FailurePixelRef(const SkImageInfo& info) : SkPixelRef(info) {}
-protected:
-    bool onNewLockPixels(LockRec*) override { return false; }
-    void onUnlockPixels() override {}
-};
-
-// crbug.com/295895
-// Crashing in skia when a pixelref fails in lockPixels
-//
-static void test_faulty_pixelref(skiatest::Reporter* reporter) {
-    // need a cache, but don't expect to use it, so the budget is not critical
-    sk_sp<SkDiscardableMemoryPool> pool(
-        SkDiscardableMemoryPool::Create(10 * 1000, nullptr));
-
-    SkBitmap bm;
-    const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
-    bm.setInfo(info);
-    bm.setPixelRef(sk_make_sp<FailurePixelRef>(info), 0, 0);
-    // now our bitmap has a pixelref, but we know it will fail to lock
-
-    auto surface(SkSurface::MakeRasterN32Premul(200, 200));
-    SkCanvas* canvas = surface->getCanvas();
-
-    const SkFilterQuality levels[] = {
-        kNone_SkFilterQuality,
-        kLow_SkFilterQuality,
-        kMedium_SkFilterQuality,
-        kHigh_SkFilterQuality,
-    };
-
-    SkPaint paint;
-    canvas->scale(2, 2);    // need a scale, otherwise we may ignore filtering
-    for (size_t i = 0; i < SK_ARRAY_COUNT(levels); ++i) {
-        paint.setFilterQuality(levels[i]);
-        canvas->drawBitmap(bm, 0, 0, &paint);
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) {
@@ -266,8 +226,6 @@
 }
 
 static bool check_for_all_zeros(const SkBitmap& bm) {
-    SkAutoLockPixels alp(bm);
-
     size_t count = bm.width() * bm.bytesPerPixel();
     for (int y = 0; y < bm.height(); y++) {
         const uint8_t* ptr = reinterpret_cast<const uint8_t*>(bm.getAddr(0, y));
@@ -308,5 +266,4 @@
     test_giantrepeat_crbug118018(reporter);
 
     test_treatAsSprite(reporter);
-    test_faulty_pixelref(reporter);
 }
diff --git a/tests/DrawPathTest.cpp b/tests/DrawPathTest.cpp
index 87d51b1..2434bdf 100644
--- a/tests/DrawPathTest.cpp
+++ b/tests/DrawPathTest.cpp
@@ -26,7 +26,7 @@
     int y = SkScalarRoundToInt(r.top());
 
     // check that the pixel in question starts as transparent (by the surface)
-    if (canvas->readPixels(&output, x, y)) {
+    if (canvas->readPixels(output, x, y)) {
         REPORTER_ASSERT(reporter, 0 == pixel[0]);
     } else {
         REPORTER_ASSERT_MESSAGE(reporter, false, "readPixels failed");
@@ -39,7 +39,7 @@
     canvas->drawRect(r, paint);
 
     // Now check that it is BLACK
-    if (canvas->readPixels(&output, x, y)) {
+    if (canvas->readPixels(output, x, y)) {
         // don't know what swizzling PMColor did, but white should always
         // appear the same.
         REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]);
diff --git a/tests/DrawTextTest.cpp b/tests/DrawTextTest.cpp
index bf9bd39..2f8fe05 100644
--- a/tests/DrawTextTest.cpp
+++ b/tests/DrawTextTest.cpp
@@ -37,9 +37,6 @@
     const int xOff = itest.fLeft - iref.fLeft;
     const int yOff = itest.fTop - iref.fTop;
 
-    SkAutoLockPixels alpRef(ref);
-    SkAutoLockPixels alpTest(test);
-
     for (int y = 0; y < test.height(); ++y) {
         for (int x = 0; x < test.width(); ++x) {
             SkColor testColor = test.getColor(x, y);
@@ -123,11 +120,11 @@
     SkScalar oddballs[] = { 0.0f, (float)INFINITY, (float)NAN, 34359738368.0f };
 
     for (auto x : oddballs) {
-        canvas->drawText("a", 1, +x, 0.0f, SkPaint());
-        canvas->drawText("a", 1, -x, 0.0f, SkPaint());
+        canvas->drawString("a", +x, 0.0f, SkPaint());
+        canvas->drawString("a", -x, 0.0f, SkPaint());
     }
     for (auto y : oddballs) {
-        canvas->drawText("a", 1, 0.0f, +y, SkPaint());
-        canvas->drawText("a", 1, 0.0f, -y, SkPaint());
+        canvas->drawString("a", 0.0f, +y, SkPaint());
+        canvas->drawString("a", 0.0f, -y, SkPaint());
     }
 }
diff --git a/tests/EGLImageTest.cpp b/tests/EGLImageTest.cpp
index 998e339..ae66db5 100644
--- a/tests/EGLImageTest.cpp
+++ b/tests/EGLImageTest.cpp
@@ -13,6 +13,7 @@
 #include "GrContextFactory.h"
 #include "GrShaderCaps.h"
 #include "GrSurfaceContext.h"
+#include "GrTest.h"
 #include "gl/GrGLGpu.h"
 #include "gl/GrGLUtil.h"
 #include "gl/GLTestContext.h"
@@ -129,14 +130,12 @@
     externalTexture.fID = glCtx0->eglImageToExternalTexture(image);
 
     // Wrap this texture ID in a GrTexture
-    GrBackendTextureDesc externalDesc;
-    externalDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    externalDesc.fWidth = kSize;
-    externalDesc.fHeight = kSize;
-    externalDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+    GrBackendTexture backendTex(kSize, kSize, kRGBA_8888_GrPixelConfig, externalTexture);
 
+    // TODO: If I make this TopLeft origin to match resolve_origin calls for kDefault, this test
+    // fails on the Nexus5. Why?
     sk_sp<GrSurfaceContext> surfaceContext = context0->contextPriv().makeBackendSurfaceContext(
-                                                                           externalDesc, nullptr);
+            backendTex, kBottomLeft_GrSurfaceOrigin, kNone_GrBackendTextureFlag, 0, nullptr);
 
     if (!surfaceContext) {
         ERRORF(reporter, "Error wrapping external texture in GrSurfaceContext.");
@@ -146,25 +145,21 @@
 
     // Should not be able to wrap as a RT
     {
-        externalDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
-
         sk_sp<GrSurfaceContext> temp = context0->contextPriv().makeBackendSurfaceContext(
-                                                                           externalDesc, nullptr);
+            backendTex, kBottomLeft_GrSurfaceOrigin, kRenderTarget_GrBackendTextureFlag, 0,
+            nullptr);
         if (temp) {
             ERRORF(reporter, "Should not be able to wrap an EXTERNAL texture as a RT.");
         }
-        externalDesc.fFlags = kNone_GrBackendTextureFlag;
     }
 
     // Should not be able to wrap with a sample count
     {
-        externalDesc.fSampleCnt = 4;
         sk_sp<GrSurfaceContext> temp = context0->contextPriv().makeBackendSurfaceContext(
-                                                                           externalDesc, nullptr);
+            backendTex, kBottomLeft_GrSurfaceOrigin, kNone_GrBackendTextureFlag, 4, nullptr);
         if (temp) {
             ERRORF(reporter, "Should not be able to wrap an EXTERNAL texture with MSAA.");
         }
-        externalDesc.fSampleCnt = 0;
     }
 
     test_read_pixels(reporter, surfaceContext.get(), pixels.get(), "EGLImageTest-read");
diff --git a/tests/EmptyPathTest.cpp b/tests/EmptyPathTest.cpp
index 060ef8d..5b857a0 100644
--- a/tests/EmptyPathTest.cpp
+++ b/tests/EmptyPathTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkPath.h"
 #include "Test.h"
diff --git a/tests/EncodeTest.cpp b/tests/EncodeTest.cpp
new file mode 100644
index 0000000..4d0ade1
--- /dev/null
+++ b/tests/EncodeTest.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 "Resources.h"
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkEncodedImageFormat.h"
+#include "SkImage.h"
+#include "SkJpegEncoder.h"
+#include "SkPngEncoder.h"
+#include "SkStream.h"
+#include "SkWebpEncoder.h"
+
+static bool encode(SkEncodedImageFormat format, SkWStream* dst, const SkPixmap& src) {
+    switch (format) {
+        case SkEncodedImageFormat::kJPEG:
+            return SkJpegEncoder::Encode(dst, src, SkJpegEncoder::Options());
+        case SkEncodedImageFormat::kPNG:
+            return SkPngEncoder::Encode(dst, src, SkPngEncoder::Options());
+        default:
+            return false;
+    }
+}
+
+static std::unique_ptr<SkEncoder> make(SkEncodedImageFormat format, SkWStream* dst,
+                                       const SkPixmap& src) {
+    switch (format) {
+        case SkEncodedImageFormat::kJPEG:
+            return SkJpegEncoder::Make(dst, src, SkJpegEncoder::Options());
+        case SkEncodedImageFormat::kPNG:
+            return SkPngEncoder::Make(dst, src, SkPngEncoder::Options());
+        default:
+            return nullptr;
+    }
+}
+
+static void test_encode(skiatest::Reporter* r, SkEncodedImageFormat format) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
+    success = encode(format, &dst0, src);
+    REPORTER_ASSERT(r, success);
+
+    auto encoder1 = make(format, &dst1, src);
+    for (int i = 0; i < src.height(); i++) {
+        success = encoder1->encodeRows(1);
+        REPORTER_ASSERT(r, success);
+    }
+
+    auto encoder2 = make(format, &dst2, src);
+    for (int i = 0; i < src.height(); i+=3) {
+        success = encoder2->encodeRows(3);
+        REPORTER_ASSERT(r, success);
+    }
+
+    auto encoder3 = make(format, &dst3, src);
+    success = encoder3->encodeRows(200);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    sk_sp<SkData> data3 = dst3.detachAsData();
+    REPORTER_ASSERT(r, data0->equals(data1.get()));
+    REPORTER_ASSERT(r, data0->equals(data2.get()));
+    REPORTER_ASSERT(r, data0->equals(data3.get()));
+}
+
+DEF_TEST(Encode, r) {
+    test_encode(r, SkEncodedImageFormat::kJPEG);
+    test_encode(r, SkEncodedImageFormat::kPNG);
+}
+
+static inline bool almost_equals(SkPMColor a, SkPMColor b, int tolerance) {
+    if (SkTAbs((int)SkGetPackedR32(a) - (int)SkGetPackedR32(b)) > tolerance) {
+        return false;
+    }
+
+    if (SkTAbs((int)SkGetPackedG32(a) - (int)SkGetPackedG32(b)) > tolerance) {
+        return false;
+    }
+
+    if (SkTAbs((int)SkGetPackedB32(a) - (int)SkGetPackedB32(b)) > tolerance) {
+        return false;
+    }
+
+    if (SkTAbs((int)SkGetPackedA32(a) - (int)SkGetPackedA32(b)) > tolerance) {
+        return false;
+    }
+
+    return true;
+}
+
+static inline bool almost_equals(const SkBitmap& a, const SkBitmap& b, int tolerance) {
+    if (a.info() != b.info()) {
+        return false;
+    }
+
+    SkASSERT(kN32_SkColorType == a.colorType());
+    for (int y = 0; y < a.height(); y++) {
+        for (int x = 0; x < a.width(); x++) {
+            if (!almost_equals(*a.getAddr32(x, y), *b.getAddr32(x, y), tolerance)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+DEF_TEST(Encode_JpegDownsample, r) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2;
+    SkJpegEncoder::Options options;
+    success = SkJpegEncoder::Encode(&dst0, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fDownsample = SkJpegEncoder::Downsample::k422;
+    success = SkJpegEncoder::Encode(&dst1, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fDownsample = SkJpegEncoder::Downsample::k444;
+    success = SkJpegEncoder::Encode(&dst2, src, options);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    REPORTER_ASSERT(r, data0->size() < data1->size());
+    REPORTER_ASSERT(r, data1->size() < data2->size());
+
+    SkBitmap bm0, bm1, bm2;
+    SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, almost_equals(bm0, bm1, 60));
+    REPORTER_ASSERT(r, almost_equals(bm1, bm2, 60));
+}
+
+DEF_TEST(Encode_PngOptions, r) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2;
+    SkPngEncoder::Options options;
+    success = SkPngEncoder::Encode(&dst0, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fFilterFlags = SkPngEncoder::FilterFlag::kUp;
+    success = SkPngEncoder::Encode(&dst1, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fZLibLevel = 3;
+    success = SkPngEncoder::Encode(&dst2, src, options);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    REPORTER_ASSERT(r, data0->size() < data1->size());
+    REPORTER_ASSERT(r, data1->size() < data2->size());
+
+    SkBitmap bm0, bm1, bm2;
+    SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
+    REPORTER_ASSERT(r, almost_equals(bm0, bm2, 0));
+}
+
+DEF_TEST(Encode_WebpOptions, r) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("google_chrome.ico", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
+    SkWebpEncoder::Options options;
+    options.fCompression = SkWebpEncoder::Compression::kLossless;
+    options.fQuality = 0.0f;
+    success = SkWebpEncoder::Encode(&dst0, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fQuality = 100.0f;
+    success = SkWebpEncoder::Encode(&dst1, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fCompression = SkWebpEncoder::Compression::kLossy;
+    options.fQuality = 100.0f;
+    success = SkWebpEncoder::Encode(&dst2, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fCompression = SkWebpEncoder::Compression::kLossy;
+    options.fQuality = 50.0f;
+    success = SkWebpEncoder::Encode(&dst3, src, options);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    sk_sp<SkData> data3 = dst3.detachAsData();
+    REPORTER_ASSERT(r, data0->size() > data1->size());
+    REPORTER_ASSERT(r, data1->size() > data2->size());
+    REPORTER_ASSERT(r, data2->size() > data3->size());
+
+    SkBitmap bm0, bm1, bm2, bm3;
+    SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data3)->asLegacyBitmap(&bm3, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
+    REPORTER_ASSERT(r, almost_equals(bm0, bm2, 90));
+    REPORTER_ASSERT(r, almost_equals(bm2, bm3, 45));
+}
diff --git a/tests/F16StagesTest.cpp b/tests/F16StagesTest.cpp
new file mode 100644
index 0000000..51cb861
--- /dev/null
+++ b/tests/F16StagesTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "SkRasterPipeline.h"
+#include "Test.h"
+
+DEF_TEST(F16Stages, r) {
+    // Make sure SkRasterPipeline::load_f16 and store_f16 can handle a range of
+    // ordinary (0<=x<=1) and interesting (x<0, x>1) values.
+    float floats[16] = {
+        0.0f, 0.25f, 0.5f, 1.0f,
+        -1.25f, -0.5f, 1.25f, 2.0f,
+        0,0,0,0, 0,0,0,0,  // pad a bit to make sure we qualify for platform-specific code
+    };
+    uint16_t halfs[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
+
+    float*    f32 = floats;
+    uint16_t* f16 = halfs;
+
+    {
+        SkRasterPipeline_<256> p;
+        p.append(SkRasterPipeline:: load_f32, &f32);
+        p.append(SkRasterPipeline::store_f16, &f16);
+        p.run(0,0,16/4);
+    }
+    REPORTER_ASSERT(r, f16[0] == 0x0000);
+    REPORTER_ASSERT(r, f16[1] == 0x3400);
+    REPORTER_ASSERT(r, f16[2] == 0x3800);
+    REPORTER_ASSERT(r, f16[3] == 0x3c00);
+    REPORTER_ASSERT(r, f16[4] == 0xbd00);
+    REPORTER_ASSERT(r, f16[5] == 0xb800);
+    REPORTER_ASSERT(r, f16[6] == 0x3d00);
+    REPORTER_ASSERT(r, f16[7] == 0x4000);
+
+    {
+        SkRasterPipeline_<256> p;
+        p.append(SkRasterPipeline:: load_f16, &f16);
+        p.append(SkRasterPipeline::store_f32, &f32);
+        p.run(0,0,16/4);
+    }
+    REPORTER_ASSERT(r, f32[0] ==  0.00f);
+    REPORTER_ASSERT(r, f32[1] ==  0.25f);
+    REPORTER_ASSERT(r, f32[2] ==  0.50f);
+    REPORTER_ASSERT(r, f32[3] ==  1.00f);
+    REPORTER_ASSERT(r, f32[4] == -1.25f);
+    REPORTER_ASSERT(r, f32[5] == -0.50f);
+    REPORTER_ASSERT(r, f32[6] ==  1.25f);
+    REPORTER_ASSERT(r, f32[7] ==  2.00f);
+}
diff --git a/tests/FlattenDrawableTest.cpp b/tests/FlattenDrawableTest.cpp
index d8f5142..9df8ba0 100644
--- a/tests/FlattenDrawableTest.cpp
+++ b/tests/FlattenDrawableTest.cpp
@@ -257,7 +257,7 @@
     canvas->drawPaint(paint);
     SkPaint textPaint;
     textPaint.setColor(SK_ColorBLUE);
-    canvas->drawText("TEXT", 4, 467.0f, 100.0f, textPaint);
+    canvas->drawString("TEXT", 467.0f, 100.0f, textPaint);
 
     // Draw some drawables as well
     sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
diff --git a/tests/FlattenableFactoryToName.cpp b/tests/FlattenableFactoryToName.cpp
index 35ec984..fc47dfc 100644
--- a/tests/FlattenableFactoryToName.cpp
+++ b/tests/FlattenableFactoryToName.cpp
@@ -37,6 +37,5 @@
     bm.allocN32Pixels(8, 8);
     bm.eraseColor(SK_ColorCYAN);
     sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
-    auto shader = image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-    test_flattenable(r, shader.get(), "SkImage::newShader()");
+    test_flattenable(r, image->makeShader().get(), "SkImage::newShader()");
 }
diff --git a/tests/FloatingPointTextureTest.cpp b/tests/FloatingPointTextureTest.cpp
index 4214e4e..e092921 100644
--- a/tests/FloatingPointTextureTest.cpp
+++ b/tests/FloatingPointTextureTest.cpp
@@ -17,6 +17,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrResourceProvider.h"
 #include "GrTexture.h"
 #include "SkHalf.h"
@@ -51,14 +52,23 @@
         desc.fHeight = DEV_H;
         desc.fConfig = config;
         desc.fOrigin = 0 == origin ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
-        sk_sp<GrTexture> fpTexture(context->resourceProvider()->createTexture(
-            desc, SkBudgeted::kNo, controlPixelData.begin(), 0));
+        sk_sp<GrTextureProxy> fpProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                     desc, SkBudgeted::kNo,
+                                                                     controlPixelData.begin(), 0);
         // Floating point textures are NOT supported everywhere
-        if (nullptr == fpTexture) {
+        if (!fpProxy) {
             continue;
         }
-        REPORTER_ASSERT(reporter,
-                        fpTexture->readPixels(0, 0, DEV_W, DEV_H, desc.fConfig, readBuffer.begin(), 0));
+
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                    std::move(fpProxy), nullptr);
+        REPORTER_ASSERT(reporter, sContext);
+
+        bool result = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                               0, 0, DEV_W, DEV_H,
+                                                               desc.fConfig, nullptr,
+                                                               readBuffer.begin(), 0);
+        REPORTER_ASSERT(reporter, result);
         REPORTER_ASSERT(reporter,
                         0 == memcmp(readBuffer.begin(), controlPixelData.begin(), readBuffer.bytes()));
     }
diff --git a/tests/FontHostStreamTest.cpp b/tests/FontHostStreamTest.cpp
index 7c4cb67..c3bc878 100644
--- a/tests/FontHostStreamTest.cpp
+++ b/tests/FontHostStreamTest.cpp
@@ -39,9 +39,6 @@
     const int xOff = itest.fLeft - iref.fLeft;
     const int yOff = itest.fTop - iref.fTop;
 
-    SkAutoLockPixels alpRef(ref);
-    SkAutoLockPixels alpTest(test);
-
     for (int y = 0; y < test.height(); ++y) {
         for (int x = 0; x < test.width(); ++x) {
             SkColor testColor = test.getColor(x, y);
@@ -85,7 +82,7 @@
 
         // Test: origTypeface and streamTypeface from orig data draw the same
         drawBG(&origCanvas);
-        origCanvas.drawText("A", 1, point.fX, point.fY, paint);
+        origCanvas.drawString("A", point.fX, point.fY, paint);
 
         sk_sp<SkTypeface> typeface(paint.getTypeface() ? paint.refTypeface()
                                                        : SkTypeface::MakeDefault());
diff --git a/tests/FontMgrTest.cpp b/tests/FontMgrTest.cpp
index b6a0cc5..ef420f3 100644
--- a/tests/FontMgrTest.cpp
+++ b/tests/FontMgrTest.cpp
@@ -5,14 +5,14 @@
  * found in the LICENSE file.
  */
 
+#include "SkAdvancedTypefaceMetrics.h"
 #include "SkCommandLineFlags.h"
+#include "SkFont.h"
 #include "SkFontMgr.h"
+#include "SkPaint.h"
 #include "SkTypeface.h"
 #include "Test.h"
 
-#include "SkFont.h"
-#include "SkPaint.h"
-
 #include <initializer_list>
 #include <limits>
 #include <vector>
@@ -114,6 +114,12 @@
     }
 }
 
+static void test_match(skiatest::Reporter* reporter) {
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkFontStyleSet> styleSet(fm->matchFamily(nullptr));
+    REPORTER_ASSERT(reporter, styleSet);
+}
+
 static void test_matchStyleCSS3(skiatest::Reporter* reporter) {
     static const SkFontStyle invalidFontStyle(101, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
 
@@ -127,9 +133,9 @@
             return nullptr;
         }
         void onFilterRec(SkScalerContextRec*) const override { }
-        virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-            PerGlyphInfo,
-            const uint32_t*, uint32_t) const override { return nullptr; }
+        std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
+            return nullptr;
+        }
         void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
         virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
             uint16_t glyphs[], int glyphCount) const override {
@@ -710,6 +716,7 @@
 DEFINE_bool(verboseFontMgr, false, "run verbose fontmgr tests.");
 
 DEF_TEST(FontMgr, reporter) {
+    test_match(reporter);
     test_matchStyleCSS3(reporter);
     test_fontiter(reporter, FLAGS_verboseFontMgr);
     test_alias_names(reporter);
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index a8434ec..5125978 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -113,7 +113,7 @@
     class GLFP : public GrGLSLFragmentProcessor {
     public:
         void emitCode(EmitArgs& args) override {
-            this->emitChild(0, nullptr, args);
+            this->emitChild(0, args);
         }
 
     private:
@@ -148,7 +148,7 @@
                                                 : kBottomLeft_GrSurfaceOrigin;
     int sampleCnt = random->nextBool() ? SkTMin(4, caps->maxSampleCount()) : 0;
 
-    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
                                                                            SkBackingFit::kExact,
                                                                            kRenderTargetWidth,
                                                                            kRenderTargetHeight,
@@ -218,7 +218,11 @@
     if (d->fRandom->nextF() < procTreeProbability) {
         // A full tree with 5 levels (31 nodes) may cause a program that exceeds shader limits
         // (e.g. uniform or varying limits); maxTreeLevels should be a number from 1 to 4 inclusive.
-        const int maxTreeLevels = 4;
+        int maxTreeLevels = 4;
+        // On iOS we can exceed the maximum number of varyings. http://skbug.com/6627.
+#ifdef SK_BUILD_FOR_IOS
+        maxTreeLevels = 2;
+#endif
         sk_sp<GrFragmentProcessor> fp(create_random_proc_tree(d, 2, maxTreeLevels));
         paint->addColorFragmentProcessor(std::move(fp));
     } else {
@@ -240,43 +244,15 @@
     }
 }
 
-static bool set_random_state(GrPaint* paint, SkRandom* random) {
+static void set_random_state(GrPaint* paint, SkRandom* random) {
     if (random->nextBool()) {
         paint->setDisableOutputConversionToSRGB(true);
     }
     if (random->nextBool()) {
         paint->setAllowSRGBInputs(true);
     }
-    return random->nextBool();
 }
 
-// right now, the only thing we seem to care about in drawState's stencil is 'doesWrite()'
-static const GrUserStencilSettings* get_random_stencil(SkRandom* random) {
-    static constexpr GrUserStencilSettings kDoesWriteStencil(
-        GrUserStencilSettings::StaticInit<
-            0xffff,
-            GrUserStencilTest::kAlways,
-            0xffff,
-            GrUserStencilOp::kReplace,
-            GrUserStencilOp::kReplace,
-            0xffff>()
-    );
-    static constexpr GrUserStencilSettings kDoesNotWriteStencil(
-        GrUserStencilSettings::StaticInit<
-            0xffff,
-            GrUserStencilTest::kNever,
-            0xffff,
-            GrUserStencilOp::kKeep,
-            GrUserStencilOp::kKeep,
-            0xffff>()
-    );
-
-    if (random->nextBool()) {
-        return &kDoesWriteStencil;
-    } else {
-        return &kDoesNotWriteStencil;
-    }
-}
 #endif
 
 #if !GR_TEST_UTILS
@@ -285,28 +261,30 @@
 bool GrDrawingManager::ProgramUnitTest(GrContext* context, int maxStages) {
     GrDrawingManager* drawingManager = context->contextPriv().drawingManager();
 
+    sk_sp<GrTextureProxy> proxies[2];
+
     // setup dummy textures
     GrSurfaceDesc dummyDesc;
     dummyDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    dummyDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
     dummyDesc.fConfig = kRGBA_8888_GrPixelConfig;
     dummyDesc.fWidth = 34;
     dummyDesc.fHeight = 18;
-    sk_sp<GrTexture> dummyTexture1(
-        context->resourceProvider()->createTexture(dummyDesc, SkBudgeted::kNo, nullptr, 0));
+    proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                              dummyDesc, SkBudgeted::kNo, nullptr, 0);
     dummyDesc.fFlags = kNone_GrSurfaceFlags;
+    dummyDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
     dummyDesc.fConfig = kAlpha_8_GrPixelConfig;
     dummyDesc.fWidth = 16;
     dummyDesc.fHeight = 22;
-    sk_sp<GrTexture> dummyTexture2(
-        context->resourceProvider()->createTexture(dummyDesc, SkBudgeted::kNo, nullptr, 0));
+    proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                              dummyDesc, SkBudgeted::kNo, nullptr, 0);
 
-    if (!dummyTexture1 || ! dummyTexture2) {
+    if (!proxies[0] || !proxies[1]) {
         SkDebugf("Could not allocate dummy textures");
         return false;
     }
 
-    GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
-
     // dummy scissor state
     GrScissorState scissor;
 
@@ -321,29 +299,18 @@
             return false;
         }
 
-        GrPaint grPaint;
-
-        std::unique_ptr<GrMeshDrawOp> op(GrRandomDrawOp(&random, context));
-        SkASSERT(op);
-
-        GrProcessorTestData ptd(&random, context, renderTargetContext.get(), dummyTextures);
-        set_random_color_coverage_stages(&grPaint, &ptd, maxStages);
-        set_random_xpf(&grPaint, &ptd);
-        bool snapToCenters = set_random_state(&grPaint, &random);
-        const GrUserStencilSettings* uss = get_random_stencil(&random);
-        // We don't use kHW because we will hit an assertion if the render target is not
-        // multisampled
-        static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kCoverage};
-        GrAAType aaType = kAATypes[random.nextULessThan(SK_ARRAY_COUNT(kAATypes))];
-
-        renderTargetContext->priv().testingOnly_addMeshDrawOp(std::move(grPaint), aaType,
-                                                              std::move(op), uss, snapToCenters);
+        GrPaint paint;
+        GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies);
+        set_random_color_coverage_stages(&paint, &ptd, maxStages);
+        set_random_xpf(&paint, &ptd);
+        set_random_state(&paint, &random);
+        GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint));
     }
     // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes)
-    drawingManager->flush();
+    drawingManager->flush(nullptr);
 
     // Validate that GrFPs work correctly without an input.
-    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
                                                                            SkBackingFit::kExact,
                                                                            kRenderTargetWidth,
                                                                            kRenderTargetHeight,
@@ -358,21 +325,17 @@
     for (int i = 0; i < fpFactoryCnt; ++i) {
         // Since FP factories internally randomize, call each 10 times.
         for (int j = 0; j < 10; ++j) {
-            std::unique_ptr<GrMeshDrawOp> op(GrRandomDrawOp(&random, context));
-            SkASSERT(op);
-            GrProcessorTestData ptd(&random, context, renderTargetContext.get(), dummyTextures);
-            GrPaint grPaint;
-            grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
+            GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies);
 
+            GrPaint paint;
+            paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
             sk_sp<GrFragmentProcessor> fp(
                 GrProcessorTestFactory<GrFragmentProcessor>::MakeIdx(i, &ptd));
             sk_sp<GrFragmentProcessor> blockFP(
                 BlockInputFragmentProcessor::Make(std::move(fp)));
-            grPaint.addColorFragmentProcessor(std::move(blockFP));
-
-            renderTargetContext->priv().testingOnly_addMeshDrawOp(
-                    std::move(grPaint), GrAAType::kNone, std::move(op));
-            drawingManager->flush();
+            paint.addColorFragmentProcessor(std::move(blockFP));
+            GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint));
+            drawingManager->flush(nullptr);
         }
     }
 
@@ -382,56 +345,32 @@
 
 static int get_glprograms_max_stages(GrContext* context) {
     GrGLGpu* gpu = static_cast<GrGLGpu*>(context->getGpu());
-    /*
-     * For the time being, we only support the test with desktop GL or for android on
-     * ARM platforms
-     * TODO When we run ES 3.00 GLSL in more places, test again
-     */
-    if (kGL_GrGLStandard == gpu->glStandard() ||
-        kARM_GrGLVendor == gpu->ctxInfo().vendor()) {
-        return 6;
-    } else if (kTegra3_GrGLRenderer == gpu->ctxInfo().renderer() ||
-               kOther_GrGLRenderer == gpu->ctxInfo().renderer()) {
-        return 1;
-    }
-    return 0;
-}
-
-static void test_glprograms_native(skiatest::Reporter* reporter,
-                                   const sk_gpu_test::ContextInfo& ctxInfo) {
-    int maxStages = get_glprograms_max_stages(ctxInfo.grContext());
-    if (maxStages == 0) {
-        return;
-    }
-    REPORTER_ASSERT(reporter, GrDrawingManager::ProgramUnitTest(ctxInfo.grContext(), maxStages));
-}
-
-static void test_glprograms_other_contexts(
-            skiatest::Reporter* reporter,
-            const sk_gpu_test::ContextInfo& ctxInfo) {
-    int maxStages = get_glprograms_max_stages(ctxInfo.grContext());
-#ifdef SK_BUILD_FOR_WIN
-    // Some long shaders run out of temporary registers in the D3D compiler on ANGLE and
-    // command buffer.
-    maxStages = SkTMin(maxStages, 2);
+    int maxStages = 6;
+    if (kGLES_GrGLStandard == gpu->glStandard()) {
+    // We've had issues with driver crashes and HW limits being exceeded with many effects on
+    // Android devices. We have passes on ARM devices with the default number of stages.
+    // TODO When we run ES 3.00 GLSL in more places, test again
+#ifdef SK_BUILD_FOR_ANDROID
+        if (kARM_GrGLVendor != gpu->ctxInfo().vendor()) {
+            maxStages = 1;
+        }
 #endif
+    // On iOS we can exceed the maximum number of varyings. http://skbug.com/6627.
+#ifdef SK_BUILD_FOR_IOS
+        maxStages = 3;
+#endif
+    }
+    return maxStages;
+}
+
+static void test_glprograms(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo) {
+    int maxStages = get_glprograms_max_stages(ctxInfo.grContext());
     if (maxStages == 0) {
         return;
     }
     REPORTER_ASSERT(reporter, GrDrawingManager::ProgramUnitTest(ctxInfo.grContext(), maxStages));
 }
 
-static bool is_native_gl_context_type(sk_gpu_test::GrContextFactory::ContextType type) {
-    return type == sk_gpu_test::GrContextFactory::kGL_ContextType ||
-           type == sk_gpu_test::GrContextFactory::kGLES_ContextType;
-}
-
-static bool is_other_rendering_gl_context_type(sk_gpu_test::GrContextFactory::ContextType type) {
-    return !is_native_gl_context_type(type) &&
-           kOpenGL_GrBackend == sk_gpu_test::GrContextFactory::ContextTypeBackend(type) &&
-           sk_gpu_test::GrContextFactory::IsRenderingContext(type);
-}
-
 DEF_GPUTEST(GLPrograms, reporter, /*factory*/) {
     // Set a locale that would cause shader compilation to fail because of , as decimal separator.
     // skbug 3330
@@ -445,10 +384,8 @@
     GrContextOptions opts;
     opts.fSuppressPrints = true;
     sk_gpu_test::GrContextFactory debugFactory(opts);
-    skiatest::RunWithGPUTestContexts(test_glprograms_native, &is_native_gl_context_type,
-                                     reporter, &debugFactory);
-    skiatest::RunWithGPUTestContexts(test_glprograms_other_contexts,
-                                     &is_other_rendering_gl_context_type, reporter, &debugFactory);
+    skiatest::RunWithGPUTestContexts(test_glprograms, &skiatest::IsRenderingGLContextType, reporter,
+                                     &debugFactory);
 }
 
 #endif
diff --git a/tests/GifTest.cpp b/tests/GifTest.cpp
index dae2bc9..0168d89 100644
--- a/tests/GifTest.cpp
+++ b/tests/GifTest.cpp
@@ -256,7 +256,7 @@
     options.fColorCount = colorCountPtr;
 
     SkBitmap bm;
-    bm.allocPixels(codec->getInfo(), nullptr, colorTable.get());
+    bm.allocPixels(codec->getInfo(), colorTable);
     const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(),
             bm.rowBytes(), &options);
     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
diff --git a/tests/GpuSampleLocationsTest.cpp b/tests/GpuSampleLocationsTest.cpp
index 5cb9b60..75d2846 100644
--- a/tests/GpuSampleLocationsTest.cpp
+++ b/tests/GpuSampleLocationsTest.cpp
@@ -91,19 +91,8 @@
     virtual ~TestSampleLocationsInterface() {}
 };
 
-static void construct_dummy_pipeline(GrRenderTargetContext* dc, GrPipeline* pipeline) {
-    GrPipelineBuilder dummyBuilder(GrPaint(), GrAAType::kNone);
-    GrScissorState dummyScissor;
-    GrWindowRectsState dummyWindows;
-
-    GrProcessorSet::FragmentProcessorAnalysis analysis;
-    GrPipeline::InitArgs args;
-    dummyBuilder.getPipelineInitArgs(&args);
-    args.fRenderTarget = dc->accessRenderTarget();
-    args.fAnalysis = &analysis;
-    args.fCaps = dc->caps();
-    args.fDstTexture = GrXferProcessor::DstTexture();
-    pipeline->init(args);
+static sk_sp<GrPipeline> construct_dummy_pipeline(GrRenderTargetContext* dc) {
+    return sk_sp<GrPipeline>(new GrPipeline(dc->accessRenderTarget(), SkBlendMode::kSrcOver));
 }
 
 void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern,
@@ -133,11 +122,11 @@
     for (int i = 0; i < numTestPatterns; ++i) {
         int numSamples = (int)kTestPatterns[i].size();
         GrAlwaysAssert(numSamples > 1 && SkIsPow2(numSamples));
-        bottomUps[i] = ctx->makeRenderTargetContextWithFallback(
+        bottomUps[i] = ctx->makeDeferredRenderTargetContextWithFallback(
                            SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr,
                            rand.nextRangeU(1 + numSamples / 2, numSamples),
                            kBottomLeft_GrSurfaceOrigin);
-        topDowns[i] = ctx->makeRenderTargetContextWithFallback(
+        topDowns[i] = ctx->makeDeferredRenderTargetContextWithFallback(
                           SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr,
                           rand.nextRangeU(1 + numSamples / 2, numSamples),
                           kTopLeft_GrSurfaceOrigin);
@@ -148,11 +137,10 @@
         for (int i = 0; i < numTestPatterns; ++i) {
             testInterface->overrideSamplePattern(kTestPatterns[i]);
             for (GrRenderTargetContext* dc : {bottomUps[i].get(), topDowns[i].get()}) {
-                GrPipeline dummyPipeline;
-                construct_dummy_pipeline(dc, &dummyPipeline);
+                sk_sp<GrPipeline> dummyPipeline = construct_dummy_pipeline(dc);
                 GrRenderTarget* rt = dc->accessRenderTarget();
                 assert_equal(reporter, kTestPatterns[i],
-                             rt->renderTargetPriv().getMultisampleSpecs(dummyPipeline),
+                             rt->renderTargetPriv().getMultisampleSpecs(*dummyPipeline),
                              kBottomLeft_GrSurfaceOrigin == rt->origin());
             }
         }
@@ -202,6 +190,11 @@
 DEF_GPUTEST(GLSampleLocations, reporter, /*factory*/) {
     GLTestSampleLocationsInterface testInterface;
     sk_sp<GrContext> ctx(GrContext::Create(kOpenGL_GrBackend, testInterface));
+
+    // This test relies on at least 2 samples.
+    if (ctx->caps()->maxSampleCount() < 2) {
+        return;
+    }
     test_sampleLocations(reporter, &testInterface, ctx.get());
 }
 
diff --git a/tests/GrGLSLPrettyPrintTest.cpp b/tests/GrGLSLPrettyPrintTest.cpp
deleted file mode 100644
index 1a5152f..0000000
--- a/tests/GrGLSLPrettyPrintTest.cpp
+++ /dev/null
@@ -1,125 +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 "SkTypes.h"
-
-#if SK_SUPPORT_GPU
-#include "Test.h"
-#include "gl/GrGLSLPrettyPrint.h"
-
-#define ASSERT(x) REPORTER_ASSERT(r, x)
-
-const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;"
-                     "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}"
-                     "void main()");
-const SkString input2("{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){"
-                     "doingstuff"
-                     ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this"
-                     " comment\n is");
-const SkString input3(" dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n"
-                     "//a comment");
-const SkString input4("breaking in comment");
-const SkString input5("continuing the comment");
-const SkString input6("\n}}a; little ;  love; for   ; leading;  spaces;} "
-                     "an struct = { int a; int b; };"
-                     "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }");
-
-const SkString output1(
-        "   1\t#this is not a realshader\n"
-        "   2\tvec4 some stuff;\n"
-        "   3\toutside of a function;\n"
-        "   4\tint i(int b, int c) \n"
-        "   5\t{\n"
-        "   6\t\t{\n"
-        "   7\t\t\tsome stuff;\n"
-        "   8\t\t}\n"
-        "   9\t\tfake block;\n"
-        "  10\t\t//comments\n"
-        "  11\t\treturn i;\n"
-        "  12\t}\n"
-        "  13\tvoid main()\n"
-        "  14\t{\n"
-        "  15\t\tnowin a function;\n"
-        "  16\t\t{\n"
-        "  17\t\t\tindenting;\n"
-        "  18\t\t\t{\n"
-        "  19\t\t\t\tabit more;\n"
-        "  20\t\t\t\tdreadedfor((;;)(;)((;;);))\n"
-        "  21\t\t\t\t{\n"
-        "  22\t\t\t\t\tdoingstuff;\n"
-        "  23\t\t\t\t\tfor(;;;)\n"
-        "  24\t\t\t\t\t{\n"
-        "  25\t\t\t\t\t\tand more stufff;\n"
-        "  26\t\t\t\t\t\tmixed garbage/*using this comment\n"
-        "  27\t\t\t\t\t\t is dangerous\n"
-        "  28\t\t\t\t\t\tdo so at your own\n"
-        "  29\t\t\t\t\t\t risk*/;\n"
-        "  30\t\t\t\t\t\t//a commentbreaking in commentcontinuing the comment\n"
-        "  31\t\t\t\t\t}\n"
-        "  32\t\t\t\t}\n"
-        "  33\t\t\t\ta;\n"
-        "  34\t\t\t\tlittle ;\n"
-        "  35\t\t\t\tlove;\n"
-        "  36\t\t\t\tfor   ;\n"
-        "  37\t\t\t\tleading;\n"
-        "  38\t\t\t\tspaces;\n"
-        "  39\t\t\t}\n"
-        "  40\t\t\tan struct = \n"
-        "  41\t\t\t{\n"
-        "  42\t\t\t\tint a;\n"
-        "  43\t\t\t\tint b;\n"
-        "  44\t\t\t}\n"
-        "  45\t\t\t;\n"
-        "  46\t\t\tint[5] arr = int[5](1,2,3,4,5);\n"
-        "  47\t\t}\n"
-        "  48\t\tsome code at the bottom;\n"
-        "  49\t\tfor(;;) \n"
-        "  50\t\t{\n"
-        "  51\t\t}\n"
-        "  52\t}\n"
-        "  53\t");
-
-const SkString neg1("{;;{{{{;;;{{{{{{{{{{{");
-const SkString neg2("###\n##\n#####(((((((((((((unbalanced verything;;;");
-const SkString neg3("}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
-        ";;;;;;/////");
-
-DEF_TEST(GrGLSLPrettyPrint, r) {
-    SkTArray<const char*> testStr;
-    SkTArray<int> lengths;
-    testStr.push_back(input1.c_str());
-    lengths.push_back((int)input1.size());
-    testStr.push_back(input2.c_str());
-    lengths.push_back((int)input2.size());
-    testStr.push_back(input3.c_str());
-    lengths.push_back((int)input3.size());
-    testStr.push_back(input4.c_str());
-    lengths.push_back((int)input4.size());
-    testStr.push_back(input5.c_str());
-    lengths.push_back((int)input5.size());
-    testStr.push_back(input6.c_str());
-    lengths.push_back((int)input6.size());
-
-    SkString test = GrGLSLPrettyPrint::PrettyPrintGLSL(testStr.begin(), lengths.begin(),
-                                                       testStr.count(), true);
-    ASSERT(output1 == test);
-
-    testStr.reset();
-    lengths.reset();
-    testStr.push_back(neg1.c_str());
-    lengths.push_back((int)neg1.size());
-    testStr.push_back(neg2.c_str());
-    lengths.push_back((int)neg2.size());
-    testStr.push_back(neg3.c_str());
-    lengths.push_back((int)neg3.size());
-
-    // Just test we don't crash with garbage input
-    ASSERT(GrGLSLPrettyPrint::PrettyPrintGLSL(testStr.begin(), lengths.begin(), 1,
-                                              true).c_str() != nullptr);
-}
-
-#endif
diff --git a/tests/GrGetCoeffBlendKnownComponentsTest.cpp b/tests/GrGetCoeffBlendKnownComponentsTest.cpp
deleted file mode 100644
index 53bd09e..0000000
--- a/tests/GrGetCoeffBlendKnownComponentsTest.cpp
+++ /dev/null
@@ -1,111 +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.
- */
-
-#include "Test.h"
-
-#if SK_SUPPORT_GPU
-
-#include "GrBlend.h"
-#include "SkGr.h"
-#include "SkRandom.h"
-
-static inline SkPMColor GrColorToSkPMColor(GrColor c) {
-    GrColorIsPMAssert(c);
-    return SkPackARGB32(GrColorUnpackA(c), GrColorUnpackR(c), GrColorUnpackG(c), GrColorUnpackB(c));
-}
-
-static inline GrColor SkPMColorToGrColor(SkPMColor c) {
-    return GrColorPackRGBA(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c),
-                           SkGetPackedA32(c));
-}
-
-static GrColor make_baseline_color(GrColor src, GrColor dst, const SkXfermode* xm) {
-    SkPMColor skSrc = GrColorToSkPMColor(src);
-    SkPMColor skDst = GrColorToSkPMColor(dst);
-    if (xm) {
-        xm->xfer32(&skDst, &skSrc, 1, nullptr);
-    } else {
-        // null means src-over
-        skDst = SkPMSrcOver(skSrc, skDst);
-    }
-    return SkPMColorToGrColor(skDst);
-}
-
-DEF_TEST(GrGetCoeffBlendKnownComponents, reporter) {
-    SkRandom random;
-    for (int i = 0; i < SkXfermode::kLastCoeffMode; ++i) {
-        SkXfermode::Mode mode = (SkXfermode::Mode)i;
-        auto xm(SkXfermode::Make(mode));
-        SkXfermode::Coeff srcCoeff, dstCoeff;
-        SkAssertResult(SkXfermode::ModeAsCoeff(mode, &srcCoeff, &dstCoeff));
-        for (int j = 0; j < 1000; ++j) {
-            GrColor src = GrPremulColor(random.nextU());
-            GrColor dst = GrPremulColor(random.nextU());
-            GrColor outColor;
-            GrColorComponentFlags outFlags;
-            GrGetCoeffBlendKnownComponents(SkXfermodeCoeffToGrBlendCoeff(srcCoeff),
-                                           SkXfermodeCoeffToGrBlendCoeff(dstCoeff),
-                                           src, kRGBA_GrColorComponentFlags,
-                                           dst, kRGBA_GrColorComponentFlags,
-                                           &outColor, &outFlags);
-            GrColor baselineColor = make_baseline_color(src, dst, xm.get());
-            if (SkAbs32(GrColorUnpackA(baselineColor) - GrColorUnpackA(outColor)) > 1 ||
-                SkAbs32(GrColorUnpackR(baselineColor) - GrColorUnpackR(outColor)) > 1 ||
-                SkAbs32(GrColorUnpackG(baselineColor) - GrColorUnpackG(outColor)) > 1 ||
-                SkAbs32(GrColorUnpackB(baselineColor) - GrColorUnpackB(outColor)) > 1) {
-                ERRORF(reporter, "Blended color is 0x%08x, expected 0x%08x", outColor,
-                       baselineColor);
-            }
-            GrColorIsPMAssert(outColor);
-        }
-    }
-    GrColor outColor;
-    GrColorComponentFlags outFlags;
-    GrGetCoeffBlendKnownComponents(kZero_GrBlendCoeff, kZero_GrBlendCoeff,
-                                   0xFFFFFFFF, kNone_GrColorComponentFlags,
-                                   0xFFFFFFFF, kNone_GrColorComponentFlags,
-                                   &outColor, &outFlags);
-    REPORTER_ASSERT(reporter, GrColor_TRANSPARENT_BLACK == outColor &&
-                              kRGBA_GrColorComponentFlags == outFlags);
-    GrGetCoeffBlendKnownComponents(
-        kOne_GrBlendCoeff, kOne_GrBlendCoeff,
-        0x80FF0100, (kG_GrColorComponentFlag | kB_GrColorComponentFlag | kA_GrColorComponentFlag),
-        0x7F00FFFF, (kR_GrColorComponentFlag | kG_GrColorComponentFlag | kA_GrColorComponentFlag),
-        &outColor, &outFlags);
-    REPORTER_ASSERT(reporter, GrColor_WHITE == outColor && kRGBA_GrColorComponentFlags == outFlags);
-
-    GrGetCoeffBlendKnownComponents(
-        kOne_GrBlendCoeff, kISA_GrBlendCoeff,
-        0x0000000, kRGBA_GrColorComponentFlags,
-        0x80010203, kRGBA_GrColorComponentFlags,
-        &outColor, &outFlags);
-    REPORTER_ASSERT(reporter, 0x80010203 == outColor && kRGBA_GrColorComponentFlags == outFlags);
-
-    GrGetCoeffBlendKnownComponents(kZero_GrBlendCoeff, kISA_GrBlendCoeff,
-                                   0x0000000, kA_GrColorComponentFlag,
-                                   0x80010203, kRGBA_GrColorComponentFlags,
-                                   &outColor, &outFlags);
-    REPORTER_ASSERT(reporter, 0x80010203 == outColor && kRGBA_GrColorComponentFlags == outFlags);
-
-    GrGetCoeffBlendKnownComponents(
-        kIDC_GrBlendCoeff, kSC_GrBlendCoeff,
-        0x0, kNone_GrColorComponentFlags,
-        0x0, kRGBA_GrColorComponentFlags,
-        &outColor, &outFlags);
-    REPORTER_ASSERT(reporter, kNone_GrColorComponentFlags == outFlags);
-
-    GrGetCoeffBlendKnownComponents(
-        kOne_GrBlendCoeff, kISA_GrBlendCoeff,
-        0xFF808080, (kG_GrColorComponentFlag | kB_GrColorComponentFlag | kA_GrColorComponentFlag),
-        0xFF606060, kRGBA_GrColorComponentFlags,
-        &outColor, &outFlags);
-    REPORTER_ASSERT(reporter,
-                    (kG_GrColorComponentFlag | kB_GrColorComponentFlag | kA_GrColorComponentFlag) == outFlags &&
-                    (outColor & 0xFFFFFF00) == 0xFF808000);
-}
-
-#endif
diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp
new file mode 100644
index 0000000..3a2be78
--- /dev/null
+++ b/tests/GrMeshTest.cpp
@@ -0,0 +1,404 @@
+/*
+ * 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 "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrGeometryProcessor.h"
+#include "GrOpFlushState.h"
+#include "GrRenderTargetContext.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrResourceKey.h"
+#include "SkMakeUnique.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLGeometryProcessor.h"
+#include "glsl/GrGLSLVarying.h"
+#include <array>
+#include <vector>
+
+
+GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
+
+static constexpr int kBoxSize = 2;
+static constexpr int kBoxCountY = 8;
+static constexpr int kBoxCountX = 8;
+static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
+
+static constexpr int kImageWidth = kBoxCountY * kBoxSize;
+static constexpr int kImageHeight = kBoxCountX * kBoxSize;
+
+static constexpr int kIndexPatternRepeatCount = 3;
+constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
+
+
+class DrawMeshHelper {
+public:
+    DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
+
+    sk_sp<const GrBuffer> getIndexBuffer();
+
+    template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
+        return this->makeVertexBuffer(data.begin(), data.count());
+    }
+    template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
+        return this->makeVertexBuffer(data.data(), data.size());
+    }
+    template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
+
+    void drawMesh(const GrMesh& mesh);
+
+private:
+    GrOpFlushState* fState;
+};
+
+struct Box {
+    float fX, fY;
+    GrColor fColor;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This is a GPU-backend specific test. It tries to test all possible usecases of GrMesh. The test
+ * works by drawing checkerboards of colored boxes, reading back the pixels, and comparing with
+ * expected results. The boxes are drawn on integer boundaries and the (opaque) colors are chosen
+ * from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to produce exact matches.
+ */
+
+static void run_test(const char* testName, skiatest::Reporter*, const sk_sp<GrRenderTargetContext>&,
+                     const SkBitmap& gold, std::function<void(DrawMeshHelper*)> testFn);
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
+    GrContext* const context = ctxInfo.grContext();
+
+    sk_sp<GrRenderTargetContext> rtc(
+        context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kImageWidth, kImageHeight,
+                                                 kRGBA_8888_GrPixelConfig, nullptr));
+    if (!rtc) {
+        ERRORF(reporter, "could not create render target context.");
+        return;
+    }
+
+    SkTArray<Box> boxes;
+    SkTArray<std::array<Box, 4>> vertexData;
+    SkBitmap gold;
+
+    // ---- setup ----------
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc);
+    gold.allocN32Pixels(kImageWidth, kImageHeight);
+
+    SkCanvas goldCanvas(gold);
+
+    for (int y = 0; y < kBoxCountY; ++y) {
+        for (int x = 0; x < kBoxCountX; ++x) {
+            int c = y + x;
+            int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
+
+            const Box box = boxes.push_back() = {
+                float(x * kBoxSize),
+                float(y * kBoxSize),
+                GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
+            };
+
+            std::array<Box, 4>& boxVertices = vertexData.push_back();
+            for (int i = 0; i < 4; ++i) {
+                boxVertices[i] = {
+                    box.fX + (i/2) * kBoxSize,
+                    box.fY + (i%2) * kBoxSize,
+                    box.fColor
+                };
+            }
+
+            paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
+            goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
+        }
+    }
+
+    goldCanvas.flush();
+
+    // ---- tests ----------
+
+#define VALIDATE(buff) \
+    if (!buff) { \
+        ERRORF(reporter, #buff " is null."); \
+        return; \
+    }
+
+    run_test("setNonIndexedNonInstanced", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+        SkTArray<Box> expandedVertexData;
+        for (int i = 0; i < kBoxCount; ++i) {
+            for (int j = 0; j < 6; ++j) {
+                expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
+            }
+        }
+
+        // Draw boxes one line at a time to exercise base vertex.
+        auto vbuff = helper->makeVertexBuffer(expandedVertexData);
+        VALIDATE(vbuff);
+        for (int y = 0; y < kBoxCountY; ++y) {
+            GrMesh mesh(kTriangles_GrPrimitiveType);
+            mesh.setNonIndexedNonInstanced(kBoxCountX * 6);
+            mesh.setVertexData(vbuff.get(), y * kBoxCountX * 6);
+            helper->drawMesh(mesh);
+        }
+    });
+
+    run_test("setIndexed", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+        auto ibuff = helper->getIndexBuffer();
+        VALIDATE(ibuff);
+        auto vbuff = helper->makeVertexBuffer(vertexData);
+        VALIDATE(vbuff);
+        int baseRepetition = 0;
+        int i = 0;
+
+        // Start at various repetitions within the patterned index buffer to exercise base index.
+        while (i < kBoxCount) {
+            GR_STATIC_ASSERT(kIndexPatternRepeatCount >= 3);
+            int repetitionCount = SkTMin(3 - baseRepetition, kBoxCount - i);
+
+            GrMesh mesh(kTriangles_GrPrimitiveType);
+            mesh.setIndexed(ibuff.get(), repetitionCount * 6, baseRepetition * 6,
+                            baseRepetition * 4, (baseRepetition + repetitionCount) * 4 - 1);
+            mesh.setVertexData(vbuff.get(), (i - baseRepetition) * 4);
+            helper->drawMesh(mesh);
+
+            baseRepetition = (baseRepetition + 1) % 3;
+            i += repetitionCount;
+        }
+    });
+
+    run_test("setIndexedPatterned", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+        auto ibuff = helper->getIndexBuffer();
+        VALIDATE(ibuff);
+        auto vbuff = helper->makeVertexBuffer(vertexData);
+        VALIDATE(vbuff);
+
+        // Draw boxes one line at a time to exercise base vertex. setIndexedPatterned does not
+        // support a base index.
+        for (int y = 0; y < kBoxCountY; ++y) {
+            GrMesh mesh(kTriangles_GrPrimitiveType);
+            mesh.setIndexedPatterned(ibuff.get(), 6, 4, kBoxCountX, kIndexPatternRepeatCount);
+            mesh.setVertexData(vbuff.get(), y * kBoxCountX * 4);
+            helper->drawMesh(mesh);
+        }
+    });
+
+    for (bool indexed : {false, true}) {
+        if (!context->caps()->instanceAttribSupport()) {
+            break;
+        }
+
+        run_test(indexed ? "setIndexedInstanced" : "setInstanced",
+                 reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+            auto idxbuff = indexed ? helper->getIndexBuffer() : nullptr;
+            auto instbuff = helper->makeVertexBuffer(boxes);
+            VALIDATE(instbuff);
+            auto vbuff = helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
+            VALIDATE(vbuff);
+            auto vbuff2 = helper->makeVertexBuffer( // for testing base vertex.
+                              std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
+            VALIDATE(vbuff2);
+
+            // Draw boxes one line at a time to exercise base instance, base vertex, and null vertex
+            // buffer. setIndexedInstanced intentionally does not support a base index.
+            for (int y = 0; y < kBoxCountY; ++y) {
+                GrMesh mesh(indexed ? kTriangles_GrPrimitiveType : kTriangleStrip_GrPrimitiveType);
+                if (indexed) {
+                    VALIDATE(idxbuff);
+                    mesh.setIndexedInstanced(idxbuff.get(), 6,
+                                             instbuff.get(), kBoxCountX, y * kBoxCountX);
+                } else {
+                    mesh.setInstanced(instbuff.get(), kBoxCountX, y * kBoxCountX, 4);
+                }
+                switch (y % 3) {
+                    case 0:
+                        if (context->caps()->shaderCaps()->vertexIDSupport()) {
+                            if (y % 2) {
+                                // We don't need this call because it's the initial state of GrMesh.
+                                mesh.setVertexData(nullptr);
+                            }
+                            break;
+                        }
+                        // Fallthru.
+                    case 1:
+                        mesh.setVertexData(vbuff.get());
+                        break;
+                    case 2:
+                        mesh.setVertexData(vbuff2.get(), 2);
+                        break;
+                }
+                helper->drawMesh(mesh);
+            }
+        });
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GrMeshTestOp : public GrDrawOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    GrMeshTestOp(std::function<void(DrawMeshHelper*)> testFn)
+        : INHERITED(ClassID())
+        , fTestFn(testFn) {
+        this->setBounds(SkRect::MakeIWH(kImageWidth, kImageHeight),
+                        HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+private:
+    const char* name() const override { return "GrMeshTestOp"; }
+    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
+    bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override { return false; }
+    bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
+    void onPrepare(GrOpFlushState*) override {}
+    void onExecute(GrOpFlushState* state) override {
+        DrawMeshHelper helper(state);
+        fTestFn(&helper);
+    }
+
+    std::function<void(DrawMeshHelper*)> fTestFn;
+
+    typedef GrDrawOp INHERITED;
+};
+
+class GrMeshTestProcessor : public GrGeometryProcessor {
+public:
+    GrMeshTestProcessor(bool instanced, bool hasVertexBuffer)
+        : fInstanceLocation(nullptr)
+        , fVertex(nullptr)
+        , fColor(nullptr) {
+        if (instanced) {
+            fInstanceLocation = &this->addInstanceAttrib("location", kVec2f_GrVertexAttribType);
+            if (hasVertexBuffer) {
+                fVertex = &this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType);
+            }
+            fColor = &this->addInstanceAttrib("color", kVec4ub_GrVertexAttribType);
+        } else {
+            fVertex = &this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType);
+            fColor = &this->addVertexAttrib("color", kVec4ub_GrVertexAttribType);
+        }
+        this->initClassID<GrMeshTestProcessor>();
+    }
+
+    const char* name() const override { return "GrMeshTest Processor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
+        b->add32(SkToBool(fInstanceLocation));
+        b->add32(SkToBool(fVertex));
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+protected:
+    const Attribute* fInstanceLocation;
+    const Attribute* fVertex;
+    const Attribute* fColor;
+
+    friend class GLSLMeshTestProcessor;
+    typedef GrGeometryProcessor INHERITED;
+};
+
+class GLSLMeshTestProcessor : public GrGLSLGeometryProcessor {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+                 FPCoordTransformIter&& transformIter) final {}
+
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
+        const GrMeshTestProcessor& mp = args.fGP.cast<GrMeshTestProcessor>();
+
+        GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+        varyingHandler->emitAttributes(mp);
+        varyingHandler->addPassThroughAttribute(mp.fColor, args.fOutputColor);
+
+        GrGLSLVertexBuilder* v = args.fVertBuilder;
+        if (!mp.fInstanceLocation) {
+            v->codeAppendf("vec2 vertex = %s;", mp.fVertex->fName);
+        } else {
+            if (mp.fVertex) {
+                v->codeAppendf("vec2 offset = %s;", mp.fVertex->fName);
+            } else {
+                v->codeAppend ("vec2 offset = vec2(sk_VertexID / 2, sk_VertexID % 2);");
+            }
+            v->codeAppendf("vec2 vertex = %s + offset * %i;",
+                           mp.fInstanceLocation->fName, kBoxSize);
+        }
+        gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex");
+
+        GrGLSLPPFragmentBuilder* f = args.fFragBuilder;
+        f->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
+    }
+};
+
+GrGLSLPrimitiveProcessor* GrMeshTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
+    return new GLSLMeshTestProcessor;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
+    return sk_sp<const GrBuffer>(
+        fState->resourceProvider()->createBuffer(
+            count * sizeof(T), kVertex_GrBufferType, kDynamic_GrAccessPattern,
+            GrResourceProvider::kNoPendingIO_Flag |
+            GrResourceProvider::kRequireGpuMemory_Flag, data));
+}
+
+sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
+    GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
+    return sk_sp<const GrBuffer>(
+        fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
+            kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey));
+}
+
+void DrawMeshHelper::drawMesh(const GrMesh& mesh) {
+    GrRenderTarget* rt = fState->drawOpArgs().fRenderTarget;
+    GrPipeline pipeline(rt, SkBlendMode::kSrc);
+    GrMeshTestProcessor mtp(mesh.isInstanced(), mesh.hasVertexData());
+    fState->commandBuffer()->draw(pipeline, mtp, &mesh, 1,
+                                  SkRect::MakeIWH(kImageWidth, kImageHeight));
+}
+
+static void run_test(const char* testName, skiatest::Reporter* reporter,
+                     const sk_sp<GrRenderTargetContext>& rtc, const SkBitmap& gold,
+                     std::function<void(DrawMeshHelper*)> testFn) {
+    const int w = gold.width(), h = gold.height(), rowBytes = gold.rowBytes();
+    const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
+    if (h != rtc->height() || w != rtc->width()) {
+        ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
+        return;
+    }
+    if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
+        ERRORF(reporter, "unexpected row bytes in gold image.", testName);
+        return;
+    }
+
+    SkAutoSTMalloc<kImageHeight * kImageWidth, uint32_t> resultPx(h * rowBytes);
+    rtc->clear(nullptr, 0xbaaaaaad, true);
+    rtc->priv().testingOnly_addDrawOp(skstd::make_unique<GrMeshTestOp>(testFn));
+    rtc->readPixels(gold.info(), resultPx, rowBytes, 0, 0, 0);
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint32_t expected = goldPx[y * kImageWidth + x];
+            uint32_t actual = resultPx[y * kImageWidth + x];
+            if (expected != actual) {
+                ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
+                       testName, x, y, actual, expected);
+                return;
+            }
+        }
+    }
+}
+
+#endif
diff --git a/tests/GrPorterDuffTest.cpp b/tests/GrPorterDuffTest.cpp
index ef21701..78497d2 100644
--- a/tests/GrPorterDuffTest.cpp
+++ b/tests/GrPorterDuffTest.cpp
@@ -11,8 +11,10 @@
 
 #include "GrContextFactory.h"
 #include "GrContextOptions.h"
+#include "GrContextPriv.h"
 #include "GrGpu.h"
 #include "GrResourceProvider.h"
+#include "GrTest.h"
 #include "GrXferProcessor.h"
 #include "effects/GrPorterDuffXferProcessor.h"
 #include "gl/GrGLCaps.h"
@@ -21,7 +23,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
-static void test_color_unknown_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_not_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
 static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
 static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
 static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
@@ -35,7 +37,7 @@
     }
 
     test_color_unknown_with_coverage(reporter, caps);
-    test_color_unknown_no_coverage(reporter, caps);
+    test_color_not_opaque_no_coverage(reporter, caps);
     test_color_opaque_with_coverage(reporter, caps);
     test_color_opaque_no_coverage(reporter, caps);
     test_lcd_coverage(reporter, caps);
@@ -54,34 +56,52 @@
     kISAModulate_OutputType,
     kISCModulate_OutputType
 };
+static const int kInvalid_OutputType = -1;
 
-enum {
-    kNone_OptFlags                    = GrXferProcessor::kNone_OptFlags,
-    kIgnoreColor_OptFlag              = GrXferProcessor::kIgnoreColor_OptFlag,
-    kCanTweakAlphaForCoverage_OptFlag = GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag
-};
+static GrProcessorSet::Analysis do_analysis(const GrXPFactory* xpf,
+                                            const GrProcessorAnalysisColor& colorInput,
+                                            GrProcessorAnalysisCoverage coverageInput,
+                                            const GrCaps& caps) {
+    GrPaint paint;
+    paint.setXPFactory(xpf);
+    GrProcessorSet procs(std::move(paint));
+    GrColor overrideColor;
+    GrProcessorSet::Analysis analysis =
+            procs.finalize(colorInput, coverageInput, nullptr, false, caps, &overrideColor);
+    return analysis;
+}
 
 class GrPorterDuffTest {
 public:
     struct XPInfo {
         XPInfo(skiatest::Reporter* reporter, SkBlendMode xfermode, const GrCaps& caps,
-               const GrProcessorSet::FragmentProcessorAnalysis& analysis) {
+               GrProcessorAnalysisColor inputColor, GrProcessorAnalysisCoverage inputCoverage) {
             const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode);
-            // The GrXPFactory query assumes no coverage.
-            fCanCombineOverlappedStencilAndCover =
-                    !analysis.hasCoverage() && GrXPFactory::CanCombineOverlappedStencilAndCover(
-                                                       xpf, analysis.isOutputColorOpaque());
-            sk_sp<GrXferProcessor> xp(xpf->createXferProcessor(analysis, false, nullptr, caps));
-            TEST_ASSERT(!GrXPFactory::WillNeedDstTexture(xpf, caps, analysis));
-            fOptFlags = xp->getOptimizations(analysis);
+
+            bool isLCD = GrProcessorAnalysisCoverage::kLCD == inputCoverage;
+
+            GrProcessorSet::Analysis analysis = do_analysis(xpf, inputColor, inputCoverage, caps);
+            fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha();
+            fCanCombineOverlappedStencilAndCover = analysis.canCombineOverlappedStencilAndCover();
+            fIgnoresInputColor = analysis.inputColorIsIgnored();
+            sk_sp<const GrXferProcessor> xp(
+                    GrXPFactory::MakeXferProcessor(xpf, inputColor, inputCoverage, false, caps));
+            TEST_ASSERT(!analysis.requiresDstTexture() ||
+                        (isLCD &&
+                         !caps.shaderCaps()->dstReadInShaderSupport() &&
+                         (SkBlendMode::kSrcOver != xfermode ||
+                          !inputColor.isOpaque())));
             GetXPOutputTypes(xp.get(), &fPrimaryOutputType, &fSecondaryOutputType);
             xp->getBlendInfo(&fBlendInfo);
-            TEST_ASSERT(!xp->willReadDstColor());
+            TEST_ASSERT(!xp->willReadDstColor() ||
+                        (isLCD && (SkBlendMode::kSrcOver != xfermode ||
+                                   !inputColor.isOpaque())));
             TEST_ASSERT(xp->hasSecondaryOutput() == GrBlendCoeffRefsSrc2(fBlendInfo.fDstBlend));
         }
 
         bool fCanCombineOverlappedStencilAndCover;
-        int fOptFlags;
+        bool fCompatibleWithCoverageAsAlpha;
+        bool fIgnoresInputColor;
         int fPrimaryOutputType;
         int fSecondaryOutputType;
         GrXferProcessor::BlendInfo fBlendInfo;
@@ -93,50 +113,50 @@
 };
 
 static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
-    GrProcessorSet::FragmentProcessorAnalysis analysis(GrPipelineAnalysisColor(),
-                                                       GrPipelineAnalysisCoverage::kLCD, caps);
-    SkASSERT(!analysis.isOutputColorOpaque());
-    SkASSERT(!analysis.hasKnownOutputColor());
-    SkASSERT(analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kLCD);
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kLCD;
 
     for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
         SkBlendMode xfermode = static_cast<SkBlendMode>(m);
-        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, analysis);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
         switch (xfermode) {
             case SkBlendMode::kClear:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
-                TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
-                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kSrc:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
                 TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kDst:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
-                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
-                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kSrcOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kSAModulate_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -146,112 +166,123 @@
                 break;
             case SkBlendMode::kDstOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kSrcIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kDstIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kISAModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
-                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kSrcOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kDstOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kSAModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kSrcATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kSAModulate_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kDstATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kISAModulate_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kXor:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kSAModulate_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kPlus:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
                 TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kModulate:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
-                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
-                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kScreen:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
-                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
-                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
                 TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             default:
@@ -261,20 +292,17 @@
     }
 }
 static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
-    GrProcessorSet::FragmentProcessorAnalysis analysis(
-            GrPipelineAnalysisColor(), GrPipelineAnalysisCoverage::kSingleChannel, caps);
-
-    SkASSERT(!analysis.isOutputColorOpaque());
-    SkASSERT(!analysis.hasKnownOutputColor());
-    SkASSERT(analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kSingleChannel);
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kNo;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kSingleChannel;
 
     for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
         SkBlendMode xfermode = static_cast<SkBlendMode>(m);
-        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, analysis);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
         switch (xfermode) {
             case SkBlendMode::kClear:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -284,7 +312,8 @@
                 break;
             case SkBlendMode::kSrc:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -294,8 +323,8 @@
                 break;
             case SkBlendMode::kDst:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -305,7 +334,8 @@
                 break;
             case SkBlendMode::kSrcOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -315,7 +345,8 @@
                 break;
             case SkBlendMode::kDstOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -325,7 +356,8 @@
                 break;
             case SkBlendMode::kSrcIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -335,7 +367,8 @@
                 break;
             case SkBlendMode::kDstIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kISAModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -345,7 +378,8 @@
                 break;
             case SkBlendMode::kSrcOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -355,7 +389,8 @@
                 break;
             case SkBlendMode::kDstOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -365,7 +400,8 @@
                 break;
             case SkBlendMode::kSrcATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -375,7 +411,8 @@
                 break;
             case SkBlendMode::kDstATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kISAModulate_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -385,7 +422,8 @@
                 break;
             case SkBlendMode::kXor:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -395,7 +433,8 @@
                 break;
             case SkBlendMode::kPlus:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -405,7 +444,8 @@
                 break;
             case SkBlendMode::kModulate:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -415,7 +455,8 @@
                 break;
             case SkBlendMode::kScreen:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -430,21 +471,18 @@
     }
 }
 
-static void test_color_unknown_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
-    GrProcessorSet::FragmentProcessorAnalysis analysis(GrColorPackRGBA(229, 0, 154, 240),
-                                                       GrPipelineAnalysisCoverage::kNone, caps);
-
-    SkASSERT(!analysis.isOutputColorOpaque());
-    SkASSERT(analysis.hasKnownOutputColor());
-    SkASSERT(!analysis.hasCoverage());
+static void test_color_not_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+    GrProcessorAnalysisColor inputColor(GrColorPackRGBA(229, 0, 154, 240));
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kNone;
 
     for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
         SkBlendMode xfermode = static_cast<SkBlendMode>(m);
-        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, analysis);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
         switch (xfermode) {
             case SkBlendMode::kClear:
                 TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kIgnoreColor_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -454,7 +492,8 @@
                 break;
             case SkBlendMode::kSrc:
                 TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -464,8 +503,8 @@
                 break;
             case SkBlendMode::kDst:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -475,7 +514,8 @@
                 break;
             case SkBlendMode::kSrcOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -485,7 +525,8 @@
                 break;
             case SkBlendMode::kDstOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -495,7 +536,8 @@
                 break;
             case SkBlendMode::kSrcIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -505,7 +547,8 @@
                 break;
             case SkBlendMode::kDstIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -515,7 +558,8 @@
                 break;
             case SkBlendMode::kSrcOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -525,7 +569,8 @@
                 break;
             case SkBlendMode::kDstOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -535,7 +580,8 @@
                 break;
             case SkBlendMode::kSrcATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -545,7 +591,8 @@
                 break;
             case SkBlendMode::kDstATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -555,7 +602,8 @@
                 break;
             case SkBlendMode::kXor:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -565,7 +613,8 @@
                 break;
             case SkBlendMode::kPlus:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -575,7 +624,8 @@
                 break;
             case SkBlendMode::kModulate:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -585,7 +635,8 @@
                 break;
             case SkBlendMode::kScreen:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -601,21 +652,17 @@
 }
 
 static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
-    GrProcessorSet::FragmentProcessorAnalysis analysis(GrPipelineAnalysisColor::Opaque::kYes,
-                                                       GrPipelineAnalysisCoverage::kSingleChannel,
-                                                       caps);
-
-    SkASSERT(analysis.isOutputColorOpaque());
-    SkASSERT(!analysis.hasKnownOutputColor());
-    SkASSERT(analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kSingleChannel);
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kSingleChannel;
 
     for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
         SkBlendMode xfermode = static_cast<SkBlendMode>(m);
-        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, analysis);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
         switch (xfermode) {
             case SkBlendMode::kClear:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -625,7 +672,8 @@
                 break;
             case SkBlendMode::kSrc:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -635,8 +683,8 @@
                 break;
             case SkBlendMode::kDst:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -646,7 +694,8 @@
                 break;
             case SkBlendMode::kSrcOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -656,7 +705,8 @@
                 break;
             case SkBlendMode::kDstOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -666,7 +716,8 @@
                 break;
             case SkBlendMode::kSrcIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -676,8 +727,8 @@
                 break;
             case SkBlendMode::kDstIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -687,7 +738,8 @@
                 break;
             case SkBlendMode::kSrcOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -697,7 +749,8 @@
                 break;
             case SkBlendMode::kDstOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -707,7 +760,8 @@
                 break;
             case SkBlendMode::kSrcATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -717,7 +771,8 @@
                 break;
             case SkBlendMode::kDstATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -727,7 +782,8 @@
                 break;
             case SkBlendMode::kXor:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -737,7 +793,8 @@
                 break;
             case SkBlendMode::kPlus:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -747,7 +804,7 @@
                 break;
             case SkBlendMode::kModulate:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kNone_OptFlags) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
                 TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -757,7 +814,8 @@
                 break;
             case SkBlendMode::kScreen:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -773,21 +831,18 @@
 }
 
 static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
-    GrProcessorSet::FragmentProcessorAnalysis analysis(GrPipelineAnalysisColor::Opaque::kYes,
-                                                       GrPipelineAnalysisCoverage::kNone, caps);
-
-    SkASSERT(analysis.isOutputColorOpaque());
-    SkASSERT(!analysis.hasKnownOutputColor());
-    SkASSERT(!analysis.hasCoverage());
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kNone;
 
     for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
         SkBlendMode xfermode = static_cast<SkBlendMode>(m);
-        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, analysis);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
 
         switch (xfermode) {
             case SkBlendMode::kClear:
                 TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kIgnoreColor_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -797,7 +852,8 @@
                 break;
             case SkBlendMode::kSrc:
                 TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -807,8 +863,8 @@
                 break;
             case SkBlendMode::kDst:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -817,18 +873,21 @@
                 TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kSrcOver:
-                TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                // We don't specialize opaque src-over. See note in GrPorterDuffXferProcessor.cpp
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
                 TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
-                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
                 TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
                 break;
             case SkBlendMode::kDstOver:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -838,7 +897,8 @@
                 break;
             case SkBlendMode::kSrcIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -848,8 +908,8 @@
                 break;
             case SkBlendMode::kDstIn:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT((kIgnoreColor_OptFlag |
-                             kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -859,7 +919,8 @@
                 break;
             case SkBlendMode::kSrcOut:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -869,7 +930,8 @@
                 break;
             case SkBlendMode::kDstOut:
                 TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kIgnoreColor_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -879,7 +941,8 @@
                 break;
             case SkBlendMode::kSrcATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -889,7 +952,8 @@
                 break;
             case SkBlendMode::kDstATop:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -899,7 +963,8 @@
                 break;
             case SkBlendMode::kXor:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -909,7 +974,8 @@
                 break;
             case SkBlendMode::kPlus:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -919,7 +985,8 @@
                 break;
             case SkBlendMode::kModulate:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kNone_OptFlags == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -929,7 +996,8 @@
                 break;
             case SkBlendMode::kScreen:
                 TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
-                TEST_ASSERT(kCanTweakAlphaForCoverage_OptFlag == xpi.fOptFlags);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
                 TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
                 TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
                 TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
@@ -945,47 +1013,34 @@
 }
 
 static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const GrCaps& caps) {
-    class TestLCDCoverageOp : public GrMeshDrawOp {
-    public:
-        DEFINE_OP_CLASS_ID
-
-        TestLCDCoverageOp() : INHERITED(ClassID()) {}
-
-        const char* name() const override { return "Test LCD Text Op"; }
-
-    private:
-        void getFragmentProcessorAnalysisInputs(
-                GrPipelineAnalysisColor* color,
-                GrPipelineAnalysisCoverage* coverage) const override {
-            color->setToConstant(GrColorPackRGBA(123, 45, 67, 221));
-            *coverage = GrPipelineAnalysisCoverage::kLCD;
-        }
-
-        void applyPipelineOptimizations(const GrPipelineOptimizations&) override {}
-        bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
-        void onPrepareDraws(Target*) const override {}
-
-        typedef GrMeshDrawOp INHERITED;
-    } testLCDCoverageOp;
-
-    GrProcessorSet::FragmentProcessorAnalysis analysis;
-    testLCDCoverageOp.analyzeProcessors(&analysis, GrProcessorSet(GrPaint()), nullptr, caps);
-
-    SkASSERT(analysis.hasKnownOutputColor());
-    SkASSERT(analysis.outputCoverageType() == GrPipelineAnalysisCoverage::kLCD);
-
     const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver);
-    TEST_ASSERT(!GrXPFactory::WillNeedDstTexture(xpf, caps, analysis));
+    GrProcessorAnalysisColor color = GrColorPackRGBA(123, 45, 67, 255);
+    GrProcessorAnalysisCoverage coverage = GrProcessorAnalysisCoverage::kLCD;
+    TEST_ASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
+                  GrXPFactory::AnalysisProperties::kRequiresDstTexture));
+    sk_sp<const GrXferProcessor> xp_opaque(
+            GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps));
+    if (!xp_opaque) {
+        ERRORF(reporter, "Failed to create an XP with LCD coverage.");
+        return;
+    }
 
-    sk_sp<GrXferProcessor> xp(xpf->createXferProcessor(analysis, false, nullptr, caps));
+    GrXferProcessor::BlendInfo blendInfo;
+    xp_opaque->getBlendInfo(&blendInfo);
+    TEST_ASSERT(blendInfo.fWriteColor);
+
+    // Test with non-opaque alpha
+    color = GrColorPackRGBA(123, 45, 67, 221);
+    coverage = GrProcessorAnalysisCoverage::kLCD;
+    TEST_ASSERT(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
+                GrXPFactory::AnalysisProperties::kRequiresDstTexture);
+    sk_sp<const GrXferProcessor> xp(
+            GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps));
     if (!xp) {
         ERRORF(reporter, "Failed to create an XP with LCD coverage.");
         return;
     }
 
-    xp->getOptimizations(analysis);
-
-    GrXferProcessor::BlendInfo blendInfo;
     xp->getBlendInfo(&blendInfo);
     TEST_ASSERT(blendInfo.fWriteColor);
 }
@@ -1006,44 +1061,43 @@
         return;
     }
 
-    GrBackendObject backendTex =
+    GrBackendObject backendTexHandle =
         ctx->getGpu()->createTestingOnlyBackendTexture(nullptr, 100, 100, kRGBA_8888_GrPixelConfig);
-    GrBackendTextureDesc fakeDesc;
-    fakeDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    fakeDesc.fWidth = fakeDesc.fHeight = 100;
-    fakeDesc.fTextureHandle = backendTex;
-    GrXferProcessor::DstTexture fakeDstTexture;
-    fakeDstTexture.setTexture(
-        ctx->resourceProvider()->wrapBackendTexture(fakeDesc, kBorrow_GrWrapOwnership));
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(ctx->contextPriv().getBackend(),
+                                                               100,
+                                                               100,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendTexHandle);
 
-    static const GrPipelineAnalysisColor colorInputs[] = {
-            GrPipelineAnalysisColor::Opaque::kNo, GrPipelineAnalysisColor::Opaque::kYes,
-            GrPipelineAnalysisColor(GrColorPackRGBA(0, 82, 17, 100)),
-            GrPipelineAnalysisColor(GrColorPackRGBA(0, 82, 17, 255))};
+    GrXferProcessor::DstProxy fakeDstProxy;
+    {
+        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrappedBackend(ctx, backendTex,
+                                                                         kTopLeft_GrSurfaceOrigin);
+        fakeDstProxy.setProxy(std::move(proxy));
+    }
+
+    static const GrProcessorAnalysisColor colorInputs[] = {
+            GrProcessorAnalysisColor::Opaque::kNo, GrProcessorAnalysisColor::Opaque::kYes,
+            GrProcessorAnalysisColor(GrColorPackRGBA(0, 82, 17, 100)),
+            GrProcessorAnalysisColor(GrColorPackRGBA(0, 82, 17, 255))};
 
     for (const auto& colorInput : colorInputs) {
-        GrProcessorSet::FragmentProcessorAnalysis analysis;
-        for (GrPipelineAnalysisCoverage coverageType :
-             {GrPipelineAnalysisCoverage::kSingleChannel, GrPipelineAnalysisCoverage::kNone}) {
-            analysis = GrProcessorSet::FragmentProcessorAnalysis(colorInput, coverageType, caps);
+        for (GrProcessorAnalysisCoverage coverageType :
+             {GrProcessorAnalysisCoverage::kSingleChannel, GrProcessorAnalysisCoverage::kNone}) {
             for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
                 SkBlendMode xfermode = static_cast<SkBlendMode>(m);
                 const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode);
-                GrXferProcessor::DstTexture* dstTexture =
-                        GrXPFactory::WillNeedDstTexture(xpf, caps, analysis) ? &fakeDstTexture : 0;
-                sk_sp<GrXferProcessor> xp(
-                        xpf->createXferProcessor(analysis, false, dstTexture, caps));
+                sk_sp<const GrXferProcessor> xp(
+                        GrXPFactory::MakeXferProcessor(xpf, colorInput, coverageType, false, caps));
                 if (!xp) {
                     ERRORF(reporter, "Failed to create an XP without dual source blending.");
                     return;
                 }
                 TEST_ASSERT(!xp->hasSecondaryOutput());
-                xp->getOptimizations(analysis);
-                TEST_ASSERT(!xp->hasSecondaryOutput());
             }
         }
     }
-    ctx->getGpu()->deleteTestingOnlyBackendTexture(backendTex);
+    ctx->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
 }
 
 #endif
diff --git a/tests/GrSKSLPrettyPrintTest.cpp b/tests/GrSKSLPrettyPrintTest.cpp
new file mode 100644
index 0000000..ec5dddb
--- /dev/null
+++ b/tests/GrSKSLPrettyPrintTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrSKSLPrettyPrint.h"
+
+#define ASSERT(x) REPORTER_ASSERT(r, x)
+
+const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;"
+                     "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}"
+                     "void main()");
+const SkString input2("{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){"
+                     "doingstuff"
+                     ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this"
+                     " comment\n is");
+const SkString input3(" dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n"
+                     "//a comment");
+const SkString input4("breaking in comment");
+const SkString input5("continuing the comment");
+const SkString input6("\n}}a; little ;  love; for   ; leading;  spaces;} "
+                     "an struct = { int a; int b; };"
+                     "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }");
+
+const SkString output1(
+        "   1\t#this is not a realshader\n"
+        "   2\tvec4 some stuff;\n"
+        "   3\toutside of a function;\n"
+        "   4\tint i(int b, int c) \n"
+        "   5\t{\n"
+        "   6\t\t{\n"
+        "   7\t\t\tsome stuff;\n"
+        "   8\t\t}\n"
+        "   9\t\tfake block;\n"
+        "  10\t\t//comments\n"
+        "  11\t\treturn i;\n"
+        "  12\t}\n"
+        "  13\tvoid main()\n"
+        "  14\t{\n"
+        "  15\t\tnowin a function;\n"
+        "  16\t\t{\n"
+        "  17\t\t\tindenting;\n"
+        "  18\t\t\t{\n"
+        "  19\t\t\t\tabit more;\n"
+        "  20\t\t\t\tdreadedfor((;;)(;)((;;);))\n"
+        "  21\t\t\t\t{\n"
+        "  22\t\t\t\t\tdoingstuff;\n"
+        "  23\t\t\t\t\tfor(;;;)\n"
+        "  24\t\t\t\t\t{\n"
+        "  25\t\t\t\t\t\tand more stufff;\n"
+        "  26\t\t\t\t\t\tmixed garbage/*using this comment\n"
+        "  27\t\t\t\t\t\t is dangerous\n"
+        "  28\t\t\t\t\t\tdo so at your own\n"
+        "  29\t\t\t\t\t\t risk*/;\n"
+        "  30\t\t\t\t\t\t//a commentbreaking in commentcontinuing the comment\n"
+        "  31\t\t\t\t\t}\n"
+        "  32\t\t\t\t}\n"
+        "  33\t\t\t\ta;\n"
+        "  34\t\t\t\tlittle ;\n"
+        "  35\t\t\t\tlove;\n"
+        "  36\t\t\t\tfor   ;\n"
+        "  37\t\t\t\tleading;\n"
+        "  38\t\t\t\tspaces;\n"
+        "  39\t\t\t}\n"
+        "  40\t\t\tan struct = \n"
+        "  41\t\t\t{\n"
+        "  42\t\t\t\tint a;\n"
+        "  43\t\t\t\tint b;\n"
+        "  44\t\t\t}\n"
+        "  45\t\t\t;\n"
+        "  46\t\t\tint[5] arr = int[5](1,2,3,4,5);\n"
+        "  47\t\t}\n"
+        "  48\t\tsome code at the bottom;\n"
+        "  49\t\tfor(;;) \n"
+        "  50\t\t{\n"
+        "  51\t\t}\n"
+        "  52\t}\n"
+        "  53\t");
+
+const SkString neg1("{;;{{{{;;;{{{{{{{{{{{");
+const SkString neg2("###\n##\n#####(((((((((((((unbalanced verything;;;");
+const SkString neg3("}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
+        ";;;;;;/////");
+
+DEF_TEST(GrSKSLPrettyPrint, r) {
+    SkTArray<const char*> testStr;
+    SkTArray<int> lengths;
+    testStr.push_back(input1.c_str());
+    lengths.push_back((int)input1.size());
+    testStr.push_back(input2.c_str());
+    lengths.push_back((int)input2.size());
+    testStr.push_back(input3.c_str());
+    lengths.push_back((int)input3.size());
+    testStr.push_back(input4.c_str());
+    lengths.push_back((int)input4.size());
+    testStr.push_back(input5.c_str());
+    lengths.push_back((int)input5.size());
+    testStr.push_back(input6.c_str());
+    lengths.push_back((int)input6.size());
+
+    SkString test = GrSKSLPrettyPrint::PrettyPrint(testStr.begin(), lengths.begin(),
+                                                   testStr.count(), true);
+    ASSERT(output1 == test);
+
+    testStr.reset();
+    lengths.reset();
+    testStr.push_back(neg1.c_str());
+    lengths.push_back((int)neg1.size());
+    testStr.push_back(neg2.c_str());
+    lengths.push_back((int)neg2.size());
+    testStr.push_back(neg3.c_str());
+    lengths.push_back((int)neg3.size());
+
+    // Just test we don't crash with garbage input
+    ASSERT(GrSKSLPrettyPrint::PrettyPrint(testStr.begin(), lengths.begin(), 1,
+                                          true).c_str() != nullptr);
+}
+
+#endif
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index d98d0d0..85fc835 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -10,11 +10,14 @@
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrGpu.h"
 #include "GrRenderTarget.h"
 #include "GrResourceProvider.h"
+#include "GrTest.h"
 #include "GrTexture.h"
 #include "GrSurfacePriv.h"
+#include "SkMipMap.h"
 #include "Test.h"
 
 // Tests that GrSurface::asTexture(), GrSurface::asRenderTarget(), and static upcasting of texture
@@ -27,11 +30,10 @@
     desc.fWidth = 256;
     desc.fHeight = 256;
     desc.fSampleCnt = 0;
-    GrSurface* texRT1 = context->resourceProvider()->createTexture(
-            desc, SkBudgeted::kNo, nullptr, 0);
+    sk_sp<GrSurface> texRT1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo);
 
-    REPORTER_ASSERT(reporter, texRT1 == texRT1->asRenderTarget());
-    REPORTER_ASSERT(reporter, texRT1 == texRT1->asTexture());
+    REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asRenderTarget());
+    REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asTexture());
     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
                     texRT1->asTexture());
     REPORTER_ASSERT(reporter, texRT1->asRenderTarget() ==
@@ -40,23 +42,23 @@
                     static_cast<GrSurface*>(texRT1->asTexture()));
 
     desc.fFlags = kNone_GrSurfaceFlags;
-    GrSurface* tex1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo, nullptr, 0);
+    sk_sp<GrTexture> tex1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo);
     REPORTER_ASSERT(reporter, nullptr == tex1->asRenderTarget());
-    REPORTER_ASSERT(reporter, tex1 == tex1->asTexture());
-    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1) == tex1->asTexture());
+    REPORTER_ASSERT(reporter, tex1.get() == tex1->asTexture());
+    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1.get()) == tex1->asTexture());
 
-    GrBackendObject backendTex = context->getGpu()->createTestingOnlyBackendTexture(
+    GrBackendObject backendTexHandle = context->getGpu()->createTestingOnlyBackendTexture(
         nullptr, 256, 256, kRGBA_8888_GrPixelConfig);
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               256,
+                                                               256,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendTexHandle);
 
-    GrBackendTextureDesc backendDesc;
-    backendDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    backendDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
-    backendDesc.fWidth = 256;
-    backendDesc.fHeight = 256;
-    backendDesc.fSampleCnt = 0;
-    backendDesc.fTextureHandle = backendTex;
     sk_sp<GrSurface> texRT2 = context->resourceProvider()->wrapBackendTexture(
-        backendDesc, kBorrow_GrWrapOwnership);
+        backendTex, kTopLeft_GrSurfaceOrigin, kRenderTarget_GrBackendTextureFlag, 0,
+        kBorrow_GrWrapOwnership);
+
     REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asRenderTarget());
     REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asTexture());
     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
@@ -66,9 +68,177 @@
     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
                     static_cast<GrSurface*>(texRT2->asTexture()));
 
-    texRT1->unref();
-    tex1->unref();
-    context->getGpu()->deleteTestingOnlyBackendTexture(backendTex);
+    context->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
 }
 
+// This test checks that the isConfigTexturable and isConfigRenderable are
+// consistent with createTexture's result.
+DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrResourceProvider* resourceProvider = context->resourceProvider();
+    const GrCaps* caps = context->caps();
+
+    GrPixelConfig configs[] = {
+        kUnknown_GrPixelConfig,
+        kAlpha_8_GrPixelConfig,
+        kGray_8_GrPixelConfig,
+        kRGB_565_GrPixelConfig,
+        kRGBA_4444_GrPixelConfig,
+        kRGBA_8888_GrPixelConfig,
+        kBGRA_8888_GrPixelConfig,
+        kSRGBA_8888_GrPixelConfig,
+        kSBGRA_8888_GrPixelConfig,
+        kRGBA_8888_sint_GrPixelConfig,
+        kRGBA_float_GrPixelConfig,
+        kRG_float_GrPixelConfig,
+        kAlpha_half_GrPixelConfig,
+        kRGBA_half_GrPixelConfig,
+    };
+    SkASSERT(kGrPixelConfigCnt == SK_ARRAY_COUNT(configs));
+
+    GrSurfaceDesc desc;
+    desc.fWidth = 64;
+    desc.fHeight = 64;
+
+    // Enough space for the first mip of our largest pixel config
+    const size_t pixelBufferSize = desc.fWidth * desc.fHeight *
+                                   GrBytesPerPixel(kRGBA_float_GrPixelConfig);
+    std::unique_ptr<char[]> pixelData(new char[pixelBufferSize]);
+    memset(pixelData.get(), 0, pixelBufferSize);
+
+    // We re-use the same mip level objects (with updated pointers and rowBytes) for each config
+    const int levelCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
+    std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[levelCount]);
+
+    for (GrPixelConfig config : configs) {
+        for (GrSurfaceOrigin origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
+            desc.fFlags = kNone_GrSurfaceFlags;
+            desc.fOrigin = origin;
+            desc.fSampleCnt = 0;
+            desc.fConfig = config;
+
+            sk_sp<GrSurface> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
+            REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigTexturable(desc.fConfig));
+
+            size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig);
+            for (int i = 0; i < levelCount; ++i) {
+                texels[i].fPixels = pixelData.get();
+                texels[i].fRowBytes = rowBytes >> i;
+            }
+            sk_sp<GrTextureProxy> proxy = resourceProvider->createMipMappedTexture(
+                desc, SkBudgeted::kNo, texels.get(), levelCount);
+            REPORTER_ASSERT(reporter, SkToBool(proxy.get()) ==
+                            (caps->isConfigTexturable(desc.fConfig) &&
+                             caps->mipMapSupport() &&
+                             !GrPixelConfigIsSint(desc.fConfig)));
+
+            desc.fFlags = kRenderTarget_GrSurfaceFlag;
+            tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
+            REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigRenderable(config, false));
+
+            desc.fSampleCnt = 4;
+            tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
+            REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigRenderable(config, true));
+        }
+    }
+}
+
+#include "GrDrawingManager.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureContext.h"
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(InitialTextureClear, reporter, context_info) {
+    static constexpr int kSize = 100;
+    GrSurfaceDesc desc;
+    desc.fWidth = desc.fHeight = kSize;
+    std::unique_ptr<uint32_t[]> data(new uint32_t[kSize * kSize]);
+    GrContext* context = context_info.grContext();
+    for (int c = 0; c <= kLast_GrPixelConfig; ++c) {
+        desc.fConfig = static_cast<GrPixelConfig>(c);
+        if (!context_info.grContext()->caps()->isConfigTexturable(desc.fConfig)) {
+            continue;
+        }
+        desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
+        for (bool rt : {false, true}) {
+            if (rt && !context->caps()->isConfigRenderable(desc.fConfig, false)) {
+                continue;
+            }
+            desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+            for (bool mipped : {false, true}) {
+                desc.fIsMipMapped = mipped;
+                for (GrSurfaceOrigin origin :
+                     {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
+                    desc.fOrigin = origin;
+                    for (bool approx : {false, true}) {
+                        auto resourceProvider = context->resourceProvider();
+                        // Try directly creating the texture.
+                        // Do this twice in an attempt to hit the cache on the second time through.
+                        for (int i = 0; i < 2; ++i) {
+                            sk_sp<GrTexture> tex;
+                            if (approx) {
+                                tex = sk_sp<GrTexture>(
+                                        resourceProvider->createApproxTexture(desc, 0));
+                            } else {
+                                tex = resourceProvider->createTexture(desc, SkBudgeted::kYes);
+                            }
+                            if (!tex) {
+                                continue;
+                            }
+                            auto proxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
+                            auto texCtx = context->contextPriv().makeWrappedSurfaceContext(
+                                    std::move(proxy), nullptr);
+                            SkImageInfo info = SkImageInfo::Make(
+                                    kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+                            memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
+                            if (texCtx->readPixels(info, data.get(), 0, 0, 0)) {
+                                uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+                                for (int i = 0; i < kSize * kSize; ++i) {
+                                    if (cmp != data.get()[i]) {
+                                        ERRORF(reporter, "Failed on config %d", desc.fConfig);
+                                        break;
+                                    }
+                                }
+                            }
+                            memset(data.get(), 0xBC, kSize * kSize * sizeof(uint32_t));
+                            // Here we overwrite the texture so that the second time through we
+                            // test against recycling without reclearing.
+                            if (0 == i) {
+                                texCtx->writePixels(info, data.get(), 0, 0, 0);
+                            }
+                        }
+                        context->purgeAllUnlockedResources();
+
+                        // Try creating the texture as a deferred proxy.
+                        for (int i = 0; i < 2; ++i) {
+                            auto surfCtx = context->contextPriv().makeDeferredSurfaceContext(
+                                    desc, approx ? SkBackingFit::kApprox : SkBackingFit::kExact,
+                                    SkBudgeted::kYes);
+                            if (!surfCtx) {
+                                continue;
+                            }
+                            SkImageInfo info = SkImageInfo::Make(
+                                    kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+                            memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
+                            if (surfCtx->readPixels(info, data.get(), 0, 0, 0)) {
+                                uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+                                for (int i = 0; i < kSize * kSize; ++i) {
+                                    if (cmp != data.get()[i]) {
+                                        ERRORF(reporter, "Failed on config %d", desc.fConfig);
+                                        break;
+                                    }
+                                }
+                            }
+                            // Here we overwrite the texture so that the second time through we
+                            // test against recycling without reclearing.
+                            if (0 == i) {
+                                surfCtx->writePixels(info, data.get(), 0, 0, 0);
+                            }
+                        }
+                        context->purgeAllUnlockedResources();
+                    }
+                }
+            }
+        }
+    }
+}
 #endif
diff --git a/tests/GradientTest.cpp b/tests/GradientTest.cpp
index e46bfb4..732bfbc 100644
--- a/tests/GradientTest.cpp
+++ b/tests/GradientTest.cpp
@@ -149,7 +149,6 @@
     outBitmap.allocN32Pixels(10, 1);
     SkCanvas canvas(outBitmap);
     canvas.drawPaint(paint);
-    SkAutoLockPixels alp(outBitmap);
     for (int i = 0; i < 10; i++) {
         // The following is commented out because it currently fails
         // Related bug: https://code.google.com/p/skia/issues/detail?id=1098
@@ -481,6 +480,51 @@
     }
 }
 
+static void test_sweep_fuzzer(skiatest::Reporter*) {
+    static const SkColor gColors0[] = { 0x30303030, 0x30303030, 0x30303030 };
+    static const SkScalar   gPos0[] = { -47919293023455565225163489280.0f, 0, 1 };
+    static const SkScalar gMatrix0[9] = {
+        1.12116716e-13f,  0              ,  8.50489682e+16f,
+        4.1917041e-41f ,  3.51369881e-23f, -2.54344271e-26f,
+        9.61111907e+17f, -3.35263808e-29f, -1.35659403e+14f
+    };
+    static const struct {
+        SkPoint            fCenter;
+        const SkColor*     fColors;
+        const SkScalar*    fPos;
+        int                fCount;
+        const SkScalar*    fGlobalMatrix;
+    } gConfigs[] = {
+        {
+            { 0, 0 },
+            gColors0,
+            gPos0,
+            SK_ARRAY_COUNT(gColors0),
+            gMatrix0
+        },
+    };
+
+    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100);
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+
+    for (const auto& config : gConfigs) {
+        paint.setShader(SkGradientShader::MakeSweep(config.fCenter.x(),
+                                                    config.fCenter.y(),
+                                                    config.fColors,
+                                                    config.fPos,
+                                                    config.fCount));
+
+        SkAutoCanvasRestore acr(canvas, false);
+        if (config.fGlobalMatrix) {
+            SkMatrix m;
+            m.set9(config.fGlobalMatrix);
+            canvas->save();
+            canvas->concat(m);
+        }
+        canvas->drawPaint(paint);
+    }
+}
 
 DEF_TEST(Gradient, reporter) {
     TestGradientShaders(reporter);
@@ -494,4 +538,5 @@
     test_clamping_overflow(reporter);
     test_degenerate_linear(reporter);
     test_linear_fuzzer(reporter);
+    test_sweep_fuzzer(reporter);
 }
diff --git a/tests/HighContrastFilterTest.cpp b/tests/HighContrastFilterTest.cpp
index 35b0c9f..7c3fc84 100644
--- a/tests/HighContrastFilterTest.cpp
+++ b/tests/HighContrastFilterTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkHighContrastFilter.h"
 #include "Test.h"
@@ -33,8 +34,6 @@
     paint.setColorFilter(SkHighContrastFilter::Make(config));
     canvasFilter.drawRect(r, paint);
 
-    paintResult.lockPixels();
-    filterResult.lockPixels();
     for (int y = r.top(); y < r.bottom(); ++y) {
         for (int x = r.left(); x < r.right(); ++x) {
             SkColor paintColor = paintResult.getColor(x, y);
@@ -44,8 +43,6 @@
                 paint.getColorFilter()->filterColor(paintColor));
         }
     }
-    paintResult.unlockPixels();
-    filterResult.unlockPixels();
 }
 
 DEF_TEST(HighContrastFilter_SanityCheck, reporter) {
diff --git a/tests/ImageCacheTest.cpp b/tests/ImageCacheTest.cpp
index 6d1e1ac..457df91 100644
--- a/tests/ImageCacheTest.cpp
+++ b/tests/ImageCacheTest.cpp
@@ -121,7 +121,7 @@
         test_cache(reporter, cache, true);
     }
     {
-        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Create(defLimit, nullptr));
+        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(defLimit));
         gPool = pool.get();
         SkResourceCache cache(pool_factory);
         test_cache(reporter, cache, true);
diff --git a/tests/ImageFilterCacheTest.cpp b/tests/ImageFilterCacheTest.cpp
index fe67925..d0c6c5b 100644
--- a/tests/ImageFilterCacheTest.cpp
+++ b/tests/ImageFilterCacheTest.cpp
@@ -179,19 +179,9 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrResourceProvider.h"
-
-static GrTexture* create_texture(GrContext* context) {
-    SkBitmap srcBM = create_bm();
-
-    GrSurfaceDesc desc;
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
-    desc.fFlags  = kNone_GrSurfaceFlags;
-    desc.fWidth  = kFullSize;
-    desc.fHeight = kFullSize;
-
-    return context->resourceProvider()->createTexture(desc, SkBudgeted::kNo, srcBM.getPixels(), 0);
-}
+#include "GrTest.h"
 
 static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider) {
     SkBitmap srcBM = create_bm();
@@ -208,24 +198,29 @@
                                         srcBM.rowBytes());
 }
 
-
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) {
-    sk_sp<GrTexture> srcTexture(create_texture(ctxInfo.grContext()));
-    if (!srcTexture) {
+    GrContext* context = ctxInfo.grContext();
+
+    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider()));
+    if (!srcProxy) {
         return;
     }
 
-    GrBackendTextureDesc backendDesc;
-    backendDesc.fFlags = kNone_GrBackendTextureFlag;
-    backendDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
-    backendDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    backendDesc.fWidth = kFullSize;
-    backendDesc.fHeight = kFullSize;
-    backendDesc.fSampleCnt = 0;
-    backendDesc.fTextureHandle = srcTexture->getTextureHandle();
-    sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(ctxInfo.grContext(),
-                                                     backendDesc,
-                                                     kPremul_SkAlphaType));
+    if (!srcProxy->instantiate(context->resourceProvider())) {
+        return;
+    }
+    GrTexture* tex = srcProxy->priv().peekTexture();
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               kFullSize,
+                                                               kFullSize,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               tex->getTextureHandle());
+    GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
+    sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(context,
+                                                     backendTex,
+                                                     texOrigin,
+                                                     kPremul_SkAlphaType, nullptr));
     if (!srcImage) {
         return;
     }
@@ -234,18 +229,18 @@
     GrBackendObject readBackHandle = srcImage->getTextureHandle(false, &readBackOrigin);
     // TODO: Make it so we can check this (see skbug.com/5019)
 #if 0
-    if (readBackHandle != backendDesc.fTextureHandle) {
+    if (readBackHandle != tex->getTextureHandle()) {
         ERRORF(reporter, "backend mismatch %d %d\n",
-                       (int)readBackHandle, (int)backendDesc.fTextureHandle);
+                       (int)readBackHandle, (int)tex->getTextureHandle());
     }
-    REPORTER_ASSERT(reporter, readBackHandle == backendDesc.fTextureHandle);
+    REPORTER_ASSERT(reporter, readBackHandle == tex->getTextureHandle());
 #else
     REPORTER_ASSERT(reporter, SkToBool(readBackHandle));
 #endif
-    if (readBackOrigin != backendDesc.fOrigin) {
-        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, backendDesc.fOrigin);
+    if (readBackOrigin != texOrigin) {
+        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
     }
-    REPORTER_ASSERT(reporter, readBackOrigin == backendDesc.fOrigin);
+    REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
 
     test_image_backed(reporter, srcImage);
 }
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 79f34f7..d884644 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -63,6 +63,9 @@
         offset->fX = offset->fY = 0;
         return sk_ref_sp<SkSpecialImage>(source);
     }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return sk_ref_sp(const_cast<MatrixTestImageFilter*>(this));
+    }
 
     void flatten(SkWriteBuffer& buffer) const override {
         SkDEBUGFAIL("Should never get here");
@@ -90,6 +93,9 @@
                                         SkIPoint* offset) const override {
         return nullptr;
     }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return nullptr;
+    }
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
@@ -238,9 +244,8 @@
                                                                          cropRect));
         }
         {
-            SkRTreeFactory factory;
             SkPictureRecorder recorder;
-            SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
+            SkCanvas* recordingCanvas = recorder.beginRecording(64, 64);
 
             SkPaint greenPaint;
             greenPaint.setColor(SK_ColorGREEN);
@@ -594,10 +599,6 @@
     REPORTER_ASSERT(reporter, negativeResult1->getROPixels(&negativeResultBM1));
     REPORTER_ASSERT(reporter, negativeResult2->getROPixels(&negativeResultBM2));
 
-    SkAutoLockPixels lockP1(positiveResultBM1);
-    SkAutoLockPixels lockP2(positiveResultBM2);
-    SkAutoLockPixels lockN1(negativeResultBM1);
-    SkAutoLockPixels lockN2(negativeResultBM2);
     for (int y = 0; y < height; y++) {
         int diffs = memcmp(positiveResultBM1.getAddr32(0, y),
                            negativeResultBM1.getAddr32(0, y),
@@ -656,7 +657,6 @@
 
     REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
 
-    SkAutoLockPixels lock(resultBM);
     for (int y = 0; y < resultBM.height(); y++) {
         for (int x = 0; x < resultBM.width(); x++) {
             bool diff = *resultBM.getAddr32(x, y) != SK_ColorGREEN;
@@ -696,7 +696,6 @@
     if (result.get()) {
         SkBitmap resultBM;
         REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
-        SkAutoLockPixels lock(resultBM);
         REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
     }
 }
@@ -739,14 +738,14 @@
             SkScalar ypos = SkIntToScalar(height);
             untiledCanvas.save();
             untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
-            untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
+            untiledCanvas.drawString(text, 0, ypos, paint);
             untiledCanvas.restore();
             for (int y = 0; y < height; y += tileSize) {
                 for (int x = 0; x < width; x += tileSize) {
                     tiledCanvas.save();
                     tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
                     tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
-                    tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
+                    tiledCanvas.drawString(text, 0, ypos, paint);
                     tiledCanvas.restore();
                 }
             }
@@ -1573,7 +1572,6 @@
 
     SkBitmap resultBM;
     REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
-    SkAutoLockPixels lock(resultBM);
     REPORTER_ASSERT(reporter, resultBM.getColor(50, 50) == SK_ColorGREEN);
 }
 
diff --git a/tests/ImageFrom565Bitmap.cpp b/tests/ImageFrom565Bitmap.cpp
index fada9f0..53f65df 100644
--- a/tests/ImageFrom565Bitmap.cpp
+++ b/tests/ImageFrom565Bitmap.cpp
@@ -14,7 +14,6 @@
     SkBitmap bm;
     bm.allocPixels(SkImageInfo::Make(
         5, 7, kRGB_565_SkColorType, kOpaque_SkAlphaType));
-    SkAutoLockPixels autoLockPixels(bm);
     bm.eraseColor(SK_ColorBLACK);
     REPORTER_ASSERT(r, SkImage::MakeFromBitmap(bm) != nullptr);
 }
diff --git a/tests/ImageGeneratorTest.cpp b/tests/ImageGeneratorTest.cpp
index b644596..3d82534 100644
--- a/tests/ImageGeneratorTest.cpp
+++ b/tests/ImageGeneratorTest.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkData.h"
+#include "SkCanvas.h"
 #include "SkGraphics.h"
 #include "SkImageGenerator.h"
 #include "Test.h"
@@ -69,3 +70,42 @@
         test_imagegenerator_factory(reporter);
     }
 }
+
+#include "SkAutoMalloc.h"
+#include "SkPictureRecorder.h"
+
+static sk_sp<SkPicture> make_picture() {
+    SkPictureRecorder recorder;
+    recorder.beginRecording(100, 100)->drawColor(SK_ColorRED);
+    return recorder.finishRecordingAsPicture();
+}
+
+DEF_TEST(PictureImageGenerator, reporter) {
+    const struct {
+        SkColorType fColorType;
+        SkAlphaType fAlphaType;
+        bool        fExpectSuccess;
+    } recs[] = {
+        { kRGBA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_8888_SkColorType == kN32_SkColorType },
+        { kBGRA_8888_SkColorType, kPremul_SkAlphaType, kBGRA_8888_SkColorType == kN32_SkColorType },
+        { kRGBA_F16_SkColorType,  kPremul_SkAlphaType, true },
+
+        { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, false },
+        { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, false },
+        { kRGBA_F16_SkColorType,  kUnpremul_SkAlphaType, false },
+    };
+
+    auto colorspace = SkColorSpace::MakeSRGB();
+    auto picture = make_picture();
+    auto gen = SkImageGenerator::MakeFromPicture({100, 100}, picture, nullptr, nullptr,
+                                                 SkImage::BitDepth::kU8, colorspace);
+
+    // worst case for all requests
+    SkAutoMalloc storage(100 * 100 * SkColorTypeBytesPerPixel(kRGBA_F16_SkColorType));
+
+    for (const auto& rec : recs) {
+        SkImageInfo info = SkImageInfo::Make(100, 100, rec.fColorType, rec.fAlphaType, colorspace);
+        bool success = gen->getPixels(info, storage.get(), info.minRowBytes());
+        REPORTER_ASSERT(reporter, success == rec.fExpectSuccess);
+    }
+}
diff --git a/tests/ImageIsOpaqueTest.cpp b/tests/ImageIsOpaqueTest.cpp
index 81f1d76..bf3dea6 100644
--- a/tests/ImageIsOpaqueTest.cpp
+++ b/tests/ImageIsOpaqueTest.cpp
@@ -12,6 +12,7 @@
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
 #endif
+#include "SkCanvas.h"
 #include "SkColorSpace_Base.h"
 #include "SkImage.h"
 #include "SkSurface.h"
diff --git a/tests/ImageNewShaderTest.cpp b/tests/ImageNewShaderTest.cpp
index 4091db0..fe01f04 100644
--- a/tests/ImageNewShaderTest.cpp
+++ b/tests/ImageNewShaderTest.cpp
@@ -17,9 +17,6 @@
 #endif
 
 static void test_bitmap_equality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) {
-    SkAutoLockPixels lockBm1(bm1);
-    SkAutoLockPixels lockBm2(bm2);
-
     REPORTER_ASSERT(reporter, bm1.getSize() == bm2.getSize());
     REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getPixels(), bm2.getPixels(), bm1.getSize()));
 }
@@ -57,14 +54,14 @@
     destinationCanvas->clear(SK_ColorTRANSPARENT);
     destinationCanvas->drawPaint(paint);
 
-    SkIRect rect = info.bounds();
-
     SkBitmap bmOrig;
-    sourceSurface->getCanvas()->readPixels(rect, &bmOrig);
+    bmOrig.allocN32Pixels(info.width(), info.height());
+    sourceSurface->getCanvas()->readPixels(bmOrig, 0, 0);
 
 
     SkBitmap bm;
-    destinationCanvas->readPixels(rect, &bm);
+    bm.allocN32Pixels(info.width(), info.height());
+    destinationCanvas->readPixels(bm, 0, 0);
 
     test_bitmap_equality(reporter, bmOrig, bm);
 
@@ -85,11 +82,11 @@
     destinationCanvas->drawPaint(paintTranslated);
 
     SkBitmap bmt;
-    destinationCanvas->readPixels(rect, &bmt);
+    bmt.allocN32Pixels(info.width(), info.height());
+    destinationCanvas->readPixels(bmt, 0, 0);
 
     //  Test correctness
     {
-        SkAutoLockPixels lockBm(bmt);
         for (int y = 0; y < info.height(); y++) {
             REPORTER_ASSERT(reporter, 0xFFFF0000 == bmt.getColor(0, y));
 
diff --git a/tests/ImageStorageTest.cpp b/tests/ImageStorageTest.cpp
index 59bb38b..d818b39 100644
--- a/tests/ImageStorageTest.cpp
+++ b/tests/ImageStorageTest.cpp
@@ -20,19 +20,23 @@
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageStorageLoad, reporter, ctxInfo) {
     class TestFP : public GrFragmentProcessor {
     public:
-        static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTexture> texture, GrSLMemoryModel mm,
+        static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
+                                               sk_sp<GrTextureProxy> proxy,
+                                               GrSLMemoryModel mm,
                                                GrSLRestrict restrict) {
-            return sk_sp<GrFragmentProcessor>(new TestFP(std::move(texture), mm, restrict));
+            return sk_sp<GrFragmentProcessor>(new TestFP(resourceProvider,
+                                                         std::move(proxy), mm, restrict));
         }
 
         const char* name() const override { return "Image Load Test FP"; }
 
     private:
-        TestFP(sk_sp<GrTexture> texture, GrSLMemoryModel mm, GrSLRestrict restrict)
+        TestFP(GrResourceProvider* resourceProvider,
+               sk_sp<GrTextureProxy> proxy, GrSLMemoryModel mm, GrSLRestrict restrict)
                 : INHERITED(kNone_OptimizationFlags)
-                , fImageStorageAccess(std::move(texture), kRead_GrIOType, mm, restrict) {
+                , fImageStorageAccess(std::move(proxy), kRead_GrIOType, mm, restrict) {
             this->initClassID<TestFP>();
-            this->addImageStorageAccess(&fImageStorageAccess);
+            this->addImageStorageAccess(resourceProvider, &fImageStorageAccess);
         }
 
         void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
@@ -50,7 +54,7 @@
                     fb->codeAppend("highp vec2 coord = sk_FragCoord.xy;");
                     fb->appendImageStorageLoad(&imageLoadStr, args.fImageStorages[0],
                                                "ivec2(coord)");
-                    if (GrPixelConfigIsSint(tfp.fImageStorageAccess.texture()->config())) {
+                    if (GrPixelConfigIsSint(tfp.fImageStorageAccess.peekTexture()->config())) {
                         // Map the signed bytes so that when then get read back as unorm values they
                         // will have their original bit pattern.
                         fb->codeAppendf("highp ivec4 ivals = %s;", imageLoadStr.c_str());
@@ -127,15 +131,17 @@
                     continue;
                 }
                 desc.fConfig = test.fConfig;
-                sk_sp<GrTexture> imageStorageTexture(context->resourceProvider()->createTexture(
-                        desc, SkBudgeted::kYes, test.fData.get(), 0));
+                sk_sp<GrTextureProxy> imageStorageTexture =
+                    GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc,
+                                                 SkBudgeted::kYes, test.fData.get(), 0);
 
                 sk_sp<GrRenderTargetContext> rtContext =
-                    context->makeRenderTargetContext(SkBackingFit::kExact, kS, kS,
-                                                     kRGBA_8888_GrPixelConfig, nullptr);
+                    context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kS, kS,
+                                                             kRGBA_8888_GrPixelConfig, nullptr);
                 GrPaint paint;
                 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-                paint.addColorFragmentProcessor(TestFP::Make(imageStorageTexture, mm, restrict));
+                paint.addColorFragmentProcessor(TestFP::Make(context->resourceProvider(),
+                                                             imageStorageTexture, mm, restrict));
                 rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I());
                 std::unique_ptr<uint32_t[]> readData(new uint32_t[kS * kS]);
                 SkImageInfo info = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType,
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 3a7a192..c747b8e 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -32,7 +32,10 @@
 #include "sk_tool_utils.h"
 
 #if SK_SUPPORT_GPU
+#include "GrContextPriv.h"
 #include "GrGpu.h"
+#include "GrResourceCache.h"
+#include "GrTest.h"
 #endif
 
 using namespace sk_gpu_test;
@@ -568,10 +571,8 @@
 DEF_TEST(ImageFromIndex8Bitmap, r) {
     SkPMColor pmColors[1] = {SkPreMultiplyColor(SK_ColorWHITE)};
     SkBitmap bm;
-    sk_sp<SkColorTable> ctable( new SkColorTable(pmColors, SK_ARRAY_COUNT(pmColors)));
     SkImageInfo info = SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType);
-    bm.allocPixels(info, nullptr, ctable.get());
-    SkAutoLockPixels autoLockPixels(bm);
+    bm.allocPixels(info, SkColorTable::Make(pmColors, SK_ARRAY_COUNT(pmColors)));
     *bm.getAddr8(0, 0) = 0;
     sk_sp<SkImage> img(SkImage::MakeFromBitmap(bm));
     REPORTER_ASSERT(r, img != nullptr);
@@ -693,7 +694,6 @@
         REPORTER_ASSERT(reporter, bitmap.isImmutable());
     }
 
-    SkAutoLockPixels alp(bitmap);
     REPORTER_ASSERT(reporter, bitmap.getPixels());
 
     const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType());
@@ -807,48 +807,182 @@
         static_cast<TextureReleaseChecker*>(self)->fReleaseCount++;
     }
 };
+
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_NewFromTextureRelease, reporter, ctxInfo) {
     const int kWidth = 10;
     const int kHeight = 10;
     std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
-    GrBackendTextureDesc backendDesc;
-    backendDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
-    backendDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    backendDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    backendDesc.fWidth = kWidth;
-    backendDesc.fHeight = kHeight;
-    backendDesc.fSampleCnt = 0;
-    backendDesc.fTextureHandle = ctxInfo.grContext()->getGpu()->createTestingOnlyBackendTexture(
-        pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
+
+    GrContext* ctx = ctxInfo.grContext();
+
+    GrBackendObject backendTexHandle =
+            ctxInfo.grContext()->getGpu()->createTestingOnlyBackendTexture(
+                    pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(ctx->contextPriv().getBackend(),
+                                                               kWidth,
+                                                               kHeight,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendTexHandle);
 
     TextureReleaseChecker releaseChecker;
+    GrSurfaceOrigin texOrigin = kBottomLeft_GrSurfaceOrigin;
     sk_sp<SkImage> refImg(
-        SkImage::MakeFromTexture(ctxInfo.grContext(), backendDesc, kPremul_SkAlphaType,
+        SkImage::MakeFromTexture(ctx, backendTex, texOrigin, kPremul_SkAlphaType, nullptr,
                                  TextureReleaseChecker::Release, &releaseChecker));
 
     GrSurfaceOrigin readBackOrigin;
     GrBackendObject readBackHandle = refImg->getTextureHandle(false, &readBackOrigin);
     // TODO: Make it so we can check this (see skbug.com/5019)
 #if 0
-    if (*readBackHandle != *(backendDesc.fTextureHandle)) {
+    if (*readBackHandle != *(backendTexHandle)) {
         ERRORF(reporter, "backend mismatch %d %d\n",
-                       (int)readBackHandle, (int)backendDesc.fTextureHandle);
+                       (int)readBackHandle, (int)backendTexHandle);
     }
-    REPORTER_ASSERT(reporter, readBackHandle == backendDesc.fTextureHandle);
+    REPORTER_ASSERT(reporter, readBackHandle == backendTexHandle);
 #else
     REPORTER_ASSERT(reporter, SkToBool(readBackHandle));
 #endif
-    if (readBackOrigin != backendDesc.fOrigin) {
-        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, backendDesc.fOrigin);
+    if (readBackOrigin != texOrigin) {
+        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
     }
-    REPORTER_ASSERT(reporter, readBackOrigin == backendDesc.fOrigin);
+    REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
 
     // Now exercise the release proc
     REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
     refImg.reset(nullptr); // force a release of the image
     REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
 
-    ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendDesc.fTextureHandle);
+    ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
+}
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_MakeCrossContextRelease, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
+
+    // If we don't have proper support for this feature, the factory will fallback to returning
+    // codec-backed images. Those will "work", but some of our checks will fail because we expect
+    // the cross-context images not to work on multiple contexts at once.
+    if (!ctx->caps()->crossContextTextureSupport()) {
+        return;
+    }
+
+    // We test three lifetime patterns for a single context:
+    // 1) Create image, free image
+    // 2) Create image, draw, flush, free image
+    // 3) Create image, draw, free image, flush
+    // ... and then repeat the last two patterns with drawing on a second* context:
+    // 4) Create image, draw*, flush*, free image
+    // 5) Create image, draw*, free iamge, flush*
+
+    sk_sp<SkData> data = GetResourceAsData("mandrill_128.png");
+    SkASSERT(data.get());
+
+    // Case #1: Create image, free image
+    {
+        sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+        refImg.reset(nullptr); // force a release of the image
+    }
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128);
+    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
+    SkCanvas* canvas = surface->getCanvas();
+
+    // Case #2: Create image, draw, flush, free image
+    {
+        sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+        canvas->drawImage(refImg, 0, 0);
+        canvas->flush();
+
+        refImg.reset(nullptr); // force a release of the image
+    }
+
+    // Case #3: Create image, draw, free image, flush
+    {
+        sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+        canvas->drawImage(refImg, 0, 0);
+        refImg.reset(nullptr); // force a release of the image
+
+        canvas->flush();
+    }
+
+    // Configure second context
+    sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
+
+    GrContextFactory otherFactory;
+    ContextInfo otherContextInfo = otherFactory.getContextInfo(pick_second_context_type(ctxInfo));
+    GrContext* otherCtx = otherContextInfo.grContext();
+    sk_gpu_test::TestContext* otherTestContext = otherContextInfo.testContext();
+
+    surface = SkSurface::MakeRenderTarget(otherCtx, SkBudgeted::kNo, info);
+    canvas = surface->getCanvas();
+
+    // Case #4: Create image, draw*, flush*, free image
+    {
+        testContext->makeCurrent();
+        sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+        otherTestContext->makeCurrent();
+        canvas->drawImage(refImg, 0, 0);
+        canvas->flush();
+
+        testContext->makeCurrent();
+        refImg.reset(nullptr); // force a release of the image
+    }
+
+    // Case #5: Create image, draw*, free image, flush*
+    {
+        testContext->makeCurrent();
+        sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+        otherTestContext->makeCurrent();
+        canvas->drawImage(refImg, 0, 0);
+
+        testContext->makeCurrent();
+        refImg.reset(nullptr); // force a release of the image
+
+        otherTestContext->makeCurrent();
+        canvas->flush();
+    }
+
+    // Case #6: Verify that only one context can be using the image at a time
+    {
+        testContext->makeCurrent();
+        sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+        // Any context should be able to borrow the texture at this point
+        sk_sp<SkColorSpace> texColorSpace;
+        sk_sp<GrTextureProxy> proxy = as_IB(refImg)->asTextureProxyRef(
+            ctx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+        REPORTER_ASSERT(reporter, proxy);
+
+        // But once it's borrowed, no other context should be able to borrow
+        otherTestContext->makeCurrent();
+        sk_sp<GrTextureProxy> otherProxy = as_IB(refImg)->asTextureProxyRef(
+            otherCtx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+        REPORTER_ASSERT(reporter, !otherProxy);
+
+        // Original context (that's already borrowing) should be okay
+        testContext->makeCurrent();
+        sk_sp<GrTextureProxy> proxySecondRef = as_IB(refImg)->asTextureProxyRef(
+            ctx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+        REPORTER_ASSERT(reporter, proxySecondRef);
+
+        // Releae all refs from the original context
+        proxy.reset(nullptr);
+        proxySecondRef.reset(nullptr);
+
+        // Now we should be able to borrow the texture from the other context
+        otherTestContext->makeCurrent();
+        otherProxy = as_IB(refImg)->asTextureProxyRef(
+            otherCtx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+        REPORTER_ASSERT(reporter, otherProxy);
+
+        // Release everything
+        otherProxy.reset(nullptr);
+        refImg.reset(nullptr);
+    }
 }
 
 static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) {
@@ -1063,29 +1197,27 @@
     *srgbBitmap.getAddr32(0, 0) = SkSwizzle_RGBA_to_PMColor(0xFF604020);
     srgbBitmap.setImmutable();
     sk_sp<SkImage> srgbImage = SkImage::MakeFromBitmap(srgbBitmap);
-    sk_sp<SkImage> p3Image = as_IB(srgbImage)->makeColorSpace(p3);
+    sk_sp<SkImage> p3Image = srgbImage->makeColorSpace(p3, SkTransferFunctionBehavior::kIgnore);
     SkBitmap p3Bitmap;
     bool success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode);
     REPORTER_ASSERT(r, success);
-    p3Bitmap.lockPixels();
     REPORTER_ASSERT(r, almost_equal(0x28, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
     REPORTER_ASSERT(r, almost_equal(0x40, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
     REPORTER_ASSERT(r, almost_equal(0x5E, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
 
-    sk_sp<SkImage> adobeImage = as_IB(srgbImage)->makeColorSpace(adobeGamut);
+    sk_sp<SkImage> adobeImage = srgbImage->makeColorSpace(adobeGamut,
+                                                          SkTransferFunctionBehavior::kIgnore);
     SkBitmap adobeBitmap;
     success = adobeImage->asLegacyBitmap(&adobeBitmap, SkImage::kRO_LegacyBitmapMode);
     REPORTER_ASSERT(r, success);
-    adobeBitmap.lockPixels();
     REPORTER_ASSERT(r, almost_equal(0x21, SkGetPackedR32(*adobeBitmap.getAddr32(0, 0))));
     REPORTER_ASSERT(r, almost_equal(0x31, SkGetPackedG32(*adobeBitmap.getAddr32(0, 0))));
     REPORTER_ASSERT(r, almost_equal(0x4C, SkGetPackedB32(*adobeBitmap.getAddr32(0, 0))));
 
     srgbImage = GetResourceAsImage("1x1.png");
-    p3Image = as_IB(srgbImage)->makeColorSpace(p3);
+    p3Image = srgbImage->makeColorSpace(p3, SkTransferFunctionBehavior::kIgnore);
     success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode);
     REPORTER_ASSERT(r, success);
-    p3Bitmap.lockPixels();
     REPORTER_ASSERT(r, almost_equal(0x8B, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
     REPORTER_ASSERT(r, almost_equal(0x82, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
     REPORTER_ASSERT(r, almost_equal(0x77, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
@@ -1148,3 +1280,67 @@
 
     REPORTER_ASSERT(reporter, equal(bm0, bm2));
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void check_scaled_pixels(skiatest::Reporter* reporter, SkPixmap* pmap, uint32_t expected) {
+    // Verify that all pixels contain the original test color
+    for (auto y = 0; y < pmap->height(); ++y) {
+        for (auto x = 0; x < pmap->width(); ++x) {
+            uint32_t pixel = *pmap->addr32(x, y);
+            if (pixel != expected) {
+                ERRORF(reporter, "Expected scaled pixels to be the same. At %d,%d 0x%08x != 0x%08x",
+                       x, y, pixel, expected);
+                return;
+            }
+        }
+    }
+}
+
+static void test_scale_pixels(skiatest::Reporter* reporter, const SkImage* image,
+                              uint32_t expected) {
+    SkImageInfo info = SkImageInfo::MakeN32Premul(image->width() * 2, image->height() * 2);
+
+    // Make sure to test kDisallow first, so we don't just get a cache hit in that case
+    for (auto chint : { SkImage::kDisallow_CachingHint, SkImage::kAllow_CachingHint }) {
+        SkAutoPixmapStorage scaled;
+        scaled.alloc(info);
+        if (!image->scalePixels(scaled, kLow_SkFilterQuality, chint)) {
+            ERRORF(reporter, "Failed to scale image");
+            continue;
+        }
+
+        check_scaled_pixels(reporter, &scaled, expected);
+    }
+}
+
+DEF_TEST(ImageScalePixels, reporter) {
+    const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0);
+    const SkColor red = SK_ColorRED;
+
+    // Test raster image
+    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
+    surface->getCanvas()->clear(red);
+    sk_sp<SkImage> rasterImage = surface->makeImageSnapshot();
+    test_scale_pixels(reporter, rasterImage.get(), pmRed);
+
+    // Test encoded image
+    sk_sp<SkData> data(rasterImage->encode());
+    sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(data);
+    test_scale_pixels(reporter, codecImage.get(), pmRed);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageScalePixels_Gpu, reporter, ctxInfo) {
+    const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0);
+    const SkColor red = SK_ColorRED;
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(16, 16);
+    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo,
+                                                           info);
+    surface->getCanvas()->clear(red);
+    sk_sp<SkImage> gpuImage = surface->makeImageSnapshot();
+    test_scale_pixels(reporter, gpuImage.get(), pmRed);
+}
+#endif
diff --git a/tests/InsetConvexPolyTest.cpp b/tests/InsetConvexPolyTest.cpp
index 4271162..9c1349c 100644
--- a/tests/InsetConvexPolyTest.cpp
+++ b/tests/InsetConvexPolyTest.cpp
@@ -23,8 +23,7 @@
         SkVector v0 = poly[j] - poly[i];
         SkVector v1 = poly[k] - poly[i];
         SkScalar perpDot = v0.cross(v1);
-        int side = winding*perpDot;
-        if (side < 0) {
+        if (winding*perpDot < 0) {
             return false;
         }
     }
diff --git a/tests/IntTextureTest.cpp b/tests/IntTextureTest.cpp
index 346f56b..bfd54ad 100644
--- a/tests/IntTextureTest.cpp
+++ b/tests/IntTextureTest.cpp
@@ -10,6 +10,7 @@
 #if SK_SUPPORT_GPU
 #include "GrClip.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrRenderTargetContext.h"
 #include "GrResourceProvider.h"
 #include "GrTexture.h"
@@ -41,6 +42,7 @@
     static const size_t kRowBytes = kS * sizeof(int32_t);
 
     GrSurfaceDesc desc;
+    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
     desc.fConfig = kRGBA_8888_sint_GrPixelConfig;
     desc.fWidth = kS;
     desc.fHeight = kS;
@@ -64,7 +66,8 @@
         levels[1].fPixels = testData.get();
         levels[1].fRowBytes = (kS / 2) * sizeof(int32_t);
 
-        sk_sp<GrTexture> temp(context->resourceProvider()->createMipMappedTexture(desc,
+        sk_sp<GrTextureProxy> temp(context->resourceProvider()->createMipMappedTexture(
+                                                                                  desc,
                                                                                   SkBudgeted::kYes,
                                                                                   levels, 2));
         REPORTER_ASSERT(reporter, !temp);
@@ -80,21 +83,27 @@
         return;
     }
 
-    GrTexture* texture = proxy->instantiate(context->resourceProvider());
-    REPORTER_ASSERT(reporter, texture);
-    if (!texture) {
+    sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                    std::move(proxy), nullptr);
+    if (!sContext) {
         return;
     }
 
     std::unique_ptr<int32_t[]> readData(new int32_t[kS * kS]);
     // Test that reading to a non-integer config fails.
     {
-        bool success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_GrPixelConfig, readData.get());
+        bool success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                                0, 0, kS, kS,
+                                                                kRGBA_8888_GrPixelConfig,
+                                                                nullptr, readData.get());
         REPORTER_ASSERT(reporter, !success);
     }
     {
         std::unique_ptr<uint16_t[]> halfData(new uint16_t[4 * kS * kS]);
-        bool success = texture->readPixels(0, 0, kS, kS, kRGBA_half_GrPixelConfig, halfData.get());
+        bool success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                                0, 0, kS, kS,
+                                                                kRGBA_half_GrPixelConfig,
+                                                                nullptr, halfData.get());
         REPORTER_ASSERT(reporter, !success);
     }
     {
@@ -102,8 +111,10 @@
         // we don't support. Right now this test is counting on GR_RGBA_INTEGER/GL_BYTE being the
         // implementation-dependent second format).
         sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
-        bool success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig,
-                                           readData.get());
+        bool success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                                0, 0, kS, kS,
+                                                                kRGBA_8888_sint_GrPixelConfig,
+                                                                nullptr, readData.get());
         REPORTER_ASSERT(reporter, success);
         if (success) {
             check_pixels(reporter, kS, kS, testData.get(), readData.get(), "readPixels");
@@ -111,30 +122,28 @@
     }
     {
         // readPixels should fail if we attempt to use the unpremul flag with an integer texture.
-        bool success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig,
-                                           readData.get(), 0, GrContext::kUnpremul_PixelOpsFlag);
+        bool success = context->contextPriv().readSurfacePixels(
+                                                sContext.get(),
+                                                0, 0, kS, kS,
+                                                kRGBA_8888_sint_GrPixelConfig,
+                                                nullptr, readData.get(), 0,
+                                                GrContextPriv::kUnpremul_PixelOpsFlag);
         REPORTER_ASSERT(reporter, !success);
     }
 
     // Test that copying from one integer texture to another succeeds.
     {
         sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, desc,
-                                                                    proxy.get()));
+                                                                    sContext->asSurfaceProxy()));
         REPORTER_ASSERT(reporter, dstContext);
         if (!dstContext || !dstContext->asTextureProxy()) {
             return;
         }
 
-        GrSurface* copySurface = dstContext->asTextureProxy()->instantiate(
-                                                                    context->resourceProvider());
-        REPORTER_ASSERT(reporter, copySurface);
-        if (!copySurface) {
-            return;
-        }
-
         sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
-        bool success = copySurface->readPixels(0, 0, kS, kS,
-                                               kRGBA_8888_sint_GrPixelConfig, readData.get());
+        bool success = context->contextPriv().readSurfacePixels(dstContext.get(), 0, 0, kS, kS,
+                                                                kRGBA_8888_sint_GrPixelConfig,
+                                                                nullptr, readData.get());
         REPORTER_ASSERT(reporter, success);
         if (success) {
             check_pixels(reporter, kS, kS, testData.get(), readData.get(), "copyIntegerToInteger");
@@ -148,7 +157,7 @@
         nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig;
 
         sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc,
-                                                                    proxy.get()));
+                                                                    sContext->asSurfaceProxy()));
         REPORTER_ASSERT(reporter, !dstContext);
     }
 
@@ -158,7 +167,7 @@
         nonIntDesc.fConfig = kRGBA_half_GrPixelConfig;
 
         sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc,
-                                                                    proxy.get()));
+                                                                    sContext->asSurfaceProxy()));
         REPORTER_ASSERT(reporter, !dstContext);
     }
 
@@ -168,27 +177,39 @@
 
     {
         // Can't write pixels from a non-int config.
-        bool success = texture->writePixels(0, 0, kS/2, kS/2, kRGBA_8888_GrPixelConfig,
-                                            bottomRightQuarter, kRowBytes);
+        bool success = context->contextPriv().writeSurfacePixels(sContext.get(),
+                                                                 0, 0, kS/2, kS/2,
+                                                                 kRGBA_8888_GrPixelConfig, nullptr,
+                                                                 bottomRightQuarter, kRowBytes);
         REPORTER_ASSERT(reporter, !success);
     }
     {
         // Can't use unpremul flag.
-        bool success = texture->writePixels(0, 0, kS/2, kS/2, kRGBA_8888_sint_GrPixelConfig,
+        bool success = context->contextPriv().writeSurfacePixels(
+                                            sContext.get(),
+                                            0, 0, kS/2, kS/2,
+                                            kRGBA_8888_sint_GrPixelConfig,
+                                            nullptr,
                                             bottomRightQuarter, kRowBytes,
-                                            GrContext::kUnpremul_PixelOpsFlag);
+                                            GrContextPriv::kUnpremul_PixelOpsFlag);
         REPORTER_ASSERT(reporter, !success);
     }
     {
-        bool success = texture->writePixels(0, 0, kS/2, kS/2, kRGBA_8888_sint_GrPixelConfig,
-                                            bottomRightQuarter, kRowBytes);
+        bool success = context->contextPriv().writeSurfacePixels(sContext.get(),
+                                                                 0, 0, kS/2, kS/2,
+                                                                 kRGBA_8888_sint_GrPixelConfig,
+                                                                 nullptr,
+                                                                 bottomRightQuarter, kRowBytes);
         REPORTER_ASSERT(reporter, success);
         if (!success) {
             return;
         }
 
         sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
-        success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, readData.get());
+        success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                           0, 0, kS, kS,
+                                                           kRGBA_8888_sint_GrPixelConfig,
+                                                           nullptr, readData.get(), 0);
         REPORTER_ASSERT(reporter, success);
         if (!success) {
             return;
@@ -219,9 +240,12 @@
         expectedData.get()[i] = ((0xFF * a) << 24) | ((0xFF * b) << 16) |
                                 ((0xFF * g) << 8) | (0xFF * r);
     }
-    texture->writePixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, testData.get());
+    context->contextPriv().writeSurfacePixels(sContext.get(),
+                                              0, 0, kS, kS,
+                                              kRGBA_8888_sint_GrPixelConfig, nullptr,
+                                              testData.get(), 0);
 
-    sk_sp<GrRenderTargetContext> rtContext = context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> rtContext = context->makeDeferredRenderTargetContext(
             SkBackingFit::kExact, kS, kS, kRGBA_8888_GrPixelConfig, nullptr);
 
     struct {
@@ -234,7 +258,9 @@
     };
 
     for (auto filter : kNamedFilters) {
-        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture, nullptr,
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(context->resourceProvider(),
+                                                                  sContext->asTextureProxyRef(),
+                                                                  nullptr,
                                                                   SkMatrix::I(),
                                                                   filter.fMode));
         REPORTER_ASSERT(reporter, fp);
diff --git a/tests/MallocPixelRefTest.cpp b/tests/MallocPixelRefTest.cpp
index b89d121..096e3e6 100644
--- a/tests/MallocPixelRefTest.cpp
+++ b/tests/MallocPixelRefTest.cpp
@@ -25,8 +25,8 @@
     REPORTER_ASSERT(reporter, true);
     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 13);
     {
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewAllocate(info, info.minRowBytes() - 1, nullptr));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeAllocate(info, info.minRowBytes() - 1, nullptr));
         // rowbytes too small.
         REPORTER_ASSERT(reporter, nullptr == pr.get());
     }
@@ -34,8 +34,8 @@
         size_t rowBytes = info.minRowBytes() - 1;
         size_t size = info.getSafeSize(rowBytes);
         sk_sp<SkData> data(SkData::MakeUninitialized(size));
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithData(info, rowBytes, nullptr, data.get()));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithData(info, rowBytes, nullptr, data));
         // rowbytes too small.
         REPORTER_ASSERT(reporter, nullptr == pr.get());
     }
@@ -43,8 +43,8 @@
         size_t rowBytes = info.minRowBytes() + 2;
         size_t size = info.getSafeSize(rowBytes) - 1;
         sk_sp<SkData> data(SkData::MakeUninitialized(size));
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithData(info, rowBytes, nullptr, data.get()));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithData(info, rowBytes, nullptr, data));
         // data too small.
         REPORTER_ASSERT(reporter, nullptr == pr.get());
     }
@@ -52,32 +52,32 @@
     size_t size = info.getSafeSize(rowBytes) + 9;
     {
         SkAutoMalloc memory(size);
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewDirect(info, memory.get(), rowBytes, nullptr));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeDirect(info, memory.get(), rowBytes, nullptr));
         REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, memory.get() == pr->pixels());
     }
     {
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewAllocate(info, rowBytes, nullptr));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeAllocate(info, rowBytes, nullptr));
         REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, pr->pixels());
     }
     {
         void* addr = static_cast<void*>(new uint8_t[size]);
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(info, rowBytes, nullptr, addr,
-                                          delete_uint8_proc, nullptr));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(info, rowBytes, nullptr, addr,
+                                           delete_uint8_proc, nullptr));
         REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, addr == pr->pixels());
     }
     {
         int x = 0;
         SkAutoMalloc memory(size);
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(info, rowBytes, nullptr,
-                                          memory.get(), set_to_one_proc,
-                                          static_cast<void*>(&x)));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(info, rowBytes, nullptr,
+                                           memory.get(), set_to_one_proc,
+                                           static_cast<void*>(&x)));
         REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, memory.get() == pr->pixels());
         REPORTER_ASSERT(reporter, 0 == x);
@@ -88,10 +88,10 @@
     {
         int x = 0;
         SkAutoMalloc memory(size);
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(SkImageInfo::MakeN32Premul(-1, -1), rowBytes, nullptr,
-                                          memory.get(), set_to_one_proc,
-                                          static_cast<void*>(&x)));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(SkImageInfo::MakeN32Premul(-1, -1), rowBytes, nullptr,
+                                           memory.get(), set_to_one_proc,
+                                           static_cast<void*>(&x)));
         REPORTER_ASSERT(reporter, pr.get() == nullptr);
         // make sure that set_to_one_proc was called.
         REPORTER_ASSERT(reporter, 1 == x);
@@ -99,17 +99,16 @@
     {
         void* addr = static_cast<void*>(new uint8_t[size]);
         REPORTER_ASSERT(reporter, addr != nullptr);
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(info, rowBytes, nullptr, addr,
-                                          delete_uint8_proc, nullptr));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(info, rowBytes, nullptr, addr,
+                                           delete_uint8_proc, nullptr));
         REPORTER_ASSERT(reporter, addr == pr->pixels());
     }
     {
         sk_sp<SkData> data(SkData::MakeUninitialized(size));
         SkData* dataPtr = data.get();
         REPORTER_ASSERT(reporter, dataPtr->unique());
-        sk_sp<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithData(info, rowBytes, nullptr, data.get()));
+        sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithData(info, rowBytes, nullptr, data);
         REPORTER_ASSERT(reporter, !(dataPtr->unique()));
         data.reset(nullptr);
         REPORTER_ASSERT(reporter, dataPtr->unique());
diff --git a/tests/OnFlushCallbackTest.cpp b/tests/OnFlushCallbackTest.cpp
new file mode 100644
index 0000000..f8026e8
--- /dev/null
+++ b/tests/OnFlushCallbackTest.cpp
@@ -0,0 +1,613 @@
+/*
+ * 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 "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrClip.h"
+#include "GrContextPriv.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrOnFlushResourceProvider.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrQuad.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "ops/GrTestMeshDrawOp.h"
+
+// This is a simplified mesh drawing op that can be used in the atlas generation test.
+// Please see AtlasedRectOp below.
+class NonAARectOp : public GrLegacyMeshDrawOp {
+public:
+    DEFINE_OP_CLASS_ID
+    const char* name() const override { return "NonAARectOp"; }
+
+    // This creates an instance of a simple non-AA solid color rect-drawing Op
+    static std::unique_ptr<GrDrawOp> Make(const SkRect& r, GrColor color) {
+        return std::unique_ptr<GrDrawOp>(new NonAARectOp(ClassID(), r, color));
+    }
+
+    // This creates an instance of a simple non-AA textured rect-drawing Op
+    static std::unique_ptr<GrDrawOp> Make(const SkRect& r, GrColor color, const SkRect& local) {
+        return std::unique_ptr<GrDrawOp>(new NonAARectOp(ClassID(), r, color, local));
+    }
+
+    GrColor color() const { return fColor; }
+
+protected:
+    NonAARectOp(uint32_t classID, const SkRect& r, GrColor color)
+        : INHERITED(classID)
+        , fColor(color)
+        , fHasLocalRect(false)
+        , fRect(r) {
+        // Choose some conservative values for aa bloat and zero area.
+        this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
+    }
+
+    NonAARectOp(uint32_t classID, const SkRect& r, GrColor color, const SkRect& local)
+        : INHERITED(classID)
+        , fColor(color)
+        , fHasLocalRect(true)
+        , fLocalQuad(local)
+        , fRect(r) {
+        // Choose some conservative values for aa bloat and zero area.
+        this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
+    }
+
+    GrColor fColor;
+    bool    fHasLocalRect;
+    GrQuad  fLocalQuad;
+    SkRect  fRect;
+
+private:
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
+        color->setToUnknown();
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
+    }
+
+    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
+        optimizations.getOverrideColorIfSet(&fColor);
+    }
+
+    bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
+
+    void onPrepareDraws(Target* target) const override {
+        using namespace GrDefaultGeoProcFactory;
+
+        // The vertex attrib order is always pos, color, local coords.
+        static const int kColorOffset = sizeof(SkPoint);
+        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+
+        sk_sp<GrGeometryProcessor> gp =
+                GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+                                              Coverage::kSolid_Type,
+                                              fHasLocalRect ? LocalCoords::kHasExplicit_Type
+                                                            : LocalCoords::kUnused_Type,
+                                              SkMatrix::I());
+        if (!gp) {
+            SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
+            return;
+        }
+
+        size_t vertexStride = gp->getVertexStride();
+
+        SkASSERT(fHasLocalRect
+                    ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
+                    : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
+
+        const GrBuffer* indexBuffer;
+        int firstIndex;
+        uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
+        if (!indices) {
+            SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
+            return;
+        }
+
+        const GrBuffer* vertexBuffer;
+        int firstVertex;
+        void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
+        if (!vertices) {
+            SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
+            return;
+        }
+
+        // Setup indices
+        indices[0] = 0;
+        indices[1] = 1;
+        indices[2] = 2;
+        indices[3] = 0;
+        indices[4] = 2;
+        indices[5] = 3;
+
+        // Setup positions
+        SkPoint* position = (SkPoint*) vertices;
+        position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride);
+
+        // Setup vertex colors
+        GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
+        for (int i = 0; i < 4; ++i) {
+            *color = fColor;
+            color = (GrColor*)((intptr_t)color + vertexStride);
+        }
+
+        // Setup local coords
+        if (fHasLocalRect) {
+            SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
+            for (int i = 0; i < 4; i++) {
+                *coords = fLocalQuad.point(i);
+                coords = (SkPoint*)((intptr_t) coords + vertexStride);
+            }
+        }
+
+        GrMesh mesh(kTriangles_GrPrimitiveType);
+        mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+
+        target->draw(gp.get(), this->pipeline(), mesh);
+    }
+
+    typedef GrLegacyMeshDrawOp INHERITED;
+};
+
+#ifdef SK_DEBUG
+#include "SkImageEncoder.h"
+#include "sk_tool_utils.h"
+
+static void save_bm(const SkBitmap& bm, const char name[]) {
+    bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
+    SkASSERT(result);
+}
+#endif
+
+/*
+ * Atlased ops just draw themselves as textured rects with the texture pixels being
+ * pulled out of the atlas. Their color is based on their ID.
+ */
+class AtlasedRectOp final : public NonAARectOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    ~AtlasedRectOp() override {
+        fID = -1;
+    }
+
+    const char* name() const override { return "AtlasedRectOp"; }
+
+    int id() const { return fID; }
+
+    static std::unique_ptr<AtlasedRectOp> Make(const SkRect& r, int id) {
+        return std::unique_ptr<AtlasedRectOp>(new AtlasedRectOp(r, id));
+    }
+
+    void setColor(GrColor color) { fColor = color; }
+    void setLocalRect(const SkRect& localRect) {
+        SkASSERT(fHasLocalRect);    // This should've been created to anticipate this
+        fLocalQuad.set(localRect);
+    }
+
+    AtlasedRectOp* next() const { return fNext; }
+    void setNext(AtlasedRectOp* next) {
+        fNext = next;
+    }
+
+private:
+    // We set the initial color of the NonAARectOp based on the ID.
+    // Note that we force creation of a NonAARectOp that has local coords in anticipation of
+    // pulling from the atlas.
+    AtlasedRectOp(const SkRect& r, int id)
+        : INHERITED(ClassID(), r, kColors[id], SkRect::MakeEmpty())
+        , fID(id)
+        , fNext(nullptr) {
+        SkASSERT(fID < kMaxIDs);
+    }
+
+    static const int kMaxIDs = 9;
+    static const SkColor kColors[kMaxIDs];
+
+    int            fID;
+    // The Atlased ops have an internal singly-linked list of ops that land in the same opList
+    AtlasedRectOp* fNext;
+
+    typedef NonAARectOp INHERITED;
+};
+
+const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
+    GrColorPackRGBA(255, 0, 0, 255),
+    GrColorPackRGBA(0, 255, 0, 255),
+    GrColorPackRGBA(0, 0, 255, 255),
+    GrColorPackRGBA(0, 255, 255, 255),
+    GrColorPackRGBA(255, 0, 255, 255),
+    GrColorPackRGBA(255, 255, 0, 255),
+    GrColorPackRGBA(0, 0, 0, 255),
+    GrColorPackRGBA(128, 128, 128, 255),
+    GrColorPackRGBA(255, 255, 255, 255)
+};
+
+static const int kDrawnTileSize = 16;
+
+/*
+ * Rather than performing any rect packing, this atlaser just lays out constant-sized
+ * tiles in an Nx1 row
+ */
+static const int kAtlasTileSize = 2;
+
+/*
+ * This class aggregates the op information required for atlasing
+ */
+class AtlasObject final : public GrOnFlushCallbackObject {
+public:
+    AtlasObject() : fDone(false) { }
+
+    ~AtlasObject() override {
+        SkASSERT(fDone);
+    }
+
+    void markAsDone() {
+        fDone = true;
+    }
+
+    // Insert the new op in an internal singly-linked list for 'opListID'
+    void addOp(uint32_t opListID, AtlasedRectOp* op) {
+        LinkedListHeader* header = nullptr;
+        for (int i = 0; i < fOps.count(); ++i) {
+            if (opListID == fOps[i].fID) {
+                header = &(fOps[i]);
+            }
+        }
+
+        if (!header) {
+            fOps.push({opListID, nullptr});
+            header = &(fOps[fOps.count()-1]);
+        }
+
+        op->setNext(header->fHead);
+        header->fHead = op;
+    }
+
+    // For the time being we need to pre-allocate the atlas.
+    void setAtlasDest(sk_sp<GrTextureProxy> atlasDest) {
+        fAtlasDest = atlasDest;
+    }
+
+    void saveRTC(sk_sp<GrRenderTargetContext> rtc) {
+        SkASSERT(!fRTC);
+        fRTC = rtc;
+    }
+
+#ifdef SK_DEBUG
+    void saveAtlasToDisk() {
+        SkBitmap readBack;
+        readBack.allocN32Pixels(fRTC->width(), fRTC->height());
+
+        bool result = fRTC->readPixels(readBack.info(),
+                                       readBack.getPixels(), readBack.rowBytes(), 0, 0);
+        SkASSERT(result);
+        save_bm(readBack, "atlas-real.png");
+    }
+#endif
+
+    /*
+     * This callback back creates the atlas and updates the AtlasedRectOps to read from it
+     */
+    void preFlush(GrOnFlushResourceProvider* resourceProvider,
+                  const uint32_t* opListIDs, int numOpListIDs,
+                  SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
+        SkASSERT(!results->count());
+
+        // Until MDB is landed we will most-likely only have one opList.
+        SkTDArray<LinkedListHeader*> lists;
+        for (int i = 0; i < numOpListIDs; ++i) {
+            if (LinkedListHeader* list = this->getList(opListIDs[i])) {
+                lists.push(list);
+            }
+        }
+
+        if (!lists.count()) {
+            return; // nothing to atlas
+        }
+
+        // TODO: right now we have to pre-allocate the atlas bc the TextureSamplers need a
+        // hard GrTexture
+#if 0
+        GrSurfaceDesc desc;
+        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+        desc.fWidth = this->numOps() * kAtlasTileSize;
+        desc.fHeight = kAtlasTileSize;
+        desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+        sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(desc,
+                                                                                     nullptr,
+                                                                                     nullptr);
+#else
+        // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
+        // there should either be two writes to clear it or no writes.
+        SkASSERT(9 == fAtlasDest->getPendingReadCnt_TestOnly());
+        SkASSERT(2 == fAtlasDest->getPendingWriteCnt_TestOnly() ||
+                 0 == fAtlasDest->getPendingWriteCnt_TestOnly());
+        sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
+                                                                           fAtlasDest,
+                                                                           nullptr, nullptr);
+#endif
+
+        rtc->clear(nullptr, 0xFFFFFFFF, true); // clear the atlas
+
+        int blocksInAtlas = 0;
+        for (int i = 0; i < lists.count(); ++i) {
+            for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
+                SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
+                                              kAtlasTileSize, kAtlasTileSize);
+
+                // For now, we avoid the resource buffer issues and just use clears
+#if 1
+                rtc->clear(&r, op->color(), false);
+#else
+                std::unique_ptr<GrDrawOp> drawOp(GrNonAARectOp::Make(SkRect::Make(r),
+                                                 atlasedOp->color()));
+
+                GrPaint paint;
+                rtc->priv().testingOnly_addDrawOp(std::move(paint),
+                                                  GrAAType::kNone,
+                                                  std::move(drawOp));
+#endif
+                blocksInAtlas++;
+
+                // Set the atlased Op's color to white (so we know we're not using it for
+                // the final draw).
+                op->setColor(0xFFFFFFFF);
+
+                // Set the atlased Op's localRect to point to where it landed in the atlas
+                op->setLocalRect(SkRect::Make(r));
+
+                // TODO: we also need to set the op's GrSuperDeferredSimpleTextureEffect to point
+                // to the rtc's proxy!
+            }
+
+            // We've updated all these ops and we certainly don't want to process them again
+            this->clearOpsFor(lists[i]);
+        }
+
+        // Hide a ref to the RTC in AtlasData so we can check on it later
+        this->saveRTC(rtc);
+
+        results->push_back(std::move(rtc));
+    }
+
+private:
+    typedef struct {
+        uint32_t       fID;
+        AtlasedRectOp* fHead;
+    } LinkedListHeader;
+
+    LinkedListHeader* getList(uint32_t opListID) {
+        for (int i = 0; i < fOps.count(); ++i) {
+            if (opListID == fOps[i].fID) {
+                return &(fOps[i]);
+            }
+        }
+        return nullptr;
+    }
+
+    void clearOpsFor(LinkedListHeader* header) {
+        // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
+        // forget about them in the laziest way possible.
+        header->fHead = nullptr;
+        header->fID = 0;            // invalid opList ID
+    }
+
+    // Each opList containing AtlasedRectOps gets its own internal singly-linked list
+    SkTDArray<LinkedListHeader>  fOps;
+
+    // The RTC used to create the atlas
+    sk_sp<GrRenderTargetContext> fRTC;
+
+    // For the time being we need to pre-allocate the atlas bc the TextureSamplers require
+    // a GrTexture
+    sk_sp<GrTextureProxy>        fAtlasDest;
+
+    // Set to true when the testing harness expects this object to be no longer used
+    bool                         fDone;
+};
+
+// 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> fakeAtlas) {
+
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
+                                                                      SkBackingFit::kApprox,
+                                                                      3*kDrawnTileSize,
+                                                                      kDrawnTileSize,
+                                                                      kRGBA_8888_GrPixelConfig,
+                                                                      nullptr));
+
+    rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255), true);
+
+    for (int i = 0; i < 3; ++i) {
+        SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
+
+        std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(r, start+i));
+
+        // TODO: here is the blocker for deferring creation of the atlas. The TextureSamplers
+        // created here currently require a hard GrTexture.
+        sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(context->resourceProvider(),
+                                                                    fakeAtlas,
+                                                                    nullptr, SkMatrix::I());
+
+        GrPaint paint;
+        paint.addColorFragmentProcessor(std::move(fp));
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+        AtlasedRectOp* sparePtr = op.get();
+
+        uint32_t opListID = rtc->priv().testingOnly_addLegacyMeshDrawOp(
+                std::move(paint), GrAAType::kNone, std::move(op));
+
+        object->addOp(opListID, sparePtr);
+    }
+
+    return rtc->asTextureProxyRef();
+}
+
+// Enable this if you want to debug the final draws w/o having the atlasCallback create the
+// atlas
+#if 0
+#include "SkGrPriv.h"
+
+sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
+    SkBitmap bm;
+    bm.allocN32Pixels(18, 2, true);
+    bm.erase(SK_ColorRED,     SkIRect::MakeXYWH(0, 0, 2, 2));
+    bm.erase(SK_ColorGREEN,   SkIRect::MakeXYWH(2, 0, 2, 2));
+    bm.erase(SK_ColorBLUE,    SkIRect::MakeXYWH(4, 0, 2, 2));
+    bm.erase(SK_ColorCYAN,    SkIRect::MakeXYWH(6, 0, 2, 2));
+    bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
+    bm.erase(SK_ColorYELLOW,  SkIRect::MakeXYWH(10, 0, 2, 2));
+    bm.erase(SK_ColorBLACK,   SkIRect::MakeXYWH(12, 0, 2, 2));
+    bm.erase(SK_ColorGRAY,    SkIRect::MakeXYWH(14, 0, 2, 2));
+    bm.erase(SK_ColorWHITE,   SkIRect::MakeXYWH(16, 0, 2, 2));
+
+#if 1
+    save_bm(bm, "atlas-fake.png");
+#endif
+
+    GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
+    desc.fFlags |= kRenderTarget_GrSurfaceFlag;
+
+    sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
+                                                             context->textureProvider(),
+                                                             desc, SkBudgeted::kYes,
+                                                             bm.getPixels(), bm.rowBytes());
+
+    return sk_ref_sp(tmp->asTextureProxy());
+}
+#else
+// TODO: this is unfortunate and must be removed. We want the atlas to be created later.
+sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fConfig = kSkia8888_GrPixelConfig;
+    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+    desc.fWidth = 32;
+    desc.fHeight = 16;
+    sk_sp<GrSurfaceProxy> atlasDest = GrSurfaceProxy::MakeDeferred(
+                                                            context->resourceProvider(),
+                                                            desc, SkBackingFit::kExact,
+                                                            SkBudgeted::kYes,
+                                                            GrResourceProvider::kNoPendingIO_Flag);
+    return sk_ref_sp(atlasDest->asTextureProxy());
+}
+#endif
+
+static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
+    SkColor readback = bm.getColor(x, kDrawnTileSize/2);
+    REPORTER_ASSERT(reporter, expected == readback);
+    if (expected != readback) {
+        SkDebugf("Color mismatch: %x %x\n", expected, readback);
+    }
+}
+
+/*
+ * For the atlasing test we make a DAG that looks like:
+ *
+ *    RT1 with ops: 0,1,2       RT2 with ops: 3,4,5       RT3 with ops: 6,7,8
+ *                     \         /
+ *                      \       /
+ *                         RT4
+ * We then flush RT4 and expect only ops 0-5 to be atlased together.
+ * Each op is just a solid colored rect so both the atlas and the final image should appear as:
+ *           R G B C M Y
+ * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
+ *
+ * Note: until MDB lands, the atlas will actually have width= 9*kAtlasTileSize and look like:
+ *           R G B C M Y K Grey White
+ */
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
+    static const int kNumProxies = 3;
+
+    GrContext* context = ctxInfo.grContext();
+
+    if (context->caps()->useDrawInsteadOfClear()) {
+        // TODO: fix the buffer issues so this can run on all devices
+        return;
+    }
+
+    AtlasObject object;
+
+    // For now (until we add a GrSuperDeferredSimpleTextureEffect), we create the final atlas
+    // proxy ahead of time.
+    sk_sp<GrTextureProxy> atlasDest = pre_create_atlas(context);
+
+    object.setAtlasDest(atlasDest);
+
+    context->contextPriv().addOnFlushCallbackObject(&object);
+
+    sk_sp<GrTextureProxy> proxies[kNumProxies];
+    for (int i = 0; i < kNumProxies; ++i) {
+        proxies[i] = make_upstream_image(context, &object, i*3, atlasDest);
+    }
+
+    static const int kFinalWidth = 6*kDrawnTileSize;
+    static const int kFinalHeight = kDrawnTileSize;
+
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
+                                                                      SkBackingFit::kApprox,
+                                                                      kFinalWidth,
+                                                                      kFinalHeight,
+                                                                      kRGBA_8888_GrPixelConfig,
+                                                                      nullptr));
+
+    rtc->clear(nullptr, 0xFFFFFFFF, true);
+
+    // Note that this doesn't include the third texture proxy
+    for (int i = 0; i < kNumProxies-1; ++i) {
+        SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
+
+        SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
+
+        GrPaint paint;
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(context->resourceProvider(),
+                                                                  std::move(proxies[i]),
+                                                                  nullptr, t));
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.addColorFragmentProcessor(std::move(fp));
+
+        rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
+    }
+
+    rtc->prepareForExternalIO();
+
+    SkBitmap readBack;
+    readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
+
+    SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
+                                               readBack.rowBytes(), 0, 0);
+    SkASSERT(result);
+
+    context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
+
+    object.markAsDone();
+
+#if 0
+    save_bm(readBack, "atlas-final-image.png");
+    data.saveAtlasToDisk();
+#endif
+
+    int x = kDrawnTileSize/2;
+    test_color(reporter, readBack, x, SK_ColorRED);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorGREEN);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorBLUE);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorCYAN);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorMAGENTA);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorYELLOW);
+}
+
+#endif
diff --git a/tests/PDFDocumentTest.cpp b/tests/PDFDocumentTest.cpp
index 5fdb3ba..f01d0f9 100644
--- a/tests/PDFDocumentTest.cpp
+++ b/tests/PDFDocumentTest.cpp
@@ -161,7 +161,7 @@
     canvas->translate(20.0f, 10.0f);
     canvas->rotate(30.0f);
     const char text[] = "HELLO";
-    canvas->drawText(text, strlen(text), 0, 0, SkPaint());
+    canvas->drawString(text, 0, 0, SkPaint());
 }
 
 static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
@@ -212,8 +212,8 @@
 
     static const char* moreExpectations[] = {
         "/Producer (phoney library)",
-        "/ProductionLibrary (Skia/PDF ",
-        "<!-- <skia:ProductionLibrary>Skia/PDF ",
+        "/ProductionLibrary (Skia/PDF m",
+        "<!-- <skia:ProductionLibrary>Skia/PDF m",
         "<pdf:Producer>phoney library</pdf:Producer>",
     };
     for (const char* expectation : moreExpectations) {
diff --git a/tests/PDFInvalidBitmapTest.cpp b/tests/PDFInvalidBitmapTest.cpp
deleted file mode 100644
index 54acf02..0000000
--- a/tests/PDFInvalidBitmapTest.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkDocument.h"
-#include "SkImageInfo.h"
-#include "SkPixelRef.h"
-#include "SkRefCnt.h"
-#include "SkStream.h"
-
-#include "Test.h"
-
-namespace {
-
-// SkPixelRef which fails to lock, as a lazy pixel ref might if its pixels
-// cannot be generated.
-class InvalidPixelRef : public SkPixelRef {
-public:
-    InvalidPixelRef(const SkImageInfo& info) : SkPixelRef(info) {}
-private:
-    bool onNewLockPixels(LockRec*) override { return false; }
-    void onUnlockPixels() override {
-        SkDEBUGFAIL("InvalidPixelRef can't be locked");
-    }
-};
-
-SkBitmap make_invalid_bitmap(const SkImageInfo& imageInfo) {
-    SkBitmap bitmap;
-    bitmap.setInfo(imageInfo);
-    bitmap.setPixelRef(sk_make_sp<InvalidPixelRef>(imageInfo), 0 ,0);
-    return bitmap;
-}
-
-SkBitmap make_invalid_bitmap(SkColorType colorType) {
-    return make_invalid_bitmap(
-        SkImageInfo::Make(100, 100, colorType, kPremul_SkAlphaType));
-}
-
-}  // namespace
-
-DEF_TEST(SkPDF_InvalidBitmap, reporter) {
-    SkDynamicMemoryWStream stream;
-    sk_sp<SkDocument> document(SkDocument::MakePDF(&stream));
-    if (!document) {
-        return;
-    }
-    SkCanvas* canvas = document->beginPage(100, 100);
-
-    canvas->drawBitmap(SkBitmap(), 0, 0);
-    canvas->drawBitmap(make_invalid_bitmap(SkImageInfo()), 0, 0);
-    canvas->drawBitmap(make_invalid_bitmap(kN32_SkColorType), 0, 0);
-    canvas->drawBitmap(make_invalid_bitmap(kIndex_8_SkColorType), 0, 0);
-    canvas->drawBitmap(make_invalid_bitmap(kARGB_4444_SkColorType), 0, 0);
-    canvas->drawBitmap(make_invalid_bitmap(kRGB_565_SkColorType), 0, 0);
-    canvas->drawBitmap(make_invalid_bitmap(kAlpha_8_SkColorType), 0, 0);
-
-    // This test passes if it does not crash.
-}
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index de9af07..766bbef 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -366,6 +366,9 @@
         offset->fX = offset->fY = 0;
         return sk_ref_sp<SkSpecialImage>(source);
     }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return sk_ref_sp(const_cast<DummyImageFilter*>(this));
+    }
 
 private:
     DummyImageFilter(bool visited) : INHERITED(nullptr, 0, nullptr), fVisited(visited) {}
diff --git a/tests/PackedConfigsTextureTest.cpp b/tests/PackedConfigsTextureTest.cpp
index 4b8e3cd..e104be5 100644
--- a/tests/PackedConfigsTextureTest.cpp
+++ b/tests/PackedConfigsTextureTest.cpp
@@ -15,6 +15,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrResourceProvider.h"
 #include "GrTexture.h"
 
@@ -95,32 +96,40 @@
     }
 }
 
-template <typename T>
-void runTest(skiatest::Reporter* reporter, GrContext* context,
-             T val1, T val2, int arraySize, GrPixelConfig config) {
-    SkTDArray<T> controlPixelData;
-    // We will read back into an 8888 buffer since 565/4444 read backes aren't supported
+static void run_test(skiatest::Reporter* reporter, GrContext* context,
+                     int arraySize, GrPixelConfig config) {
+    SkTDArray<uint16_t> controlPixelData;
+    // We will read back into an 8888 buffer since 565/4444 read backs aren't supported
     SkTDArray<GrColor> readBuffer;
     controlPixelData.setCount(arraySize);
     readBuffer.setCount(arraySize);
 
     for (int i = 0; i < arraySize; i += 2) {
-        controlPixelData[i] = val1;
-        controlPixelData[i + 1] = val2;
+        controlPixelData[i] = 0xFF00;
+        controlPixelData[i + 1] = 0xFA62;
     }
 
-    for (int origin = 0; origin < 2; ++origin) {
+    const SkImageInfo dstInfo = SkImageInfo::Make(DEV_W, DEV_H,
+                                                  kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
+
+    for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
         GrSurfaceDesc desc;
         desc.fFlags = kNone_GrSurfaceFlags;
         desc.fWidth = DEV_W;
         desc.fHeight = DEV_H;
         desc.fConfig = config;
-        desc.fOrigin = 0 == origin ?
-            kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
-        sk_sp<GrTexture> fpTexture(context->resourceProvider()->createTexture(
-            desc, SkBudgeted::kNo, controlPixelData.begin(), 0));
-        SkASSERT(fpTexture);
-        fpTexture->readPixels(0, 0, DEV_W, DEV_H, kRGBA_8888_GrPixelConfig, readBuffer.begin(), 0);
+        desc.fOrigin = origin;
+
+        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                   desc, SkBudgeted::kNo,
+                                                                   controlPixelData.begin(), 0);
+        SkASSERT(proxy);
+
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                        std::move(proxy), nullptr);
+
+        SkAssertResult(sContext->readPixels(dstInfo, readBuffer.begin(), 0, 0, 0));
+
         if (kRGBA_4444_GrPixelConfig == config) {
             check_4444(reporter, controlPixelData, readBuffer);
         } else {
@@ -133,13 +142,11 @@
 static const int CONTROL_ARRAY_SIZE = DEV_W * DEV_H;
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RGBA4444TextureTest, reporter, ctxInfo) {
-    runTest<uint16_t>(reporter, ctxInfo.grContext(), 0xFF00, 0xFA62,
-                      CONTROL_ARRAY_SIZE, kRGBA_4444_GrPixelConfig);
+    run_test(reporter, ctxInfo.grContext(), CONTROL_ARRAY_SIZE, kRGBA_4444_GrPixelConfig);
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RGB565TextureTest, reporter, ctxInfo) {
-    runTest<uint16_t>(reporter, ctxInfo.grContext(), 0xFF00, 0xFA62,
-                      CONTROL_ARRAY_SIZE, kRGB_565_GrPixelConfig);
+    run_test(reporter, ctxInfo.grContext(), CONTROL_ARRAY_SIZE, kRGB_565_GrPixelConfig);
 }
 
 #endif
diff --git a/tests/ParametricStageTest.cpp b/tests/ParametricStageTest.cpp
new file mode 100644
index 0000000..5b34e16
--- /dev/null
+++ b/tests/ParametricStageTest.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "SkColorSpace.h"
+#include "SkRasterPipeline.h"
+#include "Test.h"
+
+static void check_error(skiatest::Reporter* r, float limit, SkColorSpaceTransferFn fn) {
+    float in[256], out[256];
+    for (int i = 0; i < 256; i++) {
+        in [i] = i / 255.0f;
+        out[i] = 0.0f;  // Not likely important.  Just being tidy.
+    }
+
+    const float* ip = in;
+    float*       op = out;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_f32, &ip);
+    p.append(SkRasterPipeline::parametric_r, &fn);
+    p.append(SkRasterPipeline::parametric_g, &fn);
+    p.append(SkRasterPipeline::parametric_b, &fn);
+    p.append(SkRasterPipeline::parametric_a, &fn);
+    p.append(SkRasterPipeline::store_f32, &op);
+
+    p.run(0,0, 256/4);
+
+
+    for (int i = 0; i < 256; i++) {
+        float want = (in[i] <= fn.fD) ? fn.fC * in[i] + fn.fF
+                                      : powf(in[i] * fn.fA + fn.fB, fn.fG) + fn.fE;
+        float err = fabsf(out[i] - want);
+        if (err > limit) {
+            ERRORF(r, "At %d, error was %g (got %g, want %g)", i, err, out[i], want);
+        }
+    }
+}
+
+static void check_error(skiatest::Reporter* r, float limit, float gamma) {
+    SkColorSpaceTransferFn fn = {0,0,0,0,0,0,0};
+    fn.fG = gamma;
+    fn.fA = 1;
+    check_error(r, limit, fn);
+}
+
+DEF_TEST(Parametric_sRGB, r) {
+    // Test our good buddy the sRGB transfer function in resplendent 7-parameter glory.
+    check_error(r, 1/510.0f, {
+        2.4f,
+        1.0f / 1.055f,
+        0.055f / 1.055f,
+        1.0f / 12.92f,
+        0.04045f,
+        0.0f,
+        0.0f,
+    });
+}
+
+// A nice little spread of simple gammas.
+DEF_TEST(Parametric_1dot0, r) { check_error(r, 1/510.0f, 1.0f); }
+
+DEF_TEST(Parametric_1dot2, r) { check_error(r, 1/510.0f, 1.2f); }
+DEF_TEST(Parametric_1dot4, r) { check_error(r, 1/510.0f, 1.4f); }
+DEF_TEST(Parametric_1dot8, r) { check_error(r, 1/510.0f, 1.8f); }
+DEF_TEST(Parametric_2dot0, r) { check_error(r, 1/510.0f, 2.0f); }
+DEF_TEST(Parametric_2dot2, r) { check_error(r, 1/510.0f, 2.2f); }
+DEF_TEST(Parametric_2dot4, r) { check_error(r, 1/510.0f, 2.4f); }
+
+DEF_TEST(Parametric_inv_1dot2, r) { check_error(r, 1/510.0f, 1/1.2f); }
+DEF_TEST(Parametric_inv_1dot4, r) { check_error(r, 1/510.0f, 1/1.4f); }
+DEF_TEST(Parametric_inv_1dot8, r) { check_error(r, 1/510.0f, 1/1.8f); }
+DEF_TEST(Parametric_inv_2dot0, r) { check_error(r, 1/510.0f, 1/2.0f); }
+DEF_TEST(Parametric_inv_2dot2, r) { check_error(r, 1/510.0f, 1/2.2f); }
+DEF_TEST(Parametric_inv_2dot4, r) { check_error(r, 1/510.0f, 1/2.4f); }
diff --git a/tests/PathOpsAngleIdeas.cpp b/tests/PathOpsAngleIdeas.cpp
index 003242f..c50260f 100644
--- a/tests/PathOpsAngleIdeas.cpp
+++ b/tests/PathOpsAngleIdeas.cpp
@@ -558,8 +558,7 @@
 }
 
 DEF_TEST(PathOpsAngleOverlapHullsOne, reporter) {
-    char storage[4096];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<4096> allocator;
 //    gPathOpsAngleIdeasVerbose = true;
     const QuadPts quads[] = {
 {{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
@@ -574,8 +573,7 @@
 }
 
 DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
-    char storage[4096];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<4096> allocator;
     if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
         return;
     }
diff --git a/tests/PathOpsAngleTest.cpp b/tests/PathOpsAngleTest.cpp
index 4226a77..7f4c04a 100644
--- a/tests/PathOpsAngleTest.cpp
+++ b/tests/PathOpsAngleTest.cpp
@@ -239,8 +239,7 @@
 static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
 
 DEF_TEST(PathOpsAngleCircle, reporter) {
-    char storage[4096];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<4096> allocator;
     SkOpContourHead contour;
     SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
     contour.init(&state, false, false);
@@ -432,8 +431,7 @@
 };
 
 DEF_TEST(PathOpsAngleAfter, reporter) {
-    char storage[4096];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<4096> allocator;
     SkOpContourHead contour;
     SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
     contour.init(&state, false, false);
@@ -445,7 +443,7 @@
             contour.reset();
             for (int index3 = 0; index3 < 3; ++index3) {
                 IntersectData& data = dataArray[index2 + index3];
-                SkPoint* temp = (SkPoint*) SkOpTAllocator<FourPoints>::Allocate(&allocator);
+                SkPoint* temp = (SkPoint*) allocator.make<FourPoints>();
                 for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
                     temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
                 }
@@ -485,7 +483,7 @@
             : this->addT(startT);
     SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
             : this->addT(endT);
-    SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
+    SkOpAngle* angle = this->globalState()->allocator()->make<SkOpAngle>();
     SkOpSpanBase* startSpan = &fHead;
     while (startSpan->ptT() != startPtT) {
         startSpan = startSpan->upCast()->next();
@@ -505,8 +503,7 @@
 }
 
 DEF_TEST(PathOpsAngleAllOnOneSide, reporter) {
-    char storage[4096];
-    SkArenaAlloc allocator(storage);
+    SkSTArenaAlloc<4096> allocator;
     SkOpContourHead contour;
     SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
     contour.init(&state, false, false);
diff --git a/tests/PathOpsCubicIntersectionTest.cpp b/tests/PathOpsCubicIntersectionTest.cpp
index 638ecb0..66becf3 100644
--- a/tests/PathOpsCubicIntersectionTest.cpp
+++ b/tests/PathOpsCubicIntersectionTest.cpp
@@ -646,11 +646,11 @@
         c[i] = cubic.fPts[i].asSkPoint();
     }
     SkScalar loopT[3];
-    SkScalar d[3];
+    SkScalar d[4];
     SkCubicType cubicType = SkClassifyCubic(c, d);
     int breaks = SkDCubic::ComplexBreak(c, loopT);
     SkASSERT(breaks < 2);
-    if (breaks && cubicType == SkCubicType::kLoop_SkCubicType) {
+    if (breaks && cubicType == SkCubicType::kLoop) {
         SkIntersections i;
         SkPoint twoCubics[7];
         SkChopCubicAt(c, twoCubics, loopT[0]);
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index 8f24adf..c668963 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -21,6 +21,17 @@
 #include <sys/sysctl.h>
 #endif
 
+// std::to_string isn't implemented on android
+#include <sstream>
+
+template <typename T>
+std::string std_to_string(T value)
+{
+    std::ostringstream os ;
+    os << value ;
+    return os.str() ;
+}
+
 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
              SkDEBUGPARAMS(bool skipAssert)
              SkDEBUGPARAMS(const char* testName));
@@ -179,7 +190,7 @@
     state->fSerialNo[8] = '\0';
     SkDebugf("%s\n", state->fSerialNo);
     if (strcmp(state->fSerialNo, state->fKey) == 0) {
-        SkDebugf("%s\n", state->fPathStr);
+        SkDebugf("%s\n", state->fPathStr.c_str());
     }
 #endif
 }
@@ -371,8 +382,9 @@
 static int testNumber = 55;
 static const char* testName = "pathOpTest";
 
-static void appendTestName(const char* nameSuffix, SkString& out) {
-    out.appendf("%s%d", testName, testNumber);
+static void appendTestName(const char* nameSuffix, std::string& out) {
+    out += testName;
+    out += std_to_string(testNumber);
     ++testNumber;
     if (nameSuffix) {
         out.append(nameSuffix);
@@ -380,7 +392,7 @@
 }
 
 static void appendTest(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
-                       const char* testFunction, bool twoPaths, SkString& out) {
+                       const char* testFunction, bool twoPaths, std::string& out) {
 #if 0
     out.append("\n<div id=\"");
     appendTestName(nameSuffix, out);
@@ -406,7 +418,9 @@
     if (pathPrefix) {
         out.append(pathPrefix);
     }
-    out.appendf("%s    %s\n}\n\n", pathStr, testFunction);
+    out += pathStr;
+    out += "    ";
+    out += testFunction;
 #if 0
     out.append("static void (*firstTest)() = ");
     appendTestName(nameSuffix, out);
@@ -440,7 +454,7 @@
     int result = comparePaths(state.fReporter, nullptr, path, out, *state.fBitmap);
     if (result) {
         SkAutoMutexAcquire autoM(simplifyDebugOut);
-        SkString str;
+        std::string str;
         const char* pathPrefix = nullptr;
         const char* nameSuffix = nullptr;
         if (fillType == SkPath::kEvenOdd_FillType) {
diff --git a/tests/PathOpsThreadedCommon.h b/tests/PathOpsThreadedCommon.h
index 1462aa2..706da6b 100644
--- a/tests/PathOpsThreadedCommon.h
+++ b/tests/PathOpsThreadedCommon.h
@@ -11,9 +11,10 @@
 #include "SkGraphics.h"
 #include "SkPath.h"
 #include "SkPathOps.h"
-#include "SkString.h"
 #include "SkTDArray.h"
 
+#include <string>
+
 #define PATH_STR_SIZE 512
 
 class PathOpsThreadedRunnable;
@@ -27,7 +28,7 @@
     unsigned char fB;
     unsigned char fC;
     unsigned char fD;
-    SkString fPathStr;
+    std::string fPathStr;
     const char* fKey;
     char fSerialNo[256];
     skiatest::Reporter* fReporter;
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index d41ed47..117da5b 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -8,6 +8,7 @@
 #include "SkAutoMalloc.h"
 #include "SkCanvas.h"
 #include "SkGeometry.h"
+#include "SkNullCanvas.h"
 #include "SkPaint.h"
 #include "SkParse.h"
 #include "SkParsePath.h"
@@ -24,6 +25,7 @@
 #include "Test.h"
 #include <cmath>
 
+
 static void set_radii(SkVector radii[4], int index, float rad) {
     sk_bzero(radii, sizeof(SkVector) * 4);
     radii[index].set(rad, rad);
@@ -4531,7 +4533,7 @@
     test_path_crbugskia6003();
     test_fuzz_crbug_668907();
 
-    SkTSize<SkScalar>::Make(3,4);
+    SkSize::Make(3, 4);
 
     SkPath  p, empty;
     SkRect  bounds, bounds2;
@@ -4751,3 +4753,25 @@
         }
     }
 }
+
+DEF_TEST(skbug_6450, r) {
+    SkRect ri = { 0.18554693f, 195.26283f, 0.185784385f, 752.644409f };
+    SkVector rdi[4] = {
+        { 1.81159976e-09f, 7.58768801e-05f },
+        { 0.000118725002f, 0.000118725002f },
+        { 0.000118725002f, 0.000118725002f },
+        { 0.000118725002f, 0.486297607f }
+    };
+    SkRRect irr;
+    irr.setRectRadii(ri, rdi);
+    SkRect ro = { 9.18354821e-39f, 2.1710848e+9f, 2.16945843e+9f, 3.47808128e+9f };
+    SkVector rdo[4] = {
+        { 0, 0 },
+        { 0.0103298295f, 0.185887396f },
+        { 2.52999727e-29f, 169.001938f },
+        { 195.262741f, 195.161255f }
+    };
+    SkRRect orr;
+    orr.setRectRadii(ro, rdo);
+    SkMakeNullCanvas()->drawDRRect(orr, irr, SkPaint());
+}
diff --git a/tests/PictureBBHTest.cpp b/tests/PictureBBHTest.cpp
index 69cb443..0bc81c2 100644
--- a/tests/PictureBBHTest.cpp
+++ b/tests/PictureBBHTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkBBoxHierarchy.h"
 #include "SkPaint.h"
@@ -91,3 +92,15 @@
     EmptyClipPictureBBHTest emptyClipPictureTest;
     emptyClipPictureTest.run(reporter);
 }
+
+DEF_TEST(RTreeMakeLargest, r) {
+    // A call to insert() with 2 or more rects and a bounds of SkRect::MakeLargest()
+    // used to fall into an infinite loop.
+
+    SkRTreeFactory factory;
+    std::unique_ptr<SkBBoxHierarchy> bbh{ factory(SkRect::MakeLargest()) };
+
+    SkRect rects[] = { {0,0, 10,10}, {5,5,15,15} };
+    bbh->insert(rects, SK_ARRAY_COUNT(rects));
+    REPORTER_ASSERT(r, bbh->getRootBound() == SkRect::MakeWH(15,15));
+}
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 0b929d6..0dc3bfe 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -850,7 +850,7 @@
     SkPaint paint;
     paint.setTypeface(SkTypeface::MakeFromName("Arial",
                                                SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
-    canvas->drawText("Q", 1, 0, 10, paint);
+    canvas->drawString("Q", 0, 10, paint);
     sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
     SkDynamicMemoryWStream stream;
     picture->serialize(&stream);
@@ -1137,66 +1137,38 @@
 
 #endif // SK_SUPPORT_GPU
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+// If we record bounded ops into a picture with a big cull and calculate the
+// bounds of those ops, we should trim down the picture cull to the ops' bounds.
+// If we're not using an SkBBH, we shouldn't change it.
+DEF_TEST(Picture_UpdatedCull_1, r) {
+    // Testing 1 draw exercises SkMiniPicture.
+    SkRTreeFactory factory;
+    SkPictureRecorder recorder;
 
-// Disable until we properly fix https://bugs.chromium.org/p/skia/issues/detail?id=5548
-#if 0
-static void empty_ops(SkCanvas* canvas) {
-}
-static void clip_ops(SkCanvas* canvas) {
-    canvas->save();
-    canvas->clipRect(SkRect::MakeWH(20, 20));
-    canvas->restore();
-}
-static void matrix_ops(SkCanvas* canvas) {
-    canvas->save();
-    canvas->scale(2, 3);
-    canvas->restore();
-}
-static void matrixclip_ops(SkCanvas* canvas) {
-    canvas->save();
-    canvas->scale(2, 3);
-    canvas->clipRect(SkRect::MakeWH(20, 20));
-    canvas->restore();
-}
-typedef void (*CanvasProc)(SkCanvas*);
+    auto canvas = recorder.beginRecording(SkRect::MakeLargest(), &factory);
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    auto pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
 
-// Test the kReturnNullForEmpty_FinishFlag option when recording
-//
-DEF_TEST(Picture_RecordEmpty, r) {
-    const SkRect cull = SkRect::MakeWH(100, 100);
-
-    CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
-
-    for (auto proc : procs) {
-        {
-            SkPictureRecorder rec;
-            proc(rec.beginRecording(cull));
-            sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
-            REPORTER_ASSERT(r, pic.get());
-            REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
-        }
-        {
-            SkPictureRecorder rec;
-            proc(rec.beginRecording(cull));
-            sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
-                                                 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
-            REPORTER_ASSERT(r, !pic.get());
-        }
-        {
-            SkPictureRecorder rec;
-            proc(rec.beginRecording(cull));
-            sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
-            REPORTER_ASSERT(r, dr.get());
-        }
-        {
-            SkPictureRecorder rec;
-            proc(rec.beginRecording(cull));
-            sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
-                                                 SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
-            REPORTER_ASSERT(r, !dr.get());
-        }
-    }
+    canvas = recorder.beginRecording(SkRect::MakeLargest());
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeLargest());
 }
-#endif
+DEF_TEST(Picture_UpdatedCull_2, r) {
+    // Testing >1 draw exercises SkBigPicture.
+    SkRTreeFactory factory;
+    SkPictureRecorder recorder;
 
+    auto canvas = recorder.beginRecording(SkRect::MakeLargest(), &factory);
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
+    auto pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
+
+    canvas = recorder.beginRecording(SkRect::MakeLargest());
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
+    pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeLargest());
+}
diff --git a/tests/PixelRefTest.cpp b/tests/PixelRefTest.cpp
index 487e519..683e249 100644
--- a/tests/PixelRefTest.cpp
+++ b/tests/PixelRefTest.cpp
@@ -69,7 +69,7 @@
 DEF_TEST(PixelRef_GenIDChange, r) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
 
-    sk_sp<SkPixelRef> pixelRef(SkMallocPixelRef::NewAllocate(info, 0, nullptr));
+    sk_sp<SkPixelRef> pixelRef = SkMallocPixelRef::MakeAllocate(info, 0, nullptr);
 
     // Register a listener.
     int count = 0;
diff --git a/tests/PreFlushCallbackTest.cpp b/tests/PreFlushCallbackTest.cpp
deleted file mode 100644
index e008009..0000000
--- a/tests/PreFlushCallbackTest.cpp
+++ /dev/null
@@ -1,612 +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 "Test.h"
-
-#if SK_SUPPORT_GPU
-
-#include "GrClip.h"
-#include "GrContextPriv.h"
-#include "GrDefaultGeoProcFactory.h"
-#include "GrPreFlushResourceProvider.h"
-#include "GrRenderTargetContextPriv.h"
-#include "GrResourceProvider.h"
-#include "GrQuad.h"
-#include "effects/GrSimpleTextureEffect.h"
-#include "ops/GrTestMeshDrawOp.h"
-
-// This is a simplified mesh drawing op that can be used in the atlas generation test.
-// Please see AtlasedRectOp below.
-class NonAARectOp : public GrMeshDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
-    const char* name() const override { return "NonAARectOp"; }
-
-    // This creates an instance of a simple non-AA solid color rect-drawing Op
-    static std::unique_ptr<GrDrawOp> Make(const SkRect& r, GrColor color) {
-        return std::unique_ptr<GrDrawOp>(new NonAARectOp(ClassID(), r, color));
-    }
-
-    // This creates an instance of a simple non-AA textured rect-drawing Op
-    static std::unique_ptr<GrDrawOp> Make(const SkRect& r, GrColor color, const SkRect& local) {
-        return std::unique_ptr<GrDrawOp>(new NonAARectOp(ClassID(), r, color, local));
-    }
-
-    GrColor color() const { return fColor; }
-
-protected:
-    NonAARectOp(uint32_t classID, const SkRect& r, GrColor color)
-        : INHERITED(classID)
-        , fColor(color)
-        , fHasLocalRect(false)
-        , fRect(r) {
-        // Choose some conservative values for aa bloat and zero area.
-        this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
-    }
-
-    NonAARectOp(uint32_t classID, const SkRect& r, GrColor color, const SkRect& local)
-        : INHERITED(classID)
-        , fColor(color)
-        , fHasLocalRect(true)
-        , fLocalQuad(local)
-        , fRect(r) {
-        // Choose some conservative values for aa bloat and zero area.
-        this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
-    }
-
-    GrColor fColor;
-    bool    fHasLocalRect;
-    GrQuad  fLocalQuad;
-    SkRect  fRect;
-
-private:
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
-        color->setToUnknown();
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fColor);
-    }
-
-    bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
-
-    void onPrepareDraws(Target* target) const override {
-        using namespace GrDefaultGeoProcFactory;
-
-        // The vertex attrib order is always pos, color, local coords.
-        static const int kColorOffset = sizeof(SkPoint);
-        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
-
-        sk_sp<GrGeometryProcessor> gp =
-                GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
-                                              Coverage::kSolid_Type,
-                                              fHasLocalRect ? LocalCoords::kHasExplicit_Type
-                                                            : LocalCoords::kUnused_Type,
-                                              SkMatrix::I());
-        if (!gp) {
-            SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
-            return;
-        }
-
-        size_t vertexStride = gp->getVertexStride();
-
-        SkASSERT(fHasLocalRect
-                    ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
-                    : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
-
-        const GrBuffer* indexBuffer;
-        int firstIndex;
-        uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
-        if (!indices) {
-            SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
-            return;
-        }
-
-        const GrBuffer* vertexBuffer;
-        int firstVertex;
-        void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
-        if (!vertices) {
-            SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
-            return;
-        }
-
-        // Setup indices
-        indices[0] = 0;
-        indices[1] = 1;
-        indices[2] = 2;
-        indices[3] = 0;
-        indices[4] = 2;
-        indices[5] = 3;
-
-        // Setup positions
-        SkPoint* position = (SkPoint*) vertices;
-        position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride);
-
-        // Setup vertex colors
-        GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
-        for (int i = 0; i < 4; ++i) {
-            *color = fColor;
-            color = (GrColor*)((intptr_t)color + vertexStride);
-        }
-
-        // Setup local coords
-        if (fHasLocalRect) {
-            SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
-            for (int i = 0; i < 4; i++) {
-                *coords = fLocalQuad.point(i);
-                coords = (SkPoint*)((intptr_t) coords + vertexStride);
-            }
-        }
-
-        GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType,
-                         vertexBuffer, indexBuffer,
-                         firstVertex, firstIndex,
-                         4, 6);
-
-        target->draw(gp.get(), mesh);
-    }
-
-    typedef GrMeshDrawOp INHERITED;
-};
-
-#ifdef SK_DEBUG
-#include "SkImageEncoder.h"
-#include "sk_tool_utils.h"
-
-static void save_bm(const SkBitmap& bm, const char name[]) {
-    bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
-    SkASSERT(result);
-}
-#endif
-
-/*
- * Atlased ops just draw themselves as textured rects with the texture pixels being
- * pulled out of the atlas. Their color is based on their ID.
- */
-class AtlasedRectOp final : public NonAARectOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    ~AtlasedRectOp() override {
-        fID = -1;
-    }
-
-    const char* name() const override { return "AtlasedRectOp"; }
-
-    int id() const { return fID; }
-
-    static std::unique_ptr<AtlasedRectOp> Make(const SkRect& r, int id) {
-        return std::unique_ptr<AtlasedRectOp>(new AtlasedRectOp(r, id));
-    }
-
-    void setColor(GrColor color) { fColor = color; }
-    void setLocalRect(const SkRect& localRect) {
-        SkASSERT(fHasLocalRect);    // This should've been created to anticipate this
-        fLocalQuad.set(localRect);
-    }
-
-    AtlasedRectOp* next() const { return fNext; }
-    void setNext(AtlasedRectOp* next) {
-        fNext = next;
-    }
-
-private:
-    // We set the initial color of the NonAARectOp based on the ID.
-    // Note that we force creation of a NonAARectOp that has local coords in anticipation of
-    // pulling from the atlas.
-    AtlasedRectOp(const SkRect& r, int id)
-        : INHERITED(ClassID(), r, kColors[id], SkRect::MakeEmpty())
-        , fID(id)
-        , fNext(nullptr) {
-        SkASSERT(fID < kMaxIDs);
-    }
-
-    static const int kMaxIDs = 9;
-    static const SkColor kColors[kMaxIDs];
-
-    int            fID;
-    // The Atlased ops have an internal singly-linked list of ops that land in the same opList
-    AtlasedRectOp* fNext;
-
-    typedef NonAARectOp INHERITED;
-};
-
-const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
-    GrColorPackRGBA(255, 0, 0, 255),
-    GrColorPackRGBA(0, 255, 0, 255),
-    GrColorPackRGBA(0, 0, 255, 255),
-    GrColorPackRGBA(0, 255, 255, 255),
-    GrColorPackRGBA(255, 0, 255, 255),
-    GrColorPackRGBA(255, 255, 0, 255),
-    GrColorPackRGBA(0, 0, 0, 255),
-    GrColorPackRGBA(128, 128, 128, 255),
-    GrColorPackRGBA(255, 255, 255, 255)
-};
-
-static const int kDrawnTileSize = 16;
-
-/*
- * Rather than performing any rect packing, this atlaser just lays out constant-sized
- * tiles in an Nx1 row
- */
-static const int kAtlasTileSize = 2;
-
-/*
- * This class aggregates the op information required for atlasing
- */
-class AtlasObject final : public GrPreFlushCallbackObject {
-public:
-    AtlasObject() : fDone(false) { }
-
-    ~AtlasObject() override {
-        SkASSERT(fDone);
-    }
-
-    void markAsDone() {
-        fDone = true;
-    }
-
-    // Insert the new op in an internal singly-linked list for 'opListID'
-    void addOp(uint32_t opListID, AtlasedRectOp* op) {
-        LinkedListHeader* header = nullptr;
-        for (int i = 0; i < fOps.count(); ++i) {
-            if (opListID == fOps[i].fID) {
-                header = &(fOps[i]);
-            }
-        }
-
-        if (!header) {
-            fOps.push({opListID, nullptr});
-            header = &(fOps[fOps.count()-1]);
-        }
-
-        op->setNext(header->fHead);
-        header->fHead = op;
-    }
-
-    // For the time being we need to pre-allocate the atlas.
-    void setAtlasDest(sk_sp<GrTextureProxy> atlasDest) {
-        fAtlasDest = atlasDest;
-    }
-
-    void saveRTC(sk_sp<GrRenderTargetContext> rtc) {
-        SkASSERT(!fRTC);
-        fRTC = rtc;
-    }
-
-#ifdef SK_DEBUG
-    void saveAtlasToDisk() {
-        SkBitmap readBack;
-        readBack.allocN32Pixels(fRTC->width(), fRTC->height());
-
-        bool result = fRTC->readPixels(readBack.info(),
-                                       readBack.getPixels(), readBack.rowBytes(), 0, 0);
-        SkASSERT(result);
-        save_bm(readBack, "atlas-real.png");
-    }
-#endif
-
-    /*
-     * This callback back creates the atlas and updates the AtlasedRectOps to read from it
-     */
-    void preFlush(GrPreFlushResourceProvider* resourceProvider,
-                  const uint32_t* opListIDs, int numOpListIDs,
-                  SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
-        SkASSERT(!results->count());
-
-        // Until MDB is landed we will most-likely only have one opList.
-        SkTDArray<LinkedListHeader*> lists;
-        for (int i = 0; i < numOpListIDs; ++i) {
-            if (LinkedListHeader* list = this->getList(opListIDs[i])) {
-                lists.push(list);
-            }
-        }
-
-        if (!lists.count()) {
-            return; // nothing to atlas
-        }
-
-        // TODO: right now we have to pre-allocate the atlas bc the TextureSamplers need a
-        // hard GrTexture
-#if 0
-        GrSurfaceDesc desc;
-        desc.fFlags = kRenderTarget_GrSurfaceFlag;
-        desc.fWidth = this->numOps() * kAtlasTileSize;
-        desc.fHeight = kAtlasTileSize;
-        desc.fConfig = kRGBA_8888_GrPixelConfig;
-
-        sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(desc,
-                                                                                     nullptr,
-                                                                                     nullptr);
-#else
-        // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
-        // there should either be two writes to clear it or no writes.
-        SkASSERT(9 == fAtlasDest->getPendingReadCnt_TestOnly());
-        SkASSERT(2 == fAtlasDest->getPendingWriteCnt_TestOnly() ||
-                 0 == fAtlasDest->getPendingWriteCnt_TestOnly());
-        sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
-                                                                           fAtlasDest,
-                                                                           nullptr, nullptr);
-#endif
-
-        rtc->clear(nullptr, 0xFFFFFFFF, true); // clear the atlas
-
-        int blocksInAtlas = 0;
-        for (int i = 0; i < lists.count(); ++i) {
-            for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
-                SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
-                                              kAtlasTileSize, kAtlasTileSize);
-
-                // For now, we avoid the resource buffer issues and just use clears
-#if 1
-                rtc->clear(&r, op->color(), false);
-#else
-                std::unique_ptr<GrDrawOp> drawOp(GrNonAARectOp::Make(SkRect::Make(r),
-                                                 atlasedOp->color()));
-
-                GrPaint paint;
-                rtc->priv().testingOnly_addDrawOp(std::move(paint),
-                                                  GrAAType::kNone,
-                                                  std::move(drawOp));
-#endif
-                blocksInAtlas++;
-
-                // Set the atlased Op's color to white (so we know we're not using it for
-                // the final draw).
-                op->setColor(0xFFFFFFFF);
-
-                // Set the atlased Op's localRect to point to where it landed in the atlas
-                op->setLocalRect(SkRect::Make(r));
-
-                // TODO: we also need to set the op's GrSuperDeferredSimpleTextureEffect to point
-                // to the rtc's proxy!
-            }
-
-            // We've updated all these ops and we certainly don't want to process them again
-            this->clearOpsFor(lists[i]);
-        }
-
-        // Hide a ref to the RTC in AtlasData so we can check on it later
-        this->saveRTC(rtc);
-
-        results->push_back(std::move(rtc));
-    }
-
-private:
-    typedef struct {
-        uint32_t       fID;
-        AtlasedRectOp* fHead;
-    } LinkedListHeader;
-
-    LinkedListHeader* getList(uint32_t opListID) {
-        for (int i = 0; i < fOps.count(); ++i) {
-            if (opListID == fOps[i].fID) {
-                return &(fOps[i]);
-            }
-        }
-        return nullptr;
-    }
-
-    void clearOpsFor(LinkedListHeader* header) {
-        // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
-        // forget about them in the laziest way possible.
-        header->fHead = nullptr;
-        header->fID = 0;            // invalid opList ID
-    }
-
-    // Each opList containing AtlasedRectOps gets its own internal singly-linked list
-    SkTDArray<LinkedListHeader>  fOps;
-
-    // The RTC used to create the atlas
-    sk_sp<GrRenderTargetContext> fRTC;
-
-    // For the time being we need to pre-allocate the atlas bc the TextureSamplers require
-    // a GrTexture
-    sk_sp<GrTextureProxy>        fAtlasDest;
-
-    // Set to true when the testing harness expects this object to be no longer used
-    bool                         fDone;
-};
-
-// 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> fakeAtlas) {
-
-    sk_sp<GrRenderTargetContext> rtc(context->makeRenderTargetContext(SkBackingFit::kApprox,
-                                                                      3*kDrawnTileSize,
-                                                                      kDrawnTileSize,
-                                                                      kRGBA_8888_GrPixelConfig,
-                                                                      nullptr));
-
-    rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255), true);
-
-    for (int i = 0; i < 3; ++i) {
-        SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
-
-        std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(r, start+i));
-
-        // TODO: here is the blocker for deferring creation of the atlas. The TextureSamplers
-        // created here currently require a hard GrTexture.
-        sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(context->resourceProvider(),
-                                                                    fakeAtlas,
-                                                                    nullptr, SkMatrix::I());
-
-        GrPaint paint;
-        paint.addColorFragmentProcessor(std::move(fp));
-        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-
-        AtlasedRectOp* sparePtr = op.get();
-
-        uint32_t opListID = rtc->priv().testingOnly_addMeshDrawOp(std::move(paint),
-                                                                  GrAAType::kNone,
-                                                                  std::move(op));
-
-        object->addOp(opListID, sparePtr);
-    }
-
-    return rtc->asTextureProxyRef();
-}
-
-// Enable this if you want to debug the final draws w/o having the atlasCallback create the
-// atlas
-#if 0
-#include "SkGrPriv.h"
-
-sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
-    SkBitmap bm;
-    bm.allocN32Pixels(18, 2, true);
-    bm.erase(SK_ColorRED,     SkIRect::MakeXYWH(0, 0, 2, 2));
-    bm.erase(SK_ColorGREEN,   SkIRect::MakeXYWH(2, 0, 2, 2));
-    bm.erase(SK_ColorBLUE,    SkIRect::MakeXYWH(4, 0, 2, 2));
-    bm.erase(SK_ColorCYAN,    SkIRect::MakeXYWH(6, 0, 2, 2));
-    bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
-    bm.erase(SK_ColorYELLOW,  SkIRect::MakeXYWH(10, 0, 2, 2));
-    bm.erase(SK_ColorBLACK,   SkIRect::MakeXYWH(12, 0, 2, 2));
-    bm.erase(SK_ColorGRAY,    SkIRect::MakeXYWH(14, 0, 2, 2));
-    bm.erase(SK_ColorWHITE,   SkIRect::MakeXYWH(16, 0, 2, 2));
-
-#if 1
-    save_bm(bm, "atlas-fake.png");
-#endif
-
-    GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
-    desc.fFlags |= kRenderTarget_GrSurfaceFlag;
-
-    sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
-                                                             context->textureProvider(),
-                                                             desc, SkBudgeted::kYes,
-                                                             bm.getPixels(), bm.rowBytes());
-
-    return sk_ref_sp(tmp->asTextureProxy());
-}
-#else
-// TODO: this is unfortunate and must be removed. We want the atlas to be created later.
-sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
-    GrSurfaceDesc desc;
-    desc.fFlags = kRenderTarget_GrSurfaceFlag;
-    desc.fConfig = kSkia8888_GrPixelConfig;
-    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    desc.fWidth = 32;
-    desc.fHeight = 16;
-    sk_sp<GrSurfaceProxy> atlasDest = GrSurfaceProxy::MakeDeferred(
-                                                            context->resourceProvider(),
-                                                            desc, SkBackingFit::kExact,
-                                                            SkBudgeted::kYes,
-                                                            GrResourceProvider::kNoPendingIO_Flag);
-    return sk_ref_sp(atlasDest->asTextureProxy());
-}
-#endif
-
-static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
-    SkColor readback = bm.getColor(x, kDrawnTileSize/2);
-    REPORTER_ASSERT(reporter, expected == readback);
-    if (expected != readback) {
-        SkDebugf("Color mismatch: %x %x\n", expected, readback);
-    }
-}
-
-/*
- * For the atlasing test we make a DAG that looks like:
- *
- *    RT1 with ops: 0,1,2       RT2 with ops: 3,4,5       RT3 with ops: 6,7,8
- *                     \         /
- *                      \       /
- *                         RT4
- * We then flush RT4 and expect only ops 0-5 to be atlased together.
- * Each op is just a solid colored rect so both the atlas and the final image should appear as:
- *           R G B C M Y
- * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
- *
- * Note: until MDB lands, the atlas will actually have width= 9*kAtlasTileSize and look like:
- *           R G B C M Y K Grey White
- */
-DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(PreFlushCallbackTest, reporter, ctxInfo) {
-    static const int kNumProxies = 3;
-
-    GrContext* context = ctxInfo.grContext();
-
-    if (context->caps()->useDrawInsteadOfClear()) {
-        // TODO: fix the buffer issues so this can run on all devices
-        return;
-    }
-
-    sk_sp<AtlasObject> object = sk_make_sp<AtlasObject>();
-
-    // For now (until we add a GrSuperDeferredSimpleTextureEffect), we create the final atlas
-    // proxy ahead of time.
-    sk_sp<GrTextureProxy> atlasDest = pre_create_atlas(context);
-
-    object->setAtlasDest(atlasDest);
-
-    context->contextPriv().addPreFlushCallbackObject(object);
-
-    sk_sp<GrTextureProxy> proxies[kNumProxies];
-    for (int i = 0; i < kNumProxies; ++i) {
-        proxies[i] = make_upstream_image(context, object.get(), i*3, atlasDest);
-    }
-
-    static const int kFinalWidth = 6*kDrawnTileSize;
-    static const int kFinalHeight = kDrawnTileSize;
-
-    sk_sp<GrRenderTargetContext> rtc(context->makeRenderTargetContext(SkBackingFit::kApprox,
-                                                                      kFinalWidth,
-                                                                      kFinalHeight,
-                                                                      kRGBA_8888_GrPixelConfig,
-                                                                      nullptr));
-
-    rtc->clear(nullptr, 0xFFFFFFFF, true);
-
-    // Note that this doesn't include the third texture proxy
-    for (int i = 0; i < kNumProxies-1; ++i) {
-        SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
-
-        SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
-
-        GrPaint paint;
-        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(context->resourceProvider(),
-                                                                  std::move(proxies[i]),
-                                                                  nullptr, t));
-        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-        paint.addColorFragmentProcessor(std::move(fp));
-
-        rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
-    }
-
-    rtc->prepareForExternalIO();
-
-    SkBitmap readBack;
-    readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
-
-    SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
-                                               readBack.rowBytes(), 0, 0);
-    SkASSERT(result);
-
-    object->markAsDone();
-
-#if 0
-    save_bm(readBack, "atlas-final-image.png");
-    data.saveAtlasToDisk();
-#endif
-
-    int x = kDrawnTileSize/2;
-    test_color(reporter, readBack, x, SK_ColorRED);
-    x += kDrawnTileSize;
-    test_color(reporter, readBack, x, SK_ColorGREEN);
-    x += kDrawnTileSize;
-    test_color(reporter, readBack, x, SK_ColorBLUE);
-    x += kDrawnTileSize;
-    test_color(reporter, readBack, x, SK_ColorCYAN);
-    x += kDrawnTileSize;
-    test_color(reporter, readBack, x, SK_ColorMAGENTA);
-    x += kDrawnTileSize;
-    test_color(reporter, readBack, x, SK_ColorYELLOW);
-}
-
-#endif
diff --git a/tests/PremulAlphaRoundTripTest.cpp b/tests/PremulAlphaRoundTripTest.cpp
index b1310e3..7719ad8 100644
--- a/tests/PremulAlphaRoundTripTest.cpp
+++ b/tests/PremulAlphaRoundTripTest.cpp
@@ -76,10 +76,10 @@
         readBmp1.eraseColor(0);
         readBmp2.eraseColor(0);
 
-        canvas->readPixels(&readBmp1, 0, 0);
+        canvas->readPixels(readBmp1, 0, 0);
         sk_tool_utils::write_pixels(canvas, readBmp1, 0, 0, gUnpremul[upmaIdx].fColorType,
                                     kUnpremul_SkAlphaType);
-        canvas->readPixels(&readBmp2, 0, 0);
+        canvas->readPixels(readBmp2, 0, 0);
 
         bool success = true;
         for (int y = 0; y < 256 && success; ++y) {
diff --git a/tests/PrimitiveProcessorTest.cpp b/tests/PrimitiveProcessorTest.cpp
index 0a9d002..66b96f7 100644
--- a/tests/PrimitiveProcessorTest.cpp
+++ b/tests/PrimitiveProcessorTest.cpp
@@ -25,14 +25,14 @@
 #include "ops/GrMeshDrawOp.h"
 
 namespace {
-class Op : public GrMeshDrawOp {
+class Op : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
 
     const char* name() const override { return "Dummy Op"; }
 
-    static std::unique_ptr<GrMeshDrawOp> Make(int numAttribs) {
-        return std::unique_ptr<GrMeshDrawOp>(new Op(numAttribs));
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make(int numAttribs) {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new Op(numAttribs));
     }
 
 private:
@@ -40,13 +40,13 @@
         this->setBounds(SkRect::MakeWH(1.f, 1.f), HasAABloat::kNo, IsZeroArea::kNo);
     }
 
-    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
-                                            GrPipelineAnalysisCoverage* coverage) const override {
+    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
+                                    GrProcessorAnalysisCoverage* coverage) const override {
         color->setToUnknown();
-        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
+        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     }
 
-    void applyPipelineOptimizations(const GrPipelineOptimizations&) override {}
+    void applyPipelineOptimizations(const PipelineOptimizations&) override {}
     bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
     void onPrepareDraws(Target* target) const override {
         class GP : public GrGeometryProcessor {
@@ -69,7 +69,7 @@
                     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
                         const GP& gp = args.fGP.cast<GP>();
                         args.fVaryingHandler->emitAttributes(gp);
-                        this->setupPosition(args.fVertBuilder, gpArgs, gp.fAttribs[0].fName);
+                        this->setupPosition(args.fVertBuilder, gpArgs, gp.getAttrib(0).fName);
                         GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
                         fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputColor);
                         fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
@@ -93,19 +93,19 @@
         size_t vertexStride = gp->getVertexStride();
         SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
         vertices->setRectFan(0.f, 0.f, 1.f, 1.f, vertexStride);
-        helper.recordDraw(target, gp.get());
+        helper.recordDraw(target, gp.get(), this->pipeline());
     }
 
     int fNumAttribs;
 
-    typedef GrMeshDrawOp INHERITED;
+    typedef GrLegacyMeshDrawOp INHERITED;
 };
 }
 
 DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
 
-    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
                                                                      SkBackingFit::kApprox,
                                                                      1, 1, kRGBA_8888_GrPixelConfig,
                                                                      nullptr));
@@ -126,16 +126,16 @@
 #endif
     GrPaint grPaint;
     // This one should succeed.
-    renderTargetContext->priv().testingOnly_addMeshDrawOp(GrPaint(grPaint), GrAAType::kNone,
-                                                          Op::Make(attribCnt));
+    renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(GrPaint(grPaint), GrAAType::kNone,
+                                                                Op::Make(attribCnt));
     context->flush();
 #if GR_GPU_STATS
     REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1);
     REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0);
 #endif
     context->resetGpuStats();
-    renderTargetContext->priv().testingOnly_addMeshDrawOp(std::move(grPaint), GrAAType::kNone,
-                                                          Op::Make(attribCnt + 1));
+    renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(std::move(grPaint), GrAAType::kNone,
+                                                                Op::Make(attribCnt + 1));
     context->flush();
 #if GR_GPU_STATS
     REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0);
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index 140ff15..528dd61 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -9,6 +9,7 @@
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
+#include <random>
 #include "GrClip.h"
 #include "GrContext.h"
 #include "GrGpuResource.h"
@@ -20,7 +21,6 @@
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "ops/GrNonAAFillRectOp.h"
 #include "ops/GrTestMeshDrawOp.h"
-#include <random>
 
 namespace {
 class TestOp : public GrTestMeshDrawOp {
@@ -28,8 +28,8 @@
     DEFINE_OP_CLASS_ID
     const char* name() const override { return "TestOp"; }
 
-    static std::unique_ptr<GrMeshDrawOp> Make() {
-        return std::unique_ptr<GrMeshDrawOp>(new TestOp);
+    static std::unique_ptr<GrLegacyMeshDrawOp> Make() {
+        return std::unique_ptr<GrLegacyMeshDrawOp>(new TestOp);
     }
 
 private:
@@ -47,8 +47,8 @@
 class TestFP : public GrFragmentProcessor {
 public:
     struct Image {
-        Image(sk_sp<GrTexture> texture, GrIOType ioType) : fTexture(texture), fIOType(ioType) {}
-        sk_sp<GrTexture> fTexture;
+        Image(sk_sp<GrTextureProxy> proxy, GrIOType ioType) : fProxy(proxy), fIOType(ioType) {}
+        sk_sp<GrTextureProxy> fProxy;
         GrIOType fIOType;
     };
     static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor> child) {
@@ -82,8 +82,9 @@
             this->addBufferAccess(&fBuffers.emplace_back(kRGBA_8888_GrPixelConfig, buffer.get()));
         }
         for (const Image& image : images) {
-            this->addImageStorageAccess(&fImages.emplace_back(
-                    image.fTexture, image.fIOType, GrSLMemoryModel::kNone, GrSLRestrict::kNo));
+            fImages.emplace_back(image.fProxy, image.fIOType,
+                                 GrSLMemoryModel::kNone, GrSLRestrict::kNo);
+            this->addImageStorageAccess(context->resourceProvider(), &fImages.back());
         }
     }
 
@@ -137,21 +138,23 @@
     desc.fHeight = 10;
 
     for (int parentCnt = 0; parentCnt < 2; parentCnt++) {
-        sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
+        sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
                 SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig, nullptr));
         {
             bool texelBufferSupport = context->caps()->shaderCaps()->texelBufferSupport();
             bool imageLoadStoreSupport = context->caps()->shaderCaps()->imageLoadStoreSupport();
             sk_sp<GrTextureProxy> proxy1(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                      desc,
-                                                                      SkBackingFit::kExact,
+                                                                      desc, SkBackingFit::kExact,
                                                                       SkBudgeted::kYes));
-            sk_sp<GrTexture> texture2(
-                    context->resourceProvider()->createTexture(desc, SkBudgeted::kYes));
-            sk_sp<GrTexture> texture3(
-                    context->resourceProvider()->createTexture(desc, SkBudgeted::kYes));
-            sk_sp<GrTexture> texture4(
-                    context->resourceProvider()->createTexture(desc, SkBudgeted::kYes));
+            sk_sp<GrTextureProxy> proxy2(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
+            sk_sp<GrTextureProxy> proxy3(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
+            sk_sp<GrTextureProxy> proxy4(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
             sk_sp<GrBuffer> buffer(texelBufferSupport
                                            ? context->resourceProvider()->createBuffer(
                                                      1024, GrBufferType::kTexel_GrBufferType,
@@ -166,11 +169,11 @@
                     buffers.push_back(buffer);
                 }
                 if (imageLoadStoreSupport) {
-                    images.emplace_back(texture2, GrIOType::kRead_GrIOType);
-                    images.emplace_back(texture3, GrIOType::kWrite_GrIOType);
-                    images.emplace_back(texture4, GrIOType::kRW_GrIOType);
+                    images.emplace_back(proxy2, GrIOType::kRead_GrIOType);
+                    images.emplace_back(proxy3, GrIOType::kWrite_GrIOType);
+                    images.emplace_back(proxy4, GrIOType::kRW_GrIOType);
                 }
-                std::unique_ptr<GrMeshDrawOp> op(TestOp::Make());
+                std::unique_ptr<GrLegacyMeshDrawOp> op(TestOp::Make());
                 GrPaint paint;
                 auto fp = TestFP::Make(context,
                                        std::move(proxies), std::move(buffers), std::move(images));
@@ -178,7 +181,7 @@
                     fp = TestFP::Make(std::move(fp));
                 }
                 paint.addColorFragmentProcessor(std::move(fp));
-                renderTargetContext->priv().testingOnly_addMeshDrawOp(
+                renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
                         std::move(paint), GrAAType::kNone, std::move(op));
             }
             int refCnt, readCnt, writeCnt;
@@ -196,17 +199,17 @@
             }
 
             if (imageLoadStoreSupport) {
-                testingOnly_getIORefCnts(texture2.get(), &refCnt, &readCnt, &writeCnt);
+                testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
                 REPORTER_ASSERT(reporter, 1 == refCnt);
                 REPORTER_ASSERT(reporter, 1 == readCnt);
                 REPORTER_ASSERT(reporter, 0 == writeCnt);
 
-                testingOnly_getIORefCnts(texture3.get(), &refCnt, &readCnt, &writeCnt);
+                testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
                 REPORTER_ASSERT(reporter, 1 == refCnt);
                 REPORTER_ASSERT(reporter, 0 == readCnt);
                 REPORTER_ASSERT(reporter, 1 == writeCnt);
 
-                testingOnly_getIORefCnts(texture4.get(), &refCnt, &readCnt, &writeCnt);
+                testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
                 REPORTER_ASSERT(reporter, 1 == refCnt);
                 REPORTER_ASSERT(reporter, 1 == readCnt);
                 REPORTER_ASSERT(reporter, 1 == writeCnt);
@@ -227,17 +230,17 @@
             }
 
             if (texelBufferSupport) {
-                testingOnly_getIORefCnts(texture2.get(), &refCnt, &readCnt, &writeCnt);
+                testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
                 REPORTER_ASSERT(reporter, 1 == refCnt);
                 REPORTER_ASSERT(reporter, 0 == readCnt);
                 REPORTER_ASSERT(reporter, 0 == writeCnt);
 
-                testingOnly_getIORefCnts(texture3.get(), &refCnt, &readCnt, &writeCnt);
+                testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
                 REPORTER_ASSERT(reporter, 1 == refCnt);
                 REPORTER_ASSERT(reporter, 0 == readCnt);
                 REPORTER_ASSERT(reporter, 0 == writeCnt);
 
-                testingOnly_getIORefCnts(texture4.get(), &refCnt, &readCnt, &writeCnt);
+                testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
                 REPORTER_ASSERT(reporter, 1 == refCnt);
                 REPORTER_ASSERT(reporter, 0 == readCnt);
                 REPORTER_ASSERT(reporter, 0 == writeCnt);
@@ -266,11 +269,11 @@
                                    nullptr, SkMatrix::I());
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    GrPipelineBuilder pb(std::move(paint), GrAAType::kNone);
-    auto op =
-            GrNonAAFillRectOp::Make(GrColor_WHITE, SkMatrix::I(),
-                                    SkRect::MakeWH(rtc->width(), rtc->height()), nullptr, nullptr);
-    rtc->addMeshDrawOp(pb, GrNoClip(), std::move(op));
+
+    auto op = GrNonAAFillRectOp::Make(std::move(paint), SkMatrix::I(),
+                                      SkRect::MakeWH(rtc->width(), rtc->height()), nullptr, nullptr,
+                                      GrAAType::kNone);
+    rtc->addDrawOp(GrNoClip(), std::move(op));
 }
 
 #include "SkCommandLineFlags.h"
@@ -290,7 +293,7 @@
     // hard-code that value here:
     SkRandom random(seed);
 
-    sk_sp<GrRenderTargetContext> rtc = context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> rtc = context->makeDeferredRenderTargetContext(
             SkBackingFit::kExact, 256, 256, kRGBA_8888_GrPixelConfig, nullptr);
     GrSurfaceDesc desc;
     desc.fWidth = 256;
@@ -298,6 +301,8 @@
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
+    sk_sp<GrTextureProxy> proxies[2];
+
     // Put premul data into the RGBA texture that the test FPs can optionally use.
     std::unique_ptr<GrColor[]> rgbaData(new GrColor[256 * 256]);
     for (int y = 0; y < 256; ++y) {
@@ -306,8 +311,8 @@
                     texel_color(random.nextULessThan(256), random.nextULessThan(256));
         }
     }
-    sk_sp<GrTexture> tex0(context->resourceProvider()->createTexture(
-            desc, SkBudgeted::kYes, rgbaData.get(), 256 * sizeof(GrColor)));
+    proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
+                                              rgbaData.get(), 256 * sizeof(GrColor));
 
     // Put random values into the alpha texture that the test FPs can optionally use.
     desc.fConfig = kAlpha_8_GrPixelConfig;
@@ -317,10 +322,14 @@
             alphaData.get()[256 * y + x] = random.nextULessThan(256);
         }
     }
-    sk_sp<GrTexture> tex1(context->resourceProvider()->createTexture(desc, SkBudgeted::kYes,
-                                                                     alphaData.get(), 256));
-    GrTexture* textures[] = {tex0.get(), tex1.get()};
-    GrProcessorTestData testData(&random, context, rtc.get(), textures);
+    proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
+                                              alphaData.get(), 256);
+
+    if (!proxies[0] || !proxies[1]) {
+        return;
+    }
+
+    GrProcessorTestData testData(&random, context, rtc.get(), proxies);
 
     // Use a different array of premul colors for the output of the fragment processor that preceeds
     // the fragment processor under test.
@@ -349,6 +358,10 @@
         }
         for (int j = 0; j < timesToInvokeFactory; ++j) {
             fp = FPFactory::MakeIdx(i, &testData);
+            if (!fp->instantiate(context->resourceProvider())) {
+                continue;
+            }
+
             if (!fp->hasConstantOutputForConstantInput() && !fp->preservesOpaqueInput() &&
                 !fp->compatibleWithCoverageAsAlpha()) {
                 continue;
@@ -434,6 +447,7 @@
         }
     }
 }
+
 #endif  // GR_TEST_UTILS
 #endif  // SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
 #endif  // SK_SUPPORT_GPU
diff --git a/tests/ProxyConversionTest.cpp b/tests/ProxyConversionTest.cpp
index a041d68..167cc4b 100644
--- a/tests/ProxyConversionTest.cpp
+++ b/tests/ProxyConversionTest.cpp
@@ -10,6 +10,7 @@
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
+#include "GrBackendSurface.h"
 #include "GrRenderTargetProxy.h"
 #include "GrResourceProvider.h"
 #include "GrSurfaceProxy.h"
@@ -18,16 +19,12 @@
 static sk_sp<GrSurfaceProxy> make_wrapped_FBO0(GrResourceProvider* provider,
                                                skiatest::Reporter* reporter,
                                                const GrSurfaceDesc& desc) {
-    GrBackendRenderTargetDesc backendDesc;
-    backendDesc.fWidth = desc.fWidth;
-    backendDesc.fHeight = desc.fHeight;
-    backendDesc.fConfig = desc.fConfig;
-    backendDesc.fOrigin = desc.fOrigin;
-    backendDesc.fSampleCnt = desc.fSampleCnt;
-    backendDesc.fStencilBits = 8;
-    backendDesc.fRenderTargetHandle = 0;
+    GrGLFramebufferInfo fboInfo;
+    fboInfo.fFBOID = 0;
+    GrBackendRenderTarget backendRT(desc.fWidth, desc.fHeight, desc.fSampleCnt, 8,
+                                    desc.fConfig, fboInfo);
 
-    sk_sp<GrRenderTarget> defaultFBO(provider->wrapBackendRenderTarget(backendDesc));
+    sk_sp<GrRenderTarget> defaultFBO(provider->wrapBackendRenderTarget(backendRT, desc.fOrigin));
     SkASSERT(!defaultFBO->asTexture());
 
     return GrSurfaceProxy::MakeWrapped(std::move(defaultFBO));
@@ -62,6 +59,7 @@
     desc.fWidth = 64;
     desc.fHeight = 64;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
 
     if (kOpenGL_GrBackend == ctxInfo.backend()) {
         // External on-screen render target.
diff --git a/tests/ProxyRefTest.cpp b/tests/ProxyRefTest.cpp
index ec47597..0775fde 100644
--- a/tests/ProxyRefTest.cpp
+++ b/tests/ProxyRefTest.cpp
@@ -10,6 +10,7 @@
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
+#include "GrContextPriv.h"
 #include "GrRenderTargetPriv.h"
 #include "GrRenderTargetProxy.h"
 #include "GrResourceProvider.h"
@@ -30,7 +31,6 @@
 
 int32_t GrIORefProxy::getPendingReadCnt_TestOnly() const {
     if (fTarget) {
-        SkASSERT(!fPendingReads);
         return fTarget->fPendingReads;
     }
 
@@ -39,17 +39,18 @@
 
 int32_t GrIORefProxy::getPendingWriteCnt_TestOnly() const {
     if (fTarget) {
-        SkASSERT(!fPendingWrites);
         return fTarget->fPendingWrites;
     }
 
     return fPendingWrites;
 }
 
+#ifndef SK_DISABLE_DEFERRED_PROXIES
+
 static const int kWidthHeight = 128;
 
 static void check_refs(skiatest::Reporter* reporter,
-                       GrSurfaceProxy* proxy,
+                       GrTextureProxy* proxy,
                        int32_t expectedProxyRefs,
                        int32_t expectedBackingRefs,
                        int32_t expectedNumReads,
@@ -65,144 +66,130 @@
     SkASSERT(proxy->getPendingWriteCnt_TestOnly() == expectedNumWrites);
 }
 
-static sk_sp<GrSurfaceProxy> make_deferred(GrResourceProvider* provider) {
+static sk_sp<GrTextureProxy> make_deferred(GrContext* context) {
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = kWidthHeight;
     desc.fHeight = kWidthHeight;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    return GrSurfaceProxy::MakeDeferred(provider, desc,
-                                        SkBackingFit::kApprox, SkBudgeted::kYes);
+    return GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc,
+                                        SkBackingFit::kApprox, SkBudgeted::kYes,
+                                        GrResourceProvider::kNoPendingIO_Flag);
 }
 
-static sk_sp<GrSurfaceProxy> make_wrapped(GrResourceProvider* provider) {
+static sk_sp<GrTextureProxy> make_wrapped(GrContext* context) {
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = kWidthHeight;
     desc.fHeight = kWidthHeight;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    sk_sp<GrTexture> tex(provider->createTexture(desc, SkBudgeted::kNo));
-
-    // Flush the IOWrite from the initial discard or it will confuse the later ref count checks
-    tex->flushWrites();
+    sk_sp<GrTexture> tex(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo));
 
     return GrSurfaceProxy::MakeWrapped(std::move(tex));
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ProxyRefTest, reporter, ctxInfo) {
     GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
-    const GrCaps& caps = *ctxInfo.grContext()->caps();
 
-    // Currently the op itself takes a pending write and the render target op list does as well.
-    static const int kWritesForDiscard = 2;
     for (auto make : { make_deferred, make_wrapped }) {
         // A single write
         {
-            sk_sp<GrSurfaceProxy> sProxy((*make)(provider));
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
 
-            GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(sProxy.get());
+            GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(proxy.get());
 
-            check_refs(reporter, sProxy.get(), 1, 1, 0, 1);
+            static const int kExpectedReads = 0;
+            static const int kExpectedWrites = 1;
 
-            // In the deferred case, the discard op created on instantiation adds an
-            // extra ref and write
-            bool proxyGetsDiscardRef = !sProxy->isWrapped_ForTesting() &&
-                                       caps.discardRenderTargetSupport();
-            int expectedWrites = 1 + (proxyGetsDiscardRef ? kWritesForDiscard : 0);
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
 
-            sProxy->instantiate(provider);
+            proxy->instantiate(provider);
 
             // In the deferred case, this checks that the refs transfered to the GrSurface
-            check_refs(reporter, sProxy.get(), 1, 1, 0, expectedWrites);
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
         }
 
         // A single read
         {
-            sk_sp<GrSurfaceProxy> sProxy((*make)(provider));
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
 
-            GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(sProxy.get());
+            GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(proxy.get());
 
-            check_refs(reporter, sProxy.get(), 1, 1, 1, 0);
+            static const int kExpectedReads = 1;
+            static const int kExpectedWrites = 0;
 
-            // In the deferred case, the discard op created on instantiation adds an
-            // extra ref and write
-            bool proxyGetsDiscardRef = !sProxy->isWrapped_ForTesting() &&
-                                       caps.discardRenderTargetSupport();
-            int expectedWrites = proxyGetsDiscardRef ? kWritesForDiscard : 0;
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
 
-            sProxy->instantiate(provider);
+            proxy->instantiate(provider);
 
             // In the deferred case, this checks that the refs transfered to the GrSurface
-            check_refs(reporter, sProxy.get(), 1, 1, 1, expectedWrites);
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
         }
 
         // A single read/write pair
         {
-            sk_sp<GrSurfaceProxy> sProxy((*make)(provider));
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
 
-            GrPendingIOResource<GrSurfaceProxy, kRW_GrIOType> fRW(sProxy.get());
+            GrPendingIOResource<GrSurfaceProxy, kRW_GrIOType> fRW(proxy.get());
 
-            check_refs(reporter, sProxy.get(), 1, 1, 1, 1);
+            static const int kExpectedReads = 1;
+            static const int kExpectedWrites = 1;
 
-            // In the deferred case, the discard op created on instantiation adds an
-            // extra ref and write
-            bool proxyGetsDiscardRef = !sProxy->isWrapped_ForTesting() &&
-                                       caps.discardRenderTargetSupport();
-            int expectedWrites = 1 + (proxyGetsDiscardRef ? kWritesForDiscard : 0);
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
 
-            sProxy->instantiate(provider);
+            proxy->instantiate(provider);
 
             // In the deferred case, this checks that the refs transferred to the GrSurface
-            check_refs(reporter, sProxy.get(), 1, 1, 1, expectedWrites);
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
         }
 
         // Multiple normal refs
         {
-            sk_sp<GrSurfaceProxy> sProxy((*make)(provider));
-            sProxy->ref();
-            sProxy->ref();
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+            proxy->ref();
+            proxy->ref();
 
-            check_refs(reporter, sProxy.get(), 3, 3, 0, 0);
+            static const int kExpectedReads = 0;
+            static const int kExpectedWrites = 0;
 
-            bool proxyGetsDiscardRef = !sProxy->isWrapped_ForTesting() &&
-                                       caps.discardRenderTargetSupport();
-            int expectedWrites = proxyGetsDiscardRef ? kWritesForDiscard : 0;
+            check_refs(reporter, proxy.get(), 3, 3,kExpectedReads, kExpectedWrites);
 
-            sProxy->instantiate(provider);
+            proxy->instantiate(provider);
 
             // In the deferred case, this checks that the refs transferred to the GrSurface
-            check_refs(reporter, sProxy.get(), 3, 3, 0, expectedWrites);
+            check_refs(reporter, proxy.get(), 3, 3, kExpectedReads, kExpectedWrites);
 
-            sProxy->unref();
-            sProxy->unref();
+            proxy->unref();
+            proxy->unref();
         }
 
         // Continue using (reffing) proxy after instantiation
         {
-            sk_sp<GrSurfaceProxy> sProxy((*make)(provider));
-            sProxy->ref();
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+            proxy->ref();
 
-            GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(sProxy.get());
+            GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(proxy.get());
 
-            check_refs(reporter, sProxy.get(), 2, 2, 0, 1);
+            static const int kExpectedWrites = 1;
 
-            bool proxyGetsDiscardRef = !sProxy->isWrapped_ForTesting() &&
-                                       caps.discardRenderTargetSupport();
-            int expectedWrites = 1 + (proxyGetsDiscardRef ? kWritesForDiscard : 0);
+            check_refs(reporter, proxy.get(), 2, 2, 0, kExpectedWrites);
 
-            sProxy->instantiate(provider);
+            proxy->instantiate(provider);
 
             // In the deferred case, this checks that the refs transfered to the GrSurface
-            check_refs(reporter, sProxy.get(), 2, 2, 0, expectedWrites);
+            check_refs(reporter, proxy.get(), 2, 2, 0, kExpectedWrites);
 
-            sProxy->unref();
-            check_refs(reporter, sProxy.get(), 1, 1, 0, expectedWrites);
+            proxy->unref();
+            check_refs(reporter, proxy.get(), 1, 1, 0, kExpectedWrites);
 
-            GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(sProxy.get());
-            check_refs(reporter, sProxy.get(), 1, 1, 1, expectedWrites);
+            GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(proxy.get());
+            check_refs(reporter, proxy.get(), 1, 1, 1, kExpectedWrites);
         }
     }
 }
+
+#endif
+
 #endif
diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp
index 648ae1d..75df5aa 100644
--- a/tests/ProxyTest.cpp
+++ b/tests/ProxyTest.cpp
@@ -10,6 +10,10 @@
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
+
+#ifndef SK_DISABLE_DEFERRED_PROXIES
+
+#include "GrBackendSurface.h"
 #include "GrRenderTargetPriv.h"
 #include "GrRenderTargetProxy.h"
 #include "GrResourceProvider.h"
@@ -20,7 +24,7 @@
 static void check_surface(skiatest::Reporter* reporter,
                           GrSurfaceProxy* proxy,
                           GrSurfaceOrigin origin,
-                          int width, int height, 
+                          int width, int height,
                           GrPixelConfig config,
                           const GrGpuResource::UniqueID& uniqueID,
                           SkBudgeted budgeted) {
@@ -48,8 +52,8 @@
     REPORTER_ASSERT(reporter, rtProxy->numStencilSamples() == numSamples);
 
     GrSurfaceProxy::UniqueID idBefore = rtProxy->uniqueID();
-    GrRenderTarget* rt = rtProxy->instantiate(provider);
-    REPORTER_ASSERT(reporter, rt);
+    REPORTER_ASSERT(reporter, rtProxy->instantiate(provider));
+    GrRenderTarget* rt = rtProxy->priv().peekRenderTarget();
 
     REPORTER_ASSERT(reporter, rtProxy->uniqueID() == idBefore);
     if (wasWrapped) {
@@ -70,12 +74,9 @@
     }
     REPORTER_ASSERT(reporter, rt->config() == rtProxy->config());
 
-    REPORTER_ASSERT(reporter, rt->isUnifiedMultisampled() == rtProxy->isUnifiedMultisampled());
-    REPORTER_ASSERT(reporter, rt->isStencilBufferMultisampled() ==
-                              rtProxy->isStencilBufferMultisampled());
+    REPORTER_ASSERT(reporter, rt->fsaaType() == rtProxy->fsaaType());
     REPORTER_ASSERT(reporter, rt->numColorSamples() == rtProxy->numColorSamples());
     REPORTER_ASSERT(reporter, rt->numStencilSamples() == rtProxy->numStencilSamples());
-    REPORTER_ASSERT(reporter, rt->isMixedSampled() == rtProxy->isMixedSampled());
     REPORTER_ASSERT(reporter, rt->renderTargetPriv().flags() == rtProxy->testingOnly_getFlags());
 }
 
@@ -85,8 +86,9 @@
                           SkBackingFit fit,
                           bool wasWrapped) {
     GrSurfaceProxy::UniqueID idBefore = texProxy->uniqueID();
-    GrTexture* tex = texProxy->instantiate(provider);
-    REPORTER_ASSERT(reporter, tex);
+
+    REPORTER_ASSERT(reporter, texProxy->instantiate(provider));
+    GrTexture* tex = texProxy->priv().peekTexture();
 
     REPORTER_ASSERT(reporter, texProxy->uniqueID() == idBefore);
     if (wasWrapped) {
@@ -120,7 +122,7 @@
     for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
         for (auto widthHeight : { 100, 128, 1048576 }) {
             for (auto config : { kAlpha_8_GrPixelConfig, kRGB_565_GrPixelConfig,
-                                 kETC1_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
+                                 kRGBA_8888_GrPixelConfig }) {
                 for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
                     for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                         for (auto numSamples : { 0, 4, 16, 128 }) {
@@ -135,9 +137,9 @@
                             {
                                 sk_sp<GrTexture> tex;
                                 if (SkBackingFit::kApprox == fit) {
-                                    tex.reset(provider->createApproxTexture(desc, 0));
+                                    tex = provider->createApproxTexture(desc, 0);
                                 } else {
-                                    tex.reset(provider->createTexture(desc, budgeted));
+                                    tex = provider->createTexture(desc, budgeted);
                                 }
 
                                 sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(
@@ -168,9 +170,9 @@
                             {
                                 sk_sp<GrTexture> tex;
                                 if (SkBackingFit::kApprox == fit) {
-                                    tex.reset(provider->createApproxTexture(desc, 0));
+                                    tex = provider->createApproxTexture(desc, 0);
                                 } else {
-                                    tex.reset(provider->createTexture(desc, budgeted));
+                                    tex = provider->createTexture(desc, budgeted);
                                 }
 
                                 sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(provider,
@@ -213,6 +215,10 @@
         for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
             for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                 for (auto numSamples: { 0, 4}) {
+                    if (caps.maxSampleCount() < numSamples) {
+                        continue;
+                    }
+
                     bool renderable = caps.isConfigRenderable(config, numSamples > 0);
 
                     GrSurfaceDesc desc;
@@ -224,17 +230,13 @@
 
                     // External on-screen render target.
                     if (renderable && kOpenGL_GrBackend == ctxInfo.backend()) {
-                        GrBackendRenderTargetDesc backendDesc;
-                        backendDesc.fWidth = kWidthHeight;
-                        backendDesc.fHeight = kWidthHeight;
-                        backendDesc.fConfig = config;
-                        backendDesc.fOrigin = origin;
-                        backendDesc.fSampleCnt = numSamples;
-                        backendDesc.fStencilBits = 8;
-                        backendDesc.fRenderTargetHandle = 0;
+                        GrGLFramebufferInfo fboInfo;
+                        fboInfo.fFBOID = 0;
+                        GrBackendRenderTarget backendRT(kWidthHeight, kWidthHeight, numSamples, 8,
+                                                        config, fboInfo);
 
                         sk_sp<GrRenderTarget> defaultFBO(
-                            provider->wrapBackendRenderTarget(backendDesc));
+                            provider->wrapBackendRenderTarget(backendRT, origin));
 
                         sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(defaultFBO));
                         check_surface(reporter, sProxy.get(), origin,
@@ -249,7 +251,7 @@
                     // Internal offscreen render target.
                     if (renderable) {
                         desc.fFlags = kRenderTarget_GrSurfaceFlag;
-                        tex.reset(provider->createTexture(desc, budgeted));
+                        tex = provider->createTexture(desc, budgeted);
                         sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget()));
 
                         sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(rt));
@@ -264,7 +266,7 @@
                     if (!tex) {
                         SkASSERT(kNone_GrSurfaceFlags == desc.fFlags );
                         desc.fSampleCnt = 0;
-                        tex.reset(provider->createTexture(desc, budgeted));
+                        tex = provider->createTexture(desc, budgeted);
                     }
 
                     sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(tex));
@@ -278,4 +280,36 @@
     }
 }
 
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ZeroSizedProxyTest, reporter, ctxInfo) {
+    GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
+
+    for (auto flags : { kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags }) {
+        for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
+            for (int width : { 0, 100 }) {
+                for (int height : { 0, 100}) {
+                    if (width && height) {
+                        continue; // not zero-sized
+                    }
+
+                    GrSurfaceDesc desc;
+                    desc.fFlags = flags;
+                    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+                    desc.fWidth = width;
+                    desc.fHeight = height;
+                    desc.fConfig = kRGBA_8888_GrPixelConfig;
+                    desc.fSampleCnt = 0;
+
+                    sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(provider,
+                                                                             desc,
+                                                                             fit,
+                                                                             SkBudgeted::kNo));
+                    REPORTER_ASSERT(reporter, !proxy);
+                }
+            }
+        }
+    }
+}
+
+#endif
+
 #endif
diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp
index 774f0af..9363e71 100644
--- a/tests/ReadPixelsTest.cpp
+++ b/tests/ReadPixelsTest.cpp
@@ -16,6 +16,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrResourceProvider.h"
 #include "SkGr.h"
 #endif
@@ -122,18 +123,7 @@
     canvas->restore();
 }
 
-#if SK_SUPPORT_GPU
-static void fill_src_texture(GrTexture* texture) {
-    SkBitmap bmp = make_src_bitmap();
-    bmp.lockPixels();
-    texture->writePixels(0, 0, DEV_W, DEV_H, kSkia8888_GrPixelConfig, bmp.getPixels(),
-                         bmp.rowBytes());
-    bmp.unlockPixels();
-}
-#endif
-
 static void fill_dst_bmp_with_init_data(SkBitmap* bitmap) {
-    SkAutoLockPixels alp(*bitmap);
     int w = bitmap->width();
     int h = bitmap->height();
     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
@@ -193,7 +183,6 @@
     if (!clippedSrcRect.intersect(srcRect)) {
         clippedSrcRect.setEmpty();
     }
-    SkAutoLockPixels alp(bitmap);
     if (kAlpha_8_SkColorType == ct) {
         for (int by = 0; by < bh; ++by) {
             for (int bx = 0; bx < bw; ++bx) {
@@ -256,8 +245,7 @@
 enum BitmapInit {
     kFirstBitmapInit = 0,
 
-    kNoPixels_BitmapInit = kFirstBitmapInit,
-    kTight_BitmapInit,
+    kTight_BitmapInit = kFirstBitmapInit,
     kRowBytes_BitmapInit,
     kRowBytesOdd_BitmapInit,
 
@@ -279,10 +267,7 @@
                         SkAlphaType at) {
     SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), ct, at);
     size_t rowBytes = 0;
-    bool alloc = true;
     switch (init) {
-        case kNoPixels_BitmapInit:
-            alloc = false;
         case kTight_BitmapInit:
             break;
         case kRowBytes_BitmapInit:
@@ -295,12 +280,7 @@
             SkASSERT(0);
             break;
     }
-
-    if (alloc) {
-        bitmap->allocPixels(info, rowBytes);
-    } else {
-        bitmap->setInfo(info, rowBytes);
-    }
+    bitmap->allocPixels(info, rowBytes);
 }
 
 static const struct {
@@ -379,7 +359,7 @@
                     fill_dst_bmp_with_init_data(&bmp);
                 }
                 uint32_t idBefore = surface->generationID();
-                bool success = canvas->readPixels(&bmp, srcRect.fLeft, srcRect.fTop);
+                bool success = canvas->readPixels(bmp, srcRect.fLeft, srcRect.fTop);
                 uint32_t idAfter = surface->generationID();
 
                 // we expect to succeed when the read isn't fully clipped
@@ -400,21 +380,6 @@
                     REPORTER_ASSERT(reporter, bmp.isNull());
                 }
             }
-            // check the old webkit version of readPixels that clips the
-            // bitmap size
-            SkBitmap wkbmp;
-            bool success = canvas->readPixels(srcRect, &wkbmp);
-            SkIRect clippedRect = DEV_RECT;
-            if (clippedRect.intersect(srcRect)) {
-                REPORTER_ASSERT(reporter, success);
-                REPORTER_ASSERT(reporter, kN32_SkColorType == wkbmp.colorType());
-                REPORTER_ASSERT(reporter, kPremul_SkAlphaType == wkbmp.alphaType());
-                check_read(reporter, wkbmp, clippedRect.fLeft,
-                           clippedRect.fTop, true, false,
-                           kN32_SkColorType, kPremul_SkAlphaType);
-            } else {
-                REPORTER_ASSERT(reporter, !success);
-            }
         }
     }
 }
@@ -436,8 +401,8 @@
 #endif
 
 #if SK_SUPPORT_GPU
-static void test_readpixels_texture(skiatest::Reporter* reporter, GrTexture* texture) {
-    fill_src_texture(texture);
+static void test_readpixels_texture(skiatest::Reporter* reporter,
+                                    sk_sp<GrSurfaceContext> sContext) {
     for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) {
         const SkIRect& srcRect = gReadPixelsTestRects[rect];
         for (BitmapInit bmi = kFirstBitmapInit; bmi <= kLast_BitmapInit; bmi = nextBMI(bmi)) {
@@ -452,17 +417,13 @@
                 // Try doing the read directly from a non-renderable texture
                 if (startsWithPixels) {
                     fill_dst_bmp_with_init_data(&bmp);
-                    GrPixelConfig dstConfig =
-                            SkImageInfo2GrPixelConfig(bmp.info(), *texture->getContext()->caps());
                     uint32_t flags = 0;
                     if (gReadPixelsConfigs[c].fAlphaType == kUnpremul_SkAlphaType) {
-                        flags = GrContext::kUnpremul_PixelOpsFlag;
+                        flags = GrContextPriv::kUnpremul_PixelOpsFlag;
                     }
-                    bmp.lockPixels();
-                    bool success = texture->readPixels(srcRect.fLeft, srcRect.fTop, bmp.width(),
-                                                       bmp.height(), dstConfig, bmp.getPixels(),
-                                                       bmp.rowBytes(), flags);
-                    bmp.unlockPixels();
+                    bool success = sContext->readPixels(bmp.info(), bmp.getPixels(),
+                                                        bmp.rowBytes(),
+                                                        srcRect.fLeft, srcRect.fTop, flags);
                     check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
                                success, true,
                                gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
@@ -471,7 +432,12 @@
         }
     }
 }
+
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Texture, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    SkBitmap bmp = make_src_bitmap();
+
     // On the GPU we will also try reading back from a non-renderable texture.
     for (auto origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
         for (auto flags : {kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag}) {
@@ -481,9 +447,16 @@
             desc.fHeight = DEV_H;
             desc.fConfig = kSkia8888_GrPixelConfig;
             desc.fOrigin = origin;
-            sk_sp<GrTexture> texture(ctxInfo.grContext()->resourceProvider()->createTexture(desc,
-                SkBudgeted::kNo));
-            test_readpixels_texture(reporter, texture.get());
+
+            sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                       desc, SkBudgeted::kNo,
+                                                                       bmp.getPixels(),
+                                                                       bmp.rowBytes());
+
+            sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                        std::move(proxy), nullptr);
+
+            test_readpixels_texture(reporter, std::move(sContext));
         }
     }
 }
@@ -557,7 +530,7 @@
 
 static void test_conversion(skiatest::Reporter* r, const SkImageInfo& dstInfo,
                             const SkImageInfo& srcInfo) {
-    if (!SkImageInfoIsValid(srcInfo)) {
+    if (!SkImageInfoIsValidRenderingCS(srcInfo)) {
         return;
     }
 
diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp
index ac34b9d..531159f 100644
--- a/tests/ReadWriteAlphaTest.cpp
+++ b/tests/ReadWriteAlphaTest.cpp
@@ -142,13 +142,17 @@
         }
     }
 
+    const SkImageInfo dstInfo = SkImageInfo::Make(X_SIZE, Y_SIZE,
+                                                  kAlpha_8_SkColorType,
+                                                  kPremul_SkAlphaType);
+
     // Attempt to read back just alpha from a RGBA/BGRA texture. Once with a texture-only src and
     // once with a render target.
-    for (auto cfg : kRGBAConfigs) {
+    for (auto config : kRGBAConfigs) {
         for (int rt = 0; rt < 2; ++rt) {
             GrSurfaceDesc desc;
             desc.fFlags     = rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
-            desc.fConfig    = cfg;
+            desc.fConfig    = config;
             desc.fWidth     = X_SIZE;
             desc.fHeight    = Y_SIZE;
 
@@ -159,9 +163,10 @@
                     rgbaData[y * X_SIZE + x] = GrColorPackRGBA(6, 7, 8, alphaData[y * X_SIZE + x]);
                 }
             }
-            sk_sp<GrTexture> texture(
-                context->resourceProvider()->createTexture(desc, SkBudgeted::kNo, rgbaData, 0));
-            if (!texture) {
+            sk_sp<GrTextureProxy> proxy =
+                GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo,
+                                             rgbaData, 0);
+            if (!proxy) {
                 // We always expect to be able to create a RGBA texture
                 if (!rt  && kRGBA_8888_GrPixelConfig == desc.fConfig) {
                     ERRORF(reporter, "Failed to create RGBA texture.");
@@ -169,6 +174,9 @@
                 continue;
             }
 
+            sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                       std::move(proxy), nullptr);
+
             for (auto rowBytes : kRowBytes) {
                 size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE;
 
@@ -177,9 +185,7 @@
                 memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE);
 
                 // read the texture back
-                bool result = texture->readPixels(0, 0, desc.fWidth, desc.fHeight,
-                                                  kAlpha_8_GrPixelConfig,
-                                                  readback.get(), rowBytes);
+                bool result = sContext->readPixels(dstInfo, readback.get(), rowBytes, 0, 0);
                 REPORTER_ASSERT_MESSAGE(reporter, result, "8888 readPixels failed");
 
                 // make sure the original & read back versions match
diff --git a/tests/RecordOptsTest.cpp b/tests/RecordOptsTest.cpp
index 09e774e..cb80987 100644
--- a/tests/RecordOptsTest.cpp
+++ b/tests/RecordOptsTest.cpp
@@ -190,10 +190,20 @@
 
     // saveLayer w/ backdrop should NOT go away
     sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
-    recorder.saveLayer({ nullptr, nullptr, filter.get(), 0});
+    recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0});
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
     assert_savelayer_draw_restore(r, &record, 18, false);
+
+    // saveLayer w/ clip mask should also NOT go away
+    {
+        sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(10, 10));
+        recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot().get(),
+                             nullptr, 0});
+            recorder.drawRect(draw, opaqueDrawPaint);
+        recorder.restore();
+        assert_savelayer_draw_restore(r, &record, 21, false);
+    }
 }
 #endif
 
@@ -265,10 +275,12 @@
                             for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
                                 bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
 
-                                recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, 0});
+                                recorder.saveLayer({firstBounds[i], firstPaints[j], outerF,
+                                                    nullptr, nullptr, 0});
                                 recorder.save();
                                 recorder.clipRect(clip);
-                                recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, 0});
+                                recorder.saveLayer({secondBounds[k], secondPaints[m], innerF,
+                                                    nullptr, nullptr, 0});
                                 recorder.restore();
                                 recorder.restore();
                                 recorder.restore();
diff --git a/tests/RecordingXfermodeTest.cpp b/tests/RecordingXfermodeTest.cpp
index db4262c..20dccc6 100644
--- a/tests/RecordingXfermodeTest.cpp
+++ b/tests/RecordingXfermodeTest.cpp
@@ -7,6 +7,7 @@
 
 #include "Test.h"
 
+#include "../include/core/SkBitmap.h"
 #include "../include/core/SkCanvas.h"
 #include "../include/core/SkPicture.h"
 #include "../include/core/SkStream.h"
diff --git a/tests/RectangleTextureTest.cpp b/tests/RectangleTextureTest.cpp
index 41ee7b3..9c85ad4 100644
--- a/tests/RectangleTextureTest.cpp
+++ b/tests/RectangleTextureTest.cpp
@@ -13,24 +13,25 @@
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrRenderTargetContext.h"
+#include "GrTest.h"
 #include "gl/GLTestContext.h"
 #include "gl/GrGLGpu.h"
 #include "gl/GrGLUtil.h"
 
 // skbug.com/5932
 static void test_basic_draw_as_src(skiatest::Reporter* reporter, GrContext* context,
-                                   sk_sp<GrSurfaceProxy> rectProxy, uint32_t expectedPixelValues[]) {
+                                   sk_sp<GrTextureProxy> rectProxy, uint32_t expectedPixelValues[]) {
     sk_sp<GrRenderTargetContext> rtContext(
-            context->makeRenderTargetContext(SkBackingFit::kExact, rectProxy->width(),
-                                             rectProxy->height(), rectProxy->config(),
-                                             nullptr));
+            context->makeDeferredRenderTargetContext(SkBackingFit::kExact, rectProxy->width(),
+                                                     rectProxy->height(), rectProxy->config(),
+                                                     nullptr));
     for (auto filter : {GrSamplerParams::kNone_FilterMode,
                         GrSamplerParams::kBilerp_FilterMode,
                         GrSamplerParams::kMipMap_FilterMode}) {
         rtContext->clear(nullptr, 0xDDCCBBAA, true);
         sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(
                                                         context->resourceProvider(),
-                                                        sk_ref_sp(rectProxy->asTextureProxy()),
+                                                        rectProxy,
                                                         nullptr,
                                                         SkMatrix::I(), filter));
         GrPaint paint;
@@ -99,7 +100,9 @@
         }
     }
 
-    for (int origin = 0; origin < 2; ++origin) {
+    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
+        bool useBLOrigin = kBottomLeft_GrSurfaceOrigin == origin;
+
         GrGLuint rectTexID = glContext->createTextureRectangle(kWidth, kHeight, GR_GL_RGBA,
                                                                GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
                                                                pixels);
@@ -116,25 +119,19 @@
         rectangleInfo.fID = rectTexID;
         rectangleInfo.fTarget = GR_GL_TEXTURE_RECTANGLE;
 
-        GrBackendTextureDesc rectangleDesc;
-        rectangleDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
-        rectangleDesc.fConfig = kRGBA_8888_GrPixelConfig;
-        rectangleDesc.fWidth = kWidth;
-        rectangleDesc.fHeight = kHeight;
-        rectangleDesc.fOrigin = origin ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
-        rectangleDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&rectangleInfo);
+        GrBackendTexture rectangleTex(kWidth, kHeight, kRGBA_8888_GrPixelConfig, rectangleInfo);
 
         GrColor refPixels[kWidth * kHeight];
-        bool flipRef = rectangleDesc.fOrigin == kBottomLeft_GrSurfaceOrigin;
         for (int y = 0; y < kHeight; ++y) {
             for (int x = 0; x < kWidth; ++x) {
-                int y0 = flipRef ? kHeight - y - 1 : y;
+                int y0 = useBLOrigin ? kHeight - y - 1 : y;
                 refPixels[y * kWidth + x] = pixels[y0 * kWidth + x];
             }
         }
 
-        sk_sp<GrSurfaceProxy> rectProxy = GrSurfaceProxy::MakeWrappedBackend(context,
-                                                                             rectangleDesc);
+        sk_sp<GrTextureProxy> rectProxy = GrSurfaceProxy::MakeWrappedBackend(context,
+                                                                             rectangleTex,
+                                                                             origin);
         if (!rectProxy) {
             ERRORF(reporter, "Error creating proxy for rectangle texture.");
             GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID));
diff --git a/tests/RenderTargetContextTest.cpp b/tests/RenderTargetContextTest.cpp
index 632ae82..dbb4185 100644
--- a/tests/RenderTargetContextTest.cpp
+++ b/tests/RenderTargetContextTest.cpp
@@ -9,23 +9,21 @@
 
 #include "Test.h"
 
+// MDB TODO: With the move of the discard calls to the RenderTargetContext, deferred RTCs are being
+// instantiated early. This test can be re-enabled once discards do not force an instantiation
+// (i.e., when renderTargetProxies carry the op IORefs)
+#if 0
+
 #if SK_SUPPORT_GPU
 #include "GrTextureProxy.h"
 #include "GrRenderTargetContext.h"
 
 static const int kSize = 64;
 
-static sk_sp<GrRenderTargetContext> get_rtc(GrContext* ctx, bool wrapped) {
-
-    if (wrapped) {
-        return ctx->makeRenderTargetContext(SkBackingFit::kExact,
-                                            kSize, kSize,
-                                            kRGBA_8888_GrPixelConfig, nullptr);
-    } else {
-        return ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact,
-                                                    kSize, kSize,
-                                                    kRGBA_8888_GrPixelConfig, nullptr);
-    }
+static sk_sp<GrRenderTargetContext> get_rtc(GrContext* ctx) {
+    return ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact,
+                                                kSize, kSize,
+                                                kRGBA_8888_GrPixelConfig, nullptr);
 }
 
 static void check_is_wrapped_status(skiatest::Reporter* reporter,
@@ -42,29 +40,10 @@
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RenderTargetContextTest, reporter, ctxInfo) {
     GrContext* ctx = ctxInfo.grContext();
 
-    // A wrapped rtCtx's textureProxy is also wrapped
-    {
-        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx, true));
-        check_is_wrapped_status(reporter, rtCtx.get(), true);
-    }
-
-    // A deferred rtCtx's textureProxy is also deferred and GrRenderTargetContext::instantiate()
-    // swaps both from deferred to wrapped
-    {
-        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx, false));
-
-        check_is_wrapped_status(reporter, rtCtx.get(), false);
-
-        GrRenderTarget* rt = rtCtx->instantiate();
-        REPORTER_ASSERT(reporter, rt);
-
-        check_is_wrapped_status(reporter, rtCtx.get(), true);
-    }
-
     // Calling instantiate on a GrRenderTargetContext's textureProxy also instantiates the
     // GrRenderTargetContext
     {
-        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx, false));
+        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx));
 
         check_is_wrapped_status(reporter, rtCtx.get(), false);
 
@@ -79,7 +58,7 @@
 
     // readPixels switches a deferred rtCtx to wrapped
     {
-        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx, false));
+        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx));
 
         check_is_wrapped_status(reporter, rtCtx.get(), false);
 
@@ -98,3 +77,4 @@
     // GrRenderTargetContext
 }
 #endif
+#endif
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index a7a2b45..460d4ee 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -11,6 +11,7 @@
 #if SK_SUPPORT_GPU
 #include <thread>
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrContextFactory.h"
 #include "GrGpu.h"
 #include "GrGpuResourceCacheAccess.h"
@@ -66,7 +67,7 @@
 
     for (int i = 0; i < 100; ++i) {
         canvas->drawBitmap(src, 0, 0);
-        canvas->readPixels(size, &readback);
+        canvas->readPixels(readback, 0, 0);
 
         // "modify" the src texture
         src.notifyPixelsChanged();
@@ -100,6 +101,9 @@
     smallDesc.fHeight = 4;
     smallDesc.fSampleCnt = 0;
 
+    if (context->caps()->avoidStencilBuffers()) {
+        return;
+    }
     GrResourceProvider* resourceProvider = context->resourceProvider();
     // Test that two budgeted RTs with the same desc share a stencil buffer.
     sk_sp<GrTexture> smallRT0(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes));
@@ -184,7 +188,7 @@
             smallMSAART0 && smallMSAART0->asRenderTarget() &&
             smallMSAART0->asRenderTarget()->numColorSamples() < 8) {
             smallMSAADesc.fSampleCnt = 8;
-            smallMSAART1.reset(resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo));
+            smallMSAART1 = resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo);
             sk_sp<GrTexture> smallMSAART1(
                 resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo));
             if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
@@ -208,64 +212,53 @@
         return;
     }
 
-    GrBackendObject texHandles[3];
+    GrBackendObject texHandles[2];
     static const int kW = 100;
     static const int kH = 100;
 
     texHandles[0] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
     texHandles[1] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
-    texHandles[2] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
 
     context->resetContext();
 
-    GrBackendTextureDesc desc;
-    desc.fConfig = kBGRA_8888_GrPixelConfig;
-    desc.fWidth = kW;
-    desc.fHeight = kH;
-
-    desc.fTextureHandle = texHandles[0];
+    GrBackendTexture backendTex1 = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                                kW,
+                                                                kH,
+                                                                kRGBA_8888_GrPixelConfig,
+                                                                texHandles[0]);
     sk_sp<GrTexture> borrowed(context->resourceProvider()->wrapBackendTexture(
-                              desc, kBorrow_GrWrapOwnership));
+                              backendTex1, kTopLeft_GrSurfaceOrigin, kNone_GrBackendTextureFlag, 0,
+                              kBorrow_GrWrapOwnership));
 
-    desc.fTextureHandle = texHandles[1];
+    GrBackendTexture backendTex2 = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                                kW,
+                                                                kH,
+                                                                kRGBA_8888_GrPixelConfig,
+                                                                texHandles[1]);
     sk_sp<GrTexture> adopted(context->resourceProvider()->wrapBackendTexture(
-                             desc, kAdopt_GrWrapOwnership));
+                             backendTex2, kTopLeft_GrSurfaceOrigin, kNone_GrBackendTextureFlag, 0,
+                             kAdopt_GrWrapOwnership));
 
-    desc.fTextureHandle = texHandles[2];
-    sk_sp<GrTexture> adoptedAndCached(context->resourceProvider()->wrapBackendTexture(
-                                      desc, kAdoptAndCache_GrWrapOwnership));
-
-    REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr &&
-                              adoptedAndCached != nullptr);
-    if (!borrowed || !adopted || !adoptedAndCached) {
+    REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr);
+    if (!borrowed || !adopted) {
         return;
     }
 
     borrowed.reset(nullptr);
     adopted.reset(nullptr);
-    adoptedAndCached.reset(nullptr);
 
     context->flush();
 
     bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[0]);
     bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[1]);
-    bool adoptedAndCachedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[2]);
 
     REPORTER_ASSERT(reporter, borrowedIsAlive);
     REPORTER_ASSERT(reporter, !adoptedIsAlive);
-    REPORTER_ASSERT(reporter, adoptedAndCachedIsAlive); // Still alive because it's in the cache
 
     gpu->deleteTestingOnlyBackendTexture(texHandles[0], !borrowedIsAlive);
     gpu->deleteTestingOnlyBackendTexture(texHandles[1], !adoptedIsAlive);
-    // We can't delete texHandles[2] - we've given control of the lifetime to the context/cache
 
     context->resetContext();
-
-    // Purge the cache. This should force texHandles[2] to be deleted
-    context->getResourceCache()->purgeAllUnlocked();
-    adoptedAndCachedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[2]);
-    REPORTER_ASSERT(reporter, !adoptedAndCachedIsAlive);
-    gpu->deleteTestingOnlyBackendTexture(texHandles[2], !adoptedAndCachedIsAlive);
 }
 
 class TestResource : public GrGpuResource {
@@ -460,11 +453,15 @@
             new TestResource(context->getGpu(), SkBudgeted::kNo);
     unbudgeted->setSize(13);
 
-    // Make sure we can't add a unique key to the wrapped resource
+    // Make sure we can add a unique key to the wrapped resource
     GrUniqueKey uniqueKey2;
     make_unique_key<0>(&uniqueKey2, 1);
     wrapped->resourcePriv().setUniqueKey(uniqueKey2);
-    REPORTER_ASSERT(reporter, nullptr == cache->findAndRefUniqueResource(uniqueKey2));
+    GrGpuResource* wrappedViaKey = cache->findAndRefUniqueResource(uniqueKey2);
+    REPORTER_ASSERT(reporter, wrappedViaKey != nullptr);
+
+    // Remove the extra ref we just added.
+    wrappedViaKey->unref();
 
     // Make sure sizes are as we expect
     REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
@@ -474,6 +471,7 @@
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
                               cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
     // Our refs mean that the resources are non purgeable.
     cache->purgeAllUnlocked();
@@ -484,43 +482,51 @@
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
                               cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
     // Unreffing the wrapped resource should free it right away.
     wrapped->unref();
     REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
                               unbudgeted->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
     // Now try freeing the budgeted resources first
     wrapped = TestResource::CreateWrapped(context->getGpu());
     scratch->setSize(12);
     unique->unref();
+    REPORTER_ASSERT(reporter, 11 == cache->getPurgeableBytes());
     cache->purgeAllUnlocked();
     REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() +
                               unbudgeted->gpuMemorySize() == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
     scratch->unref();
+    REPORTER_ASSERT(reporter, 12 == cache->getPurgeableBytes());
     cache->purgeAllUnlocked();
     REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() ==
                               cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
     wrapped->unref();
     REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
     unbudgeted->unref();
     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 }
 
 static void test_unbudgeted(skiatest::Reporter* reporter) {
@@ -546,6 +552,7 @@
     REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes());
 
     unique = new TestResource(context->getGpu());
     unique->setSize(11);
@@ -555,6 +562,7 @@
     REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
 
     size_t large = 2 * cache->getResourceBytes();
     unbudgeted = new TestResource(context->getGpu(), SkBudgeted::kNo, large);
@@ -562,30 +570,35 @@
     REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
 
     unbudgeted->unref();
     REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
 
     wrapped = TestResource::CreateWrapped(context->getGpu(), large);
     REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
 
     wrapped->unref();
     REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
 
     cache->purgeAllUnlocked();
     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
     REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
     REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 }
 
 // This method can't be static because it needs to friended in GrGpuResource::CacheAccess.
@@ -612,6 +625,7 @@
         REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
         REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
         REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
 
         // Once it is unrefed, it should become available as scratch.
         resource->unref();
@@ -619,6 +633,7 @@
         REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
         REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
         REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, size == cache->getPurgeableBytes());
         resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0));
         REPORTER_ASSERT(reporter, resource);
         REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
@@ -636,6 +651,7 @@
             REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
             REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
             REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
+            REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
             REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid());
             REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
             REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
@@ -646,6 +662,7 @@
             REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
             REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
             REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+            REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
         }
     }
 }
@@ -1343,6 +1360,114 @@
     }
 }
 
+static void test_partial_purge(skiatest::Reporter* reporter) {
+    Mock mock(6, 100);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    enum TestsCase {
+        kOnlyScratch_TestCase = 0,
+        kPartialScratch_TestCase = 1,
+        kAllScratch_TestCase = 2,
+        kPartial_TestCase = 3,
+        kAll_TestCase = 4,
+        kNone_TestCase = 5,
+        kEndTests_TestCase = kNone_TestCase + 1
+    };
+
+    for (int testCase = 0; testCase < kEndTests_TestCase; testCase++) {
+
+        GrUniqueKey key1, key2, key3;
+        make_unique_key<0>(&key1, 1);
+        make_unique_key<0>(&key2, 2);
+        make_unique_key<0>(&key3, 3);
+
+        // Add three unique resources to the cache.
+        TestResource *unique1 = new TestResource(context->getGpu());
+        TestResource *unique2 = new TestResource(context->getGpu());
+        TestResource *unique3 = new TestResource(context->getGpu());
+
+        unique1->resourcePriv().setUniqueKey(key1);
+        unique2->resourcePriv().setUniqueKey(key2);
+        unique3->resourcePriv().setUniqueKey(key3);
+
+        unique1->setSize(10);
+        unique2->setSize(11);
+        unique3->setSize(12);
+
+        // Add two scratch resources to the cache.
+        TestResource *scratch1 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                             TestResource::kA_SimulatedProperty);
+        TestResource *scratch2 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                             TestResource::kB_SimulatedProperty);
+        scratch1->setSize(13);
+        scratch2->setSize(14);
+
+
+        REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+        // Add resources to the purgeable queue
+        unique1->unref();
+        scratch1->unref();
+        unique2->unref();
+        scratch2->unref();
+        unique3->unref();
+
+        REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes());
+
+        switch(testCase) {
+            case kOnlyScratch_TestCase: {
+                context->purgeUnlockedResources(14, true);
+                REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 33 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kPartialScratch_TestCase: {
+                context->purgeUnlockedResources(3, true);
+                REPORTER_ASSERT(reporter, 4 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 47 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kAllScratch_TestCase: {
+                context->purgeUnlockedResources(50, true);
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kPartial_TestCase: {
+                context->purgeUnlockedResources(13, false);
+                REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 37 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kAll_TestCase: {
+                context->purgeUnlockedResources(50, false);
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kNone_TestCase: {
+                context->purgeUnlockedResources(0, true);
+                context->purgeUnlockedResources(0, false);
+                REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
+                REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes());
+                break;
+            }
+        };
+
+        // ensure all are purged before the next
+        context->purgeAllUnlockedResources();
+        REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    }
+}
+
 static void test_large_resource_count(skiatest::Reporter* reporter) {
     // Set the cache size to double the resource count because we're going to create 2x that number
     // resources, using two different key domains. Add a little slop to the bytes because we resize
@@ -1372,6 +1497,7 @@
     }
 
     REPORTER_ASSERT(reporter, TestResource::NumAlive() == 2 * kResourceCnt);
+    REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 2 * kResourceCnt);
     REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 2 * kResourceCnt);
     REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 2 * kResourceCnt);
     REPORTER_ASSERT(reporter, cache->getResourceBytes() == 2 * kResourceCnt);
@@ -1387,6 +1513,7 @@
 
     cache->purgeAllUnlocked();
     REPORTER_ASSERT(reporter, TestResource::NumAlive() == 0);
+    REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 0);
     REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 0);
     REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 0);
     REPORTER_ASSERT(reporter, cache->getResourceBytes() == 0);
@@ -1496,6 +1623,7 @@
     test_timestamp_wrap(reporter);
     test_flush(reporter);
     test_time_purge(reporter);
+    test_partial_purge(reporter);
     test_large_resource_count(reporter);
     test_custom_data(reporter);
     test_abandoned(reporter);
@@ -1514,13 +1642,13 @@
     desc.fConfig = kRGBA_8888_GrPixelConfig;
     desc.fSampleCnt = sampleCnt;
 
-    return sk_sp<GrTexture>(provider->createTexture(desc, SkBudgeted::kYes));
+    return provider->createTexture(desc, SkBudgeted::kYes);
 }
 
-static sk_sp<GrTexture> make_mipmap_texture(GrResourceProvider* provider,
-                                            GrSurfaceFlags flags,
-                                            int width, int height,
-                                            int sampleCnt) {
+static sk_sp<GrTextureProxy> make_mipmap_proxy(GrResourceProvider* provider,
+                                               GrSurfaceFlags flags,
+                                               int width, int height,
+                                               int sampleCnt) {
     SkBitmap bm;
 
     bm.allocN32Pixels(width, height, true);
@@ -1552,8 +1680,7 @@
     desc.fSampleCnt = sampleCnt;
     desc.fIsMipMapped = true;
 
-    return sk_sp<GrTexture>(provider->createMipMappedTexture(desc, SkBudgeted::kYes,
-                                                             texels.get(), mipLevelCount));
+    return provider->createMipMappedTexture(desc, SkBudgeted::kYes, texels.get(), mipLevelCount);
 }
 
 // Exercise GrSurface::gpuMemorySize for different combos of MSAA, RT-only,
@@ -1564,42 +1691,49 @@
 
     static const int kSize = 64;
 
-    sk_sp<GrTexture> tex;
-
     // Normal versions
-    tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
-    size_t size = tex->gpuMemorySize();
-    REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
+    {
+        sk_sp<GrTexture> tex;
 
-    if (context->caps()->maxSampleCount() >= 4) {
-        tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 4);
+        tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
+        size_t size = tex->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
+
+        if (context->caps()->maxSampleCount() >= 4) {
+            tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 4);
+            size = tex->gpuMemorySize();
+            REPORTER_ASSERT(reporter, kSize*kSize*4 == size ||    // msaa4 failed
+                                      kSize*kSize*4*4 == size ||  // auto-resolving
+                                      kSize*kSize*4*5 == size);   // explicit resolve buffer
+        }
+
+        tex = make_normal_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
         size = tex->gpuMemorySize();
-        REPORTER_ASSERT(reporter, kSize*kSize*4 == size ||    // msaa4 failed
-                                  kSize*kSize*4*4 == size ||  // auto-resolving
-                                  kSize*kSize*4*5 == size);   // explicit resolve buffer
+        REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
     }
 
-    tex = make_normal_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
-    size = tex->gpuMemorySize();
-    REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
 
     // Mipmapped versions
-    tex = make_mipmap_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
-    size = tex->gpuMemorySize();
-    REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
+    if (context->caps()->mipMapSupport()) {
+        sk_sp<GrTextureProxy> proxy;
 
-    if (context->caps()->maxSampleCount() >= 4) {
-        tex = make_mipmap_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 4);
-        size = tex->gpuMemorySize();
-        REPORTER_ASSERT(reporter, 
+        proxy = make_mipmap_proxy(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
+        size_t size = proxy->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
+
+        if (context->caps()->maxSampleCount() >= 4) {
+            proxy = make_mipmap_proxy(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 4);
+            size = proxy->gpuMemorySize();
+            REPORTER_ASSERT(reporter,
                             kSize*kSize*4+(kSize*kSize*4)/3 == size ||   // msaa4 failed
                             kSize*kSize*4*4+(kSize*kSize*4)/3 == size || // auto-resolving
                             kSize*kSize*4*5+(kSize*kSize*4)/3 == size);  // explicit resolve buffer
-    }
+        }
 
-    tex = make_mipmap_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
-    size = tex->gpuMemorySize();
-    REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
+        proxy = make_mipmap_proxy(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
+        size = proxy->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
+    }
 }
 
 #endif
diff --git a/tests/SRGBMipMapTest.cpp b/tests/SRGBMipMapTest.cpp
index 89a47c9..33d99f2 100644
--- a/tests/SRGBMipMapTest.cpp
+++ b/tests/SRGBMipMapTest.cpp
@@ -13,6 +13,7 @@
 #include "GrRenderTargetContext.h"
 #include "GrResourceProvider.h"
 #include "SkCanvas.h"
+#include "SkGr.h"
 #include "SkSurface.h"
 #include "gl/GrGLGpu.h"
 
@@ -45,16 +46,19 @@
     }
 }
 
-void read_and_check_pixels(skiatest::Reporter* reporter, GrTexture* texture, U8CPU expected,
+void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context,
+                           U8CPU expected, const SkImageInfo& dstInfo,
                            U8CPU error, const char* subtestName) {
-    int w = texture->width();
-    int h = texture->height();
+    int w = dstInfo.width();
+    int h = dstInfo.height();
     SkAutoTMalloc<uint32_t> readData(w * h);
     memset(readData.get(), 0, sizeof(uint32_t) * w * h);
-    if (!texture->readPixels(0, 0, w, h, texture->config(), readData.get())) {
+
+    if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) {
         ERRORF(reporter, "Could not read pixels for %s.", subtestName);
         return;
     }
+
     for (int j = 0; j < h; ++j) {
         for (int i = 0; i < w; ++i) {
             uint32_t read = readData[j * w + i];
@@ -112,6 +116,12 @@
     const U8CPU expectedLinear = srgb60 / 2;
     const U8CPU error = 10;
 
+    const SkImageInfo iiSRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType,
+                                                  kPremul_SkAlphaType,
+                                                  SkColorSpace::MakeSRGB());
+    const SkImageInfo iiRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType,
+                                                 kPremul_SkAlphaType);
+
     // Create our test texture
     GrSurfaceDesc desc;
     desc.fFlags = kNone_GrSurfaceFlags;
@@ -126,9 +136,9 @@
 
     // Create two render target contexts (L32 and S32)
     sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
-    sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeDeferredRenderTargetContext(
         SkBackingFit::kExact, rtS, rtS, kRGBA_8888_GrPixelConfig, nullptr);
-    sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeRenderTargetContext(
+    sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeDeferredRenderTargetContext(
         SkBackingFit::kExact, rtS, rtS, kSRGBA_8888_GrPixelConfig, std::move(srgbColorSpace));
 
     SkRect rect = SkRect::MakeWH(SkIntToScalar(rtS), SkIntToScalar(rtS));
@@ -142,7 +152,7 @@
     // 1) Draw texture to S32 surface (should generate/use sRGB mips)
     paint.setGammaCorrect(true);
     s32RenderTargetContext->drawRect(noClip, GrPaint(paint), GrAA::kNo, SkMatrix::I(), rect);
-    read_and_check_pixels(reporter, s32RenderTargetContext->asTexture().get(), expectedSRGB, error,
+    read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error,
                           "first render of sRGB");
 
     // 2) Draw texture to L32 surface (should generate/use linear mips)
@@ -161,14 +171,14 @@
     GrGLGpu* glGpu = static_cast<GrGLGpu*>(context->getGpu());
     if (glGpu->glCaps().srgbDecodeDisableSupport() &&
         glGpu->glCaps().srgbDecodeDisableAffectsMipmaps()) {
-        read_and_check_pixels(reporter, l32RenderTargetContext->asTexture().get(), expectedLinear,
+        read_and_check_pixels(reporter, l32RenderTargetContext.get(), expectedLinear, iiRGBA,
                               error, "re-render as linear");
     }
 
     // 3) Go back to sRGB
     paint.setGammaCorrect(true);
     s32RenderTargetContext->drawRect(noClip, std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
-    read_and_check_pixels(reporter, s32RenderTargetContext->asTexture().get(), expectedSRGB, error,
+    read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error,
                           "re-render as sRGB");
 }
 #endif
diff --git a/tests/SRGBReadWritePixelsTest.cpp b/tests/SRGBReadWritePixelsTest.cpp
index 5addc2e..9038b29 100644
--- a/tests/SRGBReadWritePixelsTest.cpp
+++ b/tests/SRGBReadWritePixelsTest.cpp
@@ -9,8 +9,11 @@
 #if SK_SUPPORT_GPU
 #include "GrCaps.h"
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrResourceProvider.h"
+#include "GrSurfaceContext.h"
 #include "SkCanvas.h"
+#include "SkGr.h"
 #include "SkSurface.h"
 
 // using anonymous namespace because these functions are used as template params.
@@ -112,17 +115,20 @@
 
 typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error);
 
-void read_and_check_pixels(skiatest::Reporter* reporter, GrTexture* texture, uint32_t* origData,
-                           GrPixelConfig readConfig, CheckFn checker, float error,
+void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context,
+                           uint32_t* origData,
+                           const SkImageInfo& dstInfo, CheckFn checker, float error,
                            const char* subtestName) {
-    int w = texture->width();
-    int h = texture->height();
+    int w = dstInfo.width();
+    int h = dstInfo.height();
     SkAutoTMalloc<uint32_t> readData(w * h);
     memset(readData.get(), 0, sizeof(uint32_t) * w * h);
-    if (!texture->readPixels(0, 0, w, h, readConfig, readData.get())) {
+
+    if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) {
         ERRORF(reporter, "Could not read pixels for %s.", subtestName);
         return;
     }
+
     for (int j = 0; j < h; ++j) {
         for (int i = 0; i < w; ++i) {
             uint32_t orig = origData[j * w + i];
@@ -156,32 +162,40 @@
         }
     }
 
+    const SkImageInfo iiSRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType,
+                                                  kPremul_SkAlphaType,
+                                                  SkColorSpace::MakeSRGB());
+    const SkImageInfo iiRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType,
+                                                 kPremul_SkAlphaType);
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
     desc.fWidth = kW;
     desc.fHeight = kH;
     desc.fConfig = kSRGBA_8888_GrPixelConfig;
     if (context->caps()->isConfigRenderable(desc.fConfig, false) &&
         context->caps()->isConfigTexturable(desc.fConfig)) {
-        sk_sp<GrTexture> tex(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo));
-        if (!tex) {
-            ERRORF(reporter, "Could not create SRGBA texture.");
+
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext(
+                                                                    desc, SkBackingFit::kExact,
+                                                                    SkBudgeted::kNo);
+        if (!sContext) {
+            ERRORF(reporter, "Could not create SRGBA surface context.");
             return;
         }
 
         float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f  : 0.5f;
 
         // Write srgba data and read as srgba and then as rgba
-        if (tex->writePixels(0, 0, kW, kH, kSRGBA_8888_GrPixelConfig, origData)) {
+        if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) {
             // For the all-srgba case, we allow a small error only for devices that have
             // precision variation because the srgba data gets converted to linear and back in
             // the shader.
-            float smallError = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.f :
-                    0.0f;
-            read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig,
+            float smallError = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.f : 0.0f;
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
                                   check_srgb_to_linear_to_srgb_conversion, smallError,
                                   "write/read srgba to srgba texture");
-            read_and_check_pixels(reporter, tex.get(), origData, kRGBA_8888_GrPixelConfig,
+            read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA,
                                   check_srgb_to_linear_conversion, error,
                                   "write srgba/read rgba with srgba texture");
         } else {
@@ -189,12 +203,12 @@
         }
 
         // Now verify that we can write linear data
-        if (tex->writePixels(0, 0, kW, kH, kRGBA_8888_GrPixelConfig, origData)) {
+        if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) {
             // We allow more error on GPUs with lower precision shader variables.
-            read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig,
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
                                   check_linear_to_srgb_conversion, error,
                                   "write rgba/read srgba with srgba texture");
-            read_and_check_pixels(reporter, tex.get(), origData, kRGBA_8888_GrPixelConfig,
+            read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA,
                                   check_linear_to_srgb_to_linear_conversion, error,
                                   "write/read rgba with srgba texture");
         } else {
@@ -202,27 +216,37 @@
         }
 
         desc.fConfig = kRGBA_8888_GrPixelConfig;
-        tex.reset(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo));
-        if (!tex) {
-            ERRORF(reporter, "Could not create RGBA texture.");
+        sContext = context->contextPriv().makeDeferredSurfaceContext(desc, SkBackingFit::kExact,
+                                                                     SkBudgeted::kNo);
+        if (!sContext) {
+            ERRORF(reporter, "Could not create RGBA surface context.");
             return;
         }
 
         // Write srgba data to a rgba texture and read back as srgba and rgba
-        if (tex->writePixels(0, 0, kW, kH, kSRGBA_8888_GrPixelConfig, origData)) {
-            read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig,
+        if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) {
+#if 0
+            // We don't support this conversion (read from untagged source into tagged destination.
+            // If we decide there is a meaningful way to implement this, restore this test.
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
                                   check_srgb_to_linear_to_srgb_conversion, error,
                                   "write/read srgba to rgba texture");
-            read_and_check_pixels(reporter, tex.get(), origData, kRGBA_8888_GrPixelConfig,
-                                  check_srgb_to_linear_conversion, error,
+#endif
+            // We expect the sRGB -> linear write to do no sRGB conversion (to match the behavior of
+            // drawing tagged sources). skbug.com/6547. So the data we read should still contain
+            // sRGB encoded values.
+            //
+            // srgb_to_linear_to_srgb is a proxy for the expected identity transform.
+            read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA,
+                                  check_srgb_to_linear_to_srgb_conversion, error,
                                   "write srgba/read rgba to rgba texture");
         } else {
             ERRORF(reporter, "Could not write srgba data to rgba texture.");
         }
 
         // Write rgba data to a rgba texture and read back as srgba
-        if (tex->writePixels(0, 0, kW, kH, kRGBA_8888_GrPixelConfig, origData)) {
-            read_and_check_pixels(reporter, tex.get(), origData, kSRGBA_8888_GrPixelConfig,
+        if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) {
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
                                   check_linear_to_srgb_conversion, 1.2f,
                                   "write rgba/read srgba to rgba texture");
         } else {
diff --git a/tests/SRGBTest.cpp b/tests/SRGBTest.cpp
index 77c1fb7..4a519ca 100644
--- a/tests/SRGBTest.cpp
+++ b/tests/SRGBTest.cpp
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "SkPM4f.h"
+#include "SkRasterPipeline.h"
 #include "SkSRGB.h"
 #include "SkTypes.h"
 #include "Test.h"
@@ -37,3 +39,49 @@
         f = pun.flt;
     }
 }
+
+DEF_TEST(sk_pipeline_srgb_roundtrip, r) {
+    uint32_t reds[256];
+    for (int i = 0; i < 256; i++) {
+        reds[i] = i;
+    }
+
+    auto ptr = (void*)reds;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_8888,  &ptr);
+    p.append_from_srgb(kUnpremul_SkAlphaType);
+    p.append(SkRasterPipeline::to_srgb);
+    p.append(SkRasterPipeline::store_8888, &ptr);
+
+    p.run(0,0,256);
+
+    for (int i = 0; i < 256; i++) {
+        if (reds[i] != (uint32_t)i) {
+            ERRORF(r, "%d doesn't round trip, %d", i, reds[i]);
+        }
+    }
+}
+
+DEF_TEST(sk_pipeline_srgb_edge_cases, r) {
+    // We need to run at least 4 pixels to make sure we hit all specializations.
+    SkPM4f colors[4] = { {{0,1,1,1}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}} };
+    auto& color = colors[0];
+    void* dst = &color;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::constant_color, &color);
+    p.append(SkRasterPipeline::to_srgb);
+    p.append(SkRasterPipeline::store_f32, &dst);
+    p.run(0,0,4);
+
+    if (color.r() != 0.0f) {
+        ERRORF(r, "expected to_srgb() to map 0.0f to 0.0f, got %f", color.r());
+    }
+    if (color.g() != 1.0f) {
+        float f = color.g();
+        uint32_t x;
+        memcpy(&x, &f, 4);
+        ERRORF(r, "expected to_srgb() to map 1.0f to 1.0f, got %f (%08x)", color.g(), x);
+    }
+}
diff --git a/tests/SerializationTest.cpp b/tests/SerializationTest.cpp
index 3de422b..fccf211 100644
--- a/tests/SerializationTest.cpp
+++ b/tests/SerializationTest.cpp
@@ -18,6 +18,7 @@
 #include "SkNormalSource.h"
 #include "SkOSFile.h"
 #include "SkPictureRecorder.h"
+#include "SkShaderBase.h"
 #include "SkTableColorFilter.h"
 #include "SkTemplates.h"
 #include "SkTypeface.h"
@@ -318,8 +319,6 @@
                             const SkBitmap& b1, const SkBitmap& b2) {
     REPORTER_ASSERT(reporter, b1.width() == b2.width());
     REPORTER_ASSERT(reporter, b1.height() == b2.height());
-    SkAutoLockPixels autoLockPixels1(b1);
-    SkAutoLockPixels autoLockPixels2(b2);
 
     if ((b1.width() != b2.width()) ||
         (b1.height() != b2.height())) {
@@ -440,7 +439,7 @@
     canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint);
     paint.setColor(SK_ColorBLACK);
     paint.setTextSize(SkIntToScalar(kBitmapSize/3));
-    canvas->drawText("Picture", 7, SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
+    canvas->drawString("Picture", SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
 }
 
 DEF_TEST(Serialization, reporter) {
@@ -612,32 +611,22 @@
         sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuseShader,
                                                                 normalSource,
                                                                 fLights);
-        sk_sp<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter));
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
 
         lightingShader = SkLightingShader::Make(std::move(diffuseShader),
                                                 nullptr,
                                                 fLights);
-        sk_sp<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter));
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
 
         lightingShader = SkLightingShader::Make(nullptr,
                                                 std::move(normalSource),
                                                 fLights);
-        sk_sp<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter));
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
 
         lightingShader = SkLightingShader::Make(nullptr,
                                                 nullptr,
                                                 fLights);
-        sk_sp<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter));
-    }
-
-    // Test NormalBevelSource serialization
-    {
-        sk_sp<SkNormalSource> bevelSource = SkNormalSource::MakeBevel(
-                SkNormalSource::BevelType::kLinear, 2.0f, 5.0f);
-
-        sk_sp<SkNormalSource>(TestFlattenableSerialization(bevelSource.get(), true, reporter));
-        // TODO test equality?
-
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
     }
 }
 
diff --git a/tests/ShadowUtilsTest.cpp b/tests/ShadowUtilsTest.cpp
index e2aa1d1..c0e20d5 100644
--- a/tests/ShadowUtilsTest.cpp
+++ b/tests/ShadowUtilsTest.cpp
@@ -14,29 +14,25 @@
 
 void tessellate_shadow(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm,
                        bool expectSuccess) {
-    static constexpr SkScalar kAmbientAlpha = 0.25f;
-    static constexpr SkScalar kSpotAlpha = 0.25f;
 
-    auto heightFunc = [] (SkScalar, SkScalar) { return 4; };
-    
-    auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightFunc, kAmbientAlpha, true);
+    auto heightParams = SkPoint3::Make(0, 0, 4);
+
+    auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, true);
     if (expectSuccess != SkToBool(verts)) {
         ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
                expectSuccess ? "succeed" : "fail");
     }
-    verts = SkShadowTessellator::MakeAmbient(path, ctm, heightFunc, kAmbientAlpha, false);
+    verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, false);
     if (expectSuccess != SkToBool(verts)) {
         ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
                expectSuccess ? "succeed" : "fail");
     }
-    verts = SkShadowTessellator::MakeSpot(path, ctm, heightFunc, {0, 0, 128}, 128.f,
-                                          kSpotAlpha, false);
+    verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false);
     if (expectSuccess != SkToBool(verts)) {
         ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
                expectSuccess ? "succeed" : "fail");
     }
-    verts = SkShadowTessellator::MakeSpot(path, ctm, heightFunc, {0, 0, 128}, 128.f,
-                                          kSpotAlpha, false);
+    verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false);
     if (expectSuccess != SkToBool(verts)) {
         ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
                expectSuccess ? "succeed" : "fail");
@@ -45,11 +41,10 @@
 
 DEF_TEST(ShadowUtils, reporter) {
     SkCanvas canvas(100, 100);
-    // Currently SkShadowUtils doesn't really support cubics when compiled without SK_SUPPORT_GPU.
-    // However, this should now not crash.
+
     SkPath path;
     path.cubicTo(100, 50, 20, 100, 0, 0);
-    tessellate_shadow(reporter, path, canvas.getTotalMatrix(), (bool)SK_SUPPORT_GPU);
+    tessellate_shadow(reporter, path, canvas.getTotalMatrix(), true);
 
     // This line segment has no area and no shadow.
     path.reset();
diff --git a/tests/SizeTest.cpp b/tests/SizeTest.cpp
index 9800aa2..9dccdeb 100644
--- a/tests/SizeTest.cpp
+++ b/tests/SizeTest.cpp
@@ -16,7 +16,7 @@
     REPORTER_ASSERT(reporter, a.isEmpty());
     a.set(5, -5);
     REPORTER_ASSERT(reporter, a.isEmpty());
-    a.clampNegToZero();
+    a = SkISize{5, 0};
     REPORTER_ASSERT(reporter, a.isEmpty());
     b.set(5, 0);
     REPORTER_ASSERT(reporter, a == b);
@@ -42,7 +42,7 @@
     REPORTER_ASSERT(reporter, a.isEmpty());
     a.set(x, -x);
     REPORTER_ASSERT(reporter, a.isEmpty());
-    a.clampNegToZero();
+    a = SkSize{x, 0};
     REPORTER_ASSERT(reporter, a.isEmpty());
     b.set(x, 0);
     REPORTER_ASSERT(reporter, a == b);
diff --git a/tests/SkRasterPipelineTest.cpp b/tests/SkRasterPipelineTest.cpp
index f7c1456..014683c 100644
--- a/tests/SkRasterPipelineTest.cpp
+++ b/tests/SkRasterPipelineTest.cpp
@@ -20,14 +20,14 @@
     void* load_d_ctx = &red;
     void* store_ctx  = &result;
 
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::load_f16, &load_s_ctx);
     p.append(SkRasterPipeline::move_src_dst);
     p.append(SkRasterPipeline::load_f16, &load_d_ctx);
     p.append(SkRasterPipeline::swap);
     p.append(SkRasterPipeline::srcover);
     p.append(SkRasterPipeline::store_f16, &store_ctx);
-    p.run(0,1);
+    p.run(0,0,1);
 
     // We should see half-intensity magenta.
     REPORTER_ASSERT(r, ((result >>  0) & 0xffff) == 0x3800);
@@ -38,16 +38,16 @@
 
 DEF_TEST(SkRasterPipeline_empty, r) {
     // No asserts... just a test that this is safe to run.
-    SkRasterPipeline p;
-    p.run(0,20);
+    SkRasterPipeline_<256> p;
+    p.run(0,0,20);
 }
 
 DEF_TEST(SkRasterPipeline_nonsense, r) {
     // No asserts... just a test that this is safe to run and terminates.
     // srcover() calls st->next(); this makes sure we've always got something there to call.
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline::srcover);
-    p.run(0,20);
+    p.run(0,0,20);
 }
 
 DEF_TEST(SkRasterPipeline_JIT, r) {
@@ -66,10 +66,10 @@
     uint32_t*       dst = buf + 36;
 
     // Copy buf[x] to buf[x+36] for x in [15,35).
-    SkRasterPipeline p;
+    SkRasterPipeline_<256> p;
     p.append(SkRasterPipeline:: load_8888, &src);
     p.append(SkRasterPipeline::store_8888, &dst);
-    p.run(15, 20);
+    p.run(15,0, 20);
 
     for (int i = 0; i < 36; i++) {
         if (i < 15 || i == 35) {
@@ -79,3 +79,157 @@
         }
     }
 }
+
+static uint16_t h(float f) {
+    // Remember, a float is 1-8-23 (sign-exponent-mantissa) with 127 exponent bias.
+    uint32_t sem;
+    memcpy(&sem, &f, sizeof(sem));
+    uint32_t s  = sem & 0x80000000,
+             em = sem ^ s;
+
+    // Convert to 1-5-10 half with 15 bias, flushing denorm halfs (including zero) to zero.
+    auto denorm = (int32_t)em < 0x38800000;  // I32 comparison is often quicker, and always safe
+    // here.
+    return denorm ? SkTo<uint16_t>(0)
+                  : SkTo<uint16_t>((s>>16) + (em>>13) - ((127-15)<<10));
+}
+
+static uint16_t n(uint16_t x) {
+    return (x<<8) | (x>>8);
+}
+
+static float a(uint16_t x) {
+    return (1/65535.0f) * x;
+}
+
+DEF_TEST(SkRasterPipeline_tail, r) {
+    {
+        float data[][4] = {
+            {00, 01, 02, 03},
+            {10, 11, 12, 13},
+            {20, 21, 22, 23},
+            {30, 31, 32, 33},
+        };
+
+        float buffer[4][4];
+        float* src = &data[0][0];
+        float* dst = &buffer[0][0];
+
+        for (unsigned i = 1; i <= 4; i++) {
+            memset(buffer, 0xff, sizeof(buffer));
+            SkRasterPipeline_<256> p;
+            p.append(SkRasterPipeline::load_f32, &src);
+            p.append(SkRasterPipeline::store_f32, &dst);
+            p.run(0,0, i);
+            for (unsigned j = 0; j < i; j++) {
+                for (unsigned k = 0; k < 4; k++) {
+                    if (buffer[j][k] != data[j][k]) {
+                        ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, data[j][k], buffer[j][k]);
+                    }
+                }
+            }
+            for (int j = i; j < 4; j++) {
+                for (auto f : buffer[j]) {
+                    REPORTER_ASSERT(r, SkScalarIsNaN(f));
+                }
+            }
+        }
+    }
+
+    {
+        uint16_t data[][4] = {
+            {h(00), h(01), h(02), h(03)},
+            {h(10), h(11), h(12), h(13)},
+            {h(20), h(21), h(22), h(23)},
+            {h(30), h(31), h(32), h(33)},
+        };
+        uint16_t buffer[4][4];
+        uint16_t* src = &data[0][0];
+        uint16_t* dst = &buffer[0][0];
+
+        for (unsigned i = 1; i <= 4; i++) {
+            memset(buffer, 0xff, sizeof(buffer));
+            SkRasterPipeline_<256> p;
+            p.append(SkRasterPipeline::load_f16, &src);
+            p.append(SkRasterPipeline::store_f16, &dst);
+            p.run(0,0, i);
+            for (unsigned j = 0; j < i; j++) {
+                REPORTER_ASSERT(r,
+                                !memcmp(&data[j][0], &buffer[j][0], sizeof(buffer[j])));
+            }
+            for (int j = i; j < 4; j++) {
+                for (auto f : buffer[j]) {
+                    REPORTER_ASSERT(r, f == 0xffff);
+                }
+            }
+        }
+    }
+
+    {
+        uint16_t data[][3] = {
+            {n(00), n(01), n(02)},
+            {n(10), n(11), n(12)},
+            {n(20), n(21), n(22)},
+            {n(30), n(31), n(32)}
+        };
+
+        float answer[][4] = {
+            {a(00), a(01), a(02), 1.0f},
+            {a(10), a(11), a(12), 1.0f},
+            {a(20), a(21), a(22), 1.0f},
+            {a(30), a(31), a(32), 1.0f}
+        };
+
+        float buffer[4][4];
+        uint16_t* src = &data[0][0];
+        float* dst = &buffer[0][0];
+
+        for (unsigned i = 1; i <= 4; i++) {
+            memset(buffer, 0xff, sizeof(buffer));
+            SkRasterPipeline_<256> p;
+            p.append(SkRasterPipeline::load_rgb_u16_be, &src);
+            p.append(SkRasterPipeline::store_f32, &dst);
+            p.run(0,0, i);
+            for (unsigned j = 0; j < i; j++) {
+                for (unsigned k = 0; k < 4; k++) {
+                    if (buffer[j][k] != answer[j][k]) {
+                        ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, answer[j][k], buffer[j][k]);
+                    }
+                }
+            }
+            for (int j = i; j < 4; j++) {
+                for (auto f : buffer[j]) {
+                    REPORTER_ASSERT(r, SkScalarIsNaN(f));
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(SkRasterPipeline_lowp, r) {
+    uint32_t rgba[64];
+    for (int i = 0; i < 64; i++) {
+        rgba[i] = (4*i+0) << 0
+                | (4*i+1) << 8
+                | (4*i+2) << 16
+                | (4*i+3) << 24;
+    }
+
+    void* ptr = rgba;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_8888,  &ptr);
+    p.append(SkRasterPipeline::swap_rb);
+    p.append(SkRasterPipeline::store_8888, &ptr);
+    p.run(0,0,64);
+
+    for (int i = 0; i < 64; i++) {
+        uint32_t want = (4*i+0) << 16
+                      | (4*i+1) << 8
+                      | (4*i+2) << 0
+                      | (4*i+3) << 24;
+        if (rgba[i] != want) {
+            ERRORF(r, "got %08x, want %08x\n", rgba[i], want);
+        }
+    }
+}
diff --git a/tests/SkResourceCacheTest.cpp b/tests/SkResourceCacheTest.cpp
index b260d89..5f8770d 100644
--- a/tests/SkResourceCacheTest.cpp
+++ b/tests/SkResourceCacheTest.cpp
@@ -10,6 +10,7 @@
 #include "SkCanvas.h"
 #include "SkDiscardableMemoryPool.h"
 #include "SkGraphics.h"
+#include "SkMakeUnique.h"
 #include "SkMipMap.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -126,7 +127,7 @@
         testBitmapCache_discarded_bitmap(reporter, &cache, nullptr);
     }
     {
-        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Create(byteLimit, nullptr));
+        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(byteLimit));
         gPool = pool.get();
         SkResourceCache::DiscardableFactory factory = pool_factory;
         SkResourceCache cache(factory);
@@ -201,3 +202,88 @@
         });
     }
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void* gTestNamespace;
+
+struct TestKey : SkResourceCache::Key {
+    int32_t fData;
+
+    TestKey(int sharedID, int32_t data) : fData(data) {
+        this->init(&gTestNamespace, sharedID, sizeof(fData));
+    }
+};
+
+struct TestRec : SkResourceCache::Rec {
+    enum {
+        kDidInstall = 1 << 0,
+    };
+
+    TestKey fKey;
+    int*    fFlags;
+    bool    fCanBePurged;
+
+    TestRec(int sharedID, int32_t data, int* flagPtr) : fKey(sharedID, data), fFlags(flagPtr) {
+        fCanBePurged = false;
+    }
+
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return 1024; /* just need a value */ }
+    bool canBePurged() override { return fCanBePurged; }
+    void postAddInstall(void*) override {
+        *fFlags |= kDidInstall;
+    }
+    const char* getCategory() const override { return "test-category"; }
+};
+
+static void test_duplicate_add(SkResourceCache* cache, skiatest::Reporter* reporter,
+                               bool purgable) {
+    int sharedID = 1;
+    int data = 0;
+
+    int flags0 = 0, flags1 = 0;
+
+    auto rec0 = skstd::make_unique<TestRec>(sharedID, data, &flags0);
+    auto rec1 = skstd::make_unique<TestRec>(sharedID, data, &flags1);
+    SkASSERT(rec0->getKey() == rec1->getKey());
+
+    TestRec* r0 = rec0.get();   // save the bare-pointer since we will release rec0
+    r0->fCanBePurged = purgable;
+
+    REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
+    REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
+
+    cache->add(rec0.release(), nullptr);
+    REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
+    REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
+    flags0 = 0; // reset the flag
+
+    cache->add(rec1.release(), nullptr);
+    if (purgable) {
+        // we purged rec0, and did install rec1
+        REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
+        REPORTER_ASSERT(reporter, flags1 & TestRec::kDidInstall);
+    } else {
+        // we re-used rec0 and did not install rec1
+        REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
+        REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
+        r0->fCanBePurged = true;  // so we can cleanup the cache
+    }
+}
+
+/*
+ *  Test behavior when the same key is added more than once.
+ */
+DEF_TEST(ResourceCache_purge, reporter) {
+    for (bool purgable : { false, true }) {
+        {
+            SkResourceCache cache(1024 * 1024);
+            test_duplicate_add(&cache, reporter, purgable);
+        }
+        {
+            SkResourceCache cache(SkDiscardableMemory::Create);
+            test_duplicate_add(&cache, reporter, purgable);
+        }
+    }
+}
diff --git a/tests/SkSLErrorTest.cpp b/tests/SkSLErrorTest.cpp
index bde5e70..c631382 100644
--- a/tests/SkSLErrorTest.cpp
+++ b/tests/SkSLErrorTest.cpp
@@ -13,17 +13,11 @@
 
 static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
     SkSL::Compiler compiler;
-    SkDynamicMemoryWStream out;
     SkSL::Program::Settings settings;
     sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
     settings.fCaps = caps.get();
-    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
-                                                                     SkString(src), settings);
-    if (program) {
-        SkString ignored;
-        compiler.toSPIRV(*program, &ignored);
-    }
-    SkString skError(error);
+    compiler.convertProgram(SkSL::Program::kFragment_Kind, SkString(src), settings);
+    SkSL::String skError(error);
     if (compiler.errorText() != skError) {
         SkDebugf("SKSL ERROR:\n    source: %s\n    expected: %s    received: %s", src, error,
                  compiler.errorText().c_str());
@@ -33,15 +27,12 @@
 
 static void test_success(skiatest::Reporter* r, const char* src) {
     SkSL::Compiler compiler;
-    SkDynamicMemoryWStream out;
     SkSL::Program::Settings settings;
     sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
     settings.fCaps = caps.get();
     std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
                                                                      SkString(src), settings);
     REPORTER_ASSERT(r, program);
-    SkString ignored;
-    REPORTER_ASSERT(r, compiler.toSPIRV(*program, &ignored));
 }
 
 DEF_TEST(SkSLUndefinedSymbol, r) {
@@ -127,6 +118,9 @@
     test_failure(r,
                  "struct foo { int x; } foo; void main() { vec2 x = vec2(foo); }",
                  "error: 1: 'foo' is not a valid parameter to 'vec2' constructor\n1 error\n");
+    test_failure(r,
+                 "void main() { mat2 x = mat2(true); }",
+                 "error: 1: expected 'float', but found 'bool'\n1 error\n");
 }
 
 DEF_TEST(SkSLConstructorArgumentCount, r) {
@@ -350,9 +344,9 @@
     test_failure(r,
                  "void main() { for (;;) { continue; int x = 1; } }",
                  "error: 1: unreachable\n1 error\n");
-    test_failure(r,
+/*    test_failure(r,
                  "void main() { for (;;) { } return; }",
-                 "error: 1: unreachable\n1 error\n");
+                 "error: 1: unreachable\n1 error\n");*/
     test_failure(r,
                  "void main() { if (true) return; else discard; return; }",
                  "error: 1: unreachable\n1 error\n");
@@ -404,15 +398,6 @@
                  "error: 1: unknown capability flag 'bugFreeDriver'\n1 error\n");
 }
 
-DEF_TEST(SkSLBadOffset, r) {
-    test_failure(r,
-                 "struct Bad { layout (offset = 5) int x; } bad; void main() { bad.x = 5; }",
-                 "error: 1: offset of field 'x' must be a multiple of 4\n1 error\n");
-    test_failure(r,
-                 "struct Bad { int x; layout (offset = 0) int y; } bad; void main() { bad.x = 5; }",
-                 "error: 1: offset of field 'y' must be at least 4\n1 error\n");
-}
-
 DEF_TEST(SkSLDivByZero, r) {
     test_failure(r,
                  "int x = 1 / 0;",
@@ -458,4 +443,50 @@
                  "error: 1: duplicate case value\n1 error\n");
 }
 
+DEF_TEST(SkSLFieldAfterRuntimeArray, r) {
+    test_failure(r,
+                 "buffer broken { float x[]; float y; };",
+                 "error: 1: only the last entry in an interface block may be a runtime-sized "
+                 "array\n1 error\n");
+}
+
+DEF_TEST(SkSLStaticIf, r) {
+    test_success(r,
+                 "void main() { float x = 5; float y = 10;"
+                 "@if (x < y) { sk_FragColor = vec4(1); } }");
+    test_failure(r,
+                 "void main() { float x = sqrt(25); float y = 10;"
+                 "@if (x < y) { sk_FragColor = vec4(1); } }",
+                 "error: 1: static if has non-static test\n1 error\n");
+}
+
+DEF_TEST(SkSLStaticSwitch, r) {
+    test_success(r,
+                 "void main() {"
+                 "int x = 1;"
+                 "@switch (x) {"
+                 "case 1: sk_FragColor = vec4(1); break;"
+                 "default: sk_FragColor = vec4(0);"
+                 "}"
+                 "}");
+    test_failure(r,
+                 "void main() {"
+                 "int x = int(sqrt(1));"
+                 "@switch (x) {"
+                 "case 1: sk_FragColor = vec4(1); break;"
+                 "default: sk_FragColor = vec4(0);"
+                 "}"
+                 "}",
+                 "error: 1: static switch has non-static test\n1 error\n");
+    test_failure(r,
+                 "void main() {"
+                 "int x = 1;"
+                 "@switch (x) {"
+                 "case 1: sk_FragColor = vec4(1); if (sqrt(0) < sqrt(1)) break;"
+                 "default: sk_FragColor = vec4(0);"
+                 "}"
+                 "}",
+                 "error: 1: static switch contains non-static conditional break\n1 error\n");
+}
+
 #endif
diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp
index 53e5c6b..678ff13 100644
--- a/tests/SkSLGLSLTest.cpp
+++ b/tests/SkSLGLSLTest.cpp
@@ -11,11 +11,18 @@
 
 #if SK_SUPPORT_GPU
 
+// Note that the optimizer will aggressively kill dead code and substitute constants in place of
+// variables, so we have to jump through a few hoops to ensure that the code in these tests has the
+// necessary side-effects to remain live. In some cases we rely on the optimizer not (yet) being
+// smart enough to optimize around certain constructs; as the optimizer gets smarter it will
+// undoubtedly end up breaking some of these tests. That is a good thing, as long as the new code is
+// equivalent!
+
 static void test(skiatest::Reporter* r, const char* src, const SkSL::Program::Settings& settings,
                  const char* expected, SkSL::Program::Inputs* inputs,
                  SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) {
     SkSL::Compiler compiler;
-    SkString output;
+    SkSL::String output;
     std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, SkString(src), settings);
     if (!program) {
         SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
@@ -24,7 +31,7 @@
     *inputs = program->fInputs;
     REPORTER_ASSERT(r, compiler.toGLSL(*program, &output));
     if (program) {
-        SkString skExpected(expected);
+        SkSL::String skExpected(expected);
         if (output != skExpected) {
             SkDebugf("GLSL MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
                      expected, output.c_str());
@@ -57,7 +64,7 @@
          "void main() {"
          "if (sqrt(2) > 5) { sk_FragColor = vec4(0.75); } else { discard; }"
          "int i = 0;"
-         "while (i < 10) sk_FragColor *= 0.5;"
+         "while (i < 10) { sk_FragColor *= 0.5; i++; }"
          "do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.75);"
          "for (int i = 0; i < 10; i++) {"
          "if (i % 2 == 1) break; else continue;"
@@ -74,7 +81,10 @@
          "        discard;\n"
          "    }\n"
          "    int i = 0;\n"
-         "    while (true) sk_FragColor *= 0.5;\n"
+         "    while (i < 10) {\n"
+         "        sk_FragColor *= 0.5;\n"
+         "        i++;\n"
+         "    }\n"
          "    do {\n"
          "        sk_FragColor += 0.01;\n"
          "    } while (sk_FragColor.x < 0.75);\n"
@@ -93,7 +103,7 @@
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
-         "float foo(in float v[2]) {\n"
+         "float foo(float v[2]) {\n"
          "    return v[0] * v[1];\n"
          "}\n"
          "void bar(inout float x) {\n"
@@ -105,8 +115,8 @@
          "}\n"
          "void main() {\n"
          "    float x = 10.0;\n"
-         "    bar(10.0);\n"
-         "    sk_FragColor = vec4(10.0);\n"
+         "    bar(x);\n"
+         "    sk_FragColor = vec4(x);\n"
          "}\n");
 }
 
@@ -131,6 +141,8 @@
          "z >>= 2;"
          "z <<= 4;"
          "z %= 5;"
+         "x = (vec2(sqrt(1)) , 6);"
+         "z = (vec2(sqrt(1)) , 6);"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
@@ -154,6 +166,8 @@
          "    z >>= 2;\n"
          "    z <<= 4;\n"
          "    z %= 5;\n"
+         "    x = float((vec2(sqrt(1.0)) , 6));\n"
+         "    z = (vec2(sqrt(1.0)) , 6);\n"
          "}\n");
 }
 
@@ -163,18 +177,18 @@
          "mat2x4 x = mat2x4(1);"
          "mat3x2 y = mat3x2(1, 0, 0, 1, vec2(2, 2));"
          "mat3x4 z = x * y;"
-         "vec3 v1 = mat3(1) * vec3(1);"
-         "vec3 v2 = vec3(1) * mat3(1);"
+         "vec3 v1 = mat3(1) * vec3(2);"
+         "vec3 v2 = vec3(2) * mat3(1);"
+         "sk_FragColor = vec4(z[0].x, v1 + v2);"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    mat2x4 x = mat2x4(1.0);\n"
-         "    mat3x2 y = mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
-         "    mat3x4 z = x * y;\n"
-         "    vec3 v1 = mat3(1.0) * vec3(1.0);\n"
-         "    vec3 v2 = vec3(1.0) * mat3(1.0);\n"
+         "    mat3x4 z = mat2x4(1.0) * mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
+         "    vec3 v1 = mat3(1.0) * vec3(2.0);\n"
+         "    vec3 v2 = vec3(2.0) * mat3(1.0);\n"
+         "    sk_FragColor = vec4(z[0].x, v1 + v2);\n"
          "}\n");
 }
 
@@ -289,16 +303,21 @@
 
 DEF_TEST(SkSLUsesPrecisionModifiers, r) {
     test(r,
-         "void main() { float x = 0.75; highp float y = 1; }",
+         "void main() { float x = 0.75; highp float y = 1; x++; y++;"
+         "sk_FragColor.rg = vec2(x, y); }",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
          "    float x = 0.75;\n"
          "    float y = 1.0;\n"
-         "}\n");    
+         "    x++;\n"
+         "    y++;\n"
+         "    sk_FragColor.xy = vec2(x, y);\n"
+         "}\n");
     test(r,
-         "void main() { float x = 0.75; highp float y = 1; }",
+         "void main() { float x = 0.75; highp float y = 1; x++; y++;"
+         "sk_FragColor.rg = vec2(x, y); }",
          *SkSL::ShaderCapsFactory::UsesPrecisionModifiers(),
          "#version 400\n"
          "precision highp float;\n"
@@ -306,27 +325,29 @@
          "void main() {\n"
          "    float x = 0.75;\n"
          "    highp float y = 1.0;\n"
-         "}\n");    
+         "    x++;\n"
+         "    y++;\n"
+         "    sk_FragColor.xy = vec2(x, y);\n"
+         "}\n");
 }
 
 DEF_TEST(SkSLMinAbs, r) {
     test(r,
          "void main() {"
          "float x = -5;"
-         "x = min(abs(x), 6);"
+         "sk_FragColor.r = min(abs(x), 6);"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    float x = -5.0;\n"
-         "    x = min(abs(-5.0), 6.0);\n"
+         "    sk_FragColor.x = min(abs(-5.0), 6.0);\n"
          "}\n");
 
     test(r,
          "void main() {"
          "float x = -5.0;"
-         "x = min(abs(x), 6.0);"
+         "sk_FragColor.r = min(abs(x), 6.0);"
          "}",
          *SkSL::ShaderCapsFactory::CannotUseMinAndAbsTogether(),
          "#version 400\n"
@@ -334,30 +355,29 @@
          "void main() {\n"
          "    float minAbsHackVar0;\n"
          "    float minAbsHackVar1;\n"
-         "    float x = -5.0;\n"
-         "    x = ((minAbsHackVar0 = abs(-5.0)) < (minAbsHackVar1 = 6.0) ? minAbsHackVar0 : "
-                                                                                "minAbsHackVar1);\n"
+         "    sk_FragColor.x = ((minAbsHackVar0 = abs(-5.0)) < (minAbsHackVar1 = 6.0) ? "
+                                                               "minAbsHackVar0 : minAbsHackVar1);\n"
          "}\n");
 }
 
 DEF_TEST(SkSLNegatedAtan, r) {
     test(r,
-         "void main() { vec2 x = vec2(1, 2); float y = atan(x.x, -(2 * x.y)); }",
+         "void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    vec2 x = vec2(1.0, 2.0);\n"
-         "    float y = atan(x.x, -(2.0 * x.y));\n"
+         "    vec2 x = vec2(sqrt(2.0));\n"
+         "    sk_FragColor.x = atan(x.x, -x.y);\n"
          "}\n");
     test(r,
-         "void main() { vec2 x = vec2(1, 2); float y = atan(x.x, -(2 * x.y)); }",
+         "void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",
          *SkSL::ShaderCapsFactory::MustForceNegatedAtanParamToFloat(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    vec2 x = vec2(1.0, 2.0);\n"
-         "    float y = atan(x.x, -1.0 * (2.0 * x.y));\n"
+         "    vec2 x = vec2(sqrt(2.0));\n"
+         "    sk_FragColor.x = atan(x.x, -1.0 * x.y);\n"
          "}\n");
 }
 
@@ -377,28 +397,46 @@
     test(r,
          "void main() {"
          "int i1 = 0x0;"
+         "i1++;"
          "int i2 = 0x1234abcd;"
+         "i2++;"
          "int i3 = 0x7fffffff;"
+         "i3++;"
          "int i4 = 0xffffffff;"
+         "i4++;"
          "int i5 = -0xbeef;"
+         "i5++;"
          "uint u1 = 0x0;"
+         "u1++;"
          "uint u2 = 0x1234abcd;"
+         "u2++;"
          "uint u3 = 0x7fffffff;"
+         "u3++;"
          "uint u4 = 0xffffffff;"
+         "u4++;"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
          "    int i1 = 0;\n"
+         "    i1++;\n"
          "    int i2 = 305441741;\n"
+         "    i2++;\n"
          "    int i3 = 2147483647;\n"
+         "    i3++;\n"
          "    int i4 = -1;\n"
+         "    i4++;\n"
          "    int i5 = -48879;\n"
+         "    i5++;\n"
          "    uint u1 = 0u;\n"
+         "    u1++;\n"
          "    uint u2 = 305441741u;\n"
+         "    u2++;\n"
          "    uint u3 = 2147483647u;\n"
+         "    u3++;\n"
          "    uint u4 = 4294967295u;\n"
+         "    u4++;\n"
          "}\n");
 }
 
@@ -438,110 +476,366 @@
 
 DEF_TEST(SkSLDerivatives, r) {
     test(r,
-         "void main() { float x = dFdx(1); }",
+         "void main() { sk_FragColor.r = dFdx(1); }",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    float x = dFdx(1.0);\n"
+         "    sk_FragColor.x = dFdx(1.0);\n"
          "}\n");
     test(r,
-         "void main() { float x = 1; }",
+         "void main() { sk_FragColor.r = 1; }",
          *SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    float x = 1.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
          "}\n");
     test(r,
-         "void main() { float x = dFdx(1); }",
+         "void main() { sk_FragColor.r = dFdx(1); }",
          *SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(),
          "#version 400\n"
          "#extension GL_OES_standard_derivatives : require\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    float x = dFdx(1.0);\n"
+         "    sk_FragColor.x = dFdx(1.0);\n"
          "}\n");
 }
 
-DEF_TEST(SkSLConstantFolding, r) {
+
+DEF_TEST(SkSLIntFolding, r) {
     test(r,
          "void main() {"
-         "float f_add = 32 + 2;"
-         "float f_sub = 32 - 2;"
-         "float f_mul = 32 * 2;"
-         "float f_div = 32 / 2;"
-         "float mixed = (12 > 2.0) ? (10 * 2 / 5 + 18 - 3) : 0;"
-         "int i_add = 32 + 2;"
-         "int i_sub = 32 - 2;"
-         "int i_mul = 32 * 2;"
-         "int i_div = 32 / 2;"
-         "int i_or = 12 | 6;"
-         "int i_and = 254 & 7;"
-         "int i_xor = 2 ^ 7;"
-         "int i_shl = 1 << 4;"
-         "int i_shr = 128 >> 2;"
-         "bool gt_it = 6 > 5;"
-         "bool gt_if = 6 > 6;"
-         "bool gt_ft = 6.0 > 5.0;"
-         "bool gt_ff = 6.0 > 6.0;"
-         "bool gte_it = 6 >= 6;"
-         "bool gte_if = 6 >= 7;"
-         "bool gte_ft = 6.0 >= 6.0;"
-         "bool gte_ff = 6.0 >= 7.0;"
-         "bool lte_it = 6 <= 6;"
-         "bool lte_if = 6 <= 5;"
-         "bool lte_ft = 6.0 <= 6.0;"
-         "bool lte_ff = 6.0 <= 5.0;"
-         "bool or_t = 1 == 1 || 2 == 8;"
-         "bool or_f = 1 > 1 || 2 == 8;"
-         "bool and_t = 1 == 1 && 2 <= 8;"
-         "bool and_f = 1 == 2 && 2 == 8;"
-         "bool xor_t = 1 == 1 ^^ 1 != 1;"
-         "bool xor_f = 1 == 1 ^^ 1 == 1;"
-         "int ternary = 10 > 5 ? 10 : 5;"
+         "sk_FragColor.r = 32 + 2;"
+         "sk_FragColor.r = 32 - 2;"
+         "sk_FragColor.r = 32 * 2;"
+         "sk_FragColor.r = 32 / 2;"
+         "sk_FragColor.r = 12 | 6;"
+         "sk_FragColor.r = 254 & 7;"
+         "sk_FragColor.r = 2 ^ 7;"
+         "sk_FragColor.r = 1 << 4;"
+         "sk_FragColor.r = 128 >> 2;"
+         "sk_FragColor.r = -1 == -1 ? 1 : -1;"
+         "sk_FragColor.r = -1 == -2 ? 2 : -2;"
+         "sk_FragColor.r = 0 != 1 ? 3 : -3;"
+         "sk_FragColor.r = 0 != 0 ? 4 : -4;"
+         "sk_FragColor.r = 6 > 5 ? 5 : -5;"
+         "sk_FragColor.r = 6 > 6 ? 6 : -6;"
+         "sk_FragColor.r = -1 < 0 ? 7 : -7;"
+         "sk_FragColor.r = 1 < 0 ? 8 : -8;"
+         "sk_FragColor.r = 6 >= 6 ? 9 : -9;"
+         "sk_FragColor.r = 6 >= 7 ? 10 : -10;"
+         "sk_FragColor.r = 6 <= 6 ? 11 : -11;"
+         "sk_FragColor.r = 6 <= 5 ? 12 : -12;"
+         "sk_FragColor.r = int(sqrt(1)) + 0;"
+         "sk_FragColor.r = 0 + int(sqrt(2));"
+         "sk_FragColor.r = int(sqrt(3)) - 0;"
+         "sk_FragColor.r = int(sqrt(4)) * 0;"
+         "sk_FragColor.r = int(sqrt(5)) * 1;"
+         "sk_FragColor.r = 1 * int(sqrt(6));"
+         "sk_FragColor.r = 0 * int(sqrt(7));"
+         "sk_FragColor.r = int(sqrt(8)) / 1;"
+         "sk_FragColor.r = 0 / int(sqrt(9));"
+         "int x = int(sqrt(2));"
+         "x += 1;"
+         "x += 0;"
+         "x -= 1;"
+         "x -= 0;"
+         "x *= 1;"
+         "x *= 2;"
+         "x /= 1;"
+         "x /= 2;"
+         "sk_FragColor.r = x;"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    float f_add = 34.0;\n"
-         "    float f_sub = 30.0;\n"
-         "    float f_mul = 64.0;\n"
-         "    float f_div = 16.0;\n"
-         "    float mixed = 19.0;\n"
-         "    int i_add = 34;\n"
-         "    int i_sub = 30;\n"
-         "    int i_mul = 64;\n"
-         "    int i_div = 16;\n"
-         "    int i_or = 14;\n"
-         "    int i_and = 6;\n"
-         "    int i_xor = 5;\n"
-         "    int i_shl = 16;\n"
-         "    int i_shr = 32;\n"
-         "    bool gt_it = true;\n"
-         "    bool gt_if = false;\n"
-         "    bool gt_ft = true;\n"
-         "    bool gt_ff = false;\n"
-         "    bool gte_it = true;\n"
-         "    bool gte_if = false;\n"
-         "    bool gte_ft = true;\n"
-         "    bool gte_ff = false;\n"
-         "    bool lte_it = true;\n"
-         "    bool lte_if = false;\n"
-         "    bool lte_ft = true;\n"
-         "    bool lte_ff = false;\n"
-         "    bool or_t = true;\n"
-         "    bool or_f = false;\n"
-         "    bool and_t = true;\n"
-         "    bool and_f = false;\n"
-         "    bool xor_t = true;\n"
-         "    bool xor_f = false;\n"
-         "    int ternary = 10;\n"
+         "    sk_FragColor.x = 34.0;\n"
+         "    sk_FragColor.x = 30.0;\n"
+         "    sk_FragColor.x = 64.0;\n"
+         "    sk_FragColor.x = 16.0;\n"
+         "    sk_FragColor.x = 14.0;\n"
+         "    sk_FragColor.x = 6.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = 16.0;\n"
+         "    sk_FragColor.x = 32.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = -10.0;\n"
+         "    sk_FragColor.x = 11.0;\n"
+         "    sk_FragColor.x = -12.0;\n"
+         "    sk_FragColor.x = float(int(sqrt(1.0)));\n"
+         "    sk_FragColor.x = float(int(sqrt(2.0)));\n"
+         "    sk_FragColor.x = float(int(sqrt(3.0)));\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = float(int(sqrt(5.0)));\n"
+         "    sk_FragColor.x = float(int(sqrt(6.0)));\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = float(int(sqrt(8.0)));\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    int x = int(sqrt(2.0));\n"
+         "    x += 1;\n"
+         "    x -= 1;\n"
+         "    x *= 2;\n"
+         "    x /= 2;\n"
+         "    sk_FragColor.x = float(x);\n"
          "}\n");
 }
 
-DEF_TEST(SkSLStaticIf, r) {
+DEF_TEST(SkSLFloatFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = 32.0 + 2.0;"
+         "sk_FragColor.r = 32.0 - 2.0;"
+         "sk_FragColor.r = 32.0 * 2.0;"
+         "sk_FragColor.r = 32.0 / 2.0;"
+         "sk_FragColor.r = (12 > 2.0) ? (10 * 2 / 5 + 18 - 3) : 0;"
+         "sk_FragColor.r = 0.0 == 0.0 ? 1 : -1;"
+         "sk_FragColor.r = 0.0 == 1.0 ? 2 : -2;"
+         "sk_FragColor.r = 0.0 != 1.0 ? 3 : -3;"
+         "sk_FragColor.r = 0.0 != 0.0 ? 4 : -4;"
+         "sk_FragColor.r = 6.0 > 5.0 ? 5 : -5;"
+         "sk_FragColor.r = 6.0 > 6.0 ? 6 : -6;"
+         "sk_FragColor.r = 6.0 >= 6.0 ? 7 : -7;"
+         "sk_FragColor.r = 6.0 >= 7.0 ? 8 : -8;"
+         "sk_FragColor.r = 5.0 < 6.0 ? 9 : -9;"
+         "sk_FragColor.r = 6.0 < 6.0 ? 10 : -10;"
+         "sk_FragColor.r = 6.0 <= 6.0 ? 11 : -11;"
+         "sk_FragColor.r = 6.0 <= 5.0 ? 12 : -12;"
+         "sk_FragColor.r = sqrt(1) + 0;"
+         "sk_FragColor.r = 0 + sqrt(2);"
+         "sk_FragColor.r = sqrt(3) - 0;"
+         "sk_FragColor.r = sqrt(4) * 0;"
+         "sk_FragColor.r = sqrt(5) * 1;"
+         "sk_FragColor.r = 1 * sqrt(6);"
+         "sk_FragColor.r = 0 * sqrt(7);"
+         "sk_FragColor.r = sqrt(8) / 1;"
+         "sk_FragColor.r = 0 / sqrt(9);"
+         "sk_FragColor.r += 1;"
+         "sk_FragColor.r += 0;"
+         "sk_FragColor.r -= 1;"
+         "sk_FragColor.r -= 0;"
+         "sk_FragColor.r *= 1;"
+         "sk_FragColor.r *= 2;"
+         "sk_FragColor.r /= 1;"
+         "sk_FragColor.r /= 2;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 34.0;\n"
+         "    sk_FragColor.x = 30.0;\n"
+         "    sk_FragColor.x = 64.0;\n"
+         "    sk_FragColor.x = 16.0;\n"
+         "    sk_FragColor.x = 19.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = -10.0;\n"
+         "    sk_FragColor.x = 11.0;\n"
+         "    sk_FragColor.x = -12.0;\n"
+         "    sk_FragColor.x = sqrt(1.0);\n"
+         "    sk_FragColor.x = sqrt(2.0);\n"
+         "    sk_FragColor.x = sqrt(3.0);\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = sqrt(5.0);\n"
+         "    sk_FragColor.x = sqrt(6.0);\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = sqrt(8.0);\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x += 1.0;\n"
+         "    sk_FragColor.x -= 1.0;\n"
+         "    sk_FragColor.x *= 2.0;\n"
+         "    sk_FragColor.x /= 2.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLBoolFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = 1 == 1 || 2 == 8 ? 1 : -1;"
+         "sk_FragColor.r = 1 > 1 || 2 == 8 ? 2 : -2;"
+         "sk_FragColor.r = 1 == 1 && 2 <= 8 ? 3 : -3;"
+         "sk_FragColor.r = 1 == 2 && 2 == 8 ? 4 : -4;"
+         "sk_FragColor.r = 1 == 1 ^^ 1 != 1 ? 5 : -5;"
+         "sk_FragColor.r = 1 == 1 ^^ 1 == 1 ? 6 : -6;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLVecFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = vec4(0.5, 1, 1, 1).x;"
+         "sk_FragColor = vec4(vec2(1), vec2(2, 3)) + vec4(5, 6, 7, 8);"
+         "sk_FragColor = vec4(8, vec3(10)) - vec4(1);"
+         "sk_FragColor = vec4(2) * vec4(1, 2, 3, 4);"
+         "sk_FragColor = vec4(12) / vec4(1, 2, 3, 4);"
+         "sk_FragColor.r = (vec4(12) / vec4(1, 2, 3, 4)).y;"
+         "sk_FragColor.x = vec4(1) == vec4(1) ? 1.0 : -1.0;"
+         "sk_FragColor.x = vec4(1) == vec4(2) ? 2.0 : -2.0;"
+         "sk_FragColor.x = vec2(1) == vec2(1, 1) ? 3.0 : -3.0;"
+         "sk_FragColor.x = vec2(1, 1) == vec2(1, 1) ? 4.0 : -4.0;"
+         "sk_FragColor.x = vec2(1) == vec2(1, 0) ? 5.0 : -5.0;"
+         "sk_FragColor.x = vec4(1) == vec4(vec2(1), vec2(1)) ? 6.0 : -6.0;"
+         "sk_FragColor.x = vec4(vec3(1), 1) == vec4(vec2(1), vec2(1)) ? 7.0 : -7.0;"
+         "sk_FragColor.x = vec4(vec3(1), 1) == vec4(vec2(1), 1, 0) ? 8.0 : -8.0;"
+         "sk_FragColor.x = vec2(1) != vec2(1, 0) ? 9.0 : -9.0;"
+         "sk_FragColor.x = vec4(1) != vec4(vec2(1), vec2(1)) ? 10.0 : -10.0;"
+         "sk_FragColor = vec4(sqrt(1)) * vec4(1);"
+         "sk_FragColor = vec4(1) * vec4(sqrt(2));"
+         "sk_FragColor = vec4(0) * vec4(sqrt(3));"
+         "sk_FragColor = vec4(sqrt(4)) * vec4(0);"
+         "sk_FragColor = vec4(0) / vec4(sqrt(5));"
+         "sk_FragColor = vec4(0) + vec4(sqrt(6));"
+         "sk_FragColor = vec4(sqrt(7)) + vec4(0);"
+         "sk_FragColor = vec4(sqrt(8)) - vec4(0);"
+         "sk_FragColor = vec4(0) + sqrt(9);"
+         "sk_FragColor = vec4(0) * sqrt(10);"
+         "sk_FragColor = vec4(0) / sqrt(11);"
+         "sk_FragColor = vec4(1) * sqrt(12);"
+         "sk_FragColor = 0 + vec4(sqrt(13));"
+         "sk_FragColor = 0 * vec4(sqrt(14));"
+         "sk_FragColor = 0 / vec4(sqrt(15));"
+         "sk_FragColor = 1 * vec4(sqrt(16));"
+         "sk_FragColor = vec4(sqrt(17)) + 0;"
+         "sk_FragColor = vec4(sqrt(18)) * 0;"
+         "sk_FragColor = vec4(sqrt(19)) * 1;"
+         "sk_FragColor = vec4(sqrt(19.5)) - 0;"
+         "sk_FragColor = sqrt(20) * vec4(1);"
+         "sk_FragColor = sqrt(21) + vec4(0);"
+         "sk_FragColor = sqrt(22) - vec4(0);"
+         "sk_FragColor = sqrt(23) / vec4(1);"
+         "sk_FragColor = vec4(sqrt(24)) / 1;"
+         "sk_FragColor += vec4(1);"
+         "sk_FragColor += vec4(0);"
+         "sk_FragColor -= vec4(1);"
+         "sk_FragColor -= vec4(0);"
+         "sk_FragColor *= vec4(1);"
+         "sk_FragColor *= vec4(2);"
+         "sk_FragColor /= vec4(1);"
+         "sk_FragColor /= vec4(2);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 0.5;\n"
+         "    sk_FragColor = vec4(6.0, 7.0, 9.0, 11.0);\n"
+         "    sk_FragColor = vec4(7.0, 9.0, 9.0, 9.0);\n"
+         "    sk_FragColor = vec4(2.0, 4.0, 6.0, 8.0);\n"
+         "    sk_FragColor = vec4(12.0, 6.0, 4.0, 3.0);\n"
+         "    sk_FragColor.x = 6.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = 4.0;\n"
+         "    sk_FragColor.x = -5.0;\n"
+         "    sk_FragColor.x = 6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = -10.0;\n"
+         "    sk_FragColor = vec4(sqrt(1.0));\n"
+         "    sk_FragColor = vec4(sqrt(2.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(6.0));\n"
+         "    sk_FragColor = vec4(sqrt(7.0));\n"
+         "    sk_FragColor = vec4(sqrt(8.0));\n"
+         "    sk_FragColor = vec4(sqrt(9.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(12.0));\n"
+         "    sk_FragColor = vec4(sqrt(13.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(16.0));\n"
+         "    sk_FragColor = vec4(sqrt(17.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(19.0));\n"
+         "    sk_FragColor = vec4(sqrt(19.5));\n"
+         "    sk_FragColor = vec4(sqrt(20.0));\n"
+         "    sk_FragColor = vec4(sqrt(21.0));\n"
+         "    sk_FragColor = vec4(sqrt(22.0));\n"
+         "    sk_FragColor = vec4(sqrt(23.0));\n"
+         "    sk_FragColor = vec4(sqrt(24.0));\n"
+         "    sk_FragColor += vec4(1.0);\n"
+         "    sk_FragColor -= vec4(1.0);\n"
+         "    sk_FragColor *= vec4(2.0);\n"
+         "    sk_FragColor /= vec4(2.0);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLMatFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.x = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) == "
+                          "mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 1 : -1;"
+         "sk_FragColor.x = mat2(vec2(1.0, 0.0), vec2(1.0, 1.0)) == "
+                          "mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 2 : -2;"
+         "sk_FragColor.x = mat2(1) == mat2(1) ? 3 : -3;"
+         "sk_FragColor.x = mat2(1) == mat2(0) ? 4 : -4;"
+         "sk_FragColor.x = mat2(1) == mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 5 : -5;"
+         "sk_FragColor.x = mat2(2) == mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 6 : -6;"
+         "sk_FragColor.x = mat3x2(2) == mat3x2(vec2(2.0, 0.0), vec2(0.0, 2.0), vec2(0.0)) ? 7 : -7;"
+         "sk_FragColor.x = mat2(1) != mat2(1) ? 8 : -8;"
+         "sk_FragColor.x = mat2(1) != mat2(0) ? 9 : -9;"
+         "sk_FragColor.x = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 0.0)) == "
+                          "mat3(mat2(1.0)) ? 10 : -10;"
+         "sk_FragColor.x = mat2(mat3(1.0)) == mat2(1.0) ? 11 : -11;"
+         "sk_FragColor.x = mat2(vec4(1.0, 0.0, 0.0, 1.0)) == mat2(1.0) ? 12 : -12;"
+         "sk_FragColor.x = mat2(1.0, 0.0, vec2(0.0, 1.0)) == mat2(1.0) ? 13 : -13;"
+         "sk_FragColor.x = mat2(vec2(1.0, 0.0), 0.0, 1.0) == mat2(1.0) ? 14 : -14;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = 10.0;\n"
+         "    sk_FragColor.x = 11.0;\n"
+         "    sk_FragColor.x = 12.0;\n"
+         "    sk_FragColor.x = 13.0;\n"
+         "    sk_FragColor.x = 14.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLConstantIf, r) {
     test(r,
          "void main() {"
          "int x;"
@@ -549,40 +843,34 @@
          "if (2 > 1) x = 2; else x = 3;"
          "if (1 > 2) x = 4; else x = 5;"
          "if (false) x = 6;"
+         "sk_FragColor.r = x;"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    int x;\n"
-         "    x = 1;\n"
-         "    x = 2;\n"
-         "    x = 5;\n"
-         "    {\n"
-         "    }\n"
+         "    sk_FragColor.x = 5.0;\n"
          "}\n");
 }
 
 DEF_TEST(SkSLCaps, r) {
     test(r,
          "void main() {"
-         "int x;"
+         "int x = 0;"
+         "int y = 0;"
+         "int z = 0;"
+         "int w = 0;"
          "if (sk_Caps.externalTextureSupport) x = 1;"
-         "if (sk_Caps.fbFetchSupport) x = 2;"
-         "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) x = 3;"
-         "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) x = 4;"
+         "if (sk_Caps.fbFetchSupport) y = 1;"
+         "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) z = 1;"
+         "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) w = 1;"
+         "sk_FragColor = vec4(x, y, z, w);"
          "}",
          *SkSL::ShaderCapsFactory::VariousCaps(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    int x;\n"
-         "    x = 1;\n"
-         "    {\n"
-         "    }\n"
-         "    x = 3;\n"
-         "    {\n"
-         "    }\n"
+         "    sk_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n"
          "}\n");
 }
 
@@ -595,6 +883,7 @@
          "vec4 b = texture(two, vec2(0));"
          "vec4 c = texture(one, vec2(0));"
          "vec4 d = texture(two, vec3(0));"
+         "sk_FragColor = vec4(a.x, b.x, c.x, d.x);"
          "}",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
@@ -606,6 +895,7 @@
          "    vec4 b = texture(two, vec2(0.0));\n"
          "    vec4 c = textureProj(one, vec2(0.0));\n"
          "    vec4 d = textureProj(two, vec3(0.0));\n"
+         "    sk_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
          "}\n");
     test(r,
          "uniform sampler1D one;"
@@ -615,6 +905,7 @@
          "vec4 b = texture(two, vec2(0));"
          "vec4 c = texture(one, vec2(0));"
          "vec4 d = texture(two, vec3(0));"
+         "sk_FragColor = vec4(a.x, b.x, c.x, d.x);"
          "}",
          *SkSL::ShaderCapsFactory::Version110(),
          "#version 110\n"
@@ -625,6 +916,7 @@
          "    vec4 b = texture2D(two, vec2(0.0));\n"
          "    vec4 c = texture1DProj(one, vec2(0.0));\n"
          "    vec4 d = texture2DProj(two, vec3(0.0));\n"
+         "    gl_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
          "}\n");
 }
 
@@ -740,13 +1032,14 @@
 DEF_TEST(SkSLArrayTypes, r) {
     test(r,
          "void main() { vec2 x[2] = vec2[2](vec2(1), vec2(2));"
-         "vec2[2] y = vec2[2](vec2(3), vec2(4)); }",
+         "vec2[2] y = vec2[2](vec2(3), vec2(4));"
+         "sk_FragColor = vec4(x[0], y[1]); }",
          *SkSL::ShaderCapsFactory::Default(),
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    vec2 x[2] = vec2[2](vec2(1.0), vec2(2.0));\n"
-         "    vec2[2] y = vec2[2](vec2(3.0), vec2(4.0));\n"
+         "    sk_FragColor = vec4(vec2[2](vec2(1.0), vec2(2.0))[0], "
+                                 "vec2[2](vec2(3.0), vec2(4.0))[1]);\n"
          "}\n");
 }
 
@@ -778,10 +1071,11 @@
 }
 
 DEF_TEST(SkSLSwitch, r) {
+    // basic "does a switch even work" test
     test(r,
          "void main() {"
          "    float x;"
-         "    switch (1) {"
+         "    switch (int(sqrt(1))) {"
          "        case 0:"
          "            x = 0.0;"
          "            break;"
@@ -798,7 +1092,7 @@
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
          "    float x;\n"
-         "    switch (1) {\n"
+         "    switch (int(sqrt(1.0))) {\n"
          "        case 0:\n"
          "            x = 0.0;\n"
          "            break;\n"
@@ -810,10 +1104,11 @@
          "    }\n"
          "    sk_FragColor = vec4(x);\n"
          "}\n");
+    // dead code inside of switch
     test(r,
          "void main() {"
          "    float x;"
-         "    switch (2) {"
+         "    switch (int(sqrt(2))) {"
          "        case 0:"
          "            x = 0.0;"
          "        case 1:"
@@ -827,21 +1122,21 @@
          "#version 400\n"
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
-         "    float x;\n"
-         "    switch (2) {\n"
+         "    switch (int(sqrt(2.0))) {\n"
          "        case 0:\n"
-         "            x = 0.0;\n"
+         "            ;\n"
          "        case 1:\n"
-         "            x = 1.0;\n"
+         "            ;\n"
          "        default:\n"
-         "            x = 2.0;\n"
+         "            ;\n"
          "    }\n"
          "    sk_FragColor = vec4(2.0);\n"
          "}\n");
+    // non-static test w/ fallthrough
     test(r,
          "void main() {"
          "    float x = 0.0;"
-         "    switch (3) {"
+         "    switch (int(sqrt(3))) {"
          "        case 0:"
          "            x = 0.0;"
          "        case 1:"
@@ -854,7 +1149,7 @@
          "out vec4 sk_FragColor;\n"
          "void main() {\n"
          "    float x = 0.0;\n"
-         "    switch (3) {\n"
+         "    switch (int(sqrt(3.0))) {\n"
          "        case 0:\n"
          "            x = 0.0;\n"
          "        case 1:\n"
@@ -862,6 +1157,222 @@
          "    }\n"
          "    sk_FragColor = vec4(x);\n"
          "}\n");
+    // static test w/ fallthrough
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+    // static test w/ fallthrough, different entry point
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (1) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+    // static test w/ break
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "}\n");
+    // static test w/ static conditional break
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            if (x < 1) break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "}\n");
+    // static test w/ non-static conditional break
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            if (x < sqrt(1)) break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 0.0;\n"
+         "    switch (0) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "            if (0.0 < sqrt(1.0)) break;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLRectangleTexture, r) {
+    test(r,
+         "uniform sampler2D test;"
+         "void main() {"
+         "    sk_FragColor = texture(test, vec2(0.5));"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler2D test;\n"
+         "void main() {\n"
+         "    sk_FragColor = texture(test, vec2(0.5));\n"
+         "}\n");
+    test(r,
+         "uniform sampler2DRect test;"
+         "void main() {"
+         "    sk_FragColor = texture(test, vec2(0.5));"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler2DRect test;\n"
+         "void main() {\n"
+         "    sk_FragColor = texture(test, textureSize(test) * vec2(0.5));\n"
+         "}\n");
+    test(r,
+         "uniform sampler2DRect test;"
+         "void main() {"
+         "    sk_FragColor = texture(test, vec3(0.5));"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler2DRect test;\n"
+         "void main() {\n"
+         "    sk_FragColor = texture(test, vec3(textureSize(test), 1.0) * vec3(0.5));\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLUnusedVars, r) {
+    test(r,
+         "void main() {"
+         "float a = 1, b = 2, c = 3;"
+         "float d = c;"
+         "float e = d;"
+         "b++;"
+         "d++;"
+         "sk_FragColor = vec4(b, b, d, d);"
+         "}",
+        *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float b = 2.0;\n"
+         "    float d = 3.0;\n"
+         "    b++;\n"
+         "    d++;\n"
+         "    sk_FragColor = vec4(b, b, d, d);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLMultipleAssignments, r) {
+    test(r,
+         "void main() {"
+         "float x;"
+         "float y;"
+         "int z;"
+         "x = y = z = 1;"
+         "sk_FragColor = vec4(z);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLComplexDelete, r) {
+    test(r,
+         "uniform mat4 colorXform;"
+         "uniform sampler2D sampler;"
+         "void main() {"
+         "vec4 tmpColor;"
+         "sk_FragColor = vec4(1.0) * (tmpColor = texture(sampler, vec2(1)) , "
+         "colorXform != mat4(1.0) ? vec4(clamp((mat4(colorXform) * vec4(tmpColor.xyz, 1.0)).xyz, "
+         "0.0, tmpColor.w), tmpColor.w) : tmpColor);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform mat4 colorXform;\n"
+         "uniform sampler2D sampler;\n"
+         "void main() {\n"
+         "    vec4 tmpColor;\n"
+         "    sk_FragColor = (tmpColor = texture(sampler, vec2(1.0)) , colorXform != mat4(1.0) ? "
+         "vec4(clamp((colorXform * vec4(tmpColor.xyz, 1.0)).xyz, 0.0, tmpColor.w), tmpColor.w) : "
+         "tmpColor);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLDependentInitializers, r) {
+    test(r,
+         "void main() {"
+         "float x = 0.5, y = x * 2;"
+         "sk_FragColor = vec4(y);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
 }
 
 #endif
diff --git a/tests/SkSLSPIRVTest.cpp b/tests/SkSLSPIRVTest.cpp
new file mode 100644
index 0000000..4d5457f
--- /dev/null
+++ b/tests/SkSLSPIRVTest.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "SkSLCompiler.h"
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
+    SkSL::Compiler compiler;
+    SkSL::Program::Settings settings;
+    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
+    settings.fCaps = caps.get();
+    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
+                                                                     SkString(src), settings);
+    if (program) {
+        SkSL::String ignored;
+        compiler.toSPIRV(*program, &ignored);
+    }
+    SkSL::String skError(error);
+    if (compiler.errorText() != skError) {
+        SkDebugf("SKSL ERROR:\n    source: %s\n    expected: %s    received: %s", src, error,
+                 compiler.errorText().c_str());
+    }
+    REPORTER_ASSERT(r, compiler.errorText() == skError);
+}
+
+DEF_TEST(SkSLBadOffset, r) {
+    test_failure(r,
+                 "struct Bad { layout (offset = 5) int x; } bad; void main() { bad.x = 5; }",
+                 "error: 1: offset of field 'x' must be a multiple of 4\n1 error\n");
+    test_failure(r,
+                 "struct Bad { int x; layout (offset = 0) int y; } bad; void main() { bad.x = 5; }",
+                 "error: 1: offset of field 'y' must be at least 4\n1 error\n");
+}
+
+#endif
diff --git a/tests/SpecialSurfaceTest.cpp b/tests/SpecialSurfaceTest.cpp
index 1867e25..a336cf6 100644
--- a/tests/SpecialSurfaceTest.cpp
+++ b/tests/SpecialSurfaceTest.cpp
@@ -5,6 +5,7 @@
 * found in the LICENSE file
 */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkSpecialImage.h"
 #include "SkSpecialSurface.h"
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index f620cff..b8092e4 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -25,6 +25,7 @@
 #include "GrRenderTargetContext.h"
 #include "GrGpu.h"
 #include "GrResourceProvider.h"
+#include "GrTest.h"
 #include <vector>
 #endif
 
@@ -331,7 +332,7 @@
     EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0))
     EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, testRect, nullptr))
     EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, nullptr))
-    EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint))
+    EXPECT_COPY_ON_WRITE(drawString(testText, 0, 1, testPaint))
     EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \
         testPaint))
     EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, nullptr, \
@@ -587,20 +588,24 @@
     const int kHeight = 10;
     std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
     sk_memset32(pixels.get(), color, kWidth * kHeight);
-    GrBackendTextureDesc desc;
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
-    desc.fWidth = kWidth;
-    desc.fHeight = kHeight;
-    desc.fFlags = kRenderTarget_GrBackendTextureFlag;
-    desc.fTextureHandle = context->getGpu()->createTestingOnlyBackendTexture(
+
+    GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture(
         pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
-    desc.fSampleCnt = sampleCnt;
-    sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(context, desc, nullptr);
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               kWidth,
+                                                               kHeight,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendHandle);
+
+    sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(context, backendTex,
+                                                                 kDefault_GrSurfaceOrigin, sampleCnt,
+                                                                 nullptr, nullptr);
     if (!surface) {
-        context->getGpu()->deleteTestingOnlyBackendTexture(desc.fTextureHandle);
+        context->getGpu()->deleteTestingOnlyBackendTexture(backendHandle);
         return nullptr;
     }
-    *outTexture = desc.fTextureHandle;
+    *outTexture = backendHandle;
     return surface;
 }
 
@@ -610,21 +615,23 @@
     const int kHeight = 10;
     std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
     sk_memset32(pixels.get(), color, kWidth * kHeight);
-    GrBackendTextureDesc desc;
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
-    desc.fWidth = kWidth;
-    desc.fHeight = kHeight;
-    desc.fFlags = kRenderTarget_GrBackendTextureFlag;
-    desc.fTextureHandle = context->getGpu()->createTestingOnlyBackendTexture(
+
+    GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture(
         pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
-    desc.fSampleCnt = sampleCnt;
-    sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTextureAsRenderTarget(context, desc,
-                                                                               nullptr);
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               kWidth,
+                                                               kHeight,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendHandle);
+    sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
+            context, backendTex, kDefault_GrSurfaceOrigin, sampleCnt, nullptr, nullptr);
+
     if (!surface) {
-        context->getGpu()->deleteTestingOnlyBackendTexture(desc.fTextureHandle);
+        context->getGpu()->deleteTestingOnlyBackendTexture(backendHandle);
         return nullptr;
     }
-    *outTexture = desc.fTextureHandle;
+    *outTexture = backendHandle;
     return surface;
 }
 
@@ -774,6 +781,9 @@
     if (!gpu) {
         return;
     }
+    if (gpu->caps()->avoidStencilBuffers()) {
+        return;
+    }
     static const uint32_t kOrigColor = 0xFFAABBCC;
 
     for (auto& surfaceFunc : {&create_gpu_surface_backend_texture,
@@ -879,26 +889,33 @@
 
     std::vector<GrBackendObject> textureHandles;
     auto wrappedSurfaceMaker = [context,&textureHandles](const SkImageInfo& info) {
-        GrBackendTextureDesc desc;
-        desc.fConfig = SkImageInfo2GrPixelConfig(info, *context->caps());
-        desc.fWidth = 10;
-        desc.fHeight = 10;
-        desc.fFlags = kRenderTarget_GrBackendTextureFlag;
-        desc.fTextureHandle = context->getGpu()->createTestingOnlyBackendTexture(
-            nullptr, desc.fWidth, desc.fHeight, desc.fConfig, true);
+        static const int kSize = 10;
+        GrPixelConfig config = SkImageInfo2GrPixelConfig(info, *context->caps());
 
-        if (!desc.fTextureHandle) {
+        GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture(
+                nullptr, kSize, kSize, config, true);
+
+        if (!backendHandle) {
             return sk_sp<SkSurface>(nullptr);
         }
-        textureHandles.push_back(desc.fTextureHandle);
+        textureHandles.push_back(backendHandle);
 
-        return SkSurface::MakeFromBackendTexture(context, desc, sk_ref_sp(info.colorSpace()),
-                                                 nullptr);
+        GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                                   kSize,
+                                                                   kSize,
+                                                                   config,
+                                                                   backendHandle);
+
+        return SkSurface::MakeFromBackendTexture(context, backendTex,
+                                                 kDefault_GrSurfaceOrigin, 0,
+                                                 sk_ref_sp(info.colorSpace()), nullptr);
     };
 
     test_surface_creation_and_snapshot_with_color_space(reporter, "wrapped", f16Support,
                                                         wrappedSurfaceMaker);
 
+    context->flush();
+
     for (auto textureHandle : textureHandles) {
         context->getGpu()->deleteTestingOnlyBackendTexture(textureHandle);
     }
@@ -912,7 +929,6 @@
 
     SkBitmap bitmap;
     image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
-    bitmap.lockPixels();
     for (int y = 0; y < 10; y++) {
         for (int x = 0; x < 10; x++) {
             REPORTER_ASSERT(r, 1 == SkGetPackedA32(*bitmap.getAddr32(x, y)));
diff --git a/tests/TDPQueueTest.cpp b/tests/TDPQueueTest.cpp
index 70367a7..75eab25 100644
--- a/tests/TDPQueueTest.cpp
+++ b/tests/TDPQueueTest.cpp
@@ -150,7 +150,56 @@
    }
 }
 
+void sort_test(skiatest::Reporter* reporter) {
+    SkRandom random;
+
+    SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqTest;
+    SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqControl;
+
+    // Create a random set of Dummy objects and populate the test queue.
+    int count = random.nextULessThan(100);
+    SkTDArray<Dummy> testArray;
+    testArray.setReserve(count);
+    for (int i = 0; i < count; i++) {
+        Dummy *dummy = testArray.append();
+        dummy->fPriority = random.nextS();
+        dummy->fValue = random.nextS();
+        dummy->fIndex = -1;
+        pqTest.insert(&testArray[i]);
+    }
+
+    // Stick equivalent dummy objects into the control queue.
+    SkTDArray<Dummy> controlArray;
+    controlArray.setReserve(count);
+    for (int i = 0; i < count; i++) {
+        Dummy *dummy = controlArray.append();
+        dummy->fPriority = testArray[i].fPriority;
+        dummy->fValue = testArray[i].fValue;
+        dummy->fIndex = -1;
+        pqControl.insert(&controlArray[i]);
+    }
+
+    // Sort the queue
+    pqTest.sort();
+
+    // Compare elements in the queue to ensure they are in sorted order
+    int prevPriority = pqTest.peek()->fPriority;
+    for (int i = 0; i < count; i++) {
+        REPORTER_ASSERT(reporter, i <= pqTest.at(i)->fIndex);
+        REPORTER_ASSERT(reporter, prevPriority <= pqTest.at(i)->fPriority);
+        prevPriority = pqTest.at(i)->fPriority;
+    }
+
+    // Verify that after sorting the queue still produces the same result as the control queue
+    for (int i = 0; i < count; i++) {
+        REPORTER_ASSERT(reporter, *pqControl.peek() == *pqTest.peek());
+        pqControl.pop();
+        pqTest.pop();
+    }
+}
+
 DEF_TEST(TDPQueueTest, reporter) {
     simple_test(reporter);
     random_test(reporter);
+    sort_test(reporter);
 }
diff --git a/tests/TemplatesTest.cpp b/tests/TemplatesTest.cpp
index fc3bc32..9d5ca77 100644
--- a/tests/TemplatesTest.cpp
+++ b/tests/TemplatesTest.cpp
@@ -126,3 +126,20 @@
     test_realloc_to_zero<SkAutoTMalloc<int> >(reporter);
     test_realloc_to_zero<SkAutoSTMalloc<kStackPreallocCount, int> >(reporter);
 }
+
+DEF_TEST(SkAutoTMallocSelfMove, r) {
+#if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wself-move"
+#endif
+
+    SkAutoTMalloc<int> foo(20);
+    REPORTER_ASSERT(r, foo.get());
+
+    foo = std::move(foo);
+    REPORTER_ASSERT(r, foo.get());
+
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+}
diff --git a/tests/TessellatingPathRendererTests.cpp b/tests/TessellatingPathRendererTests.cpp
index 8579c4d..7871193 100644
--- a/tests/TessellatingPathRendererTests.cpp
+++ b/tests/TessellatingPathRendererTests.cpp
@@ -13,6 +13,7 @@
 #include "GrClip.h"
 #include "GrContext.h"
 #include "SkGradientShader.h"
+#include "SkShaderBase.h"
 #include "ops/GrTessellatingPathRenderer.h"
 
 /*
@@ -261,14 +262,66 @@
     return path;
 }
 
+// A shape with a vertex collinear to the right hand edge.
+// This messes up find_enclosing_edges.
+static SkPath create_path_18() {
+    SkPath path;
+    path.moveTo(80, 20);
+    path.lineTo(80, 60);
+    path.lineTo(20, 60);
+    path.moveTo(80, 50);
+    path.lineTo(80, 80);
+    path.lineTo(20, 80);
+    return path;
+}
+
+// Exercises the case where an edge becomes collinear with *two* of its
+// adjacent neighbour edges after splitting.
+// This is a reduction from
+// http://mooooo.ooo/chebyshev-sine-approximation/horner_ulp.svg
+static SkPath create_path_19() {
+    SkPath path;
+    path.moveTo(  351.99298095703125,         348.23046875);
+    path.lineTo(  351.91876220703125,         347.33984375);
+    path.lineTo(  351.91876220703125,          346.1953125);
+    path.lineTo(  351.90313720703125,           347.734375);
+    path.lineTo(  351.90313720703125,          346.1328125);
+    path.lineTo(  351.87579345703125,         347.93359375);
+    path.lineTo(  351.87579345703125,           345.484375);
+    path.lineTo(  351.86407470703125,          347.7890625);
+    path.lineTo(  351.86407470703125,          346.2109375);
+    path.lineTo(  351.84844970703125,   347.63763427734375);
+    path.lineTo(  351.84454345703125,   344.19232177734375);
+    path.lineTo(  351.78204345703125,    346.9483642578125);
+    path.lineTo( 351.758636474609375,      347.18310546875);
+    path.lineTo(  351.75469970703125,               346.75);
+    path.lineTo(  351.75469970703125,            345.46875);
+    path.lineTo(         352.5546875,            345.46875);
+    path.lineTo(        352.55078125,         347.01953125);
+    path.lineTo(  351.75079345703125,   347.02313232421875);
+    path.lineTo(  351.74688720703125,   346.15203857421875);
+    path.lineTo(  351.74688720703125,  347.646148681640625);
+    path.lineTo(         352.5390625,         346.94140625);
+    path.lineTo(  351.73907470703125,   346.94268798828125);
+    path.lineTo(  351.73516845703125,   344.48565673828125);
+    path.lineTo(          352.484375,         346.73828125);
+    path.lineTo(  351.68438720703125,    346.7401123046875);
+    path.lineTo(         352.4765625,           346.546875);
+    path.lineTo(  351.67657470703125,   346.54937744140625);
+    path.lineTo(        352.47265625,         346.75390625);
+    path.lineTo(  351.67266845703125,  346.756622314453125);
+    path.lineTo(  351.66876220703125,  345.612091064453125);
+    return path;
+}
+
 static sk_sp<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) {
     SkPoint pts[2] = { {0, 0}, {1, 1} };
     SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE };
     sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
         pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode);
-    SkShader::AsFPArgs args(
+    SkShaderBase::AsFPArgs args(
         ctx, &SkMatrix::I(), &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, nullptr);
-    return shader->asFragmentProcessor(args);
+    return as_SB(shader)->asFragmentProcessor(args);
 }
 
 static void test_path(GrContext* ctx,
@@ -303,7 +356,8 @@
 DEF_GPUTEST_FOR_ALL_CONTEXTS(TessellatingPathRendererTests, reporter, ctxInfo) {
     GrContext* ctx = ctxInfo.grContext();
 
-    sk_sp<GrRenderTargetContext> rtc(ctx->makeRenderTargetContext(SkBackingFit::kApprox,
+    sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext(
+                                                                  SkBackingFit::kApprox,
                                                                   800, 800,
                                                                   kRGBA_8888_GrPixelConfig,
                                                                   nullptr,
@@ -334,5 +388,7 @@
     SkMatrix nonInvertibleMatrix = SkMatrix::MakeScale(0, 0);
     sk_sp<GrFragmentProcessor> fp(create_linear_gradient_processor(ctx));
     test_path(ctx, rtc.get(), create_path_17(), nonInvertibleMatrix, GrAAType::kCoverage, fp);
+    test_path(ctx, rtc.get(), create_path_18());
+    test_path(ctx, rtc.get(), create_path_19());
 }
 #endif
diff --git a/tests/TestConfigParsing.cpp b/tests/TestConfigParsing.cpp
index 7dfda4f..83051aa 100644
--- a/tests/TestConfigParsing.cpp
+++ b/tests/TestConfigParsing.cpp
@@ -80,7 +80,7 @@
         "pdf", "skp", "svg", "xps", "angle_d3d11_es2", "angle_gl_es2", "commandbuffer", "mesa",
         "hwui", "glf16", "glessrgb", "gl", "glnvpr4", "glnvprdit4", "glsrgb", "glmsaa4", "vk",
         "glinst", "glinst4", "glinstdit4", "glinst8", "glinstdit8", "glesinst", "glesinst4",
-        "glesinstdit4", "glwide", "glnarrow"
+        "glesinstdit4", "glwide", "glnarrow", "glnostencils"
     });
 
     SkCommandLineConfigArray configs;
@@ -314,6 +314,8 @@
 #endif
     }
 }
+
+#if SK_SUPPORT_GPU
 DEF_TEST(ParseConfigs_ViaParsing, reporter) {
     SkCommandLineFlags::StringArray config1 = make_string_array({
         "a-b-c-8888",
@@ -344,6 +346,7 @@
         }
     }
 }
+#endif
 
 DEF_TEST(ParseConfigs_ViaParsingExtendedForm, reporter) {
     SkCommandLineFlags::StringArray config1 = make_string_array({
diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp
index d034961..aca7509 100644
--- a/tests/TestUtils.cpp
+++ b/tests/TestUtils.cpp
@@ -80,6 +80,8 @@
         }
 
         copyDstDesc.fFlags = flags;
+        copyDstDesc.fOrigin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin
+                                                              : kBottomLeft_GrSurfaceOrigin;
 
         sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, copyDstDesc, proxy));
 
@@ -106,6 +108,8 @@
 
     for (auto flags : { kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag }) {
         copySrcDesc.fFlags = flags;
+        copySrcDesc.fOrigin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin
+                                                              : kBottomLeft_GrSurfaceOrigin;
 
         sk_sp<GrTextureProxy> src(GrSurfaceProxy::MakeDeferred(resourceProvider,
                                                                copySrcDesc,
diff --git a/tests/TypefaceTest.cpp b/tests/TypefaceTest.cpp
index 4786cc2..1da94bb 100644
--- a/tests/TypefaceTest.cpp
+++ b/tests/TypefaceTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkAdvancedTypefaceMetrics.h"
 #include "SkData.h"
 #include "SkFixed.h"
 #include "SkFontMgr.h"
@@ -96,10 +97,14 @@
         REPORT_FAILURE(reporter, "distortable", SkString());
         return;
     }
+    constexpr int numberOfAxesInDistortable = 1;
 
     sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
+    // The position may be over specified. If there are multiple values for a given axis,
+    // ensure the last one since that's what css-fonts-4 requires.
     const SkFontArguments::VariationPosition::Coordinate position[] = {
-        { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 }
+        { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
+        { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
     };
     SkFontArguments params;
     params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
@@ -110,17 +115,17 @@
     if (count == -1) {
         return;
     }
-    REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(position));
+    REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
 
-    SkFontArguments::VariationPosition::Coordinate positionRead[SK_ARRAY_COUNT(position)];
+    SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
     count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
-    REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(position));
+    REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
 
-    REPORTER_ASSERT(reporter, positionRead[0].axis == position[0].axis);
+    REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
 
     // Convert to fixed for "almost equal".
     SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
-    SkFixed fixedOriginal = SkScalarToFixed(position[0].value);
+    SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
     REPORTER_ASSERT(reporter, fixedRead == fixedOriginal);
 }
 
@@ -192,9 +197,9 @@
         return nullptr;
     }
     void onFilterRec(SkScalerContextRec*) const override { }
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                PerGlyphInfo,
-                                const uint32_t*, uint32_t) const override { return nullptr; }
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
+        return nullptr;
+    }
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
     virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
                                 uint16_t glyphs[], int glyphCount) const override {
diff --git a/tests/VarAllocTest.cpp b/tests/VarAllocTest.cpp
deleted file mode 100644
index 77eaa89..0000000
--- a/tests/VarAllocTest.cpp
+++ /dev/null
@@ -1,20 +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.
- */
-
-#include "Test.h"
-#include "SkVarAlloc.h"
-
-DEF_TEST(VarAlloc, r) {
-    SkVarAlloc va(4/*start allocating at 16B*/);
-    char* p = va.alloc(128);
-    sk_bzero(p, 128);  // Just checking this is safe.
-
-#if !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__)
-    // This method will always return 0 on Android and UCLIBC platforms.
-    REPORTER_ASSERT(r, va.approxBytesAllocated() >= 128);
-#endif
-}
diff --git a/tests/VerticesTest.cpp b/tests/VerticesTest.cpp
index 9621d80..399aba2 100644
--- a/tests/VerticesTest.cpp
+++ b/tests/VerticesTest.cpp
@@ -50,8 +50,8 @@
 }
 
 DEF_TEST(Vertices, reporter) {
-    int vCount = 4;
-    int iCount = 6;
+    int vCount = 5;
+    int iCount = 9; // odd value exercises padding logic in encode()
 
     const uint32_t texFlags[] = { 0, SkVertices::kHasTexCoords_BuilderFlag };
     const uint32_t colFlags[] = { 0, SkVertices::kHasColors_BuilderFlag };
@@ -59,7 +59,7 @@
         for (auto colF : colFlags) {
             uint32_t flags = texF | colF;
 
-            SkVertices::Builder builder(SkCanvas::kTriangles_VertexMode, vCount, iCount, flags);
+            SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vCount, iCount, flags);
 
             for (int i = 0; i < vCount; ++i) {
                 float x = (float)i;
diff --git a/tests/VkUploadPixelsTests.cpp b/tests/VkUploadPixelsTests.cpp
index 8b6a56b..7f5001c 100644
--- a/tests/VkUploadPixelsTests.cpp
+++ b/tests/VkUploadPixelsTests.cpp
@@ -12,7 +12,10 @@
 #if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_VULKAN)
 
 #include "GrContextFactory.h"
+#include "GrContextPriv.h"
+#include "GrSurfaceProxy.h"
 #include "GrTest.h"
+#include "SkGr.h"
 #include "Test.h"
 #include "vk/GrVkGpu.h"
 
@@ -32,7 +35,6 @@
 
 bool does_full_buffer_contain_correct_color(GrColor* srcBuffer,
                                             GrColor* dstBuffer,
-                                            GrPixelConfig config,
                                             int width,
                                             int height) {
     GrColor* srcPtr = srcBuffer;
@@ -50,9 +52,7 @@
 }
 
 void basic_texture_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config,
-                        bool renderTarget, bool linearTiling) {
-    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
-
+                        bool renderTarget) {
     const int kWidth = 16;
     const int kHeight = 16;
     SkAutoTMalloc<GrColor> srcBuffer(kWidth*kHeight);
@@ -60,87 +60,93 @@
 
     fill_pixel_data(kWidth, kHeight, srcBuffer.get());
 
-    const GrVkCaps* caps = reinterpret_cast<const GrVkCaps*>(context->caps());
-
-    bool canCreate = true;
-    // the expectation is that the given config is texturable/renderable with optimal tiling
-    // but may not be with linear tiling
-    if (linearTiling) {
-        if (!caps->isConfigTexturableLinearly(config) ||
-            (renderTarget && !caps->isConfigRenderableLinearly(config, false))) {
-            canCreate = false;
-        }
-    }
-
     GrSurfaceDesc surfDesc;
     surfDesc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
-    if (linearTiling) {
-        surfDesc.fFlags |= kZeroCopy_GrSurfaceFlag;
-    }
     surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
     surfDesc.fWidth = kWidth;
     surfDesc.fHeight = kHeight;
     surfDesc.fConfig = config;
     surfDesc.fSampleCnt = 0;
-    GrTexture* tex0 = gpu->createTexture(surfDesc, SkBudgeted::kNo, srcBuffer, 0);
-    if (tex0) {
-        REPORTER_ASSERT(reporter, canCreate);
-        gpu->readPixels(tex0, 0, 0, kWidth, kHeight, config, dstBuffer, 0);
+
+    SkColorType ct;
+    SkAssertResult(GrPixelConfigToColorType(config, &ct));
+
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                               surfDesc, SkBudgeted::kNo,
+                                                               srcBuffer, 0);
+    REPORTER_ASSERT(reporter, proxy);
+    if (proxy) {
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                                proxy, nullptr);
+
+        SkImageInfo dstInfo = SkImageInfo::Make(kWidth, kHeight, ct, kOpaque_SkAlphaType);
+
+        bool result = sContext->readPixels(dstInfo, dstBuffer, 0, 0, 0);
+        REPORTER_ASSERT(reporter, result);
         REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
                                                                          dstBuffer,
-                                                                         config,
                                                                          kWidth,
                                                                          kHeight));
 
-        tex0->writePixels(2, 10, 10, 2, config, srcBuffer);
+        dstInfo = SkImageInfo::Make(10, 2, ct, kOpaque_SkAlphaType);
+        result = sContext->writePixels(dstInfo, srcBuffer, 0, 2, 10);
+        REPORTER_ASSERT(reporter, result);
+
         memset(dstBuffer, 0, kWidth*kHeight*sizeof(GrColor));
-        gpu->readPixels(tex0, 2, 10, 10, 2, config, dstBuffer, 0);
+
+        result = sContext->readPixels(dstInfo, dstBuffer, 0, 2, 10);
+        REPORTER_ASSERT(reporter, result);
+
         REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
                                                                          dstBuffer,
-                                                                         config,
                                                                          10,
                                                                          2));
-
-        tex0->unref();
-    } else {
-        REPORTER_ASSERT(reporter, !canCreate);
     }
 
     surfDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    GrTexture* tex1 = gpu->createTexture(surfDesc, SkBudgeted::kNo, srcBuffer, 0);
-    if (tex1) {
-        REPORTER_ASSERT(reporter, canCreate);
-        gpu->readPixels(tex1, 0, 0, kWidth, kHeight, config, dstBuffer, 0);
+
+    proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                         surfDesc, SkBudgeted::kNo,
+                                         srcBuffer, 0);
+    REPORTER_ASSERT(reporter, proxy);
+    if (proxy) {
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                                proxy, nullptr);
+
+        SkImageInfo dstInfo = SkImageInfo::Make(kWidth, kHeight, ct, kOpaque_SkAlphaType);
+
+        bool result = sContext->readPixels(dstInfo, dstBuffer, 0, 0, 0);
+        REPORTER_ASSERT(reporter, result);
         REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
                                                                          dstBuffer,
-                                                                         config,
                                                                          kWidth,
                                                                          kHeight));
 
-        tex1->writePixels(5, 4, 4, 5, config, srcBuffer);
+        dstInfo = SkImageInfo::Make(4, 5, ct, kOpaque_SkAlphaType);
+        result = sContext->writePixels(dstInfo, srcBuffer, 0, 5, 4);
+        REPORTER_ASSERT(reporter, result);
+
         memset(dstBuffer, 0, kWidth*kHeight*sizeof(GrColor));
-        gpu->readPixels(tex1, 5, 4, 4, 5, config, dstBuffer, 0);
+
+        result = sContext->readPixels(dstInfo, dstBuffer, 0, 5, 4);
+        REPORTER_ASSERT(reporter, result);
+
         REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
                                                                          dstBuffer,
-                                                                         config,
                                                                          4,
                                                                          5));
 
-        tex1->unref();
-    } else {
-        REPORTER_ASSERT(reporter, !canCreate);
     }
 }
 
 DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkUploadPixelsTests, reporter, ctxInfo) {
-    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, false, false);
-    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, true, false);
-    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, false, true);
-    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, true, true);
-    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, false, false);
-    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, true, false);
-    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, false, true);
-    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, true, true);
+    // RGBA
+    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, false);
+    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, true);
+
+    // BGRA
+    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, false);
+    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, true);
 }
 
 #endif
diff --git a/tests/VkWrapTests.cpp b/tests/VkWrapTests.cpp
index 9279ef6..c2edae4 100644
--- a/tests/VkWrapTests.cpp
+++ b/tests/VkWrapTests.cpp
@@ -31,37 +31,58 @@
 
     GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig,
                                                                       false);
-    const GrVkImageInfo* backendTex = reinterpret_cast<const GrVkImageInfo*>(backendObj);
+    const GrVkImageInfo* imageInfo = reinterpret_cast<const GrVkImageInfo*>(backendObj);
 
-    // check basic borrowed creation
-    GrBackendTextureDesc desc;
-    desc.fConfig = kPixelConfig;
-    desc.fWidth = kW;
-    desc.fHeight = kH;
-    desc.fTextureHandle = backendObj;
-    sk_sp<GrTexture> tex = gpu->wrapBackendTexture(desc, kBorrow_GrWrapOwnership);
+    GrBackendTexture backendTex = GrBackendTexture(kW, kH, *imageInfo);
+    sk_sp<GrTexture> tex = gpu->wrapBackendTexture(backendTex,
+                                                   kTopLeft_GrSurfaceOrigin,
+                                                   kNone_GrBackendTextureFlag,
+                                                   0,
+                                                   kBorrow_GrWrapOwnership);
     REPORTER_ASSERT(reporter, tex);
 
     // image is null
-    GrVkImageInfo backendCopy = *backendTex;
+    GrVkImageInfo backendCopy = *imageInfo;
     backendCopy.fImage = VK_NULL_HANDLE;
-    desc.fTextureHandle = (GrBackendObject) &backendCopy;
-    tex = gpu->wrapBackendTexture(desc, kBorrow_GrWrapOwnership);
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kNone_GrBackendTextureFlag,
+                                  0,
+                                  kBorrow_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
-    tex = gpu->wrapBackendTexture(desc, kAdopt_GrWrapOwnership);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kNone_GrBackendTextureFlag,
+                                  0,
+                                  kAdopt_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
 
     // alloc is null
-    backendCopy.fImage = backendTex->fImage;
+    backendCopy.fImage = imageInfo->fImage;
     backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
-    tex = gpu->wrapBackendTexture(desc, kBorrow_GrWrapOwnership);
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kNone_GrBackendTextureFlag,
+                                  0,
+                                  kBorrow_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
-    tex = gpu->wrapBackendTexture(desc, kAdopt_GrWrapOwnership);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kNone_GrBackendTextureFlag,
+                                  0,
+                                  kAdopt_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
-
     // check adopt creation
-    backendCopy.fAlloc = backendTex->fAlloc;
-    tex = gpu->wrapBackendTexture(desc, kAdopt_GrWrapOwnership);
+    backendCopy.fAlloc = imageInfo->fAlloc;
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kNone_GrBackendTextureFlag,
+                                  0,
+                                  kAdopt_GrWrapOwnership);
+
     REPORTER_ASSERT(reporter, tex);
 
     gpu->deleteTestingOnlyBackendTexture(backendObj, true);
@@ -74,30 +95,24 @@
                                                                       true);
     const GrVkImageInfo* backendTex = reinterpret_cast<const GrVkImageInfo*>(backendObj);
 
-    // check basic borrowed creation
-    GrBackendRenderTargetDesc desc;
-    desc.fWidth = kW;
-    desc.fHeight = kH;
-    desc.fConfig = kPixelConfig;
-    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-    desc.fSampleCnt = 0;
-    desc.fStencilBits = 0;
-    desc.fRenderTargetHandle = backendObj;
-    sk_sp<GrRenderTarget> rt = gpu->wrapBackendRenderTarget(desc);
+    GrBackendRenderTarget backendRT(kW, kH, 0, 0, *backendTex);
+
+    sk_sp<GrRenderTarget> rt = gpu->wrapBackendRenderTarget(backendRT, kTopLeft_GrSurfaceOrigin);
     REPORTER_ASSERT(reporter, rt);
 
     // image is null
     GrVkImageInfo backendCopy = *backendTex;
     backendCopy.fImage = VK_NULL_HANDLE;
-    desc.fRenderTargetHandle = (GrBackendObject)&backendCopy;
-    rt = gpu->wrapBackendRenderTarget(desc);
+    GrBackendRenderTarget backendRT2(kW, kH, 0, 0, backendCopy);
+    rt = gpu->wrapBackendRenderTarget(backendRT2, kTopLeft_GrSurfaceOrigin);
     REPORTER_ASSERT(reporter, !rt);
 
     // alloc is null
     backendCopy.fImage = backendTex->fImage;
     backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
     // can wrap null alloc
-    rt = gpu->wrapBackendRenderTarget(desc);
+    GrBackendRenderTarget backendRT3(kW, kH, 0, 0, backendCopy);
+    rt = gpu->wrapBackendRenderTarget(backendRT3, kTopLeft_GrSurfaceOrigin);
     REPORTER_ASSERT(reporter, rt);
 
     // When we wrapBackendRenderTarget it is always borrowed, so we must make sure to free the
@@ -110,38 +125,58 @@
 
     GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig,
                                                                       true);
-    const GrVkImageInfo* backendTex = reinterpret_cast<const GrVkImageInfo*>(backendObj);
+    const GrVkImageInfo* imageInfo = reinterpret_cast<const GrVkImageInfo*>(backendObj);
 
-    // check basic borrowed creation
-    GrBackendTextureDesc desc;
-    desc.fFlags = kRenderTarget_GrBackendTextureFlag;
-    desc.fConfig = kPixelConfig;
-    desc.fWidth = kW;
-    desc.fHeight = kH;
-    desc.fTextureHandle = backendObj;
-    sk_sp<GrTexture> tex = gpu->wrapBackendTexture(desc, kBorrow_GrWrapOwnership);
+    GrBackendTexture backendTex = GrBackendTexture(kW, kH, *imageInfo);
+    sk_sp<GrTexture> tex = gpu->wrapBackendTexture(backendTex,
+                                                   kTopLeft_GrSurfaceOrigin,
+                                                   kRenderTarget_GrBackendTextureFlag,
+                                                   0,
+                                                   kBorrow_GrWrapOwnership);
     REPORTER_ASSERT(reporter, tex);
 
     // image is null
-    GrVkImageInfo backendCopy = *backendTex;
+    GrVkImageInfo backendCopy = *imageInfo;
     backendCopy.fImage = VK_NULL_HANDLE;
-    desc.fTextureHandle = (GrBackendObject)&backendCopy;
-    tex = gpu->wrapBackendTexture(desc, kBorrow_GrWrapOwnership);
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kRenderTarget_GrBackendTextureFlag,
+                                  0,
+                                  kBorrow_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
-    tex = gpu->wrapBackendTexture(desc, kAdopt_GrWrapOwnership);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kRenderTarget_GrBackendTextureFlag,
+                                  0,
+                                  kAdopt_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
 
     // alloc is null
-    backendCopy.fImage = backendTex->fImage;
+    backendCopy.fImage = imageInfo->fImage;
     backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
-    tex = gpu->wrapBackendTexture(desc, kBorrow_GrWrapOwnership);
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kRenderTarget_GrBackendTextureFlag,
+                                  0,
+                                  kBorrow_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
-    tex = gpu->wrapBackendTexture(desc, kAdopt_GrWrapOwnership);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kRenderTarget_GrBackendTextureFlag,
+                                  0,
+                                  kAdopt_GrWrapOwnership);
     REPORTER_ASSERT(reporter, !tex);
 
     // check adopt creation
-    backendCopy.fAlloc = backendTex->fAlloc;
-    tex = gpu->wrapBackendTexture(desc, kAdopt_GrWrapOwnership);
+    backendCopy.fAlloc = imageInfo->fAlloc;
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kRenderTarget_GrBackendTextureFlag,
+                                  0,
+                                  kAdopt_GrWrapOwnership);
     REPORTER_ASSERT(reporter, tex);
 
     gpu->deleteTestingOnlyBackendTexture(backendObj, true);
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index c38e4d6..e26e134 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -14,6 +14,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrGpu.h"
 #endif
 
 #include <initializer_list>
@@ -191,11 +192,11 @@
     // At some point this will be unsupported, as we won't allow accessBitmap() to magically call
     // readPixels for the client.
     SkBitmap secretDevBitmap;
-    if (!canvas->readPixels(canvasInfo.bounds(), &secretDevBitmap)) {
+    secretDevBitmap.allocN32Pixels(canvasInfo.width(), canvasInfo.height());
+    if (!canvas->readPixels(secretDevBitmap, 0, 0)) {
         return false;
     }
 
-    SkAutoLockPixels alp(secretDevBitmap);
     canvasRowBytes = secretDevBitmap.rowBytes();
     canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels());
 
@@ -263,7 +264,7 @@
     if (!bm->setInfo(info, rowBytes)) {
         return false;
     }
-    sk_sp<SkPixelRef> pr(SkMallocPixelRef::NewAllocate(info, rowBytes, nullptr));
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, rowBytes, nullptr);
     bm->setPixelRef(std::move(pr), 0, 0);
     return true;
 }
@@ -278,7 +279,6 @@
     if (!alloc_row_bytes(bm, info, rowBytes)) {
         return false;
     }
-    SkAutoLockPixels alp(*bm);
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
             *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at);
@@ -411,9 +411,45 @@
     const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
 
     for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
-        sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo,
-                                                             ii, 0, origin, nullptr));
-        test_write_pixels(reporter, surface.get());
+        for (int sampleCnt : {0, 4}) {
+            sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
+                                                                 SkBudgeted::kNo, ii, sampleCnt,
+                                                                 origin, nullptr));
+            if (!surface && sampleCnt > 0) {
+                // Some platforms don't support MSAA
+                continue;
+            }
+            test_write_pixels(reporter, surface.get());
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTexture_Gpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
+        for (int sampleCnt : {0, 4}) {
+            GrBackendTextureDesc desc;
+            desc.fConfig = kSkia8888_GrPixelConfig;
+            desc.fWidth = DEV_W;
+            desc.fHeight = DEV_H;
+            desc.fFlags = kRenderTarget_GrBackendTextureFlag;
+            desc.fSampleCnt = sampleCnt;
+            desc.fOrigin = origin;
+            desc.fTextureHandle = context->getGpu()->createTestingOnlyBackendTexture(
+                nullptr, DEV_W, DEV_H, kSkia8888_GrPixelConfig, true);
+            sk_sp<SkSurface> surface(SkSurface::MakeFromBackendTextureAsRenderTarget(context, desc,
+                                                                                     nullptr));
+            if (!surface) {
+                context->getGpu()->deleteTestingOnlyBackendTexture(desc.fTextureHandle);
+                continue;
+            }
+
+            test_write_pixels(reporter, surface.get());
+
+            surface.reset();
+            context->getGpu()->deleteTestingOnlyBackendTexture(desc.fTextureHandle);
+        }
     }
 }
 #endif
diff --git a/third_party/angle2/BUILD.gn b/third_party/angle2/BUILD.gn
index b6bec35..547d7af 100644
--- a/third_party/angle2/BUILD.gn
+++ b/third_party/angle2/BUILD.gn
@@ -41,6 +41,7 @@
     "$root_gen_dir/angle2",
     "$angle_root/include",
     "$angle_root/src",
+    "$angle_root/src/third_party/mrucache",
     "$angle_root/src/common/third_party/numerics",
     "$angle_root/src/third_party/khronos",
   ]
diff --git a/third_party/gif/SkGifImageReader.cpp b/third_party/gif/SkGifImageReader.cpp
index f617ccd..0666fed 100644
--- a/third_party/gif/SkGifImageReader.cpp
+++ b/third_party/gif/SkGifImageReader.cpp
@@ -131,19 +131,19 @@
         drowEnd = drowStart + rowDup;
 
         // Extend if bottom edge isn't covered because of the shift upward.
-        if (((m_frameContext->height() - 1) - drowEnd) <= rowShift)
+        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 ((unsigned)drowEnd >= m_frameContext->height())
+        if (drowEnd >= m_frameContext->height())
             drowEnd = m_frameContext->height() - 1;
     }
 
     // Protect against too much image data.
-    if ((unsigned)drowStart >= m_frameContext->height())
+    if (drowStart >= m_frameContext->height())
         return true;
 
     // CALLBACK: Let the client know we have decoded a row.
@@ -159,7 +159,7 @@
             switch (ipass) {
             case 1:
                 irow += 8;
-                if (irow >= m_frameContext->height()) {
+                if (irow >= (unsigned) m_frameContext->height()) {
                     ipass++;
                     irow = 4;
                 }
@@ -167,7 +167,7 @@
 
             case 2:
                 irow += 8;
-                if (irow >= m_frameContext->height()) {
+                if (irow >= (unsigned) m_frameContext->height()) {
                     ipass++;
                     irow = 2;
                 }
@@ -175,7 +175,7 @@
 
             case 3:
                 irow += 4;
-                if (irow >= m_frameContext->height()) {
+                if (irow >= (unsigned) m_frameContext->height()) {
                     ipass++;
                     irow = 1;
                 }
@@ -183,7 +183,7 @@
 
             case 4:
                 irow += 2;
-                if (irow >= m_frameContext->height()) {
+                if (irow >= (unsigned) m_frameContext->height()) {
                     ipass++;
                     irow = 0;
                 }
@@ -192,7 +192,7 @@
             default:
                 break;
             }
-        } while (irow > (m_frameContext->height() - 1));
+        } while (irow > (unsigned) (m_frameContext->height() - 1));
     }
     return true;
 }
@@ -202,7 +202,7 @@
 // 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)
 {
-    const size_t width = m_frameContext->width();
+    const int width = m_frameContext->width();
 
     if (rowIter == rowBuffer.end())
         return true;
@@ -306,14 +306,14 @@
 }
 
 sk_sp<SkColorTable> SkGIFColorMap::buildTable(SkStreamBuffer* streamBuffer, SkColorType colorType,
-                                              size_t transparentPixel) const
+                                              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 > (unsigned) m_table->count()
+        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.
@@ -331,7 +331,7 @@
     SkASSERT(m_colors <= SK_MAX_COLORS);
     const uint8_t* srcColormap = rawData->bytes();
     SkPMColor colorStorage[SK_MAX_COLORS];
-    for (size_t i = 0; i < m_colors; i++) {
+    for (int i = 0; i < m_colors; i++) {
         if (i == transparentPixel) {
             colorStorage[i] = SK_ColorTRANSPARENT;
         } else {
@@ -339,21 +339,21 @@
         }
         srcColormap += SK_BYTES_PER_COLORMAP_ENTRY;
     }
-    for (size_t i = m_colors; i < SK_MAX_COLORS; i++) {
+    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, size_t index) {
-    if (index >= m_frames.size()) {
+sk_sp<SkColorTable> SkGifImageReader::getColorTable(SkColorType colorType, int index) {
+    if (index < 0 || static_cast<size_t>(index) >= m_frames.size()) {
         return nullptr;
     }
 
     const SkGIFFrameContext* frameContext = m_frames[index].get();
     const SkGIFColorMap& localColorMap = frameContext->localColorMap();
-    const size_t transPix = frameContext->transparentPixel();
+    const int transPix = frameContext->transparentPixel();
     if (localColorMap.isDefined()) {
         return localColorMap.buildTable(&m_streamBuffer, colorType, transPix);
     }
@@ -385,7 +385,8 @@
     }
 
     // Some bad GIFs have extra blocks beyond the last row, which we don't want to decode.
-    while (m_currentLzwBlock < m_lzwBlocks.size() && m_lzwContext->hasRemainingRows()) {
+    while (static_cast<size_t>(m_currentLzwBlock) < m_lzwBlocks.size()
+           && m_lzwContext->hasRemainingRows()) {
         const auto& block = m_lzwBlocks[m_currentLzwBlock];
         const size_t len = block.blockSize;
 
@@ -411,7 +412,7 @@
 // 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(size_t frameIndex, bool* frameComplete)
+bool SkGifImageReader::decode(int frameIndex, bool* frameComplete)
 {
     SkGIFFrameContext* currentFrame = m_frames[frameIndex].get();
 
@@ -490,10 +491,10 @@
             // 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.
-            m_screenWidth = GETINT16(currentComponent);
-            m_screenHeight = GETINT16(currentComponent + 2);
+            fScreenWidth = GETINT16(currentComponent);
+            fScreenHeight = GETINT16(currentComponent + 2);
 
-            const size_t globalColorMapColors = 2 << (currentComponent[4] & 0x07);
+            const int globalColorMapColors = 2 << (currentComponent[4] & 0x07);
 
             if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* global map */
                 m_globalColorMap.setNumColors(globalColorMapColors);
@@ -622,7 +623,7 @@
                 currentFrame->setDisposalMethod(SkCodecAnimation::Keep_DisposalMethod);
                 break;
             }
-            currentFrame->setDelayTime(GETINT16(currentComponent + 1) * 10);
+            currentFrame->setDuration(GETINT16(currentComponent + 1) * 10);
             GETN(1, SkGIFConsumeBlock);
             break;
         }
@@ -705,7 +706,7 @@
         }
 
         case SkGIFImageHeader: {
-            unsigned height, width, xOffset, yOffset;
+            int height, width, xOffset, yOffset;
             const unsigned char* currentComponent =
                 reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
 
@@ -729,8 +730,8 @@
             // set to zero, since usually the first frame completely fills
             // the image.
             if (currentFrameIsFirstFrame()) {
-                m_screenHeight = std::max(m_screenHeight, yOffset + height);
-                m_screenWidth = std::max(m_screenWidth, xOffset + width);
+                fScreenHeight = std::max(fScreenHeight, yOffset + height);
+                fScreenWidth = std::max(fScreenWidth, xOffset + width);
             }
 
             // NOTE: Chromium placed this block after setHeaderDefined, down
@@ -742,8 +743,8 @@
             // Work around more broken GIF files that have zero image width or
             // height.
             if (!height || !width) {
-                height = m_screenHeight;
-                width = m_screenWidth;
+                height = fScreenHeight;
+                width = fScreenWidth;
                 if (!height || !width) {
                     // This prevents attempting to continue reading this invalid stream.
                     GETN(0, SkGIFDone);
@@ -753,15 +754,19 @@
 
             const bool isLocalColormapDefined = SkToBool(currentComponent[8] & 0x80);
             // The three low-order bits of currentComponent[8] specify the bits per pixel.
-            const size_t numColors = 2 << (currentComponent[8] & 0x7);
+            const int numColors = 2 << (currentComponent[8] & 0x7);
             if (currentFrameIsFirstFrame()) {
-                if (hasTransparentPixel(0, isLocalColormapDefined, numColors)) {
+                const int transPix = m_frames.empty() ? SkGIFColorMap::kNotFound
+                                                      : m_frames[0]->transparentPixel();
+                if (this->hasTransparency(transPix,
+                        isLocalColormapDefined, numColors))
+                {
                     m_firstFrameHasAlpha = true;
                     m_firstFrameSupportsIndex8 = true;
                 } else {
                     const bool frameIsSubset = xOffset > 0 || yOffset > 0
-                            || xOffset + width < m_screenWidth
-                            || yOffset + height < m_screenHeight;
+                            || width < fScreenWidth
+                            || height < fScreenHeight;
                     m_firstFrameHasAlpha = frameIsSubset;
                     m_firstFrameSupportsIndex8 = !frameIsSubset;
                 }
@@ -779,7 +784,7 @@
             }
 
 
-            currentFrame->setRect(xOffset, yOffset, width, height);
+            currentFrame->setXYWH(xOffset, yOffset, width, height);
             currentFrame->setInterlaced(SkToBool(currentComponent[8] & 0x40));
 
             // Overlaying interlaced, transparent GIFs over
@@ -799,7 +804,7 @@
                 break;
             }
 
-            setRequiredFrame(currentFrame);
+            setAlphaAndRequiredFrame(currentFrame);
             GETN(1, SkGIFLZWStart);
             break;
         }
@@ -809,7 +814,7 @@
             auto* currentFrame = m_frames.back().get();
             auto& cmap = currentFrame->localColorMap();
             cmap.setTablePosition(m_streamBuffer.markPosition());
-            setRequiredFrame(currentFrame);
+            setAlphaAndRequiredFrame(currentFrame);
             GETN(1, SkGIFLZWStart);
             break;
         }
@@ -851,33 +856,26 @@
     return true;
 }
 
-bool SkGifImageReader::hasTransparentPixel(size_t i, bool isLocalColormapDefined,
-                                           size_t localColors) {
-    if (m_frames.size() <= i) {
-        // This should only happen when parsing the first frame.
-        SkASSERT(0 == i);
-
-        // We did not see a Graphics Control Extension, so no transparent
-        // pixel was specified. But if there is no color table, this frame is
-        // still transparent.
-        return !isLocalColormapDefined && m_globalColorMap.numColors() == 0;
+bool SkGifImageReader::hasTransparency(int transparentPixel, bool isLocalColormapDefined,
+                                       int localColors) const {
+    const int globalColors = m_globalColorMap.numColors();
+    if (!isLocalColormapDefined && globalColors == 0) {
+        // No color table for this frame, so it is completely transparent.
+        return true;
     }
 
-    const size_t transparentPixel = m_frames[i]->transparentPixel();
+    if (transparentPixel < 0) {
+        SkASSERT(SkGIFColorMap::kNotFound == transparentPixel);
+        return false;
+    }
+
     if (isLocalColormapDefined) {
         return transparentPixel < localColors;
     }
 
-    const size_t globalColors = m_globalColorMap.numColors();
-    if (!globalColors) {
-        // No color table for this frame, so the frame is empty.
-        // This is technically different from having a transparent
-        // pixel, but we'll treat it the same - nothing to draw here.
-        return true;
-    }
-
     // If there is a global color table, it will be parsed before reaching
     // here. If its numColors is set, it will be defined.
+    SkASSERT(globalColors > 0);
     SkASSERT(m_globalColorMap.isDefined());
     return transparentPixel < globalColors;
 }
@@ -886,60 +884,117 @@
 {
     if (m_frames.empty() || m_frames.back()->isComplete()) {
         const size_t i = m_frames.size();
-        std::unique_ptr<SkGIFFrameContext> frame(new SkGIFFrameContext(i));
+        std::unique_ptr<SkGIFFrameContext> frame(new SkGIFFrameContext(this, static_cast<int>(i)));
         m_frames.push_back(std::move(frame));
     }
 }
 
-void SkGifImageReader::setRequiredFrame(SkGIFFrameContext* frame) {
-    const size_t i = frame->frameId();
+static SkIRect frame_rect_on_screen(SkIRect frameRect,
+                                    const SkIRect& screenRect) {
+    if (!frameRect.intersect(screenRect)) {
+        return SkIRect::MakeEmpty();
+    }
+
+    return frameRect;
+}
+
+static bool independent(const SkFrame& frame) {
+    return frame.getRequiredFrame() == SkCodec::kNone;
+}
+
+static bool restore_bg(const SkFrame& frame) {
+    return frame.getDisposalMethod() == SkCodecAnimation::RestoreBGColor_DisposalMethod;
+}
+
+bool SkGIFFrameContext::onReportsAlpha() 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 m_owner->hasTransparency(this->transparentPixel(),
+            m_localColorMap.isDefined(), m_localColorMap.numColors());
+}
+
+void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) {
+    const bool reportsAlpha = frame->reportsAlpha();
+    const auto screenRect = SkIRect::MakeWH(fScreenWidth, fScreenHeight);
+    const auto frameRect = frame_rect_on_screen(frame->frameRect(), screenRect);
+
+    const int i = frame->frameId();
     if (0 == i) {
+        frame->setHasAlpha(reportsAlpha || frameRect != screenRect);
         frame->setRequiredFrame(SkCodec::kNone);
         return;
     }
 
-    const SkGIFFrameContext* prevFrame = m_frames[i - 1].get();
-    if (prevFrame->getDisposalMethod() == SkCodecAnimation::RestorePrevious_DisposalMethod) {
-        frame->setRequiredFrame(prevFrame->getRequiredFrame());
+
+    const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kPriorFrame;
+    if ((!reportsAlpha || !blendWithPrevFrame) && frameRect == screenRect) {
+        frame->setHasAlpha(reportsAlpha);
+        frame->setRequiredFrame(SkCodec::kNone);
         return;
     }
 
-    // Note: We could correct these after decoding - i.e. some frames may turn out to be
-    // independent if they do not use the transparent pixel, but that would require
-    // checking whether each pixel used the transparent pixel.
-    const SkGIFColorMap& localMap = frame->localColorMap();
-    const bool transValid = hasTransparentPixel(i, localMap.isDefined(), localMap.numColors());
+    const SkFrame* prevFrame = this->getFrame(i-1);
+    while (prevFrame->getDisposalMethod() == SkCodecAnimation::RestorePrevious_DisposalMethod) {
+        const int prevId = prevFrame->frameId();
+        if (0 == prevId) {
+            frame->setHasAlpha(true);
+            frame->setRequiredFrame(SkCodec::kNone);
+            return;
+        }
 
-    const SkIRect prevFrameRect = prevFrame->frameRect();
-    const bool frameCoversPriorFrame = frame->frameRect().contains(prevFrameRect);
+        prevFrame = this->getFrame(prevId - 1);
+    }
 
-    if (!transValid && frameCoversPriorFrame) {
-        frame->setRequiredFrame(prevFrame->getRequiredFrame());
+    const bool clearPrevFrame = restore_bg(*prevFrame);
+    auto prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
+
+    if (clearPrevFrame) {
+        if (prevFrameRect == screenRect || independent(*prevFrame)) {
+            frame->setHasAlpha(true);
+            frame->setRequiredFrame(SkCodec::kNone);
+            return;
+        }
+    }
+
+    if (reportsAlpha && blendWithPrevFrame) {
+        // Note: We could be more aggressive here. If prevFrame clears
+        // to background color and covers its required frame (and that
+        // frame is independent), prevFrame could be marked independent.
+        // Would this extra complexity be worth it?
+        frame->setRequiredFrame(prevFrame->frameId());
+        frame->setHasAlpha(prevFrame->hasAlpha() || clearPrevFrame);
         return;
     }
 
-    switch (prevFrame->getDisposalMethod()) {
-        case SkCodecAnimation::Keep_DisposalMethod:
-            frame->setRequiredFrame(i - 1);
-            break;
-        case SkCodecAnimation::RestorePrevious_DisposalMethod:
-            // This was already handled above.
-            SkASSERT(false);
-            break;
-        case SkCodecAnimation::RestoreBGColor_DisposalMethod:
-            // If the prior frame covers the whole image
-            if (prevFrameRect == SkIRect::MakeWH(m_screenWidth, m_screenHeight)
-                    // Or the prior frame was independent
-                    || prevFrame->getRequiredFrame() == SkCodec::kNone)
-            {
-                // This frame is independent, since we clear everything in the
-                // prior frame to the BG color
-                frame->setRequiredFrame(SkCodec::kNone);
-            } else {
-                frame->setRequiredFrame(i - 1);
-            }
-            break;
+    while (frameRect.contains(prevFrameRect)) {
+        const int prevRequiredFrame = prevFrame->getRequiredFrame();
+        if (prevRequiredFrame == SkCodec::kNone) {
+            frame->setRequiredFrame(SkCodec::kNone);
+            frame->setHasAlpha(true);
+            return;
+        }
+
+        prevFrame = this->getFrame(prevRequiredFrame);
+        prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect);
     }
+
+    if (restore_bg(*prevFrame)) {
+        frame->setHasAlpha(true);
+        if (prevFrameRect == screenRect || independent(*prevFrame)) {
+            frame->setRequiredFrame(SkCodec::kNone);
+        } else {
+            // Note: As above, frame could still be independent, e.g. if
+            // prevFrame covers its required frame and that frame is
+            // independent.
+            frame->setRequiredFrame(prevFrame->frameId());
+        }
+        return;
+    }
+
+    SkASSERT(prevFrame->getDisposalMethod() == SkCodecAnimation::Keep_DisposalMethod);
+    frame->setRequiredFrame(prevFrame->frameId());
+    frame->setHasAlpha(prevFrame->hasAlpha() || (reportsAlpha && !blendWithPrevFrame));
 }
 
 // FIXME: Move this method to close to doLZW().
diff --git a/third_party/gif/SkGifImageReader.h b/third_party/gif/SkGifImageReader.h
index 24c917a..f105a1d 100644
--- a/third_party/gif/SkGifImageReader.h
+++ b/third_party/gif/SkGifImageReader.h
@@ -47,6 +47,7 @@
 #include "SkCodecAnimation.h"
 #include "SkColorTable.h"
 #include "SkData.h"
+#include "SkFrameHolder.h"
 #include "SkImageInfo.h"
 #include "SkStreamBuffer.h"
 #include "../private/SkTArray.h"
@@ -85,7 +86,7 @@
     SkGIFConsumeComment
 };
 
-struct SkGIFFrameContext;
+class SkGIFFrameContext;
 class SkGIFColorMap;
 
 // LZW decoder state machine.
@@ -148,7 +149,7 @@
 
 class SkGIFColorMap final {
 public:
-    static constexpr size_t kNotFound = static_cast<size_t>(-1);
+    static constexpr int kNotFound = -1;
 
     SkGIFColorMap()
         : m_isDefined(false)
@@ -159,7 +160,7 @@
     {
     }
 
-    void setNumColors(size_t colors) {
+    void setNumColors(int colors) {
         SkASSERT(!m_colors);
         SkASSERT(!m_position);
 
@@ -173,36 +174,33 @@
         m_isDefined = true;
     }
 
-    size_t numColors() const { return m_colors; }
+    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,
-                                   size_t transparentPixel) const;
+                                   int transparentPixel) const;
 
 private:
     bool m_isDefined;
     size_t m_position;
-    size_t m_colors;
+    int m_colors;
     // Cached values. If these match on a new request, we can reuse m_table.
-    mutable size_t m_transPixel;
+    mutable int m_transPixel;
     mutable PackColorProc m_packColorProc;
     mutable sk_sp<SkColorTable> m_table;
 };
 
+class SkGifImageReader;
+
 // LocalFrame output state machine.
-struct SkGIFFrameContext : SkNoncopyable {
+class SkGIFFrameContext : public SkFrame {
 public:
-    SkGIFFrameContext(int id)
-        : m_frameId(id)
-        , m_xOffset(0)
-        , m_yOffset(0)
-        , m_width(0)
-        , m_height(0)
+    SkGIFFrameContext(SkGifImageReader* reader, int id)
+        : INHERITED(id)
+        , m_owner(reader)
         , m_transparentPixel(SkGIFColorMap::kNotFound)
-        , m_disposalMethod(SkCodecAnimation::Keep_DisposalMethod)
-        , m_requiredFrame(kUninitialized)
         , m_dataSize(0)
         , m_progressiveDisplay(false)
         , m_interlaced(false)
@@ -214,7 +212,7 @@
     {
     }
 
-    ~SkGIFFrameContext()
+    ~SkGIFFrameContext() override
     {
     }
 
@@ -225,29 +223,8 @@
 
     bool decode(SkStreamBuffer*, SkGifCodec* client, bool* frameDecoded);
 
-    int frameId() const { return m_frameId; }
-    void setRect(unsigned x, unsigned y, unsigned width, unsigned height)
-    {
-        m_xOffset = x;
-        m_yOffset = y;
-        m_width = width;
-        m_height = height;
-    }
-    SkIRect frameRect() const { return SkIRect::MakeXYWH(m_xOffset, m_yOffset, m_width, m_height); }
-    unsigned xOffset() const { return m_xOffset; }
-    unsigned yOffset() const { return m_yOffset; }
-    unsigned width() const { return m_width; }
-    unsigned height() const { return m_height; }
-    size_t transparentPixel() const { return m_transparentPixel; }
-    void setTransparentPixel(size_t pixel) { m_transparentPixel = pixel; }
-    SkCodecAnimation::DisposalMethod getDisposalMethod() const { return m_disposalMethod; }
-    void setDisposalMethod(SkCodecAnimation::DisposalMethod disposalMethod) { m_disposalMethod = disposalMethod; }
-
-    size_t getRequiredFrame() const {
-        SkASSERT(this->reachedStartOfData());
-        return m_requiredFrame;
-    }
-    void setRequiredFrame(size_t req) { m_requiredFrame = req; }
+    int transparentPixel() const { return m_transparentPixel; }
+    void setTransparentPixel(int pixel) { m_transparentPixel = pixel; }
 
     unsigned delayTime() const { return m_delayTime; }
     void setDelayTime(unsigned delay) { m_delayTime = delay; }
@@ -271,19 +248,14 @@
     const SkGIFColorMap& localColorMap() const { return m_localColorMap; }
     SkGIFColorMap& localColorMap() { return m_localColorMap; }
 
-    bool reachedStartOfData() const { return m_requiredFrame != kUninitialized; }
+protected:
+    bool onReportsAlpha() const override;
 
 private:
-    static constexpr size_t kUninitialized = static_cast<size_t>(-2);
+    // Unowned pointer to the object that owns this frame.
+    const SkGifImageReader* m_owner;
 
-    int m_frameId;
-    unsigned m_xOffset;
-    unsigned m_yOffset; // With respect to "screen" origin.
-    unsigned m_width;
-    unsigned m_height;
-    size_t m_transparentPixel; // Index of transparent pixel. Value is kNotFound if there is no transparent pixel.
-    SkCodecAnimation::DisposalMethod m_disposalMethod; // Restore to background, leave in place, etc.
-    size_t m_requiredFrame;
+    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.
@@ -297,13 +269,15 @@
 
     SkGIFColorMap m_localColorMap;
 
-    size_t m_currentLzwBlock;
+    int m_currentLzwBlock;
     bool m_isComplete;
     bool m_isHeaderDefined;
     bool m_isDataSizeDefined;
+
+    typedef SkFrame INHERITED;
 };
 
-class SkGifImageReader final : public SkNoncopyable {
+class SkGifImageReader final : public SkFrameHolder {
 public:
     // This takes ownership of stream.
     SkGifImageReader(SkStream* stream)
@@ -311,8 +285,6 @@
         , m_state(SkGIFType)
         , m_bytesToConsume(6) // Number of bytes for GIF type, either "GIF87a" or "GIF89a".
         , m_version(0)
-        , m_screenWidth(0)
-        , m_screenHeight(0)
         , m_loopCount(cLoopCountNotSeen)
         , m_streamBuffer(stream)
         , m_parseCompleted(false)
@@ -321,15 +293,12 @@
     {
     }
 
-    ~SkGifImageReader()
+    ~SkGifImageReader() override
     {
     }
 
     void setClient(SkGifCodec* client) { m_client = client; }
 
-    unsigned screenWidth() const { return m_screenWidth; }
-    unsigned screenHeight() const { return m_screenHeight; }
-
     // 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 {
@@ -351,15 +320,15 @@
     // 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(size_t frameIndex, bool* frameComplete);
+    bool decode(int frameIndex, bool* frameComplete);
 
-    size_t imagesCount() const
+    int imagesCount() const
     {
         // Report the first frame immediately, so the parser can stop when it
         // sees the size on a SizeQuery.
         const size_t frames = m_frames.size();
         if (frames <= 1) {
-            return frames;
+            return static_cast<int>(frames);
         }
 
         // This avoids counting an empty frame when the file is truncated (or
@@ -367,7 +336,7 @@
         // 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;
+        return static_cast<int>(m_frames.back()->reachedStartOfData() ? frames : frames - 1);
     }
     int loopCount() const {
         if (cLoopCountNotSeen == m_loopCount) {
@@ -381,9 +350,10 @@
         return m_globalColorMap;
     }
 
-    const SkGIFFrameContext* frameContext(size_t index) const
+    const SkGIFFrameContext* frameContext(int index) const
     {
-        return index < m_frames.size() ? m_frames[index].get() : 0;
+        return index >= 0 && index < static_cast<int>(m_frames.size())
+                ? m_frames[index].get() : 0;
     }
 
     void clearDecodeState() {
@@ -393,12 +363,22 @@
     }
 
     // Return the color table for frame index (which may be the global color table).
-    sk_sp<SkColorTable> getColorTable(SkColorType dstColorType, size_t index);
+    sk_sp<SkColorTable> getColorTable(SkColorType dstColorType, int index);
 
     bool firstFrameHasAlpha() const { return m_firstFrameHasAlpha; }
 
     bool firstFrameSupportsIndex8() const { return m_firstFrameSupportsIndex8; }
 
+    // Helper function that returns whether an SkGIFFrameContext has transparency.
+    // This method is sometimes called before creating one/parsing its color map,
+    // so it cannot rely on SkGIFFrameContext::transparentPixel or ::localColorMap().
+    bool hasTransparency(int transPix, bool hasLocalColorMap, int localMapColors) const;
+
+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 {
@@ -406,11 +386,6 @@
     }
 
     void addFrameIfNecessary();
-    // Must be called *after* the SkGIFFrameContext's color table (if any) has been parsed.
-    void setRequiredFrame(SkGIFFrameContext*);
-    // This method is sometimes called before creating a SkGIFFrameContext, so it cannot rely
-    // on SkGIFFrameContext::localColorMap().
-    bool hasTransparentPixel(size_t frameIndex, bool hasLocalColorMap, size_t localMapColors);
     bool currentFrameIsFirstFrame() const
     {
         return m_frames.empty() || (m_frames.size() == 1u && !m_frames[0]->isComplete());
@@ -425,8 +400,6 @@
 
     // Global (multi-image) state.
     int m_version; // Either 89 for GIF89 or 87 for GIF87.
-    unsigned m_screenWidth; // Logical screen width & height.
-    unsigned m_screenHeight;
     SkGIFColorMap m_globalColorMap;
 
     static constexpr int cLoopCountNotSeen = -2;
diff --git a/third_party/spirv-tools/BUILD.gn b/third_party/spirv-tools/BUILD.gn
index 00f2423..4f9304e 100644
--- a/third_party/spirv-tools/BUILD.gn
+++ b/third_party/spirv-tools/BUILD.gn
@@ -13,11 +13,14 @@
     ]
     args = [
       "--spirv-core-grammar=" + rebase_path(
-              "../externals/spirv-headers/include/spirv/$version/spirv.core.grammar.json"),
+              "../externals/spirv-headers/include/spirv/$version/spirv.core.grammar.json",
+              root_build_dir),
       "--core-insts-output=" +
-          rebase_path("$root_out_dir/spirv-tools/core.insts-$version.inc"),
+          rebase_path("$root_out_dir/spirv-tools/core.insts-$version.inc",
+                      root_build_dir),
       "--operand-kinds-output=" +
-          rebase_path("$root_out_dir/spirv-tools/operand.kinds-$version.inc"),
+          rebase_path("$root_out_dir/spirv-tools/operand.kinds-$version.inc",
+                      root_build_dir),
     ]
   }
 }
@@ -40,12 +43,15 @@
   ]
   args = [
     "--spirv-core-grammar=" + rebase_path(
-            "../externals/spirv-headers/include/spirv/1.0/spirv.core.grammar.json"),
+            "../externals/spirv-headers/include/spirv/1.0/spirv.core.grammar.json",
+            root_build_dir),
     "--extinst-glsl-grammar=" +
         rebase_path("../externals/spirv-headers/include/spirv/1.0/" +
-                    "extinst.glsl.std.450.grammar.json"),
+                        "extinst.glsl.std.450.grammar.json",
+                    root_build_dir),
     "--glsl-insts-output=" +
-        rebase_path("$root_out_dir/spirv-tools/glsl.std.450.insts-1.0.inc"),
+        rebase_path("$root_out_dir/spirv-tools/glsl.std.450.insts-1.0.inc",
+                    root_build_dir),
   ]
 }
 
@@ -60,12 +66,15 @@
   ]
   args = [
     "--spirv-core-grammar=" + rebase_path(
-            "../externals/spirv-headers/include/spirv/1.0/spirv.core.grammar.json"),
+            "../externals/spirv-headers/include/spirv/1.0/spirv.core.grammar.json",
+            root_build_dir),
     "--extinst-opencl-grammar=" +
         rebase_path("../externals/spirv-headers/include/spirv/1.0/" +
-                    "extinst.glsl.std.450.grammar.json"),
+                        "extinst.glsl.std.450.grammar.json",
+                    root_build_dir),
     "--opencl-insts-output=" +
-        rebase_path("$root_out_dir/spirv-tools/opencl.std.insts-1.0.inc"),
+        rebase_path("$root_out_dir/spirv-tools/opencl.std.insts-1.0.inc",
+                    root_build_dir),
   ]
 }
 
@@ -78,8 +87,8 @@
     "$root_out_dir/spirv-tools/build.inc",
   ]
   args = [
-    rebase_path("../externals/spirv-tools"),
-    rebase_path("$root_out_dir/spirv-tools/build-version.inc"),
+    rebase_path("../externals/spirv-tools", root_build_dir),
+    rebase_path("$root_out_dir/spirv-tools/build-version.inc", root_build_dir),
   ]
 }
 
@@ -93,9 +102,12 @@
   ]
   args = [
     "--xml=" +
-        rebase_path("../externals/spirv-headers/include/spirv/spir-v.xml"),
+        rebase_path("../externals/spirv-headers/include/spirv/spir-v.xml",
+                    root_build_dir),
     "--generator-output=" +
-        rebase_path("$root_out_dir/spirv-tools/generators.inc"),
+        rebase_path("$root_out_dir/spirv-tools/generators.inc",
+                    root_build_dir,
+                    root_build_dir),
   ]
 }
 
diff --git a/third_party/vulkan/vulkan/vk_platform.h b/third_party/vulkan/vulkan/vk_platform.h
new file mode 100644
index 0000000..9279e07
--- /dev/null
+++ b/third_party/vulkan/vulkan/vk_platform.h
@@ -0,0 +1,97 @@
+//
+// File: vk_platform.h
+//
+/*
+** Copyright (c) 2014-2017 The Khronos Group Inc.
+**
+** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+ * Below this point is based on the original Khronos vk_platform.h header, but simplified for use in
+ * Skia when we are not building with a Vulkan backend, but still need the type declarations for
+ * compiling.
+ */
+
+#ifndef VK_PLATFORM_H_
+#define VK_PLATFORM_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+
+/*
+***************************************************************************************************
+*   Platform-specific directives and type declarations
+***************************************************************************************************
+*/
+
+/* Platform-specific calling convention macros.
+ *
+ * Platforms should define these so that Vulkan clients call Vulkan commands
+ * with the same calling conventions that the Vulkan implementation expects.
+ *
+ * VKAPI_ATTR - Placed before the return type in function declarations.
+ *              Useful for C++11 and GCC/Clang-style function attribute syntax.
+ * VKAPI_CALL - Placed after the return type in function declarations.
+ *              Useful for MSVC-style calling convention syntax.
+ * VKAPI_PTR  - Placed between the '(' and '*' in function pointer types.
+ *
+ * Function declaration:  VKAPI_ATTR void VKAPI_CALL vkCommand(void);
+ * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void);
+ */
+#if defined(_WIN32)
+    // On Windows, Vulkan commands use the stdcall convention
+    #define VKAPI_ATTR
+    #define VKAPI_CALL __stdcall
+    #define VKAPI_PTR  VKAPI_CALL
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
+    #error "Vulkan isn't supported for the 'armeabi' NDK ABI"
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
+    // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
+    // calling convention, i.e. float parameters are passed in registers. This
+    // is true even if the rest of the application passes floats on the stack,
+    // as it does by default when compiling for the armeabi-v7a NDK ABI.
+    #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
+    #define VKAPI_CALL
+    #define VKAPI_PTR  VKAPI_ATTR
+#else
+    // On other platforms, use the default calling convention
+    #define VKAPI_ATTR
+    #define VKAPI_CALL
+    #define VKAPI_PTR
+#endif
+
+#include <stddef.h>
+
+#if !defined(VK_NO_STDINT_H)
+    #if defined(_MSC_VER) && (_MSC_VER < 1600)
+        typedef signed   __int8  int8_t;
+        typedef unsigned __int8  uint8_t;
+        typedef signed   __int16 int16_t;
+        typedef unsigned __int16 uint16_t;
+        typedef signed   __int32 int32_t;
+        typedef unsigned __int32 uint32_t;
+        typedef signed   __int64 int64_t;
+        typedef unsigned __int64 uint64_t;
+    #else
+        #include <stdint.h>
+    #endif
+#endif // !defined(VK_NO_STDINT_H)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif
diff --git a/third_party/vulkan/vulkan/vulkan.h b/third_party/vulkan/vulkan/vulkan.h
new file mode 100644
index 0000000..c981c6f
--- /dev/null
+++ b/third_party/vulkan/vulkan/vulkan.h
@@ -0,0 +1,5355 @@
+#ifndef VULKAN_H_
+#define VULKAN_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** Copyright (c) 2015-2017 The Khronos Group Inc.
+**
+** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+** This header is generated from the Khronos Vulkan XML API Registry.
+**
+*/
+
+/*
+ * Below this point is based on the original Khronos vulkan.h header, but simplified for use in Skia
+ * when we are not building with a Vulkan backend, but still need the type declarations for compiling.
+ */
+
+#define VK_VERSION_1_0 1
+#include "vk_platform.h"
+
+#define VK_MAKE_VERSION(major, minor, patch) \
+    (((major) << 22) | ((minor) << 12) | (patch))
+
+// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead.
+//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0)
+
+// Vulkan 1.0 version number
+#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)
+
+#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
+#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
+#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
+// Version of this file
+#define VK_HEADER_VERSION 46
+
+
+#define VK_NULL_HANDLE 0
+        
+
+
+#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
+
+
+#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+        #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
+#else
+        #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
+#endif
+#endif
+        
+
+
+typedef uint32_t VkFlags;
+typedef uint32_t VkBool32;
+typedef uint64_t VkDeviceSize;
+typedef uint32_t VkSampleMask;
+
+VK_DEFINE_HANDLE(VkInstance)
+VK_DEFINE_HANDLE(VkPhysicalDevice)
+VK_DEFINE_HANDLE(VkDevice)
+VK_DEFINE_HANDLE(VkQueue)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore)
+VK_DEFINE_HANDLE(VkCommandBuffer)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkQueryPool)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferView)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipeline)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool)
+
+#define VK_LOD_CLAMP_NONE                 1000.0f
+#define VK_REMAINING_MIP_LEVELS           (~0U)
+#define VK_REMAINING_ARRAY_LAYERS         (~0U)
+#define VK_WHOLE_SIZE                     (~0ULL)
+#define VK_ATTACHMENT_UNUSED              (~0U)
+#define VK_TRUE                           1
+#define VK_FALSE                          0
+#define VK_QUEUE_FAMILY_IGNORED           (~0U)
+#define VK_SUBPASS_EXTERNAL               (~0U)
+#define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE  256
+#define VK_UUID_SIZE                      16
+#define VK_MAX_MEMORY_TYPES               32
+#define VK_MAX_MEMORY_HEAPS               16
+#define VK_MAX_EXTENSION_NAME_SIZE        256
+#define VK_MAX_DESCRIPTION_SIZE           256
+
+
+typedef enum VkPipelineCacheHeaderVersion {
+    VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1,
+    VK_PIPELINE_CACHE_HEADER_VERSION_BEGIN_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE,
+    VK_PIPELINE_CACHE_HEADER_VERSION_END_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE,
+    VK_PIPELINE_CACHE_HEADER_VERSION_RANGE_SIZE = (VK_PIPELINE_CACHE_HEADER_VERSION_ONE - VK_PIPELINE_CACHE_HEADER_VERSION_ONE + 1),
+    VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF
+} VkPipelineCacheHeaderVersion;
+
+typedef enum VkResult {
+    VK_SUCCESS = 0,
+    VK_NOT_READY = 1,
+    VK_TIMEOUT = 2,
+    VK_EVENT_SET = 3,
+    VK_EVENT_RESET = 4,
+    VK_INCOMPLETE = 5,
+    VK_ERROR_OUT_OF_HOST_MEMORY = -1,
+    VK_ERROR_OUT_OF_DEVICE_MEMORY = -2,
+    VK_ERROR_INITIALIZATION_FAILED = -3,
+    VK_ERROR_DEVICE_LOST = -4,
+    VK_ERROR_MEMORY_MAP_FAILED = -5,
+    VK_ERROR_LAYER_NOT_PRESENT = -6,
+    VK_ERROR_EXTENSION_NOT_PRESENT = -7,
+    VK_ERROR_FEATURE_NOT_PRESENT = -8,
+    VK_ERROR_INCOMPATIBLE_DRIVER = -9,
+    VK_ERROR_TOO_MANY_OBJECTS = -10,
+    VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
+    VK_ERROR_FRAGMENTED_POOL = -12,
+    VK_ERROR_SURFACE_LOST_KHR = -1000000000,
+    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001,
+    VK_SUBOPTIMAL_KHR = 1000001003,
+    VK_ERROR_OUT_OF_DATE_KHR = -1000001004,
+    VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,
+    VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,
+    VK_ERROR_INVALID_SHADER_NV = -1000012000,
+    VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000,
+    VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX = -1000072003,
+    VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL,
+    VK_RESULT_END_RANGE = VK_INCOMPLETE,
+    VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1),
+    VK_RESULT_MAX_ENUM = 0x7FFFFFFF
+} VkResult;
+
+typedef enum VkStructureType {
+    VK_STRUCTURE_TYPE_APPLICATION_INFO = 0,
+    VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1,
+    VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2,
+    VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3,
+    VK_STRUCTURE_TYPE_SUBMIT_INFO = 4,
+    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO = 5,
+    VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE = 6,
+    VK_STRUCTURE_TYPE_BIND_SPARSE_INFO = 7,
+    VK_STRUCTURE_TYPE_FENCE_CREATE_INFO = 8,
+    VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO = 9,
+    VK_STRUCTURE_TYPE_EVENT_CREATE_INFO = 10,
+    VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO = 11,
+    VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO = 12,
+    VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO = 13,
+    VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO = 14,
+    VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO = 15,
+    VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO = 16,
+    VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO = 17,
+    VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO = 18,
+    VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19,
+    VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20,
+    VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22,
+    VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23,
+    VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24,
+    VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25,
+    VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26,
+    VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27,
+    VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO = 28,
+    VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO = 29,
+    VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO = 30,
+    VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO = 31,
+    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32,
+    VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO = 33,
+    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO = 34,
+    VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET = 35,
+    VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET = 36,
+    VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO = 37,
+    VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO = 38,
+    VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO = 39,
+    VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO = 40,
+    VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO = 41,
+    VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO = 42,
+    VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO = 43,
+    VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER = 44,
+    VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER = 45,
+    VK_STRUCTURE_TYPE_MEMORY_BARRIER = 46,
+    VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47,
+    VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000,
+    VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001,
+    VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR = 1000002000,
+    VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001,
+    VK_STRUCTURE_TYPE_DISPLAY_PRESENT_INFO_KHR = 1000003000,
+    VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000,
+    VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000,
+    VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
+    VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
+    VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000,
+    VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
+    VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000,
+    VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000,
+    VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000,
+    VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001,
+    VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
+    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX = 1000053000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX = 1000053001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX = 1000053002,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001,
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001,
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004,
+    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
+    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007,
+    VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009,
+    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012,
+    VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+    VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX = 1000070000,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX = 1000070001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX = 1000071000,
+    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX = 1000071001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX = 1000071002,
+    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX = 1000071003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX = 1000071004,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHX = 1000071005,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHX = 1000071006,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHX = 1000071007,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX = 1000072000,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX = 1000072001,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX = 1000072002,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073001,
+    VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX = 1000073002,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX = 1000074000,
+    VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX = 1000074001,
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX = 1000075000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX = 1000076000,
+    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX = 1000076001,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX = 1000077000,
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078000,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078001,
+    VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX = 1000078002,
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX = 1000079000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000,
+    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = 1000085000,
+    VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
+    VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
+    VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
+    VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000,
+    VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000,
+    VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001,
+    VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003,
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000,
+    VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001,
+    VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000,
+    VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000,
+    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
+    VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+    VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
+    VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
+    VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkStructureType;
+
+typedef enum VkSystemAllocationScope {
+    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0,
+    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1,
+    VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2,
+    VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3,
+    VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4,
+    VK_SYSTEM_ALLOCATION_SCOPE_BEGIN_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND,
+    VK_SYSTEM_ALLOCATION_SCOPE_END_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE,
+    VK_SYSTEM_ALLOCATION_SCOPE_RANGE_SIZE = (VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND + 1),
+    VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF
+} VkSystemAllocationScope;
+
+typedef enum VkInternalAllocationType {
+    VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0,
+    VK_INTERNAL_ALLOCATION_TYPE_BEGIN_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE,
+    VK_INTERNAL_ALLOCATION_TYPE_END_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE,
+    VK_INTERNAL_ALLOCATION_TYPE_RANGE_SIZE = (VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE + 1),
+    VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkInternalAllocationType;
+
+typedef enum VkFormat {
+    VK_FORMAT_UNDEFINED = 0,
+    VK_FORMAT_R4G4_UNORM_PACK8 = 1,
+    VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2,
+    VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3,
+    VK_FORMAT_R5G6B5_UNORM_PACK16 = 4,
+    VK_FORMAT_B5G6R5_UNORM_PACK16 = 5,
+    VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6,
+    VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7,
+    VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8,
+    VK_FORMAT_R8_UNORM = 9,
+    VK_FORMAT_R8_SNORM = 10,
+    VK_FORMAT_R8_USCALED = 11,
+    VK_FORMAT_R8_SSCALED = 12,
+    VK_FORMAT_R8_UINT = 13,
+    VK_FORMAT_R8_SINT = 14,
+    VK_FORMAT_R8_SRGB = 15,
+    VK_FORMAT_R8G8_UNORM = 16,
+    VK_FORMAT_R8G8_SNORM = 17,
+    VK_FORMAT_R8G8_USCALED = 18,
+    VK_FORMAT_R8G8_SSCALED = 19,
+    VK_FORMAT_R8G8_UINT = 20,
+    VK_FORMAT_R8G8_SINT = 21,
+    VK_FORMAT_R8G8_SRGB = 22,
+    VK_FORMAT_R8G8B8_UNORM = 23,
+    VK_FORMAT_R8G8B8_SNORM = 24,
+    VK_FORMAT_R8G8B8_USCALED = 25,
+    VK_FORMAT_R8G8B8_SSCALED = 26,
+    VK_FORMAT_R8G8B8_UINT = 27,
+    VK_FORMAT_R8G8B8_SINT = 28,
+    VK_FORMAT_R8G8B8_SRGB = 29,
+    VK_FORMAT_B8G8R8_UNORM = 30,
+    VK_FORMAT_B8G8R8_SNORM = 31,
+    VK_FORMAT_B8G8R8_USCALED = 32,
+    VK_FORMAT_B8G8R8_SSCALED = 33,
+    VK_FORMAT_B8G8R8_UINT = 34,
+    VK_FORMAT_B8G8R8_SINT = 35,
+    VK_FORMAT_B8G8R8_SRGB = 36,
+    VK_FORMAT_R8G8B8A8_UNORM = 37,
+    VK_FORMAT_R8G8B8A8_SNORM = 38,
+    VK_FORMAT_R8G8B8A8_USCALED = 39,
+    VK_FORMAT_R8G8B8A8_SSCALED = 40,
+    VK_FORMAT_R8G8B8A8_UINT = 41,
+    VK_FORMAT_R8G8B8A8_SINT = 42,
+    VK_FORMAT_R8G8B8A8_SRGB = 43,
+    VK_FORMAT_B8G8R8A8_UNORM = 44,
+    VK_FORMAT_B8G8R8A8_SNORM = 45,
+    VK_FORMAT_B8G8R8A8_USCALED = 46,
+    VK_FORMAT_B8G8R8A8_SSCALED = 47,
+    VK_FORMAT_B8G8R8A8_UINT = 48,
+    VK_FORMAT_B8G8R8A8_SINT = 49,
+    VK_FORMAT_B8G8R8A8_SRGB = 50,
+    VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51,
+    VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52,
+    VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53,
+    VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54,
+    VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55,
+    VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56,
+    VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57,
+    VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58,
+    VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59,
+    VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60,
+    VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61,
+    VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62,
+    VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63,
+    VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64,
+    VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65,
+    VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66,
+    VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67,
+    VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68,
+    VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69,
+    VK_FORMAT_R16_UNORM = 70,
+    VK_FORMAT_R16_SNORM = 71,
+    VK_FORMAT_R16_USCALED = 72,
+    VK_FORMAT_R16_SSCALED = 73,
+    VK_FORMAT_R16_UINT = 74,
+    VK_FORMAT_R16_SINT = 75,
+    VK_FORMAT_R16_SFLOAT = 76,
+    VK_FORMAT_R16G16_UNORM = 77,
+    VK_FORMAT_R16G16_SNORM = 78,
+    VK_FORMAT_R16G16_USCALED = 79,
+    VK_FORMAT_R16G16_SSCALED = 80,
+    VK_FORMAT_R16G16_UINT = 81,
+    VK_FORMAT_R16G16_SINT = 82,
+    VK_FORMAT_R16G16_SFLOAT = 83,
+    VK_FORMAT_R16G16B16_UNORM = 84,
+    VK_FORMAT_R16G16B16_SNORM = 85,
+    VK_FORMAT_R16G16B16_USCALED = 86,
+    VK_FORMAT_R16G16B16_SSCALED = 87,
+    VK_FORMAT_R16G16B16_UINT = 88,
+    VK_FORMAT_R16G16B16_SINT = 89,
+    VK_FORMAT_R16G16B16_SFLOAT = 90,
+    VK_FORMAT_R16G16B16A16_UNORM = 91,
+    VK_FORMAT_R16G16B16A16_SNORM = 92,
+    VK_FORMAT_R16G16B16A16_USCALED = 93,
+    VK_FORMAT_R16G16B16A16_SSCALED = 94,
+    VK_FORMAT_R16G16B16A16_UINT = 95,
+    VK_FORMAT_R16G16B16A16_SINT = 96,
+    VK_FORMAT_R16G16B16A16_SFLOAT = 97,
+    VK_FORMAT_R32_UINT = 98,
+    VK_FORMAT_R32_SINT = 99,
+    VK_FORMAT_R32_SFLOAT = 100,
+    VK_FORMAT_R32G32_UINT = 101,
+    VK_FORMAT_R32G32_SINT = 102,
+    VK_FORMAT_R32G32_SFLOAT = 103,
+    VK_FORMAT_R32G32B32_UINT = 104,
+    VK_FORMAT_R32G32B32_SINT = 105,
+    VK_FORMAT_R32G32B32_SFLOAT = 106,
+    VK_FORMAT_R32G32B32A32_UINT = 107,
+    VK_FORMAT_R32G32B32A32_SINT = 108,
+    VK_FORMAT_R32G32B32A32_SFLOAT = 109,
+    VK_FORMAT_R64_UINT = 110,
+    VK_FORMAT_R64_SINT = 111,
+    VK_FORMAT_R64_SFLOAT = 112,
+    VK_FORMAT_R64G64_UINT = 113,
+    VK_FORMAT_R64G64_SINT = 114,
+    VK_FORMAT_R64G64_SFLOAT = 115,
+    VK_FORMAT_R64G64B64_UINT = 116,
+    VK_FORMAT_R64G64B64_SINT = 117,
+    VK_FORMAT_R64G64B64_SFLOAT = 118,
+    VK_FORMAT_R64G64B64A64_UINT = 119,
+    VK_FORMAT_R64G64B64A64_SINT = 120,
+    VK_FORMAT_R64G64B64A64_SFLOAT = 121,
+    VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122,
+    VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123,
+    VK_FORMAT_D16_UNORM = 124,
+    VK_FORMAT_X8_D24_UNORM_PACK32 = 125,
+    VK_FORMAT_D32_SFLOAT = 126,
+    VK_FORMAT_S8_UINT = 127,
+    VK_FORMAT_D16_UNORM_S8_UINT = 128,
+    VK_FORMAT_D24_UNORM_S8_UINT = 129,
+    VK_FORMAT_D32_SFLOAT_S8_UINT = 130,
+    VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131,
+    VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132,
+    VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133,
+    VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134,
+    VK_FORMAT_BC2_UNORM_BLOCK = 135,
+    VK_FORMAT_BC2_SRGB_BLOCK = 136,
+    VK_FORMAT_BC3_UNORM_BLOCK = 137,
+    VK_FORMAT_BC3_SRGB_BLOCK = 138,
+    VK_FORMAT_BC4_UNORM_BLOCK = 139,
+    VK_FORMAT_BC4_SNORM_BLOCK = 140,
+    VK_FORMAT_BC5_UNORM_BLOCK = 141,
+    VK_FORMAT_BC5_SNORM_BLOCK = 142,
+    VK_FORMAT_BC6H_UFLOAT_BLOCK = 143,
+    VK_FORMAT_BC6H_SFLOAT_BLOCK = 144,
+    VK_FORMAT_BC7_UNORM_BLOCK = 145,
+    VK_FORMAT_BC7_SRGB_BLOCK = 146,
+    VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147,
+    VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148,
+    VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149,
+    VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150,
+    VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151,
+    VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152,
+    VK_FORMAT_EAC_R11_UNORM_BLOCK = 153,
+    VK_FORMAT_EAC_R11_SNORM_BLOCK = 154,
+    VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155,
+    VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156,
+    VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157,
+    VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158,
+    VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159,
+    VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160,
+    VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161,
+    VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162,
+    VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163,
+    VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164,
+    VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165,
+    VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166,
+    VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167,
+    VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168,
+    VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169,
+    VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170,
+    VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171,
+    VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172,
+    VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173,
+    VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174,
+    VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175,
+    VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176,
+    VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177,
+    VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178,
+    VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179,
+    VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180,
+    VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181,
+    VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
+    VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
+    VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
+    VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED,
+    VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
+    VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1),
+    VK_FORMAT_MAX_ENUM = 0x7FFFFFFF
+} VkFormat;
+
+typedef enum VkImageType {
+    VK_IMAGE_TYPE_1D = 0,
+    VK_IMAGE_TYPE_2D = 1,
+    VK_IMAGE_TYPE_3D = 2,
+    VK_IMAGE_TYPE_BEGIN_RANGE = VK_IMAGE_TYPE_1D,
+    VK_IMAGE_TYPE_END_RANGE = VK_IMAGE_TYPE_3D,
+    VK_IMAGE_TYPE_RANGE_SIZE = (VK_IMAGE_TYPE_3D - VK_IMAGE_TYPE_1D + 1),
+    VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkImageType;
+
+typedef enum VkImageTiling {
+    VK_IMAGE_TILING_OPTIMAL = 0,
+    VK_IMAGE_TILING_LINEAR = 1,
+    VK_IMAGE_TILING_BEGIN_RANGE = VK_IMAGE_TILING_OPTIMAL,
+    VK_IMAGE_TILING_END_RANGE = VK_IMAGE_TILING_LINEAR,
+    VK_IMAGE_TILING_RANGE_SIZE = (VK_IMAGE_TILING_LINEAR - VK_IMAGE_TILING_OPTIMAL + 1),
+    VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF
+} VkImageTiling;
+
+typedef enum VkPhysicalDeviceType {
+    VK_PHYSICAL_DEVICE_TYPE_OTHER = 0,
+    VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1,
+    VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2,
+    VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3,
+    VK_PHYSICAL_DEVICE_TYPE_CPU = 4,
+    VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE = VK_PHYSICAL_DEVICE_TYPE_OTHER,
+    VK_PHYSICAL_DEVICE_TYPE_END_RANGE = VK_PHYSICAL_DEVICE_TYPE_CPU,
+    VK_PHYSICAL_DEVICE_TYPE_RANGE_SIZE = (VK_PHYSICAL_DEVICE_TYPE_CPU - VK_PHYSICAL_DEVICE_TYPE_OTHER + 1),
+    VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkPhysicalDeviceType;
+
+typedef enum VkQueryType {
+    VK_QUERY_TYPE_OCCLUSION = 0,
+    VK_QUERY_TYPE_PIPELINE_STATISTICS = 1,
+    VK_QUERY_TYPE_TIMESTAMP = 2,
+    VK_QUERY_TYPE_BEGIN_RANGE = VK_QUERY_TYPE_OCCLUSION,
+    VK_QUERY_TYPE_END_RANGE = VK_QUERY_TYPE_TIMESTAMP,
+    VK_QUERY_TYPE_RANGE_SIZE = (VK_QUERY_TYPE_TIMESTAMP - VK_QUERY_TYPE_OCCLUSION + 1),
+    VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkQueryType;
+
+typedef enum VkSharingMode {
+    VK_SHARING_MODE_EXCLUSIVE = 0,
+    VK_SHARING_MODE_CONCURRENT = 1,
+    VK_SHARING_MODE_BEGIN_RANGE = VK_SHARING_MODE_EXCLUSIVE,
+    VK_SHARING_MODE_END_RANGE = VK_SHARING_MODE_CONCURRENT,
+    VK_SHARING_MODE_RANGE_SIZE = (VK_SHARING_MODE_CONCURRENT - VK_SHARING_MODE_EXCLUSIVE + 1),
+    VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF
+} VkSharingMode;
+
+typedef enum VkImageLayout {
+    VK_IMAGE_LAYOUT_UNDEFINED = 0,
+    VK_IMAGE_LAYOUT_GENERAL = 1,
+    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2,
+    VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3,
+    VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4,
+    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5,
+    VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6,
+    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7,
+    VK_IMAGE_LAYOUT_PREINITIALIZED = 8,
+    VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002,
+    VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED,
+    VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED,
+    VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1),
+    VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF
+} VkImageLayout;
+
+typedef enum VkImageViewType {
+    VK_IMAGE_VIEW_TYPE_1D = 0,
+    VK_IMAGE_VIEW_TYPE_2D = 1,
+    VK_IMAGE_VIEW_TYPE_3D = 2,
+    VK_IMAGE_VIEW_TYPE_CUBE = 3,
+    VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4,
+    VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5,
+    VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6,
+    VK_IMAGE_VIEW_TYPE_BEGIN_RANGE = VK_IMAGE_VIEW_TYPE_1D,
+    VK_IMAGE_VIEW_TYPE_END_RANGE = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,
+    VK_IMAGE_VIEW_TYPE_RANGE_SIZE = (VK_IMAGE_VIEW_TYPE_CUBE_ARRAY - VK_IMAGE_VIEW_TYPE_1D + 1),
+    VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkImageViewType;
+
+typedef enum VkComponentSwizzle {
+    VK_COMPONENT_SWIZZLE_IDENTITY = 0,
+    VK_COMPONENT_SWIZZLE_ZERO = 1,
+    VK_COMPONENT_SWIZZLE_ONE = 2,
+    VK_COMPONENT_SWIZZLE_R = 3,
+    VK_COMPONENT_SWIZZLE_G = 4,
+    VK_COMPONENT_SWIZZLE_B = 5,
+    VK_COMPONENT_SWIZZLE_A = 6,
+    VK_COMPONENT_SWIZZLE_BEGIN_RANGE = VK_COMPONENT_SWIZZLE_IDENTITY,
+    VK_COMPONENT_SWIZZLE_END_RANGE = VK_COMPONENT_SWIZZLE_A,
+    VK_COMPONENT_SWIZZLE_RANGE_SIZE = (VK_COMPONENT_SWIZZLE_A - VK_COMPONENT_SWIZZLE_IDENTITY + 1),
+    VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF
+} VkComponentSwizzle;
+
+typedef enum VkVertexInputRate {
+    VK_VERTEX_INPUT_RATE_VERTEX = 0,
+    VK_VERTEX_INPUT_RATE_INSTANCE = 1,
+    VK_VERTEX_INPUT_RATE_BEGIN_RANGE = VK_VERTEX_INPUT_RATE_VERTEX,
+    VK_VERTEX_INPUT_RATE_END_RANGE = VK_VERTEX_INPUT_RATE_INSTANCE,
+    VK_VERTEX_INPUT_RATE_RANGE_SIZE = (VK_VERTEX_INPUT_RATE_INSTANCE - VK_VERTEX_INPUT_RATE_VERTEX + 1),
+    VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF
+} VkVertexInputRate;
+
+typedef enum VkPrimitiveTopology {
+    VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0,
+    VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1,
+    VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2,
+    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3,
+    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4,
+    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5,
+    VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6,
+    VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7,
+    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8,
+    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9,
+    VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10,
+    VK_PRIMITIVE_TOPOLOGY_BEGIN_RANGE = VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
+    VK_PRIMITIVE_TOPOLOGY_END_RANGE = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST,
+    VK_PRIMITIVE_TOPOLOGY_RANGE_SIZE = (VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - VK_PRIMITIVE_TOPOLOGY_POINT_LIST + 1),
+    VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF
+} VkPrimitiveTopology;
+
+typedef enum VkPolygonMode {
+    VK_POLYGON_MODE_FILL = 0,
+    VK_POLYGON_MODE_LINE = 1,
+    VK_POLYGON_MODE_POINT = 2,
+    VK_POLYGON_MODE_BEGIN_RANGE = VK_POLYGON_MODE_FILL,
+    VK_POLYGON_MODE_END_RANGE = VK_POLYGON_MODE_POINT,
+    VK_POLYGON_MODE_RANGE_SIZE = (VK_POLYGON_MODE_POINT - VK_POLYGON_MODE_FILL + 1),
+    VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF
+} VkPolygonMode;
+
+typedef enum VkFrontFace {
+    VK_FRONT_FACE_COUNTER_CLOCKWISE = 0,
+    VK_FRONT_FACE_CLOCKWISE = 1,
+    VK_FRONT_FACE_BEGIN_RANGE = VK_FRONT_FACE_COUNTER_CLOCKWISE,
+    VK_FRONT_FACE_END_RANGE = VK_FRONT_FACE_CLOCKWISE,
+    VK_FRONT_FACE_RANGE_SIZE = (VK_FRONT_FACE_CLOCKWISE - VK_FRONT_FACE_COUNTER_CLOCKWISE + 1),
+    VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF
+} VkFrontFace;
+
+typedef enum VkCompareOp {
+    VK_COMPARE_OP_NEVER = 0,
+    VK_COMPARE_OP_LESS = 1,
+    VK_COMPARE_OP_EQUAL = 2,
+    VK_COMPARE_OP_LESS_OR_EQUAL = 3,
+    VK_COMPARE_OP_GREATER = 4,
+    VK_COMPARE_OP_NOT_EQUAL = 5,
+    VK_COMPARE_OP_GREATER_OR_EQUAL = 6,
+    VK_COMPARE_OP_ALWAYS = 7,
+    VK_COMPARE_OP_BEGIN_RANGE = VK_COMPARE_OP_NEVER,
+    VK_COMPARE_OP_END_RANGE = VK_COMPARE_OP_ALWAYS,
+    VK_COMPARE_OP_RANGE_SIZE = (VK_COMPARE_OP_ALWAYS - VK_COMPARE_OP_NEVER + 1),
+    VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF
+} VkCompareOp;
+
+typedef enum VkStencilOp {
+    VK_STENCIL_OP_KEEP = 0,
+    VK_STENCIL_OP_ZERO = 1,
+    VK_STENCIL_OP_REPLACE = 2,
+    VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3,
+    VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4,
+    VK_STENCIL_OP_INVERT = 5,
+    VK_STENCIL_OP_INCREMENT_AND_WRAP = 6,
+    VK_STENCIL_OP_DECREMENT_AND_WRAP = 7,
+    VK_STENCIL_OP_BEGIN_RANGE = VK_STENCIL_OP_KEEP,
+    VK_STENCIL_OP_END_RANGE = VK_STENCIL_OP_DECREMENT_AND_WRAP,
+    VK_STENCIL_OP_RANGE_SIZE = (VK_STENCIL_OP_DECREMENT_AND_WRAP - VK_STENCIL_OP_KEEP + 1),
+    VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF
+} VkStencilOp;
+
+typedef enum VkLogicOp {
+    VK_LOGIC_OP_CLEAR = 0,
+    VK_LOGIC_OP_AND = 1,
+    VK_LOGIC_OP_AND_REVERSE = 2,
+    VK_LOGIC_OP_COPY = 3,
+    VK_LOGIC_OP_AND_INVERTED = 4,
+    VK_LOGIC_OP_NO_OP = 5,
+    VK_LOGIC_OP_XOR = 6,
+    VK_LOGIC_OP_OR = 7,
+    VK_LOGIC_OP_NOR = 8,
+    VK_LOGIC_OP_EQUIVALENT = 9,
+    VK_LOGIC_OP_INVERT = 10,
+    VK_LOGIC_OP_OR_REVERSE = 11,
+    VK_LOGIC_OP_COPY_INVERTED = 12,
+    VK_LOGIC_OP_OR_INVERTED = 13,
+    VK_LOGIC_OP_NAND = 14,
+    VK_LOGIC_OP_SET = 15,
+    VK_LOGIC_OP_BEGIN_RANGE = VK_LOGIC_OP_CLEAR,
+    VK_LOGIC_OP_END_RANGE = VK_LOGIC_OP_SET,
+    VK_LOGIC_OP_RANGE_SIZE = (VK_LOGIC_OP_SET - VK_LOGIC_OP_CLEAR + 1),
+    VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF
+} VkLogicOp;
+
+typedef enum VkBlendFactor {
+    VK_BLEND_FACTOR_ZERO = 0,
+    VK_BLEND_FACTOR_ONE = 1,
+    VK_BLEND_FACTOR_SRC_COLOR = 2,
+    VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 3,
+    VK_BLEND_FACTOR_DST_COLOR = 4,
+    VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 5,
+    VK_BLEND_FACTOR_SRC_ALPHA = 6,
+    VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 7,
+    VK_BLEND_FACTOR_DST_ALPHA = 8,
+    VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 9,
+    VK_BLEND_FACTOR_CONSTANT_COLOR = 10,
+    VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 11,
+    VK_BLEND_FACTOR_CONSTANT_ALPHA = 12,
+    VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 13,
+    VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 14,
+    VK_BLEND_FACTOR_SRC1_COLOR = 15,
+    VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16,
+    VK_BLEND_FACTOR_SRC1_ALPHA = 17,
+    VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18,
+    VK_BLEND_FACTOR_BEGIN_RANGE = VK_BLEND_FACTOR_ZERO,
+    VK_BLEND_FACTOR_END_RANGE = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,
+    VK_BLEND_FACTOR_RANGE_SIZE = (VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA - VK_BLEND_FACTOR_ZERO + 1),
+    VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF
+} VkBlendFactor;
+
+typedef enum VkBlendOp {
+    VK_BLEND_OP_ADD = 0,
+    VK_BLEND_OP_SUBTRACT = 1,
+    VK_BLEND_OP_REVERSE_SUBTRACT = 2,
+    VK_BLEND_OP_MIN = 3,
+    VK_BLEND_OP_MAX = 4,
+    VK_BLEND_OP_BEGIN_RANGE = VK_BLEND_OP_ADD,
+    VK_BLEND_OP_END_RANGE = VK_BLEND_OP_MAX,
+    VK_BLEND_OP_RANGE_SIZE = (VK_BLEND_OP_MAX - VK_BLEND_OP_ADD + 1),
+    VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF
+} VkBlendOp;
+
+typedef enum VkDynamicState {
+    VK_DYNAMIC_STATE_VIEWPORT = 0,
+    VK_DYNAMIC_STATE_SCISSOR = 1,
+    VK_DYNAMIC_STATE_LINE_WIDTH = 2,
+    VK_DYNAMIC_STATE_DEPTH_BIAS = 3,
+    VK_DYNAMIC_STATE_BLEND_CONSTANTS = 4,
+    VK_DYNAMIC_STATE_DEPTH_BOUNDS = 5,
+    VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6,
+    VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7,
+    VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8,
+    VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000,
+    VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000,
+    VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT,
+    VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE,
+    VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1),
+    VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF
+} VkDynamicState;
+
+typedef enum VkFilter {
+    VK_FILTER_NEAREST = 0,
+    VK_FILTER_LINEAR = 1,
+    VK_FILTER_CUBIC_IMG = 1000015000,
+    VK_FILTER_BEGIN_RANGE = VK_FILTER_NEAREST,
+    VK_FILTER_END_RANGE = VK_FILTER_LINEAR,
+    VK_FILTER_RANGE_SIZE = (VK_FILTER_LINEAR - VK_FILTER_NEAREST + 1),
+    VK_FILTER_MAX_ENUM = 0x7FFFFFFF
+} VkFilter;
+
+typedef enum VkSamplerMipmapMode {
+    VK_SAMPLER_MIPMAP_MODE_NEAREST = 0,
+    VK_SAMPLER_MIPMAP_MODE_LINEAR = 1,
+    VK_SAMPLER_MIPMAP_MODE_BEGIN_RANGE = VK_SAMPLER_MIPMAP_MODE_NEAREST,
+    VK_SAMPLER_MIPMAP_MODE_END_RANGE = VK_SAMPLER_MIPMAP_MODE_LINEAR,
+    VK_SAMPLER_MIPMAP_MODE_RANGE_SIZE = (VK_SAMPLER_MIPMAP_MODE_LINEAR - VK_SAMPLER_MIPMAP_MODE_NEAREST + 1),
+    VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF
+} VkSamplerMipmapMode;
+
+typedef enum VkSamplerAddressMode {
+    VK_SAMPLER_ADDRESS_MODE_REPEAT = 0,
+    VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1,
+    VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2,
+    VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3,
+    VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4,
+    VK_SAMPLER_ADDRESS_MODE_BEGIN_RANGE = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+    VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+    VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1),
+    VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF
+} VkSamplerAddressMode;
+
+typedef enum VkBorderColor {
+    VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0,
+    VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1,
+    VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2,
+    VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3,
+    VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4,
+    VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5,
+    VK_BORDER_COLOR_BEGIN_RANGE = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
+    VK_BORDER_COLOR_END_RANGE = VK_BORDER_COLOR_INT_OPAQUE_WHITE,
+    VK_BORDER_COLOR_RANGE_SIZE = (VK_BORDER_COLOR_INT_OPAQUE_WHITE - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK + 1),
+    VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF
+} VkBorderColor;
+
+typedef enum VkDescriptorType {
+    VK_DESCRIPTOR_TYPE_SAMPLER = 0,
+    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1,
+    VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2,
+    VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3,
+    VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4,
+    VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5,
+    VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6,
+    VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7,
+    VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8,
+    VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9,
+    VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10,
+    VK_DESCRIPTOR_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_TYPE_SAMPLER,
+    VK_DESCRIPTOR_TYPE_END_RANGE = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
+    VK_DESCRIPTOR_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1),
+    VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkDescriptorType;
+
+typedef enum VkAttachmentLoadOp {
+    VK_ATTACHMENT_LOAD_OP_LOAD = 0,
+    VK_ATTACHMENT_LOAD_OP_CLEAR = 1,
+    VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2,
+    VK_ATTACHMENT_LOAD_OP_BEGIN_RANGE = VK_ATTACHMENT_LOAD_OP_LOAD,
+    VK_ATTACHMENT_LOAD_OP_END_RANGE = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+    VK_ATTACHMENT_LOAD_OP_RANGE_SIZE = (VK_ATTACHMENT_LOAD_OP_DONT_CARE - VK_ATTACHMENT_LOAD_OP_LOAD + 1),
+    VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF
+} VkAttachmentLoadOp;
+
+typedef enum VkAttachmentStoreOp {
+    VK_ATTACHMENT_STORE_OP_STORE = 0,
+    VK_ATTACHMENT_STORE_OP_DONT_CARE = 1,
+    VK_ATTACHMENT_STORE_OP_BEGIN_RANGE = VK_ATTACHMENT_STORE_OP_STORE,
+    VK_ATTACHMENT_STORE_OP_END_RANGE = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+    VK_ATTACHMENT_STORE_OP_RANGE_SIZE = (VK_ATTACHMENT_STORE_OP_DONT_CARE - VK_ATTACHMENT_STORE_OP_STORE + 1),
+    VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF
+} VkAttachmentStoreOp;
+
+typedef enum VkPipelineBindPoint {
+    VK_PIPELINE_BIND_POINT_GRAPHICS = 0,
+    VK_PIPELINE_BIND_POINT_COMPUTE = 1,
+    VK_PIPELINE_BIND_POINT_BEGIN_RANGE = VK_PIPELINE_BIND_POINT_GRAPHICS,
+    VK_PIPELINE_BIND_POINT_END_RANGE = VK_PIPELINE_BIND_POINT_COMPUTE,
+    VK_PIPELINE_BIND_POINT_RANGE_SIZE = (VK_PIPELINE_BIND_POINT_COMPUTE - VK_PIPELINE_BIND_POINT_GRAPHICS + 1),
+    VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF
+} VkPipelineBindPoint;
+
+typedef enum VkCommandBufferLevel {
+    VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0,
+    VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1,
+    VK_COMMAND_BUFFER_LEVEL_BEGIN_RANGE = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+    VK_COMMAND_BUFFER_LEVEL_END_RANGE = VK_COMMAND_BUFFER_LEVEL_SECONDARY,
+    VK_COMMAND_BUFFER_LEVEL_RANGE_SIZE = (VK_COMMAND_BUFFER_LEVEL_SECONDARY - VK_COMMAND_BUFFER_LEVEL_PRIMARY + 1),
+    VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF
+} VkCommandBufferLevel;
+
+typedef enum VkIndexType {
+    VK_INDEX_TYPE_UINT16 = 0,
+    VK_INDEX_TYPE_UINT32 = 1,
+    VK_INDEX_TYPE_BEGIN_RANGE = VK_INDEX_TYPE_UINT16,
+    VK_INDEX_TYPE_END_RANGE = VK_INDEX_TYPE_UINT32,
+    VK_INDEX_TYPE_RANGE_SIZE = (VK_INDEX_TYPE_UINT32 - VK_INDEX_TYPE_UINT16 + 1),
+    VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkIndexType;
+
+typedef enum VkSubpassContents {
+    VK_SUBPASS_CONTENTS_INLINE = 0,
+    VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1,
+    VK_SUBPASS_CONTENTS_BEGIN_RANGE = VK_SUBPASS_CONTENTS_INLINE,
+    VK_SUBPASS_CONTENTS_END_RANGE = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS,
+    VK_SUBPASS_CONTENTS_RANGE_SIZE = (VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS - VK_SUBPASS_CONTENTS_INLINE + 1),
+    VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF
+} VkSubpassContents;
+
+typedef VkFlags VkInstanceCreateFlags;
+
+typedef enum VkFormatFeatureFlagBits {
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 0x00000001,
+    VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT = 0x00000002,
+    VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT = 0x00000004,
+    VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000008,
+    VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT = 0x00000010,
+    VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 0x00000020,
+    VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT = 0x00000040,
+    VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT = 0x00000080,
+    VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT = 0x00000100,
+    VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000200,
+    VK_FORMAT_FEATURE_BLIT_SRC_BIT = 0x00000400,
+    VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000,
+    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000,
+    VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000,
+    VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkFormatFeatureFlagBits;
+typedef VkFlags VkFormatFeatureFlags;
+
+typedef enum VkImageUsageFlagBits {
+    VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001,
+    VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002,
+    VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004,
+    VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008,
+    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010,
+    VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020,
+    VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040,
+    VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080,
+    VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkImageUsageFlagBits;
+typedef VkFlags VkImageUsageFlags;
+
+typedef enum VkImageCreateFlagBits {
+    VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001,
+    VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002,
+    VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004,
+    VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008,
+    VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010,
+    VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040,
+    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020,
+    VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkImageCreateFlagBits;
+typedef VkFlags VkImageCreateFlags;
+
+typedef enum VkSampleCountFlagBits {
+    VK_SAMPLE_COUNT_1_BIT = 0x00000001,
+    VK_SAMPLE_COUNT_2_BIT = 0x00000002,
+    VK_SAMPLE_COUNT_4_BIT = 0x00000004,
+    VK_SAMPLE_COUNT_8_BIT = 0x00000008,
+    VK_SAMPLE_COUNT_16_BIT = 0x00000010,
+    VK_SAMPLE_COUNT_32_BIT = 0x00000020,
+    VK_SAMPLE_COUNT_64_BIT = 0x00000040,
+    VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSampleCountFlagBits;
+typedef VkFlags VkSampleCountFlags;
+
+typedef enum VkQueueFlagBits {
+    VK_QUEUE_GRAPHICS_BIT = 0x00000001,
+    VK_QUEUE_COMPUTE_BIT = 0x00000002,
+    VK_QUEUE_TRANSFER_BIT = 0x00000004,
+    VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
+    VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkQueueFlagBits;
+typedef VkFlags VkQueueFlags;
+
+typedef enum VkMemoryPropertyFlagBits {
+    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001,
+    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002,
+    VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004,
+    VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008,
+    VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010,
+    VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkMemoryPropertyFlagBits;
+typedef VkFlags VkMemoryPropertyFlags;
+
+typedef enum VkMemoryHeapFlagBits {
+    VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001,
+    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX = 0x00000002,
+    VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkMemoryHeapFlagBits;
+typedef VkFlags VkMemoryHeapFlags;
+typedef VkFlags VkDeviceCreateFlags;
+typedef VkFlags VkDeviceQueueCreateFlags;
+
+typedef enum VkPipelineStageFlagBits {
+    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001,
+    VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002,
+    VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004,
+    VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008,
+    VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010,
+    VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020,
+    VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040,
+    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080,
+    VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100,
+    VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200,
+    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400,
+    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800,
+    VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000,
+    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 0x00002000,
+    VK_PIPELINE_STAGE_HOST_BIT = 0x00004000,
+    VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000,
+    VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000,
+    VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000,
+    VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkPipelineStageFlagBits;
+typedef VkFlags VkPipelineStageFlags;
+typedef VkFlags VkMemoryMapFlags;
+
+typedef enum VkImageAspectFlagBits {
+    VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001,
+    VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002,
+    VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004,
+    VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008,
+    VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkImageAspectFlagBits;
+typedef VkFlags VkImageAspectFlags;
+
+typedef enum VkSparseImageFormatFlagBits {
+    VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001,
+    VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 0x00000002,
+    VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 0x00000004,
+    VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSparseImageFormatFlagBits;
+typedef VkFlags VkSparseImageFormatFlags;
+
+typedef enum VkSparseMemoryBindFlagBits {
+    VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001,
+    VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSparseMemoryBindFlagBits;
+typedef VkFlags VkSparseMemoryBindFlags;
+
+typedef enum VkFenceCreateFlagBits {
+    VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001,
+    VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkFenceCreateFlagBits;
+typedef VkFlags VkFenceCreateFlags;
+typedef VkFlags VkSemaphoreCreateFlags;
+typedef VkFlags VkEventCreateFlags;
+typedef VkFlags VkQueryPoolCreateFlags;
+
+typedef enum VkQueryPipelineStatisticFlagBits {
+    VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 0x00000001,
+    VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT = 0x00000002,
+    VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT = 0x00000004,
+    VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT = 0x00000008,
+    VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT = 0x00000010,
+    VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT = 0x00000020,
+    VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT = 0x00000040,
+    VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 0x00000080,
+    VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 0x00000100,
+    VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 0x00000200,
+    VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 0x00000400,
+    VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkQueryPipelineStatisticFlagBits;
+typedef VkFlags VkQueryPipelineStatisticFlags;
+
+typedef enum VkQueryResultFlagBits {
+    VK_QUERY_RESULT_64_BIT = 0x00000001,
+    VK_QUERY_RESULT_WAIT_BIT = 0x00000002,
+    VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004,
+    VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008,
+    VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkQueryResultFlagBits;
+typedef VkFlags VkQueryResultFlags;
+
+typedef enum VkBufferCreateFlagBits {
+    VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001,
+    VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002,
+    VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004,
+    VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkBufferCreateFlagBits;
+typedef VkFlags VkBufferCreateFlags;
+
+typedef enum VkBufferUsageFlagBits {
+    VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 0x00000001,
+    VK_BUFFER_USAGE_TRANSFER_DST_BIT = 0x00000002,
+    VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000004,
+    VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 0x00000008,
+    VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 0x00000010,
+    VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 0x00000020,
+    VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040,
+    VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080,
+    VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100,
+    VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkBufferUsageFlagBits;
+typedef VkFlags VkBufferUsageFlags;
+typedef VkFlags VkBufferViewCreateFlags;
+typedef VkFlags VkImageViewCreateFlags;
+typedef VkFlags VkShaderModuleCreateFlags;
+typedef VkFlags VkPipelineCacheCreateFlags;
+
+typedef enum VkPipelineCreateFlagBits {
+    VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001,
+    VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002,
+    VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004,
+    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX = 0x00000008,
+    VK_PIPELINE_CREATE_DISPATCH_BASE_KHX = 0x00000010,
+    VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkPipelineCreateFlagBits;
+typedef VkFlags VkPipelineCreateFlags;
+typedef VkFlags VkPipelineShaderStageCreateFlags;
+
+typedef enum VkShaderStageFlagBits {
+    VK_SHADER_STAGE_VERTEX_BIT = 0x00000001,
+    VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002,
+    VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004,
+    VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008,
+    VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010,
+    VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020,
+    VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F,
+    VK_SHADER_STAGE_ALL = 0x7FFFFFFF,
+    VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkShaderStageFlagBits;
+typedef VkFlags VkPipelineVertexInputStateCreateFlags;
+typedef VkFlags VkPipelineInputAssemblyStateCreateFlags;
+typedef VkFlags VkPipelineTessellationStateCreateFlags;
+typedef VkFlags VkPipelineViewportStateCreateFlags;
+typedef VkFlags VkPipelineRasterizationStateCreateFlags;
+
+typedef enum VkCullModeFlagBits {
+    VK_CULL_MODE_NONE = 0,
+    VK_CULL_MODE_FRONT_BIT = 0x00000001,
+    VK_CULL_MODE_BACK_BIT = 0x00000002,
+    VK_CULL_MODE_FRONT_AND_BACK = 0x00000003,
+    VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkCullModeFlagBits;
+typedef VkFlags VkCullModeFlags;
+typedef VkFlags VkPipelineMultisampleStateCreateFlags;
+typedef VkFlags VkPipelineDepthStencilStateCreateFlags;
+typedef VkFlags VkPipelineColorBlendStateCreateFlags;
+
+typedef enum VkColorComponentFlagBits {
+    VK_COLOR_COMPONENT_R_BIT = 0x00000001,
+    VK_COLOR_COMPONENT_G_BIT = 0x00000002,
+    VK_COLOR_COMPONENT_B_BIT = 0x00000004,
+    VK_COLOR_COMPONENT_A_BIT = 0x00000008,
+    VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkColorComponentFlagBits;
+typedef VkFlags VkColorComponentFlags;
+typedef VkFlags VkPipelineDynamicStateCreateFlags;
+typedef VkFlags VkPipelineLayoutCreateFlags;
+typedef VkFlags VkShaderStageFlags;
+typedef VkFlags VkSamplerCreateFlags;
+
+typedef enum VkDescriptorSetLayoutCreateFlagBits {
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001,
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkDescriptorSetLayoutCreateFlagBits;
+typedef VkFlags VkDescriptorSetLayoutCreateFlags;
+
+typedef enum VkDescriptorPoolCreateFlagBits {
+    VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001,
+    VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkDescriptorPoolCreateFlagBits;
+typedef VkFlags VkDescriptorPoolCreateFlags;
+typedef VkFlags VkDescriptorPoolResetFlags;
+typedef VkFlags VkFramebufferCreateFlags;
+typedef VkFlags VkRenderPassCreateFlags;
+
+typedef enum VkAttachmentDescriptionFlagBits {
+    VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001,
+    VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkAttachmentDescriptionFlagBits;
+typedef VkFlags VkAttachmentDescriptionFlags;
+
+typedef enum VkSubpassDescriptionFlagBits {
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001,
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002,
+    VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSubpassDescriptionFlagBits;
+typedef VkFlags VkSubpassDescriptionFlags;
+
+typedef enum VkAccessFlagBits {
+    VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001,
+    VK_ACCESS_INDEX_READ_BIT = 0x00000002,
+    VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004,
+    VK_ACCESS_UNIFORM_READ_BIT = 0x00000008,
+    VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010,
+    VK_ACCESS_SHADER_READ_BIT = 0x00000020,
+    VK_ACCESS_SHADER_WRITE_BIT = 0x00000040,
+    VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080,
+    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100,
+    VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200,
+    VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400,
+    VK_ACCESS_TRANSFER_READ_BIT = 0x00000800,
+    VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000,
+    VK_ACCESS_HOST_READ_BIT = 0x00002000,
+    VK_ACCESS_HOST_WRITE_BIT = 0x00004000,
+    VK_ACCESS_MEMORY_READ_BIT = 0x00008000,
+    VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000,
+    VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000,
+    VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000,
+    VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkAccessFlagBits;
+typedef VkFlags VkAccessFlags;
+
+typedef enum VkDependencyFlagBits {
+    VK_DEPENDENCY_BY_REGION_BIT = 0x00000001,
+    VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX = 0x00000002,
+    VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX = 0x00000004,
+    VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkDependencyFlagBits;
+typedef VkFlags VkDependencyFlags;
+
+typedef enum VkCommandPoolCreateFlagBits {
+    VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001,
+    VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002,
+    VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkCommandPoolCreateFlagBits;
+typedef VkFlags VkCommandPoolCreateFlags;
+
+typedef enum VkCommandPoolResetFlagBits {
+    VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 0x00000001,
+    VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkCommandPoolResetFlagBits;
+typedef VkFlags VkCommandPoolResetFlags;
+
+typedef enum VkCommandBufferUsageFlagBits {
+    VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 0x00000001,
+    VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 0x00000002,
+    VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 0x00000004,
+    VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkCommandBufferUsageFlagBits;
+typedef VkFlags VkCommandBufferUsageFlags;
+
+typedef enum VkQueryControlFlagBits {
+    VK_QUERY_CONTROL_PRECISE_BIT = 0x00000001,
+    VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkQueryControlFlagBits;
+typedef VkFlags VkQueryControlFlags;
+
+typedef enum VkCommandBufferResetFlagBits {
+    VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 0x00000001,
+    VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkCommandBufferResetFlagBits;
+typedef VkFlags VkCommandBufferResetFlags;
+
+typedef enum VkStencilFaceFlagBits {
+    VK_STENCIL_FACE_FRONT_BIT = 0x00000001,
+    VK_STENCIL_FACE_BACK_BIT = 0x00000002,
+    VK_STENCIL_FRONT_AND_BACK = 0x00000003,
+    VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkStencilFaceFlagBits;
+typedef VkFlags VkStencilFaceFlags;
+
+typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)(
+    void*                                       pUserData,
+    size_t                                      size,
+    size_t                                      alignment,
+    VkSystemAllocationScope                     allocationScope);
+
+typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)(
+    void*                                       pUserData,
+    void*                                       pOriginal,
+    size_t                                      size,
+    size_t                                      alignment,
+    VkSystemAllocationScope                     allocationScope);
+
+typedef void (VKAPI_PTR *PFN_vkFreeFunction)(
+    void*                                       pUserData,
+    void*                                       pMemory);
+
+typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)(
+    void*                                       pUserData,
+    size_t                                      size,
+    VkInternalAllocationType                    allocationType,
+    VkSystemAllocationScope                     allocationScope);
+
+typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)(
+    void*                                       pUserData,
+    size_t                                      size,
+    VkInternalAllocationType                    allocationType,
+    VkSystemAllocationScope                     allocationScope);
+
+typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void);
+
+typedef struct VkApplicationInfo {
+    VkStructureType    sType;
+    const void*        pNext;
+    const char*        pApplicationName;
+    uint32_t           applicationVersion;
+    const char*        pEngineName;
+    uint32_t           engineVersion;
+    uint32_t           apiVersion;
+} VkApplicationInfo;
+
+typedef struct VkInstanceCreateInfo {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkInstanceCreateFlags       flags;
+    const VkApplicationInfo*    pApplicationInfo;
+    uint32_t                    enabledLayerCount;
+    const char* const*          ppEnabledLayerNames;
+    uint32_t                    enabledExtensionCount;
+    const char* const*          ppEnabledExtensionNames;
+} VkInstanceCreateInfo;
+
+typedef struct VkAllocationCallbacks {
+    void*                                   pUserData;
+    PFN_vkAllocationFunction                pfnAllocation;
+    PFN_vkReallocationFunction              pfnReallocation;
+    PFN_vkFreeFunction                      pfnFree;
+    PFN_vkInternalAllocationNotification    pfnInternalAllocation;
+    PFN_vkInternalFreeNotification          pfnInternalFree;
+} VkAllocationCallbacks;
+
+typedef struct VkPhysicalDeviceFeatures {
+    VkBool32    robustBufferAccess;
+    VkBool32    fullDrawIndexUint32;
+    VkBool32    imageCubeArray;
+    VkBool32    independentBlend;
+    VkBool32    geometryShader;
+    VkBool32    tessellationShader;
+    VkBool32    sampleRateShading;
+    VkBool32    dualSrcBlend;
+    VkBool32    logicOp;
+    VkBool32    multiDrawIndirect;
+    VkBool32    drawIndirectFirstInstance;
+    VkBool32    depthClamp;
+    VkBool32    depthBiasClamp;
+    VkBool32    fillModeNonSolid;
+    VkBool32    depthBounds;
+    VkBool32    wideLines;
+    VkBool32    largePoints;
+    VkBool32    alphaToOne;
+    VkBool32    multiViewport;
+    VkBool32    samplerAnisotropy;
+    VkBool32    textureCompressionETC2;
+    VkBool32    textureCompressionASTC_LDR;
+    VkBool32    textureCompressionBC;
+    VkBool32    occlusionQueryPrecise;
+    VkBool32    pipelineStatisticsQuery;
+    VkBool32    vertexPipelineStoresAndAtomics;
+    VkBool32    fragmentStoresAndAtomics;
+    VkBool32    shaderTessellationAndGeometryPointSize;
+    VkBool32    shaderImageGatherExtended;
+    VkBool32    shaderStorageImageExtendedFormats;
+    VkBool32    shaderStorageImageMultisample;
+    VkBool32    shaderStorageImageReadWithoutFormat;
+    VkBool32    shaderStorageImageWriteWithoutFormat;
+    VkBool32    shaderUniformBufferArrayDynamicIndexing;
+    VkBool32    shaderSampledImageArrayDynamicIndexing;
+    VkBool32    shaderStorageBufferArrayDynamicIndexing;
+    VkBool32    shaderStorageImageArrayDynamicIndexing;
+    VkBool32    shaderClipDistance;
+    VkBool32    shaderCullDistance;
+    VkBool32    shaderFloat64;
+    VkBool32    shaderInt64;
+    VkBool32    shaderInt16;
+    VkBool32    shaderResourceResidency;
+    VkBool32    shaderResourceMinLod;
+    VkBool32    sparseBinding;
+    VkBool32    sparseResidencyBuffer;
+    VkBool32    sparseResidencyImage2D;
+    VkBool32    sparseResidencyImage3D;
+    VkBool32    sparseResidency2Samples;
+    VkBool32    sparseResidency4Samples;
+    VkBool32    sparseResidency8Samples;
+    VkBool32    sparseResidency16Samples;
+    VkBool32    sparseResidencyAliased;
+    VkBool32    variableMultisampleRate;
+    VkBool32    inheritedQueries;
+} VkPhysicalDeviceFeatures;
+
+typedef struct VkFormatProperties {
+    VkFormatFeatureFlags    linearTilingFeatures;
+    VkFormatFeatureFlags    optimalTilingFeatures;
+    VkFormatFeatureFlags    bufferFeatures;
+} VkFormatProperties;
+
+typedef struct VkExtent3D {
+    uint32_t    width;
+    uint32_t    height;
+    uint32_t    depth;
+} VkExtent3D;
+
+typedef struct VkImageFormatProperties {
+    VkExtent3D            maxExtent;
+    uint32_t              maxMipLevels;
+    uint32_t              maxArrayLayers;
+    VkSampleCountFlags    sampleCounts;
+    VkDeviceSize          maxResourceSize;
+} VkImageFormatProperties;
+
+typedef struct VkPhysicalDeviceLimits {
+    uint32_t              maxImageDimension1D;
+    uint32_t              maxImageDimension2D;
+    uint32_t              maxImageDimension3D;
+    uint32_t              maxImageDimensionCube;
+    uint32_t              maxImageArrayLayers;
+    uint32_t              maxTexelBufferElements;
+    uint32_t              maxUniformBufferRange;
+    uint32_t              maxStorageBufferRange;
+    uint32_t              maxPushConstantsSize;
+    uint32_t              maxMemoryAllocationCount;
+    uint32_t              maxSamplerAllocationCount;
+    VkDeviceSize          bufferImageGranularity;
+    VkDeviceSize          sparseAddressSpaceSize;
+    uint32_t              maxBoundDescriptorSets;
+    uint32_t              maxPerStageDescriptorSamplers;
+    uint32_t              maxPerStageDescriptorUniformBuffers;
+    uint32_t              maxPerStageDescriptorStorageBuffers;
+    uint32_t              maxPerStageDescriptorSampledImages;
+    uint32_t              maxPerStageDescriptorStorageImages;
+    uint32_t              maxPerStageDescriptorInputAttachments;
+    uint32_t              maxPerStageResources;
+    uint32_t              maxDescriptorSetSamplers;
+    uint32_t              maxDescriptorSetUniformBuffers;
+    uint32_t              maxDescriptorSetUniformBuffersDynamic;
+    uint32_t              maxDescriptorSetStorageBuffers;
+    uint32_t              maxDescriptorSetStorageBuffersDynamic;
+    uint32_t              maxDescriptorSetSampledImages;
+    uint32_t              maxDescriptorSetStorageImages;
+    uint32_t              maxDescriptorSetInputAttachments;
+    uint32_t              maxVertexInputAttributes;
+    uint32_t              maxVertexInputBindings;
+    uint32_t              maxVertexInputAttributeOffset;
+    uint32_t              maxVertexInputBindingStride;
+    uint32_t              maxVertexOutputComponents;
+    uint32_t              maxTessellationGenerationLevel;
+    uint32_t              maxTessellationPatchSize;
+    uint32_t              maxTessellationControlPerVertexInputComponents;
+    uint32_t              maxTessellationControlPerVertexOutputComponents;
+    uint32_t              maxTessellationControlPerPatchOutputComponents;
+    uint32_t              maxTessellationControlTotalOutputComponents;
+    uint32_t              maxTessellationEvaluationInputComponents;
+    uint32_t              maxTessellationEvaluationOutputComponents;
+    uint32_t              maxGeometryShaderInvocations;
+    uint32_t              maxGeometryInputComponents;
+    uint32_t              maxGeometryOutputComponents;
+    uint32_t              maxGeometryOutputVertices;
+    uint32_t              maxGeometryTotalOutputComponents;
+    uint32_t              maxFragmentInputComponents;
+    uint32_t              maxFragmentOutputAttachments;
+    uint32_t              maxFragmentDualSrcAttachments;
+    uint32_t              maxFragmentCombinedOutputResources;
+    uint32_t              maxComputeSharedMemorySize;
+    uint32_t              maxComputeWorkGroupCount[3];
+    uint32_t              maxComputeWorkGroupInvocations;
+    uint32_t              maxComputeWorkGroupSize[3];
+    uint32_t              subPixelPrecisionBits;
+    uint32_t              subTexelPrecisionBits;
+    uint32_t              mipmapPrecisionBits;
+    uint32_t              maxDrawIndexedIndexValue;
+    uint32_t              maxDrawIndirectCount;
+    float                 maxSamplerLodBias;
+    float                 maxSamplerAnisotropy;
+    uint32_t              maxViewports;
+    uint32_t              maxViewportDimensions[2];
+    float                 viewportBoundsRange[2];
+    uint32_t              viewportSubPixelBits;
+    size_t                minMemoryMapAlignment;
+    VkDeviceSize          minTexelBufferOffsetAlignment;
+    VkDeviceSize          minUniformBufferOffsetAlignment;
+    VkDeviceSize          minStorageBufferOffsetAlignment;
+    int32_t               minTexelOffset;
+    uint32_t              maxTexelOffset;
+    int32_t               minTexelGatherOffset;
+    uint32_t              maxTexelGatherOffset;
+    float                 minInterpolationOffset;
+    float                 maxInterpolationOffset;
+    uint32_t              subPixelInterpolationOffsetBits;
+    uint32_t              maxFramebufferWidth;
+    uint32_t              maxFramebufferHeight;
+    uint32_t              maxFramebufferLayers;
+    VkSampleCountFlags    framebufferColorSampleCounts;
+    VkSampleCountFlags    framebufferDepthSampleCounts;
+    VkSampleCountFlags    framebufferStencilSampleCounts;
+    VkSampleCountFlags    framebufferNoAttachmentsSampleCounts;
+    uint32_t              maxColorAttachments;
+    VkSampleCountFlags    sampledImageColorSampleCounts;
+    VkSampleCountFlags    sampledImageIntegerSampleCounts;
+    VkSampleCountFlags    sampledImageDepthSampleCounts;
+    VkSampleCountFlags    sampledImageStencilSampleCounts;
+    VkSampleCountFlags    storageImageSampleCounts;
+    uint32_t              maxSampleMaskWords;
+    VkBool32              timestampComputeAndGraphics;
+    float                 timestampPeriod;
+    uint32_t              maxClipDistances;
+    uint32_t              maxCullDistances;
+    uint32_t              maxCombinedClipAndCullDistances;
+    uint32_t              discreteQueuePriorities;
+    float                 pointSizeRange[2];
+    float                 lineWidthRange[2];
+    float                 pointSizeGranularity;
+    float                 lineWidthGranularity;
+    VkBool32              strictLines;
+    VkBool32              standardSampleLocations;
+    VkDeviceSize          optimalBufferCopyOffsetAlignment;
+    VkDeviceSize          optimalBufferCopyRowPitchAlignment;
+    VkDeviceSize          nonCoherentAtomSize;
+} VkPhysicalDeviceLimits;
+
+typedef struct VkPhysicalDeviceSparseProperties {
+    VkBool32    residencyStandard2DBlockShape;
+    VkBool32    residencyStandard2DMultisampleBlockShape;
+    VkBool32    residencyStandard3DBlockShape;
+    VkBool32    residencyAlignedMipSize;
+    VkBool32    residencyNonResidentStrict;
+} VkPhysicalDeviceSparseProperties;
+
+typedef struct VkPhysicalDeviceProperties {
+    uint32_t                            apiVersion;
+    uint32_t                            driverVersion;
+    uint32_t                            vendorID;
+    uint32_t                            deviceID;
+    VkPhysicalDeviceType                deviceType;
+    char                                deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
+    uint8_t                             pipelineCacheUUID[VK_UUID_SIZE];
+    VkPhysicalDeviceLimits              limits;
+    VkPhysicalDeviceSparseProperties    sparseProperties;
+} VkPhysicalDeviceProperties;
+
+typedef struct VkQueueFamilyProperties {
+    VkQueueFlags    queueFlags;
+    uint32_t        queueCount;
+    uint32_t        timestampValidBits;
+    VkExtent3D      minImageTransferGranularity;
+} VkQueueFamilyProperties;
+
+typedef struct VkMemoryType {
+    VkMemoryPropertyFlags    propertyFlags;
+    uint32_t                 heapIndex;
+} VkMemoryType;
+
+typedef struct VkMemoryHeap {
+    VkDeviceSize         size;
+    VkMemoryHeapFlags    flags;
+} VkMemoryHeap;
+
+typedef struct VkPhysicalDeviceMemoryProperties {
+    uint32_t        memoryTypeCount;
+    VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];
+    uint32_t        memoryHeapCount;
+    VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
+} VkPhysicalDeviceMemoryProperties;
+
+typedef struct VkDeviceQueueCreateInfo {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkDeviceQueueCreateFlags    flags;
+    uint32_t                    queueFamilyIndex;
+    uint32_t                    queueCount;
+    const float*                pQueuePriorities;
+} VkDeviceQueueCreateInfo;
+
+typedef struct VkDeviceCreateInfo {
+    VkStructureType                    sType;
+    const void*                        pNext;
+    VkDeviceCreateFlags                flags;
+    uint32_t                           queueCreateInfoCount;
+    const VkDeviceQueueCreateInfo*     pQueueCreateInfos;
+    uint32_t                           enabledLayerCount;
+    const char* const*                 ppEnabledLayerNames;
+    uint32_t                           enabledExtensionCount;
+    const char* const*                 ppEnabledExtensionNames;
+    const VkPhysicalDeviceFeatures*    pEnabledFeatures;
+} VkDeviceCreateInfo;
+
+typedef struct VkExtensionProperties {
+    char        extensionName[VK_MAX_EXTENSION_NAME_SIZE];
+    uint32_t    specVersion;
+} VkExtensionProperties;
+
+typedef struct VkLayerProperties {
+    char        layerName[VK_MAX_EXTENSION_NAME_SIZE];
+    uint32_t    specVersion;
+    uint32_t    implementationVersion;
+    char        description[VK_MAX_DESCRIPTION_SIZE];
+} VkLayerProperties;
+
+typedef struct VkSubmitInfo {
+    VkStructureType                sType;
+    const void*                    pNext;
+    uint32_t                       waitSemaphoreCount;
+    const VkSemaphore*             pWaitSemaphores;
+    const VkPipelineStageFlags*    pWaitDstStageMask;
+    uint32_t                       commandBufferCount;
+    const VkCommandBuffer*         pCommandBuffers;
+    uint32_t                       signalSemaphoreCount;
+    const VkSemaphore*             pSignalSemaphores;
+} VkSubmitInfo;
+
+typedef struct VkMemoryAllocateInfo {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkDeviceSize       allocationSize;
+    uint32_t           memoryTypeIndex;
+} VkMemoryAllocateInfo;
+
+typedef struct VkMappedMemoryRange {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkDeviceMemory     memory;
+    VkDeviceSize       offset;
+    VkDeviceSize       size;
+} VkMappedMemoryRange;
+
+typedef struct VkMemoryRequirements {
+    VkDeviceSize    size;
+    VkDeviceSize    alignment;
+    uint32_t        memoryTypeBits;
+} VkMemoryRequirements;
+
+typedef struct VkSparseImageFormatProperties {
+    VkImageAspectFlags          aspectMask;
+    VkExtent3D                  imageGranularity;
+    VkSparseImageFormatFlags    flags;
+} VkSparseImageFormatProperties;
+
+typedef struct VkSparseImageMemoryRequirements {
+    VkSparseImageFormatProperties    formatProperties;
+    uint32_t                         imageMipTailFirstLod;
+    VkDeviceSize                     imageMipTailSize;
+    VkDeviceSize                     imageMipTailOffset;
+    VkDeviceSize                     imageMipTailStride;
+} VkSparseImageMemoryRequirements;
+
+typedef struct VkSparseMemoryBind {
+    VkDeviceSize               resourceOffset;
+    VkDeviceSize               size;
+    VkDeviceMemory             memory;
+    VkDeviceSize               memoryOffset;
+    VkSparseMemoryBindFlags    flags;
+} VkSparseMemoryBind;
+
+typedef struct VkSparseBufferMemoryBindInfo {
+    VkBuffer                     buffer;
+    uint32_t                     bindCount;
+    const VkSparseMemoryBind*    pBinds;
+} VkSparseBufferMemoryBindInfo;
+
+typedef struct VkSparseImageOpaqueMemoryBindInfo {
+    VkImage                      image;
+    uint32_t                     bindCount;
+    const VkSparseMemoryBind*    pBinds;
+} VkSparseImageOpaqueMemoryBindInfo;
+
+typedef struct VkImageSubresource {
+    VkImageAspectFlags    aspectMask;
+    uint32_t              mipLevel;
+    uint32_t              arrayLayer;
+} VkImageSubresource;
+
+typedef struct VkOffset3D {
+    int32_t    x;
+    int32_t    y;
+    int32_t    z;
+} VkOffset3D;
+
+typedef struct VkSparseImageMemoryBind {
+    VkImageSubresource         subresource;
+    VkOffset3D                 offset;
+    VkExtent3D                 extent;
+    VkDeviceMemory             memory;
+    VkDeviceSize               memoryOffset;
+    VkSparseMemoryBindFlags    flags;
+} VkSparseImageMemoryBind;
+
+typedef struct VkSparseImageMemoryBindInfo {
+    VkImage                           image;
+    uint32_t                          bindCount;
+    const VkSparseImageMemoryBind*    pBinds;
+} VkSparseImageMemoryBindInfo;
+
+typedef struct VkBindSparseInfo {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    uint32_t                                    waitSemaphoreCount;
+    const VkSemaphore*                          pWaitSemaphores;
+    uint32_t                                    bufferBindCount;
+    const VkSparseBufferMemoryBindInfo*         pBufferBinds;
+    uint32_t                                    imageOpaqueBindCount;
+    const VkSparseImageOpaqueMemoryBindInfo*    pImageOpaqueBinds;
+    uint32_t                                    imageBindCount;
+    const VkSparseImageMemoryBindInfo*          pImageBinds;
+    uint32_t                                    signalSemaphoreCount;
+    const VkSemaphore*                          pSignalSemaphores;
+} VkBindSparseInfo;
+
+typedef struct VkFenceCreateInfo {
+    VkStructureType       sType;
+    const void*           pNext;
+    VkFenceCreateFlags    flags;
+} VkFenceCreateInfo;
+
+typedef struct VkSemaphoreCreateInfo {
+    VkStructureType           sType;
+    const void*               pNext;
+    VkSemaphoreCreateFlags    flags;
+} VkSemaphoreCreateInfo;
+
+typedef struct VkEventCreateInfo {
+    VkStructureType       sType;
+    const void*           pNext;
+    VkEventCreateFlags    flags;
+} VkEventCreateInfo;
+
+typedef struct VkQueryPoolCreateInfo {
+    VkStructureType                  sType;
+    const void*                      pNext;
+    VkQueryPoolCreateFlags           flags;
+    VkQueryType                      queryType;
+    uint32_t                         queryCount;
+    VkQueryPipelineStatisticFlags    pipelineStatistics;
+} VkQueryPoolCreateInfo;
+
+typedef struct VkBufferCreateInfo {
+    VkStructureType        sType;
+    const void*            pNext;
+    VkBufferCreateFlags    flags;
+    VkDeviceSize           size;
+    VkBufferUsageFlags     usage;
+    VkSharingMode          sharingMode;
+    uint32_t               queueFamilyIndexCount;
+    const uint32_t*        pQueueFamilyIndices;
+} VkBufferCreateInfo;
+
+typedef struct VkBufferViewCreateInfo {
+    VkStructureType            sType;
+    const void*                pNext;
+    VkBufferViewCreateFlags    flags;
+    VkBuffer                   buffer;
+    VkFormat                   format;
+    VkDeviceSize               offset;
+    VkDeviceSize               range;
+} VkBufferViewCreateInfo;
+
+typedef struct VkImageCreateInfo {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkImageCreateFlags       flags;
+    VkImageType              imageType;
+    VkFormat                 format;
+    VkExtent3D               extent;
+    uint32_t                 mipLevels;
+    uint32_t                 arrayLayers;
+    VkSampleCountFlagBits    samples;
+    VkImageTiling            tiling;
+    VkImageUsageFlags        usage;
+    VkSharingMode            sharingMode;
+    uint32_t                 queueFamilyIndexCount;
+    const uint32_t*          pQueueFamilyIndices;
+    VkImageLayout            initialLayout;
+} VkImageCreateInfo;
+
+typedef struct VkSubresourceLayout {
+    VkDeviceSize    offset;
+    VkDeviceSize    size;
+    VkDeviceSize    rowPitch;
+    VkDeviceSize    arrayPitch;
+    VkDeviceSize    depthPitch;
+} VkSubresourceLayout;
+
+typedef struct VkComponentMapping {
+    VkComponentSwizzle    r;
+    VkComponentSwizzle    g;
+    VkComponentSwizzle    b;
+    VkComponentSwizzle    a;
+} VkComponentMapping;
+
+typedef struct VkImageSubresourceRange {
+    VkImageAspectFlags    aspectMask;
+    uint32_t              baseMipLevel;
+    uint32_t              levelCount;
+    uint32_t              baseArrayLayer;
+    uint32_t              layerCount;
+} VkImageSubresourceRange;
+
+typedef struct VkImageViewCreateInfo {
+    VkStructureType            sType;
+    const void*                pNext;
+    VkImageViewCreateFlags     flags;
+    VkImage                    image;
+    VkImageViewType            viewType;
+    VkFormat                   format;
+    VkComponentMapping         components;
+    VkImageSubresourceRange    subresourceRange;
+} VkImageViewCreateInfo;
+
+typedef struct VkShaderModuleCreateInfo {
+    VkStructureType              sType;
+    const void*                  pNext;
+    VkShaderModuleCreateFlags    flags;
+    size_t                       codeSize;
+    const uint32_t*              pCode;
+} VkShaderModuleCreateInfo;
+
+typedef struct VkPipelineCacheCreateInfo {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkPipelineCacheCreateFlags    flags;
+    size_t                        initialDataSize;
+    const void*                   pInitialData;
+} VkPipelineCacheCreateInfo;
+
+typedef struct VkSpecializationMapEntry {
+    uint32_t    constantID;
+    uint32_t    offset;
+    size_t      size;
+} VkSpecializationMapEntry;
+
+typedef struct VkSpecializationInfo {
+    uint32_t                           mapEntryCount;
+    const VkSpecializationMapEntry*    pMapEntries;
+    size_t                             dataSize;
+    const void*                        pData;
+} VkSpecializationInfo;
+
+typedef struct VkPipelineShaderStageCreateInfo {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    VkPipelineShaderStageCreateFlags    flags;
+    VkShaderStageFlagBits               stage;
+    VkShaderModule                      module;
+    const char*                         pName;
+    const VkSpecializationInfo*         pSpecializationInfo;
+} VkPipelineShaderStageCreateInfo;
+
+typedef struct VkVertexInputBindingDescription {
+    uint32_t             binding;
+    uint32_t             stride;
+    VkVertexInputRate    inputRate;
+} VkVertexInputBindingDescription;
+
+typedef struct VkVertexInputAttributeDescription {
+    uint32_t    location;
+    uint32_t    binding;
+    VkFormat    format;
+    uint32_t    offset;
+} VkVertexInputAttributeDescription;
+
+typedef struct VkPipelineVertexInputStateCreateInfo {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkPipelineVertexInputStateCreateFlags       flags;
+    uint32_t                                    vertexBindingDescriptionCount;
+    const VkVertexInputBindingDescription*      pVertexBindingDescriptions;
+    uint32_t                                    vertexAttributeDescriptionCount;
+    const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions;
+} VkPipelineVertexInputStateCreateInfo;
+
+typedef struct VkPipelineInputAssemblyStateCreateInfo {
+    VkStructureType                            sType;
+    const void*                                pNext;
+    VkPipelineInputAssemblyStateCreateFlags    flags;
+    VkPrimitiveTopology                        topology;
+    VkBool32                                   primitiveRestartEnable;
+} VkPipelineInputAssemblyStateCreateInfo;
+
+typedef struct VkPipelineTessellationStateCreateInfo {
+    VkStructureType                           sType;
+    const void*                               pNext;
+    VkPipelineTessellationStateCreateFlags    flags;
+    uint32_t                                  patchControlPoints;
+} VkPipelineTessellationStateCreateInfo;
+
+typedef struct VkViewport {
+    float    x;
+    float    y;
+    float    width;
+    float    height;
+    float    minDepth;
+    float    maxDepth;
+} VkViewport;
+
+typedef struct VkOffset2D {
+    int32_t    x;
+    int32_t    y;
+} VkOffset2D;
+
+typedef struct VkExtent2D {
+    uint32_t    width;
+    uint32_t    height;
+} VkExtent2D;
+
+typedef struct VkRect2D {
+    VkOffset2D    offset;
+    VkExtent2D    extent;
+} VkRect2D;
+
+typedef struct VkPipelineViewportStateCreateInfo {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkPipelineViewportStateCreateFlags    flags;
+    uint32_t                              viewportCount;
+    const VkViewport*                     pViewports;
+    uint32_t                              scissorCount;
+    const VkRect2D*                       pScissors;
+} VkPipelineViewportStateCreateInfo;
+
+typedef struct VkPipelineRasterizationStateCreateInfo {
+    VkStructureType                            sType;
+    const void*                                pNext;
+    VkPipelineRasterizationStateCreateFlags    flags;
+    VkBool32                                   depthClampEnable;
+    VkBool32                                   rasterizerDiscardEnable;
+    VkPolygonMode                              polygonMode;
+    VkCullModeFlags                            cullMode;
+    VkFrontFace                                frontFace;
+    VkBool32                                   depthBiasEnable;
+    float                                      depthBiasConstantFactor;
+    float                                      depthBiasClamp;
+    float                                      depthBiasSlopeFactor;
+    float                                      lineWidth;
+} VkPipelineRasterizationStateCreateInfo;
+
+typedef struct VkPipelineMultisampleStateCreateInfo {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkPipelineMultisampleStateCreateFlags    flags;
+    VkSampleCountFlagBits                    rasterizationSamples;
+    VkBool32                                 sampleShadingEnable;
+    float                                    minSampleShading;
+    const VkSampleMask*                      pSampleMask;
+    VkBool32                                 alphaToCoverageEnable;
+    VkBool32                                 alphaToOneEnable;
+} VkPipelineMultisampleStateCreateInfo;
+
+typedef struct VkStencilOpState {
+    VkStencilOp    failOp;
+    VkStencilOp    passOp;
+    VkStencilOp    depthFailOp;
+    VkCompareOp    compareOp;
+    uint32_t       compareMask;
+    uint32_t       writeMask;
+    uint32_t       reference;
+} VkStencilOpState;
+
+typedef struct VkPipelineDepthStencilStateCreateInfo {
+    VkStructureType                           sType;
+    const void*                               pNext;
+    VkPipelineDepthStencilStateCreateFlags    flags;
+    VkBool32                                  depthTestEnable;
+    VkBool32                                  depthWriteEnable;
+    VkCompareOp                               depthCompareOp;
+    VkBool32                                  depthBoundsTestEnable;
+    VkBool32                                  stencilTestEnable;
+    VkStencilOpState                          front;
+    VkStencilOpState                          back;
+    float                                     minDepthBounds;
+    float                                     maxDepthBounds;
+} VkPipelineDepthStencilStateCreateInfo;
+
+typedef struct VkPipelineColorBlendAttachmentState {
+    VkBool32                 blendEnable;
+    VkBlendFactor            srcColorBlendFactor;
+    VkBlendFactor            dstColorBlendFactor;
+    VkBlendOp                colorBlendOp;
+    VkBlendFactor            srcAlphaBlendFactor;
+    VkBlendFactor            dstAlphaBlendFactor;
+    VkBlendOp                alphaBlendOp;
+    VkColorComponentFlags    colorWriteMask;
+} VkPipelineColorBlendAttachmentState;
+
+typedef struct VkPipelineColorBlendStateCreateInfo {
+    VkStructureType                               sType;
+    const void*                                   pNext;
+    VkPipelineColorBlendStateCreateFlags          flags;
+    VkBool32                                      logicOpEnable;
+    VkLogicOp                                     logicOp;
+    uint32_t                                      attachmentCount;
+    const VkPipelineColorBlendAttachmentState*    pAttachments;
+    float                                         blendConstants[4];
+} VkPipelineColorBlendStateCreateInfo;
+
+typedef struct VkPipelineDynamicStateCreateInfo {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkPipelineDynamicStateCreateFlags    flags;
+    uint32_t                             dynamicStateCount;
+    const VkDynamicState*                pDynamicStates;
+} VkPipelineDynamicStateCreateInfo;
+
+typedef struct VkGraphicsPipelineCreateInfo {
+    VkStructureType                                  sType;
+    const void*                                      pNext;
+    VkPipelineCreateFlags                            flags;
+    uint32_t                                         stageCount;
+    const VkPipelineShaderStageCreateInfo*           pStages;
+    const VkPipelineVertexInputStateCreateInfo*      pVertexInputState;
+    const VkPipelineInputAssemblyStateCreateInfo*    pInputAssemblyState;
+    const VkPipelineTessellationStateCreateInfo*     pTessellationState;
+    const VkPipelineViewportStateCreateInfo*         pViewportState;
+    const VkPipelineRasterizationStateCreateInfo*    pRasterizationState;
+    const VkPipelineMultisampleStateCreateInfo*      pMultisampleState;
+    const VkPipelineDepthStencilStateCreateInfo*     pDepthStencilState;
+    const VkPipelineColorBlendStateCreateInfo*       pColorBlendState;
+    const VkPipelineDynamicStateCreateInfo*          pDynamicState;
+    VkPipelineLayout                                 layout;
+    VkRenderPass                                     renderPass;
+    uint32_t                                         subpass;
+    VkPipeline                                       basePipelineHandle;
+    int32_t                                          basePipelineIndex;
+} VkGraphicsPipelineCreateInfo;
+
+typedef struct VkComputePipelineCreateInfo {
+    VkStructureType                    sType;
+    const void*                        pNext;
+    VkPipelineCreateFlags              flags;
+    VkPipelineShaderStageCreateInfo    stage;
+    VkPipelineLayout                   layout;
+    VkPipeline                         basePipelineHandle;
+    int32_t                            basePipelineIndex;
+} VkComputePipelineCreateInfo;
+
+typedef struct VkPushConstantRange {
+    VkShaderStageFlags    stageFlags;
+    uint32_t              offset;
+    uint32_t              size;
+} VkPushConstantRange;
+
+typedef struct VkPipelineLayoutCreateInfo {
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkPipelineLayoutCreateFlags     flags;
+    uint32_t                        setLayoutCount;
+    const VkDescriptorSetLayout*    pSetLayouts;
+    uint32_t                        pushConstantRangeCount;
+    const VkPushConstantRange*      pPushConstantRanges;
+} VkPipelineLayoutCreateInfo;
+
+typedef struct VkSamplerCreateInfo {
+    VkStructureType         sType;
+    const void*             pNext;
+    VkSamplerCreateFlags    flags;
+    VkFilter                magFilter;
+    VkFilter                minFilter;
+    VkSamplerMipmapMode     mipmapMode;
+    VkSamplerAddressMode    addressModeU;
+    VkSamplerAddressMode    addressModeV;
+    VkSamplerAddressMode    addressModeW;
+    float                   mipLodBias;
+    VkBool32                anisotropyEnable;
+    float                   maxAnisotropy;
+    VkBool32                compareEnable;
+    VkCompareOp             compareOp;
+    float                   minLod;
+    float                   maxLod;
+    VkBorderColor           borderColor;
+    VkBool32                unnormalizedCoordinates;
+} VkSamplerCreateInfo;
+
+typedef struct VkDescriptorSetLayoutBinding {
+    uint32_t              binding;
+    VkDescriptorType      descriptorType;
+    uint32_t              descriptorCount;
+    VkShaderStageFlags    stageFlags;
+    const VkSampler*      pImmutableSamplers;
+} VkDescriptorSetLayoutBinding;
+
+typedef struct VkDescriptorSetLayoutCreateInfo {
+    VkStructureType                        sType;
+    const void*                            pNext;
+    VkDescriptorSetLayoutCreateFlags       flags;
+    uint32_t                               bindingCount;
+    const VkDescriptorSetLayoutBinding*    pBindings;
+} VkDescriptorSetLayoutCreateInfo;
+
+typedef struct VkDescriptorPoolSize {
+    VkDescriptorType    type;
+    uint32_t            descriptorCount;
+} VkDescriptorPoolSize;
+
+typedef struct VkDescriptorPoolCreateInfo {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkDescriptorPoolCreateFlags    flags;
+    uint32_t                       maxSets;
+    uint32_t                       poolSizeCount;
+    const VkDescriptorPoolSize*    pPoolSizes;
+} VkDescriptorPoolCreateInfo;
+
+typedef struct VkDescriptorSetAllocateInfo {
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkDescriptorPool                descriptorPool;
+    uint32_t                        descriptorSetCount;
+    const VkDescriptorSetLayout*    pSetLayouts;
+} VkDescriptorSetAllocateInfo;
+
+typedef struct VkDescriptorImageInfo {
+    VkSampler        sampler;
+    VkImageView      imageView;
+    VkImageLayout    imageLayout;
+} VkDescriptorImageInfo;
+
+typedef struct VkDescriptorBufferInfo {
+    VkBuffer        buffer;
+    VkDeviceSize    offset;
+    VkDeviceSize    range;
+} VkDescriptorBufferInfo;
+
+typedef struct VkWriteDescriptorSet {
+    VkStructureType                  sType;
+    const void*                      pNext;
+    VkDescriptorSet                  dstSet;
+    uint32_t                         dstBinding;
+    uint32_t                         dstArrayElement;
+    uint32_t                         descriptorCount;
+    VkDescriptorType                 descriptorType;
+    const VkDescriptorImageInfo*     pImageInfo;
+    const VkDescriptorBufferInfo*    pBufferInfo;
+    const VkBufferView*              pTexelBufferView;
+} VkWriteDescriptorSet;
+
+typedef struct VkCopyDescriptorSet {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkDescriptorSet    srcSet;
+    uint32_t           srcBinding;
+    uint32_t           srcArrayElement;
+    VkDescriptorSet    dstSet;
+    uint32_t           dstBinding;
+    uint32_t           dstArrayElement;
+    uint32_t           descriptorCount;
+} VkCopyDescriptorSet;
+
+typedef struct VkFramebufferCreateInfo {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkFramebufferCreateFlags    flags;
+    VkRenderPass                renderPass;
+    uint32_t                    attachmentCount;
+    const VkImageView*          pAttachments;
+    uint32_t                    width;
+    uint32_t                    height;
+    uint32_t                    layers;
+} VkFramebufferCreateInfo;
+
+typedef struct VkAttachmentDescription {
+    VkAttachmentDescriptionFlags    flags;
+    VkFormat                        format;
+    VkSampleCountFlagBits           samples;
+    VkAttachmentLoadOp              loadOp;
+    VkAttachmentStoreOp             storeOp;
+    VkAttachmentLoadOp              stencilLoadOp;
+    VkAttachmentStoreOp             stencilStoreOp;
+    VkImageLayout                   initialLayout;
+    VkImageLayout                   finalLayout;
+} VkAttachmentDescription;
+
+typedef struct VkAttachmentReference {
+    uint32_t         attachment;
+    VkImageLayout    layout;
+} VkAttachmentReference;
+
+typedef struct VkSubpassDescription {
+    VkSubpassDescriptionFlags       flags;
+    VkPipelineBindPoint             pipelineBindPoint;
+    uint32_t                        inputAttachmentCount;
+    const VkAttachmentReference*    pInputAttachments;
+    uint32_t                        colorAttachmentCount;
+    const VkAttachmentReference*    pColorAttachments;
+    const VkAttachmentReference*    pResolveAttachments;
+    const VkAttachmentReference*    pDepthStencilAttachment;
+    uint32_t                        preserveAttachmentCount;
+    const uint32_t*                 pPreserveAttachments;
+} VkSubpassDescription;
+
+typedef struct VkSubpassDependency {
+    uint32_t                srcSubpass;
+    uint32_t                dstSubpass;
+    VkPipelineStageFlags    srcStageMask;
+    VkPipelineStageFlags    dstStageMask;
+    VkAccessFlags           srcAccessMask;
+    VkAccessFlags           dstAccessMask;
+    VkDependencyFlags       dependencyFlags;
+} VkSubpassDependency;
+
+typedef struct VkRenderPassCreateInfo {
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkRenderPassCreateFlags           flags;
+    uint32_t                          attachmentCount;
+    const VkAttachmentDescription*    pAttachments;
+    uint32_t                          subpassCount;
+    const VkSubpassDescription*       pSubpasses;
+    uint32_t                          dependencyCount;
+    const VkSubpassDependency*        pDependencies;
+} VkRenderPassCreateInfo;
+
+typedef struct VkCommandPoolCreateInfo {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkCommandPoolCreateFlags    flags;
+    uint32_t                    queueFamilyIndex;
+} VkCommandPoolCreateInfo;
+
+typedef struct VkCommandBufferAllocateInfo {
+    VkStructureType         sType;
+    const void*             pNext;
+    VkCommandPool           commandPool;
+    VkCommandBufferLevel    level;
+    uint32_t                commandBufferCount;
+} VkCommandBufferAllocateInfo;
+
+typedef struct VkCommandBufferInheritanceInfo {
+    VkStructureType                  sType;
+    const void*                      pNext;
+    VkRenderPass                     renderPass;
+    uint32_t                         subpass;
+    VkFramebuffer                    framebuffer;
+    VkBool32                         occlusionQueryEnable;
+    VkQueryControlFlags              queryFlags;
+    VkQueryPipelineStatisticFlags    pipelineStatistics;
+} VkCommandBufferInheritanceInfo;
+
+typedef struct VkCommandBufferBeginInfo {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkCommandBufferUsageFlags                flags;
+    const VkCommandBufferInheritanceInfo*    pInheritanceInfo;
+} VkCommandBufferBeginInfo;
+
+typedef struct VkBufferCopy {
+    VkDeviceSize    srcOffset;
+    VkDeviceSize    dstOffset;
+    VkDeviceSize    size;
+} VkBufferCopy;
+
+typedef struct VkImageSubresourceLayers {
+    VkImageAspectFlags    aspectMask;
+    uint32_t              mipLevel;
+    uint32_t              baseArrayLayer;
+    uint32_t              layerCount;
+} VkImageSubresourceLayers;
+
+typedef struct VkImageCopy {
+    VkImageSubresourceLayers    srcSubresource;
+    VkOffset3D                  srcOffset;
+    VkImageSubresourceLayers    dstSubresource;
+    VkOffset3D                  dstOffset;
+    VkExtent3D                  extent;
+} VkImageCopy;
+
+typedef struct VkImageBlit {
+    VkImageSubresourceLayers    srcSubresource;
+    VkOffset3D                  srcOffsets[2];
+    VkImageSubresourceLayers    dstSubresource;
+    VkOffset3D                  dstOffsets[2];
+} VkImageBlit;
+
+typedef struct VkBufferImageCopy {
+    VkDeviceSize                bufferOffset;
+    uint32_t                    bufferRowLength;
+    uint32_t                    bufferImageHeight;
+    VkImageSubresourceLayers    imageSubresource;
+    VkOffset3D                  imageOffset;
+    VkExtent3D                  imageExtent;
+} VkBufferImageCopy;
+
+typedef union VkClearColorValue {
+    float       float32[4];
+    int32_t     int32[4];
+    uint32_t    uint32[4];
+} VkClearColorValue;
+
+typedef struct VkClearDepthStencilValue {
+    float       depth;
+    uint32_t    stencil;
+} VkClearDepthStencilValue;
+
+typedef union VkClearValue {
+    VkClearColorValue           color;
+    VkClearDepthStencilValue    depthStencil;
+} VkClearValue;
+
+typedef struct VkClearAttachment {
+    VkImageAspectFlags    aspectMask;
+    uint32_t              colorAttachment;
+    VkClearValue          clearValue;
+} VkClearAttachment;
+
+typedef struct VkClearRect {
+    VkRect2D    rect;
+    uint32_t    baseArrayLayer;
+    uint32_t    layerCount;
+} VkClearRect;
+
+typedef struct VkImageResolve {
+    VkImageSubresourceLayers    srcSubresource;
+    VkOffset3D                  srcOffset;
+    VkImageSubresourceLayers    dstSubresource;
+    VkOffset3D                  dstOffset;
+    VkExtent3D                  extent;
+} VkImageResolve;
+
+typedef struct VkMemoryBarrier {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkAccessFlags      srcAccessMask;
+    VkAccessFlags      dstAccessMask;
+} VkMemoryBarrier;
+
+typedef struct VkBufferMemoryBarrier {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkAccessFlags      srcAccessMask;
+    VkAccessFlags      dstAccessMask;
+    uint32_t           srcQueueFamilyIndex;
+    uint32_t           dstQueueFamilyIndex;
+    VkBuffer           buffer;
+    VkDeviceSize       offset;
+    VkDeviceSize       size;
+} VkBufferMemoryBarrier;
+
+typedef struct VkImageMemoryBarrier {
+    VkStructureType            sType;
+    const void*                pNext;
+    VkAccessFlags              srcAccessMask;
+    VkAccessFlags              dstAccessMask;
+    VkImageLayout              oldLayout;
+    VkImageLayout              newLayout;
+    uint32_t                   srcQueueFamilyIndex;
+    uint32_t                   dstQueueFamilyIndex;
+    VkImage                    image;
+    VkImageSubresourceRange    subresourceRange;
+} VkImageMemoryBarrier;
+
+typedef struct VkRenderPassBeginInfo {
+    VkStructureType        sType;
+    const void*            pNext;
+    VkRenderPass           renderPass;
+    VkFramebuffer          framebuffer;
+    VkRect2D               renderArea;
+    uint32_t               clearValueCount;
+    const VkClearValue*    pClearValues;
+} VkRenderPassBeginInfo;
+
+typedef struct VkDispatchIndirectCommand {
+    uint32_t    x;
+    uint32_t    y;
+    uint32_t    z;
+} VkDispatchIndirectCommand;
+
+typedef struct VkDrawIndexedIndirectCommand {
+    uint32_t    indexCount;
+    uint32_t    instanceCount;
+    uint32_t    firstIndex;
+    int32_t     vertexOffset;
+    uint32_t    firstInstance;
+} VkDrawIndexedIndirectCommand;
+
+typedef struct VkDrawIndirectCommand {
+    uint32_t    vertexCount;
+    uint32_t    instanceCount;
+    uint32_t    firstVertex;
+    uint32_t    firstInstance;
+} VkDrawIndirectCommand;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
+typedef void (VKAPI_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties);
+typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName);
+typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char* pName);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
+typedef void (VKAPI_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
+typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
+typedef VkResult (VKAPI_PTR *PFN_vkQueueWaitIdle)(VkQueue queue);
+typedef VkResult (VKAPI_PTR *PFN_vkDeviceWaitIdle)(VkDevice device);
+typedef VkResult (VKAPI_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory);
+typedef void (VKAPI_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData);
+typedef void (VKAPI_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory);
+typedef VkResult (VKAPI_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges);
+typedef VkResult (VKAPI_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges);
+typedef void (VKAPI_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes);
+typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset);
+typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset);
+typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements);
+typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements);
+typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef void (VKAPI_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences);
+typedef VkResult (VKAPI_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence);
+typedef VkResult (VKAPI_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore);
+typedef void (VKAPI_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent);
+typedef void (VKAPI_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event);
+typedef VkResult (VKAPI_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event);
+typedef VkResult (VKAPI_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
+typedef void (VKAPI_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
+typedef void (VKAPI_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
+typedef void (VKAPI_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage);
+typedef void (VKAPI_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView);
+typedef void (VKAPI_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule);
+typedef void (VKAPI_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache);
+typedef void (VKAPI_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData);
+typedef VkResult (VKAPI_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines);
+typedef void (VKAPI_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout);
+typedef void (VKAPI_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler);
+typedef void (VKAPI_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout);
+typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool);
+typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags);
+typedef VkResult (VKAPI_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
+typedef VkResult (VKAPI_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets);
+typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer);
+typedef void (VKAPI_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+typedef void (VKAPI_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool);
+typedef void (VKAPI_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags);
+typedef VkResult (VKAPI_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
+typedef void (VKAPI_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
+typedef VkResult (VKAPI_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer);
+typedef VkResult (VKAPI_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags);
+typedef void (VKAPI_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline);
+typedef void (VKAPI_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports);
+typedef void (VKAPI_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors);
+typedef void (VKAPI_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth);
+typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor);
+typedef void (VKAPI_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants[4]);
+typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds);
+typedef void (VKAPI_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask);
+typedef void (VKAPI_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask);
+typedef void (VKAPI_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference);
+typedef void (VKAPI_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets);
+typedef void (VKAPI_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType);
+typedef void (VKAPI_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets);
+typedef void (VKAPI_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
+typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
+typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);
+typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
+typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
+typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
+typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
+typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
+typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
+typedef void (VKAPI_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects);
+typedef void (VKAPI_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions);
+typedef void (VKAPI_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask);
+typedef void (VKAPI_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask);
+typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers);
+typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers);
+typedef void (VKAPI_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags);
+typedef void (VKAPI_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query);
+typedef void (VKAPI_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
+typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query);
+typedef void (VKAPI_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags);
+typedef void (VKAPI_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues);
+typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents);
+typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents);
+typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer);
+typedef void (VKAPI_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(
+    const VkInstanceCreateInfo*                 pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkInstance*                                 pInstance);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(
+    VkInstance                                  instance,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(
+    VkInstance                                  instance,
+    uint32_t*                                   pPhysicalDeviceCount,
+    VkPhysicalDevice*                           pPhysicalDevices);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceFeatures*                   pFeatures);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkFormatProperties*                         pFormatProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkImageType                                 type,
+    VkImageTiling                               tiling,
+    VkImageUsageFlags                           usage,
+    VkImageCreateFlags                          flags,
+    VkImageFormatProperties*                    pImageFormatProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceProperties*                 pProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties*                    pQueueFamilyProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceMemoryProperties*           pMemoryProperties);
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(
+    VkInstance                                  instance,
+    const char*                                 pName);
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(
+    VkDevice                                    device,
+    const char*                                 pName);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(
+    VkPhysicalDevice                            physicalDevice,
+    const VkDeviceCreateInfo*                   pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDevice*                                   pDevice);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDevice(
+    VkDevice                                    device,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(
+    const char*                                 pLayerName,
+    uint32_t*                                   pPropertyCount,
+    VkExtensionProperties*                      pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties(
+    VkPhysicalDevice                            physicalDevice,
+    const char*                                 pLayerName,
+    uint32_t*                                   pPropertyCount,
+    VkExtensionProperties*                      pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(
+    uint32_t*                                   pPropertyCount,
+    VkLayerProperties*                          pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pPropertyCount,
+    VkLayerProperties*                          pProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue(
+    VkDevice                                    device,
+    uint32_t                                    queueFamilyIndex,
+    uint32_t                                    queueIndex,
+    VkQueue*                                    pQueue);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(
+    VkQueue                                     queue,
+    uint32_t                                    submitCount,
+    const VkSubmitInfo*                         pSubmits,
+    VkFence                                     fence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle(
+    VkQueue                                     queue);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle(
+    VkDevice                                    device);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAllocateMemory(
+    VkDevice                                    device,
+    const VkMemoryAllocateInfo*                 pAllocateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDeviceMemory*                             pMemory);
+
+VKAPI_ATTR void VKAPI_CALL vkFreeMemory(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkDeviceSize                                offset,
+    VkDeviceSize                                size,
+    VkMemoryMapFlags                            flags,
+    void**                                      ppData);
+
+VKAPI_ATTR void VKAPI_CALL vkUnmapMemory(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkFlushMappedMemoryRanges(
+    VkDevice                                    device,
+    uint32_t                                    memoryRangeCount,
+    const VkMappedMemoryRange*                  pMemoryRanges);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkInvalidateMappedMemoryRanges(
+    VkDevice                                    device,
+    uint32_t                                    memoryRangeCount,
+    const VkMappedMemoryRange*                  pMemoryRanges);
+
+VKAPI_ATTR void VKAPI_CALL vkGetDeviceMemoryCommitment(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkDeviceSize*                               pCommittedMemoryInBytes);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory(
+    VkDevice                                    device,
+    VkBuffer                                    buffer,
+    VkDeviceMemory                              memory,
+    VkDeviceSize                                memoryOffset);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory(
+    VkDevice                                    device,
+    VkImage                                     image,
+    VkDeviceMemory                              memory,
+    VkDeviceSize                                memoryOffset);
+
+VKAPI_ATTR void VKAPI_CALL vkGetBufferMemoryRequirements(
+    VkDevice                                    device,
+    VkBuffer                                    buffer,
+    VkMemoryRequirements*                       pMemoryRequirements);
+
+VKAPI_ATTR void VKAPI_CALL vkGetImageMemoryRequirements(
+    VkDevice                                    device,
+    VkImage                                     image,
+    VkMemoryRequirements*                       pMemoryRequirements);
+
+VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements(
+    VkDevice                                    device,
+    VkImage                                     image,
+    uint32_t*                                   pSparseMemoryRequirementCount,
+    VkSparseImageMemoryRequirements*            pSparseMemoryRequirements);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkImageType                                 type,
+    VkSampleCountFlagBits                       samples,
+    VkImageUsageFlags                           usage,
+    VkImageTiling                               tiling,
+    uint32_t*                                   pPropertyCount,
+    VkSparseImageFormatProperties*              pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkQueueBindSparse(
+    VkQueue                                     queue,
+    uint32_t                                    bindInfoCount,
+    const VkBindSparseInfo*                     pBindInfo,
+    VkFence                                     fence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateFence(
+    VkDevice                                    device,
+    const VkFenceCreateInfo*                    pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyFence(
+    VkDevice                                    device,
+    VkFence                                     fence,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkResetFences(
+    VkDevice                                    device,
+    uint32_t                                    fenceCount,
+    const VkFence*                              pFences);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus(
+    VkDevice                                    device,
+    VkFence                                     fence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkWaitForFences(
+    VkDevice                                    device,
+    uint32_t                                    fenceCount,
+    const VkFence*                              pFences,
+    VkBool32                                    waitAll,
+    uint64_t                                    timeout);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore(
+    VkDevice                                    device,
+    const VkSemaphoreCreateInfo*                pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSemaphore*                                pSemaphore);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroySemaphore(
+    VkDevice                                    device,
+    VkSemaphore                                 semaphore,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateEvent(
+    VkDevice                                    device,
+    const VkEventCreateInfo*                    pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkEvent*                                    pEvent);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyEvent(
+    VkDevice                                    device,
+    VkEvent                                     event,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetEventStatus(
+    VkDevice                                    device,
+    VkEvent                                     event);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkSetEvent(
+    VkDevice                                    device,
+    VkEvent                                     event);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkResetEvent(
+    VkDevice                                    device,
+    VkEvent                                     event);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateQueryPool(
+    VkDevice                                    device,
+    const VkQueryPoolCreateInfo*                pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkQueryPool*                                pQueryPool);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyQueryPool(
+    VkDevice                                    device,
+    VkQueryPool                                 queryPool,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetQueryPoolResults(
+    VkDevice                                    device,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    firstQuery,
+    uint32_t                                    queryCount,
+    size_t                                      dataSize,
+    void*                                       pData,
+    VkDeviceSize                                stride,
+    VkQueryResultFlags                          flags);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateBuffer(
+    VkDevice                                    device,
+    const VkBufferCreateInfo*                   pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkBuffer*                                   pBuffer);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyBuffer(
+    VkDevice                                    device,
+    VkBuffer                                    buffer,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferView(
+    VkDevice                                    device,
+    const VkBufferViewCreateInfo*               pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkBufferView*                               pView);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyBufferView(
+    VkDevice                                    device,
+    VkBufferView                                bufferView,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage(
+    VkDevice                                    device,
+    const VkImageCreateInfo*                    pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkImage*                                    pImage);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyImage(
+    VkDevice                                    device,
+    VkImage                                     image,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout(
+    VkDevice                                    device,
+    VkImage                                     image,
+    const VkImageSubresource*                   pSubresource,
+    VkSubresourceLayout*                        pLayout);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView(
+    VkDevice                                    device,
+    const VkImageViewCreateInfo*                pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkImageView*                                pView);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyImageView(
+    VkDevice                                    device,
+    VkImageView                                 imageView,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateShaderModule(
+    VkDevice                                    device,
+    const VkShaderModuleCreateInfo*             pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkShaderModule*                             pShaderModule);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyShaderModule(
+    VkDevice                                    device,
+    VkShaderModule                              shaderModule,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineCache(
+    VkDevice                                    device,
+    const VkPipelineCacheCreateInfo*            pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkPipelineCache*                            pPipelineCache);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineCache(
+    VkDevice                                    device,
+    VkPipelineCache                             pipelineCache,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineCacheData(
+    VkDevice                                    device,
+    VkPipelineCache                             pipelineCache,
+    size_t*                                     pDataSize,
+    void*                                       pData);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkMergePipelineCaches(
+    VkDevice                                    device,
+    VkPipelineCache                             dstCache,
+    uint32_t                                    srcCacheCount,
+    const VkPipelineCache*                      pSrcCaches);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateGraphicsPipelines(
+    VkDevice                                    device,
+    VkPipelineCache                             pipelineCache,
+    uint32_t                                    createInfoCount,
+    const VkGraphicsPipelineCreateInfo*         pCreateInfos,
+    const VkAllocationCallbacks*                pAllocator,
+    VkPipeline*                                 pPipelines);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateComputePipelines(
+    VkDevice                                    device,
+    VkPipelineCache                             pipelineCache,
+    uint32_t                                    createInfoCount,
+    const VkComputePipelineCreateInfo*          pCreateInfos,
+    const VkAllocationCallbacks*                pAllocator,
+    VkPipeline*                                 pPipelines);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyPipeline(
+    VkDevice                                    device,
+    VkPipeline                                  pipeline,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineLayout(
+    VkDevice                                    device,
+    const VkPipelineLayoutCreateInfo*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkPipelineLayout*                           pPipelineLayout);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineLayout(
+    VkDevice                                    device,
+    VkPipelineLayout                            pipelineLayout,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateSampler(
+    VkDevice                                    device,
+    const VkSamplerCreateInfo*                  pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSampler*                                  pSampler);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroySampler(
+    VkDevice                                    device,
+    VkSampler                                   sampler,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorSetLayout(
+    VkDevice                                    device,
+    const VkDescriptorSetLayoutCreateInfo*      pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDescriptorSetLayout*                      pSetLayout);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorSetLayout(
+    VkDevice                                    device,
+    VkDescriptorSetLayout                       descriptorSetLayout,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorPool(
+    VkDevice                                    device,
+    const VkDescriptorPoolCreateInfo*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDescriptorPool*                           pDescriptorPool);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorPool(
+    VkDevice                                    device,
+    VkDescriptorPool                            descriptorPool,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkResetDescriptorPool(
+    VkDevice                                    device,
+    VkDescriptorPool                            descriptorPool,
+    VkDescriptorPoolResetFlags                  flags);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAllocateDescriptorSets(
+    VkDevice                                    device,
+    const VkDescriptorSetAllocateInfo*          pAllocateInfo,
+    VkDescriptorSet*                            pDescriptorSets);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkFreeDescriptorSets(
+    VkDevice                                    device,
+    VkDescriptorPool                            descriptorPool,
+    uint32_t                                    descriptorSetCount,
+    const VkDescriptorSet*                      pDescriptorSets);
+
+VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSets(
+    VkDevice                                    device,
+    uint32_t                                    descriptorWriteCount,
+    const VkWriteDescriptorSet*                 pDescriptorWrites,
+    uint32_t                                    descriptorCopyCount,
+    const VkCopyDescriptorSet*                  pDescriptorCopies);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateFramebuffer(
+    VkDevice                                    device,
+    const VkFramebufferCreateInfo*              pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFramebuffer*                              pFramebuffer);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyFramebuffer(
+    VkDevice                                    device,
+    VkFramebuffer                               framebuffer,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass(
+    VkDevice                                    device,
+    const VkRenderPassCreateInfo*               pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkRenderPass*                               pRenderPass);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyRenderPass(
+    VkDevice                                    device,
+    VkRenderPass                                renderPass,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkGetRenderAreaGranularity(
+    VkDevice                                    device,
+    VkRenderPass                                renderPass,
+    VkExtent2D*                                 pGranularity);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool(
+    VkDevice                                    device,
+    const VkCommandPoolCreateInfo*              pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkCommandPool*                              pCommandPool);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyCommandPool(
+    VkDevice                                    device,
+    VkCommandPool                               commandPool,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandPool(
+    VkDevice                                    device,
+    VkCommandPool                               commandPool,
+    VkCommandPoolResetFlags                     flags);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers(
+    VkDevice                                    device,
+    const VkCommandBufferAllocateInfo*          pAllocateInfo,
+    VkCommandBuffer*                            pCommandBuffers);
+
+VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers(
+    VkDevice                                    device,
+    VkCommandPool                               commandPool,
+    uint32_t                                    commandBufferCount,
+    const VkCommandBuffer*                      pCommandBuffers);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBeginCommandBuffer(
+    VkCommandBuffer                             commandBuffer,
+    const VkCommandBufferBeginInfo*             pBeginInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer(
+    VkCommandBuffer                             commandBuffer);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer(
+    VkCommandBuffer                             commandBuffer,
+    VkCommandBufferResetFlags                   flags);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBindPipeline(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineBindPoint                         pipelineBindPoint,
+    VkPipeline                                  pipeline);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetViewport(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstViewport,
+    uint32_t                                    viewportCount,
+    const VkViewport*                           pViewports);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetScissor(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstScissor,
+    uint32_t                                    scissorCount,
+    const VkRect2D*                             pScissors);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetLineWidth(
+    VkCommandBuffer                             commandBuffer,
+    float                                       lineWidth);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBias(
+    VkCommandBuffer                             commandBuffer,
+    float                                       depthBiasConstantFactor,
+    float                                       depthBiasClamp,
+    float                                       depthBiasSlopeFactor);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetBlendConstants(
+    VkCommandBuffer                             commandBuffer,
+    const float                                 blendConstants[4]);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBounds(
+    VkCommandBuffer                             commandBuffer,
+    float                                       minDepthBounds,
+    float                                       maxDepthBounds);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilCompareMask(
+    VkCommandBuffer                             commandBuffer,
+    VkStencilFaceFlags                          faceMask,
+    uint32_t                                    compareMask);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilWriteMask(
+    VkCommandBuffer                             commandBuffer,
+    VkStencilFaceFlags                          faceMask,
+    uint32_t                                    writeMask);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilReference(
+    VkCommandBuffer                             commandBuffer,
+    VkStencilFaceFlags                          faceMask,
+    uint32_t                                    reference);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBindDescriptorSets(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineBindPoint                         pipelineBindPoint,
+    VkPipelineLayout                            layout,
+    uint32_t                                    firstSet,
+    uint32_t                                    descriptorSetCount,
+    const VkDescriptorSet*                      pDescriptorSets,
+    uint32_t                                    dynamicOffsetCount,
+    const uint32_t*                             pDynamicOffsets);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBindIndexBuffer(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkIndexType                                 indexType);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstBinding,
+    uint32_t                                    bindingCount,
+    const VkBuffer*                             pBuffers,
+    const VkDeviceSize*                         pOffsets);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDraw(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    vertexCount,
+    uint32_t                                    instanceCount,
+    uint32_t                                    firstVertex,
+    uint32_t                                    firstInstance);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexed(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    indexCount,
+    uint32_t                                    instanceCount,
+    uint32_t                                    firstIndex,
+    int32_t                                     vertexOffset,
+    uint32_t                                    firstInstance);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirect(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    uint32_t                                    drawCount,
+    uint32_t                                    stride);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirect(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    uint32_t                                    drawCount,
+    uint32_t                                    stride);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDispatch(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    groupCountX,
+    uint32_t                                    groupCountY,
+    uint32_t                                    groupCountZ);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDispatchIndirect(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    srcBuffer,
+    VkBuffer                                    dstBuffer,
+    uint32_t                                    regionCount,
+    const VkBufferCopy*                         pRegions);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage(
+    VkCommandBuffer                             commandBuffer,
+    VkImage                                     srcImage,
+    VkImageLayout                               srcImageLayout,
+    VkImage                                     dstImage,
+    VkImageLayout                               dstImageLayout,
+    uint32_t                                    regionCount,
+    const VkImageCopy*                          pRegions);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage(
+    VkCommandBuffer                             commandBuffer,
+    VkImage                                     srcImage,
+    VkImageLayout                               srcImageLayout,
+    VkImage                                     dstImage,
+    VkImageLayout                               dstImageLayout,
+    uint32_t                                    regionCount,
+    const VkImageBlit*                          pRegions,
+    VkFilter                                    filter);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    srcBuffer,
+    VkImage                                     dstImage,
+    VkImageLayout                               dstImageLayout,
+    uint32_t                                    regionCount,
+    const VkBufferImageCopy*                    pRegions);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer(
+    VkCommandBuffer                             commandBuffer,
+    VkImage                                     srcImage,
+    VkImageLayout                               srcImageLayout,
+    VkBuffer                                    dstBuffer,
+    uint32_t                                    regionCount,
+    const VkBufferImageCopy*                    pRegions);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdUpdateBuffer(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    dstBuffer,
+    VkDeviceSize                                dstOffset,
+    VkDeviceSize                                dataSize,
+    const void*                                 pData);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    dstBuffer,
+    VkDeviceSize                                dstOffset,
+    VkDeviceSize                                size,
+    uint32_t                                    data);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage(
+    VkCommandBuffer                             commandBuffer,
+    VkImage                                     image,
+    VkImageLayout                               imageLayout,
+    const VkClearColorValue*                    pColor,
+    uint32_t                                    rangeCount,
+    const VkImageSubresourceRange*              pRanges);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdClearDepthStencilImage(
+    VkCommandBuffer                             commandBuffer,
+    VkImage                                     image,
+    VkImageLayout                               imageLayout,
+    const VkClearDepthStencilValue*             pDepthStencil,
+    uint32_t                                    rangeCount,
+    const VkImageSubresourceRange*              pRanges);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdClearAttachments(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    attachmentCount,
+    const VkClearAttachment*                    pAttachments,
+    uint32_t                                    rectCount,
+    const VkClearRect*                          pRects);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage(
+    VkCommandBuffer                             commandBuffer,
+    VkImage                                     srcImage,
+    VkImageLayout                               srcImageLayout,
+    VkImage                                     dstImage,
+    VkImageLayout                               dstImageLayout,
+    uint32_t                                    regionCount,
+    const VkImageResolve*                       pRegions);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent(
+    VkCommandBuffer                             commandBuffer,
+    VkEvent                                     event,
+    VkPipelineStageFlags                        stageMask);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent(
+    VkCommandBuffer                             commandBuffer,
+    VkEvent                                     event,
+    VkPipelineStageFlags                        stageMask);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    eventCount,
+    const VkEvent*                              pEvents,
+    VkPipelineStageFlags                        srcStageMask,
+    VkPipelineStageFlags                        dstStageMask,
+    uint32_t                                    memoryBarrierCount,
+    const VkMemoryBarrier*                      pMemoryBarriers,
+    uint32_t                                    bufferMemoryBarrierCount,
+    const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
+    uint32_t                                    imageMemoryBarrierCount,
+    const VkImageMemoryBarrier*                 pImageMemoryBarriers);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineStageFlags                        srcStageMask,
+    VkPipelineStageFlags                        dstStageMask,
+    VkDependencyFlags                           dependencyFlags,
+    uint32_t                                    memoryBarrierCount,
+    const VkMemoryBarrier*                      pMemoryBarriers,
+    uint32_t                                    bufferMemoryBarrierCount,
+    const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
+    uint32_t                                    imageMemoryBarrierCount,
+    const VkImageMemoryBarrier*                 pImageMemoryBarriers);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBeginQuery(
+    VkCommandBuffer                             commandBuffer,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    query,
+    VkQueryControlFlags                         flags);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdEndQuery(
+    VkCommandBuffer                             commandBuffer,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    query);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdResetQueryPool(
+    VkCommandBuffer                             commandBuffer,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    firstQuery,
+    uint32_t                                    queryCount);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineStageFlagBits                     pipelineStage,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    query);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdCopyQueryPoolResults(
+    VkCommandBuffer                             commandBuffer,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    firstQuery,
+    uint32_t                                    queryCount,
+    VkBuffer                                    dstBuffer,
+    VkDeviceSize                                dstOffset,
+    VkDeviceSize                                stride,
+    VkQueryResultFlags                          flags);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineLayout                            layout,
+    VkShaderStageFlags                          stageFlags,
+    uint32_t                                    offset,
+    uint32_t                                    size,
+    const void*                                 pValues);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass(
+    VkCommandBuffer                             commandBuffer,
+    const VkRenderPassBeginInfo*                pRenderPassBegin,
+    VkSubpassContents                           contents);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass(
+    VkCommandBuffer                             commandBuffer,
+    VkSubpassContents                           contents);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass(
+    VkCommandBuffer                             commandBuffer);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdExecuteCommands(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    commandBufferCount,
+    const VkCommandBuffer*                      pCommandBuffers);
+#endif
+
+#define VK_KHR_surface 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR)
+
+#define VK_KHR_SURFACE_SPEC_VERSION       25
+#define VK_KHR_SURFACE_EXTENSION_NAME     "VK_KHR_surface"
+#define VK_COLORSPACE_SRGB_NONLINEAR_KHR  VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
+
+
+typedef enum VkColorSpaceKHR {
+    VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0,
+    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001,
+    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002,
+    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104003,
+    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004,
+    VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005,
+    VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006,
+    VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104007,
+    VK_COLOR_SPACE_HDR10_ST2084_EXT = 1000104008,
+    VK_COLOR_SPACE_DOLBYVISION_EXT = 1000104009,
+    VK_COLOR_SPACE_HDR10_HLG_EXT = 1000104010,
+    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011,
+    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012,
+    VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013,
+    VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+    VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+    VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1),
+    VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkColorSpaceKHR;
+
+typedef enum VkPresentModeKHR {
+    VK_PRESENT_MODE_IMMEDIATE_KHR = 0,
+    VK_PRESENT_MODE_MAILBOX_KHR = 1,
+    VK_PRESENT_MODE_FIFO_KHR = 2,
+    VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3,
+    VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR,
+    VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR,
+    VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1),
+    VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkPresentModeKHR;
+
+
+typedef enum VkSurfaceTransformFlagBitsKHR {
+    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001,
+    VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002,
+    VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 0x00000004,
+    VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 0x00000008,
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 0x00000010,
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 0x00000020,
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040,
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080,
+    VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100,
+    VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSurfaceTransformFlagBitsKHR;
+typedef VkFlags VkSurfaceTransformFlagsKHR;
+
+typedef enum VkCompositeAlphaFlagBitsKHR {
+    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001,
+    VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002,
+    VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004,
+    VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008,
+    VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkCompositeAlphaFlagBitsKHR;
+typedef VkFlags VkCompositeAlphaFlagsKHR;
+
+typedef struct VkSurfaceCapabilitiesKHR {
+    uint32_t                         minImageCount;
+    uint32_t                         maxImageCount;
+    VkExtent2D                       currentExtent;
+    VkExtent2D                       minImageExtent;
+    VkExtent2D                       maxImageExtent;
+    uint32_t                         maxImageArrayLayers;
+    VkSurfaceTransformFlagsKHR       supportedTransforms;
+    VkSurfaceTransformFlagBitsKHR    currentTransform;
+    VkCompositeAlphaFlagsKHR         supportedCompositeAlpha;
+    VkImageUsageFlags                supportedUsageFlags;
+} VkSurfaceCapabilitiesKHR;
+
+typedef struct VkSurfaceFormatKHR {
+    VkFormat           format;
+    VkColorSpaceKHR    colorSpace;
+} VkSurfaceFormatKHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR(
+    VkInstance                                  instance,
+    VkSurfaceKHR                                surface,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceSupportKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    queueFamilyIndex,
+    VkSurfaceKHR                                surface,
+    VkBool32*                                   pSupported);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    VkSurfaceCapabilitiesKHR*                   pSurfaceCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormatsKHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    uint32_t*                                   pSurfaceFormatCount,
+    VkSurfaceFormatKHR*                         pSurfaceFormats);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModesKHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    uint32_t*                                   pPresentModeCount,
+    VkPresentModeKHR*                           pPresentModes);
+#endif
+
+#define VK_KHR_swapchain 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR)
+
+#define VK_KHR_SWAPCHAIN_SPEC_VERSION     68
+#define VK_KHR_SWAPCHAIN_EXTENSION_NAME   "VK_KHR_swapchain"
+
+
+typedef enum VkSwapchainCreateFlagBitsKHR {
+    VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX = 0x00000001,
+    VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSwapchainCreateFlagBitsKHR;
+typedef VkFlags VkSwapchainCreateFlagsKHR;
+
+typedef struct VkSwapchainCreateInfoKHR {
+    VkStructureType                  sType;
+    const void*                      pNext;
+    VkSwapchainCreateFlagsKHR        flags;
+    VkSurfaceKHR                     surface;
+    uint32_t                         minImageCount;
+    VkFormat                         imageFormat;
+    VkColorSpaceKHR                  imageColorSpace;
+    VkExtent2D                       imageExtent;
+    uint32_t                         imageArrayLayers;
+    VkImageUsageFlags                imageUsage;
+    VkSharingMode                    imageSharingMode;
+    uint32_t                         queueFamilyIndexCount;
+    const uint32_t*                  pQueueFamilyIndices;
+    VkSurfaceTransformFlagBitsKHR    preTransform;
+    VkCompositeAlphaFlagBitsKHR      compositeAlpha;
+    VkPresentModeKHR                 presentMode;
+    VkBool32                         clipped;
+    VkSwapchainKHR                   oldSwapchain;
+} VkSwapchainCreateInfoKHR;
+
+typedef struct VkPresentInfoKHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 waitSemaphoreCount;
+    const VkSemaphore*       pWaitSemaphores;
+    uint32_t                 swapchainCount;
+    const VkSwapchainKHR*    pSwapchains;
+    const uint32_t*          pImageIndices;
+    VkResult*                pResults;
+} VkPresentInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain);
+typedef void (VKAPI_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages);
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex);
+typedef VkResult (VKAPI_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR* pPresentInfo);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR(
+    VkDevice                                    device,
+    const VkSwapchainCreateInfoKHR*             pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSwapchainKHR*                             pSwapchain);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroySwapchainKHR(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint32_t*                                   pSwapchainImageCount,
+    VkImage*                                    pSwapchainImages);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint64_t                                    timeout,
+    VkSemaphore                                 semaphore,
+    VkFence                                     fence,
+    uint32_t*                                   pImageIndex);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR(
+    VkQueue                                     queue,
+    const VkPresentInfoKHR*                     pPresentInfo);
+#endif
+
+#define VK_KHR_display 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayKHR)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayModeKHR)
+
+#define VK_KHR_DISPLAY_SPEC_VERSION       21
+#define VK_KHR_DISPLAY_EXTENSION_NAME     "VK_KHR_display"
+
+
+typedef enum VkDisplayPlaneAlphaFlagBitsKHR {
+    VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR = 0x00000001,
+    VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR = 0x00000002,
+    VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR = 0x00000004,
+    VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR = 0x00000008,
+    VK_DISPLAY_PLANE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkDisplayPlaneAlphaFlagBitsKHR;
+typedef VkFlags VkDisplayPlaneAlphaFlagsKHR;
+typedef VkFlags VkDisplayModeCreateFlagsKHR;
+typedef VkFlags VkDisplaySurfaceCreateFlagsKHR;
+
+typedef struct VkDisplayPropertiesKHR {
+    VkDisplayKHR                  display;
+    const char*                   displayName;
+    VkExtent2D                    physicalDimensions;
+    VkExtent2D                    physicalResolution;
+    VkSurfaceTransformFlagsKHR    supportedTransforms;
+    VkBool32                      planeReorderPossible;
+    VkBool32                      persistentContent;
+} VkDisplayPropertiesKHR;
+
+typedef struct VkDisplayModeParametersKHR {
+    VkExtent2D    visibleRegion;
+    uint32_t      refreshRate;
+} VkDisplayModeParametersKHR;
+
+typedef struct VkDisplayModePropertiesKHR {
+    VkDisplayModeKHR              displayMode;
+    VkDisplayModeParametersKHR    parameters;
+} VkDisplayModePropertiesKHR;
+
+typedef struct VkDisplayModeCreateInfoKHR {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkDisplayModeCreateFlagsKHR    flags;
+    VkDisplayModeParametersKHR     parameters;
+} VkDisplayModeCreateInfoKHR;
+
+typedef struct VkDisplayPlaneCapabilitiesKHR {
+    VkDisplayPlaneAlphaFlagsKHR    supportedAlpha;
+    VkOffset2D                     minSrcPosition;
+    VkOffset2D                     maxSrcPosition;
+    VkExtent2D                     minSrcExtent;
+    VkExtent2D                     maxSrcExtent;
+    VkOffset2D                     minDstPosition;
+    VkOffset2D                     maxDstPosition;
+    VkExtent2D                     minDstExtent;
+    VkExtent2D                     maxDstExtent;
+} VkDisplayPlaneCapabilitiesKHR;
+
+typedef struct VkDisplayPlanePropertiesKHR {
+    VkDisplayKHR    currentDisplay;
+    uint32_t        currentStackIndex;
+} VkDisplayPlanePropertiesKHR;
+
+typedef struct VkDisplaySurfaceCreateInfoKHR {
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkDisplaySurfaceCreateFlagsKHR    flags;
+    VkDisplayModeKHR                  displayMode;
+    uint32_t                          planeIndex;
+    uint32_t                          planeStackIndex;
+    VkSurfaceTransformFlagBitsKHR     transform;
+    float                             globalAlpha;
+    VkDisplayPlaneAlphaFlagBitsKHR    alphaMode;
+    VkExtent2D                        imageExtent;
+} VkDisplaySurfaceCreateInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPropertiesKHR* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneSupportedDisplaysKHR)(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModePropertiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModePropertiesKHR* pProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR* pCapabilities);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayPlaneSurfaceKHR)(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pPropertyCount,
+    VkDisplayPropertiesKHR*                     pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlanePropertiesKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pPropertyCount,
+    VkDisplayPlanePropertiesKHR*                pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneSupportedDisplaysKHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t                                    planeIndex,
+    uint32_t*                                   pDisplayCount,
+    VkDisplayKHR*                               pDisplays);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModePropertiesKHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkDisplayKHR                                display,
+    uint32_t*                                   pPropertyCount,
+    VkDisplayModePropertiesKHR*                 pProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayModeKHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkDisplayKHR                                display,
+    const VkDisplayModeCreateInfoKHR*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDisplayModeKHR*                           pMode);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilitiesKHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkDisplayModeKHR                            mode,
+    uint32_t                                    planeIndex,
+    VkDisplayPlaneCapabilitiesKHR*              pCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR(
+    VkInstance                                  instance,
+    const VkDisplaySurfaceCreateInfoKHR*        pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+
+#define VK_KHR_display_swapchain 1
+#define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 9
+#define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain"
+
+typedef struct VkDisplayPresentInfoKHR {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkRect2D           srcRect;
+    VkRect2D           dstRect;
+    VkBool32           persistent;
+} VkDisplayPresentInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateSharedSwapchainsKHR)(VkDevice device, uint32_t swapchainCount, const VkSwapchainCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateSharedSwapchainsKHR(
+    VkDevice                                    device,
+    uint32_t                                    swapchainCount,
+    const VkSwapchainCreateInfoKHR*             pCreateInfos,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSwapchainKHR*                             pSwapchains);
+#endif
+
+#define VK_KHR_sampler_mirror_clamp_to_edge 1
+#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1
+#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge"
+
+
+#define VK_KHR_get_physical_device_properties2 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+typedef struct VkPhysicalDeviceFeatures2KHR {
+    VkStructureType             sType;
+    void*                       pNext;
+    VkPhysicalDeviceFeatures    features;
+} VkPhysicalDeviceFeatures2KHR;
+
+typedef struct VkPhysicalDeviceProperties2KHR {
+    VkStructureType               sType;
+    void*                         pNext;
+    VkPhysicalDeviceProperties    properties;
+} VkPhysicalDeviceProperties2KHR;
+
+typedef struct VkFormatProperties2KHR {
+    VkStructureType       sType;
+    void*                 pNext;
+    VkFormatProperties    formatProperties;
+} VkFormatProperties2KHR;
+
+typedef struct VkImageFormatProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkImageFormatProperties    imageFormatProperties;
+} VkImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceImageFormatInfo2KHR {
+    VkStructureType       sType;
+    const void*           pNext;
+    VkFormat              format;
+    VkImageType           type;
+    VkImageTiling         tiling;
+    VkImageUsageFlags     usage;
+    VkImageCreateFlags    flags;
+} VkPhysicalDeviceImageFormatInfo2KHR;
+
+typedef struct VkQueueFamilyProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkQueueFamilyProperties    queueFamilyProperties;
+} VkQueueFamilyProperties2KHR;
+
+typedef struct VkPhysicalDeviceMemoryProperties2KHR {
+    VkStructureType                     sType;
+    void*                               pNext;
+    VkPhysicalDeviceMemoryProperties    memoryProperties;
+} VkPhysicalDeviceMemoryProperties2KHR;
+
+typedef struct VkSparseImageFormatProperties2KHR {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkSparseImageFormatProperties    properties;
+} VkSparseImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkFormat                 format;
+    VkImageType              type;
+    VkSampleCountFlagBits    samples;
+    VkImageUsageFlags        usage;
+    VkImageTiling            tiling;
+} VkPhysicalDeviceSparseImageFormatInfo2KHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceFeatures2KHR*               pFeatures);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceProperties2KHR*             pProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkFormatProperties2KHR*                     pFormatProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
+    VkImageFormatProperties2KHR*                pImageFormatProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties2KHR*                pQueueFamilyProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
+    uint32_t*                                   pPropertyCount,
+    VkSparseImageFormatProperties2KHR*          pProperties);
+#endif
+
+#define VK_KHR_shader_draw_parameters 1
+#define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1
+#define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters"
+
+
+#define VK_KHR_maintenance1 1
+#define VK_KHR_MAINTENANCE1_SPEC_VERSION  1
+#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1"
+
+typedef VkFlags VkCommandPoolTrimFlagsKHR;
+
+typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlagsKHR flags);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR(
+    VkDevice                                    device,
+    VkCommandPool                               commandPool,
+    VkCommandPoolTrimFlagsKHR                   flags);
+#endif
+
+#define VK_KHR_push_descriptor 1
+#define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 1
+#define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor"
+
+typedef struct VkPhysicalDevicePushDescriptorPropertiesKHR {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxPushDescriptors;
+} VkPhysicalDevicePushDescriptorPropertiesKHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetKHR)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetKHR(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineBindPoint                         pipelineBindPoint,
+    VkPipelineLayout                            layout,
+    uint32_t                                    set,
+    uint32_t                                    descriptorWriteCount,
+    const VkWriteDescriptorSet*                 pDescriptorWrites);
+#endif
+
+#define VK_KHR_incremental_present 1
+#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+
+typedef struct VkRectLayerKHR {
+    VkOffset2D    offset;
+    VkExtent2D    extent;
+    uint32_t      layer;
+} VkRectLayerKHR;
+
+typedef struct VkPresentRegionKHR {
+    uint32_t                 rectangleCount;
+    const VkRectLayerKHR*    pRectangles;
+} VkPresentRegionKHR;
+
+typedef struct VkPresentRegionsKHR {
+    VkStructureType              sType;
+    const void*                  pNext;
+    uint32_t                     swapchainCount;
+    const VkPresentRegionKHR*    pRegions;
+} VkPresentRegionsKHR;
+
+
+
+#define VK_KHR_descriptor_update_template 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplateKHR)
+
+#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1
+#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
+
+
+typedef enum VkDescriptorUpdateTemplateTypeKHR {
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = 0,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_BEGIN_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_END_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_RANGE_SIZE_KHR = (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR + 1),
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkDescriptorUpdateTemplateTypeKHR;
+
+typedef VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR;
+
+typedef struct VkDescriptorUpdateTemplateEntryKHR {
+    uint32_t            dstBinding;
+    uint32_t            dstArrayElement;
+    uint32_t            descriptorCount;
+    VkDescriptorType    descriptorType;
+    size_t              offset;
+    size_t              stride;
+} VkDescriptorUpdateTemplateEntryKHR;
+
+typedef struct VkDescriptorUpdateTemplateCreateInfoKHR {
+    VkStructureType                              sType;
+    void*                                        pNext;
+    VkDescriptorUpdateTemplateCreateFlagsKHR     flags;
+    uint32_t                                     descriptorUpdateEntryCount;
+    const VkDescriptorUpdateTemplateEntryKHR*    pDescriptorUpdateEntries;
+    VkDescriptorUpdateTemplateTypeKHR            templateType;
+    VkDescriptorSetLayout                        descriptorSetLayout;
+    VkPipelineBindPoint                          pipelineBindPoint;
+    VkPipelineLayout                             pipelineLayout;
+    uint32_t                                     set;
+} VkDescriptorUpdateTemplateCreateInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplateKHR)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate);
+typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplateKHR)(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplateKHR)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetWithTemplateKHR)(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplateKHR(
+    VkDevice                                    device,
+    const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDescriptorUpdateTemplateKHR*              pDescriptorUpdateTemplate);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorUpdateTemplateKHR(
+    VkDevice                                    device,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplateKHR(
+    VkDevice                                    device,
+    VkDescriptorSet                             descriptorSet,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    const void*                                 pData);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplateKHR(
+    VkCommandBuffer                             commandBuffer,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    VkPipelineLayout                            layout,
+    uint32_t                                    set,
+    const void*                                 pData);
+#endif
+
+#define VK_EXT_debug_report 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
+
+#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  6
+#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
+#define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT
+
+
+typedef enum VkDebugReportObjectTypeEXT {
+    VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3,
+    VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5,
+    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6,
+    VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8,
+    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9,
+    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10,
+    VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11,
+    VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12,
+    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13,
+    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17,
+    VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23,
+    VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24,
+    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
+    VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
+    VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT,
+    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
+    VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDebugReportObjectTypeEXT;
+
+typedef enum VkDebugReportErrorEXT {
+    VK_DEBUG_REPORT_ERROR_NONE_EXT = 0,
+    VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1,
+    VK_DEBUG_REPORT_ERROR_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_ERROR_NONE_EXT,
+    VK_DEBUG_REPORT_ERROR_END_RANGE_EXT = VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT,
+    VK_DEBUG_REPORT_ERROR_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT - VK_DEBUG_REPORT_ERROR_NONE_EXT + 1),
+    VK_DEBUG_REPORT_ERROR_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDebugReportErrorEXT;
+
+
+typedef enum VkDebugReportFlagBitsEXT {
+    VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001,
+    VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002,
+    VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004,
+    VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008,
+    VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010,
+    VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDebugReportFlagBitsEXT;
+typedef VkFlags VkDebugReportFlagsEXT;
+
+typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)(
+    VkDebugReportFlagsEXT                       flags,
+    VkDebugReportObjectTypeEXT                  objectType,
+    uint64_t                                    object,
+    size_t                                      location,
+    int32_t                                     messageCode,
+    const char*                                 pLayerPrefix,
+    const char*                                 pMessage,
+    void*                                       pUserData);
+
+
+typedef struct VkDebugReportCallbackCreateInfoEXT {
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkDebugReportFlagsEXT           flags;
+    PFN_vkDebugReportCallbackEXT    pfnCallback;
+    void*                           pUserData;
+} VkDebugReportCallbackCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
+typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT(
+    VkInstance                                  instance,
+    const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDebugReportCallbackEXT*                   pCallback);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT(
+    VkInstance                                  instance,
+    VkDebugReportCallbackEXT                    callback,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT(
+    VkInstance                                  instance,
+    VkDebugReportFlagsEXT                       flags,
+    VkDebugReportObjectTypeEXT                  objectType,
+    uint64_t                                    object,
+    size_t                                      location,
+    int32_t                                     messageCode,
+    const char*                                 pLayerPrefix,
+    const char*                                 pMessage);
+#endif
+
+#define VK_NV_glsl_shader 1
+#define VK_NV_GLSL_SHADER_SPEC_VERSION    1
+#define VK_NV_GLSL_SHADER_EXTENSION_NAME  "VK_NV_glsl_shader"
+
+
+#define VK_IMG_filter_cubic 1
+#define VK_IMG_FILTER_CUBIC_SPEC_VERSION  1
+#define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic"
+
+
+#define VK_AMD_rasterization_order 1
+#define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1
+#define VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME "VK_AMD_rasterization_order"
+
+
+typedef enum VkRasterizationOrderAMD {
+    VK_RASTERIZATION_ORDER_STRICT_AMD = 0,
+    VK_RASTERIZATION_ORDER_RELAXED_AMD = 1,
+    VK_RASTERIZATION_ORDER_BEGIN_RANGE_AMD = VK_RASTERIZATION_ORDER_STRICT_AMD,
+    VK_RASTERIZATION_ORDER_END_RANGE_AMD = VK_RASTERIZATION_ORDER_RELAXED_AMD,
+    VK_RASTERIZATION_ORDER_RANGE_SIZE_AMD = (VK_RASTERIZATION_ORDER_RELAXED_AMD - VK_RASTERIZATION_ORDER_STRICT_AMD + 1),
+    VK_RASTERIZATION_ORDER_MAX_ENUM_AMD = 0x7FFFFFFF
+} VkRasterizationOrderAMD;
+
+typedef struct VkPipelineRasterizationStateRasterizationOrderAMD {
+    VkStructureType            sType;
+    const void*                pNext;
+    VkRasterizationOrderAMD    rasterizationOrder;
+} VkPipelineRasterizationStateRasterizationOrderAMD;
+
+
+
+#define VK_AMD_shader_trinary_minmax 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+
+#define VK_AMD_shader_explicit_vertex_parameter 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
+
+#define VK_EXT_debug_marker 1
+#define VK_EXT_DEBUG_MARKER_SPEC_VERSION  4
+#define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker"
+
+typedef struct VkDebugMarkerObjectNameInfoEXT {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkDebugReportObjectTypeEXT    objectType;
+    uint64_t                      object;
+    const char*                   pObjectName;
+} VkDebugMarkerObjectNameInfoEXT;
+
+typedef struct VkDebugMarkerObjectTagInfoEXT {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkDebugReportObjectTypeEXT    objectType;
+    uint64_t                      object;
+    uint64_t                      tagName;
+    size_t                        tagSize;
+    const void*                   pTag;
+} VkDebugMarkerObjectTagInfoEXT;
+
+typedef struct VkDebugMarkerMarkerInfoEXT {
+    VkStructureType    sType;
+    const void*        pNext;
+    const char*        pMarkerName;
+    float              color[4];
+} VkDebugMarkerMarkerInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(
+    VkDevice                                    device,
+    VkDebugMarkerObjectTagInfoEXT*              pTagInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(
+    VkDevice                                    device,
+    VkDebugMarkerObjectNameInfoEXT*             pNameInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(
+    VkCommandBuffer                             commandBuffer,
+    VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(
+    VkCommandBuffer                             commandBuffer);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(
+    VkCommandBuffer                             commandBuffer,
+    VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo);
+#endif
+
+#define VK_AMD_gcn_shader 1
+#define VK_AMD_GCN_SHADER_SPEC_VERSION    1
+#define VK_AMD_GCN_SHADER_EXTENSION_NAME  "VK_AMD_gcn_shader"
+
+
+#define VK_NV_dedicated_allocation 1
+#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
+typedef struct VkDedicatedAllocationImageCreateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           dedicatedAllocation;
+} VkDedicatedAllocationImageCreateInfoNV;
+
+typedef struct VkDedicatedAllocationBufferCreateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           dedicatedAllocation;
+} VkDedicatedAllocationBufferCreateInfoNV;
+
+typedef struct VkDedicatedAllocationMemoryAllocateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkBuffer           buffer;
+} VkDedicatedAllocationMemoryAllocateInfoNV;
+
+
+
+#define VK_AMD_draw_indirect_count 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkBuffer                                    countBuffer,
+    VkDeviceSize                                countBufferOffset,
+    uint32_t                                    maxDrawCount,
+    uint32_t                                    stride);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkBuffer                                    countBuffer,
+    VkDeviceSize                                countBufferOffset,
+    uint32_t                                    maxDrawCount,
+    uint32_t                                    stride);
+#endif
+
+#define VK_AMD_negative_viewport_height 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+
+#define VK_AMD_gpu_shader_half_float 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+
+#define VK_AMD_shader_ballot 1
+#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+
+#define VK_KHX_multiview 1
+#define VK_KHX_MULTIVIEW_SPEC_VERSION     1
+#define VK_KHX_MULTIVIEW_EXTENSION_NAME   "VK_KHX_multiview"
+
+typedef struct VkRenderPassMultiviewCreateInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           subpassCount;
+    const uint32_t*    pViewMasks;
+    uint32_t           dependencyCount;
+    const int32_t*     pViewOffsets;
+    uint32_t           correlationMaskCount;
+    const uint32_t*    pCorrelationMasks;
+} VkRenderPassMultiviewCreateInfoKHX;
+
+typedef struct VkPhysicalDeviceMultiviewFeaturesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           multiview;
+    VkBool32           multiviewGeometryShader;
+    VkBool32           multiviewTessellationShader;
+} VkPhysicalDeviceMultiviewFeaturesKHX;
+
+typedef struct VkPhysicalDeviceMultiviewPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxMultiviewViewCount;
+    uint32_t           maxMultiviewInstanceIndex;
+} VkPhysicalDeviceMultiviewPropertiesKHX;
+
+
+
+#define VK_IMG_format_pvrtc 1
+#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION  1
+#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+
+#define VK_NV_external_memory_capabilities 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsNV {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsNV;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsNV;
+
+typedef enum VkExternalMemoryFeatureFlagBitsNV {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsNV;
+typedef VkFlags VkExternalMemoryFeatureFlagsNV;
+
+typedef struct VkExternalImageFormatPropertiesNV {
+    VkImageFormatProperties              imageFormatProperties;
+    VkExternalMemoryFeatureFlagsNV       externalMemoryFeatures;
+    VkExternalMemoryHandleTypeFlagsNV    exportFromImportedHandleTypes;
+    VkExternalMemoryHandleTypeFlagsNV    compatibleHandleTypes;
+} VkExternalImageFormatPropertiesNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkImageType                                 type,
+    VkImageTiling                               tiling,
+    VkImageUsageFlags                           usage,
+    VkImageCreateFlags                          flags,
+    VkExternalMemoryHandleTypeFlagsNV           externalHandleType,
+    VkExternalImageFormatPropertiesNV*          pExternalImageFormatProperties);
+#endif
+
+#define VK_NV_external_memory 1
+#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+typedef struct VkExternalMemoryImageCreateInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleTypes;
+} VkExternalMemoryImageCreateInfoNV;
+
+typedef struct VkExportMemoryAllocateInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleTypes;
+} VkExportMemoryAllocateInfoNV;
+
+
+
+#define VK_KHX_device_group 1
+#define VK_MAX_DEVICE_GROUP_SIZE_KHX      32
+#define VK_KHX_DEVICE_GROUP_SPEC_VERSION  1
+#define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+
+
+typedef enum VkPeerMemoryFeatureFlagBitsKHX {
+    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX = 0x00000001,
+    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX = 0x00000002,
+    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX = 0x00000004,
+    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX = 0x00000008,
+    VK_PEER_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkPeerMemoryFeatureFlagBitsKHX;
+typedef VkFlags VkPeerMemoryFeatureFlagsKHX;
+
+typedef enum VkMemoryAllocateFlagBitsKHX {
+    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX = 0x00000001,
+    VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkMemoryAllocateFlagBitsKHX;
+typedef VkFlags VkMemoryAllocateFlagsKHX;
+
+typedef enum VkDeviceGroupPresentModeFlagBitsKHX {
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX = 0x00000001,
+    VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX = 0x00000002,
+    VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX = 0x00000004,
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX = 0x00000008,
+    VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkDeviceGroupPresentModeFlagBitsKHX;
+typedef VkFlags VkDeviceGroupPresentModeFlagsKHX;
+
+typedef struct VkMemoryAllocateFlagsInfoKHX {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkMemoryAllocateFlagsKHX    flags;
+    uint32_t                    deviceMask;
+} VkMemoryAllocateFlagsInfoKHX;
+
+typedef struct VkBindBufferMemoryInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBuffer           buffer;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+} VkBindBufferMemoryInfoKHX;
+
+typedef struct VkBindImageMemoryInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+    uint32_t           SFRRectCount;
+    const VkRect2D*    pSFRRects;
+} VkBindImageMemoryInfoKHX;
+
+typedef struct VkDeviceGroupRenderPassBeginInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceMask;
+    uint32_t           deviceRenderAreaCount;
+    const VkRect2D*    pDeviceRenderAreas;
+} VkDeviceGroupRenderPassBeginInfoKHX;
+
+typedef struct VkDeviceGroupCommandBufferBeginInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceMask;
+} VkDeviceGroupCommandBufferBeginInfoKHX;
+
+typedef struct VkDeviceGroupSubmitInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           waitSemaphoreCount;
+    const uint32_t*    pWaitSemaphoreDeviceIndices;
+    uint32_t           commandBufferCount;
+    const uint32_t*    pCommandBufferDeviceMasks;
+    uint32_t           signalSemaphoreCount;
+    const uint32_t*    pSignalSemaphoreDeviceIndices;
+} VkDeviceGroupSubmitInfoKHX;
+
+typedef struct VkDeviceGroupBindSparseInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           resourceDeviceIndex;
+    uint32_t           memoryDeviceIndex;
+} VkDeviceGroupBindSparseInfoKHX;
+
+typedef struct VkDeviceGroupPresentCapabilitiesKHX {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    uint32_t                            presentMask[VK_MAX_DEVICE_GROUP_SIZE_KHX];
+    VkDeviceGroupPresentModeFlagsKHX    modes;
+} VkDeviceGroupPresentCapabilitiesKHX;
+
+typedef struct VkImageSwapchainCreateInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+} VkImageSwapchainCreateInfoKHX;
+
+typedef struct VkBindImageMemorySwapchainInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+    uint32_t           imageIndex;
+} VkBindImageMemorySwapchainInfoKHX;
+
+typedef struct VkAcquireNextImageInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+    uint64_t           timeout;
+    VkSemaphore        semaphore;
+    VkFence            fence;
+    uint32_t           deviceMask;
+} VkAcquireNextImageInfoKHX;
+
+typedef struct VkDeviceGroupPresentInfoKHX {
+    VkStructureType                        sType;
+    const void*                            pNext;
+    uint32_t                               swapchainCount;
+    const uint32_t*                        pDeviceMasks;
+    VkDeviceGroupPresentModeFlagBitsKHX    mode;
+} VkDeviceGroupPresentInfoKHX;
+
+typedef struct VkDeviceGroupSwapchainCreateInfoKHX {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    VkDeviceGroupPresentModeFlagsKHX    modes;
+} VkDeviceGroupSwapchainCreateInfoKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures);
+typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos);
+typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos);
+typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes);
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX(
+    VkDevice                                    device,
+    uint32_t                                    heapIndex,
+    uint32_t                                    localDeviceIndex,
+    uint32_t                                    remoteDeviceIndex,
+    VkPeerMemoryFeatureFlagsKHX*                pPeerMemoryFeatures);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindBufferMemoryInfoKHX*            pBindInfos);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindImageMemoryInfoKHX*             pBindInfos);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    deviceMask);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
+    VkDevice                                    device,
+    VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
+    VkDevice                                    device,
+    VkSurfaceKHR                                surface,
+    VkDeviceGroupPresentModeFlagsKHX*           pModes);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
+    VkDevice                                    device,
+    const VkAcquireNextImageInfoKHX*            pAcquireInfo,
+    uint32_t*                                   pImageIndex);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    baseGroupX,
+    uint32_t                                    baseGroupY,
+    uint32_t                                    baseGroupZ,
+    uint32_t                                    groupCountX,
+    uint32_t                                    groupCountY,
+    uint32_t                                    groupCountZ);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    uint32_t*                                   pRectCount,
+    VkRect2D*                                   pRects);
+#endif
+
+#define VK_EXT_validation_flags 1
+#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+
+typedef enum VkValidationCheckEXT {
+    VK_VALIDATION_CHECK_ALL_EXT = 0,
+    VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+    VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+    VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1),
+    VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkValidationCheckEXT;
+
+typedef struct VkValidationFlagsEXT {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 disabledValidationCheckCount;
+    VkValidationCheckEXT*    pDisabledValidationChecks;
+} VkValidationFlagsEXT;
+
+
+#define VK_EXT_shader_subgroup_ballot 1
+#define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1
+#define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
+
+
+#define VK_EXT_shader_subgroup_vote 1
+#define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1
+#define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote"
+
+
+#define VK_KHX_device_group_creation 1
+#define VK_KHX_DEVICE_GROUP_CREATION_SPEC_VERSION 1
+#define VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHX_device_group_creation"
+
+typedef struct VkPhysicalDeviceGroupPropertiesKHX {
+    VkStructureType     sType;
+    void*               pNext;
+    uint32_t            physicalDeviceCount;
+    VkPhysicalDevice    physicalDevices[VK_MAX_DEVICE_GROUP_SIZE_KHX];
+    VkBool32            subsetAllocation;
+} VkPhysicalDeviceGroupPropertiesKHX;
+
+typedef struct VkDeviceGroupDeviceCreateInfoKHX {
+    VkStructureType            sType;
+    const void*                pNext;
+    uint32_t                   physicalDeviceCount;
+    const VkPhysicalDevice*    pPhysicalDevices;
+} VkDeviceGroupDeviceCreateInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroupsKHX)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHX(
+    VkInstance                                  instance,
+    uint32_t*                                   pPhysicalDeviceGroupCount,
+    VkPhysicalDeviceGroupPropertiesKHX*         pPhysicalDeviceGroupProperties);
+#endif
+
+#define VK_KHX_external_memory_capabilities 1
+#define VK_LUID_SIZE_KHX                  8
+#define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX = 0x00000010,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX = 0x00000020,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX = 0x00000040,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsKHX;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsKHX;
+
+typedef enum VkExternalMemoryFeatureFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsKHX;
+typedef VkFlags VkExternalMemoryFeatureFlagsKHX;
+
+typedef struct VkExternalMemoryPropertiesKHX {
+    VkExternalMemoryFeatureFlagsKHX       externalMemoryFeatures;
+    VkExternalMemoryHandleTypeFlagsKHX    exportFromImportedHandleTypes;
+    VkExternalMemoryHandleTypeFlagsKHX    compatibleHandleTypes;
+} VkExternalMemoryPropertiesKHX;
+
+typedef struct VkPhysicalDeviceExternalImageFormatInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalImageFormatInfoKHX;
+
+typedef struct VkExternalImageFormatPropertiesKHX {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkExternalMemoryPropertiesKHX    externalMemoryProperties;
+} VkExternalImageFormatPropertiesKHX;
+
+typedef struct VkPhysicalDeviceExternalBufferInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkBufferCreateFlags                      flags;
+    VkBufferUsageFlags                       usage;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalBufferInfoKHX;
+
+typedef struct VkExternalBufferPropertiesKHX {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkExternalMemoryPropertiesKHX    externalMemoryProperties;
+} VkExternalBufferPropertiesKHX;
+
+typedef struct VkPhysicalDeviceIDPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint8_t            deviceUUID[VK_UUID_SIZE];
+    uint8_t            driverUUID[VK_UUID_SIZE];
+    uint8_t            deviceLUID[VK_LUID_SIZE_KHX];
+    VkBool32           deviceLUIDValid;
+} VkPhysicalDeviceIDPropertiesKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, VkExternalBufferPropertiesKHX* pExternalBufferProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo,
+    VkExternalBufferPropertiesKHX*              pExternalBufferProperties);
+#endif
+
+#define VK_KHX_external_memory 1
+#define VK_KHX_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHX_external_memory"
+#define VK_QUEUE_FAMILY_EXTERNAL_KHX      (~0U-1)
+
+typedef struct VkExternalMemoryImageCreateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExternalMemoryImageCreateInfoKHX;
+
+typedef struct VkExternalMemoryBufferCreateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExternalMemoryBufferCreateInfoKHX;
+
+typedef struct VkExportMemoryAllocateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExportMemoryAllocateInfoKHX;
+
+
+#define VK_KHX_external_memory_fd 1
+#define VK_KHX_EXTERNAL_MEMORY_FD_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHX_external_memory_fd"
+
+typedef struct VkImportMemoryFdInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+    int                                      fd;
+} VkImportMemoryFdInfoKHX;
+
+typedef struct VkMemoryFdPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           memoryTypeBits;
+} VkMemoryFdPropertiesKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd);
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdPropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, VkMemoryFdPropertiesKHX* pMemoryFdProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHX(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    int*                                        pFd);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHX(
+    VkDevice                                    device,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    int                                         fd,
+    VkMemoryFdPropertiesKHX*                    pMemoryFdProperties);
+#endif
+
+#define VK_KHX_external_semaphore_capabilities 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_semaphore_capabilities"
+
+
+typedef enum VkExternalSemaphoreHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX = 0x00000008,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX = 0x00000010,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalSemaphoreHandleTypeFlagBitsKHX;
+typedef VkFlags VkExternalSemaphoreHandleTypeFlagsKHX;
+
+typedef enum VkExternalSemaphoreFeatureFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalSemaphoreFeatureFlagBitsKHX;
+typedef VkFlags VkExternalSemaphoreFeatureFlagsKHX;
+
+typedef struct VkPhysicalDeviceExternalSemaphoreInfoKHX {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalSemaphoreInfoKHX;
+
+typedef struct VkExternalSemaphorePropertiesKHX {
+    VkStructureType                          sType;
+    void*                                    pNext;
+    VkExternalSemaphoreHandleTypeFlagsKHX    exportFromImportedHandleTypes;
+    VkExternalSemaphoreHandleTypeFlagsKHX    compatibleHandleTypes;
+    VkExternalSemaphoreFeatureFlagsKHX       externalSemaphoreFeatures;
+} VkExternalSemaphorePropertiesKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, VkExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphorePropertiesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
+    VkExternalSemaphorePropertiesKHX*           pExternalSemaphoreProperties);
+#endif
+
+#define VK_KHX_external_semaphore 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHX_external_semaphore"
+
+typedef struct VkExportSemaphoreCreateInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalSemaphoreHandleTypeFlagsKHX    handleTypes;
+} VkExportSemaphoreCreateInfoKHX;
+
+
+#define VK_KHX_external_semaphore_fd 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHX_external_semaphore_fd"
+
+typedef struct VkImportSemaphoreFdInfoKHX {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkSemaphore                                 semaphore;
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType;
+    int                                         fd;
+} VkImportSemaphoreFdInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreFdKHX)(VkDevice device, const VkImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreFdKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreFdKHX(
+    VkDevice                                    device,
+    const VkImportSemaphoreFdInfoKHX*           pImportSemaphoreFdInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHX(
+    VkDevice                                    device,
+    VkSemaphore                                 semaphore,
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+    int*                                        pFd);
+#endif
+
+#define VK_NVX_device_generated_commands 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
+
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+
+typedef enum VkIndirectCommandsTokenTypeNVX {
+    VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0,
+    VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1,
+    VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2,
+    VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3,
+    VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6,
+    VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1),
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsTokenTypeNVX;
+
+typedef enum VkObjectEntryTypeNVX {
+    VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0,
+    VK_OBJECT_ENTRY_PIPELINE_NVX = 1,
+    VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2,
+    VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3,
+    VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4,
+    VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX,
+    VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX,
+    VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1),
+    VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryTypeNVX;
+
+
+typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX {
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsLayoutUsageFlagBitsNVX;
+typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX;
+
+typedef enum VkObjectEntryUsageFlagBitsNVX {
+    VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001,
+    VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002,
+    VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryUsageFlagBitsNVX;
+typedef VkFlags VkObjectEntryUsageFlagsNVX;
+
+typedef struct VkDeviceGeneratedCommandsFeaturesNVX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           computeBindingPointSupport;
+} VkDeviceGeneratedCommandsFeaturesNVX;
+
+typedef struct VkDeviceGeneratedCommandsLimitsNVX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           maxIndirectCommandsLayoutTokenCount;
+    uint32_t           maxObjectEntryCounts;
+    uint32_t           minSequenceCountBufferOffsetAlignment;
+    uint32_t           minSequenceIndexBufferOffsetAlignment;
+    uint32_t           minCommandsTokenBufferOffsetAlignment;
+} VkDeviceGeneratedCommandsLimitsNVX;
+
+typedef struct VkIndirectCommandsTokenNVX {
+    VkIndirectCommandsTokenTypeNVX    tokenType;
+    VkBuffer                          buffer;
+    VkDeviceSize                      offset;
+} VkIndirectCommandsTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutTokenNVX {
+    VkIndirectCommandsTokenTypeNVX    tokenType;
+    uint32_t                          bindingUnit;
+    uint32_t                          dynamicCount;
+    uint32_t                          divisor;
+} VkIndirectCommandsLayoutTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutCreateInfoNVX {
+    VkStructureType                            sType;
+    const void*                                pNext;
+    VkPipelineBindPoint                        pipelineBindPoint;
+    VkIndirectCommandsLayoutUsageFlagsNVX      flags;
+    uint32_t                                   tokenCount;
+    const VkIndirectCommandsLayoutTokenNVX*    pTokens;
+} VkIndirectCommandsLayoutCreateInfoNVX;
+
+typedef struct VkCmdProcessCommandsInfoNVX {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkObjectTableNVX                     objectTable;
+    VkIndirectCommandsLayoutNVX          indirectCommandsLayout;
+    uint32_t                             indirectCommandsTokenCount;
+    const VkIndirectCommandsTokenNVX*    pIndirectCommandsTokens;
+    uint32_t                             maxSequencesCount;
+    VkCommandBuffer                      targetCommandBuffer;
+    VkBuffer                             sequencesCountBuffer;
+    VkDeviceSize                         sequencesCountOffset;
+    VkBuffer                             sequencesIndexBuffer;
+    VkDeviceSize                         sequencesIndexOffset;
+} VkCmdProcessCommandsInfoNVX;
+
+typedef struct VkCmdReserveSpaceForCommandsInfoNVX {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkObjectTableNVX               objectTable;
+    VkIndirectCommandsLayoutNVX    indirectCommandsLayout;
+    uint32_t                       maxSequencesCount;
+} VkCmdReserveSpaceForCommandsInfoNVX;
+
+typedef struct VkObjectTableCreateInfoNVX {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    uint32_t                             objectCount;
+    const VkObjectEntryTypeNVX*          pObjectEntryTypes;
+    const uint32_t*                      pObjectEntryCounts;
+    const VkObjectEntryUsageFlagsNVX*    pObjectEntryUsageFlags;
+    uint32_t                             maxUniformBuffersPerDescriptor;
+    uint32_t                             maxStorageBuffersPerDescriptor;
+    uint32_t                             maxStorageImagesPerDescriptor;
+    uint32_t                             maxSampledImagesPerDescriptor;
+    uint32_t                             maxPipelineLayouts;
+} VkObjectTableCreateInfoNVX;
+
+typedef struct VkObjectTableEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+} VkObjectTableEntryNVX;
+
+typedef struct VkObjectTablePipelineEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipeline                    pipeline;
+} VkObjectTablePipelineEntryNVX;
+
+typedef struct VkObjectTableDescriptorSetEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipelineLayout              pipelineLayout;
+    VkDescriptorSet               descriptorSet;
+} VkObjectTableDescriptorSetEntryNVX;
+
+typedef struct VkObjectTableVertexBufferEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkBuffer                      buffer;
+} VkObjectTableVertexBufferEntryNVX;
+
+typedef struct VkObjectTableIndexBufferEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkBuffer                      buffer;
+    VkIndexType                   indexType;
+} VkObjectTableIndexBufferEntryNVX;
+
+typedef struct VkObjectTablePushConstantEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipelineLayout              pipelineLayout;
+    VkShaderStageFlags            stageFlags;
+} VkObjectTablePushConstantEntryNVX;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout);
+typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable);
+typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const*    ppObjectTableEntries, const uint32_t* pObjectIndices);
+typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX(
+    VkCommandBuffer                             commandBuffer,
+    const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX(
+    VkCommandBuffer                             commandBuffer,
+    const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX(
+    VkDevice                                    device,
+    const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkIndirectCommandsLayoutNVX*                pIndirectCommandsLayout);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX(
+    VkDevice                                    device,
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX(
+    VkDevice                                    device,
+    const VkObjectTableCreateInfoNVX*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkObjectTableNVX*                           pObjectTable);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    uint32_t                                    objectCount,
+    const VkObjectTableEntryNVX* const*         ppObjectTableEntries,
+    const uint32_t*                             pObjectIndices);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    uint32_t                                    objectCount,
+    const VkObjectEntryTypeNVX*                 pObjectEntryTypes,
+    const uint32_t*                             pObjectIndices);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+    VkPhysicalDevice                            physicalDevice,
+    VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
+    VkDeviceGeneratedCommandsLimitsNVX*         pLimits);
+#endif
+
+#define VK_NV_clip_space_w_scaling 1
+#define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1
+#define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling"
+
+typedef struct VkViewportWScalingNV {
+    float    xcoeff;
+    float    ycoeff;
+} VkViewportWScalingNV;
+
+typedef struct VkPipelineViewportWScalingStateCreateInfoNV {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkBool32                       viewportWScalingEnable;
+    uint32_t                       viewportCount;
+    const VkViewportWScalingNV*    pViewportWScalings;
+} VkPipelineViewportWScalingStateCreateInfoNV;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWScalingNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWScalingNV(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstViewport,
+    uint32_t                                    viewportCount,
+    const VkViewportWScalingNV*                 pViewportWScalings);
+#endif
+
+#define VK_EXT_direct_mode_display 1
+#define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1
+#define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display"
+
+typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkDisplayKHR                                display);
+#endif
+
+#define VK_EXT_display_surface_counter 1
+#define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
+#define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+
+
+typedef enum VkSurfaceCounterFlagBitsEXT {
+    VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001,
+    VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkSurfaceCounterFlagBitsEXT;
+typedef VkFlags VkSurfaceCounterFlagsEXT;
+
+typedef struct VkSurfaceCapabilities2EXT {
+    VkStructureType                  sType;
+    void*                            pNext;
+    uint32_t                         minImageCount;
+    uint32_t                         maxImageCount;
+    VkExtent2D                       currentExtent;
+    VkExtent2D                       minImageExtent;
+    VkExtent2D                       maxImageExtent;
+    uint32_t                         maxImageArrayLayers;
+    VkSurfaceTransformFlagsKHR       supportedTransforms;
+    VkSurfaceTransformFlagBitsKHR    currentTransform;
+    VkCompositeAlphaFlagsKHR         supportedCompositeAlpha;
+    VkImageUsageFlags                supportedUsageFlags;
+    VkSurfaceCounterFlagsEXT         supportedSurfaceCounters;
+} VkSurfaceCapabilities2EXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    VkSurfaceCapabilities2EXT*                  pSurfaceCapabilities);
+#endif
+
+#define VK_EXT_display_control 1
+#define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1
+#define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control"
+
+
+typedef enum VkDisplayPowerStateEXT {
+    VK_DISPLAY_POWER_STATE_OFF_EXT = 0,
+    VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1,
+    VK_DISPLAY_POWER_STATE_ON_EXT = 2,
+    VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT,
+    VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT,
+    VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1),
+    VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDisplayPowerStateEXT;
+
+typedef enum VkDeviceEventTypeEXT {
+    VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0,
+    VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT,
+    VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT,
+    VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1),
+    VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDeviceEventTypeEXT;
+
+typedef enum VkDisplayEventTypeEXT {
+    VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0,
+    VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT,
+    VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT,
+    VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1),
+    VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDisplayEventTypeEXT;
+
+typedef struct VkDisplayPowerInfoEXT {
+    VkStructureType           sType;
+    const void*               pNext;
+    VkDisplayPowerStateEXT    powerState;
+} VkDisplayPowerInfoEXT;
+
+typedef struct VkDeviceEventInfoEXT {
+    VkStructureType         sType;
+    const void*             pNext;
+    VkDeviceEventTypeEXT    deviceEvent;
+} VkDeviceEventInfoEXT;
+
+typedef struct VkDisplayEventInfoEXT {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkDisplayEventTypeEXT    displayEvent;
+} VkDisplayEventInfoEXT;
+
+typedef struct VkSwapchainCounterCreateInfoEXT {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkSurfaceCounterFlagsEXT    surfaceCounters;
+} VkSwapchainCounterCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainCounterEXT)(VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkDisplayPowerControlEXT(
+    VkDevice                                    device,
+    VkDisplayKHR                                display,
+    const VkDisplayPowerInfoEXT*                pDisplayPowerInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDeviceEventEXT(
+    VkDevice                                    device,
+    const VkDeviceEventInfoEXT*                 pDeviceEventInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDisplayEventEXT(
+    VkDevice                                    device,
+    VkDisplayKHR                                display,
+    const VkDisplayEventInfoEXT*                pDisplayEventInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkSurfaceCounterFlagBitsEXT                 counter,
+    uint64_t*                                   pCounterValue);
+#endif
+
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+    uint64_t    refreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+    uint64_t    actualPresentTime;
+    uint64_t    earliestPresentTime;
+    uint64_t    presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+    VkStructureType               sType;
+    const void*                   pNext;
+    uint32_t                      swapchainCount;
+    const VkPresentTimeGOOGLE*    pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint32_t*                                   pPresentationTimingCount,
+    VkPastPresentationTimingGOOGLE*             pPresentationTimings);
+#endif
+
+#define VK_NV_sample_mask_override_coverage 1
+#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1
+#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage"
+
+
+#define VK_NV_geometry_shader_passthrough 1
+#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1
+#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough"
+
+
+#define VK_NV_viewport_array2 1
+#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1
+#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2"
+
+
+#define VK_NVX_multiview_per_view_attributes 1
+#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1
+#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes"
+
+typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           perViewPositionAllComponents;
+} VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX;
+
+
+
+#define VK_NV_viewport_swizzle 1
+#define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1
+#define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle"
+
+
+typedef enum VkViewportCoordinateSwizzleNV {
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV = 2,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV = 3,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV = 4,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_BEGIN_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_END_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_RANGE_SIZE_NV = (VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV + 1),
+    VK_VIEWPORT_COORDINATE_SWIZZLE_MAX_ENUM_NV = 0x7FFFFFFF
+} VkViewportCoordinateSwizzleNV;
+
+typedef VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV;
+
+typedef struct VkViewportSwizzleNV {
+    VkViewportCoordinateSwizzleNV    x;
+    VkViewportCoordinateSwizzleNV    y;
+    VkViewportCoordinateSwizzleNV    z;
+    VkViewportCoordinateSwizzleNV    w;
+} VkViewportSwizzleNV;
+
+typedef struct VkPipelineViewportSwizzleStateCreateInfoNV {
+    VkStructureType                                sType;
+    const void*                                    pNext;
+    VkPipelineViewportSwizzleStateCreateFlagsNV    flags;
+    uint32_t                                       viewportCount;
+    const VkViewportSwizzleNV*                     pViewportSwizzles;
+} VkPipelineViewportSwizzleStateCreateInfoNV;
+
+
+
+#define VK_EXT_discard_rectangles 1
+#define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
+#define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
+
+
+typedef enum VkDiscardRectangleModeEXT {
+    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
+    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
+    VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1),
+    VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDiscardRectangleModeEXT;
+
+typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT;
+
+typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxDiscardRectangles;
+} VkPhysicalDeviceDiscardRectanglePropertiesEXT;
+
+typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT {
+    VkStructureType                                  sType;
+    const void*                                      pNext;
+    VkPipelineDiscardRectangleStateCreateFlagsEXT    flags;
+    VkDiscardRectangleModeEXT                        discardRectangleMode;
+    uint32_t                                         discardRectangleCount;
+    const VkRect2D*                                  pDiscardRectangles;
+} VkPipelineDiscardRectangleStateCreateInfoEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDiscardRectangleEXT(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstDiscardRectangle,
+    uint32_t                                    discardRectangleCount,
+    const VkRect2D*                             pDiscardRectangles);
+#endif
+
+#define VK_EXT_swapchain_colorspace 1
+#define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 2
+#define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
+
+
+#define VK_EXT_hdr_metadata 1
+#define VK_EXT_HDR_METADATA_SPEC_VERSION  1
+#define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
+
+typedef struct VkXYColorEXT {
+    float    x;
+    float    y;
+} VkXYColorEXT;
+
+typedef struct VkHdrMetadataEXT {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkXYColorEXT       displayPrimaryRed;
+    VkXYColorEXT       displayPrimaryGreen;
+    VkXYColorEXT       displayPrimaryBlue;
+    VkXYColorEXT       whitePoint;
+    float              maxLuminance;
+    float              minLuminance;
+    float              maxContentLightLevel;
+    float              maxFrameAverageLightLevel;
+} VkHdrMetadataEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT(
+    VkDevice                                    device,
+    uint32_t                                    swapchainCount,
+    const VkSwapchainKHR*                       pSwapchains,
+    const VkHdrMetadataEXT*                     pMetadata);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tools/Resources.cpp b/tools/Resources.cpp
index d7f9018..f93cf24 100644
--- a/tools/Resources.cpp
+++ b/tools/Resources.cpp
@@ -33,12 +33,9 @@
     if (!gen) {
         return false;
     }
-    SkPMColor ctStorage[256];
-    sk_sp<SkColorTable> ctable(new SkColorTable(ctStorage, 256));
-    int count = ctable->count();
-    return dst->tryAllocPixels(gen->getInfo(), nullptr, ctable.get()) &&
+    return dst->tryAllocPixels(gen->getInfo()) &&
         gen->getPixels(gen->getInfo().makeColorSpace(nullptr), dst->getPixels(), dst->rowBytes(),
-                       const_cast<SkPMColor*>(ctable->readColors()), &count);
+                       nullptr);
 }
 
 sk_sp<SkImage> GetResourceAsImage(const char* resource) {
diff --git a/tools/check-headers-self-sufficient b/tools/check-headers-self-sufficient
index 0513aef..674289b 100755
--- a/tools/check-headers-self-sufficient
+++ b/tools/check-headers-self-sufficient
@@ -11,6 +11,12 @@
 import subprocess
 import sys
 
+'''
+If called with arguments, this script will verify that those headers are
+self-sufficient and idempotent.
+
+Otherwise, test all checked-in headers except for those in the ignore list.
+'''
 
 public_header_args = [
     '-Iinclude/core',
@@ -27,6 +33,7 @@
     '-Iinclude/utils',
     '-Iinclude/utils/mac',
     '-Iinclude/views',
+    '-Ithird_party/vulkan',
 ]
 
 all_header_args = [
@@ -75,6 +82,7 @@
     '-Ithird_party/externals/sfntly/cpp/src',
     '-Ithird_party/externals/zlib',
     '-Ithird_party/gif',
+    '-Ithird_party/vulkan',
 ]
 
 ignore = [
@@ -85,7 +93,6 @@
     'experimental/*',
     'include/config/*',
     'include/core/SkPostConfig.h',
-    'include/gpu/vk/*',
     'include/ports/SkFontMgr_android.h',
     'include/ports/SkFontMgr_fontconfig.h',
     'include/ports/SkTypeface_win.h',
@@ -99,7 +106,6 @@
     'src/core/SkLinearBitmapPipeline.h',
     'src/core/SkLinearBitmapPipeline_*.h',
     'src/core/SkUnPreMultiplyPriv.h',
-    'src/gpu/vk/*.h',
     'src/opts/*_SSE2.h',
     'src/opts/*_SSSE3.h',
     'src/opts/*_neon.h',
@@ -130,20 +136,22 @@
         return '\n\033[7m ERROR: %s \033[0m\n%s\n\n' % (header, errors)
     return None
 
-def main():
+#     for h in headers:
+#         compile_header(h)
+# ...Except use a multiprocessing pool.
+# Exit at first error.
+def compile_headers(headers):
     class N: good = True
-    # N.good is a global scoped to main() to make a print_and_exit_if() a closure
+    # N.good is a global scoped to this function to make a print_and_exit_if() a closure
     pool = multiprocessing.Pool()
     def print_and_exit_if(r):
         if r is not None:
             sys.stdout.write(r)
             N.good = False
             pool.terminate()
-
-    os.chdir(os.path.join(os.path.dirname(__file__), os.pardir))
-    for path in subprocess.check_output(['git', 'ls-files']).splitlines():
-        if path.endswith('.h') and not any(fnmatch.fnmatch(path, pattern) for pattern in ignore):
-            pool.apply_async(compile_header, args=(path, ), callback=print_and_exit_if)
+    for path in headers:
+        assert os.path.exists(path)
+        pool.apply_async(compile_header, args=(path, ), callback=print_and_exit_if)
     pool.close()
     pool.join()
     if N.good:
@@ -151,6 +159,20 @@
     else:
         exit(1)
 
+
+def main(argv):
+    skia_dir = os.path.join(os.path.dirname(__file__), os.pardir)
+    if len(argv) > 1:
+        paths = [os.path.relpath(os.path.abspath(arg), skia_dir) for arg in argv[1:]]
+        os.chdir(skia_dir)
+    else:
+        os.chdir(skia_dir)
+        paths = [path for path in subprocess.check_output(['git', 'ls-files']).splitlines()
+                 if path.endswith('.h')
+                 and not any(fnmatch.fnmatch(path, pattern) for pattern in ignore)]
+    compile_headers(paths)
+
+
 if __name__ == '__main__':
-    main()
+    main(sys.argv)
 
diff --git a/tools/colorspaceinfo.cpp b/tools/colorspaceinfo.cpp
index 30880b9..4d4d8c0 100644
--- a/tools/colorspaceinfo.cpp
+++ b/tools/colorspaceinfo.cpp
@@ -20,22 +20,60 @@
 
 #include "sk_tool_utils.h"
 
-DEFINE_string(input, "input.png", "A path to the input image or icc profile.");
-DEFINE_string(gamut_output, "gamut_output.png", "A path to the output gamut image.");
-DEFINE_string(gamma_output, "gamma_output.png", "A path to the output gamma image.");
+#include <sstream>
+#include <string>
+#include <vector>
+
+DEFINE_string(input, "input.png", "A path to the input image (or icc profile with --icc).");
+DEFINE_string(output, ".", "A path to the output image directory.");
+DEFINE_bool(icc, false, "Indicates that the input is an icc profile.");
 DEFINE_bool(sRGB_gamut, false, "Draws the sRGB gamut on the gamut visualization.");
 DEFINE_bool(adobeRGB, false, "Draws the Adobe RGB gamut on the gamut visualization.");
 DEFINE_bool(sRGB_gamma, false, "Draws the sRGB gamma on all gamma output images.");
 DEFINE_string(uncorrected, "", "A path to reencode the uncorrected input image.");
 
+
+//-------------------------------------------------------------------------------------------------
+//------------------------------------ Gamma visualizations ---------------------------------------
+
 static const char* kRGBChannelNames[3] = {
-    "Red  ", "Green", "Blue "
+    "Red  ",
+    "Green",
+    "Blue "
+};
+static const SkColor kRGBChannelColors[3] = {
+    SkColorSetARGB(128, 255, 0, 0),
+    SkColorSetARGB(128, 0, 255, 0),
+    SkColorSetARGB(128, 0, 0, 255)
 };
 
-static const SkColor kRGBChannelColors[3] = {
-    SkColorSetARGB(164, 255, 32, 32),
-    SkColorSetARGB(164, 32, 255, 32),
-    SkColorSetARGB(164, 32, 32, 255)
+static const char* kGrayChannelNames[1] = { "Gray"};
+static const SkColor kGrayChannelColors[1] = { SkColorSetRGB(128, 128, 128) };
+
+static const char* kCMYKChannelNames[4] = {
+    "Cyan   ",
+    "Magenta",
+    "Yellow ",
+    "Black  "
+};
+static const SkColor kCMYKChannelColors[4] = {
+    SkColorSetARGB(128, 0, 255, 255),
+    SkColorSetARGB(128, 255, 0, 255),
+    SkColorSetARGB(128, 255, 255, 0),
+    SkColorSetARGB(128, 16, 16, 16)
+};
+
+static const char*const*const kChannelNames[4] = {
+    kGrayChannelNames,
+    kRGBChannelNames,
+    kRGBChannelNames,
+    kCMYKChannelNames
+};
+static const SkColor*const kChannelColors[4] = {
+    kGrayChannelColors,
+    kRGBChannelColors,
+    kRGBChannelColors,
+    kCMYKChannelColors
 };
 
 static void dump_transfer_fn(SkGammaNamed gammaNamed) {
@@ -55,35 +93,38 @@
 
 }
 
+static constexpr int kGammaImageWidth = 500;
+static constexpr int kGammaImageHeight = 500;
+
 static void dump_transfer_fn(const SkGammas& gammas) {
-    SkASSERT(gammas.channels() == 3);
+    SkASSERT(gammas.channels() <= 4);
+    const char*const*const channels = kChannelNames[gammas.channels() - 1];
     for (int i = 0; i < gammas.channels(); i++) {
         if (gammas.isNamed(i)) {
             switch (gammas.data(i).fNamed) {
                 case kSRGB_SkGammaNamed:
-                    SkDebugf("%s Transfer Function: sRGB\n", kRGBChannelNames[i]);
+                    SkDebugf("%s Transfer Function: sRGB\n", channels[i]);
                     return;
                 case k2Dot2Curve_SkGammaNamed:
-                    SkDebugf("%s Transfer Function: Exponent 2.2\n", kRGBChannelNames[i]);
+                    SkDebugf("%s Transfer Function: Exponent 2.2\n", channels[i]);
                     return;
                 case kLinear_SkGammaNamed:
-                    SkDebugf("%s Transfer Function: Linear\n", kRGBChannelNames[i]);
+                    SkDebugf("%s Transfer Function: Linear\n", channels[i]);
                     return;
                 default:
                     SkASSERT(false);
                     continue;
             }
         } else if (gammas.isValue(i)) {
-            SkDebugf("%s Transfer Function: Exponent %.3f\n", kRGBChannelNames[i],
-                     gammas.data(i).fValue);
+            SkDebugf("%s Transfer Function: Exponent %.3f\n", channels[i], gammas.data(i).fValue);
         } else if (gammas.isParametric(i)) {
             const SkColorSpaceTransferFn& fn = gammas.data(i).params(&gammas);
             SkDebugf("%s Transfer Function: Parametric A = %.3f, B = %.3f, C = %.3f, D = %.3f, "
-                     "E = %.3f, F = %.3f, G = %.3f\n", kRGBChannelNames[i], fn.fA, fn.fB, fn.fC,
-                     fn.fD, fn.fE, fn.fF, fn.fG);
+                     "E = %.3f, F = %.3f, G = %.3f\n", channels[i], fn.fA, fn.fB, fn.fC, fn.fD,
+                     fn.fE, fn.fF, fn.fG);
         } else {
             SkASSERT(gammas.isTable(i));
-            SkDebugf("%s Transfer Function: Table (%d entries)\n", kRGBChannelNames[i],
+            SkDebugf("%s Transfer Function: Table (%d entries)\n", channels[i],
                     gammas.data(i).fTable.fSize);
         }
     }
@@ -95,7 +136,7 @@
 }
 
 static void draw_transfer_fn(SkCanvas* canvas, SkGammaNamed gammaNamed, const SkGammas* gammas,
-                             SkColor color, int col) {
+                             SkColor color) {
     SkColorSpaceTransferFn fn[4];
     struct TableInfo {
         const float* fTable;
@@ -142,16 +183,14 @@
     // note: gamma has positive values going up in this image so this origin is
     //       the bottom left and we must subtract y instead of adding.
     const float gap         = 16.0f;
-    const float cellWidth   = 500.0f;
-    const float cellHeight  = 500.0f;
-    const float gammaWidth  = cellWidth - 2 * gap;
-    const float gammaHeight = cellHeight - 2 * gap;
+    const float gammaWidth  = kGammaImageWidth - 2 * gap;
+    const float gammaHeight = kGammaImageHeight - 2 * gap;
     // gamma origin point
-    const float ox = gap + cellWidth * col;
+    const float ox = gap;
     const float oy = gap + gammaHeight;
     for (int i = 0; i < channels; ++i) {
         if (kNonStandard_SkGammaNamed == gammaNamed) {
-            paint.setColor(kRGBChannelColors[i]);
+            paint.setColor(kChannelColors[channels - 1][i]);
         } else {
             paint.setColor(color);
         }
@@ -183,6 +222,98 @@
     canvas->drawRect({ ox, oy - gammaHeight, ox + gammaWidth, oy }, paint);
 }
 
+//-------------------------------------------------------------------------------------------------
+//------------------------------------ CLUT visualizations ----------------------------------------
+static void dump_clut(const SkColorLookUpTable& clut) {
+    SkDebugf("CLUT: ");
+    for (int i = 0; i < clut.inputChannels(); ++i) {
+        SkDebugf("[%d]", clut.gridPoints(i));
+    }
+    SkDebugf(" -> [%d]\n", clut.outputChannels());
+}
+
+constexpr int kClutGap = 8;
+constexpr float kClutCanvasSize = 2000;
+
+static inline int usedGridPoints(const SkColorLookUpTable& clut, int dimension) {
+    const int gp = clut.gridPoints(dimension);
+    return gp <= 16 ? gp : 16;
+}
+
+// how many rows of cross-section cuts to display
+static inline int cut_rows(const SkColorLookUpTable& clut, int dimOrder[4]) {
+    // and vertical ones for the 4th dimension (if applicable)
+    return clut.inputChannels() >= 4 ? usedGridPoints(clut, dimOrder[3]) : 1;
+}
+
+// how many columns of cross-section cuts to display
+static inline int cut_cols(const SkColorLookUpTable& clut, int dimOrder[4]) {
+    // do horizontal cuts for the 3rd dimension (if applicable)
+    return clut.inputChannels() >= 3 ? usedGridPoints(clut, dimOrder[2]) : 1;
+}
+
+// gets the width/height to use for cross-sections of a CLUT
+static int cut_size(const SkColorLookUpTable& clut, int dimOrder[4]) {
+    const int rows = cut_rows(clut, dimOrder);
+    const int cols = cut_cols(clut, dimOrder);
+    // make sure the cross-section CLUT cuts are square still by using the
+    // smallest of the width/height, then adjust the gaps between accordingly
+    const int cutWidth = (kClutCanvasSize - kClutGap * (1 + cols)) / cols;
+    const int cutHeight = (kClutCanvasSize - kClutGap * (1 + rows)) / rows;
+    return cutWidth < cutHeight ? cutWidth : cutHeight;
+}
+
+static void draw_clut(SkCanvas* canvas, const SkColorLookUpTable& clut, int dimOrder[4]) {
+    dump_clut(clut);
+
+    const int cutSize = cut_size(clut, dimOrder);
+    const int rows = cut_rows(clut, dimOrder);
+    const int cols = cut_cols(clut, dimOrder);
+    const int cutHorizGap = (kClutCanvasSize - cutSize * cols) / (1 + cols);
+    const int cutVertGap = (kClutCanvasSize - cutSize * rows) / (1 + rows);
+
+    SkPaint paint;
+    for (int row = 0; row < rows; ++row) {
+        for (int col = 0; col < cols; ++col) {
+            // make sure to move at least one pixel, but otherwise move per-gridpoint
+            const float xStep = 1.0f / (SkTMin(cutSize, clut.gridPoints(dimOrder[0])) - 1);
+            const float yStep = 1.0f / (SkTMin(cutSize, clut.gridPoints(dimOrder[1])) - 1);
+            const float ox = clut.inputChannels() >= 3 ? (1 + col) * cutHorizGap + col * cutSize
+                                                       : kClutGap;
+            const float oy = clut.inputChannels() >= 4 ? (1 + row) * cutVertGap + row * cutSize
+                                                       : kClutGap;
+            // for each cross-section cut, draw a bunch of squares whose colour is the top-left's
+            // colour in the CLUT (usually this will just draw the gridpoints)
+            for (float x = 0.0f; x < 1.0f; x += xStep) {
+                for (float y = 0.0f; y < 1.0f; y += yStep) {
+                    const float z = col / (cols - 1.0f);
+                    const float w = row / (rows - 1.0f);
+                    const float input[4] = {x, y, z, w};
+                    float output[3];
+                    clut.interp(output, input);
+                    paint.setColor(SkColorSetRGB(255*output[0], 255*output[1], 255*output[2]));
+                    canvas->drawRect(SkRect::MakeLTRB(ox + cutSize * x, oy + cutSize * y,
+                                                      ox + cutSize * (x + xStep),
+                                                      oy + cutSize * (y + yStep)), paint);
+                }
+            }
+        }
+    }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+//------------------------------------ Gamut visualizations ---------------------------------------
+static void dump_matrix(const SkMatrix44& m) {
+    for (int r = 0; r < 4; ++r) {
+        SkDebugf("|");
+        for (int c = 0; c < 4; ++c) {
+            SkDebugf(" %f ", m.get(r, c));
+        }
+        SkDebugf("|\n");
+    }
+}
+
 /**
  *  Loads the triangular gamut as a set of three points.
  */
@@ -252,28 +383,70 @@
     paint.setTextSize(75.0f);
     canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, rgb, paint);
     if (label) {
-        canvas->drawText("R", 1, rgb[0].fX + 5.0f, rgb[0].fY + 75.0f, paint);
-        canvas->drawText("G", 1, rgb[1].fX + 5.0f, rgb[1].fY - 5.0f, paint);
-        canvas->drawText("B", 1, rgb[2].fX - 75.0f, rgb[2].fY - 5.0f, paint);
+        canvas->drawString("R", rgb[0].fX + 5.0f, rgb[0].fY + 75.0f, paint);
+        canvas->drawString("G", rgb[1].fX + 5.0f, rgb[1].fY - 5.0f, paint);
+        canvas->drawString("B", rgb[2].fX - 75.0f, rgb[2].fY - 5.0f, paint);
     }
 }
 
+
+//-------------------------------------------------------------------------------------------------
+//----------------------------------------- Main code ---------------------------------------------
+static SkBitmap transparentBitmap(int width, int height) {
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(width, height);
+    bitmap.eraseColor(SkColorSetARGB(0, 0, 0, 0));
+    return bitmap;
+}
+
+class OutputCanvas {
+public:
+    OutputCanvas(SkBitmap&& bitmap)
+        :fBitmap(bitmap)
+        ,fCanvas(fBitmap)
+    {}
+
+    bool save(std::vector<std::string>* output, const std::string& filename) {
+        // Finally, encode the result to the output file.
+        sk_sp<SkData> out = sk_tool_utils::EncodeImageToData(fBitmap, SkEncodedImageFormat::kPNG,
+                                                             100);
+        if (!out) {
+            SkDebugf("Failed to encode %s output.\n", filename.c_str());
+            return false;
+        }
+        SkFILEWStream stream(filename.c_str());
+        if (!stream.write(out->data(), out->size())) {
+            SkDebugf("Failed to write %s output.\n", filename.c_str());
+            return false;
+        }
+        // record name of canvas
+        output->push_back(filename);
+        return true;
+    }
+
+    SkCanvas* canvas() { return &fCanvas; }
+
+private:
+    SkBitmap fBitmap;
+    SkCanvas fCanvas;
+};
+
 int main(int argc, char** argv) {
     SkCommandLineFlags::SetUsage(
-            "Usage: colorspaceinfo --input <path to input image or icc profile> "
-                                  "--gamma_output <path to output gamma image> "
-                                  "--gamut_output <path to output gamut image>"
-                                  "--sRGB <draw canonical sRGB gamut> "
+            "Usage: colorspaceinfo --input <path to input image (or icc profile with --icc)> "
+                                  "--output <directory to output images> "
+                                  "--icc <indicates that the input is an icc profile>"
+                                  "--sRGB_gamut <draw canonical sRGB gamut> "
                                   "--adobeRGB <draw canonical Adobe RGB gamut> "
+                                  "--sRGB_gamma <draw sRGB gamma> "
                                   "--uncorrected <path to reencoded, uncorrected input image>\n"
             "Description: Writes visualizations of the color space to the output image(s)  ."
                          "Also, if a path is provided, writes uncorrected bytes to an unmarked "
                          "png, for comparison with the input image.\n");
     SkCommandLineFlags::Parse(argc, argv);
     const char* input = FLAGS_input[0];
-    const char* gamut_output = FLAGS_gamut_output[0];
-    const char* gamma_output = FLAGS_gamma_output[0];
-    if (!input || !gamut_output || !gamma_output) {
+    const char* output = FLAGS_output[0];
+    if (!input || !output) {
         SkCommandLineFlags::PrintUsage();
         return -1;
     }
@@ -283,13 +456,14 @@
         SkDebugf("Cannot find input image.\n");
         return -1;
     }
-    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+
+    std::unique_ptr<SkCodec> codec = nullptr;
     sk_sp<SkColorSpace> colorSpace = nullptr;
-    const bool isImage = (codec != nullptr);
-    if (isImage) {
-        colorSpace = sk_ref_sp(codec->getInfo().colorSpace());
-    } else {
+    if (FLAGS_icc) {
         colorSpace = SkColorSpace::MakeICC(data->bytes(), data->size());
+    } else {
+        codec.reset(SkCodec::NewFromData(data));
+        colorSpace = sk_ref_sp(codec->getInfo().colorSpace());
     }
 
     if (!colorSpace) {
@@ -297,86 +471,154 @@
         return -1;
     }
 
-    // Load a graph of the CIE XYZ color gamut.
-    SkBitmap gamutCanvasBitmap;
-    if (!GetResourceAsBitmap("gamut.png", &gamutCanvasBitmap)) {
-        SkDebugf("Program failure.\n");
-        return -1;
-    }
-    SkCanvas gamutCanvas(gamutCanvasBitmap);
+    // TODO: command line tweaking of this order
+    int dimOrder[4] = {0, 1, 2, 3};
 
-    SkBitmap gammaCanvasBitmap;
-    gammaCanvasBitmap.allocN32Pixels(500, 500);
-    SkCanvas gammaCanvas(gammaCanvasBitmap);
+    std::vector<std::string> outputFilenames;
 
-    // Draw the sRGB gamut if requested.
-    if (FLAGS_sRGB_gamut) {
-        sk_sp<SkColorSpace> sRGBSpace = SkColorSpace::MakeSRGB();
-        const SkMatrix44* mat = as_CSB(sRGBSpace)->toXYZD50();
-        SkASSERT(mat);
-        draw_gamut(&gamutCanvas, *mat, "sRGB", 0xFFFF9394, false);
-    }
+    auto createOutputFilename = [output](const char* category, int index) -> std::string {
+        std::stringstream ss;
+        ss << output << '/' << category << '_' << index << ".png";
+        return ss.str();
+    };
 
-    // Draw the Adobe RGB gamut if requested.
-    if (FLAGS_adobeRGB) {
-        sk_sp<SkColorSpace> adobeRGBSpace =
-                SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named);
-        const SkMatrix44* mat = as_CSB(adobeRGBSpace)->toXYZD50();
-        SkASSERT(mat);
-        draw_gamut(&gamutCanvas, *mat, "Adobe RGB", 0xFF31a9e1, false);
-    }
-
-    int gammaCol = 0;
     if (SkColorSpace_Base::Type::kXYZ == as_CSB(colorSpace)->type()) {
+        SkDebugf("XYZ/TRC color space\n");
+
+        // Load a graph of the CIE XYZ color gamut.
+        SkBitmap gamutCanvasBitmap;
+        if (!GetResourceAsBitmap("gamut.png", &gamutCanvasBitmap)) {
+            SkDebugf("Program failure (could not load gamut.png).\n");
+            return -1;
+        }
+        OutputCanvas gamutCanvas(std::move(gamutCanvasBitmap));
+        // Draw the sRGB gamut if requested.
+        if (FLAGS_sRGB_gamut) {
+            sk_sp<SkColorSpace> sRGBSpace = SkColorSpace::MakeSRGB();
+            const SkMatrix44* mat = as_CSB(sRGBSpace)->toXYZD50();
+            SkASSERT(mat);
+            draw_gamut(gamutCanvas.canvas(), *mat, "sRGB", 0xFFFF9394, false);
+        }
+
+        // Draw the Adobe RGB gamut if requested.
+        if (FLAGS_adobeRGB) {
+            sk_sp<SkColorSpace> adobeRGBSpace = SkColorSpace::MakeRGB(
+                    SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kAdobeRGB_Gamut);
+            const SkMatrix44* mat = as_CSB(adobeRGBSpace)->toXYZD50();
+            SkASSERT(mat);
+            draw_gamut(gamutCanvas.canvas(), *mat, "Adobe RGB", 0xFF31a9e1, false);
+        }
         const SkMatrix44* mat = as_CSB(colorSpace)->toXYZD50();
         SkASSERT(mat);
         auto xyz = static_cast<SkColorSpace_XYZ*>(colorSpace.get());
-        draw_gamut(&gamutCanvas, *mat, input, 0xFF000000, true);
-        if (FLAGS_sRGB_gamma) {
-            draw_transfer_fn(&gammaCanvas, kSRGB_SkGammaNamed, nullptr, 0xFFFF9394, gammaCol);
+        draw_gamut(gamutCanvas.canvas(), *mat, input, 0xFF000000, true);
+        if (!gamutCanvas.save(&outputFilenames, createOutputFilename("gamut", 0))) {
+            return -1;
         }
-        draw_transfer_fn(&gammaCanvas, xyz->gammaNamed(), xyz->gammas(), 0xFF000000, gammaCol++);
+
+        OutputCanvas gammaCanvas(transparentBitmap(kGammaImageWidth, kGammaImageHeight));
+        if (FLAGS_sRGB_gamma) {
+            draw_transfer_fn(gammaCanvas.canvas(), kSRGB_SkGammaNamed, nullptr, 0xFFFF9394);
+        }
+        draw_transfer_fn(gammaCanvas.canvas(), xyz->gammaNamed(), xyz->gammas(), 0xFF000000);
+        if (!gammaCanvas.save(&outputFilenames, createOutputFilename("gamma", 0))) {
+            return -1;
+        }
     } else {
-        SkDebugf("Color space is defined using an A2B tag.  It cannot be represented by "
-                 "a transfer function and to D50 matrix.\n");
-        return -1;
+        SkDebugf("A2B color space");
+        SkColorSpace_A2B* a2b = static_cast<SkColorSpace_A2B*>(colorSpace.get());
+        SkDebugf("Conversion type: ");
+        switch (a2b->iccType()) {
+            case SkColorSpace_Base::kRGB_ICCTypeFlag:
+                SkDebugf("RGB");
+                break;
+            case SkColorSpace_Base::kCMYK_ICCTypeFlag:
+                SkDebugf("CMYK");
+                break;
+            case SkColorSpace_Base::kGray_ICCTypeFlag:
+                SkDebugf("Gray");
+                break;
+            default:
+                SkASSERT(false);
+                break;
+
+        }
+        SkDebugf(" -> ");
+        switch (a2b->pcs()) {
+            case SkColorSpace_A2B::PCS::kXYZ:
+                SkDebugf("XYZ\n");
+                break;
+            case SkColorSpace_A2B::PCS::kLAB:
+                SkDebugf("LAB\n");
+                break;
+        }
+        int clutCount = 0;
+        int gammaCount = 0;
+        for (int i = 0; i < a2b->count(); ++i) {
+            const SkColorSpace_A2B::Element& e = a2b->element(i);
+            switch (e.type()) {
+                case SkColorSpace_A2B::Element::Type::kGammaNamed: {
+                    OutputCanvas gammaCanvas(transparentBitmap(kGammaImageWidth,
+                                                               kGammaImageHeight));
+                    if (FLAGS_sRGB_gamma) {
+                        draw_transfer_fn(gammaCanvas.canvas(), kSRGB_SkGammaNamed, nullptr,
+                                         0xFFFF9394);
+                    }
+                    draw_transfer_fn(gammaCanvas.canvas(), e.gammaNamed(), nullptr,
+                                     0xFF000000);
+                    if (!gammaCanvas.save(&outputFilenames,
+                                          createOutputFilename("gamma", gammaCount++))) {
+                        return -1;
+                    }
+                }
+                break;
+                case SkColorSpace_A2B::Element::Type::kGammas: {
+                    OutputCanvas gammaCanvas(transparentBitmap(kGammaImageWidth,
+                                                               kGammaImageHeight));
+                    if (FLAGS_sRGB_gamma) {
+                        draw_transfer_fn(gammaCanvas.canvas(), kSRGB_SkGammaNamed, nullptr,
+                                         0xFFFF9394);
+                    }
+                    draw_transfer_fn(gammaCanvas.canvas(), kNonStandard_SkGammaNamed,
+                                     &e.gammas(), 0xFF000000);
+                    if (!gammaCanvas.save(&outputFilenames,
+                                          createOutputFilename("gamma", gammaCount++))) {
+                        return -1;
+                    }
+                }
+                break;
+                case SkColorSpace_A2B::Element::Type::kCLUT: {
+                    const SkColorLookUpTable& clut = e.colorLUT();
+                    const int cutSize = cut_size(clut, dimOrder);
+                    const int clutWidth = clut.inputChannels() >= 3 ? kClutCanvasSize
+                                                                    : 2 * kClutGap + cutSize;
+                    const int clutHeight = clut.inputChannels() >= 4 ? kClutCanvasSize
+                                                                     : 2 * kClutGap + cutSize;
+                    OutputCanvas clutCanvas(transparentBitmap(clutWidth, clutHeight));
+                    draw_clut(clutCanvas.canvas(), e.colorLUT(), dimOrder);
+                    if (!clutCanvas.save(&outputFilenames,
+                                         createOutputFilename("clut", clutCount++))) {
+                        return -1;
+                    }
+                }
+                break;
+                case SkColorSpace_A2B::Element::Type::kMatrix:
+                    dump_matrix(e.matrix());
+                    break;
+            }
+        }
     }
 
     // marker to tell the web-tool the names of all images output
     SkDebugf("=========\n");
-    auto saveCanvasBitmap = [](const SkBitmap& bitmap, const char *fname) {
-        // Finally, encode the result to the output file.
-        sk_sp<SkData> out = sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG,
-                                                             100);
-        if (!out) {
-            SkDebugf("Failed to encode %s output.\n", fname);
-            return false;
-        }
-        SkFILEWStream stream(fname);
-        if (!stream.write(out->data(), out->size())) {
-            SkDebugf("Failed to write %s output.\n", fname);
-            return false;
-        }
-        // record name of canvas
-        SkDebugf("%s\n", fname);
-        return true;
-    };
-
-    // only XYZ images have a gamut visualization since the matrix in A2B is not
-    // a gamut adjustment from RGB->XYZ always (or ever)
-    if (SkColorSpace_Base::Type::kXYZ == as_CSB(colorSpace)->type() &&
-        !saveCanvasBitmap(gamutCanvasBitmap, gamut_output)) {
-        return -1;
+    for (const std::string& filename : outputFilenames) {
+        SkDebugf("%s\n", filename.c_str());
     }
-    if (gammaCol > 0 && !saveCanvasBitmap(gammaCanvasBitmap, gamma_output)) {
-        return -1;
-    }
-
-    if (isImage) {
+    if (!FLAGS_icc) {
         SkDebugf("%s\n", input);
     }
     // Also, if requested, decode and reencode the uncorrected input image.
-    if (!FLAGS_uncorrected.isEmpty() && isImage) {
+    if (!FLAGS_uncorrected.isEmpty() && !FLAGS_icc) {
         SkBitmap bitmap;
         int width = codec->getInfo().width();
         int height = codec->getInfo().height();
diff --git a/tools/create_flutter_test_images.cpp b/tools/create_flutter_test_images.cpp
new file mode 100644
index 0000000..69a0d11
--- /dev/null
+++ b/tools/create_flutter_test_images.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "SkColorSpacePriv.h"
+#include "SkImage.h"
+#include "SkPngEncoder.h"
+
+#include "Resources.h"
+
+/**
+ *  Create a color space that swaps the red, green, and blue channels.
+ */
+static sk_sp<SkColorSpace> gbr_color_space() {
+    float gbr[9];
+    gbr[0] = gSRGB_toXYZD50[1];
+    gbr[1] = gSRGB_toXYZD50[2];
+    gbr[2] = gSRGB_toXYZD50[0];
+    gbr[3] = gSRGB_toXYZD50[4];
+    gbr[4] = gSRGB_toXYZD50[5];
+    gbr[5] = gSRGB_toXYZD50[3];
+    gbr[6] = gSRGB_toXYZD50[7];
+    gbr[7] = gSRGB_toXYZD50[8];
+    gbr[8] = gSRGB_toXYZD50[6];
+    SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
+    toXYZD50.set3x3RowMajorf(gbr);
+    return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
+}
+
+/**
+ *  Create a color space with a steep transfer function.
+ */
+static sk_sp<SkColorSpace> tf_color_space() {
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 50.f;
+    return SkColorSpace::MakeRGB(fn, SkColorSpace::kSRGB_Gamut);
+}
+
+/**
+ *  Create a wide gamut color space.
+ */
+static sk_sp<SkColorSpace> wide_gamut_color_space() {
+    return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                 SkColorSpace::kRec2020_Gamut);
+}
+
+int main(int argc, char** argv) {
+    sk_sp<SkImage> image = GetResourceAsImage("flutter_logo.jpg");
+    if (!image) {
+        SkDebugf("Cannot find flutter_logo.jpg in resources.\n");
+        return 1;
+    }
+
+    // Encode an image with a gbr color space.
+    SkImageInfo info = SkImageInfo::MakeN32(image->width(), image->height(), kOpaque_SkAlphaType,
+                                            gbr_color_space());
+    size_t rowBytes = info.minRowBytes();
+    SkAutoTMalloc<uint8_t> storage(rowBytes * image->height());
+    SkPixmap src(info, storage.get(), rowBytes);
+    image->readPixels(src, 0, 0, SkImage::kDisallow_CachingHint);
+    SkFILEWStream dst0("gbr.png");
+    SkPngEncoder::Options opts;
+    opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore; // Does not matter for opaque src
+    SkAssertResult(SkPngEncoder::Encode(&dst0, src, opts));
+
+    // Encode an image with steep transfer function.
+    src.setColorSpace(tf_color_space());
+    image->readPixels(src, 0, 0, SkImage::kDisallow_CachingHint);
+    SkFILEWStream dst1("tf.png");
+    SkAssertResult(SkPngEncoder::Encode(&dst1, src, opts));
+
+    // Encode a wide gamut image.
+    src.setColorSpace(wide_gamut_color_space());
+    image->readPixels(src, 0, 0, SkImage::kDisallow_CachingHint);
+    SkFILEWStream dst2("wide-gamut.png");
+    SkAssertResult(SkPngEncoder::Encode(&dst2, src, opts));
+
+    return 0;
+}
diff --git a/tools/debugger/SkDebugCanvas.cpp b/tools/debugger/SkDebugCanvas.cpp
index 6349810..fdde0a6 100644
--- a/tools/debugger/SkDebugCanvas.cpp
+++ b/tools/debugger/SkDebugCanvas.cpp
@@ -58,17 +58,6 @@
         this->SkCanvas::onDrawPicture(picture, matrix, paint);
     }
 
-    void onDrawShadowedPicture(const SkPicture* picture,
-                               const SkMatrix* matrix,
-                               const SkPaint* paint,
-                               const SkShadowParams& params) {
-#ifdef SK_EXPERIMENTAL_SHADOWING
-        this->SkCanvas::onDrawShadowedPicture(picture, matrix, paint, params);
-#else
-        this->SkCanvas::onDrawPicture(picture, matrix, paint);
-#endif
-    }
-
 private:
     bool fOverdrawViz;
     bool fOverrideFilterQuality;
@@ -149,39 +138,6 @@
     return layer;
 }
 
-class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
-public:
-    SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
-
-    void clipRect(const SkRect& r, SkClipOp, bool doAA) override {
-        SkPaint p;
-        p.setColor(SK_ColorRED);
-        p.setStyle(SkPaint::kStroke_Style);
-        p.setAntiAlias(doAA);
-        fCanvas->drawRect(r, p);
-    }
-    void clipRRect(const SkRRect& rr, SkClipOp, bool doAA) override {
-        SkPaint p;
-        p.setColor(SK_ColorGREEN);
-        p.setStyle(SkPaint::kStroke_Style);
-        p.setAntiAlias(doAA);
-        fCanvas->drawRRect(rr, p);
-    }
-    void clipPath(const SkPath& path, SkClipOp, bool doAA) override {
-        SkPaint p;
-        p.setColor(SK_ColorBLUE);
-        p.setStyle(SkPaint::kStroke_Style);
-        p.setAntiAlias(doAA);
-        fCanvas->drawPath(path, p);
-    }
-
-protected:
-    SkCanvas* fCanvas;
-
-private:
-    typedef SkCanvas::ClipVisitor INHERITED;
-};
-
 // set up the saveLayer commands so that the active ones
 // return true in their 'active' method
 void SkDebugCanvas::markActiveCommands(int index) {
@@ -288,23 +244,6 @@
         filterCanvas.restore();
     }
 
-    if (fMegaVizMode) {
-        filterCanvas.save();
-        // nuke the CTM
-        filterCanvas.resetMatrix();
-        // turn off clipping
-        if (!windowRect.isEmpty()) {
-            SkRect r = windowRect;
-            r.outset(SK_Scalar1, SK_Scalar1);
-            filterCanvas.clipRect(r, kReplace_SkClipOp);
-        }
-        // visualize existing clips
-        SkDebugClipVisitor visitor(&filterCanvas);
-
-        filterCanvas.replayClips(&visitor);
-
-        filterCanvas.restore();
-    }
     if (pathOpsMode) {
         this->resetClipStackData();
         const SkClipStack* clipStack = nullptr;//HACK filterCanvas.getClipStack();
@@ -348,7 +287,7 @@
         // drawn offscreen
         GrRenderTargetContext* rtc =
                 originalCanvas->internal_private_accessTopLayerRenderTargetContext();
-        GrGpuResource::UniqueID rtID = rtc->accessRenderTarget()->uniqueID();
+        GrSurfaceProxy::UniqueID proxyID = rtc->asSurfaceProxy()->uniqueID();
 
         // get the bounding boxes to draw
         SkTArray<GrAuditTrail::OpInfo> childrenBounds;
@@ -362,7 +301,7 @@
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(1);
         for (int i = 0; i < childrenBounds.count(); i++) {
-            if (childrenBounds[i].fRenderTargetUniqueID != rtID) {
+            if (childrenBounds[i].fProxyUniqueID != proxyID) {
                 // offscreen draw, ignore for now
                 continue;
             }
@@ -595,16 +534,6 @@
     this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
 }
 
-void SkDebugCanvas::onDrawShadowedPicture(const SkPicture* picture,
-                                          const SkMatrix* matrix,
-                                          const SkPaint* paint,
-                                          const SkShadowParams& params) {
-    this->addDrawCommand(new SkBeginDrawShadowedPictureCommand(picture, matrix, paint, params));
-    SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
-    picture->playback(this);
-    this->addDrawCommand(new SkEndDrawShadowedPictureCommand(SkToBool(matrix) || SkToBool(paint)));
-}
-
 void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count,
                                  const SkPoint pts[], const SkPaint& paint) {
     this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
@@ -691,13 +620,6 @@
     this->INHERITED::didSetMatrix(matrix);
 }
 
-void SkDebugCanvas::didTranslateZ(SkScalar z) {
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    this->addDrawCommand(new SkTranslateZCommand(z));
-    this->INHERITED::didTranslateZ(z);
-#endif
-}
-
 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
     SkASSERT(index < fCommandVector.count());
     fCommandVector[index]->setVisible(toggle);
diff --git a/tools/debugger/SkDebugCanvas.h b/tools/debugger/SkDebugCanvas.h
index 7c6a0f4..9b944f7 100644
--- a/tools/debugger/SkDebugCanvas.h
+++ b/tools/debugger/SkDebugCanvas.h
@@ -195,12 +195,6 @@
 
     void didSetMatrix(const SkMatrix &) override;
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void didTranslateZ(SkScalar) override;
-#else
-    void didTranslateZ(SkScalar);
-#endif
-
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
@@ -244,18 +238,6 @@
 
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    void onDrawShadowedPicture(const SkPicture*,
-                               const SkMatrix*,
-                               const SkPaint*,
-                               const SkShadowParams& params) override;
-#else
-    void onDrawShadowedPicture(const SkPicture*,
-                               const SkMatrix*,
-                               const SkPaint*,
-                               const SkShadowParams& params);
-#endif
-
     void markActiveCommands(int index);
 
 private:
diff --git a/tools/debugger/SkDrawCommand.cpp b/tools/debugger/SkDrawCommand.cpp
index 691a4f8..16a270b 100644
--- a/tools/debugger/SkDrawCommand.cpp
+++ b/tools/debugger/SkDrawCommand.cpp
@@ -204,7 +204,6 @@
 const char* SkDrawCommand::GetCommandString(OpType type) {
     switch (type) {
         case kBeginDrawPicture_OpType: return "BeginDrawPicture";
-        case kBeginDrawShadowedPicture_OpType: return "BeginDrawShadowedPicture";
         case kClipPath_OpType: return "ClipPath";
         case kClipRegion_OpType: return "ClipRegion";
         case kClipRect_OpType: return "ClipRect";
@@ -234,12 +233,10 @@
         case kDrawTextRSXform_OpType: return "DrawTextRSXform";
         case kDrawVertices_OpType: return "DrawVertices";
         case kEndDrawPicture_OpType: return "EndDrawPicture";
-        case kEndDrawShadowedPicture_OpType: return "EndDrawShadowedPicture";
         case kRestore_OpType: return "Restore";
         case kSave_OpType: return "Save";
         case kSaveLayer_OpType: return "SaveLayer";
         case kSetMatrix_OpType: return "SetMatrix";
-        case kTranslateZ_OpType: return "TranslateZ";
         default:
             SkDebugf("OpType error 0x%08x\n", type);
             SkASSERT(0);
@@ -297,9 +294,6 @@
         INSTALL_FACTORY(Save);
         INSTALL_FACTORY(SaveLayer);
         INSTALL_FACTORY(SetMatrix);
-#ifdef SK_EXPERIMENTAL_SHADOWING
-        INSTALL_FACTORY(TranslateZ);
-#endif
     }
     SkString name = SkString(command[SKDEBUGCANVAS_ATTRIBUTE_COMMAND].asCString());
     FROM_JSON* factory = factories.find(name);
@@ -837,7 +831,9 @@
         return bitmap;
     }
     SkBitmap* dst = new SkBitmap();
-    if (bitmap->copyTo(dst, colorType)) {
+    if (dst->tryAllocPixels(bitmap->info().makeColorType(colorType)) &&
+        bitmap->readPixels(dst->info(), dst->getPixels(), dst->rowBytes(), 0, 0))
+    {
         delete bitmap;
         return dst;
     }
@@ -889,9 +885,7 @@
 
 bool SkDrawCommand::flatten(const SkBitmap& bitmap, Json::Value* target,
                             UrlDataManager& urlDataManager) {
-    bitmap.lockPixels();
     sk_sp<SkImage> image(SkImage::MakeFromBitmap(bitmap));
-    bitmap.unlockPixels();
     (*target)[SKDEBUGCANVAS_ATTRIBUTE_COLOR] = Json::Value(color_type_name(bitmap.colorType()));
     (*target)[SKDEBUGCANVAS_ATTRIBUTE_ALPHA] = Json::Value(alpha_type_name(bitmap.alphaType()));
     bool success = flatten(*image, target, urlDataManager);
@@ -1572,14 +1566,6 @@
     result->set9(values);
 }
 
-#ifdef SK_EXPERIMENTAL_SHADOWING
-// somehow this is only used in shadows...
-static void extract_json_scalar(Json::Value& scalar, SkScalar* result) {
-    SkScalar value = scalar.asFloat();
-    *result = value;
-}
-#endif
-
 static void extract_json_path(Json::Value& path, SkPath* result) {
     const char* fillType = path[SKDEBUGCANVAS_ATTRIBUTE_FILLTYPE].asCString();
     if (!strcmp(fillType, SKDEBUGCANVAS_FILLTYPE_WINDING)) {
@@ -2595,91 +2581,6 @@
     }
 }
 
-SkBeginDrawShadowedPictureCommand::SkBeginDrawShadowedPictureCommand(const SkPicture* picture,
-                                                                     const SkMatrix* matrix,
-                                                                     const SkPaint* paint,
-                                                                     const SkShadowParams& params)
-        : INHERITED(kBeginDrawShadowedPicture_OpType)
-#ifdef SK_EXPERIMENTAL_SHADOWING
-        , fPicture(SkRef(picture))
-        , fShadowParams(params) {
-#else
-        , fPicture(SkRef(picture)) {
-#endif
-    SkString* str = new SkString;
-    str->appendf("SkPicture: L: %f T: %f R: %f B: %f\n",
-                 picture->cullRect().fLeft, picture->cullRect().fTop,
-                 picture->cullRect().fRight, picture->cullRect().fBottom);
-    str->appendf("SkShadowParams: bias:%f, minVariance:%f, shRadius:%f, shType:",
-                   params.fBiasingConstant,
-                   params.fMinVariance,
-                   params.fShadowRadius);
-
-    SkASSERT(SkShadowParams::kShadowTypeCount == 2);
-
-    switch (params.fType) {
-        case SkShadowParams::ShadowType::kNoBlur_ShadowType:
-            str->append("kNoBlur_ShadowType\n");
-            break;
-        case SkShadowParams::ShadowType::kVariance_ShadowType:
-            str->append("kVariance_ShadowType\n");
-            break;
-    }
-
-    fInfo.push(str);
-
-    if (matrix) {
-        fMatrix.set(*matrix);
-        fInfo.push(SkObjectParser::MatrixToString(*matrix));
-    }
-
-    if (paint) {
-        fPaint.set(*paint);
-        fInfo.push(SkObjectParser::PaintToString(*paint));
-    }
-}
-
-void SkBeginDrawShadowedPictureCommand::execute(SkCanvas* canvas) const {
-    if (fPaint.isValid()) {
-        SkRect bounds = fPicture->cullRect();
-        if (fMatrix.isValid()) {
-            fMatrix.get()->mapRect(&bounds);
-        }
-        canvas->saveLayer(&bounds, fPaint.get());
-    }
-
-    if (fMatrix.isValid()) {
-        if (!fPaint.isValid()) {
-            canvas->save();
-        }
-        canvas->concat(*fMatrix.get());
-    }
-}
-
-bool SkBeginDrawShadowedPictureCommand::render(SkCanvas* canvas) const {
-    canvas->clear(0xFFFFFFFF);
-    canvas->save();
-
-    xlate_and_scale_to_bounds(canvas, fPicture->cullRect());
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    canvas->drawShadowedPicture(fPicture.get(), fMatrix.get(), fPaint.get(), fShadowParams);
-#else
-    canvas->drawPicture(fPicture.get(), fMatrix.get(), fPaint.get());
-#endif
-    canvas->restore();
-
-    return true;
-}
-
-SkEndDrawShadowedPictureCommand::SkEndDrawShadowedPictureCommand(bool restore)
-        : INHERITED(kEndDrawShadowedPicture_OpType) , fRestore(restore) { }
-
-void SkEndDrawShadowedPictureCommand::execute(SkCanvas* canvas) const {
-    if (fRestore) {
-        canvas->restore();
-    }
-}
-
 SkDrawPointsCommand::SkDrawPointsCommand(SkCanvas::PointMode mode, size_t count,
                                          const SkPoint pts[], const SkPaint& paint)
     : INHERITED(kDrawPoints_OpType) {
@@ -3556,32 +3457,3 @@
     extract_json_matrix(command[SKDEBUGCANVAS_ATTRIBUTE_MATRIX], &matrix);
     return new SkSetMatrixCommand(matrix);
 }
-
-SkTranslateZCommand::SkTranslateZCommand(SkScalar z)
-    : INHERITED(kTranslateZ_OpType) {
-    fZTranslate = z;
-    fInfo.push(SkObjectParser::ScalarToString(fZTranslate, "drawDepthTranslation"));
-}
-
-void SkTranslateZCommand::execute(SkCanvas* canvas) const {
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    canvas->translateZ(fZTranslate);
-#endif
-}
-
-Json::Value SkTranslateZCommand::toJSON(UrlDataManager& urlDataManager) const {
-    Json::Value result = INHERITED::toJSON(urlDataManager);
-    result[SKDEBUGCANVAS_ATTRIBUTE_DRAWDEPTHTRANS] = MakeJsonScalar(fZTranslate);
-    return result;
-}
-
-SkTranslateZCommand* SkTranslateZCommand::fromJSON(Json::Value& command,
-                                       UrlDataManager& urlDataManager) {
-    SkScalar z;
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    extract_json_scalar(command[SKDEBUGCANVAS_ATTRIBUTE_DRAWDEPTHTRANS], &z);
-#else
-    z = 0;
-#endif
-    return new SkTranslateZCommand(z);
-}
diff --git a/tools/debugger/SkDrawCommand.h b/tools/debugger/SkDrawCommand.h
index 4004b3b..1b3d9ab 100644
--- a/tools/debugger/SkDrawCommand.h
+++ b/tools/debugger/SkDrawCommand.h
@@ -8,6 +8,7 @@
 #ifndef SKDRAWCOMMAND_H_
 #define SKDRAWCOMMAND_H_
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkTLazy.h"
 #include "SkPath.h"
@@ -24,7 +25,6 @@
 public:
     enum OpType {
         kBeginDrawPicture_OpType,
-        kBeginDrawShadowedPicture_OpType,
         kClipPath_OpType,
         kClipRegion_OpType,
         kClipRect_OpType,
@@ -54,14 +54,12 @@
         kDrawTextRSXform_OpType,
         kDrawVertices_OpType,
         kEndDrawPicture_OpType,
-        kEndDrawShadowedPicture_OpType,
         kRestore_OpType,
         kSave_OpType,
         kSaveLayer_OpType,
         kSetMatrix_OpType,
-        kTranslateZ_OpType,
 
-        kLast_OpType = kTranslateZ_OpType
+        kLast_OpType = kSetMatrix_OpType
     };
 
     static const int kOpTypeCount = kLast_OpType + 1;
@@ -496,39 +494,6 @@
     typedef SkDrawCommand INHERITED;
 };
 
-class SkBeginDrawShadowedPictureCommand : public SkDrawCommand {
-public:
-    SkBeginDrawShadowedPictureCommand(const SkPicture* picture,
-                                      const SkMatrix* matrix,
-                                      const SkPaint* paint,
-                                      const SkShadowParams& params);
-
-    void execute(SkCanvas* canvas) const override;
-    bool render(SkCanvas* canvas) const override;
-
-private:
-    sk_sp<const SkPicture>        fPicture;
-    SkTLazy<SkMatrix>             fMatrix;
-    SkTLazy<SkPaint>              fPaint;
-#ifdef SK_EXPERIMENTAL_SHADOWING
-    SkShadowParams                fShadowParams;
-#endif
-
-    typedef SkDrawCommand INHERITED;
-};
-
-class SkEndDrawShadowedPictureCommand : public SkDrawCommand {
-public:
-    SkEndDrawShadowedPictureCommand(bool restore);
-
-    void execute(SkCanvas* canvas) const override;
-
-private:
-    bool fRestore;
-
-    typedef SkDrawCommand INHERITED;
-};
-
 class SkDrawPointsCommand : public SkDrawCommand {
 public:
     SkDrawPointsCommand(SkCanvas::PointMode mode, size_t count, const SkPoint pts[],
@@ -796,18 +761,5 @@
 
     typedef SkDrawCommand INHERITED;
 };
-
-class SkTranslateZCommand : public SkDrawCommand {
-public:
-    SkTranslateZCommand(SkScalar);
-    void execute(SkCanvas* canvas) const override;
-    Json::Value toJSON(UrlDataManager& urlDataManager) const override;
-    static SkTranslateZCommand* fromJSON(Json::Value& command, UrlDataManager& urlDataManager);
-
-private:
-    SkScalar fZTranslate;
-
-    typedef SkDrawCommand INHERITED;
-};
 #endif
 
diff --git a/tools/dump_record.cpp b/tools/dump_record.cpp
index c1b87e7..24017bd 100644
--- a/tools/dump_record.cpp
+++ b/tools/dump_record.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "DumpRecord.h"
+#include "SkBitmap.h"
 #include "SkCommandLineFlags.h"
 #include "SkDeferredCanvas.h"
 #include "SkPicture.h"
diff --git a/tools/fiddle/animate.sh b/tools/fiddle/animate.sh
new file mode 100755
index 0000000..cbd62bb
--- /dev/null
+++ b/tools/fiddle/animate.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# 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 a 3 second long animation from the Raster output of fiddle at 15 fps.
+FPS=15
+DURATION=3
+FRAMES=$((DURATION * FPS))
+mkdir -p /tmp/animation
+for i in $(seq -f "%05g" 0 $FRAMES)
+do
+    ./out/Release/fiddle --duration $DURATION --frame `bc -l <<< "$i/$FRAMES"` | ./tools/fiddle/parse-fiddle-output
+    cp /tmp/fiddle_Raster.png /tmp/animation/image-"$i".png
+done
+cd /tmp/animation; ffmpeg -r $FPS -pattern_type glob -i '*.png' -c:v libvpx-vp9 -lossless 1 output.webm
diff --git a/tools/fiddle/draw.cpp b/tools/fiddle/draw.cpp
index 9eabd3b..8e94883 100644
--- a/tools/fiddle/draw.cpp
+++ b/tools/fiddle/draw.cpp
@@ -19,7 +19,7 @@
     canvas->clear(SK_ColorWHITE);
     SkMatrix matrix;
     matrix.setScale(0.75f, 0.75f);
-    matrix.preRotate(30.0f);
+    matrix.preRotate(frame * 30.0f * duration); // If an animation, rotate at 30 deg/s.
     SkPaint paint;
     paint.setShader(image->makeShader(SkShader::kRepeat_TileMode,
                                       SkShader::kRepeat_TileMode,
diff --git a/tools/fiddle/egl_context.cpp b/tools/fiddle/egl_context.cpp
new file mode 100644
index 0000000..107bb4f
--- /dev/null
+++ b/tools/fiddle/egl_context.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "fiddle_main.h"
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+static const EGLint configAttribs[] = {
+    EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+    EGL_BLUE_SIZE, 8,
+    EGL_GREEN_SIZE, 8,
+    EGL_RED_SIZE, 8,
+    EGL_DEPTH_SIZE, 8,
+    EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+    EGL_NONE
+};
+
+static const int pbufferWidth = 9;
+static const int pbufferHeight = 9;
+
+static const EGLint pbufferAttribs[] = {
+    EGL_WIDTH, pbufferWidth,
+    EGL_HEIGHT, pbufferHeight,
+    EGL_NONE,
+};
+
+// create_grcontext implementation for EGL.
+sk_sp<GrContext> create_grcontext(std::ostringstream &driverinfo) {
+    EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (EGL_NO_DISPLAY == eglDpy) {
+        return nullptr;
+    }
+
+    EGLint major, minor;
+    if (EGL_TRUE != eglInitialize(eglDpy, &major, &minor)) {
+        return nullptr;
+    }
+
+    EGLint numConfigs;
+    EGLConfig eglCfg;
+    if (EGL_TRUE != eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs)) {
+        return nullptr;
+    }
+
+    EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs);
+    if (EGL_NO_SURFACE == eglSurf) {
+        return nullptr;
+    }
+
+    if (EGL_TRUE != eglBindAPI(EGL_OPENGL_API)) {
+        return nullptr;
+    }
+
+    EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL);
+    if (EGL_NO_CONTEXT == eglCtx) {
+        return nullptr;
+    }
+    if (EGL_FALSE == eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx)) {
+        return nullptr;
+    }
+
+    driverinfo << "EGL " << major << "." << minor << "\n";
+    GrGLGetStringProc getString = (GrGLGetStringProc )eglGetProcAddress("glGetString");
+    driverinfo << "GL Versionr: " << getString(GL_VERSION) << "\n";
+    driverinfo << "GL Vendor: " << getString(GL_VENDOR) << "\n";
+    driverinfo << "GL Renderer: " << getString(GL_RENDERER) << "\n";
+    driverinfo << "GL Extensions: " << getString(GL_EXTENSIONS) << "\n";
+
+    auto interface = GrGLCreateNativeInterface();
+    if (!interface) {
+        return nullptr;
+    }
+    eglTerminate(eglDpy);
+
+    return sk_sp<GrContext>(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)interface));
+}
diff --git a/tools/fiddle/fiddle_main.cpp b/tools/fiddle/fiddle_main.cpp
index 62289d2..dbc0141 100644
--- a/tools/fiddle/fiddle_main.cpp
+++ b/tools/fiddle/fiddle_main.cpp
@@ -10,15 +10,25 @@
 #include <sstream>
 #include <string>
 
+#include "SkCommandLineFlags.h"
+
 #include "fiddle_main.h"
 
+DEFINE_double(duration, 1.0, "The total duration, in seconds, of the animation we are drawing.");
+DEFINE_double(frame, 1.0, "A double value in [0, 1] that specifies the point in animation to draw.");
+
 // Globals externed in fiddle_main.h
 SkBitmap source;
 sk_sp<SkImage> image;
+double duration; // The total duration of the animation in seconds.
+double frame;    // A value in [0, 1] of where we are in the animation.
 
 // Global used by the local impl of SkDebugf.
 std::ostringstream gTextOutput;
 
+// Global to record the GL driver info via create_grcontext().
+std::ostringstream gGLDriverInfo;
+
 void SkDebugf(const char * fmt, ...) {
     va_list args;
     va_start(args, fmt);
@@ -83,35 +93,10 @@
     return img ? img->encode() : nullptr;
 }
 
-#if defined(__linux) && !defined(__ANDROID__)
-    #include <GL/osmesa.h>
-    static sk_sp<GrContext> create_grcontext() {
-        // We just leak the OSMesaContext... the process will die soon anyway.
-        if (OSMesaContext osMesaContext = OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, nullptr)) {
-            static uint32_t buffer[16 * 16];
-            OSMesaMakeCurrent(osMesaContext, &buffer, GL_UNSIGNED_BYTE, 16, 16);
-        }
-
-        auto osmesa_get = [](void* ctx, const char name[]) {
-            SkASSERT(nullptr == ctx);
-            SkASSERT(OSMesaGetCurrentContext());
-            return OSMesaGetProcAddress(name);
-        };
-        sk_sp<const GrGLInterface> mesa(GrGLAssembleInterface(nullptr, osmesa_get));
-        if (!mesa) {
-            return nullptr;
-        }
-        return sk_sp<GrContext>(GrContext::Create(
-                                        kOpenGL_GrBackend,
-                                        reinterpret_cast<intptr_t>(mesa.get())));
-    }
-#else
-    static sk_sp<GrContext> create_grcontext() { return nullptr; }
-#endif
-
-
-
-int main() {
+int main(int argc, char** argv) {
+    SkCommandLineFlags::Parse(argc, argv);
+    duration = FLAGS_duration;
+    frame = FLAGS_frame;
     DrawOptions options = GetDrawOptions();
     // If textOnly then only do one type of image, otherwise the text
     // output is duplicated for each type.
@@ -155,7 +140,7 @@
         rasterData.reset(encode_snapshot(rasterSurface));
     }
     if (options.gpu) {
-        auto grContext = create_grcontext();
+        auto grContext = create_grcontext(gGLDriverInfo);
         if (!grContext) {
             fputs("Unable to get GrContext.\n", stderr);
         } else {
@@ -193,14 +178,16 @@
 
     printf("{\n");
     if (!options.textOnly) {
-        dump_output(rasterData, "Raster", !gpuData && !pdfData && !skpData);
-        dump_output(gpuData, "Gpu", !pdfData && !skpData);
-        dump_output(pdfData, "Pdf", !skpData);
-        dump_output(skpData, "Skp");
+        dump_output(rasterData, "Raster", false);
+        dump_output(gpuData, "Gpu", false);
+        dump_output(pdfData, "Pdf", false);
+        dump_output(skpData, "Skp", false);
     } else {
         std::string textoutput = gTextOutput.str();
-        dump_output(textoutput.c_str(), textoutput.length(), "Text");
+        dump_output(textoutput.c_str(), textoutput.length(), "Text", false);
     }
+    std::string glinfo = gGLDriverInfo.str();
+    dump_output(glinfo.c_str(), glinfo.length(), "GLInfo", true);
     printf("}\n");
 
     return 0;
diff --git a/tools/fiddle/fiddle_main.h b/tools/fiddle/fiddle_main.h
index 078c26c..fb9d409 100644
--- a/tools/fiddle/fiddle_main.h
+++ b/tools/fiddle/fiddle_main.h
@@ -20,8 +20,12 @@
     #include "skia.h"
 #endif
 
+#include <sstream>
+
 extern SkBitmap source;
 extern sk_sp<SkImage> image;
+extern double duration; // The total duration of the animation in seconds.
+extern double frame;    // A value in [0, 1] of where we are in the animation.
 
 struct DrawOptions {
     DrawOptions(int w, int h, bool r, bool g, bool p, bool k, bool srgb, bool f16, bool textOnly, const char* s)
@@ -53,4 +57,8 @@
 extern void SkDebugf(const char * format, ...);
 extern void draw(SkCanvas*);
 
+// There are different implementations of create_grcontext() for EGL, Mesa,
+// and a fallback to a null context.
+extern sk_sp<GrContext> create_grcontext(std::ostringstream &driverinfo);
+
 #endif  // fiddle_main_DEFINED
diff --git a/tools/fiddle/mesa_context.cpp b/tools/fiddle/mesa_context.cpp
new file mode 100644
index 0000000..3b3726b
--- /dev/null
+++ b/tools/fiddle/mesa_context.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "fiddle_main.h"
+
+#include <GL/osmesa.h>
+
+// create_grcontext implementation for Mesa.
+sk_sp<GrContext> create_grcontext(std::ostringstream &driverinfo) {
+    // We just leak the OSMesaContext... the process will die soon anyway.
+    if (OSMesaContext osMesaContext = OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, nullptr)) {
+        static uint32_t buffer[16 * 16];
+        OSMesaMakeCurrent(osMesaContext, &buffer, GL_UNSIGNED_BYTE, 16, 16);
+    }
+    driverinfo << "Mesa";
+
+    auto osmesa_get = [](void* ctx, const char name[]) {
+        SkASSERT(nullptr == ctx);
+        SkASSERT(OSMesaGetCurrentContext());
+        return OSMesaGetProcAddress(name);
+    };
+    sk_sp<const GrGLInterface> mesa(GrGLAssembleInterface(nullptr, osmesa_get));
+    if (!mesa) {
+        return nullptr;
+    }
+    return sk_sp<GrContext>(GrContext::Create(
+                kOpenGL_GrBackend,
+                reinterpret_cast<intptr_t>(mesa.get())));
+}
diff --git a/tools/fiddle/null_context.cpp b/tools/fiddle/null_context.cpp
new file mode 100644
index 0000000..3fe0054
--- /dev/null
+++ b/tools/fiddle/null_context.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "fiddle_main.h"
+
+// create_grcontext for when neither Mesa nor EGL are available.
+sk_sp<GrContext> create_grcontext(std::ostringstream &driverinfo) {
+    driverinfo << "(no GL driver available)";
+    return nullptr;
+}
diff --git a/tools/fiddle/parse-fiddle-output b/tools/fiddle/parse-fiddle-output
index f9a2fac..f44ff64 100755
--- a/tools/fiddle/parse-fiddle-output
+++ b/tools/fiddle/parse-fiddle-output
@@ -9,9 +9,10 @@
     type=$(echo $line | sed -n 's/[^"]*"\([^"]*\)":.*/\1/p')
     if [ "$type" ]; then
         case "$type" in
-            Raster|Gpu) ext='.png';;
-            Pdf)        ext='.pdf';;
-            Skp)        ext='.skp';;
+            Raster|Gpu)  ext='.png';;
+            Pdf)         ext='.pdf';;
+            Skp)         ext='.skp';;
+            Text|GLInfo) ext='.txt';;
         esac
         dst="${TMPDIR:-/tmp}/fiddle_${type}${ext}"
         echo $line | sed 's/[^"]*"[^"]*": "//; s/"\(,\|\)$//' \
diff --git a/tools/flags/SkCommonFlagsConfig.cpp b/tools/flags/SkCommonFlagsConfig.cpp
index 1e980d2..458962e 100644
--- a/tools/flags/SkCommonFlagsConfig.cpp
+++ b/tools/flags/SkCommonFlagsConfig.cpp
@@ -64,6 +64,7 @@
     { "glsrgb",                "gpu", "api=gl,color=srgb" },
     { "glwide",                "gpu", "api=gl,color=f16_wide" },
     { "glnarrow",              "gpu", "api=gl,color=f16_narrow" },
+    { "glnostencils",          "gpu", "api=gl,stencils=false" },
     { "glessrgb",              "gpu", "api=gles,color=srgb" },
     { "gleswide",              "gpu", "api=gles,color=f16_wide" },
     { "glesnarrow",            "gpu", "api=gles,color=f16_narrow" },
@@ -151,6 +152,8 @@
     "\t    Use NV_path_rendering OpenGL and OpenGL ES extension.\n"
     "\tsamples\ttype: int\tdefault: 0.\n"
     "\t    Use multisampling with N samples.\n"
+    "\tstencils\ttype: bool\tdefault: true.\n"
+    "\t    Allow the use of stencil buffers.\n"
     "\n"
     "Predefined configs:\n\n"
     // Help text for pre-defined configs is auto-generated from gPredefinedConfigs
@@ -181,7 +184,7 @@
 SkCommandLineConfigGpu::SkCommandLineConfigGpu(
     const SkString& tag, const SkTArray<SkString>& viaParts, ContextType contextType, bool useNVPR,
     bool useInstanced, bool useDIText, int samples, SkColorType colorType,
-    sk_sp<SkColorSpace> colorSpace)
+    sk_sp<SkColorSpace> colorSpace, bool useStencilBuffers)
         : SkCommandLineConfig(tag, SkString("gpu"), viaParts)
         , fContextType(contextType)
         , fContextOverrides(ContextOverrides::kNone)
@@ -210,6 +213,9 @@
         fContextOverrides |= ContextOverrides::kRequireSRGBSupport;
         fContextOverrides |= ContextOverrides::kAllowSRGBWithoutDecodeControl;
     }
+    if (!useStencilBuffers) {
+        fContextOverrides |= ContextOverrides::kAvoidStencilBuffers;
+    }
 }
 static bool parse_option_int(const SkString& value, int* outInt) {
     if (value.isEmpty()) {
@@ -370,6 +376,8 @@
     bool seenColor = false;
     SkColorType colorType = kRGBA_8888_SkColorType;
     sk_sp<SkColorSpace> colorSpace = nullptr;
+    bool seenUseStencils = false;
+    bool useStencils = true;
 
     SkTArray<SkString> optionParts;
     SkStrSplit(options.c_str(), ",", kStrict_SkStrSplitMode, &optionParts);
@@ -400,6 +408,9 @@
         } else if (key.equals("color") && !seenColor) {
             valueOk = parse_option_gpu_color(value, &colorType, &colorSpace);
             seenColor = true;
+        } else if (key.equals("stencils") && !seenUseStencils) {
+            valueOk = parse_option_bool(value, &useStencils);
+            seenUseStencils = true;
         }
         if (!valueOk) {
             return nullptr;
@@ -409,7 +420,7 @@
         return nullptr;
     }
     return new SkCommandLineConfigGpu(tag, vias, contextType, useNVPR, useInstanced, useDIText,
-                                      samples, colorType, colorSpace);
+                                      samples, colorType, colorSpace, useStencils);
 }
 #endif
 
diff --git a/tools/flags/SkCommonFlagsConfig.h b/tools/flags/SkCommonFlagsConfig.h
index e6ebefd..a3c3b38 100644
--- a/tools/flags/SkCommonFlagsConfig.h
+++ b/tools/flags/SkCommonFlagsConfig.h
@@ -55,7 +55,8 @@
     typedef sk_gpu_test::GrContextFactory::ContextOverrides ContextOverrides;
     SkCommandLineConfigGpu(const SkString& tag, const SkTArray<SkString>& viaParts,
                            ContextType contextType, bool useNVPR, bool useInstanced, bool useDIText,
-                           int samples, SkColorType colorType, sk_sp<SkColorSpace> colorSpace);
+                           int samples, SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
+                           bool useStencilBuffers);
     const SkCommandLineConfigGpu* asConfigGpu() const override { return this; }
     ContextType getContextType() const { return fContextType; }
     ContextOverrides getContextOverrides() const { return fContextOverrides; }
diff --git a/tools/get_images_from_skps.cpp b/tools/get_images_from_skps.cpp
index c413c6d..11faa42 100644
--- a/tools/get_images_from_skps.cpp
+++ b/tools/get_images_from_skps.cpp
@@ -23,7 +23,7 @@
 #include <iostream>
 #include <map>
 
-DEFINE_string2(skps, s, "skps", "A path to a directory of skps.");
+DEFINE_string2(skps, s, "skps", "A path to a directory of skps or a single skp.");
 DEFINE_string2(out, o, "img-out", "A path to an output directory.");
 DEFINE_bool(testDecode, false, "Indicates if we want to test that the images decode successfully.");
 DEFINE_bool(writeImages, true,
@@ -142,6 +142,14 @@
     SkData* onEncode(const SkPixmap&) override { return nullptr; }
 };
 
+static void get_images_from_file(const SkString& file) {
+    auto stream = SkStream::MakeFromFile(file.c_str());
+    sk_sp<SkPicture> picture(SkPicture::MakeFromStream(stream.get()));
+
+    SkDynamicMemoryWStream scratch;
+    Sniffer sniff(file.c_str());
+    picture->serialize(&scratch, &sniff);
+}
 
 int main(int argc, char** argv) {
     SkCommandLineFlags::SetUsage(
@@ -152,7 +160,7 @@
     const char* inputs = FLAGS_skps[0];
     gOutputDir = FLAGS_out[0];
 
-    if (!sk_isdir(inputs) || !sk_isdir(gOutputDir)) {
+    if (!sk_isdir(gOutputDir)) {
         SkCommandLineFlags::PrintUsage();
         return 1;
     }
@@ -163,15 +171,13 @@
     }
 #endif
 
-    SkOSFile::Iter iter(inputs, "skp");
-    for (SkString file; iter.next(&file); ) {
-        std::unique_ptr<SkStream> stream =
-                SkStream::MakeFromFile(SkOSPath::Join(inputs, file.c_str()).c_str());
-        sk_sp<SkPicture> picture(SkPicture::MakeFromStream(stream.get()));
-
-        SkDynamicMemoryWStream scratch;
-        Sniffer sniff(file.c_str());
-        picture->serialize(&scratch, &sniff);
+    if (sk_isdir(inputs)) {
+        SkOSFile::Iter iter(inputs, "skp");
+        for (SkString file; iter.next(&file); ) {
+            get_images_from_file(SkOSPath::Join(inputs, file.c_str()));
+        }
+    } else {
+        get_images_from_file(SkString(inputs));
     }
     /**
      JSON results are written out in the following format:
diff --git a/tools/gpu/GrContextFactory.cpp b/tools/gpu/GrContextFactory.cpp
index 6ed8954..0238898 100644
--- a/tools/gpu/GrContextFactory.cpp
+++ b/tools/gpu/GrContextFactory.cpp
@@ -162,9 +162,11 @@
                                                  glShareContext).release();
                     break;
 #endif
+#ifndef SK_NO_COMMAND_BUFFER
                 case kCommandBuffer_ContextType:
                     glCtx = CommandBufferGLTestContext::Create(glShareContext);
                     break;
+#endif
 #if SK_MESA
                 case kMESA_ContextType:
                     glCtx = CreateMesaGLTestContext(glShareContext);
@@ -189,16 +191,14 @@
             break;
         }
 #ifdef SK_VULKAN
-        case kVulkan_GrBackend:
-            if (masterContext) {
-                // Shared contexts not supported yet
-                return ContextInfo();
-            }
+        case kVulkan_GrBackend: {
+            VkTestContext* vkSharedContext = masterContext
+                    ? static_cast<VkTestContext*>(masterContext->fTestContext) : nullptr;
             SkASSERT(kVulkan_ContextType == type);
             if (ContextOverrides::kRequireNVPRSupport & overrides) {
                 return ContextInfo();
             }
-            testCtx.reset(CreatePlatformVkTestContext());
+            testCtx.reset(CreatePlatformVkTestContext(vkSharedContext));
             if (!testCtx) {
                 return ContextInfo();
             }
@@ -214,6 +214,7 @@
             }
             backendContext = testCtx->backendContext();
             break;
+        }
 #endif
         default:
             return ContextInfo();
@@ -230,6 +231,9 @@
     if (ContextOverrides::kAllowSRGBWithoutDecodeControl & overrides) {
         grOptions.fRequireDecodeDisableForSRGB = false;
     }
+    if (ContextOverrides::kAvoidStencilBuffers & overrides) {
+        grOptions.fAvoidStencilBuffers = true;
+    }
     sk_sp<GrContext> grCtx(GrContext::Create(backend, backendContext, grOptions));
     if (!grCtx.get()) {
         return ContextInfo();
diff --git a/tools/gpu/GrContextFactory.h b/tools/gpu/GrContextFactory.h
index 0067e52..eb54b50 100644
--- a/tools/gpu/GrContextFactory.h
+++ b/tools/gpu/GrContextFactory.h
@@ -94,9 +94,10 @@
         kDisableNVPR                   = 0x1,
         kUseInstanced                  = 0x2,
         kAllowSRGBWithoutDecodeControl = 0x4,
+        kAvoidStencilBuffers           = 0x8,
 
-        kRequireNVPRSupport            = 0x8,
-        kRequireSRGBSupport            = 0x10
+        kRequireNVPRSupport            = 0x10,
+        kRequireSRGBSupport            = 0x20,
     };
 
     static bool IsRenderingContext(ContextType type) {
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index 28c1738..32f2d76 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -6,22 +6,25 @@
  */
 
 #include "GrTest.h"
-
+#include <algorithm>
+#include "GrBackendSurface.h"
 #include "GrContextOptions.h"
+#include "GrContextPriv.h"
 #include "GrDrawOpAtlas.h"
 #include "GrDrawingManager.h"
 #include "GrGpuResourceCacheAccess.h"
 #include "GrPipelineBuilder.h"
+#include "GrRenderTargetContext.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrRenderTargetProxy.h"
 #include "GrResourceCache.h"
 #include "GrSemaphore.h"
-
+#include "GrSurfaceContextPriv.h"
 #include "SkGr.h"
 #include "SkImage_Gpu.h"
 #include "SkMathPriv.h"
 #include "SkString.h"
-
+#include "ops/GrMeshDrawOp.h"
 #include "text/GrAtlasGlyphCache.h"
 #include "text/GrTextBlobCache.h"
 
@@ -54,6 +57,18 @@
 
     context->setTextContextAtlasSizes_ForTesting(configs);
 }
+
+GrBackendTexture CreateBackendTexture(GrBackend backend, int width, int height,
+                                      GrPixelConfig config, GrBackendObject handle) {
+    if (kOpenGL_GrBackend == backend) {
+        GrGLTextureInfo* glInfo = (GrGLTextureInfo*)(handle);
+        return GrBackendTexture(width, height, config, *glInfo);
+    } else {
+        SkASSERT(kVulkan_GrBackend == backend);
+        GrVkImageInfo* vkInfo = (GrVkImageInfo*)(handle);
+        return GrBackendTexture(width, height, *vkInfo);
+    }
+}
 };
 
 bool GrSurfaceProxy::isWrapped_ForTesting() const {
@@ -226,20 +241,21 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #define ASSERT_SINGLE_OWNER \
-    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fRenderTargetContext->fSingleOwner);)
+    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fRenderTargetContext->singleOwner());)
 
-uint32_t GrRenderTargetContextPriv::testingOnly_addMeshDrawOp(GrPaint&& paint,
-                                                              GrAAType aaType,
-                                                              std::unique_ptr<GrMeshDrawOp> op,
-                                                              const GrUserStencilSettings* uss,
-                                                              bool snapToCenters) {
+uint32_t GrRenderTargetContextPriv::testingOnly_addLegacyMeshDrawOp(
+        GrPaint&& paint,
+        GrAAType aaType,
+        std::unique_ptr<GrLegacyMeshDrawOp> op,
+        const GrUserStencilSettings* uss,
+        bool snapToCenters) {
     ASSERT_SINGLE_OWNER
     if (fRenderTargetContext->drawingManager()->wasAbandoned()) {
         return SK_InvalidUniqueID;
     }
-    SkDEBUGCODE(fRenderTargetContext->validate();)
+    SkDEBUGCODE(fRenderTargetContext->validate());
     GR_AUDIT_TRAIL_AUTO_FRAME(fRenderTargetContext->fAuditTrail,
-                              "GrRenderTargetContext::testingOnly_addMeshDrawOp");
+                              "GrRenderTargetContext::testingOnly_addLegacyMeshDrawOp");
 
     GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
     if (uss) {
@@ -247,7 +263,19 @@
     }
     pipelineBuilder.setSnapVerticesToPixelCenters(snapToCenters);
 
-    return fRenderTargetContext->addMeshDrawOp(pipelineBuilder, GrNoClip(), std::move(op));
+    return fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), GrNoClip(),
+                                                     std::move(op));
+}
+
+uint32_t GrRenderTargetContextPriv::testingOnly_addDrawOp(std::unique_ptr<GrDrawOp> op) {
+    ASSERT_SINGLE_OWNER
+    if (fRenderTargetContext->drawingManager()->wasAbandoned()) {
+        return SK_InvalidUniqueID;
+    }
+    SkDEBUGCODE(fRenderTargetContext->validate());
+    GR_AUDIT_TRAIL_AUTO_FRAME(fRenderTargetContext->fAuditTrail,
+                              "GrRenderTargetContext::testingOnly_addDrawOp");
+    return fRenderTargetContext->addDrawOp(GrNoClip(), std::move(op));
 }
 
 #undef ASSERT_SINGLE_OWNER
@@ -269,10 +297,11 @@
 class MockCaps : public GrCaps {
 public:
     explicit MockCaps(const GrContextOptions& options) : INHERITED(options) {}
-    bool isConfigTexturable(GrPixelConfig config) const override { return false; }
+    bool isConfigTexturable(GrPixelConfig) const override { return false; }
     bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const override { return false; }
     bool canConfigBeImageStorage(GrPixelConfig) const override { return false; }
-    bool initDescForDstCopy(const GrRenderTarget* src, GrSurfaceDesc* desc) const override {
+    bool initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* desc,
+                            bool* rectsMustMatch, bool* disallowSubrect) const override {
         return false;
     }
 
@@ -302,7 +331,7 @@
 
     void onQueryMultisampleSpecs(GrRenderTarget* rt, const GrStencilSettings&,
                                  int* effectiveSampleCnt, SamplePattern*) override {
-        *effectiveSampleCnt = rt->desc().fSampleCnt;
+        *effectiveSampleCnt = rt->numStencilSamples();
     }
 
     GrGpuCommandBuffer* createCommandBuffer(const GrGpuCommandBuffer::LoadAndStoreInfo&,
@@ -310,41 +339,41 @@
         return nullptr;
     }
 
-    void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {}
-
     GrFence SK_WARN_UNUSED_RESULT insertFence() override { return 0; }
     bool waitFence(GrFence, uint64_t) override { return true; }
     void deleteFence(GrFence) const override {}
-    void flush() override {}
 
     sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override { return nullptr; }
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override {}
+    void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override {}
     void waitSemaphore(sk_sp<GrSemaphore> semaphore) override {}
+    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override { return nullptr; }
 
 private:
     void onResetContext(uint32_t resetBits) override {}
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
-    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                               const SkTArray<GrMipLevel>& texels) override {
+    sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
+                                     const SkTArray<GrMipLevel>& texels) override {
         return nullptr;
     }
 
-    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
-                                         const SkTArray<GrMipLevel>& texels) override {
+    sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
+                                          GrSurfaceOrigin,
+                                          GrBackendTextureFlags,
+                                          int sampleCnt,
+                                          GrWrapOwnership) override {
         return nullptr;
     }
 
-    sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) override {
+    sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTarget&,
+                                                    GrSurfaceOrigin) override {
         return nullptr;
     }
 
-    sk_sp<GrRenderTarget> onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) override {
-        return nullptr;
-    }
-
-    sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&) override {
+    sk_sp<GrRenderTarget> onWrapBackendTextureAsRenderTarget(const GrBackendTexture&,
+                                                             GrSurfaceOrigin,
+                                                             int sampleCnt) override {
         return nullptr;
     }
 
@@ -415,3 +444,101 @@
     // resources in the buffer pools.
     fDrawingManager->abandon();
 }
+
+//////////////////////////////////////////////////////////////////////////////
+
+void GrContextPriv::testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject* cb) {
+    fContext->flush();
+    fContext->fDrawingManager->testingOnly_removeOnFlushCallbackObject(cb);
+}
+
+void GrDrawingManager::testingOnly_removeOnFlushCallbackObject(GrOnFlushCallbackObject* cb) {
+    int n = std::find(fOnFlushCBObjects.begin(), fOnFlushCBObjects.end(), cb) -
+            fOnFlushCBObjects.begin();
+    SkASSERT(n < fOnFlushCBObjects.count());
+    fOnFlushCBObjects.removeShuffle(n);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define DRAW_OP_TEST_EXTERN(Op) \
+    extern std::unique_ptr<GrDrawOp> Op##__Test(GrPaint&&, SkRandom*, GrContext*, GrFSAAType);
+
+#define LEGACY_MESH_DRAW_OP_TEST_EXTERN(Op) \
+    extern std::unique_ptr<GrLegacyMeshDrawOp> Op##__Test(SkRandom*, GrContext*);
+
+#define DRAW_OP_TEST_ENTRY(Op) Op##__Test
+
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAConvexPathOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAFillRectOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAFillRectOpLocalMatrix);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAFlatteningConvexPathOp)
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAHairlineOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAStrokeRectOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(AnalyticRectOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(DashOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(DefaultPathOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(GrDrawAtlasOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(NonAAStrokeRectOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(SmallPathOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(TesselatingPathOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(TextBlobOp);
+LEGACY_MESH_DRAW_OP_TEST_EXTERN(VerticesOp);
+
+DRAW_OP_TEST_EXTERN(CircleOp)
+DRAW_OP_TEST_EXTERN(DIEllipseOp);
+DRAW_OP_TEST_EXTERN(EllipseOp);
+DRAW_OP_TEST_EXTERN(NonAAFillRectOp)
+DRAW_OP_TEST_EXTERN(RRectOp);
+
+void GrDrawRandomOp(SkRandom* random, GrRenderTargetContext* renderTargetContext, GrPaint&& paint) {
+    GrContext* context = renderTargetContext->surfPriv().getContext();
+    using MakeTestLegacyMeshDrawOpFn = std::unique_ptr<GrLegacyMeshDrawOp>(SkRandom*, GrContext*);
+    static constexpr MakeTestLegacyMeshDrawOpFn* gLegacyFactories[] = {
+        DRAW_OP_TEST_ENTRY(AAConvexPathOp),
+        DRAW_OP_TEST_ENTRY(AAFillRectOp),
+        DRAW_OP_TEST_ENTRY(AAFillRectOpLocalMatrix),
+        DRAW_OP_TEST_ENTRY(AAFlatteningConvexPathOp),
+        DRAW_OP_TEST_ENTRY(AAHairlineOp),
+        DRAW_OP_TEST_ENTRY(AAStrokeRectOp),
+        DRAW_OP_TEST_ENTRY(AnalyticRectOp),
+        DRAW_OP_TEST_ENTRY(DashOp),
+        DRAW_OP_TEST_ENTRY(DefaultPathOp),
+        DRAW_OP_TEST_ENTRY(GrDrawAtlasOp),
+        DRAW_OP_TEST_ENTRY(NonAAStrokeRectOp),
+        DRAW_OP_TEST_ENTRY(SmallPathOp),
+        DRAW_OP_TEST_ENTRY(TesselatingPathOp),
+        DRAW_OP_TEST_ENTRY(TextBlobOp),
+        DRAW_OP_TEST_ENTRY(VerticesOp)
+    };
+
+    using MakeDrawOpFn = std::unique_ptr<GrDrawOp>(GrPaint&&, SkRandom*, GrContext*, GrFSAAType);
+    static constexpr MakeDrawOpFn* gFactories[] = {
+        DRAW_OP_TEST_ENTRY(CircleOp),
+        DRAW_OP_TEST_ENTRY(DIEllipseOp),
+        DRAW_OP_TEST_ENTRY(EllipseOp),
+        DRAW_OP_TEST_ENTRY(NonAAFillRectOp),
+        DRAW_OP_TEST_ENTRY(RRectOp),
+    };
+
+    static constexpr size_t kTotal = SK_ARRAY_COUNT(gLegacyFactories) + SK_ARRAY_COUNT(gFactories);
+
+    uint32_t index = random->nextULessThan(static_cast<uint32_t>(kTotal));
+    if (index < SK_ARRAY_COUNT(gLegacyFactories)) {
+        const GrUserStencilSettings* uss = GrGetRandomStencil(random, context);
+        // We don't use kHW because we will hit an assertion if the render target is not
+        // multisampled
+        static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kCoverage};
+        GrAAType aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))];
+        bool snapToCenters = random->nextBool();
+        auto op = gLegacyFactories[index](random, context);
+        SkASSERT(op);
+        renderTargetContext->priv().testingOnly_addLegacyMeshDrawOp(
+                std::move(paint), aaType, std::move(op), uss, snapToCenters);
+    } else {
+        auto op = gFactories[index - SK_ARRAY_COUNT(gLegacyFactories)](
+                std::move(paint), random, context, renderTargetContext->fsaaType());
+        SkASSERT(op);
+        renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
+    }
+}
diff --git a/tools/gpu/GrTest.h b/tools/gpu/GrTest.h
index db4c451..d4a4c6d 100644
--- a/tools/gpu/GrTest.h
+++ b/tools/gpu/GrTest.h
@@ -8,6 +8,7 @@
 #ifndef GrTest_DEFINED
 #define GrTest_DEFINED
 
+#include "GrBackendSurface.h"
 #include "GrContext.h"
 
 namespace GrTest {
@@ -16,6 +17,9 @@
      * constantly be evicting entries
      */
     void SetupAlwaysEvictAtlas(GrContext*);
+
+    GrBackendTexture CreateBackendTexture(GrBackend, int width, int height,
+                                          GrPixelConfig, GrBackendObject);
 };
 
 #endif
diff --git a/tools/gpu/gl/GLTestContext.cpp b/tools/gpu/gl/GLTestContext.cpp
index f677193..abbb8a6 100644
--- a/tools/gpu/gl/GLTestContext.cpp
+++ b/tools/gpu/gl/GLTestContext.cpp
@@ -51,10 +51,13 @@
         }
         ret.reset(new GLFenceSync(ctx));
     } else {
-        if (!ctx->gl()->hasExtension("GL_APPLE_sync")) {
+        if (ctx->gl()->hasExtension("GL_APPLE_sync")) {
+            ret.reset(new GLFenceSync(ctx, "APPLE"));
+        } else if (GrGLGetVersion(ctx->gl()) >= GR_GL_VER(3, 0)) {
+            ret.reset(new GLFenceSync(ctx));
+        } else {
             return nullptr;
         }
-        ret.reset(new GLFenceSync(ctx, "APPLE"));
     }
     if (!ret->validate()) {
         ret = nullptr;
diff --git a/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp b/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp
index 82888d8..54845fc 100644
--- a/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp
+++ b/tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp
@@ -6,6 +6,8 @@
  * found in the LICENSE file.
  */
 
+#ifndef SK_NO_COMMAND_BUFFER
+
 #include "SkMutex.h"
 #include "SkOnce.h"
 #include "gl/GrGLInterface.h"
@@ -302,3 +304,4 @@
 }
 
 }  // namespace sk_gpu_test
+#endif // SK_NO_COMMAND_BUFFER
diff --git a/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp b/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp
index d41adff..c006098 100644
--- a/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp
+++ b/tools/gpu/gl/egl/CreatePlatformGLTestContext_egl.cpp
@@ -10,7 +10,6 @@
 #define GL_GLEXT_PROTOTYPES
 #include <GLES2/gl2.h>
 
-#define EGL_EGLEXT_PROTOTYPES
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
@@ -29,7 +28,11 @@
     void deleteFence(sk_gpu_test::PlatformFence fence) const override;
 
 private:
-    EGLFenceSync(EGLDisplay display) : fDisplay(display) {}
+    EGLFenceSync(EGLDisplay display);
+
+    PFNEGLCREATESYNCKHRPROC       fEGLCreateSyncKHR;
+    PFNEGLCLIENTWAITSYNCKHRPROC   fEGLClientWaitSyncKHR;
+    PFNEGLDESTROYSYNCKHRPROC      fEGLDestroySyncKHR;
 
     EGLDisplay                    fDisplay;
 
@@ -234,6 +237,7 @@
     if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
         return 0;
     }
+#ifndef EGL_NO_IMAGE_EXTERNAL
     typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);
 
     EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
@@ -257,6 +261,9 @@
         return 0;
     }
     return texID;
+#else
+    return 0;
+#endif //EGL_NO_IMAGE_EXTERNAL
 }
 
 std::unique_ptr<sk_gpu_test::GLTestContext> EGLGLTestContext::makeNew() const {
@@ -306,23 +313,31 @@
     return std::unique_ptr<EGLFenceSync>(new EGLFenceSync(display));
 }
 
+EGLFenceSync::EGLFenceSync(EGLDisplay display)
+    : fDisplay(display) {
+    fEGLCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
+    fEGLClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR");
+    fEGLDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
+    SkASSERT(fEGLCreateSyncKHR && fEGLClientWaitSyncKHR && fEGLDestroySyncKHR);
+}
+
 sk_gpu_test::PlatformFence EGLFenceSync::insertFence() const {
-    EGLSyncKHR eglsync = eglCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+    EGLSyncKHR eglsync = fEGLCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, nullptr);
     return reinterpret_cast<sk_gpu_test::PlatformFence>(eglsync);
 }
 
 bool EGLFenceSync::waitFence(sk_gpu_test::PlatformFence platformFence) const {
     EGLSyncKHR eglsync = reinterpret_cast<EGLSyncKHR>(platformFence);
     return EGL_CONDITION_SATISFIED_KHR ==
-            eglClientWaitSyncKHR(fDisplay,
-                                 eglsync,
-                                 EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
-                                 EGL_FOREVER_KHR);
+            fEGLClientWaitSyncKHR(fDisplay,
+                                  eglsync,
+                                  EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+                                  EGL_FOREVER_KHR);
 }
 
 void EGLFenceSync::deleteFence(sk_gpu_test::PlatformFence platformFence) const {
     EGLSyncKHR eglsync = reinterpret_cast<EGLSyncKHR>(platformFence);
-    eglDestroySyncKHR(fDisplay, eglsync);
+    fEGLDestroySyncKHR(fDisplay, eglsync);
 }
 
 GR_STATIC_ASSERT(sizeof(EGLSyncKHR) <= sizeof(sk_gpu_test::PlatformFence));
diff --git a/tools/gpu/vk/VkTestContext.cpp b/tools/gpu/vk/VkTestContext.cpp
index 502dea8..125ead2 100644
--- a/tools/gpu/vk/VkTestContext.cpp
+++ b/tools/gpu/vk/VkTestContext.cpp
@@ -108,8 +108,14 @@
 // TODO: Implement swap buffers and finish
 class VkTestContextImpl : public sk_gpu_test::VkTestContext {
 public:
-    static VkTestContext* Create() {
-        sk_sp<const GrVkBackendContext> backendContext(GrVkBackendContext::Create());
+    static VkTestContext* Create(VkTestContext* sharedContext) {
+        sk_sp<const GrVkBackendContext> backendContext;
+        if (sharedContext) {
+            backendContext = sharedContext->getVkBackendContext();
+        } else {
+            backendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr,
+                                                            vkGetDeviceProcAddr));
+        }
         if (!backendContext) {
             return nullptr;
         }
@@ -146,7 +152,9 @@
 }  // anonymous namespace
 
 namespace sk_gpu_test {
-VkTestContext* CreatePlatformVkTestContext() { return VkTestContextImpl::Create(); }
+VkTestContext* CreatePlatformVkTestContext(VkTestContext* sharedContext) {
+    return VkTestContextImpl::Create(sharedContext);
+}
 }  // namespace sk_gpu_test
 
 #endif
diff --git a/tools/gpu/vk/VkTestContext.h b/tools/gpu/vk/VkTestContext.h
index ecec17b..85acb0e 100644
--- a/tools/gpu/vk/VkTestContext.h
+++ b/tools/gpu/vk/VkTestContext.h
@@ -22,6 +22,10 @@
         return reinterpret_cast<GrBackendContext>(fVk.get());
     }
 
+    sk_sp<const GrVkBackendContext> getVkBackendContext() {
+        return fVk;
+    }
+
     bool isValid() const override { return NULL != this->vk(); }
 
     const GrVkInterface* vk() const { return fVk->fInterface.get(); }
@@ -38,7 +42,7 @@
 /**
  * Creates Vk context object bound to the native Vk library.
  */
-VkTestContext* CreatePlatformVkTestContext();
+VkTestContext* CreatePlatformVkTestContext(VkTestContext*);
 
 }  // namespace sk_gpu_test
 
diff --git a/tools/monobench.cpp b/tools/monobench.cpp
index fa0de8a..ff0abc2 100644
--- a/tools/monobench.cpp
+++ b/tools/monobench.cpp
@@ -8,6 +8,7 @@
 #include "Benchmark.h"
 #include "OverwriteLine.h"
 #include "SkGraphics.h"
+#include "SkSurface.h"
 #include "SkTaskGroup.h"
 #include <algorithm>
 #include <chrono>
@@ -48,7 +49,8 @@
 
         std::string name = bench->getName();
         if (std::regex_search(name, pattern) &&
-                bench->isSuitableFor(Benchmark::kNonRendering_Backend)) {
+                (bench->isSuitableFor(Benchmark::kNonRendering_Backend) ||
+                 bench->isSuitableFor(Benchmark::      kRaster_Backend))) {
             bench->delayedSetup();
             benches.emplace_back(Bench{std::move(bench), name,
                                        ns{std::numeric_limits<double>::infinity()}});
@@ -96,12 +98,22 @@
     while (samples < limit) {
         std::random_shuffle(benches.begin(), benches.end());
         for (auto& bench : benches) {
+            sk_sp<SkSurface> dst;
+            SkCanvas* canvas = nullptr;
+            if (!bench.b->isSuitableFor(Benchmark::kNonRendering_Backend)) {
+                dst = SkSurface::MakeRaster(SkImageInfo::MakeS32(bench.b->getSize().x(),
+                                                                 bench.b->getSize().y(),
+                                                                 kPremul_SkAlphaType));
+                canvas = dst->getCanvas();
+                bench.b->perCanvasPreDraw(canvas);
+            }
             for (int loops = 1; loops < 1000000000;) {
-                bench.b->preDraw(nullptr);
+
+                bench.b->preDraw(canvas);
                 auto start = clock::now();
-                    bench.b->draw(loops, nullptr);
+                    bench.b->draw(loops, canvas);
                 ns elapsed = clock::now() - start;
-                bench.b->postDraw(nullptr);
+                bench.b->postDraw(canvas);
 
                 if (elapsed < std::chrono::milliseconds{10}) {
                     loops *= 2;
@@ -131,6 +143,9 @@
                 }
                 break;
             }
+            if (canvas) {
+                bench.b->perCanvasPostDraw(canvas);
+            }
         }
     }
 
diff --git a/tools/ok.cpp b/tools/ok.cpp
index 9022857..edda9be 100644
--- a/tools/ok.cpp
+++ b/tools/ok.cpp
@@ -10,79 +10,92 @@
 //   * ok is entirely opt-in.  No more maintaining huge --blacklists.
 
 #include "SkGraphics.h"
-#include "SkOSFile.h"
+#include "SkImage.h"
 #include "ok.h"
 #include <chrono>
 #include <future>
 #include <list>
-#include <regex>
 #include <stdio.h>
 #include <stdlib.h>
 #include <thread>
+#include <vector>
 
 #if !defined(__has_include)
     #define  __has_include(x) 0
 #endif
 
-static thread_local const char* tls_name = "";
+static thread_local const char* tls_currently_running = "";
 
 #if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
     #include <execinfo.h>
     #include <fcntl.h>
     #include <signal.h>
 
-    static int crash_stacktrace_fd = 2/*stderr*/;
+    static int log_fd = 2/*stderr*/;
+
+    static void log(const char* msg) {
+        write(log_fd, msg, strlen(msg));
+    }
 
     static void setup_crash_handler() {
         static void (*original_handlers[32])(int);
-
         for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
             original_handlers[sig] = signal(sig, [](int sig) {
-                // To prevent interlaced output, lock the stacktrace log file until we die.
-                lockf(crash_stacktrace_fd, F_LOCK, 0);
+                lockf(log_fd, F_LOCK, 0);
+                    log("\ncaught signal ");
+                    switch (sig) {
+                    #define CASE(s) case s: log(#s); break
+                        CASE(SIGABRT);
+                        CASE(SIGBUS);
+                        CASE(SIGFPE);
+                        CASE(SIGILL);
+                        CASE(SIGSEGV);
+                    #undef CASE
+                    }
+                    log(" while running '");
+                    log(tls_currently_running);
+                    log("'\n");
 
-                auto ez_write = [](const char* str) {
-                    write(crash_stacktrace_fd, str, strlen(str));
-                };
-                ez_write("\ncaught signal ");
-                switch (sig) {
-                #define CASE(s) case s: ez_write(#s); break
-                    CASE(SIGABRT);
-                    CASE(SIGBUS);
-                    CASE(SIGFPE);
-                    CASE(SIGILL);
-                    CASE(SIGSEGV);
-                #undef CASE
-                }
-                ez_write(" while running '");
-                ez_write(tls_name);
-                ez_write("'\n");
+                    void* stack[128];
+                    int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
+                    backtrace_symbols_fd(stack, frames, log_fd);
+                lockf(log_fd, F_ULOCK, 0);
 
-                void* stack[128];
-                int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
-                backtrace_symbols_fd(stack, frames, crash_stacktrace_fd);
                 signal(sig, original_handlers[sig]);
                 raise(sig);
             });
         }
     }
 
-    static void defer_crash_stacktraces() {
-        crash_stacktrace_fd = fileno(tmpfile());
+    static void defer_logging() {
+        log_fd = fileno(tmpfile());
         atexit([] {
-            lseek(crash_stacktrace_fd, 0, SEEK_SET);
+            lseek(log_fd, 0, SEEK_SET);
             char buf[1024];
-            while (size_t bytes = read(crash_stacktrace_fd, buf, sizeof(buf))) {
+            while (size_t bytes = read(log_fd, buf, sizeof(buf))) {
                 write(2, buf, bytes);
             }
         });
     }
+
+    void ok_log(const char* msg) {
+        lockf(log_fd, F_LOCK, 0);
+            log("[");
+            log(tls_currently_running);
+            log("]\t");
+            log(msg);
+            log("\n");
+        lockf(log_fd, F_ULOCK, 0);
+    }
+
 #else
     static void setup_crash_handler() {}
-    static void defer_crash_stacktraces() {}
-#endif
+    static void defer_logging() {}
 
-enum class Status { OK, Failed, Crashed, Skipped, None };
+    void ok_log(const char* msg) {
+        fprintf(stderr, "%s\n", msg);
+    }
+#endif
 
 struct Engine {
     virtual ~Engine() {}
@@ -107,6 +120,7 @@
 
 struct ThreadEngine : Engine {
     std::list<std::future<Status>> live;
+    const std::chrono::steady_clock::time_point the_past = std::chrono::steady_clock::now();
 
     bool spawn(std::function<Status(void)> fn) override {
         live.push_back(std::async(std::launch::async, fn));
@@ -120,7 +134,7 @@
 
         for (;;) {
             for (auto it = live.begin(); it != live.end(); it++) {
-                if (it->wait_for(std::chrono::seconds::zero()) == std::future_status::ready) {
+                if (it->wait_until(the_past) == std::future_status::ready) {
                     Status s = it->get();
                     live.erase(it);
                     return s;
@@ -159,80 +173,72 @@
 #endif
 
 struct StreamType {
-    const char* name;
+    const char *name, *help;
     std::unique_ptr<Stream> (*factory)(Options);
 };
 static std::vector<StreamType> stream_types;
 
 struct DstType {
-    const char* name;
+    const char *name, *help;
     std::unique_ptr<Dst> (*factory)(Options);
 };
 static std::vector<DstType> dst_types;
 
 struct ViaType {
-    const char* name;
+    const char *name, *help;
     std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
 };
 static std::vector<ViaType> via_types;
 
+template <typename T>
+static std::string help_for(std::vector<T> registered) {
+    std::string help;
+    for (auto r : registered) {
+        help += "\n    ";
+        help += r.name;
+        help += ": ";
+        help += r.help;
+    }
+    return help;
+}
+
 int main(int argc, char** argv) {
     SkGraphics::Init();
     setup_crash_handler();
 
-    int         jobs        {1};
-    std::regex  match       {".*"};
-    std::regex  search      {".*"};
-    std::string write_dir   {""};
-
+    int                                       jobs{1};
     std::unique_ptr<Stream>                   stream;
-    std::function<std::unique_ptr<Dst>(void)> dst_factory;
+    std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
+        // A default Dst that's enough for unit tests and not much else.
+        struct : Dst {
+            Status draw(Src* src)  override { return src->draw(nullptr); }
+            sk_sp<SkImage> image() override { return nullptr; }
+        } dst;
+        return move_unique(dst);
+    };
 
     auto help = [&] {
-        std::string stream_names, dst_names, via_names;
-        for (auto s : stream_types) {
-            if (!stream_names.empty()) {
-                stream_names += ", ";
-            }
-            stream_names += s.name;
-        }
-        for (auto d : dst_types) {
-            if (!dst_names.empty()) {
-                dst_names += ", ";
-            }
-            dst_names += d.name;
-        }
-        for (auto v : via_types) {
-            if (!via_names.empty()) {
-                via_names += ", ";
-            }
-            via_names += v.name;
-        }
+        std::string stream_help = help_for(stream_types),
+                       dst_help = help_for(   dst_types),
+                       via_help = help_for(   via_types);
 
-        printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h]                        \n"
-                "  src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...]                   \n"
+        printf("%s [-j N] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...]            \n"
                 "  -j: Run at most N processes at any time.                          \n"
                 "      If <0, use -N threads instead.                                \n"
                 "      If 0, use one thread in one process.                          \n"
                 "      If 1 (default) or -1, auto-detect N.                          \n"
-                "  -m: Run only names matching regex exactly.                        \n"
-                "  -s: Run only names matching regex anywhere.                       \n"
-                "  -w: If set, write .pngs into dir.                                 \n"
-                "  -h: Print this message and exit.                                  \n"
-                " src: content to draw: %s                                           \n"
-                " dst: how to draw that content: %s                                  \n"
-                " via: front-patches to the dst: %s                                  \n"
-                " Some srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
-                argv[0], stream_names.c_str(), dst_names.c_str(), via_names.c_str());
+                " src: content to draw%s                                             \n"
+                " dst: how to draw that content%s                                    \n"
+                " via: wrappers around dst%s                                         \n"
+                " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
+                argv[0], stream_help.c_str(), dst_help.c_str(), via_help.c_str());
         return 1;
     };
 
     for (int i = 1; i < argc; i++) {
-        if (0 == strcmp("-j", argv[i])) { jobs      = atoi(argv[++i]); }
-        if (0 == strcmp("-m", argv[i])) { match     =      argv[++i] ; }
-        if (0 == strcmp("-s", argv[i])) { search    =      argv[++i] ; }
-        if (0 == strcmp("-w", argv[i])) { write_dir =      argv[++i] ; }
-        if (0 == strcmp("-h", argv[i])) { return help(); }
+        if (0 == strcmp("-j",     argv[i])) { jobs = atoi(argv[++i]); }
+        if (0 == strcmp("-h",     argv[i])) { return help(); }
+        if (0 == strcmp("--help", argv[i])) { return help(); }
 
         for (auto s : stream_types) {
             size_t len = strlen(s.name);
@@ -268,28 +274,14 @@
         }
     }
     if (!stream) { return help(); }
-    if (!dst_factory) {
-        // A default Dst that's enough for unit tests and not much else.
-        dst_factory = []{
-            struct : Dst {
-                bool draw(Src* src) override { return src->draw(nullptr); }
-                sk_sp<SkImage> image() override { return nullptr; }
-            } dst;
-            return move_unique(dst);
-        };
-    }
 
     std::unique_ptr<Engine> engine;
-    if (jobs == 0) { engine.reset(new SerialEngine);                            }
-    if (jobs  > 0) { engine.reset(new   ForkEngine); defer_crash_stacktraces(); }
-    if (jobs  < 0) { engine.reset(new ThreadEngine); jobs = -jobs;              }
+    if (jobs == 0) { engine.reset(new SerialEngine);                  }
+    if (jobs  > 0) { engine.reset(new   ForkEngine); defer_logging(); }
+    if (jobs  < 0) { engine.reset(new ThreadEngine); jobs = -jobs;    }
 
     if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
 
-    if (!write_dir.empty()) {
-        sk_mkdir(write_dir.c_str());
-    }
-
     int ok = 0, failed = 0, crashed = 0, skipped = 0;
 
     auto update_stats = [&](Status s) {
@@ -328,25 +320,10 @@
         spawn([=] {
             std::unique_ptr<Src> src{raw};
 
-            auto name = src->name();
-            tls_name = name.c_str();
-            if (!std::regex_match (name, match) ||
-                !std::regex_search(name, search)) {
-                return Status::Skipped;
-            }
+            std::string name = src->name();
+            tls_currently_running = name.c_str();
 
-            auto dst = dst_factory();
-            if (!dst->draw(src.get())) {
-                return Status::Failed;
-            }
-
-            if (!write_dir.empty()) {
-                auto image = dst->image();
-                sk_sp<SkData> png{image->encode()};
-                SkFILEWStream{(write_dir + "/" + name + ".png").c_str()}
-                    .write(png->data(), png->size());
-            }
-            return Status::OK;
+            return dst_factory()->draw(src.get());
         });
     }
 
@@ -359,15 +336,17 @@
 }
 
 
-Register::Register(const char* name, std::unique_ptr<Stream> (*factory)(Options)) {
-    stream_types.push_back(StreamType{name, factory});
+Register::Register(const char* name, const char* help,
+                   std::unique_ptr<Stream> (*factory)(Options)) {
+    stream_types.push_back(StreamType{name, help, factory});
 }
-Register::Register(const char* name, std::unique_ptr<Dst> (*factory)(Options)) {
-    dst_types.push_back(DstType{name, factory});
+Register::Register(const char* name, const char* help,
+                   std::unique_ptr<Dst> (*factory)(Options)) {
+    dst_types.push_back(DstType{name, help, factory});
 }
-Register::Register(const char* name,
+Register::Register(const char* name, const char* help,
                    std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
-    via_types.push_back(ViaType{name, factory});
+    via_types.push_back(ViaType{name, help, factory});
 }
 
 Options::Options(std::string str) {
diff --git a/tools/ok.h b/tools/ok.h
index 02bd939..f55842b 100644
--- a/tools/ok.h
+++ b/tools/ok.h
@@ -20,11 +20,15 @@
     return std::unique_ptr<T>{new T{std::move(v)}};
 }
 
+void ok_log(const char*);
+
+enum class Status { OK, Failed, Crashed, Skipped, None };
+
 struct Src {
     virtual ~Src() {}
-    virtual std::string name()   = 0;
-    virtual SkISize     size()   = 0;
-    virtual bool draw(SkCanvas*) = 0;
+    virtual std::string name()     = 0;
+    virtual SkISize     size()     = 0;
+    virtual Status draw(SkCanvas*) = 0;
 };
 
 struct Stream {
@@ -34,7 +38,7 @@
 
 struct Dst {
     virtual ~Dst() {}
-    virtual bool draw(Src*)        = 0;
+    virtual Status draw(Src*)      = 0;
     virtual sk_sp<SkImage> image() = 0;
 };
 
@@ -48,9 +52,10 @@
 
 // Create globals to register your new type of Stream or Dst.
 struct Register {
-    Register(const char* name, std::unique_ptr<Stream> (*factory)(Options));
-    Register(const char* name, std::unique_ptr<Dst>    (*factory)(Options));
-    Register(const char* name, std::unique_ptr<Dst>    (*factory)(Options, std::unique_ptr<Dst>));
+    Register(const char* name, const char* help, std::unique_ptr<Stream> (*factory)(Options));
+    Register(const char* name, const char* help, std::unique_ptr<Dst>    (*factory)(Options));
+    Register(const char* name, const char* help,
+             std::unique_ptr<Dst>(*factory)(Options, std::unique_ptr<Dst>));
 };
 
 #endif//ok_DEFINED
diff --git a/tools/ok_dsts.cpp b/tools/ok_dsts.cpp
index 034ca45..d03c950 100644
--- a/tools/ok_dsts.cpp
+++ b/tools/ok_dsts.cpp
@@ -17,12 +17,18 @@
         if (options("ct") == "565") { info = info.makeColorType(kRGB_565_SkColorType); }
         if (options("ct") == "f16") { info = info.makeColorType(kRGBA_F16_SkColorType); }
 
+        if (options("cs") == "srgb") {
+            auto cs = info.colorType() == kRGBA_F16_SkColorType ? SkColorSpace::MakeSRGBLinear()
+                                                                : SkColorSpace::MakeSRGB();
+            info = info.makeColorSpace(std::move(cs));
+        }
+
         SWDst dst;
         dst.info = info;
         return move_unique(dst);
     }
 
-    bool draw(Src* src) override {
+    Status draw(Src* src) override {
         auto size = src->size();
         surface = SkSurface::MakeRaster(info.makeWH(size.width(), size.height()));
         return src->draw(surface->getCanvas());
@@ -32,9 +38,20 @@
         return surface->makeImageSnapshot();
     }
 };
-static Register sw{"sw", SWDst::Create};
+static Register sw{"sw", "draw with the software backend", SWDst::Create};
 
-static Register _565{"565", [](Options options) {
+static Register _565{"565", "alias for sw:ct=565", [](Options options) {
     options["ct"] = "565";
     return SWDst::Create(options);
 }};
+
+static Register srgb{"srgb", "alias for sw:cs=srgb", [](Options options) {
+    options["cs"] = "srgb";
+    return SWDst::Create(options);
+}};
+
+static Register f16{"f16", "alias for sw:ct=f16,cs=srgb", [](Options options) {
+    options["ct"] = "f16";
+    options["cs"] = "srgb";
+    return SWDst::Create(options);
+}};
diff --git a/tools/ok_srcs.cpp b/tools/ok_srcs.cpp
index a7d262c..5630a34 100644
--- a/tools/ok_srcs.cpp
+++ b/tools/ok_srcs.cpp
@@ -7,6 +7,7 @@
 
 #include "ok.h"
 #include "gm.h"
+#include "SkData.h"
 #include "SkOSFile.h"
 #include "SkPicture.h"
 #include <vector>
@@ -38,12 +39,12 @@
             return gm->getISize();
         }
 
-        bool draw(SkCanvas* canvas) override {
+        Status draw(SkCanvas* canvas) override {
             this->init();
             canvas->clear(0xffffffff);
             canvas->concat(gm->getInitialTransform());
             gm->draw(canvas);
-            return true;
+            return Status::OK;
         }
     };
 
@@ -57,7 +58,7 @@
         return move_unique(src);
     }
 };
-static Register gm{"gm", GMStream::Create};
+static Register gm{"gm", "draw GMs linked into this binary", GMStream::Create};
 
 struct SKPStream : Stream {
     std::string dir;
@@ -92,11 +93,11 @@
             return pic->cullRect().roundOut().size();
         }
 
-        bool draw(SkCanvas* canvas) override {
+        Status draw(SkCanvas* canvas) override {
             this->init();
             canvas->clear(0xffffffff);
             pic->playback(canvas);
-            return true;
+            return Status::OK;
         }
     };
 
@@ -111,4 +112,4 @@
         return move_unique(src);
     }
 };
-static Register skp{"skp", SKPStream::Create};
+static Register skp{"skp", "draw SKPs from dir=skps", SKPStream::Create};
diff --git a/tools/ok_test.cpp b/tools/ok_test.cpp
index dcb18a1..f5c4f22 100644
--- a/tools/ok_test.cpp
+++ b/tools/ok_test.cpp
@@ -8,6 +8,10 @@
 #include "ok.h"
 #include "Test.h"
 
+#if SK_SUPPORT_GPU
+    #include "GrContextFactory.h"
+#endif
+
 struct TestStream : Stream {
     const skiatest::TestRegistry* registry = skiatest::TestRegistry::Head();
     bool extended = false, verbose = false;
@@ -27,27 +31,31 @@
         std::string name() override { return test.name; }
         SkISize     size() override { return {0,0}; }
 
-        bool draw(SkCanvas*) override {
-            // TODO(mtklein): GrContext
+        Status draw(SkCanvas*) override {
 
             struct : public skiatest::Reporter {
-                bool ok = true;
-                const char* name;
+                Status status = Status::OK;
                 bool extended, verbose_;
 
                 void reportFailed(const skiatest::Failure& failure) override {
-                    SkDebugf("%s: %s\n", name, failure.toString().c_str());
-                    ok = false;
+                    ok_log(failure.toString().c_str());
+                    status = Status::Failed;
                 }
                 bool allowExtendedTest() const override { return extended; }
                 bool           verbose() const override { return verbose_; }
             } reporter;
-            reporter.name     = test.name;
             reporter.extended = extended;
             reporter.verbose_ = verbose;
 
-            test.proc(&reporter, nullptr);
-            return reporter.ok;
+            sk_gpu_test::GrContextFactory* factory = nullptr;
+        #if SK_SUPPORT_GPU
+            GrContextOptions options;
+            sk_gpu_test::GrContextFactory a_real_factory(options);
+            factory = &a_real_factory;
+        #endif
+
+            test.proc(&reporter, factory);
+            return reporter.status;
         }
     };
 
@@ -63,7 +71,7 @@
         return move_unique(src);
     }
 };
-static Register test{"test", TestStream::Create};
+static Register test{"test", "run unit tests linked into this binary", TestStream::Create};
 
 // Hey, now why were these defined in DM.cpp?  That's kind of weird.
 namespace skiatest {
diff --git a/tools/ok_vias.cpp b/tools/ok_vias.cpp
index b273d3c..50db49f 100644
--- a/tools/ok_vias.cpp
+++ b/tools/ok_vias.cpp
@@ -5,17 +5,23 @@
  * found in the LICENSE file.
  */
 
-#include "ok.h"
+#include "SkImage.h"
+#include "SkOSFile.h"
 #include "SkPictureRecorder.h"
+#include "SkPngEncoder.h"
+#include "Timer.h"
+#include "ok.h"
+#include <chrono>
+#include <regex>
 
-static std::unique_ptr<Src> proxy(Src* original, std::function<bool(SkCanvas*)> fn) {
+static std::unique_ptr<Src> proxy(Src* original, std::function<Status(SkCanvas*)> fn) {
     struct : Src {
-        Src*                           original;
-        std::function<bool(SkCanvas*)> fn;
+        Src*                             original;
+        std::function<Status(SkCanvas*)> fn;
 
         std::string name() override { return original->name(); }
         SkISize     size() override { return original->size(); }
-        bool draw(SkCanvas* canvas) override { return fn(canvas); }
+        Status draw(SkCanvas* canvas) override { return fn(canvas); }
     } src;
     src.original = original;
     src.fn       = fn;
@@ -33,20 +39,20 @@
         return move_unique(via);
     }
 
-    bool draw(Src* src) override {
+    Status draw(Src* src) override {
         SkRTreeFactory factory;
         SkPictureRecorder rec;
         rec.beginRecording(SkRect::MakeSize(SkSize::Make(src->size())),
                            rtree ? &factory : nullptr);
 
-        if (!src->draw(rec.getRecordingCanvas())) {
-            return false;
+        for (auto status = src->draw(rec.getRecordingCanvas()); status != Status::OK; ) {
+            return status;
         }
         auto pic = rec.finishRecordingAsPicture();
 
         return target->draw(proxy(src, [=](SkCanvas* canvas) {
             pic->playback(canvas);
-            return true;
+            return Status::OK;
         }).get());
     }
 
@@ -54,4 +60,102 @@
         return target->image();
     }
 };
-static Register via_pic{"via_pic", ViaPic::Create};
+static Register via_pic{"via_pic", "record then play back an SkPicture", ViaPic::Create};
+
+struct Png : Dst {
+    std::unique_ptr<Dst> target;
+    std::string          dir;
+
+    static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
+        Png via;
+        via.target = std::move(dst);
+        via.dir    = options("dir", "ok");
+        return move_unique(via);
+    }
+
+    Status draw(Src* src) override {
+        for (auto status = target->draw(src); status != Status::OK; ) {
+            return status;
+        }
+
+        SkBitmap bm;
+        SkPixmap pm;
+        if (!target->image()->asLegacyBitmap(&bm, SkImage::kRO_LegacyBitmapMode) ||
+            !bm.peekPixels(&pm)) {
+            return Status::Failed;
+        }
+
+        sk_mkdir(dir.c_str());
+        SkFILEWStream dst{(dir + "/" + src->name() + ".png").c_str()};
+
+        SkPngEncoder::Options options;
+        options.fFilterFlags      = SkPngEncoder::FilterFlag::kNone;
+        options.fZLibLevel        = 1;
+        options.fUnpremulBehavior = pm.colorSpace() ? SkTransferFunctionBehavior::kRespect
+                                                    : SkTransferFunctionBehavior::kIgnore;
+        return SkPngEncoder::Encode(&dst, pm, options) ? Status::OK
+                                                       : Status::Failed;
+    }
+
+    sk_sp<SkImage> image() override {
+        return target->image();
+    }
+};
+static Register png{"png", "dump PNGs to dir=ok" , Png::Create};
+
+struct Filter : Dst {
+    std::unique_ptr<Dst> target;
+    std::regex match, search;
+
+    static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
+        Filter via;
+        via.target = std::move(dst);
+        via.match  = options("match",  ".*");
+        via.search = options("search", ".*");
+        return move_unique(via);
+    }
+
+    Status draw(Src* src) override {
+        auto name = src->name();
+        if (!std::regex_match (name, match) ||
+            !std::regex_search(name, search)) {
+            return Status::Skipped;
+        }
+        return target->draw(src);
+    }
+
+    sk_sp<SkImage> image() override {
+        return target->image();
+    }
+};
+static Register filter{"filter",
+                       "run only srcs matching match=.* exactly and search=.* somewhere",
+                       Filter::Create};
+
+struct Time : Dst {
+    std::unique_ptr<Dst> target;
+
+    static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
+        Time via;
+        via.target = std::move(dst);
+        return move_unique(via);
+    }
+
+    Status draw(Src* src) override {
+        auto start = std::chrono::steady_clock::now();
+            Status status = target->draw(src);
+        std::chrono::duration<double, std::milli> elapsed = std::chrono::steady_clock::now()
+                                                          - start;
+
+        auto msg = HumanizeMs(elapsed.count());
+        ok_log(msg.c_str());
+        return status;
+    }
+
+    sk_sp<SkImage> image() override {
+        return target->image();
+    }
+};
+static Register _time{"time",
+                      "print wall run time",
+                      Time::Create};
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index 967ac31..d8371ad 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -7,26 +7,21 @@
 <div style="height:0">
 
 <div id="cubics">
-{{{403.28299,497.196991}, {403.424011,497.243988}, {391.110992,495.556}, {391.110992,495.556}}}

-{{{398.375,501.976013}, {403.28299,497.196991}}}

-{{{403.42401123046875, 497.243988037109375}, {378.7979736328125, 493.868011474609375}}}, id=0

-{{{415.5605946972181641, 498.8838844379989155}, {378.8318672594865006, 493.8897667505245295}}}, id=1

-{{{403.2954048004600622, 497.117149457477467}, {403.2729768294798873, 497.2781265045034615}}}, id=2

-{{{403.42401123046875, 497.243988037109375}, {378.7979736328125, 493.868011474609375}}}, id=3

-

+{{{152, 16}, {152, 16.0685501}, {91.06044, 16.1242027}, {16, 16.1242027}}}, id=0

+{{{16, 16.1242027}, {-59.06044, 16.1242027}, {-120, 16.0685501}, {-120, 16}}}, id=1

+{{{-120, 16}, {-120, 15.9314508}, {-59.06044, 15.8757973}, {16, 15.8757973}}}, id=2

+{{{16, 15.8757973}, {91.06044, 15.8757973}, {152, 15.9314508}, {152, 16}}}, id=3

+{{{16, 16}, {152, 16}}}, id=4

+{{{16, 17}, {152, 17}}}, id=5

+{{{16, 16}, {16, 17}}}, id=6

+{{{152, 16}, {152, 17}}}, id=7

 </div>
 
-<div id="c1">
-{{{403.28299f, 497.196991f}, {403.284241f, 497.197388f}, {403.28418f, 497.197632f}}}
-{{{403.28418f, 497.197632f}, {403.280487f, 497.224304f}, {391.110992f, 495.556f}, {391.110992f, 495.556f}}}    

-

-</div>
     </div>
 
 <script type="text/javascript">
 
     var testDivs = [
-    c1,
     cubics,
     ];
 
@@ -39,8 +34,9 @@
 
     var subscale = 1;
     var xmin, xmax, ymin, ymax;
-    var scale;
-    var initScale;
+    var hscale, vscale;
+    var hinitScale, vinitScale;
+    var uniformScale = true;
     var mouseX, mouseY;
     var mouseDown = false;
     var srcLeft, srcTop;
@@ -146,9 +142,10 @@
             subscale /= 10;
         }
         setScale(xmin, xmax, ymin, ymax);
-        mouseX = (screenWidth / 2) / scale + srcLeft;
-        mouseY = (screenHeight / 2) / scale + srcTop;
-        initScale = scale;
+        mouseX = (screenWidth / 2) / hscale + srcLeft;
+        mouseY = (screenHeight / 2) / vscale + srcTop;
+        hinitScale = hscale;
+        vinitScale = vscale;

     }
 
     function setScale(x0, x1, y0, y1) {
@@ -159,19 +156,26 @@
         var yDigits = Math.ceil(Math.log(Math.abs(ymax)) / Math.log(10));
         usableWidth -= (xDigits + yDigits) * 10;
         usableWidth -= decimal_places * 10;
-        var hscale = usableWidth / srcWidth;
-        var vscale = screenHeight / srcHeight;
-        scale = Math.min(hscale, vscale);
-        var invScale = 1 / scale;
-        var sxmin = x0 - invScale * 5;
-        var symin = y0 - invScale * 10;
-        var sxmax = x1 + invScale * (6 * decimal_places + 10);
-        var symax = y1 + invScale * 10;
+        hscale = usableWidth / srcWidth;
+        vscale = screenHeight / srcHeight;
+        if (uniformScale) {
+            hscale = Math.min(hscale, vscale);
+            vscale = hscale;

+        }
+        var hinvScale = 1 / hscale;
+        var vinvScale = 1 / vscale;
+        var sxmin = x0 - hinvScale * 5;
+        var symin = y0 - vinvScale * 10;
+        var sxmax = x1 + hinvScale * (6 * decimal_places + 10);
+        var symax = y1 + vinvScale * 10;
         srcWidth = sxmax - sxmin;
         srcHeight = symax - symin;
         hscale = usableWidth / srcWidth;
         vscale = screenHeight / srcHeight;
-        scale = Math.min(hscale, vscale);
+        if (uniformScale) {
+            hscale = Math.min(hscale, vscale);
+            vscale = hscale;
+        }
         srcLeft = sxmin;
         srcTop = symin;
     }
@@ -467,10 +471,10 @@
 
     function drawLine(x1, y1, x2, y2) {
         ctx.beginPath();
-        ctx.moveTo((x1 - srcLeft) * scale,
-                (y1 - srcTop) * scale);
-        ctx.lineTo((x2 - srcLeft) * scale,
-                (y2 - srcTop) * scale);
+        ctx.moveTo((x1 - srcLeft) * hscale,
+                (y1 - srcTop) * vscale);
+        ctx.lineTo((x2 - srcLeft) * hscale,
+                (y2 - srcTop) * vscale);
         ctx.stroke();
     }
 
@@ -484,8 +488,8 @@
         }
         drawnPts.push(px);
         drawnPts.push(py);
-        var _px = (px - srcLeft) * scale;
-        var _py = (py - srcTop) * scale;
+        var _px = (px - srcLeft) * hscale;
+        var _py = (py - srcTop) * vscale;
         ctx.beginPath();
         if (xend) {
             ctx.moveTo(_px - 3, _py - 3);
@@ -607,15 +611,15 @@
         return 1 << pow2;
     }
 
-    function drawConic(curve, srcLeft, srcTop, scale) {
-        var tol = 1 / scale;
+    function drawConic(curve, srcLeft, srcTop, hscale, vscale) {
+        var tol = 1 / Math.min(hscale, vscale);
         var pow2 = computeQuadPOW2(curve, tol);
         var pts = [];
         chopIntoQuadsPOW2(curve, pow2, pts);
         for (var i = 0; i < pts.length; i += 4) {
             ctx.quadraticCurveTo(
-                (pts[i + 0] - srcLeft) * scale, (pts[i + 1] - srcTop) * scale,
-                (pts[i + 2] - srcLeft) * scale, (pts[i + 3] - srcTop) * scale);
+                (pts[i + 0] - srcLeft) * hscale, (pts[i + 1] - srcTop) * vscale,
+                (pts[i + 2] - srcLeft) * hscale, (pts[i + 3] - srcTop) * vscale);
         }
     }
 
@@ -762,20 +766,20 @@
                 }
             }
             ctx.beginPath();
-            ctx.moveTo((curve[0] - srcLeft) * scale, (curve[1] - srcTop) * scale);
+            ctx.moveTo((curve[0] - srcLeft) * hscale, (curve[1] - srcTop) * vscale);
             if (curve.length == 4) {
-                ctx.lineTo((curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale);
+                ctx.lineTo((curve[2] - srcLeft) * hscale, (curve[3] - srcTop) * vscale);
             } else if (curve.length == 6) {
                 ctx.quadraticCurveTo(
-                    (curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale,
-                    (curve[4] - srcLeft) * scale, (curve[5] - srcTop) * scale);
+                    (curve[2] - srcLeft) * hscale, (curve[3] - srcTop) * vscale,
+                    (curve[4] - srcLeft) * hscale, (curve[5] - srcTop) * vscale);
             } else if (curve.length == 7) {
-                drawConic(curve, srcLeft, srcTop, scale);
+                drawConic(curve, srcLeft, srcTop, hscale, vscale);
             } else {
                 ctx.bezierCurveTo(
-                    (curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale,
-                    (curve[4] - srcLeft) * scale, (curve[5] - srcTop) * scale,
-                    (curve[6] - srcLeft) * scale, (curve[7] - srcTop) * scale);
+                    (curve[2] - srcLeft) * hscale, (curve[3] - srcTop) * vscale,
+                    (curve[4] - srcLeft) * hscale, (curve[5] - srcTop) * vscale,
+                    (curve[6] - srcLeft) * hscale, (curve[7] - srcTop) * vscale);
             }
             if (draw_cubic_red ? curve.length == 8 : firstInside == curves) {
                 ctx.strokeStyle = "rgba(255,0,0, 1)";
@@ -859,8 +863,8 @@
             if (draw_order) {
                 var px = x_at_t(curve, 0.75);
                 var py = y_at_t(curve, 0.75);
-                var _px = (px - srcLeft) * scale;
-                var _py = (py - srcTop) * scale;
+                var _px = (px - srcLeft) * hscale;
+                var _py = (py - srcTop) * vscale;
                 ctx.beginPath();
                 ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
                 ctx.closePath();
@@ -886,12 +890,17 @@
                     drawPointSolid(x, y);
                 }
             }
-            if (!approximately_zero(scale - initScale)) {
+            if (!approximately_zero(hscale - hinitScale)) {
                 ctx.font = "normal 20px Arial";
                 ctx.fillStyle = "rgba(0,0,0, 0.3)";
                 ctx.textAlign = "right";
-                ctx.fillText(scale.toFixed(decimal_places) + 'x',
-                        screenWidth - 10, screenHeight - 5);
+                var scaleTextOffset = hscale != vscale ? -25 : -5;
+                ctx.fillText(hscale.toFixed(decimal_places) + 'x',
+                        screenWidth - 10, screenHeight - scaleTextOffset);
+                if (hscale != vscale) {

+                    ctx.fillText(vscale.toFixed(decimal_places) + 'y',
+                            screenWidth - 10, screenHeight - 5);

+                }

             }
             if (draw_t) {
                 drawPointAtT(curve);
@@ -907,8 +916,8 @@
                 if (id >= 0) {
                     var px = x_at_t(curve, 0.5);
                     var py = y_at_t(curve, 0.5);
-                    var _px = (px - srcLeft) * scale;
-                    var _py = (py - srcTop) * scale;
+                    var _px = (px - srcLeft) * hscale;
+                    var _py = (py - srcTop) * vscale;
                     ctx.beginPath();
                     ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
                     ctx.closePath();
@@ -1095,9 +1104,11 @@
                 focusWasOn = focus_on_selection;
                 if (focusWasOn) {
                     focus_on_selection = false;
-                    scale /= 1.2;
+                    hscale /= 1.2;
+                    vscale /= 1.2;

                 } else {
-                    scale /= 2;
+                    hscale /= 2;

+                    vscale /= 2;

                 }
                 calcLeftTop();
                 redraw();
@@ -1108,9 +1119,11 @@
                 focusWasOn = focus_on_selection;
                 if (focusWasOn) {
                     focus_on_selection = false;
-                    scale *= 1.2;
+                    hscale *= 1.2;

+                    vscale *= 1.2;

                 } else {
-                    scale *= 2;
+                    hscale *= 2;

+                    vscale *= 2;

                 }
                 calcLeftTop();
                 redraw();
@@ -1147,6 +1160,26 @@
                 draw_derivative ^= true;
                 redraw();
                 break;
+            case 'g':
+                hscale *= 1.2;
+                calcLeftTop();
+                redraw();
+                break;
+            case 'G':
+                hscale /= 1.2;
+                calcLeftTop();
+                redraw();
+                break;
+            case 'h':
+                vscale *= 1.2;
+                calcLeftTop();
+                redraw();
+                break;
+            case 'H':
+                vscale /= 1.2;
+                calcLeftTop();
+                redraw();
+                break;
             case 'i':
                 draw_ray_intersect = (draw_ray_intersect + 1) % 3;
                 redraw();
@@ -1314,13 +1347,13 @@
         var tgt = e.target || e.srcElement;
         var left = tgt.offsetLeft;
         var top = tgt.offsetTop;
-        mouseX = (e.clientX - left) / scale + srcLeft;
-        mouseY = (e.clientY - top) / scale + srcTop;
+        mouseX = (e.clientX - left) / hscale + srcLeft;
+        mouseY = (e.clientY - top) / vscale + srcTop;
     }
 
     function calcLeftTop() {
-        srcLeft = mouseX - screenWidth / 2 / scale;
-        srcTop = mouseY - screenHeight / 2 / scale;
+        srcLeft = mouseX - screenWidth / 2 / hscale;
+        srcTop = mouseY - screenHeight / 2 / vscale;
     }
 
     function handleMouseClick() {
diff --git a/tools/picture_utils.cpp b/tools/picture_utils.cpp
index b0fd224..bd32f19 100644
--- a/tools/picture_utils.cpp
+++ b/tools/picture_utils.cpp
@@ -26,7 +26,6 @@
             return;
         }
 
-        SkAutoLockPixels lock(bitmap);
         for (int y = 0; y < bitmap.height(); y++) {
             for (int x = 0; x < bitmap.width(); x++) {
                 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
@@ -87,7 +86,6 @@
             bitmap.colorSpace() == srgbColorSpace.get()) {
             // These are premul sRGB 8-bit pixels in SkPMColor order.
             // We want unpremul sRGB 8-bit pixels in RGBA order.  We'll get there via floats.
-            bitmap.lockPixels();
             auto px = (const uint32_t*)bitmap.getPixels();
             if (!px) {
                 return nullptr;
@@ -105,7 +103,6 @@
         } else if (bitmap.colorType() == kRGBA_F16_SkColorType) {
             // These are premul linear half-float pixels in RGBA order.
             // We want unpremul sRGB 8-bit pixels in RGBA order.  We'll get there via floats.
-            bitmap.lockPixels();
             auto px = (const uint64_t*)bitmap.getPixels();
             if (!px) {
                 return nullptr;
@@ -129,7 +126,7 @@
             // Convert smaller formats up to premul linear 8-bit (in SkPMColor order).
             if (bitmap.colorType() != kN32_SkColorType) {
                 SkBitmap n32;
-                if (!bitmap.copyTo(&n32, kN32_SkColorType)) {
+                if (!sk_tool_utils::copy_to(&n32, kN32_SkColorType, bitmap)) {
                     return nullptr;
                 }
                 bitmap = n32;
diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp
index f8236bf..c792c48 100644
--- a/tools/sk_tool_utils.cpp
+++ b/tools/sk_tool_utils.cpp
@@ -40,6 +40,9 @@
     void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override {
         SK_ABORT("SkSRGBColorFilter is only implemented for GPU");
     }
+    void onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, bool) const override {
+        SK_ABORT("SkSRGBColorFilter is only implemented for GPU");
+    }
     Factory getFactory() const override { return nullptr; }
 
 #ifndef SK_IGNORE_TO_STRING
@@ -225,8 +228,6 @@
 void write_pixels(SkCanvas* canvas, const SkBitmap& bitmap, int x, int y,
                   SkColorType colorType, SkAlphaType alphaType) {
     SkBitmap tmp(bitmap);
-    tmp.lockPixels();
-
     const SkImageInfo info = SkImageInfo::Make(tmp.width(), tmp.height(), colorType, alphaType);
 
     canvas->writePixels(info, tmp.getPixels(), tmp.rowBytes(), x, y);
@@ -271,7 +272,7 @@
     paint.setTextSize(SkIntToScalar(textSize));
 
     canvas.clear(0x00000000);
-    canvas.drawText(str, strlen(str), SkIntToScalar(x), SkIntToScalar(y), paint);
+    canvas.drawString(str, SkIntToScalar(x), SkIntToScalar(y), paint);
 
     // Tag data as sRGB (without doing any color space conversion). Color-space aware configs
     // will process this correctly but legacy configs will render as if this returned N32.
@@ -588,6 +589,44 @@
     return SkRect::MakeLTRB(r.fLeft + maxL, r.fTop, r.fRight - maxR, r.fBottom);
 }
 
+bool copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) {
+    SkPixmap srcPM;
+    if (!src.peekPixels(&srcPM)) {
+        return false;
+    }
+
+    SkBitmap tmpDst;
+    SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
+    if (!tmpDst.setInfo(dstInfo)) {
+        return false;
+    }
+
+    // allocate colortable if srcConfig == kIndex8_Config
+    sk_sp<SkColorTable> ctable = nullptr;
+    if (dstColorType == kIndex_8_SkColorType) {
+        if (src.colorType() != kIndex_8_SkColorType) {
+            return false;
+        }
+
+        ctable = sk_ref_sp(srcPM.ctable());
+    }
+    if (!tmpDst.tryAllocPixels(ctable.get())) {
+        return false;
+    }
+
+    SkPixmap dstPM;
+    if (!tmpDst.peekPixels(&dstPM)) {
+        return false;
+    }
+
+    if (!srcPM.readPixels(dstPM)) {
+        return false;
+    }
+
+    dst->swap(tmpDst);
+    return true;
+}
+
 void copy_to_g8(SkBitmap* dst, const SkBitmap& src) {
     SkASSERT(kBGRA_8888_SkColorType == src.colorType() ||
              kRGBA_8888_SkColorType == src.colorType());
diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h
index e319411..111f966 100644
--- a/tools/sk_tool_utils.h
+++ b/tools/sk_tool_utils.h
@@ -253,6 +253,7 @@
         return sk_make_sp<EncodeImagePixelSerializer>();
     }
 
+    bool copy_to(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src);
     void copy_to_g8(SkBitmap* dst, const SkBitmap& src);
 
 #if SK_SUPPORT_GPU
diff --git a/tools/skdiff/skdiff.cpp b/tools/skdiff/skdiff.cpp
index ae6d72c..c7ddf7d 100644
--- a/tools/skdiff/skdiff.cpp
+++ b/tools/skdiff/skdiff.cpp
@@ -163,8 +163,6 @@
         return;
     }
 
-    SkAutoLockPixels alpDiff(dr->fDifference.fBitmap);
-    SkAutoLockPixels alpWhite(dr->fWhite.fBitmap);
     int mismatchedPixels = 0;
     int totalMismatchA = 0;
     int totalMismatchR = 0;
diff --git a/tools/skdiff/skdiff_utils.cpp b/tools/skdiff/skdiff_utils.cpp
index f8eed90..7a1b7e8 100644
--- a/tools/skdiff/skdiff_utils.cpp
+++ b/tools/skdiff/skdiff_utils.cpp
@@ -71,7 +71,6 @@
 
 /** Thanks to PNG, we need to force all pixels 100% opaque. */
 static void force_all_opaque(const SkBitmap& bitmap) {
-   SkAutoLockPixels lock(bitmap);
    for (int y = 0; y < bitmap.height(); y++) {
        for (int x = 0; x < bitmap.width(); x++) {
            *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
@@ -81,7 +80,7 @@
 
 bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
     SkBitmap copy;
-    bitmap.copyTo(&copy, kN32_SkColorType);
+    sk_tool_utils::copy_to(&copy, kN32_SkColorType, bitmap);
     force_all_opaque(copy);
     return sk_tool_utils::EncodeImageToFile(path.c_str(), copy,
                                       SkEncodedImageFormat::kPNG, 100);
diff --git a/tools/skiaserve/Request.cpp b/tools/skiaserve/Request.cpp
index 7c2b893..12f35968 100644
--- a/tools/skiaserve/Request.cpp
+++ b/tools/skiaserve/Request.cpp
@@ -46,9 +46,9 @@
 
 SkBitmap* Request::getBitmapFromCanvas(SkCanvas* canvas) {
     SkBitmap* bmp = new SkBitmap();
-    bmp->setInfo(canvas->imageInfo());
-    if (!canvas->readPixels(bmp, 0, 0)) {
+    if (!bmp->tryAllocPixels(canvas->imageInfo()) || !canvas->readPixels(*bmp, 0, 0)) {
         fprintf(stderr, "Can't read pixels\n");
+        delete bmp;
         return nullptr;
     }
     return bmp;
diff --git a/tools/skpbench/skpbench.cpp b/tools/skpbench/skpbench.cpp
index 569c204..20ba8b4 100644
--- a/tools/skpbench/skpbench.cpp
+++ b/tools/skpbench/skpbench.cpp
@@ -335,8 +335,8 @@
     // Save a proof (if one was requested).
     if (!FLAGS_png.isEmpty()) {
         SkBitmap bmp;
-        bmp.setInfo(info);
-        if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) {
+        bmp.allocPixels(info);
+        if (!surface->getCanvas()->readPixels(bmp, 0, 0)) {
             exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png");
         }
         const SkString &dirname = SkOSPath::Dirname(FLAGS_png[0]),
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index fecb14d..3503e64 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -33,6 +33,7 @@
 #include "SkSwizzle.h"
 #include "SkTaskGroup.h"
 #include "SkTime.h"
+#include "SkVertices.h"
 
 #include "imgui.h"
 
@@ -83,7 +84,12 @@
     } else if (Window::kUp_InputState == state) {
         io.MouseDown[0] = false;
     }
-    return true;
+    if (io.WantCaptureMouse) {
+        return true;
+    } else {
+        Viewer* viewer = reinterpret_cast<Viewer*>(userData);
+        return viewer->onMouse(x, y, state, modifiers);
+    }
 }
 
 static bool on_mouse_wheel_handler(float delta, uint32_t modifiers, void* userData) {
@@ -140,8 +146,8 @@
 #endif
 
 #ifdef SK_BUILD_FOR_ANDROID
-static DEFINE_string(skps, "/data/local/tmp/skia", "Directory to read skps from.");
-static DEFINE_string(jpgs, "/data/local/tmp/skia", "Directory to read jpgs from.");
+static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from.");
+static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from.");
 #else
 static DEFINE_string(skps, "skps", "Directory to read skps from.");
 static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
@@ -244,10 +250,7 @@
     , fBackendType(sk_app::Window::kNativeGL_BackendType)
     , fColorMode(ColorMode::kLegacy)
     , fColorSpacePrimaries(gSrgbPrimaries)
-    , fZoomCenterX(0.0f)
-    , fZoomCenterY(0.0f)
     , fZoomLevel(0.0f)
-    , fZoomScale(SK_Scalar1)
 {
     static SkTaskGroup::Enabler kTaskGroupEnabler;
     SkGraphics::Init();
@@ -275,11 +278,11 @@
 
     SkCommandLineFlags::Parse(argc, argv);
 #ifdef SK_BUILD_FOR_ANDROID
-    SetResourcePath("/data/local/tmp/skia");
+    SetResourcePath("/data/local/tmp/resources");
 #endif
 
     if (FLAGS_atrace) {
-        SkEventTracer::SetInstance(new SkATrace());
+        SkAssertResult(SkEventTracer::SetInstance(new SkATrace()));
     }
 
     fBackendType = get_backend_type(FLAGS_backend[0]);
@@ -425,8 +428,7 @@
     SkPixmap pmap(info, pixels, info.minRowBytes());
     SkMatrix localMatrix = SkMatrix::MakeScale(1.0f / w, 1.0f / h);
     auto fontImage = SkImage::MakeFromRaster(pmap, nullptr, nullptr);
-    auto fontShader = fontImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
-                                            &localMatrix);
+    auto fontShader = fontImage->makeShader(&localMatrix);
     fImGuiFontPaint.setShader(fontShader);
     fImGuiFontPaint.setColor(SK_ColorWHITE);
     fImGuiFontPaint.setFilterQuality(kLow_SkFilterQuality);
@@ -434,9 +436,7 @@
 
     auto gamutImage = GetResourceAsImage("gamut.png");
     if (gamutImage) {
-        auto gamutShader = gamutImage->makeShader(SkShader::kClamp_TileMode,
-                                                  SkShader::kClamp_TileMode);
-        fImGuiGamutPaint.setShader(gamutShader);
+        fImGuiGamutPaint.setShader(gamutImage->makeShader());
     }
     fImGuiGamutPaint.setColor(SK_ColorWHITE);
     fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality);
@@ -616,26 +616,20 @@
 
     fGesture.reset();
     fDefaultMatrix.reset();
-    fDefaultMatrixInv.reset();
 
-    if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) {
-        const SkRect contentRect = fWindow->getContentRect();
-        const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
-        const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
-        if (contentRect.width() > 0 && contentRect.height() > 0) {
-            fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit);
-            SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv));
+    const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
+    const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
+    const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
+
+    // Start with a matrix that scales the slide to the available screen space
+    if (fWindow->scaleContentToFit()) {
+        if (windowRect.width() > 0 && windowRect.height() > 0) {
+            fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit);
         }
     }
 
-    if (fWindow->supportsContentRect()) {
-        const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
-        SkRect windowRect = fWindow->getContentRect();
-        fDefaultMatrixInv.mapRect(&windowRect);
-        fGesture.setTransLimit(SkRect::MakeWH(SkIntToScalar(slideSize.width()), 
-                                              SkIntToScalar(slideSize.height())),
-                               windowRect);
-    }
+    // Prevent the user from dragging content so far outside the window they can't find it again
+    fGesture.setTransLimit(slideBounds, windowRect, fDefaultMatrix);
 
     this->updateTitle();
     this->updateUIState();
@@ -650,35 +644,18 @@
 
 void Viewer::changeZoomLevel(float delta) {
     fZoomLevel += delta;
-    if (fZoomLevel > 0) {
-        fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
-        fZoomScale = fZoomLevel + SK_Scalar1;
-    } else if (fZoomLevel < 0) {
-        fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
-        fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
-    } else {
-        fZoomScale = SK_Scalar1;
-    }
+    fZoomLevel = SkScalarPin(fZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
 }
 
 SkMatrix Viewer::computeMatrix() {
     SkMatrix m;
-    m.reset();
 
-    if (fZoomLevel) {
-        SkPoint center;
-        //m = this->getLocalMatrix();//.invert(&m);
-        m.mapXY(fZoomCenterX, fZoomCenterY, &center);
-        SkScalar cx = center.fX;
-        SkScalar cy = center.fY;
-
-        m.setTranslate(-cx, -cy);
-        m.postScale(fZoomScale, fZoomScale);
-        m.postTranslate(cx, cy);
-    }
-
-    m.preConcat(fGesture.localM());
+    SkScalar zoomScale = (fZoomLevel < 0) ? SK_Scalar1 / (SK_Scalar1 - fZoomLevel)
+                                          : SK_Scalar1 + fZoomLevel;
+    m = fGesture.localM();
     m.preConcat(fGesture.globalM());
+    m.preConcat(fDefaultMatrix);
+    m.preScale(zoomScale, zoomScale);
 
     return m;
 }
@@ -692,6 +669,7 @@
     // Switching from OpenGL to Vulkan in the same window is problematic at this point on
     // Windows, so we just delete the window and recreate it.
     if (sk_app::Window::kVulkan_BackendType == fBackendType) {
+        DisplayParams params = fWindow->getRequestedDisplayParams();
         delete fWindow;
         fWindow = Window::CreateNativeWindow(nullptr);
 
@@ -705,6 +683,7 @@
         fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
         fWindow->registerKeyFunc(on_key_handler, this);
         fWindow->registerCharFunc(on_char_handler, this);
+        fWindow->setRequestedDisplayParams(params);
     }
 #endif
 
@@ -732,12 +711,6 @@
 void Viewer::drawSlide(SkCanvas* canvas) {
     SkAutoCanvasRestore autorestore(canvas, false);
 
-    if (fWindow->supportsContentRect()) {
-        SkRect contentRect = fWindow->getContentRect();
-        canvas->clipRect(contentRect);
-        canvas->translate(contentRect.fLeft, contentRect.fTop);
-    }
-
     // By default, we render directly into the window's surface/canvas
     SkCanvas* slideCanvas = canvas;
     fLastImage.reset();
@@ -780,7 +753,6 @@
 
     int count = slideCanvas->save();
     slideCanvas->clear(SK_ColorWHITE);
-    slideCanvas->concat(fDefaultMatrix);
     slideCanvas->concat(computeMatrix());
     // Time the painting logic of the slide
     double startTime = SkTime::GetMSecs();
@@ -848,18 +820,36 @@
 
 bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) {
     void* castedOwner = reinterpret_cast<void*>(owner);
-    SkPoint touchPoint = fDefaultMatrixInv.mapXY(x, y);
     switch (state) {
         case Window::kUp_InputState: {
             fGesture.touchEnd(castedOwner);
             break;
         }
         case Window::kDown_InputState: {
-            fGesture.touchBegin(castedOwner, touchPoint.fX, touchPoint.fY);
+            fGesture.touchBegin(castedOwner, x, y);
             break;
         }
         case Window::kMove_InputState: {
-            fGesture.touchMoved(castedOwner, touchPoint.fX, touchPoint.fY);
+            fGesture.touchMoved(castedOwner, x, y);
+            break;
+        }
+    }
+    fWindow->inval();
+    return true;
+}
+
+bool Viewer::onMouse(float x, float y, Window::InputState state, uint32_t modifiers) {
+    switch (state) {
+        case Window::kUp_InputState: {
+            fGesture.touchEnd(nullptr);
+            break;
+        }
+        case Window::kDown_InputState: {
+            fGesture.touchBegin(nullptr, x, y);
+            break;
+        }
+        case Window::kMove_InputState: {
+            fGesture.touchMoved(nullptr, x, y);
             break;
         }
     }
@@ -882,12 +872,6 @@
     SkPaint paint;
     canvas->save();
 
-    if (fWindow->supportsContentRect()) {
-        SkRect contentRect = fWindow->getContentRect();
-        canvas->clipRect(contentRect);
-        canvas->translate(contentRect.fLeft, contentRect.fTop);
-    }
-
     canvas->clipRect(rect);
     paint.setColor(SK_ColorBLACK);
     canvas->drawRect(rect, paint);
@@ -1024,6 +1008,10 @@
                 if (ctx && ImGui::Checkbox("Instanced Rendering", inst)) {
                     paramsChanged = true;
                 }
+                bool* wire = &params.fGrContextOptions.fWireframeMode;
+                if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
+                    paramsChanged = true;
+                }
 
                 if (ctx) {
                     int sampleCount = fWindow->sampleCount();
@@ -1151,8 +1139,7 @@
             static int zoomFactor = 4;
             ImGui::SliderInt("Scale", &zoomFactor, 1, 16);
 
-            zoomImagePaint.setShader(fLastImage->makeShader(SkShader::kClamp_TileMode,
-                                                            SkShader::kClamp_TileMode));
+            zoomImagePaint.setShader(fLastImage->makeShader());
             zoomImagePaint.setColor(SK_ColorWHITE);
 
             // Zoom by shrinking the corner UVs towards the mouse cursor
@@ -1209,10 +1196,12 @@
                 canvas->save();
                 canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y,
                                                   drawCmd->ClipRect.z, drawCmd->ClipRect.w));
-                canvas->drawVertices(SkCanvas::kTriangles_VertexMode, drawList->VtxBuffer.size(),
-                                     pos.begin(), uv.begin(), color.begin(),
-                                     drawList->IdxBuffer.begin() + indexOffset, drawCmd->ElemCount,
-                                     *paint);
+                canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                                          drawList->VtxBuffer.size(), pos.begin(),
+                                                          uv.begin(), color.begin(),
+                                                          drawCmd->ElemCount,
+                                                          drawList->IdxBuffer.begin() + indexOffset),
+                                     SkBlendMode::kModulate, *paint);
                 indexOffset += drawCmd->ElemCount;
                 canvas->restore();
             }
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index c50f750..fac280f 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -27,6 +27,7 @@
     void onPaint(SkCanvas* canvas);
     void onIdle() override;
     bool onTouch(intptr_t owner, sk_app::Window::InputState state, float x, float y);
+    bool onMouse(float x, float y, sk_app::Window::InputState state, uint32_t modifiers);
     void onUIStateChanged(const SkString& stateName, const SkString& stateValue);
     bool onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers);
     bool onChar(SkUnichar c, uint32_t modifiers);
@@ -86,10 +87,7 @@
     SkColorSpacePrimaries  fColorSpacePrimaries;
 
     // transform data
-    SkScalar               fZoomCenterX;
-    SkScalar               fZoomCenterY;
     SkScalar               fZoomLevel;
-    SkScalar               fZoomScale;
 
     sk_app::CommandSet     fCommands;
 
@@ -97,7 +95,6 @@
 
     // identity unless the window initially scales the content to fit the screen.
     SkMatrix               fDefaultMatrix;
-    SkMatrix               fDefaultMatrixInv;
 
     SkTArray<std::function<void(void)>> fDeferredActions;
 
diff --git a/tools/viewer/sk_app/CommandSet.cpp b/tools/viewer/sk_app/CommandSet.cpp
index 689b97f..d0154d6 100644
--- a/tools/viewer/sk_app/CommandSet.cpp
+++ b/tools/viewer/sk_app/CommandSet.cpp
@@ -138,14 +138,14 @@
         if (kGrouped_HelpMode == fHelpMode && lastGroup != cmd.fGroup) {
             // Group change. Advance and print header:
             y += paint.getTextSize();
-            canvas->drawText(cmd.fGroup.c_str(), cmd.fGroup.size(), x, y, groupPaint);
+            canvas->drawString(cmd.fGroup, x, y, groupPaint);
             y += groupPaint.getTextSize() + 2;
             lastGroup = cmd.fGroup;
         }
 
-        canvas->drawText(cmd.fKeyName.c_str(), cmd.fKeyName.size(), x, y, paint);
+        canvas->drawString(cmd.fKeyName, x, y, paint);
         SkString text = SkStringPrintf(": %s", cmd.fDescription.c_str());
-        canvas->drawText(text.c_str(), text.size(), x + keyWidth, y, paint);
+        canvas->drawString(text, x + keyWidth, y, paint);
         y += paint.getTextSize() + 2;
     }
 }
diff --git a/tools/viewer/sk_app/GLWindowContext.cpp b/tools/viewer/sk_app/GLWindowContext.cpp
index ff56ce8..faa78ab 100644
--- a/tools/viewer/sk_app/GLWindowContext.cpp
+++ b/tools/viewer/sk_app/GLWindowContext.cpp
@@ -6,27 +6,25 @@
  * found in the LICENSE file.
  */
 
+#include "GrBackendSurface.h"
 #include "GrContext.h"
-#include "SkSurface.h"
+#include "GrRenderTarget.h"
 #include "GLWindowContext.h"
 
 #include "gl/GrGLDefines.h"
-
 #include "gl/GrGLUtil.h"
-#include "GrRenderTarget.h"
-#include "GrContext.h"
 
 #include "SkCanvas.h"
 #include "SkImage_Base.h"
 #include "SkMathPriv.h"
+#include "SkSurface.h"
 
 namespace sk_app {
 
 GLWindowContext::GLWindowContext(const DisplayParams& params)
-    : WindowContext()
+    : WindowContext(params)
     , fBackendContext(nullptr)
     , fSurface(nullptr) {
-    fDisplayParams = params;
     fDisplayParams.fMSAASampleCount = fDisplayParams.fMSAASampleCount ?
                                       GrNextPow2(fDisplayParams.fMSAASampleCount) :
                                       0;
@@ -64,7 +62,7 @@
         fContext->unref();
         fContext = nullptr;
     }
-    
+
     fBackendContext.reset(nullptr);
 
     this->onDestroyContext();
@@ -73,18 +71,21 @@
 sk_sp<SkSurface> GLWindowContext::getBackbufferSurface() {
     if (nullptr == fSurface) {
         if (fContext) {
-            GrBackendRenderTargetDesc desc;
-            desc.fWidth = this->fWidth;
-            desc.fHeight = this->fHeight;
-            desc.fConfig = fPixelConfig;
-            desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-            desc.fSampleCnt = fSampleCount;
-            desc.fStencilBits = fStencilBits;
+            GrGLFramebufferInfo fbInfo;
             GrGLint buffer;
-            GR_GL_CALL(fBackendContext.get(), GetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer));
-            desc.fRenderTargetHandle = buffer;
+            GR_GL_CALL(fBackendContext.get(), GetIntegerv(GR_GL_FRAMEBUFFER_BINDING,
+                                                          &buffer));
+            fbInfo.fFBOID = buffer;
 
-            fSurface = SkSurface::MakeFromBackendRenderTarget(fContext, desc,
+            GrBackendRenderTarget backendRT(fWidth,
+                                            fHeight,
+                                            fSampleCount,
+                                            fStencilBits,
+                                            fPixelConfig,
+                                            fbInfo);
+
+            fSurface = SkSurface::MakeFromBackendRenderTarget(fContext, backendRT,
+                                                              kBottomLeft_GrSurfaceOrigin,
                                                               fDisplayParams.fColorSpace,
                                                               &fSurfaceProps);
         }
diff --git a/tools/viewer/sk_app/RasterWindowContext.h b/tools/viewer/sk_app/RasterWindowContext.h
index 0393e9a..75bde03 100644
--- a/tools/viewer/sk_app/RasterWindowContext.h
+++ b/tools/viewer/sk_app/RasterWindowContext.h
@@ -14,6 +14,8 @@
 
 class RasterWindowContext : public WindowContext {
 public:
+    RasterWindowContext(const DisplayParams& params) : WindowContext(params) {}
+
     // Explicitly convert nullptr to GrBackendContext is needed for compiling
     GrBackendContext getBackendContext() override { return (GrBackendContext) nullptr; }
 
diff --git a/tools/viewer/sk_app/VulkanWindowContext.cpp b/tools/viewer/sk_app/VulkanWindowContext.cpp
index 8e8c059..e53cc7c 100644
--- a/tools/viewer/sk_app/VulkanWindowContext.cpp
+++ b/tools/viewer/sk_app/VulkanWindowContext.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 
+#include "GrBackendSurface.h"
 #include "GrContext.h"
 #include "GrRenderTarget.h"
 #include "SkAutoMalloc.h"
@@ -30,7 +31,9 @@
 VulkanWindowContext::VulkanWindowContext(const DisplayParams& params,
                                          CreateVkSurfaceFn createVkSurface,
                                          CanPresentFn canPresent)
-    : WindowContext()
+    : WindowContext(params)
+    , fCreateVkSurfaceFn(createVkSurface)
+    , fCanPresentFn(canPresent)
     , fSurface(VK_NULL_HANDLE)
     , fSwapchain(VK_NULL_HANDLE)
     , fImages(nullptr)
@@ -38,9 +41,13 @@
     , fSurfaces(nullptr)
     , fCommandPool(VK_NULL_HANDLE)
     , fBackbuffers(nullptr) {
+    this->initializeContext();
+}
 
+void VulkanWindowContext::initializeContext() {
     // any config code here (particularly for msaa)?
-    fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent));
+    fBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
+                                                     &fPresentQueueIndex, fCanPresentFn));
 
     if (!(fBackendContext->fExtensions & kKHR_surface_GrVkExtensionFlag) ||
         !(fBackendContext->fExtensions & kKHR_swapchain_GrVkExtensionFlag)) {
@@ -62,9 +69,9 @@
     GET_DEV_PROC(QueuePresentKHR);
 
     fContext = GrContext::Create(kVulkan_GrBackend, (GrBackendContext) fBackendContext.get(),
-                                 params.fGrContextOptions);
+                                 fDisplayParams.fGrContextOptions);
 
-    fSurface = createVkSurface(instance);
+    fSurface = fCreateVkSurfaceFn(instance);
     if (VK_NULL_HANDLE == fSurface) {
         fBackendContext.reset(nullptr);
         return;
@@ -79,7 +86,7 @@
         return;
     }
 
-    if (!this->createSwapchain(-1, -1, params)) {
+    if (!this->createSwapchain(-1, -1, fDisplayParams)) {
         this->destroyContext();
         return;
     }
@@ -175,8 +182,8 @@
     auto srgbColorSpace = SkColorSpace::MakeSRGB();
     bool wantSRGB = srgbColorSpace == params.fColorSpace;
     for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
-        GrPixelConfig config;
-        if (GrVkFormatToPixelConfig(surfaceFormats[i].format, &config) &&
+        GrPixelConfig config = GrVkFormatToPixelConfig(surfaceFormats[i].format);
+        if (kUnknown_GrPixelConfig != config &&
             GrPixelConfigIsSRGB(config) == wantSRGB) {
             surfaceFormat = surfaceFormats[i].format;
             colorSpace = surfaceFormats[i].colorSpace;
@@ -250,7 +257,8 @@
 }
 
 void VulkanWindowContext::createBuffers(VkFormat format) {
-    GrVkFormatToPixelConfig(format, &fPixelConfig);
+    fPixelConfig = GrVkFormatToPixelConfig(format);
+    SkASSERT(kUnknown_GrPixelConfig != fPixelConfig);
 
     fGetSwapchainImagesKHR(fBackendContext->fDevice, fSwapchain, &fImageCount, nullptr);
     SkASSERT(fImageCount);
@@ -263,7 +271,6 @@
     for (uint32_t i = 0; i < fImageCount; ++i) {
         fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
 
-        GrBackendRenderTargetDesc desc;
         GrVkImageInfo info;
         info.fImage = fImages[i];
         info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
@@ -271,17 +278,14 @@
         info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
         info.fFormat = format;
         info.fLevelCount = 1;
-        desc.fWidth = fWidth;
-        desc.fHeight = fHeight;
-        desc.fConfig = fPixelConfig;
-        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-        desc.fSampleCnt = fSampleCount;
-        desc.fStencilBits = fStencilBits;
-        desc.fRenderTargetHandle = (GrBackendObject) &info;
 
-        fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(fContext, desc,
-                                                              fDisplayParams.fColorSpace,
-                                                              &fSurfaceProps);
+        GrBackendTexture backendTex(fWidth, fHeight, info);
+
+        fSurfaces[i] = SkSurface::MakeFromBackendTextureAsRenderTarget(fContext, backendTex,
+                                                                       kTopLeft_GrSurfaceOrigin,
+                                                                       fSampleCount,
+                                                                       fDisplayParams.fColorSpace,
+                                                                       &fSurfaceProps);
     }
 
     // create the command pool for the command buffers
diff --git a/tools/viewer/sk_app/VulkanWindowContext.h b/tools/viewer/sk_app/VulkanWindowContext.h
index cafaead..81e5f3d 100644
--- a/tools/viewer/sk_app/VulkanWindowContext.h
+++ b/tools/viewer/sk_app/VulkanWindowContext.h
@@ -33,7 +33,9 @@
     }
 
     void setDisplayParams(const DisplayParams& params) override {
-        this->createSwapchain(fWidth, fHeight, params);
+        this->destroyContext();
+        fDisplayParams = params;
+        this->initializeContext();
     }
 
     GrBackendContext getBackendContext() override {
@@ -48,6 +50,7 @@
     VulkanWindowContext(const DisplayParams&, CreateVkSurfaceFn, CanPresentFn);
 
 private:
+    void initializeContext();
     void destroyContext();
 
     struct BackbufferInfo {
@@ -75,6 +78,10 @@
         FNPTR_TYPE fPtr;
     };
 
+    // Create functions
+    CreateVkSurfaceFn fCreateVkSurfaceFn;
+    CanPresentFn      fCanPresentFn;
+
     // WSI interface functions
     VkPtr<PFN_vkDestroySurfaceKHR> fDestroySurfaceKHR;
     VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> fGetPhysicalDeviceSurfaceSupportKHR;
diff --git a/tools/viewer/sk_app/Window.h b/tools/viewer/sk_app/Window.h
index c5d8c34..752dc47 100644
--- a/tools/viewer/sk_app/Window.h
+++ b/tools/viewer/sk_app/Window.h
@@ -38,8 +38,6 @@
     void inval();
 
     virtual bool scaleContentToFit() const { return false; }
-    virtual bool supportsContentRect() const { return false; }
-    virtual SkRect getContentRect() { return SkRect::MakeEmpty(); }
 
     enum BackendType {
         kNativeGL_BackendType,
diff --git a/tools/viewer/sk_app/WindowContext.h b/tools/viewer/sk_app/WindowContext.h
index 75b52b9..fbd2756 100644
--- a/tools/viewer/sk_app/WindowContext.h
+++ b/tools/viewer/sk_app/WindowContext.h
@@ -20,10 +20,12 @@
 
 class WindowContext {
 public:
-    WindowContext() : fContext(nullptr)
-                    , fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)
-                    , fSampleCount(0)
-                    , fStencilBits(0) {}
+    WindowContext(const DisplayParams& params)
+        : fContext(nullptr)
+        , fDisplayParams(params)
+        , fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)
+        , fSampleCount(0)
+        , fStencilBits(0) {}
 
     virtual ~WindowContext() {}
 
diff --git a/tools/viewer/sk_app/android/GLWindowContext_android.cpp b/tools/viewer/sk_app/android/GLWindowContext_android.cpp
index 1529b3a..12c12a7 100644
--- a/tools/viewer/sk_app/android/GLWindowContext_android.cpp
+++ b/tools/viewer/sk_app/android/GLWindowContext_android.cpp
@@ -32,17 +32,20 @@
 
     EGLDisplay fDisplay;
     EGLContext fEGLContext;
-    EGLSurface fSurface;
+    EGLSurface fSurfaceAndroid;
 
     // For setDisplayParams and resize which call onInitializeContext with null platformData
     ANativeWindow* fNativeWindow = nullptr;
+
+    typedef GLWindowContext INHERITED;
 };
 
-GLWindowContext_android::GLWindowContext_android(ANativeWindow* window, const DisplayParams& params)
-    : GLWindowContext(params)
+GLWindowContext_android::GLWindowContext_android(ANativeWindow* window,
+                                                 const DisplayParams& params)
+    : INHERITED(params)
     , fDisplay(EGL_NO_DISPLAY)
     , fEGLContext(EGL_NO_CONTEXT)
-    , fSurface(EGL_NO_SURFACE)
+    , fSurfaceAndroid(EGL_NO_SURFACE)
     , fNativeWindow(window) {
 
     // any config code here (particularly for msaa)?
@@ -110,14 +113,14 @@
         windowAttribs = srgbWindowAttribs;
     }
 
-    fSurface = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, windowAttribs);
-    if (EGL_NO_SURFACE == fSurface && windowAttribs) {
+    fSurfaceAndroid = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, windowAttribs);
+    if (EGL_NO_SURFACE == fSurfaceAndroid && windowAttribs) {
         // Try again without sRGB
-        fSurface = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, nullptr);
+        fSurfaceAndroid = eglCreateWindowSurface(fDisplay, surfaceConfig, fNativeWindow, nullptr);
     }
-    SkASSERT(EGL_NO_SURFACE != fSurface);
+    SkASSERT(EGL_NO_SURFACE != fSurfaceAndroid);
 
-    SkAssertResult(eglMakeCurrent(fDisplay, fSurface, fSurface, fEGLContext));
+    SkAssertResult(eglMakeCurrent(fDisplay, fSurfaceAndroid, fSurfaceAndroid, fEGLContext));
     // GLWindowContext::initializeContext will call GrGLCreateNativeInterface so we
     // won't call it here.
 
@@ -131,19 +134,19 @@
 }
 
 void GLWindowContext_android::onDestroyContext() {
-    if (!fDisplay || !fEGLContext || !fSurface) {
+    if (!fDisplay || !fEGLContext || !fSurfaceAndroid) {
         return;
     }
     eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    SkAssertResult(eglDestroySurface(fDisplay, fSurface));
+    SkAssertResult(eglDestroySurface(fDisplay, fSurfaceAndroid));
     SkAssertResult(eglDestroyContext(fDisplay, fEGLContext));
     fEGLContext = EGL_NO_CONTEXT;
-    fSurface = EGL_NO_SURFACE;
+    fSurfaceAndroid = EGL_NO_SURFACE;
 }
 
 void GLWindowContext_android::onSwapBuffers() {
-    if (fDisplay && fEGLContext && fSurface) {
-        eglSwapBuffers(fDisplay, fSurface);
+    if (fDisplay && fEGLContext && fSurfaceAndroid) {
+        eglSwapBuffers(fDisplay, fSurfaceAndroid);
     }
 }
 
diff --git a/tools/viewer/sk_app/android/RasterWindowContext_android.cpp b/tools/viewer/sk_app/android/RasterWindowContext_android.cpp
index 7077e2c..101e51e 100644
--- a/tools/viewer/sk_app/android/RasterWindowContext_android.cpp
+++ b/tools/viewer/sk_app/android/RasterWindowContext_android.cpp
@@ -32,11 +32,13 @@
     ANativeWindow* fNativeWindow = nullptr;
     ANativeWindow_Buffer fBuffer;
     ARect fBounds;
+
+    typedef RasterWindowContext INHERITED;
 };
 
 RasterWindowContext_android::RasterWindowContext_android(ANativeWindow* window,
-                                                         const DisplayParams& params) {
-    fDisplayParams = params;
+                                                         const DisplayParams& params)
+    : INHERITED(params) {
     fNativeWindow = window;
     fWidth = ANativeWindow_getWidth(fNativeWindow);
     fHeight = ANativeWindow_getHeight(fNativeWindow);
diff --git a/tools/viewer/sk_app/android/Window_android.h b/tools/viewer/sk_app/android/Window_android.h
index 062f2fe..7918011 100644
--- a/tools/viewer/sk_app/android/Window_android.h
+++ b/tools/viewer/sk_app/android/Window_android.h
@@ -32,13 +32,9 @@
     void paintIfNeeded();
 
     bool scaleContentToFit() const override { return true; }
-    bool supportsContentRect() const override { return true; }
-    SkRect getContentRect() override { return fContentRect; }
-    void setContentRect(int l, int t, int r, int b) { fContentRect.set(l,t,r,b); }
 
 private:
     SkiaAndroidApp* fSkiaAndroidApp = nullptr;
-    SkRect fContentRect;
     BackendType fBackendType;
 };
 
diff --git a/tools/viewer/sk_app/android/main_android.cpp b/tools/viewer/sk_app/android/main_android.cpp
index 9c3316f..cb8db6c 100644
--- a/tools/viewer/sk_app/android/main_android.cpp
+++ b/tools/viewer/sk_app/android/main_android.cpp
@@ -27,7 +27,7 @@
     static const char* gCmdLine[] = {
         "viewer",
         "--skps",
-        "/data/local/tmp/skp",
+        "/data/local/tmp/skps",
         // TODO: figure out how to use am start with extra params to pass in additional arguments at
         // runtime
         // "--atrace",
diff --git a/tools/viewer/sk_app/android/surface_glue_android.cpp b/tools/viewer/sk_app/android/surface_glue_android.cpp
index 178f04d..4fb6c3d 100644
--- a/tools/viewer/sk_app/android/surface_glue_android.cpp
+++ b/tools/viewer/sk_app/android/surface_glue_android.cpp
@@ -123,7 +123,6 @@
                 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
             }
             window_android->onResize(width, height);
-            window_android->setContentRect(0, 0, width, height);
             window_android->paintIfNeeded();
             break;
         }
diff --git a/tools/viewer/sk_app/unix/GLWindowContext_unix.cpp b/tools/viewer/sk_app/unix/GLWindowContext_unix.cpp
index 530a5b6..ce2727e 100644
--- a/tools/viewer/sk_app/unix/GLWindowContext_unix.cpp
+++ b/tools/viewer/sk_app/unix/GLWindowContext_unix.cpp
@@ -37,10 +37,12 @@
     GLXFBConfig* fFBConfig;
     XVisualInfo* fVisualInfo;
     GLXContext   fGLContext;
+
+    typedef GLWindowContext INHERITED;
 };
 
 GLWindowContext_xlib::GLWindowContext_xlib(const XlibWindowInfo& winInfo, const DisplayParams& params)
-        : GLWindowContext(params)
+        : INHERITED(params)
         , fDisplay(winInfo.fDisplay)
         , fWindow(winInfo.fWindow)
         , fFBConfig(winInfo.fFBConfig)
diff --git a/tools/viewer/sk_app/unix/RasterWindowContext_unix.cpp b/tools/viewer/sk_app/unix/RasterWindowContext_unix.cpp
index 73846a1..6bfa6fd 100644
--- a/tools/viewer/sk_app/unix/RasterWindowContext_unix.cpp
+++ b/tools/viewer/sk_app/unix/RasterWindowContext_unix.cpp
@@ -29,13 +29,15 @@
     Display* fDisplay;
     XWindow  fWindow;
     GC       fGC;
+
+    typedef RasterWindowContext INHERITED;
 };
 
 RasterWindowContext_xlib::RasterWindowContext_xlib(Display* display, XWindow window, int width,
                                                    int height, const DisplayParams& params)
-        : fDisplay(display)
+        : INHERITED(params)
+        , fDisplay(display)
         , fWindow(window) {
-    fDisplayParams = params;
     fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
     this->resize(width, height);
     fWidth = width;
diff --git a/tools/viewer/sk_app/win/GLWindowContext_win.cpp b/tools/viewer/sk_app/win/GLWindowContext_win.cpp
index 24db965..20c3d91 100644
--- a/tools/viewer/sk_app/win/GLWindowContext_win.cpp
+++ b/tools/viewer/sk_app/win/GLWindowContext_win.cpp
@@ -33,10 +33,12 @@
 private:
     HWND              fHWND;
     HGLRC             fHGLRC;
+
+    typedef GLWindowContext INHERITED;
 };
 
 GLWindowContext_win::GLWindowContext_win(HWND wnd, const DisplayParams& params)
-    : GLWindowContext(params)
+    : INHERITED(params)
     , fHWND(wnd)
     , fHGLRC(NULL) {
 
diff --git a/tools/viewer/sk_app/win/RasterWindowContext_win.cpp b/tools/viewer/sk_app/win/RasterWindowContext_win.cpp
index 96fe8f2..85bb65e 100644
--- a/tools/viewer/sk_app/win/RasterWindowContext_win.cpp
+++ b/tools/viewer/sk_app/win/RasterWindowContext_win.cpp
@@ -31,11 +31,14 @@
     SkAutoMalloc fSurfaceMemory;
     sk_sp<SkSurface> fBackbufferSurface;
     HWND fWnd;
+
+private:
+    typedef RasterWindowContext INHERITED;
 };
 
 RasterWindowContext_win::RasterWindowContext_win(HWND wnd, const DisplayParams& params)
-        : fWnd(wnd) {
-    fDisplayParams = params;
+    : INHERITED(params)
+    , fWnd(wnd) {
     RECT rect;
     GetWindowRect(wnd, &rect);
     this->resize(rect.right - rect.left, rect.bottom - rect.top);
diff --git a/whitespace.txt b/whitespace.txt
index 9e1e2e9..f9ffb83 100644
--- a/whitespace.txt
+++ b/whitespace.txt
@@ -1,6 +1,7 @@
 You can modify this file to create no-op changelists (like this one).
 I LOVE W	H	I	T	E	S	P	A	C	E!
 Python also loves\n\twhitespace.¯\_(ツ)_/¯
+
 Lithp.
 Use the Forth.
 Life is a mistry.  Everyone must WAKA WAKA WAKA
@@ -10,5 +11,5 @@
 more  whitespace
 all the things!!!
 Yet another line.
-Driver update 20170316
+Driver update 20170522
 Gerrit 10k!